mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 21:31:04 +00:00
Merge inbound to mozilla-central a=merge
This commit is contained in:
commit
5527acb8d8
@ -56,7 +56,7 @@ support-files =
|
||||
!/toolkit/components/passwordmgr/test/browser/insecure_test.html
|
||||
!/toolkit/components/passwordmgr/test/browser/insecure_test_subframe.html
|
||||
[browser_mcb_redirect.js]
|
||||
skip-if = verify && !debug && os == 'mac'
|
||||
skip-if = (verify && !debug && os == 'mac') || (os == 'linux') || (os == 'mac') # Bug 1376771
|
||||
tags = mcb
|
||||
support-files =
|
||||
test_mcb_redirect.html
|
||||
|
@ -52,6 +52,7 @@ skip-if = !e10s # Pref and test only relevant for e10s.
|
||||
[browser_newwindow_tabstrip_overflow.js]
|
||||
[browser_open_newtab_start_observer_notification.js]
|
||||
[browser_opened_file_tab_navigated_to_web.js]
|
||||
skip-if = (os == 'mac' && debug) || (os == 'linux' && debug) # Bug 1356347
|
||||
[browser_overflowScroll.js]
|
||||
[browser_pinnedTabs_clickOpen.js]
|
||||
[browser_pinnedTabs_closeByKeyboard.js]
|
||||
|
@ -25,6 +25,13 @@
|
||||
fun:_ZL13SaveWordToEnvPKcRK12nsTSubstringIcE
|
||||
...
|
||||
}
|
||||
{
|
||||
PR_SetEnv requires its argument to be leaked, but does not appear on stacks. (See bug 793549.)
|
||||
Memcheck:Leak
|
||||
...
|
||||
fun:SaveWordToEnv
|
||||
...
|
||||
}
|
||||
{
|
||||
PR_SetEnv requires its argument to be leaked, but does not appear on stacks. (See bug 944133.)
|
||||
Memcheck:Leak
|
||||
|
@ -5,12 +5,13 @@
|
||||
@import "chrome://global/skin/in-content/common.css";
|
||||
@import "resource://devtools/client/aboutdebugging-new/src/components/App.css";
|
||||
@import "resource://devtools/client/aboutdebugging-new/src/components/ConnectPage.css";
|
||||
@import "resource://devtools/client/aboutdebugging-new/src/components/DebugTargetItem.css";
|
||||
@import "resource://devtools/client/aboutdebugging-new/src/components/DebugTargetList.css";
|
||||
@import "resource://devtools/client/aboutdebugging-new/src/components/RuntimeInfo.css";
|
||||
@import "resource://devtools/client/aboutdebugging-new/src/components/Sidebar.css";
|
||||
@import "resource://devtools/client/aboutdebugging-new/src/components/SidebarItem.css";
|
||||
@import "resource://devtools/client/aboutdebugging-new/src/components/debugtarget/DebugTargetItem.css";
|
||||
@import "resource://devtools/client/aboutdebugging-new/src/components/debugtarget/DebugTargetList.css";
|
||||
@import "resource://devtools/client/aboutdebugging-new/src/components/debugtarget/ExtensionDetail.css";
|
||||
@import "resource://devtools/client/aboutdebugging-new/src/components/debugtarget/WorkerDetail.css";
|
||||
|
||||
:root {
|
||||
/* Import css variables from common.css */
|
||||
|
@ -10,6 +10,7 @@ const { BrowserToolboxProcess } =
|
||||
const { Cc, Ci } = require("chrome");
|
||||
const { DebuggerClient } = require("devtools/shared/client/debugger-client");
|
||||
const { DebuggerServer } = require("devtools/server/main");
|
||||
const { gDevToolsBrowser } = require("devtools/client/framework/devtools-browser");
|
||||
|
||||
const {
|
||||
CONNECT_RUNTIME_FAILURE,
|
||||
@ -25,6 +26,9 @@ const {
|
||||
REQUEST_TABS_FAILURE,
|
||||
REQUEST_TABS_START,
|
||||
REQUEST_TABS_SUCCESS,
|
||||
REQUEST_WORKERS_FAILURE,
|
||||
REQUEST_WORKERS_START,
|
||||
REQUEST_WORKERS_SUCCESS,
|
||||
} = require("../constants");
|
||||
|
||||
let browserToolboxProcess = null;
|
||||
@ -43,6 +47,7 @@ function connectRuntime() {
|
||||
dispatch({ type: CONNECT_RUNTIME_SUCCESS, client });
|
||||
dispatch(requestExtensions());
|
||||
dispatch(requestTabs());
|
||||
dispatch(requestWorkers());
|
||||
} catch (e) {
|
||||
dispatch({ type: CONNECT_RUNTIME_FAILURE, error: e.message });
|
||||
}
|
||||
@ -67,26 +72,39 @@ function disconnectRuntime() {
|
||||
}
|
||||
|
||||
function inspectDebugTarget(type, id) {
|
||||
if (type === DEBUG_TARGETS.TAB) {
|
||||
window.open(`about:devtools-toolbox?type=tab&id=${ id }`);
|
||||
} else if (type === DEBUG_TARGETS.EXTENSION) {
|
||||
// Close previous addon debugging toolbox.
|
||||
if (browserToolboxProcess) {
|
||||
browserToolboxProcess.close();
|
||||
}
|
||||
|
||||
browserToolboxProcess = BrowserToolboxProcess.init({
|
||||
addonID: id,
|
||||
onClose: () => {
|
||||
browserToolboxProcess = null;
|
||||
return async (_, getState) => {
|
||||
switch (type) {
|
||||
case DEBUG_TARGETS.TAB: {
|
||||
// Open tab debugger in new window.
|
||||
window.open(`about:devtools-toolbox?type=tab&id=${ id }`);
|
||||
break;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
console.error(`Failed to inspect the debug target of type: ${ type } id: ${ id }`);
|
||||
}
|
||||
case DEBUG_TARGETS.EXTENSION: {
|
||||
// Close current debugging toolbox and open a new one.
|
||||
if (browserToolboxProcess) {
|
||||
browserToolboxProcess.close();
|
||||
}
|
||||
|
||||
// We cancel the redux flow here since the inspection does not need to update the state.
|
||||
return () => {};
|
||||
browserToolboxProcess = BrowserToolboxProcess.init({
|
||||
addonID: id,
|
||||
onClose: () => {
|
||||
browserToolboxProcess = null;
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
case DEBUG_TARGETS.WORKER: {
|
||||
// Open worker toolbox in new window.
|
||||
gDevToolsBrowser.openWorkerToolbox(getState().runtime.client, id);
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
console.error("Failed to inspect the debug target of " +
|
||||
`type: ${ type } id: ${ id }`);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function installTemporaryExtension() {
|
||||
@ -116,6 +134,18 @@ function installTemporaryExtension() {
|
||||
return () => {};
|
||||
}
|
||||
|
||||
function pushServiceWorker(actor) {
|
||||
return async (_, getState) => {
|
||||
const client = getState().runtime.client;
|
||||
|
||||
try {
|
||||
await client.request({ to: actor, type: "push" });
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function reloadTemporaryExtension(actor) {
|
||||
return async (_, getState) => {
|
||||
const client = getState().runtime.client;
|
||||
@ -181,13 +211,53 @@ function requestExtensions() {
|
||||
};
|
||||
}
|
||||
|
||||
function requestWorkers() {
|
||||
return async (dispatch, getState) => {
|
||||
dispatch({ type: REQUEST_WORKERS_START });
|
||||
|
||||
const client = getState().runtime.client;
|
||||
|
||||
try {
|
||||
const {
|
||||
other: otherWorkers,
|
||||
service: serviceWorkers,
|
||||
shared: sharedWorkers,
|
||||
} = await client.mainRoot.listAllWorkers();
|
||||
|
||||
dispatch({
|
||||
type: REQUEST_WORKERS_SUCCESS,
|
||||
otherWorkers,
|
||||
serviceWorkers,
|
||||
sharedWorkers,
|
||||
});
|
||||
} catch (e) {
|
||||
dispatch({ type: REQUEST_WORKERS_FAILURE, error: e.message });
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function startServiceWorker(actor) {
|
||||
return async (_, getState) => {
|
||||
const client = getState().runtime.client;
|
||||
|
||||
try {
|
||||
await client.request({ to: actor, type: "start" });
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
connectRuntime,
|
||||
disconnectRuntime,
|
||||
inspectDebugTarget,
|
||||
installTemporaryExtension,
|
||||
pushServiceWorker,
|
||||
reloadTemporaryExtension,
|
||||
removeTemporaryExtension,
|
||||
requestTabs,
|
||||
requestExtensions,
|
||||
requestWorkers,
|
||||
startServiceWorker,
|
||||
};
|
||||
|
@ -9,10 +9,16 @@ const { createFactory, PureComponent } = require("devtools/client/shared/vendor/
|
||||
const dom = require("devtools/client/shared/vendor/react-dom-factories");
|
||||
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
|
||||
|
||||
const DebugTargetPane = createFactory(require("./DebugTargetPane"));
|
||||
const DebugTargetPane = createFactory(require("./debugtarget/DebugTargetPane"));
|
||||
const ExtensionDetail = createFactory(require("./debugtarget/ExtensionDetail"));
|
||||
const InspectAction = createFactory(require("./debugtarget/InspectAction"));
|
||||
const RuntimeInfo = createFactory(require("./RuntimeInfo"));
|
||||
const ServiceWorkerAction = createFactory(require("./debugtarget/ServiceWorkerAction"));
|
||||
const TabDetail = createFactory(require("./debugtarget/TabDetail"));
|
||||
const TemporaryExtensionAction = createFactory(require("./debugtarget/TemporaryExtensionAction"));
|
||||
const TemporaryExtensionInstaller =
|
||||
createFactory(require("./debugtarget/TemporaryExtensionInstaller"));
|
||||
const WorkerDetail = createFactory(require("./debugtarget/WorkerDetail"));
|
||||
|
||||
const Services = require("Services");
|
||||
|
||||
@ -21,13 +27,24 @@ class RuntimePage extends PureComponent {
|
||||
return {
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
installedExtensions: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
otherWorkers: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
serviceWorkers: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
sharedWorkers: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
tabs: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
temporaryExtensions: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
const { dispatch, installedExtensions, tabs, temporaryExtensions } = this.props;
|
||||
const {
|
||||
dispatch,
|
||||
installedExtensions,
|
||||
otherWorkers,
|
||||
serviceWorkers,
|
||||
sharedWorkers,
|
||||
tabs,
|
||||
temporaryExtensions,
|
||||
} = this.props;
|
||||
|
||||
return dom.article(
|
||||
{
|
||||
@ -40,20 +57,47 @@ class RuntimePage extends PureComponent {
|
||||
}),
|
||||
TemporaryExtensionInstaller({ dispatch }),
|
||||
DebugTargetPane({
|
||||
actionComponent: TemporaryExtensionAction,
|
||||
detailComponent: ExtensionDetail,
|
||||
dispatch,
|
||||
name: "Temporary Extensions",
|
||||
targets: temporaryExtensions,
|
||||
}),
|
||||
DebugTargetPane({
|
||||
actionComponent: InspectAction,
|
||||
detailComponent: ExtensionDetail,
|
||||
dispatch,
|
||||
name: "Extensions",
|
||||
targets: installedExtensions,
|
||||
}),
|
||||
DebugTargetPane({
|
||||
actionComponent: InspectAction,
|
||||
detailComponent: TabDetail,
|
||||
dispatch,
|
||||
name: "Tabs",
|
||||
targets: tabs
|
||||
}),
|
||||
DebugTargetPane({
|
||||
actionComponent: ServiceWorkerAction,
|
||||
detailComponent: WorkerDetail,
|
||||
dispatch,
|
||||
name: "Service Workers",
|
||||
targets: serviceWorkers
|
||||
}),
|
||||
DebugTargetPane({
|
||||
actionComponent: InspectAction,
|
||||
detailComponent: WorkerDetail,
|
||||
dispatch,
|
||||
name: "Shared Workers",
|
||||
targets: sharedWorkers
|
||||
}),
|
||||
DebugTargetPane({
|
||||
actionComponent: InspectAction,
|
||||
detailComponent: WorkerDetail,
|
||||
dispatch,
|
||||
name: "Other Workers",
|
||||
targets: otherWorkers
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -61,6 +105,9 @@ class RuntimePage extends PureComponent {
|
||||
const mapStateToProps = state => {
|
||||
return {
|
||||
installedExtensions: state.runtime.installedExtensions,
|
||||
otherWorkers: state.runtime.otherWorkers,
|
||||
serviceWorkers: state.runtime.serviceWorkers,
|
||||
sharedWorkers: state.runtime.sharedWorkers,
|
||||
tabs: state.runtime.tabs,
|
||||
temporaryExtensions: state.runtime.temporaryExtensions,
|
||||
};
|
||||
|
@ -4,63 +4,31 @@
|
||||
|
||||
"use strict";
|
||||
|
||||
const { createFactory, PureComponent } = require("devtools/client/shared/vendor/react");
|
||||
const { PureComponent } = require("devtools/client/shared/vendor/react");
|
||||
const dom = require("devtools/client/shared/vendor/react-dom-factories");
|
||||
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
|
||||
|
||||
const ExtensionDetail = createFactory(require("./debugtarget/ExtensionDetail"));
|
||||
const TabDetail = createFactory(require("./debugtarget/TabDetail"));
|
||||
const TemporaryExtensionAction =
|
||||
createFactory(require("./debugtarget/TemporaryExtensionAction"));
|
||||
|
||||
const Actions = require("../actions/index");
|
||||
const { DEBUG_TARGETS } = require("../constants");
|
||||
|
||||
/**
|
||||
* This component displays debug target.
|
||||
*/
|
||||
class DebugTargetItem extends PureComponent {
|
||||
static get propTypes() {
|
||||
return {
|
||||
actionComponent: PropTypes.any.isRequired,
|
||||
detailComponent: PropTypes.any.isRequired,
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
target: PropTypes.object.isRequired,
|
||||
};
|
||||
}
|
||||
|
||||
inspect() {
|
||||
const { dispatch, target } = this.props;
|
||||
dispatch(Actions.inspectDebugTarget(target.type, target.id));
|
||||
}
|
||||
|
||||
renderAction() {
|
||||
const { dispatch, target } = this.props;
|
||||
|
||||
return dom.div(
|
||||
{},
|
||||
dom.button(
|
||||
{
|
||||
onClick: e => this.inspect(),
|
||||
className: "aboutdebugging-button",
|
||||
},
|
||||
"Inspect"
|
||||
),
|
||||
target.details.temporarilyInstalled
|
||||
? TemporaryExtensionAction({ dispatch, target }) : null,
|
||||
);
|
||||
const { actionComponent, dispatch, target } = this.props;
|
||||
return actionComponent({ dispatch, target });
|
||||
}
|
||||
|
||||
renderDetail() {
|
||||
const { target } = this.props;
|
||||
|
||||
switch (target.type) {
|
||||
case DEBUG_TARGETS.EXTENSION:
|
||||
return ExtensionDetail({ target });
|
||||
case DEBUG_TARGETS.TAB:
|
||||
return TabDetail({ target });
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
const { detailComponent, target } = this.props;
|
||||
return detailComponent({ target });
|
||||
}
|
||||
|
||||
renderIcon() {
|
||||
@ -71,8 +39,6 @@ class DebugTargetItem extends PureComponent {
|
||||
}
|
||||
|
||||
renderInfo() {
|
||||
const { target } = this.props;
|
||||
|
||||
return dom.div(
|
||||
{
|
||||
className: "debug-target-item__info",
|
||||
@ -80,9 +46,9 @@ class DebugTargetItem extends PureComponent {
|
||||
dom.div(
|
||||
{
|
||||
className: "debug-target-item__info__name ellipsis-text",
|
||||
title: target.name,
|
||||
title: this.props.target.name,
|
||||
},
|
||||
target.name
|
||||
this.props.target.name
|
||||
),
|
||||
this.renderDetail(),
|
||||
);
|
@ -16,19 +16,22 @@ const DebugTargetItem = createFactory(require("./DebugTargetItem"));
|
||||
class DebugTargetList extends PureComponent {
|
||||
static get propTypes() {
|
||||
return {
|
||||
actionComponent: PropTypes.any.isRequired,
|
||||
detailComponent: PropTypes.any.isRequired,
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
targets: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
const { dispatch, targets } = this.props;
|
||||
const { actionComponent, detailComponent, dispatch, targets } = this.props;
|
||||
|
||||
return dom.ul(
|
||||
{
|
||||
className: "debug-target-list",
|
||||
},
|
||||
targets.map(target => DebugTargetItem({ dispatch, target })),
|
||||
targets.map(target =>
|
||||
DebugTargetItem({ actionComponent, detailComponent, dispatch, target })),
|
||||
);
|
||||
}
|
||||
}
|
@ -16,6 +16,8 @@ const DebugTargetList = createFactory(require("./DebugTargetList"));
|
||||
class DebugTargetPane extends PureComponent {
|
||||
static get propTypes() {
|
||||
return {
|
||||
actionComponent: PropTypes.any.isRequired,
|
||||
detailComponent: PropTypes.any.isRequired,
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
name: PropTypes.string.isRequired,
|
||||
targets: PropTypes.arrayOf(PropTypes.Object).isRequired,
|
||||
@ -23,12 +25,12 @@ class DebugTargetPane extends PureComponent {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { dispatch, name, targets } = this.props;
|
||||
const { actionComponent, detailComponent, dispatch, name, targets } = this.props;
|
||||
|
||||
return dom.section(
|
||||
{},
|
||||
dom.h2({}, name),
|
||||
DebugTargetList({ dispatch, targets }),
|
||||
DebugTargetList({ actionComponent, detailComponent, dispatch, targets }),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const { PureComponent } = require("devtools/client/shared/vendor/react");
|
||||
const dom = require("devtools/client/shared/vendor/react-dom-factories");
|
||||
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
|
||||
|
||||
const Actions = require("../../actions/index");
|
||||
|
||||
/**
|
||||
* This component provides inspect button.
|
||||
*/
|
||||
class InspectAction extends PureComponent {
|
||||
static get propTypes() {
|
||||
return {
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
target: PropTypes.object.isRequired,
|
||||
};
|
||||
}
|
||||
|
||||
inspect() {
|
||||
const { dispatch, target } = this.props;
|
||||
dispatch(Actions.inspectDebugTarget(target.type, target.id));
|
||||
}
|
||||
|
||||
render() {
|
||||
return dom.button(
|
||||
{
|
||||
onClick: e => this.inspect(),
|
||||
className: "aboutdebugging-button",
|
||||
},
|
||||
"Inspect"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = InspectAction;
|
@ -0,0 +1,70 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const { createFactory, PureComponent } = require("devtools/client/shared/vendor/react");
|
||||
const dom = require("devtools/client/shared/vendor/react-dom-factories");
|
||||
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
|
||||
|
||||
const InspectAction = createFactory(require("./InspectAction"));
|
||||
|
||||
const Actions = require("../../actions/index");
|
||||
|
||||
/**
|
||||
* This component displays buttons for service worker.
|
||||
*/
|
||||
class ServiceWorkerAction extends PureComponent {
|
||||
static get propTypes() {
|
||||
return {
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
target: PropTypes.object.isRequired,
|
||||
};
|
||||
}
|
||||
|
||||
push() {
|
||||
const { dispatch, target } = this.props;
|
||||
dispatch(Actions.pushServiceWorker(target.id));
|
||||
}
|
||||
|
||||
start() {
|
||||
const { dispatch, target } = this.props;
|
||||
dispatch(Actions.startServiceWorker(target.details.registrationActor));
|
||||
}
|
||||
|
||||
_renderAction() {
|
||||
const { dispatch, target } = this.props;
|
||||
const { isActive, isRunning } = target.details;
|
||||
|
||||
if (!isRunning) {
|
||||
return this._renderButton("Start", this.start.bind(this));
|
||||
}
|
||||
|
||||
if (!isActive) {
|
||||
// Only debug button is available if the service worker is not active.
|
||||
return InspectAction({ dispatch, target });
|
||||
}
|
||||
|
||||
return [
|
||||
this._renderButton("Push", this.push.bind(this)),
|
||||
InspectAction({ dispatch, target }),
|
||||
];
|
||||
}
|
||||
|
||||
_renderButton(label, onClick) {
|
||||
return dom.button(
|
||||
{
|
||||
className: "aboutdebugging-button",
|
||||
onClick: e => onClick(),
|
||||
},
|
||||
label,
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
return dom.div({}, this._renderAction());
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ServiceWorkerAction;
|
@ -4,14 +4,16 @@
|
||||
|
||||
"use strict";
|
||||
|
||||
const { PureComponent } = require("devtools/client/shared/vendor/react");
|
||||
const { createFactory, PureComponent } = require("devtools/client/shared/vendor/react");
|
||||
const dom = require("devtools/client/shared/vendor/react-dom-factories");
|
||||
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
|
||||
|
||||
const InspectAction = createFactory(require("./InspectAction"));
|
||||
|
||||
const Actions = require("../../actions/index");
|
||||
|
||||
/**
|
||||
* This component provides components that reload/remove temporary extension.
|
||||
* This component provides components that inspect/reload/remove temporary extension.
|
||||
*/
|
||||
class TemporaryExtensionAction extends PureComponent {
|
||||
static get propTypes() {
|
||||
@ -32,7 +34,11 @@ class TemporaryExtensionAction extends PureComponent {
|
||||
}
|
||||
|
||||
render() {
|
||||
return [
|
||||
const { dispatch, target } = this.props;
|
||||
|
||||
return dom.div(
|
||||
{},
|
||||
InspectAction({ dispatch, target }),
|
||||
dom.button(
|
||||
{
|
||||
className: "aboutdebugging-button",
|
||||
@ -47,7 +53,7 @@ class TemporaryExtensionAction extends PureComponent {
|
||||
},
|
||||
"Remove",
|
||||
),
|
||||
];
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,52 @@
|
||||
/* 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/. */
|
||||
|
||||
.worker-detail {
|
||||
--worker-status-font-size: 10px;
|
||||
}
|
||||
|
||||
/*
|
||||
* The current layout of worker detail is
|
||||
*
|
||||
* +----------------+--------------------+
|
||||
* | detail name dt | detail value dd |
|
||||
* | (60px ) | (auto) |
|
||||
* +----------------+--------------------+
|
||||
* | detail name dt | detail value dd |
|
||||
* +----------------+--------------------+
|
||||
* | detail name dt | detail value dd |
|
||||
* +----------------+--------------------+
|
||||
*/
|
||||
.worker-detail {
|
||||
display: grid;
|
||||
grid-template-columns: 60px auto;
|
||||
margin-block-start: 4px;
|
||||
}
|
||||
|
||||
/*
|
||||
* worker-detail__status has a ui like badge and the color change by the status.
|
||||
* For now, the background-color of running status is palegreen, stopped is lightgrey
|
||||
* though, might be changed since this is not Photon color.
|
||||
*/
|
||||
.worker-detail__status {
|
||||
border-style: solid;
|
||||
border-width: 1px;
|
||||
box-sizing: border-box;
|
||||
display: inline-block;
|
||||
font-size: var(--worker-status-font-size);
|
||||
margin-block-start: 6px;
|
||||
padding-block-start: 2px;
|
||||
padding-block-end: 2px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.worker-detail__status--running {
|
||||
border-color: limegreen;
|
||||
background-color: palegreen;
|
||||
}
|
||||
|
||||
.worker-detail__status--stopped {
|
||||
border-color: grey;
|
||||
background-color: lightgrey;
|
||||
}
|
@ -0,0 +1,71 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const { PureComponent } = require("devtools/client/shared/vendor/react");
|
||||
const dom = require("devtools/client/shared/vendor/react-dom-factories");
|
||||
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
|
||||
|
||||
const {
|
||||
SERVICE_WORKER_FETCH_STATES,
|
||||
} = require("../../constants");
|
||||
|
||||
/**
|
||||
* This component displays detail information for worker.
|
||||
*/
|
||||
class WorkerDetail extends PureComponent {
|
||||
static get propTypes() {
|
||||
return {
|
||||
target: PropTypes.object.isRequired,
|
||||
};
|
||||
}
|
||||
|
||||
renderFetch() {
|
||||
const { fetch } = this.props.target.details;
|
||||
const label = fetch === SERVICE_WORKER_FETCH_STATES.LISTENING
|
||||
? "Listening for fetch events"
|
||||
: "Not listening for fetch events";
|
||||
return this.renderField("Fetch", label);
|
||||
}
|
||||
|
||||
renderField(name, value) {
|
||||
return [
|
||||
dom.dt({}, name),
|
||||
dom.dd(
|
||||
{
|
||||
className: "ellipsis-text",
|
||||
title: value,
|
||||
},
|
||||
value,
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
renderStatus() {
|
||||
const status = this.props.target.details.status.toLowerCase();
|
||||
|
||||
return dom.div(
|
||||
{
|
||||
className: `worker-detail__status worker-detail__status--${ status }`,
|
||||
},
|
||||
status
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { fetch, scope, status } = this.props.target.details;
|
||||
|
||||
return dom.dl(
|
||||
{
|
||||
className: "worker-detail",
|
||||
},
|
||||
fetch ? this.renderFetch() : null,
|
||||
scope ? this.renderField("Scope", scope) : null,
|
||||
status ? this.renderStatus() : null,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = WorkerDetail;
|
@ -3,9 +3,18 @@
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
DevToolsModules(
|
||||
'DebugTargetItem.css',
|
||||
'DebugTargetItem.js',
|
||||
'DebugTargetList.css',
|
||||
'DebugTargetList.js',
|
||||
'DebugTargetPane.js',
|
||||
'ExtensionDetail.css',
|
||||
'ExtensionDetail.js',
|
||||
'InspectAction.js',
|
||||
'ServiceWorkerAction.js',
|
||||
'TabDetail.js',
|
||||
'TemporaryExtensionAction.js',
|
||||
'TemporaryExtensionInstaller.js',
|
||||
'WorkerDetail.css',
|
||||
'WorkerDetail.js',
|
||||
)
|
||||
|
@ -11,11 +11,6 @@ DevToolsModules(
|
||||
'App.js',
|
||||
'ConnectPage.css',
|
||||
'ConnectPage.js',
|
||||
'DebugTargetItem.css',
|
||||
'DebugTargetItem.js',
|
||||
'DebugTargetList.css',
|
||||
'DebugTargetList.js',
|
||||
'DebugTargetPane.js',
|
||||
'RuntimeInfo.css',
|
||||
'RuntimeInfo.js',
|
||||
'RuntimePage.js',
|
||||
|
@ -19,11 +19,15 @@ const actionTypes = {
|
||||
REQUEST_TABS_FAILURE: "REQUEST_TABS_FAILURE",
|
||||
REQUEST_TABS_START: "REQUEST_TABS_START",
|
||||
REQUEST_TABS_SUCCESS: "REQUEST_TABS_SUCCESS",
|
||||
REQUEST_WORKERS_FAILURE: "REQUEST_WORKERS_FAILURE",
|
||||
REQUEST_WORKERS_START: "REQUEST_WORKERS_START",
|
||||
REQUEST_WORKERS_SUCCESS: "REQUEST_WORKERS_SUCCESS",
|
||||
};
|
||||
|
||||
const DEBUG_TARGETS = {
|
||||
EXTENSION: "EXTENSION",
|
||||
TAB: "TAB",
|
||||
WORKER: "WORKER",
|
||||
};
|
||||
|
||||
const PAGES = {
|
||||
@ -31,8 +35,21 @@ const PAGES = {
|
||||
CONNECT: "connect",
|
||||
};
|
||||
|
||||
const SERVICE_WORKER_FETCH_STATES = {
|
||||
LISTENING: "LISTENING",
|
||||
NOT_LISTENING: "NOT_LISTENING",
|
||||
};
|
||||
|
||||
const SERVICE_WORKER_STATUSES = {
|
||||
RUNNING: "RUNNING",
|
||||
REGISTERING: "REGISTERING",
|
||||
STOPPED: "STOPPED",
|
||||
};
|
||||
|
||||
// flatten constants
|
||||
module.exports = Object.assign({}, {
|
||||
DEBUG_TARGETS,
|
||||
PAGES,
|
||||
SERVICE_WORKER_FETCH_STATES,
|
||||
SERVICE_WORKER_STATUSES,
|
||||
}, actionTypes);
|
||||
|
@ -11,6 +11,9 @@ const rootReducer = require("./reducers/index");
|
||||
const { RuntimeState } = require("./reducers/runtime-state");
|
||||
const { UiState } = require("./reducers/ui-state");
|
||||
const debugTargetListenerMiddleware = require("./middleware/debug-target-listener");
|
||||
const extensionComponentDataMiddleware = require("./middleware/extension-component-data");
|
||||
const tabComponentDataMiddleware = require("./middleware/tab-component-data");
|
||||
const workerComponentDataMiddleware = require("./middleware/worker-component-data");
|
||||
const { getNetworkLocations } = require("./modules/network-locations");
|
||||
|
||||
function configureStore() {
|
||||
@ -19,7 +22,11 @@ function configureStore() {
|
||||
ui: getUiState()
|
||||
};
|
||||
|
||||
const middleware = applyMiddleware(thunk, debugTargetListenerMiddleware);
|
||||
const middleware = applyMiddleware(thunk,
|
||||
debugTargetListenerMiddleware,
|
||||
extensionComponentDataMiddleware,
|
||||
tabComponentDataMiddleware,
|
||||
workerComponentDataMiddleware);
|
||||
|
||||
return createStore(rootReducer, initialState, middleware);
|
||||
}
|
||||
|
@ -12,13 +12,13 @@ const {
|
||||
} = require("../constants");
|
||||
const Actions = require("../actions/index");
|
||||
|
||||
function debugTargetListenerMiddleware(state) {
|
||||
function debugTargetListenerMiddleware(store) {
|
||||
const onExtensionsUpdated = () => {
|
||||
state.dispatch(Actions.requestExtensions());
|
||||
store.dispatch(Actions.requestExtensions());
|
||||
};
|
||||
|
||||
const onTabsUpdated = () => {
|
||||
state.dispatch(Actions.requestTabs());
|
||||
store.dispatch(Actions.requestTabs());
|
||||
};
|
||||
|
||||
const extensionsListener = {
|
||||
@ -47,16 +47,32 @@ function debugTargetListenerMiddleware(state) {
|
||||
},
|
||||
};
|
||||
|
||||
const onWorkersUpdated = () => {
|
||||
store.dispatch(Actions.requestWorkers());
|
||||
};
|
||||
|
||||
return next => action => {
|
||||
switch (action.type) {
|
||||
case CONNECT_RUNTIME_SUCCESS: {
|
||||
action.client.addListener("tabListChanged", onTabsUpdated);
|
||||
const { client } = action;
|
||||
client.addListener("tabListChanged", onTabsUpdated);
|
||||
AddonManager.addAddonListener(extensionsListener);
|
||||
client.addListener("workerListChanged", onWorkersUpdated);
|
||||
client.addListener("serviceWorkerRegistrationListChanged", onWorkersUpdated);
|
||||
client.addListener("processListChanged", onWorkersUpdated);
|
||||
client.addListener("registration-changed", onWorkersUpdated);
|
||||
client.addListener("push-subscription-modified", onWorkersUpdated);
|
||||
break;
|
||||
}
|
||||
case DISCONNECT_RUNTIME_START: {
|
||||
state.getState().runtime.client.removeListener("tabListChanged", onTabsUpdated);
|
||||
const { client } = store.getState().runtime;
|
||||
client.removeListener("tabListChanged", onTabsUpdated);
|
||||
AddonManager.removeAddonListener(extensionsListener);
|
||||
client.removeListener("workerListChanged", onWorkersUpdated);
|
||||
client.removeListener("serviceWorkerRegistrationListChanged", onWorkersUpdated);
|
||||
client.removeListener("processListChanged", onWorkersUpdated);
|
||||
client.removeListener("registration-changed", onWorkersUpdated);
|
||||
client.removeListener("push-subscription-modified", onWorkersUpdated);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,70 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const {
|
||||
DEBUG_TARGETS,
|
||||
REQUEST_EXTENSIONS_SUCCESS,
|
||||
} = require("../constants");
|
||||
|
||||
/**
|
||||
* This middleware converts extensions object that get from DebuggerClient.listAddons()
|
||||
* to data which is used in DebugTargetItem.
|
||||
*/
|
||||
const extensionComponentDataMiddleware = store => next => action => {
|
||||
switch (action.type) {
|
||||
case REQUEST_EXTENSIONS_SUCCESS: {
|
||||
action.installedExtensions = toComponentData(action.installedExtensions);
|
||||
action.temporaryExtensions = toComponentData(action.temporaryExtensions);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return next(action);
|
||||
};
|
||||
|
||||
function getFilePath(extension) {
|
||||
// Only show file system paths, and only for temporarily installed add-ons.
|
||||
if (!extension.temporarilyInstalled ||
|
||||
!extension.url ||
|
||||
!extension.url.startsWith("file://")) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Strip a leading slash from Windows drive letter URIs.
|
||||
// file:///home/foo ~> /home/foo
|
||||
// file:///C:/foo ~> C:/foo
|
||||
const windowsRegex = /^file:\/\/\/([a-zA-Z]:\/.*)/;
|
||||
|
||||
if (windowsRegex.test(extension.url)) {
|
||||
return windowsRegex.exec(extension.url)[1];
|
||||
}
|
||||
|
||||
return extension.url.slice("file://".length);
|
||||
}
|
||||
|
||||
function toComponentData(extensions) {
|
||||
return extensions.map(extension => {
|
||||
const type = DEBUG_TARGETS.EXTENSION;
|
||||
const { actor, iconURL, id, manifestURL, name } = extension;
|
||||
const icon = iconURL || "chrome://mozapps/skin/extensions/extensionGeneric.svg";
|
||||
const location = getFilePath(extension);
|
||||
const uuid = manifestURL ? /moz-extension:\/\/([^/]*)/.exec(manifestURL)[1] : null;
|
||||
return {
|
||||
name,
|
||||
icon,
|
||||
id,
|
||||
type,
|
||||
details: {
|
||||
actor,
|
||||
location,
|
||||
manifestURL,
|
||||
uuid,
|
||||
},
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = extensionComponentDataMiddleware;
|
@ -4,4 +4,7 @@
|
||||
|
||||
DevToolsModules(
|
||||
'debug-target-listener.js',
|
||||
'extension-component-data.js',
|
||||
'tab-component-data.js',
|
||||
'worker-component-data.js',
|
||||
)
|
||||
|
@ -0,0 +1,48 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const {
|
||||
DEBUG_TARGETS,
|
||||
REQUEST_TABS_SUCCESS,
|
||||
} = require("../constants");
|
||||
|
||||
/**
|
||||
* This middleware converts tabs object that get from DebuggerClient.listTabs() to data
|
||||
* which is used in DebugTargetItem.
|
||||
*/
|
||||
const tabComponentDataMiddleware = store => next => action => {
|
||||
switch (action.type) {
|
||||
case REQUEST_TABS_SUCCESS: {
|
||||
action.tabs = toComponentData(action.tabs);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return next(action);
|
||||
};
|
||||
|
||||
function toComponentData(tabs) {
|
||||
return tabs.map(tab => {
|
||||
const type = DEBUG_TARGETS.TAB;
|
||||
const id = tab.outerWindowID;
|
||||
const icon = tab.favicon
|
||||
? `data:image/png;base64,${ btoa(String.fromCharCode.apply(String, tab.favicon)) }`
|
||||
: "chrome://devtools/skin/images/globe.svg";
|
||||
const name = tab.title || tab.url;
|
||||
const url = tab.url;
|
||||
return {
|
||||
name,
|
||||
icon,
|
||||
id,
|
||||
type,
|
||||
details: {
|
||||
url,
|
||||
},
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = tabComponentDataMiddleware;
|
@ -0,0 +1,78 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const {
|
||||
DEBUG_TARGETS,
|
||||
REQUEST_WORKERS_SUCCESS,
|
||||
SERVICE_WORKER_FETCH_STATES,
|
||||
SERVICE_WORKER_STATUSES,
|
||||
} = require("../constants");
|
||||
|
||||
/**
|
||||
* This middleware converts workers object that get from DebuggerClient.listAllWorkers()
|
||||
* to data which is used in DebugTargetItem.
|
||||
*/
|
||||
const workerComponentDataMiddleware = store => next => action => {
|
||||
switch (action.type) {
|
||||
case REQUEST_WORKERS_SUCCESS: {
|
||||
action.otherWorkers = toComponentData(action.otherWorkers);
|
||||
action.serviceWorkers = toComponentData(action.serviceWorkers, true);
|
||||
action.sharedWorkers = toComponentData(action.sharedWorkers);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return next(action);
|
||||
};
|
||||
|
||||
function getServiceWorkerStatus(isActive, isRunning) {
|
||||
if (isActive && isRunning) {
|
||||
return SERVICE_WORKER_STATUSES.RUNNING;
|
||||
} else if (isActive) {
|
||||
return SERVICE_WORKER_STATUSES.STOPPED;
|
||||
}
|
||||
// We cannot get service worker registrations unless the registration is in
|
||||
// ACTIVE state. Unable to know the actual state ("installing", "waiting"), we
|
||||
// display a custom state "registering" for now. See Bug 1153292.
|
||||
return SERVICE_WORKER_STATUSES.REGISTERING;
|
||||
}
|
||||
|
||||
function toComponentData(workers, isServiceWorker) {
|
||||
return workers.map(worker => {
|
||||
const type = DEBUG_TARGETS.WORKER;
|
||||
const id = worker.workerTargetActor;
|
||||
const icon = "chrome://devtools/skin/images/debugging-workers.svg";
|
||||
let { fetch, name, registrationActor, scope } = worker;
|
||||
let isActive = false;
|
||||
let isRunning = false;
|
||||
let status = null;
|
||||
|
||||
if (isServiceWorker) {
|
||||
fetch = fetch ? SERVICE_WORKER_FETCH_STATES.LISTENING
|
||||
: SERVICE_WORKER_FETCH_STATES.NOT_LISTENING;
|
||||
isActive = worker.active;
|
||||
isRunning = !!worker.workerTargetActor;
|
||||
status = getServiceWorkerStatus(isActive, isRunning);
|
||||
}
|
||||
|
||||
return {
|
||||
name,
|
||||
icon,
|
||||
id,
|
||||
type,
|
||||
details: {
|
||||
fetch,
|
||||
isActive,
|
||||
isRunning,
|
||||
registrationActor,
|
||||
scope,
|
||||
status,
|
||||
},
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = workerComponentDataMiddleware;
|
@ -6,16 +6,19 @@
|
||||
|
||||
const {
|
||||
CONNECT_RUNTIME_SUCCESS,
|
||||
DEBUG_TARGETS,
|
||||
DISCONNECT_RUNTIME_SUCCESS,
|
||||
REQUEST_EXTENSIONS_SUCCESS,
|
||||
REQUEST_TABS_SUCCESS,
|
||||
REQUEST_WORKERS_SUCCESS,
|
||||
} = require("../constants");
|
||||
|
||||
function RuntimeState() {
|
||||
return {
|
||||
client: null,
|
||||
installedExtensions: [],
|
||||
otherWorkers: [],
|
||||
serviceWorkers: [],
|
||||
sharedWorkers: [],
|
||||
tabs: [],
|
||||
temporaryExtensions: [],
|
||||
};
|
||||
@ -32,14 +35,15 @@ function runtimeReducer(state = RuntimeState(), action) {
|
||||
}
|
||||
case REQUEST_EXTENSIONS_SUCCESS: {
|
||||
const { installedExtensions, temporaryExtensions } = action;
|
||||
return Object.assign({}, state, {
|
||||
installedExtensions: toExtensionComponentData(installedExtensions),
|
||||
temporaryExtensions: toExtensionComponentData(temporaryExtensions),
|
||||
});
|
||||
return Object.assign({}, state, { installedExtensions, temporaryExtensions });
|
||||
}
|
||||
case REQUEST_TABS_SUCCESS: {
|
||||
const { tabs } = action;
|
||||
return Object.assign({}, state, { tabs: toTabComponentData(tabs) });
|
||||
return Object.assign({}, state, { tabs });
|
||||
}
|
||||
case REQUEST_WORKERS_SUCCESS: {
|
||||
const { otherWorkers, serviceWorkers, sharedWorkers } = action;
|
||||
return Object.assign({}, state, { otherWorkers, serviceWorkers, sharedWorkers });
|
||||
}
|
||||
|
||||
default:
|
||||
@ -47,70 +51,6 @@ function runtimeReducer(state = RuntimeState(), action) {
|
||||
}
|
||||
}
|
||||
|
||||
function getExtensionFilePath(extension) {
|
||||
// Only show file system paths, and only for temporarily installed add-ons.
|
||||
if (!extension.temporarilyInstalled ||
|
||||
!extension.url ||
|
||||
!extension.url.startsWith("file://")) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Strip a leading slash from Windows drive letter URIs.
|
||||
// file:///home/foo ~> /home/foo
|
||||
// file:///C:/foo ~> C:/foo
|
||||
const windowsRegex = /^file:\/\/\/([a-zA-Z]:\/.*)/;
|
||||
|
||||
if (windowsRegex.test(extension.url)) {
|
||||
return windowsRegex.exec(extension.url)[1];
|
||||
}
|
||||
|
||||
return extension.url.slice("file://".length);
|
||||
}
|
||||
|
||||
function toExtensionComponentData(extensions) {
|
||||
return extensions.map(extension => {
|
||||
const type = DEBUG_TARGETS.EXTENSION;
|
||||
const { actor, iconURL, id, manifestURL, name, temporarilyInstalled } = extension;
|
||||
const icon = iconURL || "chrome://mozapps/skin/extensions/extensionGeneric.svg";
|
||||
const location = getExtensionFilePath(extension);
|
||||
const uuid = manifestURL ? /moz-extension:\/\/([^/]*)/.exec(manifestURL)[1] : null;
|
||||
return {
|
||||
name,
|
||||
icon,
|
||||
id,
|
||||
type,
|
||||
details: {
|
||||
actor,
|
||||
location,
|
||||
manifestURL,
|
||||
temporarilyInstalled,
|
||||
uuid,
|
||||
},
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function toTabComponentData(tabs) {
|
||||
return tabs.map(tab => {
|
||||
const type = DEBUG_TARGETS.TAB;
|
||||
const id = tab.outerWindowID;
|
||||
const icon = tab.favicon
|
||||
? `data:image/png;base64,${ btoa(String.fromCharCode.apply(String, tab.favicon)) }`
|
||||
: "chrome://devtools/skin/images/globe.svg";
|
||||
const name = tab.title || tab.url;
|
||||
const url = tab.url;
|
||||
return {
|
||||
name,
|
||||
icon,
|
||||
id,
|
||||
type,
|
||||
details: {
|
||||
url,
|
||||
},
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
RuntimeState,
|
||||
runtimeReducer,
|
||||
|
@ -4884,6 +4884,7 @@ exports.CSS_PROPERTIES = {
|
||||
"margin-box",
|
||||
"none",
|
||||
"padding-box",
|
||||
"path",
|
||||
"polygon",
|
||||
"stroke-box",
|
||||
"unset",
|
||||
@ -8269,6 +8270,7 @@ exports.CSS_PROPERTIES = {
|
||||
"margin-box",
|
||||
"none",
|
||||
"padding-box",
|
||||
"path",
|
||||
"polygon",
|
||||
"radial-gradient",
|
||||
"repeating-linear-gradient",
|
||||
|
@ -11,7 +11,6 @@
|
||||
#include "nsIPresShell.h"
|
||||
#include "nsView.h"
|
||||
#include "nsCaret.h"
|
||||
#include "nsEditorCID.h"
|
||||
#include "nsLayoutCID.h"
|
||||
#include "nsITextControlFrame.h"
|
||||
#include "nsContentCreatorFunctions.h"
|
||||
|
45
dom/security/fuzztest/csp_fuzzer.cpp
Normal file
45
dom/security/fuzztest/csp_fuzzer.cpp
Normal file
@ -0,0 +1,45 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "FuzzingInterface.h"
|
||||
#include "nsCSPContext.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsStringFwd.h"
|
||||
|
||||
static int
|
||||
LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
|
||||
{
|
||||
nsresult ret;
|
||||
nsCOMPtr<nsIURI> selfURI;
|
||||
ret = NS_NewURI(getter_AddRefs(selfURI), "http://selfuri.com");
|
||||
if (ret != NS_OK)
|
||||
return 0;
|
||||
|
||||
mozilla::OriginAttributes attrs;
|
||||
nsCOMPtr<nsIPrincipal> selfURIPrincipal =
|
||||
mozilla::BasePrincipal::CreateCodebasePrincipal(selfURI, attrs);
|
||||
if (!selfURIPrincipal)
|
||||
return 0;
|
||||
|
||||
nsCOMPtr<nsIContentSecurityPolicy> csp =
|
||||
do_CreateInstance(NS_CSPCONTEXT_CONTRACTID, &ret);
|
||||
if (ret != NS_OK)
|
||||
return 0;
|
||||
|
||||
ret = csp->SetRequestContext(nullptr, selfURIPrincipal);
|
||||
if (ret != NS_OK)
|
||||
return 0;
|
||||
|
||||
NS_ConvertASCIItoUTF16 policy(reinterpret_cast<const char*>(data), size);
|
||||
if (!policy.get())
|
||||
return 0;
|
||||
csp->AppendPolicy(policy, false, false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
MOZ_FUZZING_INTERFACE_RAW(nullptr, LLVMFuzzerTestOneInput, ContentSecurityPolicyParser);
|
||||
|
95
dom/security/fuzztest/csp_fuzzer.dict
Normal file
95
dom/security/fuzztest/csp_fuzzer.dict
Normal file
@ -0,0 +1,95 @@
|
||||
### dom/security/nsCSPParser.cpp
|
||||
# tokens
|
||||
":"
|
||||
";"
|
||||
"/"
|
||||
"+"
|
||||
"-"
|
||||
"."
|
||||
"_"
|
||||
"~"
|
||||
"*"
|
||||
"'"
|
||||
"#"
|
||||
"?"
|
||||
"%"
|
||||
"!"
|
||||
"$"
|
||||
"&"
|
||||
"("
|
||||
")"
|
||||
"="
|
||||
"@"
|
||||
|
||||
### https://www.w3.org/TR/{CSP,CSP2,CSP3}/
|
||||
# directive names
|
||||
"default-src"
|
||||
"script-src"
|
||||
"object-src"
|
||||
"style-src"
|
||||
"img-src"
|
||||
"media-src"
|
||||
"frame-src"
|
||||
"font-src"
|
||||
"connect-src"
|
||||
"report-uri"
|
||||
"frame-ancestors"
|
||||
"reflected-xss"
|
||||
"base-uri"
|
||||
"form-action"
|
||||
"manifest-src"
|
||||
"upgrade-insecure-requests"
|
||||
"child-src"
|
||||
"block-all-mixed-content"
|
||||
"require-sri-for"
|
||||
"sandbox"
|
||||
"worker-src"
|
||||
"plugin-types"
|
||||
"disown-opener"
|
||||
"report-to"
|
||||
|
||||
# directive values
|
||||
"'self'"
|
||||
"'unsafe-inline'"
|
||||
"'unsafe-eval'"
|
||||
"'none'"
|
||||
"'strict-dynamic'"
|
||||
"'unsafe-hashed-attributes'"
|
||||
"'nonce-AA=='"
|
||||
"'sha256-fw=='"
|
||||
"'sha384-/w=='"
|
||||
"'sha512-//8='"
|
||||
|
||||
# subresources
|
||||
"a"
|
||||
"audio"
|
||||
"embed"
|
||||
"iframe"
|
||||
"img"
|
||||
"link"
|
||||
"object"
|
||||
"script"
|
||||
"source"
|
||||
"style"
|
||||
"track"
|
||||
"video"
|
||||
|
||||
# sandboxing flags
|
||||
"allow-forms"
|
||||
"allow-pointer-lock"
|
||||
"allow-popups"
|
||||
"allow-same-origin"
|
||||
"allow-scripts"
|
||||
"allow-top-navigation"
|
||||
|
||||
# URI components
|
||||
"https:"
|
||||
"ws:"
|
||||
"blob:"
|
||||
"data:"
|
||||
"filesystem:"
|
||||
"javascript:"
|
||||
"http://"
|
||||
"selfuri.com"
|
||||
"127.0.0.1"
|
||||
"::1"
|
21
dom/security/fuzztest/moz.build
Normal file
21
dom/security/fuzztest/moz.build
Normal file
@ -0,0 +1,21 @@
|
||||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
Library('FuzzingDOMSecurity')
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'/dom/security',
|
||||
'/netwerk/base',
|
||||
]
|
||||
|
||||
include('/tools/fuzzing/libfuzzer-config.mozbuild')
|
||||
|
||||
SOURCES += [
|
||||
'csp_fuzzer.cpp'
|
||||
]
|
||||
|
||||
FINAL_LIBRARY = 'xul-gtest'
|
||||
|
@ -51,3 +51,11 @@ LOCAL_INCLUDES += [
|
||||
'/netwerk/base',
|
||||
'/netwerk/protocol/data', # for nsDataHandler.h
|
||||
]
|
||||
|
||||
include('/tools/fuzzing/libfuzzer-config.mozbuild')
|
||||
|
||||
if CONFIG['FUZZING_INTERFACES']:
|
||||
TEST_DIRS += [
|
||||
'fuzztest'
|
||||
]
|
||||
|
||||
|
@ -222,17 +222,6 @@ ServiceWorkerRegistration::Update(ErrorResult& aRv)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (RefPtr<ServiceWorkerGlobalScope> serviceWorkerGlobal =
|
||||
do_QueryObject(global)) {
|
||||
WorkerPrivate* wp;
|
||||
if (serviceWorkerGlobal->Registration() == this &&
|
||||
(wp = GetCurrentThreadWorkerPrivate()) &&
|
||||
wp->IsLoadingWorkerScript()) {
|
||||
outer->MaybeResolve(*this);
|
||||
return outer.forget();
|
||||
}
|
||||
}
|
||||
|
||||
RefPtr<ServiceWorkerRegistration> self = this;
|
||||
|
||||
mPendingUpdatePromises += 1;
|
||||
|
@ -826,8 +826,13 @@ ServiceWorkerRegistrationWorkerThread::Update(ServiceWorkerRegistrationCallback&
|
||||
return;
|
||||
}
|
||||
|
||||
// This is ensured by the binding layer.
|
||||
MOZ_ASSERT(!workerRef->Private()->IsLoadingWorkerScript());
|
||||
// Avoid infinite update loops by ignoring update() calls during top
|
||||
// level script evaluation. See:
|
||||
// https://github.com/slightlyoff/ServiceWorker/issues/800
|
||||
if (workerRef->Private()->IsLoadingWorkerScript()) {
|
||||
aSuccessCB(mDescriptor);
|
||||
return;
|
||||
}
|
||||
|
||||
auto promise = MakeRefPtr<ServiceWorkerRegistrationPromise::Private>(__func__);
|
||||
auto holder =
|
||||
|
@ -560,7 +560,8 @@ SVGPathData::BuildPathForMeasuring() const
|
||||
SVGPathData::BuildPath(const nsTArray<StylePathCommand>& aPath,
|
||||
PathBuilder* aBuilder,
|
||||
uint8_t aStrokeLineCap,
|
||||
Float aStrokeWidth)
|
||||
Float aStrokeWidth,
|
||||
float aZoomFactor)
|
||||
{
|
||||
if (aPath.IsEmpty() || !aPath[0].IsMoveTo()) {
|
||||
return nullptr; // paths without an initial moveto are invalid
|
||||
@ -592,6 +593,10 @@ SVGPathData::BuildPath(const nsTArray<StylePathCommand>& aPath,
|
||||
Point cp1, cp2; // previous bezier's control points
|
||||
Point tcp1, tcp2; // temporaries
|
||||
|
||||
auto scale = [aZoomFactor](const Point& p) {
|
||||
return Point(p.x * aZoomFactor, p.y * aZoomFactor);
|
||||
};
|
||||
|
||||
// Regarding cp1 and cp2: If the previous segment was a cubic bezier curve,
|
||||
// then cp2 is its second control point. If the previous segment was a
|
||||
// quadratic curve, then cp1 is its (only) control point.
|
||||
@ -610,7 +615,7 @@ SVGPathData::BuildPath(const nsTArray<StylePathCommand>& aPath,
|
||||
MAYBE_APPROXIMATE_ZERO_LENGTH_SUBPATH_SQUARE_CAPS_TO_DT;
|
||||
const Point& p = toGfxPoint(cmd.move_to.point);
|
||||
pathStart = segEnd = cmd.move_to.absolute ? p : segStart + p;
|
||||
aBuilder->MoveTo(segEnd);
|
||||
aBuilder->MoveTo(scale(segEnd));
|
||||
subpathHasLength = false;
|
||||
break;
|
||||
}
|
||||
@ -619,7 +624,7 @@ SVGPathData::BuildPath(const nsTArray<StylePathCommand>& aPath,
|
||||
segEnd = cmd.line_to.absolute ? p : segStart + p;
|
||||
if (segEnd != segStart) {
|
||||
subpathHasLength = true;
|
||||
aBuilder->LineTo(segEnd);
|
||||
aBuilder->LineTo(scale(segEnd));
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -636,7 +641,7 @@ SVGPathData::BuildPath(const nsTArray<StylePathCommand>& aPath,
|
||||
|
||||
if (segEnd != segStart || segEnd != cp1 || segEnd != cp2) {
|
||||
subpathHasLength = true;
|
||||
aBuilder->BezierTo(cp1, cp2, segEnd);
|
||||
aBuilder->BezierTo(scale(cp1), scale(cp2), scale(segEnd));
|
||||
}
|
||||
break;
|
||||
|
||||
@ -655,7 +660,7 @@ SVGPathData::BuildPath(const nsTArray<StylePathCommand>& aPath,
|
||||
|
||||
if (segEnd != segStart || segEnd != cp1) {
|
||||
subpathHasLength = true;
|
||||
aBuilder->BezierTo(tcp1, tcp2, segEnd);
|
||||
aBuilder->BezierTo(scale(tcp1), scale(tcp2), scale(segEnd));
|
||||
}
|
||||
break;
|
||||
|
||||
@ -669,12 +674,12 @@ SVGPathData::BuildPath(const nsTArray<StylePathCommand>& aPath,
|
||||
if (segEnd != segStart) {
|
||||
subpathHasLength = true;
|
||||
if (radii.x == 0.0f || radii.y == 0.0f) {
|
||||
aBuilder->LineTo(segEnd);
|
||||
aBuilder->LineTo(scale(segEnd));
|
||||
} else {
|
||||
nsSVGArcConverter converter(segStart, segEnd, radii, arc.angle,
|
||||
arc.large_arc_flag, arc.sweep_flag);
|
||||
while (converter.GetNextSegment(&cp1, &cp2, &segEnd)) {
|
||||
aBuilder->BezierTo(cp1, cp2, segEnd);
|
||||
aBuilder->BezierTo(scale(cp1), scale(cp2), scale(segEnd));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -689,7 +694,7 @@ SVGPathData::BuildPath(const nsTArray<StylePathCommand>& aPath,
|
||||
|
||||
if (segEnd != segStart) {
|
||||
subpathHasLength = true;
|
||||
aBuilder->LineTo(segEnd);
|
||||
aBuilder->LineTo(scale(segEnd));
|
||||
}
|
||||
break;
|
||||
|
||||
@ -702,7 +707,7 @@ SVGPathData::BuildPath(const nsTArray<StylePathCommand>& aPath,
|
||||
|
||||
if (segEnd != segStart) {
|
||||
subpathHasLength = true;
|
||||
aBuilder->LineTo(segEnd);
|
||||
aBuilder->LineTo(scale(segEnd));
|
||||
}
|
||||
break;
|
||||
|
||||
@ -718,7 +723,7 @@ SVGPathData::BuildPath(const nsTArray<StylePathCommand>& aPath,
|
||||
|
||||
if (segEnd != segStart || segEnd != cp1 || segEnd != cp2) {
|
||||
subpathHasLength = true;
|
||||
aBuilder->BezierTo(cp1, cp2, segEnd);
|
||||
aBuilder->BezierTo(scale(cp1), scale(cp2), scale(segEnd));
|
||||
}
|
||||
break;
|
||||
|
||||
@ -734,7 +739,7 @@ SVGPathData::BuildPath(const nsTArray<StylePathCommand>& aPath,
|
||||
|
||||
if (segEnd != segStart || segEnd != cp1) {
|
||||
subpathHasLength = true;
|
||||
aBuilder->BezierTo(tcp1, tcp2, segEnd);
|
||||
aBuilder->BezierTo(scale(tcp1), scale(tcp2), scale(segEnd));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -178,7 +178,8 @@ public:
|
||||
BuildPath(const nsTArray<StylePathCommand>& aPath,
|
||||
PathBuilder* aBuilder,
|
||||
uint8_t aCapStyle,
|
||||
Float aStrokeWidth);
|
||||
Float aStrokeWidth,
|
||||
float aZoomFactor = 1.0);
|
||||
|
||||
const_iterator begin() const { return mData.Elements(); }
|
||||
const_iterator end() const { return mData.Elements() + mData.Length(); }
|
||||
|
@ -677,7 +677,6 @@ SharedWorkerGlobalScope::Close()
|
||||
NS_IMPL_CYCLE_COLLECTION_INHERITED(ServiceWorkerGlobalScope, WorkerGlobalScope,
|
||||
mClients, mRegistration)
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ServiceWorkerGlobalScope)
|
||||
NS_INTERFACE_MAP_ENTRY_CONCRETE(ServiceWorkerGlobalScope)
|
||||
NS_INTERFACE_MAP_END_INHERITING(WorkerGlobalScope)
|
||||
|
||||
NS_IMPL_ADDREF_INHERITED(ServiceWorkerGlobalScope, WorkerGlobalScope)
|
||||
|
@ -302,9 +302,6 @@ public:
|
||||
IMPL_EVENT_HANDLER(connect)
|
||||
};
|
||||
|
||||
#define NS_DOM_SERVICEWORKERGLOBALSCOPE_IID \
|
||||
{0x552bfa7e, 0x0dd5, 0x4e94, {0xa0, 0x43, 0xff, 0x34, 0x6b, 0x6e, 0x04, 0x46}}
|
||||
|
||||
class ServiceWorkerGlobalScope final : public WorkerGlobalScope
|
||||
{
|
||||
const nsString mScope;
|
||||
@ -314,7 +311,6 @@ class ServiceWorkerGlobalScope final : public WorkerGlobalScope
|
||||
~ServiceWorkerGlobalScope();
|
||||
|
||||
public:
|
||||
NS_DECLARE_STATIC_IID_ACCESSOR(NS_DOM_SERVICEWORKERGLOBALSCOPE_IID)
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ServiceWorkerGlobalScope,
|
||||
WorkerGlobalScope)
|
||||
@ -359,8 +355,6 @@ public:
|
||||
void EventListenerAdded(nsAtom* aType) override;
|
||||
};
|
||||
|
||||
NS_DEFINE_STATIC_IID_ACCESSOR(ServiceWorkerGlobalScope, NS_DOM_SERVICEWORKERGLOBALSCOPE_IID)
|
||||
|
||||
class WorkerDebuggerGlobalScope final : public DOMEventTargetHelper,
|
||||
public nsIGlobalObject
|
||||
{
|
||||
|
@ -31,10 +31,6 @@ XPIDL_SOURCES += [
|
||||
|
||||
XPIDL_MODULE = 'editor'
|
||||
|
||||
EXPORTS += [
|
||||
'nsEditorCID.h',
|
||||
]
|
||||
|
||||
TESTING_JS_MODULES += [
|
||||
'AsyncSpellCheckTestHelper.jsm',
|
||||
]
|
||||
|
@ -1,25 +0,0 @@
|
||||
/* 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/. */
|
||||
|
||||
#ifndef NSEDITORCID_H___
|
||||
|
||||
#define NS_EDITOR_CID \
|
||||
{/* {D3DE3431-8A75-11d2-918C-0080C8E44DB5}*/ \
|
||||
0xd3de3431, 0x8a75, 0x11d2, \
|
||||
{ 0x91, 0x8c, 0x0, 0x80, 0xc8, 0xe4, 0x4d, 0xb5 } }
|
||||
|
||||
#define NS_TEXTEDITOR_CID \
|
||||
{/* {e197cc01-cfe1-11d4-8eb0-87ae406dfd3f}*/ \
|
||||
0xe197cc01, 0xcfe1, 0x11d4, \
|
||||
{ 0x8e, 0xb0, 0x87, 0xae, 0x40, 0x6d, 0xfd, 0x3f } }
|
||||
|
||||
#define NS_HTMLEDITOR_CID \
|
||||
{/* {ed0244e0-c144-11d2-8f4c-006008159b0c}*/ \
|
||||
0xed0244e0, 0xc144, 0x11d2, \
|
||||
{ 0x8f, 0x4c, 0x0, 0x60, 0x08, 0x15, 0x9b, 0x0c } }
|
||||
|
||||
#endif //NSEDITORCID_H___
|
||||
|
||||
|
||||
|
@ -10,10 +10,17 @@ let { exports } = wasmEvalText(`(module
|
||||
(func (export "f64") (result f64) (param f64)
|
||||
get_local 0
|
||||
)
|
||||
|
||||
(func (export "mixed_args") (result f64)
|
||||
(param i32) (param i32) (param i32) (param i32) (param i32) ;; 5 i32
|
||||
(param $f64 f64) ;; 1 f64
|
||||
(param i32)
|
||||
get_local $f64
|
||||
)
|
||||
)`);
|
||||
|
||||
const options = getJitCompilerOptions();
|
||||
const jitThreshold = options['ion.warmup.trigger'] * 2;
|
||||
const jitThreshold = options['ion.warmup.trigger'] * 2 + 2;
|
||||
|
||||
let coercions = {
|
||||
i32(x) { return x|0; },
|
||||
@ -38,23 +45,93 @@ function call(func, coercion, arg) {
|
||||
}
|
||||
}
|
||||
|
||||
const inputs = [
|
||||
42,
|
||||
3.5,
|
||||
-0,
|
||||
-Infinity,
|
||||
2**32,
|
||||
true,
|
||||
Symbol(),
|
||||
undefined,
|
||||
null,
|
||||
{},
|
||||
{ valueOf() { return 13.37; } },
|
||||
"bonjour"
|
||||
];
|
||||
// Test misc kinds of arguments.
|
||||
(function() {
|
||||
const inputs = [
|
||||
42,
|
||||
3.5,
|
||||
-0,
|
||||
-Infinity,
|
||||
2**32,
|
||||
true,
|
||||
Symbol(),
|
||||
undefined,
|
||||
null,
|
||||
{},
|
||||
{ valueOf() { return 13.37; } },
|
||||
"bonjour"
|
||||
];
|
||||
|
||||
for (let arg of inputs) {
|
||||
for (let func of ['i32', 'f32', 'f64']) {
|
||||
call(exports[func], coercions[func], arg);
|
||||
for (let arg of inputs) {
|
||||
for (let func of ['i32', 'f32', 'f64']) {
|
||||
call(exports[func], coercions[func], arg);
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
||||
// Test mixup of float and int arguments.
|
||||
(function() {
|
||||
for (let i = 0; i < 10; i++) {
|
||||
assertEq(exports.mixed_args(i, i+1, i+2, i+3, i+4, i+0.5, i+5), i+0.5);
|
||||
}
|
||||
})();
|
||||
|
||||
// Test high number of arguments.
|
||||
// All integers.
|
||||
let {func} = wasmEvalText(`(module
|
||||
(func (export "func") (result i32)
|
||||
${Array(32).join('(param i32)')}
|
||||
(param $last i32)
|
||||
get_local $last
|
||||
)
|
||||
)`).exports;
|
||||
|
||||
(function() {
|
||||
for (let i = 0; i < 10; i++) {
|
||||
assertEq(func(i, i+1, i+2, i+3, i+4, i+5, i+6, i+7, i+8, i+9, i+10, i+11, i+12, i+13, i+14, i+15,
|
||||
i+16, i+17, i+18, i+19, i+20, i+21, i+22, i+23, i+24, i+25, i+26, i+27, i+28, i+29, i+30, i+31
|
||||
), i+31);
|
||||
}
|
||||
})();
|
||||
|
||||
// All floats.
|
||||
func = wasmEvalText(`(module
|
||||
(func (export "func") (result i32)
|
||||
${Array(32).join('(param f64)')}
|
||||
(param $last i32)
|
||||
get_local $last
|
||||
)
|
||||
)`).exports.func;
|
||||
|
||||
(function() {
|
||||
for (let i = 0; i < 10; i++) {
|
||||
assertEq(func(i, i+1, i+2, i+3, i+4, i+5, i+6, i+7, i+8, i+9, i+10, i+11, i+12, i+13, i+14, i+15,
|
||||
i+16, i+17, i+18, i+19, i+20, i+21, i+22, i+23, i+24, i+25, i+26, i+27, i+28, i+29, i+30, i+31
|
||||
), i+31);
|
||||
}
|
||||
})();
|
||||
|
||||
// Mix em up! 1 i32, then 1 f32, then 1 f64, and again up to 32 args.
|
||||
let params = [];
|
||||
for (let i = 0; i < 32; i++) {
|
||||
params.push((i % 3 == 0) ? 'i32' :
|
||||
(i % 3 == 1) ? 'f32' :
|
||||
'f64'
|
||||
);
|
||||
}
|
||||
|
||||
func = wasmEvalText(`(module
|
||||
(func (export "func") (result i32)
|
||||
${Array(32).join('(param f64)')}
|
||||
(param $last i32)
|
||||
get_local $last
|
||||
)
|
||||
)`).exports.func;
|
||||
|
||||
(function() {
|
||||
for (let i = 0; i < 10; i++) {
|
||||
assertEq(func(i, i+1, i+2, i+3, i+4, i+5, i+6, i+7, i+8, i+9, i+10, i+11, i+12, i+13, i+14, i+15,
|
||||
i+16, i+17, i+18, i+19, i+20, i+21, i+22, i+23, i+24, i+25, i+26, i+27, i+28, i+29, i+30, i+31
|
||||
), i+31);
|
||||
}
|
||||
})();
|
||||
|
@ -10,8 +10,6 @@ const TRIGGER = options['ion.warmup.trigger'] + 10;
|
||||
const ITER = 2 * TRIGGER;
|
||||
const EXCEPTION_ITER = ITER - 2;
|
||||
|
||||
enableGeckoProfiling();
|
||||
|
||||
var instance = wasmEvalText(`(module
|
||||
(func $add (export "add") (result i32) (param i32) (param i32)
|
||||
get_local 0
|
||||
@ -25,8 +23,22 @@ var instance = wasmEvalText(`(module
|
||||
call $add
|
||||
i64.extend_s/i32
|
||||
)
|
||||
|
||||
(func $add_two_i64 (export "add_two_i64") (result i64) (param i64) (param i64)
|
||||
get_local 0
|
||||
get_local 1
|
||||
i64.add
|
||||
)
|
||||
)`).exports;
|
||||
|
||||
(function() {
|
||||
// In ion-eager mode, make sure we don't try to inline a function that
|
||||
// takes or returns i64 arguments.
|
||||
assertErrorMessage(() => instance.add_two_i64(0, 1), TypeError, /cannot pass i64 to or from JS/);
|
||||
})();
|
||||
|
||||
enableGeckoProfiling();
|
||||
|
||||
var callToMain;
|
||||
|
||||
function main() {
|
||||
@ -73,7 +85,9 @@ function main() {
|
||||
assertEq(+lines[0], arrayCallLine);
|
||||
assertEq(+lines[1], callToMain);
|
||||
} else if ((i % 2) == 0) {
|
||||
// Regular call to wasm add on 32 bits integers.
|
||||
assertEqPreciseStacks(profilingStack, [
|
||||
['', '0', ''], // supa-dupa fast path
|
||||
['', '>', '0,>', '>', ''], // fast path
|
||||
['', '!>', '0,!>', '!>', ''], // slow path
|
||||
]);
|
||||
|
@ -12,6 +12,7 @@ const EXCEPTION_ITER = TRIGGER + 5;
|
||||
|
||||
const SLOW_ENTRY_STACK = ['', '!>', '0,!>', '!>', ''];
|
||||
const FAST_ENTRY_STACK = ['', '>', '0,>', '>', ''];
|
||||
const INLINED_CALL_STACK = ['', '0', ''];
|
||||
const FAST_OOL_ENTRY_STACK = ['', '>', '<,>', 'ool>,>', '<,>', '>', '0,>', '>', ''];
|
||||
const EXCEPTION_ENTRY_STACK = ['', '>', '<,>', 'ool>,>', '<,>', '>', ''];
|
||||
|
||||
@ -44,7 +45,7 @@ for (let type of ['i32', 'f32', 'f64']) {
|
||||
for (var i = 0; i < ITER; i++) {
|
||||
startProfiling();
|
||||
loopBody(i + 1, i + EXCEPTION_ITER + 1);
|
||||
assertEqPreciseStacks(endProfiling(), [FAST_ENTRY_STACK, SLOW_ENTRY_STACK]);
|
||||
assertEqPreciseStacks(endProfiling(), [INLINED_CALL_STACK, FAST_ENTRY_STACK, SLOW_ENTRY_STACK]);
|
||||
|
||||
if (i === EXCEPTION_ITER) {
|
||||
x = { valueOf: function innerValueOf() { throw new Error("ph34r"); }};
|
||||
|
@ -25,6 +25,7 @@ let { add } = wasmEvalText(`(module
|
||||
|
||||
const SLOW_ENTRY_STACK = ['', '!>', '0,!>', '!>', ''];
|
||||
const FAST_ENTRY_STACK = ['', '>', '0,>', '>', ''];
|
||||
const INLINED_CALL_STACK = ['', '0', ''];
|
||||
|
||||
function main() {
|
||||
for (let i = 0; i < 50; i++) {
|
||||
@ -37,7 +38,7 @@ function main() {
|
||||
assertStackTrace(e, ['wasm-function[0]', 'main', '']);
|
||||
}
|
||||
let stack = endProfiling();
|
||||
assertEqPreciseStacks(stack, [FAST_ENTRY_STACK, SLOW_ENTRY_STACK]);
|
||||
assertEqPreciseStacks(stack, [INLINED_CALL_STACK, FAST_ENTRY_STACK, SLOW_ENTRY_STACK]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -12,6 +12,8 @@ const EXCEPTION_ITER = TRIGGER + 5;
|
||||
|
||||
const SLOW_ENTRY_STACK = ['', '!>', '0,!>', '!>', ''];
|
||||
const FAST_ENTRY_STACK = ['', '>', '0,>', '>', ''];
|
||||
const INLINED_CALL_STACK = ['', '0', ''];
|
||||
const EXPECTED_STACKS = [SLOW_ENTRY_STACK, FAST_ENTRY_STACK, INLINED_CALL_STACK];
|
||||
|
||||
function main() {
|
||||
var { table } = wasmEvalText(`(module
|
||||
@ -27,7 +29,7 @@ function main() {
|
||||
for (var i = 0; i < ITER; i++) {
|
||||
startProfiling();
|
||||
assertEq(table.get(0)(i, i+1), i*2+1);
|
||||
assertEqPreciseStacks(endProfiling(), [FAST_ENTRY_STACK, SLOW_ENTRY_STACK]);
|
||||
assertEqPreciseStacks(endProfiling(), EXPECTED_STACKS);
|
||||
}
|
||||
}
|
||||
|
||||
@ -50,13 +52,13 @@ function withTier2() {
|
||||
i++;
|
||||
startProfiling();
|
||||
assertEq(table.get(0)(i, i+1), i*2+1);
|
||||
assertEqPreciseStacks(endProfiling(), [FAST_ENTRY_STACK, SLOW_ENTRY_STACK]);
|
||||
assertEqPreciseStacks(endProfiling(), EXPECTED_STACKS);
|
||||
} while (!wasmHasTier2CompilationCompleted(module));
|
||||
|
||||
for (i = 0; i < ITER; i++) {
|
||||
startProfiling();
|
||||
assertEq(table.get(0)(i, i+1), i*2+1);
|
||||
assertEqPreciseStacks(endProfiling(), [FAST_ENTRY_STACK, SLOW_ENTRY_STACK]);
|
||||
assertEqPreciseStacks(endProfiling(), EXPECTED_STACKS);
|
||||
}
|
||||
|
||||
setJitCompilerOption('wasm.delay-tier2', 0);
|
||||
|
@ -5,7 +5,12 @@ const Module = WebAssembly.Module;
|
||||
const Instance = WebAssembly.Instance;
|
||||
const Table = WebAssembly.Table;
|
||||
|
||||
const { assertEqImpreciseStacks, startProfiling, endProfiling } = WasmHelpers;
|
||||
const {
|
||||
assertEqImpreciseStacks,
|
||||
assertEqPreciseStacks,
|
||||
startProfiling,
|
||||
endProfiling
|
||||
} = WasmHelpers;
|
||||
|
||||
function test(code, importObj, expectedStacks)
|
||||
{
|
||||
@ -380,3 +385,33 @@ for (let type of ['f32', 'f64']) {
|
||||
disableSingleStepProfiling();
|
||||
disableGeckoProfiling();
|
||||
})();
|
||||
|
||||
// Ion->wasm calls.
|
||||
let func = wasmEvalText(`(module
|
||||
(func $inner (result i32) (param i32) (param i32)
|
||||
get_local 0
|
||||
get_local 1
|
||||
i32.add
|
||||
)
|
||||
(func (export "add") (result i32) (param i32) (param i32)
|
||||
get_local 0
|
||||
get_local 1
|
||||
call $inner
|
||||
)
|
||||
)`).exports.add;
|
||||
|
||||
(function() {
|
||||
enableGeckoProfiling();
|
||||
// 10 is enough in ion eager mode.
|
||||
for (let i = 0; i < 10; i++) {
|
||||
enableSingleStepProfiling();
|
||||
let res = func(i - 1, i + 1);
|
||||
assertEqPreciseStacks(disableSingleStepProfiling(), [
|
||||
['', '>', '1,>', '0,1,>' , '1,>', '>', ''], // slow entry
|
||||
['', '!>', '1,!>', '0,1,!>' , '1,!>', '!>', ''], // fast entry
|
||||
['', '1', '0,1' , '1', ''], // inlined jit call
|
||||
]);
|
||||
assertEq(res, i+i);
|
||||
}
|
||||
disableGeckoProfiling();
|
||||
})();
|
||||
|
@ -108,7 +108,7 @@ static const uint32_t FAKE_EXITFP_FOR_BAILOUT_ADDR = 0xba2;
|
||||
static uint8_t* const FAKE_EXITFP_FOR_BAILOUT =
|
||||
reinterpret_cast<uint8_t*>(FAKE_EXITFP_FOR_BAILOUT_ADDR);
|
||||
|
||||
static_assert(!(FAKE_EXITFP_FOR_BAILOUT_ADDR & JitActivation::ExitFpWasmBit),
|
||||
static_assert(!(FAKE_EXITFP_FOR_BAILOUT_ADDR & wasm::ExitOrJitEntryFPTag),
|
||||
"FAKE_EXITFP_FOR_BAILOUT could be mistaken as a low-bit tagged wasm exit fp");
|
||||
|
||||
// BailoutStack is an architecture specific pointer to the stack, given by the
|
||||
|
@ -54,6 +54,7 @@
|
||||
#include "vm/TraceLogging.h"
|
||||
#include "vm/TypedArrayObject.h"
|
||||
#include "vtune/VTuneWrapper.h"
|
||||
#include "wasm/WasmStubs.h"
|
||||
|
||||
#include "builtin/Boolean-inl.h"
|
||||
#include "jit/MacroAssembler-inl.h"
|
||||
@ -13556,6 +13557,133 @@ CodeGenerator::visitGetPrototypeOf(LGetPrototypeOf* lir)
|
||||
masm.bind(ool->rejoin());
|
||||
}
|
||||
|
||||
template <size_t NumDefs>
|
||||
void
|
||||
CodeGenerator::emitIonToWasmCallBase(LIonToWasmCallBase<NumDefs>* lir)
|
||||
{
|
||||
wasm::JitCallStackArgVector stackArgs;
|
||||
masm.propagateOOM(stackArgs.reserve(lir->numOperands()));
|
||||
if (masm.oom())
|
||||
return;
|
||||
|
||||
const wasm::FuncExport& funcExport = lir->mir()->funcExport();
|
||||
const wasm::FuncType& sig = funcExport.funcType();
|
||||
|
||||
ABIArgGenerator abi;
|
||||
for (size_t i = 0; i < lir->numOperands(); i++) {
|
||||
MIRType argMir;
|
||||
switch (sig.args()[i].code()) {
|
||||
case wasm::ValType::I32:
|
||||
case wasm::ValType::F32:
|
||||
case wasm::ValType::F64:
|
||||
argMir = ToMIRType(sig.args()[i]);
|
||||
break;
|
||||
case wasm::ValType::I64:
|
||||
case wasm::ValType::Ref:
|
||||
case wasm::ValType::AnyRef:
|
||||
// Don't forget to trace GC type arguments in TraceJitExitFrames
|
||||
// when they're enabled.
|
||||
MOZ_CRASH("unexpected argument type when calling from ion to wasm");
|
||||
}
|
||||
|
||||
ABIArg arg = abi.next(argMir);
|
||||
switch (arg.kind()) {
|
||||
case ABIArg::GPR:
|
||||
case ABIArg::FPU: {
|
||||
MOZ_ASSERT(ToAnyRegister(lir->getOperand(i)) == arg.reg());
|
||||
stackArgs.infallibleEmplaceBack(wasm::JitCallStackArg());
|
||||
break;
|
||||
}
|
||||
case ABIArg::Stack: {
|
||||
const LAllocation* larg = lir->getOperand(i);
|
||||
if (larg->isConstant())
|
||||
stackArgs.infallibleEmplaceBack(ToInt32(larg));
|
||||
else if (larg->isGeneralReg())
|
||||
stackArgs.infallibleEmplaceBack(ToRegister(larg));
|
||||
else if (larg->isFloatReg())
|
||||
stackArgs.infallibleEmplaceBack(ToFloatRegister(larg));
|
||||
else
|
||||
stackArgs.infallibleEmplaceBack(ToAddress(larg));
|
||||
break;
|
||||
}
|
||||
#ifdef JS_CODEGEN_REGISTER_PAIR
|
||||
case ABIArg::GPR_PAIR: {
|
||||
MOZ_CRASH("no way to pass i64, and wasm uses hardfp for function calls");
|
||||
}
|
||||
#endif
|
||||
case ABIArg::Uninitialized: {
|
||||
MOZ_CRASH("Uninitialized ABIArg kind");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch (sig.ret().code()) {
|
||||
case wasm::ExprType::Void:
|
||||
MOZ_ASSERT(lir->mir()->type() == MIRType::Value);
|
||||
break;
|
||||
case wasm::ExprType::I32:
|
||||
MOZ_ASSERT(lir->mir()->type() == MIRType::Int32);
|
||||
MOZ_ASSERT(ToRegister(lir->output()) == ReturnReg);
|
||||
break;
|
||||
case wasm::ExprType::F32:
|
||||
MOZ_ASSERT(lir->mir()->type() == MIRType::Float32);
|
||||
MOZ_ASSERT(ToFloatRegister(lir->output()) == ReturnFloat32Reg);
|
||||
break;
|
||||
case wasm::ExprType::F64:
|
||||
MOZ_ASSERT(lir->mir()->type() == MIRType::Double);
|
||||
MOZ_ASSERT(ToFloatRegister(lir->output()) == ReturnDoubleReg);
|
||||
break;
|
||||
case wasm::ExprType::Ref:
|
||||
case wasm::ExprType::AnyRef:
|
||||
case wasm::ExprType::I64:
|
||||
// Don't forget to trace GC type return value in TraceJitExitFrames
|
||||
// when they're enabled.
|
||||
MOZ_CRASH("unexpected return type when calling from ion to wasm");
|
||||
case wasm::ExprType::Limit:
|
||||
MOZ_CRASH("Limit");
|
||||
}
|
||||
|
||||
bool profilingEnabled = isProfilerInstrumentationEnabled();
|
||||
WasmInstanceObject* instObj = lir->mir()->instanceObject();
|
||||
|
||||
bool wasmGcEnabled = false;
|
||||
#ifdef ENABLE_WASM_GC
|
||||
wasmGcEnabled = gen->options.wasmGcEnabled();
|
||||
#endif
|
||||
|
||||
Register scratch = ToRegister(lir->temp());
|
||||
|
||||
uint32_t callOffset;
|
||||
GenerateDirectCallFromJit(masm,
|
||||
funcExport,
|
||||
instObj->instance(),
|
||||
stackArgs,
|
||||
profilingEnabled,
|
||||
wasmGcEnabled,
|
||||
scratch,
|
||||
&callOffset);
|
||||
|
||||
// Add the instance object to the constant pool, so it is transferred to
|
||||
// the owning IonScript and so that it gets traced as long as the IonScript
|
||||
// lives.
|
||||
|
||||
uint32_t unused;
|
||||
masm.propagateOOM(graph.addConstantToPool(ObjectValue(*instObj), &unused));
|
||||
|
||||
markSafepointAt(callOffset, lir);
|
||||
}
|
||||
|
||||
void
|
||||
CodeGenerator::visitIonToWasmCall(LIonToWasmCall* lir)
|
||||
{
|
||||
emitIonToWasmCallBase(lir);
|
||||
}
|
||||
void
|
||||
CodeGenerator::visitIonToWasmCallV(LIonToWasmCallV* lir)
|
||||
{
|
||||
emitIonToWasmCallBase(lir);
|
||||
}
|
||||
|
||||
static_assert(!std::is_polymorphic<CodeGenerator>::value,
|
||||
"CodeGenerator should not have any virtual methods");
|
||||
|
||||
|
@ -219,6 +219,9 @@ class CodeGenerator final : public CodeGeneratorSpecific
|
||||
|
||||
void emitWasmCallBase(MWasmCall* mir, bool needsBoundsCheck);
|
||||
|
||||
template<size_t NumDefs>
|
||||
void emitIonToWasmCallBase(LIonToWasmCallBase<NumDefs>* lir);
|
||||
|
||||
IonScriptCounts* maybeCreateScriptCounts();
|
||||
|
||||
// This function behaves like testValueTruthy with the exception that it can
|
||||
|
@ -322,6 +322,9 @@ JitCompileOptions::JitCompileOptions()
|
||||
: cloneSingletons_(false),
|
||||
profilerSlowAssertionsEnabled_(false),
|
||||
offThreadCompilationAvailable_(false)
|
||||
#ifdef ENABLE_WASM_GC
|
||||
, wasmGcEnabled_(false)
|
||||
#endif
|
||||
{
|
||||
}
|
||||
|
||||
@ -331,4 +334,7 @@ JitCompileOptions::JitCompileOptions(JSContext* cx)
|
||||
profilerSlowAssertionsEnabled_ = cx->runtime()->geckoProfiler().enabled() &&
|
||||
cx->runtime()->geckoProfiler().slowAssertionsEnabled();
|
||||
offThreadCompilationAvailable_ = OffThreadCompilationAvailable(cx);
|
||||
#ifdef ENABLE_WASM_GC
|
||||
wasmGcEnabled_ = cx->options().wasmGc();
|
||||
#endif
|
||||
}
|
||||
|
@ -138,10 +138,19 @@ class JitCompileOptions
|
||||
return offThreadCompilationAvailable_;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_WASM_GC
|
||||
bool wasmGcEnabled() const {
|
||||
return wasmGcEnabled_;
|
||||
}
|
||||
#endif
|
||||
|
||||
private:
|
||||
bool cloneSingletons_;
|
||||
bool profilerSlowAssertionsEnabled_;
|
||||
bool offThreadCompilationAvailable_;
|
||||
#ifdef ENABLE_WASM_GC
|
||||
bool wasmGcEnabled_;
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace jit
|
||||
|
@ -11703,7 +11703,7 @@ IonBuilder::setPropTryTypedObject(bool* emitted, MDefinition* obj,
|
||||
TypedObjectPrediction fieldPrediction;
|
||||
size_t fieldOffset;
|
||||
size_t fieldIndex;
|
||||
bool fieldMutable;
|
||||
bool fieldMutable = false;
|
||||
if (!typedObjectHasField(obj, name, &fieldOffset, &fieldPrediction, &fieldIndex, &fieldMutable))
|
||||
return Ok();
|
||||
|
||||
|
@ -750,6 +750,7 @@ class IonBuilder
|
||||
InliningResult inlineObjectHasPrototype(CallInfo& callInfo);
|
||||
InliningResult inlineFinishBoundFunctionInit(CallInfo& callInfo);
|
||||
InliningResult inlineIsPackedArray(CallInfo& callInfo);
|
||||
InliningResult inlineWasmCall(CallInfo& callInfo, JSFunction* target);
|
||||
|
||||
// Testing functions.
|
||||
InliningResult inlineBailout(CallInfo& callInfo);
|
||||
|
@ -418,7 +418,7 @@ enum class IntConversionInputKind {
|
||||
// The ordering of this enumeration is important: Anything < Value is a
|
||||
// specialized type. Furthermore, anything < String has trivial conversion to
|
||||
// a number.
|
||||
enum class MIRType
|
||||
enum class MIRType: uint8_t
|
||||
{
|
||||
Undefined,
|
||||
Null,
|
||||
@ -462,7 +462,7 @@ enum class MIRType
|
||||
static inline bool
|
||||
IsSimdType(MIRType type)
|
||||
{
|
||||
return ((unsigned(type) >> VECTOR_SCALE_SHIFT) & VECTOR_SCALE_MASK) != 0;
|
||||
return ((uint8_t(type) >> VECTOR_SCALE_SHIFT) & VECTOR_SCALE_MASK) != 0;
|
||||
}
|
||||
|
||||
static inline MIRType
|
||||
|
@ -32,14 +32,15 @@ JSJitFrameIter::JSJitFrameIter(const JitActivation* activation)
|
||||
}
|
||||
}
|
||||
|
||||
JSJitFrameIter::JSJitFrameIter(const JitActivation* activation, uint8_t* fp)
|
||||
JSJitFrameIter::JSJitFrameIter(const JitActivation* activation, FrameType frameType, uint8_t* fp)
|
||||
: current_(fp),
|
||||
type_(JitFrame_JSJitToWasm),
|
||||
type_(frameType),
|
||||
returnAddressToFp_(nullptr),
|
||||
frameSize_(0),
|
||||
cachedSafepointIndex_(nullptr),
|
||||
activation_(activation)
|
||||
{
|
||||
MOZ_ASSERT(type_ == JitFrame_JSJitToWasm || type_ == JitFrame_Exit);
|
||||
MOZ_ASSERT(!activation_->bailoutData());
|
||||
MOZ_ASSERT(!TlsContext.get()->inUnsafeCallWithABI);
|
||||
}
|
||||
|
@ -119,7 +119,7 @@ class JSJitFrameIter
|
||||
|
||||
// A constructor specialized for jit->wasm frames, which starts at a
|
||||
// specific FP.
|
||||
JSJitFrameIter(const JitActivation* activation, uint8_t* fp);
|
||||
JSJitFrameIter(const JitActivation* activation, FrameType frameType, uint8_t* fp);
|
||||
|
||||
// Used only by DebugModeOSRVolatileJitFrameIter.
|
||||
void exchangeReturnAddressIfMatch(uint8_t* oldAddr, uint8_t* newAddr) {
|
||||
|
@ -1170,6 +1170,12 @@ TraceJitExitFrame(JSTracer* trc, const JSJitFrameIter& frame)
|
||||
return;
|
||||
}
|
||||
|
||||
if (frame.isExitFrameLayout<DirectWasmJitCallFrameLayout>()) {
|
||||
// Nothing to do: we can't have object arguments (yet!) and the callee
|
||||
// is traced elsewhere.
|
||||
return;
|
||||
}
|
||||
|
||||
if (frame.isBareExit()) {
|
||||
// Nothing to trace. Fake exit frame pushed for VM functions with
|
||||
// nothing to trace on the stack.
|
||||
|
@ -471,18 +471,19 @@ class IonICCallFrameLayout : public CommonFrameLayout
|
||||
|
||||
enum class ExitFrameType : uint8_t
|
||||
{
|
||||
CallNative = 0x0,
|
||||
ConstructNative = 0x1,
|
||||
IonDOMGetter = 0x2,
|
||||
IonDOMSetter = 0x3,
|
||||
IonDOMMethod = 0x4,
|
||||
IonOOLNative = 0x5,
|
||||
IonOOLProxy = 0x6,
|
||||
WasmJitEntry = 0x7,
|
||||
InterpreterStub = 0xFC,
|
||||
VMFunction = 0xFD,
|
||||
LazyLink = 0xFE,
|
||||
Bare = 0xFF,
|
||||
CallNative = 0x0,
|
||||
ConstructNative = 0x1,
|
||||
IonDOMGetter = 0x2,
|
||||
IonDOMSetter = 0x3,
|
||||
IonDOMMethod = 0x4,
|
||||
IonOOLNative = 0x5,
|
||||
IonOOLProxy = 0x6,
|
||||
WasmGenericJitEntry = 0x7,
|
||||
DirectWasmJitCall = 0x8,
|
||||
InterpreterStub = 0xFC,
|
||||
VMFunction = 0xFD,
|
||||
LazyLink = 0xFE,
|
||||
Bare = 0xFF,
|
||||
};
|
||||
|
||||
// GC related data used to keep alive data surrounding the Exit frame.
|
||||
@ -855,10 +856,10 @@ class InterpreterStubExitFrameLayout : public CalledFromJitExitFrameLayout
|
||||
static ExitFrameType Type() { return ExitFrameType::InterpreterStub; }
|
||||
};
|
||||
|
||||
class WasmExitFrameLayout : CalledFromJitExitFrameLayout
|
||||
class WasmGenericJitEntryFrameLayout : CalledFromJitExitFrameLayout
|
||||
{
|
||||
public:
|
||||
static ExitFrameType Type() { return ExitFrameType::WasmJitEntry; }
|
||||
static ExitFrameType Type() { return ExitFrameType::WasmGenericJitEntry; }
|
||||
};
|
||||
|
||||
template<>
|
||||
@ -867,7 +868,7 @@ ExitFrameLayout::is<CalledFromJitExitFrameLayout>()
|
||||
{
|
||||
return is<InterpreterStubExitFrameLayout>() ||
|
||||
is<LazyLinkExitFrameLayout>() ||
|
||||
is<WasmExitFrameLayout>();
|
||||
is<WasmGenericJitEntryFrameLayout>();
|
||||
}
|
||||
|
||||
template <>
|
||||
@ -880,6 +881,15 @@ ExitFrameLayout::as<CalledFromJitExitFrameLayout>()
|
||||
return reinterpret_cast<CalledFromJitExitFrameLayout*>(sp);
|
||||
}
|
||||
|
||||
class DirectWasmJitCallFrameLayout
|
||||
{
|
||||
protected: // silence clang warning about unused private fields
|
||||
ExitFooterFrame footer_;
|
||||
ExitFrameLayout exit_;
|
||||
public:
|
||||
static ExitFrameType Type() { return ExitFrameType::DirectWasmJitCall; }
|
||||
};
|
||||
|
||||
class ICStub;
|
||||
|
||||
class JitStubFrameLayout : public CommonFrameLayout
|
||||
|
@ -5343,5 +5343,58 @@ LIRGenerator::visitUnknownValue(MUnknownValue* ins)
|
||||
MOZ_CRASH("Can not lower unknown value.");
|
||||
}
|
||||
|
||||
void
|
||||
LIRGenerator::visitIonToWasmCall(MIonToWasmCall* ins)
|
||||
{
|
||||
// The instruction needs a temp register:
|
||||
// - that's not the FramePointer, since wasm is going to use it in the
|
||||
// function.
|
||||
// - that's not aliasing an input register.
|
||||
LDefinition scratch = tempFixed(ABINonArgReg0);
|
||||
|
||||
// Also prevent register allocation from using wasm's FramePointer, in
|
||||
// non-profiling mode.
|
||||
LDefinition fp = gen->isProfilerInstrumentationEnabled()
|
||||
? LDefinition::BogusTemp()
|
||||
: tempFixed(FramePointer);
|
||||
|
||||
// Note that since this is a LIR call instruction, regalloc will prevent
|
||||
// the use*AtStart below from reusing any of the temporaries.
|
||||
|
||||
LInstruction* lir;
|
||||
if (ins->type() == MIRType::Value)
|
||||
lir = allocateVariadic<LIonToWasmCallV>(ins->numOperands(), scratch, fp);
|
||||
else
|
||||
lir = allocateVariadic<LIonToWasmCall>(ins->numOperands(), scratch, fp);
|
||||
if (!lir) {
|
||||
abort(AbortReason::Alloc, "Couldn't allocate for LIonToWasmCallBase");
|
||||
return;
|
||||
}
|
||||
|
||||
ABIArgGenerator abi;
|
||||
for (unsigned i = 0; i < ins->numOperands(); i++) {
|
||||
MDefinition* argDef = ins->getOperand(i);
|
||||
ABIArg arg = abi.next(ToMIRType(argDef->type()));
|
||||
switch (arg.kind()) {
|
||||
case ABIArg::GPR:
|
||||
case ABIArg::FPU:
|
||||
lir->setOperand(i, useFixedAtStart(argDef, arg.reg()));
|
||||
break;
|
||||
case ABIArg::Stack:
|
||||
lir->setOperand(i, useAtStart(argDef));
|
||||
break;
|
||||
#ifdef JS_CODEGEN_REGISTER_PAIR
|
||||
case ABIArg::GPR_PAIR:
|
||||
MOZ_CRASH("no way to pass i64, and wasm uses hardfp for function calls");
|
||||
#endif
|
||||
case ABIArg::Uninitialized:
|
||||
MOZ_CRASH("Uninitialized ABIArg kind");
|
||||
}
|
||||
}
|
||||
|
||||
defineReturn(lir, ins);
|
||||
assignSafepoint(lir, ins);
|
||||
}
|
||||
|
||||
static_assert(!std::is_polymorphic<LIRGenerator>::value,
|
||||
"LIRGenerator should not have any virtual methods");
|
||||
|
@ -31,6 +31,7 @@
|
||||
#include "vm/SelfHosting.h"
|
||||
#include "vm/SharedArrayObject.h"
|
||||
#include "vm/TypedArrayObject.h"
|
||||
#include "wasm/WasmInstance.h"
|
||||
|
||||
#include "jit/shared/Lowering-shared-inl.h"
|
||||
#include "vm/JSScript-inl.h"
|
||||
@ -40,6 +41,7 @@
|
||||
|
||||
using mozilla::ArrayLength;
|
||||
using mozilla::AssertedCast;
|
||||
using mozilla::Maybe;
|
||||
|
||||
using JS::DoubleNaNValue;
|
||||
using JS::TrackedOutcome;
|
||||
@ -57,7 +59,11 @@ IonBuilder::inlineNativeCall(CallInfo& callInfo, JSFunction* target)
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
if (!target->hasJitInfo() || target->jitInfo()->type() != JSJitInfo::InlinableNative) {
|
||||
bool isWasmCall = target->isWasmOptimized();
|
||||
if (!isWasmCall &&
|
||||
(!target->hasJitInfo() ||
|
||||
target->jitInfo()->type() != JSJitInfo::InlinableNative))
|
||||
{
|
||||
// Reaching here means we tried to inline a native for which there is no
|
||||
// Ion specialization.
|
||||
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeNoSpecialization);
|
||||
@ -81,6 +87,9 @@ IonBuilder::inlineNativeCall(CallInfo& callInfo, JSFunction* target)
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
if (isWasmCall)
|
||||
return inlineWasmCall(callInfo, target);
|
||||
|
||||
switch (InlinableNative inlNative = target->jitInfo()->inlinableNative) {
|
||||
// Array natives.
|
||||
case InlinableNative::Array:
|
||||
@ -3773,6 +3782,83 @@ IonBuilder::inlineConstructTypedObject(CallInfo& callInfo, TypeDescr* descr)
|
||||
return InliningStatus_Inlined;
|
||||
}
|
||||
|
||||
IonBuilder::InliningResult
|
||||
IonBuilder::inlineWasmCall(CallInfo& callInfo, JSFunction* target)
|
||||
{
|
||||
MOZ_ASSERT(target->isWasmOptimized());
|
||||
MOZ_ASSERT(target->realm() == script()->realm());
|
||||
|
||||
// Don't inline wasm constructors.
|
||||
if (callInfo.constructing()) {
|
||||
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
wasm::Instance& inst = wasm::ExportedFunctionToInstance(target);
|
||||
uint32_t funcIndex = inst.code().getFuncIndex(target);
|
||||
|
||||
auto bestTier = inst.code().bestTier();
|
||||
const wasm::FuncExport& funcExport = inst.metadata(bestTier).lookupFuncExport(funcIndex);
|
||||
const wasm::FuncType& sig = funcExport.funcType();
|
||||
|
||||
// Check that the function doesn't take or return non-compatible JS
|
||||
// argument types before adding nodes to the MIR graph, otherwise they'd be
|
||||
// dead code.
|
||||
if (sig.hasI64ArgOrRet() || sig.temporarilyUnsupportedAnyRef())
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
// If there are too many arguments, don't inline (we won't be able to store
|
||||
// the arguments in the LIR node).
|
||||
static constexpr size_t MaxNumInlinedArgs = 8;
|
||||
static_assert(MaxNumInlinedArgs <= MaxNumLInstructionOperands,
|
||||
"inlined arguments can all be LIR operands");
|
||||
if (sig.args().length() > MaxNumInlinedArgs)
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
auto* call = MIonToWasmCall::New(alloc(), inst.object(), funcExport);
|
||||
if (!call)
|
||||
return abort(AbortReason::Alloc);
|
||||
|
||||
Maybe<MDefinition*> undefined;
|
||||
for (size_t i = 0; i < sig.args().length(); i++) {
|
||||
if (!alloc().ensureBallast())
|
||||
return abort(AbortReason::Alloc);
|
||||
|
||||
// Add undefined if an argument is missing.
|
||||
if (i >= callInfo.argc() && !undefined)
|
||||
undefined.emplace(constant(UndefinedValue()));
|
||||
|
||||
MDefinition* arg = i >= callInfo.argc() ? *undefined : callInfo.getArg(i);
|
||||
|
||||
MInstruction* conversion = nullptr;
|
||||
switch (sig.args()[i].code()) {
|
||||
case wasm::ValType::I32:
|
||||
conversion = MTruncateToInt32::New(alloc(), arg);
|
||||
break;
|
||||
case wasm::ValType::F32:
|
||||
conversion = MToFloat32::New(alloc(), arg);
|
||||
break;
|
||||
case wasm::ValType::F64:
|
||||
conversion = MToDouble::New(alloc(), arg);
|
||||
break;
|
||||
case wasm::ValType::I64:
|
||||
case wasm::ValType::AnyRef:
|
||||
case wasm::ValType::Ref:
|
||||
MOZ_CRASH("impossible per above check");
|
||||
}
|
||||
|
||||
current->add(conversion);
|
||||
call->initArg(i, conversion);
|
||||
}
|
||||
|
||||
current->push(call);
|
||||
current->add(call);
|
||||
|
||||
callInfo.setImplicitlyUsedUnchecked();
|
||||
|
||||
return InliningStatus_Inlined;
|
||||
}
|
||||
|
||||
#define ADD_NATIVE(native) const JSJitInfo JitInfo_##native { \
|
||||
{ nullptr }, { uint16_t(InlinableNative::native) }, { 0 }, JSJitInfo::InlinableNative };
|
||||
INLINABLE_NATIVE_LIST(ADD_NATIVE)
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include "jit/RangeAnalysis.h"
|
||||
#include "js/Conversions.h"
|
||||
#include "util/Text.h"
|
||||
#include "wasm/WasmCode.h"
|
||||
|
||||
#include "builtin/Boolean-inl.h"
|
||||
|
||||
@ -6305,3 +6306,33 @@ jit::PropertyWriteNeedsTypeBarrier(TempAllocator& alloc, CompilerConstraintList*
|
||||
*pobj = AddGroupGuard(alloc, current, *pobj, excluded, /* bailOnEquality = */ true);
|
||||
return false;
|
||||
}
|
||||
|
||||
MIonToWasmCall*
|
||||
MIonToWasmCall::New(TempAllocator& alloc, WasmInstanceObject* instanceObj,
|
||||
const wasm::FuncExport& funcExport)
|
||||
{
|
||||
wasm::ExprType retType = funcExport.funcType().ret();
|
||||
|
||||
MIRType resultType = retType.code() == wasm::ExprType::Void
|
||||
? MIRType::Value
|
||||
: ToMIRType(retType);
|
||||
|
||||
auto* ins = new(alloc) MIonToWasmCall(instanceObj, resultType, funcExport);
|
||||
if (!ins->init(alloc, funcExport.funcType().args().length()))
|
||||
return nullptr;
|
||||
return ins;
|
||||
}
|
||||
|
||||
bool
|
||||
MIonToWasmCall::appendRoots(MRootList& roots) const
|
||||
{
|
||||
return roots.append(instanceObj_);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
bool
|
||||
MIonToWasmCall::isConsistentFloat32Use(MUse* use) const
|
||||
{
|
||||
return funcExport_.funcType().args()[use->index()].code() == wasm::ValType::F32;
|
||||
}
|
||||
#endif
|
||||
|
@ -36,6 +36,10 @@
|
||||
|
||||
namespace js {
|
||||
|
||||
namespace wasm {
|
||||
class FuncExport;
|
||||
}
|
||||
|
||||
class StringObject;
|
||||
|
||||
namespace jit {
|
||||
@ -13966,6 +13970,47 @@ class MUnknownValue : public MNullaryInstruction
|
||||
TRIVIAL_NEW_WRAPPERS
|
||||
};
|
||||
|
||||
class MIonToWasmCall final :
|
||||
public MVariadicInstruction,
|
||||
public NoTypePolicy::Data
|
||||
{
|
||||
CompilerGCPointer<WasmInstanceObject*> instanceObj_;
|
||||
const wasm::FuncExport& funcExport_;
|
||||
|
||||
MIonToWasmCall(WasmInstanceObject* instanceObj, MIRType resultType,
|
||||
const wasm::FuncExport& funcExport)
|
||||
: MVariadicInstruction(classOpcode),
|
||||
instanceObj_(instanceObj),
|
||||
funcExport_(funcExport)
|
||||
{
|
||||
setResultType(resultType);
|
||||
}
|
||||
|
||||
public:
|
||||
INSTRUCTION_HEADER(IonToWasmCall);
|
||||
|
||||
static MIonToWasmCall* New(TempAllocator& alloc, WasmInstanceObject* instanceObj,
|
||||
const wasm::FuncExport& funcExport);
|
||||
|
||||
void initArg(size_t i, MDefinition* arg) {
|
||||
initOperand(i, arg);
|
||||
}
|
||||
|
||||
WasmInstanceObject* instanceObject() const {
|
||||
return instanceObj_;
|
||||
}
|
||||
const wasm::FuncExport& funcExport() const {
|
||||
return funcExport_;
|
||||
}
|
||||
bool possiblyCalls() const override {
|
||||
return true;
|
||||
}
|
||||
bool appendRoots(MRootList& roots) const override;
|
||||
#ifdef DEBUG
|
||||
bool isConsistentFloat32Use(MUse* use) const override;
|
||||
#endif
|
||||
};
|
||||
|
||||
#undef INSTRUCTION_HEADER
|
||||
|
||||
void MUse::init(MDefinition* producer, MNode* consumer)
|
||||
|
@ -218,6 +218,14 @@ MacroAssembler::callJit(TrampolinePtr code)
|
||||
return currentOffset();
|
||||
}
|
||||
|
||||
uint32_t
|
||||
MacroAssembler::callJit(ImmPtr callee)
|
||||
{
|
||||
AutoProfilerCallInstrumentation profiler(*this);
|
||||
call(callee);
|
||||
return currentOffset();
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::makeFrameDescriptor(Register frameSizeReg, FrameType type, uint32_t headerSize)
|
||||
{
|
||||
|
@ -597,6 +597,7 @@ class MacroAssembler : public MacroAssemblerSpecific
|
||||
inline uint32_t callJit(Register callee);
|
||||
inline uint32_t callJit(JitCode* code);
|
||||
inline uint32_t callJit(TrampolinePtr code);
|
||||
inline uint32_t callJit(ImmPtr callee);
|
||||
|
||||
// The frame descriptor is the second field of all Jit frames, pushed before
|
||||
// calling the Jit function. It is a composite value defined in JitFrames.h
|
||||
|
@ -254,6 +254,15 @@ class InstructionDataMap
|
||||
}
|
||||
};
|
||||
|
||||
inline void
|
||||
TakeJitRegisters(bool isProfiling, AllocatableRegisterSet* set)
|
||||
{
|
||||
#if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_ARM64)
|
||||
if (isProfiling)
|
||||
set->take(AnyRegister(FramePointer));
|
||||
#endif
|
||||
}
|
||||
|
||||
// Common superclass for register allocators.
|
||||
class RegisterAllocator
|
||||
{
|
||||
@ -280,14 +289,10 @@ class RegisterAllocator
|
||||
graph(graph),
|
||||
allRegisters_(RegisterSet::All())
|
||||
{
|
||||
if (mir->compilingWasm()) {
|
||||
if (mir->compilingWasm())
|
||||
takeWasmRegisters(allRegisters_);
|
||||
} else {
|
||||
#if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_ARM64)
|
||||
if (mir->instrumentedProfiling())
|
||||
allRegisters_.take(AnyRegister(FramePointer));
|
||||
#endif
|
||||
}
|
||||
else
|
||||
TakeJitRegisters(mir->instrumentedProfiling(), &allRegisters_);
|
||||
}
|
||||
|
||||
MOZ_MUST_USE bool init();
|
||||
|
@ -19,10 +19,13 @@ namespace js {
|
||||
namespace jit {
|
||||
|
||||
struct AnyRegister {
|
||||
typedef uint32_t Code;
|
||||
typedef uint8_t Code;
|
||||
|
||||
static const uint32_t Total = Registers::Total + FloatRegisters::Total;
|
||||
static const uint32_t Invalid = UINT_MAX;
|
||||
static const uint8_t Total = Registers::Total + FloatRegisters::Total;
|
||||
static const uint8_t Invalid = UINT8_MAX;
|
||||
|
||||
static_assert(size_t(Registers::Total) + FloatRegisters::Total <= UINT8_MAX,
|
||||
"Number of registers must fit in uint8_t");
|
||||
|
||||
private:
|
||||
Code code_;
|
||||
@ -36,7 +39,7 @@ struct AnyRegister {
|
||||
explicit AnyRegister(FloatRegister fpu) {
|
||||
code_ = fpu.code() + Registers::Total;
|
||||
}
|
||||
static AnyRegister FromCode(uint32_t i) {
|
||||
static AnyRegister FromCode(uint8_t i) {
|
||||
MOZ_ASSERT(i < Total);
|
||||
AnyRegister r;
|
||||
r.code_ = i;
|
||||
@ -72,7 +75,7 @@ struct AnyRegister {
|
||||
bool volatile_() const {
|
||||
return isFloat() ? fpu().volatile_() : gpr().volatile_();
|
||||
}
|
||||
AnyRegister aliased(uint32_t aliasIdx) const {
|
||||
AnyRegister aliased(uint8_t aliasIdx) const {
|
||||
AnyRegister ret;
|
||||
if (isFloat())
|
||||
ret = AnyRegister(fpu().aliased(aliasIdx));
|
||||
@ -81,7 +84,7 @@ struct AnyRegister {
|
||||
MOZ_ASSERT_IF(aliasIdx == 0, ret == *this);
|
||||
return ret;
|
||||
}
|
||||
uint32_t numAliased() const {
|
||||
uint8_t numAliased() const {
|
||||
if (isFloat())
|
||||
return fpu().numAliased();
|
||||
return gpr().numAliased();
|
||||
|
@ -781,7 +781,7 @@ GetIndexFromString(JSString* str)
|
||||
if (!str->isFlat())
|
||||
return -1;
|
||||
|
||||
uint32_t index;
|
||||
uint32_t index = UINT32_MAX;
|
||||
if (!str->asFlat().isIndex(&index) || index > INT32_MAX)
|
||||
return -1;
|
||||
|
||||
|
@ -9835,6 +9835,45 @@ class LGetPrototypeOf : public LInstructionHelper<BOX_PIECES, 1, 0>
|
||||
}
|
||||
};
|
||||
|
||||
template<size_t NumDefs>
|
||||
class LIonToWasmCallBase : public LVariadicInstruction<NumDefs, 2>
|
||||
{
|
||||
using Base = LVariadicInstruction<NumDefs, 2>;
|
||||
public:
|
||||
explicit LIonToWasmCallBase(LNode::Opcode classOpcode, uint32_t numOperands,
|
||||
const LDefinition& temp, const LDefinition& fp)
|
||||
: Base(classOpcode, numOperands)
|
||||
{
|
||||
this->setIsCall();
|
||||
this->setTemp(0, temp);
|
||||
this->setTemp(1, fp);
|
||||
}
|
||||
MIonToWasmCall* mir() const {
|
||||
return this->mir_->toIonToWasmCall();
|
||||
}
|
||||
const LDefinition* temp() {
|
||||
return this->getTemp(0);
|
||||
}
|
||||
};
|
||||
|
||||
class LIonToWasmCall : public LIonToWasmCallBase<1>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(IonToWasmCall);
|
||||
LIonToWasmCall(uint32_t numOperands, const LDefinition& temp, const LDefinition& fp)
|
||||
: LIonToWasmCallBase<1>(classOpcode, numOperands, temp, fp)
|
||||
{}
|
||||
};
|
||||
|
||||
class LIonToWasmCallV : public LIonToWasmCallBase<BOX_PIECES>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(IonToWasmCallV);
|
||||
LIonToWasmCallV(uint32_t numOperands, const LDefinition& temp, const LDefinition& fp)
|
||||
: LIonToWasmCallBase<BOX_PIECES>(classOpcode, numOperands, temp, fp)
|
||||
{}
|
||||
};
|
||||
|
||||
} // namespace jit
|
||||
} // namespace js
|
||||
|
||||
|
@ -36,6 +36,7 @@
|
||||
#include "builtin/String.h"
|
||||
#include "js/Conversions.h"
|
||||
#include "js/Date.h"
|
||||
#include "js/LocaleSensitive.h"
|
||||
#include "js/Wrapper.h"
|
||||
#include "util/StringBuffer.h"
|
||||
#include "util/Text.h"
|
||||
|
@ -614,12 +614,13 @@ JitFrameIter::settle()
|
||||
|
||||
MOZ_ASSERT(wasmFrame.done());
|
||||
uint8_t* prevFP = wasmFrame.unwoundIonCallerFP();
|
||||
jit::FrameType prevFrameType = wasmFrame.unwoundIonFrameType();
|
||||
|
||||
if (mustUnwindActivation_)
|
||||
act_->setJSExitFP(prevFP);
|
||||
|
||||
iter_.destroy();
|
||||
iter_.construct<jit::JSJitFrameIter>(act_, prevFP);
|
||||
iter_.construct<jit::JSJitFrameIter>(act_, prevFrameType, prevFP);
|
||||
MOZ_ASSERT(!asJSJit().done());
|
||||
return;
|
||||
}
|
||||
|
@ -1622,13 +1622,10 @@ class BailoutFrameInfo;
|
||||
// A JitActivation is used for frames running in Baseline or Ion.
|
||||
class JitActivation : public Activation
|
||||
{
|
||||
public:
|
||||
static const uintptr_t ExitFpWasmBit = 0x1;
|
||||
|
||||
private:
|
||||
// If Baseline, Ion or Wasm code is on the stack, and has called into C++,
|
||||
// this will be aligned to an ExitFrame. The last bit indicates if it's a
|
||||
// wasm frame (bit set to ExitFpWasmBit) or not (bit set to !ExitFpWasmBit).
|
||||
// wasm frame (bit set to wasm::ExitOrJitEntryFPTag) or not
|
||||
// (bit set to ~wasm::ExitOrJitEntryFPTag).
|
||||
uint8_t* packedExitFP_;
|
||||
|
||||
// When hasWasmExitFP(), encodedWasmExitReason_ holds ExitReason.
|
||||
@ -1704,14 +1701,14 @@ class JitActivation : public Activation
|
||||
return !!packedExitFP_;
|
||||
}
|
||||
uint8_t* jsOrWasmExitFP() const {
|
||||
return (uint8_t*)(uintptr_t(packedExitFP_) & ~ExitFpWasmBit);
|
||||
return (uint8_t*)(uintptr_t(packedExitFP_) & ~wasm::ExitOrJitEntryFPTag);
|
||||
}
|
||||
static size_t offsetOfPackedExitFP() {
|
||||
return offsetof(JitActivation, packedExitFP_);
|
||||
}
|
||||
|
||||
bool hasJSExitFP() const {
|
||||
return !(uintptr_t(packedExitFP_) & ExitFpWasmBit);
|
||||
return !(uintptr_t(packedExitFP_) & wasm::ExitOrJitEntryFPTag);
|
||||
}
|
||||
uint8_t* jsExitFP() const {
|
||||
MOZ_ASSERT(hasJSExitFP());
|
||||
@ -1802,16 +1799,16 @@ class JitActivation : public Activation
|
||||
|
||||
// WebAssembly specific attributes.
|
||||
bool hasWasmExitFP() const {
|
||||
return uintptr_t(packedExitFP_) & ExitFpWasmBit;
|
||||
return uintptr_t(packedExitFP_) & wasm::ExitOrJitEntryFPTag;
|
||||
}
|
||||
wasm::Frame* wasmExitFP() const {
|
||||
MOZ_ASSERT(hasWasmExitFP());
|
||||
return (wasm::Frame*)(uintptr_t(packedExitFP_) & ~ExitFpWasmBit);
|
||||
return (wasm::Frame*)(uintptr_t(packedExitFP_) & ~wasm::ExitOrJitEntryFPTag);
|
||||
}
|
||||
void setWasmExitFP(const wasm::Frame* fp) {
|
||||
if (fp) {
|
||||
MOZ_ASSERT(!(uintptr_t(fp) & ExitFpWasmBit));
|
||||
packedExitFP_ = (uint8_t*)(uintptr_t(fp) | ExitFpWasmBit);
|
||||
MOZ_ASSERT(!(uintptr_t(fp) & wasm::ExitOrJitEntryFPTag));
|
||||
packedExitFP_ = (uint8_t*)(uintptr_t(fp) | wasm::ExitOrJitEntryFPTag);
|
||||
MOZ_ASSERT(hasWasmExitFP());
|
||||
} else {
|
||||
packedExitFP_ = nullptr;
|
||||
|
@ -40,6 +40,7 @@ WasmFrameIter::WasmFrameIter(JitActivation* activation, wasm::Frame* fp)
|
||||
lineOrBytecode_(0),
|
||||
fp_(fp ? fp : activation->wasmExitFP()),
|
||||
unwoundIonCallerFP_(nullptr),
|
||||
unwoundIonFrameType_(jit::FrameType(-1)),
|
||||
unwind_(Unwind::False),
|
||||
unwoundAddressOfReturnAddress_(nullptr)
|
||||
{
|
||||
@ -112,7 +113,39 @@ WasmFrameIter::popFrame()
|
||||
{
|
||||
Frame* prevFP = fp_;
|
||||
fp_ = prevFP->callerFP;
|
||||
MOZ_ASSERT(!(uintptr_t(fp_) & JitActivation::ExitFpWasmBit));
|
||||
|
||||
if (uintptr_t(fp_) & ExitOrJitEntryFPTag) {
|
||||
// We just unwound a frame pointer which has the low bit set,
|
||||
// indicating this is a direct call from the jit into the wasm
|
||||
// function's body. The call stack resembles this at this point:
|
||||
//
|
||||
// |---------------------|
|
||||
// | JIT FRAME |
|
||||
// | JIT FAKE EXIT FRAME | <-- tagged fp_
|
||||
// | WASM FRAME | <-- prevFP (already unwound)
|
||||
// |---------------------|
|
||||
//
|
||||
// fp_ points to the fake exit frame set up by the jit caller, and the
|
||||
// return-address-to-fp is in JIT code, thus doesn't belong to any wasm
|
||||
// instance's code (in particular, there's no associated CodeRange).
|
||||
// Mark the frame as such and untag FP.
|
||||
MOZ_ASSERT(!LookupCode(prevFP->returnAddress));
|
||||
|
||||
unwoundIonCallerFP_ = (uint8_t*)(uintptr_t(fp_) & ~uintptr_t(ExitOrJitEntryFPTag));
|
||||
unwoundIonFrameType_ = JitFrame_Exit;
|
||||
|
||||
fp_ = nullptr;
|
||||
code_ = nullptr;
|
||||
codeRange_ = nullptr;
|
||||
|
||||
if (unwind_ == Unwind::True) {
|
||||
activation_->setJSExitFP(unwoundIonCallerFP_);
|
||||
unwoundAddressOfReturnAddress_ = &prevFP->returnAddress;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(done());
|
||||
return;
|
||||
}
|
||||
|
||||
if (!fp_) {
|
||||
code_ = nullptr;
|
||||
@ -135,7 +168,20 @@ WasmFrameIter::popFrame()
|
||||
MOZ_ASSERT(codeRange_);
|
||||
|
||||
if (codeRange_->isJitEntry()) {
|
||||
// This wasm function has been called through the generic JIT entry by
|
||||
// a JIT caller, so the call stack resembles this:
|
||||
//
|
||||
// |---------------------|
|
||||
// | JIT FRAME |
|
||||
// | JSJIT TO WASM EXIT | <-- fp_
|
||||
// | WASM JIT ENTRY | <-- prevFP (already unwound)
|
||||
// | WASM FRAME | (already unwound)
|
||||
// |---------------------|
|
||||
//
|
||||
// The next value of FP is just a regular jit frame used marked to
|
||||
// know that we should transition to a JSJit frame iterator.
|
||||
unwoundIonCallerFP_ = (uint8_t*) fp_;
|
||||
unwoundIonFrameType_ = JitFrame_JSJitToWasm;
|
||||
|
||||
fp_ = nullptr;
|
||||
code_ = nullptr;
|
||||
@ -275,6 +321,14 @@ WasmFrameIter::debugFrame() const
|
||||
return DebugFrame::from(fp_);
|
||||
}
|
||||
|
||||
jit::FrameType
|
||||
WasmFrameIter::unwoundIonFrameType() const
|
||||
{
|
||||
MOZ_ASSERT(unwoundIonCallerFP_);
|
||||
MOZ_ASSERT(unwoundIonFrameType_ != jit::FrameType(-1));
|
||||
return unwoundIonFrameType_;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
// Prologue/epilogue code generation
|
||||
|
||||
@ -358,9 +412,9 @@ wasm::SetExitFP(MacroAssembler& masm, ExitReason reason, Register scratch)
|
||||
masm.store32(Imm32(reason.encode()),
|
||||
Address(scratch, JitActivation::offsetOfEncodedWasmExitReason()));
|
||||
|
||||
masm.orPtr(Imm32(JitActivation::ExitFpWasmBit), FramePointer);
|
||||
masm.orPtr(Imm32(ExitOrJitEntryFPTag), FramePointer);
|
||||
masm.storePtr(FramePointer, Address(scratch, JitActivation::offsetOfPackedExitFP()));
|
||||
masm.andPtr(Imm32(int32_t(~JitActivation::ExitFpWasmBit)), FramePointer);
|
||||
masm.andPtr(Imm32(int32_t(~ExitOrJitEntryFPTag)), FramePointer);
|
||||
}
|
||||
|
||||
void
|
||||
@ -629,7 +683,7 @@ AssertNoWasmExitFPInJitExit(MacroAssembler& masm)
|
||||
Label ok;
|
||||
masm.branchTestPtr(Assembler::Zero,
|
||||
Address(scratch, JitActivation::offsetOfPackedExitFP()),
|
||||
Imm32(uintptr_t(JitActivation::ExitFpWasmBit)),
|
||||
Imm32(uintptr_t(ExitOrJitEntryFPTag)),
|
||||
&ok);
|
||||
masm.breakpoint();
|
||||
masm.bind(&ok);
|
||||
@ -734,6 +788,18 @@ ProfilingFrameIterator::ProfilingFrameIterator(const JitActivation& activation,
|
||||
initFromExitFP(fp);
|
||||
}
|
||||
|
||||
static inline void
|
||||
AssertDirectJitCall(const void* fp)
|
||||
{
|
||||
// Called via an inlined fast JIT to wasm call: in this case, FP is
|
||||
// pointing in the middle of the exit frame, right before the exit
|
||||
// footer; ensure the exit frame type is the expected one.
|
||||
#ifdef DEBUG
|
||||
auto* jitCaller = (ExitFrameLayout*)(uintptr_t(fp) & ~ExitOrJitEntryFPTag);
|
||||
MOZ_ASSERT(jitCaller->footer()->type() == jit::ExitFrameType::DirectWasmJitCall);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void
|
||||
AssertMatchesCallSite(void* callerPC, Frame* callerFP)
|
||||
{
|
||||
@ -741,7 +807,11 @@ AssertMatchesCallSite(void* callerPC, Frame* callerFP)
|
||||
const CodeRange* callerCodeRange;
|
||||
const Code* code = LookupCode(callerPC, &callerCodeRange);
|
||||
|
||||
MOZ_ASSERT(code);
|
||||
if (!code) {
|
||||
AssertDirectJitCall(callerFP);
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(callerCodeRange);
|
||||
|
||||
if (callerCodeRange->isInterpEntry()) {
|
||||
@ -768,7 +838,18 @@ ProfilingFrameIterator::initFromExitFP(const Frame* fp)
|
||||
void* pc = fp->returnAddress;
|
||||
|
||||
code_ = LookupCode(pc, &codeRange_);
|
||||
MOZ_ASSERT(code_);
|
||||
|
||||
if (!code_) {
|
||||
// This is a direct call from the JIT, the caller FP is pointing to a
|
||||
// tagged JIT caller's frame.
|
||||
MOZ_ASSERT(uintptr_t(fp->callerFP) & ExitOrJitEntryFPTag);
|
||||
AssertDirectJitCall(fp->callerFP);
|
||||
|
||||
unwoundIonCallerFP_ = (uint8_t*)(uintptr_t(fp->callerFP) & ~ExitOrJitEntryFPTag);
|
||||
MOZ_ASSERT(done());
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(codeRange_);
|
||||
|
||||
// Since we don't have the pc for fp, start unwinding at the caller of fp.
|
||||
@ -808,6 +889,15 @@ ProfilingFrameIterator::initFromExitFP(const Frame* fp)
|
||||
MOZ_ASSERT(!done());
|
||||
}
|
||||
|
||||
static void
|
||||
AssertCallerFP(DebugOnly<bool> fpWasTagged, Frame* const fp, void** const sp)
|
||||
{
|
||||
MOZ_ASSERT_IF(!fpWasTagged.value,
|
||||
fp == reinterpret_cast<Frame*>(sp)->callerFP);
|
||||
MOZ_ASSERT_IF(fpWasTagged.value,
|
||||
(Frame*)(uintptr_t(fp) | 0x1) == reinterpret_cast<Frame*>(sp)->callerFP);
|
||||
}
|
||||
|
||||
bool
|
||||
js::wasm::StartUnwinding(const RegisterState& registers, UnwindState* unwindState,
|
||||
bool* unwoundCaller)
|
||||
@ -816,9 +906,12 @@ js::wasm::StartUnwinding(const RegisterState& registers, UnwindState* unwindStat
|
||||
uint8_t* const pc = (uint8_t*) registers.pc;
|
||||
void** const sp = (void**) registers.sp;
|
||||
|
||||
// The frame pointer might be in the process of tagging/untagging; make
|
||||
// sure it's untagged.
|
||||
Frame* const fp = (Frame*) (intptr_t(registers.fp) & ~JitActivation::ExitFpWasmBit);
|
||||
// The frame pointer might be:
|
||||
// - in the process of tagging/untagging when calling into the JITs;
|
||||
// make sure it's untagged.
|
||||
// - tagged by an direct JIT call.
|
||||
DebugOnly<bool> fpWasTagged = uintptr_t(registers.fp) & ExitOrJitEntryFPTag;
|
||||
Frame* const fp = (Frame*) (intptr_t(registers.fp) & ~ExitOrJitEntryFPTag);
|
||||
|
||||
// Get the CodeRange describing pc and the base address to which the
|
||||
// CodeRange is relative. If the pc is not in a wasm module or a builtin
|
||||
@ -927,7 +1020,7 @@ js::wasm::StartUnwinding(const RegisterState& registers, UnwindState* unwindStat
|
||||
AssertMatchesCallSite(fixedPC, fixedFP);
|
||||
} else if (offsetFromEntry == PushedFP) {
|
||||
// The full Frame has been pushed; fp is still the caller's fp.
|
||||
MOZ_ASSERT(fp == reinterpret_cast<Frame*>(sp)->callerFP);
|
||||
AssertCallerFP(fpWasTagged, fp, sp);
|
||||
fixedPC = reinterpret_cast<Frame*>(sp)->returnAddress;
|
||||
fixedFP = fp;
|
||||
AssertMatchesCallSite(fixedPC, fixedFP);
|
||||
@ -939,7 +1032,7 @@ js::wasm::StartUnwinding(const RegisterState& registers, UnwindState* unwindStat
|
||||
// The fixedFP field of the Frame has been loaded into fp.
|
||||
// The ra and TLS might also be loaded, but the Frame structure is
|
||||
// still on stack, so we can acess the ra form there.
|
||||
MOZ_ASSERT(fp == reinterpret_cast<Frame*>(sp)->callerFP);
|
||||
AssertCallerFP(fpWasTagged, fp, sp);
|
||||
fixedPC = reinterpret_cast<Frame*>(sp)->returnAddress;
|
||||
fixedFP = fp;
|
||||
AssertMatchesCallSite(fixedPC, fixedFP);
|
||||
@ -1017,7 +1110,7 @@ js::wasm::StartUnwinding(const RegisterState& registers, UnwindState* unwindStat
|
||||
fixedPC = nullptr;
|
||||
|
||||
// On the error return path, FP might be set to FailFP. Ignore these transient frames.
|
||||
if (intptr_t(fixedFP) == (FailFP & ~JitActivation::ExitFpWasmBit))
|
||||
if (intptr_t(fixedFP) == (FailFP & ~ExitOrJitEntryFPTag))
|
||||
return false;
|
||||
break;
|
||||
case CodeRange::Throw:
|
||||
@ -1063,13 +1156,24 @@ ProfilingFrameIterator::ProfilingFrameIterator(const JitActivation& activation,
|
||||
if (unwoundCaller) {
|
||||
callerFP_ = unwindState.fp;
|
||||
callerPC_ = unwindState.pc;
|
||||
// If the original FP value is tagged, then we're being called through
|
||||
// a direct JIT call. We can't observe transient tagged values of FP
|
||||
// (during wasm::SetExitFP) here because StartUnwinding would not have
|
||||
// unwound for us in this case.
|
||||
if ((uintptr_t(state.fp) & ExitOrJitEntryFPTag))
|
||||
unwoundIonCallerFP_ = (uint8_t*) callerFP_;
|
||||
} else {
|
||||
callerFP_ = unwindState.fp->callerFP;
|
||||
callerPC_ = unwindState.fp->returnAddress;
|
||||
// See comment above.
|
||||
if ((uintptr_t(callerFP_) & ExitOrJitEntryFPTag))
|
||||
unwoundIonCallerFP_ = (uint8_t*)(uintptr_t(callerFP_) & ~ExitOrJitEntryFPTag);
|
||||
}
|
||||
|
||||
if (unwindState.codeRange->isJitEntry())
|
||||
if (unwindState.codeRange->isJitEntry()) {
|
||||
MOZ_ASSERT(!unwoundIonCallerFP_);
|
||||
unwoundIonCallerFP_ = (uint8_t*) callerFP_;
|
||||
}
|
||||
|
||||
if (unwindState.codeRange->isInterpEntry()) {
|
||||
unwindState.codeRange = nullptr;
|
||||
@ -1086,15 +1190,15 @@ void
|
||||
ProfilingFrameIterator::operator++()
|
||||
{
|
||||
if (!exitReason_.isNone()) {
|
||||
DebugOnly<ExitReason> prevExitReason = exitReason_;
|
||||
DebugOnly<bool> wasInterpEntry = exitReason_.isInterpEntry();
|
||||
exitReason_ = ExitReason::None();
|
||||
MOZ_ASSERT(!codeRange_ == prevExitReason.value.isInterpEntry());
|
||||
MOZ_ASSERT(done() == prevExitReason.value.isInterpEntry());
|
||||
MOZ_ASSERT((!codeRange_) == wasInterpEntry);
|
||||
MOZ_ASSERT(done() == wasInterpEntry);
|
||||
return;
|
||||
}
|
||||
|
||||
if (unwoundIonCallerFP_) {
|
||||
MOZ_ASSERT(codeRange_->isJitEntry());
|
||||
MOZ_ASSERT(codeRange_->isFunction() || codeRange_->isJitEntry());
|
||||
callerPC_ = nullptr;
|
||||
callerFP_ = nullptr;
|
||||
codeRange_ = nullptr;
|
||||
@ -1120,6 +1224,17 @@ ProfilingFrameIterator::operator++()
|
||||
}
|
||||
|
||||
code_ = LookupCode(callerPC_, &codeRange_);
|
||||
|
||||
if (!code_ && uintptr_t(callerFP_) & ExitOrJitEntryFPTag) {
|
||||
// The parent frame is an inlined wasm call, the tagged FP points to
|
||||
// the fake exit frame.
|
||||
MOZ_ASSERT(!codeRange_);
|
||||
AssertDirectJitCall(callerFP_);
|
||||
unwoundIonCallerFP_ = (uint8_t*) (uintptr_t(callerFP_) & ~uintptr_t(ExitOrJitEntryFPTag));
|
||||
MOZ_ASSERT(done());
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(codeRange_);
|
||||
|
||||
if (codeRange_->isJitEntry()) {
|
||||
|
@ -19,6 +19,7 @@
|
||||
#ifndef wasm_frame_iter_h
|
||||
#define wasm_frame_iter_h
|
||||
|
||||
#include "jit/JSJitFrameIter.h"
|
||||
#include "js/ProfilingFrameIterator.h"
|
||||
#include "js/TypeDecls.h"
|
||||
#include "wasm/WasmTypes.h"
|
||||
@ -64,6 +65,7 @@ class WasmFrameIter
|
||||
unsigned lineOrBytecode_;
|
||||
Frame* fp_;
|
||||
uint8_t* unwoundIonCallerFP_;
|
||||
jit::FrameType unwoundIonFrameType_;
|
||||
Unwind unwind_;
|
||||
void** unwoundAddressOfReturnAddress_;
|
||||
|
||||
@ -88,6 +90,7 @@ class WasmFrameIter
|
||||
void** unwoundAddressOfReturnAddress() const;
|
||||
bool debugEnabled() const;
|
||||
DebugFrame* debugFrame() const;
|
||||
jit::FrameType unwoundIonFrameType() const;
|
||||
uint8_t* unwoundIonCallerFP() const { return unwoundIonCallerFP_; }
|
||||
};
|
||||
|
||||
@ -264,6 +267,19 @@ bool
|
||||
StartUnwinding(const RegisterState& registers, UnwindState* unwindState,
|
||||
bool* unwoundCaller);
|
||||
|
||||
// Bit set as the lowest bit of a frame pointer, used in two different mutually
|
||||
// exclusive situations:
|
||||
// - either it's a low bit tag in a FramePointer value read from the
|
||||
// Frame::callerFP of an inner wasm frame. This indicates the previous call
|
||||
// frame has been set up by a JIT caller that directly called into a wasm
|
||||
// function's body. This is only stored in Frame::callerFP for a wasm frame
|
||||
// called from JIT code, and thus it can not appear in a JitActivation's
|
||||
// exitFP.
|
||||
// - or it's the low big tag set when exiting wasm code in JitActivation's
|
||||
// exitFP.
|
||||
|
||||
constexpr uintptr_t ExitOrJitEntryFPTag = 0x1;
|
||||
|
||||
} // namespace wasm
|
||||
} // namespace js
|
||||
|
||||
|
@ -20,6 +20,7 @@
|
||||
|
||||
#include "mozilla/ArrayUtils.h"
|
||||
|
||||
#include "jit/RegisterAllocator.h"
|
||||
#include "wasm/WasmCode.h"
|
||||
#include "wasm/WasmGenerator.h"
|
||||
#include "wasm/WasmInstance.h"
|
||||
@ -501,7 +502,8 @@ GenerateJitEntryThrow(MacroAssembler& masm, unsigned frameSize)
|
||||
MoveSPForJitABI(masm);
|
||||
|
||||
masm.loadPtr(Address(WasmTlsReg, offsetof(TlsData, cx)), ScratchIonEntry);
|
||||
masm.enterFakeExitFrameForWasm(ScratchIonEntry, ScratchIonEntry, ExitFrameType::WasmJitEntry);
|
||||
masm.enterFakeExitFrameForWasm(ScratchIonEntry, ScratchIonEntry,
|
||||
ExitFrameType::WasmGenericJitEntry);
|
||||
|
||||
masm.loadPtr(Address(WasmTlsReg, offsetof(TlsData, instance)), ScratchIonEntry);
|
||||
masm.loadPtr(Address(ScratchIonEntry, Instance::offsetOfJSJitExceptionHandler()),
|
||||
@ -844,6 +846,164 @@ GenerateJitEntry(MacroAssembler& masm, size_t funcExportIndex, const FuncExport&
|
||||
return FinishOffsets(masm, offsets);
|
||||
}
|
||||
|
||||
void
|
||||
wasm::GenerateDirectCallFromJit(MacroAssembler& masm,
|
||||
const FuncExport& fe,
|
||||
const Instance& inst,
|
||||
const JitCallStackArgVector& stackArgs,
|
||||
bool profilingEnabled,
|
||||
bool wasmGcEnabled,
|
||||
Register scratch,
|
||||
uint32_t* callOffset)
|
||||
{
|
||||
MOZ_ASSERT(!IsCompilingWasm());
|
||||
|
||||
size_t framePushedAtStart = masm.framePushed();
|
||||
|
||||
if (profilingEnabled) {
|
||||
// FramePointer isn't volatile, manually preserve it because it will be
|
||||
// clobbered below.
|
||||
masm.Push(FramePointer);
|
||||
} else {
|
||||
#ifdef DEBUG
|
||||
// Ensure that the FramePointer is actually Ion-volatile. This might
|
||||
// assert when bug 1426134 lands.
|
||||
AllocatableRegisterSet set(RegisterSet::All());
|
||||
TakeJitRegisters(/* profiling */ false, &set);
|
||||
MOZ_ASSERT(set.has(FramePointer),
|
||||
"replace the whole if branch by the then body when this fails");
|
||||
#endif
|
||||
}
|
||||
|
||||
// Push a special frame descriptor that indicates the frame size so we can
|
||||
// directly iterate from the current JIT frame without an extra call.
|
||||
*callOffset = masm.buildFakeExitFrame(scratch);
|
||||
masm.loadJSContext(scratch);
|
||||
|
||||
masm.moveStackPtrTo(FramePointer);
|
||||
masm.enterFakeExitFrame(scratch, scratch, ExitFrameType::DirectWasmJitCall);
|
||||
masm.orPtr(Imm32(ExitOrJitEntryFPTag), FramePointer);
|
||||
|
||||
// Move stack arguments to their final locations.
|
||||
unsigned bytesNeeded = StackArgBytes(fe.funcType().args());
|
||||
bytesNeeded = StackDecrementForCall(WasmStackAlignment, masm.framePushed(), bytesNeeded);
|
||||
if (bytesNeeded)
|
||||
masm.reserveStack(bytesNeeded);
|
||||
|
||||
for (ABIArgValTypeIter iter(fe.funcType().args()); !iter.done(); iter++) {
|
||||
MOZ_ASSERT_IF(iter->kind() == ABIArg::GPR, iter->gpr() != scratch);
|
||||
MOZ_ASSERT_IF(iter->kind() == ABIArg::GPR, iter->gpr() != FramePointer);
|
||||
if (iter->kind() != ABIArg::Stack)
|
||||
continue;
|
||||
|
||||
Address dst(masm.getStackPointer(), iter->offsetFromArgBase());
|
||||
|
||||
const JitCallStackArg& stackArg = stackArgs[iter.index()];
|
||||
switch (stackArg.tag()) {
|
||||
case JitCallStackArg::Tag::Imm32:
|
||||
masm.storePtr(ImmWord(stackArg.imm32()), dst);
|
||||
break;
|
||||
case JitCallStackArg::Tag::GPR:
|
||||
MOZ_ASSERT(stackArg.gpr() != scratch);
|
||||
MOZ_ASSERT(stackArg.gpr() != FramePointer);
|
||||
masm.storePtr(stackArg.gpr(), dst);
|
||||
break;
|
||||
case JitCallStackArg::Tag::FPU:
|
||||
switch (iter.mirType()) {
|
||||
case MIRType::Double:
|
||||
masm.storeDouble(stackArg.fpu(), dst);
|
||||
break;
|
||||
case MIRType::Float32:
|
||||
masm.storeFloat32(stackArg.fpu(), dst);
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("unexpected MIR type for a float register in wasm fast call");
|
||||
}
|
||||
break;
|
||||
case JitCallStackArg::Tag::Address: {
|
||||
// The address offsets were valid *before* we pushed our frame.
|
||||
Address src = stackArg.addr();
|
||||
src.offset += masm.framePushed() - framePushedAtStart;
|
||||
switch (iter.mirType()) {
|
||||
case MIRType::Double:
|
||||
masm.loadDouble(src, ScratchDoubleReg);
|
||||
masm.storeDouble(ScratchDoubleReg, dst);
|
||||
break;
|
||||
case MIRType::Float32:
|
||||
masm.loadFloat32(src, ScratchFloat32Reg);
|
||||
masm.storeFloat32(ScratchFloat32Reg, dst);
|
||||
break;
|
||||
case MIRType::Int32:
|
||||
masm.loadPtr(src, scratch);
|
||||
masm.storePtr(scratch, dst);
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("unexpected MIR type for a stack slot in wasm fast call");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case JitCallStackArg::Tag::Undefined: {
|
||||
MOZ_CRASH("can't happen because of arg.kind() check");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Load tls; from now on, WasmTlsReg is live.
|
||||
masm.movePtr(ImmPtr(inst.tlsData()), WasmTlsReg);
|
||||
masm.loadWasmPinnedRegsFromTls();
|
||||
|
||||
#ifdef ENABLE_WASM_GC
|
||||
if (wasmGcEnabled)
|
||||
SuppressGC(masm, 1, ABINonArgReg0);
|
||||
#endif
|
||||
|
||||
// Actual call.
|
||||
const wasm::CodeTier& codeTier = inst.code().codeTier(inst.code().bestTier());
|
||||
uint32_t offset = codeTier.metadata().codeRanges[fe.funcCodeRangeIndex()].funcNormalEntry();
|
||||
void* callee = codeTier.segment().base() + offset;
|
||||
|
||||
masm.assertStackAlignment(WasmStackAlignment);
|
||||
masm.callJit(ImmPtr(callee));
|
||||
masm.assertStackAlignment(WasmStackAlignment);
|
||||
|
||||
#ifdef ENABLE_WASM_GC
|
||||
if (wasmGcEnabled)
|
||||
SuppressGC(masm, -1, WasmTlsReg);
|
||||
#endif
|
||||
|
||||
masm.branchPtr(Assembler::Equal, FramePointer, Imm32(wasm::FailFP), masm.exceptionLabel());
|
||||
|
||||
// Store the return value in the appropriate place.
|
||||
switch (fe.funcType().ret().code()) {
|
||||
case wasm::ExprType::Void:
|
||||
masm.moveValue(UndefinedValue(), JSReturnOperand);
|
||||
break;
|
||||
case wasm::ExprType::I32:
|
||||
break;
|
||||
case wasm::ExprType::F32:
|
||||
masm.canonicalizeFloat(ReturnFloat32Reg);
|
||||
break;
|
||||
case wasm::ExprType::F64:
|
||||
masm.canonicalizeDouble(ReturnDoubleReg);
|
||||
break;
|
||||
case wasm::ExprType::Ref:
|
||||
case wasm::ExprType::AnyRef:
|
||||
case wasm::ExprType::I64:
|
||||
MOZ_CRASH("unexpected return type when calling from ion to wasm");
|
||||
case wasm::ExprType::Limit:
|
||||
MOZ_CRASH("Limit");
|
||||
}
|
||||
|
||||
// Free args + frame descriptor.
|
||||
masm.leaveExitFrame(bytesNeeded + ExitFrameLayout::Size());
|
||||
|
||||
// If we pushed it, free FramePointer.
|
||||
if (profilingEnabled)
|
||||
masm.Pop(FramePointer);
|
||||
|
||||
MOZ_ASSERT(framePushedAtStart == masm.framePushed());
|
||||
}
|
||||
|
||||
static void
|
||||
StackCopy(MacroAssembler& masm, MIRType type, Register scratch, Address src, Address dst)
|
||||
{
|
||||
|
@ -41,6 +41,87 @@ GenerateEntryStubs(jit::MacroAssembler& masm, size_t funcExportIndex,
|
||||
const FuncExport& funcExport, const Maybe<jit::ImmPtr>& callee,
|
||||
bool isAsmJS, HasGcTypes gcTypesEnabled, CodeRangeVector* codeRanges);
|
||||
|
||||
// An argument that will end up on the stack according to the system ABI, to be
|
||||
// passed to GenerateDirectCallFromJit. Since the direct JIT call creates its
|
||||
// own frame, it is its responsibility to put stack arguments to their expected
|
||||
// locations; so the caller of GenerateDirectCallFromJit can put them anywhere.
|
||||
|
||||
class JitCallStackArg
|
||||
{
|
||||
public:
|
||||
enum class Tag {
|
||||
Imm32,
|
||||
GPR,
|
||||
FPU,
|
||||
Address,
|
||||
Undefined,
|
||||
};
|
||||
|
||||
private:
|
||||
Tag tag_;
|
||||
union U {
|
||||
int32_t imm32_;
|
||||
jit::Register gpr_;
|
||||
jit::FloatRegister fpu_;
|
||||
jit::Address addr_;
|
||||
U() {}
|
||||
} arg;
|
||||
|
||||
public:
|
||||
JitCallStackArg()
|
||||
: tag_(Tag::Undefined)
|
||||
{}
|
||||
explicit JitCallStackArg(int32_t imm32)
|
||||
: tag_(Tag::Imm32)
|
||||
{
|
||||
arg.imm32_ = imm32;
|
||||
}
|
||||
explicit JitCallStackArg(jit::Register gpr)
|
||||
: tag_(Tag::GPR)
|
||||
{
|
||||
arg.gpr_ = gpr;
|
||||
}
|
||||
explicit JitCallStackArg(jit::FloatRegister fpu)
|
||||
: tag_(Tag::FPU)
|
||||
{
|
||||
new (&arg) jit::FloatRegister(fpu);
|
||||
}
|
||||
explicit JitCallStackArg(const jit::Address& addr)
|
||||
: tag_(Tag::Address)
|
||||
{
|
||||
new (&arg) jit::Address(addr);
|
||||
}
|
||||
|
||||
Tag tag() const { return tag_; }
|
||||
int32_t imm32() const { MOZ_ASSERT(tag_ == Tag::Imm32); return arg.imm32_; }
|
||||
jit::Register gpr() const { MOZ_ASSERT(tag_ == Tag::GPR); return arg.gpr_; }
|
||||
jit::FloatRegister fpu() const { MOZ_ASSERT(tag_ == Tag::FPU); return arg.fpu_; }
|
||||
const jit::Address& addr() const { MOZ_ASSERT(tag_ == Tag::Address); return arg.addr_; }
|
||||
};
|
||||
|
||||
using JitCallStackArgVector = Vector<JitCallStackArg, 4, SystemAllocPolicy>;
|
||||
|
||||
// Generates an inline wasm call (during jit compilation) to a specific wasm
|
||||
// function (as specifed by the given FuncExport).
|
||||
// This call doesn't go through a wasm entry, but rather creates its own
|
||||
// inlined exit frame.
|
||||
// Assumes:
|
||||
// - all the registers have been preserved by the caller,
|
||||
// - all arguments passed in registers have been set up at the expected
|
||||
// locations,
|
||||
// - all arguments passed on stack slot are alive as defined by a corresponding
|
||||
// JitCallStackArg.
|
||||
|
||||
extern void
|
||||
GenerateDirectCallFromJit(jit::MacroAssembler& masm,
|
||||
const FuncExport& fe,
|
||||
const Instance& inst,
|
||||
const JitCallStackArgVector& stackArgs,
|
||||
bool profilingEnabled,
|
||||
bool wasmGcEnabled,
|
||||
jit::Register scratch,
|
||||
uint32_t* callOffset);
|
||||
|
||||
} // namespace wasm
|
||||
} // namespace js
|
||||
|
||||
|
@ -94,9 +94,7 @@ using mozilla::dom::PushNotifier;
|
||||
using mozilla::dom::AudioChannelAgent;
|
||||
|
||||
// Editor stuff
|
||||
#include "nsEditorCID.h"
|
||||
#include "mozilla/EditorController.h" //CID
|
||||
#include "mozilla/HTMLEditor.h"
|
||||
|
||||
#include "nsScriptSecurityManager.h"
|
||||
#include "ExpandedPrincipal.h"
|
||||
@ -179,12 +177,8 @@ using mozilla::gmp::GeckoMediaPluginService;
|
||||
{ 0x1f15dbc8, 0xbfaa, 0x45de, \
|
||||
{ 0x8a, 0x46, 0x08, 0xe2, 0xe2, 0x63, 0x26, 0xb0 } }
|
||||
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR(TextEditor)
|
||||
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR(nsParserUtils)
|
||||
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR(HTMLEditor)
|
||||
|
||||
// PresentationDeviceManager
|
||||
/* e1e79dec-4085-4994-ac5b-744b016697e6 */
|
||||
#define PRESENTATION_DEVICE_MANAGER_CID \
|
||||
@ -533,7 +527,6 @@ NS_DEFINE_NAMED_CID(NS_HOSTOBJECTURIMUTATOR_CID);
|
||||
NS_DEFINE_NAMED_CID(NS_SDBCONNECTION_CID);
|
||||
NS_DEFINE_NAMED_CID(NS_DOMSESSIONSTORAGEMANAGER_CID);
|
||||
NS_DEFINE_NAMED_CID(NS_DOMLOCALSTORAGEMANAGER_CID);
|
||||
NS_DEFINE_NAMED_CID(NS_TEXTEDITOR_CID);
|
||||
NS_DEFINE_NAMED_CID(DOMREQUEST_SERVICE_CID);
|
||||
NS_DEFINE_NAMED_CID(QUOTAMANAGER_SERVICE_CID);
|
||||
NS_DEFINE_NAMED_CID(SERVICEWORKERMANAGER_CID);
|
||||
@ -542,7 +535,6 @@ NS_DEFINE_NAMED_CID(NOTIFICATIONTELEMETRYSERVICE_CID);
|
||||
NS_DEFINE_NAMED_CID(PUSHNOTIFIER_CID);
|
||||
NS_DEFINE_NAMED_CID(WORKERDEBUGGERMANAGER_CID);
|
||||
NS_DEFINE_NAMED_CID(NS_AUDIOCHANNELAGENT_CID);
|
||||
NS_DEFINE_NAMED_CID(NS_HTMLEDITOR_CID);
|
||||
NS_DEFINE_NAMED_CID(NS_EDITORCONTROLLER_CID);
|
||||
NS_DEFINE_NAMED_CID(NS_EDITINGCONTROLLER_CID);
|
||||
NS_DEFINE_NAMED_CID(NS_EDITORCOMMANDTABLE_CID);
|
||||
@ -770,7 +762,6 @@ static const mozilla::Module::CIDEntry kLayoutCIDs[] = {
|
||||
{ &kNS_SDBCONNECTION_CID, false, nullptr, SDBConnection::Create },
|
||||
{ &kNS_DOMSESSIONSTORAGEMANAGER_CID, false, nullptr, SessionStorageManagerConstructor },
|
||||
{ &kNS_DOMLOCALSTORAGEMANAGER_CID, false, nullptr, LocalStorageManagerConstructor },
|
||||
{ &kNS_TEXTEDITOR_CID, false, nullptr, TextEditorConstructor },
|
||||
{ &kDOMREQUEST_SERVICE_CID, false, nullptr, DOMRequestServiceConstructor },
|
||||
{ &kQUOTAMANAGER_SERVICE_CID, false, nullptr, QuotaManagerServiceConstructor },
|
||||
{ &kSERVICEWORKERMANAGER_CID, false, nullptr, ServiceWorkerManagerConstructor },
|
||||
@ -779,7 +770,6 @@ static const mozilla::Module::CIDEntry kLayoutCIDs[] = {
|
||||
{ &kPUSHNOTIFIER_CID, false, nullptr, PushNotifierConstructor },
|
||||
{ &kWORKERDEBUGGERMANAGER_CID, true, nullptr, WorkerDebuggerManagerConstructor },
|
||||
{ &kNS_AUDIOCHANNELAGENT_CID, true, nullptr, AudioChannelAgentConstructor },
|
||||
{ &kNS_HTMLEDITOR_CID, false, nullptr, HTMLEditorConstructor },
|
||||
{ &kNS_EDITORCONTROLLER_CID, false, nullptr, EditorControllerConstructor },
|
||||
{ &kNS_EDITINGCONTROLLER_CID, false, nullptr, nsEditingControllerConstructor },
|
||||
{ &kNS_EDITORCOMMANDTABLE_CID, false, nullptr, nsEditorCommandTableConstructor },
|
||||
@ -872,7 +862,6 @@ static const mozilla::Module::ContractIDEntry kLayoutContracts[] = {
|
||||
// Keeping the old ContractID for backward compatibility
|
||||
{ "@mozilla.org/dom/storagemanager;1", &kNS_DOMLOCALSTORAGEMANAGER_CID },
|
||||
{ "@mozilla.org/dom/sessionStorage-manager;1", &kNS_DOMSESSIONSTORAGEMANAGER_CID },
|
||||
{ "@mozilla.org/editor/texteditor;1", &kNS_TEXTEDITOR_CID },
|
||||
{ DOMREQUEST_SERVICE_CONTRACTID, &kDOMREQUEST_SERVICE_CID },
|
||||
{ QUOTAMANAGER_SERVICE_CONTRACTID, &kQUOTAMANAGER_SERVICE_CID },
|
||||
{ SERVICEWORKERMANAGER_CONTRACTID, &kSERVICEWORKERMANAGER_CID },
|
||||
@ -881,7 +870,6 @@ static const mozilla::Module::ContractIDEntry kLayoutContracts[] = {
|
||||
{ PUSHNOTIFIER_CONTRACTID, &kPUSHNOTIFIER_CID },
|
||||
{ WORKERDEBUGGERMANAGER_CONTRACTID, &kWORKERDEBUGGERMANAGER_CID },
|
||||
{ NS_AUDIOCHANNELAGENT_CONTRACTID, &kNS_AUDIOCHANNELAGENT_CID },
|
||||
{ "@mozilla.org/editor/htmleditor;1", &kNS_HTMLEDITOR_CID },
|
||||
{ "@mozilla.org/editor/editorcontroller;1", &kNS_EDITORCONTROLLER_CID },
|
||||
{ "@mozilla.org/editor/editingcontroller;1", &kNS_EDITINGCONTROLLER_CID },
|
||||
{ "@mozilla.org/geolocation;1", &kNS_GEOLOCATION_CID },
|
||||
|
@ -2713,8 +2713,8 @@ ComputeClipForMaskItem(nsDisplayListBuilder* aBuilder, nsIFrame* aMaskedFrame,
|
||||
aBuilder->FindReferenceFrameFor(aMaskedFrame, &toReferenceFrame);
|
||||
|
||||
Maybe<gfxRect> combinedClip;
|
||||
if (maskUsage.shouldApplyBasicShape) {
|
||||
Rect result = nsCSSClipPathInstance::GetBoundingRectForBasicShapeClip(
|
||||
if (maskUsage.shouldApplyBasicShapeOrPath) {
|
||||
Rect result = nsCSSClipPathInstance::GetBoundingRectForBasicShapeOrPathClip(
|
||||
aMaskedFrame, svgReset->mClipPath);
|
||||
combinedClip = Some(ThebesRect(result));
|
||||
} else if (maskUsage.shouldApplyClipPath) {
|
||||
|
@ -190,7 +190,7 @@ function do_test() {
|
||||
|
||||
// Regression test for bug 1255379.
|
||||
var expected = [ "inherit", "initial", "unset", "none", "url",
|
||||
"polygon", "circle", "ellipse", "inset",
|
||||
"polygon", "circle", "ellipse", "inset", "path",
|
||||
"fill-box", "stroke-box", "view-box", "margin-box",
|
||||
"border-box", "padding-box", "content-box" ];
|
||||
var values = InspectorUtils.getCSSValuesForProperty("clip-path");
|
||||
|
@ -0,0 +1,29 @@
|
||||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>CSS Masking: Reference for clip-path's path function with nonzero</title>
|
||||
<style type="text/css">
|
||||
#rect {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
background-color: green;
|
||||
clip-path: url("#clip");
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<p>The test passes if there are a green filled rect.</p>
|
||||
<div id="rect"></div>
|
||||
<svg height="0" width="0">
|
||||
<defs>
|
||||
<clipPath id="clip">
|
||||
<path clip-rule="nonzero" d="M10,10h80v80h-80zM25,25h50v50h-50z"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,24 @@
|
||||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>CSS Masking: Test clip-path property and path function with nonzero</title>
|
||||
<link rel="help" href="https://drafts.csswg.org/css-shapes-2/#funcdef-path">
|
||||
<link rel="stylesheet" href="chrome://reftest/content/path.css">
|
||||
<link rel="match" href="clip-path-path-001-ref.html">
|
||||
</head>
|
||||
<style>
|
||||
#rect {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
background-color: green;
|
||||
}
|
||||
</style>
|
||||
<body>
|
||||
<p>The test passes if there are a green filled rect.</p>
|
||||
<div id="rect" class="path_nonzero_rect"></div>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,29 @@
|
||||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>CSS Masking: Reference for clip-path's path function with evenodd</title>
|
||||
<style type="text/css">
|
||||
#rect {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
background-color: green;
|
||||
clip-path: url("#clip");
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<p>The test passes if there are a green hollow rect.</p>
|
||||
<div id="rect"></div>
|
||||
<svg height="0" width="0">
|
||||
<defs>
|
||||
<clipPath id="clip">
|
||||
<path clip-rule="evenodd" d="M10,10h80v80h-80zM25,25h50v50h-50z"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,24 @@
|
||||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>CSS Masking: Test clip-path property and path function with evenodd</title>
|
||||
<link rel="help" href="https://drafts.csswg.org/css-shapes-2/#funcdef-path">
|
||||
<link rel="stylesheet" href="chrome://reftest/content/path.css">
|
||||
<link rel="match" href="clip-path-path-002-ref.html">
|
||||
</head>
|
||||
<style>
|
||||
#rect {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
background-color: green;
|
||||
}
|
||||
</style>
|
||||
<body>
|
||||
<p>The test passes if there are a green hollow rect.</p>
|
||||
<div id="rect" class="path_evenodd_rect"></div>
|
||||
</body>
|
||||
</html>
|
7
layout/reftests/svg/svg-integration/clip-path/path.css
Normal file
7
layout/reftests/svg/svg-integration/clip-path/path.css
Normal file
@ -0,0 +1,7 @@
|
||||
.path_nonzero_rect {
|
||||
clip-path: path(nonzero, 'M10,10h80v80h-80zM25,25h50v50h-50z');
|
||||
}
|
||||
|
||||
.path_evenodd_rect {
|
||||
clip-path: path(evenodd, 'M10,10h80v80h-80zM25,25h50v50h-50z');
|
||||
}
|
@ -59,3 +59,6 @@ fuzzy(0-64,0-340) fuzzy-if(webrender,104-104,311-311) == clip-path-inset-003.htm
|
||||
== clip-path-stroke-001.html clip-path-stroke-001-ref.html
|
||||
|
||||
== clip-path-transform-001.html clip-path-transform-001-ref.html
|
||||
|
||||
== clip-path-path-001.html clip-path-path-001-ref.html
|
||||
== clip-path-path-002.html clip-path-path-002-ref.html
|
||||
|
@ -1978,9 +1978,14 @@ struct StyleSVGPath final
|
||||
return mPath;
|
||||
}
|
||||
|
||||
StyleFillRule FillRule() const
|
||||
{
|
||||
return mFillRule;
|
||||
}
|
||||
|
||||
bool operator==(const StyleSVGPath& aOther) const
|
||||
{
|
||||
return mPath == aOther.mPath;
|
||||
return mPath == aOther.mPath && mFillRule == aOther.mFillRule;
|
||||
}
|
||||
|
||||
bool operator!=(const StyleSVGPath& aOther) const
|
||||
@ -1990,6 +1995,7 @@ struct StyleSVGPath final
|
||||
|
||||
private:
|
||||
nsTArray<StylePathCommand> mPath;
|
||||
StyleFillRule mFillRule = StyleFillRule::Nonzero;
|
||||
};
|
||||
|
||||
struct StyleShapeSource final
|
||||
|
@ -8129,6 +8129,19 @@ if (false) {
|
||||
other_values: [ "green", "#fc3" ],
|
||||
invalid_values: [ "000000", "ff00ff" ]
|
||||
};
|
||||
|
||||
// |clip-path: path()| is chrome-only.
|
||||
gCSSProperties["clip-path"].other_values.push(
|
||||
"path(nonzero, 'M 10 10 h 100 v 100 h-100 v-100 z')",
|
||||
"path(evenodd, 'M 10 10 h 100 v 100 h-100 v-100 z')",
|
||||
"path('M10,30A20,20 0,0,1 50,30A20,20 0,0,1 90,30Q90,60 50,90Q10,60 10,30z')",
|
||||
);
|
||||
|
||||
gCSSProperties["clip-path"].invalid_values.push(
|
||||
"path(nonzero)",
|
||||
"path(evenodd, '')",
|
||||
"path(abs, 'M 10 10 L 10 10 z')",
|
||||
);
|
||||
}
|
||||
|
||||
if (IsCSSPropertyPrefEnabled("layout.css.filters.enabled")) {
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "gfx2DGlue.h"
|
||||
#include "gfxContext.h"
|
||||
#include "gfxPlatform.h"
|
||||
#include "mozilla/dom/SVGPathData.h"
|
||||
#include "mozilla/gfx/2D.h"
|
||||
#include "mozilla/gfx/PathHelpers.h"
|
||||
#include "mozilla/ShapeUtils.h"
|
||||
@ -25,16 +26,17 @@ using namespace mozilla::dom;
|
||||
using namespace mozilla::gfx;
|
||||
|
||||
/* static*/ void
|
||||
nsCSSClipPathInstance::ApplyBasicShapeClip(gfxContext& aContext,
|
||||
nsIFrame* aFrame)
|
||||
nsCSSClipPathInstance::ApplyBasicShapeOrPathClip(gfxContext& aContext,
|
||||
nsIFrame* aFrame)
|
||||
{
|
||||
auto& clipPathStyle = aFrame->StyleSVGReset()->mClipPath;
|
||||
|
||||
#ifdef DEBUG
|
||||
StyleShapeSourceType type = clipPathStyle.GetType();
|
||||
MOZ_ASSERT(type == StyleShapeSourceType::Shape ||
|
||||
type == StyleShapeSourceType::Box,
|
||||
"This function is used with basic-shape and geometry-box only.");
|
||||
type == StyleShapeSourceType::Box ||
|
||||
type == StyleShapeSourceType::Path,
|
||||
"This is used with basic-shape, geometry-box, and path() only");
|
||||
#endif
|
||||
|
||||
nsCSSClipPathInstance instance(aFrame, clipPathStyle);
|
||||
@ -46,8 +48,8 @@ nsCSSClipPathInstance::ApplyBasicShapeClip(gfxContext& aContext,
|
||||
}
|
||||
|
||||
/* static*/ bool
|
||||
nsCSSClipPathInstance::HitTestBasicShapeClip(nsIFrame* aFrame,
|
||||
const gfxPoint& aPoint)
|
||||
nsCSSClipPathInstance::HitTestBasicShapeOrPathClip(nsIFrame* aFrame,
|
||||
const gfxPoint& aPoint)
|
||||
{
|
||||
auto& clipPathStyle = aFrame->StyleSVGReset()->mClipPath;
|
||||
StyleShapeSourceType type = clipPathStyle.GetType();
|
||||
@ -69,11 +71,13 @@ nsCSSClipPathInstance::HitTestBasicShapeClip(nsIFrame* aFrame,
|
||||
}
|
||||
|
||||
/* static */ Rect
|
||||
nsCSSClipPathInstance::GetBoundingRectForBasicShapeClip(nsIFrame* aFrame,
|
||||
const StyleShapeSource& aClipPathStyle)
|
||||
nsCSSClipPathInstance::GetBoundingRectForBasicShapeOrPathClip(
|
||||
nsIFrame* aFrame,
|
||||
const StyleShapeSource& aClipPathStyle)
|
||||
{
|
||||
MOZ_ASSERT(aClipPathStyle.GetType() == StyleShapeSourceType::Shape ||
|
||||
aClipPathStyle.GetType() == StyleShapeSourceType::Box);
|
||||
aClipPathStyle.GetType() == StyleShapeSourceType::Box ||
|
||||
aClipPathStyle.GetType() == StyleShapeSourceType::Path);
|
||||
|
||||
nsCSSClipPathInstance instance(aFrame, aClipPathStyle);
|
||||
|
||||
@ -86,6 +90,10 @@ nsCSSClipPathInstance::GetBoundingRectForBasicShapeClip(nsIFrame* aFrame,
|
||||
already_AddRefed<Path>
|
||||
nsCSSClipPathInstance::CreateClipPath(DrawTarget* aDrawTarget)
|
||||
{
|
||||
if (mClipPathStyle.GetType() == StyleShapeSourceType::Path) {
|
||||
return CreateClipPathPath(aDrawTarget);
|
||||
}
|
||||
|
||||
nsRect r =
|
||||
nsLayoutUtils::ComputeGeometryBox(mTargetFrame,
|
||||
mClipPathStyle.GetReferenceBox());
|
||||
@ -213,3 +221,18 @@ nsCSSClipPathInstance::CreateClipPathInset(DrawTarget* aDrawTarget,
|
||||
}
|
||||
return builder->Finish();
|
||||
}
|
||||
|
||||
already_AddRefed<Path>
|
||||
nsCSSClipPathInstance::CreateClipPathPath(DrawTarget* aDrawTarget)
|
||||
{
|
||||
const StyleSVGPath* path = mClipPathStyle.GetPath();
|
||||
MOZ_ASSERT(path);
|
||||
|
||||
RefPtr<PathBuilder> builder = aDrawTarget->CreatePathBuilder(
|
||||
path->FillRule() == StyleFillRule::Nonzero ? FillRule::FILL_WINDING
|
||||
: FillRule::FILL_EVEN_ODD);
|
||||
float scale = float(AppUnitsPerCSSPixel()) /
|
||||
mTargetFrame->PresContext()->AppUnitsPerDevPixel();
|
||||
return SVGPathData::BuildPath(
|
||||
path->Path(), builder, NS_STYLE_STROKE_LINECAP_BUTT, 0.0, scale);
|
||||
}
|
||||
|
@ -23,14 +23,16 @@ class nsCSSClipPathInstance
|
||||
typedef mozilla::gfx::Rect Rect;
|
||||
|
||||
public:
|
||||
static void ApplyBasicShapeClip(gfxContext& aContext,
|
||||
nsIFrame* aFrame);
|
||||
static void ApplyBasicShapeOrPathClip(gfxContext& aContext,
|
||||
nsIFrame* aFrame);
|
||||
// aPoint is in CSS pixels.
|
||||
static bool HitTestBasicShapeClip(nsIFrame* aFrame,
|
||||
const gfxPoint& aPoint);
|
||||
static bool HitTestBasicShapeOrPathClip(nsIFrame* aFrame,
|
||||
const gfxPoint& aPoint);
|
||||
|
||||
static Rect GetBoundingRectForBasicShapeOrPathClip(
|
||||
nsIFrame* aFrame,
|
||||
const StyleShapeSource& aClipPathStyle);
|
||||
|
||||
static Rect GetBoundingRectForBasicShapeClip(nsIFrame* aFrame,
|
||||
const StyleShapeSource& aClipPathStyle);
|
||||
private:
|
||||
explicit nsCSSClipPathInstance(nsIFrame* aFrame,
|
||||
const StyleShapeSource aClipPathStyle)
|
||||
@ -53,6 +55,7 @@ private:
|
||||
already_AddRefed<Path> CreateClipPathInset(DrawTarget* aDrawTarget,
|
||||
const nsRect& aRefBox);
|
||||
|
||||
already_AddRefed<Path> CreateClipPathPath(DrawTarget* aDrawTarget);
|
||||
|
||||
/**
|
||||
* The frame for the element that is currently being clipped.
|
||||
|
@ -809,13 +809,13 @@ nsSVGIntegrationUtils::PaintMask(const PaintFramesParams& aParams)
|
||||
|
||||
// Paint clip-path-basic-shape onto ctx
|
||||
gfxContextAutoSaveRestore basicShapeSR;
|
||||
if (maskUsage.shouldApplyBasicShape) {
|
||||
if (maskUsage.shouldApplyBasicShapeOrPath) {
|
||||
matSR.SetContext(&ctx);
|
||||
|
||||
MoveContextOriginToUserSpace(firstFrame, aParams);
|
||||
|
||||
basicShapeSR.SetContext(&ctx);
|
||||
nsCSSClipPathInstance::ApplyBasicShapeClip(ctx, frame);
|
||||
nsCSSClipPathInstance::ApplyBasicShapeOrPathClip(ctx, frame);
|
||||
if (!maskUsage.shouldGenerateMaskLayer) {
|
||||
// Only have basic-shape clip-path effect. Fill clipped region by
|
||||
// opaque white.
|
||||
@ -997,17 +997,17 @@ nsSVGIntegrationUtils::PaintMaskAndClipPath(const PaintFramesParams& aParams)
|
||||
/* If this frame has only a trivial clipPath, set up cairo's clipping now so
|
||||
* we can just do normal painting and get it clipped appropriately.
|
||||
*/
|
||||
if (maskUsage.shouldApplyClipPath || maskUsage.shouldApplyBasicShape) {
|
||||
if (maskUsage.shouldApplyClipPath || maskUsage.shouldApplyBasicShapeOrPath) {
|
||||
gfxContextMatrixAutoSaveRestore matSR(&context);
|
||||
|
||||
MoveContextOriginToUserSpace(firstFrame, aParams);
|
||||
|
||||
MOZ_ASSERT(!maskUsage.shouldApplyClipPath ||
|
||||
!maskUsage.shouldApplyBasicShape);
|
||||
!maskUsage.shouldApplyBasicShapeOrPath);
|
||||
if (maskUsage.shouldApplyClipPath) {
|
||||
clipPathFrame->ApplyClipPath(context, frame, cssPxToDevPxMatrix);
|
||||
} else {
|
||||
nsCSSClipPathInstance::ApplyBasicShapeClip(context, frame);
|
||||
nsCSSClipPathInstance::ApplyBasicShapeOrPathClip(context, frame);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1036,7 +1036,7 @@ nsSVGIntegrationUtils::PaintMaskAndClipPath(const PaintFramesParams& aParams)
|
||||
maskUsage.shouldGenerateClipMaskLayer) {
|
||||
overlayColor.g = 1.0f; // green represents clip-path:<clip-source>.
|
||||
}
|
||||
if (maskUsage.shouldApplyBasicShape) {
|
||||
if (maskUsage.shouldApplyBasicShapeOrPath) {
|
||||
overlayColor.b = 1.0f; // blue represents
|
||||
// clip-path:<basic-shape>||<geometry-box>.
|
||||
}
|
||||
@ -1045,7 +1045,7 @@ nsSVGIntegrationUtils::PaintMaskAndClipPath(const PaintFramesParams& aParams)
|
||||
context.Fill();
|
||||
}
|
||||
|
||||
if (maskUsage.shouldApplyClipPath || maskUsage.shouldApplyBasicShape) {
|
||||
if (maskUsage.shouldApplyClipPath || maskUsage.shouldApplyBasicShapeOrPath) {
|
||||
context.PopClip();
|
||||
}
|
||||
|
||||
|
@ -523,11 +523,13 @@ nsSVGUtils::DetermineMaskUsage(nsIFrame* aFrame, bool aHandleOpacity,
|
||||
break;
|
||||
case StyleShapeSourceType::Shape:
|
||||
case StyleShapeSourceType::Box:
|
||||
aUsage.shouldApplyBasicShape = true;
|
||||
case StyleShapeSourceType::Path:
|
||||
aUsage.shouldApplyBasicShapeOrPath = true;
|
||||
break;
|
||||
case StyleShapeSourceType::None:
|
||||
MOZ_ASSERT(!aUsage.shouldGenerateClipMaskLayer &&
|
||||
!aUsage.shouldApplyClipPath && !aUsage.shouldApplyBasicShape);
|
||||
!aUsage.shouldApplyClipPath &&
|
||||
!aUsage.shouldApplyBasicShapeOrPath);
|
||||
break;
|
||||
default:
|
||||
MOZ_ASSERT_UNREACHABLE("Unsupported clip-path type.");
|
||||
@ -807,11 +809,11 @@ nsSVGUtils::PaintFrameWithEffects(nsIFrame *aFrame,
|
||||
/* If this frame has only a trivial clipPath, set up cairo's clipping now so
|
||||
* we can just do normal painting and get it clipped appropriately.
|
||||
*/
|
||||
if (maskUsage.shouldApplyClipPath || maskUsage.shouldApplyBasicShape) {
|
||||
if (maskUsage.shouldApplyClipPath || maskUsage.shouldApplyBasicShapeOrPath) {
|
||||
if (maskUsage.shouldApplyClipPath) {
|
||||
clipPathFrame->ApplyClipPath(aContext, aFrame, aTransform);
|
||||
} else {
|
||||
nsCSSClipPathInstance::ApplyBasicShapeClip(aContext, aFrame);
|
||||
nsCSSClipPathInstance::ApplyBasicShapeOrPathClip(aContext, aFrame);
|
||||
}
|
||||
}
|
||||
|
||||
@ -856,7 +858,7 @@ nsSVGUtils::PaintFrameWithEffects(nsIFrame *aFrame,
|
||||
svgFrame->PaintSVG(*target, aTransform, aImgParams, aDirtyRect);
|
||||
}
|
||||
|
||||
if (maskUsage.shouldApplyClipPath || maskUsage.shouldApplyBasicShape) {
|
||||
if (maskUsage.shouldApplyClipPath || maskUsage.shouldApplyBasicShapeOrPath) {
|
||||
aContext.PopClip();
|
||||
}
|
||||
|
||||
@ -878,7 +880,7 @@ nsSVGUtils::HitTestClip(nsIFrame *aFrame, const gfxPoint &aPoint)
|
||||
if (!props.mClipPath) {
|
||||
const nsStyleSVGReset *style = aFrame->StyleSVGReset();
|
||||
if (style->HasClipPath()) {
|
||||
return nsCSSClipPathInstance::HitTestBasicShapeClip(aFrame, aPoint);
|
||||
return nsCSSClipPathInstance::HitTestBasicShapeOrPathClip(aFrame, aPoint);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -606,19 +606,22 @@ public:
|
||||
bool shouldGenerateMaskLayer;
|
||||
bool shouldGenerateClipMaskLayer;
|
||||
bool shouldApplyClipPath;
|
||||
bool shouldApplyBasicShape;
|
||||
bool shouldApplyBasicShapeOrPath;
|
||||
float opacity;
|
||||
|
||||
MaskUsage()
|
||||
: shouldGenerateMaskLayer(false), shouldGenerateClipMaskLayer(false),
|
||||
shouldApplyClipPath(false), shouldApplyBasicShape(false), opacity(0.0)
|
||||
: shouldGenerateMaskLayer(false)
|
||||
, shouldGenerateClipMaskLayer(false)
|
||||
, shouldApplyClipPath(false)
|
||||
, shouldApplyBasicShapeOrPath(false)
|
||||
, opacity(0.0)
|
||||
{ }
|
||||
|
||||
bool shouldDoSomething() {
|
||||
return shouldGenerateMaskLayer
|
||||
|| shouldGenerateClipMaskLayer
|
||||
|| shouldApplyClipPath
|
||||
|| shouldApplyBasicShape
|
||||
|| shouldApplyBasicShapeOrPath
|
||||
|| opacity != 1.0;
|
||||
}
|
||||
};
|
||||
|
@ -8,6 +8,7 @@ reftest.jar:
|
||||
content/PerTestCoverageUtils.jsm (../../../tools/code-coverage/PerTestCoverageUtils.jsm)
|
||||
content/input.css (../../../editor/reftests/xul/input.css)
|
||||
content/moz-bool-pref.css (../../../layout/reftests/css-parsing/moz-bool-pref.css)
|
||||
content/path.css (../../../layout/reftests/svg/svg-integration/clip-path/path.css)
|
||||
content/progress.css (../../../layout/reftests/forms/progress/style.css)
|
||||
* content/manifest.jsm (manifest.jsm)
|
||||
* content/reftest.jsm (reftest.jsm)
|
||||
|
@ -47,7 +47,7 @@ class SessionLifecycleTest : BaseSessionTest() {
|
||||
@Test fun open_allowCallsWhileClosed() {
|
||||
sessionRule.session.close()
|
||||
|
||||
sessionRule.session.loadUri(HELLO_HTML_PATH)
|
||||
sessionRule.session.loadTestPath(HELLO_HTML_PATH)
|
||||
sessionRule.session.reload()
|
||||
|
||||
sessionRule.session.open()
|
||||
|
@ -3,10 +3,15 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "RedirectChannelRegistrar.h"
|
||||
#include "mozilla/StaticPtr.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
namespace {
|
||||
StaticRefPtr<RedirectChannelRegistrar> gSingleton;
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS(RedirectChannelRegistrar, nsIRedirectChannelRegistrar)
|
||||
|
||||
RedirectChannelRegistrar::RedirectChannelRegistrar()
|
||||
@ -15,6 +20,26 @@ RedirectChannelRegistrar::RedirectChannelRegistrar()
|
||||
, mId(1)
|
||||
, mLock("RedirectChannelRegistrar")
|
||||
{
|
||||
MOZ_ASSERT(!gSingleton);
|
||||
}
|
||||
|
||||
// static
|
||||
already_AddRefed<nsIRedirectChannelRegistrar>
|
||||
RedirectChannelRegistrar::GetOrCreate()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (!gSingleton) {
|
||||
gSingleton = new RedirectChannelRegistrar();
|
||||
}
|
||||
return do_AddRef(gSingleton);
|
||||
}
|
||||
|
||||
// static
|
||||
void
|
||||
RedirectChannelRegistrar::Shutdown()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
gSingleton = nullptr;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
@ -26,6 +26,13 @@ class RedirectChannelRegistrar final : public nsIRedirectChannelRegistrar
|
||||
private:
|
||||
~RedirectChannelRegistrar() = default;
|
||||
|
||||
public:
|
||||
// Singleton accessor
|
||||
static already_AddRefed<nsIRedirectChannelRegistrar> GetOrCreate();
|
||||
|
||||
// Cleanup the singleton instance on shutdown
|
||||
static void Shutdown();
|
||||
|
||||
protected:
|
||||
typedef nsInterfaceHashtable<nsUint32HashKey, nsIChannel>
|
||||
ChannelHashtable;
|
||||
|
@ -176,6 +176,7 @@ EXPORTS.mozilla.net += [
|
||||
'MemoryDownloader.h',
|
||||
'PartiallySeekableInputStream.h',
|
||||
'Predictor.h',
|
||||
'RedirectChannelRegistrar.h',
|
||||
'ReferrerPolicy.h',
|
||||
'SimpleChannelParent.h',
|
||||
'TCPFastOpen.h',
|
||||
|
@ -47,7 +47,7 @@
|
||||
#include "nsIPrivateBrowsingChannel.h"
|
||||
#include "nsIPropertyBag2.h"
|
||||
#include "nsIProtocolProxyService.h"
|
||||
#include "nsIRedirectChannelRegistrar.h"
|
||||
#include "mozilla/net/RedirectChannelRegistrar.h"
|
||||
#include "nsIRequestObserverProxy.h"
|
||||
#include "nsIScriptSecurityManager.h"
|
||||
#include "nsISensitiveInfoHiddenURI.h"
|
||||
@ -2757,11 +2757,9 @@ NS_LinkRedirectChannels(uint32_t channelId,
|
||||
nsIParentChannel *parentChannel,
|
||||
nsIChannel **_result)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
nsCOMPtr<nsIRedirectChannelRegistrar> registrar =
|
||||
do_GetService("@mozilla.org/redirectchannelregistrar;1", &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
RedirectChannelRegistrar::GetOrCreate();
|
||||
MOZ_ASSERT(registrar);
|
||||
|
||||
return registrar->LinkChannels(channelId,
|
||||
parentChannel,
|
||||
|
@ -474,17 +474,6 @@
|
||||
#define NS_URICLASSIFIERSERVICE_CONTRACTID \
|
||||
"@mozilla.org/uriclassifierservice"
|
||||
|
||||
// Redirect channel registrar used for redirect to various protocols
|
||||
#define NS_REDIRECTCHANNELREGISTRAR_CONTRACTID \
|
||||
"@mozilla.org/redirectchannelregistrar;1"
|
||||
#define NS_REDIRECTCHANNELREGISTRAR_CID \
|
||||
{ /* {b69043a6-8929-4d60-8d17-a27e44a8393e} */ \
|
||||
0xb69043a6, \
|
||||
0x8929, \
|
||||
0x4d60, \
|
||||
{ 0x8d, 0x17, 0xa2, 0x7e, 0x44, 0xa8, 0x39, 0x3e } \
|
||||
}
|
||||
|
||||
// service implementing nsINetworkPredictor
|
||||
#define NS_NETWORKPREDICTOR_CONTRACTID \
|
||||
"@mozilla.org/network/predictor;1"
|
||||
|
@ -39,6 +39,7 @@
|
||||
#include "Predictor.h"
|
||||
#include "nsIThreadPool.h"
|
||||
#include "mozilla/net/NeckoChild.h"
|
||||
#include "RedirectChannelRegistrar.h"
|
||||
|
||||
#include "nsNetCID.h"
|
||||
|
||||
@ -131,10 +132,6 @@ NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsEffectiveTLDService, Init)
|
||||
#include "nsSerializationHelper.h"
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR(nsSerializationHelper)
|
||||
|
||||
#include "RedirectChannelRegistrar.h"
|
||||
typedef mozilla::net::RedirectChannelRegistrar RedirectChannelRegistrar;
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR(RedirectChannelRegistrar)
|
||||
|
||||
#include "CacheStorageService.h"
|
||||
typedef mozilla::net::CacheStorageService CacheStorageService;
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR(CacheStorageService)
|
||||
@ -652,6 +649,8 @@ static void nsNetShutdown()
|
||||
|
||||
mozilla::net::Http2CompressionCleanup();
|
||||
|
||||
mozilla::net::RedirectChannelRegistrar::Shutdown();
|
||||
|
||||
delete gNetSniffers;
|
||||
gNetSniffers = nullptr;
|
||||
delete gDataSniffers;
|
||||
@ -768,7 +767,6 @@ NS_DEFINE_NAMED_CID(NS_NETWORK_LINK_SERVICE_CID);
|
||||
NS_DEFINE_NAMED_CID(NS_NETWORK_LINK_SERVICE_CID);
|
||||
#endif
|
||||
NS_DEFINE_NAMED_CID(NS_SERIALIZATION_HELPER_CID);
|
||||
NS_DEFINE_NAMED_CID(NS_REDIRECTCHANNELREGISTRAR_CID);
|
||||
NS_DEFINE_NAMED_CID(NS_CACHE_STORAGE_SERVICE_CID);
|
||||
NS_DEFINE_NAMED_CID(NS_NSILOADCONTEXTINFOFACTORY_CID);
|
||||
NS_DEFINE_NAMED_CID(NS_NETWORKPREDICTOR_CID);
|
||||
@ -893,7 +891,6 @@ static const mozilla::Module::CIDEntry kNeckoCIDs[] = {
|
||||
{ &kNS_NETWORK_LINK_SERVICE_CID, false, nullptr, nsNotifyAddrListenerConstructor },
|
||||
#endif
|
||||
{ &kNS_SERIALIZATION_HELPER_CID, false, nullptr, nsSerializationHelperConstructor },
|
||||
{ &kNS_REDIRECTCHANNELREGISTRAR_CID, false, nullptr, RedirectChannelRegistrarConstructor },
|
||||
{ &kNS_CACHE_STORAGE_SERVICE_CID, false, nullptr, CacheStorageServiceConstructor },
|
||||
{ &kNS_NSILOADCONTEXTINFOFACTORY_CID, false, nullptr, LoadContextInfoFactoryConstructor },
|
||||
{ &kNS_NETWORKPREDICTOR_CID, false, nullptr, mozilla::net::Predictor::Create },
|
||||
@ -1017,7 +1014,6 @@ static const mozilla::Module::ContractIDEntry kNeckoContracts[] = {
|
||||
{ NS_NETWORK_LINK_SERVICE_CONTRACTID, &kNS_NETWORK_LINK_SERVICE_CID },
|
||||
#endif
|
||||
{ NS_SERIALIZATION_HELPER_CONTRACTID, &kNS_SERIALIZATION_HELPER_CID },
|
||||
{ NS_REDIRECTCHANNELREGISTRAR_CONTRACTID, &kNS_REDIRECTCHANNELREGISTRAR_CID },
|
||||
{ NS_CACHE_STORAGE_SERVICE_CONTRACTID, &kNS_CACHE_STORAGE_SERVICE_CID },
|
||||
{ NS_CACHE_STORAGE_SERVICE_CONTRACTID2, &kNS_CACHE_STORAGE_SERVICE_CID },
|
||||
{ NS_NSILOADCONTEXTINFOFACTORY_CONTRACTID, &kNS_NSILOADCONTEXTINFOFACTORY_CID },
|
||||
|
@ -45,7 +45,7 @@
|
||||
#include "nsCORSListenerProxy.h"
|
||||
#include "nsIIPCSerializableInputStream.h"
|
||||
#include "nsIPrompt.h"
|
||||
#include "nsIRedirectChannelRegistrar.h"
|
||||
#include "mozilla/net/RedirectChannelRegistrar.h"
|
||||
#include "nsIWindowWatcher.h"
|
||||
#include "nsIDocument.h"
|
||||
#include "nsStreamUtils.h"
|
||||
@ -972,7 +972,7 @@ HttpChannelParent::RecvRedirect2Verify(const nsresult& aResult,
|
||||
|
||||
// Wait for background channel ready on target channel
|
||||
nsCOMPtr<nsIRedirectChannelRegistrar> redirectReg =
|
||||
do_GetService(NS_REDIRECTCHANNELREGISTRAR_CONTRACTID);
|
||||
RedirectChannelRegistrar::GetOrCreate();
|
||||
MOZ_ASSERT(redirectReg);
|
||||
|
||||
nsCOMPtr<nsIParentChannel> redirectParentChannel;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user