merge b2g-inbound to mozilla-central a=merge

This commit is contained in:
Carsten "Tomcat" Book 2014-07-07 15:05:36 +02:00
commit 14b7e0163c
62 changed files with 2864 additions and 795 deletions

View File

@ -19,7 +19,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="93daa354671a698634a3dc661c8c9dcb7d824c31"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="99f56d9db3cd37c684b01de6fed786421f47e2b7"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="230f11aff069d90d20fc2dc63b48e9ae3d4bdcd1"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>

View File

@ -17,7 +17,7 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="93daa354671a698634a3dc661c8c9dcb7d824c31"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="99f56d9db3cd37c684b01de6fed786421f47e2b7"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="230f11aff069d90d20fc2dc63b48e9ae3d4bdcd1"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="96cdde4b5b5d8d3785b36c3c68cd746aff3005cc"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="7f9ec13a30f1b2cc8bdb1a199b7da54b9ab8860f"/>

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="276ce45e78b09c4a4ee643646f691d22804754c1">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="93daa354671a698634a3dc661c8c9dcb7d824c31"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="99f56d9db3cd37c684b01de6fed786421f47e2b7"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="230f11aff069d90d20fc2dc63b48e9ae3d4bdcd1"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>

View File

@ -19,7 +19,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="93daa354671a698634a3dc661c8c9dcb7d824c31"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="99f56d9db3cd37c684b01de6fed786421f47e2b7"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="230f11aff069d90d20fc2dc63b48e9ae3d4bdcd1"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>

View File

@ -17,7 +17,7 @@
</project>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="93daa354671a698634a3dc661c8c9dcb7d824c31"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="99f56d9db3cd37c684b01de6fed786421f47e2b7"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="230f11aff069d90d20fc2dc63b48e9ae3d4bdcd1"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="96cdde4b5b5d8d3785b36c3c68cd746aff3005cc"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="7f9ec13a30f1b2cc8bdb1a199b7da54b9ab8860f"/>

View File

@ -4,6 +4,6 @@
"remote": "",
"branch": ""
},
"revision": "333f877ae4029d7cb1a0893f89da484d8e3cc14f",
"revision": "278dd1b102a39cf2c48f11fe3038eaf8f0779d7d",
"repo_path": "/integration/gaia-central"
}

View File

@ -17,7 +17,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="93daa354671a698634a3dc661c8c9dcb7d824c31"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="99f56d9db3cd37c684b01de6fed786421f47e2b7"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="230f11aff069d90d20fc2dc63b48e9ae3d4bdcd1"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>

View File

@ -15,7 +15,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="93daa354671a698634a3dc661c8c9dcb7d824c31"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="99f56d9db3cd37c684b01de6fed786421f47e2b7"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="230f11aff069d90d20fc2dc63b48e9ae3d4bdcd1"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>

View File

@ -17,7 +17,7 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="93daa354671a698634a3dc661c8c9dcb7d824c31"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="99f56d9db3cd37c684b01de6fed786421f47e2b7"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="230f11aff069d90d20fc2dc63b48e9ae3d4bdcd1"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="96cdde4b5b5d8d3785b36c3c68cd746aff3005cc"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="7f9ec13a30f1b2cc8bdb1a199b7da54b9ab8860f"/>

View File

@ -17,7 +17,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="93daa354671a698634a3dc661c8c9dcb7d824c31"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="99f56d9db3cd37c684b01de6fed786421f47e2b7"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="230f11aff069d90d20fc2dc63b48e9ae3d4bdcd1"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>

View File

@ -405,6 +405,8 @@
@BINPATH@/components/DOMWifiManager.manifest
@BINPATH@/components/DOMWifiP2pManager.js
@BINPATH@/components/DOMWifiP2pManager.manifest
@BINPATH@/components/EthernetManager.js
@BINPATH@/components/EthernetManager.manifest
@BINPATH@/components/NetworkInterfaceListService.js
@BINPATH@/components/NetworkInterfaceListService.manifest
@BINPATH@/components/NetworkManager.js

View File

