Merge autoland to mozilla-central. a=merge
authorCsoregi Natalia <ncsoregi@mozilla.com>
Fri, 12 Oct 2018 13:13:46 +0300
changeset 489172 8bd12e6c3f9979d00afb2fc699bb292a03962f0a
parent 489159 580c03c8ae389ea12073561e2b445312257d7cf3 (current diff)
parent 489171 a37d277dfb6c70f6633366d3c6ba34ddd7aeb827 (diff)
child 489173 7a0840d602524d3b4552a867092267bb3fd5e79e
push id246
push userfmarier@mozilla.com
push dateSat, 13 Oct 2018 00:15:40 +0000
reviewersmerge
milestone64.0a1
Merge autoland to mozilla-central. a=merge
--- a/browser/components/sessionstore/test/browser_formdata_xpath.js
+++ b/browser/components/sessionstore/test/browser_formdata_xpath.js
@@ -16,17 +16,17 @@ add_task(function setup() {
     Services.prefs.clearUserPref("browser.sessionstore.privacy_level");
   });
 });
 
 const FILE1 = createFilePath("346337_test1.file");
 const FILE2 = createFilePath("346337_test2.file");
 
 const FIELDS = {
-  "//input[@name='input']":     Date.now().toString(),
+  "//input[@name='input']":     Date.now().toString(16),
   "//input[@name='spaced 1']":  Math.random().toString(),
   "//input[3]":                 "three",
   "//input[@type='checkbox']":  true,
   "//input[@name='uncheck']":   false,
   "//input[@type='radio'][1]":  false,
   "//input[@type='radio'][2]":  true,
   "//input[@type='radio'][3]":  false,
   "//select":                   2,
--- a/devtools/client/inspector/flexbox/test/head.js
+++ b/devtools/client/inspector/flexbox/test/head.js
@@ -5,17 +5,23 @@
 /* import-globals-from ../../test/head.js */
 "use strict";
 
 // Import the inspector's head.js first (which itself imports shared-head.js).
 Services.scriptloader.loadSubScript(
   "chrome://mochitests/content/browser/devtools/client/inspector/test/head.js",
   this);
 
+// Make sure the flexbox inspector is enabled before running the tests.
+Services.prefs.setBoolPref("devtools.flexboxinspector.enabled", true);
+
 // Make sure only the flexbox layout accordion is opened, and the others are closed.
 Services.prefs.setBoolPref("devtools.layout.flexbox.opened", true);
 Services.prefs.setBoolPref("devtools.layout.boxmodel.opened", false);
 Services.prefs.setBoolPref("devtools.layout.grid.opened", false);
+
+// Clear all set prefs.
 registerCleanupFunction(() => {
+  Services.prefs.clearUserPref("devtools.flexboxinspector.enabled");
   Services.prefs.clearUserPref("devtools.layout.flexbox.opened");
   Services.prefs.clearUserPref("devtools.layout.boxmodel.opened");
   Services.prefs.clearUserPref("devtools.layout.grid.opened");
 });
--- a/dom/audiochannel/AudioChannelService.cpp
+++ b/dom/audiochannel/AudioChannelService.cpp
@@ -22,17 +22,17 @@
 #include "nsPIDOMWindow.h"
 #include "nsServiceManagerUtils.h"
 
 #include "mozilla/Preferences.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
-static mozilla::LazyLogModule gAudioChannelLog("AudioChannel");
+mozilla::LazyLogModule gAudioChannelLog("AudioChannel");
 
 namespace {
 
 bool sAudioChannelCompeting = false;
 bool sAudioChannelCompetingAllAgents = false;
 bool sXPCOMShuttingDown = false;
 
 class NotifyChannelActiveRunnable final : public Runnable
--- a/dom/media/AudioSegment.h
+++ b/dom/media/AudioSegment.h
@@ -217,16 +217,34 @@ struct AudioChunk {
     mBufferFormat = AUDIO_FORMAT_SILENCE;
     mPrincipalHandle = PRINCIPAL_HANDLE_NONE;
   }
 
   size_t ChannelCount() const { return mChannelData.Length(); }
 
   bool IsMuted() const { return mVolume == 0.0f; }
 
+  bool IsAudible() const
+  {
+    for (auto&& channel : mChannelData) {
+      // Transform sound into dB RMS and assume that the value smaller than -100
+      // is inaudible.
+      float dbrms = 0.0;
+      for (uint32_t idx = 0; idx < mDuration; idx++) {
+        dbrms += std::pow(static_cast<const AudioDataValue*>(channel)[idx], 2);
+      }
+      dbrms /= mDuration;
+      dbrms = std::sqrt(dbrms) != 0.0 ? 20 * log10(dbrms) : -1000.0;
+      if (dbrms > -100.0) {
+        return true;
+      }
+    }
+    return false;
+  }
+
   size_t SizeOfExcludingThisIfUnshared(MallocSizeOf aMallocSizeOf) const
   {
     return SizeOfExcludingThis(aMallocSizeOf, true);
   }
 
   size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf, bool aUnshared) const
   {
     size_t amount = 0;
--- a/dom/media/webaudio/AudioBlock.h
+++ b/dom/media/webaudio/AudioBlock.h
@@ -35,16 +35,17 @@ public:
     : AudioChunk(aChunk)
   {
     MOZ_ASSERT(aChunk.mDuration == WEBAUDIO_BLOCK_SIZE);
   }
   ~AudioBlock();
 
   using AudioChunk::GetDuration;
   using AudioChunk::IsNull;
+  using AudioChunk::IsAudible;
   using AudioChunk::ChannelCount;
   using AudioChunk::ChannelData;
   using AudioChunk::SizeOfExcludingThisIfUnshared;
   using AudioChunk::SizeOfExcludingThis;
   // mDuration is not exposed.  Use GetDuration().
   // mBuffer is not exposed.  Use SetBuffer().
   using AudioChunk::mChannelData;
   using AudioChunk::mVolume;
--- a/dom/media/webaudio/AudioDestinationNode.cpp
+++ b/dom/media/webaudio/AudioDestinationNode.cpp
@@ -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/. */
 
 #include "AudioDestinationNode.h"
 #include "AudioContext.h"
 #include "AlignmentUtils.h"
 #include "AudioContext.h"
+#include "CubebUtils.h"
 #include "mozilla/dom/AudioDestinationNodeBinding.h"
 #include "mozilla/dom/OfflineAudioCompletionEvent.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/BaseAudioContextBinding.h"
 #include "mozilla/Services.h"
 #include "AudioChannelAgent.h"
 #include "AudioChannelService.h"
 #include "AudioNodeEngine.h"
@@ -21,16 +22,21 @@
 #include "nsContentUtils.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsIDocShell.h"
 #include "nsIPermissionManager.h"
 #include "nsIScriptObjectPrincipal.h"
 #include "nsServiceManagerUtils.h"
 #include "mozilla/dom/Promise.h"
 
+extern mozilla::LazyLogModule gAudioChannelLog;
+
+#define AUDIO_CHANNEL_LOG(msg, ...)                                     \
+  MOZ_LOG(gAudioChannelLog, LogLevel::Debug, (msg, ##__VA_ARGS__))
+
 namespace mozilla {
 namespace dom {
 
 static uint8_t gWebAudioOutputKey;
 
 class OfflineDestinationNodeEngine final : public AudioNodeEngine
 {
 public:
@@ -198,52 +204,25 @@ private:
   uint32_t mWriteIndex;
   uint32_t mNumberOfChannels;
   // How many frames the OfflineAudioContext intends to produce.
   uint32_t mLength;
   float mSampleRate;
   bool mBufferAllocated;
 };
 
-class InputMutedRunnable final : public Runnable
-{
-public:
-  InputMutedRunnable(AudioNodeStream* aStream, bool aInputMuted)
-    : Runnable("dom::InputMutedRunnable")
-    , mStream(aStream)
-    , mInputMuted(aInputMuted)
-  {
-  }
-
-  NS_IMETHOD Run() override
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-    RefPtr<AudioNode> node = mStream->Engine()->NodeMainThread();
-
-    if (node) {
-      RefPtr<AudioDestinationNode> destinationNode =
-        static_cast<AudioDestinationNode*>(node.get());
-      destinationNode->InputMuted(mInputMuted);
-    }
-    return NS_OK;
-  }
-
-private:
-  RefPtr<AudioNodeStream> mStream;
-  bool mInputMuted;
-};
-
 class DestinationNodeEngine final : public AudioNodeEngine
 {
 public:
   explicit DestinationNodeEngine(AudioDestinationNode* aNode)
     : AudioNodeEngine(aNode)
     , mVolume(1.0f)
-    , mLastInputMuted(true)
+    , mLastInputAudible(false)
     , mSuspended(false)
+    , mSampleRate(CubebUtils::PreferredSampleRate())
   {
     MOZ_ASSERT(aNode);
   }
 
   void ProcessBlock(AudioNodeStream* aStream,
                     GraphTime aFrom,
                     const AudioBlock& aInput,
                     AudioBlock* aOutput,
@@ -251,24 +230,60 @@ public:
   {
     *aOutput = aInput;
     aOutput->mVolume *= mVolume;
 
     if (mSuspended) {
       return;
     }
 
-    bool newInputMuted = aInput.IsNull() || aInput.IsMuted();
-    if (newInputMuted != mLastInputMuted) {
-      mLastInputMuted = newInputMuted;
+    bool isInputAudible = !aInput.IsNull() &&
+                          !aInput.IsMuted() &&
+                          aInput.IsAudible();
 
-      RefPtr<InputMutedRunnable> runnable =
-        new InputMutedRunnable(aStream, newInputMuted);
+    auto shouldNotifyChanged = [&] () {
+      // We don't want to notify state changed frequently if the input stream is
+      // consist of interleaving audible and inaudible blocks. This situation is
+      // really common, especially when user is using OscillatorNode to produce
+      // sound. Sending unnessary runnable frequently would cause performance
+      // debasing. If the stream contains 10 interleaving samples and 5 of them
+      // are audible, others are inaudible, user would tend to feel the stream is
+      // audible. Therefore, we have the loose checking when stream is changing
+      // from inaudible to audible, but have strict checking when streaming is
+      // changing from audible to inaudible. If the inaudible blocks continue
+      // over a speicific time threshold, then we will treat the stream as inaudible.
+      if (isInputAudible && !mLastInputAudible) {
+        return true;
+      }
+      // Use more strict condition, choosing 1 seconds as a threshold.
+      if (!isInputAudible && mLastInputAudible &&
+          aFrom - mLastInputAudibleTime >= mSampleRate) {
+        return true;
+      }
+      return false;
+    };
+    if (shouldNotifyChanged()) {
+      mLastInputAudible = isInputAudible;
+      RefPtr<AudioNodeStream> stream = aStream;
+      auto r = [stream, isInputAudible] () -> void {
+        MOZ_ASSERT(NS_IsMainThread());
+        RefPtr<AudioNode> node = stream->Engine()->NodeMainThread();
+        if (node) {
+          RefPtr<AudioDestinationNode> destinationNode =
+            static_cast<AudioDestinationNode*>(node.get());
+          destinationNode->NotifyAudibleStateChanged(isInputAudible);
+        }
+      };
+
       aStream->Graph()->DispatchToMainThreadAfterStreamStateUpdate(
-        runnable.forget());
+        NS_NewRunnableFunction("dom::WebAudioAudibleStateChangedRunnable", r));
+    }
+
+    if (isInputAudible) {
+      mLastInputAudibleTime = aFrom;
     }
   }
 
   bool IsActive() const override
   {
     // Keep processing to track stream time, which is used for all timelines
     // associated with the same AudioContext.  If there are no other engines
     // for the AudioContext, then this could return false to suspend the
@@ -284,35 +299,37 @@ public:
     }
   }
 
   void SetInt32Parameter(uint32_t aIndex, int32_t aParam) override
   {
     if (aIndex == SUSPENDED) {
       mSuspended = !!aParam;
       if (mSuspended) {
-        mLastInputMuted = true;
+        mLastInputAudible = false;
       }
     }
   }
 
   enum Parameters {
     VOLUME,
     SUSPENDED,
   };
 
   size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override
   {
     return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
   }
 
 private:
   float mVolume;
-  bool mLastInputMuted;
+  bool mLastInputAudible;
+  GraphTime mLastInputAudibleTime = 0;
   bool mSuspended;
+  int mSampleRate;
 };
 
 NS_IMPL_CYCLE_COLLECTION_INHERITED(AudioDestinationNode, AudioNode,
                                    mAudioChannelAgent,
                                    mOfflineRenderingPromise)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AudioDestinationNode)
   NS_INTERFACE_MAP_ENTRY(nsIAudioChannelAgentCallback)
@@ -507,20 +524,19 @@ AudioDestinationNode::StartRendering(Pro
 
 NS_IMETHODIMP
 AudioDestinationNode::WindowVolumeChanged(float aVolume, bool aMuted)
 {
   if (!mStream) {
     return NS_OK;
   }
 
-  MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
-         ("AudioDestinationNode, WindowVolumeChanged, "
-          "this = %p, aVolume = %f, aMuted = %s\n",
-          this, aVolume, aMuted ? "true" : "false"));
+  AUDIO_CHANNEL_LOG("AudioDestinationNode %p WindowVolumeChanged, "
+                    "aVolume = %f, aMuted = %s\n",
+                    this, aVolume, aMuted ? "true" : "false");
 
   float volume = aMuted ? 0.0 : aVolume;
   mStream->SetAudioOutputVolume(&gWebAudioOutputKey, volume);
 
   AudioChannelService::AudibleState audible = volume > 0.0 ?
     AudioChannelService::AudibleState::eAudible :
     AudioChannelService::AudibleState::eNotAudible;
   if (mAudible != audible) {
@@ -538,19 +554,18 @@ AudioDestinationNode::WindowSuspendChang
     return NS_OK;
   }
 
   bool suspended = (aSuspend != nsISuspendedTypes::NONE_SUSPENDED);
   if (mAudioChannelSuspended == suspended) {
     return NS_OK;
   }
 
-  MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
-         ("AudioDestinationNode, WindowSuspendChanged, "
-          "this = %p, aSuspend = %s\n", this, SuspendTypeToStr(aSuspend)));
+  AUDIO_CHANNEL_LOG("AudioDestinationNode %p WindowSuspendChanged, "
+                    "aSuspend = %s\n", this, SuspendTypeToStr(aSuspend));
 
   mAudioChannelSuspended = suspended;
 
   DisabledTrackMode disabledMode = suspended ? DisabledTrackMode::SILENCE_BLACK
                                              : DisabledTrackMode::ENABLED;
   mStream->SetTrackEnabled(AudioNodeStream::AUDIO_TRACK, disabledMode);
 
   AudioChannelService::AudibleState audible =
@@ -606,36 +621,39 @@ AudioDestinationNode::CreateAudioChannel
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   return NS_OK;
 }
 
 void
-AudioDestinationNode::InputMuted(bool aMuted)
+AudioDestinationNode::NotifyAudibleStateChanged(bool aAudible)
 {
   MOZ_ASSERT(Context() && !Context()->IsOffline());
 
   if (!mAudioChannelAgent) {
-    if (aMuted) {
+    if (!aAudible) {
       return;
     }
     CreateAudioChannelAgent();
   }
 
-  if (aMuted) {
+  AUDIO_CHANNEL_LOG("AudioDestinationNode %p NotifyAudibleStateChanged, audible=%d",
+    this, aAudible);
+
+  if (!aAudible) {
     mAudioChannelAgent->NotifyStoppedPlaying();
     // Reset the state, and it would always be regard as audible.
     mAudible = AudioChannelService::AudibleState::eAudible;
     return;
   }
 
   if (mDurationBeforeFirstTimeAudible.IsZero()) {
-    MOZ_ASSERT(!aMuted);
+    MOZ_ASSERT(aAudible);
     mDurationBeforeFirstTimeAudible = TimeStamp::Now() - mCreatedTime;
     Telemetry::Accumulate(Telemetry::WEB_AUDIO_BECOMES_AUDIBLE_TIME,
                           mDurationBeforeFirstTimeAudible.ToSeconds());
   }
 
   AudioPlaybackConfig config;
   nsresult rv = mAudioChannelAgent->NotifyStartedPlaying(&config,
                                                          mAudible);
--- a/dom/media/webaudio/AudioDestinationNode.h
+++ b/dom/media/webaudio/AudioDestinationNode.h
@@ -70,17 +70,17 @@ public:
   const char* NodeType() const override
   {
     return "AudioDestinationNode";
   }
 
   size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override;
   size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override;
 
-  void InputMuted(bool aInputMuted);
+  void NotifyAudibleStateChanged(bool aAudible);
   void ResolvePromise(AudioBuffer* aRenderedBuffer);
 
   unsigned long Length()
   {
     MOZ_ASSERT(mIsOffline);
     return mFramesToProduce;
   }
 
--- a/gfx/layers/FrameMetrics.cpp
+++ b/gfx/layers/FrameMetrics.cpp
@@ -14,67 +14,75 @@ namespace layers {
 const FrameMetrics::ViewID FrameMetrics::NULL_SCROLL_ID = 0;
 
 void
 FrameMetrics::RecalculateViewportOffset()
 {
   if (!mIsRootContent) {
     return;
   }
-  CSSRect visualViewport = GetVisualViewport();
+  KeepLayoutViewportEnclosingVisualViewport(GetVisualViewport(), mViewport);
+}
+
+/* static */ void
+FrameMetrics::KeepLayoutViewportEnclosingVisualViewport(
+    const CSSRect& aVisualViewport,
+    CSSRect& aLayoutViewport)
+{
   // If the visual viewport is contained within the layout viewport, we don't
   // need to make any adjustments, so we can exit early.
   //
   // Additionally, if the composition bounds changes (due to an orientation
-  // change, window resize, etc.), it may take a few frames for mViewport to
+  // change, window resize, etc.), it may take a few frames for aLayoutViewport to
   // update and during that time, the visual viewport may be larger than the
   // layout viewport. In such situations, we take an early exit if the visual
   // viewport contains the layout viewport.
-  if (mViewport.Contains(visualViewport) || visualViewport.Contains(mViewport)) {
+  if (aLayoutViewport.Contains(aVisualViewport) || aVisualViewport.Contains(aLayoutViewport)) {
     return;
   }
 
   // If visual viewport size is greater than the layout viewport, move the layout
   // viewport such that it remains inside the visual viewport. Otherwise,
   // move the layout viewport such that the visual viewport is contained
   // inside the layout viewport.
-  if ((mViewport.Width() < visualViewport.Width() &&
-        !FuzzyEqualsMultiplicative(mViewport.Width(), visualViewport.Width())) ||
-       (mViewport.Height() < visualViewport.Height() &&
-        !FuzzyEqualsMultiplicative(mViewport.Height(), visualViewport.Height()))) {
+  if ((aLayoutViewport.Width() < aVisualViewport.Width() &&
+        !FuzzyEqualsMultiplicative(aLayoutViewport.Width(), aVisualViewport.Width())) ||
+       (aLayoutViewport.Height() < aVisualViewport.Height() &&
+        !FuzzyEqualsMultiplicative(aLayoutViewport.Height(), aVisualViewport.Height()))) {
 
-     if (mViewport.X() < visualViewport.X()) {
+     if (aLayoutViewport.X() < aVisualViewport.X()) {
         // layout viewport moves right
-        mViewport.MoveToX(visualViewport.X());
-     } else if (visualViewport.XMost() < mViewport.XMost()) {
+        aLayoutViewport.MoveToX(aVisualViewport.X());
+     } else if (aVisualViewport.XMost() < aLayoutViewport.XMost()) {
         // layout viewport moves left
-        mViewport.MoveByX(visualViewport.XMost() - mViewport.XMost());
+        aLayoutViewport.MoveByX(aVisualViewport.XMost() - aLayoutViewport.XMost());
      }
-     if (mViewport.Y() < visualViewport.Y()) {
+     if (aLayoutViewport.Y() < aVisualViewport.Y()) {
         // layout viewport moves down
-        mViewport.MoveToY(visualViewport.Y());
-     } else if (visualViewport.YMost() < mViewport.YMost()) {
+        aLayoutViewport.MoveToY(aVisualViewport.Y());
+     } else if (aVisualViewport.YMost() < aLayoutViewport.YMost()) {
         // layout viewport moves up
-        mViewport.MoveByY(visualViewport.YMost() - mViewport.YMost());
+        aLayoutViewport.MoveByY(aVisualViewport.YMost() - aLayoutViewport.YMost());
      }
    } else {
 
-     if (visualViewport.X() < mViewport.X()) {
-        mViewport.MoveToX(visualViewport.X());
-     } else if (mViewport.XMost() < visualViewport.XMost()) {
-        mViewport.MoveByX(visualViewport.XMost() - mViewport.XMost());
+     if (aVisualViewport.X() < aLayoutViewport.X()) {
+        aLayoutViewport.MoveToX(aVisualViewport.X());
+     } else if (aLayoutViewport.XMost() < aVisualViewport.XMost()) {
+        aLayoutViewport.MoveByX(aVisualViewport.XMost() - aLayoutViewport.XMost());
      }
-     if (visualViewport.Y() < mViewport.Y()) {
-        mViewport.MoveToY(visualViewport.Y());
-     } else if (mViewport.YMost() < visualViewport.YMost()) {
-        mViewport.MoveByY(visualViewport.YMost() - mViewport.YMost());
+     if (aVisualViewport.Y() < aLayoutViewport.Y()) {
+        aLayoutViewport.MoveToY(aVisualViewport.Y());
+     } else if (aLayoutViewport.YMost() < aVisualViewport.YMost()) {
+        aLayoutViewport.MoveByY(aVisualViewport.YMost() - aLayoutViewport.YMost());
      }
    }
 }
 
+
 void
 ScrollMetadata::SetUsesContainerScrolling(bool aValue) {
   mUsesContainerScrolling = aValue;
 }
 
 static OverscrollBehavior
 ToOverscrollBehavior(StyleOverscrollBehavior aBehavior)
 {
--- a/gfx/layers/FrameMetrics.h
+++ b/gfx/layers/FrameMetrics.h
@@ -533,16 +533,26 @@ public:
 
   // Determine if the visual viewport is outside of the layout viewport and
   // adjust the x,y-offset in mViewport accordingly. This is necessary to
   // allow APZ to async-scroll the layout viewport.
   //
   // This is a no-op if mIsRootContent is false.
   void RecalculateViewportOffset();
 
+  // Helper function for RecalculateViewportOffset(). Exposed so that
+  // APZC can perform the operation on other copies of the layout
+  // and visual viewport rects (e.g. the "effective" ones used to implement
+  // the frame delay).
+  // Modifies |aLayoutViewport| to continue enclosing |aVisualViewport|
+  // if possible.
+  static void KeepLayoutViewportEnclosingVisualViewport(
+      const CSSRect& aVisualViewport,
+      CSSRect& aLayoutViewport);
+
 private:
   // A unique ID assigned to each scrollable frame.
   ViewID mScrollId;
 
   // The pres-shell resolution that has been induced on the document containing
   // this scroll frame as a result of zooming this scroll frame (whether via
   // user action, or choosing an initial zoom level on page load). This can
   // only be different from 1.0 for frames that are zoomable, which currently
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -3343,16 +3343,24 @@ void AsyncPanZoomController::AdjustScrol
   CSSRect scrollRange = Metrics().CalculateScrollRange();
   // Apply shift to Metrics().mScrollOffset.
   SetScrollOffset(scrollRange.ClampPoint(
       Metrics().GetScrollOffset() + adjustment));
   // Apply shift to mCompositedScrollOffset, since the dynamic toolbar expects
   // the shift to take effect right away, without the usual frame delay.
   mCompositedScrollOffset = scrollRange.ClampPoint(
       mCompositedScrollOffset + adjustment);
+  // For a similar reason, apply the shift to mCompositedLayoutViewport.
+  // mCompositedLayoutViewport also needs to immediately pick up any new
+  // size from Metrics().GetViewport() to make sure it reflects any height
+  // change due to dynamic toolbar movement.
+  mCompositedLayoutViewport.SizeTo(Metrics().GetViewport().Size());
+  FrameMetrics::KeepLayoutViewportEnclosingVisualViewport(
+      CSSRect(mCompositedScrollOffset, Metrics().CalculateCompositedSizeInCssPixels()),
+      mCompositedLayoutViewport);
   RequestContentRepaint();
   UpdateSharedCompositorFrameMetrics();
 }
 
 void AsyncPanZoomController::SetScrollOffset(const CSSPoint& aOffset) {
   Metrics().SetScrollOffset(aOffset);
   Metrics().RecalculateViewportOffset();
 }
@@ -4230,20 +4238,29 @@ void AsyncPanZoomController::NotifyLayer
   bool smoothScrollRequested = aLayerMetrics.GetDoSmoothScroll()
        && (aLayerMetrics.GetScrollGeneration() != Metrics().GetScrollGeneration());
 
   // TODO if we're in a drag and scrollOffsetUpdated is set then we want to
   // ignore it
 
   bool needContentRepaint = false;
   bool viewportUpdated = false;
-  if (FuzzyEqualsAdditive(aLayerMetrics.GetCompositionBounds().Width(), Metrics().GetCompositionBounds().Width()) &&
-      FuzzyEqualsAdditive(aLayerMetrics.GetCompositionBounds().Height(), Metrics().GetCompositionBounds().Height())) {
-    // Remote content has sync'd up to the composition geometry
-    // change, so we can accept the viewport it's calculated.
+
+  // We usually don't entertain viewport updates on the same transaction as
+  // a composition bounds update, but we make an exception for Android
+  // to avoid the composition bounds and the viewport diverging during
+  // orientation changes and dynamic toolbar transitions.
+  // TODO: Do this on all platforms.
+  bool entertainViewportUpdates =
+       FuzzyEqualsAdditive(aLayerMetrics.GetCompositionBounds().Width(), Metrics().GetCompositionBounds().Width()) &&
+       FuzzyEqualsAdditive(aLayerMetrics.GetCompositionBounds().Height(), Metrics().GetCompositionBounds().Height());
+#if defined(MOZ_WIDGET_ANDROID)
+  entertainViewportUpdates = true;
+#endif
+  if (entertainViewportUpdates) {
     if (Metrics().GetViewport().Width() != aLayerMetrics.GetViewport().Width() ||
         Metrics().GetViewport().Height() != aLayerMetrics.GetViewport().Height()) {
       needContentRepaint = true;
       viewportUpdated = true;
     }
     if (viewportUpdated || scrollOffsetUpdated) {
       Metrics().SetViewport(aLayerMetrics.GetViewport());
     }
--- a/mobile/android/base/java/org/mozilla/gecko/notifications/NotificationClient.java
+++ b/mobile/android/base/java/org/mozilla/gecko/notifications/NotificationClient.java
@@ -151,17 +151,17 @@ public final class NotificationClient im
                 .setAutoCancel(true)
                 .setDefaults(Notification.DEFAULT_SOUND)
                 .setStyle(new NotificationCompat.BigTextStyle()
                         .bigText(alertText)
                         .setSummaryText(host));
 
         if (!AppConstants.Versions.preO) {
             builder.setChannelId(NotificationHelper.getInstance(mContext)
-                    .getNotificationChannel(NotificationHelper.Channel.SITE_NOTIFICATIONS).getId());
+                    .getNotificationChannel(NotificationHelper.Channel.DEFAULT).getId());
         }
 
         // Fetch icon.
         if (!imageUrl.isEmpty()) {
             final Bitmap image = BitmapUtils.decodeUrl(imageUrl);
             builder.setLargeIcon(image);
         }
 
--- a/mobile/android/base/java/org/mozilla/gecko/notifications/NotificationHelper.java
+++ b/mobile/android/base/java/org/mozilla/gecko/notifications/NotificationHelper.java
@@ -111,20 +111,16 @@ public final class NotificationHelper im
         /**
          * Synced tabs notification channel
          */
         SYNCED_TABS,
         /**
          * Leanplum notification channel - use only when <code>AppConstants.MOZ_ANDROID_MMA</code> is true.
          */
         LP_DEFAULT,
-        /**
-         * HTML5 web site notifications
-         */
-        SITE_NOTIFICATIONS,
     }
 
     // Holds the mapping between the Channel enum used by the rest of our codebase and the
     // channel ID used for communication with the system NotificationManager.
     // How to determine the initialCapacity: Count all channels (including the Updater, which is
     // only added further down in initNotificationChannels), multiply by 4/3 for a maximum load
     // factor of 75 % and round up to the next multiple of two.
     private final Map<Channel, String> mDefinedNotificationChannels = new HashMap<Channel, String>(16) {{
@@ -142,19 +138,16 @@ public final class NotificationHelper im
 
         if (AppConstants.MOZ_ANDROID_MMA) {
             final String LP_DEFAULT_CHANNEL_TAG = "lp-default-notification-channel";
             put(Channel.LP_DEFAULT, LP_DEFAULT_CHANNEL_TAG);
         }
 
         final String SYNCED_TABS_CHANNEL_TAG = "synced-tabs-notification-channel";
         put(Channel.SYNCED_TABS, SYNCED_TABS_CHANNEL_TAG);
-
-        final String SITE_NOTIFICATIONS_CHANNEL_TAG = "site-notifications";
-        put(Channel.SITE_NOTIFICATIONS, SITE_NOTIFICATIONS_CHANNEL_TAG);
     }};
 
     // These are channels we no longer require and want to retire from Android's settings UI.
     private final List<String> mDeprecatedNotificationChannels = new ArrayList<>(Arrays.asList(
             "default-notification-channel",
             null
     ));
 
@@ -263,22 +256,16 @@ public final class NotificationHelper im
 
                 case LP_DEFAULT: {
                     channel = new NotificationChannel(mDefinedNotificationChannels.get(definedChannel),
                             mContext.getString(R.string.leanplum_default_notifications_channel),
                             NotificationManager.IMPORTANCE_LOW);
                 }
                 break;
 
-                case SITE_NOTIFICATIONS: {
-                    channel = new NotificationChannel(mDefinedNotificationChannels.get(definedChannel),
-                            mContext.getString(R.string.site_notifications_channel),
-                            NotificationManager.IMPORTANCE_DEFAULT);
-                }
-
                 case DEFAULT:
                 default: {
                     channel = new NotificationChannel(mDefinedNotificationChannels.get(definedChannel),
                             mContext.getString(R.string.default_notification_channel),
                             NotificationManager.IMPORTANCE_LOW);
                 }
                 break;
             }
--- a/mobile/android/base/locales/en-US/android_strings.dtd
+++ b/mobile/android/base/locales/en-US/android_strings.dtd
@@ -896,11 +896,8 @@ Picture-in-picture mini window -->
 <!ENTITY default_notification_channel "&brandShortName;">
 <!ENTITY mls_notification_channel "&vendorShortName; Location Service">
 <!ENTITY download_notification_channel "Downloads">
 <!ENTITY media_notification_channel "Media playback">
 <!-- These push notifications come without a specific channel and/or name from Leanplum -->
 <!ENTITY leanplum_default_notifications_channel "&brandShortName; Push notifications">
 <!ENTITY updater_notification_channel "App updates">
 <!ENTITY synced_tabs_notification_channel "Synced tabs">
-<!-- LOCALIZATION NOTE (site_notifications_channel): This is for system notifications displayed by
-web sites through the HTML Notifications API. -->
-<!ENTITY site_notifications_channel "Site notifications">
--- a/mobile/android/base/strings.xml.in
+++ b/mobile/android/base/strings.xml.in
@@ -648,10 +648,9 @@
 
   <string name="default_notification_channel">&default_notification_channel;</string>
   <string name="mls_notification_channel">&mls_notification_channel;</string>
   <string name="media_notification_channel">&media_notification_channel;</string>
   <string name="download_notification_channel">&download_notification_channel;</string>
   <string name="leanplum_default_notifications_channel">&leanplum_default_notifications_channel;</string>
   <string name="updater_notification_channel">&updater_notification_channel;</string>
   <string name="synced_tabs_notification_channel">&synced_tabs_notification_channel;</string>
-  <string name="site_notifications_channel">&site_notifications_channel;</string>
 </resources>
--- a/toolkit/components/satchel/test/test_form_submission.html
+++ b/toolkit/components/satchel/test/test_form_submission.html
@@ -119,17 +119,17 @@
         input.type = "text";
         input.name = "test" + (i + 1);
         form.appendChild(input);
       }
     </script>
     <button type="submit">Submit</button>
   </form>
 
-  <!-- input with sensitive data (9 digit credit card number) -->
+  <!-- input with sensitive data (19 digit credit card number) -->
   <form id="form17" onsubmit="return checkSubmit(17)">
     <input type="text" name="test1">
     <button type="submit">Submit</button>
   </form>
 
   <!-- input with sensitive data (16 digit hyphenated credit card number) -->
   <form id="form18" onsubmit="return checkSubmit(18)">
     <input type="text" name="test1">
@@ -317,29 +317,29 @@ function startTest() {
   for (let i = 0; i != testData.length; i++) {
     $_(15, "test" + (i + 1)).value = testData[i];
   }
 
   testData = ccNumbers.valid15;
   for (let i = 0; i != testData.length; i++) {
     $_(16, "test" + (i + 1)).value = testData[i];
   }
-  $_(17, "test1").value = "001064088";
+  $_(17, "test1").value = "6799990100000000019";
   $_(18, "test1").value = "0000-0000-0080-4609";
   $_(19, "test1").value = "0000 0000 0222 331";
   $_(20, "test1").value = "dontSaveThis";
   $_(21, "test1").value = "dontSaveThis";
   $_(22, "searchbar-history").value = "dontSaveThis";
 
   $_(101, "test1").value = "savedValue";
   $_(102, "test2").value = "savedValue";
   $_(103, "test3").value = "savedValue";
   $_(104, "test4").value = " trimTrailingAndLeadingSpace ";
   $_(105, "test5").value = "\t trimTrailingAndLeadingWhitespace\t ";
-  $_(106, "test6").value = "00000000109181";
+  $_(106, "test6").value = "55555555555544445553"; // passes luhn but too long
 
   testData = ccNumbers.invalid16;
   for (let i = 0; i != testData.length; i++) {
     $_(107, "test7_" + (i + 1)).value = testData[i];
   }
 
   testData = ccNumbers.invalid15;
   for (let i = 0; i != testData.length; i++) {
@@ -407,17 +407,17 @@ function checkSubmit(formNum) {
       checkForSave("test4", "trimTrailingAndLeadingSpace",
                    "checking saved value is trimmed on both sides");
       break;
     case 105:
       checkForSave("test5", "trimTrailingAndLeadingWhitespace",
                    "checking saved value is trimmed on both sides");
       break;
     case 106:
-      checkForSave("test6", "00000000109181", "checking saved value");
+      checkForSave("test6", "55555555555544445553", "checking saved value");
       break;
     case 107:
       for (let i = 0; i != ccNumbers.invalid16.length; i++) {
         checkForSave("test7_" + (i + 1), ccNumbers.invalid16[i], "checking saved value");
       }
       break;
     case 108:
       for (let i = 0; i != ccNumbers.invalid15.length; i++) {
--- a/toolkit/content/tests/browser/browser.ini
+++ b/toolkit/content/tests/browser/browser.ini
@@ -134,8 +134,11 @@ support-files =
 skip-if = e10s # Bug ?????? - test directly manipulates content (gBrowser.contentDocument.getElementById("postForm").submit();)
 [browser_saveImageURL.js]
 uses-unsafe-cpows = true
 [browser_sound_indicator_silent_video.js]
 tags = audiochannel
 [browser_resume_bkg_video_on_tab_hover.js]
 [browser_webAudio_hideSoundPlayingIcon.js]
 tags = audiochannel
+[browser_webAudio_silentData.js]
+tags = audiochannel
+
new file mode 100644
--- /dev/null
+++ b/toolkit/content/tests/browser/browser_webAudio_silentData.js
@@ -0,0 +1,64 @@
+/**
+ * This test is used to make sure we won't show the sound indicator for silent
+ * web audio.
+ */
+ /* eslint-disable mozilla/no-arbitrary-setTimeout */
+"use strict";
+
+function createAudioContext() {
+  content.ac = new content.AudioContext();
+  const ac = content.ac;
+  const dest = ac.destination;
+  const source = ac.createBufferSource();
+  const buf = ac.createBuffer(1, 3 * ac.sampleRate, ac.sampleRate);
+  const bufData = buf.getChannelData(0);
+  for (let idx = 0; idx < buf.length; idx++) {
+    bufData[idx] = 0.0;
+  }
+  source.buffer = buf;
+  source.connect(dest);
+  source.start();
+}
+
+async function waitUntilAudioContextStarts() {
+  const ac = content.ac;
+  if (ac.state == "running") {
+   return;
+  }
+
+  await new Promise((resolve) => {
+    ac.onstatechange = () => {
+      if (ac.state == "running") {
+        ac.onstatechange = null;
+        resolve();
+      }
+    };
+  });
+}
+
+add_task(async function testSilentAudioContext() {
+  info(`- create new tab -`);
+  const tab = await BrowserTestUtils.openNewForegroundTab(window.gBrowser,
+                                                          "about:blank");
+  const browser = tab.linkedBrowser;
+
+  info(`- create audio context -`);
+  // We want the same audio context to be used across different content
+  // tasks, so it needs to be loaded by a frame script.
+  const mm = tab.linkedBrowser.messageManager;
+  mm.loadFrameScript("data:,(" + createAudioContext.toString() + ")();", false);
+
+  info(`- check AudioContext's state -`);
+  await ContentTask.spawn(browser, null, waitUntilAudioContextStarts);
+  ok(true, `AudioContext is running.`);
+
+  info(`- should not show sound indicator -`);
+  // If we do the next step too early, then we can't make sure whether that the
+  // reason of no showing sound indicator is because of silent web audio, or
+  // because the indicator is just not showing yet.
+  await new Promise(r => setTimeout(r, 1000));
+  await waitForTabPlayingEvent(tab, false);
+
+  info(`- remove tab -`);
+  await BrowserTestUtils.removeTab(tab);
+});
--- a/toolkit/modules/CreditCard.jsm
+++ b/toolkit/modules/CreditCard.jsm
@@ -103,44 +103,46 @@ class CreditCard {
   get number() {
     return this._number;
   }
 
   set number(value) {
     if (value) {
       let normalizedNumber = value.replace(/[-\s]/g, "");
       // Based on the information on wiki[1], the shortest valid length should be
-      // 9 digits (Canadian SIN).
-      // [1] https://en.wikipedia.org/wiki/Social_Insurance_Number
-      normalizedNumber = normalizedNumber.match(/^\d{9,}$/) ?
+      // 12 digits (Maestro).
+      // [1] https://en.wikipedia.org/wiki/Payment_card_number
+      normalizedNumber = normalizedNumber.match(/^\d{12,}$/) ?
         normalizedNumber : null;
       this._number = normalizedNumber;
     }
   }
 
   get network() {
     return this._network;
   }
 
   set network(value) {
     this._network = value || undefined;
   }
 
   // Implements the Luhn checksum algorithm as described at
   // http://wikipedia.org/wiki/Luhn_algorithm
+  // Number digit lengths vary with network, but should fall within 12-19 range. [2]
+  // More details at https://en.wikipedia.org/wiki/Payment_card_number
   isValidNumber() {
     if (!this._number) {
       return false;
     }
 
     // Remove dashes and whitespace
     let number = this._number.replace(/[\-\s]/g, "");
 
     let len = number.length;
-    if (len != 9 && len != 15 && len != 16) {
+    if (len < 12 || len > 19) {
       return false;
     }
 
     if (!/^\d+$/.test(number)) {
       return false;
     }
 
     let total = 0;
--- a/toolkit/modules/tests/xpcshell/test_CreditCard.js
+++ b/toolkit/modules/tests/xpcshell/test_CreditCard.js
@@ -10,30 +10,52 @@ add_task(function isValidNumber() {
     if (shouldPass) {
       ok(CreditCard.isValidNumber(number), `${number} should be considered valid`);
     } else {
       ok(!CreditCard.isValidNumber(number), `${number} should not be considered valid`);
     }
   }
 
   testValid("0000000000000000", true);
+
+  testValid("41111111112", false); // passes Luhn but too short
+  testValid("4111-1111-112", false); // passes Luhn but too short
+  testValid("55555555555544440018", false); // passes Luhn but too long
+  testValid("5555 5555 5555 4444 0018", false); // passes Luhn but too long
+
   testValid("4929001587121045", true);
   testValid("5103059495477870", true);
   testValid("6011029476355493", true);
   testValid("3589993783099582", true);
   testValid("5415425865751454", true);
-  if (CreditCard.isValidNumber("30190729470495")) {
-    ok(false, "todo: 14-digit numbers (Diners Club) aren't supported by isValidNumber yet");
-  }
-  if (CreditCard.isValidNumber("36333851788250")) {
-    ok(false, "todo: 14-digit numbers (Diners Club) aren't supported by isValidNumber yet");
-  }
-  if (CreditCard.isValidNumber("3532596776688495393")) {
-    ok(false, "todo: 19-digit numbers (JCB, Discover, Maestro) could have 16-19 digits");
-  }
+
+  testValid("378282246310005", true); // American Express test number
+  testValid("371449635398431", true); // American Express test number
+  testValid("378734493671000", true); // American Express Corporate test number
+  testValid("5610591081018250", true); // Australian BankCard test number
+  testValid("6759649826438453", true); // Maestro test number
+  testValid("6799990100000000019", true); // 19 digit Maestro test number
+  testValid("6799-9901-0000-0000019", true); // 19 digit Maestro test number
+  testValid("30569309025904", true); // 14 digit Diners Club test number
+  testValid("38520000023237", true); // 14 digit Diners Club test number
+  testValid("6011111111111117", true); // Discover test number
+  testValid("6011000990139424", true); // Discover test number
+  testValid("3530111333300000", true); // JCB test number
+  testValid("3566002020360505", true); // JCB test number
+  testValid("3532596776688495393", true); // 19-digit JCB number. JCB, Discover, Maestro could have 16-19 digits
+  testValid("3532 5967 7668 8495393", true); // 19-digit JCB number. JCB, Discover, Maestro could have 16-19 digits
+  testValid("5555555555554444", true); // MasterCard test number
+  testValid("5105105105105100", true); // MasterCard test number
+  testValid("2221000000000009", true); // 2-series MasterCard test number
+  testValid("4111111111111111", true); // Visa test number
+  testValid("4012888888881881", true); // Visa test number
+  testValid("4222222222222", true); // 13 digit Visa test number
+  testValid("4222 2222 22222", true); // 13 digit Visa test number
+  testValid("4035 5010 0000 0008", true); // Visadebit/Cartebancaire test number
+
   testValid("5038146897157463", true);
   testValid("4026313395502338", true);
   testValid("6387060366272981", true);
   testValid("474915027480942", true);
   testValid("924894781317325", true);
   testValid("714816113937185", true);
   testValid("790466087343106", true);
   testValid("474320195408363", true);
@@ -50,17 +72,18 @@ add_task(function isValidNumber() {
   testValid("4302068493801686", true);
   testValid("2721398408985465", true);
   testValid("6160334316984331", true);
   testValid("8643619970075142", true);
   testValid("0218246069710785", true);
   testValid("0000-0000-0080-4609", true);
   testValid("0000 0000 0222 331", true);
   testValid("344060747836806", true);
-  testValid("001064088", true);
+  testValid("001064088", false); // too short
+  testValid("00-10-64-088", false); // still too short
   testValid("4929001587121046", false);
   testValid("5103059495477876", false);
   testValid("6011029476355494", false);
   testValid("3589993783099581", false);
   testValid("5415425865751455", false);
   testValid("5038146897157462", false);
   testValid("4026313395502336", false);
   testValid("6387060366272980", false);
@@ -112,16 +135,17 @@ add_task(function test_maskNumber() {
   }
   testMask("0000000000000000", "**** 0000");
   testMask("4929001587121045", "**** 1045");
   testMask("5103059495477870", "**** 7870");
   testMask("6011029476355493", "**** 5493");
   testMask("3589993783099582", "**** 9582");
   testMask("5415425865751454", "**** 1454");
   testMask("344060747836806", "**** 6806");
+  testMask("6799990100000000019", "**** 0019");
   Assert.throws(() => (new CreditCard({number: "1234"})).maskedNumber,
     /Invalid credit card number/,
     "Four or less numbers should throw when retrieving the maskedNumber");
 });
 
 add_task(function test_longMaskedNumber() {
   function testMask(number, expected) {
     let card = new CreditCard({number});
@@ -130,16 +154,18 @@ add_task(function test_longMaskedNumber(
   }
   testMask("0000000000000000", "************0000");
   testMask("4929001587121045", "************1045");
   testMask("5103059495477870", "************7870");
   testMask("6011029476355493", "************5493");
   testMask("3589993783099582", "************9582");
   testMask("5415425865751454", "************1454");
   testMask("344060747836806", "***********6806");
+  testMask("6799990100000000019", "***************0019");
+
   Assert.throws(() => (new CreditCard({number: "1234"})).longMaskedNumber,
     /Invalid credit card number/,
     "Four or less numbers should throw when retrieving the maskedNumber");
 });
 
 add_task(function test_isValid() {
   function testValid(number, expirationMonth, expirationYear, shouldPass, message) {
     let card = new CreditCard({