mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-09 19:35:51 +00:00
Merge mozilla-inbound to mozilla-central. a=merge
This commit is contained in:
commit
9769f2300a
@ -1,13 +1,13 @@
|
||||
This is the debugger.html project output.
|
||||
See https://github.com/devtools-html/debugger.html
|
||||
|
||||
Version 54
|
||||
Version 55
|
||||
|
||||
Comparison: https://github.com/devtools-html/debugger.html/compare/release-53...release-54
|
||||
Comparison: https://github.com/devtools-html/debugger.html/compare/release-54...release-55
|
||||
|
||||
Packages:
|
||||
- babel-plugin-transform-es2015-modules-commonjs @6.26.2
|
||||
- babel-preset-react @6.24.1
|
||||
- react @16.2.0
|
||||
- react-dom @16.2.0
|
||||
- webpack @3.11.0
|
||||
- webpack @3.12.0
|
||||
|
177
devtools/client/debugger/new/dist/debugger.css
vendored
177
devtools/client/debugger/new/dist/debugger.css
vendored
@ -963,6 +963,89 @@ img.close::before {
|
||||
* 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/>. */
|
||||
|
||||
.command-bar-button {
|
||||
appearance: none;
|
||||
background: transparent;
|
||||
border: none;
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
padding: 0px 5px;
|
||||
fill: currentColor;
|
||||
}
|
||||
|
||||
.command-bar-button:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.command-bar-button:disabled {
|
||||
opacity: 0.8;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.command-bar-button:not(.disabled):hover {
|
||||
background: var(--theme-toolbar-background-hover);
|
||||
}
|
||||
|
||||
:root.theme-dark .command-bar-button {
|
||||
color: var(--theme-body-color);
|
||||
}
|
||||
|
||||
.command-bar-button > * {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
/* 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/>. */
|
||||
|
||||
.toggle-button {
|
||||
transform: translate(0, 0px);
|
||||
transition: transform 0.25s ease-in-out;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.toggle-button .togglePanes {
|
||||
vertical-align: -2px;
|
||||
}
|
||||
|
||||
.toggle-button svg {
|
||||
fill: var(--theme-comment);
|
||||
vertical-align: 0;
|
||||
}
|
||||
|
||||
:root.theme-dark .toggle-button svg {
|
||||
fill: var(--theme-comment-alt);
|
||||
}
|
||||
|
||||
.toggle-button.end {
|
||||
margin-inline-end: 0px;
|
||||
margin-inline-start: auto;
|
||||
}
|
||||
|
||||
.toggle-button.start {
|
||||
margin-inline-start: 0px;
|
||||
}
|
||||
|
||||
html:not([dir="rtl"]) .toggle-button.end svg,
|
||||
html[dir="rtl"] .toggle-button.start svg {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
html .toggle-button.end.vertical svg {
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
|
||||
.toggle-button.start.collapsed,
|
||||
.toggle-button.end.collapsed {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
/* 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-shadow {
|
||||
margin: 1px;
|
||||
}
|
||||
@ -1670,89 +1753,6 @@ menuseparator {
|
||||
* 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/>. */
|
||||
|
||||
.command-bar-button {
|
||||
appearance: none;
|
||||
background: transparent;
|
||||
border: none;
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
padding: 0px 5px;
|
||||
fill: currentColor;
|
||||
}
|
||||
|
||||
.command-bar-button:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.command-bar-button:disabled {
|
||||
opacity: 0.8;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.command-bar-button:not(.disabled):hover {
|
||||
background: var(--theme-toolbar-background-hover);
|
||||
}
|
||||
|
||||
:root.theme-dark .command-bar-button {
|
||||
color: var(--theme-body-color);
|
||||
}
|
||||
|
||||
.command-bar-button > * {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
/* 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/>. */
|
||||
|
||||
.toggle-button {
|
||||
transform: translate(0, 0px);
|
||||
transition: transform 0.25s ease-in-out;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.toggle-button .togglePanes {
|
||||
vertical-align: -2px;
|
||||
}
|
||||
|
||||
.toggle-button svg {
|
||||
fill: var(--theme-comment);
|
||||
vertical-align: 0;
|
||||
}
|
||||
|
||||
:root.theme-dark .toggle-button svg {
|
||||
fill: var(--theme-comment-alt);
|
||||
}
|
||||
|
||||
.toggle-button.end {
|
||||
margin-inline-end: 0px;
|
||||
margin-inline-start: auto;
|
||||
}
|
||||
|
||||
.toggle-button.start {
|
||||
margin-inline-start: 0px;
|
||||
}
|
||||
|
||||
html:not([dir="rtl"]) .toggle-button.end svg,
|
||||
html[dir="rtl"] .toggle-button.start svg {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
html .toggle-button.end.vertical svg {
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
|
||||
.toggle-button.start.collapsed,
|
||||
.toggle-button.end.collapsed {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
/* 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/>. */
|
||||
|
||||
.source-footer {
|
||||
background: var(--theme-body-background);
|
||||
border-top: 1px solid var(--theme-splitter-color);
|
||||
@ -3916,6 +3916,17 @@ img.skipPausing {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.welcomebox__searchSources,
|
||||
.welcomebox__searchProject {
|
||||
transition: color 0.15s linear;
|
||||
}
|
||||
|
||||
.welcomebox__searchSources:hover,
|
||||
.welcomebox__searchProject:hover {
|
||||
color: var(--theme-body-color);
|
||||
}
|
||||
|
||||
.shortcutKey {
|
||||
|
@ -179,11 +179,16 @@ function enableBreakpoint(location) {
|
||||
|
||||
if (!breakpoint || breakpoint.loading) {
|
||||
return;
|
||||
}
|
||||
} // To instantly reflect in the UI, we optimistically enable the breakpoint
|
||||
|
||||
|
||||
const enabledBreakpoint = _objectSpread({}, breakpoint, {
|
||||
disabled: false
|
||||
});
|
||||
|
||||
return dispatch({
|
||||
type: "ENABLE_BREAKPOINT",
|
||||
breakpoint,
|
||||
breakpoint: enabledBreakpoint,
|
||||
[_promise.PROMISE]: (0, _addBreakpoint2.default)(getState, client, sourceMaps, breakpoint)
|
||||
});
|
||||
};
|
||||
|
@ -56,10 +56,10 @@ function paused(pauseInfo) {
|
||||
why,
|
||||
loadedObjects
|
||||
} = pauseInfo;
|
||||
const rootFrame = frames.length > 0 ? frames[0] : null;
|
||||
const topFrame = frames.length > 0 ? frames[0] : null;
|
||||
|
||||
if (rootFrame) {
|
||||
const mappedFrame = await (0, _mapFrames.updateFrameLocation)(rootFrame, sourceMaps);
|
||||
if (topFrame && why.type == "resumeLimit") {
|
||||
const mappedFrame = await (0, _mapFrames.updateFrameLocation)(topFrame, sourceMaps);
|
||||
const source = await getOriginalSourceForFrame(getState(), mappedFrame); // Ensure that the original file has loaded if there is one.
|
||||
|
||||
await dispatch((0, _loadSourceText.loadSourceText)(source));
|
||||
@ -74,7 +74,7 @@ function paused(pauseInfo) {
|
||||
type: "PAUSED",
|
||||
why,
|
||||
frames,
|
||||
selectedFrameId: rootFrame ? rootFrame.id : undefined,
|
||||
selectedFrameId: topFrame ? topFrame.id : undefined,
|
||||
loadedObjects: loadedObjects || []
|
||||
});
|
||||
const hiddenBreakpointLocation = (0, _selectors.getHiddenBreakpointLocation)(getState());
|
||||
|
@ -5,6 +5,7 @@ Object.defineProperty(exports, "__esModule", {
|
||||
});
|
||||
exports.addSearchQuery = addSearchQuery;
|
||||
exports.clearSearchQuery = clearSearchQuery;
|
||||
exports.addSearchResult = addSearchResult;
|
||||
exports.clearSearchResults = clearSearchResults;
|
||||
exports.clearSearch = clearSearch;
|
||||
exports.updateSearchStatus = updateSearchStatus;
|
||||
@ -43,6 +44,17 @@ function clearSearchQuery() {
|
||||
};
|
||||
}
|
||||
|
||||
function addSearchResult(sourceId, filepath, matches) {
|
||||
return {
|
||||
type: "ADD_SEARCH_RESULT",
|
||||
result: {
|
||||
sourceId,
|
||||
filepath,
|
||||
matches
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function clearSearchResults() {
|
||||
return {
|
||||
type: "CLEAR_SEARCH_RESULTS"
|
||||
@ -105,13 +117,6 @@ function searchSource(sourceId, query) {
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch({
|
||||
type: "ADD_SEARCH_RESULT",
|
||||
result: {
|
||||
sourceId: sourceRecord.id,
|
||||
filepath: sourceRecord.url,
|
||||
matches
|
||||
}
|
||||
});
|
||||
dispatch(addSearchResult(sourceRecord.id, sourceRecord.url, matches));
|
||||
};
|
||||
}
|
@ -4,6 +4,18 @@ Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
|
||||
var _blackbox = require("./blackbox");
|
||||
|
||||
Object.keys(_blackbox).forEach(function (key) {
|
||||
if (key === "default" || key === "__esModule") return;
|
||||
Object.defineProperty(exports, key, {
|
||||
enumerable: true,
|
||||
get: function () {
|
||||
return _blackbox[key];
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
var _loadSourceText = require("./loadSourceText");
|
||||
|
||||
Object.keys(_loadSourceText).forEach(function (key) {
|
||||
@ -16,18 +28,6 @@ Object.keys(_loadSourceText).forEach(function (key) {
|
||||
});
|
||||
});
|
||||
|
||||
var _prettyPrint = require("./prettyPrint");
|
||||
|
||||
Object.keys(_prettyPrint).forEach(function (key) {
|
||||
if (key === "default" || key === "__esModule") return;
|
||||
Object.defineProperty(exports, key, {
|
||||
enumerable: true,
|
||||
get: function () {
|
||||
return _prettyPrint[key];
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
var _newSources = require("./newSources");
|
||||
|
||||
Object.keys(_newSources).forEach(function (key) {
|
||||
@ -40,14 +40,14 @@ Object.keys(_newSources).forEach(function (key) {
|
||||
});
|
||||
});
|
||||
|
||||
var _blackbox = require("./blackbox");
|
||||
var _prettyPrint = require("./prettyPrint");
|
||||
|
||||
Object.keys(_blackbox).forEach(function (key) {
|
||||
Object.keys(_prettyPrint).forEach(function (key) {
|
||||
if (key === "default" || key === "__esModule") return;
|
||||
Object.defineProperty(exports, key, {
|
||||
enumerable: true,
|
||||
get: function () {
|
||||
return _blackbox[key];
|
||||
return _prettyPrint[key];
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@ -96,7 +96,7 @@ function loadSourceText(source) {
|
||||
const newSource = (0, _selectors.getSource)(getState(), source.get("id")).toJS();
|
||||
|
||||
if ((0, _devtoolsSourceMap.isOriginalId)(newSource.id) && !newSource.isWasm) {
|
||||
const generatedSource = (0, _selectors.getGeneratedSource)(getState(), source.toJS());
|
||||
const generatedSource = (0, _selectors.getGeneratedSource)(getState(), source);
|
||||
await dispatch(loadSourceText(generatedSource));
|
||||
}
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.clearSelectedLocation = exports.setPendingSelectedLocation = exports.setSelectedLocation = undefined;
|
||||
exports.selectSourceURL = selectSourceURL;
|
||||
exports.selectSource = selectSource;
|
||||
exports.selectLocation = selectLocation;
|
||||
@ -37,6 +38,21 @@ function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { va
|
||||
|
||||
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
||||
|
||||
const setSelectedLocation = exports.setSelectedLocation = (source, location) => ({
|
||||
type: "SET_SELECTED_LOCATION",
|
||||
source,
|
||||
location
|
||||
});
|
||||
|
||||
const setPendingSelectedLocation = exports.setPendingSelectedLocation = (url, options) => ({
|
||||
type: "SET_PENDING_SELECTED_LOCATION",
|
||||
url: url,
|
||||
line: options.location ? options.location.line : null
|
||||
});
|
||||
|
||||
const clearSelectedLocation = exports.clearSelectedLocation = () => ({
|
||||
type: "CLEAR_SELECTED_LOCATION"
|
||||
});
|
||||
/**
|
||||
* Deterministically select a source that has a given URL. This will
|
||||
* work regardless of the connection status or if the source exists
|
||||
@ -46,6 +62,8 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
|
||||
* @memberof actions/sources
|
||||
* @static
|
||||
*/
|
||||
|
||||
|
||||
function selectSourceURL(url, options = {}) {
|
||||
return async ({
|
||||
dispatch,
|
||||
@ -60,11 +78,7 @@ function selectSourceURL(url, options = {}) {
|
||||
}));
|
||||
await dispatch(selectLocation(location));
|
||||
} else {
|
||||
dispatch({
|
||||
type: "SELECT_SOURCE_URL",
|
||||
url: url,
|
||||
line: options.location ? options.location.line : null
|
||||
});
|
||||
dispatch(setPendingSelectedLocation(url, options));
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -106,9 +120,7 @@ function selectLocation(location) {
|
||||
|
||||
if (!sourceRecord) {
|
||||
// If there is no source we deselect the current selected source
|
||||
return dispatch({
|
||||
type: "CLEAR_SELECTED_SOURCE"
|
||||
});
|
||||
return dispatch(clearSelectedLocation());
|
||||
}
|
||||
|
||||
const activeSearch = (0, _selectors.getActiveSearch)(getState());
|
||||
@ -119,11 +131,7 @@ function selectLocation(location) {
|
||||
|
||||
const source = sourceRecord.toJS();
|
||||
dispatch((0, _tabs.addTab)(source.url, 0));
|
||||
dispatch({
|
||||
type: "SELECT_SOURCE",
|
||||
source,
|
||||
location
|
||||
});
|
||||
dispatch(setSelectedLocation(source, location));
|
||||
await dispatch((0, _loadSourceText.loadSourceText)(sourceRecord));
|
||||
const selectedSource = (0, _selectors.getSelectedSource)(getState());
|
||||
|
||||
@ -164,9 +172,7 @@ function selectSpecificLocation(location) {
|
||||
|
||||
if (!sourceRecord) {
|
||||
// If there is no source we deselect the current selected source
|
||||
return dispatch({
|
||||
type: "CLEAR_SELECTED_SOURCE"
|
||||
});
|
||||
return dispatch(clearSelectedLocation());
|
||||
}
|
||||
|
||||
const activeSearch = (0, _selectors.getActiveSearch)(getState());
|
||||
@ -177,11 +183,7 @@ function selectSpecificLocation(location) {
|
||||
|
||||
const source = sourceRecord.toJS();
|
||||
dispatch((0, _tabs.addTab)(source, 0));
|
||||
dispatch({
|
||||
type: "SELECT_SOURCE",
|
||||
source,
|
||||
location
|
||||
});
|
||||
dispatch(setSelectedLocation(source, location));
|
||||
await dispatch((0, _loadSourceText.loadSourceText)(sourceRecord));
|
||||
const selectedSource = (0, _selectors.getSelectedSource)(getState());
|
||||
|
||||
|
@ -28,9 +28,7 @@ var _sources = require("../../reducers/sources");
|
||||
|
||||
var _editor = require("../../utils/editor/index");
|
||||
|
||||
var _PaneToggle = require("../shared/Button/PaneToggle");
|
||||
|
||||
var _PaneToggle2 = _interopRequireDefault(_PaneToggle);
|
||||
var _Button = require("../shared/Button/index");
|
||||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
|
||||
@ -129,7 +127,7 @@ class SourceFooter extends _react.PureComponent {
|
||||
return;
|
||||
}
|
||||
|
||||
return _react2.default.createElement(_PaneToggle2.default, {
|
||||
return _react2.default.createElement(_Button.PaneToggleButton, {
|
||||
position: "end",
|
||||
collapsed: !this.props.endPanelCollapsed,
|
||||
horizontal: this.props.horizontal,
|
||||
@ -189,11 +187,10 @@ class SourceFooter extends _react.PureComponent {
|
||||
|
||||
const mapStateToProps = state => {
|
||||
const selectedSource = (0, _selectors.getSelectedSource)(state);
|
||||
const selectedId = selectedSource.get("id");
|
||||
const source = selectedSource.toJS();
|
||||
const selectedId = selectedSource.id;
|
||||
return {
|
||||
selectedSource,
|
||||
mappedSource: (0, _sources.getGeneratedSource)(state, source),
|
||||
mappedSource: (0, _sources.getGeneratedSource)(state, selectedSource),
|
||||
prettySource: (0, _selectors.getPrettySource)(state, selectedId),
|
||||
endPanelCollapsed: (0, _selectors.getPaneCollapse)(state, "end")
|
||||
};
|
||||
|
@ -68,7 +68,7 @@ class Popup extends _react.Component {
|
||||
return _temp = super(...args), this.onMouseLeave = e => {
|
||||
const relatedTarget = e.relatedTarget;
|
||||
|
||||
if (relatedTarget && (relatedTarget.classList.contains("popover") || relatedTarget.classList.contains("debug-expression") || relatedTarget.classList.contains("editor-mount"))) {
|
||||
if (relatedTarget && relatedTarget.classList && (relatedTarget.classList.contains("popover") || relatedTarget.classList.contains("debug-expression") || relatedTarget.classList.contains("editor-mount"))) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -12,9 +12,7 @@ var _reactRedux = require("devtools/client/shared/vendor/react-redux");
|
||||
|
||||
var _devtoolsContextmenu = require("devtools/client/debugger/new/dist/vendors").vendored["devtools-contextmenu"];
|
||||
|
||||
var _Close = require("../shared/Button/Close");
|
||||
|
||||
var _Close2 = _interopRequireDefault(_Close);
|
||||
var _Button = require("../shared/Button/index");
|
||||
|
||||
var _actions = require("../../actions/index");
|
||||
|
||||
@ -171,7 +169,7 @@ class Tab extends _react.PureComponent {
|
||||
title: (0, _source.getFileURL)(src)
|
||||
}, sourceAnnotation, _react2.default.createElement("div", {
|
||||
className: "filename"
|
||||
}, filename), _react2.default.createElement(_Close2.default, {
|
||||
}, filename), _react2.default.createElement(_Button.CloseButton, {
|
||||
handleClick: onClickClose,
|
||||
tooltip: L10N.getStr("sourceTabs.closeTabButtonTooltip")
|
||||
}));
|
||||
|
@ -32,9 +32,7 @@ var _Tab = require("./Tab");
|
||||
|
||||
var _Tab2 = _interopRequireDefault(_Tab);
|
||||
|
||||
var _PaneToggle = require("../shared/Button/PaneToggle");
|
||||
|
||||
var _PaneToggle2 = _interopRequireDefault(_PaneToggle);
|
||||
var _Button = require("../shared/Button/index");
|
||||
|
||||
var _Dropdown = require("../shared/Dropdown");
|
||||
|
||||
@ -174,7 +172,7 @@ class Tabs extends _react.PureComponent {
|
||||
}
|
||||
|
||||
renderStartPanelToggleButton() {
|
||||
return _react2.default.createElement(_PaneToggle2.default, {
|
||||
return _react2.default.createElement(_Button.PaneToggleButton, {
|
||||
position: "start",
|
||||
collapsed: !this.props.startPanelCollapsed,
|
||||
handleClick: this.props.togglePaneCollapse
|
||||
@ -192,7 +190,7 @@ class Tabs extends _react.PureComponent {
|
||||
return;
|
||||
}
|
||||
|
||||
return _react2.default.createElement(_PaneToggle2.default, {
|
||||
return _react2.default.createElement(_Button.PaneToggleButton, {
|
||||
position: "end",
|
||||
collapsed: !endPanelCollapsed,
|
||||
handleClick: togglePaneCollapse,
|
||||
|
@ -433,6 +433,7 @@ class QuickOpenModal extends _react.Component {
|
||||
const newResults = results && results.slice(0, 100);
|
||||
const items = this.highlightMatching(query, newResults || []);
|
||||
const expanded = !!items && items.length > 0;
|
||||
const summaryMsg = this.isGotoQuery() ? L10N.getStr("shortcuts.gotoLine") : "";
|
||||
return _react2.default.createElement(_Modal2.default, {
|
||||
"in": enabled,
|
||||
handleClose: this.closeModal
|
||||
@ -441,7 +442,7 @@ class QuickOpenModal extends _react.Component {
|
||||
hasPrefix: true,
|
||||
count: this.getResultCount(),
|
||||
placeholder: L10N.getStr("sourceSearch.search"),
|
||||
summaryMsg: "",
|
||||
summaryMsg: summaryMsg,
|
||||
showErrorEmoji: this.shouldShowErrorEmoji(),
|
||||
onChange: this.onChange,
|
||||
onKeyDown: this.onKeyDown,
|
||||
|
@ -18,9 +18,7 @@ var _classnames2 = _interopRequireDefault(_classnames);
|
||||
|
||||
var _devtoolsSourceMap = require("devtools/client/shared/source-map/index.js");
|
||||
|
||||
var _Close = require("../shared/Button/Close");
|
||||
|
||||
var _Close2 = _interopRequireDefault(_Close);
|
||||
var _Button = require("../shared/Button/index");
|
||||
|
||||
var _breakpoint = require("../../utils/breakpoint/index");
|
||||
|
||||
@ -166,7 +164,7 @@ class Breakpoint extends _react.Component {
|
||||
className: "breakpoint-line-close"
|
||||
}, _react2.default.createElement("div", {
|
||||
className: "breakpoint-line"
|
||||
}, getBreakpointLocation(breakpoint.source, line, column)), _react2.default.createElement(_Close2.default, {
|
||||
}, getBreakpointLocation(breakpoint.source, line, column)), _react2.default.createElement(_Button.CloseButton, {
|
||||
handleClick: onCloseClick,
|
||||
tooltip: L10N.getStr("breakpoints.removeBreakpointTooltip")
|
||||
}));
|
||||
|
@ -156,19 +156,21 @@ class Breakpoints extends _react.Component {
|
||||
|
||||
const groupedBreakpoints = (0, _lodash.groupBy)((0, _lodash.sortBy)([...breakpoints.valueSeq()], bp => bp.location.line), bp => (0, _source.getRawSourceURL)(bp.source.url));
|
||||
return [...Object.keys(groupedBreakpoints).sort(sortFilenames).map(url => {
|
||||
const file = (0, _source.getFilenameFromURL)(url);
|
||||
const groupBreakpoints = groupedBreakpoints[url].filter(bp => !bp.hidden && (bp.text || bp.originalText));
|
||||
|
||||
if (!groupBreakpoints.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const {
|
||||
source
|
||||
} = groupBreakpoints[0];
|
||||
return [_react2.default.createElement("div", {
|
||||
className: "breakpoint-heading",
|
||||
title: url,
|
||||
key: url,
|
||||
onClick: () => this.props.selectSource(groupBreakpoints[0].source.id)
|
||||
}, file), ...groupBreakpoints.map(bp => this.renderBreakpoint(bp))];
|
||||
onClick: () => this.props.selectSource(source.id)
|
||||
}, (0, _source.getFilename)(source)), ...groupBreakpoints.map(bp => this.renderBreakpoint(bp))];
|
||||
})];
|
||||
}
|
||||
|
||||
|
@ -16,9 +16,7 @@ var _actions2 = _interopRequireDefault(_actions);
|
||||
|
||||
var _selectors = require("../../selectors/index");
|
||||
|
||||
var _Close = require("../shared/Button/Close");
|
||||
|
||||
var _Close2 = _interopRequireDefault(_Close);
|
||||
var _Button = require("../shared/Button/index");
|
||||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
|
||||
@ -58,7 +56,7 @@ class EventListeners extends _react.Component {
|
||||
className: "type"
|
||||
}, type), _react2.default.createElement("span", {
|
||||
className: "selector"
|
||||
}, selector), breakpoint ? _react2.default.createElement(_Close2.default, {
|
||||
}, selector), breakpoint ? _react2.default.createElement(_Button.CloseButton, {
|
||||
handleClick: ev => this.removeBreakpoint(ev, breakpoint)
|
||||
}) : "");
|
||||
};
|
||||
|
@ -26,9 +26,7 @@ var _expressions = require("../../utils/expressions");
|
||||
|
||||
var _firefox = require("../../client/firefox");
|
||||
|
||||
var _Close = require("../shared/Button/Close");
|
||||
|
||||
var _Close2 = _interopRequireDefault(_Close);
|
||||
var _Button = require("../shared/Button/index");
|
||||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
|
||||
@ -149,7 +147,7 @@ class Expressions extends _react.Component {
|
||||
createObjectClient: grip => (0, _firefox.createObjectClient)(grip)
|
||||
}), _react2.default.createElement("div", {
|
||||
className: "expression-container__close-btn"
|
||||
}, _react2.default.createElement(_Close2.default, {
|
||||
}, _react2.default.createElement(_Button.CloseButton, {
|
||||
handleClick: e => this.deleteExpression(e, expression)
|
||||
}))));
|
||||
};
|
||||
|
@ -19,9 +19,7 @@ var _selectors = require("../selectors/index");
|
||||
|
||||
var _text = require("../utils/text");
|
||||
|
||||
var _PaneToggle = require("./shared/Button/PaneToggle");
|
||||
|
||||
var _PaneToggle2 = _interopRequireDefault(_PaneToggle);
|
||||
var _Button = require("./shared/Button/index");
|
||||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
|
||||
@ -40,7 +38,7 @@ class WelcomeBox extends _react.Component {
|
||||
return;
|
||||
}
|
||||
|
||||
return _react2.default.createElement(_PaneToggle2.default, {
|
||||
return _react2.default.createElement(_Button.PaneToggleButton, {
|
||||
position: "end",
|
||||
collapsed: !endPanelCollapsed,
|
||||
horizontal: horizontal,
|
||||
@ -65,6 +63,8 @@ class WelcomeBox extends _react.Component {
|
||||
className: "shortcutFunction"
|
||||
}, _react2.default.createElement("p", {
|
||||
className: "welcomebox__searchSources",
|
||||
role: "button",
|
||||
tabIndex: "0",
|
||||
onClick: () => openQuickOpen()
|
||||
}, _react2.default.createElement("span", {
|
||||
className: "shortcutKey"
|
||||
@ -72,6 +72,8 @@ class WelcomeBox extends _react.Component {
|
||||
className: "shortcutLabel"
|
||||
}, searchSourcesLabel)), _react2.default.createElement("p", {
|
||||
className: "welcomebox__searchProject",
|
||||
role: "button",
|
||||
tabIndex: "0",
|
||||
onClick: setActiveSearch.bind(null, "project")
|
||||
}, _react2.default.createElement("span", {
|
||||
className: "shortcutKey"
|
||||
|
@ -0,0 +1,30 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
|
||||
var _react = require("devtools/client/shared/vendor/react");
|
||||
|
||||
var _react2 = _interopRequireDefault(_react);
|
||||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
|
||||
/* 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/>. */
|
||||
function CloseButton({
|
||||
handleClick,
|
||||
buttonClass,
|
||||
tooltip
|
||||
}) {
|
||||
return _react2.default.createElement("button", {
|
||||
className: buttonClass ? `close-btn ${buttonClass}` : "close-btn",
|
||||
onClick: handleClick,
|
||||
title: tooltip
|
||||
}, _react2.default.createElement("img", {
|
||||
className: "close"
|
||||
}));
|
||||
}
|
||||
|
||||
exports.default = CloseButton;
|
@ -0,0 +1,52 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
|
||||
var _react = require("devtools/client/shared/vendor/react");
|
||||
|
||||
var _react2 = _interopRequireDefault(_react);
|
||||
|
||||
var _classnames = require("devtools/client/debugger/new/dist/vendors").vendored["classnames"];
|
||||
|
||||
var _classnames2 = _interopRequireDefault(_classnames);
|
||||
|
||||
var _Svg = require("devtools/client/debugger/new/dist/vendors").vendored["Svg"];
|
||||
|
||||
var _Svg2 = _interopRequireDefault(_Svg);
|
||||
|
||||
var _ = require(".//index");
|
||||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
|
||||
/* 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/>. */
|
||||
class PaneToggleButton extends _react.PureComponent {
|
||||
render() {
|
||||
const {
|
||||
position,
|
||||
collapsed,
|
||||
horizontal,
|
||||
handleClick
|
||||
} = this.props;
|
||||
const title = !collapsed ? L10N.getStr("expandPanes") : L10N.getStr("collapsePanes");
|
||||
return _react2.default.createElement(_.CommandBarButton, {
|
||||
className: (0, _classnames2.default)("toggle-button", position, {
|
||||
collapsed,
|
||||
vertical: !horizontal
|
||||
}),
|
||||
onClick: () => handleClick(position, collapsed),
|
||||
title: title
|
||||
}, _react2.default.createElement(_Svg2.default, {
|
||||
name: "togglePanes"
|
||||
}));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
PaneToggleButton.defaultProps = {
|
||||
horizontal: false
|
||||
};
|
||||
exports.default = PaneToggleButton;
|
@ -0,0 +1,28 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.PaneToggleButton = exports.debugBtn = exports.CommandBarButton = exports.CloseButton = undefined;
|
||||
|
||||
var _CloseButton = require("./CloseButton");
|
||||
|
||||
var _CloseButton2 = _interopRequireDefault(_CloseButton);
|
||||
|
||||
var _CommandBarButton = require("./CommandBarButton");
|
||||
|
||||
var _CommandBarButton2 = _interopRequireDefault(_CommandBarButton);
|
||||
|
||||
var _PaneToggleButton = require("./PaneToggleButton");
|
||||
|
||||
var _PaneToggleButton2 = _interopRequireDefault(_PaneToggleButton);
|
||||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
|
||||
/* 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/>. */
|
||||
exports.CloseButton = _CloseButton2.default;
|
||||
exports.CommandBarButton = _CommandBarButton2.default;
|
||||
exports.debugBtn = _CommandBarButton.debugBtn;
|
||||
exports.PaneToggleButton = _PaneToggleButton2.default;
|
@ -8,7 +8,8 @@ DIRS += [
|
||||
]
|
||||
|
||||
DevToolsModules(
|
||||
'Close.js',
|
||||
'CloseButton.js',
|
||||
'CommandBarButton.js',
|
||||
'PaneToggle.js',
|
||||
'index.js',
|
||||
'PaneToggleButton.js',
|
||||
)
|
||||
|
@ -8,9 +8,7 @@ var _react = require("devtools/client/shared/vendor/react");
|
||||
|
||||
var _react2 = _interopRequireDefault(_react);
|
||||
|
||||
var _Close = require("./Button/Close");
|
||||
|
||||
var _Close2 = _interopRequireDefault(_Close);
|
||||
var _Button = require("./Button/index");
|
||||
|
||||
var _Svg = require("devtools/client/debugger/new/dist/vendors").vendored["Svg"];
|
||||
|
||||
@ -173,7 +171,7 @@ class SearchInput extends _react.Component {
|
||||
"aria-expanded": expanded
|
||||
}, this.renderSvg(), _react2.default.createElement("input", inputProps), summaryMsg && _react2.default.createElement("div", {
|
||||
className: "summary"
|
||||
}, summaryMsg), this.renderNav(), showClose && _react2.default.createElement(_Close2.default, {
|
||||
}, summaryMsg), this.renderNav(), showClose && _react2.default.createElement(_Button.CloseButton, {
|
||||
handleClick: handleClose,
|
||||
buttonClass: size
|
||||
})));
|
||||
|
@ -219,7 +219,7 @@ function update(state = createPauseState(), action) {
|
||||
{
|
||||
return action.status === "start" ? _objectSpread({}, state, emptyPauseState, {
|
||||
command: action.command,
|
||||
previousLocation: buildPreviousLocation(state, action)
|
||||
previousLocation: getPauseLocation(state, action)
|
||||
}) : _objectSpread({}, state, {
|
||||
command: null
|
||||
});
|
||||
@ -253,17 +253,18 @@ function update(state = createPauseState(), action) {
|
||||
return state;
|
||||
}
|
||||
|
||||
function buildPreviousLocation(state, action) {
|
||||
function getPauseLocation(state, action) {
|
||||
const {
|
||||
frames,
|
||||
previousLocation
|
||||
} = state;
|
||||
} = state; // NOTE: We store the previous location so that we ensure that we
|
||||
// do not stop at the same location twice when we step over.
|
||||
|
||||
if (action.command !== "stepOver") {
|
||||
return null;
|
||||
}
|
||||
|
||||
const frame = frames && frames.length > 0 ? frames[0] : null;
|
||||
const frame = frames && frames[0];
|
||||
|
||||
if (!frame) {
|
||||
return previousLocation;
|
||||
|
@ -89,7 +89,7 @@ function update(state = initialSourcesState(), action) {
|
||||
return action.sources.reduce((newState, source) => updateSource(newState, source), state);
|
||||
}
|
||||
|
||||
case "SELECT_SOURCE":
|
||||
case "SET_SELECTED_LOCATION":
|
||||
location = _objectSpread({}, action.location, {
|
||||
url: action.source.url
|
||||
});
|
||||
@ -98,7 +98,7 @@ function update(state = initialSourcesState(), action) {
|
||||
sourceId: action.source.id
|
||||
}, action.location)).set("pendingSelectedLocation", location);
|
||||
|
||||
case "CLEAR_SELECTED_SOURCE":
|
||||
case "CLEAR_SELECTED_LOCATION":
|
||||
location = {
|
||||
url: ""
|
||||
};
|
||||
@ -107,7 +107,7 @@ function update(state = initialSourcesState(), action) {
|
||||
sourceId: ""
|
||||
}).set("pendingSelectedLocation", location);
|
||||
|
||||
case "SELECT_SOURCE_URL":
|
||||
case "SET_PENDING_SELECTED_LOCATION":
|
||||
location = {
|
||||
url: action.url,
|
||||
line: action.line
|
||||
@ -353,12 +353,12 @@ function getSourceByURL(state, url) {
|
||||
return getSourceByUrlInSources(state.sources.sources, url);
|
||||
}
|
||||
|
||||
function getGeneratedSource(state, source) {
|
||||
if (!source || !(0, _devtoolsSourceMap.isOriginalId)(source.id)) {
|
||||
function getGeneratedSource(state, sourceRecord) {
|
||||
if (!sourceRecord || !(0, _devtoolsSourceMap.isOriginalId)(sourceRecord.id)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return getSource(state, (0, _devtoolsSourceMap.originalToGeneratedId)(source.id));
|
||||
return getSource(state, (0, _devtoolsSourceMap.originalToGeneratedId)(sourceRecord.id));
|
||||
}
|
||||
|
||||
function getPendingSelectedLocation(state) {
|
||||
|
@ -357,21 +357,16 @@ add_task(async function() {
|
||||
]
|
||||
);
|
||||
|
||||
await breakpointScopes(
|
||||
dbg,
|
||||
"webpack-functions",
|
||||
{ line: 4, column: 0 },
|
||||
[
|
||||
"Block",
|
||||
["<this>", "{\u2026}"],
|
||||
["arguments", "Arguments"],
|
||||
["x", "4"],
|
||||
"webpackFunctions",
|
||||
["__webpack_exports__", "(optimized away)"],
|
||||
["__webpack_require__", "(optimized away)"],
|
||||
["arguments", "(unavailable)"],
|
||||
["module", "{\u2026}"],
|
||||
["root", "(optimized away)"]
|
||||
]
|
||||
);
|
||||
await breakpointScopes(dbg, "webpack-functions", { line: 4, column: 0 }, [
|
||||
"Block",
|
||||
["<this>", "{\u2026}"],
|
||||
["arguments", "Arguments"],
|
||||
["x", "4"],
|
||||
"webpackFunctions",
|
||||
["__webpack_exports__", "(optimized away)"],
|
||||
["__webpack_require__", "(optimized away)"],
|
||||
["arguments", "(unavailable)"],
|
||||
["module", "{\u2026}"],
|
||||
["root", "(optimized away)"]
|
||||
]);
|
||||
});
|
||||
|
@ -8,7 +8,7 @@ async function waitForSourceCount(dbg, i) {
|
||||
// source tree batches its rendering.
|
||||
await waitUntil(() => {
|
||||
return findAllElements(dbg, "sourceNodes").length === i;
|
||||
});
|
||||
}, `waiting for ${i} sources`);
|
||||
}
|
||||
|
||||
async function assertSourceCount(dbg, count) {
|
||||
@ -36,7 +36,7 @@ add_task(async function() {
|
||||
await clickElement(dbg, "sourceDirectoryLabel", 3);
|
||||
await assertSourceCount(dbg, 8);
|
||||
|
||||
const selected = waitForDispatch(dbg, "SELECT_SOURCE");
|
||||
const selected = waitForDispatch(dbg, "SET_SELECTED_LOCATION");
|
||||
await clickElement(dbg, "sourceNode", 4);
|
||||
await selected;
|
||||
await waitForSelectedSource(dbg);
|
||||
|
@ -266,8 +266,9 @@ Inspector.prototype = {
|
||||
await this.setupToolbar();
|
||||
|
||||
// Show the 3 pane onboarding tooltip only if the inspector is visisble since the
|
||||
// Accessibility panel initializes the Inspector.
|
||||
if (this.show3PaneTooltip && this.toolbox.currentToolId === "inspector") {
|
||||
// Accessibility panel initializes the Inspector and if it is not the browser toolbox.
|
||||
if (this.show3PaneTooltip && !this.target.chrome &&
|
||||
this.toolbox.currentToolId === "inspector") {
|
||||
this.threePaneTooltip = new ThreePaneOnboardingTooltip(this.toolbox, this.panelDoc);
|
||||
}
|
||||
|
||||
@ -486,6 +487,10 @@ Inspector.prototype = {
|
||||
* @return {Boolean} true if the inspector should be in landscape mode.
|
||||
*/
|
||||
useLandscapeMode: function() {
|
||||
if (!this.panelDoc) {
|
||||
return true;
|
||||
}
|
||||
|
||||
let { clientWidth } = this.panelDoc.getElementById("inspector-splitter-box");
|
||||
return this.is3PaneModeEnabled && this.toolbox.hostType == Toolbox.HostType.SIDE ?
|
||||
clientWidth > SIDE_PORTAIT_MODE_WIDTH_THRESHOLD :
|
||||
@ -552,8 +557,12 @@ Inspector.prototype = {
|
||||
* to `horizontal` to support portrait view.
|
||||
*/
|
||||
onPanelWindowResize: function() {
|
||||
this.splitBox.setState({
|
||||
vert: this.useLandscapeMode(),
|
||||
window.cancelIdleCallback(this._resizeTimerId);
|
||||
this._resizeTimerId = window.requestIdleCallback(() => {
|
||||
this.splitBox.setState({
|
||||
vert: this.useLandscapeMode(),
|
||||
});
|
||||
this.emit("inspector-resize");
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -47,7 +47,9 @@ add_task(async function() {
|
||||
let hostWindow = toolbox.win.parent;
|
||||
let originalWidth = hostWindow.outerWidth;
|
||||
let originalHeight = hostWindow.outerHeight;
|
||||
let inspectorResized = inspector.once("inspector-resize");
|
||||
hostWindow.resizeTo(640, 300);
|
||||
await inspectorResized;
|
||||
|
||||
info("Testing transitions ltr");
|
||||
await pushPref("intl.uidirection", 0);
|
||||
|
@ -668,7 +668,7 @@ const Grip = __webpack_require__(3656);
|
||||
// List of all registered template.
|
||||
// XXX there should be a way for extensions to register a new
|
||||
// or modify an existing rep.
|
||||
const reps = [RegExp, StyleSheet, Event, DateTime, CommentNode, ElementNode, TextNode, Attribute, Func, PromiseRep, ArrayRep, Document, DocumentType, Window, ObjectWithText, ObjectWithURL, ErrorRep, GripArray, GripMap, GripMapEntry, Grip, Undefined, Null, StringRep, Number, SymbolRep, InfinityRep, NaNRep, Accessor];
|
||||
const reps = [RegExp, StyleSheet, Event, DateTime, CommentNode, ElementNode, TextNode, Attribute, Func, PromiseRep, ArrayRep, Document, DocumentType, Window, ObjectWithText, ObjectWithURL, ErrorRep, GripArray, GripMap, GripMapEntry, Grip, Undefined, Null, StringRep, Number, SymbolRep, InfinityRep, NaNRep, Accessor, Obj];
|
||||
|
||||
/**
|
||||
* Generic rep that is used for rendering native JS types or an object.
|
||||
@ -698,7 +698,7 @@ const Rep = function (props) {
|
||||
* @param noGrip {Boolean} If true, will only check reps not made for remote
|
||||
* objects.
|
||||
*/
|
||||
function getRep(object, defaultRep = Obj, noGrip = false) {
|
||||
function getRep(object, defaultRep = Grip, noGrip = false) {
|
||||
for (let i = 0; i < reps.length; i++) {
|
||||
const rep = reps[i];
|
||||
try {
|
||||
@ -1153,8 +1153,8 @@ function getLength(object) {
|
||||
return object.length;
|
||||
}
|
||||
|
||||
function supportsObject(object) {
|
||||
return Array.isArray(object) || Object.prototype.toString.call(object) === "[object Arguments]";
|
||||
function supportsObject(object, noGrip = false) {
|
||||
return noGrip && (Array.isArray(object) || Object.prototype.toString.call(object) === "[object Arguments]");
|
||||
}
|
||||
|
||||
const maxLengthMap = new Map();
|
||||
@ -4765,8 +4765,8 @@ function isInterestingProp(value) {
|
||||
return type == "boolean" || type == "number" || type == "string" && value;
|
||||
}
|
||||
|
||||
function supportsObject(object) {
|
||||
return true;
|
||||
function supportsObject(object, noGrip = false) {
|
||||
return noGrip;
|
||||
}
|
||||
|
||||
// Exports from this module
|
||||
|
@ -882,6 +882,7 @@ CreateInterfacePrototypeObject(JSContext* cx, JS::Handle<JSObject*> global,
|
||||
const NativeProperties* properties,
|
||||
const NativeProperties* chromeOnlyProperties,
|
||||
const char* const* unscopableNames,
|
||||
const char* toStringTag,
|
||||
bool isGlobal)
|
||||
{
|
||||
JS::Rooted<JSObject*> ourProto(cx,
|
||||
@ -917,6 +918,21 @@ CreateInterfacePrototypeObject(JSContext* cx, JS::Handle<JSObject*> global,
|
||||
}
|
||||
}
|
||||
|
||||
if (toStringTag) {
|
||||
JS::Rooted<JSString*> toStringTagStr(cx,
|
||||
JS_NewStringCopyZ(cx, toStringTag));
|
||||
if (!toStringTagStr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
JS::Rooted<jsid> toStringTagId(cx,
|
||||
SYMBOL_TO_JSID(JS::GetWellKnownSymbol(cx, JS::SymbolCode::toStringTag)));
|
||||
if (!JS_DefinePropertyById(cx, ourProto, toStringTagId, toStringTagStr,
|
||||
JSPROP_READONLY)) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return ourProto;
|
||||
}
|
||||
|
||||
@ -966,6 +982,7 @@ void
|
||||
CreateInterfaceObjects(JSContext* cx, JS::Handle<JSObject*> global,
|
||||
JS::Handle<JSObject*> protoProto,
|
||||
const js::Class* protoClass, JS::Heap<JSObject*>* protoCache,
|
||||
const char* toStringTag,
|
||||
JS::Handle<JSObject*> constructorProto,
|
||||
const js::Class* constructorClass,
|
||||
unsigned ctorNargs, const NamedConstructor* namedConstructors,
|
||||
@ -1003,6 +1020,8 @@ CreateInterfaceObjects(JSContext* cx, JS::Handle<JSObject*> global,
|
||||
MOZ_ASSERT(constructorProto || !constructorClass,
|
||||
"Must have a constructor proto if we plan to create a constructor "
|
||||
"object");
|
||||
MOZ_ASSERT(protoClass || !toStringTag,
|
||||
"Must have a prototype object if we have a @@toStringTag");
|
||||
|
||||
bool isChrome = nsContentUtils::ThreadsafeIsSystemCaller(cx);
|
||||
|
||||
@ -1012,7 +1031,7 @@ CreateInterfaceObjects(JSContext* cx, JS::Handle<JSObject*> global,
|
||||
CreateInterfacePrototypeObject(cx, global, protoProto, protoClass,
|
||||
properties,
|
||||
isChrome ? chromeOnlyProperties : nullptr,
|
||||
unscopableNames, isGlobal);
|
||||
unscopableNames, toStringTag, isGlobal);
|
||||
if (!proto) {
|
||||
return;
|
||||
}
|
||||
@ -4096,5 +4115,40 @@ GetPerInterfaceObjectHandle(JSContext* aCx,
|
||||
return JS::Handle<JSObject*>::fromMarkedLocation(entrySlot.address());
|
||||
}
|
||||
|
||||
namespace binding_detail {
|
||||
bool
|
||||
IsGetterEnabled(JSContext* aCx, JS::Handle<JSObject*> aObj,
|
||||
JSJitGetterOp aGetter,
|
||||
const Prefable<const JSPropertySpec>* aAttributes)
|
||||
{
|
||||
MOZ_ASSERT(aAttributes);
|
||||
MOZ_ASSERT(aAttributes->specs);
|
||||
do {
|
||||
if (aAttributes->isEnabled(aCx, aObj)) {
|
||||
const JSPropertySpec* specs = aAttributes->specs;
|
||||
do {
|
||||
MOZ_ASSERT(specs->isAccessor());
|
||||
if (specs->isSelfHosted()) {
|
||||
// It won't have a JSJitGetterOp.
|
||||
continue;
|
||||
}
|
||||
const JSJitInfo* info = specs->accessors.getter.native.info;
|
||||
if (!info) {
|
||||
continue;
|
||||
}
|
||||
MOZ_ASSERT(info->type() == JSJitInfo::OpType::Getter);
|
||||
if (info->getter == aGetter) {
|
||||
return true;
|
||||
}
|
||||
} while ((++specs)->name);
|
||||
}
|
||||
} while ((++aAttributes)->specs);
|
||||
|
||||
// Didn't find it.
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace binding_detail
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
@ -703,6 +703,8 @@ struct NamedConstructor
|
||||
* protoCache a pointer to a JSObject pointer where we should cache the
|
||||
* interface prototype object. This must be null if protoClass is and
|
||||
* vice versa.
|
||||
* toStringTag if not null, a string to define as @@toStringTag on the prototype.
|
||||
* Must be null if protoClass is.
|
||||
* constructorClass is the JSClass to use for the interface object.
|
||||
* This is null if we should not create an interface object or
|
||||
* if it should be a function object.
|
||||
@ -741,6 +743,7 @@ void
|
||||
CreateInterfaceObjects(JSContext* cx, JS::Handle<JSObject*> global,
|
||||
JS::Handle<JSObject*> protoProto,
|
||||
const js::Class* protoClass, JS::Heap<JSObject*>* protoCache,
|
||||
const char* toStringTag,
|
||||
JS::Handle<JSObject*> interfaceProto,
|
||||
const js::Class* constructorClass,
|
||||
unsigned ctorNargs, const NamedConstructor* namedConstructors,
|
||||
@ -3490,6 +3493,14 @@ HTMLConstructor(JSContext* aCx, unsigned aArgc, JS::Value* aVp,
|
||||
constructors::id::ID aConstructorId,
|
||||
prototypes::id::ID aProtoId,
|
||||
CreateInterfaceObjectsMethod aCreator);
|
||||
|
||||
// A method to test whether an attribute with the given JSJitGetterOp getter is
|
||||
// enabled in the given set of prefable proeprty specs. For use for toJSON
|
||||
// conversions. aObj is the object that would be used as the "this" value.
|
||||
bool
|
||||
IsGetterEnabled(JSContext* aCx, JS::Handle<JSObject*> aObj,
|
||||
JSJitGetterOp aGetter,
|
||||
const Prefable<const JSPropertySpec>* aAttributes);
|
||||
} // namespace binding_detail
|
||||
|
||||
} // namespace dom
|
||||
|
@ -517,7 +517,7 @@ class CGDOMJSClass(CGThing):
|
||||
static_assert(${reservedSlots} >= ${slotCount},
|
||||
"Must have enough reserved slots.");
|
||||
""",
|
||||
name=self.descriptor.interface.identifier.name,
|
||||
name=self.descriptor.interface.getClassName(),
|
||||
flags=classFlags,
|
||||
addProperty=ADDPROPERTY_HOOK_NAME if wantsAddProperty(self.descriptor) else 'nullptr',
|
||||
newEnumerate=newEnumerateHook,
|
||||
@ -678,7 +678,7 @@ class CGPrototypeJSClass(CGThing):
|
||||
${protoGetter}
|
||||
};
|
||||
""",
|
||||
name=self.descriptor.interface.identifier.name,
|
||||
name=self.descriptor.interface.getClassName(),
|
||||
slotCount=slotCount,
|
||||
type=type,
|
||||
hooks=NativePropertyHooks(self.descriptor),
|
||||
@ -1075,15 +1075,15 @@ class CGHeaders(CGWrapper):
|
||||
ancestors.append(parent)
|
||||
interfaceDeps.extend(ancestors)
|
||||
|
||||
# Include parent interface headers needed for jsonifier code.
|
||||
# Include parent interface headers needed for default toJSON code.
|
||||
jsonInterfaceParents = []
|
||||
for desc in descriptors:
|
||||
if not desc.operations['Jsonifier']:
|
||||
if not desc.hasDefaultToJSON:
|
||||
continue
|
||||
parent = desc.interface.parent
|
||||
while parent:
|
||||
parentDesc = desc.getDescriptor(parent.identifier.name)
|
||||
if parentDesc.operations['Jsonifier']:
|
||||
if parentDesc.hasDefaultToJSON:
|
||||
jsonInterfaceParents.append(parentDesc.interface)
|
||||
parent = parent.parent
|
||||
interfaceDeps.extend(jsonInterfaceParents)
|
||||
@ -2411,20 +2411,6 @@ class MethodDefiner(PropertyDefiner):
|
||||
self.chrome.append(toStringDesc)
|
||||
else:
|
||||
self.regular.append(toStringDesc)
|
||||
jsonifier = descriptor.operations['Jsonifier']
|
||||
if (jsonifier and
|
||||
unforgeable == MemberIsUnforgeable(jsonifier, descriptor)):
|
||||
toJSONDesc = {
|
||||
"name": "toJSON",
|
||||
"nativeName": jsonifier.identifier.name,
|
||||
"length": 0,
|
||||
"flags": "JSPROP_ENUMERATE",
|
||||
"condition": PropertyDefiner.getControllingCondition(jsonifier, descriptor)
|
||||
}
|
||||
if isChromeOnly(jsonifier):
|
||||
self.chrome.append(toJSONDesc)
|
||||
else:
|
||||
self.regular.append(toJSONDesc)
|
||||
if (unforgeable and
|
||||
descriptor.interface.getExtendedAttribute("Unforgeable")):
|
||||
# Synthesize our valueOf method
|
||||
@ -2878,36 +2864,62 @@ class CGNativeProperties(CGList):
|
||||
return CGList.define(self)
|
||||
|
||||
|
||||
class CGJsonifyAttributesMethod(CGAbstractMethod):
|
||||
class CGCollectJSONAttributesMethod(CGAbstractMethod):
|
||||
"""
|
||||
Generate the JsonifyAttributes method for an interface descriptor
|
||||
Generate the CollectJSONAttributes method for an interface descriptor
|
||||
"""
|
||||
def __init__(self, descriptor):
|
||||
def __init__(self, descriptor, toJSONMethod):
|
||||
args = [Argument('JSContext*', 'aCx'),
|
||||
Argument('JS::Handle<JSObject*>', 'obj'),
|
||||
Argument('%s*' % descriptor.nativeType, 'self'),
|
||||
Argument('JS::Rooted<JSObject*>&', 'aResult')]
|
||||
CGAbstractMethod.__init__(self, descriptor, 'JsonifyAttributes',
|
||||
CGAbstractMethod.__init__(self, descriptor, 'CollectJSONAttributes',
|
||||
'bool', args, canRunScript=True)
|
||||
self.toJSONMethod = toJSONMethod
|
||||
|
||||
def definition_body(self):
|
||||
ret = ''
|
||||
interface = self.descriptor.interface
|
||||
toJSONCondition = PropertyDefiner.getControllingCondition(self.toJSONMethod,
|
||||
self.descriptor)
|
||||
for m in interface.members:
|
||||
if m.isAttr() and not m.isStatic() and m.type.isSerializable():
|
||||
ret += fill(
|
||||
if m.isAttr() and not m.isStatic() and m.type.isJSONType():
|
||||
getAndDefine = fill(
|
||||
"""
|
||||
{ // scope for "temp"
|
||||
JS::Rooted<JS::Value> temp(aCx);
|
||||
if (!get_${name}(aCx, obj, self, JSJitGetterCallArgs(&temp))) {
|
||||
return false;
|
||||
}
|
||||
if (!JS_DefineProperty(aCx, aResult, "${name}", temp, JSPROP_ENUMERATE)) {
|
||||
return false;
|
||||
}
|
||||
JS::Rooted<JS::Value> temp(aCx);
|
||||
if (!get_${name}(aCx, obj, self, JSJitGetterCallArgs(&temp))) {
|
||||
return false;
|
||||
}
|
||||
if (!JS_DefineProperty(aCx, aResult, "${name}", temp, JSPROP_ENUMERATE)) {
|
||||
return false;
|
||||
}
|
||||
""",
|
||||
name=IDLToCIdentifier(m.identifier.name))
|
||||
# Make sure we don't include things which are supposed to be
|
||||
# disabled. Things that either don't have disablers or whose
|
||||
# disablers match the disablers for our toJSON method can't
|
||||
# possibly be disabled, but other things might be.
|
||||
condition = PropertyDefiner.getControllingCondition(m, self.descriptor)
|
||||
if condition.hasDisablers() and condition != toJSONCondition:
|
||||
ret += fill(
|
||||
"""
|
||||
// This is unfortunately a linear scan through sAttributes, but we
|
||||
// only do it for things which _might_ be disabled, which should
|
||||
// help keep the performance problems down.
|
||||
if (IsGetterEnabled(aCx, obj, (JSJitGetterOp)get_${name}, sAttributes)) {
|
||||
$*{getAndDefine}
|
||||
}
|
||||
""",
|
||||
name=IDLToCIdentifier(m.identifier.name),
|
||||
getAndDefine=getAndDefine)
|
||||
else:
|
||||
ret += fill(
|
||||
"""
|
||||
{ // scope for "temp"
|
||||
$*{getAndDefine}
|
||||
}
|
||||
""",
|
||||
getAndDefine=getAndDefine)
|
||||
ret += 'return true;\n'
|
||||
return ret
|
||||
|
||||
@ -3051,12 +3063,19 @@ class CGCreateInterfaceObjectsMethod(CGAbstractMethod):
|
||||
else:
|
||||
chromeProperties = "nullptr"
|
||||
|
||||
toStringTag = self.descriptor.interface.toStringTag
|
||||
if toStringTag:
|
||||
toStringTag = '"%s"' % toStringTag
|
||||
else:
|
||||
toStringTag = "nullptr"
|
||||
|
||||
call = fill(
|
||||
"""
|
||||
JS::Heap<JSObject*>* protoCache = ${protoCache};
|
||||
JS::Heap<JSObject*>* interfaceCache = ${interfaceCache};
|
||||
dom::CreateInterfaceObjects(aCx, aGlobal, ${parentProto},
|
||||
${protoClass}, protoCache,
|
||||
${toStringTag},
|
||||
${constructorProto}, ${interfaceClass}, ${constructArgs}, ${namedConstructors},
|
||||
interfaceCache,
|
||||
${properties},
|
||||
@ -3068,6 +3087,7 @@ class CGCreateInterfaceObjectsMethod(CGAbstractMethod):
|
||||
protoClass=protoClass,
|
||||
parentProto=parentProto,
|
||||
protoCache=protoCache,
|
||||
toStringTag=toStringTag,
|
||||
constructorProto=constructorProto,
|
||||
interfaceClass=interfaceClass,
|
||||
constructArgs=constructArgs,
|
||||
@ -8615,9 +8635,9 @@ class CGMethodPromiseWrapper(CGAbstractStaticMethod):
|
||||
return methodName + "_promiseWrapper"
|
||||
|
||||
|
||||
class CGJsonifierMethod(CGSpecializedMethod):
|
||||
class CGDefaultToJSONMethod(CGSpecializedMethod):
|
||||
def __init__(self, descriptor, method):
|
||||
assert method.isJsonifier()
|
||||
assert method.isDefaultToJSON()
|
||||
CGSpecializedMethod.__init__(self, descriptor, method)
|
||||
|
||||
def definition_body(self):
|
||||
@ -8632,7 +8652,7 @@ class CGJsonifierMethod(CGSpecializedMethod):
|
||||
interface = self.descriptor.interface.parent
|
||||
while interface:
|
||||
descriptor = self.descriptor.getDescriptor(interface.identifier.name)
|
||||
if descriptor.operations['Jsonifier']:
|
||||
if descriptor.hasDefaultToJSON:
|
||||
jsonDescriptors.append(descriptor)
|
||||
interface = interface.parent
|
||||
|
||||
@ -8640,7 +8660,7 @@ class CGJsonifierMethod(CGSpecializedMethod):
|
||||
for descriptor in jsonDescriptors[::-1]:
|
||||
ret += fill(
|
||||
"""
|
||||
if (!${parentclass}::JsonifyAttributes(cx, obj, self, result)) {
|
||||
if (!${parentclass}::CollectJSONAttributes(cx, obj, self, result)) {
|
||||
return false;
|
||||
}
|
||||
""",
|
||||
@ -12238,15 +12258,12 @@ class MemberProperties:
|
||||
self.isCrossOriginMethod = False
|
||||
self.isCrossOriginGetter = False
|
||||
self.isCrossOriginSetter = False
|
||||
self.isJsonifier = False
|
||||
|
||||
|
||||
def memberProperties(m, descriptor):
|
||||
props = MemberProperties()
|
||||
if m.isMethod():
|
||||
if m == descriptor.operations['Jsonifier']:
|
||||
props.isJsonifier = True
|
||||
elif (not m.isIdentifierLess() or m == descriptor.operations['Stringifier']):
|
||||
if (not m.isIdentifierLess() or m == descriptor.operations['Stringifier']):
|
||||
if not m.isStatic() and descriptor.interface.hasInterfacePrototypeObject():
|
||||
if m.getExtendedAttribute("CrossOriginCallable"):
|
||||
props.isCrossOriginMethod = True
|
||||
@ -12286,7 +12303,7 @@ class CGDescriptor(CGThing):
|
||||
" \"Can't inherit from an interface with a different ownership model.\");\n" %
|
||||
toBindingNamespace(descriptor.parentPrototypeName)))
|
||||
|
||||
jsonifierMethod = None
|
||||
defaultToJSONMethod = None
|
||||
crossOriginMethods, crossOriginGetters, crossOriginSetters = set(), set(), set()
|
||||
unscopableNames = list()
|
||||
for n in descriptor.interface.namedConstructors:
|
||||
@ -12302,8 +12319,8 @@ class CGDescriptor(CGThing):
|
||||
if m.getExtendedAttribute("Unscopable"):
|
||||
assert not m.isStatic()
|
||||
unscopableNames.append(m.identifier.name)
|
||||
if props.isJsonifier:
|
||||
jsonifierMethod = m
|
||||
if m.isDefaultToJSON():
|
||||
defaultToJSONMethod = m
|
||||
elif not m.isIdentifierLess() or m == descriptor.operations['Stringifier']:
|
||||
if m.isStatic():
|
||||
assert descriptor.interface.hasInterfaceObject()
|
||||
@ -12364,10 +12381,9 @@ class CGDescriptor(CGThing):
|
||||
if m.isConst() and m.type.isPrimitive():
|
||||
cgThings.append(CGConstDefinition(m))
|
||||
|
||||
if jsonifierMethod:
|
||||
cgThings.append(CGJsonifyAttributesMethod(descriptor))
|
||||
cgThings.append(CGJsonifierMethod(descriptor, jsonifierMethod))
|
||||
cgThings.append(CGMemberJITInfo(descriptor, jsonifierMethod))
|
||||
if defaultToJSONMethod:
|
||||
cgThings.append(CGDefaultToJSONMethod(descriptor, defaultToJSONMethod))
|
||||
cgThings.append(CGMemberJITInfo(descriptor, defaultToJSONMethod))
|
||||
if descriptor.interface.isNavigatorProperty():
|
||||
cgThings.append(CGConstructNavigatorObject(descriptor))
|
||||
|
||||
@ -12391,6 +12407,11 @@ class CGDescriptor(CGThing):
|
||||
cgThings.append(CGGeneric(define=str(properties)))
|
||||
cgThings.append(CGNativeProperties(descriptor, properties))
|
||||
|
||||
if defaultToJSONMethod:
|
||||
# Now that we know about our property arrays, we can
|
||||
# output our "collect attribute values" method, which uses those.
|
||||
cgThings.append(CGCollectJSONAttributesMethod(descriptor, defaultToJSONMethod))
|
||||
|
||||
if descriptor.interface.hasInterfaceObject():
|
||||
cgThings.append(CGClassConstructor(descriptor,
|
||||
descriptor.interface.ctor()))
|
||||
@ -14692,9 +14713,6 @@ class CGBindingImplClass(CGClass):
|
||||
else:
|
||||
# We already added this method
|
||||
return
|
||||
if name == "Jsonifier":
|
||||
# We already added this method
|
||||
return
|
||||
self.methodDecls.append(
|
||||
CGNativeMember(descriptor, op,
|
||||
name,
|
||||
|
@ -417,10 +417,11 @@ class Descriptor(DescriptorProvider):
|
||||
'NamedDeleter': None,
|
||||
'Stringifier': None,
|
||||
'LegacyCaller': None,
|
||||
'Jsonifier': None
|
||||
}
|
||||
|
||||
# Stringifiers and jsonifiers need to be set up whether an interface is
|
||||
self.hasDefaultToJSON = False
|
||||
|
||||
# Stringifiers need to be set up whether an interface is
|
||||
# concrete or not, because they're actually prototype methods and hence
|
||||
# can apply to instances of descendant interfaces. Legacy callers and
|
||||
# named/indexed operations only need to be set up on concrete
|
||||
@ -436,8 +437,8 @@ class Descriptor(DescriptorProvider):
|
||||
for m in self.interface.members:
|
||||
if m.isMethod() and m.isStringifier():
|
||||
addOperation('Stringifier', m)
|
||||
if m.isMethod() and m.isJsonifier():
|
||||
addOperation('Jsonifier', m)
|
||||
if m.isMethod() and m.isDefaultToJSON():
|
||||
self.hasDefaultToJSON = True
|
||||
|
||||
if self.concrete:
|
||||
self.proxy = False
|
||||
|
@ -684,7 +684,7 @@ def globalNameSetToExposureSet(globalScope, nameSet, exposureSet):
|
||||
|
||||
class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
|
||||
def __init__(self, location, parentScope, name, parent, members,
|
||||
isKnownNonPartial):
|
||||
isKnownNonPartial, toStringTag):
|
||||
assert isinstance(parentScope, IDLScope)
|
||||
assert isinstance(name, IDLUnresolvedIdentifier)
|
||||
assert isKnownNonPartial or not parent
|
||||
@ -722,6 +722,8 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
|
||||
# interface we're iterating for in order to get its nativeType.
|
||||
self.iterableInterface = None
|
||||
|
||||
self.toStringTag = toStringTag
|
||||
|
||||
IDLObjectWithScope.__init__(self, location, parentScope, name)
|
||||
IDLExposureMixins.__init__(self, location)
|
||||
|
||||
@ -1017,10 +1019,9 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
|
||||
[self.location])
|
||||
|
||||
for m in self.members:
|
||||
if ((m.isMethod() and m.isJsonifier()) or
|
||||
m.identifier.name == "toJSON"):
|
||||
if m.identifier.name == "toJSON":
|
||||
raise WebIDLError("Unforgeable interface %s has a "
|
||||
"jsonifier so we won't be able to add "
|
||||
"toJSON so we won't be able to add "
|
||||
"one ourselves" % self.identifier.name,
|
||||
[self.location, m.location])
|
||||
|
||||
@ -1115,15 +1116,12 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
|
||||
memberType = "deleters"
|
||||
elif member.isStringifier():
|
||||
memberType = "stringifiers"
|
||||
elif member.isJsonifier():
|
||||
memberType = "jsonifiers"
|
||||
elif member.isLegacycaller():
|
||||
memberType = "legacycallers"
|
||||
else:
|
||||
continue
|
||||
|
||||
if (memberType != "stringifiers" and memberType != "legacycallers" and
|
||||
memberType != "jsonifiers"):
|
||||
if (memberType != "stringifiers" and memberType != "legacycallers"):
|
||||
if member.isNamed():
|
||||
memberType = "named " + memberType
|
||||
else:
|
||||
@ -1579,9 +1577,12 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
|
||||
|
||||
class IDLInterface(IDLInterfaceOrNamespace):
|
||||
def __init__(self, location, parentScope, name, parent, members,
|
||||
isKnownNonPartial):
|
||||
isKnownNonPartial, classNameOverride=None,
|
||||
toStringTag=None):
|
||||
IDLInterfaceOrNamespace.__init__(self, location, parentScope, name,
|
||||
parent, members, isKnownNonPartial)
|
||||
parent, members, isKnownNonPartial,
|
||||
toStringTag)
|
||||
self.classNameOverride = classNameOverride
|
||||
|
||||
def __str__(self):
|
||||
return "Interface '%s'" % self.identifier.name
|
||||
@ -1589,6 +1590,11 @@ class IDLInterface(IDLInterfaceOrNamespace):
|
||||
def isInterface(self):
|
||||
return True
|
||||
|
||||
def getClassName(self):
|
||||
if self.classNameOverride:
|
||||
return self.classNameOverride
|
||||
return self.identifier.name
|
||||
|
||||
def addExtendedAttributes(self, attrs):
|
||||
for attr in attrs:
|
||||
identifier = attr.identifier()
|
||||
@ -1766,7 +1772,8 @@ class IDLInterface(IDLInterfaceOrNamespace):
|
||||
class IDLNamespace(IDLInterfaceOrNamespace):
|
||||
def __init__(self, location, parentScope, name, members, isKnownNonPartial):
|
||||
IDLInterfaceOrNamespace.__init__(self, location, parentScope, name,
|
||||
None, members, isKnownNonPartial)
|
||||
None, members, isKnownNonPartial,
|
||||
toStringTag=None)
|
||||
|
||||
def __str__(self):
|
||||
return "Namespace '%s'" % self.identifier.name
|
||||
@ -2149,7 +2156,7 @@ class IDLType(IDLObject):
|
||||
# Should only call this on float types
|
||||
assert self.isFloat()
|
||||
|
||||
def isSerializable(self):
|
||||
def isJSONType(self):
|
||||
return False
|
||||
|
||||
def tag(self):
|
||||
@ -2347,8 +2354,8 @@ class IDLNullableType(IDLParametrizedType):
|
||||
def isUnion(self):
|
||||
return self.inner.isUnion()
|
||||
|
||||
def isSerializable(self):
|
||||
return self.inner.isSerializable()
|
||||
def isJSONType(self):
|
||||
return self.inner.isJSONType()
|
||||
|
||||
def tag(self):
|
||||
return self.inner.tag()
|
||||
@ -2427,8 +2434,8 @@ class IDLSequenceType(IDLParametrizedType):
|
||||
def isEnum(self):
|
||||
return False
|
||||
|
||||
def isSerializable(self):
|
||||
return self.inner.isSerializable()
|
||||
def isJSONType(self):
|
||||
return self.inner.isJSONType()
|
||||
|
||||
def tag(self):
|
||||
return IDLType.Tags.sequence
|
||||
@ -2473,6 +2480,9 @@ class IDLRecordType(IDLParametrizedType):
|
||||
def isRecord(self):
|
||||
return True
|
||||
|
||||
def isJSONType(self):
|
||||
return self.inner.isJSONType()
|
||||
|
||||
def tag(self):
|
||||
return IDLType.Tags.record
|
||||
|
||||
@ -2522,8 +2532,8 @@ class IDLUnionType(IDLType):
|
||||
def isUnion(self):
|
||||
return True
|
||||
|
||||
def isSerializable(self):
|
||||
return all(m.isSerializable() for m in self.memberTypes)
|
||||
def isJSONType(self):
|
||||
return all(m.isJSONType() for m in self.memberTypes)
|
||||
|
||||
def includesRestrictedFloat(self):
|
||||
return any(t.includesRestrictedFloat() for t in self.memberTypes)
|
||||
@ -2665,6 +2675,9 @@ class IDLTypedefType(IDLType):
|
||||
def isVoid(self):
|
||||
return self.inner.isVoid()
|
||||
|
||||
def isJSONType(self):
|
||||
return self.inner.isJSONType()
|
||||
|
||||
def isSequence(self):
|
||||
return self.inner.isSequence()
|
||||
|
||||
@ -2806,15 +2819,20 @@ class IDLWrapperType(IDLType):
|
||||
def isEnum(self):
|
||||
return isinstance(self.inner, IDLEnum)
|
||||
|
||||
def isSerializable(self):
|
||||
def isJSONType(self):
|
||||
if self.isInterface():
|
||||
if self.inner.isExternal():
|
||||
return False
|
||||
return any(m.isMethod() and m.isJsonifier() for m in self.inner.members)
|
||||
iface = self.inner
|
||||
while iface:
|
||||
if any(m.isMethod() and m.isToJSON() for m in self.inner.members):
|
||||
return True
|
||||
iface = iface.parent
|
||||
return False
|
||||
elif self.isEnum():
|
||||
return True
|
||||
elif self.isDictionary():
|
||||
return all(m.type.isSerializable() for m in self.inner.members)
|
||||
return all(m.type.isJSONType() for m in self.inner.members)
|
||||
else:
|
||||
raise WebIDLError("IDLWrapperType wraps type %s that we don't know if "
|
||||
"is serializable" % type(self.inner), [self.location])
|
||||
@ -3100,8 +3118,8 @@ class IDLBuiltinType(IDLType):
|
||||
return (self._typeTag == IDLBuiltinType.Types.unrestricted_float or
|
||||
self._typeTag == IDLBuiltinType.Types.unrestricted_double)
|
||||
|
||||
def isSerializable(self):
|
||||
return self.isPrimitive() or self.isString() or self.isDate()
|
||||
def isJSONType(self):
|
||||
return self.isPrimitive() or self.isString() or self.isObject()
|
||||
|
||||
def includesRestrictedFloat(self):
|
||||
return self.isFloat() and not self.isUnrestricted()
|
||||
@ -4660,7 +4678,7 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
|
||||
def __init__(self, location, identifier, returnType, arguments,
|
||||
static=False, getter=False, setter=False,
|
||||
deleter=False, specialType=NamedOrIndexed.Neither,
|
||||
legacycaller=False, stringifier=False, jsonifier=False,
|
||||
legacycaller=False, stringifier=False,
|
||||
maplikeOrSetlikeOrIterable=None, htmlConstructor=False):
|
||||
# REVIEW: specialType is NamedOrIndexed -- wow, this is messed up.
|
||||
IDLInterfaceMember.__init__(self, location, identifier,
|
||||
@ -4685,8 +4703,6 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
|
||||
self._legacycaller = legacycaller
|
||||
assert isinstance(stringifier, bool)
|
||||
self._stringifier = stringifier
|
||||
assert isinstance(jsonifier, bool)
|
||||
self._jsonifier = jsonifier
|
||||
assert maplikeOrSetlikeOrIterable is None or isinstance(maplikeOrSetlikeOrIterable, IDLMaplikeOrSetlikeOrIterableBase)
|
||||
self.maplikeOrSetlikeOrIterable = maplikeOrSetlikeOrIterable
|
||||
assert isinstance(htmlConstructor, bool)
|
||||
@ -4734,12 +4750,6 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
|
||||
assert len(overload.arguments) == 0
|
||||
assert overload.returnType == BuiltinTypes[IDLBuiltinType.Types.domstring]
|
||||
|
||||
if self._jsonifier:
|
||||
assert len(self._overloads) == 1
|
||||
overload = self._overloads[0]
|
||||
assert len(overload.arguments) == 0
|
||||
assert overload.returnType == BuiltinTypes[IDLBuiltinType.Types.object]
|
||||
|
||||
def isStatic(self):
|
||||
return self._static
|
||||
|
||||
@ -4771,8 +4781,11 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
|
||||
def isStringifier(self):
|
||||
return self._stringifier
|
||||
|
||||
def isJsonifier(self):
|
||||
return self._jsonifier
|
||||
def isToJSON(self):
|
||||
return self.identifier.name == "toJSON"
|
||||
|
||||
def isDefaultToJSON(self):
|
||||
return self.isToJSON() and self.getExtendedAttribute("Default")
|
||||
|
||||
def isMaplikeOrSetlikeOrIterableMethod(self):
|
||||
"""
|
||||
@ -4786,8 +4799,7 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
|
||||
self.isSetter() or
|
||||
self.isDeleter() or
|
||||
self.isLegacycaller() or
|
||||
self.isStringifier() or
|
||||
self.isJsonifier())
|
||||
self.isStringifier())
|
||||
|
||||
def isHTMLConstructor(self):
|
||||
return self._htmlConstructor
|
||||
@ -4843,8 +4855,6 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
|
||||
assert not method.isDeleter()
|
||||
assert not self.isStringifier()
|
||||
assert not method.isStringifier()
|
||||
assert not self.isJsonifier()
|
||||
assert not method.isJsonifier()
|
||||
assert not self.isHTMLConstructor()
|
||||
assert not method.isHTMLConstructor()
|
||||
|
||||
@ -4967,6 +4977,19 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
|
||||
" methods on JS-implemented classes only.",
|
||||
[self.location])
|
||||
|
||||
# Ensure that toJSON methods satisfy the spec constraints on them.
|
||||
if self.identifier.name == "toJSON":
|
||||
if len(self.signatures()) != 1:
|
||||
raise WebIDLError("toJSON method has multiple overloads",
|
||||
[self._overloads[0].location,
|
||||
self._overloads[1].location])
|
||||
if len(self.signatures()[0][1]) != 0:
|
||||
raise WebIDLError("toJSON method has arguments",
|
||||
[self.location])
|
||||
if not self.signatures()[0][0].isJSONType():
|
||||
raise WebIDLError("toJSON method has non-JSON return type",
|
||||
[self.location])
|
||||
|
||||
def overloadsForArgCount(self, argc):
|
||||
return [overload for overload in self._overloads if
|
||||
len(overload.arguments) == argc or
|
||||
@ -5100,6 +5123,14 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
|
||||
raise WebIDLError("[CEReactions] is only allowed on operation, "
|
||||
"attribute, setter, and deleter",
|
||||
[attr.location, self.location])
|
||||
elif identifier == "Default":
|
||||
if not attr.noArguments():
|
||||
raise WebIDLError("[Default] must take no arguments",
|
||||
[attr.location])
|
||||
|
||||
if not self.isToJSON():
|
||||
raise WebIDLError("[Default] is only allowed on toJSON operations",
|
||||
[attr.location, self.location])
|
||||
elif (identifier == "Throws" or
|
||||
identifier == "CanOOM" or
|
||||
identifier == "NewObject" or
|
||||
@ -5287,7 +5318,6 @@ class Tokenizer(object):
|
||||
"false": "FALSE",
|
||||
"serializer": "SERIALIZER",
|
||||
"stringifier": "STRINGIFIER",
|
||||
"jsonifier": "JSONIFIER",
|
||||
"unrestricted": "UNRESTRICTED",
|
||||
"attribute": "ATTRIBUTE",
|
||||
"readonly": "READONLY",
|
||||
@ -6118,19 +6148,6 @@ class Parser(Tokenizer):
|
||||
stringifier=True)
|
||||
p[0] = method
|
||||
|
||||
def p_Jsonifier(self, p):
|
||||
"""
|
||||
Operation : JSONIFIER SEMICOLON
|
||||
"""
|
||||
identifier = IDLUnresolvedIdentifier(BuiltinLocation("<auto-generated-identifier>"),
|
||||
"__jsonifier", allowDoubleUnderscore=True)
|
||||
method = IDLMethod(self.getLocation(p, 1),
|
||||
identifier,
|
||||
returnType=BuiltinTypes[IDLBuiltinType.Types.object],
|
||||
arguments=[],
|
||||
jsonifier=True)
|
||||
p[0] = method
|
||||
|
||||
def p_QualifierStatic(self, p):
|
||||
"""
|
||||
Qualifier : STATIC
|
||||
@ -6284,7 +6301,6 @@ class Parser(Tokenizer):
|
||||
| SETTER
|
||||
| STATIC
|
||||
| STRINGIFIER
|
||||
| JSONIFIER
|
||||
| TYPEDEF
|
||||
| UNRESTRICTED
|
||||
| NAMESPACE
|
||||
@ -6436,7 +6452,6 @@ class Parser(Tokenizer):
|
||||
| SHORT
|
||||
| STATIC
|
||||
| STRINGIFIER
|
||||
| JSONIFIER
|
||||
| TRUE
|
||||
| TYPEDEF
|
||||
| UNSIGNED
|
||||
@ -6952,9 +6967,12 @@ class Parser(Tokenizer):
|
||||
nextMethod.addExtendedAttributes([simpleExtendedAttr("Throws")])
|
||||
itr_ident = IDLUnresolvedIdentifier(iface.location,
|
||||
iface.identifier.name + "Iterator")
|
||||
toStringTag = iface.identifier.name + " Iterator"
|
||||
itr_iface = IDLInterface(iface.location, self.globalScope(),
|
||||
itr_ident, None, [nextMethod],
|
||||
isKnownNonPartial=True)
|
||||
isKnownNonPartial=True,
|
||||
classNameOverride=toStringTag,
|
||||
toStringTag=toStringTag)
|
||||
itr_iface.addExtendedAttributes([simpleExtendedAttr("NoInterfaceObject")])
|
||||
# Make sure the exposure set for the iterator interface is the
|
||||
# same as the exposure set for the iterable interface, because
|
||||
|
@ -131,17 +131,3 @@ def WebIDLTest(parser, harness):
|
||||
harness.ok(threw,
|
||||
"Should have thrown for [CEReactions] used on a stringifier")
|
||||
|
||||
parser = parser.reset()
|
||||
threw = False
|
||||
try:
|
||||
parser.parse("""
|
||||
interface Foo {
|
||||
[CEReactions] jsonifier;
|
||||
};
|
||||
""")
|
||||
|
||||
results = parser.finish()
|
||||
except:
|
||||
threw = True
|
||||
|
||||
harness.ok(threw, "Should have thrown for [CEReactions] used on a jsonifier")
|
||||
|
59
dom/bindings/parser/tests/test_toJSON.py
Normal file
59
dom/bindings/parser/tests/test_toJSON.py
Normal file
@ -0,0 +1,59 @@
|
||||
def WebIDLTest(parser, harness):
|
||||
threw = False
|
||||
try:
|
||||
parser.parse(
|
||||
"""
|
||||
interface Test {
|
||||
object toJSON();
|
||||
};
|
||||
""")
|
||||
results = parser.finish()
|
||||
except:
|
||||
threw = True
|
||||
harness.ok(not threw, "Should allow a toJSON method.")
|
||||
|
||||
parser = parser.reset()
|
||||
threw = False
|
||||
try:
|
||||
parser.parse(
|
||||
"""
|
||||
interface Test {
|
||||
object toJSON(object arg);
|
||||
object toJSON(long arg);
|
||||
};
|
||||
""")
|
||||
results = parser.finish()
|
||||
except:
|
||||
threw = True
|
||||
harness.ok(threw, "Should not allow overloads of a toJSON method.")
|
||||
|
||||
parser = parser.reset()
|
||||
threw = False
|
||||
try:
|
||||
parser.parse(
|
||||
"""
|
||||
interface Test {
|
||||
object toJSON(object arg);
|
||||
};
|
||||
""")
|
||||
results = parser.finish()
|
||||
except:
|
||||
threw = True
|
||||
harness.ok(threw, "Should not allow a toJSON method with arguments.")
|
||||
|
||||
parser = parser.reset()
|
||||
threw = False
|
||||
try:
|
||||
parser.parse(
|
||||
"""
|
||||
interface Test {
|
||||
any toJSON();
|
||||
};
|
||||
""")
|
||||
results = parser.finish()
|
||||
except:
|
||||
threw = True
|
||||
harness.ok(threw, "Should not allow a toJSON method with a non-JSON return type.")
|
||||
|
||||
# We should probably write some tests here about what types are
|
||||
# considered JSON types. Bug 1462537.
|
@ -945,12 +945,12 @@ public:
|
||||
TestInterface* PutForwardsAttr();
|
||||
TestInterface* PutForwardsAttr2();
|
||||
TestInterface* PutForwardsAttr3();
|
||||
void GetJsonifierShouldSkipThis(JSContext*, JS::MutableHandle<JS::Value>);
|
||||
void SetJsonifierShouldSkipThis(JSContext*, JS::Rooted<JS::Value>&);
|
||||
TestParentInterface* JsonifierShouldSkipThis2();
|
||||
void SetJsonifierShouldSkipThis2(TestParentInterface&);
|
||||
TestCallbackInterface* JsonifierShouldSkipThis3();
|
||||
void SetJsonifierShouldSkipThis3(TestCallbackInterface&);
|
||||
void GetToJSONShouldSkipThis(JSContext*, JS::MutableHandle<JS::Value>);
|
||||
void SetToJSONShouldSkipThis(JSContext*, JS::Rooted<JS::Value>&);
|
||||
TestParentInterface* ToJSONShouldSkipThis2();
|
||||
void SetToJSONShouldSkipThis2(TestParentInterface&);
|
||||
TestCallbackInterface* ToJSONShouldSkipThis3();
|
||||
void SetToJSONShouldSkipThis3(TestCallbackInterface&);
|
||||
void ThrowingMethod(ErrorResult& aRv);
|
||||
bool GetThrowingAttr(ErrorResult& aRv) const;
|
||||
void SetThrowingAttr(bool arg, ErrorResult& aRv);
|
||||
|
@ -963,10 +963,10 @@ interface TestInterface {
|
||||
optional Dict arg3, optional double arg4 = 5.0,
|
||||
optional float arg5);
|
||||
|
||||
attribute any jsonifierShouldSkipThis;
|
||||
attribute TestParentInterface jsonifierShouldSkipThis2;
|
||||
attribute TestCallbackInterface jsonifierShouldSkipThis3;
|
||||
jsonifier;
|
||||
attribute any toJSONShouldSkipThis;
|
||||
attribute TestParentInterface toJSONShouldSkipThis2;
|
||||
attribute TestCallbackInterface toJSONShouldSkipThis3;
|
||||
[Default] object toJSON();
|
||||
|
||||
attribute byte dashed-attribute;
|
||||
void dashed-method();
|
||||
|
@ -796,10 +796,10 @@ interface TestExampleInterface {
|
||||
optional TestInterface? arg2 = null,
|
||||
optional Dict arg3, optional double arg4 = 5.0,
|
||||
optional float arg5);
|
||||
attribute any jsonifierShouldSkipThis;
|
||||
attribute TestParentInterface jsonifierShouldSkipThis2;
|
||||
attribute TestCallbackInterface jsonifierShouldSkipThis3;
|
||||
jsonifier;
|
||||
attribute any toJSONShouldSkipThis;
|
||||
attribute TestParentInterface toJSONShouldSkipThis2;
|
||||
attribute TestCallbackInterface toJSONShouldSkipThis3;
|
||||
[Default] object toJSON();
|
||||
|
||||
attribute byte dashed-attribute;
|
||||
void dashed-method();
|
||||
|
@ -814,10 +814,10 @@ interface TestJSImplInterface {
|
||||
optional TestInterface? arg2 = null,
|
||||
optional Dict arg3, optional double arg4 = 5.0,
|
||||
optional float arg5);
|
||||
attribute any jsonifierShouldSkipThis;
|
||||
attribute TestParentInterface jsonifierShouldSkipThis2;
|
||||
attribute TestCallbackInterface jsonifierShouldSkipThis3;
|
||||
jsonifier;
|
||||
attribute any toJSONShouldSkipThis;
|
||||
attribute TestParentInterface toJSONShouldSkipThis2;
|
||||
attribute TestCallbackInterface toJSONShouldSkipThis3;
|
||||
[Default] object toJSON();
|
||||
|
||||
attribute byte dashed-attribute;
|
||||
void dashed-method();
|
||||
|
@ -171,7 +171,7 @@
|
||||
is(entry.value, undefined, "IterableDouble: Entry iterator value should be undefined");
|
||||
is(entry.done, true, "IterableDouble: Entry iterator done should be true");
|
||||
is(Object.prototype.toString.call(Object.getPrototypeOf(key_itr)),
|
||||
"[object TestInterfaceIterableDoubleIteratorPrototype]",
|
||||
"[object TestInterfaceIterableDouble Iterator]",
|
||||
"iterator prototype should have the right brand");
|
||||
|
||||
// Simple dual type iterable creation and functionality test
|
||||
@ -231,7 +231,7 @@
|
||||
is(entry.value, undefined, "IterableDoubleUnion: Entry iterator value should be undefined");
|
||||
is(entry.done, true, "IterableDoubleUnion: Entry iterator done should be true");
|
||||
is(Object.prototype.toString.call(Object.getPrototypeOf(key_itr)),
|
||||
"[object TestInterfaceIterableDoubleUnionIteratorPrototype]",
|
||||
"[object TestInterfaceIterableDoubleUnion Iterator]",
|
||||
"iterator prototype should have the right brand");
|
||||
|
||||
SimpleTest.finish();
|
||||
|
@ -19,6 +19,7 @@ support-files =
|
||||
[test_worker_performance_now.html]
|
||||
[test_timeOrigin.html]
|
||||
[test_worker_performance_entries.html]
|
||||
[test_performance_timing_json.html]
|
||||
[test_performance_server_timing.html]
|
||||
scheme = https
|
||||
[test_performance_server_timing_plain_http.html]
|
||||
|
@ -33,6 +33,8 @@ promise_test(t => {
|
||||
return promise.then(list => {
|
||||
assert_equals(list.getEntries().length, 1);
|
||||
assert_equals(list.getEntries()[0].serverTiming, undefined);
|
||||
assert_equals(list.getEntries()[0].toJSON().serverTiming, undefined,
|
||||
"toJSON should not pick up properties that aren't on the object");
|
||||
});
|
||||
}, "server-timing test");
|
||||
|
||||
|
32
dom/performance/tests/test_performance_timing_json.html
Normal file
32
dom/performance/tests/test_performance_timing_json.html
Normal file
@ -0,0 +1,32 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1375829
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug 1375829</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<script type="application/javascript">
|
||||
|
||||
/** Test for Bug 1375829 **/
|
||||
var json = performance.timing.toJSON();
|
||||
|
||||
// Ensure it doesn't have any attributes that performance.timing doesn't have
|
||||
for (let key of Object.keys(json)) {
|
||||
ok(key in performance.timing, key + " should be a property of performance.timing");
|
||||
}
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1375829">Mozilla Bug 1375829</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -20,5 +20,5 @@ interface MediaDeviceInfo {
|
||||
readonly attribute DOMString label;
|
||||
readonly attribute DOMString groupId;
|
||||
|
||||
jsonifier;
|
||||
[Default] object toJSON();
|
||||
};
|
||||
|
@ -10,9 +10,7 @@
|
||||
[SecureContext,
|
||||
Func="mozilla::dom::PaymentRequest::PrefEnabled"]
|
||||
interface PaymentAddress {
|
||||
// TODO: Use serializer once available. (Bug 863402)
|
||||
// serializer = {attribute};
|
||||
jsonifier;
|
||||
[Default] object toJSON();
|
||||
|
||||
readonly attribute DOMString country;
|
||||
// TODO: Use FrozenArray once available. (Bug 1236777)
|
||||
|
@ -16,9 +16,7 @@ enum PaymentComplete {
|
||||
[SecureContext,
|
||||
Func="mozilla::dom::PaymentRequest::PrefEnabled"]
|
||||
interface PaymentResponse {
|
||||
// TODO: Use serializer once available. (Bug 863402)
|
||||
// serializer = {attribute};
|
||||
jsonifier;
|
||||
[Default] object toJSON();
|
||||
|
||||
readonly attribute DOMString requestId;
|
||||
readonly attribute DOMString methodName;
|
||||
|
@ -29,7 +29,7 @@ partial interface Performance {
|
||||
[Constant]
|
||||
readonly attribute PerformanceNavigation navigation;
|
||||
|
||||
jsonifier;
|
||||
[Default] object toJSON();
|
||||
};
|
||||
|
||||
// http://www.w3.org/TR/performance-timeline/#sec-window.performance-attribute
|
||||
|
@ -18,5 +18,5 @@ interface PerformanceEntry
|
||||
readonly attribute DOMHighResTimeStamp startTime;
|
||||
readonly attribute DOMHighResTimeStamp duration;
|
||||
|
||||
jsonifier;
|
||||
[Default] object toJSON();
|
||||
};
|
||||
|
@ -19,5 +19,5 @@ interface PerformanceNavigation {
|
||||
readonly attribute unsigned short type;
|
||||
readonly attribute unsigned short redirectCount;
|
||||
|
||||
jsonifier;
|
||||
[Default] object toJSON();
|
||||
};
|
||||
|
@ -29,5 +29,5 @@ interface PerformanceNavigationTiming : PerformanceResourceTiming {
|
||||
readonly attribute NavigationType type;
|
||||
readonly attribute unsigned short redirectCount;
|
||||
|
||||
jsonifier;
|
||||
[Default] object toJSON();
|
||||
};
|
||||
|
@ -54,5 +54,5 @@ interface PerformanceResourceTiming : PerformanceEntry
|
||||
[SecureContext, Frozen, Cached, Pure, NeedsSubjectPrincipal]
|
||||
readonly attribute sequence<PerformanceServerTiming> serverTiming;
|
||||
|
||||
jsonifier;
|
||||
[Default] object toJSON();
|
||||
};
|
||||
|
@ -16,5 +16,5 @@ interface PerformanceServerTiming {
|
||||
readonly attribute DOMHighResTimeStamp duration;
|
||||
readonly attribute DOMString description;
|
||||
|
||||
jsonifier;
|
||||
[Default] object toJSON();
|
||||
};
|
||||
|
@ -45,5 +45,5 @@ interface PerformanceTiming {
|
||||
[Pref="dom.performance.time_to_dom_content_flushed.enabled"]
|
||||
readonly attribute unsigned long long timeToDOMContentFlushed;
|
||||
|
||||
jsonifier;
|
||||
[Default] object toJSON();
|
||||
};
|
||||
|
@ -21,5 +21,5 @@ interface RTCIceCandidate {
|
||||
attribute DOMString? sdpMid;
|
||||
attribute unsigned short? sdpMLineIndex;
|
||||
|
||||
jsonifier;
|
||||
[Default] object toJSON();
|
||||
};
|
||||
|
@ -27,5 +27,5 @@ interface RTCSessionDescription {
|
||||
attribute RTCSdpType type;
|
||||
attribute DOMString sdp;
|
||||
|
||||
jsonifier;
|
||||
[Default] object toJSON();
|
||||
};
|
||||
|
@ -266,22 +266,6 @@ BaselineCacheIRCompiler::emitGuardGroup()
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
BaselineCacheIRCompiler::emitGuardGroupHasUnanalyzedNewScript()
|
||||
{
|
||||
Address addr(stubAddress(reader.stubOffset()));
|
||||
AutoScratchRegister scratch1(allocator, masm);
|
||||
AutoScratchRegister scratch2(allocator, masm);
|
||||
|
||||
FailurePath* failure;
|
||||
if (!addFailurePath(&failure))
|
||||
return false;
|
||||
|
||||
masm.loadPtr(addr, scratch1);
|
||||
masm.guardGroupHasUnanalyzedNewScript(scratch1, scratch2, failure->label());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
BaselineCacheIRCompiler::emitGuardProto()
|
||||
{
|
||||
|
@ -2992,8 +2992,11 @@ void CacheIRCompiler::emitLoadStubFieldConstant(StubFieldOffset val, Register de
|
||||
case StubField::Type::String:
|
||||
masm.movePtr(ImmGCPtr(stringStubField(val.getOffset())), dest);
|
||||
break;
|
||||
case StubField::Type::ObjectGroup:
|
||||
masm.movePtr(ImmGCPtr(groupStubField(val.getOffset())), dest);
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("Unhandled stub field constant type");
|
||||
MOZ_CRASH("Unhandled stub field constant type");
|
||||
}
|
||||
}
|
||||
|
||||
@ -3112,4 +3115,20 @@ CacheIRCompiler::emitMegamorphicLoadSlotResult()
|
||||
masm.speculationBarrier();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
CacheIRCompiler::emitGuardGroupHasUnanalyzedNewScript()
|
||||
{
|
||||
StubFieldOffset group(reader.stubOffset(), StubField::Type::ObjectGroup);
|
||||
AutoScratchRegister scratch1(allocator, masm);
|
||||
AutoScratchRegister scratch2(allocator, masm);
|
||||
|
||||
FailurePath* failure;
|
||||
if (!addFailurePath(&failure))
|
||||
return false;
|
||||
|
||||
emitLoadStubField(group, scratch1);
|
||||
masm.guardGroupHasUnanalyzedNewScript(scratch1, scratch2, failure->label());
|
||||
return true;
|
||||
}
|
@ -27,6 +27,7 @@ namespace jit {
|
||||
_(GuardIsInt32Index) \
|
||||
_(GuardType) \
|
||||
_(GuardClass) \
|
||||
_(GuardGroupHasUnanalyzedNewScript) \
|
||||
_(GuardIsNativeFunction) \
|
||||
_(GuardIsNativeObject) \
|
||||
_(GuardIsProxy) \
|
||||
|
@ -657,22 +657,6 @@ IonCacheIRCompiler::emitGuardGroup()
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
IonCacheIRCompiler::emitGuardGroupHasUnanalyzedNewScript()
|
||||
{
|
||||
ObjectGroup* group = groupStubField(reader.stubOffset());
|
||||
AutoScratchRegister scratch1(allocator, masm);
|
||||
AutoScratchRegister scratch2(allocator, masm);
|
||||
|
||||
FailurePath* failure;
|
||||
if (!addFailurePath(&failure))
|
||||
return false;
|
||||
|
||||
masm.movePtr(ImmGCPtr(group), scratch1);
|
||||
masm.guardGroupHasUnanalyzedNewScript(scratch1, scratch2, failure->label());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
IonCacheIRCompiler::emitGuardProto()
|
||||
{
|
||||
|
@ -422,7 +422,7 @@ def run_test_harness(parser, options):
|
||||
if retVal:
|
||||
return retVal
|
||||
|
||||
if options.printDeviceInfo:
|
||||
if options.printDeviceInfo and not options.verify:
|
||||
reftest.printDeviceInfo()
|
||||
|
||||
retVal = 0
|
||||
@ -438,7 +438,7 @@ def run_test_harness(parser, options):
|
||||
|
||||
reftest.stopWebServer(options)
|
||||
|
||||
if options.printDeviceInfo:
|
||||
if options.printDeviceInfo and not options.verify:
|
||||
reftest.printDeviceInfo(printLogcat=True)
|
||||
|
||||
return retVal
|
||||
|
@ -135,7 +135,6 @@ sm-tsan-linux64/opt:
|
||||
job-name: sm-tsan-linux64-opt
|
||||
treeherder:
|
||||
symbol: SM(tsan)
|
||||
tier: 3
|
||||
platform: linux64/opt
|
||||
run:
|
||||
spidermonkey-variant: tsan
|
||||
|
@ -340,7 +340,7 @@ def run_test_harness(parser, options):
|
||||
|
||||
mochitest = MochiRemote(options)
|
||||
|
||||
if options.log_mach is None:
|
||||
if options.log_mach is None and not options.verify:
|
||||
mochitest.printDeviceInfo()
|
||||
|
||||
try:
|
||||
@ -358,7 +358,7 @@ def run_test_harness(parser, options):
|
||||
traceback.print_exc()
|
||||
retVal = 1
|
||||
|
||||
if options.log_mach is None:
|
||||
if options.log_mach is None and not options.verify:
|
||||
mochitest.printDeviceInfo(printLogcat=True)
|
||||
|
||||
mochitest.message_logger.finish()
|
||||
|
@ -30,6 +30,12 @@ from mozharness.mozilla.testing.codecoverage import CodeCoverageMixin
|
||||
|
||||
|
||||
class AndroidEmulatorTest(TestingMixin, BaseScript, MozbaseMixin, CodeCoverageMixin):
|
||||
"""
|
||||
A mozharness script for Android functional tests (like mochitests and reftests)
|
||||
run on an Android emulator. This script starts and manages an Android emulator
|
||||
for the duration of the required tests. This is like desktop_unittest.py, but
|
||||
for Android emulator test platforms.
|
||||
"""
|
||||
config_options = [[
|
||||
["--test-suite"],
|
||||
{"action": "store",
|
||||
@ -71,8 +77,6 @@ class AndroidEmulatorTest(TestingMixin, BaseScript, MozbaseMixin, CodeCoverageMi
|
||||
'virtualenv_modules': [],
|
||||
'virtualenv_requirements': [],
|
||||
'require_test_zip': True,
|
||||
# IP address of the host as seen from the emulator
|
||||
'remote_webserver': '10.0.2.2',
|
||||
}
|
||||
)
|
||||
|
||||
@ -100,14 +104,6 @@ class AndroidEmulatorTest(TestingMixin, BaseScript, MozbaseMixin, CodeCoverageMi
|
||||
self.sdk_level = None
|
||||
self.xre_path = None
|
||||
|
||||
def _query_tests_dir(self):
|
||||
dirs = self.query_abs_dirs()
|
||||
try:
|
||||
test_dir = self.config["suite_definitions"][self.test_suite]["testsdir"]
|
||||
except Exception:
|
||||
test_dir = self.test_suite
|
||||
return os.path.join(dirs['abs_test_install_dir'], test_dir)
|
||||
|
||||
def query_abs_dirs(self):
|
||||
if self.abs_dirs:
|
||||
return self.abs_dirs
|
||||
@ -142,6 +138,39 @@ class AndroidEmulatorTest(TestingMixin, BaseScript, MozbaseMixin, CodeCoverageMi
|
||||
self.abs_dirs = abs_dirs
|
||||
return self.abs_dirs
|
||||
|
||||
def _query_tests_dir(self, test_suite):
|
||||
dirs = self.query_abs_dirs()
|
||||
try:
|
||||
test_dir = self.config["suite_definitions"][test_suite]["testsdir"]
|
||||
except Exception:
|
||||
test_dir = test_suite
|
||||
return os.path.join(dirs['abs_test_install_dir'], test_dir)
|
||||
|
||||
def _query_package_name(self):
|
||||
if self.app_name is None:
|
||||
# For convenience, assume geckoview.test/geckoview_example when install
|
||||
# target looks like geckoview.
|
||||
if 'androidTest' in self.installer_path:
|
||||
self.app_name = 'org.mozilla.geckoview.test'
|
||||
elif 'geckoview' in self.installer_path:
|
||||
self.app_name = 'org.mozilla.geckoview_example'
|
||||
if self.app_name is None:
|
||||
# Find appname from package-name.txt - assumes download-and-extract
|
||||
# has completed successfully.
|
||||
# The app/package name will typically be org.mozilla.fennec,
|
||||
# but org.mozilla.firefox for release builds, and there may be
|
||||
# other variations. 'aapt dump badging <apk>' could be used as an
|
||||
# alternative to package-name.txt, but introduces a dependency
|
||||
# on aapt, found currently in the Android SDK build-tools component.
|
||||
apk_dir = self.abs_dirs['abs_work_dir']
|
||||
self.apk_path = os.path.join(apk_dir, self.installer_path)
|
||||
unzip = self.query_exe("unzip")
|
||||
package_path = os.path.join(apk_dir, 'package-name.txt')
|
||||
unzip_cmd = [unzip, '-q', '-o', self.apk_path]
|
||||
self.run_command(unzip_cmd, cwd=apk_dir, halt_on_failure=True)
|
||||
self.app_name = str(self.read_from_file(package_path, verbose=True)).rstrip()
|
||||
return self.app_name
|
||||
|
||||
def _launch_emulator(self):
|
||||
env = self.query_env()
|
||||
|
||||
@ -192,13 +221,14 @@ class AndroidEmulatorTest(TestingMixin, BaseScript, MozbaseMixin, CodeCoverageMi
|
||||
if "emulator_extra_args" in self.config:
|
||||
command += self.config["emulator_extra_args"].split()
|
||||
|
||||
tmp_file = tempfile.NamedTemporaryFile(mode='w')
|
||||
tmp_stdout = open(tmp_file.name, 'w')
|
||||
self.info("Trying to start the emulator with this command: %s" % ' '.join(command))
|
||||
proc = subprocess.Popen(command, stdout=tmp_stdout, stderr=tmp_stdout, env=env)
|
||||
dir = self.query_abs_dirs()['abs_blob_upload_dir']
|
||||
tmp_file = tempfile.NamedTemporaryFile(mode='w', prefix='emulator-',
|
||||
suffix='.log', dir=dir, delete=False)
|
||||
self.info("Launching the emulator with: %s" % ' '.join(command))
|
||||
self.info("Writing log to %s" % tmp_file.name)
|
||||
proc = subprocess.Popen(command, stdout=tmp_file, stderr=tmp_file, env=env)
|
||||
return {
|
||||
"process": proc,
|
||||
"tmp_file": tmp_file,
|
||||
}
|
||||
|
||||
def _retry(self, max_attempts, interval, func, description, max_time=0):
|
||||
@ -232,6 +262,10 @@ class AndroidEmulatorTest(TestingMixin, BaseScript, MozbaseMixin, CodeCoverageMi
|
||||
timeout_cmd = ['timeout', '%s' % timeout] + cmd
|
||||
return self._run_proc(timeout_cmd, quiet=quiet)
|
||||
|
||||
def _run_adb_with_timeout(self, timeout, cmd, quiet=False):
|
||||
cmd = [self.adb_path, '-s', self.emulator['device_id']] + cmd
|
||||
return self._run_with_timeout(timeout, cmd, quiet)
|
||||
|
||||
def _run_proc(self, cmd, quiet=False):
|
||||
self.info('Running %s' % subprocess.list2cmdline(cmd))
|
||||
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
@ -254,9 +288,8 @@ class AndroidEmulatorTest(TestingMixin, BaseScript, MozbaseMixin, CodeCoverageMi
|
||||
return False
|
||||
|
||||
def _is_boot_completed(self):
|
||||
boot_cmd = [self.adb_path, '-s', self.emulator['device_id'],
|
||||
'shell', 'getprop', 'sys.boot_completed']
|
||||
out, _ = self._run_with_timeout(30, boot_cmd)
|
||||
boot_cmd = ['shell', 'getprop', 'sys.boot_completed']
|
||||
out, _ = self._run_adb_with_timeout(30, boot_cmd)
|
||||
if out.strip() == '1':
|
||||
return True
|
||||
return False
|
||||
@ -282,11 +315,9 @@ class AndroidEmulatorTest(TestingMixin, BaseScript, MozbaseMixin, CodeCoverageMi
|
||||
def _verify_emulator_and_restart_on_fail(self):
|
||||
emulator_ok = self._verify_emulator()
|
||||
if not emulator_ok:
|
||||
self._dump_host_state()
|
||||
self._screenshot("emulator-startup-screenshot-")
|
||||
self._kill_processes(self.config["emulator_process_name"])
|
||||
self._run_proc(['ps', '-ef'])
|
||||
self._dump_emulator_log()
|
||||
# remove emulator tmp files
|
||||
for dir in glob.glob("/tmp/android-*"):
|
||||
self.rmtree(dir)
|
||||
@ -298,12 +329,10 @@ class AndroidEmulatorTest(TestingMixin, BaseScript, MozbaseMixin, CodeCoverageMi
|
||||
def _install_target_apk(self):
|
||||
install_ok = False
|
||||
if int(self.sdk_level) >= 23:
|
||||
cmd = [self.adb_path, '-s', self.emulator['device_id'], 'install', '-r', '-g',
|
||||
self.installer_path]
|
||||
cmd = ['install', '-r', '-g', self.installer_path]
|
||||
else:
|
||||
cmd = [self.adb_path, '-s', self.emulator['device_id'], 'install', '-r',
|
||||
self.installer_path]
|
||||
out, err = self._run_with_timeout(300, cmd, True)
|
||||
cmd = ['install', '-r', self.installer_path]
|
||||
out, err = self._run_adb_with_timeout(300, cmd, True)
|
||||
if 'Success' in out or 'Success' in err:
|
||||
install_ok = True
|
||||
return install_ok
|
||||
@ -311,31 +340,18 @@ class AndroidEmulatorTest(TestingMixin, BaseScript, MozbaseMixin, CodeCoverageMi
|
||||
def _install_robocop_apk(self):
|
||||
install_ok = False
|
||||
if int(self.sdk_level) >= 23:
|
||||
cmd = [self.adb_path, '-s', self.emulator['device_id'], 'install', '-r', '-g',
|
||||
self.robocop_path]
|
||||
cmd = ['install', '-r', '-g', self.robocop_path]
|
||||
else:
|
||||
cmd = [self.adb_path, '-s', self.emulator['device_id'], 'install', '-r',
|
||||
self.robocop_path]
|
||||
out, err = self._run_with_timeout(300, cmd, True)
|
||||
cmd = ['install', '-r', self.robocop_path]
|
||||
out, err = self._run_adb_with_timeout(300, cmd, True)
|
||||
if 'Success' in out or 'Success' in err:
|
||||
install_ok = True
|
||||
return install_ok
|
||||
|
||||
def _dump_host_state(self):
|
||||
self._run_proc(['ps', '-ef'])
|
||||
self._run_proc(['netstat', '-a', '-p', '-n', '-t', '-u'])
|
||||
|
||||
def _dump_emulator_log(self):
|
||||
self.info("##### %s emulator log begins" % self.emulator["name"])
|
||||
output = self.read_from_file(self.emulator_proc["tmp_file"].name, verbose=False)
|
||||
if output:
|
||||
self.info(output)
|
||||
self.info("##### %s emulator log ends" % self.emulator["name"])
|
||||
|
||||
def _kill_processes(self, process_name):
|
||||
p = subprocess.Popen(['ps', '-A'], stdout=subprocess.PIPE)
|
||||
out, err = p.communicate()
|
||||
self.info("Let's kill every process called %s" % process_name)
|
||||
self.info("Killing every process called %s" % process_name)
|
||||
for line in out.splitlines():
|
||||
if process_name in line:
|
||||
pid = int(line.split(None, 1)[0])
|
||||
@ -348,7 +364,7 @@ class AndroidEmulatorTest(TestingMixin, BaseScript, MozbaseMixin, CodeCoverageMi
|
||||
|
||||
def _screenshot(self, prefix):
|
||||
"""
|
||||
Save a screenshot of the entire screen to the blob upload directory.
|
||||
Save a screenshot of the entire screen to the upload directory.
|
||||
"""
|
||||
dirs = self.query_abs_dirs()
|
||||
utility = os.path.join(self.xre_path, "screentopng")
|
||||
@ -364,35 +380,6 @@ class AndroidEmulatorTest(TestingMixin, BaseScript, MozbaseMixin, CodeCoverageMi
|
||||
except OSError, err:
|
||||
self.warning("Failed to take screenshot: %s" % err.strerror)
|
||||
|
||||
def _query_package_name(self):
|
||||
if self.app_name is None:
|
||||
# For convenience, assume geckoview.test/geckoview_example when install
|
||||
# target looks like geckoview.
|
||||
if 'androidTest' in self.installer_path:
|
||||
self.app_name = 'org.mozilla.geckoview.test'
|
||||
elif 'geckoview' in self.installer_path:
|
||||
self.app_name = 'org.mozilla.geckoview_example'
|
||||
if self.app_name is None:
|
||||
# Find appname from package-name.txt - assumes download-and-extract
|
||||
# has completed successfully.
|
||||
# The app/package name will typically be org.mozilla.fennec,
|
||||
# but org.mozilla.firefox for release builds, and there may be
|
||||
# other variations. 'aapt dump badging <apk>' could be used as an
|
||||
# alternative to package-name.txt, but introduces a dependency
|
||||
# on aapt, found currently in the Android SDK build-tools component.
|
||||
apk_dir = self.abs_dirs['abs_work_dir']
|
||||
self.apk_path = os.path.join(apk_dir, self.installer_path)
|
||||
unzip = self.query_exe("unzip")
|
||||
package_path = os.path.join(apk_dir, 'package-name.txt')
|
||||
unzip_cmd = [unzip, '-q', '-o', self.apk_path]
|
||||
self.run_command(unzip_cmd, cwd=apk_dir, halt_on_failure=True)
|
||||
self.app_name = str(self.read_from_file(package_path, verbose=True)).rstrip()
|
||||
return self.app_name
|
||||
|
||||
def preflight_install(self):
|
||||
# in the base class, this checks for mozinstall, but we don't use it
|
||||
pass
|
||||
|
||||
def _build_command(self):
|
||||
c = self.config
|
||||
dirs = self.query_abs_dirs()
|
||||
@ -404,7 +391,7 @@ class AndroidEmulatorTest(TestingMixin, BaseScript, MozbaseMixin, CodeCoverageMi
|
||||
self.query_python_path('python'),
|
||||
'-u',
|
||||
os.path.join(
|
||||
self._query_tests_dir(),
|
||||
self._query_tests_dir(self.test_suite),
|
||||
self.config["suite_definitions"][self.test_suite]["run_filename"]
|
||||
),
|
||||
]
|
||||
@ -415,7 +402,8 @@ class AndroidEmulatorTest(TestingMixin, BaseScript, MozbaseMixin, CodeCoverageMi
|
||||
error_summary_file = os.path.join(dirs['abs_blob_upload_dir'],
|
||||
'%s_errorsummary.log' % self.test_suite)
|
||||
str_format_values = {
|
||||
'remote_webserver': c['remote_webserver'],
|
||||
# IP address of the host as seen from the emulator
|
||||
'remote_webserver': '10.0.2.2',
|
||||
'xre_path': self.xre_path,
|
||||
'utility_path': self.xre_path,
|
||||
'certs_path': os.path.join(dirs['abs_work_dir'], 'tests/certs'),
|
||||
@ -505,7 +493,6 @@ class AndroidEmulatorTest(TestingMixin, BaseScript, MozbaseMixin, CodeCoverageMi
|
||||
|
||||
def _install_emulator(self):
|
||||
dirs = self.query_abs_dirs()
|
||||
self.mkdir_p(dirs['abs_work_dir'])
|
||||
if self.config.get('emulator_url'):
|
||||
self.download_unpack(self.config['emulator_url'], dirs['abs_work_dir'])
|
||||
elif self.config.get('emulator_manifest'):
|
||||
@ -542,23 +529,24 @@ class AndroidEmulatorTest(TestingMixin, BaseScript, MozbaseMixin, CodeCoverageMi
|
||||
out, _ = self._run_proc(['ps', '-ef'], quiet=True)
|
||||
f.write(out)
|
||||
|
||||
f.write('\n\nHost netstat:\n')
|
||||
out, _ = self._run_proc(['netstat', '-a', '-p', '-n', '-t', '-u'], quiet=True)
|
||||
f.write(out)
|
||||
|
||||
f.write('\n\nEmulator /proc/cpuinfo:\n')
|
||||
cmd = [self.adb_path, '-s', self.emulator['device_id'],
|
||||
'shell', 'cat', '/proc/cpuinfo']
|
||||
out, _ = self._run_with_timeout(30, cmd, quiet=True)
|
||||
cmd = ['shell', 'cat', '/proc/cpuinfo']
|
||||
out, _ = self._run_adb_with_timeout(30, cmd, quiet=True)
|
||||
f.write(out)
|
||||
cpuinfo = out
|
||||
|
||||
f.write('\n\nEmulator /proc/meminfo:\n')
|
||||
cmd = [self.adb_path, '-s', self.emulator['device_id'],
|
||||
'shell', 'cat', '/proc/meminfo']
|
||||
out, _ = self._run_with_timeout(30, cmd, quiet=True)
|
||||
cmd = ['shell', 'cat', '/proc/meminfo']
|
||||
out, _ = self._run_adb_with_timeout(30, cmd, quiet=True)
|
||||
f.write(out)
|
||||
|
||||
f.write('\n\nEmulator process list:\n')
|
||||
cmd = [self.adb_path, '-s', self.emulator['device_id'],
|
||||
'shell', 'ps']
|
||||
out, _ = self._run_with_timeout(30, cmd, quiet=True)
|
||||
cmd = ['shell', 'ps']
|
||||
out, _ = self._run_adb_with_timeout(30, cmd, quiet=True)
|
||||
f.write(out)
|
||||
|
||||
# Search android cpuinfo for "BogoMIPS"; if found and < 250, retry
|
||||
@ -608,6 +596,10 @@ class AndroidEmulatorTest(TestingMixin, BaseScript, MozbaseMixin, CodeCoverageMi
|
||||
# Actions for AndroidEmulatorTest #
|
||||
##########################################
|
||||
|
||||
def preflight_install(self):
|
||||
# in the base class, this checks for mozinstall, but we don't use it
|
||||
pass
|
||||
|
||||
@PreScriptAction('create-virtualenv')
|
||||
def pre_create_virtualenv(self, action):
|
||||
dirs = self.query_abs_dirs()
|
||||
@ -632,6 +624,8 @@ class AndroidEmulatorTest(TestingMixin, BaseScript, MozbaseMixin, CodeCoverageMi
|
||||
'''
|
||||
c = self.config
|
||||
dirs = self.query_abs_dirs()
|
||||
self.mkdir_p(dirs['abs_work_dir'])
|
||||
self.mkdir_p(dirs['abs_blob_upload_dir'])
|
||||
|
||||
# Always start with a clean AVD: AVD includes Android images
|
||||
# which can be stateful.
|
||||
@ -679,7 +673,6 @@ class AndroidEmulatorTest(TestingMixin, BaseScript, MozbaseMixin, CodeCoverageMi
|
||||
Check to see if the emulator can be contacted via adb.
|
||||
If any communication attempt fails, kill the emulator, re-launch, and re-check.
|
||||
'''
|
||||
self.mkdir_p(self.query_abs_dirs()['abs_blob_upload_dir'])
|
||||
max_restarts = 5
|
||||
emulator_ok = self._retry(max_restarts, 10, self._verify_emulator_and_restart_on_fail,
|
||||
"Check emulator")
|
||||
@ -689,7 +682,7 @@ class AndroidEmulatorTest(TestingMixin, BaseScript, MozbaseMixin, CodeCoverageMi
|
||||
self._dump_perf_info()
|
||||
# Start logcat for the emulator. The adb process runs until the
|
||||
# corresponding emulator is killed. Output is written directly to
|
||||
# the blobber upload directory so that it is uploaded automatically
|
||||
# the upload directory so that it is uploaded automatically
|
||||
# at the end of the job.
|
||||
logcat_filename = 'logcat-%s.log' % self.emulator["device_id"]
|
||||
logcat_path = os.path.join(self.abs_dirs['abs_blob_upload_dir'], logcat_filename)
|
||||
@ -698,8 +691,8 @@ class AndroidEmulatorTest(TestingMixin, BaseScript, MozbaseMixin, CodeCoverageMi
|
||||
self.info(logcat_cmd)
|
||||
os.system(logcat_cmd)
|
||||
# Get a post-boot emulator process list for diagnostics
|
||||
ps_cmd = [self.adb_path, '-s', self.emulator["device_id"], 'shell', 'ps']
|
||||
self._run_with_timeout(30, ps_cmd)
|
||||
ps_cmd = ['shell', 'ps']
|
||||
self._run_adb_with_timeout(30, ps_cmd)
|
||||
|
||||
def download_and_extract(self):
|
||||
"""
|
||||
@ -738,9 +731,8 @@ class AndroidEmulatorTest(TestingMixin, BaseScript, MozbaseMixin, CodeCoverageMi
|
||||
assert self.installer_path is not None, \
|
||||
"Either add installer_path to the config or use --installer-path."
|
||||
|
||||
cmd = [self.adb_path, '-s', self.emulator['device_id'], 'shell',
|
||||
'getprop', 'ro.build.version.sdk']
|
||||
self.sdk_level, _ = self._run_with_timeout(30, cmd)
|
||||
cmd = ['shell', 'getprop', 'ro.build.version.sdk']
|
||||
self.sdk_level, _ = self._run_adb_with_timeout(30, cmd)
|
||||
|
||||
# Install Fennec
|
||||
install_ok = self._retry(3, 30, self._install_target_apk, "Install app APK")
|
||||
@ -774,10 +766,7 @@ class AndroidEmulatorTest(TestingMixin, BaseScript, MozbaseMixin, CodeCoverageMi
|
||||
|
||||
cmd = self._build_command()
|
||||
|
||||
try:
|
||||
cwd = self._query_tests_dir()
|
||||
except Exception:
|
||||
self.fatal("Don't know how to run --test-suite '%s'!" % self.test_suite)
|
||||
cwd = self._query_tests_dir(self.test_suite)
|
||||
env = self.query_env()
|
||||
if minidump:
|
||||
env['MINIDUMP_STACKWALK'] = minidump
|
||||
@ -805,8 +794,7 @@ class AndroidEmulatorTest(TestingMixin, BaseScript, MozbaseMixin, CodeCoverageMi
|
||||
final_cmd.remove(arg)
|
||||
final_cmd.extend(per_test_args)
|
||||
|
||||
self.info("Running on %s the command %s" % (self.emulator["name"],
|
||||
subprocess.list2cmdline(final_cmd)))
|
||||
self.info("Running the command %s" % subprocess.list2cmdline(final_cmd))
|
||||
self.info("##### %s log begins" % self.test_suite)
|
||||
|
||||
suite_category = self.test_suite
|
||||
|
@ -1,7 +0,0 @@
|
||||
[default-iterator-object.html]
|
||||
[Object.prototype.toString returns correct value]
|
||||
expected: FAIL
|
||||
|
||||
[@@toStringTag has correct value from prototype]
|
||||
expected: FAIL
|
||||
|
@ -1,7 +0,0 @@
|
||||
[iterator-prototype-object.html]
|
||||
[Object.prototype.toString returns correct value]
|
||||
expected: FAIL
|
||||
|
||||
[@@toStringTag has correct value]
|
||||
expected: FAIL
|
||||
|
@ -20,6 +20,10 @@ AddonTestUtils.init(this);
|
||||
|
||||
createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "42");
|
||||
|
||||
// Some multibyte characters. This sample was taken from the encoding/api-basics.html web platform test.
|
||||
const MULTIBYTE_STRING = "z\xA2\u6C34\uD834\uDD1E\uF8FF\uDBFF\uDFFD\uFFFE";
|
||||
let getCSS = (a, b) => `a { content: '${a}'; } b { content: '${b}'; }`;
|
||||
|
||||
let extensionData = {
|
||||
background: function() {
|
||||
function backgroundFetch(url) {
|
||||
@ -42,7 +46,7 @@ let extensionData = {
|
||||
browser.test.notifyPass("i18n-css");
|
||||
});
|
||||
|
||||
browser.test.sendMessage("ready", browser.runtime.getURL("foo.css"));
|
||||
browser.test.sendMessage("ready", browser.runtime.getURL("/"));
|
||||
},
|
||||
|
||||
manifest: {
|
||||
@ -52,7 +56,7 @@ let extensionData = {
|
||||
},
|
||||
},
|
||||
|
||||
"web_accessible_resources": ["foo.css", "foo.txt", "locale.css"],
|
||||
"web_accessible_resources": ["foo.css", "foo.txt", "locale.css", "multibyte.css"],
|
||||
|
||||
"content_scripts": [
|
||||
{
|
||||
@ -75,6 +79,9 @@ let extensionData = {
|
||||
"message": "max-width: 42px",
|
||||
"description": "foo",
|
||||
},
|
||||
"multibyteKey": {
|
||||
"message": MULTIBYTE_STRING,
|
||||
},
|
||||
}),
|
||||
|
||||
"content.js": function() {
|
||||
@ -86,6 +93,7 @@ let extensionData = {
|
||||
"bar.CsS": "body { __MSG_foo__; }",
|
||||
"foo.txt": "body { __MSG_foo__; }",
|
||||
"locale.css": '* { content: "__MSG_@@ui_locale__ __MSG_@@bidi_dir__ __MSG_@@bidi_reversed_dir__ __MSG_@@bidi_start_edge__ __MSG_@@bidi_end_edge__" }',
|
||||
"multibyte.css": getCSS("__MSG_multibyteKey__", MULTIBYTE_STRING),
|
||||
},
|
||||
};
|
||||
|
||||
@ -94,11 +102,11 @@ async function test_i18n_css(options = {}) {
|
||||
let extension = ExtensionTestUtils.loadExtension(extensionData);
|
||||
|
||||
await extension.startup();
|
||||
let cssURL = await extension.awaitMessage("ready");
|
||||
let baseURL = await extension.awaitMessage("ready");
|
||||
|
||||
let contentPage = await ExtensionTestUtils.loadContentPage(`${BASE_URL}/file_sample.html`);
|
||||
|
||||
let css = await contentPage.fetch(cssURL);
|
||||
let css = await contentPage.fetch(baseURL + "foo.css");
|
||||
|
||||
equal(css, "body { max-width: 42px; }", "CSS file localized in mochitest scope");
|
||||
|
||||
@ -106,11 +114,12 @@ async function test_i18n_css(options = {}) {
|
||||
|
||||
equal(maxWidth, "42px", "stylesheet correctly applied");
|
||||
|
||||
cssURL = cssURL.replace(/foo.css$/, "locale.css");
|
||||
|
||||
css = await contentPage.fetch(cssURL);
|
||||
css = await contentPage.fetch(baseURL + "locale.css");
|
||||
equal(css, '* { content: "en-US ltr rtl left right" }', "CSS file localized in mochitest scope");
|
||||
|
||||
css = await contentPage.fetch(baseURL + "multibyte.css");
|
||||
equal(css, getCSS(MULTIBYTE_STRING, MULTIBYTE_STRING), "CSS file contains multibyte string");
|
||||
|
||||
await contentPage.close();
|
||||
|
||||
// We don't currently have a good way to mock this.
|
||||
@ -124,7 +133,7 @@ async function test_i18n_css(options = {}) {
|
||||
Services.locale.setRequestedLocales(["he"]);
|
||||
Preferences.set(DIR, 1);
|
||||
|
||||
css = await fetch(cssURL);
|
||||
css = await fetch(baseURL + "locale.css");
|
||||
equal(css, '* { content: "he rtl ltr right left" }', "CSS file localized in mochitest scope");
|
||||
|
||||
Services.locale.setRequestedLocales(origReqLocales);
|
||||
|
@ -129,5 +129,5 @@ add_task(async function testInvalidUUID() {
|
||||
add_task(async function testEmptyStream() {
|
||||
let stream = StringStream("");
|
||||
let resultStream = convService.convert(stream, FROM_TYPE, TO_TYPE, URI);
|
||||
equal(resultStream.data, "");
|
||||
equal(resultStream.available(), 0, "Size of output stream should match size of input stream");
|
||||
});
|
||||
|
@ -25,6 +25,9 @@ XPCOMUtils.defineLazyServiceGetter(this, "catMan", "@mozilla.org/categorymanager
|
||||
"nsICategoryManager");
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "streamConv", "@mozilla.org/streamConverters;1",
|
||||
"nsIStreamConverterService");
|
||||
const ArrayBufferInputStream = Components.Constructor(
|
||||
"@mozilla.org/io/arraybuffer-input-stream;1",
|
||||
"nsIArrayBufferInputStream", "setData");
|
||||
|
||||
/*
|
||||
* This class provides a stream filter for locale messages in CSS files served
|
||||
@ -68,21 +71,18 @@ AddonLocalizationConverter.prototype = {
|
||||
},
|
||||
|
||||
convertToStream(aAddon, aString) {
|
||||
let stream = Cc["@mozilla.org/io/string-input-stream;1"]
|
||||
.createInstance(Ci.nsIStringInputStream);
|
||||
|
||||
stream.data = aAddon.localize(aString);
|
||||
return stream;
|
||||
aString = aAddon.localize(aString);
|
||||
let bytes = new TextEncoder().encode(aString).buffer;
|
||||
return new ArrayBufferInputStream(bytes, 0, bytes.byteLength);
|
||||
},
|
||||
|
||||
convert(aStream, aFromType, aToType, aContext) {
|
||||
this.checkTypes(aFromType, aToType);
|
||||
let addon = this.getAddon(aContext);
|
||||
|
||||
let string = (
|
||||
aStream.available() ?
|
||||
NetUtil.readInputStreamToString(aStream, aStream.available()) : ""
|
||||
);
|
||||
let count = aStream.available();
|
||||
let string = count ?
|
||||
new TextDecoder().decode(NetUtil.readInputStream(aStream, count)) : "";
|
||||
return this.convertToStream(addon, string);
|
||||
},
|
||||
|
||||
@ -94,20 +94,23 @@ AddonLocalizationConverter.prototype = {
|
||||
|
||||
onStartRequest(aRequest, aContext) {
|
||||
this.parts = [];
|
||||
this.decoder = new TextDecoder();
|
||||
},
|
||||
|
||||
onDataAvailable(aRequest, aContext, aInputStream, aOffset, aCount) {
|
||||
this.parts.push(NetUtil.readInputStreamToString(aInputStream, aCount));
|
||||
let bytes = NetUtil.readInputStream(aInputStream, aCount);
|
||||
this.parts.push(this.decoder.decode(bytes, {stream: true}));
|
||||
},
|
||||
|
||||
onStopRequest(aRequest, aContext, aStatusCode) {
|
||||
try {
|
||||
this.listener.onStartRequest(aRequest, null);
|
||||
if (Components.isSuccessCode(aStatusCode)) {
|
||||
this.parts.push(this.decoder.decode());
|
||||
let string = this.parts.join("");
|
||||
let stream = this.convertToStream(this.addon, string);
|
||||
|
||||
this.listener.onDataAvailable(aRequest, null, stream, 0, stream.data.length);
|
||||
this.listener.onDataAvailable(aRequest, null, stream, 0, stream.available());
|
||||
}
|
||||
} catch (e) {
|
||||
aStatusCode = e.result || Cr.NS_ERROR_FAILURE;
|
||||
|
Loading…
Reference in New Issue
Block a user