merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Thu, 14 Jul 2016 11:45:54 +0200
changeset 344976 08f8a5aacd8308a73f6040fe522be7ba38497561
parent 344951 cd9da00ffcc3f37ae32b9401dfbeaa892e314ab3 (current diff)
parent 344975 77bd53db031e18f025a25b95d44bc18db26983bd (diff)
child 344979 29c9b171623423acdf3aa228be457c56eee42839
child 344997 1edac68f1ff9b83372459c90ec682d66c792fb48
child 345058 1983612a169bfc1720ef48b9371e65bd0ec9eef1
push id6389
push userraliiev@mozilla.com
push dateMon, 19 Sep 2016 13:38:22 +0000
treeherdermozilla-beta@01d67bfe6c81 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone50.0a1
first release with
nightly linux32
08f8a5aacd83 / 50.0a1 / 20160714030208 / files
nightly linux64
08f8a5aacd83 / 50.0a1 / 20160714030208 / files
nightly mac
08f8a5aacd83 / 50.0a1 / 20160714030208 / files
nightly win32
08f8a5aacd83 / 50.0a1 / 20160714030208 / files
nightly win64
08f8a5aacd83 / 50.0a1 / 20160714030208 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge mozilla-inbound to mozilla-central a=merge
--- a/chrome/nsChromeRegistryChrome.cpp
+++ b/chrome/nsChromeRegistryChrome.cpp
@@ -118,25 +118,27 @@ nsChromeRegistryChrome::Init()
   bool safeMode = false;
   nsCOMPtr<nsIXULRuntime> xulrun (do_GetService(XULAPPINFO_SERVICE_CONTRACTID));
   if (xulrun)
     xulrun->GetInSafeMode(&safeMode);
 
   nsCOMPtr<nsIPrefService> prefserv (do_GetService(NS_PREFSERVICE_CONTRACTID));
   nsCOMPtr<nsIPrefBranch> prefs;
 
