Merge from mozilla-central

This commit is contained in:
Ehsan Akhgari 2012-03-17 12:32:28 -04:00
commit 9b15a69d3e
26 changed files with 880 additions and 277 deletions

View File

@ -2,9 +2,9 @@
#export GONK_PRODUCT=generic
#gonk="/home/cjones/mozilla/gonk-toolchain-$GONK_TOOLCHAIN_VERSION"
mk_add_options MOZ_OBJDIR=@TOPSRCDIR@/objdir-prof-gonk
mk_add_options MOZ_OBJDIR=@TOPSRCDIR@/obj-b2g
mk_add_options MOZ_MAKE_FLAGS="-s -j16"
mk_add_options MOZ_MAKE_FLAGS="-j8"
ac_add_options --enable-application=b2g

View File

@ -1,5 +1,5 @@
<?xml version="1.0"?>
<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1331241604000">
<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1331848989000">
<emItems>
<emItem blockID="i58" id="webmaster@buzzzzvideos.info">
<versionRange minVersion="0" maxVersion="*">
@ -65,8 +65,8 @@
<versionRange minVersion="0" maxVersion="*">
</versionRange>
</emItem>
<emItem blockID="i72" id="{4ED1F68A-5463-4931-9384-8FFF5ED91D92}">
<versionRange minVersion="0" maxVersion="3.4.1.194" severity="1">
<emItem blockID="i72" os="WINNT" id="{4ED1F68A-5463-4931-9384-8FFF5ED91D92}">
<versionRange minVersion="3.4.1" maxVersion="3.4.1.194" severity="1">
</versionRange>
</emItem>
<emItem blockID="i40" id="{28387537-e3f9-4ed7-860c-11e69af4a8a0}">
@ -153,6 +153,10 @@
<versionRange minVersion="2.0" maxVersion="2.0">
</versionRange>
</emItem>
<emItem blockID="i73" id="a1g0a9g219d@a1.com">
<versionRange minVersion="0" maxVersion="*">
</versionRange>
</emItem>
<emItem blockID="i51" id="admin@youtubeplayer.com">
<versionRange minVersion="0" maxVersion="*">
</versionRange>

View File

