mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 14:30:27 +00:00
import changes from `dev' branch of rmottola/Arctic-Fox:
- Bug 1195755: Don't assert recursion depth sanity on Mac, because there is none. r=me (e25096acc1) - Bug 1217940 - remove BindingUtils.h from CycleCollectedJSRuntime.cpp; r=mccr8 (012fad0b80) - Bug 1118285 - The browser.newtab.url preference is abused and should be removed. (ca573649c6) - Bug 1209591 - allow loadURI consumers to expose whether an error page was immediately loaded as result of an error, r=smaug,mak (c033d86f07) - Bug 1167132 - Part 14: [NetworkManager] Move network information into a separate interface (NetStats). r=ethan (87acc048cc) - Bug 1167132 - Part 15: [NetworkManager] Move network information into a separate interface (NetworkInterfaceList). r=echen (a2a96e481e) - Bug 1205240 - Add JSON Validation code in order to prevent invalid file. r=seanlin (8c7261ba8c) - Bug 1215429 - Add import statement in order to access file object in chrome code of TVSimulatorService. r=seanlin (5ba9e78581) - Bug 1217093 - Remove for-each from dom/. r=smaug (5af3efbd62) - var-let (576b2489ec) - Bug 1183440 - Replaces Promise.defer() with the Promise constructor in push tests. r=kitcambridge (16dfaa59b3) - Bug 1191453 - Drop subscriptions for a site when the user revokes push permissions. r=mt,MattN (5edd10e5ad) - Bug 1159641, Part 1 - Skip the permission check in `pushManager.getSubscription()`. r=mt (d399c496d7) - Bug 1159641, Part 2 - Use tasks in the Push permissions test. r=mt (132484c355) - Bug 1206302 - Use DOMException for Push errors. r=mt (5a675714fa) - Bug 1193365 - Disable push debug. r=kitcambridge (1dc20e69b0) - Bug 1219063, Part 1 - Use transactions for updating Push subscription permissions. r=mt (8c28453942) - Bug 1219063, Part 2 - Remove obsolete "push" permission. r=mt (84a36931cd) - Bug 1217065 - Unconditionally ack incoming updates. r=dragana,benbangert (e0bfa4454f) - Bug 1212593 - Fix PushService behavior when we are switching between push servers. r=kcambridge (0afa39e743) - Bug 1206163 - Retry failed register requests on reconnect. r=dragana (6ed1258b15) - Bug 1218591 - Reset the WebSocket retry counter when the server replies. r=dragana (64e800db60) - Bug 1210943 - Drop subscriptions unconditionally if the UAID changes. r=benbangert (52f538a7de) - Bug 1214366 - Part 1: Don't preprocess PushServiceWebSocket.jsm. r=kitcambridge (a78b9fc838) - Bug 1214366 - Part 3: Use getLastVisited equivalent in PushService.jsm. r=kitcambridge,rnewman (bc7004ad32) - Bug 1210896, Part 1 - Use Console.jsm to log Push errors. r=mt (04335cc37f) - Bug 1216683 - For the WebSocket version unregister should return true even if we are offline. r=kitcambridge (0f6e397a03) - Bug 1210896, Part 2 - Use JS errors to reject internal Push promises. r=mt (3546b2f7c8) - Bug 1223481 - Use the "potentially trustworthy origin" helper to validate Push server URLs. r=dragana (0c21f551f3) - Bug 1223202 - Only send subscription change events if the Push permission is granted. r=mt (afeaf0dceb) - Bug 1201128 - Don't send channel IDs in the Push handshake. r=nsm (dbbadb5c16) - var-let (a35cb6aeca) - Bug 1210211 - Part 1: Delay updating push quota. r=kitcambridge (53f5735ff0) - Bug 1210211 - Part 2: Notify Push service of visible notifications. r=baku (9182bcb7d1) - Bug 1170115 - Use clear-origin-data to remove Push records. r=allstars.chh (47f1070bab) - Bug 1211418 - Part 1: Ensure Data Consistency after Collision of SMS Segment. r=echen. (f2d5221984) - Bug 1211418 - Part 2: Add Test Coverage for the Collision of SMS Segment. r=echen. (06f7ba7308) - Bug 1159132 - Part 1: Use dun apn only when config ro.tethering.dun_required is set. r=echen (bbb4fd2798) - Bug 1159132 - Part 2: Set ro.tethering.dun_required when running dun test case. r=echen (11fe9344be) - Bug 1187262 - Let the flag 'Services.io.offline' reference the state of tethering. r=jjong (ee22fd9358) - Bug 1148671 - ipv6 and dual stack support on Lollipop. r=hchang (a9f7dc570e) - Bug 1173671 - just warn if we fail to remove old default routes. r=echen (b4ab24da9f) - Bug 1175817 - [NetworkManager] remove old default routes explicitly. r=echen,smaug (3f9a0b98ab) - Bug 1174998 - Part 1: add setMtu() support in NetworkService. r=echen,smaug (9621036470) - Bug 1174998 - Part 2: Set MTU for connected network interfaces. r=echen (397c898942) - Bug 1197667 - [NetworkManager] Part 1: add missing implementation for 'allNetworkInfo'. r=echen (a49fd3498b) - Bug 1197667 - [NetworkManager] Part 2: add test case for 'allNetworkInfo'. r=echen (942a52b0d4) - Bug 1057091 - Add USB tethring command supporting IPv6 outgoing interface. r=hchang (9210eb5a1d) - Bug 1177236 - Usage alert doesn't work when tethering is enabled. r=ethan (4bdd8ae226) - Bug 1168938 - Memory safety bug in NetworkUtils::postTetherInterfaceList. r=fabrice (97485ac95c) - Bug 1138757 - Part 1: Fix the logic of checking invalid port in CDMA WAP Push. r=echen (68dac00e52) - Bug 1138757 - Part 2: Add Test Coverage for CDMA Wap Push. r=echen (9d54278aa9) - Bug 1209891 - Do Not Reply Read-Report if a MMS Message Was Marked from Unread to Read Multiple Times. r=echen (421550db06) - var-let (2ed380bb64) - bug 1175005: performance regression. backout_f081c464c1e2 (28e1ee74b9) - Bug 1207665 - Block Intel GMA 3150 for d3d11/d2d on all drivers. (bug 1207665 part 1, r=jrmuizel). r=jrmuizel (bb8eac6fa8) - Bug 1188105: Parse bad driver versions. r=botond (8c856cac36) - Bug 1075089 - Move popup menu frame offset to LookAndFeel and fix default offset for OS X. r=Enn (e1f7d0c418) - Bug 1134385. Delete main thread assertion in CompositorVsyncDispatcher. r=kats (0945e91185) - some profiler stuff (d3d68abdad) - Bug 1156283 - Avoid shutdown observer race when shutting down gfx on Mac. r=roc (f66195546b)
This commit is contained in:
@@ -4630,7 +4630,10 @@ nsDocShell::LoadURIWithOptions(const char16_t* aURI,
|
||||
// what happens
|
||||
|
||||
if (NS_ERROR_MALFORMED_URI == rv) {
|
||||
DisplayLoadError(rv, uri, aURI, nullptr);
|
||||
if (DisplayLoadError(rv, uri, aURI, nullptr) &&
|
||||
(aLoadFlags & LOAD_FLAGS_ERROR_LOAD_CHANGES_RV) != 0) {
|
||||
return NS_ERROR_LOAD_SHOWED_ERRORPAGE;
|
||||
}
|
||||
}
|
||||
|
||||
if (NS_FAILED(rv) || !uri) {
|
||||
@@ -4695,8 +4698,10 @@ nsDocShell::LoadURIWithOptions(const char16_t* aURI,
|
||||
NS_IMETHODIMP
|
||||
nsDocShell::DisplayLoadError(nsresult aError, nsIURI* aURI,
|
||||
const char16_t* aURL,
|
||||
nsIChannel* aFailedChannel)
|
||||
nsIChannel* aFailedChannel,
|
||||
bool* aDisplayedErrorPage)
|
||||
{
|
||||
*aDisplayedErrorPage = false;
|
||||
// Get prompt and string bundle servcies
|
||||
nsCOMPtr<nsIPrompt> prompter;
|
||||
nsCOMPtr<nsIStringBundle> stringBundle;
|
||||
@@ -5068,8 +5073,10 @@ nsDocShell::DisplayLoadError(nsresult aError, nsIURI* aURI,
|
||||
|
||||
if (UseErrorPages()) {
|
||||
// Display an error page
|
||||
LoadErrorPage(aURI, aURL, errorPage.get(), error.get(),
|
||||
messageStr.get(), cssClass.get(), aFailedChannel);
|
||||
nsresult loadedPage = LoadErrorPage(aURI, aURL, errorPage.get(),
|
||||
error.get(), messageStr.get(),
|
||||
cssClass.get(), aFailedChannel);
|
||||
*aDisplayedErrorPage = NS_SUCCEEDED(loadedPage);
|
||||
} else {
|
||||
// The prompter reqires that our private window has a document (or it
|
||||
// asserts). Satisfy that assertion now since GetDoc will force
|
||||
@@ -10325,7 +10332,10 @@ nsDocShell::InternalLoad2(nsIURI* aURI,
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
nsCOMPtr<nsIChannel> chan(do_QueryInterface(req));
|
||||
DisplayLoadError(rv, aURI, nullptr, chan);
|
||||
if (DisplayLoadError(rv, aURI, nullptr, chan) &&
|
||||
(aFlags & LOAD_FLAGS_ERROR_LOAD_CHANGES_RV) != 0) {
|
||||
return NS_ERROR_LOAD_SHOWED_ERRORPAGE;
|
||||
}
|
||||
}
|
||||
|
||||
return rv;
|
||||
@@ -11713,7 +11723,7 @@ nsDocShell::ShouldAddToSessionHistory(nsIURI* aURI)
|
||||
// should just do a spec compare, rather than two gets of the scheme and
|
||||
// then the path. -Gagan
|
||||
nsresult rv;
|
||||
nsAutoCString buf, pref;
|
||||
nsAutoCString buf;
|
||||
|
||||
rv = aURI->GetScheme(buf);
|
||||
if (NS_FAILED(rv)) {
|
||||
@@ -11731,16 +11741,17 @@ nsDocShell::ShouldAddToSessionHistory(nsIURI* aURI)
|
||||
}
|
||||
}
|
||||
|
||||
rv = Preferences::GetDefaultCString("browser.newtab.url", &pref);
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
return true;
|
||||
// Check if the webbrowser chrome wants us to proceed - by default it ensures
|
||||
// aURI is not the newtab URI.
|
||||
nsCOMPtr<nsIWebBrowserChrome3> browserChrome3 = do_GetInterface(mTreeOwner);
|
||||
if (browserChrome3) {
|
||||
bool shouldAdd;
|
||||
rv = browserChrome3->ShouldAddToSessionHistory(this, aURI, &shouldAdd);
|
||||
NS_ENSURE_SUCCESS(rv, true);
|
||||
return shouldAdd;
|
||||
}
|
||||
|
||||
rv = aURI->GetSpec(buf);
|
||||
NS_ENSURE_SUCCESS(rv, true);
|
||||
|
||||
return !buf.Equals(pref);
|
||||
return true;
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
||||
@@ -733,6 +733,14 @@ protected:
|
||||
*/
|
||||
void MaybeInitTiming();
|
||||
|
||||
bool DisplayLoadError(nsresult aError, nsIURI* aURI, const char16_t* aURL,
|
||||
nsIChannel* aFailedChannel)
|
||||
{
|
||||
bool didDisplayLoadError = false;
|
||||
DisplayLoadError(aError, aURI, aURL, aFailedChannel, &didDisplayLoadError);
|
||||
return didDisplayLoadError;
|
||||
}
|
||||
|
||||
public:
|
||||
// Event type dispatched by RestorePresentation
|
||||
class RestorePresentationEvent : public nsRunnable
|
||||
|
||||
@@ -46,7 +46,7 @@ interface nsITabParent;
|
||||
|
||||
typedef unsigned long nsLoadFlags;
|
||||
|
||||
[scriptable, builtinclass, uuid(9f2babc4-4c2a-4cf7-929f-a1efc325b0df)]
|
||||
[scriptable, builtinclass, uuid(b1df6e41-c8dd-45c2-bc18-dd330d986214)]
|
||||
interface nsIDocShell : nsIDocShellTreeItem
|
||||
{
|
||||
/**
|
||||
@@ -449,11 +449,14 @@ interface nsIDocShell : nsIDocShellTreeItem
|
||||
* @param aURI nsIURI of the page where the error happened
|
||||
* @param aURL wstring of the page where the error happened
|
||||
* @param aFailedChannel The channel related to this error
|
||||
*
|
||||
* Returns whether or not we displayed an error page (note: will always
|
||||
* return false if in-content error pages are disabled!)
|
||||
*/
|
||||
void displayLoadError(in nsresult aError,
|
||||
in nsIURI aURI,
|
||||
in wstring aURL,
|
||||
[optional] in nsIChannel aFailedChannel);
|
||||
boolean displayLoadError(in nsresult aError,
|
||||
in nsIURI aURI,
|
||||
in wstring aURL,
|
||||
[optional] in nsIChannel aFailedChannel);
|
||||
|
||||
/**
|
||||
* The channel that failed to load and resulted in an error page.
|
||||
|
||||
@@ -16,7 +16,7 @@ interface nsIURI;
|
||||
* location, stop or restart an in process load, or determine where the object
|
||||
* has previously gone.
|
||||
*/
|
||||
[scriptable, uuid(e186891c-b053-4fe7-a268-a1c80234b8a2)]
|
||||
[scriptable, uuid(3ade79d4-8cb9-4952-b18d-4f9b63ca0d31)]
|
||||
interface nsIWebNavigation : nsISupports
|
||||
{
|
||||
/**
|
||||
@@ -184,6 +184,12 @@ interface nsIWebNavigation : nsISupports
|
||||
*/
|
||||
const unsigned long LOAD_FLAGS_DISALLOW_INHERIT_OWNER = 0x40000;
|
||||
|
||||
/**
|
||||
* Overwrite the returned error code with a specific result code
|
||||
* when an error page is displayed.
|
||||
*/
|
||||
const unsigned long LOAD_FLAGS_ERROR_LOAD_CHANGES_RV = 0x80000;
|
||||
|
||||
/**
|
||||
* This flag specifies that the URI may be submitted to a third-party
|
||||
* server for correction. This should only be applied to non-sensitive
|
||||
@@ -196,8 +202,6 @@ interface nsIWebNavigation : nsISupports
|
||||
*/
|
||||
const unsigned long LOAD_FLAGS_FIXUP_SCHEME_TYPOS = 0x200000;
|
||||
|
||||
/* Note that flag 0x80000 is available. */
|
||||
|
||||
/**
|
||||
* Loads a given URI. This will give priority to loading the requested URI
|
||||
* in the object implementing this interface. If it can't be loaded here
|
||||
|
||||
@@ -596,7 +596,7 @@ this.expandPermissions = function expandPermissions(aPermName, aAccess) {
|
||||
|
||||
// Add the same suffix to each of the additions.
|
||||
if (tableEntry.additional) {
|
||||
for each (let additional in tableEntry.additional) {
|
||||
for (let additional of tableEntry.additional) {
|
||||
permArr = permArr.concat(appendAccessToPermName(additional, requestedSuffixes));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,6 +87,9 @@ enum DOM4ErrorTypeCodeMap {
|
||||
BtAuthFailureError = 0,
|
||||
BtRmtDevDownError = 0,
|
||||
BtAuthRejectedError = 0,
|
||||
|
||||
/* Push API errors */
|
||||
PermissionDeniedError = 0,
|
||||
};
|
||||
|
||||
#define DOM4_MSG_DEF(name, message, nsresult) {(nsresult), name, #name, message},
|
||||
|
||||
@@ -151,5 +151,11 @@ DOM4_MSG_DEF(InvalidStateError, "A mutation operation was attempted on a file st
|
||||
DOM4_MSG_DEF(AbortError, "A request was aborted, for example through a call to FileHandle.abort.", NS_ERROR_DOM_FILEHANDLE_ABORT_ERR)
|
||||
DOM4_MSG_DEF(QuotaExceededError, "The current file handle exceeded its quota limitations.", NS_ERROR_DOM_FILEHANDLE_QUOTA_ERR)
|
||||
|
||||
/* Push API errors. */
|
||||
DOM4_MSG_DEF(InvalidStateError, "Invalid service worker registration.", NS_ERROR_DOM_PUSH_INVALID_REGISTRATION_ERR)
|
||||
DOM4_MSG_DEF(PermissionDeniedError, "User denied permission to use the Push API.", NS_ERROR_DOM_PUSH_DENIED_ERR)
|
||||
DOM4_MSG_DEF(AbortError, "Error retrieving push subscription.", NS_ERROR_DOM_PUSH_ABORT_ERR)
|
||||
DOM4_MSG_DEF(NetworkError, "Push service unreachable.", NS_ERROR_DOM_PUSH_SERVICE_UNREACHABLE)
|
||||
|
||||
DOM_MSG_DEF(NS_ERROR_DOM_JS_EXCEPTION, "A callback threw an exception")
|
||||
DOM_MSG_DEF(NS_ERROR_DOM_DOMEXCEPTION, "A DOMException was thrown")
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<script type="application/javascript">
|
||||
<![CDATA[
|
||||
var imports = [ "SimpleTest", "is", "isnot", "ok" ];
|
||||
for each (var name in imports) {
|
||||
for (var name of imports) {
|
||||
window[name] = window.opener.wrappedJSObject[name];
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ function handleRequest(request, response) {
|
||||
invalidHeaders.push("Authorization");
|
||||
}
|
||||
|
||||
for each (let header in invalidHeaders) {
|
||||
for (let header of invalidHeaders) {
|
||||
if (request.hasHeader(header)) {
|
||||
response.setStatusLine(null, 500, "Server Error");
|
||||
headers[header.toLowerCase()] = request.getHeader(header);
|
||||
|
||||
@@ -119,7 +119,7 @@
|
||||
mm.loadFrameScript("data:,(" + childFrameScript.toString() + ")();",
|
||||
false);
|
||||
|
||||
for each (let message in messages) {
|
||||
for (let message of messages) {
|
||||
mm.sendAsyncMessage("test:ipcClonedMessage", message);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -338,7 +338,7 @@ BrowserElementAuthPrompt.prototype = {
|
||||
prompt.authInfo.password = password;
|
||||
}
|
||||
|
||||
for each (let consumer in prompt.consumers) {
|
||||
for (let consumer of prompt.consumers) {
|
||||
if (!consumer.callback) {
|
||||
// Not having a callback means that consumer didn't provide it
|
||||
// or canceled the notification.
|
||||
|
||||
@@ -38,7 +38,7 @@ addLoadEvent(function() {
|
||||
}
|
||||
}
|
||||
|
||||
for each(var event in ["online", "offline"]) {
|
||||
for (var event of ["online", "offline"]) {
|
||||
document.documentElement.addEventListener(
|
||||
event,
|
||||
makeHandler("document.body.addEventListener('%1', ..., false)",
|
||||
|
||||
@@ -67,12 +67,12 @@ function createTestEventValue(name) {
|
||||
is(ev.pointerId, pointerId, "Correct pointerId");
|
||||
}
|
||||
|
||||
for each (let target in [document, window, testTarget, parent])
|
||||
for (let target of [document, window, testTarget, parent])
|
||||
target.addEventListener(name, check, false);
|
||||
|
||||
testTarget.dispatchEvent(event);
|
||||
|
||||
for each (let target in [document, window, testTarget, parent])
|
||||
for (let target of [document, window, testTarget, parent])
|
||||
target.removeEventListener(name, check, false);
|
||||
|
||||
|
||||
|
||||
@@ -29,12 +29,12 @@ and
|
||||
<script class="testbody" type="text/javascript; version=1.7">
|
||||
|
||||
/** Test for Bug 418756 and 617528 **/
|
||||
let group1;
|
||||
let group2;
|
||||
let group3;
|
||||
var group1;
|
||||
var group2;
|
||||
var group3;
|
||||
|
||||
let tags = ["input", "menuitem"];
|
||||
for each (let tag in tags) {
|
||||
var tags = ["input", "menuitem"];
|
||||
for (let tag of tags) {
|
||||
|
||||
function bounce(node) {
|
||||
let n = node.nextSibling;
|
||||
@@ -43,10 +43,10 @@ function bounce(node) {
|
||||
p.insertBefore(node, n);
|
||||
}
|
||||
|
||||
let createdNodes = [];
|
||||
var createdNodes = [];
|
||||
|
||||
function cleanup() {
|
||||
for each (let node in createdNodes) {
|
||||
for (let node of createdNodes) {
|
||||
if (node.parentNode) {
|
||||
node.parentNode.removeChild(node);
|
||||
}
|
||||
@@ -55,12 +55,12 @@ function cleanup() {
|
||||
createdNodes = [];
|
||||
}
|
||||
|
||||
let typeMapper = {
|
||||
var typeMapper = {
|
||||
'c': 'checkbox',
|
||||
'r': 'radio'
|
||||
};
|
||||
|
||||
let id = 0;
|
||||
var id = 0;
|
||||
|
||||
// type can be 'c' for 'checkbox' and 'r' for 'radio'
|
||||
function createNode(type, name, checked) {
|
||||
@@ -75,11 +75,11 @@ function createNode(type, name, checked) {
|
||||
return node;
|
||||
}
|
||||
|
||||
let types = ['c', 'r'];
|
||||
var types = ['c', 'r'];
|
||||
|
||||
// First make sure that setting .checked makes .defaultChecked changes no
|
||||
// longer affect .checked.
|
||||
for each (let type in types) {
|
||||
for (let type of types) {
|
||||
let n = createNode(type, '', false);
|
||||
is(n.defaultChecked, false, "Bogus defaultChecked on " + typeMapper[type]);
|
||||
is(n.checked, false, "Bogus checked on " + typeMapper[type]);
|
||||
@@ -109,7 +109,7 @@ cleanup();
|
||||
|
||||
// Now check that bouncing a control that's the only one of its kind has no
|
||||
// effect
|
||||
for each (let type in types) {
|
||||
for (let type of types) {
|
||||
let n = createNode(type, 'test1', true);
|
||||
$(tag == "input" ? "f1" : "m1").appendChild(n);
|
||||
n.checked = false;
|
||||
|
||||
@@ -57,3 +57,21 @@ interface nsIPushNotificationService : nsISupports
|
||||
*/
|
||||
jsval clearForDomain(in string domain);
|
||||
};
|
||||
|
||||
[scriptable, uuid(a2555e70-46f8-4b52-bf02-d978b979d143)]
|
||||
interface nsIPushQuotaManager : nsISupports
|
||||
{
|
||||
/**
|
||||
* Informs the quota manager that a notification
|
||||
* for the given origin has been shown. Used to
|
||||
* determine if push quota should be relaxed.
|
||||
*/
|
||||
void notificationForOriginShown(in string origin);
|
||||
|
||||
/**
|
||||
* Informs the quota manager that a notification
|
||||
* for the given origin has been closed. Used to
|
||||
* determine if push quota should be relaxed.
|
||||
*/
|
||||
void notificationForOriginClosed(in string origin);
|
||||
};
|
||||
|
||||
@@ -25,7 +25,7 @@ function isNetworkReady() {
|
||||
for (var i = 0; i < num; i++) {
|
||||
var ips = {};
|
||||
var prefixLengths = {};
|
||||
var length = itfList.getInterface(i).getAddresses(ips, prefixLengths);
|
||||
var length = itfList.getInterfaceInfo(i).getAddresses(ips, prefixLengths);
|
||||
|
||||
for (var j = 0; j < length; j++) {
|
||||
var ip = ips.value[j];
|
||||
|
||||
@@ -1647,7 +1647,7 @@ MmsService.prototype = {
|
||||
|
||||
savable.receivers = [];
|
||||
// We don't have Bcc in recevied MMS message.
|
||||
for each (let type in ["cc", "to"]) {
|
||||
for (let type of ["cc", "to"]) {
|
||||
if (intermediate.headers[type]) {
|
||||
if (intermediate.headers[type] instanceof Array) {
|
||||
for (let index in intermediate.headers[type]) {
|
||||
|
||||
@@ -673,7 +673,7 @@ MobileMessageDB.prototype = {
|
||||
stores = txn.objectStore(storeNames[0]);
|
||||
} else {
|
||||
stores = [];
|
||||
for each (let storeName in storeNames) {
|
||||
for (let storeName of storeNames) {
|
||||
if (DEBUG) debug("Retrieving object store " + storeName);
|
||||
stores.push(txn.objectStore(storeName));
|
||||
}
|
||||
@@ -1460,7 +1460,7 @@ MobileMessageDB.prototype = {
|
||||
let timestamp = messageRecord.timestamp;
|
||||
// Setup participantIdsIndex.
|
||||
messageRecord.participantIdsIndex = [];
|
||||
for each (let id in participantIds) {
|
||||
for (let id of participantIds) {
|
||||
messageRecord.participantIdsIndex.push([id, timestamp]);
|
||||
}
|
||||
if (threadRecord) {
|
||||
@@ -2857,7 +2857,7 @@ MobileMessageDB.prototype = {
|
||||
aMessageRecord.threadIdIndex = [threadId, timestamp];
|
||||
// Setup participantIdsIndex.
|
||||
aMessageRecord.participantIdsIndex = [];
|
||||
for each (let id in participantIds) {
|
||||
for (let id of participantIds) {
|
||||
aMessageRecord.participantIdsIndex.push([id, timestamp]);
|
||||
}
|
||||
|
||||
@@ -3827,8 +3827,35 @@ MobileMessageDB.prototype = {
|
||||
}
|
||||
|
||||
if (segmentRecord.segments[seq]) {
|
||||
if (DEBUG) debug("Got duplicated segment no. " + seq);
|
||||
return;
|
||||
if (segmentRecord.encoding == RIL.PDU_DCS_MSG_CODING_8BITS_ALPHABET &&
|
||||
segmentRecord.encoding == aSmsSegment.encoding &&
|
||||
segmentRecord.segments[seq].length == aSmsSegment.data.length &&
|
||||
segmentRecord.segments[seq].every(function(aElement, aIndex) {
|
||||
return aElement == aSmsSegment.data[aIndex];
|
||||
})) {
|
||||
if (DEBUG) {
|
||||
debug("Got duplicated binary segment no: " + seq);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (segmentRecord.encoding != RIL.PDU_DCS_MSG_CODING_8BITS_ALPHABET &&
|
||||
aSmsSegment.encoding != RIL.PDU_DCS_MSG_CODING_8BITS_ALPHABET &&
|
||||
segmentRecord.segments[seq] == aSmsSegment.body) {
|
||||
if (DEBUG) {
|
||||
debug("Got duplicated text segment no: " + seq);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Update mandatory properties to ensure that the segments could be
|
||||
// concatenated properly.
|
||||
segmentRecord.encoding = aSmsSegment.encoding;
|
||||
segmentRecord.originatorPort = aSmsSegment.originatorPort;
|
||||
segmentRecord.destinationPort = aSmsSegment.destinationPort;
|
||||
segmentRecord.teleservice = aSmsSegment.teleservice;
|
||||
// Decrease the counter for this collided segment.
|
||||
segmentRecord.receivedSegments--;
|
||||
}
|
||||
|
||||
segmentRecord.timestamp = aSmsSegment.timestamp;
|
||||
@@ -3847,11 +3874,11 @@ MobileMessageDB.prototype = {
|
||||
// save it into the segmentRecord.
|
||||
if (aSmsSegment.teleservice === RIL.PDU_CDMA_MSG_TELESERIVCIE_ID_WAP
|
||||
&& seq === 1) {
|
||||
if (aSmsSegment.originatorPort === Ci.nsIGonkSmsService.SMS_APPLICATION_PORT_INVALID) {
|
||||
if (aSmsSegment.originatorPort !== Ci.nsIGonkSmsService.SMS_APPLICATION_PORT_INVALID) {
|
||||
segmentRecord.originatorPort = aSmsSegment.originatorPort;
|
||||
}
|
||||
|
||||
if (aSmsSegment.destinationPort === Ci.nsIGonkSmsService.SMS_APPLICATION_PORT_INVALID) {
|
||||
if (aSmsSegment.destinationPort !== Ci.nsIGonkSmsService.SMS_APPLICATION_PORT_INVALID) {
|
||||
segmentRecord.destinationPort = aSmsSegment.destinationPort;
|
||||
}
|
||||
}
|
||||
@@ -4099,8 +4126,11 @@ MobileMessageDB.prototype = {
|
||||
* @param {boolean} value
|
||||
* The updated <code>read</code> value.
|
||||
* @param {boolean} aSendReadReport
|
||||
* <code>true</code> to update the <code>isReadReportSent</code>
|
||||
* property if the message is MMS.
|
||||
* <code>true</code> to reply the read report of an incoming MMS
|
||||
* message whose <code>isReadReportSent</code> is 'false'.
|
||||
* Note: <code>isReadReportSent</code> will be set to 'true' no
|
||||
* matter aSendReadReport is true or not when a message was marked
|
||||
* from UNREAD to READ. See bug 1180470 for the new UX policy.
|
||||
* @param {nsIMobileMessageCallback} aRequest
|
||||
* The callback object.
|
||||
*/
|
||||
@@ -4153,17 +4183,18 @@ MobileMessageDB.prototype = {
|
||||
messageRecord.read = value ? FILTER_READ_READ : FILTER_READ_UNREAD;
|
||||
messageRecord.readIndex = [messageRecord.read, messageRecord.timestamp];
|
||||
let readReportMessageId, readReportTo;
|
||||
if (aSendReadReport &&
|
||||
messageRecord.type == "mms" &&
|
||||
if (messageRecord.type == "mms" &&
|
||||
messageRecord.delivery == DELIVERY_RECEIVED &&
|
||||
messageRecord.read == FILTER_READ_READ &&
|
||||
messageRecord.headers["x-mms-read-report"] &&
|
||||
!messageRecord.isReadReportSent) {
|
||||
messageRecord.isReadReportSent = true;
|
||||
|
||||
let from = messageRecord.headers["from"];
|
||||
readReportTo = from && from.address;
|
||||
readReportMessageId = messageRecord.headers["message-id"];
|
||||
if (aSendReadReport) {
|
||||
let from = messageRecord.headers["from"];
|
||||
readReportTo = from && from.address;
|
||||
readReportMessageId = messageRecord.headers["message-id"];
|
||||
}
|
||||
}
|
||||
|
||||
if (DEBUG) debug("Message.read set to: " + value);
|
||||
@@ -4240,7 +4271,7 @@ MobileMessageDB.prototype = {
|
||||
}
|
||||
};
|
||||
|
||||
let FilterSearcherHelper = {
|
||||
var FilterSearcherHelper = {
|
||||
|
||||
/**
|
||||
* @param index
|
||||
|
||||
@@ -551,12 +551,35 @@ SmsService.prototype = {
|
||||
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(aSegment));
|
||||
if (options.encoding == Ci.nsIGonkSmsService.SMS_MESSAGE_ENCODING_8BITS_ALPHABET &&
|
||||
options.encoding == aSegment.encoding &&
|
||||
options.segments[seq].length == aSegment.data.length &&
|
||||
options.segments[seq].every(function(aElement, aIndex) {
|
||||
return aElement == aSegment.data[aIndex];
|
||||
})) {
|
||||
if (DEBUG) {
|
||||
debug("Got duplicated binary segment no: " + seq);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
|
||||
if (options.encoding != Ci.nsIGonkSmsService.SMS_MESSAGE_ENCODING_8BITS_ALPHABET &&
|
||||
aSegment.encoding != Ci.nsIGonkSmsService.SMS_MESSAGE_ENCODING_8BITS_ALPHABET &&
|
||||
options.segments[seq] == aSegment.body) {
|
||||
if (DEBUG) {
|
||||
debug("Got duplicated text segment no: " + seq);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Update mandatory properties to ensure that the segments could be
|
||||
// concatenated properly.
|
||||
options.encoding = aSegment.encoding;
|
||||
options.originatorPort = aSegment.originatorPort;
|
||||
options.destinationPort = aSegment.destinationPort;
|
||||
options.teleservice = aSegment.teleservice;
|
||||
// Decrease the counter for this collided segment.
|
||||
options.receivedSegments--;
|
||||
}
|
||||
|
||||
if (options.receivedSegments > 0) {
|
||||
|
||||
@@ -43,6 +43,7 @@ qemu = true
|
||||
[test_mmdb_full_storage.js]
|
||||
[test_mmdb_upgradeSchema_current_structure.js]
|
||||
[test_mmdb_upgradeSchema_22.js]
|
||||
[test_mmdb_ports_in_cdma_wappush.js]
|
||||
[test_replace_short_message_type.js]
|
||||
[test_mt_sms_concatenation.js]
|
||||
[test_error_of_mms_manual_retrieval.js]
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
MARIONETTE_TIMEOUT = 60000;
|
||||
MARIONETTE_HEAD_JS = 'mmdb_head.js';
|
||||
|
||||
const DBNAME = "test_mmdb_ports_in_cdma_wappush:" + newUUID();
|
||||
|
||||
const TEST_PDU = [
|
||||
{
|
||||
sender: "+0987654321",
|
||||
iccId: "1029384756",
|
||||
segmentRef: 0,
|
||||
segmentSeq: 1, // 1st segment
|
||||
segmentMaxSeq: 2,
|
||||
encoding: 0x04, // 8-bit encoding
|
||||
data: [0, 1, 2],
|
||||
teleservice: 0x1004, // PDU_CDMA_MSG_TELESERIVCIE_ID_WAP
|
||||
// Port numbers are only provided in 1st segment from CDMA SMS PDUs.
|
||||
originatorPort: 9200, // WDP_PORT_PUSH (server-side)
|
||||
destinationPort: 2948 // WDP_PORT_PUSH (client-side)
|
||||
},
|
||||
{
|
||||
sender: "+0987654321",
|
||||
iccId: "1029384756",
|
||||
segmentRef: 0,
|
||||
segmentSeq: 2, // 2nd segment
|
||||
segmentMaxSeq: 2,
|
||||
encoding: 0x04, // 8-bit encoding
|
||||
data: [3, 4, 5],
|
||||
teleservice: 0x1004, // PDU_CDMA_MSG_TELESERIVCIE_ID_WAP
|
||||
// Port numbers are only provided in 1st segment from CDMA SMS PDUs.
|
||||
originatorPort: Ci.nsIGonkSmsService.SMS_APPLICATION_PORT_INVALID,
|
||||
destinationPort: Ci.nsIGonkSmsService.SMS_APPLICATION_PORT_INVALID
|
||||
}
|
||||
];
|
||||
|
||||
function testSaveCdmaWapPush(aMmdb, aReverse) {
|
||||
log("testSaveCdmaWapPush(), aReverse: " + aReverse);
|
||||
|
||||
let testPDUs = aReverse ? Array.from(TEST_PDU).reverse() : TEST_PDU;
|
||||
let lengthOfFullData = 0;
|
||||
let promises = [];
|
||||
|
||||
for (let pdu of testPDUs) {
|
||||
lengthOfFullData += pdu.data.length;
|
||||
promises.push(saveSmsSegment(aMmdb, pdu));
|
||||
};
|
||||
|
||||
return Promise.all(promises)
|
||||
.then((aResults) => {
|
||||
// Complete message shall be returned after 2 segments are saved.
|
||||
let completeMsg = aResults[1][1];
|
||||
|
||||
is(completeMsg.originatorPort, TEST_PDU[0].originatorPort, "originatorPort");
|
||||
is(completeMsg.destinationPort, TEST_PDU[0].destinationPort, "destinationPort");
|
||||
|
||||
is(completeMsg.fullData.length, lengthOfFullData, "fullData.length");
|
||||
for (let i = 0; i < lengthOfFullData; i++) {
|
||||
is(completeMsg.fullData[i], i, "completeMsg.fullData[" + i + "]");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
startTestBase(function testCaseMain() {
|
||||
|
||||
let mmdb = newMobileMessageDB();
|
||||
return initMobileMessageDB(mmdb, DBNAME, 0)
|
||||
|
||||
.then(() => testSaveCdmaWapPush(mmdb, false))
|
||||
.then(() => testSaveCdmaWapPush(mmdb, true))
|
||||
|
||||
.then(() => closeMobileMessageDB(mmdb));
|
||||
});
|
||||
@@ -171,7 +171,7 @@ let LEGACY = {
|
||||
aMessageRecord.threadIdIndex = [threadId, timestamp];
|
||||
// Setup participantIdsIndex.
|
||||
aMessageRecord.participantIdsIndex = [];
|
||||
for each (let id in participantIds) {
|
||||
for (let id of participantIds) {
|
||||
aMessageRecord.participantIdsIndex.push([id, timestamp]);
|
||||
}
|
||||
|
||||
|
||||
@@ -25,8 +25,8 @@ function byteValueToHexString(aValue) {
|
||||
return str.length == 1 ? "0" + str : str;
|
||||
}
|
||||
|
||||
let ref_num = 0;
|
||||
function buildTextPdus(aDcs) {
|
||||
var ref_num = 0;
|
||||
function buildTextPdus(aDcs, aInsertDuplication = false) {
|
||||
ref_num++;
|
||||
|
||||
let IEI_CONCATE_1 = "0003" + byteValueToHexString(ref_num) + "0301";
|
||||
@@ -42,14 +42,24 @@ function buildTextPdus(aDcs) {
|
||||
let PDU_COMMON = PDU_SMSC_NONE + PDU_FIRST_OCTET + PDU_SENDER +
|
||||
PDU_PID_NORMAL + aDcs + PDU_TIMESTAMP + PDU_UDL + PDU_UDHL;
|
||||
|
||||
return [
|
||||
let pdus = [
|
||||
PDU_COMMON + IEI_CONCATE_1 + PDU_UD_A,
|
||||
PDU_COMMON + IEI_CONCATE_2 + PDU_UD_B,
|
||||
PDU_COMMON + IEI_CONCATE_3 + PDU_UD_C
|
||||
];
|
||||
|
||||
if (aInsertDuplication) {
|
||||
log("Insert Duplicated text PDU");
|
||||
// The 2nd PDU_UD_A is expected to be ignored.
|
||||
pdus.unshift(PDU_COMMON + IEI_CONCATE_1 + PDU_UD_A);
|
||||
// Expected to be replaced with PDU_UD_A;
|
||||
pdus.unshift(PDU_COMMON + IEI_CONCATE_1 + PDU_UD_C);
|
||||
}
|
||||
|
||||
return pdus;
|
||||
}
|
||||
|
||||
function buildBinaryPdus(aDcs) {
|
||||
function buildBinaryPdus(aDcs, aInsertDuplication = false) {
|
||||
ref_num++;
|
||||
let IEI_PORT = "05040B8423F0";
|
||||
|
||||
@@ -81,10 +91,20 @@ function buildBinaryPdus(aDcs) {
|
||||
return udl + ud;
|
||||
}
|
||||
|
||||
return [
|
||||
let pdus = [
|
||||
PDU_COMMON + construstBinaryUserData(PDU_DATA1, 1),
|
||||
PDU_COMMON + construstBinaryUserData(PDU_DATA2, 2)
|
||||
];
|
||||
|
||||
if (aInsertDuplication) {
|
||||
log("Insert Duplicated binary PDU");
|
||||
// The 2nd PDU_DATA1 is expected to be ignored.
|
||||
pdus.unshift(PDU_COMMON + construstBinaryUserData(PDU_DATA1, 1));
|
||||
// Expected to be replaced with PDU_DATA1;
|
||||
pdus.unshift(PDU_COMMON + construstBinaryUserData(PDU_DATA2, 1));
|
||||
}
|
||||
|
||||
return pdus;
|
||||
}
|
||||
|
||||
function verifyTextMessage(aMessage, aMessageClass) {
|
||||
@@ -104,12 +124,16 @@ function verifyBinaryMessage(aMessage) {
|
||||
function testText(aDcs, aClass) {
|
||||
log("testText(): aDcs = " + aDcs + ", aClass = " + aClass);
|
||||
return sendMultipleRawSmsToEmulatorAndWait(buildTextPdus(aDcs))
|
||||
.then((results) => verifyTextMessage(results[0].message, aClass))
|
||||
.then(() => sendMultipleRawSmsToEmulatorAndWait(buildTextPdus(aDcs, true)))
|
||||
.then((results) => verifyTextMessage(results[0].message, aClass));
|
||||
}
|
||||
|
||||
function testBinary(aDcs) {
|
||||
log("testBinary(): aDcs = " + aDcs);
|
||||
return sendMultipleRawSmsToEmulatorAndWait(buildBinaryPdus(aDcs))
|
||||
.then((results) => verifyBinaryMessage(results[0].message))
|
||||
.then(() => sendMultipleRawSmsToEmulatorAndWait(buildBinaryPdus(aDcs, true)))
|
||||
.then((results) => verifyBinaryMessage(results[0].message));
|
||||
}
|
||||
|
||||
|
||||
@@ -26,8 +26,8 @@ const NET_NETWORKSTATSSERVICE_CID = Components.ID("{18725604-e9ac-488a-8aa0-2471
|
||||
const TOPIC_BANDWIDTH_CONTROL = "netd-bandwidth-control"
|
||||
|
||||
const TOPIC_CONNECTION_STATE_CHANGED = "network-connection-state-changed";
|
||||
const NET_TYPE_WIFI = Ci.nsINetworkInterface.NETWORK_TYPE_WIFI;
|
||||
const NET_TYPE_MOBILE = Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE;
|
||||
const NET_TYPE_WIFI = Ci.nsINetworkInfo.NETWORK_TYPE_WIFI;
|
||||
const NET_TYPE_MOBILE = Ci.nsINetworkInfo.NETWORK_TYPE_MOBILE;
|
||||
|
||||
// Networks have different status that NetworkStats API needs to be aware of.
|
||||
// Network is present and ready, so NetworkManager provides the whole info.
|
||||
@@ -192,10 +192,10 @@ this.NetworkStatsService = {
|
||||
// the stats are updated for the new interface without waiting to
|
||||
// complete the updating period.
|
||||
|
||||
let network = aSubject.QueryInterface(Ci.nsINetworkInterface);
|
||||
debug("Network " + network.name + " of type " + network.type + " status change");
|
||||
let networkInfo = aSubject.QueryInterface(Ci.nsINetworkInfo);
|
||||
debug("Network " + networkInfo.name + " of type " + networkInfo.type + " status change");
|
||||
|
||||
let netId = this.convertNetworkInterface(network);
|
||||
let netId = this.convertNetworkInfo(networkInfo);
|
||||
if (!netId) {
|
||||
break;
|
||||
}
|
||||
@@ -269,33 +269,33 @@ this.NetworkStatsService = {
|
||||
return networks;
|
||||
},
|
||||
|
||||
convertNetworkInterface: function(aNetwork) {
|
||||
if (aNetwork.type != NET_TYPE_MOBILE &&
|
||||
aNetwork.type != NET_TYPE_WIFI) {
|
||||
convertNetworkInfo: function(aNetworkInfo) {
|
||||
if (aNetworkInfo.type != NET_TYPE_MOBILE &&
|
||||
aNetworkInfo.type != NET_TYPE_WIFI) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let id = '0';
|
||||
if (aNetwork.type == NET_TYPE_MOBILE) {
|
||||
if (!(aNetwork instanceof Ci.nsIRilNetworkInterface)) {
|
||||
debug("Error! Mobile network should be an nsIRilNetworkInterface!");
|
||||
if (aNetworkInfo.type == NET_TYPE_MOBILE) {
|
||||
if (!(aNetworkInfo instanceof Ci.nsIRilNetworkInfo)) {
|
||||
debug("Error! Mobile network should be an nsIRilNetworkInfo!");
|
||||
return null;
|
||||
}
|
||||
|
||||
let rilNetwork = aNetwork.QueryInterface(Ci.nsIRilNetworkInterface);
|
||||
let rilNetwork = aNetworkInfo.QueryInterface(Ci.nsIRilNetworkInfo);
|
||||
id = rilNetwork.iccId;
|
||||
}
|
||||
|
||||
let netId = this.getNetworkId(id, aNetwork.type);
|
||||
let netId = this.getNetworkId(id, aNetworkInfo.type);
|
||||
|
||||
if (!this._networks[netId]) {
|
||||
this._networks[netId] = Object.create(null);
|
||||
this._networks[netId].network = { id: id,
|
||||
type: aNetwork.type };
|
||||
type: aNetworkInfo.type };
|
||||
}
|
||||
|
||||
this._networks[netId].status = NETWORK_STATUS_READY;
|
||||
this._networks[netId].interfaceName = aNetwork.name;
|
||||
this._networks[netId].interfaceName = aNetworkInfo.name;
|
||||
return netId;
|
||||
},
|
||||
|
||||
@@ -738,10 +738,10 @@ this.NetworkStatsService = {
|
||||
/*
|
||||
* Function responsible for receiving stats which are not from netd.
|
||||
*/
|
||||
saveStats: function saveStats(aAppId, aIsInBrowser, aServiceType, aNetwork,
|
||||
saveStats: function saveStats(aAppId, aIsInBrowser, aServiceType, aNetworkInfo,
|
||||
aTimeStamp, aRxBytes, aTxBytes, aIsAccumulative,
|
||||
aCallback) {
|
||||
let netId = this.convertNetworkInterface(aNetwork);
|
||||
let netId = this.convertNetworkInfo(aNetworkInfo);
|
||||
if (!netId) {
|
||||
if (aCallback) {
|
||||
aCallback(false, "Invalid network type");
|
||||
|
||||
@@ -29,19 +29,19 @@ NetworkStatsServiceProxy.prototype = {
|
||||
* Function called in the protocol layer (HTTP, FTP, WebSocket ...etc)
|
||||
* to pass the per-app stats to NetworkStatsService.
|
||||
*/
|
||||
saveAppStats: function saveAppStats(aAppId, aIsInBrowser, aNetwork, aTimeStamp,
|
||||
saveAppStats: function saveAppStats(aAppId, aIsInBrowser, aNetworkInfo, aTimeStamp,
|
||||
aRxBytes, aTxBytes, aIsAccumulative,
|
||||
aCallback) {
|
||||
if (!aNetwork) {
|
||||
if (!aNetworkInfo) {
|
||||
if (DEBUG) {
|
||||
debug("|aNetwork| is not specified. Failed to save stats. Returning.");
|
||||
debug("|aNetworkInfo| is not specified. Failed to save stats. Returning.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (DEBUG) {
|
||||
debug("saveAppStats: " + aAppId + " " + aIsInBrowser + " " +
|
||||
aNetwork.type + " " + aTimeStamp + " " +
|
||||
aNetworkInfo.type + " " + aTimeStamp + " " +
|
||||
aRxBytes + " " + aTxBytes + " " + aIsAccumulative);
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ NetworkStatsServiceProxy.prototype = {
|
||||
aCallback = aCallback.notify;
|
||||
}
|
||||
|
||||
NetworkStatsService.saveStats(aAppId, aIsInBrowser, "", aNetwork,
|
||||
NetworkStatsService.saveStats(aAppId, aIsInBrowser, "", aNetworkInfo,
|
||||
aTimeStamp, aRxBytes, aTxBytes,
|
||||
aIsAccumulative, aCallback);
|
||||
},
|
||||
@@ -58,18 +58,18 @@ NetworkStatsServiceProxy.prototype = {
|
||||
* Function called in the points of different system services
|
||||
* to pass the per-service stats to NetworkStatsService.
|
||||
*/
|
||||
saveServiceStats: function saveServiceStats(aServiceType, aNetwork,
|
||||
saveServiceStats: function saveServiceStats(aServiceType, aNetworkInfo,
|
||||
aTimeStamp, aRxBytes, aTxBytes,
|
||||
aIsAccumulative, aCallback) {
|
||||
if (!aNetwork) {
|
||||
if (!aNetworkInfo) {
|
||||
if (DEBUG) {
|
||||
debug("|aNetwork| is not specified. Failed to save stats. Returning.");
|
||||
debug("|aNetworkInfo| is not specified. Failed to save stats. Returning.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (DEBUG) {
|
||||
debug("saveServiceStats: " + aServiceType + " " + aNetwork.type + " " +
|
||||
debug("saveServiceStats: " + aServiceType + " " + aNetworkInfo.type + " " +
|
||||
aTimeStamp + " " + aRxBytes + " " + aTxBytes + " " +
|
||||
aIsAccumulative);
|
||||
}
|
||||
@@ -78,7 +78,7 @@ NetworkStatsServiceProxy.prototype = {
|
||||
aCallback = aCallback.notify;
|
||||
}
|
||||
|
||||
NetworkStatsService.saveStats(0, false, aServiceType ,aNetwork, aTimeStamp,
|
||||
NetworkStatsService.saveStats(0, false, aServiceType , aNetworkInfo, aTimeStamp,
|
||||
aRxBytes, aTxBytes, aIsAccumulative,
|
||||
aCallback);
|
||||
},
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
interface nsINetworkInterface;
|
||||
interface nsINetworkInfo;
|
||||
|
||||
[scriptable, function, uuid(5f821529-1d80-4ab5-a933-4e1b3585b6bc)]
|
||||
interface nsINetworkStatsServiceProxyCallback : nsISupports
|
||||
@@ -16,7 +16,7 @@ interface nsINetworkStatsServiceProxyCallback : nsISupports
|
||||
void notify(in boolean aResult, in jsval aMessage);
|
||||
};
|
||||
|
||||
[scriptable, uuid(98fd8f69-784e-4626-aa59-56d6436a3c24)]
|
||||
[scriptable, uuid(f4f3e901-e102-499d-9d37-dc9951f52df7)]
|
||||
interface nsINetworkStatsServiceProxy : nsISupports
|
||||
{
|
||||
/*
|
||||
@@ -32,7 +32,7 @@ interface nsINetworkStatsServiceProxy : nsISupports
|
||||
*/
|
||||
void saveAppStats(in unsigned long aAppId,
|
||||
in boolean aIsInBrowser,
|
||||
in nsINetworkInterface aNetwork,
|
||||
in nsINetworkInfo aNetworkInfo,
|
||||
in unsigned long long aTimeStamp,
|
||||
in unsigned long long aRxBytes,
|
||||
in unsigned long long aTxBytes,
|
||||
@@ -50,7 +50,7 @@ interface nsINetworkStatsServiceProxy : nsISupports
|
||||
* @param aCallback an optional callback
|
||||
*/
|
||||
void saveServiceStats(in string aServiceType,
|
||||
in nsINetworkInterface aNetwork,
|
||||
in nsINetworkInfo aNetworkInfo,
|
||||
in unsigned long long aTimeStamp,
|
||||
in unsigned long long aRxBytes,
|
||||
in unsigned long long aTxBytes,
|
||||
|
||||
@@ -936,7 +936,7 @@ add_test(function test_addAlarm() {
|
||||
netStatsDb.addAlarm(alarms[0], function(error, result) {
|
||||
do_check_eq(error, null);
|
||||
alarmsDbId = result;
|
||||
netStatsDb.getAlarms(Ci.nsINetworkInterface.NETWORK_TYPE_WIFI, exampleManifestURL, function(error, result) {
|
||||
netStatsDb.getAlarms(Ci.nsINetworkInfo.NETWORK_TYPE_WIFI, exampleManifestURL, function(error, result) {
|
||||
do_check_eq(error, null);
|
||||
do_check_eq(result.length, 1);
|
||||
do_check_eq(result[0].id, alarmsDbId);
|
||||
|
||||
@@ -260,7 +260,7 @@ add_test(function test_fireAlarm() {
|
||||
NetworkStatsService._networks[wifiId].status = NETWORK_STATUS_STANDBY;
|
||||
|
||||
NetworkStatsService._db.addAlarm(alarm, function addSuccessCb(error, newId) {
|
||||
NetworkStatsService._db.getAlarms(Ci.nsINetworkInterface.NETWORK_TYPE_WIFI,
|
||||
NetworkStatsService._db.getAlarms(Ci.nsINetworkInfo.NETWORK_TYPE_WIFI,
|
||||
testManifestURL, function onGet(error, result) {
|
||||
do_check_eq(error, null);
|
||||
do_check_eq(result.length, 1);
|
||||
@@ -272,7 +272,7 @@ add_test(function test_fireAlarm() {
|
||||
result[0].manifestURL = testManifestURL;
|
||||
|
||||
NetworkStatsService._fireAlarm(result[0], false);
|
||||
NetworkStatsService._db.getAlarms(Ci.nsINetworkInterface.NETWORK_TYPE_WIFI,
|
||||
NetworkStatsService._db.getAlarms(Ci.nsINetworkInfo.NETWORK_TYPE_WIFI,
|
||||
testManifestURL, function onGet(error, result) {
|
||||
do_check_eq(error, undefined);
|
||||
do_check_eq(result.length, 0);
|
||||
|
||||
@@ -9,24 +9,24 @@ XPCOMUtils.defineLazyServiceGetter(this, "nssProxy",
|
||||
"@mozilla.org/networkstatsServiceProxy;1",
|
||||
"nsINetworkStatsServiceProxy");
|
||||
|
||||
function mokConvertNetworkInterface() {
|
||||
NetworkStatsService.convertNetworkInterface = function(aNetwork) {
|
||||
if (aNetwork.type != Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE &&
|
||||
aNetwork.type != Ci.nsINetworkInterface.NETWORK_TYPE_WIFI) {
|
||||
function mokConvertNetworkInfo() {
|
||||
NetworkStatsService.convertNetworkInfo = function(aNetworkInfo) {
|
||||
if (aNetworkInfo.type != Ci.nsINetworkInfo.NETWORK_TYPE_MOBILE &&
|
||||
aNetworkInfo.type != Ci.nsINetworkInfo.NETWORK_TYPE_WIFI) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let id = '0';
|
||||
if (aNetwork.type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE) {
|
||||
if (aNetworkInfo.type == Ci.nsINetworkInfo.NETWORK_TYPE_MOBILE) {
|
||||
id = '1234'
|
||||
}
|
||||
|
||||
let netId = this.getNetworkId(id, aNetwork.type);
|
||||
let netId = this.getNetworkId(id, aNetworkInfo.type);
|
||||
|
||||
if (!this._networks[netId]) {
|
||||
this._networks[netId] = Object.create(null);
|
||||
this._networks[netId].network = { id: id,
|
||||
type: aNetwork.type };
|
||||
type: aNetworkInfo.type };
|
||||
}
|
||||
|
||||
return netId;
|
||||
@@ -37,13 +37,12 @@ add_test(function test_saveAppStats() {
|
||||
var cachedStats = NetworkStatsService.cachedStats;
|
||||
var timestamp = NetworkStatsService.cachedStatsDate.getTime();
|
||||
|
||||
// Create to fake nsINetworkInterfaces. As nsINetworkInterface can not
|
||||
// be instantiated, these two vars will emulate it by filling the properties
|
||||
// that will be used.
|
||||
var wifi = {type: Ci.nsINetworkInterface.NETWORK_TYPE_WIFI, id: "0"};
|
||||
var mobile = {type: Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE, id: "1234"};
|
||||
// Create to fake nsINetworkInfos. As nsINetworkInfo can not be instantiated,
|
||||
// these two vars will emulate it by filling the properties that will be used.
|
||||
var wifi = {type: Ci.nsINetworkInfo.NETWORK_TYPE_WIFI, id: "0"};
|
||||
var mobile = {type: Ci.nsINetworkInfo.NETWORK_TYPE_MOBILE, id: "1234"};
|
||||
|
||||
// Insert fake mobile network interface in NetworkStatsService
|
||||
// Insert fake mobile network info in NetworkStatsService
|
||||
var mobileNetId = NetworkStatsService.getNetworkId(mobile.id, mobile.type);
|
||||
|
||||
do_check_eq(Object.keys(cachedStats).length, 0);
|
||||
@@ -83,13 +82,12 @@ add_test(function test_saveAppStats() {
|
||||
add_test(function test_saveServiceStats() {
|
||||
var timestamp = NetworkStatsService.cachedStatsDate.getTime();
|
||||
|
||||
// Create to fake nsINetworkInterfaces. As nsINetworkInterface can not
|
||||
// be instantiated, these two vars will emulate it by filling the properties
|
||||
// that will be used.
|
||||
var wifi = {type: Ci.nsINetworkInterface.NETWORK_TYPE_WIFI, id: "0"};
|
||||
var mobile = {type: Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE, id: "1234"};
|
||||
// Create to fake nsINetworkInfos. As nsINetworkInfo can not be instantiated,
|
||||
// these two vars will emulate it by filling the properties that will be used.
|
||||
var wifi = {type: Ci.nsINetworkInfo.NETWORK_TYPE_WIFI, id: "0"};
|
||||
var mobile = {type: Ci.nsINetworkInfo.NETWORK_TYPE_MOBILE, id: "1234"};
|
||||
|
||||
// Insert fake mobile network interface in NetworkStatsService
|
||||
// Insert fake mobile network info in NetworkStatsService
|
||||
var mobileNetId = NetworkStatsService.getNetworkId(mobile.id, mobile.type);
|
||||
|
||||
NetworkStatsService.updateCachedStats(function (success, msg) {
|
||||
@@ -138,7 +136,7 @@ add_test(function test_saveStatsWithDifferentDates() {
|
||||
var today = NetworkStatsService.cachedStatsDate;
|
||||
var tomorrow = new Date(today.getTime() + (24 * 60 * 60 * 1000));
|
||||
|
||||
var mobile = {type: Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE, id: "1234"};
|
||||
var mobile = {type: Ci.nsINetworkInfo.NETWORK_TYPE_MOBILE, id: "1234"};
|
||||
|
||||
NetworkStatsService.updateCachedStats(function (success, message) {
|
||||
do_check_eq(success, true);
|
||||
@@ -173,7 +171,7 @@ add_test(function test_saveStatsWithDifferentDates() {
|
||||
add_test(function test_saveStatsWithMaxCachedTraffic() {
|
||||
var timestamp = NetworkStatsService.cachedStatsDate.getTime();
|
||||
var maxtraffic = NetworkStatsService.maxCachedTraffic;
|
||||
var wifi = {type: Ci.nsINetworkInterface.NETWORK_TYPE_WIFI, id: "0"};
|
||||
var wifi = {type: Ci.nsINetworkInfo.NETWORK_TYPE_WIFI, id: "0"};
|
||||
|
||||
NetworkStatsService.updateCachedStats(function (success, message) {
|
||||
do_check_eq(success, true);
|
||||
@@ -199,11 +197,11 @@ add_test(function test_saveAppStats() {
|
||||
var cachedStats = NetworkStatsService.cachedStats;
|
||||
var timestamp = NetworkStatsService.cachedStatsDate.getTime();
|
||||
|
||||
// Create to fake nsINetworkInterfaces. As nsINetworkInterface can not
|
||||
// Create to fake nsINetworkInfo. As nsINetworkInfo can not
|
||||
// be instantiated, these two vars will emulate it by filling the properties
|
||||
// that will be used.
|
||||
var wifi = {type: Ci.nsINetworkInterface.NETWORK_TYPE_WIFI, id: "0"};
|
||||
var mobile = {type: Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE, id: "1234"};
|
||||
var wifi = {type: Ci.nsINetworkInfo.NETWORK_TYPE_WIFI, id: "0"};
|
||||
var mobile = {type: Ci.nsINetworkInfo.NETWORK_TYPE_MOBILE, id: "1234"};
|
||||
|
||||
// Insert fake mobile network interface in NetworkStatsService
|
||||
var mobileNetId = NetworkStatsService.getNetworkId(mobile.id, mobile.type);
|
||||
@@ -232,9 +230,9 @@ function run_test() {
|
||||
|
||||
Cu.import("resource://gre/modules/NetworkStatsService.jsm");
|
||||
|
||||
// Function convertNetworkInterface of NetworkStatsService causes errors when dealing
|
||||
// Function convertNetworkInfo of NetworkStatsService causes errors when dealing
|
||||
// with RIL to get the iccid, so overwrite it.
|
||||
mokConvertNetworkInterface();
|
||||
mokConvertNetworkInfo();
|
||||
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
@@ -51,6 +51,10 @@
|
||||
#include "nsIDOMDesktopNotification.h"
|
||||
#endif
|
||||
|
||||
#ifndef MOZ_SIMPLEPUSH
|
||||
#include "nsIPushNotificationService.h"
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
@@ -651,6 +655,8 @@ protected:
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
}
|
||||
|
||||
nsresult AdjustPushQuota(const char* aTopic);
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(NotificationObserver, nsIObserver)
|
||||
@@ -1172,11 +1178,39 @@ NotificationObserver::Observe(nsISupports* aSubject, const char* aTopic,
|
||||
ContentChild::GetSingleton()->SendOpenNotificationSettings(
|
||||
IPC::Principal(mPrincipal));
|
||||
return NS_OK;
|
||||
} else if (!strcmp("alertshow", aTopic) ||
|
||||
!strcmp("alertfinished", aTopic)) {
|
||||
Unused << NS_WARN_IF(NS_FAILED(AdjustPushQuota(aTopic)));
|
||||
}
|
||||
|
||||
return mObserver->Observe(aSubject, aTopic, aData);
|
||||
}
|
||||
|
||||
nsresult
|
||||
NotificationObserver::AdjustPushQuota(const char* aTopic)
|
||||
{
|
||||
#ifdef MOZ_SIMPLEPUSH
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
#else
|
||||
nsCOMPtr<nsIPushQuotaManager> pushQuotaManager =
|
||||
do_GetService("@mozilla.org/push/NotificationService;1");
|
||||
if (!pushQuotaManager) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsAutoCString origin;
|
||||
nsresult rv = mPrincipal->GetOrigin(origin);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (!strcmp("alertshow", aTopic)) {
|
||||
return pushQuotaManager->NotificationForOriginShown(origin.get());
|
||||
}
|
||||
return pushQuotaManager->NotificationForOriginClosed(origin.get());
|
||||
#endif
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
MainThreadNotificationObserver::Observe(nsISupports* aSubject, const char* aTopic,
|
||||
const char16_t* aData)
|
||||
|
||||
@@ -45,7 +45,7 @@ let PaymentManager = {
|
||||
}
|
||||
} catch(e) {}
|
||||
|
||||
for each (let msgname in PAYMENT_IPC_MSG_NAMES) {
|
||||
for (let msgname of PAYMENT_IPC_MSG_NAMES) {
|
||||
ppmm.addMessageListener(msgname, this);
|
||||
}
|
||||
|
||||
@@ -401,7 +401,7 @@ let PaymentManager = {
|
||||
|
||||
observe: function observe(subject, topic, data) {
|
||||
if (topic == "xpcom-shutdown") {
|
||||
for each (let msgname in PAYMENT_IPC_MSG_NAMES) {
|
||||
for (let msgname of PAYMENT_IPC_MSG_NAMES) {
|
||||
ppmm.removeMessageListener(msgname, this);
|
||||
}
|
||||
this.registeredProviders = null;
|
||||
|
||||
+127
-115
@@ -4,14 +4,6 @@
|
||||
|
||||
"use strict";
|
||||
|
||||
// Don't modify this, instead set dom.push.debug.
|
||||
let gDebuggingEnabled = true;
|
||||
|
||||
function debug(s) {
|
||||
if (gDebuggingEnabled)
|
||||
dump("-*- Push.js: " + s + "\n");
|
||||
}
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cu = Components.utils;
|
||||
@@ -21,6 +13,14 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/DOMRequestHelper.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "console", () => {
|
||||
let {ConsoleAPI} = Cu.import("resource://gre/modules/Console.jsm", {});
|
||||
return new ConsoleAPI({
|
||||
maxLogLevelPref: "dom.push.loglevel",
|
||||
prefix: "Push",
|
||||
});
|
||||
});
|
||||
|
||||
const PUSH_CID = Components.ID("{cde1d019-fad8-4044-b141-65fb4fb7a245}");
|
||||
|
||||
/**
|
||||
@@ -29,7 +29,7 @@ const PUSH_CID = Components.ID("{cde1d019-fad8-4044-b141-65fb4fb7a245}");
|
||||
* one actually performing all operations.
|
||||
*/
|
||||
function Push() {
|
||||
debug("Push Constructor");
|
||||
console.debug("Push()");
|
||||
}
|
||||
|
||||
Push.prototype = {
|
||||
@@ -44,11 +44,7 @@ Push.prototype = {
|
||||
Ci.nsIObserver]),
|
||||
|
||||
init: function(aWindow) {
|
||||
// Set debug first so that all debugging actually works.
|
||||
// NOTE: We don't add an observer here like in PushService. Flipping the
|
||||
// pref will require a reload of the app/page, which seems acceptable.
|
||||
gDebuggingEnabled = Services.prefs.getBoolPref("dom.push.debug");
|
||||
debug("init()");
|
||||
console.debug("init()");
|
||||
|
||||
this._window = aWindow;
|
||||
|
||||
@@ -60,127 +56,69 @@ Push.prototype = {
|
||||
},
|
||||
|
||||
setScope: function(scope){
|
||||
debug('setScope ' + scope);
|
||||
console.debug("setScope()", scope);
|
||||
this._scope = scope;
|
||||
},
|
||||
|
||||
askPermission: function (aAllowCallback, aCancelCallback) {
|
||||
debug("askPermission");
|
||||
console.debug("askPermission()");
|
||||
|
||||
let principal = this._window.document.nodePrincipal;
|
||||
let type = "push";
|
||||
let permValue =
|
||||
Services.perms.testExactPermissionFromPrincipal(principal, type);
|
||||
return this.createPromise((resolve, reject) => {
|
||||
let permissionDenied = () => {
|
||||
reject(new this._window.DOMException(
|
||||
"User denied permission to use the Push API",
|
||||
"PermissionDeniedError"
|
||||
));
|
||||
};
|
||||
|
||||
if (permValue == Ci.nsIPermissionManager.ALLOW_ACTION) {
|
||||
aAllowCallback();
|
||||
return;
|
||||
}
|
||||
let permission = Ci.nsIPermissionManager.UNKNOWN_ACTION;
|
||||
try {
|
||||
permission = this._testPermission();
|
||||
} catch (e) {
|
||||
permissionDenied();
|
||||
return;
|
||||
}
|
||||
|
||||
if (permValue == Ci.nsIPermissionManager.DENY_ACTION) {
|
||||
aCancelCallback();
|
||||
return;
|
||||
}
|
||||
|
||||
// Create an array with a single nsIContentPermissionType element.
|
||||
type = {
|
||||
type: "push",
|
||||
access: null,
|
||||
options: [],
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentPermissionType])
|
||||
};
|
||||
let typeArray = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
|
||||
typeArray.appendElement(type, false);
|
||||
|
||||
// create a nsIContentPermissionRequest
|
||||
let request = {
|
||||
types: typeArray,
|
||||
principal: principal,
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentPermissionRequest]),
|
||||
allow: function() {
|
||||
aAllowCallback();
|
||||
},
|
||||
cancel: function() {
|
||||
aCancelCallback();
|
||||
},
|
||||
window: this._window
|
||||
};
|
||||
|
||||
// Using askPermission from nsIDOMWindowUtils that takes care of the
|
||||
// remoting if needed.
|
||||
let windowUtils = this._window.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils);
|
||||
windowUtils.askPermission(request);
|
||||
},
|
||||
|
||||
getEndpointResponse: function(fn) {
|
||||
debug("GetEndpointResponse " + fn.toSource());
|
||||
let that = this;
|
||||
let p = this.createPromise(function(resolve, reject) {
|
||||
this.askPermission(
|
||||
() => {
|
||||
fn(that._scope, that._principal, {
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIPushEndpointCallback]),
|
||||
onPushEndpoint: function(ok, endpoint, keyLen, key) {
|
||||
if (ok === Cr.NS_OK) {
|
||||
if (endpoint) {
|
||||
let sub;
|
||||
if (keyLen) {
|
||||
let publicKey = new ArrayBuffer(keyLen);
|
||||
let keyView = new Uint8Array(publicKey);
|
||||
keyView.set(key);
|
||||
sub = new that._window.PushSubscription(endpoint,
|
||||
that._scope,
|
||||
publicKey);
|
||||
} else {
|
||||
sub = new that._window.PushSubscription(endpoint,
|
||||
that._scope,
|
||||
null);
|
||||
}
|
||||
sub.setPrincipal(that._principal);
|
||||
resolve(sub);
|
||||
} else {
|
||||
resolve(null);
|
||||
}
|
||||
} else {
|
||||
reject("AbortError");
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
() => {
|
||||
reject("PermissionDeniedError");
|
||||
}
|
||||
);
|
||||
}.bind(this));
|
||||
return p;
|
||||
if (permission == Ci.nsIPermissionManager.ALLOW_ACTION) {
|
||||
resolve();
|
||||
} else if (permission == Ci.nsIPermissionManager.DENY_ACTION) {
|
||||
permissionDenied();
|
||||
} else {
|
||||
this._requestPermission(resolve, permissionDenied);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
subscribe: function() {
|
||||
debug("subscribe()");
|
||||
console.debug("subscribe()", this._scope);
|
||||
|
||||
let histogram = Services.telemetry.getHistogramById("PUSH_API_USED");
|
||||
histogram.add(true);
|
||||
return this.getEndpointResponse(this._client.subscribe.bind(this._client));
|
||||
return this.askPermission().then(() =>
|
||||
this.createPromise((resolve, reject) => {
|
||||
let callback = new PushEndpointCallback(this, resolve, reject);
|
||||
this._client.subscribe(this._scope, this._principal, callback);
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
getSubscription: function() {
|
||||
debug("getSubscription()" + this._scope);
|
||||
return this.getEndpointResponse(this._client.getSubscription.bind(this._client));
|
||||
console.debug("getSubscription()", this._scope);
|
||||
|
||||
return this.createPromise((resolve, reject) => {
|
||||
let callback = new PushEndpointCallback(this, resolve, reject);
|
||||
this._client.getSubscription(this._scope, this._principal, callback);
|
||||
});
|
||||
},
|
||||
|
||||
permissionState: function() {
|
||||
debug("permissionState()" + this._scope);
|
||||
console.debug("permissionState()", this._scope);
|
||||
|
||||
let p = this.createPromise((resolve, reject) => {
|
||||
let permission = Ci.nsIPermissionManager.DENY_ACTION;
|
||||
return this.createPromise((resolve, reject) => {
|
||||
let permission = Ci.nsIPermissionManager.UNKNOWN_ACTION;
|
||||
|
||||
try {
|
||||
let permissionManager = Cc["@mozilla.org/permissionmanager;1"]
|
||||
.getService(Ci.nsIPermissionManager);
|
||||
permission =
|
||||
permissionManager.testExactPermissionFromPrincipal(this._principal,
|
||||
"push");
|
||||
permission = this._testPermission();
|
||||
} catch(e) {
|
||||
reject();
|
||||
return;
|
||||
@@ -194,8 +132,82 @@ Push.prototype = {
|
||||
}
|
||||
resolve(pushPermissionStatus);
|
||||
});
|
||||
return p;
|
||||
},
|
||||
|
||||
_testPermission: function() {
|
||||
return Services.perms.testExactPermissionFromPrincipal(
|
||||
this._principal, "desktop-notification");
|
||||
},
|
||||
|
||||
_requestPermission: function(allowCallback, cancelCallback) {
|
||||
// Create an array with a single nsIContentPermissionType element.
|
||||
type = {
|
||||
type: "push",
|
||||
access: null,
|
||||
options: [],
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentPermissionType]),
|
||||
};
|
||||
let typeArray = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
|
||||
typeArray.appendElement(type, false);
|
||||
|
||||
// create a nsIContentPermissionRequest
|
||||
let request = {
|
||||
types: typeArray,
|
||||
principal: principal,
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentPermissionRequest]),
|
||||
allow: function() {
|
||||
allowCallback();
|
||||
},
|
||||
cancel: function() {
|
||||
cancelCallback();
|
||||
},
|
||||
window: this._window,
|
||||
};
|
||||
|
||||
// Using askPermission from nsIDOMWindowUtils that takes care of the
|
||||
// remoting if needed.
|
||||
let windowUtils = this._window.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils);
|
||||
windowUtils.askPermission(request);
|
||||
},
|
||||
};
|
||||
|
||||
function PushEndpointCallback(pushManager, resolve, reject) {
|
||||
this.pushManager = pushManager;
|
||||
this.resolve = resolve;
|
||||
this.reject = reject;
|
||||
}
|
||||
|
||||
PushEndpointCallback.prototype = {
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIPushEndpointCallback]),
|
||||
onPushEndpoint: function(ok, endpoint, keyLen, key) {
|
||||
let {pushManager} = this;
|
||||
if (!Components.isSuccessCode(ok)) {
|
||||
this.reject(new pushManager._window.DOMException(
|
||||
"Error retrieving push subscription",
|
||||
"AbortError"
|
||||
));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!endpoint) {
|
||||
this.resolve(null);
|
||||
return;
|
||||
}
|
||||
|
||||
let publicKey = null;
|
||||
if (keyLen) {
|
||||
publicKey = new ArrayBuffer(keyLen);
|
||||
let keyView = new Uint8Array(publicKey);
|
||||
keyView.set(key);
|
||||
}
|
||||
|
||||
let sub = new pushManager._window.PushSubscription(endpoint,
|
||||
pushManager._scope,
|
||||
publicKey);
|
||||
sub.setPrincipal(pushManager._principal);
|
||||
this.resolve(sub);
|
||||
},
|
||||
};
|
||||
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([Push]);
|
||||
|
||||
+27
-29
@@ -4,14 +4,6 @@
|
||||
|
||||
"use strict";
|
||||
|
||||
// Don't modify this, instead set dom.push.debug.
|
||||
let gDebuggingEnabled = true;
|
||||
|
||||
function debug(s) {
|
||||
if (gDebuggingEnabled)
|
||||
dump("-*- PushClient.js: " + s + "\n");
|
||||
}
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cu = Components.utils;
|
||||
@@ -20,6 +12,14 @@ const Cr = Components.results;
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "console", () => {
|
||||
let {ConsoleAPI} = Cu.import("resource://gre/modules/Console.jsm", {});
|
||||
return new ConsoleAPI({
|
||||
maxLogLevelPref: "dom.push.loglevel",
|
||||
prefix: "PushClient",
|
||||
});
|
||||
});
|
||||
|
||||
const kMessages = [
|
||||
"PushService:Register:OK",
|
||||
"PushService:Register:KO",
|
||||
@@ -30,7 +30,7 @@ const kMessages = [
|
||||
];
|
||||
|
||||
this.PushClient = function PushClient() {
|
||||
debug("PushClient created!");
|
||||
console.debug("PushClient()");
|
||||
this._cpmm = Cc["@mozilla.org/childprocessmessagemanager;1"]
|
||||
.getService(Ci.nsISyncMessageSender);
|
||||
this._requests = {};
|
||||
@@ -71,7 +71,7 @@ PushClient.prototype = {
|
||||
},
|
||||
|
||||
subscribe: function(scope, principal, callback) {
|
||||
debug("subscribe() " + scope);
|
||||
console.debug("subscribe()", scope);
|
||||
let requestId = this.addRequest(callback);
|
||||
this._cpmm.sendAsyncMessage("Push:Register", {
|
||||
scope: scope,
|
||||
@@ -80,7 +80,7 @@ PushClient.prototype = {
|
||||
},
|
||||
|
||||
unsubscribe: function(scope, principal, callback) {
|
||||
debug("unsubscribe() " + scope);
|
||||
console.debug("unsubscribe()", scope);
|
||||
let requestId = this.addRequest(callback);
|
||||
this._cpmm.sendAsyncMessage("Push:Unregister", {
|
||||
scope: scope,
|
||||
@@ -89,9 +89,10 @@ PushClient.prototype = {
|
||||
},
|
||||
|
||||
getSubscription: function(scope, principal, callback) {
|
||||
debug("getSubscription()" + scope);
|
||||
console.debug("getSubscription()", scope);
|
||||
let requestId = this.addRequest(callback);
|
||||
debug("Going to send " + scope + " " + principal + " " + requestId);
|
||||
console.debug("getSubscription: Going to send", scope, principal,
|
||||
requestId);
|
||||
this._cpmm.sendAsyncMessage("Push:Registration", {
|
||||
scope: scope,
|
||||
requestID: requestId,
|
||||
@@ -99,6 +100,10 @@ PushClient.prototype = {
|
||||
},
|
||||
|
||||
_deliverPushEndpoint: function(request, registration) {
|
||||
if (!registration) {
|
||||
request.onPushEndpoint(Cr.NS_OK, "", 0, null);
|
||||
return;
|
||||
}
|
||||
if (registration.p256dhKey) {
|
||||
let key = new Uint8Array(registration.p256dhKey);
|
||||
request.onPushEndpoint(Cr.NS_OK,
|
||||
@@ -112,38 +117,31 @@ PushClient.prototype = {
|
||||
},
|
||||
|
||||
receiveMessage: function(aMessage) {
|
||||
console.debug("receiveMessage()", aMessage);
|
||||
|
||||
let json = aMessage.data;
|
||||
let request = this.takeRequest(json.requestID);
|
||||
|
||||
if (!request) {
|
||||
console.error("receiveMessage: Unknown request ID", json.requestID);
|
||||
return;
|
||||
}
|
||||
|
||||
debug("receiveMessage(): " + JSON.stringify(aMessage))
|
||||
switch (aMessage.name) {
|
||||
case "PushService:Register:OK":
|
||||
this._deliverPushEndpoint(request, json);
|
||||
break;
|
||||
case "PushService:Register:KO":
|
||||
request.onPushEndpoint(Cr.NS_ERROR_FAILURE, "", 0, null);
|
||||
break;
|
||||
case "PushService:Registration:OK":
|
||||
{
|
||||
let endpoint = "";
|
||||
if (!json.registration) {
|
||||
request.onPushEndpoint(Cr.NS_OK, "", 0, null);
|
||||
} else {
|
||||
this._deliverPushEndpoint(request, json.registration);
|
||||
}
|
||||
this._deliverPushEndpoint(request, json.result);
|
||||
break;
|
||||
}
|
||||
|
||||
case "PushService:Register:KO":
|
||||
case "PushService:Registration:KO":
|
||||
request.onPushEndpoint(Cr.NS_ERROR_FAILURE, "", 0, null);
|
||||
break;
|
||||
|
||||
case "PushService:Unregister:OK":
|
||||
if (typeof json.result !== "boolean") {
|
||||
debug("Expected boolean result from PushService!");
|
||||
console.error("receiveMessage: Expected boolean for unregister response",
|
||||
json.result);
|
||||
request.onUnsubscribe(Cr.NS_ERROR_FAILURE, false);
|
||||
return;
|
||||
}
|
||||
@@ -154,7 +152,7 @@ PushClient.prototype = {
|
||||
request.onUnsubscribe(Cr.NS_ERROR_FAILURE, false);
|
||||
break;
|
||||
default:
|
||||
debug("NOT IMPLEMENTED! receiveMessage for " + aMessage.name);
|
||||
console.error("receiveMessage: NOT IMPLEMENTED!", aMessage.name);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
+88
-62
@@ -5,26 +5,24 @@
|
||||
|
||||
"use strict";
|
||||
|
||||
// Don't modify this, instead set dom.push.debug.
|
||||
let gDebuggingEnabled = false;
|
||||
|
||||
function debug(s) {
|
||||
if (gDebuggingEnabled) {
|
||||
dump("-*- PushDB.jsm: " + s + "\n");
|
||||
}
|
||||
}
|
||||
|
||||
const Cu = Components.utils;
|
||||
Cu.import("resource://gre/modules/IndexedDBHelper.jsm");
|
||||
Cu.import("resource://gre/modules/Preferences.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.importGlobalProperties(["indexedDB"]);
|
||||
|
||||
const prefs = new Preferences("dom.push.");
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["PushDB"];
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "console", () => {
|
||||
let {ConsoleAPI} = Cu.import("resource://gre/modules/Console.jsm", {});
|
||||
return new ConsoleAPI({
|
||||
maxLogLevelPref: "dom.push.loglevel",
|
||||
prefix: "PushDB",
|
||||
});
|
||||
});
|
||||
|
||||
this.PushDB = function PushDB(dbName, dbVersion, dbStoreName, keyPath, model) {
|
||||
debug("PushDB()");
|
||||
console.debug("PushDB()");
|
||||
this._dbStoreName = dbStoreName;
|
||||
this._keyPath = keyPath;
|
||||
this._model = model;
|
||||
@@ -32,8 +30,6 @@ this.PushDB = function PushDB(dbName, dbVersion, dbStoreName, keyPath, model) {
|
||||
// set the indexeddb database
|
||||
this.initDBHelper(dbName, dbVersion,
|
||||
[dbStoreName]);
|
||||
gDebuggingEnabled = prefs.get("debug");
|
||||
prefs.observe("debug", this);
|
||||
};
|
||||
|
||||
this.PushDB.prototype = {
|
||||
@@ -90,7 +86,7 @@ this.PushDB.prototype = {
|
||||
*/
|
||||
|
||||
put: function(aRecord) {
|
||||
debug("put()" + JSON.stringify(aRecord));
|
||||
console.debug("put()", aRecord);
|
||||
if (!this.isValidRecord(aRecord)) {
|
||||
return Promise.reject(new TypeError(
|
||||
"Scope, originAttributes, and quota are required! " +
|
||||
@@ -107,8 +103,8 @@ this.PushDB.prototype = {
|
||||
aTxn.result = undefined;
|
||||
|
||||
aStore.put(aRecord).onsuccess = aEvent => {
|
||||
debug("Request successful. Updated record ID: " +
|
||||
aEvent.target.result);
|
||||
console.debug("put: Request successful. Updated record",
|
||||
aEvent.target.result);
|
||||
aTxn.result = this.toPushRecord(aRecord);
|
||||
};
|
||||
},
|
||||
@@ -123,30 +119,18 @@ this.PushDB.prototype = {
|
||||
* The ID of record to be deleted.
|
||||
*/
|
||||
delete: function(aKeyID) {
|
||||
debug("delete()");
|
||||
console.debug("delete()");
|
||||
|
||||
return new Promise((resolve, reject) =>
|
||||
this.newTxn(
|
||||
"readwrite",
|
||||
this._dbStoreName,
|
||||
function txnCb(aTxn, aStore) {
|
||||
debug("Going to delete " + aKeyID);
|
||||
aStore.delete(aKeyID);
|
||||
},
|
||||
resolve,
|
||||
reject
|
||||
)
|
||||
);
|
||||
},
|
||||
|
||||
clearAll: function clear() {
|
||||
return new Promise((resolve, reject) =>
|
||||
this.newTxn(
|
||||
"readwrite",
|
||||
this._dbStoreName,
|
||||
function (aTxn, aStore) {
|
||||
debug("Going to clear all!");
|
||||
aStore.clear();
|
||||
console.debug("delete: Removing record", aKeyID);
|
||||
aStore.get(aKeyID).onsuccess = event => {
|
||||
aTxn.result = event.target.result;
|
||||
aStore.delete(aKeyID);
|
||||
};
|
||||
},
|
||||
resolve,
|
||||
reject
|
||||
@@ -157,7 +141,7 @@ this.PushDB.prototype = {
|
||||
// testFn(record) is called with a database record and should return true if
|
||||
// that record should be deleted.
|
||||
clearIf: function(testFn) {
|
||||
debug("clearIf()");
|
||||
console.debug("clearIf()");
|
||||
return new Promise((resolve, reject) =>
|
||||
this.newTxn(
|
||||
"readwrite",
|
||||
@@ -168,10 +152,12 @@ this.PushDB.prototype = {
|
||||
aStore.openCursor().onsuccess = event => {
|
||||
let cursor = event.target.result;
|
||||
if (cursor) {
|
||||
if (testFn(this.toPushRecord(cursor.value))) {
|
||||
let record = this.toPushRecord(cursor.value);
|
||||
if (testFn(record)) {
|
||||
let deleteRequest = cursor.delete();
|
||||
deleteRequest.onerror = e => {
|
||||
debug("Failed to delete entry even when test succeeded!");
|
||||
console.error("clearIf: Error removing record",
|
||||
record.keyID, e);
|
||||
}
|
||||
}
|
||||
cursor.continue();
|
||||
@@ -185,7 +171,7 @@ this.PushDB.prototype = {
|
||||
},
|
||||
|
||||
getByPushEndpoint: function(aPushEndpoint) {
|
||||
debug("getByPushEndpoint()");
|
||||
console.debug("getByPushEndpoint()");
|
||||
|
||||
return new Promise((resolve, reject) =>
|
||||
this.newTxn(
|
||||
@@ -196,8 +182,9 @@ this.PushDB.prototype = {
|
||||
|
||||
let index = aStore.index("pushEndpoint");
|
||||
index.get(aPushEndpoint).onsuccess = aEvent => {
|
||||
aTxn.result = this.toPushRecord(aEvent.target.result);
|
||||
debug("Fetch successful " + aEvent.target.result);
|
||||
let record = this.toPushRecord(aEvent.target.result);
|
||||
console.debug("getByPushEndpoint: Got record", record);
|
||||
aTxn.result = record;
|
||||
};
|
||||
},
|
||||
resolve,
|
||||
@@ -207,7 +194,7 @@ this.PushDB.prototype = {
|
||||
},
|
||||
|
||||
getByKeyID: function(aKeyID) {
|
||||
debug("getByKeyID()");
|
||||
console.debug("getByKeyID()");
|
||||
|
||||
return new Promise((resolve, reject) =>
|
||||
this.newTxn(
|
||||
@@ -217,8 +204,52 @@ this.PushDB.prototype = {
|
||||
aTxn.result = undefined;
|
||||
|
||||
aStore.get(aKeyID).onsuccess = aEvent => {
|
||||
aTxn.result = this.toPushRecord(aEvent.target.result);
|
||||
debug("Fetch successful " + aEvent.target.result);
|
||||
let record = this.toPushRecord(aEvent.target.result);
|
||||
console.debug("getByKeyID: Got record", record);
|
||||
aTxn.result = record;
|
||||
};
|
||||
},
|
||||
resolve,
|
||||
reject
|
||||
)
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Reduces all records associated with an origin to a single value.
|
||||
*
|
||||
* @param {String} origin The origin, matched as a prefix against the scope.
|
||||
* @param {String} originAttributes Additional origin attributes. Requires
|
||||
* an exact match.
|
||||
* @param {Function} callback A function with the signature `(result,
|
||||
* record, cursor)`, where `result` is the value returned by the previous
|
||||
* invocation, `record` is the registration, and `cursor` is an `IDBCursor`.
|
||||
* @param {Object} [initialValue] The value to use for the first invocation.
|
||||
* @returns {Promise} Resolves with the value of the last invocation.
|
||||
*/
|
||||
reduceByOrigin: function(origin, originAttributes, callback, initialValue) {
|
||||
console.debug("forEachOrigin()");
|
||||
|
||||
return new Promise((resolve, reject) =>
|
||||
this.newTxn(
|
||||
"readwrite",
|
||||
this._dbStoreName,
|
||||
(aTxn, aStore) => {
|
||||
aTxn.result = initialValue;
|
||||
|
||||
let index = aStore.index("identifiers");
|
||||
let range = IDBKeyRange.bound(
|
||||
[origin, originAttributes],
|
||||
[origin + "\x7f", originAttributes]
|
||||
);
|
||||
index.openCursor(range).onsuccess = event => {
|
||||
let cursor = event.target.result;
|
||||
if (!cursor) {
|
||||
return;
|
||||
}
|
||||
let record = this.toPushRecord(cursor.value);
|
||||
aTxn.result = callback(aTxn.result, record, cursor);
|
||||
cursor.continue();
|
||||
};
|
||||
},
|
||||
resolve,
|
||||
@@ -229,12 +260,11 @@ this.PushDB.prototype = {
|
||||
|
||||
// Perform a unique match against { scope, originAttributes }
|
||||
getByIdentifiers: function(aPageRecord) {
|
||||
debug("getByIdentifiers() { " + aPageRecord.scope + ", " +
|
||||
JSON.stringify(aPageRecord.originAttributes) + " }");
|
||||
console.debug("getByIdentifiers()", aPageRecord);
|
||||
if (!aPageRecord.scope || aPageRecord.originAttributes == undefined) {
|
||||
return Promise.reject(
|
||||
new TypeError("Scope and originAttributes are required! " +
|
||||
JSON.stringify(aPageRecord)));
|
||||
console.error("getByIdentifiers: Scope and originAttributes are required",
|
||||
aPageRecord);
|
||||
return Promise.reject(new TypeError("Invalid page record"));
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) =>
|
||||
@@ -289,7 +319,7 @@ this.PushDB.prototype = {
|
||||
},
|
||||
|
||||
getAllKeyIDs: function() {
|
||||
debug("getAllKeyIDs()");
|
||||
console.debug("getAllKeyIDs()");
|
||||
|
||||
return new Promise((resolve, reject) =>
|
||||
this.newTxn(
|
||||
@@ -309,7 +339,7 @@ this.PushDB.prototype = {
|
||||
},
|
||||
|
||||
_getAllByPushQuota: function(range) {
|
||||
debug("getAllByPushQuota()");
|
||||
console.debug("getAllByPushQuota()");
|
||||
|
||||
return new Promise((resolve, reject) =>
|
||||
this.newTxn(
|
||||
@@ -334,12 +364,12 @@ this.PushDB.prototype = {
|
||||
},
|
||||
|
||||
getAllUnexpired: function() {
|
||||
debug("getAllUnexpired()");
|
||||
console.debug("getAllUnexpired()");
|
||||
return this._getAllByPushQuota(IDBKeyRange.lowerBound(1));
|
||||
},
|
||||
|
||||
getAllExpired: function() {
|
||||
debug("getAllExpired()");
|
||||
console.debug("getAllExpired()");
|
||||
return this._getAllByPushQuota(IDBKeyRange.only(0));
|
||||
},
|
||||
|
||||
@@ -365,16 +395,17 @@ this.PushDB.prototype = {
|
||||
|
||||
let record = aEvent.target.result;
|
||||
if (!record) {
|
||||
debug("update: Key ID " + aKeyID + " does not exist");
|
||||
console.error("update: Record does not exist", aKeyID);
|
||||
return;
|
||||
}
|
||||
let newRecord = aUpdateFunc(this.toPushRecord(record));
|
||||
if (!this.isValidRecord(newRecord)) {
|
||||
debug("update: Ignoring invalid update for key ID " + aKeyID);
|
||||
console.error("update: Ignoring invalid update",
|
||||
aKeyID, newRecord);
|
||||
return;
|
||||
}
|
||||
aStore.put(newRecord).onsuccess = aEvent => {
|
||||
debug("update: Update successful for key ID " + aKeyID);
|
||||
console.debug("update: Update successful", aKeyID, newRecord);
|
||||
aTxn.result = newRecord;
|
||||
};
|
||||
};
|
||||
@@ -386,7 +417,7 @@ this.PushDB.prototype = {
|
||||
},
|
||||
|
||||
drop: function() {
|
||||
debug("drop()");
|
||||
console.debug("drop()");
|
||||
|
||||
return new Promise((resolve, reject) =>
|
||||
this.newTxn(
|
||||
@@ -400,9 +431,4 @@ this.PushDB.prototype = {
|
||||
)
|
||||
);
|
||||
},
|
||||
|
||||
observe: function observe(aSubject, aTopic, aData) {
|
||||
if ((aTopic == "nsPref:changed") && (aData == "dom.push.debug"))
|
||||
gDebuggingEnabled = prefs.get("debug");
|
||||
}
|
||||
};
|
||||
|
||||
+53
-45
@@ -33,6 +33,39 @@ namespace dom {
|
||||
|
||||
using namespace workers;
|
||||
|
||||
namespace {
|
||||
|
||||
nsresult
|
||||
GetPermissionState(nsIPrincipal* aPrincipal,
|
||||
PushPermissionState& aState)
|
||||
{
|
||||
nsCOMPtr<nsIPermissionManager> permManager =
|
||||
mozilla::services::GetPermissionManager();
|
||||
|
||||
if (!permManager) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
uint32_t permission = nsIPermissionManager::UNKNOWN_ACTION;
|
||||
nsresult rv = permManager->TestExactPermissionFromPrincipal(
|
||||
aPrincipal,
|
||||
"desktop-notification",
|
||||
&permission);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (permission == nsIPermissionManager::ALLOW_ACTION) {
|
||||
aState = PushPermissionState::Granted;
|
||||
} else if (permission == nsIPermissionManager::DENY_ACTION) {
|
||||
aState = PushPermissionState::Denied;
|
||||
} else {
|
||||
aState = PushPermissionState::Prompt;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
class UnsubscribeResultCallback final : public nsIUnsubscribeResultCallback
|
||||
{
|
||||
public:
|
||||
@@ -393,12 +426,13 @@ public:
|
||||
do_CreateInstance("@mozilla.org/push/PushClient;1");
|
||||
if (!client) {
|
||||
callback->OnUnsubscribe(NS_ERROR_FAILURE, false);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIPrincipal> principal = mProxy->GetWorkerPrivate()->GetPrincipal();
|
||||
if (NS_WARN_IF(NS_FAILED(client->Unsubscribe(mScope, principal, callback)))) {
|
||||
callback->OnUnsubscribe(NS_ERROR_FAILURE, false);
|
||||
return NS_ERROR_FAILURE;
|
||||
return NS_OK;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
@@ -488,7 +522,7 @@ public:
|
||||
promise->MaybeResolve(sub);
|
||||
}
|
||||
} else {
|
||||
promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
|
||||
promise->MaybeReject(NS_ERROR_DOM_PUSH_ABORT_ERR);
|
||||
}
|
||||
|
||||
mProxy->CleanUp(aCx);
|
||||
@@ -580,22 +614,20 @@ public:
|
||||
|
||||
RefPtr<GetSubscriptionCallback> callback = new GetSubscriptionCallback(mProxy, mScope);
|
||||
|
||||
nsCOMPtr<nsIPermissionManager> permManager =
|
||||
mozilla::services::GetPermissionManager();
|
||||
if (!permManager) {
|
||||
nsCOMPtr<nsIPrincipal> principal = mProxy->GetWorkerPrivate()->GetPrincipal();
|
||||
|
||||
PushPermissionState state;
|
||||
nsresult rv = GetPermissionState(principal, state);
|
||||
if (NS_FAILED(rv)) {
|
||||
callback->OnPushEndpoint(NS_ERROR_FAILURE, EmptyString(), 0, nullptr);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIPrincipal> principal = mProxy->GetWorkerPrivate()->GetPrincipal();
|
||||
|
||||
uint32_t permission = nsIPermissionManager::DENY_ACTION;
|
||||
nsresult rv = permManager->TestExactPermissionFromPrincipal(
|
||||
principal,
|
||||
"push",
|
||||
&permission);
|
||||
|
||||
if (NS_WARN_IF(NS_FAILED(rv)) || permission != nsIPermissionManager::ALLOW_ACTION) {
|
||||
if (state != PushPermissionState::Granted) {
|
||||
if (mAction == WorkerPushManager::GetSubscriptionAction) {
|
||||
callback->OnPushEndpoint(NS_OK, EmptyString(), 0, nullptr);
|
||||
return NS_OK;
|
||||
}
|
||||
callback->OnPushEndpoint(NS_ERROR_FAILURE, EmptyString(), 0, nullptr);
|
||||
return NS_OK;
|
||||
}
|
||||
@@ -616,7 +648,7 @@ public:
|
||||
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
callback->OnPushEndpoint(NS_ERROR_FAILURE, EmptyString(), 0, nullptr);
|
||||
return rv;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
@@ -646,7 +678,7 @@ WorkerPushManager::PerformSubscriptionAction(SubscriptionAction aAction, ErrorRe
|
||||
|
||||
RefPtr<PromiseWorkerProxy> proxy = PromiseWorkerProxy::Create(worker, p);
|
||||
if (!proxy) {
|
||||
p->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
|
||||
p->MaybeReject(NS_ERROR_DOM_PUSH_ABORT_ERR);
|
||||
return p.forget();
|
||||
}
|
||||
|
||||
@@ -728,35 +760,11 @@ public:
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIPermissionManager> permManager =
|
||||
mozilla::services::GetPermissionManager();
|
||||
|
||||
nsresult rv = NS_ERROR_FAILURE;
|
||||
PushPermissionState state = PushPermissionState::Denied;
|
||||
|
||||
if (permManager) {
|
||||
uint32_t permission = nsIPermissionManager::DENY_ACTION;
|
||||
rv = permManager->TestExactPermissionFromPrincipal(
|
||||
mProxy->GetWorkerPrivate()->GetPrincipal(),
|
||||
"push",
|
||||
&permission);
|
||||
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
switch (permission) {
|
||||
case nsIPermissionManager::ALLOW_ACTION:
|
||||
state = PushPermissionState::Granted;
|
||||
break;
|
||||
case nsIPermissionManager::DENY_ACTION:
|
||||
state = PushPermissionState::Denied;
|
||||
break;
|
||||
case nsIPermissionManager::PROMPT_ACTION:
|
||||
state = PushPermissionState::Prompt;
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("Unexpected case!");
|
||||
}
|
||||
}
|
||||
}
|
||||
PushPermissionState state;
|
||||
nsresult rv = GetPermissionState(
|
||||
mProxy->GetWorkerPrivate()->GetPrincipal(),
|
||||
state
|
||||
);
|
||||
|
||||
AutoJSAPI jsapi;
|
||||
jsapi.Init();
|
||||
|
||||
@@ -36,10 +36,11 @@ PushNotificationService.prototype = {
|
||||
_xpcom_factory: XPCOMUtils.generateSingletonFactory(PushNotificationService),
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
|
||||
Ci.nsISupportsWeakReference,
|
||||
Ci.nsIPushNotificationService]),
|
||||
Ci.nsIPushNotificationService,
|
||||
Ci.nsIPushQuotaManager,]),
|
||||
|
||||
register: function register(scope, originAttributes) {
|
||||
return PushService._register({
|
||||
return PushService.register({
|
||||
scope: scope,
|
||||
originAttributes: originAttributes,
|
||||
maxQuota: Infinity,
|
||||
@@ -47,11 +48,11 @@ PushNotificationService.prototype = {
|
||||
},
|
||||
|
||||
unregister: function unregister(scope, originAttributes) {
|
||||
return PushService._unregister({scope, originAttributes});
|
||||
return PushService.unregister({scope, originAttributes});
|
||||
},
|
||||
|
||||
registration: function registration(scope, originAttributes) {
|
||||
return PushService._registration({scope, originAttributes});
|
||||
return PushService.registration({scope, originAttributes});
|
||||
},
|
||||
|
||||
clearAll: function clearAll() {
|
||||
@@ -74,6 +75,26 @@ PushNotificationService.prototype = {
|
||||
}
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
// nsIPushQuotaManager methods
|
||||
|
||||
notificationForOriginShown: function(origin) {
|
||||
if (!isParent) {
|
||||
Services.cpmm.sendAsyncMessage("Push:NotificationForOriginShown", origin);
|
||||
return;
|
||||
}
|
||||
|
||||
PushService._notificationForOriginShown(origin);
|
||||
},
|
||||
|
||||
notificationForOriginClosed: function(origin) {
|
||||
if (!isParent) {
|
||||
Services.cpmm.sendAsyncMessage("Push:NotificationForOriginClosed", origin);
|
||||
return;
|
||||
}
|
||||
|
||||
PushService._notificationForOriginClosed(origin);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+64
-35
@@ -9,10 +9,14 @@ const Ci = Components.interfaces;
|
||||
const Cu = Components.utils;
|
||||
const Cr = Components.results;
|
||||
|
||||
Cu.import("resource://gre/modules/AppConstants.jsm");
|
||||
Cu.import("resource://gre/modules/Preferences.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Messaging",
|
||||
"resource://gre/modules/Messaging.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
|
||||
"resource://gre/modules/PlacesUtils.jsm");
|
||||
|
||||
@@ -26,18 +30,6 @@ this.EXPORTED_SYMBOLS = ["PushRecord"];
|
||||
|
||||
const prefs = new Preferences("dom.push.");
|
||||
|
||||
// History transition types that can fire a `pushsubscriptionchange` event
|
||||
// when the user visits a site with expired push registrations. Visits only
|
||||
// count if the user sees the origin in the address bar. This excludes embedded
|
||||
// resources, downloads, and framed links.
|
||||
const QUOTA_REFRESH_TRANSITIONS_SQL = [
|
||||
Ci.nsINavHistoryService.TRANSITION_LINK,
|
||||
Ci.nsINavHistoryService.TRANSITION_TYPED,
|
||||
Ci.nsINavHistoryService.TRANSITION_BOOKMARK,
|
||||
Ci.nsINavHistoryService.TRANSITION_REDIRECT_PERMANENT,
|
||||
Ci.nsINavHistoryService.TRANSITION_REDIRECT_TEMPORARY
|
||||
].join(",");
|
||||
|
||||
function PushRecord(props) {
|
||||
this.pushEndpoint = props.pushEndpoint;
|
||||
this.scope = props.scope;
|
||||
@@ -52,8 +44,15 @@ function PushRecord(props) {
|
||||
|
||||
PushRecord.prototype = {
|
||||
setQuota(suggestedQuota) {
|
||||
this.quota = (!isNaN(suggestedQuota) && suggestedQuota >= 0) ?
|
||||
suggestedQuota : prefs.get("maxQuotaPerSubscription");
|
||||
if (!isNaN(suggestedQuota) && suggestedQuota >= 0) {
|
||||
this.quota = suggestedQuota;
|
||||
} else {
|
||||
this.resetQuota();
|
||||
}
|
||||
},
|
||||
|
||||
resetQuota() {
|
||||
this.quota = prefs.get("maxQuotaPerSubscription");
|
||||
},
|
||||
|
||||
updateQuota(lastVisit) {
|
||||
@@ -68,25 +67,15 @@ PushRecord.prototype = {
|
||||
this.quota = 0;
|
||||
return;
|
||||
}
|
||||
let currentQuota;
|
||||
if (lastVisit > this.lastPush) {
|
||||
// If the user visited the site since the last time we received a
|
||||
// notification, reset the quota.
|
||||
let daysElapsed = (Date.now() - lastVisit) / 24 / 60 / 60 / 1000;
|
||||
currentQuota = Math.min(
|
||||
this.quota = Math.min(
|
||||
Math.round(8 * Math.pow(daysElapsed, -0.8)),
|
||||
prefs.get("maxQuotaPerSubscription")
|
||||
);
|
||||
Services.telemetry.getHistogramById("PUSH_API_QUOTA_RESET_TO").add(currentQuota - 1);
|
||||
} else {
|
||||
// The user hasn't visited the site since the last notification.
|
||||
currentQuota = this.quota;
|
||||
}
|
||||
this.quota = Math.max(currentQuota - 1, 0);
|
||||
// We check for ctime > 0 to skip older records that did not have ctime.
|
||||
if (this.isExpired() && this.ctime > 0) {
|
||||
let duration = Date.now() - this.ctime;
|
||||
Services.telemetry.getHistogramById("PUSH_API_QUOTA_EXPIRATION_TIME").add(duration / 1000);
|
||||
Services.telemetry.getHistogramById("PUSH_API_QUOTA_RESET_TO").add(this.quota);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -96,6 +85,15 @@ PushRecord.prototype = {
|
||||
this.lastPush = Date.now();
|
||||
},
|
||||
|
||||
reduceQuota() {
|
||||
this.quota = Math.max(this.quota - 1, 0);
|
||||
// We check for ctime > 0 to skip older records that did not have ctime.
|
||||
if (this.isExpired() && this.ctime > 0) {
|
||||
let duration = Date.now() - this.ctime;
|
||||
Services.telemetry.getHistogramById("PUSH_API_QUOTA_EXPIRATION_TIME").add(duration / 1000);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Queries the Places database for the last time a user visited the site
|
||||
* associated with a push registration.
|
||||
@@ -107,9 +105,34 @@ PushRecord.prototype = {
|
||||
getLastVisit() {
|
||||
if (!this.quotaApplies() || this.isTabOpen()) {
|
||||
// If the registration isn't subject to quota, or the user already
|
||||
// has the site open, skip the Places query.
|
||||
// has the site open, skip expensive database queries.
|
||||
return Promise.resolve(Date.now());
|
||||
}
|
||||
|
||||
if (AppConstants.MOZ_ANDROID_HISTORY) {
|
||||
return Messaging.sendRequestForResult({
|
||||
type: "History:GetPrePathLastVisitedTimeMilliseconds",
|
||||
prePath: this.uri.prePath,
|
||||
}).then(result => {
|
||||
if (result == 0) {
|
||||
return -Infinity;
|
||||
}
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
// Places History transition types that can fire a
|
||||
// `pushsubscriptionchange` event when the user visits a site with expired push
|
||||
// registrations. Visits only count if the user sees the origin in the address
|
||||
// bar. This excludes embedded resources, downloads, and framed links.
|
||||
const QUOTA_REFRESH_TRANSITIONS_SQL = [
|
||||
Ci.nsINavHistoryService.TRANSITION_LINK,
|
||||
Ci.nsINavHistoryService.TRANSITION_TYPED,
|
||||
Ci.nsINavHistoryService.TRANSITION_BOOKMARK,
|
||||
Ci.nsINavHistoryService.TRANSITION_REDIRECT_PERMANENT,
|
||||
Ci.nsINavHistoryService.TRANSITION_REDIRECT_TEMPORARY
|
||||
].join(",");
|
||||
|
||||
return PlacesUtils.withConnectionWrapper("PushRecord.getLastVisit", db => {
|
||||
// We're using a custom query instead of `nsINavHistoryQueryOptions`
|
||||
// because the latter doesn't expose a way to filter by transition type:
|
||||
@@ -180,6 +203,14 @@ PushRecord.prototype = {
|
||||
return permission == Ci.nsIPermissionManager.ALLOW_ACTION;
|
||||
},
|
||||
|
||||
quotaChanged() {
|
||||
if (!this.hasPermission()) {
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
return this.getLastVisit()
|
||||
.then(lastVisit => lastVisit > this.lastPush);
|
||||
},
|
||||
|
||||
quotaApplies() {
|
||||
return Number.isFinite(this.quota);
|
||||
},
|
||||
@@ -188,7 +219,12 @@ PushRecord.prototype = {
|
||||
return this.quota === 0;
|
||||
},
|
||||
|
||||
toRegistration() {
|
||||
matchesOriginAttributes(pattern) {
|
||||
return ChromeUtils.originAttributesMatchPattern(
|
||||
this.principal.originAttributes, pattern);
|
||||
},
|
||||
|
||||
toSubscription() {
|
||||
return {
|
||||
pushEndpoint: this.pushEndpoint,
|
||||
lastPush: this.lastPush,
|
||||
@@ -196,13 +232,6 @@ PushRecord.prototype = {
|
||||
p256dhKey: this.p256dhPublicKey,
|
||||
};
|
||||
},
|
||||
|
||||
toRegister() {
|
||||
return {
|
||||
pushEndpoint: this.pushEndpoint,
|
||||
p256dhKey: this.p256dhPublicKey,
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
// Define lazy getters for the principal and scope URI. IndexedDB can't store
|
||||
|
||||
+506
-322
File diff suppressed because it is too large
Load Diff
+92
-128
@@ -28,18 +28,16 @@ const {
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["PushServiceHttp2"];
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "console", () => {
|
||||
let {ConsoleAPI} = Cu.import("resource://gre/modules/Console.jsm", {});
|
||||
return new ConsoleAPI({
|
||||
maxLogLevelPref: "dom.push.loglevel",
|
||||
prefix: "PushServiceHttp2",
|
||||
});
|
||||
});
|
||||
|
||||
const prefs = new Preferences("dom.push.");
|
||||
|
||||
// Don't modify this, instead set dom.push.debug.
|
||||
// Set debug first so that all debugging actually works.
|
||||
var gDebuggingEnabled = prefs.get("debug");
|
||||
|
||||
function debug(s) {
|
||||
if (gDebuggingEnabled) {
|
||||
dump("-*- PushServiceHttp2.jsm: " + s + "\n");
|
||||
}
|
||||
}
|
||||
|
||||
const kPUSHHTTP2DB_DB_NAME = "pushHttp2";
|
||||
const kPUSHHTTP2DB_DB_VERSION = 5; // Change this if the IndexedDB format changes
|
||||
const kPUSHHTTP2DB_STORE_NAME = "pushHttp2";
|
||||
@@ -53,7 +51,7 @@ const kPUSHHTTP2DB_STORE_NAME = "pushHttp2";
|
||||
* It's easier to stop listening than to have checks at specific points.
|
||||
*/
|
||||
var PushSubscriptionListener = function(pushService, uri) {
|
||||
debug("Creating a new pushSubscription listener.");
|
||||
console.debug("PushSubscriptionListener()");
|
||||
this._pushService = pushService;
|
||||
this.uri = uri;
|
||||
};
|
||||
@@ -73,12 +71,12 @@ PushSubscriptionListener.prototype = {
|
||||
},
|
||||
|
||||
onStartRequest: function(aRequest, aContext) {
|
||||
debug("PushSubscriptionListener onStartRequest()");
|
||||
console.debug("PushSubscriptionListener: onStartRequest()");
|
||||
// We do not do anything here.
|
||||
},
|
||||
|
||||
onDataAvailable: function(aRequest, aContext, aStream, aOffset, aCount) {
|
||||
debug("PushSubscriptionListener onDataAvailable()");
|
||||
console.debug("PushSubscriptionListener: onDataAvailable()");
|
||||
// Nobody should send data, but just to be sure, otherwise necko will
|
||||
// complain.
|
||||
if (aCount === 0) {
|
||||
@@ -93,7 +91,7 @@ PushSubscriptionListener.prototype = {
|
||||
},
|
||||
|
||||
onStopRequest: function(aRequest, aContext, aStatusCode) {
|
||||
debug("PushSubscriptionListener onStopRequest()");
|
||||
console.debug("PushSubscriptionListener: onStopRequest()");
|
||||
if (!this._pushService) {
|
||||
return;
|
||||
}
|
||||
@@ -104,7 +102,7 @@ PushSubscriptionListener.prototype = {
|
||||
},
|
||||
|
||||
onPush: function(associatedChannel, pushChannel) {
|
||||
debug("PushSubscriptionListener onPush()");
|
||||
console.debug("PushSubscriptionListener: onPush()");
|
||||
var pushChannelListener = new PushChannelListener(this);
|
||||
pushChannel.asyncOpen(pushChannelListener, pushChannel);
|
||||
},
|
||||
@@ -119,7 +117,7 @@ PushSubscriptionListener.prototype = {
|
||||
* OnDataAvailable and send to the app in OnStopRequest.
|
||||
*/
|
||||
var PushChannelListener = function(pushSubscriptionListener) {
|
||||
debug("Creating a new push channel listener.");
|
||||
console.debug("PushChannelListener()");
|
||||
this._mainListener = pushSubscriptionListener;
|
||||
this._message = [];
|
||||
this._ackUri = null;
|
||||
@@ -132,7 +130,7 @@ PushChannelListener.prototype = {
|
||||
},
|
||||
|
||||
onDataAvailable: function(aRequest, aContext, aStream, aOffset, aCount) {
|
||||
debug("push channel listener onDataAvailable()");
|
||||
console.debug("PushChannelListener: onDataAvailable()");
|
||||
|
||||
if (aCount === 0) {
|
||||
return;
|
||||
@@ -148,7 +146,8 @@ PushChannelListener.prototype = {
|
||||
},
|
||||
|
||||
onStopRequest: function(aRequest, aContext, aStatusCode) {
|
||||
debug("push channel listener onStopRequest() status code:" + aStatusCode);
|
||||
console.debug("PushChannelListener: onStopRequest()", "status code",
|
||||
aStatusCode);
|
||||
if (Components.isSuccessCode(aStatusCode) &&
|
||||
this._mainListener &&
|
||||
this._mainListener._pushService) {
|
||||
@@ -228,14 +227,14 @@ PushServiceDelete.prototype = {
|
||||
if (Components.isSuccessCode(aStatusCode)) {
|
||||
this._resolve();
|
||||
} else {
|
||||
this._reject({status: 0, error: "NetworkError"});
|
||||
this._reject(new Error("Error removing subscription: " + aStatusCode));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var SubscriptionListener = function(aSubInfo, aResolve, aReject,
|
||||
aServerURI, aPushServiceHttp2) {
|
||||
debug("Creating a new subscription listener.");
|
||||
console.debug("SubscriptionListener()");
|
||||
this._subInfo = aSubInfo;
|
||||
this._resolve = aResolve;
|
||||
this._reject = aReject;
|
||||
@@ -250,7 +249,7 @@ SubscriptionListener.prototype = {
|
||||
onStartRequest: function(aRequest, aContext) {},
|
||||
|
||||
onDataAvailable: function(aRequest, aContext, aStream, aOffset, aCount) {
|
||||
debug("subscription listener onDataAvailable()");
|
||||
console.debug("SubscriptionListener: onDataAvailable()");
|
||||
|
||||
// We do not expect any data, but necko will complain if we do not consume
|
||||
// it.
|
||||
@@ -266,16 +265,16 @@ SubscriptionListener.prototype = {
|
||||
},
|
||||
|
||||
onStopRequest: function(aRequest, aContext, aStatus) {
|
||||
debug("subscription listener onStopRequest()");
|
||||
console.debug("SubscriptionListener: onStopRequest()");
|
||||
|
||||
// Check if pushService is still active.
|
||||
if (!this._service.hasmainPushService()) {
|
||||
this._reject({error: "Service deactivated"});
|
||||
this._reject(new Error("Push service unavailable"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Components.isSuccessCode(aStatus)) {
|
||||
this._reject({error: "Error status" + aStatus});
|
||||
this._reject(new Error("Error listening for messages: " + aStatus));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -292,11 +291,11 @@ SubscriptionListener.prototype = {
|
||||
}),
|
||||
retryAfter);
|
||||
} else {
|
||||
this._reject({error: "Error response code: " + statusCode });
|
||||
this._reject(new Error("Unexpected server response: " + statusCode));
|
||||
}
|
||||
return;
|
||||
} else if (statusCode != 201) {
|
||||
this._reject({error: "Error response code: " + statusCode });
|
||||
this._reject(new Error("Unexpected server response: " + statusCode));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -304,37 +303,39 @@ SubscriptionListener.prototype = {
|
||||
try {
|
||||
subscriptionUri = aRequest.getResponseHeader("location");
|
||||
} catch (err) {
|
||||
this._reject({error: "Return code 201, but the answer is bogus"});
|
||||
this._reject(new Error("Missing Location header"));
|
||||
return;
|
||||
}
|
||||
|
||||
debug("subscriptionUri: " + subscriptionUri);
|
||||
console.debug("onStopRequest: subscriptionUri", subscriptionUri);
|
||||
|
||||
var linkList;
|
||||
try {
|
||||
linkList = aRequest.getResponseHeader("link");
|
||||
} catch (err) {
|
||||
this._reject({error: "Return code 201, but the answer is bogus"});
|
||||
this._reject(new Error("Missing Link header"));
|
||||
return;
|
||||
}
|
||||
|
||||
var linkParserResult = linkParser(linkList, this._serverURI);
|
||||
if (linkParserResult.error) {
|
||||
this._reject(linkParserResult);
|
||||
var linkParserResult;
|
||||
try {
|
||||
linkParserResult = linkParser(linkList, this._serverURI);
|
||||
} catch (e) {
|
||||
this._reject(e);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!subscriptionUri) {
|
||||
this._reject({error: "Return code 201, but the answer is bogus," +
|
||||
" missing subscriptionUri"});
|
||||
this._reject(new Error("Invalid Location header"));
|
||||
return;
|
||||
}
|
||||
try {
|
||||
let uriTry = Services.io.newURI(subscriptionUri, null, null);
|
||||
} catch (e) {
|
||||
debug("Invalid URI " + subscriptionUri);
|
||||
this._reject({error: "Return code 201, but URI is bogus. " +
|
||||
subscriptionUri});
|
||||
console.error("onStopRequest: Invalid subscription URI",
|
||||
subscriptionUri);
|
||||
this._reject(new Error("Invalid subscription endpoint: " +
|
||||
subscriptionUri));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -372,7 +373,7 @@ function linkParser(linkHeader, serverURI) {
|
||||
|
||||
var linkList = linkHeader.split(',');
|
||||
if ((linkList.length < 1)) {
|
||||
return {error: "Return code 201, but the answer is bogus"};
|
||||
throw new Error("Invalid Link header");
|
||||
}
|
||||
|
||||
var pushEndpoint;
|
||||
@@ -393,32 +394,23 @@ function linkParser(linkHeader, serverURI) {
|
||||
}
|
||||
});
|
||||
|
||||
debug("pushEndpoint: " + pushEndpoint);
|
||||
debug("pushReceiptEndpoint: " + pushReceiptEndpoint);
|
||||
console.debug("linkParser: pushEndpoint", pushEndpoint);
|
||||
console.debug("linkParser: pushReceiptEndpoint", pushReceiptEndpoint);
|
||||
// Missing pushReceiptEndpoint is allowed.
|
||||
if (!pushEndpoint) {
|
||||
return {error: "Return code 201, but the answer is bogus, missing" +
|
||||
" pushEndpoint"};
|
||||
throw new Error("Missing push endpoint");
|
||||
}
|
||||
|
||||
var uri;
|
||||
var resUri = [];
|
||||
try {
|
||||
[pushEndpoint, pushReceiptEndpoint].forEach(u => {
|
||||
if (u) {
|
||||
uri = u;
|
||||
resUri[u] = Services.io.newURI(uri, null, serverURI);
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
debug("Invalid URI " + uri);
|
||||
return {error: "Return code 201, but URI is bogus. " + uri};
|
||||
var pushURI = Services.io.newURI(pushEndpoint, null, serverURI);
|
||||
var pushReceiptURI;
|
||||
if (pushReceiptEndpoint) {
|
||||
pushReceiptURI = Services.io.newURI(pushReceiptEndpoint, null,
|
||||
serverURI);
|
||||
}
|
||||
|
||||
return {
|
||||
pushEndpoint: resUri[pushEndpoint].spec,
|
||||
pushReceiptEndpoint: (pushReceiptEndpoint) ? resUri[pushReceiptEndpoint].spec
|
||||
: ""
|
||||
pushEndpoint: pushURI.spec,
|
||||
pushReceiptEndpoint: (pushReceiptURI) ? pushReceiptURI.spec : "",
|
||||
};
|
||||
}
|
||||
|
||||
@@ -441,44 +433,26 @@ this.PushServiceHttp2 = {
|
||||
PushRecordHttp2);
|
||||
},
|
||||
|
||||
serviceType: function() {
|
||||
return "http2";
|
||||
},
|
||||
|
||||
hasmainPushService: function() {
|
||||
return this._mainPushService !== null;
|
||||
},
|
||||
|
||||
checkServerURI: function(serverURL) {
|
||||
if (!serverURL) {
|
||||
debug("No dom.push.serverURL found!");
|
||||
return;
|
||||
}
|
||||
|
||||
let uri;
|
||||
try {
|
||||
uri = Services.io.newURI(serverURL, null, null);
|
||||
} catch(e) {
|
||||
debug("Error creating valid URI from dom.push.serverURL (" +
|
||||
serverURL + ")");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (uri.scheme !== "https") {
|
||||
debug("Unsupported websocket scheme " + uri.scheme);
|
||||
return null;
|
||||
}
|
||||
return uri;
|
||||
},
|
||||
|
||||
observe: function(aSubject, aTopic, aData) {
|
||||
if (aTopic == "nsPref:changed") {
|
||||
if (aData == "dom.push.debug") {
|
||||
gDebuggingEnabled = prefs.get("debug");
|
||||
}
|
||||
}
|
||||
validServerURI: function(serverURI) {
|
||||
return serverURI.scheme == "http" || serverURI.scheme == "https";
|
||||
},
|
||||
|
||||
connect: function(subscriptions) {
|
||||
this.startConnections(subscriptions);
|
||||
},
|
||||
|
||||
isConnected: function() {
|
||||
return this._mainPushService != null;
|
||||
},
|
||||
|
||||
disconnect: function() {
|
||||
this._shutdownConnections(false);
|
||||
},
|
||||
@@ -508,7 +482,7 @@ this.PushServiceHttp2 = {
|
||||
* Subscribe new resource.
|
||||
*/
|
||||
_subscribeResource: function(aRecord) {
|
||||
debug("subscribeResource()");
|
||||
console.debug("subscribeResource()");
|
||||
|
||||
return this._subscribeResourceInternal({
|
||||
record: aRecord,
|
||||
@@ -533,7 +507,7 @@ this.PushServiceHttp2 = {
|
||||
},
|
||||
|
||||
_subscribeResourceInternal: function(aSubInfo) {
|
||||
debug("subscribeResourceInternal()");
|
||||
console.debug("subscribeResourceInternal()");
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
var listener = new SubscriptionListener(aSubInfo,
|
||||
@@ -544,11 +518,7 @@ this.PushServiceHttp2 = {
|
||||
|
||||
var chan = this._makeChannel(this._serverURI.spec);
|
||||
chan.requestMethod = "POST";
|
||||
try {
|
||||
chan.asyncOpen(listener, null);
|
||||
} catch(e) {
|
||||
reject({status: 0, error: "NetworkError"});
|
||||
}
|
||||
chan.asyncOpen(listener, null);
|
||||
})
|
||||
.catch(err => {
|
||||
if ("retry" in err) {
|
||||
@@ -564,11 +534,7 @@ this.PushServiceHttp2 = {
|
||||
return new Promise((resolve,reject) => {
|
||||
var chan = this._makeChannel(aUri);
|
||||
chan.requestMethod = "DELETE";
|
||||
try {
|
||||
chan.asyncOpen(new PushServiceDelete(resolve, reject), null);
|
||||
} catch(err) {
|
||||
reject({status: 0, error: "NetworkError"});
|
||||
}
|
||||
chan.asyncOpen(new PushServiceDelete(resolve, reject), null);
|
||||
});
|
||||
},
|
||||
|
||||
@@ -577,7 +543,7 @@ this.PushServiceHttp2 = {
|
||||
* We can't do anything about it if it fails, so we don't listen for response.
|
||||
*/
|
||||
_unsubscribeResource: function(aSubscriptionUri) {
|
||||
debug("unsubscribeResource()");
|
||||
console.debug("unsubscribeResource()");
|
||||
|
||||
return this._deleteResource(aSubscriptionUri);
|
||||
},
|
||||
@@ -586,9 +552,10 @@ this.PushServiceHttp2 = {
|
||||
* Start listening for messages.
|
||||
*/
|
||||
_listenForMsgs: function(aSubscriptionUri) {
|
||||
debug("listenForMsgs() " + aSubscriptionUri);
|
||||
console.debug("listenForMsgs()", aSubscriptionUri);
|
||||
if (!this._conns[aSubscriptionUri]) {
|
||||
debug("We do not have this subscription " + aSubscriptionUri);
|
||||
console.warn("listenForMsgs: We do not have this subscription",
|
||||
aSubscriptionUri);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -603,7 +570,8 @@ this.PushServiceHttp2 = {
|
||||
try {
|
||||
chan.asyncOpen(listener, chan);
|
||||
} catch (e) {
|
||||
debug("Error connecting to push server. asyncOpen failed!");
|
||||
console.error("listenForMsgs: Error connecting to push server.",
|
||||
"asyncOpen failed", e);
|
||||
conn.listener.disconnect();
|
||||
chan.cancel(Cr.NS_ERROR_ABORT);
|
||||
this._retryAfterBackoff(aSubscriptionUri, -1);
|
||||
@@ -617,22 +585,20 @@ this.PushServiceHttp2 = {
|
||||
},
|
||||
|
||||
_ackMsgRecv: function(aAckUri) {
|
||||
debug("ackMsgRecv() " + aAckUri);
|
||||
console.debug("ackMsgRecv()", aAckUri);
|
||||
// We can't do anything about it if it fails,
|
||||
// so we don't listen for response.
|
||||
this._deleteResource(aAckUri);
|
||||
},
|
||||
|
||||
init: function(aOptions, aMainPushService, aServerURL) {
|
||||
debug("init()");
|
||||
console.debug("init()");
|
||||
this._mainPushService = aMainPushService;
|
||||
this._serverURI = aServerURL;
|
||||
gDebuggingEnabled = prefs.get("debug");
|
||||
prefs.observe("debug", this);
|
||||
},
|
||||
|
||||
_retryAfterBackoff: function(aSubscriptionUri, retryAfter) {
|
||||
debug("retryAfterBackoff()");
|
||||
console.debug("retryAfterBackoff()");
|
||||
|
||||
var resetRetryCount = prefs.get("http2.reset_retry_count_after_ms");
|
||||
// If it was running for some time, reset retry counter.
|
||||
@@ -672,12 +638,13 @@ this.PushServiceHttp2 = {
|
||||
this._conns[aSubscriptionUri].waitingForAlarm = true;
|
||||
this._mainPushService.setAlarm(retryAfter);
|
||||
}
|
||||
debug("Retry in " + retryAfter);
|
||||
|
||||
console.debug("retryAfterBackoff: Retry in", retryAfter);
|
||||
},
|
||||
|
||||
// Close connections.
|
||||
_shutdownConnections: function(deleteInfo) {
|
||||
debug("shutdownConnections()");
|
||||
console.debug("shutdownConnections()");
|
||||
|
||||
for (let subscriptionUri in this._conns) {
|
||||
if (this._conns[subscriptionUri]) {
|
||||
@@ -702,20 +669,21 @@ this.PushServiceHttp2 = {
|
||||
|
||||
// Start listening if subscriptions present.
|
||||
startConnections: function(aSubscriptions) {
|
||||
debug("startConnections() " + aSubscriptions.length);
|
||||
console.debug("startConnections()", aSubscriptions.length);
|
||||
|
||||
for (let i = 0; i < aSubscriptions.length; i++) {
|
||||
let record = aSubscriptions[i];
|
||||
this._mainPushService.ensureP256dhKey(record).then(record => {
|
||||
this._startSingleConnection(record);
|
||||
}, error => {
|
||||
debug("startConnections: Error updating record " + record.keyID);
|
||||
console.error("startConnections: Error updating record",
|
||||
record.keyID, error);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
_startSingleConnection: function(record) {
|
||||
debug("_startSingleConnection()");
|
||||
console.debug("_startSingleConnection()");
|
||||
if (typeof this._conns[record.subscriptionUri] != "object") {
|
||||
this._conns[record.subscriptionUri] = {channel: null,
|
||||
listener: null,
|
||||
@@ -730,7 +698,7 @@ this.PushServiceHttp2 = {
|
||||
|
||||
// Start listening if subscriptions present.
|
||||
_startConnectionsWaitingForAlarm: function() {
|
||||
debug("startConnectionsWaitingForAlarm()");
|
||||
console.debug("startConnectionsWaitingForAlarm()");
|
||||
for (let subscriptionUri in this._conns) {
|
||||
if ((this._conns[subscriptionUri]) &&
|
||||
!this._conns[subscriptionUri].conn &&
|
||||
@@ -743,7 +711,7 @@ this.PushServiceHttp2 = {
|
||||
|
||||
// Close connection and notify apps that subscription are gone.
|
||||
_shutdownSubscription: function(aSubscriptionUri) {
|
||||
debug("shutdownSubscriptions()");
|
||||
console.debug("shutdownSubscriptions()");
|
||||
|
||||
if (typeof this._conns[aSubscriptionUri] == "object") {
|
||||
if (this._conns[aSubscriptionUri].listener) {
|
||||
@@ -760,7 +728,7 @@ this.PushServiceHttp2 = {
|
||||
},
|
||||
|
||||
uninit: function() {
|
||||
debug("uninit()");
|
||||
console.debug("uninit()");
|
||||
this._shutdownConnections(true);
|
||||
this._mainPushService = null;
|
||||
},
|
||||
@@ -769,11 +737,12 @@ this.PushServiceHttp2 = {
|
||||
request: function(action, aRecord) {
|
||||
switch (action) {
|
||||
case "register":
|
||||
debug("register");
|
||||
return this._subscribeResource(aRecord);
|
||||
case "unregister":
|
||||
this._shutdownSubscription(aRecord.subscriptionUri);
|
||||
return this._unsubscribeResource(aRecord.subscriptionUri);
|
||||
default:
|
||||
return Promise.reject(new Error("Unknown request type: " + action));
|
||||
}
|
||||
},
|
||||
|
||||
@@ -801,7 +770,7 @@ this.PushServiceHttp2 = {
|
||||
|
||||
connOnStop: function(aRequest, aSuccess,
|
||||
aSubscriptionUri) {
|
||||
debug("connOnStop() succeeded: " + aSuccess);
|
||||
console.debug("connOnStop() succeeded", aSuccess);
|
||||
|
||||
var conn = this._conns[aSubscriptionUri];
|
||||
if (!conn) {
|
||||
@@ -832,7 +801,7 @@ this.PushServiceHttp2 = {
|
||||
},
|
||||
|
||||
_pushChannelOnStop: function(aUri, aAckUri, aMessage, dh, salt, rs) {
|
||||
debug("pushChannelOnStop() ");
|
||||
console.debug("pushChannelOnStop()");
|
||||
|
||||
let cryptoParams = {
|
||||
dh: dh,
|
||||
@@ -847,7 +816,8 @@ this.PushServiceHttp2 = {
|
||||
)
|
||||
.then(_ => this._ackMsgRecv(aAckUri))
|
||||
.catch(err => {
|
||||
debug("Error receiving message: " + err);
|
||||
console.error("pushChannelOnStop: Error receiving message",
|
||||
err);
|
||||
});
|
||||
},
|
||||
|
||||
@@ -870,14 +840,8 @@ PushRecordHttp2.prototype = Object.create(PushRecord.prototype, {
|
||||
},
|
||||
});
|
||||
|
||||
PushRecordHttp2.prototype.toRegistration = function() {
|
||||
let registration = PushRecord.prototype.toRegistration.call(this);
|
||||
registration.pushReceiptEndpoint = this.pushReceiptEndpoint;
|
||||
return registration;
|
||||
};
|
||||
|
||||
PushRecordHttp2.prototype.toRegister = function() {
|
||||
let register = PushRecord.prototype.toRegister.call(this);
|
||||
register.pushReceiptEndpoint = this.pushReceiptEndpoint;
|
||||
return register;
|
||||
PushRecordHttp2.prototype.toSubscription = function() {
|
||||
let subscription = PushRecord.prototype.toSubscription.call(this);
|
||||
subscription.pushReceiptEndpoint = this.pushReceiptEndpoint;
|
||||
return subscription;
|
||||
};
|
||||
|
||||
+251
-232
File diff suppressed because it is too large
Load Diff
+1
-4
@@ -10,10 +10,6 @@ EXTRA_COMPONENTS += [
|
||||
'PushNotificationService.js',
|
||||
]
|
||||
|
||||
EXTRA_PP_JS_MODULES += [
|
||||
'PushServiceWebSocket.jsm',
|
||||
]
|
||||
|
||||
EXTRA_JS_MODULES += [
|
||||
'PushCrypto.jsm',
|
||||
'PushDB.jsm',
|
||||
@@ -21,6 +17,7 @@ EXTRA_JS_MODULES += [
|
||||
'PushService.jsm',
|
||||
'PushServiceChildPreload.jsm',
|
||||
'PushServiceHttp2.jsm',
|
||||
'PushServiceWebSocket.jsm',
|
||||
]
|
||||
|
||||
MOCHITEST_MANIFESTS += [
|
||||
|
||||
@@ -120,7 +120,6 @@ http://creativecommons.org/licenses/publicdomain/
|
||||
|
||||
SpecialPowers.pushPrefEnv({"set": [
|
||||
["dom.push.enabled", true],
|
||||
["dom.push.debug", true],
|
||||
["dom.serviceWorkers.exemptFromPerDomainMax", true],
|
||||
["dom.serviceWorkers.enabled", true],
|
||||
["dom.serviceWorkers.testing.enabled", true]
|
||||
|
||||
@@ -324,7 +324,7 @@
|
||||
["dom.serviceWorkers.testing.enabled", true],
|
||||
["dom.serviceWorkers.interception.enabled", true]
|
||||
]}, runTest);
|
||||
SpecialPowers.addPermission('push', true, document);
|
||||
SpecialPowers.addPermission('desktop-notification', true, document);
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
</script>
|
||||
</body>
|
||||
|
||||
@@ -7,10 +7,12 @@ let {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
||||
|
||||
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
|
||||
Cu.import('resource://gre/modules/Services.jsm');
|
||||
Cu.import('resource://gre/modules/Task.jsm');
|
||||
Cu.import('resource://gre/modules/Timer.jsm');
|
||||
Cu.import('resource://gre/modules/Promise.jsm');
|
||||
Cu.import('resource://gre/modules/Preferences.jsm');
|
||||
Cu.import('resource://gre/modules/PlacesUtils.jsm');
|
||||
Cu.import('resource://gre/modules/ObjectUtils.jsm');
|
||||
|
||||
const serviceExports = Cu.import('resource://gre/modules/PushService.jsm', {});
|
||||
const servicePrefs = new Preferences('dom.push.');
|
||||
@@ -180,7 +182,7 @@ function disableServiceWorkerEvents(...scopes) {
|
||||
for (let scope of scopes) {
|
||||
Services.perms.add(
|
||||
Services.io.newURI(scope, null, null),
|
||||
'push',
|
||||
'desktop-notification',
|
||||
Ci.nsIPermissionManager.DENY_ACTION
|
||||
);
|
||||
}
|
||||
@@ -194,7 +196,7 @@ function disableServiceWorkerEvents(...scopes) {
|
||||
*/
|
||||
function setPrefs(prefs = {}) {
|
||||
let defaultPrefs = Object.assign({
|
||||
debug: true,
|
||||
loglevel: 'all',
|
||||
serverURL: 'wss://push.example.org',
|
||||
'connection.enabled': true,
|
||||
userAgentID: '',
|
||||
@@ -221,6 +223,7 @@ function setPrefs(prefs = {}) {
|
||||
'http2.retryInterval': 500,
|
||||
'http2.reset_retry_count_after_ms': 60000,
|
||||
maxQuotaPerSubscription: 16,
|
||||
quotaUpdateDelay: 3000,
|
||||
}, prefs);
|
||||
for (let pref in defaultPrefs) {
|
||||
servicePrefs.set(pref, defaultPrefs[pref]);
|
||||
@@ -378,7 +381,11 @@ MockWebSocket.prototype = {
|
||||
() => this._listener.onServerClose(this._context, statusCode, reason),
|
||||
() => this._listener.onStop(this._context, Cr.NS_BASE_STREAM_CLOSED)
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
serverInterrupt(result = Cr.NS_ERROR_NET_RESET) {
|
||||
waterfall(() => this._listener.onStop(this._context, result));
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -25,7 +25,6 @@ add_task(function* test_unregister_success() {
|
||||
quota: Infinity,
|
||||
});
|
||||
|
||||
let unregisterDefer = Promise.defer();
|
||||
PushService.init({
|
||||
serverURI: "wss://push.example.org/",
|
||||
networkInfo: new MockDesktopNetworkInfo(),
|
||||
|
||||
@@ -0,0 +1,144 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
'use strict';
|
||||
|
||||
const {PushDB, PushService, PushServiceWebSocket} = serviceExports;
|
||||
|
||||
const userAgentID = 'bd744428-f125-436a-b6d0-dd0c9845837f';
|
||||
|
||||
let clearForPattern = Task.async(function* (testRecords, pattern) {
|
||||
let patternString = JSON.stringify(pattern);
|
||||
yield PushService._clearOriginData(patternString);
|
||||
|
||||
for (let length = testRecords.length; length--;) {
|
||||
let test = testRecords[length];
|
||||
let originSuffix = ChromeUtils.originAttributesToSuffix(
|
||||
test.originAttributes);
|
||||
|
||||
let registration = yield PushNotificationService.registration(
|
||||
test.scope,
|
||||
originSuffix
|
||||
);
|
||||
|
||||
let url = test.scope + originSuffix;
|
||||
|
||||
if (ObjectUtils.deepEqual(test.clearIf, pattern)) {
|
||||
ok(!registration, 'Should clear registration ' + url +
|
||||
' for pattern ' + patternString);
|
||||
testRecords.splice(length, 1);
|
||||
} else {
|
||||
ok(registration, 'Should not clear registration ' + url +
|
||||
' for pattern ' + patternString);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
function run_test() {
|
||||
do_get_profile();
|
||||
setPrefs({
|
||||
userAgentID,
|
||||
requestTimeout: 1000,
|
||||
retryBaseInterval: 150
|
||||
});
|
||||
disableServiceWorkerEvents(
|
||||
'https://example.org/1'
|
||||
);
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* test_webapps_cleardata() {
|
||||
let db = PushServiceWebSocket.newPushDB();
|
||||
do_register_cleanup(() => {return db.drop().then(_ => db.close());});
|
||||
|
||||
let testRecords = [{
|
||||
scope: 'https://example.org/1',
|
||||
originAttributes: { appId: 1 },
|
||||
clearIf: { appId: 1, inBrowser: false },
|
||||
}, {
|
||||
scope: 'https://example.org/1',
|
||||
originAttributes: { appId: 1, inBrowser: true },
|
||||
clearIf: { appId: 1 },
|
||||
}, {
|
||||
scope: 'https://example.org/1',
|
||||
originAttributes: { appId: 2, inBrowser: true },
|
||||
clearIf: { appId: 2, inBrowser: true },
|
||||
}, {
|
||||
scope: 'https://example.org/2',
|
||||
originAttributes: { appId: 1 },
|
||||
clearIf: { appId: 1, inBrowser: false },
|
||||
}, {
|
||||
scope: 'https://example.org/2',
|
||||
originAttributes: { appId: 2, inBrowser: true },
|
||||
clearIf: { appId: 2, inBrowser: true },
|
||||
}, {
|
||||
scope: 'https://example.org/3',
|
||||
originAttributes: { appId: 3, inBrowser: true },
|
||||
clearIf: { inBrowser: true },
|
||||
}, {
|
||||
scope: 'https://example.org/3',
|
||||
originAttributes: { appId: 4, inBrowser: true },
|
||||
clearIf: { inBrowser: true },
|
||||
}];
|
||||
|
||||
let unregisterDone;
|
||||
let unregisterPromise = new Promise(resolve =>
|
||||
unregisterDone = after(testRecords.length, resolve));
|
||||
|
||||
PushService.init({
|
||||
serverURI: "wss://push.example.org",
|
||||
networkInfo: new MockDesktopNetworkInfo(),
|
||||
db,
|
||||
makeWebSocket(uri) {
|
||||
return new MockWebSocket(uri, {
|
||||
onHello(data) {
|
||||
equal(data.messageType, 'hello', 'Handshake: wrong message type');
|
||||
equal(data.uaid, userAgentID, 'Handshake: wrong device ID');
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'hello',
|
||||
status: 200,
|
||||
uaid: userAgentID
|
||||
}));
|
||||
},
|
||||
onRegister(data) {
|
||||
equal(data.messageType, 'register', 'Register: wrong message type');
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'register',
|
||||
status: 200,
|
||||
channelID: data.channelID,
|
||||
uaid: userAgentID,
|
||||
pushEndpoint: 'https://example.com/update/' + Math.random(),
|
||||
}));
|
||||
},
|
||||
onUnregister(data) {
|
||||
unregisterDone();
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
yield Promise.all(testRecords.map(test =>
|
||||
PushNotificationService.register(
|
||||
test.scope,
|
||||
ChromeUtils.originAttributesToSuffix(test.originAttributes)
|
||||
)
|
||||
));
|
||||
|
||||
// Removes records for all scopes with the same app ID. Excludes records
|
||||
// where `inBrowser` is true.
|
||||
yield clearForPattern(testRecords, { appId: 1, inBrowser: false });
|
||||
|
||||
// Removes the remaining record for app ID 1, where `inBrowser` is true.
|
||||
yield clearForPattern(testRecords, { appId: 1 });
|
||||
|
||||
// Removes all records for all scopes with the same app ID, where
|
||||
// `inBrowser` is true.
|
||||
yield clearForPattern(testRecords, { appId: 2, inBrowser: true });
|
||||
|
||||
// Removes all records where `inBrowser` is true.
|
||||
yield clearForPattern(testRecords, { inBrowser: true });
|
||||
|
||||
equal(testRecords.length, 0, 'Should remove all test records');
|
||||
yield waitForPromise(unregisterPromise, DEFAULT_TIMEOUT,
|
||||
'Timed out waiting for unregister');
|
||||
});
|
||||
@@ -0,0 +1,153 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
'use strict';
|
||||
|
||||
const {PushDB, PushService, PushServiceWebSocket} = serviceExports;
|
||||
|
||||
const userAgentID = '2c43af06-ab6e-476a-adc4-16cbda54fb89';
|
||||
|
||||
var db;
|
||||
var quotaURI;
|
||||
var permURI;
|
||||
|
||||
function visitURI(uri, timestamp) {
|
||||
return addVisit({
|
||||
uri: uri,
|
||||
title: uri.spec,
|
||||
visits: [{
|
||||
visitDate: timestamp * 1000,
|
||||
transitionType: Ci.nsINavHistoryService.TRANSITION_LINK,
|
||||
}],
|
||||
});
|
||||
}
|
||||
|
||||
var putRecord = Task.async(function* ({scope, perm, quota, lastPush, lastVisit}) {
|
||||
let uri = Services.io.newURI(scope, null, null);
|
||||
|
||||
Services.perms.add(uri, 'desktop-notification',
|
||||
Ci.nsIPermissionManager[perm]);
|
||||
do_register_cleanup(() => {
|
||||
Services.perms.remove(uri, 'desktop-notification');
|
||||
});
|
||||
|
||||
yield visitURI(uri, lastVisit);
|
||||
|
||||
yield db.put({
|
||||
channelID: uri.path,
|
||||
pushEndpoint: 'https://example.org/push' + uri.path,
|
||||
scope: uri.spec,
|
||||
pushCount: 0,
|
||||
lastPush: lastPush,
|
||||
version: null,
|
||||
originAttributes: '',
|
||||
quota: quota,
|
||||
});
|
||||
|
||||
return uri;
|
||||
});
|
||||
|
||||
function run_test() {
|
||||
do_get_profile();
|
||||
setPrefs({
|
||||
userAgentID: userAgentID,
|
||||
});
|
||||
|
||||
db = PushServiceWebSocket.newPushDB();
|
||||
do_register_cleanup(() => {return db.drop().then(_ => db.close());});
|
||||
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* setUp() {
|
||||
// An expired registration that should be evicted on startup. Permission is
|
||||
// granted for this origin, and the last visit is more recent than the last
|
||||
// push message.
|
||||
yield putRecord({
|
||||
scope: 'https://example.com/expired-quota-restored',
|
||||
perm: 'ALLOW_ACTION',
|
||||
quota: 0,
|
||||
lastPush: Date.now() - 10,
|
||||
lastVisit: Date.now(),
|
||||
});
|
||||
|
||||
// An expired registration that we should evict when the origin is visited
|
||||
// again.
|
||||
quotaURI = yield putRecord({
|
||||
scope: 'https://example.xyz/expired-quota-exceeded',
|
||||
perm: 'ALLOW_ACTION',
|
||||
quota: 0,
|
||||
lastPush: Date.now() - 10,
|
||||
lastVisit: Date.now() - 20,
|
||||
});
|
||||
|
||||
// An expired registration that we should evict when permission is granted
|
||||
// again.
|
||||
permURI = yield putRecord({
|
||||
scope: 'https://example.info/expired-perm-revoked',
|
||||
perm: 'DENY_ACTION',
|
||||
quota: 0,
|
||||
lastPush: Date.now() - 10,
|
||||
lastVisit: Date.now(),
|
||||
});
|
||||
|
||||
// An active registration that we should leave alone.
|
||||
yield putRecord({
|
||||
scope: 'https://example.ninja/active',
|
||||
perm: 'ALLOW_ACTION',
|
||||
quota: 16,
|
||||
lastPush: Date.now() - 10,
|
||||
lastVisit: Date.now() - 20,
|
||||
});
|
||||
|
||||
let subChangePromise = promiseObserverNotification(
|
||||
'push-subscription-change',
|
||||
(subject, data) => data == 'https://example.com/expired-quota-restored'
|
||||
);
|
||||
|
||||
PushService.init({
|
||||
serverURI: 'wss://push.example.org/',
|
||||
networkInfo: new MockDesktopNetworkInfo(),
|
||||
db,
|
||||
makeWebSocket(uri) {
|
||||
return new MockWebSocket(uri, {
|
||||
onHello(request) {
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'hello',
|
||||
status: 200,
|
||||
uaid: userAgentID,
|
||||
}));
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
yield waitForPromise(subChangePromise, DEFAULT_TIMEOUT,
|
||||
'Timed out waiting for subscription change event on startup');
|
||||
});
|
||||
|
||||
add_task(function* test_site_visited() {
|
||||
let subChangePromise = promiseObserverNotification(
|
||||
'push-subscription-change',
|
||||
(subject, data) => data == 'https://example.xyz/expired-quota-exceeded'
|
||||
);
|
||||
|
||||
yield visitURI(quotaURI, Date.now());
|
||||
PushService.observe(null, 'idle-daily', '');
|
||||
|
||||
yield waitForPromise(subChangePromise, DEFAULT_TIMEOUT,
|
||||
'Timed out waiting for subscription change event after visit');
|
||||
});
|
||||
|
||||
add_task(function* test_perm_restored() {
|
||||
let subChangePromise = promiseObserverNotification(
|
||||
'push-subscription-change',
|
||||
(subject, data) => data == 'https://example.info/expired-perm-revoked'
|
||||
);
|
||||
|
||||
Services.perms.add(permURI, 'desktop-notification',
|
||||
Ci.nsIPermissionManager.ALLOW_ACTION);
|
||||
|
||||
yield waitForPromise(subChangePromise, DEFAULT_TIMEOUT,
|
||||
'Timed out waiting for subscription change event after permission');
|
||||
});
|
||||
@@ -54,7 +54,8 @@ add_task(function* test_notification_ack() {
|
||||
]);
|
||||
|
||||
let acks = 0;
|
||||
let ackDefer = Promise.defer();
|
||||
let ackDone;
|
||||
let ackPromise = new Promise(resolve => ackDone = resolve);
|
||||
PushService.init({
|
||||
serverURI: "wss://push.example.org/",
|
||||
networkInfo: new MockDesktopNetworkInfo(),
|
||||
@@ -64,11 +65,6 @@ add_task(function* test_notification_ack() {
|
||||
onHello(request) {
|
||||
equal(request.uaid, userAgentID,
|
||||
'Should send matching device IDs in handshake');
|
||||
deepEqual(request.channelIDs.sort(), [
|
||||
'21668e05-6da8-42c9-b8ab-9cc3f4d5630c',
|
||||
'5477bfda-22db-45d4-9614-fee369630260',
|
||||
'9a5ff87f-47c9-4215-b2b8-0bdd38b4b305'
|
||||
], 'Should send matching channel IDs in handshake');
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'hello',
|
||||
uaid: userAgentID,
|
||||
@@ -115,7 +111,7 @@ add_task(function* test_notification_ack() {
|
||||
channelID: '5477bfda-22db-45d4-9614-fee369630260',
|
||||
version: 6
|
||||
}], updates, 'Wrong updates for acknowledgement 3');
|
||||
ackDefer.resolve();
|
||||
ackDone();
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -128,6 +124,6 @@ add_task(function* test_notification_ack() {
|
||||
|
||||
yield waitForPromise(notifyPromise, DEFAULT_TIMEOUT,
|
||||
'Timed out waiting for notifications');
|
||||
yield waitForPromise(ackDefer.promise, DEFAULT_TIMEOUT,
|
||||
yield waitForPromise(ackPromise, DEFAULT_TIMEOUT,
|
||||
'Timed out waiting for multiple acknowledgements');
|
||||
});
|
||||
|
||||
@@ -0,0 +1,193 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
'use strict';
|
||||
|
||||
const {PushDB, PushService, PushServiceWebSocket} = serviceExports;
|
||||
|
||||
let db;
|
||||
let userAgentID = 'f5b47f8d-771f-4ea3-b999-91c135f8766d';
|
||||
|
||||
function run_test() {
|
||||
do_get_profile();
|
||||
setPrefs({
|
||||
userAgentID: userAgentID,
|
||||
});
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
function putRecord(channelID, scope, publicKey, privateKey) {
|
||||
return db.put({
|
||||
channelID: channelID,
|
||||
pushEndpoint: 'https://example.org/push/' + channelID,
|
||||
scope: scope,
|
||||
pushCount: 0,
|
||||
lastPush: 0,
|
||||
originAttributes: '',
|
||||
quota: Infinity,
|
||||
p256dhPublicKey: publicKey,
|
||||
p256dhPrivateKey: privateKey,
|
||||
});
|
||||
}
|
||||
|
||||
add_task(function* test_notification_ack_data() {
|
||||
db = PushServiceWebSocket.newPushDB();
|
||||
do_register_cleanup(() => {return db.drop().then(_ => db.close());});
|
||||
|
||||
yield putRecord(
|
||||
'subscription1',
|
||||
'https://example.com/page/1',
|
||||
'BPCd4gNQkjwRah61LpdALdzZKLLnU5UAwDztQ5_h0QsT26jk0IFbqcK6-JxhHAm-rsHEwy0CyVJjtnfOcqc1tgA',
|
||||
{
|
||||
crv: 'P-256',
|
||||
d: '1jUPhzVsRkzV0vIzwL4ZEsOlKdNOWm7TmaTfzitJkgM',
|
||||
ext: true,
|
||||
key_ops: ["deriveBits"],
|
||||
kty: "EC",
|
||||
x: '8J3iA1CSPBFqHrUul0At3NkosudTlQDAPO1Dn-HRCxM',
|
||||
y: '26jk0IFbqcK6-JxhHAm-rsHEwy0CyVJjtnfOcqc1tgA'
|
||||
}
|
||||
);
|
||||
yield putRecord(
|
||||
'subscription2',
|
||||
'https://example.com/page/2',
|
||||
'BPnWyUo7yMnuMlyKtERuLfWE8a09dtdjHSW2lpC9_BqR5TZ1rK8Ldih6ljyxVwnBA-nygQHGRpEmu1jV5K8437E',
|
||||
{
|
||||
crv: 'P-256',
|
||||
d: 'lFm4nPsUKYgNGBJb5nXXKxl8bspCSp0bAhCYxbveqT4',
|
||||
ext: true,
|
||||
key_ops: ["deriveBits"],
|
||||
kty: 'EC',
|
||||
x: '-dbJSjvIye4yXIq0RG4t9YTxrT1212MdJbaWkL38GpE',
|
||||
y: '5TZ1rK8Ldih6ljyxVwnBA-nygQHGRpEmu1jV5K8437E'
|
||||
}
|
||||
);
|
||||
yield putRecord(
|
||||
'subscription3',
|
||||
'https://example.com/page/3',
|
||||
'BDhUHITSeVrWYybFnb7ylVTCDDLPdQWMpf8gXhcWwvaaJa6n3YH8TOcH8narDF6t8mKVvg2ioLW-8MH5O4dzGcI',
|
||||
{
|
||||
crv: 'P-256',
|
||||
d: 'Q1_SE1NySTYzjbqgWwPgrYh7XRg3adqZLkQPsy319G8',
|
||||
ext: true,
|
||||
key_ops: ["deriveBits"],
|
||||
kty: 'EC',
|
||||
x: 'OFQchNJ5WtZjJsWdvvKVVMIMMs91BYyl_yBeFxbC9po',
|
||||
y: 'Ja6n3YH8TOcH8narDF6t8mKVvg2ioLW-8MH5O4dzGcI'
|
||||
}
|
||||
);
|
||||
|
||||
let updates = [];
|
||||
let notifyPromise = promiseObserverNotification('push-notification', function(subject, data) {
|
||||
let notification = subject.QueryInterface(Ci.nsIPushObserverNotification);
|
||||
updates.push({
|
||||
scope: data,
|
||||
data: notification.data,
|
||||
});
|
||||
return updates.length == 3;
|
||||
});
|
||||
|
||||
let acks = 0;
|
||||
let ackDone;
|
||||
let ackPromise = new Promise(resolve => ackDone = resolve);
|
||||
PushService.init({
|
||||
serverURI: "wss://push.example.org/",
|
||||
networkInfo: new MockDesktopNetworkInfo(),
|
||||
db,
|
||||
makeWebSocket(uri) {
|
||||
return new MockWebSocket(uri, {
|
||||
onHello(request) {
|
||||
equal(request.uaid, userAgentID,
|
||||
'Should send matching device IDs in handshake');
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'hello',
|
||||
uaid: userAgentID,
|
||||
status: 200,
|
||||
use_webpush: true,
|
||||
}));
|
||||
// subscription1 will send a message with no rs and padding
|
||||
// length 1.
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'notification',
|
||||
channelID: 'subscription1',
|
||||
headers: {
|
||||
encryption_key: 'keyid="notification1"; dh="BO_tgGm-yvYAGLeRe16AvhzaUcpYRiqgsGOlXpt0DRWDRGGdzVLGlEVJMygqAUECarLnxCiAOHTP_znkedrlWoU"',
|
||||
encryption: 'keyid="notification1";salt="uAZaiXpOSfOLJxtOCZ09dA"',
|
||||
},
|
||||
data: 'NwrrOWPxLE8Sv5Rr0Kep7n0-r_j3rsYrUw_CXPo',
|
||||
version: 'v1',
|
||||
}));
|
||||
},
|
||||
onACK(request) {
|
||||
switch (++acks) {
|
||||
case 1:
|
||||
deepEqual([{
|
||||
channelID: 'subscription1',
|
||||
version: 'v1',
|
||||
}], request.updates, 'Wrong updates for acknowledgement 1');
|
||||
// subscription2 will send a message with no rs and padding
|
||||
// length 16.
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'notification',
|
||||
channelID: 'subscription2',
|
||||
headers: {
|
||||
encryption_key: 'keyid="notification2"; dh="BKVdQcgfncpNyNWsGrbecX0zq3eHIlHu5XbCGmVcxPnRSbhjrA6GyBIeGdqsUL69j5Z2CvbZd-9z1UBH0akUnGQ"',
|
||||
encryption: 'keyid="notification2";salt="vFn3t3M_k42zHBdpch3VRw"',
|
||||
},
|
||||
data: 'Zt9dEdqgHlyAL_l83385aEtb98ZBilz5tgnGgmwEsl5AOCNgesUUJ4p9qUU',
|
||||
version: 'v2',
|
||||
}));
|
||||
break;
|
||||
|
||||
case 2:
|
||||
deepEqual([{
|
||||
channelID: 'subscription2',
|
||||
version: 'v2',
|
||||
}], request.updates, 'Wrong updates for acknowledgement 2');
|
||||
// subscription3 will send a message with rs equal 24 and
|
||||
// padding length 16.
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'notification',
|
||||
channelID: 'subscription3',
|
||||
headers: {
|
||||
encryption_key: 'keyid="notification3";dh="BD3xV_ACT8r6hdIYES3BJj1qhz9wyv7MBrG9vM2UCnjPzwE_YFVpkD-SGqE-BR2--0M-Yf31wctwNsO1qjBUeMg"',
|
||||
encryption: 'keyid="notification3"; salt="DFq188piWU7osPBgqn4Nlg"; rs=24',
|
||||
},
|
||||
data: 'LKru3ZzxBZuAxYtsaCfaj_fehkrIvqbVd1iSwnwAUgnL-cTeDD-83blxHXTq7r0z9ydTdMtC3UjAcWi8LMnfY-BFzi0qJAjGYIikDA',
|
||||
version: 'v3',
|
||||
}));
|
||||
break;
|
||||
|
||||
case 3:
|
||||
deepEqual([{
|
||||
channelID: 'subscription3',
|
||||
version: 'v3',
|
||||
}], request.updates, 'Wrong updates for acknowledgement 3');
|
||||
ackDone();
|
||||
break;
|
||||
|
||||
default:
|
||||
ok(false, 'Unexpected acknowledgement ' + acks);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
yield waitForPromise(notifyPromise, DEFAULT_TIMEOUT,
|
||||
'Timed out waiting for notifications');
|
||||
yield waitForPromise(ackPromise, DEFAULT_TIMEOUT,
|
||||
'Timed out waiting for multiple acknowledgements');
|
||||
|
||||
updates.sort((a, b) => a.scope < b.scope ? -1 : a.scope > b.scope ? 1 : 0);
|
||||
deepEqual([{
|
||||
scope: 'https://example.com/page/1',
|
||||
data: 'Some message',
|
||||
}, {
|
||||
scope: 'https://example.com/page/2',
|
||||
data: 'Some message',
|
||||
}, {
|
||||
scope: 'https://example.com/page/3',
|
||||
data: 'Some message',
|
||||
}], updates, 'Wrong data for notifications');
|
||||
});
|
||||
@@ -5,9 +5,13 @@
|
||||
|
||||
const {PushDB, PushService, PushServiceWebSocket} = serviceExports;
|
||||
|
||||
const userAgentID = '1500e7d9-8cbe-4ee6-98da-7fa5d6a39852';
|
||||
|
||||
function run_test() {
|
||||
do_get_profile();
|
||||
setPrefs();
|
||||
setPrefs({
|
||||
userAgentID: userAgentID,
|
||||
});
|
||||
disableServiceWorkerEvents(
|
||||
'https://example.com/1',
|
||||
'https://example.com/2'
|
||||
@@ -41,8 +45,8 @@ add_task(function* test_notification_duplicate() {
|
||||
let notifyPromise = promiseObserverNotification('push-notification');
|
||||
|
||||
let acks = 0;
|
||||
let ackDefer = Promise.defer();
|
||||
let ackDone = after(2, ackDefer.resolve);
|
||||
let ackDone;
|
||||
let ackPromise = new Promise(resolve => ackDone = after(2, resolve));
|
||||
PushService.init({
|
||||
serverURI: "wss://push.example.org/",
|
||||
networkInfo: new MockDesktopNetworkInfo(),
|
||||
@@ -53,7 +57,7 @@ add_task(function* test_notification_duplicate() {
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'hello',
|
||||
status: 200,
|
||||
uaid: '1500e7d9-8cbe-4ee6-98da-7fa5d6a39852'
|
||||
uaid: userAgentID,
|
||||
}));
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'notification',
|
||||
@@ -73,7 +77,7 @@ add_task(function* test_notification_duplicate() {
|
||||
|
||||
yield waitForPromise(notifyPromise, DEFAULT_TIMEOUT,
|
||||
'Timed out waiting for notifications');
|
||||
yield waitForPromise(ackDefer.promise, DEFAULT_TIMEOUT,
|
||||
yield waitForPromise(ackPromise, DEFAULT_TIMEOUT,
|
||||
'Timed out waiting for stale acknowledgement');
|
||||
|
||||
let staleRecord = yield db.getByKeyID(
|
||||
|
||||
@@ -5,9 +5,13 @@
|
||||
|
||||
const {PushDB, PushService, PushServiceWebSocket} = serviceExports;
|
||||
|
||||
const userAgentID = '3c7462fc-270f-45be-a459-b9d631b0d093';
|
||||
|
||||
function run_test() {
|
||||
do_get_profile();
|
||||
setPrefs();
|
||||
setPrefs({
|
||||
userAgentID: userAgentID,
|
||||
});
|
||||
disableServiceWorkerEvents(
|
||||
'https://example.com/a',
|
||||
'https://example.com/b',
|
||||
@@ -58,8 +62,8 @@ add_task(function* test_notification_error() {
|
||||
)
|
||||
]);
|
||||
|
||||
let ackDefer = Promise.defer();
|
||||
let ackDone = after(records.length, ackDefer.resolve);
|
||||
let ackDone;
|
||||
let ackPromise = new Promise(resolve => ackDone = after(records.length, resolve));
|
||||
PushService.init({
|
||||
serverURI: "wss://push.example.org/",
|
||||
networkInfo: new MockDesktopNetworkInfo(),
|
||||
@@ -74,15 +78,10 @@ add_task(function* test_notification_error() {
|
||||
makeWebSocket(uri) {
|
||||
return new MockWebSocket(uri, {
|
||||
onHello(request) {
|
||||
deepEqual(request.channelIDs.sort(), [
|
||||
'3c3930ba-44de-40dc-a7ca-8a133ec1a866',
|
||||
'b63f7bef-0a0d-4236-b41e-086a69dfd316',
|
||||
'f04f1e46-9139-4826-b2d1-9411b0821283'
|
||||
], 'Wrong channel list');
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'hello',
|
||||
status: 200,
|
||||
uaid: '3c7462fc-270f-45be-a459-b9d631b0d093'
|
||||
uaid: userAgentID,
|
||||
}));
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'notification',
|
||||
@@ -112,7 +111,7 @@ add_task(function* test_notification_error() {
|
||||
'Wrong endpoint for notification C');
|
||||
equal(cPush.version, 4, 'Wrong version for notification C');
|
||||
|
||||
yield waitForPromise(ackDefer.promise, DEFAULT_TIMEOUT,
|
||||
yield waitForPromise(ackPromise, DEFAULT_TIMEOUT,
|
||||
'Timed out waiting for acknowledgements');
|
||||
|
||||
let aRecord = yield db.getByIdentifiers({scope: 'https://example.com/a',
|
||||
|
||||
@@ -5,9 +5,13 @@
|
||||
|
||||
const {PushDB, PushService, PushServiceWebSocket} = serviceExports;
|
||||
|
||||
const userAgentID = '1ca1cf66-eeb4-4df7-87c1-d5c92906ab90';
|
||||
|
||||
function run_test() {
|
||||
do_get_profile();
|
||||
setPrefs();
|
||||
setPrefs({
|
||||
userAgentID: userAgentID,
|
||||
});
|
||||
disableServiceWorkerEvents(
|
||||
'https://example.com/page/1',
|
||||
'https://example.com/page/2',
|
||||
@@ -57,8 +61,8 @@ add_task(function* test_notification_incomplete() {
|
||||
ok(false, 'Should not deliver malformed updates');
|
||||
}, 'push-notification', false);
|
||||
|
||||
let notificationDefer = Promise.defer();
|
||||
let notificationDone = after(2, notificationDefer.resolve);
|
||||
let notificationDone;
|
||||
let notificationPromise = new Promise(resolve => notificationDone = after(2, resolve));
|
||||
let prevHandler = PushServiceWebSocket._handleNotificationReply;
|
||||
PushServiceWebSocket._handleNotificationReply = function _handleNotificationReply() {
|
||||
notificationDone();
|
||||
@@ -74,7 +78,7 @@ add_task(function* test_notification_incomplete() {
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'hello',
|
||||
status: 200,
|
||||
uaid: '1ca1cf66-eeb4-4df7-87c1-d5c92906ab90'
|
||||
uaid: userAgentID,
|
||||
}));
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
// Missing "updates" field; should ignore message.
|
||||
@@ -107,7 +111,7 @@ add_task(function* test_notification_incomplete() {
|
||||
}
|
||||
});
|
||||
|
||||
yield waitForPromise(notificationDefer.promise, DEFAULT_TIMEOUT,
|
||||
yield waitForPromise(notificationPromise, DEFAULT_TIMEOUT,
|
||||
'Timed out waiting for incomplete notifications');
|
||||
|
||||
let storeRecords = yield db.getAllKeyIDs();
|
||||
|
||||
@@ -5,9 +5,13 @@
|
||||
|
||||
const {PushDB, PushService, PushServiceWebSocket} = serviceExports;
|
||||
|
||||
const userAgentID = 'ba31ac13-88d4-4984-8e6b-8731315a7cf8';
|
||||
|
||||
function run_test() {
|
||||
do_get_profile();
|
||||
setPrefs();
|
||||
setPrefs({
|
||||
userAgentID: userAgentID,
|
||||
});
|
||||
disableServiceWorkerEvents(
|
||||
'https://example.net/case'
|
||||
);
|
||||
@@ -28,7 +32,8 @@ add_task(function* test_notification_version_string() {
|
||||
|
||||
let notifyPromise = promiseObserverNotification('push-notification');
|
||||
|
||||
let ackDefer = Promise.defer();
|
||||
let ackDone;
|
||||
let ackPromise = new Promise(resolve => ackDone = resolve);
|
||||
PushService.init({
|
||||
serverURI: "wss://push.example.org/",
|
||||
networkInfo: new MockDesktopNetworkInfo(),
|
||||
@@ -39,7 +44,7 @@ add_task(function* test_notification_version_string() {
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'hello',
|
||||
status: 200,
|
||||
uaid: 'ba31ac13-88d4-4984-8e6b-8731315a7cf8'
|
||||
uaid: userAgentID,
|
||||
}));
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'notification',
|
||||
@@ -49,7 +54,7 @@ add_task(function* test_notification_version_string() {
|
||||
}]
|
||||
}));
|
||||
},
|
||||
onACK: ackDefer.resolve
|
||||
onACK: ackDone
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -65,7 +70,7 @@ add_task(function* test_notification_version_string() {
|
||||
'Wrong push endpoint');
|
||||
strictEqual(message.version, 4, 'Wrong version');
|
||||
|
||||
yield waitForPromise(ackDefer.promise, DEFAULT_TIMEOUT,
|
||||
yield waitForPromise(ackPromise, DEFAULT_TIMEOUT,
|
||||
'Timed out waiting for string acknowledgement');
|
||||
|
||||
let storeRecord = yield db.getByKeyID(
|
||||
|
||||
@@ -0,0 +1,268 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
'use strict';
|
||||
|
||||
const {PushDB, PushService, PushServiceWebSocket} = serviceExports;
|
||||
|
||||
const userAgentID = '2c43af06-ab6e-476a-adc4-16cbda54fb89';
|
||||
|
||||
let db;
|
||||
|
||||
function run_test() {
|
||||
do_get_profile();
|
||||
setPrefs({
|
||||
userAgentID,
|
||||
});
|
||||
|
||||
db = PushServiceWebSocket.newPushDB();
|
||||
do_register_cleanup(() => {return db.drop().then(_ => db.close());});
|
||||
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
let unregisterDefers = {};
|
||||
|
||||
function putRecord(channelID, scope, quota) {
|
||||
return db.put({
|
||||
channelID: channelID,
|
||||
pushEndpoint: 'https://example.org/push/' + channelID,
|
||||
scope: scope,
|
||||
pushCount: 0,
|
||||
lastPush: 0,
|
||||
version: null,
|
||||
originAttributes: '',
|
||||
quota: quota,
|
||||
});
|
||||
}
|
||||
|
||||
function makePushPermission(url, capability) {
|
||||
return {
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIPermission]),
|
||||
capability: Ci.nsIPermissionManager[capability],
|
||||
expireTime: 0,
|
||||
expireType: Ci.nsIPermissionManager.EXPIRE_NEVER,
|
||||
principal: Services.scriptSecurityManager.getCodebasePrincipal(
|
||||
Services.io.newURI(url, null, null)
|
||||
),
|
||||
type: 'desktop-notification',
|
||||
};
|
||||
}
|
||||
|
||||
function promiseSubscriptionChanges(count) {
|
||||
let notifiedScopes = [];
|
||||
let subChangePromise = promiseObserverNotification('push-subscription-change', (subject, data) => {
|
||||
notifiedScopes.push(data);
|
||||
return notifiedScopes.length == count;
|
||||
});
|
||||
return subChangePromise.then(_ => notifiedScopes.sort());
|
||||
}
|
||||
|
||||
function allExpired(...keyIDs) {
|
||||
return Promise.all(keyIDs.map(
|
||||
keyID => db.getByKeyID(keyID)
|
||||
)).then(records =>
|
||||
records.every(record => record.isExpired())
|
||||
);
|
||||
}
|
||||
|
||||
add_task(function* setUp() {
|
||||
// Active registration; quota should be reset to 16. Since the quota isn't
|
||||
// exposed to content, we shouldn't receive a subscription change event.
|
||||
yield putRecord('active-allow', 'https://example.info/page/1', 8);
|
||||
|
||||
// Expired registration; should be dropped.
|
||||
yield putRecord('expired-allow', 'https://example.info/page/2', 0);
|
||||
|
||||
// Active registration; should be expired when we change the permission
|
||||
// to "deny".
|
||||
yield putRecord('active-deny-changed', 'https://example.xyz/page/1', 16);
|
||||
|
||||
// Two active registrations for a visited site. These will expire when we
|
||||
// add a "deny" permission.
|
||||
yield putRecord('active-deny-added-1', 'https://example.net/ham', 16);
|
||||
yield putRecord('active-deny-added-2', 'https://example.net/green', 8);
|
||||
|
||||
// An already-expired registration for a visited site. We shouldn't send an
|
||||
// `unregister` request for this one, but still receive an observer
|
||||
// notification when we restore permissions.
|
||||
yield putRecord('expired-deny-added', 'https://example.net/eggs', 0);
|
||||
|
||||
// A registration that should not be affected by permission list changes
|
||||
// because its quota is set to `Infinity`.
|
||||
yield putRecord('never-expires', 'app://chrome/only', Infinity);
|
||||
|
||||
// A registration that should be dropped when we clear the permission
|
||||
// list.
|
||||
yield putRecord('drop-on-clear', 'https://example.edu/lonely', 16);
|
||||
|
||||
let handshakeDone;
|
||||
let handshakePromise = new Promise(resolve => handshakeDone = resolve);
|
||||
PushService.init({
|
||||
serverURI: 'wss://push.example.org/',
|
||||
networkInfo: new MockDesktopNetworkInfo(),
|
||||
db,
|
||||
makeWebSocket(uri) {
|
||||
return new MockWebSocket(uri, {
|
||||
onHello(request) {
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'hello',
|
||||
status: 200,
|
||||
uaid: userAgentID,
|
||||
}));
|
||||
handshakeDone();
|
||||
},
|
||||
onUnregister(request) {
|
||||
let resolve = unregisterDefers[request.channelID];
|
||||
equal(typeof resolve, 'function',
|
||||
'Dropped unexpected channel ID ' + request.channelID);
|
||||
delete unregisterDefers[request.channelID];
|
||||
resolve();
|
||||
},
|
||||
onACK(request) {},
|
||||
});
|
||||
}
|
||||
});
|
||||
yield waitForPromise(handshakePromise, DEFAULT_TIMEOUT,
|
||||
'Timed out waiting for handshake');
|
||||
});
|
||||
|
||||
add_task(function* test_permissions_allow_added() {
|
||||
let subChangePromise = promiseSubscriptionChanges(1);
|
||||
|
||||
yield PushService._onPermissionChange(
|
||||
makePushPermission('https://example.info', 'ALLOW_ACTION'),
|
||||
'added'
|
||||
);
|
||||
let notifiedScopes = yield waitForPromise(subChangePromise, DEFAULT_TIMEOUT,
|
||||
'Timed out waiting for notifications after adding allow');
|
||||
|
||||
deepEqual(notifiedScopes, [
|
||||
'https://example.info/page/2',
|
||||
], 'Wrong scopes after adding allow');
|
||||
|
||||
let record = yield db.getByKeyID('active-allow');
|
||||
equal(record.quota, 16,
|
||||
'Should reset quota for active records after adding allow');
|
||||
|
||||
record = yield db.getByKeyID('expired-allow');
|
||||
ok(!record, 'Should drop expired records after adding allow');
|
||||
});
|
||||
|
||||
add_task(function* test_permissions_allow_deleted() {
|
||||
let unregisterPromise = new Promise(resolve => unregisterDefers[
|
||||
'active-allow'] = resolve);
|
||||
|
||||
yield PushService._onPermissionChange(
|
||||
makePushPermission('https://example.info', 'ALLOW_ACTION'),
|
||||
'deleted'
|
||||
);
|
||||
|
||||
yield waitForPromise(unregisterPromise, DEFAULT_TIMEOUT,
|
||||
'Timed out waiting for unregister after deleting allow');
|
||||
|
||||
let record = yield db.getByKeyID('active-allow');
|
||||
ok(record.isExpired(),
|
||||
'Should expire active record after deleting allow');
|
||||
});
|
||||
|
||||
add_task(function* test_permissions_deny_added() {
|
||||
let unregisterPromise = Promise.all([
|
||||
new Promise(resolve => unregisterDefers[
|
||||
'active-deny-added-1'] = resolve),
|
||||
new Promise(resolve => unregisterDefers[
|
||||
'active-deny-added-2'] = resolve),
|
||||
]);
|
||||
|
||||
yield PushService._onPermissionChange(
|
||||
makePushPermission('https://example.net', 'DENY_ACTION'),
|
||||
'added'
|
||||
);
|
||||
yield waitForPromise(unregisterPromise, DEFAULT_TIMEOUT,
|
||||
'Timed out waiting for notifications after adding deny');
|
||||
|
||||
let isExpired = yield allExpired(
|
||||
'active-deny-added-1',
|
||||
'expired-deny-added'
|
||||
);
|
||||
ok(isExpired, 'Should expire all registrations after adding deny');
|
||||
});
|
||||
|
||||
add_task(function* test_permissions_deny_deleted() {
|
||||
yield PushService._onPermissionChange(
|
||||
makePushPermission('https://example.net', 'DENY_ACTION'),
|
||||
'deleted'
|
||||
);
|
||||
|
||||
let isExpired = yield allExpired(
|
||||
'active-deny-added-1',
|
||||
'expired-deny-added'
|
||||
);
|
||||
ok(isExpired, 'Should retain expired registrations after deleting deny');
|
||||
});
|
||||
|
||||
add_task(function* test_permissions_allow_changed() {
|
||||
let subChangePromise = promiseSubscriptionChanges(3);
|
||||
|
||||
yield PushService._onPermissionChange(
|
||||
makePushPermission('https://example.net', 'ALLOW_ACTION'),
|
||||
'changed'
|
||||
);
|
||||
|
||||
let notifiedScopes = yield waitForPromise(subChangePromise, DEFAULT_TIMEOUT,
|
||||
'Timed out waiting for notifications after changing to allow');
|
||||
|
||||
deepEqual(notifiedScopes, [
|
||||
'https://example.net/eggs',
|
||||
'https://example.net/green',
|
||||
'https://example.net/ham'
|
||||
], 'Wrong scopes after changing to allow');
|
||||
|
||||
let droppedRecords = yield Promise.all([
|
||||
db.getByKeyID('active-deny-added-1'),
|
||||
db.getByKeyID('active-deny-added-2'),
|
||||
db.getByKeyID('expired-deny-added'),
|
||||
]);
|
||||
ok(!droppedRecords.some(Boolean),
|
||||
'Should drop all expired registrations after changing to allow');
|
||||
});
|
||||
|
||||
add_task(function* test_permissions_deny_changed() {
|
||||
let unregisterPromise = new Promise(resolve => unregisterDefers[
|
||||
'active-deny-changed'] = resolve);
|
||||
|
||||
yield PushService._onPermissionChange(
|
||||
makePushPermission('https://example.xyz', 'DENY_ACTION'),
|
||||
'changed'
|
||||
);
|
||||
|
||||
yield waitForPromise(unregisterPromise, DEFAULT_TIMEOUT,
|
||||
'Timed out waiting for unregister after changing to deny');
|
||||
|
||||
let record = yield db.getByKeyID('active-deny-changed');
|
||||
ok(record.isExpired(),
|
||||
'Should expire active record after changing to allow');
|
||||
});
|
||||
|
||||
add_task(function* test_permissions_clear() {
|
||||
let records = yield db.getAllKeyIDs();
|
||||
deepEqual(records.map(record => record.keyID).sort(), [
|
||||
'active-allow',
|
||||
'active-deny-changed',
|
||||
'drop-on-clear',
|
||||
'never-expires',
|
||||
], 'Wrong records in database before clearing');
|
||||
|
||||
let unregisterPromise = new Promise(resolve => unregisterDefers[
|
||||
'drop-on-clear'] = resolve);
|
||||
|
||||
yield PushService._onPermissionChange(null, 'cleared');
|
||||
|
||||
yield waitForPromise(unregisterPromise, DEFAULT_TIMEOUT,
|
||||
'Timed out waiting for unregister requests after clearing permissions');
|
||||
|
||||
records = yield db.getAllKeyIDs();
|
||||
deepEqual(records.map(record => record.keyID).sort(), [
|
||||
'never-expires',
|
||||
], 'Unrestricted registrations should not be dropped');
|
||||
});
|
||||
@@ -85,7 +85,9 @@ add_task(function* test_expiration_origin_threshold() {
|
||||
updates++;
|
||||
return updates == 6;
|
||||
});
|
||||
let unregisterDefer = Promise.defer();
|
||||
|
||||
let unregisterDone;
|
||||
let unregisterPromise = new Promise(resolve => unregisterDone = resolve);
|
||||
|
||||
PushService.init({
|
||||
serverURI: 'wss://push.example.org/',
|
||||
@@ -94,10 +96,6 @@ add_task(function* test_expiration_origin_threshold() {
|
||||
makeWebSocket(uri) {
|
||||
return new MockWebSocket(uri, {
|
||||
onHello(request) {
|
||||
deepEqual(request.channelIDs.sort(), [
|
||||
'46cc6f6a-c106-4ffa-bb7c-55c60bd50c41',
|
||||
'eb33fc90-c883-4267-b5cb-613969e8e349',
|
||||
], 'Wrong active registrations in handshake');
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'hello',
|
||||
status: 200,
|
||||
@@ -127,7 +125,7 @@ add_task(function* test_expiration_origin_threshold() {
|
||||
},
|
||||
onUnregister(request) {
|
||||
equal(request.channelID, 'eb33fc90-c883-4267-b5cb-613969e8e349', 'Unregistered wrong channel ID');
|
||||
unregisterDefer.resolve();
|
||||
unregisterDone();
|
||||
},
|
||||
// We expect to receive acks, but don't care about their
|
||||
// contents.
|
||||
@@ -136,7 +134,7 @@ add_task(function* test_expiration_origin_threshold() {
|
||||
},
|
||||
});
|
||||
|
||||
yield waitForPromise(unregisterDefer.promise, DEFAULT_TIMEOUT,
|
||||
yield waitForPromise(unregisterPromise, DEFAULT_TIMEOUT,
|
||||
'Timed out waiting for unregister request');
|
||||
|
||||
yield waitForPromise(notifyPromise, DEFAULT_TIMEOUT,
|
||||
|
||||
@@ -7,6 +7,8 @@ const {PushDB, PushService, PushServiceWebSocket} = serviceExports;
|
||||
|
||||
const userAgentID = '28cd09e2-7506-42d8-9e50-b02785adc7ef';
|
||||
|
||||
var db;
|
||||
|
||||
function run_test() {
|
||||
do_get_profile();
|
||||
setPrefs({
|
||||
@@ -15,12 +17,24 @@ function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
let putRecord = Task.async(function* (perm, record) {
|
||||
let uri = Services.io.newURI(record.scope, null, null);
|
||||
|
||||
Services.perms.add(uri, 'desktop-notification',
|
||||
Ci.nsIPermissionManager[perm]);
|
||||
do_register_cleanup(() => {
|
||||
Services.perms.remove(uri, 'desktop-notification');
|
||||
});
|
||||
|
||||
yield db.put(record);
|
||||
});
|
||||
|
||||
add_task(function* test_expiration_history_observer() {
|
||||
let db = PushServiceWebSocket.newPushDB();
|
||||
db = PushServiceWebSocket.newPushDB();
|
||||
do_register_cleanup(() => db.drop().then(_ => db.close()));
|
||||
|
||||
// A registration that we'll expire...
|
||||
yield db.put({
|
||||
yield putRecord('ALLOW_ACTION', {
|
||||
channelID: '379c0668-8323-44d2-a315-4ee83f1a9ee9',
|
||||
pushEndpoint: 'https://example.org/push/1',
|
||||
scope: 'https://example.com/deals',
|
||||
@@ -31,11 +45,11 @@ add_task(function* test_expiration_history_observer() {
|
||||
quota: 16,
|
||||
});
|
||||
|
||||
// ...And an expired registration that we'll revive later.
|
||||
yield db.put({
|
||||
channelID: 'eb33fc90-c883-4267-b5cb-613969e8e349',
|
||||
pushEndpoint: 'https://example.org/push/2',
|
||||
scope: 'https://example.com/auctions',
|
||||
// ...And a registration that we'll evict on startup.
|
||||
yield putRecord('ALLOW_ACTION', {
|
||||
channelID: '4cb6e454-37cf-41c4-a013-4e3a7fdd0bf1',
|
||||
pushEndpoint: 'https://example.org/push/3',
|
||||
scope: 'https://example.com/stuff',
|
||||
pushCount: 0,
|
||||
lastPush: 0,
|
||||
version: null,
|
||||
@@ -52,7 +66,10 @@ add_task(function* test_expiration_history_observer() {
|
||||
}],
|
||||
});
|
||||
|
||||
let unregisterDefer = Promise.defer();
|
||||
let unregisterDone;
|
||||
let unregisterPromise = new Promise(resolve => unregisterDone = resolve);
|
||||
let subChangePromise = promiseObserverNotification('push-subscription-change', (subject, data) =>
|
||||
data == 'https://example.com/stuff');
|
||||
|
||||
PushService.init({
|
||||
serverURI: 'wss://push.example.org/',
|
||||
@@ -61,9 +78,6 @@ add_task(function* test_expiration_history_observer() {
|
||||
makeWebSocket(uri) {
|
||||
return new MockWebSocket(uri, {
|
||||
onHello(request) {
|
||||
deepEqual(request.channelIDs, [
|
||||
'379c0668-8323-44d2-a315-4ee83f1a9ee9',
|
||||
], 'Should not include expired channel IDs');
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'hello',
|
||||
status: 200,
|
||||
@@ -79,25 +93,39 @@ add_task(function* test_expiration_history_observer() {
|
||||
},
|
||||
onUnregister(request) {
|
||||
equal(request.channelID, '379c0668-8323-44d2-a315-4ee83f1a9ee9', 'Dropped wrong channel ID');
|
||||
unregisterDefer.resolve();
|
||||
unregisterDone();
|
||||
},
|
||||
onACK(request) {},
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
yield waitForPromise(unregisterDefer.promise, DEFAULT_TIMEOUT,
|
||||
yield waitForPromise(subChangePromise, DEFAULT_TIMEOUT,
|
||||
'Timed out waiting for subscription change event on startup');
|
||||
yield waitForPromise(unregisterPromise, DEFAULT_TIMEOUT,
|
||||
'Timed out waiting for unregister request');
|
||||
|
||||
let expiredRecord = yield db.getByKeyID('379c0668-8323-44d2-a315-4ee83f1a9ee9');
|
||||
strictEqual(expiredRecord.quota, 0, 'Expired record not updated');
|
||||
|
||||
let notifiedScopes = [];
|
||||
let subChangePromise = promiseObserverNotification('push-subscription-change', (subject, data) => {
|
||||
subChangePromise = promiseObserverNotification('push-subscription-change', (subject, data) => {
|
||||
notifiedScopes.push(data);
|
||||
return notifiedScopes.length == 2;
|
||||
});
|
||||
|
||||
// Add an expired registration that we'll revive later.
|
||||
yield putRecord('ALLOW_ACTION', {
|
||||
channelID: 'eb33fc90-c883-4267-b5cb-613969e8e349',
|
||||
pushEndpoint: 'https://example.org/push/2',
|
||||
scope: 'https://example.com/auctions',
|
||||
pushCount: 0,
|
||||
lastPush: 0,
|
||||
version: null,
|
||||
originAttributes: '',
|
||||
quota: 0,
|
||||
});
|
||||
|
||||
// Now visit the site...
|
||||
yield addVisit({
|
||||
uri: 'https://example.com/another-page',
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
'use strict';
|
||||
|
||||
const {PushDB, PushService, PushServiceWebSocket} = serviceExports;
|
||||
|
||||
function run_test() {
|
||||
do_get_profile();
|
||||
setPrefs({
|
||||
requestTimeout: 10000,
|
||||
retryBaseInterval: 150
|
||||
});
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* test_reconnect_retry() {
|
||||
let db = PushServiceWebSocket.newPushDB();
|
||||
do_register_cleanup(() => {return db.drop().then(_ => db.close());});
|
||||
|
||||
let registers = 0;
|
||||
let channelID;
|
||||
PushService.init({
|
||||
serverURI: "wss://push.example.org/",
|
||||
networkInfo: new MockDesktopNetworkInfo(),
|
||||
db,
|
||||
makeWebSocket(uri) {
|
||||
return new MockWebSocket(uri, {
|
||||
onHello(request) {
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'hello',
|
||||
status: 200,
|
||||
uaid: '083e6c17-1063-4677-8638-ab705aebebc2'
|
||||
}));
|
||||
},
|
||||
onRegister(request) {
|
||||
registers++;
|
||||
if (registers == 1) {
|
||||
channelID = request.channelID;
|
||||
this.serverClose();
|
||||
return;
|
||||
}
|
||||
if (registers == 2) {
|
||||
equal(request.channelID, channelID,
|
||||
'Should retry registers after reconnect');
|
||||
}
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'register',
|
||||
channelID: request.channelID,
|
||||
pushEndpoint: 'https://example.org/push/' + request.channelID,
|
||||
status: 200,
|
||||
}));
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
let registration = yield PushNotificationService.register(
|
||||
'https://example.com/page/1',
|
||||
ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false })
|
||||
);
|
||||
let retryEndpoint = 'https://example.org/push/' + channelID;
|
||||
equal(registration.pushEndpoint, retryEndpoint, 'Wrong endpoint for retried request');
|
||||
|
||||
registration = yield PushNotificationService.register(
|
||||
'https://example.com/page/2',
|
||||
ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false })
|
||||
);
|
||||
notEqual(registration.pushEndpoint, retryEndpoint, 'Wrong endpoint for new request')
|
||||
|
||||
equal(registers, 3, 'Wrong registration count');
|
||||
});
|
||||
@@ -79,7 +79,6 @@ add_task(function* test1() {
|
||||
|
||||
PushService.init({
|
||||
serverURI: serverURL + "/subscribe5xxCode",
|
||||
service: PushServiceHttp2,
|
||||
db
|
||||
});
|
||||
|
||||
@@ -91,15 +90,11 @@ add_task(function* test1() {
|
||||
var subscriptionUri = serverURL + '/subscription';
|
||||
var pushEndpoint = serverURL + '/pushEndpoint';
|
||||
var pushReceiptEndpoint = serverURL + '/receiptPushEndpoint';
|
||||
equal(newRecord.subscriptionUri, subscriptionUri,
|
||||
'Wrong subscription ID in registration record');
|
||||
equal(newRecord.pushEndpoint, pushEndpoint,
|
||||
'Wrong push endpoint in registration record');
|
||||
|
||||
equal(newRecord.pushReceiptEndpoint, pushReceiptEndpoint,
|
||||
'Wrong push endpoint receipt in registration record');
|
||||
equal(newRecord.scope, 'https://example.com/retry5xxCode',
|
||||
'Wrong scope in registration record');
|
||||
|
||||
let record = yield db.getByKeyID(subscriptionUri);
|
||||
equal(record.subscriptionUri, subscriptionUri,
|
||||
|
||||
@@ -54,12 +54,8 @@ add_task(function* test_register_case() {
|
||||
);
|
||||
equal(newRecord.pushEndpoint, 'https://example.com/update/case',
|
||||
'Wrong push endpoint in registration record');
|
||||
equal(newRecord.scope, 'https://example.net/case',
|
||||
'Wrong scope in registration record');
|
||||
|
||||
let record = yield db.getByKeyID(newRecord.channelID);
|
||||
equal(record.pushEndpoint, 'https://example.com/update/case',
|
||||
'Wrong push endpoint in database record');
|
||||
let record = yield db.getByPushEndpoint('https://example.com/update/case');
|
||||
equal(record.scope, 'https://example.net/case',
|
||||
'Wrong scope in database record');
|
||||
});
|
||||
|
||||
@@ -49,10 +49,7 @@ add_task(function* test_pushSubscriptionNoConnection() {
|
||||
PushNotificationService.register(
|
||||
'https://example.net/page/invalid-response',
|
||||
ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false })),
|
||||
function(error) {
|
||||
return error && error.includes("Error");
|
||||
},
|
||||
'Wrong error for not being able to establish connecion.'
|
||||
'Expected error for not being able to establish connecion.'
|
||||
);
|
||||
|
||||
let record = yield db.getAllKeyIDs();
|
||||
@@ -90,10 +87,7 @@ add_task(function* test_pushSubscriptionMissingLocation() {
|
||||
PushNotificationService.register(
|
||||
'https://example.net/page/invalid-response',
|
||||
ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false })),
|
||||
function(error) {
|
||||
return error && error.includes("Return code 201, but the answer is bogus");
|
||||
},
|
||||
'Wrong error for the missing location header.'
|
||||
'Expected error for the missing location header.'
|
||||
);
|
||||
|
||||
let record = yield db.getAllKeyIDs();
|
||||
@@ -117,10 +111,7 @@ add_task(function* test_pushSubscriptionMissingLink() {
|
||||
PushNotificationService.register(
|
||||
'https://example.net/page/invalid-response',
|
||||
ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false })),
|
||||
function(error) {
|
||||
return error && error.includes("Return code 201, but the answer is bogus");
|
||||
},
|
||||
'Wrong error for the missing link header.'
|
||||
'Expected error for the missing link header.'
|
||||
);
|
||||
|
||||
let record = yield db.getAllKeyIDs();
|
||||
@@ -144,10 +135,7 @@ add_task(function* test_pushSubscriptionMissingLink1() {
|
||||
PushNotificationService.register(
|
||||
'https://example.net/page/invalid-response',
|
||||
ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false })),
|
||||
function(error) {
|
||||
return error && error.includes("Return code 201, but the answer is bogus");
|
||||
},
|
||||
'Wrong error for the missing push endpoint.'
|
||||
'Expected error for the missing push endpoint.'
|
||||
);
|
||||
|
||||
let record = yield db.getAllKeyIDs();
|
||||
@@ -171,10 +159,7 @@ add_task(function* test_pushSubscriptionLocationBogus() {
|
||||
PushNotificationService.register(
|
||||
'https://example.net/page/invalid-response',
|
||||
ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false })),
|
||||
function(error) {
|
||||
return error && error.includes("Return code 201, but URI is bogus.");
|
||||
},
|
||||
'Wrong error for the bogus location'
|
||||
'Expected error for the bogus location'
|
||||
);
|
||||
|
||||
let record = yield db.getAllKeyIDs();
|
||||
@@ -198,10 +183,7 @@ add_task(function* test_pushSubscriptionNot2xxCode() {
|
||||
PushNotificationService.register(
|
||||
'https://example.net/page/invalid-response',
|
||||
ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false })),
|
||||
function(error) {
|
||||
return error && error.includes("Error");
|
||||
},
|
||||
'Wrong error for not 201 responce code.'
|
||||
'Expected error for not 201 responce code.'
|
||||
);
|
||||
|
||||
let record = yield db.getAllKeyIDs();
|
||||
|
||||
@@ -37,8 +37,8 @@ add_task(function* test_register_flush() {
|
||||
|
||||
let notifyPromise = promiseObserverNotification('push-notification');
|
||||
|
||||
let ackDefer = Promise.defer();
|
||||
let ackDone = after(2, ackDefer.resolve);
|
||||
let ackDone;
|
||||
let ackPromise = new Promise(resolve => ackDone = after(2, resolve));
|
||||
PushService.init({
|
||||
serverURI: "wss://push.example.org/",
|
||||
networkInfo: new MockDesktopNetworkInfo(),
|
||||
@@ -80,14 +80,12 @@ add_task(function* test_register_flush() {
|
||||
'https://example.com/page/2', '');
|
||||
equal(newRecord.pushEndpoint, 'https://example.org/update/2',
|
||||
'Wrong push endpoint in record');
|
||||
equal(newRecord.scope, 'https://example.com/page/2',
|
||||
'Wrong scope in record');
|
||||
|
||||
let {data: scope} = yield waitForPromise(notifyPromise, DEFAULT_TIMEOUT,
|
||||
'Timed out waiting for notification');
|
||||
equal(scope, 'https://example.com/page/1', 'Wrong notification scope');
|
||||
|
||||
yield waitForPromise(ackDefer.promise, DEFAULT_TIMEOUT,
|
||||
yield waitForPromise(ackPromise, DEFAULT_TIMEOUT,
|
||||
'Timed out waiting for acknowledgements');
|
||||
|
||||
let prevRecord = yield db.getByKeyID(
|
||||
@@ -97,8 +95,6 @@ add_task(function* test_register_flush() {
|
||||
strictEqual(prevRecord.version, 3,
|
||||
'Should record version updates sent before register responses');
|
||||
|
||||
let registeredRecord = yield db.getByKeyID(newRecord.channelID);
|
||||
equal(registeredRecord.pushEndpoint, 'https://example.org/update/2',
|
||||
'Wrong new push endpoint');
|
||||
let registeredRecord = yield db.getByPushEndpoint('https://example.org/update/2');
|
||||
ok(!registeredRecord.version, 'Should not record premature updates');
|
||||
});
|
||||
|
||||
@@ -50,10 +50,7 @@ add_task(function* test_register_invalid_channel() {
|
||||
yield rejects(
|
||||
PushNotificationService.register('https://example.com/invalid-channel',
|
||||
ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false })),
|
||||
function(error) {
|
||||
return error == 'Invalid channel ID';
|
||||
},
|
||||
'Wrong error for invalid channel ID'
|
||||
'Expected error for invalid channel ID'
|
||||
);
|
||||
|
||||
let record = yield db.getByKeyID(channelID);
|
||||
|
||||
@@ -52,10 +52,7 @@ add_task(function* test_register_invalid_endpoint() {
|
||||
PushNotificationService.register(
|
||||
'https://example.net/page/invalid-endpoint',
|
||||
ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false })),
|
||||
function(error) {
|
||||
return error && error.includes('Invalid pushEndpoint');
|
||||
},
|
||||
'Wrong error for invalid endpoint'
|
||||
'Expected error for invalid endpoint'
|
||||
);
|
||||
|
||||
let record = yield db.getByKeyID(channelID);
|
||||
|
||||
@@ -21,8 +21,8 @@ function run_test() {
|
||||
}
|
||||
|
||||
add_task(function* test_register_invalid_json() {
|
||||
let helloDefer = Promise.defer();
|
||||
let helloDone = after(2, helloDefer.resolve);
|
||||
let helloDone;
|
||||
let helloPromise = new Promise(resolve => helloDone = after(2, resolve));
|
||||
let registers = 0;
|
||||
|
||||
PushServiceWebSocket._generateID = () => channelID;
|
||||
@@ -51,13 +51,10 @@ add_task(function* test_register_invalid_json() {
|
||||
yield rejects(
|
||||
PushNotificationService.register('https://example.net/page/invalid-json',
|
||||
ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false })),
|
||||
function(error) {
|
||||
return error == 'TimeoutError';
|
||||
},
|
||||
'Wrong error for invalid JSON response'
|
||||
'Expected error for invalid JSON response'
|
||||
);
|
||||
|
||||
yield waitForPromise(helloDefer.promise, DEFAULT_TIMEOUT,
|
||||
yield waitForPromise(helloPromise, DEFAULT_TIMEOUT,
|
||||
'Reconnect after invalid JSON response timed out');
|
||||
equal(registers, 1, 'Wrong register count');
|
||||
});
|
||||
|
||||
@@ -23,8 +23,8 @@ function run_test() {
|
||||
|
||||
add_task(function* test_register_no_id() {
|
||||
let registers = 0;
|
||||
let helloDefer = Promise.defer();
|
||||
let helloDone = after(2, helloDefer.resolve);
|
||||
let helloDone;
|
||||
let helloPromise = new Promise(resolve => helloDone = after(2, resolve));
|
||||
|
||||
PushServiceWebSocket._generateID = () => channelID;
|
||||
PushService.init({
|
||||
@@ -55,13 +55,10 @@ add_task(function* test_register_no_id() {
|
||||
yield rejects(
|
||||
PushNotificationService.register('https://example.com/incomplete',
|
||||
ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false })),
|
||||
function(error) {
|
||||
return error == 'TimeoutError';
|
||||
},
|
||||
'Wrong error for incomplete register response'
|
||||
'Expected error for incomplete register response'
|
||||
);
|
||||
|
||||
yield waitForPromise(helloDefer.promise, DEFAULT_TIMEOUT,
|
||||
yield waitForPromise(helloPromise, DEFAULT_TIMEOUT,
|
||||
'Reconnect after incomplete register response timed out');
|
||||
equal(registers, 1, 'Wrong register count');
|
||||
});
|
||||
|
||||
@@ -21,15 +21,16 @@ add_task(function* test_register_request_queue() {
|
||||
let db = PushServiceWebSocket.newPushDB();
|
||||
do_register_cleanup(() => {return db.drop().then(_ => db.close());});
|
||||
|
||||
let helloDefer = Promise.defer();
|
||||
let onHello = after(2, function onHello(request) {
|
||||
let onHello;
|
||||
let helloPromise = new Promise(resolve => onHello = after(2, function onHello(request) {
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'hello',
|
||||
status: 200,
|
||||
uaid: '54b08a9e-59c6-4ed7-bb54-f4fd60d6f606'
|
||||
}));
|
||||
helloDefer.resolve();
|
||||
});
|
||||
resolve();
|
||||
}));
|
||||
|
||||
PushService.init({
|
||||
serverURI: "wss://push.example.org/",
|
||||
networkInfo: new MockDesktopNetworkInfo(),
|
||||
@@ -54,14 +55,10 @@ add_task(function* test_register_request_queue() {
|
||||
);
|
||||
|
||||
yield waitForPromise(Promise.all([
|
||||
rejects(firstRegister, function(error) {
|
||||
return error == 'TimeoutError';
|
||||
}, 'Should time out the first request'),
|
||||
rejects(secondRegister, function(error) {
|
||||
return error == 'TimeoutError';
|
||||
}, 'Should time out the second request')
|
||||
rejects(firstRegister, 'Should time out the first request'),
|
||||
rejects(secondRegister, 'Should time out the second request')
|
||||
]), DEFAULT_TIMEOUT, 'Queued requests did not time out');
|
||||
|
||||
yield waitForPromise(helloDefer.promise, DEFAULT_TIMEOUT,
|
||||
yield waitForPromise(helloPromise, DEFAULT_TIMEOUT,
|
||||
'Timed out waiting for reconnect');
|
||||
});
|
||||
|
||||
@@ -27,7 +27,8 @@ add_task(function* test_register_rollback() {
|
||||
|
||||
let handshakes = 0;
|
||||
let registers = 0;
|
||||
let unregisterDefer = Promise.defer();
|
||||
let unregisterDone;
|
||||
let unregisterPromise = new Promise(resolve => unregisterDone = resolve);
|
||||
PushServiceWebSocket._generateID = () => channelID;
|
||||
PushService.init({
|
||||
serverURI: "wss://push.example.org/",
|
||||
@@ -66,7 +67,7 @@ add_task(function* test_register_rollback() {
|
||||
status: 200,
|
||||
channelID
|
||||
}));
|
||||
unregisterDefer.resolve();
|
||||
unregisterDone();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -76,14 +77,11 @@ add_task(function* test_register_rollback() {
|
||||
yield rejects(
|
||||
PushNotificationService.register('https://example.com/storage-error',
|
||||
ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false })),
|
||||
function(error) {
|
||||
return error == 'universe has imploded';
|
||||
},
|
||||
'Wrong error for unregister database failure'
|
||||
'Expected error for unregister database failure'
|
||||
);
|
||||
|
||||
// Should send an out-of-band unregister request.
|
||||
yield waitForPromise(unregisterDefer.promise, DEFAULT_TIMEOUT,
|
||||
yield waitForPromise(unregisterPromise, DEFAULT_TIMEOUT,
|
||||
'Unregister request timed out');
|
||||
equal(handshakes, 1, 'Wrong handshake count');
|
||||
equal(registers, 1, 'Wrong register count');
|
||||
|
||||
@@ -60,22 +60,14 @@ add_task(function* test_register_success() {
|
||||
'https://example.org/1',
|
||||
ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false })
|
||||
);
|
||||
equal(newRecord.channelID, channelID,
|
||||
'Wrong channel ID in registration record');
|
||||
equal(newRecord.pushEndpoint, 'https://example.com/update/1',
|
||||
'Wrong push endpoint in registration record');
|
||||
equal(newRecord.scope, 'https://example.org/1',
|
||||
'Wrong scope in registration record');
|
||||
equal(newRecord.quota, Infinity,
|
||||
'Wrong quota in registration record');
|
||||
|
||||
let record = yield db.getByKeyID(channelID);
|
||||
equal(record.channelID, channelID,
|
||||
'Wrong channel ID in database record');
|
||||
equal(record.pushEndpoint, 'https://example.com/update/1',
|
||||
'Wrong push endpoint in database record');
|
||||
equal(record.scope, 'https://example.org/1',
|
||||
'Wrong scope in database record');
|
||||
equal(record.quota, Infinity,
|
||||
'Wrong quota in database record');
|
||||
});
|
||||
|
||||
@@ -64,15 +64,11 @@ add_task(function* test_pushSubscriptionSuccess() {
|
||||
var subscriptionUri = serverURL + '/pushSubscriptionSuccesss';
|
||||
var pushEndpoint = serverURL + '/pushEndpointSuccess';
|
||||
var pushReceiptEndpoint = serverURL + '/receiptPushEndpointSuccess';
|
||||
equal(newRecord.subscriptionUri, subscriptionUri,
|
||||
'Wrong subscription ID in registration record');
|
||||
equal(newRecord.pushEndpoint, pushEndpoint,
|
||||
'Wrong push endpoint in registration record');
|
||||
|
||||
equal(newRecord.pushReceiptEndpoint, pushReceiptEndpoint,
|
||||
'Wrong push endpoint receipt in registration record');
|
||||
equal(newRecord.scope, 'https://example.org/1',
|
||||
'Wrong scope in registration record');
|
||||
|
||||
let record = yield db.getByKeyID(subscriptionUri);
|
||||
equal(record.subscriptionUri, subscriptionUri,
|
||||
@@ -107,15 +103,11 @@ add_task(function* test_pushSubscriptionMissingLink2() {
|
||||
var subscriptionUri = serverURL + '/subscriptionMissingLink2';
|
||||
var pushEndpoint = serverURL + '/pushEndpointMissingLink2';
|
||||
var pushReceiptEndpoint = '';
|
||||
equal(newRecord.subscriptionUri, subscriptionUri,
|
||||
'Wrong subscription ID in registration record');
|
||||
equal(newRecord.pushEndpoint, pushEndpoint,
|
||||
'Wrong push endpoint in registration record');
|
||||
|
||||
equal(newRecord.pushReceiptEndpoint, pushReceiptEndpoint,
|
||||
'Wrong push endpoint receipt in registration record');
|
||||
equal(newRecord.scope, 'https://example.org/no_receiptEndpoint',
|
||||
'Wrong scope in registration record');
|
||||
|
||||
let record = yield db.getByKeyID(subscriptionUri);
|
||||
equal(record.subscriptionUri, subscriptionUri,
|
||||
|
||||
@@ -22,7 +22,8 @@ function run_test() {
|
||||
|
||||
add_task(function* test_register_timeout() {
|
||||
let handshakes = 0;
|
||||
let timeoutDefer = Promise.defer();
|
||||
let timeoutDone;
|
||||
let timeoutPromise = new Promise(resolve => timeoutDone = resolve);
|
||||
let registers = 0;
|
||||
|
||||
let db = PushServiceWebSocket.newPushDB();
|
||||
@@ -36,23 +37,14 @@ add_task(function* test_register_timeout() {
|
||||
makeWebSocket(uri) {
|
||||
return new MockWebSocket(uri, {
|
||||
onHello(request) {
|
||||
switch (handshakes) {
|
||||
case 0:
|
||||
if (handshakes === 0) {
|
||||
equal(request.uaid, null, 'Should not include device ID');
|
||||
deepEqual(request.channelIDs, [],
|
||||
'Should include empty channel list');
|
||||
break;
|
||||
|
||||
case 1:
|
||||
} else if (handshakes === 1) {
|
||||
// Should use the previously-issued device ID when reconnecting,
|
||||
// but should not include the timed-out channel ID.
|
||||
equal(request.uaid, userAgentID,
|
||||
'Should include device ID on reconnect');
|
||||
deepEqual(request.channelIDs, [],
|
||||
'Should not include failed channel ID');
|
||||
break;
|
||||
|
||||
default:
|
||||
} else {
|
||||
ok(false, 'Unexpected reconnect attempt ' + handshakes);
|
||||
}
|
||||
handshakes++;
|
||||
@@ -74,7 +66,7 @@ add_task(function* test_register_timeout() {
|
||||
uaid: userAgentID,
|
||||
pushEndpoint: 'https://example.com/update/timeout',
|
||||
}));
|
||||
timeoutDefer.resolve();
|
||||
timeoutDone();
|
||||
}, 2000);
|
||||
registers++;
|
||||
}
|
||||
@@ -85,17 +77,14 @@ add_task(function* test_register_timeout() {
|
||||
yield rejects(
|
||||
PushNotificationService.register('https://example.net/page/timeout',
|
||||
ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false })),
|
||||
function(error) {
|
||||
return error == 'TimeoutError';
|
||||
},
|
||||
'Wrong error for request timeout'
|
||||
'Expected error for request timeout'
|
||||
);
|
||||
|
||||
let record = yield db.getByKeyID(channelID);
|
||||
ok(!record, 'Should not store records for timed-out responses');
|
||||
|
||||
yield waitForPromise(
|
||||
timeoutDefer.promise,
|
||||
timeoutPromise,
|
||||
DEFAULT_TIMEOUT,
|
||||
'Reconnect timed out'
|
||||
);
|
||||
|
||||
@@ -25,8 +25,8 @@ function run_test() {
|
||||
add_task(function* test_register_wrong_id() {
|
||||
// Should reconnect after the register request times out.
|
||||
let registers = 0;
|
||||
let helloDefer = Promise.defer();
|
||||
let helloDone = after(2, helloDefer.resolve);
|
||||
let helloDone;
|
||||
let helloPromise = new Promise(resolve => helloDone = after(2, resolve));
|
||||
|
||||
PushServiceWebSocket._generateID = () => clientChannelID;
|
||||
PushService.init({
|
||||
@@ -61,13 +61,10 @@ add_task(function* test_register_wrong_id() {
|
||||
yield rejects(
|
||||
PushNotificationService.register('https://example.com/mismatched',
|
||||
ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false })),
|
||||
function(error) {
|
||||
return error == 'TimeoutError';
|
||||
},
|
||||
'Wrong error for mismatched register reply'
|
||||
'Expected error for mismatched register reply'
|
||||
);
|
||||
|
||||
yield waitForPromise(helloDefer.promise, DEFAULT_TIMEOUT,
|
||||
yield waitForPromise(helloPromise, DEFAULT_TIMEOUT,
|
||||
'Reconnect after mismatched register reply timed out');
|
||||
equal(registers, 1, 'Wrong register count');
|
||||
});
|
||||
|
||||
@@ -21,8 +21,8 @@ function run_test() {
|
||||
|
||||
add_task(function* test_register_wrong_type() {
|
||||
let registers = 0;
|
||||
let helloDefer = Promise.defer();
|
||||
let helloDone = after(2, helloDefer.resolve);
|
||||
let helloDone;
|
||||
let helloPromise = new Promise(resolve => helloDone = after(2, resolve));
|
||||
|
||||
PushService._generateID = () => '1234';
|
||||
PushService.init({
|
||||
@@ -52,18 +52,13 @@ add_task(function* test_register_wrong_type() {
|
||||
}
|
||||
});
|
||||
|
||||
let promise =
|
||||
|
||||
yield rejects(
|
||||
PushNotificationService.register('https://example.com/mistyped',
|
||||
ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false })),
|
||||
function(error) {
|
||||
return error == 'TimeoutError';
|
||||
},
|
||||
'Wrong error for non-string channel ID'
|
||||
'Expected error for non-string channel ID'
|
||||
);
|
||||
|
||||
yield waitForPromise(helloDefer.promise, DEFAULT_TIMEOUT,
|
||||
yield waitForPromise(helloPromise, DEFAULT_TIMEOUT,
|
||||
'Reconnect after sending non-string channel ID timed out');
|
||||
equal(registers, 1, 'Wrong register count');
|
||||
});
|
||||
|
||||
@@ -21,9 +21,6 @@ add_task(function* test_registration_missing_scope() {
|
||||
});
|
||||
yield rejects(
|
||||
PushNotificationService.registration('', ''),
|
||||
function(error) {
|
||||
return error.error == 'NotFoundError';
|
||||
},
|
||||
'Record missing page and manifest URLs'
|
||||
);
|
||||
});
|
||||
|
||||
@@ -42,7 +42,8 @@ add_task(function* test_registration_success() {
|
||||
yield db.put(record);
|
||||
}
|
||||
|
||||
let handshakeDefer = Promise.defer();
|
||||
let handshakeDone;
|
||||
let handshakePromise = new Promise(resolve => handshakeDone = resolve);
|
||||
PushService.init({
|
||||
serverURI: "wss://push.example.org/",
|
||||
networkInfo: new MockDesktopNetworkInfo(),
|
||||
@@ -50,24 +51,19 @@ add_task(function* test_registration_success() {
|
||||
return new MockWebSocket(uri, {
|
||||
onHello(request) {
|
||||
equal(request.uaid, userAgentID, 'Wrong device ID in handshake');
|
||||
deepEqual(request.channelIDs.sort(), [
|
||||
'b1cf38c9-6836-4d29-8a30-a3e98d59b728',
|
||||
'bf001fe0-2684-42f2-bc4d-a3e14b11dd5b',
|
||||
'f6edfbcd-79d6-49b8-9766-48b9dcfeff0f',
|
||||
], 'Wrong channel list in handshake');
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'hello',
|
||||
status: 200,
|
||||
uaid: userAgentID
|
||||
}));
|
||||
handshakeDefer.resolve();
|
||||
handshakeDone();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
yield waitForPromise(
|
||||
handshakeDefer.promise,
|
||||
handshakePromise,
|
||||
DEFAULT_TIMEOUT,
|
||||
'Timed out waiting for handshake'
|
||||
);
|
||||
|
||||
@@ -83,7 +83,6 @@ add_task(function* test1() {
|
||||
|
||||
PushService.init({
|
||||
serverURI: serverURL + "/subscribe",
|
||||
service: PushServiceHttp2,
|
||||
db
|
||||
});
|
||||
|
||||
|
||||
@@ -93,7 +93,6 @@ add_task(function* test1() {
|
||||
|
||||
PushService.init({
|
||||
serverURI: serverURL + "/subscribe",
|
||||
service: PushServiceHttp2,
|
||||
db
|
||||
});
|
||||
|
||||
|
||||
@@ -88,7 +88,6 @@ add_task(function* test1() {
|
||||
|
||||
PushService.init({
|
||||
serverURI: serverURL + "/subscribe",
|
||||
service: PushServiceHttp2,
|
||||
db
|
||||
});
|
||||
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
'use strict';
|
||||
|
||||
const {PushDB, PushService, PushServiceWebSocket} = serviceExports;
|
||||
|
||||
const userAgentID = '05f7b940-51b6-4b6f-8032-b83ebb577ded';
|
||||
|
||||
function run_test() {
|
||||
do_get_profile();
|
||||
setPrefs({
|
||||
userAgentID: userAgentID,
|
||||
pingInterval: 10000,
|
||||
retryBaseInterval: 25,
|
||||
});
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* test_ws_retry() {
|
||||
let db = PushServiceWebSocket.newPushDB();
|
||||
do_register_cleanup(() => {return db.drop().then(_ => db.close());});
|
||||
|
||||
yield db.put({
|
||||
channelID: '61770ba9-2d57-4134-b949-d40404630d5b',
|
||||
pushEndpoint: 'https://example.org/push/1',
|
||||
scope: 'https://example.net/push/1',
|
||||
version: 1,
|
||||
originAttributes: '',
|
||||
quota: Infinity,
|
||||
});
|
||||
|
||||
let alarmDelays = [];
|
||||
let setAlarm = PushService.setAlarm;
|
||||
PushService.setAlarm = function(delay) {
|
||||
alarmDelays.push(delay);
|
||||
setAlarm.apply(this, arguments);
|
||||
};
|
||||
|
||||
let handshakeDone;
|
||||
let handshakePromise = new Promise(resolve => handshakeDone = resolve);
|
||||
PushService.init({
|
||||
serverURI: "wss://push.example.org/",
|
||||
networkInfo: new MockDesktopNetworkInfo(),
|
||||
makeWebSocket(uri) {
|
||||
return new MockWebSocket(uri, {
|
||||
onHello(request) {
|
||||
if (alarmDelays.length == 10) {
|
||||
PushService.setAlarm = setAlarm;
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'hello',
|
||||
status: 200,
|
||||
uaid: userAgentID,
|
||||
}));
|
||||
handshakeDone();
|
||||
return;
|
||||
}
|
||||
this.serverInterrupt();
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
yield waitForPromise(
|
||||
handshakePromise,
|
||||
45000,
|
||||
'Timed out waiting for successful handshake'
|
||||
);
|
||||
deepEqual(alarmDelays, [25, 50, 100, 200, 400, 800, 1600, 3200, 6400, 10000],
|
||||
'Wrong reconnect alarm delays');
|
||||
});
|
||||
@@ -31,9 +31,6 @@ add_task(function* test_unregister_empty_scope() {
|
||||
yield rejects(
|
||||
PushNotificationService.unregister('',
|
||||
ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false })),
|
||||
function(error) {
|
||||
return error.error == 'NotFoundError';
|
||||
},
|
||||
'Wrong error for empty endpoint'
|
||||
'Expected error for empty endpoint'
|
||||
);
|
||||
});
|
||||
|
||||
@@ -25,7 +25,8 @@ add_task(function* test_unregister_error() {
|
||||
quota: Infinity,
|
||||
});
|
||||
|
||||
let unregisterDefer = Promise.defer();
|
||||
let unregisterDone;
|
||||
let unregisterPromise = new Promise(resolve => unregisterDone = resolve);
|
||||
PushService.init({
|
||||
serverURI: "wss://push.example.org/",
|
||||
networkInfo: new MockDesktopNetworkInfo(),
|
||||
@@ -49,7 +50,7 @@ add_task(function* test_unregister_error() {
|
||||
error: 'omg, everything is exploding',
|
||||
channelID
|
||||
}));
|
||||
unregisterDefer.resolve();
|
||||
unregisterDone();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -62,6 +63,6 @@ add_task(function* test_unregister_error() {
|
||||
ok(!result, 'Deleted push record exists');
|
||||
|
||||
// Make sure we send a request to the server.
|
||||
yield waitForPromise(unregisterDefer.promise, DEFAULT_TIMEOUT,
|
||||
yield waitForPromise(unregisterPromise, DEFAULT_TIMEOUT,
|
||||
'Timed out waiting for unregister');
|
||||
});
|
||||
|
||||
@@ -39,8 +39,8 @@ add_task(function* test_unregister_invalid_json() {
|
||||
yield db.put(record);
|
||||
}
|
||||
|
||||
let unregisterDefer = Promise.defer();
|
||||
let unregisterDone = after(2, unregisterDefer.resolve);
|
||||
let unregisterDone;
|
||||
let unregisterPromise = new Promise(resolve => unregisterDone = after(2, resolve));
|
||||
PushService.init({
|
||||
serverURI: "wss://push.example.org/",
|
||||
networkInfo: new MockDesktopNetworkInfo(),
|
||||
@@ -77,6 +77,6 @@ add_task(function* test_unregister_invalid_json() {
|
||||
ok(!record,
|
||||
'Failed to delete unregistered record after receiving invalid JSON');
|
||||
|
||||
yield waitForPromise(unregisterDefer.promise, DEFAULT_TIMEOUT,
|
||||
yield waitForPromise(unregisterPromise, DEFAULT_TIMEOUT,
|
||||
'Timed out waiting for unregister');
|
||||
});
|
||||
|
||||
@@ -25,7 +25,8 @@ add_task(function* test_unregister_success() {
|
||||
quota: Infinity,
|
||||
});
|
||||
|
||||
let unregisterDefer = Promise.defer();
|
||||
let unregisterDone;
|
||||
let unregisterPromise = new Promise(resolve => unregisterDone = resolve);
|
||||
PushService.init({
|
||||
serverURI: "wss://push.example.org/",
|
||||
networkInfo: new MockDesktopNetworkInfo(),
|
||||
@@ -46,7 +47,7 @@ add_task(function* test_unregister_success() {
|
||||
status: 200,
|
||||
channelID
|
||||
}));
|
||||
unregisterDefer.resolve();
|
||||
unregisterDone();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -57,6 +58,6 @@ add_task(function* test_unregister_success() {
|
||||
let record = yield db.getByKeyID(channelID);
|
||||
ok(!record, 'Unregister did not remove record');
|
||||
|
||||
yield waitForPromise(unregisterDefer.promise, DEFAULT_TIMEOUT,
|
||||
yield waitForPromise(unregisterPromise, DEFAULT_TIMEOUT,
|
||||
'Timed out waiting for unregister');
|
||||
});
|
||||
|
||||
@@ -66,7 +66,6 @@ add_task(function* test1() {
|
||||
|
||||
PushService.init({
|
||||
serverURI: serverURL + "/subscribe",
|
||||
service: PushServiceHttp2,
|
||||
db
|
||||
});
|
||||
|
||||
|
||||
@@ -77,8 +77,7 @@ add_task(function* test_with_data_enabled() {
|
||||
'https://example.com/page/3',
|
||||
ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false })
|
||||
);
|
||||
ok(newRecord.p256dhPublicKey, 'Should generate public keys for new records');
|
||||
ok(newRecord.p256dhPrivateKey, 'Should generate private keys for new records');
|
||||
ok(newRecord.p256dhKey, 'Should generate public keys for new records');
|
||||
|
||||
let record = yield db.getByKeyID('eb18f12a-cc42-4f14-accb-3bfc1227f1aa');
|
||||
ok(record.p256dhPublicKey, 'Should add public key to partial record');
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
'use strict';
|
||||
|
||||
const {PushDB, PushService, PushServiceWebSocket} = serviceExports;
|
||||
|
||||
const userAgentID = 'bd744428-f125-436a-b6d0-dd0c9845837f';
|
||||
const channelIDs = ['0ef2ad4a-6c49-41ad-af6e-95d2425276bf', '4818b54a-97c5-4277-ad5d-0bfe630e4e50'];
|
||||
var channelIDCounter = 0;
|
||||
|
||||
function run_test() {
|
||||
do_get_profile();
|
||||
setPrefs({
|
||||
userAgentID,
|
||||
requestTimeout: 1000,
|
||||
retryBaseInterval: 150
|
||||
});
|
||||
disableServiceWorkerEvents(
|
||||
'https://example.org/1'
|
||||
);
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* test_webapps_cleardata() {
|
||||
let db = PushServiceWebSocket.newPushDB();
|
||||
do_register_cleanup(() => {return db.drop().then(_ => db.close());});
|
||||
|
||||
PushService.init({
|
||||
serverURI: "wss://push.example.org",
|
||||
networkInfo: new MockDesktopNetworkInfo(),
|
||||
db,
|
||||
makeWebSocket(uri) {
|
||||
return new MockWebSocket(uri, {
|
||||
onHello(data) {
|
||||
equal(data.messageType, 'hello', 'Handshake: wrong message type');
|
||||
equal(data.uaid, userAgentID, 'Handshake: wrong device ID');
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'hello',
|
||||
status: 200,
|
||||
uaid: userAgentID
|
||||
}));
|
||||
},
|
||||
onRegister(data) {
|
||||
equal(data.messageType, 'register', 'Register: wrong message type');
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'register',
|
||||
status: 200,
|
||||
channelID: data.channelID,
|
||||
uaid: userAgentID,
|
||||
pushEndpoint: 'https://example.com/update/' + Math.random(),
|
||||
}));
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
let registers = yield Promise.all([
|
||||
PushNotificationService.register(
|
||||
'https://example.org/1',
|
||||
ChromeUtils.originAttributesToSuffix({ appId: 1, inBrowser: false })),
|
||||
PushNotificationService.register(
|
||||
'https://example.org/1',
|
||||
ChromeUtils.originAttributesToSuffix({ appId: 1, inBrowser: true })),
|
||||
]);
|
||||
|
||||
Services.obs.notifyObservers(
|
||||
{ appId: 1, browserOnly: false,
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.mozIApplicationClearPrivateDataParams])},
|
||||
"webapps-clear-data", "");
|
||||
|
||||
let waitAWhile = new Promise(function(res) {
|
||||
setTimeout(res, 2000);
|
||||
});
|
||||
yield waitAWhile;
|
||||
|
||||
let registration;
|
||||
registration = yield PushNotificationService.registration(
|
||||
'https://example.org/1',
|
||||
ChromeUtils.originAttributesToSuffix({ appId: 1, inBrowser: false }));
|
||||
ok(!registration, 'Registration for { 1, false } should not exist.');
|
||||
|
||||
registration = yield PushNotificationService.registration(
|
||||
'https://example.org/1',
|
||||
ChromeUtils.originAttributesToSuffix({ appId: 1, inBrowser: true }));
|
||||
ok(registration, 'Registration for { 1, true } should still exist.');
|
||||
});
|
||||
|
||||
@@ -4,11 +4,18 @@ tail =
|
||||
# Push notifications and alarms are currently disabled on Android.
|
||||
skip-if = toolkit == 'android'
|
||||
|
||||
[test_clear_origin_data.js]
|
||||
[test_drop_expired.js]
|
||||
[test_notification_ack.js]
|
||||
[test_notification_data.js]
|
||||
[test_notification_duplicate.js]
|
||||
[test_notification_error.js]
|
||||
[test_notification_incomplete.js]
|
||||
[test_notification_version_string.js]
|
||||
|
||||
[test_permissions.js]
|
||||
run-sequentially = This will delete all existing push subscriptions.
|
||||
|
||||
[test_quota_exceeded.js]
|
||||
[test_quota_observer.js]
|
||||
[test_register_case.js]
|
||||
@@ -32,8 +39,9 @@ skip-if = toolkit == 'android'
|
||||
[test_unregister_invalid_json.js]
|
||||
[test_unregister_not_found.js]
|
||||
[test_unregister_success.js]
|
||||
[test_webapps_cleardata.js]
|
||||
[test_updateRecordNoEncryptionKeys_ws.js]
|
||||
[test_reconnect_retry.js]
|
||||
[test_retry_ws.js]
|
||||
#http2 test
|
||||
[test_resubscribe_4xxCode_http2.js]
|
||||
[test_resubscribe_5xxCode_http2.js]
|
||||
@@ -60,4 +68,4 @@ skip-if = !hasNode
|
||||
run-sequentially = node server exceptions dont replay well
|
||||
[test_clearAll_successful.js]
|
||||
skip-if = !hasNode
|
||||
run-sequentially = This will delete all existing push subscritions.
|
||||
run-sequentially = This will delete all existing push subscriptions.
|
||||
|
||||
@@ -447,7 +447,8 @@ nsContentSecurityManager::IsURIPotentiallyTrustworthy(nsIURI* aURI, bool* aIsTru
|
||||
|
||||
if (scheme.EqualsLiteral("https") ||
|
||||
scheme.EqualsLiteral("file") ||
|
||||
scheme.EqualsLiteral("app")) {
|
||||
scheme.EqualsLiteral("app") ||
|
||||
scheme.EqualsLiteral("wss")) {
|
||||
*aIsTrustWorthy = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@@ -1576,14 +1576,6 @@ this.PushService = {
|
||||
if (this._UAID)
|
||||
data["uaid"] = this._UAID;
|
||||
|
||||
function sendHelloMessage(ids) {
|
||||
// On success, ids is an array, on error its not.
|
||||
data["channelIDs"] = ids.map ?
|
||||
ids.map(function(el) { return el.channelID; }) : [];
|
||||
this._wsSendMessage(data);
|
||||
this._currentState = STATE_WAITING_FOR_HELLO;
|
||||
}
|
||||
|
||||
this._getNetworkState((networkState) => {
|
||||
if (networkState.ip) {
|
||||
// Opening an available UDP port.
|
||||
@@ -1602,8 +1594,8 @@ this.PushService = {
|
||||
};
|
||||
}
|
||||
|
||||
this._db.getAllChannelIDs(sendHelloMessage.bind(this),
|
||||
sendHelloMessage.bind(this));
|
||||
this._wsSendMessage(data);
|
||||
this._currentState = STATE_WAITING_FOR_HELLO;
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
@@ -79,13 +79,13 @@ var tests = [
|
||||
{ values: "foo bar baz qux", length: 4 } ] }
|
||||
];
|
||||
|
||||
for each (let test in tests) {
|
||||
for (let test of tests) {
|
||||
let list = test.element;
|
||||
for each (let property in test.listProperty.split(".")) {
|
||||
for (let property of test.listProperty.split(".")) {
|
||||
list = list[property];
|
||||
}
|
||||
|
||||
for each (let subtest in test.subtests) {
|
||||
for (let subtest of test.subtests) {
|
||||
if (subtest.values) {
|
||||
test.element.setAttribute(test.attribute, subtest.values);
|
||||
}
|
||||
|
||||
@@ -267,7 +267,7 @@ function isValidInterpolation(aFromType, aToType)
|
||||
// Runs the test.
|
||||
function run()
|
||||
{
|
||||
for each (let additive in [false, true]) {
|
||||
for (let additive of [false, true]) {
|
||||
let indexOfExpectedArguments = additive ? 3 : 2;
|
||||
|
||||
// Add subtests for each combination of prefix and suffix, and additive
|
||||
@@ -279,7 +279,7 @@ function run()
|
||||
toArguments = suffixEntry[1],
|
||||
expectedArguments = suffixEntry[indexOfExpectedArguments];
|
||||
|
||||
for each (let prefixEntry in gPrefixes) {
|
||||
for (let prefixEntry of gPrefixes) {
|
||||
let [prefixLength, prefix] = prefixEntry;
|
||||
addTest(prefixLength, prefix, fromType, fromArguments,
|
||||
toType, toArguments, toType, expectedArguments, additive);
|
||||
@@ -298,9 +298,9 @@ function run()
|
||||
"a", [60, 70, 80, 1, 0, 90, 100], additive);
|
||||
|
||||
// Test all pairs of segment types that cannot be interpolated between.
|
||||
for each (let fromType in gTypes) {
|
||||
for (let fromType of gTypes) {
|
||||
let fromArguments = generatePathSegmentArguments(fromType, 0);
|
||||
for each (let toType in gTypes) {
|
||||
for (let toType of gTypes) {
|
||||
if (!isValidInterpolation(fromType, toType)) {
|
||||
let toArguments = generatePathSegmentArguments(toType, 1000);
|
||||
addTest(1, "M100,100", fromType, fromArguments,
|
||||
@@ -314,7 +314,7 @@ function run()
|
||||
gSVG.setCurrentTime(4);
|
||||
|
||||
// Inspect the results of each subtest.
|
||||
for each (let test in gTests) {
|
||||
for (let test of gTests) {
|
||||
let list = test.element.animatedPathSegList;
|
||||
is(list.numberOfItems, test.prefixLength + 1,
|
||||
"Length of animatedPathSegList for interpolation " +
|
||||
|
||||
@@ -50,7 +50,7 @@ NetworkInterfaceListService.prototype = {
|
||||
}
|
||||
};
|
||||
|
||||
function FakeNetworkInterface(aAttributes) {
|
||||
function FakeNetworkInfo(aAttributes) {
|
||||
this.state = aAttributes.state;
|
||||
this.type = aAttributes.type;
|
||||
this.name = aAttributes.name;
|
||||
@@ -58,11 +58,9 @@ function FakeNetworkInterface(aAttributes) {
|
||||
this.prefixLengths = aAttributes.prefixLengths;
|
||||
this.gateways = aAttributes.gateways;
|
||||
this.dnses = aAttributes.dnses;
|
||||
this.httpProxyHost = aAttributes.httpProxyHost;
|
||||
this.httpProxyPort = aAttributes.httpProxyPort;
|
||||
}
|
||||
FakeNetworkInterface.prototype = {
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsINetworkInterface]),
|
||||
FakeNetworkInfo.prototype = {
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsINetworkInfo]),
|
||||
|
||||
getAddresses: function (ips, prefixLengths) {
|
||||
ips.value = this.ips.slice();
|
||||
@@ -89,7 +87,7 @@ FakeNetworkInterface.prototype = {
|
||||
function NetworkInterfaceList (aInterfaceLiterals) {
|
||||
this._interfaces = [];
|
||||
for (let entry of aInterfaceLiterals) {
|
||||
this._interfaces.push(new FakeNetworkInterface(entry));
|
||||
this._interfaces.push(new FakeNetworkInfo(entry));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,7 +98,7 @@ NetworkInterfaceList.prototype = {
|
||||
return this._interfaces.length;
|
||||
},
|
||||
|
||||
getInterface: function(index) {
|
||||
getInterfaceInfo: function(index) {
|
||||
if (!this._interfaces) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -35,6 +35,10 @@ XPCOMUtils.defineLazyServiceGetter(this, "gPACGenerator",
|
||||
"@mozilla.org/pac-generator;1",
|
||||
"nsIPACGenerator");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "gTetheringService",
|
||||
"@mozilla.org/tethering/service;1",
|
||||
"nsITetheringService");
|
||||
|
||||
const TOPIC_INTERFACE_REGISTERED = "network-interface-registered";
|
||||
const TOPIC_INTERFACE_UNREGISTERED = "network-interface-unregistered";
|
||||
const TOPIC_ACTIVE_CHANGED = "network-active-changed";
|
||||
@@ -99,6 +103,7 @@ function ExtraNetworkInfo(aNetwork) {
|
||||
this.dnses = aNetwork.info.getDnses();
|
||||
this.httpProxyHost = aNetwork.httpProxyHost;
|
||||
this.httpProxyPort = aNetwork.httpProxyPort;
|
||||
this.mtu = aNetwork.mtu;
|
||||
}
|
||||
ExtraNetworkInfo.prototype = {
|
||||
getAddresses: function(aIps, aPrefixLengths) {
|
||||
@@ -231,7 +236,9 @@ NetworkManager.prototype = {
|
||||
let excludeFota = aMsg.json.excludeFota;
|
||||
let interfaces = [];
|
||||
|
||||
for each (let i in this.networkInterfaces) {
|
||||
for (let key in this.networkInterfaces) {
|
||||
let network = this.networkInterfaces[key];
|
||||
let i = network.info;
|
||||
if ((i.type == Ci.nsINetworkInfo.NETWORK_TYPE_MOBILE_MMS && excludeMms) ||
|
||||
(i.type == Ci.nsINetworkInfo.NETWORK_TYPE_MOBILE_SUPL && excludeSupl) ||
|
||||
(i.type == Ci.nsINetworkInfo.NETWORK_TYPE_MOBILE_IMS && excludeIms) ||
|
||||
@@ -251,9 +258,7 @@ NetworkManager.prototype = {
|
||||
ips: ips.value,
|
||||
prefixLengths: prefixLengths.value,
|
||||
gateways: i.getGateways(),
|
||||
dnses: i.getDnses(),
|
||||
httpProxyHost: i.httpProxyHost,
|
||||
httpProxyPort: i.httpProxyPort
|
||||
dnses: i.getDnses()
|
||||
});
|
||||
}
|
||||
return interfaces;
|
||||
@@ -370,6 +375,13 @@ NetworkManager.prototype = {
|
||||
return this.setSecondaryDefaultRoute(extNetworkInfo);
|
||||
})
|
||||
.then(() => this._addSubnetRoutes(extNetworkInfo))
|
||||
.then(() => {
|
||||
if (extNetworkInfo.mtu <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
return this._setMtu(extNetworkInfo);
|
||||
})
|
||||
.then(() => this.setAndConfigureActive())
|
||||
.then(() => {
|
||||
// Update data connection when Wifi connected/disconnected
|
||||
@@ -487,6 +499,18 @@ NetworkManager.prototype = {
|
||||
|
||||
networkInterfaceLinks: null,
|
||||
|
||||
get allNetworkInfo() {
|
||||
let allNetworkInfo = {};
|
||||
|
||||
for (let networkId in this.networkInterfaces) {
|
||||
if (this.networkInterfaces.hasOwnProperty(networkId)) {
|
||||
allNetworkInfo[networkId] = this.networkInterfaces[networkId].info;
|
||||
}
|
||||
}
|
||||
|
||||
return allNetworkInfo;
|
||||
},
|
||||
|
||||
_preferredNetworkType: DEFAULT_PREFERRED_NETWORK_TYPE,
|
||||
get preferredNetworkType() {
|
||||
return this._preferredNetworkType;
|
||||
@@ -802,7 +826,8 @@ NetworkManager.prototype = {
|
||||
this._activeNetwork = null;
|
||||
let anyConnected = false;
|
||||
|
||||
for each (let network in this.networkInterfaces) {
|
||||
for (let key in this.networkInterfaces) {
|
||||
let network = this.networkInterfaces[key];
|
||||
if (network.info.state != Ci.nsINetworkInfo.NETWORK_STATE_CONNECTED) {
|
||||
continue;
|
||||
}
|
||||
@@ -836,7 +861,9 @@ NetworkManager.prototype = {
|
||||
}
|
||||
|
||||
if (this._manageOfflineStatus) {
|
||||
Services.io.offline = !anyConnected;
|
||||
Services.io.offline = !anyConnected &&
|
||||
(gTetheringService.state ===
|
||||
Ci.nsITetheringService.TETHERING_STATE_INACTIVE);
|
||||
}
|
||||
});
|
||||
},
|
||||
@@ -932,6 +959,18 @@ NetworkManager.prototype = {
|
||||
});
|
||||
},
|
||||
|
||||
_setMtu: function(aNetworkInfo) {
|
||||
return new Promise((aResolve, aReject) => {
|
||||
gNetworkService.setMtu(aNetworkInfo.name, aNetworkInfo.mtu, (aSuccess) => {
|
||||
if (!aSuccess) {
|
||||
debug("setMtu failed");
|
||||
}
|
||||
// Always resolve.
|
||||
aResolve();
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
_createNetwork: function(aInterfaceName) {
|
||||
return new Promise((aResolve, aReject) => {
|
||||
gNetworkService.createNetwork(aInterfaceName, (aSuccess) => {
|
||||
@@ -982,13 +1021,18 @@ NetworkManager.prototype = {
|
||||
});
|
||||
},
|
||||
|
||||
_setDefaultRouteAndProxy: function(aNetwork, aOldInterface) {
|
||||
_setDefaultRouteAndProxy: function(aNetwork, aOldNetwork) {
|
||||
if (aOldNetwork) {
|
||||
return this._removeDefaultRoute(aOldNetwork.info)
|
||||
.then(() => this._setDefaultRouteAndProxy(aNetwork, null));
|
||||
}
|
||||
|
||||
return new Promise((aResolve, aReject) => {
|
||||
let networkInfo = aNetwork.info;
|
||||
let gateways = networkInfo.getGateways();
|
||||
let oldInterfaceName = (aOldInterface ? aOldInterface.info.name : "");
|
||||
|
||||
gNetworkService.setDefaultRoute(networkInfo.name, gateways.length, gateways,
|
||||
oldInterfaceName, (aSuccess) => {
|
||||
(aSuccess) => {
|
||||
if (!aSuccess) {
|
||||
gNetworkService.destroyNetwork(networkInfo.name, function() {
|
||||
aReject("setDefaultRoute failed");
|
||||
|
||||
@@ -269,6 +269,58 @@ NetworkService.prototype = {
|
||||
});
|
||||
},
|
||||
|
||||
setNetworkTetheringAlarm(aEnable, aInterface) {
|
||||
// Method called when enabling disabling tethering, it checks if there is
|
||||
// some alarm active and move from interfaceAlarm to globalAlarm because
|
||||
// interfaceAlarm doens't work in tethering scenario due to forwarding.
|
||||
debug("setNetworkTetheringAlarm for tethering" + aEnable);
|
||||
|
||||
let filename = aEnable ? "/proc/net/xt_quota/" + aInterface + "Alert" :
|
||||
"/proc/net/xt_quota/globalAlert";
|
||||
|
||||
let file = new FileUtils.File(filename);
|
||||
if (!file) {
|
||||
return;
|
||||
}
|
||||
|
||||
NetUtil.asyncFetch({
|
||||
uri: NetUtil.newURI(file),
|
||||
loadUsingSystemPrincipal: true
|
||||
}, (inputStream, status) => {
|
||||
if (Components.isSuccessCode(status)) {
|
||||
let data = NetUtil.readInputStreamToString(inputStream, inputStream.available())
|
||||
.split("\n");
|
||||
if (data) {
|
||||
let threshold = parseInt(data[0], 10);
|
||||
|
||||
this._setNetworkTetheringAlarm(aEnable, aInterface, threshold);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
_setNetworkTetheringAlarm(aEnable, aInterface, aThreshold, aCallback) {
|
||||
debug("_setNetworkTetheringAlarm for tethering" + aEnable);
|
||||
|
||||
let cmd = aEnable ? "setTetheringAlarm" : "removeTetheringAlarm";
|
||||
|
||||
let params = {
|
||||
cmd: cmd,
|
||||
ifname: aInterface,
|
||||
threshold: aThreshold,
|
||||
};
|
||||
|
||||
this.controlMessage(params, function(aData) {
|
||||
let code = aData.resultCode;
|
||||
let reason = aData.resultReason;
|
||||
let enableString = aEnable ? "Enable" : "Disable";
|
||||
debug(enableString + " tethering Alarm result: Code " + code + " reason " + reason);
|
||||
if (aCallback) {
|
||||
aCallback.networkUsageAlarmResult(null);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
setNetworkInterfaceAlarm: function(aInterfaceName, aThreshold, aCallback) {
|
||||
if (!aInterfaceName) {
|
||||
aCallback.networkUsageAlarmResult(-1);
|
||||
@@ -286,7 +338,26 @@ NetworkService.prototype = {
|
||||
return
|
||||
}
|
||||
|
||||
self._setNetworkInterfaceAlarm(aInterfaceName, aThreshold, aCallback);
|
||||
// Check if tethering is enabled
|
||||
let params = {
|
||||
cmd: "getTetheringStatus"
|
||||
};
|
||||
|
||||
self.controlMessage(params, function(aResult) {
|
||||
if (isError(aResult.resultCode)) {
|
||||
aCallback.networkUsageAlarmResult(aResult.reason);
|
||||
return;
|
||||
}
|
||||
|
||||
if (aResult.resultReason.indexOf('started') == -1) {
|
||||
// Tethering disabled, set interfaceAlarm
|
||||
self._setNetworkInterfaceAlarm(aInterfaceName, aThreshold, aCallback);
|
||||
return;
|
||||
}
|
||||
|
||||
// Tethering enabled, set globalAlarm
|
||||
self._setNetworkTetheringAlarm(true, aInterfaceName, aThreshold, aCallback);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
@@ -392,14 +463,11 @@ NetworkService.prototype = {
|
||||
});
|
||||
},
|
||||
|
||||
setDefaultRoute: function(aInterfaceName, aCount, aGateways,
|
||||
aOldInterfaceName, aCallback) {
|
||||
setDefaultRoute: function(aInterfaceName, aCount, aGateways, aCallback) {
|
||||
debug("Going to change default route to " + aInterfaceName);
|
||||
let options = {
|
||||
cmd: "setDefaultRoute",
|
||||
ifname: aInterfaceName,
|
||||
oldIfname: (aOldInterfaceName && aOldInterfaceName !== aInterfaceName) ?
|
||||
aOldInterfaceName : null,
|
||||
gateways: aGateways
|
||||
};
|
||||
this.controlMessage(options, function(aResult) {
|
||||
@@ -545,7 +613,7 @@ NetworkService.prototype = {
|
||||
aConfig.cmd = "setWifiTethering";
|
||||
|
||||
// The callback function in controlMessage may not be fired immediately.
|
||||
this.controlMessage(aConfig, function(aData) {
|
||||
this.controlMessage(aConfig, (aData) => {
|
||||
let code = aData.resultCode;
|
||||
let reason = aData.resultReason;
|
||||
let enable = aData.enable;
|
||||
@@ -553,6 +621,8 @@ NetworkService.prototype = {
|
||||
|
||||
debug(enableString + " Wifi tethering result: Code " + code + " reason " + reason);
|
||||
|
||||
this.setNetworkTetheringAlarm(aEnable, aConfig.externalIfname);
|
||||
|
||||
if (isError(code)) {
|
||||
aCallback.wifiTetheringEnabledChange("netd command error");
|
||||
} else {
|
||||
@@ -565,7 +635,7 @@ NetworkService.prototype = {
|
||||
setUSBTethering: function(aEnable, aConfig, aCallback) {
|
||||
aConfig.cmd = "setUSBTethering";
|
||||
// The callback function in controlMessage may not be fired immediately.
|
||||
this.controlMessage(aConfig, function(aData) {
|
||||
this.controlMessage(aConfig, (aData) => {
|
||||
let code = aData.resultCode;
|
||||
let reason = aData.resultReason;
|
||||
let enable = aData.enable;
|
||||
@@ -573,6 +643,8 @@ NetworkService.prototype = {
|
||||
|
||||
debug(enableString + " USB tethering result: Code " + code + " reason " + reason);
|
||||
|
||||
this.setNetworkTetheringAlarm(aEnable, aConfig.externalIfname);
|
||||
|
||||
if (isError(code)) {
|
||||
aCallback.usbTetheringEnabledChange("netd command error");
|
||||
} else {
|
||||
@@ -729,6 +801,20 @@ NetworkService.prototype = {
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
setMtu: function (aInterfaceName, aMtu, aCallback) {
|
||||
debug("Set MTU on " + aInterfaceName + ": " + aMtu);
|
||||
|
||||
let params = {
|
||||
cmd: "setMtu",
|
||||
ifname: aInterfaceName,
|
||||
mtu: aMtu
|
||||
};
|
||||
|
||||
this.controlMessage(params, function(aResult) {
|
||||
aCallback.nativeCommandResult(!aResult.error);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([NetworkService]);
|
||||
|
||||
@@ -51,6 +51,11 @@ static const char* USB_FUNCTION_ADB = "adb";
|
||||
// Use this command to continue the function chain.
|
||||
static const char* DUMMY_COMMAND = "tether status";
|
||||
|
||||
// IPV6 Tethering is not supported in AOSP, use the property to
|
||||
// identify vendor specific support in IPV6. We can remove this flag
|
||||
// once upstream Android support IPV6 in tethering.
|
||||
static const char* IPV6_TETHERING = "ro.tethering.ipv6";
|
||||
|
||||
// Retry 20 times (2 seconds) for usb state transition.
|
||||
static const uint32_t USB_FUNCTION_RETRY_TIMES = 20;
|
||||
// Check "sys.usb.state" every 100ms.
|
||||
@@ -81,6 +86,7 @@ static const uint32_t BUF_SIZE = 1024;
|
||||
static const int32_t SUCCESS = 0;
|
||||
|
||||
static uint32_t SDK_VERSION;
|
||||
static uint32_t SUPPORT_IPV6_TETHERING;
|
||||
|
||||
struct IFProperties {
|
||||
char gateway[PROPERTY_VALUE_MAX];
|
||||
@@ -201,6 +207,7 @@ const CommandFunc NetworkUtils::sUSBEnableChain[] = {
|
||||
NetworkUtils::tetheringStatus,
|
||||
NetworkUtils::startTethering,
|
||||
NetworkUtils::setDnsForwarders,
|
||||
NetworkUtils::addUpstreamInterface,
|
||||
NetworkUtils::usbTetheringSuccess
|
||||
};
|
||||
|
||||
@@ -209,6 +216,7 @@ const CommandFunc NetworkUtils::sUSBDisableChain[] = {
|
||||
NetworkUtils::removeInterfaceFromLocalNetwork,
|
||||
NetworkUtils::preTetherInterfaceList,
|
||||
NetworkUtils::postTetherInterfaceList,
|
||||
NetworkUtils::removeUpstreamInterface,
|
||||
NetworkUtils::disableNat,
|
||||
NetworkUtils::setIpForwardingEnabled,
|
||||
NetworkUtils::stopTethering,
|
||||
@@ -223,7 +231,9 @@ const CommandFunc NetworkUtils::sUSBFailChain[] = {
|
||||
|
||||
const CommandFunc NetworkUtils::sUpdateUpStreamChain[] = {
|
||||
NetworkUtils::cleanUpStream,
|
||||
NetworkUtils::removeUpstreamInterface,
|
||||
NetworkUtils::createUpStream,
|
||||
NetworkUtils::addUpstreamInterface,
|
||||
NetworkUtils::updateUpStreamSuccess
|
||||
};
|
||||
|
||||
@@ -256,6 +266,23 @@ const CommandFunc NetworkUtils::sNetworkInterfaceSetAlarmChain[] = {
|
||||
NetworkUtils::networkInterfaceAlarmSuccess
|
||||
};
|
||||
|
||||
const CommandFunc NetworkUtils::sTetheringInterfaceSetAlarmChain[] = {
|
||||
NetworkUtils::setGlobalAlarm,
|
||||
NetworkUtils::removeAlarm,
|
||||
NetworkUtils::networkInterfaceAlarmSuccess
|
||||
};
|
||||
|
||||
const CommandFunc NetworkUtils::sTetheringInterfaceRemoveAlarmChain[] = {
|
||||
NetworkUtils::removeGlobalAlarm,
|
||||
NetworkUtils::setAlarm,
|
||||
NetworkUtils::networkInterfaceAlarmSuccess
|
||||
};
|
||||
|
||||
const CommandFunc NetworkUtils::sTetheringGetStatusChain[] = {
|
||||
NetworkUtils::tetheringStatus,
|
||||
NetworkUtils::defaultAsyncSuccessHandler
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper function to get the mask from given prefix length.
|
||||
*/
|
||||
@@ -710,6 +737,36 @@ void NetworkUtils::setAlarm(CommandChain* aChain,
|
||||
doCommand(command, aChain, aCallback);
|
||||
}
|
||||
|
||||
void NetworkUtils::removeAlarm(CommandChain* aChain,
|
||||
CommandCallback aCallback,
|
||||
NetworkResultOptions& aResult)
|
||||
{
|
||||
char command[MAX_COMMAND_SIZE];
|
||||
PR_snprintf(command, MAX_COMMAND_SIZE - 1, "bandwidth removeinterfacealert %s", GET_CHAR(mIfname));
|
||||
|
||||
doCommand(command, aChain, aCallback);
|
||||
}
|
||||
|
||||
void NetworkUtils::setGlobalAlarm(CommandChain* aChain,
|
||||
CommandCallback aCallback,
|
||||
NetworkResultOptions& aResult)
|
||||
{
|
||||
char command[MAX_COMMAND_SIZE];
|
||||
|
||||
PR_snprintf(command, MAX_COMMAND_SIZE - 1, "bandwidth setglobalalert %ld", GET_FIELD(mThreshold));
|
||||
doCommand(command, aChain, aCallback);
|
||||
}
|
||||
|
||||
void NetworkUtils::removeGlobalAlarm(CommandChain* aChain,
|
||||
CommandCallback aCallback,
|
||||
NetworkResultOptions& aResult)
|
||||
{
|
||||
char command[MAX_COMMAND_SIZE];
|
||||
|
||||
PR_snprintf(command, MAX_COMMAND_SIZE - 1, "bandwidth removeglobalalert");
|
||||
doCommand(command, aChain, aCallback);
|
||||
}
|
||||
|
||||
void NetworkUtils::setInterfaceUp(CommandChain* aChain,
|
||||
CommandCallback aCallback,
|
||||
NetworkResultOptions& aResult)
|
||||
@@ -807,12 +864,87 @@ void NetworkUtils::postTetherInterfaceList(CommandChain* aChain,
|
||||
|
||||
char buf[BUF_SIZE];
|
||||
NS_ConvertUTF16toUTF8 reason(aResult.mResultReason);
|
||||
memcpy(buf, reason.get(), reason.Length() + 1);
|
||||
|
||||
size_t length = reason.Length() + 1 < BUF_SIZE ? reason.Length() + 1 : BUF_SIZE;
|
||||
memcpy(buf, reason.get(), length);
|
||||
split(buf, INTERFACE_DELIMIT, GET_FIELD(mInterfaceList));
|
||||
|
||||
doCommand(command, aChain, aCallback);
|
||||
}
|
||||
|
||||
bool isCommandChainIPv6(CommandChain* aChain, const char *externalInterface) {
|
||||
// Check by gateway address
|
||||
if (getIpType(GET_CHAR(mGateway)) == AF_INET6) {
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t length = GET_FIELD(mGateways).Length();
|
||||
for (uint32_t i = 0; i < length; i++) {
|
||||
NS_ConvertUTF16toUTF8 autoGateway(GET_FIELD(mGateways)[i]);
|
||||
if(getIpType(autoGateway.get()) == AF_INET6) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Check by external inteface address
|
||||
FILE *file = fopen("/proc/net/if_inet6", "r");
|
||||
if (!file) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isIPv6 = false;
|
||||
char interface[32];
|
||||
while(fscanf(file, "%*s %*s %*s %*s %*s %32s", interface)) {
|
||||
if (strcmp(interface, externalInterface) == 0) {
|
||||
isIPv6 = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
return isIPv6;
|
||||
}
|
||||
|
||||
void NetworkUtils::addUpstreamInterface(CommandChain* aChain,
|
||||
CommandCallback aCallback,
|
||||
NetworkResultOptions& aResult)
|
||||
{
|
||||
nsCString interface(GET_CHAR(mExternalIfname));
|
||||
if (!interface.get()[0]) {
|
||||
interface = GET_CHAR(mCurExternalIfname);
|
||||
}
|
||||
|
||||
if (SUPPORT_IPV6_TETHERING == 0 || !isCommandChainIPv6(aChain, interface.get())) {
|
||||
aCallback(aChain, false, aResult);
|
||||
return;
|
||||
}
|
||||
|
||||
char command[MAX_COMMAND_SIZE];
|
||||
snprintf(command, MAX_COMMAND_SIZE - 1, "tether interface add_upstream %s",
|
||||
interface.get());
|
||||
doCommand(command, aChain, aCallback);
|
||||
}
|
||||
|
||||
void NetworkUtils::removeUpstreamInterface(CommandChain* aChain,
|
||||
CommandCallback aCallback,
|
||||
NetworkResultOptions& aResult)
|
||||
{
|
||||
nsCString interface(GET_CHAR(mExternalIfname));
|
||||
if (!interface.get()[0]) {
|
||||
interface = GET_CHAR(mPreExternalIfname);
|
||||
}
|
||||
|
||||
if (SUPPORT_IPV6_TETHERING == 0 || !isCommandChainIPv6(aChain, interface.get())) {
|
||||
aCallback(aChain, false, aResult);
|
||||
return;
|
||||
}
|
||||
|
||||
char command[MAX_COMMAND_SIZE];
|
||||
snprintf(command, MAX_COMMAND_SIZE - 1, "tether interface remove_upstream %s",
|
||||
interface.get());
|
||||
doCommand(command, aChain, aCallback);
|
||||
}
|
||||
|
||||
void NetworkUtils::setIpForwardingEnabled(CommandChain* aChain,
|
||||
CommandCallback aCallback,
|
||||
NetworkResultOptions& aResult)
|
||||
@@ -991,14 +1123,39 @@ void NetworkUtils::removeDefaultRoute(CommandChain* aChain,
|
||||
CommandCallback aCallback,
|
||||
NetworkResultOptions& aResult)
|
||||
{
|
||||
char command[MAX_COMMAND_SIZE];
|
||||
// FIXME: (Bug 1121795) We only remove the first gateway to the default route.
|
||||
// For dual stack (ipv4/ipv6) device, one of the gateway would
|
||||
// not be added to the default route.
|
||||
snprintf(command, MAX_COMMAND_SIZE - 1, "network route remove %d %s 0.0.0.0/0 %s",
|
||||
GET_FIELD(mNetId), GET_CHAR(mIfname), GET_CHAR(mGateways[0]));
|
||||
if (GET_FIELD(mLoopIndex) >= GET_FIELD(mGateways).Length()) {
|
||||
aCallback(aChain, false, aResult);
|
||||
return;
|
||||
}
|
||||
|
||||
doCommand(command, aChain, aCallback);
|
||||
char command[MAX_COMMAND_SIZE];
|
||||
nsTArray<nsString>& gateways = GET_FIELD(mGateways);
|
||||
NS_ConvertUTF16toUTF8 autoGateway(gateways[GET_FIELD(mLoopIndex)]);
|
||||
|
||||
int type = getIpType(autoGateway.get());
|
||||
snprintf(command, MAX_COMMAND_SIZE - 1, "network route remove %d %s %s/0 %s",
|
||||
GET_FIELD(mNetId), GET_CHAR(mIfname),
|
||||
type == AF_INET6 ? "::" : "0.0.0.0", autoGateway.get());
|
||||
|
||||
struct MyCallback {
|
||||
static void callback(CommandCallback::CallbackType aOriginalCallback,
|
||||
CommandChain* aChain,
|
||||
bool aError,
|
||||
mozilla::dom::NetworkResultOptions& aResult)
|
||||
{
|
||||
NS_ConvertUTF16toUTF8 reason(aResult.mResultReason);
|
||||
NU_DBG("removeDefaultRoute's reason: %s", reason.get());
|
||||
if (aError && !reason.EqualsASCII("removeRoute() failed (No such process)")) {
|
||||
return aOriginalCallback(aChain, aError, aResult);
|
||||
}
|
||||
|
||||
GET_FIELD(mLoopIndex)++;
|
||||
return removeDefaultRoute(aChain, aOriginalCallback, aResult);
|
||||
}
|
||||
};
|
||||
|
||||
CommandCallback wrappedCallback(MyCallback::callback, aCallback);
|
||||
doCommand(command, aChain, wrappedCallback);
|
||||
}
|
||||
|
||||
void NetworkUtils::setInterfaceDns(CommandChain* aChain,
|
||||
@@ -1146,13 +1303,19 @@ void NetworkUtils::addDefaultRouteToNetwork(CommandChain* aChain,
|
||||
CommandCallback aCallback,
|
||||
NetworkResultOptions& aResult)
|
||||
{
|
||||
char command[MAX_COMMAND_SIZE];
|
||||
if (GET_FIELD(mLoopIndex) >= GET_FIELD(mGateways).Length()) {
|
||||
aCallback(aChain, false, aResult);
|
||||
return;
|
||||
}
|
||||
|
||||
// FIXME: (Bug 1121795) We only add the first gateway to the default route.
|
||||
// For dual stack (ipv4/ipv6) device, one of the gateway would
|
||||
// not be added to the default route.
|
||||
snprintf(command, MAX_COMMAND_SIZE - 1, "network route add %d %s 0.0.0.0/0 %s",
|
||||
GET_FIELD(mNetId), GET_CHAR(mIfname), GET_CHAR(mGateways[0]));
|
||||
char command[MAX_COMMAND_SIZE];
|
||||
nsTArray<nsString>& gateways = GET_FIELD(mGateways);
|
||||
NS_ConvertUTF16toUTF8 autoGateway(gateways[GET_FIELD(mLoopIndex)]);
|
||||
|
||||
int type = getIpType(autoGateway.get());
|
||||
snprintf(command, MAX_COMMAND_SIZE - 1, "network route add %d %s %s/0 %s",
|
||||
GET_FIELD(mNetId), GET_CHAR(mIfname),
|
||||
type == AF_INET6 ? "::" : "0.0.0.0", autoGateway.get());
|
||||
|
||||
struct MyCallback {
|
||||
static void callback(CommandCallback::CallbackType aOriginalCallback,
|
||||
@@ -1162,11 +1325,12 @@ void NetworkUtils::addDefaultRouteToNetwork(CommandChain* aChain,
|
||||
{
|
||||
NS_ConvertUTF16toUTF8 reason(aResult.mResultReason);
|
||||
NU_DBG("addDefaultRouteToNetwork's reason: %s", reason.get());
|
||||
if (aError && reason.EqualsASCII("addRoute() failed (File exists)")) {
|
||||
NU_DBG("Ignore \"File exists\" error when adding host route.");
|
||||
return aOriginalCallback(aChain, false, aResult);
|
||||
if (aError && !reason.EqualsASCII("addRoute() failed (File exists)")) {
|
||||
return aOriginalCallback(aChain, aError, aResult);
|
||||
}
|
||||
aOriginalCallback(aChain, aError, aResult);
|
||||
|
||||
GET_FIELD(mLoopIndex)++;
|
||||
return addDefaultRouteToNetwork(aChain, aOriginalCallback, aResult);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1272,6 +1436,17 @@ void NetworkUtils::disableIpv6(CommandChain* aChain,
|
||||
setIpv6Enabled(aChain, aCallback, aResult, false);
|
||||
}
|
||||
|
||||
void NetworkUtils::setMtu(CommandChain* aChain,
|
||||
CommandCallback aCallback,
|
||||
NetworkResultOptions& aResult)
|
||||
{
|
||||
char command[MAX_COMMAND_SIZE];
|
||||
PR_snprintf(command, MAX_COMMAND_SIZE - 1, "interface setmtu %s %d",
|
||||
GET_CHAR(mIfname), GET_FIELD(mMtu));
|
||||
|
||||
doCommand(command, aChain, aCallback);
|
||||
}
|
||||
|
||||
#undef GET_CHAR
|
||||
#undef GET_FIELD
|
||||
|
||||
@@ -1450,6 +1625,9 @@ NetworkUtils::NetworkUtils(MessageCallback aCallback)
|
||||
property_get("ro.build.version.sdk", value, nullptr);
|
||||
SDK_VERSION = atoi(value);
|
||||
|
||||
property_get(IPV6_TETHERING, value, "0");
|
||||
SUPPORT_IPV6_TETHERING = atoi(value);
|
||||
|
||||
gNetworkUtils = this;
|
||||
}
|
||||
|
||||
@@ -1489,6 +1667,9 @@ void NetworkUtils::ExecuteCommand(NetworkParams aOptions)
|
||||
BUILD_ENTRY(setNetworkInterfaceAlarm),
|
||||
BUILD_ENTRY(enableNetworkInterfaceAlarm),
|
||||
BUILD_ENTRY(disableNetworkInterfaceAlarm),
|
||||
BUILD_ENTRY(setTetheringAlarm),
|
||||
BUILD_ENTRY(removeTetheringAlarm),
|
||||
BUILD_ENTRY(getTetheringStatus),
|
||||
BUILD_ENTRY(setWifiOperationMode),
|
||||
BUILD_ENTRY(setDhcpServer),
|
||||
BUILD_ENTRY(setWifiTethering),
|
||||
@@ -1504,6 +1685,7 @@ void NetworkUtils::ExecuteCommand(NetworkParams aOptions)
|
||||
BUILD_ENTRY(createNetwork),
|
||||
BUILD_ENTRY(destroyNetwork),
|
||||
BUILD_ENTRY(getNetId),
|
||||
BUILD_ENTRY(setMtu),
|
||||
|
||||
#undef BUILD_ENTRY
|
||||
};
|
||||
@@ -1822,8 +2004,9 @@ CommandResult NetworkUtils::setDefaultRoute(NetworkParams& aOptions)
|
||||
}
|
||||
|
||||
aOptions.mNetId = netIdInfo.mNetId;
|
||||
|
||||
aOptions.mLoopIndex = 0;
|
||||
runChain(aOptions, COMMAND_CHAIN, defaultAsyncFailureHandler);
|
||||
|
||||
return CommandResult::Pending();
|
||||
}
|
||||
|
||||
@@ -1834,13 +2017,6 @@ CommandResult NetworkUtils::setDefaultRouteLegacy(NetworkParams& aOptions)
|
||||
{
|
||||
NS_ConvertUTF16toUTF8 autoIfname(aOptions.mIfname);
|
||||
|
||||
if (!aOptions.mOldIfname.IsEmpty()) {
|
||||
// Remove IPv4's default route.
|
||||
RETURN_IF_FAILED(mNetUtils->do_ifc_remove_default_route(GET_CHAR(mOldIfname)));
|
||||
// Remove IPv6's default route.
|
||||
WARN_IF_FAILED(mNetUtils->do_ifc_remove_route(GET_CHAR(mOldIfname), "::", 0, NULL));
|
||||
}
|
||||
|
||||
uint32_t length = aOptions.mGateways.Length();
|
||||
if (length > 0) {
|
||||
for (uint32_t i = 0; i < length; i++) {
|
||||
@@ -1916,6 +2092,7 @@ CommandResult NetworkUtils::removeDefaultRoute(NetworkParams& aOptions)
|
||||
NU_DBG("Obtained netid %d for interface %s", netIdInfo.mNetId, GET_CHAR(mIfname));
|
||||
|
||||
aOptions.mNetId = netIdInfo.mNetId;
|
||||
aOptions.mLoopIndex = 0;
|
||||
runChain(aOptions, COMMAND_CHAIN, defaultAsyncFailureHandler);
|
||||
|
||||
return CommandResult::Pending();
|
||||
@@ -2233,6 +2410,27 @@ CommandResult NetworkUtils::disableNetworkInterfaceAlarm(NetworkParams& aOptions
|
||||
return CommandResult::Pending();
|
||||
}
|
||||
|
||||
CommandResult NetworkUtils::setTetheringAlarm(NetworkParams& aOptions)
|
||||
{
|
||||
NU_DBG("setTetheringAlarm");
|
||||
runChain(aOptions, sTetheringInterfaceSetAlarmChain, networkInterfaceAlarmFail);
|
||||
return CommandResult::Pending();
|
||||
}
|
||||
|
||||
CommandResult NetworkUtils::removeTetheringAlarm(NetworkParams& aOptions)
|
||||
{
|
||||
NU_DBG("removeTetheringAlarm");
|
||||
runChain(aOptions, sTetheringInterfaceRemoveAlarmChain, networkInterfaceAlarmFail);
|
||||
return CommandResult::Pending();
|
||||
}
|
||||
|
||||
CommandResult NetworkUtils::getTetheringStatus(NetworkParams& aOptions)
|
||||
{
|
||||
NU_DBG("getTetheringStatus");
|
||||
runChain(aOptions, sTetheringGetStatusChain, networkInterfaceAlarmFail);
|
||||
return CommandResult::Pending();
|
||||
}
|
||||
|
||||
/**
|
||||
* handling main thread's reload Wifi firmware request
|
||||
*/
|
||||
@@ -2526,6 +2724,23 @@ CommandResult NetworkUtils::getNetId(NetworkParams& aOptions)
|
||||
return result;
|
||||
}
|
||||
|
||||
CommandResult NetworkUtils::setMtu(NetworkParams& aOptions)
|
||||
{
|
||||
// Setting/getting mtu is supported since Kitkat.
|
||||
if (SDK_VERSION < 19) {
|
||||
ERROR("setMtu is not supported in current SDK_VERSION.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
static CommandFunc COMMAND_CHAIN[] = {
|
||||
setMtu,
|
||||
defaultAsyncSuccessHandler,
|
||||
};
|
||||
|
||||
runChain(aOptions, COMMAND_CHAIN, defaultAsyncFailureHandler);
|
||||
return CommandResult::Pending();
|
||||
}
|
||||
|
||||
void NetworkUtils::sendBroadcastMessage(uint32_t code, char* reason)
|
||||
{
|
||||
NetworkResultOptions result;
|
||||
|
||||
@@ -110,7 +110,6 @@ public:
|
||||
COPY_OPT_STRING_FIELD(mIfname, EmptyString())
|
||||
COPY_OPT_STRING_FIELD(mIp, EmptyString())
|
||||
COPY_OPT_FIELD(mPrefixLength, 0)
|
||||
COPY_OPT_STRING_FIELD(mOldIfname, EmptyString())
|
||||
COPY_OPT_STRING_FIELD(mMode, EmptyString())
|
||||
COPY_OPT_FIELD(mReport, false)
|
||||
COPY_OPT_FIELD(mEnabled, false)
|
||||
@@ -145,6 +144,9 @@ public:
|
||||
COPY_OPT_FIELD(mGateway_long, 0)
|
||||
COPY_OPT_FIELD(mDns1_long, 0)
|
||||
COPY_OPT_FIELD(mDns2_long, 0)
|
||||
COPY_OPT_FIELD(mMtu, 0)
|
||||
|
||||
mLoopIndex = 0;
|
||||
|
||||
#undef COPY_SEQUENCE_FIELD
|
||||
#undef COPY_OPT_STRING_FIELD
|
||||
@@ -161,7 +163,6 @@ public:
|
||||
nsString mIfname;
|
||||
nsString mIp;
|
||||
uint32_t mPrefixLength;
|
||||
nsString mOldIfname;
|
||||
nsString mMode;
|
||||
bool mReport;
|
||||
bool mEnabled;
|
||||
@@ -196,9 +197,11 @@ public:
|
||||
long mGateway_long;
|
||||
long mDns1_long;
|
||||
long mDns2_long;
|
||||
long mMtu;
|
||||
|
||||
// Auxiliary information required to carry accros command chain.
|
||||
int mNetId; // A locally defined id per interface.
|
||||
int mNetId; // A locally defined id per interface.
|
||||
uint32_t mLoopIndex; // Loop index for adding/removing multiple gateways.
|
||||
};
|
||||
|
||||
// CommandChain store the necessary information to execute command one by one.
|
||||
@@ -299,6 +302,9 @@ private:
|
||||
CommandResult setNetworkInterfaceAlarm(NetworkParams& aOptions);
|
||||
CommandResult enableNetworkInterfaceAlarm(NetworkParams& aOptions);
|
||||
CommandResult disableNetworkInterfaceAlarm(NetworkParams& aOptions);
|
||||
CommandResult setTetheringAlarm(NetworkParams& aOptions);
|
||||
CommandResult removeTetheringAlarm(NetworkParams& aOptions);
|
||||
CommandResult getTetheringStatus(NetworkParams& aOptions);
|
||||
CommandResult setWifiOperationMode(NetworkParams& aOptions);
|
||||
CommandResult setDhcpServer(NetworkParams& aOptions);
|
||||
CommandResult setWifiTethering(NetworkParams& aOptions);
|
||||
@@ -308,6 +314,7 @@ private:
|
||||
CommandResult createNetwork(NetworkParams& aOptions);
|
||||
CommandResult destroyNetwork(NetworkParams& aOptions);
|
||||
CommandResult getNetId(NetworkParams& aOptions);
|
||||
CommandResult setMtu(NetworkParams& aOptions);
|
||||
|
||||
CommandResult addHostRouteLegacy(NetworkParams& aOptions);
|
||||
CommandResult removeHostRouteLegacy(NetworkParams& aOptions);
|
||||
@@ -334,7 +341,9 @@ private:
|
||||
static const CommandFunc sNetworkInterfaceEnableAlarmChain[];
|
||||
static const CommandFunc sNetworkInterfaceDisableAlarmChain[];
|
||||
static const CommandFunc sNetworkInterfaceSetAlarmChain[];
|
||||
|
||||
static const CommandFunc sTetheringInterfaceSetAlarmChain[];
|
||||
static const CommandFunc sTetheringInterfaceRemoveAlarmChain[];
|
||||
static const CommandFunc sTetheringGetStatusChain[];
|
||||
/**
|
||||
* Individual netd command stored in command chain.
|
||||
*/
|
||||
@@ -354,12 +363,17 @@ private:
|
||||
static void setQuota(PARAMS);
|
||||
static void removeQuota(PARAMS);
|
||||
static void setAlarm(PARAMS);
|
||||
static void removeAlarm(PARAMS);
|
||||
static void setGlobalAlarm(PARAMS);
|
||||
static void removeGlobalAlarm(PARAMS);
|
||||
static void setInterfaceUp(PARAMS);
|
||||
static void tetherInterface(PARAMS);
|
||||
static void addInterfaceToLocalNetwork(PARAMS);
|
||||
static void addRouteToLocalNetwork(PARAMS);
|
||||
static void preTetherInterfaceList(PARAMS);
|
||||
static void postTetherInterfaceList(PARAMS);
|
||||
static void addUpstreamInterface(PARAMS);
|
||||
static void removeUpstreamInterface(PARAMS);
|
||||
static void setIpForwardingEnabled(PARAMS);
|
||||
static void tetheringStatus(PARAMS);
|
||||
static void stopTethering(PARAMS);
|
||||
@@ -391,6 +405,7 @@ private:
|
||||
static void modifyRouteOnInterface(PARAMS, bool aDoAdd);
|
||||
static void enableIpv6(PARAMS);
|
||||
static void disableIpv6(PARAMS);
|
||||
static void setMtu(PARAMS);
|
||||
static void setIpv6Enabled(PARAMS, bool aEnabled);
|
||||
static void addRouteToSecondaryTable(PARAMS);
|
||||
static void removeRouteFromSecondaryTable(PARAMS);
|
||||
|
||||
@@ -39,12 +39,11 @@ XPCOMUtils.defineLazyGetter(this, "gRil", function() {
|
||||
return null;
|
||||
});
|
||||
|
||||
const TOPIC_INTERFACE_REGISTERED = "network-interface-registered";
|
||||
const TOPIC_INTERFACE_UNREGISTERED = "network-interface-unregistered";
|
||||
const TOPIC_MOZSETTINGS_CHANGED = "mozsettings-changed";
|
||||
const TOPIC_CONNECTION_STATE_CHANGED = "network-connection-state-changed";
|
||||
const TOPIC_PREF_CHANGED = "nsPref:changed";
|
||||
const TOPIC_XPCOM_SHUTDOWN = "xpcom-shutdown";
|
||||
const PREF_MANAGE_OFFLINE_STATUS = "network.gonk.manage-offline-status";
|
||||
const PREF_NETWORK_DEBUG_ENABLED = "network.debugging.enabled";
|
||||
|
||||
const POSSIBLE_USB_INTERFACE_NAME = "rndis0,usb0";
|
||||
@@ -126,9 +125,15 @@ function TetheringService() {
|
||||
Services.obs.addObserver(this, TOPIC_XPCOM_SHUTDOWN, false);
|
||||
Services.obs.addObserver(this, TOPIC_MOZSETTINGS_CHANGED, false);
|
||||
Services.obs.addObserver(this, TOPIC_CONNECTION_STATE_CHANGED, false);
|
||||
Services.obs.addObserver(this, TOPIC_INTERFACE_REGISTERED, false);
|
||||
Services.obs.addObserver(this, TOPIC_INTERFACE_UNREGISTERED, false);
|
||||
Services.prefs.addObserver(PREF_NETWORK_DEBUG_ENABLED, this, false);
|
||||
Services.prefs.addObserver(PREF_MANAGE_OFFLINE_STATUS, this, false);
|
||||
|
||||
try {
|
||||
this._manageOfflineStatus =
|
||||
Services.prefs.getBoolPref(PREF_MANAGE_OFFLINE_STATUS);
|
||||
} catch(ex) {
|
||||
// Ignore.
|
||||
}
|
||||
|
||||
this._dataDefaultServiceId = 0;
|
||||
|
||||
@@ -234,6 +239,12 @@ TetheringService.prototype = {
|
||||
// Arguments for pending wifi tethering request.
|
||||
_pendingWifiTetheringRequestArgs: null,
|
||||
|
||||
// The state of tethering.
|
||||
state: Ci.nsITetheringService.TETHERING_STATE_INACTIVE,
|
||||
|
||||
// Flag to check if we can modify the Services.io.offline.
|
||||
_manageOfflineStatus: true,
|
||||
|
||||
// nsIObserver
|
||||
|
||||
observe: function(aSubject, aTopic, aData) {
|
||||
@@ -257,33 +268,24 @@ TetheringService.prototype = {
|
||||
" changed state to " + network.state);
|
||||
this.onConnectionChanged(network);
|
||||
break;
|
||||
case TOPIC_INTERFACE_REGISTERED:
|
||||
network = aSubject.QueryInterface(Ci.nsINetworkInterface);
|
||||
if (network &&
|
||||
network.type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_DUN) {
|
||||
debug("Force setting " + SETTINGS_DUN_REQUIRED + " to true.");
|
||||
this.tetheringSettings[SETTINGS_DUN_REQUIRED] = true;
|
||||
}
|
||||
break;
|
||||
case TOPIC_INTERFACE_UNREGISTERED:
|
||||
network = aSubject.QueryInterface(Ci.nsINetworkInterface);
|
||||
if (network &&
|
||||
network.type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_DUN) {
|
||||
this.tetheringSettings[SETTINGS_DUN_REQUIRED] =
|
||||
libcutils.property_get("ro.tethering.dun_required") === "1";
|
||||
}
|
||||
break;
|
||||
case TOPIC_XPCOM_SHUTDOWN:
|
||||
Services.obs.removeObserver(this, TOPIC_XPCOM_SHUTDOWN);
|
||||
Services.obs.removeObserver(this, TOPIC_MOZSETTINGS_CHANGED);
|
||||
Services.obs.removeObserver(this, TOPIC_CONNECTION_STATE_CHANGED);
|
||||
Services.obs.removeObserver(this, TOPIC_INTERFACE_REGISTERED);
|
||||
Services.obs.removeObserver(this, TOPIC_INTERFACE_UNREGISTERED);
|
||||
Services.prefs.removeObserver(PREF_NETWORK_DEBUG_ENABLED, this);
|
||||
Services.prefs.removeObserver(PREF_MANAGE_OFFLINE_STATUS, this);
|
||||
|
||||
this.dunConnectTimer.cancel();
|
||||
this.dunRetryTimer.cancel();
|
||||
break;
|
||||
case PREF_MANAGE_OFFLINE_STATUS:
|
||||
try {
|
||||
this._manageOfflineStatus =
|
||||
Services.prefs.getBoolPref(PREF_MANAGE_OFFLINE_STATUS);
|
||||
} catch(ex) {
|
||||
// Ignore.
|
||||
}
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
@@ -361,7 +363,8 @@ TetheringService.prototype = {
|
||||
},
|
||||
|
||||
getNetworkInfo: function(aType, aServiceId) {
|
||||
for each (let networkInfo in gNetworkManager.allNetworkInfo) {
|
||||
for (let networkId in gNetworkManager.allNetworkInfo) {
|
||||
let networkInfo = gNetworkManager.allNetworkInfo[networkId];
|
||||
if (networkInfo.type == aType) {
|
||||
try {
|
||||
if (networkInfo instanceof Ci.nsIRilNetworkInfo) {
|
||||
@@ -604,10 +607,31 @@ TetheringService.prototype = {
|
||||
|
||||
this._wifiTetheringRequestOngoing = true;
|
||||
gNetworkService.setWifiTethering(aEnable, aConfig, (aError) => {
|
||||
// Disconnect dun on error or when wifi tethering is disabled.
|
||||
if (this.tetheringSettings[SETTINGS_DUN_REQUIRED] &&
|
||||
(!aEnable || aError)) {
|
||||
this.handleDunConnection(false);
|
||||
// Change the tethering state to WIFI if there is no error.
|
||||
if (aEnable && !aError) {
|
||||
this.state = Ci.nsITetheringService.TETHERING_STATE_WIFI;
|
||||
} else {
|
||||
// If wifi thethering is disable, or any error happens,
|
||||
// then consider the following statements.
|
||||
|
||||
// Check whether the state is USB now or not. If no then just change
|
||||
// it to INACTIVE, if yes then just keep it.
|
||||
// It means that don't let the disable or error of WIFI affect
|
||||
// the original active state.
|
||||
if (this.state != Ci.nsITetheringService.TETHERING_STATE_USB) {
|
||||
this.state = Ci.nsITetheringService.TETHERING_STATE_INACTIVE;
|
||||
}
|
||||
|
||||
// Disconnect dun on error or when wifi tethering is disabled.
|
||||
if (this.tetheringSettings[SETTINGS_DUN_REQUIRED]) {
|
||||
this.handleDunConnection(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (this._manageOfflineStatus) {
|
||||
Services.io.offline = !this.isAnyConnected() &&
|
||||
(this.state ===
|
||||
Ci.nsITetheringService.TETHERING_STATE_INACTIVE);
|
||||
}
|
||||
|
||||
let resetSettings = aError;
|
||||
@@ -644,6 +668,10 @@ TetheringService.prototype = {
|
||||
return;
|
||||
}
|
||||
|
||||
// Re-check again, test cases set this property later.
|
||||
this.tetheringSettings[SETTINGS_DUN_REQUIRED] =
|
||||
libcutils.property_get("ro.tethering.dun_required") === "1";
|
||||
|
||||
if (!aEnable) {
|
||||
this.enableWifiTethering(false, aConfig, aCallback);
|
||||
return;
|
||||
@@ -738,19 +766,36 @@ TetheringService.prototype = {
|
||||
// Skip others request when we found an error.
|
||||
this._usbTetheringRequestCount = 0;
|
||||
this._usbTetheringAction = TETHERING_STATE_IDLE;
|
||||
// If the thethering state is WIFI now, then just keep it,
|
||||
// if not, just change the state to INACTIVE.
|
||||
// It means that don't let the error of USB affect the original active state.
|
||||
if (this.state != Ci.nsITetheringService.TETHERING_STATE_WIFI) {
|
||||
this.state = Ci.nsITetheringService.TETHERING_STATE_INACTIVE;
|
||||
}
|
||||
if (this.tetheringSettings[SETTINGS_DUN_REQUIRED]) {
|
||||
this.handleDunConnection(false);
|
||||
}
|
||||
} else {
|
||||
if (aEnable) {
|
||||
this._usbTetheringAction = TETHERING_STATE_ACTIVE;
|
||||
this.state = Ci.nsITetheringService.TETHERING_STATE_USB;
|
||||
} else {
|
||||
this._usbTetheringAction = TETHERING_STATE_IDLE;
|
||||
// If the state is now WIFI, don't let the disable of USB affect it.
|
||||
if (this.state != Ci.nsITetheringService.TETHERING_STATE_WIFI) {
|
||||
this.state = Ci.nsITetheringService.TETHERING_STATE_INACTIVE;
|
||||
}
|
||||
if (this.tetheringSettings[SETTINGS_DUN_REQUIRED]) {
|
||||
this.handleDunConnection(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (this._manageOfflineStatus) {
|
||||
Services.io.offline = !this.isAnyConnected() &&
|
||||
(this.state ===
|
||||
Ci.nsITetheringService.TETHERING_STATE_INACTIVE);
|
||||
}
|
||||
|
||||
this.handleLastUsbTetheringRequest();
|
||||
}
|
||||
},
|
||||
@@ -830,6 +875,17 @@ TetheringService.prototype = {
|
||||
|
||||
callback.call(this);
|
||||
},
|
||||
|
||||
isAnyConnected: function() {
|
||||
let allNetworkInfo = gNetworkManager.allNetworkInfo;
|
||||
for (let networkId in allNetworkInfo) {
|
||||
if (allNetworkInfo.hasOwnProperty(networkId) &&
|
||||
allNetworkInfo[networkId].state === Ci.nsINetworkInfo.NETWORK_STATE_CONNECTED) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
};
|
||||
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([TetheringService]);
|
||||
|
||||
@@ -80,7 +80,7 @@ interface nsINetworkInfo : nsISupports
|
||||
[array, size_is(count), retval] out wstring dnses);
|
||||
};
|
||||
|
||||
[scriptable, uuid(9a025351-8684-4ab5-a0c1-f21a9f83d405)]
|
||||
[scriptable, uuid(8b1345fa-b34c-41b3-8d21-09f961bf8887)]
|
||||
interface nsINetworkInterface : nsISupports
|
||||
{
|
||||
/**
|
||||
@@ -97,4 +97,9 @@ interface nsINetworkInterface : nsISupports
|
||||
* The port number of the http proxy server.
|
||||
*/
|
||||
readonly attribute long httpProxyPort;
|
||||
|
||||
/*
|
||||
* The Maximun Transmit Unit for this network interface, -1 if not set.
|
||||
*/
|
||||
readonly attribute long mtu;
|
||||
};
|
||||
|
||||
@@ -2,10 +2,11 @@
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsINetworkManager.idl"
|
||||
#include "nsISupports.idl"
|
||||
|
||||
[scriptable, uuid(b44d74db-c9d6-41dd-98ae-a56918d6e6ad)]
|
||||
interface nsINetworkInfo;
|
||||
|
||||
[scriptable, uuid(55779d32-1e28-4f43-af87-09d04bc3cce9)]
|
||||
interface nsINetworkInterfaceList : nsISupports
|
||||
{
|
||||
/**
|
||||
@@ -14,10 +15,10 @@ interface nsINetworkInterfaceList : nsISupports
|
||||
long getNumberOfInterface();
|
||||
|
||||
/**
|
||||
* Get the i-th interface from the list.
|
||||
* Get the i-th interface info info from the list.
|
||||
* @param interfaceIndex index of interface, from 0 to number of interface - 1.
|
||||
*/
|
||||
nsINetworkInterface getInterface(in long interfaceIndex);
|
||||
nsINetworkInfo getInterfaceInfo(in long interfaceIndex);
|
||||
};
|
||||
|
||||
[scriptable, uuid(21d7fc8b-28c4-4a4f-a15e-1f9defbc2cec)]
|
||||
|
||||
@@ -157,7 +157,7 @@ interface nsIDhcpRequestCallback : nsISupports
|
||||
/**
|
||||
* Provide network services.
|
||||
*/
|
||||
[scriptable, uuid(5d6b1877-890a-4c7f-8403-94d57ceba6cf)]
|
||||
[scriptable, uuid(8f689d9f-30c0-4809-8bf6-87120e71f3ee)]
|
||||
interface nsINetworkService : nsISupports
|
||||
{
|
||||
const long MODIFY_ROUTE_ADD = 0;
|
||||
@@ -310,8 +310,6 @@ interface nsINetworkService : nsISupports
|
||||
* Number of elements in gateways.
|
||||
* @param gateways
|
||||
* Default gateways for setting default route.
|
||||
* @param oldInterfaceName
|
||||
* The previous network interface name.
|
||||
*
|
||||
* @param callback
|
||||
* Callback to notify the result of setting default route.
|
||||
@@ -319,7 +317,6 @@ interface nsINetworkService : nsISupports
|
||||
void setDefaultRoute(in DOMString interfaceName,
|
||||
in unsigned long count,
|
||||
[array, size_is(count)] in wstring gateways,
|
||||
in DOMString oldInterfaceName,
|
||||
in nsINativeCommandCallback callback);
|
||||
|
||||
/**
|
||||
@@ -485,9 +482,9 @@ interface nsINetworkService : nsISupports
|
||||
in nsINativeCommandCallback callback);
|
||||
|
||||
/**
|
||||
* Reset all connections
|
||||
* Reset all connections on a specified network interface.
|
||||
*
|
||||
* @param networkInterface
|
||||
* @param interfaceName
|
||||
* The network interface name which we want to reset.
|
||||
*
|
||||
* @param callback
|
||||
@@ -497,25 +494,25 @@ interface nsINetworkService : nsISupports
|
||||
in nsINativeCommandCallback callback);
|
||||
|
||||
/**
|
||||
* Create network (required to call prior to any networking operation)
|
||||
* Create network (required to call prior to any networking operation).
|
||||
*
|
||||
* @param networkInterface
|
||||
* The network interface name which we want to reset.
|
||||
* @param interfaceName
|
||||
* The network interface name which we want to create network for.
|
||||
*
|
||||
* @param callback
|
||||
* Callback to notify the result of resetting connections.
|
||||
* Callback to notify the result of creating network.
|
||||
*/
|
||||
void createNetwork(in DOMString interfaceName,
|
||||
in nsINativeCommandCallback callback);
|
||||
|
||||
/**
|
||||
* Destroy network (required to call prior to any networking operation)
|
||||
* Destroy network.
|
||||
*
|
||||
* @param networkInterface
|
||||
* The network interface name which we want to reset.
|
||||
* @param interfaceName
|
||||
* The network interface name of the network we want to destroy.
|
||||
*
|
||||
* @param callback
|
||||
* Callback to notify the result of resetting connections.
|
||||
* Callback to notify the result of destroying network.
|
||||
*/
|
||||
void destroyNetwork(in DOMString interfaceName,
|
||||
in nsINativeCommandCallback callback);
|
||||
@@ -532,4 +529,18 @@ interface nsINetworkService : nsISupports
|
||||
*
|
||||
*/
|
||||
jsval getNetId(in DOMString interfaceName);
|
||||
|
||||
/**
|
||||
* Set maximum transmission unit on a network interface.
|
||||
*
|
||||
* @param interfaceName
|
||||
* The name of the network interface that we want to set mtu.
|
||||
* @param mtu
|
||||
* Size of maximum tranmission unit.
|
||||
*
|
||||
* @param callback
|
||||
* Callback to notify the result of setting mtu.
|
||||
*/
|
||||
void setMtu(in DOMString interfaceName, in long mtu,
|
||||
in nsINativeCommandCallback callback);
|
||||
};
|
||||
|
||||
@@ -7,11 +7,20 @@
|
||||
interface nsINetworkInterface;
|
||||
interface nsIWifiTetheringCallback;
|
||||
|
||||
[scriptable, uuid(993b79df-f10e-4697-a5dc-5981cf8ff7e6)]
|
||||
[scriptable, uuid(779de2d3-6d29-4ee6-b069-6251839f757a)]
|
||||
interface nsITetheringService : nsISupports
|
||||
{
|
||||
const long TETHERING_STATE_INACTIVE = 0;
|
||||
const long TETHERING_STATE_WIFI = 1;
|
||||
const long TETHERING_STATE_USB = 2;
|
||||
|
||||
/**
|
||||
* Enable or disable Wifi Tethering
|
||||
* Current tethering state. One of the TETHERING_STATE_* constants.
|
||||
*/
|
||||
readonly attribute long state;
|
||||
|
||||
/**
|
||||
* Enable or disable Wifi Tethering.
|
||||
*
|
||||
* @param enabled
|
||||
* Boolean that indicates whether tethering should be enabled (true) or
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user