merge mozilla-inbound to mozilla-central
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Tue, 03 Jun 2014 14:40:04 +0200
changeset 198721 caff98d085eec587ff9cd40b4f0d27900ac3f33a
parent 198666 f28005b84ed07f99be809f5308fca4ec2f16e9ed (current diff)
parent 198720 dcb77f2cdc4ce9a4e1a2973794e7376f967912fe (diff)
child 198733 b13d1d327c5fec27062b6795f4aa7d659ef91f24
push id5990
push userasasaki@mozilla.com
push dateMon, 09 Jun 2014 21:40:24 +0000
treeherdermozilla-aurora@0796197efbc9 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone32.0a1
merge mozilla-inbound to mozilla-central
js/src/jit-test/tests/basic/latin1-indexOf.js
js/src/jit-test/tests/basic/latin1-indexing.js
js/src/jit-test/tests/basic/latin1.js
js/src/jsworkers.cpp
js/src/jsworkers.h
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -971,16 +971,19 @@ pref("gfx.canvas.willReadFrequently.enab
 
 // Disable autofocus until we can have it not bring up the keyboard.
 // https://bugzilla.mozilla.org/show_bug.cgi?id=965763
 pref("browser.autofocus", false);
 
 // Enable wakelock
 pref("dom.wakelock.enabled", true);
 
+// Disable touch caret by default
+pref("touchcaret.enabled", false);
+
 // Enable sync and mozId with Firefox Accounts.
 #ifdef MOZ_SERVICES_FXACCOUNTS
 pref("services.sync.fxaccounts.enabled", true);
 pref("identity.fxaccounts.enabled", true);
 #endif
 
 // Enable mapped array buffer
 pref("dom.mapped_arraybuffer.enabled", true);
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -666,16 +666,19 @@
 @BINPATH@/res/table-add-row-before-hover.gif
 @BINPATH@/res/table-add-row-before.gif
 @BINPATH@/res/table-remove-column-active.gif
 @BINPATH@/res/table-remove-column-hover.gif
 @BINPATH@/res/table-remove-column.gif
 @BINPATH@/res/table-remove-row-active.gif
 @BINPATH@/res/table-remove-row-hover.gif
 @BINPATH@/res/table-remove-row.gif
+@BINPATH@/res/text_selection_handle.png
+@BINPATH@/res/text_selection_handle@1.5.png
+@BINPATH@/res/text_selection_handle@2.png
 @BINPATH@/res/grabber.gif
 #ifdef XP_MACOSX
 @BINPATH@/res/cursors/*
 #endif
 @BINPATH@/res/fonts/*
 @BINPATH@/res/dtd/*
 @BINPATH@/res/html/*
 @BINPATH@/res/langGroups.properties
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1512,16 +1512,19 @@ pref("social.sidebar.unload_timeout_ms",
 pref("dom.identity.enabled", false);
 
 // Turn on the CSP 1.0 parser for Content Security Policy headers
 pref("security.csp.speccompliant", true);
 
 // Block insecure active content on https pages
 pref("security.mixed_content.block_active_content", true);
 
+// 1 = allow MITM for certificate pinning checks.
+pref("security.cert_pinning.enforcement_level", 1);
+
 // Override the Gecko-default value of false for Firefox.
 pref("plain_text.wrap_long_lines", true);
 
 // If this turns true, Moz*Gesture events are not called stopPropagation()
 // before content.
 pref("dom.debug.propagate_gesture_events_through_content", false);
 
 // The request URL of the GeoLocation backend.
--- a/caps/src/nsScriptSecurityManager.cpp
+++ b/caps/src/nsScriptSecurityManager.cpp
@@ -1034,17 +1034,17 @@ nsScriptSecurityManager::CanCreateWrappe
 // XXX Special case for nsIXPCException ?
     ClassInfoData objClassInfo = ClassInfoData(aClassInfo, nullptr);
     if (objClassInfo.IsDOMClass())
     {
         return NS_OK;
     }
 
     // We give remote-XUL whitelisted domains a free pass here. See bug 932906.
-    if (!xpc::AllowXBLScope(js::GetContextCompartment(cx)))
+    if (!xpc::AllowContentXBLScope(js::GetContextCompartment(cx)))
     {
         return NS_OK;
     }
 
     if (nsContentUtils::IsCallerChrome())
     {
         return NS_OK;
     }
--- a/content/base/public/nsContentUtils.h
+++ b/content/base/public/nsContentUtils.h
@@ -183,17 +183,17 @@ public:
 
   /**
    * Get a JSContext from the document's scope object.
    */
   static JSContext* GetContextFromDocument(nsIDocument *aDocument);
 
   static bool     IsCallerChrome();
   static bool     ThreadsafeIsCallerChrome();
-  static bool     IsCallerXBL();
+  static bool     IsCallerContentXBL();
 
   static bool     IsImageSrcSetDisabled();
 
   static bool LookupBindingMember(JSContext* aCx, nsIContent *aContent,
                                   JS::Handle<jsid> aId,
                                   JS::MutableHandle<JSPropertyDescriptor> aDesc);
 
   /**
--- a/content/base/src/nsCSPParser.cpp
+++ b/content/base/src/nsCSPParser.cpp
@@ -691,17 +691,18 @@ nsCSPParser::sourceList(nsTArray<nsCSPBa
   if (isNone) {
     // If the directive contains no other srcs, then we set the 'none'
     if (outSrcs.Length() == 0) {
       nsCSPKeywordSrc *keyword = new nsCSPKeywordSrc(CSP_NONE);
       outSrcs.AppendElement(keyword);
     }
     // Otherwise, we ignore 'none' and report a warning
     else {
-      const char16_t* params[] = { NS_ConvertUTF8toUTF16(CSP_EnumToKeyword(CSP_NONE)).get() };
+      NS_ConvertUTF8toUTF16 unicodeNone(CSP_EnumToKeyword(CSP_NONE));
+      const char16_t* params[] = { unicodeNone.get() };
       logWarningErrorToConsole(nsIScriptError::warningFlag, "ignoringUnknownOption",
                                params, ArrayLength(params));
     }
   }
 }
 
 void
 nsCSPParser::reportURIList(nsTArray<nsCSPBaseSrc*>& outSrcs)
@@ -882,17 +883,18 @@ nsCSPParser::parseContentSecurityPolicy(
 
   // Check that report-only policies define a report-uri, otherwise log warning.
   if (aReportOnly) {
     policy->setReportOnlyFlag(true);
     if (!policy->directiveExists(CSP_REPORT_URI)) {
       nsAutoCString prePath;
       nsresult rv = aSelfURI->GetPrePath(prePath);
       NS_ENSURE_SUCCESS(rv, policy);
-      const char16_t* params[] = { NS_ConvertUTF8toUTF16(prePath).get() };
+      NS_ConvertUTF8toUTF16 unicodePrePath(prePath);
+      const char16_t* params[] = { unicodePrePath.get() };
       parser.logWarningErrorToConsole(nsIScriptError::warningFlag, "reportURInotInReportOnlyHeader",
                                       params, ArrayLength(params));
     }
   }
 
   if (policy->getNumDirectives() == 0) {
     // Individual errors were already reported in the parser, but if
     // we do not have an enforcable directive at all, we return null.
--- a/content/base/src/nsCSPService.cpp
+++ b/content/base/src/nsCSPService.cpp
@@ -362,17 +362,18 @@ CSPService::AsyncOnChannelRedirect(nsICh
       return NS_OK;
     }
   }
 
   // The redirecting channel isn't a writable property bag, we won't be able
   // to enforce the load policy if it redirects again, so we stop it now.
   nsAutoCString newUriSpec;
   rv = newUri->GetSpec(newUriSpec);
-  const char16_t *formatParams[] = { NS_ConvertUTF8toUTF16(newUriSpec).get() };
+  NS_ConvertUTF8toUTF16 unicodeSpec(newUriSpec);
+  const char16_t *formatParams[] = { unicodeSpec.get() };
   if (NS_SUCCEEDED(rv)) {
     nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
                                     NS_LITERAL_CSTRING("Redirect Error"), nullptr,
                                     nsContentUtils::eDOM_PROPERTIES,
                                     "InvalidRedirectChannelWarning",
                                     formatParams, 1);
   }
 
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -1709,32 +1709,32 @@ bool
 nsContentUtils::ThreadsafeIsCallerChrome()
 {
   return NS_IsMainThread() ?
     IsCallerChrome() :
     mozilla::dom::workers::IsCurrentThreadRunningChromeWorker();
 }
 
 bool
-nsContentUtils::IsCallerXBL()
+nsContentUtils::IsCallerContentXBL()
 {
     JSContext *cx = GetCurrentJSContext();
     if (!cx)
         return false;
 
     JSCompartment *c = js::GetContextCompartment(cx);
 
     // For remote XUL, we run XBL in the XUL scope. Given that we care about
     // compat and not security for remote XUL, just always claim to be XBL.
-    if (!xpc::AllowXBLScope(c)) {
+    if (!xpc::AllowContentXBLScope(c)) {
       MOZ_ASSERT(nsContentUtils::AllowXULXBLForPrincipal(xpc::GetCompartmentPrincipal(c)));
       return true;
     }
 
-    return xpc::IsXBLScope(c);
+    return xpc::IsContentXBLScope(c);
 }
 
 
 bool
 nsContentUtils::IsImageSrcSetDisabled()
 {
   return Preferences::GetBool("dom.disable_image_src_set") &&
          !IsCallerChrome();
@@ -5580,17 +5580,17 @@ nsContentTypeParser::GetParameter(const 
                                     aResult);
 }
 
 /* static */
 
 bool
 nsContentUtils::CanAccessNativeAnon()
 {
-  return IsCallerChrome() || IsCallerXBL();
+  return IsCallerChrome() || IsCallerContentXBL();
 }
 
 /* static */ nsresult
 nsContentUtils::DispatchXULCommand(nsIContent* aTarget,
                                    bool aTrusted,
                                    nsIDOMEvent* aSourceEvent,
                                    nsIPresShell* aShell,
                                    bool aCtrl,
--- a/content/base/src/nsINode.cpp
+++ b/content/base/src/nsINode.cpp
@@ -2655,17 +2655,17 @@ nsINode::WrapObject(JSContext *aCx)
       !hasHadScriptHandlingObject &&
       !nsContentUtils::IsCallerChrome()) {
     Throw(aCx, NS_ERROR_UNEXPECTED);
     return nullptr;
   }
 
   JS::Rooted<JSObject*> obj(aCx, WrapNode(aCx));
   MOZ_ASSERT_IF(ChromeOnlyAccess(),
-                xpc::IsInXBLScope(obj) || !xpc::UseXBLScope(js::GetObjectCompartment(obj)));
+                xpc::IsInContentXBLScope(obj) || !xpc::UseContentXBLScope(js::GetObjectCompartment(obj)));
   return obj;
 }
 
 already_AddRefed<nsINode>
 nsINode::CloneNode(bool aDeep, ErrorResult& aError)
 {
   bool callUserDataHandlers = NodeType() != nsIDOMNode::DOCUMENT_NODE ||
                               !static_cast<nsIDocument*>(this)->CreatingStaticClone();
--- a/content/base/src/nsObjectLoadingContent.cpp
+++ b/content/base/src/nsObjectLoadingContent.cpp
@@ -2645,17 +2645,17 @@ nsObjectLoadingContent::ScriptRequestPlu
   // The below methods pull the cx off the stack, so make sure they match.
   //
   // NB: Sometimes there's a null cx on the stack, in which case |cx| is the
   // safe JS context. But in that case, IsCallerChrome() will return true,
   // so the ensuing expression is short-circuited.
   MOZ_ASSERT_IF(nsContentUtils::GetCurrentJSContext(),
                 aCx == nsContentUtils::GetCurrentJSContext());
   bool callerIsContentJS = (!nsContentUtils::IsCallerChrome() &&
-                            !nsContentUtils::IsCallerXBL() &&
+                            !nsContentUtils::IsCallerContentXBL() &&
                             js::IsContextRunningJS(aCx));
 
   nsCOMPtr<nsIContent> thisContent =
     do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
 
   *aResult = nullptr;
 
   // The first time content script attempts to access placeholder content, fire
--- a/content/canvas/src/WebGLContext.cpp
+++ b/content/canvas/src/WebGLContext.cpp
@@ -78,28 +78,31 @@ WebGLMemoryPressureObserver::Observe(nsI
 {
     if (strcmp(aTopic, "memory-pressure"))
         return NS_OK;
 
     bool wantToLoseContext = true;
 
     if (!mContext->mCanLoseContextInForeground &&
         ProcessPriorityManager::CurrentProcessIsForeground())
+    {
         wantToLoseContext = false;
-    else if (!nsCRT::strcmp(aSomeData,
-                            MOZ_UTF16("heap-minimize")))
+    } else if (!nsCRT::strcmp(aSomeData,
+                              MOZ_UTF16("heap-minimize")))
+    {
         wantToLoseContext = mContext->mLoseContextOnHeapMinimize;
+    }
 
-    if (wantToLoseContext)
+    if (wantToLoseContext) {
         mContext->ForceLoseContext();
+    }
 
     return NS_OK;
 }
 
-
 WebGLContextOptions::WebGLContextOptions()
     : alpha(true), depth(true), stencil(false),
       premultipliedAlpha(true), antialias(true),
       preserveDrawingBuffer(false)
 {
     // Set default alpha state based on preference.
     if (Preferences::GetBool("webgl.default-no-alpha", false))
         alpha = false;
@@ -163,22 +166,22 @@ WebGLContext::WebGLContext()
     mGLMaxTransformFeedbackSeparateAttribs = 0;
 
     // See OpenGL ES 2.0.25 spec, 6.2 State Tables, table 6.13
     mPixelStorePackAlignment = 4;
     mPixelStoreUnpackAlignment = 4;
 
     WebGLMemoryTracker::AddWebGLContext(this);
 
-    mAllowRestore = true;
+    mAllowContextRestore = true;
+    mLastLossWasSimulated = false;
     mContextLossTimerRunning = false;
-    mDrawSinceContextLossTimerSet = false;
+    mRunContextLossTimerAgain = false;
     mContextRestorer = do_CreateInstance("@mozilla.org/timer;1");
     mContextStatus = ContextNotLost;
-    mContextLostErrorSet = false;
     mLoseContextOnHeapMinimize = false;
     mCanLoseContextInForeground = true;
 
     mAlreadyGeneratedWarnings = 0;
     mAlreadyWarnedAboutFakeVertexAttrib0 = false;
     mAlreadyWarnedAboutViewportLargerThanDest = false;
     mMaxWarnings = Preferences::GetInt("webgl.max-warnings-per-context", 32);
     if (mMaxWarnings < -1)
@@ -566,21 +569,18 @@ WebGLContext::SetDimensions(int32_t widt
 
     mWidth = width;
     mHeight = height;
     mViewportWidth = width;
     mViewportHeight = height;
     mResetLayer = true;
     mOptionsFrozen = true;
 
-    mHasRobustness = gl->HasRobustness();
-
     // increment the generation number
     ++mGeneration;
-
 #if 0
     if (mGeneration > 0) {
         // XXX dispatch context lost event
     }
 #endif
 
     MakeContextCurrent();
 
@@ -1110,162 +1110,239 @@ WebGLContext::PresentScreenBuffer()
 void
 WebGLContext::DummyFramebufferOperation(const char *info)
 {
     GLenum status = CheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
     if (status != LOCAL_GL_FRAMEBUFFER_COMPLETE)
         ErrorInvalidFramebufferOperation("%s: incomplete framebuffer", info);
 }
 
+static bool
+CheckContextLost(GLContext* gl, bool* out_isGuilty)
+{
+    MOZ_ASSERT(gl);
+    MOZ_ASSERT(out_isGuilty);
+
+    bool isEGL = gl->GetContextType() == gl::GLContextType::EGL;
+
+    GLenum resetStatus = LOCAL_GL_NO_ERROR;
+    if (gl->HasRobustness()) {
+        gl->MakeCurrent();
+        resetStatus = gl->fGetGraphicsResetStatus();
+    } else if (isEGL) {
+        // Simulate a ARB_robustness guilty context loss for when we
+        // get an EGL_CONTEXT_LOST error. It may not actually be guilty,
+        // but we can't make any distinction.
+        if (!gl->MakeCurrent(true) && gl->IsContextLost()) {
+            resetStatus = LOCAL_GL_UNKNOWN_CONTEXT_RESET_ARB;
+        }
+    }
+
+    if (resetStatus == LOCAL_GL_NO_ERROR) {
+        *out_isGuilty = false;
+        return false;
+    }
+
+    // Assume guilty unless we find otherwise!
+    bool isGuilty = true;
+    switch (resetStatus) {
+    case LOCAL_GL_INNOCENT_CONTEXT_RESET_ARB:
+        // Either nothing wrong, or not our fault.
+        isGuilty = false;
+        break;
+    case LOCAL_GL_GUILTY_CONTEXT_RESET_ARB:
+        NS_WARNING("WebGL content on the page definitely caused the graphics"
+                   " card to reset.");
+        break;
+    case LOCAL_GL_UNKNOWN_CONTEXT_RESET_ARB:
+        NS_WARNING("WebGL content on the page might have caused the graphics"
+                   " card to reset");
+        // If we can't tell, assume guilty.
+        break;
+    default:
+        MOZ_ASSERT(false, "Unreachable.");
+        // If we do get here, let's pretend to be guilty as an escape plan.
+        break;
+    }
+
+    if (isGuilty) {
+        NS_WARNING("WebGL context on this page is considered guilty, and will"
+                   " not be restored.");
+    }
+
+    *out_isGuilty = isGuilty;
+    return true;
+}
+
+bool
+WebGLContext::TryToRestoreContext()
+{
+    if (NS_FAILED(SetDimensions(mWidth, mHeight)))
+        return false;
+
+    return true;
+}
+
+class UpdateContextLossStatusTask : public nsRunnable
+{
+    nsRefPtr<WebGLContext> mContext;
+
+public:
+    UpdateContextLossStatusTask(WebGLContext* context)
+        : mContext(context)
+    {
+    }
+
+    NS_IMETHOD Run() {
+        mContext->UpdateContextLossStatus();
+
+        return NS_OK;
+    }
+};
+
+void
+WebGLContext::EnqueueUpdateContextLossStatus()
+{
+    nsCOMPtr<nsIRunnable> task = new UpdateContextLossStatusTask(this);
+    NS_DispatchToCurrentThread(task);
+}
+
 // We use this timer for many things. Here are the things that it is activated for:
 // 1) If a script is using the MOZ_WEBGL_lose_context extension.
 // 2) If we are using EGL and _NOT ANGLE_, we query periodically to see if the
 //    CONTEXT_LOST_WEBGL error has been triggered.
 // 3) If we are using ANGLE, or anything that supports ARB_robustness, query the
 //    GPU periodically to see if the reset status bit has been set.
 // In all of these situations, we use this timer to send the script context lost
 // and restored events asynchronously. For example, if it triggers a context loss,
 // the webglcontextlost event will be sent to it the next time the robustness timer
 // fires.
 // Note that this timer mechanism is not used unless one of these 3 criteria
 // are met.
 // At a bare minimum, from context lost to context restores, it would take 3
 // full timer iterations: detection, webglcontextlost, webglcontextrestored.
 void
-WebGLContext::RobustnessTimerCallback(nsITimer* timer)
+WebGLContext::UpdateContextLossStatus()
 {
-    TerminateContextLossTimer();
-
     if (!mCanvasElement) {
         // the canvas is gone. That happens when the page was closed before we got
         // this timer event. In this case, there's nothing to do here, just don't crash.
         return;
     }
+    if (mContextStatus == ContextNotLost) {
+        // We don't know that we're lost, but we might be, so we need to
+        // check. If we're guilty, don't allow restores, though.
 
-    // If the context has been lost and we're waiting for it to be restored, do
-    // that now.
+        bool isGuilty = true;
+        MOZ_ASSERT(gl); // Shouldn't be missing gl if we're NotLost.
+        bool isContextLost = CheckContextLost(gl, &isGuilty);
+
+        if (isContextLost) {
+            if (isGuilty)
+                mAllowContextRestore = false;
+
+            ForceLoseContext();
+        }
+
+        // Fall through.
+    }
+
     if (mContextStatus == ContextLostAwaitingEvent) {
-        bool defaultAction;
+        // The context has been lost and we haven't yet triggered the
+        // callback, so do that now.
+
+        bool useDefaultHandler;
         nsContentUtils::DispatchTrustedEvent(mCanvasElement->OwnerDoc(),
                                              static_cast<nsIDOMHTMLCanvasElement*>(mCanvasElement),
                                              NS_LITERAL_STRING("webglcontextlost"),
                                              true,
                                              true,
-                                             &defaultAction);
+                                             &useDefaultHandler);
+        // We sent the callback, so we're just 'regular lost' now.
+        mContextStatus = ContextLost;
+        // If we're told to use the default handler, it means the script
+        // didn't bother to handle the event. In this case, we shouldn't
+        // auto-restore the context.
+        if (useDefaultHandler)
+            mAllowContextRestore = false;
 
-        // If the script didn't handle the event, we don't allow restores.
-        if (defaultAction)
-            mAllowRestore = false;
+        // Fall through.
+    }
+
+    if (mContextStatus == ContextLost) {
+        // Context is lost, and we've already sent the callback. We
+        // should try to restore the context if we're both allowed to,
+        // and supposed to.
 
-        // If the script handled the event and we are allowing restores, then
-        // mark it to be restored. Otherwise, leave it as context lost
-        // (unusable).
-        if (!defaultAction && mAllowRestore) {
-            ForceRestoreContext();
-            // Restart the timer so that it will be restored on the next
-            // callback.
-            SetupContextLossTimer();
-        } else {
+        // Are we allowed to restore the context?
+        if (!mAllowContextRestore)
+            return;
+
+        // If we're only simulated-lost, we shouldn't auto-restore, and
+        // instead we should wait for restoreContext() to be called.
+        if (mLastLossWasSimulated)
+            return;
+
+        ForceRestoreContext();
+        return;
+    }
+
+    if (mContextStatus == ContextLostAwaitingRestore) {
+        // Context is lost, but we should try to restore it.
+
+        if (!mAllowContextRestore) {
+            // We might decide this after thinking we'd be OK restoring
+            // the context, so downgrade.
             mContextStatus = ContextLost;
-        }
-    } else if (mContextStatus == ContextLostAwaitingRestore) {
-        // Try to restore the context. If it fails, try again later.
-        if (NS_FAILED(SetDimensions(mWidth, mHeight))) {
-            SetupContextLossTimer();
             return;
         }
+
+        if (!TryToRestoreContext()) {
+            // Failed to restore. Try again later.
+            RunContextLossTimer();
+            return;
+        }
+
+        // Revival!
         mContextStatus = ContextNotLost;
         nsContentUtils::DispatchTrustedEvent(mCanvasElement->OwnerDoc(),
                                              static_cast<nsIDOMHTMLCanvasElement*>(mCanvasElement),
                                              NS_LITERAL_STRING("webglcontextrestored"),
                                              true,
                                              true);
-        // Set all flags back to the state they were in before the context was
-        // lost.
         mEmitContextLostErrorOnce = true;
-        mAllowRestore = true;
-    }
-
-    MaybeRestoreContext();
-    return;
-}
-
-void
-WebGLContext::MaybeRestoreContext()
-{
-    // Don't try to handle it if we already know it's busted.
-    if (mContextStatus != ContextNotLost || gl == nullptr)
         return;
-
-    bool isEGL = gl->GetContextType() == gl::GLContextType::EGL,
-         isANGLE = gl->IsANGLE();
-
-    GLContext::ContextResetARB resetStatus = GLContext::CONTEXT_NO_ERROR;
-    if (mHasRobustness) {
-        gl->MakeCurrent();
-        resetStatus = (GLContext::ContextResetARB) gl->fGetGraphicsResetStatus();
-    } else if (isEGL) {
-        // Simulate a ARB_robustness guilty context loss for when we
-        // get an EGL_CONTEXT_LOST error. It may not actually be guilty,
-        // but we can't make any distinction, so we must assume the worst
-        // case.
-        if (!gl->MakeCurrent(true) && gl->IsContextLost()) {
-            resetStatus = GLContext::CONTEXT_GUILTY_CONTEXT_RESET_ARB;
-        }
-    }
-
-    if (resetStatus != GLContext::CONTEXT_NO_ERROR) {
-        // It's already lost, but clean up after it and signal to JS that it is
-        // lost.
-        ForceLoseContext();
-    }
-
-    switch (resetStatus) {
-        case GLContext::CONTEXT_NO_ERROR:
-            // If there has been activity since the timer was set, it's possible
-            // that we did or are going to miss something, so clear this flag and
-            // run it again some time later.
-            if (mDrawSinceContextLossTimerSet)
-                SetupContextLossTimer();
-            break;
-        case GLContext::CONTEXT_GUILTY_CONTEXT_RESET_ARB:
-            NS_WARNING("WebGL content on the page caused the graphics card to reset; not restoring the context");
-            mAllowRestore = false;
-            break;
-        case GLContext::CONTEXT_INNOCENT_CONTEXT_RESET_ARB:
-            break;
-        case GLContext::CONTEXT_UNKNOWN_CONTEXT_RESET_ARB:
-            NS_WARNING("WebGL content on the page might have caused the graphics card to reset");
-            if (isEGL && isANGLE) {
-                // If we're using ANGLE, we ONLY get back UNKNOWN context resets, including for guilty contexts.
-                // This means that we can't restore it or risk restoring a guilty context. Should this ever change,
-                // we can get rid of the whole IsANGLE() junk from GLContext.h since, as of writing, this is the
-                // only use for it. See ANGLE issue 261.
-                mAllowRestore = false;
-            }
-            break;
     }
 }
 
 void
 WebGLContext::ForceLoseContext()
 {
-    if (mContextStatus == ContextLostAwaitingEvent)
-        return;
-
+    printf_stderr("WebGL(%p)::ForceLoseContext\n", this);
+    MOZ_ASSERT(!IsContextLost());
     mContextStatus = ContextLostAwaitingEvent;
-    // Queue up a task to restore the event.
-    SetupContextLossTimer();
+    mContextLostErrorSet = false;
+    mLastLossWasSimulated = false;
+
+    // Burn it all!
     DestroyResourcesAndContext();
+
+    // Queue up a task, since we know the status changed.
+    EnqueueUpdateContextLossStatus();
 }
 
 void
 WebGLContext::ForceRestoreContext()
 {
+    printf_stderr("WebGL(%p)::ForceRestoreContext\n", this);
     mContextStatus = ContextLostAwaitingRestore;
+    mAllowContextRestore = true; // Hey, you did say 'force'.
+
+    // Queue up a task, since we know the status changed.
+    EnqueueUpdateContextLossStatus();
 }
 
 void
 WebGLContext::MakeContextCurrent() const { gl->MakeCurrent(); }
 
 mozilla::TemporaryRef<mozilla::gfx::SourceSurface>
 WebGLContext::GetSurfaceSnapshot(bool* aPremultAlpha)
 {
--- a/content/canvas/src/WebGLContext.h
+++ b/content/canvas/src/WebGLContext.h
@@ -197,19 +197,16 @@ public:
     NS_IMETHOD Redraw(const gfxRect&) { return NS_ERROR_NOT_IMPLEMENTED; }
     NS_IMETHOD Swap(mozilla::ipc::Shmem& aBack,
                     int32_t x, int32_t y, int32_t w, int32_t h)
                     { return NS_ERROR_NOT_IMPLEMENTED; }
     NS_IMETHOD Swap(uint32_t nativeID,
                     int32_t x, int32_t y, int32_t w, int32_t h)
                     { return NS_ERROR_NOT_IMPLEMENTED; }
 
-    bool LoseContext();
-    bool RestoreContext();
-
     void SynthesizeGLError(GLenum err);
     void SynthesizeGLError(GLenum err, const char *fmt, ...);
 
     void ErrorInvalidEnum(const char *fmt = 0, ...);
     void ErrorInvalidOperation(const char *fmt = 0, ...);
     void ErrorInvalidValue(const char *fmt = 0, ...);
     void ErrorInvalidFramebufferOperation(const char *fmt = 0, ...);
     void ErrorInvalidEnumInfo(const char *info, GLenum enumvalue);
@@ -256,21 +253,24 @@ public:
     void ForceClearFramebufferWithDefaultValues(GLbitfield mask, const bool colorAttachmentsMask[kMaxColorAttachments]);
 
     // Calls ForceClearFramebufferWithDefaultValues() for the Context's 'screen'.
     void ClearScreen();
     void ClearBackbufferIfNeeded();
 
     bool MinCapabilityMode() const { return mMinCapability; }
 
-    void RobustnessTimerCallback(nsITimer* timer);
-    static void RobustnessTimerCallbackStatic(nsITimer* timer, void *thisPointer);
-    void SetupContextLossTimer();
+    void UpdateContextLossStatus();
+    void EnqueueUpdateContextLossStatus();
+    static void ContextLossCallbackStatic(nsITimer* timer, void* thisPointer);
+    void RunContextLossTimer();
     void TerminateContextLossTimer();
 
+    bool TryToRestoreContext();
+
     void AssertCachedBindings();
     void AssertCachedState();
 
     // WebIDL WebGLRenderingContext API
     dom::HTMLCanvasElement* GetCanvas() const { return mCanvasElement; }
     GLsizei DrawingBufferWidth() const { return IsContextLost() ? 0 : mWidth; }
     GLsizei DrawingBufferHeight() const { return IsContextLost() ? 0 : mHeight; }
 
@@ -661,18 +661,22 @@ public:
                                           GLint& location, uint32_t& numElementsToUpload, uint32_t arrayLength,
                                           WebGLboolean aTranspose);
     bool ValidateUniformSetter(const char* name, WebGLUniformLocation *location_object, GLint& location);
     void ValidateProgram(WebGLProgram *prog);
     bool ValidateUniformLocation(const char* info, WebGLUniformLocation *location_object);
     bool ValidateSamplerUniformSetter(const char* info,
                                     WebGLUniformLocation *location,
                                     GLint value);
-
     void Viewport(GLint x, GLint y, GLsizei width, GLsizei height);
+// -----------------------------------------------------------------------------
+// WEBGL_lose_context
+public:
+    void LoseContext();
+    void RestoreContext();
 
 // -----------------------------------------------------------------------------
 // Asynchronous Queries (WebGLContextAsyncQueries.cpp)
 public:
     already_AddRefed<WebGLQuery> CreateQuery();
     void DeleteQuery(WebGLQuery *query);
     void BeginQuery(GLenum target, WebGLQuery *query);
     void EndQuery(GLenum target);
@@ -864,17 +868,16 @@ protected:
 
     WebGLContextOptions mOptions;
 
     bool mInvalidated;
     bool mResetLayer;
     bool mOptionsFrozen;
     bool mMinCapability;
     bool mDisableExtensions;
-    bool mHasRobustness;
     bool mIsMesa;
     bool mLoseContextOnHeapMinimize;
     bool mCanLoseContextInForeground;
     bool mShouldPresent;
     bool mBackbufferNeedsClear;
     bool mDisableFragHighP;
 
     template<typename WebGLObjectType>
@@ -1111,17 +1114,16 @@ protected:
                              GLenum internalFormat,
                              GLsizei width,
                              GLsizei height,
                              GLint border,
                              GLenum format,
                              GLenum type,
                              const GLvoid *data);
 
-    void MaybeRestoreContext();
     void ForceLoseContext();
     void ForceRestoreContext();
 
     nsTArray<WebGLRefPtr<WebGLTexture> > mBound2DTextures;
     nsTArray<WebGLRefPtr<WebGLTexture> > mBoundCubeMapTextures;
 
     WebGLRefPtr<WebGLProgram> mCurrentProgram;
 
@@ -1173,33 +1175,34 @@ protected:
     GLfloat mVertexAttrib0Vector[4];
     GLfloat mFakeVertexAttrib0BufferObjectVector[4];
     size_t mFakeVertexAttrib0BufferObjectSize;
     GLuint mFakeVertexAttrib0BufferObject;
     WebGLVertexAttrib0Status mFakeVertexAttrib0BufferStatus;
 
     GLint mStencilRefFront, mStencilRefBack;
     GLuint mStencilValueMaskFront, mStencilValueMaskBack,
-              mStencilWriteMaskFront, mStencilWriteMaskBack;
+           mStencilWriteMaskFront, mStencilWriteMaskBack;
     realGLboolean mColorWriteMask[4];
     realGLboolean mDepthWriteMask;
     GLfloat mColorClearValue[4];
     GLint mStencilClearValue;
     GLfloat mDepthClearValue;
 
     GLint mViewportX;
     GLint mViewportY;
     GLsizei mViewportWidth;
     GLsizei mViewportHeight;
     bool mAlreadyWarnedAboutViewportLargerThanDest;
 
     nsCOMPtr<nsITimer> mContextRestorer;
-    bool mAllowRestore;
+    bool mAllowContextRestore;
+    bool mLastLossWasSimulated;
     bool mContextLossTimerRunning;
-    bool mDrawSinceContextLossTimerSet;
+    bool mRunContextLossTimerAgain;
     ContextStatus mContextStatus;
     bool mContextLostErrorSet;
 
     // Used for some hardware (particularly Tegra 2 and 4) that likes to
     // be Flushed while doing hundreds of draw calls.
     int mDrawCallsSinceLastFlush;
 
     int mAlreadyGeneratedWarnings;
--- a/content/canvas/src/WebGLContextDraw.cpp
+++ b/content/canvas/src/WebGLContextDraw.cpp
@@ -123,17 +123,17 @@ WebGLContext::DrawArrays(GLenum mode, GL
         return;
 
     if (!ValidateDrawModeEnum(mode, "drawArrays: mode"))
         return;
 
     if (!DrawArrays_check(first, count, 1, "drawArrays"))
         return;
 
-    SetupContextLossTimer();
+    RunContextLossTimer();
     gl->fDrawArrays(mode, first, count);
 
     Draw_cleanup();
 }
 
 void
 WebGLContext::DrawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsizei primcount)
 {
@@ -144,17 +144,17 @@ WebGLContext::DrawArraysInstanced(GLenum
         return;
 
     if (!DrawArrays_check(first, count, primcount, "drawArraysInstanced"))
         return;
 
     if (!DrawInstanced_check("drawArraysInstanced"))
         return;
 
-    SetupContextLossTimer();
+    RunContextLossTimer();
     gl->fDrawArraysInstanced(mode, first, count, primcount);
 
     Draw_cleanup();
 }
 
 bool
 WebGLContext::DrawElements_check(GLsizei count, GLenum type,
                                  WebGLintptr byteOffset, GLsizei primcount,
@@ -291,17 +291,17 @@ WebGLContext::DrawElements(GLenum mode, 
 
     GLuint upperBound = UINT_MAX;
     if (!DrawElements_check(count, type, byteOffset, 1, "drawElements",
                             &upperBound))
     {
         return;
     }
 
-    SetupContextLossTimer();
+    RunContextLossTimer();
 
     if (gl->IsSupported(gl::GLFeature::draw_range_elements)) {
         gl->fDrawRangeElements(mode, 0, upperBound,
                                count, type, reinterpret_cast<GLvoid*>(byteOffset));
     } else {
         gl->fDrawElements(mode, count, type, reinterpret_cast<GLvoid*>(byteOffset));
     }
 
@@ -319,17 +319,17 @@ WebGLContext::DrawElementsInstanced(GLen
         return;
 
     if (!DrawElements_check(count, type, byteOffset, primcount, "drawElementsInstanced"))
         return;
 
     if (!DrawInstanced_check("drawElementsInstanced"))
         return;
 
-    SetupContextLossTimer();
+    RunContextLossTimer();
     gl->fDrawElementsInstanced(mode, count, type, reinterpret_cast<GLvoid*>(byteOffset), primcount);
 
     Draw_cleanup();
 }
 
 void WebGLContext::Draw_cleanup()
 {
     UndoFakeVertexAttrib0();
--- a/content/canvas/src/WebGLContextGL.cpp
+++ b/content/canvas/src/WebGLContextGL.cpp
@@ -3852,37 +3852,44 @@ WebGLContext::TexSubImage2D(GLenum targe
     return TexSubImage2D_base(target, level, xoffset, yoffset,
                               pixels->Width(), pixels->Height(),
                               4*pixels->Width(), format, type,
                               arr.Data(), arr.Length(),
                               -1,
                               WebGLTexelFormat::RGBA8, false);
 }
 
-bool
+void
 WebGLContext::LoseContext()
 {
     if (IsContextLost())
-        return false;
+        return ErrorInvalidOperation("loseContext: Context is already lost.");
 
     ForceLoseContext();
-
-    return true;
+    mLastLossWasSimulated = true;
 }
 
-bool
+void
 WebGLContext::RestoreContext()
 {
-    if (!IsContextLost() || !mAllowRestore) {
-        return false;
+    if (!IsContextLost())
+        return ErrorInvalidOperation("restoreContext: Context is not lost.");
+
+    if (!mLastLossWasSimulated) {
+        return ErrorInvalidOperation("restoreContext: Context loss was not simulated."
+                                     " Cannot simulate restore.");
     }
+    // If we're currently lost, and the last loss was simulated, then
+    // we're currently only simulated-lost, allowing us to call
+    // restoreContext().
+
+    if (!mAllowContextRestore)
+        return ErrorInvalidOperation("restoreContext: Context cannot be restored.");
 
     ForceRestoreContext();
-
-    return true;
 }
 
 bool
 BaseTypeAndSizeFromUniformType(GLenum uType, GLenum *baseType, GLint *unitSize)
 {
     switch (uType) {
         case LOCAL_GL_INT:
         case LOCAL_GL_INT_VEC2:
--- a/content/canvas/src/WebGLContextLossTimer.cpp
+++ b/content/canvas/src/WebGLContextLossTimer.cpp
@@ -3,38 +3,50 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WebGLContext.h"
 
 using namespace mozilla;
 
 /* static */ void
-WebGLContext::RobustnessTimerCallbackStatic(nsITimer* timer, void *thisPointer) {
-    static_cast<WebGLContext*>(thisPointer)->RobustnessTimerCallback(timer);
+WebGLContext::ContextLossCallbackStatic(nsITimer* timer, void* thisPointer)
+{
+    (void)timer;
+    WebGLContext* context = static_cast<WebGLContext*>(thisPointer);
+
+    context->TerminateContextLossTimer();
+
+    context->UpdateContextLossStatus();
 }
 
 void
-WebGLContext::SetupContextLossTimer() {
+WebGLContext::RunContextLossTimer()
+{
     // If the timer was already running, don't restart it here. Instead,
     // wait until the previous call is done, then fire it one more time.
-    // This is an optimization to prevent unnecessary cross-communication
-    // between threads.
+    // This is an optimization to prevent unnecessary
+    // cross-communication between threads.
     if (mContextLossTimerRunning) {
-        mDrawSinceContextLossTimerSet = true;
+        mRunContextLossTimerAgain = true;
         return;
     }
-
-    mContextRestorer->InitWithFuncCallback(RobustnessTimerCallbackStatic,
-                                            static_cast<void*>(this),
-                                            1000,
-                                            nsITimer::TYPE_ONE_SHOT);
+    mContextRestorer->InitWithFuncCallback(ContextLossCallbackStatic,
+                                           static_cast<void*>(this),
+                                           1000,
+                                           nsITimer::TYPE_ONE_SHOT);
     mContextLossTimerRunning = true;
-    mDrawSinceContextLossTimerSet = false;
+    mRunContextLossTimerAgain = false;
 }
 
 void
-WebGLContext::TerminateContextLossTimer() {
-    if (mContextLossTimerRunning) {
-        mContextRestorer->Cancel();
-        mContextLossTimerRunning = false;
+WebGLContext::TerminateContextLossTimer()
+{
+    if (!mContextLossTimerRunning)
+        return;
+
+    mContextRestorer->Cancel();
+    mContextLossTimerRunning = false;
+
+    if (mRunContextLossTimerAgain) {
+        RunContextLossTimer();
     }
 }
--- a/content/canvas/src/WebGLExtensionLoseContext.cpp
+++ b/content/canvas/src/WebGLExtensionLoseContext.cpp
@@ -16,20 +16,18 @@ WebGLExtensionLoseContext::WebGLExtensio
 
 WebGLExtensionLoseContext::~WebGLExtensionLoseContext()
 {
 }
 
 void
 WebGLExtensionLoseContext::LoseContext()
 {
-    if (!mContext->LoseContext())
-        mContext->mWebGLError = LOCAL_GL_INVALID_OPERATION;
+    mContext->LoseContext();
 }
 
-void 
+void
 WebGLExtensionLoseContext::RestoreContext()
 {
-    if (!mContext->RestoreContext())
-        mContext->mWebGLError = LOCAL_GL_INVALID_OPERATION;
+    mContext->RestoreContext();
 }
 
 IMPL_WEBGL_EXTENSION_GOOP(WebGLExtensionLoseContext)
--- a/content/canvas/test/webgl-conformance/conformance/context/context-lost-restored.html
+++ b/content/canvas/test/webgl-conformance/conformance/context/context-lost-restored.html
@@ -94,17 +94,17 @@ function testLosingAndRestoringContext()
     debug("");
     debug("Test losing and restoring a context.");
 
     canvas.addEventListener("webglcontextlost", function(e) {
       testLostContext(e);
       // restore the context after this event has exited.
       setTimeout(function() {
         shouldGenerateGLError(gl, gl.NO_ERROR, "extension.restoreContext()");
-        // The context should still be lost. It will not get restored until the 
+        // The context should still be lost. It will not get restored until the
         // webglrestorecontext event is fired.
         shouldBeTrue("gl.isContextLost()");
         shouldBe("gl.getError()", "gl.NO_ERROR");
         // gl methods should still be no-ops
         shouldGenerateGLError(gl, gl.NO_ERROR, "gl.blendFunc(gl.TEXTURE_2D, gl.TEXTURE_CUBE_MAP)");
       }, 0);
     });
     canvas.addEventListener("webglcontextrestored", function() {
--- a/content/html/content/src/HTMLMediaElement.cpp
+++ b/content/html/content/src/HTMLMediaElement.cpp
@@ -3059,16 +3059,24 @@ void HTMLMediaElement::UpdateReadyStateF
   if (mReadyState < nsIDOMHTMLMediaElement::HAVE_METADATA) {
     // aNextFrame might have a next frame because the decoder can advance
     // on its own thread before ResourceLoaded or MetadataLoaded gets
     // a chance to run.
     // The arrival of more data can't change us out of this readyState.
     return;
   }
 
+  // Section 2.4.3.1 of the Media Source Extensions spec requires
+  // changing to HAVE_METADATA when seeking into an unbuffered
+  // range.
+  if (aNextFrame == MediaDecoderOwner::NEXT_FRAME_WAIT_FOR_MSE_DATA) {
+    ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_METADATA);
+    return;
+  }
+
   if (mReadyState > nsIDOMHTMLMediaElement::HAVE_METADATA &&
       mDownloadSuspendedByCache &&
       mDecoder &&
       !mDecoder->IsEnded()) {
     // The decoder has signalled that the download has been suspended by the
     // media cache. So move readyState into HAVE_ENOUGH_DATA, in case there's
     // script waiting for a "canplaythrough" event; without this forced
     // transition, we will never fire the "canplaythrough" event if the
--- a/content/media/MediaDecoderOwner.h
+++ b/content/media/MediaDecoderOwner.h
@@ -114,16 +114,19 @@ public:
   enum NextFrameStatus {
     // The next frame of audio/video is available
     NEXT_FRAME_AVAILABLE,
     // The next frame of audio/video is unavailable because the decoder
     // is paused while it buffers up data
     NEXT_FRAME_UNAVAILABLE_BUFFERING,
     // The next frame of audio/video is unavailable for some other reasons
     NEXT_FRAME_UNAVAILABLE,
+    // The next frame is unavailable due to waiting for more Media Source
+    // Extensions data to become available.
+    NEXT_FRAME_WAIT_FOR_MSE_DATA,
     // Sentinel value
     NEXT_FRAME_UNINITIALIZED
   };
 
   // Called by the decoder when some data has been downloaded or
   // buffering/seeking has ended. aNextFrameAvailable is true when
   // the data for the next frame is available. This method will
   // decide whether to set the ready state to HAVE_CURRENT_DATA,
--- a/content/media/gtest/TestWebMWriter.cpp
+++ b/content/media/gtest/TestWebMWriter.cpp
@@ -147,17 +147,16 @@ TEST(WebMWriter, Cluster)
   writer.SetVP8Metadata(width, height, displayWidth,
                         displayHeight, aTrackRate);
 
   nsTArray<nsTArray<uint8_t> > encodedBuf;
   writer.GetContainerData(&encodedBuf, ContainerWriter::GET_HEADER);
   EXPECT_TRUE(encodedBuf.Length() > 0);
   encodedBuf.Clear();
 
-  uint64_t timestamp = 0;
   // write the first I-Frame.
   writer.AppendDummyFrame(EncodedFrame::VP8_I_FRAME, FIXED_DURATION);
   // No data because the cluster is not closed.
   EXPECT_FALSE(writer.HaveValidCluster());
 
   // The second I-Frame.
   writer.AppendDummyFrame(EncodedFrame::VP8_I_FRAME, FIXED_DURATION);
   // Should have data because the first cluster is closed.
@@ -186,17 +185,16 @@ TEST(WebMWriter, FLUSH_NEEDED)
   int32_t width = 176;
   int32_t height = 352;
   int32_t displayWidth = 176;
   int32_t displayHeight = 352;
   TrackRate aTrackRate = 100000;
   writer.SetVP8Metadata(width, height, displayWidth,
                         displayHeight, aTrackRate);
 
-  uint64_t timestamp = 0;
   // write the first I-Frame.
   writer.AppendDummyFrame(EncodedFrame::VP8_I_FRAME, FIXED_DURATION);
 
   // P-Frame
   writer.AppendDummyFrame(EncodedFrame::VP8_P_FRAME, FIXED_DURATION);
   // Have data because the metadata is finished.
   EXPECT_TRUE(writer.HaveValidCluster());
   // No data because the cluster is not closed and the metatdata had been
--- a/content/media/gtest/moz.build
+++ b/content/media/gtest/moz.build
@@ -21,8 +21,10 @@ EXPORT_LIBRARY = True
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 LOCAL_INCLUDES += [
     '/content/media/encoder',
 ]
 
 FINAL_LIBRARY = 'xul-gtest'
+
+FAIL_ON_WARNINGS = True
--- a/content/media/mediasource/MediaSource.cpp
+++ b/content/media/mediasource/MediaSource.cpp
@@ -294,16 +294,17 @@ MediaSource::Detach()
   SetReadyState(MediaSourceReadyState::Closed);
 }
 
 MediaSource::MediaSource(nsPIDOMWindow* aWindow)
   : DOMEventTargetHelper(aWindow)
   , mDuration(UnspecifiedNaN<double>())
   , mDecoder(nullptr)
   , mReadyState(MediaSourceReadyState::Closed)
+  , mWaitForDataMonitor("MediaSource.WaitForData.Monitor")
 {
   mSourceBuffers = new SourceBufferList(this);
   mActiveSourceBuffers = new SourceBufferList(this);
 
 #ifdef PR_LOGGING
   if (!gMediaSourceLog) {
     gMediaSourceLog = PR_NewLogModule("MediaSource");
   }
@@ -390,16 +391,30 @@ MediaSource::WrapObject(JSContext* aCx)
 void
 MediaSource::NotifyEvicted(double aStart, double aEnd)
 {
   // Cycle through all SourceBuffers and tell them to evict data in
   // the given range.
   mSourceBuffers->Evict(aStart, aEnd);
 }
 
+void
+MediaSource::WaitForData()
+{
+  MonitorAutoLock lock(mWaitForDataMonitor);
+  lock.Wait();
+}
+
+void
+MediaSource::NotifyGotData()
+{
+  MonitorAutoLock lock(mWaitForDataMonitor);
+  lock.NotifyAll();
+}
+
 NS_IMPL_CYCLE_COLLECTION_INHERITED(MediaSource, DOMEventTargetHelper,
                                    mSourceBuffers, mActiveSourceBuffers)
 
 NS_IMPL_ADDREF_INHERITED(MediaSource, DOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(MediaSource, DOMEventTargetHelper)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(MediaSource)
   NS_INTERFACE_MAP_ENTRY(mozilla::dom::MediaSource)
--- a/content/media/mediasource/MediaSource.h
+++ b/content/media/mediasource/MediaSource.h
@@ -8,16 +8,17 @@
 #define mozilla_dom_MediaSource_h_
 
 #include "MediaSourceDecoder.h"
 #include "js/RootingAPI.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/DOMEventTargetHelper.h"
 #include "mozilla/dom/MediaSourceBinding.h"
+#include "mozilla/Monitor.h"
 #include "nsAutoPtr.h"
 #include "nsCOMPtr.h"
 #include "nsCycleCollectionNoteChild.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsID.h"
 #include "nsISupports.h"
 #include "nscore.h"
 
@@ -84,16 +85,22 @@ public:
     return mDecoder;
   }
 
   // Called by SourceBuffers to notify this MediaSource that data has
   // been evicted from the buffered data. The start and end times
   // that were evicted are provided.
   void NotifyEvicted(double aStart, double aEnd);
 
+  // Block thread waiting for data to be appended to a SourceBuffer.
+  void WaitForData();
+
+  // Unblock threads waiting for data to be appended to a SourceBuffer.
+  void NotifyGotData();
+
 private:
   explicit MediaSource(nsPIDOMWindow* aWindow);
 
   friend class AsyncEventRunner<MediaSource>;
   void DispatchSimpleEvent(const char* aName);
   void QueueAsyncSimpleEvent(const char* aName);
 
   void DurationChange(double aNewDuration, ErrorResult& aRv);
@@ -101,16 +108,20 @@ private:
   double mDuration;
 
   nsRefPtr<SourceBufferList> mSourceBuffers;
   nsRefPtr<SourceBufferList> mActiveSourceBuffers;
 
   nsRefPtr<MediaSourceDecoder> mDecoder;
 
   MediaSourceReadyState mReadyState;
+
+  // Monitor for waiting for when new data is appended to
+  // a Source Buffer.
+  Monitor mWaitForDataMonitor;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(MediaSource, MOZILLA_DOM_MEDIASOURCE_IMPLEMENTATION_IID)
 
 } // namespace dom
 
 } // namespace mozilla
 #endif /* mozilla_dom_MediaSource_h_ */
--- a/content/media/mediasource/MediaSourceDecoder.cpp
+++ b/content/media/mediasource/MediaSourceDecoder.cpp
@@ -125,16 +125,21 @@ public:
   nsresult Seek(int64_t aTime, int64_t aStartTime, int64_t aEndTime,
                 int64_t aCurrentTime) MOZ_OVERRIDE;
   nsresult GetBuffered(dom::TimeRanges* aBuffered, int64_t aStartTime) MOZ_OVERRIDE;
   already_AddRefed<SubBufferDecoder> CreateSubDecoder(const nsACString& aType,
                                                       MediaSourceDecoder* aParentDecoder);
 
   void InitializePendingDecoders();
 
+  bool IsShutdown() {
+    ReentrantMonitorAutoEnter decoderMon(mDecoder->GetReentrantMonitor());
+    return mDecoder->IsShutdown();
+  }
+
 private:
   bool MaybeSwitchVideoReaders(int64_t aTimeThreshold) {
     ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
     MOZ_ASSERT(mActiveVideoDecoder != -1);
 
     InitializePendingDecoders();
 
     for (uint32_t i = mActiveVideoDecoder + 1; i < mDecoders.Length(); ++i) {
@@ -378,35 +383,70 @@ MediaSourceReader::CreateSubDecoder(cons
   if (NS_FAILED(static_cast<MediaSourceDecoder*>(mDecoder)->EnqueueDecoderInitialization())) {
     MSE_DEBUG("%p: Failed to enqueue decoder initialization task", this);
     return nullptr;
   }
   mDecoder->NotifyWaitingForResourcesStatusChanged();
   return decoder.forget();
 }
 
+namespace {
+class ChangeToHaveMetadata : public nsRunnable {
+public:
+  ChangeToHaveMetadata(AbstractMediaDecoder* aDecoder) :
+    mDecoder(aDecoder)
+  {
+  }
+
+  NS_IMETHOD Run() MOZ_OVERRIDE MOZ_FINAL {
+    auto owner = mDecoder->GetOwner();
+    if (owner) {
+      owner->UpdateReadyStateForData(MediaDecoderOwner::NEXT_FRAME_WAIT_FOR_MSE_DATA);
+    }
+    return NS_OK;
+  }
+
+private:
+  nsRefPtr<AbstractMediaDecoder> mDecoder;
+};
+}
+
 nsresult
 MediaSourceReader::Seek(int64_t aTime, int64_t aStartTime, int64_t aEndTime,
                         int64_t aCurrentTime)
 {
-  ResetDecode();
+  if (!mMediaSource->ActiveSourceBuffers()->AllContainsTime (aTime / USECS_PER_S)) {
+    NS_DispatchToMainThread(new ChangeToHaveMetadata(mDecoder));
+  }
+
+  // Loop until we have the requested time range in the source buffers.
+  // This is a workaround for our lack of async functionality in the
+  // MediaDecoderStateMachine. Bug 979104 implements what we need and
+  // we'll remove this for an async approach based on that in bug XXXXXXX.
+  while (!mMediaSource->ActiveSourceBuffers()->AllContainsTime (aTime / USECS_PER_S)
+         && !IsShutdown()) {
+    mMediaSource->WaitForData();
+    MaybeSwitchVideoReaders(aTime);
+  }
 
-  dom::SourceBufferList* sbl = mMediaSource->ActiveSourceBuffers();
-  if (sbl->AllContainsTime (aTime / USECS_PER_S)) {
-    if (GetAudioReader()) {
-      nsresult rv = GetAudioReader()->Seek(aTime, aStartTime, aEndTime, aCurrentTime);
-      if (NS_FAILED(rv)) {
-        return rv;
-      }
+  if (IsShutdown()) {
+    return NS_OK;
+  }
+
+  ResetDecode();
+  if (GetAudioReader()) {
+    nsresult rv = GetAudioReader()->Seek(aTime, aStartTime, aEndTime, aCurrentTime);
+    if (NS_FAILED(rv)) {
+      return rv;
     }
-    if (GetVideoReader()) {
-      nsresult rv = GetVideoReader()->Seek(aTime, aStartTime, aEndTime, aCurrentTime);
-      if (NS_FAILED(rv)) {
-        return rv;
-      }
+  }
+  if (GetVideoReader()) {
+    nsresult rv = GetVideoReader()->Seek(aTime, aStartTime, aEndTime, aCurrentTime);
+     if (NS_FAILED(rv)) {
+      return rv;
     }
   }
   return NS_OK;
 }
 
 nsresult
 MediaSourceReader::GetBuffered(dom::TimeRanges* aBuffered, int64_t aStartTime)
 {
--- a/content/media/mediasource/SourceBuffer.cpp
+++ b/content/media/mediasource/SourceBuffer.cpp
@@ -488,16 +488,18 @@ SourceBuffer::AppendData(const uint8_t* 
     // the current start point.
     mMediaSource->NotifyEvicted(0.0, start);
   }
   StopUpdating();
 
   // Schedule the state machine thread to ensure playback starts
   // if required when data is appended.
   mMediaSource->GetDecoder()->ScheduleStateMachineThread();
+
+  mMediaSource->NotifyGotData();
 }
 
 void
 SourceBuffer::GetBufferedStartEndTime(double* aStart, double* aEnd)
 {
   ErrorResult dummy;
   nsRefPtr<TimeRanges> ranges = GetBuffered(dummy);
   if (!ranges || ranges->Length() == 0) {
--- a/content/media/webm/WebMWriter.cpp
+++ b/content/media/webm/WebMWriter.cpp
@@ -9,16 +9,21 @@
 namespace mozilla {
 
 WebMWriter::WebMWriter(uint32_t aTrackTypes) : ContainerWriter()
 {
   mMetadataRequiredFlag = aTrackTypes;
   mEbmlComposer = new EbmlComposer();
 }
 
+WebMWriter::~WebMWriter()
+{
+  // Out-of-line dtor so mEbmlComposer nsAutoPtr can delete a complete type.
+}
+
 nsresult
 WebMWriter::WriteEncodedTrack(const EncodedFrameContainer& aData,
                               uint32_t aFlags)
 {
   for (uint32_t i = 0 ; i < aData.GetEncodedFrames().Length(); i++) {
     mEbmlComposer->WriteSimpleBlock(aData.GetEncodedFrames().ElementAt(i).get());
   }
   return NS_OK;
--- a/content/media/webm/WebMWriter.h
+++ b/content/media/webm/WebMWriter.h
@@ -42,16 +42,18 @@ public:
  * The ctor/dtor run in the MediaRecorder thread, others run in MediaEncoder thread.
  */
 class WebMWriter : public ContainerWriter
 {
 public:
   // aTrackTypes indicate this muxer should multiplex into Video only or A/V foramt.
   // Run in MediaRecorder thread
   WebMWriter(uint32_t aTrackTypes);
+  virtual ~WebMWriter();
+
   // WriteEncodedTrack inserts raw packets into WebM stream.
   nsresult WriteEncodedTrack(const EncodedFrameContainer &aData,
                              uint32_t aFlags = 0) MOZ_OVERRIDE;
 
   // GetContainerData outputs multiplexing data.
   // aFlags indicates the muxer should enter into finished stage and flush out
   // queue data.
   nsresult GetContainerData(nsTArray<nsTArray<uint8_t> >* aOutputBufs,
--- a/dom/base/nsFocusManager.cpp
+++ b/dom/base/nsFocusManager.cpp
@@ -2161,35 +2161,34 @@ nsFocusManager::SetCaretVisible(nsIPresS
 
   nsRefPtr<nsFrameSelection> docFrameSelection = aPresShell->FrameSelection();
 
   if (docFrameSelection && caret &&
      (frameSelection == docFrameSelection || !aContent)) {
     nsISelection* domSelection = docFrameSelection->
       GetSelection(nsISelectionController::SELECTION_NORMAL);
     if (domSelection) {
+      nsCOMPtr<nsISelectionController> selCon(do_QueryInterface(aPresShell));
+      if (!selCon) {
+        return NS_ERROR_FAILURE;
+      }
       // First, hide the caret to prevent attempting to show it in SetCaretDOMSelection
-      caret->SetCaretVisible(false);
+      selCon->SetCaretEnabled(false);
 
       // Caret must blink on non-editable elements
       caret->SetIgnoreUserModify(true);
       // Tell the caret which selection to use
       caret->SetCaretDOMSelection(domSelection);
 
       // In content, we need to set the caret. The only special case is edit
       // fields, which have a different frame selection from the document.
       // They will take care of making the caret visible themselves.
 
-      nsCOMPtr<nsISelectionController> selCon(do_QueryInterface(aPresShell));
-      if (!selCon)
-        return NS_ERROR_FAILURE;
-
       selCon->SetCaretReadOnly(false);
       selCon->SetCaretEnabled(aVisible);
-      caret->SetCaretVisible(aVisible);
     }
   }
 
   return NS_OK;
 }
 
 nsresult
 nsFocusManager::GetSelectionLocation(nsIDocument* aDocument,
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -565,17 +565,17 @@ nsTimeout::HasRefCntOne()
   return mRefCnt.get() == 1;
 }
 
 nsPIDOMWindow::nsPIDOMWindow(nsPIDOMWindow *aOuterWindow)
 : mFrameElement(nullptr), mDocShell(nullptr), mModalStateDepth(0),
   mRunningTimeout(nullptr), mMutationBits(0), mIsDocumentLoaded(false),
   mIsHandlingResizeEvent(false), mIsInnerWindow(aOuterWindow != nullptr),
   mMayHavePaintEventListener(false), mMayHaveTouchEventListener(false),
-  mMayHaveMouseEnterLeaveEventListener(false),
+  mMayHaveTouchCaret(false), mMayHaveMouseEnterLeaveEventListener(false),
   mMayHavePointerEnterLeaveEventListener(false),
   mIsModalContentWindow(false),
   mIsActive(false), mIsBackground(false),
   mAudioMuted(false), mAudioVolume(1.0),
   mInnerWindow(nullptr), mOuterWindow(aOuterWindow),
   // Make sure no actual window ends up with mWindowID == 0
   mWindowID(++gNextWindowID), mHasNotifiedGlobalCreated(false),
   mMarkedCCGeneration(0)
--- a/dom/base/nsPIDOMWindow.h
+++ b/dom/base/nsPIDOMWindow.h
@@ -55,18 +55,18 @@ enum PopupControlState {
 enum UIStateChangeType
 {
   UIStateChangeType_NoChange,
   UIStateChangeType_Set,
   UIStateChangeType_Clear
 };
 
 #define NS_PIDOMWINDOW_IID \
-{ 0xf26953de, 0xa799, 0x4a92, \
-  { 0x87, 0x49, 0x7c, 0x37, 0xe5, 0x90, 0x3f, 0x37 } }
+{ 0x33403513, 0x6e4a, 0x4985, \
+  { 0x99, 0x8d, 0xfc, 0x02, 0x81, 0x6e, 0xb9, 0xf2 } }
 
 class nsPIDOMWindow : public nsIDOMWindowInternal
 {
 public:
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_PIDOMWINDOW_IID)
 
   virtual nsPIDOMWindow* GetPrivateRoot() = 0;
 
@@ -463,16 +463,31 @@ public:
     MaybeUpdateTouchState();
   }
 
   bool HasTouchEventListeners()
   {
     return mMayHaveTouchEventListener;
   }
 
+   /**
+   * Will be called when touch caret visibility has changed. mMayHaveTouchCaret
+   * is set if that some node (this window, its document, or content in that
+   * document) has a visible touch caret.
+   */
+  void SetMayHaveTouchCaret(bool aSetValue)
+  {
+    mMayHaveTouchCaret = aSetValue;
+  }
+
+  bool MayHaveTouchCaret()
+  {
+    return mMayHaveTouchCaret;
+  }
+
   /**
    * Moves the top-level window into fullscreen mode if aIsFullScreen is true,
    * otherwise exits fullscreen. If aRequireTrust is true, this method only
    * changes window state in a context trusted for write.
    */
   virtual nsresult SetFullScreenInternal(bool aIsFullScreen, bool aRequireTrust) = 0;
 
   /**
@@ -756,16 +771,17 @@ protected:
 
   uint32_t               mMutationBits;
 
   bool                   mIsDocumentLoaded;
   bool                   mIsHandlingResizeEvent;
   bool                   mIsInnerWindow;
   bool                   mMayHavePaintEventListener;
   bool                   mMayHaveTouchEventListener;
+  bool                   mMayHaveTouchCaret;
   bool                   mMayHaveMouseEnterLeaveEventListener;
   bool                   mMayHavePointerEnterLeaveEventListener;
 
   // This variable is used on both inner and outer windows (and they
   // should match).
   bool                   mIsModalContentWindow;
 
   // Tracks activation state that's used for :-moz-window-inactive.
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -1400,17 +1400,17 @@ WrapNativeParent(JSContext* cx, T* p, ns
 
   JSObject* parent = WrapNativeParentHelper<T>::Wrap(cx, p, cache);
   if (!useXBLScope) {
     return parent;
   }
 
   // If useXBLScope is true, it means that the canonical reflector for this
   // native object should live in the XBL scope.
-  if (xpc::IsInXBLScope(parent)) {
+  if (xpc::IsInContentXBLScope(parent)) {
     return parent;
   }
   JS::Rooted<JSObject*> rootedParent(cx, parent);
   JS::Rooted<JSObject*> xblScope(cx, xpc::GetXBLScope(cx, rootedParent));
   JSAutoCompartment ac(cx, xblScope);
   if (NS_WARN_IF(!JS_WrapObject(cx, &rootedParent))) {
     return nullptr;
   }
--- a/dom/bluetooth/ipc/BluetoothParent.cpp
+++ b/dom/bluetooth/ipc/BluetoothParent.cpp
@@ -19,17 +19,17 @@
 
 using mozilla::unused;
 USING_BLUETOOTH_NAMESPACE
 
 /*******************************************************************************
  * BluetoothRequestParent::ReplyRunnable
  ******************************************************************************/
 
-class BluetoothRequestParent::ReplyRunnable : public BluetoothReplyRunnable
+class BluetoothRequestParent::ReplyRunnable MOZ_FINAL : public BluetoothReplyRunnable
 {
   BluetoothRequestParent* mRequest;
 
 public:
   ReplyRunnable(BluetoothRequestParent* aRequest)
   : BluetoothReplyRunnable(nullptr), mRequest(aRequest)
   {
     MOZ_ASSERT(NS_IsMainThread());
@@ -54,25 +54,32 @@ public:
 
     ReleaseMembers();
     return NS_OK;
   }
 
   void
   Revoke()
   {
-    MOZ_ASSERT(NS_IsMainThread());
-    mRequest = nullptr;
+    ReleaseMembers();
   }
 
   virtual bool
   ParseSuccessfulReply(JS::MutableHandle<JS::Value> aValue) MOZ_OVERRIDE
   {
     MOZ_CRASH("This should never be called!");
   }
+
+  virtual void
+  ReleaseMembers() MOZ_OVERRIDE
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    mRequest = nullptr;
+    BluetoothReplyRunnable::ReleaseMembers();
+  }
 };
 
 /*******************************************************************************
  * BluetoothParent
  ******************************************************************************/
 
 BluetoothParent::BluetoothParent()
 : mShutdownState(Running)
--- a/dom/identity/Identity.manifest
+++ b/dom/identity/Identity.manifest
@@ -1,9 +1,8 @@
 # nsDOMIdentity.js
 component {210853d9-2c97-4669-9761-b1ab9cbf57ef} nsDOMIdentity.js
-contract @mozilla.org/dom/identity;1 {210853d9-2c97-4669-9761-b1ab9cbf57ef}
-category JavaScript-navigator-property mozId @mozilla.org/dom/identity;1
+contract @mozilla.org/identity/manager;1 {210853d9-2c97-4669-9761-b1ab9cbf57ef}
 
 # nsIDService.js (initialization on startup)
 component {4e0a0e98-b1d3-4745-a1eb-f815199dd06b} nsIDService.js
 contract @mozilla.org/dom/identity/service;1 {4e0a0e98-b1d3-4745-a1eb-f815199dd06b}
 category app-startup IDService @mozilla.org/dom/identity/service;1
--- a/dom/identity/nsDOMIdentity.js
+++ b/dom/identity/nsDOMIdentity.js
@@ -43,39 +43,20 @@ const ERRORS = {
   "ERROR_NOT_AUTHORIZED_FOR_FIREFOX_ACCOUNTS":
     "Only privileged and certified apps may use Firefox Accounts",
   "ERROR_INVALID_ASSERTION_AUDIENCE":
     "Assertion audience may not differ from origin",
   "ERROR_REQUEST_WHILE_NOT_HANDLING_USER_INPUT":
     "The request() method may only be invoked when handling user input",
 };
 
-function nsDOMIdentity(aIdentityInternal) {
-  this._identityInternal = aIdentityInternal;
+function nsDOMIdentity() {
 }
-nsDOMIdentity.prototype = {
-  __exposedProps__: {
-    // Relying Party (RP)
-    watch: 'r',
-    request: 'r',
-    logout: 'r',
-    get: 'r',
-    getVerifiedEmail: 'r',
 
-    // Provisioning
-    beginProvisioning: 'r',
-    genKeyPair: 'r',
-    registerCertificate: 'r',
-    raiseProvisioningFailure: 'r',
-
-    // Authentication
-    beginAuthentication: 'r',
-    completeAuthentication: 'r',
-    raiseAuthenticationFailure: 'r'
-  },
+nsDOMIdentity.prototype = {
 
   // require native events unless syntheticEventsOk is set
   get nativeEventsRequired() {
     if (Services.prefs.prefHasUserValue(PREF_SYNTHETIC_EVENTS_OK) &&
         (Services.prefs.getPrefType(PREF_SYNTHETIC_EVENTS_OK) ===
          Ci.nsIPrefBranch.PREF_BOOL)) {
       return !Services.prefs.getBoolPref(PREF_SYNTHETIC_EVENTS_OK);
     }
@@ -144,17 +125,17 @@ nsDOMIdentity.prototype = {
     this._rpWatcher.audience = message.audience;
 
     if (message.errors.length) {
       this.reportErrors(message);
       // We don't delete the rpWatcher object, because we don't want the
       // broken client to be able to call watch() any more.  It's broken.
       return;
     }
-    this._identityInternal._mm.sendAsyncMessage("Identity:RP:Watch", message);
+    this._mm.sendAsyncMessage("Identity:RP:Watch", message);
   },
 
   request: function nsDOMIdentity_request(aOptions = {}) {
     this._log("request: " + JSON.stringify(aOptions));
 
     // Has the caller called watch() before this?
     if (!this._rpWatcher) {
       throw new Error("navigator.id.request called before navigator.id.watch");
@@ -214,17 +195,17 @@ nsDOMIdentity.prototype = {
         throw new Error("oncancel is not a function");
       } else {
         // Store optional cancel callback for later.
         this._onCancelRequestCallback = aOptions.oncancel;
       }
     }
 
     this._rpCalls++;
-    this._identityInternal._mm.sendAsyncMessage("Identity:RP:Request", message);
+    this._mm.sendAsyncMessage("Identity:RP:Request", message);
   },
 
   logout: function nsDOMIdentity_logout() {
     if (!this._rpWatcher) {
       throw new Error("navigator.id.logout called before navigator.id.watch");
     }
     if (this._rpCalls > MAX_RP_CALLS) {
       throw new Error("navigator.id.logout called too many times");
@@ -234,17 +215,17 @@ nsDOMIdentity.prototype = {
     let message = this.DOMIdentityMessage();
 
     // Report and fail hard on any errors.
     if (message.errors.length) {
       this.reportErrors(message);
       return;
     }
 
-    this._identityInternal._mm.sendAsyncMessage("Identity:RP:Logout", message);
+    this._mm.sendAsyncMessage("Identity:RP:Logout", message);
   },
 
   /*
    * Get an assertion.  This function is deprecated.  RPs are
    * encouraged to use the observer API instead (watch + request).
    */
   get: function nsDOMIdentity_get(aCallback, aOptions) {
     var opts = {};
@@ -317,65 +298,65 @@ nsDOMIdentity.prototype = {
     if (this._beginProvisioningCallback) {
       throw new Error("navigator.id.beginProvisioning already called.");
     }
     if (!aCallback || typeof(aCallback) !== "function") {
       throw new Error("beginProvisioning callback is required.");
     }
 
     this._beginProvisioningCallback = aCallback;
-    this._identityInternal._mm.sendAsyncMessage("Identity:IDP:BeginProvisioning",
+    this._mm.sendAsyncMessage("Identity:IDP:BeginProvisioning",
                                                 this.DOMIdentityMessage());
   },
 
   genKeyPair: function nsDOMIdentity_genKeyPair(aCallback) {
     this._log("genKeyPair");
     if (!this._beginProvisioningCallback) {
       throw new Error("navigator.id.genKeyPair called outside of provisioning");
     }
     if (this._genKeyPairCallback) {
       throw new Error("navigator.id.genKeyPair already called.");
     }
     if (!aCallback || typeof(aCallback) !== "function") {
       throw new Error("genKeyPair callback is required.");
     }
 
     this._genKeyPairCallback = aCallback;
-    this._identityInternal._mm.sendAsyncMessage("Identity:IDP:GenKeyPair",
+    this._mm.sendAsyncMessage("Identity:IDP:GenKeyPair",
                                                 this.DOMIdentityMessage());
   },
 
   registerCertificate: function nsDOMIdentity_registerCertificate(aCertificate) {
     this._log("registerCertificate");
     if (!this._genKeyPairCallback) {
       throw new Error("navigator.id.registerCertificate called outside of provisioning");
     }
     if (this._provisioningEnded) {
       throw new Error("Provisioning already ended");
     }
     this._provisioningEnded = true;
 
     let message = this.DOMIdentityMessage();
     message.cert = aCertificate;
-    this._identityInternal._mm.sendAsyncMessage("Identity:IDP:RegisterCertificate", message);
+    this._mm.sendAsyncMessage("Identity:IDP:RegisterCertificate", message);
   },
 
   raiseProvisioningFailure: function nsDOMIdentity_raiseProvisioningFailure(aReason) {
     this._log("raiseProvisioningFailure '" + aReason + "'");
     if (this._provisioningEnded) {
       throw new Error("Provisioning already ended");
     }
     if (!aReason || typeof(aReason) != "string") {
       throw new Error("raiseProvisioningFailure reason is required");
     }
     this._provisioningEnded = true;
 
     let message = this.DOMIdentityMessage();
     message.reason = aReason;
-    this._identityInternal._mm.sendAsyncMessage("Identity:IDP:ProvisioningFailure", message);
+    this._mm.sendAsyncMessage("Identity:IDP:ProvisioningFailure", message);
   },
 
   /**
    *  Identity Provider (IDP) Authentication APIs
    */
 
   beginAuthentication: function nsDOMIdentity_beginAuthentication(aCallback) {
     this._log("beginAuthentication");
@@ -385,64 +366,44 @@ nsDOMIdentity.prototype = {
     if (typeof(aCallback) !== "function") {
       throw new Error("beginAuthentication callback is required.");
     }
     if (!aCallback || typeof(aCallback) !== "function") {
       throw new Error("beginAuthentication callback is required.");
     }
 
     this._beginAuthenticationCallback = aCallback;
-    this._identityInternal._mm.sendAsyncMessage("Identity:IDP:BeginAuthentication",
+    this._mm.sendAsyncMessage("Identity:IDP:BeginAuthentication",
                                                 this.DOMIdentityMessage());
   },
 
   completeAuthentication: function nsDOMIdentity_completeAuthentication() {
     if (this._authenticationEnded) {
       throw new Error("Authentication already ended");
     }
     if (!this._beginAuthenticationCallback) {
       throw new Error("navigator.id.completeAuthentication called outside of authentication");
     }
     this._authenticationEnded = true;
 
-    this._identityInternal._mm.sendAsyncMessage("Identity:IDP:CompleteAuthentication",
+    this._mm.sendAsyncMessage("Identity:IDP:CompleteAuthentication",
                                                 this.DOMIdentityMessage());
   },
 
   raiseAuthenticationFailure: function nsDOMIdentity_raiseAuthenticationFailure(aReason) {
     if (this._authenticationEnded) {
       throw new Error("Authentication already ended");
     }
     if (!aReason || typeof(aReason) != "string") {
       throw new Error("raiseProvisioningFailure reason is required");
     }
 
     let message = this.DOMIdentityMessage();
     message.reason = aReason;
-    this._identityInternal._mm.sendAsyncMessage("Identity:IDP:AuthenticationFailure", message);
-  },
-
-  // Private.
-  _init: function nsDOMIdentity__init(aWindow) {
-
-    this._initializeState();
-
-    // Store window and origin URI.
-    this._window = aWindow;
-    this._origin = aWindow.document.nodePrincipal.origin;
-    this._appStatus = aWindow.document.nodePrincipal.appStatus;
-    this._appId = aWindow.document.nodePrincipal.appId;
-
-    // Setup identifiers for current window.
-    let util = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
-                      .getInterface(Ci.nsIDOMWindowUtils);
-
-    // We need to inherit the id from the internalIdentity service.
-    // See comments below in that service's init.
-    this._id = this._identityInternal._id;
+    this._mm.sendAsyncMessage("Identity:IDP:AuthenticationFailure", message);
   },
 
   /**
    * Called during init and shutdown.
    */
   _initializeState: function nsDOMIdentity__initializeState() {
     // Some state to prevent abuse
     // Limit the number of calls to .request
@@ -452,22 +413,28 @@ nsDOMIdentity.prototype = {
 
     this._rpWatcher = null;
     this._onCancelRequestCallback = null;
     this._beginProvisioningCallback = null;
     this._genKeyPairCallback = null;
     this._beginAuthenticationCallback = null;
   },
 
-  _receiveMessage: function nsDOMIdentity_receiveMessage(aMessage) {
+  // nsIMessageListener
+  receiveMessage: function nsDOMIdentity_receiveMessage(aMessage) {
     let msg = aMessage.json;
 
+    // Is this message intended for this window?
+    if (msg.id != this._id) {
+      return;
+    }
+
     switch (aMessage.name) {
       case "Identity:ResetState":
-        if (!this._identityInternal._debug) {
+        if (!this._debug) {
           return;
         }
         this._initializeState();
         Services.obs.notifyObservers(null, "identity-DOM-state-reset", this._id);
         break;
       case "Identity:RP:Watch:OnLogin":
         // Do we have a watcher?
         if (!this._rpWatcher) {
@@ -533,20 +500,16 @@ nsDOMIdentity.prototype = {
         this._callGenKeyPairCallback(msg);
         break;
       case "Identity:IDP:CallBeginAuthenticationCallback":
         this._callBeginAuthenticationCallback(msg);
         break;
     }
   },
 
-  _log: function nsDOMIdentity__log(msg) {
-    this._identityInternal._log(msg);
-  },
-
   _callGenKeyPairCallback: function nsDOMIdentity__callGenKeyPairCallback(message) {
     // create a pubkey object that works
     let chrome_pubkey = JSON.parse(message.publicKey);
 
     // bunch of stuff to create a proper object in window context
     function genPropDesc(value) {
       return {
         enumerable: true, configurable: true, writable: true, value: value
@@ -637,71 +600,49 @@ nsDOMIdentity.prototype = {
     // Replace any audience supplied by the RP with one that has been sanitised
     message.audience = _audience;
 
     this._log("DOMIdentityMessage: " + JSON.stringify(message));
 
     return message;
   },
 
-  uninit: function DOMIdentity_uninit() {
-    this._log("nsDOMIdentity uninit() " + this._id);
-    this._identityInternal._mm.sendAsyncMessage(
-      "Identity:RP:Unwatch",
-      { id: this._id }
-    );
-  }
-
-};
-
-/**
- * Internal functions that shouldn't be exposed to content.
- */
-function nsDOMIdentityInternal() {
-}
-nsDOMIdentityInternal.prototype = {
-
-  // nsIMessageListener
-  receiveMessage: function nsDOMIdentityInternal_receiveMessage(aMessage) {
-    let msg = aMessage.json;
-    // Is this message intended for this window?
-    if (msg.id != this._id) {
-      return;
-    }
-    this._identity._receiveMessage(aMessage);
-  },
+  /*
+   * Internal methods that are not exposed to content.
+   * See dom/webidl/Identity.webidl for the public interface.
+   */
 
   // nsIObserver
   observe: function nsDOMIdentityInternal_observe(aSubject, aTopic, aData) {
     let wId = aSubject.QueryInterface(Ci.nsISupportsPRUint64).data;
     if (wId != this._innerWindowID) {
       return;
     }
 
-    this._identity.uninit();
+    this.uninit();
 
     Services.obs.removeObserver(this, "inner-window-destroyed");
-    this._identity._initializeState();
-    this._identity = null;
+    this._initializeState();
 
     // TODO: Also send message to DOMIdentity notifiying window is no longer valid
     // ie. in the case that the user closes the auth. window and we need to know.
 
     try {
       for (let msgName of this._messages) {
         this._mm.removeMessageListener(msgName, this);
       }
     } catch (ex) {
       // Avoid errors when removing more than once.
     }
 
     this._mm = null;
   },
 
-  // nsIDOMGlobalPropertyInitializer
+  //  Because we implement nsIDOMGlobalPropertyInitializer, our init() method
+  //  is invoked with content window as its single argument.
   init: function nsDOMIdentityInternal_init(aWindow) {
     if (Services.prefs.getPrefType(PREF_ENABLED) != Ci.nsIPrefBranch.PREF_BOOL
         || !Services.prefs.getBoolPref(PREF_ENABLED)) {
       return null;
     }
 
     this._debug =
       Services.prefs.getPrefType(PREF_DEBUG) == Ci.nsIPrefBranch.PREF_BOOL
@@ -715,18 +656,27 @@ nsDOMIdentityInternal.prototype = {
     //
     // XXX Bug 869182 - use a combination of child process id and
     // innerwindow id to construct the unique id.
     this._id = uuidgen.generateUUID().toString();
     this._innerWindowID = util.currentInnerWindowID;
 
     // nsDOMIdentity needs to know our _id, so this goes after
     // its creation.
-    this._identity = new nsDOMIdentity(this);
-    this._identity._init(aWindow);
+    this._initializeState();
+
+    // Store window and origin URI.
+    this._window = aWindow;
+    this._origin = aWindow.document.nodePrincipal.origin;
+    this._appStatus = aWindow.document.nodePrincipal.appStatus;
+    this._appId = aWindow.document.nodePrincipal.appId;
+
+    // Setup identifiers for current window.
+    let util = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
+                      .getInterface(Ci.nsIDOMWindowUtils);
 
     this._log("init was called from " + aWindow.document.location);
 
     this._mm = cpmm;
 
     // Setup listeners for messages from parent process.
     this._messages = [
       "Identity:ResetState",
@@ -740,34 +690,42 @@ nsDOMIdentityInternal.prototype = {
       "Identity:IDP:CallBeginAuthenticationCallback"
     ];
     this._messages.forEach(function(msgName) {
       this._mm.addMessageListener(msgName, this);
     }, this);
 
     // Setup observers so we can remove message listeners.
     Services.obs.addObserver(this, "inner-window-destroyed", false);
+  },
 
-    return this._identity;
+  uninit: function DOMIdentity_uninit() {
+    this._log("nsDOMIdentity uninit() " + this._id);
+    this._mm.sendAsyncMessage(
+      "Identity:RP:Unwatch",
+      { id: this._id }
+    );
   },
 
   // Private.
   _log: function nsDOMIdentityInternal__log(msg) {
     if (!this._debug) {
       return;
     }
     dump("nsDOMIdentity (" + this._id + "): " + msg + "\n");
   },
 
   // Component setup.
   classID: Components.ID("{210853d9-2c97-4669-9761-b1ab9cbf57ef}"),
 
-  QueryInterface: XPCOMUtils.generateQI(
-    [Ci.nsIDOMGlobalPropertyInitializer, Ci.nsIMessageListener]
-  ),
+  QueryInterface: XPCOMUtils.generateQI([
+      Ci.nsIMessageListener,
+      Ci.nsIObserver,
+      Ci.nsIDOMGlobalPropertyInitializer
+  ]),
 
   classInfo: XPCOMUtils.generateCI({
     classID: Components.ID("{210853d9-2c97-4669-9761-b1ab9cbf57ef}"),
     contractID: "@mozilla.org/dom/identity;1",
     interfaces: [],
     classDescription: "Identity DOM Implementation"
   })
 };
@@ -799,9 +757,9 @@ function assertCorrectCallbacks(aOptions
 
   for (let cbName of optionalCallbacks) {
     if (aOptions[cbName] && typeof(aOptions[cbName]) != "function") {
       throw new Error(cbName + " must be a function");
     }
   }
 }
 
-this.NSGetFactory = XPCOMUtils.generateNSGetFactory([nsDOMIdentityInternal]);
+this.NSGetFactory = XPCOMUtils.generateNSGetFactory([nsDOMIdentity]);
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -2004,17 +2004,18 @@ TabChild::RecvRealTouchEvent(const Widge
 
   if (aEvent.message == NS_TOUCH_START && localEvent.touches.Length() > 0) {
     mActiveElementManager->SetTargetElement(localEvent.touches[0]->GetTarget());
   }
 
   nsCOMPtr<nsPIDOMWindow> outerWindow = do_GetInterface(WebNavigation());
   nsCOMPtr<nsPIDOMWindow> innerWindow = outerWindow->GetCurrentInnerWindow();
 
-  if (!innerWindow || !innerWindow->HasTouchEventListeners()) {
+  if (!innerWindow || (!innerWindow->HasTouchEventListeners() &&
+                       !innerWindow->MayHaveTouchCaret())) {
     SendContentReceivedTouch(aGuid, false);
     return true;
   }
 
   bool isTouchPrevented = nsIPresShell::gPreventMouseEvents ||
                           localEvent.mFlags.mMultipleActionsPrevented;
   switch (aEvent.message) {
   case NS_TOUCH_START: {
--- a/dom/media/PeerConnection.js
+++ b/dom/media/PeerConnection.js
@@ -14,30 +14,33 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 
 const PC_CONTRACT = "@mozilla.org/dom/peerconnection;1";
 const PC_OBS_CONTRACT = "@mozilla.org/dom/peerconnectionobserver;1";
 const PC_ICE_CONTRACT = "@mozilla.org/dom/rtcicecandidate;1";
 const PC_SESSION_CONTRACT = "@mozilla.org/dom/rtcsessiondescription;1";
 const PC_MANAGER_CONTRACT = "@mozilla.org/dom/peerconnectionmanager;1";
 const PC_STATS_CONTRACT = "@mozilla.org/dom/rtcstatsreport;1";
 const PC_IDENTITY_CONTRACT = "@mozilla.org/dom/rtcidentityassertion;1";
+const PC_STATIC_CONTRACT = "@mozilla.org/dom/peerconnectionstatic;1";
 
 const PC_CID = Components.ID("{00e0e20d-1494-4776-8e0e-0f0acbea3c79}");
 const PC_OBS_CID = Components.ID("{d1748d4c-7f6a-4dc5-add6-d55b7678537e}");
 const PC_ICE_CID = Components.ID("{02b9970c-433d-4cc2-923d-f7028ac66073}");
 const PC_SESSION_CID = Components.ID("{1775081b-b62d-4954-8ffe-a067bbf508a7}");
 const PC_MANAGER_CID = Components.ID("{7293e901-2be3-4c02-b4bd-cbef6fc24f78}");
 const PC_STATS_CID = Components.ID("{7fe6e18b-0da3-4056-bf3b-440ef3809e06}");
 const PC_IDENTITY_CID = Components.ID("{1abc7499-3c54-43e0-bd60-686e2703f072}");
+const PC_STATIC_CID = Components.ID("{0fb47c47-a205-4583-a9fc-cbadf8c95880}");
 
 // Global list of PeerConnection objects, so they can be cleaned up when
 // a page is torn down. (Maps inner window ID to an array of PC objects).
 function GlobalPCList() {
   this._list = {};
   this._networkdown = false; // XXX Need to query current state somehow
+  this._lifecycleobservers = {};
   Services.obs.addObserver(this, "inner-window-destroyed", true);
   Services.obs.addObserver(this, "profile-change-net-teardown", true);
   Services.obs.addObserver(this, "network:offline-about-to-go-offline", true);
   Services.obs.addObserver(this, "network:offline-status-changed", true);
 }
 GlobalPCList.prototype = {
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
                                          Ci.nsISupportsWeakReference,
@@ -47,16 +50,22 @@ GlobalPCList.prototype = {
     createInstance: function(outer, iid) {
       if (outer) {
         throw Cr.NS_ERROR_NO_AGGREGATION;
       }
       return _globalPCList.QueryInterface(iid);
     }
   },
 
+  notifyLifecycleObservers: function(pc, type) {
+    for (var key of Object.keys(this._lifecycleobservers)) {
+      this._lifecycleobservers[key](pc, pc._winID, type);
+    }
+  },
+
   addPC: function(pc) {
     let winID = pc._winID;
     if (this._list[winID]) {
       this._list[winID].push(Cu.getWeakReference(pc));
     } else {
       this._list[winID] = [Cu.getWeakReference(pc)];
     }
     this.removeNullRefs(winID);
@@ -92,17 +101,22 @@ GlobalPCList.prototype = {
     let cleanupWinId = function(list, winID) {
       if (list.hasOwnProperty(winID)) {
         list[winID].forEach(cleanupPcRef);
         delete list[winID];
       }
     };
 
     if (topic == "inner-window-destroyed") {
-      cleanupWinId(this._list, subject.QueryInterface(Ci.nsISupportsPRUint64).data);
+      let winID = subject.QueryInterface(Ci.nsISupportsPRUint64).data;
+      cleanupWinId(this._list, winID);
+
+      if (this._lifecycleobservers.hasOwnProperty(winID)) {
+        delete this._lifecycleobservers[winID];
+      }
     } else if (topic == "profile-change-net-teardown" ||
                topic == "network:offline-about-to-go-offline") {
       // Delete all peerconnections on shutdown - mostly synchronously (we
       // need them to be done deleting transports and streams before we
       // return)! All socket operations must be queued to STS thread
       // before we return to here.
       // Also kill them if "Work Offline" is selected - more can be created
       // while offline, but attempts to connect them should fail.
@@ -116,16 +130,19 @@ GlobalPCList.prototype = {
         // this._list shold be empty here
         this._networkdown = true;
       } else if (data == "online") {
         this._networkdown = false;
       }
     }
   },
 
+  _registerPeerConnectionLifecycleCallback: function(winID, cb) {
+    this._lifecycleobservers[winID] = cb;
+  },
 };
 let _globalPCList = new GlobalPCList();
 
 function RTCIceCandidate() {
   this.candidate = this.sdpMid = this.sdpMLineIndex = null;
 }
 RTCIceCandidate.prototype = {
   classDescription: "mozRTCIceCandidate",
@@ -330,16 +347,17 @@ RTCPeerConnection.prototype = {
       wait: !this._trickleIce
     });
   },
 
   _initialize: function(rtcConfig) {
     this._impl.initialize(this._observer, this._win, rtcConfig,
                           Services.tm.currentThread);
     this._initIdp();
+    _globalPCList.notifyLifecycleObservers(this, "initialized");
   },
 
   get _impl() {
     if (!this._pc) {
       throw new this._win.DOMError("",
           "RTCPeerConnection is gone (did you enter Offline mode?)");
     }
     return this._pc;
@@ -872,16 +890,17 @@ RTCPeerConnection.prototype = {
     if (sdp.length == 0) {
       return null;
     }
     return new this._win.mozRTCSessionDescription({ type: this._remoteType,
                                                     sdp: sdp });
   },
 
   get peerIdentity() { return this._peerIdentity; },
+  get id() { return this._impl.id; },
   get iceGatheringState()  { return this._iceGatheringState; },
   get iceConnectionState() { return this._iceConnectionState; },
 
   get signalingState() {
     // checking for our local pc closed indication
     // before invoking the pc methods.
     if (this._closed) {
       return "closed";
@@ -894,20 +913,22 @@ RTCPeerConnection.prototype = {
       "SignalingHaveLocalPranswer":  "have-local-pranswer",
       "SignalingHaveRemotePranswer": "have-remote-pranswer",
       "SignalingClosed":             "closed"
     }[this._impl.signalingState];
   },
 
   changeIceGatheringState: function(state) {
     this._iceGatheringState = state;
+    _globalPCList.notifyLifecycleObservers(this, "icegatheringstatechange");
   },
 
   changeIceConnectionState: function(state) {
     this._iceConnectionState = state;
+    _globalPCList.notifyLifecycleObservers(this, "iceconnectionstatechange");
     this.dispatchEvent(new this._win.Event("iceconnectionstatechange"));
   },
 
   getStats: function(selector, onSuccess, onError) {
     this._queueOrRun({
       func: this._getStats,
       args: [selector, onSuccess, onError],
       wait: true
@@ -1279,17 +1300,38 @@ PeerConnectionObserver.prototype = {
                                                                 { channel: channel }));
   },
 
   getSupportedConstraints: function(dict) {
     return dict;
   },
 };
 
+function RTCPeerConnectionStatic() {
+}
+RTCPeerConnectionStatic.prototype = {
+  classDescription: "mozRTCPeerConnectionStatic",
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports,
+                                         Ci.nsIDOMGlobalPropertyInitializer]),
+
+  classID: PC_STATIC_CID,
+  contractID: PC_STATIC_CONTRACT,
+
+  init: function(win) {
+    this._winID = win.QueryInterface(Ci.nsIInterfaceRequestor)
+      .getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID;
+  },
+
+  registerPeerConnectionLifecycleCallback: function(cb) {
+    _globalPCList._registerPeerConnectionLifecycleCallback(this._winID, cb);
+  },
+};
+
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory(
   [GlobalPCList,
    RTCIceCandidate,
    RTCSessionDescription,
    RTCPeerConnection,
+   RTCPeerConnectionStatic,
    RTCStatsReport,
    RTCIdentityAssertion,
    PeerConnectionObserver]
 );
--- a/dom/media/PeerConnection.manifest
+++ b/dom/media/PeerConnection.manifest
@@ -1,15 +1,17 @@
 component {00e0e20d-1494-4776-8e0e-0f0acbea3c79} PeerConnection.js
 component {d1748d4c-7f6a-4dc5-add6-d55b7678537e} PeerConnection.js
 component {02b9970c-433d-4cc2-923d-f7028ac66073} PeerConnection.js
 component {1775081b-b62d-4954-8ffe-a067bbf508a7} PeerConnection.js
 component {7293e901-2be3-4c02-b4bd-cbef6fc24f78} PeerConnection.js
 component {7fe6e18b-0da3-4056-bf3b-440ef3809e06} PeerConnection.js
 component {1abc7499-3c54-43e0-bd60-686e2703f072} PeerConnection.js
+component {0fb47c47-a205-4583-a9fc-cbadf8c95880} PeerConnection.js
 
 contract @mozilla.org/dom/peerconnection;1 {00e0e20d-1494-4776-8e0e-0f0acbea3c79}
 contract @mozilla.org/dom/peerconnectionobserver;1 {d1748d4c-7f6a-4dc5-add6-d55b7678537e}
 contract @mozilla.org/dom/rtcicecandidate;1 {02b9970c-433d-4cc2-923d-f7028ac66073}
 contract @mozilla.org/dom/rtcsessiondescription;1 {1775081b-b62d-4954-8ffe-a067bbf508a7}
 contract @mozilla.org/dom/peerconnectionmanager;1 {7293e901-2be3-4c02-b4bd-cbef6fc24f78}
 contract @mozilla.org/dom/rtcstatsreport;1 {7fe6e18b-0da3-4056-bf3b-440ef3809e06}
 contract @mozilla.org/dom/rtcidentityassertion;1 {1abc7499-3c54-43e0-bd60-686e2703f072}
+contract @mozilla.org/dom/peerconnectionstatic;1 {0fb47c47-a205-4583-a9fc-cbadf8c95880}
--- a/dom/media/tests/mochitest/mochitest.ini
+++ b/dom/media/tests/mochitest/mochitest.ini
@@ -14,16 +14,18 @@ skip-if = toolkit == 'gonk' #Bug 962984 
 [test_dataChannel_basicAudioVideo.html]
 # Disabled on OS X for bug 930481 timeouts
 skip-if = os == 'mac' || toolkit=='gonk' # b2g(Bug 960442, video support for WebRTC is disabled on b2g) b2g-debug(Bug 960442, video support for WebRTC is disabled on b2g)
 [test_dataChannel_basicAudioVideoCombined.html]
 # Disabled on OS X for bug 930481 timeouts
 skip-if = os == 'mac' || toolkit=='gonk' # b2g(Bug 960442, video support for WebRTC is disabled on b2g) b2g-debug(Bug 960442, video support for WebRTC is disabled on b2g)
 [test_dataChannel_basicDataOnly.html]
 [test_dataChannel_basicVideo.html]
+skip-if = toolkit=='gonk' # b2g emulator seems to bee too slow (Bug 1016498 and 1008080)
+[test_dataChannel_bug1013809.html]
 skip-if = toolkit=='gonk' # b2g(Bug 960442, video support for WebRTC is disabled on b2g) b2g-debug(Bug 960442, video support for WebRTC is disabled on b2g)
 [test_dataChannel_noOffer.html]
 [test_getUserMedia_basicAudio.html]
 skip-if = (toolkit == 'gonk' && debug) #debug-only failure
 [test_getUserMedia_basicVideo.html]
 skip-if = (toolkit == 'gonk' && debug) #debug-only failure
 [test_getUserMedia_basicVideoAudio.html]
 skip-if = (toolkit == 'gonk' && debug) #debug-only failure, turned an intermittent (bug 962579) into a permanant orange
@@ -55,16 +57,18 @@ skip-if = toolkit=='gonk' # b2g(Bug 9604
 [test_peerConnection_basicVideo.html]
 skip-if = toolkit=='gonk' # b2g(Bug 960442, video support for WebRTC is disabled on b2g) b2g-debug(Bug 960442, video support for WebRTC is disabled on b2g)
 [test_peerConnection_bug822674.html]
 [test_peerConnection_bug825703.html]
 [test_peerConnection_bug827843.html]
 skip-if = toolkit=='gonk' # b2g(Bug 960442, video support for WebRTC is disabled on b2g) b2g-debug(Bug 960442, video support for WebRTC is disabled on b2g)
 [test_peerConnection_bug834153.html]
 [test_peerConnection_bug835370.html]
+skip-if = toolkit=='gonk' # b2g emulator seems to bee too slow (Bug 1016498 and 1008080)
+[test_peerConnection_bug1013809.html]
 [test_peerConnection_close.html]
 [test_peerConnection_errorCallbacks.html]
 [test_peerConnection_offerRequiresReceiveAudio.html]
 skip-if = toolkit=='gonk' # b2g(Bug 960442, video support for WebRTC is disabled on b2g) b2g-debug(Bug 960442, video support for WebRTC is disabled on b2g)
 [test_peerConnection_offerRequiresReceiveVideo.html]
 skip-if = toolkit=='gonk' # b2g(Bug 960442, video support for WebRTC is disabled on b2g) b2g-debug(Bug 960442, video support for WebRTC is disabled on b2g)
 [test_peerConnection_offerRequiresReceiveVideoAudio.html]
 skip-if = toolkit=='gonk' # b2g(Bug 960442, video support for WebRTC is disabled on b2g) b2g-debug(Bug 960442, video support for WebRTC is disabled on b2g)
--- a/dom/media/tests/mochitest/pc.js
+++ b/dom/media/tests/mochitest/pc.js
@@ -476,18 +476,16 @@ function PeerConnectionTest(options) {
   else
     this.pcLocal = null;
 
   if (options.is_remote)
     this.pcRemote = new PeerConnectionWrapper('pcRemote', options.config_remote || options.config_local);
   else
     this.pcRemote = null;
 
-  this.connected = false;
-
   // Create command chain instance and assign default commands
   this.chain = new CommandChain(this, options.commands);
   if (!options.is_local) {
     this.chain.filterOut(/^PC_LOCAL/);
   }
   if (!options.is_remote) {
     this.chain.filterOut(/^PC_REMOTE/);
   }
@@ -505,17 +503,17 @@ function PeerConnectionTest(options) {
 
 /**
  * Closes the peer connection if it is active
  *
  * @param {Function} onSuccess
  *        Callback to execute when the peer connection has been closed successfully
  */
 PeerConnectionTest.prototype.close = function PCT_close(onSuccess) {
-  info("Closing peer connections. Connection state=" + this.connected);
+  info("Closing peer connections");
 
   var self = this;
   var closeTimeout = null;
   var waitingForLocal = false;
   var waitingForRemote = false;
   var everythingClosed = false;
 
   function verifyClosed() {
@@ -528,17 +526,16 @@ PeerConnectionTest.prototype.close = fun
       info("No closure pending");
       if (self.pcLocal) {
         is(self.pcLocal.signalingState, "closed", "pcLocal is in 'closed' state");
       }
       if (self.pcRemote) {
         is(self.pcRemote.signalingState, "closed", "pcRemote is in 'closed' state");
       }
       clearTimeout(closeTimeout);
-      self.connected = false;
       everythingClosed = true;
       onSuccess();
     }
   }
 
   function signalingstatechangeLocalClose(state) {
     info("'onsignalingstatechange' event '" + state + "' received");
     is(state, "closed", "onsignalingstatechange event is closed");
@@ -561,21 +558,24 @@ PeerConnectionTest.prototype.close = fun
       self.pcLocal.close();
     }
     if ((self.pcRemote) && (self.pcRemote.signalingState !== "closed")) {
       info("Closing pcRemote");
       self.pcRemote.onsignalingstatechange = signalingstatechangeRemoteClose;
       self.waitingForRemote = true;
       self.pcRemote.close();
     }
-    verifyClosed();
+    // give the signals handlers time to fire
+    setTimeout(verifyClosed, 1000);
   }
 
   closeTimeout = setTimeout(function() {
-    ok(false, "Closing PeerConnections timed out!");
+    var closed = ((self.pcLocal && (self.pcLocal.signalingState === "closed")) &&
+      (self.pcRemote && (self.pcRemote.signalingState === "closed")));
+    ok(closed, "Closing PeerConnections timed out");
     // it is not a success, but the show must go on
     onSuccess();
   }, 60000);
 
   closeEverything();
 };
 
 /**
@@ -789,63 +789,183 @@ function DataChannelTest(options) {
 }
 
 DataChannelTest.prototype = Object.create(PeerConnectionTest.prototype, {
   close : {
     /**
      * Close the open data channels, followed by the underlying peer connection
      *
      * @param {Function} onSuccess
-     *        Callback to execute when the connection has been closed
+     *        Callback to execute when all connections have been closed
      */
     value : function DCT_close(onSuccess) {
       var self = this;
+      var pendingDcClose = []
+      var closeTimeout = null;
 
-      function _closeChannels() {
-        var length = self.pcLocal.dataChannels.length;
+      info("DataChannelTest.close() called");
 
-        if (length > 0) {
-          self.closeDataChannel(length - 1, function () {
-            _closeChannels();
-          });
+      function _closePeerConnection() {
+        info("DataChannelTest closing PeerConnection");
+        PeerConnectionTest.prototype.close.call(self, onSuccess);
+      }
+
+      function _closePeerConnectionCallback(index) {
+        info("_closePeerConnection called with index " + index);
+        var pos = pendingDcClose.indexOf(index);
+        if (pos != -1) {
+          pendingDcClose.splice(pos, 1);
         }
         else {
-          PeerConnectionTest.prototype.close.call(self, onSuccess);
+          info("_closePeerConnection index " + index + " is missing from pendingDcClose: " + pendingDcClose);
+        }
+        if (pendingDcClose.length === 0) {
+          clearTimeout(closeTimeout);
+          _closePeerConnection();
         }
       }
 
-      _closeChannels();
+      var myDataChannels = null;
+      if (self.pcLocal) {
+        myDataChannels = self.pcLocal.dataChannels;
+      }
+      else if (self.pcRemote) {
+        myDataChannels = self.pcRemote.dataChannels;
+      }
+      var length = myDataChannels.length;
+      for (var i = 0; i < length; i++) {
+        var dataChannel = myDataChannels[i];
+        if (dataChannel.readyState !== "closed") {
+          pendingDcClose.push(i);
+          self.closeDataChannels(i, _closePeerConnectionCallback);
+        }
+      }
+      if (pendingDcClose.length === 0) {
+        _closePeerConnection();
+      }
+      else {
+        closeTimeout = setTimeout(function() {
+          ok(false, "Failed to properly close data channels: " +
+            pendingDcClose);
+          _closePeerConnection();
+        }, 60000);
+      }
     }
   },
 
-  closeDataChannel : {
+  closeDataChannels : {
     /**
-     * Close the specified data channel
+     * Close the specified data channels
      *
      * @param {Number} index
-     *        Index of the data channel to close on both sides
+     *        Index of the data channels to close on both sides
      * @param {Function} onSuccess
-     *        Callback to execute when the data channel has been closed
+     *        Callback to execute when the data channels has been closed
      */
-    value : function DCT_closeDataChannel(index, onSuccess) {
-      var localChannel = this.pcLocal.dataChannels[index];
-      var remoteChannel = this.pcRemote.dataChannels[index];
+    value : function DCT_closeDataChannels(index, onSuccess) {
+      info("_closeDataChannels called with index: " + index);
+      var localChannel = null;
+      if (this.pcLocal) {
+        localChannel = this.pcLocal.dataChannels[index];
+      }
+      var remoteChannel = null;
+      if (this.pcRemote) {
+        remoteChannel = this.pcRemote.dataChannels[index];
+      }
 
       var self = this;
+      var wait = false;
+      var pollingMode = false;
+      var everythingClosed = false;
+      var verifyInterval = null;
+      var remoteCloseTimer = null;
 
-      // Register handler for remote channel, cause we have to wait until
-      // the current close operation has been finished.
-      remoteChannel.onclose = function () {
-        self.pcRemote.dataChannels.splice(index, 1);
+      function _allChannelsAreClosed() {
+        var ret = null;
+        if (localChannel) {
+          ret = (localChannel.readyState === "closed");
+        }
+        if (remoteChannel) {
+          if (ret !== null) {
+            ret = (ret && (remoteChannel.readyState === "closed"));
+          }
+          else {
+            ret = (remoteChannel.readyState === "closed");
+          }
+        }
+        return ret;
+      }
+
+      function verifyClosedChannels() {
+        if (everythingClosed) {
+          // safety protection against events firing late
+          return;
+        }
+        if (_allChannelsAreClosed) {
+          ok(true, "DataChannel(s) have reached 'closed' state for data channel " + index);
+          if (remoteCloseTimer !== null) {
+            clearTimeout(remoteCloseTimer);
+          }
+          if (verifyInterval !== null) {
+            clearInterval(verifyInterval);
+          }
+          everythingClosed = true;
+          onSuccess(index);
+        }
+        else {
+          info("Still waiting for DataChannel closure");
+        }
+      }
 
-        onSuccess(remoteChannel);
-      };
+      if ((localChannel) && (localChannel.readyState !== "closed")) {
+        // in case of steeplechase there is no far end, so we can only poll
+        if (remoteChannel) {
+          remoteChannel.onclose = function () {
+            is(remoteChannel.readyState, "closed", "remoteChannel is in state 'closed'");
+            verifyClosedChannels();
+          };
+        }
+        else {
+          pollingMode = true;
+          verifyInterval = setInterval(verifyClosedChannels, 1000);
+        }
+
+        localChannel.close();
+        wait = true;
+      }
+      if ((remoteChannel) && (remoteChannel.readyState !== "closed")) {
+        if (localChannel) {
+          localChannel.onclose = function () {
+            is(localChannel.readyState, "closed", "localChannel is in state 'closed'");
+            verifyClosedChannels();
+          };
 
-      localChannel.close();
-      this.pcLocal.dataChannels.splice(index, 1);
+          // Apparently we are running a local test which has both ends of the
+          // data channel locally available, so by default lets wait for the
+          // remoteChannel.onclose handler from above to confirm closure on both
+          // ends.
+          remoteCloseTimer = setTimeout(function() {
+            todo(false, "localChannel.close() did not resulted in close signal on remote side");
+            remoteChannel.close();
+            verifyClosedChannels();
+          }, 30000);
+        }
+        else {
+          pollingMode = true;
+          verifyTimer = setInterval(verifyClosedChannels, 1000);
+
+          remoteChannel.close();
+        }
+
+        wait = true;
+      }
+
+      if (!wait) {
+        onSuccess(index);
+      }
     }
   },
 
   createDataChannel : {
     /**
      * Create a data channel
      *
      * @param {Dict} options
@@ -855,17 +975,17 @@ DataChannelTest.prototype = Object.creat
      */
     value : function DCT_createDataChannel(options, onSuccess) {
       var localChannel = null;
       var remoteChannel = null;
       var self = this;
 
       // Method to synchronize all asynchronous events.
       function check_next_test() {
-        if (self.connected && localChannel && remoteChannel) {
+        if (localChannel && remoteChannel) {
           onSuccess(localChannel, remoteChannel);
         }
       }
 
       if (!options.negotiated) {
         // Register handlers for the remote peer
         this.pcRemote.registerDataChannelOpenEvents(function (channel) {
           remoteChannel = channel;
@@ -920,91 +1040,72 @@ DataChannelTest.prototype = Object.creat
 
       source.send(data);
     }
   },
 
   setLocalDescription : {
     /**
      * Sets the local description for the specified peer connection instance
-     * and automatically handles the failure case. In case for the final call
-     * it will setup the requested datachannel.
+     * and automatically handles the failure case.
      *
      * @param {PeerConnectionWrapper} peer
               The peer connection wrapper to run the command on
      * @param {mozRTCSessionDescription} desc
      *        Session description for the local description request
      * @param {function} onSuccess
      *        Callback to execute if the local description was set successfully
      */
     value : function DCT_setLocalDescription(peer, desc, state, onSuccess) {
-      // If the peer has a remote offer we are in the final call, and have
-      // to wait for the datachannel connection to be open. It will also set
-      // the local description internally.
-      if (peer.signalingState === 'have-remote-offer') {
-        this.waitForInitialDataChannel(peer, desc, state, onSuccess);
-      }
-      else {
-        PeerConnectionTest.prototype.setLocalDescription.call(this, peer,
+      PeerConnectionTest.prototype.setLocalDescription.call(this, peer,
                                                               desc, state, onSuccess);
-      }
 
     }
   },
 
   waitForInitialDataChannel : {
     /**
-     * Create an initial data channel before the peer connection has been connected
+     * Wait for the initial data channel to get into the open state
      *
      * @param {PeerConnectionWrapper} peer
-              The peer connection wrapper to run the command on
-     * @param {mozRTCSessionDescription} desc
-     *        Session description for the local description request
+     *        The peer connection wrapper to run the command on
      * @param {Function} onSuccess
      *        Callback when the creation was successful
      */
-    value : function DCT_waitForInitialDataChannel(peer, desc, state, onSuccess) {
-      var self = this;
-
-      var targetPeer = peer;
-      var targetChannel = null;
+    value : function DCT_waitForInitialDataChannel(peer, onSuccess, onFailure) {
+      var dcConnectionTimeout = null;
 
-      var sourcePeer = (peer == this.pcLocal) ? this.pcRemote : this.pcLocal;
-      var sourceChannel = null;
+      function dataChannelConnected(channel) {
+        clearTimeout(dcConnectionTimeout);
+        is(channel.readyState, "open", peer + " dataChannels[0] is in state: 'open'");
+        onSuccess();
+      }
 
-      // Method to synchronize all asynchronous events which current happen
-      // due to a non-predictable flow. With bug 875346 fixed we will be able
-      // to simplify this code.
-      function check_next_test() {
-        if (self.connected && sourceChannel && targetChannel) {
-          onSuccess(sourceChannel, targetChannel);
-        }
+      if ((peer.dataChannels.length >= 1) &&
+          (peer.dataChannels[0].readyState === "open")) {
+        is(peer.dataChannels[0].readyState, "open", peer + " dataChannels[0] is in state: 'open'");
+        onSuccess();
+        return;
       }
 
-      // Register 'onopen' handler for the first local data channel
-      sourcePeer.dataChannels[0].onopen = function (channel) {
-        sourceChannel = channel;
-        check_next_test();
-      };
+      // TODO: drno: convert dataChannels into an object and make
+      //             registerDataChannelOPenEvent a generic function
+      if (peer == this.pcLocal) {
+        peer.dataChannels[0].onopen = dataChannelConnected;
+      } else {
+        peer.registerDataChannelOpenEvents(dataChannelConnected);
+      }
 
-      // Register handlers for the target peer
-      targetPeer.registerDataChannelOpenEvents(function (channel) {
-        targetChannel = channel;
-        check_next_test();
-      });
-
-      PeerConnectionTest.prototype.setLocalDescription.call(this, targetPeer, desc,
-        state,
-        function () {
-          self.connected = true;
-          check_next_test();
-        }
-      );
+      dcConnectionTimeout = setTimeout(function () {
+        info(peer + " timed out while waiting for dataChannels[0] to connect");
+        onFailure();
+      }, 60000);
     }
   }
+
 });
 
 /**
  * This class acts as a wrapper around a DataChannel instance.
  *
  * @param dataChannel
  * @param peerConnectionWrapper
  * @constructor
@@ -1961,20 +2062,17 @@ PeerConnectionWrapper.prototype = {
    *
    * @param {Function} onDataChannelOpened
    *        Callback to execute when the data channel has been opened
    */
   registerDataChannelOpenEvents : function (onDataChannelOpened) {
     info(this + ": Register callbacks for 'ondatachannel' and 'onopen'");
 
     this.ondatachannel = function (targetChannel) {
-      targetChannel.onopen = function (targetChannel) {
-        onDataChannelOpened(targetChannel);
-      };
-
+      targetChannel.onopen = onDataChannelOpened;
       this.dataChannels.push(targetChannel);
     };
   },
 
   /**
    * Returns the string representation of the class
    *
    * @returns {String} The string representation
--- a/dom/media/tests/mochitest/templates.js
+++ b/dom/media/tests/mochitest/templates.js
@@ -130,16 +130,26 @@ var commandsPeerConnection = [
           send_message({"answer": test.pcRemote._last_answer,
                         "media_constraints": test.pcRemote.constraints});
         }
         test.next();
       });
     }
   ],
   [
+    'PC_REMOTE_SET_LOCAL_DESCRIPTION',
+    function (test) {
+      test.setLocalDescription(test.pcRemote, test.pcRemote._last_answer, STABLE, function () {
+        is(test.pcRemote.signalingState, STABLE,
+           "signalingState after remote setLocalDescription is 'stable'");
+        test.next();
+      });
+    }
+  ],
+  [
     'PC_LOCAL_GET_ANSWER',
     function (test) {
       if (test.pcRemote) {
         test._remote_answer = test.pcRemote._last_answer;
         test._remote_constraints = test.pcRemote.constraints;
         test.next();
       } else {
         wait_for_message().then(function(message) {
@@ -157,26 +167,16 @@ var commandsPeerConnection = [
       test.setRemoteDescription(test.pcLocal, test._remote_answer, STABLE, function () {
         is(test.pcLocal.signalingState, STABLE,
            "signalingState after local setRemoteDescription is 'stable'");
         test.next();
       });
     }
   ],
   [
-    'PC_REMOTE_SET_LOCAL_DESCRIPTION',
-    function (test) {
-      test.setLocalDescription(test.pcRemote, test.pcRemote._last_answer, STABLE, function () {
-        is(test.pcRemote.signalingState, STABLE,
-           "signalingState after remote setLocalDescription is 'stable'");
-        test.next();
-      });
-    }
-  ],
-  [
     'PC_LOCAL_WAIT_FOR_ICE_CONNECTED',
     function (test) {
       var myTest = test;
       var myPc = myTest.pcLocal;
 
       function onIceConnectedSuccess () {
         ok(true, "pc_local: ICE switched to 'connected' state");
         myTest.next();
@@ -370,39 +370,82 @@ var commandsDataChannel = [
       test.createAnswer(test.pcRemote, function () {
         is(test.pcRemote.signalingState, HAVE_REMOTE_OFFER,
            "Remote create offer does not change signaling state");
         test.next();
       });
     }
   ],
   [
+    'PC_LOCAL_SETUP_DATA_CHANNEL_CALLBACK',
+    function (test) {
+      test.waitForInitialDataChannel(test.pcLocal, function () {
+        ok(true, test.pcLocal + " dataChannels[0] switched to 'open'");
+      }, function () {
+        ok(false, test.pcLocal + " initial dataChannels[0] failed to switch to 'open'");
+        unexpectedEventAndFinish(this, 'timeout')
+      });
+      test.next();
+    }
+  ],
+  [
+    'PC_REMOTE_SETUP_DATA_CHANNEL_CALLBACK',
+    function (test) {
+      test.waitForInitialDataChannel(test.pcRemote, function () {
+        ok(true, test.pcRemote + " dataChannels[0] switched to 'open'");
+      }, function () {
+        ok(false, test.pcRemote + " initial dataChannels[0] failed to switch to 'open'");
+        unexpectedEventAndFinish(this, 'timeout');
+      });
+      test.next();
+    }
+  ],
+  [
+    'PC_REMOTE_SET_LOCAL_DESCRIPTION',
+    function (test) {
+      test.setLocalDescription(test.pcRemote, test.pcRemote._last_answer, STABLE,
+        function () {
+          is(test.pcRemote.signalingState, STABLE,
+             "signalingState after remote setLocalDescription is 'stable'");
+          test.next();
+        }
+      );
+    }
+  ],
+  [
     'PC_LOCAL_SET_REMOTE_DESCRIPTION',
     function (test) {
       test.setRemoteDescription(test.pcLocal, test.pcRemote._last_answer, STABLE,
         function () {
         is(test.pcLocal.signalingState, STABLE,
            "signalingState after local setRemoteDescription is 'stable'");
         test.next();
       });
     }
   ],
   [
-    'PC_REMOTE_SET_LOCAL_DESCRIPTION',
+    'PC_LOCAL_VERIFY_DATA_CHANNEL_STATE',
     function (test) {
-      test.setLocalDescription(test.pcRemote, test.pcRemote._last_answer, STABLE,
-        function (sourceChannel, targetChannel) {
-          is(sourceChannel.readyState, "open", test.pcLocal + " is in state: 'open'");
-          is(targetChannel.readyState, "open", test.pcRemote + " is in state: 'open'");
-
-          is(test.pcRemote.signalingState, STABLE,
-             "signalingState after remote setLocalDescription is 'stable'");
-          test.next();
-        }
-      );
+      test.waitForInitialDataChannel(test.pcLocal, function() {
+        test.next();
+      }, function() {
+        ok(false, test.pcLocal + " initial dataChannels[0] failed to switch to 'open'");
+        unexpectedEventAndFinish(this, 'timeout')
+      });
+    }
+  ],
+  [
+    'PC_REMOTE_VERIFY_DATA_CHANNEL_STATE',
+    function (test) {
+      test.waitForInitialDataChannel(test.pcRemote, function() {
+        test.next();
+      }, function() {
+        ok(false, test.pcRemote + " initial dataChannels[0] failed to switch to 'open'");
+        unexpectedEventAndFinish(this, 'timeout');
+      });
     }
   ],
   [
     'PC_LOCAL_CHECK_MEDIA_TRACKS',
     function (test) {
       test.pcLocal.checkMediaTracks(test.pcRemote.constraints, function () {
         test.next();
       });
@@ -503,33 +546,51 @@ var commandsDataChannel = [
         is(test.pcRemote.dataChannels.indexOf(channel), 0, "1st channel used");
         is(data, message, "Received message has the correct content.");
 
         test.next();
       }, options);
     }
   ],
   [
+    'SEND_MESSAGE_BACK_THROUGH_FIRST_CHANNEL',
+    function (test) {
+      var message = "Return a message also through 1st channel";
+      var options = {
+        sourceChannel: test.pcRemote.dataChannels[0],
+        targetChannel: test.pcLocal.dataChannels[0]
+      };
+
+      test.send(message, function (channel, data) {
+        is(test.pcLocal.dataChannels.indexOf(channel), 0, "1st channel used");
+        is(data, message, "Return message has the correct content.");
+
+        test.next();
+      }, options);
+    }
+  ],
+  [
     'CREATE_NEGOTIATED_DATA_CHANNEL',
     function (test) {
       var options = {negotiated:true, id: 5, protocol:"foo/bar", ordered:false,
-		     maxRetransmits:500};
+        maxRetransmits:500};
       test.createDataChannel(options, function (sourceChannel2, targetChannel2) {
         is(sourceChannel2.readyState, "open", sourceChannel2 + " is in state: 'open'");
         is(targetChannel2.readyState, "open", targetChannel2 + " is in state: 'open'");
 
         is(targetChannel2.binaryType, "blob", targetChannel2 + " is of binary type 'blob'");
         is(targetChannel2.readyState, "open", targetChannel2 + " is in state: 'open'");
 
         if (options.id != undefined) {
           is(sourceChannel2.id, options.id, sourceChannel2 + " id is:" + sourceChannel2.id);
-	} else {
-	  options.id = sourceChannel2.id;
-	}
-	var reliable = !options.ordered ? false : (options.maxRetransmits || options.maxRetransmitTime);
+        }
+        else {
+          options.id = sourceChannel2.id;
+        }
+        var reliable = !options.ordered ? false : (options.maxRetransmits || options.maxRetransmitTime);
         is(sourceChannel2.protocol, options.protocol, sourceChannel2 + " protocol is:" + sourceChannel2.protocol);
         is(sourceChannel2.reliable, reliable, sourceChannel2 + " reliable is:" + sourceChannel2.reliable);
 /*
   These aren't exposed by IDL yet
         is(sourceChannel2.ordered, options.ordered, sourceChannel2 + " ordered is:" + sourceChannel2.ordered);
         is(sourceChannel2.maxRetransmits, options.maxRetransmits, sourceChannel2 + " maxRetransmits is:" +
 	   sourceChannel2.maxRetransmits);
         is(sourceChannel2.maxRetransmitTime, options.maxRetransmitTime, sourceChannel2 + " maxRetransmitTime is:" +
@@ -560,34 +621,10 @@ var commandsDataChannel = [
 
       test.send(message, function (channel, data) {
         is(channels.indexOf(channel), channels.length - 1, "Last channel used");
         is(data, message, "Received message has the correct content.");
 
         test.next();
       });
     }
-  ],
-  [
-    'CLOSE_LAST_OPENED_DATA_CHANNEL2',
-    function (test) {
-      var channels = test.pcRemote.dataChannels;
-
-      test.closeDataChannel(channels.length - 1, function (channel) {
-        is(channel.readyState, "closed", "Channel is in state: 'closed'");
-
-        test.next();
-      });
-    }
-  ],
-  [
-    'CLOSE_LAST_OPENED_DATA_CHANNEL',
-    function (test) {
-      var channels = test.pcRemote.dataChannels;
-
-      test.closeDataChannel(channels.length - 1, function (channel) {
-        is(channel.readyState, "closed", "Channel is in state: 'closed'");
-
-        test.next();
-      });
-    }
   ]
 ];
new file mode 100644
--- /dev/null
+++ b/dom/media/tests/mochitest/test_dataChannel_bug1013809.html
@@ -0,0 +1,31 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="head.js"></script>
+  <script type="application/javascript" src="pc.js"></script>
+  <script type="application/javascript" src="templates.js"></script>
+  <script type="application/javascript" src="turnConfig.js"></script>
+</head>
+<body>
+<pre id="test">
+<script type="application/javascript">
+  createHTML({
+    bug: "796895",
+    title: "Basic data channel audio connection"
+  });
+
+  var test;
+  runTest(function () {
+    test = new DataChannelTest();
+    var sld = test.chain.remove("PC_REMOTE_SET_LOCAL_DESCRIPTION");
+    test.chain.insertAfter("PC_LOCAL_SET_REMOTE_DESCRIPTION", sld);
+    test.setMediaConstraints([{audio: true}], [{audio: true}]);
+    test.run();
+  }, true);
+
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/media/tests/mochitest/test_peerConnection_bug1013809.html
@@ -0,0 +1,30 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="head.js"></script>
+  <script type="application/javascript" src="pc.js"></script>
+  <script type="application/javascript" src="templates.js"></script>
+  <script type="application/javascript" src="turnConfig.js"></script>
+</head>
+<body>
+<pre id="test">
+<script type="application/javascript">
+  createHTML({
+    bug: "1013809",
+    title: "Audio-only peer connection with swapped setLocal and setRemote steps"
+  });
+
+  var test;
+  runTest(function (options) {
+    test = new PeerConnectionTest(options);
+    var sld = test.chain.remove("PC_REMOTE_SET_LOCAL_DESCRIPTION");
+    test.chain.insertAfter("PC_LOCAL_SET_REMOTE_DESCRIPTION", sld);
+    test.setMediaConstraints([{audio: true}], [{audio: true}]);
+    test.run();
+  });
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/webidl/Identity.webidl
@@ -0,0 +1,70 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+callback IdentityOnReadyCallback = void();
+callback IdentityOnLoginCallback = void(DOMString identityAssertion);
+callback IdentityOnLogoutCallback = void();
+callback IdentityOnCancelCallback = void(DOMString? error);
+callback IdentityOnErrorCallback = void(DOMString error);
+
+dictionary IdentityWatchOptions {
+  // Required callback
+  IdentityOnLoginCallback onlogin;
+
+  // Optional parameters
+  DOMString wantIssuer;
+  DOMString loggedInUser;
+
+  // Optional callbacks
+  IdentityOnReadyCallback onready;
+  IdentityOnLogoutCallback onlogout;
+  IdentityOnErrorCallback onerror;
+
+  // Certified apps can specify this
+  DOMString audience;
+};
+
+dictionary IdentityRequestOptions {
+  // Optional parameters
+  long refreshAuthentication;
+  DOMString termsOfService;
+  DOMString privacyPolicy;
+  DOMString backgroundColor;
+  DOMString siteLogo;
+  DOMString siteName;
+  DOMString returnTo;
+
+  IdentityOnCancelCallback oncancel;
+
+  // Certified apps can specify this
+  DOMString origin;
+};
+
+dictionary IdentityGetOptions {
+  DOMString privacyPolicy;
+  DOMString termsOfService;
+  DOMString privacyURL;
+  DOMString tosURL;
+  DOMString siteName;
+  DOMString siteLogo;
+};
+
+[JSImplementation="@mozilla.org/identity/manager;1",
+ NoInterfaceObject,
+ NavigatorProperty="mozId",
+ Pref="dom.identity.enabled"]
+interface IdentityManager {
+  void watch(optional IdentityWatchOptions options);
+  void request(optional IdentityRequestOptions options);
+  void logout();
+
+  [Pref="dom.identity.exposeLegacyGetAPI"]
+  void get(IdentityOnLoginCallback callback, optional IdentityGetOptions options);
+
+  [Pref="dom.identity.exposeLegacyGetVerifiedEmailAPI"]
+  void getVerifiedEmail(IdentityOnLoginCallback callback);
+};
+
--- a/dom/webidl/PeerConnectionImpl.webidl
+++ b/dom/webidl/PeerConnectionImpl.webidl
@@ -65,16 +65,17 @@ interface PeerConnectionImpl  {
   readonly attribute DOMString fingerprint;
   readonly attribute DOMString localDescription;
   readonly attribute DOMString remoteDescription;
 
   readonly attribute PCImplIceConnectionState iceConnectionState;
   readonly attribute PCImplIceGatheringState iceGatheringState;
   readonly attribute PCImplSignalingState signalingState;
   readonly attribute PCImplSipccState sipccState;
+  readonly attribute DOMString id;
 
   attribute DOMString peerIdentity;
   readonly attribute boolean privacyRequested;
 
   /* Data channels */
   [Throws]
   DataChannel createDataChannel(DOMString label, DOMString protocol,
     unsigned short type, boolean outOfOrderAllowed,
--- a/dom/webidl/RTCPeerConnection.webidl
+++ b/dom/webidl/RTCPeerConnection.webidl
@@ -113,16 +113,19 @@ interface mozRTCPeerConnection : EventTa
   void addIceCandidate (mozRTCIceCandidate candidate,
                         optional VoidFunction successCallback,
                         optional RTCPeerConnectionErrorCallback failureCallback);
   readonly attribute RTCIceGatheringState iceGatheringState;
   readonly attribute RTCIceConnectionState iceConnectionState;
   [Pref="media.peerconnection.identity.enabled"]
   readonly attribute RTCIdentityAssertion? peerIdentity;
 
+  [ChromeOnly]
+  readonly attribute DOMString id;
+
   sequence<MediaStream> getLocalStreams ();
   sequence<MediaStream> getRemoteStreams ();
   MediaStream? getStreamById (DOMString streamId);
   void addStream (MediaStream stream, optional MediaConstraints constraints);
   void removeStream (MediaStream stream);
   void close ();
   attribute EventHandler onnegotiationneeded;
   attribute EventHandler onicecandidate;
@@ -143,8 +146,9 @@ interface mozRTCPeerConnection : EventTa
   attribute EventHandler onidentityresult;
   [Pref="media.peerconnection.identity.enabled"]
   attribute EventHandler onpeeridentity;
   [Pref="media.peerconnection.identity.enabled"]
   attribute EventHandler onidpassertionerror;
   [Pref="media.peerconnection.identity.enabled"]
   attribute EventHandler onidpvalidationerror;
 };
+
new file mode 100644
--- /dev/null
+++ b/dom/webidl/RTCPeerConnectionStatic.webidl
@@ -0,0 +1,39 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+/*
+  Right now, it is not possible to add static functions to a JS implemented
+  interface (see bug 863952), so we need to create a simple interface with a
+  trivial constructor and no data to hold these functions that really ought to
+  be static in mozRTCPeerConnection.
+  TODO(bcampen@mozilla.com) Merge this code into RTCPeerConnection once this
+  limitation is gone. (Bug 1017082)
+*/
+
+enum RTCLifecycleEvent {
+    "initialized",
+    "icegatheringstatechange",
+    "iceconnectionstatechange"
+};
+
+callback PeerConnectionLifecycleCallback = void (mozRTCPeerConnection pc,
+                                                 unsigned long long windowId,
+                                                 RTCLifecycleEvent eventType);
+
+[ChromeOnly,
+ Pref="media.peerconnection.enabled",
+ JSImplementation="@mozilla.org/dom/peerconnectionstatic;1",
+ Constructor()]
+interface mozRTCPeerConnectionStatic {
+
+  /* One slot per window (the window in which the register call is made),
+     automatically unregistered when window goes away.
+     Fires when a PC is created, and whenever the ICE connection state or
+     gathering state changes. */
+  void registerPeerConnectionLifecycleCallback(
+    PeerConnectionLifecycleCallback cb);
+};
+
--- a/dom/webidl/WebrtcGlobalInformation.webidl
+++ b/dom/webidl/WebrtcGlobalInformation.webidl
@@ -10,16 +10,17 @@ dictionary WebrtcGlobalStatisticsReport 
 
 callback WebrtcGlobalStatisticsCallback = void (WebrtcGlobalStatisticsReport reports);
 callback WebrtcGlobalLoggingCallback = void (sequence<DOMString> logMessages);
 
 [ChromeOnly]
 interface WebrtcGlobalInformation {
 
   [Throws]
-  static void getAllStats(WebrtcGlobalStatisticsCallback callback);
+  static void getAllStats(WebrtcGlobalStatisticsCallback callback,
+                          optional DOMString pcIdFilter);
 
   [Throws]
   static void getLogging(DOMString pattern,
                          WebrtcGlobalLoggingCallback callback);
 };
 
 
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -207,16 +207,17 @@ WEBIDL_FILES = [
     'IDBFileHandle.webidl',
     'IDBIndex.webidl',
     'IDBKeyRange.webidl',
     'IDBObjectStore.webidl',
     'IDBOpenDBRequest.webidl',
     'IDBRequest.webidl',
     'IDBTransaction.webidl',
     'IDBVersionChangeEvent.webidl',
+    'Identity.webidl',
     'ImageData.webidl',
     'ImageDocument.webidl',
     'InputEvent.webidl',
     'InputMethod.webidl',
     'InspectorUtils.webidl',
     'InstallEvent.webidl',
     'InstallPhaseEvent.webidl',
     'InterAppConnection.webidl',
@@ -293,16 +294,17 @@ WEBIDL_FILES = [
     'PushManager.webidl',
     'Range.webidl',
     'Rect.webidl',
     'RGBColor.webidl',
     'RTCConfiguration.webidl',
     'RTCIceCandidate.webidl',
     'RTCIdentityAssertion.webidl',
     'RTCPeerConnection.webidl',
+    'RTCPeerConnectionStatic.webidl',
     'RTCSessionDescription.webidl',
     'RTCStatsReport.webidl',
     'Screen.webidl',
     'ScriptProcessorNode.webidl',
     'ScrollAreaEvent.webidl',
     'Selection.webidl',
     'ServiceWorker.webidl',
     'ServiceWorkerContainer.webidl',
--- a/dom/xbl/nsXBLBinding.cpp
+++ b/dom/xbl/nsXBLBinding.cpp
@@ -99,28 +99,28 @@ static const JSClass gPrototypeJSClass =
     nullptr, nullptr, nullptr, nullptr
 };
 
 // Implementation /////////////////////////////////////////////////////////////////
 
 // Constructors/Destructors
 nsXBLBinding::nsXBLBinding(nsXBLPrototypeBinding* aBinding)
   : mMarkedForDeath(false)
-  , mUsingXBLScope(false)
+  , mUsingContentXBLScope(false)
   , mPrototypeBinding(aBinding)
 {
   NS_ASSERTION(mPrototypeBinding, "Must have a prototype binding!");
   // Grab a ref to the document info so the prototype binding won't die
   NS_ADDREF(mPrototypeBinding->XBLDocumentInfo());
 }
 
 // Constructor used by web components.
 nsXBLBinding::nsXBLBinding(ShadowRoot* aShadowRoot, nsXBLPrototypeBinding* aBinding)
   : mMarkedForDeath(false),
-    mUsingXBLScope(false),
+    mUsingContentXBLScope(false),
     mPrototypeBinding(aBinding),
     mContent(aShadowRoot)
 {
   NS_ASSERTION(mPrototypeBinding, "Must have a prototype binding!");
   // Grab a ref to the document info so the prototype binding won't die
   NS_ADDREF(mPrototypeBinding->XBLDocumentInfo());
 }
 
@@ -273,17 +273,17 @@ nsXBLBinding::SetBoundElement(nsIContent
   // Compute whether we're using an XBL scope.
   //
   // We disable XBL scopes for remote XUL, where we care about compat more
   // than security. So we need to know whether we're using an XBL scope so that
   // we can decide what to do about untrusted events when "allowuntrusted"
   // is not given in the handler declaration.
   nsCOMPtr<nsIGlobalObject> go = mBoundElement->OwnerDoc()->GetScopeObject();
   NS_ENSURE_TRUE_VOID(go && go->GetGlobalJSObject());
-  mUsingXBLScope = xpc::UseXBLScope(js::GetObjectCompartment(go->GetGlobalJSObject()));
+  mUsingContentXBLScope = xpc::UseContentXBLScope(js::GetObjectCompartment(go->GetGlobalJSObject()));
 }
 
 bool
 nsXBLBinding::HasStyleSheets() const
 {
   // Find out if we need to re-resolve style.  We'll need to do this
   // if we have additional stylesheets in our binding document.
   if (mPrototypeBinding->HasStyleSheets())
@@ -507,33 +507,33 @@ nsXBLBinding::InstallEventHandlers()
           if ((curr->GetType() & (NS_HANDLER_TYPE_XBL_COMMAND |
                                   NS_HANDLER_TYPE_SYSTEM)) &&
               (isChromeBinding || mBoundElement->IsInNativeAnonymousSubtree())) {
             flags.mInSystemGroup = true;
           }
 
           bool hasAllowUntrustedAttr = curr->HasAllowUntrustedAttr();
           if ((hasAllowUntrustedAttr && curr->AllowUntrustedEvents()) ||
-              (!hasAllowUntrustedAttr && !isChromeDoc && !mUsingXBLScope)) {
+              (!hasAllowUntrustedAttr && !isChromeDoc && !mUsingContentXBLScope)) {
             flags.mAllowUntrustedEvents = true;
           }
 
           manager->AddEventListenerByType(handler,
                                           nsDependentAtomString(eventAtom),
                                           flags);
         }
       }
 
       const nsCOMArray<nsXBLKeyEventHandler>* keyHandlers =
         mPrototypeBinding->GetKeyEventHandlers();
       int32_t i;
       for (i = 0; i < keyHandlers->Count(); ++i) {
         nsXBLKeyEventHandler* handler = keyHandlers->ObjectAt(i);
         handler->SetIsBoundToChrome(isChromeDoc);
-        handler->SetUsingXBLScope(mUsingXBLScope);
+        handler->SetUsingContentXBLScope(mUsingContentXBLScope);
 
         nsAutoString type;
         handler->GetEventName(type);
 
         // If this is a command, add it in the system event group, otherwise
         // add it to the standard event group.
 
         // Figure out if we're using capturing or not.
@@ -915,18 +915,18 @@ GetOrCreateMapEntryForPrototype(JSContex
   // node with grand-proto HTMLDivElement and a bound anonymous node whose
   // grand-proto is the XBL scope's cross-compartment wrapper to HTMLDivElement.
   // Since we have to wrap the WeakMap keys into its scope, this distinction
   // would be lost if we don't do something about it.
   //
   // So we define two maps - one class objects that live in content (prototyped
   // to content prototypes), and the other for class objects that live in the
   // XBL scope (prototyped to cross-compartment-wrapped content prototypes).
-  const char* name = xpc::IsInXBLScope(proto) ? "__ContentClassObjectMap__"
-                                              : "__XBLClassObjectMap__";
+  const char* name = xpc::IsInContentXBLScope(proto) ? "__ContentClassObjectMap__"
+                                                     : "__XBLClassObjectMap__";
 
   // Now, enter the XBL scope, since that's where we need to operate, and wrap
   // the proto accordingly.
   JS::Rooted<JSObject*> scope(cx, xpc::GetXBLScopeOrGlobal(cx, proto));
   JS::Rooted<JSObject*> wrappedProto(cx, proto);
   JSAutoCompartment ac(cx, scope);
   if (!JS_WrapObject(cx, &wrappedProto)) {
     return nullptr;
@@ -1111,17 +1111,17 @@ nsXBLBinding::LookupMember(JSContext* aC
   //
   // Note that we only end up in LookupMember for XrayWrappers from XBL scopes
   // into content. So for NAC reflectors that live in the XBL scope, we should
   // never get here. But on the off-chance that someone adds new callsites to
   // LookupMember, we do a release-mode assertion as belt-and-braces.
   // We do a release-mode assertion here to be extra safe.
   JS::Rooted<JSObject*> boundScope(aCx,
     js::GetGlobalForObjectCrossCompartment(mBoundElement->GetWrapper()));
-  MOZ_RELEASE_ASSERT(!xpc::IsInXBLScope(boundScope));
+  MOZ_RELEASE_ASSERT(!xpc::IsInContentXBLScope(boundScope));
   JS::Rooted<JSObject*> xblScope(aCx, xpc::GetXBLScope(aCx, boundScope));
   NS_ENSURE_TRUE(xblScope, false);
   MOZ_ASSERT(boundScope != xblScope);
 
   // Enter the xbl scope and invoke the internal version.
   {
     JSAutoCompartment ac(aCx, xblScope);
     JS::Rooted<jsid> id(aCx, aId);
--- a/dom/xbl/nsXBLBinding.h
+++ b/dom/xbl/nsXBLBinding.h
@@ -155,17 +155,17 @@ public:
   // Returns a live node list that iterates over the anonymous nodes generated
   // by this binding.
   nsAnonymousContentList* GetAnonymousNodeList();
 
 // MEMBER VARIABLES
 protected:
 
   bool mMarkedForDeath;
-  bool mUsingXBLScope;
+  bool mUsingContentXBLScope;
 
   nsXBLPrototypeBinding* mPrototypeBinding; // Weak, but we're holding a ref to the docinfo
   nsCOMPtr<nsIContent> mContent; // Strong. Our anonymous content stays around with us.
   nsRefPtr<nsXBLBinding> mNextBinding; // Strong. The derived binding owns the base class bindings.
 
   nsIContent* mBoundElement; // [WEAK] We have a reference, but we don't own it.
 
   // The <xbl:children> elements that we found in our <xbl:content> when we
--- a/dom/xbl/nsXBLEventHandler.cpp
+++ b/dom/xbl/nsXBLEventHandler.cpp
@@ -66,17 +66,17 @@ nsXBLMouseEventHandler::EventMatched(nsI
 }
 
 nsXBLKeyEventHandler::nsXBLKeyEventHandler(nsIAtom* aEventType, uint8_t aPhase,
                                            uint8_t aType)
   : mEventType(aEventType),
     mPhase(aPhase),
     mType(aType),
     mIsBoundToChrome(false),
-    mUsingXBLScope(false)
+    mUsingContentXBLScope(false)
 {
 }
 
 nsXBLKeyEventHandler::~nsXBLKeyEventHandler()
 {
 }
 
 NS_IMPL_ISUPPORTS(nsXBLKeyEventHandler, nsIDOMEventListener)
@@ -92,17 +92,17 @@ nsXBLKeyEventHandler::ExecuteMatchedHand
   nsCOMPtr<EventTarget> target = aKeyEvent->InternalDOMEvent()->GetCurrentTarget();
 
   bool executed = false;
   for (uint32_t i = 0; i < mProtoHandlers.Length(); ++i) {
     nsXBLPrototypeHandler* handler = mProtoHandlers[i];
     bool hasAllowUntrustedAttr = handler->HasAllowUntrustedAttr();
     if ((trustedEvent ||
         (hasAllowUntrustedAttr && handler->AllowUntrustedEvents()) ||
-        (!hasAllowUntrustedAttr && !mIsBoundToChrome && !mUsingXBLScope)) &&
+        (!hasAllowUntrustedAttr && !mIsBoundToChrome && !mUsingContentXBLScope)) &&
         handler->KeyEventMatched(aKeyEvent, aCharCode, aIgnoreShiftKey)) {
       handler->ExecuteHandler(target, aKeyEvent);
       executed = true;
     }
   }
   return executed;
 }
 
--- a/dom/xbl/nsXBLEventHandler.h
+++ b/dom/xbl/nsXBLEventHandler.h
@@ -81,32 +81,32 @@ public:
     return mType;
   }
 
   void SetIsBoundToChrome(bool aIsBoundToChrome)
   {
     mIsBoundToChrome = aIsBoundToChrome;
   }
 
-  void SetUsingXBLScope(bool aUsingXBLScope)
+  void SetUsingContentXBLScope(bool aUsingContentXBLScope)
   {
-    mUsingXBLScope = aUsingXBLScope;
+    mUsingContentXBLScope = aUsingContentXBLScope;
   }
 
 private:
   nsXBLKeyEventHandler();
   bool ExecuteMatchedHandlers(nsIDOMKeyEvent* aEvent, uint32_t aCharCode,
                                 bool aIgnoreShiftKey);
 
   nsTArray<nsXBLPrototypeHandler*> mProtoHandlers;
   nsCOMPtr<nsIAtom> mEventType;
   uint8_t mPhase;
   uint8_t mType;
   bool mIsBoundToChrome;
-  bool mUsingXBLScope;
+  bool mUsingContentXBLScope;
 };
 
 nsresult
 NS_NewXBLEventHandler(nsXBLPrototypeHandler* aHandler,
                       nsIAtom* aEventType,
                       nsXBLEventHandler** aResult);
 
 nsresult
--- a/dom/xbl/nsXBLProtoImplMethod.cpp
+++ b/dom/xbl/nsXBLProtoImplMethod.cpp
@@ -99,17 +99,17 @@ nsresult
 nsXBLProtoImplMethod::InstallMember(JSContext* aCx,
                                     JS::Handle<JSObject*> aTargetClassObject)
 {
   NS_PRECONDITION(IsCompiled(),
                   "Should not be installing an uncompiled method");
   MOZ_ASSERT(js::IsObjectInContextCompartment(aTargetClassObject, aCx));
 
   JS::Rooted<JSObject*> globalObject(aCx, JS_GetGlobalForObject(aCx, aTargetClassObject));
-  MOZ_ASSERT(xpc::IsInXBLScope(globalObject) ||
+  MOZ_ASSERT(xpc::IsInContentXBLScope(globalObject) ||
              globalObject == xpc::GetXBLScope(aCx, globalObject));
 
   JS::Rooted<JSObject*> jsMethodObject(aCx, GetCompiledMethod());
   if (jsMethodObject) {
     nsDependentString name(mName);
 
     JS::Rooted<JSObject*> method(aCx, JS_CloneFunctionObject(aCx, jsMethodObject, globalObject));
     NS_ENSURE_TRUE(method, NS_ERROR_OUT_OF_MEMORY);
--- a/dom/xbl/nsXBLProtoImplProperty.cpp
+++ b/dom/xbl/nsXBLProtoImplProperty.cpp
@@ -124,17 +124,17 @@ nsresult
 nsXBLProtoImplProperty::InstallMember(JSContext *aCx,
                                       JS::Handle<JSObject*> aTargetClassObject)
 {
   NS_PRECONDITION(mIsCompiled,
                   "Should not be installing an uncompiled property");
   MOZ_ASSERT(mGetter.IsCompiled() && mSetter.IsCompiled());
   MOZ_ASSERT(js::IsObjectInContextCompartment(aTargetClassObject, aCx));
   JS::Rooted<JSObject*> globalObject(aCx, JS_GetGlobalForObject(aCx, aTargetClassObject));
-  MOZ_ASSERT(xpc::IsInXBLScope(globalObject) ||
+  MOZ_ASSERT(xpc::IsInContentXBLScope(globalObject) ||
              globalObject == xpc::GetXBLScope(aCx, globalObject));
 
   JS::Rooted<JSObject*> getter(aCx, mGetter.GetJSFunction());
   JS::Rooted<JSObject*> setter(aCx, mSetter.GetJSFunction());
   if (getter || setter) {
     if (getter) {
       if (!(getter = ::JS_CloneFunctionObject(aCx, getter, globalObject)))
         return NS_ERROR_OUT_OF_MEMORY;
--- a/editor/composer/src/moz.build
+++ b/editor/composer/src/moz.build
@@ -34,9 +34,12 @@ RESOURCE_FILES += [
     'res/table-add-row-before-hover.gif',
     'res/table-add-row-before.gif',
     'res/table-remove-column-active.gif',
     'res/table-remove-column-hover.gif',
     'res/table-remove-column.gif',
     'res/table-remove-row-active.gif',
     'res/table-remove-row-hover.gif',
     'res/table-remove-row.gif',
+    'res/text_selection_handle.png',
+    'res/text_selection_handle@1.5.png',
+    'res/text_selection_handle@2.png',
 ]
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..656854decc593d3ccd8767654f1743fb2523b64e
GIT binary patch
literal 1580
zc$|Gy4NMbf7`{?1D%Saj!@qGJ8^SW$-j)7JZ3`_w&azsV!ZaDzyKAY?-g!N2AqXO|
zj?5q8R%L27j8hSv6VVu%N<bM9LAU78;S33OnrsflE+~+~t|)9-)Gv4UeczksdEWPX
zzb98=Oi$s@STq9w0DfAk&cx31*f(MtFZkX6<6G>skkMx`8Ki}Ap)>(#anekHX?D~~
zm<SXv+|*7a008%0TV@uMWmv7mNV@<HVgznG1q4Te+D)NY9>IWS!fJD<p#C2kA<%}a
zpmh?1$Ur3#xwh0In#d?h&%}!IFa-{&SAq#{B`aVj7!-8d^Bqp5TLq2lD%o`~EQG*u
z2$QFRUKEvOFoH=WO@I=CI1UrRFesG@;CP8t3V#41A{Y^h*hd-%$15dLB_ab~4v5W0
z;})e!m;5ppyHi2A3_~e}LYK=WaES#ZZ56@_g(9edAaN`r&ROVSP<Nce88xAxBb*p*
zqZk|M0E2tfOcpRI2%0#8otl(&IA6AjZJ5xFQbJfD68;BhFuWORw@;#-jES(k8STt0
zqzIvjaFPWy#$KEyDoCc3Ni=~nB%Mi;`4e3<=8_EQ%q1x>DMJdz7*NdS2nJ%u83uzg
z&EaHF2S%jnR1g~x*lf5`s!LSJ^{_;N=o4XBFIH%El0-x<k%<*>q88CkaCIbBU?&{R
z1Q-98D}60DXoeSTg0^iY@MM~_gX4!*+FntMR)$DL$qFs2Cx3M<FSz*YYl(ktEkd>!
z;s36BBE<S9xSTXDyP32;;b8qvv&J4cFOCI(kgwBp+Dv!1IsVG9mlG*J+1XiB`n>F}
ze*N}!n)73IZC8I?J!Pi3>z-@HKE2Ub*S&4&A7J^wiPpS`*%ab`>J^EQvuCfzdP9ct
z@r69v+to1d=hYh$8o-qYYw(?>9`n-T(^zwO`un%KHqtS9448@34cu$!Trl0x8`UkU
z)YjI^VxN2>KZW6FxMod93l@{{+gabxIq5t39vEtk{t1l!ZkKhZ!soriW@_)Hn;Ii~
zc9$qz(BsXu{Z8<i+H+llep~O~C_27(SX<01lWd~0{Rg@a-QG~33-y8XuWAG#tM1l)
z*dE96@RxJv0J(=}9<S|M(9$<`uZY{^;c)kbM%=B}lyzuAa&T<_gL>+5D3aaeDZ8}X
zyDqZ*TGNSU4KHWT{8{_Rs#CL&#Xe-#+BFq9Jem^~CBATFVX8W8p4{v7=ORX+>S7^(
z>--gMB56)hueG`@-m<I;?HDT=8%?gr9xEMv&bbj)9sm4M;9$y^5rcpJ;vWH>8c$%j
z#QzjtwPX;zc`TqAmbIPf|EMUhD=;$nJ691MD0V+;KKb4)0p}}qmG;KS$iV&5oT8zd
zM@S2Q=f-3+fB4yhk^L#1Qw!96_b<)#l~_EM+l}As?%UF`?EB`H6UUD1IMhQe`rF@n
z!TQAkWzXqP&xN&ZnSQObN_&)dCUq!`ez-%mxL=IGTX>6VcpY;#$S)82M(?NW8d|C;
z-yCwWx@}~)X$q%4JS^s5NL@l$V|6XQ*1KX)-T7soSAI54yj*stvA^d^pm^}fKx<(A
zp37DDN`aa_!E80hT*(<b<F$fh`6@mNF5sGt<|!Ym!2xwO#p#t9$;+XNxfc_zhD*WD
sCC2jX!$+^HYaaF+ha(%Jw*p+Ca&A|{+oOLS3;w^;^y#|Oi8<T;0Y<7^EC2ui
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..d99b3c93b6c9471deb388c0693250dcbc77fc0dc
GIT binary patch
literal 1899
zc$|Gz4NwzD6b^zIsiD@N3YFUPl42B-aKQvP2sSYxh7uYBEi@`}B!@&uE?f>0pjZWe
zKvUETRvE1Si0CM&v?wYTG`3_^{0WK*Q9!M<Xj`N+5NLZ*v7OG~&hG8LH{bX6?b~nX
ziWD(Yn%8??6bgkVTPRkN^ZVprP(8_S<|AkYIr$Qj1R@qoAxsF4QiK{T83klIWI3ut
z5lwc+PiQ!W;?B^<B@hYnXjqNu*oX_mHtP%&R|}tSHX!OWlmL>^<yyS}d{*B80$Pm#
zjOWP%<%R`ls&-)xj>hK1#Hn-A)S()1egqJ1hRFarlt2KpE?sYg%>wX6T$o(D+#C>i
z0U^=^;LD^E<O*N`hNA$F9mG-xLJ+{`v!QuBJ|CJ3a04MOCy?xX7Bmm$@nLQ-@T!62
zZn!1|R*EBE?L}GwFqI$-Fo$C@nb@WvHij?fK%t?bt{7Y{i$t)D*?Iyov-C##NCYuz
zRO4C$p~duo%SMv1OhN#HBS+8~UWe5iUzLe07{`nlI1oFK^B<sG{${97_Zn>^lxWJE
z(Z;xJ1IkgNMl2IolNXmlcaaV70vtsM43ERG^pPqmQZWKEreX$QK`b9&$`Q3z?{dt3
z!H~;gnchesdNnE&3qaDt)@n5{Un~j@k&yM_N<<JO2?`a8c_MBIFE}U^5(&A&5v~|h
zXX;QrF~ZgS%N35wb&2668^yI*s3sD}bij*4!`jhv2_8L{mt4*0xrB_$<&ep6{&&?Q
zE>b_P<!j-R#%t-LdQ$H=DQwQ$Ur5OJ(k>GV<IHz1e3QL!O2(8+k2{u{ZY|yPsZGa|
zhw&%f+2HM!&-8--V%ogFiW#%-`X9TzVZga>f8{oi$!uo8w72%m-B{h!+~{qwt1w$y
zd*-pWC&L<l&l|Oi*3TH)J}`WH$JLdNXTu%21&inGysbEL@5r{H2@sX~#Az?L4xCwg
zwAtjJ(0Jfn!PkSU8cQlGME#dC%6w(gud17_$M__+m4Kz=XR(io&VT!*-@7$;s`0JD
zDwRypd$Uc2itysVOS4tePCx<Ds!P=Ia}L$fTLo*lj74XzSbS<(Plk5RR(42+=$n0V
zPnoxY{0Qf>;E5-#+`fFop3+_|bSo_QnvvOjatwtz_(f5auDCtpw@Ds5l7CcqM4wA2
z*}Vg<r~b)-Z(~0xhOcJ~d~)Tm)w0|(zjOG*aox(kvwwWPq3*(TcejgHxPAr^!AVTB
zscw13KHas4nX3FY&q0%RPF&Yzxp`tjQCI!r^{iUF=pilI-Tm~mRYdbfRlw94JHmXD
zHccukiY<;=8D3MFj4YebiLK?GDq*Jw@#E-Jp4u-jZ;9~9^A11Jne~(PrL2lZT75Ob
zEobnm7=8AipZDfuv<|w{c0%!o3#V@l-VRQTs}}iwP`c$*e0eQ(=0WSzh|}v7Rh^Kp
zg?}NvNOgjxQUp{gA;?NZ`!~s=CfB6*dJ*NhyP0l3Kbl*(3P*p?%!9c(ZdI0i<(R)N
zY6|&LG0a2P%C<<yGG#0J{i9QwrZ!XKN<5fjWgpSQO7@qCJm}0TS($a+>+r-)AN!qk
zGuda=-&1^NyT<57Jo<wZ;r`H;-d{IQ?m2XLpfF)f39at5-_tS->s|vVmPX`ntRLrf
zfF{ez3eQVmPHS;=s2oX`^Sr-r@3eGR&Cjd5p#DB@v-=&@_AjCPS+&&R8cP59)T6dA
z!=$}Oazj=>?Wyu|)|A&9?y|iZ7E?y<;@^9ZR|K>M`Pq&)b+)!{Th}A-mEQmEGlT9K
zSbVe9{;)S7^&H;XT(i=lSoPc4Sj-mVcs#h|_ZbzH%aWbHZc!8!yw$U8GX0xlyWZBJ
z{!{vW4rQMoc+hX_DW@F-B0C!IU6HbGVQOnZZD8a(Gs|i!hG)h08>d~S*PgDv&nzxb
z$J_DVdzT-jJ&sCxINf6HIG@xG;9<?BTLLXDSw1xFVphx2=;X*UOG{Uep&STlxcZB;
z?fDIzbA>bW@4+-_+g48M#-y8ltGj+VOuJZ+H)UTWjXm>>tqIwkD7}13Qtp_wfB#lT
z@I1dE)ow>O=Rse=1N)7=E7y0~&Don98a$m1cQH3l3T}_GiucA#T)#h=Bu0Exl$8Gu
DQ5go@
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..a7fc3901ae69ac20299ea421aab904807801831e
GIT binary patch
literal 2382
zc$|G!dpMM7A09bo$RxWt8^aQ7%)!hs!-Sck#K^~pB#fCC6Z6i|GC73JM8wx&m(;4r
zDhee{j!_96bk3#~wjxQ$iir6}rSH4G>uR6tec$K#{jU4|J@@l_?mymaKi>`7nueMX
z2t?a!Bh6niTPa?&hMMA=a$aMdVlohW1d9WBQQ`!q5P-O|d659zi_44#`~fCgBIp8K
zAP{9s4kK6`O!pzNcwBqt62?A}%ZDrt7uQ5SlNAey;gLWz2P7lL8!sc^95xvdgr}qF
zd@2yb*|<{(1nl%>uy)3>h-`%Gdbmp>Nl}0ch?($2ZX75gC6W=J>XH=er8Eiw{{#`o
zk`bRp1=IcDRGtuk<Lw=gEHnlKClKs0I6Q%Xv4dmL7%U2{cnC-gj)W(Wu#WIA1EJVW
z$c`fU(>%ZIrHIIg7_pd7LZK2866_Nk?0Ldy6oyD7F4e$dkqQJ-Bmu?DL?kG(Sgt?=
zL@XhPFXr$-_)^S_<i(502*mOcxcskWLD82sDH?`KWb#oMdo=2QfOPslL%H0qXpz_-
zi27%=h#}zvD1Si2ix;vK7Z+u*MCOyILVzje2^l<I+;SKFVt8VnD2B&}Qv(R_b#x|+
z11=?OJ~8NYk{2ivGeH*MMI$2=8G8<gO(M`JL?;gnjfnN2U@#sIM0XmVf_1_>IuJ1w
zcdYv|m&Rkoa{*Ai%w_+V>+wzQk{Ld;0U_rnfbA*dap9j1P2zl83+3BdK6BaM)<XRz
z7o`w``oF7QPAUAewESvZMex=70I2Z0P+{z+xnK$eqT1?3b7v&>UOSu;7Z9k|I{$V)
zrl)SZBcZqMe0rq;E3OqrQPa0@=-ilPg)KEN(st!5zp&VWpl0aOahm$TwXQ|K)%#Z;
zMXXu_!>x+e-zrvq-*#Q)z0$Uh$Kcr9Wbw@7qyq1ei>Yxb*7cI{zLu7mQQ~lMQnyWM
zR%oHyL8#H5lMyYU1qMcZQ=b)jS?AajQ?ZRxwcR+fM)g*lPIxEnT?EWrFJs_=j(kq7
z@?No?)=-X3S<;$8<Fz)%SrZ!suX80Ku+nD}uC}H5NpaIt6F%7|N~<s>NZ@tx&#GEC
zi@Ma0ruKS=e#B|tXIr|u=%QDhdbs0}k9QYYhf!hot8&M&jPex?T0KYME4V91!i!VY
zt^l+Bu!F_Zzeg<|a4M<e54bs1!kYPWBBNZ`z}YmNzzYAU6u)yw-EgS7)>)^(ch~Rd
z>*sAY9QABF<gZ09%AwzoR^VZISMQMfY7Xb*+%c1b(g(=xg3b#gV+MnTdb>68!?dxG
zGRaW)@s?|oYSh8l4TET@raC44#>W#?@Szj8u&4GN$7?SjZ(cZe;XdR1)vP}FIw#of
z()naf&~Y1sQ|r3NFS#@CjQ_BH_@80FU6dcW6@l;YfwUJ9#y1tURLuquwoI{4Ha5SQ
zeiq*shvIY|n;|!e+ssQiE><c=+ADum{$-@0O!CX!AiKR?Yq}ov%VYw2w|#zHf=XDD
zavO$4HM#Q;nbc$mI%e;+fmL&lm!xKMpf2%|P69hKqs&cD>QR9*yXqYk$Nyc^d#U8j
z6t|24i@L7-JmPgp@x`GhRIQtbzYDNSE@95Uu^29fyBA7X3y216>AU>M_4k5LW#vKE
zn{z&NUgqvGK^AO?5v}Om?WH=pFM~K8@1d7Dn8}`coN#e&yfW?nQC-hhh?pyh9)~g=
z3pL~GfBb92c-39^Lym<#Hf-1_t!Bx5aovUL)oAnhnP}+1`^hwlReBEiT{A_=Yc8_Z
zER|tiT0>gU-BKB_MwFLvPB3X8%xEh-js2eMJ*ED9(9@Y}UfXKfM!~{XTyI<Ovv$<~
zTR<LCy7pnKbaMX<+XI%9xI$FxUQk8_(Spt)OZQ#kYP~&UvX^S2_-C)C^FJPFlnqO7
z{iHWv(E?i68?+jgJN?N&9p-Ca=yH)1QvJuqEl~b}p%GhojjyorjWC^9wfzHBFkr5W
zmNk|?cpDd6vA<)~W3pTJvRq)2*4m_^MR#hvJUgr3VAC7gJ&MFHJnC@f7mfXnm$pjJ
zmbb*3w9L&sIW^bRGy2vxN&e&CsZO3X*DVdT+A;IfCe@7AyZ6kH=%<;r)@RD|>Piw=
z`#vP+cGspz4wsJ;LK^yd+w<+BA&>w=85oZvG}j0(zYz4N7i=`3{&7fmG$?nrSLwa?
zS~b<5Il<)UMM|*5AwbvkOtY-0r#iK#?`hvk)ck<{$xZc1t(FbBMou%LwdI@j8PP*_
zg>kp*cY<}YrlzR;souaB6YF~W+t&{vj2G$n2E!<|UGlOA&UgE(y1IUYssOjxZcj$#
zF8!gGb{rdx>d@8#+7~^7UMe-FoIExFHKNKxFNEwOg;e_9@rQOf_+NQsirXF(^d#w}
zNm=!l?7Tr!&DW-D@_G;vg17q?&%32C7|oWgC-QHHu?gJXdMqJn60xQ9k$SD1=p1rn
zyqNf5+pu+G?D2y-5w%;-)js9pCj2xFWT&tEwO94<<{YKf8k?-}=+g}A7p`RQf+Gd?
zg#icZFR73%G+b2vl{iLl_R*L#zO8NY*epL(Iv;Kp#?^>~<h$QUo^hu?lty}ORadvw
z#XZbwpW2kHv(ouw?DMvpsVdHRrP}8r^|}R>K50Ys1}RkKhsSUO+cSZtSNBItCZXH)
zZ>6VX886gXX5POvB|oayZmdepI}=W&S!OHYLuX$ZRGNKn8w_`IYpZ!^tZn6=7FHq0
fo5S|YRrDbvd#9*bW<Mk^{o#0d_|mE<5gGph#aQOF
--- a/editor/libeditor/base/nsEditorEventListener.cpp
+++ b/editor/libeditor/base/nsEditorEventListener.cpp
@@ -726,16 +726,20 @@ nsEditorEventListener::CleanupDragDropCa
   if (mCaret)
   {
     mCaret->EraseCaret();
     mCaret->SetCaretVisible(false);    // hide it, so that it turns off its timer
 
     nsCOMPtr<nsIPresShell> presShell = GetPresShell();
     if (presShell)
     {
+      nsCOMPtr<nsISelectionController> selCon(do_QueryInterface(presShell));
+      if (selCon) {
+        selCon->SetCaretEnabled(false);
+      }
       presShell->RestoreCaret();
     }
 
     mCaret->Terminate();
     mCaret = nullptr;
   }
 }
 
--- a/gfx/gl/GLContext.h
+++ b/gfx/gl/GLContext.h
@@ -2695,29 +2695,16 @@ public:
 
     // Shared code for GL extensions and GLX extensions.
     static bool ListHasExtension(const GLubyte *extensions,
                                  const char *extension);
 
     GLint GetMaxTextureImageSize() { return mMaxTextureImageSize; }
 
 public:
-    /**
-     * Context reset constants.
-     * These are used to determine who is guilty when a context reset
-     * happens.
-     */
-    enum ContextResetARB {
-        CONTEXT_NO_ERROR = 0,
-        CONTEXT_GUILTY_CONTEXT_RESET_ARB = 0x8253,
-        CONTEXT_INNOCENT_CONTEXT_RESET_ARB = 0x8254,
-        CONTEXT_UNKNOWN_CONTEXT_RESET_ARB = 0x8255
-    };
-
-public:
     std::map<GLuint, SharedSurface_GL*> mFBOMapping;
 
     enum {
         DebugEnabled = 1 << 0,
         DebugTrace = 1 << 1,
         DebugAbortOnError = 1 << 2
     };
 
--- a/gfx/ipc/GfxMessageUtils.h
+++ b/gfx/ipc/GfxMessageUtils.h
@@ -753,16 +753,17 @@ struct ParamTraits<mozilla::layers::Fram
     WriteParam(aMsg, aParam.mCompositionBounds);
     WriteParam(aMsg, aParam.mRootCompositionSize);
     WriteParam(aMsg, aParam.mScrollId);
     WriteParam(aMsg, aParam.mResolution);
     WriteParam(aMsg, aParam.mCumulativeResolution);
     WriteParam(aMsg, aParam.mZoom);
     WriteParam(aMsg, aParam.mDevPixelsPerCSSPixel);
     WriteParam(aMsg, aParam.mMayHaveTouchListeners);
+    WriteParam(aMsg, aParam.mMayHaveTouchCaret);
     WriteParam(aMsg, aParam.mPresShellId);
     WriteParam(aMsg, aParam.mIsRoot);
     WriteParam(aMsg, aParam.mHasScrollgrab);
     WriteParam(aMsg, aParam.mUpdateScrollOffset);
     WriteParam(aMsg, aParam.mScrollGeneration);
     aMsg->WriteBytes(aParam.mContentDescription,
                      sizeof(aParam.mContentDescription));
     WriteParam(aMsg, aParam.mTransformScale);
@@ -780,16 +781,17 @@ struct ParamTraits<mozilla::layers::Fram
             ReadParam(aMsg, aIter, &aResult->mCompositionBounds) &&
             ReadParam(aMsg, aIter, &aResult->mRootCompositionSize) &&
             ReadParam(aMsg, aIter, &aResult->mScrollId) &&
             ReadParam(aMsg, aIter, &aResult->mResolution) &&
             ReadParam(aMsg, aIter, &aResult->mCumulativeResolution) &&
             ReadParam(aMsg, aIter, &aResult->mZoom) &&
             ReadParam(aMsg, aIter, &aResult->mDevPixelsPerCSSPixel) &&
             ReadParam(aMsg, aIter, &aResult->mMayHaveTouchListeners) &&
+            ReadParam(aMsg, aIter, &aResult->mMayHaveTouchCaret) &&
             ReadParam(aMsg, aIter, &aResult->mPresShellId) &&
             ReadParam(aMsg, aIter, &aResult->mIsRoot) &&
             ReadParam(aMsg, aIter, &aResult->mHasScrollgrab) &&
             ReadParam(aMsg, aIter, &aResult->mUpdateScrollOffset) &&
             ReadParam(aMsg, aIter, &aResult->mScrollGeneration) &&
             aMsg->ReadBytes(aIter,
                             reinterpret_cast<const char**>(&aResult->mContentDescription),
                             sizeof(aResult->mContentDescription)) &&
--- a/gfx/layers/FrameMetrics.h
+++ b/gfx/layers/FrameMetrics.h
@@ -76,16 +76,17 @@ public:
     , mCriticalDisplayPort(0, 0, 0, 0)
     , mViewport(0, 0, 0, 0)
     , mScrollableRect(0, 0, 0, 0)
     , mResolution(1)
     , mCumulativeResolution(1)
     , mTransformScale(1)
     , mDevPixelsPerCSSPixel(1)
     , mMayHaveTouchListeners(false)
+    , mMayHaveTouchCaret(false)
     , mIsRoot(false)
     , mHasScrollgrab(false)
     , mScrollId(NULL_SCROLL_ID)
     , mScrollOffset(0, 0)
     , mZoom(1)
     , mUpdateScrollOffset(false)
     , mScrollGeneration(0)
     , mContentDescription()
@@ -108,16 +109,17 @@ public:
            mUseDisplayPortMargins == aOther.mUseDisplayPortMargins &&
            mCriticalDisplayPort.IsEqualEdges(aOther.mCriticalDisplayPort) &&
            mViewport.IsEqualEdges(aOther.mViewport) &&
            mScrollableRect.IsEqualEdges(aOther.mScrollableRect) &&
            mResolution == aOther.mResolution &&
            mCumulativeResolution == aOther.mCumulativeResolution &&
            mDevPixelsPerCSSPixel == aOther.mDevPixelsPerCSSPixel &&
            mMayHaveTouchListeners == aOther.mMayHaveTouchListeners &&
+           mMayHaveTouchCaret == aOther.mMayHaveTouchCaret &&
            mPresShellId == aOther.mPresShellId &&
            mIsRoot == aOther.mIsRoot &&
            mScrollId == aOther.mScrollId &&
            mScrollOffset == aOther.mScrollOffset &&
            mHasScrollgrab == aOther.mHasScrollgrab &&
            mUpdateScrollOffset == aOther.mUpdateScrollOffset;
   }
   bool operator!=(const FrameMetrics& aOther) const
@@ -339,16 +341,19 @@ public:
   // This can vary based on a variety of things, such as reflowing-zoom. The
   // conversion factor for device pixels to layers pixels is just the
   // resolution.
   CSSToLayoutDeviceScale mDevPixelsPerCSSPixel;
 
   // Whether or not this frame may have touch listeners.
   bool mMayHaveTouchListeners;
 
+  // Whether or not this frame may have touch caret.
+  bool mMayHaveTouchCaret;
+
   // Whether or not this is the root scroll frame for the root content document.
   bool mIsRoot;
 
   // Whether or not this frame is for an element marked 'scrollgrab'.
   bool mHasScrollgrab;
 
 public:
   void SetScrollOffset(const CSSPoint& aScrollOffset)
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -642,17 +642,18 @@ nsEventStatus AsyncPanZoomController::Re
 
   // If we may have touch listeners and touch action property is enabled, we
   // enable the machinery that allows touch listeners to preventDefault any touch inputs
   // and also waits for the allowed touch behavior values to be received from the outside.
   // This should not happen unless there are actually touch listeners and touch-action property
   // enable as it introduces potentially unbounded lag because it causes a round-trip through
   // content.  Usually, if content is responding in a timely fashion, this only introduces a
   // nearly constant few hundred ms of lag.
-  if (mFrameMetrics.mMayHaveTouchListeners && aEvent.mInputType == MULTITOUCH_INPUT &&
+  if ((mFrameMetrics.mMayHaveTouchListeners || mFrameMetrics.mMayHaveTouchCaret) &&
+      aEvent.mInputType == MULTITOUCH_INPUT &&
       (mState == NOTHING || mState == TOUCHING || IsPanningState(mState))) {
     const MultiTouchInput& multiTouchInput = aEvent.AsMultiTouchInput();
     if (multiTouchInput.mType == MultiTouchInput::MULTITOUCH_START) {
       SetState(WAITING_CONTENT_RESPONSE);
     }
   }
 
   if (mState == WAITING_CONTENT_RESPONSE || mHandlingTouchQueue) {
@@ -2029,16 +2030,17 @@ gfx3DMatrix AsyncPanZoomController::GetT
 void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aLayerMetrics, bool aIsFirstPaint) {
   ReentrantMonitorAutoEnter lock(mMonitor);
   bool isDefault = mFrameMetrics.IsDefault();
 
   mLastContentPaintMetrics = aLayerMetrics;
   UpdateTransformScale();
 
   mFrameMetrics.mMayHaveTouchListeners = aLayerMetrics.mMayHaveTouchListeners;
+  mFrameMetrics.mMayHaveTouchCaret = aLayerMetrics.mMayHaveTouchCaret;
   APZC_LOG_FM(aLayerMetrics, "%p got a NotifyLayersUpdated with aIsFirstPaint=%d", this, aIsFirstPaint);
 
   LogRendertraceRect(GetGuid(), "page", "brown", aLayerMetrics.mScrollableRect);
   LogRendertraceRect(GetGuid(), "painted displayport", "lightgreen",
     aLayerMetrics.mDisplayPort + aLayerMetrics.GetScrollOffset());
   if (!aLayerMetrics.mCriticalDisplayPort.IsEmpty()) {
     LogRendertraceRect(GetGuid(), "painted critical displayport", "darkgreen",
       aLayerMetrics.mCriticalDisplayPort + aLayerMetrics.GetScrollOffset());
@@ -2238,17 +2240,18 @@ void AsyncPanZoomController::ContentRece
   mTouchBlockState.mPreventDefaultSet = true;
   mTouchBlockState.mPreventDefault = aPreventDefault;
   CheckContentResponse();
 }
 
 void AsyncPanZoomController::CheckContentResponse() {
   bool canProceedToTouchState = true;
 
-  if (mFrameMetrics.mMayHaveTouchListeners) {
+  if (mFrameMetrics.mMayHaveTouchListeners ||
+      mFrameMetrics.mMayHaveTouchCaret) {
     canProceedToTouchState &= mTouchBlockState.mPreventDefaultSet;
   }
 
   if (mTouchActionPropertyEnabled) {
     canProceedToTouchState &= mTouchBlockState.mAllowedTouchBehaviorSet;
   }
 
   if (!canProceedToTouchState) {
--- a/gfx/layers/client/ClientTiledThebesLayer.cpp
+++ b/gfx/layers/client/ClientTiledThebesLayer.cpp
@@ -78,58 +78,23 @@ ClientTiledThebesLayer::BeginPaint()
     return;
   }
 
   mPaintData.mLowPrecisionPaintCount = 0;
   mPaintData.mPaintFinished = false;
   mPaintData.mCompositionBounds.SetEmpty();
   mPaintData.mCriticalDisplayPort.SetEmpty();
 
-  if (!GetBaseTransform().Is2DIntegerTranslation()) {
-    // Give up if the layer is transformed. The code below assumes that there
-    // is no transform set, and not making that assumption would cause huge
-    // complication to handle a quite rare case.
-    //
-    // FIXME The intention is to bail out of this function when there's a CSS
-    //       transform set on the layer, but unfortunately there's no way to
-    //       distinguish transforms due to scrolling from transforms due to
-    //       CSS transforms.
-    //
-    //       Because of this, there may be unintended behaviour when setting
-    //       2d CSS translations on the children of scrollable displayport
-    //       layers.
+  if (!GetBaseTransform().Is2D()) {
+    // Give up if there is a complex CSS transform on the layer. We might
+    // eventually support these but for now it's too complicated to handle
+    // given that it's a pretty rare scenario.
     return;
   }
 
-#ifdef MOZ_WIDGET_ANDROID
-  // Subframes on Fennec are not async scrollable because they have no displayport.
-  // However, the code in RenderLayer() picks up a displayport from the nearest
-  // scrollable ancestor container layer anyway, which is incorrect for Fennec. This
-  // behaviour results in the subframe getting clipped improperly and perma-blank areas
-  // while scrolling the subframe. To work around this, we detect if this layer is
-  // the primary scrollable layer and disable the tiling behaviour if it is not.
-  bool isPrimaryScrollableThebesLayer = false;
-  if (Layer* scrollable = ClientManager()->GetPrimaryScrollableLayer()) {
-    if (GetParent() == scrollable) {
-      for (Layer* child = scrollable->GetFirstChild(); child; child = child->GetNextSibling()) {
-        if (child->GetType() == Layer::TYPE_THEBES) {
-          if (child == this) {
-            // |this| is the first thebes layer child of the GetPrimaryScrollableLayer()
-            isPrimaryScrollableThebesLayer = true;
-          }
-          break;
-        }
-      }
-    }
-  }
-  if (!isPrimaryScrollableThebesLayer) {
-    return;
-  }
-#endif
-
   // Get the metrics of the nearest scrollable layer and the nearest layer
   // with a displayport.
   ContainerLayer* scrollAncestor = nullptr;
   ContainerLayer* displayPortAncestor = nullptr;
   for (ContainerLayer* ancestor = GetParent(); ancestor; ancestor = ancestor->GetParent()) {
     const FrameMetrics& metrics = ancestor->GetFrameMetrics();
     if (!scrollAncestor && metrics.GetScrollId() != FrameMetrics::NULL_SCROLL_ID) {
       scrollAncestor = ancestor;
--- a/gfx/layers/client/ContentClient.cpp
+++ b/gfx/layers/client/ContentClient.cpp
@@ -512,16 +512,19 @@ ContentClientDoubleBuffered::FinalizeFra
   }
 }
 
 void
 ContentClientDoubleBuffered::EnsureBackBufferIfFrontBuffer()
 {
   if (!mTextureClient && mFrontClient) {
     CreateBackBuffer(mFrontBufferRect);
+
+    mBufferRect = mFrontBufferRect;
+    mBufferRotation = mFrontBufferRotation;
   }
 }
 
 void
 ContentClientDoubleBuffered::UpdateDestinationFrom(const RotatedBuffer& aSource,
                                                    const nsIntRegion& aUpdateRegion)
 {
   DrawIterator iter;
--- a/gfx/thebes/gfxASurface.cpp
+++ b/gfx/thebes/gfxASurface.cpp
@@ -150,17 +150,17 @@ void
 gfxASurface::SetSurfaceWrapper(cairo_surface_t *csurf, gfxASurface *asurf)
 {
     if (!csurf)
         return;
     cairo_surface_set_user_data(csurf, &gfxasurface_pointer_key, asurf, SurfaceDestroyFunc);
 }
 
 already_AddRefed<gfxASurface>
-gfxASurface::Wrap (cairo_surface_t *csurf)
+gfxASurface::Wrap (cairo_surface_t *csurf, const gfxIntSize& aSize)
 {
     nsRefPtr<gfxASurface> result;
 
     /* Do we already have a wrapper for this surface? */
     result = GetSurfaceWrapper(csurf);
     if (result) {
         // fprintf(stderr, "Existing wrapper for %p -> %p\n", csurf, result);
         return result.forget();
@@ -185,29 +185,29 @@ gfxASurface::Wrap (cairo_surface_t *csur
 #endif
 #ifdef MOZ_X11
     else if (stype == CAIRO_SURFACE_TYPE_XLIB) {
         result = new gfxXlibSurface(csurf);
     }
 #endif
 #ifdef CAIRO_HAS_QUARTZ_SURFACE
     else if (stype == CAIRO_SURFACE_TYPE_QUARTZ) {
-        result = new gfxQuartzSurface(csurf);
+        result = new gfxQuartzSurface(csurf, aSize);
     }
     else if (stype == CAIRO_SURFACE_TYPE_QUARTZ_IMAGE) {
         result = new gfxQuartzImageSurface(csurf);
     }
 #endif
 #if defined(CAIRO_HAS_QT_SURFACE) && defined(MOZ_WIDGET_QT)
     else if (stype == CAIRO_SURFACE_TYPE_QT) {
         result = new gfxQPainterSurface(csurf);
     }
 #endif
     else {
-        result = new gfxUnknownSurface(csurf);
+        result = new gfxUnknownSurface(csurf, aSize);
     }
 
     // fprintf(stderr, "New wrapper for %p -> %p\n", csurf, result);
 
     return result.forget();
 }
 
 void
@@ -332,17 +332,17 @@ gfxASurface::CreateSimilarSurface(gfxCon
     cairo_surface_t *surface =
         cairo_surface_create_similar(mSurface, cairo_content_t(int(aContent)),
                                      aSize.width, aSize.height);
     if (cairo_surface_status(surface)) {
         cairo_surface_destroy(surface);
         return nullptr;
     }
 
-    nsRefPtr<gfxASurface> result = Wrap(surface);
+    nsRefPtr<gfxASurface> result = Wrap(surface, aSize);
     cairo_surface_destroy(surface);
     return result.forget();
 }
 
 already_AddRefed<gfxImageSurface>
 gfxASurface::GetAsReadableARGB32ImageSurface()
 {
     nsRefPtr<gfxImageSurface> imgSurface = GetAsImageSurface();
--- a/gfx/thebes/gfxASurface.h
+++ b/gfx/thebes/gfxASurface.h
@@ -45,17 +45,17 @@ public:
     virtual nsrefcnt Release(void);
 #endif
 
 public:
 
     /** Wrap the given cairo surface and return a gfxASurface for it.
      * This adds a reference to csurf (owned by the returned gfxASurface).
      */
-    static already_AddRefed<gfxASurface> Wrap(cairo_surface_t *csurf);
+    static already_AddRefed<gfxASurface> Wrap(cairo_surface_t *csurf, const gfxIntSize& aSize = gfxIntSize(-1, -1));
 
     /*** this DOES NOT addref the surface */
     cairo_surface_t *CairoSurface() {
         return mSurface;
     }
 
     gfxSurfaceType GetType() const;
 
@@ -269,23 +269,22 @@ protected:
     bool mAllowUseAsSource;
 };
 
 /**
  * An Unknown surface; used to wrap unknown cairo_surface_t returns from cairo
  */
 class gfxUnknownSurface : public gfxASurface {
 public:
-    gfxUnknownSurface(cairo_surface_t *surf)
-        : mSize(-1, -1)
+    gfxUnknownSurface(cairo_surface_t *surf, const gfxIntSize& aSize)
+        : mSize(aSize)
     {
         Init(surf, true);
     }
 
     virtual ~gfxUnknownSurface() { }
     virtual const nsIntSize GetSize() const { return mSize; }
-    void SetSize(const nsIntSize& aSize) { mSize = aSize; }
 
 private:
     nsIntSize mSize;
 };
 
 #endif /* GFX_ASURFACE_H */
--- a/gfx/thebes/gfxQuartzSurface.cpp
+++ b/gfx/thebes/gfxQuartzSurface.cpp
@@ -84,18 +84,19 @@ gfxQuartzSurface::gfxQuartzSurface(CGCon
 
     Init(surf);
     if (mSurfaceValid) {
       RecordMemoryUsed(mSize.height * 4 + sizeof(gfxQuartzSurface));
     }
 }
 
 gfxQuartzSurface::gfxQuartzSurface(cairo_surface_t *csurf,
+                                   const gfxIntSize& aSize,
                                    bool aForPrinting) :
-    mSize(-1.0, -1.0), mForPrinting(aForPrinting)
+    mSize(aSize), mForPrinting(aForPrinting)
 {
     mCGContext = cairo_quartz_surface_get_cg_context (csurf);
     CGContextRetain (mCGContext);
 
     Init(csurf, true);
 }
 
 gfxQuartzSurface::gfxQuartzSurface(unsigned char *data,
@@ -156,17 +157,17 @@ gfxQuartzSurface::CreateSimilarSurface(g
     cairo_surface_t *surface =
         cairo_quartz_surface_create_cg_layer(mSurface, (cairo_content_t)aType,
                                              aSize.width, aSize.height);
     if (cairo_surface_status(surface)) {
         cairo_surface_destroy(surface);
         return nullptr;
     }
 
-    nsRefPtr<gfxASurface> result = Wrap(surface);
+    nsRefPtr<gfxASurface> result = Wrap(surface, aSize);
     cairo_surface_destroy(surface);
     return result.forget();
 }
 
 CGContextRef
 gfxQuartzSurface::GetCGContextWithClip(gfxContext *ctx)
 {
 	return cairo_quartz_get_cg_context_with_clip(ctx->GetCairo());
--- a/gfx/thebes/gfxQuartzSurface.h
+++ b/gfx/thebes/gfxQuartzSurface.h
@@ -15,17 +15,17 @@
 class gfxContext;
 class gfxImageSurface;
 
 class gfxQuartzSurface : public gfxASurface {
 public:
     gfxQuartzSurface(const gfxSize& size, gfxImageFormat format, bool aForPrinting = false);
     gfxQuartzSurface(CGContextRef context, const gfxSize& size, bool aForPrinting = false);
     gfxQuartzSurface(CGContextRef context, const gfxIntSize& size, bool aForPrinting = false);
-    gfxQuartzSurface(cairo_surface_t *csurf, bool aForPrinting = false);
+    gfxQuartzSurface(cairo_surface_t *csurf, const gfxIntSize& aSize, bool aForPrinting = false);
     gfxQuartzSurface(unsigned char *data, const gfxSize& size, long stride, gfxImageFormat format, bool aForPrinting = false);
     gfxQuartzSurface(unsigned char *data, const gfxIntSize& size, long stride, gfxImageFormat format, bool aForPrinting = false);
 
     virtual ~gfxQuartzSurface();
 
     virtual already_AddRefed<gfxASurface> CreateSimilarSurface(gfxContentType aType,
                                                                const gfxIntSize& aSize);
 
--- a/gfx/thebes/gfxWindowsSurface.cpp
+++ b/gfx/thebes/gfxWindowsSurface.cpp
@@ -143,22 +143,17 @@ gfxWindowsSurface::CreateSimilarSurface(
                                        aSize.width, aSize.height);
     }
 
     if (cairo_surface_status(surface)) {
         cairo_surface_destroy(surface);
         return nullptr;
     }
 
-    nsRefPtr<gfxASurface> result = Wrap(surface);
-    if (mForPrinting) {
-      MOZ_ASSERT(result->GetType() == gfxSurfaceType::Recording);
-      gfxUnknownSurface *unknown = static_cast<gfxUnknownSurface*>(result.get());
-      unknown->SetSize(aSize);
-    }
+    nsRefPtr<gfxASurface> result = Wrap(surface, aSize);
     cairo_surface_destroy(surface);
     return result.forget();
 }
 
 gfxWindowsSurface::~gfxWindowsSurface()
 {
     if (mOwnsDC) {
         if (mWnd)
--- a/js/public/GCAPI.h
+++ b/js/public/GCAPI.h
@@ -369,25 +369,34 @@ extern JS_FRIEND_API(void)
 ShrinkGCBuffers(JSRuntime *rt);
 
 /*
  * Assert if a GC occurs while this class is live. This class does not disable
  * the static rooting hazard analysis.
  */
 class JS_PUBLIC_API(AutoAssertOnGC)
 {
+#ifdef DEBUG
     JSRuntime *runtime;
     size_t gcNumber;
 
   public:
     AutoAssertOnGC();
     explicit AutoAssertOnGC(JSRuntime *rt);
     ~AutoAssertOnGC();
 
     static void VerifyIsSafeToGC(JSRuntime *rt);
+#else
+  public:
+    AutoAssertOnGC() {}
+    explicit AutoAssertOnGC(JSRuntime *rt) {}
+    ~AutoAssertOnGC() {}
+
+    static void VerifyIsSafeToGC(JSRuntime *rt) {}
+#endif
 };
 
 /*
  * Disable the static rooting hazard analysis in the live region, but assert if
  * any GC occurs while this guard object is live. This is most useful to help
  * the exact rooting hazard analysis in complex regions, since it cannot
  * understand dataflow.
  *
--- a/js/src/frontend/TokenStream.cpp
+++ b/js/src/frontend/TokenStream.cpp
@@ -14,20 +14,20 @@
 #include <stdarg.h>
 #include <stdio.h>
 #include <string.h>
 
 #include "jsatom.h"
 #include "jscntxt.h"
 #include "jsexn.h"
 #include "jsnum.h"
-#include "jsworkers.h"
 
 #include "frontend/BytecodeCompiler.h"
 #include "js/CharacterEncoding.h"
+#include "vm/HelperThreads.h"
 #include "vm/Keywords.h"
 #include "vm/StringBuffer.h"
 
 using namespace js;
 using namespace js::frontend;
 using namespace js::unicode;
 
 using mozilla::Maybe;
--- a/js/src/gc/GCInternals.h
+++ b/js/src/gc/GCInternals.h
@@ -3,19 +3,19 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef gc_GCInternals_h
 #define gc_GCInternals_h
 
 #include "jscntxt.h"
-#include "jsworkers.h"
 
 #include "gc/Zone.h"
+#include "vm/HelperThreads.h"
 #include "vm/Runtime.h"
 
 namespace js {
 namespace gc {
 
 void
 MarkPersistentRootedChains(JSTracer *trc);
 
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -532,23 +532,25 @@ class GCRuntime
     size_t                systemPageSize;
 
     /* The OS allocation granularity may not match the page size. */
     size_t                systemAllocGranularity;
 
     /* Strong references on scripts held for PCCount profiling API. */
     js::ScriptAndCountsVector *scriptAndCountsVector;
 
+#ifdef DEBUG
     /*
      * Some regions of code are hard for the static rooting hazard analysis to
      * understand. In those cases, we trade the static analysis for a dynamic
      * analysis. When this is non-zero, we should assert if we trigger, or
      * might trigger, a GC.
      */
     int inUnsafeRegion;
+#endif
 
   private:
     /* Always preserve JIT code during GCs, for testing. */
     bool                  alwaysPreserveCode;
 
 #ifdef DEBUG
     size_t                noGCOrAllocationCheck;
 #endif
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/bug1018620.js
@@ -0,0 +1,5 @@
+
+String.prototype.search = evalcx('').String.prototype.search;
+''.search(/()/);
+gcPreserveCode();
+gc(this);
rename from js/src/jit-test/tests/basic/latin1.js
rename to js/src/jit-test/tests/latin1/basic.js
rename from js/src/jit-test/tests/basic/latin1-indexOf.js
rename to js/src/jit-test/tests/latin1/indexOf.js
rename from js/src/jit-test/tests/basic/latin1-indexing.js
rename to js/src/jit-test/tests/latin1/indexing.js
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/latin1/startsWith-endsWith.js
@@ -0,0 +1,65 @@
+function testStartsWith() {
+    var s1 = toLatin1("abc\u0099def");
+    var s2 = toLatin1("abc\u0099d");
+    var s3 = toLatin1("abc\u0098d");
+    var s4 = toLatin1("bc\u0099");
+
+    // Latin1 + Latin1
+    assertEq(s1.startsWith(s2), true);
+    assertEq(s1.startsWith(s3), false);
+    assertEq(s1.startsWith(s4), false);
+    assertEq(s1.startsWith(s4, 1), true);
+    assertEq(s1.startsWith(s1), true);
+
+    // Latin1 + TwoByte
+    assertEq(s1.startsWith("abc\u0099\u1200".slice(0, -1)), true);
+    assertEq(s1.startsWith("abc\u0099e\u1200".slice(0, -1)), false);
+    assertEq(s1.startsWith("bc\u0099\u1200".slice(0, -1), 1), true);
+    assertEq(s1.startsWith("\u1200"), false);
+
+    // TwoByte + Latin1
+    var s5 = "abc\u0099de\u1200";
+    assertEq(s5.startsWith(s1), false);
+    assertEq(s5.startsWith(s2), true);
+    assertEq(s5.startsWith(s4), false);
+    assertEq(s5.startsWith(s4, 1), true);
+
+    // TwoByte + TwoByte
+    assertEq(s5.startsWith(s5), true);
+    assertEq(s5.startsWith("\u1200"), false);
+    assertEq(s5.startsWith("\u1200", 6), true);
+}
+testStartsWith();
+
+function testEndsWith() {
+    var s1 = toLatin1("zabc\u0099defg");
+    var s2 = toLatin1("\u0099defg");
+    var s3 = toLatin1("\u0098defg");
+    var s4 = toLatin1("zabc\u0099def");
+
+    // Latin1 + Latin1
+    assertEq(s1.endsWith(s2), true);
+    assertEq(s1.endsWith(s3), false);
+    assertEq(s1.endsWith(s4), false);
+    assertEq(s1.endsWith(s4, 8), true);
+    assertEq(s1.endsWith(s1), true);
+
+    // Latin1 + TwoByte
+    assertEq(s1.endsWith("abc\u0099defg\u1200".slice(0, -1)), true);
+    assertEq(s1.endsWith("\u1100efg\u1200".slice(0, -1)), false);
+    assertEq(s1.endsWith("bc\u0099\u1200".slice(0, -1), 5), true);
+    assertEq(s1.endsWith("\u1200"), false);
+
+    // TwoByte + Latin1
+    var s5 = "\u1200zabc\u0099defg";
+    assertEq(s5.endsWith(s1), true);
+    assertEq(s5.endsWith(s2), true);
+    assertEq(s5.endsWith(s4), false);
+    assertEq(s5.endsWith(s4, 9), true);
+
+    // TwoByte + TwoByte
+    assertEq(s5.endsWith(s5), true);
+    assertEq(s5.endsWith("\u1200"), false);
+    assertEq(s5.endsWith("\u1200za", 3), true);
+}
+testEndsWith();
--- a/js/src/jit/AsmJS.cpp
+++ b/js/src/jit/AsmJS.cpp
@@ -9,31 +9,31 @@
 #include "mozilla/Move.h"
 
 #ifdef MOZ_VTUNE
 # include "vtune/VTuneWrapper.h"
 #endif
 
 #include "jsmath.h"
 #include "jsprf.h"
-#include "jsworkers.h"
 #include "prmjtime.h"
 
 #include "assembler/assembler/MacroAssembler.h"
 #include "frontend/Parser.h"
 #include "jit/AsmJSLink.h"
 #include "jit/AsmJSModule.h"
 #include "jit/AsmJSSignalHandlers.h"
 #include "jit/CodeGenerator.h"
 #include "jit/CompileWrappers.h"
 #include "jit/MIR.h"
 #include "jit/MIRGraph.h"
 #ifdef JS_ION_PERF
 # include "jit/PerfSpewer.h"
 #endif
+#include "vm/HelperThreads.h"
 #include "vm/Interpreter.h"
 
 #include "jsinferinlines.h"
 #include "jsobjinlines.h"
 
 #include "frontend/ParseNode-inl.h"
 #include "frontend/Parser-inl.h"
 
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -8652,20 +8652,20 @@ CodeGenerator::visitAsmJSCall(LAsmJSCall
                 FloatRegister fr = ToFloatRegister(a);
                 int srcId = fr.code() * 2;
                 masm.ma_vxfer(fr, Register::FromCode(srcId), Register::FromCode(srcId+1));
             }
         }
     }
 #endif
 
-   if (mir->spIncrement())
+    if (mir->spIncrement())
         masm.freeStack(mir->spIncrement());
 
-   JS_ASSERT((AlignmentAtPrologue +  masm.framePushed()) % StackAlignment == 0);
+    JS_ASSERT((AlignmentAtPrologue + masm.framePushed()) % StackAlignment == 0);
 
 #ifdef DEBUG
     Label ok;
     JS_ASSERT(IsPowerOfTwo(StackAlignment));
     masm.branchTestPtr(Assembler::Zero, StackPointer, Imm32(StackAlignment - 1), &ok);
     masm.assumeUnreachable("Stack should be aligned.");
     masm.bind(&ok);
 #endif
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -6,17 +6,16 @@
 
 #include "jit/Ion.h"
 
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/ThreadLocal.h"
 
 #include "jscompartment.h"
 #include "jsprf.h"
-#include "jsworkers.h"
 
 #include "gc/Marking.h"
 #include "jit/AliasAnalysis.h"
 #include "jit/AsmJSModule.h"
 #include "jit/BacktrackingAllocator.h"
 #include "jit/BaselineDebugModeOSR.h"
 #include "jit/BaselineFrame.h"
 #include "jit/BaselineInspector.h"
@@ -36,16 +35,17 @@
 #include "jit/Lowering.h"
 #include "jit/ParallelSafetyAnalysis.h"
 #include "jit/PerfSpewer.h"
 #include "jit/RangeAnalysis.h"
 #include "jit/StupidAllocator.h"
 #include "jit/UnreachableCodeElimination.h"
 #include "jit/ValueNumbering.h"
 #include "vm/ForkJoin.h"
+#include "vm/HelperThreads.h"
 #include "vm/TraceLogging.h"
 
 #include "jscompartmentinlines.h"
 #include "jsgcinlines.h"
 #include "jsinferinlines.h"
 #include "jsobjinlines.h"
 
 #include "jit/ExecutionMode-inl.h"
--- a/js/src/jit/IonSpewer.cpp
+++ b/js/src/jit/IonSpewer.cpp
@@ -3,19 +3,18 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifdef DEBUG
 
 #include "jit/IonSpewer.h"
 
-#include "jsworkers.h"
-
 #include "jit/Ion.h"
+#include "vm/HelperThreads.h"
 
 #ifndef ION_SPEW_DIR
 # if defined(_WIN32)
 #  define ION_SPEW_DIR ""
 # elif defined(__ANDROID__)
 #  define ION_SPEW_DIR "/data/local/tmp/"
 # else
 #  define ION_SPEW_DIR "/tmp/"
--- a/js/src/jit/LIR.h
+++ b/js/src/jit/LIR.h
@@ -1549,17 +1549,17 @@ class LIRGraph
     }
     // Return the localSlotCount() value rounded up so that it satisfies the
     // platform stack alignment requirement, and so that it's a multiple of
     // the number of slots per Value.
     uint32_t paddedLocalSlotCount() const {
         // Round to StackAlignment, but also round to at least sizeof(Value) in
         // case that's greater, because StackOffsetOfPassedArg rounds argument
         // slots to 8-byte boundaries.
-        size_t Alignment = Max(sizeof(StackAlignment), sizeof(Value));
+        size_t Alignment = Max(size_t(StackAlignment), sizeof(Value));
         return AlignBytes(localSlotCount(), Alignment);
     }
     size_t paddedLocalSlotsSize() const {
         return paddedLocalSlotCount();
     }
     void setArgumentSlotCount(uint32_t argumentSlotCount) {
         argumentSlotCount_ = argumentSlotCount;
     }
--- a/js/src/jit/MIRGenerator.h
+++ b/js/src/jit/MIRGenerator.h
@@ -113,24 +113,27 @@ class MIRGenerator
         maxAsmJSStackArgBytes_ = n;
     }
     void setPerformsCall() {
         performsCall_ = true;
     }
     bool performsCall() const {
         return performsCall_;
     }
+    void setNeedsInitialStackAlignment() {
+        needsInitialStackAlignment_ = true;
+    }
+    bool needsInitialStackAlignment() const {
+        JS_ASSERT(compilingAsmJS());
+        return needsInitialStackAlignment_;
+    }
     void setPerformsAsmJSCall() {
         JS_ASSERT(compilingAsmJS());
         setPerformsCall();
-        performsAsmJSCall_ = true;
-    }
-    bool performsAsmJSCall() const {
-        JS_ASSERT(compilingAsmJS());
-        return performsAsmJSCall_;
+        setNeedsInitialStackAlignment();
     }
     void noteMinAsmJSHeapLength(uint32_t len) {
         minAsmJSHeapLength_ = len;
     }
     uint32_t minAsmJSHeapLength() const {
         return minAsmJSHeapLength_;
     }
 
@@ -149,17 +152,17 @@ class MIRGenerator
     uint32_t nslots_;
     MIRGraph *graph_;
     AbortReason abortReason_;
     bool error_;
     mozilla::Atomic<bool, mozilla::Relaxed> cancelBuild_;
 
     uint32_t maxAsmJSStackArgBytes_;
     bool performsCall_;
-    bool performsAsmJSCall_;
+    bool needsInitialStackAlignment_;
     uint32_t minAsmJSHeapLength_;
 
     // Keep track of whether frame arguments are modified during execution.
     // RegAlloc needs to know this as spilling values back to their register
     // slots is not compatible with that.
     bool modifiesFrameArguments_;
 
 #if defined(JS_ION_PERF)
--- a/js/src/jit/MIRGraph.cpp
+++ b/js/src/jit/MIRGraph.cpp
@@ -24,17 +24,17 @@ MIRGenerator::MIRGenerator(CompileCompar
     optimizationInfo_(optimizationInfo),
     alloc_(alloc),
     graph_(graph),
     abortReason_(AbortReason_NoAbort),
     error_(false),
     cancelBuild_(false),
     maxAsmJSStackArgBytes_(0),
     performsCall_(false),
-    performsAsmJSCall_(false),
+    needsInitialStackAlignment_(false),
     minAsmJSHeapLength_(AsmJSAllocationGranularity),
     modifiesFrameArguments_(false),
     options(options)
 { }
 
 bool
 MIRGenerator::abortFmt(const char *message, va_list ap)
 {
--- a/js/src/jit/shared/Assembler-shared.h
+++ b/js/src/jit/shared/Assembler-shared.h
@@ -6,22 +6,21 @@
 
 #ifndef jit_shared_Assembler_shared_h
 #define jit_shared_Assembler_shared_h
 
 #include "mozilla/PodOperations.h"
 
 #include <limits.h>
 
-#include "jsworkers.h"
-
 #include "jit/IonAllocPolicy.h"
 #include "jit/Label.h"
 #include "jit/Registers.h"
 #include "jit/RegisterSets.h"
+#include "vm/HelperThreads.h"
 
 #if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_ARM)
 // JS_SMALL_BRANCH means the range on a branch instruction
 // is smaller than the whole address space
 #    define JS_SMALL_BRANCH
 #endif
 namespace js {
 namespace jit {
--- a/js/src/jit/shared/CodeGenerator-shared.cpp
+++ b/js/src/jit/shared/CodeGenerator-shared.cpp
@@ -47,17 +47,18 @@ CodeGeneratorShared::CodeGeneratorShared
     deoptTable_(nullptr),
 #ifdef DEBUG
     pushedArgs_(0),
 #endif
     lastOsiPointOffset_(0),
     sps_(&GetIonContext()->runtime->spsProfiler(), &lastPC_),
     osrEntryOffset_(0),
     skipArgCheckEntryOffset_(0),
-    frameDepth_(graph->paddedLocalSlotsSize() + graph->argumentsSize())
+    frameDepth_(graph->paddedLocalSlotsSize() + graph->argumentsSize()),
+    frameInitialAdjustment_(0)
 {
     if (!gen->compilingAsmJS())
         masm.setInstrumentation(&sps_);
 
     // Since asm.js uses the system ABI which does not necessarily use a
     // regular array where all slots are sizeof(Value), it maintains the max
     // argument stack depth separately.
     if (gen->compilingAsmJS()) {
@@ -67,20 +68,22 @@ CodeGeneratorShared::CodeGeneratorShared
         // An MAsmJSCall does not align the stack pointer at calls sites but instead
         // relies on the a priori stack adjustment (in the prologue) on platforms
         // (like x64) which require the stack to be aligned.
 #if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS)
         bool forceAlign = true;
 #else
         bool forceAlign = false;
 #endif
-        if (gen->performsAsmJSCall() || forceAlign) {
+        if (gen->needsInitialStackAlignment() || forceAlign) {
             unsigned alignmentAtCall = AlignmentMidPrologue + frameDepth_;
-            if (unsigned rem = alignmentAtCall % StackAlignment)
-                frameDepth_ += StackAlignment - rem;
+            if (unsigned rem = alignmentAtCall % StackAlignment) {
+                frameInitialAdjustment_ = StackAlignment - rem;
+                frameDepth_ += frameInitialAdjustment_;
+            }
         }
 
         // FrameSizeClass is only used for bailing, which cannot happen in
         // asm.js code.
         frameClass_ = FrameSizeClass::None();
     } else {
         frameClass_ = FrameSizeClass::FromDepth(frameDepth_);
     }
--- a/js/src/jit/shared/CodeGenerator-shared.h
+++ b/js/src/jit/shared/CodeGenerator-shared.h
@@ -139,16 +139,21 @@ class CodeGeneratorShared : public LInst
     void dropArguments(unsigned argc);
 
   protected:
     // The initial size of the frame in bytes. These are bytes beyond the
     // constant header present for every Ion frame, used for pre-determined
     // spills.
     int32_t frameDepth_;
 
+    // In some cases, we force stack alignment to platform boundaries, see
+    // also CodeGeneratorShared constructor. This value records the adjustment
+    // we've done.
+    int32_t frameInitialAdjustment_;
+
     // Frame class this frame's size falls into (see IonFrame.h).
     FrameSizeClass frameClass_;
 
     // For arguments to the current function.
     inline int32_t ArgToStackOffset(int32_t slot) const {
         return masm.framePushed() +
                (gen->compilingAsmJS() ? NativeFrameSize : sizeof(IonJSFrameLayout)) +
                slot;
@@ -156,28 +161,28 @@ class CodeGeneratorShared : public LInst
 
     // For the callee of the current function.
     inline int32_t CalleeStackOffset() const {
         return masm.framePushed() + IonJSFrameLayout::offsetOfCalleeToken();
     }
 
     inline int32_t SlotToStackOffset(int32_t slot) const {
         JS_ASSERT(slot > 0 && slot <= int32_t(graph.localSlotCount()));
-        int32_t offset = masm.framePushed() - slot;
+        int32_t offset = masm.framePushed() - frameInitialAdjustment_ - slot;
         JS_ASSERT(offset >= 0);
         return offset;
     }
     inline int32_t StackOffsetToSlot(int32_t offset) const {
         // See: SlotToStackOffset. This is used to convert pushed arguments
         // to a slot index that safepoints can use.
         //
-        // offset = framePushed - slot
-        // offset + slot = framePushed
-        // slot = framePushed - offset
-        return masm.framePushed() - offset;
+        // offset = framePushed - frameInitialAdjustment - slot
+        // offset + slot = framePushed - frameInitialAdjustment
+        // slot = framePushed - frameInitialAdjustement - offset
+        return masm.framePushed() - frameInitialAdjustment_ - offset;
     }
 
     // For argument construction for calls. Argslots are Value-sized.
     inline int32_t StackOffsetOfPassedArg(int32_t slot) const {
         // A slot of 0 is permitted only to calculate %esp offset for calls.
         JS_ASSERT(slot >= 0 && slot <= int32_t(graph.argumentSlotCount()));
         int32_t offset = masm.framePushed() -
                        graph.paddedLocalSlotsSize() -
--- a/js/src/jit/shared/MoveEmitter-x86-shared.cpp
+++ b/js/src/jit/shared/MoveEmitter-x86-shared.cpp
@@ -236,30 +236,27 @@ MoveEmitterX86::breakCycle(const MoveOpe
       case MoveOp::DOUBLE:
         if (to.isMemory()) {
             masm.loadDouble(toAddress(to), ScratchFloatReg);
             masm.storeDouble(ScratchFloatReg, cycleSlot());
         } else {
             masm.storeDouble(to.floatReg(), cycleSlot());
         }
         break;
+      case MoveOp::INT32:
 #ifdef JS_CODEGEN_X64
-      case MoveOp::INT32:
         // x64 can't pop to a 32-bit destination, so don't push.
         if (to.isMemory()) {
             masm.load32(toAddress(to), ScratchReg);
             masm.store32(ScratchReg, cycleSlot());
         } else {
             masm.store32(to.reg(), cycleSlot());
         }
         break;
 #endif
-#ifndef JS_CODEGEN_X64
-      case MoveOp::INT32:
-#endif
       case MoveOp::GENERAL:
         masm.Push(toOperand(to));
         break;
       default:
         MOZ_ASSUME_UNREACHABLE("Unexpected move type");
     }
 }
 
@@ -288,32 +285,29 @@ MoveEmitterX86::completeCycle(const Move
         JS_ASSERT(pushedAtCycle_ - pushedAtStart_ >= sizeof(double));
         if (to.isMemory()) {
             masm.loadDouble(cycleSlot(), ScratchFloatReg);
             masm.storeDouble(ScratchFloatReg, toAddress(to));
         } else {
             masm.loadDouble(cycleSlot(), to.floatReg());
         }
         break;
+      case MoveOp::INT32:
 #ifdef JS_CODEGEN_X64
-      case MoveOp::INT32:
         JS_ASSERT(pushedAtCycle_ != -1);
         JS_ASSERT(pushedAtCycle_ - pushedAtStart_ >= sizeof(int32_t));
         // x64 can't pop to a 32-bit destination.
         if (to.isMemory()) {
             masm.load32(cycleSlot(), ScratchReg);
             masm.store32(ScratchReg, toAddress(to));
         } else {
             masm.load32(cycleSlot(), to.reg());
         }
         break;
 #endif
-#ifndef JS_CODEGEN_X64
-      case MoveOp::INT32:
-#endif
       case MoveOp::GENERAL:
         JS_ASSERT(masm.framePushed() - pushedAtStart_ >= sizeof(intptr_t));
         masm.Pop(toPopOperand(to));
         break;
       default:
         MOZ_ASSUME_UNREACHABLE("Unexpected move type");
     }
 }
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -35,19 +35,16 @@
 #include "jsprf.h"
 #include "jsproxy.h"
 #include "jsscript.h"
 #include "jsstr.h"
 #include "jstypes.h"
 #include "jsutil.h"
 #include "jswatchpoint.h"
 #include "jsweakmap.h"
-#ifdef JS_THREADSAFE
-#include "jsworkers.h"
-#endif
 #include "jswrapper.h"
 #include "prmjtime.h"
 
 #include "builtin/Eval.h"
 #include "builtin/Intl.h"
 #include "builtin/MapObject.h"
 #include "builtin/RegExp.h"
 #ifdef ENABLE_BINARYDATA
@@ -65,16 +62,17 @@
 #include "js/StructuredClone.h"
 #if ENABLE_INTL_API
 #include "unicode/uclean.h"
 #include "unicode/utypes.h"
 #endif // ENABLE_INTL_API
 #include "vm/DateObject.h"
 #include "vm/Debugger.h"
 #include "vm/ErrorObject.h"
+#include "vm/HelperThreads.h"
 #include "vm/Interpreter.h"
 #include "vm/NumericConversions.h"
 #include "vm/RegExpStatics.h"
 #include "vm/Runtime.h"
 #include "vm/Shape.h"
 #include "vm/SharedArrayObject.h"
 #include "vm/StopIterationObject.h"
 #include "vm/StringBuffer.h"
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -32,24 +32,24 @@
 #include "jsobj.h"
 #include "jsopcode.h"
 #include "jsprf.h"
 #include "jspubtd.h"
 #include "jsscript.h"
 #include "jsstr.h"
 #include "jstypes.h"
 #include "jswatchpoint.h"
-#include "jsworkers.h"
 
 #include "gc/Marking.h"
 #ifdef JS_ION
 #include "jit/Ion.h"
 #endif
 #include "js/CharacterEncoding.h"
 #include "js/OldDebugAPI.h"
+#include "vm/HelperThreads.h"
 #include "vm/Shape.h"
 
 #include "jsobjinlines.h"
 #include "jsscriptinlines.h"
 
 #include "vm/Stack-inl.h"
 
 using namespace js;
--- a/js/src/jscntxtinlines.h
+++ b/js/src/jscntxtinlines.h
@@ -6,21 +6,21 @@
 
 #ifndef jscntxtinlines_h
 #define jscntxtinlines_h
 
 #include "jscntxt.h"
 #include "jscompartment.h"
 
 #include "jsiter.h"
-#include "jsworkers.h"
 
 #include "builtin/Object.h"
 #include "jit/IonFrames.h"
 #include "vm/ForkJoin.h"
+#include "vm/HelperThreads.h"
 #include "vm/Interpreter.h"
 #include "vm/ProxyObject.h"
 
 namespace js {
 
 #ifdef JS_CRASH_DIAGNOSTICS
 class CompartmentChecker
 {
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -1110,17 +1110,19 @@ GCRuntime::GCRuntime(JSRuntime *rt) :
 #endif
     validate(true),
     fullCompartmentChecks(false),
     gcCallback(nullptr),
     sliceCallback(nullptr),
     mallocBytes(0),
     mallocGCTriggered(false),
     scriptAndCountsVector(nullptr),
+#ifdef DEBUG
     inUnsafeRegion(0),
+#endif
     alwaysPreserveCode(false),
 #ifdef DEBUG
     noGCOrAllocationCheck(0),
 #endif
     lock(nullptr),
     lockOwner(nullptr),
     helperState(rt)
 {
@@ -2432,39 +2434,33 @@ void
 GCHelperState::work()
 {
 #ifdef JS_THREADSAFE
     AutoLockGC lock(rt);
 
     JS_ASSERT(!thread);
     thread = PR_GetCurrentThread();
 
+    TraceLogger *logger = TraceLoggerForCurrentThread();
+
     switch (state()) {
 
       case IDLE:
         MOZ_ASSUME_UNREACHABLE("GC helper triggered on idle state");
         break;
 
       case SWEEPING: {
-#if JS_TRACE_LOGGING
-        AutoTraceLog logger(TraceLogging::getLogger(TraceLogging::GC_BACKGROUND),
-                            TraceLogging::GC_SWEEPING_START,
-                            TraceLogging::GC_SWEEPING_STOP);
-#endif
+        AutoTraceLog logSweeping(logger, TraceLogger::GCSweeping);
         doSweep();
         JS_ASSERT(state() == SWEEPING);
         break;
       }
 
       case ALLOCATING: {
-#if JS_TRACE_LOGGING
-        AutoTraceLog logger(TraceLogging::getLogger(TraceLogging::GC_BACKGROUND),
-                            TraceLogging::GC_ALLOCATING_START,
-                            TraceLogging::GC_ALLOCATING_STOP);
-#endif
+        AutoTraceLog logAllocation(logger, TraceLogger::GCAllocation);
         do {
             Chunk *chunk;
             {
                 AutoUnlockGC unlock(rt);
                 chunk = Chunk::allocate(rt);
             }
 
             /* OOM stops the background allocation. */
@@ -5615,16 +5611,17 @@ JS::GetGCNumber()
 {
     JSRuntime *rt = js::TlsPerThreadData.get()->runtimeFromMainThread();
     if (!rt)
         return 0;
     return rt->gc.number;
 }
 #endif
 
+#ifdef DEBUG
 JS::AutoAssertOnGC::AutoAssertOnGC()
   : runtime(nullptr), gcNumber(0)
 {
     js::PerThreadData *data = js::TlsPerThreadData.get();
     if (data) {
         /*
          * GC's from off-thread will always assert, so off-thread is implicitly
          * AutoAssertOnGC. We still need to allow AutoAssertOnGC to be used in
@@ -5660,8 +5657,9 @@ JS::AutoAssertOnGC::~AutoAssertOnGC()
 }
 
 /* static */ void
 JS::AutoAssertOnGC::VerifyIsSafeToGC(JSRuntime *rt)
 {
     if (rt->gc.inUnsafeRegion > 0)
         MOZ_CRASH("[AutoAssertOnGC] possible GC in GC-unsafe region");
 }
+#endif
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -13,27 +13,27 @@
 #include "jsapi.h"
 #include "jscntxt.h"
 #include "jsgc.h"
 #include "jshashutil.h"
 #include "jsobj.h"
 #include "jsprf.h"
 #include "jsscript.h"
 #include "jsstr.h"
-#include "jsworkers.h"
 #include "prmjtime.h"
 
 #include "gc/Marking.h"
 #ifdef JS_ION
 #include "jit/BaselineJIT.h"
 #include "jit/Ion.h"
 #include "jit/IonAnalysis.h"
 #include "jit/JitCompartment.h"
 #endif
 #include "js/MemoryMetrics.h"
+#include "vm/HelperThreads.h"
 #include "vm/Opcodes.h"
 #include "vm/Shape.h"
 
 #include "jsatominlines.h"
 #include "jsgcinlines.h"
 #include "jsobjinlines.h"
 #include "jsscriptinlines.h"
 
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -1556,16 +1556,49 @@ str_lastIndexOf(JSContext *cx, unsigned 
         else
             res = LastIndexOfImpl(textChars, textLen, pat->twoByteChars(nogc), patLen, start);
     }
 
     args.rval().setInt32(res);
     return true;
 }
 
+static bool
+EqualCharsLatin1TwoByte(const Latin1Char *s1, const jschar *s2, size_t len)
+{
+    for (const Latin1Char *s1end = s1 + len; s1 < s1end; s1++, s2++) {
+        if (jschar(*s1) != *s2)
+            return false;
+    }
+    return true;
+}
+
+static bool
+HasSubstringAt(JSLinearString *text, JSLinearString *pat, size_t start)
+{
+    MOZ_ASSERT(start + pat->length() <= text->length());
+
+    size_t patLen = pat->length();
+
+    AutoCheckCannotGC nogc;
+    if (text->hasLatin1Chars()) {
+        const Latin1Char *textChars = text->latin1Chars(nogc) + start;
+        if (pat->hasLatin1Chars())
+            return PodEqual(textChars, pat->latin1Chars(nogc), patLen);
+
+        return EqualCharsLatin1TwoByte(textChars, pat->twoByteChars(nogc), patLen);
+    }
+
+    const jschar *textChars = text->twoByteChars(nogc) + start;
+    if (pat->hasTwoByteChars())
+        return PodEqual(textChars, pat->twoByteChars(nogc), patLen);
+
+    return EqualCharsLatin1TwoByte(pat->latin1Chars(nogc), textChars, patLen);
+}
+
 /* ES6 20131108 draft 21.1.3.18. */
 static bool
 str_startsWith(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     // Steps 1, 2, and 3
     RootedString str(cx, ThisToStringForStringProto(cx, args));
@@ -1595,35 +1628,35 @@ str_startsWith(JSContext *cx, unsigned a
             if (!ToInteger(cx, args[1], &d))
                 return false;
             pos = uint32_t(Min(Max(d, 0.0), double(UINT32_MAX)));
         }
     }
 
     // Step 9
     uint32_t textLen = str->length();
-    const jschar *textChars = str->getChars(cx);
-    if (!textChars)
-        return false;
 
     // Step 10
     uint32_t start = Min(Max(pos, 0U), textLen);
 
     // Step 11
     uint32_t searchLen = searchStr->length();
-    const jschar *searchChars = searchStr->chars();
 
     // Step 12
     if (searchLen + start < searchLen || searchLen + start > textLen) {
         args.rval().setBoolean(false);
         return true;
     }
 
     // Steps 13 and 14
-    args.rval().setBoolean(PodEqual(textChars + start, searchChars, searchLen));
+    JSLinearString *text = str->ensureLinear(cx);
+    if (!text)
+        return false;
+
+    args.rval().setBoolean(HasSubstringAt(text, searchStr, start));
     return true;
 }
 
 /* ES6 20131108 draft 21.1.3.7. */
 static bool
 str_endsWith(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
@@ -1642,19 +1675,16 @@ str_endsWith(JSContext *cx, unsigned arg
 
     // Steps 5 and 6
     Rooted<JSLinearString *> searchStr(cx, ArgToRootedString(cx, args, 0));
     if (!searchStr)
         return false;
 
     // Step 7
     uint32_t textLen = str->length();
-    const jschar *textChars = str->getChars(cx);
-    if (!textChars)
-        return false;
 
     // Steps 8 and 9
     uint32_t pos = textLen;
     if (args.hasDefined(1)) {
         if (args[1].isInt32()) {
             int i = args[1].toInt32();
             pos = (i < 0) ? 0U : uint32_t(i);
         } else {
@@ -1665,29 +1695,32 @@ str_endsWith(JSContext *cx, unsigned arg
         }
     }
 
     // Step 10
     uint32_t end = Min(Max(pos, 0U), textLen);
 
     // Step 11
     uint32_t searchLen = searchStr->length();
-    const jschar *searchChars = searchStr->chars();
 
     // Step 13 (reordered)
     if (searchLen > end) {
         args.rval().setBoolean(false);
         return true;
     }
 
     // Step 12
     uint32_t start = end - searchLen;
 
     // Steps 14 and 15
-    args.rval().setBoolean(PodEqual(textChars + start, searchChars, searchLen));
+    JSLinearString *text = str->ensureLinear(cx);
+    if (!text)
+        return false;
+
+    args.rval().setBoolean(HasSubstringAt(text, searchStr, start));
     return true;
 }
 
 static bool
 js_TrimString(JSContext *cx, Value *vp, bool trimLeft, bool trimRight)
 {
     CallReceiver call = CallReceiverFromVp(vp);
     RootedString str(cx, ThisToStringForStringProto(cx, call));
@@ -4241,26 +4274,16 @@ js::ValueToSource(JSContext *cx, HandleV
 
 JSString *
 js::StringToSource(JSContext *cx, JSString *str)
 {
     return js_QuoteString(cx, str, '"');
 }
 
 static bool
-EqualCharsLatin1TwoByte(const Latin1Char *s1, const jschar *s2, size_t len)
-{
-    for (const Latin1Char *s1end = s1 + len; s1 < s1end; s1++, s2++) {
-        if (jschar(*s1) != *s2)
-            return false;
-    }
-    return true;
-}
-
-static bool
 EqualChars(JSLinearString *str1, JSLinearString *str2)
 {
     MOZ_ASSERT(str1->length() == str2->length());
 
     size_t len = str1->length();
 
     AutoCheckCannotGC nogc;
     if (str1->hasTwoByteChars()) {
--- a/js/src/jswrapper.cpp
+++ b/js/src/jswrapper.cpp
@@ -562,18 +562,26 @@ CrossCompartmentWrapper::fun_toString(JS
     if (!cx->compartment()->wrap(cx, str.address()))
         return nullptr;
     return str;
 }
 
 bool
 CrossCompartmentWrapper::regexp_toShared(JSContext *cx, HandleObject wrapper, RegExpGuard *g)
 {
-    AutoCompartment call(cx, wrappedObject(wrapper));
-    return Wrapper::regexp_toShared(cx, wrapper, g);
+    RegExpGuard wrapperGuard(cx);
+    {
+        AutoCompartment call(cx, wrappedObject(wrapper));
+        if (!Wrapper::regexp_toShared(cx, wrapper, &wrapperGuard))
+            return false;
+    }
+
+    // Get an equivalent RegExpShared associated with the current compartment.
+    RegExpShared *re = wrapperGuard.re();
+    return cx->compartment()->regExps.get(cx, re->getSource(), re->getFlags(), g);
 }
 
 bool
 CrossCompartmentWrapper::defaultValue(JSContext *cx, HandleObject wrapper, JSType hint,
                                       MutableHandleValue vp)
 {
     PIERCE(cx, wrapper,
            NOTHING,
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -145,31 +145,31 @@ UNIFIED_SOURCES += [
     'jsprf.cpp',
     'jspropertytree.cpp',
     'jsproxy.cpp',
     'jsreflect.cpp',
     'jsscript.cpp',
     'jsstr.cpp',
     'jswatchpoint.cpp',
     'jsweakmap.cpp',
-    'jsworkers.cpp',
     'jswrapper.cpp',
     'perf/jsperf.cpp',
     'prmjtime.cpp',
     'vm/ArgumentsObject.cpp',
     'vm/ArrayBufferObject.cpp',
     'vm/CallNonGenericMethod.cpp',
     'vm/CharacterEncoding.cpp',
     'vm/Compression.cpp',
     'vm/DateTime.cpp',
     'vm/Debugger.cpp',
     'vm/DebuggerMemory.cpp',
     'vm/ErrorObject.cpp',
     'vm/ForkJoin.cpp',
     'vm/GlobalObject.cpp',
+    'vm/HelperThreads.cpp',
     'vm/Id.cpp',
     'vm/Interpreter.cpp',
     'vm/MemoryMetrics.cpp',
     'vm/Monitor.cpp',
     'vm/ObjectImpl.cpp',
     'vm/OldDebugAPI.cpp',
     'vm/PIC.cpp',
     'vm/Probes.cpp',
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -46,30 +46,30 @@
 #include "jsobj.h"
 #include "jsprf.h"
 #include "jsscript.h"
 #include "jstypes.h"
 #include "jsutil.h"
 #ifdef XP_WIN
 # include "jswin.h"
 #endif
-#include "jsworkers.h"
 #include "jswrapper.h"
 #include "prmjtime.h"
 
 #include "builtin/TestingFunctions.h"
 #include "frontend/Parser.h"
 #include "jit/arm/Simulator-arm.h"
 #include "jit/Ion.h"
 #include "js/OldDebugAPI.h"
 #include "js/StructuredClone.h"
 #include "perf/jsperf.h"
 #include "shell/jsheaptools.h"
 #include "shell/jsoptparse.h"
 #include "vm/ArgumentsObject.h"
+#include "vm/HelperThreads.h"
 #include "vm/Monitor.h"
 #include "vm/Shape.h"
 #include "vm/TypedArrayObject.h"
 #include "vm/WrapperObject.h"
 
 #include "jscompartmentinlines.h"
 #include "jsobjinlines.h"
 
@@ -6015,22 +6015,16 @@ SetRuntimeOptions(JSRuntime *rt, const O
 
     jsCacheDir = op.getStringOption("js-cache");
     if (jsCacheDir) {
         if (op.getBoolOption("js-cache-per-process"))
             jsCacheDir = JS_smprintf("%s/%u", jsCacheDir, (unsigned)getpid());
         jsCacheAsmJSPath = JS_smprintf("%s/asmjs.cache", jsCacheDir);
     }
 
-#ifdef JS_THREADSAFE
-    int32_t threadCount = op.getIntOption("thread-count");
-    if (threadCount >= 0)
-        SetFakeCPUCount(threadCount);
-#endif /* JS_THREADSAFE */
-
 #ifdef DEBUG
     dumpEntrainedVariables = op.getBoolOption("dump-entrained-variables");
 #endif
 
     return true;
 }
 
 static int
@@ -6272,16 +6266,24 @@ main(int argc, char **argv, char **envp)
     if (op.getBoolOption("no-sse4")) {
         JSC::MacroAssembler::SetSSE4Disabled();
         PropagateFlagToNestedShells("--no-sse4");
     }
 #endif
 
 #endif // DEBUG
 
+#ifdef JS_THREADSAFE
+    // The fake thread count must be set before initializing the Runtime,
+    // which spins up the thread pool.
+    int32_t threadCount = op.getIntOption("thread-count");
+    if (threadCount >= 0)
+        SetFakeCPUCount(threadCount);
+#endif /* JS_THREADSAFE */
+
     // Start the engine.
     if (!JS_Init())
         return 1;
 
     /* Use the same parameters as the browser in xpcjsruntime.cpp. */
     rt = JS_NewRuntime(32L * 1024L * 1024L);
     if (!rt)
         return 1;
--- a/js/src/vm/GlobalObject.cpp
+++ b/js/src/vm/GlobalObject.cpp
@@ -9,27 +9,27 @@
 #include "jscntxt.h"
 #include "jsdate.h"
 #include "jsexn.h"
 #include "jsfriendapi.h"
 #include "jsmath.h"
 #include "json.h"
 #include "jsprototypes.h"
 #include "jsweakmap.h"
-#include "jsworkers.h"
 
 #include "builtin/Eval.h"
 #if EXPOSE_INTL_API
 # include "builtin/Intl.h"
 #endif
 #include "builtin/MapObject.h"
 #include "builtin/Object.h"
 #include "builtin/RegExp.h"
 #include "builtin/SIMD.h"
 #include "builtin/TypedObject.h"
+#include "vm/HelperThreads.h"
 #include "vm/PIC.h"
 #include "vm/RegExpStatics.h"
 #include "vm/StopIterationObject.h"
 #include "vm/WeakMapObject.h"
 
 #include "jscompartmentinlines.h"
 #include "jsobjinlines.h"
 #include "jsscriptinlines.h"
rename from js/src/jsworkers.cpp
rename to js/src/vm/HelperThreads.cpp
--- a/js/src/jsworkers.cpp
+++ b/js/src/vm/HelperThreads.cpp
@@ -1,15 +1,15 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "jsworkers.h"
+#include "vm/HelperThreads.h"
 
 #ifdef JS_THREADSAFE
 
 #include "mozilla/DebugOnly.h"
 
 #include "jsnativestack.h"
 #include "prmjtime.h"
 
rename from js/src/jsworkers.h
rename to js/src/vm/HelperThreads.h
--- a/js/src/jsworkers.h
+++ b/js/src/vm/HelperThreads.h
@@ -5,18 +5,18 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /*
  * Definitions for managing off-main-thread work using a process wide list
  * of worklist items and pool of threads. Worklist items are engine internal,
  * and are distinct from e.g. web workers.
  */
 
-#ifndef jsworkers_h
-#define jsworkers_h
+#ifndef vm_HelperThreads_h
+#define vm_HelperThreads_h
 
 #include "mozilla/GuardObjects.h"
 #include "mozilla/PodOperations.h"
 
 #include "jscntxt.h"
 #include "jslock.h"
 
 #include "frontend/TokenStream.h"
@@ -502,9 +502,9 @@ struct SourceCompressionTask
     bool complete();
     void abort() { abort_ = true; }
     bool active() const { return !!ss; }
     ScriptSource *source() { return ss; }
 };
 
 } /* namespace js */
 
-#endif /* jsworkers_h */
+#endif /* vm_HelperThreads_h */
--- a/js/src/vm/RegExpObject.h
+++ b/js/src/vm/RegExpObject.h
@@ -497,24 +497,17 @@ class RegExpObject : public JSObject
  * Parse regexp flags. Report an error and return false if an invalid
  * sequence of flags is encountered (repeat/invalid flag).
  *
  * N.B. flagStr must be rooted.
  */
 bool
 ParseRegExpFlags(JSContext *cx, JSString *flagStr, RegExpFlag *flagsOut);
 
-/*
- * Assuming ObjectClassIs(obj, ESClass_RegExp), return obj's RegExpShared.
- *
- * Beware: this RegExpShared can be owned by a compartment other than
- * cx->compartment. Normal RegExpGuard (which is necessary anyways)
- * will protect the object but it is important not to assign the return value
- * to be the private of any RegExpObject.
- */
+/* Assuming ObjectClassIs(obj, ESClass_RegExp), return a RegExpShared for obj. */
 inline bool
 RegExpToShared(JSContext *cx, HandleObject obj, RegExpGuard *g)
 {
     if (obj->is<RegExpObject>())
         return obj->as<RegExpObject>().getShared(cx, g);
     return Proxy::regexp_toShared(cx, obj, g);
 }
 
--- a/js/src/vm/String.h
+++ b/js/src/vm/String.h
@@ -633,16 +633,17 @@ class JSLinearString : public JSString
     MOZ_ALWAYS_INLINE
     const jschar *twoByteChars(const JS::AutoCheckCannotGC &nogc) const;
 
     JS::TwoByteChars range() const {
         JS_ASSERT(JSString::isLinear());
         return JS::TwoByteChars(chars(), length());
     }
 
+    MOZ_ALWAYS_INLINE
     jschar latin1OrTwoByteChar(size_t index) const {
         MOZ_ASSERT(JSString::isLinear());
         MOZ_ASSERT(index < length());
         JS::AutoCheckCannotGC nogc;
         return hasLatin1Chars() ? latin1Chars(nogc)[index] : twoByteChars(nogc)[index];
     }
 
     /* Temporary, unsafe helper function for bug 998392. Don't use for anything else. */
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -498,29 +498,29 @@ Scriptability::SetDocShellAllowsScript(b
 /* static */
 Scriptability&
 Scriptability::Get(JSObject *aScope)
 {
     return EnsureCompartmentPrivate(aScope)->scriptability;
 }
 
 bool
-IsXBLScope(JSCompartment *compartment)
+IsContentXBLScope(JSCompartment *compartment)
 {
     // We always eagerly create compartment privates for XBL scopes.
     CompartmentPrivate *priv = GetCompartmentPrivate(compartment);
     if (!priv || !priv->scope)
         return false;
-    return priv->scope->IsXBLScope();
+    return priv->scope->IsContentXBLScope();
 }
 
 bool
-IsInXBLScope(JSObject *obj)
+IsInContentXBLScope(JSObject *obj)
 {
-    return IsXBLScope(js::GetObjectCompartment(obj));
+    return IsContentXBLScope(js::GetObjectCompartment(obj));
 }
 
 bool
 IsUniversalXPConnectEnabled(JSCompartment *compartment)
 {
     CompartmentPrivate *priv = GetCompartmentPrivate(compartment);
     if (!priv)
         return false;
--- a/js/xpconnect/src/XPCWrappedNativeScope.cpp
+++ b/js/xpconnect/src/XPCWrappedNativeScope.cpp
@@ -68,17 +68,17 @@ RemoteXULForbidsXBLScope(nsIPrincipal *a
 
 XPCWrappedNativeScope::XPCWrappedNativeScope(JSContext *cx,
                                              JS::HandleObject aGlobal)
       : mWrappedNativeMap(Native2WrappedNativeMap::newMap(XPC_NATIVE_MAP_SIZE)),
         mWrappedNativeProtoMap(ClassInfo2WrappedNativeProtoMap::newMap(XPC_NATIVE_PROTO_MAP_SIZE)),
         mComponents(nullptr),
         mNext(nullptr),
         mGlobalJSObject(aGlobal),
-        mIsXBLScope(false)
+        mIsContentXBLScope(false)
 {
     // add ourselves to the scopes list
     {
         MOZ_ASSERT(aGlobal);
         DebugOnly<const js::Class*> clasp = js::GetObjectClass(aGlobal);
         MOZ_ASSERT(clasp->flags & (JSCLASS_PRIVATE_IS_NSISUPPORTS |
                                    JSCLASS_HAS_PRIVATE) ||
                    mozilla::dom::IsDOMClass(clasp));
@@ -96,28 +96,28 @@ XPCWrappedNativeScope::XPCWrappedNativeS
     // Attach ourselves to the compartment private.
     CompartmentPrivate *priv = EnsureCompartmentPrivate(aGlobal);
     priv->scope = this;
 
     // Determine whether we would allow an XBL scope in this situation.
     // In addition to being pref-controlled, we also disable XBL scopes for
     // remote XUL domains, _except_ if we have an additional pref override set.
     nsIPrincipal *principal = GetPrincipal();
-    mAllowXBLScope = !RemoteXULForbidsXBLScope(principal, aGlobal);
+    mAllowContentXBLScope = !RemoteXULForbidsXBLScope(principal, aGlobal);
 
     // Determine whether to use an XBL scope.
-    mUseXBLScope = mAllowXBLScope;
-    if (mUseXBLScope) {
+    mUseContentXBLScope = mAllowContentXBLScope;
+    if (mUseContentXBLScope) {
       const js::Class *clasp = js::GetObjectClass(mGlobalJSObject);
-      mUseXBLScope = !strcmp(clasp->name, "Window") ||
-                     !strcmp(clasp->name, "ChromeWindow") ||
-                     !strcmp(clasp->name, "ModalContentWindow");
+      mUseContentXBLScope = !strcmp(clasp->name, "Window") ||
+                            !strcmp(clasp->name, "ChromeWindow") ||
+                            !strcmp(clasp->name, "ModalContentWindow");
     }
-    if (mUseXBLScope) {
-      mUseXBLScope = principal && !nsContentUtils::IsSystemPrincipal(principal);
+    if (mUseContentXBLScope) {
+      mUseContentXBLScope = principal && !nsContentUtils::IsSystemPrincipal(principal);
     }
 }
 
 // static
 bool
 XPCWrappedNativeScope::IsDyingScope(XPCWrappedNativeScope *scope)
 {
     for (XPCWrappedNativeScope *cur = gDyingScopes; cur; cur = cur->mNext) {
@@ -181,30 +181,30 @@ XPCWrappedNativeScope::AttachComponentsO
     MOZ_ASSERT(js::IsObjectInContextCompartment(global, aCx));
 
     RootedId id(aCx, XPCJSRuntime::Get()->GetStringID(XPCJSRuntime::IDX_COMPONENTS));
     return JS_DefinePropertyById(aCx, global, id, components,
                                  JSPROP_PERMANENT | JSPROP_READONLY);
 }
 
 JSObject*
-XPCWrappedNativeScope::EnsureXBLScope(JSContext *cx)
+XPCWrappedNativeScope::EnsureContentXBLScope(JSContext *cx)
 {
     JS::RootedObject global(cx, GetGlobalJSObject());
     MOZ_ASSERT(js::IsObjectInContextCompartment(global, cx));
-    MOZ_ASSERT(!mIsXBLScope);
+    MOZ_ASSERT(!mIsContentXBLScope);
     MOZ_ASSERT(strcmp(js::GetObjectClass(global)->name,
                       "nsXBLPrototypeScript compilation scope"));
 
     // If we already have a special XBL scope object, we know what to use.
-    if (mXBLScope)
-        return mXBLScope;
+    if (mContentXBLScope)
+        return mContentXBLScope;
 
     // If this scope doesn't need an XBL scope, just return the global.
-    if (!mUseXBLScope)
+    if (!mUseContentXBLScope)
         return global;
 
     // Set up the sandbox options. Note that we use the DOM global as the
     // sandboxPrototype so that the XBL scope can access all the DOM objects
     // it's accustomed to accessing.
     //
     // NB: One would think that wantXrays wouldn't make a difference here.
     // However, wantXrays lives a secret double life, and one of its other
@@ -223,56 +223,58 @@ XPCWrappedNativeScope::EnsureXBLScope(JS
     nsTArray< nsCOMPtr<nsIPrincipal> > principalAsArray(1);
     principalAsArray.AppendElement(principal);
     ep = new nsExpandedPrincipal(principalAsArray);
 
     // Create the sandbox.
     RootedValue v(cx);
     nsresult rv = CreateSandboxObject(cx, &v, ep, options);
     NS_ENSURE_SUCCESS(rv, nullptr);
-    mXBLScope = &v.toObject();
+    mContentXBLScope = &v.toObject();
 
     // Tag it.
-    EnsureCompartmentPrivate(js::UncheckedUnwrap(mXBLScope))->scope->mIsXBLScope = true;
+    EnsureCompartmentPrivate(js::UncheckedUnwrap(mContentXBLScope))->scope->mIsContentXBLScope = true;
 
     // Good to go!
-    return mXBLScope;
+    return mContentXBLScope;
 }
 
 bool
-XPCWrappedNativeScope::AllowXBLScope()
+XPCWrappedNativeScope::AllowContentXBLScope()
 {
     // We only disallow XBL scopes in remote XUL situations.
-    MOZ_ASSERT_IF(!mAllowXBLScope,
+    MOZ_ASSERT_IF(!mAllowContentXBLScope,
                   nsContentUtils::AllowXULXBLForPrincipal(GetPrincipal()));
-    return mAllowXBLScope;
+    return mAllowContentXBLScope;
 }
 
 namespace xpc {
 JSObject *GetXBLScope(JSContext *cx, JSObject *contentScopeArg)
 {
     JS::RootedObject contentScope(cx, contentScopeArg);
     JSAutoCompartment ac(cx, contentScope);
-    JSObject *scope = EnsureCompartmentPrivate(contentScope)->scope->EnsureXBLScope(cx);
+    JSObject *scope = EnsureCompartmentPrivate(contentScope)->scope->EnsureContentXBLScope(cx);
     NS_ENSURE_TRUE(scope, nullptr); // See bug 858642.
     scope = js::UncheckedUnwrap(scope);
     JS::ExposeObjectToActiveJS(scope);
     return scope;
 }
 
-bool AllowXBLScope(JSCompartment *c)
+bool
+AllowContentXBLScope(JSCompartment *c)
 {
   XPCWrappedNativeScope *scope = EnsureCompartmentPrivate(c)->scope;
-  return scope && scope->AllowXBLScope();
+  return scope && scope->AllowContentXBLScope();
 }
 
-bool UseXBLScope(JSCompartment *c)
+bool
+UseContentXBLScope(JSCompartment *c)
 {
   XPCWrappedNativeScope *scope = EnsureCompartmentPrivate(c)->scope;
-  return scope && scope->UseXBLScope();
+  return scope && scope->UseContentXBLScope();
 }
 
 } /* namespace xpc */
 
 XPCWrappedNativeScope::~XPCWrappedNativeScope()
 {
     MOZ_COUNT_DTOR(XPCWrappedNativeScope);
 
@@ -296,17 +298,17 @@ XPCWrappedNativeScope::~XPCWrappedNative
     // XXX we should assert that we are dead or that xpconnect has shutdown
     // XXX might not want to do this at xpconnect shutdown time???
     mComponents = nullptr;
 
     if (mXrayExpandos.initialized())
         mXrayExpandos.destroy();
 
     JSRuntime *rt = XPCJSRuntime::Get()->Runtime();
-    mXBLScope.finalize(rt);
+    mContentXBLScope.finalize(rt);
     mGlobalJSObject.finalize(rt);
 }
 
 static PLDHashOperator
 WrappedNativeJSGCThingTracer(PLDHashTable *table, PLDHashEntryHdr *hdr,
                              uint32_t number, void *arg)
 {
     XPCWrappedNative* wrapper = ((Native2WrappedNativeMap::Entry*)hdr)->value;
--- a/js/xpconnect/src/nsXPConnect.cpp
+++ b/js/xpconnect/src/nsXPConnect.cpp
@@ -1572,15 +1572,15 @@ IsChromeOrXBL(JSContext* cx, JSObject* /
 {
     MOZ_ASSERT(NS_IsMainThread());
     JSCompartment* c = js::GetContextCompartment(cx);
 
     // For remote XUL, we run XBL in the XUL scope. Given that we care about
     // compat and not security for remote XUL, we just always claim to be XBL.
     //
     // Note that, for performance, we don't check AllowXULXBLForPrincipal here,
-    // and instead rely on the fact that AllowXBLScope() only returns false in
+    // and instead rely on the fact that AllowContentXBLScope() only returns false in
     // remote XUL situations.
-    return AccessCheck::isChrome(c) || IsXBLScope(c) || !AllowXBLScope(c);
+    return AccessCheck::isChrome(c) || IsContentXBLScope(c) || !AllowContentXBLScope(c);
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -1015,18 +1015,18 @@ public:
     SystemIsBeingShutDown();
 
     static void
     TraceWrappedNativesInAllScopes(JSTracer* trc, XPCJSRuntime* rt);
 
     void TraceSelf(JSTracer *trc) {
         MOZ_ASSERT(mGlobalJSObject);
         mGlobalJSObject.trace(trc, "XPCWrappedNativeScope::mGlobalJSObject");
-        if (mXBLScope)
-            mXBLScope.trace(trc, "XPCWrappedNativeScope::mXBLScope");
+        if (mContentXBLScope)
+            mContentXBLScope.trace(trc, "XPCWrappedNativeScope::mXBLScope");
         if (mXrayExpandos.initialized())
             mXrayExpandos.trace(trc);
     }
 
     static void
     SuspectAllWrappers(XPCJSRuntime* rt, nsCycleCollectionNoteRootCallback &cb);
 
     static void
@@ -1092,25 +1092,25 @@ public:
     void RemoveDOMExpandoObject(JSObject *expando) {
         if (mDOMExpandoSet)
             mDOMExpandoSet->remove(expando);
     }
 
     // Gets the appropriate scope object for XBL in this scope. The context
     // must be same-compartment with the global upon entering, and the scope
     // object is wrapped into the compartment of the global.
-    JSObject *EnsureXBLScope(JSContext *cx);
+    JSObject *EnsureContentXBLScope(JSContext *cx);
 
     XPCWrappedNativeScope(JSContext *cx, JS::HandleObject aGlobal);
 
     nsAutoPtr<JSObject2JSObjectMap> mWaiverWrapperMap;
 
-    bool IsXBLScope() { return mIsXBLScope; }
-    bool AllowXBLScope();
-    bool UseXBLScope() { return mUseXBLScope; }
+    bool IsContentXBLScope() { return mIsContentXBLScope; }
+    bool AllowContentXBLScope();
+    bool UseContentXBLScope() { return mUseContentXBLScope; }
 
 protected:
     virtual ~XPCWrappedNativeScope();
 
     static void KillDyingScopes();
 
     XPCWrappedNativeScope(); // not implemented
 
@@ -1125,39 +1125,39 @@ private:
     XPCWrappedNativeScope*           mNext;
     // The JS global object for this scope.  If non-null, this will be the
     // default parent for the XPCWrappedNatives that have us as the scope,
     // unless a PreCreate hook overrides it.  Note that this _may_ be null (see
     // constructor).
     JS::ObjectPtr                    mGlobalJSObject;
 
     // XBL Scope. This is is a lazily-created sandbox for non-system scopes.
-    // EnsureXBLScope() decides whether it needs to be created or not.
+    // EnsureContentXBLScope() decides whether it needs to be created or not.
     // This reference is wrapped into the compartment of mGlobalJSObject.
-    JS::ObjectPtr                    mXBLScope;
+    JS::ObjectPtr                    mContentXBLScope;
 
     nsAutoPtr<DOMExpandoSet> mDOMExpandoSet;
 
     JS::WeakMapPtr<JSObject*, JSObject*> mXrayExpandos;
 
-    bool mIsXBLScope;
+    bool mIsContentXBLScope;
 
     // For remote XUL domains, we run all XBL in the content scope for compat
     // reasons (though we sometimes pref this off for automation). We separately
-    // track the result of this decision (mAllowXBLScope), from the decision
-    // of whether to actually _use_ an XBL scope (mUseXBLScope), which depends
+    // track the result of this decision (mAllowContentXBLScope), from the decision
+    // of whether to actually _use_ an XBL scope (mUseContentXBLScope), which depends
     // on the type of global and whether the compartment is system principal
     // or not.
     //
     // This distinction is useful primarily because, if true, we know that we
     // have no way of distinguishing XBL script from content script for the
     // given scope. In these (unsupported) situations, we just always claim to
     // be XBL.
-    bool mAllowXBLScope;
-    bool mUseXBLScope;
+    bool mAllowContentXBLScope;
+    bool mUseContentXBLScope;
 };
 
 /***************************************************************************/
 // XPCNativeMember represents a single idl declared method, attribute or
 // constant.
 
 // Tight. No virtual methods. Can be bitwise copied (until any resolution done).
 
--- a/js/xpconnect/src/xpcpublic.h
+++ b/js/xpconnect/src/xpcpublic.h
@@ -65,18 +65,18 @@ private:
     // Whether the new-style domain policy when this compartment was created
     // forbids script execution.
     bool mScriptBlockedByPolicy;
 };
 
 JSObject *
 TransplantObject(JSContext *cx, JS::HandleObject origobj, JS::HandleObject target);
 
-bool IsXBLScope(JSCompartment *compartment);
-bool IsInXBLScope(JSObject *obj);
+bool IsContentXBLScope(JSCompartment *compartment);
+bool IsInContentXBLScope(JSObject *obj);
 
 // Return a raw XBL scope object corresponding to contentScope, which must
 // be an object whose global is a DOM window.
 //
 // The return value is not wrapped into cx->compartment, so be sure to enter
 // its compartment before doing anything meaningful.
 //
 // Also note that XBL scopes are lazily created, so the return-value should be
@@ -85,33 +85,34 @@ bool IsInXBLScope(JSObject *obj);
 //
 // This function asserts if |contentScope| is itself in an XBL scope to catch
 // sloppy consumers. Conversely, GetXBLScopeOrGlobal will handle objects that
 // are in XBL scope (by just returning the global).
 JSObject *
 GetXBLScope(JSContext *cx, JSObject *contentScope);
 
 inline JSObject *
-GetXBLScopeOrGlobal(JSContext *cx, JSObject *obj) {
-    if (IsInXBLScope(obj))
+GetXBLScopeOrGlobal(JSContext *cx, JSObject *obj)
+{
+    if (IsInContentXBLScope(obj))
         return js::GetGlobalForObjectCrossCompartment(obj);
     return GetXBLScope(cx, obj);
 }
 
 // Returns whether XBL scopes have been explicitly disabled for code running
-// in this compartment. See the comment around mAllowXBLScope.
+// in this compartment. See the comment around mAllowContentXBLScope.
 bool
-AllowXBLScope(JSCompartment *c);
+AllowContentXBLScope(JSCompartment *c);
 
 // Returns whether we will use an XBL scope for this compartment. This is
 // semantically equivalent to comparing global != GetXBLScope(global), but it
 // does not have the side-effect of eagerly creating the XBL scope if it does
 // not already exist.
 bool
-UseXBLScope(JSCompartment *c);
+UseContentXBLScope(JSCompartment *c);
 
 bool
 IsSandboxPrototypeProxy(JSObject *obj);
 
 bool
 IsReflector(JSObject *obj);
 
 bool
--- a/js/xpconnect/tests/chrome/test_xrayToJS.xul
+++ b/js/xpconnect/tests/chrome/test_xrayToJS.xul
@@ -152,19 +152,24 @@ https://bugzilla.mozilla.org/show_bug.cg
     xrayProto.expando = 42;
     is(xray.expando, 42, "Xrayed instances see proto expandos");
     is(xray2.expando, 42, "Xrayed instances see proto expandos");
   }
 
   function testDate() {
     // toGMTString is handled oddly in the engine. We don't bother to support
     // it over Xrays.
-    //
-    // We don't yet support self-hosted functions on Xrays. See bug 972987.
-    let propsToSkip = ['toGMTString', 'toLocaleString',
-                       'toLocaleDateString', 'toLocaleTimeString'];
+    let propsToSkip = ['toGMTString'];
+
     testXray('Date', new iwin.Date(), new iwin.Date(), propsToSkip);
+
+    // Test the self-hosted toLocaleString.
+    var d = new iwin.Date();
+    isnot(d.toLocaleString, Cu.unwaiveXrays(d.wrappedJSObject.toLocaleString), "Different function identities");
+    is(Cu.getGlobalForObject(d.toLocaleString), window, "Xray global is correct");
+    is(Cu.getGlobalForObject(d.wrappedJSObject.toLocaleString), iwin, "Underlying global is correct");
+    is(d.toLocaleString('de-DE'), d.wrappedJSObject.toLocaleString('de-DE'), "Results match");
   }
 
   ]]>
   </script>
   <iframe id="ifr" onload="go();" src="http://example.org/tests/js/xpconnect/tests/mochitest/file_empty.html" />
 </window>
--- a/js/xpconnect/wrappers/WrapperFactory.cpp
+++ b/js/xpconnect/wrappers/WrapperFactory.cpp
@@ -426,17 +426,17 @@ WrapperFactory::Rewrap(JSContext *cx, Ha
     // a privileged scope is wrapped with a CrossCompartmentWrapper, even though
     // the lack of a waiver _really_ should give it an opaque wrapper. This is
     // a bit too entrenched to change for content-chrome, but we can at least fix
     // it for XBL scopes.
     //
     // See bug 843829.
     else if (targetSubsumesOrigin && !originSubsumesTarget &&
              !waiveXrayFlag && xrayType == NotXray &&
-             IsXBLScope(target))
+             IsContentXBLScope(target))
     {
         wrapper = &FilteringWrapper<CrossCompartmentSecurityWrapper, GentlyOpaque>::singleton;
     }
 
     //
     // Now, handle the regular cases.
     //
     // These are wrappers we can compute using a rule-based approach. In order
@@ -457,20 +457,20 @@ WrapperFactory::Rewrap(JSContext *cx, Ha
         bool wantXrays = !(sameOrigin && !targetdata->wantXrays);
 
         // If Xrays are warranted, the caller may waive them for non-security
         // wrappers.
         bool waiveXrays = wantXrays && !securityWrapper && waiveXrayFlag;
 
         // We have slightly different behavior for the case when the object
         // being wrapped is in an XBL scope.
-        bool originIsXBLScope = IsXBLScope(origin);
+        bool originIsContentXBLScope = IsContentXBLScope(origin);
 
         wrapper = SelectWrapper(securityWrapper, wantXrays, xrayType, waiveXrays,
-                                originIsXBLScope);
+                                originIsContentXBLScope);
     }
 
     if (!targetSubsumesOrigin) {
         // Do a belt-and-suspenders check against exposing eval()/Function() to
         // non-subsuming content.
         JSFunction *fun = JS_GetObjectFunction(obj);
         if (fun) {
             if (JS_IsBuiltinEvalFunction(fun) || JS_IsBuiltinFunctionConstructor(fun)) {
--- a/js/xpconnect/wrappers/XrayWrapper.cpp
+++ b/js/xpconnect/wrappers/XrayWrapper.cpp
@@ -447,65 +447,87 @@ JSXrayTraits::resolveOwnProperty(JSConte
     // properties when we start supporting arrays.
     if (!JSID_IS_STRING(id))
         return true;
     Rooted<JSFlatString*> str(cx, JSID_TO_FLAT_STRING(id));
 
     // Scan through the functions.
     const JSFunctionSpec *fsMatch = nullptr;
     for (const JSFunctionSpec *fs = clasp->spec.prototypeFunctions; fs && fs->name; ++fs) {
-        // We don't support self-hosted functions yet. See bug 972987.
-        if (fs->selfHostedName)
-            continue;
         if (JS_FlatStringEqualsAscii(str, fs->name)) {
             fsMatch = fs;
             break;
         }
     }
     if (fsMatch) {
         // Generate an Xrayed version of the method.
-        Rooted<JSFunction*> fun(cx, JS_NewFunctionById(cx, fsMatch->call.op, fsMatch->nargs,
-                                                       0, wrapper, id));
+        RootedFunction fun(cx);
+        if (fsMatch->selfHostedName) {
+            fun = JS::GetSelfHostedFunction(cx, fsMatch->selfHostedName, id, fsMatch->nargs);
+        } else {
+            fun = JS_NewFunctionById(cx, fsMatch->call.op, fsMatch->nargs,
+                                     0, wrapper, id);
+        }
         if (!fun)
             return false;
 
         // The generic Xray machinery only defines non-own properties on the holder.
         // This is broken, and will be fixed at some point, but for now we need to
         // cache the value explicitly. See the corresponding call to
         // JS_GetPropertyById at the top of this function.
         RootedObject funObj(cx, JS_GetFunctionObject(fun));
         return JS_DefinePropertyById(cx, holder, id, funObj, 0) &&
                JS_GetPropertyDescriptorById(cx, holder, id, desc);
     }
 
     // Scan through the properties.
     const JSPropertySpec *psMatch = nullptr;
     for (const JSPropertySpec *ps = clasp->spec.prototypeProperties; ps && ps->name; ++ps) {
-        // We don't support self-hosted accessors yet (see bug 972987). And given
-        // the confusion outlined in bug 992977, we can't support JSPropertyOp-
-        // backed entries either (which in practice is fine).
-        if (!(ps->flags & JSPROP_NATIVE_ACCESSORS))
-            continue;
         if (JS_FlatStringEqualsAscii(str, ps->name)) {
             psMatch = ps;
             break;
         }
     }
     if (psMatch) {
+        desc.value().setUndefined();
+        // Note that this is also kind of an abuse of JSPROP_NATIVE_ACCESSORS.
+        // See bug 992977.
+        RootedFunction getterObj(cx);
+        RootedFunction setterObj(cx);
+        unsigned flags = psMatch->flags;
+        if (flags & JSPROP_NATIVE_ACCESSORS) {
+            desc.setGetter(psMatch->getter.propertyOp.op);
+            desc.setSetter(psMatch->setter.propertyOp.op);
+        } else {
+            MOZ_ASSERT(flags & JSPROP_GETTER);
+            getterObj = JS::GetSelfHostedFunction(cx, psMatch->getter.selfHosted.funname, id, 0);
+            if (!getterObj)
+                return false;
+            desc.setGetterObject(JS_GetFunctionObject(getterObj));
+            if (psMatch->setter.selfHosted.funname) {
+                MOZ_ASSERT(flags & JSPROP_SETTER);
+                setterObj = JS::GetSelfHostedFunction(cx, psMatch->setter.selfHosted.funname, id, 0);
+                if (!setterObj)
+                    return false;
+                desc.setSetterObject(JS_GetFunctionObject(setterObj));
+            }
+        }
+        desc.setAttributes(flags);
+
         // The generic Xray machinery only defines non-own properties on the holder.
         // This is broken, and will be fixed at some point, but for now we need to
         // cache the value explicitly. See the corresponding call to
         // JS_GetPropertyById at the top of this function.
         //
         // Note also that the public-facing API here doesn't give us a way to
         // pass along JITInfo. It's probably ok though, since Xrays are already
         // pretty slow.
         return JS_DefinePropertyById(cx, holder, id,
-                                     UndefinedHandleValue, psMatch->flags,
-                                     psMatch->getter.propertyOp.op, psMatch->setter.propertyOp.op) &&
+                                     UndefinedHandleValue, desc.attributes(),
+                                     desc.getter(), desc.setter()) &&
                JS_GetPropertyDescriptorById(cx, holder, id, desc);
     }
 
     return true;
 }
 
 bool
 JSXrayTraits::enumerateNames(JSContext *cx, HandleObject wrapper, unsigned flags,
@@ -522,31 +544,30 @@ JSXrayTraits::enumerateNames(JSContext *
     // Grab the JSClass. We require all Xrayable classes to have a ClassSpec.
     RootedObject target(cx, getTargetObject(wrapper));
     const js::Class *clasp = js::GetObjectClass(target);
     MOZ_ASSERT(JSCLASS_CACHED_PROTO_KEY(clasp) == getProtoKey(holder));
     MOZ_ASSERT(clasp->spec.defined());
 
     // Intern all the strings, and pass theme to the caller.
     for (const JSFunctionSpec *fs = clasp->spec.prototypeFunctions; fs && fs->name; ++fs) {
-        // We don't support self-hosted functions yet. See bug 972987.
-        if (fs->selfHostedName)
-            continue;
         RootedString str(cx, JS_InternString(cx, fs->name));
         if (!str)
             return false;
         if (!props.append(INTERNED_STRING_TO_JSID(cx, str)))
             return false;
     }
     for (const JSPropertySpec *ps = clasp->spec.prototypeProperties; ps && ps->name; ++ps) {
-        // We don't support self-hosted functions yet. See bug 972987.
-        // Note that this is also kind of an abuse of JSPROP_NATIVE_ACCESSORS.
-        // See bug 992977.
-        if (!(ps->flags & JSPROP_NATIVE_ACCESSORS))
-            continue;
+        // We have code to Xray self-hosted accessors. But at present, there don't appear
+        // to be any self-hosted accessors anywhere in SpiderMonkey, let alone in on an
+        // Xrayable class, so we can't test it. Assert against it to make sure that we get
+        // test coverage in test_XrayToJS.xul when the time comes.
+        MOZ_ASSERT(ps->flags & JSPROP_NATIVE_ACCESSORS,
+                   "Self-hosted accessor added to Xrayable class - ping the XPConnect "
+                   "module owner about adding test coverage");
         RootedString str(cx, JS_InternString(cx, ps->name));
         if (!str)
             return false;
         if (!props.append(INTERNED_STRING_TO_JSID(cx, str)))
             return false;
     }
 
     // Add the 'constructor' property.
@@ -1749,17 +1770,17 @@ static void
 DEBUG_CheckXBLCallable(JSContext *cx, JSObject *obj)
 {
     // In general, we shouldn't have cross-compartment wrappers here, because
     // we should be running in an XBL scope, and the content prototype should
     // contain wrappers to functions defined in the XBL scope. But if the node
     // has been adopted into another compartment, those prototypes will now point
     // to a different XBL scope (which is ok).
     MOZ_ASSERT_IF(js::IsCrossCompartmentWrapper(obj),
-                  xpc::IsXBLScope(js::GetObjectCompartment(js::UncheckedUnwrap(obj))));
+                  xpc::IsContentXBLScope(js::GetObjectCompartment(js::UncheckedUnwrap(obj))));
     MOZ_ASSERT(JS_ObjectIsCallable(cx, obj));
 }
 
 static void
 DEBUG_CheckXBLLookup(JSContext *cx, JSPropertyDescriptor *desc)
 {
     if (!desc->obj)
         return;
@@ -1904,17 +1925,17 @@ XrayWrapper<Base, Traits>::getPropertyDe
     // While we have to do some sketchy walking through content land, we should
     // be protected by read-only/non-configurable properties, and any functions
     // we end up with should _always_ be living in an XBL scope (usually ours,
     // but could be another if the node has been adopted).
     //
     // Make sure to assert this.
     nsCOMPtr<nsIContent> content;
     if (!desc.object() &&
-        EnsureCompartmentPrivate(wrapper)->scope->IsXBLScope() &&
+        EnsureCompartmentPrivate(wrapper)->scope->IsContentXBLScope() &&
         (content = do_QueryInterfaceNative(cx, wrapper)))
     {
         if (!nsContentUtils::LookupBindingMember(cx, content, id, desc))
             return false;
         DEBUG_CheckXBLLookup(cx, desc.address());
     }
 
     // If we still have nothing, we're done.
new file mode 100644
--- /dev/null
+++ b/layout/base/TouchCaret.cpp
@@ -0,0 +1,820 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 sw=2 et tw=78: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "TouchCaret.h"
+
+#include "nsCOMPtr.h"
+#include "nsFrameSelection.h"
+#include "nsIFrame.h"
+#include "nsIScrollableFrame.h"
+#include "nsIDOMNode.h"
+#include "nsISelection.h"
+#include "nsISelectionPrivate.h"
+#include "nsIContent.h"
+#include "nsIPresShell.h"
+#include "nsCanvasFrame.h"
+#include "nsRenderingContext.h"
+#include "nsPresContext.h"
+#include "nsBlockFrame.h"
+#include "nsISelectionController.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/BasicEvents.h"
+#include "nsIDOMWindow.h"
+#include "nsQueryContentEventResult.h"
+#include "nsIInterfaceRequestorUtils.h"
+#include "nsView.h"
+#include "nsDOMTokenList.h"
+#include <algorithm>
+
+using namespace mozilla;
+
+#define TOUCHCARET_LOG(...)
+// #define TOUCHCARET_LOG(...) printf_stderr("TouchCaret: " __VA_ARGS__)
+
+// Click on the boundary of input/textarea will place the caret at the
+// front/end of the content. To advoid this, we need to deflate the content
+// boundary by 61 app units (1 pixel + 1 app unit).
+static const int32_t kBoundaryAppUnits = 61;
+// The auto scroll timer's interval in milliseconds.
+static const int32_t kAutoScrollTimerDelay = 30;
+
+NS_IMPL_ISUPPORTS(TouchCaret, nsISelectionListener)
+
+/*static*/ int32_t TouchCaret::sTouchCaretMaxDistance = 0;
+/*static*/ int32_t TouchCaret::sTouchCaretExpirationTime = 0;
+
+TouchCaret::TouchCaret(nsIPresShell* aPresShell)
+  : mState(TOUCHCARET_NONE),
+    mActiveTouchId(-1),
+    mCaretCenterToDownPointOffsetY(0),
+    mVisible(false)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  static bool addedTouchCaretPref = false;
+  if (!addedTouchCaretPref) {
+    Preferences::AddIntVarCache(&sTouchCaretMaxDistance,
+                                "touchcaret.distance.threshold");
+    Preferences::AddIntVarCache(&sTouchCaretExpirationTime,
+                                "touchcaret.expiration.time");
+    addedTouchCaretPref = true;
+  }
+
+  // The presshell owns us, so no addref.
+  mPresShell = do_GetWeakReference(aPresShell);
+  MOZ_ASSERT(mPresShell, "Hey, pres shell should support weak refs");
+}
+
+TouchCaret::~TouchCaret()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (mTouchCaretExpirationTimer) {
+    mTouchCaretExpirationTimer->Cancel();
+    mTouchCaretExpirationTimer = nullptr;
+  }
+}
+
+nsIFrame*
+TouchCaret::GetCanvasFrame()
+{
+  nsCOMPtr<nsIPresShell> presShell = do_QueryReferent(mPresShell);
+  if (!presShell) {
+    return nullptr;
+  }
+  return presShell->GetCanvasFrame();
+}
+
+void
+TouchCaret::SetVisibility(bool aVisible)
+{
+  if (mVisible == aVisible) {
+    return;
+  }
+  mVisible = aVisible;
+
+  nsCOMPtr<nsIPresShell> presShell = do_QueryReferent(mPresShell);
+  if (!presShell) {
+    return;
+  }
+  mozilla::dom::Element* touchCaretElement = presShell->GetTouchCaretElement();
+  if (!touchCaretElement) {
+    return;
+  }
+
+  // Set touch caret visibility.
+  ErrorResult err;
+  touchCaretElement->ClassList()->Toggle(NS_LITERAL_STRING("hidden"),
+                                         dom::Optional<bool>(!mVisible),
+                                         err);
+  // Set touch caret expiration time.
+  mVisible ? LaunchExpirationTimer() : CancelExpirationTimer();
+
+  // We must call SetHasTouchCaret() in order to get APZC to wait until the
+  // event has been round-tripped and check whether it has been handled,
+  // otherwise B2G will end up panning the document when the user tries to drag
+  // touch caret.
+  presShell->SetMayHaveTouchCaret(mVisible);
+}
+
+nsRect
+TouchCaret::GetTouchFrameRect()
+{
+  nsCOMPtr<nsIPresShell> presShell = do_QueryReferent(mPresShell);
+  if (!presShell) {
+    return nsRect();
+  }
+
+  dom::Element* touchCaretElement = presShell->GetTouchCaretElement();
+  if (!touchCaretElement) {
+    return nsRect();
+  }
+
+  // Get touch caret position relative to canvas frame.
+  nsIFrame* touchCaretFrame = touchCaretElement->GetPrimaryFrame();
+  nsRect tcRect = touchCaretFrame->GetRectRelativeToSelf();
+  nsIFrame* canvasFrame = GetCanvasFrame();
+
+  nsLayoutUtils::TransformResult rv =
+    nsLayoutUtils::TransformRect(touchCaretFrame, canvasFrame, tcRect);
+  return rv == nsLayoutUtils::TRANSFORM_SUCCEEDED ? tcRect : nsRect();
+}
+
+nsRect
+TouchCaret::GetContentBoundary()
+{
+  nsCOMPtr<nsIPresShell> presShell = do_QueryReferent(mPresShell);
+  if (!presShell) {
+    return nsRect();
+  }
+
+  nsRefPtr<nsCaret> caret = presShell->GetCaret();
+  nsISelection* caretSelection = caret->GetCaretDOMSelection();
+  nsRect focusRect;
+  nsIFrame* focusFrame = caret->GetGeometry(caretSelection, &focusRect);
+  nsIFrame* canvasFrame = GetCanvasFrame();
+
+  // Get the editing host to determine the touch caret dragable boundary.
+  dom::Element* editingHost = focusFrame->GetContent()->GetEditingHost();
+  if (!editingHost) {
+    return nsRect();
+  }
+
+  nsRect resultRect;
+  for (nsIFrame* frame = editingHost->GetPrimaryFrame(); frame;
+       frame = frame->GetNextContinuation()) {
+    nsRect rect = frame->GetContentRectRelativeToSelf();
+    nsLayoutUtils::TransformRect(frame, canvasFrame, rect);
+    resultRect = resultRect.Union(rect);
+
+    mozilla::layout::FrameChildListIterator lists(frame);
+    for (; !lists.IsDone(); lists.Next()) {
+      // Loop over all children to take the overflow rect in to consideration.
+      nsFrameList::Enumerator childFrames(lists.CurrentList());
+      for (; !childFrames.AtEnd(); childFrames.Next()) {
+        nsIFrame* kid = childFrames.get();
+        nsRect overflowRect = kid->GetScrollableOverflowRect();
+        nsLayoutUtils::TransformRect(kid, canvasFrame, overflowRect);
+        resultRect = resultRect.Union(overflowRect);
+      }
+    }
+  }
+  // Shrink rect to make sure we never hit the boundary.
+  resultRect.Deflate(kBoundaryAppUnits);
+
+  return resultRect;
+}
+
+nscoord
+TouchCaret::GetCaretYCenterPosition()
+{
+  nsCOMPtr<nsIPresShell> presShell = do_QueryReferent(mPresShell);
+  if (!presShell) {
+    return 0;
+  }
+
+  nsRefPtr<nsCaret> caret = presShell->GetCaret();
+  nsISelection* caretSelection = caret->GetCaretDOMSelection();
+  nsRect focusRect;
+  nsIFrame* focusFrame = caret->GetGeometry(caretSelection, &focusRect);
+  nsRect caretRect = focusFrame->GetRectRelativeToSelf();
+  nsIFrame *canvasFrame = GetCanvasFrame();
+  nsLayoutUtils::TransformRect(focusFrame, canvasFrame, caretRect);
+
+  return (caretRect.y + caretRect.height / 2);
+}
+
+void
+TouchCaret::SetTouchFramePos(const nsPoint& aOrigin)
+{
+  nsCOMPtr<nsIPresShell> presShell = do_QueryReferent(mPresShell);
+  if (!presShell) {
+    return;
+  }
+
+  mozilla::dom::Element* touchCaretElement = presShell->GetTouchCaretElement();
+  if (!touchCaretElement) {
+    return;
+  }
+
+  // Convert aOrigin to CSS pixels.
+  nsRefPtr<nsPresContext> presContext = presShell->GetPresContext();
+  int32_t x = presContext->AppUnitsToIntCSSPixels(aOrigin.x);
+  int32_t y = presContext->AppUnitsToIntCSSPixels(aOrigin.y);
+
+  nsAutoString styleStr;
+  styleStr.AppendLiteral("left: ");
+  styleStr.AppendInt(x);
+  styleStr.AppendLiteral("px; top: ");
+  styleStr.AppendInt(y);
+  styleStr.AppendLiteral("px;");
+
+  touchCaretElement->SetAttr(kNameSpaceID_None, nsGkAtoms::style,
+                             styleStr, true);
+}
+
+void
+TouchCaret::MoveCaret(const nsPoint& movePoint)
+{
+  nsCOMPtr<nsIPresShell> presShell = do_QueryReferent(mPresShell);
+  if (!presShell) {
+    return;
+  }
+
+  // Get scrollable frame.
+  nsRefPtr<nsCaret> caret = presShell->GetCaret();
+  nsISelection* caretSelection = caret->GetCaretDOMSelection();
+  nsRect focusRect;
+  nsIFrame* focusFrame = caret->GetGeometry(caretSelection, &focusRect);
+  nsIFrame* scrollable =
+    nsLayoutUtils::GetClosestFrameOfType(focusFrame, nsGkAtoms::scrollFrame);
+
+  // Convert touch/mouse position to frame coordinates.
+  nsIFrame* canvasFrame = GetCanvasFrame();
+  if (!canvasFrame) {
+    return;
+  }
+  nsPoint offsetToCanvasFrame = nsPoint(0,0);
+  nsLayoutUtils::TransformPoint(scrollable, canvasFrame, offsetToCanvasFrame);
+  nsPoint pt = movePoint - offsetToCanvasFrame;
+
+  // Evaluate offsets.
+  nsIFrame::ContentOffsets offsets =
+    scrollable->GetContentOffsetsFromPoint(pt, nsIFrame::SKIP_HIDDEN);
+
+  // Move caret position.
+  nsWeakFrame weakScrollable = scrollable;
+  nsRefPtr<nsFrameSelection> fs = scrollable->GetFrameSelection();
+  fs->HandleClick(offsets.content, offsets.StartOffset(),
+                  offsets.EndOffset(),
+                  false,
+                  false,
+                  offsets.associateWithNext);
+
+  if (!weakScrollable.IsAlive()) {
+    return;
+  }
+
+  // Scroll scrolled frame.
+  nsIScrollableFrame* saf = do_QueryFrame(scrollable);
+  nsIFrame* capturingFrame = saf->GetScrolledFrame();
+  offsetToCanvasFrame = nsPoint(0,0);
+  nsLayoutUtils::TransformPoint(capturingFrame, canvasFrame, offsetToCanvasFrame);
+  pt = movePoint - offsetToCanvasFrame;
+  fs->StartAutoScrollTimer(capturingFrame, pt, kAutoScrollTimerDelay);
+}
+
+bool
+TouchCaret::IsOnTouchCaret(const nsPoint& aPoint)
+{
+  // Return false if touch caret is not visible.
+  if (!mVisible) {
+    return false;
+  }
+
+  nsRect tcRect = GetTouchFrameRect();
+
+  // Check if the click was in the bounding box of the touch caret.
+  int32_t distance;
+  if (tcRect.Contains(aPoint.x, aPoint.y)) {
+    distance = 0;
+  } else {
+    // If click is outside the bounding box of the touch caret, check the
+    // distance to the center of the touch caret.
+    int32_t posX = (tcRect.x + (tcRect.width / 2));
+    int32_t posY = (tcRect.y + (tcRect.height / 2));
+    int32_t dx = Abs(aPoint.x - posX);
+    int32_t dy = Abs(aPoint.y - posY);
+    distance = dx + dy;
+  }
+  return (distance <= TouchCaretMaxDistance());
+}
+
+nsresult
+TouchCaret::NotifySelectionChanged(nsIDOMDocument* aDoc, nsISelection* aSel,
+                                     int16_t aReason)
+{
+  // Hide touch caret while no caret exists.
+  nsCOMPtr<nsIPresShell> presShell = do_QueryReferent(mPresShell);
+  if (!presShell) {
+    return NS_OK;
+  }
+
+  nsRefPtr<nsCaret> caret = presShell->GetCaret();
+  if (!caret) {
+    SetVisibility(false);
+    return NS_OK;
+  }
+
+  // The same touch caret is shared amongst the document and any text widgets it
+  // may contain. This means that the touch caret could get notifications from
+  // multiple selections.
+  // If this notification is for a selection that is not the one the
+  // the caret is currently interested in , then there is nothing to do!
+  if (aSel != caret->GetCaretDOMSelection()) {
+    return NS_OK;
+  }
+
+  // Update touch caret position and visibility.
+  // Hide touch caret while key event causes selection change.
+  if ((aReason == nsISelectionListener::NO_REASON) ||
+      (aReason & nsISelectionListener::KEYPRESS_REASON)) {
+    UpdateTouchCaret(false);
+  } else {
+    UpdateTouchCaret(true);
+  }
+
+  return NS_OK;
+}
+
+void
+TouchCaret::UpdateTouchCaret(bool aVisible)
+{
+  // Hide touch caret while no caret exists.
+  nsCOMPtr<nsIPresShell> presShell = do_QueryReferent(mPresShell);
+  if (!presShell) {
+    return;
+  }
+
+  nsRefPtr<nsCaret> caret = presShell->GetCaret();
+  if (!caret) {
+    SetVisibility(false);
+    return;
+  }
+
+  // Hide touch caret while caret is not visible.
+  bool caretVisible = false;
+  caret->GetCaretVisible(&caretVisible);
+  if (!caretVisible) {
+    SetVisibility(false);
+    return;
+  }
+
+  // Caret is visible and shown, update touch caret.
+  nsISelection* caretSelection = caret->GetCaretDOMSelection();
+  nsRect focusRect;
+  nsIFrame* focusFrame = caret->GetGeometry(caretSelection, &focusRect);
+  if (!focusFrame || focusRect.IsEmpty()) {
+    SetVisibility(false);
+    return;
+  }
+
+  // Position of the touch caret relative to focusFrame.
+  nsPoint pos = nsPoint(focusRect.x + (focusRect.width / 2),
+                        focusRect.y + focusRect.height);
+
+  // Transform the position to make it relative to canvas frame.
+  nsIFrame* canvasFrame = GetCanvasFrame();
+  if (!canvasFrame) {
+    return;
+  }
+  nsLayoutUtils::TransformPoint(focusFrame, canvasFrame, pos);
+
+  // Clamp the touch caret position to the scrollframe boundary.
+  nsIFrame* closestScrollFrame =
+    nsLayoutUtils::GetClosestFrameOfType(focusFrame, nsGkAtoms::scrollFrame);
+  while (closestScrollFrame) {
+    nsIScrollableFrame* sf = do_QueryFrame(closestScrollFrame);
+    nsRect visualRect = sf->GetScrollPortRect();
+    // Clamp the touch caret in the scroll port.
+    nsLayoutUtils::TransformRect(closestScrollFrame, canvasFrame, visualRect);
+    pos = visualRect.ClampPoint(pos);
+
+    // Get next ancestor scroll frame.
+    closestScrollFrame =
+      nsLayoutUtils::GetClosestFrameOfType(closestScrollFrame->GetParent(),
+                                           nsGkAtoms::scrollFrame);
+  }
+
+  SetTouchFramePos(pos);
+  SetVisibility(aVisible);
+}
+
+/* static */void
+TouchCaret::DisableTouchCaretCallback(nsITimer* aTimer, void* aTouchCaret)
+{
+  nsRefPtr<TouchCaret> self = static_cast<TouchCaret*>(aTouchCaret);
+  NS_PRECONDITION(aTimer == self->mTouchCaretExpirationTimer,
+                  "Unexpected timer");
+
+  self->SetVisibility(false);
+}
+
+void
+TouchCaret::LaunchExpirationTimer()
+{
+  if (TouchCaretExpirationTime() > 0) {
+    if (!mTouchCaretExpirationTimer) {
+      mTouchCaretExpirationTimer = do_CreateInstance("@mozilla.org/timer;1");
+    }
+
+    if (mTouchCaretExpirationTimer) {
+      mTouchCaretExpirationTimer->Cancel();
+      mTouchCaretExpirationTimer->InitWithFuncCallback(DisableTouchCaretCallback,
+                                                       this,
+                                                       TouchCaretExpirationTime(),
+                                                       nsITimer::TYPE_ONE_SHOT);
+    }
+  }
+}
+
+void
+TouchCaret::CancelExpirationTimer()
+{
+  if (mTouchCaretExpirationTimer) {
+    mTouchCaretExpirationTimer->Cancel();
+  }
+}
+
+nsEventStatus
+TouchCaret::HandleEvent(WidgetEvent* aEvent)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  nsCOMPtr<nsIPresShell> presShell = do_QueryReferent(mPresShell);
+  if (!presShell) {
+    return nsEventStatus_eIgnore;
+  }
+
+  mozilla::dom::Element* touchCaretElement = presShell->GetTouchCaretElement();
+  if (!touchCaretElement) {
+    return nsEventStatus_eIgnore;
+  }
+
+  nsEventStatus status = nsEventStatus_eIgnore;
+
+  switch (aEvent->message) {
+    case NS_TOUCH_START:
+    case NS_TOUCH_ENTER:
+      status = HandleTouchDownEvent(aEvent->AsTouchEvent());
+      break;
+    case NS_MOUSE_BUTTON_DOWN:
+      status = HandleMouseDownEvent(aEvent->AsMouseEvent());
+      break;
+    case NS_TOUCH_END:
+      status = HandleTouchUpEvent(aEvent->AsTouchEvent());
+      break;
+   case NS_MOUSE_BUTTON_UP:
+      status = HandleMouseUpEvent(aEvent->AsMouseEvent());
+      break;
+    case NS_TOUCH_MOVE:
+      status = HandleTouchMoveEvent(aEvent->AsTouchEvent());
+      break;
+    case NS_MOUSE_MOVE:
+      status = HandleMouseMoveEvent(aEvent->AsMouseEvent());
+      break;
+    case NS_TOUCH_CANCEL:
+      mTouchesId.Clear();
+      SetState(TOUCHCARET_NONE);
+      LaunchExpirationTimer();
+      break;
+    case NS_KEY_UP:
+    case NS_KEY_DOWN:
+    case NS_KEY_PRESS:
+    case NS_WHEEL_EVENT_START:
+      // Disable touch caret while key/wheel event is received.
+      SetVisibility(false);
+      break;
+    default:
+      break;
+  }
+
+  return status;
+}
+
+nsPoint
+TouchCaret::GetEventPosition(WidgetTouchEvent* aEvent, int32_t aIdentifier)
+{
+  for (size_t i = 0; i < aEvent->touches.Length(); i++) {
+    if (aEvent->touches[i]->mIdentifier == aIdentifier) {
+      // Get event coordinate relative to canvas frame.
+      nsIFrame* canvasFrame = GetCanvasFrame();
+      nsIntPoint touchIntPoint = aEvent->touches[i]->mRefPoint;
+      return nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent,
+                                                          touchIntPoint,
+                                                          canvasFrame);
+    }
+  }
+  return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
+}
+
+nsPoint
+TouchCaret::GetEventPosition(WidgetMouseEvent* aEvent)
+{
+  // Get event coordinate relative to canvas frame.
+  nsIFrame* canvasFrame = GetCanvasFrame();
+  nsIntPoint mouseIntPoint =
+    LayoutDeviceIntPoint::ToUntyped(aEvent->AsGUIEvent()->refPoint);
+  return nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent,
+                                                      mouseIntPoint,
+                                                      canvasFrame);
+}
+
+nsEventStatus
+TouchCaret::HandleMouseMoveEvent(WidgetMouseEvent* aEvent)
+{
+  TOUCHCARET_LOG("%p got a mouse-move in state %d\n", this, mState);
+  nsEventStatus status = nsEventStatus_eIgnore;
+
+  switch (mState) {
+    case TOUCHCARET_NONE:
+      break;
+
+    case TOUCHCARET_MOUSEDRAG_ACTIVE:
+      {
+        nsPoint movePoint = GetEventPosition(aEvent);
+        movePoint.y += mCaretCenterToDownPointOffsetY;
+        nsRect contentBoundary = GetContentBoundary();
+        movePoint = contentBoundary.ClampPoint(movePoint);
+
+        MoveCaret(movePoint);
+        status = nsEventStatus_eConsumeNoDefault;
+      }
+      break;
+
+    case TOUCHCARET_TOUCHDRAG_ACTIVE:
+    case TOUCHCARET_TOUCHDRAG_INACTIVE:
+      // Consume mouse event in touch sequence.
+      status = nsEventStatus_eConsumeNoDefault;
+      break;
+  }
+
+  return status;
+}
+
+nsEventStatus
+TouchCaret::HandleTouchMoveEvent(WidgetTouchEvent* aEvent)
+{
+  TOUCHCARET_LOG("%p got a touch-move in state %d\n", this, mState);
+  nsEventStatus status = nsEventStatus_eIgnore;
+
+  switch (mState) {
+    case TOUCHCARET_NONE:
+      break;
+
+    case TOUCHCARET_MOUSEDRAG_ACTIVE:
+      // Consume touch event in mouse sequence.
+      status = nsEventStatus_eConsumeNoDefault;
+      break;
+
+    case TOUCHCARET_TOUCHDRAG_ACTIVE:
+      {
+        nsPoint movePoint = GetEventPosition(aEvent, mActiveTouchId);
+        movePoint.y += mCaretCenterToDownPointOffsetY;
+        nsRect contentBoundary = GetContentBoundary();
+        movePoint = contentBoundary.ClampPoint(movePoint);
+
+        MoveCaret(movePoint);
+        status = nsEventStatus_eConsumeNoDefault;
+      }
+      break;
+
+    case TOUCHCARET_TOUCHDRAG_INACTIVE:
+      // Consume NS_TOUCH_MOVE event in TOUCHCARET_TOUCHDRAG_INACTIVE state.
+      status = nsEventStatus_eConsumeNoDefault;
+      break;
+  }
+
+  return status;
+}
+
+nsEventStatus
+TouchCaret::HandleMouseUpEvent(WidgetMouseEvent* aEvent)
+{
+  TOUCHCARET_LOG("%p got a mouse-up in state %d\n", this, mState);
+  nsEventStatus status = nsEventStatus_eIgnore;
+
+  switch (mState) {
+    case TOUCHCARET_NONE:
+      break;
+
+    case TOUCHCARET_MOUSEDRAG_ACTIVE:
+      if (aEvent->button == WidgetMouseEvent::eLeftButton) {
+        LaunchExpirationTimer();
+        SetState(TOUCHCARET_NONE);
+        status = nsEventStatus_eConsumeNoDefault;
+      }
+      break;
+
+    case TOUCHCARET_TOUCHDRAG_ACTIVE:
+    case TOUCHCARET_TOUCHDRAG_INACTIVE:
+      // Consume mouse event in touch sequence.
+      status = nsEventStatus_eConsumeNoDefault;
+      break;
+  }
+
+  return status;
+}
+
+nsEventStatus
+TouchCaret::HandleTouchUpEvent(WidgetTouchEvent* aEvent)
+{
+  TOUCHCARET_LOG("%p got a touch-end in state %d\n", this, mState);
+  // Remove touches from cache if the stroke is gone in TOUCHDRAG states.
+  if (mState == TOUCHCARET_TOUCHDRAG_ACTIVE ||
+      mState == TOUCHCARET_TOUCHDRAG_INACTIVE) {
+    for (size_t i = 0; i < aEvent->touches.Length(); i++) {
+      nsTArray<int32_t>::index_type index =
+        mTouchesId.IndexOf(aEvent->touches[i]->mIdentifier);
+      MOZ_ASSERT(index != nsTArray<int32_t>::NoIndex);
+      mTouchesId.RemoveElementAt(index);
+    }
+  }
+
+  nsEventStatus status = nsEventStatus_eIgnore;
+
+  switch (mState) {
+    case TOUCHCARET_NONE:
+      break;
+
+    case TOUCHCARET_MOUSEDRAG_ACTIVE:
+      // Consume touch event in mouse sequence.
+      status = nsEventStatus_eConsumeNoDefault;
+      break;
+
+    case TOUCHCARET_TOUCHDRAG_ACTIVE:
+      if (mTouchesId.Length() == 0) {
+        // No more finger on the screen.
+        SetState(TOUCHCARET_NONE);
+        LaunchExpirationTimer();
+      } else {
+        // Still has finger touching on the screen.
+        if (aEvent->touches[0]->mIdentifier == mActiveTouchId) {
+          // Remove finger from the touch caret.
+          SetState(TOUCHCARET_TOUCHDRAG_INACTIVE);
+          LaunchExpirationTimer();
+        } else {
+          // If the finger removed is not the finger on touch caret, remain in
+          // TOUCHCARET_DRAG_ACTIVE state.
+        }
+      }
+      status = nsEventStatus_eConsumeNoDefault;
+      break;
+
+    case TOUCHCARET_TOUCHDRAG_INACTIVE:
+      if (mTouchesId.Length() == 0) {
+        // No more finger on the screen.
+        SetState(TOUCHCARET_NONE);
+      }
+      status = nsEventStatus_eConsumeNoDefault;
+      break;
+  }
+
+  return status;
+}
+
+nsEventStatus
+TouchCaret::HandleMouseDownEvent(WidgetMouseEvent* aEvent)
+{
+  TOUCHCARET_LOG("%p got a mouse-down in state %d\n", this, mState);
+  if (!GetVisibility()) {
+    // If touch caret is invisible, bypass event.
+    return nsEventStatus_eIgnore;
+  }
+
+  nsEventStatus status = nsEventStatus_eIgnore;
+
+  switch (mState) {
+    case TOUCHCARET_NONE:
+      if (aEvent->button == WidgetMouseEvent::eLeftButton) {
+        nsPoint point = GetEventPosition(aEvent);
+        if (IsOnTouchCaret(point)) {
+          // Cache distence of the event point to the center of touch caret.
+          mCaretCenterToDownPointOffsetY = GetCaretYCenterPosition() - point.y;
+          // Enter TOUCHCARET_MOUSEDRAG_ACTIVE state and cancel the timer.
+          SetState(TOUCHCARET_MOUSEDRAG_ACTIVE);
+          CancelExpirationTimer();
+          status = nsEventStatus_eConsumeNoDefault;
+        } else {
+          // Set touch caret invisible if HisTest fails. Bypass event.
+          SetVisibility(false);
+          status = nsEventStatus_eIgnore;
+        }
+      } else {
+        // Set touch caret invisible if not left button down event.
+        SetVisibility(false);
+        status = nsEventStatus_eIgnore;
+      }
+      break;
+
+    case TOUCHCARET_MOUSEDRAG_ACTIVE:
+      SetVisibility(false);
+      SetState(TOUCHCARET_NONE);
+      break;
+
+    case TOUCHCARET_TOUCHDRAG_ACTIVE:
+    case TOUCHCARET_TOUCHDRAG_INACTIVE:
+      // Consume mouse event in touch sequence.
+      status = nsEventStatus_eConsumeNoDefault;
+      break;
+  }
+
+  return status;
+}
+
+nsEventStatus
+TouchCaret::HandleTouchDownEvent(WidgetTouchEvent* aEvent)
+{
+  TOUCHCARET_LOG("%p got a touch-start in state %d\n", this, mState);
+
+  nsEventStatus status = nsEventStatus_eIgnore;
+
+  switch (mState) {
+    case TOUCHCARET_NONE:
+      if (!GetVisibility()) {
+        // If touch caret is invisible, bypass event.
+        status = nsEventStatus_eIgnore;
+      } else {
+        nsPoint point = GetEventPosition(aEvent, 0);
+        if (IsOnTouchCaret(point)) {
+          // Touch start position is contained in touch caret.
+          mActiveTouchId = aEvent->touches[0]->mIdentifier;
+          // Cache distance of the event point to the center of touch caret.
+          mCaretCenterToDownPointOffsetY = GetCaretYCenterPosition() - point.y;
+          // Enter TOUCHCARET_TOUCHDRAG_ACTIVE state and cancel the timer.
+          SetState(TOUCHCARET_TOUCHDRAG_ACTIVE);
+          CancelExpirationTimer();
+          status = nsEventStatus_eConsumeNoDefault;
+        } else {
+          // Set touch caret invisible if HisTest fails. Bypass event.
+          SetVisibility(false);
+          status = nsEventStatus_eIgnore;
+        }
+      }
+      break;
+
+    case TOUCHCARET_MOUSEDRAG_ACTIVE:
+    case TOUCHCARET_TOUCHDRAG_ACTIVE:
+    case TOUCHCARET_TOUCHDRAG_INACTIVE:
+      // Consume NS_TOUCH_START event.
+      status = nsEventStatus_eConsumeNoDefault;
+      break;
+  }
+
+  // Cache active touch IDs in TOUCHDRAG states.
+  if (mState == TOUCHCARET_TOUCHDRAG_ACTIVE ||
+      mState == TOUCHCARET_TOUCHDRAG_INACTIVE) {
+    mTouchesId.Clear();
+    for (size_t i = 0; i < aEvent->touches.Length(); i++) {
+      mTouchesId.AppendElement(aEvent->touches[i]->mIdentifier);
+    }
+  }
+
+  return status;
+}
+
+void
+TouchCaret::SetState(TouchCaretState aState)
+{
+  TOUCHCARET_LOG("%p state changed from %d to %d\n", this, mState, aState);
+  if (mState == TOUCHCARET_NONE) {
+    MOZ_ASSERT(aState != TOUCHCARET_TOUCHDRAG_INACTIVE,
+               "mState: NONE => TOUCHDRAG_INACTIVE isn't allowed!");
+  }
+
+  if (mState == TOUCHCARET_TOUCHDRAG_ACTIVE) {
+    MOZ_ASSERT(aState != TOUCHCARET_MOUSEDRAG_ACTIVE,
+               "mState: TOUCHDRAG_ACTIVE => MOUSEDRAG_ACTIVE isn't allowed!");
+  }
+
+  if (mState == TOUCHCARET_MOUSEDRAG_ACTIVE) {
+    MOZ_ASSERT(aState == TOUCHCARET_MOUSEDRAG_ACTIVE ||
+               aState == TOUCHCARET_NONE,
+               "MOUSEDRAG_ACTIVE allowed next state: NONE!");
+  }
+
+  if (mState == TOUCHCARET_TOUCHDRAG_INACTIVE) {
+    MOZ_ASSERT(aState == TOUCHCARET_TOUCHDRAG_INACTIVE ||
+               aState == TOUCHCARET_NONE,
+               "TOUCHDRAG_INACTIVE allowed next state: NONE!");
+  }
+
+  mState = aState;
+
+  if (mState == TOUCHCARET_NONE) {
+    mActiveTouchId = -1;
+    mCaretCenterToDownPointOffsetY = 0;
+  }
+}
new file mode 100644
--- /dev/null
+++ b/layout/base/TouchCaret.h
@@ -0,0 +1,240 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 sw=2 et tw=78: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_TouchCaret_h__
+#define mozilla_TouchCaret_h__
+
+#include "nsISelectionListener.h"
+#include "nsIScrollObserver.h"
+#include "nsIWeakReferenceUtils.h"
+#include "nsFrameSelection.h"
+#include "nsITimer.h"
+#include "mozilla/EventForwards.h"
+#include "mozilla/TouchEvents.h"
+#include "Units.h"
+
+namespace mozilla {
+
+/**
+ * The TouchCaret places a touch caret according to caret postion when the
+ * caret is shown.
+ * TouchCaret is also responsible for touch caret visibility. Touch caret
+ * won't be shown when timer expires or while key event causes selection change.
+ */
+class TouchCaret MOZ_FINAL : public nsISelectionListener
+{
+public:
+  explicit TouchCaret(nsIPresShell* aPresShell);
+  ~TouchCaret();
+
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSISELECTIONLISTENER
+
+  void Terminate()
+  {
+    mPresShell = nullptr;
+  }
+
+  /**
+   * Handle mouse and touch event only.
+   * Depends on visibility and position of touch caret, HandleEvent may consume
+   * that input event and return nsEventStatus_eConsumeNoDefault to the caller.
+   * In that case, caller should stop bubble up that input event.
+   */
+  nsEventStatus HandleEvent(WidgetEvent* aEvent);
+
+  /**
+   * By calling this function, touch caret recalculate touch frame position and
+   * update accordingly.
+   */
+  void UpdateTouchCaret(bool aVisible);
+
+  /**
+   * SetVisibility will set the visibility of the touch caret.
+   * SetVisibility performs an attribute-changed notification which could, in
+   * theory, destroy frames.
+   */
+  void SetVisibility(bool aVisible);
+
+  /**
+   * GetVisibility will get the visibility of the touch caret.
+   */
+  bool GetVisibility() const
+  {
+    return mVisible;
+  }
+
+private:
+  // Hide default constructor.
+  TouchCaret() MOZ_DELETE;
+
+  /**
+   * Find the nsCanvasFrame which holds the touch caret.
+   */
+  nsIFrame* GetCanvasFrame();
+
+  /**
+   * Retrieve the bounding rectangle of the touch caret.
+   *
+   * @returns A nsRect representing the bounding rectangle of this touch caret.
+   *          The returned offset is relative to the canvas frame.
+   */
+  nsRect GetTouchFrameRect();
+
+  /**
+   * Retrieve the bounding rectangle where the caret can be positioned.
+   * If we're positioning a caret in an input field, make sure the touch caret
+   * stays within the bounds of the field.
+   *
+   * @returns A nsRect representing the bounding rectangle of this valid area.
+   *          The returned offset is relative to the canvas frame.
+   */
+  nsRect GetContentBoundary();
+
+  /**
+   * Retrieve the center y position of the caret.
+   * The returned point is relative to the canvas frame.
+   */
+  nscoord GetCaretYCenterPosition();
+
+  /**
+   * Set the position of the touch caret.
+   * Touch caret is an absolute positioned div.
+   */
+  void SetTouchFramePos(const nsPoint& aOrigin);
+
+  void LaunchExpirationTimer();
+  void CancelExpirationTimer();
+  static void DisableTouchCaretCallback(nsITimer* aTimer, void* aPresShell);
+
+  /**
+   * Move the caret to movePoint which is relative to the canvas frame.
+   * Caret will be scrolled into view.
+   *
+   * @param movePoint tap location relative to the canvas frame.
+   */
+  void MoveCaret(const nsPoint& movePoint);
+
+  /**
+   * Check if aPoint is inside the touch caret frame.
+   *
+   * @param aPoint tap location relative to the canvas frame.
+   */
+  bool IsOnTouchCaret(const nsPoint& aPoint);
+
+  /**
+   * These Handle* functions comprise input alphabet of the TouchCaret
+   * finite-state machine triggering state transitions.
+   */
+  nsEventStatus HandleMouseMoveEvent(WidgetMouseEvent* aEvent);
+  nsEventStatus HandleMouseUpEvent(WidgetMouseEvent* aEvent);
+  nsEventStatus HandleMouseDownEvent(WidgetMouseEvent* aEvent);
+  nsEventStatus HandleTouchMoveEvent(WidgetTouchEvent* aEvent);
+  nsEventStatus HandleTouchUpEvent(WidgetTouchEvent* aEvent);
+  nsEventStatus HandleTouchDownEvent(WidgetTouchEvent* aEvent);
+
+  /**
+   * Get the coordinates of a given touch event, relative to canvas frame.
+   * @param aEvent the event
+   * @param aIdentifier the mIdentifier of the touch which is to be converted.
+   * @return the point, or (NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE) if
+   * for some reason the coordinates for the touch are not known (e.g.,
+   * the mIdentifier touch is not found).
+   */
+  nsPoint GetEventPosition(WidgetTouchEvent* aEvent, int32_t aIdentifier);
+
+  /**
+   * Get the coordinates of a given mouse event, relative to canvas frame.
+   * @param aEvent the event
+   * @return the point, or (NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE) if
+   * for some reason the coordinates for the mouse are not known.
+   */
+  nsPoint GetEventPosition(WidgetMouseEvent* aEvent);
+
+  /**
+   * States of TouchCaret finite-state machine.
+   */
+  enum TouchCaretState {
+    // In this state, either there is no touch/mouse event going on, or the
+    // first stroke does not hit the touch caret.
+    // Will enter TOUCHCARET_TOUCHDRAG_ACTIVE state if the first touch stroke
+    // hits the touch caret. Will enter TOUCHCARET_MOUSEDRAG_ACTIVE state if
+    // mouse (left button) down hits the touch caret.
+    // Allowed next state: TOUCHCARET_MOUSEDRAG_ACTIVE,
+    //                     TOUCHCARET_TOUCHDRAG_ACTIVE.
+    TOUCHCARET_NONE,
+    // The first (left button) mouse down hits on the touch caret and is
+    // alive. Will enter TOUCHCARET_NONE state if the left button is release.
+    // Allowed next states: TOUCHCARET_NONE.
+    TOUCHCARET_MOUSEDRAG_ACTIVE,
+    // The first touch start event hits on touch caret and is alive.
+    // Will enter TOUCHCARET_NONE state if the finger on touch caret is
+    // removed and there are no more fingers on the screen; will enter
+    // TOUCHCARET_TOUCHDRAG_INACTIVE state if the finger on touch caret is
+    // removed but still has fingers touching on the screen.
+    // Allowed next states: TOUCHCARET_NONE, TOUCHCARET_TOUCHDRAG_INACTIVE.
+    TOUCHCARET_TOUCHDRAG_ACTIVE,
+    // The first touch stroke, which hit on touch caret, is dead, but still has
+    // fingers touching on the screen.
+    // Will enter TOUCHCARET_NONE state if all the fingers are removed from the
+    // screen.
+    // Allowed next state: TOUCHCARET_NONE.
+    TOUCHCARET_TOUCHDRAG_INACTIVE,
+  };
+
+  /**
+   * Do actual state transition and reset substates.
+   */
+  void SetState(TouchCaretState aState);
+
+  /**
+   * Current state we're dealing with.
+   */
+  TouchCaretState mState;
+
+  /**
+   * Array containing all active touch IDs. When a touch happens, it gets added
+   * to this array, even if we choose not to handle it. When it ends, we remove
+   * it. We need to maintain this array in order to detect the end of the
+   * "multitouch" states because touch start events contain all current touches,
+   * but touch end events contain only those touches that have gone.
+   */
+  nsTArray<int32_t> mTouchesId;
+
+  /**
+   * The mIdentifier of the touch which is on the touch caret.
+   */
+  int32_t mActiveTouchId;
+
+  /**
+   * The offset between the tap location and the center of caret along y axis.
+   */
+  nscoord mCaretCenterToDownPointOffsetY;
+
+  static int32_t TouchCaretMaxDistance()
+  {
+    return sTouchCaretMaxDistance;
+  }
+
+  static int32_t TouchCaretExpirationTime()
+  {
+    return sTouchCaretExpirationTime;
+  }
+
+protected:
+  nsWeakPtr mPresShell;
+
+  // Touch caret visibility
+  bool mVisible;
+  // Touch caret timer
+  nsCOMPtr<nsITimer> mTouchCaretExpirationTimer;
+
+  // Preference
+  static int32_t sTouchCaretMaxDistance;
+  static int32_t sTouchCaretExpirationTime;
+};
+} //namespace mozilla
+#endif //mozilla_TouchCaret_h__
--- a/layout/base/moz.build
+++ b/layout/base/moz.build
@@ -86,16 +86,17 @@ UNIFIED_SOURCES += [
     'nsQuoteList.cpp',
     'nsStyleChangeList.cpp',
     'nsStyleSheetService.cpp',
     'PaintTracker.cpp',
     'PositionedEventTargeting.cpp',
     'RestyleManager.cpp',
     'RestyleTracker.cpp',
     'StackArena.cpp',
+    'TouchCaret.cpp',
 ]
 
 # nsDocumentViewer.cpp and nsPresShell.cpp need to be built separately
 # because they force NSPR logging.
 # nsPresArena.cpp needs to be built separately because it uses plarena.h.
 # nsRefreshDriver.cpp needs to be built separately because of name clashes in the OS X headers
 SOURCES += [
     'nsDocumentViewer.cpp',
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -24,16 +24,17 @@
 #include "nsPresContext.h"
 #include "nsIDocument.h"
 #include "nsTableFrame.h"
 #include "nsTableColFrame.h"
 #include "nsTableRowFrame.h"
 #include "nsTableCellFrame.h"
 #include "nsIDOMHTMLDocument.h"
 #include "nsHTMLParts.h"
+#include "nsPresShell.h"
 #include "nsIPresShell.h"
 #include "nsUnicharUtils.h"
 #include "nsStyleSet.h"
 #include "nsViewManager.h"
 #include "nsStyleConsts.h"
 #include "nsIDOMXULElement.h"
 #include "nsContainerFrame.h"
 #include "nsNameSpaceManager.h"
@@ -2592,16 +2593,22 @@ nsCSSFrameConstructor::ConstructDocEleme
     contentFrame->SetInitialChildList(kPrincipalList, childItems);
   }
 
   // set the primary frame
   aDocElement->SetPrimaryFrame(contentFrame);
 
   SetInitialSingleChild(mDocElementContainingBlock, newFrame);
 
+  // Create touch caret frame if there is a canvas frame
+  if (mDocElementContainingBlock->GetType() == nsGkAtoms::canvasFrame) {
+    ConstructAnonymousContentForCanvas(state, mDocElementContainingBlock,
+                                       aDocElement);
+  }
+
   return newFrame;
 }
 
 
 nsIFrame*
 nsCSSFrameConstructor::ConstructRootFrame()
 {
   AUTO_LAYOUT_PHASE_ENTRY_POINT(mPresShell->GetPresContext(), FrameC);
@@ -2847,16 +2854,39 @@ nsCSSFrameConstructor::SetUpDocElementCo
   if (viewportFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW) {
     SetInitialSingleChild(viewportFrame, newFrame);
   } else {
     nsFrameList newFrameList(newFrame, newFrame);
     viewportFrame->AppendFrames(kPrincipalList, newFrameList);
   }
 }
 
+void
+nsCSSFrameConstructor::ConstructAnonymousContentForCanvas(nsFrameConstructorState& aState,
+                                                          nsIFrame* aFrame,
+                                                          nsIContent* aDocElement)
+{
+  NS_ASSERTION(aFrame->GetType() == nsGkAtoms::canvasFrame, "aFrame should be canvas frame!");
+
+  nsAutoTArray<nsIAnonymousContentCreator::ContentInfo, 4> anonymousItems;
+  GetAnonymousContent(aDocElement, aFrame, anonymousItems);
+  if (anonymousItems.IsEmpty()) {
+    // Touch caret is not enabled.
+    return;
+  }
+
+  FrameConstructionItemList itemsToConstruct;
+  nsContainerFrame* frameAsContainer = do_QueryFrame(aFrame);
+  AddFCItemsForAnonymousContent(aState, frameAsContainer, anonymousItems, itemsToConstruct);
+
+  nsFrameItems frameItems;
+  ConstructFramesFromItemList(aState, itemsToConstruct, frameAsContainer, frameItems);
+  frameAsContainer->AppendFrames(kPrincipalList, frameItems);
+}
+
 nsContainerFrame*
 nsCSSFrameConstructor::ConstructPageFrame(nsIPresShell*  aPresShell,
                                           nsPresContext* aPresContext,
                                           nsContainerFrame* aParentFrame,
                                           nsIFrame*      aPrevPageFrame,
                                           nsContainerFrame*& aCanvasFrame)
 {
   nsStyleContext* parentStyleContext = aParentFrame->StyleContext();
--- a/layout/base/nsCSSFrameConstructor.h
+++ b/layout/base/nsCSSFrameConstructor.h
@@ -1764,16 +1764,20 @@ private:
    * style contexts in the undisplayed content map must be non-pseudo
    * contexts and also handles unbinding undisplayed generated content
    * as needed.
    */
   static void SetAsUndisplayedContent(FrameConstructionItemList& aList,
                                       nsIContent* aContent,
                                       nsStyleContext* aStyleContext,
                                       bool aIsGeneratedContent);
+  // Create touch caret frame.
+  void ConstructAnonymousContentForCanvas(nsFrameConstructorState& aState,
+                                          nsIFrame* aFrame,
+                                          nsIContent* aDocElement);
 
 public:
 
   friend class nsFrameConstructorState;
 
 private:
 
   nsIDocument*        mDocument;  // Weak ref
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -714,16 +714,17 @@ static void RecordFrameMetrics(nsIFrame*
     nsIDocument* document = nullptr;
     document = presShell->GetDocument();
     if (document) {
       nsCOMPtr<nsPIDOMWindow> innerWin(document->GetInnerWindow());
       if (innerWin) {
         metrics.mMayHaveTouchListeners = innerWin->HasTouchEventListeners();
       }
     }
+    metrics.mMayHaveTouchCaret = presShell->MayHaveTouchCaret();
   }
 
   LayoutDeviceToParentLayerScale layoutToParentLayerScale =
     // The ScreenToParentLayerScale should be mTransformScale which is not calculated yet,
     // but we don't yet handle CSS transforms, so we assume it's 1 here.
     metrics.mCumulativeResolution * LayerToScreenScale(1.0) * ScreenToParentLayerScale(1.0);
 
   // Calculate the composition bounds as the size of the scroll frame and
--- a/layout/base/nsIPresShell.h
+++ b/layout/base/nsIPresShell.h
@@ -48,18 +48,22 @@ class nsDocShell;
 class nsIDocument;
 class nsIFrame;
 class nsPresContext;
 class nsStyleSet;
 class nsViewManager;
 class nsView;
 class nsRenderingContext;
 class nsIPageSequenceFrame;
+class nsCanvasFrame;
 class nsAString;
 class nsCaret;
+namespace mozilla {
+class TouchCaret;
+} // namespace mozilla
 class nsFrameSelection;
 class nsFrameManager;
 class nsILayoutHistoryState;
 class nsIReflowCallback;
 class nsIDOMNode;
 class nsIntRegion;
 class nsIStyleSheet;
 class nsCSSFrameConstructor;
@@ -127,20 +131,20 @@ typedef struct CapturingContentInfo {
   // capture should only be allowed during a mousedown event
   bool mAllowed;
   bool mPointerLock;
   bool mRetargetToElement;
   bool mPreventDrag;
   nsIContent* mContent;
 } CapturingContentInfo;
 
-//61e60df7-128a-4cdd-a684-5f0bd2ceb61f
+//a4e5ff3a-dc5c-4b3a-a625-d164a9e50619
 #define NS_IPRESSHELL_IID \
-{ 0x61e60df7, 0x128a, 0x4cdd, \
-  {0xa6, 0x84, 0x5f, 0x0b, 0xd2, 0xce, 0xb6, 0x1f}}
+{ 0xa4e5ff3a, 0xdc5c, 0x4b3a, \
+  {0xa6, 0x25, 0xd1, 0x64, 0xa9, 0xe5, 0x06, 0x19}}
 
 // debug VerifyReflow flags
 #define VERIFY_REFLOW_ON                    0x01
 #define VERIFY_REFLOW_NOISY                 0x02
 #define VERIFY_REFLOW_ALL                   0x04
 #define VERIFY_REFLOW_DUMP_COMMANDS         0x08
 #define VERIFY_REFLOW_NOISY_RC              0x10
 #define VERIFY_REFLOW_REALLY_NOISY_RC       0x20
@@ -463,16 +467,22 @@ public:
 
   /**
    * Returns the page sequence frame associated with the frame hierarchy.
    * Returns nullptr if not a paginated view.
    */
   virtual nsIPageSequenceFrame* GetPageSequenceFrame() const = 0;
 
   /**
+  * Returns the canvas frame associated with the frame hierarchy.
+  * Returns nullptr if is XUL document.
+  */
+  virtual nsCanvasFrame* GetCanvasFrame() const = 0;
+
+  /**
    * Gets the real primary frame associated with the content object.
    *
    * In the case of absolutely positioned elements and floated elements,
    * the real primary frame is the frame that is out of the flow and not the
    * placeholder frame.
    */
   virtual nsIFrame* GetRealPrimaryFrameFor(nsIContent* aContent) const = 0;
 
@@ -731,16 +741,37 @@ public:
   /**
    * Notification sent by a frame informing the pres shell that it is about to
    * be destroyed.
    * This allows any outstanding references to the frame to be cleaned up
    */
   virtual void NotifyDestroyingFrame(nsIFrame* aFrame) = 0;
 
   /**
+   * Get the touch caret, if it exists. AddRefs it.
+   */
+  virtual already_AddRefed<mozilla::TouchCaret> GetTouchCaret() const = 0;
+
+  /**
+   * Returns the touch caret element of the presshell.
+   */
+  virtual mozilla::dom::Element* GetTouchCaretElement() const = 0;
+
+  /**
+   * Will be called when touch caret visibility has changed.
+   * Set the mMayHaveTouchCaret flag to aSet.
+   */
+  virtual void SetMayHaveTouchCaret(bool aSet) = 0;
+
+  /**
+   * Get the mMayHaveTouchCaret flag.
+   */
+  virtual bool MayHaveTouchCaret() = 0;
+
+  /**
    * Get the caret, if it exists. AddRefs it.
    */
   virtual already_AddRefed<nsCaret> GetCaret() const = 0;
 
   /**
    * Invalidate the caret's current position if it's outside of its frame's
    * boundaries. This function is useful if you're batching selection
    * notifications and might remove the caret's frame out from under it.
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -2225,16 +2225,76 @@ nsLayoutUtils::TransformPoints(nsIFrame*
     // Divide here so that when the devPixelsPerCSSPixels are the same, we get the correct
     // answer instead of some inaccuracy multiplying a number by its reciprocal.
     aPoints[i] = LayoutDevicePoint(toDevPixels.x, toDevPixels.y) /
         devPixelsPerCSSPixelToFrame;
   }
   return TRANSFORM_SUCCEEDED;
 }
 
+nsLayoutUtils::TransformResult
+nsLayoutUtils::TransformPoint(nsIFrame* aFromFrame, nsIFrame* aToFrame,
+                              nsPoint& aPoint)
+{
+  nsIFrame* nearestCommonAncestor = FindNearestCommonAncestorFrame(aFromFrame, aToFrame);
+  if (!nearestCommonAncestor) {
+    return NO_COMMON_ANCESTOR;
+  }
+  gfx3DMatrix downToDest = GetTransformToAncestor(aToFrame, nearestCommonAncestor);
+  if (downToDest.IsSingular()) {
+    return NONINVERTIBLE_TRANSFORM;
+  }
+  downToDest.Invert();
+  gfx3DMatrix upToAncestor = GetTransformToAncestor(aFromFrame, nearestCommonAncestor);
+
+  float devPixelsPerAppUnitFromFrame =
+    1.0f / aFromFrame->PresContext()->AppUnitsPerDevPixel();
+  float devPixelsPerAppUnitToFrame =
+    1.0f / aToFrame->PresContext()->AppUnitsPerDevPixel();
+  gfxPoint toDevPixels = downToDest.ProjectPoint(
+      upToAncestor.ProjectPoint(
+        gfxPoint(aPoint.x * devPixelsPerAppUnitFromFrame,
+                 aPoint.y * devPixelsPerAppUnitFromFrame)));
+  aPoint.x = toDevPixels.x / devPixelsPerAppUnitToFrame;
+  aPoint.y = toDevPixels.y / devPixelsPerAppUnitToFrame;
+  return TRANSFORM_SUCCEEDED;
+}
+
+nsLayoutUtils::TransformResult
+nsLayoutUtils::TransformRect(nsIFrame* aFromFrame, nsIFrame* aToFrame,
+                             nsRect& aRect)
+{
+  nsIFrame* nearestCommonAncestor = FindNearestCommonAncestorFrame(aFromFrame, aToFrame);
+  if (!nearestCommonAncestor) {
+    return NO_COMMON_ANCESTOR;
+  }
+  gfx3DMatrix downToDest = GetTransformToAncestor(aToFrame, nearestCommonAncestor);
+  if (downToDest.IsSingular()) {
+    return NONINVERTIBLE_TRANSFORM;
+  }
+  downToDest.Invert();
+  gfx3DMatrix upToAncestor = GetTransformToAncestor(aFromFrame, nearestCommonAncestor);
+
+  float devPixelsPerAppUnitFromFrame =
+    1.0f / aFromFrame->PresContext()->AppUnitsPerDevPixel();
+  float devPixelsPerAppUnitToFrame =
+    1.0f / aToFrame->PresContext()->AppUnitsPerDevPixel();
+  gfxRect toDevPixels = downToDest.ProjectRectBounds(
+    upToAncestor.ProjectRectBounds(
+      gfxRect(aRect.x * devPixelsPerAppUnitFromFrame,
+              aRect.y * devPixelsPerAppUnitFromFrame,
+              aRect.width * devPixelsPerAppUnitFromFrame,
+              aRect.height * devPixelsPerAppUnitFromFrame)));
+  aRect.x = toDevPixels.x / devPixelsPerAppUnitToFrame;
+  aRect.y = toDevPixels.y / devPixelsPerAppUnitToFrame;
+  aRect.width = toDevPixels.width / devPixelsPerAppUnitToFrame;
+  aRect.height = toDevPixels.height / devPixelsPerAppUnitToFrame;
+  return TRANSFORM_SUCCEEDED;
+}
+
 bool
 nsLayoutUtils::GetLayerTransformForFrame(nsIFrame* aFrame,
                                          gfx3DMatrix* aTransform)
 {
   // FIXME/bug 796690: we can sometimes compute a transform in these
   // cases, it just increases complexity considerably.  Punt for now.
   if (aFrame->Preserves3DChildren() || aFrame->HasTransformGetter()) {
     return false;
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -753,16 +753,31 @@ public:
     TRANSFORM_SUCCEEDED,
     NO_COMMON_ANCESTOR,
     NONINVERTIBLE_TRANSFORM
   };
   static TransformResult TransformPoints(nsIFrame* aFromFrame, nsIFrame* aToFrame,
                                          uint32_t aPointCount, CSSPoint* aPoints);
 
   /**
+   * Same as above function, but transform points in app units and
+   * handle 1 point per call.
+   */
+  static TransformResult TransformPoint(nsIFrame* aFromFrame, nsIFrame* aToFrame,
+                                        nsPoint& aPoint);
+
+  /**
+   * Transforms a rect from aFromFrame to aToFrame. In app units.
+   * Returns the bounds of the actual rect if the transform requires rotation
+   * or anything complex like that.
+   */
+  static TransformResult TransformRect(nsIFrame* aFromFrame, nsIFrame* aToFrame,
+                                       nsRect& aRect);
+
+  /**
    * Return true if a "layer transform" could be computed for aFrame,
    * and optionally return the computed transform.  The returned
    * transform is what would be set on the layer currently if a layers
    * transaction were opened at the time this helper is called.
    */
   static bool GetLayerTransformForFrame(nsIFrame* aFrame,
                                         gfx3DMatrix* aTransform);
 
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -70,16 +70,17 @@
 #include "nsIDOMNodeList.h"
 #include "nsIDOMElement.h"
 #include "nsRange.h"
 #include "nsCOMPtr.h"
 #include "nsAutoPtr.h"
 #include "nsReadableUtils.h"
 #include "nsIPageSequenceFrame.h"
 #include "nsCaret.h"
+#include "TouchCaret.h"
 #include "nsIDOMHTMLDocument.h"
 #include "nsFrameManager.h"
 #include "nsXPCOM.h"
 #include "nsILayoutHistoryState.h"
 #include "nsILineIterator.h" // for ScrollContentIntoView
 #include "pldhash.h"
 #include "mozilla/dom/Touch.h"
 #include "mozilla/dom/PointerEventBinding.h"
@@ -696,16 +697,28 @@ nsIPresShell::FrameSelection()
   return ret.forget();
 }
 
 //----------------------------------------------------------------------
 
 static bool sSynthMouseMove = true;
 static uint32_t sNextPresShellId;
 static bool sPointerEventEnabled = true;
+static bool sTouchCaretEnabled = false;
+
+/* static */ bool
+PresShell::TouchCaretPrefEnabled()
+{
+  static bool initialized = false;
+  if (!initialized) {
+    Preferences::AddBoolVarCache(&sTouchCaretEnabled, "touchcaret.enabled");
+    initialized = true;
+  }
+  return sTouchCaretEnabled;
+}
 
 PresShell::PresShell()
   : mMouseLocation(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE)
 {
   mSelection = nullptr;
 #ifdef MOZ_REFLOW_PERF
   mReflowCountMgr = new ReflowCountMgr();
   mReflowCountMgr->SetPresContext(mPresContext);
@@ -846,16 +859,21 @@ PresShell::Init(nsIDocument* aDocument,
   // this MUST happen after we set up our style set but before we create any
   // frames.
   mPresContext->CompatibilityModeChanged();
 
   // setup the preference style rules (no forced reflow), and do it
   // before creating any frames.
   SetPreferenceStyleRules(false);
 
+  if (TouchCaretPrefEnabled()) {
+    // Create touch caret handle
+    mTouchCaret = new TouchCaret(this);
+  }
+
   NS_ADDREF(mSelection = new nsFrameSelection());
 
   mSelection->Init(this, nullptr);
 
   // Important: this has to happen after the selection has been set up
 #ifdef SHOW_CARET
   // make the caret
   mCaret = new nsCaret();
@@ -1102,16 +1120,21 @@ PresShell::Destroy()
     mCaret->Terminate();
     mCaret = nullptr;
   }
 
   if (mSelection) {
     mSelection->DisconnectFromPresShell();
   }
 
+  if (mTouchCaret) {
+    mTouchCaret->Terminate();
+    mTouchCaret = nullptr;
+  }
+
   // release our pref style sheet, if we have one still
   ClearPreferenceStyleRules();
 
   mIsDestroying = true;
 
   // We can't release all the event content in
   // mCurrentEventContentStack here since there might be code on the
   // stack that will release the event content too. Double release
@@ -2129,16 +2152,23 @@ PresShell::NotifyDestroyingFrame(nsIFram
 }
 
 already_AddRefed<nsCaret> PresShell::GetCaret() const
 {
   nsRefPtr<nsCaret> caret = mCaret;
   return caret.forget();
 }
 
+// TouchCaret
+already_AddRefed<TouchCaret> PresShell::GetTouchCaret() const
+{
+  nsRefPtr<TouchCaret> touchCaret = mTouchCaret;
+  return touchCaret.forget();
+}
+
 void PresShell::MaybeInvalidateCaretPosition()
 {
   if (mCaret) {
     mCaret->InvalidateOutsideCaret();
   }
 }
 
 void PresShell::SetCaret(nsCaret *aNewCaret)
@@ -2152,24 +2182,31 @@ void PresShell::RestoreCaret()
 }
 
 NS_IMETHODIMP PresShell::SetCaretEnabled(bool aInEnable)
 {
   bool oldEnabled = mCaretEnabled;
 
   mCaretEnabled = aInEnable;
 
-  if (mCaret && (mCaretEnabled != oldEnabled))
+  if (mCaretEnabled != oldEnabled)
   {
 /*  Don't change the caret's selection here! This was an evil side-effect of SetCaretEnabled()
     nsCOMPtr<nsIDOMSelection> domSel;
     if (NS_SUCCEEDED(GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(domSel))) && domSel)
       mCaret->SetCaretDOMSelection(domSel);
 */
-    mCaret->SetCaretVisible(mCaretEnabled);
+
+    MOZ_ASSERT(mCaret || mTouchCaret);
+    if (mCaret) {
+      mCaret->SetCaretVisible(mCaretEnabled);
+    }
+    if (mTouchCaret) {
+      mTouchCaret->UpdateTouchCaret(mCaretEnabled);
+    }
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP PresShell::SetCaretReadOnly(bool aReadOnly)
 {
   if (mCaret)
@@ -2467,16 +2504,75 @@ nsIPresShell::GetRootScrollFrameAsScroll
 
 nsIPageSequenceFrame*
 PresShell::GetPageSequenceFrame() const
 {
   nsIFrame* frame = mFrameConstructor->GetPageSequenceFrame();
   return do_QueryFrame(frame);
 }
 
+nsCanvasFrame*
+PresShell::GetCanvasFrame() const
+{
+  nsIFrame* frame = mFrameConstructor->GetDocElementContainingBlock();
+  return do_QueryFrame(frame);
+}
+
+Element*
+PresShell::GetTouchCaretElement() const
+{
+  return GetCanvasFrame() ? GetCanvasFrame()->GetTouchCaretElement() : nullptr;
+}
+
+void
+PresShell::SetMayHaveTouchCaret(bool aSet)
+{
+  if (!mPresContext) {
+    return;
+  }
+
+  if (!mPresContext->IsRoot()) {
+    nsIPresShell* rootPresShell = GetRootPresShell();
+    if (rootPresShell) {
+      rootPresShell->SetMayHaveTouchCaret(aSet);
+    }
+    return;
+  }
+
+  nsIDocument* document = GetDocument();
+  if (document) {
+    nsPIDOMWindow* innerWin = document->GetInnerWindow();
+    if (innerWin) {
+      innerWin->SetMayHaveTouchCaret(aSet);
+    }
+  }
+}
+
+bool
+PresShell::MayHaveTouchCaret()
+{
+  if (!mPresContext) {
+    return false;
+  }
+
+  if (!mPresContext->IsRoot()) {
+    nsIPresShell* rootPresShell = GetRootPresShell();
+    return rootPresShell ? rootPresShell->MayHaveTouchCaret() : false;
+  }
+
+  nsIDocument* document = GetDocument();
+  if (document) {
+    nsPIDOMWindow* innerWin = document->GetInnerWindow();
+    if (innerWin) {
+      return innerWin->MayHaveTouchCaret();
+    }
+  }
+  return false;
+}
+
 void
 PresShell::BeginUpdate(nsIDocument *aDocument, nsUpdateType aUpdateType)
 {
 #ifdef DEBUG
   mUpdateCount++;
 #endif
   mFrameConstructor->BeginUpdate();
 
@@ -6531,16 +6627,39 @@ PresShell::HandleEvent(nsIFrame* aFrame,
 
   if (mIsDestroying ||
       (sDisableNonTestMouseEvents && !aEvent->mFlags.mIsSynthesizedForTests &&
        aEvent->HasMouseEventMessage())) {
     return NS_OK;
   }
 
   RecordMouseLocation(aEvent);
+
+  // Determine whether event need to be consumed by touch caret or not.
+  if (TouchCaretPrefEnabled()) {
+    // We have to target the focus window because regardless of where the
+    // touch goes, we want to access the touch caret when user is typing on an
+    // editable element.
+    nsCOMPtr<nsPIDOMWindow> window = GetFocusedDOMWindowInOurWindow();
+    nsCOMPtr<nsIDocument> retargetEventDoc = window ? window->GetExtantDoc() : nullptr;
+    nsCOMPtr<nsIPresShell> presShell = retargetEventDoc ?
+                                       retargetEventDoc->GetShell() :
+                                       nullptr;
+    nsRefPtr<TouchCaret> touchCaret = presShell ? presShell->GetTouchCaret() : nullptr;
+    if (touchCaret) {
+      *aEventStatus = touchCaret->HandleEvent(aEvent);
+      if (*aEventStatus == nsEventStatus_eConsumeNoDefault) {
+        // If the event is consumed by the touch caret, cancel APZC panning by
+        // setting mMultipleActionsPrevented.
+        aEvent->mFlags.mMultipleActionsPrevented = true;
+        return NS_OK;
+      }
+    }
+  }
+
   if (sPointerEventEnabled) {
     UpdateActivePointerState(aEvent);
   }
 
   if (!nsContentUtils::IsSafeToRunScript())
     return NS_OK;
 
   nsIContent* capturingContent =
@@ -8105,17 +8224,17 @@ PresShell::Freeze()
 {
   mUpdateImageVisibilityEvent.Revoke();
 
   MaybeReleaseCapturingContent();
 
   mDocument->EnumerateFreezableElements(FreezeElement, nullptr);
 
   if (mCaret) {
-    mCaret->SetCaretVisible(false);
+    SetCaretEnabled(false);
   }
 
   mPaintingSuppressed = true;
 
   if (mDocument) {
     mDocument->EnumerateSubDocuments(FreezeSubDocument, nullptr);
   }
 
--- a/layout/base/nsPresShell.h
+++ b/layout/base/nsPresShell.h
@@ -62,16 +62,19 @@ class PresShell : public nsIPresShell,
 public:
   PresShell();
 
   NS_DECL_AND_IMPL_ZEROING_OPERATOR_NEW
 
   // nsISupports
   NS_DECL_ISUPPORTS
 
+  // Touch caret preference
+  static bool TouchCaretPrefEnabled();
+
   void Init(nsIDocument* aDocument, nsPresContext* aPresContext,
             nsViewManager* aViewManager, nsStyleSet* aStyleSet,
             nsCompatibility aCompatMode);
   virtual void Destroy() MOZ_OVERRIDE;
   virtual void MakeZombie() MOZ_OVERRIDE;
 
   virtual nsresult SetPreferenceStyleRules(bool aForceReflow) MOZ_OVERRIDE;
 
@@ -85,16 +88,17 @@ public:
   NS_IMETHOD RepaintSelection(SelectionType aType) MOZ_OVERRIDE;
 
   virtual void BeginObservingDocument() MOZ_OVERRIDE;
   virtual void EndObservingDocument() MOZ_OVERRIDE;
   virtual nsresult Initialize(nscoord aWidth, nscoord aHeight) MOZ_OVERRIDE;
   virtual nsresult ResizeReflow(nscoord aWidth, nscoord aHeight) MOZ_OVERRIDE;
   virtual nsresult ResizeReflowOverride(nscoord aWidth, nscoord aHeight) MOZ_OVERRIDE;
   virtual nsIPageSequenceFrame* GetPageSequenceFrame() const MOZ_OVERRIDE;
+  virtual nsCanvasFrame* GetCanvasFrame() const MOZ_OVERRIDE;
   virtual nsIFrame* GetRealPrimaryFrameFor(nsIContent* aContent) const MOZ_OVERRIDE;
 
   virtual nsIFrame* GetPlaceholderFrameFor(nsIFrame* aFrame) const MOZ_OVERRIDE;
   virtual void FrameNeedsReflow(nsIFrame *aFrame, IntrinsicDirty aIntrinsicDirty,
                                             nsFrameState aBitToAdd) MOZ_OVERRIDE;
   virtual void FrameNeedsToContinueReflow(nsIFrame *aFrame) MOZ_OVERRIDE;
   virtual void CancelAllPendingReflows() MOZ_OVERRIDE;
   virtual bool IsSafeToFlush() const MOZ_OVERRIDE;
@@ -202,16 +206,21 @@ public:
   virtual void WillPaintWindow() MOZ_OVERRIDE;
   virtual void DidPaintWindow() MOZ_OVERRIDE;
   virtual void ScheduleViewManagerFlush(PaintType aType = PAINT_DEFAULT) MOZ_OVERRIDE;
   virtual void DispatchSynthMouseMove(mozilla::WidgetGUIEvent* aEvent,
                                       bool aFlushOnHoverChange) MOZ_OVERRIDE;
   virtual void ClearMouseCaptureOnView(nsView* aView) MOZ_OVERRIDE;
   virtual bool IsVisible() MOZ_OVERRIDE;
 
+  // touch caret
+  virtual already_AddRefed<mozilla::TouchCaret> GetTouchCaret() const MOZ_OVERRIDE;
+  virtual mozilla::dom::Element* GetTouchCaretElement() const MOZ_OVERRIDE;
+  virtual void SetMayHaveTouchCaret(bool aSet) MOZ_OVERRIDE;
+  virtual bool MayHaveTouchCaret() MOZ_OVERRIDE;
   // caret handling
   virtual already_AddRefed<nsCaret> GetCaret() const MOZ_OVERRIDE;
   virtual void MaybeInvalidateCaretPosition() MOZ_OVERRIDE;
   NS_IMETHOD SetCaretEnabled(bool aInEnable) MOZ_OVERRIDE;
   NS_IMETHOD SetCaretReadOnly(bool aReadOnly) MOZ_OVERRIDE;
   NS_IMETHOD GetCaretEnabled(bool *aOutEnabled) MOZ_OVERRIDE;
   NS_IMETHOD SetCaretVisibilityDuringSelection(bool aVisibility) MOZ_OVERRIDE;
   NS_IMETHOD GetCaretVisible(bool *_retval) MOZ_OVERRIDE;
@@ -755,16 +764,19 @@ private:
 protected:
   nsRevocableEventPtr<nsSynthMouseMoveEvent> mSynthMouseMoveEvent;
   nsCOMPtr<nsIContent>      mLastAnchorScrolledTo;
   nsRefPtr<nsCaret>         mCaret;
   nsRefPtr<nsCaret>         mOriginalCaret;
   nsCallbackEventRequest*   mFirstCallbackEventRequest;
   nsCallbackEventRequest*   mLastCallbackEventRequest;
 
+  // TouchCaret
+  nsRefPtr<mozilla::TouchCaret> mTouchCaret;
+
   // This timer controls painting suppression.  Until it fires
   // or all frames are constructed, we won't paint anything but
   // our <body> background and scrollbars.
   nsCOMPtr<nsITimer>        mPaintSuppressionTimer;
 
   // At least on Win32 and Mac after interupting a reflow we need to post
   // the resume reflow event off a timer to avoid event starvation because
   // posted messages are processed before other messages when the modal
new file mode 100644
--- /dev/null
+++ b/layout/generic/crashtests/1015563-1.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<html style="display: inline-flex;">
+<body style="margin: -3642924795px; flex-grow: 1;"></body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/generic/crashtests/1015563-2.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<html>
+  <div style="display: flex">
+    <div style="margin: -3642924795px; flex-grow: 1;"></div>
+  </div>
+</body>
+</html>
--- a/layout/generic/crashtests/crashtests.list
+++ b/layout/generic/crashtests/crashtests.list
@@ -526,9 +526,11 @@ load 943509-1.html
 asserts(3-6) load 944909-1.html
 test-pref(layout.css.sticky.enabled,true) load 949932.html
 load 973701-1.xhtml
 load 973701-2.xhtml
 load 986899.html
 load 1001233.html
 load 1001258-1.html
 pref(layout.css.grid.enabled,true) load 1015562.html
+asserts(2) load 1015563-1.html
+asserts(2) load 1015563-2.html
 load outline-on-frameset.xhtml
--- a/layout/generic/nsCanvasFrame.cpp
+++ b/layout/generic/nsCanvasFrame.cpp
@@ -7,22 +7,27 @@
 
 #include "nsCanvasFrame.h"
 #include "nsContainerFrame.h"
 #include "nsCSSRendering.h"
 #include "nsPresContext.h"
 #include "nsStyleContext.h"
 #include "nsRenderingContext.h"
 #include "nsGkAtoms.h"
+#include "nsPresShell.h"
 #include "nsIPresShell.h"
 #include "nsDisplayList.h"
 #include "nsCSSFrameConstructor.h"
 #include "nsFrameManager.h"
 #include "gfxPlatform.h"
-
+// for touchcaret
+#include "nsContentList.h"
+#include "nsContentCreatorFunctions.h"
+#include "nsContentUtils.h"
+#include "nsStyleSet.h"
 // for focus
 #include "nsIScrollableFrame.h"
 #ifdef DEBUG_CANVAS_FOCUS
 #include "nsIDocShell.h"
 #endif
 
 //#define DEBUG_CANVAS_FOCUS
 
@@ -35,27 +40,73 @@ NS_NewCanvasFrame(nsIPresShell* aPresShe
 {
   return new (aPresShell) nsCanvasFrame(aContext);
 }
 
 NS_IMPL_FRAMEARENA_HELPERS(nsCanvasFrame)
 
 NS_QUERYFRAME_HEAD(nsCanvasFrame)
   NS_QUERYFRAME_ENTRY(nsCanvasFrame)
+  NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator)
 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
 
+nsresult
+nsCanvasFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements)
+{
+  // We won't create touch caret element if preference is not enabled.
+  if (!PresShell::TouchCaretPrefEnabled()) {
+    return NS_OK;
+  }
+
+  nsCOMPtr<nsIDocument> doc = mContent->OwnerDoc();
+  nsCOMPtr<nsINodeInfo> nodeInfo;
+
+  // Create and append touch caret frame.
+  nodeInfo = doc->NodeInfoManager()->GetNodeInfo(nsGkAtoms::div, nullptr,
+                                                 kNameSpaceID_XHTML,
+                                                 nsIDOMNode::ELEMENT_NODE);
+  NS_ENSURE_TRUE(nodeInfo, NS_ERROR_OUT_OF_MEMORY);
+
+  nsresult rv = NS_NewHTMLElement(getter_AddRefs(mTouchCaretElement), nodeInfo.forget(),
+                                mozilla::dom::NOT_FROM_PARSER);
+  NS_ENSURE_SUCCESS(rv, rv);
+  aElements.AppendElement(mTouchCaretElement);
+
+  // Add a _moz_anonclass attribute as touch caret selector.
+  ErrorResult er;
+  mTouchCaretElement->SetAttribute(NS_LITERAL_STRING("_moz_anonclass"),
+                                   NS_LITERAL_STRING("mozTouchCaret"), er);
+  NS_ENSURE_SUCCESS(er.ErrorCode(), er.ErrorCode());
+
+  // Set touch caret to visibility: hidden by default.
+  nsAutoString classValue;
+  classValue.AppendLiteral("moz-touchcaret hidden");
+  rv = mTouchCaretElement->SetAttr(kNameSpaceID_None, nsGkAtoms::_class,
+                                   classValue, true);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
+void
+nsCanvasFrame::AppendAnonymousContentTo(nsBaseContentList& aElements, uint32_t aFilter)
+{
+  aElements.MaybeAppendElement(mTouchCaretElement);
+}
+
 void
 nsCanvasFrame::DestroyFrom(nsIFrame* aDestructRoot)
 {
   nsIScrollableFrame* sf =
     PresContext()->GetPresShell()->GetRootScrollFrameAsScrollable();
   if (sf) {
     sf->RemoveScrollPositionListener(this);
   }
 
+  nsContentUtils::DestroyAnonymousContent(&mTouchCaretElement);
   nsContainerFrame::DestroyFrom(aDestructRoot);
 }
 
 void
 nsCanvasFrame::ScrollPositionWillChange(nscoord aX, nscoord aY)
 {
   if (mDoPaintFocus) {
     mDoPaintFocus = false;
@@ -93,19 +144,24 @@ nsCanvasFrame::SetInitialChildList(Child
   nsContainerFrame::SetInitialChildList(aListID, aChildList);
 }
 
 void
 nsCanvasFrame::AppendFrames(ChildListID     aListID,
                             nsFrameList&    aFrameList)
 {
   MOZ_ASSERT(aListID == kPrincipalList, "unexpected child list");
-  MOZ_ASSERT(mFrames.IsEmpty(), "already have a child frame");
-  MOZ_ASSERT(aFrameList.FirstChild() == aFrameList.LastChild(),
-             "Only one principal child frame allowed");
+  if (!mFrames.IsEmpty()) {
+    for (nsFrameList::Enumerator e(aFrameList); !e.AtEnd(); e.Next()) {
+      // We only allow native anonymous child frame for touch caret,
+      // which its placeholder is added to the Principal child lists.
+      MOZ_ASSERT(e.get()->GetContent()->IsInNativeAnonymousSubtree(),
+                 "invalid child list");
+    }
+  }
   nsFrame::VerifyDirtyBitSet(aFrameList);
   nsContainerFrame::AppendFrames(aListID, aFrameList);
 }
 
 void
 nsCanvasFrame::InsertFrames(ChildListID     aListID,
                             nsIFrame*       aPrevFrame,
                             nsFrameList&    aFrameList)
@@ -116,17 +172,16 @@ nsCanvasFrame::InsertFrames(ChildListID 
   AppendFrames(aListID, aFrameList);
 }
 
 void
 nsCanvasFrame::RemoveFrame(ChildListID     aListID,
                            nsIFrame*       aOldFrame)
 {
   MOZ_ASSERT(aListID == kPrincipalList, "unexpected child list");
-  MOZ_ASSERT(aOldFrame == mFrames.FirstChild(), "unknown aOldFrame");
   nsContainerFrame::RemoveFrame(aListID, aOldFrame);
 }
 #endif
 
 nsRect nsCanvasFrame::CanvasArea() const
 {
   // Not clear which overflow rect we want here, but it probably doesn't
   // matter.
--- a/layout/generic/nsCanvasFrame.h
+++ b/layout/generic/nsCanvasFrame.h
@@ -8,29 +8,31 @@
 #ifndef nsCanvasFrame_h___
 #define nsCanvasFrame_h___
 
 #include "mozilla/Attributes.h"
 #include "mozilla/EventForwards.h"
 #include "nsContainerFrame.h"
 #include "nsIScrollPositionListener.h"
 #include "nsDisplayList.h"
+#include "nsIAnonymousContentCreator.h"
 
 class nsPresContext;
 class nsRenderingContext;
 
 /**
  * Root frame class.
  *
  * The root frame is the parent frame for the document element's frame.
  * It only supports having a single child frame which must be an area
  * frame
  */
 class nsCanvasFrame : public nsContainerFrame,
-                      public nsIScrollPositionListener
+                      public nsIScrollPositionListener,
+                      public nsIAnonymousContentCreator
 {
 public:
   nsCanvasFrame(nsStyleContext* aContext)
   : nsContainerFrame(aContext),
     mDoPaintFocus(false),
     mAddedScrollPositionListener(false) {}
 
   NS_DECL_QUERYFRAME_TARGET(nsCanvasFrame)
@@ -59,16 +61,26 @@ public:
                       const nsHTMLReflowState& aReflowState,
                       nsReflowStatus&          aStatus) MOZ_OVERRIDE;
   virtual bool IsFrameOfType(uint32_t aFlags) const MOZ_OVERRIDE
   {
     return nsContainerFrame::IsFrameOfType(aFlags &
              ~(nsIFrame::eCanContainOverflowContainers));
   }
 
+  // nsIAnonymousContentCreator
+  virtual nsresult CreateAnonymousContent(nsTArray<ContentInfo>& aElements) MOZ_OVERRIDE;
+  virtual void AppendAnonymousContentTo(nsBaseContentList& aElements, uint32_t aFilter) MOZ_OVERRIDE;
+
+  // Touch caret handle function
+  mozilla::dom::Element* GetTouchCaretElement() const
+  {
+     return mTouchCaretElement;
+  }
+
   /** SetHasFocus tells the CanvasFrame to draw with focus ring
    *  @param aHasFocus true to show focus ring, false to hide it
    */
   NS_IMETHOD SetHasFocus(bool aHasFocus);
 
   virtual void BuildDisplayList(nsDisplayListBuilder*   aBuilder,
                                 const nsRect&           aDirtyRect,
                                 const nsDisplayListSet& aLists) MOZ_OVERRIDE;
@@ -106,16 +118,18 @@ public:
                                       nsIContent** aContent) MOZ_OVERRIDE;
 
   nsRect CanvasArea() const;
 
 protected:
   // Data members
   bool                      mDoPaintFocus;
   bool                      mAddedScrollPositionListener;
+
+  nsCOMPtr<mozilla::dom::Element> mTouchCaretElement;
 };
 
 /**
  * Override nsDisplayBackground methods so that we pass aBGClipRect to
  * PaintBackground, covering the whole overflow area.
  * We can also paint an "extra background color" behind the normal
  * background.
  */
--- a/layout/generic/nsFlexContainerFrame.cpp
+++ b/layout/generic/nsFlexContainerFrame.cpp
@@ -1754,22 +1754,26 @@ FlexLine::ResolveFlexibleLengths(nscoord
         item->SetMainSize(item->GetFlexBaseSize());
       }
       availableFreeSpace -= item->GetMainSize();
     }
 
     PR_LOG(GetFlexContainerLog(), PR_LOG_DEBUG,
            (" available free space = %d\n", availableFreeSpace));
 
-    MOZ_ASSERT((isUsingFlexGrow && availableFreeSpace >= 0) ||
-               (!isUsingFlexGrow && availableFreeSpace <= 0),
-               "The sign of our free space should never disagree with the "
-               "type of flexing (grow/shrink) that we're doing. Any potential "
-               "disagreement should've made us use the other type of flexing, "
-               "or should've been resolved in FreezeItemsEarly");
+
+    // The sign of our free space should agree with the type of flexing
+    // (grow/shrink) that we're doing (except if we've had integer overflow;
+    // then, all bets are off). Any disagreement should've made us use the
+    // other type of flexing, or should've been resolved in FreezeItemsEarly.
+    // XXXdholbert If & when bug 765861 is fixed, we should upgrade this
+    // assertion to be fatal except in documents with enormous lengths.
+    NS_ASSERTION((isUsingFlexGrow && availableFreeSpace >= 0) ||
+                 (!isUsingFlexGrow && availableFreeSpace <= 0),
+                 "availableFreeSpace's sign should match isUsingFlexGrow");
 
     // If we have any free space available, give each flexible item a portion
     // of availableFreeSpace.
     if (availableFreeSpace != 0) {
       // The first time we do this, we initialize origAvailableFreeSpace.
       if (!isOrigAvailFreeSpaceInitialized) {
         origAvailableFreeSpace = availableFreeSpace;
         isOrigAvailFreeSpaceInitialized = true;
--- a/layout/generic/nsSelection.cpp
+++ b/layout/generic/nsSelection.cpp
@@ -48,16 +48,17 @@ static NS_DEFINE_CID(kFrameTraversalCID,
 #include "nsThreadUtils.h"
 #include "mozilla/Preferences.h"
 #include "nsDOMClassInfoID.h"
 
 //included for desired x position;
 #include "nsPresContext.h"
 #include "nsIPresShell.h"
 #include "nsCaret.h"
+#include "TouchCaret.h"
 
 #include "mozilla/MouseEvents.h"
 #include "mozilla/TextEvents.h"
 
 #include "nsITimer.h"
 #include "nsFrameManager.h"
 // notifications
 #include "nsIDOMDocument.h"
@@ -688,16 +689,24 @@ void
 nsFrameSelection::Init(nsIPresShell *aShell, nsIContent *aLimiter)
 {
   mShell = aShell;
   mMouseDownState = false;
   mDesiredXSet = false;
   mLimiter = aLimiter;
   mCaretMovementStyle =
     Preferences::GetInt("bidi.edit.caret_movement_style", 2);
+  // Set touch caret as selection listener
+  nsRefPtr<TouchCaret> touchCaret = mShell->GetTouchCaret();
+  if (touchCaret) {
+    int8_t index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
+    if (mDomSelections[index]) {
+      mDomSelections[index]->AddSelectionListener(touchCaret);
+    }
+  }
 }
 
 nsresult
 nsFrameSelection::MoveCaret(uint32_t          aKeycode,
                             bool              aContinueSelection,
                             nsSelectionAmount aAmount)
 {
   bool visualMovement =
@@ -3006,16 +3015,22 @@ nsFrameSelection::SetDelayedCaretData(Wi
   } else {
     mDelayedMouseEventValid = false;
   }
 }
 
 void
 nsFrameSelection::DisconnectFromPresShell()
 {
+  // Remove touch caret as selection listener
+  nsRefPtr<TouchCaret> touchCaret = mShell->GetTouchCaret();
+  if (touchCaret) {
+    int8_t index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
+    mDomSelections[index]->RemoveSelectionListener(touchCaret);
+  }
   StopAutoScrollTimer();
   for (int32_t i = 0; i < nsISelectionController::NUM_SELECTIONTYPES; i++) {
     mDomSelections[i]->Clear(nullptr);
   }
   mShell = nullptr;
 }
 
 //END nsISelection interface implementations
--- a/layout/reftests/flexbox/pagination/flexbox-unbreakable-child-1-ref.html
+++ b/layout/reftests/flexbox/pagination/flexbox-unbreakable-child-1-ref.html
@@ -2,17 +2,17 @@
 <!-- Any copyright is dedicated to the Public Domain.
      http://creativecommons.org/publicdomain/zero/1.0/ -->
 <html>
   <head>
     <style>
     .multicol {
       height: 10px;
       width: 100px;
-      -moz-column-width: 20px;
+      -moz-column-width: 30px;
       -moz-column-fill: auto;
       border: 2px solid orange;
       margin-bottom: 15px; /* (just for spacing between testcases) */
     }
     .flexContainer {
       background: teal;
       border: 1px dashed black;
     }
--- a/layout/reftests/flexbox/pagination/flexbox-unbreakable-child-1a-wrap.html
+++ b/layout/reftests/flexbox/pagination/flexbox-unbreakable-child-1a-wrap.html
@@ -6,17 +6,17 @@
      "flex-wrap: wrap".
 -->
 <html>
   <head>
     <style>
     .multicol {
       height: 10px;
       width: 100px;
-      -moz-column-width: 20px;
+      -moz-column-width: 30px;
       -moz-column-fill: auto;
       border: 2px solid orange;
       margin-bottom: 15px; /* (just for spacing between testcases) */
     }
     .flexContainer {
       display: flex;
       flex-direction: row;
       flex-wrap: wrap;
--- a/layout/reftests/flexbox/pagination/flexbox-unbreakable-child-1a.html
+++ b/layout/reftests/flexbox/pagination/flexbox-unbreakable-child-1a.html
@@ -5,17 +5,17 @@
      child, with the flex container having "flex-direction: row".
 -->
 <html>
   <head>
     <style>
     .multicol {
       height: 10px;
       width: 100px;
-      -moz-column-width: 20px;
+      -moz-column-width: 30px;
       -moz-column-fill: auto;
       border: 2px solid orange;
       margin-bottom: 15px; /* (just for spacing between testcases) */
     }
     .flexContainer {
       display: flex;
       flex-direction: row;
       background: teal;
--- a/layout/reftests/flexbox/pagination/flexbox-unbreakable-child-1b-wrap.html
+++ b/layout/reftests/flexbox/pagination/flexbox-unbreakable-child-1b-wrap.html
@@ -6,17 +6,17 @@
      "flex-wrap: wrap".
 -->
 <html>
   <head>
     <style>
     .multicol {
       height: 10px;
       width: 100px;
-      -moz-column-width: 20px;
+      -moz-column-width: 30px;
       -moz-column-fill: auto;
       border: 2px solid orange;
       margin-bottom: 15px; /* (just for spacing between testcases) */
     }
     .flexContainer {
       display: flex;
       flex-direction: row-reverse;
       flex-wrap: wrap;
--- a/layout/reftests/flexbox/pagination/flexbox-unbreakable-child-1b.html
+++ b/layout/reftests/flexbox/pagination/flexbox-unbreakable-child-1b.html
@@ -5,17 +5,17 @@
      child, with the flex container having "flex-direction: row-reverse".
 -->
 <html>
   <head>
     <style>
     .multicol {
       height: 10px;
       width: 100px;
-      -moz-column-width: 20px;
+      -moz-column-width: 30px;
       -moz-column-fill: auto;
       border: 2px solid orange;
       margin-bottom: 15px; /* (just for spacing between testcases) */
     }
     .flexContainer {
       display: flex;
       flex-direction: row-reverse;
       background: teal;
--- a/layout/reftests/flexbox/pagination/flexbox-unbreakable-child-1c-wrap.html
+++ b/layout/reftests/flexbox/pagination/flexbox-unbreakable-child-1c-wrap.html
@@ -6,17 +6,17 @@
      "flex-wrap: wrap".
 -->
 <html>
   <head>
     <style>
     .multicol {
       height: 10px;
       width: 100px;
-      -moz-column-width: 20px;
+      -moz-column-width: 30px;
       -moz-column-fill: auto;
       border: 2px solid orange;
       margin-bottom: 15px; /* (just for spacing between testcases) */
     }
     .flexContainer {
       display: flex;
       flex-direction: column;
       flex-wrap: wrap;
--- a/layout/reftests/flexbox/pagination/flexbox-unbreakable-child-1c.html
+++ b/layout/reftests/flexbox/pagination/flexbox-unbreakable-child-1c.html
@@ -5,17 +5,17 @@
      child, with the flex container having "flex-direction: column".
 -->
 <html>
   <head>
     <style>
     .multicol {
       height: 10px;
       width: 100px;
-      -moz-column-width: 20px;
+      -moz-column-width: 30px;
       -moz-column-fill: auto;
       border: 2px solid orange;
       margin-bottom: 15px; /* (just for spacing between testcases) */
     }
     .flexContainer {
       display: flex;
       flex-direction: column;
       background: teal;
--- a/layout/reftests/flexbox/pagination/flexbox-unbreakable-child-1d-wrap.html
+++ b/layout/reftests/flexbox/pagination/flexbox-unbreakable-child-1d-wrap.html
@@ -6,17 +6,17 @@
      and "flex-wrap: wrap".
 -->
 <html>
   <head>
     <style>
     .multicol {
       height: 10px;
       width: 100px;
-      -moz-column-width: 20px;
+      -moz-column-width: 30px;
       -moz-column-fill: auto;
       border: 2px solid orange;
       margin-bottom: 15px; /* (just for spacing between testcases) */
     }
     .flexContainer {
       display: flex;
       flex-direction: column-reverse;
       flex-wrap: wrap;
--- a/layout/reftests/flexbox/pagination/flexbox-unbreakable-child-1d.html
+++ b/layout/reftests/flexbox/pagination/flexbox-unbreakable-child-1d.html
@@ -5,17 +5,17 @@
      child, with the flex container having "flex-direction: column-reverse".
 -->
 <html>
   <head>
     <style>
     .multicol {
       height: 10px;
       width: 100px;
-      -moz-column-width: 20px;
+      -moz-column-width: 30px;
       -moz-column-fill: auto;
       border: 2px solid orange;
       margin-bottom: 15px; /* (just for spacing between testcases) */
     }
     .flexContainer {
       display: flex;
       flex-direction: column-reverse;
       justify-content: flex-end;
--- a/layout/reftests/flexbox/pagination/flexbox-unbreakable-child-2-ref.html
+++ b/layout/reftests/flexbox/pagination/flexbox-unbreakable-child-2-ref.html
@@ -19,17 +19,17 @@
       background: yellow;
       border: 1px dashed black;
     }
     .item {
       width: 40px;
       height: 15px;
       border: 1px dotted teal;
       margin: 1px;
-      font: 10px;
+      font: 10px serif;
       float: left;
     }
     </style>
   </head>
   <body>
     <!-- auto-height container: -->
     <div class="multicol">
       <div class="flexContainer" style="height: 38px">
--- a/layout/reftests/flexbox/pagination/flexbox-unbreakable-child-2.html
+++ b/layout/reftests/flexbox/pagination/flexbox-unbreakable-child-2.html
@@ -23,17 +23,17 @@
       background: yellow;
       border: 1px dashed black;
     }
     .item {
       width: 40px;
       height: 15px;
       border: 1px dotted teal;
       margin: 1px;
-      font: 10px;
+      font: 10px serif;
     }
     </style>
   </head>
   <body>
     <!-- auto-height container: -->
     <div class="multicol">
       <div class="flexContainer">
         <div class="item">1</div>
--- a/layout/reftests/w3c-css/submitted/flexbox/flexbox-collapsed-item-baseline-1-ref.html
+++ b/layout/reftests/w3c-css/submitted/flexbox/flexbox-collapsed-item-baseline-1-ref.html
@@ -18,16 +18,17 @@
       width: 50px;
       background: yellow;
       border: 1px dotted black;
       margin: 5px;
       align-items: flex-start;
     }
     .hiddenItemForSizing {
       width: 0;
+      min-width: 0; /* disable default min-width:auto behavior */
       color: transparent;
       align-self: baseline;
     }
     .largeFont {
       font-size: 20px;
       background: lightblue;
       /* Our flex items get padding on opposite sides (top/bottom) so that they
          produce a large combined height when baseline-aligned: */
--- a/layout/reftests/w3c-css/submitted/flexbox/flexbox-items-as-stacking-contexts-2.html
+++ b/layout/reftests/w3c-css/submitted/flexbox/flexbox-items-as-stacking-contexts-2.html
@@ -28,20 +28,22 @@
       justify-content: space-between;
       width: 70px;
       padding: 2px;
       margin-bottom: 2px;
     }
     .item1 {
       background: lightblue;
       width: 30px;
+      min-width: 0; /* disable default min-width:auto behavior */
     }
     .item2 {
       background: yellow;
       width: 30px;
+      min-width: 0; /* disable default min-width:auto behavior */
     }
   </style>
 </head>
 <body>
   <!-- This container has two flex items, the first of which has content
        sticking out & overlapping the second.  If they're painting atomically
        (and in the right order), the second item's background should cover the
        first item's overflowing content. -->
--- a/layout/reftests/w3c-css/submitted/flexbox/flexbox-items-as-stacking-contexts-3.html
+++ b/layout/reftests/w3c-css/submitted/flexbox/flexbox-items-as-stacking-contexts-3.html
@@ -24,16 +24,17 @@
       width: 70px;
       height: 20px;
       padding: 2px;
       margin-bottom: 2px;
     }
     .item1 {
       background: lightblue;
       width: 30px;
+      min-width: 0; /* disable default min-width:auto behavior */
       padding: 2px;
     }
     .item2 {
       background: yellow;
       width: 30px;
       padding: 2px;
     }
     .grandchildA {
--- a/layout/style/ua.css
+++ b/layout/style/ua.css
@@ -278,8 +278,46 @@ parsererror|sourcetext {
   white-space: pre;
   font-family: -moz-fixed;
   margin-top: 2em;
   margin-bottom: 1em;
   color: red;
   font-weight: bold;
   font-size: 12pt;
 }
+
+div[\_moz_anonclass="mozTouchCaret"].moz-touchcaret {
+  background-image: url("resource://gre/res/text_selection_handle.png");
+  position: absolute;
+  width: 19px;
+  height: 24px;
+  margin-left: -10px;
+  background-position: center center;
+  z-index: 2147483647;
+}
+
+@media (min-resolution: 1.5dppx) {
+  div[\_moz_anonclass="mozTouchCaret"].moz-touchcaret {
+    background-image: url("resource://gre/res/text_selection_handle@1.5.png");
+    position: absolute;
+    width: 29px;
+    height: 36px;
+    margin-left: -15px;
+    background-position: center center;
+    z-index: 2147483647;
+  }
+}
+
+@media (min-resolution: 2dppx) {
+  div[\_moz_anonclass="mozTouchCaret"].moz-touchcaret {
+    background-image: url("resource://gre/res/text_selection_handle@2.png");
+    position: absolute;
+    width: 38px;
+    height: 48px;
+    margin-left: -19px;
+    background-position: center center;
+    z-index: 2147483647;
+  }
+}
+
+div[\_moz_anonclass="mozTouchCaret"].moz-touchcaret.hidden {
+  visibility: hidden;
+}
--- a/media/libstagefright/binding/Adts.cpp
+++ b/media/libstagefright/binding/Adts.cpp
@@ -36,17 +36,17 @@ bool
 Adts::ConvertEsdsToAdts(uint16_t aChannelCount, int8_t aFrequencyIndex,
                         int8_t aProfile, MP4Sample* aSample)
 {
   static const int kADTSHeaderSize = 7;
 
   size_t newSize = aSample->size + kADTSHeaderSize;
 
   // ADTS header uses 13 bits for packet size.
-  if (newSize >= (1 << 13) || aChannelCount < 0 || aChannelCount > 15 ||
+  if (newSize >= (1 << 13) || aChannelCount > 15 ||
       aFrequencyIndex < 0 || aProfile < 1 || aProfile > 4)
     return false;
 
   Array<uint8_t, kADTSHeaderSize> header;
   header[0] = 0xff;
   header[1] = 0xf1;
   header[2] =
     ((aProfile - 1) << 6) + (aFrequencyIndex << 2) + (aChannelCount >> 2);
--- a/media/libstagefright/moz.build
+++ b/media/libstagefright/moz.build
@@ -25,28 +25,16 @@ elif CONFIG['OS_TARGET'] in ('DragonFly'
         DEFINES['ENODATA'] = '-0x80000003'
     if CONFIG['OS_TARGET'] == 'OpenBSD':
         DEFINES['EBADMSG'] = '-0x80000006'
     DEFINES['HAVE_SYS_UIO_H'] = True
     DEFINES['off64_t'] = 'off_t'
     LOCAL_INCLUDES += [ 'ports/bsd/include' ]
 else:
     DEFINES['HAVE_SYS_UIO_H'] = True
-    if CONFIG['OS_TARGET'] == 'Linux':
-        CXXFLAGS += ['-Wno-deprecated',
-                     '-Wno-format',
-                     '-Wno-dynamic-class-memaccess',
-                     '-Wno-tautological-compare',
-                     '-Wno-tautological-constant-out-of-range-compare',
-                     '-Wno-unused-variable',
-                     '-Wno-unused-function',
-                     '-Wno-sign-compare',
-                     '-Wno-comment',
-                     '-Wno-incompatible-pointer-types-discards-qualifiers'
-        ]
 
 if CONFIG['OS_TARGET'] != 'Android':
     DEFINES['FAKE_LOG_DEVICE'] = True
     SOURCES += [
         'system/core/liblog/fake_log_device.c',
     ]
     UNIFIED_SOURCES += [
         'system/core/libcutils/strdup16to8.c',
@@ -100,8 +88,37 @@ LOCAL_INCLUDES += [
     'frameworks/av/media/libstagefright/',
     'stubs/empty',
     'stubs/include',
     'stubs/include/media/stagefright/foundation',
     'system/core/include',
 ]
 
 FINAL_LIBRARY = 'gklayout'
+
+# Suppress warnings in third-party code.
+if CONFIG['_MSC_VER']:
+    CFLAGS += [
+        '-wd4013', # 'function' undefined; assuming extern returning int
+        '-wd4101', # unreferenced local variable
+    ]
+    CXXFLAGS += [
+        '-wd4018', # '<' : signed/unsigned mismatch
+        '-wd4275', # non dll-interface class used as base for dll-interface class
+        '-wd4305', # '=' : truncation from 'double' to 'float'
+        '-wd4309', # '=' : truncation of constant value
+        '-wd4355', # 'this' : used in base member initializer list
+        '-wd4804', # '>' : unsafe use of type 'bool' in operation
+    ]
+elif CONFIG['GNU_CXX']:
+    CFLAGS += [
+        '-Wno-comment',
+        '-Wno-declaration-after-statement',
+        '-Wno-sign-compare'
+    ]
+    CXXFLAGS += [
+        '-Wno-format',
+        '-Wno-multichar',
+        '-Wno-sign-compare',
+        '-Wno-unused',
+    ]
+    if CONFIG['CLANG_CXX']:
+        CXXFLAGS += ['-Wno-tautological-constant-out-of-range-compare']
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h
@@ -401,16 +401,27 @@ public:
 
     peerIdentity.SetIsVoid(true);
     return NS_OK;
   }
 
 #ifdef MOZILLA_INTERNAL_API
   const PeerIdentity* GetPeerIdentity() const { return mPeerIdentity; }
   nsresult SetPeerIdentity(const nsAString& peerIdentity);
+
+  const std::string& GetIdAsAscii() const
+  {
+    return mName;
+  }
+
+  nsresult GetId(nsAString& id)
+  {
+    id = NS_ConvertASCIItoUTF16(mName.c_str());
+    return NS_OK;
+  }
 #endif
 
   // this method checks to see if we've made a promise to protect media.
   bool PrivacyRequested() const { return mPrivacyRequested; }
 
   NS_IMETHODIMP GetFingerprint(char** fingerprint);
   void GetFingerprint(nsAString& fingerprint)
   {
--- a/media/webrtc/signaling/src/peerconnection/WebrtcGlobalInformation.cpp
+++ b/media/webrtc/signaling/src/peerconnection/WebrtcGlobalInformation.cpp
@@ -127,16 +127,17 @@ static void GetLogging_s(
                           NS_DISPATCH_NORMAL);
 }
 
 
 void
 WebrtcGlobalInformation::GetAllStats(
   const GlobalObject& aGlobal,
   WebrtcGlobalStatisticsCallback& aStatsCallback,
+  const Optional<nsAString>& pcIdFilter,
   ErrorResult& aRv)
 {
   if (!NS_IsMainThread()) {
     aRv.Throw(NS_ERROR_NOT_SAME_THREAD);
     return;
   }
 
   nsresult rv;
@@ -159,20 +160,23 @@ WebrtcGlobalInformation::GetAllStats(
   // the API consumer doesn't care why there are no PeerConnectionImpl.
   PeerConnectionCtx *ctx = GetPeerConnectionCtx();
   if (ctx) {
     for (auto p = ctx->mPeerConnections.begin();
          p != ctx->mPeerConnections.end();
          ++p) {
       MOZ_ASSERT(p->second);
 
-      if (p->second->HasMedia()) {
-        queries->append(nsAutoPtr<RTCStatsQuery>(new RTCStatsQuery(true)));
-        p->second->BuildStatsQuery_m(nullptr, // all tracks
-                                     queries->back());
+      if (!pcIdFilter.WasPassed() ||
+          pcIdFilter.Value().EqualsASCII(p->second->GetIdAsAscii().c_str())) {
+        if (p->second->HasMedia()) {
+          queries->append(nsAutoPtr<RTCStatsQuery>(new RTCStatsQuery(true)));
+          p->second->BuildStatsQuery_m(nullptr, // all tracks
+                                       queries->back());
+        }
       }
     }
   }
 
   // CallbackObject does not support threadsafe refcounting, and must be
   // destroyed on main.
   nsMainThreadPtrHandle<WebrtcGlobalStatisticsCallback> callbackHandle(
     new nsMainThreadPtrHolder<WebrtcGlobalStatisticsCallback>(&aStatsCallback));
--- a/media/webrtc/signaling/src/peerconnection/WebrtcGlobalInformation.h
+++ b/media/webrtc/signaling/src/peerconnection/WebrtcGlobalInformation.h
@@ -1,16 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef _WEBRTC_GLOBAL_INFORMATION_H_
 #define _WEBRTC_GLOBAL_INFORMATION_H_
 
 #include "nsString.h"
+#include "mozilla/dom/BindingDeclarations.h" // for Optional
 
 namespace sipcc {
 class PeerConnectionImpl;
 }
 
 namespace mozilla {
 class ErrorResult;
 
@@ -19,16 +20,17 @@ class GlobalObject;
 class WebrtcGlobalStatisticsCallback;
 class WebrtcGlobalLoggingCallback;
 
 class WebrtcGlobalInformation
 {
 public:
   static void GetAllStats(const GlobalObject& aGlobal,
                           WebrtcGlobalStatisticsCallback& aStatsCallback,
+                          const Optional<nsAString>& pcIdFilter,
                           ErrorResult& aRv);
 
   static void GetLogging(const GlobalObject& aGlobal,
                          const nsAString& aPattern,
                          WebrtcGlobalLoggingCallback& aLoggingCallback,
                          ErrorResult& aRv);
 
   static void StoreLongTermICEStatistics(sipcc::PeerConnectionImpl& aPc);
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -1560,16 +1560,19 @@ pref("security.csp.enable", true);
 pref("security.csp.debug", false);
 pref("security.csp.experimentalEnabled", false);
 pref("security.csp.newbackend.enable", false);
 
 // Mixed content blocking
 pref("security.mixed_content.block_active_content", false);
 pref("security.mixed_content.block_display_content", false);
 
+// Disable pinning checks by default.
+pref("security.cert_pinning.enforcement_level", 0);
+
 // Modifier key prefs: default to Windows settings,
 // menu access key = alt, accelerator key = control.
 // Use 17 for Ctrl, 18 for Alt, 224 for Meta, 91 for Win, 0 for none. Mac settings in macprefs.js
 pref("ui.key.accelKey", 17);
 pref("ui.key.menuAccessKey", 18);
 pref("ui.key.generalAccessKey", -1);
 
 // If generalAccessKey is -1, use the following two prefs instead.
@@ -2079,17 +2082,17 @@ pref("svg.marker-improvements.enabled", 
 #endif
 
 #ifdef RELEASE_BUILD
 pref("svg.svg-iframe.enabled", false);
 #else
 pref("svg.svg-iframe.enabled", false);
 #endif
 
-// Is support for the new getBBox method from SVG 2 enabled?  
+// Is support for the new getBBox method from SVG 2 enabled?
 // See https://svgwg.org/svg2-draft/single-page.html#types-SVGBoundingBoxOptions
 #ifdef RELEASE_BUILD
 pref("svg.new-getBBox.enabled", false);
 #else
 pref("svg.new-getBBox.enabled", true);
 #endif
 
 // Default font types and sizes by locale
@@ -4140,16 +4143,29 @@ pref("urlclassifier.malware_table", "goo
 pref("urlclassifier.phish_table", "goog-phish-shavar,test-phish-simple");
 pref("urlclassifier.downloadBlockTable", "");
 pref("urlclassifier.downloadAllowTable", "");
 pref("urlclassifier.disallow_completions", "test-malware-simple,test-phish-simple,goog-downloadwhite-digest256");
 
 // Turn off Spatial navigation by default.
 pref("snav.enabled", false);
 
+// Turn off touch caret by default.
+pref("touchcaret.enabled", false);
+
+// Maximum distance to the center of touch caret (in app unit square) which
+// will be accepted to drag touch caret (0 means only in the bounding box of touch
+// caret is accepted)
+pref("touchcaret.distance.threshold", 1500);
+
+// We'll start to increment time when user release the control of touch caret.
+// When time exceed this expiration time, we'll hide touch caret.
+// In milliseconds. (0 means disable this feature)
+pref("touchcaret.expiration.time", 3000);
+
 // Wakelock is disabled by default.
 pref("dom.wakelock.enabled", false);
 
 // The URL of the Firefox Accounts auth server backend
 pref("identity.fxaccounts.auth.uri", "https://api.accounts.firefox.com/v1");
 
 // disable mozsample size for now
 pref("image.mozsamplesize.enabled", false);
--- a/netwerk/base/public/security-prefs.js
+++ b/netwerk/base/public/security-prefs.js
@@ -51,11 +51,8 @@ pref("security.remember_cert_checkbox_de
 pref("security.ask_for_password",        0);
 pref("security.password_lifetime",       30);
 
 pref("security.OCSP.enabled", 1);
 pref("security.OCSP.require", false);
 pref("security.OCSP.GET.enabled", false);
 
 pref("security.use_mozillapkix_verification", true);
-
-// Default to MITM mode for pinning checks.
-pref("security.cert_pinning.enforcement_level", 1);
--- a/python/mozbuild/mozbuild/mozconfig.py
+++ b/python/mozbuild/mozbuild/mozconfig.py
@@ -186,17 +186,17 @@ class MozconfigLoader(ProcessExecutionMi
         state from execution. Thus, the output from a mozconfig is a friendly
         static data structure.
         """
         if path is None:
             path = self.find_mozconfig()
 
         result = {
             'path': path,
-            'topobjdir': os.environ.get('MOZ_OBJDIR'),
+            'topobjdir': None,
             'configure_args': None,
             'make_flags': None,
             'make_extra': None,
             'env': None,
         }
 
         if path is None:
             return result
--- a/python/mozbuild/mozbuild/test/test_base.py
+++ b/python/mozbuild/mozbuild/test/test_base.py
@@ -32,17 +32,16 @@ topsrcdir = os.path.abspath(os.path.join
 log_manager = LoggingManager()
 
 
 class TestMozbuildObject(unittest.TestCase):
     def setUp(self):
         self._old_cwd = os.getcwd()
         self._old_env = dict(os.environ)
         os.environ.pop('MOZCONFIG', None)
-        os.environ.pop('MOZ_OBJDIR', None)
 
     def tearDown(self):
         os.chdir(self._old_cwd)
         os.environ.clear()
         os.environ.update(self._old_env)
 
     def get_base(self):
         return MozbuildObject(topsrcdir, None, log_manager)
--- a/python/mozbuild/mozbuild/test/test_mozconfig.py
+++ b/python/mozbuild/mozbuild/test/test_mozconfig.py
@@ -24,17 +24,16 @@ from mozbuild.mozconfig import (
     MozconfigLoader,
 )
 
 
 class TestMozconfigLoader(unittest.TestCase):
     def setUp(self):
         self._old_env = dict(os.environ)
         os.environ.pop('MOZCONFIG', None)
-        os.environ.pop('MOZ_OBJDIR', None)
         os.environ.pop('CC', None)
         os.environ.pop('CXX', None)
         self._temp_dirs = set()
 
     def tearDown(self):
         os.environ.clear()
         os.environ.update(self._old_env)
 
@@ -312,32 +311,16 @@ class TestMozconfigLoader(unittest.TestC
             mozconfig.write('mk_add_options BIZ=1\n')
             mozconfig.flush()
 
             result = self.get_loader().read_mozconfig(mozconfig.name)
             self.assertEqual(result['topobjdir'], '/foo/bar')
             self.assertEqual(result['make_flags'], '-j8')
             self.assertEqual(result['make_extra'], ['FOO=BAR BAZ', 'BIZ=1'])
 
-    def test_read_empty_mozconfig_objdir_environ(self):
-        os.environ[b'MOZ_OBJDIR'] = b'obj-firefox'
-        with NamedTemporaryFile(mode='w') as mozconfig:
-            result = self.get_loader().read_mozconfig(mozconfig.name)
-            self.assertEqual(result['topobjdir'], 'obj-firefox')
-
-    def test_read_capture_mk_options_objdir_environ(self):
-        """Ensures mk_add_options calls are captured and override the environ."""
-        os.environ[b'MOZ_OBJDIR'] = b'obj-firefox'
-        with NamedTemporaryFile(mode='w') as mozconfig:
-            mozconfig.write('mk_add_options MOZ_OBJDIR=/foo/bar\n')
-            mozconfig.flush()
-
-            result = self.get_loader().read_mozconfig(mozconfig.name)
-            self.assertEqual(result['topobjdir'], '/foo/bar')
-
     def test_read_moz_objdir_substitution(self):
         """Ensure @TOPSRCDIR@ substitution is recognized in MOZ_OBJDIR."""
         with NamedTemporaryFile(mode='w') as mozconfig:
             mozconfig.write('mk_add_options MOZ_OBJDIR=@TOPSRCDIR@/some-objdir')
             mozconfig.flush()
 
             loader = self.get_loader()
             result = loader.read_mozconfig(mozconfig.name)
--- a/security/manager/ssl/public/nsIX509Cert.idl
+++ b/security/manager/ssl/public/nsIX509Cert.idl
@@ -8,17 +8,17 @@
 
 interface nsIArray;
 interface nsIX509CertValidity;
 interface nsIASN1Object;
 
 /**
  * This represents a X.509 certificate.
  */
-[scriptable, uuid(900d6442-d8bc-11e3-aa51-0800273c564f)]
+[scriptable, uuid(891d2009-b9ba-4a0d-bebe-6b3a30e33191)]
 interface nsIX509Cert : nsISupports {
 
   /**
    *  A nickname for the certificate.
    */
   readonly attribute AString nickname;
 
   /**
@@ -123,17 +123,17 @@ interface nsIX509Cert : nsISupports {
   /**
    *  A unique identifier of this certificate within the local storage.
    */
   readonly attribute string dbKey;
 
   /**
    *  A human readable identifier to label this certificate.
    */
-  readonly attribute string windowTitle;
+  readonly attribute AString windowTitle;
 
   /**
    *  Constants to classify the type of a certificate.
    */
   const unsigned long UNKNOWN_CERT =      0;
   const unsigned long CA_CERT      = 1 << 0;
   const unsigned long USER_CERT    = 1 << 1;
   const unsigned long EMAIL_CERT   = 1 << 2;
--- a/security/manager/ssl/src/nsNSSCertHelper.cpp
+++ b/security/manager/ssl/src/nsNSSCertHelper.cpp
@@ -2012,25 +2012,28 @@ nsNSSCertificate::CreateASN1Struct(nsIAS
   nsNSSShutDownPreventionLock locker;
   if (isAlreadyShutDown())
     return NS_ERROR_NOT_AVAILABLE;
 
   nsCOMPtr<nsIASN1Sequence> sequence = new nsNSSASN1Sequence();
 
   nsCOMPtr<nsIMutableArray> asn1Objects;
   sequence->GetASN1Objects(getter_AddRefs(asn1Objects));
-  nsXPIDLCString title;
-  GetWindowTitle(getter_Copies(title));
-  
-  sequence->SetDisplayName(NS_ConvertUTF8toUTF16(title));
+
+  nsAutoString title;
+  nsresult rv = GetWindowTitle(title);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  sequence->SetDisplayName(title);
   sequence.forget(aRetVal);
 
   // This sequence will be contain the tbsCertificate, signatureAlgorithm,
   // and signatureValue.
-  nsresult rv;
   nsCOMPtr<nsINSSComponent> nssComponent(do_GetService(kNSSComponentCID, &rv));
   if (NS_FAILED(rv))
     return rv;
 
   rv = CreateTBSCertificateASN1Struct(getter_AddRefs(sequence),
                                       nssComponent);
   if (NS_FAILED(rv))
     return rv;
--- a/security/manager/ssl/src/nsNSSCertificate.cpp
+++ b/security/manager/ssl/src/nsNSSCertificate.cpp
@@ -36,16 +36,17 @@
 #include "nsCertVerificationThread.h"
 #include "nsIObjectOutputStream.h"
 #include "nsIObjectInputStream.h"
 #include "nsIProgrammingLanguage.h"
 #include "nsXULAppAPI.h"
 #include "ScopedNSSTypes.h"
 #include "nsProxyRelease.h"
 #include "mozilla/Base64.h"
+#include "NSSCertDBTrustDomain.h"
 
 #include "nspr.h"
 #include "certdb.h"
 #include "secerr.h"
 #include "nssb64.h"
 #include "secasn1.h"
 #include "secder.h"
 #include "ssl.h"
@@ -519,42 +520,49 @@ nsNSSCertificate::GetDbKey(char** aDbKey
          mCert->derIssuer.data, mCert->derIssuer.len);
 
   *aDbKey = NSSBase64_EncodeItem(nullptr, nullptr, 0, &key);
   nsMemory::Free(key.data); // SECItem is a 'c' type without a destrutor
   return (*aDbKey) ? NS_OK : NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP
-nsNSSCertificate::GetWindowTitle(char** aWindowTitle)
+nsNSSCertificate::GetWindowTitle(nsAString& aWindowTitle)
 {
   nsNSSShutDownPreventionLock locker;
-  if (isAlreadyShutDown())
+  if (isAlreadyShutDown()) {
     return NS_ERROR_NOT_AVAILABLE;
+  }
+
+  aWindowTitle.Truncate();
+
+  if (!mCert) {
+    NS_ERROR("Somehow got nullptr for mCert in nsNSSCertificate.");
+    return NS_ERROR_FAILURE;
+  }
+
+  mozilla::pkix::ScopedPtr<char, mozilla::psm::PORT_Free_string>
+    commonName(CERT_GetCommonName(&mCert->subject));
 
-  NS_ENSURE_ARG(aWindowTitle);
-  if (mCert) {
-    if (mCert->nickname) {
-      *aWindowTitle = PL_strdup(mCert->nickname);
-    } else {
-      *aWindowTitle = CERT_GetCommonName(&mCert->subject);
-      if (!*aWindowTitle) {
-        if (mCert->subjectName) {
-          *aWindowTitle = PL_strdup(mCert->subjectName);
-        } else if (mCert->emailAddr) {
-          *aWindowTitle = PL_strdup(mCert->emailAddr);
-        } else {
-          *aWindowTitle = PL_strdup("");
-        }
-      }
+  const char* titleOptions[] = {
+    mCert->nickname,
+    commonName.get(),
+    mCert->subjectName,
+    mCert->emailAddr
+  };
+
+  nsAutoCString titleOption;
+  for (size_t i = 0; i < ArrayLength(titleOptions); i++) {
+    titleOption = titleOptions[i];
+    if (titleOption.Length() > 0 && IsUTF8(titleOption)) {
+      CopyUTF8toUTF16(titleOption, aWindowTitle);
+      return NS_OK;
     }
-  } else {
-    NS_ERROR("Somehow got nullptr for mCertificate in nsNSSCertificate.");
-    *aWindowTitle = nullptr;
   }
+
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsNSSCertificate::GetNickname(nsAString& aNickname)
 {
   static NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID);
 
--- a/security/manager/ssl/src/nsNSSCertificateFakeTransport.cpp
+++ b/security/manager/ssl/src/nsNSSCertificateFakeTransport.cpp
@@ -41,19 +41,18 @@ nsNSSCertificateFakeTransport::~nsNSSCer
 /* readonly attribute string dbKey; */
 NS_IMETHODIMP
 nsNSSCertificateFakeTransport::GetDbKey(char * *aDbKey)
 {
   NS_NOTREACHED("Unimplemented on content process");
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
-/* readonly attribute string windowTitle; */
 NS_IMETHODIMP
-nsNSSCertificateFakeTransport::GetWindowTitle(char * *aWindowTitle)
+nsNSSCertificateFakeTransport::GetWindowTitle(nsAString& aWindowTitle)
 {
   NS_NOTREACHED("Unimplemented on content process");
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 nsNSSCertificateFakeTransport::GetNickname(nsAString &aNickname)
 {
--- a/security/pkix/lib/pkixbuild.cpp
+++ b/security/pkix/lib/pkixbuild.cpp
@@ -120,17 +120,16 @@ static Result BuildForward(TrustDomain& 
                            unsigned int subCACount,
                            /*out*/ ScopedCERTCertList& results);
 
 // The code that executes in the inner loop of BuildForward
 static Result
 BuildForwardInner(TrustDomain& trustDomain,
                   BackCert& subject,
                   PRTime time,
-                  EndEntityOrCA endEntityOrCA,
                   KeyPurposeId requiredEKUIfPresent,
                   const CertPolicyId& requiredPolicy,
                   CERTCertificate* potentialIssuerCertToDup,
                   unsigned int subCACount,
                   ScopedCERTCertList& results)
 {
   PORT_Assert(potentialIssuerCertToDup);
 
@@ -158,25 +157,19 @@ BuildForwardInner(TrustDomain& trustDoma
     }
   }
 
   rv = CheckNameConstraints(potentialIssuer);
   if (rv != Success) {
     return rv;
   }
 
-  unsigned int newSubCACount = subCACount;
-  if (endEntityOrCA == EndEntityOrCA::MustBeCA) {
-    newSubCACount = subCACount + 1;
-  } else {
-    PR_ASSERT(newSubCACount == 0);
-  }
   rv = BuildForward(trustDomain, potentialIssuer, time, EndEntityOrCA::MustBeCA,
                     KU_KEY_CERT_SIGN, requiredEKUIfPresent, requiredPolicy,
-                    nullptr, newSubCACount, results);
+                    nullptr, subCACount, results);
   if (rv != Success) {
     return rv;
   }
 
   if (trustDomain.VerifySignedData(&subject.GetNSSCert()->signatureWrap,
                                    potentialIssuer.GetNSSCert()) != SECSuccess) {
     return MapSECStatus(SECFailure);
   }
@@ -197,23 +190,16 @@ BuildForward(TrustDomain& trustDomain,
              EndEntityOrCA endEntityOrCA,
              KeyUsages requiredKeyUsagesIfPresent,
              KeyPurposeId requiredEKUIfPresent,
              const CertPolicyId& requiredPolicy,
              /*optional*/ const SECItem* stapledOCSPResponse,
              unsigned int subCACount,
              /*out*/ ScopedCERTCertList& results)
 {
-  // Avoid stack overflows and poor performance by limiting cert length.
-  // XXX: 6 is not enough for chains.sh anypolicywithlevel.cfg tests
-  static const size_t MAX_DEPTH = 8;
-  if (subCACount >= MAX_DEPTH - 1) {
-    return RecoverableError;
-  }
-
   Result rv;
 
   TrustLevel trustLevel;
   // If this is an end-entity and not a trust anchor, we defer reporting
   // any error found here until after attempting to find a valid chain.
   // See the explanation of error prioritization in pkix.h.
   rv = CheckIssuerIndependentProperties(trustDomain, subject, time,
                                         endEntityOrCA,
@@ -260,34 +246,45 @@ BuildForward(TrustDomain& trustDomain,
     results = CERT_NewCertList();
     if (!results) {
       return FatalError;
     }
     rv = subject.PrependNSSCertToList(results.get());
     return rv;
   }
 
+  if (endEntityOrCA == EndEntityOrCA::MustBeCA) {
+    // Avoid stack overflows and poor performance by limiting cert chain
+    // length.
+    static const unsigned int MAX_SUBCA_COUNT = 6;
+    if (subCACount >= MAX_SUBCA_COUNT) {
+      return Fail(RecoverableError, SEC_ERROR_UNKNOWN_ISSUER);
+    }
+    ++subCACount;
+  } else {
+    PR_ASSERT(subCACount == 0);
+  }
+
   // Find a trusted issuer.
   // TODO(bug 965136): Add SKI/AKI matching optimizations
   ScopedCERTCertList candidates;
   if (trustDomain.FindPotentialIssuers(&subject.GetNSSCert()->derIssuer, time,
                                        candidates) != SECSuccess) {
     return MapSECStatus(SECFailure);
   }
   if (!candidates) {
     return Fail(RecoverableError, SEC_ERROR_UNKNOWN_ISSUER);
   }
 
   PRErrorCode errorToReturn = 0;
 
   for (CERTCertListNode* n = CERT_LIST_HEAD(candidates);
        !CERT_LIST_END(n, candidates); n = CERT_LIST_NEXT(n)) {
-    rv = BuildForwardInner(trustDomain, subject, time, endEntityOrCA,
-                           requiredEKUIfPresent, requiredPolicy,
-                           n->cert, subCACount, results);
+    rv = BuildForwardInner(trustDomain, subject, time, requiredEKUIfPresent,
+                           requiredPolicy, n->cert, subCACount, results);
     if (rv == Success) {
       // If we found a valid chain but deferred reporting an error with the
       // end-entity certificate, report it now.
       if (deferredEndEntityError != 0) {
         return Fail(FatalError, deferredEndEntityError);
       }
 
       SECStatus srv = trustDomain.CheckRevocation(endEntityOrCA,
--- a/security/pkix/lib/pkixcheck.cpp
+++ b/security/pkix/lib/pkixcheck.cpp
@@ -422,55 +422,53 @@ MatchEKU(der::Input& value, KeyPurposeId
 
   if (!found) {
     switch (requiredEKU) {
       case KeyPurposeId::id_kp_serverAuth:
         // Treat CA certs with step-up OID as also having SSL server type.
         // Comodo has issued certificates that require this behavior that don't
         // expire until June 2020! TODO(bug 982932): Limit this exception to
         // old certificates.
-        match = value.MatchBytes(server) ||
+        match = value.MatchRest(server) ||
                 (endEntityOrCA == EndEntityOrCA::MustBeCA &&
-                 value.MatchBytes(serverStepUp));
+                 value.MatchRest(serverStepUp));
         break;
 
       case KeyPurposeId::id_kp_clientAuth:
-        match = value.MatchBytes(client);
+        match = value.MatchRest(client);
         break;
 
       case KeyPurposeId::id_kp_codeSigning:
-        match = value.MatchBytes(code);
+        match = value.MatchRest(code);
         break;
 
       case KeyPurposeId::id_kp_emailProtection:
-        match = value.MatchBytes(email);
+        match = value.MatchRest(email);
         break;
 
       case KeyPurposeId::id_kp_OCSPSigning:
-        match = value.MatchBytes(ocsp);
+        match = value.MatchRest(ocsp);
         break;
 
       case KeyPurposeId::anyExtendedKeyUsage:
         PR_NOT_REACHED("anyExtendedKeyUsage should start with found==true");
         return der::Fail(SEC_ERROR_LIBRARY_FAILURE);
 
       default:
         PR_NOT_REACHED("unrecognized EKU");
         return der::Fail(SEC_ERROR_LIBRARY_FAILURE);
     }
   }
 
   if (match) {
-    if (value.AtEnd()) {
-      found = true;
-      if (requiredEKU == KeyPurposeId::id_kp_OCSPSigning) {
-        foundOCSPSigning = true;
-      }
+    found = true;
+    if (requiredEKU == KeyPurposeId::id_kp_OCSPSigning) {
+      foundOCSPSigning = true;
     }
-  } else if (value.MatchBytes(ocsp) && value.AtEnd()) {
+  } else if (value.MatchRest(ocsp)) {
     foundOCSPSigning = true;
   }
 
   value.SkipToEnd(); // ignore unmatched OIDs.
 
   return der::Success;
 }
 
--- a/security/pkix/lib/pkixder.h
+++ b/security/pkix/lib/pkixder.h
@@ -111,54 +111,57 @@ public:
     this->end = data + len;
 
     return Success;
   }
 
   Result Expect(const uint8_t* expected, uint16_t expectedLen)
   {
     if (EnsureLength(expectedLen) != Success) {
-      return Fail(SEC_ERROR_BAD_DER);
+      return Failure;
     }
     if (memcmp(input, expected, expectedLen)) {
       return Fail(SEC_ERROR_BAD_DER);
     }
     input += expectedLen;
     return Success;
   }
 
   bool Peek(uint8_t expectedByte) const
   {
     return input < end && *input == expectedByte;
   }
 
   Result Read(uint8_t& out)
   {
-    if (input == end) {
-      return Fail(SEC_ERROR_BAD_DER);
+    if (EnsureLength(1) != Success) {
+      return Failure;
     }
     out = *input++;
     return Success;
   }
 
   Result Read(uint16_t& out)
   {
-    if (input == end || input + 1 == end) {
-      return Fail(SEC_ERROR_BAD_DER);
+    if (EnsureLength(2) != Success) {
+      return Failure;
     }
     out = *input++;
     out <<= 8u;
     out |= *input++;
     return Success;
   }
 
   template <uint16_t N>
-  bool MatchBytes(const uint8_t (&toMatch)[N])
+  bool MatchRest(const uint8_t (&toMatch)[N])
   {
-    if (EnsureLength(N) != Success) {
+    // Normally we use EnsureLength which compares (input + len < end), but
+    // here we want to be sure that there is nothing following the matched
+    // bytes
+    if (static_cast<size_t>(end - input) != N) {
       return false;
     }
     if (memcmp(input, toMatch, N)) {
       return false;
     }
     input += N;
     return true;
   }
@@ -186,38 +189,38 @@ public:
     }
     input += totalLen;
     return true;
   }
 
   Result Skip(uint16_t len)
   {
     if (EnsureLength(len) != Success) {
-      return Fail(SEC_ERROR_BAD_DER);
+      return Failure;
     }
     input += len;
     return Success;
   }
 
   Result Skip(uint16_t len, Input& skippedInput)
   {
     if (EnsureLength(len) != Success) {
-      return Fail(SEC_ERROR_BAD_DER);
+      return Failure;
     }
     if (skippedInput.Init(input, len) != Success) {
       return Failure;
     }
     input += len;
     return Success;
   }
 
   Result Skip(uint16_t len, SECItem& skippedItem)
   {
     if (EnsureLength(len) != Success) {
-      return Fail(SEC_ERROR_BAD_DER);
+      return Failure;
     }
     skippedItem.type = siBuffer;
     skippedItem.data = const_cast<uint8_t*>(input);
     skippedItem.len = len;
     input += len;
     return Success;
   }
 
--- a/security/pkix/test/gtest/moz.build
+++ b/security/pkix/test/gtest/moz.build
@@ -3,16 +3,17 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 LIBRARY_NAME = 'mozillapkix_gtest'
 
 SOURCES += [
     'nssgtest.cpp',
+    'pkix_cert_chain_length_tests.cpp',
     'pkix_ocsp_request_tests.cpp',
     'pkixder_input_tests.cpp',
     'pkixder_pki_types_tests.cpp',
     'pkixder_universal_types_tests.cpp',
 ]
 
 LOCAL_INCLUDES += [
     '../../include',
--- a/security/pkix/test/gtest/nssgtest.cpp
+++ b/security/pkix/test/gtest/nssgtest.cpp
@@ -18,16 +18,19 @@
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #include "nssgtest.h"
+#include "nss.h"
+#include "pkixtestutil.h"
+#include "prinit.h"
 
 using namespace std;
 using namespace testing;
 
 namespace mozilla { namespace pkix { namespace test {
 
 ostream&
 operator<<(ostream& os, SECStatusWithPRErrorCode const& value)
@@ -70,9 +73,33 @@ Pred_SECFailure(const char* expectedExpr
     return AssertionSuccess();
   }
 
   return AssertionFailure()
       << "Expected: (" << expectedExpr << ") == (" << actualExpr
       << "), actual: " << SECFailure << " != " << actual;
 }
 
+/*static*/ void
+NSSTest::SetUpTestCase()
+{
+  if (NSS_NoDB_Init(nullptr) != SECSuccess) {
+    PR_Abort();
+  }
+
+  now = PR_Now();
+  oneDayBeforeNow = now - ONE_DAY;
+  oneDayAfterNow = now + ONE_DAY;
+}
+
+NSSTest::NSSTest()
+  : arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE))
+{
+  if (!arena) {
+    PR_Abort();
+  }
+}
+
+/*static*/ PRTime NSSTest::now;
+/*static*/ PRTime NSSTest::oneDayBeforeNow;
+/*static*/ PRTime NSSTest::oneDayAfterNow;
+
 } } } // namespace mozilla::pkix::test
--- a/security/pkix/test/gtest/nssgtest.h
+++ b/security/pkix/test/gtest/nssgtest.h
@@ -22,17 +22,20 @@
  * limitations under the License.
  */
 
 #ifndef mozilla_pkix__nssgtest_h
 #define mozilla_pkix__nssgtest_h
 
 #include "stdint.h"
 #include "gtest/gtest.h"
+#include "pkix/pkixtypes.h"
+#include "pkixtestutil.h"
 #include "prerror.h"
+#include "prtime.h"
 #include "seccomon.h"
 
 namespace mozilla { namespace pkix { namespace test {
 
 class SECStatusWithPRErrorCode
 {
 public:
   SECStatusWithPRErrorCode(SECStatus rv, PRErrorCode errorCode)
@@ -60,27 +63,41 @@ private:
                                   SECStatusWithPRErrorCode const& value);
 
   void operator=(const SECStatusWithPRErrorCode&) /*delete*/;
 };
 
 ::std::ostream& operator<<(::std::ostream&,
                            SECStatusWithPRErrorCode const&);
 
-} } } // namespace mozilla::pkix::test
-
 #define ASSERT_SECSuccess(rv) \
   ASSERT_EQ(::mozilla::pkix::test::SECStatusWithPRErrorCode(SECSuccess, 0), \
             ::mozilla::pkix::test::SECStatusWithPRErrorCode(rv))
 #define EXPECT_SECSuccess(rv) \
   EXPECT_EQ(::mozilla::pkix::test::SECStatusWithPRErrorCode(SECSuccess, 0), \
             ::mozilla::pkix::test::SECStatusWithPRErrorCode(rv))
 
 #define ASSERT_SECFailure(expectedError, rv) \
   ASSERT_EQ(::mozilla::pkix::test::SECStatusWithPRErrorCode(SECFailure, \
                                                             expectedError), \
             ::mozilla::pkix::test::SECStatusWithPRErrorCode(rv))
 #define EXPECT_SECFailure(expectedError, rv) \
   EXPECT_EQ(::mozilla::pkix::test::SECStatusWithPRErrorCode(SECFailure, \
                                                             expectedError), \
             ::mozilla::pkix::test::SECStatusWithPRErrorCode(rv))
 
+class NSSTest : public ::testing::Test
+{
+public:
+  static void SetUpTestCase();
+
+protected:
+  NSSTest();
+
+  ScopedPLArenaPool arena;
+  static PRTime now;
+  static PRTime oneDayBeforeNow;
+  static PRTime oneDayAfterNow;
+};
+
+} } } // namespace mozilla::pkix::test
+
 #endif // mozilla_pkix__nssgtest_h
new file mode 100644
--- /dev/null
+++ b/security/pkix/test/gtest/pkix_cert_chain_length_tests.cpp
@@ -0,0 +1,250 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This code is made available to you under your choice of the following sets
+ * of licensing terms:
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+/* Copyright 2013 Mozilla Contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "nssgtest.h"
+#include "pkix/pkix.h"
+#include "prinit.h"
+#include "secerr.h"
+
+using namespace mozilla::pkix;
+using namespace mozilla::pkix::test;
+
+static bool
+CreateCert(PLArenaPool* arena, const char* issuerStr,
+           const char* subjectStr, EndEntityOrCA endEntityOrCA,
+           /*optional*/ SECKEYPrivateKey* issuerKey,
+           /*out*/ ScopedSECKEYPrivateKey& subjectKey,
+           /*out*/ ScopedCERTCertificate& subjectCert)
+{
+  static long serialNumberValue = 0;
+  ++serialNumberValue;
+  const SECItem* serialNumber(CreateEncodedSerialNumber(arena,
+                                                        serialNumberValue));
+  if (!serialNumber) {
+    return false;
+  }
+  const SECItem* issuerDER(ASCIIToDERName(arena, issuerStr));
+  if (!issuerDER) {
+    return false;
+  }
+  const SECItem* subjectDER(ASCIIToDERName(arena, subjectStr));
+  if (!subjectDER) {
+    return false;
+  }
+
+  const SECItem* extensions[2] = { nullptr, nullptr };
+  if (endEntityOrCA == EndEntityOrCA::MustBeCA) {
+    extensions[0] =
+      CreateEncodedBasicConstraints(arena, true, nullptr,
+                                    ExtensionCriticality::Critical);
+    if (!extensions[0]) {
+      return false;
+    }
+  }
+
+  SECItem* certDER(CreateEncodedCertificate(arena, v3, SEC_OID_SHA256,
+                                            serialNumber, issuerDER,
+                                            PR_Now() - ONE_DAY,
+                                            PR_Now() + ONE_DAY,
+                                            subjectDER, extensions,
+                                            issuerKey, SEC_OID_SHA256,
+                                            subjectKey));
+  if (!certDER) {
+    return false;
+  }
+  subjectCert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(), certDER,
+                                        nullptr, false, true);
+  return subjectCert.get() != nullptr;
+}
+
+class TestTrustDomain : public TrustDomain
+{
+public:
+  // The "cert chain tail" is a longish chain of certificates that is used by
+  // all of the tests here. We share this chain across all the tests in order
+  // to speed up the tests (generating keypairs for the certs is very slow).
+  bool SetUpCertChainTail()
+  {
+    static char const* const names[] = {
+        "CN=CA1 (Root)", "CN=CA2", "CN=CA3", "CN=CA4", "CN=CA5", "CN=CA6",
+        "CN=CA7"
+    };
+
+    static_assert(PR_ARRAY_SIZE(names) == PR_ARRAY_SIZE(certChainTail),
+                  "mismatch in sizes of names and certChainTail arrays");
+
+    ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
+    if (!arena) {
+      return false;
+    }
+
+    for (size_t i = 0; i < PR_ARRAY_SIZE(names); ++i) {
+      const char* issuerName = i == 0 ? names[0]
+                                      : certChainTail[i - 1]->subjectName;
+      if (!CreateCert(arena.get(), issuerName, names[i],
+                      EndEntityOrCA::MustBeCA, leafCAKey.get(), leafCAKey,
+                      certChainTail[i])) {
+        return false;
+      }
+    }
+
+    return true;
+  }
+
+private:
+  SECStatus GetCertTrust(EndEntityOrCA,
+                         const CertPolicyId&,
+                         const CERTCertificate* candidateCert,
+                         /*out*/ TrustLevel* trustLevel)
+  {
+    if (candidateCert == certChainTail[0].get()) {
+      *trustLevel = TrustLevel::TrustAnchor;
+    } else {
+      *trustLevel = TrustLevel::InheritsTrust;
+    }
+    return SECSuccess;
+  }
+
+  SECStatus FindPotentialIssuers(const SECItem* encodedIssuerName,
+                                 PRTime time,
+                                 /*out*/ ScopedCERTCertList& results)
+  {
+    results = CERT_CreateSubjectCertList(nullptr, CERT_GetDefaultCertDB(),
+                                         encodedIssuerName, time, true);
+    return SECSuccess;
+  }
+
+  SECStatus VerifySignedData(const CERTSignedData* signedData,
+                             const CERTCertificate* cert)
+  {
+    return ::mozilla::pkix::VerifySignedData(signedData, cert, nullptr);
+  }
+
+  SECStatus CheckRevocation(EndEntityOrCA, const CERTCertificate*,
+                            /*const*/ CERTCertificate*, PRTime,
+                            /*optional*/ const SECItem*)
+  {
+    return SECSuccess;
+  }
+
+  virtual SECStatus IsChainValid(const CERTCertList*)
+  {
+    return SECSuccess;
+  }
+
+  // We hold references to CERTCertificates in the cert chain tail so that we
+  // CERT_CreateSubjectCertList can find them.
+  ScopedCERTCertificate certChainTail[7];
+
+public:
+  ScopedSECKEYPrivateKey leafCAKey;
+  CERTCertificate* GetLeafeCACert() const
+  {
+    return certChainTail[PR_ARRAY_SIZE(certChainTail) - 1].get();
+  }
+};
+
+class pkix_cert_chain_length : public NSSTest
+{
+public:
+  static void SetUpTestCase()
+  {
+    NSSTest::SetUpTestCase();
+    // Initialize the tail of the cert chains we'll be using once, to make the
+    // tests run faster (generating the keys is slow).
+    if (!trustDomain.SetUpCertChainTail()) {
+      PR_Abort();
+    }
+  }
+
+protected:
+  static TestTrustDomain trustDomain;
+};
+
+/*static*/ TestTrustDomain pkix_cert_chain_length::trustDomain;
+
+TEST_F(pkix_cert_chain_length, MaxAcceptableCertChainLength)
+{
+  ScopedCERTCertList results;
+  ASSERT_SECSuccess(BuildCertChain(trustDomain, trustDomain.GetLeafeCACert(),
+                                   now, EndEntityOrCA::MustBeCA,
+                                   0, // key usage
+                                   KeyPurposeId::id_kp_serverAuth,
+                                   CertPolicyId::anyPolicy,
+                                   nullptr, // stapled OCSP response
+                                   results));
+
+  ScopedSECKEYPrivateKey privateKey;
+  ScopedCERTCertificate cert;
+  ASSERT_TRUE(CreateCert(arena.get(),
+                         trustDomain.GetLeafeCACert()->subjectName,
+                         "CN=Direct End-Entity",
+                         EndEntityOrCA::MustBeEndEntity,
+                         trustDomain.leafCAKey.get(), privateKey, cert));
+  ASSERT_SECSuccess(BuildCertChain(trustDomain, cert.get(), now,
+                                   EndEntityOrCA::MustBeEndEntity,
+                                   0, // key usage
+                                   KeyPurposeId::id_kp_serverAuth,
+                                   CertPolicyId::anyPolicy,
+                                   nullptr, // stapled OCSP response
+                                   results));
+}
+
+TEST_F(pkix_cert_chain_length, BeyondMaxAcceptableCertChainLength)
+{
+  ScopedCERTCertList results;
+
+  ScopedSECKEYPrivateKey caPrivateKey;
+  ScopedCERTCertificate caCert;
+  ASSERT_TRUE(CreateCert(arena.get(),
+                         trustDomain.GetLeafeCACert()->subjectName,
+                         "CN=CA Too Far", EndEntityOrCA::MustBeCA,
+                         trustDomain.leafCAKey.get(),
+                         caPrivateKey, caCert));
+  PR_SetError(0, 0);
+  ASSERT_SECFailure(SEC_ERROR_UNKNOWN_ISSUER,
+                    BuildCertChain(trustDomain, caCert.get(), now,
+                                   EndEntityOrCA::MustBeCA,
+                                   0, // key usage
+                                   KeyPurposeId::id_kp_serverAuth,
+                                   CertPolicyId::anyPolicy,
+                                   nullptr, // stapled OCSP response
+                                   results));
+
+  ScopedSECKEYPrivateKey privateKey;
+  ScopedCERTCertificate cert;
+  ASSERT_TRUE(CreateCert(arena.get(), caCert->subjectName,
+                         "CN=End-Entity Too Far",
+                         EndEntityOrCA::MustBeEndEntity,
+                         caPrivateKey.get(), privateKey, cert));
+  PR_SetError(0, 0);
+  ASSERT_SECFailure(SEC_ERROR_UNKNOWN_ISSUER,
+                    BuildCertChain(trustDomain, cert.get(), now,
+                                   EndEntityOrCA::MustBeEndEntity,
+                                   0, // key usage
+                                   KeyPurposeId::id_kp_serverAuth,
+                                   CertPolicyId::anyPolicy,
+                                   nullptr, // stapled OCSP response
+                                   results));
+}
--- a/security/pkix/test/gtest/pkix_ocsp_request_tests.cpp
+++ b/security/pkix/test/gtest/pkix_ocsp_request_tests.cpp
@@ -17,45 +17,35 @@
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
-#include <gtest/gtest.h>
-
-#include "nss.h"
+#include "nssgtest.h"
 #include "pkix/pkix.h"
 #include "pkixder.h"
-#include "pkixtestutil.h"
 #include "prerror.h"
 #include "secerr.h"
 
 using namespace mozilla::pkix;
 using namespace mozilla::pkix::test;
 
-class pkix_ocsp_request_tests : public ::testing::Test
+class pkix_ocsp_request_tests : public NSSTest
 {
 protected:
-  ScopedPLArenaPool arena;
   // These SECItems are allocated in arena, and so will be auto-cleaned.
   SECItem* unsupportedLongSerialNumber;
   SECItem* shortSerialNumber;
   SECItem* longestRequiredSerialNumber;
-  PRTime now;
-  PRTime oneDayBeforeNow;
-  PRTime oneDayAfterNow;
 
   void SetUp()
   {
-    NSS_NoDB_Init(nullptr);
-    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
-
     static const uint8_t UNSUPPORTED_LEN = 128; // must be larger than 127
     // tag + length + value is 1 + 2 + UNSUPPORTED_LEN
     unsupportedLongSerialNumber = SECITEM_AllocItem(arena.get(), nullptr,
                                                     1 + 2 + UNSUPPORTED_LEN);
     memset(unsupportedLongSerialNumber->data, 0,
            unsupportedLongSerialNumber->len);
     unsupportedLongSerialNumber->data[0] = der::INTEGER;
     // Encoding the length takes two bytes: one byte to indicate that a
@@ -74,47 +64,32 @@ protected:
     // tag + length + value is 1 + 1 + LONGEST_REQUIRED_LEN
     longestRequiredSerialNumber = SECITEM_AllocItem(arena.get(), nullptr,
                                     1 + 1 + LONGEST_REQUIRED_LEN);
     memset(longestRequiredSerialNumber->data, 0,
            longestRequiredSerialNumber->len);
     longestRequiredSerialNumber->data[0] = der::INTEGER;
     longestRequiredSerialNumber->data[1] = LONGEST_REQUIRED_LEN;
     longestRequiredSerialNumber->data[2] = 0x01; // value is 0x010000...00
-
-    now = PR_Now();
-    oneDayBeforeNow = now - ONE_DAY;
-    oneDayAfterNow = now + ONE_DAY;
-  }
-
-  const SECItem*
-  ASCIIToDERName(const char* cn)
-  {
-    ScopedPtr<CERTName, CERT_DestroyName> certName(CERT_AsciiToName(cn));
-    if (!certName) {
-      return nullptr;
-    }
-    return SEC_ASN1EncodeItem(arena.get(), nullptr, certName.get(),
-                              SEC_ASN1_GET(CERT_NameTemplate));
   }
 
   void MakeTwoCerts(const char* issuerCN, SECItem* issuerSerial,
                     /*out*/ ScopedCERTCertificate& issuer,
                     const char* childCN, SECItem* childSerial,
                     /*out*/ ScopedCERTCertificate& child)
   {
-    const SECItem* issuerNameDer = ASCIIToDERName(issuerCN);
+    const SECItem* issuerNameDer = ASCIIToDERName(arena.get(), issuerCN);
     ASSERT_TRUE(issuerNameDer);
     ScopedSECKEYPrivateKey issuerKey;
     SECItem* issuerCertDer(CreateEncodedCertificate(arena.get(), v3,
                              SEC_OID_SHA256, issuerSerial, issuerNameDer,
                              oneDayBeforeNow, oneDayAfterNow, issuerNameDer,
                              nullptr, nullptr, SEC_OID_SHA256, issuerKey));
     ASSERT_TRUE(issuerCertDer);
-    const SECItem* childNameDer = ASCIIToDERName(childCN);
+    const SECItem* childNameDer = ASCIIToDERName(arena.get(), childCN);
     ASSERT_TRUE(childNameDer);
     ScopedSECKEYPrivateKey childKey;
     SECItem* childDer(CreateEncodedCertificate(arena.get(), v3,
                         SEC_OID_SHA256, childSerial, issuerNameDer,
                         oneDayBeforeNow, oneDayAfterNow, childNameDer, nullptr,
                         issuerKey.get(), SEC_OID_SHA256, childKey));
     ASSERT_TRUE(childDer);
     issuer = CERT_NewTempCertificate(CERT_GetDefaultCertDB(), issuerCertDer,
--- a/security/pkix/test/gtest/pkixder_input_tests.cpp
+++ b/security/pkix/test/gtest/pkixder_input_tests.cpp
@@ -650,61 +650,57 @@ TEST_F(pkixder_input_tests, NestedOfWith
   ASSERT_EQ(Failure,
     NestedOf(input, SEQUENCE, INTEGER, EmptyAllowed::No,
              mozilla::pkix::bind(NestedOfHelper, mozilla::pkix::_1,
                                  mozilla::pkix::ref(readValues))));
   ASSERT_EQ(SEC_ERROR_BAD_DER, PR_GetError());
   ASSERT_EQ((size_t) 0, readValues.size());
 }
 
-TEST_F(pkixder_input_tests, MatchBytesAtEnd)
+TEST_F(pkixder_input_tests, MatchRestAtEnd)
 {
   Input input;
   static const uint8_t der[1] = { };
   ASSERT_EQ(Success, input.Init(der, 0));
   ASSERT_TRUE(input.AtEnd());
   static const uint8_t toMatch[] = { 1 };
-  ASSERT_FALSE(input.MatchBytes(toMatch));
+  ASSERT_FALSE(input.MatchRest(toMatch));
 }
 
-TEST_F(pkixder_input_tests, MatchBytes1Match)
+TEST_F(pkixder_input_tests, MatchRest1Match)
 {
   Input input;
   static const uint8_t der[] = { 1 };
   ASSERT_EQ(Success, input.Init(der, sizeof der));
   ASSERT_FALSE(input.AtEnd());
-  ASSERT_TRUE(input.MatchBytes(der));
-  ASSERT_TRUE(input.AtEnd());
+  ASSERT_TRUE(input.MatchRest(der));
 }
 
-TEST_F(pkixder_input_tests, MatchBytes1Mismatch)
+TEST_F(pkixder_input_tests, MatchRest1Mismatch)
 {
   Input input;
   static const uint8_t der[] = { 1 };
   ASSERT_EQ(Success, input.Init(der, sizeof der));
   static const uint8_t toMatch[] = { 2 };
-  ASSERT_FALSE(input.MatchBytes(toMatch));
+  ASSERT_FALSE(input.MatchRest(toMatch));
   ASSERT_FALSE(input.AtEnd());
 }
 
-TEST_F(pkixder_input_tests, MatchBytes2Match)
+TEST_F(pkixder_input_tests, MatchRest2WithTrailingByte)
 {
   Input input;
   static const uint8_t der[] = { 1, 2, 3 };
   ASSERT_EQ(Success, input.Init(der, sizeof der));
   static const uint8_t toMatch[] = { 1, 2 };
-  ASSERT_TRUE(input.MatchBytes(toMatch));
-  uint8_t followingByte;
-  ASSERT_EQ(Success, input.Read(followingByte));
-  ASSERT_EQ(3, followingByte);
+  ASSERT_FALSE(input.MatchRest(toMatch));
 }
 
-TEST_F(pkixder_input_tests, MatchBytes2Mismatch)
+TEST_F(pkixder_input_tests, MatchRest2Mismatch)
 {
   Input input;
   static const uint8_t der[] = { 1, 2, 3 };
   ASSERT_EQ(Success, input.Init(der, sizeof der));
   static const uint8_t toMatchMismatch[] = { 1, 3 };
-  ASSERT_FALSE(input.MatchBytes(toMatchMismatch));
-  ASSERT_TRUE(input.MatchBytes(der));
+  ASSERT_FALSE(input.MatchRest(toMatchMismatch));
+  ASSERT_TRUE(input.MatchRest(der));
 }
 
 } // unnamed namespace
--- a/security/pkix/test/lib/pkixtestutil.cpp
+++ b/security/pkix/test/lib/pkixtestutil.cpp
@@ -629,61 +629,72 @@ GenerateKeyPair(/*out*/ ScopedSECKEYPubl
   return SECFailure;
 }
 
 
 ///////////////////////////////////////////////////////////////////////////////
 // Certificates
 
 static SECItem* TBSCertificate(PLArenaPool* arena, long version,
-                               SECItem* serialNumber, SECOidTag signature,
+                               const SECItem* serialNumber, SECOidTag signature,
                                const SECItem* issuer, PRTime notBefore,
                                PRTime notAfter, const SECItem* subject,
                                const SECKEYPublicKey* subjectPublicKey,
                                /*optional*/ SECItem const* const* extensions);
 
 // Certificate  ::=  SEQUENCE  {
 //         tbsCertificate       TBSCertificate,
 //         signatureAlgorithm   AlgorithmIdentifier,
 //         signatureValue       BIT STRING  }
 SECItem*
 CreateEncodedCertificate(PLArenaPool* arena, long version,
-                         SECOidTag signature, SECItem* serialNumber,
+                         SECOidTag signature, const SECItem* serialNumber,
                          const SECItem* issuerNameDER, PRTime notBefore,
                          PRTime notAfter, const SECItem* subjectNameDER,
                          /*optional*/ SECItem const* const* extensions,
                          /*optional*/ SECKEYPrivateKey* issuerPrivateKey,
                          SECOidTag signatureHashAlg,
-                         /*out*/ ScopedSECKEYPrivateKey& privateKey)
+                         /*out*/ ScopedSECKEYPrivateKey& privateKeyResult)
 {
   PR_ASSERT(arena);
   PR_ASSERT(issuerNameDER);
   PR_ASSERT(subjectNameDER);
   if (!arena || !issuerNameDER || !subjectNameDER) {
     PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
     return nullptr;
   }
 
+  // It may be the case that privateKeyResult refers to the
+  // ScopedSECKEYPrivateKey that owns issuerPrivateKey; thus, we can't set
+  // privateKeyResult until after we're done with issuerPrivateKey.
   ScopedSECKEYPublicKey publicKey;
-  if (GenerateKeyPair(publicKey, privateKey) != SECSuccess) {
+  ScopedSECKEYPrivateKey privateKeyTemp;
+  if (GenerateKeyPair(publicKey, privateKeyTemp) != SECSuccess) {
     return nullptr;
   }
 
   SECItem* tbsCertificate(TBSCertificate(arena, version, serialNumber,
                                          signature, issuerNameDER, notBefore,
                                          notAfter, subjectNameDER,
                                          publicKey.get(), extensions));
   if (!tbsCertificate) {
     return nullptr;
   }
 
-  return MaybeLogOutput(SignedData(arena, tbsCertificate,
-                                   issuerPrivateKey ? issuerPrivateKey
-                                                    : privateKey.get(),
-                                   signatureHashAlg, false, nullptr), "cert");
+  SECItem*
+    result(MaybeLogOutput(SignedData(arena, tbsCertificate,
+                                     issuerPrivateKey ? issuerPrivateKey
+                                                      : privateKeyTemp.get(),
+                                     signatureHashAlg, false, nullptr),
+                          "cert"));
+  if (!result) {
+    return nullptr;
+  }
+  privateKeyResult = privateKeyTemp.release();
+  return result;
 }
 
 // TBSCertificate  ::=  SEQUENCE  {
 //      version         [0]  Version DEFAULT v1,
 //      serialNumber         CertificateSerialNumber,
 //      signature            AlgorithmIdentifier,
 //      issuer               Name,
 //      validity             Validity,
@@ -692,17 +703,17 @@ CreateEncodedCertificate(PLArenaPool* ar
 //      issuerUniqueID  [1]  IMPLICIT UniqueIdentifier OPTIONAL,
 //                           -- If present, version MUST be v2 or v3
 //      subjectUniqueID [2]  IMPLICIT UniqueIdentifier OPTIONAL,
 //                           -- If present, version MUST be v2 or v3
 //      extensions      [3]  Extensions OPTIONAL
 //                           -- If present, version MUST be v3 --  }
 static SECItem*
 TBSCertificate(PLArenaPool* arena, long versionValue,
-               SECItem* serialNumber, SECOidTag signatureOidTag,
+               const SECItem* serialNumber, SECOidTag signatureOidTag,
                const SECItem* issuer, PRTime notBeforeTime,
                PRTime notAfterTime, const SECItem* subject,
                const SECKEYPublicKey* subjectPublicKey,
                /*optional*/ SECItem const* const* extensions)
 {
   PR_ASSERT(arena);
   PR_ASSERT(issuer);
   PR_ASSERT(subject);
@@ -812,44 +823,63 @@ TBSCertificate(PLArenaPool* arena, long 
     if (output.Add(extensionsWrapped) != der::Success) {
       return nullptr;
     }
   }
 
   return output.Squash(arena, der::SEQUENCE);
 }
 
+const SECItem*
+ASCIIToDERName(PLArenaPool* arena, const char* cn)
+{
+  ScopedPtr<CERTName, CERT_DestroyName> certName(CERT_AsciiToName(cn));
+  if (!certName) {
+    return nullptr;
+  }
+  return SEC_ASN1EncodeItem(arena, nullptr, certName.get(),
+                            SEC_ASN1_GET(CERT_NameTemplate));
+}
+
+SECItem*
+CreateEncodedSerialNumber(PLArenaPool* arena, long serialNumberValue)
+{
+  return Integer(arena, serialNumberValue);
+}
+
 // BasicConstraints ::= SEQUENCE {
 //         cA                      BOOLEAN DEFAULT FALSE,
 //         pathLenConstraint       INTEGER (0..MAX) OPTIONAL }
 SECItem*
 CreateEncodedBasicConstraints(PLArenaPool* arena, bool isCA,
-                              long pathLenConstraintValue,
+                              /*optional*/ long* pathLenConstraintValue,
                               ExtensionCriticality criticality)
 {
   PR_ASSERT(arena);
   if (!arena) {
     PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
     return nullptr;
   }
 
   Output value;
 
   if (isCA) {
     if (value.Add(Boolean(arena, true)) != der::Success) {
       return nullptr;
     }
   }
 
-  SECItem* pathLenConstraint(Integer(arena, pathLenConstraintValue));
-  if (!pathLenConstraint) {
-    return nullptr;
-  }
-  if (value.Add(pathLenConstraint) != der::Success) {
-    return nullptr;
+  if (pathLenConstraintValue) {
+    SECItem* pathLenConstraint(Integer(arena, *pathLenConstraintValue));
+    if (!pathLenConstraint) {
+      return nullptr;
+    }
+    if (value.Add(pathLenConstraint) != der::Success) {
+      return nullptr;
+    }
   }
 
   return Extension(arena, SEC_OID_X509_BASIC_CONSTRAINTS, criticality, value);
 }
 
 // ExtKeyUsageSyntax ::= SEQUENCE SIZE (1..MAX) OF KeyPurposeId
 // KeyPurposeId ::= OBJECT IDENTIFIER
 SECItem*
--- a/security/pkix/test/lib/pkixtestutil.h
+++ b/security/pkix/test/lib/pkixtestutil.h
@@ -57,16 +57,19 @@ typedef mozilla::pkix::ScopedPtr<SECKEYP
 
 FILE* OpenFile(const char* dir, const char* filename, const char* mode);
 
 extern const PRTime ONE_DAY;
 
 SECStatus GenerateKeyPair(/*out*/ ScopedSECKEYPublicKey& publicKey,
                           /*out*/ ScopedSECKEYPrivateKey& privateKey);
 
+// The result will be owned by the arena
+const SECItem* ASCIIToDERName(PLArenaPool* arena, const char* cn);
+
 ///////////////////////////////////////////////////////////////////////////////
 // Encode Certificates
 
 enum Version { v1 = 0, v2 = 1, v3 = 2 };
 
 // serialNumber is assumed to be the DER encoding of an INTEGER.
 //
 // If extensions is null, then no extensions will be encoded. Otherwise,
@@ -75,30 +78,33 @@ enum Version { v1 = 0, v2 = 1, v3 = 2 };
 //
 // If issuerPrivateKey is null, then the certificate will be self-signed.
 // Parameter order is based on the order of the attributes of the certificate
 // in RFC 5280.
 //
 // The return value, if non-null, is owned by the arena in the context and
 // MUST NOT be freed.
 SECItem* CreateEncodedCertificate(PLArenaPool* arena, long version,
-                                  SECOidTag signature, SECItem* serialNumber,
+                                  SECOidTag signature,
+                                  const SECItem* serialNumber,
                                   const SECItem* issuerNameDER,
                                   PRTime notBefore, PRTime notAfter,
                                   const SECItem* subjectNameDER,
                      /*optional*/ SECItem const* const* extensions,
                      /*optional*/ SECKEYPrivateKey* issuerPrivateKey,
                                   SECOidTag signatureHashAlg,
                           /*out*/ ScopedSECKEYPrivateKey& privateKey);
 
+SECItem* CreateEncodedSerialNumber(PLArenaPool* arena, long value);
+
 MOZILLA_PKIX_ENUM_CLASS ExtensionCriticality { NotCritical = 0, Critical = 1 };
 
 // The return value, if non-null, is owned by the arena and MUST NOT be freed.
 SECItem* CreateEncodedBasicConstraints(PLArenaPool* arena, bool isCA,
-                                       long pathLenConstraint,
+                                       /*optional*/ long* pathLenConstraint,
                                        ExtensionCriticality criticality);
 
 // ekus must be non-null and must must point to a SEC_OID_UNKNOWN-terminated
 // array of SECOidTags. If the first item of the array is SEC_OID_UNKNOWN then
 // an empty EKU extension will be encoded.
 //
 // The return value, if non-null, is owned by the arena and MUST NOT be freed.
 SECItem* CreateEncodedEKUExtension(PLArenaPool* arena,
--- a/testing/gtest/gtest/src/gtest-internal-inl.h
+++ b/testing/gtest/gtest/src/gtest-internal-inl.h
@@ -198,17 +198,16 @@ class GTestFlagSaver {
   String color_;
   String death_test_style_;
   bool death_test_use_fork_;
   String filter_;
   String internal_run_death_test_;
   bool list_tests_;
   String output_;
   bool print_time_;
-  bool pretty_;
   internal::Int32 random_seed_;
   internal::Int32 repeat_;
   bool shuffle_;
   internal::Int32 stack_trace_depth_;
   String stream_result_to_;
   bool throw_on_failure_;
 } GTEST_ATTRIBUTE_UNUSED_;
 
--- a/testing/gtest/mozilla/GTestRunner.cpp
+++ b/testing/gtest/mozilla/GTestRunner.cpp
@@ -96,17 +96,17 @@ int RunGTestFunc()
       std::cerr << "Setting up crash reporting" << std::endl;
 
       nsCOMPtr<nsIProperties> dirsvc =
           do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID);
       nsCOMPtr<nsIFile> cwd;
       nsresult rv = dirsvc->Get(NS_OS_CURRENT_WORKING_DIR,
                        NS_GET_IID(nsIFile),
                        getter_AddRefs(cwd));
-      MOZ_ASSERT(NS_SUCCEEDED(rv));
+      MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
       crashreporter->SetEnabled(true);
       crashreporter->SetMinidumpPath(cwd);
     }
   }
 #endif
 
   return RUN_ALL_TESTS();
 }
--- a/testing/marionette/client/marionette/tests/unit/test_capabilities.py
+++ b/testing/marionette/client/marionette/tests/unit/test_capabilities.py
@@ -20,28 +20,24 @@ class TestCapabilities(MarionetteTestCas
 
         self.assertEqual(self.caps["browserName"], self.appinfo["name"])
         self.assertEqual(self.caps["browserVersion"], self.appinfo["version"])
         self.assertEqual(self.caps["platformName"], self.appinfo["OS"].upper())
         self.assertEqual(self.caps["platformVersion"],
                          self.appinfo["platformVersion"])
 
     def test_supported_features(self):
-        self.assertIn("cssSelectorsEnabled", self.caps)
         self.assertIn("handlesAlerts", self.caps)
-        self.assertIn("javascriptEnabled", self.caps)
         self.assertIn("nativeEvents", self.caps)
         self.assertIn("rotatable", self.caps)
         self.assertIn("secureSsl", self.caps)
         self.assertIn("takesElementScreenshot", self.caps)
         self.assertIn("takesScreenshot", self.caps)
 
-        self.assertTrue(self.caps["cssSelectorsEnabled"])
         self.assertFalse(self.caps["handlesAlerts"])
-        self.assertTrue(self.caps["javascriptEnabled"])
         self.assertFalse(self.caps["nativeEvents"])
         self.assertEqual(self.caps["rotatable"], self.appinfo["name"] == "B2G")
         self.assertFalse(self.caps["secureSsl"])
         self.assertTrue(self.caps["takesElementScreenshot"])
         self.assertTrue(self.caps["takesScreenshot"])
 
     def test_selenium2_compat(self):
         self.assertIn("platform", self.caps)
--- a/testing/marionette/client/marionette/tests/unit/test_session.py
+++ b/testing/marionette/client/marionette/tests/unit/test_session.py
@@ -18,17 +18,15 @@ class TestSession(marionette_test.Marion
         self.assertIsNotNone(self.marionette.session)
 
         # Required capabilities mandated by WebDriver spec
         self.assertIn("browserName", caps)
         self.assertIn("platformName", caps)
         self.assertIn("platformVersion", caps)
 
         # Optional capabilities we want Marionette to support
-        self.assertIn("cssSelectorsEnabled", caps)
         self.assertIn("device", caps)
         self.assertIn("handlesAlerts", caps)
-        self.assertIn("javascriptEnabled", caps)
         self.assertIn("rotatable", caps)
         self.assertIn("takesScreenshot", caps)
         self.assertIn("version", caps)
 
 
--- a/testing/marionette/client/marionette/tests/unit/test_text.py
+++ b/testing/marionette/client/marionette/tests/unit/test_text.py
@@ -21,18 +21,25 @@ class TestText(MarionetteTestCase):
         l.clear()
         self.assertEqual("", self.marionette.execute_script("return arguments[0].value;", [l]))
 
     def test_sendKeys(self):
         test_html = self.marionette.absolute_url("test.html")
         self.marionette.navigate(test_html)
         l = self.marionette.find_element("name", "myInput")
         self.assertEqual("asdf", self.marionette.execute_script("return arguments[0].value;", [l]))
+
+        # Set caret position to the middle of the input text.
+        self.marionette.execute_script(
+            """var el = arguments[0];
+            el.selectionStart = el.selectionEnd = el.value.length / 2;""",
+            script_args=[l])
+
         l.send_keys("o")
-        self.assertEqual("asdfo", self.marionette.execute_script("return arguments[0].value;", [l]))
+        self.assertEqual("asodf", self.marionette.execute_script("return arguments[0].value;", [l]))
 
     def test_send_keys_to_type_input(self):
         test_html = self.marionette.absolute_url("html5/test_html_inputs.html")
         self.marionette.navigate(test_html)
         num_input = self.marionette.find_element('id', 'number')
         self.assertEqual("", self.marionette.execute_script("return arguments[0].value", [num_input]))
         num_input.send_keys("1234")
         self.assertEqual('1234', self.marionette.execute_script("return arguments[0].value", [num_input]))
@@ -207,9 +214,8 @@ class TestText(MarionetteTestCase):
         self.assertEqual(element.get_attribute("value"), numericLineCharsNonShifted)
 
     def testShouldTypeAnInteger(self):
         test_html = self.marionette.absolute_url("javascriptPage.html")
         self.marionette.navigate(test_html)
         element = self.marionette.find_element("id", "keyReporter")
         element.send_keys(1234)
         self.assertEqual(element.get_attribute("value"), "1234")
-
--- a/testing/marionette/marionette-listener.js
+++ b/testing/marionette/marionette-listener.js
@@ -1555,21 +1555,16 @@ function isElementSelected(msg) {
 /**
  * Send keys to element
  */
 function sendKeysToElement(msg) {
   let command_id = msg.json.command_id;
 
   let el = elementManager.getKnownElement(msg.json.id, curFrame);
   if (checkVisible(el)) {
-    if (el.mozIsTextField && el.mozIsTextField(false)) {
-      var currentTextLength = el.value ? el.value.length : 0;
-      el.selectionStart = currentTextLength;
-      el.selectionEnd = currentTextLength;
-    }
     el.focus();
     var value = msg.json.value.join("");
     let hasShift = null;
     let hasCtrl = null;
     let hasAlt = null;
     let hasMeta = null;
     for (var i = 0; i < value.length; i++) {
       let upper = value.charAt(i).toUpperCase();
--- a/testing/marionette/marionette-server.js
+++ b/testing/marionette/marionette-server.js
@@ -566,19 +566,17 @@ MarionetteServerConnection.prototype = {
     let caps = {
       // Mandated capabilities
       "browserName": appName,
       "browserVersion": Services.appinfo.version,
       "platformName": platformName,
       "platformVersion": Services.appinfo.platformVersion,
 
       // Supported features
-      "cssSelectorsEnabled": true,
       "handlesAlerts": false,
-      "javascriptEnabled": true,
       "nativeEvents": false,
       "rotatable": isB2G,
       "secureSsl": false,
       "takesElementScreenshot": true,
       "takesScreenshot": true,
 
       // Selenium 2 compat
       "platform": platformName,
--- a/widget/cocoa/nsAppShell.mm
+++ b/widget/cocoa/nsAppShell.mm
@@ -479,20 +479,27 @@ nsAppShell::ProcessNextNativeEvent(bool 
       // loop, though it does queue up any newly available events from the
       // window server.
       EventRef currentEvent = AcquireFirstMatchingEventInQueue(currentEventQueue, 0, NULL,
                                                                kEventQueueOptionsNone);
       if (!currentEvent) {
         continue;
       }
       EventAttributes attrs = GetEventAttributes(currentEvent);
+      UInt32 eventKind = GetEventKind(currentEvent);
+      bool osCocoaEvent =
+        ((GetEventClass(currentEvent) == 'cgs ') &&
+         ((eventKind == NSAppKitDefined) || (eventKind == NSSystemDefined)));
       // If attrs is kEventAttributeUserEvent or kEventAttributeMonitored
       // (i.e. a user input event), we shouldn't process it here while
-      // aMayWait is false.
-      if (attrs != kEventAttributeNone) {
+      // aMayWait is false.  Likewise if currentEvent will eventually be
+      // turned into an OS-defined Cocoa event.  Doing otherwise risks
+      // doing too much work here, and preventing the event from being
+      // properly processed as a Cocoa event.
+      if ((attrs != kEventAttributeNone) || osCocoaEvent) {
         // Since we can't process the next event here (while aMayWait is false),
         // we want moreEvents to be false on return.
         eventProcessed = false;
         // This call to ReleaseEvent() matches a call to RetainEvent() in
         // AcquireFirstMatchingEventInQueue() above.
         ReleaseEvent(currentEvent);
         break;
       }
--- a/xpcom/build/nsWindowsDllInterceptor.h
+++ b/xpcom/build/nsWindowsDllInterceptor.h
@@ -350,19 +350,24 @@ protected:
         // various MOVs
         unsigned char b = origBytes[nBytes+1];
         if (((b & 0xc0) == 0xc0) ||
             (((b & 0xc0) == 0x00) &&
              ((b & 0x07) != 0x04) && ((b & 0x07) != 0x05)))
         {
           // REG=r, R/M=r or REG=r, R/M=[r]
           nBytes += 2;
-        } else if (((b & 0xc0) == 0x40) && ((b & 0x38) != 0x20)) {
-          // REG=r, R/M=[r + disp8]
-          nBytes += 3;
+        } else if ((b & 0xc0) == 0x40) {
+          if ((b & 0x07) == 0x04) {
+            // REG=r, R/M=[SIB + disp8]
+            nBytes += 4;
+          } else {
+            // REG=r, R/M=[r + disp8]
+            nBytes += 3;
+          }
         } else {
           // complex MOV, bail
           return;
         }
       } else if (origBytes[nBytes] == 0xB8) {
         // MOV 0xB8: http://ref.x86asm.net/coder32.html#xB8
         nBytes += 5;
       } else if (origBytes[nBytes] == 0x83) {