@ -229,6 +229,9 @@ body[narrow] #launcher[session] {
padding: 14px 6px;
min-width: 88px;
max-width: 176px;
max-height: 85px;
vertical-align: top;
white-space: normal;
background: transparent padding-box;
border: 1px solid transparent;
border-radius: 2.5px;
@ -241,9 +244,6 @@ body[narrow] #launcher[session] {
body[narrow] #launcher[session] > .launchButton {
margin: 4px 1px;
max-height: 85px;
vertical-align: top;
white-space: normal;
}
.launchButton:hover {

View File

@ -110,6 +110,15 @@ let gBrowserThumbnails = {
let channel = aBrowser.docShell.currentDocumentChannel;
// No valid document channel. We shouldn't take a screenshot.
if (!channel)
return false;
// Don't take screenshots of internally redirecting about: pages.
// This includes error pages.
if (channel.originalURI.schemeIs("about"))
return false;
try {
// If the channel is a nsIHttpChannel get its http status code.
let httpChannel = channel.QueryInterface(Ci.nsIHttpChannel);

View File

@ -94,7 +94,11 @@ let gDrop = {
// A new link was dragged onto the grid. Create it by pinning its URL.
let dt = aEvent.dataTransfer;
let [url, title] = dt.getData("text/x-moz-url").split(/[\r\n]+/);
gPinnedLinks.pin({url: url, title: title}, index);
let link = {url: url, title: title};
gPinnedLinks.pin(link, index);
// Make sure the newly added link is not blocked.
gBlockedLinks.unblock(link);
}
},

View File

@ -92,6 +92,7 @@ nsContextMenu.prototype = {
} catch (e) { }
this.isTextSelected = this.isTextSelection();
this.isContentSelected = this.isContentSelection();
this.onPlainTextLink = false;
// Initialize (disable/remove) menu items.
this.initItems();
@ -132,7 +133,6 @@ nsContextMenu.prototype = {
// Time to do some bad things and see if we've highlighted a URL that
// isn't actually linked.
var onPlainTextLink = false;
if (this.isTextSelected && !this.onLink) {
// Ok, we have some text, let's figure out if it looks like a URL.
let selection = document.commandDispatcher.focusedWindow
@ -190,14 +190,14 @@ nsContextMenu.prototype = {
if (uri && uri.host) {
this.linkURI = uri;
this.linkURL = this.linkURI.spec;
onPlainTextLink = true;
this.onPlainTextLink = true;
}
}
var shouldShow = this.onSaveableLink || isMailtoInternal || onPlainTextLink;
var shouldShow = this.onSaveableLink || isMailtoInternal || this.onPlainTextLink;
this.showItem("context-openlink", shouldShow);
this.showItem("context-openlinkintab", shouldShow);
this.showItem("context-openlinkincurrent", onPlainTextLink);
this.showItem("context-openlinkincurrent", this.onPlainTextLink);
this.showItem("context-sep-open", shouldShow);
},
@ -222,9 +222,9 @@ nsContextMenu.prototype = {
this.showItem("context-savepage", shouldShow);
this.showItem("context-sendpage", shouldShow);
// Save+Send link depends on whether we're in a link.
this.showItem("context-savelink", this.onSaveableLink);
this.showItem("context-sendlink", this.onSaveableLink);
// Save+Send link depends on whether we're in a link, or selected text matches valid URL pattern.
this.showItem("context-savelink", this.onSaveableLink || this.onPlainTextLink);
this.showItem("context-sendlink", this.onSaveableLink || this.onPlainTextLink);
// Save image depends on having loaded its content, video and audio don't.
this.showItem("context-saveimage", this.onLoadedImage || this.onCanvas);
@ -310,7 +310,7 @@ nsContextMenu.prototype = {
this.showItem("context-bookmarkpage",
!(this.isContentSelected || this.onTextInput || this.onLink ||
this.onImage || this.onVideo || this.onAudio));
this.showItem("context-bookmarklink", this.onLink && !this.onMailtoLink);
this.showItem("context-bookmarklink", (this.onLink && !this.onMailtoLink) || this.onPlainTextLink);
this.showItem("context-searchselect", isTextSelected);
this.showItem("context-keywordfield",
this.onTextInput && this.onKeywordField);
@ -1073,9 +1073,15 @@ nsContextMenu.prototype = {
// Save URL of clicked-on link.
saveLink: function() {
var doc = this.target.ownerDocument;
var linkText;
// If selected text is found to match valid URL pattern.
if (this.onPlainTextLink)
linkText = document.commandDispatcher.focusedWindow.getSelection().toString().trim();
else
linkText = this.linkText();
urlSecurityCheck(this.linkURL, doc.nodePrincipal);
this.saveHelper(this.linkURL, this.linkText(), null, true, doc);
this.saveHelper(this.linkURL, linkText, null, true, doc);
},
sendLink: function() {
@ -1390,8 +1396,14 @@ nsContextMenu.prototype = {
},
bookmarkLink: function CM_bookmarkLink() {
var linkText;
// If selected text is found to match valid URL pattern.
if (this.onPlainTextLink)
linkText = document.commandDispatcher.focusedWindow.getSelection().toString().trim();
else
linkText = this.linkText();
window.top.PlacesCommandHook.bookmarkLink(PlacesUtils.bookmarksMenuFolderId, this.linkURL,
this.linkText());
linkText);
},
addBookmarkForFrame: function CM_addBookmarkForFrame() {

View File

@ -25,6 +25,7 @@ _BROWSER_FILES = \
browser_newtab_bug723121.js \
browser_newtab_bug725996.js \
browser_newtab_bug734043.js \
browser_newtab_bug735987.js \
head.js \
$(NULL)

View File

@ -0,0 +1,22 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
function runTests() {
setLinks("0,1,2,3,4,5,6,7,8");
setPinnedLinks("");
yield addNewTabPageTab();
checkGrid("0,1,2,3,4,5,6,7,8");
yield simulateDrop(cells[1]);
checkGrid("0,99p,1,2,3,4,5,6,7");
yield blockCell(cells[1]);
checkGrid("0,1,2,3,4,5,6,7,8");
yield simulateDrop(cells[1]);
checkGrid("0,99p,1,2,3,4,5,6,7");
yield blockCell(cells[1]);
checkGrid("0,1,2,3,4,5,6,7,8");
}

View File

@ -58,6 +58,8 @@ Browser context menu subtest.
<menuitem></menuitem>
</menu>
</div>
<div id="test-select-text">Lorem ipsum dolor sit amet, consectetuer adipiscing elit.</div>
<div id="test-select-text-link">http://mozilla.com</div>
</body>
</html>

View File

@ -20,6 +20,7 @@ Browser context menu tests.
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
Components.utils.import("resource://gre/modules/InlineSpellChecker.jsm");
Components.utils.import("resource://gre/modules/Services.jsm");
const Cc = Components.classes;
const Ci = Components.interfaces;
@ -72,6 +73,16 @@ function invokeItemAction(generatedItemId)
ok(!pagemenu.hasAttribute("hopeless"), "attribute got removed");
}
function selectText(element) {
// Clear any previous selections before selecting new element.
subwindow.getSelection().removeAllRanges();
var div = subwindow.document.createRange();
div.setStartBefore(element);
div.setEndAfter(element);
subwindow.getSelection().addRange(div);
}
function getVisibleMenuItems(aMenu, aData) {
var items = [];
var accessKeys = {};
@ -667,6 +678,45 @@ function runTest(testNum) {
"context-viewinfo", true
].concat(inspectItems));
closeContextMenu();
selectText(selecttext); // Select text prior to opening context menu.
openContextMenuFor(selecttext); // Invoke context menu for next test.
return;
case 22:
// Context menu for selected text
if (Services.appinfo.OS == "Darwin") {
// This test is only enabled on Mac due to bug 736399.
checkContextMenu(["context-copy", true,
"context-selectall", true,
"---", null,
"context-searchselect", true,
"context-viewpartialsource-selection", true
].concat(inspectItems));
}
closeContextMenu();
selectText(selecttextlink); // Select text prior to opening context menu.
openContextMenuFor(selecttextlink); // Invoke context menu for next test.
return;
case 23:
// Context menu for selected text which matches valid URL pattern
if (Services.appinfo.OS == "Darwin") {
// This test is only enabled on Mac due to bug 736399.
checkContextMenu(["context-openlinkincurrent", true,
"context-openlinkintab", true,
"context-openlink", true,
"---", null,
"context-bookmarklink", true,
"context-savelink", true,
"context-sendlink", true,
"context-copy", true,
"context-selectall", true,
"---", null,
"context-searchselect", true,
"context-viewpartialsource-selection", true
].concat(inspectItems));
}
closeContextMenu();
subwindow.close();
SimpleTest.finish();
@ -674,7 +724,6 @@ function runTest(testNum) {
/*
* Other things that would be nice to test:
* - selected text
* - spelling / misspelled word (in text input?)
* - check state of disabled items
* - test execution of menu items (maybe as a separate test?)
@ -734,6 +783,8 @@ function startTest() {
contenteditable.focus(); // content editable needs to be focused to enable spellcheck
inputspell = subwindow.document.getElementById("test-input-spellcheck");
pagemenu = subwindow.document.getElementById("test-pagemenu");
selecttext = subwindow.document.getElementById("test-select-text");
selecttextlink = subwindow.document.getElementById("test-select-text-link");
contextMenu.addEventListener("popupshown", function() { runTest(++testNum); }, false);
runTest(1);

View File

@ -109,6 +109,7 @@ let PageThumbs = {
* @param aCallback The function to be called when finished (optional).
*/
captureAndStore: function PageThumbs_captureAndStore(aBrowser, aCallback) {
let url = aBrowser.currentURI.spec;
this.capture(aBrowser.contentWindow, function (aInputStream) {
let telemetryStoreTime = new Date();
@ -123,7 +124,7 @@ let PageThumbs = {
}
// Get a writeable cache entry.
PageThumbsCache.getWriteEntry(aBrowser.currentURI.spec, function (aEntry) {
PageThumbsCache.getWriteEntry(url, function (aEntry) {
if (!aEntry) {
finish(false);
return;

View File

@ -13,6 +13,7 @@ include $(topsrcdir)/config/rules.mk
_BROWSER_FILES = \
browser_thumbnails_capture.js \
browser_thumbnails_bug726727.js \
head.js \
$(NULL)

View File

@ -0,0 +1,19 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* These tests ensure that capturing a sites's thumbnail, saving it and
* retrieving it from the cache works.
*/
function runTests() {
// Create a tab that shows an error page.
let tab = gBrowser.addTab("http://non-existant.url/");
let browser = tab.linkedBrowser;
yield browser.addEventListener("DOMContentLoaded", function onLoad() {
browser.removeEventListener("DOMContentLoaded", onLoad, false);
executeSoon(next);
}, false);
ok(!gBrowserThumbnails._shouldCapture(browser), "we're not going to capture an error page");
}

View File

@ -383,6 +383,15 @@ let BlockedLinks = {
Storage.set("blockedLinks", this.links);
},
/**
* Unblocks a given link.
* @param aLink The link to unblock.
*/
unblock: function BlockedLinks_unblock(aLink) {
if (this.isBlocked(aLink))
delete this.links[aLink.url];
},
/**
* Returns whether a given link is blocked.
* @param aLink The link to check.

View File

@ -23,6 +23,11 @@ const FILTER_TIMESTAMP = "timestamp";
const FILTER_NUMBERS = "numbers";
const FILTER_DELIVERY = "delivery";
const READ_ONLY = "readonly";
const READ_WRITE = "readwrite";
const PREV = "prev";
const NEXT = "next";
XPCOMUtils.defineLazyServiceGetter(this, "gSmsService",
"@mozilla.org/sms/smsservice;1",
"nsISmsService");
@ -44,13 +49,13 @@ function SmsDatabaseService() {
gIDBManager.initWindowless(GLOBAL_SCOPE);
let that = this;
this.newTxn(Ci.nsIIDBTransaction.READ_ONLY, function(error, txn, store){
this.newTxn(READ_ONLY, function(error, txn, store){
if (error) {
return;
}
// In order to get the highest key value, we open a key cursor in reverse
// order and get only the first pointed value.
let request = store.openCursor(null, Ci.nsIIDBCursor.PREV);
let request = store.openCursor(null, PREV);
request.onsuccess = function onsuccess(event) {
let cursor = event.target.result;
if (!cursor) {
@ -165,7 +170,7 @@ SmsDatabaseService.prototype = {
* Start a new transaction.
*
* @param txn_type
* Type of transaction (e.g. IDBTransaction.READ_WRITE)
* Type of transaction (e.g. READ_WRITE)
* @param callback
* Function to call when the transaction is available. It will
* be invoked with the transaction and the 'sms' object store.
@ -254,7 +259,7 @@ SmsDatabaseService.prototype = {
onMessageListCreated: function onMessageListCreated(messageList, requestId) {
if (DEBUG) debug("Message list created: " + messageList);
let self = this;
self.newTxn(Ci.nsIIDBTransaction.READ_ONLY, function (error, txn, store) {
self.newTxn(READ_ONLY, function (error, txn, store) {
if (error) {
gSmsRequestManager.notifyReadMessageListFailed(
requestId, Ci.nsISmsRequestManager.INTERNAL_ERROR);
@ -295,7 +300,7 @@ SmsDatabaseService.prototype = {
this.lastKey += 1;
message.id = this.lastKey;
if (DEBUG) debug("Going to store " + JSON.stringify(message));
this.newTxn(Ci.nsIIDBTransaction.READ_WRITE, function(error, txn, store) {
this.newTxn(READ_WRITE, function(error, txn, store) {
if (error) {
return;
}
@ -330,7 +335,7 @@ SmsDatabaseService.prototype = {
getMessage: function getMessage(messageId, requestId) {
if (DEBUG) debug("Retrieving message with ID " + messageId);
this.newTxn(Ci.nsIIDBTransaction.READ_ONLY, function (error, txn, store) {
this.newTxn(READ_ONLY, function (error, txn, store) {
if (error) {
if (DEBUG) debug(error);
gSmsRequestManager.notifyGetSmsFailed(
@ -382,46 +387,28 @@ SmsDatabaseService.prototype = {
},
deleteMessage: function deleteMessage(messageId, requestId) {
let deleted = false;
let self = this;
this.newTxn(Ci.nsIIDBTransaction.READ_WRITE, function (error, txn, store) {
this.newTxn(READ_WRITE, function (error, txn, store) {
if (error) {
gSmsRequestManager.notifySmsDeleteFailed(
requestId, Ci.nsISmsRequestManager.INTERNAL_ERROR);
return;
}
let request = store.delete(messageId);
let request = store.count(messageId);
request.onerror = function onerror(event) {
if (DEBUG) debug("Caught error on request ", event.target.errorCode);
//TODO look at event.target.errorCode
gSmsRequestManager.notifySmsDeleteFailed(
requestId, Ci.nsISmsRequestManager.INTERNAL_ERROR);
request.onsuccess = function onsuccess(event) {
let count = event.target.result;
if (DEBUG) debug("Count for messageId " + messageId + ": " + count);
deleted = (count == 1);
if (deleted) {
store.delete(messageId);
}
};
txn.oncomplete = function oncomplete(event) {
if (DEBUG) debug("Transaction " + txn + " completed.");
// Once we transaction is done, we need to check if we actually deleted
// the message. As IndexedDB does not provide the affected records info,
// we need to try to get the message from the database again to check
// that it is actually gone.
self.newTxn(Ci.nsIIDBTransaction.READ_ONLY, function (error, txn, store) {
let request = store.getAll(messageId);
request.onsuccess = function onsuccess(event) {
let deleted = (event.target.result.length == 0);
gSmsRequestManager.notifySmsDeleted(requestId, deleted);
};
request.onerror = function onerror(event) {
if (DEBUG) {
debug("Error checking the message deletion " +
event.target.errorCode);
}
//TODO should we notify here as an internal error? The failed check
// does not mean that the deletion has failed, so maybe we
// should notify successfully.
gSmsRequestManager.notifySmsDeleteFailed(
requestId, Ci.nsISmsRequestManager.INTERNAL_ERROR);
};
});
gSmsRequestManager.notifySmsDeleted(requestId, deleted);
};
txn.onerror = function onerror(event) {
@ -478,7 +465,7 @@ SmsDatabaseService.prototype = {
};
let self = this;
this.newTxn(Ci.nsIIDBTransaction.READ_ONLY, function (error, txn, store) {
this.newTxn(READ_ONLY, function (error, txn, store) {
if (error) {
errorCb(error);
return;
@ -495,7 +482,7 @@ SmsDatabaseService.prototype = {
} else if (filter.endDate != null) {
timeKeyRange = IDBKeyRange.upperBound(filter.endDate.getTime());
}
let direction = reverse ? Ci.nsIIDBCursor.PREV : Ci.nsIIDBCursor.NEXT;
let direction = reverse ? PREV : NEXT;
let timeRequest = store.index("timestamp").openKeyCursor(timeKeyRange,
direction);
@ -573,7 +560,7 @@ SmsDatabaseService.prototype = {
gSmsRequestManager.notifyNoMessageInList(requestId);
return;
}
this.newTxn(Ci.nsIIDBTransaction.READ_ONLY, function (error, txn, store) {
this.newTxn(READ_ONLY, function (error, txn, store) {
if (DEBUG) debug("Fetching message " + messageId);
let request = store.get(messageId);
let message;

View File

@ -408,13 +408,13 @@ RadioInterfaceLayer.prototype = {
handleSmsReceived: function handleSmsReceived(message) {
debug("handleSmsReceived: " + JSON.stringify(message));
let id = gSmsDatabaseService.saveReceivedMessage(message.sender || null,
message.body || null,
message.fullBody || null,
message.timestamp);
let sms = gSmsService.createSmsMessage(id,
DOM_SMS_DELIVERY_RECEIVED,
message.sender || null,
message.receiver || null,
message.body || null,
message.fullBody || null,
message.timestamp);
Services.obs.notifyObservers(sms, kSmsReceivedObserverTopic, null);
},
@ -422,12 +422,14 @@ RadioInterfaceLayer.prototype = {
handleSmsSent: function handleSmsSent(message) {
debug("handleSmsSent: " + JSON.stringify(message));
let timestamp = Date.now();
let id = gSmsDatabaseService.saveSentMessage(message.number, message.body, timestamp);
let id = gSmsDatabaseService.saveSentMessage(message.number,
message.fullBody,
timestamp);
let sms = gSmsService.createSmsMessage(id,
DOM_SMS_DELIVERY_SENT,
null,
message.number,
message.body,
message.fullBody,
timestamp);
//TODO handle errors (bug 727319)
gSmsRequestManager.notifySmsSent(message.requestId, sms);
@ -532,19 +534,387 @@ RadioInterfaceLayer.prototype = {
gAudioManager.setForceForUse(nsIAudioManager.USE_COMMUNICATION, force);
},
/**
* List of tuples of national language identifier pairs.
*
* TODO: Support static/runtime settings, see bug 733331.
*/
enabledGsmTableTuples: [
[RIL.PDU_NL_IDENTIFIER_DEFAULT, RIL.PDU_NL_IDENTIFIER_DEFAULT],
],
/**
* Use 16-bit reference number for concatenated outgoint messages.
*
* TODO: Support static/runtime settings, see bug 733331.
*/
segmentRef16Bit: false,
/**
* Get valid SMS concatenation reference number.
*/
_segmentRef: 0,
get nextSegmentRef() {
let ref = this._segmentRef++;
this._segmentRef %= (this.segmentRef16Bit ? 65535 : 255);
// 0 is not a valid SMS concatenation reference number.
return ref + 1;
},
/**
* Calculate encoded length using specified locking/single shift table
*
* @param message
* message string to be encoded.
* @param langTable
* locking shift table string.
* @param langShiftTable
* single shift table string.
*
* @return encoded length in septets.
*
* @note that the algorithm used in this function must match exactly with
* GsmPDUHelper#writeStringAsSeptets.
*/
_countGsm7BitSeptets: function _countGsm7BitSeptets(message, langTable, langShiftTable) {
let length = 0;
for (let msgIndex = 0; msgIndex < message.length; msgIndex++) {
let septet = langTable.indexOf(message.charAt(msgIndex));
// According to 3GPP TS 23.038, section 6.1.1 General notes, "The
// characters marked '1)' are not used but are displayed as a space."
if (septet == RIL.PDU_NL_EXTENDED_ESCAPE) {
continue;
}
if (septet >= 0) {
length++;
continue;
}
septet = langShiftTable.indexOf(message.charAt(msgIndex));
if (septet < 0) {
return -1;
}
// According to 3GPP TS 23.038 B.2, "This code represents a control
// character and therefore must not be used for language specific
// characters."
if (septet == RIL.PDU_NL_RESERVED_CONTROL) {
continue;
}
// The character is not found in locking shfit table, but could be
// encoded as <escape><char> with single shift table. Note that it's
// still possible for septet to has the value of PDU_NL_EXTENDED_ESCAPE,
// but we can display it as a space in this case as said in previous
// comment.
length += 2;
}
return length;
},
/**
* Calculate user data length of specified message string encoded in GSM 7Bit
* alphabets.
*
* @param message
* a message string to be encoded.
*
* @return null or an options object with attributes `dcs`,
* `userDataHeaderLength`, `encodedFullBodyLength`, `langIndex`,
* `langShiftIndex`, `segmentMaxSeq` set.
*
* @see #_calculateUserDataLength().
*/
_calculateUserDataLength7Bit: function _calculateUserDataLength7Bit(message) {
let options = null;
let minUserDataSeptets = Number.MAX_VALUE;
for (let i = 0; i < this.enabledGsmTableTuples.length; i++) {
let [langIndex, langShiftIndex] = this.enabledGsmTableTuples[i];
const langTable = RIL.PDU_NL_LOCKING_SHIFT_TABLES[langIndex];
const langShiftTable = RIL.PDU_NL_SINGLE_SHIFT_TABLES[langShiftIndex];
let bodySeptets = this._countGsm7BitSeptets(message,
langTable,
langShiftTable);
if (bodySeptets < 0) {
continue;
}
let headerLen = 0;
if (langIndex != RIL.PDU_NL_IDENTIFIER_DEFAULT) {
headerLen += 3; // IEI + len + langIndex
}
if (langShiftIndex != RIL.PDU_NL_IDENTIFIER_DEFAULT) {
headerLen += 3; // IEI + len + langShiftIndex
}
// Calculate full user data length, note the extra byte is for header len
let headerSeptets = Math.ceil((headerLen ? headerLen + 1 : 0) * 8 / 7);
let userDataSeptets = bodySeptets + headerSeptets;
let segments = bodySeptets ? 1 : 0;
if (userDataSeptets > RIL.PDU_MAX_USER_DATA_7BIT) {
if (this.segmentRef16Bit) {
headerLen += 6;
} else {
headerLen += 5;
}
headerSeptets = Math.ceil((headerLen + 1) * 8 / 7);
let segmentSeptets = RIL.PDU_MAX_USER_DATA_7BIT - headerSeptets;
segments = Math.ceil(bodySeptets / segmentSeptets);
userDataSeptets = bodySeptets + headerSeptets * segments;
}
if (userDataSeptets >= minUserDataSeptets) {
continue;
}
minUserDataSeptets = userDataSeptets;
options = {
dcs: RIL.PDU_DCS_MSG_CODING_7BITS_ALPHABET,
encodedFullBodyLength: bodySeptets,
userDataHeaderLength: headerLen,
langIndex: langIndex,
langShiftIndex: langShiftIndex,
segmentMaxSeq: segments,
};
}
return options;
},
/**
* Calculate user data length of specified message string encoded in UCS2.
*
* @param message
* a message string to be encoded.
*
* @return an options object with attributes `dcs`, `userDataHeaderLength`,
* `encodedFullBodyLength`, `segmentMaxSeq` set.
*
* @see #_calculateUserDataLength().
*/
_calculateUserDataLengthUCS2: function _calculateUserDataLengthUCS2(message) {
let bodyChars = message.length;
let headerLen = 0;
let headerChars = Math.ceil((headerLen ? headerLen + 1 : 0) / 2);
let segments = bodyChars ? 1 : 0;
if ((bodyChars + headerChars) > RIL.PDU_MAX_USER_DATA_UCS2) {
if (this.segmentRef16Bit) {
headerLen += 6;
} else {
headerLen += 5;
}
headerChars = Math.ceil((headerLen + 1) / 2);
let segmentChars = RIL.PDU_MAX_USER_DATA_UCS2 - headerChars;
segments = Math.ceil(bodyChars / segmentChars);
}
return {
dcs: RIL.PDU_DCS_MSG_CODING_16BITS_ALPHABET,
encodedFullBodyLength: bodyChars * 2,
userDataHeaderLength: headerLen,
segmentMaxSeq: segments,
};
},
/**
* Calculate user data length and its encoding.
*
* @param message
* a message string to be encoded.
*
* @return an options object with some or all of following attributes set:
*
* @param dcs
* Data coding scheme. One of the PDU_DCS_MSG_CODING_*BITS_ALPHABET
* constants.
* @param fullBody
* Original unfragmented text message.
* @param userDataHeaderLength
* Length of embedded user data header, in bytes. The whole header
* size will be userDataHeaderLength + 1; 0 for no header.
* @param encodedFullBodyLength
* Length of the message body when encoded with the given DCS. For
* UCS2, in bytes; for 7-bit, in septets.
* @param langIndex
* Table index used for normal 7-bit encoded character lookup.
* @param langShiftIndex
* Table index used for escaped 7-bit encoded character lookup.
* @param segmentMaxSeq
* Max sequence number of a multi-part messages, or 1 for single one.
* This number might not be accurate for a multi-part message until
* it's processed by #_fragmentText() again.
*/
_calculateUserDataLength: function _calculateUserDataLength(message) {
let options = this._calculateUserDataLength7Bit(message);
if (!options) {
options = this._calculateUserDataLengthUCS2(message);
}
if (options) {
options.fullBody = message;
}
debug("_calculateUserDataLength: " + JSON.stringify(options));
return options;
},
/**
* Fragment GSM 7-Bit encodable string for transmission.
*
* @param text
* text string to be fragmented.
* @param langTable
* locking shift table string.
* @param langShiftTable
* single shift table string.
* @param headerLen
* Length of prepended user data header.
*
* @return an array of objects. See #_fragmentText() for detailed definition.
*/
_fragmentText7Bit: function _fragmentText7Bit(text, langTable, langShiftTable, headerLen) {
const headerSeptets = Math.ceil((headerLen ? headerLen + 1 : 0) * 8 / 7);
const segmentSeptets = RIL.PDU_MAX_USER_DATA_7BIT - headerSeptets;
let ret = [];
let begin = 0, len = 0;
for (let i = 0, inc = 0; i < text.length; i++) {
let septet = langTable.indexOf(text.charAt(i));
if (septet == RIL.PDU_NL_EXTENDED_ESCAPE) {
continue;
}
if (septet >= 0) {
inc = 1;
} else {
septet = langShiftTable.indexOf(text.charAt(i));
if (septet < 0) {
throw new Error("Given text cannot be encoded with GSM 7-bit Alphabet!");
}
if (septet == RIL.PDU_NL_RESERVED_CONTROL) {
continue;
}
inc = 2;
}
if ((len + inc) > segmentSeptets) {
ret.push({
body: text.substring(begin, i),
encodedBodyLength: len,
});
begin = i;
len = 0;
}
len += inc;
}
if (len) {
ret.push({
body: text.substring(begin),
encodedBodyLength: len,
});
}
return ret;
},
/**
* Fragment UCS2 encodable string for transmission.
*
* @param text
* text string to be fragmented.
* @param headerLen
* Length of prepended user data header.
*
* @return an array of objects. See #_fragmentText() for detailed definition.
*/
_fragmentTextUCS2: function _fragmentTextUCS2(text, headerLen) {
const headerChars = Math.ceil((headerLen ? headerLen + 1 : 0) / 2);
const segmentChars = RIL.PDU_MAX_USER_DATA_UCS2 - headerChars;
let ret = [];
for (let offset = 0; offset < text.length; offset += segmentChars) {
let str = text.substr(offset, segmentChars);
ret.push({
body: str,
encodedBodyLength: str.length * 2,
});
}
return ret;
},
/**
* Fragment string for transmission.
*
* Fragment input text string into an array of objects that contains
* attributes `body`, substring for this segment, `encodedBodyLength`,
* length of the encoded segment body in septets.
*
* @param text
* Text string to be fragmented.
* @param options
* Optional pre-calculated option object. The output array will be
* stored at options.segments if there are multiple segments.
*
* @return Populated options object.
*/
_fragmentText: function _fragmentText(text, options) {
if (!options) {
options = this._calculateUserDataLength(text);
}
if (options.segmentMaxSeq <= 1) {
options.segments = null;
return options;
}
if (options.dcs == RIL.PDU_DCS_MSG_CODING_7BITS_ALPHABET) {
const langTable = RIL.PDU_NL_LOCKING_SHIFT_TABLES[options.langIndex];
const langShiftTable = RIL.PDU_NL_SINGLE_SHIFT_TABLES[options.langShiftIndex];
options.segments = this._fragmentText7Bit(options.fullBody,
langTable, langShiftTable,
options.userDataHeaderLength);
} else {
options.segments = this._fragmentTextUCS2(options.fullBody,
options.userDataHeaderLength);
}
// Re-sync options.segmentMaxSeq with actual length of returning array.
options.segmentMaxSeq = options.segments.length;
return options;
},
getNumberOfMessagesForText: function getNumberOfMessagesForText(text) {
//TODO: this assumes 7bit encoding, which is incorrect. Need to look
// for characters not supported by 7bit alphabets and then calculate
// length in UCS2 encoding.
return Math.ceil(text.length / 160);
return this._fragmentText(text).segmentMaxSeq;
},
sendSMS: function sendSMS(number, message, requestId, processId) {
this.worker.postMessage({type: "sendSMS",
number: number,
body: message,
requestId: requestId,
processId: processId});
let options = this._calculateUserDataLength(message);
options.type = "sendSMS";
options.number = number;
options.requestId = requestId;
options.processId = processId;
this._fragmentText(message, options);
if (options.segmentMaxSeq > 1) {
options.segmentRef16Bit = this.segmentRef16Bit;
options.segmentRef = this.nextSegmentRef;
}
this.worker.postMessage(options);
},
_callbacks: null,

View File

@ -413,8 +413,12 @@ const PDU_MTI_SMS_STATUS_COMMAND = 0x02;
const PDU_MTI_SMS_SUBMIT = 0x01;
const PDU_MTI_SMS_DELIVER = 0x00;
// User Data max length in octets
// User Data max length in septets
const PDU_MAX_USER_DATA_7BIT = 160;
// User Data max length in octets
const PDU_MAX_USER_DATA_8BIT = 140;
// User Data max length in chars
const PDU_MAX_USER_DATA_UCS2 = 70;
// DCS - Data Coding Scheme
const PDU_DCS_MSG_CODING_7BITS_ALPHABET = 0x00;

View File

@ -604,6 +604,13 @@ let RIL = {
*/
currentDataCalls: {},
/**
* Hash map for received multipart sms fragments. Messages are hashed with
* its sender address and concatenation reference number. Three additional
* attributes `segmentMaxSeq`, `receivedSegments`, `segments` are inserted.
*/
_receivedSmsSegmentsMap: {},
/**
* Mute or unmute the radio.
*/
@ -981,9 +988,18 @@ let RIL = {
options.SMSC = this.SMSC;
//TODO: verify values on 'options'
//TODO: the data encoding and length in octets should eventually be
// computed on the mainthread and passed down to us.
GsmPDUHelper.calculateUserDataLength(options);
if (options.segmentMaxSeq > 1) {
if (!options.segmentSeq) {
// Fist segment to send
options.segmentSeq = 1;
options.body = options.segments[0].body;
options.encodedBodyLength = options.segments[0].encodedBodyLength;
}
} else {
options.body = options.fullBody;
options.encodedBodyLength = options.encodedFullBodyLength;
}
Buf.newParcel(REQUEST_SEND_SMS, options);
Buf.writeUint32(2);
@ -1369,6 +1385,59 @@ let RIL = {
this.muted = Object.getOwnPropertyNames(this.currentCalls).length == 0;
},
/**
* Helper for processing received multipart SMS.
*
* @return null for handled segments, and an object containing full message
* body once all segments are received.
*/
_processReceivedSmsSegment: function _processReceivedSmsSegment(original) {
let hash = original.sender + ":" + original.header.segmentRef;
let seq = original.header.segmentSeq;
let options = this._receivedSmsSegmentsMap[hash];
if (!options) {
options = original;
this._receivedSmsSegmentsMap[hash] = options;
options.segmentMaxSeq = original.header.segmentMaxSeq;
options.receivedSegments = 0;
options.segments = [];
} else if (options.segments[seq]) {
// Duplicated segment?
if (DEBUG) {
debug("Got duplicated segment no." + seq + " of a multipart SMS: "
+ JSON.stringify(original));
}
return null;
}
options.segments[seq] = original.body;
options.receivedSegments++;
if (options.receivedSegments < options.segmentMaxSeq) {
if (DEBUG) {
debug("Got segment no." + seq + " of a multipart SMS: "
+ JSON.stringify(options));
}
return null;
}
// Remove from map
delete this._receivedSmsSegmentsMap[hash];
// Rebuild full body
options.fullBody = "";
for (let i = 1; i <= options.segmentMaxSeq; i++) {
options.fullBody += options.segments[i];
}
if (DEBUG) {
debug("Got full multipart SMS: " + JSON.stringify(options));
}
return options;
},
_handleChangedCallState: function _handleChangedCallState(changedCall) {
let message = {type: "callStateChange",
call: {callIndex: changedCall.callIndex,
@ -1658,6 +1727,20 @@ RIL[REQUEST_SEND_SMS] = function REQUEST_SEND_SMS(length, options) {
options.messageRef = Buf.readUint32();
options.ackPDU = Buf.readString();
options.errorCode = Buf.readUint32();
//TODO handle errors (bug 727319)
if ((options.segmentMaxSeq > 1)
&& (options.segmentSeq < options.segmentMaxSeq)) {
// Setup attributes for sending next segment
let next = options.segmentSeq;
options.body = options.segments[next].body;
options.encodedBodyLength = options.segments[next].encodedBodyLength;
options.segmentSeq = next + 1;
this.sendSMS(options);
return;
}
options.type = "sms-sent";
this.sendDOMMessage(options);
};
@ -1932,8 +2015,16 @@ RIL[UNSOLICITED_RESPONSE_NEW_SMS] = function UNSOLICITED_RESPONSE_NEW_SMS(length
}
}
message.type = "sms-received";
this.sendDOMMessage(message);
if (message.header && (message.header.segmentMaxSeq > 1)) {
message = this._processReceivedSmsSegment(message);
} else {
message.fullBody = message.body;
}
if (message) {
message.type = "sms-received";
this.sendDOMMessage(message);
}
//TODO: this might be a lie? do we want to wait for the mainthread to
// report back?
@ -2042,13 +2133,6 @@ RIL[UNSOLICITED_RESEND_INCALL_MUTE] = null;
*/
let GsmPDUHelper = {
/**
* List of tuples of national language identifier pairs.
*/
enabledGsmTableTuples: [
[PDU_NL_IDENTIFIER_DEFAULT, PDU_NL_IDENTIFIER_DEFAULT],
],
/**
* Read one character (2 bytes) from a RIL string and decode as hex.
*
@ -2369,137 +2453,6 @@ let GsmPDUHelper = {
}
},
/**
* Calculate encoded length using specified locking/single shift table
*
* @param message
* message string to be encoded.
* @param langTable
* locking shift table string.
* @param langShiftTable
* single shift table string.
*
* @return encoded length in septets.
*
* @note that the algorithm used in this function must match exactly with
* #writeStringAsSeptets.
*/
_calculateLangEncodedSeptets: function _calculateLangEncodedSeptets(message, langTable, langShiftTable) {
let length = 0;
for (let msgIndex = 0; msgIndex < message.length; msgIndex++) {
let septet = langTable.indexOf(message.charAt(msgIndex));
// According to 3GPP TS 23.038, section 6.1.1 General notes, "The
// characters marked '1)' are not used but are displayed as a space."
if (septet == PDU_NL_EXTENDED_ESCAPE) {
continue;
}
if (septet >= 0) {
length++;
continue;
}
septet = langShiftTable.indexOf(message.charAt(msgIndex));
if (septet == -1) {
return -1;
}
// According to 3GPP TS 23.038 B.2, "This code represents a control
// character and therefore must not be used for language specific
// characters."
if (septet == PDU_NL_RESERVED_CONTROL) {
continue;
}
// The character is not found in locking shfit table, but could be
// encoded as <escape><char> with single shift table. Note that it's
// still possible for septet to has the value of PDU_NL_EXTENDED_ESCAPE,
// but we can display it as a space in this case as said in previous
// comment.
length += 2;
}
return length;
},
/**
* Calculate user data length and its encoding.
*
* The `options` parameter object should contain the `body` attribute, and
* the `dcs`, `userDataHeaderLength`, `encodedBodyLength`, `langIndex`,
* `langShiftIndex` attributes will be set as return:
*
* @param body
* String containing the message body.
* @param dcs
* Data coding scheme. One of the PDU_DCS_MSG_CODING_*BITS_ALPHABET
* constants.
* @param userDataHeaderLength
* Length of embedded user data header, in bytes. The whole header
* size will be userDataHeaderLength + 1; 0 for no header.
* @param encodedBodyLength
* Length of the message body when encoded with the given DCS. For
* UCS2, in bytes; for 7-bit, in septets.
* @param langIndex
* Table index used for normal 7-bit encoded character lookup.
* @param langShiftIndex
* Table index used for escaped 7-bit encoded character lookup.
*/
calculateUserDataLength: function calculateUserDataLength(options) {
//TODO: support multipart SMS, see bug 712933
options.dcs = PDU_DCS_MSG_CODING_7BITS_ALPHABET;
options.langIndex = PDU_NL_IDENTIFIER_DEFAULT;
options.langShiftIndex = PDU_NL_IDENTIFIER_DEFAULT;
options.encodedBodyLength = 0;
options.userDataHeaderLength = 0;
let needUCS2 = true;
let minUserDataSeptets = Number.MAX_VALUE;
for (let i = 0; i < this.enabledGsmTableTuples.length; i++) {
let [langIndex, langShiftIndex] = this.enabledGsmTableTuples[i];
const langTable = PDU_NL_LOCKING_SHIFT_TABLES[langIndex];
const langShiftTable = PDU_NL_SINGLE_SHIFT_TABLES[langShiftIndex];
let bodySeptets = this._calculateLangEncodedSeptets(options.body,
langTable,
langShiftTable);
if (bodySeptets < 0) {
continue;
}
let headerLen = 0;
if (langIndex != PDU_NL_IDENTIFIER_DEFAULT) {
headerLen += 3; // IEI + len + langIndex
}
if (langShiftIndex != PDU_NL_IDENTIFIER_DEFAULT) {
headerLen += 3; // IEI + len + langShiftIndex
}
// Calculate full user data length, note the extra byte is for header len
let headerSeptets = Math.ceil((headerLen ? headerLen + 1 : 0) * 8 / 7);
let userDataSeptets = bodySeptets + headerSeptets;
if (userDataSeptets >= minUserDataSeptets) {
continue;
}
needUCS2 = false;
minUserDataSeptets = userDataSeptets;
options.encodedBodyLength = bodySeptets;
options.userDataHeaderLength = headerLen;
options.langIndex = langIndex;
options.langShiftIndex = langShiftIndex;
}
if (needUCS2) {
options.dcs = PDU_DCS_MSG_CODING_16BITS_ALPHABET;
options.encodedBodyLength = options.body.length * 2;
options.userDataHeaderLength = 0;
}
},
/**
* Read 1 + UDHL octets and construct user data header at return.
*
@ -2528,6 +2481,30 @@ let GsmPDUHelper = {
dataAvailable -= 2;
switch (id) {
case PDU_IEI_CONCATENATED_SHORT_MESSAGES_8BIT: {
let ref = this.readHexOctet();
let max = this.readHexOctet();
let seq = this.readHexOctet();
dataAvailable -= 3;
if (max && seq && (seq <= max)) {
header.segmentRef = ref;
header.segmentMaxSeq = max;
header.segmentSeq = seq;
}
break;
}
case PDU_IEI_CONCATENATED_SHORT_MESSAGES_16BIT: {
let ref = (this.readHexOctet() << 8) | this.readHexOctet();
let max = this.readHexOctet();
let seq = this.readHexOctet();
dataAvailable -= 4;
if (max && seq && (seq <= max)) {
header.segmentRef = ref;
header.segmentMaxSeq = max;
header.segmentSeq = seq;
}
break;
}
case PDU_IEI_NATIONAL_LANGUAGE_SINGLE_SHIFT:
let langShiftIndex = this.readHexOctet();
--dataAvailable;
@ -2582,6 +2559,20 @@ let GsmPDUHelper = {
writeUserDataHeader: function writeUserDataHeader(options) {
this.writeHexOctet(options.userDataHeaderLength);
if (options.segmentMaxSeq > 1) {
if (options.segmentRef16Bit) {
this.writeHexOctet(PDU_IEI_CONCATENATED_SHORT_MESSAGES_16BIT);
this.writeHexOctet(4);
this.writeHexOctet((options.segmentRef >> 8) & 0xFF);
} else {
this.writeHexOctet(PDU_IEI_CONCATENATED_SHORT_MESSAGES_8BIT);
this.writeHexOctet(3);
}
this.writeHexOctet(options.segmentRef & 0xFF);
this.writeHexOctet(options.segmentMaxSeq & 0xFF);
this.writeHexOctet(options.segmentSeq & 0xFF);
}
if (options.langIndex != PDU_NL_IDENTIFIER_DEFAULT) {
this.writeHexOctet(PDU_IEI_NATIONAL_LANGUAGE_LOCKING_SHIFT);
this.writeHexOctet(1);
@ -2598,8 +2589,17 @@ let GsmPDUHelper = {
/**
* User data can be 7 bit (default alphabet) data, 8 bit data, or 16 bit
* (UCS2) data.
*
* @param msg
* message object for output.
* @param length
* length of user data to read in octets.
* @param codingScheme
* coding scheme used to decode user data.
* @param hasHeader
* whether a header is embedded.
*/
readUserData: function readUserData(length, codingScheme, hasHeader) {
readUserData: function readUserData(msg, length, codingScheme, hasHeader) {
if (DEBUG) {
debug("Reading " + length + " bytes of user data.");
debug("Coding scheme: " + codingScheme);
@ -2636,43 +2636,45 @@ let GsmPDUHelper = {
break;
}
let header;
if (DEBUG) debug("PDU: message encoding is " + encoding + " bit.");
let paddingBits = 0;
if (hasHeader) {
header = this.readUserDataHeader();
msg.header = this.readUserDataHeader();
if (encoding == PDU_DCS_MSG_CODING_7BITS_ALPHABET) {
let headerBits = (header.length + 1) * 8;
let headerBits = (msg.header.length + 1) * 8;
let headerSeptets = Math.ceil(headerBits / 7);
length -= headerSeptets;
paddingBits = headerSeptets * 7 - headerBits;
} else {
length -= (header.length + 1);
length -= (msg.header.length + 1);
}
}
if (DEBUG) debug("PDU: message encoding is " + encoding + " bit.");
msg.body = null;
switch (encoding) {
case PDU_DCS_MSG_CODING_7BITS_ALPHABET:
// 7 bit encoding allows 140 octets, which means 160 characters
// ((140x8) / 7 = 160 chars)
if (length > PDU_MAX_USER_DATA_7BIT) {
if (DEBUG) debug("PDU error: user data is too long: " + length);
return null;
break;
}
return this.readSeptetsToString(length,
paddingBits,
hasHeader ? header.langIndex : PDU_NL_IDENTIFIER_DEFAULT,
hasHeader ? header.langShiftIndex : PDU_NL_IDENTIFIER_DEFAULT);
let langIndex = hasHeader ? msg.header.langIndex : PDU_NL_IDENTIFIER_DEFAULT;
let langShiftIndex = hasHeader ? msg.header.langShiftIndex : PDU_NL_IDENTIFIER_DEFAULT;
msg.body = this.readSeptetsToString(length, paddingBits, langIndex,
langShiftIndex);
break;
case PDU_DCS_MSG_CODING_8BITS_ALPHABET:
// Unsupported.
return null;
break;
case PDU_DCS_MSG_CODING_16BITS_ALPHABET:
return this.readUCS2String(length);
msg.body = this.readUCS2String(length);
break;
}
return null;
},
/**
@ -2786,9 +2788,8 @@ let GsmPDUHelper = {
// - TP-User-Data -
if (userDataLength > 0) {
msg.body = this.readUserData(userDataLength,
dataCodingScheme,
hasUserDataHeader);
this.readUserData(msg, userDataLength, dataCodingScheme,
hasUserDataHeader);
}
return msg;

View File

@ -119,6 +119,20 @@ function newIncomingParcel(fakeParcelSize, response, request, data) {
return bytes;
}
/**
*
*/
function newRadioInterfaceLayer() {
let ril_ns = {
ChromeWorker: function ChromeWorker() {
// Stub function
},
};
subscriptLoader.loadSubScript("resource://gre/components/RadioInterfaceLayer.js", ril_ns);
return new ril_ns.RadioInterfaceLayer();
}
/**
* Test whether specified function throws exception with expected
* result.

View File

@ -55,10 +55,12 @@ add_test(function test_nl_single_shift_tables_validity() {
});
/**
* Verify GsmPDUHelper#_calculateLangEncodedSeptets() and
* Verify RadioInterfaceLayer#_countGsm7BitSeptets() and
* GsmPDUHelper#writeStringAsSeptets() algorithm match each other.
*/
add_test(function test_GsmPDUHelper__calculateLangEncodedSeptets() {
add_test(function test_RadioInterfaceLayer__countGsm7BitSeptets() {
let ril = newRadioInterfaceLayer();
let worker = newWorker({
postRILMessage: function fakePostRILMessage(data) {
// Do nothing
@ -78,9 +80,9 @@ add_test(function test_GsmPDUHelper__calculateLangEncodedSeptets() {
function do_check_calc(str, expectedCalcLen, lst, sst) {
do_check_eq(expectedCalcLen,
helper._calculateLangEncodedSeptets(str,
PDU_NL_LOCKING_SHIFT_TABLES[lst],
PDU_NL_SINGLE_SHIFT_TABLES[sst]));
ril._countGsm7BitSeptets(str,
PDU_NL_LOCKING_SHIFT_TABLES[lst],
PDU_NL_SINGLE_SHIFT_TABLES[sst]));
helper.resetOctetWritten();
helper.writeStringAsSeptets(str, 0, lst, sst);
@ -131,28 +133,18 @@ add_test(function test_GsmPDUHelper__calculateLangEncodedSeptets() {
});
/**
* Verify GsmPDUHelper#calculateUserDataLength handles national language
* Verify RadioInterfaceLayer#calculateUserDataLength handles national language
* selection correctly.
*/
add_test(function test_GsmPDUHelper_calculateUserDataLength() {
let worker = newWorker({
postRILMessage: function fakePostRILMessage(data) {
// Do nothing
},
postMessage: function fakePostMessage(message) {
// Do nothing
}
});
add_test(function test_RadioInterfaceLayer__calculateUserDataLength() {
let ril = newRadioInterfaceLayer();
let helper = worker.GsmPDUHelper;
let calc = helper.calculateUserDataLength;
function test_calc(str, expected, enabledGsmTableTuples) {
helper.enabledGsmTableTuples = enabledGsmTableTuples;
let options = {body: str};
calc.call(helper, options);
ril.enabledGsmTableTuples = enabledGsmTableTuples;
let options = ril._calculateUserDataLength(str);
do_check_eq(expected[0], options.dcs);
do_check_eq(expected[1], options.encodedBodyLength);
do_check_eq(expected[1], options.encodedFullBodyLength);
do_check_eq(expected[2], options.userDataHeaderLength);
do_check_eq(expected[3], options.langIndex);
do_check_eq(expected[4], options.langShiftIndex);
@ -160,9 +152,9 @@ add_test(function test_GsmPDUHelper_calculateUserDataLength() {
// Test UCS fallback
// - No any default enabled nl tables
test_calc("A", [PDU_DCS_MSG_CODING_16BITS_ALPHABET, 2, 0, 0, 0], []);
test_calc("A", [PDU_DCS_MSG_CODING_16BITS_ALPHABET, 2, 0,], []);
// - Character not defined in enabled nl tables
test_calc("A", [PDU_DCS_MSG_CODING_16BITS_ALPHABET, 2, 0, 0, 0], [[2, 2]]);
test_calc("A", [PDU_DCS_MSG_CODING_16BITS_ALPHABET, 2, 0,], [[2, 2]]);
// With GSM default nl tables
test_calc("A", [PDU_DCS_MSG_CODING_7BITS_ALPHABET, 1, 0, 0, 0], [[0, 0]]);
@ -211,6 +203,101 @@ add_test(function test_GsmPDUHelper_calculateUserDataLength() {
run_next_test();
});
function generateStringOfLength(str, length) {
while (str.length < length) {
if (str.length < (length / 2)) {
str = str + str;
} else {
str = str + str.substr(0, length - str.length);
}
}
return str;
}
/**
* Verify RadioInterfaceLayer#_calculateUserDataLength7Bit multipart handling.
*/
add_test(function test_RadioInterfaceLayer__calculateUserDataLength7Bit_multipart() {
let ril = newRadioInterfaceLayer();
function test_calc(str, expected) {
let options = ril._calculateUserDataLength7Bit(str);
do_check_eq(expected[0], options.encodedFullBodyLength);
do_check_eq(expected[1], options.userDataHeaderLength);
do_check_eq(expected[2], options.segmentMaxSeq);
}
test_calc(generateStringOfLength("A", PDU_MAX_USER_DATA_7BIT),
[PDU_MAX_USER_DATA_7BIT, 0, 1]);
test_calc(generateStringOfLength("A", PDU_MAX_USER_DATA_7BIT + 1),
[PDU_MAX_USER_DATA_7BIT + 1, 5, 2]);
run_next_test();
});
/**
* Verify RadioInterfaceLayer#_fragmentText7Bit().
*/
add_test(function test_RadioInterfaceLayer__fragmentText7Bit() {
let ril = newRadioInterfaceLayer();
function test_calc(str, expected) {
let options = ril._fragmentText(str);
if (expected) {
do_check_eq(expected, options.segments.length);
} else {
do_check_eq(null, options.segments);
}
}
// Boundary checks
test_calc("");
test_calc(generateStringOfLength("A", PDU_MAX_USER_DATA_7BIT));
test_calc(generateStringOfLength("A", PDU_MAX_USER_DATA_7BIT + 1), 2);
// Escaped character
test_calc(generateStringOfLength("{", PDU_MAX_USER_DATA_7BIT / 2));
test_calc(generateStringOfLength("{", PDU_MAX_USER_DATA_7BIT / 2 + 1), 2);
// Escaped character cannot be separated
test_calc(generateStringOfLength("{", (PDU_MAX_USER_DATA_7BIT - 7) * 2 / 2), 3);
// Test headerLen, 7 = Math.ceil(6 * 8 / 7), 6 = headerLen + 1
test_calc(generateStringOfLength("A", PDU_MAX_USER_DATA_7BIT - 7));
test_calc(generateStringOfLength("A", (PDU_MAX_USER_DATA_7BIT - 7) * 2), 2);
test_calc(generateStringOfLength("A", (PDU_MAX_USER_DATA_7BIT - 7) * 3), 3);
run_next_test();
});
/**
* Verify RadioInterfaceLayer#_fragmentTextUCS2().
*/
add_test(function test_RadioInterfaceLayer__fragmentTextUCS2() {
let ril = newRadioInterfaceLayer();
function test_calc(str, expected) {
let options = ril._fragmentText(str);
if (expected) {
do_check_eq(expected, options.segments.length);
} else {
do_check_eq(null, options.segments);
}
}
// Boundary checks
test_calc(generateStringOfLength("\ua2db", PDU_MAX_USER_DATA_UCS2));
test_calc(generateStringOfLength("\ua2db", PDU_MAX_USER_DATA_UCS2 + 1), 2);
// UCS2 character cannot be separated
ril.segmentRef16Bit = true;
test_calc(generateStringOfLength("\ua2db", (PDU_MAX_USER_DATA_UCS2 * 2 - 7) * 2 / 2), 3);
ril.segmentRef16Bit = false;
run_next_test();
});
/**
* Verify GsmPDUHelper#writeStringAsSeptets() padding bits handling.
*/
@ -386,6 +473,8 @@ function add_test_receiving_sms(expected, pdu) {
}
function test_receiving_7bit_alphabets(lst, sst) {
let ril = newRadioInterfaceLayer();
let worker = newWriteHexOctetAsUint8Worker();
let helper = worker.GsmPDUHelper;
let buf = worker.Buf;
@ -405,8 +494,7 @@ function test_receiving_7bit_alphabets(lst, sst) {
for (let i = 0; i < text.length;) {
let len = Math.min(70, text.length - i);
let expected = text.substring(i, i + len);
let septets = helper._calculateLangEncodedSeptets(expected, langTable,
langShiftTable);
let septets = ril._countGsm7BitSeptets(expected, langTable, langShiftTable);
let rawBytes = get7bitRawBytes(expected);
let pdu = compose7bitPdu(lst, sst, rawBytes, septets);
add_test_receiving_sms(expected, pdu);

View File

@ -79,7 +79,7 @@ TelephonyCall::ChangeStateInternal(PRUint16 aCallState, bool aFireEvents)
stateString.AssignLiteral("dialing");
break;
case nsIRadioInterfaceLayer::CALL_STATE_ALERTING:
stateString.AssignLiteral("ringing");
stateString.AssignLiteral("alerting");
break;
case nsIRadioInterfaceLayer::CALL_STATE_BUSY:
stateString.AssignLiteral("busy");
@ -158,7 +158,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(TelephonyCall,
Telephony, "mTelephony")
NS_CYCLE_COLLECTION_TRAVERSE_EVENT_HANDLER(statechange)
NS_CYCLE_COLLECTION_TRAVERSE_EVENT_HANDLER(dialing)
NS_CYCLE_COLLECTION_TRAVERSE_EVENT_HANDLER(ringing)
NS_CYCLE_COLLECTION_TRAVERSE_EVENT_HANDLER(alerting)
NS_CYCLE_COLLECTION_TRAVERSE_EVENT_HANDLER(busy)
NS_CYCLE_COLLECTION_TRAVERSE_EVENT_HANDLER(connecting)
NS_CYCLE_COLLECTION_TRAVERSE_EVENT_HANDLER(connected)
@ -172,7 +172,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(TelephonyCall,
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mTelephony)
NS_CYCLE_COLLECTION_UNLINK_EVENT_HANDLER(statechange)
NS_CYCLE_COLLECTION_UNLINK_EVENT_HANDLER(dialing)
NS_CYCLE_COLLECTION_UNLINK_EVENT_HANDLER(ringing)
NS_CYCLE_COLLECTION_UNLINK_EVENT_HANDLER(alerting)
NS_CYCLE_COLLECTION_UNLINK_EVENT_HANDLER(busy)
NS_CYCLE_COLLECTION_UNLINK_EVENT_HANDLER(connecting)
NS_CYCLE_COLLECTION_UNLINK_EVENT_HANDLER(connected)
@ -240,7 +240,7 @@ TelephonyCall::HangUp()
NS_IMPL_EVENT_HANDLER(TelephonyCall, statechange)
NS_IMPL_EVENT_HANDLER(TelephonyCall, dialing)
NS_IMPL_EVENT_HANDLER(TelephonyCall, ringing)
NS_IMPL_EVENT_HANDLER(TelephonyCall, alerting)
NS_IMPL_EVENT_HANDLER(TelephonyCall, busy)
NS_IMPL_EVENT_HANDLER(TelephonyCall, connecting)
NS_IMPL_EVENT_HANDLER(TelephonyCall, connected)

View File

@ -54,7 +54,7 @@ class TelephonyCall : public nsDOMEventTargetHelper,
{
NS_DECL_EVENT_HANDLER(statechange)
NS_DECL_EVENT_HANDLER(dialing)
NS_DECL_EVENT_HANDLER(ringing)
NS_DECL_EVENT_HANDLER(alerting)
NS_DECL_EVENT_HANDLER(busy)
NS_DECL_EVENT_HANDLER(connecting)
NS_DECL_EVENT_HANDLER(connected)

View File

@ -41,7 +41,7 @@
interface nsIDOMEventListener;
[scriptable, builtinclass, uuid(832b7551-ff53-403f-9e2c-d7d28e2bb40b)]
[scriptable, builtinclass, uuid(f741d52a-38bd-48f8-838b-cf4cd74a6ea5)]
interface nsIDOMTelephonyCall : nsIDOMEventTarget
{
readonly attribute DOMString number;
@ -54,7 +54,7 @@ interface nsIDOMTelephonyCall : nsIDOMEventTarget
attribute nsIDOMEventListener onstatechange;
attribute nsIDOMEventListener ondialing;
attribute nsIDOMEventListener onringing;
attribute nsIDOMEventListener onalerting;
attribute nsIDOMEventListener onbusy;
attribute nsIDOMEventListener onconnecting;
attribute nsIDOMEventListener onconnected;

View File

@ -1 +1 @@
http://hg.mozilla.org/projects/addon-sdk/archive/28b4f9e190e3.tar.bz2
http://hg.mozilla.org/projects/addon-sdk/archive/3ce8a0112619.tar.bz2

View File

@ -31,11 +31,13 @@
login2 = Cc["@mozilla.org/login-manager/loginInfo;1"].createInstance(Ci.nsILoginInfo);
login2.init("http://mochi.test:8888", null, "mochirealm", "user1name", "user1pass", "", "");
pwmgr.addLogin(login2);
startCallbackTimer();
}
function cleanup() {
var pwmgr = Cc["@mozilla.org/login-manager;1"].getService(Ci.nsILoginManager);
pwmgr.removeLogin(login);
pwmgr.removeLogin(login2);
timer.cancel();
}
function makeXHR(expectedStatus, expectedText, extra) {
@ -59,7 +61,6 @@
function testNonAnonymousCredentials() {
var xhr = makeXHR(200, "OK");
xhr.send();
startCallbackTimer();
}
function testAnonymousCredentials() {
@ -77,10 +78,14 @@
xhr.send();
}
var gExpectedDialogs = 0;
var gCurrentTest;
function runNextTest() {
is(gExpectedDialogs, 0, "received expected number of auth dialogs");
Cc["@mozilla.org/network/http-auth-manager;1"].getService(Components.interfaces.nsIHttpAuthManager).clearAll();
if (pendingTests.length > 0) {
gCurrentTest = pendingTests.shift();
({expectedDialogs: gExpectedDialogs,
test: gCurrentTest}) = pendingTests.shift();
gCurrentTest.call(this);
} else {
cleanup();
@ -88,8 +93,9 @@
}
}
var pendingTests = [testNonAnonymousCredentials, testAnonymousCredentials,
testAnonymousNoAuth];
var pendingTests = [{expectedDialogs: 2, test: testNonAnonymousCredentials},
{expectedDialogs: 1, test: testAnonymousCredentials},
{expectedDialogs: 0, test: testAnonymousNoAuth}];
init();
runNextTest();
@ -97,9 +103,8 @@
{
var dialog = doc.getElementById("commonDialog");
dialog.acceptDialog();
if (gCurrentTest == testNonAnonymousCredentials) {
startCallbackTimer();
}
gExpectedDialogs--;
startCallbackTimer();
}
</script>
</body>

View File

@ -234,13 +234,12 @@ PL_DHashTableInit(PLDHashTable *table, const PLDHashTableOps *ops, void *data,
PRUint32 nbytes;
#ifdef DEBUG
if (entrySize > 10 * sizeof(void *)) {
if (entrySize > 16 * sizeof(void *)) {
printf_stderr(
"pldhash: for the table at address %p, the given entrySize"
" of %lu %s favors chaining over double hashing.\n",
" of %lu definitely favors chaining over double hashing.\n",
(void *) table,
(unsigned long) entrySize,
(entrySize > 16 * sizeof(void*)) ? "definitely" : "probably");
(unsigned long) entrySize);
}
#endif