summaryrefslogtreecommitdiffstats
path: root/devtools/client/shared/components/reps
diff options
context:
space:
mode:
Diffstat (limited to 'devtools/client/shared/components/reps')
-rw-r--r--devtools/client/shared/components/reps/array.js186
-rw-r--r--devtools/client/shared/components/reps/attribute.js70
-rw-r--r--devtools/client/shared/components/reps/caption.js31
-rw-r--r--devtools/client/shared/components/reps/comment-node.js60
-rw-r--r--devtools/client/shared/components/reps/date-time.js70
-rw-r--r--devtools/client/shared/components/reps/document.js78
-rw-r--r--devtools/client/shared/components/reps/element-node.js114
-rw-r--r--devtools/client/shared/components/reps/event.js81
-rw-r--r--devtools/client/shared/components/reps/function.js73
-rw-r--r--devtools/client/shared/components/reps/grip-array.js198
-rw-r--r--devtools/client/shared/components/reps/grip-map.js193
-rw-r--r--devtools/client/shared/components/reps/grip.js247
-rw-r--r--devtools/client/shared/components/reps/infinity.js41
-rw-r--r--devtools/client/shared/components/reps/long-string.js71
-rw-r--r--devtools/client/shared/components/reps/moz.build40
-rw-r--r--devtools/client/shared/components/reps/nan.js41
-rw-r--r--devtools/client/shared/components/reps/null.js46
-rw-r--r--devtools/client/shared/components/reps/number.js51
-rw-r--r--devtools/client/shared/components/reps/object-with-text.js76
-rw-r--r--devtools/client/shared/components/reps/object-with-url.js76
-rw-r--r--devtools/client/shared/components/reps/object.js171
-rw-r--r--devtools/client/shared/components/reps/promise.js111
-rw-r--r--devtools/client/shared/components/reps/prop-rep.js70
-rw-r--r--devtools/client/shared/components/reps/regexp.js63
-rw-r--r--devtools/client/shared/components/reps/rep-utils.js160
-rw-r--r--devtools/client/shared/components/reps/rep.js144
-rw-r--r--devtools/client/shared/components/reps/reps.css174
-rw-r--r--devtools/client/shared/components/reps/string.js69
-rw-r--r--devtools/client/shared/components/reps/stylesheet.js77
-rw-r--r--devtools/client/shared/components/reps/symbol.js48
-rw-r--r--devtools/client/shared/components/reps/text-node.js94
-rw-r--r--devtools/client/shared/components/reps/undefined.js46
-rw-r--r--devtools/client/shared/components/reps/window.js73
33 files changed, 3143 insertions, 0 deletions
diff --git a/devtools/client/shared/components/reps/array.js b/devtools/client/shared/components/reps/array.js
new file mode 100644
index 000000000..8ec1443e1
--- /dev/null
+++ b/devtools/client/shared/components/reps/array.js
@@ -0,0 +1,186 @@
+/* -*- 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";
+
+// Make this available to both AMD and CJS environments
+define(function (require, exports, module) {
+ // Dependencies
+ const React = require("devtools/client/shared/vendor/react");
+ const { createFactories } = require("./rep-utils");
+ const { Caption } = createFactories(require("./caption"));
+
+ // Shortcuts
+ const DOM = React.DOM;
+
+ /**
+ * Renders an array. The array is enclosed by left and right bracket
+ * and the max number of rendered items depends on the current mode.
+ */
+ let ArrayRep = React.createClass({
+ displayName: "ArrayRep",
+
+ getTitle: function (object, context) {
+ return "[" + object.length + "]";
+ },
+
+ arrayIterator: function (array, max) {
+ let items = [];
+ let delim;
+
+ for (let i = 0; i < array.length && i < max; i++) {
+ try {
+ let value = array[i];
+
+ delim = (i == array.length - 1 ? "" : ", ");
+
+ items.push(ItemRep({
+ object: value,
+ // Hardcode tiny mode to avoid recursive handling.
+ mode: "tiny",
+ delim: delim
+ }));
+ } catch (exc) {
+ items.push(ItemRep({
+ object: exc,
+ mode: "tiny",
+ delim: delim
+ }));
+ }
+ }
+
+ if (array.length > max) {
+ let objectLink = this.props.objectLink || DOM.span;
+ items.push(Caption({
+ object: objectLink({
+ object: this.props.object
+ }, (array.length - max) + " more…")
+ }));
+ }
+
+ return items;
+ },
+
+ /**
+ * Returns true if the passed object is an array with additional (custom)
+ * properties, otherwise returns false. Custom properties should be
+ * displayed in extra expandable section.
+ *
+ * Example array with a custom property.
+ * let arr = [0, 1];
+ * arr.myProp = "Hello";
+ *
+ * @param {Array} array The array object.
+ */
+ hasSpecialProperties: function (array) {
+ function isInteger(x) {
+ let y = parseInt(x, 10);
+ if (isNaN(y)) {
+ return false;
+ }
+ return x === y.toString();
+ }
+
+ let props = Object.getOwnPropertyNames(array);
+ for (let i = 0; i < props.length; i++) {
+ let p = props[i];
+
+ // Valid indexes are skipped
+ if (isInteger(p)) {
+ continue;
+ }
+
+ // Ignore standard 'length' property, anything else is custom.
+ if (p != "length") {
+ return true;
+ }
+ }
+
+ return false;
+ },
+
+ // Event Handlers
+
+ onToggleProperties: function (event) {
+ },
+
+ onClickBracket: function (event) {
+ },
+
+ render: function () {
+ let mode = this.props.mode || "short";
+ let object = this.props.object;
+ let items;
+ let brackets;
+ let needSpace = function (space) {
+ return space ? { left: "[ ", right: " ]"} : { left: "[", right: "]"};
+ };
+
+ if (mode == "tiny") {
+ let isEmpty = object.length === 0;
+ items = [DOM.span({className: "length"}, isEmpty ? "" : object.length)];
+ brackets = needSpace(false);
+ } else {
+ let max = (mode == "short") ? 3 : 300;
+ items = this.arrayIterator(object, max);
+ brackets = needSpace(items.length > 0);
+ }
+
+ let objectLink = this.props.objectLink || DOM.span;
+
+ return (
+ DOM.span({
+ className: "objectBox objectBox-array"},
+ objectLink({
+ className: "arrayLeftBracket",
+ object: object
+ }, brackets.left),
+ ...items,
+ objectLink({
+ className: "arrayRightBracket",
+ object: object
+ }, brackets.right),
+ DOM.span({
+ className: "arrayProperties",
+ role: "group"}
+ )
+ )
+ );
+ },
+ });
+
+ /**
+ * Renders array item. Individual values are separated by a comma.
+ */
+ let ItemRep = React.createFactory(React.createClass({
+ displayName: "ItemRep",
+
+ render: function () {
+ const { Rep } = createFactories(require("./rep"));
+
+ let object = this.props.object;
+ let delim = this.props.delim;
+ let mode = this.props.mode;
+ return (
+ DOM.span({},
+ Rep({object: object, mode: mode}),
+ delim
+ )
+ );
+ }
+ }));
+
+ function supportsObject(object, type) {
+ return Array.isArray(object) ||
+ Object.prototype.toString.call(object) === "[object Arguments]";
+ }
+
+ // Exports from this module
+ exports.ArrayRep = {
+ rep: ArrayRep,
+ supportsObject: supportsObject
+ };
+});
diff --git a/devtools/client/shared/components/reps/attribute.js b/devtools/client/shared/components/reps/attribute.js
new file mode 100644
index 000000000..f57ed0380
--- /dev/null
+++ b/devtools/client/shared/components/reps/attribute.js
@@ -0,0 +1,70 @@
+/* -*- 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";
+
+// Make this available to both AMD and CJS environments
+define(function (require, exports, module) {
+ // ReactJS
+ const React = require("devtools/client/shared/vendor/react");
+
+ // Reps
+ const { createFactories, isGrip } = require("./rep-utils");
+ const { StringRep } = require("./string");
+
+ // Shortcuts
+ const { span } = React.DOM;
+ const { rep: StringRepFactory } = createFactories(StringRep);
+
+ /**
+ * Renders DOM attribute
+ */
+ let Attribute = React.createClass({
+ displayName: "Attr",
+
+ propTypes: {
+ object: React.PropTypes.object.isRequired
+ },
+
+ getTitle: function (grip) {
+ return grip.preview.nodeName;
+ },
+
+ render: function () {
+ let grip = this.props.object;
+ let value = grip.preview.value;
+ let objectLink = this.props.objectLink || span;
+
+ return (
+ objectLink({className: "objectLink-Attr"},
+ span({},
+ span({className: "attrTitle"},
+ this.getTitle(grip)
+ ),
+ span({className: "attrEqual"},
+ "="
+ ),
+ StringRepFactory({object: value})
+ )
+ )
+ );
+ },
+ });
+
+ // Registration
+
+ function supportsObject(grip, type) {
+ if (!isGrip(grip)) {
+ return false;
+ }
+
+ return (type == "Attr" && grip.preview);
+ }
+
+ exports.Attribute = {
+ rep: Attribute,
+ supportsObject: supportsObject
+ };
+});
diff --git a/devtools/client/shared/components/reps/caption.js b/devtools/client/shared/components/reps/caption.js
new file mode 100644
index 000000000..7f00b01e8
--- /dev/null
+++ b/devtools/client/shared/components/reps/caption.js
@@ -0,0 +1,31 @@
+/* -*- 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";
+
+// Make this available to both AMD and CJS environments
+define(function (require, exports, module) {
+ // Dependencies
+ const React = require("devtools/client/shared/vendor/react");
+ const DOM = React.DOM;
+
+ /**
+ * Renders a caption. This template is used by other components
+ * that needs to distinguish between a simple text/value and a label.
+ */
+ const Caption = React.createClass({
+ displayName: "Caption",
+
+ render: function () {
+ return (
+ DOM.span({"className": "caption"}, this.props.object)
+ );
+ },
+ });
+
+ // Exports from this module
+ exports.Caption = Caption;
+});
diff --git a/devtools/client/shared/components/reps/comment-node.js b/devtools/client/shared/components/reps/comment-node.js
new file mode 100644
index 000000000..2c69c1414
--- /dev/null
+++ b/devtools/client/shared/components/reps/comment-node.js
@@ -0,0 +1,60 @@
+/* -*- 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";
+
+// Make this available to both AMD and CJS environments
+define(function (require, exports, module) {
+ // ReactJS
+ const React = require("devtools/client/shared/vendor/react");
+ const { isGrip, cropString, cropMultipleLines } = require("./rep-utils");
+
+ // Utils
+ const nodeConstants = require("devtools/shared/dom-node-constants");
+
+ // Shortcuts
+ const { span } = React.DOM;
+
+ /**
+ * Renders DOM comment node.
+ */
+ const CommentNode = React.createClass({
+ displayName: "CommentNode",
+
+ propTypes: {
+ object: React.PropTypes.object.isRequired,
+ mode: React.PropTypes.string,
+ },
+
+ render: function () {
+ let {object} = this.props;
+
+ let mode = this.props.mode || "short";
+
+ let {textContent} = object.preview;
+ if (mode === "tiny") {
+ textContent = cropMultipleLines(textContent, 30);
+ } else if (mode === "short") {
+ textContent = cropString(textContent, 50);
+ }
+
+ return span({className: "objectBox theme-comment"}, `<!-- ${textContent} -->`);
+ },
+ });
+
+ // Registration
+ function supportsObject(object, type) {
+ if (!isGrip(object)) {
+ return false;
+ }
+ return object.preview && object.preview.nodeType === nodeConstants.COMMENT_NODE;
+ }
+
+ // Exports from this module
+ exports.CommentNode = {
+ rep: CommentNode,
+ supportsObject: supportsObject
+ };
+});
diff --git a/devtools/client/shared/components/reps/date-time.js b/devtools/client/shared/components/reps/date-time.js
new file mode 100644
index 000000000..55dfb7d2d
--- /dev/null
+++ b/devtools/client/shared/components/reps/date-time.js
@@ -0,0 +1,70 @@
+/* -*- 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";
+
+// Make this available to both AMD and CJS environments
+define(function (require, exports, module) {
+ // ReactJS
+ const React = require("devtools/client/shared/vendor/react");
+
+ // Reps
+ const { isGrip } = require("./rep-utils");
+
+ // Shortcuts
+ const { span } = React.DOM;
+
+ /**
+ * Used to render JS built-in Date() object.
+ */
+ let DateTime = React.createClass({
+ displayName: "Date",
+
+ propTypes: {
+ object: React.PropTypes.object.isRequired
+ },
+
+ getTitle: function (grip) {
+ if (this.props.objectLink) {
+ return this.props.objectLink({
+ object: grip
+ }, grip.class + " ");
+ }
+ return "";
+ },
+
+ render: function () {
+ let grip = this.props.object;
+ let date;
+ try {
+ date = span({className: "objectBox"},
+ this.getTitle(grip),
+ span({className: "Date"},
+ new Date(grip.preview.timestamp).toISOString()
+ )
+ );
+ } catch (e) {
+ date = span({className: "objectBox"}, "Invalid Date");
+ }
+ return date;
+ },
+ });
+
+ // Registration
+
+ function supportsObject(grip, type) {
+ if (!isGrip(grip)) {
+ return false;
+ }
+
+ return (type == "Date" && grip.preview);
+ }
+
+ // Exports from this module
+ exports.DateTime = {
+ rep: DateTime,
+ supportsObject: supportsObject
+ };
+});
diff --git a/devtools/client/shared/components/reps/document.js b/devtools/client/shared/components/reps/document.js
new file mode 100644
index 000000000..25e42609f
--- /dev/null
+++ b/devtools/client/shared/components/reps/document.js
@@ -0,0 +1,78 @@
+/* -*- 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";
+
+// Make this available to both AMD and CJS environments
+define(function (require, exports, module) {
+ // ReactJS
+ const React = require("devtools/client/shared/vendor/react");
+
+ // Reps
+ const { isGrip, getURLDisplayString } = require("./rep-utils");
+
+ // Shortcuts
+ const { span } = React.DOM;
+
+ /**
+ * Renders DOM document object.
+ */
+ let Document = React.createClass({
+ displayName: "Document",
+
+ propTypes: {
+ object: React.PropTypes.object.isRequired
+ },
+
+ getLocation: function (grip) {
+ let location = grip.preview.location;
+ return location ? getURLDisplayString(location) : "";
+ },
+
+ getTitle: function (grip) {
+ if (this.props.objectLink) {
+ return span({className: "objectBox"},
+ this.props.objectLink({
+ object: grip
+ }, grip.class + " ")
+ );
+ }
+ return "";
+ },
+
+ getTooltip: function (doc) {
+ return doc.location.href;
+ },
+
+ render: function () {
+ let grip = this.props.object;
+
+ return (
+ span({className: "objectBox objectBox-object"},
+ this.getTitle(grip),
+ span({className: "objectPropValue"},
+ this.getLocation(grip)
+ )
+ )
+ );
+ },
+ });
+
+ // Registration
+
+ function supportsObject(object, type) {
+ if (!isGrip(object)) {
+ return false;
+ }
+
+ return (object.preview && type == "HTMLDocument");
+ }
+
+ // Exports from this module
+ exports.Document = {
+ rep: Document,
+ supportsObject: supportsObject
+ };
+});
diff --git a/devtools/client/shared/components/reps/element-node.js b/devtools/client/shared/components/reps/element-node.js
new file mode 100644
index 000000000..6315fb5b1
--- /dev/null
+++ b/devtools/client/shared/components/reps/element-node.js
@@ -0,0 +1,114 @@
+/* -*- 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";
+
+// Make this available to both AMD and CJS environments
+define(function (require, exports, module) {
+ // ReactJS
+ const React = require("devtools/client/shared/vendor/react");
+ const { isGrip } = require("./rep-utils");
+
+ // Utils
+ const nodeConstants = require("devtools/shared/dom-node-constants");
+
+ // Shortcuts
+ const { span } = React.DOM;
+
+ /**
+ * Renders DOM element node.
+ */
+ const ElementNode = React.createClass({
+ displayName: "ElementNode",
+
+ propTypes: {
+ object: React.PropTypes.object.isRequired,
+ mode: React.PropTypes.string,
+ },
+
+ getElements: function (grip, mode) {
+ let {attributes, nodeName} = grip.preview;
+ const nodeNameElement = span({
+ className: "tag-name theme-fg-color3"
+ }, nodeName);
+
+ if (mode === "tiny") {
+ let elements = [nodeNameElement];
+ if (attributes.id) {
+ elements.push(
+ span({className: "attr-name theme-fg-color2"}, `#${attributes.id}`));
+ }
+ if (attributes.class) {
+ elements.push(
+ span({className: "attr-name theme-fg-color2"},
+ attributes.class
+ .replace(/(^\s+)|(\s+$)/g, "")
+ .split(" ")
+ .map(cls => `.${cls}`)
+ .join("")
+ )
+ );
+ }
+ return elements;
+ }
+ let attributeElements = Object.keys(attributes)
+ .sort(function getIdAndClassFirst(a1, a2) {
+ if ([a1, a2].includes("id")) {
+ return 3 * (a1 === "id" ? -1 : 1);
+ }
+ if ([a1, a2].includes("class")) {
+ return 2 * (a1 === "class" ? -1 : 1);
+ }
+
+ // `id` and `class` excepted,
+ // we want to keep the same order that in `attributes`.
+ return 0;
+ })
+ .reduce((arr, name, i, keys) => {
+ let value = attributes[name];
+ let attribute = span({},
+ span({className: "attr-name theme-fg-color2"}, `${name}`),
+ `="`,
+ span({className: "attr-value theme-fg-color6"}, `${value}`),
+ `"`
+ );
+
+ return arr.concat([" ", attribute]);
+ }, []);
+
+ return [
+ "<",
+ nodeNameElement,
+ ...attributeElements,
+ ">",
+ ];
+ },
+
+ render: function () {
+ let {object, mode} = this.props;
+ let elements = this.getElements(object, mode);
+ const baseElement = span({className: "objectBox"}, ...elements);
+
+ if (this.props.objectLink) {
+ return this.props.objectLink({object}, baseElement);
+ }
+ return baseElement;
+ },
+ });
+
+ // Registration
+ function supportsObject(object, type) {
+ if (!isGrip(object)) {
+ return false;
+ }
+ return object.preview && object.preview.nodeType === nodeConstants.ELEMENT_NODE;
+ }
+
+ // Exports from this module
+ exports.ElementNode = {
+ rep: ElementNode,
+ supportsObject: supportsObject
+ };
+});
diff --git a/devtools/client/shared/components/reps/event.js b/devtools/client/shared/components/reps/event.js
new file mode 100644
index 000000000..1d37e0150
--- /dev/null
+++ b/devtools/client/shared/components/reps/event.js
@@ -0,0 +1,81 @@
+/* -*- 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";
+
+// Make this available to both AMD and CJS environments
+define(function (require, exports, module) {
+ // ReactJS
+ const React = require("devtools/client/shared/vendor/react");
+
+ // Reps
+ const { createFactories, isGrip } = require("./rep-utils");
+ const { rep } = createFactories(require("./grip").Grip);
+
+ /**
+ * Renders DOM event objects.
+ */
+ let Event = React.createClass({
+ displayName: "event",
+
+ propTypes: {
+ object: React.PropTypes.object.isRequired
+ },
+
+ render: function () {
+ // Use `Object.assign` to keep `this.props` without changes because:
+ // 1. JSON.stringify/JSON.parse is slow.
+ // 2. Immutable.js is planned for the future.
+ let props = Object.assign({}, this.props);
+ props.object = Object.assign({}, this.props.object);
+ props.object.preview = Object.assign({}, this.props.object.preview);
+ props.object.preview.ownProperties = props.object.preview.properties;
+ delete props.object.preview.properties;
+ props.object.ownPropertyLength =
+ Object.keys(props.object.preview.ownProperties).length;
+
+ switch (props.object.class) {
+ case "MouseEvent":
+ props.isInterestingProp = (type, value, name) => {
+ return (name == "clientX" ||
+ name == "clientY" ||
+ name == "layerX" ||
+ name == "layerY");
+ };
+ break;
+ case "KeyboardEvent":
+ props.isInterestingProp = (type, value, name) => {
+ return (name == "key" ||
+ name == "charCode" ||
+ name == "keyCode");
+ };
+ break;
+ case "MessageEvent":
+ props.isInterestingProp = (type, value, name) => {
+ return (name == "isTrusted" ||
+ name == "data");
+ };
+ break;
+ }
+ return rep(props);
+ }
+ });
+
+ // Registration
+
+ function supportsObject(grip, type) {
+ if (!isGrip(grip)) {
+ return false;
+ }
+
+ return (grip.preview && grip.preview.kind == "DOMEvent");
+ }
+
+ // Exports from this module
+ exports.Event = {
+ rep: Event,
+ supportsObject: supportsObject
+ };
+});
diff --git a/devtools/client/shared/components/reps/function.js b/devtools/client/shared/components/reps/function.js
new file mode 100644
index 000000000..fd20dc318
--- /dev/null
+++ b/devtools/client/shared/components/reps/function.js
@@ -0,0 +1,73 @@
+/* -*- 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";
+
+// Make this available to both AMD and CJS environments
+define(function (require, exports, module) {
+ // ReactJS
+ const React = require("devtools/client/shared/vendor/react");
+
+ // Reps
+ const { isGrip, cropString } = require("./rep-utils");
+
+ // Shortcuts
+ const { span } = React.DOM;
+
+ /**
+ * This component represents a template for Function objects.
+ */
+ let Func = React.createClass({
+ displayName: "Func",
+
+ propTypes: {
+ object: React.PropTypes.object.isRequired
+ },
+
+ getTitle: function (grip) {
+ if (this.props.objectLink) {
+ return this.props.objectLink({
+ object: grip
+ }, "function ");
+ }
+ return "";
+ },
+
+ summarizeFunction: function (grip) {
+ let name = grip.userDisplayName || grip.displayName || grip.name || "function";
+ return cropString(name + "()", 100);
+ },
+
+ render: function () {
+ let grip = this.props.object;
+
+ return (
+ // Set dir="ltr" to prevent function parentheses from
+ // appearing in the wrong direction
+ span({dir: "ltr", className: "objectBox objectBox-function"},
+ this.getTitle(grip),
+ this.summarizeFunction(grip)
+ )
+ );
+ },
+ });
+
+ // Registration
+
+ function supportsObject(grip, type) {
+ if (!isGrip(grip)) {
+ return (type == "function");
+ }
+
+ return (type == "Function");
+ }
+
+ // Exports from this module
+
+ exports.Func = {
+ rep: Func,
+ supportsObject: supportsObject
+ };
+});
diff --git a/devtools/client/shared/components/reps/grip-array.js b/devtools/client/shared/components/reps/grip-array.js
new file mode 100644
index 000000000..04a5603bb
--- /dev/null
+++ b/devtools/client/shared/components/reps/grip-array.js
@@ -0,0 +1,198 @@
+/* -*- 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";
+
+// Make this available to both AMD and CJS environments
+define(function (require, exports, module) {
+ // Dependencies
+ const React = require("devtools/client/shared/vendor/react");
+ const { createFactories, isGrip } = require("./rep-utils");
+ const { Caption } = createFactories(require("./caption"));
+
+ // Shortcuts
+ const { span } = React.DOM;
+
+ /**
+ * Renders an array. The array is enclosed by left and right bracket
+ * and the max number of rendered items depends on the current mode.
+ */
+ let GripArray = React.createClass({
+ displayName: "GripArray",
+
+ propTypes: {
+ object: React.PropTypes.object.isRequired,
+ mode: React.PropTypes.string,
+ provider: React.PropTypes.object,
+ },
+
+ getLength: function (grip) {
+ if (!grip.preview) {
+ return 0;
+ }
+
+ return grip.preview.length || grip.preview.childNodesLength || 0;
+ },
+
+ getTitle: function (object, context) {
+ let objectLink = this.props.objectLink || span;
+ if (this.props.mode != "tiny") {
+ return objectLink({
+ object: object
+ }, object.class + " ");
+ }
+ return "";
+ },
+
+ getPreviewItems: function (grip) {
+ if (!grip.preview) {
+ return null;
+ }
+
+ return grip.preview.items || grip.preview.childNodes || null;
+ },
+
+ arrayIterator: function (grip, max) {
+ let items = [];
+ const gripLength = this.getLength(grip);
+
+ if (!gripLength) {
+ return items;
+ }
+
+ const previewItems = this.getPreviewItems(grip);
+ if (!previewItems) {
+ return items;
+ }
+
+ let delim;
+ // number of grip preview items is limited to 10, but we may have more
+ // items in grip-array.
+ let delimMax = gripLength > previewItems.length ?
+ previewItems.length : previewItems.length - 1;
+ let provider = this.props.provider;
+
+ for (let i = 0; i < previewItems.length && i < max; i++) {
+ try {
+ let itemGrip = previewItems[i];
+ let value = provider ? provider.getValue(itemGrip) : itemGrip;
+
+ delim = (i == delimMax ? "" : ", ");
+
+ items.push(GripArrayItem(Object.assign({}, this.props, {
+ object: value,
+ delim: delim
+ })));
+ } catch (exc) {
+ items.push(GripArrayItem(Object.assign({}, this.props, {
+ object: exc,
+ delim: delim
+ })));
+ }
+ }
+ if (previewItems.length > max || gripLength > previewItems.length) {
+ let objectLink = this.props.objectLink || span;
+ let leftItemNum = gripLength - max > 0 ?
+ gripLength - max : gripLength - previewItems.length;
+ items.push(Caption({
+ object: objectLink({
+ object: this.props.object
+ }, leftItemNum + " more…")
+ }));
+ }
+
+ return items;
+ },
+
+ render: function () {
+ let mode = this.props.mode || "short";
+ let object = this.props.object;
+
+ let items;
+ let brackets;
+ let needSpace = function (space) {
+ return space ? { left: "[ ", right: " ]"} : { left: "[", right: "]"};
+ };
+
+ if (mode == "tiny") {
+ let objectLength = this.getLength(object);
+ let isEmpty = objectLength === 0;
+ items = [span({className: "length"}, isEmpty ? "" : objectLength)];
+ brackets = needSpace(false);
+ } else {
+ let max = (mode == "short") ? 3 : 300;
+ items = this.arrayIterator(object, max);
+ brackets = needSpace(items.length > 0);
+ }
+
+ let objectLink = this.props.objectLink || span;
+ let title = this.getTitle(object);
+
+ return (
+ span({
+ className: "objectBox objectBox-array"},
+ title,
+ objectLink({
+ className: "arrayLeftBracket",
+ object: object
+ }, brackets.left),
+ ...items,
+ objectLink({
+ className: "arrayRightBracket",
+ object: object
+ }, brackets.right),
+ span({
+ className: "arrayProperties",
+ role: "group"}
+ )
+ )
+ );
+ },
+ });
+
+ /**
+ * Renders array item. Individual values are separated by
+ * a delimiter (a comma by default).
+ */
+ let GripArrayItem = React.createFactory(React.createClass({
+ displayName: "GripArrayItem",
+
+ propTypes: {
+ delim: React.PropTypes.string,
+ },
+
+ render: function () {
+ let { Rep } = createFactories(require("./rep"));
+
+ return (
+ span({},
+ Rep(Object.assign({}, this.props, {
+ mode: "tiny"
+ })),
+ this.props.delim
+ )
+ );
+ }
+ }));
+
+ function supportsObject(grip, type) {
+ if (!isGrip(grip)) {
+ return false;
+ }
+
+ return (grip.preview && (
+ grip.preview.kind == "ArrayLike" ||
+ type === "DocumentFragment"
+ )
+ );
+ }
+
+ // Exports from this module
+ exports.GripArray = {
+ rep: GripArray,
+ supportsObject: supportsObject
+ };
+});
diff --git a/devtools/client/shared/components/reps/grip-map.js b/devtools/client/shared/components/reps/grip-map.js
new file mode 100644
index 000000000..df673d005
--- /dev/null
+++ b/devtools/client/shared/components/reps/grip-map.js
@@ -0,0 +1,193 @@
+/* -*- 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";
+// Make this available to both AMD and CJS environments
+define(function (require, exports, module) {
+ // Dependencies
+ const React = require("devtools/client/shared/vendor/react");
+ const { createFactories, isGrip } = require("./rep-utils");
+ const { Caption } = createFactories(require("./caption"));
+ const { PropRep } = createFactories(require("./prop-rep"));
+
+ // Shortcuts
+ const { span } = React.DOM;
+ /**
+ * Renders an map. A map is represented by a list of its
+ * entries enclosed in curly brackets.
+ */
+ const GripMap = React.createClass({
+ displayName: "GripMap",
+
+ propTypes: {
+ object: React.PropTypes.object,
+ mode: React.PropTypes.string,
+ },
+
+ getTitle: function (object) {
+ let title = object && object.class ? object.class : "Map";
+ if (this.props.objectLink) {
+ return this.props.objectLink({
+ object: object
+ }, title);
+ }
+ return title;
+ },
+
+ safeEntriesIterator: function (object, max) {
+ max = (typeof max === "undefined") ? 3 : max;
+ try {
+ return this.entriesIterator(object, max);
+ } catch (err) {
+ console.error(err);
+ }
+ return [];
+ },
+
+ entriesIterator: function (object, max) {
+ // Entry filter. Show only interesting entries to the user.
+ let isInterestingEntry = this.props.isInterestingEntry || ((type, value) => {
+ return (
+ type == "boolean" ||
+ type == "number" ||
+ (type == "string" && value.length != 0)
+ );
+ });
+
+ let mapEntries = object.preview && object.preview.entries
+ ? object.preview.entries : [];
+
+ let indexes = this.getEntriesIndexes(mapEntries, max, isInterestingEntry);
+ if (indexes.length < max && indexes.length < mapEntries.length) {
+ // There are not enough entries yet, so we add uninteresting entries.
+ indexes = indexes.concat(
+ this.getEntriesIndexes(mapEntries, max - indexes.length, (t, value, name) => {
+ return !isInterestingEntry(t, value, name);
+ })
+ );
+ }
+
+ let entries = this.getEntries(mapEntries, indexes);
+ if (entries.length < mapEntries.length) {
+ // There are some undisplayed entries. Then display "more…".
+ let objectLink = this.props.objectLink || span;
+
+ entries.push(Caption({
+ key: "more",
+ object: objectLink({
+ object: object
+ }, `${mapEntries.length - max} more…`)
+ }));
+ }
+
+ return entries;
+ },
+
+ /**
+ * Get entries ordered by index.
+ *
+ * @param {Array} entries Entries array.
+ * @param {Array} indexes Indexes of entries.
+ * @return {Array} Array of PropRep.
+ */
+ getEntries: function (entries, indexes) {
+ // Make indexes ordered by ascending.
+ indexes.sort(function (a, b) {
+ return a - b;
+ });
+
+ return indexes.map((index, i) => {
+ let [key, entryValue] = entries[index];
+ let value = entryValue.value !== undefined ? entryValue.value : entryValue;
+
+ return PropRep({
+ // key,
+ name: key,
+ equal: ": ",
+ object: value,
+ // Do not add a trailing comma on the last entry
+ // if there won't be a "more..." item.
+ delim: (i < indexes.length - 1 || indexes.length < entries.length) ? ", " : "",
+ mode: "tiny",
+ objectLink: this.props.objectLink,
+ });
+ });
+ },
+
+ /**
+ * Get the indexes of entries in the map.
+ *
+ * @param {Array} entries Entries array.
+ * @param {Number} max The maximum length of indexes array.
+ * @param {Function} filter Filter the entry you want.
+ * @return {Array} Indexes of filtered entries in the map.
+ */
+ getEntriesIndexes: function (entries, max, filter) {
+ return entries
+ .reduce((indexes, [key, entry], i) => {
+ if (indexes.length < max) {
+ let value = (entry && entry.value !== undefined) ? entry.value : entry;
+ // Type is specified in grip's "class" field and for primitive
+ // values use typeof.
+ let type = (value && value.class ? value.class : typeof value).toLowerCase();
+
+ if (filter(type, value, key)) {
+ indexes.push(i);
+ }
+ }
+
+ return indexes;
+ }, []);
+ },
+
+ render: function () {
+ let object = this.props.object;
+ let props = this.safeEntriesIterator(object,
+ (this.props.mode == "long") ? 100 : 3);
+
+ let objectLink = this.props.objectLink || span;
+ if (this.props.mode == "tiny") {
+ return (
+ span({className: "objectBox objectBox-object"},
+ this.getTitle(object),
+ objectLink({
+ className: "objectLeftBrace",
+ object: object
+ }, "")
+ )
+ );
+ }
+
+ return (
+ span({className: "objectBox objectBox-object"},
+ this.getTitle(object),
+ objectLink({
+ className: "objectLeftBrace",
+ object: object
+ }, " { "),
+ props,
+ objectLink({
+ className: "objectRightBrace",
+ object: object
+ }, " }")
+ )
+ );
+ },
+ });
+
+ function supportsObject(grip, type) {
+ if (!isGrip(grip)) {
+ return false;
+ }
+ return (grip.preview && grip.preview.kind == "MapLike");
+ }
+
+ // Exports from this module
+ exports.GripMap = {
+ rep: GripMap,
+ supportsObject: supportsObject
+ };
+});
diff --git a/devtools/client/shared/components/reps/grip.js b/devtools/client/shared/components/reps/grip.js
new file mode 100644
index 000000000..c63ee19f3
--- /dev/null
+++ b/devtools/client/shared/components/reps/grip.js
@@ -0,0 +1,247 @@
+/* -*- 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";
+
+// Make this available to both AMD and CJS environments
+define(function (require, exports, module) {
+ // ReactJS
+ const React = require("devtools/client/shared/vendor/react");
+ // Dependencies
+ const { createFactories, isGrip } = require("./rep-utils");
+ const { Caption } = createFactories(require("./caption"));
+ const { PropRep } = createFactories(require("./prop-rep"));
+ // Shortcuts
+ const { span } = React.DOM;
+
+ /**
+ * Renders generic grip. Grip is client representation
+ * of remote JS object and is used as an input object
+ * for this rep component.
+ */
+ const GripRep = React.createClass({
+ displayName: "Grip",
+
+ propTypes: {
+ object: React.PropTypes.object.isRequired,
+ mode: React.PropTypes.string,
+ isInterestingProp: React.PropTypes.func
+ },
+
+ getTitle: function (object) {
+ if (this.props.objectLink) {
+ return this.props.objectLink({
+ object: object
+ }, object.class);
+ }
+ return object.class || "Object";
+ },
+
+ safePropIterator: function (object, max) {
+ max = (typeof max === "undefined") ? 3 : max;
+ try {
+ return this.propIterator(object, max);
+ } catch (err) {
+ console.error(err);
+ }
+ return [];
+ },
+
+ propIterator: function (object, max) {
+ if (object.preview && Object.keys(object.preview).includes("wrappedValue")) {
+ const { Rep } = createFactories(require("./rep"));
+
+ return [Rep({
+ object: object.preview.wrappedValue,
+ mode: this.props.mode || "tiny",
+ defaultRep: Grip,
+ })];
+ }
+
+ // Property filter. Show only interesting properties to the user.
+ let isInterestingProp = this.props.isInterestingProp || ((type, value) => {
+ return (
+ type == "boolean" ||
+ type == "number" ||
+ (type == "string" && value.length != 0)
+ );
+ });
+
+ let properties = object.preview
+ ? object.preview.ownProperties
+ : {};
+ let propertiesLength = object.preview && object.preview.ownPropertiesLength
+ ? object.preview.ownPropertiesLength
+ : object.ownPropertyLength;
+
+ if (object.preview && object.preview.safeGetterValues) {
+ properties = Object.assign({}, properties, object.preview.safeGetterValues);
+ propertiesLength += Object.keys(object.preview.safeGetterValues).length;
+ }
+
+ let indexes = this.getPropIndexes(properties, max, isInterestingProp);
+ if (indexes.length < max && indexes.length < propertiesLength) {
+ // There are not enough props yet. Then add uninteresting props to display them.
+ indexes = indexes.concat(
+ this.getPropIndexes(properties, max - indexes.length, (t, value, name) => {
+ return !isInterestingProp(t, value, name);
+ })
+ );
+ }
+
+ const truncate = Object.keys(properties).length > max;
+ let props = this.getProps(properties, indexes, truncate);
+ if (truncate) {
+ // There are some undisplayed props. Then display "more...".
+ let objectLink = this.props.objectLink || span;
+
+ props.push(Caption({
+ object: objectLink({
+ object: object
+ }, `${object.ownPropertyLength - max} more…`)
+ }));
+ }
+
+ return props;
+ },
+
+ /**
+ * Get props ordered by index.
+ *
+ * @param {Object} properties Props object.
+ * @param {Array} indexes Indexes of props.
+ * @param {Boolean} truncate true if the grip will be truncated.
+ * @return {Array} Props.
+ */
+ getProps: function (properties, indexes, truncate) {
+ let props = [];
+
+ // Make indexes ordered by ascending.
+ indexes.sort(function (a, b) {
+ return a - b;
+ });
+
+ indexes.forEach((i) => {
+ let name = Object.keys(properties)[i];
+ let value = this.getPropValue(properties[name]);
+
+ props.push(PropRep(Object.assign({}, this.props, {
+ mode: "tiny",
+ name: name,
+ object: value,
+ equal: ": ",
+ delim: i !== indexes.length - 1 || truncate ? ", " : "",
+ defaultRep: Grip
+ })));
+ });
+
+ return props;
+ },
+
+ /**
+ * Get the indexes of props in the object.
+ *
+ * @param {Object} properties Props object.
+ * @param {Number} max The maximum length of indexes array.
+ * @param {Function} filter Filter the props you want.
+ * @return {Array} Indexes of interesting props in the object.
+ */
+ getPropIndexes: function (properties, max, filter) {
+ let indexes = [];
+
+ try {
+ let i = 0;
+ for (let name in properties) {
+ if (indexes.length >= max) {
+ return indexes;
+ }
+
+ // Type is specified in grip's "class" field and for primitive
+ // values use typeof.
+ let value = this.getPropValue(properties[name]);
+ let type = (value.class || typeof value);
+ type = type.toLowerCase();
+
+ if (filter(type, value, name)) {
+ indexes.push(i);
+ }
+ i++;
+ }
+ } catch (err) {
+ console.error(err);
+ }
+ return indexes;
+ },
+
+ /**
+ * Get the actual value of a property.
+ *
+ * @param {Object} property
+ * @return {Object} Value of the property.
+ */
+ getPropValue: function (property) {
+ let value = property;
+ if (typeof property === "object") {
+ let keys = Object.keys(property);
+ if (keys.includes("value")) {
+ value = property.value;
+ } else if (keys.includes("getterValue")) {
+ value = property.getterValue;
+ }
+ }
+ return value;
+ },
+
+ render: function () {
+ let object = this.props.object;
+ let props = this.safePropIterator(object,
+ (this.props.mode == "long") ? 100 : 3);
+
+ let objectLink = this.props.objectLink || span;
+ if (this.props.mode == "tiny") {
+ return (
+ span({className: "objectBox objectBox-object"},
+ this.getTitle(object),
+ objectLink({
+ className: "objectLeftBrace",
+ object: object
+ }, "")
+ )
+ );
+ }
+
+ return (
+ span({className: "objectBox objectBox-object"},
+ this.getTitle(object),
+ objectLink({
+ className: "objectLeftBrace",
+ object: object
+ }, " { "),
+ ...props,
+ objectLink({
+ className: "objectRightBrace",
+ object: object
+ }, " }")
+ )
+ );
+ },
+ });
+
+ // Registration
+ function supportsObject(object, type) {
+ if (!isGrip(object)) {
+ return false;
+ }
+ return (object.preview && object.preview.ownProperties);
+ }
+
+ let Grip = {
+ rep: GripRep,
+ supportsObject: supportsObject
+ };
+
+ // Exports from this module
+ exports.Grip = Grip;
+});
diff --git a/devtools/client/shared/components/reps/infinity.js b/devtools/client/shared/components/reps/infinity.js
new file mode 100644
index 000000000..604e31f06
--- /dev/null
+++ b/devtools/client/shared/components/reps/infinity.js
@@ -0,0 +1,41 @@
+/* -*- 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";
+
+// Make this available to both AMD and CJS environments
+define(function (require, exports, module) {
+ // Dependencies
+ const React = require("devtools/client/shared/vendor/react");
+
+ // Shortcuts
+ const { span } = React.DOM;
+
+ /**
+ * Renders a Infinity object
+ */
+ const InfinityRep = React.createClass({
+ displayName: "Infinity",
+
+ render: function () {
+ return (
+ span({className: "objectBox objectBox-number"},
+ this.props.object.type
+ )
+ );
+ }
+ });
+
+ function supportsObject(object, type) {
+ return type == "Infinity" || type == "-Infinity";
+ }
+
+ // Exports from this module
+ exports.InfinityRep = {
+ rep: InfinityRep,
+ supportsObject: supportsObject
+ };
+});
diff --git a/devtools/client/shared/components/reps/long-string.js b/devtools/client/shared/components/reps/long-string.js
new file mode 100644
index 000000000..f19f020dc
--- /dev/null
+++ b/devtools/client/shared/components/reps/long-string.js
@@ -0,0 +1,71 @@
+/* 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";
+
+// Make this available to both AMD and CJS environments
+define(function (require, exports, module) {
+ // Dependencies
+ const React = require("devtools/client/shared/vendor/react");
+ const { sanitizeString, isGrip } = require("./rep-utils");
+ // Shortcuts
+ const { span } = React.DOM;
+
+ /**
+ * Renders a long string grip.
+ */
+ const LongStringRep = React.createClass({
+ displayName: "LongStringRep",
+
+ propTypes: {
+ useQuotes: React.PropTypes.bool,
+ style: React.PropTypes.object,
+ },
+
+ getDefaultProps: function () {
+ return {
+ useQuotes: true,
+ };
+ },
+
+ render: function () {
+ let {
+ cropLimit,
+ member,
+ object,
+ style,
+ useQuotes
+ } = this.props;
+ let {fullText, initial, length} = object;
+
+ let config = {className: "objectBox objectBox-string"};
+ if (style) {
+ config.style = style;
+ }
+
+ let string = member && member.open
+ ? fullText || initial
+ : initial.substring(0, cropLimit);
+
+ if (string.length < length) {
+ string += "\u2026";
+ }
+ let formattedString = useQuotes ? `"${string}"` : string;
+ return span(config, sanitizeString(formattedString));
+ },
+ });
+
+ function supportsObject(object, type) {
+ if (!isGrip(object)) {
+ return false;
+ }
+ return object.type === "longString";
+ }
+
+ // Exports from this module
+ exports.LongStringRep = {
+ rep: LongStringRep,
+ supportsObject: supportsObject,
+ };
+});
diff --git a/devtools/client/shared/components/reps/moz.build b/devtools/client/shared/components/reps/moz.build
new file mode 100644
index 000000000..f5df589f7
--- /dev/null
+++ b/devtools/client/shared/components/reps/moz.build
@@ -0,0 +1,40 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# 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(
+ 'array.js',
+ 'attribute.js',
+ 'caption.js',
+ 'comment-node.js',
+ 'date-time.js',
+ 'document.js',
+ 'element-node.js',
+ 'event.js',
+ 'function.js',
+ 'grip-array.js',
+ 'grip-map.js',
+ 'grip.js',
+ 'infinity.js',
+ 'long-string.js',
+ 'nan.js',
+ 'null.js',
+ 'number.js',
+ 'object-with-text.js',
+ 'object-with-url.js',
+ 'object.js',
+ 'promise.js',
+ 'prop-rep.js',
+ 'regexp.js',
+ 'rep-utils.js',
+ 'rep.js',
+ 'reps.css',
+ 'string.js',
+ 'stylesheet.js',
+ 'symbol.js',
+ 'text-node.js',
+ 'undefined.js',
+ 'window.js',
+)
diff --git a/devtools/client/shared/components/reps/nan.js b/devtools/client/shared/components/reps/nan.js
new file mode 100644
index 000000000..b76a5cfd3
--- /dev/null
+++ b/devtools/client/shared/components/reps/nan.js
@@ -0,0 +1,41 @@
+/* -*- 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";
+
+// Make this available to both AMD and CJS environments
+define(function (require, exports, module) {
+ // Dependencies
+ const React = require("devtools/client/shared/vendor/react");
+
+ // Shortcuts
+ const { span } = React.DOM;
+
+ /**
+ * Renders a NaN object
+ */
+ const NaNRep = React.createClass({
+ displayName: "NaN",
+
+ render: function () {
+ return (
+ span({className: "objectBox objectBox-nan"},
+ "NaN"
+ )
+ );
+ }
+ });
+
+ function supportsObject(object, type) {
+ return type == "NaN";
+ }
+
+ // Exports from this module
+ exports.NaNRep = {
+ rep: NaNRep,
+ supportsObject: supportsObject
+ };
+});
diff --git a/devtools/client/shared/components/reps/null.js b/devtools/client/shared/components/reps/null.js
new file mode 100644
index 000000000..5de00f026
--- /dev/null
+++ b/devtools/client/shared/components/reps/null.js
@@ -0,0 +1,46 @@
+/* -*- 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";
+
+// Make this available to both AMD and CJS environments
+define(function (require, exports, module) {
+ // Dependencies
+ const React = require("devtools/client/shared/vendor/react");
+
+ // Shortcuts
+ const { span } = React.DOM;
+
+ /**
+ * Renders null value
+ */
+ const Null = React.createClass({
+ displayName: "NullRep",
+
+ render: function () {
+ return (
+ span({className: "objectBox objectBox-null"},
+ "null"
+ )
+ );
+ },
+ });
+
+ function supportsObject(object, type) {
+ if (object && object.type && object.type == "null") {
+ return true;
+ }
+
+ return (object == null);
+ }
+
+ // Exports from this module
+
+ exports.Null = {
+ rep: Null,
+ supportsObject: supportsObject
+ };
+});
diff --git a/devtools/client/shared/components/reps/number.js b/devtools/client/shared/components/reps/number.js
new file mode 100644
index 000000000..31be3009b
--- /dev/null
+++ b/devtools/client/shared/components/reps/number.js
@@ -0,0 +1,51 @@
+/* -*- 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";
+
+// Make this available to both AMD and CJS environments
+define(function (require, exports, module) {
+ // Dependencies
+ const React = require("devtools/client/shared/vendor/react");
+
+ // Shortcuts
+ const { span } = React.DOM;
+
+ /**
+ * Renders a number
+ */
+ const Number = React.createClass({
+ displayName: "Number",
+
+ stringify: function (object) {
+ let isNegativeZero = Object.is(object, -0) ||
+ (object.type && object.type == "-0");
+
+ return (isNegativeZero ? "-0" : String(object));
+ },
+
+ render: function () {
+ let value = this.props.object;
+
+ return (
+ span({className: "objectBox objectBox-number"},
+ this.stringify(value)
+ )
+ );
+ }
+ });
+
+ function supportsObject(object, type) {
+ return ["boolean", "number", "-0"].includes(type);
+ }
+
+ // Exports from this module
+
+ exports.Number = {
+ rep: Number,
+ supportsObject: supportsObject
+ };
+});
diff --git a/devtools/client/shared/components/reps/object-with-text.js b/devtools/client/shared/components/reps/object-with-text.js
new file mode 100644
index 000000000..85168ce78
--- /dev/null
+++ b/devtools/client/shared/components/reps/object-with-text.js
@@ -0,0 +1,76 @@
+/* -*- 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";
+
+// Make this available to both AMD and CJS environments
+define(function (require, exports, module) {
+ // ReactJS
+ const React = require("devtools/client/shared/vendor/react");
+
+ // Reps
+ const { isGrip } = require("./rep-utils");
+
+ // Shortcuts
+ const { span } = React.DOM;
+
+ /**
+ * Renders a grip object with textual data.
+ */
+ let ObjectWithText = React.createClass({
+ displayName: "ObjectWithText",
+
+ propTypes: {
+ object: React.PropTypes.object.isRequired,
+ },
+
+ getTitle: function (grip) {
+ if (this.props.objectLink) {
+ return span({className: "objectBox"},
+ this.props.objectLink({
+ object: grip
+ }, this.getType(grip) + " ")
+ );
+ }
+ return "";
+ },
+
+ getType: function (grip) {
+ return grip.class;
+ },
+
+ getDescription: function (grip) {
+ return "\"" + grip.preview.text + "\"";
+ },
+
+ render: function () {
+ let grip = this.props.object;
+ return (
+ span({className: "objectBox objectBox-" + this.getType(grip)},
+ this.getTitle(grip),
+ span({className: "objectPropValue"},
+ this.getDescription(grip)
+ )
+ )
+ );
+ },
+ });
+
+ // Registration
+
+ function supportsObject(grip, type) {
+ if (!isGrip(grip)) {
+ return false;
+ }
+
+ return (grip.preview && grip.preview.kind == "ObjectWithText");
+ }
+
+ // Exports from this module
+ exports.ObjectWithText = {
+ rep: ObjectWithText,
+ supportsObject: supportsObject
+ };
+});
diff --git a/devtools/client/shared/components/reps/object-with-url.js b/devtools/client/shared/components/reps/object-with-url.js
new file mode 100644
index 000000000..9c4b9a229
--- /dev/null
+++ b/devtools/client/shared/components/reps/object-with-url.js
@@ -0,0 +1,76 @@
+/* -*- 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";
+
+// Make this available to both AMD and CJS environments
+define(function (require, exports, module) {
+ // ReactJS
+ const React = require("devtools/client/shared/vendor/react");
+
+ // Reps
+ const { isGrip, getURLDisplayString } = require("./rep-utils");
+
+ // Shortcuts
+ const { span } = React.DOM;
+
+ /**
+ * Renders a grip object with URL data.
+ */
+ let ObjectWithURL = React.createClass({
+ displayName: "ObjectWithURL",
+
+ propTypes: {
+ object: React.PropTypes.object.isRequired,
+ },
+
+ getTitle: function (grip) {
+ if (this.props.objectLink) {
+ return span({className: "objectBox"},
+ this.props.objectLink({
+ object: grip
+ }, this.getType(grip) + " ")
+ );
+ }
+ return "";
+ },
+
+ getType: function (grip) {
+ return grip.class;
+ },
+
+ getDescription: function (grip) {
+ return getURLDisplayString(grip.preview.url);
+ },
+
+ render: function () {
+ let grip = this.props.object;
+ return (
+ span({className: "objectBox objectBox-" + this.getType(grip)},
+ this.getTitle(grip),
+ span({className: "objectPropValue"},
+ this.getDescription(grip)
+ )
+ )
+ );
+ },
+ });
+
+ // Registration
+
+ function supportsObject(grip, type) {
+ if (!isGrip(grip)) {
+ return false;
+ }
+
+ return (grip.preview && grip.preview.kind == "ObjectWithURL");
+ }
+
+ // Exports from this module
+ exports.ObjectWithURL = {
+ rep: ObjectWithURL,
+ supportsObject: supportsObject
+ };
+});
diff --git a/devtools/client/shared/components/reps/object.js b/devtools/client/shared/components/reps/object.js
new file mode 100644
index 000000000..ffb1d1525
--- /dev/null
+++ b/devtools/client/shared/components/reps/object.js
@@ -0,0 +1,171 @@
+/* -*- 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";
+// Make this available to both AMD and CJS environments
+define(function (require, exports, module) {
+ // Dependencies
+ const React = require("devtools/client/shared/vendor/react");
+ const { createFactories } = require("./rep-utils");
+ const { Caption } = createFactories(require("./caption"));
+ const { PropRep } = createFactories(require("./prop-rep"));
+ // Shortcuts
+ const { span } = React.DOM;
+ /**
+ * Renders an object. An object is represented by a list of its
+ * properties enclosed in curly brackets.
+ */
+ const Obj = React.createClass({
+ displayName: "Obj",
+
+ propTypes: {
+ object: React.PropTypes.object,
+ mode: React.PropTypes.string,
+ },
+
+ getTitle: function (object) {
+ let className = object && object.class ? object.class : "Object";
+ if (this.props.objectLink) {
+ return this.props.objectLink({
+ object: object
+ }, className);
+ }
+ return className;
+ },
+
+ safePropIterator: function (object, max) {
+ max = (typeof max === "undefined") ? 3 : max;
+ try {
+ return this.propIterator(object, max);
+ } catch (err) {
+ console.error(err);
+ }
+ return [];
+ },
+
+ propIterator: function (object, max) {
+ let isInterestingProp = (t, value) => {
+ // Do not pick objects, it could cause recursion.
+ return (t == "boolean" || t == "number" || (t == "string" && value));
+ };
+
+ // Work around https://bugzilla.mozilla.org/show_bug.cgi?id=945377
+ if (Object.prototype.toString.call(object) === "[object Generator]") {
+ object = Object.getPrototypeOf(object);
+ }
+
+ // Object members with non-empty values are preferred since it gives the
+ // user a better overview of the object.
+ let props = this.getProps(object, max, isInterestingProp);
+
+ if (props.length <= max) {
+ // There are not enough props yet (or at least, not enough props to
+ // be able to know whether we should print "more…" or not).
+ // Let's display also empty members and functions.
+ props = props.concat(this.getProps(object, max, (t, value) => {
+ return !isInterestingProp(t, value);
+ }));
+ }
+
+ if (props.length > max) {
+ props.pop();
+ let objectLink = this.props.objectLink || span;
+
+ props.push(Caption({
+ object: objectLink({
+ object: object
+ }, (Object.keys(object).length - max) + " more…")
+ }));
+ } else if (props.length > 0) {
+ // Remove the last comma.
+ props[props.length - 1] = React.cloneElement(
+ props[props.length - 1], { delim: "" });
+ }
+
+ return props;
+ },
+
+ getProps: function (object, max, filter) {
+ let props = [];
+
+ max = max || 3;
+ if (!object) {
+ return props;
+ }
+
+ // Hardcode tiny mode to avoid recursive handling.
+ let mode = "tiny";
+
+ try {
+ for (let name in object) {
+ if (props.length > max) {
+ return props;
+ }
+
+ let value;
+ try {
+ value = object[name];
+ } catch (exc) {
+ continue;
+ }
+
+ let t = typeof value;
+ if (filter(t, value)) {
+ props.push(PropRep({
+ mode: mode,
+ name: name,
+ object: value,
+ equal: ": ",
+ delim: ", ",
+ }));
+ }
+ }
+ } catch (err) {
+ console.error(err);
+ }
+
+ return props;
+ },
+
+ render: function () {
+ let object = this.props.object;
+ let props = this.safePropIterator(object);
+ let objectLink = this.props.objectLink || span;
+
+ if (this.props.mode == "tiny" || !props.length) {
+ return (
+ span({className: "objectBox objectBox-object"},
+ objectLink({className: "objectTitle"}, this.getTitle(object))
+ )
+ );
+ }
+
+ return (
+ span({className: "objectBox objectBox-object"},
+ this.getTitle(object),
+ objectLink({
+ className: "objectLeftBrace",
+ object: object
+ }, " { "),
+ ...props,
+ objectLink({
+ className: "objectRightBrace",
+ object: object
+ }, " }")
+ )
+ );
+ },
+ });
+ function supportsObject(object, type) {
+ return true;
+ }
+
+ // Exports from this module
+ exports.Obj = {
+ rep: Obj,
+ supportsObject: supportsObject
+ };
+});
diff --git a/devtools/client/shared/components/reps/promise.js b/devtools/client/shared/components/reps/promise.js
new file mode 100644
index 000000000..0a903d366
--- /dev/null
+++ b/devtools/client/shared/components/reps/promise.js
@@ -0,0 +1,111 @@
+/* -*- 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";
+
+// Make this available to both AMD and CJS environments
+define(function (require, exports, module) {
+ // ReactJS
+ const React = require("devtools/client/shared/vendor/react");
+ // Dependencies
+ const { createFactories, isGrip } = require("./rep-utils");
+ const { PropRep } = createFactories(require("./prop-rep"));
+ // Shortcuts
+ const { span } = React.DOM;
+
+ /**
+ * Renders a DOM Promise object.
+ */
+ const PromiseRep = React.createClass({
+ displayName: "Promise",
+
+ propTypes: {
+ object: React.PropTypes.object.isRequired,
+ mode: React.PropTypes.string,
+ },
+
+ getTitle: function (object) {
+ const title = object.class;
+ if (this.props.objectLink) {
+ return this.props.objectLink({
+ object: object
+ }, title);
+ }
+ return title;
+ },
+
+ getProps: function (promiseState) {
+ const keys = ["state"];
+ if (Object.keys(promiseState).includes("value")) {
+ keys.push("value");
+ }
+
+ return keys.map((key, i) => {
+ return PropRep(Object.assign({}, this.props, {
+ mode: "tiny",
+ name: `<${key}>`,
+ object: promiseState[key],
+ equal: ": ",
+ delim: i < keys.length - 1 ? ", " : ""
+ }));
+ });
+ },
+
+ render: function () {
+ const object = this.props.object;
+ const {promiseState} = object;
+ let objectLink = this.props.objectLink || span;
+
+ if (this.props.mode == "tiny") {
+ let { Rep } = createFactories(require("./rep"));
+
+ return (
+ span({className: "objectBox objectBox-object"},
+ this.getTitle(object),
+ objectLink({
+ className: "objectLeftBrace",
+ object: object
+ }, " { "),
+ Rep({object: promiseState.state}),
+ objectLink({
+ className: "objectRightBrace",
+ object: object
+ }, " }")
+ )
+ );
+ }
+
+ const props = this.getProps(promiseState);
+ return (
+ span({className: "objectBox objectBox-object"},
+ this.getTitle(object),
+ objectLink({
+ className: "objectLeftBrace",
+ object: object
+ }, " { "),
+ ...props,
+ objectLink({
+ className: "objectRightBrace",
+ object: object
+ }, " }")
+ )
+ );
+ },
+ });
+
+ // Registration
+ function supportsObject(object, type) {
+ if (!isGrip(object)) {
+ return false;
+ }
+ return type === "Promise";
+ }
+
+ // Exports from this module
+ exports.PromiseRep = {
+ rep: PromiseRep,
+ supportsObject: supportsObject
+ };
+});
diff --git a/devtools/client/shared/components/reps/prop-rep.js b/devtools/client/shared/components/reps/prop-rep.js
new file mode 100644
index 000000000..775dfea2b
--- /dev/null
+++ b/devtools/client/shared/components/reps/prop-rep.js
@@ -0,0 +1,70 @@
+/* -*- 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";
+
+// Make this available to both AMD and CJS environments
+define(function (require, exports, module) {
+ const React = require("devtools/client/shared/vendor/react");
+ const { createFactories } = require("./rep-utils");
+ const { span } = React.DOM;
+
+ /**
+ * Property for Obj (local JS objects), Grip (remote JS objects)
+ * and GripMap (remote JS maps and weakmaps) reps.
+ * It's used to render object properties.
+ */
+ let PropRep = React.createFactory(React.createClass({
+ displayName: "PropRep",
+
+ propTypes: {
+ // Property name.
+ name: React.PropTypes.oneOfType([
+ React.PropTypes.string,
+ React.PropTypes.object,
+ ]).isRequired,
+ // Equal character rendered between property name and value.
+ equal: React.PropTypes.string,
+ // Delimiter character used to separate individual properties.
+ delim: React.PropTypes.string,
+ mode: React.PropTypes.string,
+ },
+
+ render: function () {
+ const { Grip } = require("./grip");
+ let { Rep } = createFactories(require("./rep"));
+
+ let key;
+ // The key can be a simple string, for plain objects,
+ // or another object for maps and weakmaps.
+ if (typeof this.props.name === "string") {
+ key = span({"className": "nodeName"}, this.props.name);
+ } else {
+ key = Rep({
+ object: this.props.name,
+ mode: this.props.mode || "tiny",
+ defaultRep: Grip,
+ objectLink: this.props.objectLink,
+ });
+ }
+
+ return (
+ span({},
+ key,
+ span({
+ "className": "objectEqual"
+ }, this.props.equal),
+ Rep(this.props),
+ span({
+ "className": "objectComma"
+ }, this.props.delim)
+ )
+ );
+ }
+ }));
+
+ // Exports from this module
+ exports.PropRep = PropRep;
+});
diff --git a/devtools/client/shared/components/reps/regexp.js b/devtools/client/shared/components/reps/regexp.js
new file mode 100644
index 000000000..2f9212658
--- /dev/null
+++ b/devtools/client/shared/components/reps/regexp.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";
+
+// Make this available to both AMD and CJS environments
+define(function (require, exports, module) {
+ // ReactJS
+ const React = require("devtools/client/shared/vendor/react");
+
+ // Reps
+ const { isGrip } = require("./rep-utils");
+
+ // Shortcuts
+ const { span } = React.DOM;
+
+ /**
+ * Renders a grip object with regular expression.
+ */
+ let RegExp = React.createClass({
+ displayName: "regexp",
+
+ propTypes: {
+ object: React.PropTypes.object.isRequired,
+ },
+
+ getSource: function (grip) {
+ return grip.displayString;
+ },
+
+ render: function () {
+ let grip = this.props.object;
+ let objectLink = this.props.objectLink || span;
+
+ return (
+ span({className: "objectBox objectBox-regexp"},
+ objectLink({
+ object: grip,
+ className: "regexpSource"
+ }, this.getSource(grip))
+ )
+ );
+ },
+ });
+
+ // Registration
+
+ function supportsObject(object, type) {
+ if (!isGrip(object)) {
+ return false;
+ }
+
+ return (type == "RegExp");
+ }
+
+ // Exports from this module
+ exports.RegExp = {
+ rep: RegExp,
+ supportsObject: supportsObject
+ };
+});
diff --git a/devtools/client/shared/components/reps/rep-utils.js b/devtools/client/shared/components/reps/rep-utils.js
new file mode 100644
index 000000000..d9580ac8d
--- /dev/null
+++ b/devtools/client/shared/components/reps/rep-utils.js
@@ -0,0 +1,160 @@
+/* globals URLSearchParams */
+/* -*- 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";
+
+// Make this available to both AMD and CJS environments
+define(function (require, exports, module) {
+ // Dependencies
+ const React = require("devtools/client/shared/vendor/react");
+
+ /**
+ * Create React factories for given arguments.
+ * Example:
+ * const { Rep } = createFactories(require("./rep"));
+ */
+ function createFactories(args) {
+ let result = {};
+ for (let p in args) {
+ result[p] = React.createFactory(args[p]);
+ }
+ return result;
+ }
+
+ /**
+ * Returns true if the given object is a grip (see RDP protocol)
+ */
+ function isGrip(object) {
+ return object && object.actor;
+ }
+
+ function escapeNewLines(value) {
+ return value.replace(/\r/gm, "\\r").replace(/\n/gm, "\\n");
+ }
+
+ function cropMultipleLines(text, limit) {
+ return escapeNewLines(cropString(text, limit));
+ }
+
+ function cropString(text, limit, alternativeText) {
+ if (!alternativeText) {
+ alternativeText = "\u2026";
+ }
+
+ // Make sure it's a string and sanitize it.
+ text = sanitizeString(text + "");
+
+ // Crop the string only if a limit is actually specified.
+ if (!limit || limit <= 0) {
+ return text;
+ }
+
+ // Set the limit at least to the length of the alternative text
+ // plus one character of the original text.
+ if (limit <= alternativeText.length) {
+ limit = alternativeText.length + 1;
+ }
+
+ let halfLimit = (limit - alternativeText.length) / 2;
+
+ if (text.length > limit) {
+ return text.substr(0, Math.ceil(halfLimit)) + alternativeText +
+ text.substr(text.length - Math.floor(halfLimit));
+ }
+
+ return text;
+ }
+
+ function sanitizeString(text) {
+ // Replace all non-printable characters, except of
+ // (horizontal) tab (HT: \x09) and newline (LF: \x0A, CR: \x0D),
+ // with unicode replacement character (u+fffd).
+ // eslint-disable-next-line no-control-regex
+ let re = new RegExp("[\x00-\x08\x0B\x0C\x0E-\x1F\x80-\x9F]", "g");
+ return text.replace(re, "\ufffd");
+ }
+
+ function parseURLParams(url) {
+ url = new URL(url);
+ return parseURLEncodedText(url.searchParams);
+ }
+
+ function parseURLEncodedText(text) {
+ let params = [];
+
+ // In case the text is empty just return the empty parameters
+ if (text == "") {
+ return params;
+ }
+
+ let searchParams = new URLSearchParams(text);
+ let entries = [...searchParams.entries()];
+ return entries.map(entry => {
+ return {
+ name: entry[0],
+ value: entry[1]
+ };
+ });
+ }
+
+ function getFileName(url) {
+ let split = splitURLBase(url);
+ return split.name;
+ }
+
+ function splitURLBase(url) {
+ if (!isDataURL(url)) {
+ return splitURLTrue(url);
+ }
+ return {};
+ }
+
+ function getURLDisplayString(url) {
+ return cropString(url);
+ }
+
+ function isDataURL(url) {
+ return (url && url.substr(0, 5) == "data:");
+ }
+
+ function splitURLTrue(url) {
+ const reSplitFile = /(.*?):\/{2,3}([^\/]*)(.*?)([^\/]*?)($|\?.*)/;
+ let m = reSplitFile.exec(url);
+
+ if (!m) {
+ return {
+ name: url,
+ path: url
+ };
+ } else if (m[4] == "" && m[5] == "") {
+ return {
+ protocol: m[1],
+ domain: m[2],
+ path: m[3],
+ name: m[3] != "/" ? m[3] : m[2]
+ };
+ }
+
+ return {
+ protocol: m[1],
+ domain: m[2],
+ path: m[2] + m[3],
+ name: m[4] + m[5]
+ };
+ }
+
+ // Exports from this module
+ exports.createFactories = createFactories;
+ exports.isGrip = isGrip;
+ exports.cropString = cropString;
+ exports.cropMultipleLines = cropMultipleLines;
+ exports.parseURLParams = parseURLParams;
+ exports.parseURLEncodedText = parseURLEncodedText;
+ exports.getFileName = getFileName;
+ exports.getURLDisplayString = getURLDisplayString;
+ exports.sanitizeString = sanitizeString;
+});
diff --git a/devtools/client/shared/components/reps/rep.js b/devtools/client/shared/components/reps/rep.js
new file mode 100644
index 000000000..0891fe0ce
--- /dev/null
+++ b/devtools/client/shared/components/reps/rep.js
@@ -0,0 +1,144 @@
+/* -*- 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";
+
+// Make this available to both AMD and CJS environments
+define(function (require, exports, module) {
+ // Dependencies
+ const React = require("devtools/client/shared/vendor/react");
+
+ const { isGrip } = require("./rep-utils");
+
+ // Load all existing rep templates
+ const { Undefined } = require("./undefined");
+ const { Null } = require("./null");
+ const { StringRep } = require("./string");
+ const { LongStringRep } = require("./long-string");
+ const { Number } = require("./number");
+ const { ArrayRep } = require("./array");
+ const { Obj } = require("./object");
+ const { SymbolRep } = require("./symbol");
+ const { InfinityRep } = require("./infinity");
+ const { NaNRep } = require("./nan");
+
+ // DOM types (grips)
+ const { Attribute } = require("./attribute");
+ const { DateTime } = require("./date-time");
+ const { Document } = require("./document");
+ const { Event } = require("./event");
+ const { Func } = require("./function");
+ const { PromiseRep } = require("./promise");
+ const { RegExp } = require("./regexp");
+ const { StyleSheet } = require("./stylesheet");
+ const { CommentNode } = require("./comment-node");
+ const { ElementNode } = require("./element-node");
+ const { TextNode } = require("./text-node");
+ const { Window } = require("./window");
+ const { ObjectWithText } = require("./object-with-text");
+ const { ObjectWithURL } = require("./object-with-url");
+ const { GripArray } = require("./grip-array");
+ const { GripMap } = require("./grip-map");
+ const { Grip } = require("./grip");
+
+ // List of all registered template.
+ // XXX there should be a way for extensions to register a new
+ // or modify an existing rep.
+ let reps = [
+ RegExp,
+ StyleSheet,
+ Event,
+ DateTime,
+ CommentNode,
+ ElementNode,
+ TextNode,
+ Attribute,
+ LongStringRep,
+ Func,
+ PromiseRep,
+ ArrayRep,
+ Document,
+ Window,
+ ObjectWithText,
+ ObjectWithURL,
+ GripArray,
+ GripMap,
+ Grip,
+ Undefined,
+ Null,
+ StringRep,
+ Number,
+ SymbolRep,
+ InfinityRep,
+ NaNRep,
+ ];
+
+ /**
+ * Generic rep that is using for rendering native JS types or an object.
+ * The right template used for rendering is picked automatically according
+ * to the current value type. The value must be passed is as 'object'
+ * property.
+ */
+ const Rep = React.createClass({
+ displayName: "Rep",
+
+ propTypes: {
+ object: React.PropTypes.any,
+ defaultRep: React.PropTypes.object,
+ mode: React.PropTypes.string
+ },
+
+ render: function () {
+ let rep = getRep(this.props.object, this.props.defaultRep);
+ return rep(this.props);
+ },
+ });
+
+ // Helpers
+
+ /**
+ * Return a rep object that is responsible for rendering given
+ * object.
+ *
+ * @param object {Object} Object to be rendered in the UI. This
+ * can be generic JS object as well as a grip (handle to a remote
+ * debuggee object).
+ *
+ * @param defaultObject {React.Component} The default template
+ * that should be used to render given object if none is found.
+ */
+ function getRep(object, defaultRep = Obj) {
+ let type = typeof object;
+ if (type == "object" && object instanceof String) {
+ type = "string";
+ } else if (object && type == "object" && object.type) {
+ type = object.type;
+ }
+
+ if (isGrip(object)) {
+ type = object.class;
+ }
+
+ for (let i = 0; i < reps.length; i++) {
+ let rep = reps[i];
+ try {
+ // supportsObject could return weight (not only true/false
+ // but a number), which would allow to priorities templates and
+ // support better extensibility.
+ if (rep.supportsObject(object, type)) {
+ return React.createFactory(rep.rep);
+ }
+ } catch (err) {
+ console.error(err);
+ }
+ }
+
+ return React.createFactory(defaultRep.rep);
+ }
+
+ // Exports from this module
+ exports.Rep = Rep;
+});
diff --git a/devtools/client/shared/components/reps/reps.css b/devtools/client/shared/components/reps/reps.css
new file mode 100644
index 000000000..61e5e3dac
--- /dev/null
+++ b/devtools/client/shared/components/reps/reps.css
@@ -0,0 +1,174 @@
+/* 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/. */
+
+.theme-dark,
+.theme-light {
+ --number-color: var(--theme-highlight-green);
+ --string-color: var(--theme-highlight-orange);
+ --null-color: var(--theme-comment);
+ --object-color: var(--theme-body-color);
+ --caption-color: var(--theme-highlight-blue);
+ --location-color: var(--theme-content-color1);
+ --source-link-color: var(--theme-highlight-blue);
+ --node-color: var(--theme-highlight-bluegrey);
+ --reference-color: var(--theme-highlight-purple);
+}
+
+.theme-firebug {
+ --number-color: #000088;
+ --string-color: #FF0000;
+ --null-color: #787878;
+ --object-color: DarkGreen;
+ --caption-color: #444444;
+ --location-color: #555555;
+ --source-link-color: blue;
+ --node-color: rgb(0, 0, 136);
+ --reference-color: rgb(102, 102, 255);
+}
+
+/******************************************************************************/
+
+.objectLink:hover {
+ cursor: pointer;
+ text-decoration: underline;
+}
+
+.inline {
+ display: inline;
+ white-space: normal;
+}
+
+.objectBox-object {
+ font-weight: bold;
+ color: var(--object-color);
+ white-space: pre-wrap;
+}
+
+.objectBox-string,
+.objectBox-symbol,
+.objectBox-text,
+.objectLink-textNode,
+.objectBox-table {
+ white-space: pre-wrap;
+}
+
+.objectBox-number,
+.objectLink-styleRule,
+.objectLink-element,
+.objectLink-textNode,
+.objectBox-array > .length {
+ color: var(--number-color);
+}
+
+.objectBox-textNode,
+.objectBox-string,
+.objectBox-symbol {
+ color: var(--string-color);
+}
+
+.objectLink-function,
+.objectBox-stackTrace,
+.objectLink-profile {
+ color: var(--object-color);
+}
+
+.objectLink-Location {
+ font-style: italic;
+ color: var(--location-color);
+}
+
+.objectBox-null,
+.objectBox-undefined,
+.objectBox-hint,
+.logRowHint {
+ font-style: italic;
+ color: var(--null-color);
+}
+
+.objectLink-sourceLink {
+ position: absolute;
+ right: 4px;
+ top: 2px;
+ padding-left: 8px;
+ font-weight: bold;
+ color: var(--source-link-color);
+}
+
+/******************************************************************************/
+
+.objectLink-event,
+.objectLink-eventLog,
+.objectLink-regexp,
+.objectLink-object,
+.objectLink-Date {
+ font-weight: bold;
+ color: var(--object-color);
+ white-space: pre-wrap;
+}
+
+/******************************************************************************/
+
+.objectLink-object .nodeName,
+.objectLink-NamedNodeMap .nodeName,
+.objectLink-NamedNodeMap .objectEqual,
+.objectLink-NamedNodeMap .arrayLeftBracket,
+.objectLink-NamedNodeMap .arrayRightBracket,
+.objectLink-Attr .attrEqual,
+.objectLink-Attr .attrTitle {
+ color: var(--node-color);
+}
+
+.objectLink-object .nodeName {
+ font-weight: normal;
+}
+
+/******************************************************************************/
+
+.objectLeftBrace,
+.objectRightBrace,
+.arrayLeftBracket,
+.arrayRightBracket {
+ cursor: pointer;
+ font-weight: bold;
+}
+
+/******************************************************************************/
+/* Cycle reference*/
+
+.objectLink-Reference {
+ font-weight: bold;
+ color: var(--reference-color);
+}
+
+.objectBox-array > .objectTitle {
+ font-weight: bold;
+ color: var(--object-color);
+}
+
+.caption {
+ font-weight: bold;
+ color: var(--caption-color);
+}
+
+/******************************************************************************/
+/* Themes */
+
+.theme-dark .objectBox-null,
+.theme-dark .objectBox-undefined,
+.theme-light .objectBox-null,
+.theme-light .objectBox-undefined {
+ font-style: normal;
+}
+
+.theme-dark .objectBox-object,
+.theme-light .objectBox-object {
+ font-weight: normal;
+ white-space: pre-wrap;
+}
+
+.theme-dark .caption,
+.theme-light .caption {
+ font-weight: normal;
+}
diff --git a/devtools/client/shared/components/reps/string.js b/devtools/client/shared/components/reps/string.js
new file mode 100644
index 000000000..f8b4b1986
--- /dev/null
+++ b/devtools/client/shared/components/reps/string.js
@@ -0,0 +1,69 @@
+/* -*- 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";
+
+// Make this available to both AMD and CJS environments
+define(function (require, exports, module) {
+ // Dependencies
+ const React = require("devtools/client/shared/vendor/react");
+ const { cropString } = require("./rep-utils");
+
+ // Shortcuts
+ const { span } = React.DOM;
+
+ /**
+ * Renders a string. String value is enclosed within quotes.
+ */
+ const StringRep = React.createClass({
+ displayName: "StringRep",
+
+ propTypes: {
+ useQuotes: React.PropTypes.bool,
+ style: React.PropTypes.object,
+ },
+
+ getDefaultProps: function () {
+ return {
+ useQuotes: true,
+ };
+ },
+
+ render: function () {
+ let text = this.props.object;
+ let member = this.props.member;
+ let style = this.props.style;
+
+ let config = {className: "objectBox objectBox-string"};
+ if (style) {
+ config.style = style;
+ }
+
+ if (member && member.open) {
+ return span(config, "\"" + text + "\"");
+ }
+
+ let croppedString = this.props.cropLimit ?
+ cropString(text, this.props.cropLimit) : cropString(text);
+
+ let formattedString = this.props.useQuotes ?
+ "\"" + croppedString + "\"" : croppedString;
+
+ return span(config, formattedString);
+ },
+ });
+
+ function supportsObject(object, type) {
+ return (type == "string");
+ }
+
+ // Exports from this module
+
+ exports.StringRep = {
+ rep: StringRep,
+ supportsObject: supportsObject,
+ };
+});
diff --git a/devtools/client/shared/components/reps/stylesheet.js b/devtools/client/shared/components/reps/stylesheet.js
new file mode 100644
index 000000000..c1fc7f1be
--- /dev/null
+++ b/devtools/client/shared/components/reps/stylesheet.js
@@ -0,0 +1,77 @@
+/* -*- 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";
+
+// Make this available to both AMD and CJS environments
+define(function (require, exports, module) {
+ // ReactJS
+ const React = require("devtools/client/shared/vendor/react");
+
+ // Reps
+ const { isGrip, getURLDisplayString } = require("./rep-utils");
+
+ // Shortcuts
+ const DOM = React.DOM;
+
+ /**
+ * Renders a grip representing CSSStyleSheet
+ */
+ let StyleSheet = React.createClass({
+ displayName: "object",
+
+ propTypes: {
+ object: React.PropTypes.object.isRequired,
+ },
+
+ getTitle: function (grip) {
+ let title = "StyleSheet ";
+ if (this.props.objectLink) {
+ return DOM.span({className: "objectBox"},
+ this.props.objectLink({
+ object: grip
+ }, title)
+ );
+ }
+ return title;
+ },
+
+ getLocation: function (grip) {
+ // Embedded stylesheets don't have URL and so, no preview.
+ let url = grip.preview ? grip.preview.url : "";
+ return url ? getURLDisplayString(url) : "";
+ },
+
+ render: function () {
+ let grip = this.props.object;
+
+ return (
+ DOM.span({className: "objectBox objectBox-object"},
+ this.getTitle(grip),
+ DOM.span({className: "objectPropValue"},
+ this.getLocation(grip)
+ )
+ )
+ );
+ },
+ });
+
+ // Registration
+
+ function supportsObject(object, type) {
+ if (!isGrip(object)) {
+ return false;
+ }
+
+ return (type == "CSSStyleSheet");
+ }
+
+ // Exports from this module
+
+ exports.StyleSheet = {
+ rep: StyleSheet,
+ supportsObject: supportsObject
+ };
+});
diff --git a/devtools/client/shared/components/reps/symbol.js b/devtools/client/shared/components/reps/symbol.js
new file mode 100644
index 000000000..111794008
--- /dev/null
+++ b/devtools/client/shared/components/reps/symbol.js
@@ -0,0 +1,48 @@
+/* -*- 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";
+
+// Make this available to both AMD and CJS environments
+define(function (require, exports, module) {
+ // Dependencies
+ const React = require("devtools/client/shared/vendor/react");
+
+ // Shortcuts
+ const { span } = React.DOM;
+
+ /**
+ * Renders a symbol.
+ */
+ const SymbolRep = React.createClass({
+ displayName: "SymbolRep",
+
+ propTypes: {
+ object: React.PropTypes.object.isRequired
+ },
+
+ render: function () {
+ let {object} = this.props;
+ let {name} = object;
+
+ return (
+ span({className: "objectBox objectBox-symbol"},
+ `Symbol(${name || ""})`
+ )
+ );
+ },
+ });
+
+ function supportsObject(object, type) {
+ return (type == "symbol");
+ }
+
+ // Exports from this module
+ exports.SymbolRep = {
+ rep: SymbolRep,
+ supportsObject: supportsObject,
+ };
+});
diff --git a/devtools/client/shared/components/reps/text-node.js b/devtools/client/shared/components/reps/text-node.js
new file mode 100644
index 000000000..d80545cea
--- /dev/null
+++ b/devtools/client/shared/components/reps/text-node.js
@@ -0,0 +1,94 @@
+/* -*- 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";
+
+// Make this available to both AMD and CJS environments
+define(function (require, exports, module) {
+ // ReactJS
+ const React = require("devtools/client/shared/vendor/react");
+
+ // Reps
+ const { isGrip, cropString } = require("./rep-utils");
+
+ // Shortcuts
+ const DOM = React.DOM;
+
+ /**
+ * Renders DOM #text node.
+ */
+ let TextNode = React.createClass({
+ displayName: "TextNode",
+
+ propTypes: {
+ object: React.PropTypes.object.isRequired,
+ mode: React.PropTypes.string,
+ },
+
+ getTextContent: function (grip) {
+ return cropString(grip.preview.textContent);
+ },
+
+ getTitle: function (grip) {
+ if (this.props.objectLink) {
+ return this.props.objectLink({
+ object: grip
+ }, "#text ");
+ }
+ return "";
+ },
+
+ render: function () {
+ let grip = this.props.object;
+ let mode = this.props.mode || "short";
+
+ if (mode == "short" || mode == "tiny") {
+ return (
+ DOM.span({className: "objectBox objectBox-textNode"},
+ this.getTitle(grip),
+ DOM.span({className: "nodeValue"},
+ "\"" + this.getTextContent(grip) + "\""
+ )
+ )
+ );
+ }
+
+ let objectLink = this.props.objectLink || DOM.span;
+ return (
+ DOM.span({className: "objectBox objectBox-textNode"},
+ this.getTitle(grip),
+ objectLink({
+ object: grip
+ }, "<"),
+ DOM.span({className: "nodeTag"}, "TextNode"),
+ " textContent=\"",
+ DOM.span({className: "nodeValue"},
+ this.getTextContent(grip)
+ ),
+ "\"",
+ objectLink({
+ object: grip
+ }, ">;")
+ )
+ );
+ },
+ });
+
+ // Registration
+
+ function supportsObject(grip, type) {
+ if (!isGrip(grip)) {
+ return false;
+ }
+
+ return (grip.preview && grip.class == "Text");
+ }
+
+ // Exports from this module
+ exports.TextNode = {
+ rep: TextNode,
+ supportsObject: supportsObject
+ };
+});
diff --git a/devtools/client/shared/components/reps/undefined.js b/devtools/client/shared/components/reps/undefined.js
new file mode 100644
index 000000000..c4e64a12c
--- /dev/null
+++ b/devtools/client/shared/components/reps/undefined.js
@@ -0,0 +1,46 @@
+/* -*- 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";
+
+// Make this available to both AMD and CJS environments
+define(function (require, exports, module) {
+ // Dependencies
+ const React = require("devtools/client/shared/vendor/react");
+
+ // Shortcuts
+ const { span } = React.DOM;
+
+ /**
+ * Renders undefined value
+ */
+ const Undefined = React.createClass({
+ displayName: "UndefinedRep",
+
+ render: function () {
+ return (
+ span({className: "objectBox objectBox-undefined"},
+ "undefined"
+ )
+ );
+ },
+ });
+
+ function supportsObject(object, type) {
+ if (object && object.type && object.type == "undefined") {
+ return true;
+ }
+
+ return (type == "undefined");
+ }
+
+ // Exports from this module
+
+ exports.Undefined = {
+ rep: Undefined,
+ supportsObject: supportsObject
+ };
+});
diff --git a/devtools/client/shared/components/reps/window.js b/devtools/client/shared/components/reps/window.js
new file mode 100644
index 000000000..628d69562
--- /dev/null
+++ b/devtools/client/shared/components/reps/window.js
@@ -0,0 +1,73 @@
+/* -*- 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";
+
+// Make this available to both AMD and CJS environments
+define(function (require, exports, module) {
+ // ReactJS
+ const React = require("devtools/client/shared/vendor/react");
+
+ // Reps
+ const { isGrip, getURLDisplayString } = require("./rep-utils");
+
+ // Shortcuts
+ const DOM = React.DOM;
+
+ /**
+ * Renders a grip representing a window.
+ */
+ let Window = React.createClass({
+ displayName: "Window",
+
+ propTypes: {
+ object: React.PropTypes.object.isRequired,
+ },
+
+ getTitle: function (grip) {
+ if (this.props.objectLink) {
+ return DOM.span({className: "objectBox"},
+ this.props.objectLink({
+ object: grip
+ }, grip.class + " ")
+ );
+ }
+ return "";
+ },
+
+ getLocation: function (grip) {
+ return getURLDisplayString(grip.preview.url);
+ },
+
+ render: function () {
+ let grip = this.props.object;
+
+ return (
+ DOM.span({className: "objectBox objectBox-Window"},
+ this.getTitle(grip),
+ DOM.span({className: "objectPropValue"},
+ this.getLocation(grip)
+ )
+ )
+ );
+ },
+ });
+
+ // Registration
+
+ function supportsObject(object, type) {
+ if (!isGrip(object)) {
+ return false;
+ }
+
+ return (object.preview && type == "Window");
+ }
+
+ // Exports from this module
+ exports.Window = {
+ rep: Window,
+ supportsObject: supportsObject
+ };
+});