Merge inbound to m-c. a=merge
Merge inbound to m-c. a=merge
--- a/browser/components/translation/translation-infobar.xml
+++ b/browser/components/translation/translation-infobar.xml
@@ -27,17 +27,17 @@
type="arrow" align="start">
<xul:image class="translation-welcome-logo"/>
<xul:vbox flex="1" class="translation-welcome-content">
<xul:description class="translation-welcome-headline"
anonid="welcomeHeadline"/>
<xul:description class="translation-welcome-body" anonid="welcomeBody"/>
<xul:hbox align="center">
<xul:label anonid="learnMore" class="plain text-link"
- onclick="openUILinkIn('https://support.mozilla.org/en-US/products/firefox/automatic-translation', 'tab'); this.parentNode.parentNode.parentNode.hidePopup();"/>
+ onclick="openUILinkIn('https://support.mozilla.org/kb/automatic-translation', 'tab'); this.parentNode.parentNode.parentNode.hidePopup();"/>
<xul:spacer flex="1"/>
<xul:button class="translate-infobar-element" anonid="thanksButton"
onclick="this.parentNode.parentNode.parentNode.hidePopup();"/>
</xul:hbox>
</xul:vbox>
</xul:panel>
<xul:deck anonid="translationStates" selectedIndex="0">
--- a/build/automation-build.mk
+++ b/build/automation-build.mk
@@ -27,22 +27,16 @@ AUTOMATION_PPARGS += -DIS_MAC=0
endif
ifeq ($(OS_ARCH),Linux)
AUTOMATION_PPARGS += -DIS_LINUX=1
else
AUTOMATION_PPARGS += -DIS_LINUX=0
endif
-ifeq ($(MOZ_BUILD_APP),camino)
-AUTOMATION_PPARGS += -DIS_CAMINO=1
-else
-AUTOMATION_PPARGS += -DIS_CAMINO=0
-endif
-
ifeq ($(host_os), cygwin)
AUTOMATION_PPARGS += -DIS_CYGWIN=1
endif
ifeq ($(ENABLE_TESTS), 1)
AUTOMATION_PPARGS += -DIS_TEST_BUILD=1
else
AUTOMATION_PPARGS += -DIS_TEST_BUILD=0
--- a/build/automation.py.in
+++ b/build/automation.py.in
@@ -61,17 +61,16 @@ from mozprofile.permissions import Serve
#expand _IS_WIN32 = len("__WIN32__") != 0
#expand _IS_MAC = __IS_MAC__ != 0
#expand _IS_LINUX = __IS_LINUX__ != 0
#ifdef IS_CYGWIN
#expand _IS_CYGWIN = __IS_CYGWIN__ == 1
#else
_IS_CYGWIN = False
#endif
-#expand _IS_CAMINO = __IS_CAMINO__ != 0
#expand _BIN_SUFFIX = __BIN_SUFFIX__
#expand _DEFAULT_APP = "./" + __BROWSER_PATH__
#expand _CERTS_SRC_DIR = __CERTS_SRC_DIR__
#expand _IS_TEST_BUILD = __IS_TEST_BUILD__
#expand _IS_DEBUG_BUILD = __IS_DEBUG_BUILD__
#expand _CRASHREPORTER = __CRASHREPORTER__ == 1
#expand _IS_ASAN = __IS_ASAN__ == 1
@@ -135,17 +134,16 @@ class Automation(object):
for setting up the browser environment.
"""
DIST_BIN = _DIST_BIN
IS_WIN32 = _IS_WIN32
IS_MAC = _IS_MAC
IS_LINUX = _IS_LINUX
IS_CYGWIN = _IS_CYGWIN
- IS_CAMINO = _IS_CAMINO
BIN_SUFFIX = _BIN_SUFFIX
UNIXISH = not IS_WIN32 and not IS_MAC
DEFAULT_APP = _DEFAULT_APP
CERTS_SRC_DIR = _CERTS_SRC_DIR
IS_TEST_BUILD = _IS_TEST_BUILD
IS_DEBUG_BUILD = _IS_DEBUG_BUILD
@@ -745,17 +743,17 @@ class Automation(object):
if status != 0 and not didTimeout and not hitMaxTime:
self.log.info("TEST-UNEXPECTED-FAIL | %s | Exited with code %d during test run", self.lastTestSeen, status)
return status
def buildCommandLine(self, app, debuggerInfo, profileDir, testURL, extraArgs):
""" build the application command line """
cmd = os.path.abspath(app)
- if self.IS_MAC and not self.IS_CAMINO and os.path.exists(cmd + "-bin"):
+ if self.IS_MAC and os.path.exists(cmd + "-bin"):
# Prefer 'app-bin' in case 'app' is a shell script.
# We can remove this hack once bug 673899 etc are fixed.
cmd += "-bin"
args = []
if debuggerInfo:
args.extend(debuggerInfo.args)
@@ -767,20 +765,17 @@ class Automation(object):
if self.IS_CYGWIN:
profileDirectory = commands.getoutput("cygpath -w \"" + profileDir + "/\"")
else:
profileDirectory = profileDir + "/"
args.extend(("-no-remote", "-profile", profileDirectory))
if testURL is not None:
- if self.IS_CAMINO:
- args.extend(("-url", testURL))
- else:
- args.append((testURL))
+ args.append((testURL))
args.extend(extraArgs)
return cmd, args
def checkForZombies(self, processLog, utilityPath, debuggerInfo):
""" Look for hung processes """
if not os.path.exists(processLog):
self.log.info('Automation Error: PID log not found: %s', processLog)
# Whilst no hung process was found, the run should still display as a failure
--- a/build/binary-location.mk
+++ b/build/binary-location.mk
@@ -7,17 +7,13 @@
ifneq (,$(filter WINNT,$(OS_ARCH)))
program = $(MOZ_APP_NAME)$(BIN_SUFFIX)
else
program = $(MOZ_APP_NAME)-bin$(BIN_SUFFIX)
endif
TARGET_DIST = $(TARGET_DEPTH)/dist
-ifeq ($(MOZ_BUILD_APP),camino)
-browser_path = $(TARGET_DIST)/Camino.app/Contents/MacOS/Camino
-else
ifeq ($(OS_ARCH),Darwin)
browser_path = $(TARGET_DIST)/$(MOZ_MACBUNDLE_NAME)/Contents/MacOS/$(program)
else
browser_path = $(TARGET_DIST)/bin/$(program)
endif
-endif
--- a/build/mach_bootstrap.py
+++ b/build/mach_bootstrap.py
@@ -40,17 +40,16 @@ SEARCH_PATHS = [
'dom/bindings/parser',
'layout/tools/reftest',
'other-licenses/ply',
'xpcom/idl-parser',
'testing',
'testing/xpcshell',
'testing/web-platform',
'testing/web-platform/harness',
- 'testing/marionette/client',
'testing/marionette/client/marionette',
'testing/marionette/transport',
'testing/mozbase/mozcrash',
'testing/mozbase/mozdebug',
'testing/mozbase/mozdevice',
'testing/mozbase/mozfile',
'testing/mozbase/mozhttpd',
'testing/mozbase/mozlog',
--- a/dom/base/URLSearchParams.cpp
+++ b/dom/base/URLSearchParams.cpp
@@ -252,21 +252,29 @@ URLSearchParams::GetAll(const nsAString&
}
}
}
void
URLSearchParams::Set(const nsAString& aName, const nsAString& aValue)
{
Param* param = nullptr;
- for (uint32_t i = 0, len = mSearchParams.Length(); i < len; ++i) {
- if (mSearchParams[i].mKey.Equals(aName)) {
+ for (uint32_t i = 0, len = mSearchParams.Length(); i < len;) {
+ if (!mSearchParams[i].mKey.Equals(aName)) {
+ ++i;
+ continue;
+ }
+ if (!param) {
param = &mSearchParams[i];
- break;
+ ++i;
+ continue;
}
+ // Remove duplicates.
+ mSearchParams.RemoveElementAt(i);
+ --len;
}
if (!param) {
param = mSearchParams.AppendElement();
param->mKey = aName;
}
param->mValue = aValue;
--- a/dom/base/test/test_urlSearchParams.html
+++ b/dom/base/test/test_urlSearchParams.html
@@ -283,28 +283,67 @@ https://bugzilla.mozilla.org/show_bug.cg
var url = new URL('http://www.example.net?a=b');
is(url.searchParams.get('b'), null, "URL.searchParams.get('b') should be null");
is(url.searchParams.get('a'), 'b', "URL.searchParams.get('a')");
runTest();
}
+ function testSet() {
+ var u = new URLSearchParams();
+ u.set('a','b');
+ u.set('e','c');
+ u.set('i','d');
+ u.set('o','f');
+ u.set('u','g');
+
+ is(u.get('a'), 'b', "URL.searchParams.get('a') should return b");
+ is(u.getAll('a').length, 1, "URLSearchParams.getAll('a').length should be 1");
+
+ u.set('a','h1');
+ u.set('a','h2');
+ u.set('a','h3');
+ u.set('a','h4');
+ is(u.get('a'), 'h4', "URL.searchParams.get('a') should return h4");
+ is(u.getAll('a').length, 1, "URLSearchParams.getAll('a').length should be 1");
+
+ is(u.get('e'), 'c', "URL.searchParams.get('e') should return c");
+ is(u.get('i'), 'd', "URL.searchParams.get('i') should return d");
+ is(u.get('o'), 'f', "URL.searchParams.get('o') should return f");
+ is(u.get('u'), 'g', "URL.searchParams.get('u') should return g");
+
+ is(u.getAll('e').length, 1, "URLSearchParams.getAll('e').length should be 1");
+ is(u.getAll('i').length, 1, "URLSearchParams.getAll('i').length should be 1");
+ is(u.getAll('o').length, 1, "URLSearchParams.getAll('o').length should be 1");
+ is(u.getAll('u').length, 1, "URLSearchParams.getAll('u').length should be 1");
+
+ u = new URLSearchParams("name1=value1&name1=value2&name1=value3");
+ is(u.get('name1'), 'value1', "URL.searchParams.get('name1') should return value1");
+ is(u.getAll('name1').length, 3, "URLSearchParams.getAll('name1').length should be 3");
+ u.set('name1','firstPair');
+ is(u.get('name1'), 'firstPair', "URL.searchParams.get('name1') should return firstPair");
+ is(u.getAll('name1').length, 1, "URLSearchParams.getAll('name1').length should be 1");
+
+ runTest();
+ }
+
var tests = [
testSimpleURLSearchParams,
testCopyURLSearchParams,
testParserURLSearchParams,
testURL,
function() { testElement(document.getElementById('anchor')) },
function() { testElement(document.getElementById('area')) },
testEncoding,
testMultiURL,
testOrdering,
testDelete,
- testGetNULL
+ testGetNULL,
+ testSet
];
function runTest() {
if (!tests.length) {
SimpleTest.finish();
return;
}
--- a/dom/canvas/WebGLContextGL.cpp
+++ b/dom/canvas/WebGLContextGL.cpp
@@ -3870,34 +3870,30 @@ WebGLContext::TexSubImage2D_base(TexImag
}
if (!tex->HasImageInfoAt(texImageTarget, level)) {
return ErrorInvalidOperation("texSubImage2D: no previously defined texture image");
}
const WebGLTexture::ImageInfo& imageInfo = tex->ImageInfoAt(texImageTarget, level);
const TexInternalFormat existingEffectiveInternalFormat = imageInfo.EffectiveInternalFormat();
- TexInternalFormat existingUnsizedInternalFormat = LOCAL_GL_NONE;
- TexType existingType = LOCAL_GL_NONE;
- UnsizedInternalFormatAndTypeFromEffectiveInternalFormat(existingEffectiveInternalFormat,
- &existingUnsizedInternalFormat,
- &existingType);
-
- if (!ValidateTexImage(2, texImageTarget, level, existingUnsizedInternalFormat.get(),
+
+ if (!ValidateTexImage(2, texImageTarget, level,
+ existingEffectiveInternalFormat.get(),
xoffset, yoffset, 0,
width, height, 0,
0, format, type, func))
{
return;
}
if (!ValidateTexInputData(type, jsArrayType, func))
return;
- if (type != existingType) {
+ if (type != TypeFromInternalFormat(existingEffectiveInternalFormat)) {
return ErrorInvalidOperation("texSubImage2D: type differs from that of the existing image");
}
size_t srcTexelSize = size_t(-1);
if (srcFormat == WebGLTexelFormat::Auto) {
const size_t bitsPerTexel = GetBitsPerTexel(existingEffectiveInternalFormat);
MOZ_ASSERT((bitsPerTexel % 8) == 0); // should not have compressed formats here.
srcTexelSize = bitsPerTexel / 8;
--- a/dom/canvas/WebGLContextValidate.cpp
+++ b/dom/canvas/WebGLContextValidate.cpp
@@ -1137,32 +1137,54 @@ WebGLContext::ValidateTexImage(GLuint di
ErrorInvalidValue("%s: border must be 0", info);
return false;
}
/* Check incoming image format and type */
if (!ValidateTexImageFormatAndType(format, type, func))
return false;
+ if (!TexInternalFormat::IsValueLegal(internalFormat)) {
+ ErrorInvalidEnum("%s: invalid internalformat enum %s", info, EnumName(internalFormat));
+ return false;
+ }
+ TexInternalFormat unsizedInternalFormat =
+ UnsizedInternalFormatFromInternalFormat(internalFormat);
+
if (IsCompressedFunc(func)) {
if (!ValidateCompTexImageInternalFormat(internalFormat, func)) {
return false;
}
} else if (IsCopyFunc(func)) {
- if (!ValidateCopyTexImageInternalFormat(internalFormat, func)) {
+ if (!ValidateCopyTexImageInternalFormat(unsizedInternalFormat.get(), func)) {
return false;
}
- } else if (format != internalFormat) {
+ } else if (format != unsizedInternalFormat) {
if (IsWebGL2()) {
// In WebGL2, it's OK to have internalformat != format if internalformat is the sized
// internal format corresponding to the (format, type) pair according to Table 3.2
// in the OpenGL ES 3.0.3 spec.
if (internalFormat != EffectiveInternalFormatFromInternalFormatAndType(format, type)) {
- ErrorInvalidOperation("%s: internalformat does not match format and type", info);
- return false;
+ bool exceptionallyAllowed = false;
+ if (internalFormat == LOCAL_GL_SRGB8_ALPHA8 &&
+ format == LOCAL_GL_RGBA &&
+ type == LOCAL_GL_UNSIGNED_BYTE)
+ {
+ exceptionallyAllowed = true;
+ }
+ else if (internalFormat == LOCAL_GL_SRGB8 &&
+ format == LOCAL_GL_RGB &&
+ type == LOCAL_GL_UNSIGNED_BYTE)
+ {
+ exceptionallyAllowed = true;
+ }
+ if (!exceptionallyAllowed) {
+ ErrorInvalidOperation("%s: internalformat does not match format and type", info);
+ return false;
+ }
}
} else {
// in WebGL 1, format must be equal to internalformat
ErrorInvalidOperation("%s: internalformat does not match format", info);
return false;
}
}
--- a/dom/inputmethod/forms.js
+++ b/dom/inputmethod/forms.js
@@ -219,17 +219,18 @@ let FormAssistant = {
isKeyboardOpened: false,
selectionStart: -1,
selectionEnd: -1,
textBeforeCursor: "",
textAfterCursor: "",
scrollIntoViewTimeout: null,
_focusedElement: null,
_focusCounter: 0, // up one for every time we focus a new element
- _observer: null,
+ _focusDeleteObserver: null,
+ _focusContentObserver: null,
_documentEncoder: null,
_editor: null,
_editing: false,
_selectionPrivate: null,
get focusedElement() {
if (this._focusedElement && Cu.isDeadWrapper(this._focusedElement))
this._focusedElement = null;
@@ -245,19 +246,23 @@ let FormAssistant = {
setFocusedElement: function fa_setFocusedElement(element) {
let self = this;
if (element === this.focusedElement)
return;
if (this.focusedElement) {
this.focusedElement.removeEventListener('compositionend', this);
- if (this._observer) {
- this._observer.disconnect();
- this._observer = null;
+ if (this._focusDeleteObserver) {
+ this._focusDeleteObserver.disconnect();
+ this._focusDeleteObserver = null;
+ }
+ if (this._focusContentObserver) {
+ this._focusContentObserver.disconnect();
+ this._focusContentObserver = null;
}
if (this._selectionPrivate) {
this._selectionPrivate.removeSelectionListener(this);
this._selectionPrivate = null;
}
}
this._documentEncoder = null;
@@ -287,33 +292,46 @@ let FormAssistant = {
if (selection) {
this._selectionPrivate = selection.QueryInterface(Ci.nsISelectionPrivate);
this._selectionPrivate.addSelectionListener(this);
}
}
// If our focusedElement is removed from DOM we want to handle it properly
let MutationObserver = element.ownerDocument.defaultView.MutationObserver;
- this._observer = new MutationObserver(function(mutations) {
+ this._focusDeleteObserver = new MutationObserver(function(mutations) {
var del = [].some.call(mutations, function(m) {
return [].some.call(m.removedNodes, function(n) {
return n.contains(element);
});
});
if (del && element === self.focusedElement) {
self.hideKeyboard();
self.selectionStart = -1;
self.selectionEnd = -1;
}
});
- this._observer.observe(element.ownerDocument.body, {
+ this._focusDeleteObserver.observe(element.ownerDocument.body, {
childList: true,
subtree: true
});
+
+ // If contenteditable, also add a mutation observer on its content and
+ // call selectionChanged when a change occurs
+ if (isContentEditable(element)) {
+ this._focusContentObserver = new MutationObserver(function() {
+ this.updateSelection();
+ }.bind(this));
+
+ this._focusContentObserver.observe(element, {
+ childList: true,
+ subtree: true
+ });
+ }
}
this.focusedElement = element;
},
notifySelectionChanged: function(aDocument, aSelection, aReason) {
this.updateSelection();
},
new file mode 100644
--- /dev/null
+++ b/dom/inputmethod/mochitest/file_test_contenteditable.html
@@ -0,0 +1,17 @@
+<!DOCTYPE HTML>
+<html>
+<body>
+<div id="text" contenteditable>Jan Jongboom</div>
+<script type="application/javascript;version=1.7">
+ var t = document.querySelector('#text');
+
+ t.focus();
+ var range = document.createRange();
+ range.selectNodeContents(t);
+ range.collapse(false);
+ var selection = window.getSelection();
+ selection.removeAllRanges();
+ selection.addRange(range);
+</script>
+</body>
+</html>
--- a/dom/inputmethod/mochitest/mochitest.ini
+++ b/dom/inputmethod/mochitest/mochitest.ini
@@ -1,24 +1,26 @@
[DEFAULT]
# Not supported on Android, bug 983015 for B2G emulator
skip-if = (toolkit == 'android' || toolkit == 'gonk') || e10s
support-files =
inputmethod_common.js
file_inputmethod.html
file_inputmethod_1043828.html
file_test_app.html
+ file_test_contenteditable.html
file_test_sendkey_cancel.html
file_test_sms_app.html
file_test_sms_app_1066515.html
[test_basic.html]
[test_bug944397.html]
[test_bug949059.html]
[test_bug953044.html]
[test_bug960946.html]
[test_bug978918.html]
[test_bug1026997.html]
[test_bug1043828.html]
+[test_bug1059163.html]
[test_bug1066515.html]
[test_delete_focused_element.html]
[test_sendkey_cancel.html]
[test_two_inputs.html]
new file mode 100644
--- /dev/null
+++ b/dom/inputmethod/mochitest/test_bug1059163.html
@@ -0,0 +1,77 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1059163
+-->
+<head>
+ <title>Basic test for repeat sendKey events</title>
+ <script type="application/javascript;version=1.7" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript;version=1.7" src="inputmethod_common.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1059163">Mozilla Bug 1059163</a>
+<p id="display"></p>
+<pre id="test">
+<script class="testbody" type="application/javascript;version=1.7">
+inputmethod_setup(function() {
+ runTest();
+});
+
+// The frame script running in the file
+function appFrameScript() {
+ addMessageListener('test:InputMethod:clear', function() {
+ var t = content.document.getElementById('text');
+ t.innerHTML = '';
+ });
+}
+
+function runTest() {
+ let im = navigator.mozInputMethod;
+
+ // Set current page as an input method.
+ SpecialPowers.wrap(im).setActive(true);
+
+ // Create an app frame to recieve keyboard inputs.
+ let app = document.createElement('iframe');
+ app.src = 'file_test_contenteditable.html';
+ app.setAttribute('mozbrowser', true);
+ document.body.appendChild(app);
+ app.addEventListener('mozbrowserloadend', function() {
+ let mm = SpecialPowers.getBrowserFrameMessageManager(app);
+
+ function register() {
+ im.inputcontext.onselectionchange = function() {
+ im.inputcontext.onselectionchange = null;
+
+ is(im.inputcontext.textBeforeCursor, '', 'textBeforeCursor');
+ is(im.inputcontext.textBeforeCursor, '', 'textAfterCursor');
+ is(im.inputcontext.selectionStart, 0, 'selectionStart');
+ is(im.inputcontext.selectionEnd, 0, 'selectionEnd');
+
+ inputmethod_cleanup();
+ };
+
+ mm.sendAsyncMessage('test:InputMethod:clear');
+ }
+
+ if (im.inputcontext) {
+ register();
+ }
+ else {
+ im.oninputcontextchange = function() {
+ if (im.inputcontext) {
+ im.oninputcontextchange = null;
+ register();
+ }
+ };
+ }
+
+ mm.loadFrameScript('data:,(' + appFrameScript.toString() + ')();', false);
+ });
+}
+</script>
+</pre>
+</body>
+</html>
+
--- a/dom/locales/en-US/chrome/layout/css.properties
+++ b/dom/locales/en-US/chrome/layout/css.properties
@@ -102,17 +102,16 @@ PEColorBadRGBContents=Expected number or
PEColorComponentBadTerm=Expected '%2$S' but found '%1$S'.
PEColorHueEOF=hue
PEExpectedComma=Expected ',' but found '%1$S'.
PEColorSaturationEOF=saturation
PEColorLightnessEOF=lightness
PEColorOpacityEOF=opacity in color value
PEExpectedNumber=Expected a number but found '%1$S'.
PEExpectedCloseParen=Expected ')' but found '%1$S'.
-PEClipPathEOF=<basic-shape> or reference box
PEDeclEndEOF=';' or '}' to end declaration
PEParseDeclarationNoColon=Expected ':' but found '%1$S'.
PEParseDeclarationDeclExpected=Expected declaration but found '%1$S'.
PEEndOfDeclEOF=end of declaration
PEImportantEOF=important
PEExpectedImportant=Expected 'important' but found '%1$S'.
PEBadDeclEnd=Expected ';' to terminate declaration but found '%1$S'.
PEBadDeclOrRuleEnd2=Expected ';' or '}' to terminate declaration but found '%1$S'.
--- a/js/src/builtin/Object.cpp
+++ b/js/src/builtin/Object.cpp
@@ -45,49 +45,63 @@ js::obj_construct(JSContext *cx, unsigne
}
/* ES5 15.2.4.7. */
static bool
obj_propertyIsEnumerable(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
+ HandleValue idValue = args.get(0);
+
+ // As an optimization, provide a fast path when rooting is not necessary and
+ // we can safely retrieve the attributes from the object's shape.
+
+ /* Steps 1-2. */
+ jsid id;
+ if (args.thisv().isObject() && ValueToId<NoGC>(cx, idValue, &id)) {
+ JSObject *obj = &args.thisv().toObject(), *pobj;
+
+ /* Step 3. */
+ Shape *shape;
+ if (!obj->is<ProxyObject>() &&
+ HasOwnProperty<NoGC>(cx, obj->getOps()->lookupGeneric, obj, id, &pobj, &shape))
+ {
+ /* Step 4. */
+ if (!shape) {
+ args.rval().setBoolean(false);
+ return true;
+ }
+
+ /* Step 5. */
+ if (pobj->isNative()) {
+ unsigned attrs = GetShapeAttributes(pobj, shape);
+ args.rval().setBoolean((attrs & JSPROP_ENUMERATE) != 0);
+ return true;
+ }
+ }
+ }
+
/* Step 1. */
- RootedId id(cx);
- if (!ValueToId<CanGC>(cx, args.get(0), &id))
+ RootedId idRoot(cx);
+ if (!ValueToId<CanGC>(cx, idValue, &idRoot))
return false;
/* Step 2. */
RootedObject obj(cx, ToObject(cx, args.thisv()));
if (!obj)
return false;
- /* Steps 3. */
- RootedObject pobj(cx);
- RootedShape prop(cx);
- if (!JSObject::lookupGeneric(cx, obj, id, &pobj, &prop))
+ /* Step 3. */
+ Rooted<PropertyDescriptor> desc(cx);
+ if (!GetOwnPropertyDescriptor(cx, obj, idRoot, &desc))
return false;
- /* Step 4. */
- if (!prop) {
- args.rval().setBoolean(false);
- return true;
- }
-
- if (pobj != obj) {
- args.rval().setBoolean(false);
- return true;
- }
-
- /* Step 5. */
- unsigned attrs;
- if (!JSObject::getGenericAttributes(cx, pobj, id, &attrs))
- return false;
-
- args.rval().setBoolean((attrs & JSPROP_ENUMERATE) != 0);
+ /* Steps 4-5. */
+ args.rval().setBoolean(desc.object() && desc.isEnumerable());
return true;
}
#if JS_HAS_TOSOURCE
static bool
obj_toSource(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Object/propertyIsEnumerable-proxy.js
@@ -0,0 +1,55 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function logProxy(object) {
+ var log = [];
+ var handler = {
+ getOwnPropertyDescriptor(target, propertyKey) {
+ log.push(propertyKey);
+ return Object.getOwnPropertyDescriptor(target, propertyKey);
+ }
+ };
+ var proxy = new Proxy(object, new Proxy(handler, {
+ get(target, propertyKey, receiver) {
+ if (!(propertyKey in target)) {
+ throw new Error(`Unexpected call to trap: "${propertyKey}"`);
+ }
+ return target[propertyKey];
+ }
+ }));
+ return {proxy, log};
+}
+
+for (var property of ["string-property", Symbol("symbol-property")]) {
+ // Test 1: property is not present on object
+ var {proxy, log} = logProxy({});
+ var result = Object.prototype.propertyIsEnumerable.call(proxy, property);
+ assertEq(result, false);
+ assertDeepEq(log, [property]);
+
+ // Test 2: property is present on object and enumerable
+ var {proxy, log} = logProxy({[property]: 0});
+ var result = Object.prototype.propertyIsEnumerable.call(proxy, property);
+ assertEq(result, true);
+ assertDeepEq(log, [property]);
+
+ // Test 3: property is present on object, but not enumerable
+ var {proxy, log} = logProxy(Object.defineProperty({[property]: 0}, property, {enumerable: false}));
+ var result = Object.prototype.propertyIsEnumerable.call(proxy, property);
+ assertEq(result, false);
+ assertDeepEq(log, [property]);
+
+ // Test 4: property is present on prototype object
+ var {proxy, log} = logProxy(Object.create({[property]: 0}));
+ var result = Object.prototype.propertyIsEnumerable.call(proxy, property);
+ assertEq(result, false);
+ assertDeepEq(log, [property]);
+
+ // Test 5: property is present on prototype object, prototype is proxy object
+ var {proxy, log} = logProxy({[property]: 0});
+ var result = Object.prototype.propertyIsEnumerable.call(Object.create(proxy), property);
+ assertEq(result, false);
+ assertDeepEq(log, []);
+}
+
+reportCompare(0, 0);
--- a/js/src/vm/Shape.h
+++ b/js/src/vm/Shape.h
@@ -568,25 +568,25 @@ typedef HashSet<ReadBarrieredUnownedBase
StackBaseShape,
SystemAllocPolicy> BaseShapeSet;
class Shape : public gc::TenuredCell
{
friend class ::JSObject;
friend class ::JSFunction;
- friend class js::Bindings;
- friend class js::Nursery;
- friend class js::gc::ForkJoinNursery;
- friend class js::NativeObject;
- friend class js::PropertyTree;
- friend class js::StaticBlockObject;
- friend class js::ShapeGetterSetterRef;
- friend struct js::StackShape;
- friend struct js::StackBaseShape;
+ friend class Bindings;
+ friend class Nursery;
+ friend class gc::ForkJoinNursery;
+ friend class NativeObject;
+ friend class PropertyTree;
+ friend class StaticBlockObject;
+ friend class ShapeGetterSetterRef;
+ friend struct StackShape;
+ friend struct StackBaseShape;
protected:
HeapPtrBaseShape base_;
PreBarrieredId propid_;
JS_ENUM_HEADER(SlotInfo, uint32_t)
{
/* Number of fixed slots in objects with this shape. */
--- a/layout/base/RestyleManager.cpp
+++ b/layout/base/RestyleManager.cpp
@@ -261,16 +261,19 @@ DoApplyRenderingChangeToTree(nsIFrame* a
// SVG effects paints the opacity without using
// nsDisplayOpacity. We need to invalidate manually.
aFrame->InvalidateFrameSubtree();
}
}
if ((aChange & nsChangeHint_UpdateTransformLayer) &&
aFrame->IsTransformed()) {
ActiveLayerTracker::NotifyRestyle(aFrame, eCSSProperty_transform);
+ if (!aFrame->GetPrevContinuation()) {
+ nsSVGEffects::InvalidateRenderingObservers(aFrame);
+ }
// If we're not already going to do an invalidating paint, see
// if we can get away with only updating the transform on a
// layer for this frame, and not scheduling an invalidating
// paint.
if (!needInvalidatingPaint) {
Layer* layer;
needInvalidatingPaint |= !aFrame->TryUpdateTransformOnly(&layer);
--- a/layout/forms/nsComboboxControlFrame.cpp
+++ b/layout/forms/nsComboboxControlFrame.cpp
@@ -1591,22 +1591,17 @@ nsComboboxControlFrame::RestoreState(nsP
return NS_ERROR_FAILURE;
nsIStatefulFrame* stateful = do_QueryFrame(mListControlFrame);
NS_ASSERTION(stateful, "Must implement nsIStatefulFrame");
return stateful->RestoreState(aState);
}
-//
-// Camino uses a native widget for the combobox
-// popup, which affects drawing and event
-// handling here and in nsListControlFrame.
-//
-// Also, Fennec use a custom combobox built-in widget
+// Fennec uses a custom combobox built-in widget.
//
/* static */
bool
nsComboboxControlFrame::ToolkitHasNativePopup()
{
#ifdef MOZ_USE_NATIVE_POPUP_WINDOWS
return true;
--- a/layout/generic/nsTextFrame.cpp
+++ b/layout/generic/nsTextFrame.cpp
@@ -4471,16 +4471,25 @@ nsTextFrame::CharacterDataChanged(Charac
}
return NS_OK;
}
/* virtual */ void
nsTextFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
{
+ // A belt-and-braces check just in case we never get the
+ // MarkIntrinsicISizesDirty call from the style system.
+ if (StyleText()->mTextTransform == NS_STYLE_TEXT_TRANSFORM_CAPITALIZE &&
+ mTextRun &&
+ !(mTextRun->GetFlags() & nsTextFrameUtils::TEXT_IS_TRANSFORMED)) {
+ NS_ERROR("the current textrun doesn't match the style");
+ // The current textrun is now of the wrong type.
+ ClearTextRuns();
+ }
nsFrame::DidSetStyleContext(aOldStyleContext);
}
class nsDisplayTextGeometry : public nsDisplayItemGenericGeometry
{
public:
nsDisplayTextGeometry(nsDisplayItem* aItem, nsDisplayListBuilder* aBuilder)
: nsDisplayItemGenericGeometry(aItem, aBuilder)
--- a/layout/style/AnimationCommon.h
+++ b/layout/style/AnimationCommon.h
@@ -145,18 +145,17 @@ typedef InfallibleTArray<nsRefPtr<dom::A
enum EnsureStyleRuleFlags {
EnsureStyleRule_IsThrottled,
EnsureStyleRule_IsNotThrottled
};
struct AnimationPlayerCollection : public PRCList
{
AnimationPlayerCollection(dom::Element *aElement, nsIAtom *aElementProperty,
- mozilla::css::CommonAnimationManager *aManager,
- TimeStamp aNow)
+ mozilla::css::CommonAnimationManager *aManager)
: mElement(aElement)
, mElementProperty(aElementProperty)
, mManager(aManager)
, mAnimationGeneration(0)
, mNeedsRefreshes(true)
#ifdef DEBUG
, mCalledPropertyDtor(false)
#endif
--- a/layout/style/nsAnimationManager.cpp
+++ b/layout/style/nsAnimationManager.cpp
@@ -137,18 +137,17 @@ nsAnimationManager::GetAnimationPlayers(
"other than :before or :after");
return nullptr;
}
AnimationPlayerCollection* collection =
static_cast<AnimationPlayerCollection*>(aElement->GetProperty(propName));
if (!collection && aCreateIfNeeded) {
// FIXME: Consider arena-allocating?
collection =
- new AnimationPlayerCollection(aElement, propName, this,
- mPresContext->RefreshDriver()->MostRecentRefresh());
+ new AnimationPlayerCollection(aElement, propName, this);
nsresult rv =
aElement->SetProperty(propName, collection,
&AnimationPlayerCollection::PropertyDtor, false);
if (NS_FAILED(rv)) {
NS_WARNING("SetProperty failed");
delete collection;
return nullptr;
}
--- a/layout/style/nsCSSParser.cpp
+++ b/layout/style/nsCSSParser.cpp
@@ -958,17 +958,17 @@ protected:
void SetParsingCompoundProperty(bool aBool) {
mParsingCompoundProperty = aBool;
}
bool IsParsingCompoundProperty(void) const {
return mParsingCompoundProperty;
}
/* Functions for basic shapes */
- bool ParseBasicShape(nsCSSValue& aValue);
+ bool ParseBasicShape(nsCSSValue& aValue, bool* aConsumedTokens);
bool ParsePolygonFunction(nsCSSValue& aValue);
/* Functions for transform Parsing */
bool ParseSingleTransform(bool aIsPrefixed, nsCSSValue& aValue);
bool ParseFunction(nsCSSKeyword aFunction, const int32_t aAllowedTypes[],
int32_t aVariantMaskAll, uint16_t aMinElems,
uint16_t aMaxElems, nsCSSValue &aValue);
bool ParseFunctionInternals(const int32_t aVariantMask[],
@@ -13839,27 +13839,29 @@ CSSParserImpl::ParsePolygonFunction(nsCS
if (numArgs > 1) {
functionArray->Item(1) = fillRuleValue;
}
return true;
}
bool
-CSSParserImpl::ParseBasicShape(nsCSSValue& aValue)
+CSSParserImpl::ParseBasicShape(nsCSSValue& aValue, bool* aConsumedTokens)
{
if (!GetToken(true)) {
return false;
}
if (mToken.mType != eCSSToken_Function) {
UngetToken();
return false;
}
+ // Specific shape function parsing always consumes tokens.
+ *aConsumedTokens = true;
nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent);
switch (keyword) {
case eCSSKeyword_polygon:
return ParsePolygonFunction(aValue);
default:
return false;
}
}
@@ -13871,39 +13873,50 @@ bool CSSParserImpl::ParseClipPath()
if (!ParseVariant(value, VARIANT_HUO, nullptr)) {
if (!nsLayoutUtils::CSSClipPathShapesEnabled()) {
// With CSS Clip Path Shapes disabled, we should only accept
// SVG clipPath reference and none.
REPORT_UNEXPECTED_TOKEN(PEExpectedNoneOrURL);
return false;
}
- bool shape = false, box = false;
nsCSSValueList* cur = value.SetListValue();
- bool eof = false;
- for (int i = 0; i < 2; ++i) {
- if (ParseBasicShape(cur->mValue) && !shape) {
- shape = true;
- } else if (ParseEnum(cur->mValue, nsCSSProps::kClipShapeSizingKTable) &&
- !box) {
- box = true;
- } else {
- break;
- }
- if (!GetToken(true)) {
- eof = true;
- break;
- }
- UngetToken();
- cur->mNext = new nsCSSValueList;
- cur = cur->mNext;
- }
- if (!shape && !box && !eof) {
- REPORT_UNEXPECTED_EOF(PEClipPathEOF);
- return false;
+
+ nsCSSValue referenceBox;
+ bool hasBox = ParseEnum(referenceBox, nsCSSProps::kClipShapeSizingKTable);
+
+ nsCSSValue basicShape;
+ bool basicShapeConsumedTokens = false;
+ bool hasShape = ParseBasicShape(basicShape, &basicShapeConsumedTokens);
+
+ // Parsing wasn't successful if ParseBasicShape consumed tokens but failed
+ // or if the token was neither a reference box nor a basic shape.
+ if ((!hasShape && basicShapeConsumedTokens) || (!hasBox && !hasShape)) {
+ return false;
+ }
+
+ // We need to preserve the specified order of arguments for inline style.
+ if (hasBox) {
+ cur->mValue = referenceBox;
+ }
+
+ if (hasShape) {
+ if (hasBox) {
+ cur->mNext = new nsCSSValueList;
+ cur = cur->mNext;
+ }
+ cur->mValue = basicShape;
+ }
+
+ // Check if the second argument is a reference box if the first wasn't.
+ if (!hasBox &&
+ ParseEnum(referenceBox, nsCSSProps::kClipShapeSizingKTable)) {
+ cur->mNext = new nsCSSValueList;
+ cur = cur->mNext;
+ cur->mValue = referenceBox;
}
}
AppendValue(eCSSProperty_clip_path, value);
return true;
}
bool CSSParserImpl::ParseTransformOrigin(bool aPerspective)
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -5185,23 +5185,24 @@ nsComputedDOMStyle::CreatePrimitiveValue
aStyleBasicShape->GetShapeType() == nsStyleBasicShape::Type::ePolygon) {
nsROCSSPrimitiveValue* val = new nsROCSSPrimitiveValue;
// Shape function name and opening parenthesis.
nsAutoString shapeFunctionString;
AppendASCIItoUTF16(nsCSSKeywords::GetStringValue(eCSSKeyword_polygon),
shapeFunctionString);
shapeFunctionString.Append('(');
- uint8_t fillRule = aStyleBasicShape->GetFillRule();
- if (fillRule == NS_STYLE_FILL_RULE_EVENODD) {
+ bool hasEvenOdd = aStyleBasicShape->GetFillRule() ==
+ NS_STYLE_FILL_RULE_EVENODD;
+ if (hasEvenOdd) {
shapeFunctionString.AppendLiteral("evenodd");
}
for (size_t i = 0; i < aStyleBasicShape->Coordinates().Length(); i += 2) {
nsAutoString coordString;
- if (i > 0 || fillRule) {
+ if (i > 0 || hasEvenOdd) {
shapeFunctionString.AppendLiteral(", ");
}
SetCssTextToCoord(coordString,
aStyleBasicShape->Coordinates()[i]);
shapeFunctionString.Append(coordString);
shapeFunctionString.Append(' ');
SetCssTextToCoord(coordString,
aStyleBasicShape->Coordinates()[i + 1]);
--- a/layout/style/nsTransitionManager.cpp
+++ b/layout/style/nsTransitionManager.cpp
@@ -597,18 +597,17 @@ nsTransitionManager::GetElementTransitio
"should never try to create transitions for pseudo "
"other than :before or :after");
return nullptr;
}
AnimationPlayerCollection* collection =
static_cast<AnimationPlayerCollection*>(aElement->GetProperty(propName));
if (!collection && aCreateIfNeeded) {
// FIXME: Consider arena-allocating?
- collection = new AnimationPlayerCollection(aElement, propName, this,
- mPresContext->RefreshDriver()->MostRecentRefresh());
+ collection = new AnimationPlayerCollection(aElement, propName, this);
nsresult rv =
aElement->SetProperty(propName, collection,
&AnimationPlayerCollection::PropertyDtor, false);
if (NS_FAILED(rv)) {
NS_WARNING("SetProperty failed");
delete collection;
return nullptr;
}
--- a/layout/style/test/property_database.js
+++ b/layout/style/test/property_database.js
@@ -4607,31 +4607,65 @@ if (SpecialPowers.getBoolPref("layout.cs
"content-box",
"padding-box",
"border-box",
"margin-box",
"fill-box",
"stroke-box",
"view-box",
- "polygon(0 0) conten-box",
+ "polygon(0 0) content-box",
"border-box polygon(0 0)",
"padding-box polygon( 0 20px , 30px 20% ) ",
+ "polygon(evenodd, 20% 20em) content-box",
+ "polygon(evenodd, 20vh 20em) padding-box",
+ "polygon(evenodd, 20vh calc(20% + 20em)) border-box",
+ "polygon(evenodd, 20vh 20vw) margin-box",
+ "polygon(evenodd, 20pt 20cm) fill-box",
+ "polygon(evenodd, 20ex 20pc) stroke-box",
+ "polygon(evenodd, 20rem 20in) view-box",
],
invalid_values: [
"url(#test) url(#tes2)",
"polygon (0 0)",
"polygon(20px, 40px)",
"border-box content-box",
"polygon(0 0) polygon(0 0)",
"polygon(nonzero 0 0)",
"polygon(evenodd 20px 20px)",
"polygon(20px 20px, evenodd)",
"polygon(20px 20px, nonzero)",
+ "polygon(0 0) conten-box content-box",
+ "content-box polygon(0 0) conten-box",
+ "padding-box polygon(0 0) conten-box",
+ "polygon(0 0) polygon(0 0) content-box",
+ "polygon(0 0) content-box polygon(0 0)",
+ "polygon(0 0), content-box",
+ "polygon(0 0), polygon(0 0)",
+ "content-box polygon(0 0) polygon(0 0)",
+ "content-box polygon(0 0) none",
+ "none content-box polygon(0 0)",
+ "inherit content-box polygon(0 0)",
+ "initial polygon(0 0)",
+ "polygon(0 0) farthest-side",
+ "farthest-corner polygon(0 0)",
+ "polygon(0 0) farthest-corner",
+ "polygon(0 0) conten-box",
+ "polygon(0 0) polygon(0 0) farthest-corner",
+ "polygon(0 0) polygon(0 0) polygon(0 0)",
+ "border-box polygon(0, 0)",
+ "border-box padding-box",
+ "margin-box farthest-side",
+ "nonsense() border-box",
+ "border-box nonsense()",
+ ],
+ unbalanced_values: [
"polygon(30% 30%",
+ "polygon(nonzero, 20% 20px",
+ "polygon(evenodd, 20px 20px",
]
};
}
if (SpecialPowers.getBoolPref("layout.css.filters.enabled")) {
gCSSProperties["filter"] = {
domProp: "filter",
--- a/layout/xul/nsMenuBarFrame.cpp
+++ b/layout/xul/nsMenuBarFrame.cpp
@@ -72,24 +72,24 @@ nsMenuBarFrame::Init(nsIContent* a
// Hook up the menu bar as a key listener on the whole document. It will see every
// key press that occurs, but after everyone else does.
mTarget = aContent->GetComposedDoc();
// Also hook up the listener to the window listening for focus events. This is so we can keep proper
// state as the user alt-tabs through processes.
- mTarget->AddEventListener(NS_LITERAL_STRING("keypress"), mMenuBarListener, false);
- mTarget->AddEventListener(NS_LITERAL_STRING("keydown"), mMenuBarListener, false);
- mTarget->AddEventListener(NS_LITERAL_STRING("keyup"), mMenuBarListener, false);
+ mTarget->AddSystemEventListener(NS_LITERAL_STRING("keypress"), mMenuBarListener, false);
+ mTarget->AddSystemEventListener(NS_LITERAL_STRING("keydown"), mMenuBarListener, false);
+ mTarget->AddSystemEventListener(NS_LITERAL_STRING("keyup"), mMenuBarListener, false);
// mousedown event should be handled in all phase
- mTarget->AddEventListener(NS_LITERAL_STRING("mousedown"), mMenuBarListener, true);
- mTarget->AddEventListener(NS_LITERAL_STRING("mousedown"), mMenuBarListener, false);
- mTarget->AddEventListener(NS_LITERAL_STRING("blur"), mMenuBarListener, true);
+ mTarget->AddSystemEventListener(NS_LITERAL_STRING("mousedown"), mMenuBarListener, true);
+ mTarget->AddSystemEventListener(NS_LITERAL_STRING("mousedown"), mMenuBarListener, false);
+ mTarget->AddSystemEventListener(NS_LITERAL_STRING("blur"), mMenuBarListener, true);
}
NS_IMETHODIMP
nsMenuBarFrame::SetActive(bool aActiveFlag)
{
// If the activity is not changed, there is nothing to do.
if (mIsActive == aActiveFlag)
return NS_OK;
@@ -406,20 +406,20 @@ nsMenuBarFrame::RemoveKeyboardNavigator(
void
nsMenuBarFrame::DestroyFrom(nsIFrame* aDestructRoot)
{
nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
if (pm)
pm->SetActiveMenuBar(this, false);
- mTarget->RemoveEventListener(NS_LITERAL_STRING("keypress"), mMenuBarListener, false);
- mTarget->RemoveEventListener(NS_LITERAL_STRING("keydown"), mMenuBarListener, false);
- mTarget->RemoveEventListener(NS_LITERAL_STRING("keyup"), mMenuBarListener, false);
+ mTarget->RemoveSystemEventListener(NS_LITERAL_STRING("keypress"), mMenuBarListener, false);
+ mTarget->RemoveSystemEventListener(NS_LITERAL_STRING("keydown"), mMenuBarListener, false);
+ mTarget->RemoveSystemEventListener(NS_LITERAL_STRING("keyup"), mMenuBarListener, false);
- mTarget->RemoveEventListener(NS_LITERAL_STRING("mousedown"), mMenuBarListener, true);
- mTarget->RemoveEventListener(NS_LITERAL_STRING("mousedown"), mMenuBarListener, false);
- mTarget->RemoveEventListener(NS_LITERAL_STRING("blur"), mMenuBarListener, true);
+ mTarget->RemoveSystemEventListener(NS_LITERAL_STRING("mousedown"), mMenuBarListener, true);
+ mTarget->RemoveSystemEventListener(NS_LITERAL_STRING("mousedown"), mMenuBarListener, false);
+ mTarget->RemoveSystemEventListener(NS_LITERAL_STRING("blur"), mMenuBarListener, true);
NS_IF_RELEASE(mMenuBarListener);
nsBoxFrame::DestroyFrom(aDestructRoot);
}
--- a/layout/xul/tree/nsTreeBodyFrame.cpp
+++ b/layout/xul/tree/nsTreeBodyFrame.cpp
@@ -6,16 +6,17 @@
#include "mozilla/AsyncEventDispatcher.h"
#include "mozilla/ContentEvents.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/EventDispatcher.h"
#include "mozilla/MathAlgorithms.h"
#include "mozilla/MouseEvents.h"
#include "mozilla/Likely.h"
+#include "nsAlgorithm.h"
#include "nsCOMPtr.h"
#include "nsPresContext.h"
#include "nsNameSpaceManager.h"
#include "nsTreeBodyFrame.h"
#include "nsTreeSelection.h"
#include "nsTreeImageListener.h"
@@ -4041,88 +4042,65 @@ nsTreeBodyFrame::ScrollToHorizontalPosit
UpdateScrollbars(parts);
return rv;
}
nsresult
nsTreeBodyFrame::ScrollToRow(int32_t aRow)
{
ScrollParts parts = GetScrollParts();
- nsresult rv = ScrollToRowInternal(parts, aRow);
- NS_ENSURE_SUCCESS(rv, rv);
+ ScrollToRowInternal(parts, aRow);
UpdateScrollbars(parts);
- return rv;
+ return NS_OK;
}
nsresult nsTreeBodyFrame::ScrollToRowInternal(const ScrollParts& aParts, int32_t aRow)
{
ScrollInternal(aParts, aRow);
return NS_OK;
}
nsresult
nsTreeBodyFrame::ScrollByLines(int32_t aNumLines)
{
- if (!mView)
+ if (!mView) {
return NS_OK;
-
+ }
int32_t newIndex = mTopRowIndex + aNumLines;
- if (newIndex < 0)
- newIndex = 0;
- else {
- int32_t lastPageTopRow = mRowCount - mPageLength;
- if (newIndex > lastPageTopRow)
- newIndex = lastPageTopRow;
- }
ScrollToRow(newIndex);
-
return NS_OK;
}
nsresult
nsTreeBodyFrame::ScrollByPages(int32_t aNumPages)
{
- if (!mView)
+ if (!mView) {
return NS_OK;
-
+ }
int32_t newIndex = mTopRowIndex + aNumPages * mPageLength;
- if (newIndex < 0)
- newIndex = 0;
- else {
- int32_t lastPageTopRow = mRowCount - mPageLength;
- if (newIndex > lastPageTopRow)
- newIndex = lastPageTopRow;
- }
ScrollToRow(newIndex);
-
return NS_OK;
}
nsresult
nsTreeBodyFrame::ScrollInternal(const ScrollParts& aParts, int32_t aRow)
{
- if (!mView)
+ if (!mView) {
return NS_OK;
-
- int32_t delta = aRow - mTopRowIndex;
-
- if (delta > 0) {
- if (mTopRowIndex == (mRowCount - mPageLength + 1))
- return NS_OK;
}
- else {
- if (mTopRowIndex == 0)
- return NS_OK;
+ int32_t maxTopRowIndex = std::max(0, mRowCount - mPageLength);
+ MOZ_ASSERT(mTopRowIndex == mozilla::clamped(mTopRowIndex, 0, maxTopRowIndex));
+
+ aRow = mozilla::clamped(aRow, 0, maxTopRowIndex);
+ if (aRow == mTopRowIndex) {
+ return NS_OK;
}
-
- mTopRowIndex += delta;
-
+ mTopRowIndex = aRow;
Invalidate();
-
PostScrollEvent();
return NS_OK;
}
nsresult
nsTreeBodyFrame::ScrollHorzInternal(const ScrollParts& aParts, int32_t aPosition)
{
if (!mView || !aParts.mColumnsScrollFrame || !aParts.mHScrollbar)
--- a/netwerk/protocol/http/Http2Compression.cpp
+++ b/netwerk/protocol/http/Http2Compression.cpp
@@ -879,36 +879,16 @@ Http2Compressor::EncodeHeaderBlock(const
}
}
if(isColonHeader) {
continue;
}
int32_t valueIndex = colonIndex + 1;
- // if we have Expect: *100-continue,*" redact the 100-continue
- // as we don't have a good mechanism for clients to make use of it
- // anyhow
- if (name.EqualsLiteral("expect")) {
- const char *continueHeader =
- nsHttp::FindToken(beginBuffer + valueIndex, "100-continue",
- HTTP_HEADER_VALUE_SEPS);
- if (continueHeader) {
- char *writableVal = const_cast<char *>(continueHeader);
- memset(writableVal, 0, 12);
- writableVal += 12;
- // this will terminate safely because CRLF EOL has been confirmed
- while ((*writableVal == ' ') || (*writableVal == '\t') ||
- (*writableVal == ',')) {
- *writableVal = ' ';
- ++writableVal;
- }
- }
- }
-
while (valueIndex < crlfIndex && beginBuffer[valueIndex] == ' ')
++valueIndex;
nsDependentCSubstring value = Substring(beginBuffer + valueIndex,
beginBuffer + crlfIndex);
if (name.EqualsLiteral("content-length")) {
int64_t len;
--- a/netwerk/protocol/http/Http2Session.cpp
+++ b/netwerk/protocol/http/Http2Session.cpp
@@ -1148,20 +1148,22 @@ Http2Session::RecvHeaders(Http2Session *
return rv;
}
}
self->ResetDownstreamState();
return NS_OK;
}
+ // make sure this is either the first headers or a trailer
if (self->mInputFrameDataStream->AllHeadersReceived() &&
!(self->mInputFrameFlags & kFlag_END_STREAM)) {
// Any header block after the first that does *not* end the stream is
// illegal.
+ LOG3(("Http2Session::Illegal Extra HeaderBlock %p 0x%X\n", self, self->mInputFrameID));
RETURN_SESSION_ERROR(self, PROTOCOL_ERROR);
}
// queue up any compression bytes
self->mDecompressBuffer.Append(self->mInputFrameBuffer + kFrameHeaderBytes + paddingControlBytes + priorityLen,
self->mInputFrameDataSize - paddingControlBytes - priorityLen - paddingLength);
self->mInputFrameDataStream->UpdateTransportReadEvents(self->mInputFrameDataSize);
@@ -1187,17 +1189,17 @@ Http2Session::RecvHeaders(Http2Session *
// should be reset with a PROTOCOL_ERROR, NS_OK when the response headers were
// fine, and any other error is fatal to the session.
nsresult
Http2Session::ResponseHeadersComplete()
{
LOG3(("Http2Session::ResponseHeadersComplete %p for 0x%X fin=%d",
this, mInputFrameDataStream->StreamID(), mInputFrameFinal));
- // only interpret headers once, afterwards ignore trailers
+ // only interpret headers once, afterwards ignore as trailers
if (mInputFrameDataStream->AllHeadersReceived()) {
LOG3(("Http2Session::ResponseHeadersComplete extra headers"));
MOZ_ASSERT(mInputFrameFlags & kFlag_END_STREAM);
nsresult rv = UncompressAndDiscard();
if (NS_FAILED(rv)) {
LOG3(("Http2Session::ResponseHeadersComplete extra uncompress failed\n"));
return rv;
}
@@ -1207,42 +1209,53 @@ Http2Session::ResponseHeadersComplete()
// need to process the fin
ChangeDownstreamState(PROCESSING_COMPLETE_HEADERS);
} else {
ResetDownstreamState();
}
return NS_OK;
}
+
+ // if this turns out to be a 1xx response code we have to
+ // undo the headers received bit that we are setting here.
+ bool didFirstSetAllRecvd = !mInputFrameDataStream->AllHeadersReceived();
mInputFrameDataStream->SetAllHeadersReceived();
// The stream needs to see flattened http headers
// Uncompressed http/2 format headers currently live in
// Http2Stream::mDecompressBuffer - convert that to HTTP format in
// mFlatHTTPResponseHeaders via ConvertHeaders()
nsresult rv;
+ int32_t httpResponseCode; // out param to ConvertResponseHeaders
mFlatHTTPResponseHeadersOut = 0;
rv = mInputFrameDataStream->ConvertResponseHeaders(&mDecompressor,
mDecompressBuffer,
- mFlatHTTPResponseHeaders);
+ mFlatHTTPResponseHeaders,
+ httpResponseCode);
if (rv == NS_ERROR_ABORT) {
LOG(("Http2Session::ResponseHeadersComplete ConvertResponseHeaders aborted\n"));
if (mInputFrameDataStream->IsTunnel()) {
gHttpHandler->ConnMgr()->CancelTransactions(
mInputFrameDataStream->Transaction()->ConnectionInfo(),
NS_ERROR_CONNECTION_REFUSED);
}
CleanupStream(mInputFrameDataStream, rv, CANCEL_ERROR);
ResetDownstreamState();
return NS_OK;
} else if (NS_FAILED(rv)) {
return rv;
}
+ // allow more headers in the case of 1xx
+ if (((httpResponseCode / 100) == 1) && didFirstSetAllRecvd) {
+ mInputFrameDataStream->UnsetAllHeadersReceived();
+ }
+
ChangeDownstreamState(PROCESSING_COMPLETE_HEADERS);
return NS_OK;
}
nsresult
Http2Session::RecvPriority(Http2Session *self)
{
MOZ_ASSERT(self->mInputFrameType == FRAME_TYPE_PRIORITY);
--- a/netwerk/protocol/http/Http2Stream.cpp
+++ b/netwerk/protocol/http/Http2Stream.cpp
@@ -835,47 +835,53 @@ Http2Stream::GenerateDataFrameHeader(uin
mTxStreamFrameSize = dataLength;
}
// ConvertHeaders is used to convert the response headers
// into HTTP/1 format and report some telemetry
nsresult
Http2Stream::ConvertResponseHeaders(Http2Decompressor *decompressor,
nsACString &aHeadersIn,
- nsACString &aHeadersOut)
+ nsACString &aHeadersOut,
+ int32_t &httpResponseCode)
{
aHeadersOut.Truncate();
aHeadersOut.SetCapacity(aHeadersIn.Length() + 512);
nsresult rv =
decompressor->DecodeHeaderBlock(reinterpret_cast<const uint8_t *>(aHeadersIn.BeginReading()),
aHeadersIn.Length(),
aHeadersOut, false);
if (NS_FAILED(rv)) {
LOG3(("Http2Stream::ConvertHeaders %p decode Error\n", this));
return NS_ERROR_ILLEGAL_VALUE;
}
- nsAutoCString status;
- decompressor->GetStatus(status);
- if (status.IsEmpty()) {
+ nsAutoCString statusString;
+ decompressor->GetStatus(statusString);
+ if (statusString.IsEmpty()) {
LOG3(("Http2Stream::ConvertHeaders %p Error - no status\n", this));
return NS_ERROR_ILLEGAL_VALUE;
}
+ nsresult errcode;
+ httpResponseCode = statusString.ToInteger(&errcode);
if (mIsTunnel) {
- nsresult errcode;
-
- int32_t code = status.ToInteger(&errcode);
- LOG3(("Http2Stream %p Tunnel Response code %d", this, code));
- if ((code / 100) != 2) {
+ LOG3(("Http2Stream %p Tunnel Response code %d", this, httpResponseCode));
+ if ((httpResponseCode / 100) != 2) {
MapStreamToPlainText();
}
}
+ if (httpResponseCode == 101) {
+ // 8.1.1 of h2 disallows 101.. throw PROTOCOL_ERROR on stream
+ LOG3(("Http2Stream::ConvertHeaders %p Error - status == 101\n", this));
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+
if (aHeadersIn.Length() && aHeadersOut.Length()) {
Telemetry::Accumulate(Telemetry::SPDY_SYN_REPLY_SIZE, aHeadersIn.Length());
uint32_t ratio =
aHeadersIn.Length() * 100 / aHeadersOut.Length();
Telemetry::Accumulate(Telemetry::SPDY_SYN_REPLY_RATIO, ratio);
}
// The decoding went ok. Now we can customize and clean up.
--- a/netwerk/protocol/http/Http2Stream.h
+++ b/netwerk/protocol/http/Http2Stream.h
@@ -80,23 +80,25 @@ public:
void SetSentReset(bool aStatus);
bool SentReset() { return mSentReset; }
void SetCountAsActive(bool aStatus) { mCountAsActive = aStatus ? 1 : 0; }
bool CountAsActive() { return mCountAsActive; }
void SetAllHeadersReceived();
+ void UnsetAllHeadersReceived() { mAllHeadersReceived = 0; }
bool AllHeadersReceived() { return mAllHeadersReceived; }
void UpdateTransportSendEvents(uint32_t count);
void UpdateTransportReadEvents(uint32_t count);
// NS_ERROR_ABORT terminates stream, other failure terminates session
- nsresult ConvertResponseHeaders(Http2Decompressor *, nsACString &, nsACString &);
+ nsresult ConvertResponseHeaders(Http2Decompressor *, nsACString &,
+ nsACString &, int32_t &);
nsresult ConvertPushHeaders(Http2Decompressor *, nsACString &, nsACString &);
bool AllowFlowControlledWrite();
void UpdateServerReceiveWindow(int32_t delta);
int64_t ServerReceiveWindow() { return mServerReceiveWindow; }
void DecrementClientReceiveWindow(uint32_t delta) {
mClientReceiveWindow -= delta;
--- a/services/fxaccounts/FxAccounts.jsm
+++ b/services/fxaccounts/FxAccounts.jsm
@@ -141,17 +141,21 @@ AccountState.prototype = {
// check with param substitutions added in bug 966674
log.debug("getCertificate" + JSON.stringify(this.signedInUser));
}
// TODO: get the lifetime from the cert's .exp field
if (this.cert && this.cert.validUntil > mustBeValidUntil) {
log.debug(" getCertificate already had one");
return this.resolve(this.cert.cert);
}
- // else get our cert signed
+
+ if (Services.io.offline) {
+ return this.reject(new Error(ERROR_OFFLINE));
+ }
+
let willBeValidUntil = this.fxaInternal.now() + CERT_LIFETIME;
return this.fxaInternal.getCertificateSigned(data.sessionToken,
keyPair.serializedPublicKey,
CERT_LIFETIME).then(
cert => {
log.debug("getCertificate got a new one: " + !!cert);
this.cert = {
cert: cert,
--- a/services/fxaccounts/FxAccountsManager.jsm
+++ b/services/fxaccounts/FxAccountsManager.jsm
@@ -175,16 +175,17 @@ this.FxAccountsManager = {
* As of May 2014, the only HTTP call triggered by this._getAssertion()
* is to /certificate/sign via:
* FxAccounts.getAssertion()
* FxAccountsInternal.getCertificateSigned()
* FxAccountsClient.signCertificate()
* See the latter method for possible (error code, errno) pairs.
*/
_handleGetAssertionError: function(reason, aAudience, aPrincipal) {
+ log.debug("FxAccountsManager._handleGetAssertionError()");
let errno = (reason ? reason.errno : NaN) || NaN;
// If the previously valid email/password pair is no longer valid ...
if (errno == ERRNO_INVALID_AUTH_TOKEN) {
return this._fxAccounts.accountStatus().then(
(exists) => {
// ... if the email still maps to an account, the password
// must have changed, so ask the user to enter the new one ...
if (exists) {
@@ -301,16 +302,19 @@ this.FxAccountsManager = {
return this._serverError(reason);
}
);
}
);
},
_uiRequest: function(aRequest, aAudience, aPrincipal, aParams) {
+ if (Services.io.offline) {
+ return this._error(ERROR_OFFLINE);
+ }
let ui = Cc["@mozilla.org/fxaccounts/fxaccounts-ui-glue;1"]
.createInstance(Ci.nsIFxAccountsUIGlue);
if (!ui[aRequest]) {
return this._error(ERROR_UI_REQUEST);
}
if (!aParams || !Array.isArray(aParams)) {
aParams = [aParams];
@@ -382,38 +386,35 @@ this.FxAccountsManager = {
);
},
getAccount: function() {
// We check first if we have session details cached.
if (this._activeSession) {
// If our cache says that the account is not yet verified,
// we kick off verification before returning what we have.
- if (this._activeSession && !this._activeSession.verified &&
- !Services.io.offline) {
+ if (!this._activeSession.verified) {
this.verificationStatus(this._activeSession);
}
-
log.debug("Account " + JSON.stringify(this._user));
return Promise.resolve(this._user);
}
// If no cached information, we try to get it from the persistent storage.
return this._fxAccounts.getSignedInUser().then(
user => {
if (!user || !user.email) {
log.debug("No signed in account");
return Promise.resolve(null);
}
this._activeSession = user;
// If we get a stored information of a not yet verified account,
// we kick off verification before returning what we have.
- if (!user.verified && !Services.io.offline) {
- log.debug("Unverified account");
+ if (!user.verified) {
this.verificationStatus(user);
}
log.debug("Account " + JSON.stringify(this._user));
return Promise.resolve(this._user);
}
);
},
@@ -456,17 +457,18 @@ this.FxAccountsManager = {
// There is no way to unverify an already verified account, so we just
// return the account details of a verified account
if (this._activeSession.verified) {
log.debug("Account already verified");
return;
}
if (Services.io.offline) {
- this._error(ERROR_OFFLINE);
+ log.warn("Offline; skipping verification.");
+ return;
}
let client = this._getFxAccountsClient();
client.recoveryEmailStatus(this._activeSession.sessionToken).then(
data => {
let error = this._getError(data);
if (error) {
this._error(error, data);
@@ -502,23 +504,23 @@ this.FxAccountsManager = {
* refreshAuthentication - (bool) Force re-auth.
* silent - (bool) Prevent any UI interaction.
* I.e., try to get an automatic assertion.
*/
getAssertion: function(aAudience, aPrincipal, aOptions) {
if (!aAudience) {
return this._error(ERROR_INVALID_AUDIENCE);
}
- if (Services.io.offline) {
- return this._error(ERROR_OFFLINE);
- }
let secMan = Cc["@mozilla.org/scriptsecuritymanager;1"]
.getService(Ci.nsIScriptSecurityManager);
let uri = Services.io.newURI(aPrincipal.origin, null, null);
+ log.debug("FxAccountsManager.getAssertion() aPrincipal: ",
+ aPrincipal.origin, aPrincipal.appId,
+ aPrincipal.isInBrowserElement);
let principal = secMan.getAppCodebasePrincipal(uri,
aPrincipal.appId, aPrincipal.isInBrowserElement);
return this.getAccount().then(
user => {
if (user) {
// Three have-user cases to consider. First: are we unverified?
if (!user.verified) {
--- a/services/fxaccounts/tests/xpcshell/test_accounts.js
+++ b/services/fxaccounts/tests/xpcshell/test_accounts.js
@@ -179,16 +179,55 @@ add_task(function test_get_signed_in_use
let localOnly = true;
yield account.signOut(localOnly);
// user should be undefined after sign out
result = yield account.getSignedInUser();
do_check_eq(result, null);
});
+add_task(function test_getCertificate() {
+ _("getCertificate()");
+ // This test, unlike the rest, uses an un-mocked FxAccounts instance.
+ // However, we still need to pass an object to the constructor to
+ // force it to expose "internal".
+ let fxa = new FxAccounts({onlySetInternal: true})
+ let credentials = {
+ email: "foo@example.com",
+ uid: "1234@lcip.org",
+ assertion: "foobar",
+ sessionToken: "dead",
+ kA: "beef",
+ kB: "cafe",
+ verified: true
+ };
+ yield fxa.setSignedInUser(credentials);
+
+ // Test that an expired cert throws if we're offline.
+ fxa.internal.currentAccountState.cert = {
+ validUntil: Date.parse("Mon, 13 Jan 2000 21:45:06 GMT")
+ };
+ let offline = Services.io.offline;
+ Services.io.offline = true;
+ // This call would break from missing parameters ...
+ fxa.internal.currentAccountState.getCertificate().then(
+ result => {
+ Services.io.offline = offline;
+ do_throw("Unexpected success");
+ },
+ err => {
+ Services.io.offline = offline;
+ // ... so we have to check the error string.
+ do_check_eq(err, "Error: OFFLINE");
+ }
+ );
+ _("----- DONE ----\n");
+});
+
+
// Sanity-check that our mocked client is working correctly
add_test(function test_client_mock() {
do_test_pending();
let fxa = new MockFxAccounts();
let client = fxa.internal.fxAccountsClient;
do_check_eq(client._verified, false);
do_check_eq(typeof client.signIn, "function");
--- a/services/fxaccounts/tests/xpcshell/test_manager.js
+++ b/services/fxaccounts/tests/xpcshell/test_manager.js
@@ -11,16 +11,19 @@ Cu.import("resource://gre/modules/FxAcco
Cu.import("resource://gre/modules/Promise.jsm");
// === Mocks ===
// Globals representing server state
let passwordResetOnServer = false;
let deletedOnServer = false;
+// Global representing FxAccounts state
+let certExpired = false;
+
// Mock RP
let principal = {origin: 'app://settings.gaiamobile.org', appId: 27}
// Override FxAccountsUIGlue.
const kFxAccountsUIGlueUUID = "{8f6d5d87-41ed-4bb5-aa28-625de57564c5}";
const kFxAccountsUIGlueContractID =
"@mozilla.org/fxaccounts/fxaccounts-ui-glue;1";
@@ -122,16 +125,18 @@ FxAccountsManager._fxAccounts = {
getAssertion: function() {
if (!this._signedInUser) {
return null;
}
let deferred = Promise.defer();
if (passwordResetOnServer || deletedOnServer) {
deferred.reject({errno: ERRNO_INVALID_AUTH_TOKEN});
+ } else if (Services.io.offline && certExpired) {
+ deferred.reject(new Error(ERROR_OFFLINE));
} else {
deferred.resolve(this._assertion);
}
return deferred.promise;
},
getSignedInUser: function() {
this._getSignedInUserCalled = true;
@@ -361,16 +366,78 @@ add_test(function(test_getAssertion_acti
run_next_test();
},
error => {
do_throw("Unexpected error: " + error);
}
);
});
+add_test(function() {
+ // getAssertion() succeeds if offline with valid cert
+ do_print("= getAssertion active session, valid cert, offline");
+ FxAccountsManager._fxAccounts._signedInUser.verified = true;
+ FxAccountsManager._activeSession.verified = true;
+ Services.io.offline = true;
+ FxAccountsManager.getAssertion("audience", principal).then(
+ result => {
+ FxAccountsManager._fxAccounts._reset();
+ Services.io.offline = false;
+ run_next_test();
+ },
+ error => {
+ Services.io.offline = false;
+ do_throw("Unexpected error: " + error);
+ }
+ );
+});
+
+add_test(function() {
+ // getAssertion() rejects if offline and cert expired.
+ do_print("= getAssertion active session, expired cert, offline");
+ FxAccountsManager._fxAccounts._signedInUser.verified = true;
+ FxAccountsManager._activeSession.verified = true;
+ Services.io.offline = true;
+ certExpired = true;
+ FxAccountsManager.getAssertion("audience", principal).then(
+ result => {
+ Services.io.offline = false;
+ certExpired = false;
+ do_throw("Unexpected success");
+ },
+ error => {
+ FxAccountsManager._fxAccounts._reset();
+ Services.io.offline = false;
+ certExpired = false;
+ run_next_test();
+ }
+ );
+});
+
+add_test(function() {
+ // getAssertion() rejects if offline and UI needed.
+ do_print("= getAssertion active session, trigger UI, offline");
+ let user = FxAccountsManager._fxAccounts._signedInUser;
+ FxAccountsManager._fxAccounts._signedInUser = null;
+ Services.io.offline = true;
+ FxAccountsManager.getAssertion("audience", principal).then(
+ result => {
+ Services.io.offline = false;
+ do_throw("Unexpected success");
+ },
+ error => {
+ do_check_false(FxAccountsUIGlue._signInFlowCalled);
+ FxAccountsManager._fxAccounts._reset();
+ FxAccountsManager._fxAccounts._signedInUser = user;
+ Services.io.offline = false;
+ run_next_test();
+ }
+ );
+});
+
add_test(function(test_getAssertion_refreshAuth) {
do_print("= getAssertion refreshAuth =");
let gracePeriod = 1200;
FxAccountsUIGlue._activeSession = {
email: "user@domain.org",
verified: true,
sessionToken: "1234"
};
--- a/testing/marionette/mach_commands.py
+++ b/testing/marionette/mach_commands.py
@@ -1,14 +1,15 @@
# 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/.
from __future__ import unicode_literals
+import imp
import os
import sys
import argparse
from mozlog.structured import commandline
from mozbuild.base import (
MachCommandBase,
@@ -31,17 +32,27 @@ VARIANT=eng ./build.sh
'''
# A parser that will accept structured logging commandline arguments.
_parser = argparse.ArgumentParser()
commandline.add_logging_group(_parser)
def run_marionette(tests, b2g_path=None, emulator=None, testtype=None,
address=None, binary=None, topsrcdir=None, **kwargs):
- from marionette.runtests import (
+
+ # Import the harness directly and under a different name here to avoid
+ # "marionette" being importable from two locations when "testing/marionette/client"
+ # is on sys.path.
+ # See bug 1050511.
+ path = os.path.join(topsrcdir, 'testing/marionette/client/marionette/runtests.py')
+ with open(path, 'r') as fh:
+ imp.load_module('marionetteharness', fh, path,
+ ('.py', 'r', imp.PY_SOURCE))
+
+ from marionetteharness import (
MarionetteTestRunner,
BaseMarionetteOptions,
startTestRunner
)
parser = BaseMarionetteOptions()
commandline.add_logging_group(parser)
options, args = parser.parse_args()
--- a/testing/mozbase/mozlog/tests/test_structured.py
+++ b/testing/mozbase/mozlog/tests/test_structured.py
@@ -577,17 +577,17 @@ class TestCommandline(unittest.TestCase)
def loglines(self):
self.logfile.seek(0)
return [line.rstrip() for line in self.logfile.readlines()]
def test_setup_logging(self):
parser = argparse.ArgumentParser()
commandline.add_logging_group(parser)
args = parser.parse_args(["--log-raw=-"])
- logger = commandline.setup_logging("test", args, {})
+ logger = commandline.setup_logging("test_setup_logging", args, {})
self.assertEqual(len(logger.handlers), 1)
def test_setup_logging_optparse(self):
parser = optparse.OptionParser()
commandline.add_logging_group(parser)
args, _ = parser.parse_args(["--log-raw=-"])
logger = commandline.setup_logging("test_optparse", args, {})
self.assertEqual(len(logger.handlers), 1)
--- a/testing/mozbase/test.py
+++ b/testing/mozbase/test.py
@@ -12,38 +12,22 @@ by default https://github.com/mozilla/mo
import imp
import manifestparser
import mozinfo
import optparse
import os
import sys
import unittest
+from mozlog import structured
from moztest.results import TestResultCollection
-
+from moztest.adapters.unit import StructuredTestRunner
here = os.path.dirname(os.path.abspath(__file__))
-
-class TBPLTextTestResult(unittest.TextTestResult):
- """
- Format the failure outputs according to TBPL. See:
- https://wiki.mozilla.org/Sheriffing/Job_Visibility_Policy#6.29_Outputs_failures_in_a_TBPL-starrable_format
- """
-
- def addFailure(self, test, err):
- super(unittest.TextTestResult, self).addFailure(test, err)
- self.stream.writeln()
- self.stream.writeln('TEST-UNEXPECTED-FAIL | %s | %s' % (test.id(), err[1]))
-
- def addUnexpectedSuccess(self, test):
- super(unittest.TextTestResult, self).addUnexpectedSuccess(test)
- self.stream.writeln()
- self.stream.writeln('TEST-UNEXPECTED-PASS | %s | Unexpected pass' % test.id())
-
def unittests(path):
"""return the unittests in a .py file"""
path = os.path.abspath(path)
unittests = []
assert os.path.exists(path)
directory = os.path.dirname(path)
sys.path.insert(0, directory) # insert directory into path for top-level imports
@@ -62,17 +46,23 @@ def main(args=sys.argv[1:]):
usage = '%prog [options] manifest.ini <manifest.ini> <...>'
parser = optparse.OptionParser(usage=usage, description=__doc__)
parser.add_option('-b', "--binary",
dest="binary", help="Binary path",
metavar=None, default=None)
parser.add_option('--list', dest='list_tests',
action='store_true', default=False,
help="list paths of tests to be run")
+ structured.commandline.add_logging_group(parser)
options, args = parser.parse_args(args)
+ logger = structured.commandline.setup_logging("mozbase",
+ options,
+ {
+ "tbpl": sys.stdout
+ })
# read the manifest
if args:
manifests = args
else:
manifests = [os.path.join(here, 'test-manifest.ini')]
missing = []
for manifest in manifests:
@@ -84,30 +74,32 @@ def main(args=sys.argv[1:]):
if options.binary:
# A specified binary should override the environment variable
os.environ['BROWSER_PATH'] = options.binary
# gather the tests
tests = manifest.active_tests(disabled=False, **mozinfo.info)
tests = [test['path'] for test in tests]
+ logger.suite_start(tests)
+
if options.list_tests:
# print test paths
print '\n'.join(tests)
sys.exit(0)
# create unittests
unittestlist = []
for test in tests:
unittestlist.extend(unittests(test))
# run the tests
suite = unittest.TestSuite(unittestlist)
- runner = unittest.TextTestRunner(verbosity=2, # default=1 does not show success of unittests
- resultclass=TBPLTextTestResult)
+ runner = StructuredTestRunner(logger=logger)
unittest_results = runner.run(suite)
results = TestResultCollection.from_unittest_results(None, unittest_results)
+ logger.suite_end()
# exit according to results
sys.exit(1 if results.num_failures else 0)
if __name__ == '__main__':
main()
--- a/testing/profiles/prefs_general.js
+++ b/testing/profiles/prefs_general.js
@@ -72,18 +72,16 @@ user_pref("extensions.installDistroAddon
// XPI extensions are required for test harnesses to load
user_pref("extensions.defaultProviders.enabled", true);
user_pref("geo.wifi.uri", "http://%(server)s/tests/dom/tests/mochitest/geolocation/network_geolocation.sjs");
user_pref("geo.wifi.timeToWaitBeforeSending", 2000);
user_pref("geo.wifi.scan", false);
user_pref("geo.wifi.logging.enabled", true);
-user_pref("camino.warn_when_closing", false); // Camino-only, harmless to others
-
// Make url-classifier updates so rare that they won't affect tests
user_pref("urlclassifier.updateinterval", 172800);
// Point the url-classifier to the local testing server for fast failures
user_pref("browser.safebrowsing.gethashURL", "http://%(server)s/safebrowsing-dummy/gethash");
user_pref("browser.safebrowsing.updateURL", "http://%(server)s/safebrowsing-dummy/update");
user_pref("browser.safebrowsing.appRepURL", "http://%(server)s/safebrowsing-dummy/update");
user_pref("browser.trackingprotection.gethashURL", "http://%(server)s/safebrowsing-dummy/gethash");
user_pref("browser.trackingprotection.updateURL", "http://%(server)s/safebrowsing-dummy/update");
--- a/widget/cocoa/nsAppShell.mm
+++ b/widget/cocoa/nsAppShell.mm
@@ -161,17 +161,17 @@ nsAppShell::nsAppShell()
, mRunningEventLoop(false)
, mStarted(false)
, mTerminated(false)
, mSkippedNativeCallback(false)
, mNativeEventCallbackDepth(0)
, mNativeEventScheduledDepth(0)
{
// A Cocoa event loop is running here if (and only if) we've been embedded
- // by a Cocoa app (like Camino).
+ // by a Cocoa app.
mRunningCocoaEmbedded = [NSApp isRunning] ? true : false;
}
nsAppShell::~nsAppShell()
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
if (mCFRunLoop) {
@@ -227,18 +227,18 @@ RemoveScreenWakeLockListener()
// interrupt the main native run loop.
//
// public
nsresult
nsAppShell::Init()
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
- // No event loop is running yet (unless Camino is running, or another
- // embedding app that uses NSApplicationMain()).
+ // No event loop is running yet (unless an embedding app that uses
+ // NSApplicationMain() is running).
NSAutoreleasePool* localPool = [[NSAutoreleasePool alloc] init];
// mAutoreleasePools is used as a stack of NSAutoreleasePool objects created
// by |this|. CFArray is used instead of NSArray because NSArray wants to
// retain each object you add to it, and you can't retain an
// NSAutoreleasePool.
mAutoreleasePools = ::CFArrayCreateMutable(nullptr, 0, nullptr);
NS_ENSURE_STATE(mAutoreleasePools);
@@ -253,19 +253,18 @@ nsAppShell::Init()
nsAutoCString nibPath;
rv = nibFile->GetNativePath(nibPath);
NS_ENSURE_SUCCESS(rv, rv);
// This call initializes NSApplication unless:
// 1) we're using xre -- NSApp's already been initialized by
// MacApplicationDelegate.mm's EnsureUseCocoaDockAPI().
- // 2) Camino is running (or another embedding app that uses
- // NSApplicationMain()) -- NSApp's already been initialized and
- // its main run loop is already running.
+ // 2) an embedding app that uses NSApplicationMain() is running -- NSApp's
+ // already been initialized and its main run loop is already running.
[NSBundle loadNibFile:
[NSString stringWithUTF8String:(const char*)nibPath.get()]
externalNameTable:
[NSDictionary dictionaryWithObject:[GeckoNSApplication sharedApplication]
forKey:@"NSOwner"]
withZone:NSDefaultMallocZone()];
mDelegate = [[AppShellDelegate alloc] initWithAppShell:this];
@@ -292,17 +291,17 @@ nsAppShell::Init()
rv = nsBaseAppShell::Init();
#ifndef __LP64__
TextInputHandler::InstallPluginKeyEventsHandler();
#endif
if (!gAppShellMethodsSwizzled) {
// We should only replace the original terminate: method if we're not
- // running in a Cocoa embedder (like Camino). See bug 604901.
+ // running in a Cocoa embedder. See bug 604901.
if (!mRunningCocoaEmbedded) {
nsToolkit::SwizzleMethods([NSApplication class], @selector(terminate:),
@selector(nsAppShell_NSApplication_terminate:));
}
gAppShellMethodsSwizzled = true;
}
[localPool release];
@@ -613,18 +612,18 @@ nsAppShell::ProcessNextNativeEvent(bool
// Run
//
// Overrides the base class's Run() method to call [NSApp run] (which spins
// the native run loop until the application quits). Since (unlike the base
// class's Run() method) we don't process any Gecko events here, they need
// to be processed elsewhere (in NativeEventCallback(), called from
// ProcessGeckoEvents()).
//
-// Camino calls [NSApp run] on its own (via NSApplicationMain()), and so
-// doesn't call nsAppShell::Run().
+// Camino called [NSApp run] on its own (via NSApplicationMain()), and so
+// didn't call nsAppShell::Run().
//
// public
NS_IMETHODIMP
nsAppShell::Run(void)
{
NS_ASSERTION(!mStarted, "nsAppShell::Run() called multiple times");
if (mStarted || mTerminated)
return NS_OK;
--- a/widget/cocoa/nsCocoaWindow.mm
+++ b/widget/cocoa/nsCocoaWindow.mm
@@ -233,17 +233,17 @@ static void FitRectToVisibleAreaForScree
// some reason this works around bug 740923. Yes, it's a bodacious hack.
// But until we know more it will have to do.
if (aUsesNativeFullScreen && aRect.y == 0 && aRect.height == screenBounds.height) {
aRect.y = 22;
aRect.height -= 22;
}
}
-// Some applications like Camino use native popup windows
+// Some applications use native popup windows
// (native context menus, native tooltips)
static bool UseNativePopupWindows()
{
#ifdef MOZ_USE_NATIVE_POPUP_WINDOWS
return true;
#else
return false;
#endif /* MOZ_USE_NATIVE_POPUP_WINDOWS */
--- a/widget/cocoa/nsToolkit.mm
+++ b/widget/cocoa/nsToolkit.mm
@@ -200,17 +200,17 @@ static CGEventRef EventTapCallback(CGEve
void
nsToolkit::RegisterForAllProcessMouseEvents()
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
if (getenv("MOZ_DEBUG"))
return;
- // Don't do this for apps that (like Camino) use native context menus.
+ // Don't do this for apps that use native context menus.
#ifdef MOZ_USE_NATIVE_POPUP_WINDOWS
return;
#endif /* MOZ_USE_NATIVE_POPUP_WINDOWS */
if (!mEventTapRLS) {
// Using an event tap for mouseDown events (instead of installing a
// handler for them on the EventMonitor target) works around an Apple
// bug that causes OS menus (like the Clock menu) not to work properly
--- a/widget/cocoa/nsWindowMap.mm
+++ b/widget/cocoa/nsWindowMap.mm
@@ -163,17 +163,17 @@
[super dealloc];
NS_OBJC_END_TRY_ABORT_BLOCK;
}
// As best I can tell, if the notification's object has a corresponding
// top-level widget (an nsCocoaWindow object), it has a delegate (set in
// nsCocoaWindow::StandardCreate()) of class WindowDelegate, and otherwise
-// not (Camino doesn't use top-level widgets (nsCocoaWindow objects) --
+// not (Camino didn't use top-level widgets (nsCocoaWindow objects) --
// only child widgets (nsChildView objects)). (The notification is sent
// to windowBecameKey: or windowBecameMain: below.)
//
// For use with clients that (like Firefox) do use top-level widgets (and
// have NSWindow delegates of class WindowDelegate).
+ (void)activateInWindow:(NSWindow*)aWindow
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
--- a/widget/windows/nsWindow.cpp
+++ b/widget/windows/nsWindow.cpp
@@ -185,16 +185,22 @@
#endif
using namespace mozilla;
using namespace mozilla::dom;
using namespace mozilla::gfx;
using namespace mozilla::layers;
using namespace mozilla::widget;
+namespace mozilla {
+namespace widget {
+ extern int32_t IsTouchDeviceSupportPresent();
+}
+}
+
/**************************************************************
**************************************************************
**
** BLOCK: Variables
**
** nsWindow Class static initializations and global variables.
**
**************************************************************
@@ -3606,17 +3612,17 @@ nsWindow::UpdateThemeGeometries(const ns
if (layerManager) {
layerManager->SetRegionToClear(clearRegion);
}
}
uint32_t
nsWindow::GetMaxTouchPoints() const
{
- if (IsWin7OrLater()) {
+ if (IsWin7OrLater() && IsTouchDeviceSupportPresent()) {
return GetSystemMetrics(SM_MAXIMUMTOUCHES);
}
return 0;
}
/**************************************************************
**************************************************************
**