diff --git a/devtools/client/markupview/markup-view.js b/devtools/client/markupview/markup-view.js index bc93a26a904c..0aa40a4dc3ab 100644 --- a/devtools/client/markupview/markup-view.js +++ b/devtools/client/markupview/markup-view.js @@ -841,14 +841,13 @@ MarkupView.prototype = { // we're not viewing. continue; } - if (type === "attributes" || type === "characterData") { + if (type === "attributes" || type === "characterData" + || type === "events" || type === "pseudoClassLock") { container.update(); } else if (type === "childList" || type === "nativeAnonymousChildList") { container.childrenDirty = true; // Update the children to take care of changes in the markup view DOM. this._updateChildren(container, {flash: true}); - } else if (type === "pseudoClassLock") { - container.update(); } } @@ -2547,7 +2546,6 @@ function ElementEditor(aContainer, aNode) { let tagName = this.node.nodeName.toLowerCase(); this.tag.textContent = tagName; this.closeTag.textContent = tagName; - this.eventNode.style.display = this.node.hasEventListeners ? "inline-block" : "none"; this.update(); this.initialized = true; @@ -2643,6 +2641,10 @@ ElementEditor.prototype = { } } + // Update the event bubble display + this.eventNode.style.display = this.node.hasEventListeners ? + "inline-block" : "none"; + this.updateTextEditor(); }, diff --git a/devtools/client/markupview/test/browser_markupview_events.js b/devtools/client/markupview/test/browser_markupview_events.js index 8656a3a42710..56f9dbdd2ffa 100644 --- a/devtools/client/markupview/test/browser_markupview_events.js +++ b/devtools/client/markupview/test/browser_markupview_events.js @@ -152,6 +152,42 @@ const TEST_DATA = [ } ] }, + // #noevents tests check that dynamically added events are properly displayed + // in the markupview + { + selector: "#noevents", + expected: [] + }, + { + selector: "#noevents", + beforeTest: function* (inspector, testActor) { + let nodeMutated = inspector.once("markupmutation"); + yield testActor.eval("window.wrappedJSObject.addNoeventsClickHandler();"); + yield nodeMutated; + }, + expected: [ + { + type: "click", + filename: TEST_URL + ":106", + attributes: [ + "Bubbling", + "DOM2" + ], + handler: 'function noeventsClickHandler(event) {\n' + + ' alert("noevents has an event listener");\n' + + '}' + } + ] + }, + { + selector: "#noevents", + beforeTest: function* (inspector, testActor) { + let nodeMutated = inspector.once("markupmutation"); + yield testActor.eval("window.wrappedJSObject.removeNoeventsClickHandler();"); + yield nodeMutated; + }, + expected: [] + }, ]; add_task(runEventPopupTests); diff --git a/devtools/client/markupview/test/doc_markup_events.html b/devtools/client/markupview/test/doc_markup_events.html index a06f7b5a65e1..1a4f358971f9 100644 --- a/devtools/client/markupview/test/doc_markup_events.html +++ b/devtools/client/markupview/test/doc_markup_events.html @@ -102,6 +102,20 @@ alert("boundHandleEvent clicked"); } }; + + function noeventsClickHandler(event) { + alert("noevents has an event listener"); + }; + + function addNoeventsClickHandler() { + let noevents = document.getElementById("noevents"); + noevents.addEventListener("click", noeventsClickHandler); + }; + + function removeNoeventsClickHandler() { + let noevents = document.getElementById("noevents"); + noevents.removeEventListener("click", noeventsClickHandler); + }; diff --git a/devtools/client/markupview/test/helper_events_test_runner.js b/devtools/client/markupview/test/helper_events_test_runner.js index 52f96bb5dd44..371fe57918ba 100644 --- a/devtools/client/markupview/test/helper_events_test_runner.js +++ b/devtools/client/markupview/test/helper_events_test_runner.js @@ -7,12 +7,12 @@ * TEST_DATA array. */ function* runEventPopupTests() { - let {inspector} = yield addTab(TEST_URL).then(openInspector); + let {inspector, testActor} = yield addTab(TEST_URL).then(openInspector); yield inspector.markup.expandAll(); - for (let {selector, expected} of TEST_DATA) { - yield checkEventsForNode(selector, expected, inspector); + for (let test of TEST_DATA) { + yield checkEventsForNode(test, inspector, testActor); } // Wait for promises to avoid leaks when running this as a single test. @@ -25,12 +25,36 @@ function* runEventPopupTests() { * Generator function that takes a selector and expected results and returns * the event info. * - * @param {String} selector - * Selector pointing at the node to be inspected + * @param {Object} test + * A test object should contain the following properties: + * - selector {String} a css selector targeting the node to edit + * - expected {Array} array of expected event objects + * - type {String} event type + * - filename {String} filename:line where the evt handler is defined + * - attributes {Array} array of event attributes ({String}) + * - handler {String} string representation of the handler + * - beforeTest {Function} (optional) a function to execute on the page + * before running the test + * @param {InspectorPanel} inspector The instance of InspectorPanel currently + * opened + * @param {TestActorFront} testActor */ -function* checkEventsForNode(selector, expected, inspector) { +function* checkEventsForNode(test, inspector, testActor) { + let {selector, expected, beforeTest} = test; let container = yield getContainerForSelector(selector, inspector); + + if (typeof beforeTest === "function") { + yield beforeTest(inspector, testActor); + } + let evHolder = container.elt.querySelector(".markupview-events"); + + if (expected.length === 0) { + // if no event is expected, simply check that the event bubble is hidden + is(evHolder.style.display, "none", "event bubble should be hidden"); + return; + } + let tooltip = inspector.markup.tooltip; yield selectNode(selector, inspector); diff --git a/devtools/server/actors/inspector.js b/devtools/server/actors/inspector.js index 0d0a66f99b53..f61cc7f5104b 100644 --- a/devtools/server/actors/inspector.js +++ b/devtools/server/actors/inspector.js @@ -863,6 +863,8 @@ var NodeFront = protocol.FrontClass(NodeActor, { this._form.incompleteValue = change.incompleteValue; } else if (change.type === "pseudoClassLock") { this._form.pseudoClassLocks = change.pseudoClassLocks; + } else if (change.type === "events") { + this._form.hasEventListeners = change.hasEventListeners; } }, @@ -1348,6 +1350,32 @@ var WalkerActor = protocol.ActorClass({ this.layoutChangeObserver.on("reflows", this._onReflows); this._onResize = this._onResize.bind(this); this.layoutChangeObserver.on("resize", this._onResize); + + this._onEventListenerChange = this._onEventListenerChange.bind(this); + eventListenerService.addListenerChangeListener(this._onEventListenerChange); + }, + + /** + * Callback for eventListenerService.addListenerChangeListener + * @param nsISimpleEnumerator changesEnum + * enumerator of nsIEventListenerChange + */ + _onEventListenerChange: function(changesEnum) { + let changes = changesEnum.enumerate(); + while (changes.hasMoreElements()) { + let current = changes.getNext().QueryInterface(Ci.nsIEventListenerChange); + let target = current.target; + + if (this._refMap.has(target)) { + let actor = this._refMap.get(target); + let mutation = { + type: "events", + target: actor.actorID, + hasEventListeners: actor._hasEventListeners + }; + this.queueMutation(mutation); + } + } }, // Returns the JSON representation of this object over the wire. @@ -1414,6 +1442,9 @@ var WalkerActor = protocol.ActorClass({ this.layoutChangeObserver = null; releaseLayoutChangesObserver(this.tabActor); + eventListenerService.removeListenerChangeListener( + this._onEventListenerChange); + this.onMutations = null; this.tabActor = null; diff --git a/devtools/server/tests/mochitest/chrome.ini b/devtools/server/tests/mochitest/chrome.ini index 9d3b222c7607..c328b5dcdf1b 100644 --- a/devtools/server/tests/mochitest/chrome.ini +++ b/devtools/server/tests/mochitest/chrome.ini @@ -69,6 +69,7 @@ skip-if = buildapp == 'mulet' [test_inspector-hide.html] [test_inspector-insert.html] [test_inspector-mutations-attr.html] +[test_inspector-mutations-events.html] [test_inspector-mutations-childlist.html] [test_inspector-mutations-frameload.html] [test_inspector-mutations-value.html] diff --git a/devtools/server/tests/mochitest/test_inspector-mutations-events.html b/devtools/server/tests/mochitest/test_inspector-mutations-events.html new file mode 100644 index 000000000000..546122e445ef --- /dev/null +++ b/devtools/server/tests/mochitest/test_inspector-mutations-events.html @@ -0,0 +1,184 @@ + + + + + + Test for Bug 1157469 + + + + + + +Mozilla Bug 1157469 +Test Document +

+ +
+
+ +