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

This commit is contained in:
Brindusan Cristian 2018-09-14 12:56:57 +03:00
commit 79316fbec0
59 changed files with 1694 additions and 249 deletions

View File

@ -22,3 +22,4 @@ exclude =
testing/mochitest/pywebsocket,
tools/lint/test/files,
build/build-infer/build-infer.py,
tools/infer/test/*.configure,

View File

@ -74,6 +74,7 @@ _OPT\.OBJ/
# Gradle cache.
^.gradle/
^tools/infer/test/.gradle/
# Local Gradle configuration properties.
^local.properties$

View File

@ -4,53 +4,7 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# Java detection
# ========================================================
option('--with-java-bin-path', nargs=1,
help='Location of Java binaries (java, javac, jar)')
@depends('--with-java-bin-path')
@imports(_from='os', _import='environ')
def java_search_paths(path):
if path:
# Look for javac and jar in the specified path.
return path
# With no path specified, look for javac and jar in $JAVA_HOME (if set)
# and $PATH.
if 'JAVA_HOME' in environ:
return [os.path.join(environ['JAVA_HOME'], 'bin'),
environ.get('PATH', '')]
return [environ.get('PATH')]
# Finds the given java tool, failing with a custom error message if we can't
# find it.
@template
def check_java_tool(tool):
check = check_prog(tool.upper(), (tool,), paths=java_search_paths,
allow_missing=True)
@depends(check)
def require_tool(result):
if result is None:
die("The program %s was not found. Set $JAVA_HOME to your Java "
"SDK directory or use '--with-java-bin-path={java-bin-dir}'"
% tool)
return result
return require_tool
check_java_tool('java')
check_java_tool('javah')
check_java_tool('jar')
check_java_tool('jarsigner')
check_java_tool('keytool')
javac = check_java_tool('javac')
include('java_common.configure')
@depends(javac)
@checking('for javac version')

View File

@ -0,0 +1,51 @@
# -*- 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/.
# Java detection
# ========================================================
option('--with-java-bin-path', nargs=1,
help='Location of Java binaries (java, javac, jar)')
@depends('--with-java-bin-path')
@imports(_from='os', _import='environ')
def java_search_paths(path):
if path:
# Look for javac and jar in the specified path.
return path
# With no path specified, look for javac and jar in $JAVA_HOME (if set)
# and $PATH.
if 'JAVA_HOME' in environ:
return [os.path.join(environ['JAVA_HOME'], 'bin'),
environ.get('PATH', '')]
return [environ.get('PATH')]
# Finds the given java tool, failing with a custom error message if we can't
# find it.
@template
def check_java_tool(tool):
check = check_prog(tool.upper(), (tool,), paths=java_search_paths,
allow_missing=True)
@depends(check)
def require_tool(result):
if result is None:
die("The program %s was not found. Set $JAVA_HOME to your Java "
"SDK directory or use '--with-java-bin-path={java-bin-dir}'"
% tool)
return result
return require_tool
check_java_tool('java')
check_java_tool('javah')
check_java_tool('jar')
check_java_tool('jarsigner')
check_java_tool('keytool')
javac = check_java_tool('javac')

View File

@ -84,7 +84,7 @@ else:
elif CONFIG['FFI_TARGET'] == 'X86_WIN32':
ffi_srcs = ['ffi.c']
# MinGW Build for 32 bit
if CONFIG['CC_TYPE'] == 'gcc':
if CONFIG['CC_TYPE'] in ('gcc', 'clang'):
DEFINES['SYMBOL_UNDERSCORE'] = True
ffi_srcs += ['win32.S']
else:

View File

@ -10,6 +10,11 @@ const L10N = new LocalizationHelper("devtools/client/locales/toolbox.properties"
const DevToolsUtils = require("devtools/shared/DevToolsUtils");
const Telemetry = require("devtools/client/shared/telemetry");
// The min-width of toolbox and browser toolbox.
const WIDTH_CHEVRON_AND_MEATBALL = 50;
const WIDTH_CHEVRON_AND_MEATBALL_AND_CLOSE = 74;
const ZOOM_VALUE_PREF = "devtools.toolbox.zoomValue";
loader.lazyRequireGetter(this, "Toolbox", "devtools/client/framework/toolbox", true);
loader.lazyRequireGetter(this, "Hosts", "devtools/client/framework/toolbox-hosts", true);
@ -54,6 +59,8 @@ function ToolboxHostManager(target, hostType, hostOptions) {
this.host = this.createHost(hostType, hostOptions);
this.hostType = hostType;
this.telemetry = new Telemetry();
this.setMinWidthWithZoom = this.setMinWidthWithZoom.bind(this);
Services.prefs.addObserver(ZOOM_VALUE_PREF, this.setMinWidthWithZoom);
}
ToolboxHostManager.prototype = {
@ -81,9 +88,27 @@ ToolboxHostManager.prototype = {
// access to the toolbox internals in order to get the session ID.
this.host.frame.setAttribute("session_id", msSinceProcessStart);
this.setMinWidthWithZoom();
return toolbox;
},
setMinWidthWithZoom: function() {
const zoomValue =
parseFloat(Services.prefs.getCharPref(ZOOM_VALUE_PREF));
if (isNaN(zoomValue)) {
return;
}
if (this.hostType === Toolbox.HostType.LEFT ||
this.hostType === Toolbox.HostType.RIGHT) {
this.host.frame.minWidth = WIDTH_CHEVRON_AND_MEATBALL_AND_CLOSE * zoomValue;
} else if (this.hostType === Toolbox.HostType.WINDOW ||
this.hostType === Toolbox.HostType.CUSTOM) {
this.host.frame.minWidth = WIDTH_CHEVRON_AND_MEATBALL * zoomValue;
}
},
handleEvent(event) {
switch (event.type) {
case "message":
@ -137,6 +162,7 @@ ToolboxHostManager.prototype = {
},
destroy() {
Services.prefs.removeObserver(ZOOM_VALUE_PREF, this.setMinWidthWithZoom);
this.destroyHost();
this.host = null;
this.hostType = null;
@ -199,6 +225,8 @@ ToolboxHostManager.prototype = {
this.host.frame.ownerDocument.defaultView.addEventListener("message", this);
this.host.frame.addEventListener("unload", this, true);
this.setMinWidthWithZoom();
if (hostType != Toolbox.HostType.CUSTOM) {
Services.prefs.setCharPref(LAST_HOST, hostType);
}

View File

@ -5,8 +5,7 @@
<html id="devtools-toolbox-window"
windowtype="devtools:toolbox"
width="900" height="350"
persist="screenX screenY width height sizemode"
minwidth="50">
persist="screenX screenY width height sizemode">
<head>
<link rel="stylesheet" href="chrome://global/skin/"/>
<link rel="stylesheet" href="resource://devtools/client/themes/common.css"/>

View File

@ -16,8 +16,7 @@
fullscreenbutton="true"
windowtype="devtools:toolbox"
width="900" height="320"
persist="screenX screenY width height sizemode"
minwidth="50">
persist="screenX screenY width height sizemode">
<commandset id="toolbox-commandset">
<command id="toolbox-cmd-close" oncommand="window.close();"/>

View File

@ -4,12 +4,6 @@
@import url("resource://devtools/client/themes/splitters.css");
.devtools-toolbox-side-iframe {
/* Toolbar should display the chevron and meatball (and close) button.
This size is sum of chevron and meatball and close button. */
min-width: 74px;
}
/* Eyedropper Widget */
/* <panel> added to mainPopupSet */

View File

