Bug 1201475 - Implement DOM Panel; r=linclark, bgrins, helenvholmes

MozReview-Commit-ID: IMYSW7ObOSh
This commit is contained in:
Jan Odvarko 2016-02-09 23:23:29 +01:00
parent e3e7f277be
commit 5493538baa
55 changed files with 2867 additions and 41 deletions

View File

@ -23,6 +23,7 @@ loader.lazyGetter(this, "PerformancePanel", () => require("devtools/client/perfo
loader.lazyGetter(this, "NetMonitorPanel", () => require("devtools/client/netmonitor/panel").NetMonitorPanel);
loader.lazyGetter(this, "StoragePanel", () => require("devtools/client/storage/panel").StoragePanel);
loader.lazyGetter(this, "ScratchpadPanel", () => require("devtools/client/scratchpad/scratchpad-panel").ScratchpadPanel);
loader.lazyGetter(this, "DomPanel", () => require("devtools/client/dom/dom-panel").DomPanel);
// Strings
const toolboxProps = "chrome://devtools/locale/toolbox.properties";
@ -38,6 +39,7 @@ const netMonitorProps = "chrome://devtools/locale/netmonitor.properties";
const storageProps = "chrome://devtools/locale/storage.properties";
const scratchpadProps = "chrome://devtools/locale/scratchpad.properties";
const memoryProps = "chrome://devtools/locale/memory.properties";
const domProps = "chrome://devtools/locale/dom.properties";
loader.lazyGetter(this, "toolboxStrings", () => Services.strings.createBundle(toolboxProps));
loader.lazyGetter(this, "performanceStrings", () => Services.strings.createBundle(performanceProps));
@ -52,6 +54,7 @@ loader.lazyGetter(this, "netMonitorStrings", () => Services.strings.createBundle
loader.lazyGetter(this, "storageStrings", () => Services.strings.createBundle(storageProps));
loader.lazyGetter(this, "scratchpadStrings", () => Services.strings.createBundle(scratchpadProps));
loader.lazyGetter(this, "memoryStrings", () => Services.strings.createBundle(memoryProps));
loader.lazyGetter(this, "domStrings", () => Services.strings.createBundle(domProps));
var Tools = {};
exports.Tools = Tools;
@ -397,6 +400,33 @@ Tools.scratchpad = {
}
};
Tools.dom = {
id: "dom",
accesskey: l10n("dom.accesskey", domStrings),
key: l10n("dom.commandkey", domStrings),
ordinal: 13,
modifiers: osString == "Darwin" ? "accel,alt" : "accel,shift",
visibilityswitch: "devtools.dom.enabled",
icon: "chrome://devtools/skin/images/tool-dom.svg",
invertIconForLightTheme: true,
url: "chrome://devtools/content/dom/dom.html",
label: l10n("dom.label", domStrings),
panelLabel: l10n("dom.panelLabel", domStrings),
get tooltip() {
return l10n("dom.tooltip", domStrings,
(osString == "Darwin" ? "Cmd+Opt+" : "Ctrl+Shift+") + this.key);
},
inMenu: true,
isTargetSupported: function(target) {
return target.getTrait("webConsoleCommands");
},
build: function(iframeWindow, toolbox) {
return new DomPanel(iframeWindow, toolbox);
}
};
var defaultTools = [
Tools.options,
Tools.webConsole,
@ -411,6 +441,7 @@ var defaultTools = [
Tools.storage,
Tools.scratchpad,
Tools.memory,
Tools.dom,
];
exports.defaultTools = defaultTools;

View File

@ -0,0 +1,15 @@
{
"globals": {
"XMLHttpRequest": true,
"window": true,
"define": true,
"addEventListener": true,
"document": true,
"dispatchEvent": true,
"MessageEvent": true
},
"rules": {
"indent": 0,
"padded-blocks": 0,
}
}

View File

@ -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;

View File

@ -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;

View File

@ -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',
)

View File

@ -0,0 +1,90 @@
/* -*- 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({
propTypes: {
object: PropTypes.any,
filter: PropTypes.string,
dispatch: PropTypes.func.isRequired,
grips: PropTypes.object,
},
displayName: "DomTree",
/**
* 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,
}));
};
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);

View File

@ -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";
// 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({
propTypes: {
object: PropTypes.any,
filter: PropTypes.string,
dispatch: PropTypes.func.isRequired,
},
displayName: "MainFrame",
/**
* Render DOM panel content
*/
render: function() {
return (
div({className: "mainFrame"},
MainToolbar({
dispatch: this.props.dispatch
}),
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);

View File

@ -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
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("../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({
propTypes: {
object: PropTypes.any,
dispatch: PropTypes.func.isRequired,
},
displayName: "MainToolbar",
onRefresh: function() {
this.props.dispatch(fetchProperties(this.props.object));
},
onSearch: function(value) {
this.props.dispatch(setVisibilityFilter(value));
},
render: function() {
return (
Toolbar({},
ToolbarButton({
className: "btn copy",
onClick: this.onRefresh},
l10n.getStr("dom.refresh")
),
SearchBox({
onSearch: this.onSearch
})
)
);
}
});
// Exports from this module
module.exports = MainToolbar;

View File

@ -0,0 +1,13 @@
# 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',
'search-box.css',
'search-box.js',
'search.svg',
)

View File

@ -0,0 +1,46 @@
/* 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/. */
/******************************************************************************/
/* Search Box */
.searchBox {
height: 18px;
font-size: 12px;
margin-top: 0;
border: 1px solid rgb(170, 188, 207);
width: 200px;
float: right;
background-image: url("./search.svg");
background-repeat: no-repeat;
background-position: 2px center;
padding-left: 20px;
margin-right: 5px;
}
/******************************************************************************/
/* Light Theme & Dark Theme*/
.theme-dark .searchBox,
.theme-light .searchBox {
border: 1px solid rgb(170, 170, 170);
background-image: url("chrome://devtools/skin/images/magnifying-glass-light.png");
background-position: 8px center;
border-radius: 2px;
padding-left: 25px;
margin-top: 1px;
height: 16px;
font-style: italic;
}
/******************************************************************************/
/* Dark Theme */
.theme-dark .searchBox {
background-color: rgba(24, 29, 32, 1);
color: rgba(184, 200, 217, 1);
border-color: var(--theme-splitter-color);
background-image: url("chrome://devtools/skin/images/magnifying-glass.png");
}

View File

@ -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";
const React = require("devtools/client/shared/vendor/react");
const { l10n } = require("../utils");
// For smooth incremental searching (in case the user is typing quickly).
const searchDelay = 250;
// Shortcuts
const { input } = React.DOM;
const PropTypes = React.PropTypes;
/**
* This object represents a search box located at the
* top right corner of the application.
*/
var SearchBox = React.createClass({
propTypes: {
onSearch: PropTypes.func,
},
displayName: "SearchBox",
componentWillUnmount: function() {
// Clean up an existing timeout.
if (this.searchTimeout) {
window.clearTimeout(this.searchTimeout);
}
},
onSearch: function(event) {
let searchBox = event.target;
// Clean up an existing timeout before creating a new one.
if (this.searchTimeout) {
window.clearTimeout(this.searchTimeout);
}
// Execute the search after a timeout. It makes the UX
// smoother if the user is typing quickly.
this.searchTimeout = window.setTimeout(() => {
this.searchTimeout = null;
this.props.onSearch(searchBox.value);
}, searchDelay);
},
render: function() {
return (
input({
className: "searchBox",
placeholder: l10n.getStr("dom.filterDOMPanel"),
onChange: this.onSearch
})
);
}
});
// Exports from this module
module.exports = SearchBox;

View File

@ -0,0 +1,22 @@
<!-- 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/. -->
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<linearGradient id="a">
<stop offset="0" stop-color="#427dc2"/>
<stop offset="1" stop-color="#5e9fce"/>
</linearGradient>
<linearGradient id="b">
<stop offset="0" stop-color="#2f5d93"/>
<stop offset="1" stop-color="#3a87bd"/>
</linearGradient>
<filter id="c" width="1.239" height="1.241" x="-.12" y="-.12" color-interpolation-filters="sRGB">
<feGaussianBlur stdDeviation=".637"/>
</filter>
<linearGradient id="d" x1="4.094" x2="4.094" y1="13.423" y2="2.743" xlink:href="#a" gradientUnits="userSpaceOnUse"/>
<linearGradient id="e" x1="8.711" x2="8.711" y1="13.58" y2="2.566" xlink:href="#b" gradientUnits="userSpaceOnUse"/>
</defs>
<path fill="#fff" stroke="#fff" stroke-width="1.5" d="M10.14 1.656c-2.35 0-4.25 1.9-4.25 4.25 0 .752.19 1.45.532 2.063L1.61 12.78l1.562 1.564 4.78-4.78c.64.384 1.387.592 2.19.592 2.35 0 4.25-1.9 4.25-4.25s-1.9-4.25-4.25-4.25zm0 1.532c1.504 0 2.72 1.214 2.72 2.718s-1.216 2.72-2.72 2.72c-1.503 0-2.718-1.216-2.718-2.72 0-1.504 1.215-2.718 2.72-2.718z" stroke-linejoin="round" filter="url(#c)"/>
<path fill="url(#d)" stroke="url(#e)" stroke-width=".6" d="M10 2C7.79 2 6 3.79 6 6c0 .828.256 1.612.688 2.25l-4.875 4.875 1.062 1.063L7.75 9.31C8.388 9.745 9.172 10 10 10c2.21 0 4-1.79 4-4s-1.79-4-4-4zm0 1c1.657 0 3 1.343 3 3s-1.343 3-3 3-3-1.343-3-3 1.343-3 3-3z" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -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";

View File

@ -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";
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 custom React template for specified object. The template
* might depend on specified column.
*/
getValueRep: function(value, colId) {
}
};
// Exports from this module
exports.DomDecorator = DomDecorator;

View File

@ -0,0 +1,111 @@
/* 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;
height: 100%;
}
/******************************************************************************/
/* TreeView Customization */
.treeTable {
width: 100%;
}
/* Space for read only properties icon */
.treeTable td.treeValueCell {
padding-left: 16px;
}
/* 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;
}
/* 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;
}
/******************************************************************************/
/* Selection */
.treeTable .treeRow:hover a,
.treeTable .treeRow:hover span {
color: var(--theme-selection-color) !important;
}
/******************************************************************************/
/* Toolbar */
.toolbar {
position: fixed;
width: 100%;
top: 0;
z-index: 2;
}
.treeTable {
z-index: 1;
margin-top: 25px;
}
/******************************************************************************/
/* Theme Dark */
.theme-dark .treeTable > tbody > tr > td {
border-bottom: none;
}
.theme-dark body {
background-color: var(--theme-body-background);
}

View File

@ -0,0 +1,58 @@
/* -*- 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() {
addEventListener("devtools/chrome/message",
this.onMessage.bind(this), true);
}
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: 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.
new DomView();

View File

@ -0,0 +1,99 @@
/* -*- 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);
}
},
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) {
if (object instanceof Property) {
return object.name;
}
},
getKey: function(object) {
if (object instanceof Property) {
return object.key;
}
},
getType: function(object) {
return object.class ? object.class : "";
},
};
// Exports from this module
exports.GripProvider = GripProvider;

View File

@ -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',
)

View File

@ -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;

View File

@ -0,0 +1,110 @@
/* -*- 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;
// Properly deal with getters.
mergeProperties(response);
// Compute list of requested children.
let ownProps = response.ownProperties || response.preview.ownProperties || [];
let props = Object.keys(ownProps).map(key => {
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 Property(name, value, key) {
this.name = name;
this.value = value;
this.key = key;
}
// Exports from this module
exports.grips = grips;
exports.Property = Property;

View File

@ -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,
};

View File

@ -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',
)

View File

@ -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;

View File

@ -0,0 +1,188 @@
/* -*- 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 { Cu } = require("chrome");
const { defer } = require("sdk/core/promise");
const { ObjectClient } = require("devtools/shared/client/main");
const promise = require("promise");
const EventEmitter = require("devtools/shared/event-emitter");
/**
* This object represents DOM panel. It's responsibility is to
* render Document Object Model of the current debugger target.
*/
function DomPanel(iframeWindow, toolbox) {
this.panelWin = iframeWindow;
this._toolbox = toolbox;
this.onTabNavigated = this.onTabNavigated.bind(this);
this.onContentMessage = this.onContentMessage.bind(this);
EventEmitter.decorate(this);
}
DomPanel.prototype = {
/**
* Open is effectively an asynchronous constructor.
*
* @return object
* A promise that is resolved when the DOM panel completes opening.
*/
open: Task.async(function*() {
if (this._opening) {
return this._opening;
}
let deferred = promise.defer();
this._opening = deferred.promise;
// Local monitoring needs to make the target remote.
if (!this.target.isRemote) {
yield this.target.makeRemote();
}
this.initialize();
this.isReady = true;
this.emit("ready");
deferred.resolve(this);
return this._opening;
}),
// Initialization
initialize: function() {
this.panelWin.addEventListener("devtools/content/message",
this.onContentMessage, true);
this.target.on("navigate", this.onTabNavigated);
let provider = {
getPrototypeAndProperties: this.getPrototypeAndProperties.bind(this)
};
exportIntoContentScope(this.panelWin, provider, "DomProvider");
this.doRefresh();
},
destroy: Task.async(function*() {
if (this._destroying) {
return this._destroying;
}
let deferred = promise.defer();
this._destroying = deferred.promise;
this.target.off("navigate", this.onTabNavigated);
this.emit("destroyed");
deferred.resolve();
return this._destroying;
}),
// Events
doRefresh: function() {
this.refresh().then(rootGrip => {
this.postContentMessage("initialize", rootGrip);
});
},
onTabNavigated: function() {
this.doRefresh();
},
getPrototypeAndProperties: function(grip) {
let deferred = defer();
if (!grip.actor) {
console.error("No actor!", grip);
deferred.reject(new Error("Failed to get actor from grip."));
return deferred.promise;
}
if (!this.target) {
console.error("No target!", grip);
deferred.reject(new Error("Failed to get debugger target."));
return deferred.promise;
}
let client = new ObjectClient(this.target.client, grip);
client.getPrototypeAndProperties(deferred.resolve);
return deferred.promise;
},
// Refresh
refresh: function() {
let deferred = defer();
// Attach Console. It might involve RDP communication, so wait
// asynchronously for the result
this.target.activeConsole.evaluateJSAsync("window", res => {
deferred.resolve(res.result);
});
return deferred.promise;
},
// Helpers
postContentMessage: function(type, args) {
let data = {
type: type,
args: args,
};
let event = new this.panelWin.MessageEvent("devtools/chrome/message", {
bubbles: true,
cancelable: true,
data: data,
});
this.panelWin.dispatchEvent(event);
},
onContentMessage: function(event) {
let data = event.data;
let method = data.type;
if (typeof this[method] == "function") {
this[method](data.args);
}
},
get target() {
return this._toolbox.target;
},
};
// Helpers
function exportIntoContentScope(win, obj, defineAs) {
let clone = Cu.createObjectIn(win, {
defineAs: defineAs
});
let props = Object.getOwnPropertyNames(obj);
for (let i = 0; i < props.length; i++) {
let propName = props[i];
let propValue = obj[propName];
if (typeof propValue == "function") {
Cu.exportFunction(propValue, clone, {
defineAs: propName
});
}
}
}
// Exports from this module
exports.DomPanel = DomPanel;

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- 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/. -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<link href="resource://devtools/client/dom/content/dom-view.css" rel="stylesheet" />
<link href="resource://devtools/client/jsonview/css/toolbar.css" rel="stylesheet" />
<link href="resource://devtools/client/shared/components/tree/tree-view.css" rel="stylesheet" />
<link href="resource://devtools/client/dom/content/components/search-box.css" rel="stylesheet" />
<script type="text/javascript;version=1.8"
src="chrome://devtools/content/shared/theme-switching.js"></script>
</head>
<body class="theme-body devtools-monospace" role="application">
<div id="content"></div>
<script type="text/javascript" src="./main.js"></script>
</body>
</html>

View File

@ -0,0 +1,26 @@
/* -*- 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 { utils: Cu } = Components;
const { XPCOMUtils } = Cu.import("resource://gre/modules/XPCOMUtils.jsm", {});
const { BrowserLoader } = Cu.import("resource://devtools/client/shared/browser-loader.js", {});
// Module Loader
const require = BrowserLoader({
baseURI: "resource://devtools/client/dom/",
window: this
}).require;
XPCOMUtils.defineConstant(this, "require", require);
// Localization
const { LocalizationHelper } = require("devtools/client/shared/l10n");
this.l10n = new LocalizationHelper("chrome://devtools/locale/dom.properties");
// Load DOM panel content
require("./content/dom-view.js");

View File

@ -0,0 +1,14 @@
# 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 += [
'content',
]
DevToolsModules(
'dom-panel.js',
'dom.html',
'main.js',
)

View File

@ -0,0 +1,5 @@
[DEFAULT]
tags = devtools
subsuite = devtools
support-files =
head.js

View File

@ -0,0 +1,4 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";

View File

@ -142,6 +142,9 @@ devtools.jar:
content/aboutdebugging/initializer.js (aboutdebugging/initializer.js)
content/responsive.html/index.xhtml (responsive.html/index.xhtml)
content/responsive.html/index.js (responsive.html/index.js)
content/dom/dom.html (dom/dom.html)
content/dom/content/dom-view.css (dom/content/dom-view.css)
content/dom/main.js (dom/main.js)
% skin devtools classic/1.0 %skin/
skin/devtools-browser.css (themes/devtools-browser.css)
skin/common.css (themes/common.css)
@ -196,6 +199,7 @@ devtools.jar:
skin/canvasdebugger.css (themes/canvasdebugger.css)
skin/debugger.css (themes/debugger.css)
skin/netmonitor.css (themes/netmonitor.css)
skin/dom.css (themes/dom.css)
skin/performance.css (themes/performance.css)
skin/memory.css (themes/memory.css)
skin/promisedebugger.css (themes/promisedebugger.css)
@ -279,6 +283,7 @@ devtools.jar:
skin/images/emojis/emoji-tool-scratchpad.svg (themes/images/emojis/emoji-tool-scratchpad.svg)
skin/images/emojis/emoji-tool-webaudio.svg (themes/images/emojis/emoji-tool-webaudio.svg)
skin/images/emojis/emoji-tool-memory.svg (themes/images/emojis/emoji-tool-memory.svg)
skin/images/emojis/emoji-tool-dom.svg (themes/images/emojis/emoji-tool-dom.svg)
skin/images/tool-options.svg (themes/images/tool-options.svg)
skin/images/tool-webconsole.svg (themes/images/tool-webconsole.svg)
skin/images/tool-canvas.svg (themes/images/tool-canvas.svg)
@ -298,6 +303,7 @@ devtools.jar:
skin/images/tool-webaudio.svg (themes/images/tool-webaudio.svg)
skin/images/tool-memory.svg (themes/images/tool-memory.svg)
skin/images/tool-memory-active.svg (themes/images/tool-memory-active.svg)
skin/images/tool-dom.svg (themes/images/tool-dom.svg)
skin/images/close.svg (themes/images/close.svg)
skin/images/clear.svg (themes/images/clear.svg)
skin/images/vview-delete.png (themes/images/vview-delete.png)

View File

@ -0,0 +1,39 @@
# 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/.
# LOCALIZATION NOTE These strings are used inside the DOM panel
# which is available from the Web Developer sub-menu -> 'DOM'.
# The correct localization of this file might be to keep it in
# English, or another language commonly spoken among web developers.
# You want to make that choice consistent across the developer tools.
# A good criteria is the language in which you'd find the best
# documentation on web development on the web.
# LOCALIZATION NOTE (dom.label):
# This string is displayed in the title of the tab when the DOM panel is
# displayed inside the developer tools window and in the Developer Tools Menu.
dom.label=DOM
# LOCALIZATION NOTE (dom.panelLabel):
# This is used as the label for the toolbox panel.
dom.panelLabel=DOM Panel
# LOCALIZATION NOTE (dom.commandkey, dom.accesskey)
# Used for the menuitem in the tool menu
dom.commandkey=W
dom.accesskey=D
# LOCALIZATION NOTE (dom.tooltip):
# This string is displayed in the tooltip of the tab when the DOM is
# displayed inside the developer tools window.
# Keyboard shortcut for DOM panel will be shown inside the brackets.
dom.tooltip=DOM (%S)
# LOCALIZATION NOTE (dom.filterDOMPanel): A placeholder text used for
# DOM panel search box.
dom.filterDOMPanel=Filter DOM Panel
# LOCALIZATION NOTE (dom.refresh): A label for Refresh button in
# DOM panel toolbar
dom.refresh=Refresh

View File

@ -12,6 +12,7 @@ DIRS += [
'canvasdebugger',
'commandline',
'debugger',
'dom',
'eyedropper',
'framework',
'inspector',

View File

@ -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 { ObjectLink } = createFactories(require("./object-link"));
const { StringRep } = require("./string");
// Shortcuts
const { span } = React.DOM;
const { rep: StringRepFactory } = createFactories(StringRep);
/**
* Renders DOM attribute
*/
let Attribute = React.createClass({
propTypes: {
object: React.PropTypes.object.isRequired
},
displayName: "Attr",
getTitle: function(grip) {
return grip.preview.nodeName;
},
render: function() {
let grip = this.props.object;
let value = grip.preview.value;
return (
ObjectLink({className: "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
};
});

View File

@ -0,0 +1,61 @@
/* -*- 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 { ObjectLink } = createFactories(require("./object-link"));
// Shortcuts
const { span } = React.DOM;
/**
* Used to render JS built-in Date() object.
*/
let DateTime = React.createClass({
propTypes: {
object: React.PropTypes.object.isRequired
},
displayName: "Date",
getTitle: function(grip) {
return new Date(grip.preview.timestamp).toString();
},
render: function() {
let grip = this.props.object;
return (
ObjectLink({className: "Date"},
span({className: "objectTitle"},
this.getTitle(grip)
)
)
);
},
});
// 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
};
});

View File

@ -0,0 +1,72 @@
/* -*- 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 { ObjectBox } = createFactories(require("./object-box"));
const { getFileName } = require("./url");
// Shortcuts
const { span } = React.DOM;
/**
* Renders DOM document object.
*/
let Document = React.createClass({
propTypes: {
object: React.PropTypes.object.isRequired
},
displayName: "Document",
getLocation: function(grip) {
let location = grip.preview.location;
return location ? getFileName(location) : "";
},
getTitle: function(win, context) {
return "document";
},
getTooltip: function(doc) {
return doc.location.href;
},
render: function() {
let grip = this.props.object;
return (
ObjectBox({className: "object"},
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
};
});

View File

@ -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) {
// ReactJS
const React = require("devtools/client/shared/vendor/react");
// Reps
const { createFactories, isGrip } = require("./rep-utils");
const { ObjectLink } = createFactories(require("./object-link"));
/**
* Renders DOM event objects.
*/
let Event = React.createClass({
propTypes: {
object: React.PropTypes.object.isRequired
},
displayName: "event",
summarizeEvent: function(grip) {
let info = [grip.preview.type, " "];
let eventFamily = grip.class;
let props = grip.preview.properties;
if (eventFamily == "MouseEvent") {
info.push("clientX=", props.clientX, ", clientY=", props.clientY);
} else if (eventFamily == "KeyboardEvent") {
info.push("charCode=", props.charCode, ", keyCode=", props.keyCode);
} else if (eventFamily == "MessageEvent") {
info.push("origin=", props.origin, ", data=", props.data);
}
return info.join("");
},
render: function() {
let grip = this.props.object;
return (
ObjectLink({className: "event"},
this.summarizeEvent(grip)
)
);
},
});
// 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
};
});

