diff options
Diffstat (limited to 'devtools/client/dom/content/reducers/grips.js')
-rw-r--r-- | devtools/client/dom/content/reducers/grips.js | 123 |
1 files changed, 123 insertions, 0 deletions
diff --git a/devtools/client/dom/content/reducers/grips.js b/devtools/client/dom/content/reducers/grips.js new file mode 100644 index 0000000000..1413baa1ce --- /dev/null +++ b/devtools/client/dom/content/reducers/grips.js @@ -0,0 +1,123 @@ +/* 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"; + +const constants = require("resource://devtools/client/dom/content/constants.js"); + +/** + * Initial state definition + */ +function getInitialState() { + return new Map(); +} + +/** + * Maintain a cache of received grip responses from the backend. + */ +function grips(state = getInitialState(), action) { + // This reducer supports only one action, fetching actor properties + // from the backend so, bail out if we are dealing with any other + // action. + if (action.type != constants.FETCH_PROPERTIES) { + return state; + } + + switch (action.status) { + case "start": + return onRequestProperties(state, action); + case "done": + return onReceiveProperties(state, action); + } + + return state; +} + +/** + * Handle requestProperties action + */ +function onRequestProperties(state, action) { + return state; +} + +/** + * Handle receiveProperties action + */ +function onReceiveProperties(cache, action) { + const response = action.response; + const from = response.from; + const className = action.grip?.getGrip + ? action.grip.getGrip().class + : action.grip.class; + + // Properly deal with getters. + mergeProperties(response); + + // Compute list of requested children. + const previewProps = response.preview ? response.preview.ownProperties : null; + const ownProps = response.ownProperties || previewProps || []; + + const props = Object.keys(ownProps).map(key => { + // Array indexes as a special case. We convert any keys that are string + // representations of integers to integers. + if (className === "Array" && isInteger(key)) { + key = parseInt(key, 10); + } + return new Property(key, ownProps[key], key); + }); + + props.sort(sortName); + + // Return new state/map. + const newCache = new Map(cache); + newCache.set(from, props); + + return newCache; +} + +// Helpers + +function mergeProperties(response) { + const { ownProperties } = response; + + // 'safeGetterValues' is new and isn't necessary defined on old grips. + const safeGetterValues = response.safeGetterValues || {}; + + // Merge the safe getter values into one object such that we can use it + // in variablesView. + for (const name of Object.keys(safeGetterValues)) { + if (name in ownProperties) { + const { getterValue, getterPrototypeLevel } = safeGetterValues[name]; + ownProperties[name].getterValue = getterValue; + ownProperties[name].getterPrototypeLevel = getterPrototypeLevel; + } else { + ownProperties[name] = safeGetterValues[name]; + } + } +} + +function sortName(a, b) { + // Display non-enumerable properties at the end. + if (!a.value.enumerable && b.value.enumerable) { + return 1; + } + if (a.value.enumerable && !b.value.enumerable) { + return -1; + } + return a.name > b.name ? 1 : -1; +} + +function isInteger(n) { + // We use parseInt(n, 10) == n to disregard scientific notation e.g. "3e24" + return isFinite(n) && parseInt(n, 10) == n; +} + +function Property(name, value, key) { + this.name = name; + this.value = value; + this.key = key; +} + +// Exports from this module +exports.grips = grips; +exports.Property = Property; |