From 80f8e996133b53d432d09510b95c3072ad8bf422 Mon Sep 17 00:00:00 2001 From: Julian Descottes Date: Thu, 25 Aug 2016 15:05:32 +0200 Subject: [PATCH 01/43] Bug 1298012 - update libraries paths in about:license for node-properties and sprintfjs;r=gerv MozReview-Commit-ID: 12paEMReI4i --HG-- extra : rebase_source : 069399f97d22498819c378156b8623946388f25c extra : intermediate-source : d0f2f6e140a0c02d142e6a460f87c661a9f802d2 extra : source : c344fba70d9511c2f665a5e375695e6e98cb4134 --- toolkit/content/license.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/toolkit/content/license.html b/toolkit/content/license.html index 1256c7603c4c..3a8f1a18d442 100644 --- a/toolkit/content/license.html +++ b/toolkit/content/license.html @@ -4182,7 +4182,7 @@ PERFORMANCE OF THIS SOFTWARE.

node-properties License

This license applies to - devtools/client/shared/vendor/node-properties.js.

+ devtools/shared/node-properties/node-properties.js.

 The MIT License (MIT)
@@ -4878,7 +4878,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     

sprintf.js License

This license applies to - devtools/client/shared/vendor/sprintf.js.

+ devtools/shared/sprintfjs/sprintf.js.

 Copyright (c) 2007-2016, Alexandru Marasteanu <hello [at) alexei (dot] ro>

From 3bc966a2e2c1e47c4ae68fb9225a0c8ef07c6c05 Mon Sep 17 00:00:00 2001
From: Edouard Oger 
Date: Sun, 28 Aug 2016 18:05:08 -0700
Subject: [PATCH 02/43] Bug 1298727 - Bump FxA device registration version on
 Fennec. r=eoger

MozReview-Commit-ID: 8NzADM5P7Zc

--HG--
extra : transplant_source : %CENG%8A%FA%8E%CD%D0%9CG%C0%11%10%3B0%D7%EE%89G%ED
---
 .../java/org/mozilla/gecko/fxa/FxAccountDeviceRegistrator.java  | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/mobile/android/services/src/main/java/org/mozilla/gecko/fxa/FxAccountDeviceRegistrator.java b/mobile/android/services/src/main/java/org/mozilla/gecko/fxa/FxAccountDeviceRegistrator.java
index 49ee74d1921c..86cd8f6f0602 100644
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/fxa/FxAccountDeviceRegistrator.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/fxa/FxAccountDeviceRegistrator.java
@@ -43,7 +43,7 @@ public class FxAccountDeviceRegistrator implements BundleEventListener {
 
   // The current version of the device registration, we use this to re-register
   // devices after we update what we send on device registration.
-  public static final Integer DEVICE_REGISTRATION_VERSION = 1;
+  public static final Integer DEVICE_REGISTRATION_VERSION = 2;
 
   private static FxAccountDeviceRegistrator instance;
   private final WeakReference context;

From 4eef729716d2f0220b4ff1a3ff776e5cd38f64bb Mon Sep 17 00:00:00 2001
From: Kevin Chan 
Date: Fri, 26 Aug 2016 09:59:00 -0400
Subject: [PATCH 03/43] Bug 1134307 - Access the title of the loaded page by
 using ctx.tab2Browser.contentTitle in browser_bug592641.js to avoid CPOW
 usage. r=mconley

---
 dom/html/test/browser_bug592641.js | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/dom/html/test/browser_bug592641.js b/dom/html/test/browser_bug592641.js
index 72cc4d231452..94e2e92c232d 100644
--- a/dom/html/test/browser_bug592641.js
+++ b/dom/html/test/browser_bug592641.js
@@ -30,7 +30,7 @@ function load1Soon() {
 
 function load1Done() {
   // Check the title
-  var title = ctx.tab1Browser.contentWindow.document.title;
+  var title = ctx.tab1Browser.contentTitle;
   checkTitle(title);
 
   // Try loading the same image in a new tab to make sure things work in
@@ -49,7 +49,7 @@ function load2Soon() {
 
 function load2Done() {
   // Check the title
-  var title = ctx.tab2Browser.contentWindow.document.title;
+  var title = ctx.tab2Browser.contentTitle;
   checkTitle(title);
 
   // Clean up

From e4c92071700a4d2596e7facf4de3bc08ffefbe7f Mon Sep 17 00:00:00 2001
From: "Fischer.json" 
Date: Mon, 1 Aug 2016 11:32:24 +0800
Subject: [PATCH 04/43] Bug 1113581 - Artifact when editing a keyword, the
 keyword is displayed under the text-area. r=jaws

MozReview-Commit-ID: 6toSvZlJeBz

--HG--
extra : transplant_source : %DC%82%29%EA%19%CD%B8%D3%CE%7D%90%FA%AA%89La%A5Rr%85
---
 toolkit/content/widgets/tree.xml | 13 ++++++++-----
 1 file changed, 8 insertions(+), 5 deletions(-)

diff --git a/toolkit/content/widgets/tree.xml b/toolkit/content/widgets/tree.xml
index 6b7428e03d8f..3c228e12d5e7 100644
--- a/toolkit/content/widgets/tree.xml
+++ b/toolkit/content/widgets/tree.xml
@@ -383,9 +383,13 @@
             }
             setTimeout(selectText, 0);
 
+            // Clear the text because we don't want the text appearing underneath the input.
+            this.view.setCellText(row, column, "");
+            // Save the original text so we can restore it after stoping editing.
+            input.setAttribute("data-original-text", input.value);
+
             this._editingRow = row;
             this._editingColumn = column;
-
             this.setAttribute("editing", "true");
             return true;
           ]]>
@@ -402,16 +406,15 @@
             var input = this.inputField;
             var editingRow = this._editingRow;
             var editingColumn = this._editingColumn;
+            var value = accept ? input.value : input.getAttribute("data-original-text");
             this._editingRow = -1;
             this._editingColumn = null;
-            if (accept) {
-              var value = input.value;
-              this.view.setCellText(editingRow, editingColumn, value);
-            }
+            this.view.setCellText(editingRow, editingColumn, value);
 
             input.hidden = true;
             input.value = "";
             this.removeAttribute("editing");
+            input.removeAttribute("data-original-text");
           ]]>
         
       

From 7428b5fd55ff9d3a36e975ccc60b9ed2c764b26d Mon Sep 17 00:00:00 2001
From: CuriousLearner 
Date: Fri, 26 Aug 2016 23:03:46 +0530
Subject: [PATCH 05/43] Bug 1271330 - Avoid homepage overrides in the default
 profile; r=davehunt,whimboo

MozReview-Commit-ID: 6vQ5C2x7pEB

--HG--
extra : rebase_source : 13a9cbc8cef2a1ca85a8f709452985dbddf92f7c
---
 testing/marionette/client/marionette_driver/geckoinstance.py | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/testing/marionette/client/marionette_driver/geckoinstance.py b/testing/marionette/client/marionette_driver/geckoinstance.py
index 7e8201ef90f7..f75a43e1a017 100644
--- a/testing/marionette/client/marionette_driver/geckoinstance.py
+++ b/testing/marionette/client/marionette_driver/geckoinstance.py
@@ -17,6 +17,8 @@ class GeckoInstance(object):
     required_prefs = {
         "browser.sessionstore.resume_from_crash": False,
         "browser.shell.checkDefaultBrowser": False,
+        # Needed for branded builds to prevent opening a second tab on startup
+        "browser.startup.homepage_override.mstone": "ignore",
         "browser.startup.page": 0,
         "browser.tabs.remote.autostart.1": False,
         "browser.tabs.remote.autostart.2": False,

From b4e537e8ebb27e56804fcb67250bb677759b487f Mon Sep 17 00:00:00 2001
From: Henrik Skupin 
Date: Wed, 24 Aug 2016 00:15:03 +0200
Subject: [PATCH 06/43] Bug 1296614 - Fix
 test_safe_browsing_initial_download.py for ASAN builds. r=francois

MozReview-Commit-ID: HzOLnXmeEWF

--HG--
extra : rebase_source : 840e17ce4a2740c61693a5479d7c23b3fc153383
---
 .../test_safe_browsing_initial_download.py    | 52 +++++++++----------
 1 file changed, 26 insertions(+), 26 deletions(-)

diff --git a/testing/firefox-ui/tests/functional/security/test_safe_browsing_initial_download.py b/testing/firefox-ui/tests/functional/security/test_safe_browsing_initial_download.py
index 385b8f398983..70deb1d27c1c 100644
--- a/testing/firefox-ui/tests/functional/security/test_safe_browsing_initial_download.py
+++ b/testing/firefox-ui/tests/functional/security/test_safe_browsing_initial_download.py
@@ -3,6 +3,7 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 import os
+import re
 
 from firefox_ui_harness.testcases import FirefoxTestCase
 from marionette_driver import Wait
@@ -11,32 +12,31 @@ from marionette_driver import Wait
 class TestSafeBrowsingInitialDownload(FirefoxTestCase):
 
     test_data = [{
-            'platforms': ['linux', 'windows_nt', 'darwin'],
-            'files': [
-                # Phishing
-                "goog-badbinurl-shavar.pset",
-                "goog-badbinurl-shavar.sbstore",
-                "goog-malware-shavar.pset",
-                "goog-malware-shavar.sbstore",
-                "goog-phish-shavar.pset",
-                "goog-phish-shavar.sbstore",
-                "goog-unwanted-shavar.pset",
-                "goog-unwanted-shavar.sbstore",
+        'platforms': ['linux', 'windows_nt', 'darwin'],
+        'files': [
+            # Phishing
+            r'goog-badbinurl-shavar.pset',
+            r'goog-badbinurl-shavar.sbstore',
+            r'goog-malware-shavar.pset',
+            r'goog-malware-shavar.sbstore',
+            r'goog(pub)?-phish-shavar.pset',
+            r'goog(pub)?-phish-shavar.sbstore',
+            r'goog-unwanted-shavar.pset',
+            r'goog-unwanted-shavar.sbstore',
 
-                # Tracking Protections
-                "base-track-digest256.pset",
-                "base-track-digest256.sbstore",
-                "mozstd-trackwhite-digest256.pset",
-                "mozstd-trackwhite-digest256.sbstore"
-                ]
-            },
-        {
-            'platforms': ['windows_nt'],
-            'files': [
-                "goog-downloadwhite-digest256.pset",
-                "goog-downloadwhite-digest256.sbstore"
-            ]
-        }
+            # Tracking Protections
+            r'base-track-digest256.pset',
+            r'base-track-digest256.sbstore',
+            r'mozstd-trackwhite-digest256.pset',
+            r'mozstd-trackwhite-digest256.sbstore'
+        ],
+    }, {
+        'platforms': ['windows_nt'],
+        'files': [
+            r'goog-downloadwhite-digest256.pset',
+            r'goog-downloadwhite-digest256.sbstore'
+        ]
+    },
     ]
 
     browser_prefs = {
@@ -72,5 +72,5 @@ class TestSafeBrowsingInitialDownload(FirefoxTestCase):
                 continue
             for item in data['files']:
                 wait.until(
-                    lambda _: os.path.exists(os.path.join(self.sb_files_path, item)),
+                    lambda _: [f for f in os.listdir(self.sb_files_path) if re.search(item, f)],
                     message='Safe Browsing File: {} not found!'.format(item))

From 0f39ba4b5897c45b0072a0cc98fa151e69441c28 Mon Sep 17 00:00:00 2001
From: Henrik Skupin 
Date: Mon, 22 Aug 2016 13:47:17 +0200
Subject: [PATCH 07/43] Bug 1296614 - Firefox-ui-functional tests should be
 also run for ASAN builds r=dustin

MozReview-Commit-ID: A4EILsuV5qH

--HG--
extra : rebase_source : afb96cbe712c2912f5f07801cc6c1c5bcda1f58b
---
 taskcluster/ci/desktop-test/test-sets.yml     |  2 ++
 .../configs/firefox_ui_tests/taskcluster.py   |  3 ---
 .../mozilla/testing/firefox_ui_tests.py       | 21 ++++++-------------
 3 files changed, 8 insertions(+), 18 deletions(-)

diff --git a/taskcluster/ci/desktop-test/test-sets.yml b/taskcluster/ci/desktop-test/test-sets.yml
index 6d377817793c..cbee666f7818 100644
--- a/taskcluster/ci/desktop-test/test-sets.yml
+++ b/taskcluster/ci/desktop-test/test-sets.yml
@@ -38,6 +38,8 @@ all-tests:
 asan-tests:
     - cppunit
     - crashtest
+    - firefox-ui-functional-local
+    - firefox-ui-functional-remote
     - gtest
     - jittests
     - jsreftest
diff --git a/testing/mozharness/configs/firefox_ui_tests/taskcluster.py b/testing/mozharness/configs/firefox_ui_tests/taskcluster.py
index dfacc3eac229..66fc72935758 100644
--- a/testing/mozharness/configs/firefox_ui_tests/taskcluster.py
+++ b/testing/mozharness/configs/firefox_ui_tests/taskcluster.py
@@ -7,8 +7,5 @@ config = {
 
     "pip_index": False,
 
-    "download_symbols": "ondemand",
-    "download_minidump_stackwalk": True,
-
     "tooltool_cache": "/builds/tooltool_cache",
 }
diff --git a/testing/mozharness/mozharness/mozilla/testing/firefox_ui_tests.py b/testing/mozharness/mozharness/mozilla/testing/firefox_ui_tests.py
index 85e7ec046988..f520aa9acf0b 100644
--- a/testing/mozharness/mozharness/mozilla/testing/firefox_ui_tests.py
+++ b/testing/mozharness/mozharness/mozilla/testing/firefox_ui_tests.py
@@ -22,6 +22,12 @@ from mozharness.mozilla.vcstools import VCSToolsScript
 
 # General command line arguments for Firefox ui tests
 firefox_ui_tests_config_options = [
+    [["--allow-software-gl-layers"], {
+        "action": "store_true",
+        "dest": "allow_software_gl_layers",
+        "default": False,
+        "help": "Permits a software GL implementation (such as LLVMPipe) to use the GL compositor.",
+    }],
     [['--dry-run'], {
         'dest': 'dry_run',
         'default': False,
@@ -33,15 +39,6 @@ firefox_ui_tests_config_options = [
         'default': False,
         'help': 'Enable multi-process (e10s) mode when running tests.',
     }],
-    [['--firefox-ui-branch'], {
-        'dest': 'firefox_ui_branch',
-        'help': 'which branch to use for firefox_ui_tests',
-    }],
-    [['--firefox-ui-repo'], {
-        'dest': 'firefox_ui_repo',
-        'default': 'https://github.com/mozilla/firefox-ui-tests.git',
-        'help': 'which firefox_ui_tests repo to use',
-    }],
     [['--symbols-path=SYMBOLS_PATH'], {
         'dest': 'symbols_path',
         'help': 'absolute path to directory containing breakpad '
@@ -51,12 +48,6 @@ firefox_ui_tests_config_options = [
         'dest': 'tag',
         'help': 'Subset of tests to run (local, remote).',
     }],
-    [["--allow-software-gl-layers"], {
-        "action": "store_true",
-        "dest": "allow_software_gl_layers",
-        "default": False,
-        "help": "Permits a software GL implementation (such as LLVMPipe) to use the GL compositor.",
-    }],
 ] + copy.deepcopy(testing_config_options)
 
 # Command line arguments for update tests

From 5b04109e85b33ccda738965f8847bb0106a5c402 Mon Sep 17 00:00:00 2001
From: Jean-Yves Avenard 
Date: Fri, 26 Aug 2016 16:39:36 +1200
Subject: [PATCH 08/43] Bug 1297037: [MSE] Update Duration Change algorithm as
 per latest spec. r=gerald

The MSE spec was recently updated to use the highest end time across tracks rather than across the buffered ranges.

See https://github.com/w3c/media-source/issues/124 and the fix described in https://github.com/w3c/media-source/pull/154

MozReview-Commit-ID: 4CqI8d2e9gu

--HG--
extra : rebase_source : b25f0e2a76c517c0dca0a9def00edd6eff38d8ad
---
 dom/media/mediasource/MediaSource.cpp         | 15 +++++------
 dom/media/mediasource/SourceBuffer.cpp        |  9 +++++++
 dom/media/mediasource/SourceBuffer.h          |  1 +
 dom/media/mediasource/SourceBufferList.cpp    | 12 +++++++++
 dom/media/mediasource/SourceBufferList.h      |  1 +
 dom/media/mediasource/TrackBuffersManager.cpp | 26 +++++++++++++++----
 dom/media/mediasource/TrackBuffersManager.h   |  1 +
 7 files changed, 51 insertions(+), 14 deletions(-)

diff --git a/dom/media/mediasource/MediaSource.cpp b/dom/media/mediasource/MediaSource.cpp
index 130fa05e7b67..7fd66b43d7ab 100644
--- a/dom/media/mediasource/MediaSource.cpp
+++ b/dom/media/mediasource/MediaSource.cpp
@@ -536,16 +536,13 @@ MediaSource::DurationChange(double aNewDuration, ErrorResult& aRv)
     return;
   }
 
-  // 3. Update duration to new duration.
-  // 4. If a user agent is unable to partially render audio frames or text cues
-  // that start before and end after the duration, then run the following steps:
-  // Update new duration to the highest end time reported by the buffered
-  // attribute across all SourceBuffer objects in sourceBuffers.
-  //   1. Update new duration to the highest end time reported by the buffered
-  //      attribute across all SourceBuffer objects in sourceBuffers.
-  //   2. Update duration to new duration.
+  // 3. Let highest end time be the largest track buffer ranges end time across
+  // all the track buffers across all SourceBuffer objects in sourceBuffers.
+  double highestEndTime = mSourceBuffers->HighestEndTime();
+  // 4. If new duration is less than highest end time, then
+  //    4.1 Update new duration to equal highest end time.
   aNewDuration =
-    std::max(aNewDuration, mSourceBuffers->GetHighestBufferedEndTime());
+    std::max(aNewDuration, highestEndTime);
 
   // 5. Update the media duration to new duration and run the HTMLMediaElement
   // duration change algorithm.
diff --git a/dom/media/mediasource/SourceBuffer.cpp b/dom/media/mediasource/SourceBuffer.cpp
index 6f32d9542e11..8ed787fe4064 100644
--- a/dom/media/mediasource/SourceBuffer.cpp
+++ b/dom/media/mediasource/SourceBuffer.cpp
@@ -554,6 +554,15 @@ SourceBuffer::HighestStartTime()
          : 0.0;
 }
 
+double
+SourceBuffer::HighestEndTime()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  return mTrackBuffersManager
+         ? mTrackBuffersManager->HighestEndTime().ToSeconds()
+         : 0.0;
+}
+
 NS_IMPL_CYCLE_COLLECTION_CLASS(SourceBuffer)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(SourceBuffer)
diff --git a/dom/media/mediasource/SourceBuffer.h b/dom/media/mediasource/SourceBuffer.h
index 18fafd470fd3..2567e53942ed 100644
--- a/dom/media/mediasource/SourceBuffer.h
+++ b/dom/media/mediasource/SourceBuffer.h
@@ -118,6 +118,7 @@ public:
   double GetBufferedStart();
   double GetBufferedEnd();
   double HighestStartTime();
+  double HighestEndTime();
 
   // Runs the range removal algorithm as defined by the MSE spec.
   void RangeRemoval(double aStart, double aEnd);
diff --git a/dom/media/mediasource/SourceBufferList.cpp b/dom/media/mediasource/SourceBufferList.cpp
index dbccad5a4588..cf7a7ed9db78 100644
--- a/dom/media/mediasource/SourceBufferList.cpp
+++ b/dom/media/mediasource/SourceBufferList.cpp
@@ -191,6 +191,18 @@ SourceBufferList::HighestStartTime()
   return highestStartTime;
 }
 
