Bug 26104. Move broadcaster maintenance out of XUL element and into the document. r=shaver, sr=hyatt

This commit is contained in:
waterson%netscape.com 2001-10-23 02:44:44 +00:00
parent d72b68fbd4
commit 100e80dc15
8 changed files with 356 additions and 408 deletions

View File

@ -297,3 +297,4 @@ XUL_ATOM(sort, "sort")
XUL_ATOM(sortDirection, "sortDirection")
XUL_ATOM(sortActive, "sortActive")
XUL_ATOM(selectedIndex, "selectedIndex")
XUL_ATOM(_star, "*")

View File

@ -341,92 +341,6 @@ FinishEventHandlerMap()
}
}
//----------------------------------------------------------------------
struct XULBroadcastListener
{
nsVoidArray* mAttributeList;
nsIDOMElement* mListener;
XULBroadcastListener(const nsAReadableString& aAttribute,
nsIDOMElement* aListener)
: mAttributeList(nsnull)
{
mListener = aListener; // WEAK REFERENCE
if (!aAttribute.Equals(NS_LITERAL_STRING("*"))) {
mAttributeList = new nsVoidArray();
mAttributeList->AppendElement((void*)(new nsString(aAttribute)));
}
// For the "*" case we leave the attribute list nulled out, and this means
// we're observing all attribute changes.
}
~XULBroadcastListener()
{
// Release all the attribute strings.
if (mAttributeList) {
PRInt32 count = mAttributeList->Count();
for (PRInt32 i = 0; i < count; i++) {
nsString* str = (nsString*)(mAttributeList->ElementAt(i));
delete str;
}
delete mAttributeList;
}
}
PRBool IsEmpty()
{
if (ObservingEverything())
return PR_FALSE;
PRInt32 count = mAttributeList->Count();
return (count == 0);
}
void RemoveAttribute(const nsAReadableString& aString)
{
if (ObservingEverything())
return;
if (mAttributeList) {
PRInt32 count = mAttributeList->Count();
for (PRInt32 i = 0; i < count; i++) {
nsString* str = (nsString*)(mAttributeList->ElementAt(i));
if (str->Equals(aString)) {
mAttributeList->RemoveElementAt(i);
delete str;
break;
}
}
}
}
PRBool ObservingEverything()
{
return (mAttributeList == nsnull);
}
PRBool ObservingAttribute(const nsAReadableString& aString)
{
if (ObservingEverything())
return PR_TRUE;
if (mAttributeList) {
PRInt32 count = mAttributeList->Count();
for (PRInt32 i = 0; i < count; i++) {
nsString* str = (nsString*)(mAttributeList->ElementAt(i));
if (str->Equals(aString))
return PR_TRUE;
}
}
return PR_FALSE;
}
};
//----------------------------------------------------------------------
static PRBool HasMutationListeners(nsIContent* aContent, PRUint32 aType)
@ -496,6 +410,23 @@ static PRBool HasMutationListeners(nsIContent* aContent, PRUint32 aType)
return PR_FALSE;
}
//----------------------------------------------------------------------
static PRInt32
StyleHintFor(nsINodeInfo* aNodeInfo)
{
nsCOMPtr<nsIAtom> tagName;
aNodeInfo->GetNameAtom(*getter_AddRefs(tagName));
if ((tagName == nsXULAtoms::broadcaster) ||
(tagName == nsXULAtoms::command) ||
(tagName == nsXULAtoms::key)) {
return NS_STYLE_HINT_NONE;
}
return NS_STYLE_HINT_UNKNOWN;
}
//----------------------------------------------------------------------
nsrefcnt nsXULElement::gRefCnt;
@ -1595,10 +1526,7 @@ nsXULElement::RemoveAttributeNS(const nsAReadableString& aNamespaceURI,
gNameSpaceManager->GetNameSpaceID(aNamespaceURI, nameSpaceId);
nsresult rv = UnsetAttr(nameSpaceId, tag, PR_TRUE);
NS_ASSERTION(NS_SUCCEEDED(rv), "unable to remove attribute");
return NS_OK;
return UnsetAttr(nameSpaceId, tag, PR_TRUE);
}
NS_IMETHODIMP
@ -2815,27 +2743,6 @@ nsXULElement::SetAttr(nsINodeInfo* aNodeInfo,
// Add popup and event listeners
AddListenerFor(aNodeInfo, PR_TRUE);
// Notify any broadcasters that are listening to this node.
if (BroadcastListeners()) {
nsAutoString attribute;
aNodeInfo->GetName(attribute);
PRInt32 count = BroadcastListeners()->Count();
for (PRInt32 i = 0; i < count; i++) {
XULBroadcastListener* xulListener =
NS_REINTERPRET_CAST(XULBroadcastListener*, BroadcastListeners()->ElementAt(i));
if (xulListener->ObservingAttribute(attribute) &&
(!aNodeInfo->Equals(nsXULAtoms::id)) &&
(!aNodeInfo->Equals(nsXULAtoms::persist)) &&
(!aNodeInfo->Equals(nsXULAtoms::ref))) {
// XXX Should have a function that knows which attributes are special.
// First we set the attribute in the observer.
xulListener->mListener->SetAttribute(attribute, aValue);
ExecuteOnBroadcastHandler(xulListener->mListener, attribute);
}
}
}
if (mDocument) {
nsCOMPtr<nsIBindingManager> bindingManager;
mDocument->GetBindingManager(getter_AddRefs(bindingManager));
@ -2872,17 +2779,12 @@ nsXULElement::SetAttr(nsINodeInfo* aNodeInfo,
}
if (aNotify) {
nsCOMPtr<nsIAtom> tagName;
NodeInfo()->GetNameAtom(*getter_AddRefs(tagName));
if ((tagName == nsXULAtoms::broadcaster) ||
(tagName == nsXULAtoms::command) ||
(tagName == nsXULAtoms::key))
return rv;
PRInt32 modHint = modification
? PRInt32(nsIDOMMutationEvent::MODIFICATION)
: PRInt32(nsIDOMMutationEvent::ADDITION);
PRInt32 modHint = modification ? PRInt32(nsIDOMMutationEvent::MODIFICATION)
: PRInt32(nsIDOMMutationEvent::ADDITION);
mDocument->AttributeChanged(this, attrns, attrName, modHint,
NS_STYLE_HINT_UNKNOWN);
StyleHintFor(NodeInfo()));
// XXXwaterson do we need to mDocument->EndUpdate() here?
}
@ -3096,40 +2998,18 @@ nsXULElement::UnsetAttr(PRInt32 aNameSpaceID,
// Check to see if the OBSERVES attribute is being unset. If so, we
// need to remove our broadcaster goop completely.
if (mDocument &&
(aNameSpaceID == kNameSpaceID_None) &&
if (mDocument && (aNameSpaceID == kNameSpaceID_None) &&
(aName == nsXULAtoms::observes || aName == nsXULAtoms::command)) {
// Do a getElementById to retrieve the broadcaster.
nsCOMPtr<nsIDOMElement> broadcaster;
nsCOMPtr<nsIDOMXULDocument> domDoc = do_QueryInterface(mDocument);
domDoc->GetElementById(oldValue, getter_AddRefs(broadcaster));
if (broadcaster) {
nsCOMPtr<nsIDOMXULElement> xulBroadcaster = do_QueryInterface(broadcaster);
if (xulBroadcaster) {
xulBroadcaster->RemoveBroadcastListener(NS_LITERAL_STRING("*"), this);
}
}
}
// Notify any broadcasters of the change.
if (BroadcastListeners()) {
PRInt32 count = BroadcastListeners()->Count();
for (PRInt32 i = 0; i < count; i++) {
XULBroadcastListener* xulListener =
NS_REINTERPRET_CAST(XULBroadcastListener*, BroadcastListeners()->ElementAt(i));
nsAutoString str;
aName->ToString(str);
if (xulListener->ObservingAttribute(str) &&
(aName != nsXULAtoms::id) &&
(aName != nsXULAtoms::persist) &&
(aName != nsXULAtoms::ref)) {
// XXX Should have a function that knows which attributes are special.
// Unset the attribute in the broadcast listener.
nsCOMPtr<nsIDOMElement> element;
element = do_QueryInterface(xulListener->mListener);
if (element)
element->RemoveAttribute(str);
nsCOMPtr<nsIDOMXULDocument> xuldoc = do_QueryInterface(mDocument);
if (xuldoc) {
// Do a getElementById to retrieve the broadcaster
nsCOMPtr<nsIDOMElement> broadcaster;
nsCOMPtr<nsIDOMXULDocument> domDoc = do_QueryInterface(mDocument);
domDoc->GetElementById(oldValue, getter_AddRefs(broadcaster));
if (broadcaster) {
xuldoc->RemoveBroadcastListenerFor(broadcaster,
NS_STATIC_CAST(nsIDOMElement*, this),
NS_LITERAL_STRING("*"));
}
}
}
@ -3144,19 +3024,11 @@ nsXULElement::UnsetAttr(PRInt32 aNameSpaceID,
binding->AttributeChanged(aName, aNameSpaceID, PR_TRUE);
if (aNotify) {
nsCOMPtr<nsIAtom> tagName;
NodeInfo()->GetNameAtom(*getter_AddRefs(tagName));
if ((tagName != nsXULAtoms::broadcaster) &&
(tagName != nsXULAtoms::command) &&
(tagName != nsXULAtoms::key)) {
// Don't notify for broadcaster, command, or key
// changes. (XXXwaterson Why?)
mDocument->AttributeChanged(NS_STATIC_CAST(nsIStyledContent*, this),
aNameSpaceID, aName, nsIDOMMutationEvent::REMOVAL,
NS_STYLE_HINT_UNKNOWN);
}
mDocument->AttributeChanged(this, aNameSpaceID, aName,
nsIDOMMutationEvent::REMOVAL,
StyleHintFor(NodeInfo()));
// XXXwaterson call nsIDocument::EndUpdate()?
// XXXwaterson do we need to mDocument->EndUpdate() here?
}
}
@ -3680,124 +3552,6 @@ nsXULElement::GetRangeList(nsVoidArray*& aResult) const
}
//----------------------------------------------------------------------
NS_IMETHODIMP
nsXULElement::AddBroadcastListener(const nsAReadableString& attr,
nsIDOMElement* anElement)
{
// Add ourselves to the array.
nsresult rv;
if (! BroadcastListeners()) {
rv = EnsureSlots();
if (NS_FAILED(rv)) return rv;
mSlots->mBroadcastListeners = new nsVoidArray();
if (! mSlots->mBroadcastListeners)
return NS_ERROR_OUT_OF_MEMORY;
}
BroadcastListeners()->AppendElement(new XULBroadcastListener(attr, anElement));
// We need to sync up the initial attribute value.
nsCOMPtr<nsIContent> listener( do_QueryInterface(anElement) );
if (attr.Equals(NS_LITERAL_STRING("*"))) {
// All of the attributes found on this node should be set on the
// listener.
PRBool haveLocalAttributes = PR_FALSE;
if (Attributes()) {
PRInt32 count = Attributes()->Count();
haveLocalAttributes = count > 0;
for (PRInt32 i = count - 1; i >= 0; --i) {
nsXULAttribute* attr = NS_REINTERPRET_CAST(nsXULAttribute*, Attributes()->ElementAt(i));
nsINodeInfo *ni = attr->GetNodeInfo();
// Don't push the |id| attribute's value.
if (ni->Equals(nsXULAtoms::id, kNameSpaceID_None))
continue;
// Don't push a value that's been over-ridden locally
if (haveLocalAttributes && FindLocalAttribute(ni))
continue;
nsAutoString value;
attr->GetValue(value);
listener->SetAttr(ni, value, PR_TRUE);
}
}
if (mPrototype) {
for (PRInt32 i = mPrototype->mNumAttributes - 1; i >= 0; --i) {
nsXULPrototypeAttribute* attr = &(mPrototype->mAttributes[i]);
nsINodeInfo* ni = attr->mNodeInfo;
if (ni->Equals(nsXULAtoms::id, kNameSpaceID_None))
continue;
nsAutoString value;
attr->mValue.GetValue(value);
listener->SetAttr(ni, value, PR_TRUE);
}
}
}
else {
// Find out if the attribute is even present at all.
nsCOMPtr<nsIAtom> kAtom = dont_AddRef(NS_NewAtom(attr));
nsAutoString attrValue;
nsresult result = GetAttr(kNameSpaceID_None, kAtom, attrValue);
PRBool attrPresent = (result == NS_CONTENT_ATTR_NO_VALUE ||
result == NS_CONTENT_ATTR_HAS_VALUE);
if (attrPresent) {
// Set the attribute
anElement->SetAttribute(attr, attrValue);
}
else {
// Unset the attribute
anElement->RemoveAttribute(attr);
}
}
return NS_OK;
}
NS_IMETHODIMP
nsXULElement::RemoveBroadcastListener(const nsAReadableString& attr,
nsIDOMElement* anElement)
{
if (BroadcastListeners()) {
// Find the element.
PRInt32 count = BroadcastListeners()->Count();
for (PRInt32 i = 0; i < count; i++) {
XULBroadcastListener* xulListener =
NS_REINTERPRET_CAST(XULBroadcastListener*, BroadcastListeners()->ElementAt(i));
if (xulListener->mListener == anElement) {
if (xulListener->ObservingEverything() || attr.Equals(NS_LITERAL_STRING("*"))) {
// Do the removal.
BroadcastListeners()->RemoveElementAt(i);
delete xulListener;
}
else {
// We're observing specific attributes and removing a specific attribute
xulListener->RemoveAttribute(attr);
if (xulListener->IsEmpty()) {
// Do the removal.
BroadcastListeners()->RemoveElementAt(i);
delete xulListener;
}
}
break;
}
}
}
return NS_OK;
}
// XXX This _should_ be an implementation method, _not_ publicly exposed :-(
NS_IMETHODIMP
nsXULElement::GetResource(nsIRDFResource** aResource)
@ -3908,88 +3662,6 @@ nsXULElement::EnsureContentsGenerated(void) const
return NS_OK;
}
nsresult
nsXULElement::ExecuteOnBroadcastHandler(nsIDOMElement* anElement, const nsAReadableString& attrName)
{
// Now we execute the onchange handler in the context of the
// observer. We need to find the observer in order to
// execute the handler.
nsCOMPtr<nsIContent> content(do_QueryInterface(anElement));
PRInt32 count;
content->ChildCount(count);
for (PRInt32 i = 0; i < count; i++) {
nsCOMPtr<nsIContent> child;
content->ChildAt(i, *getter_AddRefs(child));
nsCOMPtr<nsIAtom> tag;
child->GetTag(*getter_AddRefs(tag));
if (tag == nsXULAtoms::observes) {
nsCOMPtr<nsIDOMElement> domElement(do_QueryInterface(child));
if (domElement) {
// We have a domElement. Find out if it was listening to us.
nsAutoString listeningToID;
domElement->GetAttribute(NS_LITERAL_STRING("element"), listeningToID);
nsAutoString broadcasterID;
GetAttribute(NS_LITERAL_STRING("id"), broadcasterID);
if (listeningToID == broadcasterID) {
// We are observing the broadcaster, but is this the right
// attribute?
nsAutoString listeningToAttribute;
domElement->GetAttribute(NS_LITERAL_STRING("attribute"), listeningToAttribute);
if (listeningToAttribute.Equals(attrName)) {
// This is the right observes node.
// Execute the onchange event handler
nsEvent event;
event.eventStructType = NS_EVENT;
event.message = NS_XUL_BROADCAST;
ExecuteJSCode(domElement, &event);
}
}
}
}
}
return NS_OK;
}
nsresult
nsXULElement::ExecuteJSCode(nsIDOMElement* anElement, nsEvent* aEvent)
{
// This code executes in every presentation context in which this
// document is appearing.
nsCOMPtr<nsIContent> content;
content = do_QueryInterface(anElement);
if (!content)
return NS_OK;
nsCOMPtr<nsIDocument> document;
content->GetDocument(*getter_AddRefs(document));
if (!document)
return NS_OK;
PRInt32 count = document->GetNumberOfShells();
for (PRInt32 i = 0; i < count; i++) {
nsCOMPtr<nsIPresShell> shell;
document->GetShellAt(i, getter_AddRefs(shell));
if (!shell)
continue;
// Retrieve the context in which our DOM event will fire.
nsCOMPtr<nsIPresContext> aPresContext;
shell->GetPresContext(getter_AddRefs(aPresContext));
// Handle the DOM event
nsEventStatus status = nsEventStatus_eIgnore;
content->HandleDOMEvent(aPresContext, aEvent, nsnull, NS_EVENT_FLAG_INIT, &status);
}
return NS_OK;
}
nsresult
nsXULElement::GetElementsByTagName(nsIDOMNode* aNode,
const nsAReadableString& aTagName,
@ -5164,9 +4836,7 @@ nsresult nsXULElement::MakeHeavyweight()
//
nsXULElement::Slots::Slots(nsXULElement* aElement)
: mElement(aElement),
mBroadcastListeners(nsnull),
mAttributes(nsnull),
: mAttributes(nsnull),
mLazyState(0),
mInnerXULElement(nsnull)
{
@ -5180,19 +4850,6 @@ nsXULElement::Slots::~Slots()
NS_IF_RELEASE(mAttributes);
// Release our broadcast listeners
if (mBroadcastListeners) {
PRInt32 count = mBroadcastListeners->Count();
for (PRInt32 i = 0; i < count; i++) {
XULBroadcastListener* xulListener =
NS_REINTERPRET_CAST(XULBroadcastListener*, mBroadcastListeners->ElementAt(0));
mElement->RemoveBroadcastListener(NS_LITERAL_STRING("*"), xulListener->mListener);
}
delete mBroadcastListeners;
}
// Delete the aggregated interface, if one exists.
delete mInnerXULElement;
}

View File

@ -547,10 +547,8 @@ protected:
Slots(nsXULElement* mElement);
~Slots();
nsXULElement* mElement; // [WEAK]
nsCOMPtr<nsINameSpace> mNameSpace; // [OWNER]
nsCOMPtr<nsINodeInfo> mNodeInfo; // [OWNER]
nsVoidArray* mBroadcastListeners; // [WEAK]
nsCOMPtr<nsIControllers> mControllers; // [OWNER]
/**
@ -625,7 +623,6 @@ protected:
// delegate.
nsINameSpace* NameSpace() const { return mSlots ? mSlots->mNameSpace.get() : mPrototype->mNameSpace.get(); }
nsINodeInfo* NodeInfo() const { return mSlots ? mSlots->mNodeInfo : mPrototype->mNodeInfo; }
nsVoidArray* BroadcastListeners() const { return mSlots ? mSlots->mBroadcastListeners : nsnull; }
nsIControllers* Controllers() const { return mSlots ? mSlots->mControllers.get() : nsnull; }
nsXULAttributes* Attributes() const { return mSlots ? mSlots->mAttributes : nsnull; }
nsXULAggregateElement* InnerXULElement() const { return mSlots ? mSlots->mInnerXULElement : nsnull; }

View File

@ -69,7 +69,7 @@ class nsIURI;
{ 0x954f0811, 0x81dc, 0x11d2, { 0xb5, 0x2a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 } }
/**
* RDF document extensions to nsIDocument
* XUL extensions to nsIDocument
*/
class nsIRDFDataSource;

View File

@ -417,6 +417,17 @@ PlaceHolderRequest::~PlaceHolderRequest()
}
}
//----------------------------------------------------------------------
struct BroadcasterMapEntry : public PLDHashEntryHdr {
nsIDOMElement* mBroadcaster; // [WEAK]
nsCheapVoidArray mListeners; // [OWNING] of BroadcastListener objects
};
struct BroadcastListener {
nsIDOMElement* mListener; // [WEAK] XXXwaterson crash waiting to happen!
nsCOMPtr<nsIAtom> mAttribute;
};
//----------------------------------------------------------------------
//
@ -437,7 +448,8 @@ nsXULDocument::nsXULDocument(void)
mNextContentID(NS_CONTENT_ID_COUNTER_BASE),
mNumCapturers(0),
mState(eState_Master),
mCurrentScriptProto(nsnull)
mCurrentScriptProto(nsnull),
mBroadcasterMap(nsnull)
{
NS_INIT_REFCNT();
mCharSetID.AssignWithConversion("UTF-8");
@ -465,6 +477,10 @@ nsXULDocument::~nsXULDocument()
// decls never got resolved.
DestroyForwardReferences();
// Destroy our broadcaster map.
if (mBroadcasterMap)
PL_DHashTableDestroy(mBroadcasterMap);
// Notify observer that we're about to go away
PRInt32 i;
for (i = mObservers.Count() - 1; i >= 0; --i) {
@ -1685,6 +1701,170 @@ nsXULDocument::OnResumeContentSink()
return NS_OK;
}
static void PR_CALLBACK
ClearBroadcasterMapEntry(PLDHashTable* aTable, PLDHashEntryHdr* aEntry)
{
BroadcasterMapEntry* entry =
NS_STATIC_CAST(BroadcasterMapEntry*, aEntry);
// N.B. that we need to manually run the dtor because we
// constructed the nsCheapVoidArray object in-place.
entry->mListeners.~nsCheapVoidArray();
}
static void
SynchronizeBroadcastListener(nsIDOMElement* aBroadcaster,
nsIDOMElement* aListener,
const nsAString& aAttr)
{
nsCOMPtr<nsIContent> broadcaster = do_QueryInterface(aBroadcaster);
nsCOMPtr<nsIContent> listener = do_QueryInterface(aListener);
if (aAttr.Equals(NS_LITERAL_STRING("*"))) {
PRInt32 count;
broadcaster->GetAttrCount(count);
while (--count >= 0) {
PRInt32 nameSpaceID;
nsCOMPtr<nsIAtom> name;
nsCOMPtr<nsIAtom> prefix;
broadcaster->GetAttrNameAt(count, nameSpaceID,
*getter_AddRefs(name),
*getter_AddRefs(prefix));
// _Don't_ push the |id| attribute's value!
if ((nameSpaceID == kNameSpaceID_None) && (name == nsXULAtoms::id))
continue;
nsAutoString value;
broadcaster->GetAttr(nameSpaceID, name, value);
listener->SetAttr(nameSpaceID, name, value, PR_TRUE);
}
}
else {
// Find out if the attribute is even present at all.
nsCOMPtr<nsIAtom> name = dont_AddRef(NS_NewAtom(aAttr));
nsAutoString value;
nsresult rv = broadcaster->GetAttr(kNameSpaceID_None, name, value);
if (rv == NS_CONTENT_ATTR_NO_VALUE ||
rv == NS_CONTENT_ATTR_HAS_VALUE) {
listener->SetAttr(kNameSpaceID_None, name, value, PR_TRUE);
}
else {
listener->UnsetAttr(kNameSpaceID_None, name, PR_TRUE);
}
}
}
NS_IMETHODIMP
nsXULDocument::AddBroadcastListenerFor(nsIDOMElement* aBroadcaster,
nsIDOMElement* aListener,
const nsAString& aAttr)
{
static PLDHashTableOps gOps = {
PL_DHashAllocTable,
PL_DHashFreeTable,
PL_DHashGetKeyStub,
PL_DHashVoidPtrKeyStub,
PL_DHashMatchEntryStub,
PL_DHashMoveEntryStub,
ClearBroadcasterMapEntry,
PL_DHashFinalizeStub,
nsnull
};
if (! mBroadcasterMap) {
mBroadcasterMap =
PL_NewDHashTable(&gOps, nsnull, sizeof(BroadcasterMapEntry),
PL_DHASH_MIN_SIZE);
if (! mBroadcasterMap)
return NS_ERROR_OUT_OF_MEMORY;
}
BroadcasterMapEntry* entry =
NS_STATIC_CAST(BroadcasterMapEntry*,
PL_DHashTableOperate(mBroadcasterMap, aBroadcaster,
PL_DHASH_LOOKUP));
if (PL_DHASH_ENTRY_IS_FREE(entry)) {
entry =
NS_STATIC_CAST(BroadcasterMapEntry*,
PL_DHashTableOperate(mBroadcasterMap, aBroadcaster,
PL_DHASH_ADD));
if (! entry)
return NS_ERROR_OUT_OF_MEMORY;
entry->mBroadcaster = aBroadcaster;
// N.B. placement new to construct the nsCheapVoidArray object
// in-place
new (&entry->mListeners) nsCheapVoidArray();
}
// Only add the listener if it's not there already!
nsCOMPtr<nsIAtom> attr = dont_AddRef(NS_NewAtom(aAttr));
BroadcastListener* bl;
for (PRInt32 i = entry->mListeners.Count() - 1; i >= 0; --i) {
bl = NS_STATIC_CAST(BroadcastListener*, entry->mListeners[i]);
if ((bl->mListener == aListener) && (bl->mAttribute == attr))
return NS_OK;
}
bl = new BroadcastListener;
if (! bl)
return NS_ERROR_OUT_OF_MEMORY;
bl->mListener = aListener;
bl->mAttribute = attr;
entry->mListeners.AppendElement(bl);
SynchronizeBroadcastListener(aBroadcaster, aListener, aAttr);
return NS_OK;
}
NS_IMETHODIMP
nsXULDocument::RemoveBroadcastListenerFor(nsIDOMElement* aBroadcaster,
nsIDOMElement* aListener,
const nsAString& aAttr)
{
// If we haven't added any broadcast listeners, then there sure
// aren't any to remove.
if (! mBroadcasterMap)
return NS_OK;
BroadcasterMapEntry* entry =
NS_STATIC_CAST(BroadcasterMapEntry*,
PL_DHashTableOperate(mBroadcasterMap, aBroadcaster,
PL_DHASH_LOOKUP));
if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
nsCOMPtr<nsIAtom> attr = dont_AddRef(NS_NewAtom(aAttr));
for (PRInt32 i = entry->mListeners.Count() - 1; i >= 0; --i) {
BroadcastListener* bl =
NS_STATIC_CAST(BroadcastListener*, entry->mListeners[i]);
if ((bl->mListener == aListener) && (bl->mAttribute == attr)) {
entry->mListeners.RemoveElement(aListener);
if (entry->mListeners.Count() == 0)
PL_DHashTableOperate(mBroadcasterMap, aBroadcaster,
PL_DHASH_REMOVE);
SynchronizeBroadcastListener(aBroadcaster, aListener, aAttr);
break;
}
}
}
return NS_OK;
}
NS_IMETHODIMP
nsXULDocument::ContentChanged(nsIContent* aContent,
@ -1707,12 +1887,76 @@ nsXULDocument::ContentStatesChanged(nsIContent* aContent1, nsIContent* aContent2
return NS_OK;
}
nsresult
nsXULDocument::ExecuteOnBroadcastHandlerFor(nsIContent* aBroadcaster,
nsIDOMElement* aListener,
nsIAtom* aAttr)
{
// Now we execute the onchange handler in the context of the
// observer. We need to find the observer in order to
// execute the handler.
nsAutoString attrName;
aAttr->ToString(attrName);
nsCOMPtr<nsIContent> listener = do_QueryInterface(aListener);
PRInt32 count;
listener->ChildCount(count);
for (PRInt32 i = 0; i < count; ++i) {
nsCOMPtr<nsIContent> child;
listener->ChildAt(i, *getter_AddRefs(child));
nsCOMPtr<nsIAtom> tag;
child->GetTag(*getter_AddRefs(tag));
if (tag != nsXULAtoms::observes)
continue;
// Is this the element that was listening to us?
nsAutoString listeningToID;
aBroadcaster->GetAttr(kNameSpaceID_None, nsXULAtoms::element, listeningToID);
nsAutoString broadcasterID;
aBroadcaster->GetAttr(kNameSpaceID_None, nsXULAtoms::id, broadcasterID);
if (listeningToID != broadcasterID)
continue;
// We are observing the broadcaster, but is this the right
// attribute?
nsAutoString listeningToAttribute;
listener->GetAttr(kNameSpaceID_None, nsXULAtoms::attribute, listeningToAttribute);
if (!listeningToAttribute.Equals(attrName) &&
!listeningToAttribute.Equals(NS_LITERAL_STRING("*"))) {
continue;
}
// This is the right observes node. Execute the onchange
// event handler
nsEvent event;
event.eventStructType = NS_EVENT;
event.message = NS_XUL_BROADCAST;
PRInt32 j = mPresShells.Count();
while (--j >= 0) {
nsIPresShell* shell = NS_STATIC_CAST(nsIPresShell*, mPresShells[j]);
nsCOMPtr<nsIPresContext> aPresContext;
shell->GetPresContext(getter_AddRefs(aPresContext));
// Handle the DOM event
nsEventStatus status = nsEventStatus_eIgnore;
listener->HandleDOMEvent(aPresContext, &event, nsnull, NS_EVENT_FLAG_INIT, &status);
}
}
return NS_OK;
}
NS_IMETHODIMP
nsXULDocument::AttributeChanged(nsIContent* aElement,
PRInt32 aNameSpaceID,
nsIAtom* aAttribute,
PRInt32 aModType,
PRInt32 aHint)
PRInt32 aNameSpaceID,
nsIAtom* aAttribute,
PRInt32 aModType,
PRInt32 aHint)
{
nsresult rv;
@ -1738,8 +1982,7 @@ nsXULDocument::AttributeChanged(nsIContent* aElement,
observer->AttributeChanged(this, aElement, aNameSpaceID, aAttribute, aModType, aHint);
}
// Finally, see if there is anything we need to persist in the
// localstore.
// See if there is anything we need to persist in the localstore.
//
// XXX Namespace handling broken :-(
nsAutoString persist;
@ -1757,6 +2000,48 @@ nsXULDocument::AttributeChanged(nsIContent* aElement,
}
}
// Synchronize broadcast listeners
if (mBroadcasterMap &&
(aNameSpaceID == kNameSpaceID_None) &&
(aAttribute != nsXULAtoms::id)) {
nsCOMPtr<nsIDOMElement> domele = do_QueryInterface(aElement);
BroadcasterMapEntry* entry =
NS_STATIC_CAST(BroadcasterMapEntry*,
PL_DHashTableOperate(mBroadcasterMap, domele.get(),
PL_DHASH_LOOKUP));
if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
// We've got listeners: push the value.
nsAutoString value;
rv = aElement->GetAttr(kNameSpaceID_None, aAttribute,
value);
for (PRInt32 i = entry->mListeners.Count() - 1; i >= 0; --i) {
BroadcastListener* bl =
NS_STATIC_CAST(BroadcastListener*, entry->mListeners[i]);
if ((bl->mAttribute == aAttribute) ||
(bl->mAttribute == nsXULAtoms::_star)) {
nsCOMPtr<nsIContent> listener
= do_QueryInterface(bl->mListener);
if (rv == NS_CONTENT_ATTR_NO_VALUE ||
rv == NS_CONTENT_ATTR_HAS_VALUE) {
listener->SetAttr(kNameSpaceID_None, aAttribute,
value, PR_TRUE);
}
else {
listener->UnsetAttr(kNameSpaceID_None, aAttribute,
PR_TRUE);
}
ExecuteOnBroadcastHandlerFor(aElement, bl->mListener,
aAttribute);
}
}
}
}
return NS_OK;
}
@ -2121,7 +2406,7 @@ NS_IMETHODIMP_(PRBool)
nsXULDocument::EventCaptureRegistration(PRInt32 aCapturerIncrement)
{
mNumCapturers += aCapturerIncrement;
NS_WARN_IF_FALSE(mNumCapturers >= 0, "Number of capturers has become negative");
NS_ASSERTION(mNumCapturers >= 0, "Number of capturers has become negative");
return (mNumCapturers > 0 ? PR_TRUE : PR_FALSE);
}
@ -6477,25 +6762,19 @@ nsXULDocument::CheckBroadcasterHookup(nsXULDocument* aDocument,
return NS_ERROR_UNEXPECTED;
// Try to find the broadcaster element in the document.
nsCOMPtr<nsIDOMElement> target;
rv = aDocument->GetElementById(broadcasterID, getter_AddRefs(target));
nsCOMPtr<nsIDOMElement> broadcaster;
rv = aDocument->GetElementById(broadcasterID, getter_AddRefs(broadcaster));
if (NS_FAILED(rv)) return rv;
// If we can't find the broadcaster, then we'll need to defer the
// hookup. We may need to resolve some of the other overlays
// first.
if (! target) {
if (! broadcaster) {
*aNeedsHookup = PR_TRUE;
return NS_OK;
}
nsCOMPtr<nsIDOMXULElement> broadcaster = do_QueryInterface(target);
if (! broadcaster) {
*aNeedsHookup = PR_FALSE;
return NS_OK; // not a XUL element, so we can't subscribe
}
rv = broadcaster->AddBroadcastListener(attribute, listener);
rv = aDocument->AddBroadcastListenerFor(broadcaster, listener, attribute);
if (NS_FAILED(rv)) return rv;
#ifdef PR_LOGGING

View File

@ -86,6 +86,7 @@
#include "nsIDOMDocumentEvent.h"
#include "nsIFocusController.h"
#include "nsScriptLoader.h"
#include "pldhash.h"
class nsIAtom;
class nsIElementFactory;
@ -505,6 +506,11 @@ protected:
nsresult
AddElementToDocumentPost(nsIContent* aElement);
nsresult
ExecuteOnBroadcastHandlerFor(nsIContent* aBroadcaster,
nsIDOMElement* aListener,
nsIAtom* aAttr);
protected:
// pseudo constants
static PRInt32 gRefCnt;
@ -862,6 +868,11 @@ protected:
nsSupportsHashtable mContentWrapperHash;
/**
* A map from a broadcaster element to a list of listener elements.
*/
PLDHashTable* mBroadcasterMap;
private:
// helpers

View File

@ -57,5 +57,13 @@ interface nsIDOMXULDocument : nsIDOMDocument
nsIDOMNodeList getElementsByAttribute(in DOMString name,
in DOMString value);
void addBroadcastListenerFor(in nsIDOMElement broadcaster,
in nsIDOMElement observer,
in DOMString attr);
void removeBroadcastListenerFor(in nsIDOMElement broadcaster,
in nsIDOMElement observer,
in DOMString attr);
void persist(in DOMString id, in DOMString attr);
};

View File

@ -110,11 +110,6 @@ interface nsIDOMXULElement : nsIDOMElement
readonly attribute nsIControllers controllers;
readonly attribute nsIBoxObject boxObject;
void addBroadcastListener(in DOMString attr,
in nsIDOMElement element);
void removeBroadcastListener(in DOMString attr,
in nsIDOMElement element);
void focus();
void blur();
void click();