frontend/devtools/components/EventListenersUtils.js
function frameworkEventListeners()
    {
        var errorLines = [];
        var eventListeners = [];
        var internalHandlers = [];
        var fetchers = [jQueryFetcher];
        try {
            if (self.devtoolsFrameworkEventListeners && isArrayLike(self.devtoolsFrameworkEventListeners))
                fetchers = fetchers.concat(self.devtoolsFrameworkEventListeners);
        } catch (e) {
            errorLines.push("devtoolsFrameworkEventListeners call produced error: " + toString(e));
        }

        for (var i = 0; i < fetchers.length; ++i) {
            try {
                var fetcherResult = fetchers[i](this);
                if (fetcherResult.eventListeners && isArrayLike(fetcherResult.eventListeners)) {
                    eventListeners = eventListeners.concat(fetcherResult.eventListeners.map(checkEventListener).filter(nonEmptyObject));
                }
                if (fetcherResult.internalHandlers && isArrayLike(fetcherResult.internalHandlers))
                    internalHandlers = internalHandlers.concat(fetcherResult.internalHandlers.map(checkInternalHandler).filter(nonEmptyObject));
            } catch (e) {
                errorLines.push("fetcher call produced error: " + toString(e));
            }
        }
        var result = {eventListeners: eventListeners};
        if (internalHandlers.length)
            result.internalHandlers = internalHandlers;
        if (errorLines.length) {
            var errorString = "Framework Event Listeners API Errors:\n\t" + errorLines.join("\n\t");
            errorString = errorString.substr(0, errorString.length - 1);
            result.errorString = errorString;
        }
        return result;

        /**
         * @param {?Object} obj
         * @return {boolean}
         */
        function isArrayLike(obj)
        {
            if (!obj || typeof obj !== "object")
                return false;
            try {
                if (typeof obj.splice === "function") {
                    var len = obj.length;
                    return typeof len === "number" && (len >>> 0 === len && (len > 0 || 1 / len > 0));
                }
            } catch (e) {
            }
            return false;
        }

        /**
         * @param {*} eventListener
         * @return {?WebInspector.EventListenerObjectInInspectedPage}
         */
        function checkEventListener(eventListener)
        {
            try {
                var errorString = "";
                if (!eventListener)
                    errorString += "empty event listener, ";
                var type = eventListener.type;
                if (!type || (typeof type !== "string"))
                    errorString += "event listener's type isn't string or empty, ";
                var useCapture = eventListener.useCapture;
                if (typeof useCapture !== "boolean")
                    errorString += "event listener's useCapture isn't boolean or undefined, ";
                var passive = eventListener.passive;
                if (typeof passive !== "boolean")
                    errorString += "event listener's passive isn't boolean or undefined, ";
                var handler = eventListener.handler;
                if (!handler || (typeof handler !== "function"))
                    errorString += "event listener's handler isn't a function or empty, ";
                var remove = eventListener.remove;
                if (remove && (typeof remove !== "function"))
                    errorString += "event listener's remove isn't a function, ";
                if (!errorString){
                    return {type: type, useCapture: useCapture, passive: passive, handler: handler, remove: remove};
                } else {
                    errorLines.push(errorString.substr(0, errorString.length - 2));
                    return null;
                }
            } catch (e) {
                errorLines.push(toString(e));
                return null;
            }
        }

        /**
         * @param {*} handler
         * @return {function()|null}
         */
        function checkInternalHandler(handler)
        {
            if (handler && (typeof handler === "function"))
                return handler;
            errorLines.push("internal handler isn't a function or empty");
            return null;
        }

        /**
         * @param {*} obj
         * @return {string}
         * @suppress {uselessCode}
         */
        function toString(obj)
        {
            try {
                return "" + obj;
            } catch (e) {
                return "<error>";
            }
        }

        /**
         * @param {*} obj
         * @return {boolean}
         */
        function nonEmptyObject(obj)
        {
            return !!obj;
        }

        function jQueryFetcher(node)
        {
            if (!node || !(node instanceof Node))
                return {eventListeners: []};
            var jQuery = /** @type {?{fn,data,_data}}*/(window["jQuery"]);
            if (!jQuery || !jQuery.fn)
                return {eventListeners: []};
            var jQueryFunction = /** @type {function(!Node)} */(jQuery);
            var data = jQuery._data || jQuery.data;

            var eventListeners = [];
            var internalHandlers = [];

            if (typeof data === "function") {
                var events = data(node, "events");
                for (var type in events) {
                    for (var key in events[type]) {
                        var frameworkListener = events[type][key];
                        if (typeof frameworkListener === "object" || typeof frameworkListener === "function") {
                            var listener = {
                                handler: frameworkListener.handler || frameworkListener,
                                useCapture: true,
                                passive: false,
                                type: type
                            };
                            listener.remove = jQueryRemove.bind(node, frameworkListener.selector);
                            eventListeners.push(listener);
                        }
                    }
                }
                var nodeData = data(node);
                if (nodeData && typeof nodeData.handle === "function")
                    internalHandlers.push(nodeData.handle);
            }
            var entry = jQueryFunction(node)[0];
            if (entry) {
                var entryEvents = entry["$events"];
                for (var type in entryEvents) {
                    var events = entryEvents[type];
                    for (var key in events) {
                        if (typeof events[key] === "function") {
                            var listener = {
                                handler: events[key],
                                useCapture: true,
                                passive: false,
                                type: type
                            };
                            // We don't support removing for old version < 1.4 of jQuery because it doesn't provide API for getting "selector".
                            eventListeners.push(listener);
                        }
                    }
                }
                if (entry && entry["$handle"])
                    internalHandlers.push(entry["$handle"]);
            }
            return {eventListeners: eventListeners, internalHandlers: internalHandlers};
        }

        /**
         * @param {string} selector
         * @param {string} type
         * @param {function()} handler
         * @this {?Object}
         */
        function jQueryRemove(selector, type, handler)
        {
            if (!this || !(this instanceof Node))
                return;
            var node = /** @type {!Node} */(this);
            var jQuery = /** @type {?{fn,data,_data}}*/(window["jQuery"]);
            if (!jQuery || !jQuery.fn)
                return;
            var jQueryFunction = /** @type {function(!Node)} */(jQuery);
            jQueryFunction(node).off(type, selector, handler);
        }
    }
