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 // TODO: Bug 1246594. Empty this list once the rule has landed for all dirs
"files": [ "files": [
"gfx/layers/apz/test/mochitest/**",
"mobile/android/components/**",
"mobile/android/modules/**",
"modules/libmar/tests/unit/head_libmar.js", "modules/libmar/tests/unit/head_libmar.js",
"netwerk/protocol/http/WellKnownOpportunisticUtils.jsm", "netwerk/protocol/http/WellKnownOpportunisticUtils.jsm",
"netwerk/test/httpserver/httpd.js", "netwerk/test/httpserver/httpd.js",

View File

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

View File

@ -983,7 +983,7 @@ const menuTracker = {
gMenuBuilder.build(subject); gMenuBuilder.build(subject);
}, },
onWindowOpen(window) { async onWindowOpen(window) {
for (const id of menuTracker.menuIds) { for (const id of menuTracker.menuIds) {
const menu = window.document.getElementById(id); const menu = window.document.getElementById(id);
menu.addEventListener("popupshowing", menuTracker); menu.addEventListener("popupshowing", menuTracker);
@ -991,7 +991,10 @@ const menuTracker = {
const sidebarHeader = window.document.getElementById("sidebar-switcher-target"); const sidebarHeader = window.document.getElementById("sidebar-switcher-target");
sidebarHeader.addEventListener("SidebarShown", menuTracker.onSidebarShown); 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}); menuTracker.onSidebarShown({currentTarget: sidebarHeader});
} }
}, },

View File

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

View File

