Bug 524674, nsIEventListenerService: tracking of dynamically added and removed event listeners, r=masayuki

This commit is contained in:
Olli Pettay 2015-04-28 23:10:05 +03:00
parent d632609013
commit 5532b3f7f9
6 changed files with 225 additions and 2 deletions

View File

@ -379,6 +379,10 @@ EventListenerManager::AddEventListenerInternal(
if (aTypeAtom && mTarget) {
mTarget->EventListenerAdded(aTypeAtom);
}
if (mIsMainThreadELM && mTarget) {
EventListenerService::NotifyAboutMainThreadListenerChange(mTarget);
}
}
bool
@ -496,6 +500,9 @@ EventListenerManager::RemoveEventListenerInternal(
if (mTarget && aUserType) {
mTarget->EventListenerRemoved(aUserType);
}
if (mIsMainThreadELM && mTarget) {
EventListenerService::NotifyAboutMainThreadListenerChange(mTarget);
}
if (!deviceType
#ifdef MOZ_B2G
@ -629,6 +636,9 @@ EventListenerManager::SetEventHandlerInternal(
mTarget->EventListenerRemoved(aName);
mTarget->EventListenerAdded(aName);
}
if (mIsMainThreadELM && mTarget) {
EventListenerService::NotifyAboutMainThreadListenerChange(mTarget);
}
}
// Set flag to indicate possible need for compilation later
@ -756,6 +766,9 @@ EventListenerManager::RemoveEventHandler(nsIAtom* aName,
if (mTarget && aName) {
mTarget->EventListenerRemoved(aName);
}
if (mIsMainThreadELM && mTarget) {
EventListenerService::NotifyAboutMainThreadListenerChange(mTarget);
}
}
}

View File

@ -15,6 +15,8 @@
#include "nsJSUtils.h"
#include "nsMemory.h"
#include "nsServiceManagerUtils.h"
#include "nsArray.h"
#include "nsThreadUtils.h"
namespace mozilla {
@ -127,6 +129,21 @@ EventListenerInfo::ToSource(nsAString& aResult)
return NS_OK;
}
EventListenerService*
EventListenerService::sInstance = nullptr;
EventListenerService::EventListenerService()
{
MOZ_ASSERT(!sInstance);
sInstance = this;
}
EventListenerService::~EventListenerService()
{
MOZ_ASSERT(sInstance == this);
sInstance = nullptr;
}
NS_IMETHODIMP
EventListenerService::GetListenerInfoFor(nsIDOMEventTarget* aEventTarget,
uint32_t* aCount,
@ -288,6 +305,58 @@ EventListenerService::RemoveListenerForAllEvents(nsIDOMEventTarget* aTarget,
return NS_OK;
}
NS_IMETHODIMP
EventListenerService::AddListenerChangeListener(nsIListenerChangeListener* aListener)
{
if (!mChangeListeners.Contains(aListener)) {
mChangeListeners.AppendElement(aListener);
}
return NS_OK;
};
NS_IMETHODIMP
EventListenerService::RemoveListenerChangeListener(nsIListenerChangeListener* aListener)
{
mChangeListeners.RemoveElement(aListener);
return NS_OK;
};
void
EventListenerService::NotifyAboutMainThreadListenerChangeInternal(dom::EventTarget* aTarget)
{
MOZ_ASSERT(NS_IsMainThread());
if (mChangeListeners.IsEmpty()) {
return;
}
if (!mPendingListenerChanges) {
mPendingListenerChanges = nsArrayBase::Create();
nsCOMPtr<nsIRunnable> runnable = NS_NewRunnableMethod(this,
&EventListenerService::NotifyPendingChanges);
NS_DispatchToCurrentThread(runnable);
}
if (!mPendingListenerChangesSet.Get(aTarget)) {
mPendingListenerChanges->AppendElement(aTarget, false);
mPendingListenerChangesSet.Put(aTarget, true);
}
}
void
EventListenerService::NotifyPendingChanges()
{
nsCOMPtr<nsIMutableArray> changes;
mPendingListenerChanges.swap(changes);
mPendingListenerChangesSet.Clear();
nsTObserverArray<nsCOMPtr<nsIListenerChangeListener>>::EndLimitedIterator
iter(mChangeListeners);
while (iter.HasMore()) {
nsCOMPtr<nsIListenerChangeListener> listener = iter.GetNext();
listener->ListenersChanged(changes);
}
}
} // namespace mozilla
nsresult

View File