View File

@ -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");
// Reps
const { createFactories, isGrip } = require("./rep-utils");
const { ObjectLink } = createFactories(require("./object-link"));
const { cropString } = require("./string");
/**
* This component represents a template for Function objects.
*/
let Func = React.createClass({
propTypes: {
object: React.PropTypes.object.isRequired
},
displayName: "Func",
summarizeFunction: function(grip) {
let name = grip.displayName || grip.name || "function";
return cropString(name + "()", 100);
},
render: function() {
let grip = this.props.object;
return (
ObjectLink({className: "function"},
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
};
});

View File

@ -0,0 +1,209 @@
/* -*- 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 { ObjectBox } = createFactories(require("./object-box"));
const { Caption } = createFactories(require("./caption"));
// Shortcuts
const { a, 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({
propTypes: {
object: React.PropTypes.object.isRequired,
mode: React.PropTypes.string,
provider: React.PropTypes.object,
},
displayName: "GripArray",
getLength: function(grip) {
return grip.preview ? grip.preview.length : 0;
},
getTitle: function(object, context) {
return "[" + object.length + "]";
},
arrayIterator: function(grip, max) {
let items = [];
if (!grip.preview || !grip.preview.length) {
return items;
}
let array = grip.preview.items;
if (!array) {
return items;
}
let provider = this.props.provider;
if (!provider) {
return items;
}
let delim;
for (let i = 0; i < array.length && i <= max; i++) {
try {
let value = provider.getValue(array[i]);
delim = (i == array.length - 1 ? "" : ", ");
if (value === array) {
items.push(Reference({
key: i,
object: value,
delim: delim}
));
} else {
items.push(GripArrayItem(Object.assign({}, this.props, {
key: i,
object: value,
delim: delim}
)));
}
} catch (exc) {
items.push(GripArrayItem(Object.assign({}, this.props, {
object: exc,
delim: delim,
key: i}
)));
}
}
if (array.length > max + 1) {
items.pop();
items.push(Caption({
key: "more",
object: "more..."}
));
}
return items;
},
hasSpecialProperties: function(array) {
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;
if (mode == "tiny") {
items = span({className: "length"}, this.getLength(object));
} else {
let max = (mode == "short") ? 3 : 300;
items = this.arrayIterator(object, max);
}
return (
ObjectBox({
className: "array",
onClick: this.onToggleProperties},
a({
className: "objectLink",
onclick: this.onClickBracket},
span({
className: "arrayLeftBracket",
role: "presentation"},
"["
)
),
items,
a({
className: "objectLink",
onclick: this.onClickBracket},
span({
className: "arrayRightBracket",
role: "presentation"},
"]"
)
),
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({
propTypes: {
delim: React.PropTypes.string,
},
displayName: "GripArrayItem",
render: function() {
let { Rep } = createFactories(require("./rep"));
return (
span({},
Rep(Object.assign({}, this.props, {
mode: "tiny"
})),
this.props.delim
)
);
}
}));
/**
* Renders cycle references in an array.
*/
let Reference = React.createFactory(React.createClass({
displayName: "Reference",
render: function() {
return (
span({title: "Circular reference"},
"[...]"
)
);
}
}));
function supportsObject(grip, type) {
if (!isGrip(grip)) {
return false;
}
return (grip.preview && grip.preview.kind == "ArrayLike");
}
// Exports from this module
exports.GripArray = {
rep: GripArray,
supportsObject: supportsObject
};
});

