Merge autoland to mozilla-central a=merge

This commit is contained in:
arthur.iakab 2018-08-25 01:07:22 +03:00
commit 16c7c33a62
156 changed files with 2244 additions and 1629 deletions

View file

@ -313,7 +313,6 @@ mobile/android/locales/
# Non-standard `(catch ex if ...)`
mobile/android/chrome/content/browser.js
mobile/android/components/Snippets.js
# Only contains non-standard test files.
python/**

View file

@ -241,11 +241,10 @@ var ContentBlocking = {
this.identityPopupMultiView = $("#identity-popup-multiView");
this.reportBreakageButton = $("#identity-popup-content-blocking-report-breakage");
this.reportBreakageURL = $("#identity-popup-breakageReportView-collection-url");
this.reportBreakageUA = $("#identity-popup-breakageReportView-collection-userAgent");
this.reportBreakageLearnMore = $("#identity-popup-breakageReportView-learn-more");
let baseURL = Services.urlFormatter.formatURLPref("app.support.baseURL");
this.reportBreakageLearnMore.href = baseURL + "tracking-protection-pbm";
this.reportBreakageLearnMore.href = baseURL + "blocking-breakage";
this.updateReportBreakageUI = () => {
this.reportBreakageButton.hidden = !Services.prefs.getBoolPref(this.PREF_REPORT_BREAKAGE_ENABLED);
@ -356,7 +355,7 @@ var ContentBlocking = {
// Leave the ? at the end of the URL to signify that this URL had its query stripped.
let urlWithoutQuery = this.reportURI.asciiSpec.replace(this.reportURI.query, "");
let body = `Full URL: ${urlWithoutQuery}\n`;
body += `userAgent: ${this.reportBreakageUA.textContent}\n`;
body += `userAgent: ${navigator.userAgent}\n`;
body += "\n**Preferences**\n";
body += `${TrackingProtection.PREF_ENABLED_GLOBALLY}: ${Services.prefs.getBoolPref(TrackingProtection.PREF_ENABLED_GLOBALLY)}\n`;
@ -395,7 +394,6 @@ var ContentBlocking = {
this.reportURI = gBrowser.currentURI;
let urlWithoutQuery = this.reportURI.asciiSpec.replace("?" + this.reportURI.query, "");
this.reportBreakageURL.textContent = urlWithoutQuery;
this.reportBreakageUA.textContent = navigator.userAgent;
this.identityPopupMultiView.showSubView("identity-popup-breakageReportView");
},

View file

@ -5455,7 +5455,13 @@ nsBrowserAccess.prototype = {
}
// Pass all params to openDialog to ensure that "url" isn't passed through
// loadOneOrMoreURIs, which splits based on "|"
newWindow = openDialog(AppConstants.BROWSER_CHROME_URL, "_blank", features, url, null, null, null);
try {
newWindow = openDialog(AppConstants.BROWSER_CHROME_URL, "_blank", features,
// window.arguments
url, null, null, null, null, null, null, null, aTriggeringPrincipal);
} catch (ex) {
Cu.reportError(ex);
}
break;
case Ci.nsIBrowserDOMWindow.OPEN_NEWTAB :
// If we have an opener, that means that the caller is expecting access

View file

@ -162,10 +162,8 @@ add_task(async function testReportBreakage() {
let submitButton = document.getElementById("identity-popup-breakageReportView-submit");
let reportURL = document.getElementById("identity-popup-breakageReportView-collection-url").textContent;
let reportUA = document.getElementById("identity-popup-breakageReportView-collection-userAgent").textContent;
is(reportURL, TRACKING_PAGE, "Shows the correct URL in the report UI.");
is(reportUA, navigator.userAgent, "Shows the correct user agent in the report UI.");
// Make sure that sending the report closes the identity popup.
let popuphidden = BrowserTestUtils.waitForEvent(gIdentityHandler._identityPopup, "popuphidden");
@ -207,7 +205,7 @@ add_task(async function testReportBreakage() {
"Content-Disposition: form-data; name=\"title\"\r\n\r\ntracking.example.org\r\n",
"Content-Disposition: form-data; name=\"body\"\r\n\r\n" +
`Full URL: ${reportURL + "?"}\r\n` +
`userAgent: ${reportUA}\r\n\r\n` +
`userAgent: ${navigator.userAgent}\r\n\r\n` +
"**Preferences**\r\n" +
`${prefsBody}\r\n` +
"**Comments**\r\n" +

View file

@ -16,6 +16,9 @@ add_task(async function clearURLBarAfterParentProcessURL() {
}, {capture: true, once: true});
});
document.getElementById("home-button").click();
if (!tab.linkedBrowser.isRemoteBrowser) {
await BrowserTestUtils.waitForEvent(tab.linkedBrowser, "XULFrameLoaderCreated");
}
await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
is(gURLBar.value, "", "URL bar should be empty");
is(tab.linkedBrowser.userTypedValue, null, "The browser should have no recorded userTypedValue");
@ -36,6 +39,9 @@ add_task(async function clearURLBarAfterParentProcessURLInExistingTab() {
newTabBrowser.loadURI("about:preferences");
});
document.getElementById("home-button").click();
if (!tab.linkedBrowser.isRemoteBrowser) {
await BrowserTestUtils.waitForEvent(tab.linkedBrowser, "XULFrameLoaderCreated");
}
await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
is(gURLBar.value, "", "URL bar should be empty");
is(tab.linkedBrowser.userTypedValue, null, "The browser should have no recorded userTypedValue");

View file

@ -81,7 +81,6 @@ function startServer(cert) {
}
};
tlsServer.setSessionCache(false);
tlsServer.setSessionTickets(false);
tlsServer.setRequestClientCertificate(Ci.nsITLSServerSocket.REQUEST_ALWAYS);

View file

@ -89,7 +89,7 @@
<hbox id="identity-popup-content-blocking-category-tracking-protection"
class="identity-popup-content-blocking-category" align="center" role="group">
<image class="identity-popup-content-blocking-category-icon tracking-protection-icon"/>
<label flex="1" class="identity-popup-content-blocking-category-label">&contentBlocking.trackingProtection.label;</label>
<label flex="1" class="identity-popup-content-blocking-category-label">&contentBlocking.trackingProtection2.label;</label>
<label flex="1" class="identity-popup-content-blocking-category-state-label">&contentBlocking.trackingProtection.blocked.label;</label>
<label flex="1" class="identity-popup-content-blocking-category-add-blocking text-link"
onclick="ContentBlocking.openPreferences('identityPopup-CB-tracking-protection');">&contentBlocking.trackingProtection.add.label;</label>
@ -122,7 +122,7 @@
<label id="identity-popup-content-blocking-report-breakage"
onclick="ContentBlocking.showReportBreakageSubview();"
class="text-link subviewkeynav"
flex="1">&contentBlocking.openBreakageReportView.label;</label>
flex="1">&contentBlocking.openBreakageReportView2.label;</label>
</vbox>
</hbox>
@ -251,7 +251,7 @@
title="&contentBlocking.breakageReportView.label;"
descriptionheightworkaround="true">
<vbox id="identity-popup-breakageReportView-heading">
<description>&contentBlocking.breakageReportView.description;</description>
<description>&contentBlocking.breakageReportView2.description;</description>
<label id="identity-popup-breakageReportView-learn-more"
class="text-link">&contentBlocking.breakageReportView.learnMore;</label>
</vbox>
@ -260,10 +260,6 @@
<label class="identity-popup-breakageReportView-collection-label">&contentBlocking.breakageReportView.collection.url.label;</label>
<label id="identity-popup-breakageReportView-collection-url"/>
</vbox>
<vbox class="identity-popup-breakageReportView-collection-section">
<label class="identity-popup-breakageReportView-collection-label">&contentBlocking.breakageReportView.collection.userAgent.label;</label>
<label id="identity-popup-breakageReportView-collection-userAgent"/>
</vbox>
<vbox class="identity-popup-breakageReportView-collection-section">
<label class="identity-popup-breakageReportView-collection-label">&contentBlocking.breakageReportView.collection.comments.label;</label>
<textbox multiline="true" id="identity-popup-breakageReportView-collection-comments"/>

View file

@ -4,6 +4,7 @@
"use strict";
ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
const {actionCreators: ac, actionTypes: at} = ChromeUtils.import("resource://activity-stream/common/Actions.jsm", {});
const {TippyTopProvider} = ChromeUtils.import("resource://activity-stream/lib/TippyTopProvider.jsm", {});
@ -76,7 +77,7 @@ this.TopSitesFeed = class TopSitesFeed {
this._storage = this.store.dbStorage.getDbTable("sectionPrefs");
this.refresh({broadcast: true});
Services.obs.addObserver(this, "browser-search-engine-modified");
this._currentSearchHostname = getShortURLForCurrentSearch();
XPCOMUtils.defineLazyGetter(this, "_currentSearchHostname", getShortURLForCurrentSearch);
}
uninit() {
@ -214,6 +215,9 @@ this.TopSitesFeed = class TopSitesFeed {
async getLinksWithDefaults() {
const numItems = this.store.getState().Prefs.values[ROWS_PREF] * TOP_SITES_MAX_SITES_PER_ROW;
const searchShortcutsExperiment = this.store.getState().Prefs.values[SEARCH_SHORTCUTS_EXPERIMENT];
// We must wait for search services to initialize in order to access default
// search engine properties without triggering a synchronous initialization
await new Promise(resolve => Services.search.init(resolve));
// Get all frecent sites from history
const frecent = (await this.frecentCache.request({

View file

@ -185,7 +185,13 @@ const TEST_GLOBAL = {
appinfo: {appBuildID: "20180710100040"}
},
XPCOMUtils: {
defineLazyGetter(_1, _2, f) { f(); },
defineLazyGetter(object, name, f) {
if (object && name) {
object[name] = f();
} else {
f();
}
},
defineLazyGlobalGetters() {},
defineLazyModuleGetter() {},
defineLazyServiceGetter() {},

View file

@ -19,6 +19,9 @@ XPCOMUtils.defineLazyModuleGetters(this, {
XPCOMUtils.defineLazyServiceGetter(this, "WindowsUIUtils",
"@mozilla.org/windows-ui-utils;1", "nsIWindowsUIUtils");
XPCOMUtils.defineLazyGetter(this, "gSystemPrincipal",
() => Services.scriptSecurityManager.getSystemPrincipal());
function shouldLoadURI(aURI) {
if (aURI && !aURI.schemeIs("chrome"))
return true;
@ -162,6 +165,8 @@ function getPostUpdateOverridePage(defaultOverridePage) {
* The nsICommandLine object given to nsICommandLineHandler's handle
* method.
* Used to check if we are processing the command line for the initial launch.
* @param triggeringPrincipal
* The nsIPrincipal to use as triggering principal for the page load(s).
* @param urlOrUrlList (optional)
* When omitted, the browser window will be opened with the default
* arguments, which will usually load the homepage.
@ -175,15 +180,20 @@ function getPostUpdateOverridePage(defaultOverridePage) {
* @param forcePrivate (optional)
* Boolean. If set to true, the new window will be a private browsing one.
*/
function openBrowserWindow(cmdLine, urlOrUrlList, postData = null,
function openBrowserWindow(cmdLine, triggeringPrincipal, urlOrUrlList, postData = null,
forcePrivate = false) {
let chromeURL = AppConstants.BROWSER_CHROME_URL;
let args;
if (!urlOrUrlList) {
// Just pass in the defaultArgs directly
// Just pass in the defaultArgs directly. We'll use system principal on the other end.
args = [gBrowserContentHandler.defaultArgs];
} else if (Array.isArray(urlOrUrlList)) {
// There isn't an explicit way to pass a principal here, so we load multiple URLs
// with system principal when we get to actually loading them.
if (!triggeringPrincipal || !triggeringPrincipal.equals(gSystemPrincipal)) {
throw new Error("Can't open multiple URLs with something other than system principal.");
}
// Passing an nsIArray for the url disables the "|"-splitting behavior.
let uriArray = Cc["@mozilla.org/array;1"]
.createInstance(Ci.nsIMutableArray);
@ -197,10 +207,17 @@ function openBrowserWindow(cmdLine, urlOrUrlList, postData = null,
} else {
// Always pass at least 3 arguments to avoid the "|"-splitting behavior,
// ie. avoid the loadOneOrMoreURIs function.
// Also, we need to pass the triggering principal.
args = [urlOrUrlList,
null, // charset
null, // referer
postData];
postData,
undefined, // allowThirdPartyFixup; this would be `false` but that
// needs a conversion. Hopefully bug 1485961 will fix.
undefined, // referrer policy
undefined, // user context id
null, // origin principal
triggeringPrincipal];
}
if (cmdLine.state == Ci.nsICommandLine.STATE_INITIAL_LAUNCH) {
@ -257,7 +274,7 @@ function openPreferences(cmdLine, extraArgs) {
} else {
Services.telemetry.getHistogramById("FX_PREFERENCES_OPENED_VIA").add("other");
}
openBrowserWindow(cmdLine, "about:preferences");
openBrowserWindow(cmdLine, gSystemPrincipal, "about:preferences");
}
function doSearch(searchTerm, cmdLine) {
@ -270,7 +287,7 @@ function doSearch(searchTerm, cmdLine) {
// XXXbsmedberg: use handURIToExistingBrowser to obey tabbed-browsing
// preferences, but need nsIBrowserDOMWindow extensions
openBrowserWindow(cmdLine, submission.uri.spec, submission.postData);
openBrowserWindow(cmdLine, gSystemPrincipal, submission.uri.spec, submission.postData);
}
function nsBrowserContentHandler() {
@ -295,7 +312,7 @@ nsBrowserContentHandler.prototype = {
/* nsICommandLineHandler */
handle: function bch_handle(cmdLine) {
if (cmdLine.handleFlag("browser", false)) {
openBrowserWindow(cmdLine);
openBrowserWindow(cmdLine, gSystemPrincipal);
cmdLine.preventDefault = true;
}
@ -316,7 +333,7 @@ nsBrowserContentHandler.prototype = {
let uri = resolveURIInternal(cmdLine, uriparam);
if (!shouldLoadURI(uri))
continue;
openBrowserWindow(cmdLine, uri.spec);
openBrowserWindow(cmdLine, gSystemPrincipal, uri.spec);
cmdLine.preventDefault = true;
}
} catch (e) {
@ -328,7 +345,7 @@ nsBrowserContentHandler.prototype = {
let uri = resolveURIInternal(cmdLine, uriparam);
handURIToExistingBrowser(uri, Ci.nsIBrowserDOMWindow.OPEN_NEWTAB,
cmdLine, false,
Services.scriptSecurityManager.getSystemPrincipal());
gSystemPrincipal);
cmdLine.preventDefault = true;
}
} catch (e) {
@ -391,8 +408,7 @@ nsBrowserContentHandler.prototype = {
resolvedURI = resolveURIInternal(cmdLine, privateWindowParam);
}
handURIToExistingBrowser(resolvedURI, Ci.nsIBrowserDOMWindow.OPEN_NEWTAB,
cmdLine, forcePrivate,
Services.scriptSecurityManager.getSystemPrincipal());
cmdLine, forcePrivate, gSystemPrincipal);
cmdLine.preventDefault = true;
}
} catch (e) {
@ -401,7 +417,7 @@ nsBrowserContentHandler.prototype = {
}
// NS_ERROR_INVALID_ARG is thrown when flag exists, but has no param.
if (cmdLine.handleFlag("private-window", false)) {
openBrowserWindow(cmdLine, "about:privatebrowsing", null,
openBrowserWindow(cmdLine, gSystemPrincipal, "about:privatebrowsing", null,
PrivateBrowsingUtils.enabled);
cmdLine.preventDefault = true;
}
@ -434,7 +450,7 @@ nsBrowserContentHandler.prototype = {
if (fileParam) {
var file = cmdLine.resolveFile(fileParam);
var fileURI = Services.io.newFileURI(file);
openBrowserWindow(cmdLine, fileURI.spec);
openBrowserWindow(cmdLine, gSystemPrincipal, fileURI.spec);
cmdLine.preventDefault = true;
}
@ -663,7 +679,7 @@ function handURIToExistingBrowser(uri, location, cmdLine, forcePrivate, triggeri
var navWin = BrowserWindowTracker.getTopWindow({private: allowPrivate});
if (!navWin) {
// if we couldn't load it in an existing window, open a new one
openBrowserWindow(cmdLine, uri.spec, null, forcePrivate);
openBrowserWindow(cmdLine, triggeringPrincipal, uri.spec, null, forcePrivate);
return;
}
@ -744,8 +760,7 @@ nsDefaultCommandLineHandler.prototype = {
// current tab, new tab, or new window as prefs determine.
try {
handURIToExistingBrowser(urilist[0], Ci.nsIBrowserDOMWindow.OPEN_DEFAULTWINDOW,
cmdLine, false,
Services.scriptSecurityManager.getSystemPrincipal());
cmdLine, false, gSystemPrincipal);
return;
} catch (e) {
}
@ -753,7 +768,7 @@ nsDefaultCommandLineHandler.prototype = {
var URLlist = urilist.filter(shouldLoadURI).map(u => u.spec);
if (URLlist.length) {
openBrowserWindow(cmdLine, URLlist);
openBrowserWindow(cmdLine, gSystemPrincipal, URLlist);
}
} else if (!cmdLine.preventDefault) {
@ -767,7 +782,7 @@ nsDefaultCommandLineHandler.prototype = {
return;
}
}
openBrowserWindow(cmdLine);
openBrowserWindow(cmdLine, gSystemPrincipal);
} else {
// Need a better solution in the future to avoid opening the blank window
// when command line parameters say we are not going to show a browser

View file

@ -33,6 +33,8 @@ Preferences.addAll([
{ id: "network.proxy.backup.ssl_port", type: "int" },
{ id: "network.proxy.backup.socks", type: "string" },
{ id: "network.proxy.backup.socks_port", type: "int" },
{ id: "network.trr.mode", type: "int" },
{ id: "network.trr.uri", type: "string" },
]);
window.addEventListener("DOMContentLoaded", () => {
@ -277,5 +279,35 @@ var gConnectionsDialog = {
handleControllingExtension(PREF_SETTING_TYPE, PROXY_KEY)
.then(setInputsDisabledState);
}
},
isDnsOverHttpsEnabled() {
// values outside 1:4 are considered falsey/disabled in this context
let trrPref = Preferences.get("network.trr.mode");
let enabled = trrPref.value > 0 && trrPref.value < 5;
return enabled;
},
readDnsOverHttpsMode() {
// called to update checked element property to reflect current pref value
let enabled = this.isDnsOverHttpsEnabled();
let uriPref = Preferences.get("network.trr.uri");
uriPref.disabled = !enabled;
return enabled;
},
writeDnsOverHttpsMode() {
// called to update pref with user change
let trrModeCheckbox = document.getElementById("networkDnsOverHttps");
// we treat checked/enabled as mode 2
return trrModeCheckbox.checked ? 2 : 0;
},
writeDnsOverHttpsUri() {
// called to update pref with user input
let input = document.getElementById("networkDnsOverHttpsUrl");
let uriString = input.value.trim();
// turn an empty string into `undefined` to clear the pref back to the default
return uriString.length ? uriString : undefined;
}
};

View file

@ -148,6 +148,18 @@
<checkbox id="networkProxySOCKSRemoteDNS"
preference="network.proxy.socks_remote_dns"
data-l10n-id="connection-proxy-socks-remote-dns" />
<checkbox id="networkDnsOverHttps"
data-l10n-id="connection-dns-over-https"
preference="network.trr.mode"
onsyncfrompreference="return gConnectionsDialog.readDnsOverHttpsMode();"
onsynctopreference="return gConnectionsDialog.writeDnsOverHttpsMode()" />
<hbox class="indent" flex="1" align="center">
<label control="networkDnsOverHttpsUrl" data-l10n-id="connection-dns-over-https-url"
data-l10n-attrs="tooltiptext"/>
<textbox id="networkDnsOverHttpsUrl" flex="1" preference="network.trr.uri"
placeholder="https://doh.example.com/dns-query"
onsynctopreference="return gConnectionsDialog.writeDnsOverHttpsUri()" />
</hbox>
<separator/>
</vbox>
</dialog>

View file

@ -652,12 +652,12 @@
class="subcategory"
hidden="true"
data-category="paneGeneral">
<label class="header-name" flex="1" data-l10n-id="network-proxy-title"/>
<label class="header-name" flex="1" data-l10n-id="network-settings-title"/>
</hbox>
<!-- Network Proxy-->
<!-- Network Settings-->
<groupbox id="connectionGroup" data-category="paneGeneral" hidden="true">
<caption class="search-header" hidden="true"><label data-l10n-id="network-proxy-title"/></caption>
<caption class="search-header" hidden="true"><label data-l10n-id="network-settings-title"/></caption>
<hbox align="center">
<hbox align="center" flex="1">
@ -694,7 +694,9 @@
connection-proxy-autotype.label,
connection-proxy-reload.label,
connection-proxy-autologin.label,
connection-proxy-socks-remote-dns.label
connection-proxy-socks-remote-dns.label,
connection-dns-over-https,
connection-dns-over-https-url
" />
</hbox>
</hbox>

View file

@ -42,6 +42,7 @@ skip-if = os != "win" # Windows-specific handler application selection dialog
[browser_connection_bug388287.js]
[browser_connection_bug1445991.js]
skip-if = (verify && debug && (os == 'linux' || os == 'mac'))
[browser_connection_dnsoverhttps.js]
[browser_contentblocking.js]
[browser_cookies_exceptions.js]
[browser_defaultbrowser_alwayscheck.js]

View file

@ -0,0 +1,172 @@
ChromeUtils.import("resource://gre/modules/Services.jsm");
const SUBDIALOG_URL = "chrome://browser/content/preferences/connection.xul";
const TRR_MODE_PREF = "network.trr.mode";
const TRR_URI_PREF = "network.trr.uri";
const modeCheckboxSelector = "#networkDnsOverHttps";
const uriTextboxSelector = "#networkDnsOverHttpsUrl";
const defaultPrefValues = Object.freeze({
[TRR_MODE_PREF]: 0,
[TRR_URI_PREF]: "",
});
function resetPrefs() {
Services.prefs.clearUserPref(TRR_MODE_PREF);
Services.prefs.clearUserPref(TRR_URI_PREF);
}
async function setup() {
await new Promise(res => {
resetPrefs();
open_preferences(res);
});
}
async function openConnectionsSubDialog() {
/*
The connection dialog has type="child", So it has to be opened as a sub dialog
of the main pref tab. Prefs only get updated after the subdialog is confirmed & closed
*/
let dialog = await openAndLoadSubDialog(SUBDIALOG_URL);
ok(dialog, "connection window opened");
return dialog;
}
function waitForPrefObserver(name) {
return new Promise(resolve => {
const observer = {
observe(aSubject, aTopic, aData) {
if (aData == name) {
Services.prefs.removeObserver(name, observer);
resolve();
}
}
};
Services.prefs.addObserver(name, observer);
});
}
async function testWithProperties(props) {
info("testing with " + JSON.stringify(props));
if (props.hasOwnProperty(TRR_MODE_PREF)) {
Services.prefs.setIntPref(TRR_MODE_PREF, props[TRR_MODE_PREF]);
}
if (props.hasOwnProperty(TRR_URI_PREF)) {
Services.prefs.setStringPref(TRR_URI_PREF, props[TRR_URI_PREF]);
}
let dialog = await openConnectionsSubDialog();
let doc = dialog.document;
let dialogClosingPromise = BrowserTestUtils.waitForEvent(doc.documentElement,
"dialogclosing");
let modeCheckbox = doc.querySelector(modeCheckboxSelector);
let uriTextbox = doc.querySelector(uriTextboxSelector);
let uriPrefChangedPromise;
let modePrefChangedPromise;
if (props.hasOwnProperty("expectedModeChecked")) {
is(modeCheckbox.checked, props.expectedModeChecked, "mode checkbox has expected checked state");
}
if (props.hasOwnProperty("expectedUriValue")) {
is(uriTextbox.value, props.expectedUriValue, "URI textbox has expected value");
}
if (props.clickMode) {
modePrefChangedPromise = waitForPrefObserver(TRR_MODE_PREF);
modeCheckbox.scrollIntoView();
EventUtils.synthesizeMouseAtCenter(modeCheckbox, {},
modeCheckbox.ownerGlobal);
}
if (props.hasOwnProperty("inputUriKeys")) {
uriPrefChangedPromise = waitForPrefObserver(TRR_URI_PREF);
uriTextbox.focus();
// delete whatever is in there
EventUtils.synthesizeKey("a", { accelKey: true }, uriTextbox.ownerGlobal);
EventUtils.synthesizeKey("KEY_Backspace", {}, uriTextbox.ownerGlobal);
// and type in the new stuff
EventUtils.synthesizeKey(props.inputUriKeys, {}, uriTextbox.ownerGlobal);
let changePromise = waitForEvent(uriTextbox, "change");
// move focus to trigger change event
modeCheckbox.focus();
await changePromise;
}
doc.documentElement.acceptDialog();
let dialogClosingEvent = await dialogClosingPromise;
ok(dialogClosingEvent, "connection window closed");
await Promise.all([uriPrefChangedPromise, modePrefChangedPromise]);
if (props.hasOwnProperty("expectedFinalUriPref")) {
let uriPref = Services.prefs.getStringPref(TRR_URI_PREF);
is(uriPref, props.expectedFinalUriPref, "uri pref ended up with the expected value");
}
if (props.hasOwnProperty("expectedModePref")) {
let modePref = Services.prefs.getIntPref(TRR_MODE_PREF);
is(modePref, props.expectedModePref, "mode pref ended up with the expected value");
}
}
registerCleanupFunction(resetPrefs);
add_task(async function default_values() {
let uriPref = Services.prefs.getStringPref(TRR_URI_PREF);
let modePref = Services.prefs.getIntPref(TRR_MODE_PREF);
is(modePref, defaultPrefValues[TRR_MODE_PREF],
`Actual value of ${TRR_MODE_PREF} matches expected default value`);
is(uriPref, defaultPrefValues[TRR_URI_PREF],
`Actual value of ${TRR_MODE_PREF} matches expected default value`);
});
let testVariations = [
// verify state with defaults
{ expectedModePref: 0, expectedUriValue: "" },
// verify each of the modes maps to the correct checked state
{ [TRR_MODE_PREF]: 0, expectedModeChecked: false },
{ [TRR_MODE_PREF]: 1, expectedModeChecked: true },
{ [TRR_MODE_PREF]: 2, expectedModeChecked: true },
{ [TRR_MODE_PREF]: 3, expectedModeChecked: true },
{ [TRR_MODE_PREF]: 4, expectedModeChecked: true },
{ [TRR_MODE_PREF]: 5, expectedModeChecked: false },
// verify an out of bounds mode value maps to the correct checked state
{ [TRR_MODE_PREF]: 77, expectedModeChecked: false },
// verify toggling the checkbox gives the right outcomes
{ clickMode: true, expectedModeValue: 2, expectedUriValue: "" },
{
[TRR_MODE_PREF]: 4,
expectedModeChecked: true, clickMode: true, expectedModePref: 0,
},
{
[TRR_MODE_PREF]: 2, [TRR_URI_PREF]: "https://example.com",
expectedModeValue: true, expectedUriValue: "https://example.com",
},
{
clickMode: true, inputUriKeys: "https://example.com",
expectedModePref: 2, expectedFinalUriPref: "https://example.com",
},
// verify the uri can be cleared
{
[TRR_MODE_PREF]: 2, [TRR_URI_PREF]: "https://example.com",
expectedUriValue: "https://example.com", inputUriKeys: "", expectedFinalUriPref: "",
},
// verify uri gets sanitized
{
clickMode: true, inputUriKeys: " https://example.com ",
expectedModePref: 2, expectedFinalUriPref: "https://example.com",
},
];
for (let props of testVariations) {
add_task(async function() {
await setup();
await testWithProperties(props);
resetPrefs();
gBrowser.removeCurrentTab();
});
}

View file

@ -81,3 +81,11 @@ connection-proxy-autologin =
connection-proxy-socks-remote-dns =
.label = Proxy DNS when using SOCKS v5
.accesskey = D
connection-dns-over-https =
.label = Enable DNS over HTTPS
.accesskey = H
connection-dns-over-https-url = URL
.accesskey = U
.tooltiptext = URL for resolving DNS over HTTPS

View file

@ -405,7 +405,7 @@ browsing-search-on-start-typing =
## General Section - Proxy
network-proxy-title = Network Proxy
network-settings-title = Network Settings
network-proxy-connection-description = Configure how { -brand-short-name } connects to the internet.

View file

@ -938,7 +938,7 @@ you can use these alternative items. Otherwise, their values should be empty. -
the type of content blocking is currently not enabled. -->
<!ENTITY contentBlocking.fastBlock.add.label "Add Blocking…">
<!ENTITY contentBlocking.trackingProtection.label "Trackers">
<!ENTITY contentBlocking.trackingProtection2.label "All Detected Trackers">
<!-- LOCALIZATION NOTE (contentBlocking.trackingProtection.blocked.label):
This label signals that this type of content blocking is turned
ON and is successfully blocking tracker content, so this is
@ -964,12 +964,11 @@ you can use these alternative items. Otherwise, their values should be empty. -
the type of content blocking is currently not enabled. -->
<!ENTITY contentBlocking.3rdPartyCookies.add.label "Add Blocking…">
<!ENTITY contentBlocking.openBreakageReportView.label "Report Problems">
<!ENTITY contentBlocking.openBreakageReportView2.label "Report a problem">
<!ENTITY contentBlocking.breakageReportView.label "Report Problems">
<!ENTITY contentBlocking.breakageReportView.description "Content blocking can cause problems with some websites. When you report problems, youll help make &brandShortName; better for everyone. (This will send a URL as well as information about your privacy and content blocking settings to Mozilla.)">
<!ENTITY contentBlocking.breakageReportView2.description "Content blocking can cause problems with some websites. When you report problems, youll help make &brandShortName; better for everyone. (This will send a URL as well as information about your browser settings to Mozilla.)">
<!ENTITY contentBlocking.breakageReportView.learnMore "Learn More">
<!ENTITY contentBlocking.breakageReportView.collection.url.label "URL">
<!ENTITY contentBlocking.breakageReportView.collection.userAgent.label "&brandShortName; Version Number">
<!ENTITY contentBlocking.breakageReportView.collection.comments.label "What problems did you have? (Optional)">
<!ENTITY contentBlocking.breakageReportView.sendReport.label "Send Report">
<!ENTITY contentBlocking.breakageReportView.cancel.label "Cancel">

View file

@ -562,6 +562,10 @@ description#identity-popup-content-verifier,
margin-top: -0.1em;
}
.identity-popup-content-blocking-category-label {
max-width: 200px;
}
.identity-popup-content-blocking-category-label,
.identity-popup-permission-label {
margin-inline-start: 1em;

View file

@ -474,7 +474,8 @@ if __name__ == "__main__":
clang_repo = config["clang_repo"]
extra_repo = config.get("extra_repo")
lld_repo = config.get("lld_repo")
compiler_repo = config["compiler_repo"]
# On some packages we don't use compiler_repo
compiler_repo = config.get("compiler_repo")
libcxx_repo = config["libcxx_repo"]
libcxxabi_repo = config.get("libcxxabi_repo")
stages = 3
@ -543,7 +544,8 @@ if __name__ == "__main__":
if not args.skip_checkout:
checkout_or_update(llvm_repo, llvm_source_dir)
checkout_or_update(clang_repo, clang_source_dir)
checkout_or_update(compiler_repo, compiler_rt_source_dir)
if compiler_repo is not None:
checkout_or_update(compiler_repo, compiler_rt_source_dir)
checkout_or_update(libcxx_repo, libcxx_source_dir)
if lld_repo:
checkout_or_update(lld_repo, lld_source_dir)

View file

@ -1,23 +0,0 @@
Backport cxx14 default dialect flag from clang 6.0.0 trunk to 5.0.1
Index: lib/Frontend/CompilerInvocation.cpp
===================================================================
--- a/clang/lib/Frontend/CompilerInvocation.cpp (revision 320871)
+++ b/clang/lib/Frontend/CompilerInvocation.cpp (working copy)
@@ -1690,11 +1690,11 @@
break;
case InputKind::CXX:
case InputKind::ObjCXX:
- // The PS4 uses C++11 as the default C++ standard.
- if (T.isPS4())
- LangStd = LangStandard::lang_gnucxx11;
- else
- LangStd = LangStandard::lang_gnucxx98;
+#if defined(CLANG_DEFAULT_STD_CXX)
+ LangStd = CLANG_DEFAULT_STD_CXX;
+#else
+ LangStd = LangStandard::lang_gnucxx14;
+#endif
break;
case InputKind::RenderScript:
LangStd = LangStandard::lang_c99;

View file

@ -1,22 +1,20 @@
{
"llvm_revision": "320871",
"llvm_revision": "340491",
"stages": "1",
"build_libcxx": true,
"build_type": "Release",
"assertions": false,
"build_clang_tidy": true,
"llvm_repo": "https://llvm.org/svn/llvm-project/llvm/tags/RELEASE_501/final",
"clang_repo": "https://llvm.org/svn/llvm-project/cfe/tags/RELEASE_501/final",
"extra_repo": "https://llvm.org/svn/llvm-project/clang-tools-extra/tags/RELEASE_501/final",
"compiler_repo": "https://llvm.org/svn/llvm-project/compiler-rt/tags/RELEASE_501/final",
"libcxx_repo": "https://llvm.org/svn/llvm-project/libcxx/tags/RELEASE_501/final",
"libcxxabi_repo": "https://llvm.org/svn/llvm-project/libcxxabi/tags/RELEASE_501/final",
"llvm_repo": "https://llvm.org/svn/llvm-project/llvm/tags/RELEASE_700/rc2/",
"clang_repo": "https://llvm.org/svn/llvm-project/cfe/tags/RELEASE_700/rc2/",
"extra_repo": "https://llvm.org/svn/llvm-project/clang-tools-extra/tags/RELEASE_700/rc2/",
"libcxx_repo": "https://llvm.org/svn/llvm-project/libcxx/tags/RELEASE_700/rc2/",
"libcxxabi_repo": "https://llvm.org/svn/llvm-project/libcxxabi/tags/RELEASE_700/rc2/",
"python_path": "/usr/bin/python2.7",
"gcc_dir": "/builds/worker/workspace/build/src/gcc",
"cc": "/builds/worker/workspace/build/src/gcc/bin/gcc",
"cxx": "/builds/worker/workspace/build/src/gcc/bin/g++",
"as": "/builds/worker/workspace/build/src/gcc/bin/gcc",
"patches": [
"clang-tidy-cxx14.patch"
]
}
}

View file

@ -1,17 +1,16 @@
{
"llvm_revision": "320871",
"llvm_revision": "340491",
"stages": "1",
"build_libcxx": true,
"build_type": "Release",
"assertions": false,
"build_clang_tidy": true,
"osx_cross_compile": true,
"llvm_repo": "https://llvm.org/svn/llvm-project/llvm/tags/RELEASE_501/final",
"clang_repo": "https://llvm.org/svn/llvm-project/cfe/tags/RELEASE_501/final",
"extra_repo": "https://llvm.org/svn/llvm-project/clang-tools-extra/tags/RELEASE_501/final",
"compiler_repo": "https://llvm.org/svn/llvm-project/compiler-rt/tags/RELEASE_501/final",
"libcxx_repo": "https://llvm.org/svn/llvm-project/libcxx/tags/RELEASE_501/final",
"libcxxabi_repo": "https://llvm.org/svn/llvm-project/libcxxabi/tags/RELEASE_501/final",
"llvm_repo": "https://llvm.org/svn/llvm-project/llvm/tags/RELEASE_700/rc2",
"clang_repo": "https://llvm.org/svn/llvm-project/cfe/tags/RELEASE_700/rc2",
"extra_repo": "https://llvm.org/svn/llvm-project/clang-tools-extra/tags/RELEASE_700/rc2",
"libcxx_repo": "https://llvm.org/svn/llvm-project/libcxx/tags/RELEASE_700/rc2",
"libcxxabi_repo": "https://llvm.org/svn/llvm-project/libcxxabi/tags/RELEASE_700/rc2",
"python_path": "/usr/bin/python2.7",
"gcc_dir": "/builds/worker/workspace/build/src/gcc",
"cc": "/builds/worker/workspace/build/src/clang/bin/clang",
@ -19,11 +18,8 @@
"as": "/builds/worker/workspace/build/src/clang/bin/clang",
"ar": "/builds/worker/workspace/build/src/cctools/bin/x86_64-apple-darwin11-ar",
"ranlib": "/builds/worker/workspace/build/src/cctools/bin/x86_64-apple-darwin11-ranlib",
"libtool": "/builds/worker/workspace/build/src/cctools/bin/x86_64-apple-darwin11-libtool",
"ld": "/builds/worker/workspace/build/src/clang/bin/clang",
"patches": [
"llvm-debug-frame-for-5.patch",
"compiler-rt-cross-compile.patch",
"compiler-rt-no-codesign.patch",
"clang-tidy-cxx14.patch"
]
}

View file

@ -1,19 +1,18 @@
{
"llvm_revision": "320871",
"llvm_revision": "340491",
"stages": "1",
"build_libcxx": false,
"build_type": "Release",
"assertions": false,
"build_clang_tidy": true,
"llvm_repo": "https://llvm.org/svn/llvm-project/llvm/tags/RELEASE_501/final",
"clang_repo": "https://llvm.org/svn/llvm-project/cfe/tags/RELEASE_501/final",
"extra_repo": "https://llvm.org/svn/llvm-project/clang-tools-extra/tags/RELEASE_501/final",
"compiler_repo": "https://llvm.org/svn/llvm-project/compiler-rt/tags/RELEASE_501/final",
"llvm_repo": "https://llvm.org/svn/llvm-project/llvm/tags/RELEASE_700/rc2",
"clang_repo": "https://llvm.org/svn/llvm-project/cfe/tags/RELEASE_700/rc2",
"extra_repo": "https://llvm.org/svn/llvm-project/clang-tools-extra/tags/RELEASE_700/rc2",
"compiler_repo": "https://llvm.org/svn/llvm-project/compiler-rt/tags/RELEASE_700/rc2",
"libcxx_repo": "https://llvm.org/svn/llvm-project/libcxx/trunk",
"python_path": "c:/mozilla-build/python/python.exe",
"cc": "cl.exe",
"cxx": "cl.exe",
"patches": [
"clang-tidy-cxx14.patch"
]
}

View file

@ -1,20 +1,19 @@
{
"llvm_revision": "320871",
"llvm_revision": "340491",
"stages": "1",
"build_libcxx": false,
"build_type": "Release",
"assertions": false,
"build_clang_tidy": true,
"llvm_repo": "https://llvm.org/svn/llvm-project/llvm/tags/RELEASE_501/final",
"clang_repo": "https://llvm.org/svn/llvm-project/cfe/tags/RELEASE_501/final",
"extra_repo": "https://llvm.org/svn/llvm-project/clang-tools-extra/tags/RELEASE_501/final",
"compiler_repo": "https://llvm.org/svn/llvm-project/compiler-rt/tags/RELEASE_501/final",
"llvm_repo": "https://llvm.org/svn/llvm-project/llvm/tags/RELEASE_700/rc2",
"clang_repo": "https://llvm.org/svn/llvm-project/cfe/tags/RELEASE_700/rc2",
"extra_repo": "https://llvm.org/svn/llvm-project/clang-tools-extra/tags/RELEASE_700/rc2",
"compiler_repo": "https://llvm.org/svn/llvm-project/compiler-rt/tags/RELEASE_700/rc2",
"libcxx_repo": "https://llvm.org/svn/llvm-project/libcxx/trunk",
"python_path": "c:/mozilla-build/python/python.exe",
"cc": "cl.exe",
"cxx": "cl.exe",
"ml": "ml64.exe",
"patches": [
"clang-tidy-cxx14.patch"
]
}

View file

@ -1,13 +0,0 @@
Index: lib/CodeGen/AsmPrinter/AsmPrinter.cpp
===================================================================
--- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp (revision 226419)
+++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp (working copy)
@@ -210,6 +210,8 @@
OutStreamer->EmitFileDirective(M.getSourceFileName());
}
+ OutStreamer->EmitCFISections(true, true);
+
GCModuleInfo *MI = getAnalysisIfAvailable<GCModuleInfo>();
assert(MI && "AsmPrinter didn't require GCModuleInfo?");
for (auto &I : *MI)

View file

@ -24,10 +24,13 @@ void ExplicitImplicitChecker::check(const MatchFinder::MatchResult &Result) {
const CXXRecordDecl *Declaration =
Result.Nodes.getNodeAs<CXXRecordDecl>("class");
FixItHint FixItHint =
FixItHint::CreateInsertion(Ctor->getLocation(), "explicit ");
diag(Ctor->getLocation(), "bad implicit conversion constructor for %0",
DiagnosticIDs::Error)
<< Declaration->getDeclName();
diag(Ctor->getLocation(),
"consider adding the explicit keyword to the constructor",
DiagnosticIDs::Note);
DiagnosticIDs::Note)
<< FixItHint;
}

View file

@ -114,7 +114,7 @@ def do_import(mozilla_path, clang_tidy_path):
'LINK_LIBS', 'clangTidyMozillaModule')
add_item_to_cmake_section(os.path.join(module_path, '..', 'tool',
'CMakeLists.txt'),
'target_link_libraries', 'clangTidyMozillaModule')
'PRIVATE', 'clangTidyMozillaModule')
with open(os.path.join(module_path, '..', 'CMakeLists.txt'), 'a') as f:
f.write('add_subdirectory(%s)\n' % module)
with open(os.path.join(module_path, '..', 'tool', 'ClangTidyMain.cpp'), 'a') as f:

View file

@ -68,7 +68,6 @@ HOST_COMPILE_FLAGS['VISIBILITY'] = []
# libc++ is required to build plugins against clang on OS X.
if CONFIG['HOST_OS_ARCH'] == 'Darwin':
HOST_CXXFLAGS += ['-stdlib=libc++']
HOST_LDFLAGS += ['-lc++']
DIRS += [
'tests',

View file

@ -0,0 +1,417 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
var EXPORTED_SYMBOLS = ["TelemetryStopwatch"];
ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.defineModuleGetter(this, "Log",
"resource://gre/modules/Log.jsm");
// Weak map does not allow using null objects as keys. These objects are used
// as 'null' placeholders.
const NULL_OBJECT = {};
const NULL_KEY = {};
/**
* Timers is a variation of a Map used for storing information about running
* Stopwatches. Timers has the following data structure:
*
* {
* "HISTOGRAM_NAME": WeakMap {
* Object || NULL_OBJECT: Map {
* "KEY" || NULL_KEY: startTime
* ...
* }
* ...
* }
* ...
* }
*
*
* @example
* // Stores current time for a keyed histogram "PLAYING_WITH_CUTE_ANIMALS".
* Timers.put("PLAYING_WITH_CUTE_ANIMALS", null, "CATS", Date.now());
*
* @example
* // Returns information about a simple Stopwatch.
* let startTime = Timers.get("PLAYING_WITH_CUTE_ANIMALS", null, "CATS");
*/
const Timers = {
_timers: new Map(),
_validTypes(histogram, obj, key) {
const nonEmptyString = value => {
return typeof value === "string" && value !== "" && value.length > 0;
};
return nonEmptyString(histogram) &&
typeof obj == "object" &&
(key === NULL_KEY || nonEmptyString(key));
},
get(histogram, obj, key) {
key = key === null ? NULL_KEY : key;
obj = obj || NULL_OBJECT;
if (!this.has(histogram, obj, key)) {
return null;
}
return this._timers.get(histogram).get(obj).get(key);
},
put(histogram, obj, key, startTime) {
key = key === null ? NULL_KEY : key;
obj = obj || NULL_OBJECT;
if (!this._validTypes(histogram, obj, key)) {
return false;
}
const objectMap = this._timers.get(histogram) || new WeakMap();
const keyedInfo = objectMap.get(obj) || new Map();
keyedInfo.set(key, startTime);
objectMap.set(obj, keyedInfo);
this._timers.set(histogram, objectMap);
return true;
},
has(histogram, obj, key) {
key = key === null ? NULL_KEY : key;
obj = obj || NULL_OBJECT;
return this._timers.has(histogram) &&
this._timers.get(histogram).has(obj) &&
this._timers.get(histogram).get(obj).has(key);
},
delete(histogram, obj, key) {
key = key === null ? NULL_KEY : key;
obj = obj || NULL_OBJECT;
if (!this.has(histogram, obj, key)) {
return false;
}
const objectMap = this._timers.get(histogram);
const keyedInfo = objectMap.get(obj);
if (keyedInfo.size > 1) {
keyedInfo.delete(key);
return true;
}
objectMap.delete(obj);
// NOTE:
// We never delete empty objecMaps from this._timers because there is no
// nice solution for tracking the number of objects in a WeakMap.
// WeakMap is not enumerable, so we can't deterministically say when it's
// empty. We accept that trade-off here, given that entries for short-lived
// objects will go away when they are no longer referenced
return true;
}
};
var TelemetryStopwatch = {
/**
* Starts a timer associated with a telemetry histogram. The timer can be
* directly associated with a histogram, or with a pair of a histogram and
* an object.
*
* @param {String} aHistogram - a string which must be a valid histogram name.
*
* @param {Object} aObj - Optional parameter. If specified, the timer is
* associated with this object, meaning that multiple
* timers for the same histogram may be run
* concurrently, as long as they are associated with
* different objects.
*
* @returns {Boolean} True if the timer was successfully started, false
* otherwise. If a timer already exists, it can't be
* started again, and the existing one will be cleared in
* order to avoid measurements errors.
*/
start(aHistogram, aObj) {
return TelemetryStopwatchImpl.start(aHistogram, aObj, null);
},
/**
* Returns whether a timer associated with a telemetry histogram is currently
* running. The timer can be directly associated with a histogram, or with a
* pair of a histogram and an object.
*
* @param {String} aHistogram - a string which must be a valid histogram name.
*
* @param {Object} aObj - Optional parameter. If specified, the timer is
* associated with this object, meaning that multiple
* timers for the same histogram may be run
* concurrently, as long as they are associated with
* different objects.
*
* @returns {Boolean} True if the timer exists and is currently running.
*/
running(aHistogram, aObj) {
return TelemetryStopwatchImpl.running(aHistogram, aObj, null);
},
/**
* Deletes the timer associated with a telemetry histogram. The timer can be
* directly associated with a histogram, or with a pair of a histogram and
* an object. Important: Only use this method when a legitimate cancellation
* should be done.
*
* @param {String} aHistogram - a string which must be a valid histogram name.
*
* @param {Object} aObj - Optional parameter. If specified, the timer is
* associated with this object, meaning that multiple
* timers or a same histogram may be run concurrently,
* as long as they are associated with different
* objects.
*
* @returns {Boolean} True if the timer exist and it was cleared, False
* otherwise.
*/
cancel(aHistogram, aObj) {
return TelemetryStopwatchImpl.cancel(aHistogram, aObj, null);
},
/**
* Returns the elapsed time for a particular stopwatch. Primarily for
* debugging purposes. Must be called prior to finish.
*
* @param {String} aHistogram - a string which must be a valid histogram name.
* If an invalid name is given, the function will
* throw.
*
* @param (Object) aObj - Optional parameter which associates the histogram
* timer with the given object.
*
* @param {Boolean} aCanceledOkay - Optional parameter which will suppress any
* warnings that normally fire when a stopwatch
* is finished after being cancelled. Defaults
* to false.
*
* @returns {Integer} time in milliseconds or -1 if the stopwatch was not
* found.
*/
timeElapsed(aHistogram, aObj, aCanceledOkay) {
return TelemetryStopwatchImpl.timeElapsed(aHistogram, aObj, null,
aCanceledOkay);
},
/**
* Stops the timer associated with the given histogram (and object),
* calculates the time delta between start and finish, and adds the value
* to the histogram.
*
* @param {String} aHistogram - a string which must be a valid histogram name.
*
* @param {Object} aObj - Optional parameter which associates the histogram
* timer with the given object.
*
* @param {Boolean} aCanceledOkay - Optional parameter which will suppress any
* warnings that normally fire when a stopwatch
* is finished after being cancelled. Defaults
* to false.
*
* @returns {Boolean} True if the timer was succesfully stopped and the data
* was added to the histogram, False otherwise.
*/
finish(aHistogram, aObj, aCanceledOkay) {
return TelemetryStopwatchImpl.finish(aHistogram, aObj, null, aCanceledOkay);
},
/**
* Starts a timer associated with a keyed telemetry histogram. The timer can
* be directly associated with a histogram and its key. Similarly to
* @see{TelemetryStopwatch.stat} the histogram and its key can be associated
* with an object. Each key may have multiple associated objects and each
* object can be associated with multiple keys.
*
* @param {String} aHistogram - a string which must be a valid histogram name.
*
* @param {String} aKey - a string which must be a valid histgram key.
*
* @param {Object} aObj - Optional parameter. If specified, the timer is
* associated with this object, meaning that multiple
* timers for the same histogram may be run
* concurrently,as long as they are associated with
* different objects.
*
* @returns {Boolean} True if the timer was successfully started, false
* otherwise. If a timer already exists, it can't be
* started again, and the existing one will be cleared in
* order to avoid measurements errors.
*/
startKeyed(aHistogram, aKey, aObj) {
return TelemetryStopwatchImpl.start(aHistogram, aObj, aKey);
},
/**
* Returns whether a timer associated with a telemetry histogram is currently
* running. Similarly to @see{TelemetryStopwatch.running} the timer and its
* key can be associated with an object. Each key may have multiple associated
* objects and each object can be associated with multiple keys.
*
* @param {String} aHistogram - a string which must be a valid histogram name.
*
* @param {String} aKey - a string which must be a valid histgram key.
*
* @param {Object} aObj - Optional parameter. If specified, the timer is
* associated with this object, meaning that multiple
* timers for the same histogram may be run
* concurrently, as long as they are associated with
* different objects.
*
* @returns {Boolean} True if the timer exists and is currently running.
*/
runningKeyed(aHistogram, aKey, aObj) {
return TelemetryStopwatchImpl.running(aHistogram, aObj, aKey);
},
/**
* Deletes the timer associated with a keyed histogram. Important: Only use
* this method when a legitimate cancellation should be done.
*
* @param {String} aHistogram - a string which must be a valid histogram name.
*
* @param {String} aKey - a string which must be a valid histgram key.
*
* @param {Object} aObj - Optional parameter. If specified, the timer
* associated with this object is deleted.
*
* @return {Boolean} True if the timer exist and it was cleared, False
* otherwise.
*/
cancelKeyed(aHistogram, aKey, aObj) {
return TelemetryStopwatchImpl.cancel(aHistogram, aObj, aKey);
},
/**
* Returns the elapsed time for a particular stopwatch. Primarily for
* debugging purposes. Must be called prior to finish.
*
* @param {String} aHistogram - a string which must be a valid histogram name.
*
* @param {String} aKey - a string which must be a valid histgram key.
*
* @param {Object} aObj - Optional parameter. If specified, the timer
* associated with this object is used to calculate
* the elapsed time.
*
* @return {Integer} time in milliseconds or -1 if the stopwatch was not
* found.
*/
timeElapsedKeyed(aHistogram, aKey, aObj, aCanceledOkay) {
return TelemetryStopwatchImpl.timeElapsed(aHistogram, aObj, aKey,
aCanceledOkay);
},
/**
* Stops the timer associated with the given keyed histogram (and object),
* calculates the time delta between start and finish, and adds the value
* to the keyed histogram.
*
* @param {String} aHistogram - a string which must be a valid histogram name.
*
* @param {String} aKey - a string which must be a valid histgram key.
*
* @param {Object} aObj - optional parameter which associates the histogram
* timer with the given object.
*
* @param {Boolean} aCanceledOkay - Optional parameter which will suppress any
* warnings that normally fire when a stopwatch
* is finished after being cancelled. Defaults
* to false.
*
* @returns {Boolean} True if the timer was succesfully stopped and the data
* was added to the histogram, False otherwise.
*/
finishKeyed(aHistogram, aKey, aObj, aCanceledOkay) {
return TelemetryStopwatchImpl.finish(aHistogram, aObj, aKey, aCanceledOkay);
},
/**
* Set the testing mode. Used by tests.
*/
setTestModeEnabled(testing) {
TelemetryStopwatchImpl.suppressErrors(true);
},
};
var TelemetryStopwatchImpl = {
// Suppress errors. Used when testing.
_suppressErrors: false,
suppressErrors(suppress) {
this._suppressErrors = suppress;
},
start(histogram, object, key) {
if (Timers.has(histogram, object, key)) {
Timers.delete(histogram, object, key);
if (!this._suppressErrors) {
Cu.reportError(`TelemetryStopwatch: key "${histogram}" was already ` +
"initialized");
}
return false;
}
return Timers.put(histogram, object, key, Cu.now());
},
running(histogram, object, key) {
return Timers.has(histogram, object, key);
},
cancel(histogram, object, key) {
return Timers.delete(histogram, object, key);
},
timeElapsed(histogram, object, key, aCanceledOkay) {
const startTime = Timers.get(histogram, object, key);
if (startTime === null) {
if (!aCanceledOkay && !this._suppressErrors) {
Cu.reportError("TelemetryStopwatch: requesting elapsed time for " +
`nonexisting stopwatch. Histogram: "${histogram}", ` +
`key: "${key}"`);
}
return -1;
}
try {
const delta = Cu.now() - startTime;
return Math.round(delta / 1000);
} catch (e) {
if (!this._suppressErrors) {
Cu.reportError("TelemetryStopwatch: failed to calculate elapsed time " +
`for Histogram: "${histogram}", key: "${key}", ` +
`exception: ${Log.exceptionStr(e)}`);
}
return -1;
}
},
finish(histogram, object, key, aCanceledOkay) {
const delta = this.timeElapsed(histogram, object, key, aCanceledOkay);
if (delta == -1) {
return false;
}
try {
if (key) {
Services.telemetry.getKeyedHistogramById(histogram).add(key, delta);
} else {
Services.telemetry.getHistogramById(histogram).add(delta);
}
} catch (e) {
if (!this._suppressErrors) {
Cu.reportError("TelemetryStopwatch: failed to update the Histogram " +
`"${histogram}", using key: "${key}", ` +
`exception: ${Log.exceptionStr(e)}`);
}
return false;
}
return Timers.delete(histogram, object, key);
}
};

View file

@ -49,6 +49,7 @@ DevToolsModules(
'stylesheet-utils.js',
'suggestion-picker.js',
'telemetry.js',
'TelemetryStopwatch.jsm',
'theme.js',
'undo.js',
'unicode-url.js',

View file

@ -11,7 +11,7 @@
"use strict";
const Services = require("Services");
const { TelemetryStopwatch } = require("resource://gre/modules/TelemetryStopwatch.jsm");
const { TelemetryStopwatch } = require("devtools/client/shared/TelemetryStopwatch.jsm");
const { getNthPathExcluding } = require("devtools/shared/platform/stack");
const { TelemetryEnvironment } = require("resource://gre/modules/TelemetryEnvironment.jsm");

View file

@ -485,7 +485,6 @@ SocketListener.prototype = {
async _setAdditionalSocketOptions() {
if (this.encryption) {
this._socket.serverCert = await cert.local.getOrCreate();
this._socket.setSessionCache(false);
this._socket.setSessionTickets(false);
const requestCert = Ci.nsITLSServerSocket.REQUEST_NEVER;
this._socket.setRequestClientCertificate(requestCert);

View file

@ -11,8 +11,6 @@ const DevToolsUtils = require("devtools/shared/DevToolsUtils");
const { dumpn } = DevToolsUtils;
const flags = require("devtools/shared/flags");
const StreamUtils = require("devtools/shared/transport/stream-utils");
const promise = require("promise");
const defer = require("devtools/shared/defer");
loader.lazyGetter(this, "Pipe", () => {
return CC("@mozilla.org/pipe;1", "nsIPipe", "init");
@ -84,7 +82,7 @@ LocalDebuggerTransport.prototype = {
dumpn("Sent bulk packet " + serial + " for actor " + actor);
if (!this.other) {
const error = new Error("startBulkSend: other side of transport missing");
return promise.reject(error);
return Promise.reject(error);
}
const pipe = new Pipe(true, true, 0, 0, null);
@ -96,51 +94,48 @@ LocalDebuggerTransport.prototype = {
}
// Receiver
const deferred = defer();
const packet = {
actor: actor,
type: type,
length: length,
copyTo: (output) => {
const copying =
StreamUtils.copyStream(pipe.inputStream, output, length);
deferred.resolve(copying);
return copying;
},
stream: pipe.inputStream,
done: deferred
};
this.other.hooks.onBulkPacket(packet);
new Promise((receiverResolve) => {
const packet = {
actor: actor,
type: type,
length: length,
copyTo: (output) => {
const copying =
StreamUtils.copyStream(pipe.inputStream, output, length);
receiverResolve(copying);
return copying;
},
stream: pipe.inputStream,
done: receiverResolve
};
this.other.hooks.onBulkPacket(packet);
})
// Await the result of reading from the stream
deferred.promise.then(() => pipe.inputStream.close(), this.close);
.then(() => pipe.inputStream.close(), this.close);
}, "LocalDebuggerTransport instance's this.other.hooks.onBulkPacket"));
// Sender
const sendDeferred = defer();
// The remote transport is not capable of resolving immediately here, so we
// shouldn't be able to either.
DevToolsUtils.executeSoon(() => {
const copyDeferred = defer();
sendDeferred.resolve({
copyFrom: (input) => {
const copying =
StreamUtils.copyStream(input, pipe.outputStream, length);
copyDeferred.resolve(copying);
return copying;
},
stream: pipe.outputStream,
done: copyDeferred
return new Promise((senderResolve) => {
// The remote transport is not capable of resolving immediately here, so we
// shouldn't be able to either.
DevToolsUtils.executeSoon(() => {
return new Promise((copyResolve) => {
senderResolve({
copyFrom: (input) => {
const copying =
StreamUtils.copyStream(input, pipe.outputStream, length);
copyResolve(copying);
return copying;
},
stream: pipe.outputStream,
done: copyResolve
});
})
// Await the result of writing to the stream
.then(() => pipe.outputStream.close(), this.close);
});
// Await the result of writing to the stream
copyDeferred.promise.then(() => pipe.outputStream.close(), this.close);
});
return sendDeferred.promise;
},
/**

View file

@ -29,7 +29,6 @@ const DevToolsUtils = require("devtools/shared/DevToolsUtils");
const { dumpn, dumpv } = DevToolsUtils;
const flags = require("devtools/shared/flags");
const StreamUtils = require("devtools/shared/transport/stream-utils");
const defer = require("devtools/shared/defer");
DevToolsUtils.defineLazyGetter(this, "unicodeConverter", () => {
// eslint-disable-next-line no-shadow
@ -231,7 +230,11 @@ exports.JSONPacket = JSONPacket;
function BulkPacket(transport) {
Packet.call(this, transport);
this._done = false;
this._readyForWriting = defer();
let _resolve;
this._readyForWriting = new Promise((resolve) => {
_resolve = resolve;
});
this._readyForWriting.resolve = _resolve;
}
/**
@ -271,24 +274,23 @@ BulkPacket.prototype.read = function(stream) {
// Temporarily pause monitoring of the input stream
this._transport.pauseIncoming();
const deferred = defer();
this._transport._onBulkReadReady({
actor: this.actor,
type: this.type,
length: this.length,
copyTo: (output) => {
dumpv("CT length: " + this.length);
const copying = StreamUtils.copyStream(stream, output, this.length);
deferred.resolve(copying);
return copying;
},
stream: stream,
done: deferred
});
// Await the result of reading from the stream
deferred.promise.then(() => {
new Promise((resolve) => {
this._transport._onBulkReadReady({
actor: this.actor,
type: this.type,
length: this.length,
copyTo: (output) => {
dumpv("CT length: " + this.length);
const copying = StreamUtils.copyStream(stream, output, this.length);
resolve(copying);
return copying;
},
stream: stream,
done: resolve
});
// Await the result of reading from the stream
})
.then(() => {
dumpv("onReadDone called, ending bulk mode");
this._done = true;
this._transport.resumeIncoming();
@ -324,21 +326,20 @@ BulkPacket.prototype.write = function(stream) {
// Temporarily pause the monitoring of the output stream
this._transport.pauseOutgoing();
const deferred = defer();
this._readyForWriting.resolve({
copyFrom: (input) => {
dumpv("CF length: " + this.length);
const copying = StreamUtils.copyStream(input, stream, this.length);
deferred.resolve(copying);
return copying;
},
stream: stream,
done: deferred
});
// Await the result of writing to the stream
deferred.promise.then(() => {
new Promise((resolve) => {
this._readyForWriting.resolve({
copyFrom: (input) => {
dumpv("CF length: " + this.length);
const copying = StreamUtils.copyStream(input, stream, this.length);
resolve(copying);
return copying;
},
stream: stream,
done: resolve
});
// Await the result of writing to the stream
})
.then(() => {
dumpv("onWriteDone called, ending bulk mode");
this._done = true;
this._transport.resumeOutgoing();
@ -352,7 +353,7 @@ BulkPacket.prototype.write = function(stream) {
Object.defineProperty(BulkPacket.prototype, "streamReadyForWriting", {
get: function() {
return this._readyForWriting.promise;
return this._readyForWriting;
}
});

View file

@ -9,7 +9,6 @@ const Services = require("Services");
const DevToolsUtils = require("devtools/shared/DevToolsUtils");
const { dumpv } = DevToolsUtils;
const EventEmitter = require("devtools/shared/event-emitter");
const defer = require("devtools/shared/defer");
DevToolsUtils.defineLazyGetter(this, "IOUtil", () => {
return Cc["@mozilla.org/io-util;1"].getService(Ci.nsIIOUtil);
@ -75,7 +74,14 @@ function StreamCopier(input, output, length) {
}
this._length = length;
this._amountLeft = length;
this._deferred = defer();
let _resolve;
let _reject;
this._deferred = new Promise((resolve, reject) => {
_resolve = resolve;
_reject = reject;
});
this._deferred.resolve = _resolve;
this._deferred.reject = _reject;
this._copy = this._copy.bind(this);
this._flush = this._flush.bind(this);
@ -85,7 +91,7 @@ function StreamCopier(input, output, length) {
// Allows the copier to offer a promise interface for the simple succeed or
// fail scenarios, but also emit events (due to the EventEmitter) for other
// states, like progress.
this.then = this._deferred.promise.then.bind(this._deferred.promise);
this.then = this._deferred.then.bind(this._deferred);
this.then(this._destroy, this._destroy);
// Stream ready callback starts as |_copy|, but may switch to |_flush| at end

View file

@ -3,7 +3,7 @@
"use strict";
/* exported Cr, CC, NetUtil, defer, errorCount, initTestDebuggerServer,
/* exported Cr, CC, NetUtil, errorCount, initTestDebuggerServer,
writeTestTempFile, socket_transport, local_transport, really_long
*/
@ -12,7 +12,6 @@ var CC = Components.Constructor;
const { require } =
ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
const { NetUtil } = require("resource://gre/modules/NetUtil.jsm");
const promise = require("promise");
const defer = require("devtools/shared/defer");
const Services = require("Services");
@ -148,7 +147,7 @@ var socket_transport = async function() {
};
function local_transport() {
return promise.resolve(DebuggerServer.connectPipe());
return Promise.resolve(DebuggerServer.connectPipe());
}
/** * Sample Data ***/

View file

@ -74,22 +74,21 @@ function json_reply(client, response) {
});
// Send bulk data to server
const copyDeferred = defer();
request.on("bulk-send-ready", ({writer, done}) => {
const input = Cc["@mozilla.org/io/string-input-stream;1"]
.createInstance(Ci.nsIStringInputStream);
input.setData(reallyLong, reallyLong.length);
try {
writer.copyFrom(input, () => {
input.close();
done();
});
do_throw(new Error("Copying should fail, the stream is not async."));
} catch (e) {
Assert.ok(true);
copyDeferred.resolve();
}
return new Promise((resolve) => {
request.on("bulk-send-ready", ({writer, done}) => {
const input = Cc["@mozilla.org/io/string-input-stream;1"]
.createInstance(Ci.nsIStringInputStream);
input.setData(reallyLong, reallyLong.length);
try {
writer.copyFrom(input, () => {
input.close();
done();
});
do_throw(new Error("Copying should fail, the stream is not async."));
} catch (e) {
Assert.ok(true);
resolve();
}
});
});
return copyDeferred.promise;
}

View file

@ -105,31 +105,31 @@ var replyHandlers = {
json: function(request) {
// Receive JSON reply from server
const replyDeferred = defer();
request.on("json-reply", (reply) => {
Assert.ok(reply.allDone);
replyDeferred.resolve();
return new Promise((resolve) => {
request.on("json-reply", (reply) => {
Assert.ok(reply.allDone);
resolve();
});
});
return replyDeferred.promise;
},
bulk: function(request) {
// Receive bulk data reply from server
const replyDeferred = defer();
request.on("bulk-reply", ({length, copyTo}) => {
Assert.equal(length, really_long().length);
return new Promise((resolve) => {
request.on("bulk-reply", ({length, copyTo}) => {
Assert.equal(length, really_long().length);
const outputFile = getTestTempFile("bulk-output", true);
outputFile.create(Ci.nsIFile.NORMAL_FILE_TYPE, parseInt("666", 8));
const outputFile = getTestTempFile("bulk-output", true);
outputFile.create(Ci.nsIFile.NORMAL_FILE_TYPE, parseInt("666", 8));
const output = FileUtils.openSafeFileOutputStream(outputFile);
const output = FileUtils.openSafeFileOutputStream(outputFile);
copyTo(output).then(() => {
FileUtils.closeSafeFileOutputStream(output);
replyDeferred.resolve(verify_files());
copyTo(output).then(() => {
FileUtils.closeSafeFileOutputStream(output);
resolve(verify_files());
});
});
});
return replyDeferred.promise;
}
};
@ -141,16 +141,27 @@ var test_bulk_request_cs = async function(transportFactory, actorType, replyType
cleanup_files();
writeTestTempFile("bulk-input", really_long());
const clientDeferred = defer();
const serverDeferred = defer();
const bulkCopyDeferred = defer();
let clientResolve;
const clientDeferred = new Promise((resolve) => {
clientResolve = resolve;
});
let serverResolve;
const serverDeferred = new Promise((resolve) => {
serverResolve = resolve;
});
let bulkCopyResolve;
const bulkCopyDeferred = new Promise((resolve) => {
bulkCopyResolve = resolve;
});
const transport = await transportFactory();
const client = new DebuggerClient(transport);
client.connect().then(([app, traits]) => {
Assert.equal(traits.bulk, true);
client.listTabs().then(clientDeferred.resolve);
client.listTabs().then(clientResolve);
});
function bulkSendReadyCallback({copyFrom}) {
@ -160,12 +171,12 @@ var test_bulk_request_cs = async function(transportFactory, actorType, replyType
}, input => {
copyFrom(input).then(() => {
input.close();
bulkCopyDeferred.resolve();
bulkCopyResolve();
});
});
}
clientDeferred.promise.then(response => {
clientDeferred.then(response => {
const request = client.startBulkRequest({
actor: response.testBulk,
type: actorType,
@ -184,14 +195,14 @@ var test_bulk_request_cs = async function(transportFactory, actorType, replyType
DebuggerServer.on("connectionchange", type => {
if (type === "closed") {
serverDeferred.resolve();
serverResolve();
}
});
return promise.all([
clientDeferred.promise,
bulkCopyDeferred.promise,
serverDeferred.promise
return Promise.all([
clientDeferred,
bulkCopyDeferred,
serverDeferred
]);
};
@ -200,18 +211,25 @@ var test_json_request_cs = async function(transportFactory, actorType, replyType
cleanup_files();
writeTestTempFile("bulk-input", really_long());
const clientDeferred = defer();
const serverDeferred = defer();
let clientResolve;
const clientDeferred = new Promise((resolve) => {
clientResolve = resolve;
});
let serverResolve;
const serverDeferred = new Promise((resolve) => {
serverResolve = resolve;
});
const transport = await transportFactory();
const client = new DebuggerClient(transport);
client.connect((app, traits) => {
Assert.equal(traits.bulk, true);
client.listTabs().then(clientDeferred.resolve);
client.listTabs().then(clientResolve);
});
clientDeferred.promise.then(response => {
clientDeferred.then(response => {
const request = client.request({
to: response.testBulk,
type: actorType
@ -226,13 +244,13 @@ var test_json_request_cs = async function(transportFactory, actorType, replyType
DebuggerServer.on("connectionchange", type => {
if (type === "closed") {
serverDeferred.resolve();
serverResolve();
}
});
return promise.all([
clientDeferred.promise,
serverDeferred.promise
return Promise.all([
clientDeferred,
serverDeferred
]);
};
@ -248,19 +266,18 @@ function verify_files() {
Assert.equal(outputFile.fileSize, reallyLong.length);
// Ensure output file contents actually match
const compareDeferred = defer();
NetUtil.asyncFetch({
uri: NetUtil.newURI(getTestTempFile("bulk-output")),
loadUsingSystemPrincipal: true
}, input => {
const outputData = NetUtil.readInputStreamToString(input, reallyLong.length);
// Avoid do_check_eq here so we don't log the contents
Assert.ok(outputData === reallyLong);
input.close();
compareDeferred.resolve();
});
return compareDeferred.promise.then(cleanup_files);
return new Promise((resolve) => {
NetUtil.asyncFetch({
uri: NetUtil.newURI(getTestTempFile("bulk-output")),
loadUsingSystemPrincipal: true
}, input => {
const outputData = NetUtil.readInputStreamToString(input, reallyLong.length);
// Avoid do_check_eq here so we don't log the contents
Assert.ok(outputData === reallyLong);
input.close();
resolve();
});
}).then(cleanup_files);
}
function cleanup_files() {

View file

@ -50,27 +50,27 @@ async function test_socket_conn() {
Assert.equal(settings.host, "127.0.0.1");
Assert.equal(settings.port, gPort);
const closedDeferred = defer();
transport.hooks = {
onPacket: function(packet) {
this.onPacket = function({unicode}) {
Assert.equal(unicode, unicodeString);
transport.close();
};
// Verify that things work correctly when bigger than the output
// transport buffers and when transporting unicode...
transport.send({to: "root",
type: "echo",
reallylong: really_long(),
unicode: unicodeString});
Assert.equal(packet.from, "root");
},
onClosed: function(status) {
closedDeferred.resolve();
},
};
transport.ready();
return closedDeferred.promise;
return new Promise((resolve) => {
transport.hooks = {
onPacket: function(packet) {
this.onPacket = function({unicode}) {
Assert.equal(unicode, unicodeString);
transport.close();
};
// Verify that things work correctly when bigger than the output
// transport buffers and when transporting unicode...
transport.send({to: "root",
type: "echo",
reallylong: really_long(),
unicode: unicodeString});
Assert.equal(packet.from, "root");
},
onClosed: function(status) {
resolve();
},
};
transport.ready();
});
}
async function test_socket_shutdown() {

View file

@ -60,23 +60,23 @@ var test_helper = async function(payload) {
host: "127.0.0.1",
port: listener.port
});
const closedDeferred = defer();
transport.hooks = {
onPacket: function(packet) {
this.onPacket = function() {
do_throw(new Error("This connection should be dropped."));
transport.close();
};
return new Promise((resolve) => {
transport.hooks = {
onPacket: function(packet) {
this.onPacket = function() {
do_throw(new Error("This connection should be dropped."));
transport.close();
};
// Inject the payload directly into the stream.
transport._outgoing.push(new RawPacket(transport, payload));
transport._flushOutgoing();
},
onClosed: function(status) {
Assert.ok(true);
closedDeferred.resolve();
},
};
transport.ready();
return closedDeferred.promise;
// Inject the payload directly into the stream.
transport._outgoing.push(new RawPacket(transport, payload));
transport._flushOutgoing();
},
onClosed: function(status) {
Assert.ok(true);
resolve();
},
};
transport.ready();
});
};

View file

@ -25,8 +25,15 @@ function run_test() {
/** * Tests ***/
var test_transport = async function(transportFactory) {
const clientDeferred = defer();
const serverDeferred = defer();
let clientResolve;
const clientDeferred = new Promise((resolve) => {
clientResolve = resolve;
});
let serverResolve;
const serverDeferred = new Promise((resolve) => {
serverResolve = resolve;
});
// Ensure test files are not present from a failed run
cleanup_files();
@ -66,7 +73,7 @@ var test_transport = async function(transportFactory) {
}).then(() => {
// It's now safe to close
transport.hooks.onClosed = () => {
clientDeferred.resolve();
clientResolve();
};
transport.close();
});
@ -114,7 +121,7 @@ var test_transport = async function(transportFactory) {
DebuggerServer.on("connectionchange", type => {
if (type === "closed") {
serverDeferred.resolve();
serverResolve();
}
});
@ -134,7 +141,7 @@ var test_transport = async function(transportFactory) {
transport.ready();
return promise.all([clientDeferred.promise, serverDeferred.promise]);
return Promise.all([clientDeferred, serverDeferred]);
};
/** * Test Utils ***/
@ -149,19 +156,19 @@ function verify() {
Assert.equal(outputFile.fileSize, reallyLong.length);
// Ensure output file contents actually match
const compareDeferred = defer();
NetUtil.asyncFetch({
uri: NetUtil.newURI(getTestTempFile("bulk-output")),
loadUsingSystemPrincipal: true
}, input => {
const outputData = NetUtil.readInputStreamToString(input, reallyLong.length);
return new Promise((resolve) => {
NetUtil.asyncFetch({
uri: NetUtil.newURI(getTestTempFile("bulk-output")),
loadUsingSystemPrincipal: true
}, input => {
const outputData = NetUtil.readInputStreamToString(input, reallyLong.length);
// Avoid do_check_eq here so we don't log the contents
Assert.ok(outputData === reallyLong);
input.close();
compareDeferred.resolve();
});
return compareDeferred.promise.then(cleanup_files);
Assert.ok(outputData === reallyLong);
input.close();
resolve();
});
})
.then(cleanup_files);
}
function cleanup_files() {

View file

@ -25,8 +25,15 @@ function run_test() {
var test_bulk_transfer_transport = async function(transportFactory) {
info("Starting bulk transfer test at " + new Date().toTimeString());
const clientDeferred = defer();
const serverDeferred = defer();
let clientResolve;
const clientDeferred = new Promise((resolve) => {
clientResolve = resolve;
});
let serverResolve;
const serverDeferred = new Promise((resolve) => {
serverResolve = resolve;
});
// Ensure test files are not present from a failed run
cleanup_files();
@ -66,7 +73,7 @@ var test_bulk_transfer_transport = async function(transportFactory) {
}).then(() => {
// It's now safe to close
transport.hooks.onClosed = () => {
clientDeferred.resolve();
clientResolve();
};
transport.close();
});
@ -87,7 +94,7 @@ var test_bulk_transfer_transport = async function(transportFactory) {
DebuggerServer.on("connectionchange", type => {
if (type === "closed") {
serverDeferred.resolve();
serverResolve();
}
});
@ -105,7 +112,7 @@ var test_bulk_transfer_transport = async function(transportFactory) {
transport.ready();
return promise.all([clientDeferred.promise, serverDeferred.promise]);
return Promise.all([clientDeferred, serverDeferred]);
};
/** * Test Utils ***/
@ -120,19 +127,19 @@ function verify() {
Assert.equal(outputFile.fileSize, reallyLong.length);
// Ensure output file contents actually match
const compareDeferred = defer();
NetUtil.asyncFetch({
uri: NetUtil.newURI(getTestTempFile("bulk-output")),
loadUsingSystemPrincipal: true
}, input => {
const outputData = NetUtil.readInputStreamToString(input, reallyLong.length);
// Avoid do_check_eq here so we don't log the contents
Assert.ok(outputData === reallyLong);
input.close();
compareDeferred.resolve();
});
return compareDeferred.promise.then(cleanup_files);
return new Promise((resolve) => {
NetUtil.asyncFetch({
uri: NetUtil.newURI(getTestTempFile("bulk-output")),
loadUsingSystemPrincipal: true
}, input => {
const outputData = NetUtil.readInputStreamToString(input, reallyLong.length);
// Avoid do_check_eq here so we don't log the contents
Assert.ok(outputData === reallyLong);
input.close();
resolve();
});
})
.then(cleanup_files);
}
function cleanup_files() {

View file

@ -7,7 +7,6 @@ const { ActorPool, appendExtraActors, createExtraActors } =
const { RootActor } = require("devtools/server/actors/root");
const { ThreadActor } = require("devtools/server/actors/thread");
const { DebuggerServer } = require("devtools/server/main");
const promise = require("promise");
var gTestGlobals = [];
DebuggerServer.addTestGlobal = function(global) {
@ -47,7 +46,7 @@ function TestTabList(connection) {
TestTabList.prototype = {
constructor: TestTabList,
getList: function() {
return promise.resolve([...this._targetActors]);
return Promise.resolve([...this._targetActors]);
}
};

View file

@ -83,7 +83,7 @@
#endif
#if defined(MOZ_WIDGET_ANDROID)
#include "../../gfx/vr/gfxVRExternal.h"
#include "mozilla/layers/ImageBridgeChild.h"
#endif
// Generated
@ -236,6 +236,9 @@ WebGLContext::DestroyResourcesAndContext()
mDefaultVertexArray = nullptr;
mBoundTransformFeedback = nullptr;
mDefaultTransformFeedback = nullptr;
#if defined(MOZ_WIDGET_ANDROID)
mVRScreen = nullptr;
#endif
mQuerySlot_SamplesPassed = nullptr;
mQuerySlot_TFPrimsWritten = nullptr;
@ -788,7 +791,7 @@ WebGLContext::SetDimensions(int32_t signedWidth, int32_t signedHeight)
return NS_OK;
// If we've already drawn, we should commit the current buffer.
PresentScreenBuffer();
PresentScreenBuffer(gl->Screen());
if (IsContextLost()) {
GenerateWarning("WebGL context was lost due to swap failure.");
@ -1463,7 +1466,7 @@ WebGLContext::BlitBackbufferToCurDriverFB() const
// For an overview of how WebGL compositing works, see:
// https://wiki.mozilla.org/Platform/GFX/WebGL/Compositing
bool
WebGLContext::PresentScreenBuffer()
WebGLContext::PresentScreenBuffer(GLScreenBuffer* const targetScreen)
{
const FuncScope funcScope(*this, "<PresentScreenBuffer>");
if (IsContextLost())
@ -1475,8 +1478,8 @@ WebGLContext::PresentScreenBuffer()
if (!ValidateAndInitFB(nullptr))
return false;
const auto& screen = gl->Screen();
if (screen->Size() != mDefaultFB->mSize &&
const auto& screen = targetScreen ? targetScreen : gl->Screen();
if ((!screen->IsReadBufferReady() || screen->Size() != mDefaultFB->mSize) &&
!screen->Resize(mDefaultFB->mSize))
{
GenerateWarning("screen->Resize failed. Losing context.");
@ -1520,10 +1523,10 @@ WebGLContext::PresentScreenBuffer()
// Prepare the context for capture before compositing
void
WebGLContext::BeginComposition()
WebGLContext::BeginComposition(GLScreenBuffer* const screen)
{
// Present our screenbuffer, if needed.
PresentScreenBuffer();
PresentScreenBuffer(screen);
mDrawCallsSinceLastFlush = 0;
}
@ -2301,47 +2304,40 @@ WebGLContext::GetUnpackSize(bool isFunc3D, uint32_t width, uint32_t height,
already_AddRefed<layers::SharedSurfaceTextureClient>
WebGLContext::GetVRFrame()
{
if (!gl)
return nullptr;
if (!gl)
return nullptr;
// Create a custom GLScreenBuffer for VR.
if (!mVRScreen) {
auto caps = gl->Screen()->mCaps;
mVRScreen = GLScreenBuffer::Create(gl, gfx::IntSize(1, 1), caps);
int frameId = gfx::impl::VRDisplayExternal::sPushIndex;
static int lastFrameId = -1;
/**
* Android doesn't like duplicated GetVRFrame within the same gfxVRExternal frame.
* Ballout forced composition calls if we are in the same VRExternal push frame index.
* Also discard frameId 0 because sometimes compositor is not paused yet due to channel communication delays.
*/
const bool ignoreFrame = lastFrameId == frameId || frameId == 0;
lastFrameId = frameId;
if (!ignoreFrame) {
BeginComposition();
EndComposition();
}
RefPtr<ImageBridgeChild> imageBridge = ImageBridgeChild::GetSingleton();
if (imageBridge) {
TextureFlags flags = TextureFlags::ORIGIN_BOTTOM_LEFT;
UniquePtr<gl::SurfaceFactory> factory = gl::GLScreenBuffer::CreateFactory(gl, caps, imageBridge.get(), flags);
mVRScreen->Morph(std::move(factory));
}
}
if (!gl) {
return nullptr;
}
// Swap buffers as though composition has occurred.
// We will then share the resulting front buffer to be submitted to the VR compositor.
BeginComposition(mVRScreen.get());
EndComposition();
gl::GLScreenBuffer* screen = gl->Screen();
if (!screen) {
return nullptr;
}
if (IsContextLost())
return nullptr;
RefPtr<SharedSurfaceTextureClient> sharedSurface = screen->Front();
if (!sharedSurface || !sharedSurface->Surf()) {
return nullptr;
}
RefPtr<SharedSurfaceTextureClient> sharedSurface = mVRScreen->Front();
if (!sharedSurface || !sharedSurface->Surf())
return nullptr;
/**
* Make sure that the WebGL buffer is committed to the attached SurfaceTexture on Android.
*/
if (!ignoreFrame) {
// Make sure that the WebGL buffer is committed to the attached SurfaceTexture on Android.
sharedSurface->Surf()->ProducerAcquire();
sharedSurface->Surf()->Commit();
sharedSurface->Surf()->ProducerRelease();
}
return sharedSurface.forget();
return sharedSurface.forget();
}
#else
already_AddRefed<layers::SharedSurfaceTextureClient>

View file

@ -95,6 +95,7 @@ class VRLayerChild;
} // namespace gfx
namespace gl {
class GLScreenBuffer;
class MozFramebuffer;
} // namespace gl
@ -498,10 +499,10 @@ public:
bool IsPreservingDrawingBuffer() const { return mOptions.preserveDrawingBuffer; }
bool PresentScreenBuffer();
bool PresentScreenBuffer(gl::GLScreenBuffer* const screen = nullptr);
// Prepare the context for capture before compositing
void BeginComposition();
void BeginComposition(gl::GLScreenBuffer* const screen = nullptr);
// Clean up the context after captured for compositing
void EndComposition();
@ -1980,6 +1981,9 @@ protected:
bool mNeedsIndexValidation = false;
const bool mAllowFBInvalidation;
#if defined(MOZ_WIDGET_ANDROID)
UniquePtr<gl::GLScreenBuffer> mVRScreen;
#endif
bool Has64BitTimestamps() const;

View file

@ -90,7 +90,6 @@ PresentationControlService.prototype = {
if (aCert) {
this._serverSocket.serverCert = aCert;
this._serverSocket.setSessionCache(false);
this._serverSocket.setSessionTickets(false);
let requestCert = Ci.nsITLSServerSocket.REQUEST_NEVER;
this._serverSocket.setRequestClientCertificate(requestCert);

View file

@ -2350,18 +2350,21 @@ HTMLEditRules::WillDeleteSelection(nsIEditor::EDirection aAction,
}
// First check for table selection mode. If so, hand off to table editor.
RefPtr<Element> cell;
nsresult rv =
HTMLEditorRef().GetFirstSelectedCell(nullptr, getter_AddRefs(cell));
if (NS_SUCCEEDED(rv) && cell) {
rv = HTMLEditorRef().DeleteTableCellContents();
ErrorResult error;
RefPtr<Element> cellElement =
HTMLEditorRef().GetFirstSelectedTableCellElement(SelectionRef(),
error);
if (cellElement) {
error.SuppressException();
nsresult rv = HTMLEditorRef().DeleteTableCellContents();
if (NS_WARN_IF(!CanHandleEditAction())) {
return NS_ERROR_EDITOR_DESTROYED;
}
*aHandled = true;
return rv;
}
cell = nullptr;
nsresult rv = error.StealNSResult();
cellElement = nullptr;
// origCollapsed is used later to determine whether we should join blocks. We
// don't really care about bCollapsed because it will be modified by
@ -2443,7 +2446,8 @@ HTMLEditRules::WillDeleteSelection(nsIEditor::EDirection aAction,
if (!visNode) {
// Can't find anything to delete!
*aCancel = true;
// XXX This is the result of HTMLEditorRef().GetFirstSelectedCell().
// XXX This is the result of
// HTMLEditorRef().GetFirstSelectedTableCellElement().
// The value could be both an error and NS_OK.
return rv;
}

View file

@ -3003,18 +3003,31 @@ HTMLEditor::SetHTMLBackgroundColorWithTransaction(const nsAString& aColor)
RefPtr<nsAtom> bgColorAtom = NS_Atomize("bgcolor");
if (element) {
if (selectedCount > 0) {
// Traverse all selected cells
RefPtr<Element> cell;
rv = GetFirstSelectedCell(nullptr, getter_AddRefs(cell));
if (NS_SUCCEEDED(rv) && cell) {
while (cell) {
rv = setColor ?
SetAttributeWithTransaction(*cell, *bgColorAtom, aColor) :
RemoveAttributeWithTransaction(*cell, *bgColorAtom);
RefPtr<Selection> selection = GetSelection();
if (NS_WARN_IF(!selection)) {
return NS_ERROR_FAILURE;
}
IgnoredErrorResult ignoredError;
RefPtr<Element> cellElement =
GetFirstSelectedTableCellElement(*selection, ignoredError);
if (cellElement) {
if (setColor) {
while (cellElement) {
rv =
SetAttributeWithTransaction(*cellElement, *bgColorAtom, aColor);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
GetNextSelectedCell(nullptr, getter_AddRefs(cellElement));
}
return NS_OK;
}
while (cellElement) {
rv = RemoveAttributeWithTransaction(*cellElement, *bgColorAtom);
if (NS_FAILED(rv)) {
return rv;
}
GetNextSelectedCell(nullptr, getter_AddRefs(cell));
GetNextSelectedCell(nullptr, getter_AddRefs(cellElement));
}
return NS_OK;
}

View file

@ -530,6 +530,28 @@ protected: // May be called by friends.
*/
Element* GetSelectionContainerElement(Selection& aSelection) const;
/**
* GetFirstSelectedTableCellElement() returns a <td> or <th> element if
* first range of Selection (i.e., result of Selection::GetRangeAt(0))
* selects a <td> element or <th> element. Even if Selection is in
* a cell element, this returns nullptr. And even if 2nd or later
* range of Selection selects a cell element, also returns nullptr.
* Note that when this looks for a cell element, this resets the internal
* index of ranges of Selection. When you call GetNextSelectedCell() after
* a call of this, it'll return 2nd selected cell if there is.
*
* @param aSelection Selection for this editor.
* @param aRv Returns error if there is no selection or
* first range of Selection is unexpected.
* @return A <td> or <th> element is selected by first
* range of Selection. Note that the range must
* be: startContaienr and endContainer are same
* <tr> element, startOffset + 1 equals endOffset.
*/
already_AddRefed<Element>
GetFirstSelectedTableCellElement(Selection& aSelection,
ErrorResult& aRv) const;
void IsNextCharInNodeWhitespace(nsIContent* aContent,
int32_t aOffset,
bool* outIsSpace,
@ -1214,7 +1236,7 @@ protected: // Shouldn't be used by friend classes
nsresult GetLastCellInRow(nsINode* aRowNode,
nsINode** aCellNode);
nsresult GetCellFromRange(nsRange* aRange, Element** aCell);
static nsresult GetCellFromRange(nsRange* aRange, Element** aCell);
/**
* This sets background on the appropriate container element (table, cell,)
@ -1855,8 +1877,9 @@ protected:
bool mCSSAware;
UniquePtr<CSSEditUtils> mCSSEditUtils;
// Used by GetFirstSelectedCell and GetNextSelectedCell
int32_t mSelectedCellIndex;
// mSelectedCellIndex is reset by GetFirstSelectedTableCellElement(),
// then, it'll be referred and incremented by GetNextSelectedCell().
mutable int32_t mSelectedCellIndex;
nsString mLastStyleSheetURL;
nsString mLastOverrideStyleSheetURL;

View file

@ -203,7 +203,9 @@ HTMLEditor::DoInsertHTMLWithContext(const nsAString& aInputString,
// Get selection
RefPtr<Selection> selection = GetSelection();
NS_ENSURE_STATE(selection);
if (NS_WARN_IF(!selection)) {
return NS_ERROR_FAILURE;
}
// create a dom document fragment that represents the structure to paste
nsCOMPtr<nsINode> fragmentAsNode, streamStartParent, streamEndParent;
@ -282,9 +284,10 @@ HTMLEditor::DoInsertHTMLWithContext(const nsAString& aInputString,
// Are there any table elements in the list?
// check for table cell selection mode
bool cellSelectionMode = false;
RefPtr<Element> cell;
rv = GetFirstSelectedCell(nullptr, getter_AddRefs(cell));
if (NS_SUCCEEDED(rv) && cell) {
IgnoredErrorResult ignoredError;
RefPtr<Element> cellElement =
GetFirstSelectedTableCellElement(*selection, ignoredError);
if (cellElement) {
cellSelectionMode = true;
}

View file

@ -764,23 +764,26 @@ HTMLEditor::DeleteTableCell(int32_t aNumber)
*this, EditSubAction::eDeleteNode,
nsIEditor::eNext);
RefPtr<Element> firstCell;
rv = GetFirstSelectedCell(nullptr, getter_AddRefs(firstCell));
NS_ENSURE_SUCCESS(rv, rv);
ErrorResult error;
RefPtr<Element> firstSelectedCellElement =
GetFirstSelectedTableCellElement(*selection, error);
if (NS_WARN_IF(error.Failed())) {
return error.StealNSResult();
}
// When 2 or more cells are selected, ignore aNumber and use selected cells.
if (firstCell && selection->RangeCount() > 1) {
if (firstSelectedCellElement && selection->RangeCount() > 1) {
ErrorResult error;
TableSize tableSize(*this, *table, error);
if (NS_WARN_IF(error.Failed())) {
return error.StealNSResult();
}
CellIndexes firstCellIndexes(*firstCell, error);
CellIndexes firstCellIndexes(*firstSelectedCellElement, error);
if (NS_WARN_IF(error.Failed())) {
return error.StealNSResult();
}
cell = firstCell;
cell = firstSelectedCellElement;
startRowIndex = firstCellIndexes.mRow;
startColIndex = firstCellIndexes.mColumn;
@ -976,19 +979,20 @@ HTMLEditor::DeleteTableCellContents()
//Don't let Rules System change the selection
AutoTransactionsConserveSelection dontChangeSelection(*this);
ErrorResult error;
RefPtr<Element> firstSelectedCellElement =
GetFirstSelectedTableCellElement(*selection, error);
if (NS_WARN_IF(error.Failed())) {
return error.StealNSResult();
}
RefPtr<Element> firstCell;
rv = GetFirstSelectedCell(nullptr, getter_AddRefs(firstCell));
NS_ENSURE_SUCCESS(rv, rv);
if (firstCell) {
if (firstSelectedCellElement) {
ErrorResult error;
CellIndexes firstCellIndexes(*firstCell, error);
CellIndexes firstCellIndexes(*firstSelectedCellElement, error);
if (NS_WARN_IF(error.Failed())) {
return error.StealNSResult();
}
cell = firstCell;
cell = firstSelectedCellElement;
startRowIndex = firstCellIndexes.mRow;
startColIndex = firstCellIndexes.mColumn;
}
@ -999,10 +1003,12 @@ HTMLEditor::DeleteTableCellContents()
while (cell) {
DeleteCellContents(cell);
if (firstCell) {
if (firstSelectedCellElement) {
// We doing a selected cells, so do all of them
rv = GetNextSelectedCell(nullptr, getter_AddRefs(cell));
NS_ENSURE_SUCCESS(rv, rv);
nsresult rv = GetNextSelectedCell(nullptr, getter_AddRefs(cell));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
} else {
cell = nullptr;
}
@ -1065,14 +1071,16 @@ HTMLEditor::DeleteTableColumn(int32_t aNumber)
nsIEditor::eNext);
// Test if deletion is controlled by selected cells
RefPtr<Element> firstCell;
rv = GetFirstSelectedCell(nullptr, getter_AddRefs(firstCell));
NS_ENSURE_SUCCESS(rv, rv);
RefPtr<Element> firstSelectedCellElement =
GetFirstSelectedTableCellElement(*selection, error);
if (NS_WARN_IF(error.Failed())) {
return error.StealNSResult();
}
uint32_t rangeCount = selection->RangeCount();
if (firstCell && rangeCount > 1) {
CellIndexes firstCellIndexes(*firstCell, error);
if (firstSelectedCellElement && rangeCount > 1) {
CellIndexes firstCellIndexes(*firstSelectedCellElement, error);
if (NS_WARN_IF(error.Failed())) {
return error.StealNSResult();
}
@ -1084,12 +1092,12 @@ HTMLEditor::DeleteTableColumn(int32_t aNumber)
startColIndex, ePreviousRow,
false);
if (firstCell && rangeCount > 1) {
if (firstSelectedCellElement && rangeCount > 1) {
// Use selected cells to determine what rows to delete
cell = firstCell;
cell = firstSelectedCellElement;
while (cell) {
if (cell != firstCell) {
if (cell != firstSelectedCellElement) {
CellIndexes cellIndexes(*cell, error);
if (NS_WARN_IF(error.Failed())) {
return error.StealNSResult();
@ -1250,14 +1258,16 @@ HTMLEditor::DeleteTableRow(int32_t aNumber)
*this, EditSubAction::eDeleteNode,
nsIEditor::eNext);
RefPtr<Element> firstCell;
rv = GetFirstSelectedCell(nullptr, getter_AddRefs(firstCell));
NS_ENSURE_SUCCESS(rv, rv);
RefPtr<Element> firstSelectedCellElement =
GetFirstSelectedTableCellElement(*selection, error);
if (NS_WARN_IF(error.Failed())) {
return error.StealNSResult();
}
uint32_t rangeCount = selection->RangeCount();
if (firstCell && rangeCount > 1) {
if (firstSelectedCellElement && rangeCount > 1) {
// Fetch indexes again - may be different for selected cells
CellIndexes firstCellIndexes(*firstCell, error);
CellIndexes firstCellIndexes(*firstSelectedCellElement, error);
if (NS_WARN_IF(error.Failed())) {
return error.StealNSResult();
}
@ -1272,12 +1282,12 @@ HTMLEditor::DeleteTableRow(int32_t aNumber)
// Don't change selection during deletions
AutoTransactionsConserveSelection dontChangeSelection(*this);
if (firstCell && rangeCount > 1) {
if (firstSelectedCellElement && rangeCount > 1) {
// Use selected cells to determine what rows to delete
cell = firstCell;
cell = firstSelectedCellElement;
while (cell) {
if (cell != firstCell) {
if (cell != firstSelectedCellElement) {
CellIndexes cellIndexes(*cell, error);
if (NS_WARN_IF(error.Failed())) {
return error.StealNSResult();
@ -1289,7 +1299,7 @@ HTMLEditor::DeleteTableRow(int32_t aNumber)
// to continue after we delete this row
int32_t nextRow = startRowIndex;
while (nextRow == startRowIndex) {
rv = GetNextSelectedCell(nullptr, getter_AddRefs(cell));
nsresult rv = GetNextSelectedCell(nullptr, getter_AddRefs(cell));
NS_ENSURE_SUCCESS(rv, rv);
if (!cell) {
break;
@ -1309,7 +1319,7 @@ HTMLEditor::DeleteTableRow(int32_t aNumber)
// Check for counts too high
aNumber = std::min(aNumber, (tableSize.mRowCount - startRowIndex));
for (int32_t i = 0; i < aNumber; i++) {
rv = DeleteRow(table, startRowIndex);
nsresult rv = DeleteRow(table, startRowIndex);
// If failed in current row, try the next
if (NS_FAILED(rv)) {
startRowIndex++;
@ -1536,14 +1546,15 @@ HTMLEditor::SelectBlockOfCells(Element* aStartCell,
RefPtr<Element> cell;
int32_t currentRowIndex, currentColIndex;
RefPtr<nsRange> range;
nsresult rv =
GetFirstSelectedCell(getter_AddRefs(range), getter_AddRefs(cell));
NS_ENSURE_SUCCESS(rv, rv);
if (rv == NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND) {
cell = GetFirstSelectedTableCellElement(*selection, error);
if (NS_WARN_IF(error.Failed())) {
return error.StealNSResult();
}
if (!cell) {
return NS_OK;
}
RefPtr<nsRange> range = selection->GetRangeAt(0);
MOZ_ASSERT(range);
while (cell) {
CellIndexes currentCellIndexes(*cell, error);
if (NS_WARN_IF(error.Failed())) {
@ -1557,12 +1568,16 @@ HTMLEditor::SelectBlockOfCells(Element* aStartCell,
// Since we've removed the range, decrement pointer to next range
mSelectedCellIndex--;
}
rv = GetNextSelectedCell(getter_AddRefs(range), getter_AddRefs(cell));
NS_ENSURE_SUCCESS(rv, rv);
nsresult rv =
GetNextSelectedCell(getter_AddRefs(range), getter_AddRefs(cell));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
}
int32_t rowSpan, colSpan, actualRowSpan, actualColSpan;
bool isSelected;
nsresult rv = NS_OK;
for (int32_t row = minRow; row <= maxRow; row++) {
for (int32_t col = minColumn; col <= maxColumn;
col += std::max(actualColSpan, 1)) {
@ -2365,7 +2380,7 @@ HTMLEditor::JoinTableCells(bool aMergeNonContiguousContents)
NS_ENSURE_TRUE(range, NS_ERROR_FAILURE);
RefPtr<Element> deletedCell;
GetCellFromRange(range, getter_AddRefs(deletedCell));
HTMLEditor::GetCellFromRange(range, getter_AddRefs(deletedCell));
if (!deletedCell) {
selection->RemoveRange(*range, IgnoreErrors());
rangeCount--;
@ -3220,6 +3235,7 @@ HTMLEditor::GetCellContext(Selection** aSelection,
return NS_OK;
}
// static
nsresult
HTMLEditor::GetCellFromRange(nsRange* aRange,
Element** aCell)
@ -3264,44 +3280,87 @@ HTMLEditor::GetCellFromRange(nsRange* aRange,
}
NS_IMETHODIMP
HTMLEditor::GetFirstSelectedCell(nsRange** aRange,
Element** aCell)
HTMLEditor::GetFirstSelectedCell(nsRange** aFirstSelectedRange,
Element** aFirstSelectedCellElement)
{
NS_ENSURE_TRUE(aCell, NS_ERROR_NULL_POINTER);
*aCell = nullptr;
if (aRange) {
*aRange = nullptr;
if (NS_WARN_IF(!aFirstSelectedCellElement)) {
return NS_ERROR_INVALID_ARG;
}
*aFirstSelectedCellElement = nullptr;
if (aFirstSelectedRange) {
*aFirstSelectedRange = nullptr;
}
// XXX Oddly, when there is no ranges of Selection, we return error.
// However, despite of that we return NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND
// when first range of Selection does not select a table cell element.
RefPtr<Selection> selection = GetSelection();
NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
RefPtr<nsRange> range = selection->GetRangeAt(0);
NS_ENSURE_TRUE(range, NS_ERROR_FAILURE);
mSelectedCellIndex = 0;
nsresult rv = GetCellFromRange(range, aCell);
// Failure here probably means selection is in a text node,
// so there's no selected cell
if (NS_FAILED(rv)) {
return NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND;
}
// No cell means range was collapsed (cell was deleted)
if (!*aCell) {
return NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND;
if (NS_WARN_IF(!selection)) {
return NS_ERROR_FAILURE;
}
if (aRange) {
range.forget(aRange);
ErrorResult error;
RefPtr<Element> firstSelectedCellElement =
GetFirstSelectedTableCellElement(*selection, error);
if (NS_WARN_IF(error.Failed())) {
return error.StealNSResult();
}
// Setup for next cell
mSelectedCellIndex = 1;
if (!firstSelectedCellElement) {
// Just not found. Don't return error.
return NS_OK;
}
firstSelectedCellElement.forget(aFirstSelectedCellElement);
if (aFirstSelectedRange) {
// Returns the first range only when the caller requested the range.
RefPtr<nsRange> firstRange = selection->GetRangeAt(0);
MOZ_ASSERT(firstRange);
firstRange.forget(aFirstSelectedRange);
}
return NS_OK;
}
already_AddRefed<Element>
HTMLEditor::GetFirstSelectedTableCellElement(Selection& aSelection,
ErrorResult& aRv) const
{
MOZ_ASSERT(!aRv.Failed());
nsRange* firstRange = aSelection.GetRangeAt(0);
if (NS_WARN_IF(!firstRange)) {
// XXX Why don't we treat "not found" in this case?
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
// XXX It must be unclear when this is reset...
mSelectedCellIndex = 0;
RefPtr<Element> selectedCell;
nsresult rv =
HTMLEditor::GetCellFromRange(firstRange, getter_AddRefs(selectedCell));
if (NS_FAILED(rv)) {
// This case occurs only when Selection is in a text node in normal cases.
return nullptr;
}
if (!selectedCell) {
// This case means that the range does not select only a cell.
// E.g., selects non-table cell element, selects two or more cells, or
// does not select any cell element.
return nullptr;
}
// Setup for GetNextSelectedCell()
// XXX Oh, increment it now? Rather than when GetNextSelectedCell() is
// called?
mSelectedCellIndex = 1;
return selectedCell.forget();
}
NS_IMETHODIMP
HTMLEditor::GetNextSelectedCell(nsRange** aRange,
Element** aCell)
@ -3328,7 +3387,7 @@ HTMLEditor::GetNextSelectedCell(nsRange** aRange,
range = selection->GetRangeAt(mSelectedCellIndex);
NS_ENSURE_TRUE(range, NS_ERROR_FAILURE);
nsresult rv = GetCellFromRange(range, aCell);
nsresult rv = HTMLEditor::GetCellFromRange(range, aCell);
// Failure here means the range doesn't contain a cell
NS_ENSURE_SUCCESS(rv, NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND);
@ -3366,21 +3425,30 @@ HTMLEditor::GetFirstSelectedCellInTable(int32_t* aRowIndex,
*aColumnIndex = 0;
}
RefPtr<Element> cell;
nsresult rv = GetFirstSelectedCell(nullptr, getter_AddRefs(cell));
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_TRUE(cell, NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND);
RefPtr<Selection> selection = GetSelection();
if (NS_WARN_IF(!selection)) {
return NS_ERROR_FAILURE;
}
ErrorResult error;
RefPtr<Element> firstSelectedCellElement =
GetFirstSelectedTableCellElement(*selection, error);
if (NS_WARN_IF(error.Failed())) {
return error.StealNSResult();
}
if (NS_WARN_IF(!firstSelectedCellElement)) {
return NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND;
}
// We don't want to cell.forget() here, because we use "cell" below.
*aCell = do_AddRef(cell).take();
firstSelectedCellElement.forget(aCell);
if (!aRowIndex && !aColumnIndex) {
return NS_OK;
}
// Also return the row and/or column if requested
ErrorResult error;
CellIndexes cellIndexes(*cell, error);
CellIndexes cellIndexes(*firstSelectedCellElement, error);
if (NS_WARN_IF(error.Failed())) {
return error.StealNSResult();
}
@ -3488,13 +3556,17 @@ HTMLEditor::GetSelectedOrParentTableElement(nsAString& aTagName,
*aSelectedCount = 0;
RefPtr<Selection> selection = GetSelection();
NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
if (NS_WARN_IF(!selection)) {
return NS_ERROR_FAILURE;
}
// Try to get the first selected cell
RefPtr<Element> tableOrCellElement;
nsresult rv = GetFirstSelectedCell(nullptr,
getter_AddRefs(tableOrCellElement));
NS_ENSURE_SUCCESS(rv, rv);
ErrorResult error;
RefPtr<Element> tableOrCellElement =
GetFirstSelectedTableCellElement(*selection, error);
if (NS_WARN_IF(error.Failed())) {
return error.StealNSResult();
}
if (tableOrCellElement) {
// Each cell is in its own selection range,
@ -3550,6 +3622,11 @@ HTMLEditor::GetSelectedCellsType(Element* aElement,
NS_ENSURE_ARG_POINTER(aSelectionType);
*aSelectionType = 0;
RefPtr<Selection> selection = GetSelection();
if (NS_WARN_IF(!selection)) {
return NS_ERROR_FAILURE;
}
// Be sure we have a table element
// (if aElement is null, this uses selection's anchor node)
RefPtr<Element> table;
@ -3559,10 +3636,6 @@ HTMLEditor::GetSelectedCellsType(Element* aElement,
return NS_ERROR_FAILURE;
}
} else {
RefPtr<Selection> selection = GetSelection();
if (NS_WARN_IF(!selection)) {
return NS_ERROR_FAILURE;
}
table =
GetElementOrParentByTagNameAtSelection(*selection, *nsGkAtoms::table);
if (NS_WARN_IF(!table)) {
@ -3577,10 +3650,12 @@ HTMLEditor::GetSelectedCellsType(Element* aElement,
}
// Traverse all selected cells
RefPtr<Element> selectedCell;
nsresult rv = GetFirstSelectedCell(nullptr, getter_AddRefs(selectedCell));
NS_ENSURE_SUCCESS(rv, rv);
if (rv == NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND) {
RefPtr<Element> selectedCell =
GetFirstSelectedTableCellElement(*selection, error);
if (NS_WARN_IF(error.Failed())) {
return error.StealNSResult();
}
if (!selectedCell) {
return NS_OK;
}
@ -3592,7 +3667,7 @@ HTMLEditor::GetSelectedCellsType(Element* aElement,
bool allCellsInRowAreSelected = false;
bool allCellsInColAreSelected = false;
while (NS_SUCCEEDED(rv) && selectedCell) {
while (selectedCell) {
CellIndexes selectedCellIndexes(*selectedCell, error);
if (NS_WARN_IF(error.Failed())) {
return error.StealNSResult();
@ -3607,7 +3682,10 @@ HTMLEditor::GetSelectedCellsType(Element* aElement,
break;
}
}
rv = GetNextSelectedCell(nullptr, getter_AddRefs(selectedCell));
DebugOnly<nsresult> rv =
GetNextSelectedCell(nullptr, getter_AddRefs(selectedCell));
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"Failed to get next selected table cell element");
}
if (allCellsInRowAreSelected) {
@ -3620,8 +3698,9 @@ HTMLEditor::GetSelectedCellsType(Element* aElement,
indexArray.Clear();
// Start at first cell again
rv = GetFirstSelectedCell(nullptr, getter_AddRefs(selectedCell));
while (NS_SUCCEEDED(rv) && selectedCell) {
IgnoredErrorResult ignoredError;
selectedCell = GetFirstSelectedTableCellElement(*selection, ignoredError);
while (selectedCell) {
CellIndexes selectedCellIndexes(*selectedCell, error);
if (NS_WARN_IF(error.Failed())) {
return error.StealNSResult();
@ -3637,7 +3716,10 @@ HTMLEditor::GetSelectedCellsType(Element* aElement,
break;
}
}
rv = GetNextSelectedCell(nullptr, getter_AddRefs(selectedCell));
DebugOnly<nsresult> rv =
GetNextSelectedCell(nullptr, getter_AddRefs(selectedCell));
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"Failed to get next selected table cell element");
}
if (allCellsInColAreSelected) {
*aSelectionType = static_cast<uint32_t>(TableSelection::Column);

View file

@ -292,10 +292,11 @@ skip-if = toolkit == 'android' && debug # bug 1480702, causes permanent failure
[test_nsITableEditor_getCellAt.html]
[test_nsITableEditor_getCellIndexes.html]
[test_nsITableEditor_getFirstRow.html]
[test_nsITableEditor_getFirstSelectedCell.html]
[test_nsITableEditor_getTableSize.html]
[test_resizers_appearance.html]
[test_resizers_resizing_elements.html]
skip-if = android_version == '18' || (verify && debug && os == 'win') # bug 1147989
skip-if = toolkit == 'android' || (verify && debug && os == 'win') # bug 1147989 and bug 1485293
[test_root_element_replacement.html]
[test_select_all_without_body.html]
[test_spellcheck_pref.html]

View file

@ -0,0 +1,198 @@
<!DOCTYPE>
<html>
<head>
<title>Test for nsITableEditor.getFirstSelectedCell()</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" href="/tests/SimpleTest/test.css">
</head>
<body>
<div id="display">
</div>
<div id="content" contenteditable></div>
<pre id="test">
</pre>
<script class="testbody" type="application/javascript">
SimpleTest.waitForExplicitFinish();
SimpleTest.waitForFocus(function() {
let editor = document.getElementById("content");
let selection = document.getSelection();
selection.collapse(editor, 0);
let rangeWrapper = {};
let cell = SpecialPowers.unwrap(getTableEditor().getFirstSelectedCell(rangeWrapper));
is(cell, null,
"nsITableEditor.getFirstSelectedCell() should return null if Selection does not select cells");
is(rangeWrapper.value, null,
"nsITableEditor.getFirstSelectedCell() should return null range if Selection does not select cells");
editor.innerHTML =
'<table id="table">' +
'<tr id="r1"><td id="c1-1">cell1-1</td><td id="c1-2">cell1-2</td><td id="c1-3">cell1-3</td><td id="c1-4" colspan="2" rowspan="2">cell1-4</td></tr>' +
'<tr id="r2"><th id="c2-1" rowspan="2">cell2-1</th><td id="c2-2">cell2-2<td id="c2-3">cell2-3</td></tr>' +
'<tr id="r3"><td id="c3-2">cell3-2</td><td id="c3-3">cell3-3</td><td id="c3-4" colspan="2">cell3-4</td></tr>' +
'<tr id="r4"><td id="c4-1" rowspan="4">cell4-1</td><td id="c4-2">cell4-2</td><td id="c4-3">cell4-3</td><th id="c4-4">cell4-4</th><td id="c4-5">cell4-5</td></tr>' +
'<tr id="r5"><th id="c5-2">cell5-2</th><th id="c5-3" colspan="2">cell5-3</th><td id="c5-5">cell5-5</td></tr>' +
'<tr id="r6"><td id="c6-2">cell6-2</td><td id="c6-3">cell6-3</td><td id="c6-4"><p>cell6-4</p></td><td id="c6-5">cell6-5</td></tr>' +
'<tr id="r7"><td id="c7-2" colspan="4">cell7-2</td></tr>' +
'</table>';
let tr = document.getElementById("r1");
selection.setBaseAndExtent(tr, 0, tr, 1);
cell = SpecialPowers.unwrap(getTableEditor().getFirstSelectedCell(rangeWrapper));
is(cell, document.getElementById("c1-1"),
"#1-1 nsITableEditor.getFirstSelectedCell() should return the first cell element in the first row");
is(rangeWrapper.value.startContainer, tr,
"#1-1 nsITableEditor.getFirstSelectedCell() should return the first <tr> element as start container of the range");
is(rangeWrapper.value.startOffset, 0,
"#1-1 nsITableEditor.getFirstSelectedCell() should return 0 as start offset of the range");
is(rangeWrapper.value.endContainer, tr,
"#1-1 nsITableEditor.getFirstSelectedCell() should return the first <tr> element as end container of the range");
is(rangeWrapper.value.endOffset, 1,
"#1-1 nsITableEditor.getFirstSelectedCell() should return 1 as end offset of the range");
tr = document.getElementById("r1");
selection.setBaseAndExtent(tr, 3, tr, 4);
cell = SpecialPowers.unwrap(getTableEditor().getFirstSelectedCell(rangeWrapper));
is(cell, document.getElementById("c1-4"),
"#1-4 nsITableEditor.getFirstSelectedCell() should return the last cell element whose colspan and rowspan are 2 in the first row");
is(rangeWrapper.value.startContainer, tr,
"#1-4 nsITableEditor.getFirstSelectedCell() should return the first <tr> element as start container of the range");
is(rangeWrapper.value.startOffset, 3,
"#1-4 nsITableEditor.getFirstSelectedCell() should return 3 as start offset of the range");
is(rangeWrapper.value.endContainer, tr,
"#1-4 nsITableEditor.getFirstSelectedCell() should return the first <tr> element as end container of the range");
is(rangeWrapper.value.endOffset, 4,
"#1-4 nsITableEditor.getFirstSelectedCell() should return 4 as end offset of the range");
tr = document.getElementById("r2");
selection.setBaseAndExtent(tr, 0, tr, 1);
cell = SpecialPowers.unwrap(getTableEditor().getFirstSelectedCell(rangeWrapper));
is(cell, document.getElementById("c2-1"),
"#2-1 nsITableEditor.getFirstSelectedCell() should return the first cell element in the second row");
is(rangeWrapper.value.startContainer, tr,
"#2-1 nsITableEditor.getFirstSelectedCell() should return the second <tr> element as start container of the range");
is(rangeWrapper.value.startOffset, 0,
"#2-1 nsITableEditor.getFirstSelectedCell() should return 0 as start offset of the range");
is(rangeWrapper.value.endContainer, tr,
"#2-1 nsITableEditor.getFirstSelectedCell() should return the second <tr> element as end container of the range");
is(rangeWrapper.value.endOffset, 1,
"#2-1 nsITableEditor.getFirstSelectedCell() should return 1 as end offset of the range");
tr = document.getElementById("r7");
selection.setBaseAndExtent(tr, 0, tr, 1);
cell = SpecialPowers.unwrap(getTableEditor().getFirstSelectedCell(rangeWrapper));
is(cell, document.getElementById("c7-2"),
"#7-2 nsITableEditor.getFirstSelectedCell() should return the second cell element in the last row");
is(rangeWrapper.value.startContainer, tr,
"#7-2 nsITableEditor.getFirstSelectedCell() should return the last <tr> element as start container of the range");
is(rangeWrapper.value.startOffset, 0,
"#7-2 nsITableEditor.getFirstSelectedCell() should return 0 as start offset of the range");
is(rangeWrapper.value.endContainer, tr,
"#7-2 nsITableEditor.getFirstSelectedCell() should return the last <tr> element as end container of the range");
is(rangeWrapper.value.endOffset, 1,
"#7-2 nsITableEditor.getFirstSelectedCell() should return 1 as end offset of the range");
tr = document.getElementById("r2");
selection.removeAllRanges();
let range = document.createRange();
range.setStart(tr, 1);
range.setEnd(tr, 2);
selection.addRange(range);
range = document.createRange();
range.setStart(tr, 2);
range.setEnd(tr, 3);
selection.addRange(range);
cell = SpecialPowers.unwrap(getTableEditor().getFirstSelectedCell(rangeWrapper));
is(cell, document.getElementById("c2-2"),
"#2-2 nsITableEditor.getFirstSelectedCell() should return the second cell element in the second row");
is(rangeWrapper.value.startContainer, tr,
"#2-2 nsITableEditor.getFirstSelectedCell() should return the second <tr> element as start container of the range");
is(rangeWrapper.value.startOffset, 1,
"#2-2 nsITableEditor.getFirstSelectedCell() should return 1 as start offset of the range");
is(rangeWrapper.value.endContainer, tr,
"#2-2 nsITableEditor.getFirstSelectedCell() should return the second <tr> element as end container of the range");
is(rangeWrapper.value.endOffset, 2,
"#2-2 nsITableEditor.getFirstSelectedCell() should return 2 as end offset of the range");
tr = document.getElementById("r3");
selection.removeAllRanges();
range = document.createRange();
range.setStart(tr, 2);
range.setEnd(tr, 3);
selection.addRange(range);
range = document.createRange();
range.setStart(document.getElementById("r5"), 0);
range.setEnd(document.getElementById("r5"), 1);
selection.addRange(range);
cell = SpecialPowers.unwrap(getTableEditor().getFirstSelectedCell(rangeWrapper));
is(cell, document.getElementById("c3-4"),
"#3-4 nsITableEditor.getFirstSelectedCell() should return the last cell element in the third row");
is(rangeWrapper.value.startContainer, tr,
"#3-4 nsITableEditor.getFirstSelectedCell() should return the third <tr> element as start container of the range");
is(rangeWrapper.value.startOffset, 2,
"#3-4 nsITableEditor.getFirstSelectedCell() should return 2 as start offset of the range");
is(rangeWrapper.value.endContainer, tr,
"#3-4 nsITableEditor.getFirstSelectedCell() should return the third <tr> element as end container of the range");
is(rangeWrapper.value.endOffset, 3,
"#3-4 nsITableEditor.getFirstSelectedCell() should return 3 as end offset of the range");
cell = document.getElementById("c6-4");
selection.selectAllChildren(cell);
cell = SpecialPowers.unwrap(getTableEditor().getFirstSelectedCell(rangeWrapper));
is(cell, null,
"nsITableEditor.getFirstSelectedCell() should return null if neither <td> nor <th> element node is selected");
is(rangeWrapper.value, null,
"nsITableEditor.getFirstSelectedCell() should return null for the range if neither <td> nor <th> element node is selected");
cell = document.getElementById("c6-5");
selection.setBaseAndExtent(cell.firstChild, 0, cell.firstChild, 0);
cell = SpecialPowers.unwrap(getTableEditor().getFirstSelectedCell(rangeWrapper));
is(cell, null,
"nsITableEditor.getFirstSelectedCell() should return null if a text node is selected");
is(rangeWrapper.value, null,
"nsITableEditor.getFirstSelectedCell() should return null for the range if a text node is selected");
// XXX If cell is not selected, nsITableEditor.getFirstSelectedCell() returns null
// without throwing exception, however, if there is no selection ranges,
// throwing an exception. This inconsistency is odd.
selection.removeAllRanges();
try {
cell = SpecialPowers.unwrap(getTableEditor().getFirstSelectedCell(rangeWrapper));
ok(false, "nsITableEditor.getFirstSelectedCell() should throw an exception if there is no selection ranges");
} catch (e) {
ok(true, "nsITableEditor.getFirstSelectedCell() should throw an exception if there is no selection ranges");
}
tr = document.getElementById("r6");
selection.setBaseAndExtent(tr, 0, tr, 1);
try {
cell = SpecialPowers.unwrap(getTableEditor().getFirstSelectedCell());
ok(false, "nsITableEditor.getFirstSelectedCell() should throw an exception if it does not have argument");
} catch (e) {
ok(true, "nsITableEditor.getFirstSelectedCell() should throw an exception if it does not have argument");
}
tr = document.getElementById("r6");
selection.setBaseAndExtent(tr, 0, tr, 1);
try {
cell = SpecialPowers.unwrap(getTableEditor().getFirstSelectedCell(null));
ok(false, "nsITableEditor.getFirstSelectedCell() should throw an exception if its argument is null");
} catch (e) {
ok(true, "nsITableEditor.getFirstSelectedCell() should throw an exception if its argument is null");
}
SimpleTest.finish();
});
function getTableEditor() {
var Ci = SpecialPowers.Ci;
var editingSession = SpecialPowers.wrap(window).docShell.editingSession;
return editingSession.getEditorForWindow(window).QueryInterface(Ci.nsITableEditor);
}
</script>
</body>
</html>

View file

@ -76,6 +76,7 @@ SimpleTest.waitForFocus(async function() {
is(rect.height, 150, description + "Sanity check the height");
// Click on the target to show the resizers
ok(true, "waiting selectionchange to select the target element");
let promiseSelectionChangeEvent = waitForSelectionChange();
synthesizeMouseAtCenter(target, {});
await promiseSelectionChangeEvent;
@ -97,9 +98,12 @@ SimpleTest.waitForFocus(async function() {
synthesizeMouseAtCenter(outOfEditor, {});
// Get the new dimensions for the target
// XXX I don't know why we need 2px margin to check this on Android.
// Fortunately, this test checks whether objects are resizable
// actually. So, bigger difference is okay.
let newRect = target.getBoundingClientRect();
isfuzzy(newRect.width, rect.width + expectedDeltaX, 1, description + "The width should be increased by " + expectedDeltaX + " pixels");
isfuzzy(newRect.height, rect.height + expectedDeltaY, 1, description + "The height should be increased by " + expectedDeltaY + "pixels");
isfuzzy(newRect.width, rect.width + expectedDeltaX, 2, description + "The width should be increased by " + expectedDeltaX + " pixels");
isfuzzy(newRect.height, rect.height + expectedDeltaY, 2, description + "The height should be increased by " + expectedDeltaY + "pixels");
}
// Account for changes in the resizing behavior when we're trying to preserve

View file

@ -303,19 +303,22 @@ interface nsITableEditor : nsISupports
*/
uint32_t getSelectedCellsType(in Element aElement);
/** Get first selected element from first selection range.
* (If multiple cells were selected this is the first in the order they were selected)
* Assumes cell-selection model where each cell
* is in a separate range (selection parent node is table row)
* @param aCell [OUT] Selected cell or null if ranges don't contain
* cell selections
* @param aRange [OUT] Optional: if not null, return the selection range
* associated with the cell
* Returns the DOM cell element
* (in C++: returns NS_EDITOR_ELEMENT_NOT_FOUND if an element is not found
* passes NS_SUCCEEDED macro)
*/
Element getFirstSelectedCell(out Range aRange);
/**
* getFirstSelectedCell() returns a <td> or <th> element if first range of
* Selection selects only one table cell element (i.e., startContainer and
* endContainer are same <tr> element and startOffset + 1 equals endOffset).
* If first range of Selection does not select a table cell element, this
* returns null. However, if Selection has no range, this throws an
* exception.
*
* @param aFirstRangeOfSelection [OUT] Returns the first range of Selection
* only when this returns a <td> or <th>
* element. Otherwise, returns null.
* @return A <td> or <th> element if first range of
* Selection selects only one table cell
* element.
*/
Element getFirstSelectedCell(out Range aFirstRangeOfSelection);
/** Get first selected element in the table
* This is the upper-left-most selected cell in table,

View file

@ -222,6 +222,10 @@ public:
return mRead->Size();
}
bool IsReadBufferReady() const {
return mRead.get() != nullptr;
}
void BindAsFramebuffer(GLContext* const gl, GLenum target) const;
void RequireBlit();

View file

@ -28,6 +28,9 @@
#if defined(MOZ_WIDGET_ANDROID)
#include "mozilla/layers/CompositorThread.h"
// Max frame duration on Android before the watchdog submits a new one.
// Probably we can get rid of this when we enforce that SubmitFrame can only be called in a VRDisplay loop.
#define ANDROID_MAX_FRAME_DURATION 4000
#endif // defined(MOZ_WIDGET_ANDROID)
@ -79,6 +82,11 @@ VRDisplayHost::VRDisplayHost(VRDeviceType aType)
mDisplayInfo.mFrameId = 0;
mDisplayInfo.mDisplayState.mPresentingGeneration = 0;
mDisplayInfo.mDisplayState.mDisplayName[0] = '\0';
#if defined(MOZ_WIDGET_ANDROID)
mLastSubmittedFrameId = 0;
mLastStartedFrame = 0;
#endif // defined(MOZ_WIDGET_ANDROID)
}
VRDisplayHost::~VRDisplayHost()
@ -200,10 +208,24 @@ VRDisplayHost::StartFrame()
{
AUTO_PROFILER_TRACING("VR", "GetSensorState");
#if defined(MOZ_WIDGET_ANDROID)
const bool isPresenting = mLastUpdateDisplayInfo.GetPresentingGroups() != 0;
double duration = mLastFrameStart.IsNull() ? 0.0 : (TimeStamp::Now() - mLastFrameStart).ToMilliseconds();
/**
* Do not start more VR frames until the last submitted frame is already processed.
*/
if (isPresenting && mLastStartedFrame > 0 && mDisplayInfo.mDisplayState.mLastSubmittedFrameId < mLastStartedFrame && duration < (double)ANDROID_MAX_FRAME_DURATION) {
return;
}
#endif // !defined(MOZ_WIDGET_ANDROID)
mLastFrameStart = TimeStamp::Now();
++mDisplayInfo.mFrameId;
mDisplayInfo.mLastSensorState[mDisplayInfo.mFrameId % kVRMaxLatencyFrames] = GetSensorState();
mFrameStarted = true;
#if defined(MOZ_WIDGET_ANDROID)
mLastStartedFrame = mDisplayInfo.mFrameId;
#endif // !defined(MOZ_WIDGET_ANDROID)
}
void
@ -316,6 +338,19 @@ VRDisplayHost::SubmitFrame(VRLayerParent* aLayer,
return;
}
#if defined(MOZ_WIDGET_ANDROID)
/**
* Do not queue more submit frames until the last submitted frame is already processed
* and the new WebGL texture is ready.
*/
if (mLastSubmittedFrameId > 0 && mLastSubmittedFrameId != mDisplayInfo.mDisplayState.mLastSubmittedFrameId) {
mLastStartedFrame = 0;
return;
}
mLastSubmittedFrameId = aFrameId;
#endif // !defined(MOZ_WIDGET_ANDROID)
mFrameStarted = false;
RefPtr<Runnable> submit =

View file

@ -101,6 +101,11 @@ private:
VRDisplayInfo mLastUpdateDisplayInfo;
TimeStamp mLastFrameStart;
bool mFrameStarted;
#if defined(MOZ_WIDGET_ANDROID)
protected:
uint64_t mLastSubmittedFrameId;
uint64_t mLastStartedFrame;
#endif // defined(MOZ_WIDGET_ANDROID)
#if defined(XP_WIN)
protected:

View file

@ -141,7 +141,7 @@ VRDisplayPresentation::~VRDisplayPresentation()
void VRDisplayPresentation::SubmitFrame()
{
for (VRLayerChild *layer : mLayers) {
layer->SubmitFrame(mDisplayClient->GetDisplayInfo().GetFrameId());
layer->SubmitFrame(mDisplayClient->GetDisplayInfo());
break; // Currently only one layer supported, submit only the first
}
}

View file

@ -52,8 +52,6 @@ using namespace mozilla::gfx::impl;
using namespace mozilla::layers;
using namespace mozilla::dom;
int VRDisplayExternal::sPushIndex = 0;
VRDisplayExternal::VRDisplayExternal(const VRDisplayState& aDisplayState)
: VRDisplayHost(VRDeviceType::External)
, mBrowserState{}
@ -108,7 +106,6 @@ VRDisplayExternal::StartPresentation()
if (mBrowserState.presentationActive) {
return;
}
sPushIndex = 0;
mTelemetry.Clear();
mTelemetry.mPresentationStart = TimeStamp::Now();
@ -118,6 +115,8 @@ VRDisplayExternal::StartPresentation()
PushState();
#if defined(MOZ_WIDGET_ANDROID)
mLastSubmittedFrameId = 0;
mLastStartedFrame = 0;
/**
* Android compositor is paused when presentation starts. That causes VRManager::NotifyVsync() not to be called.
* We post a VRTask to call VRManager::NotifyVsync() while the compositor is paused on Android.
@ -162,7 +161,6 @@ VRDisplayExternal::StopPresentation()
if (!mBrowserState.presentationActive) {
return;
}
sPushIndex = 0;
// Indicate that we have stopped immersive mode
mBrowserState.presentationActive = false;
@ -276,7 +274,6 @@ VRDisplayExternal::SubmitFrame(const layers::SurfaceDescriptor& aTexture,
layer.mRightEyeRect.height = aRightEyeRect.height;
PushState(true);
sPushIndex++;
#if defined(MOZ_WIDGET_ANDROID)
PullState([&]() {

View file

@ -31,7 +31,6 @@ class VRDisplayExternal : public VRDisplayHost
{
public:
void ZeroSensor() override;
static int sPushIndex;
protected:
VRHMDSensorState GetSensorState() override;

View file

@ -50,22 +50,38 @@ VRLayerChild::Initialize(dom::HTMLCanvasElement* aCanvasElement,
}
void
VRLayerChild::SubmitFrame(uint64_t aFrameId)
VRLayerChild::SubmitFrame(const VRDisplayInfo& aDisplayInfo)
{
uint64_t frameId = aDisplayInfo.GetFrameId();
// aFrameId will not increment unless the previuosly submitted
// frame was received by the VR thread and submitted to the VR
// compositor. We early-exit here in the event that SubmitFrame
// was called twice for the same aFrameId.
if (!mCanvasElement || aFrameId == mLastSubmittedFrameId) {
if (!mCanvasElement || frameId == mLastSubmittedFrameId) {
return;
}
mLastSubmittedFrameId = aFrameId;
// Keep the SharedSurfaceTextureClient alive long enough for
// 1 extra frame, accomodating overlapped asynchronous rendering.
mLastFrameTexture = mThisFrameTexture;
#if defined(MOZ_WIDGET_ANDROID)
/**
* Do not blit WebGL to a SurfaceTexture until the last submitted frame is already processed
* and the new frame poses are ready. SurfaceTextures need to be released in the VR render thread
* in order to allow to be used again in the WebGLContext GLScreenBuffer producer.
* Not doing so causes some freezes, crashes or other undefined behaviour.
*/
if (!mThisFrameTexture || aDisplayInfo.mDisplayState.mLastSubmittedFrameId == mLastSubmittedFrameId) {
mThisFrameTexture = mCanvasElement->GetVRFrame();
}
#else
mThisFrameTexture = mCanvasElement->GetVRFrame();
#endif // defined(MOZ_WIDGET_ANDROID)
mLastSubmittedFrameId = frameId;
if (!mThisFrameTexture) {
return;
}
@ -90,7 +106,7 @@ VRLayerChild::SubmitFrame(uint64_t aFrameId)
return;
}
SendSubmitFrame(desc, aFrameId, mLeftEyeRect, mRightEyeRect);
SendSubmitFrame(desc, frameId, mLeftEyeRect, mRightEyeRect);
}
bool

View file

@ -37,7 +37,7 @@ public:
void Initialize(dom::HTMLCanvasElement* aCanvasElement,
const gfx::Rect& aLeftEyeRect, const gfx::Rect& aRightEyeRect);
void SubmitFrame(uint64_t aFrameId);
void SubmitFrame(const VRDisplayInfo& aDisplayInfo);
bool IsIPCOpen();
private:

View file

@ -1,7 +1,6 @@
use std::ffi::{CStr, CString};
use std::{mem, slice};
use std::{mem, slice, ptr, env};
use std::path::PathBuf;
use std::ptr;
use std::rc::Rc;
use std::sync::Arc;
use std::os::raw::{c_void, c_char, c_float};
@ -884,6 +883,10 @@ pub extern "C" fn wr_renderer_update_program_cache(renderer: &mut Renderer, prog
renderer.update_program_cache(program_cache);
}
fn env_var_to_bool(key: &'static str) -> bool {
env::var(key).ok().map_or(false, |v| v.parse::<usize>().unwrap_or(1) != 0)
}
// Call MakeCurrent before this.
#[no_mangle]
pub extern "C" fn wr_window_new(window_id: WrWindowId,
@ -951,6 +954,7 @@ pub extern "C" fn wr_window_new(window_id: WrWindowId,
sampler: Some(Box::new(SamplerCallback::new(window_id))),
max_texture_size: Some(8192), // Moz2D doesn't like textures bigger than this
clear_color: Some(ColorF::new(0.0, 0.0, 0.0, 0.0)),
precache_shaders: env_var_to_bool("MOZ_WR_PRECACHE_SHADERS"),
..Default::default()
};
@ -1770,12 +1774,12 @@ pub extern "C" fn wr_dp_define_clipchain(state: &mut WrState,
-> u64 {
debug_assert!(unsafe { is_in_main_thread() });
let parent = unsafe { parent_clipchain_id.as_ref() }.map(|id| ClipChainId(*id, state.pipeline_id));
let clips_slice : Vec<ClipId> = make_slice(clips, clips_count)
let pipeline_id = state.pipeline_id;
let clips = make_slice(clips, clips_count)
.iter()
.map(|id| unpack_clip_id(*id, state.pipeline_id))
.collect();
.map(|id| unpack_clip_id(*id, pipeline_id));
let clipchain_id = state.frame_builder.dl_builder.define_clip_chain(parent, clips_slice);
let clipchain_id = state.frame_builder.dl_builder.define_clip_chain(parent, clips);
assert!(clipchain_id.1 == state.pipeline_id);
clipchain_id.0
}

View file

@ -1799,8 +1799,8 @@ Parser<FullParseHandler, CharT>::checkStatementsEOF()
// This is designed to be paired with parsing a statement list at the top
// level.
//
// The statementList() call breaks on TokenKind::Rc, so make sure we've
// reached EOF here.
// The statementList() call breaks on TokenKind::RightCurly, so make sure
// we've reached EOF here.
TokenKind tt;
if (!tokenStream.peekToken(&tt, TokenStream::Operand))
return false;
@ -2980,7 +2980,7 @@ GeneralParser<ParseHandler, CharT>::matchOrInsertSemicolon()
if (tt != TokenKind::Eof &&
tt != TokenKind::Eol &&
tt != TokenKind::Semi &&
tt != TokenKind::Rc)
tt != TokenKind::RightCurly)
{
/*
* When current token is `await` and it's outside of async function,
@ -3113,7 +3113,7 @@ GeneralParser<ParseHandler, CharT>::functionArguments(YieldHandling yieldHandlin
TokenKind tt;
if (!tokenStream.getToken(&tt, firstTokenModifier))
return false;
if (tt != TokenKind::Lp) {
if (tt != TokenKind::LeftParen) {
error(kind == FunctionSyntaxKind::Arrow
? JSMSG_BAD_ARROW_ARGS
: JSMSG_PAREN_BEFORE_FORMAL);
@ -3142,7 +3142,7 @@ GeneralParser<ParseHandler, CharT>::functionArguments(YieldHandling yieldHandlin
hasArguments = true;
} else {
bool matched;
if (!tokenStream.matchToken(&matched, TokenKind::Rp, TokenStream::Operand))
if (!tokenStream.matchToken(&matched, TokenKind::RightParen, TokenStream::Operand))
return false;
if (!matched)
hasArguments = true;
@ -3193,8 +3193,8 @@ GeneralParser<ParseHandler, CharT>::functionArguments(YieldHandling yieldHandlin
return false;
if (!TokenKindIsPossibleIdentifier(tt) &&
tt != TokenKind::Lb &&
tt != TokenKind::Lc)
tt != TokenKind::LeftBracket &&
tt != TokenKind::LeftCurly)
{
error(JSMSG_NO_REST_NAME);
return false;
@ -3202,8 +3202,8 @@ GeneralParser<ParseHandler, CharT>::functionArguments(YieldHandling yieldHandlin
}
switch (tt) {
case TokenKind::Lb:
case TokenKind::Lc: {
case TokenKind::LeftBracket:
case TokenKind::LeftCurly: {
disallowDuplicateParams = true;
if (duplicatedParam) {
// Has duplicated args before the destructuring parameter.
@ -3312,7 +3312,7 @@ GeneralParser<ParseHandler, CharT>::functionArguments(YieldHandling yieldHandlin
if (!hasRest) {
if (!tokenStream.peekToken(&tt, TokenStream::Operand))
return null();
if (tt == TokenKind::Rp)
if (tt == TokenKind::RightParen)
break;
}
}
@ -3321,7 +3321,7 @@ GeneralParser<ParseHandler, CharT>::functionArguments(YieldHandling yieldHandlin
TokenKind tt;
if (!tokenStream.getToken(&tt, TokenStream::Operand))
return false;
if (tt != TokenKind::Rp) {
if (tt != TokenKind::RightParen) {
if (kind == FunctionSyntaxKind::Setter) {
error(JSMSG_ACCESSOR_WRONG_ARGS, "setter", "one", "");
return false;
@ -3412,7 +3412,7 @@ GeneralParser<ParseHandler, CharT>::addExprAndGetNextTemplStrToken(YieldHandling
TokenKind tt;
if (!tokenStream.getToken(&tt, TokenStream::Operand))
return false;
if (tt != TokenKind::Rc) {
if (tt != TokenKind::RightCurly) {
error(JSMSG_TEMPLSTR_UNTERM_EXPR);
return false;
}
@ -3884,7 +3884,7 @@ GeneralParser<ParseHandler, CharT>::functionFormalParametersAndBody(InHandling i
if (!tokenStream.getToken(&tt, TokenStream::Operand))
return false;
uint32_t openedPos = 0;
if (tt != TokenKind::Lc) {
if (tt != TokenKind::LeftCurly) {
if (kind != FunctionSyntaxKind::Arrow) {
error(JSMSG_CURLY_BEFORE_BODY);
return false;
@ -3945,7 +3945,7 @@ GeneralParser<ParseHandler, CharT>::functionFormalParametersAndBody(InHandling i
}
if (bodyType == StatementListBody) {
MUST_MATCH_TOKEN_MOD_WITH_REPORT(TokenKind::Rc, TokenStream::Operand,
MUST_MATCH_TOKEN_MOD_WITH_REPORT(TokenKind::RightCurly, TokenStream::Operand,
reportMissingClosing(JSMSG_CURLY_AFTER_BODY,
JSMSG_CURLY_OPENED, openedPos));
funbox->setEnd(anyChars);
@ -4305,7 +4305,7 @@ GeneralParser<ParseHandler, CharT>::statementList(YieldHandling yieldHandling)
isUnexpectedEOF_ = true;
return null();
}
if (tt == TokenKind::Eof || tt == TokenKind::Rc) {
if (tt == TokenKind::Eof || tt == TokenKind::RightCurly) {
TokenPos pos;
if (!tokenStream.peekTokenPos(&pos, TokenStream::Operand)) {
return null();
@ -4351,13 +4351,13 @@ template <class ParseHandler, typename CharT>
typename ParseHandler::Node
GeneralParser<ParseHandler, CharT>::condition(InHandling inHandling, YieldHandling yieldHandling)
{
MUST_MATCH_TOKEN(TokenKind::Lp, JSMSG_PAREN_BEFORE_COND);
MUST_MATCH_TOKEN(TokenKind::LeftParen, JSMSG_PAREN_BEFORE_COND);
Node pn = exprInParens(inHandling, yieldHandling, TripledotProhibited);
if (!pn)
return null();
MUST_MATCH_TOKEN_MOD(TokenKind::Rp, TokenStream::Operand, JSMSG_PAREN_AFTER_COND);
MUST_MATCH_TOKEN_MOD(TokenKind::RightParen, TokenStream::Operand, JSMSG_PAREN_AFTER_COND);
/* Check for (a = b) and warn about possible (a == b) mistype. */
if (handler.isUnparenthesizedAssignment(pn)) {
@ -4589,10 +4589,10 @@ GeneralParser<ParseHandler, CharT>::bindingIdentifierOrPattern(DeclarationKind k
YieldHandling yieldHandling,
TokenKind tt)
{
if (tt == TokenKind::Lb)
if (tt == TokenKind::LeftBracket)
return arrayBindingPattern(kind, yieldHandling);
if (tt == TokenKind::Lc)
if (tt == TokenKind::LeftCurly)
return objectBindingPattern(kind, yieldHandling);
if (!TokenKindIsPossibleIdentifierName(tt)) {
@ -4608,7 +4608,7 @@ typename ParseHandler::Node
GeneralParser<ParseHandler, CharT>::objectBindingPattern(DeclarationKind kind,
YieldHandling yieldHandling)
{
MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Lc));
MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::LeftCurly));
if (!CheckRecursionLimit(context))
return null();
@ -4624,7 +4624,7 @@ GeneralParser<ParseHandler, CharT>::objectBindingPattern(DeclarationKind kind,
TokenKind tt;
if (!tokenStream.peekToken(&tt))
return null();
if (tt == TokenKind::Rc) {
if (tt == TokenKind::RightCurly) {
anyChars.addModifierException(TokenStream::OperandIsNone);
break;
}
@ -4723,7 +4723,7 @@ GeneralParser<ParseHandler, CharT>::objectBindingPattern(DeclarationKind kind,
}
}
MUST_MATCH_TOKEN_MOD_WITH_REPORT(TokenKind::Rc, TokenStream::Operand,
MUST_MATCH_TOKEN_MOD_WITH_REPORT(TokenKind::RightCurly, TokenStream::Operand,
reportMissingClosing(JSMSG_CURLY_AFTER_LIST,
JSMSG_CURLY_OPENED, begin));
@ -4736,7 +4736,7 @@ typename ParseHandler::Node
GeneralParser<ParseHandler, CharT>::arrayBindingPattern(DeclarationKind kind,
YieldHandling yieldHandling)
{
MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Lb));
MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::LeftBracket));
if (!CheckRecursionLimit(context))
return null();
@ -4757,7 +4757,7 @@ GeneralParser<ParseHandler, CharT>::arrayBindingPattern(DeclarationKind kind,
if (!tokenStream.getToken(&tt))
return null();
if (tt == TokenKind::Rb) {
if (tt == TokenKind::RightBracket) {
anyChars.ungetToken();
anyChars.addModifierException(TokenStream::OperandIsNone);
break;
@ -4812,7 +4812,7 @@ GeneralParser<ParseHandler, CharT>::arrayBindingPattern(DeclarationKind kind,
}
}
MUST_MATCH_TOKEN_MOD_WITH_REPORT(TokenKind::Rb, TokenStream::Operand,
MUST_MATCH_TOKEN_MOD_WITH_REPORT(TokenKind::RightBracket, TokenStream::Operand,
reportMissingClosing(JSMSG_BRACKET_AFTER_LIST,
JSMSG_BRACKET_OPENED, begin));
@ -4827,9 +4827,9 @@ GeneralParser<ParseHandler, CharT>::destructuringDeclaration(DeclarationKind kin
TokenKind tt)
{
MOZ_ASSERT(anyChars.isCurrentTokenType(tt));
MOZ_ASSERT(tt == TokenKind::Lb || tt == TokenKind::Lc);
MOZ_ASSERT(tt == TokenKind::LeftBracket || tt == TokenKind::LeftCurly);
return tt == TokenKind::Lb
return tt == TokenKind::LeftBracket
? arrayBindingPattern(kind, yieldHandling)
: objectBindingPattern(kind, yieldHandling);
}
@ -4861,7 +4861,7 @@ typename ParseHandler::Node
GeneralParser<ParseHandler, CharT>::blockStatement(YieldHandling yieldHandling,
unsigned errorNumber)
{
MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Lc));
MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::LeftCurly));
uint32_t openedPos = pos().begin;
ParseContext::Statement stmt(pc, StatementKind::Block);
@ -4873,7 +4873,7 @@ GeneralParser<ParseHandler, CharT>::blockStatement(YieldHandling yieldHandling,
if (!list)
return null();
MUST_MATCH_TOKEN_MOD_WITH_REPORT(TokenKind::Rc, TokenStream::Operand,
MUST_MATCH_TOKEN_MOD_WITH_REPORT(TokenKind::RightCurly, TokenStream::Operand,
reportMissingClosing(errorNumber, JSMSG_CURLY_OPENED,
openedPos));
@ -4900,8 +4900,8 @@ GeneralParser<ParseHandler, CharT>::declarationPattern(DeclarationKind declKind,
ParseNodeKind* forHeadKind,
Node* forInOrOfExpression)
{
MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Lb) ||
anyChars.isCurrentTokenType(TokenKind::Lc));
MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::LeftBracket) ||
anyChars.isCurrentTokenType(TokenKind::LeftCurly));
Node pattern = destructuringDeclaration(declKind, yieldHandling, tt);
if (!pattern)
@ -5124,7 +5124,7 @@ GeneralParser<ParseHandler, CharT>::declarationList(YieldHandling yieldHandling,
if (!tokenStream.getToken(&tt))
return null();
Node binding = (tt == TokenKind::Lb || tt == TokenKind::Lc)
Node binding = (tt == TokenKind::LeftBracket || tt == TokenKind::LeftCurly)
? declarationPattern(declKind, tt, initialDeclaration, yieldHandling,
forHeadKind, forInOrOfExpression)
: declarationName(declKind, tt, initialDeclaration, yieldHandling,
@ -5180,7 +5180,7 @@ template <typename CharT>
bool
Parser<FullParseHandler, CharT>::namedImportsOrNamespaceImport(TokenKind tt, Node importSpecSet)
{
if (tt == TokenKind::Lc) {
if (tt == TokenKind::LeftCurly) {
while (true) {
// Handle the forms |import {} from 'a'| and
// |import { ..., } from 'a'| (where ... is non empty), by
@ -5188,7 +5188,7 @@ Parser<FullParseHandler, CharT>::namedImportsOrNamespaceImport(TokenKind tt, Nod
if (!tokenStream.getToken(&tt))
return false;
if (tt == TokenKind::Rc)
if (tt == TokenKind::RightCurly)
break;
if (!TokenKindIsPossibleIdentifierName(tt)) {
@ -5247,7 +5247,7 @@ Parser<FullParseHandler, CharT>::namedImportsOrNamespaceImport(TokenKind tt, Nod
if (!tokenStream.getToken(&next))
return false;
if (next == TokenKind::Rc)
if (next == TokenKind::RightCurly)
break;
if (next != TokenKind::Comma) {
@ -5318,7 +5318,7 @@ Parser<FullParseHandler, CharT>::importDeclaration()
// equivalent to |import {} from 'a'|.
importSpecSet->pn_pos.end = importSpecSet->pn_pos.begin;
} else {
if (tt == TokenKind::Lc || tt == TokenKind::Mul) {
if (tt == TokenKind::LeftCurly || tt == TokenKind::Mul) {
if (!namedImportsOrNamespaceImport(tt, importSpecSet))
return null();
} else if (TokenKindIsPossibleIdentifierName(tt)) {
@ -5355,7 +5355,7 @@ Parser<FullParseHandler, CharT>::importDeclaration()
if (!tokenStream.getToken(&tt))
return null();
if (tt != TokenKind::Lc && tt != TokenKind::Mul) {
if (tt != TokenKind::LeftCurly && tt != TokenKind::Mul) {
error(JSMSG_NAMED_IMPORTS_OR_NAMESPACE_IMPORT);
return null();
}
@ -5799,7 +5799,7 @@ GeneralParser<ParseHandler, CharT>::exportClause(uint32_t begin)
if (!abortIfSyntaxParser())
return null();
MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Lc));
MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::LeftCurly));
Node kid = handler.newList(ParseNodeKind::ExportSpecList, pos());
if (!kid)
@ -5812,7 +5812,7 @@ GeneralParser<ParseHandler, CharT>::exportClause(uint32_t begin)
if (!tokenStream.getToken(&tt))
return null();
if (tt == TokenKind::Rc)
if (tt == TokenKind::RightCurly)
break;
if (!TokenKindIsPossibleIdentifierName(tt)) {
@ -5847,7 +5847,7 @@ GeneralParser<ParseHandler, CharT>::exportClause(uint32_t begin)
if (!tokenStream.getToken(&next))
return null();
if (next == TokenKind::Rc)
if (next == TokenKind::RightCurly)
break;
if (next != TokenKind::Comma) {
@ -6147,7 +6147,7 @@ GeneralParser<ParseHandler, CharT>::exportDeclaration()
case TokenKind::Mul:
return exportBatch(begin);
case TokenKind::Lc:
case TokenKind::LeftCurly:
return exportClause(begin);
case TokenKind::Var:
@ -6383,7 +6383,7 @@ GeneralParser<ParseHandler, CharT>::forHeadStart(YieldHandling yieldHandling,
Maybe<ParseContext::Scope>& forLoopLexicalScope,
Node* forInOrOfExpression)
{
MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Lp));
MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::LeftParen));
TokenKind tt;
if (!tokenStream.peekToken(&tt, TokenStream::Operand))
@ -6555,7 +6555,7 @@ GeneralParser<ParseHandler, CharT>::forStatement(YieldHandling yieldHandling)
}
}
MUST_MATCH_TOKEN_MOD_WITH_REPORT(TokenKind::Lp, TokenStream::None,
MUST_MATCH_TOKEN_MOD_WITH_REPORT(TokenKind::LeftParen, TokenStream::None,
error((token == TokenKind::Await && !pc->isAsync())
? JSMSG_FOR_AWAIT_OUTSIDE_ASYNC
: JSMSG_PAREN_AFTER_FOR));
@ -6635,7 +6635,7 @@ GeneralParser<ParseHandler, CharT>::forStatement(YieldHandling yieldHandling)
return null();
Node update;
if (tt == TokenKind::Rp) {
if (tt == TokenKind::RightParen) {
update = null();
} else {
update = expr(InAllowed, yieldHandling, TripledotProhibited);
@ -6643,7 +6643,7 @@ GeneralParser<ParseHandler, CharT>::forStatement(YieldHandling yieldHandling)
return null();
}
MUST_MATCH_TOKEN_MOD(TokenKind::Rp, TokenStream::Operand, JSMSG_PAREN_AFTER_FOR_CTRL);
MUST_MATCH_TOKEN_MOD(TokenKind::RightParen, TokenStream::Operand, JSMSG_PAREN_AFTER_FOR_CTRL);
TokenPos headPos(begin, pos().end);
forHead = handler.newForHead(init, test, update, headPos);
@ -6666,7 +6666,7 @@ GeneralParser<ParseHandler, CharT>::forStatement(YieldHandling yieldHandling)
// Parser::declaration consumed everything up to the closing ')'. That
// token follows an {Assignment,}Expression and so must be interpreted
// as an operand to be consistent with normal expression tokenizing.
MUST_MATCH_TOKEN_MOD(TokenKind::Rp, TokenStream::Operand, JSMSG_PAREN_AFTER_FOR_CTRL);
MUST_MATCH_TOKEN_MOD(TokenKind::RightParen, TokenStream::Operand, JSMSG_PAREN_AFTER_FOR_CTRL);
TokenPos headPos(begin, pos().end);
forHead = handler.newForInOrOfHead(headKind, target, iteratedExpr, headPos);
@ -6695,14 +6695,14 @@ GeneralParser<ParseHandler, CharT>::switchStatement(YieldHandling yieldHandling)
MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Switch));
uint32_t begin = pos().begin;
MUST_MATCH_TOKEN(TokenKind::Lp, JSMSG_PAREN_BEFORE_SWITCH);
MUST_MATCH_TOKEN(TokenKind::LeftParen, JSMSG_PAREN_BEFORE_SWITCH);
Node discriminant = exprInParens(InAllowed, yieldHandling, TripledotProhibited);
if (!discriminant)
return null();
MUST_MATCH_TOKEN_MOD(TokenKind::Rp, TokenStream::Operand, JSMSG_PAREN_AFTER_SWITCH);
MUST_MATCH_TOKEN(TokenKind::Lc, JSMSG_CURLY_BEFORE_SWITCH);
MUST_MATCH_TOKEN_MOD(TokenKind::RightParen, TokenStream::Operand, JSMSG_PAREN_AFTER_SWITCH);
MUST_MATCH_TOKEN(TokenKind::LeftCurly, JSMSG_CURLY_BEFORE_SWITCH);
ParseContext::Statement stmt(pc, StatementKind::Switch);
ParseContext::Scope scope(this);
@ -6718,7 +6718,7 @@ GeneralParser<ParseHandler, CharT>::switchStatement(YieldHandling yieldHandling)
while (true) {
if (!tokenStream.getToken(&tt, TokenStream::Operand))
return null();
if (tt == TokenKind::Rc)
if (tt == TokenKind::RightCurly)
break;
uint32_t caseBegin = pos().begin;
@ -6756,7 +6756,7 @@ GeneralParser<ParseHandler, CharT>::switchStatement(YieldHandling yieldHandling)
while (true) {
if (!tokenStream.peekToken(&tt, TokenStream::Operand))
return null();
if (tt == TokenKind::Rc || tt == TokenKind::Case || tt == TokenKind::Default)
if (tt == TokenKind::RightCurly || tt == TokenKind::Case || tt == TokenKind::Default)
break;
if (afterReturn) {
if (!tokenStream.peekOffset(&statementBegin, TokenStream::Operand))
@ -6886,7 +6886,7 @@ GeneralParser<ParseHandler, CharT>::returnStatement(YieldHandling yieldHandling)
case TokenKind::Eol:
case TokenKind::Eof:
case TokenKind::Semi:
case TokenKind::Rc:
case TokenKind::RightCurly:
exprNode = null();
break;
default: {
@ -6929,9 +6929,9 @@ GeneralParser<ParseHandler, CharT>::yieldExpression(InHandling inHandling)
// start an expression.
case TokenKind::Eof:
case TokenKind::Semi:
case TokenKind::Rc:
case TokenKind::Rb:
case TokenKind::Rp:
case TokenKind::RightCurly:
case TokenKind::RightBracket:
case TokenKind::RightParen:
case TokenKind::Colon:
case TokenKind::Comma:
case TokenKind::In:
@ -6970,13 +6970,13 @@ GeneralParser<ParseHandler, CharT>::withStatement(YieldHandling yieldHandling)
return null();
}
MUST_MATCH_TOKEN(TokenKind::Lp, JSMSG_PAREN_BEFORE_WITH);
MUST_MATCH_TOKEN(TokenKind::LeftParen, JSMSG_PAREN_BEFORE_WITH);
Node objectExpr = exprInParens(InAllowed, yieldHandling, TripledotProhibited);
if (!objectExpr)
return null();
MUST_MATCH_TOKEN_MOD(TokenKind::Rp, TokenStream::Operand, JSMSG_PAREN_AFTER_WITH);
MUST_MATCH_TOKEN_MOD(TokenKind::RightParen, TokenStream::Operand, JSMSG_PAREN_AFTER_WITH);
Node innerBlock;
{
@ -7067,7 +7067,7 @@ GeneralParser<ParseHandler, CharT>::throwStatement(YieldHandling yieldHandling)
TokenKind tt = TokenKind::Eof;
if (!tokenStream.peekTokenSameLine(&tt, TokenStream::Operand))
return null();
if (tt == TokenKind::Eof || tt == TokenKind::Semi || tt == TokenKind::Rc) {
if (tt == TokenKind::Eof || tt == TokenKind::Semi || tt == TokenKind::RightCurly) {
error(JSMSG_MISSING_EXPR_AFTER_THROW);
return null();
}
@ -7105,14 +7105,14 @@ GeneralParser<ParseHandler, CharT>::tryStatement(YieldHandling yieldHandling)
*
* catch lvalue nodes are either:
* a single identifier
* TokenKind::Rb or TokenKind::Rc for a destructuring left-hand side
* TokenKind::RightBracket or TokenKind::RightCurly for a destructuring left-hand side
*
* finally nodes are TokenKind::Lc statement lists.
* finally nodes are TokenKind::LeftCurly statement lists.
*/
Node innerBlock;
{
MUST_MATCH_TOKEN(TokenKind::Lc, JSMSG_CURLY_BEFORE_TRY);
MUST_MATCH_TOKEN(TokenKind::LeftCurly, JSMSG_CURLY_BEFORE_TRY);
uint32_t openedPos = pos().begin;
@ -7129,7 +7129,7 @@ GeneralParser<ParseHandler, CharT>::tryStatement(YieldHandling yieldHandling)
if (!innerBlock)
return null();
MUST_MATCH_TOKEN_MOD_WITH_REPORT(TokenKind::Rc, TokenStream::Operand,
MUST_MATCH_TOKEN_MOD_WITH_REPORT(TokenKind::RightCurly, TokenStream::Operand,
reportMissingClosing(JSMSG_CURLY_AFTER_TRY,
JSMSG_CURLY_OPENED, openedPos));
}
@ -7155,20 +7155,20 @@ GeneralParser<ParseHandler, CharT>::tryStatement(YieldHandling yieldHandling)
* where lhs is a name or a destructuring left-hand side.
*/
bool omittedBinding;
if (!tokenStream.matchToken(&omittedBinding, TokenKind::Lc))
if (!tokenStream.matchToken(&omittedBinding, TokenKind::LeftCurly))
return null();
Node catchName;
if (omittedBinding) {
catchName = null();
} else {
MUST_MATCH_TOKEN(TokenKind::Lp, JSMSG_PAREN_BEFORE_CATCH);
MUST_MATCH_TOKEN(TokenKind::LeftParen, JSMSG_PAREN_BEFORE_CATCH);
if (!tokenStream.getToken(&tt))
return null();
switch (tt) {
case TokenKind::Lb:
case TokenKind::Lc:
case TokenKind::LeftBracket:
case TokenKind::LeftCurly:
catchName = destructuringDeclaration(DeclarationKind::CatchParameter,
yieldHandling, tt);
if (!catchName)
@ -7189,9 +7189,9 @@ GeneralParser<ParseHandler, CharT>::tryStatement(YieldHandling yieldHandling)
}
}
MUST_MATCH_TOKEN_MOD(TokenKind::Rp, TokenStream::Operand, JSMSG_PAREN_AFTER_CATCH);
MUST_MATCH_TOKEN_MOD(TokenKind::RightParen, TokenStream::Operand, JSMSG_PAREN_AFTER_CATCH);
MUST_MATCH_TOKEN(TokenKind::Lc, JSMSG_CURLY_BEFORE_CATCH);
MUST_MATCH_TOKEN(TokenKind::LeftCurly, JSMSG_CURLY_BEFORE_CATCH);
}
Node catchBody = catchBlockStatement(yieldHandling, scope);
@ -7213,7 +7213,7 @@ GeneralParser<ParseHandler, CharT>::tryStatement(YieldHandling yieldHandling)
Node finallyBlock = null();
if (tt == TokenKind::Finally) {
MUST_MATCH_TOKEN(TokenKind::Lc, JSMSG_CURLY_BEFORE_FINALLY);
MUST_MATCH_TOKEN(TokenKind::LeftCurly, JSMSG_CURLY_BEFORE_FINALLY);
uint32_t openedPos = pos().begin;
@ -7230,7 +7230,7 @@ GeneralParser<ParseHandler, CharT>::tryStatement(YieldHandling yieldHandling)
if (!finallyBlock)
return null();
MUST_MATCH_TOKEN_MOD_WITH_REPORT(TokenKind::Rc, TokenStream::Operand,
MUST_MATCH_TOKEN_MOD_WITH_REPORT(TokenKind::RightCurly, TokenStream::Operand,
reportMissingClosing(JSMSG_CURLY_AFTER_FINALLY,
JSMSG_CURLY_OPENED, openedPos));
} else {
@ -7270,7 +7270,7 @@ GeneralParser<ParseHandler, CharT>::catchBlockStatement(YieldHandling yieldHandl
if (!list)
return null();
MUST_MATCH_TOKEN_MOD_WITH_REPORT(TokenKind::Rc, TokenStream::Operand,
MUST_MATCH_TOKEN_MOD_WITH_REPORT(TokenKind::RightCurly, TokenStream::Operand,
reportMissingClosing(JSMSG_CURLY_AFTER_CATCH,
JSMSG_CURLY_OPENED, openedPos));
@ -7385,7 +7385,7 @@ GeneralParser<ParseHandler, CharT>::classDefinition(YieldHandling yieldHandling,
return null();
}
MUST_MATCH_TOKEN(TokenKind::Lc, JSMSG_CURLY_BEFORE_CLASS);
MUST_MATCH_TOKEN(TokenKind::LeftCurly, JSMSG_CURLY_BEFORE_CLASS);
Node classMethods = handler.newClassMethodList(pos().begin);
if (!classMethods)
@ -7396,7 +7396,7 @@ GeneralParser<ParseHandler, CharT>::classDefinition(YieldHandling yieldHandling,
TokenKind tt;
if (!tokenStream.getToken(&tt))
return null();
if (tt == TokenKind::Rc)
if (tt == TokenKind::RightCurly)
break;
if (tt == TokenKind::Semi)
@ -7406,13 +7406,13 @@ GeneralParser<ParseHandler, CharT>::classDefinition(YieldHandling yieldHandling,
if (tt == TokenKind::Static) {
if (!tokenStream.peekToken(&tt))
return null();
if (tt == TokenKind::Rc) {
if (tt == TokenKind::RightCurly) {
tokenStream.consumeKnownToken(tt);
error(JSMSG_UNEXPECTED_TOKEN, "property name", TokenKindToDesc(tt));
return null();
}
if (tt != TokenKind::Lp)
if (tt != TokenKind::LeftParen)
isStatic = true;
else
anyChars.ungetToken();
@ -7458,7 +7458,7 @@ GeneralParser<ParseHandler, CharT>::classDefinition(YieldHandling yieldHandling,
switch (propType) {
case PropertyType::Getter:
case PropertyType::Setter:
if (!anyChars.isCurrentTokenType(TokenKind::Rb)) {
if (!anyChars.isCurrentTokenType(TokenKind::RightBracket)) {
funName = prefixAccessorName(propType, propAtom);
if (!funName)
return null();
@ -7469,7 +7469,7 @@ GeneralParser<ParseHandler, CharT>::classDefinition(YieldHandling yieldHandling,
funName = name;
break;
default:
if (!anyChars.isCurrentTokenType(TokenKind::Rb))
if (!anyChars.isCurrentTokenType(TokenKind::RightBracket))
funName = propAtom;
}
@ -7547,7 +7547,7 @@ ParserBase::nextTokenContinuesLetDeclaration(TokenKind next)
TokenStreamShared::verifyConsistentModifier(TokenStreamShared::None, anyChars.nextToken());
// Destructuring continues a let declaration.
if (next == TokenKind::Lb || next == TokenKind::Lc)
if (next == TokenKind::LeftBracket || next == TokenKind::LeftCurly)
return true;
// A "let" edge case deserves special comment. Consider this:
@ -7592,7 +7592,7 @@ GeneralParser<ParseHandler, CharT>::statement(YieldHandling yieldHandling)
switch (tt) {
// BlockStatement[?Yield, ?Return]
case TokenKind::Lc:
case TokenKind::LeftCurly:
return blockStatement(yieldHandling);
// VariableStatement[?Yield]
@ -7641,10 +7641,10 @@ GeneralParser<ParseHandler, CharT>::statement(YieldHandling yieldHandling)
if (tt == TokenKind::Let) {
bool forbiddenLetDeclaration = false;
if (next == TokenKind::Lb) {
if (next == TokenKind::LeftBracket) {
// Enforce ExpressionStatement's 'let [' lookahead restriction.
forbiddenLetDeclaration = true;
} else if (next == TokenKind::Lc || TokenKindIsPossibleIdentifier(next)) {
} else if (next == TokenKind::LeftCurly || TokenKindIsPossibleIdentifier(next)) {
// 'let {' and 'let foo' aren't completely forbidden, if ASI
// causes 'let' to be the entire Statement. But if they're
// same-line, we can aggressively give a better error message.
@ -7656,7 +7656,7 @@ GeneralParser<ParseHandler, CharT>::statement(YieldHandling yieldHandling)
return null();
MOZ_ASSERT(TokenKindIsPossibleIdentifier(nextSameLine) ||
nextSameLine == TokenKind::Lc ||
nextSameLine == TokenKind::LeftCurly ||
nextSameLine == TokenKind::Eol);
forbiddenLetDeclaration = nextSameLine != TokenKind::Eol;
@ -7810,7 +7810,7 @@ GeneralParser<ParseHandler, CharT>::statementListItem(YieldHandling yieldHandlin
switch (tt) {
// BlockStatement[?Yield, ?Return]
case TokenKind::Lc:
case TokenKind::LeftCurly:
return blockStatement(yieldHandling);
// VariableStatement[?Yield]
@ -8019,13 +8019,13 @@ GeneralParser<ParseHandler, CharT>::expr(InHandling inHandling, YieldHandling yi
if (!tokenStream.peekToken(&tt, TokenStream::Operand))
return null();
if (tt == TokenKind::Rp) {
tokenStream.consumeKnownToken(TokenKind::Rp, TokenStream::Operand);
if (tt == TokenKind::RightParen) {
tokenStream.consumeKnownToken(TokenKind::RightParen, TokenStream::Operand);
if (!tokenStream.peekToken(&tt))
return null();
if (tt != TokenKind::Arrow) {
error(JSMSG_UNEXPECTED_TOKEN, "expression", TokenKindToDesc(TokenKind::Rp));
error(JSMSG_UNEXPECTED_TOKEN, "expression", TokenKindToDesc(TokenKind::RightParen));
return null();
}
@ -8362,7 +8362,7 @@ GeneralParser<ParseHandler, CharT>::assignExpr(InHandling inHandling, YieldHandl
// The AsyncArrowFunction production are
// async [no LineTerminator here] AsyncArrowBindingIdentifier ...
// async [no LineTerminator here] ArrowFormalParameters ...
if (TokenKindIsPossibleIdentifier(nextSameLine) || nextSameLine == TokenKind::Lp)
if (TokenKindIsPossibleIdentifier(nextSameLine) || nextSameLine == TokenKind::LeftParen)
asyncKind = FunctionAsyncKind::AsyncFunction;
else
anyChars.ungetToken();
@ -8683,7 +8683,7 @@ GeneralParser<ParseHandler, CharT>::argumentList(YieldHandling yieldHandling, bo
return null();
bool matched;
if (!tokenStream.matchToken(&matched, TokenKind::Rp, TokenStream::Operand))
if (!tokenStream.matchToken(&matched, TokenKind::RightParen, TokenStream::Operand))
return null();
if (matched) {
handler.setEndPosition(argsList, pos().end);
@ -8721,11 +8721,11 @@ GeneralParser<ParseHandler, CharT>::argumentList(YieldHandling yieldHandling, bo
TokenKind tt;
if (!tokenStream.peekToken(&tt, TokenStream::Operand))
return null();
if (tt == TokenKind::Rp)
if (tt == TokenKind::RightParen)
break;
}
MUST_MATCH_TOKEN_MOD(TokenKind::Rp, TokenStream::Operand, JSMSG_PAREN_AFTER_ARGS);
MUST_MATCH_TOKEN_MOD(TokenKind::RightParen, TokenStream::Operand, JSMSG_PAREN_AFTER_ARGS);
handler.setEndPosition(argsList, pos().end);
return argsList;
@ -8775,7 +8775,7 @@ GeneralParser<ParseHandler, CharT>::memberExpr(YieldHandling yieldHandling,
return null();
bool matched;
if (!tokenStream.matchToken(&matched, TokenKind::Lp))
if (!tokenStream.matchToken(&matched, TokenKind::LeftParen))
return null();
bool isSpread = false;
@ -8843,12 +8843,12 @@ GeneralParser<ParseHandler, CharT>::memberExpr(YieldHandling yieldHandling,
error(JSMSG_NAME_AFTER_DOT);
return null();
}
} else if (tt == TokenKind::Lb) {
} else if (tt == TokenKind::LeftBracket) {
Node propExpr = expr(InAllowed, yieldHandling, TripledotProhibited);
if (!propExpr)
return null();
MUST_MATCH_TOKEN_MOD(TokenKind::Rb, TokenStream::Operand, JSMSG_BRACKET_IN_INDEX);
MUST_MATCH_TOKEN_MOD(TokenKind::RightBracket, TokenStream::Operand, JSMSG_BRACKET_IN_INDEX);
if (handler.isSuperBase(lhs) && !checkAndMarkSuperScope()) {
error(JSMSG_BAD_SUPERPROP, "member");
@ -8857,7 +8857,7 @@ GeneralParser<ParseHandler, CharT>::memberExpr(YieldHandling yieldHandling,
nextMember = handler.newPropertyByValue(lhs, propExpr, pos().end);
if (!nextMember)
return null();
} else if ((allowCallSyntax && tt == TokenKind::Lp) ||
} else if ((allowCallSyntax && tt == TokenKind::LeftParen) ||
tt == TokenKind::TemplateHead ||
tt == TokenKind::NoSubsTemplate)
{
@ -8867,7 +8867,7 @@ GeneralParser<ParseHandler, CharT>::memberExpr(YieldHandling yieldHandling,
return null();
}
if (tt != TokenKind::Lp) {
if (tt != TokenKind::LeftParen) {
error(JSMSG_BAD_SUPER);
return null();
}
@ -8912,7 +8912,7 @@ GeneralParser<ParseHandler, CharT>::memberExpr(YieldHandling yieldHandling,
} else if (prop == context->names().call) {
op = JSOP_FUNCALL;
}
} else if (tt == TokenKind::Lp) {
} else if (tt == TokenKind::LeftParen) {
if (handler.isAsyncKeyword(lhs, context)) {
// |async (| can be the start of an async arrow
// function, so we need to defer reporting possible
@ -8941,7 +8941,7 @@ GeneralParser<ParseHandler, CharT>::memberExpr(YieldHandling yieldHandling,
}
}
if (tt == TokenKind::Lp) {
if (tt == TokenKind::LeftParen) {
bool isSpread = false;
PossibleError* asyncPossibleError = maybeAsyncArrow ? possibleError : nullptr;
Node args = argumentList(yieldHandling, &isSpread, asyncPossibleError);
@ -9342,7 +9342,7 @@ typename ParseHandler::Node
GeneralParser<ParseHandler, CharT>::arrayInitializer(YieldHandling yieldHandling,
PossibleError* possibleError)
{
MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Lb));
MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::LeftBracket));
uint32_t begin = pos().begin;
Node literal = handler.newArrayLiteral(begin);
@ -9353,7 +9353,7 @@ GeneralParser<ParseHandler, CharT>::arrayInitializer(YieldHandling yieldHandling
if (!tokenStream.getToken(&tt, TokenStream::Operand))
return null();
if (tt == TokenKind::Rb) {
if (tt == TokenKind::RightBracket) {
/*
* Mark empty arrays as non-constant, since we cannot easily
* determine their type.
@ -9371,7 +9371,7 @@ GeneralParser<ParseHandler, CharT>::arrayInitializer(YieldHandling yieldHandling
TokenKind tt;
if (!tokenStream.peekToken(&tt, TokenStream::Operand))
return null();
if (tt == TokenKind::Rb)
if (tt == TokenKind::RightBracket)
break;
if (tt == TokenKind::Comma) {
@ -9432,7 +9432,7 @@ GeneralParser<ParseHandler, CharT>::arrayInitializer(YieldHandling yieldHandling
possibleError->setPendingDestructuringErrorAt(pos(), JSMSG_REST_WITH_COMMA);
}
MUST_MATCH_TOKEN_MOD_WITH_REPORT(TokenKind::Rb, TokenStream::Operand,
MUST_MATCH_TOKEN_MOD_WITH_REPORT(TokenKind::RightBracket, TokenStream::Operand,
reportMissingClosing(JSMSG_BRACKET_AFTER_LIST,
JSMSG_BRACKET_OPENED, begin));
}
@ -9453,7 +9453,7 @@ GeneralParser<ParseHandler, CharT>::propertyName(YieldHandling yieldHandling,
if (!tokenStream.getToken(&ltok))
return null();
MOZ_ASSERT(ltok != TokenKind::Rc, "caller should have handled TokenKind::Rc");
MOZ_ASSERT(ltok != TokenKind::RightCurly, "caller should have handled TokenKind::RightCurly");
bool isGenerator = false;
bool isAsync = false;
@ -9479,7 +9479,7 @@ GeneralParser<ParseHandler, CharT>::propertyName(YieldHandling yieldHandling,
TokenKind tt = TokenKind::Eof;
if (!tokenStream.peekTokenSameLine(&tt))
return null();
if (tt == TokenKind::String || tt == TokenKind::Number || tt == TokenKind::Lb ||
if (tt == TokenKind::String || tt == TokenKind::Number || tt == TokenKind::LeftBracket ||
TokenKindIsPossibleIdentifierName(tt) || tt == TokenKind::Mul)
{
isAsync = true;
@ -9521,7 +9521,7 @@ GeneralParser<ParseHandler, CharT>::propertyName(YieldHandling yieldHandling,
break;
}
case TokenKind::Lb:
case TokenKind::LeftBracket:
propName = computedPropertyName(yieldHandling, maybeDecl, propList);
if (!propName)
return null();
@ -9577,8 +9577,8 @@ GeneralParser<ParseHandler, CharT>::propertyName(YieldHandling yieldHandling,
return null();
return newNumber(anyChars.currentToken());
}
if (tt == TokenKind::Lb) {
tokenStream.consumeKnownToken(TokenKind::Lb);
if (tt == TokenKind::LeftBracket) {
tokenStream.consumeKnownToken(TokenKind::LeftBracket);
return computedPropertyName(yieldHandling, maybeDecl, propList);
}
@ -9605,7 +9605,7 @@ GeneralParser<ParseHandler, CharT>::propertyName(YieldHandling yieldHandling,
}
if (TokenKindIsPossibleIdentifierName(ltok) &&
(tt == TokenKind::Comma || tt == TokenKind::Rc || tt == TokenKind::Assign))
(tt == TokenKind::Comma || tt == TokenKind::RightCurly || tt == TokenKind::Assign))
{
if (isGenerator || isAsync) {
error(JSMSG_BAD_PROP_ID);
@ -9620,7 +9620,7 @@ GeneralParser<ParseHandler, CharT>::propertyName(YieldHandling yieldHandling,
return propName;
}
if (tt == TokenKind::Lp) {
if (tt == TokenKind::LeftParen) {
anyChars.ungetToken();
if (isGenerator && isAsync)
@ -9644,7 +9644,7 @@ GeneralParser<ParseHandler, CharT>::computedPropertyName(YieldHandling yieldHand
const Maybe<DeclarationKind>& maybeDecl,
Node literal)
{
MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Lb));
MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::LeftBracket));
uint32_t begin = pos().begin;
@ -9659,7 +9659,7 @@ GeneralParser<ParseHandler, CharT>::computedPropertyName(YieldHandling yieldHand
if (!assignNode)
return null();
MUST_MATCH_TOKEN_MOD(TokenKind::Rb, TokenStream::Operand, JSMSG_COMP_PROP_UNTERM_EXPR);
MUST_MATCH_TOKEN_MOD(TokenKind::RightBracket, TokenStream::Operand, JSMSG_COMP_PROP_UNTERM_EXPR);
return handler.newComputedName(assignNode, begin, pos().end);
}
@ -9668,7 +9668,7 @@ typename ParseHandler::Node
GeneralParser<ParseHandler, CharT>::objectLiteral(YieldHandling yieldHandling,
PossibleError* possibleError)
{
MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Lc));
MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::LeftCurly));
uint32_t openedPos = pos().begin;
@ -9684,7 +9684,7 @@ GeneralParser<ParseHandler, CharT>::objectLiteral(YieldHandling yieldHandling,
TokenKind tt;
if (!tokenStream.peekToken(&tt))
return null();
if (tt == TokenKind::Rc) {
if (tt == TokenKind::RightCurly) {
anyChars.addModifierException(TokenStream::OperandIsNone);
break;
}
@ -9845,7 +9845,7 @@ GeneralParser<ParseHandler, CharT>::objectLiteral(YieldHandling yieldHandling,
return null();
} else {
RootedAtom funName(context);
if (!anyChars.isCurrentTokenType(TokenKind::Rb)) {
if (!anyChars.isCurrentTokenType(TokenKind::RightBracket)) {
funName = propAtom;
if (propType == PropertyType::Getter || propType == PropertyType::Setter) {
@ -9879,7 +9879,7 @@ GeneralParser<ParseHandler, CharT>::objectLiteral(YieldHandling yieldHandling,
possibleError->setPendingDestructuringErrorAt(pos(), JSMSG_REST_WITH_COMMA);
}
MUST_MATCH_TOKEN_MOD_WITH_REPORT(TokenKind::Rc, TokenStream::Operand,
MUST_MATCH_TOKEN_MOD_WITH_REPORT(TokenKind::RightCurly, TokenStream::Operand,
reportMissingClosing(JSMSG_CURLY_AFTER_LIST,
JSMSG_CURLY_OPENED, openedPos));
@ -10042,26 +10042,26 @@ GeneralParser<ParseHandler, CharT>::primaryExpr(YieldHandling yieldHandling,
case TokenKind::Class:
return classDefinition(yieldHandling, ClassExpression, NameRequired);
case TokenKind::Lb:
case TokenKind::LeftBracket:
return arrayInitializer(yieldHandling, possibleError);
case TokenKind::Lc:
case TokenKind::LeftCurly:
return objectLiteral(yieldHandling, possibleError);
case TokenKind::Lp: {
case TokenKind::LeftParen: {
TokenKind next;
if (!tokenStream.peekToken(&next, TokenStream::Operand))
return null();
if (next == TokenKind::Rp) {
if (next == TokenKind::RightParen) {
// Not valid expression syntax, but this is valid in an arrow function
// with no params: `() => body`.
tokenStream.consumeKnownToken(TokenKind::Rp, TokenStream::Operand);
tokenStream.consumeKnownToken(TokenKind::RightParen, TokenStream::Operand);
if (!tokenStream.peekToken(&next))
return null();
if (next != TokenKind::Arrow) {
error(JSMSG_UNEXPECTED_TOKEN, "expression", TokenKindToDesc(TokenKind::Rp));
error(JSMSG_UNEXPECTED_TOKEN, "expression", TokenKindToDesc(TokenKind::RightParen));
return null();
}
@ -10075,7 +10075,7 @@ GeneralParser<ParseHandler, CharT>::primaryExpr(YieldHandling yieldHandling,
Node expr = exprInParens(InAllowed, yieldHandling, TripledotAllowed, possibleError);
if (!expr)
return null();
MUST_MATCH_TOKEN_MOD(TokenKind::Rp, TokenStream::Operand, JSMSG_PAREN_IN_PAREN);
MUST_MATCH_TOKEN_MOD(TokenKind::RightParen, TokenStream::Operand, JSMSG_PAREN_IN_PAREN);
return handler.parenthesize(expr);
}
@ -10153,7 +10153,7 @@ GeneralParser<ParseHandler, CharT>::primaryExpr(YieldHandling yieldHandling,
if (!tokenStream.getToken(&next))
return null();
if (next == TokenKind::Lb || next == TokenKind::Lc) {
if (next == TokenKind::LeftBracket || next == TokenKind::LeftCurly) {
// Validate, but don't store the pattern right now. The whole arrow
// function is reparsed in functionFormalParametersAndBody().
if (!destructuringDeclaration(DeclarationKind::CoverArrowParameter, yieldHandling,
@ -10174,7 +10174,7 @@ GeneralParser<ParseHandler, CharT>::primaryExpr(YieldHandling yieldHandling,
if (!tokenStream.getToken(&next))
return null();
if (next != TokenKind::Rp) {
if (next != TokenKind::RightParen) {
error(JSMSG_UNEXPECTED_TOKEN, "closing parenthesis", TokenKindToDesc(next));
return null();
}
@ -10190,7 +10190,7 @@ GeneralParser<ParseHandler, CharT>::primaryExpr(YieldHandling yieldHandling,
anyChars.ungetToken(); // put back right paren
// Return an arbitrary expression node. See case TokenKind::Rp above.
// Return an arbitrary expression node. See case TokenKind::RightParen above.
return handler.newNullLiteral(pos());
}
}
@ -10203,7 +10203,7 @@ GeneralParser<ParseHandler, CharT>::exprInParens(InHandling inHandling,
TripledotHandling tripledotHandling,
PossibleError* possibleError /* = nullptr */)
{
MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Lp));
MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::LeftParen));
return expr(inHandling, yieldHandling, tripledotHandling, possibleError, PredictInvoked);
}

View file

@ -66,12 +66,12 @@
macro(Dec, "'--'") /* decrement */ \
macro(Dot, "'.'") /* member operator */ \
macro(TripleDot, "'...'") /* rest arguments and spread operator */ \
macro(Lb, "'['") \
macro(Rb, "']'") \
macro(Lc, "'{'") \
macro(Rc, "'}'") \
macro(Lp, "'('") \
macro(Rp, "')'") \
macro(LeftBracket, "'['") \
macro(RightBracket, "']'") \
macro(LeftCurly, "'{'") \
macro(RightCurly, "'}'") \
macro(LeftParen, "'('") \
macro(RightParen, "')'") \
macro(Name, "identifier") \
macro(Number, "numeric literal") \
macro(String, "string literal") \

View file

@ -443,9 +443,9 @@ TokenStreamAnyChars::TokenStreamAnyChars(JSContext* cx, const ReadOnlyCompileOpt
isExprEnding[size_t(TokenKind::Comma)] = true;
isExprEnding[size_t(TokenKind::Semi)] = true;
isExprEnding[size_t(TokenKind::Colon)] = true;
isExprEnding[size_t(TokenKind::Rp)] = true;
isExprEnding[size_t(TokenKind::Rb)] = true;
isExprEnding[size_t(TokenKind::Rc)] = true;
isExprEnding[size_t(TokenKind::RightParen)] = true;
isExprEnding[size_t(TokenKind::RightBracket)] = true;
isExprEnding[size_t(TokenKind::RightCurly)] = true;
}
template<typename CharT>
@ -1974,14 +1974,14 @@ enum FirstCharKind {
#define T_COMMA size_t(TokenKind::Comma)
#define T_COLON size_t(TokenKind::Colon)
#define T_BITNOT size_t(TokenKind::BitNot)
#define T_LP size_t(TokenKind::Lp)
#define T_RP size_t(TokenKind::Rp)
#define T_LP size_t(TokenKind::LeftParen)
#define T_RP size_t(TokenKind::RightParen)
#define T_SEMI size_t(TokenKind::Semi)
#define T_HOOK size_t(TokenKind::Hook)
#define T_LB size_t(TokenKind::Lb)
#define T_RB size_t(TokenKind::Rb)
#define T_LC size_t(TokenKind::Lc)
#define T_RC size_t(TokenKind::Rc)
#define T_LB size_t(TokenKind::LeftBracket)
#define T_RB size_t(TokenKind::RightBracket)
#define T_LC size_t(TokenKind::LeftCurly)
#define T_RC size_t(TokenKind::RightCurly)
#define _______ Other
static const uint8_t firstCharKinds[] = {
/* 0 1 2 3 4 5 6 7 8 9 */

View file

@ -5643,7 +5643,7 @@ CheckModuleReturn(ModuleValidator& m)
return false;
auto& ts = m.parser().tokenStream;
if (tk != TokenKind::Return) {
return m.failCurrentOffset((tk == TokenKind::Rc || tk == TokenKind::Eof)
return m.failCurrentOffset((tk == TokenKind::RightCurly || tk == TokenKind::Eof)
? "expecting return statement"
: "invalid asm.js. statement");
}
@ -5675,7 +5675,7 @@ CheckModuleEnd(ModuleValidator &m)
if (!GetToken(m.parser(), &tk))
return false;
if (tk != TokenKind::Eof && tk != TokenKind::Rc)
if (tk != TokenKind::Eof && tk != TokenKind::RightCurly)
return m.failCurrentOffset("top-level export (return) must be the last statement");
m.parser().tokenStream.anyCharsAccess().ungetToken();

View file

@ -4612,7 +4612,7 @@ nsBlockFrame::PlaceLine(BlockReflowInput& aState,
0 == mLines.front()->BSize() &&
aLine == mLines.begin().next()))) {
ReflowOutput metrics(aState.mReflowInput);
nsIFrame* bullet = GetOutsideBullet();
nsBulletFrame* bullet = GetOutsideBullet();
ReflowBullet(bullet, aState, metrics, aState.mBCoord);
NS_ASSERTION(!BulletIsEmpty() || metrics.BSize(wm) == 0,
"empty bullet took up space");

View file

@ -13,6 +13,7 @@
#include "LayoutLogging.h"
#include "SVGTextFrame.h"
#include "nsBlockFrame.h"
#include "nsBulletFrame.h"
#include "nsFontMetrics.h"
#include "nsStyleConsts.h"
#include "nsContainerFrame.h"
@ -1448,7 +1449,7 @@ nsLineLayout::PlaceFrame(PerFrameData* pfd, ReflowOutput& aMetrics)
}
void
nsLineLayout::AddBulletFrame(nsIFrame* aFrame,
nsLineLayout::AddBulletFrame(nsBulletFrame* aFrame,
const ReflowOutput& aMetrics)
{
NS_ASSERTION(mCurrentSpan == mRootSpan, "bad linelayout user");
@ -1464,7 +1465,14 @@ nsLineLayout::AddBulletFrame(nsIFrame* aFrame,
WritingMode lineWM = mRootSpan->mWritingMode;
PerFrameData* pfd = NewPerFrameData(aFrame);
mRootSpan->AppendFrame(pfd);
PerSpanData* psd = mRootSpan;
MOZ_ASSERT(psd->mFirstFrame, "adding bullet to an empty line?");
// Prepend the bullet frame to the line.
psd->mFirstFrame->mPrev = pfd;
pfd->mNext = psd->mFirstFrame;
psd->mFirstFrame = pfd;
pfd->mIsBullet = true;
if (aMetrics.BlockStartAscent() == ReflowOutput::ASK_FOR_BASELINE) {
pfd->mAscent = aFrame->GetLogicalBaseline(lineWM);
@ -1477,6 +1485,21 @@ nsLineLayout::AddBulletFrame(nsIFrame* aFrame,
pfd->mOverflowAreas = aMetrics.mOverflowAreas;
}
void
nsLineLayout::RemoveBulletFrame(nsBulletFrame* aFrame)
{
PerSpanData* psd = mCurrentSpan;
MOZ_ASSERT(psd == mRootSpan, "bullet on non-root span?");
MOZ_ASSERT(psd->mFirstFrame->mFrame == aFrame,
"bullet is not the first frame?");
PerFrameData* pfd = psd->mFirstFrame;
MOZ_ASSERT(pfd != psd->mLastFrame,
"bullet is the only frame?");
pfd->mNext->mPrev = nullptr;
psd->mFirstFrame = pfd->mNext;
FreeFrame(pfd);
}
#ifdef DEBUG
void
nsLineLayout::DumpPerSpanData(PerSpanData* psd, int32_t aIndent)
@ -3286,7 +3309,15 @@ nsLineLayout::TextAlignLine(nsLineBox* aLine,
if (mPresContext->BidiEnabled() &&
(!mPresContext->IsVisualMode() || !lineWM.IsBidiLTR())) {
nsBidiPresUtils::ReorderFrames(psd->mFirstFrame->mFrame,
PerFrameData* startFrame = psd->mFirstFrame;
MOZ_ASSERT(startFrame, "empty line?");
if (startFrame->mIsBullet) {
// Bullet shouldn't participate in bidi reordering.
startFrame = startFrame->mNext;
MOZ_ASSERT(startFrame, "no frame after bullet?");
MOZ_ASSERT(!startFrame->mIsBullet, "multiple bullets?");
}
nsBidiPresUtils::ReorderFrames(startFrame->mFrame,
aLine->GetChildCount(),
lineWM, mContainerSize,
psd->mIStart + mTextIndent + dx);

View file

@ -16,6 +16,7 @@
#include "BlockReflowInput.h"
#include "nsLineBox.h"
class nsBulletFrame;
class nsFloatManager;
struct nsStyleText;
@ -101,11 +102,9 @@ public:
ReflowOutput* aMetrics,
bool& aPushedFrame);
void AddBulletFrame(nsIFrame* aFrame, const ReflowOutput& aMetrics);
void AddBulletFrame(nsBulletFrame* aFrame, const ReflowOutput& aMetrics);
void RemoveBulletFrame(nsIFrame* aFrame) {
PushFrame(aFrame);
}
void RemoveBulletFrame(nsBulletFrame* aFrame);
/**
* Place frames in the block direction (CSS property vertical-align)
@ -547,10 +546,9 @@ protected:
nscoord* mBaseline;
void AppendFrame(PerFrameData* pfd) {
if (nullptr == mLastFrame) {
if (!mLastFrame) {
mFirstFrame = pfd;
}
else {
} else {
mLastFrame->mNext = pfd;
pfd->mPrev = mLastFrame;
}

View file

@ -0,0 +1,11 @@
<!DOCTYPE html>
<style>
li {
margin-left: 100px;
font-size: 100px;
}
span {
color: transparent;
}
</style>
<li><span>x x</span></li>

View file

@ -0,0 +1,12 @@
<!DOCTYPE html>
<style>
li {
margin-left: 100px;
font-size: 100px;
text-align-last: justify;
}
span {
color: transparent;
}
</style>
<li><span>x x</span></li>

View file

@ -12,3 +12,4 @@ asserts(1) == ol-reversed-1b.html ol-reversed-1-ref.html # bug 478135
== bullet-space-2.html bullet-space-2-ref.html
== bullet-intrinsic-isize-1.html bullet-intrinsic-isize-1-ref.html
== bullet-intrinsic-isize-2.html bullet-intrinsic-isize-2-ref.html
== bullet-justify-1.html bullet-justify-1-ref.html

View file

@ -1102,6 +1102,7 @@ function runTests() {
test_parseable(":-moz-tree-row(selected)");
test_parseable("::-moz-tree-row(selected)");
test_parseable("::-MoZ-trEE-RoW(sElEcTeD)");
test_parseable(":-moz-tree-row(selected focus)");
test_parseable(":-moz-tree-row(selected , focus)");
test_parseable("::-moz-tree-row(selected ,focus)");

View file

@ -45,7 +45,7 @@ public class FirstrunPanel extends Fragment {
@Override
public void onClick(View v) {
Telemetry.sendUIEvent(TelemetryContract.Event.ACTION, TelemetryContract.Method.BUTTON, "firstrun-next");
pagerNavigation.next();
next();
}
});

View file

@ -103,12 +103,6 @@ component {5850c76e-b916-4218-b99a-31f004e0a7e7} TabSource.js
contract @mozilla.org/tab-source-service;1 {5850c76e-b916-4218-b99a-31f004e0a7e7}
#endif
# Snippets.js
component {a78d7e59-b558-4321-a3d6-dffe2f1e76dd} Snippets.js
contract @mozilla.org/snippets;1 {a78d7e59-b558-4321-a3d6-dffe2f1e76dd}
category browser-delayed-startup-finished Snippets @mozilla.org/snippets;1
category update-timer Snippets @mozilla.org/snippets;1,getService,snippets-update-timer,browser.snippets.updateInterval,86400
# ColorPicker.js
component {430b987f-bb9f-46a3-99a5-241749220b29} ColorPicker.js
contract @mozilla.org/colorpicker;1 {430b987f-bb9f-46a3-99a5-241749220b29}

View file

@ -1,449 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
ChromeUtils.import("resource://gre/modules/Accounts.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyGlobalGetters(this, ["XMLHttpRequest"]);
ChromeUtils.defineModuleGetter(this, "Home", "resource://gre/modules/Home.jsm");
ChromeUtils.defineModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm");
ChromeUtils.defineModuleGetter(this, "Task", "resource://gre/modules/Task.jsm");
ChromeUtils.defineModuleGetter(this, "UITelemetry", "resource://gre/modules/UITelemetry.jsm");
XPCOMUtils.defineLazyGetter(this, "gEncoder", function() { return new gChromeWin.TextEncoder(); });
XPCOMUtils.defineLazyGetter(this, "gDecoder", function() { return new gChromeWin.TextDecoder(); });
// URL to fetch snippets, in the urlFormatter service format.
const SNIPPETS_UPDATE_URL_PREF = "browser.snippets.updateUrl";
// URL to send stats data to metrics.
const SNIPPETS_STATS_URL_PREF = "browser.snippets.statsUrl";
// URL to fetch country code, a value that's cached and refreshed once per month.
const SNIPPETS_GEO_URL_PREF = "browser.snippets.geoUrl";
// Timestamp when we last updated the user's country code.
const SNIPPETS_GEO_LAST_UPDATE_PREF = "browser.snippets.geoLastUpdate";
// Pref where we'll cache the user's country.
const SNIPPETS_COUNTRY_CODE_PREF = "browser.snippets.countryCode";
// Pref where we store an array IDs of snippets that should not be shown again
const SNIPPETS_REMOVED_IDS_PREF = "browser.snippets.removedIds";
// How frequently we update the user's country code from the server (30 days).
const SNIPPETS_GEO_UPDATE_INTERVAL_MS = 86400000*30;
// Should be bumped up if the snippets content format changes.
const SNIPPETS_VERSION = 1;
XPCOMUtils.defineLazyGetter(this, "gSnippetsURL", function() {
let updateURL = Services.prefs.getCharPref(SNIPPETS_UPDATE_URL_PREF).replace("%SNIPPETS_VERSION%", SNIPPETS_VERSION);
return Services.urlFormatter.formatURL(updateURL);
});
// Where we cache snippets data
XPCOMUtils.defineLazyGetter(this, "gSnippetsPath", function() {
return OS.Path.join(OS.Constants.Path.profileDir, "snippets.json");
});
XPCOMUtils.defineLazyGetter(this, "gStatsURL", function() {
return Services.prefs.getCharPref(SNIPPETS_STATS_URL_PREF);
});
// Where we store stats about which snippets have been shown
XPCOMUtils.defineLazyGetter(this, "gStatsPath", function() {
return OS.Path.join(OS.Constants.Path.profileDir, "snippets-stats.txt");
});
XPCOMUtils.defineLazyGetter(this, "gGeoURL", function() {
return Services.prefs.getCharPref(SNIPPETS_GEO_URL_PREF);
});
XPCOMUtils.defineLazyGetter(this, "gCountryCode", function() {
try {
return Services.prefs.getCharPref(SNIPPETS_COUNTRY_CODE_PREF);
} catch (e) {
// Return an empty string if the country code pref isn't set yet.
return "";
}
});
XPCOMUtils.defineLazyGetter(this, "gChromeWin", function() {
return Services.wm.getMostRecentWindow("navigator:browser");
});
/**
* Updates snippet data and country code (if necessary).
*/
function update() {
// Check to see if we should update the user's country code from the geo server.
let lastUpdate = 0;
try {
lastUpdate = parseFloat(Services.prefs.getCharPref(SNIPPETS_GEO_LAST_UPDATE_PREF));
} catch (e) {}
if (Date.now() - lastUpdate > SNIPPETS_GEO_UPDATE_INTERVAL_MS) {
// We should update the snippets after updating the country code,
// so that we can filter snippets to add to the banner.
updateCountryCode(updateSnippets);
} else {
updateSnippets();
}
}
/**
* Fetches the user's country code from the geo server and stores the value in a pref.
*
* @param callback function called once country code is updated
*/
function updateCountryCode(callback) {
_httpGetRequest(gGeoURL, function(responseText) {
// Store the country code in a pref.
let data = JSON.parse(responseText);
Services.prefs.setCharPref(SNIPPETS_COUNTRY_CODE_PREF, data.country_code);
// Set last update time.
Services.prefs.setCharPref(SNIPPETS_GEO_LAST_UPDATE_PREF, Date.now());
callback();
});
}
/**
* Loads snippets from snippets server, caches the response, and
* updates the home banner with the new set of snippets.
*/
function updateSnippets() {
_httpGetRequest(gSnippetsURL, function(responseText) {
try {
let messages = JSON.parse(responseText);
updateBanner(messages);
// Only cache the response if it is valid JSON.
cacheSnippets(responseText);
} catch (e) {
Cu.reportError("Error parsing snippets responseText: " + e);
}
});
}
/**
* Caches snippets server response text to `snippets.json` in profile directory.
*
* @param response responseText returned from snippets server
*/
function cacheSnippets(response) {
let data = gEncoder.encode(response);
let promise = OS.File.writeAtomic(gSnippetsPath, data, { tmpPath: gSnippetsPath + ".tmp" });
promise.catch(e => Cu.reportError("Error caching snippets: " + e));
}
/**
* Loads snippets from cached `snippets.json`.
*/
function loadSnippetsFromCache() {
let promise = OS.File.read(gSnippetsPath);
promise.then(array => {
let messages = JSON.parse(gDecoder.decode(array));
updateBanner(messages);
}, e => {
if (e instanceof OS.File.Error && e.becauseNoSuchFile) {
Services.console.logStringMessage("Couldn't show snippets because cache does not exist yet.");
} else {
Cu.reportError("Error loading snippets from cache: " + e);
}
});
}
// Array of the message ids added to the home banner, used to remove
// older set of snippets when new ones are available.
var gMessageIds = [];
/**
* Updates set of snippets in the home banner message rotation.
*
* @param messages JSON array of message data JSON objects.
* Each message object should have the following properties:
* - id (?): Unique identifier for this snippets message
* - text (string): Text to show as banner message
* - url (string): URL to open when banner is clicked
* - icon (data URI): Icon to appear in banner
* - countries (list of strings): Country codes for where this message should be shown (e.g. ["US", "GR"])
*/
function updateBanner(messages) {
// Remove the current messages, if there are any.
gMessageIds.forEach(function(id) {
Home.banner.remove(id);
})
gMessageIds = [];
try {
let removedSnippetIds = JSON.parse(Services.prefs.getCharPref(SNIPPETS_REMOVED_IDS_PREF));
messages = messages.filter(function(message) {
// Only include the snippet if it has not been previously removed.
return !removedSnippetIds.includes(message.id);
});
} catch (e) {
// If the pref doesn't exist, there aren't any snippets to filter out.
}
messages.forEach(function(message) {
// Don't add this message to the banner if it's not supposed to be shown in this country.
if ("countries" in message && !message.countries.includes(gCountryCode)) {
return;
}
let id = Home.banner.add({
text: message.text,
icon: message.icon,
weight: message.weight,
onclick: function() {
gChromeWin.BrowserApp.loadURI(message.url);
removeSnippet(id, message.id);
UITelemetry.addEvent("action.1", "banner", null, message.id);
},
ondismiss: function() {
removeSnippet(id, message.id);
UITelemetry.addEvent("cancel.1", "banner", null, message.id);
},
onshown: function() {
// 10% of the time, record the snippet id and a timestamp
if (Math.random() < .1) {
writeStat(message.id, new Date().toISOString());
}
}
});
// Keep track of the message we added so that we can remove it later.
gMessageIds.push(id);
});
}
/**
* Removes a snippet message from the home banner rotation, and stores its
* snippet id in a pref so we'll never show it again.
*
* @param messageId unique id for home banner message, returned from Home.banner API
* @param snippetId unique id for snippet, sent from snippets server
*/
function removeSnippet(messageId, snippetId) {
// Remove the message from the home banner rotation.
Home.banner.remove(messageId);
// Remove the message from the stored message ids.
gMessageIds.splice(gMessageIds.indexOf(messageId), 1);
let removedSnippetIds;
try {
removedSnippetIds = JSON.parse(Services.prefs.getCharPref(SNIPPETS_REMOVED_IDS_PREF));
} catch (e) {
removedSnippetIds = [];
}
removedSnippetIds.push(snippetId);
Services.prefs.setCharPref(SNIPPETS_REMOVED_IDS_PREF, JSON.stringify(removedSnippetIds));
}
/**
* Appends snippet id and timestamp to the end of `snippets-stats.txt`.
*
* @param snippetId unique id for snippet, sent from snippets server
* @param timestamp in ISO8601
*/
function writeStat(snippetId, timestamp) {
let data = gEncoder.encode(snippetId + "," + timestamp + ";");
Task.spawn(function*() {
try {
let file = yield OS.File.open(gStatsPath, { append: true, write: true });
try {
yield file.write(data);
} finally {
yield file.close();
}
} catch (ex) {
if (!(ex instanceof OS.File.Error && ex.becauseNoSuchFile)) {
throw ex;
}
// If the file doesn't exist yet, create it.
yield OS.File.writeAtomic(gStatsPath, data, { tmpPath: gStatsPath + ".tmp" });
}
}).catch(e => Cu.reportError("Error writing snippets stats: " + e));
}
/**
* Reads snippets stats data from `snippets-stats.txt` and sends the data to metrics.
*/
function sendStats() {
let promise = OS.File.read(gStatsPath);
promise.then(array => sendStatsRequest(gDecoder.decode(array)), e => {
if (e instanceof OS.File.Error && e.becauseNoSuchFile) {
// If the file doesn't exist, there aren't any stats to send.
} else {
Cu.reportError("Error eading snippets stats: " + e);
}
});
}
/**
* Sends stats to metrics about which snippets have been shown.
* Appends snippet ids and timestamps as parameters to a GET request.
* e.g. https://snippets-stats.mozilla.org/mobile?s1=3825&t1=2013-11-17T18:27Z&s2=6326&t2=2013-11-18T18:27Z
*
* @param data contents of stats data file
*/
function sendStatsRequest(data) {
let params = [];
let stats = data.split(";");
// The last item in the array will be an empty string, so stop before then.
for (let i = 0; i < stats.length - 1; i++) {
let stat = stats[i].split(",");
params.push("s" + i + "=" + encodeURIComponent(stat[0]));
params.push("t" + i + "=" + encodeURIComponent(stat[1]));
}
let url = gStatsURL + "?" + params.join("&");
// Remove the file after succesfully sending the data.
_httpGetRequest(url, removeStats);
}
/**
* Removes text file where we store snippets stats.
*/
function removeStats() {
let promise = OS.File.remove(gStatsPath);
promise.catch(e => Cu.reportError("Error removing snippets stats: " + e));
}
/**
* Helper function to make HTTP GET requests.
*
* @param url where we send the request
* @param callback function that is called with the xhr responseText
*/
function _httpGetRequest(url, callback) {
let xhr = new XMLHttpRequest();
try {
xhr.open("GET", url, true);
} catch (e) {
Cu.reportError("Error opening request to " + url + ": " + e);
return;
}
xhr.onerror = function onerror(e) {
Cu.reportError("Error making request to " + url + ": " + e.error);
}
xhr.onload = function onload(event) {
if (xhr.status !== 200) {
Cu.reportError("Request to " + url + " returned status " + xhr.status);
return;
}
if (callback) {
callback(xhr.responseText);
}
}
xhr.send(null);
}
function loadSyncPromoBanner() {
Accounts.anySyncAccountsExist().then(
(exist) => {
// Don't show the banner if sync accounts exist.
if (exist) {
return;
}
let stringBundle = Services.strings.createBundle("chrome://browser/locale/sync.properties");
let text = stringBundle.GetStringFromName("promoBanner.message.text");
let link = stringBundle.GetStringFromName("promoBanner.message.link");
let id = Home.banner.add({
text: text + "<a href=\"#\">" + link + "</a>",
icon: "drawable://sync_promo",
onclick: function() {
// Remove the message, so that it won't show again for the rest of the app lifetime.
Home.banner.remove(id);
Accounts.launchSetup();
UITelemetry.addEvent("action.1", "banner", null, "syncpromo");
},
ondismiss: function() {
// Remove the sync promo message from the banner and never try to show it again.
Home.banner.remove(id);
Services.prefs.setBoolPref("browser.snippets.syncPromo.enabled", false);
UITelemetry.addEvent("cancel.1", "banner", null, "syncpromo");
}
});
},
(err) => {
Cu.reportError("Error checking whether sync account exists: " + err);
}
);
}
function loadHomePanelsBanner() {
let stringBundle = Services.strings.createBundle("chrome://browser/locale/aboutHome.properties");
let text = stringBundle.GetStringFromName("banner.firstrunHomepage.text");
let id = Home.banner.add({
text: text,
icon: "drawable://homepage_banner_firstrun",
onclick: function() {
// Remove the message, so that it won't show again for the rest of the app lifetime.
Home.banner.remove(id);
// User has interacted with this snippet so don't show it again.
Services.prefs.setBoolPref("browser.snippets.firstrunHomepage.enabled", false);
UITelemetry.addEvent("action.1", "banner", null, "firstrun-homepage");
},
ondismiss: function() {
Home.banner.remove(id);
Services.prefs.setBoolPref("browser.snippets.firstrunHomepage.enabled", false);
UITelemetry.addEvent("cancel.1", "banner", null, "firstrun-homepage");
}
});
}
function Snippets() {}
Snippets.prototype = {
QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver, Ci.nsITimerCallback]),
classID: Components.ID("{a78d7e59-b558-4321-a3d6-dffe2f1e76dd}"),
observe: function(subject, topic, data) {
switch(topic) {
case "browser-delayed-startup-finished":
// Add snippets to be cycled through.
if (Services.prefs.getBoolPref("browser.snippets.firstrunHomepage.enabled")) {
loadHomePanelsBanner();
}
if (Services.prefs.getBoolPref("browser.snippets.syncPromo.enabled")) {
loadSyncPromoBanner();
}
if (Services.prefs.getBoolPref("browser.snippets.enabled")) {
loadSnippetsFromCache();
}
break;
}
},
// By default, this timer fires once every 24 hours. See the "browser.snippets.updateInterval" pref.
notify: function(timer) {
if (!Services.prefs.getBoolPref("browser.snippets.enabled")) {
return;
}
update();
sendStats();
}
};
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([Snippets]);

View file

@ -36,7 +36,6 @@ EXTRA_COMPONENTS += [
'PresentationRequestUIGlue.js',
'PromptService.js',
'SessionStore.js',
'Snippets.js',
'TabSource.js',
'XPIDialogService.js',
]

View file

@ -373,7 +373,6 @@
@BINPATH@/components/PresentationRequestUIGlue.js
@BINPATH@/components/PromptService.js
@BINPATH@/components/SessionStore.js
@BINPATH@/components/Snippets.js
@BINPATH@/components/XPIDialogService.js
#endif

View file

@ -441,6 +441,13 @@ VARCACHE_PREF(
bool, false
)
// Does arbitrary ::-webkit-* pseudo-element parsed?
VARCACHE_PREF(
"layout.css.unknown-webkit-pseudo-element",
layout_css_unknown_webkit_pseudo_element,
bool, false
)
//---------------------------------------------------------------------------
// JavaScript prefs
//---------------------------------------------------------------------------

View file

@ -49,12 +49,12 @@ TLSServerSocket::SetSocketDefaults()
SSL_OptionSet(mFD, SSL_SECURITY, true);
SSL_OptionSet(mFD, SSL_HANDSHAKE_AS_CLIENT, false);
SSL_OptionSet(mFD, SSL_HANDSHAKE_AS_SERVER, true);
SSL_OptionSet(mFD, SSL_NO_CACHE, true);
// We don't currently notify the server API consumer of renegotiation events
// (to revalidate peer certs, etc.), so disable it for now.
SSL_OptionSet(mFD, SSL_ENABLE_RENEGOTIATION, SSL_RENEGOTIATE_NEVER);
SetSessionCache(true);
SetSessionTickets(true);
SetRequestClientCertificate(REQUEST_NEVER);
@ -168,18 +168,6 @@ TLSServerSocket::SetServerCert(nsIX509Cert* aCert)
return NS_OK;
}
NS_IMETHODIMP
TLSServerSocket::SetSessionCache(bool aEnabled)
{
// If AsyncListen was already called (and set mListener), it's too late to set
// this.
if (NS_WARN_IF(mListener)) {
return NS_ERROR_IN_PROGRESS;
}
SSL_OptionSet(mFD, SSL_NO_CACHE, !aEnabled);
return NS_OK;
}
NS_IMETHODIMP
TLSServerSocket::SetSessionTickets(bool aEnabled)
{

View file

@ -19,15 +19,6 @@ interface nsITLSServerSocket : nsIServerSocket
*/
attribute nsIX509Cert serverCert;
/**
* setSessionCache
*
* Whether the server should use a session cache. Defaults to true. This
* should be set before calling |asyncListen| if you wish to change the
* default.
*/
void setSessionCache(in boolean aSessionCache);
/**
* setSessionTickets
*

View file

@ -152,7 +152,22 @@ IsLowPriority(uint16_t flags)
// this macro filters out any flags that are not used when constructing the
// host key. the significant flags are those that would affect the resulting
// host record (i.e., the flags that are passed down to PR_GetAddrInfoByName).
#define RES_KEY_FLAGS(_f) ((_f) & nsHostResolver::RES_CANON_NAME)
#define RES_KEY_FLAGS(_f) ((_f) & (nsHostResolver::RES_CANON_NAME | \
nsHostResolver::RES_DISABLE_TRR))
nsHostKey::nsHostKey(const nsACString& aHost, uint16_t aFlags,
uint16_t aAf, bool aPb, const nsACString& aOriginsuffix)
: host(aHost)
, flags(aFlags)
, af(aAf)
, pb(aPb)
, originSuffix(aOriginsuffix)
{
if (TRR_DISABLED(gTRRService->Mode())) {
// When not using TRR, lookup all answers as TRR-disabled
flags |= nsHostResolver::RES_DISABLE_TRR;
}
}
bool
nsHostKey::operator==(const nsHostKey& other) const

View file

@ -57,16 +57,8 @@ struct nsHostKey
uint16_t af;
bool pb;
const nsCString originSuffix;
nsHostKey(const nsACString& host, uint16_t flags,
uint16_t af, bool pb, const nsACString& originSuffix)
: host(host)
, flags(flags)
, af(af)
, pb(pb)
, originSuffix(originSuffix) {
}
explicit nsHostKey(const nsACString& host, uint16_t flags,
uint16_t af, bool pb, const nsACString& originSuffix);
bool operator==(const nsHostKey& other) const;
size_t SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
PLDHashNumber Hash() const;

View file

@ -5121,7 +5121,7 @@ nsHttpConnectionMgr::nsHalfOpenSocket::OnTransportStatus(nsITransport *trans,
if (NS_SUCCEEDED(rv) && !addressSet.IsEmpty()) {
for (uint32_t i = 0; i < addressSet.Length(); ++i) {
nsCString *newKey = mEnt->mCoalescingKeys.AppendElement(nsCString());
newKey->SetCapacity(kIPv6CStrBufSize + 26);
newKey->SetLength(kIPv6CStrBufSize + 26);
NetAddrToString(&addressSet[i], newKey->BeginWriting(), kIPv6CStrBufSize);
newKey->SetLength(strlen(newKey->BeginReading()));
if (mEnt->mConnInfo->GetAnonymous()) {

View file

@ -139,7 +139,6 @@ function startServer(cert, minServerVersion, maxServerVersion) {
tlsServer.init(-1, true, -1);
tlsServer.serverCert = cert;
tlsServer.setVersionRange(minServerVersion, maxServerVersion);
tlsServer.setSessionCache(false);
tlsServer.setSessionTickets(false);
tlsServer.asyncListen(new ServerSocketListener());
return tlsServer;

View file

@ -139,7 +139,6 @@ function startServer(cert, minServerVersion, maxServerVersion) {
tlsServer.init(-1, true, -1);
tlsServer.serverCert = cert;
tlsServer.setVersionRange(minServerVersion, maxServerVersion);
tlsServer.setSessionCache(false);
tlsServer.setSessionTickets(false);
tlsServer.asyncListen(new ServerSocketListener());
return tlsServer;

View file

@ -118,7 +118,6 @@ function startServer(cert, minServerVersion, maxServerVersion, expectedVersion)
tlsServer.init(-1, true, -1);
tlsServer.serverCert = cert;
tlsServer.setVersionRange(minServerVersion, maxServerVersion);
tlsServer.setSessionCache(false);
tlsServer.setSessionTickets(false);
let listener = {

View file

@ -93,7 +93,6 @@ function startServer(cert, expectingPeerCert, clientCertificateConfig,
}
};
tlsServer.setSessionCache(false);
tlsServer.setSessionTickets(false);
tlsServer.setRequestClientCertificate(clientCertificateConfig);

View file

@ -66,7 +66,6 @@ function startServer(cert) {
onStopListening: function() {}
};
tlsServer.setSessionCache(true);
tlsServer.setSessionTickets(false);
tlsServer.asyncListen(listener);

View file

@ -307,10 +307,15 @@ class TupBackend(CommonBackend):
args += ['-j%d' % jobs]
else:
args += ['-j%d' % multiprocessing.cpu_count()]
tiers = output.monitor.tiers
tiers.set_tiers(('tup',))
tiers.begin_tier('tup')
status = config.run_process(args=args,
line_handler=output.on_line,
ensure_exit_code=False,
append_env=self._get_mozconfig_env(config))
tiers.finish_tier('tup')
if not status and self.environment.substs.get('MOZ_AUTOMATION'):
src = mozpath.join(self.environment.topsrcdir, '.tup')
dst = os.environ['UPLOAD_PATH']

View file

@ -1264,7 +1264,7 @@ class PackageFrontend(MachCommandBase):
help='Use the given url as tooltool server')
@CommandArgument('--no-unpack', action='store_true',
help='Do not unpack any downloaded file')
@CommandArgument('--retry', type=int, default=0,
@CommandArgument('--retry', type=int, default=4,
help='Number of times to retry failed downloads')
@CommandArgument('--artifact-manifest', metavar='FILE',
help='Store a manifest about the downloaded taskcluster artifacts')

View file

@ -848,7 +848,7 @@ class TestEmitterBasic(unittest.TestCase):
objs = [o for o in self.read_topsrcdir(reader)
if isinstance(o, TestManifest)]
self.assertEqual(len(objs), 9)
self.assertEqual(len(objs), 8)
metadata = {
'a11y.ini': {
@ -868,13 +868,6 @@ class TestEmitterBasic(unittest.TestCase):
'support2': False,
},
},
'metro.ini': {
'flavor': 'metro-chrome',
'installs': {
'metro.ini': False,
'test_metro.js': True,
},
},
'mochitest.ini': {
'flavor': 'mochitest',
'installs': {

View file

@ -56,7 +56,6 @@ TEST_MANIFESTS = dict(
MARIONETTE_UNIT=('marionette', 'marionette', '.', False),
MARIONETTE_WEBAPI=('marionette', 'marionette', '.', False),
METRO_CHROME=('metro-chrome', 'testing/mochitest', 'metro', True),
MOCHITEST=('mochitest', 'testing/mochitest', 'tests', True),
MOCHITEST_CHROME=('chrome', 'testing/mochitest', 'chrome', True),
WEBRTC_SIGNALLING_TEST=('steeplechase', 'steeplechase', '.', True),

View file

@ -203,7 +203,7 @@ nsNSSComponent::nsNSSComponent()
, mLoadableRootsLoadedResult(NS_ERROR_FAILURE)
, mMutex("nsNSSComponent.mMutex")
, mMitmDetecionEnabled(false)
, mNonIdempotentCleanupMustHappen(false)
, mLoadLoadableRootsTaskDispatched(false)
{
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsNSSComponent::ctor\n"));
MOZ_RELEASE_ASSERT(NS_IsMainThread());
@ -1945,17 +1945,6 @@ nsNSSComponent::InitializeNSS()
MOZ_ALWAYS_SUCCEEDS(handle->TrustLoaded3rdPartyRoots());
}));
// TLSServerSocket may be run with the session cache enabled. It is
// necessary to call this once before that can happen. This specifies a
// maximum of 1000 cache entries (the default number of cache entries is
// 10000, which seems a little excessive as there probably won't be that
// many clients connecting to any TLSServerSockets the browser runs.) Note
// that this must occur before any calls to SSL_ClearSessionCache (otherwise
// memory will leak).
if (SSL_ConfigServerSessionIDCache(1000, 0, 0, nullptr) != SECSuccess) {
return NS_ERROR_FAILURE;
}
// Set dynamic options from prefs. This has to run after
// SSL_ConfigServerSessionIDCache.
setValidationOptions(true, lock);
@ -1967,7 +1956,7 @@ nsNSSComponent::InitializeNSS()
return rv;
}
mNonIdempotentCleanupMustHappen = true;
mLoadLoadableRootsTaskDispatched = true;
return NS_OK;
}
}
@ -1986,16 +1975,9 @@ nsNSSComponent::ShutdownNSS()
// it to fail to find the roots it is expecting. However, if initialization
// failed, we won't have dispatched the load loadable roots background task.
// In that case, we don't want to block on an event that will never happen.
if (mNonIdempotentCleanupMustHappen) {
if (mLoadLoadableRootsTaskDispatched) {
Unused << BlockUntilLoadableRootsLoaded();
// We can only run SSL_ShutdownServerSessionIDCache once (the rest of
// these operations are idempotent).
SSL_ClearSessionCache();
// TLSServerSocket may be run with the session cache enabled. This ensures
// those resources are cleaned up.
Unused << SSL_ShutdownServerSessionIDCache();
mNonIdempotentCleanupMustHappen = false;
mLoadLoadableRootsTaskDispatched = false;
}
::mozilla::psm::UnloadLoadableRoots();

View file

@ -118,21 +118,13 @@ private:
// The following members are accessed only on the main thread:
static int mInstanceCount;
// If initialization (i.e. InitializeNSS) succeeds, we have called
// SSL_ConfigServerSessionIDCache. To clean this up, we must call
// SSL_ClearSessionCache and SSL_ShutdownServerSessionIDCache exactly once
// each (and if we haven't called SSL_ConfigServerSessionIDCache - for example
// if initialization failed - then we mustn't call the cleanup functions
// ever). There are multiple events that can cause us to enter our cleanup
// function (ShutdownNSS) and so we keep track of if we need to call these
// non-idempotent functions in the following boolean.
// Similarly, if InitializeNSS succeeds, then we have dispatched an event to
// load the loadable roots module on a background thread. We must wait for it
// to complete before attempting to unload the module again in ShutdownNSS. If
// we never dispatched the event, then we can't wait for it to complete
// (because it will never complete) so we again use this boolean to keep track
// of if we should wait.
bool mNonIdempotentCleanupMustHappen;
// If InitializeNSS succeeds, then we have dispatched an event to load the
// loadable roots module on a background thread. We must wait for it to
// complete before attempting to unload the module again in ShutdownNSS. If we
// never dispatched the event, then we can't wait for it to complete (because
// it will never complete) so we use this boolean to keep track of if we
// should wait.
bool mLoadLoadableRootsTaskDispatched;
};
inline nsresult

View file

@ -138,7 +138,6 @@ function getStartedServer(cert) {
.createInstance(Ci.nsITLSServerSocket);
tlsServer.init(-1, true, -1);
tlsServer.serverCert = cert;
tlsServer.setSessionCache(false);
tlsServer.setSessionTickets(false);
tlsServer.asyncListen(new ServerSocketListener());
return tlsServer;

View file

@ -14,6 +14,7 @@ use properties::{ComputedValues, PropertyFlags};
use properties::longhands::display::computed_value::T as Display;
use selector_parser::{NonTSPseudoClass, PseudoElementCascadeType, SelectorImpl};
use std::fmt;
use str::{starts_with_ignore_ascii_case, string_as_ascii_lowercase};
use string_cache::Atom;
use thin_slice::ThinBoxedSlice;
use values::serialize_atom_identifier;

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