зеркало из https://github.com/mozilla/gecko-dev.git
merge fx-team to mozilla-central a=merge
This commit is contained in:
Коммит
451dae3178
|
@ -1880,8 +1880,10 @@ pref("dom.ipc.reportProcessHangs", false);
|
|||
pref("dom.ipc.reportProcessHangs", true);
|
||||
#endif
|
||||
|
||||
#ifndef NIGHTLY_BUILD
|
||||
// Disable reader mode by default.
|
||||
pref("reader.parse-on-load.enabled", false);
|
||||
#endif
|
||||
|
||||
// Disable ReadingList by default.
|
||||
pref("browser.readinglist.enabled", false);
|
||||
|
|
|
@ -221,8 +221,6 @@ let LoopCallsInternal = {
|
|||
|
||||
if (channelID == MozLoopService.channelIDs.callsFxA && MozLoopService.userProfile) {
|
||||
this._getCalls(LOOP_SESSION_TYPE.FXA, version);
|
||||
} else {
|
||||
this._getCalls(LOOP_SESSION_TYPE.GUEST, version);
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -623,6 +623,13 @@ function injectLoopAPI(targetWindow) {
|
|||
}
|
||||
},
|
||||
|
||||
TWO_WAY_MEDIA_CONN_LENGTH: {
|
||||
enumerable: true,
|
||||
get: function() {
|
||||
return Cu.cloneInto(TWO_WAY_MEDIA_CONN_LENGTH, targetWindow);
|
||||
}
|
||||
},
|
||||
|
||||
fxAEnabled: {
|
||||
enumerable: true,
|
||||
get: function() {
|
||||
|
@ -737,14 +744,14 @@ function injectLoopAPI(targetWindow) {
|
|||
/**
|
||||
* Adds a value to a telemetry histogram.
|
||||
*
|
||||
* @param {string} histogramId Name of the telemetry histogram to update.
|
||||
* @param {integer} value Value to add to the histogram.
|
||||
* @param {string} histogramId Name of the telemetry histogram to update.
|
||||
* @param {string} value Label of bucket to increment in the histogram.
|
||||
*/
|
||||
telemetryAdd: {
|
||||
telemetryAddKeyedValue: {
|
||||
enumerable: true,
|
||||
writable: true,
|
||||
value: function(histogramId, value) {
|
||||
Services.telemetry.getHistogramById(histogramId).add(value);
|
||||
Services.telemetry.getKeyedHistogramById(histogramId).add(value);
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -15,6 +15,20 @@ const LOOP_SESSION_TYPE = {
|
|||
FXA: 2,
|
||||
};
|
||||
|
||||
/***
|
||||
* Buckets that we segment 2-way media connection length telemetry probes
|
||||
* into.
|
||||
*
|
||||
* @type {{SHORTER_THAN_10S: string, BETWEEN_10S_AND_30S: string,
|
||||
* BETWEEN_30S_AND_5M: string, MORE_THAN_5M: string}}
|
||||
*/
|
||||
const TWO_WAY_MEDIA_CONN_LENGTH = {
|
||||
SHORTER_THAN_10S: "SHORTER_THAN_10S",
|
||||
BETWEEN_10S_AND_30S: "BETWEEN_10S_AND_30S",
|
||||
BETWEEN_30S_AND_5M: "BETWEEN_30S_AND_5M",
|
||||
MORE_THAN_5M: "MORE_THAN_5M",
|
||||
};
|
||||
|
||||
// See LOG_LEVELS in Console.jsm. Common examples: "All", "Info", "Warn", & "Error".
|
||||
const PREF_LOG_LEVEL = "loop.debug.loglevel";
|
||||
|
||||
|
@ -28,7 +42,7 @@ Cu.import("resource://gre/modules/FxAccountsOAuthClient.jsm");
|
|||
|
||||
Cu.importGlobalProperties(["URL"]);
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["MozLoopService", "LOOP_SESSION_TYPE"];
|
||||
this.EXPORTED_SYMBOLS = ["MozLoopService", "LOOP_SESSION_TYPE", "TWO_WAY_MEDIA_CONN_LENGTH"];
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "injectLoopAPI",
|
||||
"resource:///modules/loop/MozLoopAPI.jsm");
|
||||
|
@ -157,37 +171,6 @@ let MozLoopServiceInternal = {
|
|||
return initialDelay;
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the current latest expiry time for urls.
|
||||
*
|
||||
* In seconds since epoch.
|
||||
*/
|
||||
get expiryTimeSeconds() {
|
||||
try {
|
||||
return Services.prefs.getIntPref("loop.urlsExpiryTimeSeconds");
|
||||
} catch (x) {
|
||||
// It is ok for the pref not to exist.
|
||||
return 0;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets the expiry time to either the specified time, or keeps it the same
|
||||
* depending on which is latest.
|
||||
*/
|
||||
set expiryTimeSeconds(time) {
|
||||
if (time > this.expiryTimeSeconds) {
|
||||
Services.prefs.setIntPref("loop.urlsExpiryTimeSeconds", time);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns true if the expiry time is in the future.
|
||||
*/
|
||||
urlExpiryTimeIsInFuture: function() {
|
||||
return this.expiryTimeSeconds * 1000 > Date.now();
|
||||
},
|
||||
|
||||
/**
|
||||
* Retrieves MozLoopService Firefox Accounts OAuth token.
|
||||
*
|
||||
|
@ -390,17 +373,20 @@ let MozLoopServiceInternal = {
|
|||
let options = this.mocks.webSocket ? { mockWebSocket: this.mocks.webSocket } : {};
|
||||
this.pushHandler.initialize(options); // This can be called more than once.
|
||||
|
||||
let callsID = sessionType == LOOP_SESSION_TYPE.GUEST ?
|
||||
MozLoopService.channelIDs.callsGuest :
|
||||
MozLoopService.channelIDs.callsFxA,
|
||||
roomsID = sessionType == LOOP_SESSION_TYPE.GUEST ?
|
||||
MozLoopService.channelIDs.roomsGuest :
|
||||
MozLoopService.channelIDs.roomsFxA;
|
||||
|
||||
let regPromise = this.createNotificationChannel(
|
||||
callsID, sessionType, "calls", LoopCalls.onNotification).then(() => {
|
||||
return this.createNotificationChannel(
|
||||
roomsID, sessionType, "rooms", roomsPushNotification)});
|
||||
let regPromise;
|
||||
if (sessionType == LOOP_SESSION_TYPE.GUEST) {
|
||||
regPromise = this.createNotificationChannel(
|
||||
MozLoopService.channelIDs.roomsGuest, sessionType, "rooms",
|
||||
roomsPushNotification);
|
||||
} else {
|
||||
regPromise = this.createNotificationChannel(
|
||||
MozLoopService.channelIDs.callsFxA, sessionType, "calls",
|
||||
LoopCalls.onNotification).then(() => {
|
||||
return this.createNotificationChannel(
|
||||
MozLoopService.channelIDs.roomsFxA, sessionType, "rooms",
|
||||
roomsPushNotification);
|
||||
});
|
||||
}
|
||||
|
||||
log.debug("assigning to deferredRegistrations for sessionType:", sessionType);
|
||||
this.deferredRegistrations.set(sessionType, regPromise);
|
||||
|
@ -587,11 +573,8 @@ let MozLoopServiceInternal = {
|
|||
this.setError("login", error);
|
||||
throw error;
|
||||
});
|
||||
} else if (this.urlExpiryTimeIsInFuture()) {
|
||||
// If there are no Guest URLs in the future, don't use setError to notify the user since
|
||||
// there isn't a need for a Guest registration at this time.
|
||||
this.setError("registration", error);
|
||||
}
|
||||
this.setError("registration", error);
|
||||
throw error;
|
||||
};
|
||||
|
||||
|
@ -1058,7 +1041,6 @@ this.MozLoopService = {
|
|||
// Channel ids that will be registered with the PushServer for notifications
|
||||
return {
|
||||
callsFxA: "25389583-921f-4169-a426-a4673658944b",
|
||||
callsGuest: "801f754b-686b-43ec-bd83-1419bbf58388",
|
||||
roomsFxA: "6add272a-d316-477c-8335-f00f73dfde71",
|
||||
roomsGuest: "19d3f799-a8f3-4328-9822-b7cd02765832",
|
||||
};
|
||||
|
@ -1124,10 +1106,9 @@ this.MozLoopService = {
|
|||
|
||||
LoopRooms.on("joined", this.maybeResumeTourOnRoomJoined.bind(this));
|
||||
|
||||
// If expiresTime is not in the future and the user hasn't
|
||||
// If there's no guest room created and the user hasn't
|
||||
// previously authenticated then skip registration.
|
||||
if (!MozLoopServiceInternal.urlExpiryTimeIsInFuture() &&
|
||||
!LoopRooms.getGuestCreatedRoom() &&
|
||||
if (!LoopRooms.getGuestCreatedRoom() &&
|
||||
!MozLoopServiceInternal.fxAOAuthTokenData) {
|
||||
return Promise.resolve("registration not needed");
|
||||
}
|
||||
|
@ -1204,11 +1185,10 @@ this.MozLoopService = {
|
|||
});
|
||||
|
||||
try {
|
||||
if (MozLoopServiceInternal.urlExpiryTimeIsInFuture() ||
|
||||
LoopRooms.getGuestCreatedRoom()) {
|
||||
if (LoopRooms.getGuestCreatedRoom()) {
|
||||
yield this.promiseRegisteredWithServers(LOOP_SESSION_TYPE.GUEST);
|
||||
} else {
|
||||
log.debug("delayedInitialize: URL expiry time isn't in the future so not registering as a guest");
|
||||
log.debug("delayedInitialize: Guest Room hasn't been created so not registering as a guest");
|
||||
}
|
||||
} catch (ex) {
|
||||
log.debug("MozLoopService: Failure of guest registration", ex);
|
||||
|
@ -1461,7 +1441,7 @@ this.MozLoopService = {
|
|||
yield MozLoopServiceInternal.unregisterFromLoopServer(LOOP_SESSION_TYPE.FXA);
|
||||
}
|
||||
catch (err) {
|
||||
throw err
|
||||
throw err;
|
||||
}
|
||||
finally {
|
||||
MozLoopServiceInternal.clearSessionToken(LOOP_SESSION_TYPE.FXA);
|
||||
|
|
|
@ -66,6 +66,7 @@ loop.conversation = (function(mozL10n) {
|
|||
client: this.props.client,
|
||||
conversation: this.props.conversation,
|
||||
sdk: this.props.sdk,
|
||||
isDesktop: true,
|
||||
conversationAppStore: this.props.conversationAppStore}
|
||||
));
|
||||
}
|
||||
|
@ -120,8 +121,13 @@ loop.conversation = (function(mozL10n) {
|
|||
var sdkDriver = new loop.OTSdkDriver({
|
||||
isDesktop: true,
|
||||
dispatcher: dispatcher,
|
||||
sdk: OT
|
||||
sdk: OT,
|
||||
mozLoop: navigator.mozLoop
|
||||
});
|
||||
|
||||
// expose for functional tests
|
||||
loop.conversation._sdkDriver = sdkDriver;
|
||||
|
||||
var appVersionInfo = navigator.mozLoop.appVersionInfo;
|
||||
var feedbackClient = new loop.FeedbackAPIClient(
|
||||
navigator.mozLoop.getLoopPref("feedback.baseUrl"), {
|
||||
|
@ -201,7 +207,15 @@ loop.conversation = (function(mozL10n) {
|
|||
|
||||
return {
|
||||
AppControllerView: AppControllerView,
|
||||
init: init
|
||||
init: init,
|
||||
|
||||
/**
|
||||
* Exposed for the use of functional tests to be able to check
|
||||
* metric-related execution as the call sequence progresses.
|
||||
*
|
||||
* @type loop.OTSdkDriver
|
||||
*/
|
||||
_sdkDriver: null
|
||||
};
|
||||
})(document.mozL10n);
|
||||
|
||||
|
|
|
@ -66,6 +66,7 @@ loop.conversation = (function(mozL10n) {
|
|||
client={this.props.client}
|
||||
conversation={this.props.conversation}
|
||||
sdk={this.props.sdk}
|
||||
isDesktop={true}
|
||||
conversationAppStore={this.props.conversationAppStore}
|
||||
/>);
|
||||
}
|
||||
|
@ -120,8 +121,13 @@ loop.conversation = (function(mozL10n) {
|
|||
var sdkDriver = new loop.OTSdkDriver({
|
||||
isDesktop: true,
|
||||
dispatcher: dispatcher,
|
||||
sdk: OT
|
||||
sdk: OT,
|
||||
mozLoop: navigator.mozLoop
|
||||
});
|
||||
|
||||
// expose for functional tests
|
||||
loop.conversation._sdkDriver = sdkDriver;
|
||||
|
||||
var appVersionInfo = navigator.mozLoop.appVersionInfo;
|
||||
var feedbackClient = new loop.FeedbackAPIClient(
|
||||
navigator.mozLoop.getLoopPref("feedback.baseUrl"), {
|
||||
|
@ -201,7 +207,15 @@ loop.conversation = (function(mozL10n) {
|
|||
|
||||
return {
|
||||
AppControllerView: AppControllerView,
|
||||
init: init
|
||||
init: init,
|
||||
|
||||
/**
|
||||
* Exposed for the use of functional tests to be able to check
|
||||
* metric-related execution as the call sequence progresses.
|
||||
*
|
||||
* @type loop.OTSdkDriver
|
||||
*/
|
||||
_sdkDriver: null
|
||||
};
|
||||
})(document.mozL10n);
|
||||
|
||||
|
|
|
@ -347,10 +347,17 @@ loop.conversationViews = (function(mozL10n) {
|
|||
conversation: React.PropTypes.instanceOf(sharedModels.ConversationModel)
|
||||
.isRequired,
|
||||
sdk: React.PropTypes.object.isRequired,
|
||||
isDesktop: React.PropTypes.bool,
|
||||
conversationAppStore: React.PropTypes.instanceOf(
|
||||
loop.store.ConversationAppStore).isRequired
|
||||
},
|
||||
|
||||
getDefaultProps: function() {
|
||||
return {
|
||||
isDesktop: false
|
||||
};
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
callFailed: false, // XXX this should be removed when bug 1047410 lands.
|
||||
|
@ -403,6 +410,7 @@ loop.conversationViews = (function(mozL10n) {
|
|||
|
||||
return (
|
||||
React.createElement(sharedViews.ConversationView, {
|
||||
isDesktop: this.props.isDesktop,
|
||||
initiate: true,
|
||||
sdk: this.props.sdk,
|
||||
model: this.props.conversation,
|
||||
|
|
|
@ -347,10 +347,17 @@ loop.conversationViews = (function(mozL10n) {
|
|||
conversation: React.PropTypes.instanceOf(sharedModels.ConversationModel)
|
||||
.isRequired,
|
||||
sdk: React.PropTypes.object.isRequired,
|
||||
isDesktop: React.PropTypes.bool,
|
||||
conversationAppStore: React.PropTypes.instanceOf(
|
||||
loop.store.ConversationAppStore).isRequired
|
||||
},
|
||||
|
||||
getDefaultProps: function() {
|
||||
return {
|
||||
isDesktop: false
|
||||
};
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
callFailed: false, // XXX this should be removed when bug 1047410 lands.
|
||||
|
@ -403,6 +410,7 @@ loop.conversationViews = (function(mozL10n) {
|
|||
|
||||
return (
|
||||
<sharedViews.ConversationView
|
||||
isDesktop={this.props.isDesktop}
|
||||
initiate={true}
|
||||
sdk={this.props.sdk}
|
||||
model={this.props.conversation}
|
||||
|
|
|
@ -228,7 +228,8 @@ loop.store = loop.store || {};
|
|||
"retryCall",
|
||||
"mediaConnected",
|
||||
"setMute",
|
||||
"fetchRoomEmailLink"
|
||||
"fetchRoomEmailLink",
|
||||
"windowUnload"
|
||||
]);
|
||||
|
||||
this.setStoreState({
|
||||
|
@ -359,6 +360,15 @@ loop.store = loop.store || {};
|
|||
}.bind(this));
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when the window is unloaded, either by code, or by the user
|
||||
* explicitly closing it. Expected to do any necessary housekeeping, such
|
||||
* as shutting down the call cleanly and adding any relevant telemetry data.
|
||||
*/
|
||||
windowUnload: function() {
|
||||
this._endSession();
|
||||
},
|
||||
|
||||
/**
|
||||
* Obtains the outgoing call data from the server and handles the
|
||||
* result.
|
||||
|
|
|
@ -27,7 +27,12 @@ loop.OTSdkDriver = (function() {
|
|||
this.dispatcher = options.dispatcher;
|
||||
this.sdk = options.sdk;
|
||||
|
||||
// Note that this will only be defined and usable in a desktop-local
|
||||
// context, not in the standalone web client.
|
||||
this.mozLoop = options.mozLoop;
|
||||
|
||||
this.connections = {};
|
||||
this.connectionStartTime = this.CONNECTION_START_TIME_UNINITIALIZED;
|
||||
|
||||
this.dispatcher.register(this, [
|
||||
"setupStreamElements",
|
||||
|
@ -51,6 +56,9 @@ loop.OTSdkDriver = (function() {
|
|||
};
|
||||
|
||||
OTSdkDriver.prototype = {
|
||||
CONNECTION_START_TIME_UNINITIALIZED: -1,
|
||||
CONNECTION_START_TIME_ALREADY_NOTED: -2,
|
||||
|
||||
/**
|
||||
* Clones the publisher config into a new object, as the sdk modifies the
|
||||
* properties object.
|
||||
|
@ -228,12 +236,15 @@ loop.OTSdkDriver = (function() {
|
|||
delete this.publisher;
|
||||
}
|
||||
|
||||
this._noteConnectionLengthIfNeeded(this.connectionStartTime, performance.now());
|
||||
|
||||
// Also, tidy these variables ready for next time.
|
||||
delete this._sessionConnected;
|
||||
delete this._publisherReady;
|
||||
delete this._publishedLocalStream;
|
||||
delete this._subscribedRemoteStream;
|
||||
this.connections = {};
|
||||
this.connectionStartTime = this.CONNECTION_START_TIME_UNINITIALIZED;
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -297,6 +308,7 @@ loop.OTSdkDriver = (function() {
|
|||
if (connection && (connection.id in this.connections)) {
|
||||
delete this.connections[connection.id];
|
||||
}
|
||||
this._noteConnectionLengthIfNeeded(this.connectionStartTime, performance.now());
|
||||
this.dispatcher.dispatch(new sharedActions.RemotePeerDisconnected({
|
||||
peerHungup: event.reason === "clientDisconnected"
|
||||
}));
|
||||
|
@ -323,6 +335,8 @@ loop.OTSdkDriver = (function() {
|
|||
return;
|
||||
}
|
||||
|
||||
this._noteConnectionLengthIfNeeded(this.connectionStartTime,
|
||||
performance.now());
|
||||
this.dispatcher.dispatch(new sharedActions.ConnectionFailure({
|
||||
reason: reason
|
||||
}));
|
||||
|
@ -394,6 +408,7 @@ loop.OTSdkDriver = (function() {
|
|||
|
||||
this._subscribedRemoteStream = true;
|
||||
if (this._checkAllStreamsConnected()) {
|
||||
this.connectionStartTime = performance.now();
|
||||
this.dispatcher.dispatch(new sharedActions.MediaConnected());
|
||||
}
|
||||
},
|
||||
|
@ -513,6 +528,7 @@ loop.OTSdkDriver = (function() {
|
|||
// Now record the fact, and check if we've got all media yet.
|
||||
this._publishedLocalStream = true;
|
||||
if (this._checkAllStreamsConnected()) {
|
||||
this.connectionStartTime = performance.now();
|
||||
this.dispatcher.dispatch(new sharedActions.MediaConnected());
|
||||
}
|
||||
}
|
||||
|
@ -544,6 +560,72 @@ loop.OTSdkDriver = (function() {
|
|||
this.dispatcher.dispatch(new sharedActions.ScreenSharingState({
|
||||
state: SCREEN_SHARE_STATES.INACTIVE
|
||||
}));
|
||||
},
|
||||
|
||||
/**
|
||||
* A hook exposed only for the use of the functional tests so that
|
||||
* they can check that the bi-directional media count is being updated
|
||||
* correctly.
|
||||
*
|
||||
* @type number
|
||||
* @private
|
||||
*/
|
||||
_connectionLengthNotedCalls: 0,
|
||||
|
||||
/**
|
||||
* Wrapper for adding a keyed value that also updates
|
||||
* connectionLengthNoted calls and sets this.connectionStartTime to
|
||||
* this.CONNECTION_START_TIME_ALREADY_NOTED.
|
||||
*
|
||||
* @param {number} callLengthSeconds the call length in seconds
|
||||
* @private
|
||||
*/
|
||||
_noteConnectionLength: function(callLengthSeconds) {
|
||||
|
||||
var bucket = this.mozLoop.TWO_WAY_MEDIA_CONN_LENGTH.SHORTER_THAN_10S;
|
||||
|
||||
if (callLengthSeconds >= 10 && callLengthSeconds <= 30) {
|
||||
bucket = this.mozLoop.TWO_WAY_MEDIA_CONN_LENGTH.BETWEEN_10S_AND_30S;
|
||||
} else if (callLengthSeconds > 30 && callLengthSeconds <= 300) {
|
||||
bucket = this.mozLoop.TWO_WAY_MEDIA_CONN_LENGTH.BETWEEN_30S_AND_5M;
|
||||
} else if (callLengthSeconds > 300) {
|
||||
bucket = this.mozLoop.TWO_WAY_MEDIA_CONN_LENGTH.MORE_THAN_5M;
|
||||
}
|
||||
|
||||
this.mozLoop.telemetryAddKeyedValue("LOOP_TWO_WAY_MEDIA_CONN_LENGTH",
|
||||
bucket);
|
||||
this.connectionStartTime = this.CONNECTION_START_TIME_ALREADY_NOTED;
|
||||
|
||||
this._connectionLengthNotedCalls++;
|
||||
},
|
||||
|
||||
/**
|
||||
* Note connection length if it's valid (the startTime has been initialized
|
||||
* and is not later than endTime) and not yet already noted. If
|
||||
* this.mozLoop is not defined, we're assumed to be running in the
|
||||
* standalone client and return immediately.
|
||||
*
|
||||
* @param {number} startTime in milliseconds
|
||||
* @param {number} endTime in milliseconds
|
||||
* @private
|
||||
*/
|
||||
_noteConnectionLengthIfNeeded: function(startTime, endTime) {
|
||||
if (!this.mozLoop) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (startTime == this.CONNECTION_START_TIME_ALREADY_NOTED ||
|
||||
startTime == this.CONNECTION_START_TIME_UNINITIALIZED ||
|
||||
startTime > endTime) {
|
||||
console.log("_noteConnectionLengthIfNeeded called with " +
|
||||
" invalid params, either the calls were never" +
|
||||
" connected or there is a bug; startTime:", startTime,
|
||||
"endTime:", endTime);
|
||||
return;
|
||||
}
|
||||
|
||||
var callLengthSeconds = (endTime - startTime) / 1000;
|
||||
this._noteConnectionLength(callLengthSeconds);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -251,12 +251,14 @@ loop.shared.views = (function(_, l10n) {
|
|||
sdk: React.PropTypes.object.isRequired,
|
||||
video: React.PropTypes.object,
|
||||
audio: React.PropTypes.object,
|
||||
initiate: React.PropTypes.bool
|
||||
initiate: React.PropTypes.bool,
|
||||
isDesktop: React.PropTypes.bool
|
||||
},
|
||||
|
||||
getDefaultProps: function() {
|
||||
return {
|
||||
initiate: true,
|
||||
isDesktop: false,
|
||||
video: {enabled: true, visible: true},
|
||||
audio: {enabled: true, visible: true}
|
||||
};
|
||||
|
@ -271,6 +273,23 @@ loop.shared.views = (function(_, l10n) {
|
|||
|
||||
componentDidMount: function() {
|
||||
if (this.props.initiate) {
|
||||
/**
|
||||
* XXX This is a workaround for desktop machines that do not have a
|
||||
* camera installed. As we don't yet have device enumeration, when
|
||||
* we do, this can be removed (bug 1138851), and the sdk should handle it.
|
||||
*/
|
||||
if (this.props.isDesktop &&
|
||||
!window.MediaStreamTrack.getSources) {
|
||||
// If there's no getSources function, the sdk defines its own and caches
|
||||
// the result. So here we define the "normal" one which doesn't get cached, so
|
||||
// we can change it later.
|
||||
window.MediaStreamTrack.getSources = function(callback) {
|
||||
callback([{kind: "audio"}, {kind: "video"}]);
|
||||
};
|
||||
}
|
||||
|
||||
this.listenTo(this.props.sdk, "exception", this._handleSdkException.bind(this));
|
||||
|
||||
this.listenTo(this.props.model, "session:connected",
|
||||
this._onSessionConnected);
|
||||
this.listenTo(this.props.model, "session:stream-created",
|
||||
|
@ -317,6 +336,35 @@ loop.shared.views = (function(_, l10n) {
|
|||
}));
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles the SDK Exception event.
|
||||
*
|
||||
* https://tokbox.com/opentok/libraries/client/js/reference/ExceptionEvent.html
|
||||
*
|
||||
* @param {ExceptionEvent} event
|
||||
*/
|
||||
_handleSdkException: function(event) {
|
||||
/**
|
||||
* XXX This is a workaround for desktop machines that do not have a
|
||||
* camera installed. As we don't yet have device enumeration, when
|
||||
* we do, this can be removed (bug 1138851), and the sdk should handle it.
|
||||
*/
|
||||
if (this.publisher &&
|
||||
event.code === OT.ExceptionCodes.UNABLE_TO_PUBLISH &&
|
||||
event.message === "GetUserMedia" &&
|
||||
this.state.video.enabled) {
|
||||
this.state.video.enabled = false;
|
||||
|
||||
window.MediaStreamTrack.getSources = function(callback) {
|
||||
callback([{kind: "audio"}]);
|
||||
};
|
||||
|
||||
this.stopListening(this.publisher);
|
||||
this.publisher.destroy();
|
||||
this.startPublishing();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Publishes remote streams available once a session is connected.
|
||||
*
|
||||
|
|
|
@ -251,12 +251,14 @@ loop.shared.views = (function(_, l10n) {
|
|||
sdk: React.PropTypes.object.isRequired,
|
||||
video: React.PropTypes.object,
|
||||
audio: React.PropTypes.object,
|
||||
initiate: React.PropTypes.bool
|
||||
initiate: React.PropTypes.bool,
|
||||
isDesktop: React.PropTypes.bool
|
||||
},
|
||||
|
||||
getDefaultProps: function() {
|
||||
return {
|
||||
initiate: true,
|
||||
isDesktop: false,
|
||||
video: {enabled: true, visible: true},
|
||||
audio: {enabled: true, visible: true}
|
||||
};
|
||||
|
@ -271,6 +273,23 @@ loop.shared.views = (function(_, l10n) {
|
|||
|
||||
componentDidMount: function() {
|
||||
if (this.props.initiate) {
|
||||
/**
|
||||
* XXX This is a workaround for desktop machines that do not have a
|
||||
* camera installed. As we don't yet have device enumeration, when
|
||||
* we do, this can be removed (bug 1138851), and the sdk should handle it.
|
||||
*/
|
||||
if (this.props.isDesktop &&
|
||||
!window.MediaStreamTrack.getSources) {
|
||||
// If there's no getSources function, the sdk defines its own and caches
|
||||
// the result. So here we define the "normal" one which doesn't get cached, so
|
||||
// we can change it later.
|
||||
window.MediaStreamTrack.getSources = function(callback) {
|
||||
callback([{kind: "audio"}, {kind: "video"}]);
|
||||
};
|
||||
}
|
||||
|
||||
this.listenTo(this.props.sdk, "exception", this._handleSdkException.bind(this));
|
||||
|
||||
this.listenTo(this.props.model, "session:connected",
|
||||
this._onSessionConnected);
|
||||
this.listenTo(this.props.model, "session:stream-created",
|
||||
|
@ -317,6 +336,35 @@ loop.shared.views = (function(_, l10n) {
|
|||
}));
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles the SDK Exception event.
|
||||
*
|
||||
* https://tokbox.com/opentok/libraries/client/js/reference/ExceptionEvent.html
|
||||
*
|
||||
* @param {ExceptionEvent} event
|
||||
*/
|
||||
_handleSdkException: function(event) {
|
||||
/**
|
||||
* XXX This is a workaround for desktop machines that do not have a
|
||||
* camera installed. As we don't yet have device enumeration, when
|
||||
* we do, this can be removed (bug 1138851), and the sdk should handle it.
|
||||
*/
|
||||
if (this.publisher &&
|
||||
event.code === OT.ExceptionCodes.UNABLE_TO_PUBLISH &&
|
||||
event.message === "GetUserMedia" &&
|
||||
this.state.video.enabled) {
|
||||
this.state.video.enabled = false;
|
||||
|
||||
window.MediaStreamTrack.getSources = function(callback) {
|
||||
callback([{kind: "audio"}]);
|
||||
};
|
||||
|
||||
this.stopListening(this.publisher);
|
||||
this.publisher.destroy();
|
||||
this.startPublishing();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Publishes remote streams available once a session is connected.
|
||||
*
|
||||
|
|
|
@ -103,6 +103,7 @@ class Test1BrowserCall(MarionetteTestCase):
|
|||
self.assertEqual(media_container.tag_name, "div", "expect a video container")
|
||||
|
||||
def local_get_and_verify_room_url(self):
|
||||
self.switch_to_chatbox()
|
||||
button = self.wait_for_element_displayed(By.CLASS_NAME, "btn-copy")
|
||||
|
||||
button.click()
|
||||
|
@ -111,8 +112,8 @@ class Test1BrowserCall(MarionetteTestCase):
|
|||
room_url = pyperclip.paste()
|
||||
|
||||
self.assertIn(urlparse.urlparse(room_url).scheme, ['http', 'https'],
|
||||
"room URL returned by server " + room_url +
|
||||
" has invalid scheme")
|
||||
"room URL returned by server: '" + room_url +
|
||||
"' has invalid scheme")
|
||||
return room_url
|
||||
|
||||
def standalone_load_and_join_room(self, url):
|
||||
|
@ -152,7 +153,8 @@ class Test1BrowserCall(MarionetteTestCase):
|
|||
self.switch_to_standalone()
|
||||
self.check_video(".media .screen .OT_subscriber .OT_widget-container")
|
||||
|
||||
def local_leave_room_and_verify_feedback(self):
|
||||
def remote_leave_room_and_verify_feedback(self):
|
||||
self.switch_to_standalone()
|
||||
button = self.marionette.find_element(By.CLASS_NAME, "btn-hangup")
|
||||
|
||||
button.click()
|
||||
|
@ -161,6 +163,66 @@ class Test1BrowserCall(MarionetteTestCase):
|
|||
feedback_form = self.wait_for_element_displayed(By.CLASS_NAME, "faces")
|
||||
self.assertEqual(feedback_form.tag_name, "div", "expect feedback form")
|
||||
|
||||
def local_get_chatbox_window_expr(self, expr):
|
||||
"""
|
||||
:expr: a sub-expression which must begin with a property of the
|
||||
global content window (e.g. "location.path")
|
||||
|
||||
:return: the value of the given sub-expression as evaluated in the
|
||||
chatbox content window
|
||||
"""
|
||||
self.marionette.set_context("chrome")
|
||||
self.marionette.switch_to_frame()
|
||||
|
||||
# XXX should be using wait_for_element_displayed, but need to wait
|
||||
# for Marionette bug 1094246 to be fixed.
|
||||
chatbox = self.wait_for_element_exists(By.TAG_NAME, 'chatbox')
|
||||
script = '''
|
||||
let chatBrowser = document.getAnonymousElementByAttribute(
|
||||
arguments[0], 'class',
|
||||
'chat-frame')
|
||||
|
||||
// note that using wrappedJSObject waives X-ray vision, which
|
||||
// has security implications, but because we trust the code
|
||||
// running in the chatbox, it should be reasonably safe
|
||||
let chatGlobal = chatBrowser.contentWindow.wrappedJSObject;
|
||||
|
||||
return chatGlobal.''' + expr
|
||||
|
||||
return self.marionette.execute_script(script, [chatbox])
|
||||
|
||||
def local_get_media_start_time(self):
|
||||
return self.local_get_chatbox_window_expr(
|
||||
"loop.conversation._sdkDriver.connectionStartTime")
|
||||
|
||||
# XXX could be memoized
|
||||
def local_get_media_start_time_uninitialized(self):
|
||||
return self.local_get_chatbox_window_expr(
|
||||
"loop.conversation._sdkDriver.CONNECTION_START_TIME_UNINITIALIZED"
|
||||
)
|
||||
|
||||
def local_check_media_start_time_uninitialized(self):
|
||||
self.assertEqual(
|
||||
self.local_get_media_start_time(),
|
||||
self.local_get_media_start_time_uninitialized(),
|
||||
"media start time should be uninitialized before "
|
||||
"link clicker enters room")
|
||||
|
||||
def local_check_media_start_time_initialized(self):
|
||||
self.assertNotEqual(
|
||||
self.local_get_media_start_time(),
|
||||
self.local_get_media_start_time_uninitialized(),
|
||||
"media start time should be initialized after "
|
||||
"media is bidirectionally connected")
|
||||
|
||||
def local_check_connection_length_noted(self):
|
||||
noted_calls = self.local_get_chatbox_window_expr(
|
||||
"loop.conversation._sdkDriver._connectionLengthNotedCalls")
|
||||
|
||||
self.assertGreater(noted_calls, 0,
|
||||
"OTSdkDriver._connectionLengthNotedCalls should be "
|
||||
"> 0")
|
||||
|
||||
def test_1_browser_call(self):
|
||||
self.switch_to_panel()
|
||||
|
||||
|
@ -169,6 +231,9 @@ class Test1BrowserCall(MarionetteTestCase):
|
|||
# Check the self video in the conversation window
|
||||
self.local_check_room_self_video()
|
||||
|
||||
# make sure that the media start time is not initialized
|
||||
self.local_check_media_start_time_uninitialized()
|
||||
|
||||
room_url = self.local_get_and_verify_room_url()
|
||||
|
||||
# load the link clicker interface into the current content browser
|
||||
|
@ -178,13 +243,23 @@ class Test1BrowserCall(MarionetteTestCase):
|
|||
self.standalone_check_remote_video()
|
||||
self.local_check_remote_video()
|
||||
|
||||
# since bi-directional media is connected, make sure we've set
|
||||
# the start time
|
||||
self.local_check_media_start_time_initialized()
|
||||
|
||||
# XXX To enable this, we either need to navigate the permissions prompt
|
||||
# or have a route where we don't need the permissions prompt.
|
||||
# self.local_enable_screenshare()
|
||||
# self.standalone_check_remote_screenshare()
|
||||
|
||||
# hangup the call
|
||||
self.local_leave_room_and_verify_feedback()
|
||||
# We hangup on the remote side, because this also leaves the
|
||||
# local chatbox with the local publishing media still connected,
|
||||
# which means that the local_check_connection_length below
|
||||
# verifies that the connection is noted at the time the remote media
|
||||
# drops, rather than waiting until the window closes.
|
||||
self.remote_leave_room_and_verify_feedback()
|
||||
|
||||
self.local_check_connection_length_noted()
|
||||
|
||||
def tearDown(self):
|
||||
self.loop_test_servers.shutdown()
|
||||
|
|
|
@ -20,22 +20,30 @@ add_task(function* test_initialize() {
|
|||
});
|
||||
|
||||
/**
|
||||
* Tests that boolean histograms exist and can be updated.
|
||||
* Tests that enumerated bucket histograms exist and can be updated.
|
||||
*/
|
||||
add_task(function* test_mozLoop_telemetryAdd_boolean() {
|
||||
for (let histogramId of [
|
||||
"LOOP_CLIENT_CALL_URL_REQUESTS_SUCCESS",
|
||||
"LOOP_CLIENT_CALL_URL_SHARED",
|
||||
]) {
|
||||
let histogram = Services.telemetry.getHistogramById(histogramId);
|
||||
add_task(function* test_mozLoop_telemetryAdd_buckets() {
|
||||
let histogramId = "LOOP_TWO_WAY_MEDIA_CONN_LENGTH";
|
||||
let histogram = Services.telemetry.getKeyedHistogramById(histogramId);
|
||||
let CONN_LENGTH = gMozLoopAPI.TWO_WAY_MEDIA_CONN_LENGTH;
|
||||
|
||||
histogram.clear();
|
||||
for (let value of [false, false, true]) {
|
||||
gMozLoopAPI.telemetryAdd(histogramId, value);
|
||||
}
|
||||
|
||||
let snapshot = histogram.snapshot();
|
||||
is(snapshot.counts[0], 2, "snapshot.counts[0] == 2");
|
||||
is(snapshot.counts[1], 1, "snapshot.counts[1] == 1");
|
||||
histogram.clear();
|
||||
for (let value of [CONN_LENGTH.SHORTER_THAN_10S,
|
||||
CONN_LENGTH.BETWEEN_10S_AND_30S,
|
||||
CONN_LENGTH.BETWEEN_10S_AND_30S,
|
||||
CONN_LENGTH.BETWEEN_30S_AND_5M,
|
||||
CONN_LENGTH.BETWEEN_30S_AND_5M,
|
||||
CONN_LENGTH.BETWEEN_30S_AND_5M,
|
||||
CONN_LENGTH.MORE_THAN_5M,
|
||||
CONN_LENGTH.MORE_THAN_5M,
|
||||
CONN_LENGTH.MORE_THAN_5M,
|
||||
CONN_LENGTH.MORE_THAN_5M]) {
|
||||
gMozLoopAPI.telemetryAddKeyedValue(histogramId, value);
|
||||
}
|
||||
|
||||
let snapshot = histogram.snapshot();
|
||||
is(snapshot["SHORTER_THAN_10S"].sum, 1, "TWO_WAY_MEDIA_CONN_LENGTH.SHORTER_THAN_10S");
|
||||
is(snapshot["BETWEEN_10S_AND_30S"].sum, 2, "TWO_WAY_MEDIA_CONN_LENGTH.BETWEEN_10S_AND_30S");
|
||||
is(snapshot["BETWEEN_30S_AND_5M"].sum, 3, "TWO_WAY_MEDIA_CONN_LENGTH.BETWEEN_30S_AND_5M");
|
||||
is(snapshot["MORE_THAN_5M"].sum, 4, "TWO_WAY_MEDIA_CONN_LENGTH.MORE_THAN_5M");
|
||||
});
|
||||
|
|
|
@ -770,6 +770,14 @@ describe("loop.store.ConversationStore", function () {
|
|||
});
|
||||
});
|
||||
|
||||
describe("#windowUnload", function() {
|
||||
it("should disconnect from the servers via the sdk", function() {
|
||||
store.windowUnload();
|
||||
|
||||
sinon.assert.calledOnce(sdkDriver.disconnectSession);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Events", function() {
|
||||
describe("Websocket progress", function() {
|
||||
beforeEach(function() {
|
||||
|
|
|
@ -12,7 +12,7 @@ describe("loop.OTSdkDriver", function () {
|
|||
var SCREEN_SHARE_STATES = loop.shared.utils.SCREEN_SHARE_STATES;
|
||||
|
||||
var sandbox;
|
||||
var dispatcher, driver, publisher, sdk, session, sessionData;
|
||||
var dispatcher, driver, mozLoop, publisher, sdk, session, sessionData;
|
||||
var fakeLocalElement, fakeRemoteElement, fakeScreenElement;
|
||||
var publisherConfig, fakeEvent;
|
||||
|
||||
|
@ -64,9 +64,20 @@ describe("loop.OTSdkDriver", function () {
|
|||
}
|
||||
};
|
||||
|
||||
mozLoop = {
|
||||
telemetryAddKeyedValue: sinon.stub(),
|
||||
TWO_WAY_MEDIA_CONN_LENGTH: {
|
||||
SHORTER_THAN_10S: "SHORTER_THAN_10S",
|
||||
BETWEEN_10S_AND_30S: "BETWEEN_10S_AND_30S",
|
||||
BETWEEN_30S_AND_5M: "BETWEEN_30S_AND_5M",
|
||||
MORE_THAN_5M: "MORE_THAN_5M"
|
||||
}
|
||||
};
|
||||
|
||||
driver = new loop.OTSdkDriver({
|
||||
dispatcher: dispatcher,
|
||||
sdk: sdk
|
||||
sdk: sdk,
|
||||
mozLoop: mozLoop
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -86,6 +97,12 @@ describe("loop.OTSdkDriver", function () {
|
|||
new loop.OTSdkDriver({dispatcher: dispatcher});
|
||||
}).to.Throw(/sdk/);
|
||||
});
|
||||
|
||||
it("should initialize the connectionStartTime to 'uninitialized'", function() {
|
||||
var driver = new loop.OTSdkDriver({sdk: sdk, dispatcher: dispatcher, mozLoop: mozLoop});
|
||||
|
||||
expect(driver.connectionStartTime).to.eql(driver.CONNECTION_START_TIME_UNINITIALIZED);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#setupStreamElements", function() {
|
||||
|
@ -293,7 +310,7 @@ describe("loop.OTSdkDriver", function () {
|
|||
});
|
||||
});
|
||||
|
||||
describe("#disconnectionSession", function() {
|
||||
describe("#disconnectSession", function() {
|
||||
it("should disconnect the session", function() {
|
||||
driver.session = session;
|
||||
|
||||
|
@ -309,6 +326,94 @@ describe("loop.OTSdkDriver", function () {
|
|||
|
||||
sinon.assert.calledOnce(publisher.destroy);
|
||||
});
|
||||
|
||||
it("should call _noteConnectionLengthIfNeeded with connection duration", function() {
|
||||
driver.session = session;
|
||||
var startTime = 1;
|
||||
var endTime = 3;
|
||||
driver.connectionStartTime = startTime;
|
||||
sandbox.stub(performance, "now").returns(endTime);
|
||||
sandbox.stub(driver, "_noteConnectionLengthIfNeeded");
|
||||
|
||||
driver.disconnectSession();
|
||||
|
||||
sinon.assert.calledWith(driver._noteConnectionLengthIfNeeded, startTime,
|
||||
endTime);
|
||||
});
|
||||
|
||||
it("should reset the connectionStartTime", function() {
|
||||
driver.session = session;
|
||||
var startTime = 1;
|
||||
driver.connectionStartTime = startTime;
|
||||
sandbox.stub(performance, "now");
|
||||
sandbox.stub(driver, "_noteConnectionLengthIfNeeded");
|
||||
|
||||
driver.disconnectSession();
|
||||
|
||||
expect(driver.connectionStartTime).to.eql(driver.CONNECTION_START_TIME_UNINITIALIZED);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#_noteConnectionLengthIfNeeded", function() {
|
||||
var startTimeMS;
|
||||
beforeEach(function() {
|
||||
startTimeMS = 1;
|
||||
driver.connectionStartTime = startTimeMS;
|
||||
});
|
||||
|
||||
|
||||
it("should set connectionStartTime to CONNECTION_START_TIME_ALREADY_NOTED", function() {
|
||||
var endTimeMS = 3;
|
||||
driver._noteConnectionLengthIfNeeded(startTimeMS, endTimeMS);
|
||||
|
||||
expect(driver.connectionStartTime).to.eql(driver.CONNECTION_START_TIME_ALREADY_NOTED);
|
||||
});
|
||||
|
||||
it("should call mozLoop.noteConnectionLength with SHORTER_THAN_10S for calls less than 10s", function() {
|
||||
var endTimeMS = 9000;
|
||||
|
||||
driver._noteConnectionLengthIfNeeded(startTimeMS, endTimeMS);
|
||||
|
||||
sinon.assert.calledOnce(mozLoop.telemetryAddKeyedValue);
|
||||
sinon.assert.calledWith(mozLoop.telemetryAddKeyedValue,
|
||||
"LOOP_TWO_WAY_MEDIA_CONN_LENGTH",
|
||||
mozLoop.TWO_WAY_MEDIA_CONN_LENGTH.SHORTER_THAN_10S);
|
||||
});
|
||||
|
||||
it("should call mozLoop.noteConnectionLength with BETWEEN_10S_AND_30S for 15s calls",
|
||||
function() {
|
||||
var endTimeMS = 15000;
|
||||
|
||||
driver._noteConnectionLengthIfNeeded(startTimeMS, endTimeMS);
|
||||
|
||||
sinon.assert.calledOnce(mozLoop.telemetryAddKeyedValue);
|
||||
sinon.assert.calledWith(mozLoop.telemetryAddKeyedValue,
|
||||
"LOOP_TWO_WAY_MEDIA_CONN_LENGTH",
|
||||
mozLoop.TWO_WAY_MEDIA_CONN_LENGTH.BETWEEN_10S_AND_30S);
|
||||
});
|
||||
|
||||
it("should call mozLoop.noteConnectionLength with BETWEEN_30S_AND_5M for 60s calls",
|
||||
function() {
|
||||
var endTimeMS = 60 * 1000;
|
||||
|
||||
driver._noteConnectionLengthIfNeeded(startTimeMS, endTimeMS);
|
||||
|
||||
sinon.assert.calledOnce(mozLoop.telemetryAddKeyedValue);
|
||||
sinon.assert.calledWith(mozLoop.telemetryAddKeyedValue,
|
||||
"LOOP_TWO_WAY_MEDIA_CONN_LENGTH",
|
||||
mozLoop.TWO_WAY_MEDIA_CONN_LENGTH.BETWEEN_30S_AND_5M);
|
||||
});
|
||||
|
||||
it("should call mozLoop.noteConnectionLength with MORE_THAN_5M for 10m calls", function() {
|
||||
var endTimeMS = 10 * 60 * 1000;
|
||||
|
||||
driver._noteConnectionLengthIfNeeded(startTimeMS, endTimeMS);
|
||||
|
||||
sinon.assert.calledOnce(mozLoop.telemetryAddKeyedValue);
|
||||
sinon.assert.calledWith(mozLoop.telemetryAddKeyedValue,
|
||||
"LOOP_TWO_WAY_MEDIA_CONN_LENGTH",
|
||||
mozLoop.TWO_WAY_MEDIA_CONN_LENGTH.MORE_THAN_5M);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#forceDisconnectAll", function() {
|
||||
|
@ -388,6 +493,23 @@ describe("loop.OTSdkDriver", function () {
|
|||
sinon.assert.calledWithMatch(dispatcher.dispatch,
|
||||
sinon.match.hasOwn("peerHungup", false));
|
||||
});
|
||||
|
||||
|
||||
it("should call _noteConnectionLengthIfNeeded with connection duration", function() {
|
||||
driver.session = session;
|
||||
var startTime = 1;
|
||||
var endTime = 3;
|
||||
driver.connectionStartTime = startTime;
|
||||
sandbox.stub(performance, "now").returns(endTime);
|
||||
sandbox.stub(driver, "_noteConnectionLengthIfNeeded");
|
||||
|
||||
session.trigger("connectionDestroyed", {
|
||||
reason: "clientDisconnected"
|
||||
});
|
||||
|
||||
sinon.assert.calledWith(driver._noteConnectionLengthIfNeeded, startTime,
|
||||
endTime);
|
||||
});
|
||||
});
|
||||
|
||||
describe("sessionDisconnected", function() {
|
||||
|
@ -416,6 +538,23 @@ describe("loop.OTSdkDriver", function () {
|
|||
sinon.assert.calledWithMatch(dispatcher.dispatch,
|
||||
sinon.match.hasOwn("reason", FAILURE_DETAILS.EXPIRED_OR_INVALID));
|
||||
});
|
||||
|
||||
it("should call _noteConnectionLengthIfNeeded with connection duration", function() {
|
||||
driver.session = session;
|
||||
var startTime = 1;
|
||||
var endTime = 3;
|
||||
driver.connectionStartTime = startTime;
|
||||
sandbox.stub(performance, "now").returns(endTime);
|
||||
sandbox.stub(driver, "_noteConnectionLengthIfNeeded");
|
||||
|
||||
session.trigger("sessionDisconnected", {
|
||||
reason: "networkDisconnected"
|
||||
});
|
||||
|
||||
sinon.assert.calledWith(driver._noteConnectionLengthIfNeeded, startTime,
|
||||
endTime);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe("streamCreated (publisher/local)", function() {
|
||||
|
@ -479,7 +618,7 @@ describe("loop.OTSdkDriver", function () {
|
|||
fakeStream, fakeScreenElement, publisherConfig);
|
||||
});
|
||||
|
||||
it("should dispach a mediaConnected action if both streams are up", function() {
|
||||
it("should dispatch a mediaConnected action if both streams are up", function() {
|
||||
driver._publishedLocalStream = true;
|
||||
|
||||
session.trigger("streamCreated", {stream: fakeStream});
|
||||
|
@ -490,6 +629,17 @@ describe("loop.OTSdkDriver", function () {
|
|||
sinon.match.hasOwn("name", "mediaConnected"));
|
||||
});
|
||||
|
||||
it("should store the start time when both streams are up", function() {
|
||||
driver._publishedLocalStream = true;
|
||||
var startTime = 1;
|
||||
sandbox.stub(performance, "now").returns(startTime);
|
||||
|
||||
session.trigger("streamCreated", {stream: fakeStream});
|
||||
|
||||
expect(driver.connectionStartTime).to.eql(startTime);
|
||||
});
|
||||
|
||||
|
||||
it("should not dispatch a mediaConnected action for screen sharing streams",
|
||||
function() {
|
||||
driver._publishedLocalStream = true;
|
||||
|
|
|
@ -344,7 +344,8 @@ describe("loop.shared.views", function() {
|
|||
}, Backbone.Events);
|
||||
fakeSDK = {
|
||||
initPublisher: sandbox.stub().returns(fakePublisher),
|
||||
initSession: sandbox.stub().returns(fakeSession)
|
||||
initSession: sandbox.stub().returns(fakeSession),
|
||||
on: sandbox.stub()
|
||||
};
|
||||
model = new sharedModels.ConversationModel(fakeSessionData, {
|
||||
sdk: fakeSDK
|
||||
|
|
|
@ -126,7 +126,9 @@ describe("loop.webapp", function() {
|
|||
client: client,
|
||||
conversation: conversation,
|
||||
notifications: notifications,
|
||||
sdk: {},
|
||||
sdk: {
|
||||
on: sandbox.stub()
|
||||
},
|
||||
dispatcher: dispatcher
|
||||
});
|
||||
});
|
||||
|
|
|
@ -32,6 +32,7 @@ Services.prefs.setBoolPref("loop.enabled", true);
|
|||
|
||||
// Cleanup function for all tests
|
||||
do_register_cleanup(() => {
|
||||
Services.prefs.clearUserPref("loop.enabled");
|
||||
MozLoopService.errors.clear();
|
||||
});
|
||||
|
||||
|
@ -50,6 +51,19 @@ function setupFakeLoopServer() {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the userProfile to make the service think we're logged into FxA.
|
||||
*/
|
||||
function setupFakeFxAUserProfile() {
|
||||
MozLoopServiceInternal.fxAOAuthTokenData = { token_type: "bearer" };
|
||||
MozLoopServiceInternal.fxAOAuthProfile = { email: "fake@invalid.com" };
|
||||
|
||||
do_register_cleanup(function() {
|
||||
MozLoopServiceInternal.fxAOAuthTokenData = null;
|
||||
MozLoopServiceInternal.fxAOAuthProfile = null;
|
||||
});
|
||||
}
|
||||
|
||||
function waitForCondition(aConditionFn, aMaxTries=50, aCheckInterval=100) {
|
||||
function tryAgain() {
|
||||
function tryNow() {
|
||||
|
|
|
@ -22,55 +22,6 @@ let msgHandler = function(msg) {
|
|||
}
|
||||
};
|
||||
|
||||
add_task(function* test_busy_2guest_calls() {
|
||||
actionReceived = false;
|
||||
|
||||
mockPushHandler.registrationPushURL = kEndPointUrl;
|
||||
|
||||
yield MozLoopService.promiseRegisteredWithServers(LOOP_SESSION_TYPE.GUEST);
|
||||
|
||||
let opened = 0;
|
||||
let windowId;
|
||||
Chat.open = function(contentWindow, origin, title, url) {
|
||||
opened++;
|
||||
windowId = url.match(/about:loopconversation\#(\d+)$/)[1];
|
||||
};
|
||||
|
||||
mockPushHandler.notify(1, MozLoopService.channelIDs.callsGuest);
|
||||
|
||||
yield waitForCondition(() => { return actionReceived && opened > 0; }).then(() => {
|
||||
do_check_true(opened === 1, "should open only one chat window");
|
||||
do_check_true(actionReceived, "should respond with busy/reject to second call");
|
||||
LoopCalls.clearCallInProgress(windowId);
|
||||
}, () => {
|
||||
do_throw("should have opened a chat window for first call and rejected second call");
|
||||
});
|
||||
});
|
||||
|
||||
add_task(function* test_busy_1fxa_1guest_calls() {
|
||||
actionReceived = false;
|
||||
|
||||
yield MozLoopService.promiseRegisteredWithServers(LOOP_SESSION_TYPE.GUEST);
|
||||
yield MozLoopService.promiseRegisteredWithServers(LOOP_SESSION_TYPE.FXA);
|
||||
|
||||
let opened = 0;
|
||||
let windowId;
|
||||
Chat.open = function(contentWindow, origin, title, url) {
|
||||
opened++;
|
||||
windowId = url.match(/about:loopconversation\#(\d+)$/)[1];
|
||||
};
|
||||
|
||||
mockPushHandler.notify(1, MozLoopService.channelIDs.callsFxA);
|
||||
mockPushHandler.notify(1, MozLoopService.channelIDs.callsGuest);
|
||||
|
||||
yield waitForCondition(() => { return actionReceived && opened > 0; }).then(() => {
|
||||
do_check_true(opened === 1, "should open only one chat window");
|
||||
do_check_true(actionReceived, "should respond with busy/reject to second call");
|
||||
LoopCalls.clearCallInProgress(windowId);
|
||||
}, () => {
|
||||
do_throw("should have opened a chat window for first call and rejected second call");
|
||||
});
|
||||
});
|
||||
|
||||
add_task(function* test_busy_2fxa_calls() {
|
||||
actionReceived = false;
|
||||
|
@ -95,31 +46,6 @@ add_task(function* test_busy_2fxa_calls() {
|
|||
});
|
||||
});
|
||||
|
||||
add_task(function* test_busy_1guest_1fxa_calls() {
|
||||
actionReceived = false;
|
||||
|
||||
yield MozLoopService.promiseRegisteredWithServers(LOOP_SESSION_TYPE.GUEST);
|
||||
yield MozLoopService.promiseRegisteredWithServers(LOOP_SESSION_TYPE.FXA);
|
||||
|
||||
let opened = 0;
|
||||
let windowId;
|
||||
Chat.open = function(contentWindow, origin, title, url) {
|
||||
opened++;
|
||||
windowId = url.match(/about:loopconversation\#(\d+)$/)[1];
|
||||
};
|
||||
|
||||
mockPushHandler.notify(1, MozLoopService.channelIDs.callsGuest);
|
||||
mockPushHandler.notify(1, MozLoopService.channelIDs.callsFxA);
|
||||
|
||||
yield waitForCondition(() => { return actionReceived && opened > 0; }).then(() => {
|
||||
do_check_true(opened === 1, "should open only one chat window");
|
||||
do_check_true(actionReceived, "should respond with busy/reject to second call");
|
||||
LoopCalls.clearCallInProgress(windowId);
|
||||
}, () => {
|
||||
do_throw("should have opened a chat window for first call and rejected second call");
|
||||
});
|
||||
});
|
||||
|
||||
function run_test() {
|
||||
setupFakeLoopServer();
|
||||
|
||||
|
@ -134,6 +60,8 @@ function run_test() {
|
|||
|
||||
Services.io.offline = false;
|
||||
|
||||
mockPushHandler.registrationPushURL = kEndPointUrl;
|
||||
|
||||
// For each notification received from the PushServer, MozLoopService will first query
|
||||
// for any pending calls on the FxA hawk session and then again using the guest session.
|
||||
// A pair of response objects in the callsResponses array will be consumed for each
|
||||
|
|
|
@ -33,13 +33,13 @@ add_test(function test_do_not_disturb_disabled_should_open_chat_window() {
|
|||
|
||||
mockPushHandler.registrationPushURL = kEndPointUrl;
|
||||
|
||||
MozLoopService.promiseRegisteredWithServers().then(() => {
|
||||
MozLoopService.promiseRegisteredWithServers(LOOP_SESSION_TYPE.FXA).then(() => {
|
||||
let opened = false;
|
||||
Chat.open = function() {
|
||||
opened = true;
|
||||
};
|
||||
|
||||
mockPushHandler.notify(1, MozLoopService.channelIDs.callsGuest);
|
||||
mockPushHandler.notify(1, MozLoopService.channelIDs.callsFxA);
|
||||
|
||||
waitForCondition(function() opened).then(() => {
|
||||
run_next_test();
|
||||
|
@ -58,7 +58,7 @@ add_test(function test_do_not_disturb_enabled_shouldnt_open_chat_window() {
|
|||
opened = true;
|
||||
};
|
||||
|
||||
mockPushHandler.notify(1, MozLoopService.channelIDs.callsGuest);
|
||||
mockPushHandler.notify(1, MozLoopService.channelIDs.callsFxA);
|
||||
|
||||
do_timeout(500, function() {
|
||||
do_check_false(opened, "should not open a chat window");
|
||||
|
@ -69,6 +69,8 @@ add_test(function test_do_not_disturb_enabled_shouldnt_open_chat_window() {
|
|||
function run_test() {
|
||||
setupFakeLoopServer();
|
||||
|
||||
setupFakeFxAUserProfile();
|
||||
|
||||
loopServer.registerPathHandler("/registration", (request, response) => {
|
||||
response.setStatusLine(null, 200, "OK");
|
||||
response.processAsync();
|
||||
|
|
|
@ -173,14 +173,14 @@ add_task(cleanup_between_tests);
|
|||
function run_test() {
|
||||
setupFakeLoopServer();
|
||||
|
||||
// Set the expiry time one hour in the future so that an error is shown when the guest session expires.
|
||||
MozLoopServiceInternal.expiryTimeSeconds = (Date.now() / 1000) + 3600;
|
||||
Services.prefs.setBoolPref("loop.createdRoom", true);
|
||||
|
||||
do_register_cleanup(() => {
|
||||
Services.prefs.clearUserPref("loop.hawk-session-token");
|
||||
Services.prefs.clearUserPref("loop.hawk-session-token.fxa");
|
||||
Services.prefs.clearUserPref("loop.urlsExpiryTimeSeconds");
|
||||
Services.prefs.clearUserPref("network.dns.offline-localhost");
|
||||
Services.prefs.clearUserPref("loop.createdRoom");
|
||||
MozLoopService.errors.clear();
|
||||
});
|
||||
|
||||
|
|
|
@ -18,35 +18,31 @@ add_task(function test_initialize_no_expiry() {
|
|||
});
|
||||
|
||||
/**
|
||||
* Tests that registration doesn't happen when the expiry time is
|
||||
* in the past.
|
||||
* Tests that registration doesn't happen when there has been no
|
||||
* room created.
|
||||
*/
|
||||
add_task(function test_initialize_expiry_past() {
|
||||
// Set time to be 2 seconds in the past.
|
||||
let nowSeconds = Date.now() / 1000;
|
||||
Services.prefs.setIntPref("loop.urlsExpiryTimeSeconds", nowSeconds - 2);
|
||||
add_task(function test_initialize_no_guest_rooms() {
|
||||
Services.prefs.setBoolPref("loop.createdRoom", false);
|
||||
startTimerCalled = false;
|
||||
|
||||
MozLoopService.initialize();
|
||||
|
||||
Assert.equal(startTimerCalled, false,
|
||||
"should not register when expiry time is in past");
|
||||
"should not register when no guest rooms have been created");
|
||||
});
|
||||
|
||||
/**
|
||||
* Tests that registration happens when the expiry time is in
|
||||
* the future.
|
||||
*/
|
||||
add_task(function test_initialize_starts_timer() {
|
||||
// Set time to be 1 minute in the future
|
||||
let nowSeconds = Date.now() / 1000;
|
||||
Services.prefs.setIntPref("loop.urlsExpiryTimeSeconds", nowSeconds + 60);
|
||||
add_task(function test_initialize_with_guest_rooms() {
|
||||
Services.prefs.setBoolPref("loop.createdRoom", true);
|
||||
startTimerCalled = false;
|
||||
|
||||
MozLoopService.initialize();
|
||||
|
||||
Assert.equal(startTimerCalled, true,
|
||||
"should start the timer when expiry time is in the future");
|
||||
"should start the timer when guest rooms have been created");
|
||||
});
|
||||
|
||||
function run_test() {
|
||||
|
@ -58,5 +54,9 @@ function run_test() {
|
|||
startTimerCalled = true;
|
||||
};
|
||||
|
||||
do_register_cleanup(function() {
|
||||
Services.prefs.clearUserPref("loop.createdRoom");
|
||||
});
|
||||
|
||||
run_next_test();
|
||||
}
|
||||
|
|
|
@ -12,13 +12,13 @@ add_test(function test_openChatWindow_on_notification() {
|
|||
|
||||
mockPushHandler.registrationPushURL = kEndPointUrl;
|
||||
|
||||
MozLoopService.promiseRegisteredWithServers().then(() => {
|
||||
MozLoopService.promiseRegisteredWithServers(LOOP_SESSION_TYPE.FXA).then(() => {
|
||||
let opened = false;
|
||||
Chat.open = function() {
|
||||
opened = true;
|
||||
};
|
||||
|
||||
mockPushHandler.notify(1, MozLoopService.channelIDs.callsGuest);
|
||||
mockPushHandler.notify(1, MozLoopService.channelIDs.callsFxA);
|
||||
|
||||
waitForCondition(function() opened).then(() => {
|
||||
do_check_true(opened, "should open a chat window");
|
||||
|
@ -37,6 +37,8 @@ add_test(function test_openChatWindow_on_notification() {
|
|||
function run_test() {
|
||||
setupFakeLoopServer();
|
||||
|
||||
setupFakeFxAUserProfile();
|
||||
|
||||
loopServer.registerPathHandler("/registration", (request, response) => {
|
||||
response.setStatusLine(null, 200, "OK");
|
||||
response.processAsync();
|
||||
|
|
|
@ -59,12 +59,11 @@ function run_test() {
|
|||
response.setStatusLine(null, 200, "OK");
|
||||
});
|
||||
|
||||
let nowSeconds = Date.now() / 1000;
|
||||
Services.prefs.setIntPref("loop.urlsExpiryTimeSeconds", nowSeconds + 60);
|
||||
Services.prefs.setBoolPref("loop.createdRoom", true);
|
||||
|
||||
do_register_cleanup(function() {
|
||||
Services.prefs.clearUserPref("loop.hawk-session-token");
|
||||
Services.prefs.clearUserPref("loop.urlsExpiryTimeSeconds");
|
||||
Services.prefs.clearUserPref("loop.createdRoom");
|
||||
});
|
||||
|
||||
run_next_test();
|
||||
|
|
|
@ -13,17 +13,17 @@ const FAKE_FXA_PROFILE = JSON.stringify({
|
|||
});
|
||||
const LOOP_FXA_TOKEN_PREF = "loop.fxa_oauth.tokendata";
|
||||
const LOOP_FXA_PROFILE_PREF = "loop.fxa_oauth.profile";
|
||||
const LOOP_URL_EXPIRY_PREF = "loop.urlsExpiryTimeSeconds";
|
||||
const LOOP_CREATED_ROOM_PREF = "loop.createdRoom";
|
||||
const LOOP_INITIAL_DELAY_PREF = "loop.initialDelay";
|
||||
|
||||
/**
|
||||
* This file is to test restart+reauth.
|
||||
*/
|
||||
|
||||
add_task(function test_initialize_with_expired_urls_and_no_auth_token() {
|
||||
add_task(function test_initialize_with_no_guest_rooms_and_no_auth_token() {
|
||||
// Set time to be 2 seconds in the past.
|
||||
var nowSeconds = Date.now() / 1000;
|
||||
Services.prefs.setIntPref(LOOP_URL_EXPIRY_PREF, nowSeconds - 2);
|
||||
Services.prefs.setBoolPref(LOOP_CREATED_ROOM_PREF, false);
|
||||
Services.prefs.clearUserPref(LOOP_FXA_TOKEN_PREF);
|
||||
|
||||
yield MozLoopService.initialize().then((msg) => {
|
||||
|
@ -34,8 +34,8 @@ add_task(function test_initialize_with_expired_urls_and_no_auth_token() {
|
|||
});
|
||||
});
|
||||
|
||||
add_task(function test_initialize_with_urls_and_no_auth_token() {
|
||||
Services.prefs.setIntPref(LOOP_URL_EXPIRY_PREF, Date.now() / 1000 + 10);
|
||||
add_task(function test_initialize_with_created_room_and_no_auth_token() {
|
||||
Services.prefs.setBoolPref(LOOP_CREATED_ROOM_PREF, true);
|
||||
Services.prefs.clearUserPref(LOOP_FXA_TOKEN_PREF);
|
||||
|
||||
loopServer.registerPathHandler("/registration", (request, response) => {
|
||||
|
@ -114,7 +114,7 @@ function run_test() {
|
|||
Services.prefs.clearUserPref(LOOP_INITIAL_DELAY_PREF);
|
||||
Services.prefs.clearUserPref(LOOP_FXA_TOKEN_PREF);
|
||||
Services.prefs.clearUserPref(LOOP_FXA_PROFILE_PREF);
|
||||
Services.prefs.clearUserPref(LOOP_URL_EXPIRY_PREF);
|
||||
Services.prefs.clearUserPref(LOOP_CREATED_ROOM_PREF);
|
||||
});
|
||||
|
||||
run_next_test();
|
||||
|
|
|
@ -34,7 +34,7 @@ add_test(function test_registration_invalid_token() {
|
|||
MozLoopService.promiseRegisteredWithServers().then(() => {
|
||||
// Due to the way the time stamp checking code works in hawkclient, we expect a couple
|
||||
// of authorization requests before we reset the token.
|
||||
Assert.equal(authorizationAttempts, 4); //hawk will repeat each registration attemtp twice: calls and rooms.
|
||||
Assert.equal(authorizationAttempts, 2); // Hawk will repeat the registration attempt twice.
|
||||
Assert.equal(Services.prefs.getCharPref(LOOP_HAWK_PREF), fakeSessionToken2);
|
||||
run_next_test();
|
||||
}, err => {
|
||||
|
|
|
@ -163,7 +163,7 @@ let ReaderParent = {
|
|||
|
||||
/**
|
||||
* Gets an article for a given URL. This method will download and parse a document
|
||||
* if it does not find the article in the tab data or the cache.
|
||||
* if it does not find the article in the browser data.
|
||||
*
|
||||
* @param url The article URL.
|
||||
* @param browser The browser where the article is currently loaded.
|
||||
|
@ -177,12 +177,6 @@ let ReaderParent = {
|
|||
return article;
|
||||
}
|
||||
|
||||
// Next, try to find a parsed article in the cache.
|
||||
article = yield ReaderMode.getArticleFromCache(url);
|
||||
if (article) {
|
||||
return article;
|
||||
}
|
||||
|
||||
// Article hasn't been found in the cache, we need to
|
||||
// download the page and parse the article out of it.
|
||||
return yield ReaderMode.downloadAndParseDocument(url);
|
||||
|
|
|
@ -7180,6 +7180,13 @@
|
|||
"kind": "boolean",
|
||||
"description": "Stores 1 every time the URL is copied or shared."
|
||||
},
|
||||
"LOOP_TWO_WAY_MEDIA_CONN_LENGTH": {
|
||||
"expires_in_version": "43",
|
||||
"kind": "count",
|
||||
"keyed": true,
|
||||
"releaseChannelCollection": "opt-out",
|
||||
"description": "Connection length for bi-directionally connected media"
|
||||
},
|
||||
"E10S_AUTOSTART": {
|
||||
"expires_in_version": "never",
|
||||
"kind": "boolean",
|
||||
|
|
Загрузка…
Ссылка в новой задаче