Bug 1378123 - Make inner window track whether there is an active PeerConnection.
MozReview-Commit-ID: 98Hwhnxtt1T
--- a/dom/base/TimeoutManager.cpp
+++ b/dom/base/TimeoutManager.cpp
@@ -15,20 +15,16 @@
#include "nsITimeoutHandler.h"
#include "mozilla/dom/TabGroup.h"
#include "OrderedTimeoutIterator.h"
#include "TimeoutExecutor.h"
#include "TimeoutBudgetManager.h"
#include "mozilla/net/WebSocketEventService.h"
#include "mozilla/MediaManager.h"
-#ifdef MOZ_WEBRTC
-#include "IPeerConnection.h"
-#endif // MOZ_WEBRTC
-
using namespace mozilla;
using namespace mozilla::dom;
static LazyLogModule gLog("Timeout");
static int32_t gRunningTimeoutDepth = 0;
// The default shortest interval/timeout we permit
@@ -1238,36 +1234,22 @@ TimeoutManager::BudgetThrottlingEnabled(
}
// Check if we have active GetUserMedia
if (MediaManager::Exists() &&
MediaManager::Get()->IsWindowStillActive(mWindow.WindowID())) {
return false;
}
- bool active = false;
-#if 0
- // Check if we have active PeerConnections This doesn't actually
- // work, since we sometimes call IsActive from Resume, which in turn
- // is sometimes called from nsGlobalWindow::LeaveModalState. The
- // problem here is that LeaveModalState can be called with pending
- // exeptions on the js context, and the following call to
- // HasActivePeerConnection is a JS call, which will assert on that
- // exception. Also, calling JS is expensive so we should try to fix
- // this in some other way.
- nsCOMPtr<IPeerConnectionManager> pcManager =
- do_GetService(IPEERCONNECTION_MANAGER_CONTRACTID);
-
- if (pcManager && NS_SUCCEEDED(pcManager->HasActivePeerConnection(
- mWindow.WindowID(), &active)) &&
- active) {
+ // Check if we have active PeerConnection
+ if (mWindow.AsInner()->HasActivePeerConnections()) {
return false;
}
-#endif // MOZ_WEBRTC
+ bool active;
// Check if we have web sockets
RefPtr<WebSocketEventService> eventService = WebSocketEventService::Get();
if (eventService &&
NS_SUCCEEDED(eventService->HasListenerFor(mWindow.WindowID(), &active)) &&
active) {
return false;
}
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -267,19 +267,16 @@
#endif
#include "nsIPresShellInlines.h"
#include "mozilla/DocLoadingTimelineMarker.h"
#include "nsISpeculativeConnect.h"
#include "mozilla/MediaManager.h"
-#ifdef MOZ_WEBRTC
-#include "IPeerConnection.h"
-#endif // MOZ_WEBRTC
#include "nsIURIClassifier.h"
#include "mozilla/DocumentStyleRootIterator.h"
#include "mozilla/ServoRestyleManager.h"
using namespace mozilla;
using namespace mozilla::dom;
@@ -8742,25 +8739,18 @@ nsDocument::CanSavePresentation(nsIReque
// Check if we have active GetUserMedia use
if (MediaManager::Exists() && win &&
MediaManager::Get()->IsWindowStillActive(win->WindowID())) {
return false;
}
#ifdef MOZ_WEBRTC
// Check if we have active PeerConnections
- nsCOMPtr<IPeerConnectionManager> pcManager =
- do_GetService(IPEERCONNECTION_MANAGER_CONTRACTID);
-
- if (pcManager && win) {
- bool active;
- pcManager->HasActivePeerConnection(win->WindowID(), &active);
- if (active) {
- return false;
- }
+ if (win && win->HasActivePeerConnections()) {
+ return false;
}
#endif // MOZ_WEBRTC
// Don't save presentations for documents containing EME content, so that
// CDMs reliably shutdown upon user navigation.
if (ContainsEMEContent()) {
return false;
}
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -1014,17 +1014,17 @@ namespace dom {
extern uint64_t
NextWindowID();
} // namespace dom
} // namespace mozilla
template<class T>
nsPIDOMWindow<T>::nsPIDOMWindow(nsPIDOMWindowOuter *aOuterWindow)
: mFrameElement(nullptr), mDocShell(nullptr), mModalStateDepth(0),
- mMutationBits(0), mIsDocumentLoaded(false),
+ mMutationBits(0), mActivePeerConnections(0), mIsDocumentLoaded(false),
mIsHandlingResizeEvent(false), mIsInnerWindow(aOuterWindow != nullptr),
mMayHavePaintEventListener(false), mMayHaveTouchEventListener(false),
mMayHaveSelectionChangeEventListener(false),
mMayHaveMouseEnterLeaveEventListener(false),
mMayHavePointerEnterLeaveEventListener(false),
mInnerObjectsFreed(false),
mIsActive(false), mIsBackground(false),
mMediaSuspend(Preferences::GetBool("media.block-autoplay-until-in-foreground", true) ?
@@ -4373,16 +4373,34 @@ nsPIDOMWindowInner::Thaw()
}
void
nsPIDOMWindowInner::SyncStateFromParentWindow()
{
nsGlobalWindow::Cast(this)->SyncStateFromParentWindow();
}
+void
+nsPIDOMWindowInner::AddPeerConnection()
+{
+ nsGlobalWindow::Cast(this)->AddPeerConnection();
+}
+
+void
+nsPIDOMWindowInner::RemovePeerConnection()
+{
+ nsGlobalWindow::Cast(this)->RemovePeerConnection();
+}
+
+bool
+nsPIDOMWindowInner::HasActivePeerConnections()
+{
+ return nsGlobalWindow::Cast(this)->HasActivePeerConnections();
+}
+
bool
nsPIDOMWindowInner::IsPlayingAudio()
{
for (uint32_t i = 0; i < mAudioContexts.Length(); i++) {
if (mAudioContexts[i]->IsRunning()) {
return true;
}
}
@@ -12558,16 +12576,41 @@ nsGlobalWindow::SyncStateFromParentWindo
// Now apply only the number of Suspend() calls to reach the target
// suspend count after applying the Freeze() calls.
for (uint32_t i = 0; i < (parentSuspendDepth - parentFreezeDepth); ++i) {
Suspend();
}
}
+void
+nsGlobalWindow::AddPeerConnection()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(IsInnerWindow());
+ mActivePeerConnections++;
+}
+
+void
+nsGlobalWindow::RemovePeerConnection()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(IsInnerWindow());
+ MOZ_ASSERT(mActivePeerConnections);
+ mActivePeerConnections--;
+}
+
+bool
+nsGlobalWindow::HasActivePeerConnections()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(IsInnerWindow());
+ return mActivePeerConnections;
+}
+
template<typename Method>
void
nsGlobalWindow::CallOnChildren(Method aMethod)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(IsInnerWindow());
MOZ_ASSERT(AsInner()->IsCurrentInnerWindow());
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -400,16 +400,19 @@ public:
void Suspend();
void Resume();
virtual bool IsSuspended() const override;
void Freeze();
void Thaw();
virtual bool IsFrozen() const override;
void SyncStateFromParentWindow();
+ void AddPeerConnection();
+ void RemovePeerConnection();
+ bool HasActivePeerConnections();
virtual nsresult FireDelayedDOMEvents() override;
// Outer windows only.
virtual bool WouldReuseInnerWindow(nsIDocument* aNewDocument) override;
virtual void SetDocShell(nsIDocShell* aDocShell) override;
virtual void DetachFromDocShell() override;
--- a/dom/base/nsPIDOMWindow.h
+++ b/dom/base/nsPIDOMWindow.h
@@ -653,16 +653,18 @@ protected:
ServiceWorkerRegistrationTable;
ServiceWorkerRegistrationTable mServiceWorkerRegistrationTable;
uint32_t mModalStateDepth;
// These variables are only used on inner windows.
uint32_t mMutationBits;
+ uint32_t mActivePeerConnections;
+
bool mIsDocumentLoaded;
bool mIsHandlingResizeEvent;
bool mIsInnerWindow;
bool mMayHavePaintEventListener;
bool mMayHaveTouchEventListener;
bool mMayHaveSelectionChangeEventListener;
bool mMayHaveMouseEnterLeaveEventListener;
bool mMayHavePointerEnterLeaveEventListener;
@@ -883,16 +885,31 @@ public:
// calls.
void Freeze();
void Thaw();
// Apply the parent window's suspend, freeze, and modal state to the current
// window.
void SyncStateFromParentWindow();
+ /**
+ * Increment active peer connection count.
+ */
+ void AddPeerConnection();
+
+ /**
+ * Decrement active peer connection count.
+ */
+ void RemovePeerConnection();
+
+ /**
+ * Check whether the active peer connection count is non-zero.
+ */
+ bool HasActivePeerConnections();
+
bool IsPlayingAudio();
bool IsDocumentLoaded() const;
mozilla::dom::TimeoutManager& TimeoutManager();
bool IsRunningTimeout();
--- a/dom/media/PeerConnection.js
+++ b/dom/media/PeerConnection.js
@@ -114,21 +114,16 @@ class GlobalPCList {
this._list[winID] = this._list[winID].filter(
function(e, i, a) { return e.get() !== null; });
if (this._list[winID].length === 0) {
delete this._list[winID];
}
}
- hasActivePeerConnection(winID) {
- this.removeNullRefs(winID);
- return !!this._list[winID];
- }
-
handleGMPCrash(data) {
let broadcastPluginCrash = function(list, winID, pluginID, pluginName) {
if (list.hasOwnProperty(winID)) {
list[winID].forEach(function(pcref) {
let pc = pcref.get();
if (pc) {
pc._pc.pluginCrash(pluginID, pluginName);
}
@@ -209,18 +204,17 @@ class GlobalPCList {
_registerPeerConnectionLifecycleCallback(winID, cb) {
this._lifecycleobservers[winID] = cb;
}
}
setupPrototype(GlobalPCList, {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
Ci.nsIMessageListener,
- Ci.nsISupportsWeakReference,
- Ci.IPeerConnectionManager]),
+ Ci.nsISupportsWeakReference]),
classID: PC_MANAGER_CID,
_xpcom_factory: {
createInstance(outer, iid) {
if (outer) {
throw Cr.NS_ERROR_NO_AGGREGATION;
}
return _globalPCList.QueryInterface(iid);
}
--- a/dom/media/bridge/IPeerConnection.idl
+++ b/dom/media/bridge/IPeerConnection.idl
@@ -1,27 +1,14 @@
#include "nsIThread.idl"
#include "nsIDOMWindow.idl"
#include "nsIPropertyBag2.idl"
interface nsIDOMDataChannel;
-/*
- * Manager interface to PeerConnection.js so it is accessible from C++.
- */
-[scriptable, uuid(c2218bd2-2648-4701-8fa6-305d3379e9f8)]
-interface IPeerConnectionManager : nsISupports
-{
- boolean hasActivePeerConnection(in unsigned long innerWindowID);
-};
-
-%{C++
-#define IPEERCONNECTION_MANAGER_CONTRACTID "@mozilla.org/dom/peerconnectionmanager;1"
-%}
-
/* Do not confuse with nsIDOMRTCPeerConnection. This interface is purely for
* communication between the PeerConnection JS DOM binding and the C++
* implementation in SIPCC.
*
* See media/webrtc/signaling/include/PeerConnectionImpl.h
*/
[scriptable, uuid(d7dfe148-0416-446b-a128-66a7c71ae8d3)]
interface IPeerConnectionObserver : nsISupports
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
@@ -1,8 +1,9 @@
+
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <cstdlib>
#include <cerrno>
#include <deque>
#include <set>
@@ -310,25 +311,28 @@ PeerConnectionImpl::PeerConnectionImpl(c
, mMedia(nullptr)
, mUuidGen(MakeUnique<PCUuidGenerator>())
, mHaveConfiguredCodecs(false)
, mHaveDataStream(false)
, mAddCandidateErrorCount(0)
, mTrickle(true) // TODO(ekr@rtfm.com): Use pref
, mNegotiationNeeded(false)
, mPrivateWindow(false)
+ , mActiveOnWindow(false)
{
MOZ_ASSERT(NS_IsMainThread());
auto log = RLogConnector::CreateInstance();
if (aGlobal) {
mWindow = do_QueryInterface(aGlobal->GetAsSupports());
if (IsPrivateBrowsing(mWindow)) {
mPrivateWindow = true;
log->EnterPrivateMode();
}
+ mWindow->AddPeerConnection();
+ mActiveOnWindow = true;
}
CSFLogInfo(logTag, "%s: PeerConnectionImpl constructor for %s",
__FUNCTION__, mHandle.c_str());
STAMP_TIMECARD(mTimeCard, "Constructor Completed");
mAllowIceLoopback = Preferences::GetBool(
"media.peerconnection.ice.loopback", false);
mAllowIceLinkLocal = Preferences::GetBool(
"media.peerconnection.ice.link_local", false);
@@ -343,16 +347,24 @@ PeerConnectionImpl::~PeerConnectionImpl(
if (mTimeCard) {
STAMP_TIMECARD(mTimeCard, "Destructor Invoked");
print_timecard(mTimeCard);
destroy_timecard(mTimeCard);
mTimeCard = nullptr;
}
// This aborts if not on main thread (in Debug builds)
PC_AUTO_ENTER_API_CALL_NO_CHECK();
+ if (mWindow && mActiveOnWindow) {
+ mWindow->RemovePeerConnection();
+ // No code is supposed to observe the assignment below, but
+ // hopefully it makes looking at this object in a debugger
+ // make more sense.
+ mActiveOnWindow = false;
+ }
+
if (mPrivateWindow) {
auto * log = RLogConnector::GetInstance();
if (log) {
log->ExitPrivateMode();
}
mPrivateWindow = false;
}
if (PeerConnectionCtx::isActive()) {
@@ -3138,16 +3150,21 @@ PeerConnectionImpl::SetSignalingState_m(
if (mMaxSending[i] < sending[i]) {
mMaxSending[i] = sending[i];
}
}
}
if (mSignalingState == PCImplSignalingState::SignalingClosed) {
CloseInt();
+ // Uncount this connection as active on the inner window upon close.
+ if (mWindow && mActiveOnWindow) {
+ mWindow->RemovePeerConnection();
+ mActiveOnWindow = false;
+ }
}
RefPtr<PeerConnectionObserver> pco = do_QueryObjectReferent(mPCObserver);
if (!pco) {
return;
}
JSErrorResult rv;
pco->OnStateChange(PCObserverStateType::SignalingState, rv);
@@ -3390,16 +3407,22 @@ void PeerConnectionImpl::IceConnectionSt
if (mIceConnectionState == PCImplIceConnectionState::Connected ||
mIceConnectionState == PCImplIceConnectionState::Completed ||
mIceConnectionState == PCImplIceConnectionState::Failed) {
if (mMedia->IsIceRestarting()) {
FinalizeIceRestart();
}
}
+ // Uncount this connection as active on the inner window upon close.
+ if (mWindow && mActiveOnWindow && mIceConnectionState == PCImplIceConnectionState::Closed) {
+ mWindow->RemovePeerConnection();
+ mActiveOnWindow = false;
+ }
+
// Would be nice if we had a means of converting one of these dom enums
// to a string that wasn't almost as much text as this switch statement...
switch (mIceConnectionState) {
case PCImplIceConnectionState::New:
STAMP_TIMECARD(mTimeCard, "Ice state: new");
break;
case PCImplIceConnectionState::Checking:
// For telemetry
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h
@@ -784,16 +784,19 @@ private:
unsigned int mAddCandidateErrorCount;
bool mTrickle;
bool mNegotiationNeeded;
bool mPrivateWindow;
+ // Whether this PeerConnection is being counted as active by mWindow
+ bool mActiveOnWindow;
+
// storage for Telemetry data
uint16_t mMaxReceiving[SdpMediaSection::kMediaTypes];
uint16_t mMaxSending[SdpMediaSection::kMediaTypes];
// DTMF
struct DTMFState {
PeerConnectionImpl* mPeerConnectionImpl;
nsCOMPtr<nsITimer> mSendTimer;