@ -13,8 +13,15 @@
#include "nsIDOMEventListener.h"
#include "nsIEventListenerService.h"
#include "nsString.h"
#include "nsTObserverArray.h"
#include "nsDataHashtable.h"
class nsIMutableArray;
namespace mozilla {
namespace dom {
class EventTarget;
};
template<typename T>
class Maybe;
@ -56,10 +63,27 @@ protected:
class EventListenerService final : public nsIEventListenerService
{
~EventListenerService() {}
~EventListenerService();
public:
EventListenerService();
NS_DECL_ISUPPORTS
NS_DECL_NSIEVENTLISTENERSERVICE
static void NotifyAboutMainThreadListenerChange(dom::EventTarget* aTarget)
{
if (sInstance) {
sInstance->NotifyAboutMainThreadListenerChangeInternal(aTarget);
}
}
void NotifyPendingChanges();
private:
void NotifyAboutMainThreadListenerChangeInternal(dom::EventTarget* aTarget);
nsTObserverArray<nsCOMPtr<nsIListenerChangeListener>> mChangeListeners;
nsCOMPtr<nsIMutableArray> mPendingListenerChanges;
nsDataHashtable<nsISupportsHashKey, bool> mPendingListenerChangesSet;
static EventListenerService* sInstance;
};
} // namespace mozilla

View File

@ -7,6 +7,13 @@
interface nsIDOMEventListener;
interface nsIDOMEventTarget;
interface nsIArray;
[scriptable, function, uuid(8d5b5a6b-dec0-473d-86c4-591801dfaac1)]
interface nsIListenerChangeListener : nsISupports
{
void listenersChanged(in nsIArray aEventTargets);
};
/**
* An instance of this interface describes how an event listener
@ -39,7 +46,7 @@ interface nsIEventListenerInfo : nsISupports
AString toSource();
};
[scriptable, uuid(f6964bfb-dabe-4cab-9733-be0ee2bf8171)]
[scriptable, uuid(77aab5f7-213d-4db4-9f22-e46dfb774f15)]
interface nsIEventListenerService : nsISupports
{
/**
@ -96,5 +103,8 @@ interface nsIEventListenerService : nsISupports
in nsIDOMEventListener listener,
[optional] in boolean aUseCapture,
[optional] in boolean aSystemEventGroup);
void addListenerChangeListener(in nsIListenerChangeListener aListener);
void removeListenerChangeListener(in nsIListenerChangeListener aListener);
};

View File

@ -12,6 +12,7 @@ support-files =
[test_bug336682_2.xul]
[test_bug368835.html]
[test_bug415498.xul]
[test_bug524674.xul]
[test_bug586961.xul]
[test_bug591249.xul]
[test_bug602962.xul]

View File

@ -0,0 +1,106 @@
<?xml version="1.0"?>
<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=524674
-->
<window title="Mozilla Bug 524674"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
<!-- test results are displayed in the html:body -->
<body xmlns="http://www.w3.org/1999/xhtml">
<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=524674"
target="_blank">Mozilla Bug 524674</a>
</body>
<!-- test code goes here -->
<script type="application/javascript">
<![CDATA[
/** Test for Bug 524674 **/
var els = Components.classes["@mozilla.org/eventlistenerservice;1"]
.getService(Components.interfaces.nsIEventListenerService);
function dummyListener() {}
var runningTest = null;
var d = document.createElementNS("http://www.w3.org/1999/xhtml", "div");
var xhr = new XMLHttpRequest();
// Test also double removals and such.
var tests = [
function() {
els.addListenerChangeListener(changeListener);
d.addEventListener("foo", dummyListener);
d.addEventListener("foo", dummyListener);
xhr.addEventListener("foo", dummyListener);
tests[0] = [d, xhr];
},
function() {
d.onclick = dummyListener;
d.onclick = dummyListener;
xhr.onload = dummyListener;
tests[0] = [d, xhr];
},
function() {
d.onclick = function() {};
tests[0] = [d];
},
function() {
d.removeEventListener("foo", dummyListener);
d.removeEventListener("foo", dummyListener);
xhr.removeEventListener("foo", dummyListener);
tests[0] = [d, xhr];
},
function() {
d.onclick = null;
d.onclick = null;
xhr.onload = null;
tests[0] = [d, xhr];
},
function() {
els.removeListenerChangeListener(changeListener);
// Check that once we've removed the change listener, it isn't called anymore.
d.addEventListener("foo", dummyListener);
xhr.addEventListener("foo", dummyListener);
SimpleTest.executeSoon(function() {
SimpleTest.finish();
});
}
];
SimpleTest.executeSoon(tests[0]);
function changeListener(array) {
if (typeof tests[0] == "function") {
return;
}
var expectedEventTargets = tests[0];
var e = array.enumerate();
var i = 0;
while (e.hasMoreElements()) {
var current = e.getNext();
var expected = expectedEventTargets[i];
if (current == expected) {
is(current, expected, current + " = " + expected);
// We may get random other event listener changes here too, not just the one from the
// test.
++i
}
}
if (expectedEventTargets.length != i) {
return;
}
is(expectedEventTargets.length, i, "Should have got notification for all the changes.");
tests.shift();
ok(tests.length);
SimpleTest.executeSoon(tests[0]);
}
SimpleTest.waitForExplicitFinish();
]]>
</script>
</window>