Merge mozilla-inbound to mozilla-central. a=merge

This commit is contained in:
Dorel Luca 2018-05-18 12:56:07 +03:00
commit 9769f2300a
70 changed files with 831 additions and 549 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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];
}
});
});

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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))];
})];
}

View File

@ -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)
}) : "");
};

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -8,7 +8,8 @@ DIRS += [
]
DevToolsModules(
'Close.js',
'CloseButton.js',
'CommandBarButton.js',
'PaneToggle.js',
'index.js',
'PaneToggleButton.js',
)

View File

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

View File

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

View File

@ -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) {

View File

@ -357,11 +357,7 @@ add_task(async function() {
]
);
await breakpointScopes(
dbg,
"webpack-functions",
{ line: 4, column: 0 },
[
await breakpointScopes(dbg, "webpack-functions", { line: 4, column: 0 }, [
"Block",
["<this>", "{\u2026}"],
["arguments", "Arguments"],
@ -372,6 +368,5 @@ add_task(async function() {
["arguments", "(unavailable)"],
["module", "{\u2026}"],
["root", "(optimized away)"]
]
);
]);
});

View File

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

View File

@ -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,9 +557,13 @@ Inspector.prototype = {
* to `horizontal` to support portrait view.
*/
onPanelWindowResize: function() {
window.cancelIdleCallback(this._resizeTimerId);
this._resizeTimerId = window.requestIdleCallback(() => {
this.splitBox.setState({
vert: this.useLandscapeMode(),
});
this.emit("inspector-resize");
});
},
getSidebarSize: function() {

View File

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

View File

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

View File

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

View File

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

View File

@ -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,26 +2864,28 @@ 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;
@ -2905,9 +2893,33 @@ class CGJsonifyAttributesMethod(CGAbstractMethod):
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,

View File

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

View File

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

View File

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

View 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.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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>

View File

@ -20,5 +20,5 @@ interface MediaDeviceInfo {
readonly attribute DOMString label;
readonly attribute DOMString groupId;
jsonifier;
[Default] object toJSON();
};

View File

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

View File

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

View File

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

View File

@ -18,5 +18,5 @@ interface PerformanceEntry
readonly attribute DOMHighResTimeStamp startTime;
readonly attribute DOMHighResTimeStamp duration;
jsonifier;
[Default] object toJSON();
};

View File

@ -19,5 +19,5 @@ interface PerformanceNavigation {
readonly attribute unsigned short type;
readonly attribute unsigned short redirectCount;
jsonifier;
[Default] object toJSON();
};

View File

@ -29,5 +29,5 @@ interface PerformanceNavigationTiming : PerformanceResourceTiming {
readonly attribute NavigationType type;
readonly attribute unsigned short redirectCount;
jsonifier;
[Default] object toJSON();
};

View File

@ -54,5 +54,5 @@ interface PerformanceResourceTiming : PerformanceEntry
[SecureContext, Frozen, Cached, Pure, NeedsSubjectPrincipal]
readonly attribute sequence<PerformanceServerTiming> serverTiming;
jsonifier;
[Default] object toJSON();
};

View File

@ -16,5 +16,5 @@ interface PerformanceServerTiming {
readonly attribute DOMHighResTimeStamp duration;
readonly attribute DOMString description;
jsonifier;
[Default] object toJSON();
};

View File

@ -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();
};

View File

@ -21,5 +21,5 @@ interface RTCIceCandidate {
attribute DOMString? sdpMid;
attribute unsigned short? sdpMLineIndex;
jsonifier;
[Default] object toJSON();
};

View File

@ -27,5 +27,5 @@ interface RTCSessionDescription {
attribute RTCSdpType type;
attribute DOMString sdp;
jsonifier;
[Default] object toJSON();
};

View File

@ -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()
{

View File

@ -2992,6 +2992,9 @@ 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");
}
@ -3113,3 +3116,19 @@ CacheIRCompiler::emitMegamorphicLoadSlotResult()
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;
}

View File

@ -27,6 +27,7 @@ namespace jit {
_(GuardIsInt32Index) \
_(GuardType) \
_(GuardClass) \
_(GuardGroupHasUnanalyzedNewScript) \
_(GuardIsNativeFunction) \
_(GuardIsNativeObject) \
_(GuardIsProxy) \

View File

@ -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()
{

View File

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

View File

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

View File

@ -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()

View File

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

View File

@ -1,7 +0,0 @@
[default-iterator-object.html]
[Object.prototype.toString returns correct value]
expected: FAIL
[@@toStringTag has correct value from prototype]
expected: FAIL

View File

@ -1,7 +0,0 @@
[iterator-prototype-object.html]
[Object.prototype.toString returns correct value]
expected: FAIL
[@@toStringTag has correct value]
expected: FAIL

View File

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

View File

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

View File

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