-  if (safeMode)
-    prefserv->GetDefaultBranch(nullptr, getter_AddRefs(prefs));
-  else
-    prefs = do_QueryInterface(prefserv);
+  if (prefserv) {
+    if (safeMode) {
+      prefserv->GetDefaultBranch(nullptr, getter_AddRefs(prefs));
+    } else {
+      prefs = do_QueryInterface(prefserv);
+    }
+  }
 
   if (!prefs) {
     NS_WARNING("Could not get pref service!");
-  }
-  else {
+  } else {
     nsXPIDLCString provider;
     rv = prefs->GetCharPref(SELECTED_SKIN_PREF, getter_Copies(provider));
     if (NS_SUCCEEDED(rv))
       mSelectedSkin = provider;
 
     SelectLocaleFromPref(prefs);
 
     rv = prefs->AddObserver(MATCH_OS_LOCALE_PREF, this, true);
--- a/devtools/shared/css-lexer.js
+++ b/devtools/shared/css-lexer.js
@@ -1068,16 +1068,19 @@ Scanner.prototype = {
     aToken.mIdent.length = 0;
 
     let ch = this.Peek();
     // Do we have a string?
     if (ch == QUOTATION_MARK || ch == APOSTROPHE) {
       this.ScanString(aToken);
       if (aToken.mType == eCSSToken_Bad_String) {
         aToken.mType = eCSSToken_Bad_URL;
+        // Flag us as having been a Bad_String.
+        aToken.mInteger2 = 1;
+        this.ConsumeBadURLRemnants(aToken);
         return;
       }
     } else {
       // Otherwise, this is the start of a non-quoted url (which may be empty).
       aToken.mSymbol = 0;
       this.GatherText(IS_URL_CHAR, aToken.mIdent);
     }
 
@@ -1088,19 +1091,54 @@ Scanner.prototype = {
     if (ch < 0 || ch == RIGHT_PARENTHESIS) {
       this.Advance();
       aToken.mType = eCSSToken_URL;
       if (ch < 0) {
         this.AddEOFCharacters(eEOFCharacters_CloseParen);
       }
     } else {
       aToken.mType = eCSSToken_Bad_URL;
+      if (aToken.mSymbol != 0) {
+        // Flag us as having been a String, not a Bad_String.
+        aToken.mInteger2 = 0;
+      }
+      this.ConsumeBadURLRemnants(aToken);
     }
   },
 
+  ConsumeBadURLRemnants: function (aToken) {
+    aToken.mInteger = aToken.mIdent.length;
+    let ch = this.Peek();
+    do {
+      if (ch < 0) {
+        this.AddEOFCharacters(eEOFCharacters_CloseParen);
+        break;
+      }
+
+      if (ch == REVERSE_SOLIDUS && this.GatherEscape(aToken.mIdent, false)) {
+        // Nothing else needs to be done here for the moment; we've consumed the
+        // backslash and following escape.
+      } else {
+        // We always want to consume this character.
+        if (IsVertSpace(ch)) {
+          this.AdvanceLine();
+        } else {
+          this.Advance();
+        }
+        if (ch == 0) {
+          aToken.mIdent.push(UCS2_REPLACEMENT_CHAR);
+        } else {
+          aToken.mIdent.push(ch);
+        }
+      }
+
+      ch = this.Peek();
+    } while (ch != RIGHT_PARENTHESIS);
+  },
+
   /**
    * Primary scanner entry point.  Consume one token and fill in
    * |aToken| accordingly.  Will skip over any number of comments first,
    * and will also skip over rather than return whitespace and comment
    * tokens, depending on the value of |aSkip|.
    *
    * Returns true if it successfully consumed a token, false if EOF has
    * been reached.  Will always advance the current read position by at
--- a/devtools/shared/tests/unit/test_csslexer.js
+++ b/devtools/shared/tests/unit/test_csslexer.js
@@ -123,18 +123,19 @@ var LEX_TESTS = [
   ["23px", ["dimension:px"]],
   ["23%", ["percentage"]],
   ["url(http://example.com)", ["url:http://example.com"]],
   ["url('http://example.com')", ["url:http://example.com"]],
   ["url(  'http://example.com'  )",
              ["url:http://example.com"]],
   // In CSS Level 3, this is an ordinary URL, not a BAD_URL.
   ["url(http://example.com", ["url:http://example.com"]],
-  // See bug 1153981 to understand why this gets a SYMBOL token.
-  ["url(http://example.com @", ["bad_url:http://example.com", "symbol:@"]],
+  // We end up losing the whitespace before the '@' because it's skipped by the
+  // lexer before we discover we have a BAD_URL token.
+  ["url(http://example.com @", ["bad_url:http://example.com@"]],
   ["quo\\ting", ["ident:quoting"]],
   ["'bad string\n", ["bad_string:bad string", "whitespace"]],
   ["~=", ["includes"]],
   ["|=", ["dashmatch"]],
   ["^=", ["beginsmatch"]],
   ["$=", ["endsmatch"]],
   ["*=", ["containsmatch"]],
 
--- a/dom/canvas/WebGLContext.cpp
+++ b/dom/canvas/WebGLContext.cpp
@@ -1942,18 +1942,17 @@ WebGLContext::DidRefresh()
     if (gl) {
         gl->FlushIfHeavyGLCallsSinceLastFlush();
     }
 }
 
 bool
 WebGLContext::ValidateCurFBForRead(const char* funcName,
                                    const webgl::FormatUsageInfo** const out_format,
-                                   uint32_t* const out_width, uint32_t* const out_height,
-                                   GLenum* const out_mode)
+                                   uint32_t* const out_width, uint32_t* const out_height)
 {
     if (!mBoundReadFramebuffer) {
         const GLenum readBufferMode = gl->Screen()->GetReadBufferMode();
         if (readBufferMode == LOCAL_GL_NONE) {
             ErrorInvalidOperation("%s: Can't read from backbuffer when readBuffer mode is"
                                   " NONE.",
                                   funcName);
             return false;
@@ -1967,22 +1966,21 @@ WebGLContext::ValidateCurFBForRead(const
         auto effFormat = mOptions.alpha ? webgl::EffectiveFormat::RGBA8
                                         : webgl::EffectiveFormat::RGB8;
 
         *out_format = mFormatUsage->GetUsage(effFormat);
         MOZ_ASSERT(*out_format);
 
         *out_width = mWidth;
         *out_height = mHeight;
-        *out_mode = gl->Screen()->GetReadBufferMode();
         return true;
     }
 
     return mBoundReadFramebuffer->ValidateForRead(funcName, out_format, out_width,
-                                                  out_height, out_mode);
+                                                  out_height);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 
 WebGLContext::ScopedMaskWorkaround::ScopedMaskWorkaround(WebGLContext& webgl)
     : mWebGL(webgl)
     , mFakeNoAlpha(ShouldFakeNoAlpha(webgl))
     , mFakeNoDepth(ShouldFakeNoDepth(webgl))
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -1310,18 +1310,17 @@ protected:
                                       WebGLTexDimensions dims);
 
     bool ValidateUniformLocationForProgram(WebGLUniformLocation* location,
                                            WebGLProgram* program,
                                            const char* funcName);
 
     bool ValidateCurFBForRead(const char* funcName,
                               const webgl::FormatUsageInfo** const out_format,
-                              uint32_t* const out_width, uint32_t* const out_height,
-                              GLenum* const out_mode);
+                              uint32_t* const out_width, uint32_t* const out_height);
 
     void Invalidate();
     void DestroyResourcesAndContext();
 
     void MakeContextCurrent() const;
 
     // helpers
 
--- a/dom/canvas/WebGLContextGL.cpp
+++ b/dom/canvas/WebGLContextGL.cpp
@@ -1593,18 +1593,17 @@ WebGLContext::ReadPixels(GLint x, GLint 
 
     //////
 
     MakeContextCurrent();
 
     const webgl::FormatUsageInfo* srcFormat;
     uint32_t srcWidth;
     uint32_t srcHeight;
-    GLenum srcMode;
-    if (!ValidateCurFBForRead("readPixels", &srcFormat, &srcWidth, &srcHeight, &srcMode))
+    if (!ValidateCurFBForRead("readPixels", &srcFormat, &srcWidth, &srcHeight))
         return;
 
     // Check the format and type params to assure they are an acceptable pair (as per spec)
     auto srcType = srcFormat->format->componentType;
     GLenum mainReadFormat;
     GLenum mainReadType;
     switch (srcType) {
         case webgl::ComponentType::Float:
--- a/dom/canvas/WebGLContextState.cpp
+++ b/dom/canvas/WebGLContextState.cpp
@@ -377,34 +377,32 @@ WebGLContext::GetParameter(JSContext* cx
         case LOCAL_GL_GENERATE_MIPMAP_HINT: {
             GLint i = 0;
             gl->fGetIntegerv(pname, &i);
             return JS::NumberValue(uint32_t(i));
         }
         case LOCAL_GL_IMPLEMENTATION_COLOR_READ_TYPE: {
             const webgl::FormatUsageInfo* usage;
             uint32_t width, height;
-            GLenum mode;
-            if (!ValidateCurFBForRead(funcName, &usage, &width, &height, &mode))
+            if (!ValidateCurFBForRead(funcName, &usage, &width, &height))
                 return JS::NullValue();
 
             GLint i = 0;
             if (gl->IsSupported(gl::GLFeature::ES2_compatibility)) {
                 gl->fGetIntegerv(pname, &i);
             } else {
                 i = LOCAL_GL_UNSIGNED_BYTE;
             }
 
             return JS::NumberValue(uint32_t(i));
         }
         case LOCAL_GL_IMPLEMENTATION_COLOR_READ_FORMAT: {
             const webgl::FormatUsageInfo* usage;
             uint32_t width, height;
-            GLenum mode;
-            if (!ValidateCurFBForRead(funcName, &usage, &width, &height, &mode))
+            if (!ValidateCurFBForRead(funcName, &usage, &width, &height))
                 return JS::NullValue();
 
             GLint i = 0;
             if (gl->IsSupported(gl::GLFeature::ES2_compatibility)) {
                 gl->fGetIntegerv(pname, &i);
             } else {
                 i = LOCAL_GL_RGBA;
             }
--- a/dom/canvas/WebGLFramebuffer.cpp
+++ b/dom/canvas/WebGLFramebuffer.cpp
@@ -1197,36 +1197,34 @@ WebGLFramebuffer::FinalizeAttachments() 
     }
 
     FinalizeDrawAndReadBuffers(gl, mColorAttachment0.IsDefined());
 }
 
 bool
 WebGLFramebuffer::ValidateForRead(const char* funcName,
                                   const webgl::FormatUsageInfo** const out_format,
-                                  uint32_t* const out_width, uint32_t* const out_height,
-                                  GLenum* const out_mode)
+                                  uint32_t* const out_width, uint32_t* const out_height)
 {
     if (!ValidateAndInitAttachments(funcName))
         return false;
 
     if (mReadBufferMode == LOCAL_GL_NONE) {
-        mContext->ErrorInvalidOperation("%s: Read buffer mode must not be"
-                                        " NONE.", funcName);
+        mContext->ErrorInvalidOperation("%s: Read buffer mode must not be NONE.",
+                                        funcName);
         return false;
     }
 
     const auto attachPoint = GetAttachPoint(mReadBufferMode);
     if (!attachPoint || !attachPoint->IsDefined()) {
         mContext->ErrorInvalidOperation("%s: The attachment specified for reading is"
                                         " null.", funcName);
         return false;
     }
 
-    *out_mode = mReadBufferMode;
     *out_format = attachPoint->Format();
     attachPoint->Size(out_width, out_height);
     return true;
 }
 
 static bool
 AttachmentsDontMatch(const WebGLFBAttachPoint& a, const WebGLFBAttachPoint& b)
 {
--- a/dom/canvas/WebGLFramebuffer.h
+++ b/dom/canvas/WebGLFramebuffer.h
@@ -281,18 +281,17 @@ public:
     bool ValidateAndInitAttachments(const char* funcName);
 
     void InvalidateFramebufferStatus() const {
         mIsKnownFBComplete = false;
     }
 
     bool ValidateForRead(const char* info,
                          const webgl::FormatUsageInfo** const out_format,
-                         uint32_t* const out_width, uint32_t* const out_height,
-                         GLenum* const out_mode);
+                         uint32_t* const out_width, uint32_t* const out_height);
 
     JS::Value GetAttachmentParameter(const char* funcName, JSContext* cx, GLenum target,
                                      GLenum attachment, GLenum pname,
                                      ErrorResult* const out_error);
 };
 
 } // namespace mozilla
 
--- a/dom/canvas/WebGLTexture.h
+++ b/dom/canvas/WebGLTexture.h
@@ -247,16 +247,17 @@ protected:
                                        GLint level, uint32_t width, uint32_t height,
                                        uint32_t depth,
                                        WebGLTexture::ImageInfo** const out_imageInfo);
     bool ValidateTexImageSelection(const char* funcName, TexImageTarget target,
                                    GLint level, GLint xOffset, GLint yOffset,
                                    GLint zOffset, uint32_t width, uint32_t height,
                                    uint32_t depth,
                                    WebGLTexture::ImageInfo** const out_imageInfo);
+    bool ValidateCopyTexImageForFeedback(const char* funcName, uint32_t level) const;
 
     bool ValidateUnpack(const char* funcName, const webgl::TexUnpackBlob* blob,
                         bool isFunc3D, const webgl::PackingInfo& srcPI) const;
 public:
     void TexStorage(const char* funcName, TexTarget target, GLsizei levels,
                     GLenum sizedFormat, GLsizei width, GLsizei height, GLsizei depth);
 protected:
     void TexImage(const char* funcName, TexImageTarget target, GLint level,
--- a/dom/canvas/WebGLTextureUpload.cpp
+++ b/dom/canvas/WebGLTextureUpload.cpp
@@ -1908,23 +1908,48 @@ ValidateCopyDestUsage(const char* funcNa
                                      " dest: %s)",
                                      funcName, srcFormat->name, dstFormat->name);
         return nullptr;
     }
 
     return dstUsage;
 }
 
+bool
+WebGLTexture::ValidateCopyTexImageForFeedback(const char* funcName, uint32_t level) const
+{
+    const auto& fb = mContext->mBoundReadFramebuffer;
+    if (fb) {
+        const auto readBuffer = fb->ReadBufferMode();
+        MOZ_ASSERT(readBuffer != LOCAL_GL_NONE);
+        const uint32_t colorAttachment = readBuffer - LOCAL_GL_COLOR_ATTACHMENT0;
+        const auto& attach = fb->ColorAttachment(colorAttachment);
+
+        if (attach.Texture() == this &&
+            uint32_t(attach.MipLevel()) == level)
+        {
+            // Note that the TexImageTargets *don't* have to match for this to be
+            // undefined per GLES 3.0.4 p211, thus an INVALID_OP in WebGL.
+            mContext->ErrorInvalidOperation("%s: Feedback loop detected, as this texture"
+                                            " is already attached to READ_FRAMEBUFFER's"
+                                            " READ_BUFFER-selected COLOR_ATTACHMENT%u.",
+                                            funcName, colorAttachment);
+            return false;
+        }
+    }
+    return true;
+}
+
 // There is no CopyTexImage3D.
 void
 WebGLTexture::CopyTexImage2D(TexImageTarget target, GLint level, GLenum internalFormat,
                              GLint x, GLint y, GLsizei rawWidth, GLsizei rawHeight,
                              GLint border)
 {
-    const char funcName[] = "CopyTexImage2D";
+    const char funcName[] = "copyTexImage2D";
 
     ////////////////////////////////////
     // Get dest info
 
     uint32_t width, height, depth;
     if (!ValidateExtents(mContext, funcName, rawWidth, rawHeight, 1, border, &width,
                          &height, &depth))
     {
@@ -1940,30 +1965,22 @@ WebGLTexture::CopyTexImage2D(TexImageTar
     MOZ_ASSERT(imageInfo);
 
     ////////////////////////////////////
     // Get source info
 
     const webgl::FormatUsageInfo* srcUsage;
     uint32_t srcWidth;
     uint32_t srcHeight;
-    GLenum srcMode;
-    if (!mContext->ValidateCurFBForRead(funcName, &srcUsage, &srcWidth, &srcHeight,
-                                        &srcMode))
+    if (!mContext->ValidateCurFBForRead(funcName, &srcUsage, &srcWidth, &srcHeight))
         return;
     auto srcFormat = srcUsage->format;
 
-    // GLES 3.0.4 p145:
-    // "Calling CopyTexSubImage3D, CopyTexImage2D, or CopyTexSubImage2D will result in an
-    //  INVALID_OPERATION error if any of the following conditions is true: READ_BUFFER
-    //  is NONE"
-    if (srcMode == LOCAL_GL_NONE) {
-        mContext->ErrorInvalidOperation("%s: READ_BUFFER is NONE. ", funcName);
+    if (!ValidateCopyTexImageForFeedback(funcName, level))
         return;
-    }
 
     ////////////////////////////////////
     // Check that source and dest info are compatible
 
     const auto dstUsage = ValidateCopyDestUsage(funcName, mContext, srcFormat,
                                                 internalFormat);
     if (!dstUsage)
         return;
@@ -2081,30 +2098,22 @@ WebGLTexture::CopyTexSubImage(const char
     }
 
     ////////////////////////////////////
     // Get source info
 
     const webgl::FormatUsageInfo* srcUsage;
     uint32_t srcWidth;
     uint32_t srcHeight;
-    GLenum srcMode;
-    if (!mContext->ValidateCurFBForRead(funcName, &srcUsage, &srcWidth, &srcHeight,
-                                        &srcMode))
+    if (!mContext->ValidateCurFBForRead(funcName, &srcUsage, &srcWidth, &srcHeight))
         return;
     auto srcFormat = srcUsage->format;
 
-    // GLES 3.0.4 p145:
-    // "Calling CopyTexSubImage3D, CopyTexImage2D, or CopyTexSubImage2D will result in an
-    //  INVALID_OPERATION error if any of the following conditions is true: READ_BUFFER
-    //  is NONE"
-    if (srcMode == LOCAL_GL_NONE) {
-        mContext->ErrorInvalidOperation("%s: READ_BUFFER is NONE. ", funcName);
+    if (!ValidateCopyTexImageForFeedback(funcName, level))
         return;
-    }
 
     ////////////////////////////////////
     // Check that source and dest info are compatible
 
     if (!ValidateCopyTexImageFormats(mContext, funcName, srcFormat, dstFormat))
         return;
 
     ////////////////////////////////////
--- a/dom/canvas/test/webgl-conf/generated-mochitest.ini
+++ b/dom/canvas/test/webgl-conf/generated-mochitest.ini
@@ -3132,17 +3132,16 @@ skip-if = (os == 'android') || (os == 'l
 [generated/test_conformance__textures__tex-sub-image-2d.html]
 [generated/test_conformance__textures__texparameter-test.html]
 [generated/test_conformance__textures__texture-active-bind-2.html]
 [generated/test_conformance__textures__texture-active-bind.html]
 [generated/test_conformance__textures__texture-attachment-formats.html]
 [generated/test_conformance__textures__texture-clear.html]
 [generated/test_conformance__textures__texture-complete.html]
 [generated/test_conformance__textures__texture-copying-feedback-loops.html]
-fail-if = 1
 [generated/test_conformance__textures__texture-draw-with-2d-and-cube.html]
 [generated/test_conformance__textures__texture-fakeblack.html]
 [generated/test_conformance__textures__texture-formats-test.html]
 [generated/test_conformance__textures__texture-hd-dpi.html]
 [generated/test_conformance__textures__texture-mips.html]
 skip-if = (os == 'android' && debug)
 fail-if = (os == 'android')
 [generated/test_conformance__textures__texture-npot-video.html]
--- a/dom/canvas/test/webgl-conf/mochitest-errata.ini
+++ b/dom/canvas/test/webgl-conf/mochitest-errata.ini
@@ -34,18 +34,16 @@ skip-if = os == 'b2g' || ((os == 'linux'
 fail-if = 1
 [generated/test_conformance__context__context-lost.html]
 fail-if = 1
 [generated/test_conformance__glsl__misc__shaders-with-invariance.html]
 fail-if = 1
 [generated/test_conformance__glsl__misc__shaders-with-name-conflicts.html]
 [generated/test_conformance__renderbuffers__feedback-loop.html]
 fail-if = 1
-[generated/test_conformance__textures__texture-copying-feedback-loops.html]
-fail-if = 1
 
 ####################
 # Tests requesting non-local network connections.
 
 [generated/test_conformance__more__functions__readPixelsBadArgs.html]
 # (TODO) FATAL ERROR: Non-local network connections are disabled and a connection attempt to www.opengl.org (45.55.206.190) was made.
 skip-if = 1
 
--- a/dom/media/tests/mochitest/mochitest.ini
+++ b/dom/media/tests/mochitest/mochitest.ini
@@ -161,16 +161,18 @@ skip-if = toolkit == 'gonk' # B2G emulat
 skip-if = toolkit == 'gonk' # B2G emulator is too slow to handle a two-way audio call reliably
 [test_peerConnection_offerRequiresReceiveAudio.html]
 [test_peerConnection_offerRequiresReceiveVideo.html]
 skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
 [test_peerConnection_offerRequiresReceiveVideoAudio.html]
 skip-if = toolkit == 'gonk' || buildapp == 'mulet' || (android_version == '18' && debug) # b2g(Bug 960442, video support for WebRTC is disabled on b2g), android(Bug 1189784, timeouts on 4.3 emulator)
 [test_peerConnection_promiseSendOnly.html]
 skip-if = toolkit == 'gonk' || buildapp == 'mulet' || (android_version == '18' && debug) # b2g(Bug 960442, video support for WebRTC is disabled on b2g), android(Bug 1189784, timeouts on 4.3 emulator)
+[test_peerConnection_renderAfterRenegotiation.html]
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' || (android_version == '18' && debug) # b2g(Bug 960442, video support for WebRTC is disabled on b2g), android(Bug 1189784, timeouts on 4.3 emulator)
 [test_peerConnection_restartIce.html]
 skip-if = toolkit == 'gonk' || buildapp == 'mulet' || android_version # b2g (Bug 1059867)
 [test_peerConnection_restartIceNoBundle.html]
 skip-if = toolkit == 'gonk' || buildapp == 'mulet' || android_version # b2g (Bug 1059867)
 [test_peerConnection_restartIceNoBundleNoRtcpMux.html]
 skip-if = toolkit == 'gonk' || buildapp == 'mulet' || android_version # b2g (Bug 1059867)
 [test_peerConnection_restartIceNoRtcpMux.html]
 skip-if = toolkit == 'gonk' || buildapp == 'mulet' || android_version # b2g (Bug 1059867)
copy from dom/media/tests/mochitest/test_peerConnection_promiseSendOnly.html
copy to dom/media/tests/mochitest/test_peerConnection_renderAfterRenegotiation.html
--- a/dom/media/tests/mochitest/test_peerConnection_promiseSendOnly.html
+++ b/dom/media/tests/mochitest/test_peerConnection_renderAfterRenegotiation.html
@@ -1,63 +1,85 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <script type="application/javascript" src="pc.js"></script>
+  <script type="application/javascript" src="/tests/dom/canvas/test/captureStream_common.js"></script>
 </head>
 <body>
 <pre id="test">
 <script type="application/javascript;version=1.8">
   createHTML({
-    bug: "1091898",
-    title: "PeerConnection with promises (sendonly)",
+    bug: "1273652",
+    title: "Video receiver still renders after renegotiation",
     visible: true
   });
 
   var pc1 = new RTCPeerConnection();
   var pc2 = new RTCPeerConnection();
 
   var add = (pc, can, failed) => can && pc.addIceCandidate(can).catch(failed);
   pc1.onicecandidate = e => add(pc2, e.candidate, generateErrorCallback());
   pc2.onicecandidate = e => add(pc1, e.candidate, generateErrorCallback());
 
   var v1, v2;
   var delivered = new Promise(resolve => pc2.ontrack = e => {
     // Test RTCTrackEvent here.
     ok(e.streams.length > 0, "has streams");
     ok(e.streams[0].getTrackById(e.track.id), "has track");
     ok(pc2.getReceivers().some(receiver => receiver == e.receiver), "has receiver");
-    if (e.streams[0].getTracks().length == 2) {
+    if (e.streams[0].getTracks().length == 1) {
       // Test RTCTrackEvent required args here.
       mustThrowWith("RTCTrackEvent wo/required args",
                     "TypeError", () => new RTCTrackEvent("track", {}));
       v2.srcObject = e.streams[0];
       resolve();
     }
   });
 
   runNetworkTest(function() {
-    v1 = createMediaElement('video', 'v1');
+    var h = new CaptureStreamTestHelper2D();
+    var canvas = document.createElement('canvas');
+    canvas.id = 'source_canvas';
+    canvas.width = canvas.height = 10;
+    document.getElementById('content').appendChild(canvas);
+
     v2 = createMediaElement('video', 'v2');
-    var canPlayThrough = new Promise(resolve => v2.canplaythrough = e => resolve());
-
     is(v2.currentTime, 0, "v2.currentTime is zero at outset");
 
-    navigator.mediaDevices.getUserMedia({ video: true, audio: true })
-    .then(stream => (v1.srcObject = stream).getTracks().forEach(t => pc1.addTrack(t, stream)))
-    .then(() => pc1.createOffer({})) // check that createOffer accepts arg.
+    h.drawColor(canvas, h.blue);
+    var stream = canvas.captureStream(0);
+    stream.getTracks().forEach(t => pc1.addTrack(t, stream));
+
+    pc1.createOffer({})
     .then(offer => pc1.setLocalDescription(offer))
     .then(() => pc2.setRemoteDescription(pc1.localDescription))
     .then(() => pc2.createAnswer({}))  // check that createAnswer accepts arg.
     .then(answer => pc2.setLocalDescription(answer))
     .then(() => pc1.setRemoteDescription(pc2.localDescription))
+
+    // re-negotiate to trigger the race condition in the jitter buffer
+    .then(() => pc1.createOffer({})) // check that createOffer accepts arg.
+    .then(offer => pc1.setLocalDescription(offer))
+    .then(() => pc2.setRemoteDescription(pc1.localDescription))
+    .then(() => pc2.createAnswer({}))
+    .then(answer => pc2.setLocalDescription(answer))
+    .then(() => pc1.setRemoteDescription(pc2.localDescription))
     .then(() => delivered)
-//    .then(() => canPlayThrough)    // why doesn't this fire?
-    .then(() => waitUntil(() => v2.currentTime > 0 && v2.srcObject.currentTime > 0))
-    .then(() => ok(v2.currentTime > 0, "v2.currentTime is moving (" + v2.currentTime + ")"))
-    .then(() => ok(true, "Connected."))
+
+    // now verify that actually something gets rendered into the remote video
+    // element
+    .then(() => h.waitForPixelColor(v2, h.blue, 128,
+                                    "pcRemote's video should become green"))
+    .then(() => {
+      stream.requestFrame();
+      h.drawColor(canvas, h.red);
+      })
+    .then(() => h.waitForPixelColor(v2, h.red, 128,
+                                    "pcRemote's video should become green"))
+
     .catch(reason => ok(false, "unexpected failure: " + reason))
     .then(networkTestFinished);
   });
 </script>
 </pre>
 </body>
 </html>
--- a/dom/webidl/CSSLexer.webidl
+++ b/dom/webidl/CSSLexer.webidl
@@ -31,18 +31,20 @@ enum CSSTokenType {
   // A string.
   "string",
   // A "bad string".  This can only be returned when a string is
   // unterminated at EOF.  (However, currently the lexer returns
   // ordinary STRING tokens in this situation.)
   "bad_string",
   // A URL.  |text| holds the URL.
   "url",
-  // A "bad URL".  This is a URL that is unterminated at EOF.  |text|
-  // holds the URL.
+  // A "bad URL".  This is a URL that either contains a bad_string or contains
+  // garbage after the string or unquoted URL test.  |text| holds the URL and
+  // potentially whatever garbage came after it, up to but not including the
+  // following ')'.
   "bad_url",
   // A "symbol" is any one-character symbol.  This corresponds to the
   // DELIM token in the CSS specification.
   "symbol",
   // The "~=" token.
   "includes",
   // The "|=" token.
   "dashmatch",
--- a/ipc/glue/MessageChannel.cpp
+++ b/ipc/glue/MessageChannel.cpp
@@ -2068,20 +2068,23 @@ MessageChannel::NotifyMaybeChannelError(
     if (ChannelClosing == mChannelState) {
         // the channel closed, but we received a "Goodbye" message warning us
         // about it. no worries
         mChannelState = ChannelClosed;
         NotifyChannelClosed();
         return;
     }
 
+    Clear();
+
     // Oops, error!  Let the listener know about it.
     mChannelState = ChannelError;
+
+    // After this, the channel may be deleted.
     mListener->OnChannelError();
-    Clear();
 }
 
 void
 MessageChannel::OnNotifyMaybeChannelError()
 {
     AssertWorkerThread();
     mMonitor->AssertNotCurrentThreadOwns();
 
@@ -2223,21 +2226,22 @@ MessageChannel::Close()
 void
 MessageChannel::NotifyChannelClosed()
 {
     mMonitor->AssertNotCurrentThreadOwns();
 
     if (ChannelClosed != mChannelState)
         NS_RUNTIMEABORT("channel should have been closed!");
 
+    Clear();
+
     // OK, the IO thread just closed the channel normally.  Let the
-    // listener know about it.
+    // listener know about it. After this point the channel may be
+    // deleted.
     mListener->OnChannelClose();
-
-    Clear();
 }
 
 void
 MessageChannel::DebugAbort(const char* file, int line, const char* cond,
                            const char* why,
                            bool reply)
 {
     printf_stderr("###!!! [MessageChannel][%s][%s:%d] "
--- a/ipc/ipdl/ipdl/lower.py
+++ b/ipc/ipdl/ipdl/lower.py
@@ -3238,16 +3238,17 @@ class _GenerateProtocolActorCode(ipdl.as
             makeHandlerMethod('OnCallReceived', self.interruptSwitch,
                               hasReply=1, dispatches=dispatches),
             Whitespace.NL
         ])
 
         destroysubtreevar = ExprVar('DestroySubtree')
         deallocsubtreevar = ExprVar('DeallocSubtree')
         deallocshmemvar = ExprVar('DeallocShmems')
+        deallocselfvar = ExprVar('Dealloc' + _actorName(ptype.name(), self.side))
 
         # OnProcesingError(code)
         codevar = ExprVar('aCode')
         reasonvar = ExprVar('aReason')
         onprocessingerror = MethodDefn(
             MethodDecl('OnProcessingError',
                        params=[ Param(_Result.Type(), codevar.name),
                                 Param(Type('char', const=1, ptr=1), reasonvar.name) ]))
@@ -3316,31 +3317,33 @@ class _GenerateProtocolActorCode(ipdl.as
 
         # OnChannelClose()
         onclose = MethodDefn(MethodDecl('OnChannelClose'))
         if ptype.isToplevel():
             onclose.addstmts([
                 StmtExpr(ExprCall(destroysubtreevar,
                                   args=[ _DestroyReason.NormalShutdown ])),
                 StmtExpr(ExprCall(deallocsubtreevar)),
-                StmtExpr(ExprCall(deallocshmemvar))
+                StmtExpr(ExprCall(deallocshmemvar)),
+                StmtExpr(ExprCall(deallocselfvar))
             ])
         else:
             onclose.addstmt(
                 _fatalError("`OnClose' called on non-toplevel actor"))
         self.cls.addstmts([ onclose, Whitespace.NL ])
 
         # OnChannelError()
         onerror = MethodDefn(MethodDecl('OnChannelError'))
         if ptype.isToplevel():
             onerror.addstmts([
                 StmtExpr(ExprCall(destroysubtreevar,
                                   args=[ _DestroyReason.AbnormalShutdown ])),
                 StmtExpr(ExprCall(deallocsubtreevar)),
-                StmtExpr(ExprCall(deallocshmemvar))
+                StmtExpr(ExprCall(deallocshmemvar)),
+                StmtExpr(ExprCall(deallocselfvar))
             ])
         else:
             onerror.addstmt(
                 _fatalError("`OnError' called on non-toplevel actor"))
         self.cls.addstmts([ onerror, Whitespace.NL ])
 
         # OnChannelConnected()
         onconnected = MethodDefn(MethodDecl('OnChannelConnected',
@@ -3552,16 +3555,19 @@ class _GenerateProtocolActorCode(ipdl.as
             foreachdealloc.addstmt(StmtExpr(_shmemDealloc(shmem)))
 
             deallocshmem.addstmts([
                 foreachdealloc,
                 StmtExpr(ExprCall(ExprSelect(p.shmemMapVar(), '.', 'Clear')))
             ])
             self.cls.addstmts([ deallocshmem, Whitespace.NL ])
 
+            deallocself = MethodDefn(MethodDecl(deallocselfvar.name, virtual=1))
+            self.cls.addstmts([ deallocself, Whitespace.NL ])
+
         self.implementPickling()
 
         ## private members
         self.cls.addstmt(StmtDecl(Decl(p.channelType(), 'mChannel')))
         if ptype.isToplevel():
             self.cls.addstmts([
                 StmtDecl(Decl(Type('IDMap', T=Type('ProtocolBase')),
                               p.actorMapVar().name)),
--- a/js/src/asmjs/AsmJS.cpp
+++ b/js/src/asmjs/AsmJS.cpp
@@ -2051,25 +2051,25 @@ class MOZ_STACK_CLASS ModuleValidator
         if (maybeField)
             fieldChars = StringToNewUTF8CharsZ(cx_, *maybeField);
         else
             fieldChars = DuplicateString("");
         if (!fieldChars)
             return false;
 
         // Declare which function is exported which gives us an index into the
-        // module ExportVector.
-        uint32_t exportIndex;
-        if (!mg_.declareExport(Move(fieldChars), func.index(), &exportIndex))
+        // module FuncExportVector.
+        uint32_t funcExportIndex;
+        if (!mg_.declareFuncExport(Move(fieldChars), func.index(), &funcExportIndex))
             return false;
 
         // The exported function might have already been exported in which case
         // the index will refer into the range of AsmJSExports.
-        MOZ_ASSERT(exportIndex <= asmJSMetadata_->asmJSExports.length());
-        return exportIndex < asmJSMetadata_->asmJSExports.length() ||
+        MOZ_ASSERT(funcExportIndex <= asmJSMetadata_->asmJSExports.length());
+        return funcExportIndex < asmJSMetadata_->asmJSExports.length() ||
                asmJSMetadata_->asmJSExports.emplaceBack(func.srcBegin() - asmJSMetadata_->srcStart,
                                                         func.srcEnd() - asmJSMetadata_->srcStart);
     }
     bool addFunction(PropertyName* name, uint32_t firstUse, Sig&& sig, Func** func) {
         uint32_t sigIndex;
         if (!declareSig(Move(sig), &sigIndex))
             return false;
         uint32_t funcIndex = numFunctions();
--- a/js/src/asmjs/WasmAST.h
+++ b/js/src/asmjs/WasmAST.h
@@ -546,22 +546,22 @@ class AstExport : public AstNode
     AstName name_;
     DefinitionKind kind_;
     AstRef func_;
 
   public:
     AstExport(AstName name, AstRef func)
       : name_(name), kind_(DefinitionKind::Function), func_(func)
     {}
-    explicit AstExport(AstName name)
-      : name_(name), kind_(DefinitionKind::Memory)
+    explicit AstExport(AstName name, DefinitionKind kind)
+      : name_(name), kind_(kind)
     {}
     AstName name() const { return name_; }
     DefinitionKind kind() const { return kind_; }
-    AstRef& func() { return func_; }
+    AstRef& func() { MOZ_ASSERT(kind_ == DefinitionKind::Function); return func_; }
 };
 
 class AstDataSegment : public AstNode
 {
     uint32_t offset_;
     AstName text_;
 
   public:
--- a/js/src/asmjs/WasmBinary.h
+++ b/js/src/asmjs/WasmBinary.h
@@ -64,17 +64,18 @@ enum class ValType
 enum class TypeConstructor
 {
     Function                             = 0x40
 };
 
 enum class DefinitionKind
 {
     Function                             = 0x00,
-    Memory                               = 0x01
+    Table                                = 0x01,
+    Memory                               = 0x02
 };
 
 enum class ResizableFlags
 {
     Default                              = 0x1,
     HasMaximum                           = 0x2,
     AllowedMask                          = 0x3
 };
--- a/js/src/asmjs/WasmBinaryToAST.cpp
+++ b/js/src/asmjs/WasmBinaryToAST.cpp
@@ -1257,17 +1257,18 @@ AstDecodeMemorySection(AstDecodeContext&
     if (!maxSize.isValid())
         return AstDecodeFail(c, "maximum memory size too big");
 
     uint8_t exported;
     if (!c.d.readFixedU8(&exported))
         return AstDecodeFail(c, "expected exported byte");
 
     if (exported) {
-        AstExport* export_ = new(c.lifo) AstExport(AstName(MOZ_UTF16("memory")));
+        AstName fieldName(MOZ_UTF16("memory"));
+        AstExport* export_ = new(c.lifo) AstExport(fieldName, DefinitionKind::Memory);
         if (!export_ || !c.module().append(export_))
             return false;
     }
 
     if (!c.d.finishSection(sectionStart, sectionSize))
         return AstDecodeFail(c, "memory section byte size mismatch");
 
     c.module().setMemory(AstResizable(initialSizePages, Some(maxSizePages)));
--- a/js/src/asmjs/WasmCode.cpp
+++ b/js/src/asmjs/WasmCode.cpp
@@ -279,40 +279,40 @@ DeserializeSig(const uint8_t* cursor, Si
 
 static size_t
 SizeOfSigExcludingThis(const Sig& sig, MallocSizeOf mallocSizeOf)
 {
     return sig.args().sizeOfExcludingThis(mallocSizeOf);
 }
 
 size_t
-Export::serializedSize() const
+FuncExport::serializedSize() const
 {
     return SerializedSigSize(sig_) +
            sizeof(pod);
 }
 
 uint8_t*
-Export::serialize(uint8_t* cursor) const
+FuncExport::serialize(uint8_t* cursor) const
 {
     cursor = SerializeSig(cursor, sig_);
     cursor = WriteBytes(cursor, &pod, sizeof(pod));
     return cursor;
 }
 
 const uint8_t*
-Export::deserialize(const uint8_t* cursor)
+FuncExport::deserialize(const uint8_t* cursor)
 {
     (cursor = DeserializeSig(cursor, &sig_)) &&
     (cursor = ReadBytes(cursor, &pod, sizeof(pod)));
     return cursor;
 }
 
 size_t
-Export::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
+FuncExport::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
 {
     return SizeOfSigExcludingThis(sig_, mallocSizeOf);
 }
 
 size_t
 FuncImport::serializedSize() const
 {
     return SerializedSigSize(sig_) +
@@ -444,34 +444,34 @@ CacheableChars::sizeOfExcludingThis(Mall
     return mallocSizeOf(get());
 }
 
 size_t
 Metadata::serializedSize() const
 {
     return sizeof(pod()) +
            SerializedVectorSize(funcImports) +
-           SerializedVectorSize(exports) +
+           SerializedVectorSize(funcExports) +
            SerializedPodVectorSize(tables) +
            SerializedPodVectorSize(memoryAccesses) +
            SerializedPodVectorSize(boundsChecks) +
            SerializedPodVectorSize(codeRanges) +
            SerializedPodVectorSize(callSites) +
            SerializedPodVectorSize(callThunks) +
            SerializedPodVectorSize(funcNames) +
            filename.serializedSize() +
            assumptions.serializedSize();
 }
 
 uint8_t*
 Metadata::serialize(uint8_t* cursor) const
 {
     cursor = WriteBytes(cursor, &pod(), sizeof(pod()));
     cursor = SerializeVector(cursor, funcImports);
-    cursor = SerializeVector(cursor, exports);
+    cursor = SerializeVector(cursor, funcExports);
     cursor = SerializePodVector(cursor, tables);
     cursor = SerializePodVector(cursor, memoryAccesses);
     cursor = SerializePodVector(cursor, boundsChecks);
     cursor = SerializePodVector(cursor, codeRanges);
     cursor = SerializePodVector(cursor, callSites);
     cursor = SerializePodVector(cursor, callThunks);
     cursor = SerializePodVector(cursor, funcNames);
     cursor = filename.serialize(cursor);
@@ -479,34 +479,34 @@ Metadata::serialize(uint8_t* cursor) con
     return cursor;
 }
 
 /* static */ const uint8_t*
 Metadata::deserialize(const uint8_t* cursor)
 {
     (cursor = ReadBytes(cursor, &pod(), sizeof(pod()))) &&
     (cursor = DeserializeVector(cursor, &funcImports)) &&
-    (cursor = DeserializeVector(cursor, &exports)) &&
+    (cursor = DeserializeVector(cursor, &funcExports)) &&
     (cursor = DeserializePodVector(cursor, &tables)) &&
     (cursor = DeserializePodVector(cursor, &memoryAccesses)) &&
     (cursor = DeserializePodVector(cursor, &boundsChecks)) &&
     (cursor = DeserializePodVector(cursor, &codeRanges)) &&
     (cursor = DeserializePodVector(cursor, &callSites)) &&
     (cursor = DeserializePodVector(cursor, &callThunks)) &&
     (cursor = DeserializePodVector(cursor, &funcNames)) &&
     (cursor = filename.deserialize(cursor)) &&
     (cursor = assumptions.deserialize(cursor));
     return cursor;
 }
 
 size_t
 Metadata::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
 {
     return SizeOfVectorExcludingThis(funcImports, mallocSizeOf) +
-           SizeOfVectorExcludingThis(exports, mallocSizeOf) +
+           SizeOfVectorExcludingThis(funcExports, mallocSizeOf) +
            tables.sizeOfExcludingThis(mallocSizeOf) +
            memoryAccesses.sizeOfExcludingThis(mallocSizeOf) +
            boundsChecks.sizeOfExcludingThis(mallocSizeOf) +
            codeRanges.sizeOfExcludingThis(mallocSizeOf) +
            callSites.sizeOfExcludingThis(mallocSizeOf) +
            callThunks.sizeOfExcludingThis(mallocSizeOf) +
            funcNames.sizeOfExcludingThis(mallocSizeOf) +
            filename.sizeOfExcludingThis(mallocSizeOf) +
--- a/js/src/asmjs/WasmCode.h
+++ b/js/src/asmjs/WasmCode.h
@@ -112,30 +112,30 @@ struct ShareableBytes : ShareableBase<Sh
     const uint8_t* begin() const { return bytes.begin(); }
     const uint8_t* end() const { return bytes.end(); }
     bool append(const uint8_t *p, uint32_t ct) { return bytes.append(p, ct); }
 };
 
 typedef RefPtr<ShareableBytes> MutableBytes;
 typedef RefPtr<const ShareableBytes> SharedBytes;
 
-// An Export represents a single function inside a wasm Module that has been
+// A FuncExport represents a single function inside a wasm Module that has been
 // exported one or more times.
 
-class Export
+class FuncExport
 {
     Sig sig_;
     struct CacheablePod {
         uint32_t funcIndex_;
         uint32_t stubOffset_;
     } pod;
 
   public:
-    Export() = default;
-    explicit Export(Sig&& sig, uint32_t funcIndex)
+    FuncExport() = default;
+    explicit FuncExport(Sig&& sig, uint32_t funcIndex)
       : sig_(Move(sig))
     {
         pod.funcIndex_ = funcIndex;
         pod.stubOffset_ = UINT32_MAX;
     }
     void initStubOffset(uint32_t stubOffset) {
         MOZ_ASSERT(pod.stubOffset_ == UINT32_MAX);
         pod.stubOffset_ = stubOffset;
@@ -147,20 +147,20 @@ class Export
     uint32_t funcIndex() const {
         return pod.funcIndex_;
     }
     uint32_t stubOffset() const {
         MOZ_ASSERT(pod.stubOffset_ != UINT32_MAX);
         return pod.stubOffset_;
     }
 
-    WASM_DECLARE_SERIALIZABLE(Export)
+    WASM_DECLARE_SERIALIZABLE(FuncExport)
 };
 
-typedef Vector<Export, 0, SystemAllocPolicy> ExportVector;
+typedef Vector<FuncExport, 0, SystemAllocPolicy> FuncExportVector;
 
 // An FuncImport contains the runtime metadata needed to implement a call to an
 // imported function. Each function import has two call stubs: an optimized path
 // into JIT code and a slow path into the generic C++ js::Invoke and these
 // offsets of these stubs are stored so that function-import callsites can be
 // dynamically patched at runtime.
 
 class FuncImport
@@ -410,35 +410,58 @@ typedef Vector<char16_t, 64> TwoByteName
 
 // Metadata holds all the data that is needed to describe compiled wasm code
 // at runtime (as opposed to data that is only used to statically link or
 // instantiate a module).
 //
 // Metadata is built incrementally by ModuleGenerator and then shared immutably
 // between modules.
 
-struct MetadataCacheablePod
+
+class MetadataCacheablePod
 {
+    static const uint32_t NO_START_FUNCTION = UINT32_MAX;
+    static_assert(NO_START_FUNCTION > MaxFuncs, "sentinel value");
+
+    uint32_t              startFuncExportIndex_;
+
+  public:
     ModuleKind            kind;
     MemoryUsage           memoryUsage;
     uint32_t              minMemoryLength;
     uint32_t              maxMemoryLength;
 
-    MetadataCacheablePod() { mozilla::PodZero(this); }
+    MetadataCacheablePod() {
+        mozilla::PodZero(this);
+        startFuncExportIndex_ = NO_START_FUNCTION;
+    }
+
+    bool hasStartFunction() const {
+        return startFuncExportIndex_ != NO_START_FUNCTION;
+    }
+    void initStartFuncExportIndex(uint32_t i) {
+        MOZ_ASSERT(!hasStartFunction());
+        startFuncExportIndex_ = i;
+        MOZ_ASSERT(hasStartFunction());
+    }
+    uint32_t startFuncExportIndex() const {
+        MOZ_ASSERT(hasStartFunction());
+        return startFuncExportIndex_;
+    }
 };
 
 struct Metadata : ShareableBase<Metadata>, MetadataCacheablePod
 {
     virtual ~Metadata() {}
 
     MetadataCacheablePod& pod() { return *this; }
     const MetadataCacheablePod& pod() const { return *this; }
 
     FuncImportVector      funcImports;
-    ExportVector          exports;
+    FuncExportVector      funcExports;
     TableDescVector       tables;
     MemoryAccessVector    memoryAccesses;
     BoundsCheckVector     boundsChecks;
     CodeRangeVector       codeRanges;
     CallSiteVector        callSites;
     CallThunkVector       callThunks;
     NameInBytecodeVector  funcNames;
     CacheableChars        filename;
--- a/js/src/asmjs/WasmCompile.cpp
+++ b/js/src/asmjs/WasmCompile.cpp
@@ -939,26 +939,26 @@ static bool
 DecodeExport(Decoder& d, bool newFormat, ModuleGenerator& mg, CStringSet* dupSet)
 {
     if (!newFormat) {
         uint32_t funcIndex;
         if (!d.readVarU32(&funcIndex))
             return Fail(d, "expected export internal index");
 
         if (funcIndex >= mg.numFuncSigs())
-            return Fail(d, "export function index out of range");
+            return Fail(d, "exported function index out of bounds");
 
         if (!CheckTypeForJS(d, mg.funcSig(funcIndex)))
             return false;
 
         UniqueChars fieldName = DecodeExportName(d, dupSet);
         if (!fieldName)
             return false;
 
-        return mg.declareExport(Move(fieldName), funcIndex);
+        return mg.declareFuncExport(Move(fieldName), funcIndex);
     }
 
     UniqueChars fieldName = DecodeExportName(d, dupSet);
     if (!fieldName)
         return false;
 
     uint32_t exportKind;
     if (!d.readVarU32(&exportKind))
@@ -966,30 +966,40 @@ DecodeExport(Decoder& d, bool newFormat,
 
     switch (DefinitionKind(exportKind)) {
       case DefinitionKind::Function: {
         uint32_t funcIndex;
         if (!d.readVarU32(&funcIndex))
             return Fail(d, "expected export internal index");
 
         if (funcIndex >= mg.numFuncSigs())
-            return Fail(d, "export function index out of range");
+            return Fail(d, "exported function index out of bounds");
 
         if (!CheckTypeForJS(d, mg.funcSig(funcIndex)))
             return false;
 
-        return mg.declareExport(Move(fieldName), funcIndex);
+        return mg.declareFuncExport(Move(fieldName), funcIndex);
+      }
+      case DefinitionKind::Table: {
+        uint32_t tableIndex;
+        if (!d.readVarU32(&tableIndex))
+            return Fail(d, "expected table index");
+
+        if (tableIndex >= mg.tables().length())
+            return Fail(d, "exported table index out of bounds");
+
+        return mg.addTableExport(Move(fieldName));
       }
       case DefinitionKind::Memory: {
         uint32_t memoryIndex;
         if (!d.readVarU32(&memoryIndex))
             return Fail(d, "expected memory index");
 
         if (memoryIndex > 0 || !mg.usesMemory())
-            return Fail(d, "memory index out of bounds");
+            return Fail(d, "exported memory index out of bounds");
 
         return mg.addMemoryExport(Move(fieldName));
       }
       default:
         return Fail(d, "unexpected export kind");
     }
 
     MOZ_CRASH("unreachable");
--- a/js/src/asmjs/WasmGenerator.cpp
+++ b/js/src/asmjs/WasmGenerator.cpp
@@ -94,17 +94,17 @@ ModuleGenerator::~ModuleGenerator()
 }
 
 bool
 ModuleGenerator::init(UniqueModuleGeneratorData shared, CompileArgs&& args,
                       Metadata* maybeAsmJSMetadata)
 {
     alwaysBaseline_ = args.alwaysBaseline;
 
-    if (!funcIndexToExport_.init())
+    if (!funcIndexToFuncExportIndex_.init())
         return false;
 
     linkData_.globalDataLength = AlignBytes(InitialGlobalDataBytes, sizeof(void*));;
 
     // asm.js passes in an AsmJSMetadata subclass to use instead.
     if (maybeAsmJSMetadata) {
         MOZ_ASSERT(shared->kind == ModuleKind::AsmJS);
         metadata_ = maybeAsmJSMetadata;
@@ -350,17 +350,17 @@ ModuleGenerator::finishCodegen()
 
     {
         TempAllocator alloc(&lifo_);
         MacroAssembler masm(MacroAssembler::AsmJSToken(), alloc);
 
         if (!entries.resize(numExports()))
             return false;
         for (uint32_t i = 0; i < numExports(); i++)
-            entries[i] = GenerateEntry(masm, metadata_->exports[i], usesMemory());
+            entries[i] = GenerateEntry(masm, metadata_->funcExports[i], usesMemory());
 
         if (!interpExits.resize(numFuncImports()))
             return false;
         if (!jitExits.resize(numFuncImports()))
             return false;
         for (uint32_t i = 0; i < numFuncImports(); i++) {
             interpExits[i] = GenerateInterpExit(masm, metadata_->funcImports[i], i);
             jitExits[i] = GenerateJitExit(masm, metadata_->funcImports[i], usesMemory());
@@ -375,17 +375,17 @@ ModuleGenerator::finishCodegen()
             return false;
     }
 
     // Adjust each of the resulting Offsets (to account for being merged into
     // masm_) and then create code ranges for all the stubs.
 
     for (uint32_t i = 0; i < numExports(); i++) {
         entries[i].offsetBy(offsetInWhole);
-        metadata_->exports[i].initStubOffset(entries[i].begin);
+        metadata_->funcExports[i].initStubOffset(entries[i].begin);
         if (!metadata_->codeRanges.emplaceBack(CodeRange::Entry, entries[i]))
             return false;
     }
 
     for (uint32_t i = 0; i < numFuncImports(); i++) {
         interpExits[i].offsetBy(offsetInWhole);
         metadata_->funcImports[i].initInterpExitOffset(interpExits[i].begin);
         if (!metadata_->codeRanges.emplaceBack(CodeRange::ImportInterpExit, interpExits[i]))
@@ -642,73 +642,78 @@ ModuleGenerator::numFuncImports() const
 const FuncImportGenDesc&
 ModuleGenerator::funcImport(uint32_t funcImportIndex) const
 {
     MOZ_ASSERT(shared_->funcImports[funcImportIndex].sig);
     return shared_->funcImports[funcImportIndex];
 }
 
 bool
-ModuleGenerator::declareExport(UniqueChars fieldName, uint32_t funcIndex, uint32_t* exportIndex)
+ModuleGenerator::declareFuncExport(UniqueChars fieldName, uint32_t funcIndex,
+                                   uint32_t* funcExportIndex /* = nullptr */)
 {
-    MOZ_ASSERT(!exportMap_.hasStartFunction());
+    MOZ_ASSERT(!metadata_->hasStartFunction());
 
-    if (!exportMap_.fieldNames.append(Move(fieldName)))
-        return false;
-
-    FuncIndexMap::AddPtr p = funcIndexToExport_.lookupForAdd(funcIndex);
+    FuncIndexMap::AddPtr p = funcIndexToFuncExportIndex_.lookupForAdd(funcIndex);
     if (p) {
-        if (exportIndex)
-            *exportIndex = p->value();
-        return exportMap_.fieldsToExports.append(p->value());
+        if (funcExportIndex)
+            *funcExportIndex = p->value();
+        return exports_.emplaceBack(Move(fieldName), p->value());
     }
 
-    uint32_t newExportIndex = metadata_->exports.length();
-    MOZ_ASSERT(newExportIndex < MaxExports);
+    uint32_t newFuncExportIndex = metadata_->funcExports.length();
+    MOZ_ASSERT(newFuncExportIndex < MaxExports);
 
-    if (exportIndex)
-        *exportIndex = newExportIndex;
+    if (funcExportIndex)
+        *funcExportIndex = newFuncExportIndex;
 
     Sig copy;
     if (!copy.clone(funcSig(funcIndex)))
         return false;
 
-    return metadata_->exports.emplaceBack(Move(copy), funcIndex) &&
-           exportMap_.fieldsToExports.append(newExportIndex) &&
-           funcIndexToExport_.add(p, funcIndex, newExportIndex);
+    return metadata_->funcExports.emplaceBack(Move(copy), funcIndex) &&
+           exports_.emplaceBack(Move(fieldName), newFuncExportIndex) &&
+           funcIndexToFuncExportIndex_.add(p, funcIndex, newFuncExportIndex);
 }
 
 uint32_t
 ModuleGenerator::numExports() const
 {
-    return metadata_->exports.length();
+    return metadata_->funcExports.length();
+}
+
+bool
+ModuleGenerator::addTableExport(UniqueChars fieldName)
+{
+    return exports_.emplaceBack(Move(fieldName), DefinitionKind::Table);
 }
 
 bool
 ModuleGenerator::addMemoryExport(UniqueChars fieldName)
 {
-    return exportMap_.fieldNames.append(Move(fieldName)) &&
-           exportMap_.fieldsToExports.append(MemoryExport);
+    return exports_.emplaceBack(Move(fieldName), DefinitionKind::Memory);
 }
 
 bool
 ModuleGenerator::setStartFunction(uint32_t funcIndex)
 {
-    FuncIndexMap::AddPtr p = funcIndexToExport_.lookupForAdd(funcIndex);
+    MOZ_ASSERT(!metadata_->hasStartFunction());
+
+    FuncIndexMap::AddPtr p = funcIndexToFuncExportIndex_.lookupForAdd(funcIndex);
     if (p) {
-        exportMap_.setStartFunction(p->value());
+        metadata_->initStartFuncExportIndex(p->value());
         return true;
     }
 
     Sig copy;
     if (!copy.clone(funcSig(funcIndex)))
         return false;
 
-    exportMap_.setStartFunction(metadata_->exports.length());
-    return metadata_->exports.emplaceBack(Move(copy), funcIndex);
+    metadata_->initStartFuncExportIndex(metadata_->funcExports.length());
+    return metadata_->funcExports.emplaceBack(Move(copy), funcIndex);
 }
 
 bool
 ModuleGenerator::startFuncDefs()
 {
     MOZ_ASSERT(!startedFuncDefs_);
     MOZ_ASSERT(!finishedFuncDefs_);
 
@@ -955,14 +960,14 @@ ModuleGenerator::finish(ImportVector&& i
     }
 
     if (!finishLinkData(code))
         return nullptr;
 
     return js::MakeUnique<Module>(Move(code),
                                   Move(linkData_),
                                   Move(imports),
-                                  Move(exportMap_),
+                                  Move(exports_),
                                   Move(dataSegments_),
                                   Move(elemSegments_),
                                   *metadata_,
                                   bytecode);
 }
--- a/js/src/asmjs/WasmGenerator.h
+++ b/js/src/asmjs/WasmGenerator.h
@@ -94,30 +94,30 @@ class MOZ_STACK_CLASS ModuleGenerator
     typedef Vector<IonCompileTask*, 0, SystemAllocPolicy> IonCompileTaskPtrVector;
 
     // Constant parameters
     bool                            alwaysBaseline_;
 
     // Data that is moved into the result of finish()
     LinkData                        linkData_;
     MutableMetadata                 metadata_;
-    ExportMap                       exportMap_;
+    ExportVector                    exports_;
     DataSegmentVector               dataSegments_;
     ElemSegmentVector               elemSegments_;
 
     // Data scoped to the ModuleGenerator's lifetime
     UniqueModuleGeneratorData       shared_;
     uint32_t                        numSigs_;
     uint32_t                        numTables_;
     LifoAlloc                       lifo_;
     jit::JitContext                 jcx_;
     jit::TempAllocator              masmAlloc_;
     jit::MacroAssembler             masm_;
     Uint32Vector                    funcIndexToCodeRange_;
-    FuncIndexMap                    funcIndexToExport_;
+    FuncIndexMap                    funcIndexToFuncExportIndex_;
     uint32_t                        lastPatchedCallsite_;
     uint32_t                        startOfUnpatchedBranches_;
     JumpSiteArray                   jumpThunks_;
 
     // Parallel compilation
     bool                            parallel_;
     uint32_t                        outstanding_;
     IonCompileTaskVector            tasks_;
@@ -168,20 +168,21 @@ class MOZ_STACK_CLASS ModuleGenerator
     MOZ_MUST_USE bool allocateGlobal(ValType type, bool isConst, uint32_t* index);
     const GlobalDesc& global(unsigned index) const { return shared_->globals[index]; }
 
     // Imports:
     uint32_t numFuncImports() const;
     const FuncImportGenDesc& funcImport(uint32_t funcImportIndex) const;
 
     // Exports:
-    MOZ_MUST_USE bool declareExport(UniqueChars fieldName, uint32_t funcIndex,
-                                    uint32_t* exportIndex = nullptr);
+    MOZ_MUST_USE bool declareFuncExport(UniqueChars fieldName, uint32_t funcIndex,
+                                        uint32_t* funcExportIndex = nullptr);
+    MOZ_MUST_USE bool addTableExport(UniqueChars fieldName);
+    MOZ_MUST_USE bool addMemoryExport(UniqueChars fieldName);
     uint32_t numExports() const;
-    MOZ_MUST_USE bool addMemoryExport(UniqueChars fieldName);
 
     // Function definitions:
     MOZ_MUST_USE bool startFuncDefs();
     MOZ_MUST_USE bool startFuncDef(uint32_t lineOrBytecode, FunctionGenerator* fg);
     MOZ_MUST_USE bool finishFuncDef(uint32_t funcIndex, FunctionGenerator* fg);
     MOZ_MUST_USE bool finishFuncDefs();
 
     // Start function:
--- a/js/src/asmjs/WasmInstance.cpp
+++ b/js/src/asmjs/WasmInstance.cpp
@@ -415,19 +415,19 @@ Instance::memoryBase() const
 
 size_t
 Instance::memoryLength() const
 {
     return memory_->buffer().byteLength();
 }
 
 bool
-Instance::callExport(JSContext* cx, uint32_t exportIndex, CallArgs args)
+Instance::callExport(JSContext* cx, uint32_t funcExportIndex, CallArgs args)
 {
-    const Export& exp = metadata_->exports[exportIndex];
+    const FuncExport& fe = metadata_->funcExports[funcExportIndex];
 
     // Enable/disable profiling in the Module to match the current global
     // profiling state. Don't do this if the Module is already active on the
     // stack since this would leave the Module in a state where profiling is
     // enabled but the stack isn't unwindable.
     if (profilingEnabled() != cx->runtime()->spsProfiler.enabled() && !activation()) {
         if (!toggleProfiling(cx))
             return false;
@@ -437,23 +437,23 @@ Instance::callExport(JSContext* cx, uint
     // array of 16-byte values where each value contains either a coerced int32
     // (in the low word), a double value (in the low dword) or a SIMD vector
     // value, with the coercions specified by the wasm signature. The external
     // entry point unpacks this array into the system-ABI-specified registers
     // and stack memory and then calls into the internal entry point. The return
     // value is stored in the first element of the array (which, therefore, must
     // have length >= 1).
     Vector<ExportArg, 8> exportArgs(cx);
-    if (!exportArgs.resize(Max<size_t>(1, exp.sig().args().length())))
+    if (!exportArgs.resize(Max<size_t>(1, fe.sig().args().length())))
         return false;
 
     RootedValue v(cx);
-    for (unsigned i = 0; i < exp.sig().args().length(); ++i) {
+    for (unsigned i = 0; i < fe.sig().args().length(); ++i) {
         v = i < args.length() ? args[i] : UndefinedValue();
-        switch (exp.sig().arg(i)) {
+        switch (fe.sig().arg(i)) {
           case ValType::I32:
             if (!ToInt32(cx, v, (int32_t*)&exportArgs[i]))
                 return false;
             break;
           case ValType::I64:
             MOZ_ASSERT(JitOptions.wasmTestMode, "no int64 in asm.js/wasm");
             if (!ReadI64Object(cx, v, (int64_t*)&exportArgs[i]))
                 return false;
@@ -528,17 +528,17 @@ Instance::callExport(JSContext* cx, uint
         // when running this module. Additionally, push a JitActivation so that
         // the optimized wasm-to-Ion FFI call path (which we want to be very
         // fast) can avoid doing so. The JitActivation is marked as inactive so
         // stack iteration will skip over it.
         WasmActivation activation(cx, *this);
         JitActivation jitActivation(cx, /* active */ false);
 
         // Call the per-exported-function trampoline created by GenerateEntry.
-        auto funcPtr = JS_DATA_TO_FUNC_PTR(ExportFuncPtr, codeSegment_->code() + exp.stubOffset());
+        auto funcPtr = JS_DATA_TO_FUNC_PTR(ExportFuncPtr, codeSegment_->code() + fe.stubOffset());
         if (!CALL_GENERATED_2(funcPtr, exportArgs.begin(), codeSegment_->globalData()))
             return false;
     }
 
     if (args.isConstructing()) {
         // By spec, when a function is called as a constructor and this function
         // returns a primary type, which is the case for all wasm exported
         // functions, the returned value is discarded and an empty object is
@@ -547,17 +547,17 @@ Instance::callExport(JSContext* cx, uint
         if (!obj)
             return false;
         args.rval().set(ObjectValue(*obj));
         return true;
     }
 
     void* retAddr = &exportArgs[0];
     JSObject* retObj = nullptr;
-    switch (exp.sig().ret()) {
+    switch (fe.sig().ret()) {
       case ExprType::Void:
         args.rval().set(UndefinedValue());
         break;
       case ExprType::I32:
         args.rval().set(Int32Value(*(int32_t*)retAddr));
         break;
       case ExprType::I64:
         MOZ_ASSERT(JitOptions.wasmTestMode, "no int64 in asm.js/wasm");
--- a/js/src/asmjs/WasmInstance.h
+++ b/js/src/asmjs/WasmInstance.h
@@ -25,18 +25,16 @@
 
 namespace js {
 
 class WasmActivation;
 class WasmInstanceObject;
 
 namespace wasm {
 
-class ExportMap;
-
 // Instance represents a wasm instance and provides all the support for runtime
 // execution of code in the instance. Instances share various immutable data
 // structures with the Module from which they were instantiated and other
 // instances instantiated from the same Module. However, an Instance has no
 // direct reference to its source Module which allows a Module to be destroyed
 // while it still has live Instances.
 
 class Instance
@@ -77,23 +75,24 @@ class Instance
              HandleWasmMemoryObject memory,
              SharedTableVector&& tables,
              Handle<FunctionVector> funcImports);
     ~Instance();
     void trace(JSTracer* trc);
 
     const CodeSegment& codeSegment() const { return *codeSegment_; }
     const Metadata& metadata() const { return *metadata_; }
+    const SharedTableVector& tables() const { return tables_; }
     SharedMem<uint8_t*> memoryBase() const;
     size_t memoryLength() const;
 
     // Execute the given export given the JS call arguments, storing the return
     // value in args.rval.
 
-    MOZ_MUST_USE bool callExport(JSContext* cx, uint32_t exportIndex, CallArgs args);
+    MOZ_MUST_USE bool callExport(JSContext* cx, uint32_t funcExportIndex, CallArgs args);
 
     // An instance has a profiling mode that is updated to match the runtime's
     // profiling mode when calling an instance's exports when there are no other
     // activations of the instance live on the stack. Once in profiling mode,
     // ProfilingFrameIterator can be used to asynchronously walk the stack.
     // Otherwise, the ProfilingFrameIterator will skip any activations of this
     // instance.
 
--- a/js/src/asmjs/WasmJS.cpp
+++ b/js/src/asmjs/WasmJS.cpp
@@ -105,16 +105,18 @@ GetImports(JSContext* cx, HandleObject i
           case DefinitionKind::Function:
             if (!IsFunctionObject(v))
                 return Throw(cx, "import object field is not a Function");
 
             if (!funcImports.append(&v.toObject().as<JSFunction>()))
                 return false;
 
             break;
+          case DefinitionKind::Table:
+            MOZ_CRASH("NYI");
           case DefinitionKind::Memory:
             if (!v.isObject() || !v.toObject().is<WasmMemoryObject>())
                 return Throw(cx, "import object field is not a Memory");
 
             MOZ_ASSERT(!memoryImport);
             memoryImport.set(&v.toObject().as<WasmMemoryObject>());
             break;
         }
@@ -568,33 +570,33 @@ WasmMemoryObject::construct(JSContext* c
     if (!memoryObj)
         return false;
 
     args.rval().setObject(*memoryObj);
     return true;
 }
 
 static bool
-IsMemoryBuffer(HandleValue v)
+IsMemory(HandleValue v)
 {
     return v.isObject() && v.toObject().is<WasmMemoryObject>();
 }
 
 static bool
 MemoryBufferGetterImpl(JSContext* cx, const CallArgs& args)
 {
     args.rval().setObject(args.thisv().toObject().as<WasmMemoryObject>().buffer());
     return true;
 }
 
 static bool
 MemoryBufferGetter(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
-    return CallNonGenericMethod<IsMemoryBuffer, MemoryBufferGetterImpl>(cx, args);
+    return CallNonGenericMethod<IsMemory, MemoryBufferGetterImpl>(cx, args);
 }
 
 const JSPropertySpec WasmMemoryObject::properties[] =
 {
     JS_PSG("buffer", MemoryBufferGetter, 0),
     JS_PS_END
 };
 
@@ -628,22 +630,21 @@ static const ClassOps WasmTableObject_cl
 const Class WasmTableObject::class_ =
 {
     "WebAssembly.Table",
     JSCLASS_DELAY_METADATA_BUILDER |
     JSCLASS_HAS_RESERVED_SLOTS(WasmTableObject::RESERVED_SLOTS),
     &WasmTableObject_classOps
 };
 
-const JSPropertySpec WasmTableObject::properties[] =
-{ JS_PS_END };
+/* static */ WasmTableObject*
+WasmTableObject::create(JSContext* cx, Table& table)
+{
+    RootedObject proto(cx, &cx->global()->getPrototype(JSProto_WasmTable).toObject());
 
-/* static */ WasmTableObject*
-WasmTableObject::create(ExclusiveContext* cx, Table& table, HandleObject proto)
-{
     AutoSetNewObjectMetadata metadata(cx);
     auto* obj = NewObjectWithGivenProto<WasmTableObject>(cx, proto);
     if (!obj)
         return nullptr;
 
     obj->initReservedSlot(TABLE_SLOT, PrivateValue((void*)&table));
     table.AddRef();
     return obj;
@@ -683,25 +684,50 @@ WasmTableObject::construct(JSContext* cx
         JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_SIZE, "Table", "initial");
         return false;
     }
 
     SharedTable table = Table::create(cx, TableKind::AnyFunction, uint32_t(initialDbl));
     if (!table)
         return false;
 
-    RootedObject proto(cx, &cx->global()->getPrototype(JSProto_WasmTable).toObject());
-    RootedWasmTableObject tableObj(cx, WasmTableObject::create(cx, *table, proto));
+    RootedWasmTableObject tableObj(cx, WasmTableObject::create(cx, *table));
     if (!tableObj)
         return false;
 
     args.rval().setObject(*tableObj);
     return true;
 }
 
+static bool
+IsTable(HandleValue v)
+{
+    return v.isObject() && v.toObject().is<WasmTableObject>();
+}
+
+static bool
+TableLengthGetterImpl(JSContext* cx, const CallArgs& args)
+{
+    args.rval().setNumber(args.thisv().toObject().as<WasmTableObject>().table().length());
+    return true;
+}
+
+static bool
+TableLengthGetter(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    return CallNonGenericMethod<IsTable, TableLengthGetterImpl>(cx, args);
+}
+
+const JSPropertySpec WasmTableObject::properties[] =
+{
+    JS_PSG("length", TableLengthGetter, 0),
+    JS_PS_END
+};
+
 Table&
 WasmTableObject::table() const
 {
     return *(Table*)getReservedSlot(TABLE_SLOT).toPrivate();
 }
 
 // ============================================================================
 // WebAssembly class and static methods
--- a/js/src/asmjs/WasmJS.h
+++ b/js/src/asmjs/WasmJS.h
@@ -161,20 +161,19 @@ class WasmTableObject : public NativeObj
     static const unsigned TABLE_SLOT = 0;
   public:
     static const JSProtoKey KEY = JSProto_WasmTable;
     static const unsigned RESERVED_SLOTS = 1;
     static const Class class_;
     static const JSPropertySpec properties[];
     static bool construct(JSContext*, unsigned, Value*);
 
-    static WasmTableObject* create(ExclusiveContext* cx,
-                                   wasm::Table& table,
-                                   HandleObject proto);
+    static WasmTableObject* create(JSContext* cx, wasm::Table& table);
     wasm::Table& table() const;
 };
 
 typedef Rooted<WasmTableObject*> RootedWasmTableObject;
 typedef Handle<WasmTableObject*> HandleWasmTableObject;
+typedef MutableHandle<WasmTableObject*> MutableHandleWasmTableObject;
 
 } // namespace js
 
 #endif // wasm_js_h
--- a/js/src/asmjs/WasmModule.cpp
+++ b/js/src/asmjs/WasmModule.cpp
@@ -153,47 +153,64 @@ Import::deserialize(const uint8_t* curso
 
 size_t
 Import::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
 {
     return module.sizeOfExcludingThis(mallocSizeOf) +
            func.sizeOfExcludingThis(mallocSizeOf);
 }
 
-size_t
-ExportMap::serializedSize() const
+Export::Export(UniqueChars fieldName, uint32_t funcExportIndex)
+  : fieldName_(Move(fieldName))
+{
+    pod.kind_ = DefinitionKind::Function;
+    pod.funcExportIndex_ = funcExportIndex;
+}
+
+Export::Export(UniqueChars fieldName, DefinitionKind kind)
+  : fieldName_(Move(fieldName))
 {
-    return SerializedVectorSize(fieldNames) +
-           SerializedPodVectorSize(fieldsToExports) +
-           sizeof(uint32_t);
+    pod.kind_ = kind;
+    pod.funcExportIndex_ = 0;
+}
+
+uint32_t
+Export::funcExportIndex() const
+{
+    MOZ_ASSERT(pod.kind_ == DefinitionKind::Function);
+    return pod.funcExportIndex_;
+}
+
+size_t
+Export::serializedSize() const
+{
+    return fieldName_.serializedSize() +
+           sizeof(pod);
 }
 
 uint8_t*
-ExportMap::serialize(uint8_t* cursor) const
+Export::serialize(uint8_t* cursor) const
 {
-    cursor = SerializeVector(cursor, fieldNames);
-    cursor = SerializePodVector(cursor, fieldsToExports);
-    cursor = WriteScalar(cursor, startExportIndex);
+    cursor = fieldName_.serialize(cursor);
+    cursor = WriteBytes(cursor, &pod, sizeof(pod));
     return cursor;
 }
 
 const uint8_t*
-ExportMap::deserialize(const uint8_t* cursor)
+Export::deserialize(const uint8_t* cursor)
 {
-    (cursor = DeserializeVector(cursor, &fieldNames)) &&
-    (cursor = DeserializePodVector(cursor, &fieldsToExports)) &&
-    (cursor = ReadScalar(cursor, &startExportIndex));
+    (cursor = fieldName_.deserialize(cursor)) &&
+    (cursor = ReadBytes(cursor, &pod, sizeof(pod)));
     return cursor;
 }
 
 size_t
-ExportMap::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
+Export::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
 {
-    return SizeOfVectorExcludingThis(fieldNames, mallocSizeOf) &&
-           fieldsToExports.sizeOfExcludingThis(mallocSizeOf);
+    return fieldName_.sizeOfExcludingThis(mallocSizeOf);
 }
 
 size_t
 ElemSegment::serializedSize() const
 {
     return sizeof(tableIndex) +
            SerializedPodVectorSize(elems);
 }
@@ -221,30 +238,30 @@ ElemSegment::sizeOfExcludingThis(MallocS
 }
 
 size_t
 Module::serializedSize() const
 {
     return SerializedPodVectorSize(code_) +
            linkData_.serializedSize() +
            SerializedVectorSize(imports_) +
-           exportMap_.serializedSize() +
+           SerializedVectorSize(exports_) +
            SerializedPodVectorSize(dataSegments_) +
            SerializedVectorSize(elemSegments_) +
            metadata_->serializedSize() +
            SerializedPodVectorSize(bytecode_->bytes);
 }
 
 uint8_t*
 Module::serialize(uint8_t* cursor) const
 {
     cursor = SerializePodVector(cursor, code_);
     cursor = linkData_.serialize(cursor);
     cursor = SerializeVector(cursor, imports_);
-    cursor = exportMap_.serialize(cursor);
+    cursor = SerializeVector(cursor, exports_);
     cursor = SerializePodVector(cursor, dataSegments_);
     cursor = SerializeVector(cursor, elemSegments_);
     cursor = metadata_->serialize(cursor);
     cursor = SerializePodVector(cursor, bytecode_->bytes);
     return cursor;
 }
 
 /* static */ const uint8_t*
@@ -260,18 +277,18 @@ Module::deserialize(const uint8_t* curso
     if (!cursor)
         return nullptr;
 
     ImportVector imports;
     cursor = DeserializeVector(cursor, &imports);
     if (!cursor)
         return nullptr;
 
-    ExportMap exportMap;
-    cursor = exportMap.deserialize(cursor);
+    ExportVector exports;
+    cursor = DeserializeVector(cursor, &exports);
     if (!cursor)
         return nullptr;
 
     DataSegmentVector dataSegments;
     cursor = DeserializePodVector(cursor, &dataSegments);
     if (!cursor)
         return nullptr;
 
@@ -298,17 +315,17 @@ Module::deserialize(const uint8_t* curso
         return nullptr;
     cursor = DeserializePodVector(cursor, &bytecode->bytes);
     if (!cursor)
         return nullptr;
 
     *module = js::MakeUnique<Module>(Move(code),
                                      Move(linkData),
                                      Move(imports),
-                                     Move(exportMap),
+                                     Move(exports),
                                      Move(dataSegments),
                                      Move(elemSegments),
                                      *metadata,
                                      *bytecode);
     if (!*module)
         return nullptr;
 
     return cursor;
@@ -320,17 +337,17 @@ Module::addSizeOfMisc(MallocSizeOf mallo
                       ShareableBytes::SeenSet* seenBytes,
                       size_t* code,
                       size_t* data) const
 {
     *data += mallocSizeOf(this) +
              code_.sizeOfExcludingThis(mallocSizeOf) +
              linkData_.sizeOfExcludingThis(mallocSizeOf) +
              SizeOfVectorExcludingThis(imports_, mallocSizeOf) +
-             exportMap_.sizeOfExcludingThis(mallocSizeOf) +
+             SizeOfVectorExcludingThis(exports_, mallocSizeOf) +
              dataSegments_.sizeOfExcludingThis(mallocSizeOf) +
              SizeOfVectorExcludingThis(elemSegments_, mallocSizeOf) +
              metadata_->sizeOfIncludingThisIfNotSeen(mallocSizeOf, seenMetadata) +
              bytecode_->sizeOfIncludingThisIfNotSeen(mallocSizeOf, seenBytes);
 }
 
 // asm.js module instantiation supplies its own buffer, but for wasm, create and
 // initialize the buffer if one is requested. Either way, the buffer is wrapped
@@ -409,89 +426,98 @@ Module::instantiateTable(JSContext* cx, 
 
 static bool
 WasmCall(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     RootedFunction callee(cx, &args.callee().as<JSFunction>());
 
     Instance& instance = ExportedFunctionToInstance(callee);
-    uint32_t exportIndex = ExportedFunctionToExportIndex(callee);
+    uint32_t funcExportIndex = ExportedFunctionToExportIndex(callee);
 
-    return instance.callExport(cx, exportIndex, args);
+    return instance.callExport(cx, funcExportIndex, args);
 }
 
 static JSFunction*
-NewExportedFunction(JSContext* cx, Handle<WasmInstanceObject*> instanceObj, uint32_t exportIndex)
+NewExportedFunction(JSContext* cx, HandleWasmInstanceObject instanceObj, uint32_t funcExportIndex)
 {
     Instance& instance = instanceObj->instance();
     const Metadata& metadata = instance.metadata();
-    const Export& exp = metadata.exports[exportIndex];
-    unsigned numArgs = exp.sig().args().length();
+    const FuncExport& fe = metadata.funcExports[funcExportIndex];
+    unsigned numArgs = fe.sig().args().length();
 
-    RootedAtom name(cx, instance.getFuncAtom(cx, exp.funcIndex()));
+    RootedAtom name(cx, instance.getFuncAtom(cx, fe.funcIndex()));
     if (!name)
         return nullptr;
 
     JSFunction* fun = NewNativeConstructor(cx, WasmCall, numArgs, name,
                                            gc::AllocKind::FUNCTION_EXTENDED, GenericObject,
                                            JSFunction::ASMJS_CTOR);
     if (!fun)
         return nullptr;
 
     fun->setExtendedSlot(FunctionExtended::WASM_INSTANCE_SLOT, ObjectValue(*instanceObj));
-    fun->setExtendedSlot(FunctionExtended::WASM_EXPORT_INDEX_SLOT, Int32Value(exportIndex));
+    fun->setExtendedSlot(FunctionExtended::WASM_EXPORT_INDEX_SLOT, Int32Value(funcExportIndex));
     return fun;
 }
 
 static bool
 CreateExportObject(JSContext* cx,
                    HandleWasmInstanceObject instanceObj,
                    HandleWasmMemoryObject memoryObj,
-                   const ExportMap& exportMap,
-                   const Metadata& metadata,
+                   const ExportVector& exports,
                    MutableHandleObject exportObj)
 {
-    MOZ_ASSERT(exportMap.fieldNames.length() == exportMap.fieldsToExports.length());
+    const Instance& instance = instanceObj->instance();
+    const Metadata& metadata = instance.metadata();
+    const SharedTableVector& tables = instance.tables();
 
-    if (metadata.isAsmJS() &&
-        exportMap.fieldNames.length() == 1 &&
-        strlen(exportMap.fieldNames[0].get()) == 0)
-    {
+    if (metadata.isAsmJS() && exports.length() == 1 && strlen(exports[0].fieldName()) == 0) {
         exportObj.set(NewExportedFunction(cx, instanceObj, 0));
         return !!exportObj;
     }
 
     exportObj.set(JS_NewPlainObject(cx));
     if (!exportObj)
         return false;
 
     Rooted<ValueVector> vals(cx, ValueVector(cx));
-    for (size_t exportIndex = 0; exportIndex < metadata.exports.length(); exportIndex++) {
-        JSFunction* fun = NewExportedFunction(cx, instanceObj, exportIndex);
+    for (size_t i = 0; i < metadata.funcExports.length(); i++) {
+        JSFunction* fun = NewExportedFunction(cx, instanceObj, i);
         if (!fun || !vals.append(ObjectValue(*fun)))
             return false;
     }
 
-    for (size_t fieldIndex = 0; fieldIndex < exportMap.fieldNames.length(); fieldIndex++) {
-        const char* fieldName = exportMap.fieldNames[fieldIndex].get();
-        JSAtom* atom = AtomizeUTF8Chars(cx, fieldName, strlen(fieldName));
+    RootedWasmTableObject tableObj(cx);
+    for (const Export& exp : exports) {
+        JSAtom* atom = AtomizeUTF8Chars(cx, exp.fieldName(), strlen(exp.fieldName()));
         if (!atom)
             return false;
 
         RootedId id(cx, AtomToId(atom));
         RootedValue val(cx);
-        uint32_t exportIndex = exportMap.fieldsToExports[fieldIndex];
-        if (exportIndex == MemoryExport) {
+        switch (exp.kind()) {
+          case DefinitionKind::Function:
+            val = vals[exp.funcExportIndex()];
+            break;
+          case DefinitionKind::Table:
+            MOZ_ASSERT(tables.length() == 1);
+            if (!tableObj) {
+                tableObj = WasmTableObject::create(cx, *tables[0]);
+                if (!tableObj)
+                    return false;
+            }
+            val = ObjectValue(*tableObj);
+            break;
+          case DefinitionKind::Memory:
             if (metadata.assumptions.newFormat)
                 val = ObjectValue(*memoryObj);
             else
                 val = ObjectValue(memoryObj->buffer());
-        } else {
-            val = vals[exportIndex];
+            break;
         }
 
         if (!JS_DefinePropertyById(cx, exportObj, id, val, JSPROP_ENUMERATE))
             return false;
     }
 
     return true;
 }
@@ -544,17 +570,17 @@ Module::instantiate(JSContext* cx,
             return false;
 
         instanceObj->init(Move(instance));
     }
 
     // Create the export object.
 
     RootedObject exportObj(cx);
-    if (!CreateExportObject(cx, instanceObj, memory, exportMap_, *metadata_, &exportObj))
+    if (!CreateExportObject(cx, instanceObj, memory, exports_, &exportObj))
         return false;
 
     instanceObj->initExportsObject(exportObj);
 
     JSAtom* atom = Atomize(cx, ExportField, strlen(ExportField));
     if (!atom)
         return false;
     RootedId id(cx, AtomToId(atom));
@@ -566,19 +592,19 @@ Module::instantiate(JSContext* cx,
     // Done! Notify the Debugger of the new Instance.
 
     Debugger::onNewWasmInstance(cx, instanceObj);
 
     // Call the start function, if there's one. By specification, it does not
     // take any arguments nor does it return a value, so just create a dummy
     // arguments object.
 
-    if (exportMap_.hasStartFunction()) {
+    if (metadata_->hasStartFunction()) {
         FixedInvokeArgs<0> args(cx);
-        if (!instanceObj->instance().callExport(cx, exportMap_.startFunctionExportIndex(), args))
+        if (!instanceObj->instance().callExport(cx, metadata_->startFuncExportIndex(), args))
             return false;
     }
 
     return true;
 }
 
 bool
 wasm::IsExportedFunction(JSFunction* fun)
--- a/js/src/asmjs/WasmModule.h
+++ b/js/src/asmjs/WasmModule.h
@@ -98,59 +98,48 @@ struct Import
       : module(Move(module)), func(Move(func)), kind(kind)
     {}
 
     WASM_DECLARE_SERIALIZABLE(Import)
 };
 
 typedef Vector<Import, 0, SystemAllocPolicy> ImportVector;
 
-// ExportMap describes all of a single module's exports. The ExportMap describes
-// how the Exports (stored in Metadata) are mapped to the fields of the export
-// object produced by instantiation. The 'fieldNames' vector provides the list
-// of names of the module's exports. For each field name, 'fieldsToExports'
-// provides either:
-//  - the sentinel value MemoryExport indicating an export of linear memory; or
-//  - the index of an export into the ExportVector in Metadata
-//
-// ExportMap also contains the start function's export index, which maps to the
-// export that is called at each instantiation of a given module.
+// Export describes the export of a definition in a Module to a field in the
+// export object. For functions, Export stores an index into the
+// FuncExportVector in Metadata. For memory and table exports, there is
+// at most one (default) memory/table so no index is needed. Note: a single
+// definition can be exported by multiple Exports in the ExportVector.
 //
-// ExportMap is built incrementally by ModuleGenerator and then stored immutably
-// by Module.
-
-static const uint32_t MemoryExport = UINT32_MAX;
+// ExportVector is built incrementally by ModuleGenerator and then stored
+// immutably by Module.
 
-static const uint32_t NO_START_FUNCTION = UINT32_MAX;
-
-class ExportMap
+class Export
 {
-    uint32_t startExportIndex;
+    CacheableChars fieldName_;
+    struct CacheablePod {
+        DefinitionKind kind_;
+        uint32_t funcExportIndex_;
+    } pod;
 
   public:
-    CacheableCharsVector fieldNames;
-    Uint32Vector fieldsToExports;
+    Export() = default;
+    explicit Export(UniqueChars fieldName, uint32_t funcExportIndex);
+    explicit Export(UniqueChars fieldName, DefinitionKind kind);
 
-    ExportMap() : startExportIndex(NO_START_FUNCTION) {}
+    const char* fieldName() const { return fieldName_.get(); }
 
-    bool hasStartFunction() const {
-        return startExportIndex != NO_START_FUNCTION;
-    }
-    void setStartFunction(uint32_t index) {
-        MOZ_ASSERT(!hasStartFunction());
-        startExportIndex = index;
-    }
-    uint32_t startFunctionExportIndex() const {
-        MOZ_ASSERT(hasStartFunction());
-        return startExportIndex;
-    }
+    DefinitionKind kind() const { return pod.kind_; }
+    uint32_t funcExportIndex() const;
 
-    WASM_DECLARE_SERIALIZABLE(ExportMap)
+    WASM_DECLARE_SERIALIZABLE(Export)
 };
 
+typedef Vector<Export, 0, SystemAllocPolicy> ExportVector;
+
 // DataSegment describes the offset of a data segment in the bytecode that is
 // to be copied at a given offset into linear memory upon instantiation.
 
 struct DataSegment
 {
     uint32_t memoryOffset;
     uint32_t bytecodeOffset;
     uint32_t length;
@@ -189,38 +178,38 @@ typedef Vector<ElemSegment, 0, SystemAll
 // time it is instantiated. In the future, Module will store a shareable,
 // immutable CodeSegment that can be shared by all its instances.
 
 class Module
 {
     const Bytes             code_;
     const LinkData          linkData_;
     const ImportVector      imports_;
-    const ExportMap         exportMap_;
+    const ExportVector      exports_;
     const DataSegmentVector dataSegments_;
     const ElemSegmentVector elemSegments_;
     const SharedMetadata    metadata_;
     const SharedBytes       bytecode_;
 
     bool instantiateMemory(JSContext* cx, MutableHandleWasmMemoryObject memory) const;
     bool instantiateTable(JSContext* cx, const CodeSegment& cs, SharedTableVector* tables) const;
 
   public:
     Module(Bytes&& code,
            LinkData&& linkData,
            ImportVector&& imports,
-           ExportMap&& exportMap,
+           ExportVector&& exports,
            DataSegmentVector&& dataSegments,
            ElemSegmentVector&& elemSegments,
            const Metadata& metadata,
            const ShareableBytes& bytecode)
       : code_(Move(code)),
         linkData_(Move(linkData)),
         imports_(Move(imports)),
-        exportMap_(Move(exportMap)),
+        exports_(Move(exports)),
         dataSegments_(Move(dataSegments)),
         elemSegments_(Move(elemSegments)),
         metadata_(&metadata),
         bytecode_(&bytecode)
     {}
 
     const Metadata& metadata() const { return *metadata_; }
     const ImportVector& imports() const { return imports_; }
--- a/js/src/asmjs/WasmStubs.cpp
+++ b/js/src/asmjs/WasmStubs.cpp
@@ -91,17 +91,17 @@ static const unsigned FramePushedAfterSa
 #endif
 static const unsigned FramePushedForEntrySP = FramePushedAfterSave + sizeof(void*);
 
 // Generate a stub that enters wasm from a C++ caller via the native ABI.
 // The signature of the entry point is Module::CodePtr. The exported wasm
 // function has an ABI derived from its specific signature, so this function
 // must map from the ABI of CodePtr to the export's signature's ABI.
 Offsets
-wasm::GenerateEntry(MacroAssembler& masm, const Export& exp, bool usesHeap)
+wasm::GenerateEntry(MacroAssembler& masm, const FuncExport& fe, bool usesHeap)
 {
     masm.haltingAlign(CodeAlignment);
 
     Offsets offsets;
     offsets.begin = masm.currentOffset();
 
     // Save the return address if it wasn't already saved by the call insn.
 #if defined(JS_CODEGEN_ARM)
@@ -156,21 +156,21 @@ wasm::GenerateEntry(MacroAssembler& masm
     masm.storeStackPtr(Address(scratch, WasmActivation::offsetOfEntrySP()));
 
     // Dynamically align the stack since ABIStackAlignment is not necessarily
     // AsmJSStackAlignment. We'll use entrySP to recover the original stack
     // pointer on return.
     masm.andToStackPtr(Imm32(~(AsmJSStackAlignment - 1)));
 
     // Bump the stack for the call.
-    masm.reserveStack(AlignBytes(StackArgBytes(exp.sig().args()), AsmJSStackAlignment));
+    masm.reserveStack(AlignBytes(StackArgBytes(fe.sig().args()), AsmJSStackAlignment));
 
     // Copy parameters out of argv and into the registers/stack-slots specified by
     // the system ABI.
-    for (ABIArgValTypeIter iter(exp.sig().args()); !iter.done(); iter++) {
+    for (ABIArgValTypeIter iter(fe.sig().args()); !iter.done(); iter++) {
         unsigned argOffset = iter.index() * sizeof(ExportArg);
         Address src(argv, argOffset);
         MIRType type = iter.mirType();
         MOZ_ASSERT_IF(type == MIRType::Int64, JitOptions.wasmTestMode);
         switch (iter->kind()) {
           case ABIArg::GPR:
             if (type == MIRType::Int32)
                 masm.load32(src, iter->gpr());
@@ -248,28 +248,28 @@ wasm::GenerateEntry(MacroAssembler& masm
                 MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("unexpected stack arg type");
             }
             break;
         }
     }
 
     // Call into the real function.
     masm.assertStackAlignment(AsmJSStackAlignment);
-    masm.call(CallSiteDesc(CallSiteDesc::Relative), exp.funcIndex());
+    masm.call(CallSiteDesc(CallSiteDesc::Relative), fe.funcIndex());
 
     // Recover the stack pointer value before dynamic alignment.
     masm.loadWasmActivation(scratch);
     masm.loadStackPtr(Address(scratch, WasmActivation::offsetOfEntrySP()));
     masm.setFramePushed(FramePushedForEntrySP);
 
     // Recover the 'argv' pointer which was saved before aligning the stack.
     masm.Pop(argv);
 
     // Store the return value in argv[0]
-    switch (exp.sig().ret()) {
+    switch (fe.sig().ret()) {
       case ExprType::Void:
         break;
       case ExprType::I32:
         masm.store32(ReturnReg, Address(argv, 0));
         break;
       case ExprType::I64:
         MOZ_ASSERT(JitOptions.wasmTestMode, "no int64 in asm.js/wasm");
         masm.store64(ReturnReg64, Address(argv, 0));
--- a/js/src/asmjs/WasmStubs.h
+++ b/js/src/asmjs/WasmStubs.h
@@ -22,21 +22,21 @@
 #include "asmjs/WasmTypes.h"
 
 namespace js {
 
 namespace jit { class MacroAssembler; }
 
 namespace wasm {
 
-class Export;
+class FuncExport;
 class FuncImport;
 
 extern Offsets
-GenerateEntry(jit::MacroAssembler& masm, const Export& exp, bool usesHeap);
+GenerateEntry(jit::MacroAssembler& masm, const FuncExport& fe, bool usesHeap);
 
 extern ProfilingOffsets
 GenerateInterpExit(jit::MacroAssembler& masm, const FuncImport& fi, uint32_t funcImportIndex);
 
 extern ProfilingOffsets
 GenerateJitExit(jit::MacroAssembler& masm, const FuncImport& fi, bool usesHeap);
 
 extern Offsets
--- a/js/src/asmjs/WasmTextToBinary.cpp
+++ b/js/src/asmjs/WasmTextToBinary.cpp
@@ -2480,18 +2480,20 @@ ParseExport(WasmParseContext& c)
         return nullptr;
 
     WasmToken exportee = c.ts.get();
     switch (exportee.kind()) {
       case WasmToken::Index:
         return new(c.lifo) AstExport(name.text(), AstRef(AstName(), exportee.index()));
       case WasmToken::Name:
         return new(c.lifo) AstExport(name.text(), AstRef(exportee.name(), AstNoIndex));
+      case WasmToken::Table:
+        return new(c.lifo) AstExport(name.text(), DefinitionKind::Table);
       case WasmToken::Memory:
-        return new(c.lifo) AstExport(name.text());
+        return new(c.lifo) AstExport(name.text(), DefinitionKind::Memory);
       default:
         break;
     }
 
     c.ts.generateError(exportee, c.error);
     return nullptr;
 
 }
@@ -3070,16 +3072,17 @@ ResolveModule(LifoAlloc& lifo, AstModule
             return r.fail("duplicate import");
 
         switch (imp->kind()) {
           case DefinitionKind::Function:
             if (!r.resolveSignature(imp->funcSig()))
                 return false;
             break;
           case DefinitionKind::Memory:
+          case DefinitionKind::Table:
             break;
         }
     }
 
     for (AstExport* export_ : module->exports()) {
         if (export_->kind() != DefinitionKind::Function)
             continue;
         if (!r.resolveFunction(export_->func()))
@@ -3549,16 +3552,18 @@ EncodeImport(Encoder& e, bool newFormat,
     if (!e.writeVarU32(uint32_t(imp.kind())))
         return false;
 
     switch (imp.kind()) {
       case DefinitionKind::Function:
         if (!e.writeVarU32(imp.funcSig().index()))
             return false;
         break;
+      case DefinitionKind::Table:
+        MOZ_CRASH("NYI");
       case DefinitionKind::Memory:
         if (!EncodeResizable(e, imp.memory()))
             return false;
         break;
     }
 
     return true;
 }
@@ -3646,16 +3651,17 @@ EncodeExport(Encoder& e, bool newFormat,
     if (!e.writeVarU32(uint32_t(exp.kind())))
         return false;
 
     switch (exp.kind()) {
       case DefinitionKind::Function:
         if (!e.writeVarU32(exp.func().index()))
             return false;
         break;
+      case DefinitionKind::Table:
       case DefinitionKind::Memory:
         if (!e.writeVarU32(0))
             return false;
         break;
     }
 
     return true;
 }
--- a/js/src/jit-test/tests/wasm/basic.js
+++ b/js/src/jit-test/tests/wasm/basic.js
@@ -24,18 +24,18 @@ assertEq(desc.enumerable, true);
 assertEq(desc.configurable, true);
 assertEq(desc.value(), undefined);
 
 wasmEvalText('(module (func) (func) (export "a" 0))');
 wasmEvalText('(module (func) (func) (export "a" 1))');
 wasmEvalText('(module (func $a) (func $b) (export "a" $a) (export "b" $b))');
 wasmEvalText('(module (func $a) (func $b) (export "a" $a) (export "b" $b))');
 
-assertErrorMessage(() => wasmEvalText('(module (func) (export "a" 1))'), TypeError, /export function index out of range/);
-assertErrorMessage(() => wasmEvalText('(module (func) (func) (export "a" 2))'), TypeError, /export function index out of range/);
+assertErrorMessage(() => wasmEvalText('(module (func) (export "a" 1))'), TypeError, /exported function index out of bounds/);
+assertErrorMessage(() => wasmEvalText('(module (func) (func) (export "a" 2))'), TypeError, /exported function index out of bounds/);
 
 var o = wasmEvalText('(module (func) (export "a" 0) (export "b" 0))');
 assertEq(Object.getOwnPropertyNames(o).sort().toString(), "a,b");
 assertEq(o.a.name, "wasm-function[0]");
 assertEq(o.b.name, "wasm-function[0]");
 assertEq(o.a === o.b, true);
 
 var o = wasmEvalText('(module (func) (func) (export "a" 0) (export "b" 1))');
--- a/js/src/jit-test/tests/wasm/import-export.js
+++ b/js/src/jit-test/tests/wasm/import-export.js
@@ -51,16 +51,17 @@ assertEq(new Instance(m3, {baz:{quux:()=
 const m5 = new Module(textToBinary('(module (import "a" "b" (memory 2)))'));
 assertErrorMessage(() => new Instance(m5, {a:{b:mem1Page}}), TypeError, /imported Memory with incompatible size/);
 assertEq(new Instance(m5, {a:{b:mem2Page}}) instanceof Instance, true);
 assertEq(new Instance(m5, {a:{b:mem3Page}}) instanceof Instance, true);
 assertEq(new Instance(m5, {a:{b:mem4Page}}) instanceof Instance, true);
 
 assertErrorMessage(() => new Module(textToBinary('(module (memory 2 1))')), TypeError, /maximum length less than initial length/);
 assertErrorMessage(() => new Module(textToBinary('(module (import "a" "b" (memory 2 1)))')), TypeError, /maximum length less than initial length/);
+assertErrorMessage(() => new Module(textToBinary('(module (table (resizable 2 1)))')), TypeError, /maximum length less than initial length/);
 
 // Import order:
 
 var arr = [];
 var importObj = {
     get foo() { arr.push("foo") },
     get baz() { arr.push("bad") },
 };
@@ -153,24 +154,67 @@ assertEq(e.foo(), undefined);
 assertEq(e.bar.buffer.byteLength, 64*1024);
 
 var code = textToBinary('(module (memory 1 1) (export "" memory))');
 var e = new Instance(new Module(code)).exports;
 assertEq(Object.keys(e).length, 1);
 assertEq(String(Object.keys(e)), "");
 assertEq(e[""] instanceof Memory, true);
 
+var code = textToBinary('(module (table) (export "tbl" table))');
+var e = new Instance(new Module(code)).exports;
+assertEq(Object.keys(e).join(), "tbl");
+assertEq(e.tbl instanceof Table, true);
+assertEq(e.tbl.length, 0);
+
+var code = textToBinary('(module (table (resizable 2)) (export "t1" table) (export "t2" table))');
+var e = new Instance(new Module(code)).exports;
+assertEq(Object.keys(e).join(), "t1,t2");
+assertEq(e.t1 instanceof Table, true);
+assertEq(e.t2 instanceof Table, true);
+assertEq(e.t1, e.t2);
+assertEq(e.t1.length, 2);
+
+var code = textToBinary('(module (table (resizable 2)) (memory 1 1) (func) (export "t" table) (export "m" memory) (export "f" 0))');
+var e = new Instance(new Module(code)).exports;
+assertEq(Object.keys(e).join(), "t,m,f");
+assertEq(e.f(), undefined);
+assertEq(e.t instanceof Table, true);
+assertEq(e.m instanceof Memory, true);
+assertEq(e.t.length, 2);
+
+var code = textToBinary('(module (table (resizable 1)) (memory 1 1) (func) (export "m" memory) (export "f" 0) (export "t" table))');
+var e = new Instance(new Module(code)).exports;
+assertEq(Object.keys(e).join(), "m,f,t");
+assertEq(e.f(), undefined);
+assertEq(e.t instanceof Table, true);
+assertEq(e.m instanceof Memory, true);
++assertEq(e.t.length, 1);
+
+var code = textToBinary('(module (table) (export "" table))');
+var e = new Instance(new Module(code)).exports;
+assertEq(Object.keys(e).length, 1);
+assertEq(String(Object.keys(e)), "");
+assertEq(e[""] instanceof Table, true);
++assertEq(e[""].length, 0);
+
 // Re-exports:
 
 var code = textToBinary('(module (import "a" "b" (memory 1 1)) (export "foo" memory) (export "bar" memory))');
 var mem = new Memory({initial:1});
 var e = new Instance(new Module(code), {a:{b:mem}}).exports;
 assertEq(mem, e.foo);
 assertEq(mem, e.bar);
 
+// Non-existent export errors
+
+assertErrorMessage(() => new Module(textToBinary('(module (export "a" 0))')), TypeError, /exported function index out of bounds/);
+assertErrorMessage(() => new Module(textToBinary('(module (export "a" memory))')), TypeError, /exported memory index out of bounds/);
+assertErrorMessage(() => new Module(textToBinary('(module (export "a" table))')), TypeError, /exported table index out of bounds/);
+
 // Default memory rules
 
 assertErrorMessage(() => new Module(textToBinary('(module (import "a" "b" (memory 1 1)) (memory 1 1))')), TypeError, /already have default memory/);
 assertErrorMessage(() => new Module(textToBinary('(module (import "a" "b" (memory 1 1)) (import "x" "y" (memory 2 2)))')), TypeError, /already have default memory/);
 
 // Data segments on imports
 
 var m = new Module(textToBinary(`
--- a/js/src/jit-test/tests/wasm/jsapi.js
+++ b/js/src/jit-test/tests/wasm/jsapi.js
@@ -190,8 +190,23 @@ assertEq(tableProto, tableProtoDesc.valu
 assertEq(String(tableProto), "[object Object]");
 assertEq(Object.getPrototypeOf(tableProto), Object.prototype);
 
 // 'WebAssembly.Table' instance objects
 const tbl1 = new Table({initial:1});
 assertEq(typeof tbl1, "object");
 assertEq(String(tbl1), "[object WebAssembly.Table]");
 assertEq(Object.getPrototypeOf(tbl1), tableProto);
+
+// 'WebAssembly.Table.prototype.length' accessor property
+const lengthDesc = Object.getOwnPropertyDescriptor(tableProto, 'length');
+assertEq(typeof lengthDesc.get, "function");
+assertEq(lengthDesc.set, undefined);
+assertEq(lengthDesc.enumerable, false);
+assertEq(lengthDesc.configurable, true);
+
+// 'WebAssembly.Table.prototype.length' getter
+const lengthGetter = lengthDesc.get;
+assertErrorMessage(() => lengthGetter.call(), TypeError, /called on incompatible undefined/);
+assertErrorMessage(() => lengthGetter.call({}), TypeError, /called on incompatible Object/);
+assertEq(typeof lengthGetter.call(tbl1), "number");
+assertEq(lengthGetter.call(tbl1), 1);
+
--- a/layout/base/DisplayListClipState.cpp
+++ b/layout/base/DisplayListClipState.cpp
@@ -31,29 +31,48 @@ DisplayListClipState::GetCurrentCombined
   } else {
     mCurrentCombinedClip =
       aBuilder->AllocateDisplayItemClip(*mClipContainingBlockDescendants);
   }
   return mCurrentCombinedClip;
 }
 
 void
+DisplayListClipState::SetScrollClipForContainingBlockDescendants(
+    nsDisplayListBuilder* aBuilder,
+    const DisplayItemScrollClip* aScrollClip)
+{
+  if (aBuilder->IsPaintingToWindow() &&
+      mClipContentDescendants &&
+      aScrollClip != mScrollClipContainingBlockDescendants &&
+      !DisplayItemScrollClip::IsAncestor(mClipContentDescendantsScrollClip, aScrollClip)) {
+    if (mClipContentDescendantsScrollClip && mClipContentDescendantsScrollClip->mScrollableFrame) {
+      mClipContentDescendantsScrollClip->mScrollableFrame->SetScrollsClipOnUnscrolledOutOfFlow();
+    }
+    mClipContentDescendantsScrollClip = nullptr;
+  }
+  mScrollClipContainingBlockDescendants = aScrollClip;
+  mStackingContextAncestorSC = DisplayItemScrollClip::PickAncestor(mStackingContextAncestorSC, aScrollClip);
+}
+
+void
 DisplayListClipState::ClipContainingBlockDescendants(const nsRect& aRect,
                                                      const nscoord* aRadii,
                                                      DisplayItemClip& aClipOnStack)
 {
   if (aRadii) {
     aClipOnStack.SetTo(aRect, aRadii);
   } else {
     aClipOnStack.SetTo(aRect);
   }
   if (mClipContainingBlockDescendants) {
     aClipOnStack.IntersectWith(*mClipContainingBlockDescendants);
   }
   mClipContainingBlockDescendants = &aClipOnStack;
+  mClipContentDescendantsScrollClip = GetCurrentInnermostScrollClip();
   mCurrentCombinedClip = nullptr;
 }
 
 void
 DisplayListClipState::ClipContentDescendants(const nsRect& aRect,
                                              const nscoord* aRadii,
                                              DisplayItemClip& aClipOnStack)
 {
@@ -192,11 +211,13 @@ DisplayListClipState::InsertInactiveScro
 DisplayListClipState::AutoSaveRestore::AutoSaveRestore(nsDisplayListBuilder* aBuilder)
   : mState(aBuilder->ClipState())
   , mSavedState(aBuilder->ClipState())
 #ifdef DEBUG
   , mClipUsed(false)
   , mRestored(false)
 #endif
   , mClearedForStackingContextContents(false)
-{}
+{
+  mState.mStackingContextAncestorSC = mState.GetCurrentInnermostScrollClip();
+}
 
 } // namespace mozilla
--- a/layout/base/DisplayListClipState.h
+++ b/layout/base/DisplayListClipState.h
@@ -24,16 +24,17 @@ namespace mozilla {
 class DisplayListClipState {
 public:
   DisplayListClipState()
     : mClipContentDescendants(nullptr)
     , mClipContainingBlockDescendants(nullptr)
     , mCurrentCombinedClip(nullptr)
     , mScrollClipContentDescendants(nullptr)
     , mScrollClipContainingBlockDescendants(nullptr)
+    , mClipContentDescendantsScrollClip(nullptr)
     , mStackingContextAncestorSC(nullptr)
   {}
 
   /**
    * Returns intersection of mClipContainingBlockDescendants and
    * mClipContentDescendants, allocated on aBuilder's arena.
    */
   const DisplayItemClip* GetCurrentCombinedClip(nsDisplayListBuilder* aBuilder);
@@ -69,21 +70,18 @@ public:
 
 private:
   void SetClipForContainingBlockDescendants(const DisplayItemClip* aClip)
   {
     mClipContainingBlockDescendants = aClip;
     mCurrentCombinedClip = nullptr;
   }
 
-  void SetScrollClipForContainingBlockDescendants(const DisplayItemScrollClip* aScrollClip)
-  {
-    mScrollClipContainingBlockDescendants = aScrollClip;
-    mStackingContextAncestorSC = DisplayItemScrollClip::PickAncestor(mStackingContextAncestorSC, aScrollClip);
-  }
+  void SetScrollClipForContainingBlockDescendants(nsDisplayListBuilder* aBuilder,
+                                                  const DisplayItemScrollClip* aScrollClip);
 
   void Clear()
   {
     mClipContentDescendants = nullptr;
     mClipContainingBlockDescendants = nullptr;
     mCurrentCombinedClip = nullptr;
     // We do not clear scroll clips.
   }
@@ -179,16 +177,21 @@ private:
 
   /**
    * The same for scroll clips.
    */
   const DisplayItemScrollClip* mScrollClipContentDescendants;
   const DisplayItemScrollClip* mScrollClipContainingBlockDescendants;
 
   /**
+   * The scroll clip that was in effect when mClipContentDescendants was set.
+   */
+  const DisplayItemScrollClip* mClipContentDescendantsScrollClip;
+
+  /**
    * A scroll clip that is an ancestor of all the scroll clips that were
    * "current" on this clip state since EnterStackingContextContents was
    * called.
    */
   const DisplayItemScrollClip* mStackingContextAncestorSC;
 };
 
 /**
@@ -426,19 +429,20 @@ public:
   /**
    * *aClip must survive longer than this object. Be careful!!!
    */
   void SetClipForContainingBlockDescendants(const DisplayItemClip* aClip)
   {
     mState.SetClipForContainingBlockDescendants(aClip);
   }
 
-  void SetScrollClipForContainingBlockDescendants(const DisplayItemScrollClip* aScrollClip)
+  void SetScrollClipForContainingBlockDescendants(nsDisplayListBuilder* aBuilder,
+                                                  const DisplayItemScrollClip* aScrollClip)
   {
-    mState.SetScrollClipForContainingBlockDescendants(aScrollClip);
+    mState.SetScrollClipForContainingBlockDescendants(aBuilder, aScrollClip);
   }
 
   /**
    * Intersects the given clip rect (with optional aRadii) with the current
    * mClipContainingBlockDescendants and sets mClipContainingBlockDescendants to
    * the result, stored in aClipOnStack.
    */
   void ClipContainingBlockDescendantsExtra(const nsRect& aRect,
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -2594,17 +2594,18 @@ nsIFrame::BuildDisplayListForStackingCon
 
   CreateOwnLayerIfNeeded(aBuilder, &resultList);
 
   aList->AppendToTop(&resultList);
 }
 
 static nsDisplayItem*
 WrapInWrapList(nsDisplayListBuilder* aBuilder,
-               nsIFrame* aFrame, nsDisplayList* aList)
+               nsIFrame* aFrame, nsDisplayList* aList,
+               const DisplayItemScrollClip* aScrollClip)
 {
   nsDisplayItem* item = aList->GetBottom();
   if (!item) {
     return nullptr;
   }
 
   // For perspective items we want to treat the 'frame' as being the transform
   // frame that created it. This stops the transform frame from wrapping another
@@ -2612,17 +2613,17 @@ WrapInWrapList(nsDisplayListBuilder* aBu
   // makes the perspective frame create one (so we have an atomic entry for z-index
   // sorting).
   nsIFrame *itemFrame = item->Frame();
   if (item->GetType() == nsDisplayItem::TYPE_PERSPECTIVE) {
     itemFrame = static_cast<nsDisplayPerspective*>(item)->TransformFrame();
   }
 
   if (item->GetAbove() || itemFrame != aFrame) {
-    return new (aBuilder) nsDisplayWrapList(aBuilder, aFrame, aList);
+    return new (aBuilder) nsDisplayWrapList(aBuilder, aFrame, aList, aScrollClip);
   }
   aList->RemoveBottom();
   return item;
 }
 
 void
 nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder*   aBuilder,
                                    nsIFrame*               aChild,
@@ -2798,32 +2799,32 @@ nsIFrame::BuildDisplayListForChild(nsDis
   DisplayListClipState::AutoClipMultiple clipState(aBuilder);
   CheckForApzAwareEventHandlers(aBuilder, child);
 
   if (savedOutOfFlowData) {
     aBuilder->SetBuildingInvisibleItems(false);
 
     clipState.SetClipForContainingBlockDescendants(
       &savedOutOfFlowData->mContainingBlockClip);
-    clipState.SetScrollClipForContainingBlockDescendants(
+    clipState.SetScrollClipForContainingBlockDescendants(aBuilder,
       savedOutOfFlowData->mContainingBlockScrollClip);
   } else if (GetStateBits() & NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO &&
              isPlaceholder) {
     NS_ASSERTION(dirty.IsEmpty(), "should have empty dirty rect");
     // Every item we build from now until we descent into an out of flow that
     // does have saved out of flow data should be invisible. This state gets
     // restored when AutoBuildingDisplayList gets out of scope.
     aBuilder->SetBuildingInvisibleItems(true);
 
     // If we have nested out-of-flow frames and the outer one isn't visible
     // then we won't have stored clip data for it. We can just clear the clip
     // instead since we know we won't render anything, and the inner out-of-flow
     // frame will setup the correct clip for itself.
     clipState.SetClipForContainingBlockDescendants(nullptr);
-    clipState.SetScrollClipForContainingBlockDescendants(nullptr);
+    clipState.SetScrollClipForContainingBlockDescendants(aBuilder, nullptr);
   }
 
   // Setup clipping for the parent's overflow:-moz-hidden-unscrollable,
   // or overflow:hidden on elements that don't support scrolling (and therefore
   // don't create nsHTML/XULScrollFrame). This clipping needs to not clip
   // anything directly rendered by the parent, only the rendering of its
   // children.
   // Don't use overflowClip to restrict the dirty rect, since some of the
@@ -2903,36 +2904,39 @@ nsIFrame::BuildDisplayListForChild(nsDis
     list.AppendToTop(pseudoStack.Outlines());
     extraPositionedDescendants.AppendToTop(pseudoStack.PositionedDescendants());
 #ifdef DEBUG
     DisplayDebugBorders(aBuilder, child, aLists);
 #endif
   }
 
   buildingForChild.RestoreBuildingInvisibleItemsValue();
-
+ 
   // Clear clip rect for the construction of the items below. Since we're
   // clipping all their contents, they themselves don't need to be clipped.
   clipState.Clear();
 
+  const DisplayItemScrollClip* containerItemScrollClip =
+    aBuilder->ClipState().CurrentAncestorScrollClipForStackingContextContents();
+
   if (isPositioned || isVisuallyAtomic ||
       (aFlags & DISPLAY_CHILD_FORCE_STACKING_CONTEXT)) {
     // Genuine stacking contexts, and positioned pseudo-stacking-contexts,
     // go in this level.
     if (!list.IsEmpty()) {
-      nsDisplayItem* item = WrapInWrapList(aBuilder, child, &list);
+      nsDisplayItem* item = WrapInWrapList(aBuilder, child, &list, containerItemScrollClip);
       if (isSVG) {
         aLists.Content()->AppendNewToTop(item);
       } else {
         aLists.PositionedDescendants()->AppendNewToTop(item);
       }
     }
   } else if (!isSVG && disp->IsFloating(child)) {
     if (!list.IsEmpty()) {
-      aLists.Floats()->AppendNewToTop(WrapInWrapList(aBuilder, child, &list));
+      aLists.Floats()->AppendNewToTop(WrapInWrapList(aBuilder, child, &list, containerItemScrollClip));
     }
   } else {
     aLists.Content()->AppendToTop(&list);
   }
   // We delay placing the positioned descendants of positioned frames to here,
   // because in the absence of z-index this is the correct order for them.
   // This doesn't affect correctness because the positioned descendants list
   // is sorted by z-order and content in BuildDisplayListForStackingContext,
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -1886,16 +1886,17 @@ ScrollFrameHelper::ScrollFrameHelper(nsC
   , mWillBuildScrollableLayer(false)
   , mIsScrollParent(false)
   , mIsScrollableLayerInRootContainer(false)
   , mHasBeenScrolled(false)
   , mIgnoreMomentumScroll(false)
   , mTransformingByAPZ(false)
   , mScrollableByAPZ(false)
   , mZoomableByAPZ(false)
+  , mScrollsClipOnUnscrolledOutOfFlow(false)
   , mVelocityQueue(aOuter->PresContext())
   , mAsyncScrollEvent(END_DOM)
 {
   if (LookAndFeel::GetInt(LookAndFeel::eIntID_UseOverlayScrollbars) != 0) {
     mScrollbarActivity = new ScrollbarActivity(do_QueryFrame(aOuter));
   }
 
   EnsureFrameVisPrefsCached();
@@ -2114,16 +2115,22 @@ ScrollFrameHelper::HasPluginFrames()
 bool
 ScrollFrameHelper::HasPerspective() const
 {
   const nsStyleDisplay* disp = mOuter->StyleDisplay();
   return disp->mChildPerspective.GetUnit() != eStyleUnit_None;
 }
 
 void
+ScrollFrameHelper::SetScrollsClipOnUnscrolledOutOfFlow()
+{
+  mScrollsClipOnUnscrolledOutOfFlow = true;
+}
+
+void
 ScrollFrameHelper::ScrollToCSSPixels(const CSSIntPoint& aScrollPosition,
                                      nsIScrollableFrame::ScrollMode aMode)
 {
   nsPoint current = GetScrollPosition();
   CSSIntPoint currentCSSPixels = GetScrollPositionCSSPixels();
   nsPoint pt = CSSPoint::ToAppUnits(aScrollPosition);
   nscoord halfPixel = nsPresContext::CSSPixelsToAppUnits(0.5f);
   nsRect range(pt.x - halfPixel, pt.y - halfPixel, 2*halfPixel - 1, 2*halfPixel - 1);
@@ -2750,21 +2757,21 @@ ScrollFrameHelper::ScrollToImpl(nsPoint 
     // that needs to be painted. So even if the final tile-aligned displayport
     // is the same, we force a repaint for these elements. Bug 1254260 tracks
     // fixing this properly.
     nsRect displayPort;
     bool usingDisplayPort =
       nsLayoutUtils::GetHighResolutionDisplayPort(content, &displayPort);
     displayPort.MoveBy(-mScrolledFrame->GetPosition());
 
-    PAINT_SKIP_LOG("New scrollpos %s usingDP %d dpEqual %d scrollableByApz %d plugins %d perspective %d\n",
+    PAINT_SKIP_LOG("New scrollpos %s usingDP %d dpEqual %d scrollableByApz %d plugins %d perspective %d clip %d\n",
         Stringify(CSSPoint::FromAppUnits(GetScrollPosition())).c_str(),
         usingDisplayPort, displayPort.IsEqualEdges(oldDisplayPort),
-        mScrollableByAPZ, HasPluginFrames(), HasPerspective());
-    if (usingDisplayPort && displayPort.IsEqualEdges(oldDisplayPort) && !HasPerspective()) {
+        mScrollableByAPZ, HasPluginFrames(), HasPerspective(), mScrollsClipOnUnscrolledOutOfFlow);
+    if (usingDisplayPort && displayPort.IsEqualEdges(oldDisplayPort) && !HasPerspective() && !mScrollsClipOnUnscrolledOutOfFlow) {
       bool haveScrollLinkedEffects = content->GetComposedDoc()->HasScrollLinkedEffect();
       bool apzDisabled = haveScrollLinkedEffects && gfxPrefs::APZDisableForScrollLinkedEffects();
       if (!apzDisabled) {
         if (LastScrollOrigin() == nsGkAtoms::apz) {
           schedulePaint = false;
           PAINT_SKIP_LOG("Skipping due to APZ scroll\n");
         } else if (mScrollableByAPZ && !HasPluginFrames()) {
           nsIWidget* widget = presContext->GetNearestWidget();
--- a/layout/generic/nsGfxScrollFrame.h
+++ b/layout/generic/nsGfxScrollFrame.h
@@ -381,16 +381,17 @@ public:
     }
     NotifyPluginFrames(aTransforming ? BEGIN_APZ : END_APZ);
   }
   bool IsTransformingByAPZ() const {
     return mTransformingByAPZ;
   }
   void SetScrollableByAPZ(bool aScrollable);
   void SetZoomableByAPZ(bool aZoomable);
+  void SetScrollsClipOnUnscrolledOutOfFlow();
 
   bool UsesContainerScrolling() const;
 
   ScrollSnapInfo GetScrollSnapInfo() const;
 
   bool DecideScrollableLayer(nsDisplayListBuilder* aBuilder,
                              nsRect* aDirtyRect,
                              bool aAllowCreateDisplayPort);
@@ -576,16 +577,18 @@ public:
   bool mScrollableByAPZ:1;
 
   // True if the APZ is allowed to zoom this scrollframe.
   bool mZoomableByAPZ:1;
 
   // True if we don't want the scrollbar to repaint itself right now.
   bool mSuppressScrollbarRepaints:1;
 
+  bool mScrollsClipOnUnscrolledOutOfFlow:1;
+
   mozilla::layout::ScrollVelocityQueue mVelocityQueue;
 
 protected:
   class AutoScrollbarRepaintSuppression;
   friend class AutoScrollbarRepaintSuppression;
   class AutoScrollbarRepaintSuppression {
   public:
     AutoScrollbarRepaintSuppression(ScrollFrameHelper* aHelper, bool aSuppress)
@@ -1001,16 +1004,19 @@ public:
     return mHelper.IsTransformingByAPZ();
   }
   void SetScrollableByAPZ(bool aScrollable) override {
     mHelper.SetScrollableByAPZ(aScrollable);
   }
   void SetZoomableByAPZ(bool aZoomable) override {
     mHelper.SetZoomableByAPZ(aZoomable);
   }
+  void SetScrollsClipOnUnscrolledOutOfFlow() override {
+    mHelper.SetScrollsClipOnUnscrolledOutOfFlow();
+  }
   
   ScrollSnapInfo GetScrollSnapInfo() const override {
     return mHelper.GetScrollSnapInfo();
   }
 
 #ifdef DEBUG_FRAME_DUMP
   virtual nsresult GetFrameName(nsAString& aResult) const override;
 #endif
@@ -1404,16 +1410,19 @@ public:
     return mHelper.IsTransformingByAPZ();
   }
   void SetScrollableByAPZ(bool aScrollable) override {
     mHelper.SetScrollableByAPZ(aScrollable);
   }
   void SetZoomableByAPZ(bool aZoomable) override {
     mHelper.SetZoomableByAPZ(aZoomable);
   }
+  void SetScrollsClipOnUnscrolledOutOfFlow() override {
+    mHelper.SetScrollsClipOnUnscrolledOutOfFlow();
+  }
   virtual bool DecideScrollableLayer(nsDisplayListBuilder* aBuilder,
                                      nsRect* aDirtyRect,
                                      bool aAllowCreateDisplayPort) override {
     return mHelper.DecideScrollableLayer(aBuilder, aDirtyRect, aAllowCreateDisplayPort);
   }
   virtual void NotifyApproximateFrameVisibilityUpdate() override {
     mHelper.NotifyApproximateFrameVisibilityUpdate();
   }
--- a/layout/generic/nsIScrollableFrame.h
+++ b/layout/generic/nsIScrollableFrame.h
@@ -459,11 +459,13 @@ public:
    * own displayport and schedule a timer to do that if it is safe.
    */
   virtual void TriggerDisplayPortExpiration() = 0;
 
   /**
    * Returns information required to determine where to snap to after a scroll.
    */
   virtual ScrollSnapInfo GetScrollSnapInfo() const = 0;
+
+  virtual void SetScrollsClipOnUnscrolledOutOfFlow() = 0;
 };
 
 #endif
--- a/layout/generic/nsViewportFrame.cpp
+++ b/layout/generic/nsViewportFrame.cpp
@@ -102,17 +102,17 @@ BuildDisplayListForTopLayerFrame(nsDispl
   DisplayListClipState::AutoClipMultiple clipState(aBuilder);
   nsDisplayListBuilder::OutOfFlowDisplayData*
     savedOutOfFlowData = nsDisplayListBuilder::GetOutOfFlowData(aFrame);
   if (savedOutOfFlowData) {
     dirty = savedOutOfFlowData->mDirtyRect;
     clipState.SetClipForContainingBlockDescendants(
       &savedOutOfFlowData->mContainingBlockClip);
     clipState.SetScrollClipForContainingBlockDescendants(
-      savedOutOfFlowData->mContainingBlockScrollClip);
+      aBuilder, savedOutOfFlowData->mContainingBlockScrollClip);
   }
   nsDisplayList list;
   aFrame->BuildDisplayListForStackingContext(aBuilder, dirty, &list);
   aList->AppendToTop(&list);
 }
 
 void
 ViewportFrame::BuildDisplayListForTopLayer(nsDisplayListBuilder* aBuilder,
--- a/layout/printing/crashtests/crashtests.list
+++ b/layout/printing/crashtests/crashtests.list
@@ -1,4 +1,4 @@
 load 509839-1.html
 load 509839-2.html
-load 576878.xhtml
+asserts-if(browserIsRemote,4) load 576878.xhtml
 load 793844.html
--- a/layout/reftests/css-parsing/invalid-url-handling.xhtml
+++ b/layout/reftests/css-parsing/invalid-url-handling.xhtml
@@ -17,27 +17,29 @@
   </style>
   <style type="text/css">
   /* not a URI token, but handled according to rules for parsing errors */
   #foo { background: url(foo"bar
   ) }
   #two { background-color: green; }
   </style>
   <style type="text/css">
-  /* not a URI token; the unterminated string ends at end of line, so
-     the brace never matches */
+  /* not a URI token, the invalid URI token consumes everything up to the ')'. */
+  #three { background-color: red; }
+  #foo { background: url(foo"bar) }
   #three { background-color: green; }
-  #foo { background: url(foo"bar) }
-  #three { background-color: red; }
   </style>
   <style type="text/css">
-  /* not a URI token; the unterminated string ends at end of line */
+  #four { background-color: green; }
+  /* not a URI token; the invalid URI token consumes everything up to the ')'
+     and then there is some garbage that prevents the next rule from being
+     parsed. */
   #foo { background: url(foo"bar) }
   ) }
-  #four { background-color: green; }
+  #four { background-color: red; }
   </style>
   <style type="text/css">
   /* not a URI token; the unterminated string ends at end of line, so
      the brace never matches */
   #five { background-color: green; }
   #foo { background: url("bar) }
   #five { background-color: red; }
   </style>
@@ -63,28 +65,30 @@
   /* perfectly good URI token (image is a 404, though) */
   #ten { background: url({) green; }
   </style>
   <style type="text/css">
   /* perfectly good URI token (image is a 404, though) */
   #eleven { background: url([) green; }
   </style>
   <style type="text/css">
-  /* not a URI token; brace matching should work only after invalid URI token */
-  #twelve { background: url(}{""{)}); background-color: green; }
+  /* not a URI token; brace matching is ignored while looking for the closing
+     ')' but is used after that. */
+  #twelve { background: url(}{""{)(}); background-color: green; }
   </style>
   <style type="text/css">
   /* invalid URI token absorbs the [ */
   #thirteen { background: url([""); background-color: green; }
   </style>
   <style type="text/css">
-  /* not a URI token; the opening ( is never matched */
+  /* not a URI token; the invalid URI token consumes everything up to the
+     next ')'. */
+  #fourteen { background-color: red; }
+  #foo { background: url(() }
   #fourteen { background-color: green; }
-  #foo { background: url(() }
-  #fourteen { background-color: red; }
   </style>
   <!-- The next three tests test that invalid URI tokens absorb [ and { -->
   <style type="text/css">
   #foo { background: url(a()); }
   #fifteen { background-color: green }
   </style>
   <style type="text/css">
   #foo { background: url([()); }
--- a/layout/style/nsCSSScanner.cpp
+++ b/layout/style/nsCSSScanner.cpp
@@ -255,23 +255,35 @@ nsCSSToken::AppendToString(nsString& aBu
       nsStyleUtil::AppendEscapedCSSIdent(mIdent, aBuffer);
       aBuffer.Append('(');
       break;
 
     case eCSSToken_URL:
     case eCSSToken_Bad_URL:
       aBuffer.AppendLiteral("url(");
       if (mSymbol != char16_t(0)) {
-        nsStyleUtil::AppendEscapedCSSString(mIdent, aBuffer, mSymbol);
+        if (mType == eCSSToken_URL) {
+          nsStyleUtil::AppendEscapedCSSString(mIdent, aBuffer, mSymbol);
+        } else {
+          // Only things up to mInteger were part of the string.
+          nsStyleUtil::AppendEscapedCSSString(StringHead(mIdent, mInteger),
+                                              aBuffer, mSymbol);
+          MOZ_ASSERT(mInteger2 == 0 || mInteger2 == 1);
+          if (mInteger2 == 1) {
+            // This was a Bad_String; strip off the closing quote.
+            aBuffer.Truncate(aBuffer.Length() - 1);
+          }
+
+          // Now append the remaining garbage.
+          aBuffer.Append(Substring(mIdent, mInteger));
+        }
       } else {
         aBuffer.Append(mIdent);
       }
-      if (mType == eCSSToken_URL) {
-        aBuffer.Append(char16_t(')'));
-      }
+      aBuffer.Append(char16_t(')'));
       break;
 
     case eCSSToken_Number:
       if (mIntegerValid) {
         aBuffer.AppendInt(mInteger, 10);
       } else {
         aBuffer.AppendFloat(mNumber);
       }
@@ -1161,16 +1173,19 @@ nsCSSScanner::NextURL(nsCSSToken& aToken
   aToken.mIdent.Truncate();
 
   int32_t ch = Peek();
   // Do we have a string?
   if (ch == '"' || ch == '\'') {
     ScanString(aToken);
     if (MOZ_UNLIKELY(aToken.mType == eCSSToken_Bad_String)) {
       aToken.mType = eCSSToken_Bad_URL;
+      // Flag us as having been a Bad_String.
+      aToken.mInteger2 = 1;
+      ConsumeBadURLRemnants(aToken);
       return;
     }
     MOZ_ASSERT(aToken.mType == eCSSToken_String, "unexpected token type");
 
   } else {
     // Otherwise, this is the start of a non-quoted url (which may be empty).
     aToken.mSymbol = char16_t(0);
     GatherText(IS_URL_CHAR, aToken.mIdent);
@@ -1184,19 +1199,56 @@ nsCSSScanner::NextURL(nsCSSToken& aToken
     Advance();
     aToken.mType = eCSSToken_URL;
     if (ch < 0) {
       AddEOFCharacters(eEOFCharacters_CloseParen);
     }
   } else {
     mSeenBadToken = true;
     aToken.mType = eCSSToken_Bad_URL;
+    if (aToken.mSymbol != 0) {
+      // Flag us as having been a String, not a Bad_String.
+      aToken.mInteger2 = 0;
+    }
+    ConsumeBadURLRemnants(aToken);
   }
 }
 
+void
+nsCSSScanner::ConsumeBadURLRemnants(nsCSSToken& aToken)
+{
+  aToken.mInteger = aToken.mIdent.Length();
+  int32_t ch = Peek();
+  do {
+    if (ch < 0) {
+      AddEOFCharacters(eEOFCharacters_CloseParen);
+      break;
+    }
+
+    if (ch == '\\' && GatherEscape(aToken.mIdent, false)) {
+      // Nothing else needs to be done here for the moment; we've consumed the
+      // backslash and following escape.
+    } else {
+      // We always want to consume this character.
+      if (IsVertSpace(ch)) {
+        AdvanceLine();
+      } else {
+        Advance();
+      }
+      if (ch == 0) {
+        aToken.mIdent.Append(UCS2_REPLACEMENT_CHAR);
+      } else {
+        aToken.mIdent.Append(ch);
+      }
+    }
+
+    ch = Peek();
+  } while (ch != ')');
+}
+
 /**
  * Primary scanner entry point.  Consume one token and fill in
  * |aToken| accordingly.  Will skip over any number of comments first,
  * and will also skip over rather than return whitespace and comment
  * tokens, depending on the value of |aSkip|.
  *
  * Returns true if it successfully consumed a token, false if EOF has
  * been reached.  Will always advance the current read position by at
--- a/layout/style/nsCSSScanner.h
+++ b/layout/style/nsCSSScanner.h
@@ -54,21 +54,30 @@ enum nsCSSTokenType {
   eCSSToken_Number,         // 1 -5 +2e3 3.14159 7.297352e-3
   eCSSToken_Dimension,      // 24px 8.5in
   eCSSToken_Percentage,     // 85% 1280.4%
 
   // String-like tokens.  In all cases, mIdent holds the text
   // belonging to the string, and mSymbol holds the delimiter
   // character, which may be ', ", or zero (only for unquoted URLs).
   // Bad_String and Bad_URL tokens are emitted when the closing
-  // delimiter or parenthesis was missing.
+  // delimiter was missing.  Bad_URL is also emitted if there was trailing
+  // garbage after the string or unquoted url value.
   eCSSToken_String,         // 'foo bar' "foo bar"
   eCSSToken_Bad_String,     // 'foo bar
   eCSSToken_URL,            // url(foobar) url("foo bar")
-  eCSSToken_Bad_URL,        // url(foo
+  // For Bad_URL tokens, we need to keep track of the following state:
+  // (1) Was there a quoted string?  If so, was it a String or Bad_String?
+  // (2) Was there trailing garbage, and if so what was it?
+  // We keep track of whether there was a quoted string by setting mSymbol as
+  // described above.  If that's nonzero, then mInteger2 indicates whether we
+  // have a String or Bad_String by taking on the values 0 and 1 respectively.
+  // mInteger indicates the start of trailing garbage in mIdent (and is set to
+  // mIdent.Length() when there is no trailing garbage).
+  eCSSToken_Bad_URL,        // url(foo') url('foo'a) url('foo
 
   // Any one-character symbol.  mSymbol holds the character.
   eCSSToken_Symbol,         // . ; { } ! *
 
   // Match operators.  These are single tokens rather than pairs of
   // Symbol tokens because css3-selectors forbids the presence of
   // comments between the two characters.  No value fields are used;
   // the token type indicates which operator.
@@ -250,16 +259,20 @@ class nsCSSScanner {
 
   // Get the body of an URL token (everything after the 'url(').
   // This is exposed for use by nsCSSParser::ParseMozDocumentRule,
   // which, for historical reasons, must make additional function
   // tokens behave like url().  Please do not add new uses to the
   // parser.
   void NextURL(nsCSSToken& aTokenResult);
 
+  // Implement the "consume the remnants of a bad url" algorithm from CSS3
+  // Syntax, except we don't consume the ')'.
+  void ConsumeBadURLRemnants(nsCSSToken& aToken);
+
   // This is exposed for use by nsCSSParser::ParsePseudoClassWithNthPairArg,
   // because "2n-1" is a single DIMENSION token, and "n-1" is a single
   // IDENT token, but the :nth() selector syntax wants to interpret
   // them the same as "2n -1" and "n -1" respectively.  Please do not
   // add new uses to the parser.
   //
   // Note: this function may not be used to back up over a line boundary.
   void Backup(uint32_t n);
--- a/layout/style/test/test_csslexer.js
+++ b/layout/style/test/test_csslexer.js
@@ -50,18 +50,19 @@ var LEX_TESTS = [
   ["23px", ["dimension:px"]],
   ["23%", ["percentage"]],
   ["url(http://example.com)", ["url:http://example.com"]],
   ["url('http://example.com')", ["url:http://example.com"]],
   ["url(  'http://example.com'  )",
              ["url:http://example.com"]],
   // In CSS Level 3, this is an ordinary URL, not a BAD_URL.
   ["url(http://example.com", ["url:http://example.com"]],
-  // See bug 1153981 to understand why this gets a SYMBOL token.
-  ["url(http://example.com @", ["bad_url:http://example.com", "symbol:@"]],
+  // We end up losing the whitespace before the '@' because it's skipped by the
+  // lexer before we discover we have a BAD_URL token.
+  ["url(http://example.com @", ["bad_url:http://example.com@"]],
   ["quo\\ting", ["ident:quoting"]],
   ["'bad string\n", ["bad_string:bad string", "whitespace"]],
   ["~=", ["includes"]],
   ["|=", ["dashmatch"]],
   ["^=", ["beginsmatch"]],
   ["$=", ["endsmatch"]],
   ["*=", ["containsmatch"]],
 
--- a/layout/style/test/test_parser_diagnostics_unprintables.html
+++ b/layout/style/test/test_parser_diagnostics_unprintables.html
@@ -40,17 +40,17 @@ const patterns = [
   { i: "x{@<t>: }",        o: "declaration but found \u2018@<i>\u2019." },
   // _String
   { i: "x{ '<t>'}" ,       o: "declaration but found \u2018'<s>'\u2019." },
   // _Bad_String
   { i: "x{ '<t>\n}",      o: "declaration but found \u2018'<s>\u2019." },
   // _URL
   { i: "x{ url('<t>')}",   o: "declaration but found \u2018url('<s>')\u2019." },
   // _Bad_URL
-  { i: "x{ url('<t>'.)}" , o: "declaration but found \u2018url('<s>'\u2019." }
+  { i: "x{ url('<t>'.)}" , o: "declaration but found \u2018url('<s>'.)\u2019." }
 ];
 
 // Blocks of characters to test, and how they should be escaped when
 // they appear in identifiers and string constants.
 const substitutions = [
   // ASCII printables that _can_ normally appear in identifiers,
   // so should of course _not_ be escaped.
   { t: "-_0123456789",               i: "-_0123456789",
--- a/media/webrtc/trunk/webrtc/modules/audio_processing/utility/delay_estimator.c
+++ b/media/webrtc/trunk/webrtc/modules/audio_processing/utility/delay_estimator.c
@@ -610,26 +610,43 @@ int WebRtc_ProcessBinarySpectrum(BinaryD
   //  2) The depth of the valley is deep enough
   //      (|value_best_candidate| < |minimum_probability|)
   //     and deeper than the best estimate so far
   //      (|value_best_candidate| < |last_delay_probability|)
   valid_candidate = ((valley_depth > kProbabilityOffset) &&
       ((value_best_candidate < self->minimum_probability) ||
           (value_best_candidate < self->last_delay_probability)));
 
+  // Check for nonstationary farend signal.
+  int non_stationary_farend = 0;
+  for (i = 0; i < self->history_size; ++i) {
+    if (self->farend->far_bit_counts[i] > 0) {
+      non_stationary_farend = 1;
+      break;
+    }
+  }
+
+  if (non_stationary_farend) {
+    // Only update the validation statistics when the farend is nonstationary
+    // as the underlying estimates are otherwise frozen.
+    UpdateRobustValidationStatistics(self, candidate_delay, valley_depth,
+                                     value_best_candidate);
+  }
+
   if (self->robust_validation_enabled) {
     int is_histogram_valid = 0;
-    UpdateRobustValidationStatistics(self, candidate_delay, valley_depth,
-                                     value_best_candidate);
     is_histogram_valid = HistogramBasedValidation(self, candidate_delay);
     valid_candidate = RobustValidation(self, candidate_delay, valid_candidate,
                                        is_histogram_valid);
 
   }
-  if (valid_candidate) {
+
+  // Only update the delay estimate when the farend is nonstationary and when
+  // a valid delay candidate is available.
+  if (non_stationary_farend && valid_candidate) {
     if (candidate_delay != self->last_delay) {
       self->last_delay_histogram =
           (self->histogram[candidate_delay] > kLastHistogramMax ?
               kLastHistogramMax : self->histogram[candidate_delay]);
       // Adjust the histogram if we made a change to |last_delay|, though it was
       // not the most likely one according to the histogram.
       if (self->histogram[candidate_delay] <
           self->histogram[self->compare_delay]) {
--- a/memory/replace/dmd/DMD.cpp
+++ b/memory/replace/dmd/DMD.cpp
@@ -1059,19 +1059,17 @@ public:
     , mSlopSize(0)
     , mAllocStackTrace(nullptr)
   {}
 
   explicit DeadBlock(const LiveBlock& aLb)
     : mReqSize(aLb.ReqSize())
     , mSlopSize(aLb.SlopSize())
     , mAllocStackTrace(aLb.AllocStackTrace())
-  {
-    MOZ_ASSERT(AllocStackTrace());
-  }
+  {}
 
   ~DeadBlock() {}
 
   size_t ReqSize()    const { return mReqSize; }
   size_t SlopSize()   const { return mSlopSize; }
 
   const StackTrace* AllocStackTrace() const
   {
@@ -1826,16 +1824,25 @@ WriteBlockContents(JSONWriter& aWriter, 
     }
   }
   aWriter.EndArray();
 }
 
 static void
 AnalyzeImpl(UniquePtr<JSONWriteFunc> aWriter)
 {
+  // Some blocks may have been allocated while creating |aWriter|. Those blocks
+  // will be freed at the end of this function when |write| is destroyed. The
+  // allocations will have occurred while intercepts were not blocked, so the
+  // frees better be as well, otherwise we'll get assertion failures.
+  // Therefore, this declaration must precede the AutoBlockIntercepts
+  // declaration, to ensure that |write| is destroyed *after* intercepts are
+  // unblocked.
+  JSONWriter writer(Move(aWriter));
+
   AutoBlockIntercepts block(Thread::Fetch());
   AutoLockState lock;
 
   // Allocate this on the heap instead of the stack because it's fairly large.
   auto locService = InfallibleAllocPolicy::new_<CodeAddressService>();
 
   StackTraceSet usedStackTraces;
   MOZ_ALWAYS_TRUE(usedStackTraces.init(512));
@@ -1843,17 +1850,16 @@ AnalyzeImpl(UniquePtr<JSONWriteFunc> aWr
   PointerSet usedPcs;
   MOZ_ALWAYS_TRUE(usedPcs.init(512));
 
   size_t iscSize;
 
   static int analysisCount = 1;
   StatusMsg("Dump %d {\n", analysisCount++);
 
-  JSONWriter writer(Move(aWriter));
   writer.Start();
   {
     writer.IntProperty("version", kOutputVersionNumber);
 
     writer.StartObjectProperty("invocation");
     {
       const char* var = gOptions->DMDEnvVar();
       if (var) {
--- a/netwerk/base/Predictor.cpp
+++ b/netwerk/base/Predictor.cpp
@@ -387,21 +387,16 @@ Predictor::InstallObserver()
     mozilla::services::GetObserverService();
   if (!obs) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   rv = obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
-  if (!prefs) {
-    return NS_ERROR_NOT_AVAILABLE;
-  }
-
   Preferences::AddBoolVarCache(&mEnabled, PREDICTOR_ENABLED_PREF, true);
   Preferences::AddBoolVarCache(&mEnableHoverOnSSL,
                                PREDICTOR_SSL_HOVER_PREF, false);
 #ifdef NIGHTLY_BUILD
   Preferences::AddBoolVarCache(&mEnablePrefetch, PREDICTOR_PREFETCH_PREF, true);
 #else
   Preferences::AddBoolVarCache(&mEnablePrefetch, PREDICTOR_PREFETCH_PREF, false);
 #endif
--- a/netwerk/cache/nsCacheService.cpp
+++ b/netwerk/cache/nsCacheService.cpp
@@ -407,19 +407,22 @@ nsCacheProfilePrefObserver::Observe(nsIS
     } else if (!strcmp("suspend_process_notification", topic)) {
         // A suspended process may never return, so shutdown the cache to reduce
         // cache corruption.
         nsCacheService::GlobalInstance()->Shutdown();
     } else if (!strcmp("profile-do-change", topic)) {
         // profile after change
         mHaveProfile = true;
         nsCOMPtr<nsIPrefBranch> branch = do_GetService(NS_PREFSERVICE_CONTRACTID);
-        ReadPrefs(branch);
+        if (!branch) {
+            return NS_ERROR_FAILURE;
+        }
+        (void)ReadPrefs(branch);
         nsCacheService::OnProfileChanged();
-    
+
     } else if (!strcmp(NS_PREFBRANCH_PREFCHANGE_TOPIC_ID, topic)) {
 
         // ignore pref changes until we're done switch profiles
         if (!mHaveProfile)  
             return NS_OK;
 
         nsCOMPtr<nsIPrefBranch> branch = do_QueryInterface(subject, &rv);
         if (NS_FAILED(rv))  
--- a/netwerk/dns/ChildDNSService.cpp
+++ b/netwerk/dns/ChildDNSService.cpp
@@ -251,17 +251,16 @@ nsresult
 ChildDNSService::Init()
 {
   // Disable prefetching either by explicit preference or if a manual proxy
   // is configured
   bool disablePrefetch = false;
   int  proxyType = nsIProtocolProxyService::PROXYCONFIG_DIRECT;
 
   nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
-  prefs->GetBoolPref(kPrefNameDisablePrefetch, &disablePrefetch);
   if (prefs) {
     prefs->GetIntPref("network.proxy.type", &proxyType);
     prefs->GetBoolPref(kPrefNameDisablePrefetch, &disablePrefetch);
   }
 
   if (mFirstTime) {
     mFirstTime = false;
     if (prefs) {
--- a/netwerk/protocol/websocket/WebSocketChannel.cpp
+++ b/netwerk/protocol/websocket/WebSocketChannel.cpp
@@ -189,16 +189,19 @@ public:
   FailDelayManager()
   {
     MOZ_COUNT_CTOR(FailDelayManager);
 
     mDelaysDisabled = false;
 
     nsCOMPtr<nsIPrefBranch> prefService =
       do_GetService(NS_PREFSERVICE_CONTRACTID);
+    if (!prefService) {
+      return;
+    }
     bool boolpref = true;
     nsresult rv;
     rv = prefService->GetBoolPref("network.websocket.delay-failed-reconnects",
                                   &boolpref);
     if (NS_SUCCEEDED(rv) && !boolpref) {
       mDelaysDisabled = true;
     }
   }
--- a/startupcache/test/TestStartupCache.cpp
+++ b/startupcache/test/TestStartupCache.cpp
@@ -391,18 +391,22 @@ TestEarlyShutdown() {
 
 int main(int argc, char** argv)
 {
   ScopedXPCOM xpcom("Startup Cache");
   if (xpcom.failed())
     return 1;
 
   nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
+  if (!prefs) {
+    fail("prefs");
+    return 1;
+  }
   prefs->SetIntPref("hangmonitor.timeout", 0);
-  
+
   int rv = 0;
   nsresult scrv;
 
   // Register TestStartupCacheTelemetry
   nsCOMPtr<nsIFile> manifest;
   scrv = NS_GetSpecialDirectory(NS_GRE_DIR,
                                 getter_AddRefs(manifest));
   if (NS_FAILED(scrv)) {
--- a/toolkit/components/url-classifier/nsUrlClassifierUtils.cpp
+++ b/toolkit/components/url-classifier/nsUrlClassifierUtils.cpp
@@ -126,27 +126,27 @@ nsUrlClassifierUtils::GetKeyForURI(nsIUR
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsUrlClassifierUtils::GetProtocolVersion(const nsACString& aProvider,
                                          nsACString& aVersion)
 {
-  nsCOMPtr<nsIPrefBranch> prefBranch =
-    do_GetService(NS_PREFSERVICE_CONTRACTID);
-
-  nsPrintfCString prefName("browser.safebrowsing.provider.%s.pver",
-                           nsCString(aProvider).get());
+  nsCOMPtr<nsIPrefBranch> prefBranch = do_GetService(NS_PREFSERVICE_CONTRACTID);
+  if (prefBranch) {
+      nsPrintfCString prefName("browser.safebrowsing.provider.%s.pver",
+                               nsCString(aProvider).get());
+      nsXPIDLCString version;
+      nsresult rv = prefBranch->GetCharPref(prefName.get(), getter_Copies(version));
 
-  nsXPIDLCString version;
-  nsresult rv = prefBranch->GetCharPref(prefName.get(), getter_Copies(version));
-
-  aVersion = NS_SUCCEEDED(rv) ? version
-                              : DEFAULT_PROTOCOL_VERSION;
+      aVersion = NS_SUCCEEDED(rv) ? version : DEFAULT_PROTOCOL_VERSION;
+  } else {
+      aVersion = DEFAULT_PROTOCOL_VERSION;
+  }
 
   return NS_OK;
 }
 
 /////////////////////////////////////////////////////////////////////////////
 // non-interface methods
 
 nsresult