gecko-dev/dom/xbl/nsXBLPrototypeBinding.cpp

1577 строки
49 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 "mozilla/ArrayUtils.h"
#include "nsCOMPtr.h"
#include "nsAtom.h"
#include "nsIInputStream.h"
#include "nsNameSpaceManager.h"
#include "nsIURI.h"
#include "nsIURIMutator.h"
#include "nsIURL.h"
#include "nsIChannel.h"
#include "nsString.h"
#include "nsReadableUtils.h"
#include "nsNetUtil.h"
#include "plstr.h"
#include "nsContentCreatorFunctions.h"
#include "mozilla/dom/Document.h"
#include "nsIXMLContentSink.h"
#include "nsContentCID.h"
#include "mozilla/dom/XMLDocument.h"
#include "nsXBLService.h"
#include "nsXBLBinding.h"
#include "nsXBLPrototypeBinding.h"
#include "nsXBLContentSink.h"
#include "xptinfo.h"
#include "nsIDocumentObserver.h"
#include "nsGkAtoms.h"
#include "nsXBLProtoImpl.h"
#include "nsCRT.h"
#include "nsContentUtils.h"
#include "nsTextFragment.h"
#include "nsTextNode.h"
#include "nsIScriptError.h"
#include "nsXBLResourceLoader.h"
#include "mozilla/dom/CDATASection.h"
#include "mozilla/dom/CharacterData.h"
#include "mozilla/dom/Comment.h"
#include "mozilla/dom/Element.h"
#include "mozilla/StyleSheet.h"
#include "mozilla/StyleSheetInlines.h"
#ifdef MOZ_XUL
# include "nsXULElement.h"
#endif
using namespace mozilla;
using namespace mozilla::dom;
// Helper Classes ==============================================================
// nsXBLAttributeEntry and helpers. This class is used to efficiently handle
// attribute changes in anonymous content.
class nsXBLAttributeEntry {
public:
nsXBLAttributeEntry(nsAtom* aSrcAtom, nsAtom* aDstAtom, int32_t aDstNameSpace,
Element* aElement)
: mElement(aElement),
mSrcAttribute(aSrcAtom),
mDstAttribute(aDstAtom),
mDstNameSpace(aDstNameSpace),
mNext(nullptr) {}
~nsXBLAttributeEntry() {
NS_CONTENT_DELETE_LIST_MEMBER(nsXBLAttributeEntry, this, mNext);
}
nsAtom* GetSrcAttribute() { return mSrcAttribute; }
nsAtom* GetDstAttribute() { return mDstAttribute; }
int32_t GetDstNameSpace() { return mDstNameSpace; }
Element* GetElement() { return mElement; }
nsXBLAttributeEntry* GetNext() { return mNext; }
const nsXBLAttributeEntry* GetNext() const { return mNext; }
void SetNext(nsXBLAttributeEntry* aEntry) { mNext = aEntry; }
size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const;
protected:
Element* mElement;
RefPtr<nsAtom> mSrcAttribute;
RefPtr<nsAtom> mDstAttribute;
int32_t mDstNameSpace;
nsXBLAttributeEntry* mNext;
};
size_t nsXBLAttributeEntry::SizeOfIncludingThis(
MallocSizeOf aMallocSizeOf) const {
size_t n = 0;
for (const nsXBLAttributeEntry* entry = this; entry;
entry = entry->GetNext()) {
n += aMallocSizeOf(entry);
}
return n;
}
// =============================================================================
// Implementation //////////////////////////////////////////////////////////////
// Constructors/Destructors
nsXBLPrototypeBinding::nsXBLPrototypeBinding()
: mImplementation(nullptr),
mBaseBinding(nullptr),
mInheritStyle(true),
mCheckedBaseProto(false),
mKeyHandlersRegistered(false),
mBindToUntrustedContent(false),
mSimpleScopeChain(false),
mResources(nullptr),
mXBLDocInfoWeak(nullptr),
mBaseNameSpaceID(kNameSpaceID_None) {
MOZ_COUNT_CTOR(nsXBLPrototypeBinding);
}
nsresult nsXBLPrototypeBinding::Init(const nsACString& aID,
nsXBLDocumentInfo* aInfo,
Element* aElement, bool aFirstBinding) {
nsresult rv;
nsCOMPtr<nsIURI> bindingURI = aInfo->DocumentURI();
// The binding URI might be an immutable URI (e.g. for about: URIs). In that
// case, we'll fail in SetRef below, but that doesn't matter much for now.
if (aFirstBinding) {
mAlternateBindingURI = bindingURI;
}
rv = NS_MutateURI(bindingURI).SetRef(aID).Finalize(mBindingURI);
if (NS_FAILED(rv)) {
// If SetRef failed, mBindingURI should be a clone.
mBindingURI = bindingURI;
}
mXBLDocInfoWeak = aInfo;
// aElement will be null when reading from the cache, but the element will
// still be set later.
if (aElement) {
SetBindingElement(aElement);
}
return NS_OK;
}
bool nsXBLPrototypeBinding::CompareBindingURI(nsIURI* aURI) const {
bool equal = false;
mBindingURI->Equals(aURI, &equal);
if (!equal && mAlternateBindingURI) {
mAlternateBindingURI->Equals(aURI, &equal);
}
return equal;
}
void nsXBLPrototypeBinding::Traverse(
nsCycleCollectionTraversalCallback& cb) const {
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "proto mBinding");
cb.NoteXPCOMChild(mBinding);
if (mResources) {
mResources->Traverse(cb);
}
ImplCycleCollectionTraverse(cb, mInterfaceTable, "proto mInterfaceTable");
}
void nsXBLPrototypeBinding::Unlink() {
if (mImplementation) {
mImplementation->UnlinkJSObjects();
}
if (mResources) {
mResources->Unlink();
}
}
void nsXBLPrototypeBinding::Trace(const TraceCallbacks& aCallbacks,
void* aClosure) const {
if (mImplementation) mImplementation->Trace(aCallbacks, aClosure);
}
void nsXBLPrototypeBinding::Initialize() {
if (Element* content = GetImmediateChild(nsGkAtoms::content)) {
ConstructAttributeTable(content);
}
}
nsXBLPrototypeBinding::~nsXBLPrototypeBinding(void) {
delete mImplementation;
MOZ_COUNT_DTOR(nsXBLPrototypeBinding);
}
void nsXBLPrototypeBinding::SetBasePrototype(nsXBLPrototypeBinding* aBinding) {
if (mBaseBinding == aBinding) return;
if (mBaseBinding) {
NS_ERROR("Base XBL prototype binding is already defined!");
return;
}
mBaseBinding = aBinding;
}
void nsXBLPrototypeBinding::SetBindingElement(Element* aElement) {
mBinding = aElement;
if (mBinding->AttrValueIs(kNameSpaceID_None, nsGkAtoms::inheritstyle,
nsGkAtoms::_false, eCaseMatters))
mInheritStyle = false;
mBindToUntrustedContent = mBinding->AttrValueIs(
kNameSpaceID_None, nsGkAtoms::bindToUntrustedContent, nsGkAtoms::_true,
eCaseMatters);
// TODO(emilio): Should we imply mBindToUntrustedContent -> mSimpleScopeChain?
mSimpleScopeChain =
mBinding->AttrValueIs(kNameSpaceID_None, nsGkAtoms::simpleScopeChain,
nsGkAtoms::_true, eCaseMatters);
}
bool nsXBLPrototypeBinding::GetAllowScripts() const {
return mXBLDocInfoWeak->GetScriptAccess();
}
bool nsXBLPrototypeBinding::LoadResources(nsIContent* aBoundElement) {
if (mResources) {
return mResources->LoadResources(aBoundElement);
}
return true;
}
nsresult nsXBLPrototypeBinding::AddResource(nsAtom* aResourceType,
const nsAString& aSrc) {
EnsureResources();
mResources->AddResource(aResourceType, aSrc);
return NS_OK;
}
nsresult nsXBLPrototypeBinding::FlushSkinSheets() {
if (mResources) return mResources->FlushSkinSheets();
return NS_OK;
}
nsresult nsXBLPrototypeBinding::BindingAttached(nsIContent* aBoundElement) {
if (mImplementation && mImplementation->CompiledMembers() &&
mImplementation->mConstructor)
return mImplementation->mConstructor->Execute(aBoundElement, *this);
return NS_OK;
}
nsresult nsXBLPrototypeBinding::BindingDetached(nsIContent* aBoundElement) {
if (mImplementation && mImplementation->CompiledMembers() &&
mImplementation->mDestructor)
return mImplementation->mDestructor->Execute(aBoundElement, *this);
return NS_OK;
}
nsXBLProtoImplAnonymousMethod* nsXBLPrototypeBinding::GetConstructor() {
if (mImplementation) return mImplementation->mConstructor;
return nullptr;
}
nsXBLProtoImplAnonymousMethod* nsXBLPrototypeBinding::GetDestructor() {
if (mImplementation) return mImplementation->mDestructor;
return nullptr;
}
nsresult nsXBLPrototypeBinding::SetConstructor(
nsXBLProtoImplAnonymousMethod* aMethod) {
if (!mImplementation) return NS_ERROR_FAILURE;
mImplementation->mConstructor = aMethod;
return NS_OK;
}
nsresult nsXBLPrototypeBinding::SetDestructor(
nsXBLProtoImplAnonymousMethod* aMethod) {
if (!mImplementation) return NS_ERROR_FAILURE;
mImplementation->mDestructor = aMethod;
return NS_OK;
}
nsresult nsXBLPrototypeBinding::InstallImplementation(nsXBLBinding* aBinding) {
if (mImplementation)
return mImplementation->InstallImplementation(this, aBinding);
return NS_OK;
}
// XXXbz this duplicates lots of SetAttrs
void nsXBLPrototypeBinding::AttributeChanged(
nsAtom* aAttribute, int32_t aNameSpaceID, bool aRemoveFlag,
Element* aChangedElement, nsIContent* aAnonymousContent, bool aNotify) {
if (!mAttributeTable) return;
InnerAttributeTable* attributesNS = mAttributeTable->Get(aNameSpaceID);
if (!attributesNS) return;
nsXBLAttributeEntry* xblAttr = attributesNS->Get(aAttribute);
if (!xblAttr) return;
// Iterate over the elements in the array.
RefPtr<Element> content = GetImmediateChild(nsGkAtoms::content);
while (xblAttr) {
Element* element = xblAttr->GetElement();
RefPtr<Element> realElement =
LocateInstance(aChangedElement, content, aAnonymousContent, element);
if (realElement) {
// Hold a strong reference here so that the atom doesn't go away during
// UnsetAttr.
RefPtr<nsAtom> dstAttr = xblAttr->GetDstAttribute();
int32_t dstNs = xblAttr->GetDstNameSpace();
if (aRemoveFlag)
realElement->UnsetAttr(dstNs, dstAttr, aNotify);
else {
bool attrPresent = true;
nsAutoString value;
// Check to see if the src attribute is xbl:text. If so, then we need
// to obtain the children of the real element and get the text nodes'
// values.
if (aAttribute == nsGkAtoms::text && aNameSpaceID == kNameSpaceID_XBL) {
nsContentUtils::GetNodeTextContent(aChangedElement, false, value);
value.StripChar(char16_t('\n'));
value.StripChar(char16_t('\r'));
nsAutoString stripVal(value);
stripVal.StripWhitespace();
if (stripVal.IsEmpty()) attrPresent = false;
} else {
attrPresent =
aChangedElement->GetAttr(aNameSpaceID, aAttribute, value);
}
if (attrPresent) realElement->SetAttr(dstNs, dstAttr, value, aNotify);
}
// See if we're the <html> tag in XUL, and see if value is being
// set or unset on us. We may also be a tag that is having
// xbl:text set on us.
if ((dstAttr == nsGkAtoms::text && dstNs == kNameSpaceID_XBL) ||
(realElement->NodeInfo()->Equals(nsGkAtoms::html, kNameSpaceID_XUL) &&
dstAttr == nsGkAtoms::value)) {
// Flush out all our kids.
while (realElement->HasChildren()) {
realElement->RemoveChildNode(realElement->GetFirstChild(), aNotify);
}
if (!aRemoveFlag) {
// Construct a new text node and insert it.
nsAutoString value;
aChangedElement->GetAttr(aNameSpaceID, aAttribute, value);
if (!value.IsEmpty()) {
RefPtr<nsTextNode> textContent =
new nsTextNode(realElement->NodeInfo()->NodeInfoManager());
textContent->SetText(value, true);
realElement->AppendChildTo(textContent, true);
}
}
}
}
xblAttr = xblAttr->GetNext();
}
}
void nsXBLPrototypeBinding::SetBaseTag(int32_t aNamespaceID, nsAtom* aTag) {
mBaseNameSpaceID = aNamespaceID;
mBaseTag = aTag;
}
nsAtom* nsXBLPrototypeBinding::GetBaseTag(int32_t* aNamespaceID) {
if (mBaseTag) {
*aNamespaceID = mBaseNameSpaceID;
return mBaseTag;
}
return nullptr;
}
bool nsXBLPrototypeBinding::ImplementsInterface(REFNSIID aIID) const {
// Check our IID table.
return !!mInterfaceTable.GetWeak(aIID);
}
// Internal helpers ////////////////////////////////////////////////////////////
Element* nsXBLPrototypeBinding::GetImmediateChild(nsAtom* aTag) {
for (nsIContent* child = mBinding->GetFirstChild(); child;
child = child->GetNextSibling()) {
if (child->NodeInfo()->Equals(aTag, kNameSpaceID_XBL)) {
return child->AsElement();
}
}
return nullptr;
}
nsresult nsXBLPrototypeBinding::InitClass(
const nsString& aClassName, JSContext* aContext,
JS::Handle<JSObject*> aScriptObject,
JS::MutableHandle<JSObject*> aClassObject, bool* aNew) {
return nsXBLBinding::DoInitJSClass(aContext, aScriptObject, aClassName, this,
aClassObject, aNew);
}
Element* nsXBLPrototypeBinding::LocateInstance(Element* aBoundElement,
nsIContent* aTemplRoot,
nsIContent* aCopyRoot,
Element* aTemplChild) {
// XXX We will get in trouble if the binding instantiation deviates from the
// template in the prototype.
if (aTemplChild == aTemplRoot || !aTemplChild) return nullptr;
Element* templParent = aTemplChild->GetParentElement();
// We may be disconnected from our parent during cycle collection.
if (!templParent) return nullptr;
nsIContent* copyParent =
templParent == aTemplRoot
? aCopyRoot
: LocateInstance(aBoundElement, aTemplRoot, aCopyRoot, templParent);
if (!copyParent) return nullptr;
nsIContent* child = copyParent->GetChildAt_Deprecated(
templParent->ComputeIndexOf(aTemplChild));
if (child && child->IsElement()) {
return child->AsElement();
}
return nullptr;
}
void nsXBLPrototypeBinding::SetInitialAttributes(
Element* aBoundElement, nsIContent* aAnonymousContent) {
if (!mAttributeTable) {
return;
}
for (auto iter1 = mAttributeTable->Iter(); !iter1.Done(); iter1.Next()) {
InnerAttributeTable* xblAttributes = iter1.UserData();
if (xblAttributes) {
int32_t srcNamespace = iter1.Key();
for (auto iter2 = xblAttributes->Iter(); !iter2.Done(); iter2.Next()) {
// XXXbz this duplicates lots of AttributeChanged
nsXBLAttributeEntry* entry = iter2.UserData();
nsAtom* src = entry->GetSrcAttribute();
nsAutoString value;
bool attrPresent = true;
if (src == nsGkAtoms::text && srcNamespace == kNameSpaceID_XBL) {
nsContentUtils::GetNodeTextContent(aBoundElement, false, value);
value.StripChar(char16_t('\n'));
value.StripChar(char16_t('\r'));
nsAutoString stripVal(value);
stripVal.StripWhitespace();
if (stripVal.IsEmpty()) {
attrPresent = false;
}
} else {
attrPresent = aBoundElement->GetAttr(srcNamespace, src, value);
}
if (attrPresent) {
nsIContent* content = GetImmediateChild(nsGkAtoms::content);
nsXBLAttributeEntry* curr = entry;
while (curr) {
nsAtom* dst = curr->GetDstAttribute();
int32_t dstNs = curr->GetDstNameSpace();
Element* element = curr->GetElement();
Element* realElement = LocateInstance(aBoundElement, content,
aAnonymousContent, element);
if (realElement) {
realElement->SetAttr(dstNs, dst, value, false);
// XXXndeakin shouldn't this be done in lieu of SetAttr?
if ((dst == nsGkAtoms::text && dstNs == kNameSpaceID_XBL) ||
(realElement->NodeInfo()->Equals(nsGkAtoms::html,
kNameSpaceID_XUL) &&
dst == nsGkAtoms::value && !value.IsEmpty())) {
RefPtr<nsTextNode> textContent =
new nsTextNode(realElement->NodeInfo()->NodeInfoManager());
textContent->SetText(value, false);
realElement->AppendChildTo(textContent, false);
}
}
curr = curr->GetNext();
}
}
}
}
}
}
void nsXBLPrototypeBinding::EnsureAttributeTable() {
if (!mAttributeTable) {
mAttributeTable =
new nsClassHashtable<nsUint32HashKey, InnerAttributeTable>(2);
}
}
void nsXBLPrototypeBinding::AddToAttributeTable(int32_t aSourceNamespaceID,
nsAtom* aSourceTag,
int32_t aDestNamespaceID,
nsAtom* aDestTag,
Element* aElement) {
InnerAttributeTable* attributesNS = mAttributeTable->Get(aSourceNamespaceID);
if (!attributesNS) {
attributesNS = new InnerAttributeTable(2);
mAttributeTable->Put(aSourceNamespaceID, attributesNS);
}
nsXBLAttributeEntry* xblAttr =
new nsXBLAttributeEntry(aSourceTag, aDestTag, aDestNamespaceID, aElement);
nsXBLAttributeEntry* entry = attributesNS->Get(aSourceTag);
if (!entry) {
attributesNS->Put(aSourceTag, xblAttr);
} else {
while (entry->GetNext()) entry = entry->GetNext();
entry->SetNext(xblAttr);
}
}
void nsXBLPrototypeBinding::ConstructAttributeTable(Element* aElement) {
// Don't add entries for <children> elements, since those will get
// removed from the DOM when we construct the insertion point table.
if (!aElement->NodeInfo()->Equals(nsGkAtoms::children, kNameSpaceID_XBL)) {
nsAutoString inherits;
aElement->GetAttr(kNameSpaceID_XBL, nsGkAtoms::inherits, inherits);
if (!inherits.IsEmpty()) {
EnsureAttributeTable();
// The user specified at least one attribute.
char* str = ToNewCString(inherits);
char* newStr;
// XXX We should use a strtok function that tokenizes PRUnichars
// so that we don't have to convert from Unicode to ASCII and then back
char* token = nsCRT::strtok(str, ", ", &newStr);
while (token != nullptr) {
// Build an atom out of this attribute.
RefPtr<nsAtom> atom;
int32_t atomNsID = kNameSpaceID_None;
RefPtr<nsAtom> attribute;
int32_t attributeNsID = kNameSpaceID_None;
// Figure out if this token contains a :.
NS_ConvertASCIItoUTF16 attrTok(token);
int32_t index = attrTok.Find("=", true);
nsresult rv;
if (index != -1) {
// This attribute maps to something different.
nsAutoString left, right;
attrTok.Left(left, index);
attrTok.Right(right, attrTok.Length() - index - 1);
rv = nsContentUtils::SplitQName(aElement, left, &attributeNsID,
getter_AddRefs(attribute));
if (NS_FAILED(rv)) return;
rv = nsContentUtils::SplitQName(aElement, right, &atomNsID,
getter_AddRefs(atom));
if (NS_FAILED(rv)) return;
} else {
NS_ConvertASCIItoUTF16 tok(token);
rv = nsContentUtils::SplitQName(aElement, tok, &atomNsID,
getter_AddRefs(atom));
if (NS_FAILED(rv)) return;
attribute = atom;
attributeNsID = atomNsID;
}
AddToAttributeTable(atomNsID, atom, attributeNsID, attribute, aElement);
// Now remove the inherits attribute from the element so that it doesn't
// show up on clones of the element. It is used
// by the template only, and we don't need it anymore.
// XXXdwh Don't do this for XUL elements, since it faults them into
// heavyweight elements. Should nuke from the prototype instead.
// aElement->UnsetAttr(kNameSpaceID_XBL, nsGkAtoms::inherits, false);
token = nsCRT::strtok(newStr, ", ", &newStr);
}
free(str);
}
}
// Recur into our children.
for (nsIContent* child = aElement->GetFirstChild(); child;
child = child->GetNextSibling()) {
if (child->IsElement()) {
ConstructAttributeTable(child->AsElement());
}
}
}
nsresult nsXBLPrototypeBinding::ConstructInterfaceTable(
const nsAString& aImpls) {
if (!aImpls.IsEmpty()) {
// The user specified at least one attribute.
NS_ConvertUTF16toUTF8 utf8impl(aImpls);
char* str = utf8impl.BeginWriting();
char* newStr;
// XXX We should use a strtok function that tokenizes PRUnichars
// so that we don't have to convert from Unicode to ASCII and then back
char* token = nsCRT::strtok(str, ", ", &newStr);
while (token != nullptr) {
// get the InterfaceInfo for the name
const nsXPTInterfaceInfo* iinfo = nsXPTInterfaceInfo::ByName(token);
if (iinfo) {
// Add the iid to our table.
mInterfaceTable.Put(iinfo->IID(), mBinding);
// this block adds the parent interfaces of each interface
// defined in the xbl definition (implements="nsI...")
const nsXPTInterfaceInfo* parentInfo;
// if it has a parent, add it to the table
while ((parentInfo = iinfo->GetParent())) {
// don't add nsISupports to the table
if (parentInfo->IID().Equals(NS_GET_IID(nsISupports))) {
break;
}
// add the iid to the table
mInterfaceTable.Put(parentInfo->IID(), mBinding);
// look for the next parent
iinfo = parentInfo;
}
}
token = nsCRT::strtok(newStr, ", ", &newStr);
}
}
return NS_OK;
}
nsresult nsXBLPrototypeBinding::AddResourceListener(nsIContent* aBoundElement) {
if (!mResources)
return NS_ERROR_FAILURE; // Makes no sense to add a listener when the
// binding has no resources.
mResources->AddResourceListener(aBoundElement);
return NS_OK;
}
void nsXBLPrototypeBinding::CreateKeyHandlers() {
nsXBLPrototypeHandler* curr = mPrototypeHandler;
while (curr) {
RefPtr<nsAtom> eventAtom = curr->GetEventName();
if (eventAtom == nsGkAtoms::keyup || eventAtom == nsGkAtoms::keydown ||
eventAtom == nsGkAtoms::keypress) {
uint8_t phase = curr->GetPhase();
uint8_t type = curr->GetType();
int32_t count = mKeyHandlers.Count();
int32_t i;
nsXBLKeyEventHandler* handler = nullptr;
for (i = 0; i < count; ++i) {
handler = mKeyHandlers[i];
if (handler->Matches(eventAtom, phase, type)) break;
}
if (i == count) {
RefPtr<nsXBLKeyEventHandler> newHandler =
new nsXBLKeyEventHandler(eventAtom, phase, type);
mKeyHandlers.AppendObject(newHandler);
handler = newHandler;
}
if (handler) handler->AddProtoHandler(curr);
}
curr = curr->GetNextHandler();
}
}
class XBLPrototypeSetupCleanup {
public:
XBLPrototypeSetupCleanup(nsXBLDocumentInfo* aDocInfo, const nsACString& aID)
: mDocInfo(aDocInfo), mID(aID) {}
~XBLPrototypeSetupCleanup() {
if (mDocInfo) {
mDocInfo->RemovePrototypeBinding(mID);
}
}
void Disconnect() { mDocInfo = nullptr; }
nsXBLDocumentInfo* mDocInfo;
nsAutoCString mID;
};
nsresult nsXBLPrototypeBinding::Read(nsIObjectInputStream* aStream,
nsXBLDocumentInfo* aDocInfo,
Document* aDocument, uint8_t aFlags) {
mInheritStyle = (aFlags & XBLBinding_Serialize_InheritStyle) ? true : false;
mBindToUntrustedContent =
(aFlags & XBLBinding_Serialize_BindToUntrustedContent) ? true : false;
mSimpleScopeChain =
(aFlags & XBLBinding_Serialize_SimpleScopeChain) ? true : false;
// nsXBLContentSink::ConstructBinding doesn't create a binding with an empty
// id, so we don't here either.
nsAutoCString id;
nsresult rv = aStream->ReadCString(id);
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_TRUE(!id.IsEmpty(), NS_ERROR_FAILURE);
nsAutoCString baseBindingURI;
rv = aStream->ReadCString(baseBindingURI);
NS_ENSURE_SUCCESS(rv, rv);
mCheckedBaseProto = true;
if (!baseBindingURI.IsEmpty()) {
rv = NS_NewURI(getter_AddRefs(mBaseBindingURI), baseBindingURI);
NS_ENSURE_SUCCESS(rv, rv);
}
rv = ReadNamespace(aStream, mBaseNameSpaceID);
NS_ENSURE_SUCCESS(rv, rv);
nsAutoString baseTag;
rv = aStream->ReadString(baseTag);
NS_ENSURE_SUCCESS(rv, rv);
if (!baseTag.IsEmpty()) {
mBaseTag = NS_Atomize(baseTag);
}
mBinding = aDocument->CreateElem(NS_LITERAL_STRING("binding"), nullptr,
kNameSpaceID_XBL);
nsCOMPtr<nsIContent> child;
rv = ReadContentNode(aStream, aDocument, aDocument->NodeInfoManager(),
getter_AddRefs(child));
NS_ENSURE_SUCCESS(rv, rv);
Element* rootElement = aDocument->GetRootElement();
if (rootElement) rootElement->AppendChildTo(mBinding, false);
if (child) {
mBinding->AppendChildTo(child, false);
}
uint32_t interfaceCount;
rv = aStream->Read32(&interfaceCount);
NS_ENSURE_SUCCESS(rv, rv);
for (; interfaceCount > 0; interfaceCount--) {
nsIID iid;
rv = aStream->ReadID(&iid);
NS_ENSURE_SUCCESS(rv, rv);
mInterfaceTable.Put(iid, mBinding);
}
// We're not directly using this AutoJSAPI here, but callees use it via
// AutoJSContext.
AutoJSAPI jsapi;
if (!jsapi.Init(xpc::CompilationScope())) {
return NS_ERROR_UNEXPECTED;
}
bool isFirstBinding = aFlags & XBLBinding_Serialize_IsFirstBinding;
rv = Init(id, aDocInfo, nullptr, isFirstBinding);
NS_ENSURE_SUCCESS(rv, rv);
// We need to set the prototype binding before reading the nsXBLProtoImpl,
// as it may be retrieved within.
rv = aDocInfo->SetPrototypeBinding(id, this);
NS_ENSURE_SUCCESS(rv, rv);
XBLPrototypeSetupCleanup cleanup(aDocInfo, id);
nsAutoCString className;
rv = aStream->ReadCString(className);
NS_ENSURE_SUCCESS(rv, rv);
if (!className.IsEmpty()) {
nsXBLProtoImpl* impl; // NS_NewXBLProtoImpl will set mImplementation for us
NS_NewXBLProtoImpl(this, NS_ConvertUTF8toUTF16(className).get(), &impl);
// This needs to happen after SetPrototypeBinding as calls are made to
// retrieve the mapped bindings from within here. However, if an error
// occurs, the mapping should be removed again so that we don't keep an
// invalid binding around.
rv = mImplementation->Read(aStream, this);
NS_ENSURE_SUCCESS(rv, rv);
}
// Next read in the handlers.
nsXBLPrototypeHandler* previousHandler = nullptr;
do {
XBLBindingSerializeDetails type;
rv = aStream->Read8(&type);
NS_ENSURE_SUCCESS(rv, rv);
if (type == XBLBinding_Serialize_NoMoreItems) break;
NS_ASSERTION(
(type & XBLBinding_Serialize_Mask) == XBLBinding_Serialize_Handler,
"invalid handler type");
nsXBLPrototypeHandler* handler = new nsXBLPrototypeHandler(this);
rv = handler->Read(aStream);
if (NS_FAILED(rv)) {
delete handler;
return rv;
}
if (previousHandler) {
previousHandler->SetNextHandler(handler);
} else {
SetPrototypeHandlers(handler);
}
previousHandler = handler;
} while (1);
if (mBinding) {
while (true) {
XBLBindingSerializeDetails type;
rv = aStream->Read8(&type);
NS_ENSURE_SUCCESS(rv, rv);
if (type != XBLBinding_Serialize_Attribute) {
break;
}
int32_t attrNamespace;
rv = ReadNamespace(aStream, attrNamespace);
NS_ENSURE_SUCCESS(rv, rv);
nsAutoString attrPrefix, attrName, attrValue;
rv = aStream->ReadString(attrPrefix);
NS_ENSURE_SUCCESS(rv, rv);
rv = aStream->ReadString(attrName);
NS_ENSURE_SUCCESS(rv, rv);
rv = aStream->ReadString(attrValue);
NS_ENSURE_SUCCESS(rv, rv);
RefPtr<nsAtom> atomPrefix = NS_Atomize(attrPrefix);
RefPtr<nsAtom> atomName = NS_Atomize(attrName);
mBinding->SetAttr(attrNamespace, atomName, atomPrefix, attrValue, false);
}
}
// Finally, read in the resources.
while (true) {
XBLBindingSerializeDetails type;
rv = aStream->Read8(&type);
NS_ENSURE_SUCCESS(rv, rv);
if (type == XBLBinding_Serialize_NoMoreItems) break;
NS_ASSERTION(
(type & XBLBinding_Serialize_Mask) == XBLBinding_Serialize_Stylesheet ||
(type & XBLBinding_Serialize_Mask) == XBLBinding_Serialize_Image,
"invalid resource type");
nsAutoString src;
rv = aStream->ReadString(src);
NS_ENSURE_SUCCESS(rv, rv);
AddResource(type == XBLBinding_Serialize_Stylesheet ? nsGkAtoms::stylesheet
: nsGkAtoms::image,
src);
}
if (isFirstBinding) {
aDocInfo->SetFirstPrototypeBinding(this);
}
cleanup.Disconnect();
return NS_OK;
}
// static
nsresult nsXBLPrototypeBinding::ReadNewBinding(nsIObjectInputStream* aStream,
nsXBLDocumentInfo* aDocInfo,
Document* aDocument,
uint8_t aFlags) {
// If the Read() succeeds, |binding| will end up being owned by aDocInfo's
// binding table. Otherwise, we must manually delete it.
nsXBLPrototypeBinding* binding = new nsXBLPrototypeBinding();
nsresult rv = binding->Read(aStream, aDocInfo, aDocument, aFlags);
if (NS_FAILED(rv)) {
delete binding;
}
return rv;
}
nsresult nsXBLPrototypeBinding::Write(nsIObjectOutputStream* aStream) {
// This writes out the binding. Note that mCheckedBaseProto,
// mKeyHandlersRegistered and mKeyHandlers are not serialized as they are
// computed on demand.
// We're not directly using this AutoJSAPI here, but callees use it via
// AutoJSContext.
AutoJSAPI jsapi;
if (!jsapi.Init(xpc::CompilationScope())) {
return NS_ERROR_UNEXPECTED;
}
uint8_t flags = mInheritStyle ? XBLBinding_Serialize_InheritStyle : 0;
// mAlternateBindingURI is only set on the first binding.
if (mAlternateBindingURI) {
flags |= XBLBinding_Serialize_IsFirstBinding;
}
if (mBindToUntrustedContent) {
flags |= XBLBinding_Serialize_BindToUntrustedContent;
}
if (mSimpleScopeChain) {
flags |= XBLBinding_Serialize_SimpleScopeChain;
}
nsresult rv = aStream->Write8(flags);
NS_ENSURE_SUCCESS(rv, rv);
nsAutoCString id;
mBindingURI->GetRef(id);
rv = aStream->WriteStringZ(id.get());
NS_ENSURE_SUCCESS(rv, rv);
// write out the extends and display attribute values
nsAutoCString extends;
ResolveBaseBinding();
if (mBaseBindingURI) {
rv = mBaseBindingURI->GetSpec(extends);
NS_ENSURE_SUCCESS(rv, rv);
}
rv = aStream->WriteStringZ(extends.get());
NS_ENSURE_SUCCESS(rv, rv);
rv = WriteNamespace(aStream, mBaseNameSpaceID);
NS_ENSURE_SUCCESS(rv, rv);
nsAutoString baseTag;
if (mBaseTag) {
mBaseTag->ToString(baseTag);
}
rv = aStream->WriteWStringZ(baseTag.get());
NS_ENSURE_SUCCESS(rv, rv);
nsIContent* content = GetImmediateChild(nsGkAtoms::content);
if (content) {
rv = WriteContentNode(aStream, content);
NS_ENSURE_SUCCESS(rv, rv);
} else {
// Write a marker to indicate that there is no content.
rv = aStream->Write8(XBLBinding_Serialize_NoContent);
NS_ENSURE_SUCCESS(rv, rv);
}
// Enumerate and write out the implemented interfaces.
rv = aStream->Write32(mInterfaceTable.Count());
NS_ENSURE_SUCCESS(rv, rv);
for (auto iter = mInterfaceTable.Iter(); !iter.Done(); iter.Next()) {
// We can just write out the ids. The cache will be invalidated when a
// different build is used, so we don't need to worry about ids changing.
aStream->WriteID(iter.Key());
}
// Write out the implementation details.
if (mImplementation) {
rv = mImplementation->Write(aStream, this);
NS_ENSURE_SUCCESS(rv, rv);
} else {
// Write out an empty classname. This indicates that the binding does not
// define an implementation.
rv = aStream->WriteUtf8Z(EmptyString().get());
NS_ENSURE_SUCCESS(rv, rv);
}
// Write out the handlers.
nsXBLPrototypeHandler* handler = mPrototypeHandler;
while (handler) {
rv = handler->Write(aStream);
NS_ENSURE_SUCCESS(rv, rv);
handler = handler->GetNextHandler();
}
aStream->Write8(XBLBinding_Serialize_NoMoreItems);
NS_ENSURE_SUCCESS(rv, rv);
if (mBinding) {
uint32_t attributes = mBinding->GetAttrCount();
nsAutoString attrValue;
for (uint32_t i = 0; i < attributes; ++i) {
BorrowedAttrInfo attrInfo = mBinding->GetAttrInfoAt(i);
const nsAttrName* name = attrInfo.mName;
nsDependentAtomString attrName(attrInfo.mName->LocalName());
attrInfo.mValue->ToString(attrValue);
rv = aStream->Write8(XBLBinding_Serialize_Attribute);
NS_ENSURE_SUCCESS(rv, rv);
rv = WriteNamespace(aStream, name->NamespaceID());
NS_ENSURE_SUCCESS(rv, rv);
nsAtom* prefix = name->GetPrefix();
nsAutoString prefixString;
if (prefix) {
prefix->ToString(prefixString);
}
rv = aStream->WriteWStringZ(prefixString.get());
NS_ENSURE_SUCCESS(rv, rv);
rv = aStream->WriteWStringZ(attrName.get());
NS_ENSURE_SUCCESS(rv, rv);
rv = aStream->WriteWStringZ(attrValue.get());
NS_ENSURE_SUCCESS(rv, rv);
}
}
aStream->Write8(XBLBinding_Serialize_NoMoreItems);
NS_ENSURE_SUCCESS(rv, rv);
// Write out the resources
if (mResources) {
rv = mResources->Write(aStream);
NS_ENSURE_SUCCESS(rv, rv);
}
// Write out an end mark at the end.
return aStream->Write8(XBLBinding_Serialize_NoMoreItems);
}
nsresult nsXBLPrototypeBinding::ReadContentNode(nsIObjectInputStream* aStream,
Document* aDocument,
nsNodeInfoManager* aNim,
nsIContent** aContent) {
*aContent = nullptr;
int32_t namespaceID;
nsresult rv = ReadNamespace(aStream, namespaceID);
NS_ENSURE_SUCCESS(rv, rv);
// There is no content to read so just return.
if (namespaceID == XBLBinding_Serialize_NoContent) return NS_OK;
// If this is a text type, just read the string and return.
if (namespaceID == XBLBinding_Serialize_TextNode ||
namespaceID == XBLBinding_Serialize_CDATANode ||
namespaceID == XBLBinding_Serialize_CommentNode) {
RefPtr<CharacterData> content;
switch (namespaceID) {
case XBLBinding_Serialize_TextNode:
content = new nsTextNode(aNim);
break;
case XBLBinding_Serialize_CDATANode:
content = new CDATASection(aNim);
break;
case XBLBinding_Serialize_CommentNode:
content = new Comment(aNim);
break;
default:
break;
}
nsAutoString text;
rv = aStream->ReadString(text);
NS_ENSURE_SUCCESS(rv, rv);
content->SetText(text, false);
content.forget(aContent);
return NS_OK;
}
// Otherwise, it's an element, so read its tag, attributes and children.
nsAutoString prefix, tag;
rv = aStream->ReadString(prefix);
NS_ENSURE_SUCCESS(rv, rv);
RefPtr<nsAtom> prefixAtom;
if (!prefix.IsEmpty()) prefixAtom = NS_Atomize(prefix);
rv = aStream->ReadString(tag);
NS_ENSURE_SUCCESS(rv, rv);
RefPtr<nsAtom> tagAtom = NS_Atomize(tag);
RefPtr<NodeInfo> nodeInfo = aNim->GetNodeInfo(
tagAtom, prefixAtom, namespaceID, nsINode::ELEMENT_NODE);
uint32_t attrCount;
rv = aStream->Read32(&attrCount);
NS_ENSURE_SUCCESS(rv, rv);
RefPtr<Element> element;
// Create XUL prototype elements, or regular elements for other namespaces.
// This needs to match the code in nsXBLContentSink::CreateElement.
#ifdef MOZ_XUL
if (namespaceID == kNameSpaceID_XUL) {
nsIURI* documentURI = aDocument->GetDocumentURI();
RefPtr<nsXULPrototypeElement> prototype = new nsXULPrototypeElement();
prototype->mNodeInfo = nodeInfo;
nsXULPrototypeAttribute* attrs = nullptr;
if (attrCount > 0) {
attrs = new nsXULPrototypeAttribute[attrCount];
}
prototype->mAttributes = attrs;
prototype->mNumAttributes = attrCount;
for (uint32_t i = 0; i < attrCount; i++) {
rv = ReadNamespace(aStream, namespaceID);
NS_ENSURE_SUCCESS(rv, rv);
nsAutoString prefix, name, val;
rv = aStream->ReadString(prefix);
NS_ENSURE_SUCCESS(rv, rv);
rv = aStream->ReadString(name);
NS_ENSURE_SUCCESS(rv, rv);
rv = aStream->ReadString(val);
NS_ENSURE_SUCCESS(rv, rv);
RefPtr<nsAtom> nameAtom = NS_Atomize(name);
if (namespaceID == kNameSpaceID_None) {
attrs[i].mName.SetTo(nameAtom);
} else {
RefPtr<nsAtom> prefixAtom;
if (!prefix.IsEmpty()) prefixAtom = NS_Atomize(prefix);
RefPtr<NodeInfo> ni = aNim->GetNodeInfo(
nameAtom, prefixAtom, namespaceID, nsINode::ATTRIBUTE_NODE);
attrs[i].mName.SetTo(ni);
}
rv = prototype->SetAttrAt(i, val, documentURI);
NS_ENSURE_SUCCESS(rv, rv);
}
nsresult rv = nsXULElement::CreateFromPrototype(
prototype, aDocument, false, false, getter_AddRefs(element));
NS_ENSURE_SUCCESS(rv, rv);
} else {
#endif
NS_NewElement(getter_AddRefs(element), nodeInfo.forget(), NOT_FROM_PARSER);
for (uint32_t i = 0; i < attrCount; i++) {
rv = ReadNamespace(aStream, namespaceID);
NS_ENSURE_SUCCESS(rv, rv);
nsAutoString prefix, name, val;
rv = aStream->ReadString(prefix);
NS_ENSURE_SUCCESS(rv, rv);
rv = aStream->ReadString(name);
NS_ENSURE_SUCCESS(rv, rv);
rv = aStream->ReadString(val);
NS_ENSURE_SUCCESS(rv, rv);
RefPtr<nsAtom> prefixAtom;
if (!prefix.IsEmpty()) prefixAtom = NS_Atomize(prefix);
RefPtr<nsAtom> nameAtom = NS_Atomize(name);
element->SetAttr(namespaceID, nameAtom, prefixAtom, val, false);
}
#ifdef MOZ_XUL
}
#endif
// Now read the attribute forwarding entries (xbl:inherits)
int32_t srcNamespaceID, destNamespaceID;
rv = ReadNamespace(aStream, srcNamespaceID);
NS_ENSURE_SUCCESS(rv, rv);
while (srcNamespaceID != XBLBinding_Serialize_NoMoreAttributes) {
nsAutoString srcAttribute, destAttribute;
rv = aStream->ReadString(srcAttribute);
NS_ENSURE_SUCCESS(rv, rv);
rv = ReadNamespace(aStream, destNamespaceID);
NS_ENSURE_SUCCESS(rv, rv);
rv = aStream->ReadString(destAttribute);
NS_ENSURE_SUCCESS(rv, rv);
RefPtr<nsAtom> srcAtom = NS_Atomize(srcAttribute);
RefPtr<nsAtom> destAtom = NS_Atomize(destAttribute);
EnsureAttributeTable();
AddToAttributeTable(srcNamespaceID, srcAtom, destNamespaceID, destAtom,
element);
rv = ReadNamespace(aStream, srcNamespaceID);
NS_ENSURE_SUCCESS(rv, rv);
}
// Finally, read in the child nodes.
uint32_t childCount;
rv = aStream->Read32(&childCount);
NS_ENSURE_SUCCESS(rv, rv);
for (uint32_t i = 0; i < childCount; i++) {
nsCOMPtr<nsIContent> child;
ReadContentNode(aStream, aDocument, aNim, getter_AddRefs(child));
// Child may be null if this was a comment for example and can just be
// ignored.
if (child) {
element->AppendChildTo(child, false);
}
}
element.forget(aContent);
return NS_OK;
}
nsresult nsXBLPrototypeBinding::WriteContentNode(nsIObjectOutputStream* aStream,
nsIContent* aNode) {
nsresult rv;
if (!aNode->IsElement()) {
// Text is writen out as a single byte for the type, followed by the text.
uint8_t type = XBLBinding_Serialize_NoContent;
switch (aNode->NodeType()) {
case nsINode::TEXT_NODE:
type = XBLBinding_Serialize_TextNode;
break;
case nsINode::CDATA_SECTION_NODE:
type = XBLBinding_Serialize_CDATANode;
break;
case nsINode::COMMENT_NODE:
type = XBLBinding_Serialize_CommentNode;
break;
default:
break;
}
rv = aStream->Write8(type);
NS_ENSURE_SUCCESS(rv, rv);
nsAutoString content;
aNode->GetText()->AppendTo(content);
return aStream->WriteWStringZ(content.get());
}
// Otherwise, this is an element.
Element* element = aNode->AsElement();
// Write the namespace id followed by the tag name
rv = WriteNamespace(aStream, element->GetNameSpaceID());
NS_ENSURE_SUCCESS(rv, rv);
nsAutoString prefixStr;
element->NodeInfo()->GetPrefix(prefixStr);
rv = aStream->WriteWStringZ(prefixStr.get());
NS_ENSURE_SUCCESS(rv, rv);
rv = aStream->WriteWStringZ(
nsDependentAtomString(element->NodeInfo()->NameAtom()).get());
NS_ENSURE_SUCCESS(rv, rv);
// Write attributes
uint32_t count = element->GetAttrCount();
rv = aStream->Write32(count);
NS_ENSURE_SUCCESS(rv, rv);
uint32_t i;
for (i = 0; i < count; i++) {
// Write out the namespace id, the namespace prefix, the local tag name,
// and the value, in that order.
const BorrowedAttrInfo attrInfo = element->GetAttrInfoAt(i);
const nsAttrName* name = attrInfo.mName;
// XXXndeakin don't write out xbl:inherits?
int32_t namespaceID = name->NamespaceID();
rv = WriteNamespace(aStream, namespaceID);
NS_ENSURE_SUCCESS(rv, rv);
nsAutoString prefixStr;
nsAtom* prefix = name->GetPrefix();
if (prefix) prefix->ToString(prefixStr);
rv = aStream->WriteWStringZ(prefixStr.get());
NS_ENSURE_SUCCESS(rv, rv);
rv = aStream->WriteWStringZ(nsDependentAtomString(name->LocalName()).get());
NS_ENSURE_SUCCESS(rv, rv);
nsAutoString val;
attrInfo.mValue->ToString(val);
rv = aStream->WriteWStringZ(val.get());
NS_ENSURE_SUCCESS(rv, rv);
}
// Write out the attribute fowarding information
if (mAttributeTable) {
for (auto iter1 = mAttributeTable->Iter(); !iter1.Done(); iter1.Next()) {
int32_t srcNamespace = iter1.Key();
InnerAttributeTable* xblAttributes = iter1.UserData();
for (auto iter2 = xblAttributes->Iter(); !iter2.Done(); iter2.Next()) {
nsXBLAttributeEntry* entry = iter2.UserData();
do {
if (entry->GetElement() == element) {
WriteNamespace(aStream, srcNamespace);
aStream->WriteWStringZ(
nsDependentAtomString(entry->GetSrcAttribute()).get());
WriteNamespace(aStream, entry->GetDstNameSpace());
aStream->WriteWStringZ(
nsDependentAtomString(entry->GetDstAttribute()).get());
}
entry = entry->GetNext();
} while (entry);
}
}
}
rv = aStream->Write8(XBLBinding_Serialize_NoMoreAttributes);
NS_ENSURE_SUCCESS(rv, rv);
// Finally, write out the child nodes.
count = element->GetChildCount();
rv = aStream->Write32(count);
NS_ENSURE_SUCCESS(rv, rv);
for (i = 0; i < count; i++) {
rv = WriteContentNode(aStream, element->GetChildAt_Deprecated(i));
NS_ENSURE_SUCCESS(rv, rv);
}
return NS_OK;
}
nsresult nsXBLPrototypeBinding::ReadNamespace(nsIObjectInputStream* aStream,
int32_t& aNameSpaceID) {
uint8_t namespaceID;
nsresult rv = aStream->Read8(&namespaceID);
NS_ENSURE_SUCCESS(rv, rv);
if (namespaceID == XBLBinding_Serialize_CustomNamespace) {
nsAutoString namesp;
rv = aStream->ReadString(namesp);
NS_ENSURE_SUCCESS(rv, rv);
nsContentUtils::NameSpaceManager()->RegisterNameSpace(namesp, aNameSpaceID);
} else {
aNameSpaceID = namespaceID;
}
return NS_OK;
}
nsresult nsXBLPrototypeBinding::WriteNamespace(nsIObjectOutputStream* aStream,
int32_t aNameSpaceID) {
// Namespaces are stored as a single byte id for well-known namespaces.
// This saves time and space as other namespaces aren't very common in
// XBL. If another namespace is used however, the namespace id will be
// XBLBinding_Serialize_CustomNamespace and the string namespace written
// out directly afterwards.
nsresult rv;
if (aNameSpaceID <= kNameSpaceID_LastBuiltin) {
rv = aStream->Write8((int8_t)aNameSpaceID);
NS_ENSURE_SUCCESS(rv, rv);
} else {
rv = aStream->Write8(XBLBinding_Serialize_CustomNamespace);
NS_ENSURE_SUCCESS(rv, rv);
nsAutoString namesp;
nsContentUtils::NameSpaceManager()->GetNameSpaceURI(aNameSpaceID, namesp);
aStream->WriteWStringZ(namesp.get());
}
return NS_OK;
}
bool CheckTagNameWhiteList(int32_t aNameSpaceID, nsAtom* aTagName) {
static Element::AttrValuesArray kValidXULTagNames[] = {
nsGkAtoms::box, nsGkAtoms::browser,
nsGkAtoms::button, nsGkAtoms::hbox,
nsGkAtoms::image, nsGkAtoms::menu,
nsGkAtoms::menubar, nsGkAtoms::menuitem,
nsGkAtoms::menupopup, nsGkAtoms::row,
nsGkAtoms::slider, nsGkAtoms::spacer,
nsGkAtoms::splitter, nsGkAtoms::text,
nsGkAtoms::tree, nullptr};
uint32_t i;
if (aNameSpaceID == kNameSpaceID_XUL) {
for (i = 0; kValidXULTagNames[i]; ++i) {
if (aTagName == kValidXULTagNames[i]) {
return true;
}
}
} else if (aNameSpaceID == kNameSpaceID_SVG &&
aTagName == nsGkAtoms::generic_) {
return true;
}
return false;
}
nsresult nsXBLPrototypeBinding::ResolveBaseBinding() {
if (mCheckedBaseProto) return NS_OK;
mCheckedBaseProto = true;
nsCOMPtr<Document> doc = mXBLDocInfoWeak->GetDocument();
// Check for the presence of 'extends' and 'display' attributes
nsAutoString display, extends;
mBinding->GetAttr(kNameSpaceID_None, nsGkAtoms::extends, extends);
if (extends.IsEmpty()) return NS_OK;
mBinding->GetAttr(kNameSpaceID_None, nsGkAtoms::display, display);
bool hasDisplay = !display.IsEmpty();
nsAutoString value(extends);
// Now slice 'em up to see what we've got.
nsAutoString prefix;
int32_t offset;
if (hasDisplay) {
offset = display.FindChar(':');
if (-1 != offset) {
display.Left(prefix, offset);
display.Cut(0, offset + 1);
}
} else {
offset = extends.FindChar(':');
if (-1 != offset) {
extends.Left(prefix, offset);
extends.Cut(0, offset + 1);
display = extends;
}
}
nsAutoString nameSpace;
if (!prefix.IsEmpty()) {
mBinding->LookupNamespaceURI(prefix, nameSpace);
if (!nameSpace.IsEmpty()) {
int32_t nameSpaceID = nsContentUtils::NameSpaceManager()->GetNameSpaceID(
nameSpace, nsContentUtils::IsChromeDoc(doc));
RefPtr<nsAtom> tagName = NS_Atomize(display);
// Check the white list
if (!CheckTagNameWhiteList(nameSpaceID, tagName)) {
const char16_t* params[] = {display.get()};
nsContentUtils::ReportToConsole(
nsIScriptError::errorFlag, NS_LITERAL_CSTRING("XBL"), nullptr,
nsContentUtils::eXBL_PROPERTIES, "InvalidExtendsBinding", params,
ArrayLength(params), doc->GetDocumentURI());
NS_ASSERTION(
!nsXBLService::IsChromeOrResourceURI(doc->GetDocumentURI()),
"Invalid extends value");
return NS_ERROR_ILLEGAL_VALUE;
}
SetBaseTag(nameSpaceID, tagName);
}
}
if (hasDisplay || nameSpace.IsEmpty()) {
mBinding->UnsetAttr(kNameSpaceID_None, nsGkAtoms::extends, false);
mBinding->UnsetAttr(kNameSpaceID_None, nsGkAtoms::display, false);
return NS_NewURI(getter_AddRefs(mBaseBindingURI), value,
doc->GetDocumentCharacterSet(), doc->GetDocBaseURI());
}
return NS_OK;
}
void nsXBLPrototypeBinding::EnsureResources() {
if (!mResources) {
mResources = new nsXBLPrototypeResources(this);
}
}
void nsXBLPrototypeBinding::AppendStyleSheet(StyleSheet* aSheet) {
EnsureResources();
mResources->AppendStyleSheet(aSheet);
}
void nsXBLPrototypeBinding::RemoveStyleSheet(StyleSheet* aSheet) {
if (!mResources) {
MOZ_ASSERT(false, "Trying to remove a sheet that does not exist.");
return;
}
mResources->RemoveStyleSheet(aSheet);
}
void nsXBLPrototypeBinding::InsertStyleSheetAt(size_t aIndex,
StyleSheet* aSheet) {
EnsureResources();
mResources->InsertStyleSheetAt(aIndex, aSheet);
}
StyleSheet* nsXBLPrototypeBinding::StyleSheetAt(size_t aIndex) const {
MOZ_ASSERT(mResources);
return mResources->StyleSheetAt(aIndex);
}
size_t nsXBLPrototypeBinding::SheetCount() const {
return mResources ? mResources->SheetCount() : 0;
}
bool nsXBLPrototypeBinding::HasStyleSheets() const {
return mResources && mResources->HasStyleSheets();
}
void nsXBLPrototypeBinding::AppendStyleSheetsTo(
nsTArray<StyleSheet*>& aResult) const {
if (mResources) {
mResources->AppendStyleSheetsTo(aResult);
}
}
size_t nsXBLPrototypeBinding::SizeOfIncludingThis(
MallocSizeOf aMallocSizeOf) const {
size_t n = aMallocSizeOf(this);
n += mPrototypeHandler ? mPrototypeHandler->SizeOfIncludingThis(aMallocSizeOf)
: 0;
n += mResources ? mResources->SizeOfIncludingThis(aMallocSizeOf) : 0;
if (mAttributeTable) {
n += mAttributeTable->ShallowSizeOfIncludingThis(aMallocSizeOf);
for (auto iter = mAttributeTable->Iter(); !iter.Done(); iter.Next()) {
InnerAttributeTable* table = iter.UserData();
n += table->ShallowSizeOfIncludingThis(aMallocSizeOf);
for (auto iter2 = table->Iter(); !iter2.Done(); iter2.Next()) {
n += iter2.UserData()->SizeOfIncludingThis(aMallocSizeOf);
}
}
}
n += mInterfaceTable.ShallowSizeOfExcludingThis(aMallocSizeOf);
n += mKeyHandlers.ShallowSizeOfExcludingThis(aMallocSizeOf);
// Measurement of the following members may be added later if DMD finds it
// is worthwhile:
// - mBindingURI
// - mAlternateBindingURI
// - mBaseBindingURI
// - mImplementation
return n;
}