@ -4,6 +4,8 @@
const TP_PB_ENABLED_PREF = "privacy.trackingprotection.pbmode.enabled"; 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. * 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"); ok(urlBarHasNormalFocus(win), "url bar has normal focused");
is(win.gURLBar.value, "@google f", "url bar has search text"); 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 // Hitting ESC should reshow the in-content search
await new Promise(r => EventUtils.synthesizeKey("KEY_Escape", {}, win, r)); await new Promise(r => EventUtils.synthesizeKey("KEY_Escape", {}, win, r));
await ContentTask.spawn(tab, null, async function() { await ContentTask.spawn(tab, null, async function() {
ok(!content.document.getElementById("search-handoff-button").classList.contains("hidden"), 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); await BrowserTestUtils.closeWindow(win);
@ -176,6 +181,11 @@ add_task(async function test_search_handoff_on_paste() {
.getService(SpecialPowers.Ci.nsIClipboardHelper); .getService(SpecialPowers.Ci.nsIClipboardHelper);
helper.copyString("words"); helper.copyString("words");
await new Promise(r => EventUtils.synthesizeKey("v", {accelKey: true}, win, r)); 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"); ok(urlBarHasNormalFocus(win), "url bar has normal focused");
is(win.gURLBar.value, "@google words", "url bar has search text"); is(win.gURLBar.value, "@google words", "url bar has search text");

View File

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

View File

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

View File

@ -226,6 +226,17 @@ var UrlbarTestUtils = {
let urlbar = getUrlbarAbstraction(win); let urlbar = getUrlbarAbstraction(win);
return urlbar.isPopupOpen(); 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); "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) { async promiseResultAt(index) {
if (!this.quantumbar) { if (!this.quantumbar) {
// In the legacy address bar, old results are replaced when new results // In the legacy address bar, old results are replaced when new results

View File

@ -70,7 +70,13 @@ async function withNewWindow(callback) {
"", "chrome"); "", "chrome");
await BrowserTestUtils.waitForEvent(win, "load"); 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 // Clone the elements into the new window, so we get exact copies without having
// to replicate the xul. // to replicate the xul.

View File

@ -11,8 +11,10 @@ const START_VALUE = "example.org";
add_task(async function setup() { add_task(async function setup() {
await SpecialPowers.pushPrefEnv({ await SpecialPowers.pushPrefEnv({
set: [["browser.altClickSave", true], set: [
["browser.urlbar.autoFill", false]], ["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; await saveURLPromise;
ok(true, "SaveURL was called"); ok(true, "SaveURL was called");
@ -41,7 +43,7 @@ add_task(async function shift_left_click_test() {
let destinationURL = "http://" + TEST_VALUE + "/"; let destinationURL = "http://" + TEST_VALUE + "/";
let newWindowPromise = BrowserTestUtils.waitForNewWindow({url: destinationURL}); let newWindowPromise = BrowserTestUtils.waitForNewWindow({url: destinationURL});
triggerCommand("click", {shiftKey: true}); await triggerCommand("click", {shiftKey: true});
let win = await newWindowPromise; let win = await newWindowPromise;
info("URL should be loaded in a new window"); info("URL should be loaded in a new window");
@ -65,7 +67,7 @@ add_task(async function right_click_test() {
// Add a new tab. // Add a new tab.
await promiseOpenNewTab(); await promiseOpenNewTab();
triggerCommand("click", {button: 2}); await triggerCommand("click", {button: 2});
// Right click should do nothing (context menu will be shown). // Right click should do nothing (context menu will be shown).
is(gURLBar.value, TEST_VALUE, "Urlbar still has the value we entered"); 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 tab = await promiseOpenNewTab();
let loadStartedPromise = promiseLoadStarted(); let loadStartedPromise = promiseLoadStarted();
triggerCommand("click", {accelKey: true, shiftKey: true}); await triggerCommand("click", {accelKey: true, shiftKey: true});
await loadStartedPromise; await loadStartedPromise;
// Check the load occurred in a new background tab. // 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 // Select the new background tab
gBrowser.selectedTab = gBrowser.selectedTab.nextElementSibling; 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. // Cleanup.
gBrowser.removeCurrentTab(); 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. // Trigger a load and check it occurs in the current tab.
let loadStartedPromise = promiseLoadStarted(); let loadStartedPromise = promiseLoadStarted();
triggerCommand(type, details); await triggerCommand(type, details);
await loadStartedPromise; await loadStartedPromise;
info("URL should be loaded in the current tab"); 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", desc: "Ctrl/Cmd left click on go button",
type: "click", type: "click",
details: {accelKey: true}, details: {accelKey: true},
url: null, url: "about:blank",
}, },
{ {
desc: "Alt+Return keypress in a dirty tab", 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}`); info(`Running test: ${desc}`);
// Add a new tab. // 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. // Trigger a load and check it occurs in the current tab.
let tabSwitchedPromise = promiseNewTabSwitched(); let tabSwitchedPromise = promiseNewTabSwitched();
triggerCommand(type, details); await triggerCommand(type, details);
await tabSwitchedPromise; await tabSwitchedPromise;
// Check the load occurred in a new tab. // Check the load occurred in a new tab.
info("URL should be loaded in a new focused 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); await promiseCheckChildNoFocusedElement(gBrowser.selectedBrowser);
is(document.activeElement, gBrowser.selectedBrowser, "Content window should be focused"); is(document.activeElement, gBrowser.selectedBrowser, "Content window should be focused");
isnot(gBrowser.selectedTab, tab, "New URL was loaded in a new tab"); 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.focus();
gURLBar.value = ""; gURLBar.value = "";
EventUtils.sendString(TEST_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") { if (type == "click") {
ok(gURLBar.hasAttribute("usertyping"), ok(gURLBar.hasAttribute("usertyping"),
"usertyping attribute must be set for the go button to be visible"); "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") { async function promiseOpenNewTab(url = "about:blank") {
let tab = BrowserTestUtils.addTab(gBrowser, url); let tab = BrowserTestUtils.addTab(gBrowser, url, {userContextId: gUserContextIdSerial++});
let tabSwitchPromise = promiseNewTabSwitched(tab); let tabSwitchPromise = promiseNewTabSwitched(tab);
gBrowser.selectedTab = tab; gBrowser.selectedTab = tab;
await BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser); await BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);

View File

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

View File

@ -43,7 +43,7 @@
align-self: center; align-self: center;
grid-area: name; grid-area: name;
font-size: var(--body-20-font-size); 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; line-height: 1.5;
margin-inline-start: calc(var(--base-unit) * 3); 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 * 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/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
.sidebar {
display: grid;
grid-template-rows: auto auto;
}
.sidebar__label { .sidebar__label {
color: var(--grey-40); color: var(--grey-40);
display: block; display: block;
@ -13,3 +18,20 @@
.sidebar__refresh-usb { .sidebar__refresh-usb {
text-align: center; 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() { renderDevicesEmpty() {
return SidebarItem( return SidebarItem(
{ {},
isSelected: false,
},
Localized( Localized(
{ {
id: "about-debugging-sidebar-no-devices", id: "about-debugging-sidebar-no-devices",
@ -89,12 +87,12 @@ class Sidebar extends PureComponent {
} }
// render all devices otherwise // render all devices otherwise
return [ return [
...this.renderSidebarItems(GLOBE_ICON, networkRuntimes), ...this.renderRuntimeItems(GLOBE_ICON, networkRuntimes),
...this.renderSidebarItems(USB_ICON, usbRuntimes), ...this.renderRuntimeItems(USB_ICON, usbRuntimes),
]; ];
} }
renderSidebarItems(icon, runtimes) { renderRuntimeItems(icon, runtimes) {
const { dispatch, selectedPage, selectedRuntimeId } = this.props; const { dispatch, selectedPage, selectedRuntimeId } = this.props;
return runtimes.map(runtime => { 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() { render() {
const { dispatch, selectedPage, selectedRuntimeId, isScanningUsb } = this.props; const { dispatch, selectedPage, selectedRuntimeId, isScanningUsb } = this.props;
@ -156,17 +199,15 @@ class Sidebar extends PureComponent {
), ),
SidebarItem( SidebarItem(
{ {
className: "sidebar-item--overflow", className: "sidebar-item--overflow sidebar-item--full-width",
isSelected: false,
}, },
dom.hr({ className: "separator" }), dom.hr({ className: "separator separator--breathe" }),
this.renderAdbAddonStatus(), this.renderAdbAddonStatus(),
), ),
this.renderDevices(), this.renderDevices(),
SidebarItem( SidebarItem(
{ {
className: "sidebar-item--breathe sidebar__refresh-usb", className: "sidebar-item--breathe sidebar__refresh-usb",
isSelected: false,
key: "refresh-devices", key: "refresh-devices",
}, },
RefreshDevicesButton({ RefreshDevicesButton({
@ -174,7 +215,8 @@ class Sidebar extends PureComponent {
isScanning: isScanningUsb, isScanning: isScanningUsb,
}) })
), ),
) ),
this.renderFooter(),
); );
} }
} }

View File

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

View File

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

View File

@ -11,17 +11,29 @@
.sidebar-item { .sidebar-item {
color: var(--sidebar-text-color); color: var(--sidebar-text-color);
border-radius: 2px; border-radius: 2px;
height: var(--category-height);
padding-inline-end: var(--category-padding); padding-inline-end: var(--category-padding);
padding-inline-start: var(--category-padding); padding-inline-start: var(--category-padding);
transition: background-color var(--category-transition-duration); transition: background-color var(--category-transition-duration);
-moz-user-select: none; -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); min-height: var(--category-height);
height: auto; height: auto;
} } */
.sidebar-item__link { .sidebar-item__link {
display: block; display: block;

View File

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

View File

@ -11,15 +11,25 @@
*/ */
.sidebar-runtime-item__container { .sidebar-runtime-item__container {
font-size: 0.8em;
align-items: center; align-items: center;
display: grid; display: grid;
grid-column-gap: var(--base-unit); grid-column-gap: var(--base-unit);
grid-template-columns: calc(var(--base-unit) * 6) 1fr auto; grid-template-columns: calc(var(--base-unit) * 6) 1fr auto;
height: 100%; height: 100%;
font-size: var(--body-20-font-size);
font-weight: var(--body-20-font-weight);
} }
.sidebar-runtime-item__icon { .sidebar-runtime-item__icon {
fill: currentColor; fill: currentColor;
-moz-context-properties: fill; -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 ? const displayName = isUnknown ?
getString("about-debugging-sidebar-runtime-item-waiting-for-runtime") : name; getString("about-debugging-sidebar-runtime-item-waiting-for-runtime") : name;
const titleLocalizationId = deviceName ? const localizationId = deviceName
"about-debugging-sidebar-runtime-item-name" : ? "about-debugging-sidebar-runtime-item-name"
"about-debugging-sidebar-runtime-item-name-no-device"; : "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( return Localized(
{ {
id: titleLocalizationId, id: localizationId,
attrs: { title: true }, attrs: { title: true },
$deviceName: deviceName, $deviceName: deviceName,
$displayName: displayName, $displayName: displayName,
}, },
dom.span( deviceName ? renderWithDevice() : renderNoDevice(),
{
className: "ellipsis-text",
title: titleLocalizationId,
},
displayName,
// If a deviceName is available, display it on a separate line.
...(deviceName ? [
dom.br({}),
deviceName,
] : []),
)
); );
} }
@ -99,10 +117,11 @@ class SidebarRuntimeItem extends PureComponent {
return SidebarItem( return SidebarItem(
{ {
className: "sidebar-item--tall",
isSelected, isSelected,
to: isConnected ? `/runtime/${encodeURIComponent(runtimeId)}` : null, to: isConnected ? `/runtime/${encodeURIComponent(runtimeId)}` : null,
}, },
dom.div( dom.section(
{ {
className: "sidebar-runtime-item__container", className: "sidebar-runtime-item__container",
}, },

View File

@ -34,7 +34,7 @@ add_task(async function() {
await waitUntil(() => !usbRuntimeSidebarItem.querySelector(".js-connect-button")); await waitUntil(() => !usbRuntimeSidebarItem.querySelector(".js-connect-button"));
info("Check whether the label of item is updated after connecting"); 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"); info("Remove all USB runtimes");
mocks.removeUSBRuntime(RUNTIME_ID); 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[]) { function filterByUniqLocation(positions: MappedLocation[]) {
return uniqBy(positions, ({ location }) => makeBreakpointId(location)); return uniqBy(positions, ({ location }) => makeBreakpointId(location));
} }
@ -96,6 +104,8 @@ async function _setBreakpointPositions(sourceId, thunkArgs) {
let positions = convertToList(results, generatedSource); let positions = convertToList(results, generatedSource);
positions = await mapLocations(positions, thunkArgs); positions = await mapLocations(positions, thunkArgs);
positions = filterBySource(positions, sourceId);
positions = filterByUniqLocation(positions); positions = filterByUniqLocation(positions);
const source = getSource(getState(), sourceId); const source = getSource(getState(), sourceId);

View File

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

View File

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

View File

@ -70,7 +70,7 @@ class DebugTargetInfo extends PureComponent {
return dom.span( return dom.span(
{ {
className: "iconized-label", className: "iconized-label js-connection-info",
}, },
dom.img({ src: image, alt: `${connectionType} icon`}), dom.img({ src: image, alt: `${connectionType} icon`}),
this.props.L10N.getStr(l10nId), this.props.L10N.getStr(l10nId),
@ -108,7 +108,7 @@ class DebugTargetInfo extends PureComponent {
className: "iconized-label", className: "iconized-label",
}, },
dom.img({ src: favicon, alt: "favicon"}), 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), 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/layout.css"/>
<link rel="stylesheet" href="chrome://devtools/skin/animation.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/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/shared/components/SidebarToggle.css"/>
<link rel="stylesheet" href="resource://devtools/client/inspector/components/InspectorTabPanel.css"/> <link rel="stylesheet" href="resource://devtools/client/inspector/components/InspectorTabPanel.css"/>
<link rel="stylesheet" href="resource://devtools/client/shared/components/splitter/SplitBox.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 = about-debugging-sidebar-runtime-item-name-no-device =
.title = { $displayName } .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 # 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. # will attempt to update the list of devices displayed in the sidebar.
about-debugging-refresh-usb-devices-button = Refresh devices 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/splitter/SplitBox.css";
@import "resource://devtools/client/shared/components/tree/TreeView.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/Tabs.css";
@import "resource://devtools/client/shared/components/tabs/TabBar.css";
@import "chrome://devtools/skin/components-frame.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/lib/codemirror.css";
@import "chrome://devtools/content/shared/sourceeditor/codemirror/addon/dialog/dialog.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/splitter/SplitBox.css";
@import "resource://devtools/client/shared/components/tree/TreeView.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/Tabs.css";
@import "resource://devtools/client/shared/components/tabs/TabBar.css";
@import "chrome://devtools/skin/components-frame.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/lib/codemirror.css";
@import "chrome://devtools/content/shared/sourceeditor/codemirror/addon/dialog/dialog.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; 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 { .tabs .tabs-menu {
list-style: none; list-style: none;
padding: 0; padding: 0;
margin: 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 { .tabs .tabs-menu-item {
display: inline-block; display: inline-block;
position: relative; 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 { .tabs .tabs-menu-item a {
display: flex; display: flex;
justify-content: center; justify-content: center;
padding: 4px 8px; padding: 3px 10px;
border: 1px solid transparent; border: 1px solid transparent;
font-size: 12px; font-size: 12px;
text-decoration: none; text-decoration: none;
white-space: nowrap; 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 { .tabs .tabs-menu-item .tab-badge {
@ -54,11 +101,6 @@
-moz-user-select: none !important; -moz-user-select: none !important;
} }
.tabs .tabs-menu-item a {
cursor: default;
-moz-user-select: none;
}
/* Make sure panel content takes entire vertical space. */ /* Make sure panel content takes entire vertical space. */
.tabs .panels { .tabs .panels {
flex: 1; flex: 1;
@ -69,43 +111,4 @@
height: 100%; height: 100%;
overflow-x: hidden; overflow-x: hidden;
overflow-y: auto; 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/. # file, You can obtain one at http://mozilla.org/MPL/2.0/.
DevToolsModules( DevToolsModules(
'TabBar.css',
'TabBar.js', 'TabBar.js',
'Tabs.css', 'Tabs.css',
'Tabs.js', '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/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/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/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/side-panel.css">
<link rel="stylesheet" type="text/css" href="resource://devtools/client/inspector/components/InspectorTabPanel.css"> <link rel="stylesheet" type="text/css" href="resource://devtools/client/inspector/components/InspectorTabPanel.css">
</head> </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/SmartTrace.css"/>
<link rel="stylesheet" href="resource://devtools/client/shared/components/reps/reps.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/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="resource://devtools/client/shared/components/NotificationBox.css"/>
<link rel="stylesheet" href="chrome://devtools/content/netmonitor/src/assets/styles/httpi.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"/> <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 { DOMNode: rawNode } = this.rawAccessible;
const win = rawNode.ownerGlobal; const win = rawNode.ownerGlobal;
this.walker.loadTransitionDisablingStyleSheet(win); this.walker.clearStyles(win);
const contrastRatio = await getContrastRatioFor(rawNode.parentNode, { const contrastRatio = await getContrastRatioFor(rawNode.parentNode, {
bounds: this.bounds, bounds: this.bounds,
win, win,
}); });
this.walker.removeTransitionDisablingStyleSheet(win); this.walker.restoreStyles(win);
return contrastRatio; return contrastRatio;
}, },
@ -407,16 +407,27 @@ const AccessibleActor = ActorClassWithSpec(accessibleSpec, {
* Audit results for the accessible object. * Audit results for the accessible object.
*/ */
async audit() { async audit() {
// More audit steps will be added here in the near future. In addition to colour if (this._auditing) {
// contrast ratio we will add autits for to the missing names, invalid states, etc. return this._auditing;
// (For example see bug 1518808). }
const [ contrastRatio ] = await Promise.all([
this._getContrastRatio(),
]);
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, contrastRatio,
}; ]) => {
const audit = this.isDefunct ? null : {
contrastRatio,
};
this._auditing = null;
return audit;
});
return this._auditing;
}, },
snapshot() { snapshot() {

View File

@ -477,12 +477,14 @@ const AccessibleWalkerActor = ActorClassWithSpec(accessibleWalkerSpec, {
}, },
/** /**
* Load accessibility highlighter style sheet used for preventing transitions and * Ensure that nothing interferes with the audit for an accessible object
* applying transparency when calculating colour contrast. * (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 * @param {Object} win
* Window where highlighting happens. * Window where highlighting happens.
*/ */
loadTransitionDisablingStyleSheet(win) { clearStyles(win) {
if (this._sheetLoaded) { if (this._sheetLoaded) {
return; return;
} }
@ -493,23 +495,45 @@ const AccessibleWalkerActor = ActorClassWithSpec(accessibleWalkerSpec, {
// taking a snapshot for contrast measurement). // taking a snapshot for contrast measurement).
loadSheet(win, HIGHLIGHTER_STYLES_SHEET); loadSheet(win, HIGHLIGHTER_STYLES_SHEET);
this._sheetLoaded = true; this._sheetLoaded = true;
this.hideHighlighter();
}, },
/** /**
* Unload accessibility highlighter style sheet used for preventing transitions and * Restore CSS and overlays that could've interfered with the audit for an
* applying transparency when calculating colour contrast. * 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 * @param {Object} win
* Window where highlighting was happenning. * Window where highlighting was happenning.
*/ */
removeTransitionDisablingStyleSheet(win) { restoreStyles(win) {
if (!this._sheetLoaded) { if (!this._sheetLoaded) {
return; return;
} }
this.showHighlighter();
removeSheet(win, HIGHLIGHTER_STYLES_SHEET); removeSheet(win, HIGHLIGHTER_STYLES_SHEET);
this._sheetLoaded = false; 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 * Public method used to show an accessible object highlighter on the client
* side. * side.

View File

@ -509,6 +509,13 @@ exports.CustomHighlighterActor = protocol.ActorClassWithSpec(customHighlighterSp
release: function() {}, release: function() {},
/**
* Get current instance of the highlighter object.
*/
get instance() {
return this._highlighter;
},
/** /**
* Show the highlighter. * Show the highlighter.
* This calls through to the highlighter instance's |show(node, options)| * 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); 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. * Hide the accessible bounds container.
*/ */
_hideAccessibleBounds() { _hideAccessibleBounds() {
this._shouldRestoreBoundsVisibility = null;
setIgnoreLayoutChanges(true);
this.getElement("elements").setAttribute("hidden", "true"); this.getElement("elements").setAttribute("hidden", "true");
setIgnoreLayoutChanges(false,
this.highlighterEnv.window.document.documentElement);
} }
/** /**
* Show the accessible bounds container. * Show the accessible bounds container.
*/ */
_showAccessibleBounds() { _showAccessibleBounds() {
this._shouldRestoreBoundsVisibility = null;
if (!this.currentNode || !this.highlighterEnv.window) {
return;
}
setIgnoreLayoutChanges(true);
this.getElement("elements").removeAttribute("hidden"); 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); 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. * Show accessible bounds highlighter.
*/ */
_showAccessibleBounds() { _showAccessibleBounds() {
this._shouldRestoreBoundsVisibility = null;
if (this.container) { if (this.container) {
if (!this.currentNode || !this.highlighterEnv.window) {
return;
}
this.container.removeAttribute("hidden"); this.container.removeAttribute("hidden");
} }
} }
@ -350,6 +378,7 @@ class XULWindowAccessibleHighlighter {
* Hide accessible bounds highlighter. * Hide accessible bounds highlighter.
*/ */
_hideAccessibleBounds() { _hideAccessibleBounds() {
this._shouldRestoreBoundsVisibility = null;
if (this.container) { if (this.container) {
this.container.setAttribute("hidden", "true"); this.container.setAttribute("hidden", "true");
} }

View File

@ -22,7 +22,8 @@
#include "mozilla/dom/KeyframeEffectBinding.h" #include "mozilla/dom/KeyframeEffectBinding.h"
#include "mozilla/dom/KeyframeEffect.h" // For PropertyValuesPair etc. #include "mozilla/dom/KeyframeEffect.h" // For PropertyValuesPair etc.
#include "mozilla/dom/Nullable.h" #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 "nsClassHashtable.h"
#include "nsContentUtils.h" // For GetContextForContent #include "nsContentUtils.h" // For GetContextForContent
#include "nsCSSPropertyIDSet.h" #include "nsCSSPropertyIDSet.h"

View File

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

View File

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

View File

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

View File

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

View File

@ -606,6 +606,15 @@ dictionary WindowActorOptions {
*/ */
boolean includeChrome = false; 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. */ /** This fields are used for configuring individual sides of the actor. */
required WindowActorSidedOptions parent; required WindowActorSidedOptions parent;
required WindowActorChildOptions child; required WindowActorChildOptions child;

View File

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

View File

@ -1168,7 +1168,9 @@ void ContentChild::LaunchRDDProcess() {
nsresult rv; nsresult rv;
Endpoint<PRemoteDecoderManagerChild> endpoint; Endpoint<PRemoteDecoderManagerChild> endpoint;
Unused << SendLaunchRDDProcess(&rv, &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)); RemoteDecoderManagerChild::InitForContent(std::move(endpoint));
} }
})); }));

View File

@ -1064,6 +1064,14 @@ mozilla::ipc::IPCResult ContentParent::RecvLaunchRDDProcess(
Preferences::GetBool("media.rdd-process.enabled", false)) { Preferences::GetBool("media.rdd-process.enabled", false)) {
RDDProcessManager* rdd = RDDProcessManager::Get(); RDDProcessManager* rdd = RDDProcessManager::Get();
if (rdd) { 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(); rdd->LaunchRDDProcess();
bool rddOpened = rdd->CreateContentBridge(OtherPid(), aEndpoint); bool rddOpened = rdd->CreateContentBridge(OtherPid(), aEndpoint);

View File

@ -12,6 +12,8 @@
#include "mozilla/dom/PContent.h" #include "mozilla/dom/PContent.h"
#include "mozilla/StaticPtr.h" #include "mozilla/StaticPtr.h"
#include "mozJSComponentLoader.h" #include "mozJSComponentLoader.h"
#include "mozilla/extensions/WebExtensionContentScript.h"
#include "mozilla/Logging.h"
namespace mozilla { namespace mozilla {
namespace dom { namespace dom {
@ -119,9 +121,6 @@ class JSWindowActorProtocol final : public nsIObserver,
nsTArray<nsCString> mObservers; 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 ParentSide& Parent() const { return mParent; }
const ChildSide& Child() const { return mChild; } const ChildSide& Child() const { return mChild; }
@ -129,17 +128,22 @@ class JSWindowActorProtocol final : public nsIObserver,
void UnregisterListenersFor(EventTarget* aRoot); void UnregisterListenersFor(EventTarget* aRoot);
void AddObservers(); void AddObservers();
void RemoveObservers(); void RemoveObservers();
bool Matches(BrowsingContext* aBrowsingContext, nsIURI* aURI);
private: private:
explicit JSWindowActorProtocol(const nsAString& aName) : mName(aName) {} explicit JSWindowActorProtocol(const nsAString& aName) : mName(aName) {}
extensions::MatchPatternSet* GetURIMatcher();
~JSWindowActorProtocol() = default; ~JSWindowActorProtocol() = default;
nsString mName; nsString mName;
bool mAllFrames = false; bool mAllFrames = false;
bool mIncludeChrome = false; bool mIncludeChrome = false;
nsTArray<nsString> mMatches;
ParentSide mParent; ParentSide mParent;
ChildSide mChild; ChildSide mChild;
RefPtr<extensions::MatchPatternSet> mURIMatcher;
}; };
NS_IMPL_ISUPPORTS(JSWindowActorProtocol, nsIObserver, nsIDOMEventListener); NS_IMPL_ISUPPORTS(JSWindowActorProtocol, nsIObserver, nsIDOMEventListener);
@ -153,6 +157,7 @@ JSWindowActorProtocol::FromIPC(const JSWindowActorInfo& aInfo) {
// irrelevant and not propagated. // irrelevant and not propagated.
proto->mIncludeChrome = false; proto->mIncludeChrome = false;
proto->mAllFrames = aInfo.allFrames(); proto->mAllFrames = aInfo.allFrames();
proto->mMatches = aInfo.matches();
proto->mChild.mModuleURI.Assign(aInfo.url()); proto->mChild.mModuleURI.Assign(aInfo.url());
proto->mChild.mEvents.SetCapacity(aInfo.events().Length()); proto->mChild.mEvents.SetCapacity(aInfo.events().Length());
@ -177,6 +182,7 @@ JSWindowActorInfo JSWindowActorProtocol::ToIPC() {
JSWindowActorInfo info; JSWindowActorInfo info;
info.name() = mName; info.name() = mName;
info.allFrames() = mAllFrames; info.allFrames() = mAllFrames;
info.matches() = mMatches;
info.url() = mChild.mModuleURI; info.url() = mChild.mModuleURI;
info.events().SetCapacity(mChild.mEvents.Length()); info.events().SetCapacity(mChild.mEvents.Length());
@ -205,6 +211,11 @@ JSWindowActorProtocol::FromWebIDLOptions(const nsAString& aName,
proto->mAllFrames = aOptions.mAllFrames; proto->mAllFrames = aOptions.mAllFrames;
proto->mIncludeChrome = aOptions.mIncludeChrome; 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->mParent.mModuleURI = aOptions.mParent.mModuleURI;
proto->mChild.mModuleURI = aOptions.mChild.mModuleURI; proto->mChild.mModuleURI = aOptions.mChild.mModuleURI;
@ -302,7 +313,13 @@ NS_IMETHODIMP JSWindowActorProtocol::Observe(nsISupports* aSubject,
ErrorResult error; ErrorResult error;
RefPtr<JSWindowActorChild> actor = wgc->GetActor(mName, error); RefPtr<JSWindowActorChild> actor = wgc->GetActor(mName, error);
if (NS_WARN_IF(error.Failed())) { 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 // 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()); }
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, void JSWindowActorService::ConstructActor(
bool aParentSide, const nsAString& aName, bool aParentSide, BrowsingContext* aBrowsingContext,
BrowsingContext* aBrowsingContext, nsIURI* aURI, JS::MutableHandleObject aActor, ErrorResult& aRv) {
JS::MutableHandleObject aActor,
ErrorResult& aRv) {
MOZ_ASSERT_IF(aParentSide, XRE_IsParentProcess()); 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 // Constructing an actor requires a running script, so push an AutoEntryScript
// onto the stack. // onto the stack.
AutoEntryScript aes(xpc::PrivilegedJunkScope(), "JSWindowActor construction"); AutoEntryScript aes(xpc::PrivilegedJunkScope(), "JSWindowActor construction");
@ -515,14 +577,9 @@ void JSWindowActorService::ConstructActor(const nsAString& aName,
side = &proto->Child(); side = &proto->Child();
} }
// Check if our current BrowsingContext matches the requirements for this // Check if our current BrowsingContext and URI matches the requirements for
// actor to load. // this actor to load.
if (!proto->AllFrames() && aBrowsingContext->GetParent()) { if (!proto->Matches(aBrowsingContext, aURI)) {
aRv.Throw(NS_ERROR_NOT_AVAILABLE);
return;
}
if (!proto->IncludeChrome() && !aBrowsingContext->IsContent()) {
aRv.Throw(NS_ERROR_NOT_AVAILABLE); aRv.Throw(NS_ERROR_NOT_AVAILABLE);
return; return;
} }

View File

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

View File

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

View File

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

View File

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

View File

@ -3,6 +3,7 @@
"use strict"; "use strict";
const URL = "about:blank"; const URL = "about:blank";
const TEST_URL = "http://test2.example.org/";
let windowActorOptions = { let windowActorOptions = {
parent: { parent: {
moduleURI: "resource://testing-common/TestParent.jsm", 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() { add_task(function test_registerWindowActor() {
ok(ChromeUtils, "Should be able to get the ChromeUtils interface"); ok(ChromeUtils, "Should be able to get the ChromeUtils interface");
ChromeUtils.registerWindowActor("Test", windowActorOptions); 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() { add_task(async function test_asyncMessage() {
await BrowserTestUtils.withNewTab({gBrowser, url: URL}, await BrowserTestUtils.withNewTab({gBrowser, url: URL},
async function(browser) { async function(browser) {
@ -257,3 +207,150 @@ add_task(async function test_observers_dont_notify_with_wrong_window() {
}); });
ChromeUtils.unregisterWindowActor("Test"); 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 "mac": return 0; // value is unused, can be anything
case "linux": return 4; // value is unused, pass GDK_SCROLL_SMOOTH anyway 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() { function nativeHorizontalWheelEventMsg() {
@ -41,7 +41,7 @@ function nativeHorizontalWheelEventMsg() {
case "mac": return 0; // value is unused, can be anything case "mac": return 0; // value is unused, can be anything
case "linux": return 4; // value is unused, pass GDK_SCROLL_SMOOTH anyway 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. // 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 "linux": return 4; // GDK_BUTTON_PRESS
case "android": return 5; // ACTION_POINTER_DOWN 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() { function nativeMouseMoveEventMsg() {
@ -91,7 +91,7 @@ function nativeMouseMoveEventMsg() {
case "linux": return 3; // GDK_MOTION_NOTIFY case "linux": return 3; // GDK_MOTION_NOTIFY
case "android": return 7; // ACTION_HOVER_MOVE 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() { function nativeMouseUpEventMsg() {
@ -101,7 +101,7 @@ function nativeMouseUpEventMsg() {
case "linux": return 7; // GDK_BUTTON_RELEASE case "linux": return 7; // GDK_BUTTON_RELEASE
case "android": return 6; // ACTION_POINTER_UP 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) { function getBoundingClientRectRelativeToVisualViewport(aElement) {
@ -164,7 +164,7 @@ function rectRelativeToScreen(aElement) {
function synthesizeNativeWheel(aTarget, aX, aY, aDeltaX, aDeltaY, aObserver) { function synthesizeNativeWheel(aTarget, aX, aY, aDeltaX, aDeltaY, aObserver) {
var pt = coordinatesRelativeToScreen(aX, aY, aTarget); var pt = coordinatesRelativeToScreen(aX, aY, aTarget);
if (aDeltaX && aDeltaY) { 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); aDeltaX = nativeScrollUnits(aTarget, aDeltaX);
aDeltaY = nativeScrollUnits(aTarget, aDeltaY); aDeltaY = nativeScrollUnits(aTarget, aDeltaY);
@ -277,7 +277,10 @@ function* synthesizeNativeTouchSequences(aTarget, aPositions, aObserver = null,
continue; continue;
} }
if (aPositions[i].length != aTouchIds.length) { 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++) { for (let j = 0; j < aTouchIds.length; j++) {
if (aPositions[i][j] != null) { if (aPositions[i][j] != null) {
@ -286,7 +289,7 @@ function* synthesizeNativeTouchSequences(aTarget, aPositions, aObserver = null,
} }
} }
if (lastNonNullValue < 0) { 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 // 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) { function flushApzRepaints(aCallback, aWindow = window) {
if (!aCallback) { if (!aCallback) {
throw "A callback must be provided!"; throw new Error("A callback must be provided!");
} }
promiseApzRepaintsFlushed(aWindow).then(aCallback); promiseApzRepaintsFlushed(aWindow).then(aCallback);
} }

View File

@ -24,3 +24,4 @@ use Mozilla's I18n APIs.
locale locale
dataintl dataintl
localization 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/ContextOptions.h"
#include "js/Conversions.h" #include "js/Conversions.h"
#include "js/Date.h" #include "js/Date.h"
#include "js/ForOfIterator.h"
#include "js/Initialization.h" #include "js/Initialization.h"
#include "js/MemoryMetrics.h" #include "js/MemoryMetrics.h"
#include "js/PropertySpec.h" #include "js/PropertySpec.h"

View File

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

View File

@ -693,21 +693,6 @@ struct GlobalAccess {
typedef Vector<GlobalAccess, 0, SystemAllocPolicy> GlobalAccessVector; 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 wasm
namespace jit { namespace jit {
@ -717,7 +702,6 @@ class AssemblerShared {
wasm::CallSiteVector callSites_; wasm::CallSiteVector callSites_;
wasm::CallSiteTargetVector callSiteTargets_; wasm::CallSiteTargetVector callSiteTargets_;
wasm::TrapSiteVectorArray trapSites_; wasm::TrapSiteVectorArray trapSites_;
wasm::CallFarJumpVector callFarJumps_;
wasm::SymbolicAccessVector symbolicAccesses_; wasm::SymbolicAccessVector symbolicAccesses_;
protected: protected:
@ -756,9 +740,6 @@ class AssemblerShared {
void append(wasm::Trap trap, wasm::TrapSite site) { void append(wasm::Trap trap, wasm::TrapSite site) {
enoughMemory_ &= trapSites_[trap].append(site); enoughMemory_ &= trapSites_[trap].append(site);
} }
void append(wasm::CallFarJump jmp) {
enoughMemory_ &= callFarJumps_.append(jmp);
}
void append(const wasm::MemoryAccessDesc& access, uint32_t pcOffset) { void append(const wasm::MemoryAccessDesc& access, uint32_t pcOffset) {
appendOutOfBoundsTrap(access.trapOffset(), pcOffset); appendOutOfBoundsTrap(access.trapOffset(), pcOffset);
} }
@ -773,7 +754,6 @@ class AssemblerShared {
wasm::CallSiteVector& callSites() { return callSites_; } wasm::CallSiteVector& callSites() { return callSites_; }
wasm::CallSiteTargetVector& callSiteTargets() { return callSiteTargets_; } wasm::CallSiteTargetVector& callSiteTargets() { return callSiteTargets_; }
wasm::TrapSiteVectorArray& trapSites() { return trapSites_; } wasm::TrapSiteVectorArray& trapSites() { return trapSites_; }
wasm::CallFarJumpVector& callFarJumps() { return callFarJumps_; }
wasm::SymbolicAccessVector& symbolicAccesses() { return symbolicAccesses_; } 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 * 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/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "js/ForOfIterator.h"
#include "jsapi-tests/tests.h" #include "jsapi-tests/tests.h"
BEGIN_TEST(testForOfIterator_basicNonIterable) { 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( extern JS_PUBLIC_API RefPtr<WasmModule> DeserializeWasmModule(
PRFileDesc* bytecode, JS::UniqueChars filename, unsigned line); 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 * 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 * 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/Debug.h',
'../public/Equality.h', '../public/Equality.h',
'../public/ErrorReport.h', '../public/ErrorReport.h',
'../public/ForOfIterator.h',
'../public/GCAnnotations.h', '../public/GCAnnotations.h',
'../public/GCAPI.h', '../public/GCAPI.h',
'../public/GCHashTable.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 * 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/. */ * 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/Interpreter.h"
#include "vm/JSContext.h" #include "vm/JSContext.h"
#include "vm/JSObject.h" #include "vm/JSObject.h"

View File

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

View File

@ -52,7 +52,6 @@ bool CompiledCode::swap(MacroAssembler& masm) {
callSites.swap(masm.callSites()); callSites.swap(masm.callSites());
callSiteTargets.swap(masm.callSiteTargets()); callSiteTargets.swap(masm.callSiteTargets());
trapSites.swap(masm.trapSites()); trapSites.swap(masm.trapSites());
callFarJumps.swap(masm.callFarJumps());
symbolicAccesses.swap(masm.symbolicAccesses()); symbolicAccesses.swap(masm.symbolicAccesses());
codeLabels.swap(masm.codeLabels()); codeLabels.swap(masm.codeLabels());
return true; 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) { for (const SymbolicAccess& access : code.symbolicAccesses) {
uint32_t patchAt = offsetInModule + access.patchAt.offset(); uint32_t patchAt = offsetInModule + access.patchAt.offset();
if (!linkData_->symbolicLinks[access.target].append(patchAt)) { if (!linkData_->symbolicLinks[access.target].append(patchAt)) {
@ -923,7 +915,6 @@ bool ModuleGenerator::finishCodegen() {
MOZ_ASSERT(masm_.callSites().empty()); MOZ_ASSERT(masm_.callSites().empty());
MOZ_ASSERT(masm_.callSiteTargets().empty()); MOZ_ASSERT(masm_.callSiteTargets().empty());
MOZ_ASSERT(masm_.trapSites().empty()); MOZ_ASSERT(masm_.trapSites().empty());
MOZ_ASSERT(masm_.callFarJumps().empty());
MOZ_ASSERT(masm_.symbolicAccesses().empty()); MOZ_ASSERT(masm_.symbolicAccesses().empty());
MOZ_ASSERT(masm_.codeLabels().empty()); MOZ_ASSERT(masm_.codeLabels().empty());
@ -1262,7 +1253,6 @@ size_t CompiledCode::sizeOfExcludingThis(
codeRanges.sizeOfExcludingThis(mallocSizeOf) + codeRanges.sizeOfExcludingThis(mallocSizeOf) +
callSites.sizeOfExcludingThis(mallocSizeOf) + callSites.sizeOfExcludingThis(mallocSizeOf) +
callSiteTargets.sizeOfExcludingThis(mallocSizeOf) + trapSitesSize + callSiteTargets.sizeOfExcludingThis(mallocSizeOf) + trapSitesSize +
callFarJumps.sizeOfExcludingThis(mallocSizeOf) +
symbolicAccesses.sizeOfExcludingThis(mallocSizeOf) + symbolicAccesses.sizeOfExcludingThis(mallocSizeOf) +
codeLabels.sizeOfExcludingThis(mallocSizeOf); codeLabels.sizeOfExcludingThis(mallocSizeOf);
} }

View File

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

View File

@ -5528,6 +5528,26 @@ TEST_F(JsepSessionTest, AudioCallAnswererUsesActpass) {
ASSERT_EQ(kJsepStateHaveLocalOffer, mSessionOff->GetState()); 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 // Disabled: See Bug 1329028
TEST_F(JsepSessionTest, DISABLED_AudioCallOffererAttemptsSetupRoleSwitch) { TEST_F(JsepSessionTest, DISABLED_AudioCallOffererAttemptsSetupRoleSwitch) {
types.push_back(SdpMediaSection::kAudio); types.push_back(SdpMediaSection::kAudio);

View File

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

View File

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

View File

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

View File

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

View File

@ -1422,12 +1422,12 @@ SessionStore.prototype = {
try { try {
state = JSON.parse(aData); state = JSON.parse(aData);
} catch (e) { } 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 // 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) { 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"); let window = Services.wm.getMostRecentWindow("navigator:browser");
@ -1491,7 +1491,7 @@ SessionStore.prototype = {
getClosedTabs(aWindow) { getClosedTabs(aWindow) {
if (!aWindow.__SSID) { 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; return this._windows[aWindow.__SSID].closedTabs;
@ -1499,7 +1499,7 @@ SessionStore.prototype = {
undoCloseTab(aWindow, aCloseTabData) { undoCloseTab(aWindow, aCloseTabData) {
if (!aWindow.__SSID) { 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. // If the tab data is in the closedTabs array, remove it.
@ -1562,7 +1562,7 @@ SessionStore.prototype = {
} }
if (!aWindow.__SSID) { 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; let closedTabs = this._windows[aWindow.__SSID].closedTabs;

View File

@ -118,9 +118,9 @@ GeckoViewPermission.prototype = {
}); });
if (constraints.video && !sources.some(source => source.type === "videoinput")) { 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")) { } 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); let dispatcher = GeckoViewUtils.getDispatcherForWindow(win);

View File

@ -760,11 +760,11 @@ PromptDelegate.prototype = {
// channel's actual destination. // channel's actual destination.
if (aAuthInfo.flags & Ci.nsIAuthInformation.AUTH_PROXY) { if (aAuthInfo.flags & Ci.nsIAuthInformation.AUTH_PROXY) {
if (!(aChannel instanceof Ci.nsIProxiedChannel)) { if (!(aChannel instanceof Ci.nsIProxiedChannel)) {
throw "proxy auth needs nsIProxiedChannel"; throw new Error("proxy auth needs nsIProxiedChannel");
} }
let info = aChannel.proxyInfo; let info = aChannel.proxyInfo;
if (!info) { 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://" // Proxies don't have a scheme, but we'll use "moz-proxy://"
// so that it's more obvious what the login is for. // 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 // See bug 915424
function resolveGeckoURI(aURI) { function resolveGeckoURI(aURI) {
if (!aURI) if (!aURI)
throw "Can't resolve an empty uri"; throw new Error("Can't resolve an empty uri");
if (aURI.startsWith("chrome://")) { if (aURI.startsWith("chrome://")) {
let registry = Cc["@mozilla.org/chrome/chrome-registry;1"].getService(Ci.nsIChromeRegistry); let registry = Cc["@mozilla.org/chrome/chrome-registry;1"].getService(Ci.nsIChromeRegistry);
@ -168,7 +168,7 @@ var HomeBanner = (function() {
*/ */
remove: function(id) { remove: function(id) {
if (!(id in _messages)) { 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]; delete _messages[id];
@ -220,10 +220,10 @@ var HomePanels = (function() {
let id = data.id; let id = data.id;
let options = _registeredPanels[id](); let options = _registeredPanels[id]();
if (!options.auth) { 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") { 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(); options.auth.authenticate();
}, },
@ -233,13 +233,13 @@ var HomePanels = (function() {
let view = options.views[data.viewIndex]; let view = options.views[data.viewIndex];
if (!view) { if (!view) {
throw "Home.panels: Invalid view for panel.id = " + data.panelId throw new Error("Home.panels: Invalid view for panel.id = " +
+ ", view.index = " + data.viewIndex; `${data.panelId}, view.index = ${data.viewIndex}`);
} }
if (!view.onrefresh || typeof view.onrefresh !== "function") { if (!view.onrefresh || typeof view.onrefresh !== "function") {
throw "Home.panels: Invalid onrefresh for panel.id = " + data.panelId throw new Error("Home.panels: Invalid onrefresh for panel.id = " +
+ ", view.index = " + data.viewIndex; `${data.panelId}, view.index = ${data.viewIndex}`);
} }
view.onrefresh(); view.onrefresh();
@ -254,7 +254,7 @@ var HomePanels = (function() {
return; return;
} }
if (typeof options.oninstall !== "function") { 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(); options.oninstall();
}, },
@ -268,7 +268,7 @@ var HomePanels = (function() {
return; return;
} }
if (typeof options.onuninstall !== "function") { 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(); options.onuninstall();
}, },
@ -314,19 +314,21 @@ var HomePanels = (function() {
this.default = !!options.default; this.default = !!options.default;
if (!this.id || !this.title) { 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) { if (!this.layout) {
// Use FRAME layout by default // Use FRAME layout by default
this.layout = Layout.FRAME; this.layout = Layout.FRAME;
} else if (!_valueExists(Layout, this.layout)) { } 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) { for (let view of this.views) {
if (!_valueExists(View, view.type)) { 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) { if (!view.itemType) {
@ -338,18 +340,21 @@ var HomePanels = (function() {
view.itemType = Item.IMAGE; view.itemType = Item.IMAGE;
} }
} else if (!_valueExists(Item, view.itemType)) { } 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) { if (!view.itemHandler) {
// Use BROWSER item handler by default // Use BROWSER item handler by default
view.itemHandler = ItemHandler.BROWSER; view.itemHandler = ItemHandler.BROWSER;
} else if (!_valueExists(ItemHandler, view.itemHandler)) { } 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) { 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) { if (view.onrefresh) {
@ -359,10 +364,10 @@ var HomePanels = (function() {
if (options.auth) { if (options.auth) {
if (!options.auth.messageText) { 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) { 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 = { this.authConfig = {
@ -398,7 +403,7 @@ var HomePanels = (function() {
let _assertPanelExists = function(id) { let _assertPanelExists = function(id) {
if (!(id in _registeredPanels)) { 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) { register: function(id, optionsCallback) {
// Bail if the panel already exists // Bail if the panel already exists
if (id in _registeredPanels) { 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") { 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; _registeredPanels[id] = optionsCallback;

View File

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

View File

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

View File

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

View File

@ -116,7 +116,7 @@ var WebrtcUI = {
notificationOptions.icon = "drawable:alert_mic"; notificationOptions.icon = "drawable:alert_mic";
} else { } else {
// somethings wrong. lets throw // 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) if (this._notificationId)

View File

@ -24,6 +24,34 @@ debugger-tests:
files-changed: files-changed:
- 'devtools/client/debugger/new/**' - '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: eslint-plugin-mozilla:
description: eslint-plugin-mozilla integration tests description: eslint-plugin-mozilla integration tests
platform: linux64/opt 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