Publish Vulpes Preview 15 sources for desktop, Android and Tundra
Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
This commit is contained in:
parent
93e3a48004
commit
b38dea0597
25901 changed files with 9982925 additions and 369 deletions
16
gaia/apps/wappush/build/build.js
Executable file
16
gaia/apps/wappush/build/build.js
Executable file
|
|
@ -0,0 +1,16 @@
|
|||
'use strict';
|
||||
|
||||
/* jshint node: true */
|
||||
|
||||
var utils = require('utils');
|
||||
|
||||
exports.execute = function(config) {
|
||||
utils.copyToStage(config);
|
||||
var distDir = config.GAIA_DISTRIBUTION_DIR;
|
||||
|
||||
var file = utils.getFile(config.STAGE_APP_DIR, 'js', 'whitelist.json');
|
||||
var defaultContent = [];
|
||||
var content = utils.getDistributionFileContent('wappush-whitelist',
|
||||
defaultContent, distDir);
|
||||
utils.writeContent(file, content);
|
||||
};
|
||||
155
gaia/apps/wappush/index.html
Executable file
155
gaia/apps/wappush/index.html
Executable file
|
|
@ -0,0 +1,155 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1">
|
||||
<meta charset="utf-8">
|
||||
<title>WAP Push message</title>
|
||||
|
||||
<!-- Localization -->
|
||||
<meta name="defaultLanguage" content="en-US">
|
||||
<meta name="availableLanguages" content="en-US">
|
||||
<script defer src="shared/js/l10n.js"></script>
|
||||
<link rel="localization" href="locales/wappush.{locale}.properties">
|
||||
|
||||
<!-- Shared styles -->
|
||||
<link rel="stylesheet" type="text/css" href="shared/style/confirm.css" >
|
||||
<link rel="stylesheet" type="text/css" href="shared/style/input_areas.css" >
|
||||
<link rel="stylesheet" type="text/css" href="shared/style/status.css">
|
||||
<link rel="stylesheet" type="text/css" href="shared/style/lists.css">
|
||||
|
||||
<!-- Specific styles -->
|
||||
<link rel="stylesheet" type="text/css" href="style/wappush.css">
|
||||
|
||||
<!-- Shared code -->
|
||||
<script defer src="shared/js/dump.js"></script>
|
||||
<script defer src="shared/js/event_dispatcher.js"></script>
|
||||
<script defer src="shared/js/lazy_loader.js"></script>
|
||||
<script defer src="shared/js/notification_helper.js"></script>
|
||||
<script defer src="shared/js/uuid.js"></script>
|
||||
<!-- Web Components -->
|
||||
<script defer src="shared/elements/config.js"></script>
|
||||
<!-- <link rel="stylesheet" type="text/css" href="shared/elements/gaia-icons/gaia-icons.css" /> -->
|
||||
<link rel="stylesheet" type="text/css" href="app://theme.gaiamobile.org/shared/elements/gaia-theme/gaia-theme.css" />
|
||||
<script defer src="shared/elements/gaia-header/dist/gaia-header.js"></script>
|
||||
|
||||
<!-- Specific code -->
|
||||
<script defer src="js/activity_picker.js"></script>
|
||||
<script defer src="js/auth.js"></script>
|
||||
<script defer src="js/cp_screen_helper.js"></script>
|
||||
<script defer src="js/ext/CryptoJSv3.1.2/rollups/hmac-sha1.js"></script>
|
||||
<script defer src="js/link_action_handler.js"></script>
|
||||
<script defer src="js/messagedb.js"></script>
|
||||
<script defer src="js/parsed_doc.js"></script>
|
||||
<script defer src="js/parsed_message.js"></script>
|
||||
<script defer src="js/provisioning.js"></script>
|
||||
<script defer src="js/si_sl_screen_helper.js"></script>
|
||||
<script defer src="js/store.js"></script>
|
||||
<script defer src="js/utils.js"></script>
|
||||
<script defer src="js/whitelist.js"></script>
|
||||
<script defer src="js/wappush.js"></script>
|
||||
<script defer src="js/startup.js"></script>
|
||||
</head>
|
||||
|
||||
<body role="application" class="theme-productivity">
|
||||
<!-- SI SL message screen -->
|
||||
<article id="si-sl-screen" role="main" hidden>
|
||||
<gaia-header ignore-dir id="header-si-sl" action="close">
|
||||
<h1 id="title-si-sl"></h1>
|
||||
</gaia-header>
|
||||
|
||||
<section class="container" role="region">
|
||||
<div class="message">
|
||||
<p></p>
|
||||
<a data-url="" data-action="url-link"></a>
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
|
||||
<article id="cp-screen" role="main" hidden>
|
||||
|
||||
<!-- Client provisioning APNs screen -->
|
||||
<article id="cp-apn-screen" role="main" class="view">
|
||||
<gaia-header ignore-dir id="header-apn-list" action="close">
|
||||
<h1 id="title-apn"></h1>
|
||||
<button id="apn-accept" data-l10n-id="next" class="full recommend"></button>
|
||||
</gaia-header>
|
||||
|
||||
<section class="container" role="region" data-type="list">
|
||||
<ul class="message">
|
||||
</ul>
|
||||
</section>
|
||||
</article>
|
||||
|
||||
<!-- Client provisioning details screen -->
|
||||
<article id="cp-details-screen" role="main" class="view right">
|
||||
<gaia-header ignore-dir id="header-details" action="close">
|
||||
<h1 id="title-details"></h1>
|
||||
<button id="details-accept" data-l10n-id="next" class="full recommend"></button>
|
||||
</gaia-header>
|
||||
|
||||
<section class="container" role="region" data-type="list">
|
||||
<ul class="message">
|
||||
</ul>
|
||||
</section>
|
||||
</article>
|
||||
|
||||
<!-- Client provisioning message screen -->
|
||||
<article id="cp-pin-screen" role="main" class="view right">
|
||||
<gaia-header ignore-dir id="header-pin" action="back">
|
||||
<h1 id="title-pin"></h1>
|
||||
<button id="pin-accept" data-l10n-id="done" class="full recommend"></button>
|
||||
</gaia-header>
|
||||
|
||||
<section class="container" role="region">
|
||||
<div class="message">
|
||||
<p data-l10n-id="cp-container-title"></p>
|
||||
<input type="hidden" data-l10n-id="cp-pin" placeholder="PIN" required="">
|
||||
<p id="cp-auth-failure" data-l10n-id="cp-auth-failure" hidden></p>
|
||||
<p id="cp-done-help" data-l10n-id="cp-accept-help"></p>
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
</article>
|
||||
|
||||
<!-- Client provisioning quit app confirm dialog -->
|
||||
<form id="cp-quit-app-confirm" role="dialog" data-type="confirm" hidden>
|
||||
<section>
|
||||
<h1 data-l10n-id="cp-container-title"></h1>
|
||||
<p>
|
||||
<strong data-l10n-id="cp-quit-app-confirm-dialog-message"></strong>
|
||||
</p>
|
||||
</section>
|
||||
<menu>
|
||||
<button data-l10n-id="quit" class="quit danger"></button>
|
||||
<button data-l10n-id="cancel" class="cancel recommend"></button>
|
||||
</menu>
|
||||
</form>
|
||||
|
||||
<!-- Client provisioning install configuration confirm dialog -->
|
||||
<form id="cp-install-configuration-confirm" role="dialog" data-type="confirm" hidden>
|
||||
<section>
|
||||
<h1 data-l10n-id="cp-container-title"></h1>
|
||||
<p>
|
||||
<strong data-l10n-id="cp-install-configuration-confirm-dialog-message"></strong>
|
||||
</p>
|
||||
</section>
|
||||
<menu>
|
||||
<button data-l10n-id="cancel" class="cancel"></button>
|
||||
<button data-l10n-id="accept" class="accept recommend"></button>
|
||||
</menu>
|
||||
</form>
|
||||
|
||||
<!-- Client provisioning finish confirm dialog -->
|
||||
<form id="cp-finish-confirm" role="dialog" data-type="confirm" hidden>
|
||||
<section>
|
||||
<h1 data-l10n-id="cp-container-title"></h1>
|
||||
<p>
|
||||
<strong data-l10n-id="cp-finish-confirm-dialog-message-apn-saved"></strong>
|
||||
</p>
|
||||
</section>
|
||||
<menu>
|
||||
<button data-l10n-id="finish" class="full"></button>
|
||||
</menu>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
40
gaia/apps/wappush/js/activity_picker.js
Executable file
40
gaia/apps/wappush/js/activity_picker.js
Executable file
|
|
@ -0,0 +1,40 @@
|
|||
/* -*- Mode: js; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
|
||||
/* global MozActivity */
|
||||
|
||||
(function(exports) {
|
||||
'use strict';
|
||||
|
||||
function activityDefaultError(event) {
|
||||
console.warn('Unhandled error spawning activity: ' +
|
||||
event.target.error.message + '\n');
|
||||
}
|
||||
|
||||
var ActivityPicker = {
|
||||
url: function ap_browse(url, onsuccess, onerror) {
|
||||
if (typeof onerror !== 'function') {
|
||||
onerror = activityDefaultError;
|
||||
}
|
||||
|
||||
if (window.MozActivity) {
|
||||
var activity = new MozActivity({
|
||||
name: 'view',
|
||||
data: {
|
||||
type: 'url',
|
||||
url: url
|
||||
}
|
||||
});
|
||||
|
||||
if (typeof onsuccess === 'function') {
|
||||
activity.onsuccess = onsuccess;
|
||||
}
|
||||
|
||||
activity.onerror = onerror;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
exports.ActivityPicker = ActivityPicker;
|
||||
|
||||
}(this));
|
||||
133
gaia/apps/wappush/js/auth.js
Executable file
133
gaia/apps/wappush/js/auth.js
Executable file
|
|
@ -0,0 +1,133 @@
|
|||
/* -*- Mode: js; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
|
||||
/* global CryptoJS */
|
||||
|
||||
/* exported ProvisioningAuthentication */
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Singleton object that authenticates the sender of the client provisioning
|
||||
* message.
|
||||
*/
|
||||
var ProvisioningAuthentication = (function() {
|
||||
/** The PIN introduced by the user */
|
||||
var pin = null;
|
||||
|
||||
/** The object holding auth info */
|
||||
var auth = null;
|
||||
|
||||
/**
|
||||
* Determines whether the sender (tipically the Client Provisioning server)
|
||||
* could be authenticate as the sender of the message.
|
||||
*
|
||||
* @param {String} pin The PIN introduced by the user.
|
||||
* @param {Object} auth An object containing some info such as the
|
||||
* security mechanism used, the MAC, the raw message
|
||||
* data in binary format (Uint8Array object).
|
||||
*
|
||||
* @return {Boolean}
|
||||
*/
|
||||
function pa_isDocumentValid(userPin, authInfo) {
|
||||
pin = userPin;
|
||||
auth = authInfo;
|
||||
|
||||
var sec = auth.sec;
|
||||
switch (sec) {
|
||||
case 'NETWPIN':
|
||||
// This function won't be called since gecko will be responsible for
|
||||
// authenticate the sender of client provisioning messages with this
|
||||
// authentication mechanism. Let's throw an error.
|
||||
throw new Error('cp-finish-confirm-dialog-message-auth-error');
|
||||
case 'USERPIN':
|
||||
return pa_userPinAuthentication();
|
||||
case 'USERNETWPIN':
|
||||
return pa_userNetwPinAuthentication();
|
||||
case 'USERPINMAC':
|
||||
return pa_userPinMacAuthentication();
|
||||
default:
|
||||
throw new Error(
|
||||
'cp-finish-confirm-dialog-message-auth-unsupported-mechanism'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper than handles USERPIN authentication mechanism.
|
||||
*
|
||||
* @return {Boolean} A flag that indicates the sender of the message could be
|
||||
* authenticated as valid.
|
||||
*/
|
||||
function pa_userPinAuthentication() {
|
||||
var mac = auth.mac;
|
||||
|
||||
return mac === pa_sign(pin, auth.data, auth.data.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper than handles USERNETWPIN authentication mechanism.
|
||||
*
|
||||
* @return {Boolean} A flag that indicates the sender of the message could be
|
||||
* authenticated as valid.
|
||||
*/
|
||||
function pa_userNetwPinAuthentication() {
|
||||
// TODO: This authentication mechanism must be handle somehow by gecko
|
||||
// as well. This is not supported yet. Let's throw an error then.
|
||||
throw new Error(
|
||||
'cp-finish-confirm-dialog-message-auth-unsupported-mechanism'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper than handles USERPINMAC authentication mechanism.
|
||||
*
|
||||
* @return {Boolean} A flag that indicates the sender of the message could be
|
||||
* authenticated as valid.
|
||||
*/
|
||||
function pa_userPinMacAuthentication() {
|
||||
// TODO: This authentication mechanism has not been tested against a real
|
||||
// server so we have decided to leave it out from the implementation until
|
||||
// we are able to tested properly. Let's throw an error then.
|
||||
throw new Error(
|
||||
'cp-finish-confirm-dialog-message-auth-unsupported-mechanism'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Signs the message data. It performs a HMAC SHA1 signing method.
|
||||
*
|
||||
* @param {String} key The key string.
|
||||
* @param {Uint8Array} data The message data.
|
||||
* @param {Integer} dataLength The length of the message data.
|
||||
*
|
||||
* @return {String} The MAC we have generated.
|
||||
*/
|
||||
function pa_sign(key, data, dataLength) {
|
||||
function bin2hex(buffer) {
|
||||
var i, f = 0, a = [], data = new Uint8Array(buffer);
|
||||
f = data.length;
|
||||
for (i = 0; i < f; i++) {
|
||||
a[i] = data[i].toString(16).replace(/^([\da-f])$/, '0$1');
|
||||
}
|
||||
return a.join('');
|
||||
}
|
||||
|
||||
var _data = new Uint8Array(dataLength);
|
||||
for (var i = 0; i < dataLength; i++) {
|
||||
_data[i] = data[i];
|
||||
}
|
||||
|
||||
var hmac = CryptoJS.algo.HMAC.create(CryptoJS.algo.SHA1, key);
|
||||
var words = CryptoJS.enc.Hex.parse(bin2hex(_data));
|
||||
|
||||
hmac.update(words);
|
||||
var hash = hmac.finalize();
|
||||
|
||||
return hash.toString(CryptoJS.enc.Hex).toUpperCase();
|
||||
}
|
||||
|
||||
return {
|
||||
isDocumentValid: pa_isDocumentValid
|
||||
};
|
||||
})();
|
||||
529
gaia/apps/wappush/js/cp_screen_helper.js
Executable file
529
gaia/apps/wappush/js/cp_screen_helper.js
Executable file
|
|
@ -0,0 +1,529 @@
|
|||
/* global MessageDB, ParsedProvisioningDoc, ProvisioningAuthentication,
|
||||
StoreProvisioning, WapPushManager, Utils */
|
||||
|
||||
/* exported CpScreenHelper */
|
||||
|
||||
'use strict';
|
||||
|
||||
var CpScreenHelper = (function() {
|
||||
/** Content provisioning screen node */
|
||||
var cpScreen = null;
|
||||
|
||||
/** Pin screen node */
|
||||
var pinScreen = null;
|
||||
|
||||
/** APN screen node */
|
||||
var apnScreen = null;
|
||||
|
||||
/** Details screen node */
|
||||
var detailsScreen = null;
|
||||
|
||||
/** Quit app button node */
|
||||
var quitButton = null;
|
||||
|
||||
/** Cancel quit app button node */
|
||||
var cancelQuitButton = null;
|
||||
|
||||
/** Accept install configuration button node */
|
||||
var acceptInstallCfgButton = null;
|
||||
|
||||
/** Cancel install configuration button node */
|
||||
var cancelInstallCfgButton = null;
|
||||
|
||||
/** Finish button node */
|
||||
var finishButton = null;
|
||||
|
||||
/** PIN input node */
|
||||
var pin = null;
|
||||
|
||||
/** Quit app confirm dialog node */
|
||||
var quitAppConfirmDialog = null;
|
||||
|
||||
/** Install configuration confirm dialog node */
|
||||
var installCfgConfirmDialog = null;
|
||||
|
||||
/** Auth failure message */
|
||||
var authFailureMessage = null;
|
||||
|
||||
/** Help message */
|
||||
var pinHelp = null;
|
||||
|
||||
/** Finish confirm dialog node */
|
||||
var finishConfirmDialog = null;
|
||||
|
||||
/** User PIN is needed, show input node */
|
||||
var showPINInput = false;
|
||||
|
||||
/** The message has not been authenticated yet */
|
||||
var authenticated = false;
|
||||
|
||||
/** Flag that holds the result of the authentication process */
|
||||
var isDocumentValid = false;
|
||||
|
||||
/** The message has not been processed yet */
|
||||
var processed = false;
|
||||
|
||||
/** Message id for notification management */
|
||||
var messageTag = null;
|
||||
|
||||
/** Authentication parameter for the WAP PUSH message. */
|
||||
var authInfo = null;
|
||||
|
||||
/** WAP provisioning document */
|
||||
var provisioningDoc = null;
|
||||
|
||||
/** All APNs list */
|
||||
var apns = null;
|
||||
|
||||
/** Index of the card on which the message was received. */
|
||||
var iccCardIndex = 0;
|
||||
|
||||
/** Accept button node for each screen */
|
||||
var acceptApnButton = null;
|
||||
var acceptPinButton = null;
|
||||
var acceptDetailsButton = null;
|
||||
|
||||
/** Title of the message, usually holds the sender's number */
|
||||
var titleApn = null;
|
||||
var titleDetails = null;
|
||||
var titlePin = null;
|
||||
|
||||
/** Flag to define the flow of details screen */
|
||||
var multipleApns = false;
|
||||
|
||||
/** Header of details screen */
|
||||
var headerDetails = null;
|
||||
|
||||
function cpsh_init() {
|
||||
// Let's consider the message is completely processed once the settings are
|
||||
// stored into the database.
|
||||
processed = false;
|
||||
|
||||
// Retrieve the various page elements
|
||||
titleApn = document.getElementById('title-apn');
|
||||
titleDetails = document.getElementById('title-details');
|
||||
titlePin = document.getElementById('title-pin');
|
||||
|
||||
quitAppConfirmDialog = document.getElementById('cp-quit-app-confirm');
|
||||
quitButton = quitAppConfirmDialog.querySelector('.quit');
|
||||
cancelQuitButton = quitAppConfirmDialog.querySelector('.cancel');
|
||||
|
||||
installCfgConfirmDialog =
|
||||
document.getElementById('cp-install-configuration-confirm');
|
||||
acceptInstallCfgButton = installCfgConfirmDialog.querySelector('.accept');
|
||||
cancelInstallCfgButton = installCfgConfirmDialog.querySelector('.cancel');
|
||||
|
||||
authFailureMessage = document.getElementById('cp-auth-failure');
|
||||
pinHelp = document.getElementById('cp-done-help');
|
||||
|
||||
finishConfirmDialog = document.getElementById('cp-finish-confirm');
|
||||
finishButton = finishConfirmDialog.querySelector('button');
|
||||
|
||||
cpScreen = document.getElementById('cp-screen');
|
||||
|
||||
pinScreen = document.getElementById('cp-pin-screen');
|
||||
pin = pinScreen.querySelector('input');
|
||||
|
||||
apnScreen = document.getElementById('cp-apn-screen');
|
||||
detailsScreen = document.getElementById('cp-details-screen');
|
||||
|
||||
acceptApnButton = document.getElementById('apn-accept');
|
||||
acceptPinButton = document.getElementById('pin-accept');
|
||||
acceptDetailsButton = document.getElementById('details-accept');
|
||||
|
||||
var headerApnList = document.getElementById('header-apn-list');
|
||||
headerDetails = document.getElementById('header-details');
|
||||
var headerPin = document.getElementById('header-pin');
|
||||
|
||||
// Event handlers
|
||||
quitButton.addEventListener('click', cpsh_onQuit);
|
||||
cancelQuitButton.addEventListener('click', cpsh_onCancelQuit);
|
||||
finishButton.addEventListener('click', cpsh_onFinish);
|
||||
pin.addEventListener('keyup', cpsh_onPinInput);
|
||||
acceptApnButton.addEventListener('click', cpsh_onApnAccept);
|
||||
acceptPinButton.addEventListener('click', cpsh_onPinAccept);
|
||||
acceptDetailsButton.addEventListener('click', cpsh_onDetailsAccept);
|
||||
headerApnList.addEventListener('action', cpsh_onClose);
|
||||
headerDetails.addEventListener('action', cpsh_closeDetails);
|
||||
headerPin.addEventListener('action', cpsh_closePin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hides all the panels.
|
||||
*/
|
||||
function cpsh_hide() {
|
||||
cpScreen.hidden = true;
|
||||
finishConfirmDialog.hidden = true;
|
||||
installCfgConfirmDialog.hidden = true;
|
||||
quitAppConfirmDialog.hidden = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Init the application flow showing a warning prompt that asks to the user if
|
||||
* wants to install the configuration information.
|
||||
*/
|
||||
function cpsh_showConfirmInstallationDialog(message) {
|
||||
installCfgConfirmDialog.hidden = false;
|
||||
acceptInstallCfgButton
|
||||
.addEventListener('click',cpsh_onAcceptInstallCfg.bind(null, message));
|
||||
cancelInstallCfgButton.addEventListener('click', cpsh_onCancelInstallCfg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the client provisioning screen visible and populate it.
|
||||
*/
|
||||
function cpsh_populateScreen(message) {
|
||||
cpScreen.hidden = false;
|
||||
|
||||
var title = Utils.prepareMessageTitle(message);
|
||||
navigator.mozL10n.setAttributes(titleApn, title.id, title.args);
|
||||
navigator.mozL10n.setAttributes(titleDetails, title.id, title.args);
|
||||
navigator.mozL10n.setAttributes(titlePin, title.id, title.args);
|
||||
|
||||
iccCardIndex = message.serviceId;
|
||||
messageTag = message.timestamp;
|
||||
|
||||
authInfo = message.provisioning.authInfo;
|
||||
provisioningDoc = message.provisioning.provisioningDoc;
|
||||
|
||||
// Get the number of APNs to show details or the list of them.
|
||||
var parsedProvisioningDoc = ParsedProvisioningDoc.from(provisioningDoc);
|
||||
apns = parsedProvisioningDoc.getApns();
|
||||
|
||||
// The provisioning document might not have valid APNs (other ones that
|
||||
// those ones in APPLICATION nodes for the Browsing Enabler and AC for the
|
||||
// Multimedia Messaging System Enabler). In this case we must not continue.
|
||||
if (apns.length === 0) {
|
||||
message = finishConfirmDialog.querySelector('strong');
|
||||
navigator.mozL10n.setAttributes(message,
|
||||
'cp-finish-confirm-dialog-message-no-apns');
|
||||
finishConfirmDialog.hidden = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Make the PIN input visible when the authentication mechanism needs a PIN
|
||||
// 0 NETWPIN, there is no need for an user PIN
|
||||
// 1 USERPIN, the authentication mechanism needs a PIN from the user
|
||||
// 1 USERNETWPIN, the authentication mechanism needs a PIN from the user
|
||||
// 1 USERPINMAC, the authentication mechanism needs a PIN from the user
|
||||
showPINInput = (authInfo.sec !== 'NETWPIN');
|
||||
// 'message.authInfo.checked' property tell us the authentication process
|
||||
// was already performed by gecko
|
||||
authenticated = authInfo.checked;
|
||||
// 'message.authInfo.pass' property tell us the result of the authentication
|
||||
// process when this process was performed by gecko
|
||||
isDocumentValid = authInfo.pass;
|
||||
|
||||
if (showPINInput) {
|
||||
// If the document has not been authenticated yet and the PIN code is
|
||||
// needed, show some info and the PIN input element to the user.
|
||||
navigator.mozL10n.setAttributes(pinHelp, 'cp-accept-help-pin');
|
||||
pin.type = 'number';
|
||||
acceptPinButton.disabled = true;
|
||||
} else {
|
||||
navigator.mozL10n.setAttributes(pinHelp, 'cp-done-help');
|
||||
pin.type = 'hidden';
|
||||
pin.blur();
|
||||
acceptPinButton.disabled = false;
|
||||
}
|
||||
|
||||
if (apns.length > 1) {
|
||||
multipleApns = true;
|
||||
cpsh_showApnList();
|
||||
return;
|
||||
}
|
||||
|
||||
multipleApns = false;
|
||||
cpsh_showDetails(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the apn list screen when there are multiple apns in a configuration
|
||||
* message populating a list with each apn name.
|
||||
*/
|
||||
function cpsh_showApnList() {
|
||||
var fragment = document.createDocumentFragment();
|
||||
|
||||
for (var i = 0; i < apns.length; i++) {
|
||||
var list = document.createElement('li');
|
||||
|
||||
var a = document.createElement('a');
|
||||
a.addEventListener('click', cpsh_showDetails.bind(null, i));
|
||||
|
||||
var aside = document.createElement('aside');
|
||||
aside.className = 'pack-end';
|
||||
aside.setAttribute('data-icon', 'forward');
|
||||
// Icon is for presentation only.
|
||||
aside.setAttribute('aria-hidden', true);
|
||||
|
||||
var p = document.createElement('p');
|
||||
p.textContent = apns[i].carrier;
|
||||
|
||||
a.appendChild(aside);
|
||||
a.appendChild(p);
|
||||
list.appendChild(a);
|
||||
fragment.appendChild(list);
|
||||
}
|
||||
|
||||
var content = apnScreen.querySelector('.message');
|
||||
content.innerHTML = '';
|
||||
|
||||
content.appendChild(fragment);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the application flow when the user clicks on the 'next' button
|
||||
* from the apn list screen.
|
||||
*/
|
||||
function cpsh_onApnAccept() {
|
||||
if (showPINInput) {
|
||||
pinScreen.addEventListener('transitionend', cpsh_setPinFocus);
|
||||
}
|
||||
|
||||
apnScreen.classList.add('left');
|
||||
pinScreen.classList.remove('right');
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the apn details screen.
|
||||
*/
|
||||
function cpsh_showDetails(index) {
|
||||
if (multipleApns) {
|
||||
acceptDetailsButton.style.visibility = 'hidden';
|
||||
headerDetails.setAttribute('action', 'back');
|
||||
}
|
||||
|
||||
var details = detailsScreen.querySelector('.message');
|
||||
details.innerHTML = '';
|
||||
|
||||
cpsh_createNode(details, 'Name', apns[index].carrier);
|
||||
cpsh_createNode(details, 'APN', apns[index].apn);
|
||||
cpsh_createNode(details, 'Proxy', apns[index].proxy);
|
||||
cpsh_createNode(details, 'Port', apns[index].port);
|
||||
cpsh_createNode(details, 'Username', apns[index].user);
|
||||
cpsh_createNode(details, 'Password', apns[index].password);
|
||||
cpsh_createNode(details, 'MMSC', apns[index].mmsc);
|
||||
cpsh_createNode(details, 'MMS Proxy', apns[index].mmsproxy);
|
||||
cpsh_createNode(details, 'MMS Port', apns[index].mmsport);
|
||||
cpsh_createNode(details, 'Authentication Type', apns[index].authType);
|
||||
cpsh_createNode(details, 'APN Type', apns[index].types);
|
||||
|
||||
detailsScreen.classList.remove('right');
|
||||
apnScreen.classList.add('left');
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the application flow when the user clicks on the 'back'/ 'close'
|
||||
* button from the details screen.
|
||||
* If there are multiple APNs then back button to go back to apn list screen.
|
||||
* If only one APN then shows quit confirm.
|
||||
*/
|
||||
function cpsh_closeDetails(evt) {
|
||||
if (!multipleApns) {
|
||||
cpsh_onClose(evt);
|
||||
return;
|
||||
}
|
||||
|
||||
detailsScreen.classList.add('right');
|
||||
apnScreen.classList.remove('left');
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the application flow when the user clicks on the 'next' button
|
||||
* from the details screen.
|
||||
*/
|
||||
function cpsh_onDetailsAccept() {
|
||||
if (showPINInput) {
|
||||
pinScreen.addEventListener('transitionend', cpsh_setPinFocus);
|
||||
}
|
||||
|
||||
detailsScreen.classList.add('left');
|
||||
pinScreen.classList.remove('right');
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function used to populate the list of the APN details.
|
||||
*/
|
||||
function cpsh_createNode(rootNode, key, value) {
|
||||
if (!value) {
|
||||
return;
|
||||
}
|
||||
|
||||
var element = document.createElement('li');
|
||||
var nodeKey = document.createElement('p');
|
||||
var nodeValue = document.createElement('p');
|
||||
nodeKey.textContent = key;
|
||||
nodeValue.textContent = value;
|
||||
element.appendChild(nodeKey);
|
||||
element.appendChild(nodeValue);
|
||||
rootNode.appendChild(element);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function used to set focus to pin input when transition to pin
|
||||
* screen has finished.
|
||||
*/
|
||||
function cpsh_setPinFocus() {
|
||||
pin.focus();
|
||||
pinScreen.removeEventListener('transitionend', cpsh_setPinFocus);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the application flow when the user clicks on the 'back' button
|
||||
* from the pin screen.
|
||||
*/
|
||||
function cpsh_closePin() {
|
||||
pin.blur();
|
||||
|
||||
if (!multipleApns) {
|
||||
pinScreen.classList.add('right');
|
||||
detailsScreen.classList.remove('left');
|
||||
return;
|
||||
}
|
||||
|
||||
pinScreen.classList.add('right');
|
||||
apnScreen.classList.remove('left');
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the application flow when the user clicks on the 'close' button
|
||||
* from the client provisioning screen.
|
||||
*/
|
||||
function cpsh_onClose(evt) {
|
||||
if (processed) {
|
||||
WapPushManager.close();
|
||||
return;
|
||||
}
|
||||
quitAppConfirmDialog.hidden = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the application flow when the user clicks on the 'Quit' button
|
||||
* from the client provisioning quit app confirm dialog.
|
||||
*/
|
||||
function cpsh_onQuit(evt) {
|
||||
evt.preventDefault();
|
||||
|
||||
MessageDB.deleteByTimestamp(messageTag).then(function() {
|
||||
WapPushManager.close();
|
||||
}, function() {
|
||||
console.error('Could not delete message from the database');
|
||||
WapPushManager.close();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the application flow when the user clicks on the 'Cancel' button
|
||||
* from the client provisioning quit app confirm dialog.
|
||||
*/
|
||||
function cpsh_onCancelQuit(evt) {
|
||||
evt.preventDefault();
|
||||
quitAppConfirmDialog.hidden = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the application flow when the user clicks on the 'Cancel' button
|
||||
* from the client install configuration screen.
|
||||
*/
|
||||
function cpsh_onCancelInstallCfg(evt) {
|
||||
evt.preventDefault();
|
||||
WapPushManager.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the application flow when the user clicks on the 'Cancel' button
|
||||
* from the client install configuration screen.
|
||||
*/
|
||||
function cpsh_onAcceptInstallCfg(message, evt) {
|
||||
evt.preventDefault();
|
||||
cpsh_populateScreen(message);
|
||||
installCfgConfirmDialog.hidden = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Accepts the message, authenticates the sender and presents the settings to
|
||||
* be stored to the user
|
||||
*/
|
||||
function cpsh_onPinAccept() {
|
||||
var message = null;
|
||||
|
||||
if (!authenticated) {
|
||||
if (!pin.value) {
|
||||
// Need a valid PIN code, show error.
|
||||
cpsh_pinError();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Need to perform the authentication process.
|
||||
isDocumentValid =
|
||||
ProvisioningAuthentication.isDocumentValid(pin.value,
|
||||
authInfo);
|
||||
} catch (e) {
|
||||
message = finishConfirmDialog.querySelector('strong');
|
||||
navigator.mozL10n.setAttributes(message, e.message);
|
||||
finishConfirmDialog.hidden = false;
|
||||
return;
|
||||
}
|
||||
|
||||
authenticated = true;
|
||||
if (!isDocumentValid) {
|
||||
// The document couldn't be authenticated, show error.
|
||||
authenticated = false;
|
||||
cpsh_pinError();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
processed = true;
|
||||
// Store APNs into the database.
|
||||
StoreProvisioning.provision(apns, iccCardIndex);
|
||||
|
||||
/* Show finish confirm dialog after having deleted the message, this is
|
||||
* done even if the deletion fails for some reason. */
|
||||
MessageDB.deleteByTimestamp(messageTag).then(function() {
|
||||
finishConfirmDialog.hidden = false;
|
||||
}, function(e) {
|
||||
console.error('Could not delete message from the database: ', e);
|
||||
finishConfirmDialog.hidden = false;
|
||||
});
|
||||
}
|
||||
|
||||
function cpsh_pinError() {
|
||||
authFailureMessage.hidden = false;
|
||||
pinHelp.hidden = true;
|
||||
pin.focus();
|
||||
// Set max to -1 in order to show invalid input.
|
||||
pin.setAttribute('max', -1);
|
||||
pin.addEventListener('keyup', cpsh_onPinErrorRestore);
|
||||
}
|
||||
|
||||
function cpsh_onPinErrorRestore() {
|
||||
authFailureMessage.hidden = true;
|
||||
pinHelp.hidden = false;
|
||||
pin.removeAttribute('max');
|
||||
pin.addEventListener('keyup', cpsh_onPinInput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the application flow when the user clicks on the 'Finish' button
|
||||
* from the client provisioning finish confirm dialog.
|
||||
*/
|
||||
function cpsh_onFinish(evt) {
|
||||
evt.preventDefault();
|
||||
WapPushManager.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable / disable accept button if pin field is empty.
|
||||
*/
|
||||
function cpsh_onPinInput(evt) {
|
||||
acceptPinButton.disabled = (pin.value.length === 0);
|
||||
}
|
||||
|
||||
return {
|
||||
init: cpsh_init,
|
||||
hide: cpsh_hide,
|
||||
showConfirmInstallationDialog: cpsh_showConfirmInstallationDialog
|
||||
};
|
||||
})();
|
||||
17
gaia/apps/wappush/js/ext/CryptoJSv3.1.2/rollups/hmac-sha1.js
Executable file
17
gaia/apps/wappush/js/ext/CryptoJSv3.1.2/rollups/hmac-sha1.js
Executable file
|
|
@ -0,0 +1,17 @@
|
|||
/*
|
||||
CryptoJS v3.1.2
|
||||
code.google.com/p/crypto-js
|
||||
(c) 2009-2013 by Jeff Mott. All rights reserved.
|
||||
code.google.com/p/crypto-js/wiki/License
|
||||
*/
|
||||
var CryptoJS=CryptoJS||function(g,l){var e={},d=e.lib={},m=function(){},k=d.Base={extend:function(a){m.prototype=this;var c=new m;a&&c.mixIn(a);c.hasOwnProperty("init")||(c.init=function(){c.$super.init.apply(this,arguments)});c.init.prototype=c;c.$super=this;return c},create:function(){var a=this.extend();a.init.apply(a,arguments);return a},init:function(){},mixIn:function(a){for(var c in a)a.hasOwnProperty(c)&&(this[c]=a[c]);a.hasOwnProperty("toString")&&(this.toString=a.toString)},clone:function(){return this.init.prototype.extend(this)}},
|
||||
p=d.WordArray=k.extend({init:function(a,c){a=this.words=a||[];this.sigBytes=c!=l?c:4*a.length},toString:function(a){return(a||n).stringify(this)},concat:function(a){var c=this.words,q=a.words,f=this.sigBytes;a=a.sigBytes;this.clamp();if(f%4)for(var b=0;b<a;b++)c[f+b>>>2]|=(q[b>>>2]>>>24-8*(b%4)&255)<<24-8*((f+b)%4);else if(65535<q.length)for(b=0;b<a;b+=4)c[f+b>>>2]=q[b>>>2];else c.push.apply(c,q);this.sigBytes+=a;return this},clamp:function(){var a=this.words,c=this.sigBytes;a[c>>>2]&=4294967295<<
|
||||
32-8*(c%4);a.length=g.ceil(c/4)},clone:function(){var a=k.clone.call(this);a.words=this.words.slice(0);return a},random:function(a){for(var c=[],b=0;b<a;b+=4)c.push(4294967296*g.random()|0);return new p.init(c,a)}}),b=e.enc={},n=b.Hex={stringify:function(a){var c=a.words;a=a.sigBytes;for(var b=[],f=0;f<a;f++){var d=c[f>>>2]>>>24-8*(f%4)&255;b.push((d>>>4).toString(16));b.push((d&15).toString(16))}return b.join("")},parse:function(a){for(var c=a.length,b=[],f=0;f<c;f+=2)b[f>>>3]|=parseInt(a.substr(f,
|
||||
2),16)<<24-4*(f%8);return new p.init(b,c/2)}},j=b.Latin1={stringify:function(a){var c=a.words;a=a.sigBytes;for(var b=[],f=0;f<a;f++)b.push(String.fromCharCode(c[f>>>2]>>>24-8*(f%4)&255));return b.join("")},parse:function(a){for(var c=a.length,b=[],f=0;f<c;f++)b[f>>>2]|=(a.charCodeAt(f)&255)<<24-8*(f%4);return new p.init(b,c)}},h=b.Utf8={stringify:function(a){try{return decodeURIComponent(escape(j.stringify(a)))}catch(c){throw Error("Malformed UTF-8 data");}},parse:function(a){return j.parse(unescape(encodeURIComponent(a)))}},
|
||||
r=d.BufferedBlockAlgorithm=k.extend({reset:function(){this._data=new p.init;this._nDataBytes=0},_append:function(a){"string"==typeof a&&(a=h.parse(a));this._data.concat(a);this._nDataBytes+=a.sigBytes},_process:function(a){var c=this._data,b=c.words,f=c.sigBytes,d=this.blockSize,e=f/(4*d),e=a?g.ceil(e):g.max((e|0)-this._minBufferSize,0);a=e*d;f=g.min(4*a,f);if(a){for(var k=0;k<a;k+=d)this._doProcessBlock(b,k);k=b.splice(0,a);c.sigBytes-=f}return new p.init(k,f)},clone:function(){var a=k.clone.call(this);
|
||||
a._data=this._data.clone();return a},_minBufferSize:0});d.Hasher=r.extend({cfg:k.extend(),init:function(a){this.cfg=this.cfg.extend(a);this.reset()},reset:function(){r.reset.call(this);this._doReset()},update:function(a){this._append(a);this._process();return this},finalize:function(a){a&&this._append(a);return this._doFinalize()},blockSize:16,_createHelper:function(a){return function(b,d){return(new a.init(d)).finalize(b)}},_createHmacHelper:function(a){return function(b,d){return(new s.HMAC.init(a,
|
||||
d)).finalize(b)}}});var s=e.algo={};return e}(Math);
|
||||
(function(){var g=CryptoJS,l=g.lib,e=l.WordArray,d=l.Hasher,m=[],l=g.algo.SHA1=d.extend({_doReset:function(){this._hash=new e.init([1732584193,4023233417,2562383102,271733878,3285377520])},_doProcessBlock:function(d,e){for(var b=this._hash.words,n=b[0],j=b[1],h=b[2],g=b[3],l=b[4],a=0;80>a;a++){if(16>a)m[a]=d[e+a]|0;else{var c=m[a-3]^m[a-8]^m[a-14]^m[a-16];m[a]=c<<1|c>>>31}c=(n<<5|n>>>27)+l+m[a];c=20>a?c+((j&h|~j&g)+1518500249):40>a?c+((j^h^g)+1859775393):60>a?c+((j&h|j&g|h&g)-1894007588):c+((j^h^
|
||||
g)-899497514);l=g;g=h;h=j<<30|j>>>2;j=n;n=c}b[0]=b[0]+n|0;b[1]=b[1]+j|0;b[2]=b[2]+h|0;b[3]=b[3]+g|0;b[4]=b[4]+l|0},_doFinalize:function(){var d=this._data,e=d.words,b=8*this._nDataBytes,g=8*d.sigBytes;e[g>>>5]|=128<<24-g%32;e[(g+64>>>9<<4)+14]=Math.floor(b/4294967296);e[(g+64>>>9<<4)+15]=b;d.sigBytes=4*e.length;this._process();return this._hash},clone:function(){var e=d.clone.call(this);e._hash=this._hash.clone();return e}});g.SHA1=d._createHelper(l);g.HmacSHA1=d._createHmacHelper(l)})();
|
||||
(function(){var g=CryptoJS,l=g.enc.Utf8;g.algo.HMAC=g.lib.Base.extend({init:function(e,d){e=this._hasher=new e.init;"string"==typeof d&&(d=l.parse(d));var g=e.blockSize,k=4*g;d.sigBytes>k&&(d=e.finalize(d));d.clamp();for(var p=this._oKey=d.clone(),b=this._iKey=d.clone(),n=p.words,j=b.words,h=0;h<g;h++)n[h]^=1549556828,j[h]^=909522486;p.sigBytes=b.sigBytes=k;this.reset()},reset:function(){var e=this._hasher;e.reset();e.update(this._iKey)},update:function(e){this._hasher.update(e);return this},finalize:function(e){var d=
|
||||
this._hasher;e=d.finalize(e);d.reset();return d.finalize(this._oKey.clone().concat(e))}})})();
|
||||
48
gaia/apps/wappush/js/link_action_handler.js
Executable file
48
gaia/apps/wappush/js/link_action_handler.js
Executable file
|
|
@ -0,0 +1,48 @@
|
|||
/* -*- Mode: js; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
|
||||
/* global ActivityPicker */
|
||||
|
||||
(function(exports) {
|
||||
'use strict';
|
||||
|
||||
var inProgress = false;
|
||||
|
||||
var LinkActionHandler = {
|
||||
onClick: function lah_onClick(event) {
|
||||
var dataset = event.target.dataset;
|
||||
var action = dataset.action;
|
||||
var type;
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
if (!action) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* To avoid activity pile up, return immediately if the last activity is
|
||||
* still in progress. */
|
||||
if (inProgress) {
|
||||
return;
|
||||
}
|
||||
|
||||
inProgress = true;
|
||||
|
||||
type = action.replace('-link', '');
|
||||
|
||||
/* Use `LinkActionHandler.reset` (this.reset) as BOTH the success and
|
||||
* error callback. This ensure that any activities will be freed
|
||||
* regardless of their resulting state. */
|
||||
|
||||
ActivityPicker[type](
|
||||
dataset[type], this.reset, this.reset
|
||||
);
|
||||
},
|
||||
|
||||
reset: function lah_reset() {
|
||||
inProgress = false;
|
||||
}
|
||||
};
|
||||
|
||||
exports.LinkActionHandler = LinkActionHandler;
|
||||
}(this));
|
||||
412
gaia/apps/wappush/js/messagedb.js
Executable file
412
gaia/apps/wappush/js/messagedb.js
Executable file
|
|
@ -0,0 +1,412 @@
|
|||
/* global EventDispatcher, IDBKeyRange, Promise */
|
||||
|
||||
/* exported MessageDB */
|
||||
|
||||
'use strict';
|
||||
|
||||
var MessageDB = (function() {
|
||||
var DB_NAME = 'wappush';
|
||||
var DB_VERSION = 2;
|
||||
var MESSAGE_STORE_NAME = 'message';
|
||||
var VIEWED_STORE_NAME = 'viewed';
|
||||
var KEY_PATH = 'timestamp';
|
||||
var ID_INDEX = 'id';
|
||||
var ID_INDEX_KEYPATH = 'id';
|
||||
|
||||
var db = null;
|
||||
|
||||
/**
|
||||
* Turns an IndexedDB transaction in a promise that is resolved when the
|
||||
* transaction completes or is rejected if it fails.
|
||||
*
|
||||
* @param {Object} transaction An IDBTransaction object.
|
||||
* @return {Promise} A promise that is resolved when the transaction
|
||||
* completes or is rejected if an error occurs.
|
||||
*/
|
||||
function mdb_transactionPromise(transaction) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
transaction.oncomplete = function mdb_onTransactionComplete(event) {
|
||||
resolve();
|
||||
};
|
||||
transaction.onerror = function mdb_onTransactionError(event) {
|
||||
reject(event.target.error);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete all the messages that have been marked as viewed, this is done at
|
||||
* startup to get rid of all the messages that have already been shown to the
|
||||
* user.
|
||||
*
|
||||
* @return {Object} A promise for this operation
|
||||
*/
|
||||
function mdb_deleteViewedMessages() {
|
||||
var transaction =
|
||||
db.transaction([ MESSAGE_STORE_NAME, VIEWED_STORE_NAME ], 'readwrite');
|
||||
var promise = mdb_transactionPromise(transaction);
|
||||
var store = transaction.objectStore(MESSAGE_STORE_NAME);
|
||||
var viewedStore = transaction.objectStore(VIEWED_STORE_NAME);
|
||||
var req = viewedStore.openCursor();
|
||||
|
||||
req.onsuccess = event => {
|
||||
var cursor = event.target.result;
|
||||
|
||||
if (cursor) {
|
||||
store.delete(cursor.value.timestamp);
|
||||
cursor.delete();
|
||||
cursor.continue();
|
||||
}
|
||||
};
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the database, creates it if it has not been created already. Returns
|
||||
* a promise that is resolved with the value of the database object or
|
||||
* rejected with an error object.
|
||||
*
|
||||
* @return {Object} A promise for this operation.
|
||||
*/
|
||||
function mdb_open() {
|
||||
if (db) {
|
||||
return Promise.resolve(db);
|
||||
}
|
||||
|
||||
var indexedDB;
|
||||
|
||||
try {
|
||||
indexedDB = window.indexedDB || window.webkitIndexedDB ||
|
||||
window.mozIndexedDB || window.msIndexedDB;
|
||||
} catch (e) {
|
||||
return Promise.reject(e);
|
||||
}
|
||||
|
||||
if (!indexedDB) {
|
||||
return Promise.reject(new Error('IndexedDB is not available'));
|
||||
}
|
||||
|
||||
return new Promise(function(resolve, reject) {
|
||||
var req;
|
||||
|
||||
try {
|
||||
req = indexedDB.open(DB_NAME, DB_VERSION);
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
return;
|
||||
}
|
||||
|
||||
req.onsuccess = function mdb_opened(event) {
|
||||
db = event.target.result;
|
||||
mdb_deleteViewedMessages().then(() => resolve(db));
|
||||
};
|
||||
req.onerror = function mdb_openError(event) {
|
||||
reject(event.target.error);
|
||||
};
|
||||
req.onupgradeneeded = function mdb_upgradeNeeded(event) {
|
||||
var db = event.target.result;
|
||||
var store = null;
|
||||
|
||||
switch (event.oldVersion) {
|
||||
case 0:
|
||||
store = db.createObjectStore(
|
||||
MESSAGE_STORE_NAME, { keyPath: KEY_PATH }
|
||||
);
|
||||
store.createIndex(ID_INDEX, ID_INDEX_KEYPATH, { unique: false });
|
||||
/* falls through */
|
||||
case 1:
|
||||
store = db.createObjectStore(
|
||||
VIEWED_STORE_NAME, { keyPath: KEY_PATH }
|
||||
);
|
||||
break; // We're done with the upgrade
|
||||
default:
|
||||
event.target.transaction.abort();
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a message executing the associated action. This deals with
|
||||
* messages with a 'delete' action by deleting all coresponding message and
|
||||
* with new messages by storing them.
|
||||
*
|
||||
* @param {Object} store The messages' object store.
|
||||
* @param {Object} message The message to be processed.
|
||||
* @returns {Boolean} Returns true if the message has been stored, false
|
||||
* if it's been deleted.
|
||||
*/
|
||||
function mdb_processMessage(store, message) {
|
||||
/* If the message has a 'delete' action delete all messages with the
|
||||
* corresponding 'si-id' field and drop the current message. All other
|
||||
* messages should be stored. */
|
||||
if (message.action === 'delete') {
|
||||
mdb_deleteById(store, message.id);
|
||||
return false;
|
||||
} else {
|
||||
store.put(message);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a message to the message database according to the out-of-order
|
||||
* delivery rules specified in WAP-167 6.2. Returns a promise that is
|
||||
* resolved when the message has been stored. This will dispatch all the
|
||||
* appropriate events.
|
||||
*
|
||||
* @fires new
|
||||
* @fires update
|
||||
* @fires discard
|
||||
* @fires delete
|
||||
*
|
||||
* @param {Object} message A parsed WAP Push message to be processed.
|
||||
*
|
||||
* @return {Object} A promise for this operation.
|
||||
*/
|
||||
function mdb_put(message) {
|
||||
return mdb_open().then(function mdb_openResolved(db) {
|
||||
var transaction = db.transaction(MESSAGE_STORE_NAME, 'readwrite');
|
||||
var promise = mdb_transactionPromise(transaction);
|
||||
var store = transaction.objectStore(MESSAGE_STORE_NAME);
|
||||
var stored;
|
||||
|
||||
if (!message.id) {
|
||||
// The message has no 'si-id' field, store it
|
||||
store.put(message);
|
||||
mdb_dispatchEvent('new', message);
|
||||
return promise;
|
||||
}
|
||||
|
||||
if (!message.created) {
|
||||
/* The message has a 'si-id' field but no creation time, process it and
|
||||
* store it if needed */
|
||||
stored = mdb_processMessage(store, message);
|
||||
mdb_dispatchEvent(stored ? 'new' : 'discard', message);
|
||||
return promise;
|
||||
}
|
||||
|
||||
/* The message has both a 'si-id' and a 'created' field, make it go
|
||||
* through the out-of-order delivery logic. This will check if the
|
||||
* message is new, updates an existing one or is just outdated and
|
||||
* needs to be discarded. */
|
||||
var index = store.index(ID_INDEX);
|
||||
var req = index.openCursor(IDBKeyRange.only(message.id));
|
||||
|
||||
req.onsuccess = function mdb_cursorSuccess(event) {
|
||||
var cursor = event.target.result;
|
||||
|
||||
if (cursor) {
|
||||
var storedMessage = cursor.value;
|
||||
|
||||
/* If this is a new version of an existing message, expire the
|
||||
* previous message and flag this message as updated. Older versions
|
||||
* are discarded right away with no further action required. */
|
||||
if (storedMessage.created) {
|
||||
if (storedMessage.created < message.created) {
|
||||
cursor.delete(storedMessage);
|
||||
message.timestamp = storedMessage.timestamp;
|
||||
stored = mdb_processMessage(store, message);
|
||||
mdb_dispatchEvent(stored ? 'update' : 'discard', message);
|
||||
} else {
|
||||
mdb_dispatchEvent('discard', message);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
cursor.continue();
|
||||
} else {
|
||||
/* We found no matching message with a 'created' property set,
|
||||
* process the message and store it if needed */
|
||||
stored = mdb_processMessage(store, message);
|
||||
mdb_dispatchEvent(stored ? 'new' : 'discard', message);
|
||||
}
|
||||
};
|
||||
|
||||
return promise;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes all messages with the specified si-id. Generates a 'delete'
|
||||
* event for every message that was deleted.
|
||||
*
|
||||
* @param {Object} store The messages' object store.
|
||||
* @param {String} id The si-id of the messages to be deleted.
|
||||
*/
|
||||
function mdb_deleteById(store, id) {
|
||||
var index = store.index(ID_INDEX);
|
||||
var req = index.openCursor(id);
|
||||
|
||||
req.onsuccess = function mdb_openCursorSuccess(event) {
|
||||
var cursor = event.target.result;
|
||||
|
||||
if (cursor) {
|
||||
var message = cursor.value;
|
||||
|
||||
cursor.delete().onsuccess = function mdb_onDeleted() {
|
||||
mdb_dispatchEvent('delete', message);
|
||||
};
|
||||
cursor.continue();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a message and passes it to the success callback as its sole
|
||||
* parameter. Once retrieved the message is marked for removal and deleted
|
||||
* the next time the application is started except if it's a CP message in
|
||||
* which case it can only be deleted explicitly. A promise is returned that
|
||||
* will resolve to the message. If the message is not found then the promise
|
||||
* will resolve to the null value.
|
||||
*
|
||||
* @param {Number} timestamp The timestamp identifiying the message to be
|
||||
* retrieved.
|
||||
*
|
||||
* @return {Object} A promise for this operation.
|
||||
*/
|
||||
function mdb_retrieve(timestamp) {
|
||||
return mdb_open().then(function mdb_openResolved(db) {
|
||||
var transaction =
|
||||
db.transaction([ MESSAGE_STORE_NAME, VIEWED_STORE_NAME ], 'readwrite');
|
||||
var promise = mdb_transactionPromise(transaction);
|
||||
|
||||
var store = transaction.objectStore(MESSAGE_STORE_NAME);
|
||||
var req = store.get(timestamp.toString());
|
||||
var message = null;
|
||||
|
||||
req.onsuccess = function mdb_gotMessage(event) {
|
||||
if (event.target.result) {
|
||||
message = event.target.result;
|
||||
|
||||
if (message.type != 'text/vnd.wap.connectivity-xml') {
|
||||
var viewedStore = transaction.objectStore(VIEWED_STORE_NAME);
|
||||
|
||||
/* Mark message as viewed, we'll delete it the next time we
|
||||
* open the application. */
|
||||
viewedStore.put(message);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return promise.then(() => {
|
||||
if (message) {
|
||||
return message;
|
||||
} else {
|
||||
return Promise.reject(
|
||||
new Error('Message with id ' + timestamp + ' not found')
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes all the messages with the specified timestamp.
|
||||
*
|
||||
* @fires delete
|
||||
*
|
||||
* @param {String} timestamp The timestamp used to identify the messages to
|
||||
* be deleted.
|
||||
*
|
||||
* @return {Promise} A promise for this operation.
|
||||
*/
|
||||
function mdb_deleteByTimestamp(timestamp) {
|
||||
return mdb_open().then(function mdb_deleteCallback(db) {
|
||||
var transaction = db.transaction(MESSAGE_STORE_NAME, 'readwrite');
|
||||
var promise = mdb_transactionPromise(transaction);
|
||||
var store = transaction.objectStore(MESSAGE_STORE_NAME);
|
||||
|
||||
timestamp = timestamp.toString();
|
||||
store.get(timestamp).onsuccess = function(event) {
|
||||
var message = event.target.result;
|
||||
|
||||
store.delete(timestamp).onsuccess = function mdb_onDeleted() {
|
||||
mdb_dispatchEvent('delete', message);
|
||||
};
|
||||
};
|
||||
|
||||
return promise;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all messages from the database. Returns a promise that is
|
||||
* resolved when the operation has completed. This does not fire events.
|
||||
*
|
||||
* @return {Object} A promise for this operation.
|
||||
*/
|
||||
function mdb_clear() {
|
||||
return mdb_open().then(function mdb_openResolved(db) {
|
||||
var transaction =
|
||||
db.transaction([ MESSAGE_STORE_NAME, VIEWED_STORE_NAME ], 'readwrite');
|
||||
var promise = mdb_transactionPromise(transaction);
|
||||
var store = transaction.objectStore(MESSAGE_STORE_NAME);
|
||||
var viewedStore = transaction.objectStore(VIEWED_STORE_NAME);
|
||||
|
||||
store.clear();
|
||||
viewedStore.clear();
|
||||
|
||||
return promise;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatches a new event to the registered event handlers.
|
||||
*
|
||||
* @param {String} type The event type.
|
||||
* @param {Object} target The event target, usually a message.
|
||||
*/
|
||||
function mdb_dispatchEvent(type, target) {
|
||||
/* We explicitly use the MessageDB singleton as the EventDispatcher code
|
||||
* requires the `this' reference to operate. */
|
||||
MessageDB.emit(type, target);
|
||||
}
|
||||
|
||||
/**
|
||||
* New event, emitted when a new message has been stored in the database
|
||||
*
|
||||
* @event MessageDB#new
|
||||
* @type {Object}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Update event, emitted when an existing message was updated.
|
||||
*
|
||||
* @event MessageDB#update
|
||||
* @type {Object}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Discard event, emitted when a message was discarded instea of being
|
||||
* stored in the database.
|
||||
*
|
||||
* @event MessageDB#discard
|
||||
*/
|
||||
|
||||
/**
|
||||
* Delete event : emitted when a message has been deleted
|
||||
*
|
||||
* @event MessageDB@delete
|
||||
* @type {Object}
|
||||
*/
|
||||
|
||||
/**
|
||||
* List of events which can be listened to on this object.
|
||||
*/
|
||||
var mdb_allowedEvents = [
|
||||
'new',
|
||||
'update',
|
||||
'discard',
|
||||
'delete'
|
||||
];
|
||||
|
||||
return EventDispatcher.mixin({
|
||||
put: mdb_put,
|
||||
retrieve: mdb_retrieve,
|
||||
deleteByTimestamp: mdb_deleteByTimestamp,
|
||||
clear: mdb_clear
|
||||
}, mdb_allowedEvents);
|
||||
})();
|
||||
361
gaia/apps/wappush/js/parsed_doc.js
Executable file
361
gaia/apps/wappush/js/parsed_doc.js
Executable file
|
|
@ -0,0 +1,361 @@
|
|||
/* -*- Mode: js; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
|
||||
(function(exports) {
|
||||
'use strict';
|
||||
|
||||
var CONTENT_TYPE = 'application/xml';
|
||||
var AUTHTYPE_MAPPING = {
|
||||
'PAP': '1',
|
||||
'CHAP': '2'
|
||||
};
|
||||
var TYPE_MAPPING = {
|
||||
'w2': 'default',
|
||||
'w4': 'mms'
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents a parsed WAP provisioning document. The most important elements
|
||||
* in this document are the APN objects the device needs to set up data calls.
|
||||
*
|
||||
* @param {String} provisioningDoc The WAP provisioning document (XML string).
|
||||
*/
|
||||
function ParsedProvisioningDoc(provisioningDoc) {
|
||||
/** WAP provisioning document */
|
||||
this._provisioningDoc = provisioningDoc;
|
||||
|
||||
/** Holds the list of APPLICATION (APPLICATION characteristic is used to
|
||||
define application protocol parameters and to describe the attributes
|
||||
of an application service access point available using the protocol)
|
||||
nodes in the document. */
|
||||
this._applicationNodes = null;
|
||||
|
||||
/** Holds the list of NAPDEF (NAPDEF characteristics define network access
|
||||
points) nodes in the document */
|
||||
this._napDefNodes = null;
|
||||
|
||||
/** Holds the list of PXLOGICAL (PXLOGICAL characteristics define logical
|
||||
proxies) nodes in the document. */
|
||||
this._pxLogicalNodes = null;
|
||||
|
||||
/** Array containing APN objects */
|
||||
this._apns = [],
|
||||
|
||||
/** Array containing APN proxy objects */
|
||||
this._proxies = [];
|
||||
|
||||
/** APNs are ready to use (flag) */
|
||||
this._apnsReady = false;
|
||||
}
|
||||
|
||||
ParsedProvisioningDoc.prototype = {
|
||||
/**
|
||||
* Given a PXLOGICAL node it returns the list of PXPHYSICAL (PXPHYSICAL
|
||||
* characteristics convey information on physical instances of a logical
|
||||
* proxy) nodes inside.
|
||||
*
|
||||
* @param {element} toProxyNode A PXLOGICAL node.
|
||||
*
|
||||
* @return {NodeList} List of PXPHYSICAL nodes.
|
||||
*/
|
||||
getPxPhysicalNodes: function pd_getPxPhysicalNodes(toProxyNode) {
|
||||
var toProxy = toProxyNode.getAttribute('value');
|
||||
var pxLogicalNode = null;
|
||||
for (var i = 0; i < this._pxLogicalNodes.length; i++) {
|
||||
var selector = 'parm[name="PROXY-ID"][value="' + toProxy + '"]';
|
||||
pxLogicalNode = this._pxLogicalNodes[i].querySelector(selector);
|
||||
if (pxLogicalNode) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!pxLogicalNode) {
|
||||
return null;
|
||||
}
|
||||
return this._pxLogicalNodes[i].querySelectorAll(
|
||||
'characteristic[type="PXPHYSICAL"]'
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Given a network access point id it returns the NAPDEF node.
|
||||
*
|
||||
* @param {String} toNapId Network access point id.
|
||||
*
|
||||
* @return {element} A NAPDEF node.
|
||||
*/
|
||||
getNapDefNode: function pd_getNapDefNode(toNapId) {
|
||||
for (var i = 0; i < this._napDefNodes.length; i++) {
|
||||
var selector = 'parm[name="NAPID"][value="' + toNapId + '"]';
|
||||
var napDefNode = this._napDefNodes[i].querySelector(selector);
|
||||
if (napDefNode) {
|
||||
return this._napDefNodes[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Parses the WAP provisioning document. It mainly parses the document and
|
||||
* saves the APN objects.
|
||||
*/
|
||||
parse: function pd_parse() {
|
||||
if (!this._provisioningDoc) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function.
|
||||
* Creates an untyped object from a PXPHYSICAL node.
|
||||
*
|
||||
* @param {element} pxPhysicalNode An PXPHYSICAL node.
|
||||
* @param {boolean} isMmsProxy Determines whether PXPHYSICAL node is a
|
||||
* proxy for MMS.
|
||||
*
|
||||
* @return {Object} An untyped object holding the properties of an
|
||||
* PXPHYSICAL node.
|
||||
*/
|
||||
function parsePxPhysicalNode(pxPhysicalNode, isMmsProxy) {
|
||||
var obj = {};
|
||||
var proxyKey = null, portKey = null;
|
||||
|
||||
proxyKey = isMmsProxy ? 'mmsproxy' : 'proxy';
|
||||
portKey = isMmsProxy ? 'mmsport' : 'port';
|
||||
|
||||
var pxAddrNode = pxPhysicalNode.querySelector('parm[name="PXADDR"]');
|
||||
if (pxAddrNode) {
|
||||
obj[proxyKey] = pxAddrNode.getAttribute('value');
|
||||
}
|
||||
|
||||
obj['TO-NAPID'] = [];
|
||||
var toNapIdNode = pxPhysicalNode.querySelector('parm[name="TO-NAPID"]');
|
||||
if (toNapIdNode) {
|
||||
obj['TO-NAPID'].push(toNapIdNode.getAttribute('value'));
|
||||
}
|
||||
|
||||
var portNodes =
|
||||
pxPhysicalNode.querySelectorAll('characteristic[type="PORT"]');
|
||||
for (var j = 0; j < portNodes.length; j++) {
|
||||
var portNode = portNodes[j].querySelector('parm[name="PORTNBR"]');
|
||||
obj[portKey] = portNode.getAttribute('value');
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function.
|
||||
* Creates an untyped object from a NAPDEF node.
|
||||
*
|
||||
* @param {element} napDefNode An NAPDEF node.
|
||||
*
|
||||
* @return {Object} An untyped object holding the properties of an
|
||||
* PXPHYSICAL node.
|
||||
*/
|
||||
function parseNapDefNode(napDefNode) {
|
||||
var obj = {};
|
||||
|
||||
var napIdNode = napDefNode.querySelector('parm[name="NAPID"]');
|
||||
if (napIdNode) {
|
||||
obj.NAPID = napIdNode.getAttribute('value');
|
||||
}
|
||||
|
||||
var nameNode = napDefNode.querySelector('parm[name="NAME"]');
|
||||
if (nameNode) {
|
||||
obj.carrier = nameNode.getAttribute('value');
|
||||
}
|
||||
|
||||
var apnNode = napDefNode.querySelector('parm[name="NAP-ADDRESS"]');
|
||||
if (apnNode) {
|
||||
obj.apn = apnNode.getAttribute('value');
|
||||
}
|
||||
|
||||
var napAuthInfoNode =
|
||||
napDefNode.querySelector('characteristic[type="NAPAUTHINFO"]');
|
||||
if (napAuthInfoNode) {
|
||||
var authTypeNode =
|
||||
napAuthInfoNode.querySelector('parm[name="AUTHTYPE"]');
|
||||
if (authTypeNode) {
|
||||
var authType = AUTHTYPE_MAPPING[
|
||||
authTypeNode.getAttribute('value')
|
||||
];
|
||||
if (authType) {
|
||||
obj.authtype = authType;
|
||||
}
|
||||
}
|
||||
|
||||
var authNameNode =
|
||||
napAuthInfoNode.querySelector('parm[name="AUTHNAME"]');
|
||||
if (authNameNode) {
|
||||
obj.user = authNameNode.getAttribute('value');
|
||||
}
|
||||
|
||||
var authSecretNode =
|
||||
napAuthInfoNode.querySelector('parm[name="AUTHSECRET"]');
|
||||
if (authSecretNode) {
|
||||
obj.password = authSecretNode.getAttribute('value');
|
||||
}
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
var parser = new DOMParser();
|
||||
var domDocument = parser.parseFromString(this._provisioningDoc,
|
||||
CONTENT_TYPE);
|
||||
this._applicationNodes =
|
||||
domDocument.querySelectorAll('characteristic[type="APPLICATION"]');
|
||||
this._napDefNodes =
|
||||
domDocument.querySelectorAll('characteristic[type="NAPDEF"]');
|
||||
this._pxLogicalNodes =
|
||||
domDocument.querySelectorAll('characteristic[type="PXLOGICAL"]');
|
||||
|
||||
// The APPLICATION nodes can occur 0 or more times but it makes no sense
|
||||
// to continue when the current document doesn't have at least one one of
|
||||
// them.
|
||||
if (!this._applicationNodes) {
|
||||
return;
|
||||
}
|
||||
|
||||
var napDefNode = null;
|
||||
var apn = null;
|
||||
var type = [];
|
||||
var proxy = null;
|
||||
|
||||
var applicationNode = null;
|
||||
for (var j = 0; j < this._applicationNodes.length; j++) {
|
||||
applicationNode = this._applicationNodes[j];
|
||||
|
||||
var appIdNode = null, appId = null;
|
||||
appIdNode = applicationNode.querySelector('parm[name="APPID"]');
|
||||
appId = appIdNode.getAttribute('value');
|
||||
|
||||
var toProxyNodes = null;
|
||||
toProxyNodes =
|
||||
applicationNode.querySelectorAll('parm[name="TO-PROXY"]');
|
||||
|
||||
var toNapIdNodes = null;
|
||||
toNapIdNodes =
|
||||
applicationNode.querySelectorAll('parm[name="TO-NAPID"]');
|
||||
|
||||
var addrNode = null, addr = null;
|
||||
addrNode = applicationNode.querySelector('parm[name="ADDR"]');
|
||||
if (addrNode) {
|
||||
addr = addrNode.getAttribute('value');
|
||||
}
|
||||
|
||||
// Only look for APPLICATION nodes for the Browsing Enabler and AC for
|
||||
// the Multimedia Messaging System Enabler
|
||||
if (appId && (appId !== 'w2') && (appId !== 'w4')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!toNapIdNodes && !toProxyNodes) {
|
||||
// The TO-NAPID parameter refers to a network access point with a
|
||||
// matching NAPID parameter. At least we need one of them since it
|
||||
// links to the NAPDEF characteristic node containig the APN.
|
||||
|
||||
// The TO-PROXY parameter refers to a logical proxy with a matching
|
||||
// PROXY-ID. The logical proxy might contain a phisical proxy which
|
||||
// might point to an APN.
|
||||
continue;
|
||||
}
|
||||
|
||||
// Add the type for the APN relaying on the APPID param value. The type
|
||||
// will be added as an additional proprerty to the other APN properties.
|
||||
type = [];
|
||||
type.push(TYPE_MAPPING[appId]);
|
||||
|
||||
if (toProxyNodes) {
|
||||
// If the application characteristic node points to a logical proxy
|
||||
// characteristic node let's find the APN through the physical
|
||||
// proxy characteristic nodes that the logical proxy must contain.
|
||||
for (var l = 0; l < toProxyNodes.length; l++) {
|
||||
var pxPhysicalNodes = this.getPxPhysicalNodes(toProxyNodes[l]);
|
||||
|
||||
if (!pxPhysicalNodes) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (var m = 0; m < pxPhysicalNodes.length; m++) {
|
||||
proxy = parsePxPhysicalNode(pxPhysicalNodes[m],
|
||||
(TYPE_MAPPING[appId] === 'mms'));
|
||||
this._proxies.push(proxy);
|
||||
for (var n = 0; n < proxy['TO-NAPID'].length; n++) {
|
||||
napDefNode = this.getNapDefNode(proxy['TO-NAPID'][n]);
|
||||
apn = parseNapDefNode(napDefNode);
|
||||
// Add type property.
|
||||
apn.types = type;
|
||||
// Add mmsc property when MMS APN
|
||||
if ((TYPE_MAPPING[appId] === 'mms') && addr) {
|
||||
apn.mmsc = addr;
|
||||
}
|
||||
this._apns.push(apn);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (toNapIdNodes) {
|
||||
for (var o = 0; o < toNapIdNodes.length; o++) {
|
||||
var toNapId = toNapIdNodes[o].getAttribute('value');
|
||||
if (!toNapId) {
|
||||
continue;
|
||||
}
|
||||
napDefNode = this.getNapDefNode(toNapId);
|
||||
apn = parseNapDefNode(napDefNode);
|
||||
// Add type property.
|
||||
apn.types = type;
|
||||
this._apns.push(apn);
|
||||
}
|
||||
}
|
||||
} // for this._applicationNodes
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the list of APN objects in the WAP provisioning document.
|
||||
*
|
||||
* @return {Array} The list of APN objects.
|
||||
*/
|
||||
getApns: function ppd_getApns() {
|
||||
function addProperties(src, dst) {
|
||||
for (var key in src) {
|
||||
dst[key] = src[key];
|
||||
}
|
||||
}
|
||||
|
||||
if (this._apnsReady) {
|
||||
return this._apns;
|
||||
}
|
||||
for (var i = 0; i < this._proxies.length; i++) {
|
||||
var proxy = this._proxies[i];
|
||||
for (var j = 0; j < proxy['TO-NAPID'].length; j++) {
|
||||
var TO_NAPID = proxy['TO-NAPID'][j];
|
||||
for (var k = 0; k < this._apns.length; k++) {
|
||||
var apn = this._apns[k];
|
||||
if (TO_NAPID === apn.NAPID) {
|
||||
addProperties(proxy, apn);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this._apnsReady = true;
|
||||
return this._apns;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Parses a WAP provisioning document and returns the object holding the list
|
||||
* of APN objects in the document.
|
||||
*
|
||||
* @param {String} provisioningDoc The WAP provisioning document (XML string).
|
||||
*
|
||||
* @return {Object} A ParsedProvisioningDoc object.
|
||||
*/
|
||||
ParsedProvisioningDoc.from = function ppd_from(provisioningDoc) {
|
||||
var obj = new ParsedProvisioningDoc(provisioningDoc);
|
||||
obj.parse();
|
||||
|
||||
return obj;
|
||||
};
|
||||
|
||||
exports.ParsedProvisioningDoc = ParsedProvisioningDoc;
|
||||
})(window);
|
||||
244
gaia/apps/wappush/js/parsed_message.js
Executable file
244
gaia/apps/wappush/js/parsed_message.js
Executable file
|
|
@ -0,0 +1,244 @@
|
|||
/* global Promise, Provisioning, MessageDB */
|
||||
|
||||
(function(exports) {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Default constructor, use it to copy objects. To parse objects from XML use
|
||||
* the factory function from()
|
||||
*
|
||||
* @param {Object} [obj] An object from which to copy the fields.
|
||||
*/
|
||||
function ParsedMessage(obj) {
|
||||
if (obj) {
|
||||
for (var key in obj) {
|
||||
this[key] = obj[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ParsedMessage.prototype = {
|
||||
/**
|
||||
* Constructor, this is for internal use only
|
||||
*/
|
||||
constructor: ParsedMessage,
|
||||
|
||||
/**
|
||||
* Creates an untyped object from a typed one
|
||||
*
|
||||
* @return {Object} An untyped representation of this object.
|
||||
*/
|
||||
toJSON: function pm_toJSON() {
|
||||
var obj = {
|
||||
type: this.type,
|
||||
sender: this.sender,
|
||||
serviceId: this.serviceId,
|
||||
timestamp: this.timestamp
|
||||
};
|
||||
|
||||
if (this.href) {
|
||||
obj.href = this.href;
|
||||
}
|
||||
|
||||
if (this.id) {
|
||||
obj.id = this.id;
|
||||
}
|
||||
|
||||
if (this.created) {
|
||||
obj.created = this.created;
|
||||
}
|
||||
|
||||
if (this.expires) {
|
||||
obj.expires = this.expires;
|
||||
}
|
||||
|
||||
if (this.text) {
|
||||
obj.text = this.text;
|
||||
}
|
||||
|
||||
if (this.action) {
|
||||
obj.action = this.action;
|
||||
}
|
||||
|
||||
if (this.provisioning) {
|
||||
obj.provisioning = this.provisioning;
|
||||
}
|
||||
|
||||
return obj;
|
||||
},
|
||||
|
||||
/**
|
||||
* Saves the message in the database. Returns a promise that resolves
|
||||
* to a string describing the status of the message: 'new' if the message
|
||||
* was new, 'updated' if the message updated an existing message or
|
||||
* 'discarded' if the message was discarded.
|
||||
*
|
||||
* @return {Object} A promise for this operation.
|
||||
*/
|
||||
save: function pm_save() {
|
||||
var self = this;
|
||||
var json_message = this.toJSON();
|
||||
|
||||
return MessageDB.put(json_message).then(function(status) {
|
||||
if (status === 'updated') {
|
||||
// In case the message was updated we must update the original too.
|
||||
self.timestamp = json_message.timestamp;
|
||||
}
|
||||
|
||||
return Promise.resolve(status);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns true if the message has already expired
|
||||
*/
|
||||
isExpired: function pm_isExpired() {
|
||||
return (this.expires && (this.expires < Date.now())) || false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the message unique identifier
|
||||
*
|
||||
* @returns {String} An identifier for this message
|
||||
*/
|
||||
getUniqueId: function pm_getUniqueId() {
|
||||
return this.timestamp;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse a message and returns an object holding its fields. The returned
|
||||
* object will contain the following fields:
|
||||
* - type: the content type of the message, either 'text/vnd.wap.si' or
|
||||
* 'text/vnd.wap.sl'
|
||||
* - sender: the sender of this message, a MSISDN
|
||||
* - timestamp: a timestamp taken when parsing the object
|
||||
* - href: optional for SI messages, required for SL, a URL to be displayed
|
||||
* - id: optional for SI messages, a pseudo-unique ID for the message
|
||||
* - created: optional for SI messages, creation time of this message
|
||||
* - expires: optional for SI messages, expiration time of this message
|
||||
* - action: optional for SI/SL message, action to be executed
|
||||
* - provisioning: only for CP messages, CP related object
|
||||
* - text: optional, text to be displayed
|
||||
*
|
||||
* @param {Object} message A WAP Push message as delivered by the system.
|
||||
* @param {Object} timestamp A timestamp for the message reception time.
|
||||
*
|
||||
* @return {Object} An object holding the contents of the WAP message as
|
||||
* separate fields.
|
||||
*/
|
||||
ParsedMessage.from = function pm_from(message, timestamp) {
|
||||
var parser = new DOMParser();
|
||||
var doc = parser.parseFromString(message.content, 'text/xml');
|
||||
var obj = new ParsedMessage();
|
||||
|
||||
// We don't check for errors as the message has already been validated
|
||||
|
||||
obj.type = message.contentType;
|
||||
obj.sender = message.sender;
|
||||
obj.serviceId = message.serviceId;
|
||||
obj.timestamp = timestamp.toString();
|
||||
|
||||
if (message.contentType === 'text/vnd.wap.si') {
|
||||
// SI message
|
||||
var indicationNode = doc.querySelector('indication');
|
||||
|
||||
// 'href' attribute, optional, string
|
||||
if (indicationNode.hasAttribute('href')) {
|
||||
obj.href = indicationNode.getAttribute('href');
|
||||
}
|
||||
|
||||
obj.text = indicationNode.textContent;
|
||||
|
||||
// 'created' attribute, optional, date in ISO 8601 format
|
||||
if (indicationNode.hasAttribute('created')) {
|
||||
var date = new Date(indicationNode.getAttribute('created'));
|
||||
|
||||
obj.created = date.getTime();
|
||||
}
|
||||
|
||||
// 'si-expires' attribute, optional, date in ISO 8601 format
|
||||
if (indicationNode.hasAttribute('si-expires')) {
|
||||
var expiresDate = new Date(indicationNode.getAttribute('si-expires'));
|
||||
|
||||
obj.expires = expiresDate.getTime();
|
||||
}
|
||||
|
||||
/* 'action' attribute, optional, string, defaults to 'signal-medium' when
|
||||
* not present in the incoming message, see WAP-167 7.2 */
|
||||
if (indicationNode.hasAttribute('action')) {
|
||||
obj.action = indicationNode.getAttribute('action');
|
||||
} else {
|
||||
obj.action = 'signal-medium';
|
||||
}
|
||||
|
||||
// 'si-id' attribute, optional, string
|
||||
if (indicationNode.hasAttribute('si-id')) {
|
||||
obj.id = indicationNode.getAttribute('si-id');
|
||||
} else if (obj.action === 'delete') {
|
||||
/* If the message has a 'delete' action but no 'si-id' field than it's
|
||||
* malformed and should be immediately discarded, see WAP-167 6.2 */
|
||||
return null;
|
||||
} else if (obj.href) {
|
||||
/* If the 'si-id' attribute is not specified then its value is
|
||||
* considered to be the same as the value of the 'href' attribute,
|
||||
* see WAP-167 5.2.1 */
|
||||
obj.id = obj.href;
|
||||
}
|
||||
} else if (message.contentType === 'text/vnd.wap.sl') {
|
||||
// SL message
|
||||
var slNode = doc.querySelector('sl');
|
||||
|
||||
// 'href' attribute, always present
|
||||
obj.href = slNode.getAttribute('href');
|
||||
|
||||
/* 'action' attribute, optional, string, defaults to 'execute-low' when
|
||||
* not present in the incoming message, see WAP-168 5.2 */
|
||||
if (slNode.hasAttribute('action')) {
|
||||
obj.action = slNode.getAttribute('action');
|
||||
} else {
|
||||
obj.action = 'execute-low';
|
||||
}
|
||||
} else if (message.contentType === 'text/vnd.wap.connectivity-xml') {
|
||||
// Client provisioning (CP) message
|
||||
obj.provisioning = Provisioning.fromMessage(message);
|
||||
|
||||
// Security information is mandatory for the application. The application
|
||||
// must discard any message with no security information.
|
||||
if (!obj.provisioning.authInfo) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var authInfo = obj.provisioning.authInfo;
|
||||
// Let's discard any message that was not successfully authenticated by
|
||||
// gecko.
|
||||
if (authInfo.checked && !authInfo.pass) {
|
||||
return null;
|
||||
}
|
||||
|
||||
obj.text = 'cp-message-received';
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
return obj;
|
||||
};
|
||||
|
||||
/**
|
||||
* Loads the message corresponding to the specified timestamp from the
|
||||
* database. Returns a promise that resolves to the message. If the message
|
||||
* is not present the promise will be rejected.
|
||||
*
|
||||
* @param {Number} timestamp The timestamp of the message we want to
|
||||
* retrieve.
|
||||
*
|
||||
* @returns {Object} A promise for this operation.
|
||||
*/
|
||||
ParsedMessage.load = function pm_load(timestamp) {
|
||||
return MessageDB.retrieve(timestamp).then(function(message) {
|
||||
return new ParsedMessage(message);
|
||||
});
|
||||
};
|
||||
|
||||
exports.ParsedMessage = ParsedMessage;
|
||||
})(window);
|
||||
24
gaia/apps/wappush/js/provisioning.js
Executable file
24
gaia/apps/wappush/js/provisioning.js
Executable file
|
|
@ -0,0 +1,24 @@
|
|||
/* -*- Mode: js; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
|
||||
(function(exports) {
|
||||
'use strict';
|
||||
|
||||
function Provisioning(obj) {
|
||||
if (obj) {
|
||||
for (var key in obj) {
|
||||
this[key] = obj[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Provisioning.fromMessage = function p_fromMessage(message) {
|
||||
var obj = new Provisioning();
|
||||
|
||||
obj.provisioningDoc = message.content;
|
||||
obj.authInfo = message.authInfo;
|
||||
return obj;
|
||||
};
|
||||
|
||||
exports.Provisioning = Provisioning;
|
||||
})(window);
|
||||
88
gaia/apps/wappush/js/si_sl_screen_helper.js
Executable file
88
gaia/apps/wappush/js/si_sl_screen_helper.js
Executable file
|
|
@ -0,0 +1,88 @@
|
|||
/* global LinkActionHandler, Utils, WapPushManager */
|
||||
|
||||
/* exported SiSlScreenHelper */
|
||||
|
||||
'use strict';
|
||||
|
||||
var SiSlScreenHelper = (function() {
|
||||
/** Screen node */
|
||||
var screen = null;
|
||||
|
||||
/** Header node */
|
||||
var header = null;
|
||||
|
||||
/** Title of the message, usually holds the sender's number */
|
||||
var title = null;
|
||||
|
||||
/** Message container */
|
||||
var container = null;
|
||||
|
||||
/** Message text */
|
||||
var text = null;
|
||||
|
||||
/** Message link */
|
||||
var link = null;
|
||||
|
||||
function sssh_init() {
|
||||
// Retrieve the various page elements
|
||||
title = document.getElementById('title-si-sl');
|
||||
screen = document.getElementById('si-sl-screen');
|
||||
header = document.getElementById('header-si-sl');
|
||||
|
||||
container = screen.querySelector('.container');
|
||||
|
||||
text = container.querySelector('p');
|
||||
link = container.querySelector('a');
|
||||
|
||||
// Event handlers
|
||||
header.addEventListener('action', sssh_onClose);
|
||||
link.addEventListener(
|
||||
'click',
|
||||
LinkActionHandler.onClick.bind(LinkActionHandler)
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
function sssh_populateScreen(message) {
|
||||
screen.hidden = false;
|
||||
|
||||
// Populate the message
|
||||
if (message && !message.isExpired()) {
|
||||
var l10nTitle = Utils.prepareMessageTitle(message);
|
||||
|
||||
navigator.mozL10n.setAttributes(title, l10nTitle.id, l10nTitle.args);
|
||||
text.textContent = message.text;
|
||||
link.textContent = message.href;
|
||||
link.href = message.href;
|
||||
link.dataset.url = message.href;
|
||||
} else {
|
||||
/* If we couldn't retrieve the message then it means that the
|
||||
* message has been expired before it was displayed. */
|
||||
navigator.mozL10n.setAttributes(title, 'wap-push-message');
|
||||
navigator.mozL10n.setAttributes(text, 'this-message-has-expired');
|
||||
link.textContent = '';
|
||||
link.href = '';
|
||||
link.dataset.url = '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the application
|
||||
*/
|
||||
function sssh_onClose() {
|
||||
WapPushManager.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide the SI/SL screen helper
|
||||
*/
|
||||
function sssh_hide() {
|
||||
screen.hidden = true;
|
||||
}
|
||||
|
||||
return {
|
||||
init: sssh_init,
|
||||
hide: sssh_hide,
|
||||
populateScreen: sssh_populateScreen
|
||||
};
|
||||
})();
|
||||
8
gaia/apps/wappush/js/startup.js
Executable file
8
gaia/apps/wappush/js/startup.js
Executable file
|
|
@ -0,0 +1,8 @@
|
|||
/* -*- Mode: js; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
|
||||
/* global WapPushManager */
|
||||
|
||||
'use strict';
|
||||
|
||||
WapPushManager.init();
|
||||
171
gaia/apps/wappush/js/store.js
Executable file
171
gaia/apps/wappush/js/store.js
Executable file
|
|
@ -0,0 +1,171 @@
|
|||
/* -*- Mode: js; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
|
||||
/* exported StoreProvisioning */
|
||||
|
||||
/* global uuid */
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* The StoreProvisioning singleton is a database helper in charge of storing the
|
||||
* APNs into the settings database.
|
||||
*
|
||||
*/
|
||||
var StoreProvisioning = (function() {
|
||||
var CP_APN_KEY = 'ril.data.cp.apns';
|
||||
|
||||
var MCC_KEY = 'operatorvariant.mcc';
|
||||
|
||||
var MNC_KEY = 'operatorvariant.mnc';
|
||||
|
||||
var mccMncCodes = { mcc: '000', mnc: '00' };
|
||||
|
||||
/**
|
||||
* Loads the operator numeric value (MCC and MNC codes) stored into the
|
||||
* settings database. The APNs are stored relaying on these codes.
|
||||
*
|
||||
* @param {Number} iccCardIndex Index of the card on which the message was
|
||||
* received. We are going to add the apns to
|
||||
* the right card.
|
||||
* @param {function} callback It will be called once the work is done (only
|
||||
* useful for unit testing). This function doesn't
|
||||
* any parameter.
|
||||
*/
|
||||
function sp_getMccMncCodes(iccCardIndex, callback) {
|
||||
var settings = navigator.mozSettings;
|
||||
if (!settings) {
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
iccCardIndex = iccCardIndex || 0;
|
||||
|
||||
var transaction = navigator.mozSettings.createLock();
|
||||
var mccRequest = transaction.get(MCC_KEY);
|
||||
mccRequest.onsuccess = function() {
|
||||
var mccs = mccRequest.result[MCC_KEY];
|
||||
if (!mccs || !Array.isArray(mccs) || !mccs[iccCardIndex]) {
|
||||
mccMncCodes.mcc = '000';
|
||||
} else {
|
||||
mccMncCodes.mcc = mccs[iccCardIndex];
|
||||
}
|
||||
var mncRequest = transaction.get(MNC_KEY);
|
||||
mncRequest.onsuccess = function() {
|
||||
var mncs = mncRequest.result[MNC_KEY];
|
||||
if (!mncs || !Array.isArray(mncs) || !mncs[iccCardIndex]) {
|
||||
mccMncCodes.mnc = '00';
|
||||
} else {
|
||||
mccMncCodes.mnc = mncs[iccCardIndex];
|
||||
}
|
||||
if (callback) {
|
||||
callback(mccMncCodes.mcc, mccMncCodes.mnc);
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores the APNs into the settings database.
|
||||
*
|
||||
* @param {Array} parameters The array containing the APNs.
|
||||
* @param {Number} iccCardIndex Index of the card on which the message was
|
||||
* received.
|
||||
* @param {function} callback It will be called once the work is done (only
|
||||
* useful for unit testing). This function doesn't
|
||||
* accetp any parameter.
|
||||
*/
|
||||
function sp_provision(parameters, iccCardIndex, callback) {
|
||||
var existingApns = null;
|
||||
var newApns = {};
|
||||
|
||||
// Add an unique id to identify APNs with the same carrier name
|
||||
for (var i = 0; i < parameters.length; i++) {
|
||||
parameters[i]._id = uuid();
|
||||
}
|
||||
|
||||
var settings = navigator.mozSettings;
|
||||
if (!settings) {
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
return;
|
||||
}
|
||||
sp_getMccMncCodes(iccCardIndex, function sp_getMccMncCodesCb() {
|
||||
var transaction = navigator.mozSettings.createLock();
|
||||
var load = transaction.get(CP_APN_KEY);
|
||||
load.onsuccess = function onsuccessCb() {
|
||||
existingApns = load.result[CP_APN_KEY];
|
||||
var data = {}, mcc = mccMncCodes.mcc, mnc = mccMncCodes.mnc;
|
||||
|
||||
if (!existingApns) {
|
||||
newApns[mcc] = {};
|
||||
newApns[mcc][mnc] = parameters;
|
||||
data[CP_APN_KEY] = newApns;
|
||||
} else {
|
||||
if (!existingApns[mcc]) {
|
||||
existingApns[mcc] = {};
|
||||
}
|
||||
if (!existingApns[mcc][mnc]) {
|
||||
existingApns[mcc][mnc] = [];
|
||||
}
|
||||
// TODO: Should we handle possible duplicated APNs?
|
||||
existingApns[mcc][mnc] = existingApns[mcc][mnc].concat(parameters);
|
||||
data[CP_APN_KEY] = existingApns;
|
||||
}
|
||||
transaction.set(data);
|
||||
|
||||
iccCardIndex = iccCardIndex || 0;
|
||||
var request = transaction.get('ril.data.apnSettings');
|
||||
request.onsuccess = function() {
|
||||
var apnSettings = request.result['ril.data.apnSettings'];
|
||||
if (!apnSettings || !Array.isArray(apnSettings)) {
|
||||
apnSettings = [[], []];
|
||||
}
|
||||
|
||||
var apnList = apnSettings[iccCardIndex];
|
||||
parameters.forEach(function(apn) {
|
||||
var apnEnabled = false;
|
||||
for (var i = 0; i < apnList.length; i++) {
|
||||
if (apnList[i].types[0] === apn.types[0]) {
|
||||
apnList[i] = apn;
|
||||
apnEnabled = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!apnEnabled) {
|
||||
apnList.push(apn);
|
||||
}
|
||||
});
|
||||
|
||||
transaction.set({
|
||||
'ril.data.apnSettings': apnSettings,
|
||||
'apn.selections': null
|
||||
});
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
request.onerror = function onError() {
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
};
|
||||
load.onerror = function onerrorCb() {
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
getMccMncCodes: sp_getMccMncCodes,
|
||||
provision: sp_provision
|
||||
};
|
||||
})();
|
||||
56
gaia/apps/wappush/js/utils.js
Executable file
56
gaia/apps/wappush/js/utils.js
Executable file
|
|
@ -0,0 +1,56 @@
|
|||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
|
||||
(function(exports) {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Various utility functions mainly used to process strings and URLs
|
||||
*/
|
||||
var Utils = {
|
||||
/**
|
||||
* Retrieves the parameters from an URL and forms an object with them
|
||||
*
|
||||
* @param {String} input A string holding the parameters attached to an URL.
|
||||
*
|
||||
* @return {Object} An object built using the parameters.
|
||||
*/
|
||||
deserializeParameters: function ut_deserializeParameters(input) {
|
||||
var rparams = /([^?=&]+)(?:=([^&]*))?/g;
|
||||
var parsed = {};
|
||||
|
||||
input.replace(rparams, function($0, $1, $2) {
|
||||
parsed[$1] = decodeURIComponent($2);
|
||||
});
|
||||
|
||||
return parsed;
|
||||
},
|
||||
|
||||
/**
|
||||
* Generate message title according from message sender and the
|
||||
* serviceId if there are multiple SIMs.
|
||||
*
|
||||
* @param {Object} The message from which the title will be generated.
|
||||
*/
|
||||
prepareMessageTitle: function ut_prepareMessageTitle(message) {
|
||||
if (navigator.mozIccManager &&
|
||||
navigator.mozIccManager.iccIds.length > 1) {
|
||||
return {
|
||||
id: 'message-title-with-sim',
|
||||
args: {
|
||||
id: +message.serviceId + 1,
|
||||
title: message.sender
|
||||
}
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
id: 'message-title',
|
||||
args: { title: message.sender }
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
exports.Utils = Utils;
|
||||
|
||||
}(this));
|
||||
461
gaia/apps/wappush/js/wappush.js
Executable file
461
gaia/apps/wappush/js/wappush.js
Executable file
|
|
@ -0,0 +1,461 @@
|
|||
/* global CpScreenHelper, DUMP, MessageDB, Notification, NotificationHelper,
|
||||
ParsedMessage, Promise, SiSlScreenHelper, WhiteList, Utils */
|
||||
|
||||
/* exported WapPushManager */
|
||||
|
||||
(function(exports) {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Handles incoming WAP Push messages and contains the functionality required
|
||||
* to post notifications and display their contents
|
||||
*/
|
||||
var WapPushManager = {
|
||||
init: wpm_init,
|
||||
close: wpm_close,
|
||||
displayWapPushMessage: wpm_displayWapPushMessage,
|
||||
onVisibilityChange: wpm_onVisibilityChange,
|
||||
onWapPushReceived: wpm_onWapPushReceived
|
||||
};
|
||||
|
||||
/** Settings key for enabling/disabling WAP Push messages */
|
||||
var wapPushEnableKey = 'wap.push.enabled';
|
||||
|
||||
/** Enable/disable WAP Push notifications */
|
||||
var wapPushEnabled;
|
||||
|
||||
/** A reference to the app's object */
|
||||
var app;
|
||||
|
||||
/** Timer used to schedule a close operation */
|
||||
var closeTimeout;
|
||||
|
||||
/**
|
||||
* Number of messages that have been received but haven't been fully
|
||||
* processed yet
|
||||
*/
|
||||
var pendingMessages;
|
||||
|
||||
/** Currently displayed SI message (null for SL and CP messages) */
|
||||
var displayedSiMessage;
|
||||
|
||||
/**
|
||||
* Returns a promise used to retrieve the app's own object.
|
||||
*
|
||||
* @return {Object} A promise for the app object.
|
||||
*/
|
||||
function wpm_getApp() {
|
||||
return new Promise(function(resolve, reject) {
|
||||
var req = navigator.mozApps.getSelf();
|
||||
|
||||
req.onsuccess = function wpm_gotApp() {
|
||||
resolve(this.result);
|
||||
};
|
||||
req.onerror = function wpm_getAppError() {
|
||||
reject(this.error);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a promise used to retrieve the configuration of the app.
|
||||
*
|
||||
* @return {Object} A promise for the app configuration.
|
||||
*/
|
||||
function wpm_getConfig() {
|
||||
return new Promise(function(resolve, reject) {
|
||||
var req = navigator.mozSettings.createLock().get(wapPushEnableKey);
|
||||
|
||||
req.onsuccess = function wpm_settingsLockSuccess() {
|
||||
resolve(this.result[wapPushEnableKey]);
|
||||
};
|
||||
req.onerror = function wpm_settingsLockError() {
|
||||
reject(this.error);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the WAP Push manager, this only subscribes to the
|
||||
* wappush-received message handler at the moment.
|
||||
*
|
||||
* @return {Object} A promise that will be fullfilled once the component has
|
||||
* been fully initialized.
|
||||
*/
|
||||
function wpm_init() {
|
||||
// Reset the internal state to default values.
|
||||
wapPushEnabled = true;
|
||||
app = null;
|
||||
closeTimeout = null;
|
||||
pendingMessages = 0;
|
||||
displayedSiMessage = null;
|
||||
|
||||
// Listen to settings changes right away
|
||||
navigator.mozSettings.addObserver(wapPushEnableKey, wpm_onSettingsChange);
|
||||
|
||||
// Get the app object and configuration
|
||||
var promise = Promise.all([
|
||||
wpm_getApp(), wpm_getConfig(), WhiteList.init()
|
||||
]);
|
||||
|
||||
promise = promise.then(function(values) {
|
||||
app = values[0];
|
||||
wapPushEnabled = values[1];
|
||||
|
||||
// Init screen helpers
|
||||
SiSlScreenHelper.init();
|
||||
CpScreenHelper.init();
|
||||
|
||||
// Register event and message handlers only after initialization is done
|
||||
MessageDB.on('new', wpm_onNew);
|
||||
MessageDB.on('update', wpm_onUpdate);
|
||||
MessageDB.on('discard', wpm_onDiscard);
|
||||
MessageDB.on('delete', wpm_onDelete);
|
||||
|
||||
document.addEventListener('visibilitychange', wpm_onVisibilityChange);
|
||||
window.navigator.mozSetMessageHandler('notification', wpm_onNotification);
|
||||
window.navigator.mozSetMessageHandler('wappush-received',
|
||||
wpm_onWapPushReceived);
|
||||
}).catch(function(error) {
|
||||
// If we encountered an error don't process messages
|
||||
wapPushEnabled = false;
|
||||
var message = error.message || 'Unknown error';
|
||||
console.error('Could not initialize the WAP push manager: ', message);
|
||||
throw error;
|
||||
});
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler invoked when the 'wap.push.enabled' option changes.
|
||||
*
|
||||
* @param {Object} A settings object holding the new value.
|
||||
*/
|
||||
function wpm_onSettingsChange(v) {
|
||||
wapPushEnabled = v.settingValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the application whenever it is hidden
|
||||
*/
|
||||
function wpm_onVisibilityChange() {
|
||||
if (document.hidden) {
|
||||
wpm_close();
|
||||
} else {
|
||||
window.clearTimeout(closeTimeout);
|
||||
closeTimeout = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Establish if we must show this message or not; the message is shown only
|
||||
* if WAP Push functionality is enabled, the sender's MSISDN is whitelisted
|
||||
* or whitelisting is disabled
|
||||
*
|
||||
* @param {Object} message A parsed WAP Push message.
|
||||
*
|
||||
* @return {Boolean} true if the message should be displayed, false otherwise.
|
||||
*/
|
||||
function wpm_shouldDisplayMessage(message) {
|
||||
if (!wapPushEnabled || (message === null)) {
|
||||
/* WAP push functionality is either completely disabled, ignore it. */
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((message.type !== 'text/vnd.wap.connectivity-xml') &&
|
||||
(!WhiteList.has(message.sender) || message.isExpired())) {
|
||||
/* The message isn't a provisioning message and comes from a non
|
||||
* white-listed MSISDN or it has already been expired, ignore it. */
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a notification for the provided message
|
||||
*
|
||||
* @param {Object} message The message the user needs to be notified of
|
||||
*
|
||||
* @return {Object} A promise that is resolved once the notification has been
|
||||
* sent and its events hooked up properly.
|
||||
*/
|
||||
function wpm_sendNotification(message) {
|
||||
var iconURL = NotificationHelper.getIconURI(app);
|
||||
|
||||
/* Build the notification's text, for text/vnd.wap.connectivity-xml
|
||||
* messages this needs to be localized. */
|
||||
var body;
|
||||
|
||||
if (message.type === 'text/vnd.wap.connectivity-xml') {
|
||||
body = message.text; // The text will be localized
|
||||
} else {
|
||||
body = {
|
||||
id: (message.type === 'text/vnd.wap.si') ? 'si-message-body'
|
||||
: 'sl-message-body',
|
||||
args: {
|
||||
text: message.text || '', // The text won't be localized
|
||||
url: message.href
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
var title = Utils.prepareMessageTitle(message);
|
||||
|
||||
var options = {
|
||||
icon: iconURL,
|
||||
bodyL10n: body,
|
||||
tag: message.getUniqueId()
|
||||
};
|
||||
|
||||
return NotificationHelper.send(title, options)
|
||||
.then(function(notification) {
|
||||
notification.addEventListener('click',
|
||||
function wpm_onNotificationClick(event) {
|
||||
wpm_displayWapPushMessage(event.target.tag);
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for the wappush-received system messages, stores the message into
|
||||
* the internal database and posts a notification which can be used to
|
||||
* display the message. This method returns a promise which is currently used
|
||||
* only when testing. It is also exposed for testing reasons but shouldn't be
|
||||
* used outside of this file.
|
||||
*
|
||||
* @param {Object} wapMessage The WAP Push message as provided by the system.
|
||||
*
|
||||
* @return {Object} A promise that is resolved when the method executed
|
||||
* successfully or rejected with the error code that caused the
|
||||
* method to fail.
|
||||
*/
|
||||
function wpm_onWapPushReceived(wapMessage) {
|
||||
DUMP('Received a message: ', wapMessage);
|
||||
pendingMessages++;
|
||||
|
||||
var message = ParsedMessage.from(wapMessage, Date.now());
|
||||
|
||||
if (!wpm_shouldDisplayMessage(message)) {
|
||||
DUMP('The message will not be displayed');
|
||||
wpm_finish();
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return message.save().catch(error => {
|
||||
var message = error.message || 'Unknown error';
|
||||
console.log('Could not add a message to the database: ' + message + '\n');
|
||||
wpm_finish();
|
||||
throw error;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the contents of a WAP Push message
|
||||
*
|
||||
* @param {Object} message The notification event.
|
||||
*/
|
||||
function wpm_onNotification(message) {
|
||||
if (!message.clicked) {
|
||||
return;
|
||||
}
|
||||
|
||||
wpm_displayWapPushMessage(message.tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reacts to a new message being added to the database by showing a
|
||||
* notification or directly showing the message if required by the action
|
||||
* field.
|
||||
*
|
||||
* @param {Object} obj The new message in untyped form (i.e. JSON)
|
||||
*/
|
||||
function wpm_onNew(obj) {
|
||||
DUMP('A new message added to the DB');
|
||||
|
||||
var message = new ParsedMessage(obj);
|
||||
|
||||
if (message.action === 'signal-high' ||
|
||||
message.action === 'execute-high')
|
||||
{
|
||||
/* We just decrease the number of pending messages instead of invoking
|
||||
* wpm_finish() since we don't want to start the close procedure. */
|
||||
pendingMessages--;
|
||||
wpm_displayWapPushMessage(message.getUniqueId());
|
||||
} else {
|
||||
wpm_sendNotification(message).then(() => wpm_finish());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reacts to a message being updated in the database by updating the
|
||||
* notification and possibly the message being shown to the user.
|
||||
*
|
||||
* @param {Object} obj The new message in untyped form (i.e. JSON)
|
||||
*/
|
||||
function wpm_onUpdate(obj) {
|
||||
DUMP('A message was updated in the DB');
|
||||
|
||||
var message = new ParsedMessage(obj);
|
||||
|
||||
// Update the message if we're currently displaying it
|
||||
if (displayedSiMessage &&
|
||||
displayedSiMessage.getUniqueId() === message.getUniqueId()) {
|
||||
displayedSiMessage = message;
|
||||
SiSlScreenHelper.populateScreen(message);
|
||||
}
|
||||
|
||||
// This updates the existing notification
|
||||
wpm_sendNotification(message).then(() => wpm_finish());
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the notifications for messages that have been deleted and closes
|
||||
* the app if the message being viewed has been deleted.
|
||||
*
|
||||
* @param {Object} obj The deleted message in untyped form (i.e. JSON)
|
||||
*/
|
||||
function wpm_onDelete(obj) {
|
||||
DUMP('A message was deleted');
|
||||
|
||||
var message = new ParsedMessage(obj);
|
||||
|
||||
// Close the application if we're displaying the deleted message
|
||||
if (displayedSiMessage &&
|
||||
displayedSiMessage.getUniqueId() === message.getUniqueId()) {
|
||||
wpm_close();
|
||||
}
|
||||
|
||||
// Remove the notification associated to the deleted message
|
||||
wpm_clearNotifications(+message.getUniqueId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints a debug statement when a message has been discarded, no further
|
||||
* action required.
|
||||
*/
|
||||
function wpm_onDiscard() {
|
||||
DUMP('The message was discarded');
|
||||
wpm_finish();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a WAP Push message from the database and displays it
|
||||
*
|
||||
* @param {String} id The message id as a string.
|
||||
* @return {Promise} A promise that resolves once the message has been
|
||||
* displayed and the associated notification cleared.
|
||||
*/
|
||||
function wpm_displayWapPushMessage(id) {
|
||||
DUMP('Displaying message ' + id);
|
||||
|
||||
// Clear the close timer as the application will soon become visible
|
||||
app.launch();
|
||||
window.clearTimeout(closeTimeout);
|
||||
closeTimeout = null;
|
||||
|
||||
return ParsedMessage.load(id).then(
|
||||
function wpm_loadResolved(message) {
|
||||
switch (message.type) {
|
||||
case 'text/vnd.wap.si':
|
||||
displayedSiMessage = message;
|
||||
CpScreenHelper.hide();
|
||||
|
||||
if (message.isExpired()) {
|
||||
// Notify the user that the message has expired
|
||||
SiSlScreenHelper.populateScreen();
|
||||
} else {
|
||||
SiSlScreenHelper.populateScreen(message);
|
||||
}
|
||||
|
||||
return wpm_clearNotifications(id);
|
||||
|
||||
case 'text/vnd.wap.sl':
|
||||
displayedSiMessage = null;
|
||||
CpScreenHelper.hide();
|
||||
SiSlScreenHelper.populateScreen(message);
|
||||
return wpm_clearNotifications(id);
|
||||
|
||||
case 'text/vnd.wap.connectivity-xml':
|
||||
displayedSiMessage = null;
|
||||
SiSlScreenHelper.hide();
|
||||
CpScreenHelper.showConfirmInstallationDialog(message);
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
).catch(function(error) {
|
||||
var message = error.message || 'Unknown error';
|
||||
console.error('Could not retrieve the message: ', message);
|
||||
throw error;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove notifications for a given tag.
|
||||
*
|
||||
* @return {Promise} A promise that gets resolved once the notifications have
|
||||
* been cleared and rejects in case of error.
|
||||
*/
|
||||
function wpm_clearNotifications(tag) {
|
||||
return Notification.get({tag: tag}).then(
|
||||
function onSuccess(notifications) {
|
||||
for (var i = 0; i < notifications.length; i++) {
|
||||
notifications[i].close();
|
||||
}
|
||||
},
|
||||
function onError(error) {
|
||||
var message = error.message || 'Unknown error';
|
||||
console.error('Could not get notification: ', message);
|
||||
throw error;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks a message as processed and close the application if no more messages
|
||||
* are present and the application is not visible.
|
||||
*/
|
||||
function wpm_finish() {
|
||||
pendingMessages--;
|
||||
|
||||
if (document.hidden) {
|
||||
wpm_close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the application. Whenever this function is called it
|
||||
* starts a timer that will eventually close the application when no more
|
||||
* messages are being processed. Calling close multiple times is safe as only
|
||||
* one timer can be active at a time so subsequent calls will effectively be
|
||||
* no-ops.
|
||||
*/
|
||||
function wpm_close() {
|
||||
if (closeTimeout !== null) {
|
||||
// We're already trying to close the app
|
||||
return;
|
||||
}
|
||||
|
||||
/* We do not close the app immediately, instead we spin the event loop once
|
||||
* in the hope of catching pending messages that have been sent to the app
|
||||
* and have not yet been processed. If some messages are pending we'll
|
||||
* reschedule the close until they've all been processed. */
|
||||
closeTimeout = window.setTimeout(function wpm_delayedClose() {
|
||||
if (pendingMessages > 0) {
|
||||
// Pending messages were received since we set this timer
|
||||
closeTimeout = window.setTimeout(wpm_delayedClose, 100);
|
||||
return;
|
||||
}
|
||||
|
||||
/* There's no more pending messages and the application is in the
|
||||
* background, close for real */
|
||||
DUMP('Automatically closing the application');
|
||||
closeTimeout = null;
|
||||
window.close();
|
||||
}, 100);
|
||||
}
|
||||
|
||||
exports.WapPushManager = WapPushManager;
|
||||
})(this);
|
||||
51
gaia/apps/wappush/js/whitelist.js
Executable file
51
gaia/apps/wappush/js/whitelist.js
Executable file
|
|
@ -0,0 +1,51 @@
|
|||
/* -*- Mode: js; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
|
||||
/* global LazyLoader */
|
||||
/* exported WhiteList */
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* MSISDN whitelist, if not empty only the messages sent from the MSISDNs
|
||||
* present in the list will be shown to the user, others will be discarded.
|
||||
*/
|
||||
var WhiteList = {
|
||||
/** An array holding the list of MSISDNs */
|
||||
_whiteList: [],
|
||||
|
||||
/**
|
||||
* Read the whitelist.json file from disk and initialize the whitelist with
|
||||
* it. If the file is not present or empty the whitelist will be empty.
|
||||
*/
|
||||
init: function wl_init() {
|
||||
return LazyLoader.getJSON('js/whitelist.json')
|
||||
.then(
|
||||
(function wl_load(list) {
|
||||
this._whiteList = list;
|
||||
}).bind(this),
|
||||
function () {
|
||||
/**
|
||||
* This reject handler is important here because in case of
|
||||
* whitelist json file missing or empty, getJSON rejects the promise.
|
||||
* If this handler is not present here, the returned promise from
|
||||
* this function will fail for the outside code
|
||||
*/
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Checks if the element is in the whitelist.
|
||||
*
|
||||
* @return {Boolean} true if the element is in the whitelist, always return
|
||||
* true if the whitelist is empty.
|
||||
*/
|
||||
has: function wl_has(value) {
|
||||
if (this._whiteList.length === 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return (this._whiteList.indexOf(value) !== -1);
|
||||
}
|
||||
};
|
||||
18
gaia/apps/wappush/jsdoc.json
Executable file
18
gaia/apps/wappush/jsdoc.json
Executable file
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"name": "wappush",
|
||||
"tags": {
|
||||
"dictionaries": ["jsdoc"]
|
||||
},
|
||||
"source": {
|
||||
"include": ["apps/wappush/js/**/*.js"]
|
||||
},
|
||||
"plugins": [
|
||||
"plugins/markdown",
|
||||
"plugins/summarize"
|
||||
],
|
||||
"opts": {
|
||||
"private": true,
|
||||
"recurse": true,
|
||||
"destination": "docs/wappush"
|
||||
}
|
||||
}
|
||||
30
gaia/apps/wappush/locales/wappush.en-US.properties
Executable file
30
gaia/apps/wappush/locales/wappush.en-US.properties
Executable file
|
|
@ -0,0 +1,30 @@
|
|||
wap-push-message = WAP Push message
|
||||
close = Close
|
||||
accept = Accept
|
||||
quit = Quit
|
||||
cancel = Cancel
|
||||
try-again = Try again
|
||||
finish = Finish
|
||||
sim = SIM {{id}}
|
||||
next = Next
|
||||
done = Done
|
||||
|
||||
this-message-has-expired = This message has expired
|
||||
message-title = {{title}}
|
||||
message-title-with-sim = ({{sim}}) {{title}}
|
||||
si-message-body = {{text}} {{url}}
|
||||
sl-message-body = {{url}}
|
||||
|
||||
cp-pin.placeholder = PIN
|
||||
cp-message-received = Configuration message received
|
||||
cp-container-title = OTA Access Point Configuration
|
||||
cp-accept-help = Configuration message received, tap on “Done” to continue.
|
||||
cp-accept-help-pin = The PIN code should have been provided to you by whomever sent you the configuration message.
|
||||
cp-quit-app-confirm-dialog-message = The message has not been processed yet, do you really want to quit?
|
||||
cp-install-configuration-confirm-dialog-message = Your carrier is sharing configuration information with you. Do you want to install it?
|
||||
cp-auth-failure = PIN is not correct.
|
||||
cp-finish-confirm-dialog-message-apn-saved = APN(s) saved.
|
||||
cp-finish-confirm-dialog-message-invalid-doc = The configuration message you received could not be authenticated against your SIM card, so cannot be used to configure your device.
|
||||
cp-finish-confirm-dialog-message-auth-error = There was a error while authenticating the sender of the message.
|
||||
cp-finish-confirm-dialog-message-auth-unsupported-mechanism = The authentication mechanism is not supported yet.
|
||||
cp-finish-confirm-dialog-message-no-apns = The configuration message you received does not have valid APNs.
|
||||
52
gaia/apps/wappush/manifest.webapp
Executable file
52
gaia/apps/wappush/manifest.webapp
Executable file
|
|
@ -0,0 +1,52 @@
|
|||
{
|
||||
"name": "WAP Push manager",
|
||||
"description": "Receives, filters and displays WAP Push messages",
|
||||
"type": "certified",
|
||||
"role": "system",
|
||||
"launch_path": "/index.html",
|
||||
"developer": {
|
||||
"name": "The Gaia Team",
|
||||
"url": "https://github.com/mozilla-b2g/gaia"
|
||||
},
|
||||
"locales": {
|
||||
"ar": {
|
||||
"name": "WAP Push manager",
|
||||
"description": "Receives, filters and displays WAP Push messages"
|
||||
},
|
||||
"en-US": {
|
||||
"name": "WAP Push manager",
|
||||
"description": "Receives, filters and displays WAP Push messages"
|
||||
},
|
||||
"fr": {
|
||||
"name": "Gestionnaire Push WAP",
|
||||
"description": "Reçoit, filtre et affiche les messages Push WAP"
|
||||
},
|
||||
"zh-TW": {
|
||||
"name": "WAP Push manager",
|
||||
"description": "Receives, filters and displays WAP Push messages"
|
||||
}
|
||||
},
|
||||
"default_locale": "en-US",
|
||||
"icons": {
|
||||
"30": "/style/icons/wappush.png"
|
||||
},
|
||||
"orientation": "default",
|
||||
"messages": [
|
||||
{
|
||||
"notification": "/index.html"
|
||||
},
|
||||
{
|
||||
"wappush-received": "/index.html"
|
||||
}
|
||||
],
|
||||
"permissions": {
|
||||
"themeable": {},
|
||||
"desktop-notification": {},
|
||||
"settings": {
|
||||
"access": "readwrite"
|
||||
},
|
||||
"wappush": {},
|
||||
"mobileconnection": {},
|
||||
"moz-extremely-unstable-and-will-change-webcomponents": {}
|
||||
}
|
||||
}
|
||||
10
gaia/apps/wappush/package.json
Executable file
10
gaia/apps/wappush/package.json
Executable file
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"webide": {
|
||||
"prepackage": {
|
||||
"command": "make",
|
||||
"env": ["APP=wappush"],
|
||||
"cwd": "../.."
|
||||
},
|
||||
"packageDir": "../../build_stage/wappush/"
|
||||
}
|
||||
}
|
||||
BIN
gaia/apps/wappush/style/icons/wappush.png
Executable file
BIN
gaia/apps/wappush/style/icons/wappush.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 1.6 KiB |
86
gaia/apps/wappush/style/wappush.css
Executable file
86
gaia/apps/wappush/style/wappush.css
Executable file
|
|
@ -0,0 +1,86 @@
|
|||
html {
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
html,body {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
margin: 0;
|
||||
color: white;
|
||||
}
|
||||
|
||||
[role=main] {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: white;
|
||||
color: black;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. COMPLEX: Scoped style-sheets within <gaia-header>
|
||||
* trump all other rules. !important is required
|
||||
* for an app to over-ride the default appearance
|
||||
* of content within the custom-element. This may
|
||||
* change when we get `:host` and `:content` selectors
|
||||
* in shadow-dom.
|
||||
*/
|
||||
|
||||
#accept.hidden {
|
||||
display: none !important; /* 1 */
|
||||
}
|
||||
|
||||
#si-sl-screen {
|
||||
height: -moz-calc(100% - 5rem - 5rem);
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
#si-sl-screen .message {
|
||||
font-size: 2rem;
|
||||
padding: 1rem;
|
||||
white-space: pre-line;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
#cp-pin-screen .message {
|
||||
color: #000;
|
||||
padding: 2rem 1.5rem;
|
||||
}
|
||||
|
||||
#cp-pin-screen .message p {
|
||||
margin: 0 0 2rem;
|
||||
font-size: 1.5rem;
|
||||
line-height: 1.8rem;
|
||||
}
|
||||
|
||||
#cp-auth-failure {
|
||||
color: #f00;
|
||||
}
|
||||
|
||||
#cp-details-screen {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
[data-icon]:before {
|
||||
line-height: 6rem;
|
||||
text-align: center;
|
||||
width: 3rem;
|
||||
color: #657073;
|
||||
}
|
||||
|
||||
.view {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
transform: translateX(0 );
|
||||
transition: transform var(--transition-duration);
|
||||
}
|
||||
|
||||
.view.right {
|
||||
transform: translateX(100%);
|
||||
}
|
||||
|
||||
.view.left {
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
27
gaia/apps/wappush/test/build/integration/wappush_test.js
Executable file
27
gaia/apps/wappush/test/build/integration/wappush_test.js
Executable file
|
|
@ -0,0 +1,27 @@
|
|||
'use strict';
|
||||
|
||||
/* jshint node: true, mocha: true */
|
||||
/* global suiteSetup */
|
||||
|
||||
var helper = require('helper');
|
||||
|
||||
suite('Wappush tests', function() {
|
||||
suiteSetup(helper.cleanupWorkspace);
|
||||
teardown(helper.cleanupWorkspace);
|
||||
|
||||
test('APP=wappush make', function(done) {
|
||||
helper.exec('APP=wappush make', function(error, stdout, stderr) {
|
||||
helper.checkError(error, stdout, stderr);
|
||||
|
||||
var zipPath = process.cwd() +
|
||||
'/profile/webapps/wappush.gaiamobile.org/application.zip';
|
||||
var checkList = ['manifest.webapp'];
|
||||
|
||||
checkList.forEach(function(filePath) {
|
||||
helper.checkFilePathInZip(zipPath, filePath);
|
||||
});
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
17
gaia/apps/wappush/test/unit/.jshintrc
Executable file
17
gaia/apps/wappush/test/unit/.jshintrc
Executable file
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"extends": "../../../../.jshintrc",
|
||||
"predef": [
|
||||
"assert",
|
||||
"suite",
|
||||
"test",
|
||||
"setup",
|
||||
"teardown",
|
||||
"suiteSetup",
|
||||
"suiteTeardown",
|
||||
"require",
|
||||
"requireApp",
|
||||
"sinon",
|
||||
"mocha",
|
||||
"loadBodyHTML"
|
||||
]
|
||||
}
|
||||
91
gaia/apps/wappush/test/unit/activity_picker_test.js
Executable file
91
gaia/apps/wappush/test/unit/activity_picker_test.js
Executable file
|
|
@ -0,0 +1,91 @@
|
|||
/* -*- Mode: js; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
|
||||
/* global ActivityPicker, MockL10n, MocksHelper, MozActivity */
|
||||
|
||||
'use strict';
|
||||
|
||||
require('/shared/test/unit/mocks/mock_l10n.js');
|
||||
requireApp('wappush/js/activity_picker.js');
|
||||
requireApp('wappush/test/unit/mock_moz_activity.js');
|
||||
|
||||
var mocksHelperAP = new MocksHelper([
|
||||
'MozActivity'
|
||||
]).init();
|
||||
|
||||
suite('ActivityPicker', function() {
|
||||
var realMozL10n, onsuccess, onerror;
|
||||
|
||||
mocksHelperAP.attachTestHelpers();
|
||||
|
||||
suiteSetup(function() {
|
||||
realMozL10n = navigator.mozL10n;
|
||||
navigator.mozL10n = MockL10n;
|
||||
|
||||
onsuccess = function() {};
|
||||
onerror = function() {};
|
||||
});
|
||||
|
||||
suiteTeardown(function() {
|
||||
navigator.mozL10n = realMozL10n;
|
||||
});
|
||||
|
||||
setup(function() {
|
||||
mocksHelperAP.setup();
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
mocksHelperAP.teardown();
|
||||
});
|
||||
|
||||
suite('url', function() {
|
||||
|
||||
test('url(url) ', function() {
|
||||
ActivityPicker.url('http://mozilla.com');
|
||||
|
||||
assert.deepEqual(MozActivity.calls[0], {
|
||||
name: 'view',
|
||||
data: {
|
||||
type: 'url',
|
||||
url: 'http://mozilla.com'
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test('url(url, success) ', function() {
|
||||
ActivityPicker.url('http://mozilla.com', onsuccess);
|
||||
|
||||
assert.equal(
|
||||
MozActivity.instances[0].onsuccess, onsuccess
|
||||
);
|
||||
|
||||
assert.deepEqual(MozActivity.calls[0], {
|
||||
name: 'view',
|
||||
data: {
|
||||
type: 'url',
|
||||
url: 'http://mozilla.com'
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test('url(url, success, error) ', function() {
|
||||
ActivityPicker.url('http://mozilla.com', onsuccess, onerror);
|
||||
|
||||
assert.equal(
|
||||
MozActivity.instances[0].onsuccess, onsuccess
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
MozActivity.instances[0].onerror, onerror
|
||||
);
|
||||
|
||||
assert.deepEqual(MozActivity.calls[0], {
|
||||
name: 'view',
|
||||
data: {
|
||||
type: 'url',
|
||||
url: 'http://mozilla.com'
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
148
gaia/apps/wappush/test/unit/auth_test.js
Executable file
148
gaia/apps/wappush/test/unit/auth_test.js
Executable file
|
|
@ -0,0 +1,148 @@
|
|||
/* -*- Mode: js; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
|
||||
/* global ProvisioningAuthentication */
|
||||
|
||||
'use strict';
|
||||
|
||||
requireApp('wappush/js/ext/CryptoJSv3.1.2/rollups/hmac-sha1.js');
|
||||
requireApp('wappush/js/auth.js');
|
||||
|
||||
suite('ProvisioningAuthentication >', function() {
|
||||
suite('ProvisioningAuthentication.isDocumentValid()', function() {
|
||||
var USER_PIN = '1234567890';
|
||||
var USER_PIN_AUTH_INFO;
|
||||
|
||||
test('Unknow security mechanism', function() {
|
||||
USER_PIN_AUTH_INFO = {
|
||||
sec: 'FAKEMECHANISM'
|
||||
};
|
||||
assert.throw(
|
||||
function() {
|
||||
ProvisioningAuthentication.isDocumentValid(USER_PIN,
|
||||
USER_PIN_AUTH_INFO);
|
||||
},
|
||||
'cp-finish-confirm-dialog-message-auth-unsupported-mechanism'
|
||||
);
|
||||
});
|
||||
|
||||
test('NETWPIN security mechanism should not be called', function() {
|
||||
USER_PIN_AUTH_INFO = {
|
||||
sec: 'NETWPIN'
|
||||
};
|
||||
assert.throw(
|
||||
function() {
|
||||
ProvisioningAuthentication.isDocumentValid(USER_PIN,
|
||||
USER_PIN_AUTH_INFO);
|
||||
},
|
||||
'cp-finish-confirm-dialog-message-auth-error'
|
||||
);
|
||||
});
|
||||
|
||||
test('USERNETWPIN security mechanism is not supported yet', function() {
|
||||
USER_PIN_AUTH_INFO = {
|
||||
sec: 'USERNETWPIN'
|
||||
};
|
||||
assert.throw(
|
||||
function() {
|
||||
ProvisioningAuthentication.isDocumentValid(USER_PIN,
|
||||
USER_PIN_AUTH_INFO);
|
||||
},
|
||||
'cp-finish-confirm-dialog-message-auth-unsupported-mechanism'
|
||||
);
|
||||
});
|
||||
|
||||
test('USERPINMAC security mechanism is not supported yet', function() {
|
||||
USER_PIN_AUTH_INFO = {
|
||||
sec: 'USERPINMAC'
|
||||
};
|
||||
assert.throw(
|
||||
function() {
|
||||
ProvisioningAuthentication.isDocumentValid(USER_PIN,
|
||||
USER_PIN_AUTH_INFO);
|
||||
},
|
||||
'cp-finish-confirm-dialog-message-auth-unsupported-mechanism'
|
||||
);
|
||||
});
|
||||
|
||||
test('A message with USERPIN security mechanism was authenticated',
|
||||
function() {
|
||||
var wbxml = new Uint8Array([
|
||||
0x03, 0x0B, 0x6A, 0x00, 0x45, 0xC6, 0x56, 0x01,
|
||||
0x87, 0x07, 0x06, 0x03, 0x43, 0x48, 0x54, 0x5F,
|
||||
0x65, 0x6D, 0x6F, 0x6D, 0x65, 0x00, 0x01, 0x01,
|
||||
0xC6, 0x00, 0x01, 0x55, 0x01, 0x87, 0x36, 0x00,
|
||||
0x00, 0x06, 0x03, 0x77, 0x32, 0x00, 0x01, 0x87,
|
||||
0x00, 0x01, 0x39, 0x00, 0x00, 0x06, 0x03, 0x57,
|
||||
0x50, 0x52, 0x4F, 0x58, 0x59, 0x00, 0x01, 0x87,
|
||||
0x07, 0x06, 0x03, 0x43, 0x48, 0x54, 0x5F, 0x65,
|
||||
0x6D, 0x6F, 0x6D, 0x65, 0x00, 0x01, 0xC6, 0x00,
|
||||
0x01, 0x59, 0x01, 0x87, 0x3A, 0x00, 0x00, 0x06,
|
||||
0x03, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x2F, 0x2F,
|
||||
0x77, 0x61, 0x70, 0x2E, 0x65, 0x6D, 0x6F, 0x6D,
|
||||
0x65, 0x2E, 0x6E, 0x65, 0x74, 0x2F, 0x00, 0x01,
|
||||
0x87, 0x07, 0x06, 0x03, 0x43, 0x48, 0x54, 0x5F,
|
||||
0x65, 0x6D, 0x6F, 0x6D, 0x65, 0x00, 0x01, 0x87,
|
||||
0x1C, 0x01, 0x01, 0x01, 0xC6, 0x00, 0x01, 0x55,
|
||||
0x01, 0x87, 0x36, 0x00, 0x00, 0x06, 0x03, 0x77,
|
||||
0x34, 0x00, 0x01, 0x87, 0x00, 0x01, 0x39, 0x00,
|
||||
0x00, 0x06, 0x03, 0x4D, 0x50, 0x52, 0x4F, 0x58,
|
||||
0x59, 0x00, 0x01, 0x87, 0x00, 0x01, 0x34, 0x00,
|
||||
0x00, 0x06, 0x03, 0x68, 0x74, 0x74, 0x70, 0x3A,
|
||||
0x2F, 0x2F, 0x6D, 0x6D, 0x73, 0x3A, 0x38, 0x30,
|
||||
0x30, 0x32, 0x00, 0x01, 0x01, 0xC6, 0x51, 0x01,
|
||||
0x87, 0x15, 0x06, 0x03, 0x57, 0x50, 0x52, 0x4F,
|
||||
0x58, 0x59, 0x00, 0x01, 0x87, 0x07, 0x06, 0x03,
|
||||
0x43, 0x48, 0x54, 0x5F, 0x65, 0x6D, 0x6F, 0x6D,
|
||||
0x65, 0x00, 0x01, 0x87, 0x1C, 0x06, 0x03, 0x68,
|
||||
0x74, 0x74, 0x70, 0x3A, 0x2F, 0x2F, 0x77, 0x61,
|
||||
0x70, 0x2E, 0x65, 0x6D, 0x6F, 0x6D, 0x65, 0x2E,
|
||||
0x6E, 0x65, 0x74, 0x2F, 0x00, 0x01, 0xC6, 0x52,
|
||||
0x01, 0x87, 0x2F, 0x06, 0x03, 0x50, 0x52, 0x4F,
|
||||
0x58, 0x59, 0x31, 0x00, 0x01, 0x87, 0x20, 0x06,
|
||||
0x03, 0x31, 0x30, 0x2E, 0x31, 0x2E, 0x31, 0x2E,
|
||||
0x31, 0x00, 0x01, 0x87, 0x21, 0x06, 0x85, 0x01,
|
||||
0x87, 0x22, 0x06, 0x03, 0x43, 0x48, 0x54, 0x5F,
|
||||
0x65, 0x6D, 0x6F, 0x6D, 0x65, 0x00, 0x01, 0xC6,
|
||||
0x53, 0x01, 0x87, 0x23, 0x06, 0x03, 0x38, 0x30,
|
||||
0x38, 0x30, 0x00, 0x01, 0x01, 0x01, 0x01, 0xC6,
|
||||
0x51, 0x01, 0x87, 0x15, 0x06, 0x03, 0x4D, 0x50,
|
||||
0x52, 0x4F, 0x58, 0x59, 0x00, 0x01, 0x87, 0x07,
|
||||
0x06, 0x03, 0x43, 0x48, 0x54, 0x5F, 0x4D, 0x4D,
|
||||
0x53, 0x00, 0x01, 0xC6, 0x52, 0x01, 0x87, 0x2F,
|
||||
0x06, 0x03, 0x50, 0x52, 0x4F, 0x58, 0x59, 0x32,
|
||||
0x00, 0x01, 0x87, 0x20, 0x06, 0x03, 0x31, 0x30,
|
||||
0x2E, 0x31, 0x2E, 0x31, 0x2E, 0x31, 0x00, 0x01,
|
||||
0x87, 0x21, 0x06, 0x85, 0x01, 0x87, 0x22, 0x06,
|
||||
0x03, 0x43, 0x48, 0x54, 0x5F, 0x4D, 0x4D, 0x53,
|
||||
0x00, 0x01, 0xC6, 0x53, 0x01, 0x87, 0x23, 0x06,
|
||||
0x03, 0x38, 0x30, 0x38, 0x30, 0x00, 0x01, 0x01,
|
||||
0x01, 0x01, 0xC6, 0x55, 0x01, 0x87, 0x11, 0x06,
|
||||
0x03, 0x43, 0x48, 0x54, 0x5F, 0x65, 0x6D, 0x6F,
|
||||
0x6D, 0x65, 0x00, 0x01, 0x87, 0x07, 0x06, 0x03,
|
||||
0x43, 0x48, 0x54, 0x5F, 0x65, 0x6D, 0x6F, 0x6D,
|
||||
0x65, 0x00, 0x01, 0x87, 0x10, 0x06, 0xAB, 0x01,
|
||||
0x87, 0x08, 0x06, 0x03, 0x65, 0x6D, 0x6F, 0x6D,
|
||||
0x65, 0x00, 0x01, 0x87, 0x09, 0x06, 0x89, 0x01,
|
||||
0x01, 0xC6, 0x55, 0x01, 0x87, 0x11, 0x06, 0x03,
|
||||
0x43, 0x48, 0x54, 0x5F, 0x4D, 0x4D, 0x53, 0x00,
|
||||
0x01, 0x87, 0x07, 0x06, 0x03, 0x43, 0x48, 0x54,
|
||||
0x5F, 0x4D, 0x4D, 0x53, 0x00, 0x01, 0x87, 0x10,
|
||||
0x06, 0xAB, 0x01, 0x87, 0x08, 0x06, 0x03, 0x65,
|
||||
0x6D, 0x6F, 0x6D, 0x65, 0x00, 0x01, 0x87, 0x09,
|
||||
0x06, 0x89, 0x01, 0x01, 0x01
|
||||
]);
|
||||
|
||||
USER_PIN = '0000';
|
||||
USER_PIN_AUTH_INFO = {
|
||||
sec: 'USERPIN',
|
||||
mac: 'AA2DC41FC48AEEF3FED7351B1EE704461A8894D4',
|
||||
data: wbxml
|
||||
};
|
||||
assert.isTrue(
|
||||
ProvisioningAuthentication.isDocumentValid(USER_PIN,
|
||||
USER_PIN_AUTH_INFO)
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
75
gaia/apps/wappush/test/unit/link_action_handler_test.js
Executable file
75
gaia/apps/wappush/test/unit/link_action_handler_test.js
Executable file
|
|
@ -0,0 +1,75 @@
|
|||
/* -*- Mode: js; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
|
||||
/* global ActivityPicker, LinkActionHandler, MockL10n, MocksHelper */
|
||||
|
||||
'use strict';
|
||||
|
||||
require('/shared/test/unit/mocks/mock_l10n.js');
|
||||
requireApp('wappush/js/link_action_handler.js');
|
||||
requireApp('wappush/test/unit/mock_activity_picker.js');
|
||||
requireApp('wappush/test/unit/mock_moz_activity.js');
|
||||
|
||||
var mocksHelperLAH = new MocksHelper([
|
||||
'ActivityPicker',
|
||||
'MozActivity'
|
||||
]).init();
|
||||
|
||||
suite('LinkActionHandler', function() {
|
||||
var realMozL10n, events;
|
||||
|
||||
mocksHelperLAH.attachTestHelpers();
|
||||
|
||||
suiteSetup(function() {
|
||||
realMozL10n = navigator.mozL10n;
|
||||
navigator.mozL10n = MockL10n;
|
||||
|
||||
events = {
|
||||
url: {
|
||||
target: {
|
||||
dataset: {
|
||||
action: 'url-link',
|
||||
url: 'http://mozilla.com'
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
suiteTeardown(function() {
|
||||
navigator.mozL10n = realMozL10n;
|
||||
});
|
||||
|
||||
setup(function() {
|
||||
|
||||
Object.keys(events).forEach(function(type) {
|
||||
events[type].preventDefault = this.sinon.spy();
|
||||
events[type].stopPropagation = this.sinon.spy();
|
||||
}, this);
|
||||
|
||||
mocksHelperLAH.setup();
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
mocksHelperLAH.teardown();
|
||||
});
|
||||
|
||||
suite('onClick', function() {
|
||||
|
||||
setup(function() {
|
||||
mocksHelperLAH.setup();
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
LinkActionHandler.reset();
|
||||
mocksHelperLAH.teardown();
|
||||
});
|
||||
|
||||
test('url-link ', function() {
|
||||
LinkActionHandler.onClick(events.url);
|
||||
|
||||
assert.ok(ActivityPicker.url.called);
|
||||
assert.equal(ActivityPicker.url.calledWith[0], 'http://mozilla.com');
|
||||
});
|
||||
});
|
||||
});
|
||||
327
gaia/apps/wappush/test/unit/messagedb_test.js
Executable file
327
gaia/apps/wappush/test/unit/messagedb_test.js
Executable file
|
|
@ -0,0 +1,327 @@
|
|||
/* global MessageDB */
|
||||
|
||||
'use strict';
|
||||
|
||||
require('/shared/js/event_dispatcher.js');
|
||||
|
||||
require('/js/messagedb.js');
|
||||
|
||||
suite('MessageDB', function() {
|
||||
var messages = {
|
||||
current: {
|
||||
type: 'text/vnd.wap.si',
|
||||
sender: '+31641600986',
|
||||
serviceId: 0,
|
||||
timestamp: '1',
|
||||
href: 'http://www.mozilla.org',
|
||||
id: 'gaia-test@mozilla.org',
|
||||
created: (new Date('2013-09-03T10:35:33Z')).getTime(),
|
||||
action: 'signal-medium',
|
||||
text: 'check this out'
|
||||
},
|
||||
older: {
|
||||
type: 'text/vnd.wap.si',
|
||||
sender: '+31641600986',
|
||||
serviceId: 0,
|
||||
timestamp: '2',
|
||||
href: 'http://www.mozilla.org',
|
||||
id: 'gaia-test@mozilla.org',
|
||||
created: (new Date('2013-09-03T10:35:32Z')).getTime(),
|
||||
action: 'signal-medium',
|
||||
text: 'older message'
|
||||
},
|
||||
newer: {
|
||||
type: 'text/vnd.wap.si',
|
||||
sender: '+31641600986',
|
||||
serviceId: 0,
|
||||
timestamp: '3',
|
||||
href: 'http://www.mozilla.org',
|
||||
id: 'gaia-test@mozilla.org',
|
||||
created: (new Date('2013-09-03T10:35:34Z')).getTime(),
|
||||
action: 'signal-medium',
|
||||
text: 'newer message'
|
||||
},
|
||||
delete: {
|
||||
type: 'text/vnd.wap.si',
|
||||
sender: '+31641600986',
|
||||
serviceId: 0,
|
||||
timestamp: '3',
|
||||
href: 'http://www.mozilla.org',
|
||||
id: 'gaia-test@mozilla.org',
|
||||
action: 'delete',
|
||||
text: ''
|
||||
},
|
||||
old_delete: {
|
||||
type: 'text/vnd.wap.si',
|
||||
sender: '+31641600986',
|
||||
serviceId: 0,
|
||||
timestamp: '4',
|
||||
href: 'http://www.mozilla.org',
|
||||
id: 'gaia-test@mozilla.org',
|
||||
created: (new Date('2013-09-03T10:35:32Z')).getTime(),
|
||||
action: 'delete',
|
||||
text: ''
|
||||
},
|
||||
low: {
|
||||
type: 'text/vnd.wap.si',
|
||||
sender: '+31641600986',
|
||||
serviceId: 0,
|
||||
timestamp: '5',
|
||||
href: 'http://www.mozilla.org',
|
||||
id: 'http://www.mozilla.org',
|
||||
action: 'signal-low',
|
||||
text: 'check this out'
|
||||
},
|
||||
medium: {
|
||||
type: 'text/vnd.wap.si',
|
||||
sender: '+31641600986',
|
||||
serviceId: 0,
|
||||
timestamp: '6',
|
||||
href: 'http://www.mozilla.org',
|
||||
id: 'http://www.mozilla.org',
|
||||
action: 'signal-medium',
|
||||
text: 'check this out'
|
||||
},
|
||||
high: {
|
||||
type: 'text/vnd.wap.si',
|
||||
sender: '+31641600986',
|
||||
serviceId: 0,
|
||||
timestamp: '7',
|
||||
href: 'http://www.mozilla.org',
|
||||
id: 'http://www.mozilla.org',
|
||||
action: 'signal-high',
|
||||
text: 'check this out'
|
||||
},
|
||||
sl: {
|
||||
type: 'text/vnd.wap.sl',
|
||||
sender: '+31641600986',
|
||||
serviceId: 0,
|
||||
timestamp: '8',
|
||||
href: 'http://www.mozilla.org'
|
||||
}
|
||||
};
|
||||
|
||||
setup(function(done) {
|
||||
MessageDB.clear().then(done, done);
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
MessageDB.offAll();
|
||||
});
|
||||
|
||||
suite('storing and retrieving messages', function() {
|
||||
var onNewStub;
|
||||
|
||||
setup(function() {
|
||||
MessageDB.on('new', onNewStub = this.sinon.stub());
|
||||
});
|
||||
|
||||
test('message is stored', function(done) {
|
||||
MessageDB.put(messages.current).then(function() {
|
||||
return MessageDB.retrieve(messages.current.timestamp);
|
||||
}).then(function(message) {
|
||||
sinon.assert.calledOnce(onNewStub);
|
||||
sinon.assert.calledWith(onNewStub, message);
|
||||
assert.equal(message.type, messages.current.type);
|
||||
assert.equal(message.sender, messages.current.sender);
|
||||
assert.equal(message.serviceId, messages.current.serviceId);
|
||||
assert.equal(message.href, messages.current.href);
|
||||
assert.equal(message.text, messages.current.text);
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('non-SI message is stored', function(done) {
|
||||
MessageDB.put(messages.sl).then(function() {
|
||||
return MessageDB.retrieve(messages.sl.timestamp);
|
||||
}).then(function(message) {
|
||||
sinon.assert.calledOnce(onNewStub);
|
||||
sinon.assert.calledWith(onNewStub, message);
|
||||
assert.equal(message.type, messages.sl.type);
|
||||
assert.equal(message.sender, messages.sl.sender);
|
||||
assert.equal(message.serviceId, messages.sl.serviceId);
|
||||
assert.equal(message.href, messages.sl.href);
|
||||
}).then(done, done);
|
||||
});
|
||||
});
|
||||
|
||||
suite('handling multiple messages', function() {
|
||||
var onNewStub;
|
||||
|
||||
setup(function() {
|
||||
MessageDB.on('new', onNewStub = this.sinon.stub());
|
||||
});
|
||||
|
||||
test('storing multiple messages with the same si-id', function(done) {
|
||||
var results = {};
|
||||
|
||||
MessageDB.put(messages.low).then(function(message) {
|
||||
return MessageDB.put(messages.medium);
|
||||
}).then(function(message) {
|
||||
return MessageDB.put(messages.high);
|
||||
}).then(function(message) {
|
||||
return MessageDB.retrieve(messages.low.timestamp);
|
||||
}).then(function(message) {
|
||||
results.low = message;
|
||||
return MessageDB.retrieve(messages.medium.timestamp);
|
||||
}).then(function(message) {
|
||||
results.medium = message;
|
||||
return MessageDB.retrieve(messages.high.timestamp);
|
||||
}).then(function(message) {
|
||||
results.high = message;
|
||||
|
||||
sinon.assert.calledThrice(onNewStub);
|
||||
assert.equal(onNewStub.args[0][0], messages.low);
|
||||
assert.equal(results.low.id, messages.low.id);
|
||||
assert.equal(results.low.action, messages.low.action);
|
||||
assert.equal(onNewStub.args[1][0], messages.medium);
|
||||
assert.equal(results.medium.id, messages.medium.id);
|
||||
assert.equal(results.medium.action, messages.medium.action);
|
||||
assert.equal(onNewStub.args[2][0], messages.high);
|
||||
assert.equal(results.high.id, messages.high.id);
|
||||
assert.equal(results.high.action, messages.high.action);
|
||||
}).then(done, done);
|
||||
});
|
||||
});
|
||||
|
||||
suite('out-of-order message handling', function() {
|
||||
var onNewStub;
|
||||
var onDiscardedStub;
|
||||
var onUpdatedStub;
|
||||
|
||||
setup(function() {
|
||||
MessageDB.on('new', onNewStub = this.sinon.stub());
|
||||
MessageDB.on('update', onUpdatedStub = this.sinon.stub());
|
||||
MessageDB.on('discard', onDiscardedStub = this.sinon.stub());
|
||||
});
|
||||
|
||||
test('message is updated by newer message', function(done) {
|
||||
var results = {};
|
||||
|
||||
MessageDB.put(messages.current).then(function() {
|
||||
sinon.assert.calledOnce(onNewStub);
|
||||
sinon.assert.calledWith(onNewStub, messages.current);
|
||||
return MessageDB.put(messages.newer);
|
||||
}).then(function() {
|
||||
sinon.assert.calledOnce(onUpdatedStub);
|
||||
sinon.assert.calledWith(onUpdatedStub, messages.newer);
|
||||
return MessageDB.retrieve(messages.newer.timestamp);
|
||||
}).then(function(message) {
|
||||
results.timestamp = message.timestamp;
|
||||
results.text = message.text;
|
||||
assert.equal(results.timestamp, messages.current.timestamp);
|
||||
assert.equal(results.text, messages.newer.text);
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('old message is discarded', function(done) {
|
||||
MessageDB.put(messages.current).then(function() {
|
||||
sinon.assert.calledOnce(onNewStub);
|
||||
sinon.assert.calledWith(onNewStub, messages.current);
|
||||
return MessageDB.put(messages.older);
|
||||
}).then(function() {
|
||||
sinon.assert.calledOnce(onDiscardedStub);
|
||||
sinon.assert.calledWith(onDiscardedStub, messages.older);
|
||||
return MessageDB.retrieve(messages.older.timestamp);
|
||||
}).then(function() {
|
||||
assert(false, 'The promise should be rejected');
|
||||
}, function() {}).then(done, done);
|
||||
});
|
||||
});
|
||||
|
||||
suite('message actions', function() {
|
||||
var onDeletedStub;
|
||||
var onDiscardedStub;
|
||||
|
||||
setup(function() {
|
||||
MessageDB.on('delete', onDeletedStub = this.sinon.stub());
|
||||
MessageDB.on('discard', onDiscardedStub = this.sinon.stub());
|
||||
});
|
||||
|
||||
test('delete action removes all message with same id', function(done) {
|
||||
MessageDB.put(messages.current).then(function() {
|
||||
return MessageDB.put(messages.delete);
|
||||
}).then(function() {
|
||||
return MessageDB.retrieve(messages.current.timestamp);
|
||||
}).then(function() {
|
||||
assert(false, 'The promise should be rejected');
|
||||
}, function() {}).then(done, done);
|
||||
});
|
||||
|
||||
test('delete action messages are never stored', function(done) {
|
||||
MessageDB.put(messages.delete).then(function() {
|
||||
return MessageDB.retrieve(messages.delete.timestamp);
|
||||
}).then(function() {
|
||||
assert(false, 'The promise should be rejected');
|
||||
}, function() {
|
||||
sinon.assert.calledOnce(onDiscardedStub);
|
||||
sinon.assert.calledWith(onDiscardedStub, messages.delete);
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('delete action messages dispatch a \'deleted\' event',
|
||||
function(done) {
|
||||
MessageDB.put(messages.current).then(function() {
|
||||
return MessageDB.put(messages.delete);
|
||||
}).then(function() {
|
||||
sinon.assert.calledOnce(onDeletedStub);
|
||||
sinon.assert.calledWith(onDeletedStub, messages.current);
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('old delete action is ignored', function(done) {
|
||||
MessageDB.put(messages.current).then(function() {
|
||||
return MessageDB.put(messages.old_delete);
|
||||
}).then(function() {
|
||||
sinon.assert.notCalled(onDeletedStub);
|
||||
return MessageDB.retrieve(messages.current.timestamp);
|
||||
}).then(function(message) {
|
||||
sinon.assert.calledOnce(onDiscardedStub);
|
||||
sinon.assert.calledWith(onDiscardedStub, messages.old_delete);
|
||||
assert.ok(message);
|
||||
}).then(done, done);
|
||||
});
|
||||
});
|
||||
|
||||
suite('deleting messages', function() {
|
||||
var onDeletedStub;
|
||||
|
||||
setup(function() {
|
||||
MessageDB.on('delete', onDeletedStub = this.sinon.stub());
|
||||
});
|
||||
|
||||
test('delete messages by timestamp', function(done) {
|
||||
MessageDB.put(messages.low).then(function(status) {
|
||||
return MessageDB.put(messages.medium);
|
||||
}).then(function(status) {
|
||||
return MessageDB.put(messages.high);
|
||||
}).then(function(status) {
|
||||
return MessageDB.deleteByTimestamp(messages.low.timestamp);
|
||||
}).then(function() {
|
||||
return MessageDB.deleteByTimestamp(messages.medium.timestamp);
|
||||
}).then(function() {
|
||||
return MessageDB.deleteByTimestamp(messages.high.timestamp);
|
||||
}).then(function() {
|
||||
return MessageDB.retrieve(messages.low.timestamp);
|
||||
}).then(function() {
|
||||
assert(false, 'The promise should be rejected');
|
||||
}, function() {
|
||||
return MessageDB.retrieve(messages.medium.timestamp);
|
||||
}).then(function() {
|
||||
assert(false, 'The promise should be rejected');
|
||||
}, function() {
|
||||
return MessageDB.retrieve(messages.high.timestamp);
|
||||
}).then(function() {
|
||||
assert(false, 'The promise should be rejected');
|
||||
}, function() {}).then(done, done);
|
||||
});
|
||||
|
||||
test('a \'deleted\' event is dispatched', function(done) {
|
||||
MessageDB.put(messages.current).then(function() {
|
||||
return MessageDB.deleteByTimestamp(messages.current.timestamp);
|
||||
}).then(function() {
|
||||
sinon.assert.calledOnce(onDeletedStub);
|
||||
sinon.assert.calledWith(onDeletedStub, messages.current);
|
||||
}).then(done, done);
|
||||
});
|
||||
});
|
||||
});
|
||||
25
gaia/apps/wappush/test/unit/mock_activity_picker.js
Executable file
25
gaia/apps/wappush/test/unit/mock_activity_picker.js
Executable file
|
|
@ -0,0 +1,25 @@
|
|||
/* -*- Mode: js; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
|
||||
'use strict';
|
||||
|
||||
var MockActivityPicker = {};
|
||||
|
||||
[
|
||||
'url'
|
||||
].forEach(function(fn) {
|
||||
MockActivityPicker[fn] = function() {
|
||||
MockActivityPicker[fn].called = true;
|
||||
MockActivityPicker[fn].calledWith = [].slice.call(arguments);
|
||||
};
|
||||
|
||||
MockActivityPicker[fn].mSetup = function() {
|
||||
MockActivityPicker[fn].called = false;
|
||||
MockActivityPicker[fn].calledWith = null;
|
||||
};
|
||||
|
||||
MockActivityPicker[fn].mTeardown = function() {
|
||||
delete MockActivityPicker[fn].called;
|
||||
delete MockActivityPicker[fn].calledWith;
|
||||
};
|
||||
});
|
||||
12
gaia/apps/wappush/test/unit/mock_link_action_handler.js
Executable file
12
gaia/apps/wappush/test/unit/mock_link_action_handler.js
Executable file
|
|
@ -0,0 +1,12 @@
|
|||
/* -*- Mode: js; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
|
||||
/* exported MockLinkActionHandler */
|
||||
|
||||
'use strict';
|
||||
|
||||
var MockLinkActionHandler = {
|
||||
reset: function() {},
|
||||
onContextMenu: function() {},
|
||||
onClick: function() {}
|
||||
};
|
||||
15
gaia/apps/wappush/test/unit/mock_messagedb.js
Executable file
15
gaia/apps/wappush/test/unit/mock_messagedb.js
Executable file
|
|
@ -0,0 +1,15 @@
|
|||
/* exported MockMessageDB */
|
||||
|
||||
'use strict';
|
||||
|
||||
var MockMessageDB = {
|
||||
put: function() {},
|
||||
retrieve: function() {},
|
||||
deleteByTimestamp: function() {},
|
||||
clear: function() {},
|
||||
on: function() {},
|
||||
once: function() {},
|
||||
off: function() {},
|
||||
offAll: function() {},
|
||||
emit: function() {}
|
||||
};
|
||||
24
gaia/apps/wappush/test/unit/mock_moz_activity.js
Executable file
24
gaia/apps/wappush/test/unit/mock_moz_activity.js
Executable file
|
|
@ -0,0 +1,24 @@
|
|||
/* -*- Mode: js; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
|
||||
'use strict';
|
||||
|
||||
function MockMozActivity(activity) {
|
||||
MockMozActivity.calls.push(activity);
|
||||
MockMozActivity.instances.push(this);
|
||||
|
||||
this.onsuccess = function() {};
|
||||
this.onerror = function() {};
|
||||
}
|
||||
|
||||
// create an array to hold any calls to this function to test them
|
||||
MockMozActivity.mSetup = function() {
|
||||
MockMozActivity.calls = [];
|
||||
MockMozActivity.instances = [];
|
||||
};
|
||||
|
||||
// destroy the array so it resets every time
|
||||
MockMozActivity.mTeardown = function() {
|
||||
delete MockMozActivity.calls;
|
||||
delete MockMozActivity.instances;
|
||||
};
|
||||
30
gaia/apps/wappush/test/unit/mock_parsed_doc.js
Executable file
30
gaia/apps/wappush/test/unit/mock_parsed_doc.js
Executable file
|
|
@ -0,0 +1,30 @@
|
|||
/* -*- Mode: js; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
|
||||
/* exported MockParsedProvisioningDoc */
|
||||
|
||||
'use strict';
|
||||
|
||||
var MockParsedProvisioningDoc = {
|
||||
_apns: [],
|
||||
|
||||
mSetup: function mwl_setup(apns) {
|
||||
if (apns) {
|
||||
this._apns = apns;
|
||||
}
|
||||
},
|
||||
|
||||
from: function mppd_from(provisioningDoc) {
|
||||
var apns = this._apns;
|
||||
|
||||
return {
|
||||
getApns: function mppd_getApns() {
|
||||
return apns;
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
mTeardown: function mwl_teardown() {
|
||||
this._apns = [];
|
||||
}
|
||||
};
|
||||
34
gaia/apps/wappush/test/unit/mock_whitelist.js
Executable file
34
gaia/apps/wappush/test/unit/mock_whitelist.js
Executable file
|
|
@ -0,0 +1,34 @@
|
|||
/* -*- Mode: js; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
|
||||
/* exported MockWhiteList */
|
||||
|
||||
'use strict';
|
||||
|
||||
var MockWhiteList = {
|
||||
_whiteList: [],
|
||||
|
||||
init: function mwl_init() {
|
||||
return new Promise(function mwl_init_promise(resolve) {
|
||||
resolve();
|
||||
});
|
||||
},
|
||||
|
||||
has: function mwl_has(value) {
|
||||
if (this._whiteList.length === 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return (this._whiteList.indexOf(value) !== -1);
|
||||
},
|
||||
|
||||
mSetup: function mwl_setup(list) {
|
||||
if (list) {
|
||||
this._whiteList = list;
|
||||
}
|
||||
},
|
||||
|
||||
mTeardown: function mwl_teardown() {
|
||||
this._whiteList = [];
|
||||
}
|
||||
};
|
||||
325
gaia/apps/wappush/test/unit/parsed_doc_test.js
Executable file
325
gaia/apps/wappush/test/unit/parsed_doc_test.js
Executable file
|
|
@ -0,0 +1,325 @@
|
|||
/* -*- Mode: js; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
|
||||
/* global ParsedProvisioningDoc */
|
||||
|
||||
'use strict';
|
||||
|
||||
requireApp('wappush/js/parsed_doc.js');
|
||||
|
||||
suite('ParsedProvisioningDoc >', function() {
|
||||
|
||||
suite('ParsedProvisioningDoc.from()', function() {
|
||||
|
||||
test('Empty document. List of APN is empty', function() {
|
||||
var parsedProvisioningDoc;
|
||||
var provisioningDoc = '';
|
||||
parsedProvisioningDoc = ParsedProvisioningDoc.from(provisioningDoc);
|
||||
assert.lengthOf(parsedProvisioningDoc.getApns(), 0);
|
||||
});
|
||||
|
||||
test('TEF document with default APN', function() {
|
||||
var parsedProvisioningDoc;
|
||||
var provisioningDoc =
|
||||
'<wap-provisioningdoc>' +
|
||||
'<characteristic type="PXLOGICAL">' +
|
||||
'<parm name="PROXY-ID" value="10.138.255.133"/>' +
|
||||
'<parm name="NAME" value="Telefonica"/>' +
|
||||
'<parm name="STARTPAGE" value="http://www.movistar.es"/>' +
|
||||
'<characteristic type="PORT">' +
|
||||
'<parm name="PORTNBR" value="8080"/>' +
|
||||
'<parm name="SERVICE" value="CO-WSP"/>' +
|
||||
'</characteristic>' +
|
||||
'<characteristic type="PXPHYSICAL">' +
|
||||
'<parm name="PHYSICAL-PROXY-ID" value="PRX1"/>' +
|
||||
'<parm name="PXADDR" value="10.138.255.133"/>' +
|
||||
'<parm name="PXADDRTYPE" value="IPV4"/>' +
|
||||
'<parm name="TO-NAPID" value="NAP1"/>' +
|
||||
'<characteristic type="PORT">' +
|
||||
'<parm name="PORTNBR" value="8080"/>' +
|
||||
'<parm name="SERVICE" value="CO-WSP"/>' +
|
||||
'</characteristic>' +
|
||||
'</characteristic>' +
|
||||
'</characteristic>' +
|
||||
'<characteristic type="NAPDEF">' +
|
||||
'<parm name="NAPID" value="NAP1"/>' +
|
||||
'<parm name="BEARER" value="GSM-GPRS"/>' +
|
||||
'<parm name="NAME" value="Telefonica"/>' +
|
||||
'<parm name="NAP-ADDRESS" value="telefonica.es"/>' +
|
||||
'<parm name="NAP-ADDRTYPE" value="APN"/>' +
|
||||
'<characteristic type="NAPAUTHINFO">' +
|
||||
'<parm name="AUTHTYPE" value="PAP"/>' +
|
||||
'<parm name="AUTHNAME" value="telefonica"/>' +
|
||||
'<parm name="AUTHSECRET" value="telefonica"/>' +
|
||||
'</characteristic>' +
|
||||
'</characteristic>' +
|
||||
'<characteristic type="APPLICATION">' +
|
||||
'<parm name="APPID" value="w2"/>' +
|
||||
'<parm name="TO-PROXY" value="10.138.255.133"/>' +
|
||||
'<parm name="NAME" value="Telefonica"/>' +
|
||||
'</characteristic>' +
|
||||
'</wap-provisioningdoc>';
|
||||
|
||||
parsedProvisioningDoc = ParsedProvisioningDoc.from(provisioningDoc);
|
||||
assert.lengthOf(parsedProvisioningDoc.getApns(), 1);
|
||||
});
|
||||
|
||||
test('CHT document with default and mms APN', function() {
|
||||
var parsedProvisioningDoc;
|
||||
var provisioningDoc =
|
||||
'<wap-provisioningdoc>' +
|
||||
'<characteristic type="BOOTSTRAP">' +
|
||||
'<parm name="NAME" value="CHT_emome"/>' +
|
||||
'</characteristic>' +
|
||||
'<characteristic type="APPLICATION">' +
|
||||
'<parm name="APPID" value="w2"/>' +
|
||||
'<parm name="TO-PROXY" value="WPROXY"/>' +
|
||||
'<parm name="NAME" value="CHT_emome"/>' +
|
||||
'<characteristic type="RESOURCE">' +
|
||||
'<parm name="URI" value="http://wap.emome.net/"/>' +
|
||||
'<parm name="NAME" value="CHT_emome"/>' +
|
||||
'<parm name="STARTPAGE"/>' +
|
||||
'</characteristic>' +
|
||||
'</characteristic>' +
|
||||
'<characteristic type="APPLICATION">' +
|
||||
'<parm name="APPID" value="w4"/>' +
|
||||
'<parm name="TO-PROXY" value="MPROXY"/>' +
|
||||
'<parm name="ADDR" value="http://mms:8002"/>' +
|
||||
'</characteristic>' +
|
||||
'<characteristic type="PXLOGICAL">' +
|
||||
'<parm name="PROXY-ID" value="WPROXY"/>' +
|
||||
'<parm name="NAME" value="CHT_emome"/>' +
|
||||
'<parm name="STARTPAGE" value="http://wap.emome.net/"/>' +
|
||||
'<characteristic type="PXPHYSICAL">' +
|
||||
'<parm name="PHYSICAL-PROXY-ID" value="PROXY1"/>' +
|
||||
'<parm name="PXADDR" value="10.1.1.1"/>' +
|
||||
'<parm name="PXADDRTYPE" value="IPV4"/>' +
|
||||
'<parm name="TO-NAPID" value="CHT_emome"/>' +
|
||||
'<characteristic type="PORT">' +
|
||||
'<parm name="PORTNBR" value="8080"/>' +
|
||||
'</characteristic>' +
|
||||
'</characteristic>' +
|
||||
'</characteristic>' +
|
||||
'<characteristic type="PXLOGICAL">' +
|
||||
'<parm name="PROXY-ID" value="MPROXY"/>' +
|
||||
'<parm name="NAME" value="CHT_MMS"/>' +
|
||||
'<characteristic type="PXPHYSICAL">' +
|
||||
'<parm name="PHYSICAL-PROXY-ID" value="PROXY2"/>' +
|
||||
'<parm name="PXADDR" value="10.1.1.1"/>' +
|
||||
'<parm name="PXADDRTYPE" value="IPV4"/>' +
|
||||
'<parm name="TO-NAPID" value="CHT_MMS"/>' +
|
||||
'<characteristic type="PORT">' +
|
||||
'<parm name="PORTNBR" value="8080"/>' +
|
||||
'</characteristic>' +
|
||||
'</characteristic>' +
|
||||
'</characteristic>' +
|
||||
'<characteristic type="NAPDEF">' +
|
||||
'<parm name="NAPID" value="CHT_emome"/>' +
|
||||
'<parm name="NAME" value="CHT_emome"/>' +
|
||||
'<parm name="BEARER" value="GSM-GPRS"/>' +
|
||||
'<parm name="NAP-ADDRESS" value="emome"/>' +
|
||||
'<parm name="NAP-ADDRTYPE" value="APN"/>' +
|
||||
'</characteristic>' +
|
||||
'<characteristic type="NAPDEF">' +
|
||||
'<parm name="NAPID" value="CHT_MMS"/>' +
|
||||
'<parm name="NAME" value="CHT_MMS"/>' +
|
||||
'<parm name="BEARER" value="GSM-GPRS"/>' +
|
||||
'<Parm name="NAP-ADDRESS" value="emome"/>' +
|
||||
'<parm name="NAP-ADDRTYPE" value="APN"/>' +
|
||||
'</characteristic>' +
|
||||
'</wap-provisioningdoc>';
|
||||
|
||||
parsedProvisioningDoc = ParsedProvisioningDoc.from(provisioningDoc);
|
||||
assert.lengthOf(parsedProvisioningDoc.getApns(), 2);
|
||||
});
|
||||
|
||||
test('TEF document without APPLICATION nodes', function() {
|
||||
var parsedProvisioningDoc;
|
||||
var provisioningDoc =
|
||||
'<wap-provisioningdoc>' +
|
||||
'<characteristic type="PXLOGICAL">' +
|
||||
'<parm name="PROXY-ID" value="10.138.255.133"/>' +
|
||||
'<parm name="NAME" value="Telefonica"/>' +
|
||||
'<parm name="STARTPAGE" value="http://www.movistar.es"/>' +
|
||||
'<characteristic type="PORT">' +
|
||||
'<parm name="PORTNBR" value="8080"/>' +
|
||||
'<parm name="SERVICE" value="CO-WSP"/>' +
|
||||
'</characteristic>' +
|
||||
'<characteristic type="PXPHYSICAL">' +
|
||||
'<parm name="PHYSICAL-PROXY-ID" value="PRX1"/>' +
|
||||
'<parm name="PXADDR" value="10.138.255.133"/>' +
|
||||
'<parm name="PXADDRTYPE" value="IPV4"/>' +
|
||||
'<parm name="TO-NAPID" value="NAP1"/>' +
|
||||
'<characteristic type="PORT">' +
|
||||
'<parm name="PORTNBR" value="8080"/>' +
|
||||
'<parm name="SERVICE" value="CO-WSP"/>' +
|
||||
'</characteristic>' +
|
||||
'</characteristic>' +
|
||||
'</characteristic>' +
|
||||
'<characteristic type="NAPDEF">' +
|
||||
'<parm name="NAPID" value="NAP1"/>' +
|
||||
'<parm name="BEARER" value="GSM-GPRS"/>' +
|
||||
'<parm name="NAME" value="Telefonica"/>' +
|
||||
'<parm name="NAP-ADDRESS" value="telefonica.es"/>' +
|
||||
'<parm name="NAP-ADDRTYPE" value="APN"/>' +
|
||||
'<characteristic type="NAPAUTHINFO">' +
|
||||
'<parm name="AUTHTYPE" value="PAP"/>' +
|
||||
'<parm name="AUTHNAME" value="telefonica"/>' +
|
||||
'<parm name="AUTHSECRET" value="telefonica"/>' +
|
||||
'</characteristic>' +
|
||||
'</characteristic>' +
|
||||
'</wap-provisioningdoc>';
|
||||
|
||||
parsedProvisioningDoc = ParsedProvisioningDoc.from(provisioningDoc);
|
||||
assert.lengthOf(parsedProvisioningDoc.getApns(), 0);
|
||||
});
|
||||
|
||||
test('TEF document without valid APPLICATION nodes', function() {
|
||||
var parsedProvisioningDoc;
|
||||
var provisioningDoc =
|
||||
'<wap-provisioningdoc>' +
|
||||
'<characteristic type="NAPDEF">' +
|
||||
'<parm name="NAPID" value="Yahoo"/>' +
|
||||
'<parm name="BEARER" value="GSM-GPRS"/>' +
|
||||
'<parm name="NAME" value="Yahoo"/>' +
|
||||
'<parm name="NAP-ADDRESS" value="movistar.es"/>' +
|
||||
'<parm name="NAP-ADDRTYPE" value="APN"/>' +
|
||||
'<characteristic type="NAPAUTHINFO">' +
|
||||
'<parm name="AUTHTYPE" value="PAP"/>' +
|
||||
'<parm name="AUTHNAME" value=""/>' +
|
||||
'<parm name="AUTHSECRET" value=""/>' +
|
||||
'</characteristic>' +
|
||||
'</characteristic>' +
|
||||
'<characteristic type="APPLICATION">' +
|
||||
'<parm name="APPID" value="25"/>' +
|
||||
'<parm name="PROVIDER-ID" value="Yahoo"/>' +
|
||||
'<parm name="NAME" value="Yahoo"/>' +
|
||||
'<parm name="FROM" value="owdqa_test1@yahoo.es"/>' +
|
||||
'<parm name="TO-NAPID" value="Yahoo"/>' +
|
||||
'<characteristic type="APPADDR">' +
|
||||
'<parm name="ADDR" value="smtp.yahoo.es"/>' +
|
||||
'<characteristic type="PORT">' +
|
||||
'<parm name="PORTNBR" value="25"/>' +
|
||||
'</characteristic>' +
|
||||
'</characteristic>' +
|
||||
'</characteristic>' +
|
||||
'<characteristic type="APPLICATION">' +
|
||||
'<parm name="APPID" value="110"/>' +
|
||||
'<parm name="PROVIDER-ID" value="Yahoo"/>' +
|
||||
'<parm name="NAME" value="Yahoo"/>' +
|
||||
'<parm name="TO-NAPID" value="Yahoo"/>' +
|
||||
'<characteristic type="APPADDR">' +
|
||||
'<parm name="ADDR" value="pop.yahoo.es"/>' +
|
||||
'<characteristic type="PORT">' +
|
||||
'<parm name="PORTNBR" value="110"/>' +
|
||||
'</characteristic>' +
|
||||
'</characteristic>' +
|
||||
'<characteristic type="APPAUTH">' +
|
||||
'<parm name="AAUTHNAME" value="owdqa_test1@yahoo.es"/>' +
|
||||
'<parm name="AAUTHSECRET"/>' +
|
||||
'</characteristic>' +
|
||||
'</characteristic>' +
|
||||
'</wap-provisioningdoc>';
|
||||
|
||||
parsedProvisioningDoc = ParsedProvisioningDoc.from(provisioningDoc);
|
||||
assert.lengthOf(parsedProvisioningDoc.getApns(), 0);
|
||||
});
|
||||
|
||||
test('DT document without TO-PROXY nodes in the APPLICATION nodes',
|
||||
function() {
|
||||
var parsedProvisioningDoc;
|
||||
var provisioningDoc =
|
||||
'<wap-provisioningdoc version="1.0">' +
|
||||
'<characteristic type="BOOTSTRAP">' +
|
||||
'<parm name="NAME" value="T-Mobile Internet"/>' +
|
||||
'</characteristic>' +
|
||||
'<characteristic type="NAPDEF">' +
|
||||
'<parm name="NAPID" value="internet-apn"/>' +
|
||||
'<parm name="BEARER" value="GSM-GPRS"/>' +
|
||||
'<parm name="NAME" value="T-Mobile Internet"/>' +
|
||||
'<parm name="NAP-ADDRTYPE" value="APN"/>' +
|
||||
'<parm name="NAP-ADDRESS" value="internet.t-mobile"/>' +
|
||||
'<parm name="LINGER" value="150"/>' +
|
||||
'<parm name="DNS-ADDR" value="193.254.160.001"/>' +
|
||||
'<parm name="DNS-ADDR" value="193.254.160.130"/>' +
|
||||
'<characteristic type="NAPAUTHINFO">' +
|
||||
'<parm name="AUTHTYPE" value="PAP"/>' +
|
||||
'<parm name="AUTHNAME" value="t-mobile"/>' +
|
||||
'<parm name="AUTHSECRET" value="tm"/>' +
|
||||
'</characteristic>' +
|
||||
'</characteristic>' +
|
||||
'<characteristic type="APPLICATION">' +
|
||||
'<parm name="APPID" value="w2"/>' +
|
||||
'<parm name="TO-NAPID" value="internet-apn"/>' +
|
||||
'<parm name="NAME" value="T-Mobile Internet"/>' +
|
||||
'<characteristic type="RESOURCE">' +
|
||||
'<parm name="NAME" value="Start page"/>' +
|
||||
'<parm name="URI" value="http://www.t-mobile-favoriten.de"/>' +
|
||||
'<parm name="STARTPAGE"/>' +
|
||||
'</characteristic>' +
|
||||
'</characteristic>' +
|
||||
'</wap-provisioningdoc>';
|
||||
parsedProvisioningDoc = ParsedProvisioningDoc.from(provisioningDoc);
|
||||
assert.lengthOf(parsedProvisioningDoc.getApns(), 1);
|
||||
});
|
||||
test('Document without matched PROXY-ID node for a TO-PROXY node',
|
||||
function() {
|
||||
var parsedProvisioningDoc;
|
||||
var provisioningDoc =
|
||||
'<wap-provisioningdoc version="1.0">' +
|
||||
'<characteristic type="PXLOGICAL">' +
|
||||
'<parm name="PROXY-ID" value="www.operator.com"/>' +
|
||||
'<parm name="NAME" value="GW WAP"/>' +
|
||||
'<characteristic type="PXPHYSICAL">' +
|
||||
'<parm name="PHYSICAL-PROXY-ID" value="PROXY_WAP_3"/>' +
|
||||
'<parm name="PXADDR" value="172.150.70.5"/>' +
|
||||
'<parm name="PXADDRTYPE" value="IPV4"/>' +
|
||||
'<parm name="TO-NAPID" value="WAP_GPRS"/>' +
|
||||
'<parm name="TO-NAPID" value="WAP_CSD"/>' +
|
||||
'</characteristic>' +
|
||||
'</characteristic>' +
|
||||
'<characteristic type="NAPDEF">' +
|
||||
'<parm name="NAPID" value="WAP_GPRS"/>' +
|
||||
'<parm name="BEARER" value="GSM-GPRS"/>' +
|
||||
'<parm name="NAME" value="operator GPRS"/>' +
|
||||
'<parm name="NAP-ADDRESS" value="wap.operator.com"/>' +
|
||||
'<parm name="NAP-ADDRTYPE" value="APN"/>' +
|
||||
'<characteristic type="NAPAUTHINFO">' +
|
||||
'<parm name="AUTHTYPE" value="PAP"/>' +
|
||||
'<parm name="AUTHNAME" value="WAPOP"/>' +
|
||||
'<parm name="AUTHSECRET" value="WAPOP"/>' +
|
||||
'</characteristic>' +
|
||||
'</characteristic>' +
|
||||
'<characteristic type="NAPDEF">' +
|
||||
'<parm name="NAPID" value="WAP_GPRS"/>' +
|
||||
'<parm name="BEARER" value="GSM-GPRS"/>' +
|
||||
'<parm name="NAME" value="operator GPRS"/>' +
|
||||
'<parm name="NAP-ADDRESS" value="wapOP.operator.com"/>' +
|
||||
'<parm name="NAP-ADDRTYPE" value="APN"/>' +
|
||||
'<characteristic type="NAPAUTHINFO">' +
|
||||
'<parm name="AUTHTYPE" value="PAP"/>' +
|
||||
'<parm name="AUTHNAME" value="WAPOP"/>' +
|
||||
'<parm name="AUTHSECRET" value="WAPOP"/>' +
|
||||
'</characteristic>' +
|
||||
'</characteristic>' +
|
||||
'<characteristic type="APPLICATION">' +
|
||||
'<parm name="APPID" value="w2"/>' +
|
||||
'<parm name="NAME" value="Operator-portal"/>' +
|
||||
'<parm name="TO-PROXY" value="www.operator.com"/>' +
|
||||
'<characteristic type="RESOURCE">' +
|
||||
'<parm name="URI" value="http://wap.operator_portal.com"/>' +
|
||||
'<parm name="NAME" value="Operator portal"/>' +
|
||||
'<parm name="STARTPAGE"/>' +
|
||||
'</characteristic>' +
|
||||
'</characteristic>' +
|
||||
'<characteristic type="APPLICATION">' +
|
||||
'<parm name="APPID" value="w2"/>' +
|
||||
'<parm name="NAME" value="Operator-portal"/>' +
|
||||
'<parm name="TO-PROXY" value="www.wap.operator.com"/>' +
|
||||
'</characteristic>' +
|
||||
'</wap-provisioningdoc>';
|
||||
parsedProvisioningDoc = ParsedProvisioningDoc.from(provisioningDoc);
|
||||
assert.lengthOf(parsedProvisioningDoc.getApns(), 1);
|
||||
});
|
||||
});
|
||||
});
|
||||
277
gaia/apps/wappush/test/unit/parsed_message_test.js
Executable file
277
gaia/apps/wappush/test/unit/parsed_message_test.js
Executable file
|
|
@ -0,0 +1,277 @@
|
|||
/* -*- Mode: js; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
|
||||
/* global MockMessageDB, MocksHelper, ParsedMessage */
|
||||
|
||||
'use strict';
|
||||
|
||||
require('/js/provisioning.js');
|
||||
require('/js/parsed_message.js');
|
||||
|
||||
require('/test/unit/mock_messagedb.js');
|
||||
|
||||
var mocksHelperParsedMessage = new MocksHelper([
|
||||
'MessageDB'
|
||||
]).init();
|
||||
|
||||
suite('ParsedMessage', function() {
|
||||
var messages;
|
||||
var timestamp;
|
||||
|
||||
mocksHelperParsedMessage.attachTestHelpers();
|
||||
|
||||
suiteSetup(function() {
|
||||
timestamp = Date.now();
|
||||
messages = {
|
||||
si_text_only: {
|
||||
sender: '+31641600986',
|
||||
contentType: 'text/vnd.wap.si',
|
||||
content: '<si><indication>check this out</indication></si>',
|
||||
serviceId: 0
|
||||
},
|
||||
|
||||
si_href_only: {
|
||||
sender: '+31641600986',
|
||||
contentType: 'text/vnd.wap.si',
|
||||
content: '<si><indication href="http://www.mozilla.org"></indication>' +
|
||||
'</si>',
|
||||
serviceId: 0
|
||||
},
|
||||
|
||||
si_text_and_href: {
|
||||
sender: '+31641600986',
|
||||
contentType: 'text/vnd.wap.si',
|
||||
content: '<si><indication href="http://www.mozilla.org">' +
|
||||
'check this out</indication></si>',
|
||||
serviceId: 0
|
||||
},
|
||||
|
||||
si_id: {
|
||||
sender: '+31641600986',
|
||||
contentType: 'text/vnd.wap.si',
|
||||
content: '<si>' +
|
||||
'<indication si-id="gaia-test@mozilla.org"' +
|
||||
' href="http://www.mozilla.org">' +
|
||||
'check this out' +
|
||||
'</indication>' +
|
||||
'</si>',
|
||||
serviceId: 0
|
||||
},
|
||||
|
||||
created: {
|
||||
sender: '+31641600986',
|
||||
contentType: 'text/vnd.wap.si',
|
||||
content: '<si>' +
|
||||
'<indication si-id="gaia-test@mozilla.org"' +
|
||||
' href="http://www.mozilla.org"' +
|
||||
' created="2013-09-03T10:35:33Z">' +
|
||||
'check this out' +
|
||||
'</indication>' +
|
||||
'</si>',
|
||||
serviceId: 0
|
||||
},
|
||||
|
||||
expires: {
|
||||
sender: '+31641600986',
|
||||
contentType: 'text/vnd.wap.si',
|
||||
content: '<si>' +
|
||||
'<indication si-id="gaia-test@mozilla.org"' +
|
||||
' href="http://www.mozilla.org"' +
|
||||
' si-expires="2013-09-03T10:35:33Z">' +
|
||||
'check this out' +
|
||||
'</indication>' +
|
||||
'</si>',
|
||||
serviceId: 0
|
||||
},
|
||||
|
||||
action: {
|
||||
sender: '+31641600986',
|
||||
contentType: 'text/vnd.wap.si',
|
||||
content: '<si>' +
|
||||
'<indication action="signal-none">' +
|
||||
'check this out' +
|
||||
'</indication>' +
|
||||
'</si>',
|
||||
serviceId: 0
|
||||
},
|
||||
|
||||
invalid_delete: {
|
||||
sender: '+31641600986',
|
||||
contentType: 'text/vnd.wap.si',
|
||||
content: '<si>' +
|
||||
'<indication action="delete"' +
|
||||
' href="http://www.mozilla.org">' +
|
||||
'check this out' +
|
||||
'</indication>' +
|
||||
'</si>',
|
||||
serviceId: 0
|
||||
},
|
||||
|
||||
sl: {
|
||||
sender: '+31641600986',
|
||||
contentType: 'text/vnd.wap.sl',
|
||||
content: '<sl href="http://www.mozilla.org"/>',
|
||||
serviceId: 0
|
||||
},
|
||||
|
||||
sl_action: {
|
||||
sender: '+31641600986',
|
||||
contentType: 'text/vnd.wap.sl',
|
||||
content: '<sl href="http://www.mozilla.org" action="execute-high"/>',
|
||||
serviceId: 0
|
||||
},
|
||||
|
||||
cp_noauth: {
|
||||
sender: '22997',
|
||||
contentType: 'text/vnd.wap.connectivity-xml',
|
||||
content: '<wap-provisioningdoc></wap-provisioningdoc>',
|
||||
serviceId: 0
|
||||
},
|
||||
|
||||
cp_netwpin_checked_notpass: {
|
||||
sender: '22997',
|
||||
contentType: 'text/vnd.wap.connectivity-xml',
|
||||
content: '<wap-provisioningdoc></wap-provisioningdoc>',
|
||||
authInfo: {
|
||||
sec: 'NETWPIN',
|
||||
checked: true,
|
||||
pass: false
|
||||
},
|
||||
serviceId: 0
|
||||
},
|
||||
|
||||
unsupported: {
|
||||
sender: '+31641600986',
|
||||
contentType: 'text/foobar',
|
||||
content: '',
|
||||
serviceId: 0
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
suite('ParsedMessage.from()', function() {
|
||||
test('Text only', function() {
|
||||
var message = ParsedMessage.from(messages.si_text_only, timestamp);
|
||||
|
||||
assert.equal(message.type, 'text/vnd.wap.si');
|
||||
assert.equal(message.sender, '+31641600986');
|
||||
assert.equal(message.timestamp, timestamp);
|
||||
assert.equal(message.text, 'check this out');
|
||||
assert.equal(message.href, undefined);
|
||||
assert.equal(message.action, 'signal-medium');
|
||||
assert.equal(message.serviceId, 0);
|
||||
});
|
||||
|
||||
test('HREF only', function() {
|
||||
var message = ParsedMessage.from(messages.si_href_only, timestamp);
|
||||
|
||||
assert.equal(message.text, '');
|
||||
assert.equal(message.href, 'http://www.mozilla.org');
|
||||
assert.equal(message.id, 'http://www.mozilla.org');
|
||||
});
|
||||
|
||||
test('Text and HREF only', function() {
|
||||
var message = ParsedMessage.from(messages.si_text_and_href, timestamp);
|
||||
|
||||
assert.equal(message.text, 'check this out');
|
||||
assert.equal(message.href, 'http://www.mozilla.org');
|
||||
assert.equal(message.id, 'http://www.mozilla.org');
|
||||
});
|
||||
|
||||
test('SI message with explicit si-id field', function() {
|
||||
var message = ParsedMessage.from(messages.si_id, timestamp);
|
||||
|
||||
assert.equal(message.href, 'http://www.mozilla.org');
|
||||
assert.equal(message.id, 'gaia-test@mozilla.org');
|
||||
});
|
||||
|
||||
test('SI message with explicit creation time', function() {
|
||||
var message = ParsedMessage.from(messages.created, timestamp);
|
||||
|
||||
assert.equal(message.created, Date.parse('2013-09-03T10:35:33Z'));
|
||||
});
|
||||
|
||||
test('SI message with explicit expiration date', function() {
|
||||
var message = ParsedMessage.from(messages.expires, timestamp);
|
||||
|
||||
assert.equal(message.expires, Date.parse('2013-09-03T10:35:33Z'));
|
||||
assert.isTrue(message.isExpired());
|
||||
});
|
||||
|
||||
test('SI message with explicit action', function() {
|
||||
var message = ParsedMessage.from(messages.action, timestamp);
|
||||
|
||||
assert.equal(message.action, 'signal-none');
|
||||
});
|
||||
|
||||
test('SI message with delete action but no si-id field', function() {
|
||||
assert.isNull(ParsedMessage.from(messages.invalid_delete, timestamp));
|
||||
});
|
||||
|
||||
test('SL message', function() {
|
||||
var message = ParsedMessage.from(messages.sl, timestamp);
|
||||
|
||||
assert.equal(message.type, 'text/vnd.wap.sl');
|
||||
assert.equal(message.text, undefined);
|
||||
assert.equal(message.href, 'http://www.mozilla.org');
|
||||
assert.equal(message.action, 'execute-low');
|
||||
});
|
||||
|
||||
test('SL message with explicit action', function() {
|
||||
var message = ParsedMessage.from(messages.sl_action, timestamp);
|
||||
|
||||
assert.equal(message.action, 'execute-high');
|
||||
});
|
||||
|
||||
test('OMA CP message without auth info', function() {
|
||||
var message = ParsedMessage.from(messages.cp_noauth, timestamp);
|
||||
|
||||
assert.equal(message, null);
|
||||
});
|
||||
|
||||
test('OMA CP message with NETWPIN auth info not authenticated', function() {
|
||||
var message = ParsedMessage.from(messages.cp_netwpin_checked_notpass,
|
||||
timestamp);
|
||||
|
||||
assert.equal(message, null);
|
||||
});
|
||||
|
||||
test('unsupported content', function() {
|
||||
var message = ParsedMessage.from(messages.unsupported, timestamp);
|
||||
|
||||
assert.equal(message, null);
|
||||
});
|
||||
});
|
||||
|
||||
suite('ParsedMessage.load()', function() {
|
||||
var jsonMessage;
|
||||
|
||||
suiteSetup(function() {
|
||||
jsonMessage = ParsedMessage.from({
|
||||
sender: '+31641600986',
|
||||
contentType: 'text/vnd.wap.si',
|
||||
content: '<si><indication>check this out</indication></si>',
|
||||
serviceId: 0
|
||||
}, 0).toJSON();
|
||||
});
|
||||
|
||||
test('Successful retrieve a message', function(done) {
|
||||
this.sinon.stub(MockMessageDB, 'retrieve').returns(
|
||||
Promise.resolve(jsonMessage)
|
||||
);
|
||||
|
||||
ParsedMessage.load(0).then(message => {
|
||||
assert.instanceOf(message, ParsedMessage);
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('Rejects if no message is found', function(done) {
|
||||
this.sinon.stub(MockMessageDB, 'retrieve').returns(Promise.reject());
|
||||
|
||||
ParsedMessage.load(0).then(
|
||||
() => assert.isTrue(false, 'Should not resolve the promise'),
|
||||
() => {}
|
||||
).then(done, done);
|
||||
});
|
||||
});
|
||||
});
|
||||
7
gaia/apps/wappush/test/unit/setup.js
Executable file
7
gaia/apps/wappush/test/unit/setup.js
Executable file
|
|
@ -0,0 +1,7 @@
|
|||
/* -*- Mode: js; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
|
||||
'use strict';
|
||||
|
||||
require('/shared/test/unit/load_body_html_helper.js');
|
||||
require('/shared/test/unit/mocks/mocks_helper.js');
|
||||
172
gaia/apps/wappush/test/unit/store_test.js
Executable file
172
gaia/apps/wappush/test/unit/store_test.js
Executable file
|
|
@ -0,0 +1,172 @@
|
|||
/* -*- Mode: js; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
|
||||
/* global MockNavigatorSettings, StoreProvisioning, MocksHelper */
|
||||
|
||||
'use strict';
|
||||
|
||||
require('/shared/test/unit/mocks/mock_navigator_moz_settings.js');
|
||||
require('/js/store.js');
|
||||
require('/shared/js/uuid.js');
|
||||
require('/shared/test/unit/mocks/mocks_helper.js');
|
||||
|
||||
var mocksHelperStoreProvisioning = new MocksHelper([
|
||||
'NavigatorSettings'
|
||||
]).init();
|
||||
|
||||
suite('StoreProvisioning >', function() {
|
||||
var realSettings;
|
||||
mocksHelperStoreProvisioning.attachTestHelpers();
|
||||
|
||||
suiteSetup(function() {
|
||||
realSettings = navigator.mozSettings;
|
||||
navigator.mozSettings = MockNavigatorSettings;
|
||||
});
|
||||
|
||||
suiteTeardown(function() {
|
||||
navigator.mozSettings = realSettings;
|
||||
realSettings = null;
|
||||
});
|
||||
|
||||
setup(function() {
|
||||
MockNavigatorSettings.mSettings['operatorvariant.mcc'] = ['214', '216'];
|
||||
MockNavigatorSettings.mSettings['operatorvariant.mnc'] = ['07', '01'];
|
||||
MockNavigatorSettings.mSyncRepliesOnly = true;
|
||||
});
|
||||
|
||||
suite('StoreProvisioning.getMccMncCodes()', function() {
|
||||
test('Get SIM1 MCC code', function() {
|
||||
StoreProvisioning.getMccMncCodes(0, function(mcc, mnc) {
|
||||
assert.equal(mcc, '214');
|
||||
});
|
||||
// getMccMncCodes does 2 mozSettings requests in sequence
|
||||
MockNavigatorSettings.mReplyToRequests();
|
||||
MockNavigatorSettings.mReplyToRequests();
|
||||
});
|
||||
test('Get SIM1 MNC code', function() {
|
||||
StoreProvisioning.getMccMncCodes(0, function(mcc, mnc) {
|
||||
assert.equal(mnc, '07');
|
||||
});
|
||||
MockNavigatorSettings.mReplyToRequests();
|
||||
MockNavigatorSettings.mReplyToRequests();
|
||||
});
|
||||
test('Get SIM2 MCC code', function() {
|
||||
StoreProvisioning.getMccMncCodes(1, function(mcc, mnc) {
|
||||
assert.equal(mcc, '216');
|
||||
});
|
||||
MockNavigatorSettings.mReplyToRequests();
|
||||
MockNavigatorSettings.mReplyToRequests();
|
||||
});
|
||||
test('Get SIM2 MNC code', function() {
|
||||
StoreProvisioning.getMccMncCodes(1, function(mcc, mnc) {
|
||||
assert.equal(mnc, '01');
|
||||
});
|
||||
MockNavigatorSettings.mReplyToRequests();
|
||||
MockNavigatorSettings.mReplyToRequests();
|
||||
});
|
||||
});
|
||||
|
||||
suite('StoreProvisioning.provision()', function() {
|
||||
setup(function() {
|
||||
MockNavigatorSettings.mSettings['ril.data.cp.apns'] = '';
|
||||
MockNavigatorSettings.mSettings['ril.data.apnSettings'] = '';
|
||||
});
|
||||
|
||||
test('Add APN to SIM1: Telefonica DEFAULT type', function() {
|
||||
var apns = [{
|
||||
carrier: 'Movistar',
|
||||
apn: 'telefonica.es',
|
||||
user: 'telefonica',
|
||||
password: 'telefonica',
|
||||
type: ['default']
|
||||
}];
|
||||
StoreProvisioning.provision(apns, 0, function(){
|
||||
assert.lengthOf(
|
||||
MockNavigatorSettings.mSettings['ril.data.cp.apns']['214']['07'], 1
|
||||
);
|
||||
assert.lengthOf(
|
||||
MockNavigatorSettings.mSettings['ril.data.apnSettings'][0], 1
|
||||
);
|
||||
});
|
||||
MockNavigatorSettings.mReplyToRequests();
|
||||
});
|
||||
test('Add APN to SIM2: Pannon DEFAULT type', function() {
|
||||
var apns = [{
|
||||
carrier: 'Pannon GSM',
|
||||
apn: 'Pannon.hu',
|
||||
user: 'Pannon',
|
||||
password: 'Pannon',
|
||||
type: ['default']
|
||||
}];
|
||||
StoreProvisioning.provision(apns, 1, function() {
|
||||
assert.lengthOf(
|
||||
MockNavigatorSettings.mSettings['ril.data.cp.apns']['216']['01'], 1
|
||||
);
|
||||
assert.lengthOf(
|
||||
MockNavigatorSettings.mSettings['ril.data.apnSettings'][1], 1
|
||||
);
|
||||
});
|
||||
MockNavigatorSettings.mReplyToRequests();
|
||||
});
|
||||
});
|
||||
|
||||
suite('StoreProvisioning.provision()', function() {
|
||||
setup(function() {
|
||||
MockNavigatorSettings.mSettings['ril.data.apnSettings'] = '';
|
||||
MockNavigatorSettings.mSettings['ril.data.cp.apns'] = {};
|
||||
MockNavigatorSettings.mSettings['ril.data.cp.apns']['214'] = {};
|
||||
MockNavigatorSettings.mSettings['ril.data.cp.apns']['216'] = {};
|
||||
MockNavigatorSettings.mSettings['ril.data.cp.apns']['214']['07'] = [{
|
||||
carrier: 'Movistar',
|
||||
apn: 'telefonica.es',
|
||||
user: 'telefonica',
|
||||
password: 'telefonica',
|
||||
type: ['default']
|
||||
}];
|
||||
MockNavigatorSettings.mSettings['ril.data.cp.apns']['216']['01'] = [{
|
||||
carrier: 'Pannon GSM',
|
||||
apn: 'Pannon.hu',
|
||||
user: 'Pannon',
|
||||
password: 'Pannon',
|
||||
type: ['default']
|
||||
}];
|
||||
});
|
||||
|
||||
test('Add APN to SIM1: Telefonica MMS type', function() {
|
||||
var apns = [{
|
||||
carrier: 'Movistar MMS',
|
||||
apn: 'telefonica.es',
|
||||
user: 'telefonica',
|
||||
password: 'telefonica',
|
||||
type: ['mms']
|
||||
}];
|
||||
StoreProvisioning.provision(apns, 0, function() {
|
||||
assert.lengthOf(
|
||||
MockNavigatorSettings.mSettings['ril.data.cp.apns']['214']['07'], 2
|
||||
);
|
||||
assert.lengthOf(
|
||||
MockNavigatorSettings.mSettings['ril.data.apnSettings'][0], 1
|
||||
);
|
||||
});
|
||||
MockNavigatorSettings.mReplyToRequests();
|
||||
});
|
||||
test('Add APN to SIM2: Pannon MMS type', function() {
|
||||
var apns = [{
|
||||
carrier: 'Pannon GSM MMS',
|
||||
apn: 'Pannon.hu',
|
||||
user: 'Pannon',
|
||||
password: 'Pannon',
|
||||
type: ['mms']
|
||||
}];
|
||||
StoreProvisioning.provision(apns, 1, function(){
|
||||
assert.lengthOf(
|
||||
MockNavigatorSettings.mSettings['ril.data.cp.apns']['216']['01'], 2
|
||||
);
|
||||
assert.lengthOf(
|
||||
MockNavigatorSettings.mSettings['ril.data.apnSettings'][1], 1
|
||||
);
|
||||
});
|
||||
MockNavigatorSettings.mReplyToRequests();
|
||||
});
|
||||
});
|
||||
});
|
||||
993
gaia/apps/wappush/test/unit/wappush_test.js
Executable file
993
gaia/apps/wappush/test/unit/wappush_test.js
Executable file
|
|
@ -0,0 +1,993 @@
|
|||
/* global CpScreenHelper, loadBodyHTML, MockL10n, MessageDB,
|
||||
MockNavigatormozApps, MockNavigatorMozIccManager,
|
||||
MockNavigatormozSetMessageHandler, MockNavigatorSettings,
|
||||
MockNotification, MockNotificationHelper, MocksHelper,
|
||||
WapPushManager, MockParsedProvisioningDoc, SiSlScreenHelper */
|
||||
|
||||
'use strict';
|
||||
|
||||
require('/shared/js/event_dispatcher.js');
|
||||
require('/shared/test/unit/mocks/mock_dump.js');
|
||||
require('/shared/test/unit/mocks/mock_navigator_moz_apps.js');
|
||||
require('/shared/test/unit/mocks/mock_navigator_moz_set_message_handler.js');
|
||||
require('/shared/test/unit/mocks/mock_notification.js');
|
||||
require('/shared/test/unit/mocks/mock_notification_helper.js');
|
||||
require('/shared/test/unit/mocks/mock_navigator_moz_icc_manager.js');
|
||||
require('/shared/test/unit/mocks/mock_navigator_moz_settings.js');
|
||||
|
||||
require('/js/cp_screen_helper.js');
|
||||
require('/js/messagedb.js');
|
||||
require('/js/parsed_message.js');
|
||||
require('/js/provisioning.js');
|
||||
require('/js/si_sl_screen_helper.js');
|
||||
require('/js/utils.js');
|
||||
require('/js/wappush.js');
|
||||
|
||||
require('/shared/test/unit/mocks/mock_l10n.js');
|
||||
require('/test/unit/mock_link_action_handler.js');
|
||||
require('/test/unit/mock_whitelist.js');
|
||||
require('/test/unit/mock_parsed_doc.js');
|
||||
|
||||
var mocksHelperWapPush = new MocksHelper([
|
||||
'Dump',
|
||||
'LinkActionHandler',
|
||||
'NotificationHelper',
|
||||
'Notification',
|
||||
'WhiteList',
|
||||
'ParsedProvisioningDoc'
|
||||
]).init();
|
||||
|
||||
suite('WAP Push', function() {
|
||||
var realMozApps;
|
||||
var realMozIccManager;
|
||||
var realMozSettings;
|
||||
var realSetMessageHandler;
|
||||
var realMozL10n;
|
||||
var isDocumentHidden = false;
|
||||
|
||||
mocksHelperWapPush.attachTestHelpers();
|
||||
|
||||
suiteSetup(function() {
|
||||
realMozApps = navigator.mozApps;
|
||||
navigator.mozApps = MockNavigatormozApps;
|
||||
|
||||
realMozIccManager = navigator.mozIccManager;
|
||||
navigator.mozIccManager = MockNavigatorMozIccManager;
|
||||
|
||||
realMozL10n = navigator.mozL10n;
|
||||
navigator.mozL10n = MockL10n;
|
||||
|
||||
realMozSettings = navigator.mozSettings;
|
||||
navigator.mozSettings = MockNavigatorSettings;
|
||||
|
||||
realSetMessageHandler = navigator.mozSetMessageHandler;
|
||||
navigator.mozSetMessageHandler = MockNavigatormozSetMessageHandler;
|
||||
});
|
||||
|
||||
suiteTeardown(function() {
|
||||
navigator.mozApps = realMozApps;
|
||||
navigator.mozL10n = realMozL10n;
|
||||
navigator.mozSettings = realMozSettings;
|
||||
navigator.mozSetMessageHandler = realSetMessageHandler;
|
||||
});
|
||||
|
||||
setup(function(done) {
|
||||
var notificationGetStub = function notificationGet() {
|
||||
var options = {};
|
||||
options.icon = 'icon';
|
||||
options.tag = '0';
|
||||
return {
|
||||
then: function(onSuccess, onError, onProgress) {
|
||||
onSuccess([
|
||||
new MockNotification('1', options)
|
||||
]);
|
||||
}
|
||||
};
|
||||
};
|
||||
this.sinon.stub(MockNotification, 'get', notificationGetStub);
|
||||
|
||||
mocksHelperWapPush.setup();
|
||||
MockNavigatorSettings.createLock().set({ 'wap.push.enabled': 'true' });
|
||||
loadBodyHTML('/index.html');
|
||||
|
||||
Object.defineProperty(document, 'hidden', {
|
||||
configurable: true,
|
||||
get: function() {
|
||||
return isDocumentHidden;
|
||||
}
|
||||
});
|
||||
|
||||
MockNavigatorMozIccManager.addIcc(0, {});
|
||||
MockNavigatormozSetMessageHandler.mSetup();
|
||||
WapPushManager.init()
|
||||
.then(MessageDB.clear.bind(MessageDB))
|
||||
.then(done, done);
|
||||
MockNavigatormozApps.mTriggerLastRequestSuccess();
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
delete document.hidden;
|
||||
MockNavigatormozSetMessageHandler.mTeardown();
|
||||
MockNavigatorMozIccManager.mTeardown();
|
||||
MockNavigatormozApps.mTeardown();
|
||||
MockNavigatorSettings.mTeardown();
|
||||
mocksHelperWapPush.teardown();
|
||||
});
|
||||
|
||||
suite('init', function() {
|
||||
test('the message handlers are bound', function() {
|
||||
var handlers = MockNavigatormozSetMessageHandler.mMessageHandlers;
|
||||
assert.ok(handlers.notification);
|
||||
assert.equal(handlers['wappush-received'],
|
||||
WapPushManager.onWapPushReceived);
|
||||
});
|
||||
|
||||
test('the headers are empty', function() {
|
||||
assert.equal(document.getElementById('title-si-sl').textContent, '');
|
||||
assert.equal(document.getElementById('title-apn').textContent, '');
|
||||
assert.equal(document.getElementById('title-details').textContent, '');
|
||||
assert.equal(document.getElementById('title-pin').textContent, '');
|
||||
});
|
||||
});
|
||||
|
||||
suite('unsupported message', function() {
|
||||
var message = {
|
||||
sender: '+31641600986',
|
||||
contentType: 'text/foobar',
|
||||
content: '',
|
||||
serviceId: 0
|
||||
};
|
||||
|
||||
test('unsupported messages are discarded', function(done) {
|
||||
this.sinon.spy(MessageDB, 'put');
|
||||
|
||||
WapPushManager.onWapPushReceived(message).then(function() {
|
||||
done(function checks() {
|
||||
sinon.assert.notCalled(MessageDB.put);
|
||||
});
|
||||
}, done);
|
||||
});
|
||||
});
|
||||
|
||||
suite('receiving and displaying a SI message', function() {
|
||||
var message = {
|
||||
sender: '+31641600986',
|
||||
contentType: 'text/vnd.wap.si',
|
||||
content: '<si><indication href="http://www.mozilla.org">' +
|
||||
'check this out</indication></si>',
|
||||
serviceId: 0
|
||||
};
|
||||
|
||||
test('the notification is sent and populated correctly', function(done) {
|
||||
this.sinon.spy(MockNotificationHelper, 'send');
|
||||
|
||||
WapPushManager.onWapPushReceived(message).then(function() {
|
||||
done(function checks() {
|
||||
sinon.assert.calledWithMatch(MockNotificationHelper.send, {
|
||||
id: 'message-title',
|
||||
args: { title: message.sender }
|
||||
}, {
|
||||
bodyL10n: {
|
||||
args: {
|
||||
text: 'check this out',
|
||||
url: 'http://www.mozilla.org'
|
||||
},
|
||||
id: 'si-message-body'
|
||||
}
|
||||
});
|
||||
});
|
||||
}, done);
|
||||
});
|
||||
|
||||
test('the display is populated with the message contents', function(done) {
|
||||
var title = document.getElementById('title-si-sl');
|
||||
var screen = document.getElementById('si-sl-screen');
|
||||
var container = screen.querySelector('.container');
|
||||
var text = container.querySelector('p');
|
||||
var link = container.querySelector('a');
|
||||
|
||||
this.sinon.stub(Date, 'now').returns(0);
|
||||
this.sinon.spy(MockNotification.prototype, 'close');
|
||||
this.sinon.spy(MockL10n, 'setAttributes');
|
||||
this.sinon.spy(CpScreenHelper, 'hide');
|
||||
|
||||
WapPushManager.onWapPushReceived(message).then(function() {
|
||||
return WapPushManager.displayWapPushMessage(0);
|
||||
}).then(function() {
|
||||
sinon.assert.calledWith(MockNotification.get, { tag: 0 });
|
||||
sinon.assert.calledOnce(MockNotification.prototype.close);
|
||||
sinon.assert.calledOnce(CpScreenHelper.hide);
|
||||
sinon.assert.calledWith(MockL10n.setAttributes, title,
|
||||
'message-title', { title: message.sender });
|
||||
assert.equal(text.textContent, 'check this out');
|
||||
assert.equal(link.textContent, 'http://www.mozilla.org');
|
||||
assert.equal(link.dataset.url, 'http://www.mozilla.org');
|
||||
assert.equal(link.href, 'http://www.mozilla.org/');
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
suite('DSDS scenarios', function() {
|
||||
setup(function() {
|
||||
this.sinon.spy(MockNotificationHelper, 'send');
|
||||
|
||||
MockNavigatorMozIccManager.addIcc(1, {});
|
||||
});
|
||||
|
||||
test('the notification is populated correctly for SIM1', function(done) {
|
||||
WapPushManager.onWapPushReceived(message).then(function() {
|
||||
done(function checks() {
|
||||
sinon.assert.calledWithMatch(MockNotificationHelper.send, {
|
||||
args: {
|
||||
id: 1,
|
||||
title: message.sender
|
||||
},
|
||||
id: 'message-title-with-sim'
|
||||
});
|
||||
});
|
||||
}, done);
|
||||
});
|
||||
|
||||
test('the notification is populated correctly for SIM2', function(done) {
|
||||
message.serviceId = 1;
|
||||
|
||||
WapPushManager.onWapPushReceived(message).then(function() {
|
||||
done(function checks() {
|
||||
sinon.assert.calledWithMatch(MockNotificationHelper.send, {
|
||||
args: {
|
||||
id: 2,
|
||||
title: message.sender
|
||||
},
|
||||
id: 'message-title-with-sim'
|
||||
});
|
||||
});
|
||||
}, done);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
suite('receiving and displaying a SL message', function() {
|
||||
var message = {
|
||||
sender: '+31641600986',
|
||||
contentType: 'text/vnd.wap.sl',
|
||||
content: '<sl href="http://www.mozilla.org" />',
|
||||
serviceId: 0
|
||||
};
|
||||
|
||||
test('the notification is sent and populated correctly', function(done) {
|
||||
this.sinon.spy(MockNotificationHelper, 'send');
|
||||
|
||||
WapPushManager.onWapPushReceived(message).then(function() {
|
||||
sinon.assert.calledWithMatch(MockNotificationHelper.send, {
|
||||
id: 'message-title',
|
||||
args: { title: message.sender }
|
||||
}, {
|
||||
bodyL10n: {
|
||||
args: {
|
||||
url: 'http://www.mozilla.org'
|
||||
},
|
||||
id: 'sl-message-body'
|
||||
}
|
||||
});
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('the display is populated with the message contents', function(done) {
|
||||
var title = document.getElementById('title-si-sl');
|
||||
var screen = document.getElementById('si-sl-screen');
|
||||
var container = screen.querySelector('.container');
|
||||
var text = container.querySelector('p');
|
||||
var link = container.querySelector('a');
|
||||
|
||||
this.sinon.stub(Date, 'now').returns(0);
|
||||
this.sinon.spy(MockNotification.prototype, 'close');
|
||||
this.sinon.spy(MockL10n, 'setAttributes');
|
||||
this.sinon.spy(CpScreenHelper, 'hide');
|
||||
|
||||
WapPushManager.onWapPushReceived(message).then(function() {
|
||||
return WapPushManager.displayWapPushMessage(0);
|
||||
}).then(function() {
|
||||
done(function checks() {
|
||||
sinon.assert.calledWith(MockNotification.get, { tag: 0 });
|
||||
sinon.assert.calledOnce(MockNotification.prototype.close);
|
||||
sinon.assert.calledOnce(CpScreenHelper.hide);
|
||||
sinon.assert.calledWith(MockL10n.setAttributes, title,
|
||||
'message-title', { title: message.sender });
|
||||
assert.equal(text.textContent, '');
|
||||
assert.equal(link.textContent, 'http://www.mozilla.org');
|
||||
assert.equal(link.dataset.url, 'http://www.mozilla.org');
|
||||
assert.equal(link.href, 'http://www.mozilla.org/');
|
||||
});
|
||||
}, done);
|
||||
});
|
||||
});
|
||||
|
||||
suite('receiving and displaying a CP message', function() {
|
||||
var messages = {
|
||||
netwpin: {
|
||||
sender: '22997',
|
||||
contentType: 'text/vnd.wap.connectivity-xml',
|
||||
content: '<wap-provisioningdoc></wap-provisioningdoc>',
|
||||
authInfo: {
|
||||
pass: true,
|
||||
checked: true,
|
||||
sec: 'NETWPIN',
|
||||
mac: 'FAKEMAC',
|
||||
data: 'FAKEDATA'
|
||||
},
|
||||
serviceId: 0
|
||||
},
|
||||
userpin: {
|
||||
sender: '22997',
|
||||
contentType: 'text/vnd.wap.connectivity-xml',
|
||||
content: '<wap-provisioningdoc></wap-provisioningdoc>',
|
||||
authInfo: {
|
||||
pass: true,
|
||||
checked: true,
|
||||
sec: 'USERPIN',
|
||||
mac: 'FAKEMAC',
|
||||
data: 'FAKEDATA'
|
||||
},
|
||||
serviceId: 0
|
||||
}
|
||||
};
|
||||
|
||||
var apns = {
|
||||
none: [],
|
||||
single: [
|
||||
{ carrier: 'Test APN'}
|
||||
],
|
||||
multiple: [
|
||||
{ carrier: 'Test APN 1'},
|
||||
{ carrier: 'Test APN 2'},
|
||||
{ carrier: 'Test APN 3'}
|
||||
],
|
||||
};
|
||||
|
||||
test('the notification is sent and populated correctly', function(done) {
|
||||
this.sinon.spy(MockNotificationHelper, 'send');
|
||||
WapPushManager.onWapPushReceived(messages.netwpin).then(function() {
|
||||
done(function checks() {
|
||||
sinon.assert.calledWithMatch(MockNotificationHelper.send, {
|
||||
args: { title: messages.netwpin.sender },
|
||||
id: 'message-title'
|
||||
}, {
|
||||
bodyL10n: 'cp-message-received'
|
||||
});
|
||||
});
|
||||
}, done);
|
||||
});
|
||||
|
||||
test('the display shows warning message asking for install configuration',
|
||||
function(done) {
|
||||
var installCfgConfirmDialog =
|
||||
document.getElementById('cp-install-configuration-confirm');
|
||||
var acceptButton = installCfgConfirmDialog.querySelector('.accept');
|
||||
|
||||
this.sinon.stub(Date, 'now').returns(0);
|
||||
this.sinon.spy(MockNotification.prototype, 'close');
|
||||
this.sinon.spy(SiSlScreenHelper, 'hide');
|
||||
|
||||
WapPushManager.onWapPushReceived(messages.netwpin).then(function() {
|
||||
return WapPushManager.displayWapPushMessage(0);
|
||||
}).then(function() {
|
||||
done(function checks() {
|
||||
sinon.assert.notCalled(MockNotification.prototype.close);
|
||||
sinon.assert.calledOnce(SiSlScreenHelper.hide);
|
||||
assert.isFalse(acceptButton.classList.contains('hidden'),
|
||||
'the accept button should be visible');
|
||||
assert.isFalse(acceptButton.hidden);
|
||||
});
|
||||
}, done);
|
||||
}
|
||||
);
|
||||
|
||||
test('the app is closed when if the user does not accept the instalation',
|
||||
function(done) {
|
||||
var installCfgConfirmDialog =
|
||||
document.getElementById('cp-install-configuration-confirm');
|
||||
var cancelButton = installCfgConfirmDialog.querySelector('.cancel');
|
||||
|
||||
this.sinon.spy(WapPushManager, 'close');
|
||||
this.sinon.stub(Date, 'now').returns(0);
|
||||
this.sinon.spy(MockNotification.prototype, 'close');
|
||||
|
||||
WapPushManager.onWapPushReceived(messages.netwpin).then(function() {
|
||||
return WapPushManager.displayWapPushMessage(0);
|
||||
}).then(function() {
|
||||
done(function checks() {
|
||||
sinon.assert.notCalled(MockNotification.prototype.close);
|
||||
assert.isFalse(cancelButton.classList.contains('hidden'),
|
||||
'the accept button should be visible');
|
||||
assert.isFalse(cancelButton.hidden);
|
||||
cancelButton.click();
|
||||
sinon.assert.calledOnce(WapPushManager.close);
|
||||
});
|
||||
}, done);
|
||||
}
|
||||
);
|
||||
|
||||
test('show finish dialog when no apns are present in a CP message',
|
||||
function(done) {
|
||||
MockParsedProvisioningDoc.mSetup(apns.none);
|
||||
|
||||
var installCfgConfirmDialog =
|
||||
document.getElementById('cp-install-configuration-confirm');
|
||||
var acceptCfgButton = installCfgConfirmDialog.querySelector('.accept');
|
||||
var dialog = document.getElementById('cp-finish-confirm');
|
||||
var finishButton = dialog.querySelector('button');
|
||||
|
||||
this.sinon.stub(Date, 'now').returns(0);
|
||||
this.sinon.spy(MockNotification.prototype, 'close');
|
||||
|
||||
WapPushManager.onWapPushReceived(messages.netwpin).then(function() {
|
||||
return WapPushManager.displayWapPushMessage(0);
|
||||
}).then(function() {
|
||||
done(function checks() {
|
||||
sinon.assert.notCalled(MockNotification.prototype.close);
|
||||
|
||||
acceptCfgButton.click();
|
||||
|
||||
assert.isFalse(finishButton.classList.contains('hidden'),
|
||||
'the accept button should be visible');
|
||||
assert.isFalse(finishButton.hidden);
|
||||
});
|
||||
}, done);
|
||||
}
|
||||
);
|
||||
|
||||
test('details of apn are shown after warning message when a single apn ' +
|
||||
'is defined in CP message',
|
||||
function(done) {
|
||||
MockParsedProvisioningDoc.mSetup(apns.single);
|
||||
|
||||
var title = document.getElementById('title-details');
|
||||
var screen = document.getElementById('cp-details-screen');
|
||||
var details = screen.querySelector('.message');
|
||||
var installCfgConfirmDialog =
|
||||
document.getElementById('cp-install-configuration-confirm');
|
||||
var acceptCfgButton = installCfgConfirmDialog.querySelector('.accept');
|
||||
|
||||
this.sinon.stub(Date, 'now').returns(0);
|
||||
this.sinon.spy(MockNotification.prototype, 'close');
|
||||
this.sinon.spy(MockL10n, 'setAttributes');
|
||||
|
||||
WapPushManager.onWapPushReceived(messages.netwpin).then(function() {
|
||||
return WapPushManager.displayWapPushMessage(0);
|
||||
}).then(function() {
|
||||
done(function checks() {
|
||||
sinon.assert.notCalled(MockNotification.prototype.close);
|
||||
|
||||
acceptCfgButton.click();
|
||||
|
||||
sinon.assert.calledWith(MockL10n.setAttributes, title,
|
||||
'message-title', { title: messages.netwpin.sender });
|
||||
assert.isFalse(screen.classList.contains('left') ||
|
||||
screen.classList.contains('right'),
|
||||
'the details screen should be visible');
|
||||
|
||||
var apnList = details.getElementsByTagName('li');
|
||||
assert.equal(apns.single.length, apnList.length);
|
||||
for (var i = 0; i < apnList.length; i++) {
|
||||
assert.equal(apns.single[i].carrier,
|
||||
apnList[i].childNodes[1].textContent);
|
||||
}
|
||||
});
|
||||
}, done);
|
||||
}
|
||||
);
|
||||
|
||||
test('apn list screen is shown after warning message when multiple apns ' +
|
||||
'are defined in CP message',
|
||||
function(done) {
|
||||
MockParsedProvisioningDoc.mSetup(apns.multiple);
|
||||
|
||||
var titleApn = document.getElementById('title-apn');
|
||||
var titleDetails = document.getElementById('title-details');
|
||||
var apnScreen = document.getElementById('cp-apn-screen');
|
||||
var detailsScreen = document.getElementById('cp-details-screen');
|
||||
var apnsElement = apnScreen.querySelector('.message');
|
||||
var installCfgConfirmDialog =
|
||||
document.getElementById('cp-install-configuration-confirm');
|
||||
var acceptCfgButton = installCfgConfirmDialog.querySelector('.accept');
|
||||
|
||||
this.sinon.stub(Date, 'now').returns(0);
|
||||
this.sinon.spy(MockNotification.prototype, 'close');
|
||||
this.sinon.spy(MockL10n, 'setAttributes');
|
||||
|
||||
WapPushManager.onWapPushReceived(messages.netwpin).then(function() {
|
||||
return WapPushManager.displayWapPushMessage(0);
|
||||
}).then(function() {
|
||||
done(function checks() {
|
||||
sinon.assert.notCalled(MockNotification.prototype.close);
|
||||
|
||||
acceptCfgButton.click();
|
||||
|
||||
sinon.assert.calledWith(MockL10n.setAttributes, titleApn,
|
||||
'message-title', { title: messages.netwpin.sender });
|
||||
assert.isFalse(apnScreen.classList.contains('left') ||
|
||||
apnScreen.classList.contains('right'),
|
||||
'the apn screen should be visible');
|
||||
|
||||
sinon.assert.calledWith(MockL10n.setAttributes, titleDetails,
|
||||
'message-title', { title: messages.netwpin.sender });
|
||||
assert.isTrue(detailsScreen.classList.contains('right'),
|
||||
'the details screen should not be visible');
|
||||
|
||||
var apnList = apnsElement.getElementsByTagName('li');
|
||||
assert.equal(apns.multiple.length, apnList.length);
|
||||
for (var i = 0; i < apnList.length; i++) {
|
||||
assert.equal(apns.multiple[i].carrier,
|
||||
apnList[i].childNodes[0].childNodes[1].textContent);
|
||||
}
|
||||
|
||||
// Test that click on apn element shows its details.
|
||||
apnList[0].childNodes[0].click();
|
||||
assert.isFalse(detailsScreen.classList.contains('left') ||
|
||||
detailsScreen.classList.contains('right'),
|
||||
'the details screen should be visible');
|
||||
|
||||
assert.isTrue(apnScreen.classList.contains('left'),
|
||||
'the apn screen should not be visible');
|
||||
});
|
||||
}, done);
|
||||
}
|
||||
);
|
||||
|
||||
test('the display is populated with the NETWPIN message contents after ' +
|
||||
'accepting installation ',
|
||||
function(done) {
|
||||
MockParsedProvisioningDoc.mSetup(apns.single);
|
||||
|
||||
var title = document.getElementById('title-pin');
|
||||
var screen = document.getElementById('cp-pin-screen');
|
||||
var acceptButton = document.getElementById('details-accept');
|
||||
var pin = screen.querySelector('input');
|
||||
var installCfgConfirmDialog =
|
||||
document.getElementById('cp-install-configuration-confirm');
|
||||
var acceptInstallButton =
|
||||
installCfgConfirmDialog.querySelector('.accept');
|
||||
|
||||
this.sinon.stub(Date, 'now').returns(0);
|
||||
this.sinon.spy(MockNotification.prototype, 'close');
|
||||
this.sinon.spy(MockL10n, 'setAttributes');
|
||||
|
||||
WapPushManager.onWapPushReceived(messages.netwpin).then(function() {
|
||||
return WapPushManager.displayWapPushMessage(0);
|
||||
}).then(function() {
|
||||
done(function checks() {
|
||||
sinon.assert.notCalled(MockNotification.prototype.close);
|
||||
acceptInstallButton.click();
|
||||
acceptButton.click();
|
||||
|
||||
sinon.assert.calledWith(MockL10n.setAttributes, title,
|
||||
'message-title', { title: messages.netwpin.sender });
|
||||
assert.isFalse(screen.classList.contains('left') ||
|
||||
screen.classList.contains('right'),
|
||||
'the details screen should be visible');
|
||||
assert.equal(pin.type, 'hidden');
|
||||
});
|
||||
}, done);
|
||||
}
|
||||
);
|
||||
|
||||
test('the display is populated with the USERPIN message contents after ' +
|
||||
'accepting installation ',
|
||||
function(done) {
|
||||
MockParsedProvisioningDoc.mSetup(apns.single);
|
||||
|
||||
var title = document.getElementById('title-pin');
|
||||
var screen = document.getElementById('cp-pin-screen');
|
||||
var acceptButton = document.getElementById('details-accept');
|
||||
var pin = screen.querySelector('input');
|
||||
var installCfgConfirmDialog =
|
||||
document.getElementById('cp-install-configuration-confirm');
|
||||
var acceptInstallButton =
|
||||
installCfgConfirmDialog.querySelector('.accept');
|
||||
|
||||
this.sinon.stub(Date, 'now').returns(0);
|
||||
this.sinon.spy(MockNotification.prototype, 'close');
|
||||
this.sinon.spy(MockL10n, 'setAttributes');
|
||||
|
||||
WapPushManager.onWapPushReceived(messages.userpin).then(function() {
|
||||
return WapPushManager.displayWapPushMessage(0);
|
||||
}).then(function() {
|
||||
done(function checks() {
|
||||
sinon.assert.notCalled(MockNotification.prototype.close);
|
||||
acceptInstallButton.click();
|
||||
acceptButton.click();
|
||||
|
||||
sinon.assert.calledWith(MockL10n.setAttributes, title,
|
||||
'message-title', { title: messages.netwpin.sender });
|
||||
assert.isFalse(screen.classList.contains('left') ||
|
||||
screen.classList.contains('right'),
|
||||
'the details screen should be visible');
|
||||
assert.equal(pin.type, 'number');
|
||||
});
|
||||
}, done);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
suite('handling out-of-order reception of messages', function() {
|
||||
var messages = {
|
||||
old: {
|
||||
sender: '+31641600986',
|
||||
contentType: 'text/vnd.wap.si',
|
||||
content: '<si>' +
|
||||
'<indication si-id="gaia-test@mozilla.org" ' +
|
||||
' created="2013-09-03T12:35:33Z">' +
|
||||
'old message' +
|
||||
'</indication>' +
|
||||
'</si>',
|
||||
serviceId: 0
|
||||
},
|
||||
current: {
|
||||
sender: '+31641600986',
|
||||
contentType: 'text/vnd.wap.si',
|
||||
content: '<si>' +
|
||||
'<indication si-id="gaia-test@mozilla.org" ' +
|
||||
' created="2013-09-03T14:35:33Z">' +
|
||||
'current message' +
|
||||
'</indication>' +
|
||||
'</si>',
|
||||
serviceId: 0
|
||||
}
|
||||
};
|
||||
|
||||
test('a new message updates an older one', function(done) {
|
||||
var screen = document.getElementById('si-sl-screen');
|
||||
var container = screen.querySelector('.container');
|
||||
var text = container.querySelector('p');
|
||||
|
||||
this.sinon.spy(MockNotificationHelper, 'send');
|
||||
this.sinon.stub(Date, 'now')
|
||||
.onFirstCall().returns(0)
|
||||
.onSecondCall().returns(1);
|
||||
|
||||
WapPushManager.onWapPushReceived(messages.old).then(function() {
|
||||
return WapPushManager.onWapPushReceived(messages.current);
|
||||
}).then(function() {
|
||||
return WapPushManager.displayWapPushMessage(0);
|
||||
}).then(function() {
|
||||
done(function checks() {
|
||||
sinon.assert.alwaysCalledWithMatch(MockNotificationHelper.send, {
|
||||
id: 'message-title',
|
||||
args: { title: messages.current.sender }
|
||||
}, {
|
||||
tag: '0'
|
||||
});
|
||||
assert.equal(text.textContent, 'current message');
|
||||
});
|
||||
}, done);
|
||||
});
|
||||
|
||||
test('a new message updates an older one which is being viewed',
|
||||
function(done) {
|
||||
this.sinon.spy(SiSlScreenHelper, 'populateScreen');
|
||||
this.sinon.stub(Date, 'now')
|
||||
.onFirstCall().returns(0)
|
||||
.onSecondCall().returns(1);
|
||||
|
||||
WapPushManager.onWapPushReceived(messages.old).then(function() {
|
||||
return WapPushManager.displayWapPushMessage(0);
|
||||
}).then(function() {
|
||||
sinon.assert.calledOnce(SiSlScreenHelper.populateScreen);
|
||||
sinon.assert.calledWithMatch(SiSlScreenHelper.populateScreen, {
|
||||
text: 'old message'
|
||||
});
|
||||
SiSlScreenHelper.populateScreen.reset();
|
||||
return WapPushManager.onWapPushReceived(messages.current);
|
||||
}).then(function() {
|
||||
sinon.assert.calledOnce(SiSlScreenHelper.populateScreen);
|
||||
sinon.assert.calledWithMatch(SiSlScreenHelper.populateScreen, {
|
||||
text: 'current message'
|
||||
});
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('an outdated message does not replace a newer one', function(done) {
|
||||
var screen = document.getElementById('si-sl-screen');
|
||||
var container = screen.querySelector('.container');
|
||||
var text = container.querySelector('p');
|
||||
|
||||
this.sinon.spy(MockNotificationHelper, 'send');
|
||||
this.sinon.stub(Date, 'now')
|
||||
.onFirstCall().returns(0)
|
||||
.onSecondCall().returns(1);
|
||||
|
||||
WapPushManager.onWapPushReceived(messages.current).then(function() {
|
||||
return WapPushManager.onWapPushReceived(messages.old);
|
||||
}).then(function() {
|
||||
return WapPushManager.displayWapPushMessage(0);
|
||||
}).then(function() {
|
||||
done(function checks() {
|
||||
sinon.assert.calledOnce(MockNotificationHelper.send);
|
||||
sinon.assert.calledWithMatch(MockNotificationHelper.send, {
|
||||
id: 'message-title',
|
||||
args: { title: messages.current.sender }
|
||||
}, {
|
||||
tag: '0'
|
||||
});
|
||||
assert.equal(text.textContent, 'current message');
|
||||
});
|
||||
}, done);
|
||||
});
|
||||
});
|
||||
|
||||
suite('handling expired messages', function() {
|
||||
var message = {
|
||||
sender: '+31641600986',
|
||||
contentType: 'text/vnd.wap.si',
|
||||
content: '<si>' +
|
||||
'<indication si-expires="2013-09-03T10:35:33Z">' +
|
||||
'check this out' +
|
||||
'</indication>' +
|
||||
'</si>',
|
||||
serviceId: 0
|
||||
};
|
||||
|
||||
// UI elements
|
||||
var screen;
|
||||
var container;
|
||||
var text;
|
||||
|
||||
setup(function() {
|
||||
screen = document.getElementById('si-sl-screen');
|
||||
container = screen.querySelector('.container');
|
||||
text = container.querySelector('p');
|
||||
});
|
||||
|
||||
test('the message is expired upon arrival', function(done) {
|
||||
this.sinon.stub(Date, 'now')
|
||||
.onFirstCall().returns(0)
|
||||
.onSecondCall().returns(1378204533001);
|
||||
this.sinon.spy(MockNotificationHelper, 'send');
|
||||
|
||||
WapPushManager.onWapPushReceived(message).then(function() {
|
||||
sinon.assert.notCalled(MockNotificationHelper.send);
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('the message is expired after arrival', function(done) {
|
||||
this.sinon.stub(Date, 'now')
|
||||
.onFirstCall().returns(0)
|
||||
.onSecondCall().returns(0)
|
||||
.onThirdCall().returns(1378204533001);
|
||||
this.sinon.spy(MockL10n, 'setAttributes');
|
||||
|
||||
WapPushManager.onWapPushReceived(message).then(function() {
|
||||
return WapPushManager.displayWapPushMessage(0);
|
||||
}).then(function() {
|
||||
sinon.assert.calledWith(MockL10n.setAttributes, text,
|
||||
'this-message-has-expired');
|
||||
}).then(done, done);
|
||||
});
|
||||
});
|
||||
|
||||
suite('handling actions', function() {
|
||||
var clock;
|
||||
var messages = {
|
||||
none: {
|
||||
sender: '+31641600986',
|
||||
contentType: 'text/vnd.wap.si',
|
||||
content: '<si>' +
|
||||
'<indication si-id="gaia-test@mozilla.org"' +
|
||||
' action="signal-none">' +
|
||||
'check this out' +
|
||||
'</indication>' +
|
||||
'</si>',
|
||||
serviceId: 0
|
||||
},
|
||||
signal_high: {
|
||||
sender: '+31641600986',
|
||||
contentType: 'text/vnd.wap.si',
|
||||
content: '<si>' +
|
||||
'<indication si-id="gaia-test@mozilla.org"' +
|
||||
' action="signal-high">' +
|
||||
'check this out' +
|
||||
'</indication>' +
|
||||
'</si>',
|
||||
serviceId: 0
|
||||
},
|
||||
execute_high: {
|
||||
sender: '+31641600986',
|
||||
contentType: 'text/vnd.wap.sl',
|
||||
content: '<sl href="http://www.mozilla.org" action="execute-high"/>',
|
||||
serviceId: 0
|
||||
},
|
||||
no_action: {
|
||||
sender: '+31641600986',
|
||||
contentType: 'text/vnd.wap.si',
|
||||
content: '<si>' +
|
||||
'<indication si-id="gaia-test@mozilla.org">' +
|
||||
'check this out' +
|
||||
'</indication>' +
|
||||
'</si>',
|
||||
serviceId: 0
|
||||
},
|
||||
delete: {
|
||||
sender: '+31641600986',
|
||||
contentType: 'text/vnd.wap.si',
|
||||
content: '<si>' +
|
||||
'<indication si-id="gaia-test@mozilla.org"' +
|
||||
' action="delete">' +
|
||||
'check this out' +
|
||||
'</indication>' +
|
||||
'</si>',
|
||||
serviceId: 0
|
||||
}
|
||||
};
|
||||
|
||||
setup(function() {
|
||||
clock = this.sinon.useFakeTimers();
|
||||
isDocumentHidden = true;
|
||||
this.sinon.spy(MockNotificationHelper, 'send');
|
||||
this.sinon.spy(window, 'close');
|
||||
});
|
||||
|
||||
/* XXX: Workaround for bug 981521. We shouldn't send notifications for
|
||||
* signal-none messages but we do until we'll have another way for the user
|
||||
* to find & display them. */
|
||||
test('action=signal-none sends a notification', function(done) {
|
||||
WapPushManager.onWapPushReceived(messages.none).then(function() {
|
||||
clock.tick(100);
|
||||
sinon.assert.calledWithMatch(MockNotificationHelper.send, {
|
||||
id: 'message-title',
|
||||
args: { title: messages.none.sender }
|
||||
}, {
|
||||
bodyL10n: {
|
||||
id: 'si-message-body',
|
||||
args: { text: 'check this out' }
|
||||
}
|
||||
});
|
||||
sinon.assert.calledOnce(window.close);
|
||||
assert.isFalse(MockNavigatormozApps.mAppWasLaunched);
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('action=signal-high displays the SI message immediately',
|
||||
function(done) {
|
||||
this.sinon.stub(SiSlScreenHelper, 'populateScreen', function() {
|
||||
clock.tick(100);
|
||||
sinon.assert.notCalled(MockNotificationHelper.send);
|
||||
sinon.assert.notCalled(window.close);
|
||||
sinon.assert.calledOnce(SiSlScreenHelper.populateScreen);
|
||||
assert.isTrue(MockNavigatormozApps.mAppWasLaunched);
|
||||
done();
|
||||
});
|
||||
|
||||
WapPushManager.onWapPushReceived(messages.signal_high);
|
||||
});
|
||||
|
||||
test('action=execute-high displays the SL message immediately',
|
||||
function(done) {
|
||||
this.sinon.stub(SiSlScreenHelper, 'populateScreen', function() {
|
||||
clock.tick(100);
|
||||
sinon.assert.notCalled(MockNotificationHelper.send);
|
||||
sinon.assert.notCalled(window.close);
|
||||
sinon.assert.calledOnce(SiSlScreenHelper.populateScreen);
|
||||
assert.isTrue(MockNavigatormozApps.mAppWasLaunched);
|
||||
done();
|
||||
});
|
||||
|
||||
WapPushManager.onWapPushReceived(messages.execute_high);
|
||||
});
|
||||
|
||||
test('action=delete causes notifications of the deleted messages to ' +
|
||||
'be removed', function(done) {
|
||||
this.sinon.spy(MockNotification.prototype, 'close');
|
||||
this.sinon.stub(Date, 'now').returns(0);
|
||||
|
||||
WapPushManager.onWapPushReceived(messages.no_action).then(function() {
|
||||
return WapPushManager.onWapPushReceived(messages.delete);
|
||||
}).then(function() {
|
||||
sinon.assert.calledOnce(MockNotificationHelper.send);
|
||||
sinon.assert.calledWith(MockNotification.get, { tag: 0 });
|
||||
sinon.assert.calledOnce(MockNotification.prototype.close);
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('action=delete causes the app to be closed if the message is being ' +
|
||||
'displayed', function(done) {
|
||||
this.sinon.spy(MockNotification.prototype, 'close');
|
||||
this.sinon.stub(Date, 'now').returns(0);
|
||||
|
||||
WapPushManager.onWapPushReceived(messages.no_action).then(function() {
|
||||
return WapPushManager.displayWapPushMessage(0);
|
||||
}).then(function() {
|
||||
MockNotification.prototype.close.reset();
|
||||
MockNotification.get.reset();
|
||||
return WapPushManager.onWapPushReceived(messages.delete);
|
||||
}).then(function() {
|
||||
sinon.assert.calledWith(MockNotification.get, { tag: 0 });
|
||||
sinon.assert.calledOnce(MockNotification.prototype.close);
|
||||
clock.tick(100);
|
||||
sinon.assert.calledOnce(window.close);
|
||||
}).then(done, done);
|
||||
});
|
||||
});
|
||||
|
||||
suite('automatic closing of the application', function() {
|
||||
var message = {
|
||||
sender: '+31641600986',
|
||||
contentType: 'text/vnd.wap.si',
|
||||
content: '<si><indication href="http://www.mozilla.org">' +
|
||||
'check this out</indication></si>',
|
||||
serviceId: 0
|
||||
};
|
||||
|
||||
var clock;
|
||||
|
||||
setup(function() {
|
||||
clock = this.sinon.useFakeTimers();
|
||||
this.sinon.spy(window, 'close');
|
||||
});
|
||||
|
||||
test('the app is closed after displaying a notification', function(done) {
|
||||
isDocumentHidden = true;
|
||||
|
||||
WapPushManager.onWapPushReceived(message).then(function() {
|
||||
done(function checks() {
|
||||
clock.tick(100);
|
||||
sinon.assert.calledOnce(window.close);
|
||||
});
|
||||
}, done);
|
||||
});
|
||||
|
||||
test('the app is not closed if it is visible', function(done) {
|
||||
isDocumentHidden = false;
|
||||
|
||||
WapPushManager.onWapPushReceived(message).then(function() {
|
||||
done(function checks() {
|
||||
clock.tick(100);
|
||||
sinon.assert.notCalled(window.close);
|
||||
});
|
||||
}, done);
|
||||
});
|
||||
|
||||
test('the app is closed only after all messages have been processed',
|
||||
function(done) {
|
||||
isDocumentHidden = true;
|
||||
|
||||
WapPushManager.onWapPushReceived(message).then(function() {
|
||||
sinon.assert.notCalled(window.close);
|
||||
return WapPushManager.onWapPushReceived(message);
|
||||
}).then(function() {
|
||||
done(function checks() {
|
||||
sinon.assert.notCalled(window.close);
|
||||
clock.tick(100);
|
||||
sinon.assert.calledOnce(window.close);
|
||||
});
|
||||
}, done);
|
||||
}
|
||||
);
|
||||
|
||||
test('prevent the app from closing if it becomes visible', function(done) {
|
||||
isDocumentHidden = true;
|
||||
|
||||
WapPushManager.onWapPushReceived(message).then(function() {
|
||||
sinon.assert.notCalled(window.close);
|
||||
return WapPushManager.onWapPushReceived(message);
|
||||
}).then(function() {
|
||||
done(function checks() {
|
||||
sinon.assert.notCalled(window.close);
|
||||
clock.tick(50);
|
||||
isDocumentHidden = false;
|
||||
WapPushManager.onVisibilityChange();
|
||||
clock.tick(50);
|
||||
sinon.assert.notCalled(window.close);
|
||||
});
|
||||
}, done);
|
||||
});
|
||||
|
||||
test('the app is closed when hidden', function() {
|
||||
isDocumentHidden = false;
|
||||
WapPushManager.onVisibilityChange();
|
||||
clock.tick(100);
|
||||
isDocumentHidden = true;
|
||||
WapPushManager.onVisibilityChange();
|
||||
clock.tick(100);
|
||||
sinon.assert.calledOnce(window.close);
|
||||
});
|
||||
});
|
||||
});
|
||||
Loading…
Reference in a new issue