@ -684,6 +684,22 @@ IsSelectionInsideRuby(Selection* aSelection)
return true;
}
static Element*
GetElementOrNearestFlattenedTreeParentElement(nsINode* aNode)
{
if (!aNode->IsContent()) {
return nullptr;
}
for (nsIContent* content = aNode->AsContent();
content;
content = content->GetFlattenedTreeParent()) {
if (content->IsElement()) {
return content->AsElement();
}
}
return nullptr;
}
bool
nsCopySupport::FireClipboardEvent(EventMessage aEventMessage,
int32_t aClipboardType,
@ -716,29 +732,32 @@ nsCopySupport::FireClipboardEvent(EventMessage aEventMessage,
if (!piWindow)
return false;
// if a selection was not supplied, try to find it
nsCOMPtr<nsIContent> content;
// Event target of clipboard events should be an element node which
// contains selection start container.
RefPtr<Element> targetElement;
// If a selection was not supplied, try to find it.
RefPtr<Selection> sel = aSelection;
if (!sel) {
content = GetSelectionForCopy(doc, getter_AddRefs(sel));
GetSelectionForCopy(doc, getter_AddRefs(sel));
}
// retrieve the event target node from the start of the selection
// Retrieve the event target node from the start of the selection.
if (sel) {
RefPtr<nsRange> range = sel->GetRangeAt(0);
nsRange* range = sel->GetRangeAt(0);
if (range) {
nsINode* startContainer = range->GetStartContainer();
if (startContainer) {
content = do_QueryInterface(startContainer);
}
targetElement =
GetElementOrNearestFlattenedTreeParentElement(
range->GetStartContainer());
}
}
// if no content node was set, just get the root
if (!content) {
content = doc->GetRootElement();
if (!content)
// If there is no selection ranges, use the <body> or <frameset> element.
if (!targetElement) {
targetElement = doc->GetBody();
if (!targetElement) {
return false;
}
}
// It seems to be unsafe to fire an event handler during reflow (bug 393696)
@ -762,7 +781,7 @@ nsCopySupport::FireClipboardEvent(EventMessage aEventMessage,
nsEventStatus status = nsEventStatus_eIgnore;
InternalClipboardEvent evt(true, originalEventMessage);
evt.mClipboardData = clipboardData;
EventDispatcher::Dispatch(content, presShell->GetPresContext(), &evt,
EventDispatcher::Dispatch(targetElement, presShell->GetPresContext(), &evt,
nullptr, &status);
// If the event was cancelled, don't do the clipboard operation
doDefault = (status != nsEventStatus_eConsumeNoDefault);
@ -807,13 +826,13 @@ nsCopySupport::FireClipboardEvent(EventMessage aEventMessage,
uint32_t count = 0;
if (doDefault) {
// find the focused node
nsCOMPtr<nsIContent> srcNode = content;
if (content->IsInNativeAnonymousSubtree()) {
srcNode = content->FindFirstNonChromeOnlyAccessContent();
nsIContent* sourceContent = targetElement.get();
if (targetElement->IsInNativeAnonymousSubtree()) {
sourceContent = targetElement->FindFirstNonChromeOnlyAccessContent();
}
// check if we are looking at a password input
nsCOMPtr<nsIFormControl> formControl = do_QueryInterface(srcNode);
nsCOMPtr<nsIFormControl> formControl = do_QueryInterface(sourceContent);
if (formControl) {
if (formControl->ControlType() == NS_FORM_INPUT_PASSWORD) {
return false;
@ -822,7 +841,7 @@ nsCopySupport::FireClipboardEvent(EventMessage aEventMessage,
// when cutting non-editable content, do nothing
// XXX this is probably the wrong editable flag to check
if (originalEventMessage != eCut || content->IsEditable()) {
if (originalEventMessage != eCut || targetElement->IsEditable()) {
// get the data from the selection if any
if (sel->IsCollapsed()) {
if (aActionTaken) {

View File

@ -3361,7 +3361,7 @@ nsIDocument::LocalizationLinkAdded(Element* aLinkElement)
AutoTArray<nsString, 1> resourceIds;
resourceIds.AppendElement(href);
mDocumentL10n->AddResourceIds(resourceIds);
} else if (mReadyState == READYSTATE_COMPLETE) {
} else if (mReadyState >= READYSTATE_INTERACTIVE) {
// Otherwise, if the document has already been parsed
// we need to lazily initialize the localization.
AutoTArray<nsString, 1> resourceIds;

View File

@ -54,8 +54,8 @@ public:
name = NS_LITERAL_STRING("fullscreenerror");
break;
}
nsINode* target =
mTarget->GetComposedDoc() == mDocument ? mTarget : mDocument;
nsINode* target = mTarget->GetComposedDoc() == mDocument
? mTarget.get() : mDocument.get();
Unused << nsContentUtils::DispatchTrustedEvent(
mDocument, target, name,
CanBubble::eYes, Cancelable::eNo, Composed::eYes);

View File

@ -18,10 +18,13 @@
oncopy="compareSynthetic(event, 'copy')"
onpaste="compareSynthetic(event, 'paste')">Spot</div>
<div id="contenteditableContainer"></div>
<pre id="test">
<script class="testbody" type="text/javascript">
var content = document.getElementById("content");
var contentInput = document.getElementById("content-input");
var contenteditableContainer = document.getElementById("contenteditableContainer");
var clipboardInitialValue = "empty";
var cachedCutData, cachedCopyData, cachedPasteData;
@ -768,6 +771,44 @@ add_task(async function test_image_dataTransfer() {
}
});
add_task(async function test_event_target() {
await reset();
let copyTarget = null;
document.addEventListener("copy", (event) => { copyTarget = event.target; }, {once: true});
if (document.activeElement) {
document.activeElement.blur();
}
let selection = document.getSelection();
selection.setBaseAndExtent(content.firstChild, "CONTENT ".length,
content.firstChild, "CONTENT TEXT".length);
await putOnClipboard("TEXT", () => {
synthesizeKey("c", {accelKey: 1});
}, "copy text from non-editable element");
is(copyTarget.getAttribute("id"), "content", "Copy event's target should be always an element");
// Create a contenteditable element to check complicated event target.
contenteditableContainer.innerHTML = '<div contenteditable><p id="p1">foo</p><p id="p2">bar</p></div>';
contenteditableContainer.firstChild.focus();
let p1 = document.getElementById("p1");
let p2 = document.getElementById("p2");
selection.setBaseAndExtent(p1.firstChild, 1, p2.firstChild, 1);
let pasteTarget = null;
document.addEventListener("paste", (event) => { pasteTarget = event.target; }, {once: true});
synthesizeKey("v", {accelKey: 1});
is(pasteTarget.getAttribute("id"), "p1",
"'paste' event's target should be always an element which includes start container of the first Selection range");
contenteditableContainer.innerHTML = "";
});
</script>
</pre>
</body>

View File

@ -44,30 +44,32 @@ async function copyPlaintext(aText) {
resolve();
},
() => {
ok(false, `Failed to copy "${aText}" to clipboard`);
SimpleTest.finish();
});
});
}
async function copyHTMLContent(aInnerHTML) {
let iframe = document.getElementById("toCopyHTMLContent");
iframe.style.display = "block";
iframe.contentDocument.body.scrollTop;
iframe.contentDocument.body.innerHTML = aInnerHTML;
iframe.contentWindow.focus();
iframe.contentWindow.getSelection().selectAllChildren(iframe.contentDocument.body);
return new Promise(resolve => {
SimpleTest.waitForClipboard(
() => { return true; },
() => {
let element = document.getElementById("toCopyHTMLContent");
element.style.display = "block";
element.contentDocument.body.innerHTML = aInnerHTML;
element.contentWindow.focus();
element.contentDocument.getSelection().selectAllChildren(element.contentDocument.body);
synthesizeKey("c", {accelKey: true}, element.contentWindow);
synthesizeKey("c", {accelKey: true}, iframe.contentWindow);
},
() => {
ok(true, `Succeeded to copy "${aInnerHTML}" to clipboard as HTML`);
let element = document.getElementById("toCopyHTMLContent");
element.style.display = "none";
iframe.style.display = "none";
resolve();
},
() => {
ok(false, `Failed to copy "${aInnerHTML}" to clipboard`);
SimpleTest.finish();
},
"text/html");
@ -117,12 +119,24 @@ async function doContenteditableTests(aEditableDiv) {
"Pasted plaintext should be in <blockquote> element and each linebreaker should be <br> element");
aEditableDiv.innerHTML = "";
// Oddly, copyHTMLContent fails randomly only on Linux. Let's skip this.
if (navigator.platform.startsWith("Linux")) {
return;
}
await copyHTMLContent("<p>abc</p><p>def</p><p>ghi</p>");
aEditableDiv.focus();
synthesizeMouseAtCenter(aEditableDiv, {button: 1, ctrlKey: true});
is(aEditableDiv.innerHTML,
"<blockquote type=\"cite\"><p>abc</p><p>def</p><p>ghi</p></blockquote>",
"Pasted HTML content should be set to the <blockquote>");
if (!navigator.appVersion.includes("Android")) {
is(aEditableDiv.innerHTML,
"<blockquote type=\"cite\"><p>abc</p><p>def</p><p>ghi</p></blockquote>",
"Pasted HTML content should be set to the <blockquote>");
} else {
// Oddly, on Android, we use <br> elements for pasting <p> elements.
is(aEditableDiv.innerHTML,
"<blockquote type=\"cite\">abc<br><br>def<br><br>ghi</blockquote>",
"Pasted HTML content should be set to the <blockquote>");
}
aEditableDiv.innerHTML = "";
}

View File

@ -45,7 +45,7 @@ fails-if(Android) needs-focus != spellcheck-input-property-dynamic-override-inhe
random-if(Android) needs-focus != spellcheck-textarea-attr.html spellcheck-textarea-ref.html
needs-focus == spellcheck-textarea-focused.html spellcheck-textarea-ref.html
needs-focus == spellcheck-textarea-focused-reframe.html spellcheck-textarea-ref.html
needs-focus == spellcheck-textarea-focused-notreadonly.html spellcheck-textarea-ref2.html
skip-if(Android) needs-focus == spellcheck-textarea-focused-notreadonly.html spellcheck-textarea-ref2.html
random-if(Android) needs-focus != spellcheck-textarea-nofocus.html spellcheck-textarea-ref.html
random-if(Android) needs-focus != spellcheck-textarea-disabled.html spellcheck-textarea-ref.html
random-if(Android) needs-focus != spellcheck-textarea-attr-inherit.html spellcheck-textarea-ref.html

View File

@ -20,6 +20,8 @@
[dom/test_mozdom_translateRoots.html]
[dom/test_docl10n.xul]
[dom/test_docl10n_initialize_after_parse.xhtml]
[dom/test_docl10n_initialize_after_parse.xul]
[dom/test_docl10n.xhtml]
[dom/test_docl10n.html]
[dom/test_docl10n_ready_rejected.html]

View File

@ -0,0 +1,33 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8"></meta>
<title>Test lazy injecting l10n resource links in XHTML environment</title>
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"></link>
<script type="application/javascript">
"use strict";
SimpleTest.waitForExplicitFinish();
document.addEventListener("DOMContentLoaded", async function() {
ok(!document.l10n, "l10n not initialized");
MozXULElement.insertFTLIfNeeded("crashreporter/aboutcrashes.ftl");
ok(document.querySelector("head link[rel=localization][href='crashreporter/aboutcrashes.ftl']"), "link exists");
ok(document.l10n, "l10n initialized");
await document.l10n.ready;
ok(document.getElementById("main-desc").textContent.length > 0, "Text updated after init");
info("Confirming a second call doesn't inject another link");
MozXULElement.insertFTLIfNeeded("crashreporter/aboutcrashes.ftl");
is(document.querySelectorAll("head link[rel=localization]").length, 1, "link exists");
SimpleTest.finish();
});
</script>
</head>
<body>
<h1 id="main-desc" data-l10n-id="crash-reports-title"></h1>
</body>
</html>

View File

@ -0,0 +1,38 @@
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
type="text/css"?>
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns:html="http://www.w3.org/1999/xhtml"
title="Test lazy injecting l10n resource links in XHTML environment">
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
<script type="application/javascript">
<![CDATA[
SimpleTest.waitForExplicitFinish();
document.addEventListener("readystatechange", async function() {
if (document.readyState != "complete") {
return;
}
ok(!document.l10n, "l10n not initialized");
MozXULElement.insertFTLIfNeeded("crashreporter/aboutcrashes.ftl");
ok(document.querySelector("linkset link[rel=localization][href='crashreporter/aboutcrashes.ftl']"), "link exists");
ok(document.l10n, "l10n initialized");
await document.l10n.ready;
ok(document.getElementById("main-desc").textContent.length > 0, "Text updated after init");
info("Confirming a second call doesn't inject another link");
MozXULElement.insertFTLIfNeeded("crashreporter/aboutcrashes.ftl");
is(document.querySelectorAll("linkset link[rel=localization]").length, 1, "link exists");
SimpleTest.finish();
});
]]>
</script>
<description id="main-desc" data-l10n-id="crash-reports-title"/>
</window>

View File

@ -88,7 +88,7 @@ with Files('counter-style/**'):
with Files('counters/**'):
BUG_COMPONENT = ('Core', 'Layout')
with Files('css-animations/**'):
BUG_COMPONENT = ('Core', 'CSS Parsing and Computation')
BUG_COMPONENT = ('Core', 'CSS Transitions and Animations')
with Files('css-blending/**'):
BUG_COMPONENT = ('Core', 'Layout')
with Files('css-break/**'):
@ -130,7 +130,7 @@ with Files('css-selectors/**'):
with Files('css-submit-invalid/**'):
BUG_COMPONENT = ('Core', 'Layout: Form Controls')
with Files('css-transitions/**'):
BUG_COMPONENT = ('Core', 'CSS Parsing and Computation')
BUG_COMPONENT = ('Core', 'CSS Transitions and Animations')
with Files('css-ui-invalid/**'):
BUG_COMPONENT = ('Core', 'Layout: Form Controls')
with Files('css-ui-valid/**'):

View File

@ -26,6 +26,16 @@ SrtpFlow::~SrtpFlow() {
}
}
unsigned int SrtpFlow::KeySize(int cipher_suite) {
srtp_profile_t profile = static_cast<srtp_profile_t>(cipher_suite);
return srtp_profile_get_master_key_length(profile);
}
unsigned int SrtpFlow::SaltSize(int cipher_suite) {
srtp_profile_t profile = static_cast<srtp_profile_t>(cipher_suite);
return srtp_profile_get_master_salt_length(profile);
}
RefPtr<SrtpFlow> SrtpFlow::Create(int cipher_suite,
bool inbound,
const void *key,
@ -41,7 +51,8 @@ RefPtr<SrtpFlow> SrtpFlow::Create(int cipher_suite,
return nullptr;
}
if (key_len != SRTP_TOTAL_KEY_LENGTH) {
if ((key_len > SRTP_MAX_KEY_LENGTH) ||
(key_len < SRTP_MIN_KEY_LENGTH)) {
MOZ_MTLOG(ML_ERROR, "Invalid SRTP key length");
return nullptr;
}
@ -52,6 +63,18 @@ RefPtr<SrtpFlow> SrtpFlow::Create(int cipher_suite,
// Note that we set the same cipher suite for RTP and RTCP
// since any flow can only have one cipher suite with DTLS-SRTP
switch (cipher_suite) {
case kDtlsSrtpAeadAes256Gcm:
MOZ_MTLOG(ML_DEBUG,
"Setting SRTP cipher suite SRTP_AEAD_AES_256_GCM");
srtp_crypto_policy_set_aes_gcm_256_16_auth(&policy.rtp);
srtp_crypto_policy_set_aes_gcm_256_16_auth(&policy.rtcp);
break;
case kDtlsSrtpAeadAes128Gcm:
MOZ_MTLOG(ML_DEBUG,
"Setting SRTP cipher suite SRTP_AEAD_AES_128_GCM");
srtp_crypto_policy_set_aes_gcm_128_16_auth(&policy.rtp);
srtp_crypto_policy_set_aes_gcm_128_16_auth(&policy.rtcp);
break;
case kDtlsSrtpAes128CmHmacSha1_80:
MOZ_MTLOG(ML_DEBUG,
"Setting SRTP cipher suite SRTP_AES128_CM_HMAC_SHA1_80");

View File

@ -13,9 +13,15 @@
namespace mozilla {
#define SRTP_MASTER_KEY_LENGTH 16
#define SRTP_MASTER_SALT_LENGTH 14
#define SRTP_TOTAL_KEY_LENGTH (SRTP_MASTER_KEY_LENGTH + SRTP_MASTER_SALT_LENGTH)
#define SRTP_ICM_MASTER_KEY_LENGTH 16
#define SRTP_ICM_MASTER_SALT_LENGTH 14
#define SRTP_ICM_MAX_MASTER_LEGNTH (SRTP_ICM_MASTER_KEY_LENGTH + SRTP_ICM_MASTER_SALT_LENGTH)
#define SRTP_GCM_MASTER_KEY_LENGTH 32
#define SRTP_GCM_MASTER_SALT_LENGTH 12
#define SRTP_GCM_MAX_MASTER_LEGNTH (SRTP_GCM_MASTER_KEY_LENGTH + SRTP_GCM_MASTER_SALT_LENGTH)
#define SRTP_MIN_KEY_LENGTH SRTP_ICM_MAX_MASTER_LEGNTH
#define SRTP_MAX_KEY_LENGTH SRTP_GCM_MAX_MASTER_LEGNTH
// SRTCP requires an auth tag *plus* a 4-byte index-plus-'E'-bit value (see
// RFC 3711)
@ -26,6 +32,8 @@ class SrtpFlow {
~SrtpFlow();
public:
static unsigned int KeySize(int cipher_suite);
static unsigned int SaltSize(int cipher_suite);
static RefPtr<SrtpFlow> Create(int cipher_suite,
bool inbound,

View File

@ -537,6 +537,8 @@ class TransportTestPeer : public sigslot::has_slots<> {
void SetupSrtp() {
// this mimics the setup we do elsewhere
std::vector<uint16_t> srtp_ciphers;
srtp_ciphers.push_back(kDtlsSrtpAeadAes256Gcm);
srtp_ciphers.push_back(kDtlsSrtpAeadAes128Gcm);
srtp_ciphers.push_back(kDtlsSrtpAes128CmHmacSha1_80);
srtp_ciphers.push_back(kDtlsSrtpAes128CmHmacSha1_32);
@ -1017,7 +1019,7 @@ TEST_F(TransportTest, TestConnectSrtp) {
ASSERT_EQ(TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, p1_->cipherSuite());
// SRTP is on
ASSERT_EQ(kDtlsSrtpAes128CmHmacSha1_80, p1_->srtpCipher());
ASSERT_EQ(kDtlsSrtpAeadAes256Gcm, p1_->srtpCipher());
}
@ -1409,6 +1411,22 @@ TEST_F(TransportTest, OnlyClientSendsSrtpXtn) {
TransportLayer::TS_ERROR);
}
TEST_F(TransportTest, TestSrtpFallback) {
std::vector<uint16_t> setA;
setA.push_back(kDtlsSrtpAeadAes256Gcm);
setA.push_back(kDtlsSrtpAes128CmHmacSha1_80);
std::vector<uint16_t> setB;
setB.push_back(kDtlsSrtpAes128CmHmacSha1_80);
p1_->SetSrtpCiphers(setA);
p2_->SetSrtpCiphers(setB);
SetDtlsPeer();
ConnectSocket();
ASSERT_EQ(kDtlsSrtpAes128CmHmacSha1_80, p1_->srtpCipher());
ASSERT_EQ(kDtlsSrtpAes128CmHmacSha1_80, p1_->srtpCipher());
}
// NSS doesn't support DHE suites on the server end.
// This checks to see if we barf when that's the only option available.
TEST_F(TransportTest, TestDheOnlyFails) {

View File

@ -159,8 +159,13 @@ TransportLayerSrtp::StateChange(TransportLayer* layer, State state)
return;
}
unsigned int key_size = SrtpFlow::KeySize(cipher_suite);
unsigned int salt_size = SrtpFlow::SaltSize(cipher_suite);
unsigned int master_key_size = key_size + salt_size;
MOZ_ASSERT(master_key_size <= SRTP_MAX_KEY_LENGTH);
// SRTP Key Exporter as per RFC 5764 S 4.2
unsigned char srtp_block[SRTP_TOTAL_KEY_LENGTH * 2];
unsigned char srtp_block[SRTP_MAX_KEY_LENGTH * 2];
res = dtls->ExportKeyingMaterial(
kDTLSExporterLabel, false, "", srtp_block, sizeof(srtp_block));
if (NS_FAILED(res)) {
@ -170,22 +175,17 @@ TransportLayerSrtp::StateChange(TransportLayer* layer, State state)
}
// Slice and dice as per RFC 5764 S 4.2
unsigned char client_write_key[SRTP_TOTAL_KEY_LENGTH];
unsigned char server_write_key[SRTP_TOTAL_KEY_LENGTH];
int offset = 0;
memcpy(client_write_key, srtp_block + offset, SRTP_MASTER_KEY_LENGTH);
offset += SRTP_MASTER_KEY_LENGTH;
memcpy(server_write_key, srtp_block + offset, SRTP_MASTER_KEY_LENGTH);
offset += SRTP_MASTER_KEY_LENGTH;
memcpy(client_write_key + SRTP_MASTER_KEY_LENGTH,
srtp_block + offset,
SRTP_MASTER_SALT_LENGTH);
offset += SRTP_MASTER_SALT_LENGTH;
memcpy(server_write_key + SRTP_MASTER_KEY_LENGTH,
srtp_block + offset,
SRTP_MASTER_SALT_LENGTH);
offset += SRTP_MASTER_SALT_LENGTH;
MOZ_ASSERT(offset == sizeof(srtp_block));
unsigned char client_write_key[SRTP_MAX_KEY_LENGTH];
unsigned char server_write_key[SRTP_MAX_KEY_LENGTH];
unsigned int offset = 0;
memcpy(client_write_key, srtp_block + offset, key_size);
offset += key_size;
memcpy(server_write_key, srtp_block + offset, key_size);
offset += key_size;
memcpy(client_write_key + key_size, srtp_block + offset, salt_size);
offset += salt_size;
memcpy(server_write_key + key_size, srtp_block + offset, salt_size);
MOZ_ASSERT((offset + salt_size) == (2 * master_key_size));
unsigned char* write_key;
unsigned char* read_key;
@ -200,9 +200,9 @@ TransportLayerSrtp::StateChange(TransportLayer* layer, State state)
MOZ_ASSERT(!mSendSrtp && !mRecvSrtp);
mSendSrtp =
SrtpFlow::Create(cipher_suite, false, write_key, SRTP_TOTAL_KEY_LENGTH);
SrtpFlow::Create(cipher_suite, false, write_key, master_key_size);
mRecvSrtp =
SrtpFlow::Create(cipher_suite, true, read_key, SRTP_TOTAL_KEY_LENGTH);
SrtpFlow::Create(cipher_suite, true, read_key, master_key_size);
if (!mSendSrtp || !mRecvSrtp) {
MOZ_MTLOG(ML_ERROR, "Couldn't create SRTP flow.");
TL_SET_STATE(TS_ERROR);

View File

@ -183,7 +183,7 @@ class TransportInfo {
UniquePtr<TransportLayerSrtp> srtp(new TransportLayerSrtp(*dtls));
std::vector<uint16_t> ciphers;
ciphers.push_back(kDtlsSrtpAes128CmHmacSha1_80);
ciphers.push_back(kDtlsSrtpAeadAes256Gcm);
dtls->SetSrtpCiphers(ciphers);
dtls->SetIdentity(DtlsIdentity::Generate());
dtls->SetRole(client ? TransportLayerDtls::CLIENT :

View File

@ -625,6 +625,8 @@ PeerConnectionMedia::UpdateTransportFlow(bool aIsRtcp,
}
std::vector<uint16_t> srtpCiphers;
srtpCiphers.push_back(kDtlsSrtpAeadAes256Gcm);
srtpCiphers.push_back(kDtlsSrtpAeadAes128Gcm);
srtpCiphers.push_back(kDtlsSrtpAes128CmHmacSha1_80);
srtpCiphers.push_back(kDtlsSrtpAes128CmHmacSha1_32);

View File

@ -567,6 +567,21 @@ set_config('MAKENSISU_FLAGS', nsis_flags)
check_prog('7Z', ('7z', '7za'), allow_missing=True, when=target_is_windows)
@depends(target, host, check_build_environment, '--help')
@imports('os')
def include_infer_autotest(target, host, build_env, help):
# Do not include autotest configuration on Windows or macos
# because infer is not available there
if target.kernel in ['Linux'] and host.kernel in ['Linux']:
path = os.path.join(build_env.topsrcdir, 'tools', 'infer', 'test', 'moz.configure')
# on some builds, this path won't exists, in which case we can just
# ignore the infer autotest
if os.path.exists(path):
return path
return None
include(include_infer_autotest)
# Fallthrough to autoconf-based configure
include('build/moz.configure/old.configure')

View File

@ -1680,14 +1680,8 @@ class StaticAnalysis(MachCommandBase):
self.log_manager.enable_all_structured_loggers()
rc = self._build_compile_db(verbose=verbose)
if rc != 0:
return rc
rc = self._build_export(jobs=jobs, verbose=verbose)
if rc != 0:
return rc
rc = self._get_clang_tools(verbose=verbose)
rc = rc or self._build_export(jobs=jobs, verbose=verbose)
rc = rc or self._get_clang_tools(verbose=verbose)
if rc != 0:
return rc
@ -1775,21 +1769,15 @@ class StaticAnalysis(MachCommandBase):
checks=checks or all_checkers,
third_party_path=third_party_path
)
gradlew = mozpath.join(self.topsrcdir, 'gradlew')
rc = rc or self._gradle(['clean']) # clean so that we can recompile
# infer capture command
capture_cmd = [self._infer_path, 'capture'] + excludes + \
['--', gradlew, task]
capture_cmd = [self._infer_path, 'capture'] + excludes + ['--']
rc = rc or self._gradle([task], infer_args=capture_cmd, verbose=verbose)
tmp_file, args = self._get_infer_source_args(java_sources)
# infer analyze command
analysis_cmd = [self._infer_path, 'analyze', '--keep-going'] + \
checkers + args
# capture, then analyze the sources
for args in [[gradlew, 'clean'], capture_cmd, analysis_cmd]:
rc = self.run_process(args=args, cwd=self.topsrcdir,
pass_thru=True)
# if a command fails, break and close the tmp file before returning
if rc != 0:
break
rc = rc or self.run_process(args=analysis_cmd, cwd=self.topsrcdir, pass_thru=True)
if tmp_file:
tmp_file.close()
return rc
@ -1876,6 +1864,35 @@ class StaticAnalysis(MachCommandBase):
str(jobs), '-p', self._compilation_commands_path
] + common_args + sources
def _gradle(self, args, infer_args=None, verbose=False, autotest=False,
suppress_output=True):
infer_args = infer_args or []
java_home = os.path.dirname(os.path.dirname(self.substs['JAVA']))
if autotest:
cwd = mozpath.join(self.topsrcdir, 'tools', 'infer', 'test')
gradle = mozpath.join(cwd, 'gradlew')
else:
gradle = self.substs['GRADLE']
cwd = self.topsrcdir
extra_env = {
'GRADLE_OPTS': '-Dfile.encoding=utf-8', # see mobile/android/mach_commands.py
'JAVA_HOME': java_home,
'JAVA_TOOL_OPTIONS': '-Dfile.encoding=utf-8',
}
if suppress_output:
devnull = open(os.devnull, 'w')
return subprocess.call(
infer_args + [gradle] + args,
env=dict(os.environ, **extra_env),
cwd=cwd, stdout=devnull, stderr=subprocess.STDOUT, close_fds=True)
else:
return self.run_process(
infer_args + [gradle] + args,
append_env=extra_env,
pass_thru=True, # Allow user to run gradle interactively.
ensure_exit_code=False, # Don't throw on non-zero exit code.
cwd=cwd)
@StaticAnalysisSubCommand('static-analysis', 'autotest',
'Run the auto-test suite in order to determine that'
' the analysis did not regress.')
@ -1892,11 +1909,7 @@ class StaticAnalysis(MachCommandBase):
# do this on a local trusted clang-tidy package.
self._set_log_level(verbose)
self._dump_results = dump_results
force_download = True
if self._dump_results:
force_download = False
force_download = not self._dump_results
# Function return codes
self.TOOLS_SUCCESS = 0
@ -1908,30 +1921,9 @@ class StaticAnalysis(MachCommandBase):
self.TOOLS_CHECKER_DIFF_FAILED = 6
self.TOOLS_CHECKER_NOT_FOUND = 7
self.TOOLS_CHECKER_FAILED_FILE = 8
self.TOOLS_GRADLE_FAILED = 9
# Configure the tree or download clang-tidy package, depending on the option that we choose
if intree_tool:
_, config, _ = self._get_config_environment()
clang_tools_path = self.topsrcdir
self._clang_tidy_path = mozpath.join(
clang_tools_path, "clang", "bin",
"clang-tidy" + config.substs.get('BIN_SUFFIX', ''))
self._clang_format_path = mozpath.join(
clang_tools_path, "clang", "bin",
"clang-format" + config.substs.get('BIN_SUFFIX', ''))
self._clang_apply_replacements = mozpath.join(
clang_tools_path, "clang", "bin",
"clang-apply-replacements" + config.substs.get('BIN_SUFFIX', ''))
self._run_clang_tidy_path = mozpath.join(clang_tools_path, "clang", "share",
"clang", "run-clang-tidy.py")
self._clang_format_diff = mozpath.join(clang_tools_path, "clang", "share",
"clang", "clang-format-diff.py")
# Ensure that clang-tidy is present
rc = not os.path.exists(self._clang_tidy_path)
else:
rc = self._get_clang_tools(force=force_download, verbose=verbose)
rc = self._get_clang_tools(force=force_download, verbose=verbose, intree_tool=intree_tool)
if rc != 0:
self.log(logging.ERROR, 'ERROR: static-analysis', {},
'clang-tidy unable to locate package.')
@ -1947,8 +1939,9 @@ class StaticAnalysis(MachCommandBase):
if platform not in config['platforms']:
self.log(logging.ERROR, 'static-analysis', {},
"RUNNING: clang-tidy autotest for platform {} not supported.".format(platform))
return TOOLS_UNSUPORTED_PLATFORM
"RUNNING: clang-tidy autotest for platform {} not supported."
.format(platform))
return self.TOOLS_UNSUPORTED_PLATFORM
import concurrent.futures
import multiprocessing
@ -2009,7 +2002,7 @@ class StaticAnalysis(MachCommandBase):
self.log(logging.INFO, 'static-analysis', {}, "SUCCESS: clang-tidy all tests passed.")
# Also delete the tmp folder
shutil.rmtree(self._compilation_commands_path)
return self.TOOLS_SUCCESS
return self._autotest_infer(intree_tool, force_download, verbose)
def _create_temp_compilation_db(self, config):
directory = tempfile.mkdtemp(prefix='cc')
@ -2022,7 +2015,7 @@ class StaticAnalysis(MachCommandBase):
file = item['name'] + '.cpp'
element = {}
element["directory"] = director
element["command"] = 'cpp '+ file
element["command"] = 'cpp ' + file
element["file"] = mozpath.join(director, file)
compile_commands.append(element)
@ -2031,6 +2024,126 @@ class StaticAnalysis(MachCommandBase):
return directory
def _autotest_infer(self, intree_tool, force_download, verbose):
# infer is not available on other platforms, but autotest should work even without
# it being installed
if self.platform[0] == 'linux64':
rc = self._get_infer(force=force_download, verbose=verbose, intree_tool=intree_tool)
if rc != 0:
self.log(logging.ERROR, 'ERROR: static-analysis', {},
'infer unable to locate package.')
return self.TOOLS_FAILED_DOWNLOAD
self.__infer_tool = mozpath.join(self.topsrcdir, 'tools', 'infer')
self.__infer_test_folder = mozpath.join(self.__infer_tool, 'test')
import concurrent.futures
import multiprocessing
max_workers = multiprocessing.cpu_count()
self.log(logging.INFO, 'static-analysis', {},
"RUNNING: infer autotest for platform {0} with {1} workers.".format(
self.platform[0], max_workers))
# clean previous autotest if it exists
rc = self._gradle(['autotest:clean'], autotest=True)
if rc != 0:
return rc
import yaml
with open(mozpath.join(self.__infer_tool, 'config.yaml')) as f:
config = yaml.safe_load(f)
with concurrent.futures.ThreadPoolExecutor(max_workers=max_workers) as executor:
futures = []
for item in config['infer_checkers']:
if item['publish']:
futures.append(executor.submit(self._verify_infer_checker, item))
# this is always included in check-java, but not in config.yaml
futures.append(executor.submit(self._verify_infer_checker,
{'name': 'checkers'}))
for future in concurrent.futures.as_completed(futures):
ret_val = future.result()
if ret_val != self.TOOLS_SUCCESS:
return ret_val
self.log(logging.INFO, 'static-analysis', {}, "SUCCESS: infer all tests passed.")
else:
self.log(logging.WARNING, 'static-analysis', {},
"Skipping infer autotest, because it is only available on linux64!")
return self.TOOLS_SUCCESS
def _verify_infer_checker(self, item):
'''Given a checker, this method verifies the following:
1. if there is a `checker`.json and `checker`.java file in
`tools/infer/test/autotest/src`
2. if running infer on `checker`.java yields the same result as `checker`.json
An `item` is simply a dictionary, which needs to have a `name` field set, which is the
name of the checker.
'''
def to_camelcase(str):
return ''.join([s.capitalize() for s in str.split('-')])
check = item['name']
test_file_path = mozpath.join(self.__infer_tool, 'test', 'autotest', 'src',
'main', 'java', to_camelcase(check))
test_file_path_java = test_file_path + '.java'
test_file_path_json = test_file_path + '.json'
self.log(logging.INFO, 'static-analysis', {}, "RUNNING: infer check {}.".format(check))
# Verify if the test file exists for this checker
if not os.path.exists(test_file_path_java):
self.log(logging.ERROR, 'static-analysis', {},
"ERROR: infer check {} doesn't have a test file.".format(check))
return self.TOOLS_CHECKER_NO_TEST_FILE
# run infer on a particular test file
out_folder = mozpath.join(self.__infer_test_folder, 'test-infer-{}'.format(check))
if check == 'checkers':
check_arg = ['-a', 'checkers']
else:
check_arg = ['--{}-only'.format(check)]
infer_args = [self._infer_path, 'run'] + check_arg + ['-o', out_folder, '--']
gradle_args = ['autotest:compileInferTest{}'.format(to_camelcase(check))]
rc = self._gradle(gradle_args, infer_args=infer_args, autotest=True)
if rc != 0:
self.log(logging.ERROR, 'static-analysis', {},
"ERROR: infer failed to execute gradle {}.".format(gradle_args))
return self.TOOLS_GRADLE_FAILED
issues = json.load(open(mozpath.join(out_folder, 'report.json')))
# remove folder that infer creates because the issues are loaded into memory
import shutil
shutil.rmtree(out_folder)
# Verify to see if we got any issues, if not raise exception
if not issues:
self.log(
logging.ERROR, 'static-analysis', {},
"ERROR: infer check {0} did not find any issues in its associated test suite."
.format(check)
)
return self.TOOLS_CHECKER_RETURNED_NO_ISSUES
if self._dump_results:
self._build_autotest_result(test_file_path_json, issues)
else:
if not os.path.exists(test_file_path_json):
# Result file for test not found maybe regenerate it?
self.log(
logging.ERROR, 'static-analysis', {},
"ERROR: infer result file not found for check {0}".format(check)
)
return self.TOOLS_CHECKER_RESULT_FILE_NOT_FOUND
# Read the pre-determined issues
baseline_issues = self._get_autotest_stored_issues(test_file_path_json)
def ordered(obj):
if isinstance(obj, dict):
return sorted((k, ordered(v)) for k, v in obj.items())
if isinstance(obj, list):
return sorted(ordered(x) for x in obj)
return obj
# Compare the two lists
if ordered(issues) != ordered(baseline_issues):
error_str = "ERROR: in check {} Expected: ".format(check)
error_str += '\n' + json.dumps(baseline_issues, indent=2)
error_str += '\n Got:\n' + json.dumps(issues, indent=2)
self.log(logging.ERROR, 'static-analysis', {},
'ERROR: infer autotest for check {} failed, check stdout for more details'
.format(check))
print(error_str)
return self.TOOLS_CHECKER_DIFF_FAILED
return self.TOOLS_SUCCESS
@StaticAnalysisSubCommand('static-analysis', 'install',
'Install the static analysis helper tool')
@CommandArgument('source', nargs='?', type=str,
@ -2289,13 +2402,14 @@ class StaticAnalysis(MachCommandBase):
def _get_clang_tools(self, force=False, skip_cache=False,
source=None, download_if_needed=True,
verbose=False):
verbose=False, intree_tool=False):
rc, config, _ = self._get_config_environment()
if rc != 0:
return rc
clang_tools_path = mozpath.join(self._mach_context.state_dir, "clang-tools")
clang_tools_path = self.topsrcdir if intree_tool else \
mozpath.join(self._mach_context.state_dir, "clang-tools")
self._clang_tidy_path = mozpath.join(clang_tools_path, "clang", "bin",
"clang-tidy" + config.substs.get('BIN_SUFFIX', ''))
self._clang_format_path = mozpath.join(
@ -2308,7 +2422,9 @@ class StaticAnalysis(MachCommandBase):
"run-clang-tidy.py")
self._clang_format_diff = mozpath.join(clang_tools_path, "clang", "share", "clang",
"clang-format-diff.py")
if intree_tool:
# Ensure that clang-tidy is present
return not os.path.exists(self._clang_tidy_path)
if os.path.exists(self._clang_tidy_path) and \
os.path.exists(self._clang_format_path) and \
os.path.exists(self._clang_apply_replacements) and \
@ -2409,15 +2525,17 @@ class StaticAnalysis(MachCommandBase):
args += [':({0}){1}'.format(','.join(magics), pattern)]
return args
def _get_infer(self, force=False, skip_cache=False,
download_if_needed=True, verbose=False):
def _get_infer(self, force=False, skip_cache=False, download_if_needed=True,
verbose=False, intree_tool=False):
rc, config, _ = self._get_config_environment()
if rc != 0:
return rc
infer_path = mozpath.join(self._mach_context.state_dir, 'infer')
self._infer_path = mozpath.join(infer_path, 'infer', 'bin',
'infer' +
infer_path = self.topsrcdir if intree_tool else \
mozpath.join(self._mach_context.state_dir, 'infer')
self._infer_path = mozpath.join(infer_path, 'infer', 'bin', 'infer' +
config.substs.get('BIN_SUFFIX', ''))
if intree_tool:
return not os.path.exists(self._infer_path)
if os.path.exists(self._infer_path) and not force:
return 0
else:

View File

@ -86,9 +86,9 @@ jobs:
fetch:
symbol: I(fetch)
parent: debian9-base
infer-build:
symbol: I(infer)
parent: debian9-base
static-analysis-build:
symbol: I(static-analysis-build)
parent: android-build
mingw32-build:
symbol: I(mingw)
parent: debian9-base

View File

@ -36,6 +36,10 @@ jobs:
description: "Linux64 Debug Static Analysis Autotest"
index:
job-name: linux64-st-autotest-debug
worker:
docker-image: {in-tree: static-analysis-build}
env:
PERFHERDER_EXTRA_OPTIONS: static-analysis-autotest
treeherder:
platform: linux64/debug
worker-type: aws-provisioner-v1/gecko-t-linux-large

View File

@ -158,7 +158,7 @@ linux64-infer:
tier: 1
worker-type: aws-provisioner-v1/gecko-{level}-b-linux
worker:
docker-image: {in-tree: infer-build}
docker-image: {in-tree: static-analysis-build}
max-run-time: 3600
run:
using: toolchain-script

View File

@ -27,6 +27,7 @@ RUN apt-get update && \
gawk \
gcc-multilib \
gnupg \
openjdk-7-jdk \
p7zip-full \
procps \
python-pip \

View File

@ -1,30 +0,0 @@
# %ARG DOCKER_IMAGE_PARENT
FROM $DOCKER_IMAGE_PARENT
MAINTAINER Robert Bartlensky <rbartlensky@mozilla.com>
VOLUME /builds/worker/checkouts
VOLUME /builds/worker/workspace
VOLUME /builds/worker/tooltool-cache
ENV XZ_OPT=-T0
RUN apt-get update && \
apt-get install \
autoconf \
bison \
bzip2 \
flex \
curl \
git \
opam \
libsqlite3-dev \
autoconf \
automake \
cmake \
libc6-dev \
openjdk-8-jdk-headless \
pkg-config \
patch \
tar \
unzip \
zlib1g-dev

View File

@ -0,0 +1,58 @@
# %ARG DOCKER_IMAGE_PARENT
FROM $DOCKER_IMAGE_PARENT
MAINTAINER Robert Bartlensky <rbartlensky@mozilla.com>
VOLUME /builds/worker/checkouts
VOLUME /builds/worker/workspace
VOLUME /builds/worker/tooltool-cache
ENV XZ_OPT=-T0
RUN apt-get update && \
apt-get install \
autoconf2.13 \
automake \
bison \
bzip2 \
cmake \
flex \
curl \
opam \
libsqlite3-dev \
file \
gawk \
gcc-multilib \
gnupg \
libc6-dev \
openjdk-8-jdk-headless \
pkg-config \
patch \
p7zip-full \
procps \
python-pip \
python-setuptools \
python-virtualenv \
rsync \
screen \
tar \
unzip \
uuid \
valgrind \
wget \
yasm \
zip \
zlib1g-dev \
x11-utils \
xvfb \
linux-libc-dev \
libdbus-glib-1-dev \
libfontconfig1-dev \
libfreetype6-dev \
libgconf2-dev \
libgtk-3-dev \
libgtk2.0-dev \
libpango1.0-dev \
libpulse-dev \
libx11-xcb-dev \
libxt-dev \
lib32z1

View File

@ -1,4 +1,3 @@
#!/usr/bin/env python
# ***** BEGIN LICENSE BLOCK *****
# This Source Code Form is subject to the terms of the Mozilla Public

View File

@ -1226,13 +1226,15 @@ or run without that action (ie: --no-{action})"
def static_analysis_autotest(self):
"""Run mach static-analysis autotest, in order to make sure we dont regress"""
self.preflight_build()
self._run_mach_command_in_build_env(['static-analysis', 'autotest', '--intree-tool'])
self._run_mach_command_in_build_env(['configure'])
self._run_mach_command_in_build_env(['static-analysis', 'autotest',
'--intree-tool'],
use_subprocess=True)
def _run_mach_command_in_build_env(self, args):
def _run_mach_command_in_build_env(self, args, use_subprocess=False):
"""Run a mach command in a build context."""
env = self.query_build_env()
env.update(self.query_mach_build_env())
dirs = self.query_abs_dirs()
if 'MOZILLABUILD' in os.environ:
@ -1245,13 +1247,21 @@ or run without that action (ie: --no-{action})"
else:
mach = [sys.executable, 'mach']
return_code = self.run_command(
command=mach + ['--log-no-times'] + args,
cwd=dirs['abs_src_dir'],
env=env,
output_timeout=self.config.get('max_build_output_timeout', 60 * 40)
)
# XXX See bug 1483883
# Work around an interaction between Gradle and mozharness
# Not using `subprocess` causes gradle to hang
if use_subprocess:
import subprocess
return_code = subprocess.call(mach + ['--log-no-times'] + args,
env=env, cwd=dirs['abs_src_dir'])
else:
return_code = self.run_command(
command=mach + ['--log-no-times'] + args,
cwd=dirs['abs_src_dir'],
env=env,
output_timeout=self.config.get('max_build_output_timeout',
60 * 40)
)
if return_code:
self.return_code = self.worst_level(
EXIT_STATUS_DICT[TBPL_FAILURE], self.return_code,

View File

@ -114,6 +114,12 @@ function testScroll(target, stepSize, opt_reportFunc, opt_numSteps) {
});
}
function P_MozAfterPaint() {
return new Promise(function(resolve) {
win.addEventListener("MozAfterPaint", () => resolve(), { once: true});
});
}
function myNow() {
return (win.performance && win.performance.now) ?
win.performance.now() :
@ -196,7 +202,7 @@ function testScroll(target, stepSize, opt_reportFunc, opt_numSteps) {
}
lastScrollPos = getPos();
rAF(tick);
P_MozAfterPaint().then(tick);
}
if (typeof(TalosContentProfiler) !== "undefined") {

View File

@ -575,7 +575,8 @@ class tp5o_scroll(PageloaderTest):
tpmozafterpaint = False
preferences = {'layout.frame_rate': 0,
'docshell.event_starvation_delay_hint': 1,
'dom.send_after_paint_to_content': False,
'dom.send_after_paint_to_content': True,
'apz.paint_skipping.enabled': False,
'layout.css.scroll-behavior.spring-constant': "'10'",
'toolkit.framesRecording.bufferSize': 10000}
filters = filter.ignore_first.prepare(1) + filter.median.prepare()
@ -773,7 +774,8 @@ class tscrollx(PageloaderTest):
""" ASAP mode """
preferences = {'layout.frame_rate': 0,
'docshell.event_starvation_delay_hint': 1,
'dom.send_after_paint_to_content': False,
'dom.send_after_paint_to_content': True,
'apz.paint_skipping.enabled': False,
'layout.css.scroll-behavior.spring-constant': "'10'",
'toolkit.framesRecording.bufferSize': 10000}
filters = filter.ignore_first.prepare(5) + filter.median.prepare()

View File

@ -71,6 +71,38 @@ class MozXULElement extends XULElement {
return range.extractContents();
}
/**
* Insert a localization link to an FTL file. This is used so that
* a Custom Element can wait to inject the link until it's connected,
* and so that consuming documents don't require the correct <link>
* present in the markup.
*
* @param path
* The path to the FTL file
*/
static insertFTLIfNeeded(path) {
let container = document.head || document.querySelector("linkset");
if (!container) {
if (document.contentType != "application/vnd.mozilla.xul+xml") {
throw new Error("Attempt to inject localization link before document.head is available");
}
container = document.createXULElement("linkset");
document.documentElement.appendChild(container);
}
for (let link of container.querySelectorAll("link")) {
if (link.getAttribute("href") == path) {
return;
}
}
let link = document.createElement("link");
link.setAttribute("rel", "localization");
link.setAttribute("href", path);
container.appendChild(link);
}
/**
* Indicate that a class defining an element implements one or more
* XPCOM interfaces. The custom element getCustomInterface is added

View File

@ -5,19 +5,27 @@ target: obj-x86_64-pc-linux-gnu
platforms:
- linux64
infer_checkers:
# no issues were ever trigger by this
- name: check-nullable
publish: !!bool no
- name: biabduction
publish: !!bool yes
# very very noisy
# it could be useful, but it won't be part of the default enabled checkers
- name: eradicate
publish: !!bool no
# hard to use, not useful
- name: quandary
publish: !!bool yes
publish: !!bool no
- name: starvation
publish: !!bool yes
# experimental
- name: litho
publish: !!bool yes
publish: !!bool no
- name: racerd
publish: !!bool yes
# I think this is only for c++, can't trigger these errors in Java
- name: liveness
publish: !!bool yes
publish: !!bool no
# Third party files from mozilla-central
third_party: tools/rewriting/ThirdPartyPaths.txt

View File

@ -0,0 +1,23 @@
apply plugin: 'java'
repositories {
mavenCentral()
}
dependencies {
compile "com.google.code.findbugs:jsr305:3.0.2"
}
def createSingleTask = { name ->
task("compileInferTest${name}", type: JavaCompile) {
source = fileTree(dir: '.', include: "src/main/java/${name}.java")
classpath = project.configurations.compileClasspath
destinationDir = file("${topobjdir}/gradle/build/tools/infer/test/autotest")
}
}
createSingleTask('Biabduction')
createSingleTask('Checkers')
createSingleTask('Eradicate')
createSingleTask('Racerd')
createSingleTask('Starvation')

View File

@ -0,0 +1,22 @@
/* 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/. */
import javax.annotation.Nullable;
import java.util.List;
public class Biabduction {
private String get() { return null; }
public void f1() {
get().length(); // error
}
public void f2() {
try {
get().length(); // error
} catch (NullPointerException e) {
}
}
}

View File

@ -0,0 +1,114 @@
[
{
"bug_class": "PROVER",
"bug_trace": [
{
"column_number": -1,
"description": "start of procedure f1()",
"filename": "autotest/src/main/java/Biabduction.java",
"level": 0,
"line_number": 11
},
{
"column_number": -1,
"description": "",
"filename": "autotest/src/main/java/Biabduction.java",
"level": 0,
"line_number": 12
},
{
"column_number": -1,
"description": "start of procedure get()",
"filename": "autotest/src/main/java/Biabduction.java",
"level": 1,
"line_number": 9
},
{
"column_number": -1,
"description": "return from a call to String Biabduction.get()",
"filename": "autotest/src/main/java/Biabduction.java",
"level": 1,
"line_number": 9
},
{
"column_number": -1,
"description": "",
"filename": "autotest/src/main/java/Biabduction.java",
"level": 0,
"line_number": 12
}
],
"bug_type": "NULL_DEREFERENCE",
"bug_type_hum": "Null Dereference",
"censored_reason": "",
"column": -1,
"file": "autotest/src/main/java/Biabduction.java",
"hash": "918d7eaedf45f651f04c55554c72478c",
"key": "Biabduction.java|f1|NULL_DEREFERENCE",
"kind": "ERROR",
"line": 12,
"node_key": "9afcdcc9d4253c36267a0d34b98c337d",
"procedure": "void Biabduction.f1()",
"procedure_id": "Biabduction.f1():void.4b49520e7621606a0d5661886ff0b098",
"procedure_start_line": 11,
"qualifier": "object returned by `get(this)` could be null and is dereferenced at line 12.",
"severity": "HIGH",
"visibility": "user"
},
{
"bug_class": "PROVER",
"bug_trace": [
{
"column_number": -1,
"description": "start of procedure f2()",
"filename": "autotest/src/main/java/Biabduction.java",
"level": 0,
"line_number": 15
},
{
"column_number": -1,
"description": "",
"filename": "autotest/src/main/java/Biabduction.java",
"level": 0,
"line_number": 17
},
{
"column_number": -1,
"description": "start of procedure get()",
"filename": "autotest/src/main/java/Biabduction.java",
"level": 1,
"line_number": 9
},
{
"column_number": -1,
"description": "return from a call to String Biabduction.get()",
"filename": "autotest/src/main/java/Biabduction.java",
"level": 1,
"line_number": 9
},
{
"column_number": -1,
"description": "",
"filename": "autotest/src/main/java/Biabduction.java",
"level": 0,
"line_number": 17
}
],
"bug_type": "NULL_DEREFERENCE",
"bug_type_hum": "Null Dereference",
"censored_reason": "",
"column": -1,
"file": "autotest/src/main/java/Biabduction.java",
"hash": "bc952ce8bad58dac5cb6672dc3150524",
"key": "Biabduction.java|f2|NULL_DEREFERENCE",
"kind": "ERROR",
"line": 17,
"node_key": "9afcdcc9d4253c36267a0d34b98c337d",
"procedure": "void Biabduction.f2()",
"procedure_id": "Biabduction.f2():void.41c05a632eb912a458482c1e2e4dcbb4",
"procedure_start_line": 15,
"qualifier": "object returned by `get(this)` could be null and is dereferenced at line 17.",
"severity": "HIGH",
"visibility": "user"
}
]

View File

@ -0,0 +1,34 @@
/* 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/. */
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
public class Checkers {
public static void leak() {
try {
BufferedReader br = new BufferedReader(
new FileReader(new File("some.txt"))
);
} catch (Exception e) {
}
}
public static void error1() {
String str = null;
try {
int x = str.length(); // Error: even if exception is caught
} catch (NullPointerException e) {
}
}
public static void error2() {
String str = null;
int x = str.length(); // Error: not checking for null
}
}

View File

@ -0,0 +1,121 @@
[
{
"bug_class": "PROVER",
"bug_trace": [
{
"column_number": -1,
"description": "start of procedure leak()",
"filename": "autotest/src/main/java/Checkers.java",
"level": 0,
"line_number": 10
},
{
"column_number": -1,
"description": "",
"filename": "autotest/src/main/java/Checkers.java",
"level": 0,
"line_number": 12
}
],
"bug_type": "RESOURCE_LEAK",
"bug_type_hum": "Resource Leak",
"censored_reason": "",
"column": -1,
"file": "autotest/src/main/java/Checkers.java",
"hash": "56806153823413731f2e2166ed8d30a0",
"key": "Checkers.java|leak|RESOURCE_LEAK",
"kind": "ERROR",
"line": 12,
"node_key": "3a2af627d5d1f10e1994f6259cf18e4c",
"procedure": "void Checkers.leak()",
"procedure_id": "Checkers.leak():void.e21648e10d3037f4559cdb7c08642c84",
"procedure_start_line": 10,
"qualifier": "resource of type `java.io.FileReader` acquired by call to `new()` at line 12 is not released after line 12.",
"severity": "HIGH",
"visibility": "user"
},
{
"bug_class": "PROVER",
"bug_trace": [
{
"column_number": -1,
"description": "start of procedure error1()",
"filename": "autotest/src/main/java/Checkers.java",
"level": 0,
"line_number": 20
},
{
"column_number": -1,
"description": "",
"filename": "autotest/src/main/java/Checkers.java",
"level": 0,
"line_number": 21
},
{
"column_number": -1,
"description": "",
"filename": "autotest/src/main/java/Checkers.java",
"level": 0,
"line_number": 23
}
],
"bug_type": "NULL_DEREFERENCE",
"bug_type_hum": "Null Dereference",
"censored_reason": "",
"column": -1,
"file": "autotest/src/main/java/Checkers.java",
"hash": "6de26e7c66c71b1114ad233679d55640",
"key": "Checkers.java|error1|NULL_DEREFERENCE",
"kind": "ERROR",
"line": 23,
"node_key": "c281f77c6dae544ee5fb7d5e2bb35118",
"procedure": "void Checkers.error1()",
"procedure_id": "Checkers.error1():void.59417424d80960700a32012973e98db5",
"procedure_start_line": 20,
"qualifier": "object `str` last assigned on line 21 could be null and is dereferenced at line 23.",
"severity": "HIGH",
"visibility": "user"
},
{
"bug_class": "PROVER",
"bug_trace": [
{
"column_number": -1,
"description": "start of procedure error2()",
"filename": "autotest/src/main/java/Checkers.java",
"level": 0,
"line_number": 29
},
{
"column_number": -1,
"description": "",
"filename": "autotest/src/main/java/Checkers.java",
"level": 0,
"line_number": 30
},
{
"column_number": -1,
"description": "",
"filename": "autotest/src/main/java/Checkers.java",
"level": 0,
"line_number": 31
}
],
"bug_type": "NULL_DEREFERENCE",
"bug_type_hum": "Null Dereference",
"censored_reason": "",
"column": -1,
"file": "autotest/src/main/java/Checkers.java",
"hash": "39e021b634ab428af7be2034688491a7",
"key": "Checkers.java|error2|NULL_DEREFERENCE",
"kind": "ERROR",
"line": 31,
"node_key": "c281f77c6dae544ee5fb7d5e2bb35118",
"procedure": "void Checkers.error2()",
"procedure_id": "Checkers.error2():void.e9146d80ba20c908c11d08947cd89d06",
"procedure_start_line": 29,
"qualifier": "object `str` last assigned on line 30 could be null and is dereferenced at line 31.",
"severity": "HIGH",
"visibility": "user"
}
]

View File

@ -0,0 +1,57 @@
/* 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/. */
import javax.annotation.Nullable;
// Examples taken from the infer website.
public class Eradicate {
public String f; // Because it is not annoted with nullable -> can never be null!
public void field(@Nullable Eradicate x) {
x.f = "3"; // Error: Eradicate null field access
}
public void method(@Nullable Object x) {
String s = x.toString(); // Error: Eradicate null method call
}
public void filedNotNull(@Nullable String s) {
f = s; // Error: Eradicate field not nullable
}
public Eradicate() {} // Error: Eradicate field not initialized
public void str(Eradicate x) {
String s = x.toString();
}
public void callStr(@Nullable Eradicate x) {
str(x); // Error: Eradicate parameter not nullable
}
public String shouldNotReturnNullBecauseNotAnnotated() {
return null; // Error: Eradicate return not nullable
}
public void redundant() {
String s = new String("abc");
if (s != null) { // Error: Eradicate condition redundant
int n = s.length();
}
}
@Nullable
public static String someMethod() {
return ""; // Error: Eradicate return overannotated
}
}
class B extends Eradicate {
@Nullable public String shouldNotReturnNullBecauseNotAnnotated() {
return null; // Error: Eradicate inconsistent subclass return annotation
}
public void field(Eradicate x) {} // Error: Inconsistent subclass parameter annotation
}

View File

@ -0,0 +1,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/. */
import javax.annotation.concurrent.ThreadSafe;
// Examples taken from the infer website.
@ThreadSafe
public class Racerd {
private int mTemperature;
public void makeDinner() {
boilWater();
}
private void boilWater() {
mTemperature = 100; //Error: unprotected write.
}
}
@ThreadSafe
class Account {
int mBalance = 0;
public void deposit(int amount) {
if (amount > 0) {
mBalance += amount; // Error: unsynchronized write
}
}
public int withdraw(int amount){
if (amount >= 0 && mBalance - amount >= 0) {
mBalance -= amount; // Error: unsynchronized write
return mBalance; // Error: unsynchronized read
} else {
return 0;
}
}
}

View File

@ -0,0 +1,146 @@
[
{
"access": "hJWmvgAAAJMAAAApAAAAfgAAAHSwkNAnZGVwb3NpdKCgQCNpbnRAk6AnQWNjb3VudECQoEAkdm9pZECgoJKgIECwXAD/kgkiYXV0b3Rlc3Qvc3JjL21haW4vamF2YS9SYWNlcmQuamF2YZGgoJGwAjafTBegJHRoaXNAkAQcoKOglJOgBBpAsEBAQEAEAaCRkTBBY2NvdW50Lm1CYWxhbmNlQKAEF0A=",
"bug_class": "PROVER",
"bug_trace": [
{
"column_number": -1,
"description": "access to `this.Account.mBalance`",
"filename": "autotest/src/main/java/Racerd.java",
"level": 0,
"line_number": 28
}
],
"bug_type": "THREAD_SAFETY_VIOLATION",
"bug_type_hum": "Thread Safety Violation",
"censored_reason": "",
"column": -1,
"file": "autotest/src/main/java/Racerd.java",
"hash": "6b62cb17008a3135d218108fa3123402",
"key": "Racerd.java|deposit|THREAD_SAFETY_VIOLATION",
"kind": "ERROR",
"line": 28,
"node_key": "9c5d6d9028928346cc4fb44cced5dea1",
"procedure": "void Account.deposit(int)",
"procedure_id": "Account.deposit(int):void.a9cc1805c1e3652887a5ee12b55803af",
"procedure_start_line": 0,
"qualifier": "Unprotected write. Non-private method `void Account.deposit(int)` writes to field `this.Account.mBalance` outside of synchronization.\n Reporting because the current class is annotated `@ThreadSafe`, so we assume that this method can run in parallel with other non-private methods in the class (including itself).",
"severity": "HIGH",
"visibility": "user"
},
{
"access": "hJWmvgAAAKYAAAApAAAAhQAAAHqwkNAqbWFrZURpbm5lckCToCZSYWNlcmRAkKBAJHZvaWRAoKCQ0Clib2lsV2F0ZXJAk6AEC0AECkCwTQD/kgkiYXV0b3Rlc3Qvc3JjL21haW4vamF2YS9SYWNlcmQuamF2YZGgoJGwAjafTBegJHRoaXNAkAQboKOglJOgBBxAsEBAQEAEAaCRkTNSYWNlcmQubVRlbXBlcmF0dXJlQKCwUQD/BBdA",
"bug_class": "PROVER",
"bug_trace": [
{
"column_number": -1,
"description": "call to void Racerd.boilWater()",
"filename": "autotest/src/main/java/Racerd.java",
"level": 0,
"line_number": 13
},
{
"column_number": -1,
"description": "access to `this.Racerd.mTemperature`",
"filename": "autotest/src/main/java/Racerd.java",
"level": 1,
"line_number": 17
}
],
"bug_type": "THREAD_SAFETY_VIOLATION",
"bug_type_hum": "Thread Safety Violation",
"censored_reason": "",
"column": -1,
"file": "autotest/src/main/java/Racerd.java",
"hash": "2882383086ab102a88144ae3c2cc4701",
"key": "Racerd.java|makeDinner|THREAD_SAFETY_VIOLATION",
"kind": "ERROR",
"line": 13,
"node_key": "9c5d6d9028928346cc4fb44cced5dea1",
"procedure": "void Racerd.makeDinner()",
"procedure_id": "Racerd.makeDinner():void.2796f75396b30d2d49b24ddfab722306",
"procedure_start_line": 0,
"qualifier": "Unprotected write. Non-private method `void Racerd.makeDinner()` indirectly writes to field `this.Racerd.mTemperature` outside of synchronization.\n Reporting because the current class is annotated `@ThreadSafe`, so we assume that this method can run in parallel with other non-private methods in the class (including itself).",
"severity": "HIGH",
"visibility": "user"
},
{
"access": "hJWmvgAAAJgAAAAqAAAAgwAAAHqwkNAod2l0aGRyYXegoEAjaW50QJOgJ0FjY291bnRAkKBABAZAoKCSoCBAsGMA/5IJImF1dG90ZXN0L3NyYy9tYWluL2phdmEvUmFjZXJkLmphdmGQoKCRsAI2n0wXoCR0aGlzQJAEG6CjoJSToAQZQLBAQEBABAGgkZEwQWNjb3VudC5tQmFsYW5jZUCgBBegsGIA/wQYQA==",
"bug_class": "PROVER",
"bug_trace": [
{
"column_number": -1,
"description": "<Read trace>",
"filename": "autotest/src/main/java/Racerd.java",
"level": 0,
"line_number": 35
},
{
"column_number": -1,
"description": "access to `this.Account.mBalance`",
"filename": "autotest/src/main/java/Racerd.java",
"level": 0,
"line_number": 35
},
{
"column_number": -1,
"description": "<Write trace>",
"filename": "autotest/src/main/java/Racerd.java",
"level": 0,
"line_number": 34
},
{
"column_number": -1,
"description": "access to `this.Account.mBalance`",
"filename": "autotest/src/main/java/Racerd.java",
"level": 0,
"line_number": 34
}
],
"bug_type": "THREAD_SAFETY_VIOLATION",
"bug_type_hum": "Thread Safety Violation",
"censored_reason": "",
"column": -1,
"file": "autotest/src/main/java/Racerd.java",
"hash": "5665f12d2392f93f11f556cd1b1e238a",
"key": "Racerd.java|withdraw|THREAD_SAFETY_VIOLATION",
"kind": "ERROR",
"line": 35,
"node_key": "9c5d6d9028928346cc4fb44cced5dea1",
"procedure": "int Account.withdraw(int)",
"procedure_id": "Account.withdraw(int):int.038de5054c5c25e60d169e42e0177a16",
"procedure_start_line": 0,
"qualifier": "Read/Write race. Non-private method `int Account.withdraw(int)` reads without synchronization from `this.Account.mBalance`. Potentially races with write in method `Account.withdraw(...)`.\n Reporting because the current class is annotated `@ThreadSafe`, so we assume that this method can run in parallel with other non-private methods in the class (including itself).",
"severity": "HIGH",
"visibility": "user"
},
{
"access": "hJWmvgAAAJEAAAAoAAAAfAAAAHOwkNAod2l0aGRyYXegoEAjaW50QJOgJ0FjY291bnRAkKBABAZAoKCSoCBAsGIA/5IJImF1dG90ZXN0L3NyYy9tYWluL2phdmEvUmFjZXJkLmphdmGRoKCRsAI2n0wXoCR0aGlzQJAEG6CjoJSToAQZQLBAQEBABAGgkZEwQWNjb3VudC5tQmFsYW5jZUCgBBdA",
"bug_class": "PROVER",
"bug_trace": [
{
"column_number": -1,
"description": "access to `this.Account.mBalance`",
"filename": "autotest/src/main/java/Racerd.java",
"level": 0,
"line_number": 34
}
],
"bug_type": "THREAD_SAFETY_VIOLATION",
"bug_type_hum": "Thread Safety Violation",
"censored_reason": "",
"column": -1,
"file": "autotest/src/main/java/Racerd.java",
"hash": "a7c30fd1b251d9e16750fc7e5913b885",
"key": "Racerd.java|withdraw|THREAD_SAFETY_VIOLATION",
"kind": "ERROR",
"line": 34,
"node_key": "9c5d6d9028928346cc4fb44cced5dea1",
"procedure": "int Account.withdraw(int)",
"procedure_id": "Account.withdraw(int):int.038de5054c5c25e60d169e42e0177a16",
"procedure_start_line": 0,
"qualifier": "Unprotected write. Non-private method `int Account.withdraw(int)` writes to field `this.Account.mBalance` outside of synchronization.\n Reporting because the current class is annotated `@ThreadSafe`, so we assume that this method can run in parallel with other non-private methods in the class (including itself).",
"severity": "HIGH",
"visibility": "user"
}
]

View File

@ -0,0 +1,25 @@
/* 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/. */
// Examples taken from the infer website.
public class Starvation {
String lockA, lockB;
public void lockAThenB() {
synchronized(lockA) {
synchronized(lockB) {
// do something with both resources
}
}
}
public void lockBThenA() {
synchronized(lockB) {
synchronized(lockA) {
// do something with both resources
}
}
}
}

View File

@ -0,0 +1,65 @@
[
{
"bug_class": "PROVER",
"bug_trace": [
{
"column_number": -1,
"description": "[Trace 1] `void Starvation.lockAThenB()`",
"filename": "autotest/src/main/java/Starvation.java",
"level": 0,
"line_number": 11
},
{
"column_number": -1,
"description": "locks `this.Starvation.lockA` in class `Starvation*`",
"filename": "autotest/src/main/java/Starvation.java",
"level": 0,
"line_number": 11
},
{
"column_number": -1,
"description": "locks `this.Starvation.lockB` in class `Starvation*`",
"filename": "autotest/src/main/java/Starvation.java",
"level": 1,
"line_number": 12
},
{
"column_number": -1,
"description": "[Trace 2] `void Starvation.lockBThenA()`",
"filename": "autotest/src/main/java/Starvation.java",
"level": 0,
"line_number": 19
},
{
"column_number": -1,
"description": "locks `this.Starvation.lockB` in class `Starvation*`",
"filename": "autotest/src/main/java/Starvation.java",
"level": 0,
"line_number": 19
},
{
"column_number": -1,
"description": "locks `this.Starvation.lockA` in class `Starvation*`",
"filename": "autotest/src/main/java/Starvation.java",
"level": 1,
"line_number": 20
}
],
"bug_type": "DEADLOCK",
"bug_type_hum": "Deadlock",
"censored_reason": "",
"column": -1,
"file": "autotest/src/main/java/Starvation.java",
"hash": "043d28a94431b4c573b949b8570fb318",
"key": "Starvation.java|lockAThenB|DEADLOCK",
"kind": "ERROR",
"line": 11,
"node_key": "9c5d6d9028928346cc4fb44cced5dea1",
"procedure": "void Starvation.lockAThenB()",
"procedure_id": "Starvation.lockAThenB():void.b7eb3955306c498af42d6336f52a796f",
"procedure_start_line": 0,
"qualifier": "Potential deadlock.\nTrace 1 (starts at `void Starvation.lockAThenB()`) first locks `this.Starvation.lockA` in class `Starvation*` (line 11 in `void Starvation.lockAThenB()`) and then locks `this.Starvation.lockB` in class `Starvation*` (line 12 in `void Starvation.lockAThenB()`).\nTrace 2 (starts at `void Starvation.lockBThenA()`), first locks `this.Starvation.lockB` in class `Starvation*` (line 19 in `void Starvation.lockBThenA()`) and then locks `this.Starvation.lockA` in class `Starvation*` (line 20 in `void Starvation.lockBThenA()`).",
"severity": "HIGH",
"visibility": "user"
}
]

View File

@ -0,0 +1,6 @@
allprojects {
// Expose the per-object-directory configuration to all projects.
ext {
topobjdir = gradle.mozconfig.topobjdir
}
}

View File

@ -0,0 +1,3 @@
org.gradle.parallel=true
org.gradle.daemon=true
org.gradle.jvmargs=-Xmx2560M

Binary file not shown.

View File

@ -0,0 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip
distributionSha256Sum=7a2c66d1a78f811d5f37d14630ad21cec5e77a2a4dc61e787e2257a6341016ce

172
tools/infer/test/gradlew vendored Executable file
View File

@ -0,0 +1,172 @@
#!/usr/bin/env sh
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"

84
tools/infer/test/gradlew.bat vendored Normal file
View File

@ -0,0 +1,84 @@
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

View File

@ -0,0 +1,22 @@
# -*- 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/.
include('../../../build/moz.configure/java_common.configure')
@depends(javac)
@checking('for javac version')
@imports('subprocess')
def javac_version(javac):
try:
output = subprocess.check_output([javac, '-version'],
stderr=subprocess.STDOUT).rstrip()
version = Version(output.split(' ')[-1])
if version < '1.7':
die('javac 1.7 or higher is required (found %s). '
'Check the JAVA_HOME environment variable.' % version)
return version
except subprocess.CalledProcessError as e:
die('Failed to get javac version: %s', e.output)

View File

@ -0,0 +1,15 @@
# -*- 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/.
@depends(build_project)
def java_include(build_project):
# if mobile/android is enabled then the moz.configure from ./mobile
# will include java
if build_project == 'mobile/android':
return None
return 'java.configure'
include(java_include)

View File

@ -0,0 +1,24 @@
// Check out root/settings.gradle for more information
rootProject.name = 'infer'
def topsrcdir = rootProject.projectDir.absolutePath + '/../../..'
def commandLine = ["${topsrcdir}/mach", "environment", "--format", "json", "--verbose"]
def proc = commandLine.execute(null, new File(topsrcdir))
def standardOutput = new ByteArrayOutputStream()
proc.consumeProcessOutput(standardOutput, standardOutput)
proc.waitFor()
if (proc.exitValue() != 0) {
throw new GradleException("Process '${commandLine}' finished with non-zero exit value ${proc.exitValue()}:\n\n${standardOutput.toString()}")
}
import groovy.json.JsonSlurper
def slurper = new JsonSlurper()
def json = slurper.parseText(standardOutput.toString())
gradle.ext.mozconfig = json
include 'autotest'
project(':autotest').projectDir = new File("${json.topsrcdir}/tools/infer/test/autotest")

View File

@ -9,6 +9,7 @@
#include "mozilla/ArrayUtils.h"
#include "mozilla/BinarySearch.h"
#include "mozilla/CheckedInt.h"
#include "mozilla/TextUtils.h"
#include "nsTArray.h"
#include "nsCRT.h"
#include "plstr.h"
@ -521,8 +522,6 @@ NS_EscapeURL(const nsString& aStr, const nsTArray<char16_t>& aForbidden,
return aStr;
}
#define ISHEX(c) memchr(hexCharsUpperLower, c, sizeof(hexCharsUpperLower)-1)
bool
NS_UnescapeURL(const char* aStr, int32_t aLen, uint32_t aFlags,
nsACString& aResult)
@ -550,8 +549,15 @@ NS_UnescapeURL(const char* aStr, int32_t aLen, uint32_t aFlags,
MOZ_ASSERT(aResult.IsEmpty(),
"Passing a non-empty string as an out parameter!");
uint32_t len;
if (aLen < 0) {
aLen = strlen(aStr);
size_t stringLength = strlen(aStr);
if (stringLength >= UINT32_MAX) {
return NS_ERROR_OUT_OF_MEMORY;
}
len = stringLength;
} else {
len = aLen;
}
bool ignoreNonAscii = !!(aFlags & esc_OnlyASCII);
@ -560,50 +566,62 @@ NS_UnescapeURL(const char* aStr, int32_t aLen, uint32_t aFlags,
bool skipControl = !!(aFlags & esc_SkipControl);
bool skipInvalidHostChar = !!(aFlags & esc_Host);
unsigned char* destPtr;
uint32_t destPos;
if (writing) {
if (!aResult.SetCapacity(aLen, mozilla::fallible)) {
if (!aResult.SetLength(len, mozilla::fallible)) {
return NS_ERROR_OUT_OF_MEMORY;
}
destPos = 0;
destPtr = reinterpret_cast<unsigned char*>(aResult.BeginWriting());
}
const char* last = aStr;
const char* p = aStr;
const char* end = aStr + len;
for (int i = 0; i < aLen; ++i, ++p) {
if (*p == HEX_ESCAPE && i < aLen - 2) {
for (const char* p = aStr; p < end; ++p) {
if (*p == HEX_ESCAPE && p + 2 < end) {
unsigned char c1 = *((unsigned char*)p + 1);
unsigned char c2 = *((unsigned char*)p + 2);
unsigned char u = (UNHEX(c1) << 4) + UNHEX(c2);
if (ISHEX(c1) && ISHEX(c2) &&
if (mozilla::IsAsciiHexDigit(c1) && mozilla::IsAsciiHexDigit(c2) &&
(!skipInvalidHostChar || dontNeedEscape(u, aFlags) || c1 >= '8') &&
((c1 < '8' && !ignoreAscii) || (c1 >= '8' && !ignoreNonAscii)) &&
!(skipControl &&
(c1 < '2' || (c1 == '7' && (c2 == 'f' || c2 == 'F'))))) {
if (!writing) {
if (MOZ_UNLIKELY(!writing)) {
writing = true;
if (!aResult.SetCapacity(aLen, mozilla::fallible)) {
if (!aResult.SetLength(len, mozilla::fallible)) {
return NS_ERROR_OUT_OF_MEMORY;
}
destPos = 0;
destPtr = reinterpret_cast<unsigned char*>(aResult.BeginWriting());
}
if (p > last) {
if (!aResult.Append(last, p - last, mozilla::fallible)) {
return NS_ERROR_OUT_OF_MEMORY;
}
auto toCopy = p - last;
memcpy(destPtr + destPos, last, toCopy);
destPos += toCopy;
MOZ_ASSERT(destPos <= len);
last = p;
}
if (!aResult.Append(u, mozilla::fallible)) {
return NS_ERROR_OUT_OF_MEMORY;
}
i += 2;
destPtr[destPos] = u;
destPos += 1;
MOZ_ASSERT(destPos <= len);
p += 2;
last += 3;
}
}
}
if (writing && last < aStr + aLen) {
if (!aResult.Append(last, aStr + aLen - last, mozilla::fallible)) {
return NS_ERROR_OUT_OF_MEMORY;
}
if (writing && last < end) {
auto toCopy = end - last;
memcpy(destPtr + destPos, last, toCopy);
destPos += toCopy;
MOZ_ASSERT(destPos <= len);
}
if (writing) {
aResult.Truncate(destPos);
}
aDidAppend = writing;