summaryrefslogtreecommitdiffstats
path: root/dom/xul/nsXULPrototypeDocument.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/xul/nsXULPrototypeDocument.cpp')
-rw-r--r--dom/xul/nsXULPrototypeDocument.cpp511
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);
+ }
+}