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..6b3b5fa915cfc323a789c130648b59f523265573 |
--- /dev/null |
+++ b/Source/devtools/front_end/components/EventListenersUtils.js |
@@ -0,0 +1,289 @@ |
+// 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. |
+ |
+/** |
+ * @param {!WebInspector.RemoteObject} object |
+ * @param {function(this:Object):{eventListeners:!Array<!{type:string, handler:(function()|!Object), useCapture:boolean}>, internalHandlers:?Array<function()>}} getter |
+ * @return {!Promise<!{eventListeners:!Array<!WebInspector.EventListener>, internalHandlers:?WebInspector.RemoteArray}>} |
+ */ |
+WebInspector.frameworkEventListeners = function(object, getter) |
pfeldman
2015/09/02 02:58:52
Please don't put anything on the global WebInspect
pfeldman
2015/09/02 02:58:52
What is getter?
kozy
2015/09/02 17:34:13
Moved to WebInspector.EventListener
kozy
2015/09/02 17:34:13
Getter is moved into this function.
|
+{ |
+ var listenersResult = {eventListeners: []}; |
+ return object.callFunctionPromise(getter, undefined) |
+ .then(assertCallFunctionResult) |
+ .then(getterCallback) |
+ .then(propertiesCallback) |
+ .then(returnResult); |
+ |
+ /** |
+ * @param {!WebInspector.RemoteObject} object |
+ * @return {!Promise<!{properties: ?Array<!WebInspector.RemoteObjectProperty>, internalProperties: ?Array<!WebInspector.RemoteObjectProperty>}>} |
+ */ |
+ function getterCallback(object) |
+ { |
+ return object.getOwnPropertiesPromise(); |
+ } |
+ |
+ /** |
+ * @param {!{properties: ?Array<!WebInspector.RemoteObjectProperty>, internalProperties: ?Array<!WebInspector.RemoteObjectProperty>}} result |
+ * @return {!Promise<void>} |
+ */ |
+ function propertiesCallback(result) |
+ { |
+ if (!result.properties) |
+ return Promise.resolve(); |
+ var promises = []; |
+ for (var property of result.properties) { |
+ if (property.name === "eventListeners" && property.value) { |
pfeldman
2015/09/02 02:58:52
Could you explain the structure of the objects you
kozy
2015/09/02 17:34:13
Added before frameworkEventListenersGetter functio
|
+ promises.push(WebInspector.RemoteArray.createFromObject(property.value) |
+ .map(toEventListener) |
+ .then(nonEmptyObjects) |
+ .then(storeResultTo.bind(null, listenersResult, "eventListeners"))); |
+ } |
+ if (property.name === "internalHandlers" && property.value) { |
+ promises.push(WebInspector.RemoteArray.createFromObject(property.value) |
+ .map(toTargetFunction) |
+ .then(WebInspector.RemoteArray.createFromArray) |
+ .then(storeResultTo.bind(null, listenersResult, "internalHandlers"))); |
+ } |
+ } |
+ return /** @type {!Promise<void>} */(Promise.all(promises)); |
+ |
+ /** |
+ * @param {!WebInspector.RemoteObject} listenerObject |
+ * @return {!Promise<?WebInspector.EventListener>} |
+ */ |
+ function toEventListener(listenerObject) |
+ { |
+ /** @type {?} */ |
+ var eventListenerData = {}; |
+ var promises = []; |
+ |
+ promises.push(listenerObject.callFunctionJSONPromise(identity, undefined).then(storeResultTo.bind(null, eventListenerData, "jsonData"))); |
+ promises.push(listenerObject.callFunctionPromise(listenerEffectiveFunction).then(assertCallFunctionResult) |
+ .then(toTargetFunction) |
+ .then(storeFunctionWithDetails)); |
+ return Promise.all(promises).then(createEventListener.bind(null, eventListenerData)).catchException(/** @type {?WebInspector.EventListener} */(null)); |
+ |
+ /** |
+ * @suppressReceiverCheck |
+ * @this {T} |
+ * @return {T} |
+ * @template T |
+ */ |
+ function identity() |
+ { |
+ return this; |
+ } |
+ |
+ /** |
+ * @suppressReceiverCheck |
+ * @return {function()} |
+ * @this {Object} |
+ */ |
+ function listenerEffectiveFunction() |
+ { |
+ if (typeof this.handler === "function") |
+ return this.handler; |
+ return typeof this.handler === "object" ? (this.handler.handlerEvent || this.handler.constructor) : null; |
+ } |
+ |
+ /** |
+ * @param {!WebInspector.RemoteObject} functionObject |
+ * @return {!Promise<void>} |
+ */ |
+ function storeFunctionWithDetails(functionObject) |
+ { |
+ eventListenerData.handlerFunction = functionObject; |
+ return /** @type {!Promise<void>} */(functionObject.functionDetailsPromise().then(storeResultTo.bind(null, eventListenerData, "handlerDetails"))); |
+ } |
+ |
+ /** |
+ * @param {!{jsonData: {type:string, useCapture:boolean, handler:function()}, handlerFunction: !WebInspector.RemoteObject, handlerDetails: !WebInspector.DebuggerModel.FunctionDetails}} eventListenerData |
+ * @return {!WebInspector.EventListener} |
+ */ |
+ function createEventListener(eventListenerData) |
+ { |
+ return new WebInspector.EventListener(eventListenerData.handlerFunction._target, |
+ eventListenerData.jsonData.type, |
+ eventListenerData.jsonData.useCapture, |
+ eventListenerData.handlerFunction, |
+ /** @type {!WebInspector.DebuggerModel.Location}} */ (eventListenerData.handlerDetails.location), |
+ "frameworkUser"); |
+ } |
+ } |
+ |
+ /** |
+ * @param {!WebInspector.RemoteObject} functionObject |
+ * @return {!Promise<!WebInspector.RemoteObject>} |
+ */ |
+ function toTargetFunction(functionObject) |
+ { |
+ return WebInspector.RemoteFunction.createFromObject(functionObject).targetFunction(); |
+ } |
+ |
+ /** |
+ * @param {!Array<?T>} objects |
+ * @return {!Array<!T>} |
+ * @template T |
+ */ |
+ function nonEmptyObjects(objects) |
+ { |
+ return objects.filter(filterOutEmpty); |
+ |
+ /** |
+ * @param {?T} object |
+ * @return {boolean} |
+ * @template T |
+ */ |
+ function filterOutEmpty(object) |
+ { |
+ return !!object; |
+ } |
+ } |
+ } |
+ |
+ /** |
+ * @return {!{eventListeners:!Array<!WebInspector.EventListener>, internalHandlers:?WebInspector.RemoteArray}} |
+ */ |
+ function returnResult() |
+ { |
+ return /**@type {!{eventListeners:!Array<!WebInspector.EventListener>, internalHandlers:?WebInspector.RemoteArray}} */(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; |
+ } |
+} |
+ |
+/** |
+ * @this {Object} |
+ */ |
+WebInspector._frameworkEventListenersGetter = function() |
pfeldman
2015/09/02 02:58:51
Where is this used?
kozy
2015/09/02 17:34:13
Moved to WebInspector.EventListener.frameworkEvent
|
+{ |
+ var eventListeners = []; |
+ var internalHandlers = []; |
+ var getters = [jQueryGetter]; |
+ try { |
+ if (self.devtoolsFrameworkEventListeners && isArrayLike(self.devtoolsFrameworkEventListeners)) |
+ getters = getters.concat(self.devtoolsFrameworkEventListeners); |
+ } catch (e) { |
+ } |
+ |
+ for (var i = 0; i < getters.length; ++i) { |
+ try { |
+ var getterResult = getters[i](this); |
+ eventListeners = eventListeners.concat(getterResult.eventListeners); |
+ internalHandlers = internalHandlers.concat(getterResult.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 jQueryGetter(node) |
+ { |
+ return {eventListeners: jQueryEventListeners(node), internalHandlers: jQueryInternalHandlers(node)}; |
+ } |
+ |
+ /** |
+ * @param {?Object} node |
+ * @return {!Array<!{handler:function(), useCapture: boolean, type:string}>} |
+ */ |
+ function jQueryEventListeners(node) |
+ { |
+ if (!node || !(node instanceof Node)) |
+ return []; |
+ var hasJQuery = (typeof jQuery !== 'undefined') && jQuery.fn; |
+ if (!hasJQuery) |
+ return []; |
+ var listeners = []; |
+ var data = jQuery._data || jQuery.data; |
+ 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 |
+ }; |
+ listeners.push(listener); |
+ } |
+ } |
+ } |
+ } |
+ var entry = jQuery(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 |
+ }; |
+ listeners.push(listener); |
+ } |
+ } |
+ } |
+ } |
+ return listeners; |
+ } |
+ |
+ /** |
+ * @param {?Object} node |
+ * @return {!Array<function()>} |
+ */ |
+ function jQueryInternalHandlers(node) |
+ { |
+ if (!(node instanceof Node)) |
+ return []; |
+ var hasJQuery = (typeof jQuery !== 'undefined') && jQuery.fn; |
+ if (!hasJQuery) |
+ return []; |
+ var handlers = []; |
+ var data = jQuery._data || jQuery.data; |
+ if (typeof data === "function") { |
+ var nodeData = data(node); |
+ if (typeof nodeData.handle === "function") |
+ handlers.push(nodeData.handle); |
+ } |
+ var entry = jQuery(node)[0]; |
+ if (entry && entry["$handle"]) |
+ handlers.push(entry["$handle"]); |
+ return handlers; |
+ } |
+} |