+double
+SourceBufferList::HighestEndTime()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  double highestEndTime = 0;
+  for (auto& sourceBuffer : mSourceBuffers) {
+    highestEndTime =
+      std::max(sourceBuffer->HighestEndTime(), highestEndTime);
+  }
+  return highestEndTime;
+}
+
 JSObject*
 SourceBufferList::WrapObject(JSContext* aCx, JS::Handle aGivenProto)
 {
diff --git a/dom/media/mediasource/SourceBufferList.h b/dom/media/mediasource/SourceBufferList.h
index 7c127f1c3783..03dc5c80bf70 100644
--- a/dom/media/mediasource/SourceBufferList.h
+++ b/dom/media/mediasource/SourceBufferList.h
@@ -86,6 +86,7 @@ public:
   void ClearSimple();
 
   double HighestStartTime();
+  double HighestEndTime();
 
 private:
   ~SourceBufferList();
diff --git a/dom/media/mediasource/TrackBuffersManager.cpp b/dom/media/mediasource/TrackBuffersManager.cpp
index fc48c73350cc..2e105bae30a4 100644
--- a/dom/media/mediasource/TrackBuffersManager.cpp
+++ b/dom/media/mediasource/TrackBuffersManager.cpp
@@ -300,11 +300,11 @@ TimeIntervals
 TrackBuffersManager::Buffered()
 {
   MSE_DEBUG("");
-  MonitorAutoLock mon(mMonitor);
   // http://w3c.github.io/media-source/index.html#widl-SourceBuffer-buffered
   // 2. Let highest end time be the largest track buffer ranges end time across all the track buffers managed by this SourceBuffer object.
-  TimeUnit highestEndTime;
+  TimeUnit highestEndTime = HighestEndTime();
 
+  MonitorAutoLock mon(mMonitor);
   nsTArray tracks;
   if (HasVideo()) {
     tracks.AppendElement(&mVideoBufferedRanges);
@@ -312,9 +312,6 @@ TrackBuffersManager::Buffered()
   if (HasAudio()) {
     tracks.AppendElement(&mAudioBufferedRanges);
   }
-  for (auto trackRanges : tracks) {
-    highestEndTime = std::max(trackRanges->GetEnd(), highestEndTime);
-  }
 
   // 3. Let intersection ranges equal a TimeRange object containing a single range from 0 to highest end time.
   TimeIntervals intersection{TimeInterval(TimeUnit::FromSeconds(0), highestEndTime)};
@@ -1917,6 +1914,25 @@ TrackBuffersManager::HighestStartTime()
   return highestStartTime;
 }
 
+TimeUnit
+TrackBuffersManager::HighestEndTime()
+{
+  MonitorAutoLock mon(mMonitor);
+  TimeUnit highestEndTime;
+
+  nsTArray tracks;
+  if (HasVideo()) {
+    tracks.AppendElement(&mVideoBufferedRanges);
+  }
+  if (HasAudio()) {
+    tracks.AppendElement(&mAudioBufferedRanges);
+  }
+  for (auto trackRanges : tracks) {
+    highestEndTime = std::max(trackRanges->GetEnd(), highestEndTime);
+  }
+  return highestEndTime;
+}
+
 const TrackBuffersManager::TrackBuffer&
 TrackBuffersManager::GetTrackBuffer(TrackInfo::TrackType aTrack)
 {
diff --git a/dom/media/mediasource/TrackBuffersManager.h b/dom/media/mediasource/TrackBuffersManager.h
index e1db49b3a64e..1ef8c6005c33 100644
--- a/dom/media/mediasource/TrackBuffersManager.h
+++ b/dom/media/mediasource/TrackBuffersManager.h
@@ -123,6 +123,7 @@ public:
   // Buffered must conform to http://w3c.github.io/media-source/index.html#widl-SourceBuffer-buffered
   media::TimeIntervals Buffered();
   media::TimeUnit HighestStartTime();
+  media::TimeUnit HighestEndTime();
 
   // Return the size of the data managed by this SourceBufferContentManager.
   int64_t GetSize() const;

From 954647ed96c47be88717ee1a6968a606532edded Mon Sep 17 00:00:00 2001
From: Jean-Yves Avenard 
Date: Fri, 26 Aug 2016 16:51:18 +1200
Subject: [PATCH 09/43] Bug 1297218: [MSE] Remove unnecessary call to Find().
 r=gerald

MozReview-Commit-ID: Af7mzXbLiNy

--HG--
extra : rebase_source : b3107a0be24a839be06ab5d4d22045a6fb0c94c3
---
 dom/media/mediasource/TrackBuffersManager.cpp | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/dom/media/mediasource/TrackBuffersManager.cpp b/dom/media/mediasource/TrackBuffersManager.cpp
index 2e105bae30a4..40f3c97953d7 100644
--- a/dom/media/mediasource/TrackBuffersManager.cpp
+++ b/dom/media/mediasource/TrackBuffersManager.cpp
@@ -1979,9 +1979,8 @@ TrackBuffersManager::Seek(TrackInfo::TrackType aTrack,
   if (aTime != TimeUnit()) {
     // Determine the interval of samples we're attempting to seek to.
     TimeIntervals buffered = trackBuffer.mBufferedRanges;
-    TimeIntervals::IndexType index = buffered.Find(aTime);
     buffered.SetFuzz(aFuzz);
-    index = buffered.Find(aTime);
+    TimeIntervals::IndexType index = buffered.Find(aTime);
     MOZ_ASSERT(index != TimeIntervals::NoIndex);
 
     TimeInterval target = buffered[index];

From e45036c61656b959f2c323443c723c96a0cf7dc3 Mon Sep 17 00:00:00 2001
From: Jean-Yves Avenard 
Date: Sat, 27 Aug 2016 20:24:31 +1000
Subject: [PATCH 10/43] Bug 1298606: P1. Add Intervals::ContainsStrict method.
 r=gerald

MozReview-Commit-ID: 7XA8xVUWSi7

--HG--
extra : rebase_source : e8123ed9a11063a5e0e83f4b31bb9cafec7f02b4
---
 dom/media/Intervals.h | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/dom/media/Intervals.h b/dom/media/Intervals.h
index f2c74aaa0433..7f6fe58cdb0d 100644
--- a/dom/media/Intervals.h
+++ b/dom/media/Intervals.h
@@ -581,6 +581,15 @@ public:
     return false;
   }
 
+  bool ContainsStrict(const ElemType& aInterval) const {
+    for (const auto& interval : mIntervals) {
+      if (interval.ContainsStrict(aInterval)) {
+        return true;
+      }
+    }
+    return false;
+  }
+
   bool Contains(const T& aX) const {
     for (const auto& interval : mIntervals) {
       if (interval.Contains(aX)) {

From 85862da5ee38d511fcadb6a68ad0c5b673d59c91 Mon Sep 17 00:00:00 2001
From: Jean-Yves Avenard 
Date: Sat, 27 Aug 2016 22:27:22 +1200
Subject: [PATCH 11/43] Bug 1298606: [MSE] P2. Properly determine next frame
 status in ended state. r=gerald

MozReview-Commit-ID: 2m05GzauHes

--HG--
extra : rebase_source : d5541dd96dc0480c41e7d68cfb96a95243b9a454
---
 dom/media/mediasource/MediaSourceDecoder.cpp | 28 +++++++++++++-------
 1 file changed, 18 insertions(+), 10 deletions(-)

diff --git a/dom/media/mediasource/MediaSourceDecoder.cpp b/dom/media/mediasource/MediaSourceDecoder.cpp
index 14216b1f3afb..8c29000dc7a1 100644
--- a/dom/media/mediasource/MediaSourceDecoder.cpp
+++ b/dom/media/mediasource/MediaSourceDecoder.cpp
@@ -277,10 +277,13 @@ MediaSourceDecoder::NextFrameBufferedStatus()
   // Next frame hasn't been decoded yet.
   // Use the buffered range to consider if we have the next frame available.
   TimeUnit currentPosition = TimeUnit::FromMicroseconds(CurrentPosition());
-  TimeInterval interval(currentPosition,
-                        currentPosition + media::TimeUnit::FromMicroseconds(DEFAULT_NEXT_FRAME_AVAILABLE_BUFFERED),
-                        MediaSourceDemuxer::EOS_FUZZ);
-  return GetBuffered().Contains(ClampIntervalToEnd(interval))
+  TimeIntervals buffered = GetBuffered();
+  buffered.SetFuzz(MediaSourceDemuxer::EOS_FUZZ / 2);
+  TimeInterval interval(
+    currentPosition,
+    currentPosition
+    + media::TimeUnit::FromMicroseconds(DEFAULT_NEXT_FRAME_AVAILABLE_BUFFERED));
+  return buffered.ContainsStrict(ClampIntervalToEnd(interval))
     ? MediaDecoderOwner::NEXT_FRAME_AVAILABLE
     : MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE;
 }
@@ -308,12 +311,12 @@ MediaSourceDecoder::CanPlayThrough()
   }
   // If we have data up to the mediasource's duration or 30s ahead, we can
   // assume that we can play without interruption.
+  TimeIntervals buffered = GetBuffered();
+  buffered.SetFuzz(MediaSourceDemuxer::EOS_FUZZ / 2);
   TimeUnit timeAhead =
     std::min(duration, currentPosition + TimeUnit::FromSeconds(30));
-  TimeInterval interval(currentPosition,
-                        timeAhead,
-                        MediaSourceDemuxer::EOS_FUZZ);
-  return GetBuffered().Contains(ClampIntervalToEnd(interval));
+  TimeInterval interval(currentPosition, timeAhead);
+  return buffered.ContainsStrict(ClampIntervalToEnd(interval));
 }
 
 void
@@ -336,8 +339,13 @@ MediaSourceDecoder::ClampIntervalToEnd(const TimeInterval& aInterval)
   if (!mEnded) {
     return aInterval;
   }
-  TimeInterval interval(TimeUnit(), TimeUnit::FromSeconds(GetDuration()));
-  return aInterval.Intersection(interval);
+  TimeUnit duration = TimeUnit::FromSeconds(GetDuration());
+  if (duration < aInterval.mStart) {
+    return aInterval;
+  }
+  return TimeInterval(aInterval.mStart,
+                      std::min(aInterval.mEnd, duration),
+                      aInterval.mFuzz);
 }
 
 #undef MSE_DEBUG

From fb042f363f8f36ca3d7a4f6ca1e08dc2f43e2cac Mon Sep 17 00:00:00 2001
From: Jean-Yves Avenard 
Date: Mon, 29 Aug 2016 16:59:17 +1000
Subject: [PATCH 12/43] Bug 1298606: P3. Fix coding style. r=gerald

MozReview-Commit-ID: L5MHnYOkyDV

--HG--
extra : rebase_source : 5da52435a47cc1dae14459ad44e910329c702ce4
---
 dom/media/Intervals.h | 47 +++++++++++++++++++++++++------------------
 1 file changed, 27 insertions(+), 20 deletions(-)

diff --git a/dom/media/Intervals.h b/dom/media/Intervals.h
index 7f6fe58cdb0d..4181d8954ab0 100644
--- a/dom/media/Intervals.h
+++ b/dom/media/Intervals.h
@@ -45,7 +45,7 @@ public:
     : mStart(T())
     , mEnd(T())
     , mFuzz(T())
-  {}
+  { }
 
   template
   Interval(StartArg&& aStart, EndArg&& aEnd)
@@ -69,7 +69,7 @@ public:
     : mStart(aOther.mStart)
     , mEnd(aOther.mEnd)
     , mFuzz(aOther.mFuzz)
-  {}
+  { }
 
   Interval(SelfType&& aOther)
     : mStart(Move(aOther.mStart))
@@ -136,8 +136,8 @@ public:
 
   bool Contains(const SelfType& aOther) const
   {
-    return (mStart - mFuzz <= aOther.mStart + aOther.mFuzz) &&
-      (aOther.mEnd - aOther.mFuzz <= mEnd + mFuzz);
+    return (mStart - mFuzz <= aOther.mStart + aOther.mFuzz)
+           && (aOther.mEnd - aOther.mFuzz <= mEnd + mFuzz);
   }
 
   bool ContainsStrict(const SelfType& aOther) const
@@ -147,14 +147,14 @@ public:
 
   bool ContainsWithStrictEnd(const SelfType& aOther) const
   {
-    return (mStart - mFuzz <= aOther.mStart + aOther.mFuzz) &&
-      aOther.mEnd <= mEnd;
+    return (mStart - mFuzz <= aOther.mStart + aOther.mFuzz)
+           && aOther.mEnd <= mEnd;
   }
 
   bool Intersects(const SelfType& aOther) const
   {
-    return (mStart - mFuzz < aOther.mEnd + aOther.mFuzz) &&
-      (aOther.mStart - aOther.mFuzz < mEnd + mFuzz);
+    return (mStart - mFuzz < aOther.mEnd + aOther.mFuzz)
+           && (aOther.mStart - aOther.mFuzz < mEnd + mFuzz);
   }
 
   bool IntersectsStrict(const SelfType& aOther) const
@@ -165,8 +165,8 @@ public:
   // Same as Intersects, but including the boundaries.
   bool Touches(const SelfType& aOther) const
   {
-    return (mStart - mFuzz <= aOther.mEnd + aOther.mFuzz) &&
-      (aOther.mStart - aOther.mFuzz <= mEnd + mFuzz);
+    return (mStart - mFuzz <= aOther.mEnd + aOther.mFuzz)
+           && (aOther.mStart - aOther.mFuzz <= mEnd + mFuzz);
   }
 
   // Returns true if aOther is strictly to the right of this and contiguous.
@@ -235,9 +235,9 @@ public:
   // of aOther
   bool TouchesOnRight(const SelfType& aOther) const
   {
-    return aOther.mStart <= mStart  &&
-      (mStart - mFuzz <= aOther.mEnd + aOther.mFuzz) &&
-      (aOther.mStart - aOther.mFuzz <= mEnd + mFuzz);
+    return aOther.mStart <= mStart
+           && (mStart - mFuzz <= aOther.mEnd + aOther.mFuzz)
+           && (aOther.mStart - aOther.mFuzz <= mEnd + mFuzz);
   }
 
   T mStart;
@@ -572,7 +572,8 @@ public:
     }
   }
 
-  bool Contains(const ElemType& aInterval) const {
+  bool Contains(const ElemType& aInterval) const
+  {
     for (const auto& interval : mIntervals) {
       if (interval.Contains(aInterval)) {
         return true;
@@ -581,7 +582,8 @@ public:
     return false;
   }
 
-  bool ContainsStrict(const ElemType& aInterval) const {
+  bool ContainsStrict(const ElemType& aInterval) const
+  {
     for (const auto& interval : mIntervals) {
       if (interval.ContainsStrict(aInterval)) {
         return true;
@@ -590,8 +592,10 @@ public:
     return false;
   }
 
-  bool Contains(const T& aX) const {
-    for (const auto& interval : mIntervals) {
+  bool Contains(const T& aX) const
+  {
+    for (const auto& interval : mIntervals)
+    {
       if (interval.Contains(aX)) {
         return true;
       }
@@ -599,7 +603,8 @@ public:
     return false;
   }
 
-  bool ContainsStrict(const T& aX) const {
+  bool ContainsStrict(const T& aX) const
+  {
     for (const auto& interval : mIntervals) {
       if (interval.ContainsStrict(aX)) {
         return true;
@@ -608,7 +613,8 @@ public:
     return false;
   }
 
-  bool ContainsWithStrictEnd(const T& aX) const {
+  bool ContainsWithStrictEnd(const T& aX) const
+  {
     for (const auto& interval : mIntervals) {
       if (interval.ContainsWithStrictEnd(aX)) {
         return true;
@@ -627,7 +633,8 @@ public:
     return *this;
   }
 
-  void SetFuzz(const T& aFuzz) {
+  void SetFuzz(const T& aFuzz)
+  {
     for (auto& interval : mIntervals) {
       interval.SetFuzz(aFuzz);
     }

From e4f3e62f41d9e9ea03311499688d50fe09a0705c Mon Sep 17 00:00:00 2001
From: Jean-Yves Avenard 
Date: Fri, 26 Aug 2016 23:10:00 +1200
Subject: [PATCH 13/43] Bug 1297036: [MSE] P1. Add mochitest to verify
 behavior. r=gerald

Seeking in the buffered range should always succeed, even if we have no data in a given track

MozReview-Commit-ID: FGjsDCNIdWC

--HG--
extra : rebase_source : c91348e44055f82deee0e97da4abef0cf799b225
---
 dom/media/mediasource/test/mochitest.ini      |  2 +
 .../mediasource/test/test_SeekToEnd_mp4.html  | 57 +++++++++++++++++++
 2 files changed, 59 insertions(+)
 create mode 100644 dom/media/mediasource/test/test_SeekToEnd_mp4.html

diff --git a/dom/media/mediasource/test/mochitest.ini b/dom/media/mediasource/test/mochitest.ini
index 8ad891dc3425..9c228860ebef 100644
--- a/dom/media/mediasource/test/mochitest.ini
+++ b/dom/media/mediasource/test/mochitest.ini
@@ -104,6 +104,8 @@ skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not
 skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not supported on xp and android 2.3
 [test_SeekedEvent_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not supported on xp and android 2.3
+[test_SeekToEnd_mp4.html]
+skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not supported on xp and android 2.3
 [test_SeekTwice_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not supported on xp and android 2.3
 [test_Sequence_mp4.html]
diff --git a/dom/media/mediasource/test/test_SeekToEnd_mp4.html b/dom/media/mediasource/test/test_SeekToEnd_mp4.html
new file mode 100644
index 000000000000..2ffa53898711
--- /dev/null
+++ b/dom/media/mediasource/test/test_SeekToEnd_mp4.html
@@ -0,0 +1,57 @@
+
+
+
+  MSE: seeking to end of data with data gap.
+  
+  
+  
+
+
+
+
+
+ + From 03f6dc4d618e7ab3b6f1b4ad3ecdd8b37b32fd67 Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Fri, 26 Aug 2016 19:23:29 +1200 Subject: [PATCH 14/43] Bug 1297036: [MSE] P2. Make seek always succeed when attempting to seek past the end time. r=gerald MozReview-Commit-ID: H2YJu7vY0aP --HG-- extra : rebase_source : 73b1e324a6d9f8612ef03dcbf7dc5e5a91c7e2b2 --- dom/media/mediasource/MediaSourceDemuxer.cpp | 9 ++++++++- dom/media/mediasource/TrackBuffersManager.cpp | 11 +++++++++-- dom/media/mediasource/TrackBuffersManager.h | 1 + 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/dom/media/mediasource/MediaSourceDemuxer.cpp b/dom/media/mediasource/MediaSourceDemuxer.cpp index 0d7ece63067b..0c181bedc648 100644 --- a/dom/media/mediasource/MediaSourceDemuxer.cpp +++ b/dom/media/mediasource/MediaSourceDemuxer.cpp @@ -385,6 +385,13 @@ MediaSourceTrackDemuxer::DoSeek(media::TimeUnit aTime) buffered.SetFuzz(MediaSourceDemuxer::EOS_FUZZ / 2); TimeUnit seekTime = std::max(aTime - mPreRoll, TimeUnit::FromMicroseconds(0)); + if (mManager->IsEnded() && seekTime >= buffered.GetEnd()) { + // We're attempting to seek past the end time. Cap seekTime so that we seek + // to the last sample instead. + seekTime = + std::max(mManager->HighestStartTime(mType) - mPreRoll, + TimeUnit::FromMicroseconds(0)); + } if (!buffered.Contains(seekTime)) { if (!buffered.Contains(aTime)) { // We don't have the data to seek to. @@ -392,7 +399,7 @@ MediaSourceTrackDemuxer::DoSeek(media::TimeUnit aTime) mManager->IsEnded() ? DemuxerFailureReason::END_OF_STREAM : DemuxerFailureReason::WAITING_FOR_DATA, __func__); } - // Theorically we should reject the promise with WAITING_FOR_DATA, + // Theoretically we should reject the promise with WAITING_FOR_DATA, // however, to avoid unwanted regressions we assume that if at this time // we don't have the wanted data it won't come later. // Instead of using the pre-rolled time, use the earliest time available in diff --git a/dom/media/mediasource/TrackBuffersManager.cpp b/dom/media/mediasource/TrackBuffersManager.cpp index 40f3c97953d7..fae5575087c4 100644 --- a/dom/media/mediasource/TrackBuffersManager.cpp +++ b/dom/media/mediasource/TrackBuffersManager.cpp @@ -1893,6 +1893,13 @@ TrackBuffersManager::Buffered(TrackInfo::TrackType aTrack) return GetTracksData(aTrack).mBufferedRanges; } +const media::TimeUnit& +TrackBuffersManager::HighestStartTime(TrackInfo::TrackType aTrack) +{ + MOZ_ASSERT(OnTaskQueue()); + return GetTracksData(aTrack).mHighestStartTimestamp; +} + TimeIntervals TrackBuffersManager::SafeBuffered(TrackInfo::TrackType aTrack) const { @@ -1981,8 +1988,8 @@ TrackBuffersManager::Seek(TrackInfo::TrackType aTrack, TimeIntervals buffered = trackBuffer.mBufferedRanges; buffered.SetFuzz(aFuzz); TimeIntervals::IndexType index = buffered.Find(aTime); - MOZ_ASSERT(index != TimeIntervals::NoIndex); - + MOZ_ASSERT(index != TimeIntervals::NoIndex, + "We shouldn't be called if aTime isn't buffered"); TimeInterval target = buffered[index]; i = FindSampleIndex(track, target); } diff --git a/dom/media/mediasource/TrackBuffersManager.h b/dom/media/mediasource/TrackBuffersManager.h index 1ef8c6005c33..089ee8d6bd68 100644 --- a/dom/media/mediasource/TrackBuffersManager.h +++ b/dom/media/mediasource/TrackBuffersManager.h @@ -140,6 +140,7 @@ public: MediaInfo GetMetadata(); const TrackBuffer& GetTrackBuffer(TrackInfo::TrackType aTrack); const media::TimeIntervals& Buffered(TrackInfo::TrackType); + const media::TimeUnit& HighestStartTime(TrackInfo::TrackType); media::TimeIntervals SafeBuffered(TrackInfo::TrackType) const; bool IsEnded() const { From 90f7e263a715bd788aae1bb732879ededf42d550 Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Fri, 26 Aug 2016 19:30:50 +1200 Subject: [PATCH 15/43] Bug 1297036: P3. Revert "Bug 1293646: [MSE] P2. Only reject a seek request with EOS if it's passed the explicit duration." r=gerald This reverts commit 5a949eb358e27 Another more complete solution will follow. MozReview-Commit-ID: K3lTdrBxW7W --HG-- extra : rebase_source : d0f9443193b0816ae85972a4a368599b872fd437 --- dom/media/AbstractMediaDecoder.h | 1 - dom/media/MediaDecoder.h | 6 +++--- dom/media/MediaFormatReader.cpp | 29 ----------------------------- dom/media/MediaFormatReader.h | 6 +----- 4 files changed, 4 insertions(+), 38 deletions(-) diff --git a/dom/media/AbstractMediaDecoder.h b/dom/media/AbstractMediaDecoder.h index a8af770055e8..80fc2cbe67d9 100644 --- a/dom/media/AbstractMediaDecoder.h +++ b/dom/media/AbstractMediaDecoder.h @@ -61,7 +61,6 @@ public: virtual void NotifyDecodedFrames(const FrameStatisticsData& aStats) = 0; virtual AbstractCanonical* CanonicalDurationOrNull() { return nullptr; }; - virtual AbstractCanonical>* CanonicalExplicitDuration() { return nullptr; } // Return an event that will be notified when data arrives in MediaResource. // MediaDecoderReader will register with this event to receive notifications diff --git a/dom/media/MediaDecoder.h b/dom/media/MediaDecoder.h index 29de34aa222c..31439dac304d 100644 --- a/dom/media/MediaDecoder.h +++ b/dom/media/MediaDecoder.h @@ -826,9 +826,6 @@ protected: public: AbstractCanonical* CanonicalDurationOrNull() override; - AbstractCanonical>* CanonicalExplicitDuration() override { - return &mExplicitDuration; - } AbstractCanonical* CanonicalVolume() { return &mVolume; } @@ -841,6 +838,9 @@ public: AbstractCanonical* CanonicalEstimatedDuration() { return &mEstimatedDuration; } + AbstractCanonical>* CanonicalExplicitDuration() { + return &mExplicitDuration; + } AbstractCanonical* CanonicalPlayState() { return &mPlayState; } diff --git a/dom/media/MediaFormatReader.cpp b/dom/media/MediaFormatReader.cpp index 8d6026b22eb6..f1a19ee8fbdc 100644 --- a/dom/media/MediaFormatReader.cpp +++ b/dom/media/MediaFormatReader.cpp @@ -77,7 +77,6 @@ MediaFormatReader::MediaFormatReader(AbstractMediaDecoder* aDecoder, , mDemuxOnly(false) , mSeekScheduled(false) , mVideoFrameContainer(aVideoFrameContainer) - , mExplicitDuration(mTaskQueue, Maybe(), "MediaFormatReader::mExplicitDuration(Mirror)") { MOZ_ASSERT(aDemuxer); MOZ_COUNT_CTOR(MediaFormatReader); @@ -140,8 +139,6 @@ MediaFormatReader::Shutdown() mPlatform = nullptr; mVideoFrameContainer = nullptr; - mExplicitDuration.DisconnectIfConnected(); - return MediaDecoderReader::Shutdown(); } @@ -254,10 +251,6 @@ MediaFormatReader::AsyncReadMetadata() return MetadataPromise::CreateAndResolve(metadata, __func__); } - if (mDecoder->CanonicalExplicitDuration()) { - mExplicitDuration.Connect(mDecoder->CanonicalExplicitDuration()); - } - RefPtr p = mMetadataPromise.Ensure(__func__); mDemuxerInitRequest.Begin(mDemuxer->Init() @@ -1083,20 +1076,6 @@ MediaFormatReader::InternalSeek(TrackType aTrack, const InternalSeekTarget& aTar [self, aTrack] (DemuxerFailureReason aResult) { auto& decoder = self->GetDecoderData(aTrack); decoder.mSeekRequest.Complete(); - - if (aResult == DemuxerFailureReason::END_OF_STREAM) { - // We want to enter EOS when performing an - // internal seek only if we're attempting to seek past - // the explicit duration to avoid unwanted ended - // event to be fired. - if (self->mExplicitDuration.Ref().isSome() && - decoder.mTimeThreshold.ref().Time() < - TimeUnit::FromSeconds( - self->mExplicitDuration.Ref().ref())) { - aResult = DemuxerFailureReason::WAITING_FOR_DATA; - } - } - switch (aResult) { case DemuxerFailureReason::WAITING_FOR_DATA: self->NotifyWaitingForData(aTrack); @@ -1754,14 +1733,6 @@ MediaFormatReader::OnSeekFailed(TrackType aTrack, DemuxerFailureReason aResult) mAudio.mSeekRequest.Complete(); } - // We want to enter EOS when performing a seek only if we're attempting to - // seek past the explicit duration to avoid unwanted ended - // event to be fired. - if (mExplicitDuration.Ref().isSome() && - mPendingSeekTime.ref() < TimeUnit::FromSeconds(mExplicitDuration.Ref().ref())) { - aResult = DemuxerFailureReason::WAITING_FOR_DATA; - } - if (aResult == DemuxerFailureReason::WAITING_FOR_DATA) { if (HasVideo() && aTrack == TrackType::kAudioTrack && mFallbackSeekTime.isSome() && diff --git a/dom/media/MediaFormatReader.h b/dom/media/MediaFormatReader.h index 86782071e496..d223d1ef112a 100644 --- a/dom/media/MediaFormatReader.h +++ b/dom/media/MediaFormatReader.h @@ -9,9 +9,8 @@ #include "mozilla/Atomics.h" #include "mozilla/Maybe.h" -#include "mozilla/Monitor.h" -#include "mozilla/StateMirroring.h" #include "mozilla/TaskQueue.h" +#include "mozilla/Monitor.h" #include "MediaDataDemuxer.h" #include "MediaDecoderReader.h" @@ -585,9 +584,6 @@ private: RefPtr mCrashHelper; void SetBlankDecode(TrackType aTrack, bool aIsBlankDecode); - - // The duration explicitly set by JS, mirrored from the main thread. - Mirror> mExplicitDuration; }; } // namespace mozilla From 8d676d83a6029245a0f7a3c2acd57e7b9c812844 Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Sun, 28 Aug 2016 01:00:39 +1200 Subject: [PATCH 16/43] Bug 1297036: [MSE] P4. Only report end of stream when reaching the end. r=gerald MozReview-Commit-ID: 5EWhBVnscXY --HG-- extra : rebase_source : 914aa1b8abb8d4026993281dbd68b1ba2ef04642 --- dom/media/mediasource/MediaSourceDemuxer.cpp | 35 +++++++++++-------- dom/media/mediasource/TrackBuffersManager.cpp | 24 ++++++++++--- dom/media/mediasource/TrackBuffersManager.h | 11 +++++- 3 files changed, 51 insertions(+), 19 deletions(-) diff --git a/dom/media/mediasource/MediaSourceDemuxer.cpp b/dom/media/mediasource/MediaSourceDemuxer.cpp index 0c181bedc648..2d8754a0f325 100644 --- a/dom/media/mediasource/MediaSourceDemuxer.cpp +++ b/dom/media/mediasource/MediaSourceDemuxer.cpp @@ -379,6 +379,8 @@ MediaSourceTrackDemuxer::BreakCycles() RefPtr MediaSourceTrackDemuxer::DoSeek(media::TimeUnit aTime) { + typedef TrackBuffersManager::GetSampleResult Result; + TimeIntervals buffered = mManager->Buffered(mType); // Fuzz factor represents a +/- threshold. So when seeking it allows the gap // to be twice as big as the fuzz value. We only want to allow EOS_FUZZ gap. @@ -395,9 +397,8 @@ MediaSourceTrackDemuxer::DoSeek(media::TimeUnit aTime) if (!buffered.Contains(seekTime)) { if (!buffered.Contains(aTime)) { // We don't have the data to seek to. - return SeekPromise::CreateAndReject( - mManager->IsEnded() ? DemuxerFailureReason::END_OF_STREAM : - DemuxerFailureReason::WAITING_FOR_DATA, __func__); + return SeekPromise::CreateAndReject(DemuxerFailureReason::WAITING_FOR_DATA, + __func__); } // Theoretically we should reject the promise with WAITING_FOR_DATA, // however, to avoid unwanted regressions we assume that if at this time @@ -409,12 +410,12 @@ MediaSourceTrackDemuxer::DoSeek(media::TimeUnit aTime) seekTime = buffered[index].mStart; } seekTime = mManager->Seek(mType, seekTime, MediaSourceDemuxer::EOS_FUZZ / 2); - bool error; + Result result; RefPtr sample = mManager->GetSample(mType, media::TimeUnit(), - error); - MOZ_ASSERT(!error && sample); + result); + MOZ_ASSERT(result != Result::ERROR && sample); mNextSample = Some(sample); mReset = false; { @@ -428,33 +429,39 @@ MediaSourceTrackDemuxer::DoSeek(media::TimeUnit aTime) RefPtr MediaSourceTrackDemuxer::DoGetSamples(int32_t aNumSamples) { + typedef TrackBuffersManager::GetSampleResult Result; + if (mReset) { // If a seek (or reset) was recently performed, we ensure that the data // we are about to retrieve is still available. TimeIntervals buffered = mManager->Buffered(mType); buffered.SetFuzz(MediaSourceDemuxer::EOS_FUZZ); + if (!buffered.Length() && mManager->IsEnded()) { + return SamplesPromise::CreateAndReject(DemuxerFailureReason::END_OF_STREAM, + __func__); + } if (!buffered.Contains(TimeUnit::FromMicroseconds(0))) { - return SamplesPromise::CreateAndReject( - mManager->IsEnded() ? DemuxerFailureReason::END_OF_STREAM : - DemuxerFailureReason::WAITING_FOR_DATA, __func__); + return SamplesPromise::CreateAndReject(DemuxerFailureReason::WAITING_FOR_DATA, + __func__); } mReset = false; } - bool error = false; RefPtr sample; if (mNextSample) { sample = mNextSample.ref(); mNextSample.reset(); } else { - sample = mManager->GetSample(mType, MediaSourceDemuxer::EOS_FUZZ, error); + Result result; + sample = mManager->GetSample(mType, MediaSourceDemuxer::EOS_FUZZ, result); if (!sample) { - if (error) { + if (result == Result::ERROR) { return SamplesPromise::CreateAndReject(DemuxerFailureReason::DEMUXER_ERROR, __func__); } return SamplesPromise::CreateAndReject( - mManager->IsEnded() ? DemuxerFailureReason::END_OF_STREAM : - DemuxerFailureReason::WAITING_FOR_DATA, __func__); + (result == Result::EOS && mManager->IsEnded()) + ? DemuxerFailureReason::END_OF_STREAM + : DemuxerFailureReason::WAITING_FOR_DATA, __func__); } } RefPtr samples = new SamplesHolder; diff --git a/dom/media/mediasource/TrackBuffersManager.cpp b/dom/media/mediasource/TrackBuffersManager.cpp index fae5575087c4..3395f58d749c 100644 --- a/dom/media/mediasource/TrackBuffersManager.cpp +++ b/dom/media/mediasource/TrackBuffersManager.cpp @@ -2138,17 +2138,19 @@ TrackBuffersManager::GetSample(TrackInfo::TrackType aTrack, already_AddRefed TrackBuffersManager::GetSample(TrackInfo::TrackType aTrack, const TimeUnit& aFuzz, - bool& aError) + GetSampleResult& aResult) { MOZ_ASSERT(OnTaskQueue()); auto& trackData = GetTracksData(aTrack); const TrackBuffer& track = GetTrackBuffer(aTrack); - aError = false; + aResult = GetSampleResult::WAITING_FOR_DATA; if (!track.Length()) { + aResult = GetSampleResult::EOS; return nullptr; } + if (trackData.mNextGetSampleIndex.isNothing() && trackData.mNextSampleTimecode == TimeUnit()) { // First demux, get first sample. @@ -2156,6 +2158,10 @@ TrackBuffersManager::GetSample(TrackInfo::TrackType aTrack, } if (trackData.mNextGetSampleIndex.isSome()) { + if (trackData.mNextGetSampleIndex.ref() >= track.Length()) { + aResult = GetSampleResult::EOS; + return nullptr; + } const MediaRawData* sample = GetSample(aTrack, trackData.mNextGetSampleIndex.ref(), @@ -2168,7 +2174,7 @@ TrackBuffersManager::GetSample(TrackInfo::TrackType aTrack, RefPtr p = sample->Clone(); if (!p) { - aError = true; + aResult = GetSampleResult::ERROR; return nullptr; } trackData.mNextGetSampleIndex.ref()++; @@ -2177,9 +2183,18 @@ TrackBuffersManager::GetSample(TrackInfo::TrackType aTrack, TimeUnit::FromMicroseconds(sample->mTimecode + sample->mDuration); trackData.mNextSampleTime = TimeUnit::FromMicroseconds(sample->GetEndTime()); + aResult = GetSampleResult::NO_ERROR; return p.forget(); } + if (trackData.mNextSampleTimecode.ToMicroseconds() > + track.LastElement()->mTimecode + track.LastElement()->mDuration) { + // The next element is past our last sample. We're done. + trackData.mNextGetSampleIndex = Some(uint32_t(track.Length())); + aResult = GetSampleResult::EOS; + return nullptr; + } + // Our previous index has been overwritten, attempt to find the new one. int32_t pos = FindCurrentPosition(aTrack, aFuzz); if (pos < 0) { @@ -2193,7 +2208,7 @@ TrackBuffersManager::GetSample(TrackInfo::TrackType aTrack, RefPtr p = sample->Clone(); if (!p) { // OOM - aError = true; + aResult = GetSampleResult::ERROR; return nullptr; } trackData.mNextGetSampleIndex = Some(uint32_t(pos)+1); @@ -2201,6 +2216,7 @@ TrackBuffersManager::GetSample(TrackInfo::TrackType aTrack, TimeUnit::FromMicroseconds(sample->mTimecode + sample->mDuration); trackData.mNextSampleTime = TimeUnit::FromMicroseconds(sample->GetEndTime()); + aResult = GetSampleResult::NO_ERROR; return p.forget(); } diff --git a/dom/media/mediasource/TrackBuffersManager.h b/dom/media/mediasource/TrackBuffersManager.h index 089ee8d6bd68..9f0f086e8eb2 100644 --- a/dom/media/mediasource/TrackBuffersManager.h +++ b/dom/media/mediasource/TrackBuffersManager.h @@ -153,9 +153,18 @@ public: const media::TimeUnit& aTimeThreadshold, const media::TimeUnit& aFuzz, bool& aFound); + + enum class GetSampleResult + { + NO_ERROR, + ERROR, + WAITING_FOR_DATA, + EOS + }; + already_AddRefed GetSample(TrackInfo::TrackType aTrack, const media::TimeUnit& aFuzz, - bool& aError); + GetSampleResult& aResult); int32_t FindCurrentPosition(TrackInfo::TrackType aTrack, const media::TimeUnit& aFuzz); media::TimeUnit GetNextRandomAccessPoint(TrackInfo::TrackType aTrack, From 46842a940a609a6f5a918d54ae607856f771cd64 Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Sat, 27 Aug 2016 23:51:40 +1000 Subject: [PATCH 17/43] Bug 1297036: [MSE] P5. Make fuzz research consistent. r=gerald The aim is to only allow skipping gaps of fuzz=500ms. MozReview-Commit-ID: 8uHxni2nPHI --HG-- extra : rebase_source : ccef593170fb3a0c3ff61dc97ad1a0508be06934 --- dom/media/mediasource/MediaSourceDemuxer.cpp | 14 +++++++------- dom/media/mediasource/TrackBuffersManager.cpp | 5 ++++- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/dom/media/mediasource/MediaSourceDemuxer.cpp b/dom/media/mediasource/MediaSourceDemuxer.cpp index 2d8754a0f325..241a9aaa854b 100644 --- a/dom/media/mediasource/MediaSourceDemuxer.cpp +++ b/dom/media/mediasource/MediaSourceDemuxer.cpp @@ -394,8 +394,8 @@ MediaSourceTrackDemuxer::DoSeek(media::TimeUnit aTime) std::max(mManager->HighestStartTime(mType) - mPreRoll, TimeUnit::FromMicroseconds(0)); } - if (!buffered.Contains(seekTime)) { - if (!buffered.Contains(aTime)) { + if (!buffered.ContainsWithStrictEnd(seekTime)) { + if (!buffered.ContainsWithStrictEnd(aTime)) { // We don't have the data to seek to. return SeekPromise::CreateAndReject(DemuxerFailureReason::WAITING_FOR_DATA, __func__); @@ -409,7 +409,7 @@ MediaSourceTrackDemuxer::DoSeek(media::TimeUnit aTime) MOZ_ASSERT(index != TimeIntervals::NoIndex); seekTime = buffered[index].mStart; } - seekTime = mManager->Seek(mType, seekTime, MediaSourceDemuxer::EOS_FUZZ / 2); + seekTime = mManager->Seek(mType, seekTime, MediaSourceDemuxer::EOS_FUZZ); Result result; RefPtr sample = mManager->GetSample(mType, @@ -435,13 +435,13 @@ MediaSourceTrackDemuxer::DoGetSamples(int32_t aNumSamples) // If a seek (or reset) was recently performed, we ensure that the data // we are about to retrieve is still available. TimeIntervals buffered = mManager->Buffered(mType); - buffered.SetFuzz(MediaSourceDemuxer::EOS_FUZZ); + buffered.SetFuzz(MediaSourceDemuxer::EOS_FUZZ / 2); if (!buffered.Length() && mManager->IsEnded()) { return SamplesPromise::CreateAndReject(DemuxerFailureReason::END_OF_STREAM, __func__); } - if (!buffered.Contains(TimeUnit::FromMicroseconds(0))) { + if (!buffered.ContainsWithStrictEnd(TimeUnit::FromMicroseconds(0))) { return SamplesPromise::CreateAndReject(DemuxerFailureReason::WAITING_FOR_DATA, __func__); } @@ -480,8 +480,8 @@ MediaSourceTrackDemuxer::DoSkipToNextRandomAccessPoint(media::TimeUnit aTimeThre uint32_t parsed = 0; // Ensure that the data we are about to skip to is still available. TimeIntervals buffered = mManager->Buffered(mType); - buffered.SetFuzz(MediaSourceDemuxer::EOS_FUZZ); - if (buffered.Contains(aTimeThreadshold)) { + buffered.SetFuzz(MediaSourceDemuxer::EOS_FUZZ / 2); + if (buffered.ContainsWithStrictEnd(aTimeThreadshold)) { bool found; parsed = mManager->SkipToNextRandomAccessPoint(mType, aTimeThreadshold, diff --git a/dom/media/mediasource/TrackBuffersManager.cpp b/dom/media/mediasource/TrackBuffersManager.cpp index 3395f58d749c..90a2b96dee94 100644 --- a/dom/media/mediasource/TrackBuffersManager.cpp +++ b/dom/media/mediasource/TrackBuffersManager.cpp @@ -1986,11 +1986,14 @@ TrackBuffersManager::Seek(TrackInfo::TrackType aTrack, if (aTime != TimeUnit()) { // Determine the interval of samples we're attempting to seek to. TimeIntervals buffered = trackBuffer.mBufferedRanges; - buffered.SetFuzz(aFuzz); + // Fuzz factor is +/- aFuzz; as we want to only eliminate gaps + // that are less than aFuzz wide, we set a fuzz factor aFuzz/2. + buffered.SetFuzz(aFuzz / 2); TimeIntervals::IndexType index = buffered.Find(aTime); MOZ_ASSERT(index != TimeIntervals::NoIndex, "We shouldn't be called if aTime isn't buffered"); TimeInterval target = buffered[index]; + target.mFuzz = aFuzz; i = FindSampleIndex(track, target); } From 3b8c2264439276a6593c071fe9a52aedad21fa67 Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Sun, 28 Aug 2016 12:26:40 +1000 Subject: [PATCH 18/43] Bug 1297036: [MSE] P6. Fix invalid mochitest. r=gerald The test accidentally worked because any demuxing failures in ended mode would be treated as EOS. There's no audio between [0-3), so playback couldn't start MozReview-Commit-ID: 4B90CrVUTy4 --HG-- extra : rebase_source : 9079aa8a6983877745baac71c7868ca360b85095 --- dom/media/mediasource/test/test_TimestampOffset_mp4.html | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/dom/media/mediasource/test/test_TimestampOffset_mp4.html b/dom/media/mediasource/test/test_TimestampOffset_mp4.html index 04ddb6ea1ebf..2a80e3e5cc26 100644 --- a/dom/media/mediasource/test/test_TimestampOffset_mp4.html +++ b/dom/media/mediasource/test/test_TimestampOffset_mp4.html @@ -71,9 +71,11 @@ runWithMSE(function(ms, el) { return Promise.all([audiosb.updating ? once(audiosb, 'updateend') : Promise.resolve(), videosb.updating ? once(videosb, 'updateend') : Promise.resolve()]); }).then(function() { - ms.endOfStream(); - once(el, 'ended').then(SimpleTest.finish.bind(SimpleTest)); + info("waiting for play to complete"); el.play(); + el.currentTime = el.buffered.start(0); + ms.endOfStream(); + Promise.all([once(el, 'ended'), once(el, 'seeked')]).then(SimpleTest.finish.bind(SimpleTest)); }); }); }); From a9dab88295f7661e0281d9959c623346834d4b54 Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Sat, 27 Aug 2016 00:08:38 +1200 Subject: [PATCH 19/43] Bug 1298594: [MSE] P1. Add mochitest to verify correct behavior. r=gerald 1- We shouldn't reach ended if we have a gap in the buffered range prior the end of the file (see bug 1297036) 2- Waiting should be fired when readyState goes below HAVE_FUTURE_DATA MozReview-Commit-ID: 18bEnkNpYvO --HG-- extra : rebase_source : c42c7fd19fec9ede5bb64ea697a0086116882403 --- dom/media/mediasource/test/mochitest.ini | 2 + .../test_WaitingOnMissingDataEnded_mp4.html | 53 +++++++++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 dom/media/mediasource/test/test_WaitingOnMissingDataEnded_mp4.html diff --git a/dom/media/mediasource/test/mochitest.ini b/dom/media/mediasource/test/mochitest.ini index 9c228860ebef..8c6290ef4afd 100644 --- a/dom/media/mediasource/test/mochitest.ini +++ b/dom/media/mediasource/test/mochitest.ini @@ -128,6 +128,8 @@ skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not skip-if = true # Disabled due to bug 1124493 and friends. WebM MSE is deprioritized. [test_WaitingOnMissingData_mp4.html] skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not supported on xp and android 2.3 +[test_WaitingOnMissingDataEnded_mp4.html] +skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not supported on xp and android 2.3 [test_WaitingToEndedTransition_mp4.html] skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not supported on xp and android 2.3 diff --git a/dom/media/mediasource/test/test_WaitingOnMissingDataEnded_mp4.html b/dom/media/mediasource/test/test_WaitingOnMissingDataEnded_mp4.html new file mode 100644 index 000000000000..6e1560d011db --- /dev/null +++ b/dom/media/mediasource/test/test_WaitingOnMissingDataEnded_mp4.html @@ -0,0 +1,53 @@ + + + + MSE: |waiting| event when source data is missing + + + + + +

+
+ + From 816b36a83229829d7418efa393882a14038f4564 Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Sat, 27 Aug 2016 22:23:52 +1000 Subject: [PATCH 20/43] Bug 1298594: P2. Fire waiting event when readyState move back to HAVE_CURRENT_DATA. r=jwwang MozReview-Commit-ID: BpwYY6njXGC --HG-- extra : rebase_source : 6dfe1998341ccf9fea69d8974f08aaa54385628d --- dom/html/HTMLMediaElement.cpp | 25 +++++++++++++++---------- dom/html/HTMLMediaElement.h | 4 ---- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/dom/html/HTMLMediaElement.cpp b/dom/html/HTMLMediaElement.cpp index b0e1c427bb44..075c801e572a 100644 --- a/dom/html/HTMLMediaElement.cpp +++ b/dom/html/HTMLMediaElement.cpp @@ -2882,7 +2882,6 @@ HTMLMediaElement::HTMLMediaElement(already_AddRefed& aNo mPlayingThroughTheAudioChannelBeforeSeek(false), mPausedForInactiveDocumentOrChannel(false), mEventDeliveryPaused(false), - mWaitingFired(false), mIsRunningLoadMethod(false), mIsDoingExplicitLoad(false), mIsLoadingFromSourceChildren(false), @@ -4775,11 +4774,6 @@ HTMLMediaElement::UpdateReadyStateInternal() if (mFirstFrameLoaded) { ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA); } - if (!mWaitingFired && nextFrameStatus == MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE_BUFFERING) { - FireTimeUpdate(false); - DispatchAsyncEvent(NS_LITERAL_STRING("waiting")); - mWaitingFired = true; - } return; } @@ -4838,9 +4832,24 @@ void HTMLMediaElement::ChangeReadyState(nsMediaReadyState aState) UpdateAudioChannelPlayingState(); // Handle raising of "waiting" event during seek (see 4.8.10.9) + // or + // 4.8.12.7 Ready states: + // "If the previous ready state was HAVE_FUTURE_DATA or more, and the new + // ready state is HAVE_CURRENT_DATA or less + // If the media element was potentially playing before its readyState + // attribute changed to a value lower than HAVE_FUTURE_DATA, and the element + // has not ended playback, and playback has not stopped due to errors, + // paused for user interaction, or paused for in-band content, the user agent + // must queue a task to fire a simple event named timeupdate at the element, + // and queue a task to fire a simple event named waiting at the element." if (mPlayingBeforeSeek && mReadyState < nsIDOMHTMLMediaElement::HAVE_FUTURE_DATA) { DispatchAsyncEvent(NS_LITERAL_STRING("waiting")); + } else if (oldState >= nsIDOMHTMLMediaElement::HAVE_FUTURE_DATA && + mReadyState < nsIDOMHTMLMediaElement::HAVE_FUTURE_DATA && + !Paused() && !Ended() && !mError) { + FireTimeUpdate(false); + DispatchAsyncEvent(NS_LITERAL_STRING("waiting")); } if (oldState < nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA && @@ -4850,10 +4859,6 @@ void HTMLMediaElement::ChangeReadyState(nsMediaReadyState aState) mLoadedDataFired = true; } - if (mReadyState == nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA) { - mWaitingFired = false; - } - if (oldState < nsIDOMHTMLMediaElement::HAVE_FUTURE_DATA && mReadyState >= nsIDOMHTMLMediaElement::HAVE_FUTURE_DATA) { DispatchAsyncEvent(NS_LITERAL_STRING("canplay")); diff --git a/dom/html/HTMLMediaElement.h b/dom/html/HTMLMediaElement.h index 842240251785..c86dcc4ebeff 100644 --- a/dom/html/HTMLMediaElement.h +++ b/dom/html/HTMLMediaElement.h @@ -1516,10 +1516,6 @@ protected: // True iff event delivery is suspended (mPausedForInactiveDocumentOrChannel must also be true). bool mEventDeliveryPaused; - // True if we've reported a "waiting" event since the last - // readyState change to HAVE_CURRENT_DATA. - bool mWaitingFired; - // True if we're running the "load()" method. bool mIsRunningLoadMethod; From 9e433b8a8397a4c662077af8b57a1b0b7ffb5a74 Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Sun, 28 Aug 2016 21:20:52 +1000 Subject: [PATCH 21/43] Bug 1298594: P3. Ensure currentTime is updated prior changing readyState. r=jwwang Otherwise we get intermittent in mochitests checking the value of currenTime when events are fired MozReview-Commit-ID: AVktWrXochp --HG-- extra : rebase_source : 76c952e62e9b449834d5924454c25ddad305ada6 --- dom/media/MediaDecoderStateMachine.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/dom/media/MediaDecoderStateMachine.cpp b/dom/media/MediaDecoderStateMachine.cpp index 4a9d46c39eb9..214ccb3d351e 100644 --- a/dom/media/MediaDecoderStateMachine.cpp +++ b/dom/media/MediaDecoderStateMachine.cpp @@ -2542,6 +2542,12 @@ void MediaDecoderStateMachine::UpdateNextFrameStatus() if (status != mNextFrameStatus) { DECODER_LOG("Changed mNextFrameStatus to %s", statusString); + if(status == MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE_BUFFERING || + status == MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE) { + // Ensure currentTime is up to date prior updating mNextFrameStatus so that + // the MediaDecoderOwner fire events at correct currentTime. + UpdatePlaybackPositionPeriodically(); + } } mNextFrameStatus = status; From b3537bb63d279e471b840603a271f793aa7843dc Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Mon, 29 Aug 2016 15:46:56 +1000 Subject: [PATCH 22/43] Bug 1298594: P4. Pop the frame when current time is past the end of the current frame. r=jwwang Most frames start when the previous one stop. MozReview-Commit-ID: H92Bmiki49Q --HG-- extra : rebase_source : 071416d9151d5188550d73f0eb5a4c70a5fbd48b --- dom/media/mediasink/VideoSink.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dom/media/mediasink/VideoSink.cpp b/dom/media/mediasink/VideoSink.cpp index cf137ea1c13e..fed225b802ca 100644 --- a/dom/media/mediasink/VideoSink.cpp +++ b/dom/media/mediasink/VideoSink.cpp @@ -399,7 +399,7 @@ VideoSink::UpdateRenderedVideoFrames() // Skip frames up to the playback position. int64_t lastDisplayedFrameEndTime = 0; while (VideoQueue().GetSize() > mMinVideoQueueSize && - clockTime > VideoQueue().PeekFront()->GetEndTime()) { + clockTime >= VideoQueue().PeekFront()->GetEndTime()) { RefPtr frame = VideoQueue().PopFront(); if (frame->As()->mSentToCompositor) { lastDisplayedFrameEndTime = frame->GetEndTime(); From d9412b2ac096ecbe33cfc12690710eed8d4f13c6 Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Mon, 29 Aug 2016 16:07:38 +1000 Subject: [PATCH 23/43] Bug 1298594: P5. Fix mochitest. r=gerald The assumption was that the waiting event would be fired once the last frame prior the gap had been played. This is however incorrect, as per spec, the waiting event is to be fired once readyState is <= HAVE_CURRENT_DATA. So the waiting event is actually fired anytime between the start of the last frame and its end. MozReview-Commit-ID: AA4Qhn7okhB --HG-- extra : rebase_source : fc1f336b2e07cc2549071563804de8fae9c9ab67 --- .../mediasource/test/test_Threshold_mp4.html | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/dom/media/mediasource/test/test_Threshold_mp4.html b/dom/media/mediasource/test/test_Threshold_mp4.html index b508f8f1763a..a33dc08eceb0 100644 --- a/dom/media/mediasource/test/test_Threshold_mp4.html +++ b/dom/media/mediasource/test/test_Threshold_mp4.html @@ -15,13 +15,12 @@ SimpleTest.waitForExplicitFinish(); runWithMSE(function(ms, el) { var threshold = 0.5; // gap threshold in seconds. -// duration of a frame. The FFmpeg decoder can't calculate properly calculate the duration of the last frame. -var fuzz = 33322 / 1000000; +var fuzz = 0.000001; // fuzz when comparing double. once(ms, 'sourceopen').then(function() { ok(true, "Receive a sourceopen event"); var videosb = ms.addSourceBuffer("video/mp4"); - var vchunks = [ {start: 0, end: 3.2033}, { start: 3.2033, end: 6.4066}]; + var vchunks = [ {start: 0, end: 3.203333}, { start: 3.203333, end: 6.406666}]; fetchAndLoad(videosb, 'bipbop/bipbop_video', ['init'], '.mp4') .then(fetchAndLoad.bind(null, videosb, 'bipbop/bipbop_video', range(1, 5), '.m4s')) @@ -40,8 +39,10 @@ var fuzz = 33322 / 1000000; }).then(function() { return once(el, 'waiting'); }).then(function() { - // We're waiting for data at the end of the last segment. - isfuzzy(el.currentTime, vchunks[1].end + threshold, fuzz, "skipped the gap properly"); + // We're waiting for data after the start of the last frame. + // 0.033333 is the duration of the last frame. + ok(el.currentTime >= vchunks[1].end - 0.033333 + threshold - fuzz + && el.currentTime <= vchunks[1].end + threshold + fuzz, "skipped the gap properly: " + el.currentTime + " " + (vchunks[1].end + threshold)); is(el.buffered.length, 2, "buffered range has right length"); // Now we test that seeking will succeed despite the gap. el.currentTime = el.buffered.end(0) + (threshold / 2); @@ -68,8 +69,10 @@ var fuzz = 33322 / 1000000; }).then(function() { return once(el, 'waiting'); }).then(function() { - // We're waiting for data at the end of the first segment as the gap is too big. - isfuzzy(el.currentTime, vchunks[0].end, fuzz, "stopped at the gap properly"); + // We're waiting for data after the start of the last frame. + // 0.033333 is the duration of the last frame. + ok(el.currentTime >= vchunks[0].end - 0.033333 - fuzz + && el.currentTime <= vchunks[0].end + fuzz, "stopped at the gap properly: " + el.currentTime + " " + vchunks[0].end); SimpleTest.finish(); }); }); From d97b525caaf93067fd42be30459ec4ab9ed291d0 Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Sun, 28 Aug 2016 02:34:44 +1200 Subject: [PATCH 24/43] Bug 1298617: [MSE] P1. Don't attempt to estimate next sample time if exact value known. r=gerald MozReview-Commit-ID: 8DE9WHFsePt --HG-- extra : rebase_source : dd9edb407d6f2cc8ba76367fca0e10fb91f2eea7 --- dom/media/mediasource/TrackBuffersManager.cpp | 29 +++++++++++++++---- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/dom/media/mediasource/TrackBuffersManager.cpp b/dom/media/mediasource/TrackBuffersManager.cpp index 90a2b96dee94..10fbff94c60d 100644 --- a/dom/media/mediasource/TrackBuffersManager.cpp +++ b/dom/media/mediasource/TrackBuffersManager.cpp @@ -2086,8 +2086,10 @@ TrackBuffersManager::SkipToNextRandomAccessPoint(TrackInfo::TrackType aTrack, // SkipToNextRandomAccessPoint will not count again the parsed sample as // skipped. if (aFound) { - trackData.mNextSampleTimecode = nextSampleTimecode; - trackData.mNextSampleTime = nextSampleTime; + trackData.mNextSampleTimecode = + TimeUnit::FromMicroseconds(track[i]->mTimecode); + trackData.mNextSampleTime = + TimeUnit::FromMicroseconds(track[i]->mTime); trackData.mNextGetSampleIndex = Some(i); } else if (i > 0) { // Go back to the previous keyframe or the original position so the next @@ -2181,11 +2183,28 @@ TrackBuffersManager::GetSample(TrackInfo::TrackType aTrack, return nullptr; } trackData.mNextGetSampleIndex.ref()++; - // Estimate decode timestamp of the next sample. - trackData.mNextSampleTimecode = + // Estimate decode timestamp and timestamp of the next sample. + TimeUnit nextSampleTimecode = TimeUnit::FromMicroseconds(sample->mTimecode + sample->mDuration); - trackData.mNextSampleTime = + TimeUnit nextSampleTime = TimeUnit::FromMicroseconds(sample->GetEndTime()); + const MediaRawData* nextSample = + GetSample(aTrack, + trackData.mNextGetSampleIndex.ref(), + nextSampleTimecode, + nextSampleTime, + aFuzz); + if (nextSample) { + // We have a valid next sample, can use exact values. + trackData.mNextSampleTimecode = + TimeUnit::FromMicroseconds(nextSample->mTimecode); + trackData.mNextSampleTime = + TimeUnit::FromMicroseconds(nextSample->mTime); + } else { + // Next sample isn't available yet. Use estimates. + trackData.mNextSampleTimecode = nextSampleTimecode; + trackData.mNextSampleTime = nextSampleTime; + } aResult = GetSampleResult::NO_ERROR; return p.forget(); } From da968e2d259270a3ef296409ea0614858f22abbb Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Sun, 28 Aug 2016 02:38:59 +1200 Subject: [PATCH 25/43] Bug 1298617: [MSE] P2. Attempt to search the exactly matching sample first. r=gerald MozReview-Commit-ID: AW1T51n6WMl --HG-- extra : rebase_source : 241a200fdb4f450e8188000aa70ea058aae817b5 --- dom/media/mediasource/TrackBuffersManager.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/dom/media/mediasource/TrackBuffersManager.cpp b/dom/media/mediasource/TrackBuffersManager.cpp index 10fbff94c60d..cd76264f7d7c 100644 --- a/dom/media/mediasource/TrackBuffersManager.cpp +++ b/dom/media/mediasource/TrackBuffersManager.cpp @@ -2250,6 +2250,18 @@ TrackBuffersManager::FindCurrentPosition(TrackInfo::TrackType aTrack, auto& trackData = GetTracksData(aTrack); const TrackBuffer& track = GetTrackBuffer(aTrack); + // Perform an exact search first. + for (uint32_t i = 0; i < track.Length(); i++) { + const RefPtr& sample = track[i]; + TimeInterval sampleInterval{ + TimeUnit::FromMicroseconds(sample->mTimecode), + TimeUnit::FromMicroseconds(sample->mTimecode + sample->mDuration)}; + + if (sampleInterval.ContainsStrict(trackData.mNextSampleTimecode)) { + return i; + } + } + for (uint32_t i = 0; i < track.Length(); i++) { const RefPtr& sample = track[i]; TimeInterval sampleInterval{ From 2014b787b86e014cc63d598cc2254356c96ad028 Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Sun, 28 Aug 2016 02:43:36 +1200 Subject: [PATCH 26/43] Bug 1298617: [MSE] P3. Optimize sample search by breaking loop early. r=gerald MozReview-Commit-ID: 48YcQiy0p8S --HG-- extra : rebase_source : 00e6c3468618bc6811faa80b5da590ab9e31d582 --- dom/media/mediasource/TrackBuffersManager.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/dom/media/mediasource/TrackBuffersManager.cpp b/dom/media/mediasource/TrackBuffersManager.cpp index cd76264f7d7c..63c955be21fe 100644 --- a/dom/media/mediasource/TrackBuffersManager.cpp +++ b/dom/media/mediasource/TrackBuffersManager.cpp @@ -2260,6 +2260,11 @@ TrackBuffersManager::FindCurrentPosition(TrackInfo::TrackType aTrack, if (sampleInterval.ContainsStrict(trackData.mNextSampleTimecode)) { return i; } + if (sampleInterval.mStart > trackData.mNextSampleTimecode) { + // Samples are ordered by timecode. There's no need to search + // any further. + break; + } } for (uint32_t i = 0; i < track.Length(); i++) { @@ -2272,6 +2277,11 @@ TrackBuffersManager::FindCurrentPosition(TrackInfo::TrackType aTrack, if (sampleInterval.ContainsWithStrictEnd(trackData.mNextSampleTimecode)) { return i; } + if (sampleInterval.mStart - aFuzz > trackData.mNextSampleTimecode) { + // Samples are ordered by timecode. There's no need to search + // any further. + break; + } } // We couldn't find our sample by decode timestamp. Attempt to find it using From 6e249bab1ec477b38678d683112a6c336ba9b5d5 Mon Sep 17 00:00:00 2001 From: Anjana Vakil Date: Fri, 26 Aug 2016 15:47:09 +0200 Subject: [PATCH 27/43] Bug 1291796 - Split Marionette harness tests by class; r=maja_zf Split the Marionette harness unit tests, all of which were previously located in the single module test_marionette_runner.py, into modules that each test a specific class or group of similar classes. This will make it easier to add and locate new tests in the future, based on the class they are testing. The new module structure within tests/harness_unit/ is: * test_marionette_harness.py - tests for MarionetteHarness and the command-line interface * test_marionette_arguments.py - tests for MarionetteArguments (future: BaseMarionetteArguments and RemoteMarionetteArguments) * test_marionette_runner.py - tests for MarionetteTestRunner and BaseMarionetteTestRunner (future: MarionetteTextTestRunner) * test_marionette_test_result.py - tests for MarionetteTestResult (future: MarionetteTest) * conftest.py - pytest fixtures used in multiple modules (fixtures specific to a single module are defined in that module) MozReview-Commit-ID: CGh6Aa07lfV --HG-- extra : rebase_source : faa7d27135aa6e4c6c44fa60aab6f2b5d6339961 --- .../marionette/tests/harness_unit/conftest.py | 94 +++++ .../harness_unit/test_marionette_arguments.py | 34 ++ .../harness_unit/test_marionette_harness.py | 110 ++++++ .../harness_unit/test_marionette_runner.py | 343 +++--------------- .../test_marionette_test_result.py | 52 +++ 5 files changed, 334 insertions(+), 299 deletions(-) create mode 100644 testing/marionette/harness/marionette/tests/harness_unit/conftest.py create mode 100644 testing/marionette/harness/marionette/tests/harness_unit/test_marionette_arguments.py create mode 100644 testing/marionette/harness/marionette/tests/harness_unit/test_marionette_harness.py create mode 100644 testing/marionette/harness/marionette/tests/harness_unit/test_marionette_test_result.py diff --git a/testing/marionette/harness/marionette/tests/harness_unit/conftest.py b/testing/marionette/harness/marionette/tests/harness_unit/conftest.py new file mode 100644 index 000000000000..14742a97ffa9 --- /dev/null +++ b/testing/marionette/harness/marionette/tests/harness_unit/conftest.py @@ -0,0 +1,94 @@ +# 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/. +import pytest + +from mock import Mock, MagicMock + +from marionette_driver.marionette import Marionette + + +@pytest.fixture(scope='module') +def logger(): + """ + Fake logger to help with mocking out other runner-related classes. + """ + import mozlog + return Mock(spec=mozlog.structuredlog.StructuredLogger) + + +@pytest.fixture +def mach_parsed_kwargs(logger): + """ + Parsed and verified dictionary used during simplest + call to mach marionette-test + """ + return { + 'adb_path': None, + 'addon': None, + 'address': None, + 'app': None, + 'app_args': [], + 'avd': None, + 'avd_home': None, + 'binary': u'/path/to/firefox', + 'browsermob_port' : None, + 'browsermob_script' : None, + 'device_serial': None, + 'e10s': True, + 'emulator': False, + 'emulator_bin': None, + 'gecko_log': None, + 'jsdebugger': False, + 'log_errorsummary': None, + 'log_html': None, + 'log_mach': None, + 'log_mach_buffer': None, + 'log_mach_level': None, + 'log_mach_verbose': None, + 'log_raw': None, + 'log_raw_level': None, + 'log_tbpl': None, + 'log_tbpl_buffer': None, + 'log_tbpl_compact': None, + 'log_tbpl_level': None, + 'log_unittest': None, + 'log_xunit': None, + 'logger_name': 'Marionette-based Tests', + 'prefs': { + 'browser.tabs.remote.autostart': True, + 'browser.tabs.remote.force-enable': True, + 'extensions.e10sBlocksEnabling': False, + }, + 'prefs_args': None, + 'prefs_files': None, + 'profile': None, + 'pydebugger': None, + 'repeat': 0, + 'server_root': None, + 'shuffle': False, + 'shuffle_seed': 2276870381009474531, + 'socket_timeout': 360.0, + 'sources': None, + 'startup_timeout': 60, + 'symbols_path': None, + 'test_tags': None, + 'tests': [u'/path/to/unit-tests.ini'], + 'testvars': None, + 'this_chunk': None, + 'timeout': None, + 'total_chunks': None, + 'verbose': None, + 'workspace': None, + 'logger': logger, + } + +@pytest.fixture +def mock_marionette(request): + """ Mock marionette instance """ + marionette = MagicMock(spec=Marionette) + if 'has_crashed' in request.funcargnames: + marionette.check_for_crash.return_value = request.getfuncargvalue( + 'has_crashed' + ) + return marionette diff --git a/testing/marionette/harness/marionette/tests/harness_unit/test_marionette_arguments.py b/testing/marionette/harness/marionette/tests/harness_unit/test_marionette_arguments.py new file mode 100644 index 000000000000..c17bdf418409 --- /dev/null +++ b/testing/marionette/harness/marionette/tests/harness_unit/test_marionette_arguments.py @@ -0,0 +1,34 @@ +# 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/. +import pytest + +from marionette.runtests import MarionetteArguments + + +@pytest.mark.parametrize("sock_timeout_value", ['A', '10', '1B-', '1C2', '44.35']) +def test_parse_arg_socket_timeout_with_multiple_values(sock_timeout_value): + argv = ['marionette', '--socket-timeout', sock_timeout_value] + parser = MarionetteArguments() + + def _is_float_convertible(value): + try: + float(value) + return True + except: + return False + + if not _is_float_convertible(sock_timeout_value): + # should raising exception, since sock_timeout must be convertible to float. + with pytest.raises(SystemExit) as ex: + parser.parse_args(args=argv) + assert ex.value.code == 2 + else: + # should pass without raising exception. + args = parser.parse_args(args=argv) + assert hasattr(args, 'socket_timeout') and args.socket_timeout == float(sock_timeout_value) + + +if __name__ == '__main__': + import sys + sys.exit(pytest.main(['--verbose', __file__])) diff --git a/testing/marionette/harness/marionette/tests/harness_unit/test_marionette_harness.py b/testing/marionette/harness/marionette/tests/harness_unit/test_marionette_harness.py new file mode 100644 index 000000000000..faeedfc2b1ea --- /dev/null +++ b/testing/marionette/harness/marionette/tests/harness_unit/test_marionette_harness.py @@ -0,0 +1,110 @@ +# 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/. +import pytest + +from mock import Mock, patch, sentinel + +from marionette.runtests import MarionetteTestRunner, MarionetteHarness, cli + +# avoid importing MarionetteJSTestCase to prevent pytest from +# collecting and running it as part of this test suite +import marionette.marionette_test as marionette_test + + +@pytest.fixture +def harness_class(request): + """ + Mock based on MarionetteHarness whose run method just returns a number of + failures according to the supplied test parameter + """ + if 'num_fails_crashed' in request.funcargnames: + num_fails_crashed = request.getfuncargvalue('num_fails_crashed') + else: + num_fails_crashed = (0, 0) + harness_cls = Mock(spec=MarionetteHarness) + harness = harness_cls.return_value + if num_fails_crashed is None: + harness.run.side_effect = Exception + else: + harness.run.return_value = sum(num_fails_crashed) + return harness_cls + + +@pytest.fixture +def runner_class(request): + """ + Mock based on MarionetteTestRunner, wherein the runner.failed, + runner.crashed attributes are provided by a test parameter + """ + if 'num_fails_crashed' in request.funcargnames: + failures, crashed = request.getfuncargvalue('num_fails_crashed') + else: + failures = 0 + crashed = 0 + mock_runner_class = Mock(spec=MarionetteTestRunner) + runner = mock_runner_class.return_value + runner.failed = failures + runner.crashed = crashed + return mock_runner_class + + +@pytest.mark.parametrize( + "num_fails_crashed,exit_code", + [((0, 0), 0), ((1, 0), 10), ((0, 1), 10), (None, 1)], +) +def test_cli_exit_code(num_fails_crashed, exit_code, harness_class): + with pytest.raises(SystemExit) as err: + cli(harness_class=harness_class) + assert err.value.code == exit_code + + +@pytest.mark.parametrize("num_fails_crashed", [(0, 0), (1, 0), (1, 1)]) +def test_call_harness_with_parsed_args_yields_num_failures(mach_parsed_kwargs, + runner_class, + num_fails_crashed): + with patch( + 'marionette.runtests.MarionetteHarness.parse_args' + ) as parse_args: + failed_or_crashed = MarionetteHarness(runner_class, + args=mach_parsed_kwargs).run() + parse_args.assert_not_called() + assert failed_or_crashed == sum(num_fails_crashed) + + +def test_call_harness_with_no_args_yields_num_failures(runner_class): + with patch( + 'marionette.runtests.MarionetteHarness.parse_args', + return_value={'tests': []} + ) as parse_args: + failed_or_crashed = MarionetteHarness(runner_class).run() + assert parse_args.call_count == 1 + assert failed_or_crashed == 0 + + +def test_args_passed_to_runner_class(mach_parsed_kwargs, runner_class): + arg_list = mach_parsed_kwargs.keys() + arg_list.remove('tests') + mach_parsed_kwargs.update([(a, getattr(sentinel, a)) for a in arg_list]) + harness = MarionetteHarness(runner_class, args=mach_parsed_kwargs) + harness.process_args = Mock() + harness.run() + for arg in arg_list: + assert harness._runner_class.call_args[1][arg] is getattr(sentinel, arg) + + +def test_harness_sets_up_default_test_handlers(mach_parsed_kwargs): + """ + If the necessary TestCase is not in test_handlers, + tests are omitted silently + """ + harness = MarionetteHarness(args=mach_parsed_kwargs) + mach_parsed_kwargs.pop('tests') + runner = harness._runner_class(**mach_parsed_kwargs) + assert marionette_test.MarionetteTestCase in runner.test_handlers + assert marionette_test.MarionetteJSTestCase in runner.test_handlers + + +if __name__ == '__main__': + import sys + sys.exit(pytest.main(['--verbose', __file__])) diff --git a/testing/marionette/harness/marionette/tests/harness_unit/test_marionette_runner.py b/testing/marionette/harness/marionette/tests/harness_unit/test_marionette_runner.py index b1f4ba7ffba1..8d7cd5c1b689 100644 --- a/testing/marionette/harness/marionette/tests/harness_unit/test_marionette_runner.py +++ b/testing/marionette/harness/marionette/tests/harness_unit/test_marionette_runner.py @@ -2,133 +2,12 @@ # 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/. import pytest -from mock import patch, Mock, DEFAULT, mock_open, MagicMock, sentinel -from marionette.runtests import ( - MarionetteTestRunner, - MarionetteHarness, - MarionetteArguments, - cli -) -from marionette.runner import MarionetteTestResult -from marionette_driver.marionette import Marionette +from mock import Mock, patch, mock_open, sentinel, DEFAULT + +from marionette.runtests import MarionetteTestRunner from manifestparser import TestManifest -# avoid importing MarionetteJSTestCase to prevent pytest from -# collecting and running it as part of this test suite -import marionette.marionette_test as marionette_test - - -def _check_crash_counts(has_crashed, runner, mock_marionette): - if has_crashed: - assert mock_marionette.check_for_crash.call_count == 1 - assert runner.crashed == 1 - else: - assert runner.crashed == 0 - - -@pytest.fixture -def mock_marionette(request): - """ Mock marionette instance """ - marionette = MagicMock(spec=Marionette) - if 'has_crashed' in request.funcargnames: - marionette.check_for_crash.return_value = request.getfuncargvalue( - 'has_crashed' - ) - return marionette - - -@pytest.fixture -def empty_marionette_testcase(): - """ Testable MarionetteTestCase class """ - class EmptyTestCase(marionette_test.MarionetteTestCase): - def test_nothing(self): - pass - - return EmptyTestCase - - -@pytest.fixture -def empty_marionette_test(mock_marionette, empty_marionette_testcase): - return empty_marionette_testcase(lambda: mock_marionette, 'test_nothing') - - -@pytest.fixture(scope='module') -def logger(): - """ - Fake logger to help with mocking out other runner-related classes. - """ - import mozlog - return Mock(spec=mozlog.structuredlog.StructuredLogger) - - -@pytest.fixture -def mach_parsed_kwargs(logger): - """ - Parsed and verified dictionary used during simplest - call to mach marionette-test - """ - return { - 'adb_path': None, - 'addon': None, - 'address': None, - 'app': None, - 'app_args': [], - 'avd': None, - 'avd_home': None, - 'binary': u'/path/to/firefox', - 'browsermob_port' : None, - 'browsermob_script' : None, - 'device_serial': None, - 'e10s': True, - 'emulator': False, - 'emulator_bin': None, - 'gecko_log': None, - 'jsdebugger': False, - 'log_errorsummary': None, - 'log_html': None, - 'log_mach': None, - 'log_mach_buffer': None, - 'log_mach_level': None, - 'log_mach_verbose': None, - 'log_raw': None, - 'log_raw_level': None, - 'log_tbpl': None, - 'log_tbpl_buffer': None, - 'log_tbpl_compact': None, - 'log_tbpl_level': None, - 'log_unittest': None, - 'log_xunit': None, - 'logger_name': 'Marionette-based Tests', - 'package_name': None, - 'prefs': { - 'browser.tabs.remote.autostart': True, - 'browser.tabs.remote.force-enable': True, - 'extensions.e10sBlocksEnabling': False, - }, - 'prefs_args': None, - 'prefs_files': None, - 'profile': None, - 'pydebugger': None, - 'repeat': 0, - 'server_root': None, - 'shuffle': False, - 'shuffle_seed': 2276870381009474531, - 'socket_timeout': 360.0, - 'sources': None, - 'startup_timeout': 60, - 'symbols_path': None, - 'test_tags': None, - 'tests': [u'/path/to/unit-tests.ini'], - 'testvars': None, - 'this_chunk': None, - 'timeout': None, - 'total_chunks': None, - 'verbose': None, - 'workspace': None, - 'logger': logger, - } - @pytest.fixture def runner(mach_parsed_kwargs): @@ -153,118 +32,6 @@ def mock_runner(runner, mock_marionette, monkeypatch): return runner -@pytest.fixture -def harness_class(request): - """ - Mock based on MarionetteHarness whose run method just returns a number of - failures according to the supplied test parameter - """ - if 'num_fails_crashed' in request.funcargnames: - num_fails_crashed = request.getfuncargvalue('num_fails_crashed') - else: - num_fails_crashed = (0, 0) - harness_cls = Mock(spec=MarionetteHarness) - harness = harness_cls.return_value - if num_fails_crashed is None: - harness.run.side_effect = Exception - else: - harness.run.return_value = sum(num_fails_crashed) - return harness_cls - - -@pytest.fixture -def runner_class(request): - """ - Mock based on MarionetteTestRunner, wherein the runner.failed, - runner.crashed attributes are provided by a test parameter - """ - if 'num_fails_crashed' in request.funcargnames: - failures, crashed = request.getfuncargvalue('num_fails_crashed') - else: - failures = 0 - crashed = 0 - mock_runner_class = Mock(spec=MarionetteTestRunner) - runner = mock_runner_class.return_value - runner.failed = failures - runner.crashed = crashed - return mock_runner_class - - -@pytest.mark.parametrize( - "num_fails_crashed,exit_code", - [((0, 0), 0), ((1, 0), 10), ((0, 1), 10), (None, 1)], -) -def test_cli_exit_code(num_fails_crashed, exit_code, harness_class): - with pytest.raises(SystemExit) as err: - cli(harness_class=harness_class) - assert err.value.code == exit_code - - -@pytest.mark.parametrize("num_fails_crashed", [(0, 0), (1, 0), (1, 1)]) -def test_call_harness_with_parsed_args_yields_num_failures(mach_parsed_kwargs, - runner_class, - num_fails_crashed): - with patch( - 'marionette.runtests.MarionetteHarness.parse_args' - ) as parse_args: - failed_or_crashed = MarionetteHarness(runner_class, - args=mach_parsed_kwargs).run() - parse_args.assert_not_called() - assert failed_or_crashed == sum(num_fails_crashed) - - -@pytest.mark.parametrize("sock_timeout_value", ['A', '10', '1B-', '1C2', '44.35']) -def test_parse_arg_socket_timeout_with_multiple_values(sock_timeout_value): - argv = ['marionette', '--socket-timeout', sock_timeout_value] - parser = MarionetteArguments() - - def _is_float_convertible(value): - try: - float(value) - return True - except: - return False - - if not _is_float_convertible(sock_timeout_value): - # should raising exception, since sock_timeout must be convertible to float. - with pytest.raises(SystemExit) as ex: - parser.parse_args(args=argv) - assert ex.value.code == 2 - else: - # should pass without raising exception. - args = parser.parse_args(args=argv) - assert hasattr(args, 'socket_timeout') and args.socket_timeout == float(sock_timeout_value) - - -def test_call_harness_with_no_args_yields_num_failures(runner_class): - with patch( - 'marionette.runtests.MarionetteHarness.parse_args', - return_value={'tests': []} - ) as parse_args: - failed_or_crashed = MarionetteHarness(runner_class).run() - assert parse_args.call_count == 1 - assert failed_or_crashed == 0 - - -def test_args_passed_to_runner_class(mach_parsed_kwargs, runner_class): - arg_list = mach_parsed_kwargs.keys() - arg_list.remove('tests') - mach_parsed_kwargs.update([(a, getattr(sentinel, a)) for a in arg_list]) - harness = MarionetteHarness(runner_class, args=mach_parsed_kwargs) - harness.process_args = Mock() - harness.run() - for arg in arg_list: - assert harness._runner_class.call_args[1][arg] is getattr(sentinel, arg) - - -def test_args_passed_to_driverclass(mock_runner): - built_kwargs = {'arg1': 'value1', 'arg2': 'value2'} - mock_runner._build_kwargs = Mock(return_value=built_kwargs) - with pytest.raises(IOError): - mock_runner.run_tests(['fake_tests.ini']) - assert mock_runner.driverclass.call_args[1] == built_kwargs - - @pytest.fixture def build_kwargs_using(mach_parsed_kwargs): '''Helper function for test_build_kwargs_* functions''' @@ -307,6 +74,42 @@ def expected_driver_args(runner): return expected +@pytest.fixture(params=['enabled', 'disabled', 'enabled_disabled', 'empty']) +def manifest_fixture(request): + ''' + Fixture for the contents of mock_manifest, where a manifest + can include enabled tests, disabled tests, both, or neither (empty) + ''' + included = [] + if 'enabled' in request.param: + included += [(u'test_expected_pass.py', 'pass'), + (u'test_expected_fail.py', 'fail')] + if 'disabled' in request.param: + included += [(u'test_pass_disabled.py', 'pass', 'skip-if: true'), + (u'test_fail_disabled.py', 'fail', 'skip-if: true')] + keys = ('path', 'expected', 'disabled') + active_tests = [dict(zip(keys, values)) for values in included] + + class ManifestFixture: + def __init__(self, name, tests): + self.filepath = "/path/to/fake/manifest.ini" + self.n_disabled = len([t for t in tests if 'disabled' in t]) + self.n_enabled = len(tests) - self.n_disabled + mock_manifest = Mock(spec=TestManifest, active_tests=Mock(return_value=tests)) + self.mock_manifest = Mock(return_value=mock_manifest) + self.__repr__ = lambda: "".format(name) + + return ManifestFixture(request.param, active_tests) + + +def test_args_passed_to_driverclass(mock_runner): + built_kwargs = {'arg1': 'value1', 'arg2': 'value2'} + mock_runner._build_kwargs = Mock(return_value=built_kwargs) + with pytest.raises(IOError): + mock_runner.run_tests(['fake_tests.ini']) + assert mock_runner.driverclass.call_args[1] == built_kwargs + + def test_build_kwargs_basic_args(build_kwargs_using): '''Test the functionality of runner._build_kwargs: make sure that basic arguments (those which should @@ -386,18 +189,6 @@ def test_build_kwargs_with_emulator_or_address(expected_driver_args, build_kwarg expected_driver_args.assert_keys_not_in(built_kwargs) -def test_harness_sets_up_default_test_handlers(mach_parsed_kwargs): - """ - If the necessary TestCase is not in test_handlers, - tests are omitted silently - """ - harness = MarionetteHarness(args=mach_parsed_kwargs) - mach_parsed_kwargs.pop('tests') - runner = harness._runner_class(**mach_parsed_kwargs) - assert marionette_test.MarionetteTestCase in runner.test_handlers - assert marionette_test.MarionetteJSTestCase in runner.test_handlers - - def test_parsing_testvars(mach_parsed_kwargs): mach_parsed_kwargs.pop('tests') testvars_json_loads = [ @@ -435,28 +226,12 @@ def test_load_testvars_throws_expected_errors(mach_parsed_kwargs): assert 'not properly formatted' in json_exc.value.message -@pytest.mark.parametrize("has_crashed", [True, False]) -def test_crash_is_recorded_as_error(empty_marionette_test, - logger, - has_crashed): - """ Number of errors is incremented by stopTest iff has_crashed is true """ - # collect results from the empty test - result = MarionetteTestResult( - marionette=empty_marionette_test._marionette_weakref(), - logger=logger, verbosity=None, - stream=None, descriptions=None, - ) - result.startTest(empty_marionette_test) - assert len(result.errors) == 0 - assert len(result.failures) == 0 - assert result.testsRun == 1 - assert result.shouldStop is False - result.stopTest(empty_marionette_test) - assert result.shouldStop == has_crashed +def _check_crash_counts(has_crashed, runner, mock_marionette): if has_crashed: - assert len(result.errors) == 1 + assert mock_marionette.check_for_crash.call_count == 1 + assert runner.crashed == 1 else: - assert len(result.errors) == 0 + assert runner.crashed == 0 @pytest.mark.parametrize("has_crashed", [True, False]) @@ -511,36 +286,6 @@ def test_add_test_directory(runner): assert len(runner.tests) == 4 - - -@pytest.fixture(params=['enabled', 'disabled', 'enabled_disabled', 'empty']) -def manifest_fixture(request): - ''' - Fixture for the contents of mock_manifest, where a manifest - can include enabled tests, disabled tests, both, or neither (empty) - ''' - included = [] - if 'enabled' in request.param: - included += [(u'test_expected_pass.py', 'pass'), - (u'test_expected_fail.py', 'fail')] - if 'disabled' in request.param: - included += [(u'test_pass_disabled.py', 'pass', 'skip-if: true'), - (u'test_fail_disabled.py', 'fail', 'skip-if: true')] - keys = ('path', 'expected', 'disabled') - active_tests = [dict(zip(keys, values)) for values in included] - - class ManifestFixture: - def __init__(self, name, tests): - self.filepath = "/path/to/fake/manifest.ini" - self.n_disabled = len([t for t in tests if 'disabled' in t]) - self.n_enabled = len(tests) - self.n_disabled - mock_manifest = Mock(spec=TestManifest, active_tests=Mock(return_value=tests)) - self.mock_manifest = Mock(return_value=mock_manifest) - self.__repr__ = lambda: "".format(name) - - return ManifestFixture(request.param, active_tests) - - @pytest.mark.parametrize("test_files_exist", [True, False]) def test_add_test_manifest(mock_runner, manifest_fixture, monkeypatch, test_files_exist): monkeypatch.setattr('marionette.runner.base.TestManifest', manifest_fixture.mock_manifest) @@ -625,4 +370,4 @@ def test_catch_invalid_test_names(runner): if __name__ == '__main__': import sys - sys.exit(pytest.main(['--verbose', __file__])) \ No newline at end of file + sys.exit(pytest.main(['--verbose', __file__])) diff --git a/testing/marionette/harness/marionette/tests/harness_unit/test_marionette_test_result.py b/testing/marionette/harness/marionette/tests/harness_unit/test_marionette_test_result.py new file mode 100644 index 000000000000..7c405002daba --- /dev/null +++ b/testing/marionette/harness/marionette/tests/harness_unit/test_marionette_test_result.py @@ -0,0 +1,52 @@ +# 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/. +import pytest + +from marionette.runner import MarionetteTestResult + + +@pytest.fixture +def empty_marionette_testcase(): + """ Testable MarionetteTestCase class """ + from marionette.marionette_test import MarionetteTestCase + + class EmptyTestCase(MarionetteTestCase): + def test_nothing(self): + pass + + return EmptyTestCase + + +@pytest.fixture +def empty_marionette_test(mock_marionette, empty_marionette_testcase): + return empty_marionette_testcase(lambda: mock_marionette, 'test_nothing') + + +@pytest.mark.parametrize("has_crashed", [True, False]) +def test_crash_is_recorded_as_error(empty_marionette_test, + logger, + has_crashed): + """ Number of errors is incremented by stopTest iff has_crashed is true """ + # collect results from the empty test + result = MarionetteTestResult( + marionette=empty_marionette_test._marionette_weakref(), + logger=logger, verbosity=None, + stream=None, descriptions=None, + ) + result.startTest(empty_marionette_test) + assert len(result.errors) == 0 + assert len(result.failures) == 0 + assert result.testsRun == 1 + assert result.shouldStop is False + result.stopTest(empty_marionette_test) + assert result.shouldStop == has_crashed + if has_crashed: + assert len(result.errors) == 1 + else: + assert len(result.errors) == 0 + + +if __name__ == '__main__': + import sys + sys.exit(pytest.main(['--verbose', __file__])) From 6aa106320d5f89f9ddcc60a225b166aa37421de1 Mon Sep 17 00:00:00 2001 From: Anjana Vakil Date: Fri, 26 Aug 2016 15:52:29 +0200 Subject: [PATCH 28/43] Bug 1291796 - Minor fixes to harness unit tests; r=maja_zf In test_marionette_runner.py, fix pytest warning raised when importing TestManifest class directly in global namespace. In test_marionette_arguments.py, improve readability by shortening/changing some names and removing unnecessary comments (not needed as code is self-explanatory). MozReview-Commit-ID: GDzxlEqb7MB --HG-- extra : rebase_source : 78927cae7f8ec011d2b398e3a1ce71174d7b028c --- .../tests/harness_unit/test_marionette_arguments.py | 12 +++++------- .../tests/harness_unit/test_marionette_runner.py | 5 +++-- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/testing/marionette/harness/marionette/tests/harness_unit/test_marionette_arguments.py b/testing/marionette/harness/marionette/tests/harness_unit/test_marionette_arguments.py index c17bdf418409..16fe759be89f 100644 --- a/testing/marionette/harness/marionette/tests/harness_unit/test_marionette_arguments.py +++ b/testing/marionette/harness/marionette/tests/harness_unit/test_marionette_arguments.py @@ -6,9 +6,9 @@ import pytest from marionette.runtests import MarionetteArguments -@pytest.mark.parametrize("sock_timeout_value", ['A', '10', '1B-', '1C2', '44.35']) -def test_parse_arg_socket_timeout_with_multiple_values(sock_timeout_value): - argv = ['marionette', '--socket-timeout', sock_timeout_value] +@pytest.mark.parametrize("socket_timeout", ['A', '10', '1B-', '1C2', '44.35']) +def test_parse_arg_socket_timeout(socket_timeout): + argv = ['marionette', '--socket-timeout', socket_timeout] parser = MarionetteArguments() def _is_float_convertible(value): @@ -18,15 +18,13 @@ def test_parse_arg_socket_timeout_with_multiple_values(sock_timeout_value): except: return False - if not _is_float_convertible(sock_timeout_value): - # should raising exception, since sock_timeout must be convertible to float. + if not _is_float_convertible(socket_timeout): with pytest.raises(SystemExit) as ex: parser.parse_args(args=argv) assert ex.value.code == 2 else: - # should pass without raising exception. args = parser.parse_args(args=argv) - assert hasattr(args, 'socket_timeout') and args.socket_timeout == float(sock_timeout_value) + assert hasattr(args, 'socket_timeout') and args.socket_timeout == float(socket_timeout) if __name__ == '__main__': diff --git a/testing/marionette/harness/marionette/tests/harness_unit/test_marionette_runner.py b/testing/marionette/harness/marionette/tests/harness_unit/test_marionette_runner.py index 8d7cd5c1b689..b6125e0460e4 100644 --- a/testing/marionette/harness/marionette/tests/harness_unit/test_marionette_runner.py +++ b/testing/marionette/harness/marionette/tests/harness_unit/test_marionette_runner.py @@ -6,7 +6,7 @@ import pytest from mock import Mock, patch, mock_open, sentinel, DEFAULT from marionette.runtests import MarionetteTestRunner -from manifestparser import TestManifest +import manifestparser @pytest.fixture @@ -95,7 +95,8 @@ def manifest_fixture(request): self.filepath = "/path/to/fake/manifest.ini" self.n_disabled = len([t for t in tests if 'disabled' in t]) self.n_enabled = len(tests) - self.n_disabled - mock_manifest = Mock(spec=TestManifest, active_tests=Mock(return_value=tests)) + mock_manifest = Mock(spec=manifestparser.TestManifest, + active_tests=Mock(return_value=tests)) self.mock_manifest = Mock(return_value=mock_manifest) self.__repr__ = lambda: "".format(name) From de3b7662786293e1a07d32a3f3b6d77a7fc93b11 Mon Sep 17 00:00:00 2001 From: Jan-Ivar Bruaroey Date: Fri, 19 Aug 2016 16:39:54 -0400 Subject: [PATCH 29/43] Bug 1284909 - Allow raw deviceId constraints in gUM when caller is chrome. r=jesup MozReview-Commit-ID: IB0BhGKbdam --HG-- extra : rebase_source : 9ed1f4218e66bd3c96d044cacce167cbde79e775 --- dom/media/MediaManager.cpp | 48 ++++++++++++++-------- dom/media/MediaManager.h | 4 +- dom/media/webrtc/MediaTrackConstraints.cpp | 3 +- dom/media/webrtc/MediaTrackConstraints.h | 11 +++-- 4 files changed, 43 insertions(+), 23 deletions(-) diff --git a/dom/media/MediaManager.cpp b/dom/media/MediaManager.cpp index c96b4f1b3461..d3fb90dcc75e 100644 --- a/dom/media/MediaManager.cpp +++ b/dom/media/MediaManager.cpp @@ -799,7 +799,8 @@ MediaDevice::FitnessDistance(nsString aN, uint32_t MediaDevice::GetBestFitnessDistance( - const nsTArray& aConstraintSets) + const nsTArray& aConstraintSets, + bool aIsChrome) { nsString mediaSource; GetMediaSource(mediaSource); @@ -818,7 +819,11 @@ MediaDevice::GetBestFitnessDistance( // Forward request to underlying object to interrogate per-mode capabilities. // Pass in device's origin-specific id for deviceId constraint comparison. nsString id; - GetId(id); + if (aIsChrome) { + GetRawId(id); + } else { + GetId(id); + } return mSource->GetBestFitnessDistance(aConstraintSets, id); } @@ -1333,6 +1338,7 @@ static auto& MediaManager_AnonymizeDevices = MediaManager::AnonymizeDevices; already_AddRefed MediaManager::SelectSettings( MediaStreamConstraints& aConstraints, + bool aIsChrome, RefPtr>>& aSources) { MOZ_ASSERT(NS_IsMainThread()); @@ -1342,7 +1348,8 @@ MediaManager::SelectSettings( // Algorithm accesses device capabilities code and must run on media thread. // Modifies passed-in aSources. - MediaManager::PostTask(NewTaskFrom([id, aConstraints, aSources]() mutable { + MediaManager::PostTask(NewTaskFrom([id, aConstraints, + aSources, aIsChrome]() mutable { auto& sources = **aSources; // Since the advanced part of the constraints algorithm needs to know when @@ -1368,11 +1375,13 @@ MediaManager::SelectSettings( if (needVideo && videos.Length()) { badConstraint = MediaConstraintsHelper::SelectSettings( - NormalizedConstraints(GetInvariant(aConstraints.mVideo)), videos); + NormalizedConstraints(GetInvariant(aConstraints.mVideo)), videos, + aIsChrome); } if (!badConstraint && needAudio && audios.Length()) { badConstraint = MediaConstraintsHelper::SelectSettings( - NormalizedConstraints(GetInvariant(aConstraints.mAudio)), audios); + NormalizedConstraints(GetInvariant(aConstraints.mAudio)), audios, + aIsChrome); } if (!badConstraint && !needVideo == !videos.Length() && @@ -1414,6 +1423,7 @@ public: uint64_t aWindowID, GetUserMediaCallbackMediaStreamListener *aListener, MediaEnginePrefs &aPrefs, const nsCString& aOrigin, + bool aIsChrome, MediaManager::SourceSet* aSourceSet) : mConstraints(aConstraints) , mOnSuccess(aOnSuccess) @@ -1422,6 +1432,7 @@ public: , mListener(aListener) , mPrefs(aPrefs) , mOrigin(aOrigin) + , mIsChrome(aIsChrome) , mDeviceChosen(false) , mSourceSet(aSourceSet) , mManager(MediaManager::GetInstance()) @@ -1473,7 +1484,7 @@ public: nsTArray> audios; audios.AppendElement(mAudioDevice); badConstraint = MediaConstraintsHelper::SelectSettings( - NormalizedConstraints(constraints), audios); + NormalizedConstraints(constraints), audios, mIsChrome); } } } @@ -1486,7 +1497,7 @@ public: nsTArray> videos; videos.AppendElement(mVideoDevice); badConstraint = MediaConstraintsHelper::SelectSettings( - NormalizedConstraints(constraints), videos); + NormalizedConstraints(constraints), videos, mIsChrome); } if (mAudioDevice) { mAudioDevice->Deallocate(); @@ -1517,8 +1528,8 @@ public: NS_DispatchToMainThread(do_AddRef( new GetUserMediaStreamRunnable(mOnSuccess, mOnFailure, mWindowID, - mListener, mOrigin, mConstraints, - mAudioDevice, mVideoDevice, + mListener, mOrigin, + mConstraints, mAudioDevice, mVideoDevice, peerIdentity))); MOZ_ASSERT(!mOnSuccess); MOZ_ASSERT(!mOnFailure); @@ -1600,6 +1611,7 @@ private: RefPtr mVideoDevice; MediaEnginePrefs mPrefs; nsCString mOrigin; + bool mIsChrome; bool mDeviceChosen; public: @@ -2208,7 +2220,7 @@ MediaManager::GetUserMedia(nsPIDOMWindowInner* aWindow, false) && !IsVistaOrLater()) || #endif (!privileged && !HostIsHttps(*docURI)) || - !HostHasPermission(*docURI)) { + (!isChrome && !HostHasPermission(*docURI))) { RefPtr error = new MediaStreamError(aWindow, NS_LITERAL_STRING("NotAllowedError")); @@ -2378,7 +2390,7 @@ MediaManager::GetUserMedia(nsPIDOMWindowInner* aWindow, RefPtr p = EnumerateDevicesImpl(windowID, videoType, audioType, fake); p->Then([this, onSuccess, onFailure, windowID, c, listener, askPermission, - prefs, isHTTPS, callID, origin](SourceSet*& aDevices) mutable { + prefs, isHTTPS, callID, origin, isChrome](SourceSet*& aDevices) mutable { RefPtr>> devices( new Refcountable>(aDevices)); // grab result @@ -2390,11 +2402,11 @@ MediaManager::GetUserMedia(nsPIDOMWindowInner* aWindow, } // Apply any constraints. This modifies the passed-in list. - RefPtr p2 = SelectSettings(c, devices); + RefPtr p2 = SelectSettings(c, isChrome, devices); p2->Then([this, onSuccess, onFailure, windowID, c, - listener, askPermission, prefs, isHTTPS, - callID, origin, devices](const char*& badConstraint) mutable { + listener, askPermission, prefs, isHTTPS, callID, + origin, isChrome, devices](const char*& badConstraint) mutable { // Ensure that the captured 'this' pointer and our windowID are still good. auto* globalWindow = nsGlobalWindow::GetInnerWindowWithId(windowID); @@ -2441,6 +2453,7 @@ MediaManager::GetUserMedia(nsPIDOMWindowInner* aWindow, onFailure.forget(), windowID, listener, prefs, origin, + isChrome, devices->release())); // Store the task w/callbacks. mActiveCallbacks.Put(callID, task.forget()); @@ -3489,10 +3502,11 @@ GetUserMediaCallbackMediaStreamListener::ApplyConstraintsToTrack( RefPtr mgr = MediaManager::GetInstance(); uint32_t id = mgr->mOutstandingVoidPledges.Append(*p); uint64_t windowId = aWindow->WindowID(); + bool isChrome = nsContentUtils::IsCallerChrome(); MediaManager::PostTask(NewTaskFrom([id, windowId, audioDevice, videoDevice, - aConstraints]() mutable { + aConstraints, isChrome]() mutable { MOZ_ASSERT(MediaManager::IsInMediaThread()); RefPtr mgr = MediaManager::GetInstance(); const char* badConstraint = nullptr; @@ -3504,7 +3518,7 @@ GetUserMediaCallbackMediaStreamListener::ApplyConstraintsToTrack( nsTArray> audios; audios.AppendElement(audioDevice); badConstraint = MediaConstraintsHelper::SelectSettings( - NormalizedConstraints(aConstraints), audios); + NormalizedConstraints(aConstraints), audios, isChrome); } } else { rv = videoDevice->Restart(aConstraints, mgr->mPrefs, &badConstraint); @@ -3512,7 +3526,7 @@ GetUserMediaCallbackMediaStreamListener::ApplyConstraintsToTrack( nsTArray> videos; videos.AppendElement(videoDevice); badConstraint = MediaConstraintsHelper::SelectSettings( - NormalizedConstraints(aConstraints), videos); + NormalizedConstraints(aConstraints), videos, isChrome); } } NS_DispatchToMainThread(NewRunnableFrom([id, windowId, rv, diff --git a/dom/media/MediaManager.h b/dom/media/MediaManager.h index 4f8114a023c5..a71ee400a707 100644 --- a/dom/media/MediaManager.h +++ b/dom/media/MediaManager.h @@ -72,7 +72,8 @@ public: void SetId(const nsAString& aID); void SetRawId(const nsAString& aID); virtual uint32_t GetBestFitnessDistance( - const nsTArray& aConstraintSets); + const nsTArray& aConstraintSets, + bool aIsChrome); virtual Source* GetSource() = 0; nsresult Allocate(const dom::MediaTrackConstraints &aConstraints, const MediaEnginePrefs &aPrefs, @@ -289,6 +290,7 @@ private: already_AddRefed SelectSettings( dom::MediaStreamConstraints& aConstraints, + bool aIsChrome, RefPtr>>& aSources); StreamListeners* AddWindowID(uint64_t aWindowId); diff --git a/dom/media/webrtc/MediaTrackConstraints.cpp b/dom/media/webrtc/MediaTrackConstraints.cpp index 7ed2839ede8b..1f4d84788780 100644 --- a/dom/media/webrtc/MediaTrackConstraints.cpp +++ b/dom/media/webrtc/MediaTrackConstraints.cpp @@ -441,7 +441,8 @@ MediaConstraintsHelper::FindBadConstraint( mDeviceId(MockDevice::HasThreadSafeRefCnt::value ? aDeviceId : nsString()) {} uint32_t GetBestFitnessDistance( - const nsTArray& aConstraintSets) + const nsTArray& aConstraintSets, + bool aIsChrome) { return mMediaEngineSource->GetBestFitnessDistance(aConstraintSets, mDeviceId); diff --git a/dom/media/webrtc/MediaTrackConstraints.h b/dom/media/webrtc/MediaTrackConstraints.h index 8369d6931d50..302d9a1039da 100644 --- a/dom/media/webrtc/MediaTrackConstraints.h +++ b/dom/media/webrtc/MediaTrackConstraints.h @@ -310,7 +310,7 @@ protected: MOZ_ASSERT(aDevices.Length()); for (auto& device : aDevices) { - if (device->GetBestFitnessDistance(sets) != UINT32_MAX) { + if (device->GetBestFitnessDistance(sets, false) != UINT32_MAX) { return true; } } @@ -323,7 +323,8 @@ public: template static const char* SelectSettings(const NormalizedConstraints &aConstraints, - nsTArray>& aDevices) + nsTArray>& aDevices, + bool aIsChrome) { auto& c = aConstraints; @@ -339,7 +340,8 @@ public: std::multimap> ordered; for (uint32_t i = 0; i < aDevices.Length();) { - uint32_t distance = aDevices[i]->GetBestFitnessDistance(aggregateConstraints); + uint32_t distance = aDevices[i]->GetBestFitnessDistance(aggregateConstraints, + aIsChrome); if (distance == UINT32_MAX) { unsatisfactory.AppendElement(aDevices[i]); aDevices.RemoveElementAt(i); @@ -365,7 +367,8 @@ public: aggregateConstraints.AppendElement(&c.mAdvanced[i]); nsTArray> rejects; for (uint32_t j = 0; j < aDevices.Length();) { - if (aDevices[j]->GetBestFitnessDistance(aggregateConstraints) == UINT32_MAX) { + if (aDevices[j]->GetBestFitnessDistance(aggregateConstraints, + aIsChrome) == UINT32_MAX) { rejects.AppendElement(aDevices[j]); aDevices.RemoveElementAt(j); } else { From 02cf050a769b81e61d5a28a5579a1f7a561729bc Mon Sep 17 00:00:00 2001 From: Tim Nguyen Date: Thu, 25 Aug 2016 20:51:59 -0700 Subject: [PATCH 30/43] Bug 1297523 - Fix intermittent browser_device_change.js. r=jryans --- .../responsive.html/test/browser/browser_device_change.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/devtools/client/responsive.html/test/browser/browser_device_change.js b/devtools/client/responsive.html/test/browser/browser_device_change.js index 4e67241465f5..4ddf7701b890 100644 --- a/devtools/client/responsive.html/test/browser/browser_device_change.js +++ b/devtools/client/responsive.html/test/browser/browser_device_change.js @@ -10,8 +10,15 @@ const DEFAULT_UA = Cc["@mozilla.org/network/protocol;1?name=http"] .userAgent; const NOKIA_UA = "Mozilla/5.0 (compatible; MSIE 10.0; Windows Phone 8.0; " + "Trident/6.0; IEMobile/10.0; ARM; Touch; NOKIA; Lumia 520)"; +const Types = require("devtools/client/responsive.html/types"); addRDMTask(TEST_URL, function* ({ ui, manager }) { + let { store } = ui.toolWindow; + + // Wait until the viewport has been added and the device list has been loaded + yield waitUntilState(store, state => state.viewports.length == 1 + && state.devices.listState == Types.deviceListState.LOADED); + // Test defaults testViewportDimensions(ui, 320, 480); yield testUserAgent(ui, DEFAULT_UA); From 4460ee86980c17cec5932ec5c1e7d2f8b0c177cf Mon Sep 17 00:00:00 2001 From: Ryan VanderMeulen Date: Mon, 29 Aug 2016 11:25:00 -0400 Subject: [PATCH 31/43] Backed out changeset 5f43357fe5b4 (bug 1284909) for bustage on a CLOSED TREE. --- dom/media/MediaManager.cpp | 48 ++++++++-------------- dom/media/MediaManager.h | 4 +- dom/media/webrtc/MediaTrackConstraints.cpp | 3 +- dom/media/webrtc/MediaTrackConstraints.h | 11 ++--- 4 files changed, 23 insertions(+), 43 deletions(-) diff --git a/dom/media/MediaManager.cpp b/dom/media/MediaManager.cpp index d3fb90dcc75e..c96b4f1b3461 100644 --- a/dom/media/MediaManager.cpp +++ b/dom/media/MediaManager.cpp @@ -799,8 +799,7 @@ MediaDevice::FitnessDistance(nsString aN, uint32_t MediaDevice::GetBestFitnessDistance( - const nsTArray& aConstraintSets, - bool aIsChrome) + const nsTArray& aConstraintSets) { nsString mediaSource; GetMediaSource(mediaSource); @@ -819,11 +818,7 @@ MediaDevice::GetBestFitnessDistance( // Forward request to underlying object to interrogate per-mode capabilities. // Pass in device's origin-specific id for deviceId constraint comparison. nsString id; - if (aIsChrome) { - GetRawId(id); - } else { - GetId(id); - } + GetId(id); return mSource->GetBestFitnessDistance(aConstraintSets, id); } @@ -1338,7 +1333,6 @@ static auto& MediaManager_AnonymizeDevices = MediaManager::AnonymizeDevices; already_AddRefed MediaManager::SelectSettings( MediaStreamConstraints& aConstraints, - bool aIsChrome, RefPtr>>& aSources) { MOZ_ASSERT(NS_IsMainThread()); @@ -1348,8 +1342,7 @@ MediaManager::SelectSettings( // Algorithm accesses device capabilities code and must run on media thread. // Modifies passed-in aSources. - MediaManager::PostTask(NewTaskFrom([id, aConstraints, - aSources, aIsChrome]() mutable { + MediaManager::PostTask(NewTaskFrom([id, aConstraints, aSources]() mutable { auto& sources = **aSources; // Since the advanced part of the constraints algorithm needs to know when @@ -1375,13 +1368,11 @@ MediaManager::SelectSettings( if (needVideo && videos.Length()) { badConstraint = MediaConstraintsHelper::SelectSettings( - NormalizedConstraints(GetInvariant(aConstraints.mVideo)), videos, - aIsChrome); + NormalizedConstraints(GetInvariant(aConstraints.mVideo)), videos); } if (!badConstraint && needAudio && audios.Length()) { badConstraint = MediaConstraintsHelper::SelectSettings( - NormalizedConstraints(GetInvariant(aConstraints.mAudio)), audios, - aIsChrome); + NormalizedConstraints(GetInvariant(aConstraints.mAudio)), audios); } if (!badConstraint && !needVideo == !videos.Length() && @@ -1423,7 +1414,6 @@ public: uint64_t aWindowID, GetUserMediaCallbackMediaStreamListener *aListener, MediaEnginePrefs &aPrefs, const nsCString& aOrigin, - bool aIsChrome, MediaManager::SourceSet* aSourceSet) : mConstraints(aConstraints) , mOnSuccess(aOnSuccess) @@ -1432,7 +1422,6 @@ public: , mListener(aListener) , mPrefs(aPrefs) , mOrigin(aOrigin) - , mIsChrome(aIsChrome) , mDeviceChosen(false) , mSourceSet(aSourceSet) , mManager(MediaManager::GetInstance()) @@ -1484,7 +1473,7 @@ public: nsTArray> audios; audios.AppendElement(mAudioDevice); badConstraint = MediaConstraintsHelper::SelectSettings( - NormalizedConstraints(constraints), audios, mIsChrome); + NormalizedConstraints(constraints), audios); } } } @@ -1497,7 +1486,7 @@ public: nsTArray> videos; videos.AppendElement(mVideoDevice); badConstraint = MediaConstraintsHelper::SelectSettings( - NormalizedConstraints(constraints), videos, mIsChrome); + NormalizedConstraints(constraints), videos); } if (mAudioDevice) { mAudioDevice->Deallocate(); @@ -1528,8 +1517,8 @@ public: NS_DispatchToMainThread(do_AddRef( new GetUserMediaStreamRunnable(mOnSuccess, mOnFailure, mWindowID, - mListener, mOrigin, - mConstraints, mAudioDevice, mVideoDevice, + mListener, mOrigin, mConstraints, + mAudioDevice, mVideoDevice, peerIdentity))); MOZ_ASSERT(!mOnSuccess); MOZ_ASSERT(!mOnFailure); @@ -1611,7 +1600,6 @@ private: RefPtr mVideoDevice; MediaEnginePrefs mPrefs; nsCString mOrigin; - bool mIsChrome; bool mDeviceChosen; public: @@ -2220,7 +2208,7 @@ MediaManager::GetUserMedia(nsPIDOMWindowInner* aWindow, false) && !IsVistaOrLater()) || #endif (!privileged && !HostIsHttps(*docURI)) || - (!isChrome && !HostHasPermission(*docURI))) { + !HostHasPermission(*docURI)) { RefPtr error = new MediaStreamError(aWindow, NS_LITERAL_STRING("NotAllowedError")); @@ -2390,7 +2378,7 @@ MediaManager::GetUserMedia(nsPIDOMWindowInner* aWindow, RefPtr p = EnumerateDevicesImpl(windowID, videoType, audioType, fake); p->Then([this, onSuccess, onFailure, windowID, c, listener, askPermission, - prefs, isHTTPS, callID, origin, isChrome](SourceSet*& aDevices) mutable { + prefs, isHTTPS, callID, origin](SourceSet*& aDevices) mutable { RefPtr>> devices( new Refcountable>(aDevices)); // grab result @@ -2402,11 +2390,11 @@ MediaManager::GetUserMedia(nsPIDOMWindowInner* aWindow, } // Apply any constraints. This modifies the passed-in list. - RefPtr p2 = SelectSettings(c, isChrome, devices); + RefPtr p2 = SelectSettings(c, devices); p2->Then([this, onSuccess, onFailure, windowID, c, - listener, askPermission, prefs, isHTTPS, callID, - origin, isChrome, devices](const char*& badConstraint) mutable { + listener, askPermission, prefs, isHTTPS, + callID, origin, devices](const char*& badConstraint) mutable { // Ensure that the captured 'this' pointer and our windowID are still good. auto* globalWindow = nsGlobalWindow::GetInnerWindowWithId(windowID); @@ -2453,7 +2441,6 @@ MediaManager::GetUserMedia(nsPIDOMWindowInner* aWindow, onFailure.forget(), windowID, listener, prefs, origin, - isChrome, devices->release())); // Store the task w/callbacks. mActiveCallbacks.Put(callID, task.forget()); @@ -3502,11 +3489,10 @@ GetUserMediaCallbackMediaStreamListener::ApplyConstraintsToTrack( RefPtr mgr = MediaManager::GetInstance(); uint32_t id = mgr->mOutstandingVoidPledges.Append(*p); uint64_t windowId = aWindow->WindowID(); - bool isChrome = nsContentUtils::IsCallerChrome(); MediaManager::PostTask(NewTaskFrom([id, windowId, audioDevice, videoDevice, - aConstraints, isChrome]() mutable { + aConstraints]() mutable { MOZ_ASSERT(MediaManager::IsInMediaThread()); RefPtr mgr = MediaManager::GetInstance(); const char* badConstraint = nullptr; @@ -3518,7 +3504,7 @@ GetUserMediaCallbackMediaStreamListener::ApplyConstraintsToTrack( nsTArray> audios; audios.AppendElement(audioDevice); badConstraint = MediaConstraintsHelper::SelectSettings( - NormalizedConstraints(aConstraints), audios, isChrome); + NormalizedConstraints(aConstraints), audios); } } else { rv = videoDevice->Restart(aConstraints, mgr->mPrefs, &badConstraint); @@ -3526,7 +3512,7 @@ GetUserMediaCallbackMediaStreamListener::ApplyConstraintsToTrack( nsTArray> videos; videos.AppendElement(videoDevice); badConstraint = MediaConstraintsHelper::SelectSettings( - NormalizedConstraints(aConstraints), videos, isChrome); + NormalizedConstraints(aConstraints), videos); } } NS_DispatchToMainThread(NewRunnableFrom([id, windowId, rv, diff --git a/dom/media/MediaManager.h b/dom/media/MediaManager.h index a71ee400a707..4f8114a023c5 100644 --- a/dom/media/MediaManager.h +++ b/dom/media/MediaManager.h @@ -72,8 +72,7 @@ public: void SetId(const nsAString& aID); void SetRawId(const nsAString& aID); virtual uint32_t GetBestFitnessDistance( - const nsTArray& aConstraintSets, - bool aIsChrome); + const nsTArray& aConstraintSets); virtual Source* GetSource() = 0; nsresult Allocate(const dom::MediaTrackConstraints &aConstraints, const MediaEnginePrefs &aPrefs, @@ -290,7 +289,6 @@ private: already_AddRefed SelectSettings( dom::MediaStreamConstraints& aConstraints, - bool aIsChrome, RefPtr>>& aSources); StreamListeners* AddWindowID(uint64_t aWindowId); diff --git a/dom/media/webrtc/MediaTrackConstraints.cpp b/dom/media/webrtc/MediaTrackConstraints.cpp index 1f4d84788780..7ed2839ede8b 100644 --- a/dom/media/webrtc/MediaTrackConstraints.cpp +++ b/dom/media/webrtc/MediaTrackConstraints.cpp @@ -441,8 +441,7 @@ MediaConstraintsHelper::FindBadConstraint( mDeviceId(MockDevice::HasThreadSafeRefCnt::value ? aDeviceId : nsString()) {} uint32_t GetBestFitnessDistance( - const nsTArray& aConstraintSets, - bool aIsChrome) + const nsTArray& aConstraintSets) { return mMediaEngineSource->GetBestFitnessDistance(aConstraintSets, mDeviceId); diff --git a/dom/media/webrtc/MediaTrackConstraints.h b/dom/media/webrtc/MediaTrackConstraints.h index 302d9a1039da..8369d6931d50 100644 --- a/dom/media/webrtc/MediaTrackConstraints.h +++ b/dom/media/webrtc/MediaTrackConstraints.h @@ -310,7 +310,7 @@ protected: MOZ_ASSERT(aDevices.Length()); for (auto& device : aDevices) { - if (device->GetBestFitnessDistance(sets, false) != UINT32_MAX) { + if (device->GetBestFitnessDistance(sets) != UINT32_MAX) { return true; } } @@ -323,8 +323,7 @@ public: template static const char* SelectSettings(const NormalizedConstraints &aConstraints, - nsTArray>& aDevices, - bool aIsChrome) + nsTArray>& aDevices) { auto& c = aConstraints; @@ -340,8 +339,7 @@ public: std::multimap> ordered; for (uint32_t i = 0; i < aDevices.Length();) { - uint32_t distance = aDevices[i]->GetBestFitnessDistance(aggregateConstraints, - aIsChrome); + uint32_t distance = aDevices[i]->GetBestFitnessDistance(aggregateConstraints); if (distance == UINT32_MAX) { unsatisfactory.AppendElement(aDevices[i]); aDevices.RemoveElementAt(i); @@ -367,8 +365,7 @@ public: aggregateConstraints.AppendElement(&c.mAdvanced[i]); nsTArray> rejects; for (uint32_t j = 0; j < aDevices.Length();) { - if (aDevices[j]->GetBestFitnessDistance(aggregateConstraints, - aIsChrome) == UINT32_MAX) { + if (aDevices[j]->GetBestFitnessDistance(aggregateConstraints) == UINT32_MAX) { rejects.AppendElement(aDevices[j]); aDevices.RemoveElementAt(j); } else { From b194d26e61b473c1c888bf2db0908cdce2b38efe Mon Sep 17 00:00:00 2001 From: Dan Glastonbury Date: Mon, 29 Aug 2016 10:41:02 +0800 Subject: [PATCH 32/43] Bug 1262053 - part1 : unblock window's media when the page was first visited. r=baku MozReview-Commit-ID: 5g5cYdgC8sg --HG-- extra : rebase_source : 5af18ff846f464b1d3d195d7dc2634e03601cfcc --- dom/base/nsDocument.cpp | 23 +++++++++++++++++++++++ dom/base/nsDocument.h | 4 ++++ dom/base/nsGlobalWindow.cpp | 3 ++- dom/base/nsIDocument.h | 3 +++ modules/libpref/init/all.js | 1 + 5 files changed, 33 insertions(+), 1 deletion(-) diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp index 9b36a7cacdc9..6d50e7ec9700 100644 --- a/dom/base/nsDocument.cpp +++ b/dom/base/nsDocument.cpp @@ -1498,6 +1498,8 @@ nsDocument::nsDocument(const char* aContentType) // Add the base queue sentinel to the processing stack. sProcessingStack->AppendElement((CustomElementData*) nullptr); } + + mEverInForeground = false; } void @@ -12603,6 +12605,10 @@ nsDocument::UpdateVisibilityState() EnumerateActivityObservers(NotifyActivityChanged, nullptr); } + + if (mVisibilityState == dom::VisibilityState::Visible) { + MaybeActiveMediaComponents(); + } } VisibilityState @@ -12638,6 +12644,23 @@ nsDocument::PostVisibilityUpdateEvent() NS_DispatchToMainThread(event); } +void +nsDocument::MaybeActiveMediaComponents() +{ + if (mEverInForeground) { + return; + } + + if (!mWindow) { + return; + } + + mEverInForeground = true; + if (GetWindow()->GetMediaSuspend() == nsISuspendedTypes::SUSPENDED_BLOCK) { + GetWindow()->SetMediaSuspend(nsISuspendedTypes::NONE_SUSPENDED); + } +} + NS_IMETHODIMP nsDocument::GetMozHidden(bool* aHidden) { diff --git a/dom/base/nsDocument.h b/dom/base/nsDocument.h index 8a48a8f27aa4..0f893add9f92 100644 --- a/dom/base/nsDocument.h +++ b/dom/base/nsDocument.h @@ -1209,6 +1209,10 @@ public: // Posts an event to call UpdateVisibilityState virtual void PostVisibilityUpdateEvent() override; + // Since we wouldn't automatically play media from non-visited page, we need + // to notify window when the page was first visited. + void MaybeActiveMediaComponents(); + virtual void DocAddSizeOfExcludingThis(nsWindowSizes* aWindowSizes) const override; // DocAddSizeOfIncludingThis is inherited from nsIDocument. diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index 4fae12cdca3b..12601637bba7 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -618,7 +618,8 @@ nsPIDOMWindow::nsPIDOMWindow(nsPIDOMWindowOuter *aOuterWindow) mInnerObjectsFreed(false), mIsModalContentWindow(false), mIsActive(false), mIsBackground(false), - mMediaSuspend(nsISuspendedTypes::NONE_SUSPENDED), + mMediaSuspend(Preferences::GetBool("media.block-autoplay-until-in-foreground", true) ? + nsISuspendedTypes::SUSPENDED_BLOCK : nsISuspendedTypes::NONE_SUSPENDED), mAudioMuted(false), mAudioVolume(1.0), mAudioCaptured(false), mDesktopModeViewport(false), mInnerWindow(nullptr), mOuterWindow(aOuterWindow), diff --git a/dom/base/nsIDocument.h b/dom/base/nsIDocument.h index c022df5e262b..834935b587d7 100644 --- a/dom/base/nsIDocument.h +++ b/dom/base/nsIDocument.h @@ -3069,6 +3069,9 @@ protected: // Do we currently have an event posted to call FlushUserFontSet? bool mPostedFlushUserFontSet : 1; + // True is document has ever been in a foreground window. + bool mEverInForeground : 1; + enum Type { eUnknown, // should never be used eHTML, diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index 0ee2c29854e7..82dd1c10f7f3 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -5532,6 +5532,7 @@ pref("dom.webkitBlink.dirPicker.enabled", true); pref("dom.webkitBlink.filesystem.enabled", true); #endif +pref("media.block-autoplay-until-in-foreground", true); #ifdef MOZ_STYLO // Is the Servo-backed style system enabled? pref("layout.css.servo.enabled", true); From 1231058432cfbf8f97e956897bb4b01c4b99f2ff Mon Sep 17 00:00:00 2001 From: Chris Pearce Date: Mon, 29 Aug 2016 10:42:10 +0800 Subject: [PATCH 33/43] Bug 1262053 - part2 : remove old media.block-play-until-visible behaviour. r=cpearce MozReview-Commit-ID: GujLSVfu2rp --HG-- extra : rebase_source : 6d229b3817736039e46713c9e029dec8fd8d5913 --- dom/html/HTMLMediaElement.cpp | 32 ++++---------------------------- dom/html/HTMLMediaElement.h | 7 +------ modules/libpref/init/all.js | 4 ---- 3 files changed, 5 insertions(+), 38 deletions(-) diff --git a/dom/html/HTMLMediaElement.cpp b/dom/html/HTMLMediaElement.cpp index 075c801e572a..00fe488f104b 100644 --- a/dom/html/HTMLMediaElement.cpp +++ b/dom/html/HTMLMediaElement.cpp @@ -2903,7 +2903,6 @@ HTMLMediaElement::HTMLMediaElement(already_AddRefed& aNo mAudioChannelVolume(1.0), mPlayingThroughTheAudioChannel(false), mDisableVideo(false), - mPlayBlockedBecauseHidden(false), mElementInTreeState(ELEMENT_NOT_INTREE), mHasUserInteraction(false), mFirstFrameLoaded(false), @@ -3010,14 +3009,14 @@ HTMLMediaElement::NotifyXPCOMShutdown() void HTMLMediaElement::Play(ErrorResult& aRv) { - nsresult rv = PlayInternal(nsContentUtils::IsCallerChrome()); + nsresult rv = PlayInternal(); if (NS_FAILED(rv)) { aRv.Throw(rv); } } nsresult -HTMLMediaElement::PlayInternal(bool aCallerIsChrome) +HTMLMediaElement::PlayInternal() { if (!IsAllowedToPlay()) { return NS_OK; @@ -3036,14 +3035,6 @@ HTMLMediaElement::PlayInternal(bool aCallerIsChrome) ResumeLoad(PRELOAD_ENOUGH); } - if (Preferences::GetBool("media.block-play-until-visible", false) && - !aCallerIsChrome && - OwnerDoc()->Hidden()) { - LOG(LogLevel::Debug, ("%p Blocked playback because owner hidden.", this)); - mPlayBlockedBecauseHidden = true; - return NS_OK; - } - // Even if we just did Load() or ResumeLoad(), we could already have a decoder // here if we managed to clone an existing decoder. if (mDecoder) { @@ -3103,7 +3094,7 @@ HTMLMediaElement::PlayInternal(bool aCallerIsChrome) NS_IMETHODIMP HTMLMediaElement::Play() { - return PlayInternal(/* aCallerIsChrome = */ true); + return PlayInternal(); } HTMLMediaElement::WakeLockBoolWrapper& @@ -4961,13 +4952,6 @@ void HTMLMediaElement::CheckAutoplayDataReady() return; } - if (Preferences::GetBool("media.block-play-until-visible", false) && - OwnerDoc()->Hidden()) { - LOG(LogLevel::Debug, ("%p Blocked autoplay because owner hidden.", this)); - mPlayBlockedBecauseHidden = true; - return; - } - mPaused = false; // We changed mPaused which can affect AddRemoveSelfReference AddRemoveSelfReference(); @@ -5343,14 +5327,6 @@ HTMLMediaElement::NotifyOwnerDocumentActivityChangedInternal() bool pauseElement = !IsActive(); SuspendOrResumeElement(pauseElement, !IsActive()); - if (!mPausedForInactiveDocumentOrChannel && - mPlayBlockedBecauseHidden && - !OwnerDoc()->Hidden()) { - LOG(LogLevel::Debug, ("%p Resuming playback now that owner doc is visble.", this)); - mPlayBlockedBecauseHidden = false; - Play(); - } - AddRemoveSelfReference(); return pauseElement; @@ -5957,7 +5933,7 @@ HTMLMediaElement::ResumeFromAudioChannelPaused(SuspendTypes aSuspend) mAudioChannelSuspended == nsISuspendedTypes::SUSPENDED_PAUSE_DISPOSABLE); SetAudioChannelSuspended(nsISuspendedTypes::NONE_SUSPENDED); - nsresult rv = PlayInternal(nsContentUtils::IsCallerChrome()); + nsresult rv = PlayInternal(); if (NS_WARN_IF(NS_FAILED(rv))) { return; } diff --git a/dom/html/HTMLMediaElement.h b/dom/html/HTMLMediaElement.h index c86dcc4ebeff..0cef804f03d1 100644 --- a/dom/html/HTMLMediaElement.h +++ b/dom/html/HTMLMediaElement.h @@ -805,7 +805,7 @@ protected: nsTArray>> mTrackPorts; }; - nsresult PlayInternal(bool aCallerIsChrome); + nsresult PlayInternal(); /** Use this method to change the mReadyState member, so required * events can be fired. @@ -1611,11 +1611,6 @@ protected: // playback. bool mDisableVideo; - // True if we blocked either a play() call or autoplay because the - // media's owner doc was not visible. Only enforced when the pref - // media.block-play-until-visible=true. - bool mPlayBlockedBecauseHidden; - // An agent used to join audio channel service. nsCOMPtr mAudioChannelAgent; diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index 82dd1c10f7f3..c0b0bf54ad65 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -310,10 +310,6 @@ pref("media.wakelock_timeout", 2000); // opened as top-level documents, as opposed to inside a media element. pref("media.play-stand-alone", true); -// Whether we should delay actioning a "play()" JS function call and autoplay -// attribute until the media element's owner document is visible. -pref("media.block-play-until-visible", false); - pref("media.hardware-video-decoding.enabled", true); pref("media.hardware-video-decoding.force-enabled", false); From c060486878dec35298a2b48a300bca31398a6da7 Mon Sep 17 00:00:00 2001 From: Alastor Wu Date: Mon, 29 Aug 2016 16:34:26 +0800 Subject: [PATCH 34/43] Bug 1262053 - part3 : modify media element for blocking autoplay media. r=cpearce MozReview-Commit-ID: 8e13lkYTN46 --HG-- extra : rebase_source : 134d698c6136b8dcf0ef67c6b40df787c42cb886 --- dom/html/HTMLMediaElement.cpp | 40 +++++++++++++++++++++-------------- dom/html/HTMLMediaElement.h | 3 +++ 2 files changed, 27 insertions(+), 16 deletions(-) diff --git a/dom/html/HTMLMediaElement.cpp b/dom/html/HTMLMediaElement.cpp index 00fe488f104b..e8071d0657a3 100644 --- a/dom/html/HTMLMediaElement.cpp +++ b/dom/html/HTMLMediaElement.cpp @@ -4963,12 +4963,15 @@ void HTMLMediaElement::CheckAutoplayDataReady() if (mCurrentPlayRangeStart == -1.0) { mCurrentPlayRangeStart = CurrentTime(); } - mDecoder->Play(); + if (!ShouldElementBePaused()) { + mDecoder->Play(); + } } else if (mSrcStream) { SetPlayedOrSeeked(true); } - DispatchAsyncEvent(NS_LITERAL_STRING("play")); + // For blocked media, the event would be pending until it is resumed. + DispatchAsyncEvent(NS_LITERAL_STRING("play")); } bool HTMLMediaElement::IsActive() const @@ -5293,19 +5296,8 @@ bool HTMLMediaElement::IsBeingDestroyed() void HTMLMediaElement::NotifyOwnerDocumentActivityChanged() { - bool pauseElement = NotifyOwnerDocumentActivityChangedInternal(); - if (pauseElement && mAudioChannelAgent) { - // If the element is being paused since we are navigating away from the - // document, notify the audio channel agent. - // Be careful to ignore this event during a docshell frame swap. - auto docShell = static_cast(OwnerDoc()->GetDocShell()); - if (!docShell) { - return; - } - if (!docShell->InFrameSwap()) { - NotifyAudioChannelAgent(false); - } - } + // TODO : merge NotifyOwnerDocumentActivityChangedInternal in following patch. + NotifyOwnerDocumentActivityChangedInternal(); } bool @@ -5324,7 +5316,7 @@ HTMLMediaElement::NotifyOwnerDocumentActivityChangedInternal() mDecoder->NotifyOwnerActivityChanged(visible); } - bool pauseElement = !IsActive(); + bool pauseElement = ShouldElementBePaused(); SuspendOrResumeElement(pauseElement, !IsActive()); AddRemoveSelfReference(); @@ -6569,6 +6561,22 @@ HTMLMediaElement::OnVisibilityChange(Visibility aOldVisibility, } +bool +HTMLMediaElement::ShouldElementBePaused() +{ + // The media in the non-visited page would be blocked. + if (mAudioChannelSuspended == nsISuspendedTypes::SUSPENDED_BLOCK) { + return true; + } + + // Bfcached page or inactive document. + if (!IsActive()) { + return true; + } + + return false; +} + void HTMLMediaElement::NotifyCueDisplayStatesChanged() { diff --git a/dom/html/HTMLMediaElement.h b/dom/html/HTMLMediaElement.h index 0cef804f03d1..f23e3b2410b6 100644 --- a/dom/html/HTMLMediaElement.h +++ b/dom/html/HTMLMediaElement.h @@ -1221,6 +1221,9 @@ protected: // channel agent is ready to be used. bool MaybeCreateAudioChannelAgent(); + // Determine if the element should be paused because of suspend conditions. + bool ShouldElementBePaused(); + /** * We have different kinds of suspended cases, * - SUSPENDED_PAUSE From 9021286e9a04662b8b89d4d64ef0e4d544010f2c Mon Sep 17 00:00:00 2001 From: Alastor Wu Date: Mon, 29 Aug 2016 16:34:28 +0800 Subject: [PATCH 35/43] Bug 1262053 - part4 : don't dispatch dom event for blocked media. r=cpearce If the media was blocked, we would postpone the dom event and dispatch them after media is resumed. MozReview-Commit-ID: LcdJtH16qQn --HG-- extra : rebase_source : 209d18925a88a9e0dac82f2c0695ec1b60f54f63 --- dom/html/HTMLMediaElement.cpp | 49 +++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 23 deletions(-) diff --git a/dom/html/HTMLMediaElement.cpp b/dom/html/HTMLMediaElement.cpp index e8071d0657a3..f50570f31e39 100644 --- a/dom/html/HTMLMediaElement.cpp +++ b/dom/html/HTMLMediaElement.cpp @@ -1020,6 +1020,9 @@ void HTMLMediaElement::AbortExistingLoads() if (mTextTrackManager) { mTextTrackManager->NotifyReset(); } + + mEventDeliveryPaused = false; + mPendingEvents.Clear(); } void HTMLMediaElement::NoSupportedMediaSourceError() @@ -3053,9 +3056,28 @@ HTMLMediaElement::PlayInternal() mCurrentPlayRangeStart = CurrentTime(); } + bool oldPaused = mPaused; + mPaused = false; + mAutoplaying = false; + SetAudioChannelSuspended(nsISuspendedTypes::NONE_SUSPENDED); + + // We changed mPaused and mAutoplaying which can affect AddRemoveSelfReference + // and our preload status. + AddRemoveSelfReference(); + UpdatePreloadAction(); + UpdateSrcMediaStreamPlaying(); + UpdateAudioChannelPlayingState(); + + // The check here is to handle the case that the media element starts playing + // after it loaded fail. eg. preload the data before playing. + OpenUnsupportedMediaWithExtenalAppIfNeeded(); + + // We should check audio channel playing state before dispatching any events, + // because we don't want to dispatch events for blocked media. For blocked + // media, the event would be pending until media is resumed. // TODO: If the playback has ended, then the user agent must set // seek to the effective start. - if (mPaused) { + if (oldPaused) { DispatchAsyncEvent(NS_LITERAL_STRING("play")); switch (mReadyState) { case nsIDOMHTMLMediaElement::HAVE_NOTHING: @@ -3074,21 +3096,6 @@ HTMLMediaElement::PlayInternal() } } - mPaused = false; - mAutoplaying = false; - SetAudioChannelSuspended(nsISuspendedTypes::NONE_SUSPENDED); - - // We changed mPaused and mAutoplaying which can affect AddRemoveSelfReference - // and our preload status. - AddRemoveSelfReference(); - UpdatePreloadAction(); - UpdateSrcMediaStreamPlaying(); - UpdateAudioChannelPlayingState(); - - // The check here is to handle the case that the media element starts playing - // after it loaded fail. eg. preload the data before playing. - OpenUnsupportedMediaWithExtenalAppIfNeeded(); - return NS_OK; } @@ -3786,10 +3793,6 @@ nsresult HTMLMediaElement::FinishDecoderSetup(MediaDecoder* aDecoder, // Force a same-origin check before allowing events for this media resource. mMediaSecurityVerified = false; - // The new stream has not been suspended by us. - mPausedForInactiveDocumentOrChannel = false; - mEventDeliveryPaused = false; - mPendingEvents.Clear(); // Set mDecoder now so if methods like GetCurrentSrc get called between // here and Load(), they work. SetDecoder(aDecoder); @@ -5938,7 +5941,7 @@ HTMLMediaElement::ResumeFromAudioChannelBlocked() MOZ_ASSERT(mAudioChannelSuspended == nsISuspendedTypes::SUSPENDED_BLOCK); SetAudioChannelSuspended(nsISuspendedTypes::NONE_SUSPENDED); - mPaused.SetCanPlay(true); + mPaused = false; SuspendOrResumeElement(false /* resume */, false); } @@ -5962,8 +5965,8 @@ HTMLMediaElement::BlockByAudioChannel() } SetAudioChannelSuspended(nsISuspendedTypes::SUSPENDED_BLOCK); - SuspendOrResumeElement(true /* suspend */, false); - mPaused.SetCanPlay(false); + mPaused = true; + SuspendOrResumeElement(true /* suspend */, true /* pending event */); } void From 47079de3ad70a6616dacfb11d789ac205640e0d1 Mon Sep 17 00:00:00 2001 From: Alastor Wu Date: Mon, 29 Aug 2016 16:34:31 +0800 Subject: [PATCH 36/43] Bug 1262053 - part5 : register audio agent immediately when media element starts playing. r=baku In ancient degisn, we would only register audio channel after the media element has audio track and enoguh data to playback, that is because the "audio-playback" event would be dispatched with the registration, and then shows the tab audio indicator. However, now the event dispatching doesn't follow with the registration, it would be triggered when the media element has really audible data which would be notified from media decoder. Therefore, the media element without audio track or without enough data can also register audio channel agent, it won't affect the display of tab audio indicator. The reason we need to do that is for blocking autoplay media in the non-visited tab. The autoplay can be adding "autoplay" keyword or playing by the script, and we don't want to dispatch dom event for blocked media. Therefore, we should register audio channel agent to know whether it needs to be blocked immediately even the media element doesn't have any enough data which can let us to distinguish it have any audio track or not (this information can be known from metadata). First, we must check whether the media is blocked which is notified by audio channel agent, and then we can decide whether need to dispatch the event. If we don't register audio channel agent, that we can't get blocking information. MozReview-Commit-ID: HLLkOuecql1 --HG-- extra : rebase_source : 99c34f0185276ecd5b70ae09959b47c584d1564e --- dom/html/HTMLMediaElement.cpp | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/dom/html/HTMLMediaElement.cpp b/dom/html/HTMLMediaElement.cpp index f50570f31e39..af8a2fce1d22 100644 --- a/dom/html/HTMLMediaElement.cpp +++ b/dom/html/HTMLMediaElement.cpp @@ -5765,11 +5765,6 @@ HTMLMediaElement::IsPlayingThroughTheAudioChannel() const return false; } - // If this element doesn't have any audio tracks. - if (!HasAudio()) { - return false; - } - // We should consider any bfcached page or inactive document as non-playing. if (!IsActive()) { return false; @@ -5780,11 +5775,6 @@ HTMLMediaElement::IsPlayingThroughTheAudioChannel() const return true; } - // If we are actually playing... - if (IsCurrentlyPlaying()) { - return true; - } - // If we are seeking, we consider it as playing if (mPlayingThroughTheAudioChannelBeforeSeek) { return true; @@ -5795,7 +5785,7 @@ HTMLMediaElement::IsPlayingThroughTheAudioChannel() const return true; } - return false; + return true; } void @@ -6256,7 +6246,6 @@ HTMLMediaElement::CannotDecryptWaitingForKey() NS_IMETHODIMP HTMLMediaElement::WindowAudioCaptureChanged(bool aCapture) { MOZ_ASSERT(mAudioChannelAgent); - MOZ_ASSERT(HasAudio()); if (!OwnerDoc()->GetInnerWindow()) { return NS_OK; From 97b81589f8837e213acace768969735b71ac1020 Mon Sep 17 00:00:00 2001 From: Alastor Wu Date: Mon, 29 Aug 2016 18:56:32 +0800 Subject: [PATCH 37/43] Bug 1262053 - part6 : don't need to capture media element without audio. r=baku,cpearce MozReview-Commit-ID: GO6nXbzYwIy --HG-- extra : rebase_source : 03aba373fde9133bf373292b2cdded5f04f22781 --- dom/html/HTMLMediaElement.cpp | 94 ++++++++++++++++++++--------------- dom/html/HTMLMediaElement.h | 5 ++ 2 files changed, 58 insertions(+), 41 deletions(-) diff --git a/dom/html/HTMLMediaElement.cpp b/dom/html/HTMLMediaElement.cpp index af8a2fce1d22..fb093b17c926 100644 --- a/dom/html/HTMLMediaElement.cpp +++ b/dom/html/HTMLMediaElement.cpp @@ -4301,7 +4301,8 @@ void HTMLMediaElement::MetadataLoaded(const MediaInfo* aInfo, // get dispatched. AutoNotifyAudioChannelAgent autoNotify(this); - mMediaInfo = *aInfo; + SetMediaInfo(*aInfo); + mIsEncrypted = aInfo->IsEncrypted() #ifdef MOZ_EME || mPendingEncryptedInitData.IsEncrypted() @@ -5509,7 +5510,7 @@ HTMLMediaElement::CopyInnerTo(Element* aDest) NS_ENSURE_SUCCESS(rv, rv); if (aDest->OwnerDoc()->IsStaticDocument()) { HTMLMediaElement* dest = static_cast(aDest); - dest->mMediaInfo = mMediaInfo; + dest->SetMediaInfo(mMediaInfo); } return rv; } @@ -6247,46 +6248,10 @@ NS_IMETHODIMP HTMLMediaElement::WindowAudioCaptureChanged(bool aCapture) { MOZ_ASSERT(mAudioChannelAgent); - if (!OwnerDoc()->GetInnerWindow()) { - return NS_OK; + if (mAudioCapturedByWindow != aCapture) { + mAudioCapturedByWindow = aCapture; + AudioCaptureStreamChangeIfNeeded(); } - - if (aCapture != mAudioCapturedByWindow) { - if (aCapture) { - mAudioCapturedByWindow = true; - nsCOMPtr window = OwnerDoc()->GetInnerWindow(); - uint64_t id = window->WindowID(); - MediaStreamGraph* msg = - MediaStreamGraph::GetInstance(MediaStreamGraph::AUDIO_THREAD_DRIVER, - mAudioChannel); - - if (GetSrcMediaStream()) { - mCaptureStreamPort = msg->ConnectToCaptureStream(id, GetSrcMediaStream()); - } else { - RefPtr stream = CaptureStreamInternal(false, msg); - mCaptureStreamPort = msg->ConnectToCaptureStream(id, stream->GetPlaybackStream()); - } - } else { - mAudioCapturedByWindow = false; - if (mDecoder) { - ProcessedMediaStream* ps = - mCaptureStreamPort->GetSource()->AsProcessedStream(); - MOZ_ASSERT(ps); - - for (uint32_t i = 0; i < mOutputStreams.Length(); i++) { - if (mOutputStreams[i].mStream->GetPlaybackStream() == ps) { - mOutputStreams.RemoveElementAt(i); - break; - } - } - - mDecoder->RemoveOutputStream(ps); - } - mCaptureStreamPort->Destroy(); - mCaptureStreamPort = nullptr; - } - } - return NS_OK; } @@ -6569,6 +6534,53 @@ HTMLMediaElement::ShouldElementBePaused() return false; } +void +HTMLMediaElement::SetMediaInfo(const MediaInfo aInfo) +{ + mMediaInfo = aInfo; + AudioCaptureStreamChangeIfNeeded(); +} + +void +HTMLMediaElement::AudioCaptureStreamChangeIfNeeded() +{ + // TODO : only capture media element with audio track, see bug1298777. + if (mAudioCapturedByWindow && !mCaptureStreamPort) { + nsCOMPtr window = OwnerDoc()->GetInnerWindow(); + if (!OwnerDoc()->GetInnerWindow()) { + return; + } + + uint64_t id = window->WindowID(); + MediaStreamGraph* msg = + MediaStreamGraph::GetInstance(MediaStreamGraph::AUDIO_THREAD_DRIVER, + mAudioChannel); + + if (GetSrcMediaStream()) { + mCaptureStreamPort = msg->ConnectToCaptureStream(id, GetSrcMediaStream()); + } else { + RefPtr stream = CaptureStreamInternal(false, msg); + mCaptureStreamPort = msg->ConnectToCaptureStream(id, stream->GetPlaybackStream()); + } + } else if (!mAudioCapturedByWindow && mCaptureStreamPort) { + if (mDecoder) { + ProcessedMediaStream* ps = + mCaptureStreamPort->GetSource()->AsProcessedStream(); + MOZ_ASSERT(ps); + + for (uint32_t i = 0; i < mOutputStreams.Length(); i++) { + if (mOutputStreams[i].mStream->GetPlaybackStream() == ps) { + mOutputStreams.RemoveElementAt(i); + break; + } + } + mDecoder->RemoveOutputStream(ps); + } + mCaptureStreamPort->Destroy(); + mCaptureStreamPort = nullptr; + } +} + void HTMLMediaElement::NotifyCueDisplayStatesChanged() { diff --git a/dom/html/HTMLMediaElement.h b/dom/html/HTMLMediaElement.h index f23e3b2410b6..a0b1a37e80de 100644 --- a/dom/html/HTMLMediaElement.h +++ b/dom/html/HTMLMediaElement.h @@ -744,6 +744,8 @@ public: bool ComputedMuted() const; nsSuspendedTypes ComputedSuspended() const; + void SetMediaInfo(const MediaInfo aInfo); + protected: virtual ~HTMLMediaElement(); @@ -1224,6 +1226,9 @@ protected: // Determine if the element should be paused because of suspend conditions. bool ShouldElementBePaused(); + // Create or destroy the captured stream depend on mAudioCapturedByWindow. + void AudioCaptureStreamChangeIfNeeded(); + /** * We have different kinds of suspended cases, * - SUSPENDED_PAUSE From 85c50bb3dd6469efaf6193867935906affbab857 Mon Sep 17 00:00:00 2001 From: Alastor Wu Date: Mon, 29 Aug 2016 18:56:36 +0800 Subject: [PATCH 38/43] Bug 1262053 - part7 : give audio focus for the non-visited page. r=baku MozReview-Commit-ID: K1cu8BhEtyV --HG-- extra : rebase_source : 730746bbee7eed1b3f7568c6e2ace7449933dbb8 --- dom/audiochannel/AudioChannelService.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/dom/audiochannel/AudioChannelService.cpp b/dom/audiochannel/AudioChannelService.cpp index ce36e94b7f82..0fc238f48b1b 100644 --- a/dom/audiochannel/AudioChannelService.cpp +++ b/dom/audiochannel/AudioChannelService.cpp @@ -1056,7 +1056,11 @@ AudioChannelService::AudioChannelWindow::RequestAudioFocus(AudioChannelAgent* aA // Only foreground window can request audio focus, but it would still own the // audio focus even it goes to background. Audio focus would be abandoned // only when other foreground window starts audio competing. - mOwningAudioFocus = !(aAgent->Window()->IsBackground()); + // One exception is if the pref "media.block-autoplay-until-in-foreground" + // is on and the background page is the non-visited before. Because the media + // in that page would be blocked until the page is going to foreground. + mOwningAudioFocus = (!(aAgent->Window()->IsBackground()) || + aAgent->Window()->GetMediaSuspend() == nsISuspendedTypes::SUSPENDED_BLOCK) ; MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug, ("AudioChannelWindow, RequestAudioFocus, this = %p, " From 56e48dbbf5d5bc95b597675cbd6be28d33a1e09f Mon Sep 17 00:00:00 2001 From: Alastor Wu Date: Mon, 29 Aug 2016 18:56:38 +0800 Subject: [PATCH 39/43] Bug 1262053 - part8 : remove function NotifyOwnerDocumentActivityChangedInternal. r=cpearce MozReview-Commit-ID: DnWgQHGJLU5 --HG-- extra : rebase_source : a6c4ab94fad22b2bef1bdde07bc17246943128f7 --- dom/html/HTMLMediaElement.cpp | 17 ++++------------- dom/html/HTMLMediaElement.h | 7 +------ dom/html/HTMLVideoElement.cpp | 7 +++---- dom/html/HTMLVideoElement.h | 2 +- 4 files changed, 9 insertions(+), 24 deletions(-) diff --git a/dom/html/HTMLMediaElement.cpp b/dom/html/HTMLMediaElement.cpp index fb093b17c926..958524e5a3a0 100644 --- a/dom/html/HTMLMediaElement.cpp +++ b/dom/html/HTMLMediaElement.cpp @@ -1399,7 +1399,7 @@ void HTMLMediaElement::NotifyMediaStreamTracksAvailable(DOMMediaStream* aStream) if (videoHasChanged) { // We are a video element and HasVideo() changed so update the screen // wakelock - NotifyOwnerDocumentActivityChangedInternal(); + NotifyOwnerDocumentActivityChanged(); } mWatchManager.ManualNotify(&HTMLMediaElement::UpdateReadyStateInternal); @@ -2923,7 +2923,7 @@ HTMLMediaElement::HTMLMediaElement(already_AddRefed& aNo mPaused.SetOuter(this); RegisterActivityObserver(); - NotifyOwnerDocumentActivityChangedInternal(); + NotifyOwnerDocumentActivityChanged(); MOZ_ASSERT(NS_IsMainThread()); mWatchManager.Watch(mDownloadSuspendedByCache, &HTMLMediaElement::UpdateReadyStateInternal); @@ -3858,7 +3858,7 @@ nsresult HTMLMediaElement::FinishDecoderSetup(MediaDecoder* aDecoder, // We may want to suspend the new stream now. // This will also do an AddRemoveSelfReference. - NotifyOwnerDocumentActivityChangedInternal(); + NotifyOwnerDocumentActivityChanged(); UpdateAudioChannelPlayingState(); if (!mPaused) { @@ -4344,7 +4344,7 @@ void HTMLMediaElement::MetadataLoaded(const MediaInfo* aInfo, if (IsVideo() && aInfo->HasVideo()) { // We are a video element playing video so update the screen wakelock - NotifyOwnerDocumentActivityChangedInternal(); + NotifyOwnerDocumentActivityChanged(); } if (mDefaultPlaybackStartPosition != 0.0) { @@ -5299,13 +5299,6 @@ bool HTMLMediaElement::IsBeingDestroyed() } void HTMLMediaElement::NotifyOwnerDocumentActivityChanged() -{ - // TODO : merge NotifyOwnerDocumentActivityChangedInternal in following patch. - NotifyOwnerDocumentActivityChangedInternal(); -} - -bool -HTMLMediaElement::NotifyOwnerDocumentActivityChangedInternal() { bool visible = !IsHidden(); if (visible) { @@ -5324,8 +5317,6 @@ HTMLMediaElement::NotifyOwnerDocumentActivityChangedInternal() SuspendOrResumeElement(pauseElement, !IsActive()); AddRemoveSelfReference(); - - return pauseElement; } void HTMLMediaElement::AddRemoveSelfReference() diff --git a/dom/html/HTMLMediaElement.h b/dom/html/HTMLMediaElement.h index a0b1a37e80de..cf490f24fb82 100644 --- a/dom/html/HTMLMediaElement.h +++ b/dom/html/HTMLMediaElement.h @@ -161,12 +161,7 @@ public: * Call this to reevaluate whether we should start/stop due to our owner * document being active, inactive, visible or hidden. */ - void NotifyOwnerDocumentActivityChanged(); - - // This method does the work necessary for the - // NotifyOwnerDocumentActivityChanged() notification. It returns true if the - // media element was paused as a result. - virtual bool NotifyOwnerDocumentActivityChangedInternal(); + virtual void NotifyOwnerDocumentActivityChanged(); // Called by the video decoder object, on the main thread, // when it has read the metadata containing video dimensions, diff --git a/dom/html/HTMLVideoElement.cpp b/dom/html/HTMLVideoElement.cpp index cf26cd00c053..fd4f46cb4608 100644 --- a/dom/html/HTMLVideoElement.cpp +++ b/dom/html/HTMLVideoElement.cpp @@ -221,12 +221,11 @@ HTMLVideoElement::WrapNode(JSContext* aCx, JS::Handle aGivenProto) return HTMLVideoElementBinding::Wrap(aCx, this, aGivenProto); } -bool -HTMLVideoElement::NotifyOwnerDocumentActivityChangedInternal() +void +HTMLVideoElement::NotifyOwnerDocumentActivityChanged() { - bool pauseElement = HTMLMediaElement::NotifyOwnerDocumentActivityChangedInternal(); + HTMLMediaElement::NotifyOwnerDocumentActivityChanged(); UpdateScreenWakeLock(); - return pauseElement; } FrameStatistics* diff --git a/dom/html/HTMLVideoElement.h b/dom/html/HTMLVideoElement.h index a996c8ba57d0..72e629a949c1 100644 --- a/dom/html/HTMLVideoElement.h +++ b/dom/html/HTMLVideoElement.h @@ -128,7 +128,7 @@ public: void SetMozUseScreenWakeLock(bool aValue); - bool NotifyOwnerDocumentActivityChangedInternal() override; + void NotifyOwnerDocumentActivityChanged() override; // Gives access to the decoder's frame statistics, if present. FrameStatistics* GetFrameStatistics(); From b29b15681d3338075d45692a9d93aea733e9dea0 Mon Sep 17 00:00:00 2001 From: Alastor Wu Date: Mon, 29 Aug 2016 18:56:40 +0800 Subject: [PATCH 40/43] Bug 1262053 - part9 : add test case. r=baku,cpearce MozReview-Commit-ID: DjoIr7jdZeq --HG-- extra : rebase_source : c4a672cdf2816eaa0462928f42780c260660fb0b --- toolkit/content/tests/browser/browser.ini | 4 + .../browser/browser_block_autoplay_media.js | 87 +++++++++++++++++++ 2 files changed, 91 insertions(+) create mode 100644 toolkit/content/tests/browser/browser_block_autoplay_media.js diff --git a/toolkit/content/tests/browser/browser.ini b/toolkit/content/tests/browser/browser.ini index 295360d85c2f..26b6e8a8c2e2 100644 --- a/toolkit/content/tests/browser/browser.ini +++ b/toolkit/content/tests/browser/browser.ini @@ -9,6 +9,10 @@ tags = audiochannel support-files = file_multipleAudio.html [browser_autoscroll_disabled.js] +[browser_block_autoplay_media.js] +tags = audiochannel +support-files = + file_multipleAudio.html [browser_bug295977_autoscroll_overflow.js] [browser_bug451286.js] skip-if = !e10s diff --git a/toolkit/content/tests/browser/browser_block_autoplay_media.js b/toolkit/content/tests/browser/browser_block_autoplay_media.js new file mode 100644 index 000000000000..5d0575c02e7c --- /dev/null +++ b/toolkit/content/tests/browser/browser_block_autoplay_media.js @@ -0,0 +1,87 @@ +const PAGE = "https://example.com/browser/toolkit/content/tests/browser/file_multipleAudio.html"; + +var SuspendedType = { + NONE_SUSPENDED : 0, + SUSPENDED_PAUSE : 1, + SUSPENDED_BLOCK : 2, + SUSPENDED_PAUSE_DISPOSABLE : 3 +}; + +function* wait_for_tab_playing_event(tab, expectPlaying) { + if (tab.soundPlaying == expectPlaying) { + ok(true, "The tab should " + (expectPlaying ? "" : "not ") + "be playing"); + } else { + yield BrowserTestUtils.waitForEvent(tab, "TabAttrModified", false, (event) => { + if (event.detail.changed.indexOf("soundplaying") >= 0) { + is(tab.soundPlaying, expectPlaying, "The tab should " + (expectPlaying ? "" : "not ") + "be playing"); + return true; + } + return false; + }); + } +} + +function check_audio_suspended(suspendedType) { + var autoPlay = content.document.getElementById('autoplay'); + if (!autoPlay) { + ok(false, "Can't get the audio element!"); + } + + is(autoPlay.computedSuspended, suspendedType, + "The suspeded state of autoplay audio is correct."); +} + +add_task(function* setup_test_preference() { + yield new Promise(resolve => { + SpecialPowers.pushPrefEnv({"set": [ + ["media.useAudioChannelService.testing", true], + ["media.block-autoplay-until-in-foreground", true] + ]}, resolve); + }); +}); + +add_task(function* block_autoplay_media() { + info("- open new background tab1 -"); + let tab1 = window.gBrowser.addTab("about:blank"); + tab1.linkedBrowser.loadURI(PAGE); + yield BrowserTestUtils.browserLoaded(tab1.linkedBrowser); + + info("- should block autoplay media for non-visited tab1 -"); + yield ContentTask.spawn(tab1.linkedBrowser, SuspendedType.SUSPENDED_BLOCK, + check_audio_suspended); + + info("- open new background tab2 -"); + let tab2 = window.gBrowser.addTab("about:blank"); + tab2.linkedBrowser.loadURI(PAGE); + yield BrowserTestUtils.browserLoaded(tab2.linkedBrowser); + + info("- should block autoplay for non-visited tab2 -"); + yield ContentTask.spawn(tab2.linkedBrowser, SuspendedType.SUSPENDED_BLOCK, + check_audio_suspended); + + info("- select tab1 as foreground tab -"); + yield BrowserTestUtils.switchTab(window.gBrowser, tab1); + + info("- media should be unblocked because the tab was visited -"); + yield wait_for_tab_playing_event(tab1, true); + yield ContentTask.spawn(tab1.linkedBrowser, SuspendedType.NONE_SUSPENDED, + check_audio_suspended); + + info("- open another new foreground tab3 -"); + let tab3 = yield BrowserTestUtils.openNewForegroundTab(window.gBrowser, + "about:blank"); + info("- should still play media from tab1 -"); + yield wait_for_tab_playing_event(tab1, true); + yield ContentTask.spawn(tab1.linkedBrowser, SuspendedType.NONE_SUSPENDED, + check_audio_suspended); + + info("- should still block media from tab2 -"); + yield wait_for_tab_playing_event(tab2, false); + yield ContentTask.spawn(tab2.linkedBrowser, SuspendedType.SUSPENDED_BLOCK, + check_audio_suspended); + + info("- remove tabs -"); + yield BrowserTestUtils.removeTab(tab1); + yield BrowserTestUtils.removeTab(tab2); + yield BrowserTestUtils.removeTab(tab3); +}); \ No newline at end of file From a2979b25f8ec840ebad36bffaa119afe7693f250 Mon Sep 17 00:00:00 2001 From: Alastor Wu Date: Mon, 29 Aug 2016 18:56:43 +0800 Subject: [PATCH 41/43] Bug 1262053 - part10 : modify tests. r=baku MozReview-Commit-ID: 98JWlLo7oRG --HG-- extra : rebase_source : 207b0c68a3218ad260f8b7c97748df7587cb0616 --- .../mochitest/browserElement_NoAudioTrack.js | 2 +- .../tests/browser/browser_block_autoplay_media.js | 2 +- .../browser_mediaPlayback_suspended_multipleAudio.js | 9 +-------- 3 files changed, 3 insertions(+), 10 deletions(-) diff --git a/dom/browser-element/mochitest/browserElement_NoAudioTrack.js b/dom/browser-element/mochitest/browserElement_NoAudioTrack.js index 2d163c91a210..47f0fd5244d4 100644 --- a/dom/browser-element/mochitest/browserElement_NoAudioTrack.js +++ b/dom/browser-element/mochitest/browserElement_NoAudioTrack.js @@ -74,7 +74,7 @@ function setupTestFrame() { ac.onactivestatechanged = () => { ac.onactivestatechanged = null; - error("Should not receive onactivestatechanged!"); + ok(true, "Should receive onactivestatechanged!"); }; continueTest(); diff --git a/toolkit/content/tests/browser/browser_block_autoplay_media.js b/toolkit/content/tests/browser/browser_block_autoplay_media.js index 5d0575c02e7c..3b2a309b9a36 100644 --- a/toolkit/content/tests/browser/browser_block_autoplay_media.js +++ b/toolkit/content/tests/browser/browser_block_autoplay_media.js @@ -84,4 +84,4 @@ add_task(function* block_autoplay_media() { yield BrowserTestUtils.removeTab(tab1); yield BrowserTestUtils.removeTab(tab2); yield BrowserTestUtils.removeTab(tab3); -}); \ No newline at end of file +}); diff --git a/toolkit/content/tests/browser/browser_mediaPlayback_suspended_multipleAudio.js b/toolkit/content/tests/browser/browser_mediaPlayback_suspended_multipleAudio.js index a76493daaef3..12e2ec077401 100644 --- a/toolkit/content/tests/browser/browser_mediaPlayback_suspended_multipleAudio.js +++ b/toolkit/content/tests/browser/browser_mediaPlayback_suspended_multipleAudio.js @@ -143,14 +143,7 @@ function play_nonautoplay_audio_should_be_blocked(suspendedType) { } nonAutoPlay.play(); - return new Promise(resolve => { - nonAutoPlay.onplay = function () { - nonAutoPlay.onplay = null; - is(nonAutoPlay.computedSuspended, suspendedType, - "The suspeded state of non-autoplay audio is correct."); - resolve(); - } - }); + ok(nonAutoPlay.paused, "The blocked audio can't be playback."); } function* suspended_pause(url, browser) { From d14c6b15aad622c282646824a70effd9a477d668 Mon Sep 17 00:00:00 2001 From: Drew Willcoxon Date: Fri, 26 Aug 2016 16:36:56 -0700 Subject: [PATCH 42/43] Bug 1292345 - Downloads panel didn't shrink to the fit height after all items are downloaded. r=me MozReview-Commit-ID: AV0S8GM0ztj --HG-- extra : rebase_source : e8d01256b994368b72c73344232f64cb26d6a357 --- browser/components/downloads/content/downloads.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/browser/components/downloads/content/downloads.js b/browser/components/downloads/content/downloads.js index eeae8524eada..dcf0b377b2e4 100644 --- a/browser/components/downloads/content/downloads.js +++ b/browser/components/downloads/content/downloads.js @@ -1525,6 +1525,10 @@ const DownloadsFooter = { } else { this._footerNode.removeAttribute("showingsummary"); } + if (!aValue && this._showingSummary) { + // Make sure the panel's height shrinks when the summary is hidden. + DownloadsBlockedSubview.view.setHeightToFit(); + } this._showingSummary = aValue; } return aValue; From ecea29f6a3e43d2fd5a203177c0ed59dfb08d02b Mon Sep 17 00:00:00 2001 From: Wes Kocher Date: Mon, 29 Aug 2016 17:40:59 -0700 Subject: [PATCH 43/43] Backed out changeset 0f53bc1a9aea (bug 1290619) a=merge --- dom/ipc/ContentChild.cpp | 9 -------- dom/ipc/ContentChild.h | 20 ------------------ dom/ipc/ContentProcess.cpp | 19 ----------------- dom/ipc/ContentProcess.h | 9 -------- ipc/glue/GeckoChildProcessHost.cpp | 18 ---------------- security/sandbox/mac/Sandbox.h | 4 +--- security/sandbox/mac/Sandbox.mm | 16 +++++---------- toolkit/xre/nsEmbedFunctions.cpp | 33 +----------------------------- 8 files changed, 7 insertions(+), 121 deletions(-) diff --git a/dom/ipc/ContentChild.cpp b/dom/ipc/ContentChild.cpp index 49e3d2ef40e1..e3f2187e98ca 100644 --- a/dom/ipc/ContentChild.cpp +++ b/dom/ipc/ContentChild.cpp @@ -1303,14 +1303,6 @@ StartMacOSContentSandbox() MOZ_CRASH("Failed to get NS_OS_TEMP_DIR path"); } - nsCOMPtr profileDir; - ContentChild::GetSingleton()->GetProfileDir(getter_AddRefs(profileDir)); - nsCString profileDirPath; - rv = profileDir->GetNativePath(profileDirPath); - if (NS_FAILED(rv)) { - MOZ_CRASH("Failed to get profile path"); - } - MacSandboxInfo info; info.type = MacSandboxType_Content; info.level = info.level = sandboxLevel; @@ -1318,7 +1310,6 @@ StartMacOSContentSandbox() info.appBinaryPath.assign(appBinaryPath.get()); info.appDir.assign(appDir.get()); info.appTempDir.assign(tempDirPath.get()); - info.profileDir.assign(profileDirPath.get()); std::string err; if (!mozilla::StartMacSandbox(info, err)) { diff --git a/dom/ipc/ContentChild.h b/dom/ipc/ContentChild.h index 11fb33df94c7..f781228c4919 100644 --- a/dom/ipc/ContentChild.h +++ b/dom/ipc/ContentChild.h @@ -21,9 +21,6 @@ #include "nsWeakPtr.h" #include "nsIWindowProvider.h" -#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX) -#include "nsIFile.h" -#endif struct ChromePackage; class nsIObserver; @@ -117,19 +114,6 @@ public: void GetProcessName(nsACString& aName) const; -#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX) - void GetProfileDir(nsIFile** aProfileDir) const - { - *aProfileDir = mProfileDir; - NS_IF_ADDREF(*aProfileDir); - } - - void SetProfileDir(nsIFile* aProfileDir) - { - mProfileDir = aProfileDir; - } -#endif - bool IsAlive() const; static void AppendProcessId(nsACString& aName); @@ -697,10 +681,6 @@ private: nsCOMPtr mPolicy; nsCOMPtr mForceKillTimer; -#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX) - nsCOMPtr mProfileDir; -#endif - // Hashtable to keep track of the pending GetFilesHelper objects. // This GetFilesHelperChild objects are removed when RecvGetFilesResponse is // received. diff --git a/dom/ipc/ContentProcess.cpp b/dom/ipc/ContentProcess.cpp index c3b3290a605e..4a4697e2c0e9 100644 --- a/dom/ipc/ContentProcess.cpp +++ b/dom/ipc/ContentProcess.cpp @@ -114,21 +114,6 @@ ContentProcess::SetAppDir(const nsACString& aPath) mXREEmbed.SetAppDir(aPath); } -#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX) -void -ContentProcess::SetProfile(const nsACString& aProfile) -{ - bool flag; - nsresult rv = - XRE_GetFileFromPath(aProfile.BeginReading(), getter_AddRefs(mProfileDir)); - if (NS_FAILED(rv) || - NS_FAILED(mProfileDir->Exists(&flag)) || !flag) { - NS_WARNING("Invalid profile directory passed to content process."); - mProfileDir = nullptr; - } -} -#endif - bool ContentProcess::Init() { @@ -139,10 +124,6 @@ ContentProcess::Init() mContent.InitXPCOM(); mContent.InitGraphicsDeviceData(); -#if (defined(XP_MACOSX)) && defined(MOZ_CONTENT_SANDBOX) - mContent.SetProfileDir(mProfileDir); -#endif - #if (defined(XP_WIN) || defined(XP_MACOSX)) && defined(MOZ_CONTENT_SANDBOX) SetUpSandboxEnvironment(); #endif diff --git a/dom/ipc/ContentProcess.h b/dom/ipc/ContentProcess.h index bf9968f8cad9..67ee74ec8f3d 100644 --- a/dom/ipc/ContentProcess.h +++ b/dom/ipc/ContentProcess.h @@ -39,18 +39,9 @@ public: void SetAppDir(const nsACString& aPath); -#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX) - void SetProfile(const nsACString& aProfile); -#endif - private: ContentChild mContent; mozilla::ipc::ScopedXREEmbed mXREEmbed; - -#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX) - nsCOMPtr mProfileDir; -#endif - #if defined(XP_WIN) // This object initializes and configures COM. mozilla::mscom::MainThreadRuntime mCOMRuntime; diff --git a/ipc/glue/GeckoChildProcessHost.cpp b/ipc/glue/GeckoChildProcessHost.cpp index 3431f628e307..d4f323dcbac2 100644 --- a/ipc/glue/GeckoChildProcessHost.cpp +++ b/ipc/glue/GeckoChildProcessHost.cpp @@ -23,10 +23,6 @@ #include "prenv.h" #include "nsXPCOMPrivate.h" -#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX) -#include "nsAppDirectoryServiceDefs.h" -#endif - #include "nsExceptionHandler.h" #include "nsDirectoryServiceDefs.h" @@ -612,20 +608,6 @@ AddAppDirToCommandLine(std::vector& aCmdLine) aCmdLine.push_back(path.get()); #endif } - -#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX) - // Full path to the profile dir - nsCOMPtr profileDir; - rv = directoryService->Get(NS_APP_USER_PROFILE_50_DIR, - NS_GET_IID(nsIFile), - getter_AddRefs(profileDir)); - if (NS_SUCCEEDED(rv)) { - nsAutoCString path; - MOZ_ALWAYS_SUCCEEDS(profileDir->GetNativePath(path)); - aCmdLine.push_back("-profile"); - aCmdLine.push_back(path.get()); - } -#endif } } } diff --git a/security/sandbox/mac/Sandbox.h b/security/sandbox/mac/Sandbox.h index 7093743c85cd..525544e8f319 100644 --- a/security/sandbox/mac/Sandbox.h +++ b/security/sandbox/mac/Sandbox.h @@ -41,8 +41,7 @@ typedef struct _MacSandboxInfo { _MacSandboxInfo(const struct _MacSandboxInfo& other) : type(other.type), level(other.level), pluginInfo(other.pluginInfo), appPath(other.appPath), appBinaryPath(other.appBinaryPath), - appDir(other.appDir), appTempDir(other.appTempDir), - profileDir(other.profileDir) {} + appDir(other.appDir), appTempDir(other.appTempDir) {} MacSandboxType type; int32_t level; MacSandboxPluginInfo pluginInfo; @@ -50,7 +49,6 @@ typedef struct _MacSandboxInfo { std::string appBinaryPath; std::string appDir; std::string appTempDir; - std::string profileDir; } MacSandboxInfo; namespace mozilla { diff --git a/security/sandbox/mac/Sandbox.mm b/security/sandbox/mac/Sandbox.mm index 3561429103c0..09af10280a4e 100644 --- a/security/sandbox/mac/Sandbox.mm +++ b/security/sandbox/mac/Sandbox.mm @@ -157,7 +157,6 @@ static const char contentSandboxRules[] = "(define appBinaryPath \"%s\")\n" "(define appDir \"%s\")\n" "(define appTempDir \"%s\")\n" - "(define profileDir \"%s\")\n" "(define home-path \"%s\")\n" "\n" "; Allow read access to standard system paths.\n" @@ -233,9 +232,6 @@ static const char contentSandboxRules[] = " (define (home-literal home-relative-literal)\n" " (resolving-literal (string-append home-path home-relative-literal)))\n" "\n" - " (define (profile-subpath profile-relative-subpath)\n" - " (resolving-subpath (string-append profileDir profile-relative-subpath)))\n" - "\n" " (define (container-regex container-relative-regex)\n" " (resolving-regex (string-append \"^\" (regex-quote container-path) container-relative-regex)))\n" " (define (container-subpath container-relative-subpath)\n" @@ -375,17 +371,16 @@ static const char contentSandboxRules[] = " (allow file-read*\n" " (home-regex \"/Library/Application Support/[^/]+/Extensions/[^/]/\")\n" " (resolving-regex \"/Library/Application Support/[^/]+/Extensions/[^/]/\")\n" - " (profile-subpath \"/extensions\")\n" - " (profile-subpath \"/weave\"))\n" + " (home-regex \"/Library/Application Support/Firefox/Profiles/[^/]+/extensions/\")\n" + " (home-regex \"/Library/Application Support/Firefox/Profiles/[^/]+/weave/\"))\n" "\n" - "; the following rules should be removed when printing and\n" + "; the following rules should be removed when printing and \n" "; opening a file from disk are brokered through the main process\n" " (if\n" " (< sandbox-level 2)\n" " (allow file*\n" - " (require-all\n" - " (require-not (home-subpath \"/Library\"))\n" - " (require-not (subpath profileDir))))\n" + " (require-not\n" + " (home-subpath \"/Library\")))\n" " (allow file*\n" " (require-all\n" " (subpath home-path)\n" @@ -502,7 +497,6 @@ bool StartMacSandbox(MacSandboxInfo aInfo, std::string &aErrorMessage) aInfo.appBinaryPath.c_str(), aInfo.appDir.c_str(), aInfo.appTempDir.c_str(), - aInfo.profileDir.c_str(), getenv("HOME")); } else { fprintf(stderr, diff --git a/toolkit/xre/nsEmbedFunctions.cpp b/toolkit/xre/nsEmbedFunctions.cpp index 19fb12860cf5..fdb6bbd07a90 100644 --- a/toolkit/xre/nsEmbedFunctions.cpp +++ b/toolkit/xre/nsEmbedFunctions.cpp @@ -606,44 +606,13 @@ XRE_InitChildProcess(int aArgc, case GeckoProcessType_Content: { process = new ContentProcess(parentPID); // If passed in grab the application path for xpcom init - bool foundAppdir = false; - -#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX) - // If passed in grab the profile path for sandboxing - bool foundProfile = false; -#endif - + nsCString appDir; for (int idx = aArgc; idx > 0; idx--) { if (aArgv[idx] && !strcmp(aArgv[idx], "-appdir")) { - MOZ_ASSERT(!foundAppdir); - if (foundAppdir) { - continue; - } - nsCString appDir; appDir.Assign(nsDependentCString(aArgv[idx+1])); static_cast(process.get())->SetAppDir(appDir); - foundAppdir = true; - } - -#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX) - if (aArgv[idx] && !strcmp(aArgv[idx], "-profile")) { - MOZ_ASSERT(!foundProfile); - if (foundProfile) { - continue; - } - nsCString profile; - profile.Assign(nsDependentCString(aArgv[idx+1])); - static_cast(process.get())->SetProfile(profile); - foundProfile = true; - } - if (foundProfile && foundAppdir) { break; } -#else - if (foundAppdir) { - break; - } -#endif /* XP_MACOSX && MOZ_CONTENT_SANDBOX */ } } break;