Bug 1851451 [wpt PR 41799] - Fix WPT CSS Counters test, a=testonly
[gecko.git] / dom / xul / nsXULPrototypeDocument.cpp
blobd4f2b7962bfc88388d4b0b221dc60f7a593fb836
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"
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/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) {
46 ++gRefCnt;
49 nsresult nsXULPrototypeDocument::Init() {
50 mNodeInfoManager = new nsNodeInfoManager(nullptr, nullptr);
51 return NS_OK;
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)
74 NS_INTERFACE_MAP_END
76 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsXULPrototypeDocument)
77 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsXULPrototypeDocument)
79 NS_IMETHODIMP
80 NS_NewXULPrototypeDocument(nsXULPrototypeDocument** aResult) {
81 *aResult = nullptr;
82 RefPtr<nsXULPrototypeDocument> doc = new nsXULPrototypeDocument();
84 nsresult rv = doc->Init();
85 if (NS_FAILED(rv)) {
86 return rv;
89 doc.forget(aResult);
90 return rv;
93 //----------------------------------------------------------------------
95 // nsISerializable methods
98 NS_IMETHODIMP
99 nsXULPrototypeDocument::Read(nsIObjectInputStream* aStream) {
100 nsCOMPtr<nsISupports> supports;
101 nsresult rv = aStream->ReadObject(true, getter_AddRefs(supports));
102 if (NS_FAILED(rv)) {
103 return rv;
105 mURI = do_QueryInterface(supports);
107 // nsIPrincipal mNodeInfoManager->mPrincipal
108 nsAutoCString JSON;
109 rv = aStream->ReadCString(JSON);
110 if (NS_FAILED(rv)) {
111 return rv;
113 nsCOMPtr<nsIPrincipal> principal = mozilla::BasePrincipal::FromJSON(JSON);
115 // Better safe than sorry....
116 mNodeInfoManager->SetDocumentPrincipal(principal);
118 rv = aStream->ReadBoolean(&mWasL10nCached);
119 if (NS_FAILED(rv)) {
120 return rv;
123 mRoot = new nsXULPrototypeElement();
125 // mozilla::dom::NodeInfo table
126 nsTArray<RefPtr<mozilla::dom::NodeInfo>> nodeInfos;
128 uint32_t count, i;
129 rv = aStream->Read32(&count);
130 if (NS_FAILED(rv)) {
131 return rv;
133 nsAutoString namespaceURI, prefixStr, localName;
134 bool prefixIsNull;
135 RefPtr<nsAtom> prefix;
136 for (i = 0; i < count; ++i) {
137 rv = aStream->ReadString(namespaceURI);
138 if (NS_FAILED(rv)) {
139 return rv;
141 rv = aStream->ReadBoolean(&prefixIsNull);
142 if (NS_FAILED(rv)) {
143 return rv;
145 if (prefixIsNull) {
146 prefix = nullptr;
147 } else {
148 rv = aStream->ReadString(prefixStr);
149 if (NS_FAILED(rv)) {
150 return rv;
152 prefix = NS_Atomize(prefixStr);
154 rv = aStream->ReadString(localName);
155 if (NS_FAILED(rv)) {
156 return rv;
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
162 // matter.
163 rv = mNodeInfoManager->GetNodeInfo(localName, prefix, namespaceURI,
164 UINT16_MAX, getter_AddRefs(nodeInfo));
165 if (NS_FAILED(rv)) {
166 return rv;
168 nodeInfos.AppendElement(nodeInfo);
171 // Document contents
172 uint32_t type;
173 while (NS_SUCCEEDED(rv)) {
174 rv = aStream->Read32(&type);
175 if (NS_FAILED(rv)) {
176 return rv;
177 break;
180 if ((nsXULPrototypeNode::Type)type == nsXULPrototypeNode::eType_PI) {
181 RefPtr<nsXULPrototypePI> pi = new nsXULPrototypePI();
183 rv = pi->Deserialize(aStream, this, mURI, &nodeInfos);
184 if (NS_FAILED(rv)) {
185 return rv;
187 rv = AddProcessingInstruction(pi);
188 if (NS_FAILED(rv)) {
189 return rv;
191 } else if ((nsXULPrototypeNode::Type)type ==
192 nsXULPrototypeNode::eType_Element) {
193 rv = mRoot->Deserialize(aStream, this, mURI, &nodeInfos);
194 if (NS_FAILED(rv)) {
195 return rv;
197 break;
198 } else {
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);
213 // Search attributes
214 size_t i;
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);
221 } else {
222 ni = name->NodeInfo();
225 if (aArray.IndexOf(ni) == aArray.NoIndex) {
226 aArray.AppendElement(ni);
230 // Search children
231 for (i = 0; i < aPrototype->mChildren.Length(); ++i) {
232 nsXULPrototypeNode* child = aPrototype->mChildren[i];
233 if (child->mType == nsXULPrototypeNode::eType_Element) {
234 nsresult rv =
235 GetNodeInfos(static_cast<nsXULPrototypeElement*>(child), aArray);
236 NS_ENSURE_SUCCESS(rv, rv);
240 return NS_OK;
243 NS_IMETHODIMP
244 nsXULPrototypeDocument::Write(nsIObjectOutputStream* aStream) {
245 nsresult rv;
247 rv = aStream->WriteCompoundObject(mURI, NS_GET_IID(nsIURI), true);
249 // nsIPrincipal mNodeInfoManager->mPrincipal
250 nsAutoCString JSON;
251 mozilla::BasePrincipal::Cast(mNodeInfoManager->DocumentPrincipal())
252 ->ToJSON(JSON);
253 nsresult tmp = aStream->WriteStringZ(JSON.get());
254 if (NS_FAILED(tmp)) {
255 rv = tmp;
258 #ifdef DEBUG
259 // XXX Worrisome if we're caching things without system principal.
260 if (!mNodeInfoManager->DocumentPrincipal()->IsSystemPrincipal()) {
261 NS_WARNING("Serializing document without system principal");
263 #endif
265 tmp = aStream->WriteBoolean(mWasL10nCached);
266 if (NS_FAILED(tmp)) {
267 rv = tmp;
270 // mozilla::dom::NodeInfo table
271 nsTArray<RefPtr<mozilla::dom::NodeInfo>> nodeInfos;
272 if (mRoot) {
273 tmp = GetNodeInfos(mRoot, nodeInfos);
274 if (NS_FAILED(tmp)) {
275 rv = tmp;
279 uint32_t nodeInfoCount = nodeInfos.Length();
280 tmp = aStream->Write32(nodeInfoCount);
281 if (NS_FAILED(tmp)) {
282 rv = tmp;
284 uint32_t i;
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)) {
293 rv = tmp;
296 nsAutoString prefix;
297 nodeInfo->GetPrefix(prefix);
298 bool nullPrefix = DOMStringIsNull(prefix);
299 tmp = aStream->WriteBoolean(nullPrefix);
300 if (NS_FAILED(tmp)) {
301 rv = tmp;
303 if (!nullPrefix) {
304 tmp = aStream->WriteWStringZ(prefix.get());
305 if (NS_FAILED(tmp)) {
306 rv = tmp;
310 nsAutoString localName;
311 nodeInfo->GetName(localName);
312 tmp = aStream->WriteWStringZ(localName.get());
313 if (NS_FAILED(tmp)) {
314 rv = 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)) {
324 rv = tmp;
328 if (mRoot) {
329 tmp = mRoot->Serialize(aStream, this, &nodeInfos);
330 if (NS_FAILED(tmp)) {
331 rv = tmp;
335 return rv;
338 //----------------------------------------------------------------------
341 nsresult nsXULPrototypeDocument::InitPrincipal(nsIURI* aURI,
342 nsIPrincipal* aPrincipal) {
343 NS_ENSURE_ARG_POINTER(aURI);
345 mURI = aURI;
346 mNodeInfoManager->SetDocumentPrincipal(aPrincipal);
347 return NS_OK;
350 nsIURI* nsXULPrototypeDocument::GetURI() {
351 NS_ASSERTION(mURI, "null URI");
352 return mURI;
355 nsXULPrototypeElement* nsXULPrototypeDocument::GetRootElement() {
356 return mRoot;
359 void nsXULPrototypeDocument::SetRootElement(nsXULPrototypeElement* aElement) {
360 mRoot = 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);
369 return NS_OK;
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,
395 bool* aResult) {
396 nsresult rv = NS_OK;
398 *aResult = mLoaded;
400 if (!mLoaded) {
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));
406 return rv;
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.
415 mLoaded = true;
417 for (uint32_t i = mPrototypeWaiters.Length(); i > 0;) {
418 --i;
419 mPrototypeWaiters[i]();
421 mPrototypeWaiters.Clear();
423 return NS_OK;
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());
452 } else {
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);
461 protoAttr++;
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;
470 if (aDeep) {
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;
489 } else {
490 MOZ_ASSERT(false, "We handle only elements and text nodes here.");
493 children++;
498 void nsXULPrototypeDocument::RebuildL10nPrototype(Element* aElement,
499 bool aDeep) {
500 if (mWasL10nCached) {
501 return;
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);