Merge inbound to m-c. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Sun, 12 Oct 2014 14:45:42 -0400
changeset 210026 199fb29c3467cbf06cd4e7d6ef5ea2323530d211
parent 210007 c0d968dc9a29611ea01d51bbbb7fb28f01c72e80 (current diff)
parent 210025 9532ec1b7e807b432c2a71d169ac54331ff9e06f (diff)
child 210031 4ffbb6be512d6acd0b9157492440156ecd241652
child 210076 58a7392bde36384eaaec30381de775e9ba6fe01e
child 210079 cc0cfa77ec229fa24aac067a9687ae59c35e428a
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersmerge
milestone35.0a1
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;
 }
 
 /**************************************************************
  **************************************************************
  **