Merge inbound to mozilla-central. a=merge

This commit is contained in:
Tiberius Oros 2018-03-21 12:03:08 +02:00
commit 649398dfd9
301 changed files with 6677 additions and 7239 deletions

View File

@ -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.

View File

@ -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);
}
},

View File

@ -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) {

View File

@ -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);
},

View File

@ -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]

View File

@ -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";

View File

@ -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`);

View File

@ -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 => {

View File

@ -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";

View File

@ -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;

View File

@ -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");

View File

@ -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) {

View File

@ -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) {

View File

@ -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";

View File

@ -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;
}
}

View File

@ -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

View File

@ -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.");
});

View File

@ -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)

View File

@ -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 */ }
},
}));
}

View File

@ -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);

View File

@ -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();
}

View File

@ -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);

View File

@ -82,7 +82,6 @@ FINAL_LIBRARY = 'xul'
TEST_DIRS += [
'test/extensions/bootstrap',
'test/extensions/traditional',
]
MOCHITEST_MANIFESTS += [

View File

@ -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]

View File

@ -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']

View File

@ -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]);

View File

@ -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

View File

@ -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>

View File

@ -1,3 +0,0 @@
worker.jar:
% content worker %content/
content/worker.js (worker.js)

View File

@ -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',
]

View File

@ -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;
};

View File

@ -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);
}

View File

@ -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>

View File

@ -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>

View File

@ -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

View File

@ -698,6 +698,7 @@ EditorSpellCheck::UninitSpellChecker()
DeleteSuggestedWordList();
mDictionaryList.Clear();
mDictionaryIndex = 0;
mDictionaryFetcherGroup++;
mSpellChecker = nullptr;
return NS_OK;
}

View File

@ -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);

View File

@ -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

View File

@ -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) {

View File

@ -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();

View File

@ -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)
{

View File

@ -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;

View File

@ -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;
}

View File

@ -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;

View File

@ -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|.

View File

@ -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.')

View File

@ -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;
};

View File

@ -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
*/

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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
}

View File

@ -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

View File

@ -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"

View File

@ -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))

View File

@ -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,

View File

@ -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);
},

View File

@ -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");
}
}

View File

@ -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);
}

View File

@ -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()

View File

@ -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"]

View File

@ -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);

View File

@ -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

View File

@ -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() {

View File

@ -1 +1 @@
f0d4789c8916
c5dffd6269ea

View File

@ -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;
}

View File

@ -10,3 +10,4 @@
*/
#error "Do not include this header file."

View 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)) {

View File

@ -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);

View File

@ -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);
}

View File

@ -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);

View File

@ -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));
}

View File

@ -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

View File

@ -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();
}

View File

@ -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

View File

@ -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;

View File

@ -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};

View File

@ -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

View File

@ -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) {

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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,

View File

@ -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",

View File

@ -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

View File

@ -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);

View File

@ -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);
}
/*

View File

@ -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;
}

View File

@ -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 */

View File

@ -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"

View File

@ -791,7 +791,7 @@ tls13_CheckKeyUpdate(sslSocket *ss, CipherSpecDirection dir)
spec = ss->ssl3.cwSpec;
margin = spec->cipherDef->max_records / 4;
}
seqNum = spec->seqNum;
seqNum = spec->nextSeqNum;
keyUpdate = seqNum > spec->cipherDef->max_records - margin;
ssl_ReleaseSpecReadLock(ss);
if (!keyUpdate) {

View File

@ -81,7 +81,8 @@ static sslOptions ssl_defaults = {
.enableSignedCertTimestamps = PR_FALSE,
.requireDHENamedGroups = PR_FALSE,
.enable0RttData = PR_FALSE,
.enableTls13CompatMode = PR_FALSE
.enableTls13CompatMode = PR_FALSE,
.enableDtlsShortHeader = PR_FALSE
};
/*
@ -807,6 +808,10 @@ SSL_OptionSet(PRFileDesc *fd, PRInt32 which, PRIntn val)
ss->opt.enableTls13CompatMode = val;
break;
case SSL_ENABLE_DTLS_SHORT_HEADER:
ss->opt.enableDtlsShortHeader = val;
break;
default:
PORT_SetError(SEC_ERROR_INVALID_ARGS);
rv = SECFailure;
@ -943,6 +948,9 @@ SSL_OptionGet(PRFileDesc *fd, PRInt32 which, PRIntn *pVal)
case SSL_ENABLE_TLS13_COMPAT_MODE:
val = ss->opt.enableTls13CompatMode;
break;
case SSL_ENABLE_DTLS_SHORT_HEADER:
val = ss->opt.enableDtlsShortHeader;
break;
default:
PORT_SetError(SEC_ERROR_INVALID_ARGS);
rv = SECFailure;
@ -1063,6 +1071,9 @@ SSL_OptionGetDefault(PRInt32 which, PRIntn *pVal)
case SSL_ENABLE_TLS13_COMPAT_MODE:
val = ssl_defaults.enableTls13CompatMode;
break;
case SSL_ENABLE_DTLS_SHORT_HEADER:
val = ssl_defaults.enableDtlsShortHeader;
break;
default:
PORT_SetError(SEC_ERROR_INVALID_ARGS);
rv = SECFailure;
@ -1246,6 +1257,10 @@ SSL_OptionSetDefault(PRInt32 which, PRIntn val)
ssl_defaults.enableTls13CompatMode = val;
break;
case SSL_ENABLE_DTLS_SHORT_HEADER:
ssl_defaults.enableDtlsShortHeader = val;
break;
default:
PORT_SetError(SEC_ERROR_INVALID_ARGS);
return SECFailure;

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