Bug 564863: Speed up id/name handling by letting elements register/unregister themselves. r=smaug

This commit is contained in:
Jonas Sicking 2010-06-03 18:09:20 -07:00
parent 9658015532
commit f2dbba9fe9
30 changed files with 771 additions and 478 deletions

View File

@ -1350,12 +1350,6 @@ public:
*/
static PRBool URIIsLocalFile(nsIURI *aURI);
/**
* If aContent is an HTML element with a DOM level 0 'name', then
* return the name. Otherwise return null.
*/
static nsIAtom* IsNamedItem(Element* aElement);
/**
* Get the application manifest URI for this document. The manifest URI
* is specified in the manifest= attribute of the root element of the

View File

@ -71,8 +71,8 @@ enum nsLinkState {
// IID for the nsIContent interface
#define NS_ICONTENT_IID \
{ 0x9e3b1a15, 0x72d5, 0x4e4f, \
{ 0x8f, 0x4b, 0x75, 0xde, 0x07, 0x9c, 0x16, 0xdc } }
{ 0x1450010b, 0xcdca, 0x451c, \
{ 0xba, 0xdc, 0x07, 0x90, 0x89, 0x7b, 0xce, 0xb8 } }
/**
* A node of content in a document's content model. This interface
@ -778,7 +778,12 @@ public:
* value of the null-namespace attribute whose name is given by
* GetIDAttributeName(). This may be null if there is no ID.
*/
virtual nsIAtom* GetID() const = 0;
nsIAtom* GetID() const {
if (HasFlag(NODE_HAS_ID)) {
return DoGetID();
}
return nsnull;
}
/**
* Get the class list of this content node (this corresponds to the
@ -915,6 +920,13 @@ public:
virtual PRBool IsEqualNode(nsINode* aOther);
protected:
/**
* Hook for implementing GetID. This is guaranteed to only be
* called if the NODE_HAS_ID flag is set.
*/
virtual nsIAtom* DoGetID() const = 0;
private:
/**
* Hook for implementing GetClasses. This is guaranteed to only be

View File

@ -116,8 +116,8 @@ class Element;
} // namespace mozilla
#define NS_IDOCUMENT_IID \
{ 0xdf6c0752, 0xe780, 0x4576, \
{ 0x95, 0x3c, 0x7e, 0xf1, 0xde, 0x9f, 0xd7, 0xf0 } }
{ 0x3ee6a14b, 0x83b5, 0x4629, \
{ 0x96, 0x9b, 0xe9, 0x84, 0x7c, 0x57, 0x24, 0x3c } }
// Flag for AddStyleSheet().
#define NS_STYLESHEET_FROM_CATALOG (1 << 0)
@ -670,6 +670,17 @@ public:
*/
virtual nsScriptLoader* ScriptLoader() = 0;
/**
* Add/Remove an element to the document's id and name hashes
*/
virtual void AddToIdTable(mozilla::dom::Element* aElement, nsIAtom* aId) = 0;
virtual void RemoveFromIdTable(mozilla::dom::Element* aElement,
nsIAtom* aId) = 0;
virtual void AddToNameTable(mozilla::dom::Element* aElement,
nsIAtom* aName) = 0;
virtual void RemoveFromNameTable(mozilla::dom::Element* aElement,
nsIAtom* aName) = 0;
//----------------------------------------------------------------------
// Document notification API's
@ -802,6 +813,10 @@ public:
{
return mIsRegularHTML;
}
PRBool IsXUL() const
{
return mIsXUL;
}
virtual PRBool IsScriptEnabled() = 0;
@ -1369,8 +1384,7 @@ public:
* It prevents converting nsIDOMElement to mozill:dom::Element which is
* already converted from mozilla::dom::Element.
*/
virtual mozilla::dom::Element* GetElementById(const nsAString& aElementId,
nsresult* aResult) = 0;
virtual mozilla::dom::Element* GetElementById(const nsAString& aElementId) = 0;
protected:
~nsIDocument()
@ -1462,6 +1476,7 @@ protected:
PRPackedBool mShellIsHidden;
PRPackedBool mIsRegularHTML;
PRPackedBool mIsXUL;
// True if we're loaded as data and therefor has any dangerous stuff, such
// as scripts and plugins, disabled.

View File

@ -117,9 +117,10 @@ enum {
NODE_IS_EDITABLE = 0x00000100U,
// Optimizations to quickly check whether element may have ID, class or style
// attributes. Not all element implementations may use these!
NODE_MAY_HAVE_ID = 0x00000200U,
// Set to true if the element has a non-empty id attribute. This can in rare
// cases lie for nsXMLElement, such as when the node has been moved between
// documents with different id mappings.
NODE_HAS_ID = 0x00000200U,
// For all Element nodes, NODE_MAY_HAVE_CLASS is guaranteed to be set if the
// node in fact has a class, but may be set even if it doesn't.
NODE_MAY_HAVE_CLASS = 0x00000400U,
@ -174,8 +175,11 @@ enum {
// Set if the node has the accesskey attribute set.
NODE_HAS_ACCESSKEY = 0x00400000U,
// Set if the node has the accesskey attribute set.
NODE_HAS_NAME = 0x00800000U,
// Four bits for the script-type ID
NODE_SCRIPT_TYPE_OFFSET = 23,
NODE_SCRIPT_TYPE_OFFSET = 24,
NODE_SCRIPT_TYPE_SIZE = 4,

View File

@ -4896,35 +4896,6 @@ nsAutoGCRoot::Shutdown()
NS_IF_RELEASE(sJSRuntimeService);
}
nsIAtom*
nsContentUtils::IsNamedItem(Element* aElement)
{
// Only the content types reflected in Level 0 with a NAME
// attribute are registered. Images, layers and forms always get
// reflected up to the document. Applets and embeds only go
// to the closest container (which could be a form).
nsGenericHTMLElement* elm = nsGenericHTMLElement::FromContent(aElement);
if (!elm) {
return nsnull;
}
nsIAtom* tag = elm->Tag();
if (tag != nsGkAtoms::img &&
tag != nsGkAtoms::form &&
tag != nsGkAtoms::applet &&
tag != nsGkAtoms::embed &&
tag != nsGkAtoms::object) {
return nsnull;
}
const nsAttrValue* val = elm->GetParsedAttr(nsGkAtoms::name);
if (val && val->Type() == nsAttrValue::eAtom) {
return val->GetAtomValue();
}
return nsnull;
}
/* static */
nsIInterfaceRequestor*
nsContentUtils::GetSameOriginChecker()

View File

@ -1454,7 +1454,7 @@ nsDocument::~nsDocument()
// Destroy link map now so we don't waste time removing
// links one by one
DestroyLinkMap();
DestroyElementMaps();
nsAutoScriptBlocker scriptBlocker;
@ -1883,8 +1883,6 @@ nsDocument::ResetToURI(nsIURI *aURI, nsILoadGroup *aLoadGroup,
}
#endif
mIdentifierMap.Clear();
SetPrincipal(nsnull);
mSecurityInfo = nsnull;
@ -1900,7 +1898,7 @@ nsDocument::ResetToURI(nsIURI *aURI, nsILoadGroup *aLoadGroup,
// Destroy link map now so we don't waste time removing
// links one by one
DestroyLinkMap();
DestroyElementMaps();
PRUint32 count = mChildren.ChildCount();
{ // Scope for update
@ -2334,51 +2332,38 @@ nsDocument::GetLastModified(nsAString& aLastModified)
}
void
nsDocument::UpdateNameTableEntry(Element *aElement)
nsDocument::AddToNameTable(Element *aElement, nsIAtom* aName)
{
if (!mIsRegularHTML)
return;
nsIAtom* name = nsContentUtils::IsNamedItem(aElement);
if (!name)
return;
nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(aName);
nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(name);
if (!entry) {
// We're not tracking the elements with this name
return;
// entry is null if we're not tracking the elements with this name
if (entry) {
entry->AddNameElement(aElement);
}
entry->AddNameElement(aElement);
}
void
nsDocument::RemoveFromNameTable(Element *aElement)
nsDocument::RemoveFromNameTable(Element *aElement, nsIAtom* aName)
{
if (!mIsRegularHTML)
// Speed up document teardown
if (!mIsRegularHTML || mIdentifierMap.Count() == 0)
return;
nsIAtom* name = nsContentUtils::IsNamedItem(aElement);
if (!name)
nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(aName);
if (!entry) // Should never be false unless we had OOM when adding the entry
return;
nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(name);
if (!entry) {
// We're not tracking the elements with this name
return;
}
entry->RemoveNameElement(aElement);
}
void
nsDocument::UpdateIdTableEntry(Element *aElement)
nsDocument::AddToIdTable(Element *aElement, nsIAtom* aId)
{
nsIAtom* id = aElement->GetID();
if (!id)
return;
nsIdentifierMapEntry *entry = mIdentifierMap.PutEntry(id);
nsIdentifierMapEntry *entry = mIdentifierMap.PutEntry(aId);
if (entry) { /* True except on OOM */
entry->AddIdElement(aElement);
@ -2386,127 +2371,21 @@ nsDocument::UpdateIdTableEntry(Element *aElement)
}
void
nsDocument::RemoveFromIdTable(Element *aElement)
nsDocument::RemoveFromIdTable(Element *aElement, nsIAtom* aId)
{
nsIAtom* id = aElement->GetID();
if (!id)
return;
NS_ASSERTION(aId, "huhwhatnow?");
nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(id);
if (!entry) /* Should be false unless we had OOM when adding the entry */
// Speed up document teardown
if (mIdentifierMap.Count() == 0) {
return;
}
nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(aId);
if (!entry) // Can be null for XML elements with changing ids.
return;
if (entry->RemoveIdElement(aElement)) {
mIdentifierMap.RemoveEntry(id);
}
}
void
nsDocument::UnregisterNamedItems(nsIContent *aContent)
{
if (!aContent->IsElement()) {
// non-element nodes are not named items nor can they have children.
return;
}
RemoveFromNameTable(aContent->AsElement());
RemoveFromIdTable(aContent->AsElement());
for (nsINode::ChildIterator iter(aContent); !iter.IsDone(); iter.Next()) {
UnregisterNamedItems(iter);
}
}
void
nsDocument::RegisterNamedItems(nsIContent *aContent)
{
if (!aContent->IsElement()) {
// non-element nodes are not named items nor can they have children.
return;
}
UpdateNameTableEntry(aContent->AsElement());
UpdateIdTableEntry(aContent->AsElement());
for (nsINode::ChildIterator iter(aContent); !iter.IsDone(); iter.Next()) {
RegisterNamedItems(iter);
}
}
void
nsDocument::ContentAppended(nsIDocument* aDocument,
nsIContent* aContainer,
nsIContent* aFirstNewContent,
PRInt32 aNewIndexInContainer)
{
NS_ASSERTION(aDocument == this, "unexpected doc");
for (nsINode::ChildIterator iter(aContainer, aNewIndexInContainer);
!iter.IsDone();
iter.Next()) {
RegisterNamedItems(iter);
}
}
void
nsDocument::ContentInserted(nsIDocument* aDocument,
nsIContent* aContainer,
nsIContent* aContent,
PRInt32 aIndexInContainer)
{
NS_ASSERTION(aDocument == this, "unexpected doc");
NS_ABORT_IF_FALSE(aContent, "Null content!");
RegisterNamedItems(aContent);
}
void
nsDocument::ContentRemoved(nsIDocument* aDocument,
nsIContent* aContainer,
nsIContent* aChild,
PRInt32 aIndexInContainer)
{
NS_ASSERTION(aDocument == this, "unexpected doc");
NS_ABORT_IF_FALSE(aChild, "Null content!");
UnregisterNamedItems(aChild);
}
void
nsDocument::AttributeWillChange(nsIDocument* aDocument,
nsIContent* aContent, PRInt32 aNameSpaceID,
nsIAtom* aAttribute, PRInt32 aModType)
{
NS_ABORT_IF_FALSE(aContent && aContent->IsElement(), "Null content!");
NS_PRECONDITION(aAttribute, "Must have an attribute that's changing!");
if (aNameSpaceID != kNameSpaceID_None)
return;
if (aAttribute == nsGkAtoms::name) {
RemoveFromNameTable(aContent->AsElement());
} else if (aAttribute == aContent->GetIDAttributeName()) {
RemoveFromIdTable(aContent->AsElement());
}
}
void
nsDocument::AttributeChanged(nsIDocument* aDocument,
nsIContent* aContent, PRInt32 aNameSpaceID,
nsIAtom* aAttribute, PRInt32 aModType)
{
NS_ASSERTION(aDocument == this, "unexpected doc");
NS_ABORT_IF_FALSE(aContent && aContent->IsElement(), "Null content!");
NS_PRECONDITION(aAttribute, "Must have an attribute that's changing!");
if (aNameSpaceID != kNameSpaceID_None)
return;
if (aAttribute == nsGkAtoms::name) {
UpdateNameTableEntry(aContent->AsElement());
} else if (aAttribute == aContent->GetIDAttributeName()) {
UpdateIdTableEntry(aContent->AsElement());
mIdentifierMap.RemoveEntry(aId);
}
}
@ -3325,7 +3204,7 @@ nsDocument::RemoveChildAt(PRUint32 aIndex, PRBool aNotify, PRBool aMutationEvent
if (oldKid->IsElement()) {
// Destroy the link map up front before we mess with the child list.
DestroyLinkMap();
DestroyElementMaps();
}
nsresult rv =
@ -3859,80 +3738,35 @@ nsDocument::CheckGetElementByIdArg(const nsIAtom* aId)
return PR_TRUE;
}
nsIdentifierMapEntry*
nsDocument::GetElementByIdInternal(nsIAtom* aID)
{
// We don't have to flush before we do the initial hashtable lookup, since if
// the id is already in the hashtable it couldn't have been removed without
// us being notified (all removals notify immediately, as far as I can tell).
// So do the lookup first.
nsIdentifierMapEntry *entry = mIdentifierMap.PutEntry(aID);
NS_ENSURE_TRUE(entry, nsnull);
if (entry->GetIdElement())
return entry;
// Now we have to flush. It could be that we know nothing about this ID yet
// but more content has been added to the document since. Note that we have
// to flush notifications, so that the entry will get updated properly.
// Make sure to stash away the current generation so we can check whether
// the table changes when we flush.
PRUint32 generation = mIdentifierMap.GetGeneration();
FlushPendingNotifications(Flush_ContentAndNotify);
if (generation != mIdentifierMap.GetGeneration()) {
// Table changed, so the entry pointer is no longer valid; look up the
// entry again, adding if necessary (the adding may be necessary in case
// the flush actually deleted entries).
entry = mIdentifierMap.PutEntry(aID);
}
return entry;
}
Element*
nsDocument::GetElementById(const nsAString& aElementId, nsresult *aResult)
nsDocument::GetElementById(const nsAString& aElementId)
{
nsCOMPtr<nsIAtom> idAtom(do_GetAtom(aElementId));
if (!idAtom) {
*aResult = NS_ERROR_OUT_OF_MEMORY;
// This can only fail due to OOM when the atom doesn't exist, in which
// case there can't be an entry for it.
return nsnull;
}
if (!CheckGetElementByIdArg(idAtom)) {
*aResult = NS_OK;
return nsnull;
}
nsIdentifierMapEntry *entry = GetElementByIdInternal(idAtom);
if (!entry) {
*aResult = NS_ERROR_OUT_OF_MEMORY;
return nsnull;
}
*aResult = NS_OK;
return entry->GetIdElement();
nsIdentifierMapEntry *entry = mIdentifierMap.PutEntry(idAtom);
return entry ? entry->GetIdElement() : nsnull;
}
NS_IMETHODIMP
nsDocument::GetElementById(const nsAString& aId, nsIDOMElement** aReturn)
{
nsresult rv;
Element *content = GetElementById(aId, &rv);
Element *content = GetElementById(aId);
if (content) {
rv = CallQueryInterface(content, aReturn);
}
else {
*aReturn = nsnull;
return CallQueryInterface(content, aReturn);
}
return rv;
*aReturn = nsnull;
return NS_OK;
}
Element*
@ -3942,7 +3776,7 @@ nsDocument::AddIDTargetObserver(nsIAtom* aID, IDTargetObserver aObserver,
if (!CheckGetElementByIdArg(aID))
return nsnull;
nsIdentifierMapEntry *entry = GetElementByIdInternal(aID);
nsIdentifierMapEntry *entry = mIdentifierMap.PutEntry(aID);
NS_ENSURE_TRUE(entry, nsnull);
entry->AddContentChangeCallback(aObserver, aData);
@ -3958,9 +3792,6 @@ nsDocument::RemoveIDTargetObserver(nsIAtom* aID,
nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(aID);
if (!entry) {
// We don't need to do the stuff that GetElementByIdInternal does;
// if there's no entry already in mIdentifierMap, then there's no
// callback to remove.
return;
}
@ -7380,12 +7211,13 @@ nsDocument::ForgetLink(Link* aLink)
}
void
nsDocument::DestroyLinkMap()
nsDocument::DestroyElementMaps()
{
#ifdef DEBUG
mStyledLinksCleared = true;
#endif
mStyledLinks.Clear();
mIdentifierMap.Clear();
}
static

View File

@ -650,6 +650,17 @@ public:
*/
virtual nsScriptLoader* ScriptLoader();
/**
* Add/Remove an element to the document's id and name hashes
*/
virtual void AddToIdTable(mozilla::dom::Element* aElement, nsIAtom* aId);
virtual void RemoveFromIdTable(mozilla::dom::Element* aElement,
nsIAtom* aId);
virtual void AddToNameTable(mozilla::dom::Element* aElement,
nsIAtom* aName);
virtual void RemoveFromNameTable(mozilla::dom::Element* aElement,
nsIAtom* aName);
/**
* Add a new observer of document change notifications. Whenever
* content is changed, appended, inserted or removed the observers are
@ -807,13 +818,6 @@ public:
// nsIDOMNSEventTarget
NS_DECL_NSIDOMNSEVENTTARGET
// nsIMutationObserver
NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTEWILLCHANGE
// nsIScriptObjectPrincipal
virtual nsIPrincipal* GetPrincipal();
@ -937,17 +941,10 @@ public:
GetElementsByTagNameNS(const nsAString& aNamespaceURI,
const nsAString& aLocalName);
virtual mozilla::dom::Element *GetElementById(const nsAString& aElementId,
nsresult *aResult);
virtual mozilla::dom::Element *GetElementById(const nsAString& aElementId);
protected:
friend class nsNodeUtils;
void RegisterNamedItems(nsIContent *aContent);
void UnregisterNamedItems(nsIContent *aContent);
void UpdateNameTableEntry(Element *aElement);
void UpdateIdTableEntry(Element *aElement);
void RemoveFromNameTable(Element *aElement);
void RemoveFromIdTable(Element *aElement);
/**
* Check that aId is not empty and log a message to the console
@ -966,8 +963,8 @@ protected:
nsACString& aCharset);
// Call this before the document does something that will unbind all content.
// That will stop us from resolving URIs for all links as they are removed.
void DestroyLinkMap();
// That will stop us from doing a lot of work as each element is removed.
void DestroyElementMaps();
// Refreshes the hrefs of all the links in the document.
void RefreshLinkHrefs();

View File

@ -154,6 +154,9 @@ public:
virtual PRBool IsNodeOfType(PRUint32 aFlags) const;
virtual nsIAtom* DoGetID() const;
virtual nsIAtom *GetIDAttributeName() const;
protected:
nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const;
};
@ -195,6 +198,18 @@ nsDocumentFragment::IsNodeOfType(PRUint32 aFlags) const
return !(aFlags & ~(eCONTENT | eDOCUMENT_FRAGMENT));
}
nsIAtom*
nsDocumentFragment::DoGetID() const
{
return nsnull;
}
nsIAtom*
nsDocumentFragment::GetIDAttributeName() const
{
return nsnull;
}
DOMCI_DATA(DocumentFragment, nsDocumentFragment)
// QueryInterface implementation for nsDocumentFragment

View File

@ -1108,7 +1108,7 @@ nsGenericDOMDataNode::GetCurrentValueAtom()
}
nsIAtom*
nsGenericDOMDataNode::GetID() const
nsGenericDOMDataNode::DoGetID() const
{
return nsnull;
}

View File

@ -248,7 +248,7 @@ public:
virtual already_AddRefed<nsIURI> GetBaseURI() const;
virtual PRBool IsLink(nsIURI** aURI) const;
virtual nsIAtom* GetID() const;
virtual nsIAtom* DoGetID() const;
virtual const nsAttrValue* DoGetClasses() const;
NS_IMETHOD WalkContentStyleRules(nsRuleWalker* aRuleWalker);
virtual nsICSSStyleRule* GetInlineStyleRule();

View File

@ -3249,38 +3249,6 @@ nsGenericElement::DispatchDOMEvent(nsEvent* aEvent,
aPresContext, aEventStatus);
}
nsIAtom*
nsGenericElement::GetID() const
{
if (!HasFlag(NODE_MAY_HAVE_ID)) {
return nsnull;
}
nsIAtom* IDName = GetIDAttributeName();
if (IDName) {
const nsAttrValue* attrVal = mAttrsAndChildren.GetAttr(IDName);
if (attrVal){
if (attrVal->Type() == nsAttrValue::eAtom) {
return attrVal->GetAtomValue();
}
if(attrVal->IsEmptyString()){
return nsnull;
}
// Check if the ID has been stored as a string.
// This would occur if the ID attribute name changed after
// the ID was parsed.
if (attrVal->Type() == nsAttrValue::eString) {
nsAutoString idVal(attrVal->GetStringValue());
// Create an atom from the value and set it into the attribute list.
const_cast<nsAttrValue*>(attrVal)->ParseAtom(idVal);
return attrVal->GetAtomValue();
}
}
}
return nsnull;
}
const nsAttrValue*
nsGenericElement::DoGetClasses() const
{
@ -3371,12 +3339,6 @@ nsGenericElement::GetAttributeChangeHint(const nsIAtom* aAttribute,
return nsChangeHint(0);
}
nsIAtom *
nsGenericElement::GetIDAttributeName() const
{
return mNodeInfo->GetIDAttributeAtom();
}
nsIAtom *
nsGenericElement::GetClassAttributeName() const
{
@ -4732,15 +4694,6 @@ nsGenericElement::ParseAttribute(PRInt32 aNamespaceID,
const nsAString& aValue,
nsAttrValue& aResult)
{
if (aNamespaceID == kNameSpaceID_None &&
aAttribute == GetIDAttributeName() && !aValue.IsEmpty()) {
SetFlags(NODE_MAY_HAVE_ID);
// Store id as an atom. id="" means that the element has no id,
// not that it has an emptystring as the id.
aResult.ParseAtom(aValue);
return PR_TRUE;
}
return PR_FALSE;
}

View File

@ -379,7 +379,6 @@ public:
virtual void UnbindFromTree(PRBool aDeep = PR_TRUE,
PRBool aNullParent = PR_TRUE);
virtual already_AddRefed<nsINodeList> GetChildren(PRInt32 aChildType);
virtual nsIAtom *GetIDAttributeName() const;
virtual nsIAtom *GetClassAttributeName() const;
virtual already_AddRefed<nsINodeInfo> GetExistingAttrNameFromQName(const nsAString& aStr) const;
nsresult SetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
@ -451,7 +450,6 @@ public:
void ListAttributes(FILE* out) const;
#endif
virtual nsIAtom* GetID() const;
virtual const nsAttrValue* DoGetClasses() const;
NS_IMETHOD WalkContentStyleRules(nsRuleWalker* aRuleWalker);
virtual nsICSSStyleRule* GetInlineStyleRule();
@ -994,15 +992,29 @@ protected:
}
/**
* GetContentsAsText will take all the textnodes that are children
* of |this| and concatenate the text in them into aText. It
* completely ignores any non-text-node children of |this|; in
* particular it does not descend into any children of |this| that
* happen to be container elements.
*
* @param aText the resulting text [OUT]
* Add/remove this element to the documents id cache
*/
void GetContentsAsText(nsAString& aText);
void AddToIdTable(nsIAtom* aId) {
NS_ASSERTION(HasFlag(NODE_HAS_ID), "Node lacking NODE_HAS_ID flag");
nsIDocument* doc = GetCurrentDoc();
if (doc && (!IsInAnonymousSubtree() || doc->IsXUL())) {
doc->AddToIdTable(this, aId);
}
}
void RemoveFromIdTable() {
if (HasFlag(NODE_HAS_ID)) {
nsIDocument* doc = GetCurrentDoc();
if (doc) {
nsIAtom* id = DoGetID();
// id can be null during mutation events evilness. Also, XUL elements
// loose their proto attributes during cc-unlink, so this can happen
// during cc-unlink too.
if (id) {
doc->RemoveFromIdTable(this, DoGetID());
}
}
}
}
/**
* Functions to carry out event default actions for links of all types

View File

@ -578,13 +578,6 @@ nsNodeUtils::CloneAndAdopt(nsINode *aNode, PRBool aClone, PRBool aDeep,
aCx, aOldScope, aNewScope, aNodesWithProperties,
clone, getter_AddRefs(child));
NS_ENSURE_SUCCESS(rv, rv);
if (isDeepDocumentClone) {
NS_ASSERTION(child->IsNodeOfType(nsINode::eCONTENT),
"A clone of a child of a node is not nsIContent?");
nsIContent* content = static_cast<nsIContent*>(child.get());
static_cast<nsDocument*>(clone.get())->RegisterNamedItems(content);
}
}
}

View File

@ -71,6 +71,20 @@ nsStyledElement::GetIDAttributeName() const
return nsGkAtoms::id;
}
nsIAtom*
nsStyledElement::DoGetID() const
{
NS_ASSERTION(HasFlag(NODE_HAS_ID), "Unexpected call");
// The nullcheck here is needed because nsGenericElement::UnsetAttr calls
// out to various code between removing the attribute and we get a chance to
// clear the NODE_HAS_ID flag.
const nsAttrValue* attr = mAttrsAndChildren.GetAttr(nsGkAtoms::id);
return attr ? attr->GetAtomValue() : nsnull;
}
const nsAttrValue*
nsStyledElement::DoGetClasses() const
{
@ -93,12 +107,45 @@ nsStyledElement::ParseAttribute(PRInt32 aNamespaceID, nsIAtom* aAttribute,
aResult.ParseAtomArray(aValue);
return PR_TRUE;
}
if (aAttribute == nsGkAtoms::id) {
// Store id as an atom. id="" means that the element has no id,
// not that it has an emptystring as the id.
RemoveFromIdTable();
if (aValue.IsEmpty()) {
UnsetFlags(NODE_HAS_ID);
return PR_FALSE;
}
aResult.ParseAtom(aValue);
SetFlags(NODE_HAS_ID);
AddToIdTable(aResult.GetAtomValue());
return PR_TRUE;
}
}
return nsStyledElementBase::ParseAttribute(aNamespaceID, aAttribute, aValue,
aResult);
}
nsresult
nsStyledElement::UnsetAttr(PRInt32 aNameSpaceID, nsIAtom* aAttribute,
PRBool aNotify)
{
PRBool isId = PR_FALSE;
if (aAttribute == nsGkAtoms::id && aNameSpaceID == kNameSpaceID_None) {
// Have to do this before clearing flag. See RemoveFromIdTable
RemoveFromIdTable();
isId = PR_TRUE;
}
nsresult rv = nsGenericElement::UnsetAttr(aNameSpaceID, aAttribute, aNotify);
if (isId) {
UnsetFlags(NODE_HAS_ID);
}
return rv;
}
NS_IMETHODIMP
nsStyledElement::SetInlineStyleRule(nsICSSStyleRule* aStyleRule, PRBool aNotify)
{
@ -163,13 +210,28 @@ nsStyledElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
aCompileEventHandlers);
NS_ENSURE_SUCCESS(rv, rv);
// XXXbz if we already have a style attr parsed, this won't do
// anything... need to fix that.
ReparseStyleAttribute(PR_FALSE);
if (aDocument && HasFlag(NODE_HAS_ID) && !GetBindingParent()) {
aDocument->AddToIdTable(this, DoGetID());
}
return rv;
if (!IsXUL()) {
// XXXbz if we already have a style attr parsed, this won't do
// anything... need to fix that.
ReparseStyleAttribute(PR_FALSE);
}
return NS_OK;
}
void
nsStyledElement::UnbindFromTree(PRBool aDeep, PRBool aNullParent)
{
RemoveFromIdTable();
nsStyledElementBase::UnbindFromTree(aDeep, aNullParent);
}
// ---------------------------------------------------------------
// Others and helpers

View File

@ -64,9 +64,10 @@ protected:
public:
// nsIContent interface methods for styling
// nsIContent interface methods
virtual nsIAtom* GetClassAttributeName() const;
virtual nsIAtom* GetIDAttributeName() const;
virtual nsIAtom* DoGetID() const;
virtual const nsAttrValue* DoGetClasses() const;
virtual nsICSSStyleRule* GetInlineStyleRule();
@ -75,6 +76,10 @@ public:
virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
nsIContent* aBindingParent,
PRBool aCompileEventHandlers);
virtual void UnbindFromTree(PRBool aDeep, PRBool aNullParent);
virtual nsresult UnsetAttr(PRInt32 aNameSpaceID, nsIAtom* aAttribute,
PRBool aNotify);
protected:

View File

@ -343,6 +343,7 @@ _TEST_FILES2 = \
test_bug475156.html \
bug475156.sjs \
test_bug544642.html \
test_bug564863.xhtml \
test_copypaste.html \
test_bug503481.html \
file_bug503481.sjs \

View File

@ -0,0 +1,328 @@
<!DOCTYPE html [
<!ATTLIST ns:x id ID #REQUIRED>
<!ATTLIST ns2:x id_2 ID #REQUIRED>
]>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:ns="urn:namespace"
xmlns:ns2="urn:namespace">
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=564863
-->
<head>
<title>Test for Bug 564863</title>
<script type="text/javascript" src="/MochiKit/packed.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
<style>
#div_id {
color: rgb(10, 10, 10);
}
#a_id {
color: rgb(20, 20, 20);
}
#xul_id {
color: rgb(30, 30, 30);
}
#svg_id {
color: rgb(40, 40, 40);
}
#ns_id {
color: rgb(50, 50, 50);
}
#ns2_id {
color: rgb(60, 60, 60);
}
</style>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=564863">Mozilla Bug 564863</a>
<!-- Elements to ensure we have nodeinfos with id-attribute set -->
<div><ns:x id="ns-holder"/><ns2:x id_2="ns2-holder"/></div>
<!-- DOM to muck around with for tests -->
<p id="root">
<div id="div_id" />
<a id="a_id" />
<xul:button id="xul_id" />
<svg:svg><svg:g id="svg_id" /></svg:svg>
<ns:x id="ns_id" />
</p>
<pre id="test">
<script class="testbody" type="text/javascript">
<![CDATA[
root = $('root');
div = root.children[0];
a = root.children[1];
xul = root.children[2];
svg = root.children[3].firstChild;
nsx = root.children[4];
var div_cs = getComputedStyle(div, "");
var a_cs = getComputedStyle(a, "");
var xul_cs = getComputedStyle(xul, "");
var svg_cs = getComputedStyle(svg, "");
var nsx_cs = getComputedStyle(nsx, "");
function checkHasId(test) {
// Check computed style first to avoid flushes from hiding problems
checkHasIdNoGEBI(test);
is($("div_id"), div, "div getElementById " + test);
is($("a_id"), a, "a getElementById " + test);
is($("xul_id"), xul, "xul getElementById " + test);
is($("svg_id"), svg, "svg getElementById " + test);
is($("ns_id"), nsx, "ns getElementById " + test);
}
function checkHasIdNoGEBI(test) {
is(div_cs.color, "rgb(10, 10, 10)", "div color " + test);
is(a_cs.color, "rgb(20, 20, 20)", "a color " + test);
is(xul_cs.color, "rgb(30, 30, 30)", "xul color " + test);
is(svg_cs.color, "rgb(40, 40, 40)", "svg color " + test);
is(nsx_cs.color, "rgb(50, 50, 50)", "nsx color " + test);
is(div.id, "div_id", "div id " + test);
is(a.id, "a_id", "a id " + test);
is(xul.id, "xul_id", "xul id " + test);
is(svg.id, "svg_id", "svg id " + test);
is (nsx.getAttribute("id"), "ns_id", "ns id " + test);
}
function checkHasNoId(removed, test) {
is(div_cs.color, "rgb(0, 0, 0)", "div color " + test);
is(a_cs.color, "rgb(0, 0, 0)", "a color " + test);
is(xul_cs.color, "rgb(0, 0, 0)", "xul color " + test);
is(svg_cs.color, "rgb(0, 0, 0)", "svg color " + test);
is(nsx_cs.color, "rgb(0, 0, 0)", "nsx color " + test);
attrValue = removed ? null : "";
is(div.id, "", "div id " + test);
is(a.id, "", "a id " + test);
is(xul.id, "", "xul id " + test);
is(svg.id, "", "svg id " + test);
is(div.getAttribute("id"), attrValue, "div getAttribute " + test);
is(a.getAttribute("id"), attrValue, "a getAttribute " + test);
is(xul.getAttribute("id"), "", "xul getAttribute " + test);
is(svg.getAttribute("id"), attrValue, "svg getAttribute " + test);
is(nsx.getAttribute("id"), attrValue, "ns getAttribute " + test);
is($("div_id"), null, "div getElementById " + test);
is($("a_id"), null, "a getElementById " + test);
is($("xul_id"), null, "xul getElementById " + test);
is($("svg_id"), null, "svg getElementById " + test);
is($("ns_id"), null, "ns getElementById " + test);
}
// Check that dynamic modifications of attribute work
checkHasId("in markup");
div.id = "";
a.id = "";
xul.id = "";
svg.id = "";
nsx.setAttribute("id", "");
checkHasNoId(false, "set to empty");
div.id = "div_id";
a.id = "a_id";
xul.id = "xul_id";
svg.id = "svg_id";
nsx.setAttribute("id", "ns_id");
checkHasId("set using .id");
div.setAttribute("id", "");
a.setAttribute("id", "");
xul.setAttribute("id", "");
svg.setAttribute("id", "");
nsx.setAttribute("id", "");
checkHasNoId(false, "setAttribute to empty");
div.id = "div_id";
a.id = "a_id";
xul.id = "xul_id";
svg.id = "svg_id";
nsx.setAttribute("id", "ns_id");
checkHasId("set again using .id");
div.removeAttribute("id");
a.removeAttribute("id");
xul.removeAttribute("id");
svg.removeAttribute("id");
nsx.removeAttribute("id");
checkHasNoId(true, "removed attribute");
div.setAttribute("id", "div_id");
a.setAttribute("id", "a_id");
xul.setAttribute("id", "xul_id");
svg.setAttribute("id", "svg_id");
nsx.setAttribute("id", "ns_id");
checkHasId("set using setAttribute");
t1 = document.createElement("div");
t1.id = "div_id";
t2 = document.createElement("a");
t2.id = "a_id";
t3 = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", "button");
t3.id = "xul_id";
t4 = document.createElementNS("http://www.w3.org/2000/svg", "g");
t4.id = "svg_id";
t5 = document.createElementNS("urn:namespace", "ns:x");
t5.setAttribute("id", "ns_id");
// Check that inserting elements before/after existing work
function insertAfter(newChild, existing) {
existing.parentNode.insertBefore(newChild, existing.nextSibling);
}
function insertBefore(newChild, existing) {
existing.parentNode.insertBefore(newChild, existing);
}
function removeNode(child) {
child.parentNode.removeChild(child);
}
insertAfter(t1, div);
insertAfter(t2, a);
insertAfter(t3, xul);
insertAfter(t4, svg);
insertAfter(t5, nsx);
checkHasId("inserted after");
insertBefore(t1, div);
insertBefore(t2, a);
insertBefore(t3, xul);
insertBefore(t4, svg);
insertBefore(t5, nsx);
checkHasIdNoGEBI("inserted before");
is($("div_id"), t1, "div getElementById inserted before");
is($("a_id"), t2, "a getElementById inserted before");
is($("xul_id"), t3, "xul getElementById inserted before");
is($("svg_id"), t4, "svg getElementById inserted before");
is($("ns_id"), t5, "ns getElementById inserted before");
t1.removeAttribute("id");
t2.removeAttribute("id");
t3.removeAttribute("id");
t4.removeAttribute("id");
t5.removeAttribute("id");
checkHasId("removed tx attribute");
t1.setAttribute("id", "div_id");
t2.setAttribute("id", "a_id");
t3.setAttribute("id", "xul_id");
t4.setAttribute("id", "svg_id");
t5.setAttribute("id", "ns_id");
checkHasIdNoGEBI("setAttribute before");
is($("div_id"), t1, "div getElementById setAttribute before");
is($("a_id"), t2, "a getElementById setAttribute before");
is($("xul_id"), t3, "xul getElementById setAttribute before");
is($("svg_id"), t4, "svg getElementById setAttribute before");
is($("ns_id"), t5, "ns getElementById setAttribute before");
removeNode(t1);
removeNode(t2);
removeNode(t3);
removeNode(t4);
removeNode(t5);
checkHasId("removed temporaries");
removeNode(div);
removeNode(a);
removeNode(xul);
removeNode(svg);
removeNode(nsx);
checkHasIdNoGEBI("removed node");
// Check that removing an element during UnsetAttr works
is(div.id, "div_id", "div still has id set");
var mutateFired = false;
root.appendChild(div);
var f = function(e) {
div.removeEventListener("DOMAttrModified", f, false);
is(e.target, div, "target is div");
is(div.id, "", "div no longer has id");
is(div.getAttribute("id"), null, "div no longer has id attr");
removeNode(div);
is(div.parentNode, null, "div was removed");
mutateFired = true;
}
div.addEventListener("DOMAttrModified", f, false);
div.removeAttribute("id");
ok(mutateFired, "mutation event fired");
// Check same for XML elements
is(nsx.getAttribute("id"), "ns_id", "nsx still has id set");
mutateFired = false;
root.appendChild(nsx);
var f2 = function(e) {
nsx.removeEventListener("DOMAttrModified", f2, false);
is(e.target, nsx, "target is nsx");
is(nsx.getAttribute("id"), null, "nsx no longer has id attr");
removeNode(nsx);
is(nsx.parentNode, null, "nsx was removed");
mutateFired = true;
}
nsx.addEventListener("DOMAttrModified", f2, false);
nsx.removeAttribute("id");
ok(mutateFired, "mutation event fired");
// Check that modifying the id-name of an XML element works
ok($("ns-holder"), "ns-holder exists");
ok($("ns2-holder"), "ns2-holder exists");
root.appendChild(nsx);
nsx.setAttribute("id", "ns_id");
is(nsx_cs.color, "rgb(50, 50, 50)", "nsx has initial id");
is($("ns_id"), nsx, "gEBI works on old id");
nsx.setAttribute("id_2", "ns2_id");
nsx.prefix = "ns2";
removeNode(nsx);
root.appendChild(nsx);
is(nsx_cs.color, "rgb(60, 60, 60)", "nsx has new id");
is($("ns2_id"), nsx, "gEBI works on new id");
// Need to change id and then CC to ensure that we don't end up with dangling pointers
function gc() {
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
.getInterface(Components.interfaces.nsIDOMWindowUtils)
.garbageCollect();
}
ok($("ns-holder"), "ns-holder exists");
ok($("ns2-holder"), "ns2-holder exists");
tempNode = document.createElementNS("urn:namespace", "ns:x");
tempNode.setAttribute("id", "tempNode_id");
document.body.appendChild(tempNode);
is($("tempNode_id"), tempNode, "tempNode in gEBI table");
tempNode.prefix = "ns2";
removeNode(tempNode);
tempNode = null;
gc();
$("tempNode_id");
ok(true, "Didn't crash when accessing old id");
]]>
</script>
</pre>
</body>
</html>

View File

@ -913,7 +913,7 @@ nsGenericHTMLElement::UpdateEditableState()
return;
}
nsGenericElement::UpdateEditableState();
nsStyledElement::UpdateEditableState();
}
nsresult
@ -927,6 +927,10 @@ nsGenericHTMLElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
NS_ENSURE_SUCCESS(rv, rv);
if (aDocument) {
if (HasFlag(NODE_HAS_NAME)) {
aDocument->
AddToNameTable(this, GetParsedAttr(nsGkAtoms::name)->GetAtomValue());
}
if (HasFlag(NODE_IS_EDITABLE) && GetContentEditableValue() == eTrue) {
nsCOMPtr<nsIHTMLDocument> htmlDocument = do_QueryInterface(aDocument);
if (htmlDocument) {
@ -948,7 +952,7 @@ nsGenericHTMLElement::UnbindFromTree(PRBool aDeep, PRBool aNullParent)
}
}
nsGenericElement::UnbindFromTree(aDeep, aNullParent);
nsStyledElement::UnbindFromTree(aDeep, aNullParent);
}
nsHTMLFormElement*
@ -1161,8 +1165,8 @@ nsGenericHTMLElement::SetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
SetFlags(NODE_MAY_HAVE_CONTENT_EDITABLE_ATTR);
}
nsresult rv = nsGenericElement::SetAttr(aNameSpaceID, aName, aPrefix, aValue,
aNotify);
nsresult rv = nsStyledElement::SetAttr(aNameSpaceID, aName, aPrefix, aValue,
aNotify);
NS_ENSURE_SUCCESS(rv, rv);
if (contentEditable) {
@ -1185,7 +1189,12 @@ nsGenericHTMLElement::UnsetAttr(PRInt32 aNameSpaceID, nsIAtom* aAttribute,
// Check for event handlers
if (aNameSpaceID == kNameSpaceID_None) {
if (aAttribute == nsGkAtoms::contenteditable) {
if (aAttribute == nsGkAtoms::name) {
// Have to do this before clearing flag. See RemoveFromNameTable
RemoveFromNameTable();
UnsetFlags(NODE_HAS_NAME);
}
else if (aAttribute == nsGkAtoms::contenteditable) {
contentEditable = PR_TRUE;
contentEditableChange = GetContentEditableValue() == eTrue ? -1 : 0;
}
@ -1222,6 +1231,15 @@ nsGenericHTMLElement::GetBaseTarget(nsAString& aBaseTarget) const
//----------------------------------------------------------------------
static PRBool
CanHaveName(nsIAtom* aTag)
{
return aTag == nsGkAtoms::img ||
aTag == nsGkAtoms::form ||
aTag == nsGkAtoms::applet ||
aTag == nsGkAtoms::embed ||
aTag == nsGkAtoms::object;
}
PRBool
nsGenericHTMLElement::ParseAttribute(PRInt32 aNamespaceID,
@ -1238,10 +1256,22 @@ nsGenericHTMLElement::ParseAttribute(PRInt32 aNamespaceID,
return aResult.ParseIntWithBounds(aValue, -32768, 32767);
}
if (aAttribute == nsGkAtoms::name && !aValue.IsEmpty()) {
if (aAttribute == nsGkAtoms::name) {
// Store name as an atom. name="" means that the element has no name,
// not that it has an emptystring as the name.
RemoveFromNameTable();
if (aValue.IsEmpty()) {
UnsetFlags(NODE_HAS_NAME);
return PR_FALSE;
}
aResult.ParseAtom(aValue);
if (CanHaveName(Tag())) {
SetFlags(NODE_HAS_NAME);
AddToNameTable(aResult.GetAtomValue());
}
return PR_TRUE;
}
@ -2501,6 +2531,8 @@ nsGenericHTMLFormElement::UnbindFromTree(PRBool aDeep, PRBool aNullParent)
{
// Save state before doing anything
SaveState();
RemoveFromNameTable();
if (mForm) {
// Might need to unset mForm

View File

@ -498,6 +498,26 @@ public:
NS_HIDDEN_(nsresult) GetEditorInternal(nsIEditor** aEditor);
protected:
/**
* Add/remove this element to the documents name cache
*/
void AddToNameTable(nsIAtom* aName) {
NS_ASSERTION(HasFlag(NODE_HAS_NAME), "Node lacking NODE_HAS_NAME flag");
nsIDocument* doc = GetCurrentDoc();
if (doc && !IsInAnonymousSubtree()) {
doc->AddToNameTable(this, aName);
}
}
void RemoveFromNameTable() {
if (HasFlag(NODE_HAS_NAME)) {
nsIDocument* doc = GetCurrentDoc();
if (doc) {
doc->RemoveFromNameTable(this, GetParsedAttr(nsGkAtoms::name)->
GetAtomValue());
}
}
}
/**
* Register or unregister an access key to this element based on the
* accesskey attribute.

View File

@ -436,9 +436,7 @@ nsHTMLLabelElement::GetControlContent()
return nsnull;
}
nsresult rv;
nsIContent* content = doc->GetElementById(elementId, &rv);
NS_ENSURE_SUCCESS(rv, nsnull);
nsIContent* content = doc->GetElementById(elementId);
if (!content) {
return nsnull;
}

View File

@ -2635,26 +2635,18 @@ FindNamedItems(nsIAtom* aName, nsIContent *aContent,
NS_ASSERTION(!aEntry->IsInvalidName(),
"Entry that should never have a list passed to FindNamedItems()!");
if (!aContent->IsElement()) {
// non-elements are not named items nor can they have children.
return;
if (aContent->HasFlag(NODE_HAS_NAME)) {
NS_ASSERTION(nsGenericHTMLElement::FromContent(aContent),
"Only HTML Elements should have a name");
nsGenericHTMLElement* elm = static_cast<nsGenericHTMLElement*>(aContent);
if (elm->GetParsedAttr(nsGkAtoms::name)->GetAtomValue() == aName) {
aEntry->AddNameElement(elm);
}
}
Element* element = aContent->AsElement();
if (aName == nsContentUtils::IsNamedItem(element)) {
aEntry->AddNameElement(element);
}
if (!aEntry->GetIdElement() &&
// Maybe this node has the right id?
aName == element->GetID()) {
aEntry->AddIdElement(element);
}
PRUint32 i, count = element->GetChildCount();
for (i = 0; i < count; ++i) {
FindNamedItems(aName, element->GetChildAt(i), aEntry);
for (nsINode::ChildIterator iter(aContent); !iter.IsDone(); iter.Next()) {
FindNamedItems(aName, iter, aEntry);
}
}
@ -2677,27 +2669,7 @@ nsHTMLDocument::ResolveName(const nsAString& aName,
return NS_OK;
}
// Now we know we _might_ have items. Before looking at
// entry->mNameContentList, make sure to flush out content (see
// bug 69826).
// This is a perf killer while the document is loading!
// Make sure to stash away the current generation so we can check whether the
// table changes when we flush.
PRUint32 generation = mIdentifierMap.GetGeneration();
// If we already have an entry->mNameContentList, we need to flush out
// notifications too, so that it will get updated properly.
FlushPendingNotifications(entry->HasNameContentList() ?
Flush_ContentAndNotify : Flush_Content);
if (generation != mIdentifierMap.GetGeneration()) {
// Table changed, so the entry pointer is no longer valid; look up the
// entry again, adding if necessary (the adding may be necessary in case
// the flush actually deleted entries).
entry = mIdentifierMap.PutEntry(name);
NS_ENSURE_TRUE(entry, NS_ERROR_OUT_OF_MEMORY);
}
// Now we know we _might_ have items.
if (!entry->HasNameContentList()) {
#ifdef DEBUG_jst

View File

@ -240,10 +240,9 @@ public:
virtual NS_HIDDEN_(void) RemovedFromDocShell();
virtual mozilla::dom::Element *GetElementById(const nsAString& aElementId,
nsresult *aResult)
virtual mozilla::dom::Element *GetElementById(const nsAString& aElementId)
{
return nsDocument::GetElementById(aElementId, aResult);
return nsDocument::GetElementById(aElementId);
}
protected:

View File

@ -63,8 +63,113 @@ NS_INTERFACE_TABLE_HEAD(nsXMLElement)
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(Element)
NS_ELEMENT_INTERFACE_MAP_END
NS_IMPL_ADDREF_INHERITED(nsXMLElement, nsGenericElement)
NS_IMPL_RELEASE_INHERITED(nsXMLElement, nsGenericElement)
NS_IMPL_ELEMENT_CLONE(nsXMLElement)
nsresult
nsXMLElement::UnsetAttr(PRInt32 aNameSpaceID, nsIAtom* aAttribute,
PRBool aNotify)
{
PRBool isId = PR_FALSE;
if (aAttribute == GetIDAttributeName() &&
aNameSpaceID == kNameSpaceID_None) {
// Have to do this before clearing flag. See RemoveFromIdTable
RemoveFromIdTable();
isId = PR_TRUE;
}
nsresult rv = nsGenericElement::UnsetAttr(aNameSpaceID, aAttribute, aNotify);
if (isId) {
UnsetFlags(NODE_HAS_ID);
}
return rv;
}
nsIAtom *
nsXMLElement::GetIDAttributeName() const
{
return mNodeInfo->GetIDAttributeAtom();
}
nsIAtom*
nsXMLElement::DoGetID() const
{
NS_ASSERTION(HasFlag(NODE_HAS_ID), "Unexpected call");
nsIAtom* IDName = GetIDAttributeName();
if (IDName) {
const nsAttrValue* attrVal = mAttrsAndChildren.GetAttr(IDName);
if (attrVal) {
if (attrVal->Type() == nsAttrValue::eAtom) {
return attrVal->GetAtomValue();
}
if (attrVal->IsEmptyString()) {
return nsnull;
}
// Check if the ID has been stored as a string.
// This would occur if the ID attribute name changed after
// the ID was parsed.
if (attrVal->Type() == nsAttrValue::eString) {
nsAutoString idVal(attrVal->GetStringValue());
// Create an atom from the value and set it into the attribute list.
const_cast<nsAttrValue*>(attrVal)->ParseAtom(idVal);
return attrVal->GetAtomValue();
}
}
}
return nsnull;
}
PRBool
nsXMLElement::ParseAttribute(PRInt32 aNamespaceID,
nsIAtom* aAttribute,
const nsAString& aValue,
nsAttrValue& aResult)
{
if (aAttribute == GetIDAttributeName() &&
aNamespaceID == kNameSpaceID_None) {
// Store id as an atom. id="" means that the element has no id,
// not that it has an emptystring as the id.
RemoveFromIdTable();
if (aValue.IsEmpty()) {
UnsetFlags(NODE_HAS_ID);
return PR_FALSE;
}
aResult.ParseAtom(aValue);
SetFlags(NODE_HAS_ID);
AddToIdTable(aResult.GetAtomValue());
return PR_TRUE;
}
return PR_FALSE;
}
nsresult
nsXMLElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
nsIContent* aBindingParent,
PRBool aCompileEventHandlers)
{
nsresult rv = nsGenericElement::BindToTree(aDocument, aParent,
aBindingParent,
aCompileEventHandlers);
NS_ENSURE_SUCCESS(rv, rv);
if (aDocument && HasFlag(NODE_HAS_ID) && !GetBindingParent()) {
aDocument->AddToIdTable(this, DoGetID());
}
return NS_OK;
}
void
nsXMLElement::UnbindFromTree(PRBool aDeep, PRBool aNullParent)
{
RemoveFromIdTable();
return nsGenericElement::UnbindFromTree(aDeep, aNullParent);
}

View File

@ -62,6 +62,20 @@ public:
// nsINode interface methods
virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const;
// nsIContent interface methods
virtual nsIAtom *GetIDAttributeName() const;
virtual nsIAtom* DoGetID() const;
virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
nsIContent* aBindingParent,
PRBool aCompileEventHandlers);
virtual void UnbindFromTree(PRBool aDeep, PRBool aNullParent);
virtual nsresult UnsetAttr(PRInt32 aNameSpaceID, nsIAtom* aAttribute,
PRBool aNotify);
virtual PRBool ParseAttribute(PRInt32 aNamespaceID,
nsIAtom* aAttribute,
const nsAString& aValue,
nsAttrValue& aResult);
};
#endif // nsXMLElement_h___

View File

@ -272,7 +272,7 @@ nsXULElement::Create(nsXULPrototypeElement* aPrototype, nsINodeInfo *aNodeInfo,
element->mPrototype = aPrototype;
if (aPrototype->mHasIdAttribute) {
element->SetFlags(NODE_MAY_HAVE_ID);
element->SetFlags(NODE_HAS_ID);
}
if (aPrototype->mHasClassAttribute) {
element->SetFlags(NODE_MAY_HAVE_CLASS);
@ -874,12 +874,9 @@ nsXULElement::BindToTree(nsIDocument* aDocument,
nsIContent* aBindingParent,
PRBool aCompileEventHandlers)
{
// Calling the nsStyledElementBase method on purpose to skip over
// nsStyledElement, since we don't want the style attribute
// reparsing it does.
nsresult rv = nsStyledElementBase::BindToTree(aDocument, aParent,
aBindingParent,
aCompileEventHandlers);
nsresult rv = nsStyledElement::BindToTree(aDocument, aParent,
aBindingParent,
aCompileEventHandlers);
NS_ENSURE_SUCCESS(rv, rv);
if (aDocument) {
@ -1306,6 +1303,13 @@ nsXULElement::UnsetAttr(PRInt32 aNameSpaceID, nsIAtom* aName, PRBool aNotify)
NS_ENSURE_SUCCESS(rv, rv);
}
PRBool isId = PR_FALSE;
if (aName == nsGkAtoms::id && aNameSpaceID == kNameSpaceID_None) {
// Have to do this before clearing flag. See RemoveFromIdTable
RemoveFromIdTable();
isId = PR_TRUE;
}
PRInt32 index = mAttrsAndChildren.IndexOfAttr(aName, aNameSpaceID);
if (index < 0) {
NS_ASSERTION(!protoattr, "we used to have a protoattr, we should now "
@ -1359,6 +1363,10 @@ nsXULElement::UnsetAttr(PRInt32 aNameSpaceID, nsIAtom* aName, PRBool aNotify)
// other things.
// XXX Know how to remove POPUP event listeners when an attribute is unset?
if (isId) {
UnsetFlags(NODE_HAS_ID);
}
if (aNameSpaceID == kNameSpaceID_None) {
if (aName == nsGkAtoms::hidechrome &&
mNodeInfo->Equals(nsGkAtoms::window)) {
@ -1717,26 +1725,22 @@ nsXULElement::GetBuilder(nsIXULTemplateBuilder** aBuilder)
//----------------------------------------------------------------------
// Implementation methods
/// XXX GetID must be defined here because we have proto attrs.
// XXX DoGetID and DoGetClasses must be defined here because we have proto
// attributes.
nsIAtom*
nsXULElement::GetID() const
nsXULElement::DoGetID() const
{
if (!HasFlag(NODE_MAY_HAVE_ID)) {
return nsnull;
}
NS_ASSERTION(HasFlag(NODE_HAS_ID), "Unexpected call");
const nsAttrValue* attr =
FindLocalOrProtoAttr(kNameSpaceID_None, nsGkAtoms::id);
const nsAttrValue* attrVal = FindLocalOrProtoAttr(kNameSpaceID_None, nsGkAtoms::id);
// We need the nullcheck here because during unlink the prototype looses
// all of its attributes. We might want to change that.
// The nullcheck would also be needed if we make UnsetAttr use
// nsGenericElement::UnsetAttr as that calls out to various code between
// removing the attribute and clearing the NODE_HAS_ID flag.
NS_ASSERTION(!attrVal ||
attrVal->Type() == nsAttrValue::eAtom ||
(attrVal->Type() == nsAttrValue::eString &&
attrVal->GetStringValue().IsEmpty()),
"unexpected attribute type");
if (attrVal && attrVal->Type() == nsAttrValue::eAtom) {
return attrVal->GetAtomValue();
}
return nsnull;
return attr ? attr->GetAtomValue() : nsnull;
}
const nsAttrValue*

View File

@ -528,7 +528,7 @@ public:
virtual nsIContent *GetBindingParent() const;
virtual PRBool IsNodeOfType(PRUint32 aFlags) const;
virtual PRBool IsFocusable(PRInt32 *aTabIndex = nsnull, PRBool aWithMouse = PR_FALSE);
virtual nsIAtom* GetID() const;
virtual nsIAtom* DoGetID() const;
virtual const nsAttrValue* DoGetClasses() const;
NS_IMETHOD WalkContentStyleRules(nsRuleWalker* aRuleWalker);

View File

@ -47,10 +47,10 @@ class nsIContent;
class nsIScriptGlobalObjectOwner;
// {E486EA2A-5B37-4107-905F-EE062FB4FF97}
// 3e872e97-b678-418e-a7e3-41b8305d4e75
#define NS_IXULDOCUMENT_IID \
{ 0xe486ea2a, 0x5b37, 0x4107, \
{ 0x90, 0x5f, 0xee, 0x06, 0x2f, 0xb4, 0xff, 0x97 }}
{ 0x3e872e97, 0xb678, 0x418e, \
{ 0xa7, 0xe3, 0x41, 0xb8, 0x30, 0x5d, 0x4e, 0x75 } }
/*
@ -63,14 +63,6 @@ class nsIXULDocument : public nsISupports
public:
NS_DECLARE_STATIC_IID_ACCESSOR(NS_IXULDOCUMENT_IID)
// The resource-to-element map is a one-to-many mapping of RDF
// resources to content elements.
/**
* Add an entry to the ID-to-element map.
*/
NS_IMETHOD AddElementForID(nsIContent* aElement) = 0;
/**
* Get the elements for a particular resource --- all elements whose 'id'
* or 'ref' is aID. The nsCOMArray will be truncated and filled in with

View File

@ -238,6 +238,7 @@ nsXULDocument::nsXULDocument(void)
mCharacterSet.AssignLiteral("UTF-8");
mDefaultElementType = kNameSpaceID_XUL;
mIsXUL = PR_TRUE;
mDelayFrameLoaderInitialization = PR_TRUE;
}
@ -980,9 +981,6 @@ nsXULDocument::AttributeWillChange(nsIDocument* aDocument,
(aAttribute == nsGkAtoms::id && !aContent->GetIDAttributeName())) {
RemoveElementFromRefMap(aContent->AsElement());
}
nsXMLDocument::AttributeWillChange(aDocument, aContent, aNameSpaceID,
aAttribute, aModType);
}
void
@ -995,10 +993,6 @@ nsXULDocument::AttributeChanged(nsIDocument* aDocument,
// XXXbz once we change AttributeChanged to take Element, we can nix this line
Element* aElement = aElementContent->AsElement();
// Do this here so that all the exit paths below don't leave this undone
nsXMLDocument::AttributeChanged(aDocument, aElement, aNameSpaceID,
aAttribute, aModType);
// XXXbz check aNameSpaceID, dammit!
// See if we need to update our ref map.
if (aAttribute == nsGkAtoms::ref ||
@ -1100,9 +1094,6 @@ nsXULDocument::ContentAppended(nsIDocument* aDocument,
cur = cur->GetNextSibling()) {
rv = AddSubtreeToDocument(cur);
}
nsXMLDocument::ContentAppended(aDocument, aContainer, aFirstNewContent,
aNewIndexInContainer);
}
void
@ -1114,8 +1105,6 @@ nsXULDocument::ContentInserted(nsIDocument* aDocument,
NS_ASSERTION(aDocument == this, "unexpected doc");
AddSubtreeToDocument(aChild);
nsXMLDocument::ContentInserted(aDocument, aContainer, aChild, aIndexInContainer);
}
void
@ -1127,8 +1116,6 @@ nsXULDocument::ContentRemoved(nsIDocument* aDocument,
NS_ASSERTION(aDocument == this, "unexpected doc");
RemoveSubtreeFromDocument(aChild);
nsXMLDocument::ContentRemoved(aDocument, aContainer, aChild, aIndexInContainer);
}
//----------------------------------------------------------------------
@ -1136,19 +1123,6 @@ nsXULDocument::ContentRemoved(nsIDocument* aDocument,
// nsIXULDocument interface
//
NS_IMETHODIMP
nsXULDocument::AddElementForID(nsIContent* aElement)
{
NS_PRECONDITION(aElement != nsnull, "null ptr");
if (! aElement)
return NS_ERROR_NULL_POINTER;
if (!aElement->IsElement())
return NS_ERROR_UNEXPECTED;
UpdateIdTableEntry(aElement->AsElement());
return NS_OK;
}
NS_IMETHODIMP
nsXULDocument::GetElementsForID(const nsAString& aID,
nsCOMArray<nsIContent>& aElements)
@ -1661,17 +1635,15 @@ nsXULDocument::GetCommandDispatcher(nsIDOMXULCommandDispatcher** aTracker)
}
Element*
nsXULDocument::GetElementById(const nsAString& aId, nsresult *aResult)
nsXULDocument::GetElementById(const nsAString& aId)
{
nsCOMPtr<nsIAtom> atom(do_GetAtom(aId));
if (!atom) {
*aResult = NS_ERROR_OUT_OF_MEMORY;
// This can only fail due OOM if the atom doesn't exist, in which
// case there couldn't possibly exist an entry for it.
return nsnull;
}
*aResult = NS_OK;
if (!CheckGetElementByIdArg(atom))
return nsnull;
@ -1700,7 +1672,10 @@ nsXULDocument::AddElementToDocumentPre(Element* aElement)
// 1. Add the element to the resource-to-element map. Also add it to
// the id map, since it seems this can be called when creating
// elements from prototypes.
UpdateIdTableEntry(aElement);
nsIAtom* id = aElement->GetID();
if (id) {
AddToIdTable(aElement, id);
}
rv = AddElementToRefMap(aElement);
if (NS_FAILED(rv)) return rv;
@ -1835,7 +1810,10 @@ nsXULDocument::RemoveSubtreeFromDocument(nsIContent* aContent)
// Also remove it from the id map, since we added it in
// AddElementToDocumentPre().
RemoveElementFromRefMap(aElement);
RemoveFromIdTable(aElement);
nsIAtom* id = aElement->GetID();
if (id) {
RemoveFromIdTable(aElement, id);
}
// 3. If the element is a 'command updater', then remove the
// element from the document's command dispatcher.
@ -2472,8 +2450,6 @@ nsXULDocument::PrepareToWalk()
rv = AppendChildTo(root, PR_FALSE);
if (NS_FAILED(rv)) return rv;
// Add the root element to the XUL document's ID-to-element map.
UpdateIdTableEntry(root);
rv = AddElementToRefMap(root);
if (NS_FAILED(rv)) return rv;

View File

@ -148,7 +148,6 @@ public:
NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTEWILLCHANGE
// nsIXULDocument interface
NS_IMETHOD AddElementForID(nsIContent* aElement);
NS_IMETHOD GetElementsForID(const nsAString& aID,
nsCOMArray<nsIContent>& aElements);
@ -170,8 +169,7 @@ public:
{
return nsDocument::GetElementById(aId, aReturn);
}
virtual mozilla::dom::Element* GetElementById(const nsAString & elementId,
nsresult *aResult);
virtual mozilla::dom::Element* GetElementById(const nsAString & elementId);
// nsIDOMXULDocument interface
NS_DECL_NSIDOMXULDOCUMENT

View File

@ -634,17 +634,6 @@ nsXULContentBuilder::BuildContentFromTemplate(nsIContent *aTemplateNode,
if (NS_FAILED(rv))
return rv;
if (! aNotify) {
// XUL document will watch us, and take care of making
// sure that we get added to or removed from the
// element map if aNotify is true. If not, we gotta do
// it ourselves. Yay.
nsCOMPtr<nsIXULDocument> xuldoc =
do_QueryInterface(mRoot->GetDocument());
if (xuldoc)
xuldoc->AddElementForID(realKid);
}
// Set up the element's 'container' and 'empty' attributes.
SetContainerAttrs(realKid, aChild, PR_TRUE, PR_FALSE);
}