| /* |
| * Copyright (C) 2008, 2013 Apple Inc. All Rights Reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY |
| * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR |
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
| * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| WI.Object = class WebInspectorObject |
| { |
| constructor() |
| { |
| this._listeners = null; |
| } |
| |
| // Static |
| |
| static addEventListener(eventType, listener, thisObject) |
| { |
| thisObject = thisObject || null; |
| |
| console.assert(eventType, "Object.addEventListener: invalid event type ", eventType, "(listener: ", listener, "thisObject: ", thisObject, ")"); |
| if (!eventType) |
| return null; |
| |
| console.assert(listener, "Object.addEventListener: invalid listener ", listener, "(event type: ", eventType, "thisObject: ", thisObject, ")"); |
| if (!listener) |
| return null; |
| |
| if (!this._listeners) |
| this._listeners = new Map(); |
| |
| let listenersTable = this._listeners.get(eventType); |
| if (!listenersTable) { |
| listenersTable = new ListMultimap(); |
| this._listeners.set(eventType, listenersTable); |
| } |
| |
| listenersTable.add(thisObject, listener); |
| return listener; |
| } |
| |
| static singleFireEventListener(eventType, listener, thisObject) |
| { |
| let wrappedCallback = function() { |
| this.removeEventListener(eventType, wrappedCallback, null); |
| listener.apply(thisObject, arguments); |
| }.bind(this); |
| |
| this.addEventListener(eventType, wrappedCallback, null); |
| return wrappedCallback; |
| } |
| |
| static removeEventListener(eventType, listener, thisObject) |
| { |
| eventType = eventType || null; |
| listener = listener || null; |
| thisObject = thisObject || null; |
| |
| if (!this._listeners) |
| return; |
| |
| if (thisObject && !eventType) { |
| this._listeners.forEach(function(listenersTable) { |
| listenersTable.deleteAll(thisObject); |
| }); |
| |
| return; |
| } |
| |
| let listenersTable = this._listeners.get(eventType); |
| if (!listenersTable || listenersTable.size === 0) |
| return; |
| |
| let didDelete = listenersTable.delete(thisObject, listener); |
| console.assert(didDelete, "removeEventListener cannot remove " + eventType.toString() + " because it doesn't exist."); |
| } |
| |
| static awaitEvent(eventType) |
| { |
| let wrapper = new WI.WrappedPromise; |
| this.singleFireEventListener(eventType, (event) => wrapper.resolve(event)); |
| return wrapper.promise; |
| } |
| |
| // Only used by tests. |
| static hasEventListeners(eventType) |
| { |
| if (!this._listeners) |
| return false; |
| |
| let listenersTable = this._listeners.get(eventType); |
| return listenersTable && listenersTable.size > 0; |
| } |
| |
| // This should only be used within regression tests to detect leaks. |
| static retainedObjectsWithPrototype(proto) |
| { |
| let results = new Set; |
| |
| if (this._listeners) { |
| this._listeners.forEach(function(listenersTable, eventType) { |
| listenersTable.forEach(function(pair) { |
| let thisObject = pair[0]; |
| if (thisObject instanceof proto) |
| results.add(thisObject); |
| }); |
| }); |
| } |
| |
| return results; |
| } |
| |
| // Public |
| |
| addEventListener() { return WI.Object.addEventListener.apply(this, arguments); } |
| singleFireEventListener() { return WI.Object.singleFireEventListener.apply(this, arguments); } |
| removeEventListener() { return WI.Object.removeEventListener.apply(this, arguments); } |
| awaitEvent() { return WI.Object.awaitEvent.apply(this, arguments); } |
| hasEventListeners() { return WI.Object.hasEventListeners.apply(this, arguments); } |
| retainedObjectsWithPrototype() { return WI.Object.retainedObjectsWithPrototype.apply(this, arguments); } |
| |
| dispatchEventToListeners(eventType, eventData) |
| { |
| let event = new WI.Event(this, eventType, eventData); |
| |
| function dispatch(object) |
| { |
| if (!object || event._stoppedPropagation) |
| return; |
| |
| let listenerTypesMap = object._listeners; |
| if (!listenerTypesMap || !object.hasOwnProperty("_listeners")) |
| return; |
| |
| console.assert(listenerTypesMap instanceof Map); |
| |
| let listenersTable = listenerTypesMap.get(eventType); |
| if (!listenersTable) |
| return; |
| |
| // Make a copy with slice so mutations during the loop doesn't affect us. |
| let listeners = listenersTable.toArray(); |
| |
| // Iterate over the listeners and call them. Stop if stopPropagation is called. |
| for (let i = 0, length = listeners.length; i < length; ++i) { |
| let [thisObject, listener] = listeners[i]; |
| listener.call(thisObject, event); |
| if (event._stoppedPropagation) |
| break; |
| } |
| } |
| |
| // Dispatch to listeners of this specific object. |
| dispatch(this); |
| |
| // Allow propagation again so listeners on the constructor always have a crack at the event. |
| event._stoppedPropagation = false; |
| |
| // Dispatch to listeners on all constructors up the prototype chain, including the immediate constructor. |
| let constructor = this.constructor; |
| while (constructor) { |
| dispatch(constructor); |
| |
| if (!constructor.prototype.__proto__) |
| break; |
| |
| constructor = constructor.prototype.__proto__.constructor; |
| } |
| |
| return event.defaultPrevented; |
| } |
| }; |
| |
| WI.Event = class Event |
| { |
| constructor(target, type, data) |
| { |
| this.target = target; |
| this.type = type; |
| this.data = data; |
| this.defaultPrevented = false; |
| this._stoppedPropagation = false; |
| } |
| |
| stopPropagation() |
| { |
| this._stoppedPropagation = true; |
| } |
| |
| preventDefault() |
| { |
| this.defaultPrevented = true; |
| } |
| }; |
| |
| WI.notifications = new WI.Object; |
| |
| WI.Notification = { |
| GlobalModifierKeysDidChange: "global-modifiers-did-change", |
| PageArchiveStarted: "page-archive-started", |
| PageArchiveEnded: "page-archive-ended", |
| ExtraDomainsActivated: "extra-domains-activated", |
| VisibilityStateDidChange: "visibility-state-did-change", |
| TransitionPageTarget: "transition-page-target", |
| }; |