@ -8,10 +8,10 @@ const Cu = Components.utils;
const Cc = Components.classes;
const Ci = Components.interfaces;
// This module exposes a subset of the functionnalities of the parent DOM
// Registry to content processes, to be be used from the AppsService component.
// This module exposes a subset of the functionalities of the parent DOM
// Registry to content processes, to be used from the AppsService component.
this.EXPORTED_SYMBOLS = ["DOMApplicationRegistry"];
this.EXPORTED_SYMBOLS = ["DOMApplicationRegistry", "WrappedManifestCache"];
Cu.import("resource://gre/modules/AppsUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
@ -20,54 +20,323 @@ function debug(s) {
//dump("-*- AppsServiceChild.jsm: " + s + "\n");
}
const APPS_IPC_MSG_NAMES = [
"Webapps:AddApp",
"Webapps:RemoveApp",
"Webapps:UpdateApp",
"Webapps:CheckForUpdate:Return:KO",
"Webapps:FireEvent",
"Webapps:UpdateState"
];
// A simple cache for the wrapped manifests.
this.WrappedManifestCache = {
_cache: { },
// Gets an entry from the cache, and populates the cache if needed.
get: function mcache_get(aManifestURL, aManifest, aWindow, aInnerWindowID) {
if (!aManifest) {
return;
}
if (!(aManifestURL in this._cache)) {
this._cache[aManifestURL] = { };
}
let winObjs = this._cache[aManifestURL];
if (!(aInnerWindowID in winObjs)) {
winObjs[aInnerWindowID] = Cu.cloneInto(aManifest, aWindow);
}
return winObjs[aInnerWindowID];
},
// Invalidates an entry in the cache.
evict: function mcache_evict(aManifestURL, aInnerWindowID) {
debug("Evicting manifest " + aManifestURL + " window ID " +
aInnerWindowID);
if (aManifestURL in this._cache) {
let winObjs = this._cache[aManifestURL];
if (aInnerWindowID in winObjs) {
delete winObjs[aInnerWindowID];
}
if (Object.keys(winObjs).length == 0) {
delete this._cache[aManifestURL];
}
}
},
observe: function(aSubject, aTopic, aData) {
// Clear the cache on memory pressure.
this._cache = { };
Cu.forceGC();
},
init: function() {
Services.obs.addObserver(this, "memory-pressure", false);
}
};
this.WrappedManifestCache.init();
// DOMApplicationRegistry keeps a cache containing a list of apps in the device.
// This information is updated with the data received from the main process and
// it is queried by the DOM objects to set their state.
// This module handle all the messages broadcasted from the parent process,
// including DOM events, which are dispatched to the corresponding DOM objects.
this.DOMApplicationRegistry = {
// DOMApps will hold a list of arrays of weak references to
// mozIDOMApplication objects indexed by manifest URL.
DOMApps: {},
ready: false,
webapps: null,
init: function init() {
debug("init");
this.cpmm = Cc["@mozilla.org/childprocessmessagemanager;1"]
.getService(Ci.nsISyncMessageSender);
["Webapps:AddApp", "Webapps:RemoveApp"].forEach((function(aMsgName) {
APPS_IPC_MSG_NAMES.forEach((function(aMsgName) {
this.cpmm.addMessageListener(aMsgName, this);
}).bind(this));
// We need to prime the cache with the list of apps.
// XXX shoud we do this async and block callers if it's not yet there?
this.webapps = this.cpmm.sendSyncMessage("Webapps:GetList", { })[0];
this.cpmm.sendAsyncMessage("Webapps:RegisterForMessages", {
messages: APPS_IPC_MSG_NAMES
});
// We need to prime the cache with the list of apps.
let list = this.cpmm.sendSyncMessage("Webapps:GetList", { })[0];
this.webapps = list.webapps;
// We need a fast mapping from localId -> app, so we add an index.
// We also add the manifest to the app object.
this.localIdIndex = { };
for (let id in this.webapps) {
let app = this.webapps[id];
this.localIdIndex[app.localId] = app;
app.manifest = list.manifests[id];
}
Services.obs.addObserver(this, "xpcom-shutdown", false);
},
observe: function(aSubject, aTopic, aData) {
// cpmm.addMessageListener causes the DOMApplicationRegistry object to live
// forever if we don't clean up properly.
// cpmm.addMessageListener causes the DOMApplicationRegistry object to
// live forever if we don't clean up properly.
this.webapps = null;
["Webapps:AddApp", "Webapps:RemoveApp"].forEach((function(aMsgName) {
this.DOMApps = null;
APPS_IPC_MSG_NAMES.forEach((aMsgName) => {
this.cpmm.removeMessageListener(aMsgName, this);
}).bind(this));
});
this.cpmm.sendAsyncMessage("Webapps:UnregisterForMessages",
APPS_IPC_MSG_NAMES)
},
receiveMessage: function receiveMessage(aMessage) {
debug("Received " + aMessage.name + " message.");
let msg = aMessage.json;
let msg = aMessage.data;
switch (aMessage.name) {
case "Webapps:AddApp":
this.webapps[msg.id] = msg.app;
this.localIdIndex[msg.app.localId] = msg.app;
if (msg.manifest) {
this.webapps[msg.id].manifest = msg.manifest;
}
break;
case "Webapps:RemoveApp":
delete this.DOMApps[this.webapps[msg.id].manifestURL];
delete this.localIdIndex[this.webapps[msg.id].localId];
delete this.webapps[msg.id];
break;
case "Webapps:UpdateApp":
let app = this.webapps[msg.oldId];
if (!app) {
return;
}
if (msg.app) {
for (let prop in msg.app) {
app[prop] = msg.app[prop];
}
}
this.webapps[msg.newId] = app;
this.localIdIndex[app.localId] = app;
delete this.webapps[msg.oldId];
let apps = this.DOMApps[msg.app.manifestURL];
if (!apps) {
return;
}
for (let i = 0; i < apps.length; i++) {
let domApp = apps[i].get();
if (!domApp) {
apps.splice(i);
continue;
}
domApp._proxy = new Proxy(domApp, {
get: function(target, prop) {
if (!DOMApplicationRegistry.webapps[msg.newId]) {
return;
}
return DOMApplicationRegistry.webapps[msg.newId][prop];
},
set: function(target, prop, val) {
if (!DOMApplicationRegistry.webapps[msg.newId]) {
return;
}
DOMApplicationRegistry.webapps[msg.newId][prop] = val;
return;
},
});
}
break;
case "Webapps:FireEvent":
this._fireEvent(aMessage);
break;
case "Webapps:UpdateState":
this._updateState(msg);
break;
case "Webapps:CheckForUpdate:Return:KO":
let DOMApps = this.DOMApps[msg.manifestURL];
if (!DOMApps || !msg.requestID) {
return;
}
DOMApps.forEach((DOMApp) => {
let domApp = DOMApp.get();
if (domApp && msg.requestID) {
domApp._fireRequestResult(aMessage, true /* aIsError */);
}
});
break;
}
},
/**
* mozIDOMApplication management
*/
// Every time a DOM app is created, we save a weak reference to it that will
// be used to dispatch events and fire request results.
addDOMApp: function(aApp, aManifestURL, aId) {
let weakRef = Cu.getWeakReference(aApp);
if (!this.DOMApps[aManifestURL]) {
this.DOMApps[aManifestURL] = [];
}
let apps = this.DOMApps[aManifestURL];
// Get rid of dead weak references.
for (let i = 0; i < apps.length; i++) {
if (!apps[i].get()) {
apps.splice(i);
}
}
apps.push(weakRef);
// Each DOM app contains a proxy object used to build their state. We
// return the handler for this proxy object with traps to get and set
// app properties kept in the DOMApplicationRegistry app cache.
return {
get: function(target, prop) {
if (!DOMApplicationRegistry.webapps[aId]) {
return;
}
return DOMApplicationRegistry.webapps[aId][prop];
},
set: function(target, prop, val) {
if (!DOMApplicationRegistry.webapps[aId]) {
return;
}
DOMApplicationRegistry.webapps[aId][prop] = val;
return;
},
};
},
_fireEvent: function(aMessage) {
let msg = aMessage.data;
debug("_fireEvent " + JSON.stringify(msg));
if (!this.DOMApps || !msg.manifestURL || !msg.eventType) {
return;
}
let DOMApps = this.DOMApps[msg.manifestURL];
if (!DOMApps) {
return;
}
// The parent might ask childs to trigger more than one event in one
// shot, so in order to avoid needless IPC we allow an array for the
// 'eventType' IPC message field.
if (!Array.isArray(msg.eventType)) {
msg.eventType = [msg.eventType];
}
DOMApps.forEach((DOMApp) => {
let domApp = DOMApp.get();
if (!domApp) {
return;
}
msg.eventType.forEach((aEventType) => {
if ('on' + aEventType in domApp) {
domApp._fireEvent(aEventType);
}
});
if (msg.requestID) {
aMessage.data.result = msg.manifestURL;
domApp._fireRequestResult(aMessage);
}
});
},
_updateState: function(aMessage) {
if (!this.DOMApps || !aMessage.id) {
return;
}
let app = this.webapps[aMessage.id];
if (!app) {
return;
}
if (aMessage.app) {
for (let prop in aMessage.app) {
app[prop] = aMessage.app[prop];
}
}
if ("error" in aMessage) {
app.downloadError = aMessage.error;
}
if (aMessage.manifest) {
app.manifest = aMessage.manifest;
// Evict the wrapped manifest cache for all the affected DOM objects.
let DOMApps = this.DOMApps[app.manifestURL];
if (!DOMApps) {
return;
}
DOMApps.forEach((DOMApp) => {
let domApp = DOMApp.get();
if (!domApp) {
return;
}
WrappedManifestCache.evict(app.manifestURL, domApp.innerWindowID);
});
}
},
/**
* nsIAppsService API
*/
getAppByManifestURL: function getAppByManifestURL(aManifestURL) {
debug("getAppByManifestURL " + aManifestURL);
return AppsUtils.getAppByManifestURL(this.webapps, aManifestURL);
@ -89,7 +358,7 @@ this.DOMApplicationRegistry = {
},
getAppByLocalId: function getAppByLocalId(aLocalId) {
debug("getAppByLocalId " + aLocalId);
debug("getAppByLocalId " + aLocalId + " - ready: " + this.ready);
let app = this.localIdIndex[aLocalId];
if (!app) {
debug("Ouch, No app!");

View File

@ -12,6 +12,7 @@ Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/DOMRequestHelper.jsm");
Cu.import("resource://gre/modules/AppsUtils.jsm");
Cu.import("resource://gre/modules/BrowserElementPromptService.jsm");
Cu.import("resource://gre/modules/AppsServiceChild.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
"@mozilla.org/childprocessmessagemanager;1",
@ -278,50 +279,9 @@ WebappsRegistry.prototype = {
* mozIDOMApplication object
*/
// A simple cache for the wrapped manifests.
let manifestCache = {
_cache: { },
// Gets an entry from the cache, and populates the cache if needed.
get: function mcache_get(aManifestURL, aManifest, aWindow, aInnerWindowID) {
if (!(aManifestURL in this._cache)) {
this._cache[aManifestURL] = { };
}
let winObjs = this._cache[aManifestURL];
if (!(aInnerWindowID in winObjs)) {
winObjs[aInnerWindowID] = Cu.cloneInto(aManifest, aWindow);
}
return winObjs[aInnerWindowID];
},
// Invalidates an entry in the cache.
evict: function mcache_evict(aManifestURL, aInnerWindowID) {
if (aManifestURL in this._cache) {
let winObjs = this._cache[aManifestURL];
if (aInnerWindowID in winObjs) {
delete winObjs[aInnerWindowID];
}
if (Object.keys(winObjs).length == 0) {
delete this._cache[aManifestURL];
}
}
},
observe: function(aSubject, aTopic, aData) {
// Clear the cache on memory pressure.
this._cache = { };
},
init: function() {
Services.obs.addObserver(this, "memory-pressure", false);
}
};
function createApplicationObject(aWindow, aApp) {
let app = Cc["@mozilla.org/webapps/application;1"].createInstance(Ci.mozIDOMApplication);
let app = Cc["@mozilla.org/webapps/application;1"]
.createInstance(Ci.mozIDOMApplication);
app.wrappedJSObject.init(aWindow, aApp);
return app;
}
@ -334,27 +294,12 @@ WebappsApplication.prototype = {
__proto__: DOMRequestIpcHelper.prototype,
init: function(aWindow, aApp) {
let proxyHandler = DOMApplicationRegistry.addDOMApp(this,
aApp.manifestURL,
aApp.id);
this._proxy = new Proxy(this, proxyHandler);
this._window = aWindow;
let principal = this._window.document.nodePrincipal;
this._appStatus = principal.appStatus;
this.origin = aApp.origin;
this._manifest = aApp.manifest;
this._updateManifest = aApp.updateManifest;
this.manifestURL = aApp.manifestURL;
this.receipts = aApp.receipts;
this.installOrigin = aApp.installOrigin;
this.installTime = aApp.installTime;
this.installState = aApp.installState || "installed";
this.removable = aApp.removable;
this.lastUpdateCheck = aApp.lastUpdateCheck ? aApp.lastUpdateCheck
: Date.now();
this.updateTime = aApp.updateTime ? aApp.updateTime
: aApp.installTime;
this.progress = NaN;
this.downloadAvailable = aApp.downloadAvailable;
this.downloading = aApp.downloading;
this.readyToApplyDownload = aApp.readyToApplyDownload;
this.downloadSize = aApp.downloadSize || 0;
this._onprogress = null;
this._ondownloadsuccess = null;
@ -362,40 +307,83 @@ WebappsApplication.prototype = {
this._ondownloadavailable = null;
this._ondownloadapplied = null;
this._downloadError = null;
this.initDOMRequestHelper(aWindow);
},
this.initDOMRequestHelper(aWindow, [
{ name: "Webapps:CheckForUpdate:Return:KO", weakRef: true },
{ name: "Webapps:Connect:Return:OK", weakRef: true },
{ name: "Webapps:Connect:Return:KO", weakRef: true },
{ name: "Webapps:FireEvent", weakRef: true },
{ name: "Webapps:GetConnections:Return:OK", weakRef: true },
{ name: "Webapps:UpdateState", weakRef: true }
]);
get _appStatus() {
return this._proxy.appStatus;
},
cpmm.sendAsyncMessage("Webapps:RegisterForMessages", {
messages: ["Webapps:FireEvent",
"Webapps:UpdateState"],
app: {
id: this.id,
manifestURL: this.manifestURL,
installState: this.installState,
downloading: this.downloading
}
});
get downloadAvailable() {
return this._proxy.downloadAvailable;
},
get downloading() {
return this._proxy.downloading;
},
get downloadSize() {
return this._proxy.downloadSize;
},
get installOrigin() {
return this._proxy.installOrigin;
},
get installState() {
return this._proxy.installState;
},
get installTime() {
return this._proxy.installTime;
},
get lastUpdateCheck() {
return this._proxy.lastUpdateCheck;
},
get manifestURL() {
return this._proxy.manifestURL;
},
get origin() {
return this._proxy.origin;
},
get progress() {
return this._proxy.progress;
},
get readyToApplyDownload() {
return this._proxy.readyToApplyDownload;
},
get receipts() {
return this._proxy.receipts;
},
set receipts(aReceipts) {
this._proxy.receipts = aReceipts;
},
get removable() {
return this._proxy.removable;
},
get updateTime() {
return this._proxy.updateTime;
},
get manifest() {
return manifestCache.get(this.manifestURL,
this._manifest,
this._window,
this.innerWindowID);
return WrappedManifestCache.get(this.manifestURL,
this._proxy.manifest,
this._window,
this.innerWindowID);
},
get updateManifest() {
return this.updateManifest =
this._updateManifest ? Cu.cloneInto(this._updateManifest, this._window)
: null;
return this._proxy.updateManifest ?
Cu.cloneInto(this._proxy.updateManifest, this._window) : null;
},
set onprogress(aCallback) {
@ -440,10 +428,10 @@ WebappsApplication.prototype = {
get downloadError() {
// Only return DOMError when we have an error.
if (!this._downloadError) {
if (!this._proxy.downloadError) {
return null;
}
return new this._window.DOMError(this._downloadError);
return new this._window.DOMError(this._proxy.downloadError);
},
download: function() {
@ -485,12 +473,11 @@ WebappsApplication.prototype = {
BrowserElementPromptService.getBrowserElementChildForWindow(this._window);
if (browserChild) {
this.addMessageListeners("Webapps:ClearBrowserData:Return");
browserChild.messageManager.sendAsyncMessage(
"Webapps:ClearBrowserData",
{ manifestURL: this.manifestURL,
oid: this._id,
requestID: this.getRequestId(request) }
);
browserChild.messageManager.sendAsyncMessage("Webapps:ClearBrowserData", {
manifestURL: this.manifestURL,
oid: this._id,
requestID: this.getRequestId(request)
});
} else {
Services.DOMRequest.fireErrorAsync(request, "NO_CLEARABLE_BROWSER");
}
@ -498,28 +485,33 @@ WebappsApplication.prototype = {
},
connect: function(aKeyword, aRules) {
this.addMessageListeners(["Webapps:Connect:Return:OK",
"Webapps:Connect:Return:KO"]);
return this.createPromise(function (aResolve, aReject) {
cpmm.sendAsyncMessage("Webapps:Connect",
{ keyword: aKeyword,
rules: aRules,
manifestURL: this.manifestURL,
outerWindowID: this._id,
requestID: this.getPromiseResolverId({
resolve: aResolve,
reject: aReject
})});
cpmm.sendAsyncMessage("Webapps:Connect", {
keyword: aKeyword,
rules: aRules,
manifestURL: this.manifestURL,
outerWindowID: this._id,
requestID: this.getPromiseResolverId({
resolve: aResolve,
reject: aReject
})
});
}.bind(this));
},
getConnections: function() {
this.addMessageListeners("Webapps:GetConnections:Return:OK");
return this.createPromise(function (aResolve, aReject) {
cpmm.sendAsyncMessage("Webapps:GetConnections",
{ manifestURL: this.manifestURL,
outerWindowID: this._id,
requestID: this.getPromiseResolverId({
resolve: aResolve,
reject: aReject
})});
cpmm.sendAsyncMessage("Webapps:GetConnections", {
manifestURL: this.manifestURL,
outerWindowID: this._id,
requestID: this.getPromiseResolverId({
resolve: aResolve,
reject: aReject
})
});
}.bind(this));
},
@ -568,12 +560,7 @@ WebappsApplication.prototype = {
uninit: function() {
this._onprogress = null;
cpmm.sendAsyncMessage("Webapps:UnregisterForMessages", [
"Webapps:FireEvent",
"Webapps:UpdateState"
]);
manifestCache.evict(this.manifestURL, this.innerWindowID);
WrappedManifestCache.evict(this.manifestURL, this.innerWindowID);
},
_fireEvent: function(aName) {
@ -590,22 +577,16 @@ WebappsApplication.prototype = {
}
},
_updateState: function(aMsg) {
if (aMsg.app) {
for (let prop in aMsg.app) {
this[prop] = aMsg.app[prop];
}
_fireRequestResult: function(aMessage, aIsError) {
let req;
let msg = aMessage.data;
req = this.takeRequest(msg.requestID);
if (!req) {
return;
}
// Intentional use of 'in' so we unset the error if this is explicitly null.
if ('error' in aMsg) {
this._downloadError = aMsg.error;
}
if (aMsg.manifest) {
this._manifest = aMsg.manifest;
manifestCache.evict(this.manifestURL, this.innerWindowID);
}
aIsError ? Services.DOMRequest.fireError(req, msg.error)
: Services.DOMRequest.fireSuccess(req, msg.result);
},
receiveMessage: function(aMessage) {
@ -619,10 +600,7 @@ WebappsApplication.prototype = {
req = this.takeRequest(msg.requestID);
}
// ondownload* callbacks should be triggered on all app instances
if ((msg.oid != this._id || !req) &&
aMessage.name !== "Webapps:FireEvent" &&
aMessage.name !== "Webapps:UpdateState") {
if (msg.oid !== this._id || !req) {
return;
}
@ -637,51 +615,13 @@ WebappsApplication.prototype = {
"Webapps:Launch:Return:KO"]);
Services.DOMRequest.fireSuccess(req, null);
break;
case "Webapps:CheckForUpdate:Return:KO":
Services.DOMRequest.fireError(req, msg.error);
break;
case "Webapps:FireEvent":
if (msg.manifestURL != this.manifestURL) {
return;
}
// The parent might ask childs to trigger more than one event in one
// shot, so in order to avoid needless IPC we allow an array for the
// 'eventType' IPC message field.
if (!Array.isArray(msg.eventType)) {
msg.eventType = [msg.eventType];
}
msg.eventType.forEach((aEventType) => {
// If we are in a successful state clear any past errors.
if (aEventType === 'downloadapplied' ||
aEventType === 'downloadsuccess') {
this._downloadError = null;
}
if ("_on" + aEventType in this) {
this._fireEvent(aEventType);
} else {
dump("Unsupported event type " + aEventType + "\n");
}
});
if (req) {
Services.DOMRequest.fireSuccess(req, this.manifestURL);
}
break;
case "Webapps:UpdateState":
if (msg.manifestURL != this.manifestURL) {
return;
}
this._updateState(msg);
break;
case "Webapps:ClearBrowserData:Return":
this.removeMessageListeners(aMessage.name);
Services.DOMRequest.fireSuccess(req, null);
break;
case "Webapps:Connect:Return:OK":
this.removeMessageListeners(["Webapps:Connect:Return:OK",
"Webapps:Connect:Return:KO"]);
let messagePorts = [];
msg.messagePortIDs.forEach((aPortID) => {
let port = new this._window.MozInterAppMessagePort(aPortID);
@ -690,9 +630,12 @@ WebappsApplication.prototype = {
req.resolve(messagePorts);
break;
case "Webapps:Connect:Return:KO":
this.removeMessageListeners(["Webapps:Connect:Return:OK",
"Webapps:Connect:Return:KO"]);
req.reject("No connections registered");
break;
case "Webapps:GetConnections:Return:OK":
this.removeMessageListeners(aMessage.name);
let connections = [];
msg.connections.forEach((aConnection) => {
let connection =
@ -874,12 +817,8 @@ WebappsApplicationMgmt.prototype = {
break;
case "Webapps:Uninstall:Broadcast:Return:OK":
if (this._onuninstall) {
let detail = {
manifestURL: msg.manifestURL,
origin: msg.origin
};
let event = new this._window.MozApplicationEvent("applicationuninstall",
{ application : createApplicationObject(this._window, detail) });
{ application : createApplicationObject(this._window, msg) });
this._onuninstall.handleEvent(event);
}
break;
@ -908,7 +847,5 @@ WebappsApplicationMgmt.prototype = {
classDescription: "Webapps Application Mgmt"})
}
manifestCache.init();
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([WebappsRegistry,
WebappsApplication]);

View File

@ -1141,8 +1141,8 @@ this.DOMApplicationRegistry = {
this.removeMessageListener(["Webapps:Internal:AllMessages"], mm);
break;
case "Webapps:GetList":
this.addMessageListener(["Webapps:AddApp", "Webapps:RemoveApp"], null, mm);
return this.webapps;
return this.doGetList();
break;
case "Webapps:Download":
this.startDownload(msg.manifestURL);
break;
@ -1245,6 +1245,38 @@ this.DOMApplicationRegistry = {
return deferred.promise;
},
/**
* Returns the full list of apps and manifests.
*/
doGetList: function() {
let tmp = [];
for (let id in this.webapps) {
tmp.push({ id: id });
}
let res = {};
let done = false;
this._readManifests(tmp).then(
function(manifests) {
manifests.forEach((item) => {
res[item.id] = item.manifest;
});
done = true;
}
);
let thread = Services.tm.currentThread;
while (!done) {
//debug("before processNextEvent");
thread.processNextEvent(/* mayWait */ true);
//after("before processNextEvent");
}
return { webapps: this.webapps, manifests: res };
},
doLaunch: function (aData, aMm) {
this.launch(
aData.manifestURL,
@ -1330,7 +1362,7 @@ this.DOMApplicationRegistry = {
downloading: false
},
error: error,
manifestURL: app.manifestURL,
id: app.id
})
this.broadcastMessage("Webapps:FireEvent", {
eventType: "downloaderror",
@ -1361,7 +1393,7 @@ this.DOMApplicationRegistry = {
if (!app.downloadAvailable) {
this.broadcastMessage("Webapps:UpdateState", {
error: "NO_DOWNLOAD_AVAILABLE",
manifestURL: app.manifestURL
id: app.id
});
this.broadcastMessage("Webapps:FireEvent", {
eventType: "downloaderror",
@ -1409,7 +1441,7 @@ this.DOMApplicationRegistry = {
this.broadcastMessage("Webapps:UpdateState", {
app: app,
manifest: jsonManifest,
manifestURL: aManifestURL
id: app.id
});
this.broadcastMessage("Webapps:FireEvent", {
eventType: "downloadsuccess",
@ -1453,7 +1485,7 @@ this.DOMApplicationRegistry = {
this.broadcastMessage("Webapps:UpdateState", {
app: app,
manifestURL: aManifestURL
id: app.id
});
this.broadcastMessage("Webapps:FireEvent", {
eventType: "downloadsuccess",
@ -1555,7 +1587,7 @@ this.DOMApplicationRegistry = {
this.broadcastMessage("Webapps:UpdateState", {
app: app,
manifest: newManifest,
manifestURL: app.manifestURL
id: app.id
});
this.broadcastMessage("Webapps:FireEvent", {
eventType: "downloadapplied",
@ -1594,7 +1626,7 @@ this.DOMApplicationRegistry = {
installState: aApp.installState,
progress: 0
},
manifestURL: aApp.manifestURL
id: aApp.id
});
let cacheUpdate = updateSvc.scheduleAppUpdate(
appcacheURI, docURI, aApp.localId, false, aProfileDir);
@ -1644,6 +1676,7 @@ this.DOMApplicationRegistry = {
debug("checkForUpdate for " + aData.manifestURL);
function sendError(aError) {
debug("checkForUpdate error " + aError);
aData.error = aError;
aMm.sendAsyncMessage("Webapps:CheckForUpdate:Return:KO", aData);
}
@ -1673,8 +1706,7 @@ this.DOMApplicationRegistry = {
// then we can't have an update.
if (app.origin.startsWith("app://") &&
app.manifestURL.startsWith("app://")) {
aData.error = "NOT_UPDATABLE";
aMm.sendAsyncMessage("Webapps:CheckForUpdate:Return:KO", aData);
sendError("NOT_UPDATABLE");
return;
}
@ -1691,8 +1723,7 @@ this.DOMApplicationRegistry = {
if (onlyCheckAppCache) {
// Bail out for packaged apps.
if (app.origin.startsWith("app://")) {
aData.error = "NOT_UPDATABLE";
aMm.sendAsyncMessage("Webapps:CheckForUpdate:Return:KO", aData);
sendError("NOT_UPDATABLE");
return;
}
@ -1700,8 +1731,7 @@ this.DOMApplicationRegistry = {
this._readManifests([{ id: id }]).then((aResult) => {
let manifest = aResult[0].manifest;
if (!manifest.appcache_path) {
aData.error = "NOT_UPDATABLE";
aMm.sendAsyncMessage("Webapps:CheckForUpdate:Return:KO", aData);
sendError("NOT_UPDATABLE");
return;
}
@ -1717,7 +1747,7 @@ this.DOMApplicationRegistry = {
this._saveApps().then(() => {
this.broadcastMessage("Webapps:UpdateState", {
app: app,
manifestURL: app.manifestURL
id: app.id
});
this.broadcastMessage("Webapps:FireEvent", {
eventType: "downloadavailable",
@ -1726,8 +1756,7 @@ this.DOMApplicationRegistry = {
});
});
} else {
aData.error = "NOT_UPDATABLE";
aMm.sendAsyncMessage("Webapps:CheckForUpdate:Return:KO", aData);
sendError("NOT_UPDATABLE");
}
}
};
@ -1787,7 +1816,7 @@ this.DOMApplicationRegistry = {
: "downloadapplied";
aMm.sendAsyncMessage("Webapps:UpdateState", {
app: app,
manifestURL: app.manifestURL
id: app.id
});
aMm.sendAsyncMessage("Webapps:FireEvent", {
eventType: eventType,
@ -1814,7 +1843,7 @@ this.DOMApplicationRegistry = {
: "downloadapplied";
aMm.sendAsyncMessage("Webapps:UpdateState", {
app: app,
manifestURL: app.manifestURL
id: app.id
});
aMm.sendAsyncMessage("Webapps:FireEvent", {
eventType: eventType,
@ -1923,7 +1952,7 @@ this.DOMApplicationRegistry = {
this.broadcastMessage("Webapps:UpdateState", {
app: aApp,
manifestURL: aApp.manifestURL
id: aApp.id
});
this.broadcastMessage("Webapps:FireEvent", {
eventType: "downloadavailable",
@ -1989,7 +2018,7 @@ this.DOMApplicationRegistry = {
this.broadcastMessage("Webapps:UpdateState", {
app: aApp,
manifest: aApp.manifest,
manifestURL: aApp.manifestURL
id: aApp.id
});
this.broadcastMessage("Webapps:FireEvent", {
eventType: "downloadapplied",
@ -2023,7 +2052,7 @@ this.DOMApplicationRegistry = {
this.broadcastMessage("Webapps:UpdateState", {
app: aApp,
manifest: aApp.manifest,
manifestURL: aApp.manifestURL
id: aApp.id
});
this.broadcastMessage("Webapps:FireEvent", {
eventType: eventType,
@ -2450,7 +2479,8 @@ this.DOMApplicationRegistry = {
}
this._saveApps().then(() => {
this.broadcastMessage("Webapps:AddApp", { id: app.id, app: app });
this.broadcastMessage("Webapps:AddApp",
{ id: app.id, app: app, manifest: aManifest });
});
}),
@ -2550,6 +2580,8 @@ this.DOMApplicationRegistry = {
// saved in the registry.
yield this._saveApps();
aData.isPackage ? appObject.updateManifest = jsonManifest :
appObject.manifest = jsonManifest;
this.broadcastMessage("Webapps:AddApp", { id: id, app: appObject });
// The presence of a requestID means that we have a page to update.
@ -2626,7 +2658,8 @@ this.DOMApplicationRegistry = {
delete this._manifestCache[aId];
}
this.broadcastMessage("Webapps:AddApp", { id: aId, app: aNewApp });
this.broadcastMessage("Webapps:AddApp",
{ id: aId, app: aNewApp, manifest: aManifest });
Services.obs.notifyObservers(null, "webapps-installed",
JSON.stringify({ manifestURL: aNewApp.manifestURL }));
@ -2760,14 +2793,11 @@ this.DOMApplicationRegistry = {
oldApp,
aNewApp);
AppDownloadManager.add(
aNewApp.manifestURL,
{
channel: requestChannel,
appId: id,
previousState: aIsUpdate ? "installed" : "pending"
}
);
AppDownloadManager.add(aNewApp.manifestURL, {
channel: requestChannel,
appId: id,
previousState: aIsUpdate ? "installed" : "pending"
});
// We set the 'downloading' flag to true right before starting the fetch.
oldApp.downloading = true;
@ -2787,7 +2817,7 @@ this.DOMApplicationRegistry = {
// Clear any previous download errors.
error: null,
app: oldApp,
manifestURL: aNewApp.manifestURL
id: id
});
let zipFile = yield this._getPackage(requestChannel, id, oldApp, aNewApp);
@ -2802,7 +2832,7 @@ this.DOMApplicationRegistry = {
// We send an "applied" event right away so code awaiting that event
// can proceed to access the app. We also throw an error to alert
// the caller that the package wasn't downloaded.
this._sendAppliedEvent(aNewApp, oldApp, id);
this._sendAppliedEvent(oldApp);
throw new Error("PACKAGE_UNCHANGED");
}
@ -2942,7 +2972,7 @@ this.DOMApplicationRegistry = {
app: {
progress: aProgress
},
manifestURL: aNewApp.manifestURL
id: aNewApp.id
});
this.broadcastMessage("Webapps:FireEvent", {
eventType: "progress",
@ -3059,27 +3089,24 @@ this.DOMApplicationRegistry = {
* something similar after updating the app, and we could refactor both cases
* to use the same code to send the "applied" event.
*
* @param aNewApp {Object} the new app data
* @param aOldApp {Object} the currently stored app data
* @param aId {String} the unique id of the app
* @param aApp {Object} app data
*/
_sendAppliedEvent: function(aNewApp, aOldApp, aId) {
aOldApp.downloading = false;
aOldApp.downloadAvailable = false;
aOldApp.downloadSize = 0;
aOldApp.installState = "installed";
aOldApp.readyToApplyDownload = false;
if (aOldApp.staged && aOldApp.staged.manifestHash) {
_sendAppliedEvent: function(aApp) {
aApp.downloading = false;
aApp.downloadAvailable = false;
aApp.downloadSize = 0;
aApp.installState = "installed";
aApp.readyToApplyDownload = false;
if (aApp.staged && aApp.staged.manifestHash) {
// If we're here then the manifest has changed but the package
// hasn't. Let's clear this, so we don't keep offering
// a bogus update to the user
aOldApp.manifestHash = aOldApp.staged.manifestHash;
aOldApp.etag = aOldApp.staged.etag || aOldApp.etag;
aOldApp.staged = {};
// Move the staged update manifest to a non staged one.
aApp.manifestHash = aApp.staged.manifestHash;
aApp.etag = aApp.staged.etag || aApp.etag;
aApp.staged = {};
// Move the staged update manifest to a non staged one.
try {
let staged = this._getAppDir(aId);
let staged = this._getAppDir(aApp.id);
staged.append("staged-update.webapp");
staged.moveTo(staged.parent, "update.webapp");
} catch (ex) {
@ -3090,15 +3117,15 @@ this.DOMApplicationRegistry = {
// Save the updated registry, and cleanup the tmp directory.
this._saveApps().then(() => {
this.broadcastMessage("Webapps:UpdateState", {
app: aOldApp,
manifestURL: aNewApp.manifestURL
app: aApp,
id: aApp.id
});
this.broadcastMessage("Webapps:FireEvent", {
manifestURL: aNewApp.manifestURL,
manifestURL: aApp.manifestURL,
eventType: ["downloadsuccess", "downloadapplied"]
});
});
let file = FileUtils.getFile("TmpD", ["webapps", aId], false);
let file = FileUtils.getFile("TmpD", ["webapps", aApp.id], false);
if (file && file.exists()) {
file.remove(true);
}
@ -3403,9 +3430,10 @@ this.DOMApplicationRegistry = {
dir.moveTo(parent, newId);
});
// Signals that we need to swap the old id with the new app.
this.broadcastMessage("Webapps:RemoveApp", { id: oldId });
this.broadcastMessage("Webapps:AddApp", { id: newId,
app: aOldApp });
this.broadcastMessage("Webapps:UpdateApp", { oldId: oldId,
newId: newId,
app: aOldApp });
}
}
},
@ -3508,7 +3536,7 @@ this.DOMApplicationRegistry = {
this.broadcastMessage("Webapps:UpdateState", {
app: aOldApp,
error: aError,
manifestURL: aNewApp.manifestURL
id: aNewApp.id
});
this.broadcastMessage("Webapps:FireEvent", {
eventType: "downloaderror",
@ -4060,7 +4088,7 @@ AppcacheObserver.prototype = {
let app = this.app;
DOMApplicationRegistry.broadcastMessage("Webapps:UpdateState", {
app: app,
manifestURL: app.manifestURL
id: app.id
});
DOMApplicationRegistry.broadcastMessage("Webapps:FireEvent", {
eventType: "progress",
@ -4092,7 +4120,7 @@ AppcacheObserver.prototype = {
app.downloadAvailable = false;
DOMApplicationRegistry.broadcastMessage("Webapps:UpdateState", {
app: app,
manifestURL: app.manifestURL
id: app.id
});
DOMApplicationRegistry.broadcastMessage("Webapps:FireEvent", {
eventType: ["downloadsuccess", "downloadapplied"],
@ -4115,7 +4143,7 @@ AppcacheObserver.prototype = {
DOMApplicationRegistry.broadcastMessage("Webapps:UpdateState", {
app: app,
error: aError,
manifestURL: app.manifestURL
id: app.id
});
DOMApplicationRegistry.broadcastMessage("Webapps:FireEvent", {
eventType: "downloaderror",

View File

@ -98,6 +98,7 @@ var PackagedTestHelper = (function PackagedTestHelper() {
var aApp = evt.application;
aApp.ondownloaderror = function(evt) {
var error = aApp.downloadError.name;
ok(true, "Got downloaderror " + error);
if (error == aExpectedError) {
ok(true, "Got expected " + aExpectedError);
var expected = {

View File

@ -79,15 +79,15 @@ function updateApp(aExpectedReady, aPreviousVersion, aNextVersion) {
checkLastAppState.bind(PackagedTestHelper, miniManifestURL, false, false,
aNextVersion, PackagedTestHelper.next);
var ondownloadsuccesshandler =
checkLastAppState.bind(undefined, miniManifestURL,
aExpectedReady, false, aPreviousVersion,
function() {
navigator.mozApps.mgmt.applyDownload(lApp);
});
var ondownloadsuccesshandler =
checkLastAppState.bind(undefined, miniManifestURL,
aExpectedReady, false, aPreviousVersion,
function() {
navigator.mozApps.mgmt.applyDownload(lApp);
});
checkForUpdate(true, ondownloadsuccesshandler, ondownloadappliedhandler, null,
true);
checkForUpdate(true, ondownloadsuccesshandler, ondownloadappliedhandler,
null, true);
}
@ -251,7 +251,7 @@ var steps = [
"&appName=arandomname" +
"&appToFail1";
PackagedTestHelper.checkAppDownloadError(miniManifestURL,
"MANIFEST_MISMATCH", 2, false, true,
"MANIFEST_MISMATCH", 1, false, true,
"arandomname",
function () {
checkForUpdate(false, null, null, null, false,

View File

@ -243,4 +243,4 @@ addLoadEvent(go);
</script>
</pre>
</body>
</html>
</html>

View File

@ -1,6 +1,4 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
* vim: sw=2 ts=2 sts=2 et filetype=javascript
* This Source Code Form is subject to the terms of the Mozilla Public
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
@ -36,7 +34,7 @@ const BDADDR_ALL = "ff:ff:ff:ff:ff:ff";
const BDADDR_LOCAL = "ff:ff:ff:00:00:00";
// A user friendly name for remote BT device.
const REMOTE_DEVICE_NAME = "Remote BT Device";
const REMOTE_DEVICE_NAME = "Remote_BT_Device";
let Promise =
SpecialPowers.Cu.import("resource://gre/modules/Promise.jsm").Promise;
@ -45,6 +43,58 @@ let bluetoothManager;
let pendingEmulatorCmdCount = 0;
/**
* Push required permissions and test if |navigator.mozBluetooth| exists.
* Resolve if it does, reject otherwise.
*
* Fulfill params:
* bluetoothManager -- an reference to navigator.mozBluetooth.
* Reject params: (none)
*
* @param aPermissions
* Additional permissions to push before any test cases. Could be either
* a string or an array of strings.
*
* @return A deferred promise.
*/
function ensureBluetoothManager(aPermissions) {
let deferred = Promise.defer();
let permissions = ["bluetooth"];
if (aPermissions) {
if (Array.isArray(aPermissions)) {
permissions = permissions.concat(aPermissions);
} else if (typeof aPermissions == "string") {
permissions.push(aPermissions);
}
}
let obj = [];
for (let perm of permissions) {
obj.push({
"type": perm,
"allow": 1,
"context": document,
});
}
SpecialPowers.pushPermissions(obj, function() {
ok(true, "permissions pushed: " + JSON.stringify(permissions));
bluetoothManager = window.navigator.mozBluetooth;
log("navigator.mozBluetooth is " +
(bluetoothManager ? "available" : "unavailable"));
if (bluetoothManager instanceof BluetoothManager) {
deferred.resolve(bluetoothManager);
} else {
deferred.reject();
}
});
return deferred.promise;
}
/**
* Send emulator command with safe guard.
*
@ -193,71 +243,6 @@ function getEmulatorDeviceProperty(aAddress, aPropertyName) {
});
}
/**
* Start dicovering Bluetooth devices.
*
* Allows the device's adapter to start seeking for remote devices.
*
* Fulfill params: (none)
* Reject params: a DOMError
*
* @param aAdapter
* A BluetoothAdapter which is used to interact with local BT dev
*
* @return A deferred promise.
*/
function startDiscovery(aAdapter) {
let deferred = Promise.defer();
let request = aAdapter.startDiscovery();
request.onsuccess = function () {
log(" Start discovery - Success");
// TODO (bug 892207): Make Bluetooth APIs available for 3rd party apps.
// Currently, discovering state wouldn't change immediately here.
// We would turn on this check when the redesigned API are landed.
// is(aAdapter.discovering, true, "BluetoothAdapter.discovering");
deferred.resolve();
}
request.onerror = function (aEvent) {
ok(false, "Start discovery - Fail");
deferred.reject(aEvent.target.error);
}
return deferred.promise;
}
/**
* Stop dicovering Bluetooth devices.
*
* Allows the device's adapter to stop seeking for remote devices.
*
* Fulfill params: (none)
* Reject params: a DOMError
*
* @param aAdapter
* A BluetoothAdapter which is used to interact with local BT device.
*
* @return A deferred promise.
*/
function stopDiscovery(aAdapter) {
let deferred = Promise.defer();
let request = aAdapter.stopDiscovery();
request.onsuccess = function () {
log(" Stop discovery - Success");
// TODO (bug 892207): Make Bluetooth APIs available for 3rd party apps.
// Currently, discovering state wouldn't change immediately here.
// We would turn on this check when the redesigned API are landed.
// is(aAdapter.discovering, false, "BluetoothAdapter.discovering");
deferred.resolve();
}
request.onerror = function (aEvent) {
ok(false, "Stop discovery - Fail");
deferred.reject(aEvent.target.error);
}
return deferred.promise;
}
/**
* Get mozSettings value specified by @aKey.
*
@ -319,10 +304,9 @@ function setSettings(aSettings) {
}
/**
* Get mozSettings value of 'bluetooth.enabled'.
* Get the boolean value which indicates defaultAdapter of bluetooth is enabled.
*
* Resolve if that mozSettings value is retrieved successfully, reject
* otherwise.
* Resolve if that defaultAdapter is enabled
*
* Fulfill params:
* A boolean value.
@ -331,7 +315,9 @@ function setSettings(aSettings) {
* @return A deferred promise.
*/
function getBluetoothEnabled() {
return getSettings("bluetooth.enabled");
log("bluetoothManager.defaultAdapter.state: " + bluetoothManager.defaultAdapter.state);
return (bluetoothManager.defaultAdapter.state == "enabled");
}
/**
@ -353,58 +339,6 @@ function setBluetoothEnabled(aEnabled) {
return setSettings(obj);
}
/**
* Push required permissions and test if |navigator.mozBluetooth| exists.
* Resolve if it does, reject otherwise.
*
* Fulfill params:
* bluetoothManager -- an reference to navigator.mozBluetooth.
* Reject params: (none)
*
* @param aPermissions
* Additional permissions to push before any test cases. Could be either
* a string or an array of strings.
*
* @return A deferred promise.
*/
function ensureBluetoothManager(aPermissions) {
let deferred = Promise.defer();
let permissions = ["bluetooth"];
if (aPermissions) {
if (Array.isArray(aPermissions)) {
permissions = permissions.concat(aPermissions);
} else if (typeof aPermissions == "string") {
permissions.push(aPermissions);
}
}
let obj = [];
for (let perm of permissions) {
obj.push({
"type": perm,
"allow": 1,
"context": document,
});
}
SpecialPowers.pushPermissions(obj, function() {
ok(true, "permissions pushed: " + JSON.stringify(permissions));
bluetoothManager = window.navigator.mozBluetooth;
log("navigator.mozBluetooth is " +
(bluetoothManager ? "available" : "unavailable"));
if (bluetoothManager instanceof BluetoothManager) {
deferred.resolve(bluetoothManager);
} else {
deferred.reject();
}
});
return deferred.promise;
}
/**
* Wait for one named BluetoothManager event.
*
@ -457,73 +391,6 @@ function waitForAdapterEvent(aAdapter, aEventName) {
return deferred.promise;
}
/**
* Convenient function for setBluetoothEnabled and waitForManagerEvent
* combined.
*
* Resolve if that named event occurs. Reject if we can't set settings.
*
* Fulfill params: the DOMEvent passed.
* Reject params: (none)
*
* @return A deferred promise.
*/
function setBluetoothEnabledAndWait(aEnabled) {
let promises = [];
// Bug 969109 - Intermittent test_dom_BluetoothManager_adapteradded.js
//
// Here we want to wait for two events coming up -- Bluetooth "settings-set"
// event and one of "enabled"/"disabled" events. Special care is taken here
// to ensure that we can always receive that "enabled"/"disabled" event by
// installing the event handler *before* we ever enable/disable Bluetooth. Or
// we might just miss those events and get a timeout error.
promises.push(waitForManagerEvent(aEnabled ? "enabled" : "disabled"));
promises.push(setBluetoothEnabled(aEnabled));
return Promise.all(promises);
}
/**
* Get default adapter.
*
* Resolve if that default adapter is got, reject otherwise.
*
* Fulfill params: a BluetoothAdapter instance.
* Reject params: a DOMError, or null if if there is no adapter ready yet.
*
* @return A deferred promise.
*/
function getDefaultAdapter() {
let deferred = Promise.defer();
let request = bluetoothManager.getDefaultAdapter();
request.onsuccess = function(aEvent) {
let adapter = aEvent.target.result;
if (!(adapter instanceof BluetoothAdapter)) {
ok(false, "no BluetoothAdapter ready yet.");
deferred.reject(null);
return;
}
ok(true, "BluetoothAdapter got.");
// TODO: We have an adapter instance now, but some of its attributes may
// still remain unassigned/out-dated. Here we waste a few seconds to
// wait for the property changed events.
//
// See https://bugzilla.mozilla.org/show_bug.cgi?id=932914
window.setTimeout(function() {
deferred.resolve(adapter);
}, 3000);
};
request.onerror = function(aEvent) {
ok(false, "Failed to get default adapter.");
deferred.reject(aEvent.target.error);
};
return deferred.promise;
}
/**
* Flush permission settings and call |finish()|.
*/
@ -552,36 +419,40 @@ function startBluetoothTestBase(aPermissions, aTestCaseMain) {
function startBluetoothTest(aReenable, aTestCaseMain) {
startBluetoothTestBase(["settings-read", "settings-write"], function() {
let origEnabled, needEnable;
return Promise.resolve()
.then(function() {
origEnabled = getBluetoothEnabled();
return getBluetoothEnabled()
.then(function(aEnabled) {
origEnabled = aEnabled;
needEnable = !aEnabled;
log("Original 'bluetooth.enabled' is " + origEnabled);
needEnable = !origEnabled;
log("Original state of bluetooth is " + bluetoothManager.defaultAdapter.state);
if (aEnabled && aReenable) {
log(" Disable 'bluetooth.enabled' ...");
if (origEnabled && aReenable) {
log("Disable Bluetooth ...");
needEnable = true;
return setBluetoothEnabledAndWait(false);
isnot(bluetoothManager.defaultAdapter, null,
"bluetoothManager.defaultAdapter")
return bluetoothManager.defaultAdapter.disable();
}
})
.then(function() {
if (needEnable) {
log(" Enable 'bluetooth.enabled' ...");
log("Enable Bluetooth ...");
// See setBluetoothEnabledAndWait(). We must install all event
// handlers *before* enabling Bluetooth.
let promises = [];
promises.push(waitForManagerEvent("adapteradded"));
promises.push(setBluetoothEnabledAndWait(true));
return Promise.all(promises);
isnot(bluetoothManager.defaultAdapter, null,
"bluetoothManager.defaultAdapter")
return bluetoothManager.defaultAdapter.enable();
}
})
.then(getDefaultAdapter)
.then(() => bluetoothManager.defaultAdapter)
.then(aTestCaseMain)
.then(function() {
if (!origEnabled) {
return setBluetoothEnabledAndWait(false);
log("Disable Bluetooth ...");
return bluetoothManager.defaultAdapter.disable();
}
});
});

View File

@ -1,10 +1,6 @@
[DEFAULT]
b2g = true
browser = false
qemu = true
qemu = false
[test_dom_BluetoothManager_enabled.js]
[test_dom_BluetoothManager_adapteradded.js]
[test_dom_BluetoothAdapter_setters.js]
[test_dom_BluetoothAdapter_getters.js]
[test_dom_BluetoothAdapter_discovery.js]
[test_dom_BluetoothManager_API2.js]

View File

@ -1,47 +0,0 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
* vim: sw=2 ts=2 sts=2 et filetype=javascript
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
///////////////////////////////////////////////////////////////////////////////
// Test Purpose:
// To verify that discovery process of BluetoothAdapter is correct.
// Use B2G emulator commands to add/remote remote devices to simulate
// discovering behavior.
//
// Test Coverage:
// - BluetoothAdapter.startDiscovery()
// - BluetoothAdapter.stopDiscovery()
// - BluetoothAdapter.ondevicefound()
// - BluetoothAdapter.discovering [Temporarily turned off until BT API update]
//
///////////////////////////////////////////////////////////////////////////////
MARIONETTE_TIMEOUT = 60000;
MARIONETTE_HEAD_JS = 'head.js';
startBluetoothTest(true, function testCaseMain(aAdapter) {
log("Testing the discovery process of BluetoothAdapter ...");
// The properties of remote device.
let theProperties = {
"name": REMOTE_DEVICE_NAME,
"discoverable": true
};
return Promise.resolve()
.then(() => removeEmulatorRemoteDevice(BDADDR_ALL))
.then(() => addEmulatorRemoteDevice(/*theProperties*/ null))
.then(function(aRemoteAddress) {
let promises = [];
promises.push(waitForAdapterEvent(aAdapter, "devicefound"));
promises.push(startDiscovery(aAdapter));
return Promise.all(promises)
.then(function(aResults) {
is(aResults[0].device.address, aRemoteAddress, "BluetoothDevice.address");
});
})
.then(() => stopDiscovery(aAdapter))
.then(() => removeEmulatorRemoteDevice(BDADDR_ALL));
});

View File

@ -1,45 +0,0 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
* vim: sw=2 ts=2 sts=2 et filetype=javascript
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
///////////////////////////////////////////////////////////////////////////////
// Test Purpose:
// To verify that the properties of BluetoothAdapter can be updated and
// retrieved correctly. Use B2G emulator commands to set properties for this
// test case.
//
// Test Coverage:
// - BluetoothAdapter.name
// - BluetoothAdapter.address
// - BluetoothAdapter.class
// - BluetoothAdapter.discoverable
// - BluetoothAdapter.discovering
// ( P.S. Don't include [BluetoothAdapter.uuids], [BluetoothAdapter.devices] )
//
///////////////////////////////////////////////////////////////////////////////
MARIONETTE_TIMEOUT = 60000;
MARIONETTE_HEAD_JS = 'head.js';
function testAdapterGetter(aAdapter, aPropertyName, aParamName, aExpected) {
let cmd = "bt property " + BDADDR_LOCAL + " " + aParamName;
return runEmulatorCmdSafe(cmd)
.then(function(aResults) {
is(aResults[1], "OK", "The status report from emulator command.");
log(" Got adapter " + aResults[0]);
is(aResults[0], aParamName + ": " + aExpected, "BluetoothAdapter." + aPropertyName);
});
}
startBluetoothTest(true, function testCaseMain(aAdapter) {
log("Checking the correctness of BluetoothAdapter properties ...");
return Promise.resolve()
.then(() => testAdapterGetter(aAdapter, "name", "name", aAdapter.name))
.then(() => testAdapterGetter(aAdapter, "address", "address", aAdapter.address))
.then(() => testAdapterGetter(aAdapter, "class", "cod", "0x" + aAdapter.class.toString(16)))
.then(() => testAdapterGetter(aAdapter, "discoverable", "discoverable", aAdapter.discoverable.toString()))
.then(() => testAdapterGetter(aAdapter, "discovering", "discovering", aAdapter.discovering.toString()));
});

View File

@ -1,85 +0,0 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
* vim: sw=2 ts=2 sts=2 et filetype=javascript
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
///////////////////////////////////////////////////////////////////////////////
// Test Purpose:
// To verify that all setters of BluetoothAdapter (except for pairing related
// APIs) can change properties correctly.
//
// Test Coverage:
// - BluetoothAdapter.setName()
// - BluetoothAdapter.setDiscoverable()
// - BluetoothAdapter.setDiscoverableTimeout()
//
///////////////////////////////////////////////////////////////////////////////
MARIONETTE_TIMEOUT = 60000;
MARIONETTE_HEAD_JS = 'head.js';
const BT_DEVICE_NAME = "User friendly name of local BT device";
function setName(aAdapter, aName) {
let deferred = Promise.defer();
let request = aAdapter.setName(aName);
request.onsuccess = function () {
log(" setName succeed: " + aName);
is(aAdapter.name, aName, "aAdapter.name");
deferred.resolve();
}
request.onerror = function () {
ok(false, "setName failed")
deferred.reject();
}
return deferred.promise;
}
function setDiscoverable(aAdapter, aIsDiscoverable) {
let deferred = Promise.defer();
let request = aAdapter.setDiscoverable(aIsDiscoverable);
request.onsuccess = function () {
log(" setDiscoverable succeed: " + aIsDiscoverable);
is(aAdapter.discoverable, aIsDiscoverable, "aAdapter.discoverable");
deferred.resolve();
}
request.onerror = function () {
ok(false, "setDiscoverable failed")
deferred.reject();
}
return deferred.promise;
}
function setDiscoverableTimeout(aAdapter, aTimeout) {
let deferred = Promise.defer();
let request = aAdapter.setDiscoverableTimeout(aTimeout);
request.onsuccess = function () {
log(" setDiscoverableTimeout succeed: " + aTimeout);
is(aAdapter.discoverableTimeout, aTimeout, "aAdapter.discoverableTimeout");
deferred.resolve();
}
request.onerror = function () {
ok(false, "setDiscoverableTimeout failed")
deferred.reject();
}
return deferred.promise;
}
startBluetoothTest(true, function testCaseMain(aAdapter) {
log("Testing BluetoothAdapter setters ...");
return Promise.resolve()
.then( () => setName(aAdapter, BT_DEVICE_NAME) )
.then( () => setDiscoverableTimeout(aAdapter, 180) )
.then( () => setDiscoverable(aAdapter, true) )
.then( () => setName(aAdapter, EMULATOR_NAME) )
.then( () => setDiscoverable(aAdapter, false) )
.then( () => setDiscoverableTimeout(aAdapter, 120) );
});

View File

@ -0,0 +1,48 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
///////////////////////////////////////////////////////////////////////////////
// Test Purpose:
// To verify the basic functionality of BluetoothManager.
//
// Test Coverage:
// - BluetoothManager.defaultAdapter
// - BluetoothManager.getAdapters()
// TODO:
// - BluetoothManager.onattributechanged()
// - BluetoothManager.onadapteradded()
// - BluetoothManager.onadapterremoved()
//
///////////////////////////////////////////////////////////////////////////////
MARIONETTE_TIMEOUT = 60000;
MARIONETTE_HEAD_JS = 'head.js';
// TODO: Listens to 'onattributechanged' when B2G supports the feature that
// allows user to add/remove Bluetooth adapter.
// Currently, B2G recognizes build-in Bluetooth chip as default adapter and
// don't support adding additional Bluetooth dongles in gonk layer.
// Therefore, the 'onattributechanged' would be triggered *only* when the
// instance of BluetoothManager is created.
function waitForManagerAttributeChanged() {
let deferred = Promise.defer();
bluetoothManager.onattributechanged = function(aEvent) {
if(aEvent.attrs.indexOf("defaultAdapter")) {
bluetoothManager.onattributechanged = null;
ok(true, "BluetoothManager event 'onattributechanged' got.");
deferred.resolve(aEvent);
}
};
return deferred.promise;
}
startBluetoothTestBase(["settings-read", "settings-write"],
function testCaseMain() {
let adapters = bluetoothManager.getAdapters();
ok(Array.isArray(adapters), "Can not got the array of adapters");
ok(adapters.length, "The number of adapters should not be zero");
ok(bluetoothManager.defaultAdapter, "defaultAdapter should not be null.");
});

View File

@ -1,19 +0,0 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
* vim: sw=2 ts=2 sts=2 et filetype=javascript
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
MARIONETTE_TIMEOUT = 60000;
MARIONETTE_HEAD_JS = 'head.js';
startBluetoothTest(true, function testCaseMain(aAdapter) {
log("Checking adapter attributes ...");
is(aAdapter.name, EMULATOR_NAME, "adapter.name");
is(aAdapter.class, EMULATOR_CLASS, "adapter.class");
is(aAdapter.address, EMULATOR_ADDRESS, "adapter.address");
is(aAdapter.discovering, false, "adapter.discovering");
is(aAdapter.discoverable, false, "adapter.discoverable");
is(aAdapter.discoverableTimeout, 120, "adapter.discoverableTimeout");
});

View File

@ -1,66 +0,0 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
* vim: sw=2 ts=2 sts=2 et filetype=javascript
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
MARIONETTE_TIMEOUT = 60000;
MARIONETTE_HEAD_JS = 'head.js';
function waitEitherEnabledOrDisabled() {
let deferred = Promise.defer();
function onEnabledDisabled(aEvent) {
bluetoothManager.removeEventListener("adapteradded", onEnabledDisabled);
bluetoothManager.removeEventListener("disabled", onEnabledDisabled);
ok(true, "Got event " + aEvent.type);
deferred.resolve(aEvent.type === "adapteradded");
}
// Listen 'adapteradded' rather than 'enabled' since the current API can't
// disable BT before the BT adapter is initialized.
// We should listen to 'enabled' when gecko can handle the case I mentioned
// above, please refer to the follow-up bug 973482.
bluetoothManager.addEventListener("adapteradded", onEnabledDisabled);
bluetoothManager.addEventListener("disabled", onEnabledDisabled);
return deferred.promise;
}
function test(aEnabled) {
log("Testing 'bluetooth.enabled' => " + aEnabled);
let deferred = Promise.defer();
Promise.all([setBluetoothEnabled(aEnabled),
waitEitherEnabledOrDisabled()])
.then(function(aResults) {
/* aResults is an array of two elements:
* [ <result of setBluetoothEnabled>,
* <result of waitEitherEnabledOrDisabled> ]
*/
log(" Examine results " + JSON.stringify(aResults));
is(bluetoothManager.enabled, aEnabled, "bluetoothManager.enabled");
is(aResults[1], aEnabled, "'adapteradded' event received");
if (bluetoothManager.enabled === aEnabled && aResults[1] === aEnabled) {
deferred.resolve();
} else {
deferred.reject();
}
});
return deferred.promise;
}
startBluetoothTestBase(["settings-read", "settings-write"],
function testCaseMain() {
return getBluetoothEnabled()
.then(function(aEnabled) {
log("Original 'bluetooth.enabled' is " + aEnabled);
// Set to !aEnabled and reset back to aEnabled.
return test(!aEnabled).then(test.bind(null, aEnabled));
});
});

View File

@ -17,7 +17,7 @@ using namespace mozilla::dom;
* CellBroadcast::Listener Implementation.
*/
class CellBroadcast::Listener : public nsICellBroadcastListener
class CellBroadcast::Listener MOZ_FINAL : public nsICellBroadcastListener
{
private:
CellBroadcast* mCellBroadcast;

View File

@ -14,7 +14,7 @@ namespace dom {
class IccManager;
class Icc;
class IccListener : public nsIIccListener
class IccListener MOZ_FINAL : public nsIIccListener
{
public:
NS_DECL_ISUPPORTS

View File

@ -764,11 +764,6 @@ TabChild::Observe(nsISupports *aSubject,
mContentDocumentIsDisplayed = true;
// Reset CSS viewport and zoom to default on new page, then
// calculate them properly using the actual metadata from the
// page.
SetCSSViewport(kDefaultViewportSize);
// In some cases before-first-paint gets called before
// RecvUpdateDimensions is called and therefore before we have an
// mInnerSize value set. In such cases defer initializing the viewport

View File

@ -18,6 +18,7 @@ XPIDL_SOURCES += [
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
XPIDL_SOURCES += [
'nsIDOMNetworkStatsManager.idl',
'nsIEthernetManager.idl',
'nsINetworkStatsServiceProxy.idl',
]

View File

@ -0,0 +1,137 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsISupports.idl"
[scriptable, function, uuid(2a3ad56c-edc0-439f-8aae-900b331ddf49)]
interface nsIEthernetManagerCallback : nsISupports
{
/**
* Callback function used to report the success of different operations.
*
* @param success
* Boolean value indicates the success of an operation.
* @prarm message
* Message reported in the end of operation.
*/
void notify(in boolean success, in DOMString message);
};
[scriptable, function, uuid(1746e7dd-92d4-43fa-8ef4-bc13d0b60353)]
interface nsIEthernetManagerScanCallback : nsISupports
{
/**
* Callback function used to report the result of scan function.
*
* @param list
* List of available ethernet interfaces.
*/
void notify(in jsval list);
};
/**
* An internal idl provides control to ethernet interfaces.
*/
[scriptable, uuid(a96441dd-36b3-4f7f-963b-2c032e28a039)]
interface nsIEthernetManager : nsISupports
{
/**
* List of exisiting interface name.
*/
readonly attribute jsval interfaceList;
/**
* Scan available ethernet interfaces on device.
*
* @param callback
* Callback function.
*/
void scan(in nsIEthernetManagerScanCallback callback);
/**
* Add a new interface to the interface list.
*
* @param ifname
* Interface name. Should be the form of "eth*".
* @param callback
* Callback function.
*/
void addInterface(in DOMString ifname,
in nsIEthernetManagerCallback callback);
/**
* Remove an existing interface from the interface list.
*
* @param ifname
* Interface name.
* @param Callback
* Callback function.
*/
void removeInterface(in DOMString ifname,
in nsIEthernetManagerCallback callback);
/**
* Update a conifg of an existing interface in the interface list.
*
* @param ifname
* Interface name.
* @param config
* .ip: ip address.
* .prefixLength: mask length.
* .gateway: gateway.
* .dnses: dnses.
* .httpProxyHost: http proxy host.
* .httpProxyPort: http porxy port.
* .ipMode: ip mode, can be 'dhcp' or 'static'.
* @param callback
* Callback function.
*/
void updateInterfaceConfig(in DOMString ifname,
in jsval config,
in nsIEthernetManagerCallback callback);
/**
* Enable networking of an existing interface in the interface list.
*
* @param ifname
* Interface name.
* @param callback
* Callback function.
*/
void enable(in DOMString ifname,
in nsIEthernetManagerCallback callback);
/**
* Disable networking of an existing interface in the interface list.
*
* @param ifname
* Interface name.
* @param callback
* Callback function.
*/
void disable(in DOMString ifname,
in nsIEthernetManagerCallback callback);
/**
* Make an existing interface connect to network.
*
* @param ifname
* Interface name.
* @param callback
* Callback function.
*/
void connect(in DOMString ifname,
in nsIEthernetManagerCallback callback);
/**
* Disconnect a connected interface in the interface list.
*
* @param ifname
* Interface name.
* @param callback
* Callback function.
*/
void disconnect(in DOMString ifname,
in nsIEthernetManagerCallback callback);
};

View File

@ -0,0 +1,619 @@
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const DEBUG = false;
function debug(s) {
if (DEBUG) {
dump("-*- EthernetManager: " + s + "\n");
}
}
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
const TOPIC_INTERFACE_STATE_CHANGED = "network-interface-state-changed";
const ETHERNET_NETWORK_IFACE_PREFIX = "eth";
const DEFAULT_ETHERNET_NETWORK_IFACE = "eth0";
const INTERFACE_IPADDR_NULL = "0.0.0.0";
const INTERFACE_GATEWAY_NULL = "0.0.0.0";
const INTERFACE_PREFIX_NULL = 0;
const INTERFACE_MACADDR_NULL = "00:00:00:00:00:00";
const NETWORK_INTERFACE_UP = "up";
const NETWORK_INTERFACE_DOWN = "down";
const IP_MODE_DHCP = "dhcp";
const IP_MODE_STATIC = "static";
XPCOMUtils.defineLazyServiceGetter(this, "gNetworkManager",
"@mozilla.org/network/manager;1",
"nsINetworkManager");
XPCOMUtils.defineLazyServiceGetter(this, "gNetworkService",
"@mozilla.org/network/service;1",
"nsINetworkService");
// nsINetworkInterface
function EthernetInterface(attr) {
this.state = attr.state;
this.type = attr.type;
this.name = attr.name;
this.ipMode = attr.ipMode;
this.ips = [attr.ip];
this.prefixLengths = [attr.prefixLength];
this.gateways = [attr.gateway];
this.dnses = attr.dnses;
this.httpProxyHost = "";
this.httpProxyPort = 0;
}
EthernetInterface.prototype = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsINetworkInterface]),
updateConfig: function(config) {
debug("Interface " + this.name + " updateConfig " + JSON.stringify(config));
this.state = (config.state != undefined) ?
config.state : this.state;
this.ips = (config.ip != undefined) ? [config.ip] : this.ips;
this.prefixLengths = (config.prefixLength != undefined) ?
[config.prefixLength] : this.prefixLengths;
this.gateways = (config.gateway != undefined) ?
[config.gateway] : this.gateways;
this.dnses = (config.dnses != undefined) ? config.dnses : this.dnses;
this.httpProxyHost = (config.httpProxyHost != undefined) ?
config.httpProxyHost : this.httpProxyHost;
this.httpProxyPort = (config.httpProxyPort != undefined) ?
config.httpProxyPort : this.httpProxyPort;
this.ipMode = (config.ipMode != undefined) ?
config.ipMode : this.ipMode;
},
getAddresses: function(ips, prefixLengths) {
ips.value = this.ips.slice();
prefixLengths.value = this.prefixLengths.slice();
return this.ips.length;
},
getGateways: function(count) {
if (count) {
count.value = this.gateways.length;
}
return this.gateways.slice();
},
getDnses: function(count) {
if (count) {
count.value = this.dnses.length;
}
return this.dnses.slice();
}
};
// nsIEthernetManager
/*
* Network state transition diagram
*
* ---------- enable --------- connect ----------- disconnect --------------
* | Disabled | -----> | Enabled | -------> | Connected | <----------> | Disconnected |
* ---------- --------- ----------- connect --------------
* ^ | | |
* | disable | | |
* -----------------------------------------------------------------------
*/
function EthernetManager() {
debug("EthernetManager start");
// Interface list.
this.ethernetInterfaces = {};
// Used to memorize last connection information.
this.lastStaticConfig = {};
Services.obs.addObserver(this, "xpcom-shutdown", false);
}
EthernetManager.prototype = {
classID: Components.ID("a96441dd-36b3-4f7f-963b-2c032e28a039"),
QueryInterface: XPCOMUtils.generateQI([Ci.nsIEthernetManager]),
ethernetInterfaces: null,
lastStaticConfig: null,
observer: function(subject, topic, data) {
switch (topic) {
case "xpcom-shutdown":
debug("xpcom-shutdown");
this._shutdown();
Services.obs.removeObserver(this, "xpcom-shutdown");
break;
}
},
_shutdown: function() {
debug("shuting down.");
(function onRemove(ifnameList) {
if (!ifnameList.length) {
return;
}
let ifname = ifnameList.shift();
this.removeInterface(ifname, { notify: onRemove.bind(this, ifnameList) });
}).call(this, Object.keys(this.ethernetInterfaces));
},
get interfaceList() {
return Object.keys(this.ethernetInterfaces);
},
scan: function(callback) {
debug("scan");
gNetworkService.getInterfaces(function(success, list) {
let ethList = [];
if (!success) {
if (callback) {
callback.notify(ethList);
}
return;
}
for (let i = 0; i < list.length; i++) {
debug("Found interface " + list[i]);
if (!list[i].startsWith(ETHERNET_NETWORK_IFACE_PREFIX)) {
continue;
}
ethList.push(list[i]);
}
if (callback) {
callback.notify(ethList);
}
});
},
addInterface: function(ifname, callback) {
debug("addInterfaces " + ifname);
if (!ifname || !ifname.startsWith(ETHERNET_NETWORK_IFACE_PREFIX)) {
if (callback) {
callback.notify(false, "Invalid interface.");
}
return;
}
if (this.ethernetInterfaces[ifname]) {
if (callback) {
callback.notify(true, "Interface already exists.");
}
return;
}
gNetworkService.getInterfaceConfig(ifname, function(success, result) {
if (!success) {
if (callback) {
callback.notify(false, "Netd error.");
}
return;
}
// Since the operation may still succeed with an invalid interface name,
// check the mac address as well.
if (result.macAddr == INTERFACE_MACADDR_NULL) {
if (callback) {
callback.notify(false, "Interface not found.");
}
return;
}
this.ethernetInterfaces[ifname] = new EthernetInterface({
state: result.link == NETWORK_INTERFACE_UP ?
Ci.nsINetworkInterface.NETWORK_STATE_DISABLED :
Ci.nsINetworkInterface.NETWORK_STATE_ENABLED,
name: ifname,
type: Ci.nsINetworkInterface.NETWORK_TYPE_ETHERNET,
ip: result.ip,
prefixLength: result.prefix,
ipMode: IP_MODE_DHCP
});
// Register the interface to NetworkManager.
gNetworkManager.registerNetworkInterface(this.ethernetInterfaces[ifname]);
debug("Add interface " + ifname + " success with " +
JSON.stringify(this.ethernetInterfaces[ifname]));
if (callback) {
callback.notify(true, "ok");
}
}.bind(this));
},
removeInterface: function(ifname, callback) {
debug("removeInterface");
if (!ifname || !ifname.startsWith(ETHERNET_NETWORK_IFACE_PREFIX)) {
if (callback) {
callback.notify(false, "Invalid interface.");
}
return;
}
if (!this.ethernetInterfaces[ifname]) {
if (callback) {
callback.notify(true, "Interface does not exist.");
}
return;
}
// Make sure interface is disable before removing.
this.disable(ifname, { notify: function(success, message) {
// Unregister the interface from NetworkManager and also remove it from
// the interface list.
gNetworkManager.unregisterNetworkInterface(this.ethernetInterfaces[ifname]);
delete this.ethernetInterfaces[ifname];
debug("Remove interface " + ifname + " success.");
if (callback) {
callback.notify(true, "ok");
}
}.bind(this)});
},
updateInterfaceConfig: function(ifname, config, callback) {
debug("interfaceConfigUpdate with " + ifname);
this._ensureIfname(ifname, callback, function(iface) {
if (!config) {
if (callback) {
callback.notify(false, "No config to update.");
}
return;
}
// Network state can not be modified externally.
if (config.state) {
delete config.state;
}
let currentIpMode = iface.ipMode;
// Update config.
this.ethernetInterfaces[iface.name].updateConfig(config);
// Do not automatically re-connect if the interface is not in connected
// state.
if (iface.state != Ci.nsINetworkInterface.NETWORK_STATE_CONNECTED) {
if (callback) {
callback.notify(true, "ok");
}
return;
}
let newIpMode = this.ethernetInterfaces[iface.name].ipMode;
if (newIpMode == IP_MODE_STATIC) {
this._setStaticIP(iface.name, callback);
return;
}
if ((currentIpMode == IP_MODE_STATIC) && (newIpMode == IP_MODE_DHCP)) {
gNetworkService.stopDhcp(iface.name);
// Clear the current network settings before do dhcp request, otherwise
// dhcp settings could fail.
this.disconnect(iface.name, { notify: function(success, message) {
if (!success) {
if (callback) {
callback.notify("Disconnect failed.");
}
return;
}
this._runDhcp(iface.name, callback);
}.bind(this) });
return;
}
if (callback) {
callback.notify(true, "ok");
}
}.bind(this));
},
enable: function(ifname, callback) {
debug("enable with " + ifname);
this._ensureIfname(ifname, callback, function(iface) {
// Interface can be only enabled in the state of disabled.
if (iface.state != Ci.nsINetworkInterface.NETWORK_STATE_DISABLED) {
if (callback) {
callback.notify(true, "already enabled.");
}
return;
}
let ips = {};
let prefixLengths = {};
iface.getAddresses(ips, prefixLengths);
let config = { ifname: iface.name,
ip: ips.value[0],
prefix: prefixLengths.value[0],
link: NETWORK_INTERFACE_UP };
gNetworkService.setInterfaceConfig(config, function(success) {
if (!success) {
if (callback) {
callback.notify(false, "Netd Error.");
}
return;
}
this.ethernetInterfaces[iface.name].updateConfig({
state: Ci.nsINetworkInterface.NETWORK_STATE_ENABLED
});
debug("Interface " + iface.name + " enable success.");
if (callback) {
callback.notify(true, "ok");
}
}.bind(this));
}.bind(this));
},
disable: function(ifname, callback) {
debug("disable with " + ifname);
this._ensureIfname(ifname, callback, function(iface) {
if (iface.state == Ci.nsINetworkInterface.NETWORK_STATE_DISABLED) {
if (callback) {
callback.notify(true, "Interface is already disabled.");
}
return;
}
if (iface.state == Ci.nsINetworkInterface.NETWORK_STATE_CONNECTED) {
gNetworkService.stopDhcp(iface.name);
}
let ips = {};
let prefixLengths = {};
iface.getAddresses(ips, prefixLengths);
let config = { ifname: iface.name,
ip: ips.value[0],
prefix: prefixLengths.value[0],
link: NETWORK_INTERFACE_DOWN };
gNetworkService.setInterfaceConfig(config, function(success) {
if (!success) {
if (callback) {
callback.notify(false, "Netd Error.");
}
return;
}
this.ethernetInterfaces[iface.name].updateConfig({
state: Ci.nsINetworkInterface.NETWORK_STATE_DISABLED
});
debug("Disable interface " + iface.name + " success.");
if (callback) {
callback.notify(true, "ok");
}
}.bind(this));
}.bind(this));
},
connect: function(ifname, callback) {
debug("connect wtih " + ifname);
this._ensureIfname(ifname, callback, function(iface) {
// Interface can only be connected in the state of enabled or
// disconnected.
if (iface.state == Ci.nsINetworkInterface.NETWORK_STATE_DISABLED ||
iface.state == Ci.nsINetworkInterface.NETWORK_STATE_CONNECTED) {
if (callback) {
callback.notify(true, "Interface " + ifname + " is not available or "
+ " already connected.");
}
return;
}
if (iface.ipMode == IP_MODE_DHCP) {
this._runDhcp(iface.name, callback);
return;
}
if (iface.ipMode == IP_MODE_STATIC) {
if (this._checkConfigNull(iface) && this.lastStaticConfig[iface.name]) {
debug("connect with lastStaticConfig " +
JSON.stringify(this.lastStaticConfig[iface.name]));
this.ethernetInterfaces[iface.name].updateConfig(
this.lastStaticConfig[iface.name]);
}
this._setStaticIP(iface.name, callback);
return;
}
if (callback) {
callback.notify(false, "Ip mode is wrong or not set.");
}
}.bind(this));
},
disconnect: function(ifname, callback) {
debug("disconnect");
this._ensureIfname(ifname, callback, function(iface) {
// Interface can be only disconnected in the state of connected.
if (iface.state != Ci.nsINetworkInterface.NETWORK_STATE_CONNECTED) {
if (callback) {
callback.notify(true, "interface is already disconnected");
}
return;
}
let config = { ifname: iface.name,
ip: INTERFACE_IPADDR_NULL,
prefix: INTERFACE_PREFIX_NULL,
link: NETWORK_INTERFACE_UP };
gNetworkService.setInterfaceConfig(config, function(success) {
if (!success) {
if (callback) {
callback.notify(false, "Netd error.");
}
return;
}
// Stop dhcp daemon.
gNetworkService.stopDhcp(iface.name);
this.ethernetInterfaces[iface.name].updateConfig({
state: Ci.nsINetworkInterface.NETWORK_STATE_DISCONNECTED,
ip: INTERFACE_IPADDR_NULL,
prefixLength: INTERFACE_PREFIX_NULL,
gateway: INTERFACE_GATEWAY_NULL
});
Services.obs.notifyObservers(this.ethernetInterfaces[iface.name],
TOPIC_INTERFACE_STATE_CHANGED,
null);
debug("Disconnect interface " + iface.name + " success.");
if (callback) {
callback.notify(true, "ok");
}
}.bind(this));
}.bind(this));
},
_checkConfigNull: function(iface) {
let ips = {};
let prefixLengths = {};
let gateways = iface.getGateways();
iface.getAddresses(ips, prefixLengths);
if (ips.value[0] == INTERFACE_IPADDR_NULL &&
prefixLengths.value[0] == INTERFACE_PREFIX_NULL &&
gateways[0] == INTERFACE_GATEWAY_NULL) {
return true;
}
return false;
},
_ensureIfname: function(ifname, callback, func) {
// If no given ifname, use the default one.
if (!ifname) {
ifname = DEFAULT_ETHERNET_NETWORK_IFACE;
}
let iface = this.ethernetInterfaces[ifname];
if (!iface) {
if (callback) {
callback.notify(true, "Interface " + ifname + " is not available.");
}
return;
}
func.call(this, iface);
},
_runDhcp: function(ifname, callback) {
debug("runDhcp with " + ifname);
if (!this.ethernetInterfaces[ifname]) {
callback.notify(false, "Invalid interface.");
return
}
gNetworkService.runDhcp(ifname, function(success, result) {
if (!success) {
callback.notify(false, "Dhcp failed.");
return;
}
debug("Dhcp success with " + JSON.stringify(result));
// Clear last static network information when connecting with dhcp mode.
if (this.lastStaticConfig[ifname]) {
this.lastStaticConfig[ifname] = null;
}
this.ethernetInterfaces[ifname].updateConfig({
state: Ci.nsINetworkInterface.NETWORK_STATE_CONNECTED,
ip: result.ip,
gateway: result.gateway,
prefixLength: result.prefix,
dnses: [result.dns1, result.dns2]
});
Services.obs.notifyObservers(this.ethernetInterfaces[ifname],
TOPIC_INTERFACE_STATE_CHANGED,
null);
debug("Connect interface " + ifname + "with dhcp success.");
callback.notify(true, "ok");
}.bind(this));
},
_setStaticIP: function(ifname, callback) {
let iface = this.ethernetInterfaces[ifname];
if (!iface) {
callback.notify(false, "Invalid interface.");
return;
}
let ips = {};
let prefixLengths = {};
iface.getAddresses(ips, prefixLengths);
let config = { ifname: iface.name,
ip: ips.value[0],
prefix: prefixLengths.value[0],
link: NETWORK_INTERFACE_UP };
gNetworkService.setInterfaceConfig(config, function(success) {
if (!success) {
callback.notify(false, "Netd Error.");
return;
}
// Keep the lastest static network information.
let ips = {};
let prefixLengths = {};
let gateways = iface.getGateways();
iface.getAddresses(ips, prefixLengths);
this.lastStaticConfig[iface.name] = {
ip: ips.value[0],
prefixLength: prefixLengths.value[0],
gateway: gateways[0]
};
this.ethernetInterfaces[ifname].updateConfig({
state: Ci.nsINetworkInterface.NETWORK_STATE_CONNECTED,
});
Services.obs.notifyObservers(this.ethernetInterfaces[ifname],
TOPIC_INTERFACE_STATE_CHANGED,
null);
debug("Connect interface " + ifname + "with static ip success.");
callback.notify(true, "ok");
}.bind(this));
},
}
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([EthernetManager]);

View File

@ -0,0 +1,2 @@
component {a96441dd-36b3-4f7f-963b-2c032e28a039} EthernetManager.js
contract @mozilla.org/ethernetManager;1 {a96441dd-36b3-4f7f-963b-2c032e28a039}

View File

@ -44,6 +44,8 @@ EXTRA_PP_COMPONENTS += [
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
EXTRA_COMPONENTS += [
'EthernetManager.js',
'EthernetManager.manifest',
'NetworkStatsManager.js',
'NetworkStatsManager.manifest',
'NetworkStatsServiceProxy.js',

View File

@ -0,0 +1,551 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
let Promise = SpecialPowers.Cu.import("resource://gre/modules/Promise.jsm").Promise;
const ETHERNET_MANAGER_CONTRACT_ID = "@mozilla.org/ethernetManager;1";
const INTERFACE_UP = "UP";
const INTERFACE_DOWN = "DOWN";
let gTestSuite = (function() {
let suite = {};
// Private member variables of the returned object |suite|.
let ethernetManager = SpecialPowers.Cc[ETHERNET_MANAGER_CONTRACT_ID]
.getService(SpecialPowers.Ci.nsIEthernetManager);
let pendingEmulatorShellCount = 0;
/**
* Send emulator shell command with safe guard.
*
* We should only call |finish()| after all emulator command transactions
* end, so here comes with the pending counter. Resolve when the emulator
* gives positive response, and reject otherwise.
*
* Fulfill params: an array of emulator response lines.
* Reject params: an array of emulator response lines.
*
* @param command
* A string command to be passed to emulator through its telnet console.
*
* @return A deferred promise.
*/
function runEmulatorShellSafe(command) {
let deferred = Promise.defer();
++pendingEmulatorShellCount;
runEmulatorShell(command, function(aResult) {
--pendingEmulatorShellCount;
ok(true, "Emulator shell response: " + JSON.stringify(aResult));
if (Array.isArray(aResult)) {
deferred.resolve(aResult);
} else {
deferred.reject(aResult);
}
});
return deferred.promise;
}
/**
* Get the system network conifg by the given interface name.
*
* Use shell command 'netcfg' to get the list of network cofig.
*
* Fulfill params: An object of { name, flag, ip }
*
* @parm ifname
* Interface name.
*
* @return A deferred promise.
*/
function getNetworkConfig(ifname) {
return runEmulatorShellSafe(['netcfg'])
.then(result => {
// Sample 'netcfg' output:
//
// lo UP 127.0.0.1/8 0x00000049 00:00:00:00:00:00
// eth0 UP 10.0.2.15/24 0x00001043 52:54:00:12:34:56
// eth1 DOWN 0.0.0.0/0 0x00001002 52:54:00:12:34:57
// rmnet1 DOWN 0.0.0.0/0 0x00001002 52:54:00:12:34:59
let config;
for (let i = 0; i < result.length; i++) {
let tokens = result[i].split(/\s+/);
let name = tokens[0];
let flag = tokens[1];
let ip = tokens[2].split(/\/+/)[0];
if (name == ifname) {
config = { name: name, flag: flag, ip: ip };
break;
}
}
return config;
});
}
/**
* Get the ip assigned by dhcp server of a given interface name.
*
* Get the ip from android property 'dhcp.[ifname].ipaddress'.
*
* Fulfill params: A string of ip address.
*
* @parm ifname
* Interface name.
*
* @return A deferred promise.
*/
function getDhcpIpAddr(ifname) {
return runEmulatorShellSafe(['getprop', 'dhcp.' + ifname + '.ipaddress'])
.then(function(ipAddr) {
return ipAddr[0];
});
}
/**
* Get the gateway assigned by dhcp server of a given interface name.
*
* Get the ip from android property 'dhcp.[ifname].gateway'.
*
* Fulfill params: A string of gateway.
*
* @parm ifname
* Interface name.
*
* @return A deferred promise.
*/
function getDhcpGateway(ifname) {
return runEmulatorShellSafe(['getprop', 'dhcp.' + ifname + '.gateway'])
.then(function(gateway) {
return gateway[0];
});
}
/**
* Get the default route.
*
* Use shell command 'ip route' to get the default of device.
*
* Fulfill params: An array of { name, gateway }
*
* @return A deferred promise.
*/
function getDefaultRoute() {
return runEmulatorShellSafe(['ip', 'route'])
.then(result => {
// Sample 'ip route' output:
//
// 10.0.2.0/24 dev eth0 proto kernel scope link src 10.0.2.15
// default via 10.0.2.2 dev eth0 metric 2
let routeInfo = [];
for (let i = 0; i < result.length; i++) {
if (!result[i].match('default')) {
continue;
}
let tokens = result[i].split(/\s+/);
let name = tokens[4];
let gateway = tokens[2];
routeInfo.push({ name: name, gateway: gateway });
}
return routeInfo;
});
}
/**
* Check a specific interface is enabled or not.
*
* @parm ifname
* Interface name.
* @parm enabled
* A boolean value used to check interface is disable or not.
*
* @return A deferred promise.
*/
function checkInterfaceIsEnabled(ifname, enabled) {
return getNetworkConfig(ifname)
.then(function(config) {
if (enabled) {
is(config.flag, INTERFACE_UP, "Interface is enabled as expectation.");
} else {
is(config.flag, INTERFACE_DOWN, "Interface is disabled as expectation.");
}
});
}
/**
* Check the ip of a specific interface is equal to given ip or not.
*
* @parm ifname
* Interface name.
* @parm ip
* Given ip address.
*
* @return A deferred promise.
*/
function checkInterfaceIpAddr(ifname, ip) {
return getNetworkConfig(ifname)
.then(function(config) {
is(config.ip, ip, "IP is right as expectation.");
});
}
/**
* Check the default gateway of a specific interface is equal to given gateway
* or not.
*
* @parm ifname
* Interface name.
* @parm gateway
* Given gateway.
*
* @return A deferred promise.
*/
function checkDefaultRoute(ifname, gateway) {
return getDefaultRoute()
.then(function(routeInfo) {
for (let i = 0; i < routeInfo.length; i++) {
if (routeInfo[i].name == ifname) {
is(routeInfo[i].gateway, gateway,
"Default gateway is right as expectation.");
return true;
}
}
if (!gateway) {
ok(true, "Default route is cleared.");
return true;
}
return false;
});
}
/**
* Check the length of interface list in EthernetManager is equal to given
* length or not.
*
* @parm length
* Given length.
*/
function checkInterfaceListLength(length) {
let list = ethernetManager.interfaceList;
is(length, list.length, "List length is equal as expectation.");
}
/**
* Check the given interface exists on device or not.
*
* @parm ifname
* Interface name.
*
* @return A deferred promise.
*/
function checkInterfaceExist(ifname) {
return scanInterfaces()
.then(list => {
let index = list.indexOf(ifname);
if (index < 0) {
throw "Interface " + ifname + " not found.";
}
ok(true, ifname + " exists.")
});
}
/**
* Scan for available ethernet interfaces.
*
* Fulfill params: A list of available interfaces found in device.
*
* @return A deferred promise.
*/
function scanInterfaces() {
let deferred = Promise.defer();
ethernetManager.scan(function onScan(list) {
deferred.resolve(list);
});
return deferred.promise;
}
/**
* Add an interface into interface list.
*
* Fulfill params: A boolean value indicates success or not.
*
* @param ifname
* Interface name.
*
* @return A deferred promise.
*/
function addInterface(ifname) {
let deferred = Promise.defer();
ethernetManager.addInterface(ifname, function onAdd(success, message) {
ok(success, "Add interface " + ifname + " success.");
is(message, "ok", "Message is as expectation.");
deferred.resolve(success);
});
return deferred.promise;
}
/**
* Remove an interface form the interface list.
*
* Fulfill params: A boolean value indicates success or not.
*
* @param ifname
* Interface name.
*
* @return A deferred promise.
*/
function removeInterface(ifname) {
let deferred = Promise.defer();
ethernetManager.removeInterface(ifname, function onRemove(success, message) {
ok(success, "Remove interface " + ifname + " success.");
is(message, "ok", "Message is as expectation.");
deferred.resolve(success);
});
return deferred.promise;
}
/**
* Enable networking of an interface in the interface list.
*
* Fulfill params: A boolean value indicates success or not.
*
* @param ifname
* Interface name.
*
* @return A deferred promise.
*/
function enableInterface(ifname) {
let deferred = Promise.defer();
ethernetManager.enable(ifname, function onEnable(success, message) {
ok(success, "Enable interface " + ifname + " success.");
is(message, "ok", "Message is as expectation.");
deferred.resolve(success);
});
return deferred.promise;
}
/**
* Disable networking of an interface in the interface list.
*
* Fulfill params: A boolean value indicates success or not.
*
* @param ifname
* Interface name.
*
* @return A deferred promise.
*/
function disableInterface(ifname) {
let deferred = Promise.defer();
ethernetManager.disable(ifname, function onDisable(success, message) {
ok(success, "Disable interface " + ifname + " success.");
is(message, "ok", "Message is as expectation.");
deferred.resolve(success);
});
return deferred.promise;
}
/**
* Make an interface connect to network.
*
* Fulfill params: A boolean value indicates success or not.
*
* @param ifname
* Interface name.
*
* @return A deferred promise.
*/
function makeInterfaceConnect(ifname) {
let deferred = Promise.defer();
ethernetManager.connect(ifname, function onConnect(success, message) {
ok(success, "Interface " + ifname + " is connected successfully.");
is(message, "ok", "Message is as expectation.");
deferred.resolve(success);
});
return deferred.promise;
}
/**
* Make an interface disconnect to network.
*
* Fulfill params: A boolean value indicates success or not.
*
* @param ifname
* Interface name.
*
* @return A deferred promise.
*/
function makeInterfaceDisconnect(ifname) {
let deferred = Promise.defer();
ethernetManager.disconnect(ifname, function onDisconnect(success, message) {
ok(success, "Interface " + ifname + " is disconnected successfully.");
is(message, "ok", "Message is as expectation.");
deferred.resolve(success);
});
return deferred.promise;
}
/**
* Update the config the an interface in the interface list.
*
* @param ifname
* Interface name.
* @param config
* .ip: ip address.
* .prefixLength: mask length.
* .gateway: gateway.
* .dnses: dnses.
* .httpProxyHost: http proxy host.
* .httpProxyPort: http porxy port.
* .usingDhcp: an boolean value indicates using dhcp or not.
*
* @return A deferred promise.
*/
function updateInterfaceConfig(ifname, config) {
let deferred = Promise.defer();
ethernetManager.updateInterfaceConfig(ifname, config,
function onUpdated(success, message) {
ok(success, "Interface " + ifname + " config is updated successfully " +
" with " + JSON.stringify(config));
is(message, "ok", "Message is as expectation.");
deferred.resolve(success);
});
return deferred.promise;
}
/**
* Wait for timeout.
*
* @param timeout
* Time in ms.
*
* @return A deferred promise.
*/
function waitForTimeout(timeout) {
let deferred = Promise.defer();
setTimeout(function() {
ok(true, "waitForTimeout " + timeout);
deferred.resolve();
}, timeout);
return deferred.promise;
}
/**
* Wait for default route of a specific interface being set and
* check.
*
* @param ifname
* Interface name.
* @param gateway
* Target gateway.
*
* @return A deferred promise.
*/
function waitForDefaultRouteSet(ifname, gateway) {
return gTestSuite.waitForTimeout(500)
.then(() => gTestSuite.checkDefaultRoute(ifname, gateway))
.then(success => {
if (success) {
ok(true, "Default route is set as expectation " + gateway);
return;
}
ok(true, "Default route is not set yet, check again. " + success);
return waitForDefaultRouteSet(ifname, gateway);
});
}
//---------------------------------------------------
// Public test suite functions
//---------------------------------------------------
suite.scanInterfaces = scanInterfaces;
suite.addInterface = addInterface;
suite.removeInterface = removeInterface;
suite.enableInterface = enableInterface;
suite.disableInterface = disableInterface;
suite.makeInterfaceConnect = makeInterfaceConnect;
suite.makeInterfaceDisconnect = makeInterfaceDisconnect;
suite.updateInterfaceConfig = updateInterfaceConfig;
suite.getDhcpIpAddr = getDhcpIpAddr;
suite.getDhcpGateway = getDhcpGateway;
suite.checkInterfaceExist = checkInterfaceExist;
suite.checkInterfaceIsEnabled = checkInterfaceIsEnabled;
suite.checkInterfaceIpAddr = checkInterfaceIpAddr;
suite.checkDefaultRoute = checkDefaultRoute;
suite.checkInterfaceListLength = checkInterfaceListLength;
suite.waitForTimeout = waitForTimeout;
suite.waitForDefaultRouteSet = waitForDefaultRouteSet;
/**
* End up the test run.
*
* Wait until all pending emulator shell commands are done and then |finish|
* will be called in the end.
*/
function cleanUp() {
waitFor(finish, function() {
return pendingEmulatorShellCount === 0;
});
}
/**
* Common test routine.
*
* Start a test with the given test case chain. The test environment will be
* settled down before the test. After the test, all the affected things will
* be restored.
*
* @param aTestCaseChain
* The test case entry point, which can be a function or a promise.
*
* @return A deferred promise.
*/
suite.doTest = function(aTestCaseChain) {
return Promise.resolve()
.then(aTestCaseChain)
.then(function onresolve() {
cleanUp();
}, function onreject(aReason) {
ok(false, 'Promise rejects during test' + (aReason ? '(' + aReason + ')' : ''));
cleanUp();
});
};
return suite;
})();

View File

@ -0,0 +1,15 @@
[DEFAULT]
b2g = true
browser = false
qemu = true
[test_ethernet_add_interface.js]
[test_ethernet_remove_interface.js]
[test_ethernet_enable.js]
[test_ethernet_disable.js]
[test_ethernet_connect_with_dhcp.js]
[test_ethernet_connect_with_static_ip.js]
[test_ethernet_reconnect_with_dhcp.js]
[test_ethernet_reconnect_with_static_ip.js]
[test_ethernet_ip_mode_change.js]
[test_ethernet_disconnect.js]

View File

@ -0,0 +1,16 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
MARIONETTE_TIMEOUT = 60000;
MARIONETTE_HEAD_JS = 'head.js';
const ETHERNET_INTERFACE_NAME = "eth1";
gTestSuite.doTest(function() {
return Promise.resolve()
.then(() => gTestSuite.checkInterfaceExist(ETHERNET_INTERFACE_NAME))
.then(() => gTestSuite.checkInterfaceListLength(0))
.then(() => gTestSuite.addInterface(ETHERNET_INTERFACE_NAME))
.then(() => gTestSuite.checkInterfaceListLength(1))
.then(() => gTestSuite.removeInterface(ETHERNET_INTERFACE_NAME));
});

View File

@ -0,0 +1,26 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
MARIONETTE_TIMEOUT = 60000;
MARIONETTE_HEAD_JS = 'head.js';
const ETHERNET_INTERFACE_NAME = "eth1";
function checkDhcpResult(ifname) {
return gTestSuite.getDhcpIpAddr(ifname)
.then(ip => gTestSuite.checkInterfaceIpAddr(ifname, ip))
.then(() => gTestSuite.getDhcpGateway(ifname))
.then(gateway => gTestSuite.waitForDefaultRouteSet(ifname, gateway));
}
gTestSuite.doTest(function() {
return Promise.resolve()
.then(() => gTestSuite.checkInterfaceExist(ETHERNET_INTERFACE_NAME))
.then(() => gTestSuite.addInterface(ETHERNET_INTERFACE_NAME))
.then(() => gTestSuite.enableInterface(ETHERNET_INTERFACE_NAME))
.then(() => gTestSuite.makeInterfaceConnect(ETHERNET_INTERFACE_NAME))
.then(() => checkDhcpResult(ETHERNET_INTERFACE_NAME))
.then(() => gTestSuite.makeInterfaceDisconnect(ETHERNET_INTERFACE_NAME))
.then(() => gTestSuite.disableInterface(ETHERNET_INTERFACE_NAME))
.then(() => gTestSuite.removeInterface(ETHERNET_INTERFACE_NAME));
});

View File

@ -0,0 +1,33 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
MARIONETTE_TIMEOUT = 60000;
MARIONETTE_HEAD_JS = 'head.js';
const ETHERNET_INTERFACE_NAME = "eth1";
let staticConfig = {
ip: "1.2.3.4",
gateway: "1.2.3.5",
prefixLength: 24,
dnses: ["1.2.3.6"],
ipMode: "static"
};
function checkStaticResult(ifname) {
return gTestSuite.checkInterfaceIpAddr(ifname, staticConfig.ip)
.then(() => gTestSuite.checkDefaultRoute(ifname, staticConfig.gateway));
}
gTestSuite.doTest(function() {
return Promise.resolve()
.then(() => gTestSuite.checkInterfaceExist(ETHERNET_INTERFACE_NAME))
.then(() => gTestSuite.addInterface(ETHERNET_INTERFACE_NAME))
.then(() => gTestSuite.enableInterface(ETHERNET_INTERFACE_NAME))
.then(() => gTestSuite.updateInterfaceConfig(ETHERNET_INTERFACE_NAME, staticConfig))
.then(() => gTestSuite.makeInterfaceConnect(ETHERNET_INTERFACE_NAME))
.then(() => checkStaticResult(ETHERNET_INTERFACE_NAME))
.then(() => gTestSuite.makeInterfaceDisconnect(ETHERNET_INTERFACE_NAME))
.then(() => gTestSuite.disableInterface(ETHERNET_INTERFACE_NAME))
.then(() => gTestSuite.removeInterface(ETHERNET_INTERFACE_NAME));
});

View File

@ -0,0 +1,17 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
MARIONETTE_TIMEOUT = 60000;
MARIONETTE_HEAD_JS = 'head.js';
const ETHERNET_INTERFACE_NAME = "eth1";
gTestSuite.doTest(function() {
return Promise.resolve()
.then(() => gTestSuite.checkInterfaceExist(ETHERNET_INTERFACE_NAME))
.then(() => gTestSuite.addInterface(ETHERNET_INTERFACE_NAME))
.then(() => gTestSuite.enableInterface(ETHERNET_INTERFACE_NAME))
.then(() => gTestSuite.disableInterface(ETHERNET_INTERFACE_NAME))
.then(() => gTestSuite.checkInterfaceIsEnabled(ETHERNET_INTERFACE_NAME, false))
.then(() => gTestSuite.removeInterface(ETHERNET_INTERFACE_NAME));
});

View File

@ -0,0 +1,25 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
MARIONETTE_TIMEOUT = 60000;
MARIONETTE_HEAD_JS = 'head.js';
const ETHERNET_INTERFACE_NAME = "eth1";
const INTERFACE_IP_NONE = "0.0.0.0";
function checkIpAddrIsReset(ifname) {
return gTestSuite.checkInterfaceIpAddr(ifname, INTERFACE_IP_NONE)
.then(() => gTestSuite.checkDefaultRoute(ifname));
}
gTestSuite.doTest(function() {
return Promise.resolve()
.then(() => gTestSuite.checkInterfaceExist(ETHERNET_INTERFACE_NAME))
.then(() => gTestSuite.addInterface(ETHERNET_INTERFACE_NAME))
.then(() => gTestSuite.enableInterface(ETHERNET_INTERFACE_NAME))
.then(() => gTestSuite.makeInterfaceConnect(ETHERNET_INTERFACE_NAME))
.then(() => gTestSuite.makeInterfaceDisconnect(ETHERNET_INTERFACE_NAME))
.then(() => checkIpAddrIsReset(ETHERNET_INTERFACE_NAME))
.then(() => gTestSuite.disableInterface(ETHERNET_INTERFACE_NAME))
.then(() => gTestSuite.removeInterface(ETHERNET_INTERFACE_NAME));
});

View File

@ -0,0 +1,17 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
MARIONETTE_TIMEOUT = 60000;
MARIONETTE_HEAD_JS = 'head.js';
const ETHERNET_INTERFACE_NAME = "eth1";
gTestSuite.doTest(function() {
return Promise.resolve()
.then(() => gTestSuite.checkInterfaceExist(ETHERNET_INTERFACE_NAME))
.then(() => gTestSuite.addInterface(ETHERNET_INTERFACE_NAME))
.then(() => gTestSuite.enableInterface(ETHERNET_INTERFACE_NAME))
.then(() => gTestSuite.checkInterfaceIsEnabled(ETHERNET_INTERFACE_NAME, true))
.then(() => gTestSuite.disableInterface(ETHERNET_INTERFACE_NAME))
.then(() => gTestSuite.removeInterface(ETHERNET_INTERFACE_NAME));
});

View File

@ -0,0 +1,43 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
MARIONETTE_TIMEOUT = 60000;
MARIONETTE_HEAD_JS = 'head.js';
const ETHERNET_INTERFACE_NAME = "eth1";
let staticConfig = {
ip: "1.2.3.4",
gateway: "1.2.3.5",
prefixLength: 24,
dnses: ["1.2.3.6"],
ipMode: "static"
};
function checkStaticResult(ifname) {
return gTestSuite.checkInterfaceIpAddr(ifname, staticConfig.ip)
.then(() => gTestSuite.waitForDefaultRouteSet(ifname, staticConfig.gateway));
}
function checkDhcpResult(ifname) {
return gTestSuite.getDhcpIpAddr(ifname)
.then(ip => gTestSuite.checkInterfaceIpAddr(ifname, ip))
.then(() => gTestSuite.getDhcpGateway(ifname))
.then(gateway => gTestSuite.waitForDefaultRouteSet(ifname, gateway));
}
gTestSuite.doTest(function() {
return Promise.resolve()
.then(() => gTestSuite.checkInterfaceExist(ETHERNET_INTERFACE_NAME))
.then(() => gTestSuite.addInterface(ETHERNET_INTERFACE_NAME))
.then(() => gTestSuite.enableInterface(ETHERNET_INTERFACE_NAME))
.then(() => gTestSuite.makeInterfaceConnect(ETHERNET_INTERFACE_NAME))
.then(() => checkDhcpResult(ETHERNET_INTERFACE_NAME))
.then(() => gTestSuite.updateInterfaceConfig(ETHERNET_INTERFACE_NAME, staticConfig))
.then(() => checkStaticResult(ETHERNET_INTERFACE_NAME))
.then(() => gTestSuite.updateInterfaceConfig(ETHERNET_INTERFACE_NAME, { ipMode: "dhcp"}))
.then(() => checkDhcpResult(ETHERNET_INTERFACE_NAME))
.then(() => gTestSuite.makeInterfaceDisconnect(ETHERNET_INTERFACE_NAME))
.then(() => gTestSuite.disableInterface(ETHERNET_INTERFACE_NAME))
.then(() => gTestSuite.removeInterface(ETHERNET_INTERFACE_NAME));
});

View File

@ -0,0 +1,28 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
MARIONETTE_TIMEOUT = 60000;
MARIONETTE_HEAD_JS = 'head.js';
const ETHERNET_INTERFACE_NAME = "eth1";
function checkDhcpResult(ifname) {
return gTestSuite.getDhcpIpAddr(ifname)
.then(ip => gTestSuite.checkInterfaceIpAddr(ifname, ip))
.then(() => gTestSuite.getDhcpGateway(ifname))
.then(gateway => gTestSuite.waitForDefaultRouteSet(ifname, gateway));
}
gTestSuite.doTest(function() {
return Promise.resolve()
.then(() => gTestSuite.checkInterfaceExist(ETHERNET_INTERFACE_NAME))
.then(() => gTestSuite.addInterface(ETHERNET_INTERFACE_NAME))
.then(() => gTestSuite.enableInterface(ETHERNET_INTERFACE_NAME))
.then(() => gTestSuite.makeInterfaceConnect(ETHERNET_INTERFACE_NAME))
.then(() => checkDhcpResult(ETHERNET_INTERFACE_NAME))
.then(() => gTestSuite.makeInterfaceDisconnect(ETHERNET_INTERFACE_NAME))
.then(() => gTestSuite.makeInterfaceConnect(ETHERNET_INTERFACE_NAME))
.then(() => checkDhcpResult(ETHERNET_INTERFACE_NAME))
.then(() => gTestSuite.disableInterface(ETHERNET_INTERFACE_NAME))
.then(() => gTestSuite.removeInterface(ETHERNET_INTERFACE_NAME));
});

View File

@ -0,0 +1,36 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
MARIONETTE_TIMEOUT = 60000;
MARIONETTE_HEAD_JS = 'head.js';
const ETHERNET_INTERFACE_NAME = "eth1";
let staticConfig = {
ip: "1.2.3.4",
gateway: "1.2.3.5",
prefixLength: 24,
dnses: ["1.2.3.6"],
ipMode: "static"
};
function checkStaticResult(ifname) {
return gTestSuite.checkInterfaceIpAddr(ifname, staticConfig.ip)
.then(() => gTestSuite.checkDefaultRoute(ifname, staticConfig.gateway));
}
gTestSuite.doTest(function() {
return Promise.resolve()
.then(() => gTestSuite.checkInterfaceExist(ETHERNET_INTERFACE_NAME))
.then(() => gTestSuite.addInterface(ETHERNET_INTERFACE_NAME))
.then(() => gTestSuite.enableInterface(ETHERNET_INTERFACE_NAME))
.then(() => gTestSuite.updateInterfaceConfig(ETHERNET_INTERFACE_NAME, staticConfig))
.then(() => gTestSuite.makeInterfaceConnect(ETHERNET_INTERFACE_NAME))
.then(() => checkStaticResult(ETHERNET_INTERFACE_NAME))
.then(() => gTestSuite.makeInterfaceDisconnect(ETHERNET_INTERFACE_NAME))
.then(() => gTestSuite.makeInterfaceConnect(ETHERNET_INTERFACE_NAME))
.then(() => checkStaticResult(ETHERNET_INTERFACE_NAME))
.then(() => gTestSuite.makeInterfaceDisconnect(ETHERNET_INTERFACE_NAME))
.then(() => gTestSuite.disableInterface(ETHERNET_INTERFACE_NAME))
.then(() => gTestSuite.removeInterface(ETHERNET_INTERFACE_NAME));
});

View File

@ -0,0 +1,16 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
MARIONETTE_TIMEOUT = 60000;
MARIONETTE_HEAD_JS = 'head.js';
const ETHERNET_INTERFACE_NAME = "eth1";
gTestSuite.doTest(function() {
return Promise.resolve()
.then(() => gTestSuite.checkInterfaceExist(ETHERNET_INTERFACE_NAME))
.then(() => gTestSuite.addInterface(ETHERNET_INTERFACE_NAME))
.then(() => gTestSuite.checkInterfaceListLength(1))
.then(() => gTestSuite.removeInterface(ETHERNET_INTERFACE_NAME))
.then(() => gTestSuite.checkInterfaceListLength(0));
});

View File

@ -45,6 +45,18 @@ let emulator = (function() {
return deferred.promise;
};
function deactivate() {
let deferred = Promise.defer();
let cmd = 'nfc nci rf_intf_deactivate_ntf';
this.run(cmd, function(result) {
is(result.pop(), 'OK', 'check deactivate');
deferred.resolve();
});
return deferred.promise;
};
function notifyDiscoverRE(re, type) {
let deferred = Promise.defer();
let cmd = 'nfc nci rf_discover_ntf ' + re + ' ' + type;
@ -88,6 +100,7 @@ let emulator = (function() {
return {
run: run,
activateRE: activateRE,
deactivate: deactivate,
notifyDiscoverRE: notifyDiscoverRE,
setTagData: setTagData,
snepPutNdef: snepPutNdef

View File

@ -7,6 +7,7 @@ qemu=true
[test_nfc_enabled.js]
[test_nfc_manager_tech_discovered.js]
[test_nfc_manager_tech_discovered_ndef.js]
[test_nfc_manager_tech_lost.js]
[test_nfc_peer.js]
[test_nfc_peer_sendndef.js]
[test_nfc_tag.js]

View File

@ -0,0 +1,37 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
MARIONETTE_TIMEOUT = 30000;
MARIONETTE_HEAD_JS = 'head.js';
function handleTechnologyLost(msg) {
log('Received \'nfc-manager-tech-lost\'');
is(msg.type, 'techLost', 'check for correct message type');
toggleNFC(false).then(runNextTest)
}
function handleTechnologyDiscoveredRE0(msg) {
log('Received \'nfc-manager-tech-discovered\'');
is(msg.type, 'techDiscovered', 'check for correct message type');
is(msg.techList[0], 'P2P', 'check for correct tech type');
emulator.deactivate();
}
function testTechLost() {
log('Running \'testTechLost\'');
window.navigator.mozSetMessageHandler(
'nfc-manager-tech-discovered', handleTechnologyDiscoveredRE0);
window.navigator.mozSetMessageHandler(
'nfc-manager-tech-lost', handleTechnologyLost);
toggleNFC(true).then(() => emulator.activateRE(0));
}
let tests = [
testTechLost
];
SpecialPowers.pushPermissions(
[{'type': 'nfc-manager', 'allow': true, context: document}], runTests);

View File

@ -13,9 +13,9 @@ Cu.import("resource://gre/modules/systemlibs.js");
const NETWORKMANAGER_CONTRACTID = "@mozilla.org/network/manager;1";
const NETWORKMANAGER_CID =
Components.ID("{33901e46-33b8-11e1-9869-f46d04d25bcc}");
Components.ID("{1ba9346b-53b5-4660-9dc6-58f0b258d0a6}");
const DEFAULT_PREFERRED_NETWORK_TYPE = Ci.nsINetworkInterface.NETWORK_TYPE_WIFI;
const DEFAULT_PREFERRED_NETWORK_TYPE = Ci.nsINetworkInterface.NETWORK_TYPE_ETHERNET;
XPCOMUtils.defineLazyServiceGetter(this, "gSettingsService",
"@mozilla.org/settingsService;1",
@ -276,7 +276,8 @@ NetworkManager.prototype = {
if (network.type == Ci.nsINetworkInterface.NETWORK_TYPE_WIFI) {
gNetworkService.resetRoutingTable(network);
#ifdef MOZ_B2G_RIL
} else if (network.type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE) {
} else if (network.type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE ||
network.type == Ci.nsINetworkInterface.NETWORK_TYPE_ETHERNET) {
gNetworkService.removeDefaultRoute(network);
#endif
}
@ -367,7 +368,9 @@ NetworkManager.prototype = {
getNetworkId: function(network) {
let id = "device";
#ifdef MOZ_B2G_RIL
if (this.isNetworkTypeMobile(network.type)) {
if (this.isNetworkTypeEthernet(network.type)) {
id = network.name.substring(3);
} else if (this.isNetworkTypeMobile(network.type)) {
if (!(network instanceof Ci.nsIRilNetworkInterface)) {
throw Components.Exception("Mobile network not an nsIRilNetworkInterface",
Cr.NS_ERROR_INVALID_ARG);
@ -419,17 +422,51 @@ NetworkManager.prototype = {
_dataDefaultServiceId: null,
_networkTypePriorityList: [Ci.nsINetworkInterface.NETWORK_TYPE_ETHERNET,
Ci.nsINetworkInterface.NETWORK_TYPE_WIFI,
Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE],
get networkTypePriorityList() {
return this._networkTypePriorityList;
},
set networkTypePriorityList(val) {
if (val.length != this._networkTypePriorityList.length) {
throw "Priority list length should equal to " +
this._networkTypePriorityList.length;
}
// Check if types in new priority list are valid and also make sure there
// are no duplicate types.
let list = [Ci.nsINetworkInterface.NETWORK_TYPE_ETHERNET,
Ci.nsINetworkInterface.NETWORK_TYPE_WIFI,
Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE];
while (list.length) {
let type = list.shift();
if (val.indexOf(type) == -1) {
throw "There is missing network type";
}
}
this._networkTypePriorityList = val;
},
getPriority: function(type) {
if (this._networkTypePriorityList.indexOf(type) == -1) {
// 0 indicates the lowest priority.
return 0;
}
return this._networkTypePriorityList.length -
this._networkTypePriorityList.indexOf(type);
},
_preferredNetworkType: DEFAULT_PREFERRED_NETWORK_TYPE,
get preferredNetworkType() {
return this._preferredNetworkType;
},
set preferredNetworkType(val) {
#ifdef MOZ_B2G_RIL
if ([Ci.nsINetworkInterface.NETWORK_TYPE_WIFI,
Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE].indexOf(val) == -1) {
#else
if (val != Ci.nsINetworkInterface.NETWORK_TYPE_WIFI) {
#endif
Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE,
Ci.nsINetworkInterface.NETWORK_TYPE_ETHERNET].indexOf(val) == -1) {
throw "Invalid network type";
}
this._preferredNetworkType = val;
@ -461,6 +498,10 @@ NetworkManager.prototype = {
this.isNetworkTypeSecondaryMobile(type));
},
isNetworkTypeEthernet: function(type) {
return (type == Ci.nsINetworkInterface.NETWORK_TYPE_ETHERNET);
},
setExtraHostRoute: function(network) {
if (network.type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_MMS) {
if (!(network instanceof Ci.nsIRilNetworkInterface)) {
@ -601,11 +642,23 @@ NetworkManager.prototype = {
defaultDataNetwork = network;
}
#endif
this.active = network;
if (network.type == this.preferredNetworkType) {
this.active = network;
debug("Found our preferred type of network: " + network.name);
break;
}
// Initialize the active network with the first connected network.
if (!this.active) {
this.active = network;
continue;
}
// Compare the prioriy between two network types. If found incoming
// network with higher priority, replace the active network.
if (this.getPriority(this.active.type) < this.getPriority(network.type)) {
this.active = network;
}
}
if (this.active) {
#ifdef MOZ_B2G_RIL
@ -680,7 +733,8 @@ NetworkManager.prototype = {
// the function will return null so that it won't trigger type change event
// in NetworkInformation API.
if (network.type != Ci.nsINetworkInterface.NETWORK_TYPE_WIFI &&
network.type != Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE) {
network.type != Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE &&
network.type != Ci.nsINetworkInterface.NETWORK_TYPE_ETHERNET) {
return null;
}
@ -693,6 +747,8 @@ NetworkManager.prototype = {
return CONNECTION_TYPE_WIFI;
case Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE:
return CONNECTION_TYPE_CULLULAR;
case Ci.nsINetworkInterface.NETWORK_TYPE_ETHERNET:
return CONNECTION_TYPE_ETHERNET;
}
},

View File

@ -1,3 +1,3 @@
# NetworkManager.js
component {33901e46-33b8-11e1-9869-f46d04d25bcc} NetworkManager.js
contract @mozilla.org/network/manager;1 {33901e46-33b8-11e1-9869-f46d04d25bcc}
component {1ba9346b-53b5-4660-9dc6-58f0b258d0a6} NetworkManager.js
contract @mozilla.org/network/manager;1 {1ba9346b-53b5-4660-9dc6-58f0b258d0a6}

View File

@ -12,7 +12,7 @@ Cu.import("resource://gre/modules/NetUtil.jsm");
Cu.import("resource://gre/modules/FileUtils.jsm");
const NETWORKSERVICE_CONTRACTID = "@mozilla.org/network/service;1";
const NETWORKSERVICE_CID = Components.ID("{baec696c-c78d-42db-8b44-603f8fbfafb4}");
const NETWORKSERVICE_CID = Components.ID("{48c13741-aec9-4a86-8962-432011708261}");
XPCOMUtils.defineLazyServiceGetter(this, "gNetworkWorker",
"@mozilla.org/network/worker;1",
@ -542,6 +542,81 @@ NetworkService.prototype = {
});
},
getInterfaces: function(callback) {
let params = {
cmd: "getInterfaces",
isAsync: true
};
this.controlMessage(params, function(data) {
if(DEBUG) debug("getInterfaces result: " + JSON.stringify(data));
let success = !isError(data.resultCode);
callback.getInterfacesResult(success, data.interfaceList);
});
},
setInterfaceConfig: function(config, callback) {
config.cmd = "setInterfaceConfig";
config.isAsync = true;
this.controlMessage(config, function(data) {
if(DEBUG) debug("setInterfaceConfig result: " + JSON.stringify(data));
let success = !isError(data.resultCode);
callback.setInterfaceConfigResult(success);
});
},
getInterfaceConfig: function(ifname, callback) {
let params = {
cmd: "getInterfaceConfig",
ifname: ifname,
isAsync: true
};
this.controlMessage(params, function(data) {
if(DEBUG) debug("getInterfaceConfig result: " + JSON.stringify(data));
let success = !isError(data.resultCode);
let result = { ip: data.ipAddr,
prefix: data.maskLength,
link: data.flag,
mac: data.macAddr };
callback.getInterfaceConfigResult(success, result);
});
},
runDhcp: function(ifname, callback) {
let params = {
cmd: "runDhcp",
ifname: ifname,
isBlocking: true
};
this.controlMessage(params, function(data) {
if(DEBUG) debug("runDhcp result: " + JSON.stringify(data));
let success = data.success;
let result = {
ip: data.ipAddr,
gateway: data.gateway,
dns1: data.dns1,
dns2: data.dns2,
prefix: data.maskLength,
server: data.server
};
callback.runDhcpResult(success, result);
});
},
stopDhcp: function(ifname) {
let params = {
cmd: "stopDhcp",
ifname: ifname,
isAsync: true
};
this.controlMessage(params);
},
shutdown: false,
observe: function observe(aSubject, aTopic, aData) {

View File

@ -1,3 +1,3 @@
# NetworkService.js
component {baec696c-c78d-42db-8b44-603f8fbfafb4} NetworkService.js
contract @mozilla.org/network/service;1 {baec696c-c78d-42db-8b44-603f8fbfafb4}
component {48c13741-aec9-4a86-8962-432011708261} NetworkService.js
contract @mozilla.org/network/service;1 {48c13741-aec9-4a86-8962-432011708261}

View File

@ -111,7 +111,7 @@ CommandFunc NetworkUtils::sWifiEnableChain[] = {
NetworkUtils::startAccessPointDriver,
NetworkUtils::setAccessPoint,
NetworkUtils::startSoftAP,
NetworkUtils::setInterfaceUp,
NetworkUtils::setConfig,
NetworkUtils::tetherInterface,
NetworkUtils::setIpForwardingEnabled,
NetworkUtils::tetheringStatus,
@ -152,7 +152,7 @@ CommandFunc NetworkUtils::sWifiRetryChain[] = {
NetworkUtils::startAccessPointDriver,
NetworkUtils::setAccessPoint,
NetworkUtils::startSoftAP,
NetworkUtils::setInterfaceUp,
NetworkUtils::setConfig,
NetworkUtils::tetherInterface,
NetworkUtils::setIpForwardingEnabled,
NetworkUtils::tetheringStatus,
@ -168,7 +168,7 @@ CommandFunc NetworkUtils::sWifiOperationModeChain[] = {
};
CommandFunc NetworkUtils::sUSBEnableChain[] = {
NetworkUtils::setInterfaceUp,
NetworkUtils::setConfig,
NetworkUtils::enableNat,
NetworkUtils::setIpForwardingEnabled,
NetworkUtils::tetherInterface,
@ -201,7 +201,7 @@ CommandFunc NetworkUtils::sUpdateUpStreamChain[] = {
};
CommandFunc NetworkUtils::sStartDhcpServerChain[] = {
NetworkUtils::setInterfaceUp,
NetworkUtils::setConfig,
NetworkUtils::startTethering,
NetworkUtils::setDhcpServerSuccess
};
@ -240,6 +240,21 @@ CommandFunc NetworkUtils::sSetDnsChain[] = {
NetworkUtils::setInterfaceDns
};
CommandFunc NetworkUtils::sGetInterfacesChain[] = {
NetworkUtils::getInterfaceList,
NetworkUtils::getInterfacesSuccess
};
CommandFunc NetworkUtils::sSetInterfaceConfigChain[] = {
NetworkUtils::setConfig,
NetworkUtils::setInterfaceConfigSuccess
};
CommandFunc NetworkUtils::sGetInterfaceConfigChain[] = {
NetworkUtils::getConfig,
NetworkUtils::getInterfaceConfigSuccess
};
/**
* Helper function to get the mask from given prefix length.
*/
@ -322,6 +337,15 @@ static void join(nsTArray<nsCString>& array,
#undef CHECK_LEN
}
static void convertUTF8toUTF16(nsTArray<nsCString>& narrow,
nsTArray<nsString>& wide,
uint32_t length)
{
for (uint32_t i = 0; i < length; i++) {
wide.AppendElement(NS_ConvertUTF8toUTF16(narrow[i].get()));
}
}
/**
* Helper function to get network interface properties from the system property table.
*/
@ -686,9 +710,9 @@ void NetworkUtils::setAlarm(CommandChain* aChain,
doCommand(command, aChain, aCallback);
}
void NetworkUtils::setInterfaceUp(CommandChain* aChain,
CommandCallback aCallback,
NetworkResultOptions& aResult)
void NetworkUtils::setConfig(CommandChain* aChain,
CommandCallback aCallback,
NetworkResultOptions& aResult)
{
char command[MAX_COMMAND_SIZE];
if (SDK_VERSION >= 16) {
@ -923,6 +947,26 @@ void NetworkUtils::setInterfaceDns(CommandChain* aChain,
doCommand(command, aChain, aCallback);
}
void NetworkUtils::getInterfaceList(CommandChain* aChain,
CommandCallback aCallback,
NetworkResultOptions& aResult)
{
char command[MAX_COMMAND_SIZE];
snprintf(command, MAX_COMMAND_SIZE - 1, "interface list");
doCommand(command, aChain, aCallback);
}
void NetworkUtils::getConfig(CommandChain* aChain,
CommandCallback aCallback,
NetworkResultOptions& aResult)
{
char command[MAX_COMMAND_SIZE];
snprintf(command, MAX_COMMAND_SIZE - 1, "interface getcfg %s", GET_CHAR(mIfname));
doCommand(command, aChain, aCallback);
}
#undef GET_CHAR
#undef GET_FIELD
@ -1063,6 +1107,75 @@ void NetworkUtils::setDnsFail(NetworkParams& aOptions, NetworkResultOptions& aRe
postMessage(aOptions, aResult);
}
void NetworkUtils::getInterfacesFail(NetworkParams& aOptions, NetworkResultOptions& aResult)
{
postMessage(aOptions, aResult);
}
void NetworkUtils::getInterfacesSuccess(CommandChain* aChain,
CommandCallback aCallback,
NetworkResultOptions& aResult)
{
char buf[BUF_SIZE];
NS_ConvertUTF16toUTF8 reason(aResult.mResultReason);
memcpy(buf, reason.get(), strlen(reason.get()));
nsTArray<nsCString> result;
split(buf, INTERFACE_DELIMIT, result);
nsTArray<nsString> interfaceList;
uint32_t length = result.Length();
convertUTF8toUTF16(result, interfaceList, length);
aResult.mInterfaceList.Construct();
for (uint32_t i = 0; i < length; i++) {
aResult.mInterfaceList.Value().AppendElement(interfaceList[i]);
}
postMessage(aChain->getParams(), aResult);
}
void NetworkUtils::setInterfaceConfigFail(NetworkParams& aOptions, NetworkResultOptions& aResult)
{
postMessage(aOptions, aResult);
}
void NetworkUtils::setInterfaceConfigSuccess(CommandChain* aChain,
CommandCallback aCallback,
NetworkResultOptions& aResult)
{
postMessage(aChain->getParams(), aResult);
}
void NetworkUtils::getInterfaceConfigFail(NetworkParams& aOptions, NetworkResultOptions& aResult)
{
postMessage(aOptions, aResult);
}
void NetworkUtils::getInterfaceConfigSuccess(CommandChain* aChain,
CommandCallback aCallback,
NetworkResultOptions& aResult)
{
char buf[BUF_SIZE];
NS_ConvertUTF16toUTF8 reason(aResult.mResultReason);
memcpy(buf, reason.get(), strlen(reason.get()));
nsTArray<nsCString> result;
split(buf, NETD_MESSAGE_DELIMIT, result);
ASSIGN_FIELD_VALUE(mMacAddr, NS_ConvertUTF8toUTF16(result[0]))
ASSIGN_FIELD_VALUE(mIpAddr, NS_ConvertUTF8toUTF16(result[1]))
ASSIGN_FIELD_VALUE(mMaskLength, atof(result[2].get()))
if (result[3].Find("up")) {
ASSIGN_FIELD_VALUE(mFlag, NS_ConvertUTF8toUTF16("up"))
} else {
ASSIGN_FIELD_VALUE(mFlag, NS_ConvertUTF8toUTF16("down"))
}
postMessage(aChain->getParams(), aResult);
}
#undef ASSIGN_FIELD
#undef ASSIGN_FIELD_VALUE
@ -1127,6 +1240,14 @@ void NetworkUtils::ExecuteCommand(NetworkParams aOptions)
enableUsbRndis(aOptions);
} else if (aOptions.mCmd.EqualsLiteral("updateUpStream")) {
updateUpStream(aOptions);
} else if (aOptions.mCmd.EqualsLiteral("getInterfaces")) {
getInterfaces(aOptions);
} else if (aOptions.mCmd.EqualsLiteral("stopDhcp")) {
stopDhcp(aOptions);
} else if (aOptions.mCmd.EqualsLiteral("setInterfaceConfig")) {
setInterfaceConfig(aOptions);
} else if (aOptions.mCmd.EqualsLiteral("getInterfaceConfig")) {
getInterfaceConfig(aOptions);
} else {
WARN("unknon message");
return;
@ -1716,7 +1837,7 @@ bool NetworkUtils::enableUsbRndis(NetworkParams& aOptions)
}
/**
* handling upstream interface change event.
* Handling upstream interface change event.
*/
bool NetworkUtils::updateUpStream(NetworkParams& aOptions)
{
@ -1724,6 +1845,42 @@ bool NetworkUtils::updateUpStream(NetworkParams& aOptions)
return true;
}
/**
* Stop dhcp client deamon.
*/
bool NetworkUtils::stopDhcp(NetworkParams& aOptions)
{
mNetUtils->do_dhcp_stop(GET_CHAR(mIfname));
return true;
}
/**
* Get existing network interfaces.
*/
bool NetworkUtils::getInterfaces(NetworkParams& aOptions)
{
RUN_CHAIN(aOptions, sGetInterfacesChain, getInterfacesFail)
return true;
}
/**
* Set network config for a specified interface.
*/
bool NetworkUtils::setInterfaceConfig(NetworkParams& aOptions)
{
RUN_CHAIN(aOptions, sSetInterfaceConfigChain, setInterfaceConfigFail)
return true;
}
/**
* Get network config of a specified interface.
*/
bool NetworkUtils::getInterfaceConfig(NetworkParams& aOptions)
{
RUN_CHAIN(aOptions, sGetInterfaceConfigChain, getInterfaceConfigFail)
return true;
}
void NetworkUtils::sendBroadcastMessage(uint32_t code, char* reason)
{
NetworkResultOptions result;

View File

@ -72,6 +72,7 @@ public:
mCurInternalIfname = aOther.mCurInternalIfname;
mCurExternalIfname = aOther.mCurExternalIfname;
mThreshold = aOther.mThreshold;
mIsBlocking = aOther.mIsBlocking;
}
NetworkParams(const mozilla::dom::NetworkCommandOptions& aOther) {
@ -148,6 +149,7 @@ public:
COPY_OPT_STRING_FIELD(mCurInternalIfname, EmptyString())
COPY_OPT_STRING_FIELD(mCurExternalIfname, EmptyString())
COPY_OPT_FIELD(mThreshold, -1)
COPY_OPT_FIELD(mIsBlocking, false)
#undef COPY_SEQUENCE_FIELD
#undef COPY_OPT_STRING_FIELD
@ -198,6 +200,7 @@ public:
nsString mCurInternalIfname;
nsString mCurExternalIfname;
long mThreshold;
bool mIsBlocking;
};
// CommandChain store the necessary information to execute command one by one.
@ -281,6 +284,10 @@ private:
bool setUSBTethering(NetworkParams& aOptions);
bool enableUsbRndis(NetworkParams& aOptions);
bool updateUpStream(NetworkParams& aOptions);
bool getInterfaces(NetworkParams& aOptions);
bool stopDhcp(NetworkParams& aOptions);
bool setInterfaceConfig(NetworkParams& aOptions);
bool getInterfaceConfig(NetworkParams& aOptions);
/**
* function pointer array holds all netd commands should be executed
@ -302,6 +309,9 @@ private:
static CommandFunc sNetworkInterfaceDisableAlarmChain[];
static CommandFunc sNetworkInterfaceSetAlarmChain[];
static CommandFunc sSetDnsChain[];
static CommandFunc sGetInterfacesChain[];
static CommandFunc sSetInterfaceConfigChain[];
static CommandFunc sGetInterfaceConfigChain[];
/**
* Individual netd command stored in command chain.
@ -338,6 +348,9 @@ private:
static void disableNat(PARAMS);
static void setDefaultInterface(PARAMS);
static void setInterfaceDns(PARAMS);
static void getInterfaceList(PARAMS);
static void setConfig(PARAMS);
static void getConfig(PARAMS);
static void wifiTetheringSuccess(PARAMS);
static void usbTetheringSuccess(PARAMS);
static void networkInterfaceStatsSuccess(PARAMS);
@ -345,6 +358,9 @@ private:
static void updateUpStreamSuccess(PARAMS);
static void setDhcpServerSuccess(PARAMS);
static void wifiOperationModeSuccess(PARAMS);
static void getInterfacesSuccess(PARAMS);
static void setInterfaceConfigSuccess(PARAMS);
static void getInterfaceConfigSuccess(PARAMS);
#undef PARAMS
/**
@ -360,6 +376,9 @@ private:
static void networkInterfaceStatsFail(PARAMS);
static void networkInterfaceAlarmFail(PARAMS);
static void setDnsFail(PARAMS);
static void getInterfacesFail(PARAMS);
static void setInterfaceConfigFail(PARAMS);
static void getInterfaceConfigFail(PARAMS);
#undef PARAMS
/**

View File

@ -18,6 +18,8 @@ using namespace mozilla;
using namespace mozilla::dom;
using namespace mozilla::ipc;
#define PROPERTY_VALUE_MAX 80
namespace mozilla {
nsCOMPtr<nsIThread> gWorkerThread;
@ -37,6 +39,15 @@ public:
MOZ_ASSERT(!NS_IsMainThread());
#define COPY_FIELD(prop) mResult.prop = aResult.prop;
#define COPY_SEQUENCE_FIELD(prop, type) \
if (aResult.prop.WasPassed()) { \
mozilla::dom::Sequence<type > const & currentValue = aResult.prop.InternalValue(); \
uint32_t length = currentValue.Length(); \
mResult.prop.Construct(); \
for (uint32_t idx = 0; idx < length; idx++) { \
mResult.prop.Value().AppendElement(currentValue[idx]); \
} \
}
COPY_FIELD(mId)
COPY_FIELD(mRet)
COPY_FIELD(mBroadcast)
@ -53,6 +64,16 @@ public:
COPY_FIELD(mSuccess)
COPY_FIELD(mCurExternalIfname)
COPY_FIELD(mCurInternalIfname)
COPY_FIELD(mIpAddr)
COPY_FIELD(mGateway)
COPY_FIELD(mDns1)
COPY_FIELD(mDns2)
COPY_FIELD(mServer)
COPY_FIELD(mLease)
COPY_FIELD(mVendorInfo)
COPY_FIELD(mMaskLength)
COPY_FIELD(mFlag)
COPY_SEQUENCE_FIELD(mInterfaceList, nsString)
#undef COPY_FIELD
}
@ -92,6 +113,66 @@ private:
NetworkParams mParams;
};
// Runnable used for blocking command.
class RunDhcpEvent : public nsRunnable
{
public:
RunDhcpEvent(const NetworkParams& aParams)
: mParams(aParams)
{}
NS_IMETHOD Run()
{
MOZ_ASSERT(!NS_IsMainThread());
nsAutoPtr<NetUtils> netUtils;
netUtils = new NetUtils();
NetworkResultOptions result;
result.mId = mParams.mId;
int32_t status;
char ipaddr[PROPERTY_VALUE_MAX];
char gateway[PROPERTY_VALUE_MAX];
uint32_t prefixLength;
char dns1[PROPERTY_VALUE_MAX];
char dns2[PROPERTY_VALUE_MAX];
char server[PROPERTY_VALUE_MAX];
uint32_t lease;
char vendorinfo[PROPERTY_VALUE_MAX];
status = netUtils->do_dhcp_do_request(NS_ConvertUTF16toUTF8(mParams.mIfname).get(),
ipaddr,
gateway,
&prefixLength,
dns1,
dns2,
server,
&lease,
vendorinfo);
if (status == 0) {
// run dhcp success.
result.mSuccess = true;
result.mIpAddr = NS_ConvertUTF8toUTF16(ipaddr);
result.mGateway = NS_ConvertUTF8toUTF16(gateway);
result.mDns1 = NS_ConvertUTF8toUTF16(dns1);
result.mDns2 = NS_ConvertUTF8toUTF16(dns2);
result.mServer = NS_ConvertUTF8toUTF16(server);
result.mLease = lease;
result.mVendorInfo = NS_ConvertUTF8toUTF16(vendorinfo);
result.mMaskLength = prefixLength;
}
nsCOMPtr<nsIRunnable> runnable = new NetworkResultDispatcher(result);
NS_DispatchToMainThread(runnable);
return NS_OK;
}
private:
NetworkParams mParams;
};
// Runnable used dispatch netd result on the worker thread.
class NetdEventRunnable : public nsRunnable
{
@ -224,8 +305,14 @@ NetworkWorker::PostMessage(JS::Handle<JS::Value> aOptions, JSContext* aCx)
return NS_ERROR_FAILURE;
}
// Dispatch the command to the control thread.
NetworkParams NetworkParams(options);
if (NetworkParams.mIsBlocking) {
NetworkWorker::HandleBlockingCommand(NetworkParams);
return NS_OK;
}
// Dispatch the command to the control thread.
nsCOMPtr<nsIRunnable> runnable = new NetworkCommandDispatcher(NetworkParams);
if (gWorkerThread) {
gWorkerThread->Dispatch(runnable, nsIEventTarget::DISPATCH_NORMAL);
@ -233,6 +320,24 @@ NetworkWorker::PostMessage(JS::Handle<JS::Value> aOptions, JSContext* aCx)
return NS_OK;
}
void
NetworkWorker::HandleBlockingCommand(NetworkParams& aOptions)
{
if (aOptions.mCmd.EqualsLiteral("runDhcp")) {
NetworkWorker::RunDhcp(aOptions);
}
}
void
NetworkWorker::RunDhcp(NetworkParams& aOptions)
{
MOZ_ASSERT(NS_IsMainThread());
nsCOMPtr<nsIRunnable> runnable = new RunDhcpEvent(aOptions);
nsCOMPtr<nsIThread> thread;
NS_NewThread(getter_AddRefs(thread), runnable);
}
void
NetworkWorker::DispatchNetworkResult(const NetworkResultOptions& aOptions)
{

View File

@ -11,6 +11,8 @@
#include "nsCOMPtr.h"
#include "nsThread.h"
class NetworkParams;
namespace mozilla {
class NetworkWorker MOZ_FINAL : public nsINetworkWorker
@ -29,6 +31,9 @@ private:
static void NotifyResult(mozilla::dom::NetworkResultOptions& aResult);
void HandleBlockingCommand(NetworkParams& aParams);
void RunDhcp(NetworkParams& aParams);
nsCOMPtr<nsINetworkEventListener> mListener;
};

View File

@ -9,7 +9,7 @@ interface nsIWifiTetheringCallback;
/**
* Information about networks that is exposed to network manager API consumers.
*/
[scriptable, uuid(cb62ae03-6bda-43ff-9560-916d60203d33)]
[scriptable, uuid(8f9ab9e0-72c1-4874-80c7-8143353b979f)]
interface nsINetworkInterface : nsISupports
{
const long NETWORK_STATE_UNKNOWN = -1;
@ -17,6 +17,8 @@ interface nsINetworkInterface : nsISupports
const long NETWORK_STATE_CONNECTED = 1;
const long NETWORK_STATE_DISCONNECTING = 2;
const long NETWORK_STATE_DISCONNECTED = 3;
const long NETWORK_STATE_ENABLED = 4;
const long NETWORK_STATE_DISABLED = 5;
/**
* Current network state, one of the NETWORK_STATE_* constants.
@ -34,6 +36,7 @@ interface nsINetworkInterface : nsISupports
const long NETWORK_TYPE_WIFI_P2P = 4;
const long NETWORK_TYPE_MOBILE_IMS = 5;
const long NETWORK_TYPE_MOBILE_DUN = 6;
const long NETWORK_TYPE_ETHERNET = 7;
/**
* Network type. One of the NETWORK_TYPE_* constants.
@ -97,7 +100,7 @@ interface nsINetworkInterface : nsISupports
/**
* Manage network interfaces.
*/
[scriptable, uuid(3ea50550-4b3c-11e3-8f96-0800200c9a66)]
[scriptable, uuid(1ba9346b-53b5-4660-9dc6-58f0b258d0a6)]
interface nsINetworkManager : nsISupports
{
/**
@ -135,6 +138,15 @@ interface nsINetworkManager : nsISupports
*/
readonly attribute jsval networkInterfaces;
/**
* Priority list of network types. An array of
* nsINetworkInterface::NETWORK_TYPE_* constants.
*
* The piror position of the type indicates the higher priority. The priority
* is used to determine route when there are multiple connected networks.
*/
attribute jsval networkTypePriorityList;
/**
* The preferred network type. One of the
* nsINetworkInterface::NETWORK_TYPE_* constants.

View File

@ -101,10 +101,65 @@ interface nsIUpdateUpStreamCallback : nsISupports
void updateUpStreamResult(in boolean success, in DOMString externalIfname);
};
[scriptable, function, uuid(4a9166f3-7e4f-4d10-bb5c-b49ee21d6184)]
interface nsIRunDhcpCallback : nsISupports
{
/**
* Callback function used to report the result of dhcp request.
*
* @param success
* Boolean to indicate the operation is successful or not.
*/
void runDhcpResult(in boolean success, in jsval result);
};
[scriptable, function, uuid(88e3ee22-f1b3-4fa0-8a5d-793fb827c42c)]
interface nsIGetInterfacesCallback : nsISupports
{
/**
* Callback function used to return the list of existing network interfaces.
*
* @param success
* Boolean to indicate the operation is successful or not.
* @param interfaceList
* An array of interface name.
*/
void getInterfacesResult(in boolean success, in jsval interfaceList);
};
[scriptable, function, uuid(064e02a3-d2c0-42c5-a293-1efa84056100)]
interface nsIGetInterfaceConfigCallback : nsISupports
{
/**
* Callback function used to return the network config of a given interface.
*
* @param success
* Boolean to indicate the operation is successful or not.
* @param result
* .ip: Ip address.
* .prefix: mask length.
* .link: network link properties.
* .mac: mac address.
*/
void getInterfaceConfigResult(in boolean success, in jsval result);
};
[scriptable, function, uuid(b370f360-6ba8-4517-a4f9-31e8f004ee91)]
interface nsISetInterfaceConfigCallback : nsISupports
{
/**
* Callback function used to set network cofig for a specified interface.
*
* @param success
* Boolean to indicate the operation is successful or not.
*/
void setInterfaceConfigResult(in boolean success);
};
/**
* Provide network services.
*/
[scriptable, uuid(f96461fa-e844-45d2-a6c3-8cd23ab0916b)]
[scriptable, uuid(48c13741-aec9-4a86-8962-432011708261)]
interface nsINetworkService : nsISupports
{
/**
@ -345,4 +400,53 @@ interface nsINetworkService : nsISupports
void updateUpStream(in jsval previous,
in jsval current,
in nsIUpdateUpStreamCallback callback);
/**
* Run Dhcp request.
*
* @param ifname
* Target interface.
* @param callback
* Callback function to report the result.
*/
void runDhcp(in DOMString ifname, in nsIRunDhcpCallback callback);
/**
* Stop Dhcp daemon.
*
* @param ifname
* Target interface.
*/
void stopDhcp(in DOMString ifname);
/*
* Obtain interfaces list.
*
* @param callback
* Callback function to return the result.
*/
void getInterfaces(in nsIGetInterfacesCallback callback);
/**
* Set config for a network interface.
*
* @param config
* .ifname: Target interface.
* .ip: Ip address.
* .prefix: mask length.
* .link: network link properties.
* @param callback
* Callback function to report the result.
*/
void setInterfaceConfig(in jsval config, in nsISetInterfaceConfigCallback callback);
/**
* Get config of a network interface.
*
* @param ifname
* Target interface.
* @param callback
* Callback function to report the result.
*/
void getInterfaceConfig(in DOMString ifname, in nsIGetInterfaceConfigCallback callback);
};

View File

@ -20,7 +20,7 @@ const char* kPrefRilNumRadioInterfaces = "ril.numRadioInterfaces";
using namespace mozilla::dom;
class Voicemail::Listener : public nsIVoicemailListener
class Voicemail::Listener MOZ_FINAL : public nsIVoicemailListener
{
Voicemail* mVoicemail;

View File

@ -3,7 +3,7 @@
* You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* This dictionnary holds the parameters sent to the network worker.
* This dictionary holds the parameters sent to the network worker.
*/
dictionary NetworkCommandOptions
{
@ -55,6 +55,7 @@ dictionary NetworkCommandOptions
DOMString preExternalIfname; // for "updateUpStream".
DOMString curInternalIfname; // for "updateUpStream".
DOMString curExternalIfname; // for "updateUpStream".
boolean isBlocking; // for "runDhcp".
};
/**
@ -81,4 +82,18 @@ dictionary NetworkResultOptions
boolean success = false; // for "setDhcpServer".
DOMString curExternalIfname = ""; // for "updateUpStream".
DOMString curInternalIfname = ""; // for "updateUpStream".
DOMString ipAddr = ""; // for "runDhcp", "getInterfaceConfig".
DOMString gateway = ""; // for "runDhcp".
DOMString dns1 = ""; // for "runDhcp".
DOMString dns2 = ""; // for "runDhcp".
DOMString server = ""; // for "runDhcp".
short lease = 0; // for "runDhcp".
DOMString vendorInfo = ""; // for "runDhcp".
short maskLength = 0; // for "runDhcp".
DOMString flag = "down"; // for "getInterfaceConfig".
DOMString macAddr = ""; // for "getInterfaceConfig".
sequence<DOMString> interfaceList; // for "getInterfaceList".
};

View File

@ -28,6 +28,7 @@ skip = false
[include:../../../../../dom/events/test/marionette/manifest.ini]
[include:../../../../../dom/wifi/test/marionette/manifest.ini]
[include:../../../../../dom/cellbroadcast/tests/marionette/manifest.ini]
[include:../../../../../dom/network/tests/marionette/manifest.ini]
; layout tests
[include:../../../../../layout/base/tests/marionette/manifest.ini]

View File

@ -262,7 +262,7 @@ WebappsActor.prototype = {
reg.broadcastMessage("Webapps:UpdateState", {
app: aApp,
manifest: manifest,
manifestURL: aApp.manifestURL
id: aApp.id
});
reg.broadcastMessage("Webapps:FireEvent", {
eventType: ["downloadsuccess", "downloadapplied"],