Merge mozilla-central to inbound. a=merge CLOSED TREE
authorshindli <shindli@mozilla.com>
Tue, 31 Jul 2018 13:09:14 +0300
changeset 429370 b28bc7c7b6f8f178d8441c51c2cafa2fabe29c5f
parent 429369 cbaa8e0834cad35084d2675d9400b3e28a47ab7c (current diff)
parent 429332 0d72c7996d60a7c07e35c5f90d78b02a47d17460 (diff)
child 429371 bfe3c2d7de59914f0f1ccd3bbe7dbf952c1ed73c
push id34362
push userbtara@mozilla.com
push dateTue, 31 Jul 2018 18:30:34 +0000
treeherdermozilla-central@5a5fb40fb922 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone63.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to inbound. a=merge CLOSED TREE
dom/base/nsDOMWindowUtils.cpp
--- a/Makefile.in
+++ b/Makefile.in
@@ -273,17 +273,23 @@ symbolsfullarchive: prepsymbolsarchive
 
 .PHONY: symbolsarchive
 symbolsarchive: prepsymbolsarchive
 	$(RM) '$(DIST)/$(PKG_PATH)$(SYMBOL_ARCHIVE_BASENAME).zip'
 	$(call py_action,symbols_archive,'$(DIST)/$(PKG_PATH)$(SYMBOL_ARCHIVE_BASENAME).zip' \
                                      $(abspath $(DIST)/crashreporter-symbols))
 
 ifdef MOZ_CRASHREPORTER
+# Set MOZ_DISABLE_FULL_SYMBOLS to disable generation and upload of the full
+# crashreporter symbols archives
+ifdef MOZ_DISABLE_FULL_SYMBOLS
+buildsymbols: symbolsarchive
+else
 buildsymbols: symbolsfullarchive symbolsarchive
+endif # MOZ_DISABLE_FULL_SYMBOLS
 else
 buildsymbols:
 endif
 
 uploadsymbols:
 ifdef MOZ_CRASHREPORTER
 	$(PYTHON) -u $(topsrcdir)/toolkit/crashreporter/tools/upload_symbols.py '$(DIST)/$(PKG_PATH)$(SYMBOL_FULL_ARCHIVE_BASENAME).zip'
 endif
--- a/accessible/windows/msaa/XULMenuAccessibleWrap.cpp
+++ b/accessible/windows/msaa/XULMenuAccessibleWrap.cpp
@@ -14,17 +14,17 @@ using namespace mozilla::a11y;
 
 XULMenuitemAccessibleWrap::
   XULMenuitemAccessibleWrap(nsIContent* aContent, DocAccessible* aDoc) :
   XULMenuitemAccessible(aContent, aDoc)
 {
 }
 
 ENameValueFlag
-XULMenuitemAccessibleWrap::Name(nsString& aName)
+XULMenuitemAccessibleWrap::Name(nsString& aName) const
 {
   // XXX This should be done in MSAA's get_accName() so that Accessible::Name()]
   // provides the same results on all platforms
   XULMenuitemAccessible::Name(aName);
   if (aName.IsEmpty())
     return eNameOK;
 
   nsAutoString accel;
--- a/accessible/windows/msaa/XULMenuAccessibleWrap.h
+++ b/accessible/windows/msaa/XULMenuAccessibleWrap.h
@@ -13,15 +13,15 @@ namespace a11y {
 
 class XULMenuitemAccessibleWrap : public XULMenuitemAccessible
 {
 public:
   XULMenuitemAccessibleWrap(nsIContent* aContent, DocAccessible* aDoc);
   virtual ~XULMenuitemAccessibleWrap() {}
 
   // nsIAccessible
-  virtual mozilla::a11y::ENameValueFlag Name(nsString& aName);
+  virtual mozilla::a11y::ENameValueFlag Name(nsString& aName) const override;
 };
 
 } // namespace a11y
 } // namespace mozilla
 
 #endif
--- a/browser/components/preferences/in-content/main.js
+++ b/browser/components/preferences/in-content/main.js
@@ -230,20 +230,25 @@ if (AppConstants.MOZ_UPDATER) {
     ]);
   }
 }
 
 // A promise that resolves when the list of application handlers is loaded.
 // We store this in a global so tests can await it.
 var promiseLoadHandlersList;
 
-// Load the preferences string bundle for a given locale.
+// Load the preferences string bundle for a given locale with fallbacks.
 function getBundleForLocale(locale) {
+  let locales = Array.from(new Set([
+    locale,
+    ...Services.locale.getRequestedLocales(),
+    Services.locale.lastFallbackLocale,
+  ]));
   function generateContexts(resourceIds) {
-    return L10nRegistry.generateContexts([locale], resourceIds);
+    return L10nRegistry.generateContexts(locales, resourceIds);
   }
   return new Localization([
     "browser/preferences/preferences.ftl",
     "branding/brand.ftl",
   ], generateContexts);
 }
 
 var gNodeToObjectMap = new WeakMap();
--- a/devtools/server/actors/inspector/custom-element-watcher.js
+++ b/devtools/server/actors/inspector/custom-element-watcher.js
@@ -102,17 +102,17 @@ class CustomElementWatcher extends Event
 
     const customElements = doc.defaultView.customElements;
     return isValidName && !customElements.get(name);
   }
 
   _onCustomElementDefined(event) {
     const doc = event.target;
     const registry = doc.defaultView.customElements;
-    const registryMap = this.watchedRegistries.get(registry);
+    const registryMap = this._getMapForRegistry(registry);
 
     const name = event.detail;
     const nodeActors = registryMap.get(name);
 
     this.emit("element-defined", nodeActors);
     registryMap.delete(name);
   }
 
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -3647,16 +3647,44 @@ GetTargetFrame(const Element* aElement, 
       frame = nsLayoutUtils::GetAfterFrame(aElement);
     } else {
       return Err(NS_ERROR_INVALID_ARG);
     }
   }
   return frame;
 }
 
