mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge m-c to inbound. a=merge
This commit is contained in:
commit
6a5ec97f20
@ -15,7 +15,7 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="fe92ddd450e03b38edb2d465de7897971d68ac68">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="3802009e1ab6c3ddfc3eb15522e3140a96b33336"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="e1fd99454b6cd5da4f2c58f928fc04c6d03f478f"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="b81855b6b67f285d6f27a4f8c1cfe2e0387ea57c"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
@ -19,7 +19,7 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="3802009e1ab6c3ddfc3eb15522e3140a96b33336"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="e1fd99454b6cd5da4f2c58f928fc04c6d03f478f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="b81855b6b67f285d6f27a4f8c1cfe2e0387ea57c"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>
|
||||
|
@ -17,7 +17,7 @@
|
||||
</project>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="3802009e1ab6c3ddfc3eb15522e3140a96b33336"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="e1fd99454b6cd5da4f2c58f928fc04c6d03f478f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="b81855b6b67f285d6f27a4f8c1cfe2e0387ea57c"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="f3e998242fb9a857cf50f5bf3a02304a530ea617"/>
|
||||
|
@ -15,7 +15,7 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="fe92ddd450e03b38edb2d465de7897971d68ac68">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="3802009e1ab6c3ddfc3eb15522e3140a96b33336"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="e1fd99454b6cd5da4f2c58f928fc04c6d03f478f"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="b81855b6b67f285d6f27a4f8c1cfe2e0387ea57c"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
@ -19,7 +19,7 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="3802009e1ab6c3ddfc3eb15522e3140a96b33336"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="e1fd99454b6cd5da4f2c58f928fc04c6d03f478f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="b81855b6b67f285d6f27a4f8c1cfe2e0387ea57c"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>
|
||||
|
@ -15,7 +15,7 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="fe92ddd450e03b38edb2d465de7897971d68ac68">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="3802009e1ab6c3ddfc3eb15522e3140a96b33336"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="e1fd99454b6cd5da4f2c58f928fc04c6d03f478f"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="b81855b6b67f285d6f27a4f8c1cfe2e0387ea57c"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
@ -17,7 +17,7 @@
|
||||
</project>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="3802009e1ab6c3ddfc3eb15522e3140a96b33336"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="e1fd99454b6cd5da4f2c58f928fc04c6d03f478f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="b81855b6b67f285d6f27a4f8c1cfe2e0387ea57c"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="f3e998242fb9a857cf50f5bf3a02304a530ea617"/>
|
||||
|
@ -4,6 +4,6 @@
|
||||
"remote": "",
|
||||
"branch": ""
|
||||
},
|
||||
"revision": "a491e07757d721d4bea7302cfbb2b18460a3820d",
|
||||
"revision": "36ec1e83a5bc8dc4106bd3fa1a506f1085611ba2",
|
||||
"repo_path": "/integration/gaia-central"
|
||||
}
|
||||
|
@ -17,7 +17,7 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="3802009e1ab6c3ddfc3eb15522e3140a96b33336"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="e1fd99454b6cd5da4f2c58f928fc04c6d03f478f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="b81855b6b67f285d6f27a4f8c1cfe2e0387ea57c"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
@ -15,7 +15,7 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="3802009e1ab6c3ddfc3eb15522e3140a96b33336"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="e1fd99454b6cd5da4f2c58f928fc04c6d03f478f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="b81855b6b67f285d6f27a4f8c1cfe2e0387ea57c"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
@ -17,7 +17,7 @@
|
||||
</project>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="3802009e1ab6c3ddfc3eb15522e3140a96b33336"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="e1fd99454b6cd5da4f2c58f928fc04c6d03f478f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="b81855b6b67f285d6f27a4f8c1cfe2e0387ea57c"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="f3e998242fb9a857cf50f5bf3a02304a530ea617"/>
|
||||
|
@ -17,7 +17,7 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="3802009e1ab6c3ddfc3eb15522e3140a96b33336"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="e1fd99454b6cd5da4f2c58f928fc04c6d03f478f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="b81855b6b67f285d6f27a4f8c1cfe2e0387ea57c"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
@ -697,12 +697,15 @@ pref("plugin.state.ciscowebcommunicator", 2);
|
||||
pref("plugin.state.npmcafeemss", 2);
|
||||
#endif
|
||||
|
||||
// Cisco VGConnect for directv.com, bug 981403
|
||||
// Cisco VGConnect for directv.com, bug 981403 & bug 1051772
|
||||
#ifdef XP_WIN
|
||||
pref("plugin.state.npplayerplugin", 2);
|
||||
#endif
|
||||
#ifdef XP_MACOSX
|
||||
pref("plugin.state.playerplugin", 2);
|
||||
pref("plugin.state.playerplugin.dtv", 2);
|
||||
pref("plugin.state.playerplugin.ciscodrm", 2);
|
||||
pref("plugin.state.playerplugin.charter", 2);
|
||||
#endif
|
||||
|
||||
// Cisco Jabber Client, bug 981905
|
||||
@ -843,6 +846,15 @@ pref("plugin.state.personalplugin", 2);
|
||||
pref("plugin.state.libplugins", 2);
|
||||
#endif
|
||||
|
||||
// Novell iPrint Client, bug 1036693
|
||||
#ifdef XP_WIN
|
||||
pref("plugin.state.npnipp", 2);
|
||||
pref("plugin.state.npnisp", 2);
|
||||
#endif
|
||||
#ifdef XP_MACOSX
|
||||
pref("plugin.state.iprint", 2);
|
||||
#endif
|
||||
|
||||
#ifdef XP_MACOSX
|
||||
pref("browser.preferences.animateFadeIn", true);
|
||||
#else
|
||||
|
@ -114,7 +114,7 @@ loop.Client = (function($) {
|
||||
} else {
|
||||
sessionType = this.mozLoop.LOOP_SESSION_TYPE.GUEST;
|
||||
}
|
||||
|
||||
|
||||
this.mozLoop.hawkRequest(sessionType, "/call-url/", "POST",
|
||||
{callerId: nickname},
|
||||
function (error, responseText) {
|
||||
|
@ -406,6 +406,7 @@ loop.conversation = (function(OT, mozL10n) {
|
||||
|
||||
/*jshint newcap:false*/
|
||||
this.loadReactComponent(sharedViews.ConversationView({
|
||||
initiate: true,
|
||||
sdk: OT,
|
||||
model: this._conversation,
|
||||
video: {enabled: videoStream}
|
||||
@ -440,7 +441,8 @@ loop.conversation = (function(OT, mozL10n) {
|
||||
});
|
||||
|
||||
this.loadReactComponent(sharedViews.FeedbackView({
|
||||
feedbackApiClient: feedbackClient
|
||||
feedbackApiClient: feedbackClient,
|
||||
onAfterFeedbackReceived: window.close.bind(window)
|
||||
}));
|
||||
}
|
||||
});
|
||||
|
@ -406,6 +406,7 @@ loop.conversation = (function(OT, mozL10n) {
|
||||
|
||||
/*jshint newcap:false*/
|
||||
this.loadReactComponent(sharedViews.ConversationView({
|
||||
initiate: true,
|
||||
sdk: OT,
|
||||
model: this._conversation,
|
||||
video: {enabled: videoStream}
|
||||
@ -440,7 +441,8 @@ loop.conversation = (function(OT, mozL10n) {
|
||||
});
|
||||
|
||||
this.loadReactComponent(sharedViews.FeedbackView({
|
||||
feedbackApiClient: feedbackClient
|
||||
feedbackApiClient: feedbackClient,
|
||||
onAfterFeedbackReceived: window.close.bind(window)
|
||||
}));
|
||||
}
|
||||
});
|
||||
|
@ -51,7 +51,8 @@ loop.FeedbackAPIClient = (function($, _) {
|
||||
"platform",
|
||||
"version",
|
||||
"channel",
|
||||
"user_agent"],
|
||||
"user_agent",
|
||||
"url"],
|
||||
|
||||
/**
|
||||
* Creates a formatted payload object compliant with the Feedback API spec
|
||||
|
@ -30,11 +30,12 @@ loop.shared.views = (function(_, OT, l10n) {
|
||||
scope: React.PropTypes.string.isRequired,
|
||||
type: React.PropTypes.string.isRequired,
|
||||
action: React.PropTypes.func.isRequired,
|
||||
enabled: React.PropTypes.bool.isRequired
|
||||
enabled: React.PropTypes.bool.isRequired,
|
||||
visible: React.PropTypes.bool.isRequired
|
||||
},
|
||||
|
||||
getDefaultProps: function() {
|
||||
return {enabled: true};
|
||||
return {enabled: true, visible: true};
|
||||
},
|
||||
|
||||
handleClick: function() {
|
||||
@ -48,7 +49,8 @@ loop.shared.views = (function(_, OT, l10n) {
|
||||
"btn": true,
|
||||
"media-control": true,
|
||||
"local-media": this.props.scope === "local",
|
||||
"muted": !this.props.enabled
|
||||
"muted": !this.props.enabled,
|
||||
"hide": !this.props.visible
|
||||
};
|
||||
classesObj["btn-mute-" + this.props.type] = true;
|
||||
return cx(classesObj);
|
||||
@ -78,8 +80,8 @@ loop.shared.views = (function(_, OT, l10n) {
|
||||
var ConversationToolbar = React.createClass({displayName: 'ConversationToolbar',
|
||||
getDefaultProps: function() {
|
||||
return {
|
||||
video: {enabled: true},
|
||||
audio: {enabled: true}
|
||||
video: {enabled: true, visible: true},
|
||||
audio: {enabled: true, visible: true}
|
||||
};
|
||||
},
|
||||
|
||||
@ -103,7 +105,7 @@ loop.shared.views = (function(_, OT, l10n) {
|
||||
},
|
||||
|
||||
render: function() {
|
||||
/* jshint ignore:start */
|
||||
var cx = React.addons.classSet;
|
||||
return (
|
||||
React.DOM.ul({className: "conversation-toolbar"},
|
||||
React.DOM.li({className: "conversation-toolbar-btn-box"},
|
||||
@ -115,25 +117,31 @@ loop.shared.views = (function(_, OT, l10n) {
|
||||
React.DOM.li({className: "conversation-toolbar-btn-box"},
|
||||
MediaControlButton({action: this.handleToggleVideo,
|
||||
enabled: this.props.video.enabled,
|
||||
visible: this.props.video.visible,
|
||||
scope: "local", type: "video"})
|
||||
),
|
||||
React.DOM.li({className: "conversation-toolbar-btn-box"},
|
||||
MediaControlButton({action: this.handleToggleAudio,
|
||||
enabled: this.props.audio.enabled,
|
||||
visible: this.props.audio.visible,
|
||||
scope: "local", type: "audio"})
|
||||
)
|
||||
)
|
||||
);
|
||||
/* jshint ignore:end */
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Conversation view.
|
||||
*/
|
||||
var ConversationView = React.createClass({displayName: 'ConversationView',
|
||||
mixins: [Backbone.Events],
|
||||
|
||||
propTypes: {
|
||||
sdk: React.PropTypes.object.isRequired,
|
||||
model: React.PropTypes.object.isRequired
|
||||
video: React.PropTypes.object,
|
||||
audio: React.PropTypes.object,
|
||||
initiate: React.PropTypes.bool
|
||||
},
|
||||
|
||||
// height set to 100%" to fix video layout on Google Chrome
|
||||
@ -149,10 +157,11 @@ loop.shared.views = (function(_, OT, l10n) {
|
||||
}
|
||||
},
|
||||
|
||||
getInitialProps: function() {
|
||||
getDefaultProps: function() {
|
||||
return {
|
||||
video: {enabled: true},
|
||||
audio: {enabled: true}
|
||||
initiate: true,
|
||||
video: {enabled: true, visible: true},
|
||||
audio: {enabled: true, visible: true}
|
||||
};
|
||||
},
|
||||
|
||||
@ -164,26 +173,30 @@ loop.shared.views = (function(_, OT, l10n) {
|
||||
},
|
||||
|
||||
componentWillMount: function() {
|
||||
this.publisherConfig.publishVideo = this.props.video.enabled;
|
||||
if (this.props.initiate) {
|
||||
this.publisherConfig.publishVideo = this.props.video.enabled;
|
||||
}
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
this.listenTo(this.props.model, "session:connected",
|
||||
this.startPublishing);
|
||||
this.listenTo(this.props.model, "session:stream-created",
|
||||
this._streamCreated);
|
||||
this.listenTo(this.props.model, ["session:peer-hungup",
|
||||
"session:network-disconnected",
|
||||
"session:ended"].join(" "),
|
||||
this.stopPublishing);
|
||||
|
||||
this.props.model.startSession();
|
||||
if (this.props.initiate) {
|
||||
this.listenTo(this.props.model, "session:connected",
|
||||
this.startPublishing);
|
||||
this.listenTo(this.props.model, "session:stream-created",
|
||||
this._streamCreated);
|
||||
this.listenTo(this.props.model, ["session:peer-hungup",
|
||||
"session:network-disconnected",
|
||||
"session:ended"].join(" "),
|
||||
this.stopPublishing);
|
||||
this.props.model.startSession();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
},
|
||||
@ -282,10 +295,12 @@ loop.shared.views = (function(_, OT, l10n) {
|
||||
* Unpublishes local stream.
|
||||
*/
|
||||
stopPublishing: function() {
|
||||
// Unregister listeners for publisher events
|
||||
this.stopListening(this.publisher);
|
||||
if (this.publisher) {
|
||||
// Unregister listeners for publisher events
|
||||
this.stopListening(this.publisher);
|
||||
|
||||
this.props.model.session.unpublish(this.publisher);
|
||||
this.props.model.session.unpublish(this.publisher);
|
||||
}
|
||||
},
|
||||
|
||||
render: function() {
|
||||
@ -362,7 +377,7 @@ loop.shared.views = (function(_, OT, l10n) {
|
||||
return {category: "", description: ""};
|
||||
},
|
||||
|
||||
getInitialProps: function() {
|
||||
getDefaultProps: function() {
|
||||
return {pending: false};
|
||||
},
|
||||
|
||||
@ -467,8 +482,16 @@ loop.shared.views = (function(_, OT, l10n) {
|
||||
|
||||
/**
|
||||
* Feedback received view.
|
||||
*
|
||||
* Props:
|
||||
* - {Function} onAfterFeedbackReceived Function to execute after the
|
||||
* WINDOW_AUTOCLOSE_TIMEOUT_IN_SECONDS timeout has elapsed
|
||||
*/
|
||||
var FeedbackReceived = React.createClass({displayName: 'FeedbackReceived',
|
||||
propTypes: {
|
||||
onAfterFeedbackReceived: React.PropTypes.func
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
return {countdown: WINDOW_AUTOCLOSE_TIMEOUT_IN_SECONDS};
|
||||
},
|
||||
@ -488,7 +511,9 @@ loop.shared.views = (function(_, OT, l10n) {
|
||||
render: function() {
|
||||
if (this.state.countdown < 1) {
|
||||
clearInterval(this._timer);
|
||||
window.close();
|
||||
if (this.props.onAfterFeedbackReceived) {
|
||||
this.props.onAfterFeedbackReceived();
|
||||
}
|
||||
}
|
||||
return (
|
||||
FeedbackLayout({title: l10n.get("feedback_thank_you_heading")},
|
||||
@ -509,6 +534,7 @@ loop.shared.views = (function(_, OT, l10n) {
|
||||
propTypes: {
|
||||
// A loop.FeedbackAPIClient instance
|
||||
feedbackApiClient: React.PropTypes.object.isRequired,
|
||||
onAfterFeedbackReceived: React.PropTypes.func,
|
||||
// The current feedback submission flow step name
|
||||
step: React.PropTypes.oneOf(["start", "form", "finished"])
|
||||
},
|
||||
@ -517,7 +543,7 @@ loop.shared.views = (function(_, OT, l10n) {
|
||||
return {pending: false, step: this.props.step || "start"};
|
||||
},
|
||||
|
||||
getInitialProps: function() {
|
||||
getDefaultProps: function() {
|
||||
return {step: "start"};
|
||||
},
|
||||
|
||||
@ -552,7 +578,10 @@ loop.shared.views = (function(_, OT, l10n) {
|
||||
render: function() {
|
||||
switch(this.state.step) {
|
||||
case "finished":
|
||||
return FeedbackReceived(null);
|
||||
return (
|
||||
FeedbackReceived({
|
||||
onAfterFeedbackReceived: this.props.onAfterFeedbackReceived})
|
||||
);
|
||||
case "form":
|
||||
return FeedbackForm({feedbackApiClient: this.props.feedbackApiClient,
|
||||
sendFeedback: this.sendFeedback,
|
||||
|
@ -30,11 +30,12 @@ loop.shared.views = (function(_, OT, l10n) {
|
||||
scope: React.PropTypes.string.isRequired,
|
||||
type: React.PropTypes.string.isRequired,
|
||||
action: React.PropTypes.func.isRequired,
|
||||
enabled: React.PropTypes.bool.isRequired
|
||||
enabled: React.PropTypes.bool.isRequired,
|
||||
visible: React.PropTypes.bool.isRequired
|
||||
},
|
||||
|
||||
getDefaultProps: function() {
|
||||
return {enabled: true};
|
||||
return {enabled: true, visible: true};
|
||||
},
|
||||
|
||||
handleClick: function() {
|
||||
@ -48,7 +49,8 @@ loop.shared.views = (function(_, OT, l10n) {
|
||||
"btn": true,
|
||||
"media-control": true,
|
||||
"local-media": this.props.scope === "local",
|
||||
"muted": !this.props.enabled
|
||||
"muted": !this.props.enabled,
|
||||
"hide": !this.props.visible
|
||||
};
|
||||
classesObj["btn-mute-" + this.props.type] = true;
|
||||
return cx(classesObj);
|
||||
@ -78,8 +80,8 @@ loop.shared.views = (function(_, OT, l10n) {
|
||||
var ConversationToolbar = React.createClass({
|
||||
getDefaultProps: function() {
|
||||
return {
|
||||
video: {enabled: true},
|
||||
audio: {enabled: true}
|
||||
video: {enabled: true, visible: true},
|
||||
audio: {enabled: true, visible: true}
|
||||
};
|
||||
},
|
||||
|
||||
@ -103,7 +105,7 @@ loop.shared.views = (function(_, OT, l10n) {
|
||||
},
|
||||
|
||||
render: function() {
|
||||
/* jshint ignore:start */
|
||||
var cx = React.addons.classSet;
|
||||
return (
|
||||
<ul className="conversation-toolbar">
|
||||
<li className="conversation-toolbar-btn-box">
|
||||
@ -115,25 +117,31 @@ loop.shared.views = (function(_, OT, l10n) {
|
||||
<li className="conversation-toolbar-btn-box">
|
||||
<MediaControlButton action={this.handleToggleVideo}
|
||||
enabled={this.props.video.enabled}
|
||||
visible={this.props.video.visible}
|
||||
scope="local" type="video" />
|
||||
</li>
|
||||
<li className="conversation-toolbar-btn-box">
|
||||
<MediaControlButton action={this.handleToggleAudio}
|
||||
enabled={this.props.audio.enabled}
|
||||
visible={this.props.audio.visible}
|
||||
scope="local" type="audio" />
|
||||
</li>
|
||||
</ul>
|
||||
);
|
||||
/* jshint ignore:end */
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Conversation view.
|
||||
*/
|
||||
var ConversationView = React.createClass({
|
||||
mixins: [Backbone.Events],
|
||||
|
||||
propTypes: {
|
||||
sdk: React.PropTypes.object.isRequired,
|
||||
model: React.PropTypes.object.isRequired
|
||||
video: React.PropTypes.object,
|
||||
audio: React.PropTypes.object,
|
||||
initiate: React.PropTypes.bool
|
||||
},
|
||||
|
||||
// height set to 100%" to fix video layout on Google Chrome
|
||||
@ -149,10 +157,11 @@ loop.shared.views = (function(_, OT, l10n) {
|
||||
}
|
||||
},
|
||||
|
||||
getInitialProps: function() {
|
||||
getDefaultProps: function() {
|
||||
return {
|
||||
video: {enabled: true},
|
||||
audio: {enabled: true}
|
||||
initiate: true,
|
||||
video: {enabled: true, visible: true},
|
||||
audio: {enabled: true, visible: true}
|
||||
};
|
||||
},
|
||||
|
||||
@ -164,26 +173,30 @@ loop.shared.views = (function(_, OT, l10n) {
|
||||
},
|
||||
|
||||
componentWillMount: function() {
|
||||
this.publisherConfig.publishVideo = this.props.video.enabled;
|
||||
if (this.props.initiate) {
|
||||
this.publisherConfig.publishVideo = this.props.video.enabled;
|
||||
}
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
this.listenTo(this.props.model, "session:connected",
|
||||
this.startPublishing);
|
||||
this.listenTo(this.props.model, "session:stream-created",
|
||||
this._streamCreated);
|
||||
this.listenTo(this.props.model, ["session:peer-hungup",
|
||||
"session:network-disconnected",
|
||||
"session:ended"].join(" "),
|
||||
this.stopPublishing);
|
||||
|
||||
this.props.model.startSession();
|
||||
if (this.props.initiate) {
|
||||
this.listenTo(this.props.model, "session:connected",
|
||||
this.startPublishing);
|
||||
this.listenTo(this.props.model, "session:stream-created",
|
||||
this._streamCreated);
|
||||
this.listenTo(this.props.model, ["session:peer-hungup",
|
||||
"session:network-disconnected",
|
||||
"session:ended"].join(" "),
|
||||
this.stopPublishing);
|
||||
this.props.model.startSession();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
},
|
||||
@ -282,10 +295,12 @@ loop.shared.views = (function(_, OT, l10n) {
|
||||
* Unpublishes local stream.
|
||||
*/
|
||||
stopPublishing: function() {
|
||||
// Unregister listeners for publisher events
|
||||
this.stopListening(this.publisher);
|
||||
if (this.publisher) {
|
||||
// Unregister listeners for publisher events
|
||||
this.stopListening(this.publisher);
|
||||
|
||||
this.props.model.session.unpublish(this.publisher);
|
||||
this.props.model.session.unpublish(this.publisher);
|
||||
}
|
||||
},
|
||||
|
||||
render: function() {
|
||||
@ -362,7 +377,7 @@ loop.shared.views = (function(_, OT, l10n) {
|
||||
return {category: "", description: ""};
|
||||
},
|
||||
|
||||
getInitialProps: function() {
|
||||
getDefaultProps: function() {
|
||||
return {pending: false};
|
||||
},
|
||||
|
||||
@ -467,8 +482,16 @@ loop.shared.views = (function(_, OT, l10n) {
|
||||
|
||||
/**
|
||||
* Feedback received view.
|
||||
*
|
||||
* Props:
|
||||
* - {Function} onAfterFeedbackReceived Function to execute after the
|
||||
* WINDOW_AUTOCLOSE_TIMEOUT_IN_SECONDS timeout has elapsed
|
||||
*/
|
||||
var FeedbackReceived = React.createClass({
|
||||
propTypes: {
|
||||
onAfterFeedbackReceived: React.PropTypes.func
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
return {countdown: WINDOW_AUTOCLOSE_TIMEOUT_IN_SECONDS};
|
||||
},
|
||||
@ -488,7 +511,9 @@ loop.shared.views = (function(_, OT, l10n) {
|
||||
render: function() {
|
||||
if (this.state.countdown < 1) {
|
||||
clearInterval(this._timer);
|
||||
window.close();
|
||||
if (this.props.onAfterFeedbackReceived) {
|
||||
this.props.onAfterFeedbackReceived();
|
||||
}
|
||||
}
|
||||
return (
|
||||
<FeedbackLayout title={l10n.get("feedback_thank_you_heading")}>
|
||||
@ -509,6 +534,7 @@ loop.shared.views = (function(_, OT, l10n) {
|
||||
propTypes: {
|
||||
// A loop.FeedbackAPIClient instance
|
||||
feedbackApiClient: React.PropTypes.object.isRequired,
|
||||
onAfterFeedbackReceived: React.PropTypes.func,
|
||||
// The current feedback submission flow step name
|
||||
step: React.PropTypes.oneOf(["start", "form", "finished"])
|
||||
},
|
||||
@ -517,7 +543,7 @@ loop.shared.views = (function(_, OT, l10n) {
|
||||
return {pending: false, step: this.props.step || "start"};
|
||||
},
|
||||
|
||||
getInitialProps: function() {
|
||||
getDefaultProps: function() {
|
||||
return {step: "start"};
|
||||
},
|
||||
|
||||
@ -552,7 +578,10 @@ loop.shared.views = (function(_, OT, l10n) {
|
||||
render: function() {
|
||||
switch(this.state.step) {
|
||||
case "finished":
|
||||
return <FeedbackReceived />;
|
||||
return (
|
||||
<FeedbackReceived
|
||||
onAfterFeedbackReceived={this.props.onAfterFeedbackReceived} />
|
||||
);
|
||||
case "form":
|
||||
return <FeedbackForm feedbackApiClient={this.props.feedbackApiClient}
|
||||
sendFeedback={this.sendFeedback}
|
||||
|
@ -13,6 +13,9 @@
|
||||
# to the Gruntfile and getting rid of this Makefile entirely.
|
||||
|
||||
LOOP_SERVER_URL := $(shell echo $${LOOP_SERVER_URL-http://localhost:5000})
|
||||
LOOP_FEEDBACK_API_URL := $(shell echo $${LOOP_FEEDBACK_API_URL-"https://input.allizom.org/api/v1/feedback"})
|
||||
LOOP_FEEDBACK_PRODUCT_NAME := $(shell echo $${LOOP_FEEDBACK_PRODUCT_NAME-Loop})
|
||||
|
||||
NODE_LOCAL_BIN=./node_modules/.bin
|
||||
|
||||
install: npm_install tos
|
||||
@ -67,4 +70,6 @@ remove_old_config:
|
||||
config:
|
||||
@echo "var loop = loop || {};" > content/config.js
|
||||
@echo "loop.config = loop.config || {};" >> content/config.js
|
||||
@echo "loop.config.serverUrl = '`echo $(LOOP_SERVER_URL)`';" >> content/config.js
|
||||
@echo "loop.config.serverUrl = '`echo $(LOOP_SERVER_URL)`';" >> content/config.js
|
||||
@echo "loop.config.feedbackApiUrl = '`echo $(LOOP_FEEDBACK_API_URL)`';" >> content/config.js
|
||||
@echo "loop.config.feedbackProductName = '`echo $(LOOP_FEEDBACK_PRODUCT_NAME)`';" >> content/config.js
|
||||
|
@ -29,8 +29,12 @@ appropriate configuration file:
|
||||
|
||||
- `LOOP_SERVER_URL` defines the root url of the loop server, without trailing
|
||||
slash (default: `http://localhost:5000`).
|
||||
- `LOOP_PENDING_CALL_TIMEOUT` defines the amount of time a pending outgoing call
|
||||
should be considered timed out, in milliseconds (default: `20000`).
|
||||
- `LOOP_FEEDBACK_API_URL` sets the root URL for the
|
||||
[input API](https://input.mozilla.org/); defaults to the input stage server
|
||||
(https://input.allizom.org/api/v1/feedback). **Don't forget to set this
|
||||
value to the production server URL when deploying to production.**
|
||||
- `LOOP_FEEDBACK_PRODUCT_NAME` defines the product name to be sent to the input
|
||||
API (defaults: Loop).
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
@ -207,3 +207,41 @@ body,
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Feedback form overlay (standalone only)
|
||||
*/
|
||||
.standalone .ended-conversation {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
background-color: #444;
|
||||
text-align: left; /* as backup */
|
||||
text-align: start;
|
||||
}
|
||||
|
||||
.standalone .ended-conversation .feedback {
|
||||
position: absolute;
|
||||
width: 50%;
|
||||
max-width: 400px;
|
||||
margin: 10px auto;
|
||||
top: 20px;
|
||||
left: 10%;
|
||||
right: 10%;
|
||||
background: #FFF;
|
||||
box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.4);
|
||||
border-radius: 3px;
|
||||
z-index: 1002; /* ensures the form is always on top of the control bar */
|
||||
}
|
||||
|
||||
.standalone .ended-conversation .local-stream {
|
||||
/* Hide local media stream when feedback form is shown. */
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media screen and (max-width:640px) {
|
||||
.standalone .ended-conversation .feedback {
|
||||
width: 92%;
|
||||
top: 10%;
|
||||
left: 5px;
|
||||
right: 5px;
|
||||
}
|
||||
}
|
||||
|
@ -41,6 +41,7 @@
|
||||
<script type="text/javascript" src="shared/js/models.js"></script>
|
||||
<script type="text/javascript" src="shared/js/mixins.js"></script>
|
||||
<script type="text/javascript" src="shared/js/views.js"></script>
|
||||
<script type="text/javascript" src="shared/js/feedbackApiClient.js"></script>
|
||||
<script type="text/javascript" src="shared/js/websocket.js"></script>
|
||||
<script type="text/javascript" src="js/standaloneClient.js"></script>
|
||||
<script type="text/javascript" src="js/webapp.js"></script>
|
||||
|
@ -122,7 +122,7 @@ loop.StandaloneClient = (function($) {
|
||||
try {
|
||||
cb(null, this._validate(sessionData, expectedCallsProperties));
|
||||
} catch (err) {
|
||||
console.log("Error requesting call info", err);
|
||||
console.error("Error requesting call info", err.message);
|
||||
cb(err);
|
||||
}
|
||||
}.bind(this));
|
||||
|
@ -5,7 +5,7 @@
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/* global loop:true, React */
|
||||
/* jshint newcap:false */
|
||||
/* jshint newcap:false, maxlen:false */
|
||||
|
||||
var loop = loop || {};
|
||||
loop.webapp = (function($, _, OT, mozL10n) {
|
||||
@ -17,12 +17,6 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
var sharedModels = loop.shared.models,
|
||||
sharedViews = loop.shared.views;
|
||||
|
||||
/**
|
||||
* App router.
|
||||
* @type {loop.webapp.WebappRouter}
|
||||
*/
|
||||
var router;
|
||||
|
||||
/**
|
||||
* Homepage view.
|
||||
*/
|
||||
@ -30,7 +24,7 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
render: function() {
|
||||
return (
|
||||
React.DOM.p(null, mozL10n.get("welcome"))
|
||||
)
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@ -104,7 +98,6 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
},
|
||||
|
||||
render: function() {
|
||||
/* jshint ignore:start */
|
||||
return (
|
||||
React.DOM.div({className: "expired-url-info"},
|
||||
React.DOM.div({className: "info-panel"},
|
||||
@ -115,7 +108,6 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
PromoteFirefoxView({helper: this.props.helper})
|
||||
)
|
||||
);
|
||||
/* jshint ignore:end */
|
||||
}
|
||||
});
|
||||
|
||||
@ -146,7 +138,6 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
});
|
||||
|
||||
return (
|
||||
/* jshint ignore:start */
|
||||
React.DOM.header({className: "standalone-header header-box container-box"},
|
||||
ConversationBranding(null),
|
||||
React.DOM.div({className: "loop-logo", title: "Firefox WebRTC! logo"}),
|
||||
@ -157,7 +148,6 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
callUrlCreationDateString
|
||||
)
|
||||
)
|
||||
/* jshint ignore:end */
|
||||
);
|
||||
}
|
||||
});
|
||||
@ -176,7 +166,7 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
getInitialState: function() {
|
||||
return {
|
||||
callState: this.props.callState || "connecting"
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
propTypes: {
|
||||
@ -200,7 +190,6 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
render: function() {
|
||||
var callState = mozL10n.get("call_progress_" + this.state.callState + "_description");
|
||||
return (
|
||||
/* jshint ignore:start */
|
||||
React.DOM.div({className: "container"},
|
||||
React.DOM.div({className: "container-box"},
|
||||
React.DOM.header({className: "pending-header header-box"},
|
||||
@ -229,7 +218,6 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
|
||||
ConversationFooter(null)
|
||||
)
|
||||
/* jshint ignore:end */
|
||||
);
|
||||
}
|
||||
});
|
||||
@ -237,18 +225,21 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
/**
|
||||
* Conversation launcher view. A ConversationModel is associated and attached
|
||||
* as a `model` property.
|
||||
*
|
||||
* Required properties:
|
||||
* - {loop.shared.models.ConversationModel} model Conversation model.
|
||||
* - {loop.shared.models.NotificationCollection} notifications
|
||||
*/
|
||||
var StartConversationView = React.createClass({displayName: 'StartConversationView',
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* Required options:
|
||||
* - {loop.shared.models.ConversationModel} model Conversation model.
|
||||
* - {loop.shared.models.NotificationCollection} notifications
|
||||
*
|
||||
*/
|
||||
propTypes: {
|
||||
model: React.PropTypes.instanceOf(sharedModels.ConversationModel)
|
||||
.isRequired,
|
||||
// XXX Check more tightly here when we start injecting window.loop.*
|
||||
notifications: React.PropTypes.object.isRequired,
|
||||
client: React.PropTypes.object.isRequired
|
||||
},
|
||||
|
||||
getInitialProps: function() {
|
||||
getDefaultProps: function() {
|
||||
return {showCallOptionsMenu: false};
|
||||
},
|
||||
|
||||
@ -260,14 +251,6 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
};
|
||||
},
|
||||
|
||||
propTypes: {
|
||||
model: React.PropTypes.instanceOf(sharedModels.ConversationModel)
|
||||
.isRequired,
|
||||
// XXX Check more tightly here when we start injecting window.loop.*
|
||||
notifications: React.PropTypes.object.isRequired,
|
||||
client: React.PropTypes.object.isRequired
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
// Listen for events & hide dropdown menu if user clicks away
|
||||
window.addEventListener("click", this.clickHandler);
|
||||
@ -348,7 +331,6 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
});
|
||||
|
||||
return (
|
||||
/* jshint ignore:start */
|
||||
React.DOM.div({className: "container"},
|
||||
React.DOM.div({className: "container-box"},
|
||||
|
||||
@ -407,7 +389,37 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
|
||||
ConversationFooter(null)
|
||||
)
|
||||
/* jshint ignore:end */
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Ended conversation view.
|
||||
*/
|
||||
var EndedConversationView = React.createClass({displayName: 'EndedConversationView',
|
||||
propTypes: {
|
||||
conversation: React.PropTypes.instanceOf(sharedModels.ConversationModel)
|
||||
.isRequired,
|
||||
sdk: React.PropTypes.object.isRequired,
|
||||
feedbackApiClient: React.PropTypes.object.isRequired,
|
||||
onAfterFeedbackReceived: React.PropTypes.func.isRequired
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
React.DOM.div({className: "ended-conversation"},
|
||||
sharedViews.FeedbackView({
|
||||
feedbackApiClient: this.props.feedbackApiClient,
|
||||
onAfterFeedbackReceived: this.props.onAfterFeedbackReceived}
|
||||
),
|
||||
sharedViews.ConversationView({
|
||||
initiate: false,
|
||||
sdk: this.props.sdk,
|
||||
model: this.props.conversation,
|
||||
audio: {enabled: false, visible: false},
|
||||
video: {enabled: false, visible: false}}
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
});
|
||||
@ -426,7 +438,8 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
helper: React.PropTypes.instanceOf(WebappHelper).isRequired,
|
||||
notifications: React.PropTypes.instanceOf(sharedModels.NotificationCollection)
|
||||
.isRequired,
|
||||
sdk: React.PropTypes.object.isRequired
|
||||
sdk: React.PropTypes.object.isRequired,
|
||||
feedbackApiClient: React.PropTypes.object.isRequired
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
@ -450,13 +463,23 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
this.props.conversation.off(null, null, this);
|
||||
},
|
||||
|
||||
shouldComponentUpdate: function(nextProps, nextState) {
|
||||
// Only rerender if current state has actually changed
|
||||
return nextState.callStatus !== this.state.callStatus;
|
||||
},
|
||||
|
||||
callStatusSwitcher: function(status) {
|
||||
return function() {
|
||||
this.setState({callStatus: status});
|
||||
}.bind(this);
|
||||
},
|
||||
|
||||
/**
|
||||
* Renders the conversation views.
|
||||
*/
|
||||
render: function() {
|
||||
switch (this.state.callStatus) {
|
||||
case "failure":
|
||||
case "end":
|
||||
case "start": {
|
||||
return (
|
||||
StartConversationView({
|
||||
@ -472,19 +495,30 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
case "connected": {
|
||||
return (
|
||||
sharedViews.ConversationView({
|
||||
initiate: true,
|
||||
sdk: this.props.sdk,
|
||||
model: this.props.conversation,
|
||||
video: {enabled: this.props.conversation.hasVideoStream("outgoing")}}
|
||||
)
|
||||
);
|
||||
}
|
||||
case "end": {
|
||||
return (
|
||||
EndedConversationView({
|
||||
sdk: this.props.sdk,
|
||||
conversation: this.props.conversation,
|
||||
feedbackApiClient: this.props.feedbackApiClient,
|
||||
onAfterFeedbackReceived: this.callStatusSwitcher("start")}
|
||||
)
|
||||
);
|
||||
}
|
||||
case "expired": {
|
||||
return (
|
||||
CallUrlExpiredView({helper: this.props.helper})
|
||||
);
|
||||
}
|
||||
default: {
|
||||
return HomeView(null)
|
||||
return HomeView(null);
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -494,7 +528,7 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
* @param {{code: number, message: string}} error
|
||||
*/
|
||||
_notifyError: function(error) {
|
||||
console.log(error);
|
||||
console.error(error);
|
||||
this.props.notifications.errorL10n("connection_error_see_console_notification");
|
||||
this.setState({callStatus: "end"});
|
||||
},
|
||||
@ -628,13 +662,15 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
* @param {String} reason The reason the call was terminated.
|
||||
*/
|
||||
_handleCallTerminated: function(reason) {
|
||||
this.setState({callStatus: "end"});
|
||||
// For reasons other than cancel, display some notification text.
|
||||
if (reason !== "cancel") {
|
||||
// XXX This should really display the call failed view - bug 1046959
|
||||
// will implement this.
|
||||
this.props.notifications.errorL10n("call_timeout_notification_text");
|
||||
}
|
||||
// redirects the user to the call start view
|
||||
// XXX should switch callStatus to failed for specific reasons when we
|
||||
// get the call failed view; for now, switch back to start.
|
||||
this.setState({callStatus: "start"});
|
||||
},
|
||||
|
||||
/**
|
||||
@ -657,7 +693,8 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
helper: React.PropTypes.instanceOf(WebappHelper).isRequired,
|
||||
notifications: React.PropTypes.instanceOf(sharedModels.NotificationCollection)
|
||||
.isRequired,
|
||||
sdk: React.PropTypes.object.isRequired
|
||||
sdk: React.PropTypes.object.isRequired,
|
||||
feedbackApiClient: React.PropTypes.object.isRequired
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
@ -679,7 +716,8 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
conversation: this.props.conversation,
|
||||
helper: this.props.helper,
|
||||
notifications: this.props.notifications,
|
||||
sdk: this.props.sdk}
|
||||
sdk: this.props.sdk,
|
||||
feedbackApiClient: this.props.feedbackApiClient}
|
||||
)
|
||||
);
|
||||
} else {
|
||||
@ -721,6 +759,12 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
var conversation = new sharedModels.ConversationModel({}, {
|
||||
sdk: OT
|
||||
});
|
||||
var feedbackApiClient = new loop.FeedbackAPIClient(
|
||||
loop.config.feedbackApiUrl, {
|
||||
product: loop.config.feedbackProductName,
|
||||
user_agent: navigator.userAgent,
|
||||
url: document.location.origin
|
||||
});
|
||||
|
||||
// Obtain the loopToken and pass it to the conversation
|
||||
var locationHash = helper.locationHash();
|
||||
@ -733,7 +777,8 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
conversation: conversation,
|
||||
helper: helper,
|
||||
notifications: notifications,
|
||||
sdk: OT}
|
||||
sdk: OT,
|
||||
feedbackApiClient: feedbackApiClient}
|
||||
), document.querySelector("#main"));
|
||||
|
||||
// Set the 'lang' and 'dir' attributes to <html> when the page is translated
|
||||
@ -746,6 +791,7 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
PendingConversationView: PendingConversationView,
|
||||
StartConversationView: StartConversationView,
|
||||
OutgoingConversationView: OutgoingConversationView,
|
||||
EndedConversationView: EndedConversationView,
|
||||
HomeView: HomeView,
|
||||
UnsupportedBrowserView: UnsupportedBrowserView,
|
||||
UnsupportedDeviceView: UnsupportedDeviceView,
|
||||
|
@ -5,7 +5,7 @@
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/* global loop:true, React */
|
||||
/* jshint newcap:false */
|
||||
/* jshint newcap:false, maxlen:false */
|
||||
|
||||
var loop = loop || {};
|
||||
loop.webapp = (function($, _, OT, mozL10n) {
|
||||
@ -17,12 +17,6 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
var sharedModels = loop.shared.models,
|
||||
sharedViews = loop.shared.views;
|
||||
|
||||
/**
|
||||
* App router.
|
||||
* @type {loop.webapp.WebappRouter}
|
||||
*/
|
||||
var router;
|
||||
|
||||
/**
|
||||
* Homepage view.
|
||||
*/
|
||||
@ -30,7 +24,7 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
render: function() {
|
||||
return (
|
||||
<p>{mozL10n.get("welcome")}</p>
|
||||
)
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@ -104,7 +98,6 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
},
|
||||
|
||||
render: function() {
|
||||
/* jshint ignore:start */
|
||||
return (
|
||||
<div className="expired-url-info">
|
||||
<div className="info-panel">
|
||||
@ -115,7 +108,6 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
<PromoteFirefoxView helper={this.props.helper} />
|
||||
</div>
|
||||
);
|
||||
/* jshint ignore:end */
|
||||
}
|
||||
});
|
||||
|
||||
@ -146,7 +138,6 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
});
|
||||
|
||||
return (
|
||||
/* jshint ignore:start */
|
||||
<header className="standalone-header header-box container-box">
|
||||
<ConversationBranding />
|
||||
<div className="loop-logo" title="Firefox WebRTC! logo"></div>
|
||||
@ -157,7 +148,6 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
{callUrlCreationDateString}
|
||||
</h4>
|
||||
</header>
|
||||
/* jshint ignore:end */
|
||||
);
|
||||
}
|
||||
});
|
||||
@ -176,7 +166,7 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
getInitialState: function() {
|
||||
return {
|
||||
callState: this.props.callState || "connecting"
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
propTypes: {
|
||||
@ -200,7 +190,6 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
render: function() {
|
||||
var callState = mozL10n.get("call_progress_" + this.state.callState + "_description");
|
||||
return (
|
||||
/* jshint ignore:start */
|
||||
<div className="container">
|
||||
<div className="container-box">
|
||||
<header className="pending-header header-box">
|
||||
@ -229,7 +218,6 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
|
||||
<ConversationFooter />
|
||||
</div>
|
||||
/* jshint ignore:end */
|
||||
);
|
||||
}
|
||||
});
|
||||
@ -237,18 +225,21 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
/**
|
||||
* Conversation launcher view. A ConversationModel is associated and attached
|
||||
* as a `model` property.
|
||||
*
|
||||
* Required properties:
|
||||
* - {loop.shared.models.ConversationModel} model Conversation model.
|
||||
* - {loop.shared.models.NotificationCollection} notifications
|
||||
*/
|
||||
var StartConversationView = React.createClass({
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* Required options:
|
||||
* - {loop.shared.models.ConversationModel} model Conversation model.
|
||||
* - {loop.shared.models.NotificationCollection} notifications
|
||||
*
|
||||
*/
|
||||
propTypes: {
|
||||
model: React.PropTypes.instanceOf(sharedModels.ConversationModel)
|
||||
.isRequired,
|
||||
// XXX Check more tightly here when we start injecting window.loop.*
|
||||
notifications: React.PropTypes.object.isRequired,
|
||||
client: React.PropTypes.object.isRequired
|
||||
},
|
||||
|
||||
getInitialProps: function() {
|
||||
getDefaultProps: function() {
|
||||
return {showCallOptionsMenu: false};
|
||||
},
|
||||
|
||||
@ -260,14 +251,6 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
};
|
||||
},
|
||||
|
||||
propTypes: {
|
||||
model: React.PropTypes.instanceOf(sharedModels.ConversationModel)
|
||||
.isRequired,
|
||||
// XXX Check more tightly here when we start injecting window.loop.*
|
||||
notifications: React.PropTypes.object.isRequired,
|
||||
client: React.PropTypes.object.isRequired
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
// Listen for events & hide dropdown menu if user clicks away
|
||||
window.addEventListener("click", this.clickHandler);
|
||||
@ -348,7 +331,6 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
});
|
||||
|
||||
return (
|
||||
/* jshint ignore:start */
|
||||
<div className="container">
|
||||
<div className="container-box">
|
||||
|
||||
@ -407,7 +389,37 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
|
||||
<ConversationFooter />
|
||||
</div>
|
||||
/* jshint ignore:end */
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Ended conversation view.
|
||||
*/
|
||||
var EndedConversationView = React.createClass({
|
||||
propTypes: {
|
||||
conversation: React.PropTypes.instanceOf(sharedModels.ConversationModel)
|
||||
.isRequired,
|
||||
sdk: React.PropTypes.object.isRequired,
|
||||
feedbackApiClient: React.PropTypes.object.isRequired,
|
||||
onAfterFeedbackReceived: React.PropTypes.func.isRequired
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
<div className="ended-conversation">
|
||||
<sharedViews.FeedbackView
|
||||
feedbackApiClient={this.props.feedbackApiClient}
|
||||
onAfterFeedbackReceived={this.props.onAfterFeedbackReceived}
|
||||
/>
|
||||
<sharedViews.ConversationView
|
||||
initiate={false}
|
||||
sdk={this.props.sdk}
|
||||
model={this.props.conversation}
|
||||
audio={{enabled: false, visible: false}}
|
||||
video={{enabled: false, visible: false}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
@ -426,7 +438,8 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
helper: React.PropTypes.instanceOf(WebappHelper).isRequired,
|
||||
notifications: React.PropTypes.instanceOf(sharedModels.NotificationCollection)
|
||||
.isRequired,
|
||||
sdk: React.PropTypes.object.isRequired
|
||||
sdk: React.PropTypes.object.isRequired,
|
||||
feedbackApiClient: React.PropTypes.object.isRequired
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
@ -450,13 +463,23 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
this.props.conversation.off(null, null, this);
|
||||
},
|
||||
|
||||
shouldComponentUpdate: function(nextProps, nextState) {
|
||||
// Only rerender if current state has actually changed
|
||||
return nextState.callStatus !== this.state.callStatus;
|
||||
},
|
||||
|
||||
callStatusSwitcher: function(status) {
|
||||
return function() {
|
||||
this.setState({callStatus: status});
|
||||
}.bind(this);
|
||||
},
|
||||
|
||||
/**
|
||||
* Renders the conversation views.
|
||||
*/
|
||||
render: function() {
|
||||
switch (this.state.callStatus) {
|
||||
case "failure":
|
||||
case "end":
|
||||
case "start": {
|
||||
return (
|
||||
<StartConversationView
|
||||
@ -472,19 +495,30 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
case "connected": {
|
||||
return (
|
||||
<sharedViews.ConversationView
|
||||
initiate={true}
|
||||
sdk={this.props.sdk}
|
||||
model={this.props.conversation}
|
||||
video={{enabled: this.props.conversation.hasVideoStream("outgoing")}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
case "end": {
|
||||
return (
|
||||
<EndedConversationView
|
||||
sdk={this.props.sdk}
|
||||
conversation={this.props.conversation}
|
||||
feedbackApiClient={this.props.feedbackApiClient}
|
||||
onAfterFeedbackReceived={this.callStatusSwitcher("start")}
|
||||
/>
|
||||
);
|
||||
}
|
||||
case "expired": {
|
||||
return (
|
||||
<CallUrlExpiredView helper={this.props.helper} />
|
||||
);
|
||||
}
|
||||
default: {
|
||||
return <HomeView />
|
||||
return <HomeView />;
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -494,7 +528,7 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
* @param {{code: number, message: string}} error
|
||||
*/
|
||||
_notifyError: function(error) {
|
||||
console.log(error);
|
||||
console.error(error);
|
||||
this.props.notifications.errorL10n("connection_error_see_console_notification");
|
||||
this.setState({callStatus: "end"});
|
||||
},
|
||||
@ -628,13 +662,15 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
* @param {String} reason The reason the call was terminated.
|
||||
*/
|
||||
_handleCallTerminated: function(reason) {
|
||||
this.setState({callStatus: "end"});
|
||||
// For reasons other than cancel, display some notification text.
|
||||
if (reason !== "cancel") {
|
||||
// XXX This should really display the call failed view - bug 1046959
|
||||
// will implement this.
|
||||
this.props.notifications.errorL10n("call_timeout_notification_text");
|
||||
}
|
||||
// redirects the user to the call start view
|
||||
// XXX should switch callStatus to failed for specific reasons when we
|
||||
// get the call failed view; for now, switch back to start.
|
||||
this.setState({callStatus: "start"});
|
||||
},
|
||||
|
||||
/**
|
||||
@ -657,7 +693,8 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
helper: React.PropTypes.instanceOf(WebappHelper).isRequired,
|
||||
notifications: React.PropTypes.instanceOf(sharedModels.NotificationCollection)
|
||||
.isRequired,
|
||||
sdk: React.PropTypes.object.isRequired
|
||||
sdk: React.PropTypes.object.isRequired,
|
||||
feedbackApiClient: React.PropTypes.object.isRequired
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
@ -680,6 +717,7 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
helper={this.props.helper}
|
||||
notifications={this.props.notifications}
|
||||
sdk={this.props.sdk}
|
||||
feedbackApiClient={this.props.feedbackApiClient}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
@ -721,6 +759,12 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
var conversation = new sharedModels.ConversationModel({}, {
|
||||
sdk: OT
|
||||
});
|
||||
var feedbackApiClient = new loop.FeedbackAPIClient(
|
||||
loop.config.feedbackApiUrl, {
|
||||
product: loop.config.feedbackProductName,
|
||||
user_agent: navigator.userAgent,
|
||||
url: document.location.origin
|
||||
});
|
||||
|
||||
// Obtain the loopToken and pass it to the conversation
|
||||
var locationHash = helper.locationHash();
|
||||
@ -734,6 +778,7 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
helper={helper}
|
||||
notifications={notifications}
|
||||
sdk={OT}
|
||||
feedbackApiClient={feedbackApiClient}
|
||||
/>, document.querySelector("#main"));
|
||||
|
||||
// Set the 'lang' and 'dir' attributes to <html> when the page is translated
|
||||
@ -746,6 +791,7 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
PendingConversationView: PendingConversationView,
|
||||
StartConversationView: StartConversationView,
|
||||
OutgoingConversationView: OutgoingConversationView,
|
||||
EndedConversationView: EndedConversationView,
|
||||
HomeView: HomeView,
|
||||
UnsupportedBrowserView: UnsupportedBrowserView,
|
||||
UnsupportedDeviceView: UnsupportedDeviceView,
|
||||
|
@ -46,3 +46,32 @@ clientShortname=WebRTC!
|
||||
call_url_creation_date_label=(from {{call_url_creation_date}})
|
||||
call_progress_connecting_description=Connecting…
|
||||
call_progress_ringing_description=Ringing…
|
||||
|
||||
feedback_call_experience_heading2=How was your conversation?
|
||||
feedback_what_makes_you_sad=What makes you sad?
|
||||
feedback_thank_you_heading=Thank you for your feedback!
|
||||
feedback_category_audio_quality=Audio quality
|
||||
feedback_category_video_quality=Video quality
|
||||
feedback_category_was_disconnected=Was disconnected
|
||||
feedback_category_confusing=Confusing
|
||||
feedback_category_other=Other:
|
||||
feedback_custom_category_text_placeholder=What went wrong?
|
||||
feedback_submit_button=Submit
|
||||
feedback_back_button=Back
|
||||
## LOCALIZATION NOTE (feedback_window_will_close_in2):
|
||||
## Gaia l10n format; see https://github.com/mozilla-b2g/gaia/blob/f108c706fae43cd61628babdd9463e7695b2496e/apps/email/locales/email.en-US.properties#L387
|
||||
## In this item, don't translate the part between {{..}}
|
||||
feedback_window_will_close_in2={[ plural(countdown) ]}
|
||||
feedback_window_will_close_in2[one] = This window will close in {{countdown}} second
|
||||
feedback_window_will_close_in2[two] = This window will close in {{countdown}} seconds
|
||||
feedback_window_will_close_in2[few] = This window will close in {{countdown}} seconds
|
||||
feedback_window_will_close_in2[many] = This window will close in {{countdown}} seconds
|
||||
feedback_window_will_close_in2[other] = This window will close in {{countdown}} seconds
|
||||
|
||||
## LOCALIZATION_NOTE (feedback_rejoin_button): Displayed on the feedback form after
|
||||
## a signed-in to signed-in user call.
|
||||
## https://people.mozilla.org/~dhenein/labs/loop-mvp-spec/#feedback
|
||||
feedback_rejoin_button=Rejoin
|
||||
## LOCALIZATION NOTE (feedback_report_user_button): Used to report a user in the case of
|
||||
## an abusive user.
|
||||
feedback_report_user_button=Report User
|
||||
|
@ -7,17 +7,21 @@ var app = express();
|
||||
|
||||
var port = process.env.PORT || 3000;
|
||||
var loopServerPort = process.env.LOOP_SERVER_PORT || 5000;
|
||||
var feedbackApiUrl = process.env.LOOP_FEEDBACK_API_URL ||
|
||||
"https://input.allizom.org/api/v1/feedback";
|
||||
var feedbackProductName = process.env.LOOP_FEEDBACK_PRODUCT_NAME || "Loop";
|
||||
|
||||
function getConfigFile(req, res) {
|
||||
"use strict";
|
||||
|
||||
res.set('Content-Type', 'text/javascript');
|
||||
res.send(
|
||||
"var loop = loop || {};" +
|
||||
"loop.config = loop.config || {};" +
|
||||
"loop.config.serverUrl = 'http://localhost:" + loopServerPort + "';" +
|
||||
"loop.config.pendingCallTimeout = 20000;"
|
||||
);
|
||||
res.send([
|
||||
"var loop = loop || {};",
|
||||
"loop.config = loop.config || {};",
|
||||
"loop.config.serverUrl = 'http://localhost:" + loopServerPort + "';",
|
||||
"loop.config.feedbackApiUrl = '" + feedbackApiUrl + "';",
|
||||
"loop.config.feedbackProductName = '" + feedbackProductName + "';",
|
||||
].join("\n"));
|
||||
}
|
||||
|
||||
app.get('/content/config.js', getConfigFile);
|
||||
|
@ -108,8 +108,7 @@ describe("loop.conversation", function() {
|
||||
beforeEach(function() {
|
||||
client = new loop.Client();
|
||||
conversation = new loop.shared.models.ConversationModel({}, {
|
||||
sdk: {},
|
||||
pendingCallTimeout: 1000,
|
||||
sdk: {}
|
||||
});
|
||||
sandbox.spy(conversation, "setIncomingSessionData");
|
||||
sandbox.stub(conversation, "setOutgoingSessionData");
|
||||
|
@ -138,6 +138,13 @@ describe("loop.FeedbackAPIClient", function() {
|
||||
expect(parsed.user_agent).eql("MOZAGENT");
|
||||
});
|
||||
|
||||
it("should send url information when provided", function() {
|
||||
client.send({url: "http://fake.invalid"}, function(){});
|
||||
|
||||
var parsed = JSON.parse(requests[0].requestBody);
|
||||
expect(parsed.url).eql("http://fake.invalid");
|
||||
});
|
||||
|
||||
it("should throw on invalid feedback data", function() {
|
||||
expect(function() {
|
||||
client.send("invalid data", function(){});
|
||||
|
@ -60,8 +60,7 @@ describe("loop.shared.router", function() {
|
||||
conversation = new loop.shared.models.ConversationModel({
|
||||
loopToken: "fakeToken"
|
||||
}, {
|
||||
sdk: {},
|
||||
pendingCallTimeout: 1000
|
||||
sdk: {}
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -187,13 +187,12 @@ describe("loop.shared.views", function() {
|
||||
initSession: sandbox.stub().returns(fakeSession)
|
||||
};
|
||||
model = new sharedModels.ConversationModel(fakeSessionData, {
|
||||
sdk: fakeSDK,
|
||||
pendingCallTimeout: 1000
|
||||
sdk: fakeSDK
|
||||
});
|
||||
});
|
||||
|
||||
describe("#componentDidMount", function() {
|
||||
it("should start a session", function() {
|
||||
it("should start a session by default", function() {
|
||||
sandbox.stub(model, "startSession");
|
||||
|
||||
mountTestComponent({
|
||||
@ -205,6 +204,19 @@ describe("loop.shared.views", function() {
|
||||
sinon.assert.calledOnce(model.startSession);
|
||||
});
|
||||
|
||||
it("shouldn't start a session if initiate is false", function() {
|
||||
sandbox.stub(model, "startSession");
|
||||
|
||||
mountTestComponent({
|
||||
initiate: false,
|
||||
sdk: fakeSDK,
|
||||
model: model,
|
||||
video: {enabled: true}
|
||||
});
|
||||
|
||||
sinon.assert.notCalled(model.startSession);
|
||||
});
|
||||
|
||||
it("should set the correct stream publish options", function() {
|
||||
|
||||
var component = mountTestComponent({
|
||||
|
@ -36,6 +36,7 @@
|
||||
<script src="../../content/shared/js/mixins.js"></script>
|
||||
<script src="../../content/shared/js/views.js"></script>
|
||||
<script src="../../content/shared/js/websocket.js"></script>
|
||||
<script src="../../content/shared/js/feedbackApiClient.js"></script>
|
||||
<script src="../../standalone/content/js/standaloneClient.js"></script>
|
||||
<script src="../../standalone/content/js/webapp.js"></script>
|
||||
<!-- Test scripts -->
|
||||
|
@ -13,11 +13,15 @@ describe("loop.webapp", function() {
|
||||
var sharedModels = loop.shared.models,
|
||||
sharedViews = loop.shared.views,
|
||||
sandbox,
|
||||
notifications;
|
||||
notifications,
|
||||
feedbackApiClient;
|
||||
|
||||
beforeEach(function() {
|
||||
sandbox = sinon.sandbox.create();
|
||||
notifications = new sharedModels.NotificationCollection();
|
||||
feedbackApiClient = new loop.FeedbackAPIClient("http://invalid", {
|
||||
product: "Loop"
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
@ -31,6 +35,7 @@ describe("loop.webapp", function() {
|
||||
sandbox.stub(React, "renderComponent");
|
||||
sandbox.stub(loop.webapp.WebappHelper.prototype,
|
||||
"locationHash").returns("#call/fake-Token");
|
||||
loop.config.feedbackApiUrl = "http://fake.invalid";
|
||||
conversationSetStub =
|
||||
sandbox.stub(sharedModels.ConversationModel.prototype, "set");
|
||||
});
|
||||
@ -77,7 +82,8 @@ describe("loop.webapp", function() {
|
||||
client: client,
|
||||
conversation: conversation,
|
||||
notifications: notifications,
|
||||
sdk: {}
|
||||
sdk: {},
|
||||
feedbackApiClient: feedbackApiClient
|
||||
});
|
||||
});
|
||||
|
||||
@ -305,7 +311,7 @@ describe("loop.webapp", function() {
|
||||
conversation.trigger("session:ended");
|
||||
|
||||
TestUtils.findRenderedComponentWithType(ocView,
|
||||
loop.webapp.StartConversationView);
|
||||
loop.webapp.EndedConversationView);
|
||||
});
|
||||
});
|
||||
|
||||
@ -314,7 +320,7 @@ describe("loop.webapp", function() {
|
||||
conversation.trigger("session:peer-hungup");
|
||||
|
||||
TestUtils.findRenderedComponentWithType(ocView,
|
||||
loop.webapp.StartConversationView);
|
||||
loop.webapp.EndedConversationView);
|
||||
});
|
||||
|
||||
it("should notify the user", function() {
|
||||
@ -333,7 +339,7 @@ describe("loop.webapp", function() {
|
||||
conversation.trigger("session:network-disconnected");
|
||||
|
||||
TestUtils.findRenderedComponentWithType(ocView,
|
||||
loop.webapp.StartConversationView);
|
||||
loop.webapp.EndedConversationView);
|
||||
});
|
||||
|
||||
it("should notify the user", function() {
|
||||
@ -474,8 +480,10 @@ describe("loop.webapp", function() {
|
||||
loop.webapp.WebappRootView({
|
||||
client: client,
|
||||
helper: webappHelper,
|
||||
notifications: notifications,
|
||||
sdk: sdk,
|
||||
conversation: conversationModel
|
||||
conversation: conversationModel,
|
||||
feedbackApiClient: feedbackApiClient
|
||||
}));
|
||||
}
|
||||
|
||||
@ -772,6 +780,32 @@ describe("loop.webapp", function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe("EndedConversationView", function() {
|
||||
var view, conversation;
|
||||
|
||||
beforeEach(function() {
|
||||
conversation = new sharedModels.ConversationModel({}, {
|
||||
sdk: {}
|
||||
});
|
||||
view = React.addons.TestUtils.renderIntoDocument(
|
||||
loop.webapp.EndedConversationView({
|
||||
conversation: conversation,
|
||||
sdk: {},
|
||||
feedbackApiClient: feedbackApiClient,
|
||||
onAfterFeedbackReceived: function(){}
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it("should render a ConversationView", function() {
|
||||
TestUtils.findRenderedComponentWithType(view, sharedViews.ConversationView);
|
||||
});
|
||||
|
||||
it("should render a FeedbackView", function() {
|
||||
TestUtils.findRenderedComponentWithType(view, sharedViews.FeedbackView);
|
||||
});
|
||||
});
|
||||
|
||||
describe("PromoteFirefoxView", function() {
|
||||
describe("#render", function() {
|
||||
it("should not render when using Firefox", function() {
|
||||
|
@ -9,6 +9,10 @@
|
||||
* @type {Object}
|
||||
*/
|
||||
navigator.mozL10n = document.mozL10n = {
|
||||
initialize: function(){},
|
||||
|
||||
getDirection: function(){},
|
||||
|
||||
get: function(stringId, vars) {
|
||||
|
||||
// upcase the first letter
|
||||
|
@ -7,6 +7,7 @@
|
||||
* @type {Object}
|
||||
*/
|
||||
navigator.mozLoop = {
|
||||
ensureRegistered: function() {},
|
||||
getLoopCharPref: function() {},
|
||||
getLoopBoolPref: function() {}
|
||||
};
|
||||
|
@ -37,7 +37,7 @@
|
||||
|
||||
.showcase > section {
|
||||
position: relative;
|
||||
padding-top: 12em;
|
||||
padding-top: 14em;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
@ -149,3 +149,9 @@
|
||||
* When tokbox inserts the markup into the page the problem goes away */
|
||||
bottom: auto;
|
||||
}
|
||||
|
||||
.standalone .ended-conversation .remote_wrapper,
|
||||
.standalone .video-layout-wrapper {
|
||||
/* Removes the fake video image for ended conversations */
|
||||
background: none;
|
||||
}
|
||||
|
@ -23,6 +23,7 @@
|
||||
var CallUrlExpiredView = loop.webapp.CallUrlExpiredView;
|
||||
var PendingConversationView = loop.webapp.PendingConversationView;
|
||||
var StartConversationView = loop.webapp.StartConversationView;
|
||||
var EndedConversationView = loop.webapp.EndedConversationView;
|
||||
|
||||
// 3. Shared components
|
||||
var ConversationToolbar = loop.shared.views.ConversationToolbar;
|
||||
@ -338,6 +339,19 @@
|
||||
)
|
||||
),
|
||||
|
||||
Section({name: "EndedConversationView"},
|
||||
Example({summary: "Displays the feedback form"},
|
||||
React.DOM.div({className: "standalone"},
|
||||
EndedConversationView({sdk: mockSDK,
|
||||
video: {enabled: true},
|
||||
audio: {enabled: true},
|
||||
conversation: mockConversationModel,
|
||||
feedbackApiClient: stageFeedbackApiClient,
|
||||
onAfterFeedbackReceived: noop})
|
||||
)
|
||||
)
|
||||
),
|
||||
|
||||
Section({name: "AlertMessages"},
|
||||
Example({summary: "Various alerts"},
|
||||
React.DOM.div({className: "alert alert-warning"},
|
||||
|
@ -23,6 +23,7 @@
|
||||
var CallUrlExpiredView = loop.webapp.CallUrlExpiredView;
|
||||
var PendingConversationView = loop.webapp.PendingConversationView;
|
||||
var StartConversationView = loop.webapp.StartConversationView;
|
||||
var EndedConversationView = loop.webapp.EndedConversationView;
|
||||
|
||||
// 3. Shared components
|
||||
var ConversationToolbar = loop.shared.views.ConversationToolbar;
|
||||
@ -338,6 +339,19 @@
|
||||
</Example>
|
||||
</Section>
|
||||
|
||||
<Section name="EndedConversationView">
|
||||
<Example summary="Displays the feedback form">
|
||||
<div className="standalone">
|
||||
<EndedConversationView sdk={mockSDK}
|
||||
video={{enabled: true}}
|
||||
audio={{enabled: true}}
|
||||
conversation={mockConversationModel}
|
||||
feedbackApiClient={stageFeedbackApiClient}
|
||||
onAfterFeedbackReceived={noop} />
|
||||
</div>
|
||||
</Example>
|
||||
</Section>
|
||||
|
||||
<Section name="AlertMessages">
|
||||
<Example summary="Various alerts">
|
||||
<div className="alert alert-warning">
|
||||
|
@ -75,6 +75,8 @@ let UI = {
|
||||
}
|
||||
Services.prefs.setBoolPref("devtools.webide.autoinstallADBHelper", false);
|
||||
|
||||
this.lastConnectedRuntime = Services.prefs.getCharPref("devtools.webide.lastConnectedRuntime");
|
||||
|
||||
this.setupDeck();
|
||||
},
|
||||
|
||||
@ -125,6 +127,7 @@ let UI = {
|
||||
switch (what) {
|
||||
case "runtimelist":
|
||||
this.updateRuntimeList();
|
||||
this.autoConnectRuntime();
|
||||
break;
|
||||
case "connection":
|
||||
this.updateRuntimeButton();
|
||||
@ -145,6 +148,7 @@ let UI = {
|
||||
break;
|
||||
case "runtime":
|
||||
this.updateRuntimeButton();
|
||||
this.saveLastConnectedRuntime();
|
||||
break;
|
||||
case "project-validated":
|
||||
this.updateTitle();
|
||||
@ -343,6 +347,34 @@ let UI = {
|
||||
}
|
||||
},
|
||||
|
||||
autoConnectRuntime: function () {
|
||||
// Automatically reconnect to the previously selected runtime,
|
||||
// if available and has an ID
|
||||
if (AppManager.selectedRuntime || !this.lastConnectedRuntime) {
|
||||
return;
|
||||
}
|
||||
let [_, type, id] = this.lastConnectedRuntime.match(/^(\w+):(.+)$/);
|
||||
|
||||
type = type.toLowerCase();
|
||||
|
||||
// Local connection is mapped to AppManager.runtimeList.custom array
|
||||
if (type == "local") {
|
||||
type = "custom";
|
||||
}
|
||||
|
||||
// We support most runtimes except simulator, that needs to be manually
|
||||
// launched
|
||||
if (type == "usb" || type == "wifi" || type == "custom") {
|
||||
for (let runtime of AppManager.runtimeList[type]) {
|
||||
// Some runtimes do not expose getID function and don't support
|
||||
// autoconnect (like remote connection)
|
||||
if (typeof(runtime.getID) == "function" && runtime.getID() == id) {
|
||||
this.connectToRuntime(runtime);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
connectToRuntime: function(runtime) {
|
||||
let name = runtime.getName();
|
||||
let promise = AppManager.connectToRuntime(runtime);
|
||||
@ -359,6 +391,17 @@ let UI = {
|
||||
}
|
||||
},
|
||||
|
||||
saveLastConnectedRuntime: function () {
|
||||
if (AppManager.selectedRuntime &&
|
||||
typeof(AppManager.selectedRuntime.getID) === "function") {
|
||||
this.lastConnectedRuntime = AppManager.selectedRuntime.type + ":" + AppManager.selectedRuntime.getID();
|
||||
} else {
|
||||
this.lastConnectedRuntime = "";
|
||||
}
|
||||
Services.prefs.setCharPref("devtools.webide.lastConnectedRuntime",
|
||||
this.lastConnectedRuntime);
|
||||
},
|
||||
|
||||
/********** PROJECTS **********/
|
||||
|
||||
// Panel & button
|
||||
|
@ -651,7 +651,15 @@ exports.AppManager = AppManager = {
|
||||
let r = new USBRuntime(id);
|
||||
this.runtimeList.usb.push(r);
|
||||
r.updateNameFromADB().then(
|
||||
() => this.update("runtimelist"), () => {});
|
||||
() => {
|
||||
this.update("runtimelist");
|
||||
// Also update the runtime button label, if the currently selected
|
||||
// runtime name changes
|
||||
if (r == this.selectedRuntime) {
|
||||
this.update("runtime");
|
||||
}
|
||||
},
|
||||
() => {});
|
||||
}
|
||||
this.update("runtimelist");
|
||||
},
|
||||
|
@ -13,11 +13,21 @@ const promise = require("promise");
|
||||
|
||||
const Strings = Services.strings.createBundle("chrome://browser/locale/devtools/webide.properties");
|
||||
|
||||
// These type strings are used for logging events to Telemetry
|
||||
let RuntimeTypes = {
|
||||
usb: "USB",
|
||||
wifi: "WIFI",
|
||||
simulator: "SIMULATOR",
|
||||
remote: "REMOTE",
|
||||
local: "LOCAL"
|
||||
};
|
||||
|
||||
function USBRuntime(id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
USBRuntime.prototype = {
|
||||
type: RuntimeTypes.usb,
|
||||
connect: function(connection) {
|
||||
let device = Devices.getByName(this.id);
|
||||
if (!device) {
|
||||
@ -59,6 +69,7 @@ function WiFiRuntime(deviceName) {
|
||||
}
|
||||
|
||||
WiFiRuntime.prototype = {
|
||||
type: RuntimeTypes.wifi,
|
||||
connect: function(connection) {
|
||||
let service = discovery.getRemoteService("devtools", this.deviceName);
|
||||
if (!service) {
|
||||
@ -82,6 +93,7 @@ function SimulatorRuntime(version) {
|
||||
}
|
||||
|
||||
SimulatorRuntime.prototype = {
|
||||
type: RuntimeTypes.simulator,
|
||||
connect: function(connection) {
|
||||
let port = ConnectionManager.getFreeTCPPort();
|
||||
let simulator = Simulator.getByVersion(this.version);
|
||||
@ -105,6 +117,7 @@ SimulatorRuntime.prototype = {
|
||||
}
|
||||
|
||||
let gLocalRuntime = {
|
||||
type: RuntimeTypes.local,
|
||||
connect: function(connection) {
|
||||
if (!DebuggerServer.initialized) {
|
||||
DebuggerServer.init();
|
||||
@ -118,9 +131,13 @@ let gLocalRuntime = {
|
||||
getName: function() {
|
||||
return Strings.GetStringFromName("local_runtime");
|
||||
},
|
||||
getID: function () {
|
||||
return "local";
|
||||
}
|
||||
}
|
||||
|
||||
let gRemoteRuntime = {
|
||||
type: RuntimeTypes.remote,
|
||||
connect: function(connection) {
|
||||
let win = Services.wm.getMostRecentWindow("devtools:webide");
|
||||
if (!win) {
|
||||
|
@ -31,3 +31,4 @@ support-files =
|
||||
[test_manifestUpdate.html]
|
||||
[test_addons.html]
|
||||
[test_deviceinfo.html]
|
||||
[test_autoconnect_runtime.html]
|
||||
|
91
browser/devtools/webide/test/test_autoconnect_runtime.html
Normal file
91
browser/devtools/webide/test/test_autoconnect_runtime.html
Normal file
@ -0,0 +1,91 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf8">
|
||||
<title></title>
|
||||
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="chrome://mochikit/content/chrome-harness.js"></script>
|
||||
<script type="application/javascript;version=1.8" src="head.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<script type="application/javascript;version=1.8">
|
||||
window.onload = function() {
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
Task.spawn(function* () {
|
||||
|
||||
Cu.import("resource://gre/modules/devtools/dbg-server.jsm");
|
||||
DebuggerServer.init(function () { return true; });
|
||||
DebuggerServer.addBrowserActors();
|
||||
|
||||
let win = yield openWebIDE();
|
||||
|
||||
let fakeRuntime = {
|
||||
type: "USB",
|
||||
connect: function(connection) {
|
||||
ok(connection, win.AppManager.connection, "connection is valid");
|
||||
connection.host = null; // force connectPipe
|
||||
connection.connect();
|
||||
return promise.resolve();
|
||||
},
|
||||
|
||||
getID: function() {
|
||||
return "fakeRuntime";
|
||||
},
|
||||
|
||||
getName: function() {
|
||||
return "fakeRuntime";
|
||||
}
|
||||
};
|
||||
win.AppManager.runtimeList.usb.push(fakeRuntime);
|
||||
win.AppManager.update("runtimelist");
|
||||
|
||||
let panelNode = win.document.querySelector("#runtime-panel");
|
||||
let items = panelNode.querySelectorAll(".runtime-panel-item-usb");
|
||||
is(items.length, 1, "Found one runtime button");
|
||||
|
||||
let deferred = promise.defer();
|
||||
win.AppManager.connection.once(
|
||||
win.Connection.Events.CONNECTED,
|
||||
() => deferred.resolve());
|
||||
items[0].click();
|
||||
|
||||
ok(win.document.querySelector("window").className, "busy", "UI is busy");
|
||||
yield win.UI._busyPromise;
|
||||
is(Object.keys(DebuggerServer._connections).length, 1, "Connected");
|
||||
|
||||
yield nextTick();
|
||||
|
||||
yield closeWebIDE(win);
|
||||
|
||||
is(Object.keys(DebuggerServer._connections).length, 0, "Disconnected");
|
||||
|
||||
win = yield openWebIDE();
|
||||
|
||||
win.AppManager.runtimeList.usb.push(fakeRuntime);
|
||||
win.AppManager.update("runtimelist");
|
||||
|
||||
yield waitForUpdate(win, "list-tabs-response");
|
||||
|
||||
is(Object.keys(DebuggerServer._connections).length, 1, "Automatically reconnected");
|
||||
|
||||
yield win.Cmds.disconnectRuntime();
|
||||
|
||||
yield closeWebIDE(win);
|
||||
|
||||
DebuggerServer.destroy();
|
||||
|
||||
SimpleTest.finish();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -15,3 +15,4 @@ pref("devtools.webide.simulatorAddonID", "fxos_#SLASHED_VERSION#_simulator@mozil
|
||||
pref("devtools.webide.adbAddonURL", "https://ftp.mozilla.org/pub/mozilla.org/labs/fxos-simulator/adb-helper/#OS#/adbhelper-#OS#-latest.xpi");
|
||||
pref("devtools.webide.adbAddonID", "adbhelper@mozilla.org");
|
||||
pref("devtools.webide.monitorWebSocketURL", "ws://localhost:9000");
|
||||
pref("devtools.webide.lastConnectedRuntime", "");
|
||||
|
@ -105,9 +105,9 @@ g.edgePath.param-connection {
|
||||
fill: #4c9ed9; /* Select Highlight Blue */
|
||||
}
|
||||
|
||||
/* Text in nodes */
|
||||
/* Text in nodes and edges */
|
||||
text {
|
||||
cursor: pointer;
|
||||
cursor: default; /* override the "text" cursor */
|
||||
font-weight: 300;
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serf;
|
||||
font-size: 14px;
|
||||
@ -123,6 +123,10 @@ text {
|
||||
fill: #f0f1f2; /* Toolbars */
|
||||
}
|
||||
|
||||
.nodes text {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inspector Styles
|
||||
*/
|
||||
|
@ -1,38 +1,78 @@
|
||||
function check_ogg(v, enabled) {
|
||||
|
||||
function check_ogg(v, enabled, finish) {
|
||||
function check(type, expected) {
|
||||
is(v.canPlayType(type), enabled ? expected : "", type);
|
||||
}
|
||||
|
||||
// Ogg types
|
||||
check("video/ogg", "maybe");
|
||||
check("audio/ogg", "maybe");
|
||||
check("application/ogg", "maybe");
|
||||
function basic_test() {
|
||||
return new Promise(function(resolve, reject) {
|
||||
// Ogg types
|
||||
check("video/ogg", "maybe");
|
||||
check("audio/ogg", "maybe");
|
||||
check("application/ogg", "maybe");
|
||||
|
||||
// Supported Ogg codecs
|
||||
check("audio/ogg; codecs=vorbis", "probably");
|
||||
check("video/ogg; codecs=vorbis", "probably");
|
||||
check("video/ogg; codecs=vorbis,theora", "probably");
|
||||
check("video/ogg; codecs=\"vorbis, theora\"", "probably");
|
||||
check("video/ogg; codecs=theora", "probably");
|
||||
// Supported Ogg codecs
|
||||
check("audio/ogg; codecs=vorbis", "probably");
|
||||
check("video/ogg; codecs=vorbis", "probably");
|
||||
check("video/ogg; codecs=vorbis,theora", "probably");
|
||||
check("video/ogg; codecs=\"vorbis, theora\"", "probably");
|
||||
check("video/ogg; codecs=theora", "probably");
|
||||
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
|
||||
// Verify Opus support
|
||||
var OpusEnabled = undefined;
|
||||
try {
|
||||
OpusEnabled = SpecialPowers.getBoolPref("media.opus.enabled");
|
||||
} catch (ex) {
|
||||
// SpecialPowers failed, perhaps because Opus isn't compiled in
|
||||
console.log("media.opus.enabled pref not found; skipping Opus validation");
|
||||
}
|
||||
if (OpusEnabled !== undefined) {
|
||||
SpecialPowers.setBoolPref("media.opus.enabled", true);
|
||||
check("audio/ogg; codecs=opus", "probably");
|
||||
SpecialPowers.setBoolPref("media.opus.enabled", false);
|
||||
check("audio/ogg; codecs=opus", "");
|
||||
SpecialPowers.setBoolPref("media.opus.enabled", OpusEnabled);
|
||||
function verify_opus_support() {
|
||||
return new Promise(function(resolve, reject) {
|
||||
var OpusEnabled = undefined;
|
||||
try {
|
||||
OpusEnabled = SpecialPowers.getBoolPref("media.opus.enabled");
|
||||
} catch (ex) {
|
||||
// SpecialPowers failed, perhaps because Opus isn't compiled in
|
||||
console.log("media.opus.enabled pref not found; skipping Opus validation");
|
||||
}
|
||||
if (OpusEnabled != undefined) {
|
||||
resolve();
|
||||
} else {
|
||||
reject();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Unsupported Ogg codecs
|
||||
check("video/ogg; codecs=xyz", "");
|
||||
check("video/ogg; codecs=xyz,vorbis", "");
|
||||
check("video/ogg; codecs=vorbis,xyz", "");
|
||||
function opus_enable() {
|
||||
return new Promise(function(resolve, reject) {
|
||||
SpecialPowers.pushPrefEnv({"set": [['media.opus.enabled', true]]},
|
||||
function() {
|
||||
check("audio/ogg; codecs=opus", "probably");
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function opus_disable() {
|
||||
return new Promise(function(resolve, reject) {
|
||||
SpecialPowers.pushPrefEnv({"set": [['media.opus.enabled', false]]},
|
||||
function() {
|
||||
check("audio/ogg; codecs=opus", "");
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function unspported_ogg() {
|
||||
// Unsupported Ogg codecs
|
||||
check("video/ogg; codecs=xyz", "");
|
||||
check("video/ogg; codecs=xyz,vorbis", "");
|
||||
check("video/ogg; codecs=vorbis,xyz", "");
|
||||
|
||||
finish.call();
|
||||
}
|
||||
|
||||
basic_test()
|
||||
.then(verify_opus_support)
|
||||
.then(opus_enable)
|
||||
.then(opus_disable)
|
||||
.then(unspported_ogg, unspported_ogg);
|
||||
|
||||
}
|
||||
|
@ -22,7 +22,7 @@
|
||||
# do ok(true, "Type not supported") and stop the test.
|
||||
|
||||
[DEFAULT]
|
||||
skip-if = buildapp == 'mulet' || (buildapp == 'b2g' && (toolkit != 'gonk' || debug)) # b2g-debug,b2g-desktop(bug 918299)
|
||||
skip-if = buildapp == 'mulet' || (buildapp == 'b2g' && toolkit != 'gonk') # b2g-desktop(bug 918299)
|
||||
support-files =
|
||||
320x240.ogv
|
||||
320x240.ogv^headers^
|
||||
@ -320,8 +320,8 @@ skip-if = buildapp == 'mulet' || os == 'win' # bug 894922
|
||||
skip-if = buildapp == 'b2g' # bug 1021675
|
||||
[test_can_play_type_no_ogg.html]
|
||||
[test_can_play_type_ogg.html]
|
||||
skip-if = buildapp == 'b2g' || e10s # b2g(bug 1021675)
|
||||
[test_chaining.html]
|
||||
skip-if = toolkit == 'gonk' && debug
|
||||
[test_clone_media_element.html]
|
||||
[test_closing_connections.html]
|
||||
[test_constants.html]
|
||||
@ -367,18 +367,24 @@ skip-if = toolkit == 'gonk' && !debug # bug 1021677
|
||||
[test_mediarecorder_record_gum_video_timeslice.html]
|
||||
skip-if = buildapp == 'b2g' || toolkit == 'android' # mimetype check, bug 969289
|
||||
[test_mediarecorder_record_immediate_stop.html]
|
||||
skip-if = toolkit == 'gonk' && debug
|
||||
[test_mediarecorder_record_no_timeslice.html]
|
||||
skip-if = toolkit == 'gonk' && debug
|
||||
[test_mediarecorder_record_nosrc.html]
|
||||
[test_mediarecorder_record_session.html]
|
||||
[test_mediarecorder_record_startstopstart.html]
|
||||
skip-if = toolkit == 'gonk' && debug
|
||||
[test_mediarecorder_record_stopms.html]
|
||||
[test_mediarecorder_record_timeslice.html]
|
||||
skip-if = toolkit == 'gonk' && debug
|
||||
[test_mediarecorder_reload_crash.html]
|
||||
[test_mediarecorder_unsupported_src.html]
|
||||
[test_mediarecorder_record_getdata_afterstart.html]
|
||||
skip-if = toolkit == 'gonk' && debug
|
||||
[test_mediatrack_consuming_mediaresource.html]
|
||||
[test_mediatrack_consuming_mediastream.html]
|
||||
[test_mediatrack_events.html]
|
||||
skip-if = toolkit == 'gonk' && debug # bug 1065924
|
||||
[test_mediatrack_parsing_ogg.html]
|
||||
[test_mediatrack_replay_from_end.html]
|
||||
[test_metadata.html]
|
||||
|
@ -24,11 +24,15 @@ a Bug 469247</a>
|
||||
<script>
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
function finish() {
|
||||
mediaTestCleanup();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
SpecialPowers.pushPrefEnv({"set": [["media.ogg.enabled", false]]},
|
||||
function() {
|
||||
check_ogg(document.getElementById('v'), false);
|
||||
mediaTestCleanup();
|
||||
SimpleTest.finish();
|
||||
check_ogg(document.getElementById('v'), false, finish);
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -21,9 +21,16 @@ a Bug 469247</a>
|
||||
<pre id="test">
|
||||
<script src="can_play_type_ogg.js"></script>
|
||||
<script>
|
||||
check_ogg(document.getElementById('v'), true);
|
||||
|
||||
mediaTestCleanup();
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
function finish() {
|
||||
mediaTestCleanup();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
check_ogg(document.getElementById('v'), true, finish);
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
|
@ -22,7 +22,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "WebappOSUtils",
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
|
||||
"resource://gre/modules/NetUtil.jsm");
|
||||
|
||||
// Shared code for AppsServiceChild.jsm, Webapps.jsm and Webapps.js
|
||||
// Shared code for AppsServiceChild.jsm, TrustedHostedAppsUtils.jsm,
|
||||
// Webapps.jsm and Webapps.js
|
||||
|
||||
this.EXPORTED_SYMBOLS =
|
||||
["AppsUtils", "ManifestHelper", "isAbsoluteURI", "mozIApplication"];
|
||||
@ -116,6 +117,84 @@ this.AppsUtils = {
|
||||
return obj;
|
||||
},
|
||||
|
||||
// Creates a nsILoadContext object with a given appId and isBrowser flag.
|
||||
createLoadContext: function createLoadContext(aAppId, aIsBrowser) {
|
||||
return {
|
||||
associatedWindow: null,
|
||||
topWindow : null,
|
||||
appId: aAppId,
|
||||
isInBrowserElement: aIsBrowser,
|
||||
usePrivateBrowsing: false,
|
||||
isContent: false,
|
||||
|
||||
isAppOfType: function(appType) {
|
||||
throw Cr.NS_ERROR_NOT_IMPLEMENTED;
|
||||
},
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsILoadContext,
|
||||
Ci.nsIInterfaceRequestor,
|
||||
Ci.nsISupports]),
|
||||
getInterface: function(iid) {
|
||||
if (iid.equals(Ci.nsILoadContext))
|
||||
return this;
|
||||
throw Cr.NS_ERROR_NO_INTERFACE;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// Sends data downloaded from aRequestChannel to a file
|
||||
// identified by aId and aFileName.
|
||||
getFile: function(aRequestChannel, aId, aFileName) {
|
||||
let deferred = Promise.defer();
|
||||
|
||||
// Staging the file in TmpD until all the checks are done.
|
||||
let file = FileUtils.getFile("TmpD", ["webapps", aId, aFileName], true);
|
||||
|
||||
// We need an output stream to write the channel content to the out file.
|
||||
let outputStream = Cc["@mozilla.org/network/file-output-stream;1"]
|
||||
.createInstance(Ci.nsIFileOutputStream);
|
||||
// write, create, truncate
|
||||
outputStream.init(file, 0x02 | 0x08 | 0x20, parseInt("0664", 8), 0);
|
||||
let bufferedOutputStream =
|
||||
Cc['@mozilla.org/network/buffered-output-stream;1']
|
||||
.createInstance(Ci.nsIBufferedOutputStream);
|
||||
bufferedOutputStream.init(outputStream, 1024);
|
||||
|
||||
// Create a listener that will give data to the file output stream.
|
||||
let listener = Cc["@mozilla.org/network/simple-stream-listener;1"]
|
||||
.createInstance(Ci.nsISimpleStreamListener);
|
||||
|
||||
listener.init(bufferedOutputStream, {
|
||||
onStartRequest: function(aRequest, aContext) {
|
||||
// Nothing to do there anymore.
|
||||
},
|
||||
|
||||
onStopRequest: function(aRequest, aContext, aStatusCode) {
|
||||
bufferedOutputStream.close();
|
||||
outputStream.close();
|
||||
|
||||
if (!Components.isSuccessCode(aStatusCode)) {
|
||||
deferred.reject({ msg: "NETWORK_ERROR", downloadAvailable: true});
|
||||
return;
|
||||
}
|
||||
|
||||
// If we get a 4XX or a 5XX http status, bail out like if we had a
|
||||
// network error.
|
||||
let responseStatus = aRequestChannel.responseStatus;
|
||||
if (responseStatus >= 400 && responseStatus <= 599) {
|
||||
// unrecoverable error, don't bug the user
|
||||
deferred.reject({ msg: "NETWORK_ERROR", downloadAvailable: false});
|
||||
return;
|
||||
}
|
||||
|
||||
deferred.resolve(file);
|
||||
}
|
||||
});
|
||||
aRequestChannel.asyncOpen(listener, null);
|
||||
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
getAppByManifestURL: function getAppByManifestURL(aApps, aManifestURL) {
|
||||
debug("getAppByManifestURL " + aManifestURL);
|
||||
// This could be O(1) if |webapps| was a dictionary indexed on manifestURL
|
||||
|
@ -48,7 +48,7 @@ this.PermissionsTable = { geolocation: {
|
||||
},
|
||||
camera: {
|
||||
app: DENY_ACTION,
|
||||
trusted: DENY_ACTION,
|
||||
trusted: PROMPT_ACTION,
|
||||
privileged: PROMPT_ACTION,
|
||||
certified: ALLOW_ACTION
|
||||
},
|
||||
@ -99,28 +99,28 @@ this.PermissionsTable = { geolocation: {
|
||||
},
|
||||
"device-storage:pictures": {
|
||||
app: DENY_ACTION,
|
||||
trusted: DENY_ACTION,
|
||||
trusted: PROMPT_ACTION,
|
||||
privileged: PROMPT_ACTION,
|
||||
certified: ALLOW_ACTION,
|
||||
access: ["read", "write", "create"]
|
||||
},
|
||||
"device-storage:videos": {
|
||||
app: DENY_ACTION,
|
||||
trusted: DENY_ACTION,
|
||||
trusted: PROMPT_ACTION,
|
||||
privileged: PROMPT_ACTION,
|
||||
certified: ALLOW_ACTION,
|
||||
access: ["read", "write", "create"]
|
||||
},
|
||||
"device-storage:music": {
|
||||
app: DENY_ACTION,
|
||||
trusted: DENY_ACTION,
|
||||
trusted: PROMPT_ACTION,
|
||||
privileged: PROMPT_ACTION,
|
||||
certified: ALLOW_ACTION,
|
||||
access: ["read", "write", "create"]
|
||||
},
|
||||
"device-storage:sdcard": {
|
||||
app: DENY_ACTION,
|
||||
trusted: DENY_ACTION,
|
||||
trusted: PROMPT_ACTION,
|
||||
privileged: PROMPT_ACTION,
|
||||
certified: ALLOW_ACTION,
|
||||
access: ["read", "write", "create"]
|
||||
@ -368,7 +368,7 @@ this.PermissionsTable = { geolocation: {
|
||||
},
|
||||
"audio-channel-publicnotification": {
|
||||
app: DENY_ACTION,
|
||||
trusted: DENY_ACTION,
|
||||
trusted: ALLOW_ACTION,
|
||||
privileged: DENY_ACTION,
|
||||
certified: ALLOW_ACTION
|
||||
},
|
||||
|
@ -16,6 +16,8 @@ const APP_TRUSTED_ROOTS= ["AppMarketplaceProdPublicRoot",
|
||||
"AppMarketplaceDevPublicRoot",
|
||||
"AppMarketplaceDevReviewersRoot",
|
||||
"AppMarketplaceStageRoot",
|
||||
"TrustedHostedAppPublicRoot",
|
||||
"TrustedHostedAppTestRoot",
|
||||
"AppXPCShellRoot"];
|
||||
|
||||
this.TrustedRootCertificate = {
|
||||
|
@ -2,17 +2,25 @@
|
||||
* 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/. */
|
||||
|
||||
/* global Components, Services, dump */
|
||||
/* global Components, Services, dump, AppsUtils, NetUtil, XPCOMUtils */
|
||||
|
||||
"use strict";
|
||||
|
||||
const Cu = Components.utils;
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cr = Components.results;
|
||||
const signatureFileExtension = ".sig";
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["TrustedHostedAppsUtils"];
|
||||
|
||||
Cu.import("resource://gre/modules/AppsUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Promise.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
|
||||
"resource://gre/modules/NetUtil.jsm");
|
||||
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
// On Android, define the "debug" function as a binding of the "d" function
|
||||
@ -32,8 +40,6 @@ let debug = Services.prefs.getBoolPref("dom.mozApps.debug") ?
|
||||
|
||||
/**
|
||||
* Verification functions for Trusted Hosted Apps.
|
||||
* (Manifest signature verification is in Webapps.jsm as part of
|
||||
* regular signature verification.)
|
||||
*/
|
||||
this.TrustedHostedAppsUtils = {
|
||||
|
||||
@ -65,7 +71,8 @@ this.TrustedHostedAppsUtils = {
|
||||
throw "CERTDB_ERROR";
|
||||
}
|
||||
|
||||
if (siteSecurityService.isSecureHost(Ci.nsISiteSecurityService.HEADER_HPKP, uri.host, 0)) {
|
||||
if (siteSecurityService.isSecureHost(Ci.nsISiteSecurityService.HEADER_HPKP,
|
||||
uri.host, 0)) {
|
||||
debug("\tvalid certificate pinning for host: " + uri.host + "\n");
|
||||
return true;
|
||||
}
|
||||
@ -100,7 +107,7 @@ this.TrustedHostedAppsUtils = {
|
||||
.forEach(aList => {
|
||||
// aList[0] contains the directive name.
|
||||
// aList[1..n] contains sources.
|
||||
let directiveName = aList.shift()
|
||||
let directiveName = aList.shift();
|
||||
let sources = aList;
|
||||
|
||||
if ((-1 == validDirectives.indexOf(directiveName))) {
|
||||
@ -144,5 +151,122 @@ this.TrustedHostedAppsUtils = {
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
_verifySignedFile: function(aManifestStream, aSignatureStream, aCertDb) {
|
||||
let deferred = Promise.defer();
|
||||
|
||||
let root = Ci.nsIX509CertDB.TrustedHostedAppPublicRoot;
|
||||
try {
|
||||
// Check if we should use the test certificates.
|
||||
// Please note that this should be changed if we ever allow chages to the
|
||||
// prefs since that would create a way for an attacker to use the test
|
||||
// root for real apps.
|
||||
let useTrustedAppTestCerts = Services.prefs
|
||||
.getBoolPref("dom.mozApps.use_trustedapp_test_certs");
|
||||
if (useTrustedAppTestCerts) {
|
||||
root = Ci.nsIX509CertDB.TrustedHostedAppTestRoot;
|
||||
}
|
||||
} catch (ex) { }
|
||||
|
||||
aCertDb.verifySignedManifestAsync(
|
||||
root, aManifestStream, aSignatureStream,
|
||||
function(aRv, aCert) {
|
||||
debug("Signature verification returned code, cert & root: " + aRv + " " + aCert + " " + root);
|
||||
if (Components.isSuccessCode(aRv)) {
|
||||
deferred.resolve(aCert);
|
||||
} else if (aRv == Cr.NS_ERROR_FILE_CORRUPTED ||
|
||||
aRv == Cr.NS_ERROR_SIGNED_MANIFEST_FILE_INVALID) {
|
||||
deferred.reject("MANIFEST_SIGNATURE_FILE_INVALID");
|
||||
} else {
|
||||
deferred.reject("MANIFEST_SIGNATURE_VERIFICATION_ERROR");
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
verifySignedManifest: function(aApp, aAppId) {
|
||||
let deferred = Promise.defer();
|
||||
|
||||
let certDb;
|
||||
try {
|
||||
certDb = Cc["@mozilla.org/security/x509certdb;1"]
|
||||
.getService(Ci.nsIX509CertDB);
|
||||
} catch (e) {
|
||||
debug("nsIX509CertDB error: " + e);
|
||||
// unrecoverable error, don't bug the user
|
||||
throw "CERTDB_ERROR";
|
||||
}
|
||||
|
||||
let mRequestChannel = NetUtil.newChannel(aApp.manifestURL)
|
||||
.QueryInterface(Ci.nsIHttpChannel);
|
||||
mRequestChannel.loadFlags |= Ci.nsIRequest.INHIBIT_CACHING;
|
||||
mRequestChannel.notificationCallbacks =
|
||||
AppsUtils.createLoadContext(aAppId, false);
|
||||
|
||||
// The manifest signature must be located at the same path as the
|
||||
// manifest and have the same file name, only the file extension
|
||||
// should differ. Any fragment or query parameter will be ignored.
|
||||
let signatureURL;
|
||||
try {
|
||||
let mURL = Cc["@mozilla.org/network/io-service;1"]
|
||||
.getService(Ci.nsIIOService)
|
||||
.newURI(aApp.manifestURL, null, null)
|
||||
.QueryInterface(Ci.nsIURL);
|
||||
signatureURL = mURL.prePath +
|
||||
mURL.directory + mURL.fileBaseName + signatureFileExtension;
|
||||
} catch(e) {
|
||||
deferred.reject("SIGNATURE_PATH_INVALID");
|
||||
return;
|
||||
}
|
||||
|
||||
let sRequestChannel = NetUtil.newChannel(signatureURL)
|
||||
.QueryInterface(Ci.nsIHttpChannel);
|
||||
sRequestChannel.loadFlags |= Ci.nsIRequest.INHIBIT_CACHING;
|
||||
sRequestChannel.notificationCallbacks =
|
||||
AppsUtils.createLoadContext(aAppId, false);
|
||||
let getAsyncFetchCallback = (resolve, reject) =>
|
||||
(aInputStream, aResult) => {
|
||||
if (!Components.isSuccessCode(aResult)) {
|
||||
debug("Failed to download file");
|
||||
reject("MANIFEST_FILE_UNAVAILABLE");
|
||||
return;
|
||||
}
|
||||
resolve(aInputStream);
|
||||
};
|
||||
|
||||
Promise.all([
|
||||
new Promise((resolve, reject) => {
|
||||
NetUtil.asyncFetch(mRequestChannel,
|
||||
getAsyncFetchCallback(resolve, reject));
|
||||
}),
|
||||
new Promise((resolve, reject) => {
|
||||
NetUtil.asyncFetch(sRequestChannel,
|
||||
getAsyncFetchCallback(resolve, reject));
|
||||
})
|
||||
]).then(([aManifestStream, aSignatureStream]) => {
|
||||
this._verifySignedFile(aManifestStream, aSignatureStream, certDb)
|
||||
.then(deferred.resolve, deferred.reject);
|
||||
}, deferred.reject);
|
||||
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
verifyManifest: function(aData) {
|
||||
return new Promise((resolve, reject) => {
|
||||
// sanity check on manifest host's CA (proper CA check with
|
||||
// pinning is done by regular networking code)
|
||||
if (!this.isHostPinned(aData.app.manifestURL)) {
|
||||
reject("TRUSTED_APPLICATION_HOST_CERTIFICATE_INVALID");
|
||||
return;
|
||||
}
|
||||
if (!this.verifyCSPWhiteList(aData.app.manifest.csp)) {
|
||||
reject("TRUSTED_APPLICATION_WHITELIST_VALIDATION_FAILED");
|
||||
return;
|
||||
}
|
||||
this.verifySignedManifest(aData.app, aData.appId).then(resolve, reject);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
@ -2021,7 +2021,7 @@ this.DOMApplicationRegistry = {
|
||||
xhr.setRequestHeader("If-None-Match", app.etag);
|
||||
}
|
||||
xhr.channel.notificationCallbacks =
|
||||
this.createLoadContext(app.installerAppId, app.installerIsBrowser);
|
||||
AppsUtils.createLoadContext(app.installerAppId, app.installerIsBrowser);
|
||||
|
||||
xhr.addEventListener("load", onload.bind(this, xhr, oldManifest), false);
|
||||
xhr.addEventListener("error", (function() {
|
||||
@ -2052,30 +2052,6 @@ this.DOMApplicationRegistry = {
|
||||
});
|
||||
},
|
||||
|
||||
// Creates a nsILoadContext object with a given appId and isBrowser flag.
|
||||
createLoadContext: function createLoadContext(aAppId, aIsBrowser) {
|
||||
return {
|
||||
associatedWindow: null,
|
||||
topWindow : null,
|
||||
appId: aAppId,
|
||||
isInBrowserElement: aIsBrowser,
|
||||
usePrivateBrowsing: false,
|
||||
isContent: false,
|
||||
|
||||
isAppOfType: function(appType) {
|
||||
throw Cr.NS_ERROR_NOT_IMPLEMENTED;
|
||||
},
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsILoadContext,
|
||||
Ci.nsIInterfaceRequestor,
|
||||
Ci.nsISupports]),
|
||||
getInterface: function(iid) {
|
||||
if (iid.equals(Ci.nsILoadContext))
|
||||
return this;
|
||||
throw Cr.NS_ERROR_NO_INTERFACE;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
updatePackagedApp: Task.async(function*(aData, aId, aApp, aNewManifest) {
|
||||
debug("updatePackagedApp");
|
||||
@ -2316,24 +2292,16 @@ this.DOMApplicationRegistry = {
|
||||
// in which case we don't need to load it.
|
||||
if (app.manifest) {
|
||||
if (checkManifest()) {
|
||||
if (this.kTrustedHosted == this.appKind(app, app.manifest)) {
|
||||
// sanity check on manifest host's CA
|
||||
// (proper CA check with pinning is done by regular networking code)
|
||||
if (!TrustedHostedAppsUtils.isHostPinned(app.manifestURL)) {
|
||||
sendError("TRUSTED_APPLICATION_HOST_CERTIFICATE_INVALID");
|
||||
return;
|
||||
}
|
||||
|
||||
// Signature of the manifest should be verified here.
|
||||
// Bug 1059216.
|
||||
|
||||
if (!TrustedHostedAppsUtils.verifyCSPWhiteList(app.manifest.csp)) {
|
||||
sendError("TRUSTED_APPLICATION_WHITELIST_VALIDATION_FAILED");
|
||||
return;
|
||||
}
|
||||
debug("Installed manifest check OK");
|
||||
if (this.kTrustedHosted !== this.appKind(app, app.manifest)) {
|
||||
installApp();
|
||||
return;
|
||||
}
|
||||
|
||||
installApp();
|
||||
TrustedHostedAppsUtils.verifyManifest(aData)
|
||||
.then(installApp, sendError);
|
||||
} else {
|
||||
debug("Installed manifest check failed");
|
||||
// checkManifest() sends error before return
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -2342,8 +2310,8 @@ this.DOMApplicationRegistry = {
|
||||
.createInstance(Ci.nsIXMLHttpRequest);
|
||||
xhr.open("GET", app.manifestURL, true);
|
||||
xhr.channel.loadFlags |= Ci.nsIRequest.INHIBIT_CACHING;
|
||||
xhr.channel.notificationCallbacks = this.createLoadContext(aData.appId,
|
||||
aData.isBrowser);
|
||||
xhr.channel.notificationCallbacks = AppsUtils.createLoadContext(aData.appId,
|
||||
aData.isBrowser);
|
||||
xhr.responseType = "json";
|
||||
|
||||
xhr.addEventListener("load", (function() {
|
||||
@ -2356,21 +2324,20 @@ this.DOMApplicationRegistry = {
|
||||
|
||||
app.manifest = xhr.response;
|
||||
if (checkManifest()) {
|
||||
debug("Downloaded manifest check OK");
|
||||
app.etag = xhr.getResponseHeader("Etag");
|
||||
if (this.kTrustedHosted == this.appKind(app, app.manifest)) {
|
||||
// checking trusted host for pinning is not needed here, since
|
||||
// network code will have already done that
|
||||
|
||||
// Signature of the manifest should be verified here.
|
||||
// Bug 1059216.
|
||||
|
||||
if (!TrustedHostedAppsUtils.verifyCSPWhiteList(app.manifest.csp)) {
|
||||
sendError("TRUSTED_APPLICATION_WHITELIST_VALIDATION_FAILED");
|
||||
return;
|
||||
}
|
||||
if (this.kTrustedHosted !== this.appKind(app, app.manifest)) {
|
||||
installApp();
|
||||
return;
|
||||
}
|
||||
|
||||
installApp();
|
||||
debug("App kind: " + this.kTrustedHosted);
|
||||
TrustedHostedAppsUtils.verifyManifest(aData)
|
||||
.then(installApp, sendError);
|
||||
return;
|
||||
} else {
|
||||
debug("Downloaded manifest check failed");
|
||||
// checkManifest() sends error before return
|
||||
}
|
||||
} else {
|
||||
sendError("MANIFEST_URL_ERROR");
|
||||
@ -2455,8 +2422,8 @@ this.DOMApplicationRegistry = {
|
||||
.createInstance(Ci.nsIXMLHttpRequest);
|
||||
xhr.open("GET", app.manifestURL, true);
|
||||
xhr.channel.loadFlags |= Ci.nsIRequest.INHIBIT_CACHING;
|
||||
xhr.channel.notificationCallbacks = this.createLoadContext(aData.appId,
|
||||
aData.isBrowser);
|
||||
xhr.channel.notificationCallbacks = AppsUtils.createLoadContext(aData.appId,
|
||||
aData.isBrowser);
|
||||
xhr.responseType = "json";
|
||||
|
||||
xhr.addEventListener("load", (function() {
|
||||
@ -3219,52 +3186,15 @@ this.DOMApplicationRegistry = {
|
||||
_getPackage: function(aRequestChannel, aId, aOldApp, aNewApp) {
|
||||
let deferred = Promise.defer();
|
||||
|
||||
// Staging the zip in TmpD until all the checks are done.
|
||||
let zipFile =
|
||||
FileUtils.getFile("TmpD", ["webapps", aId, "application.zip"], true);
|
||||
|
||||
// We need an output stream to write the channel content to the zip file.
|
||||
let outputStream = Cc["@mozilla.org/network/file-output-stream;1"]
|
||||
.createInstance(Ci.nsIFileOutputStream);
|
||||
// write, create, truncate
|
||||
outputStream.init(zipFile, 0x02 | 0x08 | 0x20, parseInt("0664", 8), 0);
|
||||
let bufferedOutputStream =
|
||||
Cc['@mozilla.org/network/buffered-output-stream;1']
|
||||
.createInstance(Ci.nsIBufferedOutputStream);
|
||||
bufferedOutputStream.init(outputStream, 1024);
|
||||
|
||||
// Create a listener that will give data to the file output stream.
|
||||
let listener = Cc["@mozilla.org/network/simple-stream-listener;1"]
|
||||
.createInstance(Ci.nsISimpleStreamListener);
|
||||
|
||||
listener.init(bufferedOutputStream, {
|
||||
onStartRequest: function(aRequest, aContext) {
|
||||
// Nothing to do there anymore.
|
||||
},
|
||||
|
||||
onStopRequest: function(aRequest, aContext, aStatusCode) {
|
||||
bufferedOutputStream.close();
|
||||
outputStream.close();
|
||||
|
||||
if (!Components.isSuccessCode(aStatusCode)) {
|
||||
deferred.reject("NETWORK_ERROR");
|
||||
return;
|
||||
}
|
||||
|
||||
// If we get a 4XX or a 5XX http status, bail out like if we had a
|
||||
// network error.
|
||||
let responseStatus = aRequestChannel.responseStatus;
|
||||
if (responseStatus >= 400 && responseStatus <= 599) {
|
||||
// unrecoverable error, don't bug the user
|
||||
aOldApp.downloadAvailable = false;
|
||||
deferred.reject("NETWORK_ERROR");
|
||||
return;
|
||||
}
|
||||
|
||||
deferred.resolve(zipFile);
|
||||
AppsUtils.getFile(aRequestChannel, aId, "application.zip").then((aFile) => {
|
||||
deferred.resolve(aFile);
|
||||
}, function(rejectStatus) {
|
||||
debug("Failed to download package file: " + rejectStatus.msg);
|
||||
if (!rejectStatus.downloadAvailable) {
|
||||
aOldApp.downloadAvailable = false;
|
||||
}
|
||||
deferred.reject(rejectStatus.msg);
|
||||
});
|
||||
aRequestChannel.asyncOpen(listener, null);
|
||||
|
||||
// send a first progress event to correctly set the DOM object's properties
|
||||
this._sendDownloadProgressEvent(aNewApp, 0);
|
||||
|
@ -1220,7 +1220,9 @@ BluetoothHfpManager::Disconnect(BluetoothProfileController* aController)
|
||||
|
||||
if (!sBluetoothHfpInterface) {
|
||||
BT_LOGR("sBluetoothHfpInterface is null");
|
||||
aController->NotifyCompletion(NS_LITERAL_STRING(ERR_NO_AVAILABLE_RESOURCE));
|
||||
if (aController) {
|
||||
aController->NotifyCompletion(NS_LITERAL_STRING(ERR_NO_AVAILABLE_RESOURCE));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1664,6 +1664,13 @@ DeviceStorageFile::GetStatus(nsAString& aStatus)
|
||||
aStatus.AssignLiteral("unavailable");
|
||||
return;
|
||||
}
|
||||
bool isUnmounting;
|
||||
rv = vol->GetIsUnmounting(&isUnmounting);
|
||||
NS_ENSURE_SUCCESS_VOID(rv);
|
||||
if (isUnmounting) {
|
||||
aStatus.AssignLiteral("unavailable");
|
||||
return;
|
||||
}
|
||||
int32_t volState;
|
||||
rv = vol->GetState(&volState);
|
||||
NS_ENSURE_SUCCESS_VOID(rv);
|
||||
|
@ -1893,12 +1893,14 @@ ContentChild::RecvFileSystemUpdate(const nsString& aFsName,
|
||||
const bool& aIsMediaPresent,
|
||||
const bool& aIsSharing,
|
||||
const bool& aIsFormatting,
|
||||
const bool& aIsFake)
|
||||
const bool& aIsFake,
|
||||
const bool& aIsUnmounting)
|
||||
{
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
nsRefPtr<nsVolume> volume = new nsVolume(aFsName, aVolumeName, aState,
|
||||
aMountGeneration, aIsMediaPresent,
|
||||
aIsSharing, aIsFormatting, aIsFake);
|
||||
aIsSharing, aIsFormatting, aIsFake,
|
||||
aIsUnmounting);
|
||||
|
||||
nsRefPtr<nsVolumeService> vs = nsVolumeService::GetSingleton();
|
||||
if (vs) {
|
||||
@ -1914,6 +1916,7 @@ ContentChild::RecvFileSystemUpdate(const nsString& aFsName,
|
||||
unused << aIsSharing;
|
||||
unused << aIsFormatting;
|
||||
unused << aIsFake;
|
||||
unused << aIsUnmounting;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
@ -312,7 +312,8 @@ public:
|
||||
const bool& aIsMediaPresent,
|
||||
const bool& aIsSharing,
|
||||
const bool& aIsFormatting,
|
||||
const bool& aIsFake) MOZ_OVERRIDE;
|
||||
const bool& aIsFake,
|
||||
const bool& aIsUnmounting) MOZ_OVERRIDE;
|
||||
|
||||
virtual bool RecvNuwaFork() MOZ_OVERRIDE;
|
||||
|
||||
|
@ -2650,6 +2650,7 @@ ContentParent::Observe(nsISupports* aSubject,
|
||||
bool isSharing;
|
||||
bool isFormatting;
|
||||
bool isFake;
|
||||
bool isUnmounting;
|
||||
|
||||
vol->GetName(volName);
|
||||
vol->GetMountPoint(mountPoint);
|
||||
@ -2659,6 +2660,7 @@ ContentParent::Observe(nsISupports* aSubject,
|
||||
vol->GetIsSharing(&isSharing);
|
||||
vol->GetIsFormatting(&isFormatting);
|
||||
vol->GetIsFake(&isFake);
|
||||
vol->GetIsUnmounting(&isUnmounting);
|
||||
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
if (!(IsNuwaReady() && IsNuwaProcess()))
|
||||
@ -2666,7 +2668,8 @@ ContentParent::Observe(nsISupports* aSubject,
|
||||
{
|
||||
unused << SendFileSystemUpdate(volName, mountPoint, state,
|
||||
mountGeneration, isMediaPresent,
|
||||
isSharing, isFormatting, isFake);
|
||||
isSharing, isFormatting, isFake,
|
||||
isUnmounting);
|
||||
}
|
||||
} else if (!strcmp(aTopic, "phone-state-changed")) {
|
||||
nsString state(aData);
|
||||
|
@ -301,6 +301,7 @@ struct VolumeInfo {
|
||||
bool isSharing;
|
||||
bool isFormatting;
|
||||
bool isFake;
|
||||
bool isUnmounting;
|
||||
};
|
||||
|
||||
union MaybeFileDesc {
|
||||
@ -449,7 +450,7 @@ child:
|
||||
// VolumeInfo above.
|
||||
FileSystemUpdate(nsString fsName, nsString mountPoint, int32_t fsState,
|
||||
int32_t mountGeneration, bool isMediaPresent,
|
||||
bool isSharing, bool isFormatting, bool isFake);
|
||||
bool isSharing, bool isFormatting, bool isFake, bool isUnmounting);
|
||||
|
||||
// Ask the Nuwa process to create a new child process.
|
||||
NuwaFork();
|
||||
|
@ -25,6 +25,8 @@ const MOBILENETWORKINFO_CID =
|
||||
Components.ID("{a6c8416c-09b4-46d1-bf29-6520d677d085}");
|
||||
const MOBILECELLINFO_CID =
|
||||
Components.ID("{0635d9ab-997e-4cdf-84e7-c1883752dff3}");
|
||||
const TELEPHONYCALLBACK_CID =
|
||||
Components.ID("{6e1af17e-37f3-11e4-aed3-60a44c237d2b}");
|
||||
|
||||
const NS_XPCOM_SHUTDOWN_OBSERVER_ID = "xpcom-shutdown";
|
||||
const NS_PREFBRANCH_PREFCHANGE_TOPIC_ID = "nsPref:changed";
|
||||
@ -44,6 +46,10 @@ XPCOMUtils.defineLazyServiceGetter(this, "gRadioInterfaceLayer",
|
||||
"@mozilla.org/ril;1",
|
||||
"nsIRadioInterfaceLayer");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "gGonkTelephonyService",
|
||||
"@mozilla.org/telephony/telephonyservice;1",
|
||||
"nsIGonkTelephonyService");
|
||||
|
||||
let DEBUG = RIL.DEBUG_RIL;
|
||||
function debug(s) {
|
||||
dump("MobileConnectionService: " + s + "\n");
|
||||
@ -134,6 +140,41 @@ MMIResult.prototype = {
|
||||
additionalInformation: 'r'},
|
||||
};
|
||||
|
||||
/**
|
||||
* Wrap a MobileConnectionCallback to a TelephonyCallback.
|
||||
*/
|
||||
function TelephonyCallback(aCallback) {
|
||||
this.callback = aCallback;
|
||||
}
|
||||
TelephonyCallback.prototype = {
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsITelephonyCallback]),
|
||||
classID: TELEPHONYCALLBACK_CID,
|
||||
|
||||
notifyDialMMI: function(mmiServiceCode) {
|
||||
this.serviceCode = mmiServiceCode;
|
||||
},
|
||||
|
||||
notifyDialMMISuccess: function(result) {
|
||||
this.callback.notifySendCancelMmiSuccess(result);
|
||||
},
|
||||
|
||||
notifyDialMMIError: function(error) {
|
||||
this.callback.notifyError(error, "", this.serviceCode);
|
||||
},
|
||||
|
||||
notifyDialMMIErrorWithInfo: function(error, info) {
|
||||
this.callback.notifyError(error, "", this.serviceCode, info);
|
||||
},
|
||||
|
||||
notifyDialError: function() {
|
||||
throw Cr.NS_ERROR_UNEXPECTED;
|
||||
},
|
||||
|
||||
notifyDialSuccess: function() {
|
||||
throw Cr.NS_ERROR_UNEXPECTED;
|
||||
},
|
||||
};
|
||||
|
||||
function MobileConnectionProvider(aClientId, aRadioInterface) {
|
||||
this._clientId = aClientId;
|
||||
this._radioInterface = aRadioInterface;
|
||||
@ -188,7 +229,7 @@ MobileConnectionProvider.prototype = {
|
||||
key = "ro.telephony.default_network";
|
||||
let indexString = libcutils.property_get(key, "");
|
||||
let index = parseInt(indexString, 10);
|
||||
if (DEBUG) this._debug("Fallback to " + key + ": " + index)
|
||||
if (DEBUG) this._debug("Fallback to " + key + ": " + index);
|
||||
|
||||
let networkTypes = RIL.RIL_PREFERRED_NETWORK_TYPE_TO_GECKO[index];
|
||||
supportedNetworkTypes = networkTypes ?
|
||||
@ -382,10 +423,7 @@ MobileConnectionProvider.prototype = {
|
||||
},
|
||||
|
||||
_rulesToCallForwardingOptions: function(aRules) {
|
||||
for (let i = 0; i < aRules.length; i++) {
|
||||
let info = new CallForwardingOptions(aRules[i]);
|
||||
aRules[i] = info;
|
||||
}
|
||||
return aRules.map(rule => new CallForwardingOptions(rule));
|
||||
},
|
||||
|
||||
_dispatchNotifyError: function(aCallback, aErrorMsg) {
|
||||
@ -515,6 +553,13 @@ MobileConnectionProvider.prototype = {
|
||||
this.deliverListenerEvent("notifyRadioStateChanged");
|
||||
},
|
||||
|
||||
notifyCFStateChanged: function(aAction, aReason, aNumber, aTimeSeconds,
|
||||
aServiceClass) {
|
||||
this.deliverListenerEvent("notifyCFStateChanged",
|
||||
[true, aAction, aReason, aNumber, aTimeSeconds,
|
||||
aServiceClass]);
|
||||
},
|
||||
|
||||
getSupportedNetworkTypes: function(aTypes) {
|
||||
aTypes.value = this.supportedNetworkTypes.slice();
|
||||
return aTypes.value.length;
|
||||
@ -680,49 +725,8 @@ MobileConnectionProvider.prototype = {
|
||||
},
|
||||
|
||||
sendMMI: function(aMmi, aCallback) {
|
||||
this._radioInterface.sendWorkerMessage("sendMMI", {mmi: aMmi},
|
||||
(function(aResponse) {
|
||||
aResponse.serviceCode = aResponse.mmiServiceCode || "";
|
||||
// We expect to have an IMEI at this point if the request was supposed
|
||||
// to query for the IMEI, so getting a successful reply from the RIL
|
||||
// without containing an actual IMEI number is considered an error.
|
||||
if (aResponse.serviceCode === RIL.MMI_KS_SC_IMEI &&
|
||||
!aResponse.statusMessage) {
|
||||
aResponse.errorMsg = aResponse.errorMsg ||
|
||||
RIL.GECKO_ERROR_GENERIC_FAILURE;
|
||||
}
|
||||
|
||||
if (aResponse.errorMsg) {
|
||||
if (aResponse.additionalInformation) {
|
||||
aCallback.notifyError(aResponse.errorMsg, "",
|
||||
aResponse.serviceCode,
|
||||
aResponse.additionalInformation);
|
||||
} else {
|
||||
aCallback.notifyError(aResponse.errorMsg, "",
|
||||
aResponse.serviceCode);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (aResponse.isSetCallForward) {
|
||||
this.deliverListenerEvent("notifyCFStateChanged",
|
||||
[!aResponse.errorMsg, aResponse.action,
|
||||
aResponse.reason, aResponse.number,
|
||||
aResponse.timeSeconds, aResponse.serviceClass]);
|
||||
}
|
||||
|
||||
// MMI query call forwarding options request returns a set of rules that
|
||||
// will be exposed in the form of an array of MozCallForwardingOptions
|
||||
// instances.
|
||||
if (aResponse.serviceCode === RIL.MMI_KS_SC_CALL_FORWARDING &&
|
||||
aResponse.additionalInformation) {
|
||||
this._rulesToCallForwardingOptions(aResponse.additionalInformation);
|
||||
}
|
||||
|
||||
let mmiResult = new MMIResult(aResponse);
|
||||
aCallback.notifySendCancelMmiSuccess(mmiResult);
|
||||
return false;
|
||||
}).bind(this));
|
||||
let telephonyCallback = new TelephonyCallback(aCallback);
|
||||
gGonkTelephonyService.dialMMI(this._clientId, aMmi, telephonyCallback);
|
||||
},
|
||||
|
||||
cancelMMI: function(aCallback) {
|
||||
@ -762,11 +766,9 @@ MobileConnectionProvider.prototype = {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.deliverListenerEvent("notifyCFStateChanged",
|
||||
[!aResponse.errorMsg, aResponse.action,
|
||||
aResponse.reason, aResponse.number,
|
||||
aResponse.timeSeconds, aResponse.serviceClass]);
|
||||
|
||||
this.notifyCFStateChanged(aResponse.action, aResponse.reason,
|
||||
aResponse.number, aResponse.timeSeconds,
|
||||
aResponse.serviceClass);
|
||||
aCallback.notifySuccess();
|
||||
return false;
|
||||
}).bind(this));
|
||||
@ -786,9 +788,8 @@ MobileConnectionProvider.prototype = {
|
||||
return false;
|
||||
}
|
||||
|
||||
let infos = aResponse.rules;
|
||||
this._rulesToCallForwardingOptions(infos);
|
||||
aCallback.notifyGetCallForwardingSuccess(infos);
|
||||
aCallback.notifyGetCallForwardingSuccess(
|
||||
this._rulesToCallForwardingOptions(aResponse.rules));
|
||||
return false;
|
||||
}).bind(this));
|
||||
},
|
||||
@ -1216,6 +1217,17 @@ MobileConnectionService.prototype = {
|
||||
provider.deliverListenerEvent("notifyLastKnownHomeNetworkChanged");
|
||||
},
|
||||
|
||||
notifyCFStateChanged: function(aClientId, aAction, aReason, aNumber,
|
||||
aTimeSeconds, aServiceClass) {
|
||||
if (DEBUG) {
|
||||
debug("notifyCFStateChanged for " + aClientId);
|
||||
}
|
||||
|
||||
let provider = this.getItemByServiceId(aClientId);
|
||||
provider.notifyCFStateChanged(aAction, aReason, aNumber, aTimeSeconds,
|
||||
aServiceClass);
|
||||
},
|
||||
|
||||
/**
|
||||
* nsIObserver interface.
|
||||
*/
|
||||
|
@ -9,7 +9,7 @@
|
||||
"@mozilla.org/mobileconnection/gonkmobileconnectionservice;1"
|
||||
%}
|
||||
|
||||
[scriptable, uuid(e54fa0a4-d357-48ef-9a1e-ffc9705b44b1)]
|
||||
[scriptable, uuid(b0310517-e7f6-4fa5-a52e-fa6ff35c8fc1)]
|
||||
interface nsIGonkMobileConnectionService : nsIMobileConnectionService
|
||||
{
|
||||
void notifyNetworkInfoChanged(in unsigned long clientId, in jsval networkInfo);
|
||||
@ -46,4 +46,11 @@ interface nsIGonkMobileConnectionService : nsIMobileConnectionService
|
||||
|
||||
void notifyLastHomeNetworkChanged(in unsigned long clientId,
|
||||
in DOMString network);
|
||||
|
||||
void notifyCFStateChanged(in unsigned long clientId,
|
||||
in unsigned short action,
|
||||
in unsigned short reason,
|
||||
in DOMString number,
|
||||
in unsigned short timeSeconds,
|
||||
in unsigned short serviceClass);
|
||||
};
|
||||
|
@ -33,7 +33,7 @@ function testInvalidMMICode() {
|
||||
ok(true, MMI_CODE + " fail");
|
||||
is(aError.name, "emMmiError", "MMI error name");
|
||||
is(aError.message, "", "No message");
|
||||
is(aError.serviceCode, "", "No serviceCode");
|
||||
is(aError.serviceCode, "scUssd", "Service code USSD");
|
||||
is(aError.additionalInformation, null, "No additional information");
|
||||
});
|
||||
}
|
||||
|
@ -86,7 +86,7 @@ NotificationStorage.prototype = {
|
||||
timestamp: new Date().getTime(),
|
||||
origin: origin,
|
||||
data: data,
|
||||
behavior: behavior
|
||||
mozbehavior: behavior
|
||||
};
|
||||
|
||||
this._notifications[id] = notification;
|
||||
@ -207,7 +207,7 @@ NotificationStorage.prototype = {
|
||||
notification.tag,
|
||||
notification.icon,
|
||||
notification.data,
|
||||
notification.behavior);
|
||||
notification.mozbehavior);
|
||||
} catch (e) {
|
||||
if (DEBUG) { debug("Error calling callback handle: " + e); }
|
||||
}
|
||||
|
@ -909,14 +909,16 @@ AutoMounter::UpdateState()
|
||||
break;
|
||||
}
|
||||
|
||||
// Mark the volume as if we've started sharing. This will cause
|
||||
// apps which watch device storage notifications to see the volume
|
||||
// go into the shared state, and prompt them to close any open files
|
||||
// that they might have.
|
||||
// Mark the volume as if we've started sharing/formatting/unmmounting.
|
||||
// This will cause apps which watch device storage notifications to see
|
||||
// the volume go into the different state, and prompt them to close any
|
||||
// open files that they might have.
|
||||
if (tryToShare && vol->IsSharingEnabled()) {
|
||||
vol->SetIsSharing(true);
|
||||
} else if (vol->IsFormatRequested()){
|
||||
vol->SetIsFormatting(true);
|
||||
} else if (vol->IsUnmountRequested()){
|
||||
vol->SetIsUnmounting(true);
|
||||
}
|
||||
|
||||
// Check to see if there are any open files on the volume and
|
||||
|
@ -67,6 +67,7 @@ Volume::Volume(const nsCSubstring& aName)
|
||||
mCanBeShared(true),
|
||||
mIsSharing(false),
|
||||
mIsFormatting(false),
|
||||
mIsUnmounting(false),
|
||||
mId(sNextId++)
|
||||
{
|
||||
DBG("Volume %s: created", NameStr());
|
||||
@ -98,6 +99,18 @@ Volume::SetIsFormatting(bool aIsFormatting)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Volume::SetIsUnmounting(bool aIsUnmounting)
|
||||
{
|
||||
if (aIsUnmounting == mIsUnmounting) {
|
||||
return;
|
||||
}
|
||||
mIsUnmounting = aIsUnmounting;
|
||||
LOG("Volume %s: IsUnmounting set to %d state %s",
|
||||
NameStr(), (int)mIsUnmounting, StateStr(mState));
|
||||
mEventObserverList.Broadcast(this);
|
||||
}
|
||||
|
||||
void
|
||||
Volume::SetMediaPresent(bool aMediaPresent)
|
||||
{
|
||||
@ -201,17 +214,20 @@ Volume::SetState(Volume::STATE aNewState)
|
||||
mIsSharing = false;
|
||||
mUnmountRequested = false;
|
||||
mMountRequested = false;
|
||||
mIsUnmounting = false;
|
||||
break;
|
||||
|
||||
case nsIVolume::STATE_MOUNTED:
|
||||
mMountRequested = false;
|
||||
mIsFormatting = false;
|
||||
mIsSharing = false;
|
||||
mIsUnmounting = false;
|
||||
break;
|
||||
case nsIVolume::STATE_FORMATTING:
|
||||
mFormatRequested = false;
|
||||
mIsFormatting = true;
|
||||
mIsSharing = false;
|
||||
mIsUnmounting = false;
|
||||
break;
|
||||
|
||||
case nsIVolume::STATE_SHARED:
|
||||
@ -221,6 +237,14 @@ Volume::SetState(Volume::STATE aNewState)
|
||||
// it's conceivable that a volume could already be in a shared state
|
||||
// when b2g starts.
|
||||
mIsSharing = true;
|
||||
mIsUnmounting = false;
|
||||
mIsFormatting = false;
|
||||
break;
|
||||
|
||||
case nsIVolume::STATE_UNMOUNTING:
|
||||
mIsUnmounting = true;
|
||||
mIsFormatting = false;
|
||||
mIsSharing = false;
|
||||
break;
|
||||
|
||||
case nsIVolume::STATE_IDLE:
|
||||
|
@ -58,6 +58,7 @@ public:
|
||||
bool IsUnmountRequested() const { return CanBeMounted() && mUnmountRequested; }
|
||||
bool IsSharing() const { return mIsSharing; }
|
||||
bool IsFormatting() const { return mIsFormatting; }
|
||||
bool IsUnmounting() const { return mIsUnmounting; }
|
||||
|
||||
void SetSharingEnabled(bool aSharingEnabled);
|
||||
void SetFormatRequested(bool aFormatRequested);
|
||||
@ -88,6 +89,7 @@ private:
|
||||
|
||||
void SetIsSharing(bool aIsSharing);
|
||||
void SetIsFormatting(bool aIsFormatting);
|
||||
void SetIsUnmounting(bool aIsUnmounting);
|
||||
void SetState(STATE aNewState);
|
||||
void SetMediaPresent(bool aMediaPresent);
|
||||
void SetMountPoint(const nsCSubstring& aMountPoint);
|
||||
@ -112,6 +114,7 @@ private:
|
||||
bool mCanBeShared;
|
||||
bool mIsSharing;
|
||||
bool mIsFormatting;
|
||||
bool mIsUnmounting;
|
||||
uint32_t mId; // Unique ID (used by MTP)
|
||||
|
||||
static EventObserverList mEventObserverList;
|
||||
|
@ -5,7 +5,7 @@
|
||||
#include "nsISupports.idl"
|
||||
#include "nsIVolumeStat.idl"
|
||||
|
||||
[scriptable, uuid(13caa69c-8f1f-11e3-8e36-10bf48d707fb)]
|
||||
[scriptable, uuid(9D0DC356-395D-11E4-9306-A97C1D5D46B0)]
|
||||
interface nsIVolume : nsISupports
|
||||
{
|
||||
// These MUST match the states from android's system/vold/Volume.h header
|
||||
@ -67,6 +67,8 @@ interface nsIVolume : nsISupports
|
||||
// once the volume has been formatted and mounted again.
|
||||
readonly attribute boolean isFormatting;
|
||||
|
||||
readonly attribute boolean isUnmounting;
|
||||
|
||||
nsIVolumeStat getStats();
|
||||
|
||||
// Formats the volume in IO thread, if the volume is ready to be formatted.
|
||||
|
@ -56,7 +56,8 @@ nsVolume::nsVolume(const Volume* aVolume)
|
||||
mIsFake(false),
|
||||
mIsMediaPresent(aVolume->MediaPresent()),
|
||||
mIsSharing(aVolume->IsSharing()),
|
||||
mIsFormatting(aVolume->IsFormatting())
|
||||
mIsFormatting(aVolume->IsFormatting()),
|
||||
mIsUnmounting(aVolume->IsUnmounting())
|
||||
{
|
||||
}
|
||||
|
||||
@ -110,33 +111,45 @@ bool nsVolume::Equals(nsIVolume* aVolume)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isUnmounting;
|
||||
aVolume->GetIsUnmounting(&isUnmounting);
|
||||
if (mIsUnmounting != isUnmounting) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsVolume::GetIsMediaPresent(bool *aIsMediaPresent)
|
||||
NS_IMETHODIMP nsVolume::GetIsMediaPresent(bool* aIsMediaPresent)
|
||||
{
|
||||
*aIsMediaPresent = mIsMediaPresent;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsVolume::GetIsMountLocked(bool *aIsMountLocked)
|
||||
NS_IMETHODIMP nsVolume::GetIsMountLocked(bool* aIsMountLocked)
|
||||
{
|
||||
*aIsMountLocked = mMountLocked;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsVolume::GetIsSharing(bool *aIsSharing)
|
||||
NS_IMETHODIMP nsVolume::GetIsSharing(bool* aIsSharing)
|
||||
{
|
||||
*aIsSharing = mIsSharing;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsVolume::GetIsFormatting(bool *aIsFormatting)
|
||||
NS_IMETHODIMP nsVolume::GetIsFormatting(bool* aIsFormatting)
|
||||
{
|
||||
*aIsFormatting = mIsFormatting;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsVolume::GetIsUnmounting(bool* aIsUnmounting)
|
||||
{
|
||||
*aIsUnmounting = mIsUnmounting;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsVolume::GetName(nsAString& aName)
|
||||
{
|
||||
aName = mName;
|
||||
@ -262,11 +275,11 @@ nsVolume::LogState() const
|
||||
{
|
||||
if (mState == nsIVolume::STATE_MOUNTED) {
|
||||
LOG("nsVolume: %s state %s @ '%s' gen %d locked %d fake %d "
|
||||
"media %d sharing %d formatting %d",
|
||||
"media %d sharing %d formatting %d unmounting %d",
|
||||
NameStr().get(), StateStr(), MountPointStr().get(),
|
||||
MountGeneration(), (int)IsMountLocked(), (int)IsFake(),
|
||||
(int)IsMediaPresent(), (int)IsSharing(),
|
||||
(int)IsFormatting());
|
||||
(int)IsFormatting(), (int)IsUnmounting());
|
||||
return;
|
||||
}
|
||||
|
||||
@ -284,6 +297,7 @@ void nsVolume::Set(nsIVolume* aVolume)
|
||||
aVolume->GetIsMediaPresent(&mIsMediaPresent);
|
||||
aVolume->GetIsSharing(&mIsSharing);
|
||||
aVolume->GetIsFormatting(&mIsFormatting);
|
||||
aVolume->GetIsUnmounting(&mIsUnmounting);
|
||||
|
||||
int32_t volMountGeneration;
|
||||
aVolume->GetMountGeneration(&volMountGeneration);
|
||||
|
@ -30,7 +30,8 @@ public:
|
||||
nsVolume(const nsAString& aName, const nsAString& aMountPoint,
|
||||
const int32_t& aState, const int32_t& aMountGeneration,
|
||||
const bool& aIsMediaPresent, const bool& aIsSharing,
|
||||
const bool& aIsFormatting, const bool& aIsFake)
|
||||
const bool& aIsFormatting, const bool& aIsFake,
|
||||
const bool& aIsUnmounting)
|
||||
: mName(aName),
|
||||
mMountPoint(aMountPoint),
|
||||
mState(aState),
|
||||
@ -39,7 +40,8 @@ public:
|
||||
mIsFake(aIsFake),
|
||||
mIsMediaPresent(aIsMediaPresent),
|
||||
mIsSharing(aIsSharing),
|
||||
mIsFormatting(aIsFormatting)
|
||||
mIsFormatting(aIsFormatting),
|
||||
mIsUnmounting(aIsUnmounting)
|
||||
{
|
||||
}
|
||||
|
||||
@ -53,7 +55,8 @@ public:
|
||||
mIsFake(false),
|
||||
mIsMediaPresent(false),
|
||||
mIsSharing(false),
|
||||
mIsFormatting(false)
|
||||
mIsFormatting(false),
|
||||
mIsUnmounting(false)
|
||||
{
|
||||
}
|
||||
|
||||
@ -78,6 +81,7 @@ public:
|
||||
bool IsMediaPresent() const { return mIsMediaPresent; }
|
||||
bool IsSharing() const { return mIsSharing; }
|
||||
bool IsFormatting() const { return mIsFormatting; }
|
||||
bool IsUnmounting() const { return mIsUnmounting; }
|
||||
|
||||
typedef nsTArray<nsRefPtr<nsVolume> > Array;
|
||||
|
||||
@ -103,6 +107,7 @@ private:
|
||||
bool mIsMediaPresent;
|
||||
bool mIsSharing;
|
||||
bool mIsFormatting;
|
||||
bool mIsUnmounting;
|
||||
};
|
||||
|
||||
} // system
|
||||
|
@ -208,7 +208,8 @@ nsVolumeService::CreateOrGetVolumeByPath(const nsAString& aPath, nsIVolume** aRe
|
||||
true /* isMediaPresent*/,
|
||||
false /* isSharing */,
|
||||
false /* isFormatting */,
|
||||
true /* isFake */);
|
||||
true /* isFake */,
|
||||
false /* isUnmounting*/);
|
||||
vol.forget(aResult);
|
||||
return NS_OK;
|
||||
}
|
||||
@ -269,6 +270,7 @@ nsVolumeService::GetVolumesForIPC(nsTArray<VolumeInfo>* aResult)
|
||||
volInfo->isSharing() = vol->mIsSharing;
|
||||
volInfo->isFormatting() = vol->mIsFormatting;
|
||||
volInfo->isFake() = vol->mIsFake;
|
||||
volInfo->isUnmounting() = vol->mIsUnmounting;
|
||||
}
|
||||
}
|
||||
|
||||
@ -296,7 +298,8 @@ nsVolumeService::GetVolumesFromParent()
|
||||
volInfo.isMediaPresent(),
|
||||
volInfo.isSharing(),
|
||||
volInfo.isFormatting(),
|
||||
volInfo.isFake());
|
||||
volInfo.isFake(),
|
||||
volInfo.isUnmounting());
|
||||
UpdateVolume(vol, false);
|
||||
}
|
||||
}
|
||||
@ -420,7 +423,8 @@ nsVolumeService::CreateFakeVolume(const nsAString& name, const nsAString& path)
|
||||
true /* isMediaPresent */,
|
||||
false /* isSharing */,
|
||||
false /* isFormatting */,
|
||||
true /* isFake */);
|
||||
true /* isFake */,
|
||||
false /* isUnmounting */);
|
||||
vol->LogState();
|
||||
UpdateVolume(vol.get());
|
||||
return NS_OK;
|
||||
@ -470,11 +474,11 @@ public:
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
DBG("UpdateVolumeRunnable::Run '%s' state %s gen %d locked %d "
|
||||
"media %d sharing %d formatting %d",
|
||||
"media %d sharing %d formatting %d unmounting %d",
|
||||
mVolume->NameStr().get(), mVolume->StateStr(),
|
||||
mVolume->MountGeneration(), (int)mVolume->IsMountLocked(),
|
||||
(int)mVolume->IsMediaPresent(), mVolume->IsSharing(),
|
||||
mVolume->IsFormatting());
|
||||
mVolume->IsFormatting(), mVolume->IsUnmounting());
|
||||
|
||||
mVolumeService->UpdateVolume(mVolume);
|
||||
mVolumeService = nullptr;
|
||||
@ -491,11 +495,11 @@ void
|
||||
nsVolumeService::UpdateVolumeIOThread(const Volume* aVolume)
|
||||
{
|
||||
DBG("UpdateVolumeIOThread: Volume '%s' state %s mount '%s' gen %d locked %d "
|
||||
"media %d sharing %d formatting %d",
|
||||
"media %d sharing %d formatting %d unmounting %d",
|
||||
aVolume->NameStr(), aVolume->StateStr(), aVolume->MountPoint().get(),
|
||||
aVolume->MountGeneration(), (int)aVolume->IsMountLocked(),
|
||||
(int)aVolume->MediaPresent(), (int)aVolume->IsSharing(),
|
||||
(int)aVolume->IsFormatting());
|
||||
(int)aVolume->IsFormatting(), (int)mVolume->IsUnmounting());
|
||||
MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
|
||||
NS_DispatchToMainThread(new UpdateVolumeRunnable(this, aVolume));
|
||||
}
|
||||
|
@ -57,20 +57,6 @@ const EMERGENCY_CB_MODE_TIMEOUT_MS = 300000; // 5 mins = 300000 ms.
|
||||
|
||||
const ICC_MAX_LINEAR_FIXED_RECORDS = 0xfe;
|
||||
|
||||
// MMI match groups
|
||||
const MMI_MATCH_GROUP_FULL_MMI = 1;
|
||||
const MMI_MATCH_GROUP_PROCEDURE = 2;
|
||||
const MMI_MATCH_GROUP_SERVICE_CODE = 3;
|
||||
const MMI_MATCH_GROUP_SIA = 4;
|
||||
const MMI_MATCH_GROUP_SIB = 5;
|
||||
const MMI_MATCH_GROUP_SIC = 6;
|
||||
const MMI_MATCH_GROUP_PWD_CONFIRM = 7;
|
||||
const MMI_MATCH_GROUP_DIALING_NUMBER = 8;
|
||||
|
||||
const MMI_MAX_LENGTH_SHORT_CODE = 2;
|
||||
|
||||
const MMI_END_OF_USSD = "#";
|
||||
|
||||
const GET_CURRENT_CALLS_RETRY_MAX = 3;
|
||||
|
||||
let RILQUIRKS_CALLSTATE_EXTRA_UINT32;
|
||||
@ -2402,208 +2388,11 @@ RilObject.prototype = {
|
||||
{callback: callback});
|
||||
},
|
||||
|
||||
/**
|
||||
* Parse the dial number to extract its mmi code part.
|
||||
*
|
||||
* @param number
|
||||
* Phone number to be parsed
|
||||
*/
|
||||
parseMMIFromDialNumber: function(options) {
|
||||
// We don't have to parse mmi in cdma.
|
||||
if (!this._isCdma) {
|
||||
options.mmi = this._parseMMI(options.number);
|
||||
}
|
||||
this.sendChromeMessage(options);
|
||||
},
|
||||
|
||||
/**
|
||||
* Helper to parse MMI/USSD string. TS.22.030 Figure 3.5.3.2.
|
||||
*/
|
||||
_parseMMI: function(mmiString) {
|
||||
if (!mmiString || !mmiString.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let matches = this._getMMIRegExp().exec(mmiString);
|
||||
if (matches) {
|
||||
return {
|
||||
fullMMI: matches[MMI_MATCH_GROUP_FULL_MMI],
|
||||
procedure: matches[MMI_MATCH_GROUP_PROCEDURE],
|
||||
serviceCode: matches[MMI_MATCH_GROUP_SERVICE_CODE],
|
||||
sia: matches[MMI_MATCH_GROUP_SIA],
|
||||
sib: matches[MMI_MATCH_GROUP_SIB],
|
||||
sic: matches[MMI_MATCH_GROUP_SIC],
|
||||
pwd: matches[MMI_MATCH_GROUP_PWD_CONFIRM],
|
||||
dialNumber: matches[MMI_MATCH_GROUP_DIALING_NUMBER]
|
||||
};
|
||||
}
|
||||
|
||||
if (this._isPoundString(mmiString) || this._isMMIShortString(mmiString)) {
|
||||
return {
|
||||
fullMMI: mmiString
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Build the regex to parse MMI string.
|
||||
*
|
||||
* The resulting groups after matching will be:
|
||||
* 1 = full MMI string that might be used as a USSD request.
|
||||
* 2 = MMI procedure.
|
||||
* 3 = Service code.
|
||||
* 4 = SIA.
|
||||
* 5 = SIB.
|
||||
* 6 = SIC.
|
||||
* 7 = Password registration.
|
||||
* 8 = Dialing number.
|
||||
*
|
||||
* @see TS.22.030 Figure 3.5.3.2.
|
||||
*/
|
||||
_buildMMIRegExp: function() {
|
||||
// The general structure of the codes is as follows:
|
||||
// - Activation (*SC*SI#).
|
||||
// - Deactivation (#SC*SI#).
|
||||
// - Interrogation (*#SC*SI#).
|
||||
// - Registration (**SC*SI#).
|
||||
// - Erasure (##SC*SI#).
|
||||
//
|
||||
// where SC = Service Code (2 or 3 digits) and SI = Supplementary Info
|
||||
// (variable length).
|
||||
|
||||
// MMI procedure, which could be *, #, *#, **, ##
|
||||
let procedure = "(\\*[*#]?|##?)";
|
||||
|
||||
// MMI Service code, which is a 2 or 3 digits that uniquely specifies the
|
||||
// Supplementary Service associated with the MMI code.
|
||||
let serviceCode = "(\\d{2,3})";
|
||||
|
||||
// MMI Supplementary Information SIA, SIB and SIC. SIA may comprise e.g. a
|
||||
// PIN code or Directory Number, SIB may be used to specify the tele or
|
||||
// bearer service and SIC to specify the value of the "No Reply Condition
|
||||
// Timer". Where a particular service request does not require any SI,
|
||||
// "*SI" is not entered. The use of SIA, SIB and SIC is optional and shall
|
||||
// be entered in any of the following formats:
|
||||
// - *SIA*SIB*SIC#
|
||||
// - *SIA*SIB#
|
||||
// - *SIA**SIC#
|
||||
// - *SIA#
|
||||
// - **SIB*SIC#
|
||||
// - ***SIC#
|
||||
//
|
||||
// Also catch the additional NEW_PASSWORD for the case of a password
|
||||
// registration procedure. Ex:
|
||||
// - * 03 * ZZ * OLD_PASSWORD * NEW_PASSWORD * NEW_PASSWORD #
|
||||
// - ** 03 * ZZ * OLD_PASSWORD * NEW_PASSWORD * NEW_PASSWORD #
|
||||
// - * 03 ** OLD_PASSWORD * NEW_PASSWORD * NEW_PASSWORD #
|
||||
// - ** 03 ** OLD_PASSWORD * NEW_PASSWORD * NEW_PASSWORD #
|
||||
let si = "\\*([^*#]*)";
|
||||
let allSi = "";
|
||||
for (let i = 0; i < 4; ++i) {
|
||||
allSi = "(?:" + si + allSi + ")?";
|
||||
}
|
||||
|
||||
let fullmmi = "(" + procedure + serviceCode + allSi + "#)";
|
||||
|
||||
// dial string after the #.
|
||||
let dialString = "([^#]*)";
|
||||
|
||||
return new RegExp(fullmmi + dialString);
|
||||
},
|
||||
|
||||
/**
|
||||
* Provide the regex to parse MMI string.
|
||||
*/
|
||||
_getMMIRegExp: function() {
|
||||
if (!this._mmiRegExp) {
|
||||
this._mmiRegExp = this._buildMMIRegExp();
|
||||
}
|
||||
|
||||
return this._mmiRegExp;
|
||||
},
|
||||
|
||||
/**
|
||||
* Helper to parse # string. TS.22.030 Figure 3.5.3.2.
|
||||
*/
|
||||
_isPoundString: function(mmiString) {
|
||||
return (mmiString.charAt(mmiString.length - 1) === MMI_END_OF_USSD);
|
||||
},
|
||||
|
||||
/**
|
||||
* Helper to parse short string. TS.22.030 Figure 3.5.3.2.
|
||||
*/
|
||||
_isMMIShortString: function(mmiString) {
|
||||
if (mmiString.length > 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: Should take care of checking if the string is an emergency number
|
||||
// in Bug 889737. See Bug 1023141 for more background.
|
||||
|
||||
// In a call case.
|
||||
if (Object.getOwnPropertyNames(this.currentCalls).length > 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Input string is 2 digits starting with a "1"
|
||||
if ((mmiString.length == 2) && (mmiString.charAt(0) === '1')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
_serviceCodeToKeyString: function(serviceCode) {
|
||||
switch (serviceCode) {
|
||||
case MMI_SC_CFU:
|
||||
case MMI_SC_CF_BUSY:
|
||||
case MMI_SC_CF_NO_REPLY:
|
||||
case MMI_SC_CF_NOT_REACHABLE:
|
||||
case MMI_SC_CF_ALL:
|
||||
case MMI_SC_CF_ALL_CONDITIONAL:
|
||||
return MMI_KS_SC_CALL_FORWARDING;
|
||||
case MMI_SC_PIN:
|
||||
return MMI_KS_SC_PIN;
|
||||
case MMI_SC_PIN2:
|
||||
return MMI_KS_SC_PIN2;
|
||||
case MMI_SC_PUK:
|
||||
return MMI_KS_SC_PUK;
|
||||
case MMI_SC_PUK2:
|
||||
return MMI_KS_SC_PUK2;
|
||||
case MMI_SC_IMEI:
|
||||
return MMI_KS_SC_IMEI;
|
||||
case MMI_SC_CLIP:
|
||||
return MMI_KS_SC_CLIP;
|
||||
case MMI_SC_CLIR:
|
||||
return MMI_KS_SC_CLIR;
|
||||
case MMI_SC_BAOC:
|
||||
case MMI_SC_BAOIC:
|
||||
case MMI_SC_BAOICxH:
|
||||
case MMI_SC_BAIC:
|
||||
case MMI_SC_BAICr:
|
||||
case MMI_SC_BA_ALL:
|
||||
case MMI_SC_BA_MO:
|
||||
case MMI_SC_BA_MT:
|
||||
return MMI_KS_SC_CALL_BARRING;
|
||||
case MMI_SC_CALL_WAITING:
|
||||
return MMI_KS_SC_CALL_WAITING;
|
||||
default:
|
||||
return MMI_KS_SC_USSD;
|
||||
}
|
||||
},
|
||||
|
||||
sendMMI: function(options) {
|
||||
if (DEBUG) {
|
||||
this.context.debug("SendMMI " + JSON.stringify(options));
|
||||
}
|
||||
|
||||
let mmi = this._parseMMI(options.mmi);
|
||||
if (DEBUG) {
|
||||
this.context.debug("MMI " + JSON.stringify(mmi));
|
||||
}
|
||||
|
||||
let _sendMMIError = (function(errorMsg) {
|
||||
options.success = false;
|
||||
options.errorMsg = errorMsg;
|
||||
@ -2611,14 +2400,12 @@ RilObject.prototype = {
|
||||
}).bind(this);
|
||||
|
||||
// It's neither a valid mmi code nor an ongoing ussd.
|
||||
let mmi = options.mmi;
|
||||
if (!mmi && !this._ussdSession) {
|
||||
_sendMMIError(MMI_ERROR_KS_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
options.mmiServiceCode = mmi ?
|
||||
this._serviceCodeToKeyString(mmi.serviceCode) : MMI_KS_SC_USSD;
|
||||
|
||||
function _isValidPINPUKRequest() {
|
||||
// The only allowed MMI procedure for ICC PIN, PIN2, PUK and PUK2 handling
|
||||
// is "Registration" (**).
|
||||
@ -3576,36 +3363,34 @@ RilObject.prototype = {
|
||||
return;
|
||||
}
|
||||
|
||||
let mmiServiceCode = options.mmiServiceCode;
|
||||
let serviceCode = options.mmi.serviceCode;
|
||||
|
||||
if (options.success) {
|
||||
switch (mmiServiceCode) {
|
||||
case MMI_KS_SC_PIN:
|
||||
switch (serviceCode) {
|
||||
case MMI_SC_PIN:
|
||||
options.statusMessage = MMI_SM_KS_PIN_CHANGED;
|
||||
break;
|
||||
case MMI_KS_SC_PIN2:
|
||||
case MMI_SC_PIN2:
|
||||
options.statusMessage = MMI_SM_KS_PIN2_CHANGED;
|
||||
break;
|
||||
case MMI_KS_SC_PUK:
|
||||
case MMI_SC_PUK:
|
||||
options.statusMessage = MMI_SM_KS_PIN_UNBLOCKED;
|
||||
break;
|
||||
case MMI_KS_SC_PUK2:
|
||||
case MMI_SC_PUK2:
|
||||
options.statusMessage = MMI_SM_KS_PIN2_UNBLOCKED;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (options.retryCount <= 0) {
|
||||
if (mmiServiceCode === MMI_KS_SC_PUK) {
|
||||
if (serviceCode === MMI_SC_PUK) {
|
||||
options.errorMsg = MMI_ERROR_KS_SIM_BLOCKED;
|
||||
} else if (mmiServiceCode === MMI_KS_SC_PIN) {
|
||||
} else if (serviceCode === MMI_SC_PIN) {
|
||||
options.errorMsg = MMI_ERROR_KS_NEEDS_PUK;
|
||||
}
|
||||
} else {
|
||||
if (mmiServiceCode === MMI_KS_SC_PIN ||
|
||||
mmiServiceCode === MMI_KS_SC_PIN2) {
|
||||
if (serviceCode === MMI_SC_PIN || serviceCode === MMI_SC_PIN2) {
|
||||
options.errorMsg = MMI_ERROR_KS_BAD_PIN;
|
||||
} else if (mmiServiceCode === MMI_KS_SC_PUK ||
|
||||
mmiServiceCode === MMI_KS_SC_PUK2) {
|
||||
} else if (serviceCode === MMI_SC_PUK || serviceCode === MMI_SC_PUK2) {
|
||||
options.errorMsg = MMI_ERROR_KS_BAD_PUK;
|
||||
}
|
||||
if (options.retryCount !== undefined) {
|
||||
@ -6030,7 +5815,7 @@ RilObject.prototype[REQUEST_QUERY_CALL_FORWARD_STATUS] =
|
||||
this.sendChromeMessage(options);
|
||||
};
|
||||
RilObject.prototype[REQUEST_SET_CALL_FORWARD] =
|
||||
function REQUEST_SET_CALL_FORWARD(length, options) {
|
||||
function REQUEST_SET_CALL_FORWARD(length, options) {
|
||||
options.success = (options.rilRequestError === 0);
|
||||
if (!options.success) {
|
||||
options.errorMsg = RIL_ERROR_TO_GECKO_ERROR[options.rilRequestError];
|
||||
|
@ -7,6 +7,19 @@ function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
function createMMIOptions(procedure, serviceCode, sia, sib, sic) {
|
||||
let mmi = {
|
||||
fullMMI: Array.slice(arguments).join("*") + "#",
|
||||
procedure: procedure,
|
||||
serviceCode: serviceCode,
|
||||
sia: sia,
|
||||
sib: sib,
|
||||
sic: sic
|
||||
};
|
||||
|
||||
return mmi;
|
||||
}
|
||||
|
||||
function testSendMMI(mmi, error) {
|
||||
let workerhelper = newInterceptWorker();
|
||||
let worker = workerhelper.worker;
|
||||
@ -27,20 +40,8 @@ function testSendMMI(mmi, error) {
|
||||
* sendMMI tests.
|
||||
*/
|
||||
|
||||
add_test(function test_sendMMI_empty() {
|
||||
testSendMMI("", MMI_ERROR_KS_ERROR);
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_sendMMI_undefined() {
|
||||
testSendMMI({}, MMI_ERROR_KS_ERROR);
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_sendMMI_invalid() {
|
||||
testSendMMI("11", MMI_ERROR_KS_ERROR);
|
||||
add_test(function test_sendMMI_null() {
|
||||
testSendMMI(null, MMI_ERROR_KS_ERROR);
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
@ -61,7 +62,7 @@ add_test(function test_sendMMI_short_code() {
|
||||
};
|
||||
|
||||
context.RIL.radioState = GECKO_RADIOSTATE_ENABLED;
|
||||
context.RIL.sendMMI({mmi: "**"});
|
||||
context.RIL.sendMMI({mmi: {fullMMI: "**"}});
|
||||
|
||||
let postedMessage = workerhelper.postedMessage;
|
||||
do_check_eq(ussdOptions.ussd, "**");
|
||||
@ -72,12 +73,6 @@ add_test(function test_sendMMI_short_code() {
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_sendMMI_dial_string() {
|
||||
testSendMMI("123", MMI_ERROR_KS_ERROR);
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_sendMMI_change_PIN() {
|
||||
let workerhelper = newInterceptWorker();
|
||||
let worker = workerhelper.worker;
|
||||
@ -90,7 +85,8 @@ add_test(function test_sendMMI_change_PIN() {
|
||||
};
|
||||
|
||||
context.RIL.radioState = GECKO_RADIOSTATE_ENABLED;
|
||||
context.RIL.sendMMI({mmi: "**04*1234*4567*4567#"});
|
||||
context.RIL.sendMMI({mmi: createMMIOptions("**", "04", "1234", "4567",
|
||||
"4567")});
|
||||
|
||||
let postedMessage = workerhelper.postedMessage;
|
||||
|
||||
@ -101,25 +97,29 @@ add_test(function test_sendMMI_change_PIN() {
|
||||
});
|
||||
|
||||
add_test(function test_sendMMI_change_PIN_no_new_PIN() {
|
||||
testSendMMI("**04*1234**4567#", MMI_ERROR_KS_ERROR);
|
||||
testSendMMI(createMMIOptions("**", "04", "1234", "", "4567"),
|
||||
MMI_ERROR_KS_ERROR);
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_sendMMI_change_PIN_no_old_PIN() {
|
||||
testSendMMI("**04**1234*4567#", MMI_ERROR_KS_ERROR);
|
||||
testSendMMI(createMMIOptions("**", "04", "", "1234", "4567"),
|
||||
MMI_ERROR_KS_ERROR);
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_sendMMI_change_PIN_wrong_procedure() {
|
||||
testSendMMI("*04*1234*4567*4567#", MMI_ERROR_KS_INVALID_ACTION);
|
||||
testSendMMI(createMMIOptions("*", "04", "1234", "4567", "4567"),
|
||||
MMI_ERROR_KS_INVALID_ACTION);
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_sendMMI_change_PIN_new_PIN_mismatch() {
|
||||
testSendMMI("**04*4567*1234*4567#", MMI_ERROR_KS_MISMATCH_PIN);
|
||||
testSendMMI(createMMIOptions("**", "04", "4567", "1234", "4567"),
|
||||
MMI_ERROR_KS_MISMATCH_PIN);
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
@ -136,7 +136,8 @@ add_test(function test_sendMMI_change_PIN2() {
|
||||
};
|
||||
|
||||
context.RIL.radioState = GECKO_RADIOSTATE_ENABLED;
|
||||
context.RIL.sendMMI({mmi: "**042*1234*4567*4567#"});
|
||||
context.RIL.sendMMI({mmi: createMMIOptions("**", "042", "1234", "4567",
|
||||
"4567")});
|
||||
|
||||
let postedMessage = workerhelper.postedMessage;
|
||||
|
||||
@ -147,25 +148,29 @@ add_test(function test_sendMMI_change_PIN2() {
|
||||
});
|
||||
|
||||
add_test(function test_sendMMI_change_PIN2_no_new_PIN2() {
|
||||
testSendMMI("**042*1234**4567#", MMI_ERROR_KS_ERROR);
|
||||
testSendMMI(createMMIOptions("**", "042", "1234", "", "4567"),
|
||||
MMI_ERROR_KS_ERROR);
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_sendMMI_change_PIN2_no_old_PIN2() {
|
||||
testSendMMI("**042**1234*4567#", MMI_ERROR_KS_ERROR);
|
||||
testSendMMI(createMMIOptions("**", "042", "", "1234", "4567"),
|
||||
MMI_ERROR_KS_ERROR);
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_sendMMI_change_PIN2_wrong_procedure() {
|
||||
testSendMMI("*042*1234*4567*4567#", MMI_ERROR_KS_INVALID_ACTION);
|
||||
testSendMMI(createMMIOptions("*", "042", "1234", "4567", "4567"),
|
||||
MMI_ERROR_KS_INVALID_ACTION);
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_sendMMI_change_PIN2_new_PIN2_mismatch() {
|
||||
testSendMMI("**042*4567*1234*4567#", MMI_ERROR_KS_MISMATCH_PIN);
|
||||
testSendMMI(createMMIOptions("**", "042", "4567", "1234", "4567"),
|
||||
MMI_ERROR_KS_MISMATCH_PIN);
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
@ -182,7 +187,8 @@ add_test(function test_sendMMI_unblock_PIN() {
|
||||
};
|
||||
|
||||
context.RIL.radioState = GECKO_RADIOSTATE_ENABLED;
|
||||
context.RIL.sendMMI({mmi: "**05*1234*4567*4567#"});
|
||||
context.RIL.sendMMI({mmi: createMMIOptions("**", "05", "1234", "4567",
|
||||
"4567")});
|
||||
|
||||
let postedMessage = workerhelper.postedMessage;
|
||||
|
||||
@ -193,25 +199,29 @@ add_test(function test_sendMMI_unblock_PIN() {
|
||||
});
|
||||
|
||||
add_test(function test_sendMMI_unblock_PIN_no_new_PIN() {
|
||||
testSendMMI("**05*1234**4567#", MMI_ERROR_KS_ERROR);
|
||||
testSendMMI(createMMIOptions("**", "05", "1234", "", "4567"),
|
||||
MMI_ERROR_KS_ERROR);
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_sendMMI_unblock_PIN_no_PUK() {
|
||||
testSendMMI("**05**1234*4567#", MMI_ERROR_KS_ERROR);
|
||||
testSendMMI(createMMIOptions("**", "05", "", "1234", "4567"),
|
||||
MMI_ERROR_KS_ERROR);
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_sendMMI_unblock_PIN_wrong_procedure() {
|
||||
testSendMMI("*05*1234*4567*4567#", MMI_ERROR_KS_INVALID_ACTION);
|
||||
testSendMMI(createMMIOptions("*", "05", "1234", "4567", "4567"),
|
||||
MMI_ERROR_KS_INVALID_ACTION);
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_sendMMI_unblock_PIN_new_PIN_mismatch() {
|
||||
testSendMMI("**05*4567*1234*4567#", MMI_ERROR_KS_MISMATCH_PIN);
|
||||
testSendMMI(createMMIOptions("**", "05", "4567", "1234", "4567"),
|
||||
MMI_ERROR_KS_MISMATCH_PIN);
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
@ -228,7 +238,8 @@ add_test(function test_sendMMI_unblock_PIN2() {
|
||||
};
|
||||
|
||||
context.RIL.radioState = GECKO_RADIOSTATE_ENABLED;
|
||||
context.RIL.sendMMI({mmi: "**052*1234*4567*4567#"});
|
||||
context.RIL.sendMMI({mmi: createMMIOptions("**", "052", "1234", "4567",
|
||||
"4567")});
|
||||
|
||||
let postedMessage = workerhelper.postedMessage;
|
||||
|
||||
@ -239,25 +250,29 @@ add_test(function test_sendMMI_unblock_PIN2() {
|
||||
});
|
||||
|
||||
add_test(function test_sendMMI_unblock_PIN2_no_new_PIN2() {
|
||||
testSendMMI("**052*1234**4567#", MMI_ERROR_KS_ERROR);
|
||||
testSendMMI(createMMIOptions("**", "052", "1234", "", "4567"),
|
||||
MMI_ERROR_KS_ERROR);
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_sendMMI_unblock_PIN2_no_PUK2() {
|
||||
testSendMMI("**052**1234*4567#", MMI_ERROR_KS_ERROR);
|
||||
testSendMMI(createMMIOptions("**", "052", "", "1234", "4567"),
|
||||
MMI_ERROR_KS_ERROR);
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_sendMMI_unblock_PIN2_wrong_procedure() {
|
||||
testSendMMI("*052*1234*4567*4567#", MMI_ERROR_KS_INVALID_ACTION);
|
||||
testSendMMI(createMMIOptions("*", "052", "1234", "4567", "4567"),
|
||||
MMI_ERROR_KS_INVALID_ACTION);
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_sendMMI_unblock_PIN2_new_PIN_mismatch() {
|
||||
testSendMMI("**052*4567*1234*4567#", MMI_ERROR_KS_MISMATCH_PIN);
|
||||
testSendMMI(createMMIOptions("**", "052", "4567", "1234", "4567"),
|
||||
MMI_ERROR_KS_MISMATCH_PIN);
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
@ -275,7 +290,7 @@ add_test(function test_sendMMI_get_IMEI() {
|
||||
});
|
||||
};
|
||||
|
||||
context.RIL.sendMMI({mmi: "*#06#"});
|
||||
context.RIL.sendMMI({mmi: createMMIOptions("*#", "06")});
|
||||
|
||||
let postedMessage = workerhelper.postedMessage;
|
||||
|
||||
@ -299,7 +314,7 @@ add_test(function test_sendMMI_get_IMEI_error() {
|
||||
});
|
||||
};
|
||||
|
||||
context.RIL.sendMMI({mmi: "*#06#"});
|
||||
context.RIL.sendMMI({mmi: createMMIOptions("*#", "06")});
|
||||
|
||||
let postedMessage = workerhelper.postedMessage;
|
||||
|
||||
@ -328,7 +343,7 @@ add_test(function test_sendMMI_call_barring_BAIC_interrogation_voice() {
|
||||
};
|
||||
|
||||
context.RIL.radioState = GECKO_RADIOSTATE_ENABLED;
|
||||
context.RIL.sendMMI({mmi: "*#33#"});
|
||||
context.RIL.sendMMI({mmi: createMMIOptions("*#", "33")});
|
||||
|
||||
let postedMessage = workerhelper.postedMessage;
|
||||
|
||||
@ -358,7 +373,7 @@ add_test(function test_sendMMI_call_barring_BAIC_activation() {
|
||||
};
|
||||
|
||||
context.RIL.radioState = GECKO_RADIOSTATE_ENABLED;
|
||||
context.RIL.sendMMI({mmi: "*33#"});
|
||||
context.RIL.sendMMI({mmi: createMMIOptions("*", "33")});
|
||||
|
||||
let postedMessage = workerhelper.postedMessage;
|
||||
|
||||
@ -386,7 +401,7 @@ add_test(function test_sendMMI_call_barring_BAIC_deactivation() {
|
||||
};
|
||||
|
||||
context.RIL.radioState = GECKO_RADIOSTATE_ENABLED;
|
||||
context.RIL.sendMMI({mmi: "#33#"});
|
||||
context.RIL.sendMMI({mmi: createMMIOptions("#", "33")});
|
||||
|
||||
let postedMessage = workerhelper.postedMessage;
|
||||
|
||||
@ -398,7 +413,7 @@ add_test(function test_sendMMI_call_barring_BAIC_deactivation() {
|
||||
});
|
||||
|
||||
add_test(function test_sendMMI_call_barring_BAIC_procedure_not_supported() {
|
||||
testSendMMI("**33*0000#", MMI_ERROR_KS_NOT_SUPPORTED);
|
||||
testSendMMI(createMMIOptions("**", "33", "0000"), MMI_ERROR_KS_NOT_SUPPORTED);
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
@ -417,11 +432,11 @@ add_test(function test_sendMMI_USSD() {
|
||||
};
|
||||
|
||||
context.RIL.radioState = GECKO_RADIOSTATE_ENABLED;
|
||||
context.RIL.sendMMI({mmi: "*123#"});
|
||||
context.RIL.sendMMI({mmi: createMMIOptions("*", "123")});
|
||||
|
||||
let postedMessage = workerhelper.postedMessage;
|
||||
|
||||
do_check_eq(ussdOptions.ussd, "*123#");
|
||||
do_check_eq(ussdOptions.ussd, "**123#");
|
||||
do_check_eq (postedMessage.errorMsg, GECKO_ERROR_SUCCESS);
|
||||
do_check_true(postedMessage.success);
|
||||
do_check_true(context.RIL._ussdSession);
|
||||
@ -443,11 +458,11 @@ add_test(function test_sendMMI_USSD_error() {
|
||||
};
|
||||
|
||||
context.RIL.radioState = GECKO_RADIOSTATE_ENABLED;
|
||||
context.RIL.sendMMI({mmi: "*123#"});
|
||||
context.RIL.sendMMI({mmi: createMMIOptions("*", "123")});
|
||||
|
||||
let postedMessage = workerhelper.postedMessage;
|
||||
|
||||
do_check_eq(ussdOptions.ussd, "*123#");
|
||||
do_check_eq(ussdOptions.ussd, "**123#");
|
||||
do_check_eq (postedMessage.errorMsg, GECKO_ERROR_GENERIC_FAILURE);
|
||||
do_check_false(postedMessage.success);
|
||||
do_check_false(context.RIL._ussdSession);
|
||||
@ -476,25 +491,25 @@ function setCallWaitingSuccess(mmi) {
|
||||
}
|
||||
|
||||
add_test(function test_sendMMI_call_waiting_activation() {
|
||||
setCallWaitingSuccess("*43*10#");
|
||||
setCallWaitingSuccess(createMMIOptions("*", "43", "10"));
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_sendMMI_call_waiting_deactivation() {
|
||||
setCallWaitingSuccess("#43#");
|
||||
setCallWaitingSuccess(createMMIOptions("#", "43"));
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_sendMMI_call_waiting_registration() {
|
||||
testSendMMI("**43#", MMI_ERROR_KS_NOT_SUPPORTED);
|
||||
testSendMMI(createMMIOptions("**", "43"), MMI_ERROR_KS_NOT_SUPPORTED);
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_sendMMI_call_waiting_erasure() {
|
||||
testSendMMI("##43#", MMI_ERROR_KS_NOT_SUPPORTED);
|
||||
testSendMMI(createMMIOptions("##", "43"), MMI_ERROR_KS_NOT_SUPPORTED);
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
@ -520,7 +535,7 @@ add_test(function test_sendMMI_call_waiting_interrogation() {
|
||||
};
|
||||
|
||||
context.RIL.radioState = GECKO_RADIOSTATE_ENABLED;
|
||||
context.RIL.sendMMI({mmi: "*#43#"});
|
||||
context.RIL.sendMMI({mmi: createMMIOptions("*#", "43")});
|
||||
|
||||
let postedMessage = workerhelper.postedMessage;
|
||||
|
||||
|
@ -7,7 +7,20 @@ function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
function setCallForwardSuccess(mmi) {
|
||||
function createMMIOptions(procedure, serviceCode, sia, sib, sic) {
|
||||
let mmi = {
|
||||
fullMMI: Array.slice(arguments).join("*") + "#",
|
||||
procedure: procedure,
|
||||
serviceCode: serviceCode,
|
||||
sia: sia,
|
||||
sib: sib,
|
||||
sic: sic
|
||||
};
|
||||
|
||||
return mmi;
|
||||
}
|
||||
|
||||
function setCallForwardSuccess(procedure, serviceCode, sia, sib, sic) {
|
||||
let workerhelper = newInterceptWorker();
|
||||
let worker = workerhelper.worker;
|
||||
let context = worker.ContextPool._contexts[0];
|
||||
@ -19,22 +32,23 @@ function setCallForwardSuccess(mmi) {
|
||||
};
|
||||
|
||||
context.RIL.radioState = GECKO_RADIOSTATE_ENABLED;
|
||||
context.RIL.sendMMI({mmi: mmi});
|
||||
context.RIL.sendMMI({mmi: createMMIOptions(procedure, serviceCode, sia, sib,
|
||||
sic)});
|
||||
|
||||
let postedMessage = workerhelper.postedMessage;
|
||||
|
||||
do_check_eq(postedMessage.errorMsg, GECKO_ERROR_SUCCESS);
|
||||
do_check_eq(postedMessage.errorMsg, undefined);
|
||||
do_check_true(postedMessage.success);
|
||||
}
|
||||
|
||||
add_test(function test_sendMMI_call_forwarding_activation() {
|
||||
setCallForwardSuccess("*21*12345*99*10#");
|
||||
setCallForwardSuccess("*", "21", "12345", "99", "10");
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_sendMMI_call_forwarding_deactivation() {
|
||||
setCallForwardSuccess("#21*12345*99*10#");
|
||||
setCallForwardSuccess("#", "21", "12345", "99", "10");
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
@ -67,11 +81,11 @@ add_test(function test_sendMMI_call_forwarding_interrogation() {
|
||||
};
|
||||
|
||||
context.RIL.radioState = GECKO_RADIOSTATE_ENABLED;
|
||||
context.RIL.sendMMI({mmi: "*#21#"});
|
||||
context.RIL.sendMMI({mmi: createMMIOptions("*#", "21")});
|
||||
|
||||
let postedMessage = workerhelper.postedMessage;
|
||||
|
||||
do_check_eq(postedMessage.errorMsg, GECKO_ERROR_SUCCESS);
|
||||
do_check_eq(postedMessage.errorMsg, undefined);
|
||||
do_check_true(postedMessage.success);
|
||||
do_check_true(Array.isArray(postedMessage.rules));
|
||||
do_check_eq(postedMessage.rules.length, 1);
|
||||
@ -97,7 +111,7 @@ add_test(function test_sendMMI_call_forwarding_interrogation_no_rules() {
|
||||
};
|
||||
|
||||
context.RIL.radioState = GECKO_RADIOSTATE_ENABLED;
|
||||
context.RIL.sendMMI({mmi: "*#21#"});
|
||||
context.RIL.sendMMI({mmi: createMMIOptions("*#", "21")});
|
||||
|
||||
let postedMessage = workerhelper.postedMessage;
|
||||
|
||||
@ -109,43 +123,43 @@ add_test(function test_sendMMI_call_forwarding_interrogation_no_rules() {
|
||||
|
||||
|
||||
add_test(function test_sendMMI_call_forwarding_registration() {
|
||||
setCallForwardSuccess("**21*12345*99*10#");
|
||||
setCallForwardSuccess("**", "21", "12345", "99", "10");
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_sendMMI_call_forwarding_erasure() {
|
||||
setCallForwardSuccess("##21*12345*99#");
|
||||
setCallForwardSuccess("##", "21", "12345", "99");
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_sendMMI_call_forwarding_CFB() {
|
||||
setCallForwardSuccess("*67*12345*99*10#");
|
||||
setCallForwardSuccess("*", "67", "12345", "99", "10");
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_sendMMI_call_forwarding_CFNRy() {
|
||||
setCallForwardSuccess("*61*12345*99*10#");
|
||||
setCallForwardSuccess("*", "61", "12345", "99", "10");
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_sendMMI_call_forwarding_CFNRc() {
|
||||
setCallForwardSuccess("*62*12345*99*10#");
|
||||
setCallForwardSuccess("*", "62", "12345", "99", "10");
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_sendMMI_call_forwarding_CFAll() {
|
||||
setCallForwardSuccess("*004*12345*99*10#");
|
||||
setCallForwardSuccess("*", "004", "12345", "99", "10");
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_sendMMI_call_forwarding_CFAllConditional() {
|
||||
setCallForwardSuccess("*002*12345*99*10#");
|
||||
setCallForwardSuccess("*", "002", "12345", "99", "10");
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
@ -1,317 +0,0 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
subscriptLoader.loadSubScript("resource://gre/modules/ril_consts.js", this);
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
let worker;
|
||||
function parseMMI(mmi) {
|
||||
if (!worker) {
|
||||
worker = newWorker({
|
||||
postRILMessage: function(data) {
|
||||
// Do nothing
|
||||
},
|
||||
postMessage: function(message) {
|
||||
// Do nothing
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let context = worker.ContextPool._contexts[0];
|
||||
return context.RIL._parseMMI(mmi);
|
||||
}
|
||||
|
||||
add_test(function test_parseMMI_empty() {
|
||||
let mmi = parseMMI("");
|
||||
|
||||
do_check_null(mmi);
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_parseMMI_undefined() {
|
||||
let mmi = parseMMI();
|
||||
|
||||
do_check_null(mmi);
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_parseMMI_one_digit_short_code() {
|
||||
let mmi = parseMMI("1");
|
||||
|
||||
do_check_eq(mmi.fullMMI, "1");
|
||||
do_check_eq(mmi.procedure, undefined);
|
||||
do_check_eq(mmi.serviceCode, undefined);
|
||||
do_check_eq(mmi.sia, undefined);
|
||||
do_check_eq(mmi.sib, undefined);
|
||||
do_check_eq(mmi.sic, undefined);
|
||||
do_check_eq(mmi.pwd, undefined);
|
||||
do_check_eq(mmi.dialNumber, undefined);
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_parseMMI_invalid_short_code() {
|
||||
let mmi = parseMMI("11");
|
||||
|
||||
do_check_null(mmi);
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_parseMMI_short_code() {
|
||||
let mmi = parseMMI("21");
|
||||
|
||||
do_check_eq(mmi.fullMMI, "21");
|
||||
do_check_eq(mmi.procedure, undefined);
|
||||
do_check_eq(mmi.serviceCode, undefined);
|
||||
do_check_eq(mmi.sia, undefined);
|
||||
do_check_eq(mmi.sib, undefined);
|
||||
do_check_eq(mmi.sic, undefined);
|
||||
do_check_eq(mmi.pwd, undefined);
|
||||
do_check_eq(mmi.dialNumber, undefined);
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_parseMMI_dial_string() {
|
||||
let mmi = parseMMI("12345");
|
||||
|
||||
do_check_null(mmi);
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_parseMMI_USSD_without_asterisk_prefix() {
|
||||
let mmi = parseMMI("123#");
|
||||
|
||||
do_check_eq(mmi.fullMMI, "123#");
|
||||
do_check_eq(mmi.procedure, undefined);
|
||||
do_check_eq(mmi.serviceCode, undefined);
|
||||
do_check_eq(mmi.sia, undefined);
|
||||
do_check_eq(mmi.sib, undefined);
|
||||
do_check_eq(mmi.sic, undefined);
|
||||
do_check_eq(mmi.pwd, undefined);
|
||||
do_check_eq(mmi.dialNumber, undefined);
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_parseMMI_USSD() {
|
||||
let mmi = parseMMI("*123#");
|
||||
|
||||
do_check_eq(mmi.fullMMI, "*123#");
|
||||
do_check_eq(mmi.procedure, MMI_PROCEDURE_ACTIVATION);
|
||||
do_check_eq(mmi.serviceCode, "123");
|
||||
do_check_eq(mmi.sia, undefined);
|
||||
do_check_eq(mmi.sib, undefined);
|
||||
do_check_eq(mmi.sic, undefined);
|
||||
do_check_eq(mmi.pwd, undefined);
|
||||
do_check_eq(mmi.dialNumber, "");
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_parseMMI_sia() {
|
||||
let mmi = parseMMI("*123*1#");
|
||||
|
||||
do_check_eq(mmi.fullMMI, "*123*1#");
|
||||
do_check_eq(mmi.procedure, MMI_PROCEDURE_ACTIVATION);
|
||||
do_check_eq(mmi.serviceCode, "123");
|
||||
do_check_eq(mmi.sia, "1");
|
||||
do_check_eq(mmi.sib, undefined);
|
||||
do_check_eq(mmi.sic, undefined);
|
||||
do_check_eq(mmi.pwd, undefined);
|
||||
do_check_eq(mmi.dialNumber, "");
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_parseMMI_sib() {
|
||||
let mmi = parseMMI("*123**1#");
|
||||
|
||||
do_check_eq(mmi.fullMMI, "*123**1#");
|
||||
do_check_eq(mmi.procedure, MMI_PROCEDURE_ACTIVATION);
|
||||
do_check_eq(mmi.serviceCode, "123");
|
||||
do_check_eq(mmi.sia, "");
|
||||
do_check_eq(mmi.sib, "1");
|
||||
do_check_eq(mmi.sic, undefined);
|
||||
do_check_eq(mmi.pwd, undefined);
|
||||
do_check_eq(mmi.dialNumber, "");
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_parseMMI_sic() {
|
||||
let mmi = parseMMI("*123***1#");
|
||||
|
||||
do_check_eq(mmi.fullMMI, "*123***1#");
|
||||
do_check_eq(mmi.procedure, MMI_PROCEDURE_ACTIVATION);
|
||||
do_check_eq(mmi.serviceCode, "123");
|
||||
do_check_eq(mmi.sia, "");
|
||||
do_check_eq(mmi.sib, "");
|
||||
do_check_eq(mmi.sic, "1");
|
||||
do_check_eq(mmi.pwd, undefined);
|
||||
do_check_eq(mmi.dialNumber, "");
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_parseMMI_sia_sib() {
|
||||
let mmi = parseMMI("*123*1*1#");
|
||||
|
||||
do_check_eq(mmi.fullMMI, "*123*1*1#");
|
||||
do_check_eq(mmi.procedure, MMI_PROCEDURE_ACTIVATION);
|
||||
do_check_eq(mmi.serviceCode, "123");
|
||||
do_check_eq(mmi.sia, "1");
|
||||
do_check_eq(mmi.sib, "1");
|
||||
do_check_eq(mmi.sic, undefined);
|
||||
do_check_eq(mmi.pwd, undefined);
|
||||
do_check_eq(mmi.dialNumber, "");
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_parseMMI_sia_sic() {
|
||||
let mmi = parseMMI("*123*1**1#");
|
||||
|
||||
do_check_eq(mmi.fullMMI, "*123*1**1#");
|
||||
do_check_eq(mmi.procedure, MMI_PROCEDURE_ACTIVATION);
|
||||
do_check_eq(mmi.serviceCode, "123");
|
||||
do_check_eq(mmi.sia, "1");
|
||||
do_check_eq(mmi.sib, "");
|
||||
do_check_eq(mmi.sic, "1");
|
||||
do_check_eq(mmi.pwd, undefined);
|
||||
do_check_eq(mmi.dialNumber, "");
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_parseMMI_sib_sic() {
|
||||
let mmi = parseMMI("*123**1*1#");
|
||||
|
||||
do_check_eq(mmi.fullMMI, "*123**1*1#");
|
||||
do_check_eq(mmi.procedure, MMI_PROCEDURE_ACTIVATION);
|
||||
do_check_eq(mmi.serviceCode, "123");
|
||||
do_check_eq(mmi.sia, "");
|
||||
do_check_eq(mmi.sib, "1");
|
||||
do_check_eq(mmi.sic, "1");
|
||||
do_check_eq(mmi.pwd, undefined);
|
||||
do_check_eq(mmi.dialNumber, "");
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_parseMMI_pwd() {
|
||||
let mmi = parseMMI("*123****1#");
|
||||
|
||||
do_check_eq(mmi.fullMMI, "*123****1#");
|
||||
do_check_eq(mmi.procedure, MMI_PROCEDURE_ACTIVATION);
|
||||
do_check_eq(mmi.serviceCode, "123");
|
||||
do_check_eq(mmi.sia, "");
|
||||
do_check_eq(mmi.sib, "");
|
||||
do_check_eq(mmi.sic, "");
|
||||
do_check_eq(mmi.pwd, "1");
|
||||
do_check_eq(mmi.dialNumber, "");
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_parseMMI_dial_number() {
|
||||
let mmi = parseMMI("*123#345");
|
||||
|
||||
do_check_eq(mmi.fullMMI, "*123#");
|
||||
do_check_eq(mmi.procedure, MMI_PROCEDURE_ACTIVATION);
|
||||
do_check_eq(mmi.serviceCode, "123");
|
||||
do_check_eq(mmi.sia, undefined);
|
||||
do_check_eq(mmi.sib, undefined);
|
||||
do_check_eq(mmi.sic, undefined);
|
||||
do_check_eq(mmi.pwd, undefined);
|
||||
do_check_eq(mmi.dialNumber, "345");
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* MMI procedures tests
|
||||
*/
|
||||
|
||||
add_test(function test_parseMMI_activation() {
|
||||
let mmi = parseMMI("*00*12*34*56#");
|
||||
|
||||
do_check_eq(mmi.fullMMI, "*00*12*34*56#");
|
||||
do_check_eq(mmi.procedure, MMI_PROCEDURE_ACTIVATION);
|
||||
do_check_eq(mmi.serviceCode, "00");
|
||||
do_check_eq(mmi.sia, "12");
|
||||
do_check_eq(mmi.sib, "34");
|
||||
do_check_eq(mmi.sic, "56");
|
||||
do_check_eq(mmi.pwd, undefined);
|
||||
do_check_eq(mmi.dialNumber, "");
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_parseMMI_deactivation() {
|
||||
let mmi = parseMMI("#00*12*34*56#");
|
||||
|
||||
do_check_eq(mmi.fullMMI, "#00*12*34*56#");
|
||||
do_check_eq(mmi.procedure, MMI_PROCEDURE_DEACTIVATION);
|
||||
do_check_eq(mmi.serviceCode, "00");
|
||||
do_check_eq(mmi.sia, "12");
|
||||
do_check_eq(mmi.sib, "34");
|
||||
do_check_eq(mmi.sic, "56");
|
||||
do_check_eq(mmi.pwd, undefined);
|
||||
do_check_eq(mmi.dialNumber, "");
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_parseMMI_interrogation() {
|
||||
let mmi = parseMMI("*#00*12*34*56#");
|
||||
|
||||
do_check_eq(mmi.fullMMI, "*#00*12*34*56#");
|
||||
do_check_eq(mmi.procedure, MMI_PROCEDURE_INTERROGATION);
|
||||
do_check_eq(mmi.serviceCode, "00");
|
||||
do_check_eq(mmi.sia, "12");
|
||||
do_check_eq(mmi.sib, "34");
|
||||
do_check_eq(mmi.sic, "56");
|
||||
do_check_eq(mmi.pwd, undefined);
|
||||
do_check_eq(mmi.dialNumber, "");
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_parseMMI_registration() {
|
||||
let mmi = parseMMI("**00*12*34*56#");
|
||||
|
||||
do_check_eq(mmi.fullMMI, "**00*12*34*56#");
|
||||
do_check_eq(mmi.procedure, MMI_PROCEDURE_REGISTRATION);
|
||||
do_check_eq(mmi.serviceCode, "00");
|
||||
do_check_eq(mmi.sia, "12");
|
||||
do_check_eq(mmi.sib, "34");
|
||||
do_check_eq(mmi.sic, "56");
|
||||
do_check_eq(mmi.pwd, undefined);
|
||||
do_check_eq(mmi.dialNumber, "");
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_parseMMI_erasure() {
|
||||
let mmi = parseMMI("##00*12*34*56#");
|
||||
|
||||
do_check_eq(mmi.fullMMI, "##00*12*34*56#");
|
||||
do_check_eq(mmi.procedure, MMI_PROCEDURE_ERASURE);
|
||||
do_check_eq(mmi.serviceCode, "00");
|
||||
do_check_eq(mmi.sia, "12");
|
||||
do_check_eq(mmi.sib, "34");
|
||||
do_check_eq(mmi.sic, "56");
|
||||
do_check_eq(mmi.pwd, undefined);
|
||||
do_check_eq(mmi.dialNumber, "");
|
||||
|
||||
run_next_test();
|
||||
});
|
@ -24,7 +24,6 @@ skip-if = true
|
||||
[test_ril_worker_sms_segment_info.js]
|
||||
[test_ril_worker_mmi.js]
|
||||
[test_ril_worker_mmi_cf.js]
|
||||
[test_ril_worker_mmi_parseMMI.js]
|
||||
[test_ril_worker_cf.js]
|
||||
[test_ril_worker_cellbroadcast_config.js]
|
||||
[test_ril_worker_cellbroadcast.js]
|
||||
|
@ -5,19 +5,20 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "Telephony.h"
|
||||
#include "mozilla/dom/CallEvent.h"
|
||||
#include "mozilla/dom/TelephonyBinding.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
|
||||
#include "nsIURI.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
#include "nsIPermissionManager.h"
|
||||
|
||||
#include "mozilla/dom/UnionTypes.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/dom/CallEvent.h"
|
||||
#include "mozilla/dom/MozMobileConnectionBinding.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozilla/dom/TelephonyBinding.h"
|
||||
#include "mozilla/dom/UnionTypes.h"
|
||||
|
||||
#include "nsCharSeparatedTokenizer.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsIPermissionManager.h"
|
||||
#include "nsIURI.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
@ -25,6 +26,7 @@
|
||||
#include "TelephonyCall.h"
|
||||
#include "TelephonyCallGroup.h"
|
||||
#include "TelephonyCallId.h"
|
||||
#include "TelephonyCallback.h"
|
||||
|
||||
// Service instantiation
|
||||
#include "ipc/TelephonyIPCService.h"
|
||||
@ -34,6 +36,7 @@
|
||||
#include "nsXULAppAPI.h" // For XRE_GetProcessType()
|
||||
|
||||
using namespace mozilla::dom;
|
||||
using namespace mozilla::dom::telephony;
|
||||
using mozilla::ErrorResult;
|
||||
|
||||
class Telephony::Listener : public nsITelephonyListener
|
||||
@ -60,43 +63,6 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class Telephony::Callback : public nsITelephonyCallback
|
||||
{
|
||||
nsRefPtr<Telephony> mTelephony;
|
||||
nsRefPtr<Promise> mPromise;
|
||||
uint32_t mServiceId;
|
||||
|
||||
virtual ~Callback() {}
|
||||
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
Callback(Telephony* aTelephony, Promise* aPromise, uint32_t aServiceId)
|
||||
: mTelephony(aTelephony), mPromise(aPromise), mServiceId(aServiceId)
|
||||
{
|
||||
MOZ_ASSERT(mTelephony);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NotifyDialError(const nsAString& aError)
|
||||
{
|
||||
mPromise->MaybeRejectBrokenly(aError);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NotifyDialSuccess(uint32_t aCallIndex, const nsAString& aNumber)
|
||||
{
|
||||
nsRefPtr<TelephonyCallId> id = mTelephony->CreateCallId(aNumber);
|
||||
nsRefPtr<TelephonyCall> call =
|
||||
mTelephony->CreateCall(id, mServiceId, aCallIndex,
|
||||
nsITelephonyService::CALL_STATE_DIALING);
|
||||
|
||||
mPromise->MaybeResolve(call);
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
class Telephony::EnumerationAck : public nsRunnable
|
||||
{
|
||||
nsRefPtr<Telephony> mTelephony;
|
||||
@ -270,7 +236,8 @@ Telephony::DialInternal(uint32_t aServiceId, const nsAString& aNumber,
|
||||
}
|
||||
|
||||
nsCOMPtr<nsITelephonyCallback> callback =
|
||||
new Callback(this, promise, aServiceId);
|
||||
new TelephonyCallback(GetOwner(), this, promise, aServiceId);
|
||||
|
||||
nsresult rv = mService->Dial(aServiceId, aNumber, aEmergency, callback);
|
||||
if (NS_FAILED(rv)) {
|
||||
promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
@ -381,7 +348,6 @@ NS_IMPL_ADDREF_INHERITED(Telephony, DOMEventTargetHelper)
|
||||
NS_IMPL_RELEASE_INHERITED(Telephony, DOMEventTargetHelper)
|
||||
|
||||
NS_IMPL_ISUPPORTS(Telephony::Listener, nsITelephonyListener)
|
||||
NS_IMPL_ISUPPORTS(Telephony::Callback, nsITelephonyCallback)
|
||||
|
||||
// Telephony WebIDL
|
||||
|
||||
|
@ -21,6 +21,11 @@ class nsPIDOMWindow;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
namespace telephony {
|
||||
|
||||
class TelephonyCallback;
|
||||
|
||||
} // namespace telephony
|
||||
|
||||
class OwningTelephonyCallOrTelephonyCallGroup;
|
||||
|
||||
@ -35,12 +40,10 @@ class Telephony MOZ_FINAL : public DOMEventTargetHelper,
|
||||
* also bug 775997 comment #51.
|
||||
*/
|
||||
class Listener;
|
||||
|
||||
class Callback;
|
||||
friend class Callback;
|
||||
|
||||
class EnumerationAck;
|
||||
|
||||
friend class EnumerationAck;
|
||||
friend class telephony::TelephonyCallback;
|
||||
|
||||
nsCOMPtr<nsITelephonyService> mService;
|
||||
nsRefPtr<Listener> mListener;
|
||||
|
139
dom/telephony/TelephonyCallback.cpp
Normal file
139
dom/telephony/TelephonyCallback.cpp
Normal file
@ -0,0 +1,139 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "TelephonyCallback.h"
|
||||
|
||||
#include "mozilla/dom/DOMMMIError.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
|
||||
using namespace mozilla::dom;
|
||||
using namespace mozilla::dom::telephony;
|
||||
|
||||
NS_IMPL_ISUPPORTS(TelephonyCallback, nsITelephonyCallback)
|
||||
|
||||
TelephonyCallback::TelephonyCallback(nsPIDOMWindow* aWindow,
|
||||
Telephony* aTelephony,
|
||||
Promise* aPromise,
|
||||
uint32_t aServiceId)
|
||||
: mWindow(aWindow), mTelephony(aTelephony), mPromise(aPromise),
|
||||
mServiceId(aServiceId)
|
||||
{
|
||||
MOZ_ASSERT(mTelephony);
|
||||
}
|
||||
|
||||
nsresult
|
||||
TelephonyCallback::NotifyDialMMISuccess(const nsAString& aStatusMessage)
|
||||
{
|
||||
AutoJSAPI jsapi;
|
||||
if (!NS_WARN_IF(jsapi.Init(mWindow))) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
JSContext* cx = jsapi.cx();
|
||||
|
||||
MozMMIResult result;
|
||||
|
||||
result.mServiceCode.Assign(mServiceCode);
|
||||
result.mStatusMessage.Assign(aStatusMessage);
|
||||
|
||||
return NotifyDialMMISuccess(cx, result);
|
||||
}
|
||||
|
||||
nsresult
|
||||
TelephonyCallback::NotifyDialMMISuccess(JSContext* aCx,
|
||||
const nsAString& aStatusMessage,
|
||||
JS::Handle<JS::Value> aInfo)
|
||||
{
|
||||
RootedDictionary<MozMMIResult> result(aCx);
|
||||
|
||||
result.mServiceCode.Assign(mServiceCode);
|
||||
result.mStatusMessage.Assign(aStatusMessage);
|
||||
result.mAdditionalInformation.Construct().SetAsObject() = &aInfo.toObject();
|
||||
|
||||
return NotifyDialMMISuccess(aCx, result);
|
||||
}
|
||||
|
||||
nsresult
|
||||
TelephonyCallback::NotifyDialMMISuccess(JSContext* aCx,
|
||||
const MozMMIResult& aResult)
|
||||
{
|
||||
JS::Rooted<JS::Value> jsResult(aCx);
|
||||
|
||||
if (!ToJSValue(aCx, aResult, &jsResult)) {
|
||||
JS_ClearPendingException(aCx);
|
||||
return NS_ERROR_TYPE_ERR;
|
||||
}
|
||||
|
||||
return NotifyDialMMISuccess(jsResult);
|
||||
}
|
||||
|
||||
// nsITelephonyCallback
|
||||
|
||||
NS_IMETHODIMP
|
||||
TelephonyCallback::NotifyDialMMI(const nsAString& aServiceCode)
|
||||
{
|
||||
mMMIRequest = new DOMRequest(mWindow);
|
||||
mServiceCode.Assign(aServiceCode);
|
||||
|
||||
mPromise->MaybeResolve(mMMIRequest);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TelephonyCallback::NotifyDialError(const nsAString& aError)
|
||||
{
|
||||
mPromise->MaybeRejectBrokenly(aError);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TelephonyCallback::NotifyDialCallSuccess(uint32_t aCallIndex,
|
||||
const nsAString& aNumber)
|
||||
{
|
||||
nsRefPtr<TelephonyCallId> id = mTelephony->CreateCallId(aNumber);
|
||||
nsRefPtr<TelephonyCall> call =
|
||||
mTelephony->CreateCall(id, mServiceId, aCallIndex,
|
||||
nsITelephonyService::CALL_STATE_DIALING);
|
||||
|
||||
mPromise->MaybeResolve(call);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TelephonyCallback::NotifyDialMMISuccess(JS::Handle<JS::Value> aResult)
|
||||
{
|
||||
nsCOMPtr<nsIDOMRequestService> rs = do_GetService(DOMREQUEST_SERVICE_CONTRACTID);
|
||||
NS_ENSURE_TRUE(rs, NS_ERROR_FAILURE);
|
||||
|
||||
return rs->FireSuccessAsync(mMMIRequest, aResult);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TelephonyCallback::NotifyDialMMIError(const nsAString& aError)
|
||||
{
|
||||
Nullable<int16_t> info;
|
||||
|
||||
nsRefPtr<DOMError> error =
|
||||
new DOMMMIError(mWindow, aError, EmptyString(), mServiceCode, info);
|
||||
|
||||
nsCOMPtr<nsIDOMRequestService> rs = do_GetService(DOMREQUEST_SERVICE_CONTRACTID);
|
||||
NS_ENSURE_TRUE(rs, NS_ERROR_FAILURE);
|
||||
|
||||
return rs->FireDetailedError(mMMIRequest, error);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TelephonyCallback::NotifyDialMMIErrorWithInfo(const nsAString& aError,
|
||||
uint16_t aInfo)
|
||||
{
|
||||
Nullable<int16_t> info(aInfo);
|
||||
|
||||
nsRefPtr<DOMError> error =
|
||||
new DOMMMIError(mWindow, aError, EmptyString(), mServiceCode, info);
|
||||
|
||||
nsCOMPtr<nsIDOMRequestService> rs = do_GetService(DOMREQUEST_SERVICE_CONTRACTID);
|
||||
NS_ENSURE_TRUE(rs, NS_ERROR_FAILURE);
|
||||
|
||||
return rs->FireDetailedError(mMMIRequest, error);
|
||||
}
|
81
dom/telephony/TelephonyCallback.h
Normal file
81
dom/telephony/TelephonyCallback.h
Normal file
@ -0,0 +1,81 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_TelephonyCallback_h
|
||||
#define mozilla_dom_TelephonyCallback_h
|
||||
|
||||
#include "Telephony.h"
|
||||
#include "mozilla/dom/DOMRequest.h"
|
||||
#include "mozilla/dom/MozMobileConnectionBinding.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozilla/dom/ToJSValue.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsITelephonyService.h"
|
||||
#include "nsJSUtils.h"
|
||||
#include "nsString.h"
|
||||
|
||||
class nsPIDOMWindow;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
namespace telephony {
|
||||
|
||||
class TelephonyCallback MOZ_FINAL : public nsITelephonyCallback
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSITELEPHONYCALLBACK
|
||||
|
||||
TelephonyCallback(nsPIDOMWindow* aWindow, Telephony* aTelephony,
|
||||
Promise* aPromise, uint32_t aServiceId);
|
||||
|
||||
nsresult
|
||||
NotifyDialMMISuccess(const nsAString& aStatusMessage);
|
||||
|
||||
template<typename T>
|
||||
nsresult
|
||||
NotifyDialMMISuccess(const nsAString& aStatusMessage, const T& aInfo)
|
||||
{
|
||||
AutoJSAPI jsapi;
|
||||
if (!NS_WARN_IF(jsapi.Init(mWindow))) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
JSContext* cx = jsapi.cx();
|
||||
JS::Rooted<JS::Value> info(cx);
|
||||
|
||||
if (!ToJSValue(cx, aInfo, &info)) {
|
||||
JS_ClearPendingException(cx);
|
||||
return NS_ERROR_TYPE_ERR;
|
||||
}
|
||||
|
||||
return NotifyDialMMISuccess(cx, aStatusMessage, info);
|
||||
}
|
||||
|
||||
private:
|
||||
~TelephonyCallback() {}
|
||||
|
||||
nsresult
|
||||
NotifyDialMMISuccess(JSContext* aCx, const nsAString& aStatusMessage,
|
||||
JS::Handle<JS::Value> aInfo);
|
||||
|
||||
nsresult
|
||||
NotifyDialMMISuccess(JSContext* aCx, const MozMMIResult& aResult);
|
||||
|
||||
|
||||
nsCOMPtr<nsPIDOMWindow> mWindow;
|
||||
nsRefPtr<Telephony> mTelephony;
|
||||
nsRefPtr<Promise> mPromise;
|
||||
uint32_t mServiceId;
|
||||
|
||||
nsRefPtr<DOMRequest> mMMIRequest;
|
||||
nsString mServiceCode;
|
||||
};
|
||||
|
||||
} // namespace telephony
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_TelephonyCallback_h
|
@ -54,6 +54,16 @@ const AUDIO_STATE_NAME = [
|
||||
|
||||
const DEFAULT_EMERGENCY_NUMBERS = ["112", "911"];
|
||||
|
||||
// MMI match groups
|
||||
const MMI_MATCH_GROUP_FULL_MMI = 1;
|
||||
const MMI_MATCH_GROUP_PROCEDURE = 2;
|
||||
const MMI_MATCH_GROUP_SERVICE_CODE = 3;
|
||||
const MMI_MATCH_GROUP_SIA = 4;
|
||||
const MMI_MATCH_GROUP_SIB = 5;
|
||||
const MMI_MATCH_GROUP_SIC = 6;
|
||||
const MMI_MATCH_GROUP_PWD_CONFIRM = 7;
|
||||
const MMI_MATCH_GROUP_DIALING_NUMBER = 8;
|
||||
|
||||
let DEBUG;
|
||||
function debug(s) {
|
||||
dump("TelephonyService: " + s + "\n");
|
||||
@ -99,15 +109,52 @@ XPCOMUtils.defineLazyServiceGetter(this, "gSystemMessenger",
|
||||
"@mozilla.org/system-message-internal;1",
|
||||
"nsISystemMessagesInternal");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "gGonkMobileConnectionService",
|
||||
"@mozilla.org/mobileconnection/mobileconnectionservice;1",
|
||||
"nsIGonkMobileConnectionService");
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "gPhoneNumberUtils", function() {
|
||||
let ns = {};
|
||||
Cu.import("resource://gre/modules/PhoneNumberUtils.jsm", ns);
|
||||
return ns.PhoneNumberUtils;
|
||||
});
|
||||
|
||||
function MMIResult(aMmiServiceCode, aOptions) {
|
||||
this.serviceCode = aMmiServiceCode;
|
||||
this.statusMessage = aOptions.statusMessage;
|
||||
this.additionalInformation = aOptions.additionalInformation;
|
||||
}
|
||||
MMIResult.prototype = {
|
||||
__exposedProps__ : {serviceCode: 'r',
|
||||
statusMessage: 'r',
|
||||
additionalInformation: 'r'},
|
||||
};
|
||||
|
||||
function CallForwardingOptions(aOptions) {
|
||||
this.active = aOptions.active;
|
||||
this.action = aOptions.action;
|
||||
this.reason = aOptions.reason;
|
||||
this.number = aOptions.number;
|
||||
this.timeSeconds = aOptions.timeSeconds;
|
||||
this.serviceClass = aOptions.serviceClass;
|
||||
}
|
||||
CallForwardingOptions.prototype = {
|
||||
__exposedProps__ : {active: 'r',
|
||||
action: 'r',
|
||||
reason: 'r',
|
||||
number: 'r',
|
||||
timeSeconds: 'r',
|
||||
serviceClass: 'r'},
|
||||
};
|
||||
|
||||
function TelephonyService() {
|
||||
this._numClients = gRadioInterfaceLayer.numRadioInterfaces;
|
||||
this._listeners = [];
|
||||
|
||||
this._mmiRegExp = null;
|
||||
|
||||
this._isDialing = false;
|
||||
this._cachedDialRequest = null;
|
||||
this._currentCalls = {};
|
||||
|
||||
this._cdmaCallWaitingNumber = null;
|
||||
@ -300,6 +347,10 @@ TelephonyService.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
_rulesToCallForwardingOptions: function(aRules) {
|
||||
return aRules.map(rule => new CallForwardingOptions(rule));
|
||||
},
|
||||
|
||||
_updateDebugFlag: function() {
|
||||
try {
|
||||
DEBUG = RIL.DEBUG_RIL ||
|
||||
@ -430,12 +481,13 @@ TelephonyService.prototype = {
|
||||
aListener.enumerateCallStateComplete();
|
||||
},
|
||||
|
||||
_hasCalls: function(aClientId) {
|
||||
return Object.keys(this._currentCalls[aClientId]).length !== 0;
|
||||
},
|
||||
|
||||
_hasCallsOnOtherClient: function(aClientId) {
|
||||
for (let cid = 0; cid < this._numClients; ++cid) {
|
||||
if (cid === aClientId) {
|
||||
continue;
|
||||
}
|
||||
if (Object.keys(this._currentCalls[cid]).length !== 0) {
|
||||
if (cid !== aClientId && this._hasCalls(cid)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -499,36 +551,51 @@ TelephonyService.prototype = {
|
||||
this.notifyCallStateChanged(aClientId, parentCall);
|
||||
},
|
||||
|
||||
_composeDialRequest: function(aClientId, aNumber) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this._sendToRilWorker(aClientId, "parseMMIFromDialNumber",
|
||||
{number: aNumber}, response => {
|
||||
let options = {};
|
||||
let mmi = response.mmi;
|
||||
|
||||
if (!mmi) {
|
||||
resolve({
|
||||
number: aNumber
|
||||
});
|
||||
} else if (this._isTemporaryCLIR(mmi)) {
|
||||
resolve({
|
||||
number: mmi.dialNumber,
|
||||
clirMode: this._getTemporaryCLIRMode(mmi.procedure)
|
||||
});
|
||||
} else {
|
||||
reject(DIAL_ERROR_BAD_NUMBER);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
cachedDialRequest: null,
|
||||
isDialing: false,
|
||||
|
||||
dial: function(aClientId, aNumber, aIsDialEmergency, aCallback) {
|
||||
if (DEBUG) debug("Dialing " + (aIsDialEmergency ? "emergency " : "") + aNumber);
|
||||
|
||||
if (this.isDialing) {
|
||||
// We don't try to be too clever here, as the phone is probably in the
|
||||
// locked state. Let's just check if it's a number without normalizing
|
||||
if (!aIsDialEmergency) {
|
||||
aNumber = gPhoneNumberUtils.normalize(aNumber);
|
||||
}
|
||||
|
||||
// Validate the number.
|
||||
// Note: isPlainPhoneNumber also accepts USSD and SS numbers
|
||||
if (!gPhoneNumberUtils.isPlainPhoneNumber(aNumber)) {
|
||||
if (DEBUG) debug("Error: Number '" + aNumber + "' is not viable. Drop.");
|
||||
aCallback.notifyDialError(DIAL_ERROR_BAD_NUMBER);
|
||||
return;
|
||||
}
|
||||
|
||||
let mmi = this._parseMMI(aNumber, this._hasCalls(aClientId));
|
||||
if (!mmi) {
|
||||
this._dialCall(aClientId,
|
||||
{ number: aNumber,
|
||||
isDialEmergency: aIsDialEmergency }, aCallback);
|
||||
} else if (this._isTemporaryCLIR(mmi)) {
|
||||
this._dialCall(aClientId,
|
||||
{ number: mmi.dialNumber,
|
||||
clirMode: this._getTemporaryCLIRMode(mmi.procedure),
|
||||
isDialEmergency: aIsDialEmergency }, aCallback);
|
||||
} else {
|
||||
// Reject MMI code from dialEmergency api.
|
||||
if (aIsDialEmergency) {
|
||||
aCallback.notifyDialError(DIAL_ERROR_BAD_NUMBER);
|
||||
return;
|
||||
}
|
||||
|
||||
this._dialMMI(aClientId, mmi, aCallback);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @param aOptions.number
|
||||
* @param aOptions.clirMode (optional)
|
||||
* @param aOptions.isDialEmergency
|
||||
*/
|
||||
_dialCall: function(aClientId, aOptions, aCallback) {
|
||||
if (this._isDialing) {
|
||||
if (DEBUG) debug("Error: Already has a dialing call.");
|
||||
aCallback.notifyDialError(DIAL_ERROR_INVALID_STATE_ERROR);
|
||||
return;
|
||||
@ -549,63 +616,43 @@ TelephonyService.prototype = {
|
||||
return;
|
||||
}
|
||||
|
||||
// We don't try to be too clever here, as the phone is probably in the
|
||||
// locked state. Let's just check if it's a number without normalizing
|
||||
if (!aIsDialEmergency) {
|
||||
aNumber = gPhoneNumberUtils.normalize(aNumber);
|
||||
}
|
||||
|
||||
// Validate the number.
|
||||
// Note: isPlainPhoneNumber also accepts USSD and SS numbers
|
||||
if (!gPhoneNumberUtils.isPlainPhoneNumber(aNumber)) {
|
||||
if (DEBUG) debug("Error: Number '" + aNumber + "' is not viable. Drop.");
|
||||
aCallback.notifyDialError(DIAL_ERROR_BAD_NUMBER);
|
||||
return;
|
||||
}
|
||||
|
||||
this._composeDialRequest(aClientId, aNumber).then(options => {
|
||||
options.isEmergency = this._isEmergencyNumber(options.number);
|
||||
options.isDialEmergency = aIsDialEmergency;
|
||||
|
||||
if (options.isEmergency) {
|
||||
// Automatically select a proper clientId for emergency call.
|
||||
aClientId = gRadioInterfaceLayer.getClientIdForEmergencyCall() ;
|
||||
if (aClientId === -1) {
|
||||
if (DEBUG) debug("Error: No client is avaialble for emergency call.");
|
||||
aCallback.notifyDialError(DIAL_ERROR_INVALID_STATE_ERROR);
|
||||
return;
|
||||
}
|
||||
aOptions.isEmergency = this._isEmergencyNumber(aOptions.number);
|
||||
if (aOptions.isEmergency) {
|
||||
// Automatically select a proper clientId for emergency call.
|
||||
aClientId = gRadioInterfaceLayer.getClientIdForEmergencyCall() ;
|
||||
if (aClientId === -1) {
|
||||
if (DEBUG) debug("Error: No client is avaialble for emergency call.");
|
||||
aCallback.notifyDialError(DIAL_ERROR_INVALID_STATE_ERROR);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Before we dial, we have to hold the active call first.
|
||||
let activeCall = this._getOneActiveCall(aClientId);
|
||||
if (!activeCall) {
|
||||
this._dialInternal(aClientId, options, aCallback);
|
||||
// Before we dial, we have to hold the active call first.
|
||||
let activeCall = this._getOneActiveCall(aClientId);
|
||||
if (!activeCall) {
|
||||
this._sendDialCallRequest(aClientId, aOptions, aCallback);
|
||||
} else {
|
||||
if (DEBUG) debug("There is an active call. Hold it first before dial.");
|
||||
|
||||
this._cachedDialRequest = {
|
||||
clientId: aClientId,
|
||||
options: aOptions,
|
||||
callback: aCallback
|
||||
};
|
||||
|
||||
if (activeCall.isConference) {
|
||||
this.holdConference(aClientId);
|
||||
} else {
|
||||
if (DEBUG) debug("There is an active call. Hold it first before dial.");
|
||||
|
||||
this.cachedDialRequest = {
|
||||
clientId: aClientId,
|
||||
options: options,
|
||||
callback: aCallback
|
||||
};
|
||||
|
||||
if (activeCall.isConference) {
|
||||
this.holdConference(aClientId);
|
||||
} else {
|
||||
this.holdCall(aClientId, activeCall.callIndex);
|
||||
}
|
||||
this.holdCall(aClientId, activeCall.callIndex);
|
||||
}
|
||||
}, cause => {
|
||||
aCallback.notifyDialError(DIAL_ERROR_BAD_NUMBER);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
_dialInternal: function(aClientId, aOptions, aCallback) {
|
||||
this.isDialing = true;
|
||||
_sendDialCallRequest: function(aClientId, aOptions, aCallback) {
|
||||
this._isDialing = true;
|
||||
|
||||
this._sendToRilWorker(aClientId, "dial", aOptions, response => {
|
||||
this.isDialing = false;
|
||||
this._isDialing = false;
|
||||
|
||||
if (!response.success) {
|
||||
aCallback.notifyDialError(response.errorMsg);
|
||||
@ -616,15 +663,247 @@ TelephonyService.prototype = {
|
||||
Object.keys(this._currentCalls[aClientId])[0];
|
||||
|
||||
if (currentCdmaCallIndex == null) {
|
||||
aCallback.notifyDialSuccess(response.callIndex, response.number);
|
||||
aCallback.notifyDialCallSuccess(response.callIndex, response.number);
|
||||
} else {
|
||||
// RIL doesn't hold the 2nd call. We create one by ourselves.
|
||||
aCallback.notifyDialSuccess(CDMA_SECOND_CALL_INDEX, response.number);
|
||||
aCallback.notifyDialCallSuccess(CDMA_SECOND_CALL_INDEX, response.number);
|
||||
this._addCdmaChildCall(aClientId, response.number, currentCdmaCallIndex);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* @param aMmi
|
||||
* Parsed MMI structure.
|
||||
*/
|
||||
_dialMMI: function(aClientId, aMmi, aCallback) {
|
||||
let mmiServiceCode = aMmi ?
|
||||
this._serviceCodeToKeyString(aMmi.serviceCode) : RIL.MMI_KS_SC_USSD;
|
||||
|
||||
aCallback.notifyDialMMI(mmiServiceCode);
|
||||
|
||||
this._sendToRilWorker(aClientId, "sendMMI", { mmi: aMmi }, response => {
|
||||
if (DEBUG) debug("MMI response: " + JSON.stringify(response));
|
||||
|
||||
if (!response.success) {
|
||||
if (response.additionalInformation != null) {
|
||||
aCallback.notifyDialMMIErrorWithInfo(response.errorMsg,
|
||||
response.additionalInformation);
|
||||
} else {
|
||||
aCallback.notifyDialMMIError(response.errorMsg);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// We expect to have an IMEI at this point if the request was supposed
|
||||
// to query for the IMEI, so getting a successful reply from the RIL
|
||||
// without containing an actual IMEI number is considered an error.
|
||||
if (mmiServiceCode === RIL.MMI_KS_SC_IMEI &&
|
||||
!response.statusMessage) {
|
||||
aCallback.notifyDialMMIError(RIL.GECKO_ERROR_GENERIC_FAILURE);
|
||||
return;
|
||||
}
|
||||
|
||||
// MMI query call forwarding options request returns a set of rules that
|
||||
// will be exposed in the form of an array of MozCallForwardingOptions
|
||||
// instances.
|
||||
if (mmiServiceCode === RIL.MMI_KS_SC_CALL_FORWARDING) {
|
||||
if (response.isSetCallForward) {
|
||||
gGonkMobileConnectionService.notifyCFStateChanged(aClientId,
|
||||
response.action,
|
||||
response.reason,
|
||||
response.number,
|
||||
response.timeSeconds,
|
||||
response.serviceClass);
|
||||
}
|
||||
|
||||
if (response.additionalInformation != null) {
|
||||
response.additionalInformation =
|
||||
this._rulesToCallForwardingOptions(response.additionalInformation);
|
||||
}
|
||||
}
|
||||
|
||||
let result = new MMIResult(mmiServiceCode, response);
|
||||
aCallback.notifyDialMMISuccess(result);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Build the regex to parse MMI string. TS.22.030
|
||||
*
|
||||
* The resulting groups after matching will be:
|
||||
* 1 = full MMI string that might be used as a USSD request.
|
||||
* 2 = MMI procedure.
|
||||
* 3 = Service code.
|
||||
* 4 = SIA.
|
||||
* 5 = SIB.
|
||||
* 6 = SIC.
|
||||
* 7 = Password registration.
|
||||
* 8 = Dialing number.
|
||||
*/
|
||||
_buildMMIRegExp: function() {
|
||||
// The general structure of the codes is as follows:
|
||||
// - Activation (*SC*SI#).
|
||||
// - Deactivation (#SC*SI#).
|
||||
// - Interrogation (*#SC*SI#).
|
||||
// - Registration (**SC*SI#).
|
||||
// - Erasure (##SC*SI#).
|
||||
//
|
||||
// where SC = Service Code (2 or 3 digits) and SI = Supplementary Info
|
||||
// (variable length).
|
||||
|
||||
// Procedure, which could be *, #, *#, **, ##
|
||||
let procedure = "(\\*[*#]?|##?)";
|
||||
|
||||
// Service code, which is a 2 or 3 digits that uniquely specifies the
|
||||
// Supplementary Service associated with the MMI code.
|
||||
let serviceCode = "(\\d{2,3})";
|
||||
|
||||
// Supplementary Information SIA, SIB and SIC. SIA may comprise e.g. a PIN
|
||||
// code or Directory Number, SIB may be used to specify the tele or bearer
|
||||
// service and SIC to specify the value of the "No Reply Condition Timer".
|
||||
// Where a particular service request does not require any SI, "*SI" is
|
||||
// not entered. The use of SIA, SIB and SIC is optional and shall be
|
||||
// entered in any of the following formats:
|
||||
// - *SIA*SIB*SIC#
|
||||
// - *SIA*SIB#
|
||||
// - *SIA**SIC#
|
||||
// - *SIA#
|
||||
// - **SIB*SIC#
|
||||
// - ***SIC#
|
||||
//
|
||||
// Also catch the additional NEW_PASSWORD for the case of a password
|
||||
// registration procedure. Ex:
|
||||
// - * 03 * ZZ * OLD_PASSWORD * NEW_PASSWORD * NEW_PASSWORD #
|
||||
// - ** 03 * ZZ * OLD_PASSWORD * NEW_PASSWORD * NEW_PASSWORD #
|
||||
// - * 03 ** OLD_PASSWORD * NEW_PASSWORD * NEW_PASSWORD #
|
||||
// - ** 03 ** OLD_PASSWORD * NEW_PASSWORD * NEW_PASSWORD #
|
||||
let si = "\\*([^*#]*)";
|
||||
let allSi = "";
|
||||
for (let i = 0; i < 4; ++i) {
|
||||
allSi = "(?:" + si + allSi + ")?";
|
||||
}
|
||||
|
||||
let fullmmi = "(" + procedure + serviceCode + allSi + "#)";
|
||||
|
||||
// Dial string after the #.
|
||||
let dialString = "([^#]*)";
|
||||
|
||||
return new RegExp(fullmmi + dialString);
|
||||
},
|
||||
|
||||
/**
|
||||
* Provide the regex to parse MMI string.
|
||||
*/
|
||||
_getMMIRegExp: function() {
|
||||
if (!this._mmiRegExp) {
|
||||
this._mmiRegExp = this._buildMMIRegExp();
|
||||
}
|
||||
|
||||
return this._mmiRegExp;
|
||||
},
|
||||
|
||||
/**
|
||||
* Helper to parse # string. TS.22.030 Figure 3.5.3.2.
|
||||
*/
|
||||
_isPoundString: function(aMmiString) {
|
||||
return (aMmiString.charAt(aMmiString.length - 1) === "#");
|
||||
},
|
||||
|
||||
/**
|
||||
* Helper to parse short string. TS.22.030 Figure 3.5.3.2.
|
||||
*/
|
||||
_isShortString: function(aMmiString, hasCalls) {
|
||||
if (aMmiString.length > 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (hasCalls) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Input string is
|
||||
// - emergency number or
|
||||
// - 2 digits starting with a "1"
|
||||
if (this._isEmergencyNumber(aMmiString) ||
|
||||
(aMmiString.length == 2) && (aMmiString.charAt(0) === '1')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Helper to parse MMI/USSD string. TS.22.030 Figure 3.5.3.2.
|
||||
*/
|
||||
_parseMMI: function(aMmiString, hasCalls) {
|
||||
if (!aMmiString) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let matches = this._getMMIRegExp().exec(aMmiString);
|
||||
if (matches) {
|
||||
return {
|
||||
fullMMI: matches[MMI_MATCH_GROUP_FULL_MMI],
|
||||
procedure: matches[MMI_MATCH_GROUP_PROCEDURE],
|
||||
serviceCode: matches[MMI_MATCH_GROUP_SERVICE_CODE],
|
||||
sia: matches[MMI_MATCH_GROUP_SIA],
|
||||
sib: matches[MMI_MATCH_GROUP_SIB],
|
||||
sic: matches[MMI_MATCH_GROUP_SIC],
|
||||
pwd: matches[MMI_MATCH_GROUP_PWD_CONFIRM],
|
||||
dialNumber: matches[MMI_MATCH_GROUP_DIALING_NUMBER]
|
||||
};
|
||||
}
|
||||
|
||||
if (this._isPoundString(aMmiString) ||
|
||||
this._isShortString(aMmiString, hasCalls)) {
|
||||
return {
|
||||
fullMMI: aMmiString
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
_serviceCodeToKeyString: function(aServiceCode) {
|
||||
switch (aServiceCode) {
|
||||
case RIL.MMI_SC_CFU:
|
||||
case RIL.MMI_SC_CF_BUSY:
|
||||
case RIL.MMI_SC_CF_NO_REPLY:
|
||||
case RIL.MMI_SC_CF_NOT_REACHABLE:
|
||||
case RIL.MMI_SC_CF_ALL:
|
||||
case RIL.MMI_SC_CF_ALL_CONDITIONAL:
|
||||
return RIL.MMI_KS_SC_CALL_FORWARDING;
|
||||
case RIL.MMI_SC_PIN:
|
||||
return RIL.MMI_KS_SC_PIN;
|
||||
case RIL.MMI_SC_PIN2:
|
||||
return RIL.MMI_KS_SC_PIN2;
|
||||
case RIL.MMI_SC_PUK:
|
||||
return RIL.MMI_KS_SC_PUK;
|
||||
case RIL.MMI_SC_PUK2:
|
||||
return RIL.MMI_KS_SC_PUK2;
|
||||
case RIL.MMI_SC_IMEI:
|
||||
return RIL.MMI_KS_SC_IMEI;
|
||||
case RIL.MMI_SC_CLIP:
|
||||
return RIL.MMI_KS_SC_CLIP;
|
||||
case RIL.MMI_SC_CLIR:
|
||||
return RIL.MMI_KS_SC_CLIR;
|
||||
case RIL.MMI_SC_BAOC:
|
||||
case RIL.MMI_SC_BAOIC:
|
||||
case RIL.MMI_SC_BAOICxH:
|
||||
case RIL.MMI_SC_BAIC:
|
||||
case RIL.MMI_SC_BAICr:
|
||||
case RIL.MMI_SC_BA_ALL:
|
||||
case RIL.MMI_SC_BA_MO:
|
||||
case RIL.MMI_SC_BA_MT:
|
||||
return RIL.MMI_KS_SC_CALL_BARRING;
|
||||
case RIL.MMI_SC_CALL_WAITING:
|
||||
return RIL.MMI_KS_SC_CALL_WAITING;
|
||||
default:
|
||||
return RIL.MMI_KS_SC_USSD;
|
||||
}
|
||||
},
|
||||
|
||||
hangUp: function(aClientId, aCallIndex) {
|
||||
let parentId = this._currentCalls[aClientId][aCallIndex].parentId;
|
||||
if (parentId) {
|
||||
@ -932,12 +1211,12 @@ TelephonyService.prototype = {
|
||||
}
|
||||
|
||||
// Handle cached dial request.
|
||||
if (this.cachedDialRequest && !this._getOneActiveCall()) {
|
||||
if (this._cachedDialRequest && !this._getOneActiveCall()) {
|
||||
if (DEBUG) debug("All calls held. Perform the cached dial request.");
|
||||
|
||||
let request = this.cachedDialRequest;
|
||||
this._dialInternal(request.clientId, request.options, request.callback);
|
||||
this.cachedDialRequest = null;
|
||||
let request = this._cachedDialRequest;
|
||||
this._sendDialCallRequest(request.clientId, request.options, request.callback);
|
||||
this._cachedDialRequest = null;
|
||||
}
|
||||
|
||||
this._notifyAllListeners("callStateChanged", [aClientId,
|
||||
@ -987,6 +1266,11 @@ TelephonyService.prototype = {
|
||||
this._notifyAllListeners("conferenceCallStateChanged", [aState]);
|
||||
},
|
||||
|
||||
dialMMI: function(aClientId, aMmiString, aCallback) {
|
||||
let mmi = this._parseMMI(aMmiString, this._hasCalls(aClientId));
|
||||
this._dialMMI(aClientId, mmi, aCallback);
|
||||
},
|
||||
|
||||
/**
|
||||
* nsIObserver interface.
|
||||
*/
|
||||
|
@ -16,15 +16,37 @@ struct EnumerateCallsResponse
|
||||
// empty.
|
||||
};
|
||||
|
||||
struct DialResponse
|
||||
struct DialResponseError
|
||||
{
|
||||
// empty.
|
||||
nsString name;
|
||||
};
|
||||
|
||||
struct DialResponseCallSuccess
|
||||
{
|
||||
uint32_t callIndex;
|
||||
nsString number;
|
||||
};
|
||||
|
||||
struct DialResponseMMISuccess
|
||||
{
|
||||
nsString statusMessage;
|
||||
AdditionalInformation additionalInformation;
|
||||
};
|
||||
|
||||
struct DialResponseMMIError
|
||||
{
|
||||
nsString name;
|
||||
AdditionalInformation additionalInformation;
|
||||
};
|
||||
|
||||
union IPCTelephonyResponse
|
||||
{
|
||||
EnumerateCallsResponse;
|
||||
DialResponse;
|
||||
// dial
|
||||
DialResponseError;
|
||||
DialResponseCallSuccess;
|
||||
DialResponseMMISuccess;
|
||||
DialResponseMMIError;
|
||||
};
|
||||
|
||||
protocol PTelephonyRequest
|
||||
@ -34,9 +56,7 @@ protocol PTelephonyRequest
|
||||
child:
|
||||
NotifyEnumerateCallState(uint32_t aClientId, IPCCallStateData aData);
|
||||
|
||||
NotifyDialError(nsString aError);
|
||||
|
||||
NotifyDialSuccess(uint32_t aCallIndex, nsString aNumber);
|
||||
NotifyDialMMI(nsString aServiceCode);
|
||||
|
||||
/**
|
||||
* Sent when the asynchronous request has completed.
|
||||
|
@ -4,6 +4,8 @@
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "TelephonyChild.h"
|
||||
|
||||
#include "mozilla/dom/telephony/TelephonyCallback.h"
|
||||
#include "TelephonyIPCService.h"
|
||||
|
||||
USING_TELEPHONY_NAMESPACE
|
||||
@ -145,9 +147,14 @@ TelephonyRequestChild::Recv__delete__(const IPCTelephonyResponse& aResponse)
|
||||
case IPCTelephonyResponse::TEnumerateCallsResponse:
|
||||
mListener->EnumerateCallStateComplete();
|
||||
break;
|
||||
case IPCTelephonyResponse::TDialResponse:
|
||||
// Do nothing.
|
||||
break;
|
||||
case IPCTelephonyResponse::TDialResponseError:
|
||||
return DoResponse(aResponse.get_DialResponseError());
|
||||
case IPCTelephonyResponse::TDialResponseCallSuccess:
|
||||
return DoResponse(aResponse.get_DialResponseCallSuccess());
|
||||
case IPCTelephonyResponse::TDialResponseMMISuccess:
|
||||
return DoResponse(aResponse.get_DialResponseMMISuccess());
|
||||
case IPCTelephonyResponse::TDialResponseMMIError:
|
||||
return DoResponse(aResponse.get_DialResponseMMIError());
|
||||
default:
|
||||
MOZ_CRASH("Unknown type!");
|
||||
}
|
||||
@ -177,20 +184,79 @@ TelephonyRequestChild::RecvNotifyEnumerateCallState(const uint32_t& aClientId,
|
||||
}
|
||||
|
||||
bool
|
||||
TelephonyRequestChild::RecvNotifyDialError(const nsString& aError)
|
||||
TelephonyRequestChild::RecvNotifyDialMMI(const nsString& aServiceCode)
|
||||
{
|
||||
MOZ_ASSERT(mCallback);
|
||||
|
||||
mCallback->NotifyDialError(aError);
|
||||
mCallback->NotifyDialMMI(aServiceCode);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
TelephonyRequestChild::RecvNotifyDialSuccess(const uint32_t& aCallIndex,
|
||||
const nsString& aNumber)
|
||||
TelephonyRequestChild::DoResponse(const DialResponseError& aResponse)
|
||||
{
|
||||
MOZ_ASSERT(mCallback);
|
||||
mCallback->NotifyDialError(aResponse.name());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
TelephonyRequestChild::DoResponse(const DialResponseCallSuccess& aResponse)
|
||||
{
|
||||
MOZ_ASSERT(mCallback);
|
||||
mCallback->NotifyDialCallSuccess(aResponse.callIndex(), aResponse.number());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
TelephonyRequestChild::DoResponse(const DialResponseMMISuccess& aResponse)
|
||||
{
|
||||
MOZ_ASSERT(mCallback);
|
||||
|
||||
mCallback->NotifyDialSuccess(aCallIndex, aNumber);
|
||||
// FIXME: Need to overload NotifyDialMMISuccess in the IDL. mCallback is not
|
||||
// necessarily an instance of TelephonyCallback.
|
||||
nsRefPtr<TelephonyCallback> callback = static_cast<TelephonyCallback*>(mCallback.get());
|
||||
|
||||
nsAutoString statusMessage(aResponse.statusMessage());
|
||||
AdditionalInformation info(aResponse.additionalInformation());
|
||||
|
||||
switch (info.type()) {
|
||||
case AdditionalInformation::Tvoid_t:
|
||||
callback->NotifyDialMMISuccess(statusMessage);
|
||||
break;
|
||||
case AdditionalInformation::TArrayOfnsString:
|
||||
callback->NotifyDialMMISuccess(statusMessage, info.get_ArrayOfnsString());
|
||||
break;
|
||||
case AdditionalInformation::TArrayOfMozCallForwardingOptions:
|
||||
callback->NotifyDialMMISuccess(statusMessage, info.get_ArrayOfMozCallForwardingOptions());
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("Received invalid type!");
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
TelephonyRequestChild::DoResponse(const DialResponseMMIError& aResponse)
|
||||
{
|
||||
MOZ_ASSERT(mCallback);
|
||||
|
||||
nsAutoString name(aResponse.name());
|
||||
AdditionalInformation info(aResponse.additionalInformation());
|
||||
|
||||
switch (info.type()) {
|
||||
case AdditionalInformation::Tvoid_t:
|
||||
mCallback->NotifyDialMMIError(name);
|
||||
break;
|
||||
case AdditionalInformation::Tuint16_t:
|
||||
mCallback->NotifyDialMMIErrorWithInfo(name, info.get_uint16_t());
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("Received invalid type!");
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -80,13 +80,21 @@ protected:
|
||||
const IPCCallStateData& aData) MOZ_OVERRIDE;
|
||||
|
||||
virtual bool
|
||||
RecvNotifyDialError(const nsString& aError) MOZ_OVERRIDE;
|
||||
|
||||
virtual bool
|
||||
RecvNotifyDialSuccess(const uint32_t& aCallIndex,
|
||||
const nsString& aNumber) MOZ_OVERRIDE;
|
||||
RecvNotifyDialMMI(const nsString& aServiceCode) MOZ_OVERRIDE;
|
||||
|
||||
private:
|
||||
bool
|
||||
DoResponse(const DialResponseError& aResponse);
|
||||
|
||||
bool
|
||||
DoResponse(const DialResponseCallSuccess& aResponse);
|
||||
|
||||
bool
|
||||
DoResponse(const DialResponseMMISuccess& aResponse);
|
||||
|
||||
bool
|
||||
DoResponse(const DialResponseMMIError& aResponse);
|
||||
|
||||
nsCOMPtr<nsITelephonyListener> mListener;
|
||||
nsCOMPtr<nsITelephonyCallback> mCallback;
|
||||
};
|
||||
|
@ -434,6 +434,14 @@ TelephonyRequestParent::DoRequest(const DialRequest& aRequest)
|
||||
return true;
|
||||
}
|
||||
|
||||
nsresult
|
||||
TelephonyRequestParent::SendResponse(const IPCTelephonyResponse& aResponse)
|
||||
{
|
||||
NS_ENSURE_TRUE(!mActorDestroyed, NS_ERROR_FAILURE);
|
||||
|
||||
return Send__delete__(this, aResponse) ? NS_OK : NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// nsITelephonyListener
|
||||
|
||||
NS_IMETHODIMP
|
||||
@ -527,20 +535,105 @@ TelephonyRequestParent::SupplementaryServiceNotification(uint32_t aClientId,
|
||||
// nsITelephonyCallback
|
||||
|
||||
NS_IMETHODIMP
|
||||
TelephonyRequestParent::NotifyDialError(const nsAString& aError)
|
||||
TelephonyRequestParent::NotifyDialMMI(const nsAString& aServiceCode)
|
||||
{
|
||||
NS_ENSURE_TRUE(!mActorDestroyed, NS_ERROR_FAILURE);
|
||||
|
||||
return (SendNotifyDialError(nsString(aError)) &&
|
||||
Send__delete__(this, DialResponse())) ? NS_OK : NS_ERROR_FAILURE;
|
||||
return SendNotifyDialMMI(nsAutoString(aServiceCode)) ? NS_OK : NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TelephonyRequestParent::NotifyDialSuccess(uint32_t aCallIndex,
|
||||
const nsAString& aNumber)
|
||||
TelephonyRequestParent::NotifyDialError(const nsAString& aError)
|
||||
{
|
||||
NS_ENSURE_TRUE(!mActorDestroyed, NS_ERROR_FAILURE);
|
||||
|
||||
return (SendNotifyDialSuccess(aCallIndex, nsString(aNumber)) &&
|
||||
Send__delete__(this, DialResponse())) ? NS_OK : NS_ERROR_FAILURE;
|
||||
return SendResponse(DialResponseError(nsAutoString(aError)));
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TelephonyRequestParent::NotifyDialCallSuccess(uint32_t aCallIndex,
|
||||
const nsAString& aNumber)
|
||||
{
|
||||
return SendResponse(DialResponseCallSuccess(aCallIndex, nsAutoString(aNumber)));
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TelephonyRequestParent::NotifyDialMMISuccess(JS::Handle<JS::Value> aResult)
|
||||
{
|
||||
AutoSafeJSContext cx;
|
||||
RootedDictionary<MozMMIResult> result(cx);
|
||||
|
||||
if (!result.Init(cx, aResult)) {
|
||||
return NS_ERROR_TYPE_ERR;
|
||||
}
|
||||
|
||||
// No additionInformation passed
|
||||
if (!result.mAdditionalInformation.WasPassed()) {
|
||||
return SendResponse(DialResponseMMISuccess(result.mStatusMessage,
|
||||
AdditionalInformation(mozilla::void_t())));
|
||||
}
|
||||
|
||||
OwningUnsignedShortOrObject& info = result.mAdditionalInformation.Value();
|
||||
|
||||
// Currently, we could only accept the following values for |info|:
|
||||
// 1. array of string
|
||||
// 2. array of MozCallForwardingOptions
|
||||
if (!info.IsObject()) {
|
||||
return NS_ERROR_TYPE_ERR;
|
||||
}
|
||||
|
||||
JS::Rooted<JSObject*> object(cx, info.GetAsObject());
|
||||
JS::Rooted<JS::Value> value(cx);
|
||||
uint32_t length;
|
||||
|
||||
if (!JS_IsArrayObject(cx, object) ||
|
||||
!JS_GetArrayLength(cx, object, &length) || length <= 0 ||
|
||||
// Check first element to decide the format of array.
|
||||
!JS_GetElement(cx, object, 0, &value)) {
|
||||
return NS_ERROR_TYPE_ERR;
|
||||
}
|
||||
|
||||
if (value.isString()) {
|
||||
// String[]
|
||||
nsTArray<nsString> infos;
|
||||
|
||||
for (uint32_t i = 0; i < length; i++) {
|
||||
nsAutoJSString str;
|
||||
if (!JS_GetElement(cx, object, i, &value) || !value.isString() ||
|
||||
!str.init(cx, value.toString())) {
|
||||
return NS_ERROR_TYPE_ERR;
|
||||
}
|
||||
infos.AppendElement(str);
|
||||
}
|
||||
|
||||
return SendResponse(DialResponseMMISuccess(result.mStatusMessage,
|
||||
AdditionalInformation(infos)));
|
||||
} else {
|
||||
// IPC::MozCallForwardingOptions[]
|
||||
nsTArray<IPC::MozCallForwardingOptions> infos;
|
||||
|
||||
for (uint32_t i = 0; i < length; i++) {
|
||||
IPC::MozCallForwardingOptions info;
|
||||
if (!JS_GetElement(cx, object, i, &value) || !info.Init(cx, value)) {
|
||||
return NS_ERROR_TYPE_ERR;
|
||||
}
|
||||
infos.AppendElement(info);
|
||||
}
|
||||
|
||||
return SendResponse(DialResponseMMISuccess(result.mStatusMessage,
|
||||
AdditionalInformation(infos)));
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TelephonyRequestParent::NotifyDialMMIError(const nsAString& aError)
|
||||
{
|
||||
return SendResponse(DialResponseMMIError(nsAutoString(aError),
|
||||
AdditionalInformation(mozilla::void_t())));
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TelephonyRequestParent::NotifyDialMMIErrorWithInfo(const nsAString& aError,
|
||||
uint16_t aInfo)
|
||||
{
|
||||
return SendResponse(DialResponseMMIError(nsAutoString(aError),
|
||||
AdditionalInformation(aInfo)));
|
||||
}
|
||||
|
@ -114,6 +114,9 @@ protected:
|
||||
virtual void
|
||||
ActorDestroy(ActorDestroyReason why);
|
||||
|
||||
nsresult
|
||||
SendResponse(const IPCTelephonyResponse& aResponse);
|
||||
|
||||
private:
|
||||
bool mActorDestroyed;
|
||||
|
||||
|
@ -4,6 +4,9 @@
|
||||
* 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/. */
|
||||
|
||||
using struct mozilla::void_t from "ipc/IPCMessageUtils.h";
|
||||
using struct IPC::MozCallForwardingOptions from "mozilla/dom/mobileconnection/MobileConnectionIPCSerializer.h";
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
namespace telephony {
|
||||
@ -31,6 +34,13 @@ struct IPCCdmaWaitingCallData
|
||||
uint16_t namePresentation;
|
||||
};
|
||||
|
||||
union AdditionalInformation {
|
||||
void_t;
|
||||
uint16_t;
|
||||
nsString[];
|
||||
MozCallForwardingOptions[];
|
||||
};
|
||||
|
||||
} /* namespace telephony */
|
||||
} /* namespace dom */
|
||||
} /* namespace mozilla */
|
||||
|
@ -21,6 +21,7 @@ EXPORTS.mozilla.dom += [
|
||||
EXPORTS.mozilla.dom.telephony += [
|
||||
'ipc/TelephonyChild.h',
|
||||
'ipc/TelephonyParent.h',
|
||||
'TelephonyCallback.h',
|
||||
'TelephonyCommon.h',
|
||||
]
|
||||
|
||||
@ -31,6 +32,7 @@ UNIFIED_SOURCES += [
|
||||
'ipc/TelephonyParent.cpp',
|
||||
'Telephony.cpp',
|
||||
'TelephonyCall.cpp',
|
||||
'TelephonyCallback.cpp',
|
||||
'TelephonyCallGroup.cpp',
|
||||
'TelephonyCallId.cpp',
|
||||
]
|
||||
@ -42,6 +44,7 @@ IPDL_SOURCES += [
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk' and CONFIG['MOZ_B2G_RIL']:
|
||||
XPCSHELL_TESTS_MANIFESTS += ['test/xpcshell/xpcshell.ini']
|
||||
XPIDL_SOURCES += [
|
||||
'nsIGonkTelephonyService.idl',
|
||||
]
|
||||
|
@ -26,4 +26,7 @@ interface nsIGonkTelephonyService : nsITelephonyService
|
||||
in AString notification);
|
||||
|
||||
void notifyConferenceCallStateChanged(in short state);
|
||||
|
||||
void dialMMI(in unsigned long clientId, in AString mmiString,
|
||||
in nsITelephonyCallback callback);
|
||||
};
|
||||
|
@ -176,20 +176,55 @@ interface nsITelephonyListener : nsISupports
|
||||
in AString message);
|
||||
};
|
||||
|
||||
[scriptable, uuid(b3b2b0b0-357f-4efb-bc9f-ca2b2d5686a1)]
|
||||
/**
|
||||
* A callback interface for handling asynchronous response of Telephony.dial().
|
||||
*/
|
||||
[scriptable, uuid(67533db3-cd38-475c-a774-8d0bbf9169fb)]
|
||||
interface nsITelephonyCallback : nsISupports
|
||||
{
|
||||
/**
|
||||
* Called when a dial request is treated as an MMI code and it is about to
|
||||
* process the request.
|
||||
*
|
||||
* @param serviceCode
|
||||
* MMI service code key string that defined in MMI_KS_SC_*
|
||||
*/
|
||||
void notifyDialMMI(in AString serviceCode);
|
||||
|
||||
/**
|
||||
* Called when a dial request fails.
|
||||
*
|
||||
* @param error
|
||||
* Error from RIL.
|
||||
*/
|
||||
void notifyDialError(in AString error);
|
||||
|
||||
/**
|
||||
* Called when a dial request succeeds.
|
||||
* Called when a dial request is treated as a call setup and the result
|
||||
* succeeds.
|
||||
*
|
||||
* @param callIndex
|
||||
* Call index from RIL.
|
||||
* @param number
|
||||
* Dialed out phone number (ex: Temporary CLIR prefix will be removed)
|
||||
*/
|
||||
void notifyDialSuccess(in unsigned long callIndex, in AString number);
|
||||
void notifyDialCallSuccess(in unsigned long callIndex, in AString number);
|
||||
|
||||
/**
|
||||
* Called when a MMI code request succeeds.
|
||||
* The function should only be called after notifyDialMMI.
|
||||
*
|
||||
* @param result
|
||||
* Result of the request. See MozMMIResult.
|
||||
*/
|
||||
void notifyDialMMISuccess(in jsval result);
|
||||
|
||||
/**
|
||||
* Called when a MMI code request fails.
|
||||
* The function should only be called after notifyDialMMI.
|
||||
*/
|
||||
void notifyDialMMIError(in AString error);
|
||||
void notifyDialMMIErrorWithInfo(in AString error, in unsigned short info);
|
||||
};
|
||||
|
||||
%{C++
|
||||
|
@ -1302,3 +1302,25 @@ function startDSDSTest(test) {
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
function sendMMI(aMmi) {
|
||||
let deferred = Promise.defer();
|
||||
|
||||
telephony.dial(aMmi)
|
||||
.then(request => {
|
||||
ok(request instanceof DOMRequest,
|
||||
"request is instanceof " + request.constructor);
|
||||
|
||||
request.addEventListener("success", function(event) {
|
||||
deferred.resolve(request.result);
|
||||
});
|
||||
|
||||
request.addEventListener("error", function(event) {
|
||||
deferred.reject(request.error);
|
||||
});
|
||||
}, cause => {
|
||||
deferred.reject(cause);
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
@ -63,5 +63,7 @@ disabled = Bug 821958
|
||||
[test_incomingcall_phonestate_speaker.js]
|
||||
[test_temporary_clir.js]
|
||||
[test_outgoing_error_state.js]
|
||||
[test_mmi_code.js]
|
||||
[test_outgoing_auto_hold.js]
|
||||
[test_mmi.js]
|
||||
[test_mmi_change_pin.js]
|
||||
[test_mmi_call_forwarding.js]
|
||||
|
30
dom/telephony/test/marionette/test_mmi.js
Normal file
30
dom/telephony/test/marionette/test_mmi.js
Normal file
@ -0,0 +1,30 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
MARIONETTE_TIMEOUT = 60000;
|
||||
MARIONETTE_HEAD_JS = "head.js";
|
||||
|
||||
function testGettingIMEI() {
|
||||
log("Test *#06# ...");
|
||||
|
||||
let MMI_CODE = "*#06#";
|
||||
return sendMMI(MMI_CODE)
|
||||
.then(function resolve(aResult) {
|
||||
ok(true, MMI_CODE + " success");
|
||||
is(aResult.serviceCode, "scImei", "Service code IMEI");
|
||||
// IMEI is hardcoded as "000000000000000".
|
||||
// See it here {B2G_HOME}/external/qemu/telephony/android_modem.c
|
||||
// (The result of +CGSN).
|
||||
is(aResult.statusMessage, "000000000000000", "Emulator IMEI");
|
||||
is(aResult.additionalInformation, undefined, "No additional information");
|
||||
}, function reject() {
|
||||
ok(false, MMI_CODE + " should not fail");
|
||||
});
|
||||
}
|
||||
|
||||
// Start test
|
||||
startTest(function() {
|
||||
Promise.resolve()
|
||||
.then(() => testGettingIMEI())
|
||||
.then(finish);
|
||||
});
|
221
dom/telephony/test/marionette/test_mmi_call_forwarding.js
Normal file
221
dom/telephony/test/marionette/test_mmi_call_forwarding.js
Normal file
@ -0,0 +1,221 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
MARIONETTE_TIMEOUT = 60000;
|
||||
MARIONETTE_HEAD_JS = "head.js";
|
||||
|
||||
let connection;
|
||||
|
||||
/**
|
||||
* Wait for one named MobileConnection event.
|
||||
*
|
||||
* Resolve if that named event occurs. Never reject.
|
||||
*
|
||||
* Fulfill params: the DOMEvent passed.
|
||||
*
|
||||
* @param aEventName
|
||||
* A string event name.
|
||||
*
|
||||
* @return A deferred promise.
|
||||
*/
|
||||
function waitForManagerEvent(aEventName) {
|
||||
let deferred = Promise.defer();
|
||||
|
||||
connection.addEventListener(aEventName, function onevent(aEvent) {
|
||||
connection.removeEventListener(aEventName, onevent);
|
||||
|
||||
ok(true, "MobileConnection event '" + aEventName + "' got.");
|
||||
deferred.resolve(aEvent);
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap DOMRequest onsuccess/onerror events to Promise resolve/reject.
|
||||
*
|
||||
* Fulfill params: A DOMEvent.
|
||||
* Reject params: A DOMEvent.
|
||||
*
|
||||
* @param aRequest
|
||||
* A DOMRequest instance.
|
||||
*
|
||||
* @return A deferred promise.
|
||||
*/
|
||||
function wrapDomRequestAsPromise(aRequest) {
|
||||
let deferred = Promise.defer();
|
||||
|
||||
ok(aRequest instanceof DOMRequest,
|
||||
"aRequest is instanceof " + aRequest.constructor);
|
||||
|
||||
aRequest.addEventListener("success", function(aEvent) {
|
||||
deferred.resolve(aEvent);
|
||||
});
|
||||
aRequest.addEventListener("error", function(aEvent) {
|
||||
deferred.reject(aEvent);
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures call forward options.
|
||||
*
|
||||
* Fulfill params: (none)
|
||||
* Reject params:
|
||||
* 'RadioNotAvailable', 'RequestNotSupported', 'InvalidParameter', or
|
||||
* 'GenericFailure'.
|
||||
*
|
||||
* @param aOptions
|
||||
* A MozCallForwardingOptions.
|
||||
*
|
||||
* @return A deferred promise.
|
||||
*/
|
||||
function setCallForwardingOption(aOptions) {
|
||||
let request = connection.setCallForwardingOption(aOptions);
|
||||
return wrapDomRequestAsPromise(request)
|
||||
.then(null, () => { throw request.error; });
|
||||
}
|
||||
|
||||
const TEST_DATA = [
|
||||
{
|
||||
reason: MozMobileConnection.CALL_FORWARD_REASON_UNCONDITIONAL,
|
||||
number: "+886912345678",
|
||||
serviceClass: MozMobileConnection.ICC_SERVICE_CLASS_VOICE,
|
||||
timeSeconds: 5
|
||||
}, {
|
||||
reason: MozMobileConnection.CALL_FORWARD_REASON_MOBILE_BUSY,
|
||||
number: "0912345678",
|
||||
serviceClass: MozMobileConnection.ICC_SERVICE_CLASS_VOICE,
|
||||
timeSeconds: 10
|
||||
}, {
|
||||
reason: MozMobileConnection.CALL_FORWARD_REASON_NO_REPLY,
|
||||
number: "+886987654321",
|
||||
serviceClass: MozMobileConnection.ICC_SERVICE_CLASS_VOICE,
|
||||
timeSeconds: 15
|
||||
}, {
|
||||
reason: MozMobileConnection.CALL_FORWARD_REASON_NOT_REACHABLE,
|
||||
number: "+0987654321",
|
||||
serviceClass: MozMobileConnection.ICC_SERVICE_CLASS_VOICE,
|
||||
timeSeconds: 20
|
||||
}
|
||||
];
|
||||
|
||||
// Please see TS 22.030 Annex B
|
||||
const CF_REASON_TO_MMI = {
|
||||
/* CALL_FORWARD_REASON_UNCONDITIONAL */
|
||||
0: "21",
|
||||
/* CALL_FORWARD_REASON_MOBILE_BUSY */
|
||||
1: "67",
|
||||
/* CALL_FORWARD_REASON_NO_REPLY */
|
||||
2: "61",
|
||||
/* CALL_FORWARD_REASON_NOT_REACHABLE */
|
||||
3: "62",
|
||||
/* CALL_FORWARD_REASON_ALL_CALL_FORWARDING */
|
||||
4: "002",
|
||||
/* CALL_FORWARD_REASON_ALL_CONDITIONAL_CALL_FORWARDING */
|
||||
5: "004"
|
||||
};
|
||||
|
||||
// Please see TS 22.030 Annex C
|
||||
const SERVICE_CLASS_TO_MMI = {
|
||||
/* ICC_SERVICE_CLASS_VOICE */
|
||||
1: "11"
|
||||
};
|
||||
|
||||
function testSetCallForwarding(aData) {
|
||||
// Registration: **SC*SIA*SIB*SIC#
|
||||
let MMI_CODE = "**" + CF_REASON_TO_MMI[aData.reason] + "*" + aData.number +
|
||||
"*" + SERVICE_CLASS_TO_MMI[aData.serviceClass] +
|
||||
"*" + aData.timeSeconds + "#";
|
||||
log("Test " + MMI_CODE);
|
||||
|
||||
let promises = [];
|
||||
// Check cfstatechange event.
|
||||
promises.push(waitForManagerEvent("cfstatechange").then(function(aEvent) {
|
||||
is(aEvent.success, true, "check success");
|
||||
is(aEvent.action, MozMobileConnection.CALL_FORWARD_ACTION_REGISTRATION,
|
||||
"check action");
|
||||
is(aEvent.reason, aData.reason, "check reason");
|
||||
is(aEvent.number, aData.number, "check number");
|
||||
is(aEvent.timeSeconds, aData.timeSeconds, "check timeSeconds");
|
||||
is(aEvent.serviceClass, aData.serviceClass, "check serviceClass");
|
||||
}));
|
||||
// Check DOMRequest's result.
|
||||
promises.push(sendMMI(MMI_CODE)
|
||||
.then(function resolve(aResult) {
|
||||
is(aResult.serviceCode, "scCallForwarding", "Check service code");
|
||||
is(aResult.statusMessage, "smServiceRegistered", "Check status message");
|
||||
is(aResult.additionalInformation, undefined, "Check additional information");
|
||||
}, function reject(aError) {
|
||||
ok(false, "got '" + aError.name + "' error");
|
||||
}));
|
||||
|
||||
return Promise.all(promises);
|
||||
}
|
||||
|
||||
function testGetCallForwarding(aExpectedData) {
|
||||
// Interrogation: *#SC#
|
||||
let MMI_CODE = "*#" + CF_REASON_TO_MMI[aExpectedData.reason] + "#";
|
||||
log("Test " + MMI_CODE);
|
||||
|
||||
return sendMMI(MMI_CODE)
|
||||
.then(function resolve(aResult) {
|
||||
is(aResult.serviceCode, "scCallForwarding", "Check service code");
|
||||
is(aResult.statusMessage, "smServiceInterrogated", "Check status message");
|
||||
is(Array.isArray(aResult.additionalInformation), true,
|
||||
"additionalInformation should be an array");
|
||||
|
||||
for (let i = 0; i < aResult.additionalInformation.length; i++) {
|
||||
let result = aResult.additionalInformation[i];
|
||||
|
||||
// Only need to check the result containing the serviceClass that we are
|
||||
// interested in.
|
||||
if (!(result.serviceClass & aExpectedData.serviceClass)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
is(result.active, true, "check active");
|
||||
is(result.reason, aExpectedData.reason, "check reason");
|
||||
is(result.number, aExpectedData.number, "check number");
|
||||
is(result.timeSeconds, aExpectedData.timeSeconds, "check timeSeconds");
|
||||
}
|
||||
}, function reject(aError) {
|
||||
ok(false, MMI_CODE + " got error: " + aError.name);
|
||||
});
|
||||
}
|
||||
|
||||
function clearAllCallForwardingSettings() {
|
||||
log("Clear all call forwarding settings");
|
||||
|
||||
let promise = Promise.resolve();
|
||||
for (let reason = MozMobileConnection.CALL_FORWARD_REASON_UNCONDITIONAL;
|
||||
reason <= MozMobileConnection.CALL_FORWARD_REASON_ALL_CONDITIONAL_CALL_FORWARDING;
|
||||
reason++) {
|
||||
let options = {
|
||||
reason: reason,
|
||||
action: MozMobileConnection.CALL_FORWARD_ACTION_ERASURE
|
||||
};
|
||||
// Emulator doesn't support CALL_FORWARD_REASON_ALL_* yet, we catch the
|
||||
// reject here in order to avoid impact the test result.
|
||||
promise =
|
||||
promise.then(() => setCallForwardingOption(options).then(null, () => {}));
|
||||
}
|
||||
return promise;
|
||||
}
|
||||
|
||||
// Start tests
|
||||
startTestWithPermissions(['mobileconnection'], function() {
|
||||
connection = navigator.mozMobileConnections[0];
|
||||
|
||||
let promise = Promise.resolve();
|
||||
for (let i = 0; i < TEST_DATA.length; i++) {
|
||||
let data = TEST_DATA[i];
|
||||
promise = promise.then(() => testSetCallForwarding(data))
|
||||
.then(() => testGetCallForwarding(data));
|
||||
}
|
||||
// reset call forwarding settings.
|
||||
return promise.then(null, () => { ok(false, "promise reject during test"); })
|
||||
.then(() => clearAllCallForwardingSettings())
|
||||
.then(finish);
|
||||
});
|
111
dom/telephony/test/marionette/test_mmi_change_pin.js
Normal file
111
dom/telephony/test/marionette/test_mmi_change_pin.js
Normal file
@ -0,0 +1,111 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
MARIONETTE_TIMEOUT = 60000;
|
||||
MARIONETTE_HEAD_JS = "head.js";
|
||||
|
||||
// PIN is hardcoded as "0000" by default.
|
||||
// See it here {B2G_HOME}/external/qemu/telephony/sim_card.c,
|
||||
// in asimcard_create().
|
||||
const TEST_DATA = [
|
||||
// Test passing no pin.
|
||||
{
|
||||
pin: "",
|
||||
newPin: "0000",
|
||||
newPinAgain: "1111",
|
||||
expectedError: {
|
||||
name: "emMmiError",
|
||||
additionalInformation: null
|
||||
}
|
||||
},
|
||||
// Test passing no newPin.
|
||||
{
|
||||
pin: "0000",
|
||||
newPin: "",
|
||||
newPinAgain: "",
|
||||
expectedError: {
|
||||
name: "emMmiError",
|
||||
additionalInformation: null
|
||||
}
|
||||
},
|
||||
// Test passing mismatched newPin.
|
||||
{
|
||||
pin: "0000",
|
||||
newPin: "0000",
|
||||
newPinAgain: "1111",
|
||||
expectedError: {
|
||||
name: "emMmiErrorMismatchPin",
|
||||
additionalInformation: null
|
||||
}
|
||||
},
|
||||
// Test passing invalid pin (< 4 digit).
|
||||
{
|
||||
pin: "000",
|
||||
newPin: "0000",
|
||||
newPinAgain: "0000",
|
||||
expectedError: {
|
||||
name: "emMmiErrorInvalidPin",
|
||||
additionalInformation: null
|
||||
}
|
||||
},
|
||||
// Test passing invalid newPin (> 8 digit).
|
||||
{
|
||||
pin: "0000",
|
||||
newPin: "000000000",
|
||||
newPinAgain: "000000000",
|
||||
expectedError: {
|
||||
name: "emMmiErrorInvalidPin",
|
||||
additionalInformation: null
|
||||
}
|
||||
},
|
||||
// Test passing incorrect pin.
|
||||
{
|
||||
pin: "1234",
|
||||
newPin: "0000",
|
||||
newPinAgain: "0000",
|
||||
expectedError: {
|
||||
name: "emMmiErrorBadPin",
|
||||
// The default pin retries is 3, failed once becomes to 2
|
||||
additionalInformation: 2
|
||||
}
|
||||
},
|
||||
// Test changing pin successfully (Reset the retries).
|
||||
{
|
||||
pin: "0000",
|
||||
newPin: "0000",
|
||||
newPinAgain: "0000"
|
||||
}
|
||||
];
|
||||
|
||||
function testChangePin(aPin, aNewPin, aNewPinAgain, aExpectedError) {
|
||||
let MMI_CODE = "**04*" + aPin + "*" + aNewPin + "*" + aNewPinAgain + "#";
|
||||
log("Test " + MMI_CODE);
|
||||
|
||||
return sendMMI(MMI_CODE)
|
||||
.then(function resolve(aResult) {
|
||||
ok(!aExpectedError, MMI_CODE + " success");
|
||||
is(aResult.serviceCode, "scPin", "Check service code");
|
||||
is(aResult.statusMessage, "smPinChanged", "Check status message");
|
||||
is(aResult.additionalInformation, undefined, "Check additional information");
|
||||
}, function reject(aError) {
|
||||
ok(aExpectedError, MMI_CODE + " fail");
|
||||
is(aError.name, aExpectedError.name, "Check name");
|
||||
is(aError.message, "", "Check message");
|
||||
is(aError.serviceCode, "scPin", "Check service code");
|
||||
is(aError.additionalInformation, aExpectedError.additionalInformation,
|
||||
"Check additional information");
|
||||
});
|
||||
}
|
||||
|
||||
// Start test
|
||||
startTest(function() {
|
||||
let promise = Promise.resolve();
|
||||
for (let i = 0; i < TEST_DATA.length; i++) {
|
||||
let data = TEST_DATA[i];
|
||||
promise = promise.then(() => testChangePin(data.pin,
|
||||
data.newPin,
|
||||
data.newPinAgain,
|
||||
data.expectedError));
|
||||
}
|
||||
return promise.then(finish);
|
||||
});
|
@ -1,23 +0,0 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
MARIONETTE_TIMEOUT = 60000;
|
||||
MARIONETTE_HEAD_JS = 'head.js';
|
||||
|
||||
let number = "";
|
||||
|
||||
function dialMMI() {
|
||||
telephony.dial("*#06#").then(null, cause => {
|
||||
log("Received promise 'reject'");
|
||||
|
||||
is(telephony.active, null);
|
||||
is(telephony.calls.length, 0);
|
||||
is(cause, "BadNumberError");
|
||||
|
||||
finish();
|
||||
});
|
||||
}
|
||||
|
||||
startTest(function() {
|
||||
dialMMI();
|
||||
});
|
9
dom/telephony/test/xpcshell/header_helpers.js
Normal file
9
dom/telephony/test/xpcshell/header_helpers.js
Normal file
@ -0,0 +1,9 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
||||
|
||||
let subscriptLoader = Cc["@mozilla.org/moz/jssubscript-loader;1"]
|
||||
.getService(Ci.mozIJSSubScriptLoader);
|
308
dom/telephony/test/xpcshell/test_parseMMI.js
Normal file
308
dom/telephony/test/xpcshell/test_parseMMI.js
Normal file
@ -0,0 +1,308 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
subscriptLoader.loadSubScript("resource://gre/modules/ril_consts.js", this);
|
||||
|
||||
let NS = {};
|
||||
subscriptLoader.loadSubScript("resource://gre/components/TelephonyService.js",
|
||||
NS);
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
function parseMMI(mmiString) {
|
||||
return NS.TelephonyService.prototype._parseMMI(mmiString, false);
|
||||
}
|
||||
|
||||
add_test(function test_parseMMI_empty() {
|
||||
let mmi = parseMMI("");
|
||||
|
||||
equal(mmi, null);
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_parseMMI_undefined() {
|
||||
let mmi = parseMMI();
|
||||
|
||||
equal(mmi, null);
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_parseMMI_one_digit_short_code() {
|
||||
let mmi = parseMMI("1");
|
||||
|
||||
equal(mmi.fullMMI, "1");
|
||||
equal(mmi.procedure, undefined);
|
||||
equal(mmi.serviceCode, undefined);
|
||||
equal(mmi.sia, undefined);
|
||||
equal(mmi.sib, undefined);
|
||||
equal(mmi.sic, undefined);
|
||||
equal(mmi.pwd, undefined);
|
||||
equal(mmi.dialNumber, undefined);
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_parseMMI_invalid_short_code() {
|
||||
let mmi = parseMMI("11");
|
||||
|
||||
equal(mmi, null);
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_parseMMI_short_code() {
|
||||
let mmi = parseMMI("21");
|
||||
|
||||
equal(mmi.fullMMI, "21");
|
||||
equal(mmi.procedure, undefined);
|
||||
equal(mmi.serviceCode, undefined);
|
||||
equal(mmi.sia, undefined);
|
||||
equal(mmi.sib, undefined);
|
||||
equal(mmi.sic, undefined);
|
||||
equal(mmi.pwd, undefined);
|
||||
equal(mmi.dialNumber, undefined);
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_parseMMI_dial_string() {
|
||||
let mmi = parseMMI("12345");
|
||||
|
||||
equal(mmi, null);
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_parseMMI_USSD_without_asterisk_prefix() {
|
||||
let mmi = parseMMI("123#");
|
||||
|
||||
equal(mmi.fullMMI, "123#");
|
||||
equal(mmi.procedure, undefined);
|
||||
equal(mmi.serviceCode, undefined);
|
||||
equal(mmi.sia, undefined);
|
||||
equal(mmi.sib, undefined);
|
||||
equal(mmi.sic, undefined);
|
||||
equal(mmi.pwd, undefined);
|
||||
equal(mmi.dialNumber, undefined);
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_parseMMI_USSD() {
|
||||
let mmi = parseMMI("*123#");
|
||||
|
||||
equal(mmi.fullMMI, "*123#");
|
||||
equal(mmi.procedure, MMI_PROCEDURE_ACTIVATION);
|
||||
equal(mmi.serviceCode, "123");
|
||||
equal(mmi.sia, undefined);
|
||||
equal(mmi.sib, undefined);
|
||||
equal(mmi.sic, undefined);
|
||||
equal(mmi.pwd, undefined);
|
||||
equal(mmi.dialNumber, "");
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_parseMMI_sia() {
|
||||
let mmi = parseMMI("*123*1#");
|
||||
|
||||
equal(mmi.fullMMI, "*123*1#");
|
||||
equal(mmi.procedure, MMI_PROCEDURE_ACTIVATION);
|
||||
equal(mmi.serviceCode, "123");
|
||||
equal(mmi.sia, "1");
|
||||
equal(mmi.sib, undefined);
|
||||
equal(mmi.sic, undefined);
|
||||
equal(mmi.pwd, undefined);
|
||||
equal(mmi.dialNumber, "");
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_parseMMI_sib() {
|
||||
let mmi = parseMMI("*123**1#");
|
||||
|
||||
equal(mmi.fullMMI, "*123**1#");
|
||||
equal(mmi.procedure, MMI_PROCEDURE_ACTIVATION);
|
||||
equal(mmi.serviceCode, "123");
|
||||
equal(mmi.sia, "");
|
||||
equal(mmi.sib, "1");
|
||||
equal(mmi.sic, undefined);
|
||||
equal(mmi.pwd, undefined);
|
||||
equal(mmi.dialNumber, "");
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_parseMMI_sic() {
|
||||
let mmi = parseMMI("*123***1#");
|
||||
|
||||
equal(mmi.fullMMI, "*123***1#");
|
||||
equal(mmi.procedure, MMI_PROCEDURE_ACTIVATION);
|
||||
equal(mmi.serviceCode, "123");
|
||||
equal(mmi.sia, "");
|
||||
equal(mmi.sib, "");
|
||||
equal(mmi.sic, "1");
|
||||
equal(mmi.pwd, undefined);
|
||||
equal(mmi.dialNumber, "");
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_parseMMI_sia_sib() {
|
||||
let mmi = parseMMI("*123*1*1#");
|
||||
|
||||
equal(mmi.fullMMI, "*123*1*1#");
|
||||
equal(mmi.procedure, MMI_PROCEDURE_ACTIVATION);
|
||||
equal(mmi.serviceCode, "123");
|
||||
equal(mmi.sia, "1");
|
||||
equal(mmi.sib, "1");
|
||||
equal(mmi.sic, undefined);
|
||||
equal(mmi.pwd, undefined);
|
||||
equal(mmi.dialNumber, "");
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_parseMMI_sia_sic() {
|
||||
let mmi = parseMMI("*123*1**1#");
|
||||
|
||||
equal(mmi.fullMMI, "*123*1**1#");
|
||||
equal(mmi.procedure, MMI_PROCEDURE_ACTIVATION);
|
||||
equal(mmi.serviceCode, "123");
|
||||
equal(mmi.sia, "1");
|
||||
equal(mmi.sib, "");
|
||||
equal(mmi.sic, "1");
|
||||
equal(mmi.pwd, undefined);
|
||||
equal(mmi.dialNumber, "");
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_parseMMI_sib_sic() {
|
||||
let mmi = parseMMI("*123**1*1#");
|
||||
|
||||
equal(mmi.fullMMI, "*123**1*1#");
|
||||
equal(mmi.procedure, MMI_PROCEDURE_ACTIVATION);
|
||||
equal(mmi.serviceCode, "123");
|
||||
equal(mmi.sia, "");
|
||||
equal(mmi.sib, "1");
|
||||
equal(mmi.sic, "1");
|
||||
equal(mmi.pwd, undefined);
|
||||
equal(mmi.dialNumber, "");
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_parseMMI_pwd() {
|
||||
let mmi = parseMMI("*123****1#");
|
||||
|
||||
equal(mmi.fullMMI, "*123****1#");
|
||||
equal(mmi.procedure, MMI_PROCEDURE_ACTIVATION);
|
||||
equal(mmi.serviceCode, "123");
|
||||
equal(mmi.sia, "");
|
||||
equal(mmi.sib, "");
|
||||
equal(mmi.sic, "");
|
||||
equal(mmi.pwd, "1");
|
||||
equal(mmi.dialNumber, "");
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_parseMMI_dial_number() {
|
||||
let mmi = parseMMI("*123#345");
|
||||
|
||||
equal(mmi.fullMMI, "*123#");
|
||||
equal(mmi.procedure, MMI_PROCEDURE_ACTIVATION);
|
||||
equal(mmi.serviceCode, "123");
|
||||
equal(mmi.sia, undefined);
|
||||
equal(mmi.sib, undefined);
|
||||
equal(mmi.sic, undefined);
|
||||
equal(mmi.pwd, undefined);
|
||||
equal(mmi.dialNumber, "345");
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* MMI procedures tests
|
||||
*/
|
||||
|
||||
add_test(function test_parseMMI_activation() {
|
||||
let mmi = parseMMI("*00*12*34*56#");
|
||||
|
||||
equal(mmi.fullMMI, "*00*12*34*56#");
|
||||
equal(mmi.procedure, MMI_PROCEDURE_ACTIVATION);
|
||||
equal(mmi.serviceCode, "00");
|
||||
equal(mmi.sia, "12");
|
||||
equal(mmi.sib, "34");
|
||||
equal(mmi.sic, "56");
|
||||
equal(mmi.pwd, undefined);
|
||||
equal(mmi.dialNumber, "");
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_parseMMI_deactivation() {
|
||||
let mmi = parseMMI("#00*12*34*56#");
|
||||
|
||||
equal(mmi.fullMMI, "#00*12*34*56#");
|
||||
equal(mmi.procedure, MMI_PROCEDURE_DEACTIVATION);
|
||||
equal(mmi.serviceCode, "00");
|
||||
equal(mmi.sia, "12");
|
||||
equal(mmi.sib, "34");
|
||||
equal(mmi.sic, "56");
|
||||
equal(mmi.pwd, undefined);
|
||||
equal(mmi.dialNumber, "");
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_parseMMI_interrogation() {
|
||||
let mmi = parseMMI("*#00*12*34*56#");
|
||||
|
||||
equal(mmi.fullMMI, "*#00*12*34*56#");
|
||||
equal(mmi.procedure, MMI_PROCEDURE_INTERROGATION);
|
||||
equal(mmi.serviceCode, "00");
|
||||
equal(mmi.sia, "12");
|
||||
equal(mmi.sib, "34");
|
||||
equal(mmi.sic, "56");
|
||||
equal(mmi.pwd, undefined);
|
||||
equal(mmi.dialNumber, "");
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_parseMMI_registration() {
|
||||
let mmi = parseMMI("**00*12*34*56#");
|
||||
|
||||
equal(mmi.fullMMI, "**00*12*34*56#");
|
||||
equal(mmi.procedure, MMI_PROCEDURE_REGISTRATION);
|
||||
equal(mmi.serviceCode, "00");
|
||||
equal(mmi.sia, "12");
|
||||
equal(mmi.sib, "34");
|
||||
equal(mmi.sic, "56");
|
||||
equal(mmi.pwd, undefined);
|
||||
equal(mmi.dialNumber, "");
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_parseMMI_erasure() {
|
||||
let mmi = parseMMI("##00*12*34*56#");
|
||||
|
||||
equal(mmi.fullMMI, "##00*12*34*56#");
|
||||
equal(mmi.procedure, MMI_PROCEDURE_ERASURE);
|
||||
equal(mmi.serviceCode, "00");
|
||||
equal(mmi.sia, "12");
|
||||
equal(mmi.sib, "34");
|
||||
equal(mmi.sic, "56");
|
||||
equal(mmi.pwd, undefined);
|
||||
equal(mmi.dialNumber, "");
|
||||
|
||||
run_next_test();
|
||||
});
|
5
dom/telephony/test/xpcshell/xpcshell.ini
Normal file
5
dom/telephony/test/xpcshell/xpcshell.ini
Normal file
@ -0,0 +1,5 @@
|
||||
[DEFAULT]
|
||||
head = header_helpers.js
|
||||
tail =
|
||||
|
||||
[test_parseMMI.js]
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user