View File

@ -0,0 +1,217 @@
/* -*- 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 { ObjectBox } = createFactories(require("./object-box"));
const { Caption } = createFactories(require("./caption"));
// Shortcuts
const { span } = React.DOM;
/**
* @template TODO docs
*/
const Grip = React.createClass({
propTypes: {
object: React.PropTypes.object.isRequired,
mode: React.PropTypes.string,
},
displayName: "Grip",
getTitle: function() {
return "";
},
longPropIterator: function(object) {
try {
return this.propIterator(object, 100);
} catch (err) {
console.error(err);
}
return [];
},
shortPropIterator: function(object) {
try {
return this.propIterator(object, 3);
} catch (err) {
console.error(err);
}
return [];
},
propIterator: function(object, max) {
// Property filter. Show only interesting properties to the user.
let isInterestingProp = (type, value) => {
return (
type == "boolean" ||
type == "number" ||
type == "string" ||
type == "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);
}));
}
// getProps() can return max+1 properties (it can't return more)
// to indicate that there is more props than allowed. Remove the last
// one and append 'more...' postfix in such case.
if (props.length > max) {
props.pop();
props.push(Caption({
key: "more",
object: "more...",
}));
} else if (props.length > 0) {
// Remove the last comma.
// NOTE: do not change comp._store.props directly to update a property,
// it should be re-rendered or cloned with changed props
let last = props.length - 1;
props[last] = React.cloneElement(props[last], {
delim: ""
});
}
return props;
},
getProps: function(object, max, filter) {
let props = [];
max = max || 3;
if (!object) {
return props;
}
try {
let ownProperties = object.preview ? object.preview.ownProperties : [];
for (let name in ownProperties) {
if (props.length > max) {
return props;
}
let prop = ownProperties[name];
let value = prop.value || {};
// Type is specified in grip's "class" field and for primitive
// values use typeof.
let type = (value.class || typeof value);
type = type.toLowerCase();
// Show only interesting properties.
if (filter(type, value)) {
props.push(PropRep(Object.assign({}, this.props, {
key: name,
mode: "tiny",
name: name,
object: value,
equal: ": ",
delim: ", ",
})));
}
}
} catch (err) {
console.error(err);
}
return props;
},
render: function() {
let object = this.props.object;
let props = this.shortPropIterator(object);
if (this.props.mode == "tiny" || !props.length) {
return (
ObjectBox({className: "object"},
span({className: "objectTitle"}, this.getTitle(object)),
span({className: "objectLeftBrace", role: "presentation"}, "{}")
)
);
}
return (
ObjectBox({className: "object"},
span({className: "objectTitle"}, this.getTitle(object)),
span({className: "objectLeftBrace", role: "presentation"}, "{"),
props,
span({className: "objectRightBrace"}, "}")
)
);
},
});
/**
* Property for a grip object.
*/
let PropRep = React.createFactory(React.createClass({
propTypes: {
name: React.PropTypes.string,
equal: React.PropTypes.string,
delim: React.PropTypes.string,
},
displayName: "PropRep",
render: function() {
let { Rep } = createFactories(require("./rep"));
return (
span({},
span({
"className": "nodeName"},
this.props.name),
span({
"className": "objectEqual",
role: "presentation"},
this.props.equal
),
Rep(this.props),
span({
"className": "objectComma",
role: "presentation"},
this.props.delim
)
)
);
}
}));
// Registration
function supportsObject(object, type) {
if (!isGrip(object)) {
return false;
}
return (object.preview && object.preview.ownProperties);
}
// Exports from this module
exports.Grip = {
rep: Grip,
supportsObject: supportsObject
};
});

