зеркало из https://github.com/mozilla/gecko-dev.git
Merge fx-team to m-c. a=merge
This commit is contained in:
Коммит
50905e0270
|
@ -4,3 +4,4 @@
|
||||||
|
|
||||||
MOZ_APP_DISPLAYNAME=FirefoxDeveloperEdition
|
MOZ_APP_DISPLAYNAME=FirefoxDeveloperEdition
|
||||||
MOZ_APP_REMOTINGNAME=firefox-dev
|
MOZ_APP_REMOTINGNAME=firefox-dev
|
||||||
|
MOZ_DEV_EDITION=1
|
||||||
|
|
|
@ -417,6 +417,8 @@ loop.contacts = (function(_, mozL10n) {
|
||||||
},
|
},
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
|
let cx = React.addons.classSet;
|
||||||
|
|
||||||
let viewForItem = item => {
|
let viewForItem = item => {
|
||||||
return ContactDetail({key: item._guid, contact: item,
|
return ContactDetail({key: item._guid, contact: item,
|
||||||
handleContactAction: this.handleContactAction})
|
handleContactAction: this.handleContactAction})
|
||||||
|
@ -444,7 +446,6 @@ loop.contacts = (function(_, mozL10n) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: bug 1076767 - add a spinner whilst importing contacts.
|
|
||||||
return (
|
return (
|
||||||
React.DOM.div(null,
|
React.DOM.div(null,
|
||||||
React.DOM.div({className: "content-area"},
|
React.DOM.div({className: "content-area"},
|
||||||
|
@ -453,7 +454,11 @@ loop.contacts = (function(_, mozL10n) {
|
||||||
? mozL10n.get("importing_contacts_progress_button")
|
? mozL10n.get("importing_contacts_progress_button")
|
||||||
: mozL10n.get("import_contacts_button"),
|
: mozL10n.get("import_contacts_button"),
|
||||||
disabled: this.state.importBusy,
|
disabled: this.state.importBusy,
|
||||||
onClick: this.handleImportButtonClick}),
|
onClick: this.handleImportButtonClick},
|
||||||
|
React.DOM.div({className: cx({"contact-import-spinner": true,
|
||||||
|
spinner: true,
|
||||||
|
busy: this.state.importBusy})})
|
||||||
|
),
|
||||||
Button({caption: mozL10n.get("new_contact_button"),
|
Button({caption: mozL10n.get("new_contact_button"),
|
||||||
onClick: this.handleAddContactButtonClick})
|
onClick: this.handleAddContactButtonClick})
|
||||||
),
|
),
|
||||||
|
|
|
@ -417,6 +417,8 @@ loop.contacts = (function(_, mozL10n) {
|
||||||
},
|
},
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
|
let cx = React.addons.classSet;
|
||||||
|
|
||||||
let viewForItem = item => {
|
let viewForItem = item => {
|
||||||
return <ContactDetail key={item._guid} contact={item}
|
return <ContactDetail key={item._guid} contact={item}
|
||||||
handleContactAction={this.handleContactAction} />
|
handleContactAction={this.handleContactAction} />
|
||||||
|
@ -444,7 +446,6 @@ loop.contacts = (function(_, mozL10n) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: bug 1076767 - add a spinner whilst importing contacts.
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className="content-area">
|
<div className="content-area">
|
||||||
|
@ -453,7 +454,11 @@ loop.contacts = (function(_, mozL10n) {
|
||||||
? mozL10n.get("importing_contacts_progress_button")
|
? mozL10n.get("importing_contacts_progress_button")
|
||||||
: mozL10n.get("import_contacts_button")}
|
: mozL10n.get("import_contacts_button")}
|
||||||
disabled={this.state.importBusy}
|
disabled={this.state.importBusy}
|
||||||
onClick={this.handleImportButtonClick} />
|
onClick={this.handleImportButtonClick}>
|
||||||
|
<div className={cx({"contact-import-spinner": true,
|
||||||
|
spinner: true,
|
||||||
|
busy: this.state.importBusy})} />
|
||||||
|
</Button>
|
||||||
<Button caption={mozL10n.get("new_contact_button")}
|
<Button caption={mozL10n.get("new_contact_button")}
|
||||||
onClick={this.handleAddContactButtonClick} />
|
onClick={this.handleAddContactButtonClick} />
|
||||||
</ButtonGroup>
|
</ButtonGroup>
|
||||||
|
|
|
@ -18,7 +18,7 @@ loop.conversation = (function(mozL10n) {
|
||||||
|
|
||||||
var OutgoingConversationView = loop.conversationViews.OutgoingConversationView;
|
var OutgoingConversationView = loop.conversationViews.OutgoingConversationView;
|
||||||
var CallIdentifierView = loop.conversationViews.CallIdentifierView;
|
var CallIdentifierView = loop.conversationViews.CallIdentifierView;
|
||||||
var DesktopRoomControllerView = loop.roomViews.DesktopRoomControllerView;
|
var DesktopRoomConversationView = loop.roomViews.DesktopRoomConversationView;
|
||||||
|
|
||||||
var IncomingCallView = React.createClass({displayName: 'IncomingCallView',
|
var IncomingCallView = React.createClass({displayName: 'IncomingCallView',
|
||||||
mixins: [sharedMixins.DropdownMenuMixin, sharedMixins.AudioMixin],
|
mixins: [sharedMixins.DropdownMenuMixin, sharedMixins.AudioMixin],
|
||||||
|
@ -584,10 +584,10 @@ loop.conversation = (function(mozL10n) {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
case "room": {
|
case "room": {
|
||||||
return (DesktopRoomControllerView({
|
return (DesktopRoomConversationView({
|
||||||
mozLoop: navigator.mozLoop,
|
|
||||||
dispatcher: this.props.dispatcher,
|
dispatcher: this.props.dispatcher,
|
||||||
roomStore: this.props.roomStore}
|
roomStore: this.props.roomStore,
|
||||||
|
dispatcher: this.props.dispatcher}
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
case "failed": {
|
case "failed": {
|
||||||
|
@ -643,7 +643,8 @@ loop.conversation = (function(mozL10n) {
|
||||||
});
|
});
|
||||||
var activeRoomStore = new loop.store.ActiveRoomStore({
|
var activeRoomStore = new loop.store.ActiveRoomStore({
|
||||||
dispatcher: dispatcher,
|
dispatcher: dispatcher,
|
||||||
mozLoop: navigator.mozLoop
|
mozLoop: navigator.mozLoop,
|
||||||
|
sdkDriver: sdkDriver
|
||||||
});
|
});
|
||||||
var roomStore = new loop.store.RoomStore({
|
var roomStore = new loop.store.RoomStore({
|
||||||
dispatcher: dispatcher,
|
dispatcher: dispatcher,
|
||||||
|
|
|
@ -18,7 +18,7 @@ loop.conversation = (function(mozL10n) {
|
||||||
|
|
||||||
var OutgoingConversationView = loop.conversationViews.OutgoingConversationView;
|
var OutgoingConversationView = loop.conversationViews.OutgoingConversationView;
|
||||||
var CallIdentifierView = loop.conversationViews.CallIdentifierView;
|
var CallIdentifierView = loop.conversationViews.CallIdentifierView;
|
||||||
var DesktopRoomControllerView = loop.roomViews.DesktopRoomControllerView;
|
var DesktopRoomConversationView = loop.roomViews.DesktopRoomConversationView;
|
||||||
|
|
||||||
var IncomingCallView = React.createClass({
|
var IncomingCallView = React.createClass({
|
||||||
mixins: [sharedMixins.DropdownMenuMixin, sharedMixins.AudioMixin],
|
mixins: [sharedMixins.DropdownMenuMixin, sharedMixins.AudioMixin],
|
||||||
|
@ -584,10 +584,10 @@ loop.conversation = (function(mozL10n) {
|
||||||
/>);
|
/>);
|
||||||
}
|
}
|
||||||
case "room": {
|
case "room": {
|
||||||
return (<DesktopRoomControllerView
|
return (<DesktopRoomConversationView
|
||||||
mozLoop={navigator.mozLoop}
|
|
||||||
dispatcher={this.props.dispatcher}
|
dispatcher={this.props.dispatcher}
|
||||||
roomStore={this.props.roomStore}
|
roomStore={this.props.roomStore}
|
||||||
|
dispatcher={this.props.dispatcher}
|
||||||
/>);
|
/>);
|
||||||
}
|
}
|
||||||
case "failed": {
|
case "failed": {
|
||||||
|
@ -643,7 +643,8 @@ loop.conversation = (function(mozL10n) {
|
||||||
});
|
});
|
||||||
var activeRoomStore = new loop.store.ActiveRoomStore({
|
var activeRoomStore = new loop.store.ActiveRoomStore({
|
||||||
dispatcher: dispatcher,
|
dispatcher: dispatcher,
|
||||||
mozLoop: navigator.mozLoop
|
mozLoop: navigator.mozLoop,
|
||||||
|
sdkDriver: sdkDriver
|
||||||
});
|
});
|
||||||
var roomStore = new loop.store.RoomStore({
|
var roomStore = new loop.store.RoomStore({
|
||||||
dispatcher: dispatcher,
|
dispatcher: dispatcher,
|
||||||
|
|
|
@ -399,18 +399,21 @@ loop.panel = (function(_, mozL10n) {
|
||||||
// readOnly attr will suppress a warning regarding this issue
|
// readOnly attr will suppress a warning regarding this issue
|
||||||
// from the react lib.
|
// from the react lib.
|
||||||
var cx = React.addons.classSet;
|
var cx = React.addons.classSet;
|
||||||
var inputCSSClass = cx({
|
|
||||||
"pending": this.state.pending,
|
|
||||||
// Used in functional testing, signals that
|
|
||||||
// call url was received from loop server
|
|
||||||
"callUrl": !this.state.pending
|
|
||||||
});
|
|
||||||
return (
|
return (
|
||||||
React.DOM.div({className: "generate-url"},
|
React.DOM.div({className: "generate-url"},
|
||||||
React.DOM.header(null, __("share_link_header_text")),
|
React.DOM.header(null, __("share_link_header_text")),
|
||||||
React.DOM.input({type: "url", value: this.state.callUrl, readOnly: "true",
|
React.DOM.div({className: "generate-url-stack"},
|
||||||
onCopy: this.handleLinkExfiltration,
|
React.DOM.input({type: "url", value: this.state.callUrl, readOnly: "true",
|
||||||
className: inputCSSClass}),
|
onCopy: this.handleLinkExfiltration,
|
||||||
|
className: cx({"generate-url-input": true,
|
||||||
|
pending: this.state.pending,
|
||||||
|
// Used in functional testing, signals that
|
||||||
|
// call url was received from loop server
|
||||||
|
callUrl: !this.state.pending})}),
|
||||||
|
React.DOM.div({className: cx({"generate-url-spinner": true,
|
||||||
|
spinner: true,
|
||||||
|
busy: this.state.pending})})
|
||||||
|
),
|
||||||
ButtonGroup({additionalClass: "url-actions"},
|
ButtonGroup({additionalClass: "url-actions"},
|
||||||
Button({additionalClass: "button-email",
|
Button({additionalClass: "button-email",
|
||||||
disabled: !this.state.callUrl,
|
disabled: !this.state.callUrl,
|
||||||
|
|
|
@ -399,18 +399,21 @@ loop.panel = (function(_, mozL10n) {
|
||||||
// readOnly attr will suppress a warning regarding this issue
|
// readOnly attr will suppress a warning regarding this issue
|
||||||
// from the react lib.
|
// from the react lib.
|
||||||
var cx = React.addons.classSet;
|
var cx = React.addons.classSet;
|
||||||
var inputCSSClass = cx({
|
|
||||||
"pending": this.state.pending,
|
|
||||||
// Used in functional testing, signals that
|
|
||||||
// call url was received from loop server
|
|
||||||
"callUrl": !this.state.pending
|
|
||||||
});
|
|
||||||
return (
|
return (
|
||||||
<div className="generate-url">
|
<div className="generate-url">
|
||||||
<header>{__("share_link_header_text")}</header>
|
<header>{__("share_link_header_text")}</header>
|
||||||
<input type="url" value={this.state.callUrl} readOnly="true"
|
<div className="generate-url-stack">
|
||||||
onCopy={this.handleLinkExfiltration}
|
<input type="url" value={this.state.callUrl} readOnly="true"
|
||||||
className={inputCSSClass} />
|
onCopy={this.handleLinkExfiltration}
|
||||||
|
className={cx({"generate-url-input": true,
|
||||||
|
pending: this.state.pending,
|
||||||
|
// Used in functional testing, signals that
|
||||||
|
// call url was received from loop server
|
||||||
|
callUrl: !this.state.pending})} />
|
||||||
|
<div className={cx({"generate-url-spinner": true,
|
||||||
|
spinner: true,
|
||||||
|
busy: this.state.pending})} />
|
||||||
|
</div>
|
||||||
<ButtonGroup additionalClass="url-actions">
|
<ButtonGroup additionalClass="url-actions">
|
||||||
<Button additionalClass="button-email"
|
<Button additionalClass="button-email"
|
||||||
disabled={!this.state.callUrl}
|
disabled={!this.state.callUrl}
|
||||||
|
|
|
@ -11,6 +11,7 @@ var loop = loop || {};
|
||||||
loop.roomViews = (function(mozL10n) {
|
loop.roomViews = (function(mozL10n) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
var sharedActions = loop.shared.actions;
|
||||||
var ROOM_STATES = loop.store.ROOM_STATES;
|
var ROOM_STATES = loop.store.ROOM_STATES;
|
||||||
var sharedViews = loop.shared.views;
|
var sharedViews = loop.shared.views;
|
||||||
|
|
||||||
|
@ -37,11 +38,20 @@ loop.roomViews = (function(mozL10n) {
|
||||||
},
|
},
|
||||||
|
|
||||||
_onActiveRoomStateChanged: function() {
|
_onActiveRoomStateChanged: function() {
|
||||||
this.setState(this.props.roomStore.getStoreState("activeRoom"));
|
// Only update the state if we're mounted, to avoid the problem where
|
||||||
|
// stopListening doesn't nuke the active listeners during a event
|
||||||
|
// processing.
|
||||||
|
if (this.isMounted()) {
|
||||||
|
this.setState(this.props.roomStore.getStoreState("activeRoom"));
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
getInitialState: function() {
|
getInitialState: function() {
|
||||||
return this.props.roomStore.getStoreState("activeRoom");
|
var storeState = this.props.roomStore.getStoreState("activeRoom");
|
||||||
|
return _.extend(storeState, {
|
||||||
|
// Used by the UI showcase.
|
||||||
|
roomState: this.props.roomState || storeState.roomState
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -72,25 +82,22 @@ loop.roomViews = (function(mozL10n) {
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
return (
|
return (
|
||||||
React.DOM.div({className: "room-conversation-wrapper"},
|
React.DOM.div({className: "room-invitation-overlay"},
|
||||||
React.DOM.div({className: "room-invitation-overlay"},
|
React.DOM.form({onSubmit: this.handleFormSubmit},
|
||||||
React.DOM.form({onSubmit: this.handleFormSubmit},
|
React.DOM.input({type: "text", ref: "roomName",
|
||||||
React.DOM.input({type: "text", ref: "roomName",
|
placeholder: mozL10n.get("rooms_name_this_room_label")})
|
||||||
placeholder: mozL10n.get("rooms_name_this_room_label")})
|
|
||||||
),
|
|
||||||
React.DOM.p(null, mozL10n.get("invite_header_text")),
|
|
||||||
React.DOM.div({className: "btn-group call-action-group"},
|
|
||||||
React.DOM.button({className: "btn btn-info btn-email",
|
|
||||||
onClick: this.handleEmailButtonClick},
|
|
||||||
mozL10n.get("share_button2")
|
|
||||||
),
|
|
||||||
React.DOM.button({className: "btn btn-info btn-copy",
|
|
||||||
onClick: this.handleCopyButtonClick},
|
|
||||||
mozL10n.get("copy_url_button2")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
),
|
),
|
||||||
DesktopRoomConversationView({roomStore: this.props.roomStore})
|
React.DOM.p(null, mozL10n.get("invite_header_text")),
|
||||||
|
React.DOM.div({className: "btn-group call-action-group"},
|
||||||
|
React.DOM.button({className: "btn btn-info btn-email",
|
||||||
|
onClick: this.handleEmailButtonClick},
|
||||||
|
mozL10n.get("share_button2")
|
||||||
|
),
|
||||||
|
React.DOM.button({className: "btn btn-info btn-copy",
|
||||||
|
onClick: this.handleCopyButtonClick},
|
||||||
|
mozL10n.get("copy_url_button2")
|
||||||
|
)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -100,105 +107,150 @@ loop.roomViews = (function(mozL10n) {
|
||||||
* Desktop room conversation view.
|
* Desktop room conversation view.
|
||||||
*/
|
*/
|
||||||
var DesktopRoomConversationView = React.createClass({displayName: 'DesktopRoomConversationView',
|
var DesktopRoomConversationView = React.createClass({displayName: 'DesktopRoomConversationView',
|
||||||
mixins: [ActiveRoomStoreMixin],
|
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
|
|
||||||
video: React.PropTypes.object,
|
|
||||||
audio: React.PropTypes.object,
|
|
||||||
displayInvitation: React.PropTypes.bool
|
|
||||||
},
|
|
||||||
|
|
||||||
getDefaultProps: function() {
|
|
||||||
return {
|
|
||||||
video: {enabled: true, visible: true},
|
|
||||||
audio: {enabled: true, visible: true}
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
render: function() {
|
|
||||||
var localStreamClasses = React.addons.classSet({
|
|
||||||
local: true,
|
|
||||||
"local-stream": true,
|
|
||||||
"local-stream-audio": !this.props.video.enabled
|
|
||||||
});
|
|
||||||
return (
|
|
||||||
React.DOM.div({className: "room-conversation-wrapper"},
|
|
||||||
React.DOM.div({className: "video-layout-wrapper"},
|
|
||||||
React.DOM.div({className: "conversation room-conversation"},
|
|
||||||
React.DOM.div({className: "media nested"},
|
|
||||||
React.DOM.div({className: "video_wrapper remote_wrapper"},
|
|
||||||
React.DOM.div({className: "video_inner remote"})
|
|
||||||
),
|
|
||||||
React.DOM.div({className: localStreamClasses})
|
|
||||||
),
|
|
||||||
sharedViews.ConversationToolbar({
|
|
||||||
video: this.props.video,
|
|
||||||
audio: this.props.audio,
|
|
||||||
publishStream: noop,
|
|
||||||
hangup: noop})
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Desktop room controller view.
|
|
||||||
*/
|
|
||||||
var DesktopRoomControllerView = React.createClass({displayName: 'DesktopRoomControllerView',
|
|
||||||
mixins: [ActiveRoomStoreMixin, loop.shared.mixins.DocumentTitleMixin],
|
mixins: [ActiveRoomStoreMixin, loop.shared.mixins.DocumentTitleMixin],
|
||||||
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired
|
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_renderInvitationOverlay: function() {
|
||||||
|
if (this.state.roomState !== ROOM_STATES.HAS_PARTICIPANTS) {
|
||||||
|
return DesktopRoomInvitationView({
|
||||||
|
roomStore: this.props.roomStore,
|
||||||
|
dispatcher: this.props.dispatcher}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
|
||||||
|
componentDidMount: function() {
|
||||||
|
/**
|
||||||
|
* OT inserts inline styles into the markup. Using a listener for
|
||||||
|
* resize events helps us trigger a full width/height on the element
|
||||||
|
* so that they update to the correct dimensions.
|
||||||
|
* XXX: this should be factored as a mixin.
|
||||||
|
*/
|
||||||
|
window.addEventListener('orientationchange', this.updateVideoContainer);
|
||||||
|
window.addEventListener('resize', this.updateVideoContainer);
|
||||||
|
|
||||||
|
// The SDK needs to know about the configuration and the elements to use
|
||||||
|
// for display. So the best way seems to pass the information here - ideally
|
||||||
|
// the sdk wouldn't need to know this, but we can't change that.
|
||||||
|
this.props.dispatcher.dispatch(new sharedActions.SetupStreamElements({
|
||||||
|
publisherConfig: this._getPublisherConfig(),
|
||||||
|
getLocalElementFunc: this._getElement.bind(this, ".local"),
|
||||||
|
getRemoteElementFunc: this._getElement.bind(this, ".remote")
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
_getPublisherConfig: function() {
|
||||||
|
// height set to 100%" to fix video layout on Google Chrome
|
||||||
|
// @see https://bugzilla.mozilla.org/show_bug.cgi?id=1020445
|
||||||
|
return {
|
||||||
|
insertMode: "append",
|
||||||
|
width: "100%",
|
||||||
|
height: "100%",
|
||||||
|
publishVideo: !this.state.videoMuted,
|
||||||
|
style: {
|
||||||
|
audioLevelDisplayMode: "off",
|
||||||
|
bugDisplayMode: "off",
|
||||||
|
buttonDisplayMode: "off",
|
||||||
|
nameDisplayMode: "off",
|
||||||
|
videoDisabledDisplayMode: "off"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to update the video container whenever the orientation or size of the
|
||||||
|
* display area changes.
|
||||||
|
*/
|
||||||
|
updateVideoContainer: function() {
|
||||||
|
var localStreamParent = this._getElement('.local .OT_publisher');
|
||||||
|
var remoteStreamParent = this._getElement('.remote .OT_subscriber');
|
||||||
|
if (localStreamParent) {
|
||||||
|
localStreamParent.style.width = "100%";
|
||||||
|
}
|
||||||
|
if (remoteStreamParent) {
|
||||||
|
remoteStreamParent.style.height = "100%";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns either the required DOMNode
|
||||||
|
*
|
||||||
|
* @param {String} className The name of the class to get the element for.
|
||||||
|
*/
|
||||||
|
_getElement: function(className) {
|
||||||
|
return this.getDOMNode().querySelector(className);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes the window if the cancel button is pressed in the generic failure view.
|
||||||
|
*/
|
||||||
closeWindow: function() {
|
closeWindow: function() {
|
||||||
window.close();
|
window.close();
|
||||||
},
|
},
|
||||||
|
|
||||||
_renderRoomView: function(roomState) {
|
/**
|
||||||
switch (roomState) {
|
* Used to control publishing a stream - i.e. to mute a stream
|
||||||
case ROOM_STATES.FAILED: {
|
*
|
||||||
return loop.conversation.GenericFailureView({
|
* @param {String} type The type of stream, e.g. "audio" or "video".
|
||||||
cancelCall: this.closeWindow}
|
* @param {Boolean} enabled True to enable the stream, false otherwise.
|
||||||
);
|
*/
|
||||||
}
|
publishStream: function(type, enabled) {
|
||||||
case ROOM_STATES.INIT:
|
this.props.dispatcher.dispatch(
|
||||||
case ROOM_STATES.GATHER:
|
new sharedActions.SetMute({
|
||||||
case ROOM_STATES.READY:
|
type: type,
|
||||||
case ROOM_STATES.JOINED: {
|
enabled: enabled
|
||||||
return DesktopRoomInvitationView({
|
}));
|
||||||
dispatcher: this.props.dispatcher,
|
|
||||||
roomStore: this.props.roomStore}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
// XXX needs bug 1074686/1074702
|
|
||||||
case ROOM_STATES.HAS_PARTICIPANTS: {
|
|
||||||
return DesktopRoomConversationView({
|
|
||||||
dispatcher: this.props.dispatcher,
|
|
||||||
roomStore: this.props.roomStore}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
if (this.state.roomName) {
|
if (this.state.roomName) {
|
||||||
this.setTitle(this.state.roomName);
|
this.setTitle(this.state.roomName);
|
||||||
}
|
}
|
||||||
return (
|
|
||||||
React.DOM.div({className: "room-conversation-wrapper"},
|
var localStreamClasses = React.addons.classSet({
|
||||||
this._renderRoomView(this.state.roomState)
|
local: true,
|
||||||
)
|
"local-stream": true,
|
||||||
);
|
"local-stream-audio": !this.state.videoMuted
|
||||||
|
});
|
||||||
|
|
||||||
|
switch(this.state.roomState) {
|
||||||
|
case ROOM_STATES.FAILED: {
|
||||||
|
return loop.conversation.GenericFailureView({
|
||||||
|
cancelCall: this.closeWindow}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
return (
|
||||||
|
React.DOM.div({className: "room-conversation-wrapper"},
|
||||||
|
this._renderInvitationOverlay(),
|
||||||
|
React.DOM.div({className: "video-layout-wrapper"},
|
||||||
|
React.DOM.div({className: "conversation room-conversation"},
|
||||||
|
React.DOM.div({className: "media nested"},
|
||||||
|
React.DOM.div({className: "video_wrapper remote_wrapper"},
|
||||||
|
React.DOM.div({className: "video_inner remote"})
|
||||||
|
),
|
||||||
|
React.DOM.div({className: localStreamClasses})
|
||||||
|
),
|
||||||
|
sharedViews.ConversationToolbar({
|
||||||
|
video: {enabled: !this.state.videoMuted, visible: true},
|
||||||
|
audio: {enabled: !this.state.audioMuted, visible: true},
|
||||||
|
publishStream: this.publishStream,
|
||||||
|
hangup: noop})
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
ActiveRoomStoreMixin: ActiveRoomStoreMixin,
|
ActiveRoomStoreMixin: ActiveRoomStoreMixin,
|
||||||
DesktopRoomControllerView: DesktopRoomControllerView,
|
|
||||||
DesktopRoomConversationView: DesktopRoomConversationView,
|
DesktopRoomConversationView: DesktopRoomConversationView,
|
||||||
DesktopRoomInvitationView: DesktopRoomInvitationView
|
DesktopRoomInvitationView: DesktopRoomInvitationView
|
||||||
};
|
};
|
||||||
|
|
|
@ -11,6 +11,7 @@ var loop = loop || {};
|
||||||
loop.roomViews = (function(mozL10n) {
|
loop.roomViews = (function(mozL10n) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
var sharedActions = loop.shared.actions;
|
||||||
var ROOM_STATES = loop.store.ROOM_STATES;
|
var ROOM_STATES = loop.store.ROOM_STATES;
|
||||||
var sharedViews = loop.shared.views;
|
var sharedViews = loop.shared.views;
|
||||||
|
|
||||||
|
@ -37,11 +38,20 @@ loop.roomViews = (function(mozL10n) {
|
||||||
},
|
},
|
||||||
|
|
||||||
_onActiveRoomStateChanged: function() {
|
_onActiveRoomStateChanged: function() {
|
||||||
this.setState(this.props.roomStore.getStoreState("activeRoom"));
|
// Only update the state if we're mounted, to avoid the problem where
|
||||||
|
// stopListening doesn't nuke the active listeners during a event
|
||||||
|
// processing.
|
||||||
|
if (this.isMounted()) {
|
||||||
|
this.setState(this.props.roomStore.getStoreState("activeRoom"));
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
getInitialState: function() {
|
getInitialState: function() {
|
||||||
return this.props.roomStore.getStoreState("activeRoom");
|
var storeState = this.props.roomStore.getStoreState("activeRoom");
|
||||||
|
return _.extend(storeState, {
|
||||||
|
// Used by the UI showcase.
|
||||||
|
roomState: this.props.roomState || storeState.roomState
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -72,25 +82,22 @@ loop.roomViews = (function(mozL10n) {
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
return (
|
return (
|
||||||
<div className="room-conversation-wrapper">
|
<div className="room-invitation-overlay">
|
||||||
<div className="room-invitation-overlay">
|
<form onSubmit={this.handleFormSubmit}>
|
||||||
<form onSubmit={this.handleFormSubmit}>
|
<input type="text" ref="roomName"
|
||||||
<input type="text" ref="roomName"
|
placeholder={mozL10n.get("rooms_name_this_room_label")} />
|
||||||
placeholder={mozL10n.get("rooms_name_this_room_label")} />
|
</form>
|
||||||
</form>
|
<p>{mozL10n.get("invite_header_text")}</p>
|
||||||
<p>{mozL10n.get("invite_header_text")}</p>
|
<div className="btn-group call-action-group">
|
||||||
<div className="btn-group call-action-group">
|
<button className="btn btn-info btn-email"
|
||||||
<button className="btn btn-info btn-email"
|
onClick={this.handleEmailButtonClick}>
|
||||||
onClick={this.handleEmailButtonClick}>
|
{mozL10n.get("share_button2")}
|
||||||
{mozL10n.get("share_button2")}
|
</button>
|
||||||
</button>
|
<button className="btn btn-info btn-copy"
|
||||||
<button className="btn btn-info btn-copy"
|
onClick={this.handleCopyButtonClick}>
|
||||||
onClick={this.handleCopyButtonClick}>
|
{mozL10n.get("copy_url_button2")}
|
||||||
{mozL10n.get("copy_url_button2")}
|
</button>
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<DesktopRoomConversationView roomStore={this.props.roomStore} />
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -100,105 +107,150 @@ loop.roomViews = (function(mozL10n) {
|
||||||
* Desktop room conversation view.
|
* Desktop room conversation view.
|
||||||
*/
|
*/
|
||||||
var DesktopRoomConversationView = React.createClass({
|
var DesktopRoomConversationView = React.createClass({
|
||||||
mixins: [ActiveRoomStoreMixin],
|
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
|
|
||||||
video: React.PropTypes.object,
|
|
||||||
audio: React.PropTypes.object,
|
|
||||||
displayInvitation: React.PropTypes.bool
|
|
||||||
},
|
|
||||||
|
|
||||||
getDefaultProps: function() {
|
|
||||||
return {
|
|
||||||
video: {enabled: true, visible: true},
|
|
||||||
audio: {enabled: true, visible: true}
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
render: function() {
|
|
||||||
var localStreamClasses = React.addons.classSet({
|
|
||||||
local: true,
|
|
||||||
"local-stream": true,
|
|
||||||
"local-stream-audio": !this.props.video.enabled
|
|
||||||
});
|
|
||||||
return (
|
|
||||||
<div className="room-conversation-wrapper">
|
|
||||||
<div className="video-layout-wrapper">
|
|
||||||
<div className="conversation room-conversation">
|
|
||||||
<div className="media nested">
|
|
||||||
<div className="video_wrapper remote_wrapper">
|
|
||||||
<div className="video_inner remote"></div>
|
|
||||||
</div>
|
|
||||||
<div className={localStreamClasses}></div>
|
|
||||||
</div>
|
|
||||||
<sharedViews.ConversationToolbar
|
|
||||||
video={this.props.video}
|
|
||||||
audio={this.props.audio}
|
|
||||||
publishStream={noop}
|
|
||||||
hangup={noop} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Desktop room controller view.
|
|
||||||
*/
|
|
||||||
var DesktopRoomControllerView = React.createClass({
|
|
||||||
mixins: [ActiveRoomStoreMixin, loop.shared.mixins.DocumentTitleMixin],
|
mixins: [ActiveRoomStoreMixin, loop.shared.mixins.DocumentTitleMixin],
|
||||||
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired
|
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_renderInvitationOverlay: function() {
|
||||||
|
if (this.state.roomState !== ROOM_STATES.HAS_PARTICIPANTS) {
|
||||||
|
return <DesktopRoomInvitationView
|
||||||
|
roomStore={this.props.roomStore}
|
||||||
|
dispatcher={this.props.dispatcher}
|
||||||
|
/>;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
|
||||||
|
componentDidMount: function() {
|
||||||
|
/**
|
||||||
|
* OT inserts inline styles into the markup. Using a listener for
|
||||||
|
* resize events helps us trigger a full width/height on the element
|
||||||
|
* so that they update to the correct dimensions.
|
||||||
|
* XXX: this should be factored as a mixin.
|
||||||
|
*/
|
||||||
|
window.addEventListener('orientationchange', this.updateVideoContainer);
|
||||||
|
window.addEventListener('resize', this.updateVideoContainer);
|
||||||
|
|
||||||
|
// The SDK needs to know about the configuration and the elements to use
|
||||||
|
// for display. So the best way seems to pass the information here - ideally
|
||||||
|
// the sdk wouldn't need to know this, but we can't change that.
|
||||||
|
this.props.dispatcher.dispatch(new sharedActions.SetupStreamElements({
|
||||||
|
publisherConfig: this._getPublisherConfig(),
|
||||||
|
getLocalElementFunc: this._getElement.bind(this, ".local"),
|
||||||
|
getRemoteElementFunc: this._getElement.bind(this, ".remote")
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
_getPublisherConfig: function() {
|
||||||
|
// height set to 100%" to fix video layout on Google Chrome
|
||||||
|
// @see https://bugzilla.mozilla.org/show_bug.cgi?id=1020445
|
||||||
|
return {
|
||||||
|
insertMode: "append",
|
||||||
|
width: "100%",
|
||||||
|
height: "100%",
|
||||||
|
publishVideo: !this.state.videoMuted,
|
||||||
|
style: {
|
||||||
|
audioLevelDisplayMode: "off",
|
||||||
|
bugDisplayMode: "off",
|
||||||
|
buttonDisplayMode: "off",
|
||||||
|
nameDisplayMode: "off",
|
||||||
|
videoDisabledDisplayMode: "off"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to update the video container whenever the orientation or size of the
|
||||||
|
* display area changes.
|
||||||
|
*/
|
||||||
|
updateVideoContainer: function() {
|
||||||
|
var localStreamParent = this._getElement('.local .OT_publisher');
|
||||||
|
var remoteStreamParent = this._getElement('.remote .OT_subscriber');
|
||||||
|
if (localStreamParent) {
|
||||||
|
localStreamParent.style.width = "100%";
|
||||||
|
}
|
||||||
|
if (remoteStreamParent) {
|
||||||
|
remoteStreamParent.style.height = "100%";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns either the required DOMNode
|
||||||
|
*
|
||||||
|
* @param {String} className The name of the class to get the element for.
|
||||||
|
*/
|
||||||
|
_getElement: function(className) {
|
||||||
|
return this.getDOMNode().querySelector(className);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes the window if the cancel button is pressed in the generic failure view.
|
||||||
|
*/
|
||||||
closeWindow: function() {
|
closeWindow: function() {
|
||||||
window.close();
|
window.close();
|
||||||
},
|
},
|
||||||
|
|
||||||
_renderRoomView: function(roomState) {
|
/**
|
||||||
switch (roomState) {
|
* Used to control publishing a stream - i.e. to mute a stream
|
||||||
case ROOM_STATES.FAILED: {
|
*
|
||||||
return <loop.conversation.GenericFailureView
|
* @param {String} type The type of stream, e.g. "audio" or "video".
|
||||||
cancelCall={this.closeWindow}
|
* @param {Boolean} enabled True to enable the stream, false otherwise.
|
||||||
/>;
|
*/
|
||||||
}
|
publishStream: function(type, enabled) {
|
||||||
case ROOM_STATES.INIT:
|
this.props.dispatcher.dispatch(
|
||||||
case ROOM_STATES.GATHER:
|
new sharedActions.SetMute({
|
||||||
case ROOM_STATES.READY:
|
type: type,
|
||||||
case ROOM_STATES.JOINED: {
|
enabled: enabled
|
||||||
return <DesktopRoomInvitationView
|
}));
|
||||||
dispatcher={this.props.dispatcher}
|
|
||||||
roomStore={this.props.roomStore}
|
|
||||||
/>;
|
|
||||||
}
|
|
||||||
// XXX needs bug 1074686/1074702
|
|
||||||
case ROOM_STATES.HAS_PARTICIPANTS: {
|
|
||||||
return <DesktopRoomConversationView
|
|
||||||
dispatcher={this.props.dispatcher}
|
|
||||||
roomStore={this.props.roomStore}
|
|
||||||
/>;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
if (this.state.roomName) {
|
if (this.state.roomName) {
|
||||||
this.setTitle(this.state.roomName);
|
this.setTitle(this.state.roomName);
|
||||||
}
|
}
|
||||||
return (
|
|
||||||
<div className="room-conversation-wrapper">{
|
var localStreamClasses = React.addons.classSet({
|
||||||
this._renderRoomView(this.state.roomState)
|
local: true,
|
||||||
}</div>
|
"local-stream": true,
|
||||||
);
|
"local-stream-audio": !this.state.videoMuted
|
||||||
|
});
|
||||||
|
|
||||||
|
switch(this.state.roomState) {
|
||||||
|
case ROOM_STATES.FAILED: {
|
||||||
|
return <loop.conversation.GenericFailureView
|
||||||
|
cancelCall={this.closeWindow}
|
||||||
|
/>;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
return (
|
||||||
|
<div className="room-conversation-wrapper">
|
||||||
|
{this._renderInvitationOverlay()}
|
||||||
|
<div className="video-layout-wrapper">
|
||||||
|
<div className="conversation room-conversation">
|
||||||
|
<div className="media nested">
|
||||||
|
<div className="video_wrapper remote_wrapper">
|
||||||
|
<div className="video_inner remote"></div>
|
||||||
|
</div>
|
||||||
|
<div className={localStreamClasses}></div>
|
||||||
|
</div>
|
||||||
|
<sharedViews.ConversationToolbar
|
||||||
|
video={{enabled: !this.state.videoMuted, visible: true}}
|
||||||
|
audio={{enabled: !this.state.audioMuted, visible: true}}
|
||||||
|
publishStream={this.publishStream}
|
||||||
|
hangup={noop} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
ActiveRoomStoreMixin: ActiveRoomStoreMixin,
|
ActiveRoomStoreMixin: ActiveRoomStoreMixin,
|
||||||
DesktopRoomControllerView: DesktopRoomControllerView,
|
|
||||||
DesktopRoomConversationView: DesktopRoomConversationView,
|
DesktopRoomConversationView: DesktopRoomConversationView,
|
||||||
DesktopRoomInvitationView: DesktopRoomInvitationView
|
DesktopRoomInvitationView: DesktopRoomInvitationView
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,6 +2,16 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
.contact-import-spinner {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-import-spinner.busy {
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
-moz-margin-start: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
.content-area input.contact-filter {
|
.content-area input.contact-filter {
|
||||||
margin-top: 14px;
|
margin-top: 14px;
|
||||||
border-radius: 10000px;
|
border-radius: 10000px;
|
||||||
|
|
|
@ -46,7 +46,7 @@ body {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
color: #ccc;
|
color: #ccc;
|
||||||
border-right: 1px solid #ccc;
|
-moz-border-end: 1px solid #ccc;
|
||||||
padding: 0 10px;
|
padding: 0 10px;
|
||||||
height: 16px;
|
height: 16px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
@ -56,7 +56,7 @@ body {
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab-view > li:last-child {
|
.tab-view > li:last-child {
|
||||||
border-right-style: none;
|
-moz-border-end-style: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab-view > li[data-tab-name="call"],
|
.tab-view > li[data-tab-name="call"],
|
||||||
|
@ -307,6 +307,10 @@ body {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.button > .button-caption {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
.button:hover {
|
.button:hover {
|
||||||
background-color: #ebebeb;
|
background-color: #ebebeb;
|
||||||
}
|
}
|
||||||
|
@ -372,10 +376,41 @@ body[dir=rtl] .dropdown-menu-item {
|
||||||
background-color: #eee;
|
background-color: #eee;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Spinner */
|
||||||
|
|
||||||
|
@keyframes spinnerRotate {
|
||||||
|
to { transform: rotate(360deg); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.spinner {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: 16px 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spinner.busy {
|
||||||
|
background-image: url(../img/spinner.png);
|
||||||
|
animation-name: spinnerRotate;
|
||||||
|
animation-duration: 1s;
|
||||||
|
animation-timing-function: linear;
|
||||||
|
animation-iteration-count: infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-resolution: 2dppx) {
|
||||||
|
.spinner.busy {
|
||||||
|
background-image: url(../img/spinner@2x.png);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Share tab */
|
/* Share tab */
|
||||||
|
|
||||||
.generate-url input {
|
.generate-url-stack {
|
||||||
margin: 14px 0;
|
margin: 14px 0;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.generate-url-input {
|
||||||
outline: 0;
|
outline: 0;
|
||||||
border: 1px solid #ccc; /* Overriding background style for a text input (see
|
border: 1px solid #ccc; /* Overriding background style for a text input (see
|
||||||
below) resets its borders to a weird beveled style;
|
below) resets its borders to a weird beveled style;
|
||||||
|
@ -386,10 +421,18 @@ body[dir=rtl] .dropdown-menu-item {
|
||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.generate-url input.pending {
|
.generate-url-spinner {
|
||||||
background-image: url(../img/loading-icon.gif);
|
position: absolute;
|
||||||
background-repeat: no-repeat;
|
pointer-events: none;
|
||||||
background-position: right;
|
z-index: 1;
|
||||||
|
top: 4px;
|
||||||
|
left: auto;
|
||||||
|
right: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
body[dir=rtl] .generate-url-spinner {
|
||||||
|
left: 4px;
|
||||||
|
right: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.generate-url .button {
|
.generate-url .button {
|
||||||
|
|
Двоичные данные
browser/components/loop/content/shared/img/loading-icon.gif
Двоичные данные
browser/components/loop/content/shared/img/loading-icon.gif
Двоичный файл не отображается.
До Ширина: | Высота: | Размер: 2.5 KiB |
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 724 B |
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 1.8 KiB |
|
@ -109,9 +109,12 @@ loop.shared.actions = (function() {
|
||||||
}),
|
}),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used to indicate the peer hung up the call.
|
* Used to indicate the remote peer was disconnected for some reason.
|
||||||
|
*
|
||||||
|
* peerHungup is true if the peer intentionally disconnected, false otherwise.
|
||||||
*/
|
*/
|
||||||
PeerHungupCall: Action.define("peerHungupCall", {
|
RemotePeerDisconnected: Action.define("remotePeerDisconnected", {
|
||||||
|
peerHungup: Boolean
|
||||||
}),
|
}),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -132,6 +135,18 @@ loop.shared.actions = (function() {
|
||||||
reason: String
|
reason: String
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to notify that the sdk session is now connected to the servers.
|
||||||
|
*/
|
||||||
|
ConnectedToSdkServers: Action.define("connectedToSdkServers", {
|
||||||
|
}),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to notify that a remote peer has connected to the room.
|
||||||
|
*/
|
||||||
|
RemotePeerConnected: Action.define("remotePeerConnected", {
|
||||||
|
}),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used by the ongoing views to notify stores about the elements
|
* Used by the ongoing views to notify stores about the elements
|
||||||
* required for the sdk.
|
* required for the sdk.
|
||||||
|
|
|
@ -20,10 +20,12 @@ loop.store.ActiveRoomStore = (function() {
|
||||||
READY: "room-ready",
|
READY: "room-ready",
|
||||||
// The room is known to be joined on the loop-server
|
// The room is known to be joined on the loop-server
|
||||||
JOINED: "room-joined",
|
JOINED: "room-joined",
|
||||||
|
// The room is connected to the sdk server.
|
||||||
|
SESSION_CONNECTED: "room-session-connected",
|
||||||
|
// There are participants in the room.
|
||||||
|
HAS_PARTICIPANTS: "room-has-participants",
|
||||||
// There was an issue with the room
|
// There was an issue with the room
|
||||||
FAILED: "room-failed",
|
FAILED: "room-failed"
|
||||||
// XXX to be implemented in bug 1074686/1074702
|
|
||||||
HAS_PARTICIPANTS: "room-has-participants"
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -51,15 +53,18 @@ loop.store.ActiveRoomStore = (function() {
|
||||||
}
|
}
|
||||||
this._mozLoop = options.mozLoop;
|
this._mozLoop = options.mozLoop;
|
||||||
|
|
||||||
|
if (!options.sdkDriver) {
|
||||||
|
throw new Error("Missing option sdkDriver");
|
||||||
|
}
|
||||||
|
this._sdkDriver = options.sdkDriver;
|
||||||
|
|
||||||
|
// XXX Further actions are registered in setupWindowData and
|
||||||
|
// fetchServerData when we know what window type this is. At some stage,
|
||||||
|
// we might want to consider store mixins or some alternative which
|
||||||
|
// means the stores would only be created when we want them.
|
||||||
this._dispatcher.register(this, [
|
this._dispatcher.register(this, [
|
||||||
"roomFailure",
|
|
||||||
"setupWindowData",
|
"setupWindowData",
|
||||||
"fetchServerData",
|
"fetchServerData"
|
||||||
"updateRoomInfo",
|
|
||||||
"joinRoom",
|
|
||||||
"joinedRoom",
|
|
||||||
"windowUnload",
|
|
||||||
"leaveRoom"
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -75,7 +80,9 @@ loop.store.ActiveRoomStore = (function() {
|
||||||
* otherwise it will be unset.
|
* otherwise it will be unset.
|
||||||
*/
|
*/
|
||||||
this._storeState = {
|
this._storeState = {
|
||||||
roomState: ROOM_STATES.INIT
|
roomState: ROOM_STATES.INIT,
|
||||||
|
audioMuted: false,
|
||||||
|
videoMuted: false
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,6 +120,26 @@ loop.store.ActiveRoomStore = (function() {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers the actions with the dispatcher that this store is interested
|
||||||
|
* in.
|
||||||
|
*/
|
||||||
|
_registerActions: function() {
|
||||||
|
this._dispatcher.register(this, [
|
||||||
|
"roomFailure",
|
||||||
|
"updateRoomInfo",
|
||||||
|
"joinRoom",
|
||||||
|
"joinedRoom",
|
||||||
|
"connectedToSdkServers",
|
||||||
|
"connectionFailure",
|
||||||
|
"setMute",
|
||||||
|
"remotePeerDisconnected",
|
||||||
|
"remotePeerConnected",
|
||||||
|
"windowUnload",
|
||||||
|
"leaveRoom"
|
||||||
|
]);
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute setupWindowData event action from the dispatcher. This gets
|
* Execute setupWindowData event action from the dispatcher. This gets
|
||||||
* the room data from the mozLoop api, and dispatches an UpdateRoomInfo event.
|
* the room data from the mozLoop api, and dispatches an UpdateRoomInfo event.
|
||||||
|
@ -127,6 +154,8 @@ loop.store.ActiveRoomStore = (function() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._registerActions();
|
||||||
|
|
||||||
this.setStoreState({
|
this.setStoreState({
|
||||||
roomState: ROOM_STATES.GATHER
|
roomState: ROOM_STATES.GATHER
|
||||||
});
|
});
|
||||||
|
@ -169,6 +198,8 @@ loop.store.ActiveRoomStore = (function() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._registerActions();
|
||||||
|
|
||||||
this.setStoreState({
|
this.setStoreState({
|
||||||
roomToken: actionData.token,
|
roomToken: actionData.token,
|
||||||
roomState: ROOM_STATES.READY
|
roomState: ROOM_STATES.READY
|
||||||
|
@ -228,6 +259,57 @@ loop.store.ActiveRoomStore = (function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
this._setRefreshTimeout(actionData.expires);
|
this._setRefreshTimeout(actionData.expires);
|
||||||
|
this._sdkDriver.connectSession(actionData);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles recording when the sdk has connected to the servers.
|
||||||
|
*/
|
||||||
|
connectedToSdkServers: function() {
|
||||||
|
this.setStoreState({
|
||||||
|
roomState: ROOM_STATES.SESSION_CONNECTED
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles disconnection of this local client from the sdk servers.
|
||||||
|
*/
|
||||||
|
connectionFailure: function() {
|
||||||
|
// Treat all reasons as something failed. In theory, clientDisconnected
|
||||||
|
// could be a success case, but there's no way we should be intentionally
|
||||||
|
// sending that and still have the window open.
|
||||||
|
this._leaveRoom(ROOM_STATES.FAILED);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Records the mute state for the stream.
|
||||||
|
*
|
||||||
|
* @param {sharedActions.setMute} actionData The mute state for the stream type.
|
||||||
|
*/
|
||||||
|
setMute: function(actionData) {
|
||||||
|
var muteState = {};
|
||||||
|
muteState[actionData.type + "Muted"] = !actionData.enabled;
|
||||||
|
this.setStoreState(muteState);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles recording when a remote peer has connected to the servers.
|
||||||
|
*/
|
||||||
|
remotePeerConnected: function() {
|
||||||
|
this.setStoreState({
|
||||||
|
roomState: ROOM_STATES.HAS_PARTICIPANTS
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles a remote peer disconnecting from the session.
|
||||||
|
*/
|
||||||
|
remotePeerDisconnected: function() {
|
||||||
|
// As we only support two users at the moment, we just set this
|
||||||
|
// back to joined.
|
||||||
|
this.setStoreState({
|
||||||
|
roomState: ROOM_STATES.SESSION_CONNECTED
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -275,22 +357,27 @@ loop.store.ActiveRoomStore = (function() {
|
||||||
/**
|
/**
|
||||||
* Handles leaving a room. Clears any membership timeouts, then
|
* Handles leaving a room. Clears any membership timeouts, then
|
||||||
* signals to the server the leave of the room.
|
* signals to the server the leave of the room.
|
||||||
|
*
|
||||||
|
* @param {ROOM_STATES} nextState Optional; the next state to switch to.
|
||||||
|
* Switches to READY if undefined.
|
||||||
*/
|
*/
|
||||||
_leaveRoom: function() {
|
_leaveRoom: function(nextState) {
|
||||||
if (this._storeState.roomState !== ROOM_STATES.JOINED) {
|
this._sdkDriver.disconnectSession();
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this._timeout) {
|
if (this._timeout) {
|
||||||
clearTimeout(this._timeout);
|
clearTimeout(this._timeout);
|
||||||
delete this._timeout;
|
delete this._timeout;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._mozLoop.rooms.leave(this._storeState.roomToken,
|
if (this._storeState.roomState === ROOM_STATES.JOINED ||
|
||||||
this._storeState.sessionToken);
|
this._storeState.roomState === ROOM_STATES.SESSION_CONNECTED ||
|
||||||
|
this._storeState.roomState === ROOM_STATES.HAS_PARTICIPANTS) {
|
||||||
|
this._mozLoop.rooms.leave(this._storeState.roomToken,
|
||||||
|
this._storeState.sessionToken);
|
||||||
|
}
|
||||||
|
|
||||||
this.setStoreState({
|
this.setStoreState({
|
||||||
roomState: ROOM_STATES.READY
|
roomState: nextState ? nextState : ROOM_STATES.READY
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -118,18 +118,12 @@ loop.store.ConversationStore = (function() {
|
||||||
this.dispatcher = options.dispatcher;
|
this.dispatcher = options.dispatcher;
|
||||||
this.sdkDriver = options.sdkDriver;
|
this.sdkDriver = options.sdkDriver;
|
||||||
|
|
||||||
|
// XXX Further actions are registered in setupWindowData when
|
||||||
|
// we know what window type this is. At some stage, we might want to
|
||||||
|
// consider store mixins or some alternative which means the stores
|
||||||
|
// would only be created when we want them.
|
||||||
this.dispatcher.register(this, [
|
this.dispatcher.register(this, [
|
||||||
"connectionFailure",
|
"setupWindowData"
|
||||||
"connectionProgress",
|
|
||||||
"setupWindowData",
|
|
||||||
"connectCall",
|
|
||||||
"hangupCall",
|
|
||||||
"peerHungupCall",
|
|
||||||
"cancelCall",
|
|
||||||
"retryCall",
|
|
||||||
"mediaConnected",
|
|
||||||
"setMute",
|
|
||||||
"fetchEmailLink"
|
|
||||||
]);
|
]);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -196,6 +190,19 @@ loop.store.ConversationStore = (function() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.dispatcher.register(this, [
|
||||||
|
"connectionFailure",
|
||||||
|
"connectionProgress",
|
||||||
|
"connectCall",
|
||||||
|
"hangupCall",
|
||||||
|
"remotePeerDisconnected",
|
||||||
|
"cancelCall",
|
||||||
|
"retryCall",
|
||||||
|
"mediaConnected",
|
||||||
|
"setMute",
|
||||||
|
"fetchEmailLink"
|
||||||
|
]);
|
||||||
|
|
||||||
this.set({
|
this.set({
|
||||||
contact: actionData.contact,
|
contact: actionData.contact,
|
||||||
outgoing: windowType === "outgoing",
|
outgoing: windowType === "outgoing",
|
||||||
|
@ -236,11 +243,23 @@ loop.store.ConversationStore = (function() {
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The peer hungup the call.
|
* The remote peer disconnected from the session.
|
||||||
|
*
|
||||||
|
* @param {sharedActions.RemotePeerDisconnected} actionData
|
||||||
*/
|
*/
|
||||||
peerHungupCall: function() {
|
remotePeerDisconnected: function(actionData) {
|
||||||
this._endSession();
|
this._endSession();
|
||||||
this.set({callState: CALL_STATES.FINISHED});
|
|
||||||
|
// If the peer hungup, we end normally, otherwise
|
||||||
|
// we treat this as a call failure.
|
||||||
|
if (actionData.peerHungup) {
|
||||||
|
this.set({callState: CALL_STATES.FINISHED});
|
||||||
|
} else {
|
||||||
|
this.set({
|
||||||
|
callState: CALL_STATES.TERMINATED,
|
||||||
|
callStateReason: "peerNetworkDisconnected"
|
||||||
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -79,6 +79,7 @@ loop.OTSdkDriver = (function() {
|
||||||
connectSession: function(sessionData) {
|
connectSession: function(sessionData) {
|
||||||
this.session = this.sdk.initSession(sessionData.sessionId);
|
this.session = this.sdk.initSession(sessionData.sessionId);
|
||||||
|
|
||||||
|
this.session.on("connectionCreated", this._onConnectionCreated.bind(this));
|
||||||
this.session.on("streamCreated", this._onRemoteStreamCreated.bind(this));
|
this.session.on("streamCreated", this._onRemoteStreamCreated.bind(this));
|
||||||
this.session.on("connectionDestroyed",
|
this.session.on("connectionDestroyed",
|
||||||
this._onConnectionDestroyed.bind(this));
|
this._onConnectionDestroyed.bind(this));
|
||||||
|
@ -130,6 +131,7 @@ loop.OTSdkDriver = (function() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.dispatcher.dispatch(new sharedActions.ConnectedToSdkServers());
|
||||||
this._sessionConnected = true;
|
this._sessionConnected = true;
|
||||||
this._maybePublishLocalStream();
|
this._maybePublishLocalStream();
|
||||||
},
|
},
|
||||||
|
@ -141,18 +143,9 @@ loop.OTSdkDriver = (function() {
|
||||||
* https://tokbox.com/opentok/libraries/client/js/reference/SessionDisconnectEvent.html
|
* https://tokbox.com/opentok/libraries/client/js/reference/SessionDisconnectEvent.html
|
||||||
*/
|
*/
|
||||||
_onConnectionDestroyed: function(event) {
|
_onConnectionDestroyed: function(event) {
|
||||||
var action;
|
this.dispatcher.dispatch(new sharedActions.RemotePeerDisconnected({
|
||||||
if (event.reason === "clientDisconnected") {
|
peerHungup: event.reason === "clientDisconnected"
|
||||||
action = new sharedActions.PeerHungupCall();
|
}));
|
||||||
} else {
|
|
||||||
// Strictly speaking this isn't a failure on our part, but since our
|
|
||||||
// flow requires a full reconnection, then we just treat this as
|
|
||||||
// if a failure of our end had occurred.
|
|
||||||
action = new sharedActions.ConnectionFailure({
|
|
||||||
reason: "peerNetworkDisconnected"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
this.dispatcher.dispatch(action);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -171,6 +164,14 @@ loop.OTSdkDriver = (function() {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_onConnectionCreated: function(event) {
|
||||||
|
if (this.session.connection.id === event.connection.id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.dispatcher.dispatch(new sharedActions.RemotePeerConnected());
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles the event when the remote stream is created.
|
* Handles the event when the remote stream is created.
|
||||||
*
|
*
|
||||||
|
|
|
@ -728,7 +728,8 @@ loop.shared.views = (function(_, OT, l10n) {
|
||||||
React.DOM.button({onClick: this.props.onClick,
|
React.DOM.button({onClick: this.props.onClick,
|
||||||
disabled: this.props.disabled,
|
disabled: this.props.disabled,
|
||||||
className: cx(classObject)},
|
className: cx(classObject)},
|
||||||
this.props.caption
|
React.DOM.span({className: "button-caption"}, this.props.caption),
|
||||||
|
this.props.children
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -728,7 +728,8 @@ loop.shared.views = (function(_, OT, l10n) {
|
||||||
<button onClick={this.props.onClick}
|
<button onClick={this.props.onClick}
|
||||||
disabled={this.props.disabled}
|
disabled={this.props.disabled}
|
||||||
className={cx(classObject)}>
|
className={cx(classObject)}>
|
||||||
{this.props.caption}
|
<span className="button-caption">{this.props.caption}</span>
|
||||||
|
{this.props.children}
|
||||||
</button>
|
</button>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,8 @@ browser.jar:
|
||||||
content/browser/loop/shared/img/sad.png (content/shared/img/sad.png)
|
content/browser/loop/shared/img/sad.png (content/shared/img/sad.png)
|
||||||
content/browser/loop/shared/img/icon_32.png (content/shared/img/icon_32.png)
|
content/browser/loop/shared/img/icon_32.png (content/shared/img/icon_32.png)
|
||||||
content/browser/loop/shared/img/icon_64.png (content/shared/img/icon_64.png)
|
content/browser/loop/shared/img/icon_64.png (content/shared/img/icon_64.png)
|
||||||
content/browser/loop/shared/img/loading-icon.gif (content/shared/img/loading-icon.gif)
|
content/browser/loop/shared/img/spinner.png (content/shared/img/spinner.png)
|
||||||
|
content/browser/loop/shared/img/spinner@2x.png (content/shared/img/spinner@2x.png)
|
||||||
content/browser/loop/shared/img/audio-inverse-14x14.png (content/shared/img/audio-inverse-14x14.png)
|
content/browser/loop/shared/img/audio-inverse-14x14.png (content/shared/img/audio-inverse-14x14.png)
|
||||||
content/browser/loop/shared/img/audio-inverse-14x14@2x.png (content/shared/img/audio-inverse-14x14@2x.png)
|
content/browser/loop/shared/img/audio-inverse-14x14@2x.png (content/shared/img/audio-inverse-14x14@2x.png)
|
||||||
content/browser/loop/shared/img/facemute-14x14.png (content/shared/img/facemute-14x14.png)
|
content/browser/loop/shared/img/facemute-14x14.png (content/shared/img/facemute-14x14.png)
|
||||||
|
|
|
@ -96,6 +96,7 @@
|
||||||
<script type="text/javascript" src="shared/js/validate.js"></script>
|
<script type="text/javascript" src="shared/js/validate.js"></script>
|
||||||
<script type="text/javascript" src="shared/js/dispatcher.js"></script>
|
<script type="text/javascript" src="shared/js/dispatcher.js"></script>
|
||||||
<script type="text/javascript" src="shared/js/websocket.js"></script>
|
<script type="text/javascript" src="shared/js/websocket.js"></script>
|
||||||
|
<script type="text/javascript" src="shared/js/otSdkDriver.js"></script>
|
||||||
<script type="text/javascript" src="shared/js/activeRoomStore.js"></script>
|
<script type="text/javascript" src="shared/js/activeRoomStore.js"></script>
|
||||||
<script type="text/javascript" src="js/standaloneAppStore.js"></script>
|
<script type="text/javascript" src="js/standaloneAppStore.js"></script>
|
||||||
<script type="text/javascript" src="js/standaloneClient.js"></script>
|
<script type="text/javascript" src="js/standaloneClient.js"></script>
|
||||||
|
|
|
@ -987,6 +987,10 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||||
var client = new loop.StandaloneClient({
|
var client = new loop.StandaloneClient({
|
||||||
baseServerUrl: loop.config.serverUrl
|
baseServerUrl: loop.config.serverUrl
|
||||||
});
|
});
|
||||||
|
var sdkDriver = new loop.OTSdkDriver({
|
||||||
|
dispatcher: dispatcher,
|
||||||
|
sdk: OT
|
||||||
|
});
|
||||||
|
|
||||||
var standaloneAppStore = new loop.store.StandaloneAppStore({
|
var standaloneAppStore = new loop.store.StandaloneAppStore({
|
||||||
conversation: conversation,
|
conversation: conversation,
|
||||||
|
@ -996,7 +1000,8 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||||
});
|
});
|
||||||
var activeRoomStore = new loop.store.ActiveRoomStore({
|
var activeRoomStore = new loop.store.ActiveRoomStore({
|
||||||
dispatcher: dispatcher,
|
dispatcher: dispatcher,
|
||||||
mozLoop: standaloneMozLoop
|
mozLoop: standaloneMozLoop,
|
||||||
|
sdkDriver: sdkDriver
|
||||||
});
|
});
|
||||||
|
|
||||||
window.addEventListener("unload", function() {
|
window.addEventListener("unload", function() {
|
||||||
|
|
|
@ -987,6 +987,10 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||||
var client = new loop.StandaloneClient({
|
var client = new loop.StandaloneClient({
|
||||||
baseServerUrl: loop.config.serverUrl
|
baseServerUrl: loop.config.serverUrl
|
||||||
});
|
});
|
||||||
|
var sdkDriver = new loop.OTSdkDriver({
|
||||||
|
dispatcher: dispatcher,
|
||||||
|
sdk: OT
|
||||||
|
});
|
||||||
|
|
||||||
var standaloneAppStore = new loop.store.StandaloneAppStore({
|
var standaloneAppStore = new loop.store.StandaloneAppStore({
|
||||||
conversation: conversation,
|
conversation: conversation,
|
||||||
|
@ -996,7 +1000,8 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||||
});
|
});
|
||||||
var activeRoomStore = new loop.store.ActiveRoomStore({
|
var activeRoomStore = new loop.store.ActiveRoomStore({
|
||||||
dispatcher: dispatcher,
|
dispatcher: dispatcher,
|
||||||
mozLoop: standaloneMozLoop
|
mozLoop: standaloneMozLoop,
|
||||||
|
sdkDriver: sdkDriver
|
||||||
});
|
});
|
||||||
|
|
||||||
window.addEventListener("unload", function() {
|
window.addEventListener("unload", function() {
|
||||||
|
|
|
@ -143,7 +143,8 @@ describe("loop.conversation", function() {
|
||||||
roomStore: roomStore,
|
roomStore: roomStore,
|
||||||
sdk: {},
|
sdk: {},
|
||||||
conversationStore: conversationStore,
|
conversationStore: conversationStore,
|
||||||
conversationAppStore: conversationAppStore
|
conversationAppStore: conversationAppStore,
|
||||||
|
dispatcher: dispatcher
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -214,7 +215,7 @@ describe("loop.conversation", function() {
|
||||||
ccView = mountTestComponent();
|
ccView = mountTestComponent();
|
||||||
|
|
||||||
TestUtils.findRenderedComponentWithType(ccView,
|
TestUtils.findRenderedComponentWithType(ccView,
|
||||||
loop.roomViews.DesktopRoomControllerView);
|
loop.roomViews.DesktopRoomConversationView);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should display the GenericFailureView for failures", function() {
|
it("should display the GenericFailureView for failures", function() {
|
||||||
|
|
|
@ -32,7 +32,8 @@ describe("loop.roomViews", function () {
|
||||||
|
|
||||||
activeRoomStore = new loop.store.ActiveRoomStore({
|
activeRoomStore = new loop.store.ActiveRoomStore({
|
||||||
dispatcher: dispatcher,
|
dispatcher: dispatcher,
|
||||||
mozLoop: {}
|
mozLoop: {},
|
||||||
|
sdkDriver: {}
|
||||||
});
|
});
|
||||||
roomStore = new loop.store.RoomStore({
|
roomStore = new loop.store.RoomStore({
|
||||||
dispatcher: dispatcher,
|
dispatcher: dispatcher,
|
||||||
|
@ -62,6 +63,8 @@ describe("loop.roomViews", function () {
|
||||||
|
|
||||||
expect(testView.state).eql({
|
expect(testView.state).eql({
|
||||||
roomState: ROOM_STATES.INIT,
|
roomState: ROOM_STATES.INIT,
|
||||||
|
audioMuted: false,
|
||||||
|
videoMuted: false,
|
||||||
foo: "bar"
|
foo: "bar"
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -77,21 +80,90 @@ describe("loop.roomViews", function () {
|
||||||
|
|
||||||
activeRoomStore.setStoreState({roomState: ROOM_STATES.READY});
|
activeRoomStore.setStoreState({roomState: ROOM_STATES.READY});
|
||||||
|
|
||||||
expect(testView.state).eql({roomState: ROOM_STATES.READY});
|
expect(testView.state.roomState).eql(ROOM_STATES.READY);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("DesktopRoomControllerView", function() {
|
describe("DesktopRoomConversationView", function() {
|
||||||
var view;
|
var view;
|
||||||
|
|
||||||
|
beforeEach(function() {
|
||||||
|
sandbox.stub(dispatcher, "dispatch");
|
||||||
|
});
|
||||||
|
|
||||||
function mountTestComponent() {
|
function mountTestComponent() {
|
||||||
return TestUtils.renderIntoDocument(
|
return TestUtils.renderIntoDocument(
|
||||||
new loop.roomViews.DesktopRoomControllerView({
|
new loop.roomViews.DesktopRoomConversationView({
|
||||||
mozLoop: {},
|
dispatcher: dispatcher,
|
||||||
roomStore: roomStore
|
roomStore: roomStore
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
it("should dispatch a setupStreamElements action when the view is created",
|
||||||
|
function() {
|
||||||
|
view = mountTestComponent();
|
||||||
|
|
||||||
|
sinon.assert.calledOnce(dispatcher.dispatch);
|
||||||
|
sinon.assert.calledWithMatch(dispatcher.dispatch,
|
||||||
|
sinon.match.hasOwn("name", "setupStreamElements"));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should dispatch a setMute action when the audio mute button is pressed",
|
||||||
|
function() {
|
||||||
|
view = mountTestComponent();
|
||||||
|
|
||||||
|
view.setState({audioMuted: true});
|
||||||
|
|
||||||
|
var muteBtn = view.getDOMNode().querySelector('.btn-mute-audio');
|
||||||
|
|
||||||
|
React.addons.TestUtils.Simulate.click(muteBtn);
|
||||||
|
|
||||||
|
sinon.assert.calledWithMatch(dispatcher.dispatch,
|
||||||
|
sinon.match.hasOwn("name", "setMute"));
|
||||||
|
sinon.assert.calledWithMatch(dispatcher.dispatch,
|
||||||
|
sinon.match.hasOwn("enabled", true));
|
||||||
|
sinon.assert.calledWithMatch(dispatcher.dispatch,
|
||||||
|
sinon.match.hasOwn("type", "audio"));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should dispatch a setMute action when the video mute button is pressed",
|
||||||
|
function() {
|
||||||
|
view = mountTestComponent();
|
||||||
|
|
||||||
|
view.setState({videoMuted: false});
|
||||||
|
|
||||||
|
var muteBtn = view.getDOMNode().querySelector('.btn-mute-video');
|
||||||
|
|
||||||
|
React.addons.TestUtils.Simulate.click(muteBtn);
|
||||||
|
|
||||||
|
sinon.assert.calledWithMatch(dispatcher.dispatch,
|
||||||
|
sinon.match.hasOwn("name", "setMute"));
|
||||||
|
sinon.assert.calledWithMatch(dispatcher.dispatch,
|
||||||
|
sinon.match.hasOwn("enabled", false));
|
||||||
|
sinon.assert.calledWithMatch(dispatcher.dispatch,
|
||||||
|
sinon.match.hasOwn("type", "video"));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should set the mute button as mute off", function() {
|
||||||
|
view = mountTestComponent();
|
||||||
|
|
||||||
|
view.setState({videoMuted: false});
|
||||||
|
|
||||||
|
var muteBtn = view.getDOMNode().querySelector('.btn-mute-video');
|
||||||
|
|
||||||
|
expect(muteBtn.classList.contains("muted")).eql(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should set the mute button as mute on", function() {
|
||||||
|
view = mountTestComponent();
|
||||||
|
|
||||||
|
view.setState({audioMuted: true});
|
||||||
|
|
||||||
|
var muteBtn = view.getDOMNode().querySelector('.btn-mute-audio');
|
||||||
|
|
||||||
|
expect(muteBtn.classList.contains("muted")).eql(true);
|
||||||
|
});
|
||||||
|
|
||||||
describe("#render", function() {
|
describe("#render", function() {
|
||||||
it("should set document.title to store.serverData.roomName", function() {
|
it("should set document.title to store.serverData.roomName", function() {
|
||||||
mountTestComponent();
|
mountTestComponent();
|
||||||
|
|
|
@ -7,7 +7,7 @@ describe("loop.store.ActiveRoomStore", function () {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
var ROOM_STATES = loop.store.ROOM_STATES;
|
var ROOM_STATES = loop.store.ROOM_STATES;
|
||||||
var sandbox, dispatcher, store, fakeMozLoop;
|
var sandbox, dispatcher, store, fakeMozLoop, fakeSdkDriver;
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
sandbox = sinon.sandbox.create();
|
sandbox = sinon.sandbox.create();
|
||||||
|
@ -25,8 +25,16 @@ describe("loop.store.ActiveRoomStore", function () {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
store = new loop.store.ActiveRoomStore(
|
fakeSdkDriver = {
|
||||||
{mozLoop: fakeMozLoop, dispatcher: dispatcher});
|
connectSession: sandbox.stub(),
|
||||||
|
disconnectSession: sandbox.stub()
|
||||||
|
};
|
||||||
|
|
||||||
|
store = new loop.store.ActiveRoomStore({
|
||||||
|
dispatcher: dispatcher,
|
||||||
|
mozLoop: fakeMozLoop,
|
||||||
|
sdkDriver: fakeSdkDriver
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(function() {
|
afterEach(function() {
|
||||||
|
@ -45,6 +53,12 @@ describe("loop.store.ActiveRoomStore", function () {
|
||||||
new loop.store.ActiveRoomStore({dispatcher: dispatcher});
|
new loop.store.ActiveRoomStore({dispatcher: dispatcher});
|
||||||
}).to.Throw(/mozLoop/);
|
}).to.Throw(/mozLoop/);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should throw an error if sdkDriver is missing", function() {
|
||||||
|
expect(function() {
|
||||||
|
new loop.store.ActiveRoomStore({dispatcher: dispatcher, mozLoop: {}});
|
||||||
|
}).to.Throw(/sdkDriver/);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("#roomFailure", function() {
|
describe("#roomFailure", function() {
|
||||||
|
@ -281,6 +295,16 @@ describe("loop.store.ActiveRoomStore", function () {
|
||||||
expect(state.sessionId).eql(fakeJoinedData.sessionId);
|
expect(state.sessionId).eql(fakeJoinedData.sessionId);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should start the session connection with the sdk", function() {
|
||||||
|
var actionData = new sharedActions.JoinedRoom(fakeJoinedData);
|
||||||
|
|
||||||
|
store.joinedRoom(actionData);
|
||||||
|
|
||||||
|
sinon.assert.calledOnce(fakeSdkDriver.connectSession);
|
||||||
|
sinon.assert.calledWithExactly(fakeSdkDriver.connectSession,
|
||||||
|
actionData);
|
||||||
|
});
|
||||||
|
|
||||||
it("should call mozLoop.rooms.refreshMembership before the expiresTime",
|
it("should call mozLoop.rooms.refreshMembership before the expiresTime",
|
||||||
function() {
|
function() {
|
||||||
store.joinedRoom(new sharedActions.JoinedRoom(fakeJoinedData));
|
store.joinedRoom(new sharedActions.JoinedRoom(fakeJoinedData));
|
||||||
|
@ -330,6 +354,93 @@ describe("loop.store.ActiveRoomStore", function () {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("#connectedToSdkServers", function() {
|
||||||
|
it("should set the state to `SESSION_CONNECTED`", function() {
|
||||||
|
store.connectedToSdkServers(new sharedActions.ConnectedToSdkServers());
|
||||||
|
|
||||||
|
expect(store.getStoreState().roomState).eql(ROOM_STATES.SESSION_CONNECTED);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("#connectionFailure", function() {
|
||||||
|
beforeEach(function() {
|
||||||
|
store.setStoreState({
|
||||||
|
roomState: ROOM_STATES.JOINED,
|
||||||
|
roomToken: "fakeToken",
|
||||||
|
sessionToken: "1627384950"
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should disconnect from the servers via the sdk", function() {
|
||||||
|
store.connectionFailure();
|
||||||
|
|
||||||
|
sinon.assert.calledOnce(fakeSdkDriver.disconnectSession);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should clear any existing timeout", function() {
|
||||||
|
sandbox.stub(window, "clearTimeout");
|
||||||
|
store._timeout = {};
|
||||||
|
|
||||||
|
store.connectionFailure();
|
||||||
|
|
||||||
|
sinon.assert.calledOnce(clearTimeout);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should call mozLoop.rooms.leave", function() {
|
||||||
|
store.connectionFailure();
|
||||||
|
|
||||||
|
sinon.assert.calledOnce(fakeMozLoop.rooms.leave);
|
||||||
|
sinon.assert.calledWithExactly(fakeMozLoop.rooms.leave,
|
||||||
|
"fakeToken", "1627384950");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should set the state to `FAILED`", function() {
|
||||||
|
store.connectionFailure();
|
||||||
|
|
||||||
|
expect(store.getStoreState().roomState).eql(ROOM_STATES.FAILED);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("#setMute", function() {
|
||||||
|
it("should save the mute state for the audio stream", function() {
|
||||||
|
store.setStoreState({audioMuted: false});
|
||||||
|
|
||||||
|
store.setMute(new sharedActions.SetMute({
|
||||||
|
type: "audio",
|
||||||
|
enabled: true
|
||||||
|
}));
|
||||||
|
|
||||||
|
expect(store.getStoreState().audioMuted).eql(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should save the mute state for the video stream", function() {
|
||||||
|
store.setStoreState({videoMuted: true});
|
||||||
|
|
||||||
|
store.setMute(new sharedActions.SetMute({
|
||||||
|
type: "video",
|
||||||
|
enabled: false
|
||||||
|
}));
|
||||||
|
|
||||||
|
expect(store.getStoreState().videoMuted).eql(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("#remotePeerConnected", function() {
|
||||||
|
it("should set the state to `HAS_PARTICIPANTS`", function() {
|
||||||
|
store.remotePeerConnected();
|
||||||
|
|
||||||
|
expect(store.getStoreState().roomState).eql(ROOM_STATES.HAS_PARTICIPANTS);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("#remotePeerDisconnected", function() {
|
||||||
|
it("should set the state to `SESSION_CONNECTED`", function() {
|
||||||
|
store.remotePeerDisconnected();
|
||||||
|
|
||||||
|
expect(store.getStoreState().roomState).eql(ROOM_STATES.SESSION_CONNECTED);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe("#windowUnload", function() {
|
describe("#windowUnload", function() {
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
store.setStoreState({
|
store.setStoreState({
|
||||||
|
@ -339,6 +450,12 @@ describe("loop.store.ActiveRoomStore", function () {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should disconnect from the servers via the sdk", function() {
|
||||||
|
store.windowUnload();
|
||||||
|
|
||||||
|
sinon.assert.calledOnce(fakeSdkDriver.disconnectSession);
|
||||||
|
});
|
||||||
|
|
||||||
it("should clear any existing timeout", function() {
|
it("should clear any existing timeout", function() {
|
||||||
sandbox.stub(window, "clearTimeout");
|
sandbox.stub(window, "clearTimeout");
|
||||||
store._timeout = {};
|
store._timeout = {};
|
||||||
|
@ -372,6 +489,12 @@ describe("loop.store.ActiveRoomStore", function () {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should disconnect from the servers via the sdk", function() {
|
||||||
|
store.leaveRoom();
|
||||||
|
|
||||||
|
sinon.assert.calledOnce(fakeSdkDriver.disconnectSession);
|
||||||
|
});
|
||||||
|
|
||||||
it("should clear any existing timeout", function() {
|
it("should clear any existing timeout", function() {
|
||||||
sandbox.stub(window, "clearTimeout");
|
sandbox.stub(window, "clearTimeout");
|
||||||
store._timeout = {};
|
store._timeout = {};
|
||||||
|
|
|
@ -132,14 +132,14 @@ describe("loop.store.ConversationStore", function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should disconnect the session", function() {
|
it("should disconnect the session", function() {
|
||||||
dispatcher.dispatch(
|
store.connectionFailure(
|
||||||
new sharedActions.ConnectionFailure({reason: "fake"}));
|
new sharedActions.ConnectionFailure({reason: "fake"}));
|
||||||
|
|
||||||
sinon.assert.calledOnce(sdkDriver.disconnectSession);
|
sinon.assert.calledOnce(sdkDriver.disconnectSession);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should ensure the websocket is closed", function() {
|
it("should ensure the websocket is closed", function() {
|
||||||
dispatcher.dispatch(
|
store.connectionFailure(
|
||||||
new sharedActions.ConnectionFailure({reason: "fake"}));
|
new sharedActions.ConnectionFailure({reason: "fake"}));
|
||||||
|
|
||||||
sinon.assert.calledOnce(wsCloseSpy);
|
sinon.assert.calledOnce(wsCloseSpy);
|
||||||
|
@ -148,7 +148,7 @@ describe("loop.store.ConversationStore", function () {
|
||||||
it("should set the state to 'terminated'", function() {
|
it("should set the state to 'terminated'", function() {
|
||||||
store.set({callState: CALL_STATES.ALERTING});
|
store.set({callState: CALL_STATES.ALERTING});
|
||||||
|
|
||||||
dispatcher.dispatch(
|
store.connectionFailure(
|
||||||
new sharedActions.ConnectionFailure({reason: "fake"}));
|
new sharedActions.ConnectionFailure({reason: "fake"}));
|
||||||
|
|
||||||
expect(store.get("callState")).eql(CALL_STATES.TERMINATED);
|
expect(store.get("callState")).eql(CALL_STATES.TERMINATED);
|
||||||
|
@ -156,7 +156,7 @@ describe("loop.store.ConversationStore", function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should release mozLoop callsData", function() {
|
it("should release mozLoop callsData", function() {
|
||||||
dispatcher.dispatch(
|
store.connectionFailure(
|
||||||
new sharedActions.ConnectionFailure({reason: "fake"}));
|
new sharedActions.ConnectionFailure({reason: "fake"}));
|
||||||
|
|
||||||
sinon.assert.calledOnce(navigator.mozLoop.calls.clearCallInProgress);
|
sinon.assert.calledOnce(navigator.mozLoop.calls.clearCallInProgress);
|
||||||
|
@ -170,7 +170,7 @@ describe("loop.store.ConversationStore", function () {
|
||||||
it("should change the state from 'gather' to 'connecting'", function() {
|
it("should change the state from 'gather' to 'connecting'", function() {
|
||||||
store.set({callState: CALL_STATES.GATHER});
|
store.set({callState: CALL_STATES.GATHER});
|
||||||
|
|
||||||
dispatcher.dispatch(
|
store.connectionProgress(
|
||||||
new sharedActions.ConnectionProgress({wsState: WS_STATES.INIT}));
|
new sharedActions.ConnectionProgress({wsState: WS_STATES.INIT}));
|
||||||
|
|
||||||
expect(store.get("callState")).eql(CALL_STATES.CONNECTING);
|
expect(store.get("callState")).eql(CALL_STATES.CONNECTING);
|
||||||
|
@ -181,7 +181,7 @@ describe("loop.store.ConversationStore", function () {
|
||||||
it("should change the state from 'gather' to 'alerting'", function() {
|
it("should change the state from 'gather' to 'alerting'", function() {
|
||||||
store.set({callState: CALL_STATES.GATHER});
|
store.set({callState: CALL_STATES.GATHER});
|
||||||
|
|
||||||
dispatcher.dispatch(
|
store.connectionProgress(
|
||||||
new sharedActions.ConnectionProgress({wsState: WS_STATES.ALERTING}));
|
new sharedActions.ConnectionProgress({wsState: WS_STATES.ALERTING}));
|
||||||
|
|
||||||
expect(store.get("callState")).eql(CALL_STATES.ALERTING);
|
expect(store.get("callState")).eql(CALL_STATES.ALERTING);
|
||||||
|
@ -190,7 +190,7 @@ describe("loop.store.ConversationStore", function () {
|
||||||
it("should change the state from 'init' to 'alerting'", function() {
|
it("should change the state from 'init' to 'alerting'", function() {
|
||||||
store.set({callState: CALL_STATES.INIT});
|
store.set({callState: CALL_STATES.INIT});
|
||||||
|
|
||||||
dispatcher.dispatch(
|
store.connectionProgress(
|
||||||
new sharedActions.ConnectionProgress({wsState: WS_STATES.ALERTING}));
|
new sharedActions.ConnectionProgress({wsState: WS_STATES.ALERTING}));
|
||||||
|
|
||||||
expect(store.get("callState")).eql(CALL_STATES.ALERTING);
|
expect(store.get("callState")).eql(CALL_STATES.ALERTING);
|
||||||
|
@ -203,7 +203,7 @@ describe("loop.store.ConversationStore", function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should change the state to 'ongoing'", function() {
|
it("should change the state to 'ongoing'", function() {
|
||||||
dispatcher.dispatch(
|
store.connectionProgress(
|
||||||
new sharedActions.ConnectionProgress({wsState: WS_STATES.CONNECTING}));
|
new sharedActions.ConnectionProgress({wsState: WS_STATES.CONNECTING}));
|
||||||
|
|
||||||
expect(store.get("callState")).eql(CALL_STATES.ONGOING);
|
expect(store.get("callState")).eql(CALL_STATES.ONGOING);
|
||||||
|
@ -212,7 +212,7 @@ describe("loop.store.ConversationStore", function () {
|
||||||
it("should connect the session", function() {
|
it("should connect the session", function() {
|
||||||
store.set(fakeSessionData);
|
store.set(fakeSessionData);
|
||||||
|
|
||||||
dispatcher.dispatch(
|
store.connectionProgress(
|
||||||
new sharedActions.ConnectionProgress({wsState: WS_STATES.CONNECTING}));
|
new sharedActions.ConnectionProgress({wsState: WS_STATES.CONNECTING}));
|
||||||
|
|
||||||
sinon.assert.calledOnce(sdkDriver.connectSession);
|
sinon.assert.calledOnce(sdkDriver.connectSession);
|
||||||
|
@ -386,7 +386,7 @@ describe("loop.store.ConversationStore", function () {
|
||||||
|
|
||||||
describe("#connectCall", function() {
|
describe("#connectCall", function() {
|
||||||
it("should save the call session data", function() {
|
it("should save the call session data", function() {
|
||||||
dispatcher.dispatch(
|
store.connectCall(
|
||||||
new sharedActions.ConnectCall({sessionData: fakeSessionData}));
|
new sharedActions.ConnectCall({sessionData: fakeSessionData}));
|
||||||
|
|
||||||
expect(store.get("apiKey")).eql("fakeKey");
|
expect(store.get("apiKey")).eql("fakeKey");
|
||||||
|
@ -403,7 +403,7 @@ describe("loop.store.ConversationStore", function () {
|
||||||
on: sinon.spy()
|
on: sinon.spy()
|
||||||
});
|
});
|
||||||
|
|
||||||
dispatcher.dispatch(
|
store.connectCall(
|
||||||
new sharedActions.ConnectCall({sessionData: fakeSessionData}));
|
new sharedActions.ConnectCall({sessionData: fakeSessionData}));
|
||||||
|
|
||||||
sinon.assert.calledOnce(loop.CallConnectionWebSocket);
|
sinon.assert.calledOnce(loop.CallConnectionWebSocket);
|
||||||
|
@ -415,7 +415,7 @@ describe("loop.store.ConversationStore", function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should connect the websocket to the server", function() {
|
it("should connect the websocket to the server", function() {
|
||||||
dispatcher.dispatch(
|
store.connectCall(
|
||||||
new sharedActions.ConnectCall({sessionData: fakeSessionData}));
|
new sharedActions.ConnectCall({sessionData: fakeSessionData}));
|
||||||
|
|
||||||
sinon.assert.calledOnce(store._websocket.promiseConnect);
|
sinon.assert.calledOnce(store._websocket.promiseConnect);
|
||||||
|
@ -423,7 +423,7 @@ describe("loop.store.ConversationStore", function () {
|
||||||
|
|
||||||
describe("WebSocket connection result", function() {
|
describe("WebSocket connection result", function() {
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
dispatcher.dispatch(
|
store.connectCall(
|
||||||
new sharedActions.ConnectCall({sessionData: fakeSessionData}));
|
new sharedActions.ConnectCall({sessionData: fakeSessionData}));
|
||||||
|
|
||||||
sandbox.stub(dispatcher, "dispatch");
|
sandbox.stub(dispatcher, "dispatch");
|
||||||
|
@ -480,31 +480,31 @@ describe("loop.store.ConversationStore", function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should disconnect the session", function() {
|
it("should disconnect the session", function() {
|
||||||
dispatcher.dispatch(new sharedActions.HangupCall());
|
store.hangupCall(new sharedActions.HangupCall());
|
||||||
|
|
||||||
sinon.assert.calledOnce(sdkDriver.disconnectSession);
|
sinon.assert.calledOnce(sdkDriver.disconnectSession);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should send a media-fail message to the websocket if it is open", function() {
|
it("should send a media-fail message to the websocket if it is open", function() {
|
||||||
dispatcher.dispatch(new sharedActions.HangupCall());
|
store.hangupCall(new sharedActions.HangupCall());
|
||||||
|
|
||||||
sinon.assert.calledOnce(wsMediaFailSpy);
|
sinon.assert.calledOnce(wsMediaFailSpy);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should ensure the websocket is closed", function() {
|
it("should ensure the websocket is closed", function() {
|
||||||
dispatcher.dispatch(new sharedActions.HangupCall());
|
store.hangupCall(new sharedActions.HangupCall());
|
||||||
|
|
||||||
sinon.assert.calledOnce(wsCloseSpy);
|
sinon.assert.calledOnce(wsCloseSpy);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should set the callState to finished", function() {
|
it("should set the callState to finished", function() {
|
||||||
dispatcher.dispatch(new sharedActions.HangupCall());
|
store.hangupCall(new sharedActions.HangupCall());
|
||||||
|
|
||||||
expect(store.get("callState")).eql(CALL_STATES.FINISHED);
|
expect(store.get("callState")).eql(CALL_STATES.FINISHED);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should release mozLoop callsData", function() {
|
it("should release mozLoop callsData", function() {
|
||||||
dispatcher.dispatch(new sharedActions.HangupCall());
|
store.hangupCall(new sharedActions.HangupCall());
|
||||||
|
|
||||||
sinon.assert.calledOnce(navigator.mozLoop.calls.clearCallInProgress);
|
sinon.assert.calledOnce(navigator.mozLoop.calls.clearCallInProgress);
|
||||||
sinon.assert.calledWithExactly(
|
sinon.assert.calledWithExactly(
|
||||||
|
@ -512,7 +512,7 @@ describe("loop.store.ConversationStore", function () {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("#peerHungupCall", function() {
|
describe("#remotePeerDisconnected", function() {
|
||||||
var wsMediaFailSpy, wsCloseSpy;
|
var wsMediaFailSpy, wsCloseSpy;
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
wsMediaFailSpy = sinon.spy();
|
wsMediaFailSpy = sinon.spy();
|
||||||
|
@ -527,30 +527,56 @@ describe("loop.store.ConversationStore", function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should disconnect the session", function() {
|
it("should disconnect the session", function() {
|
||||||
dispatcher.dispatch(new sharedActions.PeerHungupCall());
|
store.remotePeerDisconnected(new sharedActions.RemotePeerDisconnected({
|
||||||
|
peerHungup: true
|
||||||
|
}));
|
||||||
|
|
||||||
sinon.assert.calledOnce(sdkDriver.disconnectSession);
|
sinon.assert.calledOnce(sdkDriver.disconnectSession);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should ensure the websocket is closed", function() {
|
it("should ensure the websocket is closed", function() {
|
||||||
dispatcher.dispatch(new sharedActions.PeerHungupCall());
|
store.remotePeerDisconnected(new sharedActions.RemotePeerDisconnected({
|
||||||
|
peerHungup: true
|
||||||
|
}));
|
||||||
|
|
||||||
sinon.assert.calledOnce(wsCloseSpy);
|
sinon.assert.calledOnce(wsCloseSpy);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should set the callState to finished", function() {
|
|
||||||
dispatcher.dispatch(new sharedActions.PeerHungupCall());
|
|
||||||
|
|
||||||
expect(store.get("callState")).eql(CALL_STATES.FINISHED);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should release mozLoop callsData", function() {
|
it("should release mozLoop callsData", function() {
|
||||||
dispatcher.dispatch(new sharedActions.PeerHungupCall());
|
store.remotePeerDisconnected(new sharedActions.RemotePeerDisconnected({
|
||||||
|
peerHungup: true
|
||||||
|
}));
|
||||||
|
|
||||||
sinon.assert.calledOnce(navigator.mozLoop.calls.clearCallInProgress);
|
sinon.assert.calledOnce(navigator.mozLoop.calls.clearCallInProgress);
|
||||||
sinon.assert.calledWithExactly(
|
sinon.assert.calledWithExactly(
|
||||||
navigator.mozLoop.calls.clearCallInProgress, "42");
|
navigator.mozLoop.calls.clearCallInProgress, "42");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should set the callState to finished if the peer hungup", function() {
|
||||||
|
store.remotePeerDisconnected(new sharedActions.RemotePeerDisconnected({
|
||||||
|
peerHungup: true
|
||||||
|
}));
|
||||||
|
|
||||||
|
expect(store.get("callState")).eql(CALL_STATES.FINISHED);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should set the callState to terminated if the peer was disconnected" +
|
||||||
|
"for an unintentional reason", function() {
|
||||||
|
store.remotePeerDisconnected(new sharedActions.RemotePeerDisconnected({
|
||||||
|
peerHungup: false
|
||||||
|
}));
|
||||||
|
|
||||||
|
expect(store.get("callState")).eql(CALL_STATES.TERMINATED);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should set the reason to peerNetworkDisconnected if the peer was" +
|
||||||
|
"disconnected for an unintentional reason", function() {
|
||||||
|
store.remotePeerDisconnected(new sharedActions.RemotePeerDisconnected({
|
||||||
|
peerHungup: false
|
||||||
|
}));
|
||||||
|
|
||||||
|
expect(store.get("callStateReason")).eql("peerNetworkDisconnected");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("#cancelCall", function() {
|
describe("#cancelCall", function() {
|
||||||
|
@ -562,25 +588,25 @@ describe("loop.store.ConversationStore", function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should disconnect the session", function() {
|
it("should disconnect the session", function() {
|
||||||
dispatcher.dispatch(new sharedActions.CancelCall());
|
store.cancelCall(new sharedActions.CancelCall());
|
||||||
|
|
||||||
sinon.assert.calledOnce(sdkDriver.disconnectSession);
|
sinon.assert.calledOnce(sdkDriver.disconnectSession);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should send a cancel message to the websocket if it is open", function() {
|
it("should send a cancel message to the websocket if it is open", function() {
|
||||||
dispatcher.dispatch(new sharedActions.CancelCall());
|
store.cancelCall(new sharedActions.CancelCall());
|
||||||
|
|
||||||
sinon.assert.calledOnce(wsCancelSpy);
|
sinon.assert.calledOnce(wsCancelSpy);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should ensure the websocket is closed", function() {
|
it("should ensure the websocket is closed", function() {
|
||||||
dispatcher.dispatch(new sharedActions.CancelCall());
|
store.cancelCall(new sharedActions.CancelCall());
|
||||||
|
|
||||||
sinon.assert.calledOnce(wsCloseSpy);
|
sinon.assert.calledOnce(wsCloseSpy);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should set the state to close if the call is connecting", function() {
|
it("should set the state to close if the call is connecting", function() {
|
||||||
dispatcher.dispatch(new sharedActions.CancelCall());
|
store.cancelCall(new sharedActions.CancelCall());
|
||||||
|
|
||||||
expect(store.get("callState")).eql(CALL_STATES.CLOSE);
|
expect(store.get("callState")).eql(CALL_STATES.CLOSE);
|
||||||
});
|
});
|
||||||
|
@ -588,13 +614,13 @@ describe("loop.store.ConversationStore", function () {
|
||||||
it("should set the state to close if the call has terminated already", function() {
|
it("should set the state to close if the call has terminated already", function() {
|
||||||
store.set({callState: CALL_STATES.TERMINATED});
|
store.set({callState: CALL_STATES.TERMINATED});
|
||||||
|
|
||||||
dispatcher.dispatch(new sharedActions.CancelCall());
|
store.cancelCall(new sharedActions.CancelCall());
|
||||||
|
|
||||||
expect(store.get("callState")).eql(CALL_STATES.CLOSE);
|
expect(store.get("callState")).eql(CALL_STATES.CLOSE);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should release mozLoop callsData", function() {
|
it("should release mozLoop callsData", function() {
|
||||||
dispatcher.dispatch(new sharedActions.CancelCall());
|
store.cancelCall(new sharedActions.CancelCall());
|
||||||
|
|
||||||
sinon.assert.calledOnce(navigator.mozLoop.calls.clearCallInProgress);
|
sinon.assert.calledOnce(navigator.mozLoop.calls.clearCallInProgress);
|
||||||
sinon.assert.calledWithExactly(
|
sinon.assert.calledWithExactly(
|
||||||
|
@ -606,7 +632,7 @@ describe("loop.store.ConversationStore", function () {
|
||||||
it("should set the state to gather", function() {
|
it("should set the state to gather", function() {
|
||||||
store.set({callState: CALL_STATES.TERMINATED});
|
store.set({callState: CALL_STATES.TERMINATED});
|
||||||
|
|
||||||
dispatcher.dispatch(new sharedActions.RetryCall());
|
store.retryCall(new sharedActions.RetryCall());
|
||||||
|
|
||||||
expect(store.get("callState")).eql(CALL_STATES.GATHER);
|
expect(store.get("callState")).eql(CALL_STATES.GATHER);
|
||||||
});
|
});
|
||||||
|
@ -619,7 +645,7 @@ describe("loop.store.ConversationStore", function () {
|
||||||
contact: contact
|
contact: contact
|
||||||
});
|
});
|
||||||
|
|
||||||
dispatcher.dispatch(new sharedActions.RetryCall());
|
store.retryCall(new sharedActions.RetryCall());
|
||||||
|
|
||||||
sinon.assert.calledOnce(client.setupOutgoingCall);
|
sinon.assert.calledOnce(client.setupOutgoingCall);
|
||||||
sinon.assert.calledWith(client.setupOutgoingCall,
|
sinon.assert.calledWith(client.setupOutgoingCall,
|
||||||
|
@ -631,7 +657,7 @@ describe("loop.store.ConversationStore", function () {
|
||||||
it("should send mediaUp via the websocket", function() {
|
it("should send mediaUp via the websocket", function() {
|
||||||
store._websocket = fakeWebsocket;
|
store._websocket = fakeWebsocket;
|
||||||
|
|
||||||
dispatcher.dispatch(new sharedActions.MediaConnected());
|
store.mediaConnected(new sharedActions.MediaConnected());
|
||||||
|
|
||||||
sinon.assert.calledOnce(wsMediaUpSpy);
|
sinon.assert.calledOnce(wsMediaUpSpy);
|
||||||
});
|
});
|
||||||
|
@ -663,7 +689,7 @@ describe("loop.store.ConversationStore", function () {
|
||||||
|
|
||||||
describe("#fetchEmailLink", function() {
|
describe("#fetchEmailLink", function() {
|
||||||
it("should request a new call url to the server", function() {
|
it("should request a new call url to the server", function() {
|
||||||
dispatcher.dispatch(new sharedActions.FetchEmailLink());
|
store.fetchEmailLink(new sharedActions.FetchEmailLink());
|
||||||
|
|
||||||
sinon.assert.calledOnce(client.requestCallUrl);
|
sinon.assert.calledOnce(client.requestCallUrl);
|
||||||
sinon.assert.calledWith(client.requestCallUrl, "");
|
sinon.assert.calledWith(client.requestCallUrl, "");
|
||||||
|
@ -674,7 +700,7 @@ describe("loop.store.ConversationStore", function () {
|
||||||
client.requestCallUrl = function(callId, cb) {
|
client.requestCallUrl = function(callId, cb) {
|
||||||
cb(null, {callUrl: "http://fake.invalid/"});
|
cb(null, {callUrl: "http://fake.invalid/"});
|
||||||
};
|
};
|
||||||
dispatcher.dispatch(new sharedActions.FetchEmailLink());
|
store.fetchEmailLink(new sharedActions.FetchEmailLink());
|
||||||
|
|
||||||
expect(store.get("emailLink")).eql("http://fake.invalid/");
|
expect(store.get("emailLink")).eql("http://fake.invalid/");
|
||||||
});
|
});
|
||||||
|
@ -685,7 +711,7 @@ describe("loop.store.ConversationStore", function () {
|
||||||
client.requestCallUrl = function(callId, cb) {
|
client.requestCallUrl = function(callId, cb) {
|
||||||
cb("error");
|
cb("error");
|
||||||
};
|
};
|
||||||
dispatcher.dispatch(new sharedActions.FetchEmailLink());
|
store.fetchEmailLink(new sharedActions.FetchEmailLink());
|
||||||
|
|
||||||
sinon.assert.calledOnce(trigger);
|
sinon.assert.calledOnce(trigger);
|
||||||
sinon.assert.calledWithExactly(trigger, "error:emailLink");
|
sinon.assert.calledWithExactly(trigger, "error:emailLink");
|
||||||
|
@ -695,7 +721,7 @@ describe("loop.store.ConversationStore", function () {
|
||||||
describe("Events", function() {
|
describe("Events", function() {
|
||||||
describe("Websocket progress", function() {
|
describe("Websocket progress", function() {
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
dispatcher.dispatch(
|
store.connectCall(
|
||||||
new sharedActions.ConnectCall({sessionData: fakeSessionData}));
|
new sharedActions.ConnectCall({sessionData: fakeSessionData}));
|
||||||
|
|
||||||
sandbox.stub(dispatcher, "dispatch");
|
sandbox.stub(dispatcher, "dispatch");
|
||||||
|
|
|
@ -231,26 +231,30 @@ describe("loop.OTSdkDriver", function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("connectionDestroyed", function() {
|
describe("connectionDestroyed", function() {
|
||||||
it("should dispatch a peerHungupCall action if the client disconnected", function() {
|
it("should dispatch a remotePeerDisconnected action if the client" +
|
||||||
session.trigger("connectionDestroyed", {
|
"disconnected", function() {
|
||||||
reason: "clientDisconnected"
|
session.trigger("connectionDestroyed", {
|
||||||
|
reason: "clientDisconnected"
|
||||||
|
});
|
||||||
|
|
||||||
|
sinon.assert.calledOnce(dispatcher.dispatch);
|
||||||
|
sinon.assert.calledWithMatch(dispatcher.dispatch,
|
||||||
|
sinon.match.hasOwn("name", "remotePeerDisconnected"));
|
||||||
|
sinon.assert.calledWithMatch(dispatcher.dispatch,
|
||||||
|
sinon.match.hasOwn("peerHungup", true));
|
||||||
});
|
});
|
||||||
|
|
||||||
sinon.assert.calledOnce(dispatcher.dispatch);
|
it("should dispatch a remotePeerDisconnected action if the connection" +
|
||||||
sinon.assert.calledWithMatch(dispatcher.dispatch,
|
"failed", function() {
|
||||||
sinon.match.hasOwn("name", "peerHungupCall"));
|
session.trigger("connectionDestroyed", {
|
||||||
});
|
reason: "networkDisconnected"
|
||||||
|
});
|
||||||
|
|
||||||
it("should dispatch a connectionFailure action if the connection failed", function() {
|
sinon.assert.calledOnce(dispatcher.dispatch);
|
||||||
session.trigger("connectionDestroyed", {
|
sinon.assert.calledWithMatch(dispatcher.dispatch,
|
||||||
reason: "networkDisconnected"
|
sinon.match.hasOwn("name", "remotePeerDisconnected"));
|
||||||
});
|
sinon.assert.calledWithMatch(dispatcher.dispatch,
|
||||||
|
sinon.match.hasOwn("peerHungup", false));
|
||||||
sinon.assert.calledOnce(dispatcher.dispatch);
|
|
||||||
sinon.assert.calledWithMatch(dispatcher.dispatch,
|
|
||||||
sinon.match.hasOwn("name", "connectionFailure"));
|
|
||||||
sinon.assert.calledWithMatch(dispatcher.dispatch,
|
|
||||||
sinon.match.hasOwn("reason", "peerNetworkDisconnected"));
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -296,5 +300,33 @@ describe("loop.OTSdkDriver", function () {
|
||||||
sinon.match.hasOwn("name", "mediaConnected"));
|
sinon.match.hasOwn("name", "mediaConnected"));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("connectionCreated", function() {
|
||||||
|
beforeEach(function() {
|
||||||
|
session.connection = {
|
||||||
|
id: "localUser"
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should dispatch a RemotePeerConnected action if this is for a remote user",
|
||||||
|
function() {
|
||||||
|
session.trigger("connectionCreated", {
|
||||||
|
connection: {id: "remoteUser"}
|
||||||
|
});
|
||||||
|
|
||||||
|
sinon.assert.calledOnce(dispatcher.dispatch);
|
||||||
|
sinon.assert.calledWithExactly(dispatcher.dispatch,
|
||||||
|
new sharedActions.RemotePeerConnected());
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not dispatch an action if this is for a local user",
|
||||||
|
function() {
|
||||||
|
session.trigger("connectionCreated", {
|
||||||
|
connection: {id: "localUser"}
|
||||||
|
});
|
||||||
|
|
||||||
|
sinon.assert.notCalled(dispatcher.dispatch);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -370,7 +370,8 @@ describe("loop.store.RoomStore", function () {
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
activeRoomStore = new loop.store.ActiveRoomStore({
|
activeRoomStore = new loop.store.ActiveRoomStore({
|
||||||
dispatcher: dispatcher,
|
dispatcher: dispatcher,
|
||||||
mozLoop: fakeMozLoop
|
mozLoop: fakeMozLoop,
|
||||||
|
sdkDriver: {}
|
||||||
});
|
});
|
||||||
store = new loop.store.RoomStore({
|
store = new loop.store.RoomStore({
|
||||||
dispatcher: dispatcher,
|
dispatcher: dispatcher,
|
||||||
|
|
|
@ -41,6 +41,7 @@
|
||||||
<script src="../../content/shared/js/validate.js"></script>
|
<script src="../../content/shared/js/validate.js"></script>
|
||||||
<script src="../../content/shared/js/dispatcher.js"></script>
|
<script src="../../content/shared/js/dispatcher.js"></script>
|
||||||
<script src="../../content/shared/js/activeRoomStore.js"></script>
|
<script src="../../content/shared/js/activeRoomStore.js"></script>
|
||||||
|
<script src="../../content/shared/js/otSdkDriver.js"></script>
|
||||||
<script src="../../standalone/content/js/multiplexGum.js"></script>
|
<script src="../../standalone/content/js/multiplexGum.js"></script>
|
||||||
<script src="../../standalone/content/js/standaloneAppStore.js"></script>
|
<script src="../../standalone/content/js/standaloneAppStore.js"></script>
|
||||||
<script src="../../standalone/content/js/standaloneClient.js"></script>
|
<script src="../../standalone/content/js/standaloneClient.js"></script>
|
||||||
|
|
|
@ -612,7 +612,8 @@ describe("loop.webapp", function() {
|
||||||
dispatcher = new loop.Dispatcher();
|
dispatcher = new loop.Dispatcher();
|
||||||
activeRoomStore = new loop.store.ActiveRoomStore({
|
activeRoomStore = new loop.store.ActiveRoomStore({
|
||||||
dispatcher: dispatcher,
|
dispatcher: dispatcher,
|
||||||
mozLoop: {}
|
mozLoop: {},
|
||||||
|
sdkDriver: {}
|
||||||
});
|
});
|
||||||
standaloneAppStore = new loop.store.StandaloneAppStore({
|
standaloneAppStore = new loop.store.StandaloneAppStore({
|
||||||
dispatcher: dispatcher,
|
dispatcher: dispatcher,
|
||||||
|
|
|
@ -22,7 +22,6 @@
|
||||||
var DesktopPendingConversationView = loop.conversationViews.PendingConversationView;
|
var DesktopPendingConversationView = loop.conversationViews.PendingConversationView;
|
||||||
var CallFailedView = loop.conversationViews.CallFailedView;
|
var CallFailedView = loop.conversationViews.CallFailedView;
|
||||||
var DesktopRoomConversationView = loop.roomViews.DesktopRoomConversationView;
|
var DesktopRoomConversationView = loop.roomViews.DesktopRoomConversationView;
|
||||||
var DesktopRoomInvitationView = loop.roomViews.DesktopRoomInvitationView;
|
|
||||||
|
|
||||||
// 2. Standalone webapp
|
// 2. Standalone webapp
|
||||||
var HomeView = loop.webapp.HomeView;
|
var HomeView = loop.webapp.HomeView;
|
||||||
|
@ -39,6 +38,9 @@
|
||||||
var ConversationView = loop.shared.views.ConversationView;
|
var ConversationView = loop.shared.views.ConversationView;
|
||||||
var FeedbackView = loop.shared.views.FeedbackView;
|
var FeedbackView = loop.shared.views.FeedbackView;
|
||||||
|
|
||||||
|
// Room constants
|
||||||
|
var ROOM_STATES = loop.store.ROOM_STATES;
|
||||||
|
|
||||||
// Local helpers
|
// Local helpers
|
||||||
function returnTrue() {
|
function returnTrue() {
|
||||||
return true;
|
return true;
|
||||||
|
@ -61,7 +63,8 @@
|
||||||
var dispatcher = new loop.Dispatcher();
|
var dispatcher = new loop.Dispatcher();
|
||||||
var activeRoomStore = new loop.store.ActiveRoomStore({
|
var activeRoomStore = new loop.store.ActiveRoomStore({
|
||||||
dispatcher: dispatcher,
|
dispatcher: dispatcher,
|
||||||
mozLoop: navigator.mozLoop
|
mozLoop: navigator.mozLoop,
|
||||||
|
sdkDriver: {}
|
||||||
});
|
});
|
||||||
var roomStore = new loop.store.RoomStore({
|
var roomStore = new loop.store.RoomStore({
|
||||||
dispatcher: dispatcher,
|
dispatcher: dispatcher,
|
||||||
|
@ -533,20 +536,24 @@
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
|
|
||||||
Section({name: "DesktopRoomInvitationView"},
|
Section({name: "DesktopRoomConversationView"},
|
||||||
Example({summary: "Desktop room invitation", dashed: "true",
|
Example({summary: "Desktop room conversation (invitation)", dashed: "true",
|
||||||
style: {width: "260px", height: "265px"}},
|
style: {width: "260px", height: "265px"}},
|
||||||
React.DOM.div({className: "fx-embedded"},
|
React.DOM.div({className: "fx-embedded"},
|
||||||
DesktopRoomInvitationView({roomStore: roomStore})
|
DesktopRoomConversationView({
|
||||||
|
roomStore: roomStore,
|
||||||
|
dispatcher: dispatcher,
|
||||||
|
roomState: ROOM_STATES.INIT})
|
||||||
)
|
)
|
||||||
)
|
),
|
||||||
),
|
|
||||||
|
|
||||||
Section({name: "DesktopRoomConversationView"},
|
|
||||||
Example({summary: "Desktop room conversation", dashed: "true",
|
Example({summary: "Desktop room conversation", dashed: "true",
|
||||||
style: {width: "260px", height: "265px"}},
|
style: {width: "260px", height: "265px"}},
|
||||||
React.DOM.div({className: "fx-embedded"},
|
React.DOM.div({className: "fx-embedded"},
|
||||||
DesktopRoomConversationView({roomStore: roomStore})
|
DesktopRoomConversationView({
|
||||||
|
roomStore: roomStore,
|
||||||
|
dispatcher: dispatcher,
|
||||||
|
roomState: ROOM_STATES.HAS_PARTICIPANTS})
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
|
|
|
@ -22,7 +22,6 @@
|
||||||
var DesktopPendingConversationView = loop.conversationViews.PendingConversationView;
|
var DesktopPendingConversationView = loop.conversationViews.PendingConversationView;
|
||||||
var CallFailedView = loop.conversationViews.CallFailedView;
|
var CallFailedView = loop.conversationViews.CallFailedView;
|
||||||
var DesktopRoomConversationView = loop.roomViews.DesktopRoomConversationView;
|
var DesktopRoomConversationView = loop.roomViews.DesktopRoomConversationView;
|
||||||
var DesktopRoomInvitationView = loop.roomViews.DesktopRoomInvitationView;
|
|
||||||
|
|
||||||
// 2. Standalone webapp
|
// 2. Standalone webapp
|
||||||
var HomeView = loop.webapp.HomeView;
|
var HomeView = loop.webapp.HomeView;
|
||||||
|
@ -39,6 +38,9 @@
|
||||||
var ConversationView = loop.shared.views.ConversationView;
|
var ConversationView = loop.shared.views.ConversationView;
|
||||||
var FeedbackView = loop.shared.views.FeedbackView;
|
var FeedbackView = loop.shared.views.FeedbackView;
|
||||||
|
|
||||||
|
// Room constants
|
||||||
|
var ROOM_STATES = loop.store.ROOM_STATES;
|
||||||
|
|
||||||
// Local helpers
|
// Local helpers
|
||||||
function returnTrue() {
|
function returnTrue() {
|
||||||
return true;
|
return true;
|
||||||
|
@ -61,7 +63,8 @@
|
||||||
var dispatcher = new loop.Dispatcher();
|
var dispatcher = new loop.Dispatcher();
|
||||||
var activeRoomStore = new loop.store.ActiveRoomStore({
|
var activeRoomStore = new loop.store.ActiveRoomStore({
|
||||||
dispatcher: dispatcher,
|
dispatcher: dispatcher,
|
||||||
mozLoop: navigator.mozLoop
|
mozLoop: navigator.mozLoop,
|
||||||
|
sdkDriver: {}
|
||||||
});
|
});
|
||||||
var roomStore = new loop.store.RoomStore({
|
var roomStore = new loop.store.RoomStore({
|
||||||
dispatcher: dispatcher,
|
dispatcher: dispatcher,
|
||||||
|
@ -533,20 +536,24 @@
|
||||||
</Example>
|
</Example>
|
||||||
</Section>
|
</Section>
|
||||||
|
|
||||||
<Section name="DesktopRoomInvitationView">
|
<Section name="DesktopRoomConversationView">
|
||||||
<Example summary="Desktop room invitation" dashed="true"
|
<Example summary="Desktop room conversation (invitation)" dashed="true"
|
||||||
style={{width: "260px", height: "265px"}}>
|
style={{width: "260px", height: "265px"}}>
|
||||||
<div className="fx-embedded">
|
<div className="fx-embedded">
|
||||||
<DesktopRoomInvitationView roomStore={roomStore} />
|
<DesktopRoomConversationView
|
||||||
|
roomStore={roomStore}
|
||||||
|
dispatcher={dispatcher}
|
||||||
|
roomState={ROOM_STATES.INIT} />
|
||||||
</div>
|
</div>
|
||||||
</Example>
|
</Example>
|
||||||
</Section>
|
|
||||||
|
|
||||||
<Section name="DesktopRoomConversationView">
|
|
||||||
<Example summary="Desktop room conversation" dashed="true"
|
<Example summary="Desktop room conversation" dashed="true"
|
||||||
style={{width: "260px", height: "265px"}}>
|
style={{width: "260px", height: "265px"}}>
|
||||||
<div className="fx-embedded">
|
<div className="fx-embedded">
|
||||||
<DesktopRoomConversationView roomStore={roomStore} />
|
<DesktopRoomConversationView
|
||||||
|
roomStore={roomStore}
|
||||||
|
dispatcher={dispatcher}
|
||||||
|
roomState={ROOM_STATES.HAS_PARTICIPANTS} />
|
||||||
</div>
|
</div>
|
||||||
</Example>
|
</Example>
|
||||||
</Section>
|
</Section>
|
||||||
|
|
|
@ -635,21 +635,8 @@ let SessionStoreInternal = {
|
||||||
let uri = activePageData ? activePageData.url || null : null;
|
let uri = activePageData ? activePageData.url || null : null;
|
||||||
browser.userTypedValue = uri;
|
browser.userTypedValue = uri;
|
||||||
|
|
||||||
// If the page has a title, set it.
|
// Update tab label and icon again after the tab history was updated.
|
||||||
if (activePageData) {
|
this.updateTabLabelAndIcon(tab, tabData);
|
||||||
if (activePageData.title) {
|
|
||||||
tab.label = activePageData.title;
|
|
||||||
tab.crop = "end";
|
|
||||||
} else if (activePageData.url != "about:blank") {
|
|
||||||
tab.label = activePageData.url;
|
|
||||||
tab.crop = "center";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Restore the tab icon.
|
|
||||||
if ("image" in tabData) {
|
|
||||||
win.gBrowser.setIcon(tab, tabData.image);
|
|
||||||
}
|
|
||||||
|
|
||||||
let event = win.document.createEvent("Events");
|
let event = win.document.createEvent("Events");
|
||||||
event.initEvent("SSTabRestoring", true, false);
|
event.initEvent("SSTabRestoring", true, false);
|
||||||
|
@ -1860,6 +1847,26 @@ let SessionStoreInternal = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
updateTabLabelAndIcon(tab, tabData) {
|
||||||
|
let activePageData = tabData.entries[tabData.index - 1] || null;
|
||||||
|
|
||||||
|
// If the page has a title, set it.
|
||||||
|
if (activePageData) {
|
||||||
|
if (activePageData.title) {
|
||||||
|
tab.label = activePageData.title;
|
||||||
|
tab.crop = "end";
|
||||||
|
} else if (activePageData.url != "about:blank") {
|
||||||
|
tab.label = activePageData.url;
|
||||||
|
tab.crop = "center";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore the tab icon.
|
||||||
|
if ("image" in tabData) {
|
||||||
|
tab.ownerDocument.defaultView.gBrowser.setIcon(tab, tabData.image);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Restores the session state stored in LastSession. This will attempt
|
* Restores the session state stored in LastSession. This will attempt
|
||||||
* to merge data into the current session. If a window was opened at startup
|
* to merge data into the current session. If a window was opened at startup
|
||||||
|
@ -2532,9 +2539,17 @@ let SessionStoreInternal = {
|
||||||
this._windows[aWindow.__SSi].selected = aSelectTab;
|
this._windows[aWindow.__SSi].selected = aSelectTab;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If we restore the selected tab, make sure it goes first.
|
||||||
|
let selectedIndex = aTabs.indexOf(tabbrowser.selectedTab);
|
||||||
|
if (selectedIndex > -1) {
|
||||||
|
this.restoreTab(tabbrowser.selectedTab, aTabData[selectedIndex]);
|
||||||
|
}
|
||||||
|
|
||||||
// Restore all tabs.
|
// Restore all tabs.
|
||||||
for (let t = 0; t < aTabs.length; t++) {
|
for (let t = 0; t < aTabs.length; t++) {
|
||||||
this.restoreTab(aTabs[t], aTabData[t]);
|
if (t != selectedIndex) {
|
||||||
|
this.restoreTab(aTabs[t], aTabData[t]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -2640,6 +2655,10 @@ let SessionStoreInternal = {
|
||||||
browser.messageManager.sendAsyncMessage("SessionStore:restoreHistory",
|
browser.messageManager.sendAsyncMessage("SessionStore:restoreHistory",
|
||||||
{tabData: tabData, epoch: epoch});
|
{tabData: tabData, epoch: epoch});
|
||||||
|
|
||||||
|
// Update tab label and icon to show something
|
||||||
|
// while we wait for the messages to be processed.
|
||||||
|
this.updateTabLabelAndIcon(tab, tabData);
|
||||||
|
|
||||||
// Restore tab attributes.
|
// Restore tab attributes.
|
||||||
if ("attributes" in tabData) {
|
if ("attributes" in tabData) {
|
||||||
TabAttributes.set(tab, tabData.attributes);
|
TabAttributes.set(tab, tabData.attributes);
|
||||||
|
|
|
@ -761,6 +761,10 @@ Toolbox.prototype = {
|
||||||
let tiltEnabled = !this.target.isMultiProcess &&
|
let tiltEnabled = !this.target.isMultiProcess &&
|
||||||
Services.prefs.getBoolPref("devtools.command-button-tilt.enabled");
|
Services.prefs.getBoolPref("devtools.command-button-tilt.enabled");
|
||||||
let tiltButton = this.doc.getElementById("command-button-tilt");
|
let tiltButton = this.doc.getElementById("command-button-tilt");
|
||||||
|
// Remote toolboxes don't add the button to the DOM at all
|
||||||
|
if (!tiltButton) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (tiltEnabled) {
|
if (tiltEnabled) {
|
||||||
tiltButton.removeAttribute("hidden");
|
tiltButton.removeAttribute("hidden");
|
||||||
|
|
|
@ -76,6 +76,14 @@ exports.showDoorhanger = Task.async(function *({ window, type, anchor }) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Call success function to set preferences/cleanup immediately,
|
||||||
|
// so if triggered multiple times, only happens once (Windows/Linux)
|
||||||
|
success();
|
||||||
|
|
||||||
|
// Wait 200ms to prevent flickering where the popup is displayed
|
||||||
|
// before the underlying window (Windows 7, 64bit)
|
||||||
|
yield wait(200);
|
||||||
|
|
||||||
let document = window.document;
|
let document = window.document;
|
||||||
|
|
||||||
let panel = document.createElementNS(XULNS, "panel");
|
let panel = document.createElementNS(XULNS, "panel");
|
||||||
|
@ -108,9 +116,6 @@ exports.showDoorhanger = Task.async(function *({ window, type, anchor }) {
|
||||||
close();
|
close();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call success function to set preferences, etc.
|
|
||||||
success();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
function setDoorhangerStyle (panel, frame) {
|
function setDoorhangerStyle (panel, frame) {
|
||||||
|
@ -147,3 +152,9 @@ function onFrameLoad (frame) {
|
||||||
function getGBrowser () {
|
function getGBrowser () {
|
||||||
return getMostRecentBrowserWindow().gBrowser;
|
return getMostRecentBrowserWindow().gBrowser;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function wait (n) {
|
||||||
|
let { resolve, promise } = Promise.defer();
|
||||||
|
setTimeout(resolve, n);
|
||||||
|
return promise;
|
||||||
|
}
|
||||||
|
|
|
@ -145,6 +145,11 @@ Telemetry.prototype = {
|
||||||
userHistogram: "DEVTOOLS_NETMONITOR_OPENED_PER_USER_FLAG",
|
userHistogram: "DEVTOOLS_NETMONITOR_OPENED_PER_USER_FLAG",
|
||||||
timerHistogram: "DEVTOOLS_NETMONITOR_TIME_ACTIVE_SECONDS"
|
timerHistogram: "DEVTOOLS_NETMONITOR_TIME_ACTIVE_SECONDS"
|
||||||
},
|
},
|
||||||
|
storage: {
|
||||||
|
histogram: "DEVTOOLS_STORAGE_OPENED_BOOLEAN",
|
||||||
|
userHistogram: "DEVTOOLS_STORAGE_OPENED_PER_USER_FLAG",
|
||||||
|
timerHistogram: "DEVTOOLS_STORAGE_TIME_ACTIVE_SECONDS"
|
||||||
|
},
|
||||||
tilt: {
|
tilt: {
|
||||||
histogram: "DEVTOOLS_TILT_OPENED_BOOLEAN",
|
histogram: "DEVTOOLS_TILT_OPENED_BOOLEAN",
|
||||||
userHistogram: "DEVTOOLS_TILT_OPENED_PER_USER_FLAG",
|
userHistogram: "DEVTOOLS_TILT_OPENED_PER_USER_FLAG",
|
||||||
|
|
|
@ -60,6 +60,7 @@ skip-if = e10s # Bug 1086492 - Disable tilt for e10s
|
||||||
[browser_telemetry_toolboxtabs_netmonitor.js]
|
[browser_telemetry_toolboxtabs_netmonitor.js]
|
||||||
[browser_telemetry_toolboxtabs_options.js]
|
[browser_telemetry_toolboxtabs_options.js]
|
||||||
[browser_telemetry_toolboxtabs_shadereditor.js]
|
[browser_telemetry_toolboxtabs_shadereditor.js]
|
||||||
|
[browser_telemetry_toolboxtabs_storage.js]
|
||||||
[browser_telemetry_toolboxtabs_styleeditor.js]
|
[browser_telemetry_toolboxtabs_styleeditor.js]
|
||||||
[browser_telemetry_toolboxtabs_webaudioeditor.js]
|
[browser_telemetry_toolboxtabs_webaudioeditor.js]
|
||||||
[browser_telemetry_toolboxtabs_webconsole.js]
|
[browser_telemetry_toolboxtabs_webconsole.js]
|
||||||
|
|
|
@ -0,0 +1,119 @@
|
||||||
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
|
const TEST_URI = "data:text/html;charset=utf-8,<p>browser_telemetry_toolboxtabs_storage.js</p>";
|
||||||
|
|
||||||
|
// Because we need to gather stats for the period of time that a tool has been
|
||||||
|
// opened we make use of setTimeout() to create tool active times.
|
||||||
|
const TOOL_DELAY = 200;
|
||||||
|
|
||||||
|
let {Promise: promise} = Cu.import("resource://gre/modules/devtools/deprecated-sync-thenables.js", {});
|
||||||
|
let {Services} = Cu.import("resource://gre/modules/Services.jsm", {});
|
||||||
|
|
||||||
|
let require = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools.require;
|
||||||
|
let Telemetry = require("devtools/shared/telemetry");
|
||||||
|
|
||||||
|
let STORAGE_PREF = "devtools.storage.enabled";
|
||||||
|
Services.prefs.setBoolPref(STORAGE_PREF, true);
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
Telemetry.prototype.telemetryInfo = {};
|
||||||
|
Telemetry.prototype._oldlog = Telemetry.prototype.log;
|
||||||
|
Telemetry.prototype.log = function(histogramId, value) {
|
||||||
|
if (!this.telemetryInfo) {
|
||||||
|
// Can be removed when Bug 992911 lands (see Bug 1011652 Comment 10)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (histogramId) {
|
||||||
|
if (!this.telemetryInfo[histogramId]) {
|
||||||
|
this.telemetryInfo[histogramId] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
this.telemetryInfo[histogramId].push(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
openToolboxTabTwice("storage", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
function openToolboxTabTwice(id, secondPass) {
|
||||||
|
let target = TargetFactory.forTab(gBrowser.selectedTab);
|
||||||
|
|
||||||
|
gDevTools.showToolbox(target, id).then(function(toolbox) {
|
||||||
|
info("Toolbox tab " + id + " opened");
|
||||||
|
|
||||||
|
toolbox.once("destroyed", function() {
|
||||||
|
if (secondPass) {
|
||||||
|
checkResults();
|
||||||
|
} else {
|
||||||
|
openToolboxTabTwice(id, true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// We use a timeout to check the tools active time
|
||||||
|
setTimeout(function() {
|
||||||
|
gDevTools.closeToolbox(target);
|
||||||
|
}, TOOL_DELAY);
|
||||||
|
}).then(null, reportError);
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkResults() {
|
||||||
|
let result = Telemetry.prototype.telemetryInfo;
|
||||||
|
|
||||||
|
for (let [histId, value] of Iterator(result)) {
|
||||||
|
if (histId.endsWith("OPENED_PER_USER_FLAG")) {
|
||||||
|
ok(value.length === 1 && value[0] === true,
|
||||||
|
"Per user value " + histId + " has a single value of true");
|
||||||
|
} else if (histId.endsWith("OPENED_BOOLEAN")) {
|
||||||
|
ok(value.length > 1, histId + " has more than one entry");
|
||||||
|
|
||||||
|
let okay = value.every(function(element) {
|
||||||
|
return element === true;
|
||||||
|
});
|
||||||
|
|
||||||
|
ok(okay, "All " + histId + " entries are === true");
|
||||||
|
} else if (histId.endsWith("TIME_ACTIVE_SECONDS")) {
|
||||||
|
ok(value.length > 1, histId + " has more than one entry");
|
||||||
|
|
||||||
|
let okay = value.every(function(element) {
|
||||||
|
return element > 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
ok(okay, "All " + histId + " entries have time > 0");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
finishUp();
|
||||||
|
}
|
||||||
|
|
||||||
|
function reportError(error) {
|
||||||
|
let stack = " " + error.stack.replace(/\n?.*?@/g, "\n JS frame :: ");
|
||||||
|
|
||||||
|
ok(false, "ERROR: " + error + " at " + error.fileName + ":" +
|
||||||
|
error.lineNumber + "\n\nStack trace:" + stack);
|
||||||
|
finishUp();
|
||||||
|
}
|
||||||
|
|
||||||
|
function finishUp() {
|
||||||
|
gBrowser.removeCurrentTab();
|
||||||
|
|
||||||
|
Telemetry.prototype.log = Telemetry.prototype._oldlog;
|
||||||
|
delete Telemetry.prototype._oldlog;
|
||||||
|
delete Telemetry.prototype.telemetryInfo;
|
||||||
|
|
||||||
|
Services.prefs.clearUserPref(STORAGE_PREF);
|
||||||
|
|
||||||
|
TargetFactory = Services = promise = require = null;
|
||||||
|
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
function test() {
|
||||||
|
waitForExplicitFinish();
|
||||||
|
gBrowser.selectedTab = gBrowser.addTab();
|
||||||
|
gBrowser.selectedBrowser.addEventListener("load", function() {
|
||||||
|
gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
|
||||||
|
waitForFocus(init, content);
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
content.location = TEST_URI;
|
||||||
|
}
|
|
@ -59,7 +59,7 @@ StoragePanel.prototype = {
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Destroy the style editor.
|
* Destroy the storage inspector.
|
||||||
*/
|
*/
|
||||||
destroy: function() {
|
destroy: function() {
|
||||||
if (!this._destroyed) {
|
if (!this._destroyed) {
|
||||||
|
|
|
@ -21,6 +21,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "ViewHelpers",
|
||||||
XPCOMUtils.defineLazyModuleGetter(this, "VariablesView",
|
XPCOMUtils.defineLazyModuleGetter(this, "VariablesView",
|
||||||
"resource:///modules/devtools/VariablesView.jsm");
|
"resource:///modules/devtools/VariablesView.jsm");
|
||||||
|
|
||||||
|
let Telemetry = require("devtools/shared/telemetry");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Localization convenience methods.
|
* Localization convenience methods.
|
||||||
*/
|
*/
|
||||||
|
@ -85,6 +87,9 @@ this.StorageUI = function StorageUI(front, target, panelWin) {
|
||||||
|
|
||||||
this.handleKeypress = this.handleKeypress.bind(this);
|
this.handleKeypress = this.handleKeypress.bind(this);
|
||||||
this._panelDoc.addEventListener("keypress", this.handleKeypress);
|
this._panelDoc.addEventListener("keypress", this.handleKeypress);
|
||||||
|
|
||||||
|
this._telemetry = new Telemetry();
|
||||||
|
this._telemetry.toolOpened("storage");
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.StorageUI = StorageUI;
|
exports.StorageUI = StorageUI;
|
||||||
|
@ -97,6 +102,7 @@ StorageUI.prototype = {
|
||||||
destroy: function() {
|
destroy: function() {
|
||||||
this.front.off("stores-update", this.onUpdate);
|
this.front.off("stores-update", this.onUpdate);
|
||||||
this._panelDoc.removeEventListener("keypress", this.handleKeypress);
|
this._panelDoc.removeEventListener("keypress", this.handleKeypress);
|
||||||
|
this._telemetry.toolClosed("storage");
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -285,7 +285,14 @@ let UI = {
|
||||||
this.cancelBusyTimeout();
|
this.cancelBusyTimeout();
|
||||||
this.unbusy();
|
this.unbusy();
|
||||||
}, (e) => {
|
}, (e) => {
|
||||||
let message = operationDescription + (e ? (": " + e) : "");
|
let message;
|
||||||
|
if (e.error && e.message) {
|
||||||
|
// Some errors come from fronts that are not based on protocol.js.
|
||||||
|
// Errors are not translated to strings.
|
||||||
|
message = operationDescription + " (" + e.error + "): " + e.message;
|
||||||
|
} else {
|
||||||
|
message = operationDescription + (e ? (": " + e) : "");
|
||||||
|
}
|
||||||
this.cancelBusyTimeout();
|
this.cancelBusyTimeout();
|
||||||
let operationCanceled = e && e.canceled;
|
let operationCanceled = e && e.canceled;
|
||||||
if (!operationCanceled) {
|
if (!operationCanceled) {
|
||||||
|
@ -817,7 +824,8 @@ let UI = {
|
||||||
playCmd.setAttribute("disabled", "true");
|
playCmd.setAttribute("disabled", "true");
|
||||||
stopCmd.setAttribute("disabled", "true");
|
stopCmd.setAttribute("disabled", "true");
|
||||||
} else {
|
} else {
|
||||||
if (AppManager.selectedProject.errorsCount == 0) {
|
if (AppManager.selectedProject.errorsCount == 0 &&
|
||||||
|
AppManager.runtimeCanHandleApps()) {
|
||||||
playCmd.removeAttribute("disabled");
|
playCmd.removeAttribute("disabled");
|
||||||
} else {
|
} else {
|
||||||
playCmd.setAttribute("disabled", "true");
|
playCmd.setAttribute("disabled", "true");
|
||||||
|
|
|
@ -106,18 +106,28 @@ let AppManager = exports.AppManager = {
|
||||||
this._listTabsResponse = null;
|
this._listTabsResponse = null;
|
||||||
} else {
|
} else {
|
||||||
this.connection.client.listTabs((response) => {
|
this.connection.client.listTabs((response) => {
|
||||||
let front = new AppActorFront(this.connection.client,
|
if (response.webappsActor) {
|
||||||
response);
|
let front = new AppActorFront(this.connection.client,
|
||||||
front.on("install-progress", this.onInstallProgress);
|
response);
|
||||||
front.watchApps(() => this.checkIfProjectIsRunning())
|
front.on("install-progress", this.onInstallProgress);
|
||||||
.then(() => front.fetchIcons())
|
front.watchApps(() => this.checkIfProjectIsRunning())
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this._appsFront = front;
|
// This can't be done earlier as many operations
|
||||||
this.checkIfProjectIsRunning();
|
// in the apps actor require watchApps to be called
|
||||||
this.update("runtime-apps-found");
|
// first.
|
||||||
});
|
this._appsFront = front;
|
||||||
this._listTabsResponse = response;
|
this._listTabsResponse = response;
|
||||||
this.update("list-tabs-response");
|
this.update("list-tabs-response");
|
||||||
|
return front.fetchIcons();
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
this.checkIfProjectIsRunning();
|
||||||
|
this.update("runtime-apps-found");
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this._listTabsResponse = response;
|
||||||
|
this.update("list-tabs-response");
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -416,6 +426,10 @@ let AppManager = exports.AppManager = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
runtimeCanHandleApps: function() {
|
||||||
|
return !!this._appsFront;
|
||||||
|
},
|
||||||
|
|
||||||
installAndRunProject: function() {
|
installAndRunProject: function() {
|
||||||
let project = this.selectedProject;
|
let project = this.selectedProject;
|
||||||
|
|
||||||
|
@ -429,6 +443,11 @@ let AppManager = exports.AppManager = {
|
||||||
return promise.reject("Can't install");
|
return promise.reject("Can't install");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!this._appsFront) {
|
||||||
|
console.error("Runtime doesn't have a webappsActor");
|
||||||
|
return promise.reject("Can't install");
|
||||||
|
}
|
||||||
|
|
||||||
return Task.spawn(function* () {
|
return Task.spawn(function* () {
|
||||||
let self = AppManager;
|
let self = AppManager;
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,7 @@
|
||||||
let fakeRuntime = {
|
let fakeRuntime = {
|
||||||
type: "USB",
|
type: "USB",
|
||||||
connect: function(connection) {
|
connect: function(connection) {
|
||||||
ok(connection, win.AppManager.connection, "connection is valid");
|
is(connection, win.AppManager.connection, "connection is valid");
|
||||||
connection.host = null; // force connectPipe
|
connection.host = null; // force connectPipe
|
||||||
connection.connect();
|
connection.connect();
|
||||||
return promise.resolve();
|
return promise.resolve();
|
||||||
|
|
|
@ -51,7 +51,7 @@
|
||||||
|
|
||||||
win.AppManager.runtimeList.usb.push({
|
win.AppManager.runtimeList.usb.push({
|
||||||
connect: function(connection) {
|
connect: function(connection) {
|
||||||
ok(connection, win.AppManager.connection, "connection is valid");
|
is(connection, win.AppManager.connection, "connection is valid");
|
||||||
connection.host = null; // force connectPipe
|
connection.host = null; // force connectPipe
|
||||||
connection.connect();
|
connection.connect();
|
||||||
return promise.resolve();
|
return promise.resolve();
|
||||||
|
@ -84,6 +84,8 @@
|
||||||
|
|
||||||
is(Object.keys(DebuggerServer._connections).length, 1, "Connected");
|
is(Object.keys(DebuggerServer._connections).length, 1, "Connected");
|
||||||
|
|
||||||
|
yield waitForUpdate(win, "list-tabs-response");
|
||||||
|
|
||||||
ok(isPlayActive(), "play button is enabled 1");
|
ok(isPlayActive(), "play button is enabled 1");
|
||||||
ok(!isStopActive(), "stop button is disabled 1");
|
ok(!isStopActive(), "stop button is disabled 1");
|
||||||
let oldProject = win.AppManager.selectedProject;
|
let oldProject = win.AppManager.selectedProject;
|
||||||
|
|
|
@ -9,3 +9,5 @@ skip-if = debug # bug 1058695
|
||||||
[browser_pdfjs_savedialog.js]
|
[browser_pdfjs_savedialog.js]
|
||||||
[browser_pdfjs_views.js]
|
[browser_pdfjs_views.js]
|
||||||
skip-if = debug # bug 1058695
|
skip-if = debug # bug 1058695
|
||||||
|
[browser_pdfjs_zoom.js]
|
||||||
|
skip-if = debug # bug 1058695
|
||||||
|
|
|
@ -0,0 +1,173 @@
|
||||||
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
|
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
|
const RELATIVE_DIR = "browser/extensions/pdfjs/test/";
|
||||||
|
const TESTROOT = "http://example.com/browser/" + RELATIVE_DIR;
|
||||||
|
|
||||||
|
const TESTS = [
|
||||||
|
{
|
||||||
|
action: {
|
||||||
|
selector: "button#zoomIn",
|
||||||
|
event: "click"
|
||||||
|
},
|
||||||
|
expectedZoom: 1, // 1 - zoom in
|
||||||
|
message: "Zoomed in using the '+' (zoom in) button"
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
action: {
|
||||||
|
selector: "button#zoomOut",
|
||||||
|
event: "click"
|
||||||
|
},
|
||||||
|
expectedZoom: -1, // -1 - zoom out
|
||||||
|
message: "Zoomed out using the '-' (zoom out) button"
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
action: {
|
||||||
|
keyboard: true,
|
||||||
|
event: "+"
|
||||||
|
},
|
||||||
|
expectedZoom: 1, // 1 - zoom in
|
||||||
|
message: "Zoomed in using the CTRL++ keys"
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
action: {
|
||||||
|
keyboard: true,
|
||||||
|
event: "-"
|
||||||
|
},
|
||||||
|
expectedZoom: -1, // -1 - zoom out
|
||||||
|
message: "Zoomed out using the CTRL+- keys"
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
action: {
|
||||||
|
selector: "select#scaleSelect",
|
||||||
|
index: 5,
|
||||||
|
event: "change"
|
||||||
|
},
|
||||||
|
expectedZoom: -1, // -1 - zoom out
|
||||||
|
message: "Zoomed using the zoom picker"
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
let initialWidth; // the initial width of the PDF document
|
||||||
|
var previousWidth; // the width of the PDF document at previous step/test
|
||||||
|
|
||||||
|
function test() {
|
||||||
|
var tab;
|
||||||
|
let handlerService = Cc["@mozilla.org/uriloader/handler-service;1"]
|
||||||
|
.getService(Ci.nsIHandlerService);
|
||||||
|
let mimeService = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService);
|
||||||
|
let handlerInfo = mimeService.getFromTypeAndExtension('application/pdf', 'pdf');
|
||||||
|
|
||||||
|
// Make sure pdf.js is the default handler.
|
||||||
|
is(handlerInfo.alwaysAskBeforeHandling, false,
|
||||||
|
'pdf handler defaults to always-ask is false');
|
||||||
|
is(handlerInfo.preferredAction, Ci.nsIHandlerInfo.handleInternally,
|
||||||
|
'pdf handler defaults to internal');
|
||||||
|
|
||||||
|
info('Pref action: ' + handlerInfo.preferredAction);
|
||||||
|
|
||||||
|
waitForExplicitFinish();
|
||||||
|
registerCleanupFunction(function() {
|
||||||
|
gBrowser.removeTab(tab);
|
||||||
|
});
|
||||||
|
|
||||||
|
tab = gBrowser.selectedTab = gBrowser.addTab(TESTROOT + "file_pdfjs_test.pdf");
|
||||||
|
var newTabBrowser = gBrowser.getBrowserForTab(tab);
|
||||||
|
|
||||||
|
newTabBrowser.addEventListener("load", function eventHandler() {
|
||||||
|
newTabBrowser.removeEventListener("load", eventHandler, true);
|
||||||
|
|
||||||
|
var document = newTabBrowser.contentDocument,
|
||||||
|
window = newTabBrowser.contentWindow;
|
||||||
|
|
||||||
|
// Runs tests after all 'load' event handlers have fired off
|
||||||
|
window.addEventListener("documentload", function() {
|
||||||
|
initialWidth = parseInt(document.querySelector("div#pageContainer1").style.width);
|
||||||
|
previousWidth = initialWidth;
|
||||||
|
runTests(document, window, finish);
|
||||||
|
}, false, true);
|
||||||
|
}, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function runTests(document, window, callback) {
|
||||||
|
// check that PDF is opened with internal viewer
|
||||||
|
ok(document.querySelector('div#viewer'), "document content has viewer UI");
|
||||||
|
ok('PDFJS' in window.wrappedJSObject, "window content has PDFJS object");
|
||||||
|
|
||||||
|
// Start the zooming tests after the document is loaded
|
||||||
|
waitForDocumentLoad(document).then(function () {
|
||||||
|
zoomPDF(document, window, TESTS.shift(), finish);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function waitForDocumentLoad(document) {
|
||||||
|
var deferred = Promise.defer();
|
||||||
|
var interval = setInterval(function () {
|
||||||
|
if (document.querySelector("div#pageContainer1") != null){
|
||||||
|
clearInterval(interval);
|
||||||
|
deferred.resolve();
|
||||||
|
}
|
||||||
|
}, 500);
|
||||||
|
|
||||||
|
return deferred.promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
function zoomPDF(document, window, test, endCallback) {
|
||||||
|
var renderedPage;
|
||||||
|
|
||||||
|
document.addEventListener("pagerendered", function onPageRendered(e) {
|
||||||
|
if(e.detail.pageNumber !== 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
document.removeEventListener("pagerendered", onPageRendered, true);
|
||||||
|
|
||||||
|
var pageZoomScale = document.querySelector('select#scaleSelect');
|
||||||
|
|
||||||
|
// The zoom value displayed in the zoom select
|
||||||
|
var zoomValue = pageZoomScale.options[pageZoomScale.selectedIndex].innerHTML;
|
||||||
|
|
||||||
|
let pageContainer = document.querySelector('div#pageContainer1');
|
||||||
|
let actualWidth = parseInt(pageContainer.style.width);
|
||||||
|
|
||||||
|
// the actual zoom of the PDF document
|
||||||
|
let computedZoomValue = parseInt(((actualWidth/initialWidth).toFixed(2))*100) + "%";
|
||||||
|
is(computedZoomValue, zoomValue, "Content has correct zoom");
|
||||||
|
|
||||||
|
// Check that document zooms in the expected way (in/out)
|
||||||
|
let zoom = (actualWidth - previousWidth) * test.expectedZoom;
|
||||||
|
ok(zoom > 0, test.message);
|
||||||
|
|
||||||
|
// Go to next test (if there is any) or finish
|
||||||
|
var nextTest = TESTS.shift();
|
||||||
|
if (nextTest) {
|
||||||
|
previousWidth = actualWidth;
|
||||||
|
zoomPDF(document, window, nextTest, endCallback);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
endCallback();
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
// We zoom using an UI element
|
||||||
|
if (test.action.selector) {
|
||||||
|
// Get the element and trigger the action for changing the zoom
|
||||||
|
var el = document.querySelector(test.action.selector);
|
||||||
|
ok(el, "Element '" + test.action.selector + "' has been found");
|
||||||
|
|
||||||
|
if (test.action.index){
|
||||||
|
el.selectedIndex = test.action.index;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dispatch the event for changing the zoom
|
||||||
|
el.dispatchEvent(new Event(test.action.event));
|
||||||
|
}
|
||||||
|
// We zoom using keyboard
|
||||||
|
else {
|
||||||
|
// Simulate key press
|
||||||
|
EventUtils.synthesizeKey(test.action.event, { ctrlKey: true });
|
||||||
|
}
|
||||||
|
}
|
|
@ -8989,7 +8989,7 @@ if test "$ACCESSIBILITY" -a "$MOZ_ENABLE_GTK" ; then
|
||||||
AC_DEFINE_UNQUOTED(ATK_REV_VERSION, $ATK_REV_VERSION)
|
AC_DEFINE_UNQUOTED(ATK_REV_VERSION, $ATK_REV_VERSION)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if test -z "$RELEASE_BUILD" -a -z "$NIGHTLY_BUILD"; then
|
if test -n "$MOZ_DEV_EDITION"; then
|
||||||
AC_DEFINE(MOZ_DEV_EDITION)
|
AC_DEFINE(MOZ_DEV_EDITION)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
|
@ -360,21 +360,27 @@ Promise::MaybeReject(const nsRefPtr<MediaStreamError>& aArg) {
|
||||||
MaybeSomething(aArg, &Promise::MaybeReject);
|
MaybeSomething(aArg, &Promise::MaybeReject);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
bool
|
||||||
Promise::PerformMicroTaskCheckpoint()
|
Promise::PerformMicroTaskCheckpoint()
|
||||||
{
|
{
|
||||||
CycleCollectedJSRuntime* runtime = CycleCollectedJSRuntime::Get();
|
CycleCollectedJSRuntime* runtime = CycleCollectedJSRuntime::Get();
|
||||||
nsTArray<nsRefPtr<nsIRunnable>>& microtaskQueue =
|
nsTArray<nsRefPtr<nsIRunnable>>& microtaskQueue =
|
||||||
runtime->GetPromiseMicroTaskQueue();
|
runtime->GetPromiseMicroTaskQueue();
|
||||||
|
|
||||||
while (!microtaskQueue.IsEmpty()) {
|
if (microtaskQueue.IsEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
nsRefPtr<nsIRunnable> runnable = microtaskQueue.ElementAt(0);
|
nsRefPtr<nsIRunnable> runnable = microtaskQueue.ElementAt(0);
|
||||||
MOZ_ASSERT(runnable);
|
MOZ_ASSERT(runnable);
|
||||||
|
|
||||||
// This function can re-enter, so we remove the element before calling.
|
// This function can re-enter, so we remove the element before calling.
|
||||||
microtaskQueue.RemoveElementAt(0);
|
microtaskQueue.RemoveElementAt(0);
|
||||||
runnable->Run();
|
runnable->Run();
|
||||||
}
|
} while (!microtaskQueue.IsEmpty());
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* static */ bool
|
/* static */ bool
|
||||||
|
|
|
@ -121,7 +121,8 @@ public:
|
||||||
// the T values we support.
|
// the T values we support.
|
||||||
|
|
||||||
// Called by DOM to let us execute our callbacks. May be called recursively.
|
// Called by DOM to let us execute our callbacks. May be called recursively.
|
||||||
static void PerformMicroTaskCheckpoint();
|
// Returns true if at least one microtask was processed.
|
||||||
|
static bool PerformMicroTaskCheckpoint();
|
||||||
|
|
||||||
// WebIDL
|
// WebIDL
|
||||||
|
|
||||||
|
|
|
@ -4243,7 +4243,7 @@ WorkerPrivate::DoRunLoop(JSContext* aCx)
|
||||||
|
|
||||||
// Only perform the Promise microtask checkpoint on the outermost event
|
// Only perform the Promise microtask checkpoint on the outermost event
|
||||||
// loop. Don't run it, for example, during sync XHR or importScripts.
|
// loop. Don't run it, for example, during sync XHR or importScripts.
|
||||||
Promise::PerformMicroTaskCheckpoint();
|
(void)Promise::PerformMicroTaskCheckpoint();
|
||||||
|
|
||||||
if (NS_HasPendingEvents(mThread)) {
|
if (NS_HasPendingEvents(mThread)) {
|
||||||
// Now *might* be a good time to GC. Let the JS engine make the decision.
|
// Now *might* be a good time to GC. Let the JS engine make the decision.
|
||||||
|
|
|
@ -1008,15 +1008,33 @@ nsXPConnect::JSToVariant(JSContext* ctx, HandleValue value, nsIVariant** _retval
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
class DummyRunnable : public nsRunnable {
|
||||||
|
public:
|
||||||
|
NS_IMETHOD Run() { return NS_OK; }
|
||||||
|
};
|
||||||
|
|
||||||
|
} // anonymous namespace
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
nsXPConnect::OnProcessNextEvent(nsIThreadInternal *aThread, bool aMayWait,
|
nsXPConnect::OnProcessNextEvent(nsIThreadInternal *aThread, bool aMayWait,
|
||||||
uint32_t aRecursionDepth)
|
uint32_t aRecursionDepth)
|
||||||
{
|
{
|
||||||
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
|
||||||
// If ProcessNextEvent was called during a Promise "then" callback, we
|
// If ProcessNextEvent was called during a Promise "then" callback, we
|
||||||
// must process any pending microtasks before blocking in the event loop,
|
// must process any pending microtasks before blocking in the event loop,
|
||||||
// otherwise we may deadlock until an event enters the queue later.
|
// otherwise we may deadlock until an event enters the queue later.
|
||||||
if (aMayWait) {
|
if (aMayWait) {
|
||||||
Promise::PerformMicroTaskCheckpoint();
|
if (Promise::PerformMicroTaskCheckpoint()) {
|
||||||
|
// If any microtask was processed, we post a dummy event in order to
|
||||||
|
// force the ProcessNextEvent call not to block. This is required
|
||||||
|
// to support nested event loops implemented using a pattern like
|
||||||
|
// "while (condition) thread.processNextEvent(true)", in case the
|
||||||
|
// condition is triggered here by a Promise "then" callback.
|
||||||
|
NS_DispatchToMainThread(new DummyRunnable());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Record this event.
|
// Record this event.
|
||||||
|
@ -1024,7 +1042,6 @@ nsXPConnect::OnProcessNextEvent(nsIThreadInternal *aThread, bool aMayWait,
|
||||||
|
|
||||||
// Push a null JSContext so that we don't see any script during
|
// Push a null JSContext so that we don't see any script during
|
||||||
// event processing.
|
// event processing.
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
|
||||||
bool ok = PushJSContextNoScriptContext(nullptr);
|
bool ok = PushJSContextNoScriptContext(nullptr);
|
||||||
NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
|
NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
|
|
|
@ -903,9 +903,6 @@ OnSharedPreferenceChangeListener
|
||||||
if (GeckoAppShell.getGeckoInterface() != null) {
|
if (GeckoAppShell.getGeckoInterface() != null) {
|
||||||
intent.putExtra("user_agent", GeckoAppShell.getGeckoInterface().getDefaultUAString());
|
intent.putExtra("user_agent", GeckoAppShell.getGeckoInterface().getDefaultUAString());
|
||||||
}
|
}
|
||||||
if (!AppConstants.MOZILLA_OFFICIAL) {
|
|
||||||
intent.putExtra("is_debug", true);
|
|
||||||
}
|
|
||||||
broadcastAction(context, intent);
|
broadcastAction(context, intent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -146,3 +146,4 @@ skip-if = android_version == "10"
|
||||||
skip-if = android_version == "10"
|
skip-if = android_version == "10"
|
||||||
|
|
||||||
[testStumblerSetting]
|
[testStumblerSetting]
|
||||||
|
skip-if = android_version == "10"
|
||||||
|
|
|
@ -7,7 +7,7 @@ package org.mozilla.mozstumbler.service;
|
||||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
|
|
||||||
public class AppGlobals {
|
public class AppGlobals {
|
||||||
public static final String LOG_PREFIX = "Stumbler:";
|
public static final String LOG_PREFIX = "Stumbler_";
|
||||||
|
|
||||||
/* All intent actions start with this string. Only locally broadcasted. */
|
/* All intent actions start with this string. Only locally broadcasted. */
|
||||||
public static final String ACTION_NAMESPACE = "org.mozilla.mozstumbler.intent.action";
|
public static final String ACTION_NAMESPACE = "org.mozilla.mozstumbler.intent.action";
|
||||||
|
@ -57,6 +57,14 @@ public class AppGlobals {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String makeLogTag(String name) {
|
||||||
|
final int maxLen = 23 - LOG_PREFIX.length();
|
||||||
|
if (name.length() > maxLen) {
|
||||||
|
name = name.substring(name.length() - maxLen, name.length());
|
||||||
|
}
|
||||||
|
return LOG_PREFIX + name;
|
||||||
|
}
|
||||||
|
|
||||||
public static final String ACTION_TEST_SETTING_ENABLED = "stumbler-test-setting-enabled";
|
public static final String ACTION_TEST_SETTING_ENABLED = "stumbler-test-setting-enabled";
|
||||||
public static final String ACTION_TEST_SETTING_DISABLED = "stumbler-test-setting-disabled";
|
public static final String ACTION_TEST_SETTING_DISABLED = "stumbler-test-setting-disabled";
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ import android.text.TextUtils;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
public final class Prefs {
|
public final class Prefs {
|
||||||
private static final String LOG_TAG = Prefs.class.getSimpleName();
|
private static final String LOG_TAG = AppGlobals.makeLogTag(Prefs.class.getSimpleName());
|
||||||
private static final String NICKNAME_PREF = "nickname";
|
private static final String NICKNAME_PREF = "nickname";
|
||||||
private static final String USER_AGENT_PREF = "user-agent";
|
private static final String USER_AGENT_PREF = "user-agent";
|
||||||
private static final String VALUES_VERSION_PREF = "values_version";
|
private static final String VALUES_VERSION_PREF = "values_version";
|
||||||
|
|
|
@ -29,7 +29,9 @@ import org.mozilla.mozstumbler.service.stumblerthread.StumblerService;
|
||||||
* is a good time to try upload, as it is likely that the network is in use.
|
* is a good time to try upload, as it is likely that the network is in use.
|
||||||
*/
|
*/
|
||||||
public class PassiveServiceReceiver extends BroadcastReceiver {
|
public class PassiveServiceReceiver extends BroadcastReceiver {
|
||||||
static final String LOG_TAG = AppGlobals.LOG_PREFIX + PassiveServiceReceiver.class.getSimpleName();
|
// This allows global debugging logs to be enabled by doing
|
||||||
|
// |adb shell setprop log.tag.PassiveStumbler DEBUG|
|
||||||
|
static final String LOG_TAG = "PassiveStumbler";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onReceive(Context context, Intent intent) {
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
@ -37,6 +39,11 @@ public class PassiveServiceReceiver extends BroadcastReceiver {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This value is cached, so if |setprop| is performed (as described on the LOG_TAG above),
|
||||||
|
// then the start/stop intent must be resent by toggling the setting or stopping/starting Fennec.
|
||||||
|
// This does not guard against dumping PII (PII in stumbler is location, wifi BSSID, cell tower details).
|
||||||
|
AppGlobals.isDebug = Log.isLoggable(LOG_TAG, Log.DEBUG);
|
||||||
|
|
||||||
final String action = intent.getAction();
|
final String action = intent.getAction();
|
||||||
final boolean isIntentFromHostApp = (action != null) && action.contains(".STUMBLER_PREF");
|
final boolean isIntentFromHostApp = (action != null) && action.contains(".STUMBLER_PREF");
|
||||||
if (!isIntentFromHostApp) {
|
if (!isIntentFromHostApp) {
|
||||||
|
@ -47,9 +54,6 @@ public class PassiveServiceReceiver extends BroadcastReceiver {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (intent.hasExtra("is_debug")) {
|
|
||||||
AppGlobals.isDebug = intent.getBooleanExtra("is_debug", false);
|
|
||||||
}
|
|
||||||
StumblerService.sFirefoxStumblingEnabled.set(intent.getBooleanExtra("enabled", false));
|
StumblerService.sFirefoxStumblingEnabled.set(intent.getBooleanExtra("enabled", false));
|
||||||
|
|
||||||
if (!StumblerService.sFirefoxStumblingEnabled.get()) {
|
if (!StumblerService.sFirefoxStumblingEnabled.get()) {
|
||||||
|
|
|
@ -30,7 +30,7 @@ import org.mozilla.mozstumbler.service.stumblerthread.scanners.GPSScanner;
|
||||||
import org.mozilla.mozstumbler.service.stumblerthread.scanners.WifiScanner;
|
import org.mozilla.mozstumbler.service.stumblerthread.scanners.WifiScanner;
|
||||||
|
|
||||||
public final class Reporter extends BroadcastReceiver {
|
public final class Reporter extends BroadcastReceiver {
|
||||||
private static final String LOG_TAG = AppGlobals.LOG_PREFIX + Reporter.class.getSimpleName();
|
private static final String LOG_TAG = AppGlobals.makeLogTag(Reporter.class.getSimpleName());
|
||||||
public static final String ACTION_FLUSH_TO_BUNDLE = AppGlobals.ACTION_NAMESPACE + ".FLUSH";
|
public static final String ACTION_FLUSH_TO_BUNDLE = AppGlobals.ACTION_NAMESPACE + ".FLUSH";
|
||||||
private boolean mIsStarted;
|
private boolean mIsStarted;
|
||||||
|
|
||||||
|
@ -195,7 +195,8 @@ public final class Reporter extends BroadcastReceiver {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (AppGlobals.isDebug) {
|
if (AppGlobals.isDebug) {
|
||||||
Log.d(LOG_TAG, "Received bundle: " + mlsObj.toString());
|
// PII: do not log the bundle without obfuscating it
|
||||||
|
Log.d(LOG_TAG, "Received bundle");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (wifiCount + cellCount < 1) {
|
if (wifiCount + cellCount < 1) {
|
||||||
|
|
|
@ -27,7 +27,7 @@ import org.mozilla.mozstumbler.service.utils.PersistentIntentService;
|
||||||
//
|
//
|
||||||
public class StumblerService extends PersistentIntentService
|
public class StumblerService extends PersistentIntentService
|
||||||
implements DataStorageManager.StorageIsEmptyTracker {
|
implements DataStorageManager.StorageIsEmptyTracker {
|
||||||
private static final String LOG_TAG = AppGlobals.LOG_PREFIX + StumblerService.class.getSimpleName();
|
private static final String LOG_TAG = AppGlobals.makeLogTag(StumblerService.class.getSimpleName());
|
||||||
public static final String ACTION_BASE = AppGlobals.ACTION_NAMESPACE;
|
public static final String ACTION_BASE = AppGlobals.ACTION_NAMESPACE;
|
||||||
public static final String ACTION_START_PASSIVE = ACTION_BASE + ".START_PASSIVE";
|
public static final String ACTION_START_PASSIVE = ACTION_BASE + ".START_PASSIVE";
|
||||||
public static final String ACTION_EXTRA_MOZ_API_KEY = ACTION_BASE + ".MOZKEY";
|
public static final String ACTION_EXTRA_MOZ_API_KEY = ACTION_BASE + ".MOZKEY";
|
||||||
|
@ -144,6 +144,8 @@ public class StumblerService extends PersistentIntentService
|
||||||
public void onDestroy() {
|
public void onDestroy() {
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
|
|
||||||
|
UploadAlarmReceiver.cancelAlarm(this, !mScanManager.isPassiveMode());
|
||||||
|
|
||||||
if (!mScanManager.isScanning()) {
|
if (!mScanManager.isScanning()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -198,7 +200,11 @@ public class StumblerService extends PersistentIntentService
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!DataStorageManager.getInstance().isDirEmpty()) {
|
boolean hasFilesWaiting = !DataStorageManager.getInstance().isDirEmpty();
|
||||||
|
if (AppGlobals.isDebug) {
|
||||||
|
Log.d(LOG_TAG, "Files waiting:" + hasFilesWaiting);
|
||||||
|
}
|
||||||
|
if (hasFilesWaiting) {
|
||||||
// non-empty on startup, schedule an upload
|
// non-empty on startup, schedule an upload
|
||||||
// This is the only upload trigger in Firefox mode
|
// This is the only upload trigger in Firefox mode
|
||||||
// Firefox triggers this ~4 seconds after startup (after Gecko is loaded), add a small delay to avoid
|
// Firefox triggers this ~4 seconds after startup (after Gecko is loaded), add a small delay to avoid
|
||||||
|
|
|
@ -11,7 +11,7 @@ import java.util.Locale;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
public final class BSSIDBlockList {
|
public final class BSSIDBlockList {
|
||||||
private static final String LOG_TAG = AppGlobals.LOG_PREFIX + BSSIDBlockList.class.getSimpleName();
|
private static final String LOG_TAG = AppGlobals.makeLogTag(BSSIDBlockList.class.getSimpleName());
|
||||||
private static final String NULL_BSSID = "000000000000";
|
private static final String NULL_BSSID = "000000000000";
|
||||||
private static final String WILDCARD_BSSID = "ffffffffffff";
|
private static final String WILDCARD_BSSID = "ffffffffffff";
|
||||||
private static final Pattern BSSID_PATTERN = Pattern.compile("([0-9a-f]{12})");
|
private static final Pattern BSSID_PATTERN = Pattern.compile("([0-9a-f]{12})");
|
||||||
|
|
|
@ -38,7 +38,7 @@ import java.util.TimerTask;
|
||||||
* when the service is destroyed.
|
* when the service is destroyed.
|
||||||
*/
|
*/
|
||||||
public class DataStorageManager {
|
public class DataStorageManager {
|
||||||
private static final String LOG_TAG = AppGlobals.LOG_PREFIX + DataStorageManager.class.getSimpleName();
|
private static final String LOG_TAG = AppGlobals.makeLogTag(DataStorageManager.class.getSimpleName());
|
||||||
|
|
||||||
// The max number of reports stored in the mCurrentReports. Each report is a GPS location plus wifi and cell scan.
|
// The max number of reports stored in the mCurrentReports. Each report is a GPS location plus wifi and cell scan.
|
||||||
// After this size is reached, data is persisted to disk, mCurrentReports is cleared.
|
// After this size is reached, data is persisted to disk, mCurrentReports is cleared.
|
||||||
|
@ -201,19 +201,7 @@ public class DataStorageManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getStorageDir(Context c) {
|
private String getStorageDir(Context c) {
|
||||||
File dir = null;
|
File dir = c.getFilesDir();
|
||||||
if (AppGlobals.isDebug) {
|
|
||||||
// in debug, put files in public location
|
|
||||||
dir = c.getExternalFilesDir(null);
|
|
||||||
if (dir != null) {
|
|
||||||
dir = new File(dir.getAbsolutePath() + "/mozstumbler");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dir == null) {
|
|
||||||
dir = c.getFilesDir();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!dir.exists()) {
|
if (!dir.exists()) {
|
||||||
boolean ok = dir.mkdirs();
|
boolean ok = dir.mkdirs();
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
|
@ -414,9 +402,6 @@ public class DataStorageManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
final String result = sb.append(kSuffix).toString();
|
final String result = sb.append(kSuffix).toString();
|
||||||
if (AppGlobals.isDebug) {
|
|
||||||
Log.d(LOG_TAG, result);
|
|
||||||
}
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -34,7 +34,7 @@ public class GPSScanner implements LocationListener {
|
||||||
public static final String NEW_STATUS_ARG_SATS = "sats";
|
public static final String NEW_STATUS_ARG_SATS = "sats";
|
||||||
public static final String NEW_LOCATION_ARG_LOCATION = "location";
|
public static final String NEW_LOCATION_ARG_LOCATION = "location";
|
||||||
|
|
||||||
private static final String LOG_TAG = AppGlobals.LOG_PREFIX + GPSScanner.class.getSimpleName();
|
private static final String LOG_TAG = AppGlobals.makeLogTag(GPSScanner.class.getSimpleName());
|
||||||
private static final int MIN_SAT_USED_IN_FIX = 3;
|
private static final int MIN_SAT_USED_IN_FIX = 3;
|
||||||
private static final long ACTIVE_MODE_GPS_MIN_UPDATE_TIME_MS = 1000;
|
private static final long ACTIVE_MODE_GPS_MIN_UPDATE_TIME_MS = 1000;
|
||||||
private static final float ACTIVE_MODE_GPS_MIN_UPDATE_DISTANCE_M = 10;
|
private static final float ACTIVE_MODE_GPS_MIN_UPDATE_DISTANCE_M = 10;
|
||||||
|
@ -191,15 +191,10 @@ public class GPSScanner implements LocationListener {
|
||||||
sendToLogActivity(logMsg);
|
sendToLogActivity(logMsg);
|
||||||
|
|
||||||
if (mBlockList.contains(location)) {
|
if (mBlockList.contains(location)) {
|
||||||
Log.w(LOG_TAG, "Blocked location: " + location);
|
|
||||||
reportLocationLost();
|
reportLocationLost();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (AppGlobals.isDebug) {
|
|
||||||
Log.d(LOG_TAG, "New location: " + location);
|
|
||||||
}
|
|
||||||
|
|
||||||
mLocation = location;
|
mLocation = location;
|
||||||
|
|
||||||
if (!mAutoGeofencing) {
|
if (!mAutoGeofencing) {
|
||||||
|
|
|
@ -10,7 +10,7 @@ import org.mozilla.mozstumbler.service.AppGlobals;
|
||||||
import org.mozilla.mozstumbler.service.Prefs;
|
import org.mozilla.mozstumbler.service.Prefs;
|
||||||
|
|
||||||
public final class LocationBlockList {
|
public final class LocationBlockList {
|
||||||
private static final String LOG_TAG = AppGlobals.LOG_PREFIX + LocationBlockList.class.getSimpleName();
|
private static final String LOG_TAG = AppGlobals.makeLogTag(LocationBlockList.class.getSimpleName());
|
||||||
private static final double MAX_ALTITUDE = 8848; // Mount Everest's altitude in meters
|
private static final double MAX_ALTITUDE = 8848; // Mount Everest's altitude in meters
|
||||||
private static final double MIN_ALTITUDE = -418; // Dead Sea's altitude in meters
|
private static final double MIN_ALTITUDE = -418; // Dead Sea's altitude in meters
|
||||||
private static final float MAX_SPEED = 340.29f; // Mach 1 in meters/second
|
private static final float MAX_SPEED = 340.29f; // Mach 1 in meters/second
|
||||||
|
|
|
@ -23,7 +23,7 @@ import java.util.Timer;
|
||||||
import java.util.TimerTask;
|
import java.util.TimerTask;
|
||||||
|
|
||||||
public class ScanManager {
|
public class ScanManager {
|
||||||
private static final String LOG_TAG = AppGlobals.LOG_PREFIX + ScanManager.class.getSimpleName();
|
private static final String LOG_TAG = AppGlobals.makeLogTag(ScanManager.class.getSimpleName());
|
||||||
private Timer mPassiveModeFlushTimer;
|
private Timer mPassiveModeFlushTimer;
|
||||||
private Context mContext;
|
private Context mContext;
|
||||||
private boolean mIsScanning;
|
private boolean mIsScanning;
|
||||||
|
|
|
@ -40,7 +40,7 @@ public class WifiScanner extends BroadcastReceiver {
|
||||||
public static final int STATUS_ACTIVE = 1;
|
public static final int STATUS_ACTIVE = 1;
|
||||||
public static final int STATUS_WIFI_DISABLED = -1;
|
public static final int STATUS_WIFI_DISABLED = -1;
|
||||||
|
|
||||||
private static final String LOG_TAG = AppGlobals.LOG_PREFIX + WifiScanner.class.getSimpleName();
|
private static final String LOG_TAG = AppGlobals.makeLogTag(WifiScanner.class.getSimpleName());
|
||||||
private static final long WIFI_MIN_UPDATE_TIME = 5000; // milliseconds
|
private static final long WIFI_MIN_UPDATE_TIME = 5000; // milliseconds
|
||||||
|
|
||||||
private boolean mStarted;
|
private boolean mStarted;
|
||||||
|
@ -194,11 +194,9 @@ public class WifiScanner extends BroadcastReceiver {
|
||||||
|
|
||||||
public static boolean shouldLog(ScanResult scanResult) {
|
public static boolean shouldLog(ScanResult scanResult) {
|
||||||
if (BSSIDBlockList.contains(scanResult)) {
|
if (BSSIDBlockList.contains(scanResult)) {
|
||||||
Log.w(LOG_TAG, "Blocked BSSID: " + scanResult);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (SSIDBlockList.contains(scanResult)) {
|
if (SSIDBlockList.contains(scanResult)) {
|
||||||
Log.w(LOG_TAG, "Blocked SSID: " + scanResult);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -19,7 +19,7 @@ import org.json.JSONObject;
|
||||||
import org.mozilla.mozstumbler.service.AppGlobals;
|
import org.mozilla.mozstumbler.service.AppGlobals;
|
||||||
|
|
||||||
public class CellInfo implements Parcelable {
|
public class CellInfo implements Parcelable {
|
||||||
private static final String LOG_TAG = AppGlobals.LOG_PREFIX + CellInfo.class.getSimpleName();
|
private static final String LOG_TAG = AppGlobals.makeLogTag(CellInfo.class.getSimpleName());
|
||||||
|
|
||||||
public static final String RADIO_GSM = "gsm";
|
public static final String RADIO_GSM = "gsm";
|
||||||
public static final String RADIO_CDMA = "cdma";
|
public static final String RADIO_CDMA = "cdma";
|
||||||
|
|
|
@ -26,7 +26,7 @@ public class CellScanner {
|
||||||
public static final String ACTION_CELLS_SCANNED_ARG_CELLS = "cells";
|
public static final String ACTION_CELLS_SCANNED_ARG_CELLS = "cells";
|
||||||
public static final String ACTION_CELLS_SCANNED_ARG_TIME = AppGlobals.ACTION_ARG_TIME;
|
public static final String ACTION_CELLS_SCANNED_ARG_TIME = AppGlobals.ACTION_ARG_TIME;
|
||||||
|
|
||||||
private static final String LOG_TAG = AppGlobals.LOG_PREFIX + CellScanner.class.getSimpleName();
|
private static final String LOG_TAG = AppGlobals.makeLogTag(CellScanner.class.getSimpleName());
|
||||||
private static final long CELL_MIN_UPDATE_TIME = 1000; // milliseconds
|
private static final long CELL_MIN_UPDATE_TIME = 1000; // milliseconds
|
||||||
|
|
||||||
private final Context mContext;
|
private final Context mContext;
|
||||||
|
|
|
@ -31,7 +31,7 @@ import java.util.List;
|
||||||
/* Fennec does not yet support the api level for WCDMA import */
|
/* Fennec does not yet support the api level for WCDMA import */
|
||||||
public class CellScannerNoWCDMA implements CellScanner.CellScannerImpl {
|
public class CellScannerNoWCDMA implements CellScanner.CellScannerImpl {
|
||||||
|
|
||||||
protected static String LOG_TAG = AppGlobals.LOG_PREFIX + CellScannerNoWCDMA.class.getSimpleName();
|
protected static String LOG_TAG = AppGlobals.makeLogTag(CellScannerNoWCDMA.class.getSimpleName());
|
||||||
protected GetAllCellInfoScannerImpl mGetAllInfoCellScanner;
|
protected GetAllCellInfoScannerImpl mGetAllInfoCellScanner;
|
||||||
protected TelephonyManager mTelephonyManager;
|
protected TelephonyManager mTelephonyManager;
|
||||||
protected boolean mIsStarted;
|
protected boolean mIsStarted;
|
||||||
|
|
|
@ -24,7 +24,7 @@ import org.mozilla.mozstumbler.service.utils.NetworkUtils;
|
||||||
* preferences, do not call any code that isn't thread-safe. You will cause suffering.
|
* preferences, do not call any code that isn't thread-safe. You will cause suffering.
|
||||||
* An exception is made for AppGlobals.isDebug, a false reading is of no consequence. */
|
* An exception is made for AppGlobals.isDebug, a false reading is of no consequence. */
|
||||||
public class AsyncUploader extends AsyncTask<Void, Void, SyncSummary> {
|
public class AsyncUploader extends AsyncTask<Void, Void, SyncSummary> {
|
||||||
private static final String LOG_TAG = AppGlobals.LOG_PREFIX + AsyncUploader.class.getSimpleName();
|
private static final String LOG_TAG = AppGlobals.makeLogTag(AsyncUploader.class.getSimpleName());
|
||||||
private final UploadSettings mSettings;
|
private final UploadSettings mSettings;
|
||||||
private final Object mListenerLock = new Object();
|
private final Object mListenerLock = new Object();
|
||||||
private AsyncUploaderListener mListener;
|
private AsyncUploaderListener mListener;
|
||||||
|
|
|
@ -30,7 +30,7 @@ import org.mozilla.mozstumbler.service.utils.NetworkUtils;
|
||||||
// - triggered from the main thread
|
// - triggered from the main thread
|
||||||
// - actual work is done the upload thread (AsyncUploader)
|
// - actual work is done the upload thread (AsyncUploader)
|
||||||
public class UploadAlarmReceiver extends BroadcastReceiver {
|
public class UploadAlarmReceiver extends BroadcastReceiver {
|
||||||
private static final String LOG_TAG = AppGlobals.LOG_PREFIX + UploadAlarmReceiver.class.getSimpleName();
|
private static final String LOG_TAG = AppGlobals.makeLogTag(UploadAlarmReceiver.class.getSimpleName());
|
||||||
private static final String EXTRA_IS_REPEATING = "is_repeating";
|
private static final String EXTRA_IS_REPEATING = "is_repeating";
|
||||||
private static boolean sIsAlreadyScheduled;
|
private static boolean sIsAlreadyScheduled;
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,7 @@ import java.net.URL;
|
||||||
|
|
||||||
public abstract class AbstractCommunicator {
|
public abstract class AbstractCommunicator {
|
||||||
|
|
||||||
private static final String LOG_TAG = AppGlobals.LOG_PREFIX + AbstractCommunicator.class.getSimpleName();
|
private static final String LOG_TAG = AppGlobals.makeLogTag(AbstractCommunicator.class.getSimpleName());
|
||||||
private static final String NICKNAME_HEADER = "X-Nickname";
|
private static final String NICKNAME_HEADER = "X-Nickname";
|
||||||
private static final String USER_AGENT_HEADER = "User-Agent";
|
private static final String USER_AGENT_HEADER = "User-Agent";
|
||||||
private HttpURLConnection mHttpURLConnection;
|
private HttpURLConnection mHttpURLConnection;
|
||||||
|
|
|
@ -11,7 +11,7 @@ import android.util.Log;
|
||||||
import org.mozilla.mozstumbler.service.AppGlobals;
|
import org.mozilla.mozstumbler.service.AppGlobals;
|
||||||
|
|
||||||
public final class NetworkUtils {
|
public final class NetworkUtils {
|
||||||
private static final String LOG_TAG = AppGlobals.LOG_PREFIX + NetworkUtils.class.getSimpleName();
|
private static final String LOG_TAG = AppGlobals.makeLogTag(NetworkUtils.class.getSimpleName());
|
||||||
|
|
||||||
ConnectivityManager mConnectivityManager;
|
ConnectivityManager mConnectivityManager;
|
||||||
static NetworkUtils sInstance;
|
static NetworkUtils sInstance;
|
||||||
|
|
|
@ -5856,6 +5856,11 @@
|
||||||
"kind": "boolean",
|
"kind": "boolean",
|
||||||
"description": "How many times has the devtool's Network Monitor been opened?"
|
"description": "How many times has the devtool's Network Monitor been opened?"
|
||||||
},
|
},
|
||||||
|
"DEVTOOLS_STORAGE_OPENED_BOOLEAN": {
|
||||||
|
"expires_in_version": "never",
|
||||||
|
"kind": "boolean",
|
||||||
|
"description": "How many times has the Storage Inspector been opened?"
|
||||||
|
},
|
||||||
"DEVTOOLS_PAINTFLASHING_OPENED_BOOLEAN": {
|
"DEVTOOLS_PAINTFLASHING_OPENED_BOOLEAN": {
|
||||||
"expires_in_version": "never",
|
"expires_in_version": "never",
|
||||||
"kind": "boolean",
|
"kind": "boolean",
|
||||||
|
@ -5976,6 +5981,11 @@
|
||||||
"kind": "flag",
|
"kind": "flag",
|
||||||
"description": "How many users have opened the devtool's Network Monitor?"
|
"description": "How many users have opened the devtool's Network Monitor?"
|
||||||
},
|
},
|
||||||
|
"DEVTOOLS_STORAGE_OPENED_PER_USER_FLAG": {
|
||||||
|
"expires_in_version": "never",
|
||||||
|
"kind": "flag",
|
||||||
|
"description": "How many users have opened the devtool's Storage Inspector?"
|
||||||
|
},
|
||||||
"DEVTOOLS_PAINTFLASHING_OPENED_PER_USER_FLAG": {
|
"DEVTOOLS_PAINTFLASHING_OPENED_PER_USER_FLAG": {
|
||||||
"expires_in_version": "never",
|
"expires_in_version": "never",
|
||||||
"kind": "flag",
|
"kind": "flag",
|
||||||
|
@ -6130,6 +6140,13 @@
|
||||||
"n_buckets": 100,
|
"n_buckets": 100,
|
||||||
"description": "How long has the network monitor been active (seconds)"
|
"description": "How long has the network monitor been active (seconds)"
|
||||||
},
|
},
|
||||||
|
"DEVTOOLS_STORAGE_TIME_ACTIVE_SECONDS": {
|
||||||
|
"expires_in_version": "never",
|
||||||
|
"kind": "exponential",
|
||||||
|
"high": "10000000",
|
||||||
|
"n_buckets": 100,
|
||||||
|
"description": "How long has the storage inspector been active (seconds)"
|
||||||
|
},
|
||||||
"DEVTOOLS_PAINTFLASHING_TIME_ACTIVE_SECONDS": {
|
"DEVTOOLS_PAINTFLASHING_TIME_ACTIVE_SECONDS": {
|
||||||
"expires_in_version": "never",
|
"expires_in_version": "never",
|
||||||
"kind": "exponential",
|
"kind": "exponential",
|
||||||
|
|
Загрузка…
Ссылка в новой задаче