mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-09 19:35:51 +00:00
Merge m-c to b2g-inbound. a=merge
This commit is contained in:
commit
b209ef93af
@ -2,21 +2,17 @@
|
||||
* 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/. */
|
||||
|
||||
body, html {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
max-width: 320px;
|
||||
position: relative;
|
||||
margin: 0px auto;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#header {
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
padding: 6px 9px;
|
||||
padding: 4px 13px;
|
||||
display: -moz-box;
|
||||
vertical-align: top;
|
||||
}
|
||||
@ -38,22 +34,13 @@ body {
|
||||
}
|
||||
|
||||
#main {
|
||||
margin: 0 10px 10px 10px;
|
||||
margin: 0 14px 10px 14px;
|
||||
box-sizing: border-box;
|
||||
width: calc(100% - 2 * 10px);
|
||||
width: calc(100% - 2 * 14px);
|
||||
position: absolute;
|
||||
border-width: 1px;
|
||||
}
|
||||
|
||||
@media (min-width: 320px) {
|
||||
body {
|
||||
position: absolute;
|
||||
width: 320px;
|
||||
left: -160px;
|
||||
margin-left: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
#content,
|
||||
#borders {
|
||||
border-width: 1px;
|
||||
@ -194,3 +181,83 @@ body.dim > #main > p,
|
||||
body.dim > #main > .tooltip {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
@media (max-height: 228px) {
|
||||
#header {
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
#margins,
|
||||
#padding {
|
||||
border-width: 21px;
|
||||
}
|
||||
#borders {
|
||||
padding: 21px;
|
||||
}
|
||||
|
||||
#content {
|
||||
height: 21px;
|
||||
}
|
||||
|
||||
.padding.top {
|
||||
top: 46px;
|
||||
}
|
||||
|
||||
.padding.bottom {
|
||||
bottom: 46px;
|
||||
}
|
||||
|
||||
.border.top {
|
||||
top: 25px;
|
||||
}
|
||||
|
||||
.border.bottom {
|
||||
bottom: 25px;
|
||||
}
|
||||
|
||||
.margin.top {
|
||||
top: 4px;
|
||||
}
|
||||
|
||||
.margin.bottom {
|
||||
bottom: 4px;
|
||||
}
|
||||
|
||||
.size,
|
||||
.margin.left,
|
||||
.margin.right,
|
||||
.border.left,
|
||||
.border.right,
|
||||
.padding.left,
|
||||
.padding.right {
|
||||
line-height: 106px;
|
||||
}
|
||||
|
||||
.margin.right,
|
||||
.margin.left,
|
||||
.border.left,
|
||||
.border.right,
|
||||
.padding.right,
|
||||
.padding.left {
|
||||
width: 21px;
|
||||
}
|
||||
|
||||
.padding.left {
|
||||
left: 43px;
|
||||
}
|
||||
|
||||
.padding.right {
|
||||
right: 43px;
|
||||
}
|
||||
|
||||
.border.left {
|
||||
left: 22px;
|
||||
}
|
||||
|
||||
.border.right {
|
||||
right: 22px;
|
||||
}
|
||||
}
|
||||
|
@ -94,15 +94,6 @@ function test() {
|
||||
is(fragment.textContent, "#F06");
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "border-top-left-color",
|
||||
value: "rgba(14, 255, 20, .5)",
|
||||
test: fragment => {
|
||||
is(countAll(fragment), 1);
|
||||
is(countColors(fragment), 1);
|
||||
is(fragment.textContent, "rgba(14, 255, 20, .5)");
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "border",
|
||||
value: "80em dotted pink",
|
||||
|
@ -8877,12 +8877,14 @@ nsDocument::OnPageHide(bool aPersisted,
|
||||
|
||||
// Dispatch observer notification to notify observers page is hidden.
|
||||
nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
|
||||
nsIPrincipal *principal = GetPrincipal();
|
||||
os->NotifyObservers(static_cast<nsIDocument*>(this),
|
||||
nsContentUtils::IsSystemPrincipal(principal) ?
|
||||
"chrome-page-hidden" :
|
||||
"content-page-hidden",
|
||||
nullptr);
|
||||
if (os) {
|
||||
nsIPrincipal* principal = GetPrincipal();
|
||||
os->NotifyObservers(static_cast<nsIDocument*>(this),
|
||||
nsContentUtils::IsSystemPrincipal(principal) ?
|
||||
"chrome-page-hidden" :
|
||||
"content-page-hidden",
|
||||
nullptr);
|
||||
}
|
||||
|
||||
DispatchPageTransition(target, NS_LITERAL_STRING("pagehide"), aPersisted);
|
||||
|
||||
|
@ -11,9 +11,10 @@
|
||||
#include "nsWrapperCache.h"
|
||||
#include "StreamBuffer.h"
|
||||
#include "nsIDOMWindow.h"
|
||||
#include "nsIPrincipal.h"
|
||||
#include "mozilla/PeerIdentity.h"
|
||||
|
||||
class nsXPCClassInfo;
|
||||
class nsIPrincipal;
|
||||
|
||||
// GetCurrentTime is defined in winbase.h as zero argument macro forwarding to
|
||||
// GetTickCount() and conflicts with NS_DECL_NSIDOMMEDIASTREAM, containing
|
||||
@ -95,6 +96,17 @@ public:
|
||||
*/
|
||||
nsIPrincipal* GetPrincipal() { return mPrincipal; }
|
||||
|
||||
/**
|
||||
* These are used in WebRTC. A peerIdentity constrained MediaStream cannot be sent
|
||||
* across the network to anything other than a peer with the provided identity.
|
||||
* If this is set, then mPrincipal should be an instance of nsNullPrincipal.
|
||||
*/
|
||||
PeerIdentity* GetPeerIdentity() const { return mPeerIdentity; }
|
||||
void SetPeerIdentity(PeerIdentity* aPeerIdentity)
|
||||
{
|
||||
mPeerIdentity = aPeerIdentity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that data will be contributed to this stream from origin aPrincipal.
|
||||
* If aPrincipal is null, this is ignored. Otherwise, from now on the contents
|
||||
@ -103,6 +115,13 @@ public:
|
||||
*/
|
||||
bool CombineWithPrincipal(nsIPrincipal* aPrincipal);
|
||||
|
||||
/**
|
||||
* This is used in WebRTC to move from a protected state (nsNullPrincipal) to
|
||||
* one where the stream is accessible to script. Don't call this.
|
||||
* CombineWithPrincipal is almost certainly more appropriate.
|
||||
*/
|
||||
void SetPrincipal(nsIPrincipal* aPrincipal) { mPrincipal = aPrincipal; }
|
||||
|
||||
/**
|
||||
* Called when this stream's MediaStreamGraph has been shut down. Normally
|
||||
* MSGs are only shut down when all streams have been removed, so this
|
||||
@ -200,6 +219,9 @@ protected:
|
||||
// Principal identifying who may access the contents of this stream.
|
||||
// If null, this stream can be used by anyone because it has no content yet.
|
||||
nsCOMPtr<nsIPrincipal> mPrincipal;
|
||||
// this is used in gUM and WebRTC to identify peers that this stream
|
||||
// is allowed to be sent to
|
||||
nsAutoPtr<PeerIdentity> mPeerIdentity;
|
||||
|
||||
nsAutoTArray<nsRefPtr<MediaStreamTrack>,2> mTracks;
|
||||
nsRefPtr<StreamListener> mListener;
|
||||
|
84
content/media/webrtc/PeerIdentity.cpp
Normal file
84
content/media/webrtc/PeerIdentity.cpp
Normal file
@ -0,0 +1,84 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* vim: sw=2 ts=2 sts=2 expandtab
|
||||
* 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 "PeerIdentity.h"
|
||||
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIIDNService.h"
|
||||
#include "nsNetCID.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
bool
|
||||
PeerIdentity::Equals(const PeerIdentity& aOther) const
|
||||
{
|
||||
return Equals(aOther.mPeerIdentity);
|
||||
}
|
||||
|
||||
bool
|
||||
PeerIdentity::Equals(const nsAString& aOtherString) const
|
||||
{
|
||||
nsString user;
|
||||
GetUser(mPeerIdentity, user);
|
||||
nsString otherUser;
|
||||
GetUser(aOtherString, otherUser);
|
||||
if (user != otherUser) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsString host;
|
||||
GetHost(mPeerIdentity, host);
|
||||
nsString otherHost;
|
||||
GetHost(aOtherString, otherHost);
|
||||
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIIDNService> idnService
|
||||
= do_GetService("@mozilla.org/network/idn-service;1", &rv);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return host == otherHost;
|
||||
}
|
||||
|
||||
nsCString normHost;
|
||||
GetNormalizedHost(idnService, host, normHost);
|
||||
nsCString normOtherHost;
|
||||
GetNormalizedHost(idnService, otherHost, normOtherHost);
|
||||
return normHost == normOtherHost;
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
PeerIdentity::GetUser(const nsAString& aPeerIdentity, nsAString& aUser)
|
||||
{
|
||||
int32_t at = aPeerIdentity.FindChar('@');
|
||||
if (at >= 0) {
|
||||
aUser = Substring(aPeerIdentity, 0, at);
|
||||
} else {
|
||||
aUser.Truncate();
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
PeerIdentity::GetHost(const nsAString& aPeerIdentity, nsAString& aHost)
|
||||
{
|
||||
int32_t at = aPeerIdentity.FindChar('@');
|
||||
if (at >= 0) {
|
||||
aHost = Substring(aPeerIdentity, at + 1);
|
||||
} else {
|
||||
aHost = aPeerIdentity;
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
PeerIdentity::GetNormalizedHost(const nsCOMPtr<nsIIDNService>& aIdnService,
|
||||
const nsAString& aHost,
|
||||
nsACString& aNormalizedHost)
|
||||
{
|
||||
nsCString chost = NS_ConvertUTF16toUTF8(aHost);
|
||||
nsresult rv = aIdnService->ConvertUTF8toACE(chost, aNormalizedHost);
|
||||
NS_WARN_IF(NS_FAILED(rv));
|
||||
}
|
||||
|
||||
} /* namespace mozilla */
|
79
content/media/webrtc/PeerIdentity.h
Normal file
79
content/media/webrtc/PeerIdentity.h
Normal file
@ -0,0 +1,79 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* vim: sw=2 ts=2 sts=2 expandtab
|
||||
* 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/. */
|
||||
|
||||
#ifndef PeerIdentity_h
|
||||
#define PeerIdentity_h
|
||||
|
||||
#ifdef MOZILLA_INTERNAL_API
|
||||
#include "nsString.h"
|
||||
#else
|
||||
#include "nsStringAPI.h"
|
||||
#endif
|
||||
|
||||
template <class T> class nsCOMPtr;
|
||||
class nsIIDNService;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
/**
|
||||
* This class implements the identifier used in WebRTC identity. Peers are
|
||||
* identified using a string in the form [<user>@]<domain>, for instance,
|
||||
* "user@example.com'. The (optional) user portion is a site-controlled string
|
||||
* containing any character other than '@'. The domain portion is a valid IDN
|
||||
* domain name and is compared accordingly.
|
||||
*
|
||||
* See: http://tools.ietf.org/html/draft-ietf-rtcweb-security-arch-09#section-5.6.5.3.3.1
|
||||
*/
|
||||
class PeerIdentity MOZ_FINAL
|
||||
{
|
||||
public:
|
||||
PeerIdentity(const nsAString& aPeerIdentity)
|
||||
: mPeerIdentity(aPeerIdentity) {}
|
||||
~PeerIdentity() {}
|
||||
|
||||
bool Equals(const PeerIdentity& aOther) const;
|
||||
bool Equals(const nsAString& aOtherString) const;
|
||||
const nsString& ToString() const { return mPeerIdentity; }
|
||||
|
||||
private:
|
||||
static void GetUser(const nsAString& aPeerIdentity, nsAString& aUser);
|
||||
static void GetHost(const nsAString& aPeerIdentity, nsAString& aHost);
|
||||
|
||||
static void GetNormalizedHost(const nsCOMPtr<nsIIDNService>& aIdnService,
|
||||
const nsAString& aHost,
|
||||
nsACString& aNormalizedHost);
|
||||
|
||||
nsString mPeerIdentity;
|
||||
};
|
||||
|
||||
inline bool
|
||||
operator==(const PeerIdentity& aOne, const PeerIdentity& aTwo)
|
||||
{
|
||||
return aOne.Equals(aTwo);
|
||||
}
|
||||
|
||||
inline bool
|
||||
operator==(const PeerIdentity& aOne, const nsAString& aString)
|
||||
{
|
||||
return aOne.Equals(aString);
|
||||
}
|
||||
|
||||
inline bool
|
||||
operator!=(const PeerIdentity& aOne, const PeerIdentity& aTwo)
|
||||
{
|
||||
return !aOne.Equals(aTwo);
|
||||
}
|
||||
|
||||
inline bool
|
||||
operator!=(const PeerIdentity& aOne, const nsAString& aString)
|
||||
{
|
||||
return !aOne.Equals(aString);
|
||||
}
|
||||
|
||||
|
||||
} /* namespace mozilla */
|
||||
|
||||
#endif /* PeerIdentity_h */
|
@ -48,6 +48,11 @@ XPIDL_SOURCES += [
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'MediaEngineDefault.cpp',
|
||||
'PeerIdentity.cpp',
|
||||
]
|
||||
|
||||
EXPORTS.mozilla += [
|
||||
'PeerIdentity.h',
|
||||
]
|
||||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
@ -3076,6 +3076,17 @@ nsDOMWindowUtils::CheckAndClearPaintedState(nsIDOMElement* aElement, bool* aResu
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Get the outermost frame for the content node, so that we can test
|
||||
// canvasframe invalidations by observing the documentElement.
|
||||
for (;;) {
|
||||
nsIFrame* parentFrame = frame->GetParent();
|
||||
if (parentFrame && parentFrame->GetContent() == content) {
|
||||
frame = parentFrame;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
*aResult = frame->CheckAndClearPaintedState();
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -90,7 +90,7 @@ NS_IMPL_CYCLE_COLLECTING_ADDREF(Touch)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(Touch)
|
||||
|
||||
EventTarget*
|
||||
Touch::Target() const
|
||||
Touch::GetTarget() const
|
||||
{
|
||||
nsCOMPtr<nsIContent> content = do_QueryInterface(mTarget);
|
||||
if (content && content->ChromeOnlyAccess() &&
|
||||
|
@ -62,7 +62,7 @@ public:
|
||||
|
||||
// WebIDL
|
||||
int32_t Identifier() const { return mIdentifier; }
|
||||
EventTarget* Target() const;
|
||||
EventTarget* GetTarget() const;
|
||||
int32_t ScreenX() const { return mScreenPoint.x; }
|
||||
int32_t ScreenY() const { return mScreenPoint.y; }
|
||||
int32_t ClientX() const { return mClientPoint.x; }
|
||||
|
@ -1980,7 +1980,7 @@ TabChild::RecvRealTouchEvent(const WidgetTouchEvent& aEvent,
|
||||
}
|
||||
|
||||
if (aEvent.message == NS_TOUCH_START && localEvent.touches.Length() > 0) {
|
||||
mActiveElementManager->SetTargetElement(localEvent.touches[0]->Target());
|
||||
mActiveElementManager->SetTargetElement(localEvent.touches[0]->GetTarget());
|
||||
}
|
||||
|
||||
nsCOMPtr<nsPIDOMWindow> outerWindow = do_GetInterface(WebNavigation());
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include "nsISupportsPrimitives.h"
|
||||
#include "nsIInterfaceRequestorUtils.h"
|
||||
#include "mozilla/Types.h"
|
||||
#include "mozilla/PeerIdentity.h"
|
||||
#include "mozilla/dom/ContentChild.h"
|
||||
#include "mozilla/dom/MediaStreamBinding.h"
|
||||
#include "mozilla/dom/MediaStreamTrackBinding.h"
|
||||
@ -39,6 +40,8 @@
|
||||
#include "nsDOMFile.h"
|
||||
#include "nsGlobalWindow.h"
|
||||
|
||||
#include "mozilla/Preferences.h"
|
||||
|
||||
/* Using WebRTC backend on Desktops (Mac, Windows, Linux), otherwise default */
|
||||
#include "MediaEngineDefault.h"
|
||||
#if defined(MOZ_WEBRTC)
|
||||
@ -494,11 +497,13 @@ public:
|
||||
uint64_t aWindowID,
|
||||
GetUserMediaCallbackMediaStreamListener* aListener,
|
||||
MediaEngineSource* aAudioSource,
|
||||
MediaEngineSource* aVideoSource)
|
||||
MediaEngineSource* aVideoSource,
|
||||
PeerIdentity* aPeerIdentity)
|
||||
: mAudioSource(aAudioSource)
|
||||
, mVideoSource(aVideoSource)
|
||||
, mWindowID(aWindowID)
|
||||
, mListener(aListener)
|
||||
, mPeerIdentity(aPeerIdentity)
|
||||
, mManager(MediaManager::GetInstance())
|
||||
{
|
||||
mSuccess.swap(aSuccess);
|
||||
@ -621,9 +626,16 @@ public:
|
||||
reinterpret_cast<uint64_t>(stream.get()),
|
||||
reinterpret_cast<int64_t>(trackunion->GetStream()));
|
||||
|
||||
trackunion->CombineWithPrincipal(window->GetExtantDoc()->NodePrincipal());
|
||||
nsCOMPtr<nsIPrincipal> principal;
|
||||
if (mPeerIdentity) {
|
||||
principal = do_CreateInstance("@mozilla.org/nullprincipal;1");
|
||||
trackunion->SetPeerIdentity(mPeerIdentity.forget());
|
||||
} else {
|
||||
principal = window->GetExtantDoc()->NodePrincipal();
|
||||
}
|
||||
trackunion->CombineWithPrincipal(principal);
|
||||
|
||||
// The listener was added at the begining in an inactive state.
|
||||
// The listener was added at the beginning in an inactive state.
|
||||
// Activate our listener. We'll call Start() on the source when get a callback
|
||||
// that the MediaStream has started consuming. The listener is freed
|
||||
// when the page is invalidated (on navigation or close).
|
||||
@ -662,6 +674,7 @@ private:
|
||||
nsRefPtr<MediaEngineSource> mVideoSource;
|
||||
uint64_t mWindowID;
|
||||
nsRefPtr<GetUserMediaCallbackMediaStreamListener> mListener;
|
||||
nsAutoPtr<PeerIdentity> mPeerIdentity;
|
||||
nsRefPtr<MediaManager> mManager; // get ref to this when creating the runnable
|
||||
};
|
||||
|
||||
@ -1055,9 +1068,14 @@ public:
|
||||
return;
|
||||
}
|
||||
}
|
||||
PeerIdentity* peerIdentity = nullptr;
|
||||
if (!mConstraints.mPeerIdentity.IsEmpty()) {
|
||||
peerIdentity = new PeerIdentity(mConstraints.mPeerIdentity);
|
||||
}
|
||||
|
||||
NS_DispatchToMainThread(new GetUserMediaStreamRunnable(
|
||||
mSuccess, mError, mWindowID, mListener, aAudioSource, aVideoSource
|
||||
mSuccess, mError, mWindowID, mListener, aAudioSource, aVideoSource,
|
||||
peerIdentity
|
||||
));
|
||||
|
||||
MOZ_ASSERT(!mSuccess);
|
||||
@ -1296,8 +1314,8 @@ MediaManager::NotifyRecordingStatusChange(nsPIDOMWindow* aWindow,
|
||||
props->SetPropertyAsAString(NS_LITERAL_STRING("requestURL"), requestURL);
|
||||
|
||||
obs->NotifyObservers(static_cast<nsIPropertyBag2*>(props),
|
||||
"recording-device-events",
|
||||
aMsg.get());
|
||||
"recording-device-events",
|
||||
aMsg.get());
|
||||
|
||||
// Forward recording events to parent process.
|
||||
// The events are gathered in chrome process and used for recording indicator
|
||||
@ -1676,8 +1694,7 @@ MediaManager::RemoveFromWindowList(uint64_t aWindowID,
|
||||
// Notify the UI that this window no longer has gUM active
|
||||
char windowBuffer[32];
|
||||
PR_snprintf(windowBuffer, sizeof(windowBuffer), "%llu", outerID);
|
||||
nsAutoString data;
|
||||
data.Append(NS_ConvertUTF8toUTF16(windowBuffer));
|
||||
nsString data = NS_ConvertUTF8toUTF16(windowBuffer);
|
||||
|
||||
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
|
||||
obs->NotifyObservers(nullptr, "recording-window-ended", data.get());
|
||||
|
@ -291,8 +291,8 @@ RTCPeerConnection.prototype = {
|
||||
this._trickleIce = Services.prefs.getBoolPref("media.peerconnection.trickle_ice");
|
||||
if (!rtcConfig.iceServers ||
|
||||
!Services.prefs.getBoolPref("media.peerconnection.use_document_iceservers")) {
|
||||
rtcConfig = {iceServers:
|
||||
JSON.parse(Services.prefs.getCharPref("media.peerconnection.default_iceservers"))};
|
||||
rtcConfig.iceServers =
|
||||
JSON.parse(Services.prefs.getCharPref("media.peerconnection.default_iceservers"));
|
||||
}
|
||||
this._mustValidateRTCConfiguration(rtcConfig,
|
||||
"RTCPeerConnection constructor passed invalid RTCConfiguration");
|
||||
@ -347,6 +347,19 @@ RTCPeerConnection.prototype = {
|
||||
return this._pc;
|
||||
},
|
||||
|
||||
callCB: function(callback, arg) {
|
||||
if (callback) {
|
||||
try {
|
||||
callback(arg);
|
||||
} catch(e) {
|
||||
// A content script (user-provided) callback threw an error. We don't
|
||||
// want this to take down peerconnection, but we still want the user
|
||||
// to see it, so we catch it, report it, and move on.
|
||||
this.logErrorAndCallOnError(e.message, e.fileName, e.lineNumber);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_initIdp: function() {
|
||||
let prefName = "media.peerconnection.identity.timeout";
|
||||
let idpTimeout = Services.prefs.getIntPref(prefName);
|
||||
@ -656,15 +669,6 @@ RTCPeerConnection.prototype = {
|
||||
"Invalid type " + desc.type + " provided to setRemoteDescription");
|
||||
}
|
||||
|
||||
try {
|
||||
let processIdentity = this._processIdentity.bind(this);
|
||||
this._remoteIdp.verifyIdentityFromSDP(desc.sdp, processIdentity);
|
||||
} catch (e) {
|
||||
this.logWarning(e.message, e.fileName, e.lineNumber);
|
||||
// only happens if processing the SDP for identity doesn't work
|
||||
// let _setRemoteDescription do the error reporting
|
||||
}
|
||||
|
||||
this._queueOrRun({
|
||||
func: this._setRemoteDescription,
|
||||
args: [type, desc.sdp, onSuccess, onError],
|
||||
@ -673,18 +677,84 @@ RTCPeerConnection.prototype = {
|
||||
});
|
||||
},
|
||||
|
||||
_processIdentity: function(message) {
|
||||
if (message) {
|
||||
/**
|
||||
* Takes a result from the IdP and checks it against expectations.
|
||||
* If OK, generates events.
|
||||
* Returns true if it is either present and valid, or if there is no
|
||||
* need for identity.
|
||||
*/
|
||||
_processIdpResult: function(message) {
|
||||
let good = !!message;
|
||||
// This might be a valid assertion, but if we are constrained to a single peer
|
||||
// identity, then we also need to make sure that the assertion matches
|
||||
if (good && this._impl.peerIdentity) {
|
||||
good = (message.identity === this._impl.peerIdentity);
|
||||
}
|
||||
if (good) {
|
||||
this._impl.peerIdentity = message.identity;
|
||||
this._peerIdentity = new this._win.RTCIdentityAssertion(
|
||||
this._remoteIdp.provider, message.identity);
|
||||
|
||||
let args = { peerIdentity: this._peerIdentity };
|
||||
this._remoteIdp.provider, message.identity);
|
||||
this.dispatchEvent(new this._win.Event("peeridentity"));
|
||||
}
|
||||
return good;
|
||||
},
|
||||
|
||||
_setRemoteDescription: function(type, sdp, onSuccess, onError) {
|
||||
this._onSetRemoteDescriptionSuccess = onSuccess;
|
||||
let idpComplete = false;
|
||||
let setRemoteComplete = false;
|
||||
let idpError = null;
|
||||
|
||||
// we can run the IdP validation in parallel with setRemoteDescription this
|
||||
// complicates much more than would be ideal, but it ensures that the IdP
|
||||
// doesn't hold things up too much when it's not on the critical path
|
||||
let allDone = () => {
|
||||
if (!setRemoteComplete || !idpComplete || !onSuccess) {
|
||||
return;
|
||||
}
|
||||
this._remoteType = this._pendingType;
|
||||
this._pendingType = null;
|
||||
this.callCB(onSuccess);
|
||||
onSuccess = null;
|
||||
this._executeNext();
|
||||
};
|
||||
|
||||
let setRemoteDone = () => {
|
||||
setRemoteComplete = true;
|
||||
allDone();
|
||||
};
|
||||
|
||||
// If we aren't waiting for something specific, allow this
|
||||
// to complete asynchronously.
|
||||
let idpDone;
|
||||
if (!this._impl.peerIdentity) {
|
||||
idpDone = this._processIdpResult.bind(this);
|
||||
idpComplete = true; // lie about this for allDone()
|
||||
} else {
|
||||
idpDone = message => {
|
||||
let idpGood = this._processIdpResult(message);
|
||||
if (!idpGood) {
|
||||
// iff we are waiting for a very specific peerIdentity
|
||||
// call the error callback directly and then close
|
||||
idpError = "Peer Identity mismatch, expected: " +
|
||||
this._impl.peerIdentity;
|
||||
this.callCB(onError, idpError);
|
||||
this.close();
|
||||
} else {
|
||||
idpComplete = true;
|
||||
allDone();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
this._remoteIdp.verifyIdentityFromSDP(sdp, idpDone);
|
||||
} catch (e) {
|
||||
// if processing the SDP for identity doesn't work
|
||||
this.logWarning(e.message, e.fileName, e.lineNumber);
|
||||
idpDone(null);
|
||||
}
|
||||
|
||||
this._onSetRemoteDescriptionSuccess = setRemoteDone;
|
||||
this._onSetRemoteDescriptionFailure = onError;
|
||||
this._impl.setRemoteDescription(type, sdp);
|
||||
},
|
||||
@ -703,14 +773,14 @@ RTCPeerConnection.prototype = {
|
||||
getIdentityAssertion: function() {
|
||||
this._checkClosed();
|
||||
|
||||
function gotAssertion(assertion) {
|
||||
var gotAssertion = assertion => {
|
||||
if (assertion) {
|
||||
this._gotIdentityAssertion(assertion);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this._localIdp.getIdentityAssertion(this._impl.fingerprint,
|
||||
gotAssertion.bind(this));
|
||||
gotAssertion);
|
||||
},
|
||||
|
||||
updateIce: function(config, constraints) {
|
||||
@ -964,21 +1034,6 @@ PeerConnectionObserver.prototype = {
|
||||
this._dompc.dispatchEvent(event);
|
||||
},
|
||||
|
||||
callCB: function(callback, arg) {
|
||||
if (callback) {
|
||||
try {
|
||||
callback(arg);
|
||||
} catch(e) {
|
||||
// A content script (user-provided) callback threw an error. We don't
|
||||
// want this to take down peerconnection, but we still want the user
|
||||
// to see it, so we catch it, report it, and move on.
|
||||
this._dompc.logErrorAndCallOnError(e.message,
|
||||
e.fileName,
|
||||
e.lineNumber);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
onCreateOfferSuccess: function(sdp) {
|
||||
let pc = this._dompc;
|
||||
let fp = pc._impl.fingerprint;
|
||||
@ -986,15 +1041,15 @@ PeerConnectionObserver.prototype = {
|
||||
if (assertion) {
|
||||
pc._gotIdentityAssertion(assertion);
|
||||
}
|
||||
this.callCB(pc._onCreateOfferSuccess,
|
||||
new pc._win.mozRTCSessionDescription({ type: "offer",
|
||||
sdp: sdp }));
|
||||
pc.callCB(pc._onCreateOfferSuccess,
|
||||
new pc._win.mozRTCSessionDescription({ type: "offer",
|
||||
sdp: sdp }));
|
||||
pc._executeNext();
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
onCreateOfferError: function(code, message) {
|
||||
this.callCB(this._dompc._onCreateOfferFailure, new RTCError(code, message));
|
||||
this._dompc.callCB(this._dompc._onCreateOfferFailure, new RTCError(code, message));
|
||||
this._dompc._executeNext();
|
||||
},
|
||||
|
||||
@ -1005,22 +1060,23 @@ PeerConnectionObserver.prototype = {
|
||||
if (assertion) {
|
||||
pc._gotIdentityAssertion(assertion);
|
||||
}
|
||||
this.callCB (pc._onCreateAnswerSuccess,
|
||||
new pc._win.mozRTCSessionDescription({ type: "answer",
|
||||
sdp: sdp }));
|
||||
pc.callCB(pc._onCreateAnswerSuccess,
|
||||
new pc._win.mozRTCSessionDescription({ type: "answer",
|
||||
sdp: sdp }));
|
||||
pc._executeNext();
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
onCreateAnswerError: function(code, message) {
|
||||
this.callCB(this._dompc._onCreateAnswerFailure, new RTCError(code, message));
|
||||
this._dompc.callCB(this._dompc._onCreateAnswerFailure,
|
||||
new RTCError(code, message));
|
||||
this._dompc._executeNext();
|
||||
},
|
||||
|
||||
onSetLocalDescriptionSuccess: function() {
|
||||
this._dompc._localType = this._dompc._pendingType;
|
||||
this._dompc._pendingType = null;
|
||||
this.callCB(this._dompc._onSetLocalDescriptionSuccess);
|
||||
this._dompc.callCB(this._dompc._onSetLocalDescriptionSuccess);
|
||||
|
||||
if (this._dompc._iceGatheringState == "complete") {
|
||||
// If we are not trickling or we completed gathering prior
|
||||
@ -1032,35 +1088,33 @@ PeerConnectionObserver.prototype = {
|
||||
},
|
||||
|
||||
onSetRemoteDescriptionSuccess: function() {
|
||||
this._dompc._remoteType = this._dompc._pendingType;
|
||||
this._dompc._pendingType = null;
|
||||
this.callCB(this._dompc._onSetRemoteDescriptionSuccess);
|
||||
this._dompc._executeNext();
|
||||
this._dompc._onSetRemoteDescriptionSuccess();
|
||||
},
|
||||
|
||||
onSetLocalDescriptionError: function(code, message) {
|
||||
this._dompc._pendingType = null;
|
||||
this.callCB(this._dompc._onSetLocalDescriptionFailure,
|
||||
new RTCError(code, message));
|
||||
this._dompc.callCB(this._dompc._onSetLocalDescriptionFailure,
|
||||
new RTCError(code, message));
|
||||
this._dompc._executeNext();
|
||||
},
|
||||
|
||||
onSetRemoteDescriptionError: function(code, message) {
|
||||
this._dompc._pendingType = null;
|
||||
this.callCB(this._dompc._onSetRemoteDescriptionFailure,
|
||||
new RTCError(code, message));
|
||||
this._dompc.callCB(this._dompc._onSetRemoteDescriptionFailure,
|
||||
new RTCError(code, message));
|
||||
this._dompc._executeNext();
|
||||
},
|
||||
|
||||
onAddIceCandidateSuccess: function() {
|
||||
this._dompc._pendingType = null;
|
||||
this.callCB(this._dompc._onAddIceCandidateSuccess);
|
||||
this._dompc.callCB(this._dompc._onAddIceCandidateSuccess);
|
||||
this._dompc._executeNext();
|
||||
},
|
||||
|
||||
onAddIceCandidateError: function(code, message) {
|
||||
this._dompc._pendingType = null;
|
||||
this.callCB(this._dompc._onAddIceCandidateError, new RTCError(code, message));
|
||||
this._dompc.callCB(this._dompc._onAddIceCandidateError,
|
||||
new RTCError(code, message));
|
||||
this._dompc._executeNext();
|
||||
},
|
||||
|
||||
@ -1161,8 +1215,8 @@ PeerConnectionObserver.prototype = {
|
||||
onStateChange: function(state) {
|
||||
switch (state) {
|
||||
case "SignalingState":
|
||||
this.callCB(this._dompc.onsignalingstatechange,
|
||||
this._dompc.signalingState);
|
||||
this._dompc.callCB(this._dompc.onsignalingstatechange,
|
||||
this._dompc.signalingState);
|
||||
break;
|
||||
|
||||
case "IceConnectionState":
|
||||
@ -1196,18 +1250,20 @@ PeerConnectionObserver.prototype = {
|
||||
let webidlobj = this._dompc._win.RTCStatsReport._create(this._dompc._win,
|
||||
chromeobj);
|
||||
chromeobj.makeStatsPublic();
|
||||
this.callCB(this._dompc._onGetStatsSuccess, webidlobj);
|
||||
this._dompc.callCB(this._dompc._onGetStatsSuccess, webidlobj);
|
||||
this._dompc._executeNext();
|
||||
},
|
||||
|
||||
onGetStatsError: function(code, message) {
|
||||
this.callCB(this._dompc._onGetStatsFailure, new RTCError(code, message));
|
||||
this._dompc.callCB(this._dompc._onGetStatsFailure,
|
||||
new RTCError(code, message));
|
||||
this._dompc._executeNext();
|
||||
},
|
||||
|
||||
onAddStream: function(stream) {
|
||||
this.dispatchEvent(new this._dompc._win.MediaStreamEvent("addstream",
|
||||
{ stream: stream }));
|
||||
let ev = new this._dompc._win.MediaStreamEvent("addstream",
|
||||
{ stream: stream });
|
||||
this._dompc.dispatchEvent(ev);
|
||||
},
|
||||
|
||||
onRemoveStream: function(stream, type) {
|
||||
|
@ -60,6 +60,7 @@ FAIL_ON_WARNINGS = True
|
||||
LOCAL_INCLUDES += [
|
||||
'../base',
|
||||
'../camera',
|
||||
'/caps/include',
|
||||
]
|
||||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
@ -5,8 +5,8 @@ support-files =
|
||||
/.well-known/idp-proxy/idp-proxy.js
|
||||
identityevent.js
|
||||
|
||||
# All tests are disabled on android due to lack of https support in mochitest
|
||||
# (Bug 975149)
|
||||
# All tests are disabled on android&b2g due to lack of https support in
|
||||
# mochitests (Bug 907770)
|
||||
# All tests are disabled on b2g due to lack of e10s support in WebRTC identity
|
||||
# (Bug 975144)
|
||||
[test_idpproxy.html]
|
||||
@ -17,5 +17,7 @@ skip-if = os == "android" || appname == "b2g"
|
||||
skip-if = os == "android" || appname == "b2g"
|
||||
[test_setIdentityProviderWithErrors.html]
|
||||
skip-if = os == "android" || appname == "b2g"
|
||||
[test_peerConnection_peerIdentity.html]
|
||||
skip-if = os == "android" || appname == "b2g"
|
||||
[../mochitest/test_zmedia_cleanup.html]
|
||||
skip-if = os == "android" || appname == "b2g"
|
||||
|
@ -26,6 +26,7 @@ var test;
|
||||
function theTest() {
|
||||
test = new PeerConnectionTest();
|
||||
test.setMediaConstraints([{audio: true}], [{audio: true}]);
|
||||
test.chain.removeAfter('PC_REMOTE_CHECK_INITIAL_SIGNALINGSTATE');
|
||||
test.chain.append([
|
||||
[
|
||||
"GET_IDENTITY_ASSERTION_FAILS_WITHOUT_PROVIDER",
|
||||
|
@ -0,0 +1,90 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="../mochitest/head.js"></script>
|
||||
<script type="application/javascript" src="../mochitest/pc.js"></script>
|
||||
<script type="application/javascript" src="../mochitest/templates.js"></script>
|
||||
<script type="application/javascript" src="../mochitest/blacksilence.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="display"></div>
|
||||
<pre id="test">
|
||||
<script type="application/javascript">
|
||||
createHTML({
|
||||
title: "setIdentityProvider leads to peerIdentity and assertions in SDP"
|
||||
});
|
||||
|
||||
var test;
|
||||
function theTest() {
|
||||
var id1 = 'someone@test1.example.com';
|
||||
var id2 = 'someone@test2.example.com';
|
||||
test = new PeerConnectionTest({
|
||||
config_pc1: {
|
||||
peerIdentity: id2
|
||||
},
|
||||
config_pc2: {
|
||||
peerIdentity: id1
|
||||
}
|
||||
});
|
||||
test.setMediaConstraints([{
|
||||
audio: true,
|
||||
peerIdentity: id2
|
||||
}, {
|
||||
video: true,
|
||||
peerIdentity: id2
|
||||
}], [{
|
||||
audio: true,
|
||||
fake: true,
|
||||
peerIdentity: id1
|
||||
}, {
|
||||
video: true,
|
||||
fake: true,
|
||||
peerIdentity: id1
|
||||
}]);
|
||||
test.setIdentityProvider(test.pcLocal, 'test1.example.com', 'idp.html');
|
||||
test.setIdentityProvider(test.pcRemote, 'test2.example.com', 'idp.html');
|
||||
test.chain.append([
|
||||
[
|
||||
"PEER_IDENTITY_IS_SET_CORRECTLY",
|
||||
function(test) {
|
||||
// no need to wait to check identity in this case,
|
||||
// setRemoteDescription should wait for the IdP to complete
|
||||
function checkIdentity(pc, pfx, idp, name) {
|
||||
is(pc.peerIdentity.idp, idp, pfx + "IdP is correct");
|
||||
is(pc.peerIdentity.name, name + "@" + idp, pfx + "identity is correct");
|
||||
}
|
||||
|
||||
checkIdentity(test.pcLocal._pc, "local: ", "test2.example.com", "someone");
|
||||
checkIdentity(test.pcRemote._pc, "remote: ", "test1.example.com", "someone");
|
||||
test.next();
|
||||
}
|
||||
],
|
||||
[
|
||||
"REMOTE_STREAMS_ARE_RESTRICTED",
|
||||
function(test) {
|
||||
var remoteStream = test.pcLocal._pc.getRemoteStreams()[0];
|
||||
var oneDone = false;
|
||||
function done() {
|
||||
if (!oneDone) {
|
||||
oneDone = true;
|
||||
return;
|
||||
}
|
||||
test.next();
|
||||
}
|
||||
|
||||
audioIsSilence(true, remoteStream, done);
|
||||
videoIsBlack(true, remoteStream, done);
|
||||
}
|
||||
],
|
||||
]);
|
||||
test.run();
|
||||
}
|
||||
runTest(theTest);
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
114
dom/media/tests/mochitest/blacksilence.js
Normal file
114
dom/media/tests/mochitest/blacksilence.js
Normal file
@ -0,0 +1,114 @@
|
||||
(function(global) {
|
||||
'use strict';
|
||||
|
||||
// an invertible check on the condition.
|
||||
// if the constraint is applied, then the check is direct
|
||||
// if not applied, then the result should be reversed
|
||||
function check(constraintApplied, condition, message) {
|
||||
var good = constraintApplied ? condition : !condition;
|
||||
message = (constraintApplied ? 'with' : 'without') +
|
||||
' constraint: should ' + (constraintApplied ? '' : 'not ') +
|
||||
message + ' = ' + (good ? 'OK' : 'waiting...');
|
||||
info(message);
|
||||
return good;
|
||||
}
|
||||
|
||||
function isSilence(audioData) {
|
||||
var silence = true;
|
||||
for (var i = 0; i < audioData.length; ++i) {
|
||||
if (audioData[i] !== 128) {
|
||||
silence = false;
|
||||
}
|
||||
}
|
||||
return silence;
|
||||
}
|
||||
|
||||
function periodicCheck(type, checkFunc, successMessage, done) {
|
||||
var interval = setInterval(function periodic() {
|
||||
if (checkFunc()) {
|
||||
ok(true, type + ' is ' + successMessage);
|
||||
clearInterval(interval);
|
||||
interval = null;
|
||||
done();
|
||||
}
|
||||
}, 200);
|
||||
return function cancel() {
|
||||
if (interval) {
|
||||
ok(false, 'timed out waiting for audio check');
|
||||
clearInterval(interval);
|
||||
done();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function checkAudio(constraintApplied, stream, done) {
|
||||
var context = new AudioContext();
|
||||
var source = context.createMediaStreamSource(stream);
|
||||
var analyser = context.createAnalyser();
|
||||
source.connect(analyser);
|
||||
analyser.connect(context.destination);
|
||||
|
||||
function testAudio() {
|
||||
var sampleCount = analyser.frequencyBinCount;
|
||||
info('got some audio samples: ' + sampleCount);
|
||||
var bucket = new ArrayBuffer(sampleCount);
|
||||
var view = new Uint8Array(bucket);
|
||||
analyser.getByteTimeDomainData(view);
|
||||
|
||||
var silent = check(constraintApplied, isSilence(view), 'be silence for audio');
|
||||
// TODO: silence cross origin input to webaudio, bug 966066
|
||||
silent = constraintApplied ? !silent : silent;
|
||||
return sampleCount > 0 && silent;
|
||||
}
|
||||
return periodicCheck('audio', testAudio,
|
||||
(constraintApplied ? '' : 'not ') + 'silent', done);
|
||||
}
|
||||
|
||||
function mkElement(type) {
|
||||
var display = document.getElementById('display');
|
||||
var e = document.createElement(type);
|
||||
e.width = 32;
|
||||
e.height = 24;
|
||||
display.appendChild(e);
|
||||
return e;
|
||||
}
|
||||
|
||||
function checkVideo(constraintApplied, stream, done) {
|
||||
var video = mkElement('video');
|
||||
video.mozSrcObject = stream;
|
||||
|
||||
var ready = false;
|
||||
video.onplaying = function() {
|
||||
ready = true;
|
||||
}
|
||||
video.play();
|
||||
|
||||
function tryToRenderToCanvas() {
|
||||
if (!ready) {
|
||||
info('waiting for video to start');
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
// every try needs a new canvas, otherwise a taint from an earlier call
|
||||
// will affect subsequent calls
|
||||
var canvas = mkElement('canvas');
|
||||
var ctx = canvas.getContext('2d');
|
||||
// have to guard drawImage with the try as well, due to bug 879717
|
||||
// if we get an error, this round fails, but that failure is usually
|
||||
// just transitory
|
||||
ctx.drawImage(video, 0, 0);
|
||||
ctx.getImageData(0, 0, 1, 1);
|
||||
return check(constraintApplied, false, 'throw on getImageData for video');
|
||||
} catch (e) {
|
||||
return check(constraintApplied, e.name === 'SecurityError', 'get a security error');
|
||||
}
|
||||
}
|
||||
|
||||
return periodicCheck('video', tryToRenderToCanvas,
|
||||
(constraintApplied ? '' : 'not ') + 'protected', done);
|
||||
}
|
||||
|
||||
global.audioIsSilence = checkAudio;
|
||||
global.videoIsBlack = checkVideo;
|
||||
}(this));
|
@ -6,6 +6,7 @@ support-files =
|
||||
pc.js
|
||||
templates.js
|
||||
NetworkPreparationChromeScript.js
|
||||
blacksilence.js
|
||||
|
||||
[test_dataChannel_basicAudio.html]
|
||||
skip-if = toolkit == 'gonk' #Bug 962984 for debug, bug 963244 for opt
|
||||
@ -42,6 +43,7 @@ skip-if = (toolkit == 'gonk' && debug) #debug-only failure
|
||||
[test_getUserMedia_stopVideoAudioStreamWithFollowupVideoAudio.html]
|
||||
[test_getUserMedia_stopVideoStream.html]
|
||||
[test_getUserMedia_stopVideoStreamWithFollowupVideo.html]
|
||||
[test_getUserMedia_peerIdentity.html]
|
||||
[test_peerConnection_addCandidateInHaveLocalOffer.html]
|
||||
[test_peerConnection_basicAudio.html]
|
||||
skip-if = (toolkit == 'gonk' && debug) #Bug 962984, test fail on b2g debug build
|
||||
|
@ -0,0 +1,59 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=942367
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test mozGetUserMedia peerIdentity Constraint</title>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="head.js"></script>
|
||||
<script type="application/javascript" src="blacksilence.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=942367">Test mozGetUserMedia peerIdentity Constraint</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script type="application/javascript">
|
||||
function theTest() {
|
||||
function testPeerIdentityConstraint(withConstraint, done) {
|
||||
var config = { audio: true, video: true, fake: true };
|
||||
if (withConstraint) {
|
||||
config.peerIdentity = 'user@example.com';
|
||||
}
|
||||
info('getting media with constraints: ' + JSON.stringify(config));
|
||||
navigator.mozGetUserMedia(config, function(stream) {
|
||||
var oneDone = false;
|
||||
function checkDone() {
|
||||
if (oneDone) {
|
||||
done();
|
||||
}
|
||||
oneDone = true;
|
||||
}
|
||||
var cancelAudioCheck = audioIsSilence(withConstraint, stream, checkDone);
|
||||
var cancelVideoCheck = videoIsBlack(withConstraint, stream, checkDone);
|
||||
setTimeout(cancelAudioCheck, 20000);
|
||||
setTimeout(cancelVideoCheck, 20000);
|
||||
}, function(e) {
|
||||
ok(false, 'gUM error: ' + e);
|
||||
});
|
||||
};
|
||||
|
||||
// without constraint
|
||||
testPeerIdentityConstraint(false, function() {
|
||||
// with the constraint
|
||||
testPeerIdentityConstraint(true, SimpleTest.finish.bind(SimpleTest));
|
||||
});
|
||||
}
|
||||
|
||||
runTest(theTest);
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -18,6 +18,7 @@ dictionary MediaStreamConstraints {
|
||||
(boolean or MediaTrackConstraints) video = false;
|
||||
boolean picture = false; // Mozilla legacy
|
||||
boolean fake = false; // for testing
|
||||
DOMString? peerIdentity = null;
|
||||
};
|
||||
|
||||
interface MediaStream {
|
||||
|
@ -72,6 +72,9 @@ interface PeerConnectionImpl {
|
||||
readonly attribute PCImplSignalingState signalingState;
|
||||
readonly attribute PCImplSipccState sipccState;
|
||||
|
||||
attribute DOMString peerIdentity;
|
||||
readonly attribute boolean privacyRequested;
|
||||
|
||||
/* Data channels */
|
||||
[Throws]
|
||||
DataChannel createDataChannel(DOMString label, DOMString protocol,
|
||||
|
@ -15,4 +15,5 @@ dictionary RTCIceServer {
|
||||
|
||||
dictionary RTCConfiguration {
|
||||
sequence<RTCIceServer> iceServers;
|
||||
DOMString? peerIdentity = null;
|
||||
};
|
||||
|
@ -12,16 +12,16 @@
|
||||
|
||||
[Func="mozilla::dom::Touch::PrefEnabled"]
|
||||
interface Touch {
|
||||
readonly attribute long identifier;
|
||||
readonly attribute EventTarget target;
|
||||
readonly attribute long screenX;
|
||||
readonly attribute long screenY;
|
||||
readonly attribute long clientX;
|
||||
readonly attribute long clientY;
|
||||
readonly attribute long pageX;
|
||||
readonly attribute long pageY;
|
||||
readonly attribute long radiusX;
|
||||
readonly attribute long radiusY;
|
||||
readonly attribute float rotationAngle;
|
||||
readonly attribute float force;
|
||||
readonly attribute long identifier;
|
||||
readonly attribute EventTarget? target;
|
||||
readonly attribute long screenX;
|
||||
readonly attribute long screenY;
|
||||
readonly attribute long clientX;
|
||||
readonly attribute long clientY;
|
||||
readonly attribute long pageX;
|
||||
readonly attribute long pageY;
|
||||
readonly attribute long radiusX;
|
||||
readonly attribute long radiusY;
|
||||
readonly attribute float rotationAngle;
|
||||
readonly attribute float force;
|
||||
};
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include "nsISupportsImpl.h" // for gfxContext::Release, etc
|
||||
#include "nsIWidget.h" // for nsIWidget
|
||||
#include "prenv.h" // for PR_GetEnv
|
||||
#include "nsLayoutUtils.h"
|
||||
#ifdef XP_WIN
|
||||
#include "gfxWindowsPlatform.h"
|
||||
#endif
|
||||
@ -655,6 +656,17 @@ ContentClientIncremental::BeginPaintBuffer(ThebesLayer* aLayer,
|
||||
if (mHasBuffer &&
|
||||
(mContentType != contentType ||
|
||||
(mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) != mHasBufferOnWhite)) {
|
||||
#ifdef MOZ_DUMP_PAINTING
|
||||
if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
|
||||
if (mContentType != contentType) {
|
||||
printf_stderr("Layer's content type has changed\n");
|
||||
}
|
||||
if ((mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) != mHasBufferOnWhite) {
|
||||
printf_stderr("Layer's component alpha status has changed\n");
|
||||
}
|
||||
printf_stderr("Invalidating entire layer %p\n", aLayer);
|
||||
}
|
||||
#endif
|
||||
// We're effectively clearing the valid region, so we need to draw
|
||||
// the entire needed region now.
|
||||
result.mRegionToInvalidate = aLayer->GetValidRegion();
|
||||
|
@ -131,6 +131,9 @@ LayerManagerComposite::Destroy()
|
||||
RootLayer()->Destroy();
|
||||
}
|
||||
mRoot = nullptr;
|
||||
|
||||
mCompositor->Destroy();
|
||||
|
||||
mDestroyed = true;
|
||||
}
|
||||
}
|
||||
|
@ -241,11 +241,7 @@ CompositorParent::Destroy()
|
||||
|
||||
// Ensure that the layer manager is destructed on the compositor thread.
|
||||
mLayerManager = nullptr;
|
||||
if (mCompositor) {
|
||||
mCompositor->Destroy();
|
||||
}
|
||||
mCompositor = nullptr;
|
||||
|
||||
mCompositionManager = nullptr;
|
||||
mApzcTreeManager->ClearTree();
|
||||
mApzcTreeManager = nullptr;
|
||||
@ -277,6 +273,7 @@ CompositorParent::RecvWillStop()
|
||||
}
|
||||
mLayerManager->Destroy();
|
||||
mLayerManager = nullptr;
|
||||
mCompositor = nullptr;
|
||||
mCompositionManager = nullptr;
|
||||
}
|
||||
|
||||
|
9
js/src/jit-test/tests/ion/bug1005590.js
Normal file
9
js/src/jit-test/tests/ion/bug1005590.js
Normal file
@ -0,0 +1,9 @@
|
||||
function f(x) {
|
||||
"use asm"
|
||||
return !(1 || x)
|
||||
}
|
||||
for (var j = 0; j < 1; j++) {
|
||||
(function(x) {
|
||||
+f(+x)
|
||||
})()
|
||||
}
|
@ -683,7 +683,7 @@ bool
|
||||
CodeGenerator::visitTestVAndBranch(LTestVAndBranch *lir)
|
||||
{
|
||||
OutOfLineTestObject *ool = nullptr;
|
||||
MDefinition* input = lir->mir()->input();
|
||||
MDefinition *input = lir->mir()->input();
|
||||
// Unfortunately, it's possible that someone (e.g. phi elimination) switched
|
||||
// out our input after we did cacheOperandMightEmulateUndefined. So we
|
||||
// might think it can emulate undefined _and_ know that it can't be an
|
||||
@ -5366,8 +5366,12 @@ CodeGenerator::visitNotV(LNotV *lir)
|
||||
Label *ifFalsy;
|
||||
|
||||
OutOfLineTestObjectWithLabels *ool = nullptr;
|
||||
if (lir->mir()->operandMightEmulateUndefined()) {
|
||||
MOZ_ASSERT(lir->mir()->operand()->mightBeType(MIRType_Object));
|
||||
MDefinition *operand = lir->mir()->operand();
|
||||
// Unfortunately, it's possible that someone (e.g. phi elimination) switched
|
||||
// out our operand after we did cacheOperandMightEmulateUndefined. So we
|
||||
// might think it can emulate undefined _and_ know that it can't be an
|
||||
// object.
|
||||
if (lir->mir()->operandMightEmulateUndefined() && operand->mightBeType(MIRType_Object)) {
|
||||
ool = new(alloc()) OutOfLineTestObjectWithLabels();
|
||||
if (!addOutOfLineCode(ool))
|
||||
return false;
|
||||
@ -5382,7 +5386,7 @@ CodeGenerator::visitNotV(LNotV *lir)
|
||||
|
||||
testValueTruthyKernel(ToValue(lir, LNotV::Input), lir->temp1(), lir->temp2(),
|
||||
ToFloatRegister(lir->tempFloat()),
|
||||
ifTruthy, ifFalsy, ool, lir->mir()->operand());
|
||||
ifTruthy, ifFalsy, ool, operand);
|
||||
|
||||
Label join;
|
||||
Register output = ToRegister(lir->output());
|
||||
|
@ -568,6 +568,8 @@ JS_Init(void)
|
||||
MOZ_ASSERT(!JSRuntime::hasLiveRuntimes(),
|
||||
"how do we have live runtimes before JS_Init?");
|
||||
|
||||
PRMJ_NowInit();
|
||||
|
||||
#ifdef DEBUG
|
||||
CheckMessageNumbering();
|
||||
CheckMessageParameterCounts();
|
||||
|
@ -39,6 +39,10 @@ JS_STATIC_ASSERT(offsetof(JSRuntime, mainThread) ==
|
||||
PerThreadDataFriendFields::PerThreadDataFriendFields()
|
||||
{
|
||||
PodArrayZero(nativeStackLimit);
|
||||
#if JS_STACK_GROWTH_DIRECTION > 0
|
||||
for (int i=0; i<StackKindCount; i++)
|
||||
nativeStackLimit[i] = UINTPTR_MAX;
|
||||
#endif
|
||||
#if defined(JSGC_USE_EXACT_ROOTING)
|
||||
PodArrayZero(thingGCRooters);
|
||||
#endif
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
#include "prmjtime.h"
|
||||
|
||||
#include "mozilla/DebugOnly.h"
|
||||
#include "mozilla/MathAlgorithms.h"
|
||||
|
||||
#ifdef SOLARIS
|
||||
@ -19,8 +20,6 @@
|
||||
#include "jstypes.h"
|
||||
#include "jsutil.h"
|
||||
|
||||
#define PRMJ_DO_MILLISECONDS 1
|
||||
|
||||
#ifdef XP_WIN
|
||||
#include <windef.h>
|
||||
#include <winbase.h>
|
||||
@ -50,23 +49,24 @@ extern int gettimeofday(struct timeval *tv);
|
||||
|
||||
#endif /* XP_UNIX */
|
||||
|
||||
#define PRMJ_YEAR_DAYS 365L
|
||||
#define PRMJ_FOUR_YEARS_DAYS (4 * PRMJ_YEAR_DAYS + 1)
|
||||
#define PRMJ_CENTURY_DAYS (25 * PRMJ_FOUR_YEARS_DAYS - 1)
|
||||
#define PRMJ_FOUR_CENTURIES_DAYS (4 * PRMJ_CENTURY_DAYS + 1)
|
||||
#define PRMJ_HOUR_SECONDS 3600L
|
||||
#define PRMJ_DAY_SECONDS (24L * PRMJ_HOUR_SECONDS)
|
||||
#define PRMJ_YEAR_SECONDS (PRMJ_DAY_SECONDS * PRMJ_YEAR_DAYS)
|
||||
#define PRMJ_MAX_UNIX_TIMET 2145859200L /*time_t value equiv. to 12/31/2037 */
|
||||
using mozilla::DebugOnly;
|
||||
|
||||
/* Constants for GMT offset from 1970 */
|
||||
#define G1970GMTMICROHI 0x00dcdcad /* micro secs to 1970 hi */
|
||||
#define G1970GMTMICROLOW 0x8b3fa000 /* micro secs to 1970 low */
|
||||
#if defined(XP_UNIX)
|
||||
int64_t
|
||||
PRMJ_Now()
|
||||
{
|
||||
struct timeval tv;
|
||||
|
||||
#define G2037GMTMICROHI 0x00e45fab /* micro secs to 2037 high */
|
||||
#define G2037GMTMICROLOW 0x7a238000 /* micro secs to 2037 low */
|
||||
#ifdef _SVID_GETTOD /* Defined only on Solaris, see Solaris <sys/types.h> */
|
||||
gettimeofday(&tv);
|
||||
#else
|
||||
gettimeofday(&tv, 0);
|
||||
#endif /* _SVID_GETTOD */
|
||||
|
||||
#if defined(XP_WIN)
|
||||
return int64_t(tv.tv_sec) * PRMJ_USEC_PER_SEC + int64_t(tv.tv_usec);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
// Returns the number of microseconds since the Unix epoch.
|
||||
static double
|
||||
@ -89,14 +89,10 @@ struct CalibrationData {
|
||||
double offset; /* The low res 'epoch' */
|
||||
double timer_offset; /* The high res 'epoch' */
|
||||
|
||||
/* The last high res time that we returned since recalibrating */
|
||||
int64_t last;
|
||||
|
||||
bool calibrated;
|
||||
|
||||
#ifdef JS_THREADSAFE
|
||||
CRITICAL_SECTION data_lock;
|
||||
CRITICAL_SECTION calibration_lock;
|
||||
#endif
|
||||
};
|
||||
|
||||
@ -105,172 +101,91 @@ static CalibrationData calibration = { 0 };
|
||||
static void
|
||||
NowCalibrate()
|
||||
{
|
||||
if (calibration.freq == 0.0) {
|
||||
LARGE_INTEGER liFreq;
|
||||
if (!QueryPerformanceFrequency(&liFreq)) {
|
||||
// High-performance timer is unavailable.
|
||||
calibration.freq = -1.0;
|
||||
return;
|
||||
}
|
||||
calibration.freq = double(liFreq.QuadPart);
|
||||
}
|
||||
if (calibration.freq > 0.0) {
|
||||
// By wrapping a timeBegin/EndPeriod pair of calls around this loop,
|
||||
// the loop seems to take much less time (1 ms vs 15ms) on Vista.
|
||||
timeBeginPeriod(1);
|
||||
FILETIME ft, ftStart;
|
||||
GetSystemTimeAsFileTime(&ftStart);
|
||||
do {
|
||||
GetSystemTimeAsFileTime(&ft);
|
||||
} while (memcmp(&ftStart,&ft, sizeof(ft)) == 0);
|
||||
timeEndPeriod(1);
|
||||
MOZ_ASSERT(calibration.freq > 0);
|
||||
|
||||
LARGE_INTEGER now;
|
||||
QueryPerformanceCounter(&now);
|
||||
// By wrapping a timeBegin/EndPeriod pair of calls around this loop,
|
||||
// the loop seems to take much less time (1 ms vs 15ms) on Vista.
|
||||
timeBeginPeriod(1);
|
||||
FILETIME ft, ftStart;
|
||||
GetSystemTimeAsFileTime(&ftStart);
|
||||
do {
|
||||
GetSystemTimeAsFileTime(&ft);
|
||||
} while (memcmp(&ftStart, &ft, sizeof(ft)) == 0);
|
||||
timeEndPeriod(1);
|
||||
|
||||
calibration.offset = FileTimeToUnixMicroseconds(ft);
|
||||
calibration.timer_offset = double(now.QuadPart);
|
||||
calibration.last = 0;
|
||||
LARGE_INTEGER now;
|
||||
QueryPerformanceCounter(&now);
|
||||
|
||||
calibration.calibrated = true;
|
||||
}
|
||||
calibration.offset = FileTimeToUnixMicroseconds(ft);
|
||||
calibration.timer_offset = double(now.QuadPart);
|
||||
calibration.calibrated = true;
|
||||
}
|
||||
|
||||
#define CALIBRATIONLOCK_SPINCOUNT 0
|
||||
#define DATALOCK_SPINCOUNT 4096
|
||||
#define LASTLOCK_SPINCOUNT 4096
|
||||
static const unsigned DataLockSpinCount = 4096;
|
||||
|
||||
#ifdef JS_THREADSAFE
|
||||
static PRStatus
|
||||
NowInit(void)
|
||||
static void (WINAPI *pGetSystemTimePreciseAsFileTime)(LPFILETIME) = nullptr;
|
||||
|
||||
void
|
||||
PRMJ_NowInit()
|
||||
{
|
||||
memset(&calibration, 0, sizeof(calibration));
|
||||
NowCalibrate();
|
||||
InitializeCriticalSectionAndSpinCount(&calibration.calibration_lock, CALIBRATIONLOCK_SPINCOUNT);
|
||||
InitializeCriticalSectionAndSpinCount(&calibration.data_lock, DATALOCK_SPINCOUNT);
|
||||
return PR_SUCCESS;
|
||||
|
||||
// According to the documentation, QueryPerformanceFrequency will never
|
||||
// return false or return a non-zero frequency on systems that run
|
||||
// Windows XP or later. Also, the frequency is fixed so we only have to
|
||||
// query it once.
|
||||
LARGE_INTEGER liFreq;
|
||||
DebugOnly<BOOL> res = QueryPerformanceFrequency(&liFreq);
|
||||
MOZ_ASSERT(res);
|
||||
calibration.freq = double(liFreq.QuadPart);
|
||||
MOZ_ASSERT(calibration.freq > 0.0);
|
||||
|
||||
#ifdef JS_THREADSAFE
|
||||
InitializeCriticalSectionAndSpinCount(&calibration.data_lock, DataLockSpinCount);
|
||||
#endif
|
||||
|
||||
// Windows 8 has a new API function we can use.
|
||||
if (HMODULE h = GetModuleHandle("kernel32.dll")) {
|
||||
pGetSystemTimePreciseAsFileTime =
|
||||
(void (WINAPI *)(LPFILETIME))GetProcAddress(h, "GetSystemTimePreciseAsFileTime");
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef JS_THREADSAFE
|
||||
void
|
||||
PRMJ_NowShutdown()
|
||||
{
|
||||
DeleteCriticalSection(&calibration.calibration_lock);
|
||||
DeleteCriticalSection(&calibration.data_lock);
|
||||
}
|
||||
|
||||
#define MUTEX_LOCK(m) EnterCriticalSection(m)
|
||||
#define MUTEX_TRYLOCK(m) TryEnterCriticalSection(m)
|
||||
#define MUTEX_UNLOCK(m) LeaveCriticalSection(m)
|
||||
#define MUTEX_SETSPINCOUNT(m, c) SetCriticalSectionSpinCount((m),(c))
|
||||
|
||||
static PRCallOnceType calibrationOnce = { 0 };
|
||||
|
||||
#else
|
||||
|
||||
#define MUTEX_LOCK(m)
|
||||
#define MUTEX_TRYLOCK(m) 1
|
||||
#define MUTEX_UNLOCK(m)
|
||||
#define MUTEX_SETSPINCOUNT(m, c)
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* XP_WIN */
|
||||
|
||||
|
||||
#if defined(XP_UNIX)
|
||||
// Please see bug 363258 for why the win32 timing code is so complex.
|
||||
int64_t
|
||||
PRMJ_Now()
|
||||
{
|
||||
struct timeval tv;
|
||||
if (pGetSystemTimePreciseAsFileTime) {
|
||||
// Windows 8 has a new API function that does all the work.
|
||||
FILETIME ft;
|
||||
pGetSystemTimePreciseAsFileTime(&ft);
|
||||
return int64_t(FileTimeToUnixMicroseconds(ft));
|
||||
}
|
||||
|
||||
#ifdef _SVID_GETTOD /* Defined only on Solaris, see Solaris <sys/types.h> */
|
||||
gettimeofday(&tv);
|
||||
#else
|
||||
gettimeofday(&tv, 0);
|
||||
#endif /* _SVID_GETTOD */
|
||||
|
||||
return int64_t(tv.tv_sec) * PRMJ_USEC_PER_SEC + int64_t(tv.tv_usec);
|
||||
}
|
||||
|
||||
#else
|
||||
/*
|
||||
|
||||
Win32 python-esque pseudo code
|
||||
Please see bug 363258 for why the win32 timing code is so complex.
|
||||
|
||||
calibration mutex : Win32CriticalSection(spincount=0)
|
||||
data mutex : Win32CriticalSection(spincount=4096)
|
||||
|
||||
def NowInit():
|
||||
init mutexes
|
||||
PRMJ_NowCalibration()
|
||||
|
||||
def NowCalibration():
|
||||
expensive up-to-15ms call
|
||||
|
||||
def PRMJ_Now():
|
||||
returnedTime = 0
|
||||
needCalibration = False
|
||||
cachedOffset = 0.0
|
||||
calibrated = False
|
||||
PR_CallOnce(PRMJ_NowInit)
|
||||
do
|
||||
if not global.calibrated or needCalibration:
|
||||
acquire calibration mutex
|
||||
acquire data mutex
|
||||
|
||||
// Only recalibrate if someone didn't already
|
||||
if cachedOffset == calibration.offset:
|
||||
// Have all waiting threads immediately wait
|
||||
set data mutex spin count = 0
|
||||
PRMJ_NowCalibrate()
|
||||
calibrated = 1
|
||||
|
||||
set data mutex spin count = default
|
||||
release data mutex
|
||||
release calibration mutex
|
||||
|
||||
calculate lowres time
|
||||
|
||||
if highres timer available:
|
||||
acquire data mutex
|
||||
calculate highres time
|
||||
cachedOffset = calibration.offset
|
||||
highres time = calibration.last = max(highres time, calibration.last)
|
||||
release data mutex
|
||||
|
||||
get kernel tick interval
|
||||
|
||||
if abs(highres - lowres) < kernel tick:
|
||||
returnedTime = highres time
|
||||
needCalibration = False
|
||||
else:
|
||||
if calibrated:
|
||||
returnedTime = lowres
|
||||
needCalibration = False
|
||||
else:
|
||||
needCalibration = True
|
||||
else:
|
||||
returnedTime = lowres
|
||||
while needCalibration
|
||||
|
||||
*/
|
||||
|
||||
int64_t
|
||||
PRMJ_Now()
|
||||
{
|
||||
bool calibrated = false;
|
||||
bool needsCalibration = false;
|
||||
int64_t returnedTime;
|
||||
bool needsCalibration = !calibration.calibrated;
|
||||
double cachedOffset = 0.0;
|
||||
|
||||
/* For non threadsafe platforms, NowInit is not necessary */
|
||||
#ifdef JS_THREADSAFE
|
||||
PR_CallOnce(&calibrationOnce, NowInit);
|
||||
#endif
|
||||
do {
|
||||
if (!calibration.calibrated || needsCalibration) {
|
||||
MUTEX_LOCK(&calibration.calibration_lock);
|
||||
while (true) {
|
||||
if (needsCalibration) {
|
||||
MUTEX_LOCK(&calibration.data_lock);
|
||||
|
||||
// Recalibrate only if no one else did before us.
|
||||
@ -284,97 +199,74 @@ PRMJ_Now()
|
||||
calibrated = true;
|
||||
|
||||
// Restore spin count.
|
||||
MUTEX_SETSPINCOUNT(&calibration.data_lock, DATALOCK_SPINCOUNT);
|
||||
MUTEX_SETSPINCOUNT(&calibration.data_lock, DataLockSpinCount);
|
||||
}
|
||||
MUTEX_UNLOCK(&calibration.data_lock);
|
||||
MUTEX_UNLOCK(&calibration.calibration_lock);
|
||||
}
|
||||
|
||||
MUTEX_UNLOCK(&calibration.data_lock);
|
||||
}
|
||||
|
||||
// Calculate a low resolution time.
|
||||
FILETIME ft;
|
||||
GetSystemTimeAsFileTime(&ft);
|
||||
double lowresTime = FileTimeToUnixMicroseconds(ft);
|
||||
|
||||
if (calibration.freq > 0.0) {
|
||||
// Grab high resolution time.
|
||||
LARGE_INTEGER now;
|
||||
QueryPerformanceCounter(&now);
|
||||
double highresTimerValue = double(now.QuadPart);
|
||||
// Grab high resolution time.
|
||||
LARGE_INTEGER now;
|
||||
QueryPerformanceCounter(&now);
|
||||
double highresTimerValue = double(now.QuadPart);
|
||||
|
||||
MUTEX_LOCK(&calibration.data_lock);
|
||||
double highresTime = calibration.offset + PRMJ_USEC_PER_SEC *
|
||||
(highresTimerValue-calibration.timer_offset)/calibration.freq;
|
||||
cachedOffset = calibration.offset;
|
||||
MUTEX_LOCK(&calibration.data_lock);
|
||||
double highresTime = calibration.offset +
|
||||
PRMJ_USEC_PER_SEC * (highresTimerValue - calibration.timer_offset) / calibration.freq;
|
||||
cachedOffset = calibration.offset;
|
||||
MUTEX_UNLOCK(&calibration.data_lock);
|
||||
|
||||
// On some dual processor/core systems, we might get an earlier time
|
||||
// so we cache the last time that we returned.
|
||||
calibration.last = js::Max(calibration.last, int64_t(highresTime));
|
||||
returnedTime = calibration.last;
|
||||
MUTEX_UNLOCK(&calibration.data_lock);
|
||||
// Assume the NT kernel ticks every 15.6 ms. Unfortunately there's no
|
||||
// good way to determine this (NtQueryTimerResolution is an undocumented
|
||||
// API), but 15.6 ms seems to be the max possible value. Hardcoding 15.6
|
||||
// means we'll recalibrate if the highres and lowres timers diverge by
|
||||
// more than 30 ms.
|
||||
static const double KernelTickInMicroseconds = 15625.25;
|
||||
|
||||
// Rather than assume the NT kernel ticks every 15.6ms, ask it.
|
||||
double skewThreshold;
|
||||
DWORD timeAdjustment, timeIncrement;
|
||||
BOOL timeAdjustmentDisabled;
|
||||
if (GetSystemTimeAdjustment(&timeAdjustment,
|
||||
&timeIncrement,
|
||||
&timeAdjustmentDisabled)) {
|
||||
if (timeAdjustmentDisabled) {
|
||||
/* timeAdjustment is in units of 100ns */
|
||||
skewThreshold = timeAdjustment/10.0;
|
||||
} else {
|
||||
/* timeIncrement is in units of 100ns */
|
||||
skewThreshold = timeIncrement/10.0;
|
||||
}
|
||||
} else {
|
||||
// Default to 15.625 ms if the syscall fails.
|
||||
skewThreshold = 15625.25;
|
||||
}
|
||||
// Check for clock skew.
|
||||
double diff = lowresTime - highresTime;
|
||||
|
||||
// Check for clock skew.
|
||||
double diff = lowresTime - highresTime;
|
||||
|
||||
// For some reason that I have not determined, the skew can be
|
||||
// up to twice a kernel tick. This does not seem to happen by
|
||||
// itself, but I have only seen it triggered by another program
|
||||
// doing some kind of file I/O. The symptoms are a negative diff
|
||||
// followed by an equally large positive diff.
|
||||
if (mozilla::Abs(diff) <= 2 * skewThreshold) {
|
||||
// No detectable clock skew.
|
||||
return int64_t(highresTime);
|
||||
}
|
||||
|
||||
if (calibrated) {
|
||||
// If we already calibrated once this instance, and the
|
||||
// clock is still skewed, then either the processor(s) are
|
||||
// wildly changing clockspeed or the system is so busy that
|
||||
// we get switched out for long periods of time. In either
|
||||
// case, it would be infeasible to make use of high
|
||||
// resolution results for anything, so let's resort to old
|
||||
// behavior for this call. It's possible that in the
|
||||
// future, the user will want the high resolution timer, so
|
||||
// we don't disable it entirely.
|
||||
return int64_t(lowresTime);
|
||||
}
|
||||
|
||||
// It is possible that when we recalibrate, we will return a
|
||||
// value less than what we have returned before; this is
|
||||
// unavoidable. We cannot tell the different between a
|
||||
// faulty QueryPerformanceCounter implementation and user
|
||||
// changes to the operating system time. Since we must
|
||||
// respect user changes to the operating system time, we
|
||||
// cannot maintain the invariant that Date.now() never
|
||||
// decreases; the old implementation has this behavior as
|
||||
// well.
|
||||
needsCalibration = true;
|
||||
} else {
|
||||
// No high resolution timer is available, so fall back.
|
||||
returnedTime = int64_t(lowresTime);
|
||||
// For some reason that I have not determined, the skew can be
|
||||
// up to twice a kernel tick. This does not seem to happen by
|
||||
// itself, but I have only seen it triggered by another program
|
||||
// doing some kind of file I/O. The symptoms are a negative diff
|
||||
// followed by an equally large positive diff.
|
||||
if (mozilla::Abs(diff) <= 2 * KernelTickInMicroseconds) {
|
||||
// No detectable clock skew.
|
||||
return int64_t(highresTime);
|
||||
}
|
||||
} while (needsCalibration);
|
||||
|
||||
return returnedTime;
|
||||
if (calibrated) {
|
||||
// If we already calibrated once this instance, and the
|
||||
// clock is still skewed, then either the processor(s) are
|
||||
// wildly changing clockspeed or the system is so busy that
|
||||
// we get switched out for long periods of time. In either
|
||||
// case, it would be infeasible to make use of high
|
||||
// resolution results for anything, so let's resort to old
|
||||
// behavior for this call. It's possible that in the
|
||||
// future, the user will want the high resolution timer, so
|
||||
// we don't disable it entirely.
|
||||
return int64_t(lowresTime);
|
||||
}
|
||||
|
||||
// It is possible that when we recalibrate, we will return a
|
||||
// value less than what we have returned before; this is
|
||||
// unavoidable. We cannot tell the different between a
|
||||
// faulty QueryPerformanceCounter implementation and user
|
||||
// changes to the operating system time. Since we must
|
||||
// respect user changes to the operating system time, we
|
||||
// cannot maintain the invariant that Date.now() never
|
||||
// decreases; the old implementation has this behavior as
|
||||
// well.
|
||||
needsCalibration = true;
|
||||
}
|
||||
|
||||
MOZ_ASSUME_UNREACHABLE("Shouldn't get here");
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -34,12 +34,22 @@ struct PRMJTime {
|
||||
extern int64_t
|
||||
PRMJ_Now();
|
||||
|
||||
/* Initialize the resources associated with PRMJ_Now. */
|
||||
#if defined(XP_WIN)
|
||||
extern void
|
||||
PRMJ_NowInit();
|
||||
#else
|
||||
inline void
|
||||
PRMJ_NowInit() {}
|
||||
#endif
|
||||
|
||||
/* Release the resources associated with PRMJ_Now; don't call PRMJ_Now again */
|
||||
#if defined(JS_THREADSAFE) && defined(XP_WIN)
|
||||
extern void
|
||||
PRMJ_NowShutdown(void);
|
||||
PRMJ_NowShutdown();
|
||||
#else
|
||||
#define PRMJ_NowShutdown()
|
||||
inline void
|
||||
PRMJ_NowShutdown() {}
|
||||
#endif
|
||||
|
||||
/* Format a time value into a buffer. Same semantics as strftime() */
|
||||
|
@ -232,10 +232,6 @@ JSRuntime::JSRuntime(JSRuntime *parentRuntime, JSUseHelperThreads useHelperThrea
|
||||
PodZero(&debugHooks);
|
||||
PodArrayZero(nativeStackQuota);
|
||||
PodZero(&asmJSCacheOps);
|
||||
|
||||
#if JS_STACK_GROWTH_DIRECTION > 0
|
||||
mainThread.nativeStackLimit = UINTPTR_MAX;
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool
|
||||
|
@ -1975,6 +1975,15 @@ ContainerState::PopThebesLayerData()
|
||||
if (userData->mForcedBackgroundColor != backgroundColor) {
|
||||
// Invalidate the entire target ThebesLayer since we're changing
|
||||
// the background color
|
||||
#ifdef MOZ_DUMP_PAINTING
|
||||
if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
|
||||
printf_stderr("Forced background color has changed from #%08X to #%08X on layer %p\n",
|
||||
userData->mForcedBackgroundColor, backgroundColor, data->mLayer);
|
||||
nsAutoCString str;
|
||||
AppendToString(str, data->mLayer->GetValidRegion());
|
||||
printf_stderr("Invalidating layer %p: %s\n", data->mLayer, str.get());
|
||||
}
|
||||
#endif
|
||||
data->mLayer->InvalidateRegion(data->mLayer->GetValidRegion());
|
||||
}
|
||||
userData->mForcedBackgroundColor = backgroundColor;
|
||||
|
@ -2163,22 +2163,28 @@ nsDisplayBackgroundImage::GetInsideClipRegion(nsDisplayItem* aItem,
|
||||
nscoord radii[8];
|
||||
nsRect clipRect;
|
||||
bool haveRadii;
|
||||
switch (aClip) {
|
||||
case NS_STYLE_BG_CLIP_BORDER:
|
||||
haveRadii = frame->GetBorderRadii(radii);
|
||||
clipRect = nsRect(aItem->ToReferenceFrame(), frame->GetSize());
|
||||
break;
|
||||
case NS_STYLE_BG_CLIP_PADDING:
|
||||
haveRadii = frame->GetPaddingBoxBorderRadii(radii);
|
||||
clipRect = frame->GetPaddingRect() - frame->GetPosition() + aItem->ToReferenceFrame();
|
||||
break;
|
||||
case NS_STYLE_BG_CLIP_CONTENT:
|
||||
haveRadii = frame->GetContentBoxBorderRadii(radii);
|
||||
clipRect = frame->GetContentRect() - frame->GetPosition() + aItem->ToReferenceFrame();
|
||||
break;
|
||||
default:
|
||||
NS_NOTREACHED("Unknown clip type");
|
||||
return result;
|
||||
if (frame->GetType() == nsGkAtoms::canvasFrame) {
|
||||
nsCanvasFrame* canvasFrame = static_cast<nsCanvasFrame*>(frame);
|
||||
haveRadii = false;
|
||||
clipRect = canvasFrame->CanvasArea() + aItem->ToReferenceFrame();
|
||||
} else {
|
||||
switch (aClip) {
|
||||
case NS_STYLE_BG_CLIP_BORDER:
|
||||
haveRadii = frame->GetBorderRadii(radii);
|
||||
clipRect = nsRect(aItem->ToReferenceFrame(), frame->GetSize());
|
||||
break;
|
||||
case NS_STYLE_BG_CLIP_PADDING:
|
||||
haveRadii = frame->GetPaddingBoxBorderRadii(radii);
|
||||
clipRect = frame->GetPaddingRect() - frame->GetPosition() + aItem->ToReferenceFrame();
|
||||
break;
|
||||
case NS_STYLE_BG_CLIP_CONTENT:
|
||||
haveRadii = frame->GetContentBoxBorderRadii(radii);
|
||||
clipRect = frame->GetContentRect() - frame->GetPosition() + aItem->ToReferenceFrame();
|
||||
break;
|
||||
default:
|
||||
NS_NOTREACHED("Unknown clip type");
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
if (haveRadii) {
|
||||
|
@ -6336,7 +6336,7 @@ FindAnyTarget(const uint32_t& aKey, nsRefPtr<dom::Touch>& aData,
|
||||
void* aAnyTarget)
|
||||
{
|
||||
if (aData) {
|
||||
dom::EventTarget* target = aData->Target();
|
||||
dom::EventTarget* target = aData->GetTarget();
|
||||
if (target) {
|
||||
nsCOMPtr<nsIContent>* content =
|
||||
static_cast<nsCOMPtr<nsIContent>*>(aAnyTarget);
|
||||
@ -6878,7 +6878,7 @@ PresShell::HandleEvent(nsIFrame* aFrame,
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIContent> content =
|
||||
do_QueryInterface(oldTouch->Target());
|
||||
do_QueryInterface(oldTouch->GetTarget());
|
||||
if (!content) {
|
||||
break;
|
||||
}
|
||||
|
@ -7,8 +7,9 @@
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<!-- Need a timeout here to allow paint unsuppression before we start the test -->
|
||||
<body onload="setTimeout(startTest,0)" style="background: url(blue-32x32.png) fixed">
|
||||
<body onload="setTimeout(startTest,0)" style="background:url(blue-32x32.png) top left no-repeat fixed; background-size: 100px 2000px; overflow:hidden;">
|
||||
<div style="height: 2048px"></div>
|
||||
|
||||
<pre id="test">
|
||||
<script type="application/javascript">
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
@ -18,15 +19,19 @@ var utils = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
|
||||
|
||||
function startTest() {
|
||||
// Do a scroll to ensure we trigger activity heuristics.
|
||||
document.documentElement.scrollTop = 1;
|
||||
waitForAllPaintsFlushed(function () {
|
||||
// Clear paint state and scroll down
|
||||
utils.checkAndClearPaintedState(document.body);
|
||||
document.documentElement.scrollTop = 100;
|
||||
document.documentElement.scrollTop = 0;
|
||||
waitForAllPaintsFlushed(function () {
|
||||
// Make sure the body didn't paint
|
||||
var painted = utils.checkAndClearPaintedState(document.body);
|
||||
is(painted, false, "Fixed background should not have been painted when scrolled");
|
||||
SimpleTest.finish();
|
||||
// Clear paint state and scroll down
|
||||
utils.checkAndClearPaintedState(document.documentElement);
|
||||
document.documentElement.scrollTop = 100;
|
||||
waitForAllPaintsFlushed(function () {
|
||||
// Make sure nothing painted
|
||||
var painted = utils.checkAndClearPaintedState(document.documentElement);
|
||||
is(painted, false, "Fixed background should not have been painted when scrolled");
|
||||
SimpleTest.finish();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -30,6 +30,10 @@
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "nsIPrefService.h"
|
||||
#include "nsIPrefBranch.h"
|
||||
#ifdef MOZILLA_INTERNAL_API
|
||||
#include "nsIPrincipal.h"
|
||||
#include "nsIDocument.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
@ -173,40 +177,40 @@ void VcmSIPCCBinding::CandidateReady(NrIceMediaStream* stream,
|
||||
|
||||
char *candidate_tmp = (char *)malloc(candidate.size() + 1);
|
||||
if (!candidate_tmp)
|
||||
return;
|
||||
return;
|
||||
sstrncpy(candidate_tmp, candidate.c_str(), candidate.size() + 1);
|
||||
// Send a message to the GSM thread.
|
||||
CC_CallFeature_FoundICECandidate(vcm_opaque->call_handle_,
|
||||
candidate_tmp,
|
||||
nullptr,
|
||||
vcm_opaque->level_,
|
||||
nullptr);
|
||||
candidate_tmp,
|
||||
nullptr,
|
||||
vcm_opaque->level_,
|
||||
nullptr);
|
||||
}
|
||||
|
||||
void VcmSIPCCBinding::setStreamObserver(StreamObserver* obs)
|
||||
{
|
||||
streamObserver = obs;
|
||||
streamObserver = obs;
|
||||
}
|
||||
|
||||
/* static */
|
||||
StreamObserver * VcmSIPCCBinding::getStreamObserver()
|
||||
{
|
||||
if (gSelf != nullptr)
|
||||
return gSelf->streamObserver;
|
||||
return gSelf->streamObserver;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void VcmSIPCCBinding::setMediaProviderObserver(MediaProviderObserver* obs)
|
||||
{
|
||||
mediaProviderObserver = obs;
|
||||
mediaProviderObserver = obs;
|
||||
}
|
||||
|
||||
|
||||
MediaProviderObserver * VcmSIPCCBinding::getMediaProviderObserver()
|
||||
{
|
||||
if (gSelf != nullptr)
|
||||
return gSelf->mediaProviderObserver;
|
||||
return gSelf->mediaProviderObserver;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
@ -571,7 +575,7 @@ static short vcmGetIceStream_m(cc_mcapid_t mcap_id,
|
||||
*
|
||||
*/
|
||||
static short vcmRxAllocICE_s(TemporaryRef<NrIceCtx> ctx_in,
|
||||
TemporaryRef<NrIceMediaStream> stream_in,
|
||||
TemporaryRef<NrIceMediaStream> stream_in,
|
||||
cc_call_handle_t call_handle,
|
||||
cc_streamid_t stream_id,
|
||||
uint16_t level,
|
||||
@ -1340,7 +1344,7 @@ short vcmRxOpen(cc_mcapid_t mcap_id,
|
||||
*port_allocated = -1;
|
||||
if(listen_ip)
|
||||
{
|
||||
csf_sprintf(dottedIP, sizeof(dottedIP), "%u.%u.%u.%u",
|
||||
csf_sprintf(dottedIP, sizeof(dottedIP), "%u.%u.%u.%u",
|
||||
(listen_ip->u.ip4 >> 24) & 0xff, (listen_ip->u.ip4 >> 16) & 0xff,
|
||||
(listen_ip->u.ip4 >> 8) & 0xff, listen_ip->u.ip4 & 0xff );
|
||||
}
|
||||
@ -1651,7 +1655,7 @@ static int vcmRxStartICE_m(cc_mcapid_t mcap_id,
|
||||
return VCM_ERROR;
|
||||
|
||||
// Now we have all the pieces, create the pipeline
|
||||
mozilla::RefPtr<mozilla::MediaPipeline> pipeline =
|
||||
mozilla::RefPtr<mozilla::MediaPipelineReceiveAudio> pipeline =
|
||||
new mozilla::MediaPipelineReceiveAudio(
|
||||
pc.impl()->GetHandle(),
|
||||
pc.impl()->GetMainThread().get(),
|
||||
@ -1712,7 +1716,7 @@ static int vcmRxStartICE_m(cc_mcapid_t mcap_id,
|
||||
return VCM_ERROR;
|
||||
|
||||
// Now we have all the pieces, create the pipeline
|
||||
mozilla::RefPtr<mozilla::MediaPipeline> pipeline =
|
||||
mozilla::RefPtr<mozilla::MediaPipelineReceiveVideo> pipeline =
|
||||
new mozilla::MediaPipelineReceiveVideo(
|
||||
pc.impl()->GetHandle(),
|
||||
pc.impl()->GetMainThread().get(),
|
||||
@ -1887,7 +1891,7 @@ void vcmRxReleasePort (cc_mcapid_t mcap_id,
|
||||
|
||||
StreamObserver* obs = VcmSIPCCBinding::getStreamObserver();
|
||||
if(obs != nullptr)
|
||||
obs->deregisterStream(call_handle, stream_id);
|
||||
obs->deregisterStream(call_handle, stream_id);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -2272,6 +2276,103 @@ int vcmTxStart(cc_mcapid_t mcap_id,
|
||||
return VCM_ERROR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a conduit for audio transmission.
|
||||
*
|
||||
* @param[in] level - the m-line index
|
||||
* @param[in] payload - codec info
|
||||
* @param[in] pc - the peer connection
|
||||
* @param [in] attrs - additional audio attributes
|
||||
* @param[out] conduit - the conduit to create
|
||||
*/
|
||||
static int vcmTxCreateAudioConduit(int level,
|
||||
const vcm_payload_info_t *payload,
|
||||
sipcc::PeerConnectionWrapper &pc,
|
||||
const vcm_mediaAttrs_t *attrs,
|
||||
mozilla::RefPtr<mozilla::MediaSessionConduit> &conduit)
|
||||
{
|
||||
mozilla::AudioCodecConfig *config_raw =
|
||||
new mozilla::AudioCodecConfig(
|
||||
payload->remote_rtp_pt,
|
||||
ccsdpCodecName(payload->codec_type),
|
||||
payload->audio.frequency,
|
||||
payload->audio.packet_size,
|
||||
payload->audio.channels,
|
||||
payload->audio.bitrate,
|
||||
pc.impl()->load_manager());
|
||||
|
||||
// Take possession of this pointer
|
||||
mozilla::ScopedDeletePtr<mozilla::AudioCodecConfig> config(config_raw);
|
||||
|
||||
// Instantiate an appropriate conduit
|
||||
mozilla::RefPtr<mozilla::MediaSessionConduit> rx_conduit =
|
||||
pc.impl()->media()->GetConduit(level, true);
|
||||
MOZ_ASSERT_IF(rx_conduit, rx_conduit->type() == MediaSessionConduit::AUDIO);
|
||||
|
||||
// The two sides of a send/receive pair of conduits each keep a raw pointer to the other,
|
||||
// and are responsible for cleanly shutting down.
|
||||
mozilla::RefPtr<mozilla::AudioSessionConduit> tx_conduit =
|
||||
mozilla::AudioSessionConduit::Create(
|
||||
static_cast<AudioSessionConduit *>(rx_conduit.get()));
|
||||
|
||||
if (!tx_conduit || tx_conduit->ConfigureSendMediaCodec(config) ||
|
||||
tx_conduit->EnableAudioLevelExtension(attrs->audio_level,
|
||||
attrs->audio_level_id)) {
|
||||
return VCM_ERROR;
|
||||
}
|
||||
CSFLogError(logTag, "Created audio pipeline audio level %d %d",
|
||||
attrs->audio_level, attrs->audio_level_id);
|
||||
|
||||
conduit = tx_conduit;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a conduit for video transmission.
|
||||
*
|
||||
* @param[in] level - the m-line index
|
||||
* @param[in] payload - codec info
|
||||
* @param[in] pc - the peer connection
|
||||
* @param[out] conduit - the conduit to create
|
||||
*/
|
||||
static int vcmTxCreateVideoConduit(int level,
|
||||
const vcm_payload_info_t *payload,
|
||||
sipcc::PeerConnectionWrapper &pc,
|
||||
mozilla::RefPtr<mozilla::MediaSessionConduit> &conduit)
|
||||
{
|
||||
mozilla::VideoCodecConfig *config_raw;
|
||||
config_raw = new mozilla::VideoCodecConfig(
|
||||
payload->remote_rtp_pt,
|
||||
ccsdpCodecName(payload->codec_type),
|
||||
payload->video.rtcp_fb_types,
|
||||
payload->video.max_fs,
|
||||
payload->video.max_fr,
|
||||
pc.impl()->load_manager());
|
||||
|
||||
// Take possession of this pointer
|
||||
mozilla::ScopedDeletePtr<mozilla::VideoCodecConfig> config(config_raw);
|
||||
|
||||
// Instantiate an appropriate conduit
|
||||
mozilla::RefPtr<mozilla::MediaSessionConduit> rx_conduit =
|
||||
pc.impl()->media()->GetConduit(level, true);
|
||||
MOZ_ASSERT_IF(rx_conduit, rx_conduit->type() == MediaSessionConduit::VIDEO);
|
||||
|
||||
// The two sides of a send/receive pair of conduits each keep a raw pointer to the other,
|
||||
// and are responsible for cleanly shutting down.
|
||||
mozilla::RefPtr<mozilla::VideoSessionConduit> tx_conduit =
|
||||
mozilla::VideoSessionConduit::Create(static_cast<VideoSessionConduit *>(rx_conduit.get()));
|
||||
if (!tx_conduit) {
|
||||
return VCM_ERROR;
|
||||
}
|
||||
if (vcmEnsureExternalCodec(tx_conduit, config, true)) {
|
||||
return VCM_ERROR;
|
||||
}
|
||||
if (tx_conduit->ConfigureSendMediaCodec(config)) {
|
||||
return VCM_ERROR;
|
||||
}
|
||||
conduit = tx_conduit;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* start tx stream
|
||||
@ -2282,7 +2383,7 @@ int vcmTxStart(cc_mcapid_t mcap_id,
|
||||
* @param[in] stream_id - stream id of the given media type.
|
||||
* @param[in] level - the m-line index
|
||||
* @param[in] pc_stream_id - the media stream index (from PC.addStream())
|
||||
* @param[i]n pc_track_id - the track within the media stream
|
||||
* @param[in] pc_track_id - the track within the media stream
|
||||
* @param[in] call_handle - call handle
|
||||
* @param[in] peerconnection - the peerconnection in use
|
||||
* @param[in] payload - payload information
|
||||
@ -2298,21 +2399,21 @@ int vcmTxStart(cc_mcapid_t mcap_id,
|
||||
#define EXTRACT_DYNAMIC_PAYLOAD_TYPE(PTYPE) ((PTYPE)>>16)
|
||||
|
||||
static int vcmTxStartICE_m(cc_mcapid_t mcap_id,
|
||||
cc_groupid_t group_id,
|
||||
cc_streamid_t stream_id,
|
||||
int level,
|
||||
int pc_stream_id,
|
||||
int pc_track_id,
|
||||
cc_call_handle_t call_handle,
|
||||
const char *peerconnection,
|
||||
const vcm_payload_info_t *payload,
|
||||
short tos,
|
||||
sdp_setup_type_e setup_type,
|
||||
const char *fingerprint_alg,
|
||||
const char *fingerprint,
|
||||
vcm_mediaAttrs_t *attrs)
|
||||
cc_groupid_t group_id,
|
||||
cc_streamid_t stream_id,
|
||||
int level,
|
||||
int pc_stream_id,
|
||||
int pc_track_id,
|
||||
cc_call_handle_t call_handle,
|
||||
const char *peerconnection,
|
||||
const vcm_payload_info_t *payload,
|
||||
short tos,
|
||||
sdp_setup_type_e setup_type,
|
||||
const char *fingerprint_alg,
|
||||
const char *fingerprint,
|
||||
vcm_mediaAttrs_t *attrs)
|
||||
{
|
||||
CSFLogDebug( logTag, "%s(%s) track = %d, stream = %d, level = %d",
|
||||
CSFLogDebug(logTag, "%s(%s) track = %d, stream = %d, level = %d",
|
||||
__FUNCTION__,
|
||||
peerconnection,
|
||||
pc_track_id,
|
||||
@ -2322,19 +2423,20 @@ static int vcmTxStartICE_m(cc_mcapid_t mcap_id,
|
||||
// Find the PC and get the stream
|
||||
sipcc::PeerConnectionWrapper pc(peerconnection);
|
||||
ENSURE_PC(pc, VCM_ERROR);
|
||||
nsRefPtr<sipcc::LocalSourceStreamInfo> stream = pc.impl()->media()->
|
||||
GetLocalStream(pc_stream_id);
|
||||
nsRefPtr<sipcc::LocalSourceStreamInfo> stream =
|
||||
pc.impl()->media()->GetLocalStream(pc_stream_id);
|
||||
|
||||
// Create the transport flows
|
||||
mozilla::RefPtr<TransportFlow> rtp_flow =
|
||||
vcmCreateTransportFlow(pc.impl(), level, false, setup_type,
|
||||
fingerprint_alg, fingerprint);
|
||||
vcmCreateTransportFlow(pc.impl(), level, false, setup_type,
|
||||
fingerprint_alg, fingerprint);
|
||||
if (!rtp_flow) {
|
||||
CSFLogError( logTag, "Could not create RTP flow");
|
||||
return VCM_ERROR;
|
||||
CSFLogError(logTag, "Could not create RTP flow");
|
||||
return VCM_ERROR;
|
||||
}
|
||||
|
||||
mozilla::RefPtr<TransportFlow> rtcp_flow = nullptr;
|
||||
if(!attrs->rtcp_mux) {
|
||||
if (!attrs->rtcp_mux) {
|
||||
rtcp_flow = vcmCreateTransportFlow(pc.impl(), level, true, setup_type,
|
||||
fingerprint_alg, fingerprint);
|
||||
if (!rtcp_flow) {
|
||||
@ -2343,121 +2445,55 @@ static int vcmTxStartICE_m(cc_mcapid_t mcap_id,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const char *mediaType;
|
||||
mozilla::RefPtr<mozilla::MediaSessionConduit> conduit;
|
||||
int err = VCM_ERROR;
|
||||
if (CC_IS_AUDIO(mcap_id)) {
|
||||
mozilla::AudioCodecConfig *config_raw;
|
||||
config_raw = new mozilla::AudioCodecConfig(
|
||||
payload->remote_rtp_pt,
|
||||
ccsdpCodecName(payload->codec_type),
|
||||
payload->audio.frequency,
|
||||
payload->audio.packet_size,
|
||||
payload->audio.channels,
|
||||
payload->audio.bitrate,
|
||||
pc.impl()->load_manager());
|
||||
|
||||
// Take possession of this pointer
|
||||
mozilla::ScopedDeletePtr<mozilla::AudioCodecConfig> config(config_raw);
|
||||
|
||||
// Instantiate an appropriate conduit
|
||||
mozilla::RefPtr<mozilla::MediaSessionConduit> rx_conduit =
|
||||
pc.impl()->media()->GetConduit(level, true);
|
||||
MOZ_ASSERT_IF(rx_conduit, rx_conduit->type() == MediaSessionConduit::AUDIO);
|
||||
|
||||
// The two sides of a send/receive pair of conduits each keep a raw pointer to the other,
|
||||
// and are responsible for cleanly shutting down.
|
||||
mozilla::RefPtr<mozilla::AudioSessionConduit> conduit =
|
||||
mozilla::AudioSessionConduit::Create(static_cast<AudioSessionConduit *>(rx_conduit.get()));
|
||||
if (!conduit || conduit->ConfigureSendMediaCodec(config))
|
||||
return VCM_ERROR;
|
||||
CSFLogError(logTag, "Created audio pipeline audio level %d %d",
|
||||
attrs->audio_level, attrs->audio_level_id);
|
||||
|
||||
if (!conduit || conduit->EnableAudioLevelExtension(attrs->audio_level, attrs->audio_level_id))
|
||||
return VCM_ERROR;
|
||||
|
||||
pc.impl()->media()->AddConduit(level, false, conduit);
|
||||
mozilla::RefPtr<mozilla::MediaPipeline> pipeline =
|
||||
new mozilla::MediaPipelineTransmit(
|
||||
pc.impl()->GetHandle(),
|
||||
pc.impl()->GetMainThread().get(),
|
||||
pc.impl()->GetSTSThread(),
|
||||
stream->GetMediaStream(),
|
||||
pc_track_id,
|
||||
level,
|
||||
conduit, rtp_flow, rtcp_flow);
|
||||
|
||||
nsresult res = pipeline->Init();
|
||||
if (NS_FAILED(res)) {
|
||||
CSFLogError(logTag, "Failure initializing audio pipeline");
|
||||
return VCM_ERROR;
|
||||
}
|
||||
CSFLogDebug(logTag, "Created audio pipeline %p, conduit=%p, pc_stream=%d pc_track=%d",
|
||||
pipeline.get(), conduit.get(), pc_stream_id, pc_track_id);
|
||||
|
||||
|
||||
// Now we have all the pieces, create the pipeline
|
||||
stream->StorePipeline(pc_track_id, pipeline);
|
||||
|
||||
mediaType = "audio";
|
||||
err = vcmTxCreateAudioConduit(level, payload, pc, attrs, conduit);
|
||||
} else if (CC_IS_VIDEO(mcap_id)) {
|
||||
mozilla::VideoCodecConfig *config_raw;
|
||||
config_raw = new mozilla::VideoCodecConfig(
|
||||
payload->remote_rtp_pt,
|
||||
ccsdpCodecName(payload->codec_type),
|
||||
payload->video.rtcp_fb_types,
|
||||
payload->video.max_fs,
|
||||
payload->video.max_fr,
|
||||
pc.impl()->load_manager());
|
||||
|
||||
// Take possession of this pointer
|
||||
mozilla::ScopedDeletePtr<mozilla::VideoCodecConfig> config(config_raw);
|
||||
|
||||
// Instantiate an appropriate conduit
|
||||
mozilla::RefPtr<mozilla::MediaSessionConduit> rx_conduit =
|
||||
pc.impl()->media()->GetConduit(level, true);
|
||||
MOZ_ASSERT_IF(rx_conduit, rx_conduit->type() == MediaSessionConduit::VIDEO);
|
||||
|
||||
// The two sides of a send/receive pair of conduits each keep a raw pointer to the other,
|
||||
// and are responsible for cleanly shutting down.
|
||||
mozilla::RefPtr<mozilla::VideoSessionConduit> conduit =
|
||||
mozilla::VideoSessionConduit::Create(static_cast<VideoSessionConduit *>(rx_conduit.get()));
|
||||
|
||||
// Find the appropriate media conduit config
|
||||
if (!conduit)
|
||||
return VCM_ERROR;
|
||||
|
||||
if (vcmEnsureExternalCodec(conduit, config_raw, true))
|
||||
return VCM_ERROR;
|
||||
|
||||
if (conduit->ConfigureSendMediaCodec(config))
|
||||
return VCM_ERROR;
|
||||
|
||||
pc.impl()->media()->AddConduit(level, false, conduit);
|
||||
|
||||
// Now we have all the pieces, create the pipeline
|
||||
mozilla::RefPtr<mozilla::MediaPipeline> pipeline =
|
||||
new mozilla::MediaPipelineTransmit(
|
||||
pc.impl()->GetHandle(),
|
||||
pc.impl()->GetMainThread().get(),
|
||||
pc.impl()->GetSTSThread(),
|
||||
stream->GetMediaStream(),
|
||||
pc_track_id,
|
||||
level,
|
||||
conduit, rtp_flow, rtcp_flow);
|
||||
|
||||
nsresult res = pipeline->Init();
|
||||
if (NS_FAILED(res)) {
|
||||
CSFLogError(logTag, "Failure initializing video pipeline");
|
||||
return VCM_ERROR;
|
||||
}
|
||||
|
||||
CSFLogDebug(logTag, "Created video pipeline %p, conduit=%p, pc_stream=%d pc_track=%d",
|
||||
pipeline.get(), conduit.get(), pc_stream_id, pc_track_id);
|
||||
|
||||
stream->StorePipeline(pc_track_id, pipeline);
|
||||
mediaType = "video";
|
||||
err = vcmTxCreateVideoConduit(level, payload, pc, conduit);
|
||||
} else {
|
||||
CSFLogError(logTag, "%s: mcap_id unrecognized", __FUNCTION__);
|
||||
}
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
pc.impl()->media()->AddConduit(level, false, conduit);
|
||||
|
||||
// Now we have all the pieces, create the pipeline
|
||||
mozilla::RefPtr<mozilla::MediaPipelineTransmit> pipeline =
|
||||
new mozilla::MediaPipelineTransmit(
|
||||
pc.impl()->GetHandle(),
|
||||
pc.impl()->GetMainThread().get(),
|
||||
pc.impl()->GetSTSThread(),
|
||||
stream->GetMediaStream(),
|
||||
pc_track_id,
|
||||
level,
|
||||
conduit, rtp_flow, rtcp_flow);
|
||||
|
||||
nsresult res = pipeline->Init();
|
||||
if (NS_FAILED(res)) {
|
||||
CSFLogError(logTag, "Failure initializing %s pipeline", mediaType);
|
||||
return VCM_ERROR;
|
||||
}
|
||||
#ifdef MOZILLA_INTERNAL_API
|
||||
// implement checking for peerIdentity (where failure == black/silence)
|
||||
nsIDocument* doc = pc.impl()->GetWindow()->GetExtantDoc();
|
||||
if (doc) {
|
||||
pipeline->UpdateSinkIdentity_m(doc->NodePrincipal(), pc.impl()->GetPeerIdentity());
|
||||
} else {
|
||||
CSFLogError(logTag, "Initializing pipeline without attached doc");
|
||||
}
|
||||
#endif
|
||||
|
||||
CSFLogDebug(logTag,
|
||||
"Created %s pipeline %p, conduit=%p, pc_stream=%d pc_track=%d",
|
||||
mediaType, pipeline.get(), conduit.get(),
|
||||
pc_stream_id, pc_track_id);
|
||||
stream->StorePipeline(pc_track_id, pipeline);
|
||||
|
||||
// This tells the receive MediaPipeline (if there is one) whether we are
|
||||
// doing bundle, and if so, updates the filter. Once the filter is finalized,
|
||||
@ -2681,7 +2717,7 @@ int vcmGetVideoCodecList(int request_type)
|
||||
CSFLogDebug( logTag, "%s(codec_mask = %X)", fname, codecMask);
|
||||
|
||||
//return codecMask;
|
||||
return VCM_CODEC_RESOURCE_H264;
|
||||
return VCM_CODEC_RESOURCE_H264;
|
||||
#else
|
||||
int codecMask = VcmSIPCCBinding::getVideoCodecs();
|
||||
CSFLogDebug(logTag, "GetVideoCodecList returning %X", codecMask);
|
||||
@ -2715,11 +2751,11 @@ void vcmMediaControl(cc_call_handle_t call_handle, vcm_media_control_to_encoder
|
||||
{
|
||||
if ( to_encoder == VCM_MEDIA_CONTROL_PICTURE_FAST_UPDATE )
|
||||
{
|
||||
StreamObserver* obs = VcmSIPCCBinding::getStreamObserver();
|
||||
if (obs != nullptr)
|
||||
{
|
||||
obs->sendIFrame(call_handle);
|
||||
}
|
||||
StreamObserver* obs = VcmSIPCCBinding::getStreamObserver();
|
||||
if (obs != nullptr)
|
||||
{
|
||||
obs->sendIFrame(call_handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2851,7 +2887,7 @@ cc_boolean vcmCheckAttribs(cc_uint32_t media_type, void *sdp_p, int level, void
|
||||
switch (media_type)
|
||||
{
|
||||
case RTP_VP8:
|
||||
return TRUE;
|
||||
return TRUE;
|
||||
|
||||
case RTP_H264_P0:
|
||||
case RTP_H264_P1:
|
||||
@ -3011,7 +3047,7 @@ int vcmDtmfBurst(int digit, int duration, int direction)
|
||||
CSFLogDebug( logTag, "vcmDtmfBurst(): digit=%d duration=%d, direction=%d", digit, duration, direction);
|
||||
StreamObserver* obs = VcmSIPCCBinding::getStreamObserver();
|
||||
if(obs != nullptr)
|
||||
obs->dtmfBurst(digit, duration, direction);
|
||||
obs->dtmfBurst(digit, duration, direction);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -39,6 +39,9 @@
|
||||
#include "transportlayerice.h"
|
||||
#include "runnable_utils.h"
|
||||
#include "libyuv/convert.h"
|
||||
#ifdef MOZILLA_INTERNAL_API
|
||||
#include "mozilla/PeerIdentity.h"
|
||||
#endif
|
||||
#include "mozilla/gfx/Point.h"
|
||||
#include "mozilla/gfx/Types.h"
|
||||
|
||||
@ -653,6 +656,25 @@ nsresult MediaPipelineTransmit::Init() {
|
||||
return MediaPipeline::Init();
|
||||
}
|
||||
|
||||
#ifdef MOZILLA_INTERNAL_API
|
||||
void MediaPipelineTransmit::UpdateSinkIdentity_m(nsIPrincipal* principal,
|
||||
const PeerIdentity* sinkIdentity) {
|
||||
ASSERT_ON_THREAD(main_thread_);
|
||||
bool enableStream = principal->Subsumes(domstream_->GetPrincipal());
|
||||
if (!enableStream) {
|
||||
// first try didn't work, but there's a chance that this is still available
|
||||
// if our stream is bound to a peerIdentity, and the peer connection (our
|
||||
// sink) is bound to the same identity, then we can enable the stream
|
||||
PeerIdentity* streamIdentity = domstream_->GetPeerIdentity();
|
||||
if (sinkIdentity && streamIdentity) {
|
||||
enableStream = (*sinkIdentity == *streamIdentity);
|
||||
}
|
||||
}
|
||||
|
||||
listener_->SetEnabled(enableStream);
|
||||
}
|
||||
#endif
|
||||
|
||||
nsresult MediaPipelineTransmit::TransportReady_s(TransportInfo &info) {
|
||||
ASSERT_ON_THREAD(sts_thread_);
|
||||
// Call base ready function.
|
||||
@ -661,7 +683,6 @@ nsresult MediaPipelineTransmit::TransportReady_s(TransportInfo &info) {
|
||||
// Should not be set for a transmitter
|
||||
MOZ_ASSERT(!possible_bundle_rtp_);
|
||||
if (&info == &rtp_) {
|
||||
// TODO(ekr@rtfm.com): Move onto MSG thread.
|
||||
listener_->SetActive(true);
|
||||
}
|
||||
|
||||
@ -935,6 +956,7 @@ NewData(MediaStreamGraph* graph, TrackID tid,
|
||||
// Ignore data in case we have a muxed stream
|
||||
return;
|
||||
}
|
||||
|
||||
AudioSegment* audio = const_cast<AudioSegment *>(
|
||||
static_cast<const AudioSegment *>(&media));
|
||||
|
||||
@ -950,6 +972,7 @@ NewData(MediaStreamGraph* graph, TrackID tid,
|
||||
// Ignore data in case we have a muxed stream
|
||||
return;
|
||||
}
|
||||
|
||||
VideoSegment* video = const_cast<VideoSegment *>(
|
||||
static_cast<const VideoSegment *>(&media));
|
||||
|
||||
@ -972,7 +995,7 @@ void MediaPipelineTransmit::PipelineListener::ProcessAudioChunk(
|
||||
// TODO(ekr@rtfm.com): Do more than one channel
|
||||
nsAutoArrayPtr<int16_t> samples(new int16_t[chunk.mDuration]);
|
||||
|
||||
if (chunk.mBuffer) {
|
||||
if (enabled_ && chunk.mBuffer) {
|
||||
switch (chunk.mBufferFormat) {
|
||||
case AUDIO_FORMAT_FLOAT32:
|
||||
{
|
||||
@ -1081,7 +1104,7 @@ void MediaPipelineTransmit::PipelineListener::ProcessVideoChunk(
|
||||
return;
|
||||
}
|
||||
|
||||
if (chunk.mFrame.GetForceBlack()) {
|
||||
if (!enabled_ || chunk.mFrame.GetForceBlack()) {
|
||||
uint32_t yPlaneLen = size.width*size.height;
|
||||
uint32_t cbcrPlaneLen = yPlaneLen/2;
|
||||
uint32_t length = yPlaneLen + cbcrPlaneLen;
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "MediaPipelineFilter.h"
|
||||
#include "AudioSegment.h"
|
||||
#include "mozilla/ReentrantMonitor.h"
|
||||
#include "mozilla/Atomics.h"
|
||||
#include "SrtpFlow.h"
|
||||
#include "databuffer.h"
|
||||
#include "runnable_utils.h"
|
||||
@ -34,6 +35,8 @@
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class PeerIdentity;
|
||||
|
||||
// A class that represents the pipeline of audio and video
|
||||
// The dataflow looks like:
|
||||
//
|
||||
@ -354,7 +357,7 @@ private:
|
||||
// A specialization of pipeline for reading from an input device
|
||||
// and transmitting to the network.
|
||||
class MediaPipelineTransmit : public MediaPipeline {
|
||||
public:
|
||||
public:
|
||||
// Set rtcp_transport to nullptr to use rtcp-mux
|
||||
MediaPipelineTransmit(const std::string& pc,
|
||||
nsCOMPtr<nsIEventTarget> main_thread,
|
||||
@ -373,7 +376,14 @@ class MediaPipelineTransmit : public MediaPipeline {
|
||||
{}
|
||||
|
||||
// Initialize (stuff here may fail)
|
||||
virtual nsresult Init();
|
||||
virtual nsresult Init() MOZ_OVERRIDE;
|
||||
|
||||
#ifdef MOZILLA_INTERNAL_API
|
||||
// when the principal of the PeerConnection changes, it calls through to here
|
||||
// so that we can determine whether to enable stream transmission
|
||||
virtual void UpdateSinkIdentity_m(nsIPrincipal* principal,
|
||||
const PeerIdentity* sinkIdentity);
|
||||
#endif
|
||||
|
||||
// Called on the main thread.
|
||||
virtual void DetachMediaStream() {
|
||||
@ -395,6 +405,7 @@ class MediaPipelineTransmit : public MediaPipeline {
|
||||
PipelineListener(const RefPtr<MediaSessionConduit>& conduit)
|
||||
: conduit_(conduit),
|
||||
active_(false),
|
||||
enabled_(false),
|
||||
direct_connect_(false),
|
||||
samples_10ms_buffer_(nullptr),
|
||||
buffer_current_(0),
|
||||
@ -416,11 +427,8 @@ class MediaPipelineTransmit : public MediaPipeline {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// XXX. This is not thread-safe but the hazard is just
|
||||
// that active_ = true takes a while to propagate. Revisit
|
||||
// when 823600 lands.
|
||||
void SetActive(bool active) { active_ = active; }
|
||||
void SetEnabled(bool enabled) { enabled_ = enabled; }
|
||||
|
||||
// Implement MediaStreamListener
|
||||
virtual void NotifyQueuedTrackChanges(MediaStreamGraph* graph, TrackID tid,
|
||||
@ -451,9 +459,16 @@ class MediaPipelineTransmit : public MediaPipeline {
|
||||
TrackRate rate, VideoChunk& chunk);
|
||||
#endif
|
||||
RefPtr<MediaSessionConduit> conduit_;
|
||||
volatile bool active_;
|
||||
|
||||
// active is true if there is a transport to send on
|
||||
mozilla::Atomic<bool> active_;
|
||||
// enabled is true if the media access control permits sending
|
||||
// actual content; when false you get black/silence
|
||||
mozilla::Atomic<bool> enabled_;
|
||||
|
||||
bool direct_connect_;
|
||||
|
||||
|
||||
// These vars handle breaking audio samples into exact 10ms chunks:
|
||||
// The buffer of 10ms audio samples that we will send once full
|
||||
// (can be carried over from one call to another).
|
||||
@ -549,7 +564,7 @@ class MediaPipelineReceiveAudio : public MediaPipelineReceive {
|
||||
stream_ = nullptr;
|
||||
}
|
||||
|
||||
virtual nsresult Init();
|
||||
virtual nsresult Init() MOZ_OVERRIDE;
|
||||
|
||||
private:
|
||||
// Separate class to allow ref counting
|
||||
@ -623,7 +638,7 @@ class MediaPipelineReceiveVideo : public MediaPipelineReceive {
|
||||
stream_ = nullptr;
|
||||
}
|
||||
|
||||
virtual nsresult Init();
|
||||
virtual nsresult Init() MOZ_OVERRIDE;
|
||||
|
||||
private:
|
||||
class PipelineRenderer : public VideoRenderer {
|
||||
|
@ -62,6 +62,8 @@
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsIDOMDataChannel.h"
|
||||
#include "nsIDOMLocation.h"
|
||||
#include "nsNullPrincipal.h"
|
||||
#include "mozilla/PeerIdentity.h"
|
||||
#include "mozilla/dom/RTCConfigurationBinding.h"
|
||||
#include "mozilla/dom/RTCStatsReportBinding.h"
|
||||
#include "mozilla/dom/RTCPeerConnectionBinding.h"
|
||||
@ -474,6 +476,7 @@ PeerConnectionImpl::PeerConnectionImpl(const GlobalObject* aGlobal)
|
||||
, mSignalingState(PCImplSignalingState::SignalingStable)
|
||||
, mIceConnectionState(PCImplIceConnectionState::New)
|
||||
, mIceGatheringState(PCImplIceGatheringState::New)
|
||||
, mDtlsConnected(false)
|
||||
, mWindow(nullptr)
|
||||
, mIdentity(nullptr)
|
||||
, mSTSThread(nullptr)
|
||||
@ -541,18 +544,27 @@ PeerConnectionImpl::~PeerConnectionImpl()
|
||||
}
|
||||
|
||||
already_AddRefed<DOMMediaStream>
|
||||
PeerConnectionImpl::MakeMediaStream(nsPIDOMWindow* aWindow,
|
||||
uint32_t aHint)
|
||||
PeerConnectionImpl::MakeMediaStream(uint32_t aHint)
|
||||
{
|
||||
nsRefPtr<DOMMediaStream> stream =
|
||||
DOMMediaStream::CreateSourceStream(aWindow, aHint);
|
||||
DOMMediaStream::CreateSourceStream(GetWindow(), aHint);
|
||||
|
||||
#ifdef MOZILLA_INTERNAL_API
|
||||
nsIDocument* doc = aWindow->GetExtantDoc();
|
||||
if (!doc) {
|
||||
return nullptr;
|
||||
}
|
||||
// Make the stream data (audio/video samples) accessible to the receiving page.
|
||||
stream->CombineWithPrincipal(doc->NodePrincipal());
|
||||
// We're only certain that privacy hasn't been requested if we're connected.
|
||||
if (mDtlsConnected && !PrivacyRequested()) {
|
||||
nsIDocument* doc = GetWindow()->GetExtantDoc();
|
||||
if (!doc) {
|
||||
return nullptr;
|
||||
}
|
||||
stream->CombineWithPrincipal(doc->NodePrincipal());
|
||||
} else {
|
||||
// we're either certain that we need isolation for the streams, OR
|
||||
// we're not sure and we can fix the stream in SetDtlsConnected
|
||||
nsCOMPtr<nsIPrincipal> principal =
|
||||
do_CreateInstance(NS_NULLPRINCIPAL_CONTRACTID);
|
||||
stream->CombineWithPrincipal(principal);
|
||||
}
|
||||
#endif
|
||||
|
||||
CSFLogDebug(logTag, "Created media stream %p, inner: %p", stream.get(), stream->GetStream());
|
||||
@ -571,7 +583,7 @@ PeerConnectionImpl::CreateRemoteSourceStreamInfo(nsRefPtr<RemoteSourceStreamInfo
|
||||
// needs to actually propagate a hint for local streams.
|
||||
// TODO(ekr@rtfm.com): Clean up when we have explicit track lists.
|
||||
// See bug 834835.
|
||||
nsRefPtr<DOMMediaStream> stream = MakeMediaStream(mWindow, 0);
|
||||
nsRefPtr<DOMMediaStream> stream = MakeMediaStream(0);
|
||||
if (!stream) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
@ -733,6 +745,10 @@ PeerConnectionImpl::Initialize(PeerConnectionObserver& aObserver,
|
||||
mWindow = aWindow;
|
||||
NS_ENSURE_STATE(mWindow);
|
||||
|
||||
if (!aRTCConfiguration->mPeerIdentity.IsEmpty()) {
|
||||
mPeerIdentity = new PeerIdentity(aRTCConfiguration->mPeerIdentity);
|
||||
mPrivacyRequested = true;
|
||||
}
|
||||
#endif // MOZILLA_INTERNAL_API
|
||||
|
||||
PRTime timestamp = PR_Now();
|
||||
@ -924,7 +940,7 @@ PeerConnectionImpl::CreateFakeMediaStream(uint32_t aHint, nsIDOMMediaStream** aR
|
||||
aHint &= ~MEDIA_STREAM_MUTE;
|
||||
}
|
||||
|
||||
nsRefPtr<DOMMediaStream> stream = MakeMediaStream(mWindow, aHint);
|
||||
nsRefPtr<DOMMediaStream> stream = MakeMediaStream(aHint);
|
||||
if (!stream) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
@ -1250,6 +1266,17 @@ PeerConnectionImpl::SetLocalDescription(int32_t aAction, const char* aSDP)
|
||||
mTimeCard = nullptr;
|
||||
STAMP_TIMECARD(tc, "Set Local Description");
|
||||
|
||||
#ifdef MOZILLA_INTERNAL_API
|
||||
nsIDocument* doc = GetWindow()->GetExtantDoc();
|
||||
bool isolated = true;
|
||||
if (doc) {
|
||||
isolated = mMedia->AnyLocalStreamIsolated(doc->NodePrincipal());
|
||||
} else {
|
||||
CSFLogInfo(logTag, "%s - no document, failing safe", __FUNCTION__);
|
||||
}
|
||||
mPrivacyRequested = mPrivacyRequested || isolated;
|
||||
#endif
|
||||
|
||||
mLocalRequestedSDP = aSDP;
|
||||
mInternal->mCall->setLocalDescription((cc_jsep_action_t)aAction,
|
||||
mLocalRequestedSDP, tc);
|
||||
@ -1385,16 +1412,67 @@ PeerConnectionImpl::CloseStreams() {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
#ifdef MOZILLA_INTERNAL_API
|
||||
nsresult
|
||||
PeerConnectionImpl::SetPeerIdentity(const nsAString& aPeerIdentity)
|
||||
{
|
||||
PC_AUTO_ENTER_API_CALL(true);
|
||||
MOZ_ASSERT(!aPeerIdentity.IsEmpty());
|
||||
|
||||
// once set, this can't be changed
|
||||
if (mPeerIdentity) {
|
||||
if (!mPeerIdentity->Equals(aPeerIdentity)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
} else {
|
||||
mPeerIdentity = new PeerIdentity(aPeerIdentity);
|
||||
nsIDocument* doc = GetWindow()->GetExtantDoc();
|
||||
if (!doc) {
|
||||
CSFLogInfo(logTag, "Can't update principal on streams; document gone");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
mMedia->UpdateSinkIdentity_m(doc->NodePrincipal(), mPeerIdentity);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
nsresult
|
||||
PeerConnectionImpl::SetDtlsConnected(bool aPrivacyRequested)
|
||||
{
|
||||
PC_AUTO_ENTER_API_CALL(false);
|
||||
|
||||
// For this, as with mPrivacyRequested, once we've connected to a peer, we
|
||||
// fixate on that peer. Dealing with multiple peers or connections is more
|
||||
// than this run-down wreck of an object can handle.
|
||||
// Besides, this is only used to say if we have been connected ever.
|
||||
#ifdef MOZILLA_INTERNAL_API
|
||||
if (!mPrivacyRequested && !aPrivacyRequested && !mDtlsConnected) {
|
||||
// now we know that privacy isn't needed for sure
|
||||
nsIDocument* doc = GetWindow()->GetExtantDoc();
|
||||
if (!doc) {
|
||||
CSFLogInfo(logTag, "Can't update principal on streams; document gone");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
mMedia->UpdateRemoteStreamPrincipals_m(doc->NodePrincipal());
|
||||
}
|
||||
#endif
|
||||
mDtlsConnected = true;
|
||||
mPrivacyRequested = mPrivacyRequested || aPrivacyRequested;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
PeerConnectionImpl::AddStream(DOMMediaStream &aMediaStream,
|
||||
const MediaConstraintsInternal& aConstraints)
|
||||
{
|
||||
return AddStream(aMediaStream, MediaConstraintsExternal(aConstraints));
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PeerConnectionImpl::AddStream(DOMMediaStream& aMediaStream,
|
||||
const MediaConstraintsExternal& aConstraints) {
|
||||
nsresult
|
||||
PeerConnectionImpl::AddStream(DOMMediaStream &aMediaStream,
|
||||
const MediaConstraintsExternal& aConstraints)
|
||||
{
|
||||
PC_AUTO_ENTER_API_CALL(true);
|
||||
|
||||
uint32_t hints = aMediaStream.GetHintContents();
|
||||
@ -1421,8 +1499,9 @@ PeerConnectionImpl::AddStream(DOMMediaStream& aMediaStream,
|
||||
|
||||
uint32_t stream_id;
|
||||
nsresult res = mMedia->AddStream(&aMediaStream, &stream_id);
|
||||
if (NS_FAILED(res))
|
||||
if (NS_FAILED(res)) {
|
||||
return res;
|
||||
}
|
||||
|
||||
// TODO(ekr@rtfm.com): these integers should be the track IDs
|
||||
if (hints & DOMMediaStream::HINT_CONTENTS_AUDIO) {
|
||||
|
@ -36,6 +36,8 @@
|
||||
#include "VideoSegment.h"
|
||||
#include "nsNSSShutDown.h"
|
||||
#include "mozilla/dom/RTCStatsReportBinding.h"
|
||||
#include "nsIPrincipal.h"
|
||||
#include "mozilla/PeerIdentity.h"
|
||||
#endif
|
||||
|
||||
namespace test {
|
||||
@ -114,6 +116,9 @@ using mozilla::DtlsIdentity;
|
||||
using mozilla::ErrorResult;
|
||||
using mozilla::NrIceStunServer;
|
||||
using mozilla::NrIceTurnServer;
|
||||
#ifdef MOZILLA_INTERNAL_API
|
||||
using mozilla::PeerIdentity;
|
||||
#endif
|
||||
|
||||
class PeerConnectionWrapper;
|
||||
class PeerConnectionMedia;
|
||||
@ -226,8 +231,7 @@ public:
|
||||
static PeerConnectionImpl* CreatePeerConnection();
|
||||
static nsresult ConvertRTCConfiguration(const RTCConfiguration& aSrc,
|
||||
IceConfiguration *aDst);
|
||||
static already_AddRefed<DOMMediaStream> MakeMediaStream(nsPIDOMWindow* aWindow,
|
||||
uint32_t aHint);
|
||||
already_AddRefed<DOMMediaStream> MakeMediaStream(uint32_t aHint);
|
||||
|
||||
nsresult CreateRemoteSourceStreamInfo(nsRefPtr<RemoteSourceStreamInfo>* aInfo);
|
||||
|
||||
@ -277,7 +281,7 @@ public:
|
||||
return mSTSThread;
|
||||
}
|
||||
|
||||
// Get the DTLS identity
|
||||
// Get the DTLS identity (local side)
|
||||
mozilla::RefPtr<DtlsIdentity> const GetIdentity() const;
|
||||
std::string GetFingerprint() const;
|
||||
std::string GetFingerprintAlgorithm() const;
|
||||
@ -371,8 +375,8 @@ public:
|
||||
rv = AddStream(aMediaStream, aConstraints);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP AddStream(DOMMediaStream & aMediaStream,
|
||||
const MediaConstraintsExternal& aConstraints);
|
||||
nsresult AddStream(DOMMediaStream &aMediaStream,
|
||||
const MediaConstraintsExternal& aConstraints);
|
||||
|
||||
NS_IMETHODIMP_TO_ERRORRESULT(RemoveStream, ErrorResult &rv,
|
||||
DOMMediaStream& aMediaStream)
|
||||
@ -380,6 +384,28 @@ public:
|
||||
rv = RemoveStream(aMediaStream);
|
||||
}
|
||||
|
||||
|
||||
nsresult GetPeerIdentity(nsAString& peerIdentity)
|
||||
{
|
||||
#ifdef MOZILLA_INTERNAL_API
|
||||
if (mPeerIdentity) {
|
||||
peerIdentity = mPeerIdentity->ToString();
|
||||
return NS_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
peerIdentity.SetIsVoid(true);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
#ifdef MOZILLA_INTERNAL_API
|
||||
const PeerIdentity* GetPeerIdentity() const { return mPeerIdentity; }
|
||||
nsresult SetPeerIdentity(const nsAString& peerIdentity);
|
||||
#endif
|
||||
|
||||
// this method checks to see if we've made a promise to protect media.
|
||||
bool PrivacyRequested() const { return mPrivacyRequested; }
|
||||
|
||||
NS_IMETHODIMP GetFingerprint(char** fingerprint);
|
||||
void GetFingerprint(nsAString& fingerprint)
|
||||
{
|
||||
@ -513,6 +539,8 @@ public:
|
||||
void SetSignalingState_m(mozilla::dom::PCImplSignalingState aSignalingState);
|
||||
|
||||
bool IsClosed() const;
|
||||
// called when DTLS connects; we only need this once
|
||||
nsresult SetDtlsConnected(bool aPrivacyRequested);
|
||||
|
||||
bool HasMedia() const;
|
||||
|
||||
@ -608,6 +636,10 @@ private:
|
||||
mozilla::dom::PCImplIceConnectionState mIceConnectionState;
|
||||
mozilla::dom::PCImplIceGatheringState mIceGatheringState;
|
||||
|
||||
// DTLS
|
||||
// this is true if we have been connected ever, see SetDtlsConnected
|
||||
bool mDtlsConnected;
|
||||
|
||||
nsCOMPtr<nsIThread> mThread;
|
||||
// TODO: Remove if we ever properly wire PeerConnection for cycle-collection.
|
||||
nsWeakPtr mPCObserver;
|
||||
@ -625,8 +657,20 @@ private:
|
||||
std::string mFingerprint;
|
||||
std::string mRemoteFingerprint;
|
||||
|
||||
// The DTLS identity
|
||||
// identity-related fields
|
||||
mozilla::RefPtr<DtlsIdentity> mIdentity;
|
||||
#ifdef MOZILLA_INTERNAL_API
|
||||
// The entity on the other end of the peer-to-peer connection;
|
||||
// void if they are not yet identified, and no constraint has been set
|
||||
nsAutoPtr<PeerIdentity> mPeerIdentity;
|
||||
#endif
|
||||
// Whether an app should be prevented from accessing media produced by the PC
|
||||
// If this is true, then media will not be sent until mPeerIdentity matches
|
||||
// local streams PeerIdentity; and remote streams are protected from content
|
||||
//
|
||||
// This can be false if mPeerIdentity is set, in the case where identity is
|
||||
// provided, but the media is not protected from the app on either side
|
||||
bool mPrivacyRequested;
|
||||
|
||||
// A handle to refer to this PC with
|
||||
std::string mHandle;
|
||||
@ -642,7 +686,7 @@ private:
|
||||
|
||||
#ifdef MOZILLA_INTERNAL_API
|
||||
// DataConnection that's used to get all the DataChannels
|
||||
nsRefPtr<mozilla::DataChannelConnection> mDataConnection;
|
||||
nsRefPtr<mozilla::DataChannelConnection> mDataConnection;
|
||||
#endif
|
||||
|
||||
nsRefPtr<PeerConnectionMedia> mMedia;
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include "AudioConduit.h"
|
||||
#include "VideoConduit.h"
|
||||
#include "runnable_utils.h"
|
||||
#include "transportlayerdtls.h"
|
||||
|
||||
#ifdef MOZILLA_INTERNAL_API
|
||||
#include "MediaStreamList.h"
|
||||
@ -137,7 +138,6 @@ PeerConnectionImpl* PeerConnectionImpl::CreatePeerConnection()
|
||||
|
||||
PeerConnectionMedia::PeerConnectionMedia(PeerConnectionImpl *parent)
|
||||
: mParent(parent),
|
||||
mLocalSourceStreamsLock("PeerConnectionMedia.mLocalSourceStreamsLock"),
|
||||
mIceCtx(nullptr),
|
||||
mDNSResolver(new mozilla::NrIceResolver()),
|
||||
mMainThread(mParent->GetMainThread()),
|
||||
@ -235,6 +235,8 @@ nsresult PeerConnectionMedia::Init(const std::vector<NrIceStunServer>& stun_serv
|
||||
nsresult
|
||||
PeerConnectionMedia::AddStream(nsIDOMMediaStream* aMediaStream, uint32_t *stream_id)
|
||||
{
|
||||
ASSERT_ON_THREAD(mMainThread);
|
||||
|
||||
if (!aMediaStream) {
|
||||
CSFLogError(logTag, "%s - aMediaStream is NULL", __FUNCTION__);
|
||||
return NS_ERROR_FAILURE;
|
||||
@ -263,7 +265,6 @@ PeerConnectionMedia::AddStream(nsIDOMMediaStream* aMediaStream, uint32_t *stream
|
||||
// allow one of each.
|
||||
// TODO(ekr@rtfm.com): remove this when multiple of each stream
|
||||
// is allowed
|
||||
mozilla::MutexAutoLock lock(mLocalSourceStreamsLock);
|
||||
for (uint32_t u = 0; u < mLocalSourceStreams.Length(); u++) {
|
||||
nsRefPtr<LocalSourceStreamInfo> localSourceStream = mLocalSourceStreams[u];
|
||||
|
||||
@ -295,13 +296,13 @@ nsresult
|
||||
PeerConnectionMedia::RemoveStream(nsIDOMMediaStream* aMediaStream, uint32_t *stream_id)
|
||||
{
|
||||
MOZ_ASSERT(aMediaStream);
|
||||
ASSERT_ON_THREAD(mMainThread);
|
||||
|
||||
DOMMediaStream* stream = static_cast<DOMMediaStream*>(aMediaStream);
|
||||
|
||||
CSFLogDebug(logTag, "%s: MediaStream: %p",
|
||||
__FUNCTION__, aMediaStream);
|
||||
|
||||
mozilla::MutexAutoLock lock(mLocalSourceStreamsLock);
|
||||
for (uint32_t u = 0; u < mLocalSourceStreams.Length(); u++) {
|
||||
nsRefPtr<LocalSourceStreamInfo> localSourceStream = mLocalSourceStreams[u];
|
||||
if (localSourceStream->GetMediaStream() == stream) {
|
||||
@ -357,6 +358,11 @@ PeerConnectionMedia::ShutdownMediaTransport_s()
|
||||
|
||||
CSFLogDebug(logTag, "%s: ", __FUNCTION__);
|
||||
|
||||
// Here we access m{Local|Remote}SourceStreams off the main thread.
|
||||
// That's OK because by here PeerConnectionImpl has forgotten about us,
|
||||
// so there is no chance of getting a call in here from outside.
|
||||
// The dispatches from SelfDestruct() and to SelfDestruct_m() provide
|
||||
// memory barriers that protect us from badness.
|
||||
for (uint32_t i=0; i < mLocalSourceStreams.Length(); ++i) {
|
||||
mLocalSourceStreams[i]->DetachTransport_s();
|
||||
}
|
||||
@ -377,6 +383,7 @@ PeerConnectionMedia::ShutdownMediaTransport_s()
|
||||
LocalSourceStreamInfo*
|
||||
PeerConnectionMedia::GetLocalStream(int aIndex)
|
||||
{
|
||||
ASSERT_ON_THREAD(mMainThread);
|
||||
if(aIndex < 0 || aIndex >= (int) mLocalSourceStreams.Length()) {
|
||||
return nullptr;
|
||||
}
|
||||
@ -388,6 +395,7 @@ PeerConnectionMedia::GetLocalStream(int aIndex)
|
||||
RemoteSourceStreamInfo*
|
||||
PeerConnectionMedia::GetRemoteStream(int aIndex)
|
||||
{
|
||||
ASSERT_ON_THREAD(mMainThread);
|
||||
if(aIndex < 0 || aIndex >= (int) mRemoteSourceStreams.Length()) {
|
||||
return nullptr;
|
||||
}
|
||||
@ -472,6 +480,7 @@ nsresult
|
||||
PeerConnectionMedia::AddRemoteStream(nsRefPtr<RemoteSourceStreamInfo> aInfo,
|
||||
int *aIndex)
|
||||
{
|
||||
ASSERT_ON_THREAD(mMainThread);
|
||||
MOZ_ASSERT(aIndex);
|
||||
|
||||
*aIndex = mRemoteSourceStreams.Length();
|
||||
@ -524,9 +533,113 @@ PeerConnectionMedia::IceStreamReady(NrIceMediaStream *aStream)
|
||||
CSFLogDebug(logTag, "%s: %s", __FUNCTION__, aStream->name().c_str());
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
LocalSourceStreamInfo::StorePipeline(int aTrack,
|
||||
mozilla::RefPtr<mozilla::MediaPipeline> aPipeline)
|
||||
PeerConnectionMedia::DtlsConnected(TransportLayer *dtlsLayer,
|
||||
TransportLayer::State state)
|
||||
{
|
||||
dtlsLayer->SignalStateChange.disconnect(this);
|
||||
|
||||
bool privacyRequested = false;
|
||||
// TODO (Bug 952678) set privacy mode, ask the DTLS layer about that
|
||||
GetMainThread()->Dispatch(
|
||||
WrapRunnable(mParent, &PeerConnectionImpl::SetDtlsConnected, privacyRequested),
|
||||
NS_DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
void
|
||||
PeerConnectionMedia::AddTransportFlow(int aIndex, bool aRtcp,
|
||||
const RefPtr<TransportFlow> &aFlow)
|
||||
{
|
||||
int index_inner = aIndex * 2 + (aRtcp ? 1 : 0);
|
||||
|
||||
MOZ_ASSERT(!mTransportFlows[index_inner]);
|
||||
mTransportFlows[index_inner] = aFlow;
|
||||
|
||||
GetSTSThread()->Dispatch(
|
||||
WrapRunnable(this, &PeerConnectionMedia::ConnectDtlsListener_s, aFlow),
|
||||
NS_DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
void
|
||||
PeerConnectionMedia::ConnectDtlsListener_s(const RefPtr<TransportFlow>& aFlow)
|
||||
{
|
||||
TransportLayer* dtls = aFlow->GetLayer(TransportLayerDtls::ID());
|
||||
dtls->SignalStateChange.connect(this, &PeerConnectionMedia::DtlsConnected);
|
||||
}
|
||||
|
||||
#ifdef MOZILLA_INTERNAL_API
|
||||
/**
|
||||
* Tells you if any local streams is isolated. Obviously, we want all the
|
||||
* streams to be isolated equally so that they can all be sent or not. But we
|
||||
* can't make that determination for certain, because stream principals change.
|
||||
* Therefore, we check once when we are setting a local description and that
|
||||
* determines if we flip the "privacy requested" bit on. If a stream cannot be
|
||||
* sent, then we'll be sending black/silence later; maybe this will correct
|
||||
* itself and we can send actual content.
|
||||
*
|
||||
* @param scriptPrincipal the principal
|
||||
* @returns true if any stream is isolated
|
||||
*/
|
||||
bool
|
||||
PeerConnectionMedia::AnyLocalStreamIsolated(nsIPrincipal *scriptPrincipal) const
|
||||
{
|
||||
ASSERT_ON_THREAD(mMainThread);
|
||||
|
||||
for (uint32_t u = 0; u < mLocalSourceStreams.Length(); u++) {
|
||||
// check if we should be asking for a private call for this stream
|
||||
DOMMediaStream* stream = mLocalSourceStreams[u]->GetMediaStream();
|
||||
if (!scriptPrincipal->Subsumes(stream->GetPrincipal())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
PeerConnectionMedia::UpdateRemoteStreamPrincipals_m(nsIPrincipal* aPrincipal)
|
||||
{
|
||||
ASSERT_ON_THREAD(mMainThread);
|
||||
|
||||
for (uint32_t u = 0; u < mRemoteSourceStreams.Length(); u++) {
|
||||
mRemoteSourceStreams[u]->UpdatePrincipal_m(aPrincipal);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
PeerConnectionMedia::UpdateSinkIdentity_m(nsIPrincipal* aPrincipal,
|
||||
const PeerIdentity* aSinkIdentity)
|
||||
{
|
||||
ASSERT_ON_THREAD(mMainThread);
|
||||
|
||||
for (uint32_t u = 0; u < mLocalSourceStreams.Length(); u++) {
|
||||
mLocalSourceStreams[u]->UpdateSinkIdentity_m(aPrincipal, aSinkIdentity);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
LocalSourceStreamInfo::UpdateSinkIdentity_m(nsIPrincipal* aPrincipal,
|
||||
const PeerIdentity* aSinkIdentity)
|
||||
{
|
||||
for (auto it = mPipelines.begin(); it != mPipelines.end(); ++it) {
|
||||
MediaPipelineTransmit* pipeline =
|
||||
static_cast<MediaPipelineTransmit*>((*it).second.get());
|
||||
pipeline->UpdateSinkIdentity_m(aPrincipal, aSinkIdentity);
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteSourceStreamInfo::UpdatePrincipal_m(nsIPrincipal* aPrincipal)
|
||||
{
|
||||
// this blasts away the existing principal
|
||||
// we only do this when we become certain that the stream is safe to make
|
||||
// accessible to the script principal
|
||||
mMediaStream->SetPrincipal(aPrincipal);
|
||||
}
|
||||
#endif // MOZILLA_INTERNAL_API
|
||||
|
||||
void
|
||||
LocalSourceStreamInfo::StorePipeline(
|
||||
int aTrack, mozilla::RefPtr<mozilla::MediaPipelineTransmit> aPipeline)
|
||||
{
|
||||
MOZ_ASSERT(mPipelines.find(aTrack) == mPipelines.end());
|
||||
if (mPipelines.find(aTrack) != mPipelines.end()) {
|
||||
@ -539,9 +652,9 @@ LocalSourceStreamInfo::StorePipeline(int aTrack,
|
||||
}
|
||||
|
||||
void
|
||||
RemoteSourceStreamInfo::StorePipeline(int aTrack,
|
||||
bool aIsVideo,
|
||||
mozilla::RefPtr<mozilla::MediaPipeline> aPipeline)
|
||||
RemoteSourceStreamInfo::StorePipeline(
|
||||
int aTrack, bool aIsVideo,
|
||||
mozilla::RefPtr<mozilla::MediaPipelineReceive> aPipeline)
|
||||
{
|
||||
MOZ_ASSERT(mPipelines.find(aTrack) == mPipelines.end());
|
||||
if (mPipelines.find(aTrack) != mPipelines.end()) {
|
||||
|
@ -31,8 +31,11 @@
|
||||
#include "VideoSegment.h"
|
||||
#endif
|
||||
|
||||
class nsIPrincipal;
|
||||
|
||||
namespace mozilla {
|
||||
class DataChannel;
|
||||
class PeerIdentity;
|
||||
namespace dom {
|
||||
class RTCInboundRTPStreamStats;
|
||||
class RTCOutboundRTPStreamStats;
|
||||
@ -180,7 +183,7 @@ public:
|
||||
}
|
||||
|
||||
SourceStreamInfo(already_AddRefed<DOMMediaStream>& aMediaStream,
|
||||
PeerConnectionMedia *aParent)
|
||||
PeerConnectionMedia *aParent)
|
||||
: mMediaStream(aMediaStream),
|
||||
mParent(aParent) {
|
||||
MOZ_ASSERT(mMediaStream);
|
||||
@ -215,7 +218,12 @@ public:
|
||||
DOMMediaStream* GetMediaStream() {
|
||||
return mMediaStream;
|
||||
}
|
||||
void StorePipeline(int aTrack, mozilla::RefPtr<mozilla::MediaPipeline> aPipeline);
|
||||
void StorePipeline(int aTrack,
|
||||
mozilla::RefPtr<mozilla::MediaPipelineTransmit> aPipeline);
|
||||
|
||||
#ifdef MOZILLA_INTERNAL_API
|
||||
void UpdateSinkIdentity_m(nsIPrincipal* aPrincipal, const PeerIdentity* aSinkIdentity);
|
||||
#endif
|
||||
|
||||
void ExpectAudio(const mozilla::TrackID);
|
||||
void ExpectVideo(const mozilla::TrackID);
|
||||
@ -243,13 +251,17 @@ class RemoteSourceStreamInfo : public SourceStreamInfo {
|
||||
return mMediaStream;
|
||||
}
|
||||
void StorePipeline(int aTrack, bool aIsVideo,
|
||||
mozilla::RefPtr<mozilla::MediaPipeline> aPipeline);
|
||||
mozilla::RefPtr<mozilla::MediaPipelineReceive> aPipeline);
|
||||
|
||||
bool SetUsingBundle_m(int aLevel, bool decision);
|
||||
|
||||
void DetachTransport_s();
|
||||
void DetachMedia_m();
|
||||
|
||||
#ifdef MOZILLA_INTERNAL_API
|
||||
void UpdatePrincipal_m(nsIPrincipal* aPrincipal);
|
||||
#endif
|
||||
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RemoteSourceStreamInfo)
|
||||
|
||||
public:
|
||||
@ -283,10 +295,10 @@ class PeerConnectionMedia : public sigslot::has_slots<> {
|
||||
return mIceStreams.size();
|
||||
}
|
||||
|
||||
// Add a stream
|
||||
// Add a stream (main thread only)
|
||||
nsresult AddStream(nsIDOMMediaStream* aMediaStream, uint32_t *stream_id);
|
||||
|
||||
// Remove a stream
|
||||
// Remove a stream (main thread only)
|
||||
nsresult RemoveStream(nsIDOMMediaStream* aMediaStream, uint32_t *stream_id);
|
||||
|
||||
// Get a specific local stream
|
||||
@ -312,6 +324,19 @@ class PeerConnectionMedia : public sigslot::has_slots<> {
|
||||
nsresult AddRemoteStream(nsRefPtr<RemoteSourceStreamInfo> aInfo, int *aIndex);
|
||||
nsresult AddRemoteStreamHint(int aIndex, bool aIsVideo);
|
||||
|
||||
#ifdef MOZILLA_INTERNAL_API
|
||||
// In cases where the peer isn't yet identified, we disable the pipeline (not
|
||||
// the stream, that would potentially affect others), so that it sends
|
||||
// black/silence. Once the peer is identified, re-enable those streams.
|
||||
void UpdateSinkIdentity_m(nsIPrincipal* aPrincipal, const PeerIdentity* aSinkIdentity);
|
||||
// this determines if any stream is isolated, given the current
|
||||
// document (or script) principal
|
||||
bool AnyLocalStreamIsolated(nsIPrincipal *scriptPrincipal) const;
|
||||
// When we finally learn who is on the other end, we need to change the ownership
|
||||
// on streams
|
||||
void UpdateRemoteStreamPrincipals_m(nsIPrincipal* aPrincipal);
|
||||
#endif
|
||||
|
||||
const nsCOMPtr<nsIThread>& GetMainThread() const { return mMainThread; }
|
||||
const nsCOMPtr<nsIEventTarget>& GetSTSThread() const { return mSTSThread; }
|
||||
|
||||
@ -329,12 +354,10 @@ class PeerConnectionMedia : public sigslot::has_slots<> {
|
||||
|
||||
// Add a transport flow
|
||||
void AddTransportFlow(int aIndex, bool aRtcp,
|
||||
const mozilla::RefPtr<mozilla::TransportFlow> &aFlow) {
|
||||
int index_inner = aIndex * 2 + (aRtcp ? 1 : 0);
|
||||
|
||||
MOZ_ASSERT(!mTransportFlows[index_inner]);
|
||||
mTransportFlows[index_inner] = aFlow;
|
||||
}
|
||||
const mozilla::RefPtr<mozilla::TransportFlow> &aFlow);
|
||||
void ConnectDtlsListener_s(const mozilla::RefPtr<mozilla::TransportFlow>& aFlow);
|
||||
void DtlsConnected(mozilla::TransportLayer* aFlow,
|
||||
mozilla::TransportLayer::State state);
|
||||
|
||||
mozilla::RefPtr<mozilla::MediaSessionConduit> GetConduit(int aStreamIndex, bool aReceive) {
|
||||
int index_inner = aStreamIndex * 2 + (aReceive ? 0 : 1);
|
||||
@ -379,10 +402,11 @@ class PeerConnectionMedia : public sigslot::has_slots<> {
|
||||
PeerConnectionImpl *mParent;
|
||||
|
||||
// A list of streams returned from GetUserMedia
|
||||
mozilla::Mutex mLocalSourceStreamsLock;
|
||||
// This is only accessed on the main thread (with one special exception)
|
||||
nsTArray<nsRefPtr<LocalSourceStreamInfo> > mLocalSourceStreams;
|
||||
|
||||
// A list of streams provided by the other side
|
||||
// This is only accessed on the main thread (with one special exception)
|
||||
nsTArray<nsRefPtr<RemoteSourceStreamInfo> > mRemoteSourceStreams;
|
||||
|
||||
// ICE objects
|
||||
|
@ -90,7 +90,7 @@ class Fake_MediaStream {
|
||||
protected:
|
||||
std::set<Fake_MediaStreamListener *> mListeners;
|
||||
mozilla::Mutex mMutex; // Lock to prevent the listener list from being modified while
|
||||
// executing Periodic().
|
||||
// executing Periodic().
|
||||
};
|
||||
|
||||
class Fake_MediaPeriodic : public nsITimerCallback {
|
||||
@ -242,6 +242,8 @@ public:
|
||||
uint32_t GetHintContents() const { return mHintContents; }
|
||||
void SetHintContents(uint32_t aHintContents) { mHintContents = aHintContents; }
|
||||
|
||||
void SetTrackEnabled(mozilla::TrackID aTrackID, bool aEnabled) {}
|
||||
|
||||
private:
|
||||
nsRefPtr<Fake_MediaStream> mMediaStream;
|
||||
|
||||
|
@ -404,12 +404,6 @@ CacheFileContextEvictor::GetContextFile(nsILoadContextInfo *aLoadContextInfo,
|
||||
nsAutoCString keyPrefix;
|
||||
CacheFileUtils::AppendKeyPrefix(aLoadContextInfo, keyPrefix);
|
||||
|
||||
// TODO: This hack is needed because current CacheFileUtils::ParseKey() can
|
||||
// parse only the whole key and not just the key prefix generated by
|
||||
// CacheFileUtils::CreateKeyPrefix(). This should be removed once bug #968593
|
||||
// is fixed.
|
||||
keyPrefix.Append(":foo");
|
||||
|
||||
nsAutoCString data64;
|
||||
rv = Base64Encode(keyPrefix, data64);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
|
@ -114,6 +114,7 @@ CacheFileHandle::CacheFileHandle(const SHA1Sum::Hash *aHash, bool aPriority)
|
||||
, mIsDoomed(false)
|
||||
, mPriority(aPriority)
|
||||
, mClosed(false)
|
||||
, mSpecialFile(false)
|
||||
, mInvalid(false)
|
||||
, mFileExists(false)
|
||||
, mFileSize(-1)
|
||||
@ -128,6 +129,7 @@ CacheFileHandle::CacheFileHandle(const nsACString &aKey, bool aPriority)
|
||||
, mIsDoomed(false)
|
||||
, mPriority(aPriority)
|
||||
, mClosed(false)
|
||||
, mSpecialFile(true)
|
||||
, mInvalid(false)
|
||||
, mFileExists(false)
|
||||
, mFileSize(-1)
|
||||
@ -145,7 +147,7 @@ CacheFileHandle::~CacheFileHandle()
|
||||
MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
|
||||
|
||||
nsRefPtr<CacheFileIOManager> ioMan = CacheFileIOManager::gInstance;
|
||||
if (ioMan) {
|
||||
if (!IsClosed() && ioMan) {
|
||||
ioMan->CloseHandleInternal(this);
|
||||
}
|
||||
}
|
||||
@ -158,19 +160,17 @@ CacheFileHandle::Log()
|
||||
mFile->GetNativeLeafName(leafName);
|
||||
}
|
||||
|
||||
if (!mHash) {
|
||||
// special file
|
||||
LOG(("CacheFileHandle::Log() [this=%p, hash=nullptr, isDoomed=%d, "
|
||||
"priority=%d, closed=%d, invalid=%d, "
|
||||
"fileExists=%d, fileSize=%lld, leafName=%s, key=%s]",
|
||||
this, mIsDoomed, mPriority, mClosed, mInvalid,
|
||||
if (mSpecialFile) {
|
||||
LOG(("CacheFileHandle::Log() - special file [this=%p, isDoomed=%d, "
|
||||
"priority=%d, closed=%d, invalid=%d, fileExists=%d, fileSize=%lld, "
|
||||
"leafName=%s, key=%s]", this, mIsDoomed, mPriority, mClosed, mInvalid,
|
||||
mFileExists, mFileSize, leafName.get(), mKey.get()));
|
||||
} else {
|
||||
LOG(("CacheFileHandle::Log() [this=%p, hash=%08x%08x%08x%08x%08x, "
|
||||
"isDoomed=%d, priority=%d, closed=%d, invalid=%d, "
|
||||
"fileExists=%d, fileSize=%lld, leafName=%s, key=%s]",
|
||||
this, LOGSHA1(mHash), mIsDoomed, mPriority, mClosed,
|
||||
mInvalid, mFileExists, mFileSize, leafName.get(), mKey.get()));
|
||||
LOG(("CacheFileHandle::Log() - entry file [this=%p, hash=%08x%08x%08x%08x"
|
||||
"%08x, isDoomed=%d, priority=%d, closed=%d, invalid=%d, fileExists=%d,"
|
||||
" fileSize=%lld, leafName=%s, key=%s]", this, LOGSHA1(mHash),
|
||||
mIsDoomed, mPriority, mClosed, mInvalid, mFileExists, mFileSize,
|
||||
leafName.get(), mKey.get()));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1297,6 +1297,13 @@ CacheFileIOManager::ShutdownInternal()
|
||||
} else {
|
||||
mHandles.RemoveHandle(h);
|
||||
}
|
||||
|
||||
// Pointer to the hash is no longer valid once the last handle with the
|
||||
// given hash is released. Null out the pointer so that we crash if there
|
||||
// is a bug in this code and we dereference the pointer after this point.
|
||||
if (!h->IsSpecialFile()) {
|
||||
h->mHash = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Assert the table is empty. When we are here, no new handles can be added
|
||||
@ -1785,6 +1792,9 @@ nsresult
|
||||
CacheFileIOManager::CloseHandleInternal(CacheFileHandle *aHandle)
|
||||
{
|
||||
LOG(("CacheFileIOManager::CloseHandleInternal() [handle=%p]", aHandle));
|
||||
|
||||
MOZ_ASSERT(!aHandle->IsClosed());
|
||||
|
||||
aHandle->Log();
|
||||
|
||||
MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
|
||||
|
@ -55,7 +55,7 @@ public:
|
||||
bool IsPriority() const { return mPriority; }
|
||||
bool FileExists() const { return mFileExists; }
|
||||
bool IsClosed() const { return mClosed; }
|
||||
bool IsSpecialFile() const { return !mHash; }
|
||||
bool IsSpecialFile() const { return mSpecialFile; }
|
||||
nsCString & Key() { return mKey; }
|
||||
|
||||
// Memory reporting
|
||||
@ -73,6 +73,7 @@ private:
|
||||
bool mIsDoomed;
|
||||
bool mPriority;
|
||||
bool mClosed;
|
||||
bool mSpecialFile;
|
||||
bool mInvalid;
|
||||
bool mFileExists; // This means that the file should exists,
|
||||
// but it can be still deleted by OS/user
|
||||
|
@ -294,16 +294,12 @@ OutputParser.prototype = {
|
||||
let win = Services.appShell.hiddenDOMWindow;
|
||||
let doc = win.document;
|
||||
|
||||
name = name.replace(/-\w{1}/g, function(match) {
|
||||
return match.charAt(1).toUpperCase();
|
||||
});
|
||||
|
||||
value = value.replace("!important", "");
|
||||
|
||||
let div = doc.createElement("div");
|
||||
div.style[name] = value;
|
||||
div.style.setProperty(name, value);
|
||||
|
||||
return !!div.style[name];
|
||||
return !!div.style.getPropertyValue(name);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -67,6 +67,8 @@ Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/AsyncShutdown.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Task",
|
||||
"resource://gre/modules/Task.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Promise",
|
||||
"resource://gre/modules/Promise.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "AddonRepository",
|
||||
@ -462,6 +464,7 @@ var gUpdateEnabled = true;
|
||||
var gAutoUpdateDefault = true;
|
||||
var gHotfixID = null;
|
||||
|
||||
var gUpdateCheckInProgress = false;
|
||||
/**
|
||||
* This is the real manager, kept here rather than in AddonManager to keep its
|
||||
* contents hidden from API users.
|
||||
@ -477,7 +480,6 @@ var AddonManagerInternal = {
|
||||
// Store telemetry details per addon provider
|
||||
telemetryDetails: {},
|
||||
|
||||
|
||||
// A read-only wrapper around the types dictionary
|
||||
typesProxy: Proxy.create({
|
||||
getOwnPropertyDescriptor: function typesProxy_getOwnPropertyDescriptor(aName) {
|
||||
@ -1132,120 +1134,121 @@ var AddonManagerInternal = {
|
||||
/**
|
||||
* Performs a background update check by starting an update for all add-ons
|
||||
* that can be updated.
|
||||
* @return Promise{null} resolves when the background update check is complete
|
||||
* (including all addon installs)
|
||||
*/
|
||||
backgroundUpdateCheck: function AMI_backgroundUpdateCheck() {
|
||||
if (!gStarted)
|
||||
throw Components.Exception("AddonManager is not initialized",
|
||||
Cr.NS_ERROR_NOT_INITIALIZED);
|
||||
|
||||
let hotfixID = this.hotfixID;
|
||||
|
||||
let checkHotfix = hotfixID &&
|
||||
Services.prefs.getBoolPref(PREF_APP_UPDATE_ENABLED) &&
|
||||
Services.prefs.getBoolPref(PREF_APP_UPDATE_AUTO);
|
||||
|
||||
if (!this.updateEnabled && !checkHotfix)
|
||||
return;
|
||||
|
||||
Services.obs.notifyObservers(null, "addons-background-update-start", null);
|
||||
|
||||
// Start this from one to ensure the whole of this function completes before
|
||||
// we can send the complete notification. Some parts can in some cases
|
||||
// complete synchronously before later parts have a chance to increment
|
||||
// pendingUpdates.
|
||||
let pendingUpdates = 1;
|
||||
|
||||
function notifyComplete() {
|
||||
if (--pendingUpdates == 0) {
|
||||
Services.obs.notifyObservers(null,
|
||||
"addons-background-update-complete",
|
||||
null);
|
||||
}
|
||||
if (gUpdateCheckInProgress) {
|
||||
throw Components.Exception("Background update check already in progress",
|
||||
Cr.NS_ERROR_UNEXPECTED);
|
||||
}
|
||||
gUpdateCheckInProgress = true;
|
||||
|
||||
if (this.updateEnabled) {
|
||||
let scope = {};
|
||||
Components.utils.import("resource://gre/modules/LightweightThemeManager.jsm", scope);
|
||||
scope.LightweightThemeManager.updateCurrentTheme();
|
||||
return Task.spawn(function* backgroundUpdateTask() {
|
||||
let hotfixID = this.hotfixID;
|
||||
|
||||
let checkHotfix = hotfixID &&
|
||||
Services.prefs.getBoolPref(PREF_APP_UPDATE_ENABLED) &&
|
||||
Services.prefs.getBoolPref(PREF_APP_UPDATE_AUTO);
|
||||
|
||||
if (!this.updateEnabled && !checkHotfix)
|
||||
return;
|
||||
|
||||
Services.obs.notifyObservers(null, "addons-background-update-start", null);
|
||||
|
||||
if (this.updateEnabled) {
|
||||
let scope = {};
|
||||
Components.utils.import("resource://gre/modules/LightweightThemeManager.jsm", scope);
|
||||
scope.LightweightThemeManager.updateCurrentTheme();
|
||||
|
||||
let aAddons = yield new Promise((resolve, reject) => this.getAllAddons(resolve));
|
||||
|
||||
pendingUpdates++;
|
||||
this.getAllAddons(function getAddonsCallback(aAddons) {
|
||||
// If there is a known hotfix then exclude it from the list of add-ons to update.
|
||||
var ids = [a.id for each (a in aAddons) if (a.id != hotfixID)];
|
||||
|
||||
// Repopulate repository cache first, to ensure compatibility overrides
|
||||
// are up to date before checking for addon updates.
|
||||
AddonRepository.backgroundUpdateCheck(
|
||||
ids, function BUC_backgroundUpdateCheckCallback() {
|
||||
pendingUpdates += aAddons.length;
|
||||
aAddons.forEach(function BUC_forEachCallback(aAddon) {
|
||||
if (aAddon.id == hotfixID) {
|
||||
notifyComplete();
|
||||
return;
|
||||
}
|
||||
yield new Promise((resolve, reject) => AddonRepository.backgroundUpdateCheck(ids, resolve));
|
||||
|
||||
// Check all add-ons for updates so that any compatibility updates will
|
||||
// be applied
|
||||
// Keep track of all the async add-on updates happening in parallel
|
||||
let updates = [];
|
||||
|
||||
for (let aAddon of aAddons) {
|
||||
if (aAddon.id == hotfixID) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check all add-ons for updates so that any compatibility updates will
|
||||
// be applied
|
||||
updates.push(new Promise((resolve, reject) => {
|
||||
aAddon.findUpdates({
|
||||
onUpdateAvailable: function BUC_onUpdateAvailable(aAddon, aInstall) {
|
||||
// Start installing updates when the add-on can be updated and
|
||||
// background updates should be applied.
|
||||
if (aAddon.permissions & AddonManager.PERM_CAN_UPGRADE &&
|
||||
AddonManager.shouldAutoUpdate(aAddon)) {
|
||||
// XXX we really should resolve when this install is done,
|
||||
// not when update-available check completes, no?
|
||||
aInstall.install();
|
||||
}
|
||||
},
|
||||
|
||||
onUpdateFinished: notifyComplete
|
||||
onUpdateFinished: resolve
|
||||
}, AddonManager.UPDATE_WHEN_PERIODIC_UPDATE);
|
||||
});
|
||||
|
||||
notifyComplete();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (checkHotfix) {
|
||||
var hotfixVersion = "";
|
||||
try {
|
||||
hotfixVersion = Services.prefs.getCharPref(PREF_EM_HOTFIX_LASTVERSION);
|
||||
}));
|
||||
}
|
||||
yield Promise.all(updates);
|
||||
}
|
||||
catch (e) { }
|
||||
|
||||
let url = null;
|
||||
if (Services.prefs.getPrefType(PREF_EM_HOTFIX_URL) == Ci.nsIPrefBranch.PREF_STRING)
|
||||
url = Services.prefs.getCharPref(PREF_EM_HOTFIX_URL);
|
||||
else
|
||||
url = Services.prefs.getCharPref(PREF_EM_UPDATE_BACKGROUND_URL);
|
||||
if (checkHotfix) {
|
||||
var hotfixVersion = "";
|
||||
try {
|
||||
hotfixVersion = Services.prefs.getCharPref(PREF_EM_HOTFIX_LASTVERSION);
|
||||
}
|
||||
catch (e) { }
|
||||
|
||||
// Build the URI from a fake add-on data.
|
||||
url = AddonManager.escapeAddonURI({
|
||||
id: hotfixID,
|
||||
version: hotfixVersion,
|
||||
userDisabled: false,
|
||||
appDisabled: false
|
||||
}, url);
|
||||
let url = null;
|
||||
if (Services.prefs.getPrefType(PREF_EM_HOTFIX_URL) == Ci.nsIPrefBranch.PREF_STRING)
|
||||
url = Services.prefs.getCharPref(PREF_EM_HOTFIX_URL);
|
||||
else
|
||||
url = Services.prefs.getCharPref(PREF_EM_UPDATE_BACKGROUND_URL);
|
||||
|
||||
pendingUpdates++;
|
||||
Components.utils.import("resource://gre/modules/addons/AddonUpdateChecker.jsm");
|
||||
AddonUpdateChecker.checkForUpdates(hotfixID, null, url, {
|
||||
onUpdateCheckComplete: function BUC_onUpdateCheckComplete(aUpdates) {
|
||||
let update = AddonUpdateChecker.getNewestCompatibleUpdate(aUpdates);
|
||||
if (!update) {
|
||||
notifyComplete();
|
||||
return;
|
||||
}
|
||||
// Build the URI from a fake add-on data.
|
||||
url = AddonManager.escapeAddonURI({
|
||||
id: hotfixID,
|
||||
version: hotfixVersion,
|
||||
userDisabled: false,
|
||||
appDisabled: false
|
||||
}, url);
|
||||
|
||||
// If the available version isn't newer than the last installed
|
||||
// version then ignore it.
|
||||
if (Services.vc.compare(hotfixVersion, update.version) >= 0) {
|
||||
notifyComplete();
|
||||
return;
|
||||
}
|
||||
Components.utils.import("resource://gre/modules/addons/AddonUpdateChecker.jsm");
|
||||
let update = null;
|
||||
try {
|
||||
let foundUpdates = yield new Promise((resolve, reject) => {
|
||||
AddonUpdateChecker.checkForUpdates(hotfixID, null, url, {
|
||||
onUpdateCheckComplete: resolve,
|
||||
onUpdateCheckError: reject
|
||||
});
|
||||
});
|
||||
update = AddonUpdateChecker.getNewestCompatibleUpdate(foundUpdates);
|
||||
} catch (e) {
|
||||
// AUC.checkForUpdates already logged the error
|
||||
}
|
||||
|
||||
// Check that we have a hotfix update, and it's newer than the one we already
|
||||
// have installed (if any)
|
||||
if (update) {
|
||||
if (Services.vc.compare(hotfixVersion, update.version) < 0) {
|
||||
logger.debug("Downloading hotfix version " + update.version);
|
||||
let aInstall = yield new Promise((resolve, reject) =>
|
||||
AddonManager.getInstallForURL(update.updateURL, resolve,
|
||||
"application/x-xpinstall", update.updateHash, null,
|
||||
null, update.version));
|
||||
|
||||
logger.debug("Downloading hotfix version " + update.version);
|
||||
AddonManager.getInstallForURL(update.updateURL,
|
||||
function BUC_getInstallForURL(aInstall) {
|
||||
aInstall.addListener({
|
||||
onDownloadEnded: function BUC_onDownloadEnded(aInstall) {
|
||||
try {
|
||||
@ -1283,17 +1286,15 @@ var AddonManagerInternal = {
|
||||
});
|
||||
|
||||
aInstall.install();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
notifyComplete();
|
||||
}, "application/x-xpinstall", update.updateHash, null,
|
||||
null, update.version);
|
||||
},
|
||||
|
||||
onUpdateCheckError: notifyComplete
|
||||
});
|
||||
}
|
||||
|
||||
notifyComplete();
|
||||
gUpdateCheckInProgress = false;
|
||||
Services.obs.notifyObservers(null,
|
||||
"addons-background-update-complete",
|
||||
null);
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -3610,7 +3610,13 @@ this.XPIProvider = {
|
||||
* The AddonInstall to remove
|
||||
*/
|
||||
removeActiveInstall: function XPI_removeActiveInstall(aInstall) {
|
||||
this.installs = this.installs.filter(function installFilter(i) i != aInstall);
|
||||
let where = this.installs.indexOf(aInstall);
|
||||
if (where == -1) {
|
||||
logger.warn("removeActiveInstall: could not find active install for "
|
||||
+ aInstall.sourceURI.spec);
|
||||
return;
|
||||
}
|
||||
this.installs.splice(where, 1);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -75,7 +75,7 @@ function install_test_addons(aCallback) {
|
||||
// Switch to the test update URL
|
||||
Services.prefs.setCharPref(PREF_UPDATEURL, TESTROOT + "browser_bug557956.rdf");
|
||||
|
||||
aCallback();
|
||||
executeSoon(aCallback);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -41,7 +41,7 @@ add_test(function () {
|
||||
gInstall = new MockInstall(undefined, undefined, addon);
|
||||
gInstall.addTestListener({
|
||||
onNewInstall: function () {
|
||||
run_next_test();
|
||||
executeSoon(run_next_test);
|
||||
}
|
||||
});
|
||||
gProvider.addInstall(gInstall);
|
||||
@ -65,7 +65,7 @@ add_test(function () {
|
||||
}
|
||||
}
|
||||
ok(false, "Item with correct name was not found");
|
||||
run_next_test();
|
||||
executeSoon(run_next_test);
|
||||
}
|
||||
});
|
||||
gInstall.install();
|
||||
|
@ -21,17 +21,18 @@ var gTestInstallListener = {
|
||||
|
||||
onInstallEnded: function(aInstall) {
|
||||
check_hidden(false);
|
||||
run_next_test();
|
||||
executeSoon(run_next_test);
|
||||
},
|
||||
|
||||
onInstallCancelled: function(aInstall) {
|
||||
ok(gExpectedCancel, "Should expect install cancel");
|
||||
check_hidden(false);
|
||||
run_next_test();
|
||||
executeSoon(run_next_test);
|
||||
},
|
||||
|
||||
onInstallFailed: function(aInstall) {
|
||||
ok(false, "Did not expect onInstallFailed");
|
||||
executeSoon(run_next_test);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -40,7 +40,7 @@ function install_locale(aCallback) {
|
||||
gInstall.addTestListener({
|
||||
onInstallEnded: function(aInstall) {
|
||||
gInstall.removeTestListener(this);
|
||||
aCallback();
|
||||
executeSoon(aCallback);
|
||||
}
|
||||
});
|
||||
gInstall.install();
|
||||
|
@ -97,7 +97,7 @@ add_test(function() {
|
||||
},
|
||||
onInstallEnded: function() {
|
||||
check_list(gItem);
|
||||
run_next_test();
|
||||
executeSoon(run_next_test);
|
||||
}
|
||||
});
|
||||
|
||||
@ -136,7 +136,7 @@ add_test(function() {
|
||||
onInstallEnded: function() {
|
||||
check_list(null);
|
||||
extension.cancel();
|
||||
run_next_test();
|
||||
executeSoon(run_next_test);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -15,51 +15,56 @@ const PREF_APP_UPDATE_ENABLED = "app.update.enabled";
|
||||
|
||||
const HOTFIX_ID = "hotfix@tests.mozilla.org";
|
||||
|
||||
var gNextTest;
|
||||
|
||||
var SuccessfulInstallListener = {
|
||||
onDownloadCancelled: function(aInstall) {
|
||||
ok(false, "Should not have seen the download cancelled");
|
||||
is(aInstall.addon.id, HOTFIX_ID, "Should have seen the right add-on");
|
||||
|
||||
AddonManager.removeInstallListener(this);
|
||||
gNextTest();
|
||||
},
|
||||
|
||||
onInstallEnded: function(aInstall) {
|
||||
ok(true, "Should have seen the install complete");
|
||||
is(aInstall.addon.id, HOTFIX_ID, "Should have installed the right add-on");
|
||||
|
||||
AddonManager.removeInstallListener(this);
|
||||
aInstall.addon.uninstall();
|
||||
Services.prefs.clearUserPref(PREF_EM_HOTFIX_LASTVERSION);
|
||||
gNextTest();
|
||||
}
|
||||
/*
|
||||
* Register an addon install listener and return a promise that:
|
||||
* resolves with the AddonInstall object if the install succeeds
|
||||
* rejects with the AddonInstall if the install fails
|
||||
*/
|
||||
function promiseInstallListener() {
|
||||
return new Promise((resolve, reject) => {
|
||||
let listener = {
|
||||
onInstallEnded: ai => {
|
||||
AddonManager.removeInstallListener(listener);
|
||||
resolve(ai);
|
||||
},
|
||||
onDownloadCancelled: ai => {
|
||||
AddonManager.removeInstallListener(listener);
|
||||
reject(ai);
|
||||
}
|
||||
};
|
||||
AddonManager.addInstallListener(listener);
|
||||
});
|
||||
}
|
||||
|
||||
var FailedInstallListener = {
|
||||
onDownloadCancelled: function(aInstall) {
|
||||
ok(true, "Should have seen the download cancelled");
|
||||
is(aInstall.addon.id, HOTFIX_ID, "Should have seen the right add-on");
|
||||
|
||||
AddonManager.removeInstallListener(this);
|
||||
gNextTest();
|
||||
},
|
||||
|
||||
onInstallEnded: function(aInstall) {
|
||||
ok(false, "Should not have seen the install complete");
|
||||
is(aInstall.addon.id, HOTFIX_ID, "Should have installed the right add-on");
|
||||
|
||||
AddonManager.removeInstallListener(this);
|
||||
aInstall.addon.uninstall();
|
||||
Services.prefs.clearUserPref(PREF_EM_HOTFIX_LASTVERSION);
|
||||
gNextTest();
|
||||
}
|
||||
function promiseSuccessfulInstall() {
|
||||
return promiseInstallListener().then(
|
||||
aInstall => {
|
||||
ok(true, "Should have seen the install complete");
|
||||
is(aInstall.addon.id, HOTFIX_ID, "Should have installed the right add-on");
|
||||
aInstall.addon.uninstall();
|
||||
Services.prefs.clearUserPref(PREF_EM_HOTFIX_LASTVERSION);
|
||||
},
|
||||
aInstall => {
|
||||
ok(false, "Should not have seen the download cancelled");
|
||||
is(aInstall.addon.id, HOTFIX_ID, "Should have seen the right add-on");
|
||||
});
|
||||
}
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
function promiseFailedInstall() {
|
||||
return promiseInstallListener().then(
|
||||
aInstall => {
|
||||
ok(false, "Should not have seen the install complete");
|
||||
is(aInstall.addon.id, HOTFIX_ID, "Should have installed the right add-on");
|
||||
aInstall.addon.uninstall();
|
||||
Services.prefs.clearUserPref(PREF_EM_HOTFIX_LASTVERSION);
|
||||
},
|
||||
aInstall => {
|
||||
ok(true, "Should have seen the download cancelled");
|
||||
is(aInstall.addon.id, HOTFIX_ID, "Should have seen the right add-on");
|
||||
});
|
||||
}
|
||||
|
||||
add_task(function setup() {
|
||||
Services.prefs.setBoolPref(PREF_APP_UPDATE_ENABLED, true);
|
||||
Services.prefs.setBoolPref(PREF_INSTALL_REQUIREBUILTINCERTS, false);
|
||||
Services.prefs.setBoolPref(PREF_UPDATE_REQUIREBUILTINCERTS, false);
|
||||
@ -78,109 +83,85 @@ function test() {
|
||||
var prefs = Services.prefs.getChildList(PREF_EM_HOTFIX_CERTS);
|
||||
prefs.forEach(Services.prefs.clearUserPref);
|
||||
});
|
||||
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
function end_test() {
|
||||
finish();
|
||||
}
|
||||
|
||||
add_test(function check_no_cert_checks() {
|
||||
Services.prefs.setBoolPref(PREF_EM_CERT_CHECKATTRIBUTES, false);
|
||||
AddonManager.addInstallListener(SuccessfulInstallListener);
|
||||
|
||||
gNextTest = run_next_test;
|
||||
|
||||
AddonManagerPrivate.backgroundUpdateCheck();
|
||||
});
|
||||
|
||||
add_test(function check_wrong_cert_fingerprint() {
|
||||
add_task(function* check_no_cert_checks() {
|
||||
Services.prefs.setBoolPref(PREF_EM_CERT_CHECKATTRIBUTES, false);
|
||||
yield Promise.all([
|
||||
promiseSuccessfulInstall(),
|
||||
AddonManagerPrivate.backgroundUpdateCheck()
|
||||
]);
|
||||
});
|
||||
|
||||
add_task(function* check_wrong_cert_fingerprint() {
|
||||
Services.prefs.setBoolPref(PREF_EM_CERT_CHECKATTRIBUTES, true);
|
||||
Services.prefs.setCharPref(PREF_EM_HOTFIX_CERTS + "1.sha1Fingerprint", "foo");
|
||||
|
||||
AddonManager.addInstallListener(FailedInstallListener);
|
||||
|
||||
gNextTest = function() {
|
||||
Services.prefs.clearUserPref(PREF_EM_HOTFIX_CERTS + "1.sha1Fingerprint");
|
||||
|
||||
run_next_test();
|
||||
};
|
||||
|
||||
AddonManagerPrivate.backgroundUpdateCheck();
|
||||
yield Promise.all([
|
||||
promiseFailedInstall(),
|
||||
AddonManagerPrivate.backgroundUpdateCheck()
|
||||
]);
|
||||
Services.prefs.clearUserPref(PREF_EM_HOTFIX_CERTS + "1.sha1Fingerprint");
|
||||
});
|
||||
|
||||
add_test(function check_right_cert_fingerprint() {
|
||||
add_task(function* check_right_cert_fingerprint() {
|
||||
Services.prefs.setBoolPref(PREF_EM_CERT_CHECKATTRIBUTES, true);
|
||||
Services.prefs.setCharPref(PREF_EM_HOTFIX_CERTS + "1.sha1Fingerprint", "3E:B9:4E:07:12:FE:3C:01:41:46:13:46:FC:84:52:1A:8C:BE:1D:A2");
|
||||
|
||||
AddonManager.addInstallListener(SuccessfulInstallListener);
|
||||
yield Promise.all([
|
||||
promiseSuccessfulInstall(),
|
||||
AddonManagerPrivate.backgroundUpdateCheck()
|
||||
]);
|
||||
|
||||
gNextTest = function() {
|
||||
Services.prefs.clearUserPref(PREF_EM_HOTFIX_CERTS + "1.sha1Fingerprint");
|
||||
|
||||
run_next_test();
|
||||
};
|
||||
|
||||
AddonManagerPrivate.backgroundUpdateCheck();
|
||||
Services.prefs.clearUserPref(PREF_EM_HOTFIX_CERTS + "1.sha1Fingerprint");
|
||||
});
|
||||
|
||||
add_test(function check_multi_cert_fingerprint_1() {
|
||||
add_task(function* check_multi_cert_fingerprint_1() {
|
||||
Services.prefs.setBoolPref(PREF_EM_CERT_CHECKATTRIBUTES, true);
|
||||
Services.prefs.setCharPref(PREF_EM_HOTFIX_CERTS + "1.sha1Fingerprint", "3E:B9:4E:07:12:FE:3C:01:41:46:13:46:FC:84:52:1A:8C:BE:1D:A2");
|
||||
Services.prefs.setCharPref(PREF_EM_HOTFIX_CERTS + "2.sha1Fingerprint", "foo");
|
||||
|
||||
AddonManager.addInstallListener(SuccessfulInstallListener);
|
||||
yield Promise.all([
|
||||
promiseSuccessfulInstall(),
|
||||
AddonManagerPrivate.backgroundUpdateCheck()
|
||||
]);
|
||||
|
||||
gNextTest = function() {
|
||||
Services.prefs.clearUserPref(PREF_EM_HOTFIX_CERTS + "1.sha1Fingerprint");
|
||||
Services.prefs.clearUserPref(PREF_EM_HOTFIX_CERTS + "2.sha1Fingerprint");
|
||||
|
||||
run_next_test();
|
||||
};
|
||||
|
||||
AddonManagerPrivate.backgroundUpdateCheck();
|
||||
Services.prefs.clearUserPref(PREF_EM_HOTFIX_CERTS + "1.sha1Fingerprint");
|
||||
Services.prefs.clearUserPref(PREF_EM_HOTFIX_CERTS + "2.sha1Fingerprint");
|
||||
});
|
||||
|
||||
add_test(function check_multi_cert_fingerprint_2() {
|
||||
add_task(function* check_multi_cert_fingerprint_2() {
|
||||
Services.prefs.setBoolPref(PREF_EM_CERT_CHECKATTRIBUTES, true);
|
||||
Services.prefs.setCharPref(PREF_EM_HOTFIX_CERTS + "1.sha1Fingerprint", "foo");
|
||||
Services.prefs.setCharPref(PREF_EM_HOTFIX_CERTS + "2.sha1Fingerprint", "3E:B9:4E:07:12:FE:3C:01:41:46:13:46:FC:84:52:1A:8C:BE:1D:A2");
|
||||
|
||||
AddonManager.addInstallListener(SuccessfulInstallListener);
|
||||
yield Promise.all([
|
||||
promiseSuccessfulInstall(),
|
||||
AddonManagerPrivate.backgroundUpdateCheck()
|
||||
]);
|
||||
|
||||
gNextTest = function() {
|
||||
Services.prefs.clearUserPref(PREF_EM_HOTFIX_CERTS + "1.sha1Fingerprint");
|
||||
Services.prefs.clearUserPref(PREF_EM_HOTFIX_CERTS + "2.sha1Fingerprint");
|
||||
|
||||
run_next_test();
|
||||
};
|
||||
|
||||
AddonManagerPrivate.backgroundUpdateCheck();
|
||||
Services.prefs.clearUserPref(PREF_EM_HOTFIX_CERTS + "1.sha1Fingerprint");
|
||||
Services.prefs.clearUserPref(PREF_EM_HOTFIX_CERTS + "2.sha1Fingerprint");
|
||||
});
|
||||
|
||||
add_test(function check_no_cert_no_checks() {
|
||||
add_task(function* check_no_cert_no_checks() {
|
||||
Services.prefs.setBoolPref(PREF_EM_CERT_CHECKATTRIBUTES, false);
|
||||
Services.prefs.setCharPref(PREF_EM_HOTFIX_URL, TESTROOT + "unsigned_hotfix.rdf");
|
||||
|
||||
AddonManager.addInstallListener(SuccessfulInstallListener);
|
||||
|
||||
gNextTest = run_next_test;
|
||||
|
||||
AddonManagerPrivate.backgroundUpdateCheck();
|
||||
yield Promise.all([
|
||||
promiseSuccessfulInstall(),
|
||||
AddonManagerPrivate.backgroundUpdateCheck()
|
||||
]);
|
||||
});
|
||||
|
||||
add_test(function check_no_cert_cert_fingerprint_check() {
|
||||
add_task(function* check_no_cert_cert_fingerprint_check() {
|
||||
Services.prefs.setBoolPref(PREF_EM_CERT_CHECKATTRIBUTES, true);
|
||||
Services.prefs.setCharPref(PREF_EM_HOTFIX_CERTS + "1.sha1Fingerprint", "3E:B9:4E:07:12:FE:3C:01:41:46:13:46:FC:84:52:1A:8C:BE:1D:A2");
|
||||
|
||||
AddonManager.addInstallListener(FailedInstallListener);
|
||||
yield Promise.all([
|
||||
promiseFailedInstall(),
|
||||
AddonManagerPrivate.backgroundUpdateCheck()
|
||||
]);
|
||||
|
||||
gNextTest = function() {
|
||||
Services.prefs.clearUserPref(PREF_EM_HOTFIX_CERTS + "1.sha1Fingerprint");
|
||||
|
||||
run_next_test();
|
||||
};
|
||||
|
||||
AddonManagerPrivate.backgroundUpdateCheck();
|
||||
Services.prefs.clearUserPref(PREF_EM_HOTFIX_CERTS + "1.sha1Fingerprint");
|
||||
});
|
||||
|
@ -184,7 +184,7 @@ add_test(function() {
|
||||
is_element_hidden(item._installStatus, "Install progress widget should be hidden");
|
||||
|
||||
if (badgeUpdated)
|
||||
run_next_test();
|
||||
executeSoon(run_next_test);
|
||||
else
|
||||
installCompleted = true;
|
||||
}
|
||||
|
@ -566,7 +566,7 @@ add_test(function() {
|
||||
|
||||
is(installBtn.hidden, true, "Install button should be hidden after install ended");
|
||||
check_filtered_results(QUERY, "relevancescore", false);
|
||||
run_next_test();
|
||||
executeSoon(run_next_test);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -30,7 +30,7 @@ function install_test_addon(aCallback) {
|
||||
onInstallEnded: function() {
|
||||
AddonManager.getAddonByID("addon1@tests.mozilla.org", function(addon) {
|
||||
gTestAddon = addon;
|
||||
aCallback();
|
||||
executeSoon(aCallback);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user