diff --git a/browser/components/privatebrowsing/content/aboutPrivateBrowsing.css b/browser/components/privatebrowsing/content/aboutPrivateBrowsing.css
index 18a2111ca1f9..2e9dce924e90 100644
--- a/browser/components/privatebrowsing/content/aboutPrivateBrowsing.css
+++ b/browser/components/privatebrowsing/content/aboutPrivateBrowsing.css
@@ -19,6 +19,9 @@ body[tpEnabled] .showTpDisabled,
body:not([tpEnabled]) .showTpEnabled {
display: none !important;
}
+body[globalTpEnabled] .showGlobalTpDisabled {
+ display: none !important;
+}
@media screen and (min-width: 1200px) and (min-height: 700px) {
body.private {
diff --git a/browser/components/privatebrowsing/content/aboutPrivateBrowsing.js b/browser/components/privatebrowsing/content/aboutPrivateBrowsing.js
index 079216d02dc5..c728d13b9b23 100644
--- a/browser/components/privatebrowsing/content/aboutPrivateBrowsing.js
+++ b/browser/components/privatebrowsing/content/aboutPrivateBrowsing.js
@@ -14,18 +14,25 @@ const FAVICON_PRIVACY = "chrome://browser/skin/Privacy-16.png";
let stringBundle = Services.strings.createBundle(
"chrome://browser/locale/aboutPrivateBrowsing.properties");
-let prefBranch = Services.prefs.getBranch("privacy.trackingprotection.pbmode.");
+let prefBranch = Services.prefs.getBranch("privacy.trackingprotection.");
let prefObserver = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
Ci.nsISupportsWeakReference]),
observe: function () {
if (prefBranch.getBoolPref("enabled")) {
+ document.body.setAttribute("globalTpEnabled", "true");
+ } else {
+ document.body.removeAttribute("globalTpEnabled");
+ }
+ if (prefBranch.getBoolPref("pbmode.enabled") ||
+ prefBranch.getBoolPref("enabled")) {
document.body.setAttribute("tpEnabled", "true");
} else {
document.body.removeAttribute("tpEnabled");
}
},
};
+prefBranch.addObserver("pbmode.enabled", prefObserver, true);
prefBranch.addObserver("enabled", prefObserver, true);
function setFavIcon(url) {
diff --git a/browser/components/privatebrowsing/content/aboutPrivateBrowsing.xhtml b/browser/components/privatebrowsing/content/aboutPrivateBrowsing.xhtml
index 0a108bedbc9a..c6590a1d4706 100644
--- a/browser/components/privatebrowsing/content/aboutPrivateBrowsing.xhtml
+++ b/browser/components/privatebrowsing/content/aboutPrivateBrowsing.xhtml
@@ -73,11 +73,11 @@
&trackingProtection.startTour1;
diff --git a/browser/devtools/netmonitor/netmonitor-view.js b/browser/devtools/netmonitor/netmonitor-view.js
index 9face7aac87d..ee52e335cdf7 100644
--- a/browser/devtools/netmonitor/netmonitor-view.js
+++ b/browser/devtools/netmonitor/netmonitor-view.js
@@ -1612,6 +1612,7 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
}
let nameWithQuery = this._getUriNameWithQuery(uri);
let hostPort = this._getUriHostPort(uri);
+ let host = this._getUriHost(uri);
let unicodeUrl = NetworkHelper.convertToUnicode(unescape(uri.spec));
let file = $(".requests-menu-file", target);
@@ -1621,6 +1622,27 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
let domain = $(".requests-menu-domain", target);
domain.setAttribute("value", hostPort);
domain.setAttribute("tooltiptext", hostPort);
+
+ // Mark local hosts specially, where "local" is as defined in the W3C
+ // spec for secure contexts.
+ // http://www.w3.org/TR/powerful-features/
+ //
+ // * If the name falls under 'localhost'
+ // * If the name is an IPv4 address within 127.0.0.0/8
+ // * If the name is an IPv6 address within ::1/128
+ //
+ // IPv6 parsing is a little sloppy; it assumes that the address has
+ // been validated before it gets here.
+ let icon = $(".requests-security-state-icon", target);
+ icon.classList.remove("security-state-local");
+ if (host.match(/(.+\.)?localhost$/) ||
+ host.match(/^127\.\d{1,3}\.\d{1,3}\.\d{1,3}/) ||
+ host.match(/\[[0:]+1\]/)) {
+ let tooltip = L10N.getStr("netmonitor.security.state.secure");
+ icon.classList.add("security-state-local");
+ icon.setAttribute("tooltiptext", tooltip);
+ }
+
break;
}
case "remoteAddress":
@@ -1630,12 +1652,17 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
domain.setAttribute("tooltiptext", tooltip);
break;
case "securityState": {
- let tooltip = L10N.getStr("netmonitor.security.state." + aValue);
let icon = $(".requests-security-state-icon", target);
+ this.attachSecurityIconClickListener(aItem);
+
+ // Security icon for local hosts is set in the "url" branch
+ if (icon.classList.contains("security-state-local")) {
+ break;
+ }
+
+ let tooltip = L10N.getStr("netmonitor.security.state." + aValue);
icon.classList.add("security-state-" + aValue);
icon.setAttribute("tooltiptext", tooltip);
-
- this.attachSecurityIconClickListener(aItem);
break;
}
case "status": {
@@ -2113,6 +2140,9 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
}
return NetworkHelper.convertToUnicode(unescape(aUrl.hostPort));
},
+ _getUriHost: function(aUrl) {
+ return this._getUriHostPort(aUrl).replace(/:\d+$/, "");
+ },
/**
* Helper for getting an abbreviated string for a mime type.
diff --git a/browser/devtools/netmonitor/test/browser_net_security-state.js b/browser/devtools/netmonitor/test/browser_net_security-state.js
index ae32473e6704..1b50719fa71a 100644
--- a/browser/devtools/netmonitor/test/browser_net_security-state.js
+++ b/browser/devtools/netmonitor/test/browser_net_security-state.js
@@ -14,6 +14,7 @@ add_task(function* () {
"example.com": "security-state-secure",
"nocert.example.com": "security-state-broken",
"rc4.example.com": "security-state-weak",
+ "localhost": "security-state-local",
};
yield new promise(resolve => {
@@ -50,6 +51,8 @@ add_task(function* () {
* - https://nocert.example.com (broken)
* - https://example.com (secure)
* - http://test1.example.com (insecure)
+ * - https://rc4.example.com (partly secure)
+ * - http://localhost (local)
* and waits until NetworkMonitor has handled all packets sent by the server.
*/
function* performRequests() {
@@ -82,14 +85,19 @@ add_task(function* () {
debuggee.performRequests(1, "https://rc4.example.com" + CORS_SJS_PATH);
yield done;
- is(RequestsMenu.itemCount, 4, "Four events logged.");
+ done = waitForSecurityBrokenNetworkEvent(true);
+ info("Requesting a resource over HTTP to localhost.");
+ debuggee.performRequests(1, "http://localhost" + CORS_SJS_PATH);
+ yield done;
+
+ is(RequestsMenu.itemCount, 5, "Five events logged.");
}
/**
* Returns a promise that's resolved once a request with security issues is
* completed.
*/
- function waitForSecurityBrokenNetworkEvent() {
+ function waitForSecurityBrokenNetworkEvent(networkError) {
let awaitedEvents = [
"UPDATING_REQUEST_HEADERS",
"RECEIVED_REQUEST_HEADERS",
@@ -102,6 +110,12 @@ add_task(function* () {
"RECEIVED_EVENT_TIMINGS",
];
+ // If the reason for breakage is a network error, then the
+ // STARTED_RECEIVING_RESPONSE event does not fire.
+ if (networkError) {
+ awaitedEvents.splice(4, 1);
+ }
+
let promises = awaitedEvents.map((event) => {
return monitor.panelWin.once(EVENTS[event]);
});
diff --git a/browser/devtools/performance/test/browser.ini b/browser/devtools/performance/test/browser.ini
index 684cad2dbba2..f3dc2c55e1e0 100644
--- a/browser/devtools/performance/test/browser.ini
+++ b/browser/devtools/performance/test/browser.ini
@@ -76,6 +76,7 @@ skip-if = os == 'linux' # Bug 1172120
[browser_perf-overview-render-02.js]
[browser_perf-overview-render-03.js]
[browser_perf-overview-render-04.js]
+skip-if = os == 'linux' # bug 1186322
[browser_perf-overview-selection-01.js]
[browser_perf-overview-selection-02.js]
[browser_perf-overview-selection-03.js]
diff --git a/browser/themes/shared/devtools/netmonitor.css b/browser/themes/shared/devtools/netmonitor.css
index 8fedc7c03ce5..a871cde5e1d5 100644
--- a/browser/themes/shared/devtools/netmonitor.css
+++ b/browser/themes/shared/devtools/netmonitor.css
@@ -142,25 +142,27 @@
width: 16px;
height: 16px;
-moz-margin-end: 4px;
+ cursor: pointer;
}
.security-state-insecure {
- list-style-image: url(chrome://browser/skin/identity-not-secure.svg);
+ list-style-image: url(chrome://browser/skin/identity-mixed-active-loaded.svg);
}
.security-state-secure {
- cursor: pointer;
list-style-image: url(chrome://browser/skin/identity-secure.svg);
}
.security-state-weak {
- cursor: pointer;
list-style-image: url(chrome://browser/skin/identity-mixed-passive-loaded.svg);
}
.security-state-broken {
- cursor: pointer;
- list-style-image: url(chrome://browser/skin/identity-mixed-active-loaded.svg);
+ list-style-image: url(chrome://browser/skin/controlcenter/warning-gray.svg);
+}
+
+.security-state-local {
+ list-style-image: url(chrome://browser/skin/identity-not-secure.svg);
}
.requests-menu-type {
@@ -196,7 +198,8 @@ label.requests-menu-status-code {
}
box.requests-menu-status:not([code]) {
- background-color: var(--theme-content-color2);
+ background-color: var(--theme-highlight-red);
+ border-radius: 0; /* squares */
}
box.requests-menu-status[code="cached"] {
diff --git a/configure.in b/configure.in
index 522a81c23eb2..65f6977d1bb3 100644
--- a/configure.in
+++ b/configure.in
@@ -767,8 +767,6 @@ See https://developer.mozilla.org/en/Windows_Build_Prerequisites.])
fi
AC_DEFINE_UNQUOTED(MOZ_WINSDK_TARGETVER,0x$MOZ_WINSDK_TARGETVER)
- # Definitions matching sdkddkver.h
- AC_DEFINE_UNQUOTED(MOZ_NTDDI_WIN7, 0x06010000)
AC_DEFINE_UNQUOTED(MOZ_WINSDK_MAXVER,$MOZ_WINSDK_MAXVER)
AC_SUBST(MOZ_WINSDK_MAXVER)
;;
diff --git a/dom/events/DataTransfer.cpp b/dom/events/DataTransfer.cpp
index 20d9cb979779..ef757fc4fc5a 100644
--- a/dom/events/DataTransfer.cpp
+++ b/dom/events/DataTransfer.cpp
@@ -850,12 +850,14 @@ DataTransfer::GetFilesAndDirectories(ErrorResult& aRv)
{
nsCOMPtr parentNode = do_QueryInterface(mParent);
if (!parentNode) {
+ aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
nsCOMPtr global = parentNode->OwnerDoc()->GetScopeObject();
MOZ_ASSERT(global);
if (!global) {
+ aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
@@ -865,43 +867,44 @@ DataTransfer::GetFilesAndDirectories(ErrorResult& aRv)
}
if (!mFiles) {
- ErrorResult dummy;
- GetFiles(dummy);
- if (!mFiles) {
+ GetFiles(aRv);
+ if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
}
Sequence filesAndDirsSeq;
- if (!filesAndDirsSeq.SetLength(mFiles->Length(), mozilla::fallible_t())) {
- p->MaybeReject(NS_ERROR_OUT_OF_MEMORY);
- return p.forget();
- }
+ if (mFiles && mFiles->Length()) {
+ if (!filesAndDirsSeq.SetLength(mFiles->Length(), mozilla::fallible_t())) {
+ p->MaybeReject(NS_ERROR_OUT_OF_MEMORY);
+ return p.forget();
+ }
- nsPIDOMWindow* window = parentNode->OwnerDoc()->GetInnerWindow();
+ nsPIDOMWindow* window = parentNode->OwnerDoc()->GetInnerWindow();
- nsRefPtr fs;
- for (uint32_t i = 0; i < mFiles->Length(); ++i) {
- if (mFiles->Item(i)->Impl()->IsDirectory()) {
+ nsRefPtr fs;
+ for (uint32_t i = 0; i < mFiles->Length(); ++i) {
+ if (mFiles->Item(i)->Impl()->IsDirectory()) {
#if defined(ANDROID) || defined(MOZ_B2G)
- MOZ_ASSERT(false,
- "Directory picking should have been redirected to normal "
- "file picking for platforms that don't have a directory "
- "picker");
+ MOZ_ASSERT(false,
+ "Directory picking should have been redirected to normal "
+ "file picking for platforms that don't have a directory "
+ "picker");
#endif
- nsAutoString path;
- mFiles->Item(i)->GetMozFullPathInternal(path, aRv);
- if (aRv.Failed()) {
- return nullptr;
+ nsAutoString path;
+ mFiles->Item(i)->GetMozFullPathInternal(path, aRv);
+ if (aRv.Failed()) {
+ return nullptr;
+ }
+ int32_t leafSeparatorIndex = path.RFind(FILE_PATH_SEPARATOR);
+ nsDependentSubstring dirname = Substring(path, 0, leafSeparatorIndex);
+ nsDependentSubstring basename = Substring(path, leafSeparatorIndex);
+ fs = MakeOrReuseFileSystem(dirname, fs, window);
+ filesAndDirsSeq[i].SetAsDirectory() = new Directory(fs, basename);
+ } else {
+ filesAndDirsSeq[i].SetAsFile() = mFiles->Item(i);
}
- int32_t leafSeparatorIndex = path.RFind(FILE_PATH_SEPARATOR);
- nsDependentSubstring dirname = Substring(path, 0, leafSeparatorIndex);
- nsDependentSubstring basename = Substring(path, leafSeparatorIndex);
- fs = MakeOrReuseFileSystem(dirname, fs, window);
- filesAndDirsSeq[i].SetAsDirectory() = new Directory(fs, basename);
- } else {
- filesAndDirsSeq[i].SetAsFile() = mFiles->Item(i);
}
}
diff --git a/dom/events/crashtests/1190036-1.html b/dom/events/crashtests/1190036-1.html
new file mode 100644
index 000000000000..00a6a25db6be
--- /dev/null
+++ b/dom/events/crashtests/1190036-1.html
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
diff --git a/dom/events/crashtests/crashtests.list b/dom/events/crashtests/crashtests.list
index 686b5b892318..1f22621e430e 100644
--- a/dom/events/crashtests/crashtests.list
+++ b/dom/events/crashtests/crashtests.list
@@ -9,6 +9,7 @@ load 1033343.html
load 1035654-1.html
load 1035654-2.html
load 1143972-1.html
+load 1190036-1.html
needs-focus load 1072137-1.html
load eventctor-nulldictionary.html
load eventctor-nullstorage.html
diff --git a/dom/media/AudioSegment.h b/dom/media/AudioSegment.h
index af90d81765ad..1e872e00116c 100644
--- a/dom/media/AudioSegment.h
+++ b/dom/media/AudioSegment.h
@@ -157,6 +157,16 @@ struct AudioChunk {
return static_cast(const_cast(mChannelData[aChannel]));
}
+ void ReleaseBufferIfShared()
+ {
+ if (mBuffer && mBuffer->IsShared()) {
+ // Remove pointers into the buffer, but keep the array allocation for
+ // chunk re-use.
+ mChannelData.ClearAndRetainStorage();
+ mBuffer = nullptr;
+ }
+ }
+
bool IsMuted() const { return mVolume == 0.0f; }
size_t SizeOfExcludingThisIfUnshared(MallocSizeOf aMallocSizeOf) const
diff --git a/dom/media/DecodedStream.cpp b/dom/media/DecodedStream.cpp
index 9396de33ae11..63d50cccea08 100644
--- a/dom/media/DecodedStream.cpp
+++ b/dom/media/DecodedStream.cpp
@@ -105,7 +105,7 @@ UpdateStreamBlocking(MediaStream* aStream, bool aBlocking)
*/
class DecodedStreamData {
public:
- DecodedStreamData(SourceMediaStream* aStream, bool aPlaying,
+ DecodedStreamData(SourceMediaStream* aStream,
MozPromiseHolder&& aPromise);
~DecodedStreamData();
bool IsFinished() const;
@@ -142,7 +142,7 @@ public:
bool mEOSVideoCompensation;
};
-DecodedStreamData::DecodedStreamData(SourceMediaStream* aStream, bool aPlaying,
+DecodedStreamData::DecodedStreamData(SourceMediaStream* aStream,
MozPromiseHolder&& aPromise)
: mAudioFramesWritten(0)
, mNextVideoTime(-1)
@@ -152,17 +152,15 @@ DecodedStreamData::DecodedStreamData(SourceMediaStream* aStream, bool aPlaying,
, mHaveSentFinishAudio(false)
, mHaveSentFinishVideo(false)
, mStream(aStream)
- , mPlaying(aPlaying)
+ , mPlaying(true)
, mEOSVideoCompensation(false)
{
// DecodedStreamGraphListener will resolve this promise.
mListener = new DecodedStreamGraphListener(mStream, Move(aPromise));
mStream->AddListener(mListener);
- // Block the stream if we are not playing.
- if (!aPlaying) {
- UpdateStreamBlocking(mStream, true);
- }
+ // mPlaying is initially true because MDSM won't start playback until playing
+ // becomes true. This is consistent with the settings of AudioSink.
}
DecodedStreamData::~DecodedStreamData()
@@ -358,7 +356,7 @@ DecodedStream::DecodedStream(AbstractThread* aOwnerThread,
MediaQueue& aAudioQueue,
MediaQueue& aVideoQueue)
: mOwnerThread(aOwnerThread)
- , mMonitor("DecodedStream::mMonitor")
+ , mShuttingDown(false)
, mPlaying(false)
, mVolume(1.0)
, mAudioQueue(aAudioQueue)
@@ -371,11 +369,17 @@ DecodedStream::~DecodedStream()
MOZ_ASSERT(mStartTime.isNothing(), "playback should've ended.");
}
+void
+DecodedStream::Shutdown()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ mShuttingDown = true;
+}
+
nsRefPtr
DecodedStream::StartPlayback(int64_t aStartTime, const MediaInfo& aInfo)
{
AssertOwnerThread();
- ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
MOZ_ASSERT(mStartTime.isNothing(), "playback already started.");
mStartTime.emplace(aStartTime);
@@ -413,7 +417,7 @@ DecodedStream::StartPlayback(int64_t aStartTime, const MediaInfo& aInfo)
void DecodedStream::StopPlayback()
{
AssertOwnerThread();
- ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
+
// Playback didn't even start at all.
if (mStartTime.isNothing()) {
return;
@@ -424,12 +428,19 @@ void DecodedStream::StopPlayback()
// Clear mData immediately when this playback session ends so we won't
// send data to the wrong stream in SendData() in next playback session.
- DecodedStreamData* data = mData.release();
- // mData is not yet created on the main thread.
- if (!data) {
+ DestroyData(Move(mData));
+}
+
+void
+DecodedStream::DestroyData(UniquePtr aData)
+{
+ AssertOwnerThread();
+
+ if (!aData) {
return;
}
+ DecodedStreamData* data = aData.release();
nsRefPtr self = this;
nsCOMPtr r = NS_NewRunnableFunction([=] () {
self->mOutputStreamManager.Disconnect();
@@ -442,35 +453,41 @@ void
DecodedStream::CreateData(MozPromiseHolder&& aPromise)
{
MOZ_ASSERT(NS_IsMainThread());
- ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
- MOZ_ASSERT(!mData, "Already created.");
// No need to create a source stream when there are no output streams. This
// happens when RemoveOutput() is called immediately after StartPlayback().
- // We also bail out when the playback session has ended. This happens when
- // StopPlayback() is called immediately after StartPlayback().
- if (!mOutputStreamManager.Graph() || mStartTime.isNothing()) {
+ // Also we don't create a source stream when MDSM has begun shutdown.
+ if (!mOutputStreamManager.Graph() || mShuttingDown) {
// Resolve the promise to indicate the end of playback.
aPromise.Resolve(true, __func__);
return;
}
auto source = mOutputStreamManager.Graph()->CreateSourceStream(nullptr);
- mData.reset(new DecodedStreamData(source, mPlaying, Move(aPromise)));
- mOutputStreamManager.Connect(mData->mStream);
+ auto data = new DecodedStreamData(source, Move(aPromise));
+ mOutputStreamManager.Connect(data->mStream);
- // Start to send data to the stream immediately
- nsRefPtr self = this;
- nsCOMPtr r = NS_NewRunnableFunction([=] () {
- ReentrantMonitorAutoEnter mon(self->GetReentrantMonitor());
- // Don't send data if playback has ended.
- if (self->mStartTime.isSome()) {
- self->SendData();
+ class R : public nsRunnable {
+ typedef void(DecodedStream::*Method)(UniquePtr);
+ public:
+ R(DecodedStream* aThis, Method aMethod, DecodedStreamData* aData)
+ : mThis(aThis), mMethod(aMethod), mData(aData) {}
+ NS_IMETHOD Run() override
+ {
+ (mThis->*mMethod)(Move(mData));
+ return NS_OK;
}
- });
- // Don't assert success because the owner thread might have begun shutdown
- // while we are still dealing with jobs on the main thread.
- mOwnerThread->Dispatch(r.forget(), AbstractThread::DontAssertDispatchSuccess);
+ private:
+ nsRefPtr mThis;
+ Method mMethod;
+ UniquePtr mData;
+ };
+
+ // Post a message to ensure |mData| is only updated on the worker thread.
+ // Note this must be done before MDSM's shutdown since dispatch could fail
+ // when the worker thread is shut down.
+ nsCOMPtr r = new R(this, &DecodedStream::OnDataCreated, data);
+ mOwnerThread->Dispatch(r.forget());
}
bool
@@ -479,10 +496,22 @@ DecodedStream::HasConsumers() const
return !mOutputStreamManager.IsEmpty();
}
-ReentrantMonitor&
-DecodedStream::GetReentrantMonitor() const
+void
+DecodedStream::OnDataCreated(UniquePtr aData)
{
- return mMonitor;
+ AssertOwnerThread();
+ MOZ_ASSERT(!mData, "Already created.");
+
+ // Start to send data to the stream immediately
+ if (mStartTime.isSome()) {
+ aData->SetPlaying(mPlaying);
+ mData = Move(aData);
+ SendData();
+ return;
+ }
+
+ // Playback has ended. Destroy aData which is not needed anymore.
+ DestroyData(Move(aData));
}
void
@@ -501,7 +530,6 @@ void
DecodedStream::SetPlaying(bool aPlaying)
{
AssertOwnerThread();
- ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
mPlaying = aPlaying;
if (mData) {
mData->SetPlaying(aPlaying);
@@ -512,7 +540,6 @@ void
DecodedStream::SetVolume(double aVolume)
{
AssertOwnerThread();
- ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
mVolume = aVolume;
}
@@ -520,7 +547,6 @@ void
DecodedStream::SetSameOrigin(bool aSameOrigin)
{
AssertOwnerThread();
- ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
mSameOrigin = aSameOrigin;
}
@@ -528,7 +554,6 @@ void
DecodedStream::InitTracks()
{
AssertOwnerThread();
- GetReentrantMonitor().AssertCurrentThreadIn();
if (mData->mStreamInitialized) {
return;
@@ -609,7 +634,6 @@ void
DecodedStream::SendAudio(double aVolume, bool aIsSameOrigin)
{
AssertOwnerThread();
- GetReentrantMonitor().AssertCurrentThreadIn();
if (!mInfo.HasAudio()) {
return;
@@ -675,7 +699,6 @@ void
DecodedStream::SendVideo(bool aIsSameOrigin)
{
AssertOwnerThread();
- GetReentrantMonitor().AssertCurrentThreadIn();
if (!mInfo.HasVideo()) {
return;
@@ -754,7 +777,6 @@ void
DecodedStream::AdvanceTracks()
{
AssertOwnerThread();
- GetReentrantMonitor().AssertCurrentThreadIn();
StreamTime endPosition = 0;
@@ -779,7 +801,6 @@ void
DecodedStream::SendData()
{
AssertOwnerThread();
- ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
MOZ_ASSERT(mStartTime.isSome(), "Must be called after StartPlayback()");
// Not yet created on the main thread. MDSM will try again later.
@@ -810,7 +831,6 @@ int64_t
DecodedStream::AudioEndTime() const
{
AssertOwnerThread();
- ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
if (mStartTime.isSome() && mInfo.HasAudio() && mData) {
CheckedInt64 t = mStartTime.ref() +
FramesToUsecs(mData->mAudioFramesWritten, mInfo.mAudio.mRate);
@@ -825,7 +845,6 @@ int64_t
DecodedStream::GetPosition() const
{
AssertOwnerThread();
- ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
// This is only called after MDSM starts playback. So mStartTime is
// guaranteed to be something.
MOZ_ASSERT(mStartTime.isSome());
@@ -836,7 +855,6 @@ bool
DecodedStream::IsFinished() const
{
AssertOwnerThread();
- ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
return mData && mData->IsFinished();
}
@@ -844,7 +862,6 @@ void
DecodedStream::ConnectListener()
{
AssertOwnerThread();
- ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
mAudioPushListener = mAudioQueue.PushEvent().Connect(
mOwnerThread, this, &DecodedStream::SendData);
@@ -860,7 +877,6 @@ void
DecodedStream::DisconnectListener()
{
AssertOwnerThread();
- ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
mAudioPushListener.Disconnect();
mVideoPushListener.Disconnect();
diff --git a/dom/media/DecodedStream.h b/dom/media/DecodedStream.h
index adc7220bada3..463bd4ae2f07 100644
--- a/dom/media/DecodedStream.h
+++ b/dom/media/DecodedStream.h
@@ -106,6 +106,8 @@ public:
MediaQueue& aAudioQueue,
MediaQueue& aVideoQueue);
+ void Shutdown();
+
// Mimic MDSM::StartAudioThread.
// Must be called before any calls to SendData().
//
@@ -132,8 +134,9 @@ protected:
virtual ~DecodedStream();
private:
- ReentrantMonitor& GetReentrantMonitor() const;
void CreateData(MozPromiseHolder&& aPromise);
+ void DestroyData(UniquePtr aData);
+ void OnDataCreated(UniquePtr aData);
void InitTracks();
void AdvanceTracks();
void SendAudio(double aVolume, bool aIsSameOrigin);
@@ -149,18 +152,18 @@ private:
const nsRefPtr mOwnerThread;
- UniquePtr mData;
+ /*
+ * Main thread only members.
+ */
// Data about MediaStreams that are being fed by the decoder.
OutputStreamManager mOutputStreamManager;
+ // True if MDSM has begun shutdown.
+ bool mShuttingDown;
- // TODO: This is a temp solution to get rid of decoder monitor on the main
- // thread in MDSM::AddOutputStream and MDSM::RecreateDecodedStream as
- // required by bug 1146482. DecodedStream needs to release monitor before
- // calling back into MDSM functions in order to prevent deadlocks.
- //
- // Please move all capture-stream related code from MDSM into DecodedStream
- // and apply "dispatch + mirroring" to get rid of this monitor in the future.
- mutable ReentrantMonitor mMonitor;
+ /*
+ * Worker thread only members.
+ */
+ UniquePtr mData;
bool mPlaying;
double mVolume;
diff --git a/dom/media/MediaDecoderStateMachine.h b/dom/media/MediaDecoderStateMachine.h
index 09c73c98e137..41acca625284 100644
--- a/dom/media/MediaDecoderStateMachine.h
+++ b/dom/media/MediaDecoderStateMachine.h
@@ -171,6 +171,7 @@ public:
void DispatchShutdown()
{
+ mDecodedStream->Shutdown();
nsCOMPtr runnable =
NS_NewRunnableMethod(this, &MediaDecoderStateMachine::Shutdown);
OwnerThread()->Dispatch(runnable.forget());
diff --git a/dom/media/MediaRecorder.cpp b/dom/media/MediaRecorder.cpp
index db951c9a3eb7..5cb916f4d677 100644
--- a/dom/media/MediaRecorder.cpp
+++ b/dom/media/MediaRecorder.cpp
@@ -776,9 +776,10 @@ MediaRecorder::MediaRecorder(AudioNode& aSrcAudioNode,
if (aSrcAudioNode.NumberOfOutputs() > 0) {
AudioContext* ctx = aSrcAudioNode.Context();
AudioNodeEngine* engine = new AudioNodeEngine(nullptr);
- mPipeStream = ctx->Graph()->CreateAudioNodeStream(engine,
- MediaStreamGraph::EXTERNAL_STREAM,
- ctx->SampleRate());
+ AudioNodeStream::Flags flags =
+ AudioNodeStream::EXTERNAL_OUTPUT |
+ AudioNodeStream::NEED_MAIN_THREAD_FINISHED;
+ mPipeStream = AudioNodeStream::Create(ctx->Graph(), engine, flags);
AudioNodeStream* ns = aSrcAudioNode.GetStream();
if (ns) {
mInputPort = mPipeStream->AllocateInputPort(aSrcAudioNode.GetStream(),
diff --git a/dom/media/MediaStreamGraph.cpp b/dom/media/MediaStreamGraph.cpp
index 5466ba970abb..6ade109fafc2 100644
--- a/dom/media/MediaStreamGraph.cpp
+++ b/dom/media/MediaStreamGraph.cpp
@@ -20,7 +20,6 @@
#include "ImageContainer.h"
#include "AudioCaptureStream.h"
#include "AudioChannelService.h"
-#include "AudioNodeEngine.h"
#include "AudioNodeStream.h"
#include "AudioNodeExternalInputStream.h"
#include "mozilla/dom/AudioContextBinding.h"
@@ -86,7 +85,7 @@ MediaStreamGraphImpl::FinishStream(MediaStream* aStream)
static const GraphTime START_TIME_DELAYED = -1;
void
-MediaStreamGraphImpl::AddStream(MediaStream* aStream)
+MediaStreamGraphImpl::AddStreamGraphThread(MediaStream* aStream)
{
// Check if we're adding a stream to a suspended context, in which case, we
// add it to mSuspendedStreams, and delay setting mBufferStartTime
@@ -113,7 +112,7 @@ MediaStreamGraphImpl::AddStream(MediaStream* aStream)
}
void
-MediaStreamGraphImpl::RemoveStream(MediaStream* aStream)
+MediaStreamGraphImpl::RemoveStreamGraphThread(MediaStream* aStream)
{
// Remove references in mStreamUpdates before we allow aStream to die.
// Pending updates are not needed (since the main thread has already given
@@ -1641,7 +1640,7 @@ public:
explicit CreateMessage(MediaStream* aStream) : ControlMessage(aStream) {}
virtual void Run() override
{
- mStream->GraphImpl()->AddStream(mStream);
+ mStream->GraphImpl()->AddStreamGraphThread(mStream);
}
virtual void RunDuringShutdown() override
{
@@ -2055,7 +2054,7 @@ MediaStream::Destroy()
mStream->RemoveAllListenersImpl();
auto graph = mStream->GraphImpl();
mStream->DestroyImpl();
- graph->RemoveStream(mStream);
+ graph->RemoveStreamGraphThread(mStream);
}
virtual void RunDuringShutdown()
{ Run(); }
@@ -2383,9 +2382,8 @@ MediaStream::AddMainThreadListener(MainThreadMediaStreamListener* aListener)
mMainThreadListeners.AppendElement(aListener);
- // If we have to send the notification or we have a runnable that will do it,
- // let finish here.
- if (!mFinishedNotificationSent || mNotificationMainThreadRunnable) {
+ // If it is not yet time to send the notification, then finish here.
+ if (!mFinishedNotificationSent) {
return;
}
@@ -2399,7 +2397,6 @@ MediaStream::AddMainThreadListener(MainThreadMediaStreamListener* aListener)
NS_IMETHOD Run() override
{
MOZ_ASSERT(NS_IsMainThread());
- mStream->mNotificationMainThreadRunnable = nullptr;
mStream->NotifyMainThreadListeners();
return NS_OK;
}
@@ -2411,11 +2408,7 @@ MediaStream::AddMainThreadListener(MainThreadMediaStreamListener* aListener)
};
nsRefPtr runnable = new NotifyRunnable(this);
- if (NS_WARN_IF(NS_FAILED(NS_DispatchToMainThread(runnable.forget())))) {
- return;
- }
-
- mNotificationMainThreadRunnable = runnable;
+ NS_WARN_IF(NS_FAILED(NS_DispatchToMainThread(runnable.forget())));
}
void
@@ -2860,7 +2853,7 @@ ProcessedMediaStream::DestroyImpl()
MediaStream::DestroyImpl();
// The stream order is only important if there are connections, in which
// case MediaInputPort::Disconnect() called SetStreamOrderDirty().
- // MediaStreamGraphImpl::RemoveStream() will also call
+ // MediaStreamGraphImpl::RemoveStreamGraphThread() will also call
// SetStreamOrderDirty(), for other reasons.
}
@@ -3106,10 +3099,7 @@ SourceMediaStream*
MediaStreamGraph::CreateSourceStream(DOMMediaStream* aWrapper)
{
SourceMediaStream* stream = new SourceMediaStream(aWrapper);
- NS_ADDREF(stream);
- MediaStreamGraphImpl* graph = static_cast(this);
- stream->SetGraphImpl(graph);
- graph->AppendMessage(new CreateMessage(stream));
+ AddStream(stream);
return stream;
}
@@ -3117,10 +3107,7 @@ ProcessedMediaStream*
MediaStreamGraph::CreateTrackUnionStream(DOMMediaStream* aWrapper)
{
TrackUnionStream* stream = new TrackUnionStream(aWrapper);
- NS_ADDREF(stream);
- MediaStreamGraphImpl* graph = static_cast(this);
- stream->SetGraphImpl(graph);
- graph->AppendMessage(new CreateMessage(stream));
+ AddStream(stream);
return stream;
}
@@ -3128,54 +3115,17 @@ ProcessedMediaStream*
MediaStreamGraph::CreateAudioCaptureStream(DOMMediaStream* aWrapper)
{
AudioCaptureStream* stream = new AudioCaptureStream(aWrapper);
- NS_ADDREF(stream);
- MediaStreamGraphImpl* graph = static_cast(this);
- stream->SetGraphImpl(graph);
- graph->AppendMessage(new CreateMessage(stream));
+ AddStream(stream);
return stream;
}
-AudioNodeExternalInputStream*
-MediaStreamGraph::CreateAudioNodeExternalInputStream(AudioNodeEngine* aEngine, TrackRate aSampleRate)
+void
+MediaStreamGraph::AddStream(MediaStream* aStream)
{
- MOZ_ASSERT(NS_IsMainThread());
- if (!aSampleRate) {
- aSampleRate = aEngine->NodeMainThread()->Context()->SampleRate();
- }
- AudioNodeExternalInputStream* stream = new AudioNodeExternalInputStream(
- aEngine, aSampleRate, aEngine->NodeMainThread()->Context()->Id());
- NS_ADDREF(stream);
+ NS_ADDREF(aStream);
MediaStreamGraphImpl* graph = static_cast(this);
- stream->SetGraphImpl(graph);
- graph->AppendMessage(new CreateMessage(stream));
- return stream;
-}
-
-AudioNodeStream*
-MediaStreamGraph::CreateAudioNodeStream(AudioNodeEngine* aEngine,
- AudioNodeStreamKind aKind,
- TrackRate aSampleRate)
-{
- MOZ_ASSERT(NS_IsMainThread());
- if (!aSampleRate) {
- aSampleRate = aEngine->NodeMainThread()->Context()->SampleRate();
- }
- // MediaRecorders use an AudioNodeStream, but no AudioNode
- AudioNode* node = aEngine->NodeMainThread();
- dom::AudioContext::AudioContextId contextIdForStream = node ? node->Context()->Id() :
- NO_AUDIO_CONTEXT;
- AudioNodeStream* stream = new AudioNodeStream(aEngine, aKind, aSampleRate,
- contextIdForStream);
- NS_ADDREF(stream);
- MediaStreamGraphImpl* graph = static_cast(this);
- stream->SetGraphImpl(graph);
- if (aEngine->HasNode()) {
- stream->SetChannelMixingParametersImpl(aEngine->NodeMainThread()->ChannelCount(),
- aEngine->NodeMainThread()->ChannelCountModeValue(),
- aEngine->NodeMainThread()->ChannelInterpretationValue());
- }
- graph->AppendMessage(new CreateMessage(stream));
- return stream;
+ aStream->SetGraphImpl(graph);
+ graph->AppendMessage(new CreateMessage(aStream));
}
class GraphStartedRunnable final : public nsRunnable
diff --git a/dom/media/MediaStreamGraph.h b/dom/media/MediaStreamGraph.h
index 05d787cb1bbe..b81e7a21e4db 100644
--- a/dom/media/MediaStreamGraph.h
+++ b/dom/media/MediaStreamGraph.h
@@ -638,7 +638,6 @@ protected:
TimeVarying mExplicitBlockerCount;
nsTArray > mListeners;
nsTArray mMainThreadListeners;
- nsRefPtr mNotificationMainThreadRunnable;
nsTArray mDisabledTrackIDs;
// Precomputed blocking status (over GraphTime).
@@ -1241,23 +1240,11 @@ public:
* Create a stream that will mix all its audio input.
*/
ProcessedMediaStream* CreateAudioCaptureStream(DOMMediaStream* aWrapper);
- // Internal AudioNodeStreams can only pass their output to another
- // AudioNode, whereas external AudioNodeStreams can pass their output
- // to an nsAudioStream for playback.
- enum AudioNodeStreamKind { SOURCE_STREAM, INTERNAL_STREAM, EXTERNAL_STREAM };
- /**
- * Create a stream that will process audio for an AudioNode.
- * Takes ownership of aEngine. aSampleRate is the sampling rate used
- * for the stream. If 0 is passed, the sampling rate of the engine's
- * node will get used.
- */
- AudioNodeStream* CreateAudioNodeStream(AudioNodeEngine* aEngine,
- AudioNodeStreamKind aKind,
- TrackRate aSampleRate = 0);
- AudioNodeExternalInputStream*
- CreateAudioNodeExternalInputStream(AudioNodeEngine* aEngine,
- TrackRate aSampleRate = 0);
+ /**
+ * Add a new stream to the graph. Main thread.
+ */
+ void AddStream(MediaStream* aStream);
/* From the main thread, ask the MSG to send back an event when the graph
* thread is running, and audio is being processed. */
diff --git a/dom/media/MediaStreamGraphImpl.h b/dom/media/MediaStreamGraphImpl.h
index 16abc3889d38..a16a51e182ba 100644
--- a/dom/media/MediaStreamGraphImpl.h
+++ b/dom/media/MediaStreamGraphImpl.h
@@ -424,12 +424,12 @@ public:
/**
* Add aStream to the graph and initializes its graph-specific state.
*/
- void AddStream(MediaStream* aStream);
+ void AddStreamGraphThread(MediaStream* aStream);
/**
* Remove aStream from the graph. Ensures that pending messages about the
* stream back to the main thread are flushed.
*/
- void RemoveStream(MediaStream* aStream);
+ void RemoveStreamGraphThread(MediaStream* aStream);
/**
* Remove aPort from the graph and release it.
*/
diff --git a/dom/media/test/test_loop.html b/dom/media/test/test_loop.html
index 4424a17c3194..d657ac696592 100644
--- a/dom/media/test/test_loop.html
+++ b/dom/media/test/test_loop.html
@@ -11,7 +11,7 @@