зеркало из https://github.com/mozilla/gecko-dev.git
Merge fx-team to m-c. a=merge
This commit is contained in:
Коммит
0d944297b7
|
@ -638,6 +638,37 @@ let LoopRoomsInternal = {
|
|||
}, callback);
|
||||
},
|
||||
|
||||
/**
|
||||
* Forwards connection status to the server.
|
||||
*
|
||||
* @param {String} roomToken The room token.
|
||||
* @param {String} sessionToken The session token for the
|
||||
* session that has been
|
||||
* joined.
|
||||
* @param {sharedActions.SdkStatus} status The connection status.
|
||||
* @param {Function} callback Optional. Function that will be invoked once
|
||||
* the operation finished. The first argument
|
||||
* passed will be an `Error` object or `null`.
|
||||
*/
|
||||
sendConnectionStatus: function(roomToken, sessionToken, status, callback) {
|
||||
if (!callback) {
|
||||
callback = function(error) {
|
||||
if (error) {
|
||||
MozLoopService.log.error(error);
|
||||
}
|
||||
};
|
||||
}
|
||||
this._postToRoom(roomToken, {
|
||||
action: "status",
|
||||
event: status.event,
|
||||
state: status.state,
|
||||
connections: status.connections,
|
||||
sendStreams: status.sendStreams,
|
||||
recvStreams: status.recvStreams,
|
||||
sessionToken: sessionToken
|
||||
}, callback);
|
||||
},
|
||||
|
||||
/**
|
||||
* Renames a room.
|
||||
*
|
||||
|
@ -780,6 +811,10 @@ this.LoopRooms = {
|
|||
return LoopRoomsInternal.leave(roomToken, sessionToken, callback);
|
||||
},
|
||||
|
||||
sendConnectionStatus: function(roomToken, sessionToken, status, callback) {
|
||||
return LoopRoomsInternal.sendConnectionStatus(roomToken, sessionToken, status, callback);
|
||||
},
|
||||
|
||||
rename: function(roomToken, newRoomName, callback) {
|
||||
return LoopRoomsInternal.rename(roomToken, newRoomName, callback);
|
||||
},
|
||||
|
|
|
@ -432,6 +432,8 @@ loop.panel = (function(_, mozL10n) {
|
|||
});
|
||||
|
||||
var RoomEntryContextItem = React.createClass({displayName: "RoomEntryContextItem",
|
||||
mixins: [loop.shared.mixins.WindowCloseMixin],
|
||||
|
||||
propTypes: {
|
||||
mozLoop: React.PropTypes.object.isRequired,
|
||||
roomUrls: React.PropTypes.object
|
||||
|
@ -441,6 +443,7 @@ loop.panel = (function(_, mozL10n) {
|
|||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
this.props.mozLoop.openURL(event.currentTarget.href);
|
||||
this.closeWindow();
|
||||
},
|
||||
|
||||
render: function() {
|
||||
|
@ -700,9 +703,17 @@ loop.panel = (function(_, mozL10n) {
|
|||
},
|
||||
|
||||
render: function() {
|
||||
var hostname;
|
||||
|
||||
try {
|
||||
hostname = new URL(this.state.url).hostname;
|
||||
} catch (ex) {
|
||||
// Empty catch - if there's an error, then we won't show the context.
|
||||
}
|
||||
|
||||
var contextClasses = React.addons.classSet({
|
||||
context: true,
|
||||
hide: !this.state.url ||
|
||||
hide: !hostname ||
|
||||
!this.props.mozLoop.getLoopPref("contextInConverations.enabled")
|
||||
});
|
||||
|
||||
|
@ -716,7 +727,7 @@ loop.panel = (function(_, mozL10n) {
|
|||
),
|
||||
React.createElement("img", {className: "context-preview", src: this.state.previewImage}),
|
||||
React.createElement("span", {className: "context-description"}, this.state.description),
|
||||
React.createElement("span", {className: "context-url"}, this.state.url)
|
||||
React.createElement("span", {className: "context-url"}, hostname)
|
||||
),
|
||||
React.createElement("button", {className: "btn btn-info new-room-button",
|
||||
onClick: this.handleCreateButtonClick,
|
||||
|
|
|
@ -432,6 +432,8 @@ loop.panel = (function(_, mozL10n) {
|
|||
});
|
||||
|
||||
var RoomEntryContextItem = React.createClass({
|
||||
mixins: [loop.shared.mixins.WindowCloseMixin],
|
||||
|
||||
propTypes: {
|
||||
mozLoop: React.PropTypes.object.isRequired,
|
||||
roomUrls: React.PropTypes.object
|
||||
|
@ -441,6 +443,7 @@ loop.panel = (function(_, mozL10n) {
|
|||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
this.props.mozLoop.openURL(event.currentTarget.href);
|
||||
this.closeWindow();
|
||||
},
|
||||
|
||||
render: function() {
|
||||
|
@ -700,9 +703,17 @@ loop.panel = (function(_, mozL10n) {
|
|||
},
|
||||
|
||||
render: function() {
|
||||
var hostname;
|
||||
|
||||
try {
|
||||
hostname = new URL(this.state.url).hostname;
|
||||
} catch (ex) {
|
||||
// Empty catch - if there's an error, then we won't show the context.
|
||||
}
|
||||
|
||||
var contextClasses = React.addons.classSet({
|
||||
context: true,
|
||||
hide: !this.state.url ||
|
||||
hide: !hostname ||
|
||||
!this.props.mozLoop.getLoopPref("contextInConverations.enabled")
|
||||
});
|
||||
|
||||
|
@ -716,7 +727,7 @@ loop.panel = (function(_, mozL10n) {
|
|||
</label>
|
||||
<img className="context-preview" src={this.state.previewImage}/>
|
||||
<span className="context-description">{this.state.description}</span>
|
||||
<span className="context-url">{this.state.url}</span>
|
||||
<span className="context-url">{hostname}</span>
|
||||
</div>
|
||||
<button className="btn btn-info new-room-button"
|
||||
onClick={this.handleCreateButtonClick}
|
||||
|
|
|
@ -499,6 +499,18 @@ loop.shared.actions = (function() {
|
|||
*/
|
||||
SendFeedbackError: Action.define("sendFeedbackError", {
|
||||
error: Error
|
||||
}),
|
||||
|
||||
/**
|
||||
* Used to inform of the current session, publisher and connection
|
||||
* status.
|
||||
*/
|
||||
ConnectionStatus: Action.define("connectionStatus", {
|
||||
event: String,
|
||||
state: String,
|
||||
connections: Number,
|
||||
sendStreams: Number,
|
||||
recvStreams: Number
|
||||
})
|
||||
};
|
||||
})();
|
||||
|
|
|
@ -156,7 +156,8 @@ loop.store.ActiveRoomStore = (function() {
|
|||
"videoDimensionsChanged",
|
||||
"startScreenShare",
|
||||
"endScreenShare",
|
||||
"updateSocialShareInfo"
|
||||
"updateSocialShareInfo",
|
||||
"connectionStatus"
|
||||
]);
|
||||
},
|
||||
|
||||
|
@ -639,6 +640,17 @@ loop.store.ActiveRoomStore = (function() {
|
|||
this.setStoreState({roomState: ROOM_STATES.SESSION_CONNECTED});
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles an SDK status update, forwarding it to the server.
|
||||
*
|
||||
* @param {sharedActions.ConnectionStatus} actionData
|
||||
*/
|
||||
connectionStatus: function(actionData) {
|
||||
this._mozLoop.rooms.sendConnectionStatus(this.getStoreState("roomToken"),
|
||||
this.getStoreState("sessionToken"),
|
||||
actionData);
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles the window being unloaded. Ensures the room is left.
|
||||
*/
|
||||
|
|
|
@ -17,39 +17,47 @@ loop.OTSdkDriver = (function() {
|
|||
* actions, and instruct the SDK what to do as a result of actions.
|
||||
*/
|
||||
var OTSdkDriver = function(options) {
|
||||
if (!options.dispatcher) {
|
||||
throw new Error("Missing option dispatcher");
|
||||
}
|
||||
if (!options.sdk) {
|
||||
throw new Error("Missing option sdk");
|
||||
if (!options.dispatcher) {
|
||||
throw new Error("Missing option dispatcher");
|
||||
}
|
||||
if (!options.sdk) {
|
||||
throw new Error("Missing option sdk");
|
||||
}
|
||||
|
||||
this.dispatcher = options.dispatcher;
|
||||
this.sdk = options.sdk;
|
||||
|
||||
this._isDesktop = !!options.isDesktop;
|
||||
|
||||
if (this._isDesktop) {
|
||||
if (!options.mozLoop) {
|
||||
throw new Error("Missing option mozLoop");
|
||||
}
|
||||
this.mozLoop = options.mozLoop;
|
||||
}
|
||||
|
||||
this.dispatcher = options.dispatcher;
|
||||
this.sdk = options.sdk;
|
||||
this.connections = {};
|
||||
|
||||
this._isDesktop = !!options.isDesktop;
|
||||
// Metrics object to keep track of the number of connections we have
|
||||
// and the amount of streams.
|
||||
this._metrics = {
|
||||
connections: 0,
|
||||
sendStreams: 0,
|
||||
recvStreams: 0
|
||||
};
|
||||
|
||||
if (this._isDesktop) {
|
||||
if (!options.mozLoop) {
|
||||
throw new Error("Missing option mozLoop");
|
||||
}
|
||||
this.mozLoop = options.mozLoop;
|
||||
}
|
||||
this.dispatcher.register(this, [
|
||||
"setupStreamElements",
|
||||
"setMute"
|
||||
]);
|
||||
|
||||
this.connections = {};
|
||||
|
||||
this.dispatcher.register(this, [
|
||||
"setupStreamElements",
|
||||
"setMute"
|
||||
]);
|
||||
|
||||
// Set loop.debug.twoWayMediaTelemetry to true in the browser
|
||||
// by changing the hidden pref loop.debug.twoWayMediaTelemetry using
|
||||
// about:config, or use
|
||||
//
|
||||
// localStorage.setItem("debug.twoWayMediaTelemetry", true);
|
||||
this._debugTwoWayMediaTelemetry =
|
||||
loop.shared.utils.getBoolPreference("debug.twoWayMediaTelemetry");
|
||||
// Set loop.debug.twoWayMediaTelemetry to true in the browser
|
||||
// by changing the hidden pref loop.debug.twoWayMediaTelemetry using
|
||||
// about:config, or use
|
||||
//
|
||||
// localStorage.setItem("debug.twoWayMediaTelemetry", true);
|
||||
this._debugTwoWayMediaTelemetry =
|
||||
loop.shared.utils.getBoolPreference("debug.twoWayMediaTelemetry");
|
||||
|
||||
/**
|
||||
* XXX This is a workaround for desktop machines that do not have a
|
||||
|
@ -104,6 +112,7 @@ loop.OTSdkDriver = (function() {
|
|||
this.publisher = this.sdk.initPublisher(this.getLocalElement(),
|
||||
this._getCopyPublisherConfig());
|
||||
this.publisher.on("streamCreated", this._onLocalStreamCreated.bind(this));
|
||||
this.publisher.on("streamDestroyed", this._onLocalStreamDestroyed.bind(this));
|
||||
this.publisher.on("accessAllowed", this._onPublishComplete.bind(this));
|
||||
this.publisher.on("accessDenied", this._onPublishDenied.bind(this));
|
||||
this.publisher.on("accessDialogOpened",
|
||||
|
@ -164,6 +173,7 @@ loop.OTSdkDriver = (function() {
|
|||
config);
|
||||
this.screenshare.on("accessAllowed", this._onScreenShareGranted.bind(this));
|
||||
this.screenshare.on("accessDenied", this._onScreenShareDenied.bind(this));
|
||||
this.screenshare.on("streamCreated", this._onScreenShareStreamCreated.bind(this));
|
||||
|
||||
this._noteSharingState(options.videoSource, true);
|
||||
},
|
||||
|
@ -193,8 +203,10 @@ loop.OTSdkDriver = (function() {
|
|||
return false;
|
||||
}
|
||||
|
||||
this._notifyMetricsEvent("Publisher.streamDestroyed");
|
||||
|
||||
this.session.unpublish(this.screenshare);
|
||||
this.screenshare.off("accessAllowed accessDenied");
|
||||
this.screenshare.off("accessAllowed accessDenied streamCreated");
|
||||
this.screenshare.destroy();
|
||||
delete this.screenshare;
|
||||
this._noteSharingState(this._windowId ? "browser" : "window", false);
|
||||
|
@ -223,18 +235,18 @@ loop.OTSdkDriver = (function() {
|
|||
this._sendTwoWayMediaTelemetry = !!sessionData.sendTwoWayMediaTelemetry;
|
||||
this._setTwoWayMediaStartTime(this.CONNECTION_START_TIME_UNINITIALIZED);
|
||||
|
||||
this.session.on("connectionCreated", this._onConnectionCreated.bind(this));
|
||||
this.session.on("streamCreated", this._onRemoteStreamCreated.bind(this));
|
||||
this.session.on("streamDestroyed", this._onRemoteStreamDestroyed.bind(this));
|
||||
this.session.on("connectionDestroyed",
|
||||
this._onConnectionDestroyed.bind(this));
|
||||
this.session.on("sessionDisconnected",
|
||||
this._onSessionDisconnected.bind(this));
|
||||
this.session.on("connectionCreated", this._onConnectionCreated.bind(this));
|
||||
this.session.on("connectionDestroyed",
|
||||
this._onConnectionDestroyed.bind(this));
|
||||
this.session.on("streamCreated", this._onRemoteStreamCreated.bind(this));
|
||||
this.session.on("streamDestroyed", this._onRemoteStreamDestroyed.bind(this));
|
||||
this.session.on("streamPropertyChanged", this._onStreamPropertyChanged.bind(this));
|
||||
|
||||
// This starts the actual session connection.
|
||||
this.session.connect(sessionData.apiKey, sessionData.sessionToken,
|
||||
this._onConnectionComplete.bind(this));
|
||||
this._onSessionConnectionCompleted.bind(this));
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -244,10 +256,11 @@ loop.OTSdkDriver = (function() {
|
|||
this.endScreenShare();
|
||||
|
||||
if (this.session) {
|
||||
this.session.off("streamCreated streamDestroyed connectionDestroyed " +
|
||||
"sessionDisconnected streamPropertyChanged");
|
||||
this.session.off("sessionDisconnected streamCreated streamDestroyed connectionCreated connectionDestroyed streamPropertyChanged");
|
||||
this.session.disconnect();
|
||||
delete this.session;
|
||||
|
||||
this._notifyMetricsEvent("Session.connectionDestroyed", "local");
|
||||
}
|
||||
if (this.publisher) {
|
||||
this.publisher.off("accessAllowed accessDenied accessDialogOpened streamCreated");
|
||||
|
@ -302,7 +315,7 @@ loop.OTSdkDriver = (function() {
|
|||
*
|
||||
* @param {Error} error An OT error object, null if there was no error.
|
||||
*/
|
||||
_onConnectionComplete: function(error) {
|
||||
_onSessionConnectionCompleted: function(error) {
|
||||
if (error) {
|
||||
console.error("Failed to complete connection", error);
|
||||
this.dispatcher.dispatch(new sharedActions.ConnectionFailure({
|
||||
|
@ -327,7 +340,11 @@ loop.OTSdkDriver = (function() {
|
|||
if (connection && (connection.id in this.connections)) {
|
||||
delete this.connections[connection.id];
|
||||
}
|
||||
|
||||
this._notifyMetricsEvent("Session.connectionDestroyed", "peer");
|
||||
|
||||
this._noteConnectionLengthIfNeeded(this._getTwoWayMediaStartTime(), performance.now());
|
||||
|
||||
this.dispatcher.dispatch(new sharedActions.RemotePeerDisconnected({
|
||||
peerHungup: event.reason === "clientDisconnected"
|
||||
}));
|
||||
|
@ -370,12 +387,95 @@ loop.OTSdkDriver = (function() {
|
|||
_onConnectionCreated: function(event) {
|
||||
var connection = event.connection;
|
||||
if (this.session.connection.id === connection.id) {
|
||||
// This is the connection event for our connection.
|
||||
this._notifyMetricsEvent("Session.connectionCreated", "local");
|
||||
return;
|
||||
}
|
||||
this.connections[connection.id] = connection;
|
||||
this._notifyMetricsEvent("Session.connectionCreated", "peer");
|
||||
this.dispatcher.dispatch(new sharedActions.RemotePeerConnected());
|
||||
},
|
||||
|
||||
/**
|
||||
* Works out the current connection state based on the streams being
|
||||
* sent or received.
|
||||
*/
|
||||
_getConnectionState: function() {
|
||||
if (this._metrics.sendStreams) {
|
||||
return this._metrics.recvStreams ? "sendrecv" : "sending";
|
||||
}
|
||||
|
||||
if (this._metrics.recvStreams) {
|
||||
return "receiving";
|
||||
}
|
||||
|
||||
return "starting";
|
||||
},
|
||||
|
||||
/**
|
||||
* Notifies of a metrics related event for tracking call setup purposes.
|
||||
* See https://wiki.mozilla.org/Loop/Architecture/Rooms#Updating_Session_State
|
||||
*
|
||||
* @param {String} eventName The name of the event for the update.
|
||||
* @param {String} clientType Used for connection created/destoryed. Indicates
|
||||
* if it is for the "peer" or the "local" client.
|
||||
*/
|
||||
_notifyMetricsEvent: function(eventName, clientType) {
|
||||
if (!eventName) {
|
||||
return;
|
||||
}
|
||||
|
||||
var state;
|
||||
|
||||
// We intentionally don't bounds check these, in case there's an error
|
||||
// somewhere, if there is, we'll see it in the server metrics and are more
|
||||
// likely to investigate.
|
||||
switch (eventName) {
|
||||
case "Session.connectionCreated":
|
||||
this._metrics.connections++;
|
||||
if (clientType === "local") {
|
||||
state = "waiting";
|
||||
}
|
||||
break;
|
||||
case "Session.connectionDestroyed":
|
||||
this._metrics.connections--;
|
||||
if (clientType === "local") {
|
||||
// Don't log this, as the server doesn't accept it after
|
||||
// the room has been left.
|
||||
return;
|
||||
} else if (!this._metrics.connections) {
|
||||
state = "waiting";
|
||||
}
|
||||
break;
|
||||
case "Publisher.streamCreated":
|
||||
this._metrics.sendStreams++;
|
||||
break;
|
||||
case "Publisher.streamDestroyed":
|
||||
this._metrics.sendStreams--;
|
||||
break;
|
||||
case "Session.streamCreated":
|
||||
this._metrics.recvStreams++;
|
||||
break;
|
||||
case "Session.streamDestroyed":
|
||||
this._metrics.recvStreams--;
|
||||
break;
|
||||
default:
|
||||
console.error("Unexpected event name", eventName);
|
||||
return;
|
||||
}
|
||||
if (!state) {
|
||||
state = this._getConnectionState();
|
||||
}
|
||||
|
||||
this.dispatcher.dispatch(new sharedActions.ConnectionStatus({
|
||||
event: eventName,
|
||||
state: state,
|
||||
connections: this._metrics.connections,
|
||||
sendStreams: this._metrics.sendStreams,
|
||||
recvStreams: this._metrics.recvStreams
|
||||
}));
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles when a remote screen share is created, subscribing to
|
||||
* the stream, and notifying the stores that a share is being
|
||||
|
@ -407,6 +507,8 @@ loop.OTSdkDriver = (function() {
|
|||
* https://tokbox.com/opentok/libraries/client/js/reference/StreamEvent.html
|
||||
*/
|
||||
_onRemoteStreamCreated: function(event) {
|
||||
this._notifyMetricsEvent("Session.streamCreated");
|
||||
|
||||
if (event.stream[STREAM_PROPERTIES.HAS_VIDEO]) {
|
||||
this.dispatcher.dispatch(new sharedActions.VideoDimensionsChanged({
|
||||
isLocal: false,
|
||||
|
@ -439,6 +541,8 @@ loop.OTSdkDriver = (function() {
|
|||
* https://tokbox.com/opentok/libraries/client/js/reference/StreamEvent.html
|
||||
*/
|
||||
_onLocalStreamCreated: function(event) {
|
||||
this._notifyMetricsEvent("Publisher.streamCreated");
|
||||
|
||||
if (event.stream[STREAM_PROPERTIES.HAS_VIDEO]) {
|
||||
this.dispatcher.dispatch(new sharedActions.VideoDimensionsChanged({
|
||||
isLocal: true,
|
||||
|
@ -506,6 +610,8 @@ loop.OTSdkDriver = (function() {
|
|||
* https://tokbox.com/opentok/libraries/client/js/reference/StreamEvent.html
|
||||
*/
|
||||
_onRemoteStreamDestroyed: function(event) {
|
||||
this._notifyMetricsEvent("Session.streamDestroyed");
|
||||
|
||||
if (event.stream.videoType !== "screen") {
|
||||
return;
|
||||
}
|
||||
|
@ -517,6 +623,13 @@ loop.OTSdkDriver = (function() {
|
|||
}));
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles the event when the remote stream is destroyed.
|
||||
*/
|
||||
_onLocalStreamDestroyed: function() {
|
||||
this._notifyMetricsEvent("Publisher.streamDestroyed");
|
||||
},
|
||||
|
||||
/**
|
||||
* Called from the sdk when the media access dialog is opened.
|
||||
* Prevents the default action, to prevent the SDK's "allow access"
|
||||
|
@ -631,6 +744,13 @@ loop.OTSdkDriver = (function() {
|
|||
}));
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when a screenshare stream is published.
|
||||
*/
|
||||
_onScreenShareStreamCreated: function() {
|
||||
this._notifyMetricsEvent("Publisher.streamCreated");
|
||||
},
|
||||
|
||||
/*
|
||||
* XXX all of the bi-directional media connection telemetry stuff in this
|
||||
* file, (much, but not all, of it is below) should be hoisted into its
|
||||
|
|
|
@ -222,6 +222,29 @@ loop.StandaloneMozLoop = (function(mozL10n) {
|
|||
}, null, false, callback);
|
||||
},
|
||||
|
||||
/**
|
||||
* Forwards connection status to the server.
|
||||
*
|
||||
* @param {String} roomToken The room token.
|
||||
* @param {String} sessionToken The session token for the session that has been
|
||||
* joined.
|
||||
* @param {sharedActions.SdkStatus} status The connection status.
|
||||
*/
|
||||
sendConnectionStatus: function(roomToken, sessionToken, status) {
|
||||
this._postToRoom(roomToken, sessionToken, {
|
||||
action: "status",
|
||||
event: status.event,
|
||||
state: status.state,
|
||||
connections: status.connections,
|
||||
sendStreams: status.sendStreams,
|
||||
recvStreams: status.recvStreams
|
||||
}, null, true, function(error) {
|
||||
if (error) {
|
||||
console.error(error);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// Dummy functions to reflect those in the desktop mozLoop.rooms that we
|
||||
// don't currently use.
|
||||
on: function() {},
|
||||
|
|
|
@ -280,15 +280,28 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
|||
roomInfoFailure: React.PropTypes.string
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
failureLogged: false
|
||||
}
|
||||
},
|
||||
|
||||
_logFailure: function(message) {
|
||||
if (!this.state.failureLogged) {
|
||||
console.error(mozL10n.get(message));
|
||||
this.state.failureLogged = true;
|
||||
}
|
||||
},
|
||||
|
||||
render: function() {
|
||||
// For failures, we currently just log the messages - UX doesn't want them
|
||||
// displayed on primary UI at the moment.
|
||||
if (this.props.roomInfoFailure === ROOM_INFO_FAILURES.WEB_CRYPTO_UNSUPPORTED) {
|
||||
return (React.createElement("h2", {className: "room-info-failure"},
|
||||
mozL10n.get("room_information_failure_unsupported_browser")
|
||||
));
|
||||
this._logFailure("room_information_failure_unsupported_browser");
|
||||
return null;
|
||||
} else if (this.props.roomInfoFailure) {
|
||||
return (React.createElement("h2", {className: "room-info-failure"},
|
||||
mozL10n.get("room_information_failure_not_available")
|
||||
));
|
||||
this._logFailure("room_information_failure_not_available");
|
||||
return null;
|
||||
}
|
||||
|
||||
// We only support one item in the context Urls array for now.
|
||||
|
|
|
@ -280,15 +280,28 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
|||
roomInfoFailure: React.PropTypes.string
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
failureLogged: false
|
||||
}
|
||||
},
|
||||
|
||||
_logFailure: function(message) {
|
||||
if (!this.state.failureLogged) {
|
||||
console.error(mozL10n.get(message));
|
||||
this.state.failureLogged = true;
|
||||
}
|
||||
},
|
||||
|
||||
render: function() {
|
||||
// For failures, we currently just log the messages - UX doesn't want them
|
||||
// displayed on primary UI at the moment.
|
||||
if (this.props.roomInfoFailure === ROOM_INFO_FAILURES.WEB_CRYPTO_UNSUPPORTED) {
|
||||
return (<h2 className="room-info-failure">
|
||||
{mozL10n.get("room_information_failure_unsupported_browser")}
|
||||
</h2>);
|
||||
this._logFailure("room_information_failure_unsupported_browser");
|
||||
return null;
|
||||
} else if (this.props.roomInfoFailure) {
|
||||
return (<h2 className="room-info-failure">
|
||||
{mozL10n.get("room_information_failure_not_available")}
|
||||
</h2>);
|
||||
this._logFailure("room_information_failure_not_available");
|
||||
return null;
|
||||
}
|
||||
|
||||
// We only support one item in the context Urls array for now.
|
||||
|
|
|
@ -614,6 +614,20 @@ describe("loop.panel", function() {
|
|||
sinon.assert.calledOnce(fakeMozLoop.openURL);
|
||||
sinon.assert.calledWithExactly(fakeMozLoop.openURL, "http://invalid/");
|
||||
});
|
||||
|
||||
it("should call close the panel after opening a url", function() {
|
||||
roomData.decryptedContext.urls = [{
|
||||
description: "invalid entry",
|
||||
location: "http://invalid/",
|
||||
thumbnail: "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="
|
||||
}];
|
||||
|
||||
roomEntry = mountEntryForContext();
|
||||
|
||||
TestUtils.Simulate.click(roomEntry.getDOMNode().querySelector("a"));
|
||||
|
||||
sinon.assert.calledOnce(fakeWindow.close);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Room Entry click", function() {
|
||||
|
@ -833,6 +847,24 @@ describe("loop.panel", function() {
|
|||
var contextInfo = view.getDOMNode().querySelector(".context");
|
||||
expect(contextInfo.classList.contains("hide")).to.equal(true);
|
||||
});
|
||||
|
||||
it("should show only the hostname of the url", function() {
|
||||
fakeMozLoop.getSelectedTabMetadata = function (callback) {
|
||||
callback({
|
||||
url: "https://www.example.com:1234",
|
||||
description: "fake description",
|
||||
previews: [""]
|
||||
});
|
||||
};
|
||||
|
||||
var view = createTestComponent();
|
||||
|
||||
// Simulate being visible
|
||||
view.onDocumentVisible();
|
||||
|
||||
var contextHostname = view.getDOMNode().querySelector(".context-url");
|
||||
expect(contextHostname.textContent).eql("www.example.com");
|
||||
});
|
||||
});
|
||||
|
||||
describe('loop.panel.ToSView', function() {
|
||||
|
|
|
@ -32,7 +32,8 @@ describe("loop.store.ActiveRoomStore", function () {
|
|||
refreshMembership: sinon.stub(),
|
||||
leave: sinon.stub(),
|
||||
on: sinon.stub(),
|
||||
off: sinon.stub()
|
||||
off: sinon.stub(),
|
||||
sendConnectionStatus: sinon.stub()
|
||||
},
|
||||
setScreenShareState: sinon.stub(),
|
||||
getActiveTabWindowId: sandbox.stub().callsArgWith(0, null, 42),
|
||||
|
@ -1121,6 +1122,29 @@ describe("loop.store.ActiveRoomStore", function () {
|
|||
});
|
||||
});
|
||||
|
||||
describe("#connectionStatus", function() {
|
||||
it("should call rooms.sendConnectionStatus on mozLoop", function() {
|
||||
store.setStoreState({
|
||||
roomToken: "fakeToken",
|
||||
sessionToken: "9876543210"
|
||||
});
|
||||
|
||||
var data = new sharedActions.ConnectionStatus({
|
||||
event: "Publisher.streamCreated",
|
||||
state: "sendrecv",
|
||||
connections: 2,
|
||||
recvStreams: 1,
|
||||
sendStreams: 2
|
||||
});
|
||||
|
||||
store.connectionStatus(data);
|
||||
|
||||
sinon.assert.calledOnce(fakeMozLoop.rooms.sendConnectionStatus);
|
||||
sinon.assert.calledWith(fakeMozLoop.rooms.sendConnectionStatus,
|
||||
"fakeToken", "9876543210", data);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#windowUnload", function() {
|
||||
beforeEach(function() {
|
||||
store.setStoreState({
|
||||
|
|
|
@ -35,6 +35,9 @@ describe("loop.OTSdkDriver", function () {
|
|||
};
|
||||
|
||||
dispatcher = new loop.Dispatcher();
|
||||
|
||||
sandbox.stub(dispatcher, "dispatch");
|
||||
|
||||
session = _.extend({
|
||||
connect: sinon.stub(),
|
||||
disconnect: sinon.stub(),
|
||||
|
@ -108,7 +111,7 @@ describe("loop.OTSdkDriver", function () {
|
|||
|
||||
describe("#setupStreamElements", function() {
|
||||
it("should call initPublisher", function() {
|
||||
dispatcher.dispatch(new sharedActions.SetupStreamElements({
|
||||
driver.setupStreamElements(new sharedActions.SetupStreamElements({
|
||||
getLocalElementFunc: function() {return fakeLocalElement;},
|
||||
getRemoteElementFunc: function() {return fakeRemoteElement;},
|
||||
publisherConfig: publisherConfig
|
||||
|
@ -154,7 +157,7 @@ describe("loop.OTSdkDriver", function () {
|
|||
beforeEach(function() {
|
||||
sdk.initPublisher.returns(publisher);
|
||||
|
||||
dispatcher.dispatch(new sharedActions.SetupStreamElements({
|
||||
driver.setupStreamElements(new sharedActions.SetupStreamElements({
|
||||
getLocalElementFunc: function() {return fakeLocalElement;},
|
||||
getRemoteElementFunc: function() {return fakeRemoteElement;},
|
||||
publisherConfig: publisherConfig
|
||||
|
@ -162,7 +165,7 @@ describe("loop.OTSdkDriver", function () {
|
|||
});
|
||||
|
||||
it("should publishAudio with the correct enabled value", function() {
|
||||
dispatcher.dispatch(new sharedActions.SetMute({
|
||||
driver.setMute(new sharedActions.SetMute({
|
||||
type: "audio",
|
||||
enabled: false
|
||||
}));
|
||||
|
@ -172,7 +175,7 @@ describe("loop.OTSdkDriver", function () {
|
|||
});
|
||||
|
||||
it("should publishVideo with the correct enabled value", function() {
|
||||
dispatcher.dispatch(new sharedActions.SetMute({
|
||||
driver.setMute(new sharedActions.SetMute({
|
||||
type: "video",
|
||||
enabled: true
|
||||
}));
|
||||
|
@ -186,7 +189,6 @@ describe("loop.OTSdkDriver", function () {
|
|||
var fakeElement;
|
||||
|
||||
beforeEach(function() {
|
||||
sandbox.stub(dispatcher, "dispatch");
|
||||
sandbox.stub(driver, "_noteSharingState");
|
||||
|
||||
fakeElement = {
|
||||
|
@ -240,7 +242,6 @@ describe("loop.OTSdkDriver", function () {
|
|||
driver.getScreenShareElementFunc = function() {
|
||||
return fakeScreenElement;
|
||||
};
|
||||
sandbox.stub(dispatcher, "dispatch");
|
||||
|
||||
driver.startScreenShare(options);
|
||||
});
|
||||
|
@ -263,7 +264,6 @@ describe("loop.OTSdkDriver", function () {
|
|||
beforeEach(function() {
|
||||
driver.getScreenShareElementFunc = function() {};
|
||||
|
||||
sandbox.stub(dispatcher, "dispatch");
|
||||
sandbox.stub(driver, "_noteSharingState");
|
||||
});
|
||||
|
||||
|
@ -327,6 +327,32 @@ describe("loop.OTSdkDriver", function () {
|
|||
|
||||
sinon.assert.calledWithExactly(driver._noteSharingState, "browser", false);
|
||||
});
|
||||
|
||||
it("should dispatch a ConnectionStatus action", function() {
|
||||
driver.startScreenShare({
|
||||
videoSource: "browser",
|
||||
constraints: {
|
||||
browserWindow: 42
|
||||
}
|
||||
});
|
||||
driver.session = session;
|
||||
|
||||
driver._metrics.connections = 2;
|
||||
driver._metrics.recvStreams = 1;
|
||||
driver._metrics.sendStreams = 2;
|
||||
|
||||
driver.endScreenShare(new sharedActions.EndScreenShare());
|
||||
|
||||
sinon.assert.calledOnce(dispatcher.dispatch);
|
||||
sinon.assert.calledWithExactly(dispatcher.dispatch,
|
||||
new sharedActions.ConnectionStatus({
|
||||
event: "Publisher.streamDestroyed",
|
||||
state: "sendrecv",
|
||||
connections: 2,
|
||||
sendStreams: 1,
|
||||
recvStreams: 1
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
describe("#connectSession", function() {
|
||||
|
@ -365,7 +391,6 @@ describe("loop.OTSdkDriver", function () {
|
|||
|
||||
it("should dispatch connectionFailure if connecting failed", function() {
|
||||
session.connect.callsArgWith(2, new Error("Failure"));
|
||||
sandbox.stub(dispatcher, "dispatch");
|
||||
|
||||
driver.connectSession(sessionData);
|
||||
|
||||
|
@ -578,14 +603,12 @@ describe("loop.OTSdkDriver", function () {
|
|||
beforeEach(function() {
|
||||
driver.connectSession(sessionData);
|
||||
|
||||
dispatcher.dispatch(new sharedActions.SetupStreamElements({
|
||||
driver.setupStreamElements(new sharedActions.SetupStreamElements({
|
||||
getLocalElementFunc: function() {return fakeLocalElement;},
|
||||
getScreenShareElementFunc: function() {return fakeScreenElement;},
|
||||
getRemoteElementFunc: function() {return fakeRemoteElement;},
|
||||
publisherConfig: publisherConfig
|
||||
}));
|
||||
|
||||
sandbox.stub(dispatcher, "dispatch");
|
||||
});
|
||||
|
||||
describe("connectionDestroyed", function() {
|
||||
|
@ -595,7 +618,7 @@ describe("loop.OTSdkDriver", function () {
|
|||
reason: "clientDisconnected"
|
||||
});
|
||||
|
||||
sinon.assert.calledOnce(dispatcher.dispatch);
|
||||
sinon.assert.called(dispatcher.dispatch);
|
||||
sinon.assert.calledWithMatch(dispatcher.dispatch,
|
||||
sinon.match.hasOwn("name", "remotePeerDisconnected"));
|
||||
sinon.assert.calledWithMatch(dispatcher.dispatch,
|
||||
|
@ -608,13 +631,30 @@ describe("loop.OTSdkDriver", function () {
|
|||
reason: "networkDisconnected"
|
||||
});
|
||||
|
||||
sinon.assert.calledOnce(dispatcher.dispatch);
|
||||
sinon.assert.called(dispatcher.dispatch);
|
||||
sinon.assert.calledWithMatch(dispatcher.dispatch,
|
||||
sinon.match.hasOwn("name", "remotePeerDisconnected"));
|
||||
sinon.assert.calledWithMatch(dispatcher.dispatch,
|
||||
sinon.match.hasOwn("peerHungup", false));
|
||||
});
|
||||
|
||||
it("should dispatch a ConnectionStatus action", function() {
|
||||
driver._metrics.connections = 1;
|
||||
|
||||
session.trigger("connectionDestroyed", {
|
||||
reason: "clientDisconnected"
|
||||
});
|
||||
|
||||
sinon.assert.called(dispatcher.dispatch);
|
||||
sinon.assert.calledWithExactly(dispatcher.dispatch,
|
||||
new sharedActions.ConnectionStatus({
|
||||
event: "Session.connectionDestroyed",
|
||||
state: "waiting",
|
||||
connections: 0,
|
||||
sendStreams: 0,
|
||||
recvStreams: 0
|
||||
}));
|
||||
});
|
||||
|
||||
it("should call _noteConnectionLengthIfNeeded with connection duration", function() {
|
||||
driver.session = session;
|
||||
|
@ -681,16 +721,20 @@ describe("loop.OTSdkDriver", function () {
|
|||
});
|
||||
|
||||
describe("streamCreated (publisher/local)", function() {
|
||||
it("should dispatch a VideoDimensionsChanged action", function() {
|
||||
var fakeStream = {
|
||||
var fakeStream;
|
||||
|
||||
beforeEach(function() {
|
||||
fakeStream = {
|
||||
hasVideo: true,
|
||||
videoType: "camera",
|
||||
videoDimensions: {width: 1, height: 2}
|
||||
};
|
||||
});
|
||||
|
||||
it("should dispatch a VideoDimensionsChanged action", function() {
|
||||
publisher.trigger("streamCreated", {stream: fakeStream});
|
||||
|
||||
sinon.assert.calledOnce(dispatcher.dispatch);
|
||||
sinon.assert.called(dispatcher.dispatch);
|
||||
sinon.assert.calledWithExactly(dispatcher.dispatch,
|
||||
new sharedActions.VideoDimensionsChanged({
|
||||
isLocal: true,
|
||||
|
@ -698,6 +742,23 @@ describe("loop.OTSdkDriver", function () {
|
|||
dimensions: {width: 1, height: 2}
|
||||
}));
|
||||
});
|
||||
|
||||
it("should dispatch a ConnectionStatus action", function() {
|
||||
driver._metrics.recvStreams = 1;
|
||||
driver._metrics.connections = 2;
|
||||
|
||||
publisher.trigger("streamCreated", {stream: fakeStream});
|
||||
|
||||
sinon.assert.called(dispatcher.dispatch);
|
||||
sinon.assert.calledWithExactly(dispatcher.dispatch,
|
||||
new sharedActions.ConnectionStatus({
|
||||
event: "Publisher.streamCreated",
|
||||
state: "sendrecv",
|
||||
connections: 2,
|
||||
recvStreams: 1,
|
||||
sendStreams: 1
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
describe("streamCreated (session/remote)", function() {
|
||||
|
@ -714,7 +775,7 @@ describe("loop.OTSdkDriver", function () {
|
|||
it("should dispatch a VideoDimensionsChanged action", function() {
|
||||
session.trigger("streamCreated", {stream: fakeStream});
|
||||
|
||||
sinon.assert.calledOnce(dispatcher.dispatch);
|
||||
sinon.assert.called(dispatcher.dispatch);
|
||||
sinon.assert.calledWithExactly(dispatcher.dispatch,
|
||||
new sharedActions.VideoDimensionsChanged({
|
||||
isLocal: false,
|
||||
|
@ -723,6 +784,22 @@ describe("loop.OTSdkDriver", function () {
|
|||
}));
|
||||
});
|
||||
|
||||
it("should dispatch a ConnectionStatus action", function() {
|
||||
driver._metrics.connections = 1;
|
||||
|
||||
session.trigger("streamCreated", {stream: fakeStream});
|
||||
|
||||
sinon.assert.called(dispatcher.dispatch);
|
||||
sinon.assert.calledWithExactly(dispatcher.dispatch,
|
||||
new sharedActions.ConnectionStatus({
|
||||
event: "Session.streamCreated",
|
||||
state: "receiving",
|
||||
connections: 1,
|
||||
recvStreams: 1,
|
||||
sendStreams: 0
|
||||
}));
|
||||
});
|
||||
|
||||
it("should subscribe to a camera stream", function() {
|
||||
session.trigger("streamCreated", {stream: fakeStream});
|
||||
|
||||
|
@ -747,9 +824,9 @@ describe("loop.OTSdkDriver", function () {
|
|||
session.trigger("streamCreated", {stream: fakeStream});
|
||||
|
||||
// Called twice due to the VideoDimensionsChanged above.
|
||||
sinon.assert.calledTwice(dispatcher.dispatch);
|
||||
sinon.assert.called(dispatcher.dispatch);
|
||||
sinon.assert.calledWithMatch(dispatcher.dispatch,
|
||||
sinon.match.hasOwn("name", "mediaConnected"));
|
||||
new sharedActions.MediaConnected({}));
|
||||
});
|
||||
|
||||
it("should store the start time when both streams are up and" +
|
||||
|
@ -803,13 +880,33 @@ describe("loop.OTSdkDriver", function () {
|
|||
session.trigger("streamCreated", {stream: fakeStream});
|
||||
|
||||
// Called twice due to the VideoDimensionsChanged above.
|
||||
sinon.assert.calledTwice(dispatcher.dispatch);
|
||||
sinon.assert.calledWithMatch(dispatcher.dispatch,
|
||||
sinon.assert.called(dispatcher.dispatch);
|
||||
sinon.assert.calledWithExactly(dispatcher.dispatch,
|
||||
new sharedActions.ReceivingScreenShare({receiving: true}));
|
||||
});
|
||||
});
|
||||
|
||||
describe("streamDestroyed", function() {
|
||||
describe("streamDestroyed (publisher/local)", function() {
|
||||
it("should dispatch a ConnectionStatus action", function() {
|
||||
driver._metrics.sendStreams = 1;
|
||||
driver._metrics.recvStreams = 1;
|
||||
driver._metrics.connections = 2;
|
||||
|
||||
publisher.trigger("streamDestroyed");
|
||||
|
||||
sinon.assert.calledOnce(dispatcher.dispatch);
|
||||
sinon.assert.calledWithExactly(dispatcher.dispatch,
|
||||
new sharedActions.ConnectionStatus({
|
||||
event: "Publisher.streamDestroyed",
|
||||
state: "receiving",
|
||||
connections: 2,
|
||||
recvStreams: 1,
|
||||
sendStreams: 0
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
describe("streamDestroyed (session/remote)", function() {
|
||||
var fakeStream;
|
||||
|
||||
beforeEach(function() {
|
||||
|
@ -821,19 +918,38 @@ describe("loop.OTSdkDriver", function () {
|
|||
it("should dispatch a ReceivingScreenShare action", function() {
|
||||
session.trigger("streamDestroyed", {stream: fakeStream});
|
||||
|
||||
sinon.assert.calledOnce(dispatcher.dispatch);
|
||||
sinon.assert.called(dispatcher.dispatch);
|
||||
sinon.assert.calledWithExactly(dispatcher.dispatch,
|
||||
new sharedActions.ReceivingScreenShare({
|
||||
receiving: false
|
||||
}));
|
||||
});
|
||||
|
||||
it("should dispatch a ConnectionStatus action", function() {
|
||||
driver._metrics.connections = 2;
|
||||
driver._metrics.sendStreams = 1;
|
||||
driver._metrics.recvStreams = 1;
|
||||
|
||||
session.trigger("streamDestroyed", {stream: fakeStream});
|
||||
|
||||
sinon.assert.called(dispatcher.dispatch);
|
||||
sinon.assert.calledWithExactly(dispatcher.dispatch,
|
||||
new sharedActions.ConnectionStatus({
|
||||
event: "Session.streamDestroyed",
|
||||
state: "sending",
|
||||
connections: 2,
|
||||
recvStreams: 0,
|
||||
sendStreams: 1
|
||||
}));
|
||||
});
|
||||
|
||||
it("should not dispatch an action if the videoType is camera", function() {
|
||||
fakeStream.videoType = "camera";
|
||||
|
||||
session.trigger("streamDestroyed", {stream: fakeStream});
|
||||
|
||||
sinon.assert.notCalled(dispatcher.dispatch);
|
||||
sinon.assert.neverCalledWithMatch(dispatcher.dispatch,
|
||||
sinon.match.hasOwn("name", "receivingScreenShare"));
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -888,25 +1004,75 @@ describe("loop.OTSdkDriver", function () {
|
|||
connection: {id: "remoteUser"}
|
||||
});
|
||||
|
||||
sinon.assert.calledOnce(dispatcher.dispatch);
|
||||
sinon.assert.called(dispatcher.dispatch);
|
||||
sinon.assert.calledWithExactly(dispatcher.dispatch,
|
||||
new sharedActions.RemotePeerConnected());
|
||||
it("should store the connection details for a remote user", function() {
|
||||
expect(driver.connections).to.include.keys("remoteUser");
|
||||
});
|
||||
});
|
||||
|
||||
it("should not dispatch an action if this is for a local user",
|
||||
it("should store the connection details for a remote user", function() {
|
||||
session.trigger("connectionCreated", {
|
||||
connection: {id: "remoteUser"}
|
||||
});
|
||||
|
||||
expect(driver.connections).to.include.keys("remoteUser");
|
||||
});
|
||||
|
||||
it("should dispatch a ConnectionStatus action for a remote user", function() {
|
||||
driver._metrics.connections = 1;
|
||||
driver._metrics.sendStreams = 1;
|
||||
|
||||
session.trigger("connectionCreated", {
|
||||
connection: {id: "remoteUser"}
|
||||
});
|
||||
|
||||
sinon.assert.called(dispatcher.dispatch);
|
||||
sinon.assert.calledWithExactly(dispatcher.dispatch,
|
||||
new sharedActions.ConnectionStatus({
|
||||
event: "Session.connectionCreated",
|
||||
state: "sending",
|
||||
connections: 2,
|
||||
recvStreams: 0,
|
||||
sendStreams: 1
|
||||
}));
|
||||
});
|
||||
|
||||
it("should not dispatch an RemotePeerConnected action if this is for a local user",
|
||||
function() {
|
||||
session.trigger("connectionCreated", {
|
||||
connection: {id: "localUser"}
|
||||
});
|
||||
|
||||
sinon.assert.notCalled(dispatcher.dispatch);
|
||||
it("should not store the connection details for a local user", function() {
|
||||
expect(driver.connections).to.not.include.keys("localUser");
|
||||
});
|
||||
sinon.assert.neverCalledWithMatch(dispatcher.dispatch,
|
||||
sinon.match.hasOwn("name", "remotePeerConnected"));
|
||||
});
|
||||
|
||||
it("should not store the connection details for a local user", function() {
|
||||
session.trigger("connectionCreated", {
|
||||
connection: {id: "localUser"}
|
||||
});
|
||||
|
||||
expect(driver.connections).to.not.include.keys("localUser");
|
||||
});
|
||||
|
||||
it("should dispatch a ConnectionStatus action for a remote user", function() {
|
||||
driver._metrics.connections = 0;
|
||||
driver._metrics.sendStreams = 0;
|
||||
|
||||
session.trigger("connectionCreated", {
|
||||
connection: {id: "localUser"}
|
||||
});
|
||||
|
||||
sinon.assert.called(dispatcher.dispatch);
|
||||
sinon.assert.calledWithExactly(dispatcher.dispatch,
|
||||
new sharedActions.ConnectionStatus({
|
||||
event: "Session.connectionCreated",
|
||||
state: "waiting",
|
||||
connections: 1,
|
||||
recvStreams: 0,
|
||||
sendStreams: 0
|
||||
}));
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe("accessAllowed", function() {
|
||||
|
@ -989,8 +1155,6 @@ describe("loop.OTSdkDriver", function () {
|
|||
driver.startScreenShare({
|
||||
videoSource: "window"
|
||||
});
|
||||
|
||||
sandbox.stub(dispatcher, "dispatch");
|
||||
});
|
||||
|
||||
describe("accessAllowed", function() {
|
||||
|
@ -1022,5 +1186,25 @@ describe("loop.OTSdkDriver", function () {
|
|||
}));
|
||||
});
|
||||
});
|
||||
|
||||
describe("streamCreated", function() {
|
||||
it("should dispatch a ConnectionStatus action", function() {
|
||||
driver._metrics.connections = 2;
|
||||
driver._metrics.recvStreams = 1;
|
||||
driver._metrics.sendStreams = 1;
|
||||
|
||||
publisher.trigger("streamCreated", fakeEvent);
|
||||
|
||||
sinon.assert.calledOnce(dispatcher.dispatch);
|
||||
sinon.assert.calledWithExactly(dispatcher.dispatch,
|
||||
new sharedActions.ConnectionStatus({
|
||||
event: "Publisher.streamCreated",
|
||||
state: "sendrecv",
|
||||
connections: 2,
|
||||
recvStreams: 1,
|
||||
sendStreams: 2
|
||||
}));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -63,13 +63,14 @@ describe("loop.standaloneRoomViews", function() {
|
|||
expect(view.getDOMNode().textContent).eql("Mike's room");
|
||||
});
|
||||
|
||||
it("should display an unsupported browser message if crypto is unsupported", function() {
|
||||
it("should log an unsupported browser message if crypto is unsupported", function() {
|
||||
var view = mountTestComponent({
|
||||
roomName: "Mark's room",
|
||||
roomInfoFailure: ROOM_INFO_FAILURES.WEB_CRYPTO_UNSUPPORTED
|
||||
});
|
||||
|
||||
expect(view.getDOMNode().textContent).match(/unsupported/);
|
||||
sinon.assert.called(console.error);
|
||||
sinon.assert.calledWithMatch(console.error, sinon.match("unsupported"));
|
||||
});
|
||||
|
||||
it("should display a general error message for any other failure", function() {
|
||||
|
@ -78,7 +79,8 @@ describe("loop.standaloneRoomViews", function() {
|
|||
roomInfoFailure: ROOM_INFO_FAILURES.NO_DATA
|
||||
});
|
||||
|
||||
expect(view.getDOMNode().textContent).match(/not_available/);
|
||||
sinon.assert.called(console.error);
|
||||
sinon.assert.calledWithMatch(console.error, sinon.match("not_available"));
|
||||
});
|
||||
|
||||
it("should display context information if a url is supplied", function() {
|
||||
|
|
|
@ -577,6 +577,26 @@ add_task(function* test_leaveRoom() {
|
|||
Assert.equal(leaveData.sessionToken, "fakeLeaveSessionToken");
|
||||
});
|
||||
|
||||
// Test if sending connection status works as expected.
|
||||
add_task(function* test_sendConnectionStatus() {
|
||||
let roomToken = "_nxD4V4FflQ";
|
||||
let extraData = {
|
||||
event: "Session.connectionDestroyed",
|
||||
state: "test",
|
||||
connections: 1,
|
||||
sendStreams: 2,
|
||||
recvStreams: 3
|
||||
};
|
||||
let statusData = yield LoopRooms.promise("sendConnectionStatus", roomToken,
|
||||
"fakeStatusSessionToken", extraData
|
||||
);
|
||||
Assert.equal(statusData.sessionToken, "fakeStatusSessionToken");
|
||||
|
||||
extraData.action = "status";
|
||||
extraData.sessionToken = "fakeStatusSessionToken";
|
||||
Assert.deepEqual(statusData, extraData);
|
||||
});
|
||||
|
||||
// Test if renaming a room works as expected.
|
||||
add_task(function* test_renameRoom() {
|
||||
Services.prefs.setBoolPref(kContextEnabledPref, true);
|
||||
|
|
|
@ -234,6 +234,7 @@ var BookmarkPropertiesPanel = {
|
|||
}
|
||||
else { // edit
|
||||
this._node = dialogInfo.node;
|
||||
this._title = this._node.title;
|
||||
switch (dialogInfo.type) {
|
||||
case "bookmark":
|
||||
this._itemType = BOOKMARK_ITEM;
|
||||
|
|
|
@ -529,7 +529,7 @@ let gEditItemOverlay = {
|
|||
|
||||
let itemId = this._paneInfo.itemId;
|
||||
let description = this._element("descriptionField").value;
|
||||
if (description != PlacesUIUtils.getItemDescription(this._itemId)) {
|
||||
if (description != PlacesUIUtils.getItemDescription(this._paneInfo.itemId)) {
|
||||
let annotation =
|
||||
{ name: PlacesUIUtils.DESCRIPTION_ANNO, value: description };
|
||||
if (!PlacesUIUtils.useAsyncTransactions) {
|
||||
|
@ -562,7 +562,7 @@ let gEditItemOverlay = {
|
|||
|
||||
if (!PlacesUIUtils.useAsyncTransactions) {
|
||||
let itemId = this._paneInfo.itemId;
|
||||
let txn = new PlacesEditBookmarkURITransaction(this._itemId, newURI);
|
||||
let txn = new PlacesEditBookmarkURITransaction(this._paneInfo.itemId, newURI);
|
||||
PlacesUtils.transactionManager.doTransaction(txn);
|
||||
return;
|
||||
}
|
||||
|
@ -677,7 +677,7 @@ let gEditItemOverlay = {
|
|||
if (aEvent.target.id == "editBMPanel_chooseFolderMenuItem") {
|
||||
// reset the selection back to where it was and expand the tree
|
||||
// (this menu-item is hidden when the tree is already visible
|
||||
let containerId = PlacesUtils.bookmarks.getFolderIdForItem(this._itemId);
|
||||
let containerId = PlacesUtils.bookmarks.getFolderIdForItem(this._paneInfo.itemId);
|
||||
let item = this._getFolderMenuItem(containerId);
|
||||
this._folderMenuList.selectedItem = item;
|
||||
// XXXmano HACK: setTimeout 100, otherwise focus goes back to the
|
||||
|
@ -697,7 +697,7 @@ let gEditItemOverlay = {
|
|||
}.bind(this));
|
||||
}
|
||||
else {
|
||||
let txn = new PlacesMoveItemTransaction(this._itemId,
|
||||
let txn = new PlacesMoveItemTransaction(this._paneInfo.itemId,
|
||||
containerId,
|
||||
PlacesUtils.bookmarks.DEFAULT_INDEX);
|
||||
PlacesUtils.transactionManager.doTransaction(txn);
|
||||
|
|
|
@ -598,6 +598,30 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
|
|||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Copy the raw request headers from the currently selected item.
|
||||
*/
|
||||
copyRequestHeaders: function() {
|
||||
let selected = this.selectedItem.attachment;
|
||||
let rawHeaders = selected.requestHeaders.rawHeaders.trim();
|
||||
if (Services.appinfo.OS !== "WINNT") {
|
||||
rawHeaders = rawHeaders.replace(/\r/g, "");
|
||||
}
|
||||
clipboardHelper.copyString(rawHeaders, document);
|
||||
},
|
||||
|
||||
/**
|
||||
* Copy the raw response headers from the currently selected item.
|
||||
*/
|
||||
copyResponseHeaders: function() {
|
||||
let selected = this.selectedItem.attachment;
|
||||
let rawHeaders = selected.responseHeaders.rawHeaders.trim();
|
||||
if (Services.appinfo.OS !== "WINNT") {
|
||||
rawHeaders = rawHeaders.replace(/\r/g, "");
|
||||
}
|
||||
clipboardHelper.copyString(rawHeaders, document);
|
||||
},
|
||||
|
||||
/**
|
||||
* Copy image as data uri.
|
||||
*/
|
||||
|
@ -1809,6 +1833,12 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
|
|||
let copyAsCurlElement = $("#request-menu-context-copy-as-curl");
|
||||
copyAsCurlElement.hidden = !selectedItem || !selectedItem.attachment.responseContent;
|
||||
|
||||
let copyRequestHeadersElement = $("#request-menu-context-copy-request-headers");
|
||||
copyRequestHeadersElement.hidden = !selectedItem || !selectedItem.attachment.requestHeaders;
|
||||
|
||||
let copyResponseHeadersElement = $("#response-menu-context-copy-response-headers");
|
||||
copyResponseHeadersElement.hidden = !selectedItem || !selectedItem.attachment.responseHeaders;
|
||||
|
||||
let copyResponse = $("#request-menu-context-copy-response");
|
||||
copyResponse.hidden = !selectedItem ||
|
||||
!selectedItem.attachment.responseContent ||
|
||||
|
|
|
@ -34,6 +34,13 @@
|
|||
<menuitem id="request-menu-context-copy-as-curl"
|
||||
label="&netmonitorUI.context.copyAsCurl;"
|
||||
oncommand="NetMonitorView.RequestsMenu.copyAsCurl();"/>
|
||||
<menuitem id="request-menu-context-copy-request-headers"
|
||||
label="&netmonitorUI.context.copyRequestHeaders;"
|
||||
accesskey="&netmonitorUI.context.copyRequestHeaders.accesskey;"
|
||||
oncommand="NetMonitorView.RequestsMenu.copyRequestHeaders();"/>
|
||||
<menuitem id="response-menu-context-copy-response-headers"
|
||||
label="&netmonitorUI.context.copyResponseHeaders;"
|
||||
oncommand="NetMonitorView.RequestsMenu.copyResponseHeaders();"/>
|
||||
<menuitem id="request-menu-context-copy-response"
|
||||
label="&netmonitorUI.context.copyResponse;"
|
||||
accesskey="&netmonitorUI.context.copyResponse.accesskey;"/>
|
||||
|
|
|
@ -54,6 +54,7 @@ skip-if= buildapp == 'mulet'
|
|||
[browser_net_copy_image_as_data_uri.js]
|
||||
[browser_net_copy_url.js]
|
||||
[browser_net_copy_response.js]
|
||||
[browser_net_copy_headers.js]
|
||||
[browser_net_copy_as_curl.js]
|
||||
skip-if = e10s # Bug 1091596
|
||||
[browser_net_cyrillic-01.js]
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Tests if copying a request's request/response headers works.
|
||||
*/
|
||||
|
||||
add_task(function*() {
|
||||
|
||||
let [ aTab, aDebuggee, aMonitor ] = yield initNetMonitor(SIMPLE_URL);
|
||||
info("Starting test... ");
|
||||
|
||||
let { NetMonitorView } = aMonitor.panelWin;
|
||||
let { RequestsMenu } = NetMonitorView;
|
||||
|
||||
RequestsMenu.lazyUpdate = false;
|
||||
|
||||
aDebuggee.location.reload();
|
||||
yield waitForNetworkEvents(aMonitor, 1);
|
||||
|
||||
let requestItem = RequestsMenu.getItemAtIndex(0);
|
||||
RequestsMenu.selectedItem = requestItem;
|
||||
|
||||
let clipboard = null;
|
||||
|
||||
const EXPECTED_REQUEST_HEADERS = [
|
||||
requestItem.attachment.method + " " + SIMPLE_URL + " " + requestItem.attachment.httpVersion,
|
||||
"Host: example.com",
|
||||
"User-Agent: " + navigator.userAgent + "",
|
||||
"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
|
||||
"Accept-Language: " + navigator.languages.join(",") + ";q=0.5",
|
||||
"Accept-Encoding: gzip, deflate",
|
||||
"Connection: keep-alive",
|
||||
"Pragma: no-cache",
|
||||
"Cache-Control: no-cache"
|
||||
].join("\n");
|
||||
|
||||
RequestsMenu.copyRequestHeaders();
|
||||
clipboard = SpecialPowers.getClipboardData("text/unicode");
|
||||
// Sometimes, a "Cookie" header is left over from other tests. Remove it:
|
||||
clipboard = clipboard.replace(/Cookie: [^\n]+\n/, "");
|
||||
is(clipboard, EXPECTED_REQUEST_HEADERS, "Clipboard contains the currently selected item's request headers.");
|
||||
|
||||
const EXPECTED_RESPONSE_HEADERS = [
|
||||
requestItem.attachment.httpVersion + " " + requestItem.attachment.status + " " + requestItem.attachment.statusText,
|
||||
"Last-Modified: Sun, 3 May 2015 11:11:11 GMT",
|
||||
"Content-Type: text/html",
|
||||
"Content-Length: 465",
|
||||
"Connection: close",
|
||||
"Server: httpd.js",
|
||||
"Date: Sun, 3 May 2015 11:11:11 GMT"
|
||||
].join("\n");
|
||||
|
||||
RequestsMenu.copyResponseHeaders();
|
||||
clipboard = SpecialPowers.getClipboardData("text/unicode");
|
||||
// Fake the "Last-Modified" and "Date" headers because they will vary:
|
||||
clipboard = clipboard
|
||||
.replace(/Last-Modified: [^\n]+ GMT/, "Last-Modified: Sun, 3 May 2015 11:11:11 GMT")
|
||||
.replace(/Date: [^\n]+ GMT/, "Date: Sun, 3 May 2015 11:11:11 GMT");
|
||||
is(clipboard, EXPECTED_RESPONSE_HEADERS, "Clipboard contains the currently selected item's response headers.");
|
||||
|
||||
teardown(aMonitor).then(finish);
|
||||
});
|
|
@ -33,7 +33,7 @@
|
|||
background-position: -48px center;
|
||||
}
|
||||
|
||||
@media (min-resolution: 2dppx) {
|
||||
@media (min-resolution: 1.25dppx) {
|
||||
#eyedropper-button {
|
||||
background-image: url("chrome://browser/skin/devtools/command-eyedropper@2x.png");
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@
|
|||
position: relative;
|
||||
}
|
||||
|
||||
@media (min-resolution: 2dppx) {
|
||||
@media (min-resolution: 1.25dppx) {
|
||||
.breakpoint {
|
||||
background-image: url("chrome://browser/skin/devtools/editor-breakpoint@2x.png");
|
||||
}
|
||||
|
@ -56,7 +56,7 @@
|
|||
background-image: url("chrome://browser/skin/devtools/editor-debug-location.png");
|
||||
}
|
||||
|
||||
@media (min-resolution: 2dppx) {
|
||||
@media (min-resolution: 1.25dppx) {
|
||||
.debugLocation {
|
||||
background-image: url("chrome://browser/skin/devtools/editor-debug-location@2x.png");
|
||||
}
|
||||
|
@ -68,7 +68,7 @@
|
|||
url("chrome://browser/skin/devtools/editor-breakpoint.png");
|
||||
}
|
||||
|
||||
@media (min-resolution: 2dppx) {
|
||||
@media (min-resolution: 1.25dppx) {
|
||||
.breakpoint.debugLocation {
|
||||
background-image:
|
||||
url("chrome://browser/skin/devtools/editor-debug-location@2x.png"),
|
||||
|
|
|
@ -260,16 +260,16 @@
|
|||
- on the context menu that copies the selected request's url -->
|
||||
<!ENTITY netmonitorUI.context.copyUrl "Copy URL">
|
||||
|
||||
<!-- LOCALIZATION NOTE (netmonitorUI.context.copyUrl.accesskey): This is the access key
|
||||
- for the Copy URL menu item displayed in the context menu for a request -->
|
||||
<!ENTITY netmonitorUI.context.copyUrl.accesskey "C">
|
||||
|
||||
<!-- LOCALIZATION NOTE (netmonitorUI.context.copyAsCurl): This is the label displayed
|
||||
- on the context menu that copies the selected request as a cURL command.
|
||||
- The capitalization is part of the official name and should be used throughout all languages.
|
||||
- http://en.wikipedia.org/wiki/CURL -->
|
||||
<!ENTITY netmonitorUI.context.copyAsCurl "Copy as cURL">
|
||||
|
||||
<!-- LOCALIZATION NOTE (netmonitorUI.context.copyUrl.accesskey): This is the access key
|
||||
- for the Copy URL menu item displayed in the context menu for a request -->
|
||||
<!ENTITY netmonitorUI.context.copyUrl.accesskey "C">
|
||||
|
||||
<!-- LOCALIZATION NOTE (netmonitorUI.context.copyImageAsDataUri): This is the label displayed
|
||||
- on the context menu that copies the selected image as data uri -->
|
||||
<!ENTITY netmonitorUI.context.copyImageAsDataUri "Copy Image as Data URI">
|
||||
|
@ -282,10 +282,22 @@
|
|||
- on the context menu that copies the selected response as a string -->
|
||||
<!ENTITY netmonitorUI.context.copyResponse "Copy Response">
|
||||
|
||||
<!-- LOCALIZATION NOTE (netmonitorUI.context.copyImageAsDataUri.accesskey): This is the access key
|
||||
<!-- LOCALIZATION NOTE (netmonitorUI.context.copyRespose.accesskey): This is the access key
|
||||
- for the Copy Response menu item displayed in the context menu for a request -->
|
||||
<!ENTITY netmonitorUI.context.copyResponse.accesskey "R">
|
||||
|
||||
<!-- LOCALIZATION NOTE (netmonitorUI.context.copyRequestHeaders): This is the label displayed
|
||||
- on the context menu that copies the selected item's request headers -->
|
||||
<!ENTITY netmonitorUI.context.copyRequestHeaders "Copy Request Headers">
|
||||
|
||||
<!-- LOCALIZATION NOTE (netmonitorUI.context.copyRequestHeaders.accesskey): This is the access key
|
||||
- for the Copy Request Headers menu item displayed in the context menu for a request -->
|
||||
<!ENTITY netmonitorUI.context.copyRequestHeaders.accesskey "H">
|
||||
|
||||
<!-- LOCALIZATION NOTE (netmonitorUI.context.copyResponseHeaders): This is the label displayed
|
||||
- on the context menu that copies the selected item's response headers -->
|
||||
<!ENTITY netmonitorUI.context.copyResponseHeaders "Copy Response Headers">
|
||||
|
||||
<!-- LOCALIZATION NOTE (netmonitorUI.summary.editAndResend): This is the label displayed
|
||||
- on the button in the headers tab that opens a form to edit and resend the currently
|
||||
displayed request -->
|
||||
|
|
|
@ -84,7 +84,7 @@ body {
|
|||
background-image: url("debugger-play.png");
|
||||
}
|
||||
|
||||
@media (min-resolution: 2dppx) {
|
||||
@media (min-resolution: 1.25dppx) {
|
||||
#element-picker::before {
|
||||
background-image: url("chrome://browser/skin/devtools/command-pick@2x.png");
|
||||
background-size: 64px;
|
||||
|
@ -156,7 +156,7 @@ body {
|
|||
background-image: url(rewind.png);
|
||||
}
|
||||
|
||||
@media (min-resolution: 2dppx) {
|
||||
@media (min-resolution: 1.25dppx) {
|
||||
.timeline .toggle::before {
|
||||
background-image: url(debugger-pause@2x.png);
|
||||
}
|
||||
|
|
|
@ -145,7 +145,7 @@
|
|||
list-style-image: url(debugger-step-out.png);
|
||||
}
|
||||
|
||||
@media (min-resolution: 2dppx) {
|
||||
@media (min-resolution: 1.25dppx) {
|
||||
#resume {
|
||||
list-style-image: url(debugger-play@2x.png);
|
||||
-moz-image-region: rect(0px,64px,32px,32px);
|
||||
|
@ -250,7 +250,7 @@
|
|||
background-size: 12px;
|
||||
}
|
||||
|
||||
@media (min-resolution: 2dppx) {
|
||||
@media (min-resolution: 1.25dppx) {
|
||||
.selected .call-item-gutter {
|
||||
background-image: url("editor-debug-location@2x.png");
|
||||
}
|
||||
|
|
|
@ -82,7 +82,7 @@
|
|||
-moz-image-region: rect(0px, 64px, 16px, 48px);
|
||||
}
|
||||
|
||||
@media (min-resolution: 2dppx) {
|
||||
@media (min-resolution: 1.25dppx) {
|
||||
#developer-toolbar-toolbox-button {
|
||||
list-style-image: url("chrome://browser/skin/devtools/toggle-tools@2x.png");
|
||||
-moz-image-region: rect(0px, 32px, 32px, 0px);
|
||||
|
@ -111,7 +111,7 @@
|
|||
opacity: 0.6;
|
||||
}
|
||||
|
||||
@media (min-resolution: 2dppx) {
|
||||
@media (min-resolution: 1.25dppx) {
|
||||
#developer-toolbar-closebutton {
|
||||
list-style-image: url("chrome://browser/skin/devtools/close@2x.png");
|
||||
}
|
||||
|
@ -199,7 +199,7 @@ html|*#gcli-output-frame {
|
|||
background-position: -16px center;
|
||||
}
|
||||
|
||||
@media (min-resolution: 2dppx) {
|
||||
@media (min-resolution: 1.25dppx) {
|
||||
.gclitoolbar-input-node::before {
|
||||
background-image: url("chrome://browser/skin/devtools/commandline-icon@2x.png");
|
||||
}
|
||||
|
|
|
@ -61,7 +61,7 @@ body {
|
|||
background-size: 5px 8px;
|
||||
}
|
||||
|
||||
@media (min-resolution: 2dppx) {
|
||||
@media (min-resolution: 1.25dppx) {
|
||||
.property-value, .other-property-value {
|
||||
background-image: url(arrow-e@2x.png);
|
||||
}
|
||||
|
|
|
@ -326,7 +326,7 @@ div.CodeMirror span.eval-text {
|
|||
background-position: -42px 0;
|
||||
}
|
||||
|
||||
@media (min-resolution: 2dppx) {
|
||||
@media (min-resolution: 1.25dppx) {
|
||||
.theme-twisty, .theme-checkbox {
|
||||
background-image: url("chrome://browser/skin/devtools/controls@2x.png");
|
||||
}
|
||||
|
@ -364,7 +364,7 @@ div.CodeMirror span.eval-text {
|
|||
margin-left: -4px;
|
||||
}
|
||||
|
||||
@media (min-resolution: 2dppx) {
|
||||
@media (min-resolution: 1.25dppx) {
|
||||
.theme-tooltip-panel .panel-arrow[side="top"],
|
||||
.theme-tooltip-panel .panel-arrow[side="bottom"] {
|
||||
list-style-image: url("chrome://browser/skin/devtools/tooltip/arrow-vertical-dark@2x.png");
|
||||
|
|
|
@ -66,7 +66,7 @@
|
|||
list-style-image: url(debugger-blackbox.png);
|
||||
}
|
||||
|
||||
@media (min-resolution: 2dppx) {
|
||||
@media (min-resolution: 1.25dppx) {
|
||||
#black-box {
|
||||
list-style-image: url(debugger-blackbox@2x.png);
|
||||
}
|
||||
|
@ -76,7 +76,7 @@
|
|||
list-style-image: url(debugger-prettyprint.png);
|
||||
}
|
||||
|
||||
@media (min-resolution: 2dppx) {
|
||||
@media (min-resolution: 1.25dppx) {
|
||||
#pretty-print {
|
||||
list-style-image: url(debugger-prettyprint@2x.png);
|
||||
}
|
||||
|
@ -86,7 +86,7 @@
|
|||
list-style-image: url(debugger-toggleBreakpoints.png);
|
||||
}
|
||||
|
||||
@media (min-resolution: 2dppx) {
|
||||
@media (min-resolution: 1.25dppx) {
|
||||
#toggle-breakpoints {
|
||||
list-style-image: url(debugger-toggleBreakpoints@2x.png);
|
||||
}
|
||||
|
@ -100,7 +100,7 @@
|
|||
-moz-image-region: rect(0px,32px,16px,16px);
|
||||
}
|
||||
|
||||
@media (min-resolution: 2dppx) {
|
||||
@media (min-resolution: 1.25dppx) {
|
||||
#sources-toolbar .devtools-toolbarbutton:not([label]) {
|
||||
-moz-image-region: rect(0px,32px,32px,0px);
|
||||
}
|
||||
|
@ -134,7 +134,7 @@
|
|||
-moz-margin-end: 5px;
|
||||
}
|
||||
|
||||
@media (min-resolution: 2dppx) {
|
||||
@media (min-resolution: 1.25dppx) {
|
||||
#black-boxed-message-button > .button-box > .button-icon {
|
||||
background-image: url(debugger-blackbox@2x.png);
|
||||
}
|
||||
|
@ -222,7 +222,7 @@
|
|||
-moz-image-region: rect(0px,32px,16px,16px);
|
||||
}
|
||||
|
||||
@media (min-resolution: 2dppx) {
|
||||
@media (min-resolution: 1.25dppx) {
|
||||
#trace {
|
||||
list-style-image: url(tracer-icon@2x.png);
|
||||
-moz-image-region: rect(0px,32px,32px,0px);
|
||||
|
@ -324,7 +324,7 @@
|
|||
margin: 2px;
|
||||
}
|
||||
|
||||
@media (min-resolution: 2dppx) {
|
||||
@media (min-resolution: 1.25dppx) {
|
||||
.dbg-expression-arrow {
|
||||
background-image: url(commandline-icon@2x.png);
|
||||
}
|
||||
|
@ -560,7 +560,7 @@
|
|||
list-style-image: url(debugger-play.png);
|
||||
}
|
||||
|
||||
@media (min-resolution: 2dppx) {
|
||||
@media (min-resolution: 1.25dppx) {
|
||||
#resume {
|
||||
list-style-image: url(debugger-pause@2x.png);
|
||||
-moz-image-region: rect(0px,32px,32px,0px);
|
||||
|
@ -592,7 +592,7 @@
|
|||
list-style-image: url(debugger-step-out.png);
|
||||
}
|
||||
|
||||
@media (min-resolution: 2dppx) {
|
||||
@media (min-resolution: 1.25dppx) {
|
||||
#step-over {
|
||||
list-style-image: url(debugger-step-over@2x.png);
|
||||
}
|
||||
|
@ -622,7 +622,7 @@
|
|||
-moz-image-region: rect(0px,32px,16px,16px);
|
||||
}
|
||||
|
||||
@media (min-resolution: 2dppx) {
|
||||
@media (min-resolution: 1.25dppx) {
|
||||
#instruments-pane-toggle {
|
||||
list-style-image: url(debugger-collapse@2x.png);
|
||||
-moz-image-region: rect(0px,32px,32px,0px);
|
||||
|
|
|
@ -60,7 +60,7 @@
|
|||
-moz-image-region: rect(0px,32px,16px,16px);
|
||||
}
|
||||
|
||||
@media (min-resolution: 2dppx) {
|
||||
@media (min-resolution: 1.25dppx) {
|
||||
#inspector-pane-toggle {
|
||||
list-style-image: url(debugger-collapse@2x.png);
|
||||
-moz-image-region: rect(0px,32px,32px,0px);
|
||||
|
|
|
@ -335,7 +335,7 @@ div.CodeMirror span.eval-text {
|
|||
background-position: -14px 0;
|
||||
}
|
||||
|
||||
@media (min-resolution: 2dppx) {
|
||||
@media (min-resolution: 1.25dppx) {
|
||||
.theme-twisty, .theme-checkbox {
|
||||
background-image: url("chrome://browser/skin/devtools/controls@2x.png");
|
||||
}
|
||||
|
@ -373,7 +373,7 @@ div.CodeMirror span.eval-text {
|
|||
margin-left: -4px;
|
||||
}
|
||||
|
||||
@media (min-resolution: 2dppx) {
|
||||
@media (min-resolution: 1.25dppx) {
|
||||
.theme-tooltip-panel .panel-arrow[side="top"],
|
||||
.theme-tooltip-panel .panel-arrow[side="bottom"] {
|
||||
list-style-image: url("chrome://browser/skin/devtools/tooltip/arrow-vertical-light@2x.png");
|
||||
|
|
|
@ -470,7 +470,7 @@ label.requests-menu-status-code {
|
|||
-moz-image-region: rect(0px,32px,16px,16px);
|
||||
}
|
||||
|
||||
@media (min-resolution: 2dppx) {
|
||||
@media (min-resolution: 1.25dppx) {
|
||||
#details-pane-toggle {
|
||||
list-style-image: url("chrome://browser/skin/devtools/debugger-collapse@2x.png");
|
||||
-moz-image-region: rect(0px,32px,32px,0px);
|
||||
|
@ -603,7 +603,7 @@ label.requests-menu-status-code {
|
|||
height: 12px;
|
||||
}
|
||||
|
||||
@media (min-resolution: 2dppx) {
|
||||
@media (min-resolution: 1.25dppx) {
|
||||
.security-warning-icon {
|
||||
background-image: url(alerticon-warning@2x.png);
|
||||
}
|
||||
|
|
|
@ -281,7 +281,7 @@
|
|||
background-image: url(magnifying-glass-light.png);
|
||||
}
|
||||
|
||||
@media (min-resolution: 2dppx) {
|
||||
@media (min-resolution: 1.25dppx) {
|
||||
.theme-dark .call-tree-zoom {
|
||||
background-image: url(magnifying-glass@2x.png);
|
||||
}
|
||||
|
@ -610,7 +610,7 @@
|
|||
background-position: -16px -16px;
|
||||
}
|
||||
|
||||
@media (min-resolution: 2dppx) {
|
||||
@media (min-resolution: 1.25dppx) {
|
||||
#jit-optimizations-view .opt-icon::before {
|
||||
background-image: url(chrome://browser/skin/devtools/webconsole@2x.png);
|
||||
}
|
||||
|
|
|
@ -190,7 +190,7 @@
|
|||
background-image: url(newtab.png);
|
||||
}
|
||||
|
||||
@media (min-resolution: 2dppx) {
|
||||
@media (min-resolution: 1.25dppx) {
|
||||
.theme-dark #profile-newtab-button {
|
||||
background-image: url(newtab-inverted@2x.png);
|
||||
}
|
||||
|
@ -367,7 +367,7 @@
|
|||
background-image: url(magnifying-glass-light.png);
|
||||
}
|
||||
|
||||
@media (min-resolution: 2dppx) {
|
||||
@media (min-resolution: 1.25dppx) {
|
||||
.theme-dark .call-tree-zoom {
|
||||
background-image: url(magnifying-glass@2x.png);
|
||||
}
|
||||
|
|
|
@ -155,7 +155,7 @@
|
|||
list-style-image: url("chrome://browser/skin/devtools/responsiveui-rotate.png");
|
||||
}
|
||||
|
||||
@media (min-resolution: 2dppx) {
|
||||
@media (min-resolution: 1.25dppx) {
|
||||
.devtools-responsiveui-close {
|
||||
list-style-image: url("chrome://browser/skin/devtools/close@2x.png");
|
||||
}
|
||||
|
@ -174,7 +174,7 @@
|
|||
-moz-image-region: rect(0px,32px,16px,16px);
|
||||
}
|
||||
|
||||
@media (min-resolution: 2dppx) {
|
||||
@media (min-resolution: 1.25dppx) {
|
||||
.devtools-responsiveui-touch {
|
||||
list-style-image: url("chrome://browser/skin/devtools/responsiveui-touch@2x.png");
|
||||
-moz-image-region: rect(0px,32px,32px,0px);
|
||||
|
@ -189,7 +189,7 @@
|
|||
list-style-image: url("chrome://browser/skin/devtools/responsiveui-screenshot.png");
|
||||
}
|
||||
|
||||
@media (min-resolution: 2dppx) {
|
||||
@media (min-resolution: 1.25dppx) {
|
||||
.devtools-responsiveui-screenshot {
|
||||
list-style-image: url("chrome://browser/skin/devtools/responsiveui-screenshot@2x.png");
|
||||
}
|
||||
|
@ -321,7 +321,7 @@
|
|||
border-bottom-left-radius: 12px;
|
||||
}
|
||||
|
||||
@media (min-resolution: 2dppx) {
|
||||
@media (min-resolution: 1.25dppx) {
|
||||
.devtools-responsiveui-resizebarV {
|
||||
background-image: url("chrome://browser/skin/devtools/responsive-vertical-resizer@2x.png");
|
||||
}
|
||||
|
|
|
@ -107,7 +107,7 @@
|
|||
}
|
||||
|
||||
|
||||
@media (min-resolution: 2dppx) {
|
||||
@media (min-resolution: 1.25dppx) {
|
||||
.ruleview-warning {
|
||||
background-image: url(alerticon-warning@2x.png);
|
||||
}
|
||||
|
@ -194,7 +194,7 @@
|
|||
background-size: 1em;
|
||||
}
|
||||
|
||||
@media (min-resolution: 2dppx) {
|
||||
@media (min-resolution: 1.25dppx) {
|
||||
.ruleview-bezierswatch {
|
||||
background: url("chrome://browser/skin/devtools/cubic-bezier-swatch@2x.png");
|
||||
background-size: 1em;
|
||||
|
|
|
@ -60,7 +60,7 @@
|
|||
border: 0;
|
||||
}
|
||||
|
||||
@media (min-resolution: 2dppx) {
|
||||
@media (min-resolution: 1.25dppx) {
|
||||
.side-menu-widget-item-checkbox .checkbox-check {
|
||||
background-image: url(itemToggle@2x.png);
|
||||
}
|
||||
|
|
|
@ -115,7 +115,7 @@
|
|||
height: 40px;
|
||||
}
|
||||
|
||||
@media (min-resolution: 2dppx) {
|
||||
@media (min-resolution: 1.25dppx) {
|
||||
.stylesheet-enabled {
|
||||
background-image: url(itemToggle@2x.png);
|
||||
}
|
||||
|
|
|
@ -316,7 +316,7 @@
|
|||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
@media (min-resolution: 2dppx) {
|
||||
@media (min-resolution: 1.25dppx) {
|
||||
.devtools-button::before {
|
||||
background-size: 32px;
|
||||
}
|
||||
|
@ -437,7 +437,7 @@
|
|||
-moz-image-region: rect(0, 32px, 16px, 16px);
|
||||
}
|
||||
|
||||
@media (min-resolution: 2dppx) {
|
||||
@media (min-resolution: 1.25dppx) {
|
||||
.theme-dark .devtools-searchinput {
|
||||
background-image: url(magnifying-glass@2x.png);
|
||||
}
|
||||
|
@ -769,7 +769,7 @@
|
|||
background-image: url("chrome://browser/skin/devtools/command-rulers.png");
|
||||
}
|
||||
|
||||
@media (min-resolution: 2dppx) {
|
||||
@media (min-resolution: 1.25dppx) {
|
||||
#command-button-paintflashing > image {
|
||||
background-image: url("chrome://browser/skin/devtools/command-paintflashing@2x.png");
|
||||
}
|
||||
|
|
|
@ -187,7 +187,7 @@ text {
|
|||
-moz-box-flex: 1;
|
||||
}
|
||||
|
||||
@media (min-resolution: 2dppx) {
|
||||
@media (min-resolution: 1.25dppx) {
|
||||
#inspector-pane-toggle {
|
||||
list-style-image: url(debugger-collapse@2x.png);
|
||||
-moz-image-region: rect(0px,32px,32px,0px);
|
||||
|
|
|
@ -52,7 +52,7 @@ a {
|
|||
display: inline-block;
|
||||
}
|
||||
|
||||
@media (min-resolution: 2dppx) {
|
||||
@media (min-resolution: 1.25dppx) {
|
||||
.message > .icon::before {
|
||||
background-image: url(chrome://browser/skin/devtools/webconsole@2x.png);
|
||||
}
|
||||
|
@ -361,7 +361,7 @@ a {
|
|||
background-size: 16px 16px;
|
||||
}
|
||||
|
||||
@media (min-resolution: 2dppx) {
|
||||
@media (min-resolution: 1.25dppx) {
|
||||
.jsterm-input-node {
|
||||
background-image: -moz-image-rect(url('chrome://browser/skin/devtools/commandline-icon@2x.png'), 0, 64, 32, 32);
|
||||
}
|
||||
|
|
|
@ -107,7 +107,7 @@
|
|||
padding: 0;
|
||||
}
|
||||
|
||||
@media (min-resolution: 2dppx) {
|
||||
@media (min-resolution: 1.25dppx) {
|
||||
.scrollbutton-up > .toolbarbutton-icon,
|
||||
.scrollbutton-down > .toolbarbutton-icon {
|
||||
background-image: url("breadcrumbs-scrollbutton@2x.png");
|
||||
|
@ -637,7 +637,7 @@
|
|||
height: 16px;
|
||||
}
|
||||
|
||||
@media (min-resolution: 2dppx) {
|
||||
@media (min-resolution: 1.25dppx) {
|
||||
.variable-or-property-non-writable-icon {
|
||||
background-image: url("chrome://browser/skin/devtools/vview-lock@2x.png");
|
||||
}
|
||||
|
@ -737,7 +737,7 @@
|
|||
height: 16px;
|
||||
}
|
||||
|
||||
@media (min-resolution: 2dppx) {
|
||||
@media (min-resolution: 1.25dppx) {
|
||||
.variables-view-delete {
|
||||
background-image: url("chrome://browser/skin/devtools/vview-delete@2x.png");
|
||||
}
|
||||
|
@ -763,7 +763,7 @@
|
|||
cursor: pointer;
|
||||
}
|
||||
|
||||
@media (min-resolution: 2dppx) {
|
||||
@media (min-resolution: 1.25dppx) {
|
||||
.variables-view-edit {
|
||||
background-image: url("chrome://browser/skin/devtools/vview-edit@2x.png");
|
||||
}
|
||||
|
@ -789,7 +789,7 @@
|
|||
cursor: pointer;
|
||||
}
|
||||
|
||||
@media (min-resolution: 2dppx) {
|
||||
@media (min-resolution: 1.25dppx) {
|
||||
.variables-view-open-inspector {
|
||||
background-image: url("chrome://browser/skin/devtools/vview-open-inspector@2x.png");
|
||||
}
|
||||
|
@ -1437,7 +1437,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
@media (min-resolution: 2dppx) {
|
||||
@media (min-resolution: 1.25dppx) {
|
||||
.tree-widget-item:before {
|
||||
background-image: url("chrome://browser/skin/devtools/controls@2x.png");
|
||||
}
|
||||
|
|
|
@ -918,13 +918,30 @@ public class BrowserApp extends GeckoApp
|
|||
@Override
|
||||
public void run() {
|
||||
if (TabQueueHelper.shouldOpenTabQueueUrls(BrowserApp.this)) {
|
||||
TabQueueHelper.openQueuedUrls(BrowserApp.this, mProfile, TabQueueHelper.FILE_NAME, false);
|
||||
openQueuedTabs();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void openQueuedTabs() {
|
||||
ThreadUtils.assertNotOnUiThread();
|
||||
|
||||
int queuedTabCount = TabQueueHelper.getTabQueueLength(BrowserApp.this);
|
||||
TabQueueHelper.openQueuedUrls(BrowserApp.this, mProfile, TabQueueHelper.FILE_NAME, false);
|
||||
|
||||
// If there's more than one tab then also show the tabs panel.
|
||||
if (queuedTabCount > 1) {
|
||||
ThreadUtils.postToUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
showNormalTabs();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
@ -3423,18 +3440,7 @@ public class BrowserApp extends GeckoApp
|
|||
ThreadUtils.postToBackgroundThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
int queuedTabCount = TabQueueHelper.getTabQueueLength(BrowserApp.this);
|
||||
TabQueueHelper.openQueuedUrls(BrowserApp.this, mProfile, TabQueueHelper.FILE_NAME, false);
|
||||
|
||||
// If there's more than one tab then also show the tabs panel.
|
||||
if (queuedTabCount > 1) {
|
||||
ThreadUtils.postToUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
showNormalTabs();
|
||||
}
|
||||
});
|
||||
}
|
||||
openQueuedTabs();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -783,7 +783,7 @@ class KeyedHistogram {
|
|||
public:
|
||||
KeyedHistogram(const nsACString &name, const nsACString &expiration,
|
||||
uint32_t histogramType, uint32_t min, uint32_t max,
|
||||
uint32_t bucketCount);
|
||||
uint32_t bucketCount, uint32_t dataset);
|
||||
nsresult GetHistogram(const nsCString& name, Histogram** histogram, bool subsession);
|
||||
Histogram* GetHistogram(const nsCString& name, bool subsession);
|
||||
uint32_t GetHistogramType() const { return mHistogramType; }
|
||||
|
@ -821,6 +821,7 @@ private:
|
|||
const uint32_t mMin;
|
||||
const uint32_t mMax;
|
||||
const uint32_t mBucketCount;
|
||||
const uint32_t mDataset;
|
||||
};
|
||||
|
||||
// Hardcoded probes
|
||||
|
@ -874,22 +875,41 @@ IsValidHistogramName(const nsACString& name)
|
|||
}
|
||||
|
||||
bool
|
||||
IsInDataset(const TelemetryHistogram& h, uint32_t dataset)
|
||||
IsInDataset(uint32_t dataset, uint32_t containingDataset)
|
||||
{
|
||||
if (h.dataset == dataset) {
|
||||
if (dataset == containingDataset) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// The "optin on release channel" dataset is a superset of the
|
||||
// "optout on release channel one".
|
||||
if (dataset == nsITelemetry::DATASET_RELEASE_CHANNEL_OPTIN
|
||||
&& h.dataset == nsITelemetry::DATASET_RELEASE_CHANNEL_OPTOUT) {
|
||||
if (containingDataset == nsITelemetry::DATASET_RELEASE_CHANNEL_OPTIN
|
||||
&& dataset == nsITelemetry::DATASET_RELEASE_CHANNEL_OPTOUT) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
CanRecordDataset(uint32_t dataset)
|
||||
{
|
||||
// If we are extended telemetry is enabled, we are allowed to record regardless of
|
||||
// the dataset.
|
||||
if (TelemetryImpl::CanRecordExtended()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If base telemetry data is enabled and we're trying to record base telemetry, allow it.
|
||||
if (TelemetryImpl::CanRecordBase() &&
|
||||
IsInDataset(dataset, nsITelemetry::DATASET_RELEASE_CHANNEL_OPTOUT)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// We're not recording extended telemetry or this is not the base dataset. Bail out.
|
||||
return false;
|
||||
}
|
||||
|
||||
nsresult
|
||||
CheckHistogramArguments(uint32_t histogramType, uint32_t min, uint32_t max,
|
||||
uint32_t bucketCount, bool haveOptArgs)
|
||||
|
@ -1079,8 +1099,13 @@ GetSubsessionHistogram(Histogram& existing)
|
|||
#endif
|
||||
|
||||
nsresult
|
||||
HistogramAdd(Histogram& histogram, int32_t value)
|
||||
HistogramAdd(Histogram& histogram, int32_t value, uint32_t dataset)
|
||||
{
|
||||
// Check if we are allowed to record the data.
|
||||
if (!CanRecordDataset(dataset)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
histogram.Add(value);
|
||||
#if !defined(MOZ_WIDGET_GONK) && !defined(MOZ_WIDGET_ANDROID)
|
||||
if (Histogram* subsession = GetSubsessionHistogram(histogram)) {
|
||||
|
@ -1091,6 +1116,26 @@ HistogramAdd(Histogram& histogram, int32_t value)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
HistogramAdd(Histogram& histogram, int32_t value)
|
||||
{
|
||||
uint32_t dataset = nsITelemetry::DATASET_RELEASE_CHANNEL_OPTIN;
|
||||
// We only really care about the dataset of the histogram if we are not recording
|
||||
// extended telemetry. Otherwise, we always record histogram data.
|
||||
if (!TelemetryImpl::CanRecordExtended()) {
|
||||
Telemetry::ID id;
|
||||
nsresult rv = TelemetryImpl::GetHistogramEnumId(histogram.histogram_name().c_str(), &id);
|
||||
if (NS_FAILED(rv)) {
|
||||
// If we can't look up the dataset, it might be because the histogram was added
|
||||
// at runtime. Since we're not recording extended telemetry, bail out.
|
||||
return NS_OK;
|
||||
}
|
||||
dataset = gHistograms[id].dataset;
|
||||
}
|
||||
|
||||
return HistogramAdd(histogram, value, dataset);
|
||||
}
|
||||
|
||||
bool
|
||||
FillRanges(JSContext *cx, JS::Handle<JSObject*> array, Histogram *h)
|
||||
{
|
||||
|
@ -1210,7 +1255,7 @@ JSHistogram_Add(JSContext *cx, unsigned argc, JS::Value *vp)
|
|||
}
|
||||
}
|
||||
|
||||
if (TelemetryImpl::CanRecordExtended()) {
|
||||
if (TelemetryImpl::CanRecordBase()) {
|
||||
HistogramAdd(*h, value);
|
||||
}
|
||||
|
||||
|
@ -1866,7 +1911,7 @@ mFailedLockCount(0)
|
|||
const nsDependentCString id(h.id());
|
||||
const nsDependentCString expiration(h.expiration());
|
||||
mKeyedHistograms.Put(id, new KeyedHistogram(id, expiration, h.histogramType,
|
||||
h.min, h.max, h.bucketCount));
|
||||
h.min, h.max, h.bucketCount, h.dataset));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1913,7 +1958,8 @@ TelemetryImpl::NewKeyedHistogram(const nsACString &name, const nsACString &expir
|
|||
}
|
||||
|
||||
KeyedHistogram* keyed = new KeyedHistogram(name, expiration, histogramType,
|
||||
min, max, bucketCount);
|
||||
min, max, bucketCount,
|
||||
nsITelemetry::DATASET_RELEASE_CHANNEL_OPTIN);
|
||||
if (MOZ_UNLIKELY(!mKeyedHistograms.Put(name, keyed, fallible))) {
|
||||
delete keyed;
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
@ -3076,7 +3122,7 @@ GetRegisteredHistogramIds(bool keyed, uint32_t dataset, uint32_t *aCount,
|
|||
for (size_t i = 0; i < ArrayLength(gHistograms); ++i) {
|
||||
const TelemetryHistogram& h = gHistograms[i];
|
||||
if (IsExpired(h.expiration()) || h.keyed != keyed ||
|
||||
!IsInDataset(h, dataset)) {
|
||||
!IsInDataset(h.dataset, dataset)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -3622,22 +3668,22 @@ namespace Telemetry {
|
|||
void
|
||||
Accumulate(ID aHistogram, uint32_t aSample)
|
||||
{
|
||||
if (!TelemetryImpl::CanRecordExtended()) {
|
||||
if (!TelemetryImpl::CanRecordBase()) {
|
||||
return;
|
||||
}
|
||||
Histogram *h;
|
||||
nsresult rv = GetHistogramByEnumId(aHistogram, &h);
|
||||
if (NS_SUCCEEDED(rv))
|
||||
HistogramAdd(*h, aSample);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
HistogramAdd(*h, aSample, gHistograms[aHistogram].dataset);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Accumulate(ID aID, const nsCString& aKey, uint32_t aSample)
|
||||
{
|
||||
if (!TelemetryImpl::CanRecordExtended()) {
|
||||
if (!TelemetryImpl::CanRecordBase()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const TelemetryHistogram& th = gHistograms[aID];
|
||||
KeyedHistogram* keyed = TelemetryImpl::GetKeyedHistogramById(nsDependentCString(th.id()));
|
||||
MOZ_ASSERT(keyed);
|
||||
|
@ -3647,7 +3693,7 @@ Accumulate(ID aID, const nsCString& aKey, uint32_t aSample)
|
|||
void
|
||||
Accumulate(const char* name, uint32_t sample)
|
||||
{
|
||||
if (!TelemetryImpl::CanRecordExtended()) {
|
||||
if (!TelemetryImpl::CanRecordBase()) {
|
||||
return;
|
||||
}
|
||||
ID id;
|
||||
|
@ -3659,7 +3705,7 @@ Accumulate(const char* name, uint32_t sample)
|
|||
Histogram *h;
|
||||
rv = GetHistogramByEnumId(id, &h);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
HistogramAdd(*h, sample);
|
||||
HistogramAdd(*h, sample, gHistograms[id].dataset);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4066,7 +4112,7 @@ XRE_TelemetryAccumulate(int aID, uint32_t aSample)
|
|||
|
||||
KeyedHistogram::KeyedHistogram(const nsACString &name, const nsACString &expiration,
|
||||
uint32_t histogramType, uint32_t min, uint32_t max,
|
||||
uint32_t bucketCount)
|
||||
uint32_t bucketCount, uint32_t dataset)
|
||||
: mHistogramMap()
|
||||
#if !defined(MOZ_WIDGET_GONK) && !defined(MOZ_WIDGET_ANDROID)
|
||||
, mSubsessionMap()
|
||||
|
@ -4077,6 +4123,7 @@ KeyedHistogram::KeyedHistogram(const nsACString &name, const nsACString &expirat
|
|||
, mMin(min)
|
||||
, mMax(max)
|
||||
, mBucketCount(bucketCount)
|
||||
, mDataset(dataset)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -4140,14 +4187,7 @@ nsresult
|
|||
KeyedHistogram::GetDataset(uint32_t* dataset) const
|
||||
{
|
||||
MOZ_ASSERT(dataset);
|
||||
|
||||
Telemetry::ID id;
|
||||
nsresult rv = TelemetryImpl::GetHistogramEnumId(mName.get(), &id);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
*dataset = gHistograms[id].dataset;
|
||||
*dataset = mDataset;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -4162,7 +4202,7 @@ KeyedHistogram::ClearHistogramEnumerator(KeyedHistogramEntry* entry, void*)
|
|||
nsresult
|
||||
KeyedHistogram::Add(const nsCString& key, uint32_t sample)
|
||||
{
|
||||
if (!TelemetryImpl::CanRecordExtended()) {
|
||||
if (!CanRecordDataset(mDataset)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -360,6 +360,60 @@ function test_privateMode() {
|
|||
do_check_neq(uneval(orig), uneval(h.snapshot()));
|
||||
}
|
||||
|
||||
// Check that telemetry records only when it is suppose to.
|
||||
function test_histogramRecording() {
|
||||
// Check that no histogram is recorded if both base and extended recording are off.
|
||||
Telemetry.canRecordBase = false;
|
||||
Telemetry.canRecordExtended = false;
|
||||
|
||||
let h = Telemetry.getHistogramById("TELEMETRY_TEST_RELEASE_OPTOUT");
|
||||
h.clear();
|
||||
let orig = h.snapshot();
|
||||
h.add(1);
|
||||
Assert.equal(orig.sum, h.snapshot().sum);
|
||||
|
||||
// Check that only base histograms are recorded.
|
||||
Telemetry.canRecordBase = true;
|
||||
h.add(1);
|
||||
Assert.equal(orig.sum + 1, h.snapshot().sum,
|
||||
"Histogram value should have incremented by 1 due to recording.");
|
||||
|
||||
// Extended histograms should not be recorded.
|
||||
h = Telemetry.getHistogramById("TELEMETRY_TEST_RELEASE_OPTIN");
|
||||
orig = h.snapshot();
|
||||
h.add(1);
|
||||
Assert.equal(orig.sum, h.snapshot().sum,
|
||||
"Histograms should be equal after recording.");
|
||||
|
||||
// Runtime created histograms should not be recorded.
|
||||
h = Telemetry.newHistogram("test::runtime_created_boolean", "never", Telemetry.HISTOGRAM_BOOLEAN);
|
||||
orig = h.snapshot();
|
||||
h.add(1);
|
||||
Assert.equal(orig.sum, h.snapshot().sum,
|
||||
"Histograms should be equal after recording.");
|
||||
|
||||
// Check that extended histograms are recorded when required.
|
||||
Telemetry.canRecordExtended = true;
|
||||
|
||||
h.add(1);
|
||||
Assert.equal(orig.sum + 1, h.snapshot().sum,
|
||||
"Runtime histogram value should have incremented by 1 due to recording.");
|
||||
|
||||
h = Telemetry.getHistogramById("TELEMETRY_TEST_RELEASE_OPTIN");
|
||||
orig = h.snapshot();
|
||||
h.add(1);
|
||||
Assert.equal(orig.sum + 1, h.snapshot().sum,
|
||||
"Histogram value should have incremented by 1 due to recording.");
|
||||
|
||||
// Check that base histograms are still being recorded.
|
||||
h = Telemetry.getHistogramById("TELEMETRY_TEST_RELEASE_OPTOUT");
|
||||
h.clear();
|
||||
orig = h.snapshot();
|
||||
h.add(1);
|
||||
Assert.equal(orig.sum + 1, h.snapshot().sum,
|
||||
"Histogram value should have incremented by 1 due to recording.");
|
||||
}
|
||||
|
||||
// Check that histograms that aren't flagged as needing extended stats
|
||||
// don't record extended stats.
|
||||
function test_extended_stats() {
|
||||
|
@ -528,6 +582,56 @@ function test_keyed_flag_histogram()
|
|||
Assert.deepEqual(h.snapshot(), {});
|
||||
}
|
||||
|
||||
function test_keyed_histogram_recording() {
|
||||
// Check that no histogram is recorded if both base and extended recording are off.
|
||||
Telemetry.canRecordBase = false;
|
||||
Telemetry.canRecordExtended = false;
|
||||
|
||||
const TEST_KEY = "record_foo";
|
||||
let h = Telemetry.getKeyedHistogramById("TELEMETRY_TEST_KEYED_RELEASE_OPTOUT");
|
||||
h.clear();
|
||||
h.add(TEST_KEY, 1);
|
||||
Assert.equal(h.snapshot(TEST_KEY).sum, 0);
|
||||
|
||||
// Check that only base histograms are recorded.
|
||||
Telemetry.canRecordBase = true;
|
||||
h.add(TEST_KEY, 1);
|
||||
Assert.equal(h.snapshot(TEST_KEY).sum, 1,
|
||||
"The keyed histogram should record the correct value.");
|
||||
|
||||
// Extended set keyed histograms should not be recorded.
|
||||
h = Telemetry.getKeyedHistogramById("TELEMETRY_TEST_KEYED_RELEASE_OPTIN");
|
||||
h.clear();
|
||||
h.add(TEST_KEY, 1);
|
||||
Assert.equal(h.snapshot(TEST_KEY).sum, 0,
|
||||
"The keyed histograms should not record any data.");
|
||||
|
||||
// Runtime created histograms should not be recorded.
|
||||
h = Telemetry.newKeyedHistogram("test::runtime_keyed_boolean", "never", Telemetry.HISTOGRAM_BOOLEAN);
|
||||
h.add(TEST_KEY, 1);
|
||||
Assert.equal(h.snapshot(TEST_KEY).sum, 0,
|
||||
"The keyed histogram should not record any data.");
|
||||
|
||||
// Check that extended histograms are recorded when required.
|
||||
Telemetry.canRecordExtended = true;
|
||||
|
||||
h.add(TEST_KEY, 1);
|
||||
Assert.equal(h.snapshot(TEST_KEY).sum, 1,
|
||||
"The runtime keyed histogram should record the correct value.");
|
||||
|
||||
h = Telemetry.getKeyedHistogramById("TELEMETRY_TEST_KEYED_RELEASE_OPTIN");
|
||||
h.clear();
|
||||
h.add(TEST_KEY, 1);
|
||||
Assert.equal(h.snapshot(TEST_KEY).sum, 1,
|
||||
"The keyed histogram should record the correct value.");
|
||||
|
||||
// Check that base histograms are still being recorded.
|
||||
h = Telemetry.getKeyedHistogramById("TELEMETRY_TEST_KEYED_RELEASE_OPTOUT");
|
||||
h.clear();
|
||||
h.add(TEST_KEY, 1);
|
||||
Assert.equal(h.snapshot(TEST_KEY).sum, 1);
|
||||
}
|
||||
|
||||
function test_keyed_histogram() {
|
||||
// Check that invalid names get rejected.
|
||||
|
||||
|
@ -554,6 +658,7 @@ function test_keyed_histogram() {
|
|||
test_keyed_boolean_histogram();
|
||||
test_keyed_count_histogram();
|
||||
test_keyed_flag_histogram();
|
||||
test_keyed_histogram_recording();
|
||||
}
|
||||
|
||||
function test_datasets()
|
||||
|
@ -770,6 +875,7 @@ function run_test()
|
|||
test_histogramFrom();
|
||||
test_getSlowSQL();
|
||||
test_privateMode();
|
||||
test_histogramRecording();
|
||||
test_addons();
|
||||
test_extended_stats();
|
||||
test_expired_histogram();
|
||||
|
|
|
@ -180,15 +180,18 @@ exports.items = [
|
|||
throw new Error(l10n.lookup("screenshotSelectorChromeConflict"));
|
||||
}
|
||||
|
||||
let capture;
|
||||
if (!args.chrome) {
|
||||
// Re-execute the command on the server
|
||||
const command = context.typed.replace(/^screenshot/, "screenshot_server");
|
||||
return context.updateExec(command).then(output => {
|
||||
capture = context.updateExec(command).then(output => {
|
||||
return output.error ? Promise.reject(output.data) : output.data;
|
||||
});
|
||||
} else {
|
||||
capture = captureScreenshot(args, context.environment.chromeDocument);
|
||||
}
|
||||
|
||||
return processScreenshot(args, context.environment.chromeDocument);
|
||||
return capture.then(saveScreenshot.bind(null, args, context));
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -199,53 +202,45 @@ exports.items = [
|
|||
returnType: "imageSummary",
|
||||
params: [ filenameParam, standardParams ],
|
||||
exec: function(args, context) {
|
||||
return processScreenshot(args, context.environment.document);
|
||||
return captureScreenshot(args, context.environment.document);
|
||||
},
|
||||
}
|
||||
];
|
||||
|
||||
/**
|
||||
* This function simply handles the --delay argument before calling
|
||||
* processScreenshotNow
|
||||
* createScreenshotData
|
||||
*/
|
||||
function processScreenshot(args, document) {
|
||||
function captureScreenshot(args, document) {
|
||||
if (args.delay > 0) {
|
||||
return new Promise((resolve, reject) => {
|
||||
document.defaultView.setTimeout(() => {
|
||||
processScreenshotNow(args, document).then(resolve, reject);
|
||||
createScreenshotData(document, args).then(resolve, reject);
|
||||
}, args.delay * 1000);
|
||||
});
|
||||
}
|
||||
else {
|
||||
return processScreenshotNow(args, document);
|
||||
return createScreenshotData(document, args);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* There are several possible destinations for the screenshot, SKIP is used
|
||||
* in processScreenshotNow() whenever one of them is not used
|
||||
* in saveScreenshot() whenever one of them is not used
|
||||
*/
|
||||
const SKIP = Promise.resolve();
|
||||
|
||||
/**
|
||||
* This is just like exec, except the 'delay' has been handled already so
|
||||
* this is where we do that actual work of process the screenshot
|
||||
* Save the captured screenshot to one of several destinations.
|
||||
*/
|
||||
function processScreenshotNow(args, document) {
|
||||
const reply = createScreenshotData(document, args);
|
||||
|
||||
const loadContext = document.defaultView
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebNavigation)
|
||||
.QueryInterface(Ci.nsILoadContext);
|
||||
|
||||
function saveScreenshot(args, context, reply) {
|
||||
const fileNeeded = args.filename != FILENAME_DEFAULT_VALUE ||
|
||||
(!args.imgur && !args.clipboard);
|
||||
|
||||
return Promise.all([
|
||||
args.clipboard ? saveToClipboard(loadContext, reply) : SKIP,
|
||||
args.imgur ? uploadToImgur(reply) : SKIP,
|
||||
fileNeeded ? saveToFile(loadContext, reply) : SKIP,
|
||||
args.clipboard ? saveToClipboard(context, reply) : SKIP,
|
||||
args.imgur ? uploadToImgur(reply) : SKIP,
|
||||
fileNeeded ? saveToFile(context, reply) : SKIP,
|
||||
]).then(() => reply);
|
||||
}
|
||||
|
||||
|
@ -300,13 +295,13 @@ function createScreenshotData(document, args) {
|
|||
window.scrollTo(currentX, currentY);
|
||||
}
|
||||
|
||||
return {
|
||||
return Promise.resolve({
|
||||
destinations: [],
|
||||
data: data,
|
||||
height: height,
|
||||
width: width,
|
||||
filename: getFilename(args.filename),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -339,8 +334,12 @@ function getFilename(defaultName) {
|
|||
* be treated exactly like imgur / file processing, but it's really sync
|
||||
* for now.
|
||||
*/
|
||||
function saveToClipboard(loadContext, reply) {
|
||||
function saveToClipboard(context, reply) {
|
||||
try {
|
||||
const loadContext = context.environment.chromeWindow
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebNavigation)
|
||||
.QueryInterface(Ci.nsILoadContext);
|
||||
const io = Cc["@mozilla.org/network/io-service;1"]
|
||||
.getService(Ci.nsIIOService);
|
||||
const channel = io.newChannel2(reply.data, null, null,
|
||||
|
@ -422,37 +421,21 @@ function uploadToImgur(reply) {
|
|||
* Save the screenshot data to disk, returning a promise which
|
||||
* is resolved on completion
|
||||
*/
|
||||
function saveToFile(loadContext, reply) {
|
||||
function saveToFile(context, reply) {
|
||||
return Task.spawn(function*() {
|
||||
try {
|
||||
let document = context.environment.chromeDocument;
|
||||
let window = context.environment.chromeWindow;
|
||||
|
||||
let filename = reply.filename;
|
||||
// Check there is a .png extension to filename
|
||||
if (!filename.match(/.png$/i)) {
|
||||
filename += ".png";
|
||||
}
|
||||
|
||||
// If the filename is relative, tack it onto the download directory
|
||||
if (!filename.match(/[\\\/]/)) {
|
||||
const preferredDir = yield Downloads.getPreferredDownloadsDirectory();
|
||||
filename = OS.Path.join(preferredDir, filename);
|
||||
reply.filename = filename;
|
||||
}
|
||||
|
||||
const file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
|
||||
file.initWithPath(filename);
|
||||
|
||||
const ioService = Cc["@mozilla.org/network/io-service;1"]
|
||||
.getService(Ci.nsIIOService);
|
||||
|
||||
const Persist = Ci.nsIWebBrowserPersist;
|
||||
const persist = Cc["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"]
|
||||
.createInstance(Persist);
|
||||
persist.persistFlags = Persist.PERSIST_FLAGS_REPLACE_EXISTING_FILES |
|
||||
Persist.PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION;
|
||||
|
||||
// TODO: UTF8? For an image?
|
||||
const source = ioService.newURI(reply.data, "UTF8", null);
|
||||
persist.saveURI(source, null, null, 0, null, null, file, loadContext);
|
||||
window.saveURL(reply.data, filename, null,
|
||||
true /* aShouldBypassCache */, true /* aSkipPrompt */,
|
||||
document.documentURIObject, document);
|
||||
|
||||
reply.destinations.push(l10n.lookup("screenshotSavedToFile") + " \"" + filename + "\"");
|
||||
}
|
||||
|
|
|
@ -100,7 +100,9 @@ const GcliActor = ActorClass({
|
|||
request: {
|
||||
customProps: Arg(0, "nullable:array:string")
|
||||
},
|
||||
response: RetVal("json")
|
||||
response: {
|
||||
value: RetVal("array:json")
|
||||
}
|
||||
}),
|
||||
|
||||
/**
|
||||
|
|
|
@ -45,7 +45,7 @@ this.AUSTLMY = {
|
|||
CHK_SHOWPROMPT_PREF: 3,
|
||||
// Incompatible add-on check disabled by preference (background download)
|
||||
CHK_ADDON_PREF_DISABLED: 4,
|
||||
// Incompatible add-on checke not performed due to same app version as the
|
||||
// Incompatible add-on check not performed due to same app version as the
|
||||
// update's app version (background download)
|
||||
CHK_ADDON_SAME_APP_VER: 5,
|
||||
// Incompatible add-ons found and all of them have updates (background download)
|
||||
|
|
|
@ -648,12 +648,11 @@ function getCanStageUpdates() {
|
|||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (AppConstants.platform == "win" && isServiceInstalled() &&
|
||||
shouldUseService()) {
|
||||
// No need to perform directory write checks, the maintenance service will
|
||||
// be able to write to all directories.
|
||||
LOG("getCanStageUpdates - able to stage updates because we'll use the service");
|
||||
LOG("getCanStageUpdates - able to stage updates using the service");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -661,7 +660,7 @@ function getCanStageUpdates() {
|
|||
// files into place.
|
||||
if (AppConstants.platform == "gonk") {
|
||||
LOG("getCanStageUpdates - able to stage updates because this is gonk");
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!hasUpdateMutex()) {
|
||||
|
|
|
@ -376,7 +376,8 @@ function run_test_pt11() {
|
|||
|
||||
function check_test_pt11() {
|
||||
let bestUpdate = gAUS.selectUpdate(gUpdates, gUpdateCount);
|
||||
Assert.ok(!bestUpdate);
|
||||
Assert.ok(!bestUpdate,
|
||||
"there should be no update available");
|
||||
run_test_pt12();
|
||||
}
|
||||
|
||||
|
@ -392,7 +393,8 @@ function run_test_pt12() {
|
|||
|
||||
function check_test_pt12() {
|
||||
let bestUpdate = gAUS.selectUpdate(gUpdates, gUpdateCount);
|
||||
Assert.ok(!!bestUpdate);
|
||||
Assert.ok(!!bestUpdate,
|
||||
"there should be one update available");
|
||||
Assert.equal(bestUpdate.displayVersion, "version 1.0",
|
||||
"the update displayVersion attribute" + MSG_SHOULD_EQUAL);
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче