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

This commit is contained in:
Daniel Varga 2019-03-26 23:59:18 +02:00
commit a9686f654d
110 changed files with 8428 additions and 671 deletions

View File

@ -55,9 +55,6 @@ module.exports = {
}, {
// TODO: Bug 1246594. Empty this list once the rule has landed for all dirs
"files": [
"gfx/layers/apz/test/mochitest/**",
"mobile/android/components/**",
"mobile/android/modules/**",
"modules/libmar/tests/unit/head_libmar.js",
"netwerk/protocol/http/WellKnownOpportunisticUtils.jsm",
"netwerk/test/httpserver/httpd.js",

View File

@ -65,6 +65,12 @@ var SidebarUI = {
_switcherArrow: null,
_inited: false,
_initDeferred: PromiseUtils.defer(),
get promiseInitialized() {
return this._initDeferred.promise;
},
get initialized() {
return this._inited;
},
@ -83,6 +89,8 @@ var SidebarUI = {
});
this._inited = true;
this._initDeferred.resolve();
},
uninit() {

View File

@ -983,7 +983,7 @@ const menuTracker = {
gMenuBuilder.build(subject);
},
onWindowOpen(window) {
async onWindowOpen(window) {
for (const id of menuTracker.menuIds) {
const menu = window.document.getElementById(id);
menu.addEventListener("popupshowing", menuTracker);
@ -991,7 +991,10 @@ const menuTracker = {
const sidebarHeader = window.document.getElementById("sidebar-switcher-target");
sidebarHeader.addEventListener("SidebarShown", menuTracker.onSidebarShown);
if (window.SidebarUI.currentID === "viewBookmarksSidebar") {
await window.SidebarUI.promiseInitialized;
if (!window.closed && window.SidebarUI.currentID === "viewBookmarksSidebar") {
menuTracker.onSidebarShown({currentTarget: sidebarHeader});
}
},

View File

@ -1185,7 +1185,6 @@ function getTransactionsForCopy(items, insertionIndex,
!PlacesUtils.bookmarks.isVirtualRootItem(guid) &&
!PlacesUtils.isVirtualLeftPaneItem(guid)) {
transaction = PlacesTransactions.Copy({
excludingAnnotation: "Places/SmartBookmark",
guid,
newIndex: index,
newParentGuid: insertionParentGuid,

View File

@ -4,6 +4,8 @@
const TP_PB_ENABLED_PREF = "privacy.trackingprotection.pbmode.enabled";
const {UrlbarTestUtils} = ChromeUtils.import("resource://testing-common/UrlbarTestUtils.jsm");
/**
* Opens a new private window and loads "about:privatebrowsing" there.
*/
@ -133,12 +135,15 @@ add_task(async function test_search_handoff_on_keydown() {
});
ok(urlBarHasNormalFocus(win), "url bar has normal focused");
is(win.gURLBar.value, "@google f", "url bar has search text");
await UrlbarTestUtils.promiseSearchComplete(win);
// Close the popup.
await UrlbarTestUtils.promisePopupClose(win);
// Hitting ESC should reshow the in-content search
await new Promise(r => EventUtils.synthesizeKey("KEY_Escape", {}, win, r));
await ContentTask.spawn(tab, null, async function() {
ok(!content.document.getElementById("search-handoff-button").classList.contains("hidden"),
"in-content search is not");
"in-content search is not hidden");
});
await BrowserTestUtils.closeWindow(win);
@ -176,6 +181,11 @@ add_task(async function test_search_handoff_on_paste() {
.getService(SpecialPowers.Ci.nsIClipboardHelper);
helper.copyString("words");
await new Promise(r => EventUtils.synthesizeKey("v", {accelKey: true}, win, r));
// TODO: Bug 1539199 We should be able to wait for search complete for AwesomeBar
// as well.
if (UrlbarPrefs.get("quantumbar")) {
await UrlbarTestUtils.promiseSearchComplete(win);
}
ok(urlBarHasNormalFocus(win), "url bar has normal focused");
is(win.gURLBar.value, "@google words", "url bar has search text");

View File

@ -554,6 +554,7 @@ class UrlbarInput {
muxer: "UnifiedComplete",
providers: ["UnifiedComplete"],
searchString,
userContextId: this.window.gBrowser.selectedBrowser.getAttribute("usercontextid"),
}));
}
@ -989,12 +990,18 @@ class UrlbarInput {
*/
_loadURL(url, openUILinkWhere, params, result = {},
browser = this.window.gBrowser.selectedBrowser) {
this.value = url;
browser.userTypedValue = url;
// No point in setting these because we'll handleRevert() a few rows below.
if (openUILinkWhere == "current") {
this.value = url;
browser.userTypedValue = url;
}
if (this.window.gInitialPages.includes(url)) {
// No point in setting this if we are loading in a new window.
if (openUILinkWhere != "window" &&
this.window.gInitialPages.includes(url)) {
browser.initialPageLoadedFromUserAction = url;
}
try {
UrlbarUtils.addToUrlbarHistory(url, this.window);
} catch (ex) {

View File

@ -395,6 +395,8 @@ class UrlbarQueryContext {
* The maximum number of results that will be displayed for this query.
* @param {boolean} options.allowAutofill
* Whether or not to allow providers to include autofill results.
* @param {number} options.userContextId
* The container id where this context was generated, if any.
*/
constructor(options = {}) {
this._checkRequiredOptions(options, [
@ -418,6 +420,8 @@ class UrlbarQueryContext {
(!Array.isArray(options.sources) || !options.sources.length)) {
throw new Error(`Invalid sources list`);
}
this.userContextId = options.userContextId;
}
/**

View File

@ -226,6 +226,17 @@ var UrlbarTestUtils = {
let urlbar = getUrlbarAbstraction(win);
return urlbar.isPopupOpen();
},
/**
* Returns the userContextId (container id) for the last search.
* @param {object} win The browser window
* @returns {Promise} resolved when fetching is complete
* @resolves {number} a userContextId
*/
promiseUserContextId(win) {
let urlbar = getUrlbarAbstraction(win);
return urlbar.promiseUserContextId();
},
};
/**
@ -340,6 +351,15 @@ class UrlbarAbstraction {
"waiting urlbar search to complete", 100, 50);
}
async promiseUserContextId() {
const defaultId = Ci.nsIScriptSecurityManager.DEFAULT_USER_CONTEXT_ID;
if (this.quantumbar) {
let context = await this.urlbar.lastQueryContextPromise;
return context.userContextId || defaultId;
}
return this.urlbar.userContextId || defaultId;
}
async promiseResultAt(index) {
if (!this.quantumbar) {
// In the legacy address bar, old results are replaced when new results

View File

@ -70,7 +70,13 @@ async function withNewWindow(callback) {
"", "chrome");
await BrowserTestUtils.waitForEvent(win, "load");
win.gBrowser = {};
win.gBrowser = {
selectedBrowser: {
getAttribute() {
return undefined;
},
},
};
// Clone the elements into the new window, so we get exact copies without having
// to replicate the xul.

View File

@ -11,8 +11,10 @@ const START_VALUE = "example.org";
add_task(async function setup() {
await SpecialPowers.pushPrefEnv({
set: [["browser.altClickSave", true],
["browser.urlbar.autoFill", false]],
set: [
["browser.altClickSave", true],
["browser.urlbar.autoFill", false],
],
});
});
@ -29,7 +31,7 @@ add_task(async function alt_left_click_test() {
};
});
triggerCommand("click", {altKey: true});
await triggerCommand("click", {altKey: true});
await saveURLPromise;
ok(true, "SaveURL was called");
@ -41,7 +43,7 @@ add_task(async function shift_left_click_test() {
let destinationURL = "http://" + TEST_VALUE + "/";
let newWindowPromise = BrowserTestUtils.waitForNewWindow({url: destinationURL});
triggerCommand("click", {shiftKey: true});
await triggerCommand("click", {shiftKey: true});
let win = await newWindowPromise;
info("URL should be loaded in a new window");
@ -65,7 +67,7 @@ add_task(async function right_click_test() {
// Add a new tab.
await promiseOpenNewTab();
triggerCommand("click", {button: 2});
await triggerCommand("click", {button: 2});
// Right click should do nothing (context menu will be shown).
is(gURLBar.value, TEST_VALUE, "Urlbar still has the value we entered");
@ -81,7 +83,7 @@ add_task(async function shift_accel_left_click_test() {
let tab = await promiseOpenNewTab();
let loadStartedPromise = promiseLoadStarted();
triggerCommand("click", {accelKey: true, shiftKey: true});
await triggerCommand("click", {accelKey: true, shiftKey: true});
await loadStartedPromise;
// Check the load occurred in a new background tab.
@ -92,7 +94,7 @@ add_task(async function shift_accel_left_click_test() {
// Select the new background tab
gBrowser.selectedTab = gBrowser.selectedTab.nextElementSibling;
is(gURLBar.value, TEST_VALUE, "New URL is loaded in new tab");
is(gURLBar.textValue, TEST_VALUE, "New URL is loaded in new tab");
// Cleanup.
gBrowser.removeCurrentTab();
@ -129,7 +131,7 @@ add_task(async function load_in_current_tab_test() {
// Trigger a load and check it occurs in the current tab.
let loadStartedPromise = promiseLoadStarted();
triggerCommand(type, details);
await triggerCommand(type, details);
await loadStartedPromise;
info("URL should be loaded in the current tab");
@ -149,7 +151,7 @@ add_task(async function load_in_new_tab_test() {
desc: "Ctrl/Cmd left click on go button",
type: "click",
details: {accelKey: true},
url: null,
url: "about:blank",
},
{
desc: "Alt+Return keypress in a dirty tab",
@ -163,16 +165,16 @@ add_task(async function load_in_new_tab_test() {
info(`Running test: ${desc}`);
// Add a new tab.
let tab = await promiseOpenNewTab(url || "about:blank");
let tab = await promiseOpenNewTab(url);
// Trigger a load and check it occurs in the current tab.
let tabSwitchedPromise = promiseNewTabSwitched();
triggerCommand(type, details);
await triggerCommand(type, details);
await tabSwitchedPromise;
// Check the load occurred in a new tab.
info("URL should be loaded in a new focused tab");
is(gURLBar.inputField.value, TEST_VALUE, "Urlbar still has the value we entered");
is(gURLBar.textValue, TEST_VALUE, "Urlbar still has the value we entered");
await promiseCheckChildNoFocusedElement(gBrowser.selectedBrowser);
is(document.activeElement, gBrowser.selectedBrowser, "Content window should be focused");
isnot(gBrowser.selectedTab, tab, "New URL was loaded in a new tab");
@ -183,11 +185,15 @@ add_task(async function load_in_new_tab_test() {
}
});
function triggerCommand(type, details = {}) {
async function triggerCommand(type, details = {}) {
gURLBar.focus();
gURLBar.value = "";
EventUtils.sendString(TEST_VALUE);
Assert.equal(await UrlbarTestUtils.promiseUserContextId(window),
gBrowser.selectedTab.getAttribute("usercontextid"),
"userContextId must be the same as the originating tab");
if (type == "click") {
ok(gURLBar.hasAttribute("usertyping"),
"usertyping attribute must be set for the go button to be visible");
@ -212,8 +218,9 @@ function promiseLoadStarted() {
});
}
let gUserContextIdSerial = 1;
async function promiseOpenNewTab(url = "about:blank") {
let tab = BrowserTestUtils.addTab(gBrowser, url);
let tab = BrowserTestUtils.addTab(gBrowser, url, {userContextId: gUserContextIdSerial++});
let tabSwitchPromise = promiseNewTabSwitched(tab);
gBrowser.selectedTab = tab;
await BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);

View File

@ -45,10 +45,14 @@
--white-100: #fff; /* for ui, no special semantic */
/* Typography from Photon */
/* See https://firefox-dev.tools/photon/visuals/typography.html */
--body-10-font-size: 13px;
--body-10-font-weight: 400;
--body-20-font-size: 15px;
--body-20-font-weight: 700;
--body-20-font-weight: 400;
--body-20-font-weight-bold: 700;
--caption-10-font-size: 11px;
--caption-10-font-weight: 400;
--caption-20-font-size: 13px;
--caption-20-font-weight: 400;
--caption-20-color: var(--grey-50);
@ -245,7 +249,7 @@ p, h1 {
/* adds breathing space to the separator */
.separator--breathe {
margin: calc(var(--base-unit) * 4) 0;
margin: calc(var(--base-unit) * 5) 0;
}
/* a series of button-like elements, layed out horizontally */

View File

@ -43,7 +43,7 @@
align-self: center;
grid-area: name;
font-size: var(--body-20-font-size);
font-weight: var(--body-20-font-weight);
font-weight: var(--body-20-font-weight-bold);
line-height: 1.5;
margin-inline-start: calc(var(--base-unit) * 3);
}

View File

@ -2,6 +2,11 @@
* 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/. */
.sidebar {
display: grid;
grid-template-rows: auto auto;
}
.sidebar__label {
color: var(--grey-40);
display: block;
@ -13,3 +18,20 @@
.sidebar__refresh-usb {
text-align: center;
}
.sidebar__footer {
align-self: flex-end;
}
.sidebar__footer__support-help {
display: flex;
align-items: center;
justify-content: flex-start;
column-gap: calc(var(--base-unit) * 4);
height: 100%;
}
.sidebar__footer__icon {
width: calc(var(--base-unit) * 4);
height: calc(var(--base-unit) * 4);
}

View File

@ -63,9 +63,7 @@ class Sidebar extends PureComponent {
renderDevicesEmpty() {
return SidebarItem(
{
isSelected: false,
},
{},
Localized(
{
id: "about-debugging-sidebar-no-devices",
@ -89,12 +87,12 @@ class Sidebar extends PureComponent {
}
// render all devices otherwise
return [
...this.renderSidebarItems(GLOBE_ICON, networkRuntimes),
...this.renderSidebarItems(USB_ICON, usbRuntimes),
...this.renderRuntimeItems(GLOBE_ICON, networkRuntimes),
...this.renderRuntimeItems(USB_ICON, usbRuntimes),
];
}
renderSidebarItems(icon, runtimes) {
renderRuntimeItems(icon, runtimes) {
const { dispatch, selectedPage, selectedRuntimeId } = this.props;
return runtimes.map(runtime => {
@ -123,6 +121,51 @@ class Sidebar extends PureComponent {
});
}
renderFooter() {
const HELP_ICON_SRC = "chrome://global/skin/icons/help.svg";
const SUPPORT_URL = "https://developer.mozilla.org/docs/Tools/about:debugging";
return dom.footer(
{
className: "sidebar__footer",
},
dom.ul(
{},
SidebarItem(
{
className: "sidebar-item--condensed",
to: SUPPORT_URL,
},
dom.span(
{
className: "sidebar__footer__support-help",
},
Localized(
{
id: "about-debugging-sidebar-support-icon",
attrs: {
alt: true,
},
},
dom.img(
{
className: "sidebar__footer__icon",
src: HELP_ICON_SRC,
}
),
),
Localized(
{
id: "about-debugging-sidebar-support",
},
dom.span({}, "about-debugging-sidebar-support"),
)
)
),
)
);
}
render() {
const { dispatch, selectedPage, selectedRuntimeId, isScanningUsb } = this.props;
@ -156,17 +199,15 @@ class Sidebar extends PureComponent {
),
SidebarItem(
{
className: "sidebar-item--overflow",
isSelected: false,
className: "sidebar-item--overflow sidebar-item--full-width",
},
dom.hr({ className: "separator" }),
dom.hr({ className: "separator separator--breathe" }),
this.renderAdbAddonStatus(),
),
this.renderDevices(),
SidebarItem(
{
className: "sidebar-item--breathe sidebar__refresh-usb",
isSelected: false,
key: "refresh-devices",
},
RefreshDevicesButton({
@ -174,7 +215,8 @@ class Sidebar extends PureComponent {
isScanning: isScanningUsb,
})
),
)
),
this.renderFooter(),
);
}
}

View File

@ -15,8 +15,9 @@
border-radius: 2px;
display: grid;
grid-template-columns: 34px 1fr;
font-size: 16px;
height: 100%;
font-size: var(--body-20-font-size);
font-weight: var(--body-20-font-weight);
}
.sidebar-fixed-item__icon {

View File

@ -33,6 +33,7 @@ class SidebarFixedItem extends PureComponent {
return SidebarItem(
{
className: "sidebar-item--tall",
isSelected,
to,
},

View File

@ -11,17 +11,29 @@
.sidebar-item {
color: var(--sidebar-text-color);
border-radius: 2px;
height: var(--category-height);
padding-inline-end: var(--category-padding);
padding-inline-start: var(--category-padding);
transition: background-color var(--category-transition-duration);
-moz-user-select: none;
}
.sidebar-item--overflow {
.sidebar-item--tall {
height: var(--category-height);
}
.sidebar-item--condensed {
height: calc(var(--base-unit) * 9);
}
.sidebar-item--full-width {
padding-inline-start: 0;
padding-inline-end: 0;
}
/* .sidebar-item--overflow {
min-height: var(--category-height);
height: auto;
}
} */
.sidebar-item__link {
display: block;

View File

@ -22,24 +22,41 @@ class SidebarItem extends PureComponent {
};
}
static get defaultProps() {
return {
isSelected: false,
};
}
renderContent() {
const { children, to } = this.props;
if (to) {
return Link(
{
className: "sidebar-item__link js-sidebar-link",
to,
},
children
);
const isExternalUrl = /^http/.test(to);
return isExternalUrl
? dom.a(
{
className: "sidebar-item__link",
href: to,
target: "_blank",
},
children,
)
: Link(
{
className: "sidebar-item__link js-sidebar-link",
to,
},
children,
);
}
return children;
}
render() {
const {className, isSelected, to } = this.props;
const { className, isSelected, to } = this.props;
return dom.li(
{

View File

@ -11,15 +11,25 @@
*/
.sidebar-runtime-item__container {
font-size: 0.8em;
align-items: center;
display: grid;
grid-column-gap: var(--base-unit);
grid-template-columns: calc(var(--base-unit) * 6) 1fr auto;
height: 100%;
font-size: var(--body-20-font-size);
font-weight: var(--body-20-font-weight);
}
.sidebar-runtime-item__icon {
fill: currentColor;
-moz-context-properties: fill;
}
.sidebar-runtime-item__runtime {
line-height: 1.2;
}
.sidebar-runtime-item__runtime__details {
font-size: var(--caption-10-font-size);
font-weight: var(--caption-10-font-weight);
}

View File

@ -57,29 +57,47 @@ class SidebarRuntimeItem extends PureComponent {
const displayName = isUnknown ?
getString("about-debugging-sidebar-runtime-item-waiting-for-runtime") : name;
const titleLocalizationId = deviceName ?
"about-debugging-sidebar-runtime-item-name" :
"about-debugging-sidebar-runtime-item-name-no-device";
const localizationId = deviceName
? "about-debugging-sidebar-runtime-item-name"
: "about-debugging-sidebar-runtime-item-name-no-device";
const className = "ellipsis-text sidebar-runtime-item__runtime";
function renderWithDevice() {
return dom.span(
{
className,
title: localizationId,
},
deviceName,
dom.br({}),
dom.span(
{
className: "sidebar-runtime-item__runtime__details",
},
displayName,
),
);
}
function renderNoDevice() {
return dom.span(
{
className,
title: localizationId,
},
displayName,
);
}
return Localized(
{
id: titleLocalizationId,
id: localizationId,
attrs: { title: true },
$deviceName: deviceName,
$displayName: displayName,
},
dom.span(
{
className: "ellipsis-text",
title: titleLocalizationId,
},
displayName,
// If a deviceName is available, display it on a separate line.
...(deviceName ? [
dom.br({}),
deviceName,
] : []),
)
deviceName ? renderWithDevice() : renderNoDevice(),
);
}
@ -99,10 +117,11 @@ class SidebarRuntimeItem extends PureComponent {
return SidebarItem(
{
className: "sidebar-item--tall",
isSelected,
to: isConnected ? `/runtime/${encodeURIComponent(runtimeId)}` : null,
},
dom.div(
dom.section(
{
className: "sidebar-runtime-item__container",
},

View File

@ -34,7 +34,7 @@ add_task(async function() {
await waitUntil(() => !usbRuntimeSidebarItem.querySelector(".js-connect-button"));
info("Check whether the label of item is updated after connecting");
ok(usbRuntimeSidebarItem.textContent.startsWith(RUNTIME_NAME), "Label of item updated");
ok(usbRuntimeSidebarItem.textContent.includes(RUNTIME_NAME), "Label of item updated");
info("Remove all USB runtimes");
mocks.removeUSBRuntime(RUNTIME_ID);

View File

@ -0,0 +1,10 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
module.exports = {
"env": {
"jest": true,
},
};

View File

@ -0,0 +1,22 @@
# Jest Tests for devtools/client/aboutdebugging-new
## About
DevTools React components can be tested using [jest](https://jestjs.io/). Jest allows to test our UI components in isolation and complement our end to end mochitests.
## Run locally
We use yarn for dependency management. To run the tests locally:
```
cd devtools/client/shared/aboutdebugging-new/test/jest
yarn && yarn test
```
## Run on try
The tests run on try on linux64 platforms. The complete name of try job is `devtools-tests`. In treeherder, they will show up as `node(devtools)`.
Adding the tests to a try push depends on the try selector you are using.
- try fuzzy: look for the job named `source-test-node-devtools-tests`
The configuration file for try can be found at `taskcluster/ci/source-test/node.yml`

View File

@ -0,0 +1,19 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Message component renders the expected snapshot for INFO level 1`] = `
<aside
className="message message--level-info js-message some-classname"
>
<img
className="message__icon"
src="chrome://devtools/skin/images/aboutdebugging-information.svg"
/>
<div
className="message__body"
>
<div>
Message content
</div>
</div>
</aside>
`;

View File

@ -0,0 +1,27 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/**
* Unit tests for the shared/Message component.
*/
const renderer = require("react-test-renderer");
const React = require("devtools/client/shared/vendor/react");
const dom = require("devtools/client/shared/vendor/react-dom-factories");
const { MESSAGE_LEVEL } = require("devtools/client/aboutdebugging-new/src/constants");
const Message = React.createFactory(require("devtools/client/aboutdebugging-new/src/components/shared/Message"));
describe("Message component", () => {
it("renders the expected snapshot for INFO level", () => {
const message = renderer.create(Message({
children: dom.div({}, "Message content"),
className: "some-classname",
level: MESSAGE_LEVEL.INFO,
}));
expect(message.toJSON()).toMatchSnapshot();
});
});

View File

@ -0,0 +1,14 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/* global __dirname */
module.exports = {
verbose: true,
moduleNameMapper: {
// Map all require("devtools/...") to the real devtools root.
"^devtools\\/(.*)": `${__dirname}/../../../../$1`,
},
};

View File

@ -0,0 +1,17 @@
{
"name": "devtools-client-framework-tests",
"license": "MPL-2.0",
"version": "0.0.1",
"engines": {
"node": ">=8.9.4"
},
"scripts": {
"test": "jest"
},
"dependencies": {
"jest": "^23.0.0",
"react-test-renderer": "16.4.1",
"react": "16.4.1",
"react-dom": "16.4.1"
}
}

File diff suppressed because it is too large Load Diff

View File

@ -34,6 +34,14 @@ async function mapLocations(
);
}
// Filter out positions, that are not in the original source Id
function filterBySource(positions, sourceId) {
if (!isOriginalId(sourceId)) {
return positions;
}
return positions.filter(position => position.location.sourceId == sourceId);
}
function filterByUniqLocation(positions: MappedLocation[]) {
return uniqBy(positions, ({ location }) => makeBreakpointId(location));
}
@ -96,6 +104,8 @@ async function _setBreakpointPositions(sourceId, thunkArgs) {
let positions = convertToList(results, generatedSource);
positions = await mapLocations(positions, thunkArgs);
positions = filterBySource(positions, sourceId);
positions = filterByUniqLocation(positions);
const source = getSource(getState(), sourceId);

View File

@ -44,7 +44,7 @@ export function generateBreakpoint(
text: "",
location: {
sourceUrl: `http://localhost:8000/examples/${filename}`,
sourceId: `${filename}/originalSource`,
sourceId: `${filename}`,
line,
column
},

View File

@ -30,19 +30,13 @@ jest.mock("../../utils/prefs", () => ({
asyncStore: {
pendingBreakpoints: {}
},
features: {
replay: false
},
clear: jest.fn()
}));
import "../sources/loadSourceText";
import {
createStore,
selectors,
actions,
makeOriginalSource,
makeSource,
waitForState
} from "../../utils/test-head";
@ -81,7 +75,7 @@ describe("when adding breakpoints", () => {
mockSourceMaps()
);
const source = makeOriginalSource("foo.js");
const source = makeSource("foo.js");
await dispatch(actions.newSource(source));
await dispatch(actions.newSource(makeSource("foo.js")));
await dispatch(actions.loadSourceText(source));
@ -116,8 +110,8 @@ describe("when adding breakpoints", () => {
mockSourceMaps()
);
const source1 = makeOriginalSource("foo");
const source2 = makeOriginalSource("foo2");
const source1 = makeSource("foo");
const source2 = makeSource("foo2");
await dispatch(actions.newSource(makeSource("foo")));
await dispatch(actions.newSource(makeSource("foo2")));
@ -147,7 +141,7 @@ describe("when adding breakpoints", () => {
mockSourceMaps()
);
const source = makeOriginalSource("foo");
const source = makeSource("foo");
await dispatch(actions.newSource(makeSource("foo")));
await dispatch(actions.newSource(source));
await dispatch(actions.loadSourceText(source));
@ -170,8 +164,8 @@ describe("when adding breakpoints", () => {
await dispatch(actions.newSource(makeSource("foo")));
await dispatch(actions.newSource(makeSource("foo2")));
const source1 = makeOriginalSource("foo");
const source2 = makeOriginalSource("foo2");
const source1 = makeSource("foo");
const source2 = makeSource("foo2");
await dispatch(actions.newSource(source1));
await dispatch(actions.newSource(source2));
@ -200,7 +194,7 @@ describe("when changing an existing breakpoint", () => {
const bp = generateBreakpoint("foo");
const id = makePendingLocationId(bp.location);
const source = makeOriginalSource("foo");
const source = makeSource("foo");
await dispatch(actions.newSource(source));
await dispatch(actions.newSource(makeSource("foo")));
await dispatch(actions.loadSourceText(source));
@ -225,7 +219,7 @@ describe("when changing an existing breakpoint", () => {
await dispatch(actions.newSource(makeSource("foo")));
const source = makeOriginalSource("foo");
const source = makeSource("foo");
await dispatch(actions.newSource(source));
await dispatch(actions.loadSourceText(source));
@ -244,7 +238,7 @@ describe("when changing an existing breakpoint", () => {
);
const bp = generateBreakpoint("foo.js");
const source = makeOriginalSource("foo.js");
const source = makeSource("foo.js");
await dispatch(actions.newSource(source));
await dispatch(actions.newSource(makeSource("foo.js")));
await dispatch(actions.loadSourceText(source));
@ -282,7 +276,7 @@ describe("initializing when pending breakpoints exist in prefs", () => {
await dispatch(actions.newSource(makeSource("bar.js")));
const source = makeOriginalSource("bar.js");
const source = makeSource("bar.js");
await dispatch(actions.newSource(source));
await dispatch(actions.loadSourceText(source));
await dispatch(actions.addBreakpoint(bar.location));
@ -299,7 +293,7 @@ describe("initializing when pending breakpoints exist in prefs", () => {
);
const bp = generateBreakpoint("foo.js");
const source = makeOriginalSource("foo.js");
const source = makeSource("foo.js");
await dispatch(actions.newSource(source));
await dispatch(actions.newSource(makeSource("foo.js")));
await dispatch(actions.loadSourceText(source));
@ -320,7 +314,7 @@ describe("initializing with disabled pending breakpoints in prefs", () => {
);
const { getState, dispatch } = store;
const source = makeOriginalSource("bar.js");
const source = makeSource("bar.js");
await dispatch(actions.newSource(makeSource("bar.js")));
await dispatch(actions.newSource(source));
@ -356,7 +350,7 @@ describe("adding sources", () => {
expect(selectors.getBreakpointCount(getState())).toEqual(0);
const source = makeOriginalSource("bar.js");
const source = makeSource("bar.js");
await dispatch(actions.newSource(makeSource("bar.js")));
await dispatch(actions.newSource(source));
@ -368,7 +362,7 @@ describe("adding sources", () => {
});
it("corresponding breakpoints are added to the original source", async () => {
const source = makeOriginalSource("bar.js", { sourceMapURL: "foo" });
const source = makeSource("bar.js", { sourceMapURL: "foo" });
const store = createStore(mockClient({ "5": [2] }), loadInitialState(), {
getOriginalURLs: async () => [source.url],
getOriginalSourceText: async () => ({ source: "" }),
@ -406,8 +400,8 @@ describe("adding sources", () => {
expect(selectors.getBreakpointCount(getState())).toEqual(0);
const source1 = makeOriginalSource("bar.js");
const source2 = makeOriginalSource("foo.js");
const source1 = makeSource("bar.js");
const source2 = makeSource("foo.js");
await dispatch(actions.newSource(makeSource("bar.js")));
await dispatch(actions.newSource(makeSource("foo.js")));
await dispatch(actions.newSources([source1, source2]));

View File

@ -70,7 +70,7 @@ class DebugTargetInfo extends PureComponent {
return dom.span(
{
className: "iconized-label",
className: "iconized-label js-connection-info",
},
dom.img({ src: image, alt: `${connectionType} icon`}),
this.props.L10N.getStr(l10nId),
@ -108,7 +108,7 @@ class DebugTargetInfo extends PureComponent {
className: "iconized-label",
},
dom.img({ src: favicon, alt: "favicon"}),
title ? dom.b({ className: "devtools-ellipsis-text"}, title) : null,
title ? dom.b({ className: "devtools-ellipsis-text js-target-title"}, title) : null,
dom.span({ className: "devtools-ellipsis-text" }, url),
);
}

View File

@ -0,0 +1,10 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
module.exports = {
"env": {
"jest": true,
},
};

View File

@ -0,0 +1,22 @@
# Jest Tests for devtools/client/framework
## About
DevTools React components can be tested using [jest](https://jestjs.io/). Jest allows to test our UI components in isolation and complement our end to end mochitests.
## Run locally
We use yarn for dependency management. To run the tests locally:
```
cd devtools/client/shared/framework/test/jest
yarn && yarn test
```
## Run on try
The tests run on try on linux64 platforms. The complete name of the try job is `devtools-tests`. In treeherder, they will show up as `node(devtools)`.
Adding the tests to a try push depends on the try selector you are using.
- try fuzzy: look for the job named `source-test-node-devtools-tests`
The configuration file for try can be found at `taskcluster/ci/source-test/node.yml`

View File

@ -0,0 +1,130 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`DebugTargetInfo component renders the expected snapshot for This Firefox target 1`] = `
<header
className="debug-target-info"
>
<span
className="iconized-label"
>
<img
className="channel-icon"
src="chrome://devtools/skin/images/aboutdebugging-firefox-release.svg"
/>
<b
className="devtools-ellipsis-text"
>
toolbox.debugTargetInfo.runtimeLabel.thisFirefox-1.0.0
</b>
<span
className="devtools-ellipsis-text"
/>
</span>
<span
className="iconized-label"
>
<img
alt="favicon"
src="chrome://devtools/skin/images/aboutdebugging-globe-icon.svg"
/>
<b
className="devtools-ellipsis-text js-target-title"
>
Test Tab Name
</b>
<span
className="devtools-ellipsis-text"
>
http://some.target/url
</span>
</span>
</header>
`;
exports[`DebugTargetInfo component renders the expected snapshot for USB Release target 1`] = `
<header
className="debug-target-info"
>
<span
className="iconized-label js-connection-info"
>
<img
alt="usb icon"
src="chrome://devtools/skin/images/aboutdebugging-usb-icon.svg"
/>
toolbox.debugTargetInfo.connection.usb
</span>
<span
className="iconized-label"
>
<img
className="channel-icon"
src="chrome://devtools/skin/images/aboutdebugging-firefox-release.svg"
/>
<b
className="devtools-ellipsis-text"
>
toolbox.debugTargetInfo.runtimeLabel-usbRuntimeBrandName-1.0.0
</b>
<span
className="devtools-ellipsis-text"
>
usbDeviceName
</span>
</span>
<span
className="iconized-label"
>
<img
alt="favicon"
src="chrome://devtools/skin/images/aboutdebugging-globe-icon.svg"
/>
<b
className="devtools-ellipsis-text js-target-title"
>
Test Tab Name
</b>
<span
className="devtools-ellipsis-text"
>
http://some.target/url
</span>
</span>
</header>
`;
exports[`DebugTargetInfo component renders the expected snapshot for a Toolbox with an unnamed target 1`] = `
<header
className="debug-target-info"
>
<span
className="iconized-label"
>
<img
className="channel-icon"
src="chrome://devtools/skin/images/aboutdebugging-firefox-release.svg"
/>
<b
className="devtools-ellipsis-text"
>
toolbox.debugTargetInfo.runtimeLabel.thisFirefox-1.0.0
</b>
<span
className="devtools-ellipsis-text"
/>
</span>
<span
className="iconized-label"
>
<img
alt="favicon"
src="chrome://devtools/skin/images/aboutdebugging-globe-icon.svg"
/>
<span
className="devtools-ellipsis-text"
>
http://some.target/without/a/name
</span>
</span>
</header>
`;

View File

@ -0,0 +1,110 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/**
* Unit tests for the DebugTargetInfo component.
*/
const renderer = require("react-test-renderer");
const React = require("devtools/client/shared/vendor/react");
const DebugTargetInfo = React.createFactory(require("devtools/client/framework/components/DebugTargetInfo"));
const { CONNECTION_TYPES } = require("devtools/client/shared/remote-debugging/remote-client-manager");
/**
* Stub for the L10N property expected by the DebugTargetInfo component.
*/
const stubL10N = {
getStr: id => id,
getFormatStr: (id, ...args) => [id, ...args].join("-"),
};
const findByClassName = (testInstance, className) => {
return testInstance.findAll(node => {
return node.props.className && node.props.className.includes(className);
});
};
const TEST_TOOLBOX = {
target: {
name: "Test Tab Name",
url: "http://some.target/url",
},
};
const TEST_TOOLBOX_NO_NAME = {
target: {
url: "http://some.target/without/a/name",
},
};
const USB_DEVICE_DESCRIPTION = {
brandName: "usbRuntimeBrandName",
connectionType: CONNECTION_TYPES.USB,
channel: "release",
deviceName: "usbDeviceName",
version: "1.0.0",
};
const THIS_FIREFOX_DEVICE_DESCRIPTION = {
brandName: "thisFirefoxRuntimeBrandName",
connectionType: CONNECTION_TYPES.THIS_FIREFOX,
channel: "release",
version: "1.0.0",
};
const USB_TARGET_INFO = DebugTargetInfo({
deviceDescription: USB_DEVICE_DESCRIPTION,
toolbox: TEST_TOOLBOX,
L10N: stubL10N,
});
const THIS_FIREFOX_TARGET_INFO = DebugTargetInfo({
deviceDescription: THIS_FIREFOX_DEVICE_DESCRIPTION,
toolbox: TEST_TOOLBOX,
L10N: stubL10N,
});
const THIS_FIREFOX_NO_NAME_TARGET_INFO = DebugTargetInfo({
deviceDescription: THIS_FIREFOX_DEVICE_DESCRIPTION,
toolbox: TEST_TOOLBOX_NO_NAME,
L10N: stubL10N,
});
describe("DebugTargetInfo component", () => {
it("displays connection info for USB Release target", () => {
const targetInfo = renderer.create(USB_TARGET_INFO);
expect(findByClassName(targetInfo.root, "js-connection-info").length).toEqual(1);
});
it("renders the expected snapshot for USB Release target", () => {
const targetInfo = renderer.create(USB_TARGET_INFO);
expect(targetInfo.toJSON()).toMatchSnapshot();
});
it("hides the connection info for This Firefox target", () => {
const targetInfo = renderer.create(THIS_FIREFOX_TARGET_INFO);
expect(findByClassName(targetInfo.root, "js-connection-info").length).toEqual(0);
});
it("displays the target title if the target of the Toolbox has a name", () => {
const targetInfo = renderer.create(THIS_FIREFOX_TARGET_INFO);
expect(findByClassName(targetInfo.root, "js-target-title").length).toEqual(1);
});
it("renders the expected snapshot for This Firefox target", () => {
const targetInfo = renderer.create(THIS_FIREFOX_TARGET_INFO);
expect(targetInfo.toJSON()).toMatchSnapshot();
});
it("doesn't display the target title if the target of the Toolbox has no name", () => {
const targetInfo = renderer.create(THIS_FIREFOX_NO_NAME_TARGET_INFO);
expect(findByClassName(targetInfo.root, "js-target-title").length).toEqual(0);
});
it("renders the expected snapshot for a Toolbox with an unnamed target", () => {
const targetInfo = renderer.create(THIS_FIREFOX_NO_NAME_TARGET_INFO);
expect(targetInfo.toJSON()).toMatchSnapshot();
});
});

View File

@ -0,0 +1,14 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/* global __dirname */
module.exports = {
verbose: true,
moduleNameMapper: {
// Map all require("devtools/...") to the real devtools root.
"^devtools\\/(.*)": `${__dirname}/../../../../$1`,
},
};

View File

@ -0,0 +1,17 @@
{
"name": "devtools-client-framework-tests",
"license": "MPL-2.0",
"version": "0.0.1",
"engines": {
"node": ">=8.9.4"
},
"scripts": {
"test": "jest"
},
"dependencies": {
"jest": "^23.0.0",
"react-test-renderer": "16.4.1",
"react": "16.4.1",
"react-dom": "16.4.1"
}
}

File diff suppressed because it is too large Load Diff

View File

@ -19,7 +19,6 @@
<link rel="stylesheet" href="chrome://devtools/skin/layout.css"/>
<link rel="stylesheet" href="chrome://devtools/skin/animation.css"/>
<link rel="stylesheet" href="resource://devtools/client/shared/components/tabs/Tabs.css"/>
<link rel="stylesheet" href="resource://devtools/client/shared/components/tabs/TabBar.css"/>
<link rel="stylesheet" href="resource://devtools/client/shared/components/SidebarToggle.css"/>
<link rel="stylesheet" href="resource://devtools/client/inspector/components/InspectorTabPanel.css"/>
<link rel="stylesheet" href="resource://devtools/client/shared/components/splitter/SplitBox.css"/>

View File

@ -63,6 +63,15 @@ about-debugging-sidebar-runtime-item-name =
about-debugging-sidebar-runtime-item-name-no-device =
.title = { $displayName }
# Text to show in the footer of the sidebar that links to a help page
# (currently: https://developer.mozilla.org/docs/Tools/about:debugging)
about-debugging-sidebar-support = Debugging Support
# Text to show as the ALT attribute of a help icon that accompanies the help about
# debugging link in the footer of the sidebar
about-debugging-sidebar-support-icon =
.alt = Help icon
# Text displayed in a sidebar button to refresh the list of USB devices. Clicking on it
# will attempt to update the list of devices displayed in the sidebar.
about-debugging-refresh-usb-devices-button = Refresh devices

View File

@ -6,7 +6,6 @@
@import "resource://devtools/client/shared/components/splitter/SplitBox.css";
@import "resource://devtools/client/shared/components/tree/TreeView.css";
@import "resource://devtools/client/shared/components/tabs/Tabs.css";
@import "resource://devtools/client/shared/components/tabs/TabBar.css";
@import "chrome://devtools/skin/components-frame.css";
@import "chrome://devtools/content/shared/sourceeditor/codemirror/lib/codemirror.css";
@import "chrome://devtools/content/shared/sourceeditor/codemirror/addon/dialog/dialog.css";

View File

@ -6,7 +6,6 @@
@import "resource://devtools/client/shared/components/splitter/SplitBox.css";
@import "resource://devtools/client/shared/components/tree/TreeView.css";
@import "resource://devtools/client/shared/components/tabs/Tabs.css";
@import "resource://devtools/client/shared/components/tabs/TabBar.css";
@import "chrome://devtools/skin/components-frame.css";
@import "chrome://devtools/content/shared/sourceeditor/codemirror/lib/codemirror.css";
@import "chrome://devtools/content/shared/sourceeditor/codemirror/addon/dialog/dialog.css";

View File

@ -1,46 +0,0 @@
/* vim:set ts=2 sw=2 sts=2 et: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* Hides the tab strip in the TabBar */
div[hidetabs=true] .tabs .tabs-navigation {
display: none;
}
.tabs .tabs-navigation {
display: flex;
line-height: 15px;
height: 24px;
}
.tabs .tabs-menu-item:first-child {
border-inline-start-width: 0;
}
/* Remove the outline focusring from tabs-menu-item. */
.tabs .tabs-navigation .tabs-menu-item > a:-moz-focusring {
outline: none;
}
.tabs .tabs-menu-item.is-active {
height: 23px;
}
/* The tab takes entire horizontal space and individual tabs
should stretch accordingly. Use flexbox for the behavior.
Use also `overflow: hidden` so, 'overflow' and 'underflow'
events are fired (it's utilized by the all-tabs-menu). */
.tabs .tabs-navigation .tabs-menu {
overflow: hidden;
display: flex;
}
.tabs .tabs-navigation .tabs-menu-item {
flex-grow: 1;
}
.tabs .tabs-navigation .tabs-menu-item a {
text-align: center;
}

View File

@ -12,25 +12,72 @@
flex-direction: column;
}
/* Hides the tab strip in the TabBar */
div[hidetabs=true] .tabs .tabs-navigation {
display: none;
}
.tabs .tabs-navigation {
display: flex;
line-height: 15px;
height: 24px;
position: relative;
border-bottom: 1px solid var(--theme-splitter-color);
background: var(--theme-tab-toolbar-background);
}
.tabs .tabs-menu {
list-style: none;
padding: 0;
margin: 0;
flex-grow: 1;
}
/* The tab takes entire horizontal space and individual tabs
should stretch accordingly. Use flexbox for the behavior.
Use also `overflow: hidden` so, 'overflow' and 'underflow'
events are fired (it's utilized by the all-tabs-menu). */
.tabs .tabs-navigation .tabs-menu {
overflow: hidden;
display: flex;
}
.tabs .tabs-menu-item {
display: inline-block;
position: relative;
margin: 0;
padding: 0;
color: var(--theme-toolbar-color);
}
.tabs .tabs-menu-item.is-active {
color: var(--theme-toolbar-selected-color);
}
.tabs .tabs-menu-item:hover {
background-color: var(--theme-toolbar-hover);
}
.tabs .tabs-menu-item:hover:active:not(.is-active) {
background-color: var(--theme-toolbar-hover-active);
}
.tabs .tabs-menu-item a {
display: flex;
justify-content: center;
padding: 4px 8px;
padding: 3px 10px;
border: 1px solid transparent;
font-size: 12px;
text-decoration: none;
white-space: nowrap;
cursor: default;
-moz-user-select: none;
text-align: center;
}
/* Remove the outline focusring from tabs-menu-item. */
.tabs .tabs-navigation .tabs-menu-item > a:-moz-focusring {
outline: none;
}
.tabs .tabs-menu-item .tab-badge {
@ -54,11 +101,6 @@
-moz-user-select: none !important;
}
.tabs .tabs-menu-item a {
cursor: default;
-moz-user-select: none;
}
/* Make sure panel content takes entire vertical space. */
.tabs .panels {
flex: 1;
@ -69,43 +111,4 @@
height: 100%;
overflow-x: hidden;
overflow-y: auto;
}
.tabs .tabs-navigation,
.tabs .tabs-navigation {
position: relative;
border-bottom: 1px solid var(--theme-splitter-color);
background: var(--theme-tab-toolbar-background);
}
.theme-dark .tabs .tabs-menu-item,
.theme-light .tabs .tabs-menu-item {
margin: 0;
padding: 0;
color: var(--theme-toolbar-color);
}
.theme-dark .tabs .tabs-menu-item.is-active,
.theme-light .tabs .tabs-menu-item.is-active {
color: var(--theme-toolbar-selected-color);
}
.theme-dark .tabs .tabs-menu-item:last-child,
.theme-light .tabs .tabs-menu-item:last-child {
border-inline-end-width: 1px;
}
.theme-dark .tabs .tabs-menu-item a,
.theme-light .tabs .tabs-menu-item a {
padding: 3px 10px;
}
.theme-dark .tabs .tabs-menu-item:hover,
.theme-light .tabs .tabs-menu-item:hover {
background-color: var(--theme-toolbar-hover);
}
.theme-dark .tabs .tabs-menu-item:hover:active:not(.is-active),
.theme-light .tabs .tabs-menu-item:hover:active:not(.is-active) {
background-color: var(--theme-toolbar-hover-active);
}
}

View File

@ -5,7 +5,6 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
DevToolsModules(
'TabBar.css',
'TabBar.js',
'Tabs.css',
'Tabs.js',

View File

@ -14,7 +14,6 @@ Test all-tabs menu.
<link rel="stylesheet" type="text/css" href="resource://devtools/client/themes/variables.css">
<link rel="stylesheet" type="text/css" href="resource://devtools/client/themes/common.css">
<link rel="stylesheet" type="text/css" href="resource://devtools/client/shared/components/tabs/Tabs.css">
<link rel="stylesheet" type="text/css" href="resource://devtools/client/shared/components/tabs/TabBar.css">
<link rel="stylesheet" type="text/css" href="resource://devtools/client/inspector/components/side-panel.css">
<link rel="stylesheet" type="text/css" href="resource://devtools/client/inspector/components/InspectorTabPanel.css">
</head>

View File

@ -17,7 +17,6 @@
<link rel="stylesheet" href="resource://devtools/client/shared/components/SmartTrace.css"/>
<link rel="stylesheet" href="resource://devtools/client/shared/components/reps/reps.css"/>
<link rel="stylesheet" href="resource://devtools/client/shared/components/tabs/Tabs.css"/>
<link rel="stylesheet" href="resource://devtools/client/shared/components/tabs/TabBar.css"/>
<link rel="stylesheet" href="resource://devtools/client/shared/components/NotificationBox.css"/>
<link rel="stylesheet" href="chrome://devtools/content/netmonitor/src/assets/styles/httpi.css"/>
<link rel="stylesheet" href="resource://devtools/client/webconsole/components/ReverseSearchInput.css"/>

View File

@ -389,13 +389,13 @@ const AccessibleActor = ActorClassWithSpec(accessibleSpec, {
const { DOMNode: rawNode } = this.rawAccessible;
const win = rawNode.ownerGlobal;
this.walker.loadTransitionDisablingStyleSheet(win);
this.walker.clearStyles(win);
const contrastRatio = await getContrastRatioFor(rawNode.parentNode, {
bounds: this.bounds,
win,
});
this.walker.removeTransitionDisablingStyleSheet(win);
this.walker.restoreStyles(win);
return contrastRatio;
},
@ -407,16 +407,27 @@ const AccessibleActor = ActorClassWithSpec(accessibleSpec, {
* Audit results for the accessible object.
*/
async audit() {
// More audit steps will be added here in the near future. In addition to colour
// contrast ratio we will add autits for to the missing names, invalid states, etc.
// (For example see bug 1518808).
const [ contrastRatio ] = await Promise.all([
this._getContrastRatio(),
]);
if (this._auditing) {
return this._auditing;
}
return this.isDefunct ? null : {
// More audit steps will be added here in the near future. In addition to
// colour contrast ratio we will add autits for to the missing names,
// invalid states, etc. (For example see bug 1518808).
this._auditing = Promise.all([
this._getContrastRatio(),
]).then(([
contrastRatio,
};
]) => {
const audit = this.isDefunct ? null : {
contrastRatio,
};
this._auditing = null;
return audit;
});
return this._auditing;
},
snapshot() {

View File

@ -477,12 +477,14 @@ const AccessibleWalkerActor = ActorClassWithSpec(accessibleWalkerSpec, {
},
/**
* Load accessibility highlighter style sheet used for preventing transitions and
* applying transparency when calculating colour contrast.
* Ensure that nothing interferes with the audit for an accessible object
* (CSS, overlays) by load accessibility highlighter style sheet used for
* preventing transitions and applying transparency when calculating colour
* contrast as well as temporarily hiding accessible highlighter overlay.
* @param {Object} win
* Window where highlighting happens.
*/
loadTransitionDisablingStyleSheet(win) {
clearStyles(win) {
if (this._sheetLoaded) {
return;
}
@ -493,23 +495,45 @@ const AccessibleWalkerActor = ActorClassWithSpec(accessibleWalkerSpec, {
// taking a snapshot for contrast measurement).
loadSheet(win, HIGHLIGHTER_STYLES_SHEET);
this._sheetLoaded = true;
this.hideHighlighter();
},
/**
* Unload accessibility highlighter style sheet used for preventing transitions and
* applying transparency when calculating colour contrast.
* Restore CSS and overlays that could've interfered with the audit for an
* accessible object by unloading accessibility highlighter style sheet used
* for preventing transitions and applying transparency when calculating
* colour contrast and potentially restoring accessible highlighter overlay.
* @param {Object} win
* Window where highlighting was happenning.
*/
removeTransitionDisablingStyleSheet(win) {
restoreStyles(win) {
if (!this._sheetLoaded) {
return;
}
this.showHighlighter();
removeSheet(win, HIGHLIGHTER_STYLES_SHEET);
this._sheetLoaded = false;
},
hideHighlighter() {
// TODO: Fix this workaround that temporarily removes higlighter bounds
// overlay that can interfere with the contrast ratio calculation.
if (this._highlighter) {
const highlighter = this._highlighter.instance;
highlighter.hideAccessibleBounds();
}
},
showHighlighter() {
// TODO: Fix this workaround that temporarily removes higlighter bounds
// overlay that can interfere with the contrast ratio calculation.
if (this._highlighter) {
const highlighter = this._highlighter.instance;
highlighter.showAccessibleBounds();
}
},
/**
* Public method used to show an accessible object highlighter on the client
* side.

View File

@ -509,6 +509,13 @@ exports.CustomHighlighterActor = protocol.ActorClassWithSpec(customHighlighterSp
release: function() {},
/**
* Get current instance of the highlighter object.
*/
get instance() {
return this._highlighter;
},
/**
* Show the highlighter.
* This calls through to the highlighter instance's |show(node, options)|

View File

@ -232,18 +232,53 @@ class AccessibleHighlighter extends AutoRefreshHighlighter {
this.highlighterEnv.window.document.documentElement);
}
/**
* Public API method to temporarily hide accessible bounds for things like
* color contrast calculation.
*/
hideAccessibleBounds() {
if (this.getElement("elements").hasAttribute("hidden")) {
return;
}
this._hideAccessibleBounds();
this._shouldRestoreBoundsVisibility = true;
}
/**
* Public API method to show accessible bounds in case they were temporarily
* hidden.
*/
showAccessibleBounds() {
if (this._shouldRestoreBoundsVisibility) {
this._showAccessibleBounds();
}
}
/**
* Hide the accessible bounds container.
*/
_hideAccessibleBounds() {
this._shouldRestoreBoundsVisibility = null;
setIgnoreLayoutChanges(true);
this.getElement("elements").setAttribute("hidden", "true");
setIgnoreLayoutChanges(false,
this.highlighterEnv.window.document.documentElement);
}
/**
* Show the accessible bounds container.
*/
_showAccessibleBounds() {
this._shouldRestoreBoundsVisibility = null;
if (!this.currentNode || !this.highlighterEnv.window) {
return;
}
setIgnoreLayoutChanges(true);
this.getElement("elements").removeAttribute("hidden");
setIgnoreLayoutChanges(false,
this.highlighterEnv.window.document.documentElement);
}
/**

View File

@ -337,11 +337,39 @@ class XULWindowAccessibleHighlighter {
return isNodeValid(node) || isNodeValid(node, TEXT_NODE);
}
/**
* Public API method to temporarily hide accessible bounds for things like
* color contrast calculation.
*/
hideAccessibleBounds() {
if (this.container.hasAttribute("hidden")) {
return;
}
this._hideAccessibleBounds();
this._shouldRestoreBoundsVisibility = true;
}
/**
* Public API method to show accessible bounds in case they were temporarily
* hidden.
*/
showAccessibleBounds() {
if (this._shouldRestoreBoundsVisibility) {
this._showAccessibleBounds();
}
}
/**
* Show accessible bounds highlighter.
*/
_showAccessibleBounds() {
this._shouldRestoreBoundsVisibility = null;
if (this.container) {
if (!this.currentNode || !this.highlighterEnv.window) {
return;
}
this.container.removeAttribute("hidden");
}
}
@ -350,6 +378,7 @@ class XULWindowAccessibleHighlighter {
* Hide accessible bounds highlighter.
*/
_hideAccessibleBounds() {
this._shouldRestoreBoundsVisibility = null;
if (this.container) {
this.container.setAttribute("hidden", "true");
}

View File

@ -22,7 +22,8 @@
#include "mozilla/dom/KeyframeEffectBinding.h"
#include "mozilla/dom/KeyframeEffect.h" // For PropertyValuesPair etc.
#include "mozilla/dom/Nullable.h"
#include "jsapi.h" // For ForOfIterator etc.
#include "jsapi.h" // For most JSAPI
#include "js/ForOfIterator.h" // For JS::ForOfIterator
#include "nsClassHashtable.h"
#include "nsContentUtils.h" // For GetContextForContent
#include "nsCSSPropertyIDSet.h"

View File

@ -19,6 +19,7 @@
#include "mozilla/dom/ShadowRoot.h"
#include "nsHTMLTags.h"
#include "jsapi.h"
#include "js/ForOfIterator.h" // JS::ForOfIterator
#include "xpcprivate.h"
#include "nsGlobalWindow.h"

View File

@ -12,7 +12,8 @@
#include "AccessCheck.h"
#include "jsapi.h"
#include "js/JSON.h"
#include "js/ForOfIterator.h" // JS::ForOfIterator
#include "js/JSON.h" // JS_ParseJSON
#include "mozAutoDocUpdate.h"
#include "mozilla/AsyncEventDispatcher.h"
#include "mozilla/CORSMode.h"

View File

@ -1134,11 +1134,18 @@ class CGHeaders(CGWrapper):
headerSet = declareIncludes
else:
headerSet = bindingHeaders
if t.nullable():
# Need to make sure that Nullable as a dictionary
# member works.
headerSet.add("mozilla/dom/Nullable.h")
unrolled = t.unroll()
# Strip off outer layers and add headers they might (conservatively:
# only nullable non-pointer types need Nullable.h, and only
# sequences outside unions require ForOfIterator.h) require.
unrolled = t
while True:
if unrolled.nullable():
headerSet.add("mozilla/dom/Nullable.h")
elif unrolled.isSequence():
bindingHeaders.add("js/ForOfIterator.h")
else:
break
unrolled = unrolled.inner
if unrolled.isUnion():
headerSet.add(self.getUnionDeclarationFilename(config, unrolled))
bindingHeaders.add("mozilla/dom/UnionConversions.h")
@ -1371,6 +1378,10 @@ def UnionTypes(unionTypes, config):
if f.nullable():
headers.add("mozilla/dom/Nullable.h")
isSequence = f.isSequence()
if isSequence:
# Dealing with sequences requires for-of-compatible
# iteration.
implheaders.add("js/ForOfIterator.h")
f = f.unroll()
if f.isPromise():
headers.add("mozilla/dom/Promise.h")
@ -1471,6 +1482,10 @@ def UnionConversions(unionTypes, config):
unionConversions[name] = CGUnionConversionStruct(t, config)
def addHeadersForType(f):
if f.isSequence():
# Sequences require JSAPI C++ for-of iteration code to fill
# them.
headers.add("js/ForOfIterator.h")
f = f.unroll()
if f.isPromise():
headers.add("mozilla/dom/Promise.h")

View File

@ -13,7 +13,6 @@
#include "mozilla/ipc/BackgroundUtils.h"
#include "nsContentUtils.h"
#include "nsIPrincipal.h"
#include "nsIScriptSecurityManager.h"
#include "nsNetUtil.h"
namespace mozilla {
@ -118,12 +117,6 @@ void PrincipalVerifier::VerifyOnMainThread() {
return;
}
nsCOMPtr<nsIScriptSecurityManager> ssm = nsContentUtils::GetSecurityManager();
if (NS_WARN_IF(!ssm)) {
DispatchToInitiatingThread(NS_ERROR_ILLEGAL_DURING_SHUTDOWN);
return;
}
// Verify if a child process uses system principal, which is not allowed
// to prevent system principal is spoofed.
if (NS_WARN_IF(actor && principal->IsSystemPrincipal())) {

View File

@ -606,6 +606,15 @@ dictionary WindowActorOptions {
*/
boolean includeChrome = false;
/**
* An array of URL match patterns (as accepted by the MatchPattern
* class in MatchPattern.webidl) which restrict which pages the actor
* may be instantiated for. If this is defined, only documents URL which match
* are allowed to have the given actor created for them. Other
* documents will fail to have their actor constructed, returning nullptr.
**/
sequence<DOMString> matches;
/** This fields are used for configuring individual sides of the actor. */
required WindowActorSidedOptions parent;
required WindowActorChildOptions child;

View File

@ -651,7 +651,7 @@ void TextComposition::EndHandlingComposition(EditorBase* aEditorBase) {
#ifdef DEBUG
RefPtr<EditorBase> editorBase = GetEditorBase();
MOZ_ASSERT(editorBase == aEditorBase,
MOZ_ASSERT(!editorBase || editorBase == aEditorBase,
"Another editor handled the composition?");
#endif // #ifdef DEBUG
mEditorBaseWeak = nullptr;

View File

@ -1168,7 +1168,9 @@ void ContentChild::LaunchRDDProcess() {
nsresult rv;
Endpoint<PRemoteDecoderManagerChild> endpoint;
Unused << SendLaunchRDDProcess(&rv, &endpoint);
if (rv == NS_OK) {
// Only call InitForContent if we got a valid enpoint back which
// indicates we needed to launch an RDD process.
if (rv == NS_OK && endpoint.IsValid()) {
RemoteDecoderManagerChild::InitForContent(std::move(endpoint));
}
}));

View File

@ -1064,6 +1064,14 @@ mozilla::ipc::IPCResult ContentParent::RecvLaunchRDDProcess(
Preferences::GetBool("media.rdd-process.enabled", false)) {
RDDProcessManager* rdd = RDDProcessManager::Get();
if (rdd) {
// If there is already an RDDChild, then we've already launched the
// RDD process. We don't need to do anything else. Specifically,
// we want to avoid calling CreateContentBridge again because that
// causes the RemoteDecoderManagerParent to rebuild needlessly.
if (rdd->GetRDDChild()) {
return IPC_OK();
}
rdd->LaunchRDDProcess();
bool rddOpened = rdd->CreateContentBridge(OtherPid(), aEndpoint);

View File

@ -12,6 +12,8 @@
#include "mozilla/dom/PContent.h"
#include "mozilla/StaticPtr.h"
#include "mozJSComponentLoader.h"
#include "mozilla/extensions/WebExtensionContentScript.h"
#include "mozilla/Logging.h"
namespace mozilla {
namespace dom {
@ -119,9 +121,6 @@ class JSWindowActorProtocol final : public nsIObserver,
nsTArray<nsCString> mObservers;
};
const nsAString& Name() const { return mName; }
bool AllFrames() const { return mAllFrames; }
bool IncludeChrome() const { return mIncludeChrome; }
const ParentSide& Parent() const { return mParent; }
const ChildSide& Child() const { return mChild; }
@ -129,17 +128,22 @@ class JSWindowActorProtocol final : public nsIObserver,
void UnregisterListenersFor(EventTarget* aRoot);
void AddObservers();
void RemoveObservers();
bool Matches(BrowsingContext* aBrowsingContext, nsIURI* aURI);
private:
explicit JSWindowActorProtocol(const nsAString& aName) : mName(aName) {}
extensions::MatchPatternSet* GetURIMatcher();
~JSWindowActorProtocol() = default;
nsString mName;
bool mAllFrames = false;
bool mIncludeChrome = false;
nsTArray<nsString> mMatches;
ParentSide mParent;
ChildSide mChild;
RefPtr<extensions::MatchPatternSet> mURIMatcher;
};
NS_IMPL_ISUPPORTS(JSWindowActorProtocol, nsIObserver, nsIDOMEventListener);
@ -153,6 +157,7 @@ JSWindowActorProtocol::FromIPC(const JSWindowActorInfo& aInfo) {
// irrelevant and not propagated.
proto->mIncludeChrome = false;
proto->mAllFrames = aInfo.allFrames();
proto->mMatches = aInfo.matches();
proto->mChild.mModuleURI.Assign(aInfo.url());
proto->mChild.mEvents.SetCapacity(aInfo.events().Length());
@ -177,6 +182,7 @@ JSWindowActorInfo JSWindowActorProtocol::ToIPC() {
JSWindowActorInfo info;
info.name() = mName;
info.allFrames() = mAllFrames;
info.matches() = mMatches;
info.url() = mChild.mModuleURI;
info.events().SetCapacity(mChild.mEvents.Length());
@ -205,6 +211,11 @@ JSWindowActorProtocol::FromWebIDLOptions(const nsAString& aName,
proto->mAllFrames = aOptions.mAllFrames;
proto->mIncludeChrome = aOptions.mIncludeChrome;
if (aOptions.mMatches.WasPassed()) {
MOZ_ASSERT(aOptions.mMatches.Value().Length());
proto->mMatches = aOptions.mMatches.Value();
}
proto->mParent.mModuleURI = aOptions.mParent.mModuleURI;
proto->mChild.mModuleURI = aOptions.mChild.mModuleURI;
@ -302,7 +313,13 @@ NS_IMETHODIMP JSWindowActorProtocol::Observe(nsISupports* aSubject,
ErrorResult error;
RefPtr<JSWindowActorChild> actor = wgc->GetActor(mName, error);
if (NS_WARN_IF(error.Failed())) {
return error.StealNSResult();
nsresult rv = error.StealNSResult();
// Don't raise an error if creation of our actor was vetoed.
if (rv == NS_ERROR_NOT_AVAILABLE) {
return NS_OK;
}
return rv;
}
// Get the wrapper for our actor. If we don't have a wrapper, the target
@ -382,6 +399,52 @@ void JSWindowActorProtocol::RemoveObservers() {
}
}
extensions::MatchPatternSet* JSWindowActorProtocol::GetURIMatcher() {
// If we've already created the pattern set, return it.
if (mURIMatcher || mMatches.IsEmpty()) {
return mURIMatcher;
}
// Constructing the MatchPatternSet requires a JS environment to be run in.
// We can construct it here in the JSM scope, as we will be keeping it around.
AutoJSAPI jsapi;
MOZ_ALWAYS_TRUE(jsapi.Init(xpc::PrivilegedJunkScope()));
GlobalObject global(jsapi.cx(), xpc::PrivilegedJunkScope());
nsTArray<OwningStringOrMatchPattern> patterns;
patterns.SetCapacity(mMatches.Length());
for (nsString& s : mMatches) {
auto* entry = patterns.AppendElement();
entry->SetAsString() = s;
}
MatchPatternOptions matchPatternOptions;
// Make MatchPattern's mSchemes create properly.
matchPatternOptions.mRestrictSchemes = false;
mURIMatcher = extensions::MatchPatternSet::Constructor(
global, patterns, matchPatternOptions, IgnoreErrors());
return mURIMatcher;
}
bool JSWindowActorProtocol::Matches(BrowsingContext* aBrowsingContext,
nsIURI* aURI) {
if (!mAllFrames && aBrowsingContext->GetParent()) {
return false;
}
if (!mIncludeChrome && !aBrowsingContext->IsContent()) {
return false;
}
if (extensions::MatchPatternSet* uriMatcher = GetURIMatcher()) {
if (!uriMatcher->Matches(aURI)) {
return false;
}
}
return true;
}
JSWindowActorService::JSWindowActorService() { MOZ_ASSERT(NS_IsMainThread()); }
JSWindowActorService::~JSWindowActorService() { MOZ_ASSERT(NS_IsMainThread()); }
@ -489,13 +552,12 @@ void JSWindowActorService::GetJSWindowActorInfos(
}
}
void JSWindowActorService::ConstructActor(const nsAString& aName,
bool aParentSide,
BrowsingContext* aBrowsingContext,
JS::MutableHandleObject aActor,
ErrorResult& aRv) {
void JSWindowActorService::ConstructActor(
const nsAString& aName, bool aParentSide, BrowsingContext* aBrowsingContext,
nsIURI* aURI, JS::MutableHandleObject aActor, ErrorResult& aRv) {
MOZ_ASSERT_IF(aParentSide, XRE_IsParentProcess());
MOZ_ASSERT(aBrowsingContext, "DocShell without a BrowsingContext!");
MOZ_ASSERT(aURI, "Must have URI!");
// Constructing an actor requires a running script, so push an AutoEntryScript
// onto the stack.
AutoEntryScript aes(xpc::PrivilegedJunkScope(), "JSWindowActor construction");
@ -515,14 +577,9 @@ void JSWindowActorService::ConstructActor(const nsAString& aName,
side = &proto->Child();
}
// Check if our current BrowsingContext matches the requirements for this
// actor to load.
if (!proto->AllFrames() && aBrowsingContext->GetParent()) {
aRv.Throw(NS_ERROR_NOT_AVAILABLE);
return;
}
if (!proto->IncludeChrome() && !aBrowsingContext->IsContent()) {
// Check if our current BrowsingContext and URI matches the requirements for
// this actor to load.
if (!proto->Matches(aBrowsingContext, aURI)) {
aRv.Throw(NS_ERROR_NOT_AVAILABLE);
return;
}

View File

@ -40,7 +40,7 @@ class JSWindowActorService final {
// This method will not initialize the actor or set its manager,
// which is handled by callers.
void ConstructActor(const nsAString& aName, bool aParentSide,
BrowsingContext* aBrowsingContext,
BrowsingContext* aBrowsingContext, nsIURI* aURI,
JS::MutableHandleObject aActor, ErrorResult& aRv);
void ReceiveMessage(nsISupports* aActor, JS::RootedObject& aObj,

View File

@ -242,6 +242,7 @@ struct JSWindowActorInfo
JSWindowActorEventDecl[] events;
nsCString[] observers;
nsString[] matches;
};
struct GMPAPITags

View File

@ -70,6 +70,8 @@ already_AddRefed<WindowGlobalChild> WindowGlobalChild::Create(
MOZ_RELEASE_ASSERT(!entry, "Duplicate WindowGlobalChild entry for ID!");
entry.OrInsert([&] { return wgc; });
// Send down our initial document URI.
wgc->SendUpdateDocumentURI(aWindow->GetDocumentURI());
return wgc.forget();
}
@ -167,7 +169,7 @@ already_AddRefed<JSWindowActorChild> WindowGlobalChild::GetActor(
JS::RootedObject obj(RootingCx());
actorSvc->ConstructActor(aName, /* aChildSide */ false, mBrowsingContext,
&obj, aRv);
mWindowGlobal->GetDocumentURI(), &obj, aRv);
if (aRv.Failed()) {
return nullptr;
}

View File

@ -210,7 +210,7 @@ already_AddRefed<JSWindowActorParent> WindowGlobalParent::GetActor(
JS::RootedObject obj(RootingCx());
actorSvc->ConstructActor(aName, /* aParentSide */ true, mBrowsingContext,
&obj, aRv);
mDocumentURI, &obj, aRv);
if (aRv.Failed()) {
return nullptr;
}

View File

@ -3,6 +3,7 @@
"use strict";
const URL = "about:blank";
const TEST_URL = "http://test2.example.org/";
let windowActorOptions = {
parent: {
moduleURI: "resource://testing-common/TestParent.jsm",
@ -20,6 +21,12 @@ let windowActorOptions = {
},
};
function teardown() {
windowActorOptions.allFrames = false;
delete windowActorOptions.matches;
ChromeUtils.unregisterWindowActor("Test");
}
add_task(function test_registerWindowActor() {
ok(ChromeUtils, "Should be able to get the ChromeUtils interface");
ChromeUtils.registerWindowActor("Test", windowActorOptions);
@ -52,63 +59,6 @@ add_task(async function test_getActor() {
});
});
add_task(async function test_getActor_without_allFrames() {
windowActorOptions.allFrames = false;
await BrowserTestUtils.withNewTab({gBrowser, url: URL},
async function(browser) {
ChromeUtils.registerWindowActor("Test", windowActorOptions);
await ContentTask.spawn(
browser, {}, async function() {
// Create and append an iframe into the window's document.
let frame = content.document.createElement("iframe");
content.document.body.appendChild(frame);
is(content.window.frames.length, 1, "There should be an iframe.");
let child = frame.contentWindow.window.getWindowGlobalChild();
Assert.throws(() => child.getActor("Test"),
/NS_ERROR_NOT_AVAILABLE/, "Should throw if allFrames is false.");
});
ChromeUtils.unregisterWindowActor("Test");
});
});
add_task(async function test_getActor_with_allFrames() {
windowActorOptions.allFrames = true;
ChromeUtils.registerWindowActor("Test", windowActorOptions);
await BrowserTestUtils.withNewTab({gBrowser, url: URL},
async function(browser) {
await ContentTask.spawn(
browser, {}, async function() {
// Create and append an iframe into the window's document.
let frame = content.document.createElement("iframe");
content.document.body.appendChild(frame);
is(content.window.frames.length, 1, "There should be an iframe.");
let child = frame.contentWindow.window.getWindowGlobalChild();
let actorChild = child.getActor("Test");
ok(actorChild, "JSWindowActorChild should have value.");
});
});
ChromeUtils.unregisterWindowActor("Test");
});
add_task(async function test_getActor_without_includeChrome() {
windowActorOptions.includeChrome = false;
let parent = window.docShell.browsingContext.currentWindowGlobal;
ChromeUtils.registerWindowActor("Test", windowActorOptions);
SimpleTest.doesThrow(() =>
parent.getActor("Test"),
"Should throw if includeChrome is false.");
ChromeUtils.unregisterWindowActor("Test");
});
add_task(async function test_getActor_with_includeChrome() {
windowActorOptions.includeChrome = true;
ChromeUtils.registerWindowActor("Test", windowActorOptions);
let parent = window.docShell.browsingContext.currentWindowGlobal;
let actorParent = parent.getActor("Test");
ok(actorParent, "JSWindowActorParent should have value.");
ChromeUtils.unregisterWindowActor("Test");
});
add_task(async function test_asyncMessage() {
await BrowserTestUtils.withNewTab({gBrowser, url: URL},
async function(browser) {
@ -257,3 +207,150 @@ add_task(async function test_observers_dont_notify_with_wrong_window() {
});
ChromeUtils.unregisterWindowActor("Test");
});
add_task(async function test_getActor_with_mismatch() {
await BrowserTestUtils.withNewTab({gBrowser, url: URL},
async function(browser) {
windowActorOptions.matches = ["*://*/*"];
ChromeUtils.registerWindowActor("Test", windowActorOptions);
let parent = browser.browsingContext.currentWindowGlobal;
ok(parent, "WindowGlobalParent should have value.");
Assert.throws(() => parent.getActor("Test"),
/NS_ERROR_NOT_AVAILABLE/, "Should throw if it doesn't match.");
await ContentTask.spawn(
browser, {}, async function() {
let child = content.window.getWindowGlobalChild();
ok(child, "WindowGlobalChild should have value.");
Assert.throws(() => child.getActor("Test"),
/NS_ERROR_NOT_AVAILABLE/, "Should throw if it doesn't match.");
});
teardown();
});
});
add_task(async function test_getActor_with_matches() {
await BrowserTestUtils.withNewTab({gBrowser, url: TEST_URL},
async function(browser) {
windowActorOptions.matches = ["*://*/*"];
ChromeUtils.registerWindowActor("Test", windowActorOptions);
let parent = browser.browsingContext.currentWindowGlobal;
ok(parent.getActor("Test"), "JSWindowActorParent should have value.");
await ContentTask.spawn(
browser, {}, async function() {
let child = content.window.getWindowGlobalChild();
ok(child, "WindowGlobalChild should have value.");
ok(child.getActor("Test"), "JSWindowActorChild should have value.");
});
teardown();
});
});
add_task(async function test_getActor_with_iframe_matches() {
await BrowserTestUtils.withNewTab({gBrowser, url: URL},
async function(browser) {
windowActorOptions.allFrames = true;
windowActorOptions.matches = ["*://*/*"];
ChromeUtils.registerWindowActor("Test", windowActorOptions);
await ContentTask.spawn(
browser, TEST_URL, async function(url) {
// Create and append an iframe into the window's document.
let frame = content.document.createElement("iframe");
frame.src = url;
content.document.body.appendChild(frame);
await ContentTaskUtils.waitForEvent(frame, "load");
is(content.window.frames.length, 1, "There should be an iframe.");
let child = frame.contentWindow.window.getWindowGlobalChild();
ok(child.getActor("Test"), "JSWindowActorChild should have value.");
});
teardown();
});
});
add_task(async function test_getActor_with_iframe_mismatch() {
await BrowserTestUtils.withNewTab({gBrowser, url: URL},
async function(browser) {
windowActorOptions.allFrames = true;
windowActorOptions.matches = ["about:home"];
ChromeUtils.registerWindowActor("Test", windowActorOptions);
await ContentTask.spawn(
browser, TEST_URL, async function(url) {
// Create and append an iframe into the window's document.
let frame = content.document.createElement("iframe");
frame.src = url;
content.document.body.appendChild(frame);
await ContentTaskUtils.waitForEvent(frame, "load");
is(content.window.frames.length, 1, "There should be an iframe.");
let child = frame.contentWindow.window.getWindowGlobalChild();
Assert.throws(() => child.getActor("Test"),
/NS_ERROR_NOT_AVAILABLE/, "Should throw if it doesn't match.");
});
teardown();
});
});
add_task(async function test_getActor_without_allFrames() {
await BrowserTestUtils.withNewTab({gBrowser, url: URL},
async function(browser) {
windowActorOptions.allFrames = false;
ChromeUtils.registerWindowActor("Test", windowActorOptions);
await ContentTask.spawn(
browser, {}, async function() {
// Create and append an iframe into the window's document.
let frame = content.document.createElement("iframe");
content.document.body.appendChild(frame);
is(content.window.frames.length, 1, "There should be an iframe.");
let child = frame.contentWindow.window.getWindowGlobalChild();
Assert.throws(() => child.getActor("Test"),
/NS_ERROR_NOT_AVAILABLE/, "Should throw if allFrames is false.");
});
ChromeUtils.unregisterWindowActor("Test");
});
});
add_task(async function test_getActor_with_allFrames() {
await BrowserTestUtils.withNewTab({gBrowser, url: URL},
async function(browser) {
windowActorOptions.allFrames = true;
ChromeUtils.registerWindowActor("Test", windowActorOptions);
await ContentTask.spawn(
browser, {}, async function() {
// Create and append an iframe into the window's document.
let frame = content.document.createElement("iframe");
content.document.body.appendChild(frame);
is(content.window.frames.length, 1, "There should be an iframe.");
let child = frame.contentWindow.window.getWindowGlobalChild();
let actorChild = child.getActor("Test");
ok(actorChild, "JSWindowActorChild should have value.");
});
ChromeUtils.unregisterWindowActor("Test");
});
});
add_task(async function test_getActor_without_includeChrome() {
windowActorOptions.includeChrome = false;
let parent = window.docShell.browsingContext.currentWindowGlobal;
ChromeUtils.registerWindowActor("Test", windowActorOptions);
SimpleTest.doesThrow(() =>
parent.getActor("Test"),
"Should throw if includeChrome is false.");
ChromeUtils.unregisterWindowActor("Test");
});
add_task(async function test_getActor_with_includeChrome() {
windowActorOptions.includeChrome = true;
ChromeUtils.registerWindowActor("Test", windowActorOptions);
let parent = window.docShell.browsingContext.currentWindowGlobal;
let actorParent = parent.getActor("Test");
ok(actorParent, "JSWindowActorParent should have value.");
ChromeUtils.unregisterWindowActor("Test");
});

View File

@ -32,7 +32,7 @@ function nativeVerticalWheelEventMsg() {
case "mac": return 0; // value is unused, can be anything
case "linux": return 4; // value is unused, pass GDK_SCROLL_SMOOTH anyway
}
throw "Native wheel events not supported on platform " + getPlatform();
throw new Error("Native wheel events not supported on platform " + getPlatform());
}
function nativeHorizontalWheelEventMsg() {
@ -41,7 +41,7 @@ function nativeHorizontalWheelEventMsg() {
case "mac": return 0; // value is unused, can be anything
case "linux": return 4; // value is unused, pass GDK_SCROLL_SMOOTH anyway
}
throw "Native wheel events not supported on platform " + getPlatform();
throw new Error("Native wheel events not supported on platform " + getPlatform());
}
// Given an event target which may be a window or an element, get the associated window.
@ -81,7 +81,7 @@ function nativeMouseDownEventMsg() {
case "linux": return 4; // GDK_BUTTON_PRESS
case "android": return 5; // ACTION_POINTER_DOWN
}
throw "Native mouse-down events not supported on platform " + getPlatform();
throw new Error("Native mouse-down events not supported on platform " + getPlatform());
}
function nativeMouseMoveEventMsg() {
@ -91,7 +91,7 @@ function nativeMouseMoveEventMsg() {
case "linux": return 3; // GDK_MOTION_NOTIFY
case "android": return 7; // ACTION_HOVER_MOVE
}
throw "Native mouse-move events not supported on platform " + getPlatform();
throw new Error("Native mouse-move events not supported on platform " + getPlatform());
}
function nativeMouseUpEventMsg() {
@ -101,7 +101,7 @@ function nativeMouseUpEventMsg() {
case "linux": return 7; // GDK_BUTTON_RELEASE
case "android": return 6; // ACTION_POINTER_UP
}
throw "Native mouse-up events not supported on platform " + getPlatform();
throw new Error("Native mouse-up events not supported on platform " + getPlatform());
}
function getBoundingClientRectRelativeToVisualViewport(aElement) {
@ -164,7 +164,7 @@ function rectRelativeToScreen(aElement) {
function synthesizeNativeWheel(aTarget, aX, aY, aDeltaX, aDeltaY, aObserver) {
var pt = coordinatesRelativeToScreen(aX, aY, aTarget);
if (aDeltaX && aDeltaY) {
throw "Simultaneous wheeling of horizontal and vertical is not supported on all platforms.";
throw new Error("Simultaneous wheeling of horizontal and vertical is not supported on all platforms.");
}
aDeltaX = nativeScrollUnits(aTarget, aDeltaX);
aDeltaY = nativeScrollUnits(aTarget, aDeltaY);
@ -277,7 +277,10 @@ function* synthesizeNativeTouchSequences(aTarget, aPositions, aObserver = null,
continue;
}
if (aPositions[i].length != aTouchIds.length) {
throw "aPositions[" + i + "] did not have the expected number of positions; expected " + aTouchIds.length + " touch points but found " + aPositions[i].length;
throw new Error(
`aPositions[${i}] did not have the expected number of positions; ` +
`expected ${aTouchIds.length} touch points but found ${aPositions[i].length}`
);
}
for (let j = 0; j < aTouchIds.length; j++) {
if (aPositions[i][j] != null) {
@ -286,7 +289,7 @@ function* synthesizeNativeTouchSequences(aTarget, aPositions, aObserver = null,
}
}
if (lastNonNullValue < 0) {
throw "All values in positions array were null!";
throw new Error("All values in positions array were null!");
}
// Insert a row of nulls at the end of aPositions, to ensure that all

View File

@ -162,7 +162,7 @@ function promiseApzRepaintsFlushed(aWindow = window) {
function flushApzRepaints(aCallback, aWindow = window) {
if (!aCallback) {
throw "A callback must be provided!";
throw new Error("A callback must be provided!");
}
promiseApzRepaintsFlushed(aWindow).then(aCallback);
}

View File

@ -24,3 +24,4 @@ use Mozilla's I18n APIs.
locale
dataintl
localization
l10n/l10n/index

118
js/public/ForOfIterator.h Normal file
View File

@ -0,0 +1,118 @@
/* -*- 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 http://mozilla.org/MPL/2.0/. */
/*
* A convenience class that makes it easy to perform the operations of a for-of
* loop.
*/
#ifndef js_ForOfIterator_h
#define js_ForOfIterator_h
#include "mozilla/Attributes.h" // MOZ_MUST_USE, MOZ_STACK_CLASS
#include <stdint.h> // UINT32_MAX, uint32_t
#include "jstypes.h" // JS_PUBLIC_API
#include "js/RootingAPI.h" // JS::{Handle,Rooted}
#include "js/Value.h" // JS::Value, JS::{,Mutable}Handle<JS::Value>
struct JSContext;
class JSObject;
namespace JS {
/**
* A convenience class for imitating a JS for-of loop. Typical usage:
*
* JS::ForOfIterator it(cx);
* if (!it.init(iterable)) {
* return false;
* }
* JS::Rooted<JS::Value> val(cx);
* while (true) {
* bool done;
* if (!it.next(&val, &done)) {
* return false;
* }
* if (done) {
* break;
* }
* if (!DoStuff(cx, val)) {
* return false;
* }
* }
*/
class MOZ_STACK_CLASS JS_PUBLIC_API ForOfIterator {
protected:
JSContext* cx_;
// Use the ForOfPIC on the global object (see vm/GlobalObject.h) to try to
// optimize iteration across arrays.
//
// Case 1: Regular Iteration
// iterator - pointer to the iterator object.
// nextMethod - value of |iterator|.next.
// index - fixed to NOT_ARRAY (== UINT32_MAX)
//
// Case 2: Optimized Array Iteration
// iterator - pointer to the array object.
// nextMethod - the undefined value.
// index - current position in array.
//
// The cases are distinguished by whether |index == NOT_ARRAY|.
Rooted<JSObject*> iterator;
Rooted<Value> nextMethod;
static constexpr uint32_t NOT_ARRAY = UINT32_MAX;
uint32_t index = NOT_ARRAY;
ForOfIterator(const ForOfIterator&) = delete;
ForOfIterator& operator=(const ForOfIterator&) = delete;
public:
explicit ForOfIterator(JSContext* cx)
: cx_(cx), iterator(cx), nextMethod(cx) {}
enum NonIterableBehavior { ThrowOnNonIterable, AllowNonIterable };
/**
* Initialize the iterator. If AllowNonIterable is passed then if getting
* the @@iterator property from iterable returns undefined init() will just
* return true instead of throwing. Callers must then check
* valueIsIterable() before continuing with the iteration.
*/
MOZ_MUST_USE bool init(
Handle<Value> iterable,
NonIterableBehavior nonIterableBehavior = ThrowOnNonIterable);
/**
* Get the next value from the iterator. If false *done is true
* after this call, do not examine val.
*/
MOZ_MUST_USE bool next(MutableHandle<Value> val, bool* done);
/**
* Close the iterator.
* For the case that completion type is throw.
*/
void closeThrow();
/**
* If initialized with throwOnNonCallable = false, check whether
* the value is iterable.
*/
bool valueIsIterable() const { return iterator; }
private:
inline bool nextFromOptimizedArray(MutableHandle<Value> val, bool* done);
};
} // namespace JS
#endif // js_ForOfIterator_h

View File

@ -19,6 +19,7 @@ typedef uint32_t HashNumber;
#include "js/ContextOptions.h"
#include "js/Conversions.h"
#include "js/Date.h"
#include "js/ForOfIterator.h"
#include "js/Initialization.h"
#include "js/MemoryMetrics.h"
#include "js/PropertySpec.h"

View File

@ -17,6 +17,7 @@
#include "gc/Heap.h"
#include "js/Debug.h"
#include "js/ForOfIterator.h" // JS::ForOfIterator
#include "js/PropertySpec.h"
#include "vm/AsyncFunction.h"
#include "vm/AsyncIteration.h"

View File

@ -693,21 +693,6 @@ struct GlobalAccess {
typedef Vector<GlobalAccess, 0, SystemAllocPolicy> GlobalAccessVector;
// A CallFarJump records the offset of a jump that needs to be patched to a
// call at the end of the module when all calls have been emitted.
struct CallFarJump {
uint32_t funcIndex;
jit::CodeOffset jump;
CallFarJump(uint32_t funcIndex, jit::CodeOffset jump)
: funcIndex(funcIndex), jump(jump) {}
void offsetBy(size_t delta) { jump.offsetBy(delta); }
};
typedef Vector<CallFarJump, 0, SystemAllocPolicy> CallFarJumpVector;
} // namespace wasm
namespace jit {
@ -717,7 +702,6 @@ class AssemblerShared {
wasm::CallSiteVector callSites_;
wasm::CallSiteTargetVector callSiteTargets_;
wasm::TrapSiteVectorArray trapSites_;
wasm::CallFarJumpVector callFarJumps_;
wasm::SymbolicAccessVector symbolicAccesses_;
protected:
@ -756,9 +740,6 @@ class AssemblerShared {
void append(wasm::Trap trap, wasm::TrapSite site) {
enoughMemory_ &= trapSites_[trap].append(site);
}
void append(wasm::CallFarJump jmp) {
enoughMemory_ &= callFarJumps_.append(jmp);
}
void append(const wasm::MemoryAccessDesc& access, uint32_t pcOffset) {
appendOutOfBoundsTrap(access.trapOffset(), pcOffset);
}
@ -773,7 +754,6 @@ class AssemblerShared {
wasm::CallSiteVector& callSites() { return callSites_; }
wasm::CallSiteTargetVector& callSiteTargets() { return callSiteTargets_; }
wasm::TrapSiteVectorArray& trapSites() { return trapSites_; }
wasm::CallFarJumpVector& callFarJumps() { return callFarJumps_; }
wasm::SymbolicAccessVector& symbolicAccesses() { return symbolicAccesses_; }
};

View File

@ -5,6 +5,7 @@
* 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/. */
#include "js/ForOfIterator.h"
#include "jsapi-tests/tests.h"
BEGIN_TEST(testForOfIterator_basicNonIterable) {

View File

@ -3165,93 +3165,6 @@ extern JS_PUBLIC_API RefPtr<WasmModule> GetWasmModule(HandleObject obj);
extern JS_PUBLIC_API RefPtr<WasmModule> DeserializeWasmModule(
PRFileDesc* bytecode, JS::UniqueChars filename, unsigned line);
/**
* Convenience class for imitating a JS level for-of loop. Typical usage:
*
* ForOfIterator it(cx);
* if (!it.init(iterable)) {
* return false;
* }
* RootedValue val(cx);
* while (true) {
* bool done;
* if (!it.next(&val, &done)) {
* return false;
* }
* if (done) {
* break;
* }
* if (!DoStuff(cx, val)) {
* return false;
* }
* }
*/
class MOZ_STACK_CLASS JS_PUBLIC_API ForOfIterator {
protected:
JSContext* cx_;
/*
* Use the ForOfPIC on the global object (see vm/GlobalObject.h) to try
* to optimize iteration across arrays.
*
* Case 1: Regular Iteration
* iterator - pointer to the iterator object.
* nextMethod - value of |iterator|.next.
* index - fixed to NOT_ARRAY (== UINT32_MAX)
*
* Case 2: Optimized Array Iteration
* iterator - pointer to the array object.
* nextMethod - the undefined value.
* index - current position in array.
*
* The cases are distinguished by whether or not |index| is equal to
* NOT_ARRAY.
*/
JS::RootedObject iterator;
JS::RootedValue nextMethod;
uint32_t index;
static const uint32_t NOT_ARRAY = UINT32_MAX;
ForOfIterator(const ForOfIterator&) = delete;
ForOfIterator& operator=(const ForOfIterator&) = delete;
public:
explicit ForOfIterator(JSContext* cx)
: cx_(cx), iterator(cx_), nextMethod(cx), index(NOT_ARRAY) {}
enum NonIterableBehavior { ThrowOnNonIterable, AllowNonIterable };
/**
* Initialize the iterator. If AllowNonIterable is passed then if getting
* the @@iterator property from iterable returns undefined init() will just
* return true instead of throwing. Callers must then check
* valueIsIterable() before continuing with the iteration.
*/
bool init(JS::HandleValue iterable,
NonIterableBehavior nonIterableBehavior = ThrowOnNonIterable);
/**
* Get the next value from the iterator. If false *done is true
* after this call, do not examine val.
*/
bool next(JS::MutableHandleValue val, bool* done);
/**
* Close the iterator.
* For the case that completion type is throw.
*/
void closeThrow();
/**
* If initialized with throwOnNonCallable = false, check whether
* the value is iterable.
*/
bool valueIsIterable() const { return iterator; }
private:
inline bool nextFromOptimizedArray(MutableHandleValue val, bool* done);
};
/**
* If a large allocation fails when calling pod_{calloc,realloc}CanGC, the JS
* engine may call the large-allocation-failure callback, if set, to allow the

View File

@ -131,6 +131,7 @@ EXPORTS.js += [
'../public/Debug.h',
'../public/Equality.h',
'../public/ErrorReport.h',
'../public/ForOfIterator.h',
'../public/GCAnnotations.h',
'../public/GCAPI.h',
'../public/GCHashTable.h',

View File

@ -4,8 +4,7 @@
* 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/. */
#include "jsapi.h"
#include "js/ForOfIterator.h"
#include "vm/Interpreter.h"
#include "vm/JSContext.h"
#include "vm/JSObject.h"

View File

@ -1149,9 +1149,7 @@ bool wasm::EnsureBuiltinThunksInitialized() {
MOZ_ASSERT(masm.callSites().empty());
MOZ_ASSERT(masm.callSiteTargets().empty());
MOZ_ASSERT(masm.callFarJumps().empty());
MOZ_ASSERT(masm.trapSites().empty());
MOZ_ASSERT(masm.callFarJumps().empty());
ExecutableAllocator::cacheFlush(thunks->codeBase, thunks->codeSize);
if (!ExecutableAllocator::makeExecutable(thunks->codeBase,

View File

@ -685,9 +685,7 @@ bool LazyStubTier::createMany(const Uint32Vector& funcExportIndices,
MOZ_ASSERT(masm.callSites().empty());
MOZ_ASSERT(masm.callSiteTargets().empty());
MOZ_ASSERT(masm.callFarJumps().empty());
MOZ_ASSERT(masm.trapSites().empty());
MOZ_ASSERT(masm.callFarJumps().empty());
if (masm.oom()) {
return false;

View File

@ -52,7 +52,6 @@ bool CompiledCode::swap(MacroAssembler& masm) {
callSites.swap(masm.callSites());
callSiteTargets.swap(masm.callSiteTargets());
trapSites.swap(masm.trapSites());
callFarJumps.swap(masm.callFarJumps());
symbolicAccesses.swap(masm.symbolicAccesses());
codeLabels.swap(masm.codeLabels());
return true;
@ -668,13 +667,6 @@ bool ModuleGenerator::linkCompiledCode(CompiledCode& code) {
}
}
auto callFarJumpOp = [=](uint32_t, CallFarJump* cfj) {
cfj->offsetBy(offsetInModule);
};
if (!AppendForEach(&callFarJumps_, code.callFarJumps, callFarJumpOp)) {
return false;
}
for (const SymbolicAccess& access : code.symbolicAccesses) {
uint32_t patchAt = offsetInModule + access.patchAt.offset();
if (!linkData_->symbolicLinks[access.target].append(patchAt)) {
@ -923,7 +915,6 @@ bool ModuleGenerator::finishCodegen() {
MOZ_ASSERT(masm_.callSites().empty());
MOZ_ASSERT(masm_.callSiteTargets().empty());
MOZ_ASSERT(masm_.trapSites().empty());
MOZ_ASSERT(masm_.callFarJumps().empty());
MOZ_ASSERT(masm_.symbolicAccesses().empty());
MOZ_ASSERT(masm_.codeLabels().empty());
@ -1262,7 +1253,6 @@ size_t CompiledCode::sizeOfExcludingThis(
codeRanges.sizeOfExcludingThis(mallocSizeOf) +
callSites.sizeOfExcludingThis(mallocSizeOf) +
callSiteTargets.sizeOfExcludingThis(mallocSizeOf) + trapSitesSize +
callFarJumps.sizeOfExcludingThis(mallocSizeOf) +
symbolicAccesses.sizeOfExcludingThis(mallocSizeOf) +
codeLabels.sizeOfExcludingThis(mallocSizeOf);
}

View File

@ -62,7 +62,6 @@ struct CompiledCode {
CallSiteVector callSites;
CallSiteTargetVector callSiteTargets;
TrapSiteVectorArray trapSites;
CallFarJumpVector callFarJumps;
SymbolicAccessVector symbolicAccesses;
jit::CodeLabelVector codeLabels;
StackMaps stackMaps;
@ -75,7 +74,6 @@ struct CompiledCode {
callSites.clear();
callSiteTargets.clear();
trapSites.clear();
callFarJumps.clear();
symbolicAccesses.clear();
codeLabels.clear();
stackMaps.clear();
@ -85,8 +83,7 @@ struct CompiledCode {
bool empty() {
return bytes.empty() && codeRanges.empty() && callSites.empty() &&
callSiteTargets.empty() && trapSites.empty() &&
callFarJumps.empty() && symbolicAccesses.empty() &&
codeLabels.empty() && stackMaps.empty();
symbolicAccesses.empty() && codeLabels.empty() && stackMaps.empty();
}
size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
@ -137,6 +134,12 @@ struct CompileTask {
class MOZ_STACK_CLASS ModuleGenerator {
typedef Vector<CompileTask, 0, SystemAllocPolicy> CompileTaskVector;
typedef Vector<jit::CodeOffset, 0, SystemAllocPolicy> CodeOffsetVector;
struct CallFarJump {
uint32_t funcIndex;
jit::CodeOffset jump;
CallFarJump(uint32_t fi, jit::CodeOffset j) : funcIndex(fi), jump(j) {}
};
typedef Vector<CallFarJump, 0, SystemAllocPolicy> CallFarJumpVector;
// Constant parameters
SharedCompileArgs const compileArgs_;

View File

@ -5528,6 +5528,26 @@ TEST_F(JsepSessionTest, AudioCallAnswererUsesActpass) {
ASSERT_EQ(kJsepStateHaveLocalOffer, mSessionOff->GetState());
}
// Verify that 'actpass' in reoffer from previous answerer doesn't result
// in a role switch.
TEST_F(JsepSessionTest, AudioCallPreviousAnswererUsesActpassInReoffer) {
types.push_back(SdpMediaSection::kAudio);
AddTracks(*mSessionOff, "audio");
AddTracks(*mSessionAns, "audio");
OfferAnswer();
ValidateSetupAttribute(*mSessionOff, SdpSetupAttribute::kActpass);
ValidateSetupAttribute(*mSessionAns, SdpSetupAttribute::kActive);
SwapOfferAnswerRoles();
OfferAnswer();
ValidateSetupAttribute(*mSessionOff, SdpSetupAttribute::kActpass);
ValidateSetupAttribute(*mSessionAns, SdpSetupAttribute::kPassive);
}
// Disabled: See Bug 1329028
TEST_F(JsepSessionTest, DISABLED_AudioCallOffererAttemptsSetupRoleSwitch) {
types.push_back(SdpMediaSection::kAudio);

View File

@ -546,8 +546,15 @@ nsresult JsepSessionImpl::CreateAnswerMsection(
MOZ_ASSERT(transceiver.GetMid() == msection.GetAttributeList().GetMid());
SdpSetupAttribute::Role role;
rv = DetermineAnswererSetupRole(remoteMsection, &role);
NS_ENSURE_SUCCESS(rv, rv);
if (transceiver.mTransport.mDtls && !IsIceRestarting()) {
role = (transceiver.mTransport.mDtls->mRole ==
JsepDtlsTransport::kJsepDtlsClient)
? SdpSetupAttribute::kActive
: SdpSetupAttribute::kPassive;
} else {
rv = DetermineAnswererSetupRole(remoteMsection, &role);
NS_ENSURE_SUCCESS(rv, rv);
}
rv = AddTransportAttributes(&msection, role);
NS_ENSURE_SUCCESS(rv, rv);
@ -1072,6 +1079,7 @@ nsresult JsepSessionImpl::FinalizeTransport(const SdpAttributeList& remote,
if (!transport->mIce || transport->mIce->mUfrag != remote.GetIceUfrag() ||
transport->mIce->mPwd != remote.GetIcePwd()) {
UniquePtr<JsepIceTransport> ice = MakeUnique<JsepIceTransport>();
transport->mDtls = nullptr;
// We do sanity-checking for these in ParseSdp
ice->mUfrag = remote.GetIceUfrag();

View File

@ -48,7 +48,7 @@ LoginManagerPrompter.prototype = {
};
if (!this.__strBundle)
throw "String bundle for Login Manager not present!";
throw new Error("String bundle for Login Manager not present!");
}
return this.__strBundle;

View File

@ -109,7 +109,7 @@ NSSDialogs.prototype = {
setPKCS12FilePassword: function(aCtx, aPassword) {
// this dialog is never shown in Fennec; in Desktop it is shown while backing up a personal
// certificate to a file via Preferences->Advanced->Encryption->View Certificates->Your Certificates
throw "Unimplemented";
throw new Error("Unimplemented");
},
getPKCS12FilePassword: function(aCtx, aPassword) {

View File

@ -719,11 +719,11 @@ var PromptUtils = {
// channel's actual destination.
if (aAuthInfo.flags & Ci.nsIAuthInformation.AUTH_PROXY) {
if (!(aChannel instanceof Ci.nsIProxiedChannel))
throw "proxy auth needs nsIProxiedChannel";
throw new Error("proxy auth needs nsIProxiedChannel");
let info = aChannel.proxyInfo;
if (!info)
throw "proxy auth needs nsIProxyInfo";
throw new Error("proxy auth needs nsIProxyInfo");
// Proxies don't have a scheme, but we'll use "moz-proxy://"
// so that it's more obvious what the login is for.

View File

@ -1422,12 +1422,12 @@ SessionStore.prototype = {
try {
state = JSON.parse(aData);
} catch (e) {
throw "Invalid session JSON: " + aData;
throw new Error("Invalid session JSON: " + aData);
}
// To do a restore, we must have at least one window with one tab
if (!state || state.windows.length == 0 || !state.windows[0].tabs || state.windows[0].tabs.length == 0) {
throw "Invalid session JSON: " + aData;
throw new Error("Invalid session JSON: " + aData);
}
let window = Services.wm.getMostRecentWindow("navigator:browser");
@ -1491,7 +1491,7 @@ SessionStore.prototype = {
getClosedTabs(aWindow) {
if (!aWindow.__SSID) {
throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
throw new Error(Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
}
return this._windows[aWindow.__SSID].closedTabs;
@ -1499,7 +1499,7 @@ SessionStore.prototype = {
undoCloseTab(aWindow, aCloseTabData) {
if (!aWindow.__SSID) {
throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
throw new Error(Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
}
// If the tab data is in the closedTabs array, remove it.
@ -1562,7 +1562,7 @@ SessionStore.prototype = {
}
if (!aWindow.__SSID) {
throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
throw new Error(Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
}
let closedTabs = this._windows[aWindow.__SSID].closedTabs;

View File

@ -118,9 +118,9 @@ GeckoViewPermission.prototype = {
});
if (constraints.video && !sources.some(source => source.type === "videoinput")) {
throw "no video source";
throw new Error("no video source");
} else if (constraints.audio && !sources.some(source => source.type === "audioinput")) {
throw "no audio source";
throw new Error("no audio source");
}
let dispatcher = GeckoViewUtils.getDispatcherForWindow(win);

View File

@ -760,11 +760,11 @@ PromptDelegate.prototype = {
// channel's actual destination.
if (aAuthInfo.flags & Ci.nsIAuthInformation.AUTH_PROXY) {
if (!(aChannel instanceof Ci.nsIProxiedChannel)) {
throw "proxy auth needs nsIProxiedChannel";
throw new Error("proxy auth needs nsIProxiedChannel");
}
let info = aChannel.proxyInfo;
if (!info) {
throw "proxy auth needs nsIProxyInfo";
throw new Error("proxy auth needs nsIProxyInfo");
}
// Proxies don't have a scheme, but we'll use "moz-proxy://"
// so that it's more obvious what the login is for.

View File

@ -132,7 +132,7 @@ var DownloadNotifications = {
}
}
throw "Couldn't find download for " + cookie;
throw new Error("Couldn't find download for " + cookie);
});
},

View File

@ -20,7 +20,7 @@ const DEFAULT_WEIGHT = 100;
// See bug 915424
function resolveGeckoURI(aURI) {
if (!aURI)
throw "Can't resolve an empty uri";
throw new Error("Can't resolve an empty uri");
if (aURI.startsWith("chrome://")) {
let registry = Cc["@mozilla.org/chrome/chrome-registry;1"].getService(Ci.nsIChromeRegistry);
@ -168,7 +168,7 @@ var HomeBanner = (function() {
*/
remove: function(id) {
if (!(id in _messages)) {
throw "Home.banner: Can't remove message that doesn't exist: id = " + id;
throw new Error("Home.banner: Can't remove message that doesn't exist: id = " + id);
}
delete _messages[id];
@ -220,10 +220,10 @@ var HomePanels = (function() {
let id = data.id;
let options = _registeredPanels[id]();
if (!options.auth) {
throw "Home.panels: Invalid auth for panel.id = " + id;
throw new Error("Home.panels: Invalid auth for panel.id = " + id);
}
if (!options.auth.authenticate || typeof options.auth.authenticate !== "function") {
throw "Home.panels: Invalid auth authenticate function: panel.id = " + this.id;
throw new Error("Home.panels: Invalid auth authenticate function: panel.id = " + this.id);
}
options.auth.authenticate();
},
@ -233,13 +233,13 @@ var HomePanels = (function() {
let view = options.views[data.viewIndex];
if (!view) {
throw "Home.panels: Invalid view for panel.id = " + data.panelId
+ ", view.index = " + data.viewIndex;
throw new Error("Home.panels: Invalid view for panel.id = " +
`${data.panelId}, view.index = ${data.viewIndex}`);
}
if (!view.onrefresh || typeof view.onrefresh !== "function") {
throw "Home.panels: Invalid onrefresh for panel.id = " + data.panelId
+ ", view.index = " + data.viewIndex;
throw new Error("Home.panels: Invalid onrefresh for panel.id = " +
`${data.panelId}, view.index = ${data.viewIndex}`);
}
view.onrefresh();
@ -254,7 +254,7 @@ var HomePanels = (function() {
return;
}
if (typeof options.oninstall !== "function") {
throw "Home.panels: Invalid oninstall function: panel.id = " + this.id;
throw new Error("Home.panels: Invalid oninstall function: panel.id = " + this.id);
}
options.oninstall();
},
@ -268,7 +268,7 @@ var HomePanels = (function() {
return;
}
if (typeof options.onuninstall !== "function") {
throw "Home.panels: Invalid onuninstall function: panel.id = " + this.id;
throw new Error("Home.panels: Invalid onuninstall function: panel.id = " + this.id);
}
options.onuninstall();
},
@ -314,19 +314,21 @@ var HomePanels = (function() {
this.default = !!options.default;
if (!this.id || !this.title) {
throw "Home.panels: Can't create a home panel without an id and title!";
throw new Error("Home.panels: Can't create a home panel without an id and title!");
}
if (!this.layout) {
// Use FRAME layout by default
this.layout = Layout.FRAME;
} else if (!_valueExists(Layout, this.layout)) {
throw "Home.panels: Invalid layout for panel: panel.id = " + this.id + ", panel.layout =" + this.layout;
throw new Error("Home.panels: Invalid layout for panel: panel.id = " +
`${this.id}, panel.layout =${this.layout}`);
}
for (let view of this.views) {
if (!_valueExists(View, view.type)) {
throw "Home.panels: Invalid view type: panel.id = " + this.id + ", view.type = " + view.type;
throw new Error("Home.panels: Invalid view type: panel.id = " +
`${this.id}, view.type = ${view.type}`);
}
if (!view.itemType) {
@ -338,18 +340,21 @@ var HomePanels = (function() {
view.itemType = Item.IMAGE;
}
} else if (!_valueExists(Item, view.itemType)) {
throw "Home.panels: Invalid item type: panel.id = " + this.id + ", view.itemType = " + view.itemType;
throw new Error("Home.panels: Invalid item type: panel.id = " +
`${this.id}, view.itemType = ${view.itemType}`);
}
if (!view.itemHandler) {
// Use BROWSER item handler by default
view.itemHandler = ItemHandler.BROWSER;
} else if (!_valueExists(ItemHandler, view.itemHandler)) {
throw "Home.panels: Invalid item handler: panel.id = " + this.id + ", view.itemHandler = " + view.itemHandler;
throw new Error("Home.panels: Invalid item handler: panel.id = " +
`${this.id}, view.itemHandler = ${view.itemHandler}`);
}
if (!view.dataset) {
throw "Home.panels: No dataset provided for view: panel.id = " + this.id + ", view.type = " + view.type;
throw new Error("Home.panels: No dataset provided for view: panel.id = " +
`${this.id}, view.type = ${view.type}`);
}
if (view.onrefresh) {
@ -359,10 +364,10 @@ var HomePanels = (function() {
if (options.auth) {
if (!options.auth.messageText) {
throw "Home.panels: Invalid auth messageText: panel.id = " + this.id;
throw new Error("Home.panels: Invalid auth messageText: panel.id = " + this.id);
}
if (!options.auth.buttonText) {
throw "Home.panels: Invalid auth buttonText: panel.id = " + this.id;
throw new Error("Home.panels: Invalid auth buttonText: panel.id = " + this.id);
}
this.authConfig = {
@ -398,7 +403,7 @@ var HomePanels = (function() {
let _assertPanelExists = function(id) {
if (!(id in _registeredPanels)) {
throw "Home.panels: Panel doesn't exist: id = " + id;
throw new Error("Home.panels: Panel doesn't exist: id = " + id);
}
};
@ -411,11 +416,11 @@ var HomePanels = (function() {
register: function(id, optionsCallback) {
// Bail if the panel already exists
if (id in _registeredPanels) {
throw "Home.panels: Panel already exists: id = " + id;
throw new Error("Home.panels: Panel already exists: id = " + id);
}
if (!optionsCallback || typeof optionsCallback !== "function") {
throw "Home.panels: Panel callback must be a function: id = " + id;
throw new Error("Home.panels: Panel callback must be a function: id = " + id);
}
_registeredPanels[id] = optionsCallback;

View File

@ -333,8 +333,8 @@ HomeStorage.prototype = {
*/
async save(data, options) {
if (data && data.length > MAX_SAVE_COUNT) {
throw "save failed for dataset = " + this.datasetId +
": you cannot save more than " + MAX_SAVE_COUNT + " items at once";
throw new Error(`save failed for dataset = ${this.datasetId}: ` +
`you cannot save more than ${MAX_SAVE_COUNT} items at once`);
}
let db = await getDatabaseConnection();

View File

@ -22,12 +22,12 @@ Notification.prototype = {
if ("icon" in aOptions && aOptions.icon != null)
this._icon = aOptions.icon;
else
throw "Notification icon is mandatory";
throw new Error("Notification icon is mandatory");
if ("title" in aOptions && aOptions.title != null)
this._title = aOptions.title;
else
throw "Notification title is mandatory";
throw new Error("Notification title is mandatory");
if ("message" in aOptions && aOptions.message != null)
this._message = aOptions.message;
@ -39,7 +39,7 @@ Notification.prototype = {
if ("buttons" in aOptions && aOptions.buttons != null) {
if (aOptions.buttons.length > 3)
throw "Too many buttons provided. The max number is 3";
throw new Error("Too many buttons provided. The max number is 3");
this._buttons = {};
for (let i = 0; i < aOptions.buttons.length; i++) {
@ -184,7 +184,7 @@ var Notifications = {
update: function notif_update(aId, aOptions) {
let notification = _notificationsMap[aId];
if (!notification)
throw "Unknown notification id";
throw new Error("Unknown notification id");
notification.fillWithOptions(aOptions);
notification.show();
},

View File

@ -18,7 +18,7 @@ var EXPORTED_SYMBOLS = ["PageActions"];
// TODO: We should move this method to a common importable location
function resolveGeckoURI(aURI) {
if (!aURI)
throw "Can't resolve an empty uri";
throw new Error("Can't resolve an empty uri");
if (aURI.startsWith("chrome://")) {
let registry = Cc["@mozilla.org/chrome/chrome-registry;1"].getService(Ci.nsIChromeRegistry);

View File

@ -61,7 +61,7 @@ function SharedPreferencesImpl(options = {}) {
}
if (options.scope == null || options.scope == undefined) {
throw "Shared Preferences must specifiy a scope.";
throw new Error("Shared Preferences must specifiy a scope.");
}
this._scope = options.scope;

View File

@ -116,7 +116,7 @@ var WebrtcUI = {
notificationOptions.icon = "drawable:alert_mic";
} else {
// somethings wrong. lets throw
throw "Couldn't find any cameras or microphones being used";
throw new Error("Couldn't find any cameras or microphones being used");
}
if (this._notificationId)

View File

@ -24,6 +24,34 @@ debugger-tests:
files-changed:
- 'devtools/client/debugger/new/**'
devtools-tests:
description: devtools node-based tests (for instance jest)
platform: linux64/opt
treeherder:
symbol: node(devtools)
kind: test
tier: 1
worker-type: aws-provisioner-v1/gecko-t-linux-xlarge
worker:
docker-image: {in-tree: "lint"}
max-run-time: 1800
run:
using: run-task
cache-dotcache: true
command: >
cd /builds/worker/checkouts/gecko/ &&
npm install &&
cd /builds/worker/checkouts/gecko/devtools/client/aboutdebugging-new/test/jest &&
yarn &&
yarn test &&
cd /builds/worker/checkouts/gecko/devtools/client/framework/test/jest &&
yarn &&
yarn test
when:
files-changed:
- 'devtools/client/aboutdebugging-new/src/components/**'
- 'devtools/client/framework/components/**'
eslint-plugin-mozilla:
description: eslint-plugin-mozilla integration tests
platform: linux64/opt

View File

@ -0,0 +1 @@
leak-threshold: [default: 30000]

Some files were not shown because too many files have changed in this diff Show More