+static OMTAValue
+GetOMTAValue(nsIFrame* aFrame,
+             DisplayItemType aDisplayItemKey,
+             WebRenderBridgeChild* aWebRenderBridgeChild)
+{
+  OMTAValue value = mozilla::null_t();
+
+  Layer* layer =
+    FrameLayerBuilder::GetDedicatedLayer(aFrame, aDisplayItemKey);
+  if (layer) {
+    ShadowLayerForwarder* forwarder = layer->Manager()->AsShadowForwarder();
+    if (forwarder && forwarder->HasShadowManager()) {
+      forwarder->GetShadowManager()->
+        SendGetAnimationValue(layer->GetCompositorAnimationsId(), &value);
+    }
+  } else if (aWebRenderBridgeChild) {
+    RefPtr<WebRenderAnimationData> animationData =
+      GetWebRenderUserData<WebRenderAnimationData>(aFrame,
+                                                   (uint32_t)aDisplayItemKey);
+    if (animationData) {
+      aWebRenderBridgeChild->SendGetAnimationValue(
+        animationData->GetAnimationInfo().GetCompositorAnimationsId(),
+        &value);
+    }
+  }
+  return value;
+}
+
 NS_IMETHODIMP
 nsDOMWindowUtils::GetOMTAStyle(Element* aElement,
                                const nsAString& aProperty,
                                const nsAString& aPseudoElement,
                                nsAString& aResult)
 {
   if (!aElement) {
     return NS_ERROR_INVALID_ARG;
@@ -3671,66 +3699,29 @@ nsDOMWindowUtils::GetOMTAStyle(Element* 
   RefPtr<nsROCSSPrimitiveValue> cssValue = nullptr;
   if (frame && nsLayoutUtils::AreAsyncAnimationsEnabled()) {
     RefPtr<LayerManager> widgetLayerManager;
     if (nsIWidget* widget = GetWidget()) {
       widgetLayerManager = widget->GetLayerManager();
     }
 
     if (aProperty.EqualsLiteral("opacity")) {
-      float value = 0;
-      bool hadAnimatedOpacity = false;
-
-      Layer* layer =
-        FrameLayerBuilder::GetDedicatedLayer(frame, DisplayItemType::TYPE_OPACITY);
-      if (layer) {
-        ShadowLayerForwarder* forwarder = layer->Manager()->AsShadowForwarder();
-        if (forwarder && forwarder->HasShadowManager()) {
-          forwarder->GetShadowManager()->
-            SendGetAnimationOpacity(layer->GetCompositorAnimationsId(),
-                                    &value,
-                                    &hadAnimatedOpacity);
-        }
-      } else if (WebRenderBridgeChild* wrbc = GetWebRenderBridge()) {
-        RefPtr<WebRenderAnimationData> animationData =
-            GetWebRenderUserData<WebRenderAnimationData>(frame, (uint32_t)DisplayItemType::TYPE_OPACITY);
-        if (animationData) {
-          wrbc->SendGetAnimationOpacity(
-              animationData->GetAnimationInfo().GetCompositorAnimationsId(),
-              &value,
-              &hadAnimatedOpacity);
-        }
-      }
-      if (hadAnimatedOpacity) {
+      OMTAValue value = GetOMTAValue(frame,
+                                     DisplayItemType::TYPE_OPACITY,
+                                     GetWebRenderBridge());
+      if (value.type() == OMTAValue::Tfloat) {
         cssValue = new nsROCSSPrimitiveValue;
-        cssValue->SetNumber(value);
+        cssValue->SetNumber(value.get_float());
       }
     } else if (aProperty.EqualsLiteral("transform")) {
-      MaybeTransform transform;
-
-      Layer* layer =
-        FrameLayerBuilder::GetDedicatedLayer(frame, DisplayItemType::TYPE_TRANSFORM);
-      if (layer) {
-        ShadowLayerForwarder* forwarder = layer->Manager()->AsShadowForwarder();
-        if (forwarder && forwarder->HasShadowManager()) {
-          forwarder->GetShadowManager()->
-            SendGetAnimationTransform(layer->GetCompositorAnimationsId(), &transform);
-        }
-      } else if (WebRenderBridgeChild* wrbc = GetWebRenderBridge()) {
-        RefPtr<WebRenderAnimationData> animationData =
-            GetWebRenderUserData<WebRenderAnimationData>(frame, (uint32_t)DisplayItemType::TYPE_TRANSFORM);
-        if (animationData) {
-          wrbc->SendGetAnimationTransform(
-              animationData->GetAnimationInfo().GetCompositorAnimationsId(),
-              &transform);
-        }
-      }
-      if (transform.type() == MaybeTransform::TMatrix4x4) {
-        Matrix4x4 matrix = transform.get_Matrix4x4();
-        cssValue = nsComputedDOMStyle::MatrixToCSSValue(matrix);
+      OMTAValue value = GetOMTAValue(frame,
+                                     DisplayItemType::TYPE_TRANSFORM,
+                                     GetWebRenderBridge());
+      if (value.type() == OMTAValue::TMatrix4x4) {
+        cssValue = nsComputedDOMStyle::MatrixToCSSValue(value.get_Matrix4x4());
       }
     }
   }
 
   if (cssValue) {
     nsString text;
     ErrorResult rv;
     cssValue->GetCssText(text, rv);
--- a/dom/base/nsGlobalWindowInner.cpp
+++ b/dom/base/nsGlobalWindowInner.cpp
@@ -2058,16 +2058,17 @@ nsGlobalWindowInner::PostHandleEvent(Eve
              aVisitor.mEvent->IsTrusted()) {
 
     // If any VR display presentation is active at unload, the next page
     // will receive a vrdisplayactive event to indicate that it should
     // immediately begin vr presentation. This should occur when navigating
     // forwards, navigating backwards, and on page reload.
     for (const auto& display : mVRDisplays) {
       if (display->IsPresenting()) {
+        display->StartVRNavigation();
         // Save this VR display ID to trigger vrdisplayactivate event
         // after the next load event.
         nsGlobalWindowOuter* outer = GetOuterWindowInternal();
         if (outer) {
           outer->SetAutoActivateVRDisplayID(display->DisplayId());
         }
 
         // XXX The WebVR 1.1 spec does not define which of multiple VR
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -95,16 +95,17 @@ class VRLayerChild;
 } // namespace gfx
 
 namespace gl {
 class MozFramebuffer;
 } // namespace gl
 
 namespace webgl {
 class AvailabilityRunnable;
+struct CachedDrawFetchLimits;
 struct LinkedProgramInfo;
 class ShaderValidator;
 class TexUnpackBlob;
 struct UniformInfo;
 struct UniformBlockInfo;
 } // namespace webgl
 
 WebGLTexelFormat GetWebGLTexelFormat(TexInternalFormat format);
@@ -275,18 +276,18 @@ public:
 
 class WebGLContext
     : public nsICanvasRenderingContextInternal
     , public nsSupportsWeakReference
     , public WebGLContextUnchecked
     , public nsWrapperCache
 {
     friend class ScopedDrawCallWrapper;
-    friend class ScopedDrawHelper;
     friend class ScopedDrawWithTransformFeedback;
+    friend class ScopedFakeVertexAttrib0;
     friend class ScopedFBRebinder;
     friend class WebGL2Context;
     friend class WebGLContextUserData;
     friend class WebGLExtensionCompressedTextureASTC;
     friend class WebGLExtensionCompressedTextureATC;
     friend class WebGLExtensionCompressedTextureES3;
     friend class WebGLExtensionCompressedTextureETC1;
     friend class WebGLExtensionCompressedTexturePVRTC;
@@ -297,16 +298,19 @@ class WebGLContext
     friend class WebGLExtensionDrawBuffers;
     friend class WebGLExtensionLoseContext;
     friend class WebGLExtensionVertexArray;
     friend class WebGLMemoryTracker;
     friend class webgl::AvailabilityRunnable;
     friend struct webgl::LinkedProgramInfo;
     friend struct webgl::UniformBlockInfo;
 
+    friend const webgl::CachedDrawFetchLimits*
+        ValidateDraw(WebGLContext*, const char*, GLenum, uint32_t);
+
     enum {
         UNPACK_FLIP_Y_WEBGL = 0x9240,
         UNPACK_PREMULTIPLY_ALPHA_WEBGL = 0x9241,
         // We throw InvalidOperation in TexImage if we fail to use GPU fast-path
         // for texture copy when it is set to true, only for debug purpose.
         UNPACK_REQUIRE_FASTPATH = 0x10001,
         CONTEXT_LOST_WEBGL = 0x9242,
         UNPACK_COLORSPACE_CONVERSION_WEBGL = 0x9243,
@@ -1406,21 +1410,18 @@ public:
         const bool isFuncInt = false;
         VertexAttribAnyPointer(funcName, isFuncInt, index, size, type, normalized, stride,
                                byteOffset);
     }
 
     void VertexAttribDivisor(GLuint index, GLuint divisor);
 
 private:
-    bool DrawArrays_check(const char* funcName, GLint first, GLsizei vertCount,
-                          GLsizei instanceCount, Maybe<uint32_t>* out_lastVert);
-    bool DrawElements_check(const char* funcName, GLsizei indexCount, GLenum type,
-                            WebGLintptr byteOffset, GLsizei instanceCount,
-                            Maybe<uint32_t>* out_lastVert);
+    WebGLBuffer* DrawElements_check(const char* funcName, GLsizei indexCount, GLenum type,
+                                    WebGLintptr byteOffset, GLsizei instanceCount);
     void Draw_cleanup(const char* funcName);
 
     void VertexAttrib1fv_base(GLuint index, uint32_t arrayLength,
                               const GLfloat* ptr);
     void VertexAttrib2fv_base(GLuint index, uint32_t arrayLength,
                               const GLfloat* ptr);
     void VertexAttrib3fv_base(GLuint index, uint32_t arrayLength,
                               const GLfloat* ptr);
@@ -1428,17 +1429,17 @@ private:
                               const GLfloat* ptr);
 
     bool BindArrayAttribToLocation0(WebGLProgram* prog);
 
 // -----------------------------------------------------------------------------
 // PROTECTED
 protected:
     WebGLVertexAttrib0Status WhatDoesVertexAttrib0Need() const;
-    bool DoFakeVertexAttrib0(const char* funcName, GLuint vertexCount);
+    bool DoFakeVertexAttrib0(const char* funcName, uint64_t vertexCount);
     void UndoFakeVertexAttrib0();
 
     CheckedUint32 mGeneration;
 
     WebGLContextOptions mOptions;
 
     bool mInvalidated;
     bool mCapturedFrameInvalidated;
--- a/dom/canvas/WebGLContextDraw.cpp
+++ b/dom/canvas/WebGLContextDraw.cpp
@@ -281,161 +281,142 @@ static bool
 DoSetsIntersect(const std::set<T>& a, const std::set<T>& b)
 {
     std::vector<T> intersection;
     std::set_intersection(a.begin(), a.end(), b.begin(), b.end(),
                           std::back_inserter(intersection));
     return bool(intersection.size());
 }
 
-class ScopedDrawHelper final
+const webgl::CachedDrawFetchLimits*
+ValidateDraw(WebGLContext* const webgl, const char* const funcName, const GLenum mode,
+             const uint32_t instanceCount)
+{
+    MOZ_ASSERT(webgl->gl->IsCurrent());
+
+    if (!webgl->BindCurFBForDraw(funcName))
+        return nullptr;
+
+    if (!webgl->ValidateDrawModeEnum(mode, funcName))
+        return nullptr;
+
+    if (!webgl->ValidateStencilParamsForDrawCall(funcName))
+        return nullptr;
+
+    if (!webgl->mActiveProgramLinkInfo) {
+        webgl->ErrorInvalidOperation("%s: The current program is not linked.", funcName);
+        return nullptr;
+    }
+    const auto& linkInfo = webgl->mActiveProgramLinkInfo;
+
+    // -
+    // Check UBO sizes.
+
+    for (const auto& cur : linkInfo->uniformBlocks) {
+        const auto& dataSize = cur->mDataSize;
+        const auto& binding = cur->mBinding;
+        if (!binding) {
+            webgl->ErrorInvalidOperation("%s: Buffer for uniform block is null.",
+                                          funcName);
+            return nullptr;
+        }
+
+        const auto availByteCount = binding->ByteCount();
+        if (dataSize > availByteCount) {
+            webgl->ErrorInvalidOperation("%s: Buffer for uniform block is smaller"
+                                         " than UNIFORM_BLOCK_DATA_SIZE.",
+                                         funcName);
+            return nullptr;
+        }
+
+        if (binding->mBufferBinding->IsBoundForTF()) {
+            webgl->ErrorInvalidOperation("%s: Buffer for uniform block is bound or"
+                                         " in use for transform feedback.",
+                                         funcName);
+            return nullptr;
+        }
+    }
+
+    // -
+
+    const auto& tfo = webgl->mBoundTransformFeedback;
+    if (tfo && tfo->IsActiveAndNotPaused()) {
+        uint32_t numUsed;
+        switch (linkInfo->transformFeedbackBufferMode) {
+        case LOCAL_GL_INTERLEAVED_ATTRIBS:
+            numUsed = 1;
+            break;
+
+        case LOCAL_GL_SEPARATE_ATTRIBS:
+            numUsed = linkInfo->transformFeedbackVaryings.size();
+            break;
+
+        default:
+            MOZ_CRASH();
+        }
+
+        for (uint32_t i = 0; i < numUsed; ++i) {
+            const auto& buffer = tfo->mIndexedBindings[i].mBufferBinding;
+            if (buffer->IsBoundForNonTF()) {
+                webgl->ErrorInvalidOperation("%s: Transform feedback varying %u's buffer"
+                                             " is bound for non-transform-feedback.",
+                                             funcName, i);
+                return nullptr;
+            }
+
+            // Technically we don't know that this will be updated yet, but we can
+            // speculatively mark it.
+            buffer->ResetLastUpdateFenceId();
+        }
+    }
+
+    // -
+
+    const auto fetchLimits = linkInfo->GetDrawFetchLimits(funcName);
+    if (!fetchLimits)
+        return nullptr;
+
+    if (instanceCount > fetchLimits->maxInstances) {
+        webgl->ErrorInvalidOperation("%s: Instance fetch requires %u, but attribs only"
+                                     " supply %u.",
+                                     funcName, instanceCount,
+                                     uint32_t(fetchLimits->maxInstances));
+        return nullptr;
+    }
+
+    // -
+
+    webgl->RunContextLossTimer();
+
+    return fetchLimits;
+}
+
+////////////////////////////////////////
+
+class ScopedFakeVertexAttrib0 final
 {
     WebGLContext* const mWebGL;
-    bool mDidFake;
+    bool mDidFake = false;
 
 public:
-    ScopedDrawHelper(WebGLContext* const webgl, const char* const funcName,
-                     const GLenum mode, const Maybe<uint32_t>& lastRequiredVertex,
-                     const uint32_t instanceCount, bool* const out_error)
+    ScopedFakeVertexAttrib0(WebGLContext* const webgl, const char* const funcName,
+                            const uint64_t vertexCount, bool* const out_error)
         : mWebGL(webgl)
-        , mDidFake(false)
     {
-        MOZ_ASSERT(mWebGL->gl->IsCurrent());
-
-        if (!mWebGL->BindCurFBForDraw(funcName)) {
-            *out_error = true;
-            return;
-        }
+        *out_error = false;
 
-        if (!mWebGL->ValidateDrawModeEnum(mode, funcName)) {
-            *out_error = true;
-            return;
-        }
-
-        if (!mWebGL->ValidateStencilParamsForDrawCall(funcName)) {
-            *out_error = true;
-            return;
-        }
-
-        if (!mWebGL->mActiveProgramLinkInfo) {
-            mWebGL->ErrorInvalidOperation("%s: The current program is not linked.", funcName);
+        if (!mWebGL->DoFakeVertexAttrib0(funcName, vertexCount)) {
             *out_error = true;
             return;
         }
-        const auto& linkInfo = mWebGL->mActiveProgramLinkInfo;
-
-        ////
-        // Check UBO sizes.
-
-        for (const auto& cur : linkInfo->uniformBlocks) {
-            const auto& dataSize = cur->mDataSize;
-            const auto& binding = cur->mBinding;
-            if (!binding) {
-                mWebGL->ErrorInvalidOperation("%s: Buffer for uniform block is null.",
-                                              funcName);
-                *out_error = true;
-                return;
-            }
-
-            const auto availByteCount = binding->ByteCount();
-            if (dataSize > availByteCount) {
-                mWebGL->ErrorInvalidOperation("%s: Buffer for uniform block is smaller"
-                                              " than UNIFORM_BLOCK_DATA_SIZE.",
-                                              funcName);
-                *out_error = true;
-                return;
-            }
-
-            if (binding->mBufferBinding->IsBoundForTF()) {
-                mWebGL->ErrorInvalidOperation("%s: Buffer for uniform block is bound or"
-                                              " in use for transform feedback.",
-                                              funcName);
-                *out_error = true;
-                return;
-            }
-        }
-
-        ////
-
-        const auto& tfo = mWebGL->mBoundTransformFeedback;
-        if (tfo && tfo->IsActiveAndNotPaused()) {
-            uint32_t numUsed;
-            switch (linkInfo->transformFeedbackBufferMode) {
-            case LOCAL_GL_INTERLEAVED_ATTRIBS:
-                numUsed = 1;
-                break;
-
-            case LOCAL_GL_SEPARATE_ATTRIBS:
-                numUsed = linkInfo->transformFeedbackVaryings.size();
-                break;
-
-            default:
-                MOZ_CRASH();
-            }
-
-            for (uint32_t i = 0; i < numUsed; ++i) {
-                const auto& buffer = tfo->mIndexedBindings[i].mBufferBinding;
-                if (buffer->IsBoundForNonTF()) {
-                    mWebGL->ErrorInvalidOperation("%s: Transform feedback varying %u's"
-                                                  " buffer is bound for"
-                                                  " non-transform-feedback.",
-                                                  funcName, i);
-                    *out_error = true;
-                    return;
-                }
-
-                // Technically we don't know that this will be updated yet, but we can
-                // speculatively mark it.
-                buffer->ResetLastUpdateFenceId();
-            }
-        }
-
-        ////
-
-        const auto& fetchLimits = linkInfo->GetDrawFetchLimits(funcName);
-        if (!fetchLimits) {
-            *out_error = true;
-            return;
-        }
-
-        if (lastRequiredVertex && instanceCount) {
-            if (lastRequiredVertex.value() >= fetchLimits->maxVerts) {
-                mWebGL->ErrorInvalidOperation("%s: Vertex fetch requires vertex #%u, but"
-                                              " attribs only supply %" PRIu64 ".",
-                                              funcName, lastRequiredVertex.value(),
-                                              fetchLimits->maxVerts);
-                *out_error = true;
-                return;
-            }
-            if (instanceCount > fetchLimits->maxInstances) {
-                mWebGL->ErrorInvalidOperation("%s: Instance fetch requires %u, but"
-                                              " attribs only supply %" PRIu64 ".",
-                                              funcName, instanceCount,
-                                              fetchLimits->maxInstances);
-                *out_error = true;
-                return;
-            }
-        }
-
-        ////
-
-        if (lastRequiredVertex) {
-            if (!mWebGL->DoFakeVertexAttrib0(funcName, lastRequiredVertex.value())) {
-                *out_error = true;
-                return;
-            }
-            mDidFake = true;
-        }
-
-        ////
-
-        mWebGL->RunContextLossTimer();
+        mDidFake = true;
     }
 
-    ~ScopedDrawHelper() {
+    ~ScopedFakeVertexAttrib0()
+    {
         if (mDidFake) {
             mWebGL->UndoFakeVertexAttrib0();
         }
     }
 };
 
 ////////////////////////////////////////
 
@@ -521,68 +502,71 @@ static bool
 HasInstancedDrawing(const WebGLContext& webgl)
 {
     return webgl.IsWebGL2() ||
            webgl.IsExtensionEnabled(WebGLExtensionID::ANGLE_instanced_arrays);
 }
 
 ////////////////////////////////////////
 
-bool
-WebGLContext::DrawArrays_check(const char* const funcName, const GLint first,
-                               const GLsizei vertCount, const GLsizei instanceCount,
-                               Maybe<uint32_t>* const out_lastVert)
+void
+WebGLContext::DrawArraysInstanced(GLenum mode, GLint first, GLsizei vertCount,
+                                  GLsizei instanceCount, const char* const funcName)
 {
+    AUTO_PROFILER_LABEL("WebGLContext::DrawArraysInstanced", GRAPHICS);
+    if (IsContextLost())
+        return;
+    const gl::GLContext::TlsScope inTls(gl);
+
+    // -
+
     if (!ValidateNonNegative(funcName, "first", first) ||
         !ValidateNonNegative(funcName, "vertCount", vertCount) ||
         !ValidateNonNegative(funcName, "instanceCount", instanceCount))
     {
-        return false;
+        return;
     }
 
     if (IsWebGL2() && !gl->IsSupported(gl::GLFeature::prim_restart_fixed)) {
         MOZ_ASSERT(gl->IsSupported(gl::GLFeature::prim_restart));
         if (mPrimRestartTypeBytes != 0) {
             mPrimRestartTypeBytes = 0;
 
             // OSX appears to have severe perf issues with leaving this enabled.
             gl->fDisable(LOCAL_GL_PRIMITIVE_RESTART);
         }
     }
 
-    if (!vertCount) {
-        *out_lastVert = Nothing();
-    } else {
-        const auto lastVert_checked = CheckedInt<uint32_t>(first) + vertCount - 1;
-        if (!lastVert_checked.isValid()) {
-            ErrorOutOfMemory("%s: `first+vertCount` out of range.", funcName);
-            return false;
-        }
-        *out_lastVert = Some(lastVert_checked.value());
-    }
-    return true;
-}
+    // -
 
-void
-WebGLContext::DrawArraysInstanced(GLenum mode, GLint first, GLsizei vertCount,
-                                  GLsizei instanceCount, const char* const funcName)
-{
-    AUTO_PROFILER_LABEL("WebGLContext::DrawArraysInstanced", GRAPHICS);
-    if (IsContextLost())
+    const auto fetchLimits = ValidateDraw(this, funcName, mode, instanceCount);
+    if (!fetchLimits)
         return;
 
-    const gl::GLContext::TlsScope inTls(gl);
+    // -
+
+    const auto totalVertCount_safe = CheckedInt<uint32_t>(first) + vertCount;
+    if (!totalVertCount_safe.isValid()) {
+        ErrorOutOfMemory("%s: `first+vertCount` out of range.", funcName);
+        return;
+    }
+    auto totalVertCount = totalVertCount_safe.value();
 
-    Maybe<uint32_t> lastVert;
-    if (!DrawArrays_check(funcName, first, vertCount, instanceCount, &lastVert))
+    if (vertCount && instanceCount &&
+        totalVertCount > fetchLimits->maxVerts)
+    {
+        ErrorInvalidOperation("%s: Vertex fetch requires %u, but attribs only supply %u.",
+                              funcName, totalVertCount, uint32_t(fetchLimits->maxVerts));
         return;
+    }
+
+    // -
 
     bool error = false;
-    const ScopedDrawHelper scopedHelper(this, funcName, mode, lastVert, instanceCount,
-                                        &error);
+    const ScopedFakeVertexAttrib0 attrib0(this, funcName, totalVertCount, &error);
     if (error)
         return;
 
     const ScopedResolveTexturesForDraw scopedResolve(this, funcName, &error);
     if (error)
         return;
 
     const ScopedDrawWithTransformFeedback scopedTF(this, funcName, mode, vertCount,
@@ -604,37 +588,36 @@ WebGLContext::DrawArraysInstanced(GLenum
     }
 
     Draw_cleanup(funcName);
     scopedTF.Advance();
 }
 
 ////////////////////////////////////////
 
-bool
+WebGLBuffer*
 WebGLContext::DrawElements_check(const char* const funcName, const GLsizei rawIndexCount,
                                  const GLenum type, const WebGLintptr byteOffset,
-                                 const GLsizei instanceCount,
-                                 Maybe<uint32_t>* const out_lastVert)
+                                 const GLsizei instanceCount)
 {
     if (mBoundTransformFeedback &&
         mBoundTransformFeedback->mIsActive &&
         !mBoundTransformFeedback->mIsPaused)
     {
         ErrorInvalidOperation("%s: DrawElements* functions are incompatible with"
                               " transform feedback.",
                               funcName);
-        return false;
+        return nullptr;
     }
 
     if (!ValidateNonNegative(funcName, "vertCount", rawIndexCount) ||
         !ValidateNonNegative(funcName, "byteOffset", byteOffset) ||
         !ValidateNonNegative(funcName, "instanceCount", instanceCount))
     {
-        return false;
+        return nullptr;
     }
     const auto indexCount = uint32_t(rawIndexCount);
 
     uint8_t bytesPerIndex = 0;
     switch (type) {
     case LOCAL_GL_UNSIGNED_BYTE:
         bytesPerIndex = 1;
         break;
@@ -646,22 +629,22 @@ WebGLContext::DrawElements_check(const c
     case LOCAL_GL_UNSIGNED_INT:
         if (IsWebGL2() || IsExtensionEnabled(WebGLExtensionID::OES_element_index_uint)) {
             bytesPerIndex = 4;
         }
         break;
     }
     if (!bytesPerIndex) {
         ErrorInvalidEnum("%s: Invalid `type`: 0x%04x", funcName, type);
-        return false;
+        return nullptr;
     }
     if (byteOffset % bytesPerIndex != 0) {
         ErrorInvalidOperation("%s: `byteOffset` must be a multiple of the size of `type`",
                               funcName);
-        return false;
+        return nullptr;
     }
 
     ////
 
     if (IsWebGL2() && !gl->IsSupported(gl::GLFeature::prim_restart_fixed)) {
         MOZ_ASSERT(gl->IsSupported(gl::GLFeature::prim_restart));
         if (mPrimRestartTypeBytes != bytesPerIndex) {
             mPrimRestartTypeBytes = bytesPerIndex;
@@ -673,35 +656,29 @@ WebGLContext::DrawElements_check(const c
     }
 
     ////
     // Index fetching
 
     const auto& indexBuffer = mBoundVertexArray->mElementArrayBuffer;
     if (!indexBuffer) {
         ErrorInvalidOperation("%s: Index buffer not bound.", funcName);
-        return false;
+        return nullptr;
     }
     MOZ_ASSERT(!indexBuffer->IsBoundForTF(), "This should be impossible.");
 
-    if (!indexCount || !instanceCount) {
-        *out_lastVert = Nothing();
-        return true;
-    }
-
     const size_t availBytes = indexBuffer->ByteLength();
     const auto availIndices = AvailGroups(availBytes, byteOffset, bytesPerIndex,
                                           bytesPerIndex);
-    if (indexCount > availIndices) {
+    if (instanceCount && indexCount > availIndices) {
         ErrorInvalidOperation("%s: Index buffer too small.", funcName);
-        return false;
+        return nullptr;
     }
 
-    *out_lastVert = indexBuffer->GetIndexedFetchMaxVert(type, byteOffset, indexCount);
-    return true;
+    return indexBuffer.get();
 }
 
 static void
 HandleDrawElementsErrors(WebGLContext* webgl, const char* funcName,
                          gl::GLContext::LocalErrorScope& errorScope)
 {
     const auto err = errorScope.GetError();
     if (err == LOCAL_GL_INVALID_OPERATION) {
@@ -725,26 +702,80 @@ WebGLContext::DrawElementsInstanced(GLen
                                     const char* const funcName)
 {
     AUTO_PROFILER_LABEL("WebGLContext::DrawElementsInstanced", GRAPHICS);
     if (IsContextLost())
         return;
 
     const gl::GLContext::TlsScope inTls(gl);
 
-    Maybe<uint32_t> lastVert;
-    if (!DrawElements_check(funcName, indexCount, type, byteOffset, instanceCount,
-                            &lastVert))
-    {
+    const auto indexBuffer = DrawElements_check(funcName, indexCount, type, byteOffset,
+                                                instanceCount);
+    if (!indexBuffer)
         return;
+
+    // -
+
+    const auto fetchLimits = ValidateDraw(this, funcName, mode, instanceCount);
+    if (!fetchLimits)
+        return;
+
+    bool collapseToDrawArrays = false;
+    auto fakeVertCount = fetchLimits->maxVerts;
+    if (fetchLimits->maxVerts == UINT64_MAX) {
+        // This isn't observable, and keeps FakeVertexAttrib0 sane.
+        collapseToDrawArrays = true;
+        fakeVertCount = 1;
     }
 
+    // -
+
+    {
+        uint64_t indexCapacity = indexBuffer->ByteLength();
+        switch (type) {
+        case LOCAL_GL_UNSIGNED_BYTE:
+            break;
+        case LOCAL_GL_UNSIGNED_SHORT:
+            indexCapacity /= 2;
+            break;
+        case LOCAL_GL_UNSIGNED_INT:
+            indexCapacity /= 4;
+            break;
+        }
+
+        uint32_t maxVertId = 0;
+        const auto isFetchValid = [&]() {
+            if (!indexCount || !instanceCount)
+                return true;
+
+            const auto globalMaxVertId = indexBuffer->GetIndexedFetchMaxVert(type, 0,
+                                                                             indexCapacity);
+            if (!globalMaxVertId)
+                return true;
+            if (globalMaxVertId.value() < fetchLimits->maxVerts)
+                return true;
+
+            const auto exactMaxVertId = indexBuffer->GetIndexedFetchMaxVert(type,
+                                                                            byteOffset,
+                                                                            indexCount);
+            maxVertId = exactMaxVertId.value();
+            return maxVertId < fetchLimits->maxVerts;
+        }();
+        if (!isFetchValid) {
+            ErrorInvalidOperation("%s: Indexed vertex fetch requires %u vertices, but"
+                                  " attribs only supply %u.",
+                                  funcName, maxVertId+1, uint32_t(fetchLimits->maxVerts));
+            return;
+        }
+    }
+
+    // -
+
     bool error = false;
-    const ScopedDrawHelper scopedHelper(this, funcName, mode, lastVert, instanceCount,
-                                        &error);
+    const ScopedFakeVertexAttrib0 attrib0(this, funcName, fakeVertCount, &error);
     if (error)
         return;
 
     const ScopedResolveTexturesForDraw scopedResolve(this, funcName, &error);
     if (error)
         return;
 
     {
@@ -757,23 +788,31 @@ WebGLContext::DrawElementsInstanced(GLen
                 // ANGLE does range validation even when it doesn't need to.
                 // With MOZ_GL_ABORT_ON_ERROR, we need to catch it or hit assertions.
                 errorScope.reset(new gl::GLContext::LocalErrorScope(*gl));
             }
 
             if (indexCount && instanceCount) {
                 AUTO_PROFILER_LABEL("glDrawElementsInstanced", GRAPHICS);
                 if (HasInstancedDrawing(*this)) {
-                    gl->fDrawElementsInstanced(mode, indexCount, type,
-                                               reinterpret_cast<GLvoid*>(byteOffset),
-                                               instanceCount);
+                    if (MOZ_UNLIKELY(collapseToDrawArrays)) {
+                        gl->fDrawArraysInstanced(mode, 0, 1, instanceCount);
+                    } else {
+                        gl->fDrawElementsInstanced(mode, indexCount, type,
+                                                   reinterpret_cast<GLvoid*>(byteOffset),
+                                                   instanceCount);
+                    }
                 } else {
                     MOZ_ASSERT(instanceCount == 1);
-                    gl->fDrawElements(mode, indexCount, type,
-                                      reinterpret_cast<GLvoid*>(byteOffset));
+                    if (MOZ_UNLIKELY(collapseToDrawArrays)) {
+                        gl->fDrawArrays(mode, 0, 1);
+                    } else {
+                        gl->fDrawElements(mode, indexCount, type,
+                                          reinterpret_cast<GLvoid*>(byteOffset));
+                    }
                 }
             }
 
             if (errorScope) {
                 HandleDrawElementsErrors(this, funcName, *errorScope);
             }
         }
     }
@@ -851,17 +890,17 @@ WebGLContext::WhatDoesVertexAttrib0Need(
     }
 
     const auto& isAttribArray0Enabled = mBoundVertexArray->mAttribs[0].mEnabled;
     return isAttribArray0Enabled ? WebGLVertexAttrib0Status::Default
                                  : WebGLVertexAttrib0Status::EmulatedInitializedArray;
 }
 
 bool
-WebGLContext::DoFakeVertexAttrib0(const char* const funcName, const uint32_t lastVert)
+WebGLContext::DoFakeVertexAttrib0(const char* const funcName, const uint64_t vertexCount)
 {
     const auto whatDoesAttrib0Need = WhatDoesVertexAttrib0Need();
     if (MOZ_LIKELY(whatDoesAttrib0Need == WebGLVertexAttrib0Status::Default))
         return true;
 
     if (!mAlreadyWarnedAboutFakeVertexAttrib0) {
         GenerateWarning("Drawing without vertex attrib 0 array enabled forces the browser "
                         "to do expensive emulation work when running on desktop OpenGL "
@@ -896,22 +935,22 @@ WebGLContext::DoFakeVertexAttrib0(const 
 
     default:
         MOZ_CRASH();
     }
 
     ////
 
     const auto bytesPerVert = sizeof(mFakeVertexAttrib0Data);
-    const auto checked_dataSize = (CheckedUint32(lastVert)+1) * bytesPerVert;
+    const auto checked_dataSize = CheckedUint32(vertexCount) * bytesPerVert;
     if (!checked_dataSize.isValid()) {
         ErrorOutOfMemory("Integer overflow trying to construct a fake vertex attrib 0"
                          " array for a draw-operation with %" PRIu64 " vertices. Try"
                          " reducing the number of vertices.",
-                         uint64_t(lastVert) + 1);
+                         vertexCount);
         return false;
     }
     const auto dataSize = checked_dataSize.value();
 
     if (mFakeVertexAttrib0BufferObjectSize < dataSize) {
         gl->fBufferData(LOCAL_GL_ARRAY_BUFFER, dataSize, nullptr, LOCAL_GL_DYNAMIC_DRAW);
         mFakeVertexAttrib0BufferObjectSize = dataSize;
         mFakeVertexAttrib0DataDefined = false;
--- a/dom/canvas/WebGLTransformFeedback.h
+++ b/dom/canvas/WebGLTransformFeedback.h
@@ -6,28 +6,33 @@
 #ifndef WEBGL_TRANSFORM_FEEDBACK_H_
 #define WEBGL_TRANSFORM_FEEDBACK_H_
 
 #include "mozilla/LinkedList.h"
 #include "nsWrapperCache.h"
 #include "WebGLObjectModel.h"
 
 namespace mozilla {
+namespace webgl {
+struct CachedDrawFetchLimits;
+}
 
 class WebGLTransformFeedback final
     : public nsWrapperCache
     , public WebGLRefCountedObject<WebGLTransformFeedback>
     , public LinkedListElement<WebGLTransformFeedback>
 {
-    friend class ScopedDrawHelper;
     friend class ScopedDrawWithTransformFeedback;
     friend class WebGLContext;
     friend class WebGL2Context;
     friend class WebGLProgram;
 
+    friend const webgl::CachedDrawFetchLimits*
+        ValidateDraw(WebGLContext*, const char*, GLenum, uint32_t);
+
 public:
     const GLuint mGLName;
 private:
     // GLES 3.0.4 p267, Table 6.24 "Transform Feedback State"
     // It's not yet in the ES3 spec, but the generic TF buffer bind point has been moved
     // to context state, instead of TFO state.
     std::vector<IndexedBufferBinding> mIndexedBindings;
     bool mIsPaused;
--- a/dom/events/CommandEvent.cpp
+++ b/dom/events/CommandEvent.cpp
@@ -10,18 +10,17 @@
 
 namespace mozilla {
 namespace dom {
 
 CommandEvent::CommandEvent(EventTarget* aOwner,
                            nsPresContext* aPresContext,
                            WidgetCommandEvent* aEvent)
   : Event(aOwner, aPresContext,
-          aEvent ? aEvent :
-                   new WidgetCommandEvent(false, nullptr, nullptr, nullptr))
+          aEvent ? aEvent : new WidgetCommandEvent())
 {
   mEvent->mTime = PR_Now();
   if (aEvent) {
     mEventIsInternal = false;
   } else {
     mEventIsInternal = true;
   }
 }
--- a/dom/vr/VRDisplay.cpp
+++ b/dom/vr/VRDisplay.cpp
@@ -494,40 +494,56 @@ VRDisplay::GetPose()
 
 void
 VRDisplay::ResetPose()
 {
   mClient->ZeroSensor();
 }
 
 void
+VRDisplay::StartVRNavigation()
+{
+  mClient->StartVRNavigation();
+}
+
+void
 VRDisplay::StartHandlingVRNavigationEvent()
 {
   mHandlingVRNavigationEventStart = TimeStamp::Now();
   ++mVRNavigationEventDepth;
+  TimeDuration timeout = TimeDuration::FromMilliseconds(gfxPrefs::VRNavigationTimeout());
+  // A 0 or negative TimeDuration indicates that content may take
+  // as long as it wishes to respond to the event, as long as
+  // it happens before the event exits.
+  if (timeout.ToMilliseconds() > 0) {
+    mClient->StopVRNavigation(timeout);
+  }
 }
 
 void
 VRDisplay::StopHandlingVRNavigationEvent()
 {
   MOZ_ASSERT(mVRNavigationEventDepth > 0);
   --mVRNavigationEventDepth;
+  if (mVRNavigationEventDepth == 0) {
+    mClient->StopVRNavigation(TimeDuration::FromMilliseconds(0));
+  }
 }
 
 bool
 VRDisplay::IsHandlingVRNavigationEvent()
 {
   if (mVRNavigationEventDepth == 0) {
     return false;
   }
   if (mHandlingVRNavigationEventStart.IsNull()) {
     return false;
   }
   TimeDuration timeout = TimeDuration::FromMilliseconds(gfxPrefs::VRNavigationTimeout());
-  return timeout <= TimeDuration(0) ||
+  return timeout.ToMilliseconds() <= 0 ||
     (TimeStamp::Now() - mHandlingVRNavigationEventStart) <= timeout;
 }
 
 already_AddRefed<Promise>
 VRDisplay::RequestPresent(const nsTArray<VRLayer>& aLayers,
                           CallerType aCallerType,
                           ErrorResult& aRv)
 {
--- a/dom/vr/VRDisplay.h
+++ b/dom/vr/VRDisplay.h
@@ -358,16 +358,17 @@ public:
                                            ErrorResult& aRv);
   already_AddRefed<Promise> ExitPresent(ErrorResult& aRv);
   void GetLayers(nsTArray<VRLayer>& result);
   void SubmitFrame();
 
   int32_t RequestAnimationFrame(mozilla::dom::FrameRequestCallback& aCallback,
                                 mozilla::ErrorResult& aError);
   void CancelAnimationFrame(int32_t aHandle, mozilla::ErrorResult& aError);
+  void StartVRNavigation();
   void StartHandlingVRNavigationEvent();
   void StopHandlingVRNavigationEvent();
   bool IsHandlingVRNavigationEvent();
 
 protected:
   VRDisplay(nsPIDOMWindowInner* aWindow, gfx::VRDisplayClient* aClient);
   virtual ~VRDisplay();
   virtual void LastRelease() override;
--- a/gfx/layers/AnimationHelper.cpp
+++ b/gfx/layers/AnimationHelper.cpp
@@ -40,54 +40,55 @@ CompositorAnimationStorage::ClearById(co
 
 AnimatedValue*
 CompositorAnimationStorage::GetAnimatedValue(const uint64_t& aId) const
 {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
   return mAnimatedValues.Get(aId);
 }
 
-Maybe<float>
-CompositorAnimationStorage::GetAnimationOpacity(const uint64_t& aId) const
+OMTAValue
+CompositorAnimationStorage::GetOMTAValue(const uint64_t& aId) const
 {
-  auto value = GetAnimatedValue(aId);
-  if (!value || value->mType != AnimatedValue::OPACITY) {
-    return Nothing();
-  }
-
-  return Some(value->mOpacity);
-}
-
-Maybe<gfx::Matrix4x4>
-CompositorAnimationStorage::GetAnimationTransform(const uint64_t& aId) const
-{
-  auto value = GetAnimatedValue(aId);
-  if (!value || value->mType != AnimatedValue::TRANSFORM) {
-    return Nothing();
+  OMTAValue omtaValue = mozilla::null_t();
+  auto animatedValue = GetAnimatedValue(aId);
+  if (!animatedValue) {
+    return omtaValue;
   }
 
-  gfx::Matrix4x4 transform = value->mTransform.mFrameTransform;
-  const TransformData& data = value->mTransform.mData;
-  float scale = data.appUnitsPerDevPixel();
-  gfx::Point3D transformOrigin = data.transformOrigin();
+  switch (animatedValue->mType) {
+    case AnimatedValue::OPACITY:
+      omtaValue = animatedValue->mOpacity;
+      break;
+    case AnimatedValue::TRANSFORM: {
+      gfx::Matrix4x4 transform = animatedValue->mTransform.mFrameTransform;
+      const TransformData& data = animatedValue->mTransform.mData;
+      float scale = data.appUnitsPerDevPixel();
+      gfx::Point3D transformOrigin = data.transformOrigin();
 
-  // Undo the rebasing applied by
-  // nsDisplayTransform::GetResultingTransformMatrixInternal
-  transform.ChangeBasis(-transformOrigin);
+      // Undo the rebasing applied by
+      // nsDisplayTransform::GetResultingTransformMatrixInternal
+      transform.ChangeBasis(-transformOrigin);
 
-  // Convert to CSS pixels (this undoes the operations performed by
-  // nsStyleTransformMatrix::ProcessTranslatePart which is called from
-  // nsDisplayTransform::GetResultingTransformMatrix)
-  double devPerCss =
-    double(scale) / double(nsDeviceContext::AppUnitsPerCSSPixel());
-  transform._41 *= devPerCss;
-  transform._42 *= devPerCss;
-  transform._43 *= devPerCss;
+      // Convert to CSS pixels (this undoes the operations performed by
+      // nsStyleTransformMatrix::ProcessTranslatePart which is called from
+      // nsDisplayTransform::GetResultingTransformMatrix)
+      double devPerCss =
+        double(scale) / double(nsDeviceContext::AppUnitsPerCSSPixel());
+      transform._41 *= devPerCss;
+      transform._42 *= devPerCss;
+      transform._43 *= devPerCss;
+      omtaValue = transform;
+      break;
+    }
+    case AnimatedValue::NONE:
+      break;
+  }
 
-  return Some(transform);
+  return omtaValue;
 }
 
 void
 CompositorAnimationStorage::SetAnimatedValue(uint64_t aId,
                                              gfx::Matrix4x4&& aTransformInDevSpace,
                                              gfx::Matrix4x4&& aFrameTransform,
                                              const TransformData& aData)
 {
--- a/gfx/layers/AnimationHelper.h
+++ b/gfx/layers/AnimationHelper.h
@@ -127,29 +127,17 @@ public:
    */
   void SetAnimatedValue(uint64_t aId, const float& aOpacity);
 
   /**
    * Return the animated value if a given id can map to its animated value
    */
   AnimatedValue* GetAnimatedValue(const uint64_t& aId) const;
 
-  /**
-   * Like GetAnimatedValue(), but ensures the value is an opacity and returns
-   * the float value if possible, or Nothing() otherwise.
-   */
-  Maybe<float> GetAnimationOpacity(const uint64_t& aId) const;
-
-  /**
-   * Like GetAnimatedValue(), but ensures the value is a transform and returns
-   * the transform matrix if possible, or Nothing() otherwise. It also does
-   * some post-processing on the transform matrix as well. See the comments
-   * inside the function for details.
-   */
-  Maybe<gfx::Matrix4x4> GetAnimationTransform(const uint64_t& aId) const;
+  OMTAValue GetOMTAValue(const uint64_t& aId) const;
 
   /**
    * Return the iterator of animated value table
    */
   AnimatedValueTable::Iterator ConstAnimatedValueTableIter() const
   {
     return mAnimatedValues.ConstIter();
   }
--- a/gfx/layers/D3D11YCbCrImage.cpp
+++ b/gfx/layers/D3D11YCbCrImage.cpp
@@ -1,15 +1,17 @@
 /* -*- 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 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 "D3D11YCbCrImage.h"
+
+#include "gfx2DGlue.h"
 #include "YCbCrUtils.h"
 #include "mozilla/gfx/gfxVars.h"
 #include "mozilla/gfx/DeviceManagerDx.h"
 #include "mozilla/layers/CompositableClient.h"
 #include "mozilla/layers/CompositableForwarder.h"
 #include "mozilla/layers/TextureD3D11.h"
 
 using namespace mozilla::gfx;
--- a/gfx/layers/composite/CompositableHost.h
+++ b/gfx/layers/composite/CompositableHost.h
@@ -43,16 +43,17 @@ class LayerComposite;
 class ImageHost;
 class Compositor;
 class ThebesBufferData;
 class TiledContentHost;
 class CompositableParentManager;
 class WebRenderImageHost;
 class ContentHost;
 class ContentHostTexture;
+class HostLayerManager;
 struct EffectChain;
 
 struct ImageCompositeNotificationInfo {
   base::ProcessId mImageBridgeProcessId;
   ImageCompositeNotification mNotification;
 };
 
 struct AsyncCompositableRef
--- a/gfx/layers/ipc/LayerTransactionParent.cpp
+++ b/gfx/layers/ipc/LayerTransactionParent.cpp
@@ -676,65 +676,35 @@ LayerTransactionParent::RecvSetTestSampl
 mozilla::ipc::IPCResult
 LayerTransactionParent::RecvLeaveTestMode()
 {
   mCompositorBridge->LeaveTestMode(GetId());
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
-LayerTransactionParent::RecvGetAnimationOpacity(const uint64_t& aCompositorAnimationsId,
-                                                float* aOpacity,
-                                                bool* aHasAnimationOpacity)
-{
-  *aHasAnimationOpacity = false;
-  if (mDestroyed || !mLayerManager || mLayerManager->IsDestroyed()) {
-    return IPC_FAIL_NO_REASON(this);
-  }
-
-  mCompositorBridge->ApplyAsyncProperties(
-    this, CompositorBridgeParentBase::TransformsToSkip::APZ);
-
-  if (!mAnimStorage) {
-    return IPC_FAIL_NO_REASON(this);
-  }
-
-  Maybe<float> opacity = mAnimStorage->GetAnimationOpacity(aCompositorAnimationsId);
-  if (opacity) {
-    *aOpacity = *opacity;
-    *aHasAnimationOpacity = true;
-  }
-  return IPC_OK();
-}
-
-mozilla::ipc::IPCResult
-LayerTransactionParent::RecvGetAnimationTransform(const uint64_t& aCompositorAnimationsId,
-                                                  MaybeTransform* aTransform)
+LayerTransactionParent::RecvGetAnimationValue(const uint64_t& aCompositorAnimationsId,
+                                              OMTAValue* aValue)
 {
   if (mDestroyed || !mLayerManager || mLayerManager->IsDestroyed()) {
     return IPC_FAIL_NO_REASON(this);
   }
 
   // Make sure we apply the latest animation style or else we can end up with
   // a race between when we temporarily clear the animation transform (in
   // CompositorBridgeParent::SetShadowProperties) and when animation recalculates
   // the value.
   mCompositorBridge->ApplyAsyncProperties(
     this, CompositorBridgeParentBase::TransformsToSkip::APZ);
 
   if (!mAnimStorage) {
     return IPC_FAIL_NO_REASON(this);
   }
 
-  Maybe<Matrix4x4> transform = mAnimStorage->GetAnimationTransform(aCompositorAnimationsId);
-  if (transform) {
-    *aTransform = *transform;
-  } else {
-    *aTransform = mozilla::void_t();
-  }
+  *aValue = mAnimStorage->GetOMTAValue(aCompositorAnimationsId);
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 LayerTransactionParent::RecvGetTransform(const LayerHandle& aLayerHandle,
                                          MaybeTransform* aTransform)
 {
   if (mDestroyed || !mLayerManager || mLayerManager->IsDestroyed()) {
--- a/gfx/layers/ipc/LayerTransactionParent.h
+++ b/gfx/layers/ipc/LayerTransactionParent.h
@@ -120,21 +120,18 @@ protected:
                                               const TextureInfo& aInfo) override;
   mozilla::ipc::IPCResult RecvReleaseLayer(const LayerHandle& aHandle) override;
   mozilla::ipc::IPCResult RecvReleaseCompositable(const CompositableHandle& aHandle) override;
 
   mozilla::ipc::IPCResult RecvClearCachedResources() override;
   mozilla::ipc::IPCResult RecvScheduleComposite() override;
   mozilla::ipc::IPCResult RecvSetTestSampleTime(const TimeStamp& aTime) override;
   mozilla::ipc::IPCResult RecvLeaveTestMode() override;
-  mozilla::ipc::IPCResult RecvGetAnimationOpacity(const uint64_t& aCompositorAnimationsId,
-                                                  float* aOpacity,
-                                                  bool* aHasAnimationOpacity) override;
-  mozilla::ipc::IPCResult RecvGetAnimationTransform(const uint64_t& aCompositorAnimationsId,
-                                                    MaybeTransform* aTransform) override;
+  mozilla::ipc::IPCResult RecvGetAnimationValue(const uint64_t& aCompositorAnimationsId,
+                                                OMTAValue* aValue) override;
   mozilla::ipc::IPCResult RecvGetTransform(const LayerHandle& aHandle,
                                            MaybeTransform* aTransform) override;
   mozilla::ipc::IPCResult RecvSetAsyncScrollOffset(const FrameMetrics::ViewID& aId,
                                                    const float& aX, const float& aY) override;
   mozilla::ipc::IPCResult RecvSetAsyncZoom(const FrameMetrics::ViewID& aId,
                                            const float& aValue) override;
   mozilla::ipc::IPCResult RecvFlushApzRepaints() override;
   mozilla::ipc::IPCResult RecvGetAPZTestData(APZTestData* aOutData) override;
--- a/gfx/layers/ipc/LayersMessages.ipdlh
+++ b/gfx/layers/ipc/LayersMessages.ipdlh
@@ -9,16 +9,17 @@ include LayersSurfaces;
 include protocol PCompositorBridge;
 include protocol PRenderFrame;
 include protocol PTexture;
 
 include "gfxipc/ShadowLayerUtils.h";
 include "mozilla/GfxMessageUtils.h";
 include "ImageLayers.h";
 
+using mozilla::gfx::Glyph from "mozilla/gfx/2D.h";
 using mozilla::gfx::SamplingFilter from "mozilla/gfx/2D.h";
 using struct mozilla::gfx::Color from "mozilla/gfx/2D.h";
 using struct mozilla::gfx::Point3D from "mozilla/gfx/Point.h";
 using mozilla::gfx::IntPoint from "mozilla/gfx/Point.h";
 using class mozilla::gfx::Matrix4x4 from "mozilla/gfx/Matrix.h";
 using nscoord from "nsCoord.h";
 using struct nsRect from "nsRect.h";
 using struct nsPoint from "nsPoint.h";
@@ -40,17 +41,16 @@ using mozilla::layers::ScaleMode from "m
 using mozilla::layers::EventRegions from "mozilla/layers/LayersTypes.h";
 using mozilla::layers::EventRegionsOverride from "mozilla/layers/LayersTypes.h";
 using mozilla::layers::DiagnosticTypes from "mozilla/layers/CompositorTypes.h";
 using mozilla::layers::FocusTarget from "mozilla/layers/FocusTarget.h";
 using struct mozilla::layers::ScrollMetadata from "FrameMetrics.h";
 using mozilla::layers::FrameMetrics::ViewID from "FrameMetrics.h";
 using mozilla::layers::LayersBackend from "mozilla/layers/LayersTypes.h";
 using mozilla::layers::MaybeLayerClip from "FrameMetrics.h";
-using mozilla::gfx::Glyph from "Layers.h";
 using mozilla::layers::LayerHandle from "mozilla/layers/LayersTypes.h";
 using mozilla::layers::CompositableHandle from "mozilla/layers/LayersTypes.h";
 using mozilla::layers::SimpleLayerAttributes from "mozilla/layers/LayerAttributes.h";
 using mozilla::CrossProcessSemaphoreHandle from "mozilla/ipc/CrossProcessSemaphore.h";
 using struct mozilla::void_t from "ipc/IPCMessageUtils.h";
 using mozilla::layers::LayersId from "mozilla/layers/LayersTypes.h";
 using mozilla::layers::TransactionId from "mozilla/layers/LayersTypes.h";
 
@@ -565,10 +565,16 @@ struct TransactionInfo
   TimeStamp fwdTime;
 };
 
 union MaybeTransform {
   Matrix4x4;
   void_t;
 };
 
+union OMTAValue {
+  null_t;
+  float;
+  Matrix4x4;
+};
+
 } // namespace
 } // namespace
--- a/gfx/layers/ipc/PLayerTransaction.ipdl
+++ b/gfx/layers/ipc/PLayerTransaction.ipdl
@@ -11,16 +11,17 @@ include protocol PCompositorBridge;
 include protocol PRenderFrame;
 include protocol PTexture;
 
 include "mozilla/GfxMessageUtils.h";
 include "mozilla/layers/LayersMessageUtils.h";
 
 using struct mozilla::layers::TextureInfo from "mozilla/layers/CompositorTypes.h";
 using struct mozilla::void_t from "ipc/IPCMessageUtils.h";
+using struct mozilla::null_t from "ipc/IPCMessageUtils.h";
 using class mozilla::layers::APZTestData from "mozilla/layers/APZTestData.h";
 using mozilla::layers::FrameMetrics::ViewID from "FrameMetrics.h";
 using struct mozilla::layers::ScrollableLayerGuid from "FrameMetrics.h";
 using struct mozilla::layers::TextureFactoryIdentifier from "mozilla/layers/CompositorTypes.h";
 using mozilla::layers::LayersBackend from "mozilla/layers/LayersTypes.h";
 using mozilla::layers::LayerHandle from "mozilla/layers/LayersTypes.h";
 using mozilla::layers::CompositableHandle from "mozilla/layers/LayersTypes.h";
 using mozilla::layers::LayersObserverEpoch from "mozilla/layers/LayersTypes.h";
@@ -71,28 +72,18 @@ parent:
   // Testing APIs
 
   // Enter test mode, set the sample time to sampleTime, and resample
   // animations. sampleTime must not be null.
   sync SetTestSampleTime(TimeStamp sampleTime);
   // Leave test mode and resume normal compositing
   sync LeaveTestMode();
 
-  // Returns the value of the opacity applied to the layer by animation.
-  // |hasAnimationOpacity| is true if the layer has an opacity value
-  // specified by animation. If it's false, |opacity| value is indefinite.
-  sync GetAnimationOpacity(uint64_t aCompositorAnimationsId) returns (float opacity,
-                                                  bool hasAnimationOpacity);
-
-  // Returns the value of the transform applied to the layer by animation after
-  // factoring out translation components introduced to account for the offset
-  // of the corresponding frame and transform origin and after converting to CSS
-  // pixels. If the layer is not transformed by animation, the return value will
-  // be void_t.
-  sync GetAnimationTransform(uint64_t aCompositorAnimationId) returns (MaybeTransform transform);
+  // Returns |OMTAValue| applied to the layer.
+  sync GetAnimationValue(uint64_t aCompositorAnimationId) returns (OMTAValue value);
 
   // Returns the value of the transform applied to the layer by animation and
   // APZC.
   sync GetTransform(LayerHandle layer) returns (MaybeTransform transform);
 
   // The next time the layer tree is composited, add this async scroll offset in
   // CSS pixels for the given ViewID.
   // Useful for testing rendering of async scrolling.
--- a/gfx/layers/ipc/PWebRenderBridge.ipdl
+++ b/gfx/layers/ipc/PWebRenderBridge.ipdl
@@ -65,19 +65,17 @@ parent:
   sync SyncWithCompositor();
 
   // These correspond exactly to the equivalent APIs in PLayerTransaction -
   // see those for documentation.
   async SetConfirmedTargetAPZC(uint64_t aInputBlockId, ScrollableLayerGuid[] aTargets);
   // More copied from PLayerTransaction, but these are only used for testing.
   sync SetTestSampleTime(TimeStamp sampleTime);
   sync LeaveTestMode();
-  sync GetAnimationOpacity(uint64_t aCompositorAnimationsId) returns (float opacity,
-                                                  bool hasAnimationOpacity);
-  sync GetAnimationTransform(uint64_t aCompositorAnimationId) returns (MaybeTransform transform);
+  sync GetAnimationValue(uint64_t aCompositorAnimationsId) returns (OMTAValue value);
   sync SetAsyncScrollOffset(ViewID scrollId, float x, float y);
   sync SetAsyncZoom(ViewID scrollId, float zoom);
   async FlushApzRepaints();
   sync GetAPZTestData() returns (APZTestData data);
 
   async Shutdown();
   sync ShutdownSync();
 child:
--- a/gfx/layers/ipc/UiCompositorControllerParent.cpp
+++ b/gfx/layers/ipc/UiCompositorControllerParent.cpp
@@ -69,16 +69,18 @@ UiCompositorControllerParent::RecvResume
 }
 
 mozilla::ipc::IPCResult
 UiCompositorControllerParent::RecvResumeAndResize(const int32_t& aWidth,
                                                   const int32_t& aHeight)
 {
   CompositorBridgeParent* parent = CompositorBridgeParent::GetCompositorBridgeParentFromLayersId(mRootLayerTreeId);
   if (parent) {
+    // Front-end expects a first paint callback upon resume/resize.
+    parent->RecvForceIsFirstPaint();
     parent->ResumeCompositionAndResize(aWidth, aHeight);
   }
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 UiCompositorControllerParent::RecvInvalidateAndRender()
 {
--- a/gfx/layers/wr/WebRenderBridgeParent.cpp
+++ b/gfx/layers/wr/WebRenderBridgeParent.cpp
@@ -1356,62 +1356,31 @@ WebRenderBridgeParent::RecvSetTestSample
 mozilla::ipc::IPCResult
 WebRenderBridgeParent::RecvLeaveTestMode()
 {
   mCompositorBridge->LeaveTestMode(GetLayersId());
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
-WebRenderBridgeParent::RecvGetAnimationOpacity(const uint64_t& aCompositorAnimationsId,
-                                               float* aOpacity,
-                                               bool* aHasAnimationOpacity)
+WebRenderBridgeParent::RecvGetAnimationValue(const uint64_t& aCompositorAnimationsId,
+                                             OMTAValue* aValue)
 {
   if (mDestroyed) {
     return IPC_FAIL_NO_REASON(this);
   }
 
   MOZ_ASSERT(mAnimStorage);
   if (RefPtr<WebRenderBridgeParent> root = GetRootWebRenderBridgeParent()) {
     root->AdvanceAnimations();
   } else {
     AdvanceAnimations();
   }
 
-  Maybe<float> opacity = mAnimStorage->GetAnimationOpacity(aCompositorAnimationsId);
-  if (opacity) {
-    *aOpacity = *opacity;
-    *aHasAnimationOpacity = true;
-  } else {
-    *aHasAnimationOpacity = false;
-  }
-  return IPC_OK();
-}
-
-mozilla::ipc::IPCResult
-WebRenderBridgeParent::RecvGetAnimationTransform(const uint64_t& aCompositorAnimationsId,
-                                                 MaybeTransform* aTransform)
-{
-  if (mDestroyed) {
-    return IPC_FAIL_NO_REASON(this);
-  }
-
-  MOZ_ASSERT(mAnimStorage);
-  if (RefPtr<WebRenderBridgeParent> root = GetRootWebRenderBridgeParent()) {
-    root->AdvanceAnimations();
-  } else {
-    AdvanceAnimations();
-  }
-
-  Maybe<Matrix4x4> transform = mAnimStorage->GetAnimationTransform(aCompositorAnimationsId);
-  if (transform) {
-    *aTransform = *transform;
-  } else {
-    *aTransform = mozilla::void_t();
-  }
+  *aValue = mAnimStorage->GetOMTAValue(aCompositorAnimationsId);
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 WebRenderBridgeParent::RecvSetAsyncScrollOffset(const FrameMetrics::ViewID& aScrollId,
                                                 const float& aX,
                                                 const float& aY)
 {
--- a/gfx/layers/wr/WebRenderBridgeParent.h
+++ b/gfx/layers/wr/WebRenderBridgeParent.h
@@ -113,21 +113,18 @@ public:
   mozilla::ipc::IPCResult RecvCapture() override;
   mozilla::ipc::IPCResult RecvSyncWithCompositor() override;
 
   mozilla::ipc::IPCResult RecvSetConfirmedTargetAPZC(const uint64_t& aBlockId,
                                                      nsTArray<ScrollableLayerGuid>&& aTargets) override;
 
   mozilla::ipc::IPCResult RecvSetTestSampleTime(const TimeStamp& aTime) override;
   mozilla::ipc::IPCResult RecvLeaveTestMode() override;
-  mozilla::ipc::IPCResult RecvGetAnimationOpacity(const uint64_t& aCompositorAnimationsId,
-                                                  float* aOpacity,
-                                                  bool* aHasAnimationOpacity) override;
-  mozilla::ipc::IPCResult RecvGetAnimationTransform(const uint64_t& aCompositorAnimationsId,
-                                                    MaybeTransform* aTransform) override;
+  mozilla::ipc::IPCResult RecvGetAnimationValue(const uint64_t& aCompositorAnimationsId,
+                                                OMTAValue* aValue) override;
   mozilla::ipc::IPCResult RecvSetAsyncScrollOffset(const FrameMetrics::ViewID& aScrollId,
                                                    const float& aX,
                                                    const float& aY) override;
   mozilla::ipc::IPCResult RecvSetAsyncZoom(const FrameMetrics::ViewID& aScrollId,
                                            const float& aZoom) override;
   mozilla::ipc::IPCResult RecvFlushApzRepaints() override;
   mozilla::ipc::IPCResult RecvGetAPZTestData(APZTestData* data) override;
 
--- a/gfx/vr/VRDisplayClient.cpp
+++ b/gfx/vr/VRDisplayClient.cpp
@@ -292,8 +292,36 @@ VRDisplayClient::UpdateSubmitFrameResult
   mSubmitFrameResult = aResult;
 }
 
 void
 VRDisplayClient::GetSubmitFrameResult(VRSubmitFrameResultInfo& aResult)
 {
   aResult = mSubmitFrameResult;
 }
+
+void
+VRDisplayClient::StartVRNavigation()
+{
+  /**
+   * A VR-to-VR site navigation has started, notify VRManager
+   * so we don't drop out of VR during the transition
+   */
+  VRManagerChild *vm = VRManagerChild::Get();
+  vm->SendStartVRNavigation(mDisplayInfo.mDisplayID);
+}
+
+void
+VRDisplayClient::StopVRNavigation(const TimeDuration& aTimeout)
+{
+  /**
+   * A VR-to-VR site navigation has ended and the new site
+   * has received a vrdisplayactivate event.
+   * Don't actually consider the navigation transition over
+   * until aTimeout has elapsed.
+   * This may be called multiple times, in which case the timeout
+   * should be reset to aTimeout.
+   * When aTimeout is TimeDuration(0), we should consider the
+   * transition immediately ended.
+   */
+  VRManagerChild *vm = VRManagerChild::Get();
+  vm->SendStopVRNavigation(mDisplayInfo.mDisplayID, aTimeout);
+}
--- a/gfx/vr/VRDisplayClient.h
+++ b/gfx/vr/VRDisplayClient.h
@@ -42,16 +42,19 @@ public:
   bool GetIsConnected() const;
 
   void NotifyDisconnected();
   void SetGroupMask(uint32_t aGroupMask);
 
   bool IsPresentationGenerationCurrent() const;
   void MakePresentationGenerationCurrent();
 
+  void StartVRNavigation();
+  void StopVRNavigation(const TimeDuration& aTimeout);
+
 protected:
   virtual ~VRDisplayClient();
 
   void FireEvents();
   void FireGamepadEvents();
 
   VRDisplayInfo mDisplayInfo;
 
--- a/gfx/vr/VRDisplayHost.cpp
+++ b/gfx/vr/VRDisplayHost.cpp
@@ -340,16 +340,28 @@ VRDisplayHost::CheckClearDisplayInfoDirt
 {
   if (mDisplayInfo == mLastUpdateDisplayInfo) {
     return false;
   }
   mLastUpdateDisplayInfo = mDisplayInfo;
   return true;
 }
 
+void
+VRDisplayHost::StartVRNavigation()
+{
+  
+}
+
+void
+VRDisplayHost::StopVRNavigation(const TimeDuration& aTimeout)
+{
+  
+}
+
 VRControllerHost::VRControllerHost(VRDeviceType aType, dom::GamepadHand aHand,
                                    uint32_t aDisplayID)
  : mControllerInfo{}
  , mVibrateIndex(0)
 {
   MOZ_COUNT_CTOR(VRControllerHost);
   mControllerInfo.mType = aType;
   mControllerInfo.mControllerState.hand = aHand;
@@ -417,8 +429,9 @@ VRControllerHost::SetVibrateIndex(uint64
   mVibrateIndex = aIndex;
 }
 
 uint64_t
 VRControllerHost::GetVibrateIndex()
 {
   return mVibrateIndex;
 }
+
--- a/gfx/vr/VRDisplayHost.h
+++ b/gfx/vr/VRDisplayHost.h
@@ -37,16 +37,18 @@ public:
   const VRDisplayInfo& GetDisplayInfo() const { return mDisplayInfo; }
 
   void AddLayer(VRLayerParent* aLayer);
   void RemoveLayer(VRLayerParent* aLayer);
 
   virtual void ZeroSensor() = 0;
   virtual void StartPresentation() = 0;
   virtual void StopPresentation() = 0;
+  virtual void StartVRNavigation();
+  virtual void StopVRNavigation(const TimeDuration& aTimeout);
   void NotifyVSync();
 
   void StartFrame();
   void SubmitFrame(VRLayerParent* aLayer,
                    const layers::SurfaceDescriptor& aTexture,
                    uint64_t aFrameId,
                    const gfx::Rect& aLeftEyeRect,
                    const gfx::Rect& aRightEyeRect);
--- a/gfx/vr/VRManager.cpp
+++ b/gfx/vr/VRManager.cpp
@@ -594,10 +594,28 @@ VRManager::NotifyVibrateHapticCompleted(
 void
 VRManager::DispatchSubmitFrameResult(uint32_t aDisplayID, const VRSubmitFrameResultInfo& aResult)
 {
   for (auto iter = mVRManagerParents.Iter(); !iter.Done(); iter.Next()) {
     Unused << iter.Get()->GetKey()->SendDispatchSubmitFrameResult(aDisplayID, aResult);
   }
 }
 
+void
+VRManager::StartVRNavigation(const uint32_t& aDisplayID)
+{
+  RefPtr<VRDisplayHost> display = GetDisplay(aDisplayID);
+  if (display) {
+    display->StartVRNavigation();
+  }
+}
+
+void
+VRManager::StopVRNavigation(const uint32_t& aDisplayID, const TimeDuration& aTimeout)
+{
+  RefPtr<VRDisplayHost> display = GetDisplay(aDisplayID);
+  if (display) {
+    display->StopVRNavigation(aTimeout);
+  }
+}
+
 } // namespace gfx
 } // namespace mozilla
--- a/gfx/vr/VRManager.h
+++ b/gfx/vr/VRManager.h
@@ -55,16 +55,18 @@ public:
   VRSystemManagerPuppet* GetPuppetManager();
   VRSystemManagerExternal* GetExternalManager();
 
   void VibrateHaptic(uint32_t aControllerIdx, uint32_t aHapticIndex,
                      double aIntensity, double aDuration, const VRManagerPromise& aPromise);
   void StopVibrateHaptic(uint32_t aControllerIdx);
   void NotifyVibrateHapticCompleted(const VRManagerPromise& aPromise);
   void DispatchSubmitFrameResult(uint32_t aDisplayID, const VRSubmitFrameResultInfo& aResult);
+  void StartVRNavigation(const uint32_t& aDisplayID);
+  void StopVRNavigation(const uint32_t& aDisplayID, const TimeDuration& aTimeout);
 
 protected:
   VRManager();
   ~VRManager();
 
 private:
 
   void Init();
--- a/gfx/vr/external_api/moz_external_vr.h
+++ b/gfx/vr/external_api/moz_external_vr.h
@@ -24,17 +24,17 @@ namespace mozilla {
 #ifdef MOZILLA_INTERNAL_API
 namespace dom {
   enum class GamepadHand : uint8_t;
   enum class GamepadCapabilityFlags : uint16_t;
 }
 #endif //  MOZILLA_INTERNAL_API
 namespace gfx {
 
-static const int32_t kVRExternalVersion = 1;
+static const int32_t kVRExternalVersion = 2;
 
 // We assign VR presentations to groups with a bitmask.
 // Currently, we will only display either content or chrome.
 // Later, we will have more groups to support VR home spaces and
 // multitasking environments.
 // These values are not exposed to regular content and only affect
 // chrome-only API's.  They may be changed at any time.
 static const uint32_t kVRGroupNone = 0;
@@ -354,16 +354,18 @@ struct VRLayerState
   };
 };
 
 struct VRBrowserState
 {
 #if defined(__ANDROID__)
   bool shutdown;
 #endif // defined(__ANDROID__)
+  bool presentationActive;
+  bool navigationTransitionActive;
   VRLayerState layerState[kVRLayerMaxCount];
 };
 
 struct VRSystemState
 {
   bool enumerationCompleted;
   VRDisplayState displayState;
   VRHMDSensorState sensorState;
--- a/gfx/vr/gfxVRExternal.cpp
+++ b/gfx/vr/gfxVRExternal.cpp
@@ -51,17 +51,17 @@ using namespace mozilla::gfx;
 using namespace mozilla::gfx::impl;
 using namespace mozilla::layers;
 using namespace mozilla::dom;
 
 int VRDisplayExternal::sPushIndex = 0;
 
 VRDisplayExternal::VRDisplayExternal(const VRDisplayState& aDisplayState)
   : VRDisplayHost(VRDeviceType::External)
-  , mIsPresenting(false)
+  , mBrowserState{}
   , mLastSensorState{}
 {
   MOZ_COUNT_CTOR_INHERITED(VRDisplayExternal, VRDisplayHost);
   mDisplayInfo.mDisplayState = aDisplayState;
 
   // default to an identity quaternion
   mLastSensorState.pose.orientation[3] = 1.0f;
 }
@@ -81,67 +81,65 @@ VRDisplayExternal::Destroy()
 void
 VRDisplayExternal::ZeroSensor()
 {
 }
 
 void
 VRDisplayExternal::Refresh()
 {
-  VRManager *vm = VRManager::Get();
-  VRSystemManagerExternal* manager = vm->GetExternalManager();
 
-  manager->PullState(&mDisplayInfo.mDisplayState, &mLastSensorState, mDisplayInfo.mControllerState);
+  if (!mVRNavigationTransitionEnd.IsNull() &&
+      TimeStamp::Now() > mVRNavigationTransitionEnd) {
+    mBrowserState.navigationTransitionActive = false;
+  }
+
+  PullState();
+  PushState();
 }
 
 VRHMDSensorState
 VRDisplayExternal::GetSensorState()
 {
   return mLastSensorState;
 }
 
 void
 VRDisplayExternal::StartPresentation()
 {
-  if (mIsPresenting) {
+  if (mBrowserState.presentationActive) {
     return;
   }
   sPushIndex = 0;
-  mIsPresenting = true;
   mTelemetry.Clear();
   mTelemetry.mPresentationStart = TimeStamp::Now();
 
   // Indicate that we are ready to start immersive mode
-  VRBrowserState state;
-  memset(&state, 0, sizeof(VRBrowserState));
-  state.layerState[0].type = VRLayerType::LayerType_Stereo_Immersive;
-  VRManager *vm = VRManager::Get();
-  VRSystemManagerExternal* manager = vm->GetExternalManager();
-  manager->PushState(&state);
+  mBrowserState.presentationActive = true;
+  mBrowserState.layerState[0].type = VRLayerType::LayerType_Stereo_Immersive;
+  PushState();
 
   // TODO - Implement telemetry:
 
   // mTelemetry.mLastDroppedFrameCount = stats.m_nNumReprojectedFrames;
 }
 
 void
 VRDisplayExternal::StopPresentation()
 {
-  if (!mIsPresenting) {
+  if (!mBrowserState.presentationActive) {
     return;
   }
-  mIsPresenting = false;
   sPushIndex = 0;
 
   // Indicate that we have stopped immersive mode
-  VRBrowserState state;
-  memset(&state, 0, sizeof(VRBrowserState));
-  VRManager *vm = VRManager::Get();
-  VRSystemManagerExternal* manager = vm->GetExternalManager();
-  manager->PushState(&state, true);
+  mBrowserState.presentationActive = false;
+  memset(mBrowserState.layerState, 0, sizeof(VRLayerState) * mozilla::ArrayLength(mBrowserState.layerState));
+
+  PushState(true);
 
   // TODO - Implement telemetry:
 
 /*
   const TimeDuration duration = TimeStamp::Now() - mTelemetry.mPresentationStart;
   Telemetry::Accumulate(Telemetry::WEBVR_USERS_VIEW_IN, 2);
   Telemetry::Accumulate(Telemetry::WEBVR_TIME_SPENT_VIEWING_IN_OPENVR,
                         duration.ToMilliseconds());
@@ -149,16 +147,35 @@ VRDisplayExternal::StopPresentation()
   ::vr::Compositor_CumulativeStats stats;
   mVRCompositor->GetCumulativeStats(&stats, sizeof(::vr::Compositor_CumulativeStats));
   const uint32_t droppedFramesPerSec = (stats.m_nNumReprojectedFrames -
                                         mTelemetry.mLastDroppedFrameCount) / duration.ToSeconds();
   Telemetry::Accumulate(Telemetry::WEBVR_DROPPED_FRAMES_IN_OPENVR, droppedFramesPerSec);
 */
 }
 
+void
+VRDisplayExternal::StartVRNavigation()
+{
+  mBrowserState.navigationTransitionActive = true;
+  mVRNavigationTransitionEnd = TimeStamp();
+  PushState();
+}
+
+void
+VRDisplayExternal::StopVRNavigation(const TimeDuration& aTimeout)
+{
+  if (aTimeout.ToMilliseconds() <= 0) {
+    mBrowserState.navigationTransitionActive = false;
+    mVRNavigationTransitionEnd = TimeStamp();
+    PushState();
+  }
+  mVRNavigationTransitionEnd = TimeStamp::Now() + aTimeout;
+}
+
 bool
 VRDisplayExternal::PopulateLayerTexture(const layers::SurfaceDescriptor& aTexture,
                                         VRLayerTextureType* aTextureType,
                                         VRLayerTextureHandle* aTextureHandle)
 {
   switch (aTexture.type()) {
 #if defined(XP_WIN)
     case SurfaceDescriptor::TSurfaceDescriptorD3D10: {
@@ -202,70 +219,97 @@ VRDisplayExternal::PopulateLayerTexture(
 }
 
 bool
 VRDisplayExternal::SubmitFrame(const layers::SurfaceDescriptor& aTexture,
                                uint64_t aFrameId,
                                const gfx::Rect& aLeftEyeRect,
                                const gfx::Rect& aRightEyeRect)
 {
-  VRBrowserState state;
-  memset(&state, 0, sizeof(VRBrowserState));
-  state.layerState[0].type = VRLayerType::LayerType_Stereo_Immersive;
-  VRLayer_Stereo_Immersive& layer = state.layerState[0].layer_stereo_immersive;
+  MOZ_ASSERT(mBrowserState.layerState[0].type == VRLayerType::LayerType_Stereo_Immersive);
+  VRLayer_Stereo_Immersive& layer = mBrowserState.layerState[0].layer_stereo_immersive;
   if (!PopulateLayerTexture(aTexture, &layer.mTextureType, &layer.mTextureHandle)) {
     return false;
   }
   layer.mFrameId = aFrameId;
   layer.mInputFrameId = mDisplayInfo.mLastSensorState[mDisplayInfo.mFrameId % kVRMaxLatencyFrames].inputFrameID;
 
   layer.mLeftEyeRect.x = aLeftEyeRect.x;
   layer.mLeftEyeRect.y = aLeftEyeRect.y;
   layer.mLeftEyeRect.width = aLeftEyeRect.width;
   layer.mLeftEyeRect.height = aLeftEyeRect.height;
   layer.mRightEyeRect.x = aRightEyeRect.x;
   layer.mRightEyeRect.y = aRightEyeRect.y;
   layer.mRightEyeRect.width = aRightEyeRect.width;
   layer.mRightEyeRect.height = aRightEyeRect.height;
 
-  VRManager *vm = VRManager::Get();
-  VRSystemManagerExternal* manager = vm->GetExternalManager();
-  manager->PushState(&state, true);
+  PushState(true);
   sPushIndex++;
 
-  VRDisplayState displayState;
-  memset(&displayState, 0, sizeof(VRDisplayState));
 #if defined(MOZ_WIDGET_ANDROID)
-  manager->PullState(&displayState, &mLastSensorState, mDisplayInfo.mControllerState, [&]() {
-    return (displayState.mLastSubmittedFrameId >= aFrameId) || displayState.mSuppressFrames || !displayState.mIsConnected;
+  PullState([&]() {
+    return (mDisplayInfo.mDisplayState.mLastSubmittedFrameId >= aFrameId) ||
+            mDisplayInfo.mDisplayState.mSuppressFrames ||
+            !mDisplayInfo.mDisplayState.mIsConnected;
   });
 
-  if (displayState.mSuppressFrames || !displayState.mIsConnected) {
+  if (mDisplayInfo.mDisplayState.mSuppressFrames || !mDisplayInfo.mDisplayState.mIsConnected) {
     // External implementation wants to supress frames, service has shut down or hardware has been disconnected.
     return false;
   }
 #else
-  while (displayState.mLastSubmittedFrameId < aFrameId) {
-    if (manager->PullState(&displayState, &mLastSensorState, mDisplayInfo.mControllerState)) {
-      if (displayState.mSuppressFrames || !displayState.mIsConnected) {
+  while (mDisplayInfo.mDisplayState.mLastSubmittedFrameId < aFrameId) {
+    if (PullState()) {
+      if (mDisplayInfo.mDisplayState.mSuppressFrames || !mDisplayInfo.mDisplayState.mIsConnected) {
         // External implementation wants to supress frames, service has shut down or hardware has been disconnected.
         return false;
       }
     }
 #ifdef XP_WIN
     Sleep(0);
 #else
     sleep(0);
 #endif
   }
 #endif // defined(MOZ_WIDGET_ANDROID)
 
-  return displayState.mLastSubmittedFrameSuccessful;
+  return mDisplayInfo.mDisplayState.mLastSubmittedFrameSuccessful;
+}
+
+void
+VRDisplayExternal::PushState(bool aNotifyCond)
+{
+  VRManager *vm = VRManager::Get();
+  VRSystemManagerExternal* manager = vm->GetExternalManager();
+  manager->PushState(&mBrowserState, aNotifyCond);
 }
 
+#if defined(MOZ_WIDGET_ANDROID)
+bool
+VRDisplayExternal::PullState(const std::function<bool()>& aWaitCondition)
+{
+  VRManager *vm = VRManager::Get();
+  VRSystemManagerExternal* manager = vm->GetExternalManager();
+  return manager->PullState(&mDisplayInfo.mDisplayState,
+                            &mLastSensorState,
+                            mDisplayInfo.mControllerState,
+                            aWaitCondition);
+}
+#else
+bool
+VRDisplayExternal::PullState()
+{
+  VRManager *vm = VRManager::Get();
+  VRSystemManagerExternal* manager = vm->GetExternalManager();
+  return manager->PullState(&mDisplayInfo.mDisplayState,
+                            &mLastSensorState,
+                            mDisplayInfo.mControllerState);
+}
+#endif
+
 VRSystemManagerExternal::VRSystemManagerExternal(VRExternalShmem* aAPIShmem /* = nullptr*/)
  : mExternalShmem(aAPIShmem)
 #if !defined(MOZ_WIDGET_ANDROID)
  , mSameProcess(aAPIShmem != nullptr)
 #endif
 {
 #if defined(XP_MACOSX)
   mShmemFD = 0;
--- a/gfx/vr/gfxVRExternal.h
+++ b/gfx/vr/gfxVRExternal.h
@@ -32,16 +32,18 @@ class VRDisplayExternal : public VRDispl
 public:
   void ZeroSensor() override;
   static int sPushIndex;
 
 protected:
   VRHMDSensorState GetSensorState() override;
   void StartPresentation() override;
   void StopPresentation() override;
+  void StartVRNavigation() override;
+  void StopVRNavigation(const TimeDuration& aTimeout) override;
 
   bool SubmitFrame(const layers::SurfaceDescriptor& aTexture,
                    uint64_t aFrameId,
                    const gfx::Rect& aLeftEyeRect,
                    const gfx::Rect& aRightEyeRect) override;
 
 public:
   explicit VRDisplayExternal(const VRDisplayState& aDisplayState);
@@ -50,19 +52,26 @@ public:
 protected:
   virtual ~VRDisplayExternal();
   void Destroy();
 
 private:
   bool PopulateLayerTexture(const layers::SurfaceDescriptor& aTexture,
                             VRLayerTextureType* aTextureType,
                             VRLayerTextureHandle* aTextureHandle);
+  void PushState(bool aNotifyCond = false);
+#if defined(MOZ_WIDGET_ANDROID)
+  bool PullState(const std::function<bool()>& aWaitCondition = nullptr);
+#else
+  bool PullState();
+#endif
 
   VRTelemetry mTelemetry;
-  bool mIsPresenting;
+  TimeStamp mVRNavigationTransitionEnd;
+  VRBrowserState mBrowserState;
   VRHMDSensorState mLastSensorState;
 };
 
 } // namespace impl
 
 class VRSystemManagerExternal : public VRSystemManager
 {
 public:
--- a/gfx/vr/ipc/PVRManager.ipdl
+++ b/gfx/vr/ipc/PVRManager.ipdl
@@ -60,16 +60,18 @@ parent:
   async SetDisplayInfoToMockDisplay(uint32_t aDeviceID, VRDisplayInfo aDisplayInfo);
   async SetSensorStateToMockDisplay(uint32_t aDeviceID, VRHMDSensorState aSensorState);
 
   async NewButtonEventToMockController(uint32_t aDeviceID, long aButton,
                                        bool aPressed);
   async NewAxisMoveEventToMockController(uint32_t aDeviceID, long aAxis,
                                          double aValue);
   async NewPoseMoveToMockController(uint32_t aDeviceID, GamepadPoseState aPose);
+  async StartVRNavigation(uint32_t aDeviceID);
+  async StopVRNavigation(uint32_t aDeviceID, TimeDuration aDuration);
 
 child:
   // Notify children of updated VR display enumeration and details.  This will
   // be sent to all children when the parent receives RefreshDisplays, even
   // if no changes have been detected.  This ensures that Promises exposed
   // through DOM calls are always resolved.
   async UpdateDisplayInfo(VRDisplayInfo[] aDisplayUpdates);
 
--- a/gfx/vr/ipc/VRLayerChild.cpp
+++ b/gfx/vr/ipc/VRLayerChild.cpp
@@ -1,15 +1,16 @@
 /* -*- 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 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 "VRLayerChild.h"
+#include "gfxPlatform.h"
 #include "GLScreenBuffer.h"
 #include "mozilla/layers/TextureClientSharedSurface.h"
 #include "SharedSurface.h"                // for SharedSurface
 #include "SharedSurfaceGL.h"              // for SharedSurface
 #include "mozilla/layers/LayersMessages.h" // for TimedTexture
 #include "nsICanvasRenderingContextInternal.h"
 #include "mozilla/dom/HTMLCanvasElement.h"
 #include "mozilla/layers/SyncObject.h" // for SyncObjectClient
--- a/gfx/vr/ipc/VRManagerParent.cpp
+++ b/gfx/vr/ipc/VRManagerParent.cpp
@@ -429,10 +429,26 @@ VRManagerParent::SendReplyGamepadVibrate
   if (mHaveControllerListener &&
       (mIsContentChild || IsSameProcess())) {
     return PVRManagerParent::SendReplyGamepadVibrateHaptic(aPromiseID);
   }
 
   return true;
 }
 
+mozilla::ipc::IPCResult
+VRManagerParent::RecvStartVRNavigation(const uint32_t& aDeviceID)
+{
+  VRManager* vm = VRManager::Get();
+  vm->StartVRNavigation(aDeviceID);
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+VRManagerParent::RecvStopVRNavigation(const uint32_t& aDeviceID, const TimeDuration& aTimeout)
+{
+  VRManager* vm = VRManager::Get();
+  vm->StopVRNavigation(aDeviceID, aTimeout);
+  return IPC_OK();
+}
+
 } // namespace gfx
 } // namespace mozilla
--- a/gfx/vr/ipc/VRManagerParent.h
+++ b/gfx/vr/ipc/VRManagerParent.h
@@ -68,17 +68,18 @@ protected:
                                                                   const VRDisplayInfo& aDisplayInfo) override;
   virtual mozilla::ipc::IPCResult RecvSetSensorStateToMockDisplay(const uint32_t& aDeviceID,
                                                                   const VRHMDSensorState& aSensorState) override;
   virtual mozilla::ipc::IPCResult RecvNewButtonEventToMockController(const uint32_t& aDeviceID, const long& aButton,
                                                                      const bool& aPressed) override;
   virtual mozilla::ipc::IPCResult RecvNewAxisMoveEventToMockController(const uint32_t& aDeviceID, const long& aAxis,
                                                                        const double& aValue) override;
   virtual mozilla::ipc::IPCResult RecvNewPoseMoveToMockController(const uint32_t& aDeviceID, const GamepadPoseState& pose) override;
-
+  virtual mozilla::ipc::IPCResult RecvStartVRNavigation(const uint32_t& aDeviceID) override;
+  virtual mozilla::ipc::IPCResult RecvStopVRNavigation(const uint32_t& aDeviceID, const TimeDuration& aTimeout) override;
 private:
   void RegisterWithManager();
   void UnregisterFromManager();
 
   void Bind(Endpoint<PVRManagerParent>&& aEndpoint);
 
   static void RegisterVRManagerInVRListenerThread(VRManagerParent* aVRManager);
 
--- a/ipc/ipdl/sync-messages.ini
+++ b/ipc/ipdl/sync-messages.ini
@@ -990,20 +990,18 @@ platform = win
 [PImageBridge::WillClose]
 description =
 [PImageBridge::NewCompositable]
 description =
 [PLayerTransaction::SetTestSampleTime]
 description =
 [PLayerTransaction::LeaveTestMode]
 description =
-[PLayerTransaction::GetAnimationOpacity]
-description =
-[PLayerTransaction::GetAnimationTransform]
-description =
+[PLayerTransaction::GetAnimationValue]
+description = test only
 [PLayerTransaction::GetTransform]
 description = test only
 [PLayerTransaction::SetAsyncScrollOffset]
 description =
 [PLayerTransaction::SetAsyncZoom]
 description =
 [PLayerTransaction::GetAPZTestData]
 description =
@@ -1020,19 +1018,17 @@ description =
 [PWebRenderBridge::Create]
 description =
 [PWebRenderBridge::GetSnapshot]
 description =
 [PWebRenderBridge::SetTestSampleTime]
 description = test only
 [PWebRenderBridge::LeaveTestMode]
 description = test only
-[PWebRenderBridge::GetAnimationOpacity]
-description = test only
-[PWebRenderBridge::GetAnimationTransform]
+[PWebRenderBridge::GetAnimationValue]
 description = test only
 [PWebRenderBridge::SetAsyncScrollOffset]
 description = test only
 [PWebRenderBridge::SetAsyncZoom]
 description = test only
 [PWebRenderBridge::GetAPZTestData]
 description = test only
 [PWebRenderBridge::ShutdownSync]
--- a/layout/base/nsLayoutDebugger.cpp
+++ b/layout/base/nsLayoutDebugger.cpp
@@ -134,36 +134,38 @@ PrintDisplayItemTo(nsDisplayListBuilder*
       contentData.AppendLiteral(" class:");
       contentData.Append(tmp);
     }
   }
   bool snap;
   nsRect rect = aItem->GetBounds(aBuilder, &snap);
   nsRect layerRect = rect - (*aItem->GetAnimatedGeometryRoot())->GetOffsetToCrossDoc(aItem->ReferenceFrame());
   nsRect vis = aItem->GetPaintRect();
+  nsRect build = aItem->GetBuildingRect();
   nsRect component = aItem->GetComponentAlphaBounds(aBuilder);
   nsDisplayList* list = aItem->GetChildren();
   const DisplayItemClip& clip = aItem->GetClip();
   nsRegion opaque = aItem->GetOpaqueRegion(aBuilder, &snap);
 
 #ifdef MOZ_DUMP_PAINTING
   if (aDumpHtml && aItem->Painted()) {
     nsCString string(aItem->Name());
     string.Append('-');
     string.AppendInt((uint64_t)aItem);
     aStream << nsPrintfCString("<a href=\"javascript:ViewImage('%s')\">", string.BeginReading());
   }
 #endif
 
-  aStream << nsPrintfCString("%s p=0x%p f=0x%p(%s) key=%d %sbounds(%d,%d,%d,%d) layerBounds(%d,%d,%d,%d) visible(%d,%d,%d,%d) componentAlpha(%d,%d,%d,%d) clip(%s) asr(%s) clipChain(%s)%s ref=0x%p agr=0x%p",
+  aStream << nsPrintfCString("%s p=0x%p f=0x%p(%s) key=%d %sbounds(%d,%d,%d,%d) layerBounds(%d,%d,%d,%d) visible(%d,%d,%d,%d) building(%d,%d,%d,%d) componentAlpha(%d,%d,%d,%d) clip(%s) asr(%s) clipChain(%s)%s ref=0x%p agr=0x%p",
           aItem->Name(), aItem, (void*)f, NS_ConvertUTF16toUTF8(contentData).get(), aItem->GetPerFrameKey(),
           (aItem->ZIndex() ? nsPrintfCString("z=%d ", aItem->ZIndex()).get() : ""),
           rect.x, rect.y, rect.width, rect.height,
           layerRect.x, layerRect.y, layerRect.width, layerRect.height,
           vis.x, vis.y, vis.width, vis.height,
+          build.x, build.y, build.width, build.height,
           component.x, component.y, component.width, component.height,
           clip.ToString().get(),
           ActiveScrolledRoot::ToString(aItem->GetActiveScrolledRoot()).get(),
           DisplayItemClipChain::ToString(aItem->GetClipChain()).get(),
           aItem->IsUniform(aBuilder) ? " uniform" : "",
           aItem->ReferenceFrame(), aItem->GetAnimatedGeometryRoot()->mFrame);
 
   for (auto iter = opaque.RectIter(); !iter.Done(); iter.Next()) {
--- a/layout/painting/RetainedDisplayListBuilder.cpp
+++ b/layout/painting/RetainedDisplayListBuilder.cpp
@@ -284,17 +284,28 @@ public:
     OldListIndex oldIndex;
     if (!HasModifiedFrame(aNewItem) &&
         HasMatchingItemInOldList(aNewItem, &oldIndex)) {
       nsDisplayItem* oldItem = mOldItems[oldIndex.val].mItem;
       MOZ_DIAGNOSTIC_ASSERT(oldItem->GetPerFrameKey() == aNewItem->GetPerFrameKey() &&
                             oldItem->Frame() == aNewItem->Frame());
       if (!mOldItems[oldIndex.val].IsChanged()) {
         MOZ_DIAGNOSTIC_ASSERT(!mOldItems[oldIndex.val].IsUsed());
-        nsDisplayItem* destItem = ShouldUseNewItem(aNewItem) ? aNewItem : oldItem;
+        nsDisplayItem* destItem;
+        if (ShouldUseNewItem(aNewItem)) {
+          destItem = aNewItem;
+        } else {
+          destItem = oldItem;
+          // The building rect can depend on the overflow rect (when the parent
+          // frame is position:fixed), which can change without invalidating
+          // the frame/items. If we're using the old item, copy the building
+          // rect across from the new item.
+          oldItem->SetBuildingRect(aNewItem->GetBuildingRect());
+        }
+
         if (aNewItem->GetChildren()) {
           Maybe<const ActiveScrolledRoot*> containerASRForChildren;
           if (mBuilder->MergeDisplayLists(aNewItem->GetChildren(),
                                           oldItem->GetChildren(),
                                           destItem->GetChildren(),
                                           containerASRForChildren,
                                           aNewItem->GetPerFrameKey())) {
             destItem->InvalidateCachedChildInfo();
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/1475971-1-ref.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+<style>
+div {
+  width: 200px;
+  height: 200px;
+}
+</style>
+
+<div style="position:fixed;">
+  <div style="transform:translateX(50px);">
+    <div id="resize" style="background-color:blue; width: 300px">
+    </div>
+  </div>
+</div>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/1475971-1.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<style>
+div {
+  width: 200px;
+  height: 200px;
+}
+</style>
+
+<div style="position:fixed;">
+  <div style="transform:translateX(50px);">
+    <div id="resize" style="background-color:blue;">
+    </div>
+  </div>
+</div>
+
+<script>
+  function doTest() {
+    document.getElementById("resize").style.width = "300px";
+    document.documentElement.removeAttribute("class");
+  }
+  window.addEventListener("MozReftestInvalidate", doTest);
+</script>
+</html>
--- a/layout/reftests/bugs/reftest.list
+++ b/layout/reftests/bugs/reftest.list
@@ -2073,8 +2073,9 @@ fuzzy-if(Android,66,574) fuzzy-if(d2d,89
 == 1432541.html 1432541-ref.html
 pref(layout.css.moz-document.url-prefix-hack.enabled,true) == 1446470.html 1035091-ref.html
 pref(layout.css.moz-document.url-prefix-hack.enabled,false) == 1446470-2.html 1035091-ref.html
 test-pref(layout.css.prefixes.gradients,false) == 1451874.html 1451874-ref.html
 == 1456111-1.html about:blank
 test-pref(layout.css.contain.enabled,false) == 1466008.html 1466008-ref.html
 fuzzy(1,625) == 1466638-1.html 1466638-1-ref.html
 == bug1472465-1.html bug1472465-1-ref.html
+== 1475971-1.html 1475971-1-ref.html
--- a/media/webrtc/signaling/src/media-conduit/VideoConduit.cpp
+++ b/media/webrtc/signaling/src/media-conduit/VideoConduit.cpp
@@ -597,17 +597,16 @@ WebrtcVideoConduit::VideoStreamFactory::
 
   // XXX webrtc.org code has a restriction on simulcast layers that each
   // layer must be 1/2 the dimension of the previous layer - not sure why.
   // This means we can't use scaleResolutionBy/scaleDownBy (yet), even if
   // the user specified it.  The one exception is that we can apply it on
   // the full-resolution stream (which also happens to handle the
   // non-simulcast usage case). NOTE: we make an assumption here, not in the
   // spec, that the first stream is the full-resolution stream.
-  auto& simulcastEncoding = mConduit->mCurSendCodecConfig->mSimulcastEncodings[0];
 #if 0
   // XXX What we'd like to do for each simulcast stream...
   if (simulcastEncoding.constraints.scaleDownBy > 1.0) {
     uint32_t new_width = width / simulcastEncoding.constraints.scaleDownBy;
     uint32_t new_height = height / simulcastEncoding.constraints.scaleDownBy;
 
     if (new_width != width || new_height != height) {
       if (streamCount == 1) {
@@ -634,17 +633,17 @@ WebrtcVideoConduit::VideoStreamFactory::
 
     // width/height will be overridden on the first frame; they must be 'sane' for
     // SetSendCodec()
     video_stream.width = width >> idx;
     video_stream.height = height >> idx;
     // We want to ensure this picks up the current framerate, so indirect
     video_stream.max_framerate = mConduit->mSendingFramerate;
 
-    simulcastEncoding = mConduit->mCurSendCodecConfig->mSimulcastEncodings[idx];
+    auto& simulcastEncoding = mConduit->mCurSendCodecConfig->mSimulcastEncodings[idx];
     MOZ_ASSERT(simulcastEncoding.constraints.scaleDownBy >= 1.0);
 
     // Calculate these first
     video_stream.max_bitrate_bps = MinIgnoreZero(simulcastEncoding.constraints.maxBr,
                                                  kDefaultMaxBitrate_bps);
     video_stream.max_bitrate_bps = MinIgnoreZero((int) mConduit->mPrefMaxBitrate*1000,
                                                  video_stream.max_bitrate_bps);
     video_stream.min_bitrate_bps = (mConduit->mMinBitrate ?
@@ -1626,16 +1625,17 @@ struct ResolutionAndBitrateLimits
 // 30fps.
 
 // XXX Populate this based on a pref (which we should consider sorting because
 // people won't assume they need to).
 static ResolutionAndBitrateLimits kResolutionAndBitrateLimits[] = {
   {MB_OF(1920, 1200), KBPS(1500), KBPS(2000), KBPS(10000)}, // >HD (3K, 4K, etc)
   {MB_OF(1280, 720), KBPS(1200), KBPS(1500), KBPS(5000)}, // HD ~1080-1200
   {MB_OF(800, 480), KBPS(600), KBPS(800), KBPS(2500)}, // HD ~720
+  {MB_OF(480, 270), KBPS(300), KBPS(500), KBPS(2000)},
   {tl::Max<MB_OF(400, 240), MB_OF(352, 288)>::value, KBPS(200), KBPS(300), KBPS(1300)}, // VGA, WVGA
   {MB_OF(176, 144), KBPS(100), KBPS(150), KBPS(500)}, // WQVGA, CIF
   {0 , KBPS(40), KBPS(80), KBPS(250)} // QCIF and below
 };
 
 void
 WebrtcVideoConduit::SelectBitrates(
   unsigned short width, unsigned short height, int cap,
--- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoView.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoView.java
@@ -203,23 +203,36 @@ public class GeckoView extends FrameLayo
         }
     }
 
     public GeckoSession releaseSession() {
         if (mSession == null) {
             return null;
         }
 
+        // Cover the view while we are not drawing to the surface.
+        coverUntilFirstPaint(Color.WHITE);
+
         GeckoSession session = mSession;
         mSession.releaseDisplay(mDisplay.release());
         mSession.getOverscrollEdgeEffect().setInvalidationCallback(null);
         mSession.getCompositorController().setFirstPaintCallback(null);
+
+        if (mSession.getAccessibility().getView() == this) {
+            mSession.getAccessibility().setView(null);
+        }
+
+        if (mSession.getTextInput().getView() == this) {
+            mSession.getTextInput().setView(null);
+        }
+
         if (session.getSelectionActionDelegate() == mSelectionActionDelegate) {
             mSession.setSelectionActionDelegate(null);
         }
+
         mSession = null;
         return session;
     }
 
     /**
      * Attach a session to this view. The session should be opened before
      * attaching.
      *
@@ -286,16 +299,24 @@ public class GeckoView extends FrameLayo
 
         session.getCompositorController().setFirstPaintCallback(new Runnable() {
             @Override
             public void run() {
                 coverUntilFirstPaint(Color.TRANSPARENT);
             }
         });
 
+        if (session.getTextInput().getView() == null) {
+            session.getTextInput().setView(this);
+        }
+
+        if (session.getAccessibility().getView() == null) {
+            session.getAccessibility().setView(this);
+        }
+
         if (session.getSelectionActionDelegate() == null && mSelectionActionDelegate != null) {
             session.setSelectionActionDelegate(mSelectionActionDelegate);
         }
     }
 
     public GeckoSession getSession() {
         return mSession;
     }
@@ -317,41 +338,27 @@ public class GeckoView extends FrameLayo
         if (mSession == null) {
             setSession(new GeckoSession(), GeckoRuntime.getDefault(getContext()));
         }
 
         if (!mSession.isOpen()) {
             mSession.open(mRuntime);
         }
 
-        if (mSession.getTextInput().getView() == null) {
-            mSession.getTextInput().setView(this);
-        }
-
-        mSession.getAccessibility().setView(this);
-
         super.onAttachedToWindow();
     }
 
     @Override
     public void onDetachedFromWindow() {
         super.onDetachedFromWindow();
 
         if (mSession == null) {
             return;
         }
 
-        if (mSession.getAccessibility().getView() == this) {
-            mSession.getAccessibility().setView(null);
-        }
-
-        if (mSession.getTextInput().getView() == this) {
-            mSession.getTextInput().setView(null);
-        }
-
         // If we saved state earlier, we don't want to close the window.
         if (!mStateSaved && mSession.isOpen()) {
             mSession.close();
         }
     }
 
     @Override
     public boolean gatherTransparentRegion(final Region region) {
--- a/security/manager/ssl/tests/unit/test_signed_apps.js
+++ b/security/manager/ssl/tests/unit/test_signed_apps.js
@@ -502,13 +502,189 @@ for (let policy of cosePolicies) {
       certdb.openSignedAppFileAsync(
         testcase.root,
         original_app_path(testcase.name),
         check_open_result(testcase.name, testcase.expectedResult));
     });
   }
 }
 
+add_signature_test(COSEAndPKCS7WithSHA256, function testCOSESigTampered() {
+  let tampered = tampered_app_path("cose_sig_tampered");
+  tamper(original_app_path("cose_signed_with_pkcs7"), tampered,
+         { "META-INF/cose.sig": truncateEntry }, []);
+  certdb.openSignedAppFileAsync(
+    Ci.nsIX509CertDB.AppXPCShellRoot, tampered,
+    check_open_result("cose_sig_tampered",
+                      Cr.NS_ERROR_SIGNED_JAR_MODIFIED_ENTRY));
+});
+
+// PKCS7 is processed before COSE, so if a COSE signature file is removed or
+// tampered with, this appears as a PKCS7 signature verification failure.
+add_signature_test(COSEAndPKCS7WithSHA256, function testCOSESigRemoved() {
+  let tampered = tampered_app_path("cose_sig_removed");
+  tamper(original_app_path("cose_signed_with_pkcs7"), tampered,
+         { "META-INF/cose.sig": removeEntry }, []);
+  certdb.openSignedAppFileAsync(
+    Ci.nsIX509CertDB.AppXPCShellRoot, tampered,
+    check_open_result("cose_sig_removed",
+                      Cr.NS_ERROR_SIGNED_JAR_ENTRY_MISSING));
+});
+
+add_signature_test(COSEAndPKCS7WithSHA256, function testCOSEManifestTampered() {
+  let tampered = tampered_app_path("cose_manifest_tampered");
+  tamper(original_app_path("cose_signed_with_pkcs7"), tampered,
+         { "META-INF/cose.manifest": truncateEntry }, []);
+  certdb.openSignedAppFileAsync(
+    Ci.nsIX509CertDB.AppXPCShellRoot, tampered,
+    check_open_result("cose_manifest_tampered",
+                      Cr.NS_ERROR_SIGNED_JAR_MODIFIED_ENTRY));
+});
+
+add_signature_test(COSEAndPKCS7WithSHA256, function testCOSEManifestRemoved() {
+  let tampered = tampered_app_path("cose_manifest_removed");
+  tamper(original_app_path("cose_signed_with_pkcs7"), tampered,
+         { "META-INF/cose.manifest": removeEntry }, []);
+  certdb.openSignedAppFileAsync(
+    Ci.nsIX509CertDB.AppXPCShellRoot, tampered,
+    check_open_result("cose_manifest_removed",
+                      Cr.NS_ERROR_SIGNED_JAR_ENTRY_MISSING));
+});
+
+add_signature_test(COSEAndPKCS7WithSHA256, function testCOSEFileAdded() {
+  let tampered = tampered_app_path("cose_file_added");
+  tamper(original_app_path("cose_signed_with_pkcs7"), tampered, {},
+         [ { "name": "unsigned.txt", "content": "unsigned content!" } ]);
+  certdb.openSignedAppFileAsync(
+    Ci.nsIX509CertDB.AppXPCShellRoot, tampered,
+    check_open_result("cose_file_added",
+                      Cr.NS_ERROR_SIGNED_JAR_UNSIGNED_ENTRY));
+});
+
+add_signature_test(COSEAndPKCS7WithSHA256, function testCOSEFileRemoved() {
+  let tampered = tampered_app_path("cose_file_removed");
+  tamper(original_app_path("cose_signed_with_pkcs7"), tampered,
+         { "manifest.json": removeEntry }, []);
+  certdb.openSignedAppFileAsync(
+    Ci.nsIX509CertDB.AppXPCShellRoot, tampered,
+    check_open_result("cose_file_removed",
+                      Cr.NS_ERROR_SIGNED_JAR_ENTRY_MISSING));
+});
+
+add_signature_test(COSEAndPKCS7WithSHA256, function testCOSEFileTampered() {
+  let tampered = tampered_app_path("cose_file_tampered");
+  tamper(original_app_path("cose_signed_with_pkcs7"), tampered,
+         { "manifest.json": truncateEntry }, []);
+  certdb.openSignedAppFileAsync(
+    Ci.nsIX509CertDB.AppXPCShellRoot, tampered,
+    check_open_result("cose_file_tampered",
+                      Cr.NS_ERROR_SIGNED_JAR_MODIFIED_ENTRY));
+});
+
+add_signature_test(COSEOnly, function testOnlyCOSESigTampered() {
+  let tampered = tampered_app_path("only_cose_sig_tampered");
+  tamper(original_app_path("only_cose_signed"), tampered,
+         { "META-INF/cose.sig": truncateEntry }, []);
+  certdb.openSignedAppFileAsync(
+    Ci.nsIX509CertDB.AppXPCShellRoot, tampered,
+    check_open_result("only_cose_sig_tampered",
+                      Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID));
+});
+
+
+add_signature_test(COSEOnly, function testOnlyCOSESigRemoved() {
+  let tampered = tampered_app_path("only_cose_sig_removed");
+  tamper(original_app_path("only_cose_signed"), tampered,
+         { "META-INF/cose.sig": removeEntry }, []);
+  certdb.openSignedAppFileAsync(
+    Ci.nsIX509CertDB.AppXPCShellRoot, tampered,
+    check_open_result("only_cose_sig_removed",
+                      Cr.NS_ERROR_SIGNED_JAR_WRONG_SIGNATURE));
+});
+
+add_signature_test(COSEOnly, function testOnlyCOSEManifestTampered() {
+  let tampered = tampered_app_path("only_cose_manifest_tampered");
+  tamper(original_app_path("only_cose_signed"), tampered,
+         { "META-INF/cose.manifest": truncateEntry }, []);
+  certdb.openSignedAppFileAsync(
+    Ci.nsIX509CertDB.AppXPCShellRoot, tampered,
+    check_open_result("only_cose_manifest_tampered",
+                      Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID));
+});
+
+
+add_signature_test(COSEOnly, function testOnlyCOSEManifestRemoved() {
+  let tampered = tampered_app_path("only_cose_manifest_removed");
+  tamper(original_app_path("only_cose_signed"), tampered,
+         { "META-INF/cose.manifest": removeEntry }, []);
+  certdb.openSignedAppFileAsync(
+    Ci.nsIX509CertDB.AppXPCShellRoot, tampered,
+    check_open_result("only_cose_manifest_removed",
+                      Cr.NS_ERROR_SIGNED_JAR_WRONG_SIGNATURE));
+});
+
+add_signature_test(COSEOnly, function testOnlyCOSEFileAdded() {
+  let tampered = tampered_app_path("only_cose_file_added");
+  tamper(original_app_path("only_cose_signed"), tampered, {},
+         [ { "name": "unsigned.txt", "content": "unsigned content!" } ]);
+  certdb.openSignedAppFileAsync(
+    Ci.nsIX509CertDB.AppXPCShellRoot, tampered,
+    check_open_result("only_cose_file_added",
+                      Cr.NS_ERROR_SIGNED_JAR_UNSIGNED_ENTRY));
+});
+
+add_signature_test(COSEOnly, function testOnlyCOSEFileRemoved() {
+  let tampered = tampered_app_path("only_cose_file_removed");
+  tamper(original_app_path("only_cose_signed"), tampered,
+         { "manifest.json": removeEntry }, []);
+  certdb.openSignedAppFileAsync(
+    Ci.nsIX509CertDB.AppXPCShellRoot, tampered,
+    check_open_result("only_cose_file_removed",
+                      Cr.NS_ERROR_SIGNED_JAR_ENTRY_MISSING));
+});
+
+add_signature_test(COSEOnly, function testOnlyCOSEFileTampered() {
+  let tampered = tampered_app_path("only_cose_file_tampered");
+  tamper(original_app_path("only_cose_signed"), tampered,
+         { "manifest.json": truncateEntry }, []);
+  certdb.openSignedAppFileAsync(
+    Ci.nsIX509CertDB.AppXPCShellRoot, tampered,
+    check_open_result("only_cose_file_tampered",
+                      Cr.NS_ERROR_SIGNED_JAR_MODIFIED_ENTRY));
+});
+
+// This was signed with only COSE first, and then the contents were tampered
+// with (making the signature invalid). Then, the file was signed with
+// PKCS7/SHA1. We need to ensure that if we're configured to process COSE, this
+// verification fails.
+add_signature_test(COSEAndPKCS7WithSHA1OrSHA256, function () {
+  certdb.openSignedAppFileAsync(
+    Ci.nsIX509CertDB.AppXPCShellRoot,
+    original_app_path("cose_tampered_good_pkcs7"),
+    check_open_result("tampered COSE with good PKCS7 signature should fail " +
+                      "when COSE and PKCS7 is processed",
+                      Cr.NS_ERROR_SIGNED_JAR_MODIFIED_ENTRY));
+});
+
+add_signature_test(COSEOnly, function () {
+  certdb.openSignedAppFileAsync(
+    Ci.nsIX509CertDB.AppXPCShellRoot,
+    original_app_path("cose_tampered_good_pkcs7"),
+    check_open_result("tampered COSE with good PKCS7 signature should fail " +
+                      "when only COSE is processed",
+                      Cr.NS_ERROR_SIGNED_JAR_MODIFIED_ENTRY));
+});
+
+// If we're not processing COSE, this should verify successfully.
+add_signature_test(PKCS7WithSHA1OrSHA256, function () {
+  certdb.openSignedAppFileAsync(
+    Ci.nsIX509CertDB.AppXPCShellRoot,
+    original_app_path("cose_tampered_good_pkcs7"),
+    check_open_result("tampered COSE with good PKCS7 signature should succeed" +
+                      "when COSE is not processed",
+                      Cr.NS_OK));
+});
+
 // TODO: tampered MF, tampered SF
 // TODO: too-large MF, too-large RSA, too-large SF
 // TODO: MF and SF that end immediately after the last main header
 //       (no CR nor LF)
 // TODO: broken headers to exercise the parser
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/app_cose_tampered/META-INF/cose.manifest
@@ -0,0 +1,10 @@
+Manifest-Version: 1.0
+
+Name: README
+SHA256-Digest: bY0l9xqGJYCpqYeJ0K6q4DWUQqu0mNBFM4H4emhjiJg=
+
+Name: manifest.json
+SHA256-Digest: BTnCpT154N26RZm8bhdD43WXd0tj5bg6ofM19NLI0OE=
+
+Name: data/image.png
+SHA256-Digest: EPjkNZwya9X+pruLlxG+FACLwGC48XU4S9oZOA0lVVQ=
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..ee9f3e2ce9f4b2a290bf8e993e9995efe7e61dd5
GIT binary patch
literal 655
zc$~YD)Z)C5rD4Ih#^y*SyG4v@ERjs+22D)H22G683z(T0nV3Y<^}JseJ~yx4e*Z^T
zU$I4YijTdC0WTY;R+~rLcV0$DZdL{ZO+$49RW{~O7G@r~ih|_gjMSVQh2qTgywnth
z#Daoig_6|b5{07t{1O8t34S93Lo-7|BXa{Vhyn{(8W<UvLIn&=42=v7AjWB8F%DuH
zm|vt&oSIx<WNKDqXb@>2%*GCM)j3-xMm7#GgPD<?*@=P0fB){?i4T7K+_;P@a!otS
zqc^PFPIflm^(NWt_*Z(n#%N_;GIq@|J0dujo$UeN+Y=WjPq;o)x>}cgG0Uq3Uds+F
z<~QIq;08KKmY;=%nTfpt&Hv0zjEoE%8M9T-9*So4NW8&(-QseU>1t1&NJjp(Q9&Aq
zzUO-9sRh`~vfi`%Vn%L0|C_}QtFKQ|JJ)nMV*7;Kt2X@mdVWvGZq6L#pd=>cUt2`q
z?MmSn5-6Khc)Q2>`0d`^@hhYj*^~(i7d9>1wRmHCb?)*HciY%*8Erag9hcy9EV(Q+
zemC#MWzX$fzI4~8Ep@oGroN##gspWy!--cuvzou>Gp^T_*Gw?A7It;}<zo~-HMdMS
z_o9OCy)prAiOuO!o7d02dgtDx3wx7`ZTE(2+Z-(A<Xz=8Yt7=4S3-H4gT4kcUCxUS
z-YnR9JnztK^SIA<qE=g+$W^@QeA#2|sfrgq`db{^7eqM3lm^Zh77REW+jUQ2@g6pV
ziT^hhm&O~Wy!m_Dse!BDnbo1AOS(7os~yVK;XYncoO3k2<6CUZ!w1F^%XS2Ow=V<$
D%eVkU
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/app_cose_tampered/README
@@ -0,0 +1,2 @@
+This is the readme for the test extension.
+This app was created by unzipping only_cose_signed.zip and adding this line (thus invalidating the COSE signature).
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..f4a62faddf2af0ee6fe6340efc1c73eefec58307
GIT binary patch
literal 534
zc%17D@N?(olHy`uVBq!ia0vp^(?FPm4M^HB7CvHNV7%+;;uumf=k2+ToD2p6M>gE9
ue|cVrvAJ+z-0gRh{(o?oGm3^R>XAz*P^#J|BfJL~-wd9velF{r5}E+F^Cvd|
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/app_cose_tampered/manifest.json
@@ -0,0 +1,5 @@
+{
+  "manifest_version": 2,
+  "name": "Test Extension",
+  "version": "0.0.1"
+}
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..4f506fc65ce72ac299450764824d846dca95ba4f
GIT binary patch
literal 4214
zc%1E5dsq`!7SBvVfP|NTNU7B+3W7kGf$$0-hCCn-VhA9R(qc$L9^^?9<k2d|7O7}O
zT6I-gsZ_KmD&STtK8h7lTVGH`1(B*<h4odcV%=(Y0wR!VclVF|W4~`FbH3cU=l;&U
zbLO1${SKdl#kpfFmuucLu2+^{6Av9*#1R#>NTbM=8jK{RL8~btK#=$7Om=}Z-=NTH
zyeUvfphck(sio+HcPgrc7==cuW>`I}8(ZrQlVB3!O?Z#MpRlnXl0)2tjxB;mMZloa
zlNhOTg#kiuqg)E*OGOg36w1}+4?h|$+ek;ZArXd`igY??p~wJ<(Zxoo1j;FbOqyba
zPN&ewAgxAKlr7d8q}c|AOe2+$Pz5B?NFb3!V$mBd7E}t26!J65P3XRw0+C7~5gA96
zQiwJ$g#lTxBBLo^>QAzIg{j&sE*8LGEaHLAp~t5Nv3Sg2%buiBza>5Y51K#X6AFZo
zU;=@MdXEB)yfq;uks1;f9z<8j(1jEzClgjh73ve%nKYd~Q_6-p;d(MXJuO*pg4Miu
zW)eA(ELF>u3brhE)Y60xXKgS(RYTLIB4K1+NVq_#j?9ru=;YA!3<+#hhULh@wYf=1
z6pzb-=P_OwMExKNR;WcXDM_c1SzBcAm3chj!Xi;rMu0Bg#8nk01Td*I?!p8bIWi-S
zoD!uK&ZEMr`SX)wt=v|xYCO6M#9%Bk=?`v0-F-3VecI=^(waw0mS%!;HUqx6OfUjA
zgQ2h)c)bP-0wCxvBXRomHzP{+UVXg$BSU1Kgd0bO9r5<F%M#0m907o6gMs~#S+EZt
zn})+WOe@rh4RWbU1&xG)C2$NQA(gMy8ewlw=MWePN05*Rc=!?yPej2XaM<$+n2dzL
z@Q6A8mzW!|HvF6q8Khzz>aKhQ&V=3Yw&<x&$ASRfZg_wNY_ahe+`PjrEuy~1zc$tp
zGIuP)UAt{VjGq%TM5>AloL9tQWX)D|gfdj&M<;!1i|=!~bNt-Lm0jy!FA1`(#SPT3
z>%OUVh8<xddZ0I)aX2h!TWa<GSTg`%?BpALP91p*NEBVfc13m;hizv$WCG3&LV@3r
zAvH(i%a7R*eW2x>T&;D!T|0MsSC#K+b7#ih%I^Bz&mNvRP~Kv%njy#mXFS<6`R;y+
zv#U$N27UJmT6_1(mh5d_n_~(lx#`Vy`)eCzCF-sBuax00hcumterEyqn7AM<yT$Qb
z-Oaf7A5<*P-7>dh$Kp~$D*l7RnBxQ74NHf#z^<U_{tLpQ-5AU#+>q=w>H;_QIVh;N
zz=i19Ec0sKwefsUZ`Ij@Vngh~^njRFlf7d-d&7=e<AAHCS@19g?9^nZG*9}VU2|k(
z#5=$A2)9QbSHF3Q)|uFFvhWr+cn@`1O~%|TQ}QOaN&Hi9FYkqF58&a|zc(37*@)!!
z)9>O-3A(>gj<kJIv3r&85p^K3-Dps?$;uzSo%Lg1sAt_i{!pBLV)Sk_N7;Iyk!ukr
zbZ%mre8}e|I1;=FmTJmMqzlmhwHC3nl(-l`e+`2HuCVCT)WYgJryK1tu*=ci-HZFr
zxB^S{pTE<6x9a!%bF2S<|IgklrY+EU7ayAHcEA!ZEC!FhGk$@2Vg!*EgVo(Q48|f(
z<Le)lBuJscW?a{cG`0n6%(&y|)KL%s5F%`kw+V2<f?jBGBaSTsYRuqcv_3vS>toOJ
z`WPp_)zd@cJ1pyFPZpf>>-uXn{>gv2KF&t`VBZmQ)Bi<%L=Wmc{xraFwQ|Ow^90aR
zw)U4NZ=-+Yae?|&^Hn)M+$A=~?b5)FF!r9?59R5qnI*05KHK^(PCwXKjo+y8DjUqZ
zwo7QrQ%dx9#ao118Ow&Io}XC_E;>c~ZJOe2B;^0`cIaEyjqb0xNAgrocP5*ie_qs}
z@M`P?DHXG+cem>LoE=v_%_Roxo>}$9dtaA}iYgn^D`PvN+xqq;PTv;MSx&n*%j06&
z-dth*BCs-;QsPpIpZWb%)w+hYqPO;5TkHCb38{5hePz$dq2C(b@aeR__w^K~t@0n6
zOM<<%itBrpuMxd**Y|?Yr`uQC@xs5GIujRo*rOu&r=M4?pMvabe#V;dBtQ8mavr<m
zvL_+z&PovF6u`e#;Aj2nwz+gDF0bhqbwy5QIa2F<N_lU~y12Znf*7xK-fLz0Gu4XS
zQ`VQxYHLb<^z5ut|I&}^nO+^6WFEc!l)+BYyb=stwz8Ff*e!uu=kb-=ZM9_D5A^=7
zWhYiVzS4cZBWvkF%A@`EkBHbk-uM0ba#AlhM3aO_mH5)PryAzGi{w3c?dQdx^j?@I
zD!M;_Nh=#{UUYasLS68qa8;nZH>ynd)tYDZbCg~k$?L(-8hhT%^;aps_Xpf)E6BNR
zCkKD)_Yn>}Iut$B7jRwur4(8Bj}w$#G4!B^7phA4#V>)2_#Yg*!K0J);#*3W*|tw@
zyz)I;i0cn^WwjKF1IQ;4Gq4p6n_4tMVCsJm(yvI7u?(w;gFRok+a8OIPdIqSm<ES<
z;M{9iRjVp{WXBeK8PzZJWW2fjQ@Y!@6a@gB8Nj@)HaoxjmqEhCp<{NH;p>{)+>=&Z
zsILyhZEo=u4_-3)neNxz&L`C{8bdhyH*v4m8dsOMymfu>YNjc#zJE)pxWCtj_+ag(
zQ!(wIC2UyJO5QyG+xkNltv{?26C+M{_x-&hqQ>n1A!V_b{>E`rPul5KNs~@TPKKs|
zwrwZc<JSO9XRgMy67KX2de#xv?;Pyj*LS5+b2h8?eEDnd|4MP;&Hlw#a(h>LVtd6L
zkEV+O(K`p?9(T|ELeAM<8O+;s6Ku8J!Vdj1|Na%%qf47=O6IiRiS_<w2iAeMj5VZJ
z-6CEN>IgpTFvZma!*gG<ZS&5DHn@PF{OGO@J)cx;#RN_)gQ5P%LfPn;{k-!SR|Zp<
zDWjx&B5OuTDDs?w295G+3Z$Y^(-4j%L~9TPvn5=rEIE?RQ8F{wnoMi%xw<a&^9+=(
zTf}|*rb$#Di^)hyB_%Qcl(7GSW+N{su~3v6pQ{t{6I9$#qe8(l36wC*%*%<;E{y#n
z-yesI5QtOKl)+M_l2dFHWoQ|!@M0NVUKE!10{8OPVxpb-X#pxhu8dI-nW>P<O-lMg
zs)-*@UdV}(y}}Nf7!yrI1apgEc}hlcfVem(fmR%%B{P#%h?p~9`zL+D7(P@HuL(CX
z6%o<|POw15<`u$e{G!ynlnAZmO8}rd_SLQD=v4|}tQ%2lsCDx=1}%EgN?K#AyR|V)
z4DhPn&Dw@_>oR7;0KTM=v4&f7$1yl!W6dP3vDU0%+}5_%9KsrFooZv)Vh5|lvxZtH
b%oy~Z!^_i$&#@W)BzEZUCW<N=KJ7mNS~lp#
--- a/security/manager/ssl/tests/unit/test_signed_apps/moz.build
+++ b/security/manager/ssl/tests/unit/test_signed_apps/moz.build
@@ -1,25 +1,24 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 @template
-def SignedAppFile(name, flags):
+def SignedAppFile(name, flags, app_directory='app/'):
     if not CONFIG['COMPILE_ENVIRONMENT']:
         return
 
     GENERATED_FILES += [name]
     props = GENERATED_FILES[name]
     props.script = '/security/manager/ssl/tests/unit/sign_app.py'
-    props.inputs = ['app/']
-    if flags:
-        props.flags = flags
+    props.inputs = [app_directory]
+    props.flags = flags
     # Turn RELATIVEDIR into list entry: like
     # 'security/manager/ssl/tests/unit/test_signed_apps' ->
     # TEST_HARNESS_FILES.xpcshell.security.manager.ssl.tests.unit.test_signed_apps.
     files = TEST_HARNESS_FILES.xpcshell
     for part in RELATIVEDIR.split('/'):
         files = files[part]
     files += ['!%s' % name]
 
@@ -62,8 +61,9 @@ def SignedAppFile(name, flags):
 #            SignedAppFile(filename, args)
 #
 # COSE test-cases
 #SignedAppFile('cose_signed_with_pkcs7.zip', ['-c', 'ES256', '-p', 'sha256'])
 #SignedAppFile('cose_int_signed_with_pkcs7.zip', ['-c', 'ES256', '-r', 'xpcshell signed apps test root', '-p', 'sha256'])
 #SignedAppFile('cose_multiple_signed_with_pkcs7.zip', ['-c', 'ES256', '-c', 'ES384', '-p', 'sha256'])
 #SignedAppFile('only_cose_signed.zip', ['-c', 'ES256'])
 #SignedAppFile('only_cose_multiple_signed.zip', ['-c', 'ES384', '-c', 'ES256'])
+#SignedAppFile('cose_tampered_good_pkcs7.zip', ['-m', 'sha1', '-s', 'sha1', '-p', 'sha1'], 'app_cose_tampered/')
--- a/taskcluster/ci/build/linux.yml
+++ b/taskcluster/ci/build/linux.yml
@@ -1,13 +1,15 @@
 linux64/opt:
     description: "Linux64 Opt"
     index:
         product: firefox
         job-name: linux64-opt
+    attributes:
+        enable-full-crashsymbols: true
     treeherder:
         platform: linux64/opt
         symbol: B
     worker-type: aws-provisioner-v1/gecko-{level}-b-linux
     worker:
         max-run-time: 7200
     run:
         using: mozharness
@@ -86,16 +88,18 @@ linux64-dmd/opt:
         - linux64-rust-size
         - linux64-sccache
 
 linux64/pgo:
     description: "Linux64 PGO"
     index:
         product: firefox
         job-name: linux64-pgo
+    attributes:
+        enable-full-crashsymbols: true
     treeherder:
         platform: linux64/pgo
         symbol: B
     worker-type: aws-provisioner-v1/gecko-{level}-b-linux
     worker:
         max-run-time: 7200
     run:
         using: mozharness
@@ -146,16 +150,18 @@ linux64-fuzzing/debug:
         - linux64-rust
         - linux64-rust-size
 
 linux64/debug:
     description: "Linux64 Debug"
     index:
         product: firefox
         job-name: linux64-debug
+    attributes:
+        enable-full-crashsymbols: true
     treeherder:
         platform: linux64/debug
         symbol: B
     worker-type: aws-provisioner-v1/gecko-{level}-b-linux
     worker:
         max-run-time: 3600
     run:
         using: mozharness
@@ -202,16 +208,17 @@ linux64-plain/debug:
         - linux64-clang
         - linux64-gcc
         - linux64-rust
 
 linux64-devedition-nightly/opt:
     description: "Linux64 devedition Nightly"
     attributes:
         nightly: true
+        enable-full-crashsymbols: true
     shipping-phase: build
     shipping-product: devedition
     index:
         product: devedition
         job-name: linux64-opt
         type: nightly
     treeherder:
         platform: linux64-devedition/opt
@@ -298,16 +305,18 @@ linux64-base-toolchains/debug:
         - linux64-rust-1.27
         - linux64-sccache
 
 linux/opt:
     description: "Linux32 Opt"
     index:
         product: firefox
         job-name: linux-opt
+    attributes:
+        enable-full-crashsymbols: true
     treeherder:
         platform: linux32/opt
         symbol: B
     worker-type: aws-provisioner-v1/gecko-{level}-b-linux
     worker:
         docker-image: {in-tree: debian7-i386-build}
         max-run-time: 7200
     run:
@@ -327,16 +336,18 @@ linux/opt:
         - linux64-rust-size
         - linux64-sccache
 
 linux/debug:
     description: "Linux32 Debug"
     index:
         product: firefox
         job-name: linux-debug
+    attributes:
+        enable-full-crashsymbols: true
     treeherder:
         platform: linux32/debug
         symbol: B
     worker-type: aws-provisioner-v1/gecko-{level}-b-linux
     worker:
         docker-image: {in-tree: debian7-i386-build}
         max-run-time: 3600
     run:
@@ -357,16 +368,18 @@ linux/debug:
         - linux64-rust-size
         - linux64-sccache
 
 linux/pgo:
     description: "Linux32 PGO"
     index:
         product: firefox
         job-name: linux-pgo
+    attributes:
+        enable-full-crashsymbols: true
     treeherder:
         platform: linux32/pgo
         symbol: B
     worker-type: aws-provisioner-v1/gecko-{level}-b-linux
     worker:
         docker-image: {in-tree: debian7-i386-build}
         max-run-time: 7200
     run:
@@ -454,16 +467,17 @@ linux-rusttests/debug:
         - linux64-gcc
         - linux64-rust
         - linux64-sccache
 
 linux-devedition-nightly/opt:
     description: "Linux32 devedition Nightly"
     attributes:
         nightly: true
+        enable-full-crashsymbols: true
     shipping-phase: build
     shipping-product: devedition
     index:
         product: devedition
         job-name: linux-opt
         type: nightly
     treeherder:
         platform: linux32-devedition/opt
@@ -491,16 +505,17 @@ linux-devedition-nightly/opt:
         - linux64-rust
         - linux64-rust-size
         - linux64-sccache
 
 linux-nightly/opt:
     description: "Linux32 Nightly"
     attributes:
         nightly: true
+        enable-full-crashsymbols: true
     shipping-phase: build
     shipping-product: firefox
     index:
         product: firefox
         job-name: linux-opt
         type: nightly
     treeherder:
         platform: linux32/opt
@@ -522,17 +537,16 @@ linux-nightly/opt:
         need-xvfb: true
     toolchains:
         - linux64-clang
         - linux64-gcc
         - linux64-rust
         - linux64-rust-size
         - linux64-sccache
 
-
 linux64-asan/opt:
     description: "Linux64 Opt ASAN"
     index:
         product: firefox
         job-name: linux64-asan-opt
     treeherder:
         platform: linux64/asan
         symbol: Bo
@@ -751,16 +765,17 @@ linux64-lto/debug:
         - linux64-rust
         - linux64-rust-size
         - linux64-sccache
 
 linux64-nightly/opt:
     description: "Linux64 Nightly"
     attributes:
         nightly: true
+        enable-full-crashsymbols: true
     shipping-phase: build
     shipping-product: firefox
     index:
         product: firefox
         job-name: linux64-opt
         type: nightly
     treeherder:
         platform: linux64/opt
--- a/taskcluster/ci/build/macosx.yml
+++ b/taskcluster/ci/build/macosx.yml
@@ -1,13 +1,15 @@
 macosx64/debug:
     description: "MacOS X x64 Cross-compile"
     index:
         product: firefox
         job-name: macosx64-debug
+    attributes:
+        enable-full-crashsymbols: true
     treeherder:
         platform: osx-cross/debug
         symbol: B
         tier: 1
     worker-type: aws-provisioner-v1/gecko-{level}-b-linux
     worker:
         max-run-time: 3600
         env:
@@ -32,16 +34,18 @@ macosx64/debug:
         - linux64-rust-size
         - linux64-sccache
 
 macosx64/opt:
     description: "MacOS X x64 Cross-compile"
     index:
         product: firefox
         job-name: macosx64-opt
+    attributes:
+        enable-full-crashsymbols: true
     treeherder:
         platform: osx-cross/opt
         symbol: B
         tier: 1
     worker-type: aws-provisioner-v1/gecko-{level}-b-linux
     worker:
         max-run-time: 3600
         env:
@@ -134,16 +138,17 @@ macosx64-dmd/opt:
         - linux64-rust-macos
         - linux64-rust-size
         - linux64-sccache
 
 macosx64-devedition-nightly/opt:
     description: "MacOS X Dev Edition x64 Nightly"
     attributes:
         nightly: true
+        enable-full-crashsymbols: true
     shipping-phase: build
     shipping-product: devedition
     index:
         product: devedition
         job-name: macosx64-opt
         type: nightly
     treeherder:
         platform: osx-cross-devedition/opt
@@ -246,16 +251,17 @@ macosx64-add-on-devel/opt:
         - linux64-rust-macos
         - linux64-rust-size
         - linux64-sccache
 
 macosx64-nightly/opt:
     description: "MacOS X x64 Cross-compile Nightly"
     attributes:
         nightly: true
+        enable-full-crashsymbols: true
     shipping-phase: build
     shipping-product: firefox
     index:
         product: firefox
         job-name: macosx64-opt
         type: nightly
     treeherder:
         platform: osx-cross/opt
--- a/taskcluster/ci/build/windows.yml
+++ b/taskcluster/ci/build/windows.yml
@@ -1,13 +1,15 @@
 win32/debug:
     description: "Win32 Debug"
     index:
         product: firefox
         job-name: win32-debug
+    attributes:
+        enable-full-crashsymbols: true
     treeherder:
         platform: windows2012-32/debug
         symbol: B
         tier: 1
     worker-type: aws-provisioner-v1/gecko-{level}-b-win2012
     worker:
         max-run-time: 7200
         env:
@@ -27,16 +29,18 @@ win32/debug:
         - win64-rust-size
         - win64-sccache
 
 win32/opt:
     description: "Win32 Opt"
     index:
         product: firefox
         job-name: win32-opt
+    attributes:
+        enable-full-crashsymbols: true
     treeherder:
         platform: windows2012-32/opt
         symbol: B
         tier: 1
     stub-installer:
         # Beta and release use a stub-requiring update channel
         by-project:
             default: false
@@ -94,16 +98,18 @@ win32-dmd/opt:
         - win64-rust-size
         - win64-sccache
 
 win32/pgo:
     description: "Win32 Opt PGO"
     index:
         product: firefox
         job-name: win32-pgo
+    attributes:
+        enable-full-crashsymbols: true
     treeherder:
         platform: windows2012-32/pgo
         symbol: B
         tier: 1
     worker-type: aws-provisioner-v1/gecko-{level}-b-win2012
     worker:
         max-run-time: 9000
         env:
@@ -123,16 +129,18 @@ win32/pgo:
         - win64-rust-size
         - win64-sccache
 
 win64/debug:
     description: "Win64 Debug"
     index:
         product: firefox
         job-name: win64-debug
+    attributes:
+        enable-full-crashsymbols: true
     treeherder:
         platform: windows2012-64/debug
         symbol: B
         tier: 1
     worker-type: aws-provisioner-v1/gecko-{level}-b-win2012
     worker:
         max-run-time: 7200
         env:
@@ -181,16 +189,18 @@ win64-plain/debug:
         - win64-clang-cl
         - win64-rust
 
 win64/opt:
     description: "Win64 Opt"
     index:
         product: firefox
         job-name: win64-opt
+    attributes:
+        enable-full-crashsymbols: true
     treeherder:
         platform: windows2012-64/opt
         symbol: B
         tier: 1
     worker-type: aws-provisioner-v1/gecko-{level}-b-win2012
     worker:
         max-run-time: 7200
         env:
@@ -274,16 +284,17 @@ win64-dmd/opt:
 win32-nightly/opt:
     description: "Win32 Nightly"
     index:
         product: firefox
         job-name: win32-opt
         type: nightly
     attributes:
         nightly: true
+        enable-full-crashsymbols: true
     stub-installer:
         by-project:
             default: false
             mozilla-central: true
             try: true
             mozilla-beta: true
             mozilla-release: true
     shipping-phase: build
@@ -317,16 +328,17 @@ win32-nightly/opt:
 win64-nightly/opt:
     description: "Win64 Nightly"
     index:
         product: firefox
         job-name: win64-opt
         type: nightly
     attributes:
         nightly: true
+        enable-full-crashsymbols: true
     shipping-phase: build
     shipping-product: firefox
     treeherder:
         platform: windows2012-64/opt
         symbol: N
         tier: 1
     worker-type: aws-provisioner-v1/gecko-{level}-b-win2012
     worker:
@@ -350,16 +362,18 @@ win64-nightly/opt:
         - win64-rust-size
         - win64-sccache
 
 win64/pgo:
     description: "Win64 Opt PGO"
     index:
         product: firefox
         job-name: win64-pgo
+    attributes:
+        enable-full-crashsymbols: true
     treeherder:
         platform: windows2012-64/pgo
         symbol: B
         tier: 1
     worker-type: aws-provisioner-v1/gecko-{level}-b-win2012
     worker:
         max-run-time: 10800
         env:
@@ -688,16 +702,17 @@ win64-asan-reporter-nightly/opt:
 win32-devedition-nightly/opt:
     description: "Win32 Dev Edition Nightly"
     index:
         product: devedition
         job-name: win32-opt
         type: nightly
     attributes:
         nightly: true
+        enable-full-crashsymbols: true
     stub-installer:
         by-project:
             default: false
             mozilla-central: true
             try: true
             mozilla-beta: true
             mozilla-release: true
     shipping-phase: build
@@ -732,16 +747,17 @@ win32-devedition-nightly/opt:
 win64-devedition-nightly/opt:
     description: "Win64 Dev Edition Nightly"
     index:
         product: devedition
         job-name: win64-opt
         type: nightly
     attributes:
         nightly: true
+        enable-full-crashsymbols: true
     shipping-phase: build
     shipping-product: devedition
     treeherder:
         platform: windows2012-64-devedition/opt
         symbol: N
         tier: 1
     worker-type: aws-provisioner-v1/gecko-{level}-b-win2012
     worker:
--- a/taskcluster/docs/attributes.rst
+++ b/taskcluster/docs/attributes.rst
@@ -220,8 +220,15 @@ build the head. If we don't point at the
 (build, promote, push).
 
 artifact_prefix
 ===============
 Most taskcluster artifacts are public, so we've hardcoded ``public/build`` in a
 lot of places. To support private artifacts, we've moved this to the
 ``artifact_prefix`` attribute. It will default to ``public/build`` but will be
 overrideable per-task.
+
+enable-full-crashsymbols
+========================
+In automation, full crashsymbol package generation is normally disabled.  For
+build kinds where the full crashsymbols should be enabled, set this attribute
+to True. The full symbol packages will then be generated and uploaded on
+release branches and on try.
--- a/taskcluster/taskgraph/transforms/build.py
+++ b/taskcluster/taskgraph/transforms/build.py
@@ -4,19 +4,23 @@
 """
 Apply some defaults and minor modifications to the jobs defined in the build
 kind.
 """
 
 from __future__ import absolute_import, print_function, unicode_literals
 
 from taskgraph.transforms.base import TransformSequence
+from taskgraph.util.attributes import RELEASE_PROJECTS
 from taskgraph.util.schema import resolve_keyed_by
 from taskgraph.util.workertypes import worker_type_implementation
 
+import logging
+logger = logging.getLogger(__name__)
+
 transforms = TransformSequence()
 
 
 @transforms.add
 def set_defaults(config, jobs):
     """Set defaults, including those that differ per worker implementation"""
     for job in jobs:
         job['treeherder'].setdefault('kind', 'build')
@@ -53,8 +57,24 @@ def set_env(config, jobs):
     """Set extra environment variables from try command line."""
     env = []
     if config.params['try_mode'] == 'try_option_syntax':
         env = config.params['try_options']['env'] or []
     for job in jobs:
         if env:
             job['worker']['env'].update(dict(x.split('=') for x in env))
         yield job
+
+
+@transforms.add
+def enable_full_crashsymbols(config, jobs):
+    """Enable full crashsymbols on jobs with
+    'enable-full-crashsymbols' set to True and on release branches, or
+    on try"""
+    branches = RELEASE_PROJECTS | {'try', }
+    for job in jobs:
+        enable_full_crashsymbols = job['attributes'].get('enable-full-crashsymbols')
+        if enable_full_crashsymbols and config.params['project'] in branches:
+            logger.debug("Enabling full symbol generation for %s", job['name'])
+        else:
+            logger.debug("Disabling full symbol generation for %s", job['name'])
+            job['worker']['env']['MOZ_DISABLE_FULL_SYMBOLS'] = '1'
+        yield job
--- a/taskcluster/taskgraph/transforms/upload_symbols.py
+++ b/taskcluster/taskgraph/transforms/upload_symbols.py
@@ -5,27 +5,35 @@
 Transform the upload-symbols task description template,
 taskcluster/ci/upload-symbols/job-template.yml into an actual task description.
 """
 
 from __future__ import absolute_import, print_function, unicode_literals
 
 from taskgraph.transforms.base import TransformSequence
 
+import logging
+logger = logging.getLogger(__name__)
 
 transforms = TransformSequence()
 
 
 @transforms.add
 def fill_template(config, tasks):
     for task in tasks:
         dep = task['dependent-task']
 
         # Fill out the dynamic fields in the task description
         task['label'] = dep.label + '-upload-symbols'
+
+        # Skip tasks where we don't have the full crashsymbols enabled
+        if not dep.attributes.get('enable-full-crashsymbols'):
+            logger.debug("Skipping upload symbols task for %s", task['label'])
+            continue
+
         task['dependencies'] = {'build': dep.label}
         task['worker']['env']['GECKO_HEAD_REPOSITORY'] = config.params['head_repository']
         task['worker']['env']['GECKO_HEAD_REV'] = config.params['head_rev']
         task['worker']['env']['SYMBOL_SECRET'] = task['worker']['env']['SYMBOL_SECRET'].format(
             level=config.params['level'])
 
         build_platform = dep.attributes.get('build_platform')
         build_type = dep.attributes.get('build_type')
--- a/toolkit/modules/AppConstants.jsm
+++ b/toolkit/modules/AppConstants.jsm
@@ -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/. */
 
 "use strict";
 
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 ChromeUtils.defineModuleGetter(this, "Services", "resource://gre/modules/Services.jsm");
+ChromeUtils.defineModuleGetter(this, "AddonManager", "resource://gre/modules/AddonManager.jsm");
 
 this.EXPORTED_SYMBOLS = ["AppConstants"];
 
 // Immutable for export.
 this.AppConstants = Object.freeze({
   // See this wiki page for more details about channel specific build
   // defines: https://wiki.mozilla.org/Platform/Channel-specific_build_defines
   NIGHTLY_BUILD:
@@ -224,16 +225,27 @@ this.AppConstants = Object.freeze({
 
   MOZ_REQUIRE_SIGNING:
 #ifdef MOZ_REQUIRE_SIGNING
   true,
 #else
   false,
 #endif
 
+  get MOZ_UNSIGNED_SCOPES() {
+    let result = 0;
+#ifdef MOZ_UNSIGNED_APP_SCOPE
+    result |= AddonManager.SCOPE_APPLICATION;
+#endif
+#ifdef MOZ_UNSIGNED_SYSTEM_SCOPE
+    result |= AddonManager.SCOPE_SYSTEM;
+#endif
+    return result;
+  },
+
   MOZ_ALLOW_LEGACY_EXTENSIONS:
 #ifdef MOZ_ALLOW_LEGACY_EXTENSIONS
   true,
 #else
   false,
 #endif
 
   MENUBAR_CAN_AUTOHIDE:
--- a/toolkit/modules/moz.build
+++ b/toolkit/modules/moz.build
@@ -313,16 +313,18 @@ for var in ('ANDROID_PACKAGE_NAME',
             'MOZ_WIDGET_TOOLKIT',
             'DLL_PREFIX',
             'DLL_SUFFIX',
             'DEBUG_JS_MODULES'):
             DEFINES[var] = CONFIG[var]
 
 for var in ('MOZ_TOOLKIT_SEARCH',
             'MOZ_SYSTEM_NSS',
+            'MOZ_UNSIGNED_APP_SCOPE',
+            'MOZ_UNSIGNED_SYSTEM_SCOPE',
             'MOZ_UPDATER',
             'MOZ_ANDROID_MOZILLA_ONLINE',
             'MOZ_SWITCHBOARD'):
     if CONFIG[var]:
         DEFINES[var] = True
 
 JAR_MANIFESTS += ['jar.mn']
 
--- a/toolkit/moz.configure
+++ b/toolkit/moz.configure
@@ -1031,8 +1031,25 @@ option('--enable-proxy-bypass-protection
        help='Prevent suspected or confirmed proxy bypasses')
 
 @depends_if('--enable-proxy-bypass-protection')
 def proxy_bypass_protection(_):
     return True
 
 set_config('MOZ_PROXY_BYPASS_PROTECTION', proxy_bypass_protection)
 set_define('MOZ_PROXY_BYPASS_PROTECTION', proxy_bypass_protection)
+
+# Addon signing
+# ==============================================================
+
+option('--with-unsigned-addon-scopes',
+       nargs='+', choices=('app', 'system'),
+       help='Addon scopes where signature is not required')
+
+@depends('--with-unsigned-addon-scopes')
+def unsigned_addon_scopes(scopes):
+    return namespace(
+        app='app' in scopes or None,
+        system='system' in scopes or None,
+    )
+
+set_config('MOZ_UNSIGNED_APP_SCOPE', unsigned_addon_scopes.app)
+set_config('MOZ_UNSIGNED_SYSTEM_SCOPE', unsigned_addon_scopes.system)
--- a/toolkit/mozapps/extensions/internal/XPIInstall.jsm
+++ b/toolkit/mozapps/extensions/internal/XPIInstall.jsm
@@ -954,16 +954,19 @@ function shouldVerifySignedState(aAddon)
   // Updated system add-ons should always have their signature checked
   if (aAddon.location.name == KEY_APP_SYSTEM_ADDONS)
     return true;
 
   // We don't care about signatures for default system add-ons
   if (aAddon.location.name == KEY_APP_SYSTEM_DEFAULTS)
     return false;
 
+  if (aAddon.location.scope & AppConstants.MOZ_UNSIGNED_SCOPES)
+    return false;
+
   // Otherwise only check signatures if the add-on is one of the signed
   // types.
   return XPIDatabase.SIGNED_TYPES.has(aAddon.type);
 }
 
 /**
  * Verifies that a bundle's contents are all correctly signed by an
  * AMO-issued certificate
--- a/widget/GfxInfoBase.cpp
+++ b/widget/GfxInfoBase.cpp
@@ -1399,16 +1399,19 @@ GfxInfoBase::DescribeFeatures(JSContext*
   JS::Rooted<JSObject*> obj(aCx);
 
   gfx::FeatureStatus gpuProcess = gfxConfig::GetValue(Feature::GPU_PROCESS);
   InitFeatureObject(aCx, aObj, "gpuProcess", gpuProcess, &obj);
 
   gfx::FeatureStatus wrQualified = gfxConfig::GetValue(Feature::WEBRENDER_QUALIFIED);
   InitFeatureObject(aCx, aObj, "wrQualified", wrQualified, &obj);
 
+  gfx::FeatureStatus webrender = gfxConfig::GetValue(Feature::WEBRENDER);
+  InitFeatureObject(aCx, aObj, "webrender", webrender, &obj);
+
   // Only include AL if the platform attempted to use it.
   gfx::FeatureStatus advancedLayers = gfxConfig::GetValue(Feature::ADVANCED_LAYERS);
   if (advancedLayers != FeatureStatus::Unused) {
     InitFeatureObject(aCx, aObj, "advancedLayers", advancedLayers, &obj);
 
     if (gfxConfig::UseFallback(Fallback::NO_CONSTANT_BUFFER_OFFSETTING)) {
       JS::Rooted<JS::Value> trueVal(aCx, JS::BooleanValue(true));
       JS_SetProperty(aCx, obj, "noConstantBufferOffsetting", trueVal);
--- a/widget/MiscEvents.h
+++ b/widget/MiscEvents.h
@@ -6,16 +6,17 @@
 #ifndef mozilla_MiscEvents_h__
 #define mozilla_MiscEvents_h__
 
 #include <stdint.h>
 
 #include "mozilla/BasicEvents.h"
 #include "nsCOMPtr.h"
 #include "nsAtom.h"
+#include "nsGkAtoms.h"
 #include "nsITransferable.h"
 
 namespace mozilla {
 
 namespace dom {
   class PBrowserParent;
   class PBrowserChild;
 } // namespace dom
@@ -101,25 +102,45 @@ public:
  * XXX Should be |WidgetChromeCommandEvent|?
  ******************************************************************************/
 
 class WidgetCommandEvent : public WidgetGUIEvent
 {
 public:
   virtual WidgetCommandEvent* AsCommandEvent() override { return this; }
 
+protected:
   WidgetCommandEvent(bool aIsTrusted, nsAtom* aEventType,
                      nsAtom* aCommand, nsIWidget* aWidget)
     : WidgetGUIEvent(aIsTrusted, eUnidentifiedEvent, aWidget,
                      eCommandEventClass)
     , mCommand(aCommand)
   {
     mSpecifiedEventType = aEventType;
   }
 
+public:
+  /**
+   * Constructor to initialize an app command.  This is the only case to
+   * initialize this class as a command in C++ stack.
+   */
+  WidgetCommandEvent(bool aIsTrusted, nsAtom* aCommand, nsIWidget* aWidget)
+    : WidgetCommandEvent(aIsTrusted, nsGkAtoms::onAppCommand,
+                         aCommand, aWidget)
+  {
+  }
+
+  /**
+   * Constructor to initialize as internal event of dom::CommandEvent.
+   */
+  WidgetCommandEvent()
+    : WidgetCommandEvent(false, nullptr, nullptr, nullptr)
+  {
+  }
+
   virtual WidgetEvent* Duplicate() const override
   {
     MOZ_ASSERT(mClass == eCommandEventClass,
                "Duplicate() must be overridden by sub class");
     // Not copying widget, it is a weak reference.
     WidgetCommandEvent* result =
       new WidgetCommandEvent(false, mSpecifiedEventType, mCommand, nullptr);
     result->AssignCommandEventData(*this, true);
--- a/widget/android/nsWindow.cpp
+++ b/widget/android/nsWindow.cpp
@@ -1259,21 +1259,16 @@ nsWindow::GeckoViewSupport::Transfer(con
                 u"GeckoView:UpdateInitData");
     }
 
     DispatchToUiThread(
             "GeckoViewSupport::Transfer",
             [compositor = LayerSession::Compositor::GlobalRef(compositor)] {
                 compositor->OnCompositorAttached();
             });
-
-    // Set the first-paint flag so that we refresh viewports, etc.
-    if (RefPtr<CompositorBridgeChild> bridge = window.GetCompositorBridgeChild()) {
-        bridge->SendForceIsFirstPaint();
-    }
 }
 
 void
 nsWindow::GeckoViewSupport::AttachEditable(const GeckoSession::Window::LocalRef& inst,
                                            jni::Object::Param aEditableParent,
                                            jni::Object::Param aEditableChild)
 {
     java::GeckoEditableChild::LocalRef editableChild(inst.Env());
--- a/widget/gtk/nsWindow.cpp
+++ b/widget/gtk/nsWindow.cpp
@@ -2928,18 +2928,18 @@ nsWindow::OnContainerFocusOutEvent(GdkEv
 
     LOGFOCUS(("Done with container focus out [%p]\n", (void *)this));
 }
 
 bool
 nsWindow::DispatchCommandEvent(nsAtom* aCommand)
 {
     nsEventStatus status;
-    WidgetCommandEvent event(true, nsGkAtoms::onAppCommand, aCommand, this);
-    DispatchEvent(&event, status);
+    WidgetCommandEvent appCommandEvent(true, aCommand, this);
+    DispatchEvent(&appCommandEvent, status);
     return TRUE;
 }
 
 bool
 nsWindow::DispatchContentCommandEvent(EventMessage aMsg)
 {
   nsEventStatus status;
   WidgetContentCommandEvent event(true, aMsg, this);
--- a/widget/windows/KeyboardLayout.cpp
+++ b/widget/windows/KeyboardLayout.cpp
@@ -2141,26 +2141,27 @@ NativeKey::DispatchCommandEvent(uint32_t
       command = nsGkAtoms::PlayPause;
       break;
     default:
       MOZ_LOG(sNativeKeyLogger, LogLevel::Info,
         ("%p   NativeKey::DispatchCommandEvent(), doesn't dispatch command "
          "event", this));
       return false;
   }
-  WidgetCommandEvent commandEvent(true, nsGkAtoms::onAppCommand,
-                                  command, mWidget);
-
-  mWidget->InitEvent(commandEvent);
+  WidgetCommandEvent appCommandEvent(true, command, mWidget);
+
+  mWidget->InitEvent(appCommandEvent);
   MOZ_LOG(sNativeKeyLogger, LogLevel::Info,
-    ("%p   NativeKey::DispatchCommandEvent(), dispatching %s command event...",
+    ("%p   NativeKey::DispatchCommandEvent(), dispatching "
+     "%s app command event...",
      this, nsAtomCString(command).get()));
-  bool ok = mWidget->DispatchWindowEvent(&commandEvent) || mWidget->Destroyed();
+  bool ok =
+    mWidget->DispatchWindowEvent(&appCommandEvent) || mWidget->Destroyed();
   MOZ_LOG(sNativeKeyLogger, LogLevel::Info,
-    ("%p   NativeKey::DispatchCommandEvent(), dispatched command event, "
+    ("%p   NativeKey::DispatchCommandEvent(), dispatched app command event, "
      "result=%s, mWidget->Destroyed()=%s",
      this, GetBoolName(ok), GetBoolName(mWidget->Destroyed())));
   return ok;
 }
 
 bool
 NativeKey::HandleAppCommandMessage() const
 {
--- a/widget/windows/WinMouseScrollHandler.cpp
+++ b/widget/windows/WinMouseScrollHandler.cpp
@@ -1410,20 +1410,20 @@ MouseScrollHandler::Device::Elantech::Ha
       WinUtils::GetScanCode(aLParam) == 0 &&
       (IS_VK_DOWN(0xFF) || IS_VK_DOWN(0xCC))) {
     if (aMsg == WM_KEYDOWN) {
       MOZ_LOG(gMouseScrollLog, LogLevel::Info,
         ("MouseScroll::Device::Elantech::HandleKeyMessage(): Dispatching "
          "%s command event",
          aWParam == VK_NEXT ? "Forward" : "Back"));
 
-      WidgetCommandEvent commandEvent(true, nsGkAtoms::onAppCommand,
+      WidgetCommandEvent appCommandEvent(true,
         (aWParam == VK_NEXT) ? nsGkAtoms::Forward : nsGkAtoms::Back, aWidget);
-      InitEvent(aWidget, commandEvent);
-      aWidget->DispatchWindowEvent(&commandEvent);
+      InitEvent(aWidget, appCommandEvent);
+      aWidget->DispatchWindowEvent(&appCommandEvent);
     }
     else {
       MOZ_LOG(gMouseScrollLog, LogLevel::Info,
         ("MouseScroll::Device::Elantech::HandleKeyMessage(): Consumed"));
     }
     return true; // consume the message (doesn't need to dispatch key events)
   }