summaryrefslogtreecommitdiffstats
path: root/devtools/client/debugger/content/views/event-listeners-view.js
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /devtools/client/debugger/content/views/event-listeners-view.js
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'devtools/client/debugger/content/views/event-listeners-view.js')
-rw-r--r--devtools/client/debugger/content/views/event-listeners-view.js295
1 files changed, 295 insertions, 0 deletions
diff --git a/devtools/client/debugger/content/views/event-listeners-view.js b/devtools/client/debugger/content/views/event-listeners-view.js
new file mode 100644
index 000000000..993d6506e
--- /dev/null
+++ b/devtools/client/debugger/content/views/event-listeners-view.js
@@ -0,0 +1,295 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+/* import-globals-from ../../debugger-controller.js */
+
+const actions = require("../actions/event-listeners");
+const { bindActionCreators } = require("devtools/client/shared/vendor/redux");
+const { Heritage, WidgetMethods } = require("devtools/client/shared/widgets/view-helpers");
+const { SideMenuWidget } = require("resource://devtools/client/shared/widgets/SideMenuWidget.jsm");
+
+/**
+ * Functions handling the event listeners UI.
+ */
+function EventListenersView(controller) {
+ dumpn("EventListenersView was instantiated");
+
+ this.actions = bindActionCreators(actions, controller.dispatch);
+ this.getState = () => controller.getState().eventListeners;
+
+ this._onCheck = this._onCheck.bind(this);
+ this._onClick = this._onClick.bind(this);
+
+ controller.onChange("event-listeners", this.renderListeners.bind(this));
+}
+
+EventListenersView.prototype = Heritage.extend(WidgetMethods, {
+ /**
+ * Initialization function, called when the debugger is started.
+ */
+ initialize: function () {
+ dumpn("Initializing the EventListenersView");
+
+ this.widget = new SideMenuWidget(document.getElementById("event-listeners"), {
+ showItemCheckboxes: true,
+ showGroupCheckboxes: true
+ });
+
+ this.emptyText = L10N.getStr("noEventListenersText");
+ this._eventCheckboxTooltip = L10N.getStr("eventCheckboxTooltip");
+ this._onSelectorString = " " + L10N.getStr("eventOnSelector") + " ";
+ this._inSourceString = " " + L10N.getStr("eventInSource") + " ";
+ this._inNativeCodeString = L10N.getStr("eventNative");
+
+ this.widget.addEventListener("check", this._onCheck, false);
+ this.widget.addEventListener("click", this._onClick, false);
+ },
+
+ /**
+ * Destruction function, called when the debugger is closed.
+ */
+ destroy: function () {
+ dumpn("Destroying the EventListenersView");
+
+ this.widget.removeEventListener("check", this._onCheck, false);
+ this.widget.removeEventListener("click", this._onClick, false);
+ },
+
+ renderListeners: function (listeners) {
+ listeners.forEach(listener => {
+ this.addListener(listener, { staged: true });
+ });
+
+ // Flushes all the prepared events into the event listeners container.
+ this.commit();
+ },
+
+ /**
+ * Adds an event to this event listeners container.
+ *
+ * @param object aListener
+ * The listener object coming from the active thread.
+ * @param object aOptions [optional]
+ * Additional options for adding the source. Supported options:
+ * - staged: true to stage the item to be appended later
+ */
+ addListener: function (aListener, aOptions = {}) {
+ let { node: { selector }, function: { url }, type } = aListener;
+ if (!type) return;
+
+ // Some listener objects may be added from plugins, thus getting
+ // translated to native code.
+ if (!url) {
+ url = this._inNativeCodeString;
+ }
+
+ // If an event item for this listener's url and type was already added,
+ // avoid polluting the view and simply increase the "targets" count.
+ let eventItem = this.getItemForPredicate(aItem =>
+ aItem.attachment.url == url &&
+ aItem.attachment.type == type);
+
+ if (eventItem) {
+ let { selectors, view: { targets } } = eventItem.attachment;
+ if (selectors.indexOf(selector) == -1) {
+ selectors.push(selector);
+ targets.setAttribute("value", L10N.getFormatStr("eventNodes", selectors.length));
+ }
+ return;
+ }
+
+ // There's no easy way of grouping event types into higher-level groups,
+ // so we need to do this by hand.
+ let is = (...args) => args.indexOf(type) != -1;
+ let has = str => type.includes(str);
+ let starts = str => type.startsWith(str);
+ let group;
+
+ if (starts("animation")) {
+ group = L10N.getStr("animationEvents");
+ } else if (starts("audio")) {
+ group = L10N.getStr("audioEvents");
+ } else if (is("levelchange")) {
+ group = L10N.getStr("batteryEvents");
+ } else if (is("cut", "copy", "paste")) {
+ group = L10N.getStr("clipboardEvents");
+ } else if (starts("composition")) {
+ group = L10N.getStr("compositionEvents");
+ } else if (starts("device")) {
+ group = L10N.getStr("deviceEvents");
+ } else if (is("fullscreenchange", "fullscreenerror", "orientationchange",
+ "overflow", "resize", "scroll", "underflow", "zoom")) {
+ group = L10N.getStr("displayEvents");
+ } else if (starts("drag") || starts("drop")) {
+ group = L10N.getStr("dragAndDropEvents");
+ } else if (starts("gamepad")) {
+ group = L10N.getStr("gamepadEvents");
+ } else if (is("canplay", "canplaythrough", "durationchange", "emptied",
+ "ended", "loadeddata", "loadedmetadata", "pause", "play", "playing",
+ "ratechange", "seeked", "seeking", "stalled", "suspend", "timeupdate",
+ "volumechange", "waiting")) {
+ group = L10N.getStr("mediaEvents");
+ } else if (is("blocked", "complete", "success", "upgradeneeded", "versionchange")) {
+ group = L10N.getStr("indexedDBEvents");
+ } else if (is("blur", "change", "focus", "focusin", "focusout", "invalid",
+ "reset", "select", "submit")) {
+ group = L10N.getStr("interactionEvents");
+ } else if (starts("key") || is("input")) {
+ group = L10N.getStr("keyboardEvents");
+ } else if (starts("mouse") || has("click") || is("contextmenu", "show", "wheel")) {
+ group = L10N.getStr("mouseEvents");
+ } else if (starts("DOM")) {
+ group = L10N.getStr("mutationEvents");
+ } else if (is("abort", "error", "hashchange", "load", "loadend", "loadstart",
+ "pagehide", "pageshow", "progress", "timeout", "unload", "uploadprogress",
+ "visibilitychange")) {
+ group = L10N.getStr("navigationEvents");
+ } else if (is("pointerlockchange", "pointerlockerror")) {
+ group = L10N.getStr("pointerLockEvents");
+ } else if (is("compassneedscalibration", "userproximity")) {
+ group = L10N.getStr("sensorEvents");
+ } else if (starts("storage")) {
+ group = L10N.getStr("storageEvents");
+ } else if (is("beginEvent", "endEvent", "repeatEvent")) {
+ group = L10N.getStr("timeEvents");
+ } else if (starts("touch")) {
+ group = L10N.getStr("touchEvents");
+ } else {
+ group = L10N.getStr("otherEvents");
+ }
+
+ // Create the element node for the event listener item.
+ const itemView = this._createItemView(type, selector, url);
+
+ // Event breakpoints survive target navigations. Make sure the newly
+ // inserted event item is correctly checked.
+ const activeEventNames = this.getState().activeEventNames;
+ const checkboxState = activeEventNames.indexOf(type) != -1;
+
+ // Append an event listener item to this container.
+ this.push([itemView.container], {
+ staged: aOptions.staged, /* stage the item to be appended later? */
+ attachment: {
+ url: url,
+ type: type,
+ view: itemView,
+ selectors: [selector],
+ group: group,
+ checkboxState: checkboxState,
+ checkboxTooltip: this._eventCheckboxTooltip
+ }
+ });
+ },
+
+ /**
+ * Gets all the event types known to this container.
+ *
+ * @return array
+ * List of event types, for example ["load", "click"...]
+ */
+ getAllEvents: function () {
+ return this.attachments.map(e => e.type);
+ },
+
+ /**
+ * Gets the checked event types in this container.
+ *
+ * @return array
+ * List of event types, for example ["load", "click"...]
+ */
+ getCheckedEvents: function () {
+ return this.attachments.filter(e => e.checkboxState).map(e => e.type);
+ },
+
+ /**
+ * Customization function for creating an item's UI.
+ *
+ * @param string aType
+ * The event type, for example "click".
+ * @param string aSelector
+ * The target element's selector.
+ * @param string url
+ * The source url in which the event listener is located.
+ * @return object
+ * An object containing the event listener view nodes.
+ */
+ _createItemView: function (aType, aSelector, aUrl) {
+ let container = document.createElement("hbox");
+ container.className = "dbg-event-listener";
+
+ let eventType = document.createElement("label");
+ eventType.className = "plain dbg-event-listener-type";
+ eventType.setAttribute("value", aType);
+ container.appendChild(eventType);
+
+ let typeSeparator = document.createElement("label");
+ typeSeparator.className = "plain dbg-event-listener-separator";
+ typeSeparator.setAttribute("value", this._onSelectorString);
+ container.appendChild(typeSeparator);
+
+ let eventTargets = document.createElement("label");
+ eventTargets.className = "plain dbg-event-listener-targets";
+ eventTargets.setAttribute("value", aSelector);
+ container.appendChild(eventTargets);
+
+ let selectorSeparator = document.createElement("label");
+ selectorSeparator.className = "plain dbg-event-listener-separator";
+ selectorSeparator.setAttribute("value", this._inSourceString);
+ container.appendChild(selectorSeparator);
+
+ let eventLocation = document.createElement("label");
+ eventLocation.className = "plain dbg-event-listener-location";
+ eventLocation.setAttribute("value", SourceUtils.getSourceLabel(aUrl));
+ eventLocation.setAttribute("flex", "1");
+ eventLocation.setAttribute("crop", "center");
+ container.appendChild(eventLocation);
+
+ return {
+ container: container,
+ type: eventType,
+ targets: eventTargets,
+ location: eventLocation
+ };
+ },
+
+ /**
+ * The check listener for the event listeners container.
+ */
+ _onCheck: function ({ detail: { description, checked }, target }) {
+ if (description == "item") {
+ this.getItemForElement(target).attachment.checkboxState = checked;
+
+ this.actions.updateEventBreakpoints(this.getCheckedEvents());
+ return;
+ }
+
+ // Check all the event items in this group.
+ this.items
+ .filter(e => e.attachment.group == description)
+ .forEach(e => this.callMethod("checkItem", e.target, checked));
+ },
+
+ /**
+ * The select listener for the event listeners container.
+ */
+ _onClick: function ({ target }) {
+ // Changing the checkbox state is handled by the _onCheck event. Avoid
+ // handling that again in this click event, so pass in "noSiblings"
+ // when retrieving the target's item, to ignore the checkbox.
+ let eventItem = this.getItemForElement(target, { noSiblings: true });
+ if (eventItem) {
+ let newState = eventItem.attachment.checkboxState ^= 1;
+ this.callMethod("checkItem", eventItem.target, newState);
+ }
+ },
+
+ _eventCheckboxTooltip: "",
+ _onSelectorString: "",
+ _inSourceString: "",
+ _inNativeCodeString: ""
+});
+
+module.exports = EventListenersView;