summaryrefslogtreecommitdiffstats
path: root/devtools/client/dom/content
diff options
context:
space:
mode:
Diffstat (limited to 'devtools/client/dom/content')
-rw-r--r--devtools/client/dom/content/actions/filter.js21
-rw-r--r--devtools/client/dom/content/actions/grips.js54
-rw-r--r--devtools/client/dom/content/actions/moz.build9
-rw-r--r--devtools/client/dom/content/components/dom-tree.js91
-rw-r--r--devtools/client/dom/content/components/main-frame.js63
-rw-r--r--devtools/client/dom/content/components/main-toolbar.js66
-rw-r--r--devtools/client/dom/content/components/moz.build10
-rw-r--r--devtools/client/dom/content/constants.js9
-rw-r--r--devtools/client/dom/content/dom-decorator.js50
-rw-r--r--devtools/client/dom/content/dom-view.css118
-rw-r--r--devtools/client/dom/content/dom-view.js65
-rw-r--r--devtools/client/dom/content/grip-provider.js97
-rw-r--r--devtools/client/dom/content/moz.build19
-rw-r--r--devtools/client/dom/content/reducers/filter.js29
-rw-r--r--devtools/client/dom/content/reducers/grips.js123
-rw-r--r--devtools/client/dom/content/reducers/index.js14
-rw-r--r--devtools/client/dom/content/reducers/moz.build10
-rw-r--r--devtools/client/dom/content/utils.js27
18 files changed, 875 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 000000000..3fac9d278
--- /dev/null
+++ b/devtools/client/dom/content/actions/filter.js
@@ -0,0 +1,21 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* 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("../constants");
+
+/**
+ * Used to filter DOM panel content.
+ */
+function setVisibilityFilter(filter) {
+ return {
+ filter: 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 000000000..23d4fc895
--- /dev/null
+++ b/devtools/client/dom/content/actions/grips.js
@@ -0,0 +1,54 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* 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("../constants");
+
+/**
+ * Used to fetch grip prototype and properties from the backend.
+ */
+function requestProperties(grip) {
+ return {
+ grip: 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: grip,
+ type: constants.FETCH_PROPERTIES,
+ status: "end",
+ response: response,
+ error: error
+ };
+}
+
+/**
+ * Used to get properties from the backend and fire an action
+ * when they are received.
+ */
+function fetchProperties(grip) {
+ return dispatch => {
+ // dispatch(requestProperties(grip));
+
+ // Use 'DomProvider' object exposed from the chrome scope.
+ return DomProvider.getPrototypeAndProperties(grip).then(response => {
+ dispatch(receiveProperties(grip, response));
+ });
+ };
+}
+
+// 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 000000000..6454c00cc
--- /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/dom-tree.js b/devtools/client/dom/content/components/dom-tree.js
new file mode 100644
index 000000000..ef529ac3f
--- /dev/null
+++ b/devtools/client/dom/content/components/dom-tree.js
@@ -0,0 +1,91 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* 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("devtools/client/shared/vendor/react");
+const { connect } = require("devtools/client/shared/vendor/react-redux");
+
+// Reps
+const { createFactories } = require("devtools/client/shared/components/reps/rep-utils");
+const TreeView = React.createFactory(require("devtools/client/shared/components/tree/tree-view"));
+const { Rep } = createFactories(require("devtools/client/shared/components/reps/rep"));
+const { Grip } = require("devtools/client/shared/components/reps/grip");
+
+// DOM Panel
+const { GripProvider } = require("../grip-provider");
+const { DomDecorator } = require("../dom-decorator");
+
+// Shortcuts
+const PropTypes = React.PropTypes;
+
+/**
+ * Renders DOM panel tree.
+ */
+var DomTree = React.createClass({
+ displayName: "DomTree",
+
+ propTypes: {
+ object: PropTypes.any,
+ filter: PropTypes.string,
+ dispatch: PropTypes.func.isRequired,
+ grips: PropTypes.object,
+ },
+
+ /**
+ * Filter DOM properties. Return true if the object
+ * should be visible in the tree.
+ */
+ onFilter: function (object) {
+ if (!this.props.filter) {
+ return true;
+ }
+
+ return (object.name && object.name.indexOf(this.props.filter) > -1);
+ },
+
+ /**
+ * Render DOM panel content
+ */
+ render: function () {
+ let columns = [{
+ "id": "value"
+ }];
+
+ // 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.
+ let renderValue = props => {
+ return Rep(Object.assign({}, props, {
+ defaultRep: Grip,
+ cropLimit: 50,
+ }));
+ };
+
+ return (
+ TreeView({
+ object: this.props.object,
+ provider: new GripProvider(this.props.grips, this.props.dispatch),
+ decorator: new DomDecorator(),
+ mode: "short",
+ columns: columns,
+ renderValue: renderValue,
+ onFilter: this.onFilter
+ })
+ );
+ }
+});
+
+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/main-frame.js b/devtools/client/dom/content/components/main-frame.js
new file mode 100644
index 000000000..d786314e2
--- /dev/null
+++ b/devtools/client/dom/content/components/main-frame.js
@@ -0,0 +1,63 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* 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("devtools/client/shared/vendor/react");
+const { connect } = require("devtools/client/shared/vendor/react-redux");
+
+// DOM Panel
+const DomTree = React.createFactory(require("./dom-tree"));
+const MainToolbar = React.createFactory(require("./main-toolbar"));
+
+// Shortcuts
+const { div } = React.DOM;
+const PropTypes = React.PropTypes;
+
+/**
+ * Renders basic layout of the DOM panel. The DOM panel cotent consists
+ * from two main parts: toolbar and tree.
+ */
+var MainFrame = React.createClass({
+ displayName: "MainFrame",
+
+ propTypes: {
+ object: PropTypes.any,
+ filter: PropTypes.string,
+ dispatch: PropTypes.func.isRequired,
+ },
+
+ /**
+ * Render DOM panel content
+ */
+ render: function () {
+ return (
+ div({className: "mainFrame"},
+ MainToolbar({
+ dispatch: this.props.dispatch,
+ object: this.props.object
+ }),
+ div({className: "treeTableBox"},
+ DomTree({
+ object: this.props.object,
+ filter: this.props.filter,
+ })
+ )
+ )
+ );
+ }
+});
+
+// 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/main-toolbar.js b/devtools/client/dom/content/components/main-toolbar.js
new file mode 100644
index 000000000..c44a6b4ca
--- /dev/null
+++ b/devtools/client/dom/content/components/main-toolbar.js
@@ -0,0 +1,66 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* 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 React = require("devtools/client/shared/vendor/react");
+const { l10n } = require("../utils");
+
+// Reps
+const { createFactories } = require("devtools/client/shared/components/reps/rep-utils");
+const { Toolbar, ToolbarButton } = createFactories(require("devtools/client/jsonview/components/reps/toolbar"));
+
+// DOM Panel
+const SearchBox = React.createFactory(require("devtools/client/shared/components/search-box"));
+
+// Actions
+const { fetchProperties } = require("../actions/grips");
+const { setVisibilityFilter } = require("../actions/filter");
+
+// Shortcuts
+const PropTypes = React.PropTypes;
+
+/**
+ * This template is responsible for rendering a toolbar
+ * within the 'Headers' panel.
+ */
+var MainToolbar = React.createClass({
+ displayName: "MainToolbar",
+
+ propTypes: {
+ object: PropTypes.any.isRequired,
+ dispatch: PropTypes.func.isRequired,
+ },
+
+ onRefresh: function () {
+ this.props.dispatch(fetchProperties(this.props.object));
+ },
+
+ onSearch: function (value) {
+ this.props.dispatch(setVisibilityFilter(value));
+ },
+
+ render: function () {
+ return (
+ Toolbar({},
+ ToolbarButton({
+ className: "btn refresh",
+ onClick: this.onRefresh},
+ l10n.getStr("dom.refresh")
+ ),
+ SearchBox({
+ delay: 250,
+ onChange: this.onSearch,
+ placeholder: l10n.getStr("dom.filterDOMPanel"),
+ type: "filter"
+ })
+ )
+ );
+ }
+});
+
+// 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 000000000..0fa1f8089
--- /dev/null
+++ b/devtools/client/dom/content/components/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(
+ 'dom-tree.js',
+ 'main-frame.js',
+ 'main-toolbar.js'
+)
diff --git a/devtools/client/dom/content/constants.js b/devtools/client/dom/content/constants.js
new file mode 100644
index 000000000..f06ca8512
--- /dev/null
+++ b/devtools/client/dom/content/constants.js
@@ -0,0 +1,9 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* 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 000000000..4042df8d3
--- /dev/null
+++ b/devtools/client/dom/content/dom-decorator.js
@@ -0,0 +1,50 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* 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("./reducers/grips");
+
+// Implementation
+
+function DomDecorator() {
+}
+
+/**
+ * Decorator for DOM panel tree component. It's responsible for
+ * appending an icon to read only properties.
+ */
+DomDecorator.prototype = {
+ getRowClass: function (object) {
+ if (object instanceof Property) {
+ let value = object.value;
+ let 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: function (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 000000000..631f8c536
--- /dev/null
+++ b/devtools/client/dom/content/dom-view.css
@@ -0,0 +1,118 @@
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* 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;
+}
+
+.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/firebug/read-only.svg") no-repeat;
+ background-position: 1px 5px;
+ background-size: 10px 10px;
+}
+
+.treeTable tr:not(.writable) td.treeValueCell:dir(rtl) {
+ background-position-x: right 1px;
+}
+
+/* Non-enumerable properties are grayed out */
+.treeTable tr:not(.enumerable) td.treeValueCell {
+ opacity: 0.7;
+}
+
+.treeTable > tbody > tr > td {
+ border-bottom: 1px solid #EFEFEF;
+}
+
+/* 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;
+}
+
+/******************************************************************************/
+/* Search box */
+.devtools-searchbox {
+ float: right;
+}
+
+.devtools-searchbox:dir(rtl) {
+ float: left;
+}
+
+/******************************************************************************/
+/* Theme Dark */
+
+.theme-dark .treeTable > tbody > tr > td {
+ border-bottom: none;
+}
+
+.theme-dark body {
+ background-color: var(--theme-body-background);
+}
diff --git a/devtools/client/dom/content/dom-view.js b/devtools/client/dom/content/dom-view.js
new file mode 100644
index 000000000..b0ea11dee
--- /dev/null
+++ b/devtools/client/dom/content/dom-view.js
@@ -0,0 +1,65 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* 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("devtools/client/shared/vendor/react");
+const ReactDOM = require("devtools/client/shared/vendor/react-dom");
+const { Provider } = require("devtools/client/shared/vendor/react-redux");
+const { combineReducers } = require("devtools/client/shared/vendor/redux");
+
+// DOM Panel
+const MainFrame = React.createFactory(require("./components/main-frame"));
+
+// Store
+const createStore = require("devtools/client/shared/redux/create-store")({
+ log: false
+});
+
+const { reducers } = require("./reducers/index");
+const store = createStore(combineReducers(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: function (rootGrip) {
+ let content = document.querySelector("#content");
+ let mainFrame = MainFrame({
+ object: rootGrip,
+ });
+
+ // Render top level component
+ let provider = React.createElement(Provider, {
+ store: this.store
+ }, mainFrame);
+
+ this.mainFrame = ReactDOM.render(provider, content);
+ },
+
+ onMessage: function (event) {
+ let data = event.data;
+ let 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 throught: |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 000000000..bcda1ff18
--- /dev/null
+++ b/devtools/client/dom/content/grip-provider.js
@@ -0,0 +1,97 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* 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("./actions/grips");
+const { Property } = require("./reducers/grips");
+
+// 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: function (object) {
+ let grip = object;
+ if (object instanceof Property) {
+ grip = this.getValue(object);
+ }
+
+ if (!grip || !grip.actor) {
+ return [];
+ }
+
+ let props = this.grips.get(grip.actor);
+ 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: function (object) {
+ if (object instanceof Property) {
+ let value = this.getValue(object);
+ if (!value) {
+ return false;
+ }
+
+ let hasChildren = value.ownPropertyLength > 0;
+
+ if (value.preview) {
+ hasChildren = hasChildren || value.preview.ownPropertiesLength > 0;
+ }
+
+ if (value.preview) {
+ let preview = value.preview;
+ let k = preview.kind;
+ let objectsWithProps = ["DOMNode", "ObjectWithURL"];
+ hasChildren = hasChildren || (objectsWithProps.indexOf(k) != -1);
+ hasChildren = hasChildren || (k == "ArrayLike" && preview.length > 0);
+ }
+
+ return (value.type == "object" && hasChildren);
+ }
+
+ return null;
+ },
+
+ getValue: function (object) {
+ if (object instanceof Property) {
+ let value = object.value;
+ return (typeof value.value != "undefined") ? value.value :
+ value.getterValue;
+ }
+
+ return object;
+ },
+
+ getLabel: function (object) {
+ return (object instanceof Property) ? object.name : null;
+ },
+
+ getKey: function (object) {
+ return (object instanceof Property) ? object.key : null;
+ },
+
+ getType: function (object) {
+ return object.class ? object.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 000000000..b4a9c76bf
--- /dev/null
+++ b/devtools/client/dom/content/moz.build
@@ -0,0 +1,19 @@
+# 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.css',
+ '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 000000000..3eb5bd3fc
--- /dev/null
+++ b/devtools/client/dom/content/reducers/filter.js
@@ -0,0 +1,29 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* 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("../constants");
+
+/**
+ * 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 000000000..c7d589434
--- /dev/null
+++ b/devtools/client/dom/content/reducers/grips.js
@@ -0,0 +1,123 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* 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("../constants");
+
+/**
+ * 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 "end":
+ return onReceiveProperties(state, action);
+ }
+
+ return state;
+}
+
+/**
+ * Handle requestProperties action
+ */
+function onRequestProperties(state, action) {
+ return state;
+}
+
+/**
+ * Handle receiveProperties action
+ */
+function onReceiveProperties(cache, action) {
+ let response = action.response;
+ let from = response.from;
+ let className = action.grip.class;
+
+ // Properly deal with getters.
+ mergeProperties(response);
+
+ // Compute list of requested children.
+ let previewProps = response.preview ? response.preview.ownProperties : null;
+ let ownProps = response.ownProperties || previewProps || [];
+
+ let 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.
+ let newCache = new Map(cache);
+ newCache.set(from, props);
+
+ return newCache;
+}
+
+// Helpers
+
+function mergeProperties(response) {
+ let { ownProperties } = response;
+
+ // 'safeGetterValues' is new and isn't necessary defined on old grips.
+ let safeGetterValues = response.safeGetterValues || {};
+
+ // Merge the safe getter values into one object such that we can use it
+ // in variablesView.
+ for (let name of Object.keys(safeGetterValues)) {
+ if (name in ownProperties) {
+ let { 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 000000000..1900487e1
--- /dev/null
+++ b/devtools/client/dom/content/reducers/index.js
@@ -0,0 +1,14 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* 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("./grips");
+const { filter } = require("./filter");
+
+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 000000000..0a00b3feb
--- /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 000000000..645ba7921
--- /dev/null
+++ b/devtools/client/dom/content/utils.js
@@ -0,0 +1,27 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* 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: function (key) {
+ let 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;