View File

@ -6,16 +6,30 @@
DevToolsModules(
'array.js',
'attribute.js',
'caption.js',
'date-time.js',
'document.js',
'event.js',
'function.js',
'grip-array.js',
'grip.js',
'named-node-map.js',
'null.js',
'number.js',
'object-box.js',
'object-link.js',
'object-with-text.js',
'object-with-url.js',
'object.js',
'regexp.js',
'rep-utils.js',
'rep.js',
'reps.css',
'string.js',
'stylesheet.js',
'text-node.js',
'undefined.js',
'url.js',
'window.js',
)

View File

@ -0,0 +1,172 @@
/* -*- 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 { ObjectLink } = createFactories(require("./object-link"));
const { Caption } = createFactories(require("./caption"));
// Shortcuts
const { span } = React.DOM;
/**
* Used to render a map of values provided as a grip.
*/
let NamedNodeMap = React.createClass({
propTypes: {
object: React.PropTypes.object.isRequired,
mode: React.PropTypes.string,
provider: React.PropTypes.object,
},
className: "NamedNodeMap",
getLength: function(object) {
return object.preview.length;
},
getTitle: function(object) {
return object.class ? object.class : "";
},
getItems: function(array, max) {
let items = this.propIterator(array, max);
items = items.map(item => PropRep(item));
if (items.length > max + 1) {
items.pop();
items.push(Caption({
key: "more",
object: "more...",
}));
}
return items;
},
propIterator: function(grip, max) {
max = max || 3;
let props = [];
let provider = this.props.provider;
if (!provider) {
return props;
}
let ownProperties = grip.preview ? grip.preview.ownProperties : [];
for (let name in ownProperties) {
if (props.length > max) {
break;
}
let item = ownProperties[name];
let label = provider.getLabel(item);
let value = provider.getValue(item);
props.push(Object.assign({}, this.props, {
name: label,
object: value,
equal: ": ",
delim: ", ",
}));
}
return props;
},
render: function() {
let grip = this.props.object;
let mode = this.props.mode;
let items;
if (mode == "tiny") {
items = this.getLength(grip);
} else {
let max = (mode == "short") ? 3 : 100;
items = this.getItems(grip, max);
}
return (
ObjectLink({className: "NamedNodeMap"},
span({className: "objectTitle"},
this.getTitle(grip)
),
span({
className: "arrayLeftBracket",
role: "presentation"},
"["
),
items,
span({
className: "arrayRightBracket",
role: "presentation"},
"]"
)
)
);
},
});
/**
* Property for a grip object.
*/
let PropRep = React.createFactory(React.createClass({
propTypes: {
equal: React.PropTypes.string,
delim: React.PropTypes.string,
},
displayName: "PropRep",
render: function() {
const { Rep } = createFactories(require("./rep"));
return (
span({},
span({
className: "nodeName"},
"$prop.name"
),
span({
className: "objectEqual",
role: "presentation"},
this.props.equal
),
Rep(this.props),
span({
className: "objectComma",
role: "presentation"},
this.props.delim
)
)
);
}
}));
// Registration
function supportsObject(grip, type) {
if (!isGrip(grip)) {
return false;
}
return (type == "NamedNodeMap" && grip.preview);
}
// Exports from this module
exports.NamedNodeMap = {
rep: NamedNodeMap,
supportsObject: supportsObject
};
});

View File

@ -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";
// 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 { ObjectLink } = createFactories(require("./object-link"));
// Shortcuts
const { span } = React.DOM;
/**
* Renders a grip object with textual data.
*/
let ObjectWithText = React.createClass({
propTypes: {
object: React.PropTypes.object.isRequired,
},
displayName: "ObjectWithText",
getType: function(grip) {
return grip.class;
},
getDescription: function(grip) {
return (grip.preview.kind == "ObjectWithText") ? grip.preview.text : "";
},
render: function() {
let grip = this.props.object;
return (
ObjectLink({className: this.getType(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
};
});

View File

@ -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";
// 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 { ObjectLink } = createFactories(require("./object-link"));
// Shortcuts
const { span } = React.DOM;
/**
* Renders a grip object with URL data.
*/
let ObjectWithURL = React.createClass({
propTypes: {
object: React.PropTypes.object.isRequired,
},
displayName: "ObjectWithURL",
getType: function(grip) {
return grip.class;
},
getDescription: function(grip) {
return (grip.preview.kind == "ObjectWithURL") ? grip.preview.url : "";
},
render: function() {
let grip = this.props.object;
return (
ObjectLink({className: this.getType(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
};
});

View File

@ -15,29 +15,20 @@ define(function(require, exports, module) {
const { Caption } = createFactories(require("./caption"));
// Shortcuts
const DOM = React.DOM;
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",
render: function() {
let object = this.props.object;
let props = this.shortPropIterator(object);
return (
ObjectBox({className: "object"},
DOM.span({className: "objectTitle"}, this.getTitle(object)),
DOM.span({className: "objectLeftBrace", role: "presentation"}, "{"),
props,
DOM.span({className: "objectRightBrace"}, "}")
)
);
propTypes: {
object: React.PropTypes.object,
mode: React.PropTypes.string,
},
displayName: "Obj",
getTitle: function() {
return "";
},
@ -61,10 +52,10 @@ define(function(require, exports, module) {
},
propIterator: function(object, max) {
function isInterestingProp(t, value) {
return (t == "boolean" || t == "number" || (t == "string" && value) ||
(t == "object" && value && value.toString));
}
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]") {
@ -73,16 +64,15 @@ define(function(require, exports, module) {
// Object members with non-empty values are preferred since it gives the
// user a better overview of the object.
let props = [];
this.getProps(props, object, max, isInterestingProp);
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.
this.getProps(props, object, max, function(t, value) {
props = props.concat(this.getProps(object, max, (t, value) => {
return !isInterestingProp(t, value);
});
}));
}
if (props.length > max) {
@ -100,10 +90,12 @@ define(function(require, exports, module) {
return props;
},
getProps: function(props, object, max, filter) {
getProps: function(object, max, filter) {
let props = [];
max = max || 3;
if (!object) {
return [];
return props;
}
let mode = this.props.mode;
@ -111,7 +103,7 @@ define(function(require, exports, module) {
try {
for (let name in object) {
if (props.length > max) {
return [];
return props;
}
let value;
@ -137,7 +129,21 @@ define(function(require, exports, module) {
console.error(err);
}
return [];
return props;
},
render: function() {
let object = this.props.object;
let props = this.shortPropIterator(object);
return (
ObjectBox({className: "object"},
span({className: "objectTitle"}, this.getTitle(object)),
span({className: "objectLeftBrace", role: "presentation"}, "{"),
props,
span({className: "objectRightBrace"}, "}")
)
);
},
});
@ -145,6 +151,14 @@ define(function(require, exports, module) {
* Renders object property, name-value pair.
*/
let PropRep = React.createFactory(React.createClass({
propTypes: {
object: React.PropTypes.any,
mode: React.PropTypes.string,
name: React.PropTypes.string,
equal: React.PropTypes.string,
delim: React.PropTypes.string,
},
displayName: "PropRep",
render: function() {
@ -153,12 +167,12 @@ define(function(require, exports, module) {
let mode = this.props.mode;
return (
DOM.span({},
DOM.span({
span({},
span({
"className": "nodeName"},
this.props.name
),
DOM.span({
span({
"className": "objectEqual",
role: "presentation"},
this.props.equal
@ -167,7 +181,7 @@ define(function(require, exports, module) {
object: object,
mode: mode
}),
DOM.span({
span({
"className": "objectComma",
role: "presentation"},
this.props.delim

View File

@ -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) {
// ReactJS
const React = require("devtools/client/shared/vendor/react");
// Reps
const { createFactories, isGrip } = require("./rep-utils");
const { ObjectLink } = createFactories(require("./object-link"));
// Shortcuts
const { span } = React.DOM;
/**
* Renders a grip object with regular expression.
*/
let RegExp = React.createClass({
propTypes: {
object: React.PropTypes.object.isRequired,
},
displayName: "regexp",
getTitle: function(grip) {
return grip.class;
},
getSource: function(grip) {
return grip.displayString;
},
render: function() {
let grip = this.props.object;
return (
ObjectLink({className: "regexp"},
span({className: "objectTitle"},
this.getTitle(grip)
),
span(" "),
span({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
};
});

View File

@ -24,6 +24,14 @@ define(function(require, exports, module) {
return result;
}
/**
* Returns true if the given object is a grip (see RDP protocol)
*/
function isGrip(object) {
return object && object.actor;
}
// Exports from this module
exports.createFactories = createFactories;
exports.isGrip = isGrip;
});

View File

@ -11,6 +11,8 @@ 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");
@ -19,11 +21,46 @@ define(function(require, exports, module) {
const { ArrayRep } = require("./array");
const { Obj } = require("./object");
// 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 { NamedNodeMap } = require("./named-node-map");
const { RegExp } = require("./regexp");
const { StyleSheet } = require("./stylesheet");
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 { 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 = [Undefined, Null, StringRep, Number, ArrayRep, Obj];
let defaultRep;
let reps = [
RegExp,
StyleSheet,
Event,
DateTime,
TextNode,
NamedNodeMap,
Attribute,
Func,
ArrayRep,
Document,
Window,
ObjectWithText,
ObjectWithURL,
GripArray,
Grip,
Undefined,
Null,
StringRep,
Number,
];
/**
* Generic rep that is using for rendering native JS types or an object.
@ -32,10 +69,15 @@ define(function(require, exports, module) {
* property.
*/
const Rep = React.createClass({
propTypes: {
object: React.PropTypes.any,
defaultRep: React.PropTypes.object,
},
displayName: "Rep",
render: function() {
let rep = getRep(this.props.object);
let rep = getRep(this.props.object, this.props.defaultRep);
return rep(this.props);
},
});
@ -49,8 +91,11 @@ define(function(require, exports, module) {
* @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) {
function getRep(object, defaultRep = Obj) {
let type = typeof object;
if (type == "object" && object instanceof String) {
type = "string";
@ -70,17 +115,13 @@ define(function(require, exports, module) {
return React.createFactory(rep.rep);
}
} catch (err) {
console.error("reps.getRep; EXCEPTION ", err, err);
console.error(err);
}
}
return React.createFactory(defaultRep.rep);
}
function isGrip(object) {
return object && object.actor;
}
// Exports from this module
exports.Rep = Rep;
});

View File

@ -96,6 +96,9 @@ define(function(require, exports, module) {
exports.StringRep = {
rep: StringRep,
supportsObject: supportsObject,
isCropped: isCropped
};
exports.isCropped = isCropped;
exports.cropString = cropString;
exports.cropMultipleLines = cropMultipleLines;
});

View File

@ -0,0 +1,67 @@
/* -*- 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 { ObjectBox } = createFactories(require("./object-box"));
const { getFileName } = require("./url");
// Shortcuts
const DOM = React.DOM;
/**
* Renders a grip representing CSSStyleSheet
*/
let StyleSheet = React.createClass({
propTypes: {
object: React.PropTypes.object.isRequired,
},
displayName: "object",
getLocation: function(grip) {
// Embedded stylesheets don't have URL and so, no preview.
let url = grip.preview ? grip.preview.url : "";
return url ? getFileName(url) : "";
},
render: function() {
let grip = this.props.object;
return (
ObjectBox({className: "object"},
"StyleSheet ",
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
};
});

View File

@ -0,0 +1,82 @@
/* -*- 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 { ObjectLink } = createFactories(require("./object-link"));
const { cropMultipleLines } = require("./string");
// Shortcuts
const DOM = React.DOM;
/**
* Renders DOM #text node.
*/
let TextNode = React.createClass({
propTypes: {
object: React.PropTypes.object.isRequired,
mode: React.PropTypes.string,
},
displayName: "TextNode",
getTextContent: function(grip) {
return cropMultipleLines(grip.preview.textContent);
},
getTitle: function(win, context) {
return "textNode";
},
render: function() {
let grip = this.props.object;
let mode = this.props.mode || "short";
if (mode == "short" || mode == "tiny") {
return (
ObjectLink({className: "textNode"},
"\"" + this.getTextContent(grip) + "\""
)
);
}
return (
ObjectLink({className: "textNode"},
"<",
DOM.span({className: "nodeTag"}, "TextNode"),
" textContent=\"",
DOM.span({className: "nodeValue"},
this.getTextContent(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
};
});

View File

@ -32,7 +32,50 @@ define(function(require, exports, module) {
});
}
function getFileName(url) {
let split = splitURLBase(url);
return split.name;
}
function splitURLBase(url) {
if (!isDataURL(url)) {
return splitURLTrue(url);
}
return {};
}
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.parseURLParams = parseURLParams;
exports.parseURLEncodedText = parseURLEncodedText;
exports.getFileName = getFileName;
});

View File

@ -0,0 +1,71 @@
/* -*- 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 { ObjectBox } = createFactories(require("./object-box"));
const { cropString } = require("./string");
// Shortcuts
const DOM = React.DOM;
/**
* Renders a grip representing a window.
*/
let Window = React.createClass({
propTypes: {
object: React.PropTypes.object.isRequired,
},
displayName: "Window",
getLocation: function(grip) {
return cropString(grip.preview.url);
},
getTitle: function(grip, context) {
return grip.class;
},
getTooltip: function(grip) {
return grip.preview.url;
},
render: function() {
let grip = this.props.object;
return (
ObjectBox({className: "Window"},
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
};
});

View File

@ -0,0 +1,9 @@
/* 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/. */
:root.theme-dark {
}
:root.theme-light {
}

View File

@ -0,0 +1,11 @@
<!-- 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/. -->
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
<path fill="#51BA7B" d="M487.819 258.669H439.1c-10.041 0-18.181-8.14-18.181-18.181s8.14-18.181 18.181-18.181h48.719c10.041 0 18.181 8.14 18.181 18.181s-8.14 18.181-18.181 18.181z"/>
<path fill="#BADEBE" d="M415.747 69.674s-.387.603-1.059 1.667a8.05 8.05 0 0 1-.638.799 59.208 59.208 0 0 0-1.057 1.933c-.357.806-.812 1.618-1.199 2.599a55.875 55.875 0 0 0-1.233 3.083c-.433 1.086-.783 2.248-1.19 3.426-.364 1.183-.742 2.395-1.064 3.614-.316 1.219-.645 2.445-.884 3.643-.286 1.219-.475 2.381-.679 3.523-.188 1.142-.308 2.214-.434 3.243-.041.505-.077.988-.118 1.457-.043.483-.07.939-.063 1.338-.007.812-.063 1.646 0 2.221.014.315.034.609.048.882 0 .14.007.273.015.399.014.105.027.209.041.301.113.777.161 1.26.161 1.26l.19 2.025a7.085 7.085 0 0 1-6.396 7.712c-2.829.267-5.421-1.177-6.774-3.467 0 0-.489-.826-1.308-2.353-.099-.189-.204-.392-.316-.603-.084-.203-.181-.413-.274-.63-.195-.448-.398-.932-.616-1.45-.484-1.058-.806-2.164-1.233-3.432a23.41 23.41 0 0 1-.561-1.933c-.174-.665-.349-1.352-.532-2.066-.301-1.387-.622-2.879-.876-4.393-.231-1.506-.491-3.089-.638-4.659-.195-1.583-.308-3.18-.419-4.784-.106-1.604-.147-3.201-.19-4.791 0-1.576-.027-3.145.043-4.665.034-1.513.125-2.998.238-4.42.077-1.408.28-2.802.414-4.063.188-1.31.371-2.48.588-3.622.301-1.31.622-2.529.853-3.411.31-1.212.477-1.913.477-1.913 1.968-7.887 9.967-12.692 17.856-10.724 7.894 1.975 12.691 9.968 10.725 17.862a14.708 14.708 0 0 1-1.815 4.266l-.083.126zm-56.35 17.01a14.892 14.892 0 0 0 1.036-4.518c.559-8.111-5.562-15.145-13.681-15.705-8.118-.56-15.151 5.562-15.711 13.681 0 0-.05.715-.133 1.976a70.1 70.1 0 0 0-.258 4.133c.014.665.055 1.233.091 1.912.048.638.063 1.366.154 2.025.174 1.331.308 2.823.595 4.245l.407 2.221c.168.729.335 1.478.511 2.234.322 1.527.783 3.033 1.19 4.575.477 1.526.91 3.068 1.457 4.574a88.52 88.52 0 0 0 1.654 4.483c.552 1.464 1.211 2.893 1.806 4.259a121.46 121.46 0 0 0 1.905 3.936c.694 1.254 1.255 2.41 1.948 3.496.665 1.086 1.226 2.052 1.864 2.936.622.882 1.079 1.611 1.618 2.248l1.59 1.941c1.849 2.255 4.973 3.243 7.887 2.234 3.74-1.289 5.715-5.373 4.426-9.107l-.469-1.338s-.168-.497-.469-1.366c-.162-.386-.303-1.051-.498-1.688-.204-.631-.357-1.478-.554-2.347-.217-.833-.344-1.891-.532-2.906a93.503 93.503 0 0 1-.428-3.362c-.097-1.198-.231-2.396-.274-3.664a61.991 61.991 0 0 1-.118-3.797c-.029-1.268.041-2.543.063-3.775.091-1.219.111-2.438.251-3.566.063-.56.12-1.121.174-1.661.077-.518.162-1.03.233-1.526.132-1.016.371-1.829.525-2.627.077-.407.21-.693.301-1.016.097-.294.181-.645.267-.841.188-.127.258-.147.342-.294l.751-1.829.079-.176z"/>
<path fill="#8ACCA0" d="M456.112 143.24c-11.449-34.634-48.8-53.434-83.441-41.983-34.634 11.449-53.431 48.807-41.982 83.442 17.717 53.598 16.873 94.849-2.59 126.04-1.716 2.393-3.661 5.076-4.907 6.585a66.228 66.228 0 0 0-7.289 6.104 66.535 66.535 0 0 0-3.758 2.035l-2.851 1.674c-.113.077-.154.112-.246.189-.041.035-.077.07-.125.112-.022.021-.029.035-.07.07l-.233.127c-.301.182-.629.371-.973.575-.364.203-.749.413-1.156.637a56.07 56.07 0 0 1-6.494 2.956c-2.711 1.044-6.03 2.109-10.03 3.04a104.442 104.442 0 0 1-13.996 2.179c-5.317.483-11.27.672-17.694.476a202.645 202.645 0 0 1-4.912-.224c-1.597-.105-3.208-.21-4.826-.322-4.134-.393-8.308-.784-12.517-1.191-1.907-.168-3.875-.42-5.821-.623-1.962-.217-3.776-.469-5.681-.693-1.828-.259-3.656-.504-5.437-.777-1.765-.287-3.537-.553-5.288-.882-1.738-.294-3.538-.673-5.331-1.023l-2.774-.603c-.925-.203-1.864-.406-2.865-.652l-2.943-.693-3.095-.77c-2.136-.539-4.26-1.078-6.382-1.618-8.713-2.241-17.861-4.617-26.604-6.732l-1.64-.398-.202-.05-.099-.028c-1.233-.357-.398-.104-.699-.189l-.4-.07-.792-.147a365.722 365.722 0 0 1-3.152-.56c-2.634-.476-5.24-.953-7.824-1.415a44.945 44.945 0 0 0-1.955-.322c-.631-.097-1.26-.196-1.891-.287-1.254-.189-2.5-.371-3.74-.56-2.516-.316-4.966-.658-7.439-.924-9.863-1.1-19.447-1.646-28.545-1.619-9.107.05-17.682.61-25.54 1.661-7.839 1.023-14.949 2.522-21.051 4.175a137.419 137.419 0 0 0-8.419 2.578 129.198 129.198 0 0 0-6.851 2.592c-2.032.847-3.79 1.646-5.204 2.326l-2.039 1.001c-.982.505-1.479.757-1.479.757-26.17 13.548-36.403 45.75-22.857 71.92 13.555 26.178 45.756 36.405 71.927 22.857l-1.45.735c-.301.14-.742.351-1.324.63-.344.155-.848.386-1.507.693-.251.133-.342.203-.21.21.169.015.504-.014 1.072-.063 1.163-.091 3.138-.259 5.955-.231 2.774 0 6.41.231 10.661.757 4.272.54 9.225 1.457 14.668 2.767 1.358.321 2.767.721 4.175 1.071.715.203 1.437.413 2.165.617l1.086.308c.378.105.735.203 1.023.302 1.394.441 2.808.882 4.231 1.331 1.765.575 3.544 1.149 5.337 1.731 7.824 2.472 15.711 5.092 24.357 7.978 2.227.743 4.462 1.478 6.709 2.221l3.496 1.142 3.692 1.17 1.864.589 1.946.588 3.923 1.177c2.704.77 5.387 1.555 8.175 2.269 2.76.75 5.527 1.415 8.294 2.087 2.745.658 5.464 1.248 8.168 1.843 2.634.54 5.344 1.121 7.901 1.604 2.584.476 5.107.981 7.704 1.429 2.543.441 5.072.876 7.586 1.31 2.208.364 4.407.722 6.6 1.086 1.443.217 2.885.427 4.315.644 1.366.182 2.724.371 4.077.553 2.711.351 5.428.666 8.139.946 10.851 1.128 21.682 1.647 32.23 1.513a239.007 239.007 0 0 0 30.479-2.291c9.666-1.366 18.689-3.313 26.716-5.548a191.408 191.408 0 0 0 20.791-7.061c1.428-.595 2.781-1.162 4.055-1.695 1.269-.568 2.459-1.093 3.566-1.59l.812-.364.912-.434c.602-.295 1.177-.568 1.731-.834 1.086-.54 2.073-1.023 2.955-1.464 1.919-.981 2.943-1.5 2.943-1.5l1.975-1.008a66.247 66.247 0 0 0 15.966-11.503 66.576 66.576 0 0 0 6.185-3.588c18.693-12.238 30.142-28.256 38.55-40.018l.926-1.294.862-1.338c23.738-36.839 36.169-79.029 36.947-125.397.602-35.782-5.867-74.417-19.227-114.833z"/>
<path fill="#FFF" d="M379.069 155.928l4.301 16.062h-.021c.007.028.027.028.034.042 1.688 6.311-2.059 12.798-8.363 14.486-6.312 1.688-12.799-2.059-14.487-8.364-.007-.014-.007-.021-.014-.049l-.05.014-4.301-16.062.14-.035c-1.057-5.989 2.55-11.887 8.532-13.492 5.969-1.597 12.048 1.709 14.116 7.425l.113-.027zm48.159-20.587c-2.697-5.45-9.107-8.048-14.858-5.792-5.765 2.263-8.693 8.532-6.971 14.367l-.132.049 6.08 15.481.05-.021c.007.021.007.027.014.042 2.387 6.08 9.254 9.071 15.334 6.683 6.08-2.381 9.071-9.254 6.682-15.334 0-.008-.027-.008-.034-.028l.021-.015-6.08-15.474-.106.042z"/>
<path fill="#51BA7B" d="M386.722 311.106l40.557 17.233c9.247 3.93 13.555 14.612 9.632 23.859-3.93 9.253-14.619 13.561-23.866 9.632a21.165 21.165 0 0 1-1.24-.581l-39.143-20.223c-8.118-4.196-11.292-14.171-7.104-22.29 3.994-7.728 13.284-10.95 21.164-7.63"/>
<path fill="#74C48D" d="M394.574 405.513c-12.623 0-25.31-3.56-36.185-10.523a5.688 5.688 0 0 1 6.134-9.581c15.343 9.826 35.001 11.501 51.304 4.37a5.69 5.69 0 0 1 4.558 10.424c-8.137 3.557-16.959 5.31-25.811 5.31zm-52.133 40.603a5.69 5.69 0 0 0-5.396-5.966c-14.406-.721-28.217-7.446-37.895-18.452a5.689 5.689 0 0 0-8.542 7.513c11.694 13.299 28.412 21.428 45.868 22.301a5.686 5.686 0 0 0 5.965-5.396zm81.464-133.345c15.644-.293 30.09-5.857 41.777-16.091a5.688 5.688 0 0 0-7.496-8.558c-9.641 8.443-21.568 13.033-34.493 13.275a5.69 5.69 0 0 0-5.581 5.794 5.687 5.687 0 0 0 5.684 5.581l.109-.001z"/>
</svg>

After

Width:  |  Height:  |  Size: 7.0 KiB

View File

@ -0,0 +1,6 @@
<!-- 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/. -->
<svg height="16" width="16" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="whitesmoke">
<path d="M.2 6.3l3.5 3.6c.1.1.3.2.5.2h.9c.3 0 .5-.2.6-.4.1-.2.1-.5-.1-.7l-3-3.1 3-2.9c.2-.2.3-.5.2-.7-.2-.4-.5-.5-.7-.5h-.9c-.2 0-.4 0-.5.2L.2 5.4c-.3.2-.3.6 0 .9M15.8 9.7l-3.5-3.6c-.1-.1-.3-.2-.5-.2h-.9c-.3 0-.5.2-.6.4-.1.2-.1.5.1.7l3 3.1-3 2.9c-.2.2-.3.5-.2.7.1.3.3.4.6.4h.9c.2 0 .3-.1.5-.2l3.5-3.4c.4-.1.4-.5.1-.8"/>
</svg>

After

Width:  |  Height:  |  Size: 647 B