diff options
Diffstat (limited to 'devtools/client/dom/content')
18 files changed, 935 insertions, 0 deletions
diff --git a/devtools/client/dom/content/actions/filter.js b/devtools/client/dom/content/actions/filter.js new file mode 100644 index 0000000000..dbd6ca7ac4 --- /dev/null +++ b/devtools/client/dom/content/actions/filter.js @@ -0,0 +1,19 @@ +/* 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"); + +/** + * Used to filter DOM panel content. + */ +function setVisibilityFilter(filter) { + return { + filter, + type: constants.SET_VISIBILITY_FILTER, + }; +} + +// Exports from this module +exports.setVisibilityFilter = setVisibilityFilter; diff --git a/devtools/client/dom/content/actions/grips.js b/devtools/client/dom/content/actions/grips.js new file mode 100644 index 0000000000..3f25d46d3e --- /dev/null +++ b/devtools/client/dom/content/actions/grips.js @@ -0,0 +1,54 @@ +/* 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/. */ +/* globals DomProvider */ +"use strict"; + +const constants = require("resource://devtools/client/dom/content/constants.js"); + +/** + * Used to fetch grip prototype and properties from the backend. + */ +function requestProperties(grip) { + return { + grip, + type: constants.FETCH_PROPERTIES, + status: "start", + error: false, + }; +} + +/** + * Executed when grip properties are received from the backend. + */ +function receiveProperties(grip, response, error) { + return { + grip, + type: constants.FETCH_PROPERTIES, + status: "done", + response, + error, + }; +} + +/** + * Used to get properties from the backend and fire an action + * when they are received. + */ +function fetchProperties(grip) { + return async ({ dispatch }) => { + try { + // Use 'DomProvider' object exposed from the chrome scope. + const response = await DomProvider.getPrototypeAndProperties(grip); + dispatch(receiveProperties(grip, response)); + } catch (e) { + console.error("Error while fetching properties", e); + } + DomProvider.onPropertiesFetched(); + }; +} + +// Exports from this module +exports.requestProperties = requestProperties; +exports.receiveProperties = receiveProperties; +exports.fetchProperties = fetchProperties; diff --git a/devtools/client/dom/content/actions/moz.build b/devtools/client/dom/content/actions/moz.build new file mode 100644 index 0000000000..8c1c56d39a --- /dev/null +++ b/devtools/client/dom/content/actions/moz.build @@ -0,0 +1,9 @@ +# vim: set filetype=python: +# 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/. + +DevToolsModules( + "filter.js", + "grips.js", +) diff --git a/devtools/client/dom/content/components/DomTree.js b/devtools/client/dom/content/components/DomTree.js new file mode 100644 index 0000000000..b82a2785dd --- /dev/null +++ b/devtools/client/dom/content/components/DomTree.js @@ -0,0 +1,141 @@ +/* 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/. */ + +/* global DomProvider */ + +"use strict"; + +// React & Redux +const { + Component, + createFactory, +} = require("resource://devtools/client/shared/vendor/react.js"); +const PropTypes = require("resource://devtools/client/shared/vendor/react-prop-types.js"); + +const { + connect, +} = require("resource://devtools/client/shared/vendor/react-redux.js"); + +const TreeView = createFactory( + require("resource://devtools/client/shared/components/tree/TreeView.js") +); +// Reps +const { + REPS, + MODE, +} = require("resource://devtools/client/shared/components/reps/index.js"); +const { Rep } = REPS; + +const Grip = REPS.Grip; +// DOM Panel +const { + GripProvider, +} = require("resource://devtools/client/dom/content/grip-provider.js"); + +const { + DomDecorator, +} = require("resource://devtools/client/dom/content/dom-decorator.js"); + +/** + * Renders DOM panel tree. + */ +class DomTree extends Component { + static get propTypes() { + return { + dispatch: PropTypes.func.isRequired, + filter: PropTypes.string, + grips: PropTypes.object, + object: PropTypes.any, + openLink: PropTypes.func, + }; + } + + constructor(props) { + super(props); + this.onFilter = this.onFilter.bind(this); + } + + /** + * Filter DOM properties. Return true if the object + * should be visible in the tree. + */ + onFilter(object) { + if (!this.props.filter) { + return true; + } + + return object.name && object.name.indexOf(this.props.filter) > -1; + } + + /** + * Render DOM panel content + */ + render() { + const { dispatch, grips, object, openLink } = this.props; + + const columns = [ + { + id: "value", + }, + ]; + + let onDOMNodeMouseOver; + let onDOMNodeMouseOut; + let onInspectIconClick; + const toolbox = DomProvider.getToolbox(); + if (toolbox) { + const highlighter = toolbox.getHighlighter(); + onDOMNodeMouseOver = async (grip, options = {}) => { + return highlighter.highlight(grip, options); + }; + onDOMNodeMouseOut = async () => { + return highlighter.unhighlight(); + }; + onInspectIconClick = async grip => { + return toolbox.viewElementInInspector(grip, "inspect_dom"); + }; + } + + // This is the integration point with Reps. The DomTree is using + // Reps to render all values. The code also specifies default rep + // used for data types that don't have its own specific template. + const renderValue = props => { + const repProps = Object.assign({}, props, { + onDOMNodeMouseOver, + onDOMNodeMouseOut, + onInspectIconClick, + defaultRep: Grip, + cropLimit: 50, + }); + + // Object can be an objectFront, while Rep always expect grips. + if (props?.object?.getGrip) { + repProps.object = props.object.getGrip(); + } + + return Rep(repProps); + }; + + return TreeView({ + columns, + decorator: new DomDecorator(), + mode: MODE.SHORT, + object, + onFilter: this.onFilter, + openLink, + provider: new GripProvider(grips, dispatch), + renderValue, + }); + } +} + +const mapStateToProps = state => { + return { + grips: state.grips, + filter: state.filter, + }; +}; + +// Exports from this module +module.exports = connect(mapStateToProps)(DomTree); diff --git a/devtools/client/dom/content/components/MainFrame.js b/devtools/client/dom/content/components/MainFrame.js new file mode 100644 index 0000000000..fedbb09bb8 --- /dev/null +++ b/devtools/client/dom/content/components/MainFrame.js @@ -0,0 +1,76 @@ +/* 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/. */ +/* globals DomProvider */ + +"use strict"; + +// React & Redux +const { + Component, + createFactory, +} = require("resource://devtools/client/shared/vendor/react.js"); +const dom = require("resource://devtools/client/shared/vendor/react-dom-factories.js"); +const PropTypes = require("resource://devtools/client/shared/vendor/react-prop-types.js"); + +const { + connect, +} = require("resource://devtools/client/shared/vendor/react-redux.js"); +// DOM Panel +const DomTree = createFactory( + require("resource://devtools/client/dom/content/components/DomTree.js") +); + +const MainToolbar = createFactory( + require("resource://devtools/client/dom/content/components/MainToolbar.js") +); +// Shortcuts +const { div } = dom; + +/** + * Renders basic layout of the DOM panel. The DOM panel content consists + * from two main parts: toolbar and tree. + */ +class MainFrame extends Component { + static get propTypes() { + return { + dispatch: PropTypes.func.isRequired, + filter: PropTypes.string, + object: PropTypes.any, + }; + } + + /** + * Render DOM panel content + */ + render() { + const { filter, object } = this.props; + + return div( + { className: "mainFrame" }, + MainToolbar({ + dispatch: this.props.dispatch, + object: this.props.object, + }), + div( + { className: "treeTableBox devtools-monospace" }, + DomTree({ + filter, + object, + openLink: url => DomProvider.openLink(url), + }) + ) + ); + } +} + +// Transform state into props +// Note: use https://github.com/faassen/reselect for better performance. +const mapStateToProps = state => { + return { + filter: state.filter, + }; +}; + +// Exports from this module +module.exports = connect(mapStateToProps)(MainFrame); diff --git a/devtools/client/dom/content/components/MainToolbar.js b/devtools/client/dom/content/components/MainToolbar.js new file mode 100644 index 0000000000..f3f018741a --- /dev/null +++ b/devtools/client/dom/content/components/MainToolbar.js @@ -0,0 +1,76 @@ +/* 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"; + +// React +const { + Component, + createFactory, +} = require("resource://devtools/client/shared/vendor/react.js"); +const dom = require("resource://devtools/client/shared/vendor/react-dom-factories.js"); +const PropTypes = require("resource://devtools/client/shared/vendor/react-prop-types.js"); +const SearchBox = createFactory( + require("resource://devtools/client/shared/components/SearchBox.js") +); + +const { l10n } = require("resource://devtools/client/dom/content/utils.js"); + +// Actions +const { + fetchProperties, +} = require("resource://devtools/client/dom/content/actions/grips.js"); +const { + setVisibilityFilter, +} = require("resource://devtools/client/dom/content/actions/filter.js"); + +/** + * This template is responsible for rendering a toolbar + * within the 'Headers' panel. + */ +class MainToolbar extends Component { + static get propTypes() { + return { + object: PropTypes.any.isRequired, + dispatch: PropTypes.func.isRequired, + }; + } + + constructor(props) { + super(props); + this.onRefresh = this.onRefresh.bind(this); + this.onSearch = this.onSearch.bind(this); + } + + onRefresh() { + this.props.dispatch(fetchProperties(this.props.object)); + } + + onSearch(value) { + this.props.dispatch(setVisibilityFilter(value)); + } + + render() { + return dom.div( + { className: "devtools-toolbar devtools-input-toolbar" }, + SearchBox({ + key: "filter", + delay: 250, + onChange: this.onSearch, + placeholder: l10n.getStr("dom.filterDOMPanel"), + type: "filter", + }), + dom.span({ className: "devtools-separator" }), + dom.button({ + key: "refresh", + className: "refresh devtools-button", + id: "dom-refresh-button", + title: l10n.getStr("dom.refresh"), + onClick: this.onRefresh, + }) + ); + } +} + +// Exports from this module +module.exports = MainToolbar; diff --git a/devtools/client/dom/content/components/moz.build b/devtools/client/dom/content/components/moz.build new file mode 100644 index 0000000000..5b669bac24 --- /dev/null +++ b/devtools/client/dom/content/components/moz.build @@ -0,0 +1,6 @@ +# vim: set filetype=python: +# 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/. + +DevToolsModules("DomTree.js", "MainFrame.js", "MainToolbar.js") diff --git a/devtools/client/dom/content/constants.js b/devtools/client/dom/content/constants.js new file mode 100644 index 0000000000..c7bb8b325c --- /dev/null +++ b/devtools/client/dom/content/constants.js @@ -0,0 +1,7 @@ +/* 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"; + +exports.FETCH_PROPERTIES = "FETCH_PROPERTIES"; +exports.SET_VISIBILITY_FILTER = "SET_VISIBILITY_FILTER"; diff --git a/devtools/client/dom/content/dom-decorator.js b/devtools/client/dom/content/dom-decorator.js new file mode 100644 index 0000000000..a711a95d83 --- /dev/null +++ b/devtools/client/dom/content/dom-decorator.js @@ -0,0 +1,48 @@ +/* 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 { + Property, +} = require("resource://devtools/client/dom/content/reducers/grips.js"); + +// Implementation + +function DomDecorator() {} + +/** + * Decorator for DOM panel tree component. It's responsible for + * appending an icon to read only properties. + */ +DomDecorator.prototype = { + getRowClass(object) { + if (object instanceof Property) { + const value = object.value; + const names = []; + + if (value.enumerable) { + names.push("enumerable"); + } + if (value.writable) { + names.push("writable"); + } + if (value.configurable) { + names.push("configurable"); + } + + return names; + } + + return null; + }, + + /** + * Return custom React template for specified object. The template + * might depend on specified column. + */ + getValueRep(value, colId) {}, +}; + +// Exports from this module +exports.DomDecorator = DomDecorator; diff --git a/devtools/client/dom/content/dom-view.css b/devtools/client/dom/content/dom-view.css new file mode 100644 index 0000000000..d2fd77e525 --- /dev/null +++ b/devtools/client/dom/content/dom-view.css @@ -0,0 +1,109 @@ +/* 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/. */ + +/******************************************************************************/ +/* General */ + +body { + padding: 0; + margin: 0; + overflow: hidden; + background-color: var(--theme-body-background); +} + +.mainFrame { + display: flex; + flex-direction: column; + height: 100vh; +} + +.mainFrame > .treeTableBox { + flex: 1 1 auto; + overflow: auto; +} + +/******************************************************************************/ +/* TreeView Customization */ + +.treeTable { + width: 100%; +} + +/* Space for read only properties icon */ +.treeTable td.treeValueCell { + padding-inline-start: 16px; +} + +.treeTable .treeLabel, +.treeTable td.treeValueCell .objectBox { + direction: ltr; /* Don't change the direction of english labels */ +} + +/* Read only properties have a padlock icon */ +.treeTable tr:not(.writable) td.treeValueCell { + background: url("chrome://devtools/skin/images/lock.svg") no-repeat; + background-position: 1px 4px; + background-size: 12px 12px; + -moz-context-properties: fill; + fill: var(--theme-icon-dimmed-color); +} + +.treeTable tr:not(.writable) td.treeValueCell:dir(rtl) { + background-position-x: right 1px; +} + +.treeTable tr:not(.writable).selected td.treeValueCell { + fill: var(--theme-selection-color); +} + +/* Non-enumerable properties are grayed out */ +.treeTable tr:not(.enumerable) td.treeValueCell { + opacity: 0.7; +} + +.theme-light .treeTable > tbody > tr > td { + border-bottom: 1px solid var(--grey-20); +} + +/* Label Types */ +.treeTable .userLabel, +.treeTable .userClassLabel, +.treeTable .userFunctionLabel { + font-weight: bold; +} + +.treeTable .userLabel { + color: #000000; +} + +.treeTable .userClassLabel { + color: #E90000; +} + +.treeTable .userFunctionLabel { + color: #025E2A; +} + +.treeTable .domLabel { + color: #000000; +} + +.treeTable .domClassLabel { + color: #E90000; +} + +.treeTable .domFunctionLabel { + color: #025E2A; +} + +.treeTable .ordinalLabel { + color: SlateBlue; + font-weight: bold; +} + +/******************************************************************************/ +/* Refresh button */ +#dom-refresh-button::before { + background-image: url("chrome://devtools/skin/images/reload.svg"); +} diff --git a/devtools/client/dom/content/dom-view.js b/devtools/client/dom/content/dom-view.js new file mode 100644 index 0000000000..6bd2252978 --- /dev/null +++ b/devtools/client/dom/content/dom-view.js @@ -0,0 +1,69 @@ +/* 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"; + +// React & Redux +const React = require("resource://devtools/client/shared/vendor/react.js"); +const ReactDOM = require("resource://devtools/client/shared/vendor/react-dom.js"); +const { + Provider, +} = require("resource://devtools/client/shared/vendor/react-redux.js"); + +// DOM Panel +const MainFrame = React.createFactory( + require("resource://devtools/client/dom/content/components/MainFrame.js") +); + +// Store +const createStore = require("resource://devtools/client/shared/redux/create-store.js"); + +const { + reducers, +} = require("resource://devtools/client/dom/content/reducers/index.js"); +const store = createStore(reducers); + +/** + * This object represents view of the DOM panel and is responsible + * for rendering the content. It renders the top level ReactJS + * component: the MainFrame. + */ +function DomView(localStore) { + addEventListener("devtools/chrome/message", this.onMessage.bind(this), true); + + // Make it local so, tests can access it. + this.store = localStore; +} + +DomView.prototype = { + initialize(rootGrip) { + const content = document.querySelector("#content"); + const mainFrame = MainFrame({ + object: rootGrip, + }); + + // Render top level component + const provider = React.createElement( + Provider, + { + store: this.store, + }, + mainFrame + ); + + this.mainFrame = ReactDOM.render(provider, content); + }, + + onMessage(event) { + const data = event.data; + const method = data.type; + + if (typeof this[method] == "function") { + this[method](data.args); + } + }, +}; + +// Construct DOM panel view object and expose it to tests. +// Tests can access it through: |panel.panelWin.view| +window.view = new DomView(store); diff --git a/devtools/client/dom/content/grip-provider.js b/devtools/client/dom/content/grip-provider.js new file mode 100644 index 0000000000..18b3308869 --- /dev/null +++ b/devtools/client/dom/content/grip-provider.js @@ -0,0 +1,102 @@ +/* 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 { + fetchProperties, +} = require("resource://devtools/client/dom/content/actions/grips.js"); +const { + Property, +} = require("resource://devtools/client/dom/content/reducers/grips.js"); + +// Implementation +function GripProvider(grips, dispatch) { + this.grips = grips; + this.dispatch = dispatch; +} + +/** + * This object provides data for the tree displayed in the tooltip + * content. + */ +GripProvider.prototype = { + /** + * Fetches properties from the backend. These properties might be + * displayed as child objects in e.g. a tree UI widget. + */ + getChildren(object) { + let grip = object; + if (object instanceof Property) { + grip = this.getValue(object); + } + + if (!grip || !grip.actorID) { + return []; + } + + const props = this.grips.get(grip.actorID); + if (!props) { + // Fetch missing data from the backend. Returning a promise + // from data provider causes the tree to show a spinner. + return this.dispatch(fetchProperties(grip)); + } + + return props; + }, + + hasChildren(object) { + if (object instanceof Property) { + const value = this.getValue(object); + if (!value) { + return false; + } + const grip = value?.getGrip ? value.getGrip() : value; + + let hasChildren = grip.ownPropertyLength > 0; + + if (grip.preview) { + hasChildren = hasChildren || grip.preview.ownPropertiesLength > 0; + } + + if (grip.preview) { + const preview = grip.preview; + const k = preview.kind; + const objectsWithProps = ["DOMNode", "ObjectWithURL"]; + hasChildren = hasChildren || objectsWithProps.includes(k); + hasChildren = hasChildren || (k == "ArrayLike" && !!preview.length); + } + + return grip.type == "object" && hasChildren; + } + + return null; + }, + + getValue(object) { + if (object instanceof Property) { + const value = object.value; + return typeof value.value != "undefined" + ? value.value + : value.getterValue; + } + + return object; + }, + + getLabel(object) { + return object instanceof Property ? object.name : null; + }, + + getKey(object) { + return object instanceof Property ? object.key : null; + }, + + getType(object) { + const grip = object?.getGrip ? object.getGrip() : object; + return grip.class ? grip.class : ""; + }, +}; + +// Exports from this module +exports.GripProvider = GripProvider; diff --git a/devtools/client/dom/content/moz.build b/devtools/client/dom/content/moz.build new file mode 100644 index 0000000000..04e0ac4efc --- /dev/null +++ b/devtools/client/dom/content/moz.build @@ -0,0 +1,18 @@ +# vim: set filetype=python: +# 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/. + +DIRS += [ + "actions", + "components", + "reducers", +] + +DevToolsModules( + "constants.js", + "dom-decorator.js", + "dom-view.js", + "grip-provider.js", + "utils.js", +) diff --git a/devtools/client/dom/content/reducers/filter.js b/devtools/client/dom/content/reducers/filter.js new file mode 100644 index 0000000000..48c63eada7 --- /dev/null +++ b/devtools/client/dom/content/reducers/filter.js @@ -0,0 +1,27 @@ +/* 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 ""; +} + +/** + * Filter displayed object properties. + */ +function filter(state = getInitialState(), action) { + if (action.type == constants.SET_VISIBILITY_FILTER) { + return action.filter; + } + + return state; +} + +// Exports from this module +exports.filter = filter; 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; diff --git a/devtools/client/dom/content/reducers/index.js b/devtools/client/dom/content/reducers/index.js new file mode 100644 index 0000000000..28461f84a8 --- /dev/null +++ b/devtools/client/dom/content/reducers/index.js @@ -0,0 +1,16 @@ +/* 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 { + grips, +} = require("resource://devtools/client/dom/content/reducers/grips.js"); +const { + filter, +} = require("resource://devtools/client/dom/content/reducers/filter.js"); + +exports.reducers = { + grips, + filter, +}; diff --git a/devtools/client/dom/content/reducers/moz.build b/devtools/client/dom/content/reducers/moz.build new file mode 100644 index 0000000000..8d98444e34 --- /dev/null +++ b/devtools/client/dom/content/reducers/moz.build @@ -0,0 +1,10 @@ +# vim: set filetype=python: +# 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/. + +DevToolsModules( + "filter.js", + "grips.js", + "index.js", +) diff --git a/devtools/client/dom/content/utils.js b/devtools/client/dom/content/utils.js new file mode 100644 index 0000000000..52c1c9b4ef --- /dev/null +++ b/devtools/client/dom/content/utils.js @@ -0,0 +1,25 @@ +/* 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"; + +/** + * The default localization just returns the last part of the key + * (all after the last dot). + */ +const DefaultL10N = { + getStr(key) { + const index = key.lastIndexOf("."); + return key.substr(index + 1); + }, +}; + +/** + * The 'l10n' object is set by main.js in case the DOM panel content + * runs within a scope with chrome privileges. + * + * Note that DOM panel content can also run within a scope with no chrome + * privileges, e.g. in an iframe with type 'content' or in a browser tab, + * which allows using our own tools for development. + */ +exports.l10n = window.l10n || DefaultL10N; |