Merge autoland to mozilla-central. a=merge
authorCsoregi Natalia <ncsoregi@mozilla.com>
Fri, 12 Oct 2018 13:13:46 +0300
changeset 499250 8bd12e6c3f9979d00afb2fc699bb292a03962f0a
parent 499237 580c03c8ae389ea12073561e2b445312257d7cf3 (current diff)
parent 499249 a37d277dfb6c70f6633366d3c6ba34ddd7aeb827 (diff)
child 499281 7a0840d602524d3b4552a867092267bb3fd5e79e
push id1864
push userffxbld-merge
push dateMon, 03 Dec 2018 15:51:40 +0000
treeherdermozilla-release@f040763d99ad [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone64.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 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({