зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-central to autoland. a=merge CLOSED TREE
This commit is contained in:
Коммит
37ae09bab7
2
CLOBBER
2
CLOBBER
|
@ -22,4 +22,4 @@
|
|||
# changes to stick? As of bug 928195, this shouldn't be necessary! Please
|
||||
# don't change CLOBBER for WebIDL changes any more.
|
||||
|
||||
Merge day clobber
|
||||
Historically updating ICU has required a CLOBBER. Bug 1445524 is a fairly notable ICU-related change, so play it safe and force a full rebuild, even if no problem along these lines has actually been observed.
|
||||
|
|
|
@ -462,28 +462,36 @@ var BrowserPageActions = {
|
|||
* @param propertyName (string, optional)
|
||||
* The name of the property to update. If not given, then DOM nodes
|
||||
* will be updated to reflect the current values of all properties.
|
||||
* @param value (optional)
|
||||
* If a property name is passed, this argument may contain its
|
||||
* current value, in order to prevent a further look-up.
|
||||
*/
|
||||
updateAction(action, propertyName = null) {
|
||||
let propertyNames = propertyName ? [propertyName] : [
|
||||
"iconURL",
|
||||
"title",
|
||||
"tooltip",
|
||||
];
|
||||
for (let name of propertyNames) {
|
||||
let upper = name[0].toUpperCase() + name.substr(1);
|
||||
this[`_updateAction${upper}`](action);
|
||||
updateAction(action, propertyName = null, value) {
|
||||
if (propertyName) {
|
||||
this[this._updateMethods[propertyName]](action, value);
|
||||
} else {
|
||||
for (let name of ["iconURL", "title", "tooltip"]) {
|
||||
this[this._updateMethods[name]](action, value);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_updateActionDisabled(action) {
|
||||
this._updateActionDisabledInPanel(action);
|
||||
_updateMethods: {
|
||||
disabled: "_updateActionDisabled",
|
||||
iconURL: "_updateActionIconURL",
|
||||
title: "_updateActionTitle",
|
||||
tooltip: "_updateActionTooltip",
|
||||
},
|
||||
|
||||
_updateActionDisabled(action, disabled) {
|
||||
this._updateActionDisabledInPanel(action, disabled);
|
||||
this.placeActionInUrlbar(action);
|
||||
},
|
||||
|
||||
_updateActionDisabledInPanel(action) {
|
||||
_updateActionDisabledInPanel(action, disabled = action.getDisabled(window)) {
|
||||
let panelButton = this.panelButtonNodeForActionID(action.id);
|
||||
if (panelButton) {
|
||||
if (action.getDisabled(window)) {
|
||||
if (disabled) {
|
||||
panelButton.setAttribute("disabled", "true");
|
||||
} else {
|
||||
panelButton.removeAttribute("disabled");
|
||||
|
@ -491,26 +499,21 @@ var BrowserPageActions = {
|
|||
}
|
||||
},
|
||||
|
||||
_updateActionIconURL(action) {
|
||||
let nodes = [
|
||||
this.panelButtonNodeForActionID(action.id),
|
||||
this.urlbarButtonNodeForActionID(action.id),
|
||||
].filter(n => !!n);
|
||||
for (let node of nodes) {
|
||||
for (let size of [16, 32]) {
|
||||
let url = action.iconURLForSize(size, window);
|
||||
let prop = `--pageAction-image-${size}px`;
|
||||
if (url) {
|
||||
node.style.setProperty(prop, `url("${url}")`);
|
||||
} else {
|
||||
node.style.removeProperty(prop);
|
||||
}
|
||||
_updateActionIconURL(action, properties = action.getIconProperties(window)) {
|
||||
let panelButton = this.panelButtonNodeForActionID(action.id);
|
||||
let urlbarButton = this.urlbarButtonNodeForActionID(action.id);
|
||||
|
||||
for (let [prop, value] of Object.entries(properties)) {
|
||||
if (panelButton) {
|
||||
panelButton.style.setProperty(prop, value);
|
||||
}
|
||||
if (urlbarButton) {
|
||||
urlbarButton.style.setProperty(prop, value);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_updateActionTitle(action) {
|
||||
let title = action.getTitle(window);
|
||||
_updateActionTitle(action, title = action.getTitle(window)) {
|
||||
if (!title) {
|
||||
// `title` is a required action property, but the bookmark action's is an
|
||||
// empty string since its actual title is set via
|
||||
|
@ -518,25 +521,28 @@ var BrowserPageActions = {
|
|||
// return is to ignore that empty title.
|
||||
return;
|
||||
}
|
||||
let attrNamesByNodeFnName = {
|
||||
panelButtonNodeForActionID: "label",
|
||||
urlbarButtonNodeForActionID: "aria-label",
|
||||
};
|
||||
for (let [fnName, attrName] of Object.entries(attrNamesByNodeFnName)) {
|
||||
let node = this[fnName](action.id);
|
||||
if (node) {
|
||||
node.setAttribute(attrName, title);
|
||||
}
|
||||
let panelButton = this.panelButtonNodeForActionID(action.id);
|
||||
if (panelButton) {
|
||||
panelButton.setAttribute("label", title);
|
||||
}
|
||||
|
||||
let urlbarButton = this.urlbarButtonNodeForActionID(action.id);
|
||||
if (urlbarButton) {
|
||||
urlbarButton.setAttribute("aria-label", title);
|
||||
|
||||
// tooltiptext falls back to the title, so update it, too.
|
||||
this._updateActionTooltip(action, undefined, title, urlbarButton);
|
||||
}
|
||||
// tooltiptext falls back to the title, so update it, too.
|
||||
this._updateActionTooltip(action);
|
||||
},
|
||||
|
||||
_updateActionTooltip(action) {
|
||||
let node = this.urlbarButtonNodeForActionID(action.id);
|
||||
_updateActionTooltip(action, tooltip = action.getTooltip(window),
|
||||
title,
|
||||
node = this.urlbarButtonNodeForActionID(action.id)) {
|
||||
if (node) {
|
||||
let tooltip = action.getTooltip(window) || action.getTitle(window);
|
||||
node.setAttribute("tooltiptext", tooltip);
|
||||
if (!tooltip && title === undefined) {
|
||||
title = action.getTitle(window);
|
||||
}
|
||||
node.setAttribute("tooltiptext", tooltip || title);
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -82,14 +82,16 @@ this.chrome_settings_overrides = class extends ExtensionAPI {
|
|||
}
|
||||
|
||||
static removeSearchSettings(id) {
|
||||
this.processDefaultSearchSetting("removeSetting", id);
|
||||
this.removeEngine(id);
|
||||
return Promise.all([
|
||||
this.processDefaultSearchSetting("removeSetting", id),
|
||||
this.removeEngine(id),
|
||||
]);
|
||||
}
|
||||
|
||||
static onUninstall(id) {
|
||||
// Note: We do not have to deal with homepage here as it is managed by
|
||||
// the ExtensionPreferencesManager.
|
||||
this.removeSearchSettings(id);
|
||||
return this.removeSearchSettings(id);
|
||||
}
|
||||
|
||||
static onUpdate(id, manifest) {
|
||||
|
|
|
@ -85,14 +85,16 @@ this.pageAction = class extends ExtensionAPI {
|
|||
|
||||
this.defaults.icon = await StartupCache.get(
|
||||
extension, ["pageAction", "default_icon"],
|
||||
() => IconDetails.normalize({path: options.default_icon}, extension));
|
||||
() => this.normalize({path: options.default_icon || ""}));
|
||||
|
||||
this.lastValues = new DefaultWeakMap(() => ({}));
|
||||
|
||||
if (!this.browserPageAction) {
|
||||
this.browserPageAction = PageActions.addAction(new PageActions.Action({
|
||||
id: widgetId,
|
||||
extensionID: extension.id,
|
||||
title: this.defaults.title,
|
||||
iconURL: this.getIconData(this.defaults.icon),
|
||||
iconURL: this.defaults.icon,
|
||||
pinnedToUrlbar: true,
|
||||
disabled: !this.defaults.show,
|
||||
onCommand: (event, buttonNode) => {
|
||||
|
@ -160,6 +162,14 @@ this.pageAction = class extends ExtensionAPI {
|
|||
}
|
||||
}
|
||||
|
||||
normalize(details, context = null) {
|
||||
let icon = IconDetails.normalize(details, this.extension, context);
|
||||
if (!Object.keys(icon).length) {
|
||||
icon = null;
|
||||
}
|
||||
return icon;
|
||||
}
|
||||
|
||||
// Updates the page action button in the given window to reflect the
|
||||
// properties of the currently selected tab:
|
||||
//
|
||||
|
@ -169,21 +179,27 @@ this.pageAction = class extends ExtensionAPI {
|
|||
updateButton(window) {
|
||||
let tab = window.gBrowser.selectedTab;
|
||||
let tabData = this.tabContext.get(tab);
|
||||
let title = tabData.title || this.extension.name;
|
||||
this.browserPageAction.setTitle(title, window);
|
||||
this.browserPageAction.setTooltip(title, window);
|
||||
let last = this.lastValues.get(window);
|
||||
|
||||
// At least one of "show" or "patternMatching" must be defined here.
|
||||
let {show = tabData.patternMatching} = tabData;
|
||||
this.browserPageAction.setDisabled(!show, window);
|
||||
window.requestAnimationFrame(() => {
|
||||
let title = tabData.title || this.extension.name;
|
||||
if (last.title !== title) {
|
||||
this.browserPageAction.setTitle(title, window);
|
||||
last.title = title;
|
||||
}
|
||||
|
||||
let iconURL;
|
||||
if (typeof(tabData.icon) == "string") {
|
||||
iconURL = IconDetails.escapeUrl(tabData.icon);
|
||||
} else {
|
||||
iconURL = this.getIconData(tabData.icon);
|
||||
}
|
||||
this.browserPageAction.setIconURL(iconURL, window);
|
||||
let show = tabData.show != null ? tabData.show : tabData.patternMatching;
|
||||
if (last.show !== show) {
|
||||
this.browserPageAction.setDisabled(!show, window);
|
||||
last.show = show;
|
||||
}
|
||||
|
||||
let icon = tabData.icon;
|
||||
if (last.icon !== icon) {
|
||||
this.browserPageAction.setIconURL(icon, window);
|
||||
last.icon = icon;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Checks whether the tab action is shown when the specified tab becomes active.
|
||||
|
@ -207,18 +223,6 @@ this.pageAction = class extends ExtensionAPI {
|
|||
return tabData.patternMatching;
|
||||
}
|
||||
|
||||
getIconData(icons) {
|
||||
let getIcon = size => {
|
||||
let {icon} = IconDetails.getPreferredIcon(icons, this.extension, size);
|
||||
// TODO: implement theme based icon for pageAction (Bug 1398156)
|
||||
return IconDetails.escapeUrl(icon);
|
||||
};
|
||||
return {
|
||||
"16": getIcon(16),
|
||||
"32": getIcon(32),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers this page action for the given window, with the same effects as
|
||||
* if it were clicked by a user.
|
||||
|
@ -367,10 +371,8 @@ this.pageAction = class extends ExtensionAPI {
|
|||
setIcon(details) {
|
||||
let tab = tabTracker.getTab(details.tabId);
|
||||
|
||||
let icon = IconDetails.normalize(details, extension, context);
|
||||
if (!Object.keys(icon).length) {
|
||||
icon = null;
|
||||
}
|
||||
let icon = pageAction.normalize(details, context);
|
||||
|
||||
pageAction.setProperty(tab, "icon", icon);
|
||||
},
|
||||
|
||||
|
|
|
@ -6,4 +6,5 @@ tags = webextensions in-process-webextensions
|
|||
[browser_ext_windows_allowScriptsToClose.js]
|
||||
|
||||
[include:browser-common.ini]
|
||||
skip-if = os == 'win' # Windows WebExtensions always run OOP
|
||||
[parent:browser-common.ini]
|
||||
|
|
|
@ -264,6 +264,7 @@ add_task(async function testDetailsObjects() {
|
|||
let browserActionWidget = getBrowserActionWidget(extension);
|
||||
|
||||
let tests = await extension.awaitMessage("ready");
|
||||
await promiseAnimationFrame();
|
||||
|
||||
// The initial icon should be the default icon since no icon is in the manifest.
|
||||
const DEFAULT_ICON = "chrome://browser/content/extension.svg";
|
||||
|
|
|
@ -65,6 +65,7 @@ function getId(tab) {
|
|||
}
|
||||
|
||||
async function check(extension, tab, expected, msg) {
|
||||
await promiseAnimationFrame();
|
||||
let widgetId = makeWidgetId(extension.id);
|
||||
let pageActionId = BrowserPageActions.urlbarButtonNodeIDForActionID(widgetId);
|
||||
is(gBrowser.selectedTab, tab, `tab ${tab.linkedBrowser.currentURI.spec} is selected`);
|
||||
|
|
|
@ -28,7 +28,7 @@ const {
|
|||
AddonTestUtils.init(this);
|
||||
AddonTestUtils.overrideCertDB();
|
||||
|
||||
createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "42");
|
||||
createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "42", "42");
|
||||
|
||||
function awaitEvent(eventName) {
|
||||
return new Promise(resolve => {
|
||||
|
|
|
@ -32,7 +32,7 @@ AddonTestUtils.init(this);
|
|||
// Allow for unsigned addons.
|
||||
AddonTestUtils.overrideCertDB();
|
||||
|
||||
createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "42");
|
||||
createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "42", "42");
|
||||
|
||||
add_task(async function test_url_overrides_newtab_update() {
|
||||
const EXTENSION_ID = "test_url_overrides_update@tests.mozilla.org";
|
||||
|
|
|
@ -9,6 +9,12 @@ Cu.importGlobalProperties(["fetch"]);
|
|||
ChromeUtils.defineModuleGetter(this, "Services",
|
||||
"resource://gre/modules/Services.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "resProto",
|
||||
"@mozilla.org/network/protocol;1?name=resource",
|
||||
"nsISubstitutingProtocolHandler");
|
||||
|
||||
const RESOURCE_HOST = "activity-stream";
|
||||
|
||||
const BROWSER_READY_NOTIFICATION = "sessionstore-windows-restored";
|
||||
const RESOURCE_BASE = "resource://activity-stream";
|
||||
|
||||
|
@ -148,6 +154,10 @@ function observe(subject, topic, data) {
|
|||
this.install = function install(data, reason) {};
|
||||
|
||||
this.startup = function startup(data, reason) {
|
||||
resProto.setSubstitutionWithFlags(RESOURCE_HOST,
|
||||
Services.io.newURI("chrome/content/", null, data.resourceURI),
|
||||
resProto.ALLOW_CONTENT_ACCESS);
|
||||
|
||||
// Cache startup data which contains stuff like the version number, etc.
|
||||
// so we can use it when we init
|
||||
startupData = data;
|
||||
|
@ -163,6 +173,8 @@ this.startup = function startup(data, reason) {
|
|||
};
|
||||
|
||||
this.shutdown = function shutdown(data, reason) {
|
||||
resProto.setSubstitution(RESOURCE_HOST, null);
|
||||
|
||||
// Uninitialize Activity Stream
|
||||
startupData = null;
|
||||
startupReason = null;
|
||||
|
|
|
@ -20,6 +20,12 @@ ChromeUtils.defineModuleGetter(this, "formAutofillParent",
|
|||
ChromeUtils.defineModuleGetter(this, "FormAutofillUtils",
|
||||
"resource://formautofill/FormAutofillUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "resProto",
|
||||
"@mozilla.org/network/protocol;1?name=resource",
|
||||
"nsISubstitutingProtocolHandler");
|
||||
|
||||
const RESOURCE_HOST = "formautofill";
|
||||
|
||||
function insertStyleSheet(domWindow, url) {
|
||||
let doc = domWindow.document;
|
||||
let styleSheetAttr = `href="${url}" type="text/css"`;
|
||||
|
@ -80,6 +86,9 @@ function startup(data) {
|
|||
return;
|
||||
}
|
||||
|
||||
resProto.setSubstitution(RESOURCE_HOST,
|
||||
Services.io.newURI("chrome/res/", null, data.resourceURI));
|
||||
|
||||
if (data.hasOwnProperty("instanceID") && data.instanceID) {
|
||||
if (AddonManagerPrivate.isDBLoaded()) {
|
||||
addUpgradeListener(data.instanceID);
|
||||
|
@ -121,6 +130,8 @@ function startup(data) {
|
|||
}
|
||||
|
||||
function shutdown() {
|
||||
resProto.setSubstitution(RESOURCE_HOST, null);
|
||||
|
||||
Services.mm.removeMessageListener("FormAutoComplete:MaybeOpenPopup", onMaybeOpenPopup);
|
||||
|
||||
let enumerator = Services.wm.getEnumerator("navigator:browser");
|
||||
|
|
|
@ -22,6 +22,10 @@ ChromeUtils.defineModuleGetter(this, "DownloadPaths",
|
|||
ChromeUtils.defineModuleGetter(this, "FileUtils",
|
||||
"resource://gre/modules/FileUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "resProto",
|
||||
"@mozilla.org/network/protocol;1?name=resource",
|
||||
"nsISubstitutingProtocolHandler");
|
||||
|
||||
do_get_profile();
|
||||
|
||||
// ================================================
|
||||
|
@ -50,6 +54,9 @@ if (!extensionDir.exists()) {
|
|||
}
|
||||
Components.manager.addBootstrappedManifestLocation(extensionDir);
|
||||
|
||||
let resURI = Services.io.newURI("chrome/res/", null, Services.io.newURI(bootstrapURI));
|
||||
resProto.setSubstitution("formautofill", resURI);
|
||||
|
||||
// Returns a reference to a temporary file that is guaranteed not to exist and
|
||||
// is cleaned up later. See FileTestUtils.getTempFile for details.
|
||||
function getTempFile(leafName) {
|
||||
|
|
|
@ -13,6 +13,12 @@ XPCOMUtils.defineLazyModuleGetters(this, {
|
|||
UIState: "resource://services-sync/UIState.jsm",
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "resProto",
|
||||
"@mozilla.org/network/protocol;1?name=resource",
|
||||
"nsISubstitutingProtocolHandler");
|
||||
|
||||
const RESOURCE_HOST = "onboarding";
|
||||
|
||||
const {PREF_STRING, PREF_BOOL, PREF_INT} = Ci.nsIPrefBranch;
|
||||
|
||||
const BROWSER_READY_NOTIFICATION = "browser-delayed-startup-finished";
|
||||
|
@ -197,6 +203,10 @@ function install(aData, aReason) {}
|
|||
function uninstall(aData, aReason) {}
|
||||
|
||||
function startup(aData, aReason) {
|
||||
resProto.setSubstitutionWithFlags(RESOURCE_HOST,
|
||||
Services.io.newURI("chrome/content/", null, aData.resourceURI),
|
||||
resProto.ALLOW_CONTENT_ACCESS);
|
||||
|
||||
// Cache startup data which contains stuff like the version number, etc.
|
||||
// so we can use it when we init the telemetry
|
||||
startupData = aData;
|
||||
|
@ -211,6 +221,8 @@ function startup(aData, aReason) {
|
|||
}
|
||||
|
||||
function shutdown(aData, aReason) {
|
||||
resProto.setSubstitution(RESOURCE_HOST, null);
|
||||
|
||||
startupData = null;
|
||||
// Stop waiting for browser to be ready
|
||||
if (waitingForBrowserReady) {
|
||||
|
|
|
@ -9,6 +9,10 @@ ChromeUtils.import("resource://gre/modules/Preferences.jsm");
|
|||
ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "resProto",
|
||||
"@mozilla.org/network/protocol;1?name=resource",
|
||||
"nsISubstitutingProtocolHandler");
|
||||
|
||||
// Load our bootstrap extension manifest so we can access our chrome/resource URIs.
|
||||
// Cargo culted from formautofill system add-on
|
||||
const EXTENSION_ID = "onboarding@mozilla.org";
|
||||
|
@ -16,12 +20,19 @@ let extensionDir = Services.dirsvc.get("GreD", Ci.nsIFile);
|
|||
extensionDir.append("browser");
|
||||
extensionDir.append("features");
|
||||
extensionDir.append(EXTENSION_ID);
|
||||
let resourceURI;
|
||||
// If the unpacked extension doesn't exist, use the packed version.
|
||||
if (!extensionDir.exists()) {
|
||||
extensionDir.leafName += ".xpi";
|
||||
|
||||
resourceURI = "jar:" + Services.io.newFileURI(extensionDir).spec + "!/chrome/content/";
|
||||
} else {
|
||||
resourceURI = Services.io.newFileURI(extensionDir).spec + "/chrome/content/";
|
||||
}
|
||||
Components.manager.addBootstrappedManifestLocation(extensionDir);
|
||||
|
||||
resProto.setSubstitution("onboarding", Services.io.newURI(resourceURI));
|
||||
|
||||
const TOURSET_VERSION = 1;
|
||||
const NEXT_TOURSET_VERSION = 2;
|
||||
const PREF_TOUR_TYPE = "browser.onboarding.tour-type";
|
||||
|
|
|
@ -62,10 +62,16 @@ const PLATFORM_NAMES = {
|
|||
* traces; see bug 1426482 for privacy review and server-side mitigation.
|
||||
*/
|
||||
class BrowserErrorReporter {
|
||||
constructor(fetchMethod = this._defaultFetch, chromeOnly = true) {
|
||||
constructor(options = {}) {
|
||||
// Test arguments for mocks and changing behavior
|
||||
this.fetch = fetchMethod;
|
||||
this.chromeOnly = chromeOnly;
|
||||
this.fetch = options.fetch || defaultFetch;
|
||||
this.chromeOnly = options.chromeOnly !== undefined ? options.chromeOnly : true;
|
||||
this.registerListener = (
|
||||
options.registerListener || (() => Services.console.registerListener(this))
|
||||
);
|
||||
this.unregisterListener = (
|
||||
options.unregisterListener || (() => Services.console.unregisterListener(this))
|
||||
);
|
||||
|
||||
// Values that don't change between error reports.
|
||||
this.requestBodyTemplate = {
|
||||
|
@ -120,7 +126,7 @@ class BrowserErrorReporter {
|
|||
|
||||
init() {
|
||||
if (this.collectionEnabled) {
|
||||
Services.console.registerListener(this);
|
||||
this.registerListener();
|
||||
|
||||
// Processing already-logged messages in case any errors occurred before
|
||||
// startup.
|
||||
|
@ -132,16 +138,16 @@ class BrowserErrorReporter {
|
|||
|
||||
uninit() {
|
||||
try {
|
||||
Services.console.unregisterListener(this);
|
||||
this.unregisterListener();
|
||||
} catch (err) {} // It probably wasn't registered.
|
||||
}
|
||||
|
||||
handleEnabledPrefChanged(prefName, previousValue, newValue) {
|
||||
if (newValue) {
|
||||
Services.console.registerListener(this);
|
||||
this.registerListener();
|
||||
} else {
|
||||
try {
|
||||
Services.console.unregisterListener(this);
|
||||
this.unregisterListener();
|
||||
} catch (err) {} // It probably wasn't registered.
|
||||
}
|
||||
}
|
||||
|
@ -165,60 +171,27 @@ class BrowserErrorReporter {
|
|||
return;
|
||||
}
|
||||
|
||||
const extensions = new Map();
|
||||
for (let extension of WebExtensionPolicy.getActiveExtensions()) {
|
||||
extensions.set(extension.mozExtensionHostname, extension);
|
||||
}
|
||||
|
||||
// Replaces any instances of moz-extension:// URLs with internal UUIDs to use
|
||||
// the add-on ID instead.
|
||||
function mangleExtURL(string, anchored = true) {
|
||||
let re = new RegExp(`${anchored ? "^" : ""}moz-extension://([^/]+)/`, "g");
|
||||
|
||||
return string.replace(re, (m0, m1) => {
|
||||
let id = extensions.has(m1) ? extensions.get(m1).id : m1;
|
||||
return `moz-extension://${id}/`;
|
||||
});
|
||||
}
|
||||
|
||||
// Parse the error type from the message if present (e.g. "TypeError: Whoops").
|
||||
let errorMessage = message.errorMessage;
|
||||
let errorName = "Error";
|
||||
if (message.errorMessage.match(ERROR_PREFIX_RE)) {
|
||||
const parts = message.errorMessage.split(":");
|
||||
errorName = parts[0];
|
||||
errorMessage = parts.slice(1).join(":").trim();
|
||||
}
|
||||
|
||||
const frames = [];
|
||||
let frame = message.stack;
|
||||
// Avoid an infinite loop by limiting traces to 100 frames.
|
||||
while (frame && frames.length < 100) {
|
||||
const normalizedFrame = await this.normalizeStackFrame(frame);
|
||||
normalizedFrame.module = mangleExtURL(normalizedFrame.module, false);
|
||||
frames.push(normalizedFrame);
|
||||
frame = frame.parent;
|
||||
}
|
||||
// Frames are sent in order from oldest to newest.
|
||||
frames.reverse();
|
||||
|
||||
const requestBody = Object.assign({}, this.requestBodyTemplate, {
|
||||
const exceptionValue = {};
|
||||
const requestBody = {
|
||||
...this.requestBodyTemplate,
|
||||
timestamp: new Date().toISOString().slice(0, -1), // Remove trailing "Z"
|
||||
project: Services.prefs.getCharPref(PREF_PROJECT_ID),
|
||||
exception: {
|
||||
values: [
|
||||
{
|
||||
type: errorName,
|
||||
value: mangleExtURL(errorMessage),
|
||||
module: message.sourceName,
|
||||
stacktrace: {
|
||||
frames,
|
||||
}
|
||||
},
|
||||
],
|
||||
values: [exceptionValue],
|
||||
},
|
||||
culprit: message.sourceName,
|
||||
});
|
||||
tags: {},
|
||||
};
|
||||
|
||||
const transforms = [
|
||||
addErrorMessage,
|
||||
addStacktrace,
|
||||
addModule,
|
||||
mangleExtensionUrls,
|
||||
tagExtensionErrors,
|
||||
];
|
||||
for (const transform of transforms) {
|
||||
await transform(message, exceptionValue, requestBody);
|
||||
}
|
||||
|
||||
const url = new URL(Services.prefs.getCharPref(PREF_SUBMIT_URL));
|
||||
url.searchParams.set("sentry_client", `${SDK_NAME}/${SDK_VERSION}`);
|
||||
|
@ -241,8 +214,36 @@ class BrowserErrorReporter {
|
|||
this.logger.warn(`Failed to send error: ${error}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async normalizeStackFrame(frame) {
|
||||
function defaultFetch(...args) {
|
||||
// Do not make network requests while running in automation
|
||||
if (Cu.isInAutomation) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return fetch(...args);
|
||||
}
|
||||
|
||||
function addErrorMessage(message, exceptionValue) {
|
||||
// Parse the error type from the message if present (e.g. "TypeError: Whoops").
|
||||
let errorMessage = message.errorMessage;
|
||||
let errorName = "Error";
|
||||
if (message.errorMessage.match(ERROR_PREFIX_RE)) {
|
||||
const parts = message.errorMessage.split(":");
|
||||
errorName = parts[0];
|
||||
errorMessage = parts.slice(1).join(":").trim();
|
||||
}
|
||||
|
||||
exceptionValue.type = errorName;
|
||||
exceptionValue.value = errorMessage;
|
||||
}
|
||||
|
||||
async function addStacktrace(message, exceptionValue) {
|
||||
const frames = [];
|
||||
let frame = message.stack;
|
||||
// Avoid an infinite loop by limiting traces to 100 frames.
|
||||
while (frame && frames.length < 100) {
|
||||
const normalizedFrame = {
|
||||
function: frame.functionDisplayName,
|
||||
module: frame.source,
|
||||
|
@ -277,15 +278,49 @@ class BrowserErrorReporter {
|
|||
// do to recover in either case.
|
||||
}
|
||||
|
||||
return normalizedFrame;
|
||||
frames.push(normalizedFrame);
|
||||
frame = frame.parent;
|
||||
}
|
||||
// Frames are sent in order from oldest to newest.
|
||||
frames.reverse();
|
||||
|
||||
exceptionValue.stacktrace = {frames};
|
||||
}
|
||||
|
||||
function addModule(message, exceptionValue) {
|
||||
exceptionValue.module = message.sourceName;
|
||||
}
|
||||
|
||||
function mangleExtensionUrls(message, exceptionValue) {
|
||||
const extensions = new Map();
|
||||
for (let extension of WebExtensionPolicy.getActiveExtensions()) {
|
||||
extensions.set(extension.mozExtensionHostname, extension);
|
||||
}
|
||||
|
||||
async _defaultFetch(...args) {
|
||||
// Do not make network requests while running in automation
|
||||
if (Cu.isInAutomation) {
|
||||
return null;
|
||||
// Replaces any instances of moz-extension:// URLs with internal UUIDs to use
|
||||
// the add-on ID instead.
|
||||
function mangleExtURL(string, anchored = true) {
|
||||
if (!string) {
|
||||
return string;
|
||||
}
|
||||
|
||||
return fetch(...args);
|
||||
let re = new RegExp(`${anchored ? "^" : ""}moz-extension://([^/]+)/`, "g");
|
||||
|
||||
return string.replace(re, (m0, m1) => {
|
||||
let id = extensions.has(m1) ? extensions.get(m1).id : m1;
|
||||
return `moz-extension://${id}/`;
|
||||
});
|
||||
}
|
||||
|
||||
exceptionValue.value = mangleExtURL(exceptionValue.value, false);
|
||||
exceptionValue.module = mangleExtURL(exceptionValue.module);
|
||||
for (const frame of exceptionValue.stacktrace.frames) {
|
||||
frame.module = mangleExtURL(frame.module);
|
||||
}
|
||||
}
|
||||
|
||||
function tagExtensionErrors(message, exceptionValue, requestBody) {
|
||||
if (exceptionValue.module && exceptionValue.module.startsWith("moz-extension://")) {
|
||||
requestBody.tags.isExtensionError = true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,6 +32,11 @@ const ACTION_ID_BUILT_IN_SEPARATOR = "builtInSeparator";
|
|||
const PREF_PERSISTED_ACTIONS = "browser.pageActions.persistedActions";
|
||||
const PERSISTED_ACTIONS_CURRENT_VERSION = 1;
|
||||
|
||||
// Escapes the given raw URL string, and returns an equivalent CSS url()
|
||||
// value for it.
|
||||
function escapeCSSURL(url) {
|
||||
return `url("${url.replace(/[\\\s"]/g, encodeURIComponent)}")`;
|
||||
}
|
||||
|
||||
var PageActions = {
|
||||
/**
|
||||
|
@ -617,6 +622,29 @@ function Action(options) {
|
|||
if (this._subview) {
|
||||
this._subview = new Subview(options.subview);
|
||||
}
|
||||
|
||||
/**
|
||||
* A cache of the pre-computed CSS variable values for a given icon
|
||||
* URLs object, as passed to _createIconProperties.
|
||||
*/
|
||||
this._iconProperties = new WeakMap();
|
||||
|
||||
/**
|
||||
* The global values for the action properties.
|
||||
*/
|
||||
this._globalProps = {
|
||||
disabled: this._disabled,
|
||||
iconURL: this._iconURL,
|
||||
iconProps: this._createIconProperties(this._iconURL),
|
||||
title: this._title,
|
||||
tooltip: this._tooltip,
|
||||
};
|
||||
|
||||
/**
|
||||
* A mapping of window-specific action property objects, each of which
|
||||
* derives from the _globalProps object.
|
||||
*/
|
||||
this._windowProps = new WeakMap();
|
||||
}
|
||||
|
||||
Action.prototype = {
|
||||
|
@ -661,7 +689,7 @@ Action.prototype = {
|
|||
* The action's disabled state (bool, nonnull)
|
||||
*/
|
||||
getDisabled(browserWindow = null) {
|
||||
return !!this._getProperty("disabled", browserWindow);
|
||||
return !!this._getProperties(browserWindow).disabled;
|
||||
},
|
||||
setDisabled(value, browserWindow = null) {
|
||||
return this._setProperty("disabled", !!value, browserWindow);
|
||||
|
@ -672,17 +700,49 @@ Action.prototype = {
|
|||
* (string or object, nullable)
|
||||
*/
|
||||
getIconURL(browserWindow = null) {
|
||||
return this._getProperty("iconURL", browserWindow);
|
||||
return this._getProperties(browserWindow).iconURL;
|
||||
},
|
||||
setIconURL(value, browserWindow = null) {
|
||||
return this._setProperty("iconURL", value, browserWindow);
|
||||
let props = this._getProperties(browserWindow, !!browserWindow);
|
||||
props.iconURL = value;
|
||||
props.iconProps = this._createIconProperties(value);
|
||||
|
||||
this._updateProperty("iconURL", props.iconProps, browserWindow);
|
||||
return value;
|
||||
},
|
||||
|
||||
/**
|
||||
* The set of CSS variables which define the action's icons in various
|
||||
* sizes. This is generated automatically from the iconURL property.
|
||||
*/
|
||||
getIconProperties(browserWindow = null) {
|
||||
return this._getProperties(browserWindow).iconProps;
|
||||
},
|
||||
|
||||
_createIconProperties(urls) {
|
||||
if (urls && typeof urls == "object") {
|
||||
let props = this._iconProperties.get(urls);
|
||||
if (!props) {
|
||||
props = Object.freeze({
|
||||
"--pageAction-image-16px": escapeCSSURL(this._iconURLForSize(urls, 16)),
|
||||
"--pageAction-image-32px": escapeCSSURL(this._iconURLForSize(urls, 32)),
|
||||
});
|
||||
this._iconProperties.set(urls, props);
|
||||
}
|
||||
return props;
|
||||
}
|
||||
|
||||
return Object.freeze({
|
||||
"--pageAction-image-16px": null,
|
||||
"--pageAction-image-32px": urls ? escapeCSSURL(urls) : null,
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* The action's title (string, nonnull)
|
||||
*/
|
||||
getTitle(browserWindow = null) {
|
||||
return this._getProperty("title", browserWindow);
|
||||
return this._getProperties(browserWindow).title;
|
||||
},
|
||||
setTitle(value, browserWindow = null) {
|
||||
return this._setProperty("title", value, browserWindow);
|
||||
|
@ -692,7 +752,7 @@ Action.prototype = {
|
|||
* The action's tooltip (string, nullable)
|
||||
*/
|
||||
getTooltip(browserWindow = null) {
|
||||
return this._getProperty("tooltip", browserWindow);
|
||||
return this._getProperties(browserWindow).tooltip;
|
||||
},
|
||||
setTooltip(value, browserWindow = null) {
|
||||
return this._setProperty("tooltip", value, browserWindow);
|
||||
|
@ -710,56 +770,45 @@ Action.prototype = {
|
|||
* globally.
|
||||
*/
|
||||
_setProperty(name, value, browserWindow) {
|
||||
if (!browserWindow) {
|
||||
// Set the global state.
|
||||
this[`_${name}`] = value;
|
||||
} else {
|
||||
// Set the per-window state.
|
||||
let props = this._propertiesByBrowserWindow.get(browserWindow);
|
||||
if (!props) {
|
||||
props = {};
|
||||
this._propertiesByBrowserWindow.set(browserWindow, props);
|
||||
}
|
||||
props[name] = value;
|
||||
}
|
||||
// This may be called before the action has been added.
|
||||
if (PageActions.actionForID(this.id)) {
|
||||
for (let bpa of allBrowserPageActions(browserWindow)) {
|
||||
bpa.updateAction(this, name);
|
||||
}
|
||||
}
|
||||
let props = this._getProperties(browserWindow, !!browserWindow);
|
||||
props[name] = value;
|
||||
|
||||
this._updateProperty(name, value, browserWindow);
|
||||
return value;
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets a property, optionally for a particular browser window.
|
||||
*
|
||||
* @param name (string, required)
|
||||
* The (non-underscored) name of the property.
|
||||
* @param browserWindow (DOM window, optional)
|
||||
* If given, then the property will be fetched from this window's
|
||||
* state. If the property does not exist in the window's state, or if
|
||||
* no window is given, then the global value is returned.
|
||||
* @return The property value.
|
||||
*/
|
||||
_getProperty(name, browserWindow) {
|
||||
if (browserWindow) {
|
||||
// Try the per-window state.
|
||||
let props = this._propertiesByBrowserWindow.get(browserWindow);
|
||||
if (props && name in props) {
|
||||
return props[name];
|
||||
_updateProperty(name, value, browserWindow) {
|
||||
// This may be called before the action has been added.
|
||||
if (PageActions.actionForID(this.id)) {
|
||||
for (let bpa of allBrowserPageActions(browserWindow)) {
|
||||
bpa.updateAction(this, name, value);
|
||||
}
|
||||
}
|
||||
// Fall back to the global state.
|
||||
return this[`_${name}`];
|
||||
},
|
||||
|
||||
// maps browser windows => object with properties for that window
|
||||
get _propertiesByBrowserWindow() {
|
||||
if (!this.__propertiesByBrowserWindow) {
|
||||
this.__propertiesByBrowserWindow = new WeakMap();
|
||||
/**
|
||||
* Returns the properties object for the given window, if it exists,
|
||||
* or the global properties object if no window-specific properties
|
||||
* exist.
|
||||
*
|
||||
* @param {Window?} window
|
||||
* The window for which to return the properties object, or
|
||||
* null to return the global properties object.
|
||||
* @param {bool} [forceWindowSpecific = false]
|
||||
* If true, always returns a window-specific properties object.
|
||||
* If a properties object does not exist for the given window,
|
||||
* one is created and cached.
|
||||
* @returns {object}
|
||||
*/
|
||||
_getProperties(window, forceWindowSpecific = false) {
|
||||
let props = window && this._windowProps.get(window);
|
||||
|
||||
if (!props && forceWindowSpecific) {
|
||||
props = Object.create(this._globalProps);
|
||||
this._windowProps.set(window, props);
|
||||
}
|
||||
return this.__propertiesByBrowserWindow;
|
||||
|
||||
return props || this._globalProps;
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -813,25 +862,33 @@ Action.prototype = {
|
|||
return iconURL;
|
||||
}
|
||||
if (typeof(iconURL) == "object") {
|
||||
// This case is copied from ExtensionParent.jsm so that our image logic is
|
||||
// the same, so that WebExtensions page action tests that deal with icons
|
||||
// pass.
|
||||
let bestSize = null;
|
||||
if (iconURL[preferredSize]) {
|
||||
bestSize = preferredSize;
|
||||
} else if (iconURL[2 * preferredSize]) {
|
||||
bestSize = 2 * preferredSize;
|
||||
} else {
|
||||
let sizes = Object.keys(iconURL)
|
||||
.map(key => parseInt(key, 10))
|
||||
.sort((a, b) => a - b);
|
||||
bestSize = sizes.find(candidate => candidate > preferredSize) || sizes.pop();
|
||||
}
|
||||
return iconURL[bestSize];
|
||||
return this._iconURLForSize(iconURL, preferredSize);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Selects the best matching icon from the given URLs object for the
|
||||
* given preferred size, as described in {@see iconURLForSize}.
|
||||
*/
|
||||
_iconURLForSize(urls, preferredSize) {
|
||||
// This case is copied from ExtensionParent.jsm so that our image logic is
|
||||
// the same, so that WebExtensions page action tests that deal with icons
|
||||
// pass.
|
||||
let bestSize = null;
|
||||
if (urls[preferredSize]) {
|
||||
bestSize = preferredSize;
|
||||
} else if (urls[2 * preferredSize]) {
|
||||
bestSize = 2 * preferredSize;
|
||||
} else {
|
||||
let sizes = Object.keys(urls)
|
||||
.map(key => parseInt(key, 10))
|
||||
.sort((a, b) => a - b);
|
||||
bestSize = sizes.find(candidate => candidate > preferredSize) || sizes.pop();
|
||||
}
|
||||
return urls[bestSize];
|
||||
},
|
||||
|
||||
/**
|
||||
* Performs the command for an action. If the action has an onCommand
|
||||
* handler, then it's called. If the action has a subview or iframe, then a
|
||||
|
|
|
@ -16,6 +16,12 @@ const PREF_PROJECT_ID = "browser.chrome.errorReporter.projectId";
|
|||
const PREF_PUBLIC_KEY = "browser.chrome.errorReporter.publicKey";
|
||||
const PREF_SAMPLE_RATE = "browser.chrome.errorReporter.sampleRate";
|
||||
const PREF_SUBMIT_URL = "browser.chrome.errorReporter.submitUrl";
|
||||
const TELEMETRY_ERROR_COLLECTED = "browser.errors.collected_count";
|
||||
const TELEMETRY_ERROR_COLLECTED_FILENAME = "browser.errors.collected_count_by_filename";
|
||||
const TELEMETRY_ERROR_COLLECTED_STACK = "browser.errors.collected_with_stack_count";
|
||||
const TELEMETRY_ERROR_REPORTED = "browser.errors.reported_success_count";
|
||||
const TELEMETRY_ERROR_REPORTED_FAIL = "browser.errors.reported_failure_count";
|
||||
const TELEMETRY_ERROR_SAMPLE_RATE = "browser.errors.sample_rate";
|
||||
|
||||
function createScriptError(options = {}) {
|
||||
const scriptError = Cc["@mozilla.org/scripterror;1"].createInstance(Ci.nsIScriptError);
|
||||
|
@ -31,20 +37,8 @@ function createScriptError(options = {}) {
|
|||
return scriptError;
|
||||
}
|
||||
|
||||
// Wrapper around Services.console.logMessage that waits for the message to be
|
||||
// logged before resolving, since messages are logged asynchronously.
|
||||
function logMessage(message) {
|
||||
return new Promise(resolve => {
|
||||
Services.console.registerListener({
|
||||
observe(loggedMessage) {
|
||||
if (loggedMessage.message === message.message) {
|
||||
Services.console.unregisterListener(this);
|
||||
resolve();
|
||||
}
|
||||
},
|
||||
});
|
||||
Services.console.logMessage(message);
|
||||
});
|
||||
function noop() {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
// Clears the console of any previous messages. Should be called at the end of
|
||||
|
@ -54,21 +48,6 @@ function resetConsole() {
|
|||
Services.console.reset();
|
||||
}
|
||||
|
||||
// Wrapper similar to logMessage, but for logStringMessage.
|
||||
function logStringMessage(message) {
|
||||
return new Promise(resolve => {
|
||||
Services.console.registerListener({
|
||||
observe(loggedMessage) {
|
||||
if (loggedMessage.message === message) {
|
||||
Services.console.unregisterListener(this);
|
||||
resolve();
|
||||
}
|
||||
},
|
||||
});
|
||||
Services.console.logStringMessage(message);
|
||||
});
|
||||
}
|
||||
|
||||
// Finds the fetch spy call for an error with a matching message.
|
||||
function fetchCallForMessage(fetchSpy, message) {
|
||||
for (const call of fetchSpy.getCalls()) {
|
||||
|
@ -88,117 +67,105 @@ function fetchPassedError(fetchSpy, message) {
|
|||
}
|
||||
|
||||
add_task(async function testInitPrefDisabled() {
|
||||
const fetchSpy = sinon.spy();
|
||||
const reporter = new BrowserErrorReporter(fetchSpy);
|
||||
let listening = false;
|
||||
const reporter = new BrowserErrorReporter({
|
||||
registerListener() {
|
||||
listening = true;
|
||||
},
|
||||
});
|
||||
await SpecialPowers.pushPrefEnv({set: [
|
||||
[PREF_ENABLED, false],
|
||||
[PREF_SAMPLE_RATE, "1.0"],
|
||||
]});
|
||||
|
||||
reporter.init();
|
||||
await logMessage(createScriptError({message: "Logged while disabled"}));
|
||||
ok(
|
||||
!fetchPassedError(fetchSpy, "Logged while disabled"),
|
||||
"Reporter does not listen for errors if the enabled pref is false.",
|
||||
);
|
||||
reporter.uninit();
|
||||
resetConsole();
|
||||
ok(!listening, "Reporter does not listen for errors if the enabled pref is false.");
|
||||
});
|
||||
|
||||
add_task(async function testInitUninitPrefEnabled() {
|
||||
const fetchSpy = sinon.spy();
|
||||
const reporter = new BrowserErrorReporter(fetchSpy);
|
||||
let listening = false;
|
||||
const reporter = new BrowserErrorReporter({
|
||||
registerListener() {
|
||||
listening = true;
|
||||
},
|
||||
unregisterListener() {
|
||||
listening = false;
|
||||
},
|
||||
});
|
||||
await SpecialPowers.pushPrefEnv({set: [
|
||||
[PREF_ENABLED, true],
|
||||
[PREF_SAMPLE_RATE, "1.0"],
|
||||
]});
|
||||
|
||||
reporter.init();
|
||||
await logMessage(createScriptError({message: "Logged after init"}));
|
||||
ok(
|
||||
fetchPassedError(fetchSpy, "Logged after init"),
|
||||
"Reporter listens for errors if the enabled pref is true.",
|
||||
);
|
||||
ok(listening, "Reporter listens for errors if the enabled pref is true.");
|
||||
|
||||
fetchSpy.reset();
|
||||
ok(!fetchSpy.called, "Fetch spy was reset.");
|
||||
reporter.uninit();
|
||||
await logMessage(createScriptError({message: "Logged after uninit"}));
|
||||
ok(
|
||||
!fetchPassedError(fetchSpy, "Logged after uninit"),
|
||||
"Reporter does not listen for errors after uninit.",
|
||||
);
|
||||
|
||||
resetConsole();
|
||||
ok(!listening, "Reporter does not listen for errors after uninit.");
|
||||
});
|
||||
|
||||
add_task(async function testInitPastMessages() {
|
||||
const fetchSpy = sinon.spy();
|
||||
const reporter = new BrowserErrorReporter(fetchSpy);
|
||||
const reporter = new BrowserErrorReporter({
|
||||
fetch: fetchSpy,
|
||||
registerListener: noop,
|
||||
unregisterListener: noop,
|
||||
});
|
||||
await SpecialPowers.pushPrefEnv({set: [
|
||||
[PREF_ENABLED, true],
|
||||
[PREF_SAMPLE_RATE, "1.0"],
|
||||
]});
|
||||
|
||||
await logMessage(createScriptError({message: "Logged before init"}));
|
||||
reporter.init();
|
||||
ok(
|
||||
fetchPassedError(fetchSpy, "Logged before init"),
|
||||
"Reporter collects errors logged before initialization.",
|
||||
);
|
||||
reporter.uninit();
|
||||
resetConsole();
|
||||
Services.console.logMessage(createScriptError({message: "Logged before init"}));
|
||||
reporter.init();
|
||||
|
||||
// Include ok() to satisfy mochitest warning for test without any assertions
|
||||
const errorWasLogged = await TestUtils.waitForCondition(
|
||||
() => fetchPassedError(fetchSpy, "Logged before init"),
|
||||
"Waiting for message to be logged",
|
||||
);
|
||||
ok(errorWasLogged, "Reporter collects errors logged before initialization.");
|
||||
|
||||
});
|
||||
|
||||
add_task(async function testEnabledPrefWatcher() {
|
||||
const fetchSpy = sinon.spy();
|
||||
const reporter = new BrowserErrorReporter(fetchSpy);
|
||||
let listening = false;
|
||||
const reporter = new BrowserErrorReporter({
|
||||
registerListener() {
|
||||
listening = true;
|
||||
},
|
||||
unregisterListener() {
|
||||
listening = false;
|
||||
},
|
||||
});
|
||||
await SpecialPowers.pushPrefEnv({set: [
|
||||
[PREF_ENABLED, false],
|
||||
[PREF_SAMPLE_RATE, "1.0"],
|
||||
]});
|
||||
|
||||
reporter.init();
|
||||
await logMessage(createScriptError({message: "Shouldn't report"}));
|
||||
ok(
|
||||
!fetchPassedError(fetchSpy, "Shouldn't report"),
|
||||
"Reporter does not collect errors if the enable pref is false.",
|
||||
);
|
||||
ok(!listening, "Reporter does not collect errors if the enable pref is false.");
|
||||
|
||||
Services.console.logMessage(createScriptError({message: "Shouldn't report"}));
|
||||
await SpecialPowers.pushPrefEnv({set: [
|
||||
[PREF_ENABLED, true],
|
||||
]});
|
||||
ok(
|
||||
!fetchPassedError(fetchSpy, "Shouldn't report"),
|
||||
"Reporter does not collect past-logged errors if it is enabled mid-run.",
|
||||
);
|
||||
await logMessage(createScriptError({message: "Should report"}));
|
||||
ok(
|
||||
fetchPassedError(fetchSpy, "Should report"),
|
||||
"Reporter collects errors logged after the enabled pref is turned on mid-run",
|
||||
);
|
||||
|
||||
reporter.uninit();
|
||||
resetConsole();
|
||||
ok(listening, "Reporter collects errors if the enabled pref switches to true.");
|
||||
});
|
||||
|
||||
add_task(async function testNonErrorLogs() {
|
||||
const fetchSpy = sinon.spy();
|
||||
const reporter = new BrowserErrorReporter(fetchSpy);
|
||||
const reporter = new BrowserErrorReporter({fetch: fetchSpy});
|
||||
await SpecialPowers.pushPrefEnv({set: [
|
||||
[PREF_ENABLED, true],
|
||||
[PREF_SAMPLE_RATE, "1.0"],
|
||||
]});
|
||||
|
||||
reporter.init();
|
||||
|
||||
await logStringMessage("Not a scripterror instance.");
|
||||
reporter.observe({message: "Not a scripterror instance."});
|
||||
ok(
|
||||
!fetchPassedError(fetchSpy, "Not a scripterror instance."),
|
||||
"Reporter does not collect normal log messages or warnings.",
|
||||
);
|
||||
|
||||
await logMessage(createScriptError({
|
||||
await reporter.observe(createScriptError({
|
||||
message: "Warning message",
|
||||
flags: Ci.nsIScriptError.warningFlag,
|
||||
}));
|
||||
|
@ -207,7 +174,7 @@ add_task(async function testNonErrorLogs() {
|
|||
"Reporter does not collect normal log messages or warnings.",
|
||||
);
|
||||
|
||||
await logMessage(createScriptError({
|
||||
await reporter.observe(createScriptError({
|
||||
message: "Non-chrome category",
|
||||
category: "totally from a website",
|
||||
}));
|
||||
|
@ -216,26 +183,22 @@ add_task(async function testNonErrorLogs() {
|
|||
"Reporter does not collect normal log messages or warnings.",
|
||||
);
|
||||
|
||||
await logMessage(createScriptError({message: "Is error"}));
|
||||
await reporter.observe(createScriptError({message: "Is error"}));
|
||||
ok(
|
||||
fetchPassedError(fetchSpy, "Is error"),
|
||||
"Reporter collects error messages.",
|
||||
);
|
||||
|
||||
reporter.uninit();
|
||||
resetConsole();
|
||||
});
|
||||
|
||||
add_task(async function testSampling() {
|
||||
const fetchSpy = sinon.spy();
|
||||
const reporter = new BrowserErrorReporter(fetchSpy);
|
||||
const reporter = new BrowserErrorReporter({fetch: fetchSpy});
|
||||
await SpecialPowers.pushPrefEnv({set: [
|
||||
[PREF_ENABLED, true],
|
||||
[PREF_SAMPLE_RATE, "1.0"],
|
||||
]});
|
||||
|
||||
reporter.init();
|
||||
await logMessage(createScriptError({message: "Should log"}));
|
||||
await reporter.observe(createScriptError({message: "Should log"}));
|
||||
ok(
|
||||
fetchPassedError(fetchSpy, "Should log"),
|
||||
"A 1.0 sample rate will cause the reporter to always collect errors.",
|
||||
|
@ -244,7 +207,7 @@ add_task(async function testSampling() {
|
|||
await SpecialPowers.pushPrefEnv({set: [
|
||||
[PREF_SAMPLE_RATE, "0.0"],
|
||||
]});
|
||||
await logMessage(createScriptError({message: "Shouldn't log"}));
|
||||
await reporter.observe(createScriptError({message: "Shouldn't log"}));
|
||||
ok(
|
||||
!fetchPassedError(fetchSpy, "Shouldn't log"),
|
||||
"A 0.0 sample rate will cause the reporter to never collect errors.",
|
||||
|
@ -253,26 +216,22 @@ add_task(async function testSampling() {
|
|||
await SpecialPowers.pushPrefEnv({set: [
|
||||
[PREF_SAMPLE_RATE, ")fasdf"],
|
||||
]});
|
||||
await logMessage(createScriptError({message: "Also shouldn't log"}));
|
||||
await reporter.observe(createScriptError({message: "Also shouldn't log"}));
|
||||
ok(
|
||||
!fetchPassedError(fetchSpy, "Also shouldn't log"),
|
||||
"An invalid sample rate will cause the reporter to never collect errors.",
|
||||
);
|
||||
|
||||
reporter.uninit();
|
||||
resetConsole();
|
||||
});
|
||||
|
||||
add_task(async function testNameMessage() {
|
||||
const fetchSpy = sinon.spy();
|
||||
const reporter = new BrowserErrorReporter(fetchSpy);
|
||||
const reporter = new BrowserErrorReporter({fetch: fetchSpy});
|
||||
await SpecialPowers.pushPrefEnv({set: [
|
||||
[PREF_ENABLED, true],
|
||||
[PREF_SAMPLE_RATE, "1.0"],
|
||||
]});
|
||||
|
||||
reporter.init();
|
||||
await logMessage(createScriptError({message: "No name"}));
|
||||
await reporter.observe(createScriptError({message: "No name"}));
|
||||
let call = fetchCallForMessage(fetchSpy, "No name");
|
||||
let body = JSON.parse(call.args[1].body);
|
||||
is(
|
||||
|
@ -286,7 +245,7 @@ add_task(async function testNameMessage() {
|
|||
"Reporter uses error message as the exception value.",
|
||||
);
|
||||
|
||||
await logMessage(createScriptError({message: "FooError: Has name"}));
|
||||
await reporter.observe(createScriptError({message: "FooError: Has name"}));
|
||||
call = fetchCallForMessage(fetchSpy, "Has name");
|
||||
body = JSON.parse(call.args[1].body);
|
||||
is(
|
||||
|
@ -300,7 +259,7 @@ add_task(async function testNameMessage() {
|
|||
"Reporter uses error message as the value parameter.",
|
||||
);
|
||||
|
||||
await logMessage(createScriptError({message: "FooError: Has :extra: colons"}));
|
||||
await reporter.observe(createScriptError({message: "FooError: Has :extra: colons"}));
|
||||
call = fetchCallForMessage(fetchSpy, "Has :extra: colons");
|
||||
body = JSON.parse(call.args[1].body);
|
||||
is(
|
||||
|
@ -313,13 +272,11 @@ add_task(async function testNameMessage() {
|
|||
"Has :extra: colons",
|
||||
"Reporter uses error message as the value parameter.",
|
||||
);
|
||||
reporter.uninit();
|
||||
resetConsole();
|
||||
});
|
||||
|
||||
add_task(async function testFetchArguments() {
|
||||
const fetchSpy = sinon.spy();
|
||||
const reporter = new BrowserErrorReporter(fetchSpy);
|
||||
const reporter = new BrowserErrorReporter({fetch: fetchSpy});
|
||||
await SpecialPowers.pushPrefEnv({set: [
|
||||
[PREF_ENABLED, true],
|
||||
[PREF_SAMPLE_RATE, "1.0"],
|
||||
|
@ -328,6 +285,7 @@ add_task(async function testFetchArguments() {
|
|||
[PREF_SUBMIT_URL, "https://errors.example.com/api/123/store/"],
|
||||
]});
|
||||
|
||||
resetConsole();
|
||||
reporter.init();
|
||||
const testPageUrl = (
|
||||
"chrome://mochitests/content/browser/browser/modules/test/browser/" +
|
||||
|
@ -405,18 +363,18 @@ add_task(async function testFetchArguments() {
|
|||
});
|
||||
|
||||
reporter.uninit();
|
||||
resetConsole();
|
||||
});
|
||||
|
||||
add_task(async function testAddonIDMangle() {
|
||||
const fetchSpy = sinon.spy();
|
||||
// Passing false here disables category checks on errors, which would
|
||||
// otherwise block errors directly from extensions.
|
||||
const reporter = new BrowserErrorReporter(fetchSpy, false);
|
||||
const reporter = new BrowserErrorReporter({fetch: fetchSpy, chromeOnly: false});
|
||||
await SpecialPowers.pushPrefEnv({set: [
|
||||
[PREF_ENABLED, true],
|
||||
[PREF_SAMPLE_RATE, "1.0"],
|
||||
]});
|
||||
resetConsole();
|
||||
reporter.init();
|
||||
|
||||
// Create and install test add-on
|
||||
|
@ -447,5 +405,47 @@ add_task(async function testAddonIDMangle() {
|
|||
|
||||
await extension.unload();
|
||||
reporter.uninit();
|
||||
resetConsole();
|
||||
});
|
||||
|
||||
add_task(async function testExtensionTag() {
|
||||
const fetchSpy = sinon.spy();
|
||||
// Passing false here disables category checks on errors, which would
|
||||
// otherwise block errors directly from extensions.
|
||||
const reporter = new BrowserErrorReporter({fetch: fetchSpy, chromeOnly: false});
|
||||
await SpecialPowers.pushPrefEnv({set: [
|
||||
[PREF_ENABLED, true],
|
||||
[PREF_SAMPLE_RATE, "1.0"],
|
||||
]});
|
||||
resetConsole();
|
||||
reporter.init();
|
||||
|
||||
// Create and install test add-on
|
||||
const id = "browsererrorcollection@example.com";
|
||||
const extension = ExtensionTestUtils.loadExtension({
|
||||
manifest: {
|
||||
applications: {
|
||||
gecko: { id },
|
||||
},
|
||||
},
|
||||
background() {
|
||||
throw new Error("testExtensionTag error");
|
||||
},
|
||||
});
|
||||
await extension.startup();
|
||||
|
||||
// Just in case the error hasn't been thrown before add-on startup.
|
||||
let call = await TestUtils.waitForCondition(
|
||||
() => fetchCallForMessage(fetchSpy, "testExtensionTag error"),
|
||||
`Wait for error from ${id} to be logged`,
|
||||
);
|
||||
let body = JSON.parse(call.args[1].body);
|
||||
ok(body.tags.isExtensionError, "Errors from extensions have an isExtensionError tag.");
|
||||
|
||||
await extension.unload();
|
||||
reporter.uninit();
|
||||
|
||||
await reporter.observe(createScriptError({message: "testExtensionTag not from extension"}));
|
||||
call = fetchCallForMessage(fetchSpy, "testExtensionTag not from extension");
|
||||
body = JSON.parse(call.args[1].body);
|
||||
is(body.tags.isExtensionError, undefined, "Normal errors do not have an isExtensionError tag.");
|
||||
});
|
||||
|
|
|
@ -80,16 +80,7 @@ if test -n "$USE_ICU"; then
|
|||
# but we'd need to check in a big-endian version of the file.
|
||||
ICU_DATA_FILE="icudt${version}l.dat"
|
||||
|
||||
dnl We won't build ICU data as a separate file when building
|
||||
dnl JS standalone so that embedders don't have to deal with it.
|
||||
dnl We also don't do it on Windows because sometimes the file goes
|
||||
dnl missing -- possibly due to overzealous antivirus software? --
|
||||
dnl which prevents the browser from starting up :(
|
||||
if test -z "$JS_STANDALONE" -a -z "$MOZ_SYSTEM_ICU" -a "$OS_TARGET" != WINNT -a "$MOZ_WIDGET_TOOLKIT" != "android"; then
|
||||
MOZ_ICU_DATA_ARCHIVE=1
|
||||
else
|
||||
MOZ_ICU_DATA_ARCHIVE=
|
||||
fi
|
||||
MOZ_ICU_DATA_ARCHIVE=
|
||||
fi
|
||||
|
||||
AC_SUBST(MOZ_ICU_VERSION)
|
||||
|
|
|
@ -88,7 +88,14 @@ define(function(require, exports, module) {
|
|||
return Rep(Object.assign({}, props, {
|
||||
cropLimit: 50,
|
||||
noGrip: true,
|
||||
openLink: url => window.open(url, "_blank"),
|
||||
openLink(str) {
|
||||
try {
|
||||
let u = new URL(str);
|
||||
if (u.protocol == "https:" || u.protocol == "http:") {
|
||||
window.open(str, "_blank");
|
||||
}
|
||||
} catch (ex) { /* the link might be bust, then we do nothing */ }
|
||||
},
|
||||
}));
|
||||
}
|
||||
|
||||
|
|
|
@ -82,6 +82,15 @@ Converter.prototype = {
|
|||
request.QueryInterface(Ci.nsIChannel);
|
||||
request.contentType = "text/html";
|
||||
|
||||
// Enforce strict CSP:
|
||||
try {
|
||||
request.QueryInterface(Ci.nsIHttpChannel);
|
||||
request.setResponseHeader("Content-Security-Policy",
|
||||
"default-src 'none' ; script-src resource:; ", false);
|
||||
} catch (ex) {
|
||||
// If this is not an HTTP channel we can't and won't do anything.
|
||||
}
|
||||
|
||||
// Don't honor the charset parameter and use UTF-8 (see bug 741776).
|
||||
request.contentCharset = "UTF-8";
|
||||
this.decoder = new TextDecoder("UTF-8");
|
||||
|
@ -94,10 +103,6 @@ Converter.prototype = {
|
|||
// origin with (other) content.
|
||||
request.loadInfo.resetPrincipalToInheritToNullPrincipal();
|
||||
|
||||
// Because the JSON might be served with a CSP, we instrument
|
||||
// the loadinfo so the Document can discard such a CSP.
|
||||
request.loadInfo.allowDocumentToBeAgnosticToCSP = true;
|
||||
|
||||
// Start the request.
|
||||
this.listener.onStartRequest(request, context);
|
||||
|
||||
|
|
|
@ -1438,7 +1438,8 @@ CanvasRenderingContext2D::AllowOpenGLCanvas() const
|
|||
// HTMLCanvasElement::GetCompositorBackendType would return LAYERS_NONE
|
||||
// as well, so it wouldn't help much.
|
||||
|
||||
return (mCompositorBackend == LayersBackend::LAYERS_OPENGL) &&
|
||||
return (mCompositorBackend == LayersBackend::LAYERS_OPENGL ||
|
||||
mCompositorBackend == LayersBackend::LAYERS_WR) &&
|
||||
gfxPlatform::GetPlatform()->AllowOpenGLCanvas();
|
||||
}
|
||||
|
||||
|
|
|
@ -726,7 +726,7 @@ RespondWithHandler::ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValu
|
|||
// the fragment, so this will never conflict with an existing fragment
|
||||
// on the response. In the future we will have to check for a response
|
||||
// fragment and avoid overriding in that case.
|
||||
if (!mRequestFragment.IsEmpty()) {
|
||||
if (!mRequestFragment.IsEmpty() && !responseURL.IsEmpty()) {
|
||||
MOZ_ASSERT(!responseURL.Contains('#'));
|
||||
responseURL.Append(NS_LITERAL_CSTRING("#"));
|
||||
responseURL.Append(mRequestFragment);
|
||||
|
|
|
@ -82,7 +82,6 @@ FINAL_LIBRARY = 'xul'
|
|||
|
||||
TEST_DIRS += [
|
||||
'test/extensions/bootstrap',
|
||||
'test/extensions/traditional',
|
||||
]
|
||||
|
||||
MOCHITEST_MANIFESTS += [
|
||||
|
|
|
@ -69,7 +69,6 @@ skip-if = (os == 'linux') # Bug 1244409
|
|||
[test_WorkerDebugger_suspended.xul]
|
||||
[test_chromeWorker.xul]
|
||||
[test_chromeWorkerJSM.xul]
|
||||
[test_extension.xul]
|
||||
[test_extensionBootstrap.xul]
|
||||
[test_file.xul]
|
||||
[test_fileBlobPosting.xul]
|
||||
|
|
|
@ -4,4 +4,4 @@
|
|||
# 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/.
|
||||
|
||||
DIRS += ['bootstrap', 'traditional']
|
||||
DIRS += ['bootstrap']
|
||||
|
|
|
@ -1,118 +0,0 @@
|
|||
/**
|
||||
* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
var gWorkerAndCallback = {
|
||||
_worker: null,
|
||||
_callback: null,
|
||||
|
||||
_ensureStarted: function() {
|
||||
if (!this._worker) {
|
||||
throw new Error("Not yet started!");
|
||||
}
|
||||
},
|
||||
|
||||
start: function() {
|
||||
if (!this._worker) {
|
||||
var worker = new Worker("chrome://worker/content/worker.js");
|
||||
worker.onerror = function(event) {
|
||||
Cu.reportError(event.message);
|
||||
event.preventDefault();
|
||||
};
|
||||
|
||||
this._worker = worker;
|
||||
}
|
||||
},
|
||||
|
||||
stop: function() {
|
||||
if (this._worker) {
|
||||
try {
|
||||
this.terminate();
|
||||
}
|
||||
catch(e) {
|
||||
Cu.reportError(e);
|
||||
}
|
||||
this._worker = null;
|
||||
}
|
||||
},
|
||||
|
||||
set callback(val) {
|
||||
this._ensureStarted();
|
||||
if (val) {
|
||||
var callback = val.QueryInterface(Ci.nsIWorkerTestCallback);
|
||||
if (this.callback != callback) {
|
||||
this._worker.onmessage = function(event) {
|
||||
callback.onmessage(event.data);
|
||||
};
|
||||
this._worker.onerror = function(event) {
|
||||
callback.onerror(event.message);
|
||||
event.preventDefault();
|
||||
};
|
||||
this._callback = callback;
|
||||
}
|
||||
}
|
||||
else {
|
||||
this._worker.onmessage = null;
|
||||
this._worker.onerror = null;
|
||||
this._callback = null;
|
||||
}
|
||||
},
|
||||
|
||||
get callback() {
|
||||
return this._callback;
|
||||
},
|
||||
|
||||
postMessage: function(data) {
|
||||
this._ensureStarted();
|
||||
this._worker.postMessage(data);
|
||||
},
|
||||
|
||||
terminate: function() {
|
||||
this._ensureStarted();
|
||||
this._worker.terminate();
|
||||
this.callback = null;
|
||||
}
|
||||
};
|
||||
|
||||
function WorkerTest() {
|
||||
}
|
||||
WorkerTest.prototype = {
|
||||
observe: function(subject, topic, data) {
|
||||
switch(topic) {
|
||||
case "profile-after-change":
|
||||
gWorkerAndCallback.start();
|
||||
Services.obs.addObserver(this, "profile-before-change");
|
||||
break;
|
||||
case "profile-before-change":
|
||||
gWorkerAndCallback.stop();
|
||||
break;
|
||||
default:
|
||||
Cu.reportError("Unknown topic: " + topic);
|
||||
}
|
||||
},
|
||||
|
||||
set callback(val) {
|
||||
gWorkerAndCallback.callback = val;
|
||||
},
|
||||
|
||||
get callback() {
|
||||
return gWorkerAndCallback.callback;
|
||||
},
|
||||
|
||||
postMessage: function(message) {
|
||||
gWorkerAndCallback.postMessage(message);
|
||||
},
|
||||
|
||||
terminate: function() {
|
||||
gWorkerAndCallback.terminate();
|
||||
},
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, Ci.nsIWorkerTest]),
|
||||
classID: Components.ID("{3b52b935-551f-4606-ba4c-decc18b67bfd}")
|
||||
};
|
||||
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([WorkerTest]);
|
|
@ -1,3 +0,0 @@
|
|||
component {3b52b935-551f-4606-ba4c-decc18b67bfd} WorkerTest.js
|
||||
contract @mozilla.org/test/workertest;1 {3b52b935-551f-4606-ba4c-decc18b67bfd}
|
||||
category profile-after-change WorkerTest @mozilla.org/test/workertest;1
|
|
@ -1,30 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
|
||||
|
||||
<Description about="urn:mozilla:install-manifest">
|
||||
<em:name>WorkerTest</em:name>
|
||||
<em:description>Worker functions for use in testing.</em:description>
|
||||
<em:creator>Mozilla</em:creator>
|
||||
<em:version>2016.03.09</em:version>
|
||||
<em:id>worker-test@mozilla.org</em:id>
|
||||
<em:type>2</em:type>
|
||||
<em:targetApplication>
|
||||
<Description>
|
||||
<!-- Firefox -->
|
||||
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
|
||||
<em:minVersion>45.0</em:minVersion>
|
||||
<em:maxVersion>*</em:maxVersion>
|
||||
</Description>
|
||||
</em:targetApplication>
|
||||
<em:targetApplication>
|
||||
<Description>
|
||||
<!-- Fennec -->
|
||||
<em:id>{aa3c5121-dab2-40e2-81ca-7ea25febc110}</em:id>
|
||||
<em:minVersion>45.0</em:minVersion>
|
||||
<em:maxVersion>*</em:maxVersion>
|
||||
</Description>
|
||||
</em:targetApplication>
|
||||
</Description>
|
||||
</RDF>
|
|
@ -1,3 +0,0 @@
|
|||
worker.jar:
|
||||
% content worker %content/
|
||||
content/worker.js (worker.js)
|
|
@ -1,30 +0,0 @@
|
|||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
XPIDL_SOURCES += [
|
||||
'nsIWorkerTest.idl',
|
||||
]
|
||||
|
||||
XPIDL_MODULE = 'WorkerTest'
|
||||
|
||||
EXTRA_COMPONENTS += [
|
||||
'WorkerTest.js',
|
||||
'WorkerTest.manifest',
|
||||
]
|
||||
|
||||
XPI_NAME = 'worker'
|
||||
|
||||
JAR_MANIFESTS += ['jar.mn']
|
||||
USE_EXTENSION_MANIFEST = True
|
||||
NO_JS_MANIFEST = True
|
||||
|
||||
FINAL_TARGET_FILES += [
|
||||
'install.rdf',
|
||||
]
|
||||
|
||||
TEST_HARNESS_FILES.testing.mochitest.extensions += [
|
||||
'worker-test@mozilla.org.xpi',
|
||||
]
|
|
@ -1,23 +0,0 @@
|
|||
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=40: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
[scriptable, uuid(10f8ebdf-1373-4640-9c34-53dee99f526f)]
|
||||
interface nsIWorkerTestCallback : nsISupports
|
||||
{
|
||||
void onmessage(in DOMString data);
|
||||
void onerror(in DOMString data);
|
||||
};
|
||||
|
||||
[scriptable, uuid(887a0614-a0f0-4c0e-80e0-cf31e6d4e286)]
|
||||
interface nsIWorkerTest : nsISupports
|
||||
{
|
||||
void postMessage(in DOMString data);
|
||||
void terminate();
|
||||
|
||||
attribute nsIWorkerTestCallback callback;
|
||||
};
|
Двоичный файл не отображается.
|
@ -1,7 +0,0 @@
|
|||
/**
|
||||
* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
onmessage = function(event) {
|
||||
postMessage(event.data);
|
||||
}
|
|
@ -1,55 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<window title="DOM Worker Threads Test"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
onload="test();">
|
||||
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
|
||||
<script type="application/javascript" src="dom_worker_helper.js"/>
|
||||
|
||||
<script type="application/javascript">
|
||||
<![CDATA[
|
||||
|
||||
function test() {
|
||||
const message = "woohoo";
|
||||
|
||||
var workertest =
|
||||
Cc["@mozilla.org/test/workertest;1"].createInstance(Ci.nsIWorkerTest);
|
||||
|
||||
workertest.callback = {
|
||||
onmessage: function(data) {
|
||||
is(data, message, "Correct message");
|
||||
workertest.callback = null;
|
||||
workertest = null;
|
||||
SimpleTest.finish();
|
||||
},
|
||||
onerror: function(data) {
|
||||
ok(false, "Worker had an error: " + data.message);
|
||||
workertest.callback = null;
|
||||
workertest = null;
|
||||
SimpleTest.finish();
|
||||
},
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIWorkerTestCallback])
|
||||
};
|
||||
|
||||
workertest.postMessage(message);
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
}
|
||||
|
||||
]]>
|
||||
</script>
|
||||
|
||||
<body xmlns="http://www.w3.org/1999/xhtml">
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display:none;"></div>
|
||||
<pre id="test"></pre>
|
||||
</body>
|
||||
<label id="test-result"/>
|
||||
</window>
|
|
@ -24,7 +24,6 @@
|
|||
#include "mozilla/dom/URLSearchParams.h"
|
||||
#include "mozilla/dom/WorkerScope.h"
|
||||
#include "mozilla/dom/WorkerPrivate.h"
|
||||
#include "mozilla/dom/WorkerRef.h"
|
||||
#include "mozilla/dom/WorkerRunnable.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
#include "nsComponentManagerUtils.h"
|
||||
|
@ -242,8 +241,9 @@ class SendRunnable final
|
|||
bool mHasUploadListeners;
|
||||
|
||||
public:
|
||||
SendRunnable(Proxy* aProxy, const nsAString& aStringBody)
|
||||
: WorkerThreadProxySyncRunnable(GetCurrentThreadWorkerPrivate(), aProxy)
|
||||
SendRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
|
||||
const nsAString& aStringBody)
|
||||
: WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy)
|
||||
, StructuredCloneHolder(CloningSupported, TransferringNotSupported,
|
||||
StructuredCloneScope::SameProcessDifferentThread)
|
||||
, mStringBody(aStringBody)
|
||||
|
@ -541,8 +541,8 @@ private:
|
|||
class SyncTeardownRunnable final : public WorkerThreadProxySyncRunnable
|
||||
{
|
||||
public:
|
||||
explicit SyncTeardownRunnable(Proxy* aProxy)
|
||||
: WorkerThreadProxySyncRunnable(GetCurrentThreadWorkerPrivate(), aProxy)
|
||||
SyncTeardownRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy)
|
||||
: WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy)
|
||||
{ }
|
||||
|
||||
private:
|
||||
|
@ -563,8 +563,9 @@ class SetBackgroundRequestRunnable final :
|
|||
bool mValue;
|
||||
|
||||
public:
|
||||
SetBackgroundRequestRunnable(Proxy* aProxy, bool aValue)
|
||||
: WorkerThreadProxySyncRunnable(GetCurrentThreadWorkerPrivate(), aProxy)
|
||||
SetBackgroundRequestRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
|
||||
bool aValue)
|
||||
: WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy)
|
||||
, mValue(aValue)
|
||||
{ }
|
||||
|
||||
|
@ -585,8 +586,9 @@ class SetWithCredentialsRunnable final :
|
|||
bool mValue;
|
||||
|
||||
public:
|
||||
SetWithCredentialsRunnable(Proxy* aProxy, bool aValue)
|
||||
: WorkerThreadProxySyncRunnable(GetCurrentThreadWorkerPrivate(), aProxy)
|
||||
SetWithCredentialsRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
|
||||
bool aValue)
|
||||
: WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy)
|
||||
, mValue(aValue)
|
||||
{ }
|
||||
|
||||
|
@ -606,9 +608,9 @@ class SetResponseTypeRunnable final : public WorkerThreadProxySyncRunnable
|
|||
XMLHttpRequestResponseType mResponseType;
|
||||
|
||||
public:
|
||||
SetResponseTypeRunnable(Proxy* aProxy,
|
||||
SetResponseTypeRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
|
||||
XMLHttpRequestResponseType aResponseType)
|
||||
: WorkerThreadProxySyncRunnable(GetCurrentThreadWorkerPrivate(), aProxy),
|
||||
: WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy),
|
||||
mResponseType(aResponseType)
|
||||
{ }
|
||||
|
||||
|
@ -637,8 +639,9 @@ class SetTimeoutRunnable final : public WorkerThreadProxySyncRunnable
|
|||
uint32_t mTimeout;
|
||||
|
||||
public:
|
||||
SetTimeoutRunnable(Proxy* aProxy, uint32_t aTimeout)
|
||||
: WorkerThreadProxySyncRunnable(GetCurrentThreadWorkerPrivate(), aProxy),
|
||||
SetTimeoutRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
|
||||
uint32_t aTimeout)
|
||||
: WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy),
|
||||
mTimeout(aTimeout)
|
||||
{ }
|
||||
|
||||
|
@ -656,8 +659,8 @@ private:
|
|||
class AbortRunnable final : public WorkerThreadProxySyncRunnable
|
||||
{
|
||||
public:
|
||||
explicit AbortRunnable(Proxy* aProxy)
|
||||
: WorkerThreadProxySyncRunnable(GetCurrentThreadWorkerPrivate(), aProxy)
|
||||
AbortRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy)
|
||||
: WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy)
|
||||
{ }
|
||||
|
||||
private:
|
||||
|
@ -674,8 +677,9 @@ class GetAllResponseHeadersRunnable final :
|
|||
nsCString& mResponseHeaders;
|
||||
|
||||
public:
|
||||
GetAllResponseHeadersRunnable(Proxy* aProxy, nsCString& aResponseHeaders)
|
||||
: WorkerThreadProxySyncRunnable(GetCurrentThreadWorkerPrivate(), aProxy),
|
||||
GetAllResponseHeadersRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
|
||||
nsCString& aResponseHeaders)
|
||||
: WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy),
|
||||
mResponseHeaders(aResponseHeaders)
|
||||
{ }
|
||||
|
||||
|
@ -696,9 +700,9 @@ class GetResponseHeaderRunnable final : public WorkerThreadProxySyncRunnable
|
|||
nsCString& mValue;
|
||||
|
||||
public:
|
||||
GetResponseHeaderRunnable(Proxy* aProxy, const nsACString& aHeader,
|
||||
nsCString& aValue)
|
||||
: WorkerThreadProxySyncRunnable(GetCurrentThreadWorkerPrivate(), aProxy),
|
||||
GetResponseHeaderRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
|
||||
const nsACString& aHeader, nsCString& aValue)
|
||||
: WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy),
|
||||
mHeader(aHeader),
|
||||
mValue(aValue)
|
||||
{ }
|
||||
|
@ -775,10 +779,9 @@ class SetRequestHeaderRunnable final : public WorkerThreadProxySyncRunnable
|
|||
nsCString mValue;
|
||||
|
||||
public:
|
||||
SetRequestHeaderRunnable(Proxy* aProxy,
|
||||
const nsACString& aHeader,
|
||||
const nsACString& aValue)
|
||||
: WorkerThreadProxySyncRunnable(GetCurrentThreadWorkerPrivate(), aProxy),
|
||||
SetRequestHeaderRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
|
||||
const nsACString& aHeader, const nsACString& aValue)
|
||||
: WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy),
|
||||
mHeader(aHeader),
|
||||
mValue(aValue)
|
||||
{ }
|
||||
|
@ -799,8 +802,9 @@ class OverrideMimeTypeRunnable final : public WorkerThreadProxySyncRunnable
|
|||
nsString mMimeType;
|
||||
|
||||
public:
|
||||
OverrideMimeTypeRunnable(Proxy* aProxy, const nsAString& aMimeType)
|
||||
: WorkerThreadProxySyncRunnable(GetCurrentThreadWorkerPrivate(), aProxy),
|
||||
OverrideMimeTypeRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
|
||||
const nsAString& aMimeType)
|
||||
: WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy),
|
||||
mMimeType(aMimeType)
|
||||
{ }
|
||||
|
||||
|
@ -1303,7 +1307,7 @@ EventRunnable::WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
|
|||
state->mResponseURL = mResponseURL;
|
||||
|
||||
XMLHttpRequestWorker* xhr = mProxy->mXMLHttpRequestPrivate;
|
||||
xhr->UpdateState(aCx, *state.get(), mUseCachedArrayBufferResponse);
|
||||
xhr->UpdateState(*state.get(), mUseCachedArrayBufferResponse);
|
||||
|
||||
if (mType.EqualsASCII(sEventStrings[STRING_readystatechange])) {
|
||||
if (mReadyState == 4 && !mUploadEvent && !mProxy->mSeenLoadStart) {
|
||||
|
@ -1539,27 +1543,24 @@ SendRunnable::RunOnMainThread(ErrorResult& aRv)
|
|||
}
|
||||
}
|
||||
|
||||
XMLHttpRequestWorker::XMLHttpRequestWorker()
|
||||
: mResponseType(XMLHttpRequestResponseType::Text)
|
||||
, mTimeout(0)
|
||||
, mBackgroundRequest(false)
|
||||
, mWithCredentials(false)
|
||||
, mCanceled(false)
|
||||
, mMozAnon(false)
|
||||
, mMozSystem(false)
|
||||
XMLHttpRequestWorker::XMLHttpRequestWorker(WorkerPrivate* aWorkerPrivate)
|
||||
: WorkerHolder("XMLHttpRequestWorker"), mWorkerPrivate(aWorkerPrivate),
|
||||
mResponseType(XMLHttpRequestResponseType::Text), mTimeout(0),
|
||||
mRooted(false), mBackgroundRequest(false), mWithCredentials(false),
|
||||
mCanceled(false), mMozAnon(false), mMozSystem(false)
|
||||
{
|
||||
MOZ_ASSERT(IsCurrentThreadRunningWorker());
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
mozilla::HoldJSObjects(this);
|
||||
}
|
||||
|
||||
XMLHttpRequestWorker::~XMLHttpRequestWorker()
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(XMLHttpRequestWorker);
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
ReleaseProxy(XHRIsGoingAway);
|
||||
|
||||
MOZ_ASSERT(!mWorkerRef);
|
||||
MOZ_ASSERT(!mRooted);
|
||||
|
||||
mozilla::DropJSObjects(this);
|
||||
}
|
||||
|
@ -1594,12 +1595,12 @@ XMLHttpRequestWorker::Construct(const GlobalObject& aGlobal,
|
|||
const MozXMLHttpRequestParameters& aParams,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
RefPtr<XMLHttpRequestWorker> xhr = new XMLHttpRequestWorker();
|
||||
|
||||
JSContext* cx = aGlobal.Context();
|
||||
WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(cx);
|
||||
MOZ_ASSERT(workerPrivate);
|
||||
|
||||
RefPtr<XMLHttpRequestWorker> xhr = new XMLHttpRequestWorker(workerPrivate);
|
||||
|
||||
if (workerPrivate->XHRParamsAllowed()) {
|
||||
if (aParams.mMozSystem)
|
||||
xhr->mMozAnon = true;
|
||||
|
@ -1614,7 +1615,8 @@ XMLHttpRequestWorker::Construct(const GlobalObject& aGlobal,
|
|||
void
|
||||
XMLHttpRequestWorker::ReleaseProxy(ReleaseType aType)
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(XMLHttpRequestWorker);
|
||||
// Can't assert that we're on the worker thread here because mWorkerPrivate
|
||||
// may be gone.
|
||||
|
||||
if (mProxy) {
|
||||
if (aType == XHRIsGoingAway) {
|
||||
|
@ -1624,10 +1626,7 @@ XMLHttpRequestWorker::ReleaseProxy(ReleaseType aType)
|
|||
new AsyncTeardownRunnable(mProxy);
|
||||
mProxy = nullptr;
|
||||
|
||||
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
|
||||
MOZ_ASSERT(workerPrivate);
|
||||
|
||||
if (NS_FAILED(workerPrivate->DispatchToMainThread(runnable.forget()))) {
|
||||
if (NS_FAILED(mWorkerPrivate->DispatchToMainThread(runnable.forget()))) {
|
||||
NS_ERROR("Failed to dispatch teardown runnable!");
|
||||
}
|
||||
} else {
|
||||
|
@ -1639,7 +1638,8 @@ XMLHttpRequestWorker::ReleaseProxy(ReleaseType aType)
|
|||
}
|
||||
|
||||
// We need to make a sync call here.
|
||||
RefPtr<SyncTeardownRunnable> runnable = new SyncTeardownRunnable(mProxy);
|
||||
RefPtr<SyncTeardownRunnable> runnable =
|
||||
new SyncTeardownRunnable(mWorkerPrivate, mProxy);
|
||||
mProxy = nullptr;
|
||||
|
||||
IgnoredErrorResult forAssertionsOnly;
|
||||
|
@ -1650,43 +1650,31 @@ XMLHttpRequestWorker::ReleaseProxy(ReleaseType aType)
|
|||
}
|
||||
}
|
||||
|
||||
bool
|
||||
XMLHttpRequestWorker::MaybePin()
|
||||
void
|
||||
XMLHttpRequestWorker::MaybePin(ErrorResult& aRv)
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(XMLHttpRequestWorker);
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
if (mWorkerRef) {
|
||||
return true;
|
||||
if (mRooted) {
|
||||
return;
|
||||
}
|
||||
|
||||
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
|
||||
MOZ_ASSERT(workerPrivate);
|
||||
|
||||
RefPtr<XMLHttpRequestWorker> self = this;
|
||||
mWorkerRef =
|
||||
StrongWorkerRef::Create(workerPrivate, "XMLHttpRequestWorker", [self] {
|
||||
self->mCanceled = true;
|
||||
self->ReleaseProxy(WorkerIsGoingAway);
|
||||
});
|
||||
|
||||
if (NS_WARN_IF(!mWorkerRef)) {
|
||||
return false;
|
||||
if (!HoldWorker(mWorkerPrivate, Canceling)) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
|
||||
NS_ADDREF_THIS();
|
||||
return true;
|
||||
|
||||
mRooted = true;
|
||||
}
|
||||
|
||||
void
|
||||
XMLHttpRequestWorker::MaybeDispatchPrematureAbortEvents(ErrorResult& aRv)
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(XMLHttpRequestWorker);
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
MOZ_ASSERT(mProxy);
|
||||
|
||||
// The worker can be closed during the dispatching of the following events.
|
||||
// Let's keep the proxy alive.
|
||||
RefPtr<Proxy> proxy = mProxy;
|
||||
|
||||
// Only send readystatechange event when state changed.
|
||||
bool isStateChanged = false;
|
||||
if ((mStateData.mReadyState == 1 && mStateData.mFlagSend) ||
|
||||
|
@ -1696,61 +1684,58 @@ XMLHttpRequestWorker::MaybeDispatchPrematureAbortEvents(ErrorResult& aRv)
|
|||
mStateData.mReadyState = 4;
|
||||
}
|
||||
|
||||
if (proxy->mSeenUploadLoadStart) {
|
||||
if (mProxy->mSeenUploadLoadStart) {
|
||||
MOZ_ASSERT(mUpload);
|
||||
|
||||
DispatchPrematureAbortEvent(mUpload, proxy, NS_LITERAL_STRING("abort"),
|
||||
true, aRv);
|
||||
if (aRv.Failed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
DispatchPrematureAbortEvent(mUpload, proxy, NS_LITERAL_STRING("loadend"),
|
||||
true, aRv);
|
||||
if (aRv.Failed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
proxy->mSeenUploadLoadStart = false;
|
||||
}
|
||||
|
||||
if (proxy->mSeenLoadStart) {
|
||||
if (isStateChanged) {
|
||||
DispatchPrematureAbortEvent(this, proxy,
|
||||
NS_LITERAL_STRING("readystatechange"), false,
|
||||
aRv);
|
||||
if (aRv.Failed()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
DispatchPrematureAbortEvent(this, proxy, NS_LITERAL_STRING("abort"), false,
|
||||
DispatchPrematureAbortEvent(mUpload, NS_LITERAL_STRING("abort"), true,
|
||||
aRv);
|
||||
if (aRv.Failed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
DispatchPrematureAbortEvent(this, proxy, NS_LITERAL_STRING("loadend"),
|
||||
false, aRv);
|
||||
DispatchPrematureAbortEvent(mUpload, NS_LITERAL_STRING("loadend"), true,
|
||||
aRv);
|
||||
if (aRv.Failed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
proxy->mSeenLoadStart = false;
|
||||
mProxy->mSeenUploadLoadStart = false;
|
||||
}
|
||||
|
||||
if (mProxy->mSeenLoadStart) {
|
||||
if (isStateChanged) {
|
||||
DispatchPrematureAbortEvent(this, NS_LITERAL_STRING("readystatechange"),
|
||||
false, aRv);
|
||||
if (aRv.Failed()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
DispatchPrematureAbortEvent(this, NS_LITERAL_STRING("abort"), false, aRv);
|
||||
if (aRv.Failed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
DispatchPrematureAbortEvent(this, NS_LITERAL_STRING("loadend"), false,
|
||||
aRv);
|
||||
if (aRv.Failed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
mProxy->mSeenLoadStart = false;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
XMLHttpRequestWorker::DispatchPrematureAbortEvent(EventTarget* aTarget,
|
||||
Proxy* aProxy,
|
||||
const nsAString& aEventType,
|
||||
bool aUploadTarget,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(XMLHttpRequestWorker);
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
MOZ_ASSERT(aTarget);
|
||||
|
||||
if (!aProxy) {
|
||||
if (!mProxy) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
|
@ -1765,14 +1750,14 @@ XMLHttpRequestWorker::DispatchPrematureAbortEvent(EventTarget* aTarget,
|
|||
init.mBubbles = false;
|
||||
init.mCancelable = false;
|
||||
if (aUploadTarget) {
|
||||
init.mLengthComputable = aProxy->mLastUploadLengthComputable;
|
||||
init.mLoaded = aProxy->mLastUploadLoaded;
|
||||
init.mTotal = aProxy->mLastUploadTotal;
|
||||
init.mLengthComputable = mProxy->mLastUploadLengthComputable;
|
||||
init.mLoaded = mProxy->mLastUploadLoaded;
|
||||
init.mTotal = mProxy->mLastUploadTotal;
|
||||
}
|
||||
else {
|
||||
init.mLengthComputable = aProxy->mLastLengthComputable;
|
||||
init.mLoaded = aProxy->mLastLoaded;
|
||||
init.mTotal = aProxy->mLastTotal;
|
||||
init.mLengthComputable = mProxy->mLastLengthComputable;
|
||||
init.mLoaded = mProxy->mLastLoaded;
|
||||
init.mTotal = mProxy->mLastTotal;
|
||||
}
|
||||
event = ProgressEvent::Constructor(aTarget, aEventType, init);
|
||||
}
|
||||
|
@ -1791,11 +1776,13 @@ XMLHttpRequestWorker::DispatchPrematureAbortEvent(EventTarget* aTarget,
|
|||
void
|
||||
XMLHttpRequestWorker::Unpin()
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(XMLHttpRequestWorker);
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
MOZ_ASSERT(mWorkerRef, "Mismatched calls to Unpin!");
|
||||
MOZ_ASSERT(mRooted, "Mismatched calls to Unpin!");
|
||||
|
||||
mWorkerRef = nullptr;
|
||||
ReleaseWorker();
|
||||
|
||||
mRooted = false;
|
||||
|
||||
NS_RELEASE_THIS();
|
||||
}
|
||||
|
@ -1805,7 +1792,7 @@ XMLHttpRequestWorker::SendInternal(SendRunnable* aRunnable,
|
|||
ErrorResult& aRv)
|
||||
{
|
||||
MOZ_ASSERT(aRunnable);
|
||||
NS_ASSERT_OWNINGTHREAD(XMLHttpRequestWorker);
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
// No send() calls when open is running.
|
||||
if (mProxy->mOpenCount) {
|
||||
|
@ -1815,21 +1802,18 @@ XMLHttpRequestWorker::SendInternal(SendRunnable* aRunnable,
|
|||
|
||||
bool hasUploadListeners = mUpload ? mUpload->HasListeners() : false;
|
||||
|
||||
if (NS_WARN_IF(!MaybePin())) {
|
||||
// Worker is shutting down. Let's ignore this send request.
|
||||
MaybePin(aRv);
|
||||
if (aRv.Failed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
AutoUnpinXHR autoUnpin(this);
|
||||
Maybe<AutoSyncLoopHolder> autoSyncLoop;
|
||||
|
||||
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
|
||||
MOZ_ASSERT(workerPrivate);
|
||||
|
||||
nsCOMPtr<nsIEventTarget> syncLoopTarget;
|
||||
bool isSyncXHR = mProxy->mIsSyncXHR;
|
||||
if (isSyncXHR) {
|
||||
autoSyncLoop.emplace(workerPrivate, Terminating);
|
||||
autoSyncLoop.emplace(mWorkerPrivate, Terminating);
|
||||
syncLoopTarget = autoSyncLoop->GetEventTarget();
|
||||
if (!syncLoopTarget) {
|
||||
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
|
@ -1848,7 +1832,7 @@ XMLHttpRequestWorker::SendInternal(SendRunnable* aRunnable,
|
|||
if (aRv.Failed()) {
|
||||
// Dispatch() may have spun the event loop and we may have already unrooted.
|
||||
// If so we don't want autoUnpin to try again.
|
||||
if (!mWorkerRef) {
|
||||
if (!mRooted) {
|
||||
autoUnpin.Clear();
|
||||
}
|
||||
return;
|
||||
|
@ -1875,6 +1859,19 @@ XMLHttpRequestWorker::SendInternal(SendRunnable* aRunnable,
|
|||
}
|
||||
}
|
||||
|
||||
bool
|
||||
XMLHttpRequestWorker::Notify(WorkerStatus aStatus)
|
||||
{
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
if (aStatus >= Canceling && !mCanceled) {
|
||||
mCanceled = true;
|
||||
ReleaseProxy(WorkerIsGoingAway);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
XMLHttpRequestWorker::Open(const nsACString& aMethod,
|
||||
const nsAString& aUrl, bool aAsync,
|
||||
|
@ -1882,10 +1879,7 @@ XMLHttpRequestWorker::Open(const nsACString& aMethod,
|
|||
const Optional<nsAString>& aPassword,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(XMLHttpRequestWorker);
|
||||
|
||||
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
|
||||
MOZ_ASSERT(workerPrivate);
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
if (mCanceled) {
|
||||
aRv.ThrowUncatchableException();
|
||||
|
@ -1894,19 +1888,19 @@ XMLHttpRequestWorker::Open(const nsACString& aMethod,
|
|||
|
||||
if (mProxy) {
|
||||
MaybeDispatchPrematureAbortEvents(aRv);
|
||||
if (aRv.Failed() || !mProxy) {
|
||||
if (aRv.Failed()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
mProxy = new Proxy(this, workerPrivate->GetClientInfo(),
|
||||
workerPrivate->GetController(), mMozAnon, mMozSystem);
|
||||
mProxy = new Proxy(this, mWorkerPrivate->GetClientInfo(),
|
||||
mWorkerPrivate->GetController(), mMozAnon, mMozSystem);
|
||||
}
|
||||
|
||||
mProxy->mOuterEventStreamId++;
|
||||
|
||||
RefPtr<OpenRunnable> runnable =
|
||||
new OpenRunnable(workerPrivate, mProxy, aMethod, aUrl, aUser, aPassword,
|
||||
new OpenRunnable(mWorkerPrivate, mProxy, aMethod, aUrl, aUser, aPassword,
|
||||
mBackgroundRequest, mWithCredentials,
|
||||
mTimeout, mResponseType);
|
||||
|
||||
|
@ -1934,7 +1928,7 @@ void
|
|||
XMLHttpRequestWorker::SetRequestHeader(const nsACString& aHeader,
|
||||
const nsACString& aValue, ErrorResult& aRv)
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(XMLHttpRequestWorker);
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
if (mCanceled) {
|
||||
aRv.ThrowUncatchableException();
|
||||
|
@ -1947,14 +1941,14 @@ XMLHttpRequestWorker::SetRequestHeader(const nsACString& aHeader,
|
|||
}
|
||||
|
||||
RefPtr<SetRequestHeaderRunnable> runnable =
|
||||
new SetRequestHeaderRunnable(mProxy, aHeader, aValue);
|
||||
new SetRequestHeaderRunnable(mWorkerPrivate, mProxy, aHeader, aValue);
|
||||
runnable->Dispatch(Terminating, aRv);
|
||||
}
|
||||
|
||||
void
|
||||
XMLHttpRequestWorker::SetTimeout(uint32_t aTimeout, ErrorResult& aRv)
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(XMLHttpRequestWorker);
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
if (mCanceled) {
|
||||
aRv.ThrowUncatchableException();
|
||||
|
@ -1970,14 +1964,14 @@ XMLHttpRequestWorker::SetTimeout(uint32_t aTimeout, ErrorResult& aRv)
|
|||
}
|
||||
|
||||
RefPtr<SetTimeoutRunnable> runnable =
|
||||
new SetTimeoutRunnable(mProxy, aTimeout);
|
||||
new SetTimeoutRunnable(mWorkerPrivate, mProxy, aTimeout);
|
||||
runnable->Dispatch(Terminating, aRv);
|
||||
}
|
||||
|
||||
void
|
||||
XMLHttpRequestWorker::SetWithCredentials(bool aWithCredentials, ErrorResult& aRv)
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(XMLHttpRequestWorker);
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
if (mCanceled) {
|
||||
aRv.ThrowUncatchableException();
|
||||
|
@ -1993,7 +1987,7 @@ XMLHttpRequestWorker::SetWithCredentials(bool aWithCredentials, ErrorResult& aRv
|
|||
}
|
||||
|
||||
RefPtr<SetWithCredentialsRunnable> runnable =
|
||||
new SetWithCredentialsRunnable(mProxy, aWithCredentials);
|
||||
new SetWithCredentialsRunnable(mWorkerPrivate, mProxy, aWithCredentials);
|
||||
runnable->Dispatch(Terminating, aRv);
|
||||
}
|
||||
|
||||
|
@ -2001,7 +1995,7 @@ void
|
|||
XMLHttpRequestWorker::SetMozBackgroundRequest(bool aBackgroundRequest,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(XMLHttpRequestWorker);
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
if (mCanceled) {
|
||||
aRv.ThrowUncatchableException();
|
||||
|
@ -2017,14 +2011,15 @@ XMLHttpRequestWorker::SetMozBackgroundRequest(bool aBackgroundRequest,
|
|||
}
|
||||
|
||||
RefPtr<SetBackgroundRequestRunnable> runnable =
|
||||
new SetBackgroundRequestRunnable(mProxy, aBackgroundRequest);
|
||||
new SetBackgroundRequestRunnable(mWorkerPrivate, mProxy,
|
||||
aBackgroundRequest);
|
||||
runnable->Dispatch(Terminating, aRv);
|
||||
}
|
||||
|
||||
XMLHttpRequestUpload*
|
||||
XMLHttpRequestWorker::GetUpload(ErrorResult& aRv)
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(XMLHttpRequestWorker);
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
if (mCanceled) {
|
||||
aRv.ThrowUncatchableException();
|
||||
|
@ -2048,7 +2043,7 @@ XMLHttpRequestWorker::Send(JSContext* aCx,
|
|||
const Nullable<DocumentOrBlobOrArrayBufferViewOrArrayBufferOrFormDataOrURLSearchParamsOrUSVString>& aData,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(XMLHttpRequestWorker);
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
if (mCanceled) {
|
||||
aRv.ThrowUncatchableException();
|
||||
|
@ -2063,7 +2058,7 @@ XMLHttpRequestWorker::Send(JSContext* aCx,
|
|||
RefPtr<SendRunnable> sendRunnable;
|
||||
|
||||
if (aData.IsNull()) {
|
||||
sendRunnable = new SendRunnable(mProxy, VoidString());
|
||||
sendRunnable = new SendRunnable(mWorkerPrivate, mProxy, VoidString());
|
||||
// Nothing to clone.
|
||||
}
|
||||
|
||||
|
@ -2089,7 +2084,7 @@ XMLHttpRequestWorker::Send(JSContext* aCx,
|
|||
return;
|
||||
}
|
||||
|
||||
sendRunnable = new SendRunnable(mProxy, EmptyString());
|
||||
sendRunnable = new SendRunnable(mWorkerPrivate, mProxy, EmptyString());
|
||||
|
||||
sendRunnable->Write(aCx, value, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
|
@ -2098,7 +2093,7 @@ XMLHttpRequestWorker::Send(JSContext* aCx,
|
|||
}
|
||||
|
||||
else if (aData.Value().IsArrayBuffer()) {
|
||||
sendRunnable = new SendRunnable(mProxy, EmptyString());
|
||||
sendRunnable = new SendRunnable(mWorkerPrivate, mProxy, EmptyString());
|
||||
|
||||
JS::Rooted<JS::Value> value(aCx);
|
||||
value.setObject(*aData.Value().GetAsArrayBuffer().Obj());
|
||||
|
@ -2119,7 +2114,7 @@ XMLHttpRequestWorker::Send(JSContext* aCx,
|
|||
return;
|
||||
}
|
||||
|
||||
sendRunnable = new SendRunnable(mProxy, EmptyString());
|
||||
sendRunnable = new SendRunnable(mWorkerPrivate, mProxy, EmptyString());
|
||||
|
||||
JS::Rooted<JS::Value> value(aCx);
|
||||
value.setObject(*body.Obj());
|
||||
|
@ -2138,7 +2133,7 @@ XMLHttpRequestWorker::Send(JSContext* aCx,
|
|||
return;
|
||||
}
|
||||
|
||||
sendRunnable = new SendRunnable(mProxy, EmptyString());
|
||||
sendRunnable = new SendRunnable(mWorkerPrivate, mProxy, EmptyString());
|
||||
|
||||
sendRunnable->Write(aCx, value, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
|
@ -2154,7 +2149,7 @@ XMLHttpRequestWorker::Send(JSContext* aCx,
|
|||
return;
|
||||
}
|
||||
|
||||
sendRunnable = new SendRunnable(mProxy, EmptyString());
|
||||
sendRunnable = new SendRunnable(mWorkerPrivate, mProxy, EmptyString());
|
||||
|
||||
sendRunnable->Write(aCx, value, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
|
@ -2163,7 +2158,8 @@ XMLHttpRequestWorker::Send(JSContext* aCx,
|
|||
}
|
||||
|
||||
else if (aData.Value().IsUSVString()) {
|
||||
sendRunnable = new SendRunnable(mProxy, aData.Value().GetAsUSVString());
|
||||
sendRunnable = new SendRunnable(mWorkerPrivate, mProxy,
|
||||
aData.Value().GetAsUSVString());
|
||||
// Nothing to clone.
|
||||
}
|
||||
|
||||
|
@ -2174,7 +2170,7 @@ XMLHttpRequestWorker::Send(JSContext* aCx,
|
|||
void
|
||||
XMLHttpRequestWorker::Abort(ErrorResult& aRv)
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(XMLHttpRequestWorker);
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
if (mCanceled) {
|
||||
aRv.ThrowUncatchableException();
|
||||
|
@ -2207,13 +2203,9 @@ XMLHttpRequestWorker::Abort(ErrorResult& aRv)
|
|||
mStateData.mReadyState = 0;
|
||||
}
|
||||
|
||||
if (!mProxy) {
|
||||
return;
|
||||
}
|
||||
|
||||
mProxy->mOuterEventStreamId++;
|
||||
|
||||
RefPtr<AbortRunnable> runnable = new AbortRunnable(mProxy);
|
||||
RefPtr<AbortRunnable> runnable = new AbortRunnable(mWorkerPrivate, mProxy);
|
||||
runnable->Dispatch(Terminating, aRv);
|
||||
}
|
||||
|
||||
|
@ -2221,7 +2213,7 @@ void
|
|||
XMLHttpRequestWorker::GetResponseHeader(const nsACString& aHeader,
|
||||
nsACString& aResponseHeader, ErrorResult& aRv)
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(XMLHttpRequestWorker);
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
if (mCanceled) {
|
||||
aRv.ThrowUncatchableException();
|
||||
|
@ -2235,7 +2227,8 @@ XMLHttpRequestWorker::GetResponseHeader(const nsACString& aHeader,
|
|||
|
||||
nsCString responseHeader;
|
||||
RefPtr<GetResponseHeaderRunnable> runnable =
|
||||
new GetResponseHeaderRunnable(mProxy, aHeader, responseHeader);
|
||||
new GetResponseHeaderRunnable(mWorkerPrivate, mProxy, aHeader,
|
||||
responseHeader);
|
||||
runnable->Dispatch(Terminating, aRv);
|
||||
if (aRv.Failed()) {
|
||||
return;
|
||||
|
@ -2247,7 +2240,7 @@ void
|
|||
XMLHttpRequestWorker::GetAllResponseHeaders(nsACString& aResponseHeaders,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(XMLHttpRequestWorker);
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
if (mCanceled) {
|
||||
aRv.ThrowUncatchableException();
|
||||
|
@ -2261,7 +2254,7 @@ XMLHttpRequestWorker::GetAllResponseHeaders(nsACString& aResponseHeaders,
|
|||
|
||||
nsCString responseHeaders;
|
||||
RefPtr<GetAllResponseHeadersRunnable> runnable =
|
||||
new GetAllResponseHeadersRunnable(mProxy, responseHeaders);
|
||||
new GetAllResponseHeadersRunnable(mWorkerPrivate, mProxy, responseHeaders);
|
||||
runnable->Dispatch(Terminating, aRv);
|
||||
if (aRv.Failed()) {
|
||||
return;
|
||||
|
@ -2273,7 +2266,7 @@ XMLHttpRequestWorker::GetAllResponseHeaders(nsACString& aResponseHeaders,
|
|||
void
|
||||
XMLHttpRequestWorker::OverrideMimeType(const nsAString& aMimeType, ErrorResult& aRv)
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(XMLHttpRequestWorker);
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
if (mCanceled) {
|
||||
aRv.ThrowUncatchableException();
|
||||
|
@ -2293,7 +2286,7 @@ XMLHttpRequestWorker::OverrideMimeType(const nsAString& aMimeType, ErrorResult&
|
|||
}
|
||||
|
||||
RefPtr<OverrideMimeTypeRunnable> runnable =
|
||||
new OverrideMimeTypeRunnable(mProxy, aMimeType);
|
||||
new OverrideMimeTypeRunnable(mWorkerPrivate, mProxy, aMimeType);
|
||||
runnable->Dispatch(Terminating, aRv);
|
||||
}
|
||||
|
||||
|
@ -2301,7 +2294,7 @@ void
|
|||
XMLHttpRequestWorker::SetResponseType(XMLHttpRequestResponseType aResponseType,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(XMLHttpRequestWorker);
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
if (mCanceled) {
|
||||
aRv.ThrowUncatchableException();
|
||||
|
@ -2329,7 +2322,7 @@ XMLHttpRequestWorker::SetResponseType(XMLHttpRequestResponseType aResponseType,
|
|||
}
|
||||
|
||||
RefPtr<SetResponseTypeRunnable> runnable =
|
||||
new SetResponseTypeRunnable(mProxy, aResponseType);
|
||||
new SetResponseTypeRunnable(mWorkerPrivate, mProxy, aResponseType);
|
||||
runnable->Dispatch(Terminating, aRv);
|
||||
if (aRv.Failed()) {
|
||||
return;
|
||||
|
@ -2339,23 +2332,23 @@ XMLHttpRequestWorker::SetResponseType(XMLHttpRequestResponseType aResponseType,
|
|||
}
|
||||
|
||||
void
|
||||
XMLHttpRequestWorker::GetResponse(JSContext* aCx,
|
||||
XMLHttpRequestWorker::GetResponse(JSContext* /* unused */,
|
||||
JS::MutableHandle<JS::Value> aResponse,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(XMLHttpRequestWorker);
|
||||
|
||||
if (NS_SUCCEEDED(mStateData.mResponseTextResult) &&
|
||||
mStateData.mResponse.isUndefined()) {
|
||||
MOZ_ASSERT(NS_SUCCEEDED(mStateData.mResponseResult));
|
||||
|
||||
if (mStateData.mResponseText.IsEmpty()) {
|
||||
mStateData.mResponse = JS_GetEmptyStringValue(aCx);
|
||||
mStateData.mResponse =
|
||||
JS_GetEmptyStringValue(mWorkerPrivate->GetJSContext());
|
||||
} else {
|
||||
XMLHttpRequestStringSnapshotReaderHelper helper(mStateData.mResponseText);
|
||||
|
||||
JSString* str =
|
||||
JS_NewUCStringCopyN(aCx, helper.Buffer(), helper.Length());
|
||||
JS_NewUCStringCopyN(mWorkerPrivate->GetJSContext(),
|
||||
helper.Buffer(), helper.Length());
|
||||
|
||||
if (!str) {
|
||||
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||
|
@ -2385,17 +2378,15 @@ XMLHttpRequestWorker::GetResponseText(DOMString& aResponseText, ErrorResult& aRv
|
|||
}
|
||||
|
||||
void
|
||||
XMLHttpRequestWorker::UpdateState(JSContext* aCx,
|
||||
const StateData& aStateData,
|
||||
XMLHttpRequestWorker::UpdateState(const StateData& aStateData,
|
||||
bool aUseCachedArrayBufferResponse)
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(XMLHttpRequestWorker);
|
||||
|
||||
if (aUseCachedArrayBufferResponse) {
|
||||
MOZ_ASSERT(mStateData.mResponse.isObject() &&
|
||||
JS_IsArrayBufferObject(&mStateData.mResponse.toObject()));
|
||||
|
||||
JS::Rooted<JS::Value> response(aCx, mStateData.mResponse);
|
||||
JS::Rooted<JS::Value> response(mWorkerPrivate->GetJSContext(),
|
||||
mStateData.mResponse);
|
||||
mStateData = aStateData;
|
||||
mStateData.mResponse = response;
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include "XMLHttpRequest.h"
|
||||
#include "XMLHttpRequestString.h"
|
||||
#include "mozilla/dom/TypedArray.h"
|
||||
#include "mozilla/dom/WorkerHolder.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
@ -17,9 +18,10 @@ namespace dom {
|
|||
class Proxy;
|
||||
class SendRunnable;
|
||||
class DOMString;
|
||||
class StrongWorkerRef;
|
||||
class WorkerPrivate;
|
||||
|
||||
class XMLHttpRequestWorker final : public XMLHttpRequest
|
||||
class XMLHttpRequestWorker final : public XMLHttpRequest,
|
||||
public WorkerHolder
|
||||
{
|
||||
public:
|
||||
struct StateData
|
||||
|
@ -46,13 +48,14 @@ public:
|
|||
|
||||
private:
|
||||
RefPtr<XMLHttpRequestUpload> mUpload;
|
||||
RefPtr<StrongWorkerRef> mWorkerRef;
|
||||
WorkerPrivate* mWorkerPrivate;
|
||||
RefPtr<Proxy> mProxy;
|
||||
XMLHttpRequestResponseType mResponseType;
|
||||
StateData mStateData;
|
||||
|
||||
uint32_t mTimeout;
|
||||
|
||||
bool mRooted;
|
||||
bool mBackgroundRequest;
|
||||
bool mWithCredentials;
|
||||
bool mCanceled;
|
||||
|
@ -74,6 +77,9 @@ public:
|
|||
void
|
||||
Unpin();
|
||||
|
||||
bool
|
||||
Notify(WorkerStatus aStatus) override;
|
||||
|
||||
virtual uint16_t
|
||||
ReadyState() const override
|
||||
{
|
||||
|
@ -245,8 +251,7 @@ public:
|
|||
}
|
||||
|
||||
void
|
||||
UpdateState(JSContext* aCx, const StateData& aStateData,
|
||||
bool aUseCachedArrayBufferResponse);
|
||||
UpdateState(const StateData& aStateData, bool aUseCachedArrayBufferResponse);
|
||||
|
||||
void
|
||||
NullResponseText()
|
||||
|
@ -273,11 +278,11 @@ public:
|
|||
bool
|
||||
SendInProgress() const
|
||||
{
|
||||
return !!mWorkerRef;
|
||||
return mRooted;
|
||||
}
|
||||
|
||||
private:
|
||||
XMLHttpRequestWorker();
|
||||
explicit XMLHttpRequestWorker(WorkerPrivate* aWorkerPrivate);
|
||||
~XMLHttpRequestWorker();
|
||||
|
||||
enum ReleaseType { Default, XHRIsGoingAway, WorkerIsGoingAway };
|
||||
|
@ -285,14 +290,14 @@ private:
|
|||
void
|
||||
ReleaseProxy(ReleaseType aType = Default);
|
||||
|
||||
bool
|
||||
MaybePin();
|
||||
void
|
||||
MaybePin(ErrorResult& aRv);
|
||||
|
||||
void
|
||||
MaybeDispatchPrematureAbortEvents(ErrorResult& aRv);
|
||||
|
||||
void
|
||||
DispatchPrematureAbortEvent(EventTarget* aTarget, Proxy* aProxy,
|
||||
DispatchPrematureAbortEvent(EventTarget* aTarget,
|
||||
const nsAString& aEventType, bool aUploadTarget,
|
||||
ErrorResult& aRv);
|
||||
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html class="reftest-wait">
|
||||
<head>
|
||||
<script type="text/javascript">
|
||||
function load()
|
||||
{
|
||||
let textarea = document.getElementById("editor");
|
||||
textarea.focus();
|
||||
|
||||
SpecialPowers.Cu.import(
|
||||
"chrome://reftest/content/AsyncSpellCheckTestHelper.jsm")
|
||||
.onSpellCheck(textarea, () => {
|
||||
let isc = SpecialPowers.wrap(textarea).editor.getInlineSpellChecker(false);
|
||||
let sc = isc.spellChecker;
|
||||
|
||||
textarea.setAttribute("lang", "en-US");
|
||||
sc.UpdateCurrentDictionary(() => {
|
||||
document.documentElement.classList.remove("reftest-wait");
|
||||
});
|
||||
sc.UninitSpellChecker();
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body onload="load()">
|
||||
<textarea id="editor" spellchecker="true">ABC</textarea>
|
||||
</body>
|
||||
</html>
|
|
@ -98,4 +98,5 @@ load 1414581.html
|
|||
load 1415231.html
|
||||
load 1425091.html
|
||||
load 1443664.html
|
||||
skip-if(Android) needs-focus load 1444630.html
|
||||
load 1446451.html
|
||||
|
|
|
@ -698,6 +698,7 @@ EditorSpellCheck::UninitSpellChecker()
|
|||
DeleteSuggestedWordList();
|
||||
mDictionaryList.Clear();
|
||||
mDictionaryIndex = 0;
|
||||
mDictionaryFetcherGroup++;
|
||||
mSpellChecker = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
|
@ -103,7 +103,7 @@ static setLcdFilterFunc setLcdFilter;
|
|||
#define MAX_OPEN_FACES 10
|
||||
/* This is the maximum font size we allow to be passed to FT_Set_Char_Size
|
||||
*/
|
||||
#define MAX_FONT_SIZE 1000
|
||||
#define MAX_FONT_SIZE 2000
|
||||
|
||||
extern FT_Face mozilla_NewFTFace(FT_Library aFTLibrary, const char* aFileName, int aFaceIndex);
|
||||
extern FT_Face mozilla_NewFTFaceFromData(FT_Library aFTLibrary, const uint8_t* aData, size_t aDataSize, int aFaceIndex);
|
||||
|
|
|
@ -8,7 +8,7 @@ diff --git a/gfx/cairo/cairo/src/cairo-ft-font.c b/gfx/cairo/cairo/src/cairo-ft-
|
|||
+
|
||||
+/* This is the maximum font size we allow to be passed to FT_Set_Char_Size
|
||||
+ */
|
||||
+#define MAX_FONT_SIZE 1000
|
||||
+#define MAX_FONT_SIZE 2000
|
||||
|
||||
/*
|
||||
* The simple 2x2 matrix is converted into separate scale and shape
|
||||
|
|
|
@ -1386,7 +1386,8 @@ bool gfxPlatform::AllowOpenGLCanvas()
|
|||
// so we let content process always assume correct compositor backend.
|
||||
// The callers have to do the right thing.
|
||||
bool correctBackend = !XRE_IsParentProcess() ||
|
||||
((mCompositorBackend == LayersBackend::LAYERS_OPENGL) &&
|
||||
((mCompositorBackend == LayersBackend::LAYERS_OPENGL ||
|
||||
mCompositorBackend == LayersBackend::LAYERS_WR) &&
|
||||
(GetContentBackendFor(mCompositorBackend) == BackendType::SKIA));
|
||||
|
||||
if (gfxPrefs::CanvasAzureAccelerated() && correctBackend) {
|
||||
|
|
|
@ -286,7 +286,7 @@ public:
|
|||
/// asking for it, we will examine the commands in the first few seconds
|
||||
/// of the canvas usage, and potentially change to accelerated or
|
||||
/// non-accelerated canvas.
|
||||
bool AllowOpenGLCanvas();
|
||||
virtual bool AllowOpenGLCanvas();
|
||||
virtual void InitializeSkiaCacheLimits();
|
||||
|
||||
static bool AsyncPanZoomEnabled();
|
||||
|
|
|
@ -509,6 +509,13 @@ gfxWindowsPlatform::UpdateRenderMode()
|
|||
}
|
||||
}
|
||||
|
||||
bool
|
||||
gfxWindowsPlatform::AllowOpenGLCanvas()
|
||||
{
|
||||
// OpenGL canvas is not supported on windows
|
||||
return false;
|
||||
}
|
||||
|
||||
mozilla::gfx::BackendType
|
||||
gfxWindowsPlatform::GetContentBackendFor(mozilla::layers::LayersBackend aLayers)
|
||||
{
|
||||
|
|
|
@ -174,6 +174,8 @@ public:
|
|||
void SchedulePaintIfDeviceReset() override;
|
||||
void CheckForContentOnlyDeviceReset();
|
||||
|
||||
bool AllowOpenGLCanvas() override;
|
||||
|
||||
mozilla::gfx::BackendType GetContentBackendFor(mozilla::layers::LayersBackend aLayers) override;
|
||||
|
||||
mozilla::gfx::BackendType GetPreferredCanvasBackend() override;
|
||||
|
|
|
@ -905,9 +905,9 @@ ToLowerCase(JSContext* cx, JSLinearString* str)
|
|||
// We don't need extra special casing checks in the loop below,
|
||||
// because U+0130 LATIN CAPITAL LETTER I WITH DOT ABOVE and U+03A3
|
||||
// GREEK CAPITAL LETTER SIGMA already have simple lower case mappings.
|
||||
MOZ_ASSERT(unicode::CanLowerCase(unicode::LATIN_CAPITAL_LETTER_I_WITH_DOT_ABOVE),
|
||||
MOZ_ASSERT(unicode::ChangesWhenLowerCased(unicode::LATIN_CAPITAL_LETTER_I_WITH_DOT_ABOVE),
|
||||
"U+0130 has a simple lower case mapping");
|
||||
MOZ_ASSERT(unicode::CanLowerCase(unicode::GREEK_CAPITAL_LETTER_SIGMA),
|
||||
MOZ_ASSERT(unicode::ChangesWhenLowerCased(unicode::GREEK_CAPITAL_LETTER_SIGMA),
|
||||
"U+03A3 has a simple lower case mapping");
|
||||
|
||||
// One element Latin-1 strings can be directly retrieved from the
|
||||
|
@ -930,7 +930,7 @@ ToLowerCase(JSContext* cx, JSLinearString* str)
|
|||
if (unicode::IsLeadSurrogate(c) && i + 1 < length) {
|
||||
CharT trail = chars[i + 1];
|
||||
if (unicode::IsTrailSurrogate(trail)) {
|
||||
if (unicode::CanLowerCaseNonBMP(c, trail))
|
||||
if (unicode::ChangesWhenLowerCasedNonBMP(c, trail))
|
||||
break;
|
||||
|
||||
i++;
|
||||
|
@ -938,7 +938,7 @@ ToLowerCase(JSContext* cx, JSLinearString* str)
|
|||
}
|
||||
}
|
||||
}
|
||||
if (unicode::CanLowerCase(c))
|
||||
if (unicode::ChangesWhenLowerCased(c))
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -1114,24 +1114,24 @@ js::str_toLocaleLowerCase(JSContext* cx, unsigned argc, Value* vp)
|
|||
#endif // EXPOSE_INTL_API
|
||||
|
||||
static inline bool
|
||||
CanUpperCaseSpecialCasing(Latin1Char charCode)
|
||||
ToUpperCaseHasSpecialCasing(Latin1Char charCode)
|
||||
{
|
||||
// Handle U+00DF LATIN SMALL LETTER SHARP S inline, all other Latin-1
|
||||
// characters don't have special casing rules.
|
||||
MOZ_ASSERT_IF(charCode != unicode::LATIN_SMALL_LETTER_SHARP_S,
|
||||
!unicode::CanUpperCaseSpecialCasing(charCode));
|
||||
// U+00DF LATIN SMALL LETTER SHARP S is the only Latin-1 code point with
|
||||
// special casing rules, so detect it inline.
|
||||
bool hasUpperCaseSpecialCasing = charCode == unicode::LATIN_SMALL_LETTER_SHARP_S;
|
||||
MOZ_ASSERT(hasUpperCaseSpecialCasing == unicode::ChangesWhenUpperCasedSpecialCasing(charCode));
|
||||
|
||||
return charCode == unicode::LATIN_SMALL_LETTER_SHARP_S;
|
||||
return hasUpperCaseSpecialCasing;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
CanUpperCaseSpecialCasing(char16_t charCode)
|
||||
ToUpperCaseHasSpecialCasing(char16_t charCode)
|
||||
{
|
||||
return unicode::CanUpperCaseSpecialCasing(charCode);
|
||||
return unicode::ChangesWhenUpperCasedSpecialCasing(charCode);
|
||||
}
|
||||
|
||||
static inline size_t
|
||||
LengthUpperCaseSpecialCasing(Latin1Char charCode)
|
||||
ToUpperCaseLengthSpecialCasing(Latin1Char charCode)
|
||||
{
|
||||
// U+00DF LATIN SMALL LETTER SHARP S is uppercased to two 'S'.
|
||||
MOZ_ASSERT(charCode == unicode::LATIN_SMALL_LETTER_SHARP_S);
|
||||
|
@ -1140,15 +1140,15 @@ LengthUpperCaseSpecialCasing(Latin1Char charCode)
|
|||
}
|
||||
|
||||
static inline size_t
|
||||
LengthUpperCaseSpecialCasing(char16_t charCode)
|
||||
ToUpperCaseLengthSpecialCasing(char16_t charCode)
|
||||
{
|
||||
MOZ_ASSERT(::CanUpperCaseSpecialCasing(charCode));
|
||||
MOZ_ASSERT(ToUpperCaseHasSpecialCasing(charCode));
|
||||
|
||||
return unicode::LengthUpperCaseSpecialCasing(charCode);
|
||||
}
|
||||
|
||||
static inline void
|
||||
AppendUpperCaseSpecialCasing(char16_t charCode, Latin1Char* elements, size_t* index)
|
||||
ToUpperCaseAppendUpperCaseSpecialCasing(char16_t charCode, Latin1Char* elements, size_t* index)
|
||||
{
|
||||
// U+00DF LATIN SMALL LETTER SHARP S is uppercased to two 'S'.
|
||||
MOZ_ASSERT(charCode == unicode::LATIN_SMALL_LETTER_SHARP_S);
|
||||
|
@ -1159,7 +1159,7 @@ AppendUpperCaseSpecialCasing(char16_t charCode, Latin1Char* elements, size_t* in
|
|||
}
|
||||
|
||||
static inline void
|
||||
AppendUpperCaseSpecialCasing(char16_t charCode, char16_t* elements, size_t* index)
|
||||
ToUpperCaseAppendUpperCaseSpecialCasing(char16_t charCode, char16_t* elements, size_t* index)
|
||||
{
|
||||
unicode::AppendUpperCaseSpecialCasing(charCode, elements, index);
|
||||
}
|
||||
|
@ -1191,12 +1191,12 @@ ToUpperCaseImpl(DestChar* destChars, const SrcChar* srcChars, size_t startIndex,
|
|||
}
|
||||
}
|
||||
|
||||
if (MOZ_UNLIKELY(c > 0x7f && ::CanUpperCaseSpecialCasing(static_cast<SrcChar>(c)))) {
|
||||
if (MOZ_UNLIKELY(c > 0x7f && ToUpperCaseHasSpecialCasing(static_cast<SrcChar>(c)))) {
|
||||
// Return if the output buffer is too small.
|
||||
if (srcLength == destLength)
|
||||
return i;
|
||||
|
||||
::AppendUpperCaseSpecialCasing(c, destChars, &j);
|
||||
ToUpperCaseAppendUpperCaseSpecialCasing(c, destChars, &j);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -1226,8 +1226,8 @@ ToUpperCaseLength(const CharT* chars, size_t startIndex, size_t length)
|
|||
for (size_t i = startIndex; i < length; i++) {
|
||||
char16_t c = chars[i];
|
||||
|
||||
if (c > 0x7f && ::CanUpperCaseSpecialCasing(static_cast<CharT>(c)))
|
||||
upperLength += ::LengthUpperCaseSpecialCasing(static_cast<CharT>(c)) - 1;
|
||||
if (c > 0x7f && ToUpperCaseHasSpecialCasing(static_cast<CharT>(c)))
|
||||
upperLength += ToUpperCaseLengthSpecialCasing(static_cast<CharT>(c)) - 1;
|
||||
}
|
||||
return upperLength;
|
||||
}
|
||||
|
@ -1307,7 +1307,7 @@ ToUpperCase(JSContext* cx, JSLinearString* str)
|
|||
}
|
||||
|
||||
MOZ_ASSERT(unicode::ToUpperCase(c) > JSString::MAX_LATIN1_CHAR ||
|
||||
::CanUpperCaseSpecialCasing(c));
|
||||
ToUpperCaseHasSpecialCasing(c));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1319,7 +1319,7 @@ ToUpperCase(JSContext* cx, JSLinearString* str)
|
|||
if (unicode::IsLeadSurrogate(c) && i + 1 < length) {
|
||||
CharT trail = chars[i + 1];
|
||||
if (unicode::IsTrailSurrogate(trail)) {
|
||||
if (unicode::CanUpperCaseNonBMP(c, trail))
|
||||
if (unicode::ChangesWhenUpperCasedNonBMP(c, trail))
|
||||
break;
|
||||
|
||||
i++;
|
||||
|
@ -1327,9 +1327,9 @@ ToUpperCase(JSContext* cx, JSLinearString* str)
|
|||
}
|
||||
}
|
||||
}
|
||||
if (unicode::CanUpperCase(c))
|
||||
if (unicode::ChangesWhenUpperCased(c))
|
||||
break;
|
||||
if (MOZ_UNLIKELY(c > 0x7f && ::CanUpperCaseSpecialCasing(c)))
|
||||
if (MOZ_UNLIKELY(c > 0x7f && ToUpperCaseHasSpecialCasing(c)))
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -2679,7 +2679,7 @@ js::unicode::IsIdentifierPartNonBMP(uint32_t codePoint)
|
|||
}
|
||||
|
||||
bool
|
||||
js::unicode::CanUpperCaseSpecialCasing(char16_t ch)
|
||||
js::unicode::ChangesWhenUpperCasedSpecialCasing(char16_t ch)
|
||||
{
|
||||
if (ch < 0x00DF || ch > 0xFB17)
|
||||
return false;
|
||||
|
|
|
@ -255,8 +255,9 @@ IsSpaceOrBOM2(char16_t ch)
|
|||
}
|
||||
|
||||
/*
|
||||
* Returns the simple upper case mapping (see CanUpperCaseSpecialCasing for
|
||||
* details) of the given UTF-16 code unit.
|
||||
* Returns the simple upper case mapping (possibly the identity mapping; see
|
||||
* ChangesWhenUpperCasedSpecialCasing for details) of the given UTF-16 code
|
||||
* unit.
|
||||
*/
|
||||
inline char16_t
|
||||
ToUpperCase(char16_t ch)
|
||||
|
@ -273,8 +274,9 @@ ToUpperCase(char16_t ch)
|
|||
}
|
||||
|
||||
/*
|
||||
* Returns the simple lower case mapping (see CanUpperCaseSpecialCasing for
|
||||
* details) of the given UTF-16 code unit.
|
||||
* Returns the simple lower case mapping (possibly the identity mapping; see
|
||||
* ChangesWhenUpperCasedSpecialCasing for details) of the given UTF-16 code
|
||||
* unit.
|
||||
*/
|
||||
inline char16_t
|
||||
ToLowerCase(char16_t ch)
|
||||
|
@ -290,32 +292,46 @@ ToLowerCase(char16_t ch)
|
|||
return uint16_t(ch) + info.lowerCase;
|
||||
}
|
||||
|
||||
// Returns true iff ToUpperCase(ch) != ch.
|
||||
/**
|
||||
* Returns true iff ToUpperCase(ch) != ch.
|
||||
*
|
||||
* This function isn't guaranteed to correctly handle code points for which
|
||||
* |ChangesWhenUpperCasedSpecialCasing| returns true, so it is *not* always the
|
||||
* same as the value of the Changes_When_Uppercased Unicode property value for
|
||||
* the code point.
|
||||
*/
|
||||
inline bool
|
||||
CanUpperCase(char16_t ch)
|
||||
ChangesWhenUpperCased(char16_t ch)
|
||||
{
|
||||
if (ch < 128)
|
||||
return ch >= 'a' && ch <= 'z';
|
||||
return CharInfo(ch).upperCase != 0;
|
||||
}
|
||||
|
||||
// Returns true iff ToUpperCase(ch) != ch.
|
||||
/**
|
||||
* Returns true iff ToUpperCase(ch) != ch.
|
||||
*
|
||||
* This function isn't guaranteed to correctly handle code points for which
|
||||
* |ChangesWhenUpperCasedSpecialCasing| returns true, so it is *not* always the
|
||||
* same as the value of the Changes_When_Uppercased Unicode property value for
|
||||
* the code point.
|
||||
*/
|
||||
inline bool
|
||||
CanUpperCase(JS::Latin1Char ch)
|
||||
ChangesWhenUpperCased(JS::Latin1Char ch)
|
||||
{
|
||||
if (MOZ_LIKELY(ch < 128))
|
||||
return ch >= 'a' && ch <= 'z';
|
||||
|
||||
// U+00B5 and U+00E0 to U+00FF, except U+00F7, have an uppercase form.
|
||||
bool canUpper = ch == MICRO_SIGN ||
|
||||
bool hasUpper = ch == MICRO_SIGN ||
|
||||
(((ch & ~0x1F) == LATIN_SMALL_LETTER_A_WITH_GRAVE) && ch != DIVISION_SIGN);
|
||||
MOZ_ASSERT(canUpper == CanUpperCase(char16_t(ch)));
|
||||
return canUpper;
|
||||
MOZ_ASSERT(hasUpper == ChangesWhenUpperCased(char16_t(ch)));
|
||||
return hasUpper;
|
||||
}
|
||||
|
||||
// Returns true iff ToLowerCase(ch) != ch.
|
||||
inline bool
|
||||
CanLowerCase(char16_t ch)
|
||||
ChangesWhenLowerCased(char16_t ch)
|
||||
{
|
||||
if (ch < 128)
|
||||
return ch >= 'A' && ch <= 'Z';
|
||||
|
@ -324,16 +340,16 @@ CanLowerCase(char16_t ch)
|
|||
|
||||
// Returns true iff ToLowerCase(ch) != ch.
|
||||
inline bool
|
||||
CanLowerCase(JS::Latin1Char ch)
|
||||
ChangesWhenLowerCased(JS::Latin1Char ch)
|
||||
{
|
||||
if (MOZ_LIKELY(ch < 128))
|
||||
return ch >= 'A' && ch <= 'Z';
|
||||
|
||||
// U+00C0 to U+00DE, except U+00D7, have a lowercase form.
|
||||
bool canLower = ((ch & ~0x1F) == LATIN_CAPITAL_LETTER_A_WITH_GRAVE) &&
|
||||
bool hasLower = ((ch & ~0x1F) == LATIN_CAPITAL_LETTER_A_WITH_GRAVE) &&
|
||||
((ch & MULTIPLICATION_SIGN) != MULTIPLICATION_SIGN);
|
||||
MOZ_ASSERT(canLower == CanLowerCase(char16_t(ch)));
|
||||
return canLower;
|
||||
MOZ_ASSERT(hasLower == ChangesWhenLowerCased(char16_t(ch)));
|
||||
return hasLower;
|
||||
}
|
||||
|
||||
#define CHECK_RANGE(FROM, TO, LEAD, TRAIL_FROM, TRAIL_TO, DIFF) \
|
||||
|
@ -341,14 +357,14 @@ CanLowerCase(JS::Latin1Char ch)
|
|||
return true;
|
||||
|
||||
inline bool
|
||||
CanUpperCaseNonBMP(char16_t lead, char16_t trail)
|
||||
ChangesWhenUpperCasedNonBMP(char16_t lead, char16_t trail)
|
||||
{
|
||||
FOR_EACH_NON_BMP_UPPERCASE(CHECK_RANGE)
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool
|
||||
CanLowerCaseNonBMP(char16_t lead, char16_t trail)
|
||||
ChangesWhenLowerCasedNonBMP(char16_t lead, char16_t trail)
|
||||
{
|
||||
FOR_EACH_NON_BMP_LOWERCASE(CHECK_RANGE)
|
||||
return false;
|
||||
|
@ -381,24 +397,36 @@ ToLowerCaseNonBMPTrail(char16_t lead, char16_t trail)
|
|||
}
|
||||
|
||||
/*
|
||||
* Returns true if the given UTF-16 code unit has a language-independent,
|
||||
* unconditional or conditional special upper case mapping.
|
||||
* Returns true if, independent of language/locale, the given UTF-16 code unit
|
||||
* has a special upper case mapping.
|
||||
*
|
||||
* Unicode defines two case mapping modes:
|
||||
* 1. "simple case mappings" for one-to-one mappings which are independent of
|
||||
* context and language (defined in UnicodeData.txt).
|
||||
* 2. "special case mappings" for mappings which can increase or decrease the
|
||||
* string length; or are dependent on context or locale (defined in
|
||||
* SpecialCasing.txt).
|
||||
*
|
||||
* The CanUpperCase() method defined above only supports simple case mappings.
|
||||
* In order to support the full case mappings of all Unicode characters,
|
||||
* callers need to check this method in addition to CanUpperCase().
|
||||
* 1. "simple case mappings" (defined in UnicodeData.txt) for one-to-one
|
||||
* mappings that are always the same regardless of locale or context
|
||||
* within a string (e.g. "a"→"A").
|
||||
* 2. "special case mappings" (defined in SpecialCasing.txt) for mappings
|
||||
* that alter string length (e.g. uppercasing "ß"→"SS") or where different
|
||||
* mappings occur depending on language/locale (e.g. uppercasing "i"→"I"
|
||||
* usually but "i"→"İ" in Turkish) or context within the string (e.g.
|
||||
* lowercasing "Σ" U+03A3 GREEK CAPITAL LETTER SIGMA to "ς" U+03C2 GREEK
|
||||
* SMALL LETTER FINAL SIGMA when the sigma appears [roughly speaking] at
|
||||
* the end of a word but "ς" U+03C3 GREEK SMALL LETTER SIGMA anywhere
|
||||
* else).
|
||||
*
|
||||
* NOTE: All special upper case mappings are unconditional in Unicode 9.
|
||||
* The ChangesWhenUpperCased*() functions defined above will return true for
|
||||
* code points that have simple case mappings, but they may not return the
|
||||
* right result for code points that have special case mappings. To correctly
|
||||
* support full case mappings for all code points, callers must determine
|
||||
* whether this function returns true or false for the code point, then use
|
||||
* AppendUpperCaseSpecialCasing in the former case and ToUpperCase in the
|
||||
* latter.
|
||||
*
|
||||
* NOTE: All special upper case mappings are unconditional (that is, they don't
|
||||
* depend on language/locale or context within the string) in Unicode 10.
|
||||
*/
|
||||
bool
|
||||
CanUpperCaseSpecialCasing(char16_t ch);
|
||||
ChangesWhenUpperCasedSpecialCasing(char16_t ch);
|
||||
|
||||
/*
|
||||
* Returns the length of the upper case mapping of |ch|.
|
||||
|
|
|
@ -613,8 +613,8 @@ def make_non_bmp_file(version,
|
|||
non_bmp_file.write(warning_message)
|
||||
non_bmp_file.write(unicode_version_message.format(version))
|
||||
non_bmp_file.write("""
|
||||
#ifndef vm_UnicodeNonBMP_h
|
||||
#define vm_UnicodeNonBMP_h
|
||||
#ifndef util_UnicodeNonBMP_h
|
||||
#define util_UnicodeNonBMP_h
|
||||
|
||||
// |macro| receives the following arguments
|
||||
// macro(FROM, TO, LEAD, TRAIL_FROM, TRAIL_TO, DIFF)
|
||||
|
@ -637,7 +637,7 @@ def make_non_bmp_file(version,
|
|||
make_non_bmp_convert_macro(non_bmp_file, 'REV_CASE_FOLDING', non_bmp_rev_folding_map, codepoint_table)
|
||||
|
||||
non_bmp_file.write("""
|
||||
#endif /* vm_UnicodeNonBMP_h */
|
||||
#endif /* util_UnicodeNonBMP_h */
|
||||
""")
|
||||
|
||||
def write_special_casing_methods(unconditional_toupper, codepoint_table, println):
|
||||
|
@ -723,10 +723,10 @@ def write_special_casing_methods(unconditional_toupper, codepoint_table, println
|
|||
println(indent, ' return {};'.format(range_test_expr))
|
||||
println(indent, '}')
|
||||
|
||||
def write_CanUpperCaseSpecialCasing():
|
||||
def write_ChangesWhenUpperCasedSpecialCasing():
|
||||
""" Checks if the input has a special upper case mapping. """
|
||||
println('bool')
|
||||
println('js::unicode::CanUpperCaseSpecialCasing(char16_t ch)')
|
||||
println('js::unicode::ChangesWhenUpperCasedSpecialCasing(char16_t ch)')
|
||||
println('{')
|
||||
|
||||
assert unconditional_toupper, "|unconditional_toupper| is not empty"
|
||||
|
@ -816,7 +816,7 @@ def write_special_casing_methods(unconditional_toupper, codepoint_table, println
|
|||
|
||||
println('}')
|
||||
|
||||
write_CanUpperCaseSpecialCasing()
|
||||
write_ChangesWhenUpperCasedSpecialCasing()
|
||||
println('')
|
||||
write_LengthUpperCaseSpecialCasing()
|
||||
println('')
|
||||
|
@ -882,7 +882,7 @@ def make_bmp_mapping_test(version, codepoint_table, unconditional_tolower, uncon
|
|||
def unicodeEsc(n):
|
||||
return '\u{:04X}'.format(n)
|
||||
|
||||
file_name = '../tests/ecma_5/String/string-upper-lower-mapping.js'
|
||||
file_name = '../tests/non262/String/string-upper-lower-mapping.js'
|
||||
with io.open(file_name, mode='wb') as output:
|
||||
write = partial(print, file=output, sep='', end='')
|
||||
println = partial(print, file=output, sep='', end='\n')
|
||||
|
@ -919,7 +919,7 @@ if (typeof reportCompare === "function")
|
|||
""")
|
||||
|
||||
def make_non_bmp_mapping_test(version, non_bmp_upper_map, non_bmp_lower_map, codepoint_table):
|
||||
file_name = '../tests/ecma_6/String/string-code-point-upper-lower-mapping.js'
|
||||
file_name = '../tests/non262/String/string-code-point-upper-lower-mapping.js'
|
||||
with io.open(file_name, mode='wb') as test_non_bmp_mapping:
|
||||
test_non_bmp_mapping.write(warning_message)
|
||||
test_non_bmp_mapping.write(unicode_version_message.format(version))
|
||||
|
@ -946,7 +946,7 @@ def make_space_test(version, test_space_table, codepoint_table):
|
|||
def hex_and_name(c):
|
||||
return ' 0x{:04X} /* {} */'.format(c, codepoint_table.name(c))
|
||||
|
||||
file_name = '../tests/ecma_5/String/string-space-trim.js'
|
||||
file_name = '../tests/non262/String/string-space-trim.js'
|
||||
with io.open(file_name, mode='wb') as test_space:
|
||||
test_space.write(warning_message)
|
||||
test_space.write(unicode_version_message.format(version))
|
||||
|
@ -968,7 +968,7 @@ def make_regexp_space_test(version, test_space_table, codepoint_table):
|
|||
def hex_and_name(c):
|
||||
return ' 0x{:04X} /* {} */'.format(c, codepoint_table.name(c))
|
||||
|
||||
file_name = '../tests/ecma_6/RegExp/character-class-escape-s.js'
|
||||
file_name = '../tests/non262/RegExp/character-class-escape-s.js'
|
||||
with io.open(file_name, mode='wb') as test_space:
|
||||
test_space.write(warning_message)
|
||||
test_space.write(unicode_version_message.format(version))
|
||||
|
@ -1002,7 +1002,7 @@ def make_icase_test(version, folding_tests, codepoint_table):
|
|||
def char_hex(c):
|
||||
return '0x{:04X}'.format(c)
|
||||
|
||||
file_name = '../tests/ecma_6/RegExp/unicode-ignoreCase.js'
|
||||
file_name = '../tests/non262/RegExp/unicode-ignoreCase.js'
|
||||
with io.open(file_name, mode='wb') as test_icase:
|
||||
test_icase.write(warning_message)
|
||||
test_icase.write(unicode_version_message.format(version))
|
||||
|
@ -1179,7 +1179,7 @@ def make_unicode_file(version,
|
|||
write(warning_message)
|
||||
write(unicode_version_message.format(version))
|
||||
write(public_domain)
|
||||
println('#include "vm/Unicode.h"')
|
||||
println('#include "util/Unicode.h"')
|
||||
println('')
|
||||
println('using namespace js;')
|
||||
println('using namespace js::unicode;')
|
||||
|
@ -1577,9 +1577,9 @@ def update_unicode(args):
|
|||
if __name__ == '__main__':
|
||||
import argparse
|
||||
|
||||
# This script must be run from js/src/vm to work correctly.
|
||||
if '/'.join(os.path.normpath(os.getcwd()).split(os.sep)[-3:]) != 'js/src/vm':
|
||||
raise RuntimeError('%s must be run from js/src/vm' % sys.argv[0])
|
||||
# This script must be run from js/src/util to work correctly.
|
||||
if '/'.join(os.path.normpath(os.getcwd()).split(os.sep)[-3:]) != 'js/src/util':
|
||||
raise RuntimeError('%s must be run from js/src/util' % sys.argv[0])
|
||||
|
||||
parser = argparse.ArgumentParser(description='Update Unicode data.')
|
||||
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
#include "mozilla/RefPtr.h"
|
||||
#include "mozilla/RefCounted.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "double-conversion/utils.h" // for DISALLOW_COPY_AND_ASSIGN
|
||||
#include "RtpSourceObserver.h"
|
||||
#include "CodecConfig.h"
|
||||
#include "VideoTypes.h"
|
||||
|
@ -90,7 +89,10 @@ private:
|
|||
mCall = std::move(aCall);
|
||||
}
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(WebRtcCallWrapper);
|
||||
// Don't allow copying/assigning.
|
||||
WebRtcCallWrapper(const WebRtcCallWrapper&) = delete;
|
||||
void operator=(const WebRtcCallWrapper&) = delete;
|
||||
|
||||
UniquePtr<webrtc::Call> mCall;
|
||||
webrtc::RtcEventLogNullImpl mEventLog;
|
||||
};
|
||||
|
|
|
@ -311,7 +311,9 @@ public:
|
|||
uint64_t MozVideoLatencyAvg();
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(WebrtcVideoConduit);
|
||||
// Don't allow copying/assigning.
|
||||
WebrtcVideoConduit(const WebrtcVideoConduit&) = delete;
|
||||
void operator=(const WebrtcVideoConduit&) = delete;
|
||||
|
||||
/** Shared statistics for receive and transmit video streams
|
||||
*/
|
||||
|
|
|
@ -1,9 +1,37 @@
|
|||
commit fe9b384793c4e79bd32133dc9053f27b75a5eeae
|
||||
Merge: 2a257b7 1d5a688
|
||||
Author: Florian Loitsch <floitsch@google.com>
|
||||
Date: Fri Sep 15 11:32:00 2017 +0200
|
||||
commit 1b5fa314800a0e68e2b5d00d17e87a5b1fa3ac5d
|
||||
Author: Shane <sffc@sffc1.com>
|
||||
Date: Fri Mar 2 01:26:53 2018 -0800
|
||||
|
||||
Merge pull request #52 from uburuntu/master
|
||||
Clarify output charset in DoubleToAscii documentation (#61)
|
||||
|
||||
Some refactorings: remove unused static, replace deprecated headers, init member in constructor
|
||||
* Clarify output charset in DoubleToAscii documentation
|
||||
|
||||
* Fixing typo in charset docs.
|
||||
|
||||
diff --git a/double-conversion/double-conversion.h b/double-conversion/double-conversion.h
|
||||
index 9978bde..1ccd7fc 100644
|
||||
--- a/double-conversion/double-conversion.h
|
||||
+++ b/double-conversion/double-conversion.h
|
||||
@@ -294,13 +294,18 @@ class DoubleToStringConverter {
|
||||
// should be at least kBase10MaximalLength + 1 characters long.
|
||||
static const int kBase10MaximalLength = 17;
|
||||
|
||||
- // Converts the given double 'v' to ascii. 'v' must not be NaN, +Infinity, or
|
||||
- // -Infinity. In SHORTEST_SINGLE-mode this restriction also applies to 'v'
|
||||
- // after it has been casted to a single-precision float. That is, in this
|
||||
- // mode static_cast<float>(v) must not be NaN, +Infinity or -Infinity.
|
||||
+ // Converts the given double 'v' to digit characters. 'v' must not be NaN,
|
||||
+ // +Infinity, or -Infinity. In SHORTEST_SINGLE-mode this restriction also
|
||||
+ // applies to 'v' after it has been casted to a single-precision float. That
|
||||
+ // is, in this mode static_cast<float>(v) must not be NaN, +Infinity or
|
||||
+ // -Infinity.
|
||||
//
|
||||
// The result should be interpreted as buffer * 10^(point-length).
|
||||
//
|
||||
+ // The digits are written to the buffer in the platform's charset, which is
|
||||
+ // often UTF-8 (with ASCII-range digits) but may be another charset, such
|
||||
+ // as EBCDIC.
|
||||
+ //
|
||||
// The output depends on the given mode:
|
||||
// - SHORTEST: produce the least amount of digits for which the internal
|
||||
// identity requirement is still satisfied. If the digits are printed
|
||||
|
|
|
@ -107,14 +107,14 @@ diff --git a/mfbt/double-conversion/double-conversion/double-conversion.h b/mfbt
|
|||
- static const int kBase10MaximalLength = 17;
|
||||
+ static const MFBT_DATA int kBase10MaximalLength = 17;
|
||||
|
||||
// Converts the given double 'v' to ascii. 'v' must not be NaN, +Infinity, or
|
||||
// -Infinity. In SHORTEST_SINGLE-mode this restriction also applies to 'v'
|
||||
// after it has been casted to a single-precision float. That is, in this
|
||||
// mode static_cast<float>(v) must not be NaN, +Infinity or -Infinity.
|
||||
// Converts the given double 'v' to digit characters. 'v' must not be NaN,
|
||||
// +Infinity, or -Infinity. In SHORTEST_SINGLE-mode this restriction also
|
||||
// applies to 'v' after it has been casted to a single-precision float. That
|
||||
// is, in this mode static_cast<float>(v) must not be NaN, +Infinity or
|
||||
// -Infinity.
|
||||
//
|
||||
// The result should be interpreted as buffer * 10^(point-length).
|
||||
//
|
||||
@@ -327,44 +328,44 @@ class DoubleToStringConverter {
|
||||
@@ -332,44 +333,44 @@ class DoubleToStringConverter {
|
||||
// DoubleToAscii expects the given buffer to be big enough to hold all
|
||||
// digits and a terminating null-character. In SHORTEST-mode it expects a
|
||||
// buffer of at least kBase10MaximalLength + 1. In all other modes the
|
||||
|
|
|
@ -136,7 +136,7 @@ class Bignum {
|
|||
// The Bignum's value equals value(bigits_) * 2^(exponent_ * kBigitSize).
|
||||
int exponent_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(Bignum);
|
||||
DC_DISALLOW_COPY_AND_ASSIGN(Bignum);
|
||||
};
|
||||
|
||||
} // namespace double_conversion
|
||||
|
|
|
@ -296,13 +296,18 @@ class DoubleToStringConverter {
|
|||
// should be at least kBase10MaximalLength + 1 characters long.
|
||||
static const MFBT_DATA int kBase10MaximalLength = 17;
|
||||
|
||||
// Converts the given double 'v' to ascii. 'v' must not be NaN, +Infinity, or
|
||||
// -Infinity. In SHORTEST_SINGLE-mode this restriction also applies to 'v'
|
||||
// after it has been casted to a single-precision float. That is, in this
|
||||
// mode static_cast<float>(v) must not be NaN, +Infinity or -Infinity.
|
||||
// Converts the given double 'v' to digit characters. 'v' must not be NaN,
|
||||
// +Infinity, or -Infinity. In SHORTEST_SINGLE-mode this restriction also
|
||||
// applies to 'v' after it has been casted to a single-precision float. That
|
||||
// is, in this mode static_cast<float>(v) must not be NaN, +Infinity or
|
||||
// -Infinity.
|
||||
//
|
||||
// The result should be interpreted as buffer * 10^(point-length).
|
||||
//
|
||||
// The digits are written to the buffer in the platform's charset, which is
|
||||
// often UTF-8 (with ASCII-range digits) but may be another charset, such
|
||||
// as EBCDIC.
|
||||
//
|
||||
// The output depends on the given mode:
|
||||
// - SHORTEST: produce the least amount of digits for which the internal
|
||||
// identity requirement is still satisfied. If the digits are printed
|
||||
|
@ -376,7 +381,7 @@ class DoubleToStringConverter {
|
|||
const int max_leading_padding_zeroes_in_precision_mode_;
|
||||
const int max_trailing_padding_zeroes_in_precision_mode_;
|
||||
|
||||
DISALLOW_IMPLICIT_CONSTRUCTORS(DoubleToStringConverter);
|
||||
DC_DISALLOW_IMPLICIT_CONSTRUCTORS(DoubleToStringConverter);
|
||||
};
|
||||
|
||||
|
||||
|
@ -540,7 +545,7 @@ class StringToDoubleConverter {
|
|||
bool read_as_double,
|
||||
int* processed_characters_count) const;
|
||||
|
||||
DISALLOW_IMPLICIT_CONSTRUCTORS(StringToDoubleConverter);
|
||||
DC_DISALLOW_IMPLICIT_CONSTRUCTORS(StringToDoubleConverter);
|
||||
};
|
||||
|
||||
} // namespace double_conversion
|
||||
|
|
|
@ -257,7 +257,7 @@ class Double {
|
|||
(biased_exponent << kPhysicalSignificandSize);
|
||||
}
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(Double);
|
||||
DC_DISALLOW_COPY_AND_ASSIGN(Double);
|
||||
};
|
||||
|
||||
class Single {
|
||||
|
@ -394,7 +394,7 @@ class Single {
|
|||
|
||||
const uint32_t d32_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(Single);
|
||||
DC_DISALLOW_COPY_AND_ASSIGN(Single);
|
||||
};
|
||||
|
||||
} // namespace double_conversion
|
||||
|
|
|
@ -205,7 +205,7 @@ static bool DoubleStrtod(Vector<const char> trimmed,
|
|||
// Note that the ARM simulator is compiled for 32bits. It therefore exhibits
|
||||
// the same problem.
|
||||
return false;
|
||||
#endif
|
||||
#else
|
||||
if (trimmed.length() <= kMaxExactDoubleIntegerDecimalDigits) {
|
||||
int read_digits;
|
||||
// The trimmed input fits into a double.
|
||||
|
@ -243,6 +243,7 @@ static bool DoubleStrtod(Vector<const char> trimmed,
|
|||
}
|
||||
}
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -98,8 +98,24 @@ inline void abort_noreturn() { MOZ_CRASH(); }
|
|||
#define DOUBLE_CONVERSION_UNUSED
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32) && !defined(__MINGW32__)
|
||||
|
||||
typedef signed char int8_t;
|
||||
typedef unsigned char uint8_t;
|
||||
typedef short int16_t; // NOLINT
|
||||
typedef unsigned short uint16_t; // NOLINT
|
||||
typedef int int32_t;
|
||||
typedef unsigned int uint32_t;
|
||||
typedef __int64 int64_t;
|
||||
typedef unsigned __int64 uint64_t;
|
||||
// intptr_t and friends are defined in crtdefs.h through stdio.h.
|
||||
|
||||
#else
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#endif
|
||||
|
||||
typedef uint16_t uc16;
|
||||
|
||||
// The following macro works on both 32 and 64-bit platforms.
|
||||
|
@ -120,8 +136,8 @@ typedef uint16_t uc16;
|
|||
|
||||
// A macro to disallow the evil copy constructor and operator= functions
|
||||
// This should be used in the private: declarations for a class
|
||||
#ifndef DISALLOW_COPY_AND_ASSIGN
|
||||
#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
|
||||
#ifndef DC_DISALLOW_COPY_AND_ASSIGN
|
||||
#define DC_DISALLOW_COPY_AND_ASSIGN(TypeName) \
|
||||
TypeName(const TypeName&); \
|
||||
void operator=(const TypeName&)
|
||||
#endif
|
||||
|
@ -132,10 +148,10 @@ typedef uint16_t uc16;
|
|||
// This should be used in the private: declarations for a class
|
||||
// that wants to prevent anyone from instantiating it. This is
|
||||
// especially useful for classes containing only static methods.
|
||||
#ifndef DISALLOW_IMPLICIT_CONSTRUCTORS
|
||||
#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \
|
||||
#ifndef DC_DISALLOW_IMPLICIT_CONSTRUCTORS
|
||||
#define DC_DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \
|
||||
TypeName(); \
|
||||
DISALLOW_COPY_AND_ASSIGN(TypeName)
|
||||
DC_DISALLOW_COPY_AND_ASSIGN(TypeName)
|
||||
#endif
|
||||
|
||||
namespace double_conversion {
|
||||
|
@ -277,7 +293,7 @@ class StringBuilder {
|
|||
|
||||
bool is_finalized() const { return position_ < 0; }
|
||||
|
||||
DISALLOW_IMPLICIT_CONSTRUCTORS(StringBuilder);
|
||||
DC_DISALLOW_IMPLICIT_CONSTRUCTORS(StringBuilder);
|
||||
};
|
||||
|
||||
// The type-based aliasing rule allows the compiler to assume that pointers of
|
||||
|
|
|
@ -11,7 +11,6 @@ set -e
|
|||
LOCAL_PATCHES=""
|
||||
|
||||
LOCAL_PATCHES="$LOCAL_PATCHES add-mfbt-api-markers.patch"
|
||||
LOCAL_PATCHES="$LOCAL_PATCHES use-StandardInteger.patch"
|
||||
LOCAL_PATCHES="$LOCAL_PATCHES use-mozilla-assertions.patch"
|
||||
LOCAL_PATCHES="$LOCAL_PATCHES ToPrecision-exponential.patch"
|
||||
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
diff --git a/mfbt/double-conversion/double-conversion/utils.h b/mfbt/double-conversion/double-conversion/utils.h
|
||||
--- a/mfbt/double-conversion/double-conversion/utils.h
|
||||
+++ b/mfbt/double-conversion/double-conversion/utils.h
|
||||
@@ -93,34 +93,18 @@ inline void abort_noreturn() { abort();
|
||||
#endif
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#define DOUBLE_CONVERSION_UNUSED __attribute__((unused))
|
||||
#else
|
||||
#define DOUBLE_CONVERSION_UNUSED
|
||||
#endif
|
||||
|
||||
-#if defined(_WIN32) && !defined(__MINGW32__)
|
||||
-
|
||||
-typedef signed char int8_t;
|
||||
-typedef unsigned char uint8_t;
|
||||
-typedef short int16_t; // NOLINT
|
||||
-typedef unsigned short uint16_t; // NOLINT
|
||||
-typedef int int32_t;
|
||||
-typedef unsigned int uint32_t;
|
||||
-typedef __int64 int64_t;
|
||||
-typedef unsigned __int64 uint64_t;
|
||||
-// intptr_t and friends are defined in crtdefs.h through stdio.h.
|
||||
-
|
||||
-#else
|
||||
-
|
||||
#include <stdint.h>
|
||||
|
||||
-#endif
|
||||
-
|
||||
typedef uint16_t uc16;
|
||||
|
||||
// The following macro works on both 32 and 64-bit platforms.
|
||||
// Usage: instead of writing 0x1234567890123456
|
||||
// write UINT64_2PART_C(0x12345678,90123456);
|
||||
#define UINT64_2PART_C(a, b) (((static_cast<uint64_t>(a) << 32) + 0x##b##u))
|
||||
|
||||
|
|
@ -1699,14 +1699,19 @@ public class GeckoSession extends LayerSession
|
|||
public static final int TARGET_WINDOW_NEW = 2;
|
||||
|
||||
/**
|
||||
* A request to open an URI.
|
||||
* A request to open an URI. This is called before each page load to
|
||||
* allow custom behavior implementation.
|
||||
* For example, this can be used to override the behavior of
|
||||
* TAGET_WINDOW_NEW requests, which defaults to requesting a new
|
||||
* GeckoSession via onNewSession.
|
||||
*
|
||||
* @param session The GeckoSession that initiated the callback.
|
||||
* @param uri The URI to be loaded.
|
||||
* @param target The target where the window has requested to open. One of
|
||||
* TARGET_WINDOW_*.
|
||||
*
|
||||
* @return Whether or not the load was handled. Returning false will allow Gecko
|
||||
* to continue the load as normal.
|
||||
* @param response A response which will state whether or not the load
|
||||
* was handled. If unhandled, Gecko will continue the
|
||||
* load as normal.
|
||||
*/
|
||||
void onLoadRequest(GeckoSession session, String uri,
|
||||
@TargetWindow int target,
|
||||
|
|
|
@ -69,7 +69,7 @@ function NS_ASSERT(cond, msg)
|
|||
var stack = new Error().stack.split(/\n/);
|
||||
dumpn(stack.map(function(val) { return "###!!! " + val; }).join("\n"));
|
||||
|
||||
throw Cr.NS_ERROR_ABORT;
|
||||
throw Components.Exception("", Cr.NS_ERROR_ABORT);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -509,7 +509,7 @@ nsHttpServer.prototype =
|
|||
_start: function(port, host)
|
||||
{
|
||||
if (this._socket)
|
||||
throw Cr.NS_ERROR_ALREADY_INITIALIZED;
|
||||
throw Components.Exception("", Cr.NS_ERROR_ALREADY_INITIALIZED);
|
||||
|
||||
this._port = port;
|
||||
this._doQuit = this._socketClosed = false;
|
||||
|
@ -579,7 +579,7 @@ nsHttpServer.prototype =
|
|||
catch (e)
|
||||
{
|
||||
dump("\n!!! could not start server on port " + port + ": " + e + "\n\n");
|
||||
throw Cr.NS_ERROR_NOT_AVAILABLE;
|
||||
throw Components.Exception("", Cr.NS_ERROR_NOT_AVAILABLE);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -589,7 +589,7 @@ nsHttpServer.prototype =
|
|||
stop: function(callback)
|
||||
{
|
||||
if (!this._socket)
|
||||
throw Cr.NS_ERROR_UNEXPECTED;
|
||||
throw Components.Exception("", Cr.NS_ERROR_UNEXPECTED);
|
||||
|
||||
// If no argument was provided to stop, return a promise.
|
||||
let returnValue = undefined;
|
||||
|
@ -624,7 +624,7 @@ nsHttpServer.prototype =
|
|||
registerFile: function(path, file)
|
||||
{
|
||||
if (file && (!file.exists() || file.isDirectory()))
|
||||
throw Cr.NS_ERROR_INVALID_ARG;
|
||||
throw Components.Exception("", Cr.NS_ERROR_INVALID_ARG);
|
||||
|
||||
this._handler.registerFile(path, file);
|
||||
},
|
||||
|
@ -639,7 +639,7 @@ nsHttpServer.prototype =
|
|||
path.charAt(path.length - 1) != "/" ||
|
||||
(directory &&
|
||||
(!directory.exists() || !directory.isDirectory())))
|
||||
throw Cr.NS_ERROR_INVALID_ARG;
|
||||
throw Components.Exception("", Cr.NS_ERROR_INVALID_ARG);
|
||||
|
||||
// XXX determine behavior of nonexistent /foo/bar when a /foo/bar/ mapping
|
||||
// exists!
|
||||
|
@ -765,7 +765,7 @@ nsHttpServer.prototype =
|
|||
iid.equals(Ci.nsISupports))
|
||||
return this;
|
||||
|
||||
throw Cr.NS_ERROR_NO_INTERFACE;
|
||||
throw Components.Exception("", Cr.NS_ERROR_NO_INTERFACE);
|
||||
},
|
||||
|
||||
|
||||
|
@ -940,7 +940,7 @@ ServerIdentity.prototype =
|
|||
get primaryScheme()
|
||||
{
|
||||
if (this._primaryPort === -1)
|
||||
throw Cr.NS_ERROR_NOT_INITIALIZED;
|
||||
throw Components.Exception("", Cr.NS_ERROR_NOT_INITIALIZED);
|
||||
return this._primaryScheme;
|
||||
},
|
||||
|
||||
|
@ -950,7 +950,7 @@ ServerIdentity.prototype =
|
|||
get primaryHost()
|
||||
{
|
||||
if (this._primaryPort === -1)
|
||||
throw Cr.NS_ERROR_NOT_INITIALIZED;
|
||||
throw Components.Exception("", Cr.NS_ERROR_NOT_INITIALIZED);
|
||||
return this._primaryHost;
|
||||
},
|
||||
|
||||
|
@ -960,7 +960,7 @@ ServerIdentity.prototype =
|
|||
get primaryPort()
|
||||
{
|
||||
if (this._primaryPort === -1)
|
||||
throw Cr.NS_ERROR_NOT_INITIALIZED;
|
||||
throw Components.Exception("", Cr.NS_ERROR_NOT_INITIALIZED);
|
||||
return this._primaryPort;
|
||||
},
|
||||
|
||||
|
@ -1056,7 +1056,7 @@ ServerIdentity.prototype =
|
|||
if (iid.equals(Ci.nsIHttpServerIdentity) || iid.equals(Ci.nsISupports))
|
||||
return this;
|
||||
|
||||
throw Cr.NS_ERROR_NO_INTERFACE;
|
||||
throw Components.Exception("", Cr.NS_ERROR_NO_INTERFACE);
|
||||
},
|
||||
|
||||
|
||||
|
@ -1126,17 +1126,17 @@ ServerIdentity.prototype =
|
|||
{
|
||||
dumpn("*** server only supports http/https schemes: '" + scheme + "'");
|
||||
dumpStack();
|
||||
throw Cr.NS_ERROR_ILLEGAL_VALUE;
|
||||
throw Components.Exception("", Cr.NS_ERROR_ILLEGAL_VALUE);
|
||||
}
|
||||
if (!HOST_REGEX.test(host))
|
||||
{
|
||||
dumpn("*** unexpected host: '" + host + "'");
|
||||
throw Cr.NS_ERROR_ILLEGAL_VALUE;
|
||||
throw Components.Exception("", Cr.NS_ERROR_ILLEGAL_VALUE);
|
||||
}
|
||||
if (port < 0 || port > 65535)
|
||||
{
|
||||
dumpn("*** unexpected port: '" + port + "'");
|
||||
throw Cr.NS_ERROR_ILLEGAL_VALUE;
|
||||
throw Components.Exception("", Cr.NS_ERROR_ILLEGAL_VALUE);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -1421,7 +1421,7 @@ RequestReader.prototype =
|
|||
aIID.equals(Ci.nsISupports))
|
||||
return this;
|
||||
|
||||
throw Cr.NS_ERROR_NO_INTERFACE;
|
||||
throw Components.Exception("", Cr.NS_ERROR_NO_INTERFACE);
|
||||
},
|
||||
|
||||
|
||||
|
@ -2504,7 +2504,7 @@ ServerHandler.prototype =
|
|||
{
|
||||
// XXX true path validation!
|
||||
if (path.charAt(0) != "/")
|
||||
throw Cr.NS_ERROR_INVALID_ARG;
|
||||
throw Components.Exception("", Cr.NS_ERROR_INVALID_ARG);
|
||||
|
||||
this._handlerToField(handler, this._overridePaths, path);
|
||||
},
|
||||
|
@ -2516,7 +2516,7 @@ ServerHandler.prototype =
|
|||
{
|
||||
// XXX true path validation!
|
||||
if (path.charAt(0) != "/" || path.charAt(path.length - 1) != "/")
|
||||
throw Cr.NS_ERROR_INVALID_ARG;
|
||||
throw Components.Exception("", Cr.NS_ERROR_INVALID_ARG);
|
||||
|
||||
this._handlerToField(handler, this._overridePrefixes, path);
|
||||
},
|
||||
|
@ -2535,7 +2535,7 @@ ServerHandler.prototype =
|
|||
// the path-to-directory mapping code requires that the first character not
|
||||
// be "/", or it will go into an infinite loop
|
||||
if (key.charAt(0) == "/")
|
||||
throw Cr.NS_ERROR_INVALID_ARG;
|
||||
throw Components.Exception("", Cr.NS_ERROR_INVALID_ARG);
|
||||
|
||||
key = toInternalPath(key, false);
|
||||
|
||||
|
@ -3199,7 +3199,7 @@ ServerHandler.prototype =
|
|||
_handleError: function(errorCode, metadata, response)
|
||||
{
|
||||
if (!metadata)
|
||||
throw Cr.NS_ERROR_NULL_POINTER;
|
||||
throw Components.Exception("", Cr.NS_ERROR_NULL_POINTER);
|
||||
|
||||
var errorX00 = errorCode - (errorCode % 100);
|
||||
|
||||
|
@ -3630,7 +3630,7 @@ Response.prototype =
|
|||
get bodyOutputStream()
|
||||
{
|
||||
if (this._finished)
|
||||
throw Cr.NS_ERROR_NOT_AVAILABLE;
|
||||
throw Components.Exception("", Cr.NS_ERROR_NOT_AVAILABLE);
|
||||
|
||||
if (!this._bodyOutputStream)
|
||||
{
|
||||
|
@ -3651,7 +3651,7 @@ Response.prototype =
|
|||
write: function(data)
|
||||
{
|
||||
if (this._finished)
|
||||
throw Cr.NS_ERROR_NOT_AVAILABLE;
|
||||
throw Components.Exception("", Cr.NS_ERROR_NOT_AVAILABLE);
|
||||
|
||||
var dataAsString = String(data);
|
||||
this.bodyOutputStream.write(dataAsString, dataAsString.length);
|
||||
|
@ -3663,11 +3663,11 @@ Response.prototype =
|
|||
setStatusLine: function(httpVersion, code, description)
|
||||
{
|
||||
if (!this._headers || this._finished || this._powerSeized)
|
||||
throw Cr.NS_ERROR_NOT_AVAILABLE;
|
||||
throw Components.Exception("", Cr.NS_ERROR_NOT_AVAILABLE);
|
||||
this._ensureAlive();
|
||||
|
||||
if (!(code >= 0 && code < 1000))
|
||||
throw Cr.NS_ERROR_INVALID_ARG;
|
||||
throw Components.Exception("", Cr.NS_ERROR_INVALID_ARG);
|
||||
|
||||
try
|
||||
{
|
||||
|
@ -3682,7 +3682,7 @@ Response.prototype =
|
|||
}
|
||||
catch (e)
|
||||
{
|
||||
throw Cr.NS_ERROR_INVALID_ARG;
|
||||
throw Components.Exception("", Cr.NS_ERROR_INVALID_ARG);
|
||||
}
|
||||
|
||||
// Reason-Phrase = *<TEXT, excluding CR, LF>
|
||||
|
@ -3694,7 +3694,7 @@ Response.prototype =
|
|||
description = "";
|
||||
for (var i = 0; i < description.length; i++)
|
||||
if (isCTL(description.charCodeAt(i)) && description.charAt(i) != "\t")
|
||||
throw Cr.NS_ERROR_INVALID_ARG;
|
||||
throw Components.Exception("", Cr.NS_ERROR_INVALID_ARG);
|
||||
|
||||
// set the values only after validation to preserve atomicity
|
||||
this._httpDescription = description;
|
||||
|
@ -3708,7 +3708,7 @@ Response.prototype =
|
|||
setHeader: function(name, value, merge)
|
||||
{
|
||||
if (!this._headers || this._finished || this._powerSeized)
|
||||
throw Cr.NS_ERROR_NOT_AVAILABLE;
|
||||
throw Components.Exception("", Cr.NS_ERROR_NOT_AVAILABLE);
|
||||
this._ensureAlive();
|
||||
|
||||
this._headers.setHeader(name, value, merge);
|
||||
|
@ -3717,7 +3717,7 @@ Response.prototype =
|
|||
setHeaderNoCheck: function(name, value)
|
||||
{
|
||||
if (!this._headers || this._finished || this._powerSeized)
|
||||
throw Cr.NS_ERROR_NOT_AVAILABLE;
|
||||
throw Components.Exception("", Cr.NS_ERROR_NOT_AVAILABLE);
|
||||
this._ensureAlive();
|
||||
|
||||
this._headers.setHeaderNoCheck(name, value);
|
||||
|
@ -3729,9 +3729,9 @@ Response.prototype =
|
|||
processAsync: function()
|
||||
{
|
||||
if (this._finished)
|
||||
throw Cr.NS_ERROR_UNEXPECTED;
|
||||
throw Components.Exception("", Cr.NS_ERROR_UNEXPECTED);
|
||||
if (this._powerSeized)
|
||||
throw Cr.NS_ERROR_NOT_AVAILABLE;
|
||||
throw Components.Exception("", Cr.NS_ERROR_NOT_AVAILABLE);
|
||||
if (this._processAsync)
|
||||
return;
|
||||
this._ensureAlive();
|
||||
|
@ -3762,9 +3762,9 @@ Response.prototype =
|
|||
seizePower: function()
|
||||
{
|
||||
if (this._processAsync)
|
||||
throw Cr.NS_ERROR_NOT_AVAILABLE;
|
||||
throw Components.Exception("", Cr.NS_ERROR_NOT_AVAILABLE);
|
||||
if (this._finished)
|
||||
throw Cr.NS_ERROR_UNEXPECTED;
|
||||
throw Components.Exception("", Cr.NS_ERROR_UNEXPECTED);
|
||||
if (this._powerSeized)
|
||||
return;
|
||||
this._ensureAlive();
|
||||
|
@ -3798,7 +3798,7 @@ Response.prototype =
|
|||
finish: function()
|
||||
{
|
||||
if (!this._processAsync && !this._powerSeized)
|
||||
throw Cr.NS_ERROR_UNEXPECTED;
|
||||
throw Components.Exception("", Cr.NS_ERROR_UNEXPECTED);
|
||||
if (this._finished)
|
||||
return;
|
||||
|
||||
|
@ -3820,7 +3820,7 @@ Response.prototype =
|
|||
if (iid.equals(Ci.nsIHttpResponse) || iid.equals(Ci.nsISupports))
|
||||
return this;
|
||||
|
||||
throw Cr.NS_ERROR_NO_INTERFACE;
|
||||
throw Components.Exception("", Cr.NS_ERROR_NO_INTERFACE);
|
||||
},
|
||||
|
||||
|
||||
|
@ -4117,7 +4117,7 @@ Response.prototype =
|
|||
if (aIID.equals(Ci.nsIRequestObserver) || aIID.equals(Ci.nsISupports))
|
||||
return this;
|
||||
|
||||
throw Cr.NS_ERROR_NO_INTERFACE;
|
||||
throw Components.Exception("", Cr.NS_ERROR_NO_INTERFACE);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -4180,7 +4180,7 @@ Response.prototype =
|
|||
if (aIID.equals(Ci.nsIRequestObserver) || aIID.equals(Ci.nsISupports))
|
||||
return this;
|
||||
|
||||
throw Cr.NS_ERROR_NO_INTERFACE;
|
||||
throw Components.Exception("", Cr.NS_ERROR_NO_INTERFACE);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -4206,7 +4206,7 @@ Response.SEGMENT_SIZE = 8192;
|
|||
/** Serves double duty in WriteThroughCopier implementation. */
|
||||
function notImplemented()
|
||||
{
|
||||
throw Cr.NS_ERROR_NOT_IMPLEMENTED;
|
||||
throw Components.Exception("", Cr.NS_ERROR_NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
/** Returns true iff the given exception represents stream closure. */
|
||||
|
@ -4241,7 +4241,7 @@ function wouldBlock(e)
|
|||
function WriteThroughCopier(source, sink, observer, context)
|
||||
{
|
||||
if (!source || !sink || !observer)
|
||||
throw Cr.NS_ERROR_NULL_POINTER;
|
||||
throw Components.Exception("", Cr.NS_ERROR_NULL_POINTER);
|
||||
|
||||
/** Stream from which data is being read. */
|
||||
this._source = source;
|
||||
|
@ -4309,7 +4309,7 @@ WriteThroughCopier.prototype =
|
|||
return this;
|
||||
}
|
||||
|
||||
throw Cr.NS_ERROR_NO_INTERFACE;
|
||||
throw Components.Exception("", Cr.NS_ERROR_NO_INTERFACE);
|
||||
},
|
||||
|
||||
|
||||
|
@ -4371,7 +4371,7 @@ WriteThroughCopier.prototype =
|
|||
// Handle the zero-data edge case in the same place as all other edge
|
||||
// cases are handled.
|
||||
if (bytesWanted === 0)
|
||||
throw Cr.NS_BASE_STREAM_CLOSED;
|
||||
throw Components.Exception("", Cr.NS_BASE_STREAM_CLOSED);
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
|
@ -4795,7 +4795,7 @@ const headerUtils =
|
|||
if (fieldName == "")
|
||||
{
|
||||
dumpn("*** Empty fieldName");
|
||||
throw Cr.NS_ERROR_INVALID_ARG;
|
||||
throw Components.Exception("", Cr.NS_ERROR_INVALID_ARG);
|
||||
}
|
||||
|
||||
for (var i = 0, sz = fieldName.length; i < sz; i++)
|
||||
|
@ -4803,7 +4803,7 @@ const headerUtils =
|
|||
if (!IS_TOKEN_ARRAY[fieldName.charCodeAt(i)])
|
||||
{
|
||||
dumpn(fieldName + " is not a valid header field name!");
|
||||
throw Cr.NS_ERROR_INVALID_ARG;
|
||||
throw Components.Exception("", Cr.NS_ERROR_INVALID_ARG);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4852,7 +4852,7 @@ const headerUtils =
|
|||
if (isCTL(val.charCodeAt(i)))
|
||||
{
|
||||
dump("*** Char " + i + " has charcode " + val.charCodeAt(i));
|
||||
throw Cr.NS_ERROR_INVALID_ARG;
|
||||
throw Components.Exception("", Cr.NS_ERROR_INVALID_ARG);
|
||||
}
|
||||
|
||||
// XXX disallows quoted-pair where CHAR is a CTL -- will not invalidly
|
||||
|
@ -5065,7 +5065,7 @@ nsHttpHeaders.prototype =
|
|||
if (name in this._headers)
|
||||
return this._headers[name];
|
||||
else
|
||||
throw Cr.NS_ERROR_NOT_AVAILABLE;
|
||||
throw Components.Exception("", Cr.NS_ERROR_NOT_AVAILABLE);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -5126,7 +5126,7 @@ nsSimpleEnumerator.prototype =
|
|||
getNext: function()
|
||||
{
|
||||
if (!this.hasMoreElements())
|
||||
throw Cr.NS_ERROR_NOT_AVAILABLE;
|
||||
throw Components.Exception("", Cr.NS_ERROR_NOT_AVAILABLE);
|
||||
|
||||
return this._items[this._nextIndex++];
|
||||
},
|
||||
|
@ -5136,7 +5136,7 @@ nsSimpleEnumerator.prototype =
|
|||
Ci.nsISupports.equals(aIID))
|
||||
return this;
|
||||
|
||||
throw Cr.NS_ERROR_NO_INTERFACE;
|
||||
throw Components.Exception("", Cr.NS_ERROR_NO_INTERFACE);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -5312,7 +5312,7 @@ Request.prototype =
|
|||
if (iid.equals(Ci.nsIHttpRequest) || iid.equals(Ci.nsISupports))
|
||||
return this;
|
||||
|
||||
throw Cr.NS_ERROR_NO_INTERFACE;
|
||||
throw Components.Exception("", Cr.NS_ERROR_NO_INTERFACE);
|
||||
},
|
||||
|
||||
|
||||
|
|
|
@ -51,7 +51,7 @@ function assertInvalidHeader(fieldName, fieldValue, headers)
|
|||
}
|
||||
catch (e)
|
||||
{
|
||||
if (e !== Cr.NS_ERROR_INVALID_ARG)
|
||||
if (e.result !== Cr.NS_ERROR_INVALID_ARG)
|
||||
do_throw("Unexpected exception thrown: " + e);
|
||||
}
|
||||
}
|
||||
|
@ -121,7 +121,7 @@ function testGetHeader()
|
|||
}
|
||||
catch (e)
|
||||
{
|
||||
if (e !== Cr.NS_ERROR_INVALID_ARG)
|
||||
if (e.result !== Cr.NS_ERROR_INVALID_ARG)
|
||||
do_throw("headers.getHeader(':') must throw invalid arg");
|
||||
}
|
||||
|
||||
|
@ -132,7 +132,7 @@ function testGetHeader()
|
|||
}
|
||||
catch (e)
|
||||
{
|
||||
if (e !== Cr.NS_ERROR_NOT_AVAILABLE)
|
||||
if (e.result !== Cr.NS_ERROR_NOT_AVAILABLE)
|
||||
do_throw("shouldn't be a header named 'valid' in headers!");
|
||||
}
|
||||
}
|
||||
|
@ -183,7 +183,7 @@ function testHasHeader()
|
|||
}
|
||||
catch (e)
|
||||
{
|
||||
if (e !== Cr.NS_ERROR_INVALID_ARG)
|
||||
if (e.result !== Cr.NS_ERROR_INVALID_ARG)
|
||||
do_throw(".hasHeader for an invalid name should throw");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -232,7 +232,7 @@ function checkPrimariesThrow(id)
|
|||
}
|
||||
catch (e)
|
||||
{
|
||||
threw = e === Cr.NS_ERROR_NOT_INITIALIZED;
|
||||
threw = e.result === Cr.NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
Assert.ok(threw);
|
||||
|
||||
|
@ -243,7 +243,7 @@ function checkPrimariesThrow(id)
|
|||
}
|
||||
catch (e)
|
||||
{
|
||||
threw = e === Cr.NS_ERROR_NOT_INITIALIZED;
|
||||
threw = e.result === Cr.NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
Assert.ok(threw);
|
||||
|
||||
|
@ -254,7 +254,7 @@ function checkPrimariesThrow(id)
|
|||
}
|
||||
catch (e)
|
||||
{
|
||||
threw = e === Cr.NS_ERROR_NOT_INITIALIZED;
|
||||
threw = e.result === Cr.NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
Assert.ok(threw);
|
||||
}
|
||||
|
|
|
@ -577,6 +577,7 @@ function test_newChannel_with_options()
|
|||
checkEqualToIOSChannel(NetUtil.newChannel({
|
||||
uri,
|
||||
loadingPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
|
||||
securityFlags: Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
|
||||
contentPolicyType: Ci.nsIContentPolicy.TYPE_OTHER,
|
||||
}));
|
||||
|
||||
|
@ -598,17 +599,22 @@ function test_newChannel_with_wrong_options()
|
|||
}, /requires a single object argument/);
|
||||
|
||||
Assert.throws(() => {
|
||||
NetUtil.newChannel({});
|
||||
NetUtil.newChannel({ loadUsingSystemPrincipal: true });
|
||||
}, /requires the 'uri' property/);
|
||||
|
||||
Assert.throws(() => {
|
||||
NetUtil.newChannel({ uri });
|
||||
NetUtil.newChannel({ uri, loadingNode: true });
|
||||
}, /requires the 'securityFlags'/);
|
||||
|
||||
Assert.throws(() => {
|
||||
NetUtil.newChannel({ uri, securityFlags: 0 });
|
||||
}, /requires at least one of the 'loadingNode'/);
|
||||
|
||||
Assert.throws(() => {
|
||||
NetUtil.newChannel({
|
||||
uri,
|
||||
loadingPrincipal: systemPrincipal,
|
||||
securityFlags: 0,
|
||||
});
|
||||
}, /requires the 'contentPolicyType'/);
|
||||
|
||||
|
@ -855,7 +861,7 @@ function test_readInputStreamToString_invalid_sequence()
|
|||
test_readInputStreamToString_too_many_bytes,
|
||||
test_readInputStreamToString_with_charset,
|
||||
test_readInputStreamToString_invalid_sequence,
|
||||
].forEach(add_test);
|
||||
].forEach(f => add_test(f));
|
||||
var index = 0;
|
||||
|
||||
function run_test()
|
||||
|
|
|
@ -13,7 +13,7 @@ const special_type = "application/x-our-special-type";
|
|||
test_upload_file,
|
||||
test_load_replace,
|
||||
do_test_finished
|
||||
].forEach(add_test);
|
||||
].forEach(f => add_test(f));
|
||||
|
||||
function getFile(key) {
|
||||
var dirSvc = Cc["@mozilla.org/file/directory_service;1"]
|
||||
|
|
|
@ -598,7 +598,7 @@ function unregisterObserver() {
|
|||
}
|
||||
|
||||
function run_test_real() {
|
||||
tests.forEach(add_test);
|
||||
tests.forEach(f => add_test(f));
|
||||
do_get_profile();
|
||||
|
||||
Services.prefs.setBoolPref("network.predictor.enabled", true);
|
||||
|
|
|
@ -23,7 +23,7 @@ var httpserv = null;
|
|||
// Commented by default as it relies on external ressources
|
||||
//test_ftp_channel,
|
||||
end
|
||||
].forEach(add_test);
|
||||
].forEach(f => add_test(f));
|
||||
|
||||
// Utility functions
|
||||
|
||||
|
|
|
@ -353,8 +353,8 @@ function run_test() {
|
|||
});
|
||||
|
||||
// blocklist load is async so we must use add_test from here
|
||||
add_task(function* () {
|
||||
yield fetch_blocklist();
|
||||
add_task(function () {
|
||||
return fetch_blocklist();
|
||||
});
|
||||
|
||||
add_test(function() {
|
||||
|
|
|
@ -1 +1 @@
|
|||
f0d4789c8916
|
||||
c5dffd6269ea
|
||||
|
|
|
@ -148,7 +148,7 @@ writeItem(PRFileDesc *fd, CK_VOID_PTR pValue,
|
|||
return PR_FAILURE;
|
||||
}
|
||||
bytesWritten = PR_Write(fd, pValue, ulValueLen);
|
||||
if (bytesWritten != ulValueLen) {
|
||||
if (bytesWritten < 0 || (CK_ULONG)bytesWritten != ulValueLen) {
|
||||
lperror(file);
|
||||
return PR_FAILURE;
|
||||
}
|
||||
|
|
|
@ -10,3 +10,4 @@
|
|||
*/
|
||||
|
||||
#error "Do not include this header file."
|
||||
|
||||
|
|
|
@ -46,6 +46,21 @@ bool TlsParser::Read(DataBuffer* val, size_t len) {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool TlsParser::ReadFromMark(DataBuffer* val, size_t len, size_t mark) {
|
||||
auto saved = offset_;
|
||||
offset_ = mark;
|
||||
|
||||
if (remaining() < len) {
|
||||
offset_ = saved;
|
||||
return false;
|
||||
}
|
||||
|
||||
val->Assign(ptr(), len);
|
||||
|
||||
offset_ = saved;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TlsParser::ReadVariable(DataBuffer* val, size_t len_size) {
|
||||
uint32_t len;
|
||||
if (!Read(&len, len_size)) {
|
||||
|
|
|
@ -123,6 +123,7 @@ class TlsParser {
|
|||
bool Read(uint32_t* val, size_t size);
|
||||
// Reads len bytes into dest buffer, overwriting it.
|
||||
bool Read(DataBuffer* dest, size_t len);
|
||||
bool ReadFromMark(DataBuffer* val, size_t len, size_t mark);
|
||||
// Reads bytes into dest buffer, overwriting it. The number of bytes is
|
||||
// determined by reading from len_size bytes from the stream first.
|
||||
bool ReadVariable(DataBuffer* dest, size_t len_size);
|
||||
|
|
|
@ -237,22 +237,23 @@ SECStatus SSLInt_AdvanceReadSeqNum(PRFileDesc *fd, PRUint64 to) {
|
|||
if (!ss) {
|
||||
return SECFailure;
|
||||
}
|
||||
if (to >= RECORD_SEQ_MAX) {
|
||||
if (to > RECORD_SEQ_MAX) {
|
||||
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
||||
return SECFailure;
|
||||
}
|
||||
ssl_GetSpecWriteLock(ss);
|
||||
spec = ss->ssl3.crSpec;
|
||||
spec->seqNum = to;
|
||||
spec->nextSeqNum = to;
|
||||
|
||||
/* For DTLS, we need to fix the record sequence number. For this, we can just
|
||||
* scrub the entire structure on the assumption that the new sequence number
|
||||
* is far enough past the last received sequence number. */
|
||||
if (spec->seqNum <= spec->recvdRecords.right + DTLS_RECVD_RECORDS_WINDOW) {
|
||||
if (spec->nextSeqNum <=
|
||||
spec->recvdRecords.right + DTLS_RECVD_RECORDS_WINDOW) {
|
||||
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
||||
return SECFailure;
|
||||
}
|
||||
dtls_RecordSetRecvd(&spec->recvdRecords, spec->seqNum);
|
||||
dtls_RecordSetRecvd(&spec->recvdRecords, spec->nextSeqNum - 1);
|
||||
|
||||
ssl_ReleaseSpecWriteLock(ss);
|
||||
return SECSuccess;
|
||||
|
@ -270,7 +271,7 @@ SECStatus SSLInt_AdvanceWriteSeqNum(PRFileDesc *fd, PRUint64 to) {
|
|||
return SECFailure;
|
||||
}
|
||||
ssl_GetSpecWriteLock(ss);
|
||||
ss->ssl3.cwSpec->seqNum = to;
|
||||
ss->ssl3.cwSpec->nextSeqNum = to;
|
||||
ssl_ReleaseSpecWriteLock(ss);
|
||||
return SECSuccess;
|
||||
}
|
||||
|
@ -284,7 +285,7 @@ SECStatus SSLInt_AdvanceWriteSeqByAWindow(PRFileDesc *fd, PRInt32 extra) {
|
|||
return SECFailure;
|
||||
}
|
||||
ssl_GetSpecReadLock(ss);
|
||||
to = ss->ssl3.cwSpec->seqNum + DTLS_RECVD_RECORDS_WINDOW + extra;
|
||||
to = ss->ssl3.cwSpec->nextSeqNum + DTLS_RECVD_RECORDS_WINDOW + extra;
|
||||
ssl_ReleaseSpecReadLock(ss);
|
||||
return SSLInt_AdvanceWriteSeqNum(fd, to);
|
||||
}
|
||||
|
|
|
@ -8,9 +8,6 @@
|
|||
#include "sslerr.h"
|
||||
#include "sslproto.h"
|
||||
|
||||
// This is an internal header, used to get TLS_1_3_DRAFT_VERSION.
|
||||
#include "ssl3prot.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "databuffer.h"
|
||||
|
@ -21,7 +18,6 @@
|
|||
|
||||
namespace nss_test {
|
||||
|
||||
static const uint8_t kD13 = TLS_1_3_DRAFT_VERSION;
|
||||
// This is a 1-RTT ClientHello with ECDHE.
|
||||
const static uint8_t kCannedTls13ClientHello[] = {
|
||||
0x01, 0x00, 0x00, 0xcf, 0x03, 0x03, 0x6c, 0xb3, 0x46, 0x81, 0xc8, 0x1a,
|
||||
|
@ -42,16 +38,7 @@ const static uint8_t kCannedTls13ClientHello[] = {
|
|||
0x1e, 0x04, 0x03, 0x05, 0x03, 0x06, 0x03, 0x02, 0x03, 0x08, 0x04, 0x08,
|
||||
0x05, 0x08, 0x06, 0x04, 0x01, 0x05, 0x01, 0x06, 0x01, 0x02, 0x01, 0x04,
|
||||
0x02, 0x05, 0x02, 0x06, 0x02, 0x02, 0x02};
|
||||
|
||||
const static uint8_t kCannedTls13ServerHello[] = {
|
||||
0x03, 0x03, 0x9c, 0xbc, 0x14, 0x9b, 0x0e, 0x2e, 0xfa, 0x0d, 0xf3,
|
||||
0xf0, 0x5c, 0x70, 0x7a, 0xe0, 0xd1, 0x9b, 0x3e, 0x5a, 0x44, 0x6b,
|
||||
0xdf, 0xe5, 0xc2, 0x28, 0x64, 0xf7, 0x00, 0xc1, 0x9c, 0x08, 0x76,
|
||||
0x08, 0x00, 0x13, 0x01, 0x00, 0x00, 0x2e, 0x00, 0x33, 0x00, 0x24,
|
||||
0x00, 0x1d, 0x00, 0x20, 0xc2, 0xcf, 0x23, 0x17, 0x64, 0x23, 0x03,
|
||||
0xf0, 0xfb, 0x45, 0x98, 0x26, 0xd1, 0x65, 0x24, 0xa1, 0x6c, 0xa9,
|
||||
0x80, 0x8f, 0x2c, 0xac, 0x0a, 0xea, 0x53, 0x3a, 0xcb, 0xe3, 0x08,
|
||||
0x84, 0xae, 0x19, 0x00, 0x2b, 0x00, 0x02, 0x7f, kD13};
|
||||
static const size_t kFirstFragmentSize = 20;
|
||||
static const char *k0RttData = "ABCDEF";
|
||||
|
||||
TEST_P(TlsAgentTest, EarlyFinished) {
|
||||
|
@ -74,8 +61,9 @@ TEST_P(TlsAgentTestClient13, CannedHello) {
|
|||
DataBuffer buffer;
|
||||
EnsureInit();
|
||||
DataBuffer server_hello;
|
||||
MakeHandshakeMessage(kTlsHandshakeServerHello, kCannedTls13ServerHello,
|
||||
sizeof(kCannedTls13ServerHello), &server_hello);
|
||||
auto sh = MakeCannedTls13ServerHello();
|
||||
MakeHandshakeMessage(kTlsHandshakeServerHello, sh.data(), sh.len(),
|
||||
&server_hello);
|
||||
MakeRecord(kTlsHandshakeType, SSL_LIBRARY_VERSION_TLS_1_3,
|
||||
server_hello.data(), server_hello.len(), &buffer);
|
||||
ProcessMessage(buffer, TlsAgent::STATE_CONNECTING);
|
||||
|
@ -83,8 +71,9 @@ TEST_P(TlsAgentTestClient13, CannedHello) {
|
|||
|
||||
TEST_P(TlsAgentTestClient13, EncryptedExtensionsInClear) {
|
||||
DataBuffer server_hello;
|
||||
MakeHandshakeMessage(kTlsHandshakeServerHello, kCannedTls13ServerHello,
|
||||
sizeof(kCannedTls13ServerHello), &server_hello);
|
||||
auto sh = MakeCannedTls13ServerHello();
|
||||
MakeHandshakeMessage(kTlsHandshakeServerHello, sh.data(), sh.len(),
|
||||
&server_hello);
|
||||
DataBuffer encrypted_extensions;
|
||||
MakeHandshakeMessage(kTlsHandshakeEncryptedExtensions, nullptr, 0,
|
||||
&encrypted_extensions, 1);
|
||||
|
@ -100,19 +89,21 @@ TEST_P(TlsAgentTestClient13, EncryptedExtensionsInClear) {
|
|||
|
||||
TEST_F(TlsAgentStreamTestClient, EncryptedExtensionsInClearTwoPieces) {
|
||||
DataBuffer server_hello;
|
||||
MakeHandshakeMessage(kTlsHandshakeServerHello, kCannedTls13ServerHello,
|
||||
sizeof(kCannedTls13ServerHello), &server_hello);
|
||||
auto sh = MakeCannedTls13ServerHello();
|
||||
MakeHandshakeMessage(kTlsHandshakeServerHello, sh.data(), sh.len(),
|
||||
&server_hello);
|
||||
DataBuffer encrypted_extensions;
|
||||
MakeHandshakeMessage(kTlsHandshakeEncryptedExtensions, nullptr, 0,
|
||||
&encrypted_extensions, 1);
|
||||
server_hello.Append(encrypted_extensions);
|
||||
DataBuffer buffer;
|
||||
MakeRecord(kTlsHandshakeType, SSL_LIBRARY_VERSION_TLS_1_3,
|
||||
server_hello.data(), 20, &buffer);
|
||||
server_hello.data(), kFirstFragmentSize, &buffer);
|
||||
|
||||
DataBuffer buffer2;
|
||||
MakeRecord(kTlsHandshakeType, SSL_LIBRARY_VERSION_TLS_1_3,
|
||||
server_hello.data() + 20, server_hello.len() - 20, &buffer2);
|
||||
server_hello.data() + kFirstFragmentSize,
|
||||
server_hello.len() - kFirstFragmentSize, &buffer2);
|
||||
|
||||
EnsureInit();
|
||||
agent_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_3,
|
||||
|
@ -124,15 +115,15 @@ TEST_F(TlsAgentStreamTestClient, EncryptedExtensionsInClearTwoPieces) {
|
|||
}
|
||||
|
||||
TEST_F(TlsAgentDgramTestClient, EncryptedExtensionsInClearTwoPieces) {
|
||||
auto sh = MakeCannedTls13ServerHello();
|
||||
DataBuffer server_hello_frag1;
|
||||
MakeHandshakeMessageFragment(
|
||||
kTlsHandshakeServerHello, kCannedTls13ServerHello,
|
||||
sizeof(kCannedTls13ServerHello), &server_hello_frag1, 0, 0, 20);
|
||||
MakeHandshakeMessageFragment(kTlsHandshakeServerHello, sh.data(), sh.len(),
|
||||
&server_hello_frag1, 0, 0, kFirstFragmentSize);
|
||||
DataBuffer server_hello_frag2;
|
||||
MakeHandshakeMessageFragment(
|
||||
kTlsHandshakeServerHello, kCannedTls13ServerHello + 20,
|
||||
sizeof(kCannedTls13ServerHello), &server_hello_frag2, 0, 20,
|
||||
sizeof(kCannedTls13ServerHello) - 20);
|
||||
MakeHandshakeMessageFragment(kTlsHandshakeServerHello,
|
||||
sh.data() + kFirstFragmentSize, sh.len(),
|
||||
&server_hello_frag2, 0, kFirstFragmentSize,
|
||||
sh.len() - kFirstFragmentSize);
|
||||
DataBuffer encrypted_extensions;
|
||||
MakeHandshakeMessage(kTlsHandshakeEncryptedExtensions, nullptr, 0,
|
||||
&encrypted_extensions, 1);
|
||||
|
|
|
@ -166,8 +166,8 @@ class TlsCipherSuiteTestBase : public TlsConnectTestBase {
|
|||
case ssl_calg_seed:
|
||||
break;
|
||||
}
|
||||
EXPECT_TRUE(false) << "No limit for " << csinfo_.cipherSuiteName;
|
||||
return 1ULL < 48;
|
||||
ADD_FAILURE() << "No limit for " << csinfo_.cipherSuiteName;
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t last_safe_write() const {
|
||||
|
@ -246,12 +246,13 @@ TEST_P(TlsCipherSuiteTest, ReadLimit) {
|
|||
|
||||
client_->SendData(10, 10);
|
||||
server_->ReadBytes(); // This should be OK.
|
||||
server_->ReadBytes(); // Read twice to flush any 1,N-1 record splitting.
|
||||
} else {
|
||||
// In TLS 1.3, reading or writing triggers a KeyUpdate. That would mean
|
||||
// that the sequence numbers would reset and we wouldn't hit the limit. So
|
||||
// we move the sequence number to one less than the limit directly and don't
|
||||
// test sending and receiving just before the limit.
|
||||
uint64_t last = record_limit() - 1;
|
||||
// move the sequence number to the limit directly and don't test sending and
|
||||
// receiving just before the limit.
|
||||
uint64_t last = record_limit();
|
||||
EXPECT_EQ(SECSuccess, SSLInt_AdvanceReadSeqNum(server_->ssl_fd(), last));
|
||||
}
|
||||
|
||||
|
|
|
@ -66,7 +66,8 @@ TEST_P(TlsConnectDatagramPre13, DropServerSecondFlightThrice) {
|
|||
Connect();
|
||||
}
|
||||
|
||||
class TlsDropDatagram13 : public TlsConnectDatagram13 {
|
||||
class TlsDropDatagram13 : public TlsConnectDatagram13,
|
||||
public ::testing::WithParamInterface<bool> {
|
||||
public:
|
||||
TlsDropDatagram13()
|
||||
: client_filters_(),
|
||||
|
@ -77,6 +78,9 @@ class TlsDropDatagram13 : public TlsConnectDatagram13 {
|
|||
void SetUp() override {
|
||||
TlsConnectDatagram13::SetUp();
|
||||
ConfigureSessionCache(RESUME_NONE, RESUME_NONE);
|
||||
int short_header = GetParam() ? PR_TRUE : PR_FALSE;
|
||||
client_->SetOption(SSL_ENABLE_DTLS_SHORT_HEADER, short_header);
|
||||
server_->SetOption(SSL_ENABLE_DTLS_SHORT_HEADER, short_header);
|
||||
SetFilters();
|
||||
}
|
||||
|
||||
|
@ -186,7 +190,7 @@ class TlsDropDatagram13 : public TlsConnectDatagram13 {
|
|||
// to the client upon receiving the client Finished.
|
||||
// Dropping complete first and second flights does not produce
|
||||
// ACKs
|
||||
TEST_F(TlsDropDatagram13, DropClientFirstFlightOnce) {
|
||||
TEST_P(TlsDropDatagram13, DropClientFirstFlightOnce) {
|
||||
client_filters_.drop_->Reset({0});
|
||||
StartConnect();
|
||||
client_->Handshake();
|
||||
|
@ -195,7 +199,7 @@ TEST_F(TlsDropDatagram13, DropClientFirstFlightOnce) {
|
|||
CheckAcks(server_filters_, 0, {0x0002000000000000ULL});
|
||||
}
|
||||
|
||||
TEST_F(TlsDropDatagram13, DropServerFirstFlightOnce) {
|
||||
TEST_P(TlsDropDatagram13, DropServerFirstFlightOnce) {
|
||||
server_filters_.drop_->Reset(0xff);
|
||||
StartConnect();
|
||||
client_->Handshake();
|
||||
|
@ -209,7 +213,7 @@ TEST_F(TlsDropDatagram13, DropServerFirstFlightOnce) {
|
|||
// Dropping the server's first record also does not produce
|
||||
// an ACK because the next record is ignored.
|
||||
// TODO(ekr@rtfm.com): We should generate an empty ACK.
|
||||
TEST_F(TlsDropDatagram13, DropServerFirstRecordOnce) {
|
||||
TEST_P(TlsDropDatagram13, DropServerFirstRecordOnce) {
|
||||
server_filters_.drop_->Reset({0});
|
||||
StartConnect();
|
||||
client_->Handshake();
|
||||
|
@ -221,7 +225,7 @@ TEST_F(TlsDropDatagram13, DropServerFirstRecordOnce) {
|
|||
|
||||
// Dropping the second packet of the server's flight should
|
||||
// produce an ACK.
|
||||
TEST_F(TlsDropDatagram13, DropServerSecondRecordOnce) {
|
||||
TEST_P(TlsDropDatagram13, DropServerSecondRecordOnce) {
|
||||
server_filters_.drop_->Reset({1});
|
||||
StartConnect();
|
||||
client_->Handshake();
|
||||
|
@ -235,7 +239,7 @@ TEST_F(TlsDropDatagram13, DropServerSecondRecordOnce) {
|
|||
|
||||
// Drop the server ACK and verify that the client retransmits
|
||||
// the ClientHello.
|
||||
TEST_F(TlsDropDatagram13, DropServerAckOnce) {
|
||||
TEST_P(TlsDropDatagram13, DropServerAckOnce) {
|
||||
StartConnect();
|
||||
client_->Handshake();
|
||||
server_->Handshake();
|
||||
|
@ -263,7 +267,7 @@ TEST_F(TlsDropDatagram13, DropServerAckOnce) {
|
|||
}
|
||||
|
||||
// Drop the client certificate verify.
|
||||
TEST_F(TlsDropDatagram13, DropClientCertVerify) {
|
||||
TEST_P(TlsDropDatagram13, DropClientCertVerify) {
|
||||
StartConnect();
|
||||
client_->SetupClientAuth();
|
||||
server_->RequestClientAuth(true);
|
||||
|
@ -284,7 +288,7 @@ TEST_F(TlsDropDatagram13, DropClientCertVerify) {
|
|||
}
|
||||
|
||||
// Shrink the MTU down so that certs get split and drop the first piece.
|
||||
TEST_F(TlsDropDatagram13, DropFirstHalfOfServerCertificate) {
|
||||
TEST_P(TlsDropDatagram13, DropFirstHalfOfServerCertificate) {
|
||||
server_filters_.drop_->Reset({2});
|
||||
StartConnect();
|
||||
ShrinkPostServerHelloMtu();
|
||||
|
@ -311,7 +315,7 @@ TEST_F(TlsDropDatagram13, DropFirstHalfOfServerCertificate) {
|
|||
}
|
||||
|
||||
// Shrink the MTU down so that certs get split and drop the second piece.
|
||||
TEST_F(TlsDropDatagram13, DropSecondHalfOfServerCertificate) {
|
||||
TEST_P(TlsDropDatagram13, DropSecondHalfOfServerCertificate) {
|
||||
server_filters_.drop_->Reset({3});
|
||||
StartConnect();
|
||||
ShrinkPostServerHelloMtu();
|
||||
|
@ -524,11 +528,11 @@ class TlsFragmentationAndRecoveryTest : public TlsDropDatagram13 {
|
|||
size_t cert_len_;
|
||||
};
|
||||
|
||||
TEST_F(TlsFragmentationAndRecoveryTest, DropFirstHalf) { RunTest(0); }
|
||||
TEST_P(TlsFragmentationAndRecoveryTest, DropFirstHalf) { RunTest(0); }
|
||||
|
||||
TEST_F(TlsFragmentationAndRecoveryTest, DropSecondHalf) { RunTest(1); }
|
||||
TEST_P(TlsFragmentationAndRecoveryTest, DropSecondHalf) { RunTest(1); }
|
||||
|
||||
TEST_F(TlsDropDatagram13, NoDropsDuringZeroRtt) {
|
||||
TEST_P(TlsDropDatagram13, NoDropsDuringZeroRtt) {
|
||||
SetupForZeroRtt();
|
||||
SetFilters();
|
||||
std::cerr << "Starting second handshake" << std::endl;
|
||||
|
@ -546,7 +550,7 @@ TEST_F(TlsDropDatagram13, NoDropsDuringZeroRtt) {
|
|||
0x0002000000000000ULL}); // Finished
|
||||
}
|
||||
|
||||
TEST_F(TlsDropDatagram13, DropEEDuringZeroRtt) {
|
||||
TEST_P(TlsDropDatagram13, DropEEDuringZeroRtt) {
|
||||
SetupForZeroRtt();
|
||||
SetFilters();
|
||||
std::cerr << "Starting second handshake" << std::endl;
|
||||
|
@ -591,7 +595,7 @@ class TlsReorderDatagram13 : public TlsDropDatagram13 {
|
|||
|
||||
// Reorder the server records so that EE comes at the end
|
||||
// of the flight and will still produce an ACK.
|
||||
TEST_F(TlsDropDatagram13, ReorderServerEE) {
|
||||
TEST_P(TlsDropDatagram13, ReorderServerEE) {
|
||||
server_filters_.drop_->Reset({1});
|
||||
StartConnect();
|
||||
client_->Handshake();
|
||||
|
@ -647,7 +651,7 @@ class TlsSendCipherSpecCapturer {
|
|||
std::vector<std::shared_ptr<TlsCipherSpec>> send_cipher_specs_;
|
||||
};
|
||||
|
||||
TEST_F(TlsDropDatagram13, SendOutOfOrderAppWithHandshakeKey) {
|
||||
TEST_P(TlsDropDatagram13, SendOutOfOrderAppWithHandshakeKey) {
|
||||
StartConnect();
|
||||
TlsSendCipherSpecCapturer capturer(client_);
|
||||
client_->Handshake();
|
||||
|
@ -662,9 +666,9 @@ TEST_F(TlsDropDatagram13, SendOutOfOrderAppWithHandshakeKey) {
|
|||
auto spec = capturer.spec(0);
|
||||
ASSERT_NE(nullptr, spec.get());
|
||||
ASSERT_EQ(2, spec->epoch());
|
||||
ASSERT_TRUE(client_->SendEncryptedRecord(
|
||||
spec, SSL_LIBRARY_VERSION_DTLS_1_2_WIRE, 0x0002000000000002,
|
||||
kTlsApplicationDataType, DataBuffer(buf, sizeof(buf))));
|
||||
ASSERT_TRUE(client_->SendEncryptedRecord(spec, 0x0002000000000002,
|
||||
kTlsApplicationDataType,
|
||||
DataBuffer(buf, sizeof(buf))));
|
||||
|
||||
// Now have the server consume the bogus message.
|
||||
server_->ExpectSendAlert(illegal_parameter, kTlsAlertFatal);
|
||||
|
@ -673,7 +677,7 @@ TEST_F(TlsDropDatagram13, SendOutOfOrderAppWithHandshakeKey) {
|
|||
EXPECT_EQ(SSL_ERROR_RX_UNKNOWN_RECORD_TYPE, PORT_GetError());
|
||||
}
|
||||
|
||||
TEST_F(TlsDropDatagram13, SendOutOfOrderHsNonsenseWithHandshakeKey) {
|
||||
TEST_P(TlsDropDatagram13, SendOutOfOrderHsNonsenseWithHandshakeKey) {
|
||||
StartConnect();
|
||||
TlsSendCipherSpecCapturer capturer(client_);
|
||||
client_->Handshake();
|
||||
|
@ -688,9 +692,9 @@ TEST_F(TlsDropDatagram13, SendOutOfOrderHsNonsenseWithHandshakeKey) {
|
|||
auto spec = capturer.spec(0);
|
||||
ASSERT_NE(nullptr, spec.get());
|
||||
ASSERT_EQ(2, spec->epoch());
|
||||
ASSERT_TRUE(client_->SendEncryptedRecord(
|
||||
spec, SSL_LIBRARY_VERSION_DTLS_1_2_WIRE, 0x0002000000000002,
|
||||
kTlsHandshakeType, DataBuffer(buf, sizeof(buf))));
|
||||
ASSERT_TRUE(client_->SendEncryptedRecord(spec, 0x0002000000000002,
|
||||
kTlsHandshakeType,
|
||||
DataBuffer(buf, sizeof(buf))));
|
||||
server_->Handshake();
|
||||
EXPECT_EQ(2UL, server_filters_.ack_->count());
|
||||
// The server acknowledges client Finished twice.
|
||||
|
@ -700,7 +704,7 @@ TEST_F(TlsDropDatagram13, SendOutOfOrderHsNonsenseWithHandshakeKey) {
|
|||
|
||||
// Shrink the MTU down so that certs get split and then swap the first and
|
||||
// second pieces of the server certificate.
|
||||
TEST_F(TlsReorderDatagram13, ReorderServerCertificate) {
|
||||
TEST_P(TlsReorderDatagram13, ReorderServerCertificate) {
|
||||
StartConnect();
|
||||
ShrinkPostServerHelloMtu();
|
||||
client_->Handshake();
|
||||
|
@ -722,7 +726,7 @@ TEST_F(TlsReorderDatagram13, ReorderServerCertificate) {
|
|||
CheckAcks(server_filters_, 0, {0x0002000000000000ULL});
|
||||
}
|
||||
|
||||
TEST_F(TlsReorderDatagram13, DataAfterEOEDDuringZeroRtt) {
|
||||
TEST_P(TlsReorderDatagram13, DataAfterEOEDDuringZeroRtt) {
|
||||
SetupForZeroRtt();
|
||||
SetFilters();
|
||||
std::cerr << "Starting second handshake" << std::endl;
|
||||
|
@ -761,7 +765,7 @@ TEST_F(TlsReorderDatagram13, DataAfterEOEDDuringZeroRtt) {
|
|||
EXPECT_EQ(PR_WOULD_BLOCK_ERROR, PORT_GetError());
|
||||
}
|
||||
|
||||
TEST_F(TlsReorderDatagram13, DataAfterFinDuringZeroRtt) {
|
||||
TEST_P(TlsReorderDatagram13, DataAfterFinDuringZeroRtt) {
|
||||
SetupForZeroRtt();
|
||||
SetFilters();
|
||||
std::cerr << "Starting second handshake" << std::endl;
|
||||
|
@ -812,12 +816,17 @@ static void GetCipherAndLimit(uint16_t version, uint16_t* cipher,
|
|||
*cipher = TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256;
|
||||
*limit = (1ULL << 48) - 1;
|
||||
} else {
|
||||
// This test probably isn't especially useful for TLS 1.3, which has a much
|
||||
// shorter sequence number encoding. That space can probably be searched in
|
||||
// a reasonable amount of time.
|
||||
*cipher = TLS_CHACHA20_POLY1305_SHA256;
|
||||
*limit = (1ULL << 48) - 1;
|
||||
// Assume that we are starting with an expected sequence number of 0.
|
||||
*limit = (1ULL << 29) - 1;
|
||||
}
|
||||
}
|
||||
|
||||
// This simulates a huge number of drops on one side.
|
||||
// See Bug 12965514 where a large gap was handled very inefficiently.
|
||||
TEST_P(TlsConnectDatagram, MissLotsOfPackets) {
|
||||
uint16_t cipher;
|
||||
uint64_t limit;
|
||||
|
@ -834,6 +843,17 @@ TEST_P(TlsConnectDatagram, MissLotsOfPackets) {
|
|||
SendReceive();
|
||||
}
|
||||
|
||||
// Send a sequence number of 0xfffffffd and it should be interpreted as that
|
||||
// (and not -3 or UINT64_MAX - 2).
|
||||
TEST_F(TlsConnectDatagram13, UnderflowSequenceNumber) {
|
||||
Connect();
|
||||
// This is only valid if short headers are disabled.
|
||||
client_->SetOption(SSL_ENABLE_DTLS_SHORT_HEADER, PR_FALSE);
|
||||
EXPECT_EQ(SECSuccess,
|
||||
SSLInt_AdvanceWriteSeqNum(client_->ssl_fd(), (1ULL << 30) - 3));
|
||||
SendReceive();
|
||||
}
|
||||
|
||||
class TlsConnectDatagram12Plus : public TlsConnectDatagram {
|
||||
public:
|
||||
TlsConnectDatagram12Plus() : TlsConnectDatagram() {}
|
||||
|
@ -865,5 +885,11 @@ INSTANTIATE_TEST_CASE_P(Datagram12Plus, TlsConnectDatagram12Plus,
|
|||
TlsConnectTestBase::kTlsV12Plus);
|
||||
INSTANTIATE_TEST_CASE_P(DatagramPre13, TlsConnectDatagramPre13,
|
||||
TlsConnectTestBase::kTlsV11V12);
|
||||
INSTANTIATE_TEST_CASE_P(DatagramDrop13, TlsDropDatagram13,
|
||||
::testing::Values(true, false));
|
||||
INSTANTIATE_TEST_CASE_P(DatagramReorder13, TlsReorderDatagram13,
|
||||
::testing::Values(true, false));
|
||||
INSTANTIATE_TEST_CASE_P(DatagramFragment13, TlsFragmentationAndRecoveryTest,
|
||||
::testing::Values(true, false));
|
||||
|
||||
} // namespace nss_test
|
||||
|
|
|
@ -20,14 +20,16 @@ namespace nss_test {
|
|||
// This class cuts every unencrypted handshake record into two parts.
|
||||
class RecordFragmenter : public PacketFilter {
|
||||
public:
|
||||
RecordFragmenter() : sequence_number_(0), splitting_(true) {}
|
||||
RecordFragmenter(bool is_dtls13)
|
||||
: is_dtls13_(is_dtls13), sequence_number_(0), splitting_(true) {}
|
||||
|
||||
private:
|
||||
class HandshakeSplitter {
|
||||
public:
|
||||
HandshakeSplitter(const DataBuffer& input, DataBuffer* output,
|
||||
uint64_t* sequence_number)
|
||||
: input_(input),
|
||||
HandshakeSplitter(bool is_dtls13, const DataBuffer& input,
|
||||
DataBuffer* output, uint64_t* sequence_number)
|
||||
: is_dtls13_(is_dtls13),
|
||||
input_(input),
|
||||
output_(output),
|
||||
cursor_(0),
|
||||
sequence_number_(sequence_number) {}
|
||||
|
@ -35,9 +37,9 @@ class RecordFragmenter : public PacketFilter {
|
|||
private:
|
||||
void WriteRecord(TlsRecordHeader& record_header,
|
||||
DataBuffer& record_fragment) {
|
||||
TlsRecordHeader fragment_header(record_header.version(),
|
||||
record_header.content_type(),
|
||||
*sequence_number_);
|
||||
TlsRecordHeader fragment_header(
|
||||
record_header.variant(), record_header.version(),
|
||||
record_header.content_type(), *sequence_number_);
|
||||
++*sequence_number_;
|
||||
if (::g_ssl_gtest_verbose) {
|
||||
std::cerr << "Fragment: " << fragment_header << ' ' << record_fragment
|
||||
|
@ -88,7 +90,7 @@ class RecordFragmenter : public PacketFilter {
|
|||
while (parser.remaining()) {
|
||||
TlsRecordHeader header;
|
||||
DataBuffer record;
|
||||
if (!header.Parse(0, &parser, &record)) {
|
||||
if (!header.Parse(is_dtls13_, 0, &parser, &record)) {
|
||||
ADD_FAILURE() << "bad record header";
|
||||
return false;
|
||||
}
|
||||
|
@ -118,6 +120,7 @@ class RecordFragmenter : public PacketFilter {
|
|||
}
|
||||
|
||||
private:
|
||||
bool is_dtls13_;
|
||||
const DataBuffer& input_;
|
||||
DataBuffer* output_;
|
||||
size_t cursor_;
|
||||
|
@ -132,7 +135,7 @@ class RecordFragmenter : public PacketFilter {
|
|||
}
|
||||
|
||||
output->Allocate(input.len());
|
||||
HandshakeSplitter splitter(input, output, &sequence_number_);
|
||||
HandshakeSplitter splitter(is_dtls13_, input, output, &sequence_number_);
|
||||
if (!splitter.Split()) {
|
||||
// If splitting fails, we obviously reached encrypted packets.
|
||||
// Stop splitting from that point onward.
|
||||
|
@ -144,18 +147,21 @@ class RecordFragmenter : public PacketFilter {
|
|||
}
|
||||
|
||||
private:
|
||||
bool is_dtls13_;
|
||||
uint64_t sequence_number_;
|
||||
bool splitting_;
|
||||
};
|
||||
|
||||
TEST_P(TlsConnectDatagram, FragmentClientPackets) {
|
||||
client_->SetFilter(std::make_shared<RecordFragmenter>());
|
||||
bool is_dtls13 = version_ >= SSL_LIBRARY_VERSION_TLS_1_3;
|
||||
client_->SetFilter(std::make_shared<RecordFragmenter>(is_dtls13));
|
||||
Connect();
|
||||
SendReceive();
|
||||
}
|
||||
|
||||
TEST_P(TlsConnectDatagram, FragmentServerPackets) {
|
||||
server_->SetFilter(std::make_shared<RecordFragmenter>());
|
||||
bool is_dtls13 = version_ >= SSL_LIBRARY_VERSION_TLS_1_3;
|
||||
server_->SetFilter(std::make_shared<RecordFragmenter>(is_dtls13));
|
||||
Connect();
|
||||
SendReceive();
|
||||
}
|
||||
|
|
|
@ -81,8 +81,9 @@ class CorrectMessageSeqAfterHrrFilter : public TlsRecordFilter {
|
|||
}
|
||||
|
||||
DataBuffer buffer(record);
|
||||
TlsRecordHeader new_header = {header.version(), header.content_type(),
|
||||
header.sequence_number() + 1};
|
||||
TlsRecordHeader new_header(header.variant(), header.version(),
|
||||
header.content_type(),
|
||||
header.sequence_number() + 1);
|
||||
|
||||
// Correct message_seq.
|
||||
buffer.Write(4, 1U, 2);
|
||||
|
@ -567,6 +568,28 @@ void TriggerHelloRetryRequest(std::shared_ptr<TlsAgent>& client,
|
|||
client->Handshake();
|
||||
server->Handshake();
|
||||
EXPECT_EQ(1U, cb_called);
|
||||
// Stop the callback from being called in future handshakes.
|
||||
EXPECT_EQ(SECSuccess,
|
||||
SSL_HelloRetryRequestCallback(server->ssl_fd(), nullptr, nullptr));
|
||||
}
|
||||
|
||||
TEST_P(TlsConnectTls13, VersionNumbersAfterRetry) {
|
||||
ConfigureSelfEncrypt();
|
||||
EnsureTlsSetup();
|
||||
auto r = MakeTlsFilter<TlsRecordRecorder>(client_);
|
||||
TriggerHelloRetryRequest(client_, server_);
|
||||
Handshake();
|
||||
ASSERT_GT(r->count(), 1UL);
|
||||
auto ch1 = r->record(0);
|
||||
if (ch1.header.is_dtls()) {
|
||||
ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_1, ch1.header.version());
|
||||
} else {
|
||||
ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_0, ch1.header.version());
|
||||
}
|
||||
auto ch2 = r->record(1);
|
||||
ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_2, ch2.header.version());
|
||||
|
||||
CheckConnected();
|
||||
}
|
||||
|
||||
TEST_P(TlsConnectTls13, RetryStateless) {
|
||||
|
@ -577,6 +600,7 @@ TEST_P(TlsConnectTls13, RetryStateless) {
|
|||
MakeNewServer();
|
||||
|
||||
Handshake();
|
||||
CheckConnected();
|
||||
SendReceive();
|
||||
}
|
||||
|
||||
|
@ -907,7 +931,10 @@ class HelloRetryRequestAgentTest : public TlsAgentTestClient {
|
|||
|
||||
hrr_data.Allocate(len + 6);
|
||||
size_t i = 0;
|
||||
i = hrr_data.Write(i, 0x0303, 2);
|
||||
i = hrr_data.Write(i, variant_ == ssl_variant_datagram
|
||||
? SSL_LIBRARY_VERSION_DTLS_1_2_WIRE
|
||||
: SSL_LIBRARY_VERSION_TLS_1_2,
|
||||
2);
|
||||
i = hrr_data.Write(i, ssl_hello_retry_random,
|
||||
sizeof(ssl_hello_retry_random));
|
||||
i = hrr_data.Write(i, static_cast<uint32_t>(0), 1); // session_id
|
||||
|
|
|
@ -383,7 +383,8 @@ class TlsPreCCSHeaderInjector : public TlsRecordFilter {
|
|||
std::cerr << "Injecting Finished header before CCS\n";
|
||||
const uint8_t hhdr[] = {kTlsHandshakeFinished, 0x00, 0x00, 0x0c};
|
||||
DataBuffer hhdr_buf(hhdr, sizeof(hhdr));
|
||||
TlsRecordHeader nhdr(record_header.version(), kTlsHandshakeType, 0);
|
||||
TlsRecordHeader nhdr(record_header.variant(), record_header.version(),
|
||||
kTlsHandshakeType, 0);
|
||||
*offset = nhdr.Write(output, *offset, hhdr_buf);
|
||||
*offset = record_header.Write(output, *offset, input);
|
||||
return CHANGE;
|
||||
|
|
|
@ -168,6 +168,29 @@ TEST_F(TlsConnectStreamTls13, TooLargeRecord) {
|
|||
EXPECT_EQ(SSL_ERROR_RECORD_OVERFLOW_ALERT, PORT_GetError());
|
||||
}
|
||||
|
||||
class ShortHeaderChecker : public PacketFilter {
|
||||
public:
|
||||
PacketFilter::Action Filter(const DataBuffer& input, DataBuffer* output) {
|
||||
// The first octet should be 0b001xxxxx.
|
||||
EXPECT_EQ(1, input.data()[0] >> 5);
|
||||
return KEEP;
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(TlsConnectDatagram13, ShortHeadersClient) {
|
||||
Connect();
|
||||
client_->SetOption(SSL_ENABLE_DTLS_SHORT_HEADER, PR_TRUE);
|
||||
client_->SetFilter(std::make_shared<ShortHeaderChecker>());
|
||||
SendReceive();
|
||||
}
|
||||
|
||||
TEST_F(TlsConnectDatagram13, ShortHeadersServer) {
|
||||
Connect();
|
||||
server_->SetOption(SSL_ENABLE_DTLS_SHORT_HEADER, PR_TRUE);
|
||||
server_->SetFilter(std::make_shared<ShortHeaderChecker>());
|
||||
SendReceive();
|
||||
}
|
||||
|
||||
const static size_t kContentSizesArr[] = {
|
||||
1, kMacSize - 1, kMacSize, 30, 31, 32, 36, 256, 257, 287, 288};
|
||||
|
||||
|
|
|
@ -44,6 +44,16 @@ const std::string TlsAgent::kServerEcdhRsa = "ecdh_rsa";
|
|||
const std::string TlsAgent::kServerEcdhEcdsa = "ecdh_ecdsa";
|
||||
const std::string TlsAgent::kServerDsa = "dsa";
|
||||
|
||||
static const uint8_t kCannedTls13ServerHello[] = {
|
||||
0x03, 0x03, 0x9c, 0xbc, 0x14, 0x9b, 0x0e, 0x2e, 0xfa, 0x0d, 0xf3,
|
||||
0xf0, 0x5c, 0x70, 0x7a, 0xe0, 0xd1, 0x9b, 0x3e, 0x5a, 0x44, 0x6b,
|
||||
0xdf, 0xe5, 0xc2, 0x28, 0x64, 0xf7, 0x00, 0xc1, 0x9c, 0x08, 0x76,
|
||||
0x08, 0x00, 0x13, 0x01, 0x00, 0x00, 0x2e, 0x00, 0x33, 0x00, 0x24,
|
||||
0x00, 0x1d, 0x00, 0x20, 0xc2, 0xcf, 0x23, 0x17, 0x64, 0x23, 0x03,
|
||||
0xf0, 0xfb, 0x45, 0x98, 0x26, 0xd1, 0x65, 0x24, 0xa1, 0x6c, 0xa9,
|
||||
0x80, 0x8f, 0x2c, 0xac, 0x0a, 0xea, 0x53, 0x3a, 0xcb, 0xe3, 0x08,
|
||||
0x84, 0xae, 0x19, 0x00, 0x2b, 0x00, 0x02, 0x7f, kD13};
|
||||
|
||||
TlsAgent::TlsAgent(const std::string& nm, Role rl, SSLProtocolVariant var)
|
||||
: name_(nm),
|
||||
variant_(var),
|
||||
|
@ -947,12 +957,13 @@ void TlsAgent::SendBuffer(const DataBuffer& buf) {
|
|||
}
|
||||
|
||||
bool TlsAgent::SendEncryptedRecord(const std::shared_ptr<TlsCipherSpec>& spec,
|
||||
uint16_t wireVersion, uint64_t seq,
|
||||
uint8_t ct, const DataBuffer& buf) {
|
||||
LOGV("Writing " << buf.len() << " bytes");
|
||||
// Ensure we are a TLS 1.3 cipher agent.
|
||||
uint64_t seq, uint8_t ct,
|
||||
const DataBuffer& buf) {
|
||||
LOGV("Encrypting " << buf.len() << " bytes");
|
||||
// Ensure that we are doing TLS 1.3.
|
||||
EXPECT_GE(expected_version_, SSL_LIBRARY_VERSION_TLS_1_3);
|
||||
TlsRecordHeader header(wireVersion, kTlsApplicationDataType, seq);
|
||||
TlsRecordHeader header(variant_, expected_version_, kTlsApplicationDataType,
|
||||
seq);
|
||||
DataBuffer padded = buf;
|
||||
padded.Write(padded.len(), ct, 1);
|
||||
DataBuffer ciphertext;
|
||||
|
@ -1074,15 +1085,20 @@ void TlsAgentTestBase::ProcessMessage(const DataBuffer& buffer,
|
|||
void TlsAgentTestBase::MakeRecord(SSLProtocolVariant variant, uint8_t type,
|
||||
uint16_t version, const uint8_t* buf,
|
||||
size_t len, DataBuffer* out,
|
||||
uint64_t seq_num) {
|
||||
uint64_t sequence_number) {
|
||||
size_t index = 0;
|
||||
index = out->Write(index, type, 1);
|
||||
if (variant == ssl_variant_stream) {
|
||||
index = out->Write(index, version, 2);
|
||||
} else if (version >= SSL_LIBRARY_VERSION_TLS_1_3 &&
|
||||
type == kTlsApplicationDataType) {
|
||||
uint32_t epoch = (sequence_number >> 48) & 0x3;
|
||||
uint32_t seqno = sequence_number & ((1ULL << 30) - 1);
|
||||
index = out->Write(index, (epoch << 30) | seqno, 4);
|
||||
} else {
|
||||
index = out->Write(index, TlsVersionToDtlsVersion(version), 2);
|
||||
index = out->Write(index, seq_num >> 32, 4);
|
||||
index = out->Write(index, seq_num & PR_UINT32_MAX, 4);
|
||||
index = out->Write(index, sequence_number >> 32, 4);
|
||||
index = out->Write(index, sequence_number & PR_UINT32_MAX, 4);
|
||||
}
|
||||
index = out->Write(index, len, 2);
|
||||
out->Write(index, buf, len);
|
||||
|
@ -1140,4 +1156,12 @@ void TlsAgentTestBase::MakeTrivialHandshakeRecord(uint8_t hs_type,
|
|||
}
|
||||
}
|
||||
|
||||
DataBuffer TlsAgentTestBase::MakeCannedTls13ServerHello() {
|
||||
DataBuffer sh(kCannedTls13ServerHello, sizeof(kCannedTls13ServerHello));
|
||||
if (variant_ == ssl_variant_datagram) {
|
||||
sh.Write(0, SSL_LIBRARY_VERSION_DTLS_1_2_WIRE, 2);
|
||||
}
|
||||
return sh;
|
||||
}
|
||||
|
||||
} // namespace nss_test
|
||||
|
|
|
@ -10,6 +10,9 @@
|
|||
#include "prio.h"
|
||||
#include "ssl.h"
|
||||
|
||||
// This is an internal header, used to get TLS_1_3_DRAFT_VERSION.
|
||||
#include "ssl3prot.h"
|
||||
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
|
||||
|
@ -57,6 +60,8 @@ typedef std::function<int32_t(TlsAgent* agent, const SECItem* srvNameArr,
|
|||
PRUint32 srvNameArrSize)>
|
||||
SniCallbackFunction;
|
||||
|
||||
static const uint8_t kD13 = TLS_1_3_DRAFT_VERSION;
|
||||
|
||||
class TlsAgent : public PollTarget {
|
||||
public:
|
||||
enum Role { CLIENT, SERVER };
|
||||
|
@ -143,8 +148,7 @@ class TlsAgent : public PollTarget {
|
|||
void SendData(size_t bytes, size_t blocksize = 1024);
|
||||
void SendBuffer(const DataBuffer& buf);
|
||||
bool SendEncryptedRecord(const std::shared_ptr<TlsCipherSpec>& spec,
|
||||
uint16_t wireVersion, uint64_t seq, uint8_t ct,
|
||||
const DataBuffer& buf);
|
||||
uint64_t seq, uint8_t ct, const DataBuffer& buf);
|
||||
// Send data directly to the underlying socket, skipping the TLS layer.
|
||||
void SendDirect(const DataBuffer& buf);
|
||||
void SendRecordDirect(const TlsRecord& record);
|
||||
|
@ -443,6 +447,7 @@ class TlsAgentTestBase : public ::testing::Test {
|
|||
size_t hs_len, DataBuffer* out,
|
||||
uint64_t seq_num, uint32_t fragment_offset,
|
||||
uint32_t fragment_length) const;
|
||||
DataBuffer MakeCannedTls13ServerHello();
|
||||
static void MakeTrivialHandshakeRecord(uint8_t hs_type, size_t hs_len,
|
||||
DataBuffer* out);
|
||||
static inline TlsAgent::Role ToRole(const std::string& str) {
|
||||
|
|
|
@ -30,11 +30,9 @@ void TlsVersioned::WriteStream(std::ostream& stream) const {
|
|||
case SSL_LIBRARY_VERSION_TLS_1_0:
|
||||
stream << "1.0";
|
||||
break;
|
||||
case SSL_LIBRARY_VERSION_DTLS_1_0_WIRE:
|
||||
case SSL_LIBRARY_VERSION_TLS_1_1:
|
||||
stream << (is_dtls() ? "1.0" : "1.1");
|
||||
break;
|
||||
case SSL_LIBRARY_VERSION_DTLS_1_2_WIRE:
|
||||
case SSL_LIBRARY_VERSION_TLS_1_2:
|
||||
stream << "1.2";
|
||||
break;
|
||||
|
@ -67,8 +65,14 @@ void TlsRecordFilter::CipherSpecChanged(void* arg, PRBool sending,
|
|||
return;
|
||||
}
|
||||
|
||||
self->in_sequence_number_ = 0;
|
||||
self->out_sequence_number_ = 0;
|
||||
uint64_t seq_no;
|
||||
if (self->agent()->variant() == ssl_variant_datagram) {
|
||||
seq_no = static_cast<uint64_t>(SSLInt_CipherSpecToEpoch(newSpec)) << 48;
|
||||
} else {
|
||||
seq_no = 0;
|
||||
}
|
||||
self->in_sequence_number_ = seq_no;
|
||||
self->out_sequence_number_ = seq_no;
|
||||
self->dropped_record_ = false;
|
||||
self->cipher_spec_.reset(new TlsCipherSpec());
|
||||
bool ret = self->cipher_spec_->Init(
|
||||
|
@ -77,33 +81,59 @@ void TlsRecordFilter::CipherSpecChanged(void* arg, PRBool sending,
|
|||
EXPECT_EQ(true, ret);
|
||||
}
|
||||
|
||||
bool TlsRecordFilter::is_dtls13() const {
|
||||
if (agent()->variant() != ssl_variant_datagram) {
|
||||
return false;
|
||||
}
|
||||
if (agent()->state() == TlsAgent::STATE_CONNECTED) {
|
||||
return agent()->version() >= SSL_LIBRARY_VERSION_TLS_1_3;
|
||||
}
|
||||
SSLPreliminaryChannelInfo info;
|
||||
EXPECT_EQ(SECSuccess, SSL_GetPreliminaryChannelInfo(agent()->ssl_fd(), &info,
|
||||
sizeof(info)));
|
||||
return (info.protocolVersion >= SSL_LIBRARY_VERSION_TLS_1_3) ||
|
||||
info.canSendEarlyData;
|
||||
}
|
||||
|
||||
PacketFilter::Action TlsRecordFilter::Filter(const DataBuffer& input,
|
||||
DataBuffer* output) {
|
||||
// Disable during shutdown.
|
||||
if (!agent()) {
|
||||
return KEEP;
|
||||
}
|
||||
|
||||
bool changed = false;
|
||||
size_t offset = 0U;
|
||||
output->Allocate(input.len());
|
||||
|
||||
output->Allocate(input.len());
|
||||
TlsParser parser(input);
|
||||
|
||||
while (parser.remaining()) {
|
||||
TlsRecordHeader header;
|
||||
DataBuffer record;
|
||||
|
||||
if (!header.Parse(in_sequence_number_, &parser, &record)) {
|
||||
if (!header.Parse(is_dtls13(), in_sequence_number_, &parser, &record)) {
|
||||
ADD_FAILURE() << "not a valid record";
|
||||
return KEEP;
|
||||
}
|
||||
|
||||
// Track the sequence number, which is necessary for stream mode (the
|
||||
// sequence number is in the header for datagram).
|
||||
// Track the sequence number, which is necessary for stream mode when
|
||||
// decrypting and for TLS 1.3 datagram to recover the sequence number.
|
||||
//
|
||||
// This isn't perfectly robust. If there is a change from an active cipher
|
||||
// We reset the counter when the cipher spec changes, but that notification
|
||||
// appears before a record is sent. If multiple records are sent with
|
||||
// different cipher specs, this would fail. This filters out cleartext
|
||||
// records, so we don't get confused by handshake messages that are sent at
|
||||
// the same time as encrypted records. Sequence numbers are therefore
|
||||
// likely to be incorrect for cleartext records.
|
||||
//
|
||||
// This isn't perfectly robust: if there is a change from an active cipher
|
||||
// spec to another active cipher spec (KeyUpdate for instance) AND writes
|
||||
// are consolidated across that change AND packets were dropped from the
|
||||
// older epoch, we will not correctly re-encrypt records in the old epoch to
|
||||
// update their sequence numbers.
|
||||
if (cipher_spec_ && header.content_type() == kTlsApplicationDataType) {
|
||||
++in_sequence_number_;
|
||||
// are consolidated across that change, this code could use the wrong
|
||||
// sequence numbers when re-encrypting records with the old keys.
|
||||
if (header.content_type() == kTlsApplicationDataType) {
|
||||
in_sequence_number_ =
|
||||
(std::max)(in_sequence_number_, header.sequence_number() + 1);
|
||||
}
|
||||
|
||||
if (FilterRecord(header, record, &offset, output) != KEEP) {
|
||||
|
@ -131,11 +161,14 @@ PacketFilter::Action TlsRecordFilter::FilterRecord(
|
|||
DataBuffer plaintext;
|
||||
|
||||
if (!Unprotect(header, record, &inner_content_type, &plaintext)) {
|
||||
if (g_ssl_gtest_verbose) {
|
||||
std::cerr << "unprotect failed: " << header << ":" << record << std::endl;
|
||||
}
|
||||
return KEEP;
|
||||
}
|
||||
|
||||
TlsRecordHeader real_header = {header.version(), inner_content_type,
|
||||
header.sequence_number()};
|
||||
TlsRecordHeader real_header(header.variant(), header.version(),
|
||||
inner_content_type, header.sequence_number());
|
||||
|
||||
PacketFilter::Action action = FilterRecord(real_header, plaintext, &filtered);
|
||||
// In stream mode, even if something doesn't change we need to re-encrypt if
|
||||
|
@ -166,8 +199,8 @@ PacketFilter::Action TlsRecordFilter::FilterRecord(
|
|||
} else {
|
||||
seq_num = out_sequence_number_++;
|
||||
}
|
||||
TlsRecordHeader out_header = {header.version(), header.content_type(),
|
||||
seq_num};
|
||||
TlsRecordHeader out_header(header.variant(), header.version(),
|
||||
header.content_type(), seq_num);
|
||||
|
||||
DataBuffer ciphertext;
|
||||
bool rv = Protect(out_header, inner_content_type, filtered, &ciphertext);
|
||||
|
@ -179,20 +212,119 @@ PacketFilter::Action TlsRecordFilter::FilterRecord(
|
|||
return CHANGE;
|
||||
}
|
||||
|
||||
bool TlsRecordHeader::Parse(uint64_t seqno, TlsParser* parser,
|
||||
size_t TlsRecordHeader::header_length() const {
|
||||
// If we have a header, return it's length.
|
||||
if (header_.len()) {
|
||||
return header_.len();
|
||||
}
|
||||
|
||||
// Otherwise make a dummy header and return the length.
|
||||
DataBuffer buf;
|
||||
return WriteHeader(&buf, 0, 0);
|
||||
}
|
||||
|
||||
uint64_t TlsRecordHeader::RecoverSequenceNumber(uint64_t expected,
|
||||
uint32_t partial,
|
||||
size_t partial_bits) {
|
||||
EXPECT_GE(32U, partial_bits);
|
||||
uint64_t mask = (1 << partial_bits) - 1;
|
||||
// First we determine the highest possible value. This is half the
|
||||
// expressible range above the expected value.
|
||||
uint64_t cap = expected + (1ULL << (partial_bits - 1));
|
||||
// Add the partial piece in. e.g., xxxx789a and 1234 becomes xxxx1234.
|
||||
uint64_t seq_no = (cap & ~mask) | partial;
|
||||
// If the partial value is higher than the same partial piece from the cap,
|
||||
// then the real value has to be lower. e.g., xxxx1234 can't become xxxx5678.
|
||||
if (partial > (cap & mask)) {
|
||||
seq_no -= 1ULL << partial_bits;
|
||||
}
|
||||
return seq_no;
|
||||
}
|
||||
|
||||
// Determine the full epoch and sequence number from an expected and raw value.
|
||||
// The expected and output values are packed as they are in DTLS 1.2 and
|
||||
// earlier: with 16 bits of epoch and 48 bits of sequence number.
|
||||
uint64_t TlsRecordHeader::ParseSequenceNumber(uint64_t expected, uint32_t raw,
|
||||
size_t seq_no_bits,
|
||||
size_t epoch_bits) {
|
||||
uint64_t epoch_mask = (1ULL << epoch_bits) - 1;
|
||||
uint64_t epoch = RecoverSequenceNumber(
|
||||
expected >> 48, (raw >> seq_no_bits) & epoch_mask, epoch_bits);
|
||||
if (epoch > (expected >> 48)) {
|
||||
// If the epoch has changed, reset the expected sequence number.
|
||||
expected = 0;
|
||||
} else {
|
||||
// Otherwise, retain just the sequence number part.
|
||||
expected &= (1ULL << 48) - 1;
|
||||
}
|
||||
uint64_t seq_no_mask = (1ULL << seq_no_bits) - 1;
|
||||
uint64_t seq_no =
|
||||
RecoverSequenceNumber(expected, raw & seq_no_mask, seq_no_bits);
|
||||
return (epoch << 48) | seq_no;
|
||||
}
|
||||
|
||||
bool TlsRecordHeader::Parse(bool is_dtls13, uint64_t seqno, TlsParser* parser,
|
||||
DataBuffer* body) {
|
||||
auto mark = parser->consumed();
|
||||
|
||||
if (!parser->Read(&content_type_)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (is_dtls13) {
|
||||
variant_ = ssl_variant_datagram;
|
||||
version_ = SSL_LIBRARY_VERSION_TLS_1_3;
|
||||
|
||||
#ifndef UNSAFE_FUZZER_MODE
|
||||
// Deal with the 7 octet header.
|
||||
if (content_type_ == kTlsApplicationDataType) {
|
||||
uint32_t tmp;
|
||||
if (!parser->Read(&tmp, 4)) {
|
||||
return false;
|
||||
}
|
||||
sequence_number_ = ParseSequenceNumber(seqno, tmp, 30, 2);
|
||||
if (!parser->ReadFromMark(&header_, parser->consumed() + 2 - mark,
|
||||
mark)) {
|
||||
return false;
|
||||
}
|
||||
return parser->ReadVariable(body, 2);
|
||||
}
|
||||
|
||||
// The short, 2 octet header.
|
||||
if ((content_type_ & 0xe0) == 0x20) {
|
||||
uint32_t tmp;
|
||||
if (!parser->Read(&tmp, 1)) {
|
||||
return false;
|
||||
}
|
||||
// Need to use the low 5 bits of the first octet too.
|
||||
tmp |= (content_type_ & 0x1f) << 8;
|
||||
content_type_ = kTlsApplicationDataType;
|
||||
sequence_number_ = ParseSequenceNumber(seqno, tmp, 12, 1);
|
||||
|
||||
if (!parser->ReadFromMark(&header_, parser->consumed() - mark, mark)) {
|
||||
return false;
|
||||
}
|
||||
return parser->Read(body, parser->remaining());
|
||||
}
|
||||
|
||||
// The full 13 octet header can only be used for a few types.
|
||||
EXPECT_TRUE(content_type_ == kTlsAlertType ||
|
||||
content_type_ == kTlsHandshakeType ||
|
||||
content_type_ == kTlsAckType);
|
||||
#endif
|
||||
}
|
||||
|
||||
uint32_t ver;
|
||||
if (!parser->Read(&ver, 2)) {
|
||||
return false;
|
||||
}
|
||||
version_ = ver;
|
||||
if (!is_dtls13) {
|
||||
variant_ = IsDtls(ver) ? ssl_variant_datagram : ssl_variant_stream;
|
||||
}
|
||||
version_ = NormalizeTlsVersion(ver);
|
||||
|
||||
// If this is DTLS, overwrite the sequence number.
|
||||
if (IsDtls(ver)) {
|
||||
if (is_dtls()) {
|
||||
// If this is DTLS, read the sequence number.
|
||||
uint32_t tmp;
|
||||
if (!parser->Read(&tmp, 4)) {
|
||||
return false;
|
||||
|
@ -205,19 +337,38 @@ bool TlsRecordHeader::Parse(uint64_t seqno, TlsParser* parser,
|
|||
} else {
|
||||
sequence_number_ = seqno;
|
||||
}
|
||||
if (!parser->ReadFromMark(&header_, parser->consumed() + 2 - mark, mark)) {
|
||||
return false;
|
||||
}
|
||||
return parser->ReadVariable(body, 2);
|
||||
}
|
||||
|
||||
size_t TlsRecordHeader::WriteHeader(DataBuffer* buffer, size_t offset,
|
||||
size_t body_len) const {
|
||||
offset = buffer->Write(offset, content_type_, 1);
|
||||
if (is_dtls() && version_ >= SSL_LIBRARY_VERSION_TLS_1_3 &&
|
||||
content_type() == kTlsApplicationDataType) {
|
||||
// application_data records in TLS 1.3 have a different header format.
|
||||
// Always use the long header here for simplicity.
|
||||
uint32_t e = (sequence_number_ >> 48) & 0x3;
|
||||
uint32_t seqno = sequence_number_ & ((1ULL << 30) - 1);
|
||||
offset = buffer->Write(offset, (e << 30) | seqno, 4);
|
||||
} else {
|
||||
uint16_t v = is_dtls() ? TlsVersionToDtlsVersion(version_) : version_;
|
||||
offset = buffer->Write(offset, v, 2);
|
||||
if (is_dtls()) {
|
||||
// write epoch (2 octet), and seqnum (6 octet)
|
||||
offset = buffer->Write(offset, sequence_number_ >> 32, 4);
|
||||
offset = buffer->Write(offset, sequence_number_ & 0xffffffff, 4);
|
||||
}
|
||||
}
|
||||
offset = buffer->Write(offset, body_len, 2);
|
||||
return offset;
|
||||
}
|
||||
|
||||
size_t TlsRecordHeader::Write(DataBuffer* buffer, size_t offset,
|
||||
const DataBuffer& body) const {
|
||||
offset = buffer->Write(offset, content_type_, 1);
|
||||
offset = buffer->Write(offset, version_, 2);
|
||||
if (is_dtls()) {
|
||||
// write epoch (2 octet), and seqnum (6 octet)
|
||||
offset = buffer->Write(offset, sequence_number_ >> 32, 4);
|
||||
offset = buffer->Write(offset, sequence_number_ & 0xffffffff, 4);
|
||||
}
|
||||
offset = buffer->Write(offset, body.len(), 2);
|
||||
offset = WriteHeader(buffer, offset, body.len());
|
||||
offset = buffer->Write(offset, body);
|
||||
return offset;
|
||||
}
|
||||
|
@ -406,6 +557,7 @@ bool TlsHandshakeFilter::HandshakeHeader::Parse(
|
|||
const DataBuffer& preceding_fragment, DataBuffer* body, bool* complete) {
|
||||
*complete = false;
|
||||
|
||||
variant_ = record_header.variant();
|
||||
version_ = record_header.version();
|
||||
if (!parser->Read(&handshake_type_)) {
|
||||
return false; // malformed
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
#include <memory>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
#include "sslt.h"
|
||||
#include "test_io.h"
|
||||
#include "tls_agent.h"
|
||||
#include "tls_parser.h"
|
||||
|
@ -27,40 +27,57 @@ class TlsCipherSpec;
|
|||
|
||||
class TlsVersioned {
|
||||
public:
|
||||
TlsVersioned() : version_(0) {}
|
||||
explicit TlsVersioned(uint16_t v) : version_(v) {}
|
||||
TlsVersioned() : variant_(ssl_variant_stream), version_(0) {}
|
||||
TlsVersioned(SSLProtocolVariant var, uint16_t ver)
|
||||
: variant_(var), version_(ver) {}
|
||||
|
||||
bool is_dtls() const { return IsDtls(version_); }
|
||||
bool is_dtls() const { return variant_ == ssl_variant_datagram; }
|
||||
SSLProtocolVariant variant() const { return variant_; }
|
||||
uint16_t version() const { return version_; }
|
||||
|
||||
void WriteStream(std::ostream& stream) const;
|
||||
|
||||
protected:
|
||||
SSLProtocolVariant variant_;
|
||||
uint16_t version_;
|
||||
};
|
||||
|
||||
class TlsRecordHeader : public TlsVersioned {
|
||||
public:
|
||||
TlsRecordHeader() : TlsVersioned(), content_type_(0), sequence_number_(0) {}
|
||||
TlsRecordHeader(uint16_t ver, uint8_t ct, uint64_t seqno)
|
||||
: TlsVersioned(ver), content_type_(ct), sequence_number_(seqno) {}
|
||||
TlsRecordHeader()
|
||||
: TlsVersioned(), content_type_(0), sequence_number_(0), header_() {}
|
||||
TlsRecordHeader(SSLProtocolVariant var, uint16_t ver, uint8_t ct,
|
||||
uint64_t seqno)
|
||||
: TlsVersioned(var, ver),
|
||||
content_type_(ct),
|
||||
sequence_number_(seqno),
|
||||
header_() {}
|
||||
|
||||
uint8_t content_type() const { return content_type_; }
|
||||
uint64_t sequence_number() const { return sequence_number_; }
|
||||
uint16_t epoch() const {
|
||||
return static_cast<uint16_t>(sequence_number_ >> 48);
|
||||
}
|
||||
size_t header_length() const { return is_dtls() ? 13 : 5; }
|
||||
size_t header_length() const;
|
||||
const DataBuffer& header() const { return header_; }
|
||||
|
||||
// Parse the header; return true if successful; body in an outparam if OK.
|
||||
bool Parse(uint64_t sequence_number, TlsParser* parser, DataBuffer* body);
|
||||
bool Parse(bool is_dtls13, uint64_t sequence_number, TlsParser* parser,
|
||||
DataBuffer* body);
|
||||
// Write the header and body to a buffer at the given offset.
|
||||
// Return the offset of the end of the write.
|
||||
size_t Write(DataBuffer* buffer, size_t offset, const DataBuffer& body) const;
|
||||
size_t WriteHeader(DataBuffer* buffer, size_t offset, size_t body_len) const;
|
||||
|
||||
private:
|
||||
static uint64_t RecoverSequenceNumber(uint64_t expected, uint32_t partial,
|
||||
size_t partial_bits);
|
||||
static uint64_t ParseSequenceNumber(uint64_t expected, uint32_t raw,
|
||||
size_t seq_no_bits, size_t epoch_bits);
|
||||
|
||||
uint8_t content_type_;
|
||||
uint64_t sequence_number_;
|
||||
DataBuffer header_;
|
||||
};
|
||||
|
||||
struct TlsRecord {
|
||||
|
@ -127,6 +144,8 @@ class TlsRecordFilter : public PacketFilter {
|
|||
return KEEP;
|
||||
}
|
||||
|
||||
bool is_dtls13() const;
|
||||
|
||||
private:
|
||||
static void CipherSpecChanged(void* arg, PRBool sending,
|
||||
ssl3CipherSpec* newSpec);
|
||||
|
|
|
@ -54,17 +54,17 @@ bool AeadCipher::AeadInner(bool decrypt, void *params, size_t param_length,
|
|||
return rv == SECSuccess;
|
||||
}
|
||||
|
||||
bool AeadCipherAesGcm::Aead(bool decrypt, uint64_t seq, const uint8_t *in,
|
||||
size_t inlen, uint8_t *out, size_t *outlen,
|
||||
size_t maxlen) {
|
||||
bool AeadCipherAesGcm::Aead(bool decrypt, const uint8_t *hdr, size_t hdr_len,
|
||||
uint64_t seq, const uint8_t *in, size_t inlen,
|
||||
uint8_t *out, size_t *outlen, size_t maxlen) {
|
||||
CK_GCM_PARAMS aeadParams;
|
||||
unsigned char nonce[12];
|
||||
|
||||
memset(&aeadParams, 0, sizeof(aeadParams));
|
||||
aeadParams.pIv = nonce;
|
||||
aeadParams.ulIvLen = sizeof(nonce);
|
||||
aeadParams.pAAD = NULL;
|
||||
aeadParams.ulAADLen = 0;
|
||||
aeadParams.pAAD = const_cast<uint8_t *>(hdr);
|
||||
aeadParams.ulAADLen = hdr_len;
|
||||
aeadParams.ulTagBits = 128;
|
||||
|
||||
FormatNonce(seq, nonce);
|
||||
|
@ -72,7 +72,8 @@ bool AeadCipherAesGcm::Aead(bool decrypt, uint64_t seq, const uint8_t *in,
|
|||
in, inlen, out, outlen, maxlen);
|
||||
}
|
||||
|
||||
bool AeadCipherChacha20Poly1305::Aead(bool decrypt, uint64_t seq,
|
||||
bool AeadCipherChacha20Poly1305::Aead(bool decrypt, const uint8_t *hdr,
|
||||
size_t hdr_len, uint64_t seq,
|
||||
const uint8_t *in, size_t inlen,
|
||||
uint8_t *out, size_t *outlen,
|
||||
size_t maxlen) {
|
||||
|
@ -82,8 +83,8 @@ bool AeadCipherChacha20Poly1305::Aead(bool decrypt, uint64_t seq,
|
|||
memset(&aeadParams, 0, sizeof(aeadParams));
|
||||
aeadParams.pNonce = nonce;
|
||||
aeadParams.ulNonceLen = sizeof(nonce);
|
||||
aeadParams.pAAD = NULL;
|
||||
aeadParams.ulAADLen = 0;
|
||||
aeadParams.pAAD = const_cast<uint8_t *>(hdr);
|
||||
aeadParams.ulAADLen = hdr_len;
|
||||
aeadParams.ulTagLen = 16;
|
||||
|
||||
FormatNonce(seq, nonce);
|
||||
|
@ -114,10 +115,12 @@ bool TlsCipherSpec::Unprotect(const TlsRecordHeader &header,
|
|||
// Make space.
|
||||
plaintext->Allocate(ciphertext.len());
|
||||
|
||||
auto header_bytes = header.header();
|
||||
size_t len;
|
||||
bool ret =
|
||||
aead_->Aead(true, header.sequence_number(), ciphertext.data(),
|
||||
ciphertext.len(), plaintext->data(), &len, plaintext->len());
|
||||
aead_->Aead(true, header_bytes.data(), header_bytes.len(),
|
||||
header.sequence_number(), ciphertext.data(), ciphertext.len(),
|
||||
plaintext->data(), &len, plaintext->len());
|
||||
if (!ret) return false;
|
||||
|
||||
plaintext->Truncate(len);
|
||||
|
@ -133,9 +136,13 @@ bool TlsCipherSpec::Protect(const TlsRecordHeader &header,
|
|||
ciphertext->Allocate(plaintext.len() +
|
||||
32); // Room for any plausible auth tag
|
||||
size_t len;
|
||||
|
||||
DataBuffer header_bytes;
|
||||
(void)header.WriteHeader(&header_bytes, 0, plaintext.len() + 16);
|
||||
bool ret =
|
||||
aead_->Aead(false, header.sequence_number(), plaintext.data(),
|
||||
plaintext.len(), ciphertext->data(), &len, ciphertext->len());
|
||||
aead_->Aead(false, header_bytes.data(), header_bytes.len(),
|
||||
header.sequence_number(), plaintext.data(), plaintext.len(),
|
||||
ciphertext->data(), &len, ciphertext->len());
|
||||
if (!ret) return false;
|
||||
ciphertext->Truncate(len);
|
||||
|
||||
|
|
|
@ -23,8 +23,9 @@ class AeadCipher {
|
|||
virtual ~AeadCipher();
|
||||
|
||||
bool Init(PK11SymKey *key, const uint8_t *iv);
|
||||
virtual bool Aead(bool decrypt, uint64_t seq, const uint8_t *in, size_t inlen,
|
||||
uint8_t *out, size_t *outlen, size_t maxlen) = 0;
|
||||
virtual bool Aead(bool decrypt, const uint8_t *hdr, size_t hdr_len,
|
||||
uint64_t seq, const uint8_t *in, size_t inlen, uint8_t *out,
|
||||
size_t *outlen, size_t maxlen) = 0;
|
||||
|
||||
protected:
|
||||
void FormatNonce(uint64_t seq, uint8_t *nonce);
|
||||
|
@ -42,8 +43,9 @@ class AeadCipherChacha20Poly1305 : public AeadCipher {
|
|||
AeadCipherChacha20Poly1305() : AeadCipher(CKM_NSS_CHACHA20_POLY1305) {}
|
||||
|
||||
protected:
|
||||
bool Aead(bool decrypt, uint64_t seq, const uint8_t *in, size_t inlen,
|
||||
uint8_t *out, size_t *outlen, size_t maxlen);
|
||||
bool Aead(bool decrypt, const uint8_t *hdr, size_t hdr_len, uint64_t seq,
|
||||
const uint8_t *in, size_t inlen, uint8_t *out, size_t *outlen,
|
||||
size_t maxlen);
|
||||
};
|
||||
|
||||
class AeadCipherAesGcm : public AeadCipher {
|
||||
|
@ -51,8 +53,9 @@ class AeadCipherAesGcm : public AeadCipher {
|
|||
AeadCipherAesGcm() : AeadCipher(CKM_AES_GCM) {}
|
||||
|
||||
protected:
|
||||
bool Aead(bool decrypt, uint64_t seq, const uint8_t *in, size_t inlen,
|
||||
uint8_t *out, size_t *outlen, size_t maxlen);
|
||||
bool Aead(bool decrypt, const uint8_t *hdr, size_t hdr_len, uint64_t seq,
|
||||
const uint8_t *in, size_t inlen, uint8_t *out, size_t *outlen,
|
||||
size_t maxlen);
|
||||
};
|
||||
|
||||
// Our analog of ssl3CipherSpec
|
||||
|
|
|
@ -11,6 +11,43 @@
|
|||
#include "sslimpl.h"
|
||||
#include "sslproto.h"
|
||||
|
||||
SECStatus
|
||||
dtls13_InsertCipherTextHeader(const sslSocket *ss, ssl3CipherSpec *cwSpec,
|
||||
sslBuffer *wrBuf, PRBool *needsLength)
|
||||
{
|
||||
PRUint32 seq;
|
||||
SECStatus rv;
|
||||
|
||||
/* Avoid using short records for the handshake. We pack multiple records
|
||||
* into the one datagram for the handshake. */
|
||||
if (ss->opt.enableDtlsShortHeader &&
|
||||
cwSpec->epoch != TrafficKeyHandshake) {
|
||||
*needsLength = PR_FALSE;
|
||||
/* The short header is comprised of two octets in the form
|
||||
* 0b001essssssssssss where 'e' is the low bit of the epoch and 's' is
|
||||
* the low 12 bits of the sequence number. */
|
||||
seq = 0x2000 |
|
||||
(((uint64_t)cwSpec->epoch & 1) << 12) |
|
||||
(cwSpec->nextSeqNum & 0xfff);
|
||||
return sslBuffer_AppendNumber(wrBuf, seq, 2);
|
||||
}
|
||||
|
||||
rv = sslBuffer_AppendNumber(wrBuf, content_application_data, 1);
|
||||
if (rv != SECSuccess) {
|
||||
return SECFailure;
|
||||
}
|
||||
|
||||
/* The epoch and sequence number are encoded on 4 octets, with the epoch
|
||||
* consuming the first two bits. */
|
||||
seq = (((uint64_t)cwSpec->epoch & 3) << 30) | (cwSpec->nextSeqNum & 0x3fffffff);
|
||||
rv = sslBuffer_AppendNumber(wrBuf, seq, 4);
|
||||
if (rv != SECSuccess) {
|
||||
return SECFailure;
|
||||
}
|
||||
*needsLength = PR_TRUE;
|
||||
return SECSuccess;
|
||||
}
|
||||
|
||||
/* DTLS 1.3 Record map for ACK processing.
|
||||
* This represents a single fragment, so a record which includes
|
||||
* multiple fragments will have one entry for each fragment on the
|
||||
|
|
|
@ -9,6 +9,10 @@
|
|||
#ifndef __dtls13con_h_
|
||||
#define __dtls13con_h_
|
||||
|
||||
SECStatus dtls13_InsertCipherTextHeader(const sslSocket *ss,
|
||||
ssl3CipherSpec *cwSpec,
|
||||
sslBuffer *wrBuf,
|
||||
PRBool *needsLength);
|
||||
SECStatus dtls13_RememberFragment(sslSocket *ss, PRCList *list,
|
||||
PRUint32 sequence, PRUint32 offset,
|
||||
PRUint32 length, DTLSEpoch epoch,
|
||||
|
|
|
@ -776,7 +776,7 @@ dtls_FragmentHandshake(sslSocket *ss, DTLSQueuedMessage *msg)
|
|||
rv = dtls13_RememberFragment(ss, &ss->ssl3.hs.dtlsSentHandshake,
|
||||
msgSeq, fragmentOffset, fragmentLen,
|
||||
msg->cwSpec->epoch,
|
||||
msg->cwSpec->seqNum);
|
||||
msg->cwSpec->nextSeqNum);
|
||||
if (rv != SECSuccess) {
|
||||
return SECFailure;
|
||||
}
|
||||
|
@ -1319,6 +1319,107 @@ DTLS_GetHandshakeTimeout(PRFileDesc *socket, PRIntervalTime *timeout)
|
|||
return SECSuccess;
|
||||
}
|
||||
|
||||
PRBool
|
||||
dtls_IsLongHeader(SSL3ProtocolVersion version, PRUint8 firstOctet)
|
||||
{
|
||||
#ifndef UNSAFE_FUZZER_MODE
|
||||
return version < SSL_LIBRARY_VERSION_TLS_1_3 ||
|
||||
firstOctet == content_handshake ||
|
||||
firstOctet == content_ack ||
|
||||
firstOctet == content_alert;
|
||||
#else
|
||||
return PR_TRUE;
|
||||
#endif
|
||||
}
|
||||
|
||||
DTLSEpoch
|
||||
dtls_ReadEpoch(const ssl3CipherSpec *crSpec, const PRUint8 *hdr)
|
||||
{
|
||||
DTLSEpoch epoch;
|
||||
DTLSEpoch maxEpoch;
|
||||
DTLSEpoch partial;
|
||||
|
||||
if (dtls_IsLongHeader(crSpec->version, hdr[0])) {
|
||||
return ((DTLSEpoch)hdr[3] << 8) | hdr[4];
|
||||
}
|
||||
|
||||
/* A lot of how we recover the epoch here will depend on how we plan to
|
||||
* manage KeyUpdate. In the case that we decide to install a new read spec
|
||||
* as a KeyUpdate is handled, crSpec will always be the highest epoch we can
|
||||
* possibly receive. That makes this easier to manage. */
|
||||
if ((hdr[0] & 0xe0) == 0x20) {
|
||||
/* Use crSpec->epoch, or crSpec->epoch - 1 if the last bit differs. */
|
||||
if (((hdr[0] >> 4) & 1) == (crSpec->epoch & 1)) {
|
||||
return crSpec->epoch;
|
||||
}
|
||||
return crSpec->epoch - 1;
|
||||
}
|
||||
|
||||
/* dtls_GatherData should ensure that this works. */
|
||||
PORT_Assert(hdr[0] == content_application_data);
|
||||
|
||||
/* This uses the same method as is used to recover the sequence number in
|
||||
* dtls_ReadSequenceNumber, except that the maximum value is set to the
|
||||
* current epoch. */
|
||||
partial = hdr[1] >> 6;
|
||||
maxEpoch = PR_MAX(crSpec->epoch, 3);
|
||||
epoch = (maxEpoch & 0xfffc) | partial;
|
||||
if (partial > (maxEpoch & 0x03)) {
|
||||
epoch -= 4;
|
||||
}
|
||||
return epoch;
|
||||
}
|
||||
|
||||
static sslSequenceNumber
|
||||
dtls_ReadSequenceNumber(const ssl3CipherSpec *spec, const PRUint8 *hdr)
|
||||
{
|
||||
sslSequenceNumber cap;
|
||||
sslSequenceNumber partial;
|
||||
sslSequenceNumber seqNum;
|
||||
sslSequenceNumber mask;
|
||||
|
||||
if (dtls_IsLongHeader(spec->version, hdr[0])) {
|
||||
static const unsigned int seqNumOffset = 5; /* type, version, epoch */
|
||||
static const unsigned int seqNumLength = 6;
|
||||
sslReader r = SSL_READER(hdr + seqNumOffset, seqNumLength);
|
||||
(void)sslRead_ReadNumber(&r, seqNumLength, &seqNum);
|
||||
return seqNum;
|
||||
}
|
||||
|
||||
/* Only the least significant bits of the sequence number is available here.
|
||||
* This recovers the value based on the next expected sequence number.
|
||||
*
|
||||
* This works by determining the maximum possible sequence number, which is
|
||||
* half the range of possible values above the expected next value (the
|
||||
* expected next value is in |spec->seqNum|). Then, the last part of the
|
||||
* sequence number is replaced. If that causes the value to exceed the
|
||||
* maximum, subtract an entire range.
|
||||
*/
|
||||
if ((hdr[0] & 0xe0) == 0x20) {
|
||||
/* A 12-bit sequence number. */
|
||||
cap = spec->nextSeqNum + (1ULL << 11);
|
||||
partial = (((sslSequenceNumber)hdr[0] & 0xf) << 8) |
|
||||
(sslSequenceNumber)hdr[1];
|
||||
mask = (1ULL << 12) - 1;
|
||||
} else {
|
||||
/* A 30-bit sequence number. */
|
||||
cap = spec->nextSeqNum + (1ULL << 29);
|
||||
partial = (((sslSequenceNumber)hdr[1] & 0x3f) << 24) |
|
||||
((sslSequenceNumber)hdr[2] << 16) |
|
||||
((sslSequenceNumber)hdr[3] << 8) |
|
||||
(sslSequenceNumber)hdr[4];
|
||||
mask = (1ULL << 30) - 1;
|
||||
}
|
||||
seqNum = (cap & ~mask) | partial;
|
||||
/* The second check prevents the value from underflowing if we get a large
|
||||
* gap at the start of a connection, where this subtraction would cause the
|
||||
* sequence number to wrap to near UINT64_MAX. */
|
||||
if ((partial > (cap & mask)) && (seqNum > mask)) {
|
||||
seqNum -= mask + 1;
|
||||
}
|
||||
return seqNum;
|
||||
}
|
||||
|
||||
/*
|
||||
* DTLS relevance checks:
|
||||
* Note that this code currently ignores all out-of-epoch packets,
|
||||
|
@ -1336,7 +1437,7 @@ dtls_IsRelevant(sslSocket *ss, const ssl3CipherSpec *spec,
|
|||
const SSL3Ciphertext *cText,
|
||||
sslSequenceNumber *seqNumOut)
|
||||
{
|
||||
sslSequenceNumber seqNum = cText->seq_num & RECORD_SEQ_MASK;
|
||||
sslSequenceNumber seqNum = dtls_ReadSequenceNumber(spec, cText->hdr);
|
||||
if (dtls_RecordGetRecvd(&spec->recvdRecords, seqNum) != 0) {
|
||||
SSL_TRC(10, ("%d: SSL3[%d]: dtls_IsRelevant, rejecting "
|
||||
"potentially replayed packet",
|
||||
|
|
|
@ -41,8 +41,10 @@ extern SSL3ProtocolVersion
|
|||
dtls_TLSVersionToDTLSVersion(SSL3ProtocolVersion tlsv);
|
||||
extern SSL3ProtocolVersion
|
||||
dtls_DTLSVersionToTLSVersion(SSL3ProtocolVersion dtlsv);
|
||||
DTLSEpoch dtls_ReadEpoch(const ssl3CipherSpec *crSpec, const PRUint8 *hdr);
|
||||
extern PRBool dtls_IsRelevant(sslSocket *ss, const ssl3CipherSpec *spec,
|
||||
const SSL3Ciphertext *cText,
|
||||
sslSequenceNumber *seqNum);
|
||||
void dtls_ReceivedFirstMessageInFlight(sslSocket *ss);
|
||||
PRBool dtls_IsLongHeader(SSL3ProtocolVersion version, PRUint8 firstOctet);
|
||||
#endif
|
||||
|
|
|
@ -254,6 +254,17 @@ SSL_IMPORT PRFileDesc *DTLS_ImportFD(PRFileDesc *model, PRFileDesc *fd);
|
|||
* no effect for a server. This setting is ignored for DTLS. */
|
||||
#define SSL_ENABLE_TLS13_COMPAT_MODE 35
|
||||
|
||||
/* Enables the sending of DTLS records using the short (two octet) record
|
||||
* header. Only do this if there are 2^10 or fewer packets in flight at a time;
|
||||
* using this with a larger number of packets in flight could mean that packets
|
||||
* are dropped if there is reordering.
|
||||
*
|
||||
* This applies to TLS 1.3 only. This is not a parameter that is negotiated
|
||||
* during the TLS handshake. Unlike other socket options, this option can be
|
||||
* changed after a handshake is complete.
|
||||
*/
|
||||
#define SSL_ENABLE_DTLS_SHORT_HEADER 36
|
||||
|
||||
#ifdef SSL_DEPRECATED_FUNCTION
|
||||
/* Old deprecated function names */
|
||||
SSL_IMPORT SECStatus SSL_Enable(PRFileDesc *fd, int option, PRIntn on);
|
||||
|
|
|
@ -990,27 +990,22 @@ ssl_ClientReadVersion(sslSocket *ss, PRUint8 **b, unsigned int *len,
|
|||
if (rv != SECSuccess) {
|
||||
return SECFailure; /* alert has been sent */
|
||||
}
|
||||
|
||||
#ifdef TLS_1_3_DRAFT_VERSION
|
||||
if (temp == SSL_LIBRARY_VERSION_TLS_1_3) {
|
||||
(void)SSL3_SendAlert(ss, alert_fatal, protocol_version);
|
||||
PORT_SetError(SSL_ERROR_UNSUPPORTED_VERSION);
|
||||
return SECFailure;
|
||||
}
|
||||
if (temp == tls13_EncodeDraftVersion(SSL_LIBRARY_VERSION_TLS_1_3)) {
|
||||
v = SSL_LIBRARY_VERSION_TLS_1_3;
|
||||
} else {
|
||||
v = (SSL3ProtocolVersion)temp;
|
||||
}
|
||||
#else
|
||||
v = (SSL3ProtocolVersion)temp;
|
||||
#endif
|
||||
|
||||
if (IS_DTLS(ss)) {
|
||||
/* If this fails, we get 0 back and the next check to fails. */
|
||||
v = dtls_DTLSVersionToTLSVersion(v);
|
||||
/* Check for failure. */
|
||||
if (!v || v > SSL_LIBRARY_VERSION_MAX_SUPPORTED) {
|
||||
SSL3_SendAlert(ss, alert_fatal, illegal_parameter);
|
||||
return SECFailure;
|
||||
}
|
||||
}
|
||||
|
||||
/* You can't negotiate TLS 1.3 this way. */
|
||||
if (v >= SSL_LIBRARY_VERSION_TLS_1_3) {
|
||||
SSL3_SendAlert(ss, alert_fatal, illegal_parameter);
|
||||
return SECFailure;
|
||||
}
|
||||
*version = v;
|
||||
return SECSuccess;
|
||||
}
|
||||
|
@ -1415,7 +1410,7 @@ ssl3_SetupPendingCipherSpec(sslSocket *ss, CipherSpecDirection direction,
|
|||
spec->macDef = ssl_GetMacDef(ss, suiteDef);
|
||||
|
||||
spec->epoch = prev->epoch + 1;
|
||||
spec->seqNum = 0;
|
||||
spec->nextSeqNum = 0;
|
||||
if (IS_DTLS(ss) && direction == CipherSpecRead) {
|
||||
dtls_InitRecvdRecords(&spec->recvdRecords);
|
||||
}
|
||||
|
@ -2004,6 +1999,7 @@ ssl3_MACEncryptRecord(ssl3CipherSpec *cwSpec,
|
|||
unsigned int ivLen = 0;
|
||||
unsigned char pseudoHeaderBuf[13];
|
||||
sslBuffer pseudoHeader = SSL_BUFFER(pseudoHeaderBuf);
|
||||
int len;
|
||||
|
||||
if (cwSpec->cipherDef->type == type_block &&
|
||||
cwSpec->version >= SSL_LIBRARY_VERSION_TLS_1_1) {
|
||||
|
@ -2013,29 +2009,32 @@ ssl3_MACEncryptRecord(ssl3CipherSpec *cwSpec,
|
|||
* record.
|
||||
*/
|
||||
ivLen = cwSpec->cipherDef->iv_size;
|
||||
if (ivLen > wrBuf->space) {
|
||||
if (ivLen > SSL_BUFFER_SPACE(wrBuf)) {
|
||||
PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
|
||||
return SECFailure;
|
||||
}
|
||||
rv = PK11_GenerateRandom(wrBuf->buf, ivLen);
|
||||
rv = PK11_GenerateRandom(SSL_BUFFER_NEXT(wrBuf), ivLen);
|
||||
if (rv != SECSuccess) {
|
||||
ssl_MapLowLevelError(SSL_ERROR_GENERATE_RANDOM_FAILURE);
|
||||
return rv;
|
||||
}
|
||||
rv = cwSpec->cipher(cwSpec->cipherContext,
|
||||
wrBuf->buf, /* output */
|
||||
(int *)&wrBuf->len, /* outlen */
|
||||
ivLen, /* max outlen */
|
||||
wrBuf->buf, /* input */
|
||||
ivLen); /* input len */
|
||||
if (rv != SECSuccess || wrBuf->len != ivLen) {
|
||||
SSL_BUFFER_NEXT(wrBuf), /* output */
|
||||
&len, /* outlen */
|
||||
ivLen, /* max outlen */
|
||||
SSL_BUFFER_NEXT(wrBuf), /* input */
|
||||
ivLen); /* input len */
|
||||
if (rv != SECSuccess || len != ivLen) {
|
||||
PORT_SetError(SSL_ERROR_ENCRYPTION_FAILURE);
|
||||
return SECFailure;
|
||||
}
|
||||
|
||||
rv = sslBuffer_Skip(wrBuf, len, NULL);
|
||||
PORT_Assert(rv == SECSuccess); /* Can't fail. */
|
||||
}
|
||||
|
||||
rv = ssl3_BuildRecordPseudoHeader(
|
||||
cwSpec->epoch, cwSpec->seqNum, type,
|
||||
cwSpec->epoch, cwSpec->nextSeqNum, type,
|
||||
cwSpec->version >= SSL_LIBRARY_VERSION_TLS_1_0, cwSpec->recordVersion,
|
||||
isDTLS, contentLen, &pseudoHeader);
|
||||
PORT_Assert(rv == SECSuccess);
|
||||
|
@ -2043,23 +2042,26 @@ ssl3_MACEncryptRecord(ssl3CipherSpec *cwSpec,
|
|||
const int nonceLen = cwSpec->cipherDef->explicit_nonce_size;
|
||||
const int tagLen = cwSpec->cipherDef->tag_size;
|
||||
|
||||
if (nonceLen + contentLen + tagLen > wrBuf->space) {
|
||||
if (nonceLen + contentLen + tagLen > SSL_BUFFER_SPACE(wrBuf)) {
|
||||
PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
|
||||
return SECFailure;
|
||||
}
|
||||
|
||||
rv = cwSpec->aead(
|
||||
&cwSpec->keyMaterial,
|
||||
PR_FALSE, /* do encrypt */
|
||||
wrBuf->buf, /* output */
|
||||
(int *)&wrBuf->len, /* out len */
|
||||
wrBuf->space, /* max out */
|
||||
pIn, contentLen, /* input */
|
||||
PR_FALSE, /* do encrypt */
|
||||
SSL_BUFFER_NEXT(wrBuf), /* output */
|
||||
&len, /* out len */
|
||||
SSL_BUFFER_SPACE(wrBuf), /* max out */
|
||||
pIn, contentLen, /* input */
|
||||
SSL_BUFFER_BASE(&pseudoHeader), SSL_BUFFER_LEN(&pseudoHeader));
|
||||
if (rv != SECSuccess) {
|
||||
PORT_SetError(SSL_ERROR_ENCRYPTION_FAILURE);
|
||||
return SECFailure;
|
||||
}
|
||||
|
||||
rv = sslBuffer_Skip(wrBuf, len, NULL);
|
||||
PORT_Assert(rv == SECSuccess); /* Can't fail. */
|
||||
} else {
|
||||
int blockSize = cwSpec->cipherDef->block_size;
|
||||
|
||||
|
@ -2069,7 +2071,7 @@ ssl3_MACEncryptRecord(ssl3CipherSpec *cwSpec,
|
|||
rv = ssl3_ComputeRecordMAC(cwSpec, SSL_BUFFER_BASE(&pseudoHeader),
|
||||
SSL_BUFFER_LEN(&pseudoHeader),
|
||||
pIn, contentLen,
|
||||
wrBuf->buf + ivLen + contentLen, &macLen);
|
||||
SSL_BUFFER_NEXT(wrBuf) + contentLen, &macLen);
|
||||
if (rv != SECSuccess) {
|
||||
ssl_MapLowLevelError(SSL_ERROR_MAC_COMPUTATION_FAILURE);
|
||||
return SECFailure;
|
||||
|
@ -2095,7 +2097,7 @@ ssl3_MACEncryptRecord(ssl3CipherSpec *cwSpec,
|
|||
PORT_Assert((fragLen % blockSize) == 0);
|
||||
|
||||
/* Pad according to TLS rules (also acceptable to SSL3). */
|
||||
pBuf = &wrBuf->buf[ivLen + fragLen - 1];
|
||||
pBuf = SSL_BUFFER_NEXT(wrBuf) + fragLen - 1;
|
||||
for (i = padding_length + 1; i > 0; --i) {
|
||||
*pBuf-- = padding_length;
|
||||
}
|
||||
|
@ -2112,14 +2114,14 @@ ssl3_MACEncryptRecord(ssl3CipherSpec *cwSpec,
|
|||
p2Len += oddLen;
|
||||
PORT_Assert((blockSize < 2) ||
|
||||
(p2Len % blockSize) == 0);
|
||||
memmove(wrBuf->buf + ivLen + p1Len, pIn + p1Len, oddLen);
|
||||
memmove(SSL_BUFFER_NEXT(wrBuf) + p1Len, pIn + p1Len, oddLen);
|
||||
}
|
||||
if (p1Len > 0) {
|
||||
int cipherBytesPart1 = -1;
|
||||
rv = cwSpec->cipher(cwSpec->cipherContext,
|
||||
wrBuf->buf + ivLen, /* output */
|
||||
&cipherBytesPart1, /* actual outlen */
|
||||
p1Len, /* max outlen */
|
||||
SSL_BUFFER_NEXT(wrBuf), /* output */
|
||||
&cipherBytesPart1, /* actual outlen */
|
||||
p1Len, /* max outlen */
|
||||
pIn,
|
||||
p1Len); /* input, and inputlen */
|
||||
PORT_Assert(rv == SECSuccess && cipherBytesPart1 == (int)p1Len);
|
||||
|
@ -2127,22 +2129,24 @@ ssl3_MACEncryptRecord(ssl3CipherSpec *cwSpec,
|
|||
PORT_SetError(SSL_ERROR_ENCRYPTION_FAILURE);
|
||||
return SECFailure;
|
||||
}
|
||||
wrBuf->len += cipherBytesPart1;
|
||||
rv = sslBuffer_Skip(wrBuf, p1Len, NULL);
|
||||
PORT_Assert(rv == SECSuccess);
|
||||
}
|
||||
if (p2Len > 0) {
|
||||
int cipherBytesPart2 = -1;
|
||||
rv = cwSpec->cipher(cwSpec->cipherContext,
|
||||
wrBuf->buf + ivLen + p1Len,
|
||||
SSL_BUFFER_NEXT(wrBuf),
|
||||
&cipherBytesPart2, /* output and actual outLen */
|
||||
p2Len, /* max outlen */
|
||||
wrBuf->buf + ivLen + p1Len,
|
||||
SSL_BUFFER_NEXT(wrBuf),
|
||||
p2Len); /* input and inputLen*/
|
||||
PORT_Assert(rv == SECSuccess && cipherBytesPart2 == (int)p2Len);
|
||||
if (rv != SECSuccess || cipherBytesPart2 != (int)p2Len) {
|
||||
PORT_SetError(SSL_ERROR_ENCRYPTION_FAILURE);
|
||||
return SECFailure;
|
||||
}
|
||||
wrBuf->len += cipherBytesPart2;
|
||||
rv = sslBuffer_Skip(wrBuf, p2Len, NULL);
|
||||
PORT_Assert(rv == SECSuccess);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2150,16 +2154,20 @@ ssl3_MACEncryptRecord(ssl3CipherSpec *cwSpec,
|
|||
}
|
||||
|
||||
/* Note: though this can report failure, it shouldn't. */
|
||||
static SECStatus
|
||||
SECStatus
|
||||
ssl_InsertRecordHeader(const sslSocket *ss, ssl3CipherSpec *cwSpec,
|
||||
SSL3ContentType contentType, unsigned int len,
|
||||
sslBuffer *wrBuf)
|
||||
SSL3ContentType contentType, sslBuffer *wrBuf,
|
||||
PRBool *needsLength)
|
||||
{
|
||||
SECStatus rv;
|
||||
|
||||
#ifndef UNSAFE_FUZZER_MODE
|
||||
if (cwSpec->version >= SSL_LIBRARY_VERSION_TLS_1_3 &&
|
||||
cwSpec->cipherDef->calg != ssl_calg_null) {
|
||||
cwSpec->epoch > TrafficKeyClearText) {
|
||||
if (IS_DTLS(ss)) {
|
||||
return dtls13_InsertCipherTextHeader(ss, cwSpec, wrBuf,
|
||||
needsLength);
|
||||
}
|
||||
contentType = content_application_data;
|
||||
}
|
||||
#endif
|
||||
|
@ -2177,16 +2185,12 @@ ssl_InsertRecordHeader(const sslSocket *ss, ssl3CipherSpec *cwSpec,
|
|||
if (rv != SECSuccess) {
|
||||
return SECFailure;
|
||||
}
|
||||
rv = sslBuffer_AppendNumber(wrBuf, cwSpec->seqNum, 6);
|
||||
rv = sslBuffer_AppendNumber(wrBuf, cwSpec->nextSeqNum, 6);
|
||||
if (rv != SECSuccess) {
|
||||
return SECFailure;
|
||||
}
|
||||
}
|
||||
rv = sslBuffer_AppendNumber(wrBuf, len, 2);
|
||||
if (rv != SECSuccess) {
|
||||
return SECFailure;
|
||||
}
|
||||
|
||||
*needsLength = PR_TRUE;
|
||||
return SECSuccess;
|
||||
}
|
||||
|
||||
|
@ -2194,66 +2198,67 @@ SECStatus
|
|||
ssl_ProtectRecord(sslSocket *ss, ssl3CipherSpec *cwSpec, SSL3ContentType type,
|
||||
const PRUint8 *pIn, PRUint32 contentLen, sslBuffer *wrBuf)
|
||||
{
|
||||
unsigned int headerLen = IS_DTLS(ss) ? DTLS_RECORD_HEADER_LENGTH
|
||||
: SSL3_RECORD_HEADER_LENGTH;
|
||||
sslBuffer protBuf = SSL_BUFFER_FIXED(SSL_BUFFER_BASE(wrBuf) + headerLen,
|
||||
SSL_BUFFER_SPACE(wrBuf) - headerLen);
|
||||
PRBool isTLS13;
|
||||
PRBool needsLength;
|
||||
unsigned int lenOffset;
|
||||
SECStatus rv;
|
||||
|
||||
PORT_Assert(cwSpec->direction == CipherSpecWrite);
|
||||
PORT_Assert(SSL_BUFFER_LEN(wrBuf) == 0);
|
||||
PORT_Assert(cwSpec->cipherDef->max_records <= RECORD_SEQ_MAX);
|
||||
if (cwSpec->seqNum >= cwSpec->cipherDef->max_records) {
|
||||
|
||||
if (cwSpec->nextSeqNum >= cwSpec->cipherDef->max_records) {
|
||||
/* We should have automatically updated before here in TLS 1.3. */
|
||||
PORT_Assert(cwSpec->version < SSL_LIBRARY_VERSION_TLS_1_3);
|
||||
SSL_TRC(3, ("%d: SSL[-]: write sequence number at limit 0x%0llx",
|
||||
SSL_GETPID(), cwSpec->seqNum));
|
||||
SSL_GETPID(), cwSpec->nextSeqNum));
|
||||
PORT_SetError(SSL_ERROR_TOO_MANY_RECORDS);
|
||||
return SECFailure;
|
||||
}
|
||||
|
||||
isTLS13 = (PRBool)(cwSpec->version >= SSL_LIBRARY_VERSION_TLS_1_3);
|
||||
rv = ssl_InsertRecordHeader(ss, cwSpec, type, wrBuf, &needsLength);
|
||||
if (rv != SECSuccess) {
|
||||
return SECFailure;
|
||||
}
|
||||
if (needsLength) {
|
||||
rv = sslBuffer_Skip(wrBuf, 2, &lenOffset);
|
||||
if (rv != SECSuccess) {
|
||||
return SECFailure;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef UNSAFE_FUZZER_MODE
|
||||
{
|
||||
int len;
|
||||
rv = Null_Cipher(NULL, SSL_BUFFER_BASE(&protBuf), &len,
|
||||
SSL_BUFFER_SPACE(&protBuf), pIn, contentLen);
|
||||
rv = Null_Cipher(NULL, SSL_BUFFER_NEXT(wrBuf), &len,
|
||||
SSL_BUFFER_SPACE(wrBuf), pIn, contentLen);
|
||||
if (rv != SECSuccess) {
|
||||
return SECFailure; /* error was set */
|
||||
}
|
||||
rv = sslBuffer_Skip(&protBuf, len, NULL);
|
||||
rv = sslBuffer_Skip(wrBuf, len, NULL);
|
||||
PORT_Assert(rv == SECSuccess); /* Can't fail. */
|
||||
}
|
||||
#else
|
||||
if (isTLS13) {
|
||||
rv = tls13_ProtectRecord(ss, cwSpec, type, pIn, contentLen, &protBuf);
|
||||
if (cwSpec->version >= SSL_LIBRARY_VERSION_TLS_1_3) {
|
||||
rv = tls13_ProtectRecord(ss, cwSpec, type, pIn, contentLen, wrBuf);
|
||||
} else {
|
||||
rv = ssl3_MACEncryptRecord(cwSpec, ss->sec.isServer, IS_DTLS(ss), type,
|
||||
pIn, contentLen, &protBuf);
|
||||
pIn, contentLen, wrBuf);
|
||||
}
|
||||
#endif
|
||||
if (rv != SECSuccess) {
|
||||
return SECFailure; /* error was set */
|
||||
}
|
||||
|
||||
PORT_Assert(protBuf.len <= MAX_FRAGMENT_LENGTH + (isTLS13 ? 256 : 1024));
|
||||
|
||||
rv = ssl_InsertRecordHeader(ss, cwSpec, type, SSL_BUFFER_LEN(&protBuf),
|
||||
wrBuf);
|
||||
if (rv != SECSuccess) {
|
||||
return SECFailure;
|
||||
if (needsLength) {
|
||||
/* Insert the length. */
|
||||
rv = sslBuffer_InsertLength(wrBuf, lenOffset, 2);
|
||||
if (rv != SECSuccess) {
|
||||
PORT_Assert(0); /* Can't fail. */
|
||||
return SECFailure;
|
||||
}
|
||||
}
|
||||
|
||||
PORT_Assert(SSL_BUFFER_LEN(wrBuf) == headerLen);
|
||||
rv = sslBuffer_Skip(wrBuf, SSL_BUFFER_LEN(&protBuf), NULL);
|
||||
if (rv != SECSuccess) {
|
||||
PORT_Assert(0); /* Can't fail. */
|
||||
return SECFailure;
|
||||
}
|
||||
++cwSpec->seqNum;
|
||||
|
||||
++cwSpec->nextSeqNum;
|
||||
return SECSuccess;
|
||||
}
|
||||
|
||||
|
@ -2291,6 +2296,7 @@ ssl_ProtectNextRecord(sslSocket *ss, ssl3CipherSpec *spec, SSL3ContentType type,
|
|||
*written = contentLen;
|
||||
return SECSuccess;
|
||||
}
|
||||
|
||||
/* Process the plain text before sending it.
|
||||
* Returns the number of bytes of plaintext that were successfully sent
|
||||
* plus the number of bytes of plaintext that were copied into the
|
||||
|
@ -2368,7 +2374,7 @@ ssl3_SendRecord(sslSocket *ss,
|
|||
rv = ssl_ProtectNextRecord(ss, spec, type, pIn, nIn, &written);
|
||||
ssl_ReleaseSpecReadLock(ss);
|
||||
if (rv != SECSuccess) {
|
||||
return SECFailure;
|
||||
goto loser;
|
||||
}
|
||||
|
||||
PORT_Assert(written > 0);
|
||||
|
@ -6165,7 +6171,6 @@ ssl3_HandleServerHello(sslSocket *ss, PRUint8 *b, PRUint32 length)
|
|||
SECItem sidBytes = { siBuffer, NULL, 0 };
|
||||
PRBool isHelloRetry;
|
||||
SSL3AlertDescription desc = illegal_parameter;
|
||||
TLSExtension *versionExtension;
|
||||
const PRUint8 *savedMsg = b;
|
||||
const PRUint32 savedLength = length;
|
||||
#ifndef TLS_1_3_DRAFT_VERSION
|
||||
|
@ -6256,16 +6261,10 @@ ssl3_HandleServerHello(sslSocket *ss, PRUint8 *b, PRUint32 length)
|
|||
}
|
||||
}
|
||||
|
||||
/* Update the version based on the extension, as necessary. */
|
||||
versionExtension = ssl3_FindExtension(ss, ssl_tls13_supported_versions_xtn);
|
||||
if (versionExtension) {
|
||||
rv = ssl_ClientReadVersion(ss, &versionExtension->data.data,
|
||||
&versionExtension->data.len,
|
||||
&ss->version);
|
||||
if (rv != SECSuccess) {
|
||||
errCode = PORT_GetError();
|
||||
goto loser; /* An alert is sent by ssl_ClientReadVersion */
|
||||
}
|
||||
/* Read supported_versions if present. */
|
||||
rv = tls13_ClientReadSupportedVersion(ss);
|
||||
if (rv != SECSuccess) {
|
||||
goto loser;
|
||||
}
|
||||
|
||||
PORT_Assert(!SSL_ALL_VERSIONS_DISABLED(&ss->vrange));
|
||||
|
@ -6350,8 +6349,9 @@ ssl3_HandleServerHello(sslSocket *ss, PRUint8 *b, PRUint32 length)
|
|||
/* Finally, now all the version-related checks have passed. */
|
||||
ss->ssl3.hs.preliminaryInfo |= ssl_preinfo_version;
|
||||
/* Update the write cipher spec to match the version. But not after
|
||||
* HelloRetryRequest, because cwSpec might be a 0-RTT cipher spec. */
|
||||
if (!ss->firstHsDone && !ss->ssl3.hs.helloRetry) {
|
||||
* HelloRetryRequest, because cwSpec might be a 0-RTT cipher spec,
|
||||
* in which case this is a no-op. */
|
||||
if (!ss->firstHsDone && !isHelloRetry) {
|
||||
ssl_GetSpecWriteLock(ss);
|
||||
ssl_SetSpecVersions(ss, ss->ssl3.cwSpec);
|
||||
ssl_ReleaseSpecWriteLock(ss);
|
||||
|
@ -8848,12 +8848,10 @@ ssl_ConstructServerHello(sslSocket *ss, PRBool helloRetry,
|
|||
SSL3ProtocolVersion version;
|
||||
sslSessionID *sid = ss->sec.ci.sid;
|
||||
|
||||
if (IS_DTLS(ss) && ss->version < SSL_LIBRARY_VERSION_TLS_1_3) {
|
||||
version = dtls_TLSVersionToDTLSVersion(ss->version);
|
||||
} else {
|
||||
version = PR_MIN(ss->version, SSL_LIBRARY_VERSION_TLS_1_2);
|
||||
version = PR_MIN(ss->version, SSL_LIBRARY_VERSION_TLS_1_2);
|
||||
if (IS_DTLS(ss)) {
|
||||
version = dtls_TLSVersionToDTLSVersion(version);
|
||||
}
|
||||
|
||||
rv = sslBuffer_AppendNumber(messageBuf, version, 2);
|
||||
if (rv != SECSuccess) {
|
||||
return SECFailure;
|
||||
|
@ -11847,6 +11845,7 @@ ssl3_UnprotectRecord(sslSocket *ss,
|
|||
unsigned int good;
|
||||
unsigned int ivLen = 0;
|
||||
SSL3ContentType rType;
|
||||
SSL3ProtocolVersion rVersion;
|
||||
unsigned int minLength;
|
||||
unsigned int originalLen = 0;
|
||||
PRUint8 headerBuf[13];
|
||||
|
@ -11919,7 +11918,9 @@ ssl3_UnprotectRecord(sslSocket *ss,
|
|||
return SECFailure;
|
||||
}
|
||||
|
||||
rType = cText->type;
|
||||
rType = (SSL3ContentType)cText->hdr[0];
|
||||
rVersion = ((SSL3ProtocolVersion)cText->hdr[1] << 8) |
|
||||
(SSL3ProtocolVersion)cText->hdr[2];
|
||||
if (cipher_def->type == type_aead) {
|
||||
/* XXX For many AEAD ciphers, the plaintext is shorter than the
|
||||
* ciphertext by a fixed byte count, but it is not true in general.
|
||||
|
@ -11929,8 +11930,8 @@ ssl3_UnprotectRecord(sslSocket *ss,
|
|||
cText->buf->len - cipher_def->explicit_nonce_size -
|
||||
cipher_def->tag_size;
|
||||
rv = ssl3_BuildRecordPseudoHeader(
|
||||
spec->epoch, IS_DTLS(ss) ? cText->seq_num : spec->seqNum,
|
||||
rType, isTLS, cText->version, IS_DTLS(ss), decryptedLen, &header);
|
||||
spec->epoch, cText->seqNum,
|
||||
rType, isTLS, rVersion, IS_DTLS(ss), decryptedLen, &header);
|
||||
PORT_Assert(rv == SECSuccess);
|
||||
rv = spec->aead(&spec->keyMaterial,
|
||||
PR_TRUE, /* do decrypt */
|
||||
|
@ -11977,8 +11978,8 @@ ssl3_UnprotectRecord(sslSocket *ss,
|
|||
|
||||
/* compute the MAC */
|
||||
rv = ssl3_BuildRecordPseudoHeader(
|
||||
spec->epoch, IS_DTLS(ss) ? cText->seq_num : spec->seqNum,
|
||||
rType, isTLS, cText->version, IS_DTLS(ss),
|
||||
spec->epoch, cText->seqNum,
|
||||
rType, isTLS, rVersion, IS_DTLS(ss),
|
||||
plaintext->len - spec->macDef->mac_size, &header);
|
||||
PORT_Assert(rv == SECSuccess);
|
||||
if (cipher_def->type == type_block) {
|
||||
|
@ -12028,13 +12029,19 @@ ssl3_UnprotectRecord(sslSocket *ss,
|
|||
return SECSuccess;
|
||||
}
|
||||
|
||||
static SECStatus
|
||||
SECStatus
|
||||
ssl3_HandleNonApplicationData(sslSocket *ss, SSL3ContentType rType,
|
||||
DTLSEpoch epoch, sslSequenceNumber seqNum,
|
||||
sslBuffer *databuf)
|
||||
{
|
||||
SECStatus rv;
|
||||
|
||||
/* check for Token Presence */
|
||||
if (!ssl3_ClientAuthTokenPresent(ss->sec.ci.sid)) {
|
||||
PORT_SetError(SSL_ERROR_TOKEN_INSERTION_REMOVAL);
|
||||
return SECFailure;
|
||||
}
|
||||
|
||||
ssl_GetSSL3HandshakeLock(ss);
|
||||
|
||||
/* All the functions called in this switch MUST set error code if
|
||||
|
@ -12080,15 +12087,16 @@ ssl3_HandleNonApplicationData(sslSocket *ss, SSL3ContentType rType,
|
|||
* Returns NULL if no appropriate cipher spec is found.
|
||||
*/
|
||||
static ssl3CipherSpec *
|
||||
ssl3_GetCipherSpec(sslSocket *ss, sslSequenceNumber seq)
|
||||
ssl3_GetCipherSpec(sslSocket *ss, SSL3Ciphertext *cText)
|
||||
{
|
||||
ssl3CipherSpec *crSpec = ss->ssl3.crSpec;
|
||||
ssl3CipherSpec *newSpec = NULL;
|
||||
DTLSEpoch epoch = seq >> 48;
|
||||
DTLSEpoch epoch;
|
||||
|
||||
if (!IS_DTLS(ss)) {
|
||||
return crSpec;
|
||||
}
|
||||
epoch = dtls_ReadEpoch(crSpec, cText->hdr);
|
||||
if (crSpec->epoch == epoch) {
|
||||
return crSpec;
|
||||
}
|
||||
|
@ -12128,16 +12136,15 @@ ssl3_GetCipherSpec(sslSocket *ss, sslSequenceNumber seq)
|
|||
* Application Data records.
|
||||
*/
|
||||
SECStatus
|
||||
ssl3_HandleRecord(sslSocket *ss, SSL3Ciphertext *cText, sslBuffer *databuf)
|
||||
ssl3_HandleRecord(sslSocket *ss, SSL3Ciphertext *cText)
|
||||
{
|
||||
SECStatus rv;
|
||||
PRBool isTLS;
|
||||
DTLSEpoch epoch;
|
||||
sslSequenceNumber seqNum = 0;
|
||||
ssl3CipherSpec *spec = NULL;
|
||||
PRBool outOfOrderSpec = PR_FALSE;
|
||||
SSL3ContentType rType;
|
||||
sslBuffer *plaintext;
|
||||
sslBuffer *plaintext = &ss->gs.buf;
|
||||
SSL3AlertDescription alert = internal_error;
|
||||
PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss));
|
||||
|
||||
|
@ -12147,27 +12154,15 @@ ssl3_HandleRecord(sslSocket *ss, SSL3Ciphertext *cText, sslBuffer *databuf)
|
|||
return SECFailure;
|
||||
}
|
||||
|
||||
/* cText is NULL when we're called from ssl3_RestartHandshakeAfterXXX().
|
||||
* This implies that databuf holds a previously deciphered SSL Handshake
|
||||
* message.
|
||||
*/
|
||||
if (cText == NULL) {
|
||||
SSL_DBG(("%d: SSL3[%d]: HandleRecord, resuming handshake",
|
||||
SSL_GETPID(), ss->fd));
|
||||
/* Note that this doesn't pass the epoch and sequence number of the
|
||||
* record through, which DTLS 1.3 depends on. DTLS doesn't support
|
||||
* asynchronous certificate validation, so that should be OK. */
|
||||
PORT_Assert(!IS_DTLS(ss));
|
||||
return ssl3_HandleNonApplicationData(ss, content_handshake,
|
||||
0, 0, databuf);
|
||||
}
|
||||
/* Clear out the buffer in case this exits early. Any data then won't be
|
||||
* processed twice. */
|
||||
plaintext->len = 0;
|
||||
|
||||
ssl_GetSpecReadLock(ss); /******************************************/
|
||||
spec = ssl3_GetCipherSpec(ss, cText->seq_num);
|
||||
spec = ssl3_GetCipherSpec(ss, cText);
|
||||
if (!spec) {
|
||||
PORT_Assert(IS_DTLS(ss));
|
||||
ssl_ReleaseSpecReadLock(ss); /*****************************/
|
||||
databuf->len = 0; /* Needed to ensure data not left around */
|
||||
return SECSuccess;
|
||||
}
|
||||
if (spec != ss->ssl3.crSpec) {
|
||||
|
@ -12178,36 +12173,30 @@ ssl3_HandleRecord(sslSocket *ss, SSL3Ciphertext *cText, sslBuffer *databuf)
|
|||
}
|
||||
isTLS = (PRBool)(spec->version > SSL_LIBRARY_VERSION_3_0);
|
||||
if (IS_DTLS(ss)) {
|
||||
if (!dtls_IsRelevant(ss, spec, cText, &seqNum)) {
|
||||
if (!dtls_IsRelevant(ss, spec, cText, &cText->seqNum)) {
|
||||
ssl_ReleaseSpecReadLock(ss); /*****************************/
|
||||
databuf->len = 0; /* Needed to ensure data not left around */
|
||||
|
||||
return SECSuccess;
|
||||
}
|
||||
} else {
|
||||
seqNum = spec->seqNum + 1;
|
||||
cText->seqNum = spec->nextSeqNum;
|
||||
}
|
||||
if (seqNum >= spec->cipherDef->max_records) {
|
||||
if (cText->seqNum >= spec->cipherDef->max_records) {
|
||||
ssl_ReleaseSpecReadLock(ss); /*****************************/
|
||||
SSL_TRC(3, ("%d: SSL[%d]: read sequence number at limit 0x%0llx",
|
||||
SSL_GETPID(), ss->fd, seqNum));
|
||||
SSL_GETPID(), ss->fd, cText->seqNum));
|
||||
PORT_SetError(SSL_ERROR_TOO_MANY_RECORDS);
|
||||
return SECFailure;
|
||||
}
|
||||
|
||||
plaintext = databuf;
|
||||
plaintext->len = 0; /* filled in by Unprotect call below. */
|
||||
|
||||
/* We're waiting for another ClientHello, which will appear unencrypted.
|
||||
* Use the content type to tell whether this is should be discarded.
|
||||
*
|
||||
* XXX If we decide to remove the content type from encrypted records, this
|
||||
* will become much more difficult to manage. */
|
||||
if (ss->ssl3.hs.zeroRttIgnore == ssl_0rtt_ignore_hrr &&
|
||||
cText->type == content_application_data) {
|
||||
cText->hdr[0] == content_application_data) {
|
||||
ssl_ReleaseSpecReadLock(ss); /*****************************/
|
||||
PORT_Assert(ss->ssl3.hs.ws == wait_client_hello);
|
||||
databuf->len = 0;
|
||||
return SECSuccess;
|
||||
}
|
||||
|
||||
|
@ -12224,6 +12213,7 @@ ssl3_HandleRecord(sslSocket *ss, SSL3Ciphertext *cText, sslBuffer *databuf)
|
|||
}
|
||||
|
||||
#ifdef UNSAFE_FUZZER_MODE
|
||||
rType = cText->hdr[0];
|
||||
rv = Null_Cipher(NULL, plaintext->buf, (int *)&plaintext->len,
|
||||
plaintext->space, cText->buf->buf, cText->buf->len);
|
||||
#else
|
||||
|
@ -12233,9 +12223,10 @@ ssl3_HandleRecord(sslSocket *ss, SSL3Ciphertext *cText, sslBuffer *databuf)
|
|||
if (spec->version < SSL_LIBRARY_VERSION_TLS_1_3 ||
|
||||
spec->cipherDef->calg == ssl_calg_null) {
|
||||
/* Unencrypted TLS 1.3 records use the pre-TLS 1.3 format. */
|
||||
rType = cText->hdr[0];
|
||||
rv = ssl3_UnprotectRecord(ss, spec, cText, plaintext, &alert);
|
||||
} else {
|
||||
rv = tls13_UnprotectRecord(ss, spec, cText, plaintext, &alert);
|
||||
rv = tls13_UnprotectRecord(ss, spec, cText, plaintext, &rType, &alert);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -12245,14 +12236,14 @@ ssl3_HandleRecord(sslSocket *ss, SSL3Ciphertext *cText, sslBuffer *databuf)
|
|||
SSL_DBG(("%d: SSL3[%d]: decryption failed", SSL_GETPID(), ss->fd));
|
||||
|
||||
/* Ensure that we don't process this data again. */
|
||||
databuf->len = 0;
|
||||
plaintext->len = 0;
|
||||
|
||||
/* Ignore a CCS if the alternative handshake is negotiated. Note that
|
||||
* this will fail if the server fails to negotiate the alternative
|
||||
* handshake type in a 0-RTT session that is resumed from a session that
|
||||
* did negotiate it. We don't care about that corner case right now. */
|
||||
if (ss->version >= SSL_LIBRARY_VERSION_TLS_1_3 &&
|
||||
cText->type == content_change_cipher_spec &&
|
||||
cText->hdr[0] == content_change_cipher_spec &&
|
||||
ss->ssl3.hs.ws != idle_handshake &&
|
||||
cText->buf->len == 1 &&
|
||||
cText->buf->buf[0] == change_cipher_spec_choice) {
|
||||
|
@ -12275,9 +12266,11 @@ ssl3_HandleRecord(sslSocket *ss, SSL3Ciphertext *cText, sslBuffer *databuf)
|
|||
}
|
||||
|
||||
/* SECSuccess */
|
||||
spec->seqNum = PR_MAX(spec->seqNum, seqNum);
|
||||
if (IS_DTLS(ss)) {
|
||||
dtls_RecordSetRecvd(&spec->recvdRecords, seqNum);
|
||||
dtls_RecordSetRecvd(&spec->recvdRecords, cText->seqNum);
|
||||
spec->nextSeqNum = PR_MAX(spec->nextSeqNum, cText->seqNum + 1);
|
||||
} else {
|
||||
++spec->nextSeqNum;
|
||||
}
|
||||
epoch = spec->epoch;
|
||||
|
||||
|
@ -12286,19 +12279,18 @@ ssl3_HandleRecord(sslSocket *ss, SSL3Ciphertext *cText, sslBuffer *databuf)
|
|||
/*
|
||||
* The decrypted data is now in plaintext.
|
||||
*/
|
||||
rType = cText->type; /* This must go after decryption because TLS 1.3
|
||||
* has encrypted content types. */
|
||||
|
||||
/* IMPORTANT: We are in DTLS 1.3 mode and we have processed something
|
||||
* from the wrong epoch. Divert to a divert processing function to make
|
||||
* sure we don't accidentally use the data unsafely. */
|
||||
if (outOfOrderSpec) {
|
||||
PORT_Assert(IS_DTLS(ss) && ss->version >= SSL_LIBRARY_VERSION_TLS_1_3);
|
||||
return dtls13_HandleOutOfEpochRecord(ss, spec, rType, databuf);
|
||||
return dtls13_HandleOutOfEpochRecord(ss, spec, rType, plaintext);
|
||||
}
|
||||
|
||||
/* Check the length of the plaintext. */
|
||||
if (isTLS && databuf->len > MAX_FRAGMENT_LENGTH) {
|
||||
if (isTLS && plaintext->len > MAX_FRAGMENT_LENGTH) {
|
||||
plaintext->len = 0;
|
||||
SSL3_SendAlert(ss, alert_fatal, record_overflow);
|
||||
PORT_SetError(SSL_ERROR_RX_RECORD_TOO_LONG);
|
||||
return SECFailure;
|
||||
|
@ -12313,14 +12305,16 @@ ssl3_HandleRecord(sslSocket *ss, SSL3Ciphertext *cText, sslBuffer *databuf)
|
|||
if (ss->version >= SSL_LIBRARY_VERSION_TLS_1_3 &&
|
||||
ss->sec.isServer &&
|
||||
ss->ssl3.hs.zeroRttState == ssl_0rtt_accepted) {
|
||||
return tls13_HandleEarlyApplicationData(ss, databuf);
|
||||
return tls13_HandleEarlyApplicationData(ss, plaintext);
|
||||
}
|
||||
plaintext->len = 0;
|
||||
(void)SSL3_SendAlert(ss, alert_fatal, unexpected_message);
|
||||
PORT_SetError(SSL_ERROR_RX_UNEXPECTED_APPLICATION_DATA);
|
||||
return SECFailure;
|
||||
}
|
||||
|
||||
return ssl3_HandleNonApplicationData(ss, rType, epoch, seqNum, databuf);
|
||||
return ssl3_HandleNonApplicationData(ss, rType, epoch, cText->seqNum,
|
||||
plaintext);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -158,6 +158,7 @@ ssl3_GatherData(sslSocket *ss, sslGather *gs, int flags, ssl2Gather *ssl2gs)
|
|||
* the length of the following encrypted data, and then
|
||||
* read in the rest of the record into gs->inbuf. */
|
||||
gs->remainder = (gs->hdr[3] << 8) | gs->hdr[4];
|
||||
gs->hdrLen = SSL3_RECORD_HEADER_LENGTH;
|
||||
} else {
|
||||
/* Probably an SSLv2 record header. No need to handle any
|
||||
* security escapes (gs->hdr[0] & 0x40) as we wouldn't get
|
||||
|
@ -264,8 +265,9 @@ static int
|
|||
dtls_GatherData(sslSocket *ss, sslGather *gs, int flags)
|
||||
{
|
||||
int nb;
|
||||
int err;
|
||||
int rv = 1;
|
||||
PRUint8 contentType;
|
||||
unsigned int headerLen;
|
||||
SECStatus rv;
|
||||
|
||||
SSL_TRC(30, ("dtls_GatherData"));
|
||||
|
||||
|
@ -285,81 +287,97 @@ dtls_GatherData(sslSocket *ss, sslGather *gs, int flags)
|
|||
** to 13 (the size of the record header).
|
||||
*/
|
||||
if (gs->dtlsPacket.space < MAX_FRAGMENT_LENGTH + 2048 + 13) {
|
||||
err = sslBuffer_Grow(&gs->dtlsPacket,
|
||||
MAX_FRAGMENT_LENGTH + 2048 + 13);
|
||||
if (err) { /* realloc has set error code to no mem. */
|
||||
return err;
|
||||
rv = sslBuffer_Grow(&gs->dtlsPacket,
|
||||
MAX_FRAGMENT_LENGTH + 2048 + 13);
|
||||
if (rv != SECSuccess) {
|
||||
return -1; /* Code already set. */
|
||||
}
|
||||
}
|
||||
|
||||
/* recv() needs to read a full datagram at a time */
|
||||
nb = ssl_DefRecv(ss, gs->dtlsPacket.buf, gs->dtlsPacket.space, flags);
|
||||
|
||||
if (nb > 0) {
|
||||
PRINT_BUF(60, (ss, "raw gather data:", gs->dtlsPacket.buf, nb));
|
||||
} else if (nb == 0) {
|
||||
/* EOF */
|
||||
SSL_TRC(30, ("%d: SSL3[%d]: EOF", SSL_GETPID(), ss->fd));
|
||||
rv = 0;
|
||||
return rv;
|
||||
return 0;
|
||||
} else /* if (nb < 0) */ {
|
||||
SSL_DBG(("%d: SSL3[%d]: recv error %d", SSL_GETPID(), ss->fd,
|
||||
PR_GetError()));
|
||||
rv = SECFailure;
|
||||
return rv;
|
||||
return -1;
|
||||
}
|
||||
|
||||
gs->dtlsPacket.len = nb;
|
||||
}
|
||||
|
||||
contentType = gs->dtlsPacket.buf[gs->dtlsPacketOffset];
|
||||
if (dtls_IsLongHeader(ss->version, contentType)) {
|
||||
headerLen = 13;
|
||||
} else if (contentType == content_application_data) {
|
||||
headerLen = 7;
|
||||
} else if ((contentType & 0xe0) == 0x20) {
|
||||
headerLen = 2;
|
||||
} else {
|
||||
SSL_DBG(("%d: SSL3[%d]: invalid first octet (%d) for DTLS",
|
||||
SSL_GETPID(), ss->fd, contentType));
|
||||
PORT_SetError(SSL_ERROR_RX_UNKNOWN_RECORD_TYPE);
|
||||
gs->dtlsPacketOffset = 0;
|
||||
gs->dtlsPacket.len = 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* At this point we should have >=1 complete records lined up in
|
||||
* dtlsPacket. Read off the header.
|
||||
*/
|
||||
if ((gs->dtlsPacket.len - gs->dtlsPacketOffset) < 13) {
|
||||
if ((gs->dtlsPacket.len - gs->dtlsPacketOffset) < headerLen) {
|
||||
SSL_DBG(("%d: SSL3[%d]: rest of DTLS packet "
|
||||
"too short to contain header",
|
||||
SSL_GETPID(), ss->fd));
|
||||
PR_SetError(PR_WOULD_BLOCK_ERROR, 0);
|
||||
PORT_SetError(PR_WOULD_BLOCK_ERROR);
|
||||
gs->dtlsPacketOffset = 0;
|
||||
gs->dtlsPacket.len = 0;
|
||||
rv = SECFailure;
|
||||
return rv;
|
||||
return -1;
|
||||
}
|
||||
memcpy(gs->hdr, gs->dtlsPacket.buf + gs->dtlsPacketOffset, 13);
|
||||
gs->dtlsPacketOffset += 13;
|
||||
memcpy(gs->hdr, SSL_BUFFER_BASE(&gs->dtlsPacket) + gs->dtlsPacketOffset,
|
||||
headerLen);
|
||||
gs->hdrLen = headerLen;
|
||||
gs->dtlsPacketOffset += headerLen;
|
||||
|
||||
/* Have received SSL3 record header in gs->hdr. */
|
||||
gs->remainder = (gs->hdr[11] << 8) | gs->hdr[12];
|
||||
if (headerLen == 13) {
|
||||
gs->remainder = (gs->hdr[11] << 8) | gs->hdr[12];
|
||||
} else if (headerLen == 7) {
|
||||
gs->remainder = (gs->hdr[5] << 8) | gs->hdr[6];
|
||||
} else {
|
||||
PORT_Assert(headerLen = 2);
|
||||
gs->remainder = gs->dtlsPacket.len - gs->dtlsPacketOffset;
|
||||
}
|
||||
|
||||
if ((gs->dtlsPacket.len - gs->dtlsPacketOffset) < gs->remainder) {
|
||||
SSL_DBG(("%d: SSL3[%d]: rest of DTLS packet too short "
|
||||
"to contain rest of body",
|
||||
SSL_GETPID(), ss->fd));
|
||||
PR_SetError(PR_WOULD_BLOCK_ERROR, 0);
|
||||
PORT_SetError(PR_WOULD_BLOCK_ERROR);
|
||||
gs->dtlsPacketOffset = 0;
|
||||
gs->dtlsPacket.len = 0;
|
||||
rv = SECFailure;
|
||||
return rv;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* OK, we have at least one complete packet, copy into inbuf */
|
||||
if (gs->remainder > gs->inbuf.space) {
|
||||
err = sslBuffer_Grow(&gs->inbuf, gs->remainder);
|
||||
if (err) { /* realloc has set error code to no mem. */
|
||||
return err;
|
||||
}
|
||||
gs->inbuf.len = 0;
|
||||
rv = sslBuffer_Append(&gs->inbuf,
|
||||
SSL_BUFFER_BASE(&gs->dtlsPacket) + gs->dtlsPacketOffset,
|
||||
gs->remainder);
|
||||
if (rv != SECSuccess) {
|
||||
return -1; /* code already set. */
|
||||
}
|
||||
|
||||
SSL_TRC(20, ("%d: SSL3[%d]: dtls gathered record type=%d len=%d",
|
||||
SSL_GETPID(), ss->fd, gs->hdr[0], gs->inbuf.len));
|
||||
|
||||
memcpy(gs->inbuf.buf, gs->dtlsPacket.buf + gs->dtlsPacketOffset,
|
||||
gs->remainder);
|
||||
gs->inbuf.len = gs->remainder;
|
||||
gs->offset = gs->remainder;
|
||||
gs->dtlsPacketOffset += gs->remainder;
|
||||
gs->state = GS_INIT;
|
||||
|
||||
SSL_TRC(20, ("%d: SSL3[%d]: dtls gathered record type=%d len=%d",
|
||||
SSL_GETPID(), ss->fd, contentType, gs->inbuf.len));
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -442,7 +460,11 @@ ssl3_GatherCompleteHandshake(sslSocket *ss, int flags)
|
|||
* We need to process it now before we overwrite it with the next
|
||||
* handshake record.
|
||||
*/
|
||||
rv = ssl3_HandleRecord(ss, NULL, &ss->gs.buf);
|
||||
SSL_DBG(("%d: SSL3[%d]: resuming handshake",
|
||||
SSL_GETPID(), ss->fd));
|
||||
PORT_Assert(!IS_DTLS(ss));
|
||||
rv = ssl3_HandleNonApplicationData(ss, content_handshake,
|
||||
0, 0, &ss->gs.buf);
|
||||
} else {
|
||||
/* State for SSLv2 client hello support. */
|
||||
ssl2Gather ssl2gs = { PR_FALSE, 0 };
|
||||
|
@ -495,20 +517,14 @@ ssl3_GatherCompleteHandshake(sslSocket *ss, int flags)
|
|||
* If it's application data, ss->gs.buf will not be empty upon return.
|
||||
* If it's a change cipher spec, alert, or handshake message,
|
||||
* ss->gs.buf.len will be 0 when ssl3_HandleRecord returns SECSuccess.
|
||||
*
|
||||
* cText only needs to be valid for this next function call, so
|
||||
* it can borrow gs.hdr.
|
||||
*/
|
||||
cText.type = (SSL3ContentType)ss->gs.hdr[0];
|
||||
cText.version = (ss->gs.hdr[1] << 8) | ss->gs.hdr[2];
|
||||
|
||||
if (IS_DTLS(ss)) {
|
||||
sslSequenceNumber seq_num;
|
||||
|
||||
/* DTLS sequence number */
|
||||
PORT_Memcpy(&seq_num, &ss->gs.hdr[3], sizeof(seq_num));
|
||||
cText.seq_num = PR_ntohll(seq_num);
|
||||
}
|
||||
|
||||
cText.hdr = ss->gs.hdr;
|
||||
cText.hdrLen = ss->gs.hdrLen;
|
||||
cText.buf = &ss->gs.inbuf;
|
||||
rv = ssl3_HandleRecord(ss, &cText, &ss->gs.buf);
|
||||
rv = ssl3_HandleRecord(ss, &cText);
|
||||
}
|
||||
}
|
||||
if (rv < 0) {
|
||||
|
@ -520,7 +536,6 @@ ssl3_GatherCompleteHandshake(sslSocket *ss, int flags)
|
|||
* completing any renegotiation handshake we may be doing.
|
||||
*/
|
||||
PORT_Assert(ss->firstHsDone);
|
||||
PORT_Assert(cText.type == content_application_data);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ typedef PRUint16 SSL3ProtocolVersion;
|
|||
/* The TLS 1.3 draft version. Used to avoid negotiating
|
||||
* between incompatible pre-standard TLS 1.3 drafts.
|
||||
* TODO(ekr@rtfm.com): Remove when TLS 1.3 is published. */
|
||||
#define TLS_1_3_DRAFT_VERSION 23
|
||||
#define TLS_1_3_DRAFT_VERSION 26
|
||||
|
||||
typedef PRUint16 ssl3CipherSuite;
|
||||
/* The cipher suites are defined in sslproto.h */
|
||||
|
|
|
@ -261,6 +261,7 @@ typedef struct sslOptionsStr {
|
|||
unsigned int requireDHENamedGroups : 1;
|
||||
unsigned int enable0RttData : 1;
|
||||
unsigned int enableTls13CompatMode : 1;
|
||||
unsigned int enableDtlsShortHeader : 1;
|
||||
} sslOptions;
|
||||
|
||||
typedef enum { sslHandshakingUndetermined = 0,
|
||||
|
@ -325,9 +326,11 @@ struct sslGatherStr {
|
|||
** than into buf or inbuf, while in the GS_HEADER state.
|
||||
** The portion of the SSL record header put here always comes off the wire
|
||||
** as plaintext, never ciphertext.
|
||||
** For SSL3/TLS, the plaintext portion is 5 bytes long. For DTLS it is 13.
|
||||
** For SSL3/TLS, the plaintext portion is 5 bytes long. For DTLS it
|
||||
** varies based on version and header type.
|
||||
*/
|
||||
unsigned char hdr[13];
|
||||
unsigned int hdrLen;
|
||||
|
||||
/* Buffer for DTLS data read off the wire as a single datagram */
|
||||
sslBuffer dtlsPacket;
|
||||
|
@ -780,9 +783,13 @@ struct ssl3StateStr {
|
|||
#define IS_DTLS(ss) (ss->protocolVariant == ssl_variant_datagram)
|
||||
|
||||
typedef struct {
|
||||
SSL3ContentType type;
|
||||
SSL3ProtocolVersion version;
|
||||
sslSequenceNumber seq_num; /* DTLS only */
|
||||
/* |seqNum| eventually contains the reconstructed sequence number. */
|
||||
sslSequenceNumber seqNum;
|
||||
/* The header of the cipherText. */
|
||||
const PRUint8 *hdr;
|
||||
unsigned int hdrLen;
|
||||
|
||||
/* |buf| is the payload of the ciphertext. */
|
||||
sslBuffer *buf;
|
||||
} SSL3Ciphertext;
|
||||
|
||||
|
@ -1375,8 +1382,11 @@ SECStatus ssl3_SendClientHello(sslSocket *ss, sslClientHelloType type);
|
|||
/*
|
||||
* input into the SSL3 machinery from the actualy network reading code
|
||||
*/
|
||||
SECStatus ssl3_HandleRecord(
|
||||
sslSocket *ss, SSL3Ciphertext *cipher, sslBuffer *out);
|
||||
SECStatus ssl3_HandleRecord(sslSocket *ss, SSL3Ciphertext *cipher);
|
||||
SECStatus ssl3_HandleNonApplicationData(sslSocket *ss, SSL3ContentType rType,
|
||||
DTLSEpoch epoch,
|
||||
sslSequenceNumber seqNum,
|
||||
sslBuffer *databuf);
|
||||
SECStatus ssl_RemoveTLSCBCPadding(sslBuffer *plaintext, unsigned int macSize);
|
||||
|
||||
int ssl3_GatherAppDataRecord(sslSocket *ss, int flags);
|
||||
|
@ -1636,6 +1646,9 @@ SSLHashType ssl_SignatureSchemeToHashType(SSLSignatureScheme scheme);
|
|||
KeyType ssl_SignatureSchemeToKeyType(SSLSignatureScheme scheme);
|
||||
|
||||
SECStatus ssl3_SetupCipherSuite(sslSocket *ss, PRBool initHashes);
|
||||
SECStatus ssl_InsertRecordHeader(const sslSocket *ss, ssl3CipherSpec *cwSpec,
|
||||
SSL3ContentType contentType, sslBuffer *wrBuf,
|
||||
PRBool *needsLength);
|
||||
|
||||
/* Pull in DTLS functions */
|
||||
#include "dtlscon.h"
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче