diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /dom/xul/nsXULPrototypeDocument.cpp | |
parent | Initial commit. (diff) | |
download | firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | dom/xul/nsXULPrototypeDocument.cpp | 511 |
1 files changed, 511 insertions, 0 deletions
diff --git a/dom/xul/nsXULPrototypeDocument.cpp b/dom/xul/nsXULPrototypeDocument.cpp new file mode 100644 index 0000000000..8205c8ea3c --- /dev/null +++ b/dom/xul/nsXULPrototypeDocument.cpp @@ -0,0 +1,511 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsXULPrototypeDocument.h" + +#include "nsXULElement.h" +#include "nsAString.h" +#include "nsIObjectInputStream.h" +#include "nsIObjectOutputStream.h" +#include "nsIPrincipal.h" +#include "nsJSPrincipals.h" +#include "nsIScriptObjectPrincipal.h" +#include "nsIURI.h" +#include "jsapi.h" +#include "jsfriendapi.h" +#include "nsString.h" +#include "nsDOMCID.h" +#include "nsNodeInfoManager.h" +#include "nsContentUtils.h" +#include "nsCCUncollectableMarker.h" +#include "xpcpublic.h" +#include "mozilla/BasePrincipal.h" +#include "mozilla/dom/BindingUtils.h" +#include "nsXULPrototypeCache.h" +#include "mozilla/DeclarationBlock.h" +#include "mozilla/dom/CustomElementRegistry.h" +#include "mozilla/dom/Document.h" +#include "mozilla/dom/Element.h" +#include "mozilla/dom/Text.h" + +using namespace mozilla; +using namespace mozilla::dom; +using mozilla::dom::DestroyProtoAndIfaceCache; + +uint32_t nsXULPrototypeDocument::gRefCnt; + +//---------------------------------------------------------------------- +// +// ctors, dtors, n' stuff +// + +nsXULPrototypeDocument::nsXULPrototypeDocument() + : mRoot(nullptr), mLoaded(false), mCCGeneration(0), mWasL10nCached(false) { + ++gRefCnt; +} + +nsresult nsXULPrototypeDocument::Init() { + mNodeInfoManager = new nsNodeInfoManager(nullptr); + return NS_OK; +} + +nsXULPrototypeDocument::~nsXULPrototypeDocument() { + if (mRoot) mRoot->ReleaseSubtree(); +} + +NS_IMPL_CYCLE_COLLECTION_CLASS(nsXULPrototypeDocument) + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXULPrototypeDocument) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mPrototypeWaiters) +NS_IMPL_CYCLE_COLLECTION_UNLINK_END +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXULPrototypeDocument) + if (nsCCUncollectableMarker::InGeneration(cb, tmp->mCCGeneration)) { + return NS_SUCCESS_INTERRUPTED_TRAVERSE; + } + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRoot) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNodeInfoManager) +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsXULPrototypeDocument) + NS_INTERFACE_MAP_ENTRY(nsISerializable) + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +NS_IMPL_CYCLE_COLLECTING_ADDREF(nsXULPrototypeDocument) +NS_IMPL_CYCLE_COLLECTING_RELEASE(nsXULPrototypeDocument) + +NS_IMETHODIMP +NS_NewXULPrototypeDocument(nsXULPrototypeDocument** aResult) { + *aResult = nullptr; + RefPtr<nsXULPrototypeDocument> doc = new nsXULPrototypeDocument(); + + nsresult rv = doc->Init(); + if (NS_FAILED(rv)) { + return rv; + } + + doc.forget(aResult); + return rv; +} + +//---------------------------------------------------------------------- +// +// nsISerializable methods +// + +NS_IMETHODIMP +nsXULPrototypeDocument::Read(nsIObjectInputStream* aStream) { + nsCOMPtr<nsISupports> supports; + nsresult rv = aStream->ReadObject(true, getter_AddRefs(supports)); + if (NS_FAILED(rv)) { + return rv; + } + mURI = do_QueryInterface(supports); + + // nsIPrincipal mNodeInfoManager->mPrincipal + nsAutoCString JSON; + rv = aStream->ReadCString(JSON); + if (NS_FAILED(rv)) { + return rv; + } + nsCOMPtr<nsIPrincipal> principal = mozilla::BasePrincipal::FromJSON(JSON); + + // Better safe than sorry.... + mNodeInfoManager->SetDocumentPrincipal(principal); + + rv = aStream->ReadBoolean(&mWasL10nCached); + if (NS_FAILED(rv)) { + return rv; + } + + mRoot = new nsXULPrototypeElement(); + + // mozilla::dom::NodeInfo table + nsTArray<RefPtr<mozilla::dom::NodeInfo>> nodeInfos; + + uint32_t count, i; + rv = aStream->Read32(&count); + if (NS_FAILED(rv)) { + return rv; + } + nsAutoString namespaceURI, prefixStr, localName; + bool prefixIsNull; + RefPtr<nsAtom> prefix; + for (i = 0; i < count; ++i) { + rv = aStream->ReadString(namespaceURI); + if (NS_FAILED(rv)) { + return rv; + } + rv = aStream->ReadBoolean(&prefixIsNull); + if (NS_FAILED(rv)) { + return rv; + } + if (prefixIsNull) { + prefix = nullptr; + } else { + rv = aStream->ReadString(prefixStr); + if (NS_FAILED(rv)) { + return rv; + } + prefix = NS_Atomize(prefixStr); + } + rv = aStream->ReadString(localName); + if (NS_FAILED(rv)) { + return rv; + } + + RefPtr<mozilla::dom::NodeInfo> nodeInfo; + // Using UINT16_MAX here as we don't know which nodeinfos will be + // used for attributes and which for elements. And that doesn't really + // matter. + rv = mNodeInfoManager->GetNodeInfo(localName, prefix, namespaceURI, + UINT16_MAX, getter_AddRefs(nodeInfo)); + if (NS_FAILED(rv)) { + return rv; + } + nodeInfos.AppendElement(nodeInfo); + } + + // Document contents + uint32_t type; + while (NS_SUCCEEDED(rv)) { + rv = aStream->Read32(&type); + if (NS_FAILED(rv)) { + return rv; + break; + } + + if ((nsXULPrototypeNode::Type)type == nsXULPrototypeNode::eType_PI) { + RefPtr<nsXULPrototypePI> pi = new nsXULPrototypePI(); + + rv = pi->Deserialize(aStream, this, mURI, &nodeInfos); + if (NS_FAILED(rv)) { + return rv; + } + rv = AddProcessingInstruction(pi); + if (NS_FAILED(rv)) { + return rv; + } + } else if ((nsXULPrototypeNode::Type)type == + nsXULPrototypeNode::eType_Element) { + rv = mRoot->Deserialize(aStream, this, mURI, &nodeInfos); + if (NS_FAILED(rv)) { + return rv; + } + break; + } else { + MOZ_ASSERT_UNREACHABLE("Unexpected prototype node type"); + return NS_ERROR_FAILURE; + } + } + + return NotifyLoadDone(); +} + +static nsresult GetNodeInfos(nsXULPrototypeElement* aPrototype, + nsTArray<RefPtr<mozilla::dom::NodeInfo>>& aArray) { + if (aArray.IndexOf(aPrototype->mNodeInfo) == aArray.NoIndex) { + aArray.AppendElement(aPrototype->mNodeInfo); + } + + // Search attributes + size_t i; + for (i = 0; i < aPrototype->mAttributes.Length(); ++i) { + RefPtr<mozilla::dom::NodeInfo> ni; + nsAttrName* name = &aPrototype->mAttributes[i].mName; + if (name->IsAtom()) { + ni = aPrototype->mNodeInfo->NodeInfoManager()->GetNodeInfo( + name->Atom(), nullptr, kNameSpaceID_None, nsINode::ATTRIBUTE_NODE); + } else { + ni = name->NodeInfo(); + } + + if (aArray.IndexOf(ni) == aArray.NoIndex) { + aArray.AppendElement(ni); + } + } + + // Search children + for (i = 0; i < aPrototype->mChildren.Length(); ++i) { + nsXULPrototypeNode* child = aPrototype->mChildren[i]; + if (child->mType == nsXULPrototypeNode::eType_Element) { + nsresult rv = + GetNodeInfos(static_cast<nsXULPrototypeElement*>(child), aArray); + NS_ENSURE_SUCCESS(rv, rv); + } + } + + return NS_OK; +} + +NS_IMETHODIMP +nsXULPrototypeDocument::Write(nsIObjectOutputStream* aStream) { + nsresult rv; + + rv = aStream->WriteCompoundObject(mURI, NS_GET_IID(nsIURI), true); + + // nsIPrincipal mNodeInfoManager->mPrincipal + nsAutoCString JSON; + mozilla::BasePrincipal::Cast(mNodeInfoManager->DocumentPrincipal()) + ->ToJSON(JSON); + nsresult tmp = aStream->WriteStringZ(JSON.get()); + if (NS_FAILED(tmp)) { + rv = tmp; + } + +#ifdef DEBUG + // XXX Worrisome if we're caching things without system principal. + if (!mNodeInfoManager->DocumentPrincipal()->IsSystemPrincipal()) { + NS_WARNING("Serializing document without system principal"); + } +#endif + + tmp = aStream->WriteBoolean(mWasL10nCached); + if (NS_FAILED(tmp)) { + rv = tmp; + } + + // mozilla::dom::NodeInfo table + nsTArray<RefPtr<mozilla::dom::NodeInfo>> nodeInfos; + if (mRoot) { + tmp = GetNodeInfos(mRoot, nodeInfos); + if (NS_FAILED(tmp)) { + rv = tmp; + } + } + + uint32_t nodeInfoCount = nodeInfos.Length(); + tmp = aStream->Write32(nodeInfoCount); + if (NS_FAILED(tmp)) { + rv = tmp; + } + uint32_t i; + for (i = 0; i < nodeInfoCount; ++i) { + mozilla::dom::NodeInfo* nodeInfo = nodeInfos[i]; + NS_ENSURE_TRUE(nodeInfo, NS_ERROR_FAILURE); + + nsAutoString namespaceURI; + nodeInfo->GetNamespaceURI(namespaceURI); + tmp = aStream->WriteWStringZ(namespaceURI.get()); + if (NS_FAILED(tmp)) { + rv = tmp; + } + + nsAutoString prefix; + nodeInfo->GetPrefix(prefix); + bool nullPrefix = DOMStringIsNull(prefix); + tmp = aStream->WriteBoolean(nullPrefix); + if (NS_FAILED(tmp)) { + rv = tmp; + } + if (!nullPrefix) { + tmp = aStream->WriteWStringZ(prefix.get()); + if (NS_FAILED(tmp)) { + rv = tmp; + } + } + + nsAutoString localName; + nodeInfo->GetName(localName); + tmp = aStream->WriteWStringZ(localName.get()); + if (NS_FAILED(tmp)) { + rv = tmp; + } + } + + // Now serialize the document contents + uint32_t count = mProcessingInstructions.Length(); + for (i = 0; i < count; ++i) { + nsXULPrototypePI* pi = mProcessingInstructions[i]; + tmp = pi->Serialize(aStream, this, &nodeInfos); + if (NS_FAILED(tmp)) { + rv = tmp; + } + } + + if (mRoot) { + tmp = mRoot->Serialize(aStream, this, &nodeInfos); + if (NS_FAILED(tmp)) { + rv = tmp; + } + } + + return rv; +} + +//---------------------------------------------------------------------- +// + +nsresult nsXULPrototypeDocument::InitPrincipal(nsIURI* aURI, + nsIPrincipal* aPrincipal) { + NS_ENSURE_ARG_POINTER(aURI); + + mURI = aURI; + mNodeInfoManager->SetDocumentPrincipal(aPrincipal); + return NS_OK; +} + +nsIURI* nsXULPrototypeDocument::GetURI() { + NS_ASSERTION(mURI, "null URI"); + return mURI; +} + +nsXULPrototypeElement* nsXULPrototypeDocument::GetRootElement() { + return mRoot; +} + +void nsXULPrototypeDocument::SetRootElement(nsXULPrototypeElement* aElement) { + mRoot = aElement; +} + +nsresult nsXULPrototypeDocument::AddProcessingInstruction( + nsXULPrototypePI* aPI) { + MOZ_ASSERT(aPI, "null ptr"); + // XXX(Bug 1631371) Check if this should use a fallible operation as it + // pretended earlier, or change the return type to void. + mProcessingInstructions.AppendElement(aPI); + return NS_OK; +} + +const nsTArray<RefPtr<nsXULPrototypePI>>& +nsXULPrototypeDocument::GetProcessingInstructions() const { + return mProcessingInstructions; +} + +nsIPrincipal* nsXULPrototypeDocument::DocumentPrincipal() { + MOZ_ASSERT(mNodeInfoManager, "missing nodeInfoManager"); + return mNodeInfoManager->DocumentPrincipal(); +} + +void nsXULPrototypeDocument::SetDocumentPrincipal(nsIPrincipal* aPrincipal) { + mNodeInfoManager->SetDocumentPrincipal(aPrincipal); +} + +void nsXULPrototypeDocument::MarkInCCGeneration(uint32_t aCCGeneration) { + mCCGeneration = aCCGeneration; +} + +nsNodeInfoManager* nsXULPrototypeDocument::GetNodeInfoManager() { + return mNodeInfoManager; +} + +nsresult nsXULPrototypeDocument::AwaitLoadDone(Callback&& aCallback, + bool* aResult) { + nsresult rv = NS_OK; + + *aResult = mLoaded; + + if (!mLoaded) { + // XXX(Bug 1631371) Check if this should use a fallible operation as it + // pretended earlier, or change the return type to void. + mPrototypeWaiters.AppendElement(std::move(aCallback)); + } + + return rv; +} + +nsresult nsXULPrototypeDocument::NotifyLoadDone() { + // Call back to each XUL document that raced to start the same + // prototype document load, lost the race, but hit the XUL + // prototype cache because the winner filled the cache with + // the not-yet-loaded prototype object. + + mLoaded = true; + + for (uint32_t i = mPrototypeWaiters.Length(); i > 0;) { + --i; + mPrototypeWaiters[i](); + } + mPrototypeWaiters.Clear(); + + return NS_OK; +} + +void nsXULPrototypeDocument::SetIsL10nCached(bool aIsCached) { + mWasL10nCached = aIsCached; +} + +void nsXULPrototypeDocument::RebuildPrototypeFromElement( + nsXULPrototypeElement* aPrototype, Element* aElement, bool aDeep) { + aPrototype->mHasIdAttribute = aElement->HasID(); + aPrototype->mHasClassAttribute = aElement->MayHaveClass(); + aPrototype->mHasStyleAttribute = aElement->MayHaveStyle(); + NodeInfo* oldNodeInfo = aElement->NodeInfo(); + RefPtr<NodeInfo> newNodeInfo = mNodeInfoManager->GetNodeInfo( + oldNodeInfo->NameAtom(), oldNodeInfo->GetPrefixAtom(), + oldNodeInfo->NamespaceID(), nsINode::ELEMENT_NODE); + aPrototype->mNodeInfo = newNodeInfo; + + // First replace the prototype attributes with the new ones from this element. + aPrototype->mAttributes.Clear(); + + uint32_t count = aElement->GetAttrCount(); + nsXULPrototypeAttribute* protoAttr = + aPrototype->mAttributes.AppendElements(count); + for (uint32_t index = 0; index < count; index++) { + BorrowedAttrInfo attr = aElement->GetAttrInfoAt(index); + + if (attr.mName->IsAtom()) { + protoAttr->mName.SetTo(attr.mName->Atom()); + } else { + NodeInfo* oldNodeInfo = attr.mName->NodeInfo(); + RefPtr<NodeInfo> newNodeInfo = mNodeInfoManager->GetNodeInfo( + oldNodeInfo->NameAtom(), oldNodeInfo->GetPrefixAtom(), + oldNodeInfo->NamespaceID(), nsINode::ATTRIBUTE_NODE); + protoAttr->mName.SetTo(newNodeInfo); + } + protoAttr->mValue.SetTo(*attr.mValue); + + protoAttr++; + } + + // Make sure the mIsAtom is correct in case this prototype element has been + // completely rebuilt. + CustomElementData* ceData = aElement->GetCustomElementData(); + nsAtom* isAtom = ceData ? ceData->GetIs(aElement) : nullptr; + aPrototype->mIsAtom = isAtom; + + if (aDeep) { + // We have to rebuild the prototype children from this element. + // First release the tree under this element. + aPrototype->ReleaseSubtree(); + + RefPtr<nsXULPrototypeNode>* children = + aPrototype->mChildren.AppendElements(aElement->GetChildCount()); + for (nsIContent* child = aElement->GetFirstChild(); child; + child = child->GetNextSibling()) { + if (child->IsElement()) { + Element* element = child->AsElement(); + RefPtr<nsXULPrototypeElement> elemProto = new nsXULPrototypeElement; + RebuildPrototypeFromElement(elemProto, element, true); + *children = elemProto; + } else if (child->IsText()) { + Text* text = child->AsText(); + RefPtr<nsXULPrototypeText> textProto = new nsXULPrototypeText(); + text->AppendTextTo(textProto->mValue); + *children = textProto; + } else { + MOZ_ASSERT(false, "We handle only elements and text nodes here."); + } + + children++; + } + } +} + +void nsXULPrototypeDocument::RebuildL10nPrototype(Element* aElement, + bool aDeep) { + if (mWasL10nCached) { + return; + } + + MOZ_ASSERT(aElement->HasAttr(nsGkAtoms::datal10nid)); + + Document* doc = aElement->OwnerDoc(); + if (RefPtr<nsXULPrototypeElement> proto = + doc->mL10nProtoElements.Get(aElement)) { + RebuildPrototypeFromElement(proto, aElement, aDeep); + } +} |