| Index: Source/devtools/front_end/components/EventListenersUtils.js
|
| diff --git a/Source/devtools/front_end/components/EventListenersUtils.js b/Source/devtools/front_end/components/EventListenersUtils.js
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..43f750f2d09bfc7f027661e22b42b34614f89f39
|
| --- /dev/null
|
| +++ b/Source/devtools/front_end/components/EventListenersUtils.js
|
| @@ -0,0 +1,337 @@
|
| +// Copyright 2015 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +/** @typedef {{eventListeners:!Array<!WebInspector.EventListener>, internalHandlers:?WebInspector.RemoteArray}} */
|
| +WebInspector.FrameworkEventListenersObject;
|
| +
|
| +/** @typedef {{type: string, useCapture: boolean, handler: function()}} */
|
| +WebInspector.EventListenerObjectInInspectedPage;
|
| +
|
| +/**
|
| + * @param {!WebInspector.RemoteObject} object
|
| + * @return {!Promise<!WebInspector.FrameworkEventListenersObject>}
|
| + */
|
| +WebInspector.EventListener.frameworkEventListeners = function(object)
|
| +{
|
| + var listenersResult = /** @type {!WebInspector.FrameworkEventListenersObject} */({eventListeners: []});
|
| + return object.callFunctionPromise(frameworkEventListeners, undefined)
|
| + .then(assertCallFunctionResult)
|
| + .then(getOwnProperties)
|
| + .then(createEventListeners)
|
| + .then(returnResult)
|
| + .catchException(listenersResult);
|
| +
|
| + /**
|
| + * @param {!WebInspector.RemoteObject} object
|
| + * @return {!Promise<!{properties: ?Array.<!WebInspector.RemoteObjectProperty>, internalProperties: ?Array.<!WebInspector.RemoteObjectProperty>}>}
|
| + */
|
| + function getOwnProperties(object)
|
| + {
|
| + return object.getOwnPropertiesPromise();
|
| + }
|
| +
|
| + /**
|
| + * @param {!{properties: ?Array<!WebInspector.RemoteObjectProperty>, internalProperties: ?Array<!WebInspector.RemoteObjectProperty>}} result
|
| + * @return {!Promise<undefined>}
|
| + */
|
| + function createEventListeners(result)
|
| + {
|
| + if (!result.properties)
|
| + throw new Error("Object properties is empty");
|
| + var promises = [];
|
| + for (var property of result.properties) {
|
| + if (property.name === "eventListeners" && property.value)
|
| + promises.push(convertToEventListeners(property.value).then(storeEventListeners));
|
| + if (property.name === "internalHandlers" && property.value)
|
| + promises.push(convertToInternalHandlers(property.value).then(storeInternalHandlers));
|
| + }
|
| + return /** @type {!Promise<undefined>} */(Promise.all(promises));
|
| + }
|
| +
|
| + /**
|
| + * @param {!WebInspector.RemoteObject} pageEventListenersObject
|
| + * @return {!Promise<!Array<!WebInspector.EventListener>>}
|
| + */
|
| + function convertToEventListeners(pageEventListenersObject)
|
| + {
|
| + return WebInspector.RemoteArray.objectAsArray(pageEventListenersObject).map(toEventListener).then(filterOutEmptyObjects);
|
| +
|
| + /**
|
| + * @param {!WebInspector.RemoteObject} listenerObject
|
| + * @return {!Promise<?WebInspector.EventListener>}
|
| + */
|
| + function toEventListener(listenerObject)
|
| + {
|
| + /** @type {string} */
|
| + var type;
|
| + /** @type {boolean} */
|
| + var useCapture;
|
| + /** @type {?WebInspector.RemoteObject} */
|
| + var handler = null;
|
| + /** @type {?WebInspector.DebuggerModel.Location} */
|
| + var location = null;
|
| +
|
| + var promises = [];
|
| + promises.push(listenerObject.callFunctionJSONPromise(truncatePageEventListener, undefined).then(storeTrunkatedListener));
|
| +
|
| + /**
|
| + * @suppressReceiverCheck
|
| + * @this {WebInspector.EventListenerObjectInInspectedPage}
|
| + * @return {!{type:string, useCapture:boolean}}
|
| + */
|
| + function truncatePageEventListener()
|
| + {
|
| + return {type: this.type, useCapture: this.useCapture};
|
| + }
|
| +
|
| + /**
|
| + * @param {!{type:string, useCapture: boolean}} truncatedListener
|
| + */
|
| + function storeTrunkatedListener(truncatedListener)
|
| + {
|
| + type = truncatedListener.type;
|
| + useCapture = truncatedListener.useCapture;
|
| + }
|
| +
|
| + promises.push(listenerObject.callFunctionPromise(handlerFunction).then(assertCallFunctionResult).then(toTargetFunction).then(storeFunctionWithDetails));
|
| +
|
| + /**
|
| + * @suppressReceiverCheck
|
| + * @return {function()}
|
| + * @this {WebInspector.EventListenerObjectInInspectedPage}
|
| + */
|
| + function handlerFunction()
|
| + {
|
| + return this.handler;
|
| + }
|
| +
|
| + /**
|
| + * @param {!WebInspector.RemoteObject} functionObject
|
| + * @return {!Promise<undefined>}
|
| + */
|
| + function storeFunctionWithDetails(functionObject)
|
| + {
|
| + handler = functionObject;
|
| + return /** @type {!Promise<undefined>} */(functionObject.functionDetailsPromise().then(storeFunctionDetails));
|
| + }
|
| +
|
| + /**
|
| + * @param {?WebInspector.DebuggerModel.FunctionDetails} functionDetails
|
| + */
|
| + function storeFunctionDetails(functionDetails)
|
| + {
|
| + location = functionDetails ? functionDetails.location : null;
|
| + }
|
| +
|
| + return Promise.all(promises).then(createEventListener).catchException(/** @type {?WebInspector.EventListener} */(null));
|
| +
|
| + /**
|
| + * @return {!WebInspector.EventListener}
|
| + */
|
| + function createEventListener()
|
| + {
|
| + if (!location)
|
| + throw new Error("Empty event listener's location");
|
| + return new WebInspector.EventListener(handler._target, type, useCapture, handler, location, "frameworkUser");
|
| + }
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * @param {!WebInspector.RemoteObject} pageInternalHandlersObject
|
| + * @return {!Promise<!WebInspector.RemoteArray>}
|
| + */
|
| + function convertToInternalHandlers(pageInternalHandlersObject)
|
| + {
|
| + return WebInspector.RemoteArray.objectAsArray(pageInternalHandlersObject).map(toTargetFunction)
|
| + .then(WebInspector.RemoteArray.createFromRemoteObjects);
|
| + }
|
| +
|
| + /**
|
| + * @param {!WebInspector.RemoteObject} functionObject
|
| + * @return {!Promise<!WebInspector.RemoteObject>}
|
| + */
|
| + function toTargetFunction(functionObject)
|
| + {
|
| + return WebInspector.RemoteFunction.objectAsFunction(functionObject).targetFunction();
|
| + }
|
| +
|
| + /**
|
| + * @param {!Array<!WebInspector.EventListener>} eventListeners
|
| + */
|
| + function storeEventListeners(eventListeners)
|
| + {
|
| + listenersResult.eventListeners = eventListeners;
|
| + }
|
| +
|
| + /**
|
| + * @param {!WebInspector.RemoteArray} internalHandlers
|
| + */
|
| + function storeInternalHandlers(internalHandlers)
|
| + {
|
| + listenersResult.internalHandlers = internalHandlers;
|
| + }
|
| +
|
| + /**
|
| + * @return {!WebInspector.FrameworkEventListenersObject}
|
| + */
|
| + function returnResult()
|
| + {
|
| + return listenersResult;
|
| + }
|
| +
|
| + /**
|
| + * @param {!WebInspector.CallFunctionResult} result
|
| + * @return {!WebInspector.RemoteObject}
|
| + */
|
| + function assertCallFunctionResult(result)
|
| + {
|
| + if (result.wasThrown || !result.object)
|
| + throw new Error("Exception in callFunction or empty result");
|
| + return result.object;
|
| + }
|
| +
|
| + /**
|
| + * @param {!Array<?T>} objects
|
| + * @return {!Array<!T>}
|
| + * @template T
|
| + */
|
| + function filterOutEmptyObjects(objects)
|
| + {
|
| + return objects.filter(filterOutEmpty);
|
| +
|
| + /**
|
| + * @param {?T} object
|
| + * @return {boolean}
|
| + * @template T
|
| + */
|
| + function filterOutEmpty(object)
|
| + {
|
| + return !!object;
|
| + }
|
| + }
|
| +
|
| + /*
|
| + frameworkEventListeners fetcher functions should produce following output:
|
| + {
|
| + // framework event listeners
|
| + "eventListeners": [
|
| + {
|
| + "handler": function(),
|
| + "useCapture": true,
|
| + "type": "change"
|
| + },
|
| + ...
|
| + ],
|
| + // internal framework event handlers
|
| + "internalHandlers": [
|
| + function(),
|
| + function(),
|
| + ...
|
| + ]
|
| + }
|
| + */
|
| + /**
|
| + * @suppressReceiverCheck
|
| + * @return {!{eventListeners:!Array<!WebInspector.EventListenerObjectInInspectedPage>, internalHandlers:?Array<function()>}}
|
| + * @this {Object}
|
| + */
|
| + function frameworkEventListeners()
|
| + {
|
| + var eventListeners = [];
|
| + var internalHandlers = [];
|
| + var fetchers = [jQueryFetcher];
|
| + try {
|
| + if (self.devtoolsPageEventListeners && isArrayLike(self.devtoolsPageEventListeners))
|
| + fetchers = fetchers.concat(self.devtoolsPageEventListeners);
|
| + } catch (e) {
|
| + }
|
| +
|
| + for (var i = 0; i < fetchers.length; ++i) {
|
| + try {
|
| + var fetcherResult = fetchers[i](this);
|
| + eventListeners = eventListeners.concat(fetcherResult.eventListeners);
|
| + if (fetcherResult.internalHandlers)
|
| + internalHandlers = internalHandlers.concat(fetcherResult.internalHandlers);
|
| + } catch (e) {
|
| + }
|
| + }
|
| + var result = {eventListeners: eventListeners};
|
| + if (internalHandlers.length)
|
| + result.internalHandlers = internalHandlers;
|
| + 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;
|
| + }
|
| +
|
| + 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,
|
| + type: type
|
| + };
|
| + eventListeners.push(listener);
|
| + }
|
| + }
|
| + }
|
| + var nodeData = data(node);
|
| + if (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,
|
| + type: type
|
| + };
|
| + eventListeners.push(listener);
|
| + }
|
| + }
|
| + }
|
| + if (entry && entry["$handle"])
|
| + internalHandlers.push(entry["$handle"]);
|
| + }
|
| + return {eventListeners: eventListeners, internalHandlers: internalHandlers};
|
| + }
|
| + }
|
| +}
|
|
|