function frameworkEventListeners()
    {
        var errorLines = [];
        var eventListeners = [];
        var internalHandlers = [];
        var fetchers = [jQueryFetcher];
        try {
            if (self.devtoolsFrameworkEventListeners && isArrayLike(self.devtoolsFrameworkEventListeners))
                fetchers = fetchers.concat(self.devtoolsFrameworkEventListeners);
        } catch (e) {
            errorLines.push("devtoolsFrameworkEventListeners call produced error: " + toString(e));
        }

        for (var i = 0; i < fetchers.length; ++i) {
            try {
                var fetcherResult = fetchers[i](this);
                if (fetcherResult.eventListeners && isArrayLike(fetcherResult.eventListeners)) {
                    eventListeners = eventListeners.concat(fetcherResult.eventListeners.map(checkEventListener).filter(nonEmptyObject));
                }
                if (fetcherResult.internalHandlers && isArrayLike(fetcherResult.internalHandlers))
                    internalHandlers = internalHandlers.concat(fetcherResult.internalHandlers.map(checkInternalHandler).filter(nonEmptyObject));
            } catch (e) {
                errorLines.push("fetcher call produced error: " + toString(e));
            }
        }
        var result = {eventListeners: eventListeners};
        if (internalHandlers.length)
            result.internalHandlers = internalHandlers;
        if (errorLines.length) {
            var errorString = "Framework Event Listeners API Errors:\n\t" + errorLines.join("\n\t");
            errorString = errorString.substr(0, errorString.length - 1);
            result.errorString = errorString;
        }
        return result;

        /**
         * @param {?Object} obj
         * @return {boolean}
         */
        function isArrayLike(obj)
        {
            if (!obj || typeof obj !== "object")
                return false;
            try {
                if (typeof obj.splice === "function") {
                    var len = obj.length;
                    return typeof len === "number" && (len >>> 0 === len && (len > 0 || 1 / len > 0));
                }
            } catch (e) {
            }
            return false;
        }

        /**
         * @param {*} eventListener
         * @return {?WebInspector.EventListenerObjectInInspectedPage}
         */
        function checkEventListener(eventListener)
        {
            try {
                var errorString = "";
                if (!eventListener)
                    errorString += "empty event listener, ";
                var type = eventListener.type;
                if (!type || (typeof type !== "string"))
                    errorString += "event listener's type isn't string or empty, ";
                var useCapture = eventListener.useCapture;
                if (typeof useCapture !== "boolean")
                    errorString += "event listener's useCapture isn't boolean or undefined, ";
                var passive = eventListener.passive;
                if (typeof passive !== "boolean")
                    errorString += "event listener's passive isn't boolean or undefined, ";
                var handler = eventListener.handler;
                if (!handler || (typeof handler !== "function"))
                    errorString += "event listener's handler isn't a function or empty, ";
                var remove = eventListener.remove;
                if (remove && (typeof remove !== "function"))
                    errorString += "event listener's remove isn't a function, ";
                if (!errorString){
                    return {type: type, useCapture: useCapture, passive: passive, handler: handler, remove: remove};
                } else {
                    errorLines.push(errorString.substr(0, errorString.length - 2));
                    return null;
                }
            } catch (e) {
                errorLines.push(toString(e));
                return null;
            }
        }

        /**
         * @param {*} handler
         * @return {function()|null}
         */
        function checkInternalHandler(handler)
        {
            if (handler && (typeof handler === "function"))
                return handler;
            errorLines.push("internal handler isn't a function or empty");
            return null;
        }

        /**
         * @param {*} obj
         * @return {string}
         * @suppress {uselessCode}
         */
        function toString(obj)
        {
            try {
                return "" + obj;
            } catch (e) {
                return "<error>";
            }
        }

        /**
         * @param {*} obj
         * @return {boolean}
         */
        function nonEmptyObject(obj)
        {
            return !!obj;
        }

        function jQueryFetcher(node)
        {
            if (!node || !(node instanceof Node))
                return {eventListeners: []};
            var jQuery = /** @type {?{fn,data,_data}}*/(window["jQuery"]);
            if (!jQuery || !jQuery.fn)
                return {eventListeners: []};
            var jQueryFunction = /** @type {function(!Node)} */(jQuery);
            var data = jQuery._data || jQuery.data;

            var eventListeners = [];
            var internalHandlers = [];

            if (typeof data === "function") {
                var events = data(node, "events");
                for (var type in events) {
                    for (var key in events[type]) {
                        var frameworkListener = events[type][key];
                        if (typeof frameworkListener === "object" || typeof frameworkListener === "function") {
                            var listener = {
                                handler: frameworkListener.handler || frameworkListener,
                                useCapture: true,
                                passive: false,
                                type: type
                            };
                            listener.remove = jQueryRemove.bind(node, frameworkListener.selector);
                            eventListeners.push(listener);
                        }
                    }
                }
                var nodeData = data(node);
                if (nodeData && typeof nodeData.handle === "function")
                    internalHandlers.push(nodeData.handle);
            }
            var entry = jQueryFunction(node)[0];
            if (entry) {
                var entryEvents = entry["$events"];
                for (var type in entryEvents) {
                    var events = entryEvents[type];
                    for (var key in events) {
                        if (typeof events[key] === "function") {
                            var listener = {
                                handler: events[key],
                                useCapture: true,
                                passive: false,
                                type: type
                            };
                            // We don't support removing for old version < 1.4 of jQuery because it doesn't provide API for getting "selector".
                            eventListeners.push(listener);
                        }
                    }
                }
                if (entry && entry["$handle"])
                    internalHandlers.push(entry["$handle"]);
            }
            return {eventListeners: eventListeners, internalHandlers: internalHandlers};
        }

        /**
         * @param {string} selector
         * @param {string} type
         * @param {function()} handler
         * @this {?Object}
         */
        function jQueryRemove(selector, type, handler)
        {
            if (!this || !(this instanceof Node))
                return;
            var node = /** @type {!Node} */(this);
            var jQuery = /** @type {?{fn,data,_data}}*/(window["jQuery"]);
            if (!jQuery || !jQuery.fn)
                return;
            var jQueryFunction = /** @type {function(!Node)} */(jQuery);
            jQueryFunction(node).off(type, selector, handler);
        }
    }
/*
frameworkEventListeners fetcher functions should produce following output:
{
framework event listeners
"eventListeners": [
{
"handler": function(),
"useCapture": true,
"passive": false,
"type": "change",
"remove": function(type, handler, useCapture, passive)
},
...
],
internal framework event handlers
"internalHandlers": [
function(),
function(),
...
]
}
 
@suppressReceiverCheck
@return {!{eventListeners:!Array<!WebInspector.EventListenerObjectInInspectedPage>, internalHandlers:?Array<function()>}}
@this {Object}