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()
49 mWasL10nCached(false) {
53 nsresult
nsXULPrototypeDocument::Init() {
54 mNodeInfoManager
= new nsNodeInfoManager();
55 return mNodeInfoManager
->Init(nullptr);
58 nsXULPrototypeDocument::~nsXULPrototypeDocument() {
59 if (mRoot
) mRoot
->ReleaseSubtree();
62 NS_IMPL_CYCLE_COLLECTION_CLASS(nsXULPrototypeDocument
)
64 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXULPrototypeDocument
)
65 NS_IMPL_CYCLE_COLLECTION_UNLINK(mPrototypeWaiters
)
66 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
67 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXULPrototypeDocument
)
68 if (nsCCUncollectableMarker::InGeneration(cb
, tmp
->mCCGeneration
)) {
69 return NS_SUCCESS_INTERRUPTED_TRAVERSE
;
71 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRoot
)
72 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNodeInfoManager
)
73 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
75 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsXULPrototypeDocument
)
76 NS_INTERFACE_MAP_ENTRY(nsISerializable
)
77 NS_INTERFACE_MAP_ENTRY(nsISupports
)
80 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsXULPrototypeDocument
)
81 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsXULPrototypeDocument
)
84 NS_NewXULPrototypeDocument(nsXULPrototypeDocument
** aResult
) {
86 RefPtr
<nsXULPrototypeDocument
> doc
= new nsXULPrototypeDocument();
88 nsresult rv
= doc
->Init();
97 //----------------------------------------------------------------------
99 // nsISerializable methods
103 nsXULPrototypeDocument::Read(nsIObjectInputStream
* aStream
) {
104 nsCOMPtr
<nsISupports
> supports
;
105 nsresult rv
= aStream
->ReadObject(true, getter_AddRefs(supports
));
109 mURI
= do_QueryInterface(supports
);
111 // nsIPrincipal mNodeInfoManager->mPrincipal
113 rv
= aStream
->ReadCString(JSON
);
117 nsCOMPtr
<nsIPrincipal
> principal
= mozilla::BasePrincipal::FromJSON(JSON
);
119 // Better safe than sorry....
120 mNodeInfoManager
->SetDocumentPrincipal(principal
);
122 rv
= aStream
->ReadBoolean(&mWasL10nCached
);
127 mRoot
= new nsXULPrototypeElement();
129 // mozilla::dom::NodeInfo table
130 nsTArray
<RefPtr
<mozilla::dom::NodeInfo
>> nodeInfos
;
133 rv
= aStream
->Read32(&count
);
137 nsAutoString namespaceURI
, prefixStr
, localName
;
139 RefPtr
<nsAtom
> prefix
;
140 for (i
= 0; i
< count
; ++i
) {
141 rv
= aStream
->ReadString(namespaceURI
);
145 rv
= aStream
->ReadBoolean(&prefixIsNull
);
152 rv
= aStream
->ReadString(prefixStr
);
156 prefix
= NS_Atomize(prefixStr
);
158 rv
= aStream
->ReadString(localName
);
163 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
167 rv
= mNodeInfoManager
->GetNodeInfo(localName
, prefix
, namespaceURI
,
168 UINT16_MAX
, getter_AddRefs(nodeInfo
));
172 nodeInfos
.AppendElement(nodeInfo
);
177 while (NS_SUCCEEDED(rv
)) {
178 rv
= aStream
->Read32(&type
);
184 if ((nsXULPrototypeNode::Type
)type
== nsXULPrototypeNode::eType_PI
) {
185 RefPtr
<nsXULPrototypePI
> pi
= new nsXULPrototypePI();
187 rv
= pi
->Deserialize(aStream
, this, mURI
, &nodeInfos
);
191 rv
= AddProcessingInstruction(pi
);
195 } else if ((nsXULPrototypeNode::Type
)type
==
196 nsXULPrototypeNode::eType_Element
) {
197 rv
= mRoot
->Deserialize(aStream
, this, mURI
, &nodeInfos
);
203 MOZ_ASSERT_UNREACHABLE("Unexpected prototype node type");
204 return NS_ERROR_FAILURE
;
208 return NotifyLoadDone();
211 static nsresult
GetNodeInfos(nsXULPrototypeElement
* aPrototype
,
212 nsTArray
<RefPtr
<mozilla::dom::NodeInfo
>>& aArray
) {
213 if (aArray
.IndexOf(aPrototype
->mNodeInfo
) == aArray
.NoIndex
) {
214 aArray
.AppendElement(aPrototype
->mNodeInfo
);
219 for (i
= 0; i
< aPrototype
->mAttributes
.Length(); ++i
) {
220 RefPtr
<mozilla::dom::NodeInfo
> ni
;
221 nsAttrName
* name
= &aPrototype
->mAttributes
[i
].mName
;
222 if (name
->IsAtom()) {
223 ni
= aPrototype
->mNodeInfo
->NodeInfoManager()->GetNodeInfo(
224 name
->Atom(), nullptr, kNameSpaceID_None
, nsINode::ATTRIBUTE_NODE
);
226 ni
= name
->NodeInfo();
229 if (aArray
.IndexOf(ni
) == aArray
.NoIndex
) {
230 aArray
.AppendElement(ni
);
235 for (i
= 0; i
< aPrototype
->mChildren
.Length(); ++i
) {
236 nsXULPrototypeNode
* child
= aPrototype
->mChildren
[i
];
237 if (child
->mType
== nsXULPrototypeNode::eType_Element
) {
239 GetNodeInfos(static_cast<nsXULPrototypeElement
*>(child
), aArray
);
240 NS_ENSURE_SUCCESS(rv
, rv
);
248 nsXULPrototypeDocument::Write(nsIObjectOutputStream
* aStream
) {
251 rv
= aStream
->WriteCompoundObject(mURI
, NS_GET_IID(nsIURI
), true);
253 // nsIPrincipal mNodeInfoManager->mPrincipal
255 mozilla::BasePrincipal::Cast(mNodeInfoManager
->DocumentPrincipal())
257 nsresult tmp
= aStream
->WriteStringZ(JSON
.get());
258 if (NS_FAILED(tmp
)) {
263 // XXX Worrisome if we're caching things without system principal.
264 if (!mNodeInfoManager
->DocumentPrincipal()->IsSystemPrincipal()) {
265 NS_WARNING("Serializing document without system principal");
269 tmp
= aStream
->WriteBoolean(mWasL10nCached
);
270 if (NS_FAILED(tmp
)) {
274 // mozilla::dom::NodeInfo table
275 nsTArray
<RefPtr
<mozilla::dom::NodeInfo
>> nodeInfos
;
277 tmp
= GetNodeInfos(mRoot
, nodeInfos
);
278 if (NS_FAILED(tmp
)) {
283 uint32_t nodeInfoCount
= nodeInfos
.Length();
284 tmp
= aStream
->Write32(nodeInfoCount
);
285 if (NS_FAILED(tmp
)) {
289 for (i
= 0; i
< nodeInfoCount
; ++i
) {
290 mozilla::dom::NodeInfo
* nodeInfo
= nodeInfos
[i
];
291 NS_ENSURE_TRUE(nodeInfo
, NS_ERROR_FAILURE
);
293 nsAutoString namespaceURI
;
294 nodeInfo
->GetNamespaceURI(namespaceURI
);
295 tmp
= aStream
->WriteWStringZ(namespaceURI
.get());
296 if (NS_FAILED(tmp
)) {
301 nodeInfo
->GetPrefix(prefix
);
302 bool nullPrefix
= DOMStringIsNull(prefix
);
303 tmp
= aStream
->WriteBoolean(nullPrefix
);
304 if (NS_FAILED(tmp
)) {
308 tmp
= aStream
->WriteWStringZ(prefix
.get());
309 if (NS_FAILED(tmp
)) {
314 nsAutoString localName
;
315 nodeInfo
->GetName(localName
);
316 tmp
= aStream
->WriteWStringZ(localName
.get());
317 if (NS_FAILED(tmp
)) {
322 // Now serialize the document contents
323 uint32_t count
= mProcessingInstructions
.Length();
324 for (i
= 0; i
< count
; ++i
) {
325 nsXULPrototypePI
* pi
= mProcessingInstructions
[i
];
326 tmp
= pi
->Serialize(aStream
, this, &nodeInfos
);
327 if (NS_FAILED(tmp
)) {
333 tmp
= mRoot
->Serialize(aStream
, this, &nodeInfos
);
334 if (NS_FAILED(tmp
)) {
342 //----------------------------------------------------------------------
345 nsresult
nsXULPrototypeDocument::InitPrincipal(nsIURI
* aURI
,
346 nsIPrincipal
* aPrincipal
) {
347 NS_ENSURE_ARG_POINTER(aURI
);
350 mNodeInfoManager
->SetDocumentPrincipal(aPrincipal
);
354 nsIURI
* nsXULPrototypeDocument::GetURI() {
355 NS_ASSERTION(mURI
, "null URI");
359 nsXULPrototypeElement
* nsXULPrototypeDocument::GetRootElement() {
363 void nsXULPrototypeDocument::SetRootElement(nsXULPrototypeElement
* aElement
) {
367 nsresult
nsXULPrototypeDocument::AddProcessingInstruction(
368 nsXULPrototypePI
* aPI
) {
369 MOZ_ASSERT(aPI
, "null ptr");
370 // XXX(Bug 1631371) Check if this should use a fallible operation as it
371 // pretended earlier, or change the return type to void.
372 mProcessingInstructions
.AppendElement(aPI
);
376 const nsTArray
<RefPtr
<nsXULPrototypePI
>>&
377 nsXULPrototypeDocument::GetProcessingInstructions() const {
378 return mProcessingInstructions
;
381 nsIPrincipal
* nsXULPrototypeDocument::DocumentPrincipal() {
382 MOZ_ASSERT(mNodeInfoManager
, "missing nodeInfoManager");
383 return mNodeInfoManager
->DocumentPrincipal();
386 void nsXULPrototypeDocument::SetDocumentPrincipal(nsIPrincipal
* aPrincipal
) {
387 mNodeInfoManager
->SetDocumentPrincipal(aPrincipal
);
390 void nsXULPrototypeDocument::MarkInCCGeneration(uint32_t aCCGeneration
) {
391 mCCGeneration
= aCCGeneration
;
394 nsNodeInfoManager
* nsXULPrototypeDocument::GetNodeInfoManager() {
395 return mNodeInfoManager
;
398 nsresult
nsXULPrototypeDocument::AwaitLoadDone(Callback
&& aCallback
,
405 // XXX(Bug 1631371) Check if this should use a fallible operation as it
406 // pretended earlier, or change the return type to void.
407 mPrototypeWaiters
.AppendElement(std::move(aCallback
));
413 nsresult
nsXULPrototypeDocument::NotifyLoadDone() {
414 // Call back to each XUL document that raced to start the same
415 // prototype document load, lost the race, but hit the XUL
416 // prototype cache because the winner filled the cache with
417 // the not-yet-loaded prototype object.
421 for (uint32_t i
= mPrototypeWaiters
.Length(); i
> 0;) {
423 mPrototypeWaiters
[i
]();
425 mPrototypeWaiters
.Clear();
430 void nsXULPrototypeDocument::TraceProtos(JSTracer
* aTrc
) {
431 // Only trace the protos once per GC if we are marking.
432 if (aTrc
->isMarkingTracer()) {
433 uint32_t currentGCNumber
= aTrc
->gcNumberForMarking();
434 if (mGCNumber
== currentGCNumber
) {
437 mGCNumber
= currentGCNumber
;
441 mRoot
->TraceAllScripts(aTrc
);
445 void nsXULPrototypeDocument::SetIsL10nCached(bool aIsCached
) {
446 mWasL10nCached
= aIsCached
;
449 void nsXULPrototypeDocument::RebuildPrototypeFromElement(
450 nsXULPrototypeElement
* aPrototype
, Element
* aElement
, bool aDeep
) {
451 aPrototype
->mHasIdAttribute
= aElement
->HasID();
452 aPrototype
->mHasClassAttribute
= aElement
->MayHaveClass();
453 aPrototype
->mHasStyleAttribute
= aElement
->MayHaveStyle();
454 NodeInfo
* oldNodeInfo
= aElement
->NodeInfo();
455 RefPtr
<NodeInfo
> newNodeInfo
= mNodeInfoManager
->GetNodeInfo(
456 oldNodeInfo
->NameAtom(), oldNodeInfo
->GetPrefixAtom(),
457 oldNodeInfo
->NamespaceID(), nsINode::ELEMENT_NODE
);
458 aPrototype
->mNodeInfo
= newNodeInfo
;
460 // First replace the prototype attributes with the new ones from this element.
461 aPrototype
->mAttributes
.Clear();
463 uint32_t count
= aElement
->GetAttrCount();
464 nsXULPrototypeAttribute
* protoAttr
=
465 aPrototype
->mAttributes
.AppendElements(count
);
466 for (uint32_t index
= 0; index
< count
; index
++) {
467 BorrowedAttrInfo attr
= aElement
->GetAttrInfoAt(index
);
469 if (attr
.mName
->IsAtom()) {
470 protoAttr
->mName
.SetTo(attr
.mName
->Atom());
472 NodeInfo
* oldNodeInfo
= attr
.mName
->NodeInfo();
473 RefPtr
<NodeInfo
> newNodeInfo
= mNodeInfoManager
->GetNodeInfo(
474 oldNodeInfo
->NameAtom(), oldNodeInfo
->GetPrefixAtom(),
475 oldNodeInfo
->NamespaceID(), nsINode::ATTRIBUTE_NODE
);
476 protoAttr
->mName
.SetTo(newNodeInfo
);
478 protoAttr
->mValue
.SetTo(*attr
.mValue
);
483 // Make sure the mIsAtom is correct in case this prototype element has been
484 // completely rebuilt.
485 CustomElementData
* ceData
= aElement
->GetCustomElementData();
486 nsAtom
* isAtom
= ceData
? ceData
->GetIs(aElement
) : nullptr;
487 aPrototype
->mIsAtom
= isAtom
;
490 // We have to rebuild the prototype children from this element.
491 // First release the tree under this element.
492 aPrototype
->ReleaseSubtree();
494 RefPtr
<nsXULPrototypeNode
>* children
=
495 aPrototype
->mChildren
.AppendElements(aElement
->GetChildCount());
496 for (nsIContent
* child
= aElement
->GetFirstChild(); child
;
497 child
= child
->GetNextSibling()) {
498 if (child
->IsElement()) {
499 Element
* element
= child
->AsElement();
500 RefPtr
<nsXULPrototypeElement
> elemProto
= new nsXULPrototypeElement
;
501 RebuildPrototypeFromElement(elemProto
, element
, true);
502 *children
= elemProto
;
503 } else if (child
->IsText()) {
504 Text
* text
= child
->AsText();
505 RefPtr
<nsXULPrototypeText
> textProto
= new nsXULPrototypeText();
506 text
->AppendTextTo(textProto
->mValue
);
507 *children
= textProto
;
509 MOZ_ASSERT(false, "We handle only elements and text nodes here.");
517 void nsXULPrototypeDocument::RebuildL10nPrototype(Element
* aElement
,
519 if (mWasL10nCached
) {
523 Document
* doc
= aElement
->OwnerDoc();
526 MOZ_RELEASE_ASSERT(aElement
->GetAttr(nsGkAtoms::datal10nid
, id
));
532 RefPtr
<nsXULPrototypeElement
> proto
= doc
->mL10nProtoElements
.Get(aElement
);
533 MOZ_RELEASE_ASSERT(proto
);
534 RebuildPrototypeFromElement(proto
, aElement
, aDeep
);