mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-10-06 07:17:30 +09:00
import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo
This commit is contained in:
commit
dcd9973243
150858 changed files with 23884658 additions and 0 deletions
96
dom/network/Connection.cpp
Normal file
96
dom/network/Connection.cpp
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* 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 <limits>
|
||||
#include "mozilla/Hal.h"
|
||||
#include "mozilla/dom/network/Connection.h"
|
||||
#include "nsIDOMClassInfo.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "Constants.h"
|
||||
|
||||
/**
|
||||
* We have to use macros here because our leak analysis tool things we are
|
||||
* leaking strings when we have |static const nsString|. Sad :(
|
||||
*/
|
||||
#define CHANGE_EVENT_NAME NS_LITERAL_STRING("typechange")
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
namespace network {
|
||||
|
||||
NS_IMPL_QUERY_INTERFACE_INHERITED(Connection, DOMEventTargetHelper,
|
||||
nsINetworkProperties)
|
||||
|
||||
// Don't use |Connection| alone, since that confuses nsTraceRefcnt since
|
||||
// we're not the only class with that name.
|
||||
NS_IMPL_ADDREF_INHERITED(dom::network::Connection, DOMEventTargetHelper)
|
||||
NS_IMPL_RELEASE_INHERITED(dom::network::Connection, DOMEventTargetHelper)
|
||||
|
||||
Connection::Connection(nsPIDOMWindowInner* aWindow)
|
||||
: DOMEventTargetHelper(aWindow)
|
||||
, mType(static_cast<ConnectionType>(kDefaultType))
|
||||
, mIsWifi(kDefaultIsWifi)
|
||||
, mDHCPGateway(kDefaultDHCPGateway)
|
||||
{
|
||||
hal::RegisterNetworkObserver(this);
|
||||
|
||||
hal::NetworkInformation networkInfo;
|
||||
hal::GetCurrentNetworkInformation(&networkInfo);
|
||||
|
||||
UpdateFromNetworkInfo(networkInfo);
|
||||
}
|
||||
|
||||
void
|
||||
Connection::Shutdown()
|
||||
{
|
||||
hal::UnregisterNetworkObserver(this);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
Connection::GetIsWifi(bool *aIsWifi)
|
||||
{
|
||||
*aIsWifi = mIsWifi;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
Connection::GetDhcpGateway(uint32_t *aGW)
|
||||
{
|
||||
*aGW = mDHCPGateway;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
Connection::UpdateFromNetworkInfo(const hal::NetworkInformation& aNetworkInfo)
|
||||
{
|
||||
mType = static_cast<ConnectionType>(aNetworkInfo.type());
|
||||
mIsWifi = aNetworkInfo.isWifi();
|
||||
mDHCPGateway = aNetworkInfo.dhcpGateway();
|
||||
}
|
||||
|
||||
void
|
||||
Connection::Notify(const hal::NetworkInformation& aNetworkInfo)
|
||||
{
|
||||
ConnectionType previousType = mType;
|
||||
|
||||
UpdateFromNetworkInfo(aNetworkInfo);
|
||||
|
||||
if (previousType == mType) {
|
||||
return;
|
||||
}
|
||||
|
||||
DispatchTrustedEvent(CHANGE_EVENT_NAME);
|
||||
}
|
||||
|
||||
JSObject*
|
||||
Connection::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return NetworkInformationBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
} // namespace network
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
80
dom/network/Connection.h
Normal file
80
dom/network/Connection.h
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_network_Connection_h
|
||||
#define mozilla_dom_network_Connection_h
|
||||
|
||||
#include "Types.h"
|
||||
#include "mozilla/DOMEventTargetHelper.h"
|
||||
#include "mozilla/Observer.h"
|
||||
#include "mozilla/dom/NetworkInformationBinding.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsINetworkProperties.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
namespace hal {
|
||||
class NetworkInformation;
|
||||
} // namespace hal
|
||||
|
||||
namespace dom {
|
||||
namespace network {
|
||||
|
||||
class Connection final : public DOMEventTargetHelper
|
||||
, public NetworkObserver
|
||||
, public nsINetworkProperties
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_NSINETWORKPROPERTIES
|
||||
|
||||
NS_REALLY_FORWARD_NSIDOMEVENTTARGET(DOMEventTargetHelper)
|
||||
|
||||
explicit Connection(nsPIDOMWindowInner* aWindow);
|
||||
|
||||
void Shutdown();
|
||||
|
||||
// For IObserver
|
||||
void Notify(const hal::NetworkInformation& aNetworkInfo) override;
|
||||
|
||||
// WebIDL
|
||||
|
||||
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
ConnectionType Type() const { return mType; }
|
||||
|
||||
IMPL_EVENT_HANDLER(typechange)
|
||||
|
||||
private:
|
||||
~Connection() {}
|
||||
|
||||
/**
|
||||
* Update the connection information stored in the object using a
|
||||
* NetworkInformation object.
|
||||
*/
|
||||
void UpdateFromNetworkInfo(const hal::NetworkInformation& aNetworkInfo);
|
||||
|
||||
/**
|
||||
* The type of current connection.
|
||||
*/
|
||||
ConnectionType mType;
|
||||
|
||||
/**
|
||||
* If the connection is WIFI
|
||||
*/
|
||||
bool mIsWifi;
|
||||
|
||||
/**
|
||||
* DHCP Gateway information for IPV4, in network byte order. 0 if unassigned.
|
||||
*/
|
||||
uint32_t mDHCPGateway;
|
||||
};
|
||||
|
||||
} // namespace network
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_network_Connection_h
|
||||
25
dom/network/Constants.h
Normal file
25
dom/network/Constants.h
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_network_Constants_h__
|
||||
#define mozilla_dom_network_Constants_h__
|
||||
|
||||
/**
|
||||
* A set of constants to be used by network backends.
|
||||
*/
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
namespace network {
|
||||
|
||||
static const uint32_t kDefaultType = 5; // ConnectionType::None
|
||||
static const bool kDefaultIsWifi = false;
|
||||
static const uint32_t kDefaultDHCPGateway = 0;
|
||||
|
||||
} // namespace network
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_network_Constants_h__
|
||||
655
dom/network/EthernetManager.js
Normal file
655
dom/network/EthernetManager.js
Normal file
|
|
@ -0,0 +1,655 @@
|
|||
/* -*- 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 {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
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";
|
||||
|
||||
const PREF_NETWORK_DEBUG_ENABLED = "network.debugging.enabled";
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "gNetworkManager",
|
||||
"@mozilla.org/network/manager;1",
|
||||
"nsINetworkManager");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "gNetworkService",
|
||||
"@mozilla.org/network/service;1",
|
||||
"nsINetworkService");
|
||||
|
||||
let debug;
|
||||
function updateDebug() {
|
||||
let debugPref = false; // set default value here.
|
||||
try {
|
||||
debugPref = debugPref || Services.prefs.getBoolPref(PREF_NETWORK_DEBUG_ENABLED);
|
||||
} catch (e) {}
|
||||
|
||||
if (debugPref) {
|
||||
debug = function(s) {
|
||||
dump("-*- EthernetManager: " + s + "\n");
|
||||
};
|
||||
} else {
|
||||
debug = function(s) {};
|
||||
}
|
||||
}
|
||||
updateDebug();
|
||||
|
||||
// nsINetworkInterface
|
||||
|
||||
function EthernetInterface(attr) {
|
||||
this.info.state = attr.state;
|
||||
this.info.type = attr.type;
|
||||
this.info.name = attr.name;
|
||||
this.info.ipMode = attr.ipMode;
|
||||
this.info.ips = [attr.ip];
|
||||
this.info.prefixLengths = [attr.prefixLength];
|
||||
this.info.gateways = [attr.gateway];
|
||||
this.info.dnses = attr.dnses;
|
||||
this.httpProxyHost = "";
|
||||
this.httpProxyPort = 0;
|
||||
}
|
||||
EthernetInterface.prototype = {
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsINetworkInterface]),
|
||||
|
||||
updateConfig: function(config) {
|
||||
debug("Interface " + this.info.name + " updateConfig " + JSON.stringify(config));
|
||||
this.info.state = (config.state != undefined) ?
|
||||
config.state : this.info.state;
|
||||
this.info.ips = (config.ip != undefined) ? [config.ip] : this.info.ips;
|
||||
this.info.prefixLengths = (config.prefixLength != undefined) ?
|
||||
[config.prefixLength] : this.info.prefixLengths;
|
||||
this.info.gateways = (config.gateway != undefined) ?
|
||||
[config.gateway] : this.info.gateways;
|
||||
this.info.dnses = (config.dnses != undefined) ? config.dnses : this.info.dnses;
|
||||
this.httpProxyHost = (config.httpProxyHost != undefined) ?
|
||||
config.httpProxyHost : this.httpProxyHost;
|
||||
this.httpProxyPort = (config.httpProxyPort != undefined) ?
|
||||
config.httpProxyPort : this.httpProxyPort;
|
||||
this.info.ipMode = (config.ipMode != undefined) ?
|
||||
config.ipMode : this.info.ipMode;
|
||||
},
|
||||
|
||||
info: {
|
||||
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("Add interface " + 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.nsINetworkInfo.NETWORK_STATE_DISABLED :
|
||||
Ci.nsINetworkInfo.NETWORK_STATE_ENABLED,
|
||||
name: ifname,
|
||||
type: Ci.nsINetworkInfo.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 + " succeeded with " +
|
||||
JSON.stringify(this.ethernetInterfaces[ifname]));
|
||||
|
||||
if (callback) {
|
||||
callback.notify(true, "ok");
|
||||
}
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
removeInterface: function(ifname, callback) {
|
||||
debug("Remove interface " + 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 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 + " succeeded.");
|
||||
|
||||
if (callback) {
|
||||
callback.notify(true, "ok");
|
||||
}
|
||||
}.bind(this)});
|
||||
},
|
||||
|
||||
updateInterfaceConfig: function(ifname, config, callback) {
|
||||
debug("Update interface config 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.info.ipMode;
|
||||
|
||||
// Update config.
|
||||
this.ethernetInterfaces[iface.info.name].updateConfig(config);
|
||||
|
||||
// Do not automatically re-connect if the interface is not in connected
|
||||
// state.
|
||||
if (iface.info.state != Ci.nsINetworkInfo.NETWORK_STATE_CONNECTED) {
|
||||
if (callback) {
|
||||
callback.notify(true, "ok");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
let newIpMode = this.ethernetInterfaces[iface.info.name].info.ipMode;
|
||||
|
||||
if (newIpMode == IP_MODE_STATIC) {
|
||||
this._setStaticIP(iface.info.name, callback);
|
||||
return;
|
||||
}
|
||||
if ((currentIpMode == IP_MODE_STATIC) && (newIpMode == IP_MODE_DHCP)) {
|
||||
gNetworkService.stopDhcp(iface.info.name, function(success) {
|
||||
if (success) {
|
||||
debug("DHCP for " + iface.info.name + " stopped.");
|
||||
}
|
||||
});
|
||||
|
||||
// Clear the current network settings before do dhcp request, otherwise
|
||||
// dhcp settings could fail.
|
||||
this.disconnect(iface.info.name, { notify: function(success, message) {
|
||||
if (!success) {
|
||||
if (callback) {
|
||||
callback.notify("Disconnect failed.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
this._runDhcp(iface.info.name, callback);
|
||||
}.bind(this) });
|
||||
return;
|
||||
}
|
||||
|
||||
if (callback) {
|
||||
callback.notify(true, "ok");
|
||||
}
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
enable: function(ifname, callback) {
|
||||
debug("Enable interface " + ifname);
|
||||
|
||||
this._ensureIfname(ifname, callback, function(iface) {
|
||||
// Interface can be only enabled in the state of disabled.
|
||||
if (iface.info.state != Ci.nsINetworkInfo.NETWORK_STATE_DISABLED) {
|
||||
if (callback) {
|
||||
callback.notify(true, "Interface already enabled.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
let ips = {};
|
||||
let prefixLengths = {};
|
||||
iface.info.getAddresses(ips, prefixLengths);
|
||||
let config = { ifname: iface.info.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.info.name].updateConfig({
|
||||
state: Ci.nsINetworkInfo.NETWORK_STATE_ENABLED
|
||||
});
|
||||
|
||||
debug("Enable interface " + iface.info.name + " succeeded.");
|
||||
|
||||
if (callback) {
|
||||
callback.notify(true, "ok");
|
||||
}
|
||||
}.bind(this));
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
disable: function(ifname, callback) {
|
||||
debug("Disable interface " + ifname);
|
||||
|
||||
this._ensureIfname(ifname, callback, function(iface) {
|
||||
if (iface.info.state == Ci.nsINetworkInfo.NETWORK_STATE_DISABLED) {
|
||||
if (callback) {
|
||||
callback.notify(true, "Interface already disabled.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (iface.info.state == Ci.nsINetworkInfo.NETWORK_STATE_CONNECTED) {
|
||||
gNetworkService.stopDhcp(iface.info.name, function(success) {
|
||||
if (success) {
|
||||
debug("DHCP for " + iface.info.name + " stopped.");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let ips = {};
|
||||
let prefixLengths = {};
|
||||
iface.info.getAddresses(ips, prefixLengths);
|
||||
let config = { ifname: iface.info.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.info.name].updateConfig({
|
||||
state: Ci.nsINetworkInfo.NETWORK_STATE_DISABLED
|
||||
});
|
||||
|
||||
debug("Disable interface " + iface.info.name + " succeeded.");
|
||||
|
||||
if (callback) {
|
||||
callback.notify(true, "ok");
|
||||
}
|
||||
}.bind(this));
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
connect: function(ifname, callback) {
|
||||
debug("Connect interface " + ifname);
|
||||
|
||||
this._ensureIfname(ifname, callback, function(iface) {
|
||||
// Interface can only be connected in the state of enabled or
|
||||
// disconnected.
|
||||
if (iface.info.state == Ci.nsINetworkInfo.NETWORK_STATE_DISABLED ||
|
||||
iface.info.state == Ci.nsINetworkInfo.NETWORK_STATE_CONNECTED) {
|
||||
if (callback) {
|
||||
callback.notify(true, "Interface " + ifname + " is not available or "
|
||||
+ " already connected.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (iface.info.ipMode == IP_MODE_DHCP) {
|
||||
this._runDhcp(iface.info.name, callback);
|
||||
return;
|
||||
}
|
||||
|
||||
if (iface.info.ipMode == IP_MODE_STATIC) {
|
||||
if (this._checkConfigNull(iface) && this.lastStaticConfig[iface.info.name]) {
|
||||
debug("Connect with lastStaticConfig " +
|
||||
JSON.stringify(this.lastStaticConfig[iface.info.name]));
|
||||
this.ethernetInterfaces[iface.info.name].updateConfig(
|
||||
this.lastStaticConfig[iface.info.name]);
|
||||
}
|
||||
this._setStaticIP(iface.info.name, callback);
|
||||
return;
|
||||
}
|
||||
|
||||
if (callback) {
|
||||
callback.notify(false, "IP mode is wrong or not set.");
|
||||
}
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
disconnect: function(ifname, callback) {
|
||||
debug("Disconnect interface " + ifname);
|
||||
|
||||
this._ensureIfname(ifname, callback, function(iface) {
|
||||
// Interface can be only disconnected in the state of connected.
|
||||
if (iface.info.state != Ci.nsINetworkInfo.NETWORK_STATE_CONNECTED) {
|
||||
if (callback) {
|
||||
callback.notify(true, "Interface is already disconnected");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
let config = { ifname: iface.info.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.info.name, function(success) {
|
||||
if (success) {
|
||||
debug("DHCP for " + iface.info.name + " stopped.");
|
||||
}
|
||||
});
|
||||
|
||||
this.ethernetInterfaces[iface.info.name].updateConfig({
|
||||
state: Ci.nsINetworkInfo.NETWORK_STATE_DISCONNECTED,
|
||||
ip: INTERFACE_IPADDR_NULL,
|
||||
prefixLength: INTERFACE_PREFIX_NULL,
|
||||
gateway: INTERFACE_GATEWAY_NULL
|
||||
});
|
||||
|
||||
gNetworkManager.updateNetworkInterface(this.ethernetInterfaces[ifname]);
|
||||
|
||||
debug("Disconnect interface " + iface.info.name + " succeeded.");
|
||||
|
||||
if (callback) {
|
||||
callback.notify(true, "ok");
|
||||
}
|
||||
}.bind(this));
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
_checkConfigNull: function(iface) {
|
||||
let ips = {};
|
||||
let prefixLengths = {};
|
||||
let gateways = iface.info.getGateways();
|
||||
iface.info.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]) {
|
||||
if (callback) {
|
||||
callback.notify(false, "Invalid interface.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
gNetworkService.dhcpRequest(ifname, function(success, result) {
|
||||
if (!success) {
|
||||
if (callback) {
|
||||
callback.notify(false, "DHCP failed.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
debug("DHCP succeeded 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.nsINetworkInfo.NETWORK_STATE_CONNECTED,
|
||||
ip: result.ipaddr_str,
|
||||
gateway: result.gateway_str,
|
||||
prefixLength: result.prefixLength,
|
||||
dnses: [result.dns1_str, result.dns2_str]
|
||||
});
|
||||
|
||||
gNetworkManager.updateNetworkInterface(this.ethernetInterfaces[ifname]);
|
||||
|
||||
debug("Connect interface " + ifname + " with DHCP succeeded.");
|
||||
|
||||
if (callback) {
|
||||
callback.notify(true, "ok");
|
||||
}
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
_setStaticIP: function(ifname, callback) {
|
||||
let iface = this.ethernetInterfaces[ifname];
|
||||
if (!iface) {
|
||||
if (callback) {
|
||||
callback.notify(false, "Invalid interface.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
let ips = {};
|
||||
let prefixLengths = {};
|
||||
iface.info.getAddresses(ips, prefixLengths);
|
||||
|
||||
let config = { ifname: iface.info.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;
|
||||
}
|
||||
|
||||
// Keep the lastest static network information.
|
||||
let ips = {};
|
||||
let prefixLengths = {};
|
||||
let gateways = iface.info.getGateways();
|
||||
iface.info.getAddresses(ips, prefixLengths);
|
||||
|
||||
this.lastStaticConfig[iface.info.name] = {
|
||||
ip: ips.value[0],
|
||||
prefixLength: prefixLengths.value[0],
|
||||
gateway: gateways[0]
|
||||
};
|
||||
|
||||
this.ethernetInterfaces[ifname].updateConfig({
|
||||
state: Ci.nsINetworkInfo.NETWORK_STATE_CONNECTED,
|
||||
});
|
||||
|
||||
gNetworkManager.updateNetworkInterface(this.ethernetInterfaces[ifname]);
|
||||
|
||||
debug("Connect interface " + ifname + " with static ip succeeded.");
|
||||
|
||||
if (callback) {
|
||||
callback.notify(true, "ok");
|
||||
}
|
||||
}.bind(this));
|
||||
},
|
||||
}
|
||||
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([EthernetManager]);
|
||||
2
dom/network/EthernetManager.manifest
Normal file
2
dom/network/EthernetManager.manifest
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
component {a96441dd-36b3-4f7f-963b-2c032e28a039} EthernetManager.js
|
||||
contract @mozilla.org/ethernetManager;1 {a96441dd-36b3-4f7f-963b-2c032e28a039}
|
||||
200
dom/network/NetUtils.cpp
Normal file
200
dom/network/NetUtils.cpp
Normal file
|
|
@ -0,0 +1,200 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* 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 "NetUtils.h"
|
||||
#include <dlfcn.h>
|
||||
#include <errno.h>
|
||||
#include "prinit.h"
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "nsDebug.h"
|
||||
#include "SystemProperty.h"
|
||||
|
||||
using mozilla::system::Property;
|
||||
|
||||
static void* sNetUtilsLib;
|
||||
static PRCallOnceType sInitNetUtilsLib;
|
||||
|
||||
static PRStatus
|
||||
InitNetUtilsLib()
|
||||
{
|
||||
sNetUtilsLib = dlopen("/system/lib/libnetutils.so", RTLD_LAZY);
|
||||
// We might fail to open the hardware lib. That's OK.
|
||||
return PR_SUCCESS;
|
||||
}
|
||||
|
||||
static void*
|
||||
GetNetUtilsLibHandle()
|
||||
{
|
||||
PR_CallOnce(&sInitNetUtilsLib, InitNetUtilsLib);
|
||||
return sNetUtilsLib;
|
||||
}
|
||||
|
||||
// static
|
||||
void*
|
||||
NetUtils::GetSharedLibrary()
|
||||
{
|
||||
void* netLib = GetNetUtilsLibHandle();
|
||||
if (!netLib) {
|
||||
NS_WARNING("No /system/lib/libnetutils.so");
|
||||
}
|
||||
return netLib;
|
||||
}
|
||||
|
||||
// static
|
||||
int32_t
|
||||
NetUtils::SdkVersion()
|
||||
{
|
||||
char propVersion[Property::VALUE_MAX_LENGTH];
|
||||
Property::Get("ro.build.version.sdk", propVersion, "0");
|
||||
int32_t version = strtol(propVersion, nullptr, 10);
|
||||
return version;
|
||||
}
|
||||
|
||||
DEFINE_DLFUNC(ifc_enable, int32_t, const char*)
|
||||
DEFINE_DLFUNC(ifc_disable, int32_t, const char*)
|
||||
DEFINE_DLFUNC(ifc_configure, int32_t, const char*, in_addr_t, uint32_t,
|
||||
in_addr_t, in_addr_t, in_addr_t)
|
||||
DEFINE_DLFUNC(ifc_reset_connections, int32_t, const char*, const int32_t)
|
||||
DEFINE_DLFUNC(ifc_set_default_route, int32_t, const char*, in_addr_t)
|
||||
DEFINE_DLFUNC(ifc_add_route, int32_t, const char*, const char*, uint32_t, const char*)
|
||||
DEFINE_DLFUNC(ifc_remove_route, int32_t, const char*, const char*, uint32_t, const char*)
|
||||
DEFINE_DLFUNC(ifc_remove_host_routes, int32_t, const char*)
|
||||
DEFINE_DLFUNC(ifc_remove_default_route, int32_t, const char*)
|
||||
DEFINE_DLFUNC(dhcp_stop, int32_t, const char*)
|
||||
|
||||
NetUtils::NetUtils()
|
||||
{
|
||||
}
|
||||
|
||||
int32_t NetUtils::do_ifc_enable(const char *ifname)
|
||||
{
|
||||
USE_DLFUNC(ifc_enable)
|
||||
return ifc_enable(ifname);
|
||||
}
|
||||
|
||||
int32_t NetUtils::do_ifc_disable(const char *ifname)
|
||||
{
|
||||
USE_DLFUNC(ifc_disable)
|
||||
return ifc_disable(ifname);
|
||||
}
|
||||
|
||||
int32_t NetUtils::do_ifc_configure(const char *ifname,
|
||||
in_addr_t address,
|
||||
uint32_t prefixLength,
|
||||
in_addr_t gateway,
|
||||
in_addr_t dns1,
|
||||
in_addr_t dns2)
|
||||
{
|
||||
USE_DLFUNC(ifc_configure)
|
||||
int32_t ret = ifc_configure(ifname, address, prefixLength, gateway, dns1, dns2);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int32_t NetUtils::do_ifc_reset_connections(const char *ifname,
|
||||
const int32_t resetMask)
|
||||
{
|
||||
USE_DLFUNC(ifc_reset_connections)
|
||||
return ifc_reset_connections(ifname, resetMask);
|
||||
}
|
||||
|
||||
int32_t NetUtils::do_ifc_set_default_route(const char *ifname,
|
||||
in_addr_t gateway)
|
||||
{
|
||||
USE_DLFUNC(ifc_set_default_route)
|
||||
return ifc_set_default_route(ifname, gateway);
|
||||
}
|
||||
|
||||
int32_t NetUtils::do_ifc_add_route(const char *ifname,
|
||||
const char *dst,
|
||||
uint32_t prefixLength,
|
||||
const char *gateway)
|
||||
{
|
||||
USE_DLFUNC(ifc_add_route)
|
||||
return ifc_add_route(ifname, dst, prefixLength, gateway);
|
||||
}
|
||||
|
||||
int32_t NetUtils::do_ifc_remove_route(const char *ifname,
|
||||
const char *dst,
|
||||
uint32_t prefixLength,
|
||||
const char *gateway)
|
||||
{
|
||||
USE_DLFUNC(ifc_remove_route)
|
||||
return ifc_remove_route(ifname, dst, prefixLength, gateway);
|
||||
}
|
||||
|
||||
int32_t NetUtils::do_ifc_remove_host_routes(const char *ifname)
|
||||
{
|
||||
USE_DLFUNC(ifc_remove_host_routes)
|
||||
return ifc_remove_host_routes(ifname);
|
||||
}
|
||||
|
||||
int32_t NetUtils::do_ifc_remove_default_route(const char *ifname)
|
||||
{
|
||||
USE_DLFUNC(ifc_remove_default_route)
|
||||
return ifc_remove_default_route(ifname);
|
||||
}
|
||||
|
||||
int32_t NetUtils::do_dhcp_stop(const char *ifname)
|
||||
{
|
||||
USE_DLFUNC(dhcp_stop)
|
||||
return dhcp_stop(ifname);
|
||||
}
|
||||
|
||||
int32_t NetUtils::do_dhcp_do_request(const char *ifname,
|
||||
char *ipaddr,
|
||||
char *gateway,
|
||||
uint32_t *prefixLength,
|
||||
char *dns1,
|
||||
char *dns2,
|
||||
char *server,
|
||||
uint32_t *lease,
|
||||
char* vendorinfo)
|
||||
{
|
||||
int32_t ret = -1;
|
||||
uint32_t sdkVersion = SdkVersion();
|
||||
|
||||
if (sdkVersion == 15) {
|
||||
// ICS
|
||||
// http://androidxref.com/4.0.4/xref/system/core/libnetutils/dhcp_utils.c#149
|
||||
DEFINE_DLFUNC(dhcp_do_request, int32_t, const char*, char*, char*, uint32_t*, char*, char*, char*, uint32_t*)
|
||||
USE_DLFUNC(dhcp_do_request)
|
||||
vendorinfo[0] = '\0';
|
||||
|
||||
ret = dhcp_do_request(ifname, ipaddr, gateway, prefixLength, dns1, dns2,
|
||||
server, lease);
|
||||
} else if (sdkVersion == 16 || sdkVersion == 17) {
|
||||
// JB 4.1 and 4.2
|
||||
// http://androidxref.com/4.1.2/xref/system/core/libnetutils/dhcp_utils.c#175
|
||||
// http://androidxref.com/4.2.2_r1/xref/system/core/include/netutils/dhcp.h#26
|
||||
DEFINE_DLFUNC(dhcp_do_request, int32_t, const char*, char*, char*, uint32_t*, char*, char*, char*, uint32_t*, char*)
|
||||
USE_DLFUNC(dhcp_do_request)
|
||||
ret = dhcp_do_request(ifname, ipaddr, gateway, prefixLength, dns1, dns2,
|
||||
server, lease, vendorinfo);
|
||||
} else if (sdkVersion == 18) {
|
||||
// JB 4.3
|
||||
// http://androidxref.com/4.3_r2.1/xref/system/core/libnetutils/dhcp_utils.c#181
|
||||
DEFINE_DLFUNC(dhcp_do_request, int32_t, const char*, char*, char*, uint32_t*, char**, char*, uint32_t*, char*, char*)
|
||||
USE_DLFUNC(dhcp_do_request)
|
||||
char *dns[3] = {dns1, dns2, nullptr};
|
||||
char domains[Property::VALUE_MAX_LENGTH];
|
||||
ret = dhcp_do_request(ifname, ipaddr, gateway, prefixLength, dns,
|
||||
server, lease, vendorinfo, domains);
|
||||
} else if (sdkVersion >= 19) {
|
||||
// KitKat 4.4.X
|
||||
// http://androidxref.com/4.4_r1/xref/system/core/libnetutils/dhcp_utils.c#18
|
||||
// Lollipop 5.0
|
||||
//http://androidxref.com/5.0.0_r2/xref/system/core/libnetutils/dhcp_utils.c#186
|
||||
DEFINE_DLFUNC(dhcp_do_request, int32_t, const char*, char*, char*, uint32_t*, char**, char*, uint32_t*, char*, char*, char*)
|
||||
USE_DLFUNC(dhcp_do_request)
|
||||
char *dns[3] = {dns1, dns2, nullptr};
|
||||
char domains[Property::VALUE_MAX_LENGTH];
|
||||
char mtu[Property::VALUE_MAX_LENGTH];
|
||||
ret = dhcp_do_request(ifname, ipaddr, gateway, prefixLength, dns, server, lease, vendorinfo, domains, mtu);
|
||||
} else {
|
||||
NS_WARNING("Unable to perform do_dhcp_request: unsupported sdk version!");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
75
dom/network/NetUtils.h
Normal file
75
dom/network/NetUtils.h
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* 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/. */
|
||||
|
||||
/**
|
||||
* Abstraction on top of the network support from libnetutils that we
|
||||
* use to set up network connections.
|
||||
*/
|
||||
|
||||
#ifndef NetUtils_h
|
||||
#define NetUtils_h
|
||||
|
||||
#include "arpa/inet.h"
|
||||
|
||||
// Copied from ifc.h
|
||||
#define RESET_IPV4_ADDRESSES 0x01
|
||||
#define RESET_IPV6_ADDRESSES 0x02
|
||||
#define RESET_ALL_ADDRESSES (RESET_IPV4_ADDRESSES | RESET_IPV6_ADDRESSES)
|
||||
|
||||
// Implements netutils functions. No need for an abstract class here since we
|
||||
// only have a one sdk specific method (dhcp_do_request)
|
||||
class NetUtils
|
||||
{
|
||||
public:
|
||||
static void* GetSharedLibrary();
|
||||
|
||||
NetUtils();
|
||||
|
||||
int32_t do_ifc_enable(const char *ifname);
|
||||
int32_t do_ifc_disable(const char *ifname);
|
||||
int32_t do_ifc_configure(const char *ifname,
|
||||
in_addr_t address,
|
||||
uint32_t prefixLength,
|
||||
in_addr_t gateway,
|
||||
in_addr_t dns1,
|
||||
in_addr_t dns2);
|
||||
int32_t do_ifc_reset_connections(const char *ifname, const int32_t resetMask);
|
||||
int32_t do_ifc_set_default_route(const char *ifname, in_addr_t gateway);
|
||||
int32_t do_ifc_add_route(const char *ifname,
|
||||
const char *dst,
|
||||
uint32_t prefixLength,
|
||||
const char *gateway);
|
||||
int32_t do_ifc_remove_route(const char *ifname,
|
||||
const char *dst,
|
||||
uint32_t prefixLength,
|
||||
const char *gateway);
|
||||
int32_t do_ifc_remove_host_routes(const char *ifname);
|
||||
int32_t do_ifc_remove_default_route(const char *ifname);
|
||||
int32_t do_dhcp_stop(const char *ifname);
|
||||
int32_t do_dhcp_do_request(const char *ifname,
|
||||
char *ipaddr,
|
||||
char *gateway,
|
||||
uint32_t *prefixLength,
|
||||
char *dns1,
|
||||
char *dns2,
|
||||
char *server,
|
||||
uint32_t *lease,
|
||||
char* vendorinfo);
|
||||
|
||||
static int32_t SdkVersion();
|
||||
};
|
||||
|
||||
// Defines a function type with the right arguments and return type.
|
||||
#define DEFINE_DLFUNC(name, ret, args...) typedef ret (*FUNC##name)(args);
|
||||
|
||||
// Set up a dlsymed function ready to use.
|
||||
#define USE_DLFUNC(name) \
|
||||
FUNC##name name = (FUNC##name) dlsym(GetSharedLibrary(), #name); \
|
||||
if (!name) { \
|
||||
MOZ_CRASH("Symbol not found in shared library : " #name); \
|
||||
}
|
||||
|
||||
#endif // NetUtils_h
|
||||
1285
dom/network/NetworkStatsDB.jsm
Normal file
1285
dom/network/NetworkStatsDB.jsm
Normal file
File diff suppressed because it is too large
Load diff
388
dom/network/NetworkStatsManager.js
Normal file
388
dom/network/NetworkStatsManager.js
Normal file
|
|
@ -0,0 +1,388 @@
|
|||
/* 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) { dump("-*- NetworkStatsManager: " + s + "\n"); }
|
||||
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/DOMRequestHelper.jsm");
|
||||
|
||||
// Ensure NetworkStatsService and NetworkStatsDB are loaded in the parent process
|
||||
// to receive messages from the child processes.
|
||||
var appInfo = Cc["@mozilla.org/xre/app-info;1"];
|
||||
var isParentProcess = !appInfo || appInfo.getService(Ci.nsIXULRuntime)
|
||||
.processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
|
||||
if (isParentProcess) {
|
||||
Cu.import("resource://gre/modules/NetworkStatsService.jsm");
|
||||
}
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
|
||||
"@mozilla.org/childprocessmessagemanager;1",
|
||||
"nsISyncMessageSender");
|
||||
|
||||
// NetworkStatsData
|
||||
const nsIClassInfo = Ci.nsIClassInfo;
|
||||
const NETWORKSTATSDATA_CID = Components.ID("{3b16fe17-5583-483a-b486-b64a3243221c}");
|
||||
|
||||
function NetworkStatsData(aWindow, aData) {
|
||||
this.rxBytes = aData.rxBytes;
|
||||
this.txBytes = aData.txBytes;
|
||||
this.date = new aWindow.Date(aData.date.getTime());
|
||||
}
|
||||
|
||||
NetworkStatsData.prototype = {
|
||||
classID : NETWORKSTATSDATA_CID,
|
||||
|
||||
QueryInterface : XPCOMUtils.generateQI([])
|
||||
};
|
||||
|
||||
// NetworkStatsInterface
|
||||
const NETWORKSTATSINTERFACE_CONTRACTID = "@mozilla.org/networkstatsinterface;1";
|
||||
const NETWORKSTATSINTERFACE_CID = Components.ID("{f540615b-d803-43ff-8200-2a9d145a5645}");
|
||||
|
||||
function NetworkStatsInterface() {
|
||||
if (DEBUG) {
|
||||
debug("NetworkStatsInterface Constructor");
|
||||
}
|
||||
}
|
||||
|
||||
NetworkStatsInterface.prototype = {
|
||||
__init: function(aNetwork) {
|
||||
this.type = aNetwork.type;
|
||||
this.id = aNetwork.id;
|
||||
},
|
||||
|
||||
classID : NETWORKSTATSINTERFACE_CID,
|
||||
|
||||
contractID: NETWORKSTATSINTERFACE_CONTRACTID,
|
||||
QueryInterface : XPCOMUtils.generateQI([])
|
||||
}
|
||||
|
||||
// NetworkStats
|
||||
const NETWORKSTATS_CID = Components.ID("{28904f59-8497-4ac0-904f-2af14b7fd3de}");
|
||||
|
||||
function NetworkStats(aWindow, aStats) {
|
||||
if (DEBUG) {
|
||||
debug("NetworkStats Constructor");
|
||||
}
|
||||
this.appManifestURL = aStats.appManifestURL || null;
|
||||
this.browsingTrafficOnly = aStats.browsingTrafficOnly || false;
|
||||
this.serviceType = aStats.serviceType || null;
|
||||
this.network = new aWindow.MozNetworkStatsInterface(aStats.network);
|
||||
this.start = aStats.start ? new aWindow.Date(aStats.start.getTime()) : null;
|
||||
this.end = aStats.end ? new aWindow.Date(aStats.end.getTime()) : null;
|
||||
|
||||
let samples = this.data = new aWindow.Array();
|
||||
for (let i = 0; i < aStats.data.length; i++) {
|
||||
samples.push(aWindow.MozNetworkStatsData._create(
|
||||
aWindow, new NetworkStatsData(aWindow, aStats.data[i])));
|
||||
}
|
||||
}
|
||||
|
||||
NetworkStats.prototype = {
|
||||
classID : NETWORKSTATS_CID,
|
||||
|
||||
QueryInterface : XPCOMUtils.generateQI()
|
||||
}
|
||||
|
||||
// NetworkStatsAlarm
|
||||
const NETWORKSTATSALARM_CID = Components.ID("{a93ea13e-409c-4189-9b1e-95fff220be55}");
|
||||
|
||||
function NetworkStatsAlarm(aWindow, aAlarm) {
|
||||
this.alarmId = aAlarm.id;
|
||||
this.network = new aWindow.MozNetworkStatsInterface(aAlarm.network);
|
||||
this.threshold = aAlarm.threshold;
|
||||
this.data = aAlarm.data;
|
||||
}
|
||||
|
||||
NetworkStatsAlarm.prototype = {
|
||||
classID : NETWORKSTATSALARM_CID,
|
||||
|
||||
QueryInterface : XPCOMUtils.generateQI([])
|
||||
};
|
||||
|
||||
// NetworkStatsManager
|
||||
|
||||
const NETWORKSTATSMANAGER_CONTRACTID = "@mozilla.org/networkStatsManager;1";
|
||||
const NETWORKSTATSMANAGER_CID = Components.ID("{ceb874cd-cc1a-4e65-b404-cc2d3e42425f}");
|
||||
|
||||
function NetworkStatsManager() {
|
||||
if (DEBUG) {
|
||||
debug("Constructor");
|
||||
}
|
||||
}
|
||||
|
||||
NetworkStatsManager.prototype = {
|
||||
__proto__: DOMRequestIpcHelper.prototype,
|
||||
|
||||
getSamples: function getSamples(aNetwork, aStart, aEnd, aOptions) {
|
||||
if (aStart > aEnd) {
|
||||
throw Components.results.NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
// appManifestURL is used to query network statistics by app;
|
||||
// serviceType is used to query network statistics by system service.
|
||||
// It is illegal to specify both of them at the same time.
|
||||
if (aOptions.appManifestURL && aOptions.serviceType) {
|
||||
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
// browsingTrafficOnly is meaningful only when querying by app.
|
||||
if (!aOptions.appManifestURL && aOptions.browsingTrafficOnly) {
|
||||
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
let appManifestURL = aOptions.appManifestURL;
|
||||
let serviceType = aOptions.serviceType;
|
||||
let browsingTrafficOnly = aOptions.browsingTrafficOnly;
|
||||
|
||||
// TODO Bug 929410 Date object cannot correctly pass through cpmm/ppmm IPC
|
||||
// This is just a work-around by passing timestamp numbers.
|
||||
aStart = aStart.getTime();
|
||||
aEnd = aEnd.getTime();
|
||||
|
||||
let request = this.createRequest();
|
||||
cpmm.sendAsyncMessage("NetworkStats:Get",
|
||||
{ network: aNetwork.toJSON(),
|
||||
start: aStart,
|
||||
end: aEnd,
|
||||
appManifestURL: appManifestURL,
|
||||
browsingTrafficOnly: browsingTrafficOnly,
|
||||
serviceType: serviceType,
|
||||
id: this.getRequestId(request) });
|
||||
return request;
|
||||
},
|
||||
|
||||
clearStats: function clearStats(aNetwork) {
|
||||
let request = this.createRequest();
|
||||
cpmm.sendAsyncMessage("NetworkStats:Clear",
|
||||
{ network: aNetwork.toJSON(),
|
||||
id: this.getRequestId(request) });
|
||||
return request;
|
||||
},
|
||||
|
||||
clearAllStats: function clearAllStats() {
|
||||
let request = this.createRequest();
|
||||
cpmm.sendAsyncMessage("NetworkStats:ClearAll",
|
||||
{id: this.getRequestId(request)});
|
||||
return request;
|
||||
},
|
||||
|
||||
addAlarm: function addAlarm(aNetwork, aThreshold, aOptions) {
|
||||
let request = this.createRequest();
|
||||
cpmm.sendAsyncMessage("NetworkStats:SetAlarm",
|
||||
{id: this.getRequestId(request),
|
||||
data: {network: aNetwork.toJSON(),
|
||||
threshold: aThreshold,
|
||||
startTime: aOptions.startTime,
|
||||
data: aOptions.data,
|
||||
manifestURL: this.manifestURL,
|
||||
pageURL: this.pageURL}});
|
||||
return request;
|
||||
},
|
||||
|
||||
getAllAlarms: function getAllAlarms(aNetwork) {
|
||||
let network = null;
|
||||
if (aNetwork) {
|
||||
network = aNetwork.toJSON();
|
||||
}
|
||||
|
||||
let request = this.createRequest();
|
||||
cpmm.sendAsyncMessage("NetworkStats:GetAlarms",
|
||||
{id: this.getRequestId(request),
|
||||
data: {network: network,
|
||||
manifestURL: this.manifestURL}});
|
||||
return request;
|
||||
},
|
||||
|
||||
removeAlarms: function removeAlarms(aAlarmId) {
|
||||
if (aAlarmId == 0) {
|
||||
aAlarmId = -1;
|
||||
}
|
||||
|
||||
let request = this.createRequest();
|
||||
cpmm.sendAsyncMessage("NetworkStats:RemoveAlarms",
|
||||
{id: this.getRequestId(request),
|
||||
data: {alarmId: aAlarmId,
|
||||
manifestURL: this.manifestURL}});
|
||||
|
||||
return request;
|
||||
},
|
||||
|
||||
getAvailableNetworks: function getAvailableNetworks() {
|
||||
let request = this.createRequest();
|
||||
cpmm.sendAsyncMessage("NetworkStats:GetAvailableNetworks",
|
||||
{ id: this.getRequestId(request) });
|
||||
return request;
|
||||
},
|
||||
|
||||
getAvailableServiceTypes: function getAvailableServiceTypes() {
|
||||
let request = this.createRequest();
|
||||
cpmm.sendAsyncMessage("NetworkStats:GetAvailableServiceTypes",
|
||||
{ id: this.getRequestId(request) });
|
||||
return request;
|
||||
},
|
||||
|
||||
get sampleRate() {
|
||||
return cpmm.sendSyncMessage("NetworkStats:SampleRate")[0];
|
||||
},
|
||||
|
||||
get maxStorageAge() {
|
||||
return cpmm.sendSyncMessage("NetworkStats:MaxStorageAge")[0];
|
||||
},
|
||||
|
||||
receiveMessage: function(aMessage) {
|
||||
if (DEBUG) {
|
||||
debug("NetworkStatsmanager::receiveMessage: " + aMessage.name);
|
||||
}
|
||||
|
||||
let msg = aMessage.json;
|
||||
let req = this.takeRequest(msg.id);
|
||||
if (!req) {
|
||||
if (DEBUG) {
|
||||
debug("No request stored with id " + msg.id);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
switch (aMessage.name) {
|
||||
case "NetworkStats:Get:Return":
|
||||
if (msg.error) {
|
||||
Services.DOMRequest.fireError(req, msg.error);
|
||||
return;
|
||||
}
|
||||
|
||||
let result = this._window.MozNetworkStats._create(
|
||||
this._window, new NetworkStats(this._window, msg.result));
|
||||
if (DEBUG) {
|
||||
debug("result: " + JSON.stringify(result));
|
||||
}
|
||||
Services.DOMRequest.fireSuccess(req, result);
|
||||
break;
|
||||
|
||||
case "NetworkStats:GetAvailableNetworks:Return":
|
||||
if (msg.error) {
|
||||
Services.DOMRequest.fireError(req, msg.error);
|
||||
return;
|
||||
}
|
||||
|
||||
let networks = new this._window.Array();
|
||||
for (let i = 0; i < msg.result.length; i++) {
|
||||
let network = new this._window.MozNetworkStatsInterface(msg.result[i]);
|
||||
networks.push(network);
|
||||
}
|
||||
|
||||
Services.DOMRequest.fireSuccess(req, networks);
|
||||
break;
|
||||
|
||||
case "NetworkStats:GetAvailableServiceTypes:Return":
|
||||
if (msg.error) {
|
||||
Services.DOMRequest.fireError(req, msg.error);
|
||||
return;
|
||||
}
|
||||
|
||||
let serviceTypes = new this._window.Array();
|
||||
for (let i = 0; i < msg.result.length; i++) {
|
||||
serviceTypes.push(msg.result[i]);
|
||||
}
|
||||
|
||||
Services.DOMRequest.fireSuccess(req, serviceTypes);
|
||||
break;
|
||||
|
||||
case "NetworkStats:Clear:Return":
|
||||
case "NetworkStats:ClearAll:Return":
|
||||
if (msg.error) {
|
||||
Services.DOMRequest.fireError(req, msg.error);
|
||||
return;
|
||||
}
|
||||
|
||||
Services.DOMRequest.fireSuccess(req, true);
|
||||
break;
|
||||
|
||||
case "NetworkStats:SetAlarm:Return":
|
||||
case "NetworkStats:RemoveAlarms:Return":
|
||||
if (msg.error) {
|
||||
Services.DOMRequest.fireError(req, msg.error);
|
||||
return;
|
||||
}
|
||||
|
||||
Services.DOMRequest.fireSuccess(req, msg.result);
|
||||
break;
|
||||
|
||||
case "NetworkStats:GetAlarms:Return":
|
||||
if (msg.error) {
|
||||
Services.DOMRequest.fireError(req, msg.error);
|
||||
return;
|
||||
}
|
||||
|
||||
let alarms = new this._window.Array();
|
||||
for (let i = 0; i < msg.result.length; i++) {
|
||||
// The WebIDL type of data is any, so we should manually clone it
|
||||
// into the content window.
|
||||
if ("data" in msg.result[i]) {
|
||||
msg.result[i].data = Cu.cloneInto(msg.result[i].data, this._window);
|
||||
}
|
||||
let alarm = new NetworkStatsAlarm(this._window, msg.result[i]);
|
||||
alarms.push(this._window.MozNetworkStatsAlarm._create(this._window, alarm));
|
||||
}
|
||||
|
||||
Services.DOMRequest.fireSuccess(req, alarms);
|
||||
break;
|
||||
|
||||
default:
|
||||
if (DEBUG) {
|
||||
debug("Wrong message: " + aMessage.name);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
init: function(aWindow) {
|
||||
let principal = aWindow.document.nodePrincipal;
|
||||
|
||||
this.initDOMRequestHelper(aWindow, ["NetworkStats:Get:Return",
|
||||
"NetworkStats:GetAvailableNetworks:Return",
|
||||
"NetworkStats:GetAvailableServiceTypes:Return",
|
||||
"NetworkStats:Clear:Return",
|
||||
"NetworkStats:ClearAll:Return",
|
||||
"NetworkStats:SetAlarm:Return",
|
||||
"NetworkStats:GetAlarms:Return",
|
||||
"NetworkStats:RemoveAlarms:Return"]);
|
||||
|
||||
// Init app properties.
|
||||
let appsService = Cc["@mozilla.org/AppsService;1"]
|
||||
.getService(Ci.nsIAppsService);
|
||||
|
||||
this.manifestURL = appsService.getManifestURLByLocalId(principal.appId);
|
||||
|
||||
let isApp = !!this.manifestURL.length;
|
||||
if (isApp) {
|
||||
this.pageURL = principal.URI.spec;
|
||||
}
|
||||
|
||||
this.window = aWindow;
|
||||
},
|
||||
|
||||
// Called from DOMRequestIpcHelper
|
||||
uninit: function uninit() {
|
||||
if (DEBUG) {
|
||||
debug("uninit call");
|
||||
}
|
||||
},
|
||||
|
||||
classID : NETWORKSTATSMANAGER_CID,
|
||||
contractID : NETWORKSTATSMANAGER_CONTRACTID,
|
||||
QueryInterface : XPCOMUtils.generateQI([Ci.nsIDOMGlobalPropertyInitializer,
|
||||
Ci.nsISupportsWeakReference,
|
||||
Ci.nsIObserver]),
|
||||
}
|
||||
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([NetworkStatsAlarm,
|
||||
NetworkStatsData,
|
||||
NetworkStatsInterface,
|
||||
NetworkStats,
|
||||
NetworkStatsManager]);
|
||||
14
dom/network/NetworkStatsManager.manifest
Normal file
14
dom/network/NetworkStatsManager.manifest
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
component {3b16fe17-5583-483a-b486-b64a3243221c} NetworkStatsManager.js
|
||||
contract @mozilla.org/networkStatsdata;1 {3b16fe17-5583-483a-b486-b64a3243221c}
|
||||
|
||||
component {28904f59-8497-4ac0-904f-2af14b7fd3de} NetworkStatsManager.js
|
||||
contract @mozilla.org/networkStats;1 {28904f59-8497-4ac0-904f-2af14b7fd3de}
|
||||
|
||||
component {f540615b-d803-43ff-8200-2a9d145a5645} NetworkStatsManager.js
|
||||
contract @mozilla.org/networkstatsinterface;1 {f540615b-d803-43ff-8200-2a9d145a5645}
|
||||
|
||||
component {a93ea13e-409c-4189-9b1e-95fff220be55} NetworkStatsManager.js
|
||||
contract @mozilla.org/networkstatsalarm;1 {a93ea13e-409c-4189-9b1e-95fff220be55}
|
||||
|
||||
component {ceb874cd-cc1a-4e65-b404-cc2d3e42425f} NetworkStatsManager.js
|
||||
contract @mozilla.org/networkStatsManager;1 {ceb874cd-cc1a-4e65-b404-cc2d3e42425f}
|
||||
1171
dom/network/NetworkStatsService.jsm
Normal file
1171
dom/network/NetworkStatsService.jsm
Normal file
File diff suppressed because it is too large
Load diff
90
dom/network/NetworkStatsServiceProxy.js
Normal file
90
dom/network/NetworkStatsServiceProxy.js
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
/* 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) { dump("-*- NetworkStatsServiceProxy: " + s + "\n"); }
|
||||
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["NetworkStatsServiceProxy"];
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/NetworkStatsService.jsm");
|
||||
|
||||
const NETWORKSTATSSERVICEPROXY_CONTRACTID = "@mozilla.org/networkstatsServiceProxy;1";
|
||||
const NETWORKSTATSSERVICEPROXY_CID = Components.ID("98fd8f69-784e-4626-aa59-56d6436a3c24");
|
||||
const nsINetworkStatsServiceProxy = Ci.nsINetworkStatsServiceProxy;
|
||||
|
||||
function NetworkStatsServiceProxy() {
|
||||
if (DEBUG) {
|
||||
debug("Proxy started");
|
||||
}
|
||||
}
|
||||
|
||||
NetworkStatsServiceProxy.prototype = {
|
||||
/*
|
||||
* Function called in the protocol layer (HTTP, FTP, WebSocket ...etc)
|
||||
* to pass the per-app stats to NetworkStatsService.
|
||||
*/
|
||||
saveAppStats: function saveAppStats(aAppId, aIsInIsolatedMozBrowser, aNetworkInfo, aTimeStamp,
|
||||
aRxBytes, aTxBytes, aIsAccumulative,
|
||||
aCallback) {
|
||||
if (!aNetworkInfo) {
|
||||
if (DEBUG) {
|
||||
debug("|aNetworkInfo| is not specified. Failed to save stats. Returning.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (DEBUG) {
|
||||
debug("saveAppStats: " + aAppId + " " + aIsInIsolatedMozBrowser + " " +
|
||||
aNetworkInfo.type + " " + aTimeStamp + " " +
|
||||
aRxBytes + " " + aTxBytes + " " + aIsAccumulative);
|
||||
}
|
||||
|
||||
if (aCallback) {
|
||||
aCallback = aCallback.notify;
|
||||
}
|
||||
|
||||
NetworkStatsService.saveStats(aAppId, aIsInIsolatedMozBrowser, "", aNetworkInfo,
|
||||
aTimeStamp, aRxBytes, aTxBytes,
|
||||
aIsAccumulative, aCallback);
|
||||
},
|
||||
|
||||
/*
|
||||
* Function called in the points of different system services
|
||||
* to pass the per-service stats to NetworkStatsService.
|
||||
*/
|
||||
saveServiceStats: function saveServiceStats(aServiceType, aNetworkInfo,
|
||||
aTimeStamp, aRxBytes, aTxBytes,
|
||||
aIsAccumulative, aCallback) {
|
||||
if (!aNetworkInfo) {
|
||||
if (DEBUG) {
|
||||
debug("|aNetworkInfo| is not specified. Failed to save stats. Returning.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (DEBUG) {
|
||||
debug("saveServiceStats: " + aServiceType + " " + aNetworkInfo.type + " " +
|
||||
aTimeStamp + " " + aRxBytes + " " + aTxBytes + " " +
|
||||
aIsAccumulative);
|
||||
}
|
||||
|
||||
if (aCallback) {
|
||||
aCallback = aCallback.notify;
|
||||
}
|
||||
|
||||
NetworkStatsService.saveStats(0, false, aServiceType , aNetworkInfo, aTimeStamp,
|
||||
aRxBytes, aTxBytes, aIsAccumulative,
|
||||
aCallback);
|
||||
},
|
||||
|
||||
classID : NETWORKSTATSSERVICEPROXY_CID,
|
||||
QueryInterface : XPCOMUtils.generateQI([nsINetworkStatsServiceProxy]),
|
||||
}
|
||||
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([NetworkStatsServiceProxy]);
|
||||
2
dom/network/NetworkStatsServiceProxy.manifest
Normal file
2
dom/network/NetworkStatsServiceProxy.manifest
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
component {98fd8f69-784e-4626-aa59-56d6436a3c24} NetworkStatsServiceProxy.js
|
||||
contract @mozilla.org/networkstatsServiceProxy;1 {98fd8f69-784e-4626-aa59-56d6436a3c24}
|
||||
32
dom/network/PTCPServerSocket.ipdl
Normal file
32
dom/network/PTCPServerSocket.ipdl
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */
|
||||
|
||||
/* 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 protocol PNecko;
|
||||
include protocol PTCPSocket;
|
||||
|
||||
include "mozilla/net/NeckoMessageUtils.h";
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
protocol PTCPServerSocket
|
||||
{
|
||||
manager PNecko;
|
||||
|
||||
parent:
|
||||
async Close();
|
||||
async RequestDelete();
|
||||
|
||||
child:
|
||||
async CallbackAccept(PTCPSocket socket);
|
||||
async __delete__();
|
||||
};
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
|
||||
83
dom/network/PTCPSocket.ipdl
Normal file
83
dom/network/PTCPSocket.ipdl
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */
|
||||
|
||||
/* 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 protocol PNecko;
|
||||
|
||||
include "mozilla/net/NeckoMessageUtils.h";
|
||||
|
||||
using struct mozilla::void_t from "ipc/IPCMessageUtils.h";
|
||||
|
||||
struct TCPError {
|
||||
nsString name;
|
||||
nsString message;
|
||||
};
|
||||
|
||||
union SendableData {
|
||||
uint8_t[];
|
||||
nsCString;
|
||||
};
|
||||
|
||||
union CallbackData {
|
||||
void_t;
|
||||
SendableData;
|
||||
TCPError;
|
||||
};
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
protocol PTCPSocket
|
||||
{
|
||||
manager PNecko;
|
||||
|
||||
parent:
|
||||
// Forward calling to child's open() method to parent, expect TCPOptions
|
||||
// is expanded to |useSSL| (from TCPOptions.useSecureTransport) and
|
||||
// |binaryType| (from TCPOption.binaryType).
|
||||
async Open(nsString host, uint16_t port, bool useSSL, bool useArrayBuffers);
|
||||
|
||||
// Ask parent to open a socket and bind the newly-opened socket to a local
|
||||
// address specified in |localAddr| and |localPort|.
|
||||
async OpenBind(nsCString host, uint16_t port,
|
||||
nsCString localAddr, uint16_t localPort,
|
||||
bool useSSL, bool aUseArrayBuffers, nsCString aFilter);
|
||||
|
||||
// When child's send() is called, this message requrests parent to send
|
||||
// data and update it's trackingNumber.
|
||||
async Data(SendableData data, uint32_t trackingNumber);
|
||||
|
||||
// Forward calling to child's upgradeToSecure() method to parent.
|
||||
async StartTLS();
|
||||
|
||||
// Forward calling to child's send() method to parent.
|
||||
async Suspend();
|
||||
|
||||
// Forward calling to child's resume() method to parent.
|
||||
async Resume();
|
||||
|
||||
// Forward calling to child's close() method to parent.
|
||||
async Close();
|
||||
|
||||
child:
|
||||
// Forward events that are dispatched by parent.
|
||||
async Callback(nsString type, CallbackData data, uint32_t readyState);
|
||||
|
||||
// Update child's bufferedAmount when parent's bufferedAmount is updated.
|
||||
// trackingNumber is also passed back to child to ensure the bufferedAmount
|
||||
// is corresponding the last call to send().
|
||||
async UpdateBufferedAmount(uint32_t bufferedAmount, uint32_t trackingNumber);
|
||||
|
||||
both:
|
||||
async RequestDelete();
|
||||
async __delete__();
|
||||
};
|
||||
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
|
||||
69
dom/network/PUDPSocket.ipdl
Normal file
69
dom/network/PUDPSocket.ipdl
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */
|
||||
|
||||
/* 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 protocol PNecko;
|
||||
include protocol PBackground;
|
||||
include protocol PBlob;
|
||||
include InputStreamParams;
|
||||
|
||||
include "mozilla/net/NeckoMessageUtils.h";
|
||||
include "mozilla/net/DNS.h";
|
||||
include "prio.h";
|
||||
|
||||
using mozilla::net::NetAddr from "mozilla/net/DNS.h";
|
||||
using struct mozilla::void_t from "ipc/IPCMessageUtils.h";
|
||||
|
||||
struct UDPAddressInfo {
|
||||
nsCString addr;
|
||||
uint16_t port;
|
||||
};
|
||||
|
||||
union UDPSocketAddr {
|
||||
UDPAddressInfo;
|
||||
NetAddr;
|
||||
};
|
||||
|
||||
union UDPData {
|
||||
uint8_t[];
|
||||
InputStreamParams;
|
||||
};
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
protocol PUDPSocket
|
||||
{
|
||||
manager PNecko or PBackground;
|
||||
|
||||
parent:
|
||||
async Bind(UDPAddressInfo addressInfo, bool addressReuse, bool loopback,
|
||||
uint32_t recvBufferSize, uint32_t sendBufferSize);
|
||||
async Connect(UDPAddressInfo addressInfo);
|
||||
|
||||
async OutgoingData(UDPData data, UDPSocketAddr addr);
|
||||
|
||||
async JoinMulticast(nsCString multicastAddress, nsCString iface);
|
||||
async LeaveMulticast(nsCString multicastAddress, nsCString iface);
|
||||
|
||||
async Close();
|
||||
|
||||
async RequestDelete();
|
||||
|
||||
child:
|
||||
async CallbackOpened(UDPAddressInfo addressInfo);
|
||||
async CallbackConnected(UDPAddressInfo addressInfo);
|
||||
async CallbackClosed();
|
||||
async CallbackReceivedData(UDPAddressInfo addressInfo, uint8_t[] data);
|
||||
async CallbackError(nsCString message, nsCString filename, uint32_t lineNumber);
|
||||
async __delete__();
|
||||
};
|
||||
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
|
||||
192
dom/network/TCPServerSocket.cpp
Normal file
192
dom/network/TCPServerSocket.cpp
Normal file
|
|
@ -0,0 +1,192 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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 "mozilla/dom/TCPServerSocketBinding.h"
|
||||
#include "mozilla/dom/TCPServerSocketEvent.h"
|
||||
#include "mozilla/dom/TCPSocketBinding.h"
|
||||
#include "TCPServerSocketParent.h"
|
||||
#include "TCPServerSocketChild.h"
|
||||
#include "mozilla/dom/Event.h"
|
||||
#include "mozilla/ErrorResult.h"
|
||||
#include "TCPServerSocket.h"
|
||||
#include "TCPSocket.h"
|
||||
|
||||
using namespace mozilla::dom;
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(TCPServerSocket)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(TCPServerSocket,
|
||||
DOMEventTargetHelper)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(TCPServerSocket,
|
||||
DOMEventTargetHelper)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mServerSocket)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mServerBridgeChild)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mServerBridgeParent)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(TCPServerSocket,
|
||||
DOMEventTargetHelper)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mServerSocket)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mServerBridgeChild)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mServerBridgeParent)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
NS_IMPL_ADDREF_INHERITED(TCPServerSocket, DOMEventTargetHelper)
|
||||
NS_IMPL_RELEASE_INHERITED(TCPServerSocket, DOMEventTargetHelper)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(TCPServerSocket)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIServerSocketListener)
|
||||
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
|
||||
|
||||
TCPServerSocket::TCPServerSocket(nsIGlobalObject* aGlobal, uint16_t aPort,
|
||||
bool aUseArrayBuffers, uint16_t aBacklog)
|
||||
: DOMEventTargetHelper(aGlobal)
|
||||
, mPort(aPort)
|
||||
, mBacklog(aBacklog)
|
||||
, mUseArrayBuffers(aUseArrayBuffers)
|
||||
{
|
||||
}
|
||||
|
||||
TCPServerSocket::~TCPServerSocket()
|
||||
{
|
||||
}
|
||||
|
||||
nsresult
|
||||
TCPServerSocket::Init()
|
||||
{
|
||||
if (mServerSocket || mServerBridgeChild) {
|
||||
NS_WARNING("Child TCPServerSocket is already listening.");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (XRE_GetProcessType() == GeckoProcessType_Content) {
|
||||
mServerBridgeChild = new TCPServerSocketChild(this, mPort, mBacklog, mUseArrayBuffers);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult rv;
|
||||
mServerSocket = do_CreateInstance("@mozilla.org/network/server-socket;1", &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = mServerSocket->Init(mPort, false, mBacklog);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = mServerSocket->GetPort(&mPort);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = mServerSocket->AsyncListen(this);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
already_AddRefed<TCPServerSocket>
|
||||
TCPServerSocket::Constructor(const GlobalObject& aGlobal,
|
||||
uint16_t aPort,
|
||||
const ServerSocketOptions& aOptions,
|
||||
uint16_t aBacklog,
|
||||
mozilla::ErrorResult& aRv)
|
||||
{
|
||||
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
|
||||
if (!global) {
|
||||
aRv = NS_ERROR_FAILURE;
|
||||
return nullptr;
|
||||
}
|
||||
bool useArrayBuffers = aOptions.mBinaryType == TCPSocketBinaryType::Arraybuffer;
|
||||
RefPtr<TCPServerSocket> socket = new TCPServerSocket(global, aPort, useArrayBuffers, aBacklog);
|
||||
nsresult rv = socket->Init();
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
aRv = NS_ERROR_FAILURE;
|
||||
return nullptr;
|
||||
}
|
||||
return socket.forget();
|
||||
}
|
||||
|
||||
uint16_t
|
||||
TCPServerSocket::LocalPort()
|
||||
{
|
||||
return mPort;
|
||||
}
|
||||
|
||||
void
|
||||
TCPServerSocket::Close()
|
||||
{
|
||||
if (mServerBridgeChild) {
|
||||
mServerBridgeChild->Close();
|
||||
}
|
||||
if (mServerSocket) {
|
||||
mServerSocket->Close();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TCPServerSocket::FireEvent(const nsAString& aType, TCPSocket* aSocket)
|
||||
{
|
||||
TCPServerSocketEventInit init;
|
||||
init.mBubbles = false;
|
||||
init.mCancelable = false;
|
||||
init.mSocket = aSocket;
|
||||
|
||||
RefPtr<TCPServerSocketEvent> event =
|
||||
TCPServerSocketEvent::Constructor(this, aType, init);
|
||||
event->SetTrusted(true);
|
||||
bool dummy;
|
||||
DispatchEvent(event, &dummy);
|
||||
|
||||
if (mServerBridgeParent) {
|
||||
mServerBridgeParent->OnConnect(event);
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TCPServerSocket::OnSocketAccepted(nsIServerSocket* aServer, nsISocketTransport* aTransport)
|
||||
{
|
||||
nsCOMPtr<nsIGlobalObject> global = GetOwnerGlobal();
|
||||
RefPtr<TCPSocket> socket = TCPSocket::CreateAcceptedSocket(global, aTransport, mUseArrayBuffers);
|
||||
if (mServerBridgeParent) {
|
||||
socket->SetAppIdAndBrowser(mServerBridgeParent->GetAppId(),
|
||||
mServerBridgeParent->GetInIsolatedMozBrowser());
|
||||
}
|
||||
FireEvent(NS_LITERAL_STRING("connect"), socket);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TCPServerSocket::OnStopListening(nsIServerSocket* aServer, nsresult aStatus)
|
||||
{
|
||||
if (aStatus != NS_BINDING_ABORTED) {
|
||||
RefPtr<Event> event = new Event(GetOwner());
|
||||
event->InitEvent(NS_LITERAL_STRING("error"), false, false);
|
||||
event->SetTrusted(true);
|
||||
bool dummy;
|
||||
DispatchEvent(event, &dummy);
|
||||
|
||||
NS_WARNING("Server socket was closed by unexpected reason.");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
mServerSocket = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
TCPServerSocket::AcceptChildSocket(TCPSocketChild* aSocketChild)
|
||||
{
|
||||
nsCOMPtr<nsIGlobalObject> global = GetOwnerGlobal();
|
||||
NS_ENSURE_TRUE(global, NS_ERROR_FAILURE);
|
||||
RefPtr<TCPSocket> socket = TCPSocket::CreateAcceptedSocket(global, aSocketChild, mUseArrayBuffers);
|
||||
NS_ENSURE_TRUE(socket, NS_ERROR_FAILURE);
|
||||
FireEvent(NS_LITERAL_STRING("connect"), socket);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
TCPServerSocket::SetServerBridgeParent(TCPServerSocketParent* aBridgeParent)
|
||||
{
|
||||
mServerBridgeParent = aBridgeParent;
|
||||
}
|
||||
|
||||
JSObject*
|
||||
TCPServerSocket::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return TCPServerSocketBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
83
dom/network/TCPServerSocket.h
Normal file
83
dom/network/TCPServerSocket.h
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_TCPServerSocket_h
|
||||
#define mozilla_dom_TCPServerSocket_h
|
||||
|
||||
#include "mozilla/DOMEventTargetHelper.h"
|
||||
#include "nsIServerSocket.h"
|
||||
|
||||
namespace mozilla {
|
||||
class ErrorResult;
|
||||
namespace dom {
|
||||
|
||||
struct ServerSocketOptions;
|
||||
class GlobalObject;
|
||||
class TCPSocket;
|
||||
class TCPSocketChild;
|
||||
class TCPServerSocketChild;
|
||||
class TCPServerSocketParent;
|
||||
|
||||
class TCPServerSocket final : public DOMEventTargetHelper
|
||||
, public nsIServerSocketListener
|
||||
{
|
||||
public:
|
||||
TCPServerSocket(nsIGlobalObject* aGlobal, uint16_t aPort, bool aUseArrayBuffers,
|
||||
uint16_t aBacklog);
|
||||
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(TCPServerSocket, DOMEventTargetHelper)
|
||||
NS_DECL_NSISERVERSOCKETLISTENER
|
||||
|
||||
nsPIDOMWindowInner* GetParentObject() const
|
||||
{
|
||||
return GetOwner();
|
||||
}
|
||||
|
||||
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
nsresult Init();
|
||||
|
||||
uint16_t LocalPort();
|
||||
void Close();
|
||||
|
||||
static already_AddRefed<TCPServerSocket>
|
||||
Constructor(const GlobalObject& aGlobal,
|
||||
uint16_t aPort,
|
||||
const ServerSocketOptions& aOptions,
|
||||
uint16_t aBacklog,
|
||||
mozilla::ErrorResult& aRv);
|
||||
|
||||
IMPL_EVENT_HANDLER(connect);
|
||||
IMPL_EVENT_HANDLER(error);
|
||||
|
||||
// Relay an accepted socket notification from the parent process and
|
||||
// initialize this object with an existing child actor for the new socket.
|
||||
nsresult AcceptChildSocket(TCPSocketChild* aSocketChild);
|
||||
// Associate this object with an IPC actor in the parent process to relay
|
||||
// notifications to content processes.
|
||||
void SetServerBridgeParent(TCPServerSocketParent* aBridgeParent);
|
||||
|
||||
private:
|
||||
~TCPServerSocket();
|
||||
// Dispatch a TCPServerSocketEvent event of a given type at this object.
|
||||
void FireEvent(const nsAString& aType, TCPSocket* aSocket);
|
||||
|
||||
// The server socket associated with this object.
|
||||
nsCOMPtr<nsIServerSocket> mServerSocket;
|
||||
// The IPC actor in the content process.
|
||||
RefPtr<TCPServerSocketChild> mServerBridgeChild;
|
||||
// The IPC actor in the parent process.
|
||||
RefPtr<TCPServerSocketParent> mServerBridgeParent;
|
||||
int32_t mPort;
|
||||
uint16_t mBacklog;
|
||||
// True if any accepted sockets should use array buffers for received messages.
|
||||
bool mUseArrayBuffers;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_TCPServerSocket_h
|
||||
92
dom/network/TCPServerSocketChild.cpp
Normal file
92
dom/network/TCPServerSocketChild.cpp
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* 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 "TCPServerSocketChild.h"
|
||||
#include "TCPSocketChild.h"
|
||||
#include "TCPServerSocket.h"
|
||||
#include "mozilla/net/NeckoChild.h"
|
||||
#include "mozilla/dom/PBrowserChild.h"
|
||||
#include "mozilla/dom/TabChild.h"
|
||||
#include "nsJSUtils.h"
|
||||
#include "jsfriendapi.h"
|
||||
|
||||
using mozilla::net::gNeckoChild;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION(TCPServerSocketChildBase, mServerSocket)
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(TCPServerSocketChildBase)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(TCPServerSocketChildBase)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TCPServerSocketChildBase)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
TCPServerSocketChildBase::TCPServerSocketChildBase()
|
||||
: mIPCOpen(false)
|
||||
{
|
||||
}
|
||||
|
||||
TCPServerSocketChildBase::~TCPServerSocketChildBase()
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMETHODIMP_(MozExternalRefCountType) TCPServerSocketChild::Release(void)
|
||||
{
|
||||
nsrefcnt refcnt = TCPServerSocketChildBase::Release();
|
||||
if (refcnt == 1 && mIPCOpen) {
|
||||
PTCPServerSocketChild::SendRequestDelete();
|
||||
return 1;
|
||||
}
|
||||
return refcnt;
|
||||
}
|
||||
|
||||
TCPServerSocketChild::TCPServerSocketChild(TCPServerSocket* aServerSocket, uint16_t aLocalPort,
|
||||
uint16_t aBacklog, bool aUseArrayBuffers)
|
||||
{
|
||||
mServerSocket = aServerSocket;
|
||||
AddIPDLReference();
|
||||
gNeckoChild->SendPTCPServerSocketConstructor(this, aLocalPort, aBacklog, aUseArrayBuffers);
|
||||
}
|
||||
|
||||
void
|
||||
TCPServerSocketChildBase::ReleaseIPDLReference()
|
||||
{
|
||||
MOZ_ASSERT(mIPCOpen);
|
||||
mIPCOpen = false;
|
||||
this->Release();
|
||||
}
|
||||
|
||||
void
|
||||
TCPServerSocketChildBase::AddIPDLReference()
|
||||
{
|
||||
MOZ_ASSERT(!mIPCOpen);
|
||||
mIPCOpen = true;
|
||||
this->AddRef();
|
||||
}
|
||||
|
||||
TCPServerSocketChild::~TCPServerSocketChild()
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
TCPServerSocketChild::RecvCallbackAccept(PTCPSocketChild *psocket)
|
||||
{
|
||||
RefPtr<TCPSocketChild> socket = static_cast<TCPSocketChild*>(psocket);
|
||||
nsresult rv = mServerSocket->AcceptChildSocket(socket);
|
||||
NS_ENSURE_SUCCESS(rv, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
TCPServerSocketChild::Close()
|
||||
{
|
||||
SendClose();
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
58
dom/network/TCPServerSocketChild.h
Normal file
58
dom/network/TCPServerSocketChild.h
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_TCPServerSocketChild_h
|
||||
#define mozilla_dom_TCPServerSocketChild_h
|
||||
|
||||
#include "mozilla/net/PTCPServerSocketChild.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsCOMPtr.h"
|
||||
|
||||
#define TCPSERVERSOCKETCHILD_CID \
|
||||
{ 0x41a77ec8, 0xfd86, 0x409e, { 0xae, 0xa9, 0xaf, 0x2c, 0xa4, 0x07, 0xef, 0x8e } }
|
||||
|
||||
class nsITCPServerSocketInternal;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class TCPServerSocket;
|
||||
|
||||
class TCPServerSocketChildBase : public nsISupports {
|
||||
public:
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS(TCPServerSocketChildBase)
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
|
||||
void AddIPDLReference();
|
||||
void ReleaseIPDLReference();
|
||||
|
||||
protected:
|
||||
TCPServerSocketChildBase();
|
||||
virtual ~TCPServerSocketChildBase();
|
||||
|
||||
RefPtr<TCPServerSocket> mServerSocket;
|
||||
bool mIPCOpen;
|
||||
};
|
||||
|
||||
class TCPServerSocketChild : public mozilla::net::PTCPServerSocketChild
|
||||
, public TCPServerSocketChildBase
|
||||
{
|
||||
public:
|
||||
NS_IMETHOD_(MozExternalRefCountType) Release() override;
|
||||
|
||||
TCPServerSocketChild(TCPServerSocket* aServerSocket, uint16_t aLocalPort,
|
||||
uint16_t aBacklog, bool aUseArrayBuffers);
|
||||
~TCPServerSocketChild();
|
||||
|
||||
void Close();
|
||||
|
||||
virtual bool RecvCallbackAccept(PTCPSocketChild *socket) override;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_TCPServerSocketChild_h
|
||||
164
dom/network/TCPServerSocketParent.cpp
Normal file
164
dom/network/TCPServerSocketParent.cpp
Normal file
|
|
@ -0,0 +1,164 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* 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 "nsIScriptSecurityManager.h"
|
||||
#include "TCPServerSocket.h"
|
||||
#include "TCPServerSocketParent.h"
|
||||
#include "nsJSUtils.h"
|
||||
#include "TCPSocketParent.h"
|
||||
#include "mozilla/Unused.h"
|
||||
#include "mozilla/AppProcessChecker.h"
|
||||
#include "mozilla/dom/ContentParent.h"
|
||||
#include "mozilla/dom/TabParent.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION(TCPServerSocketParent, mServerSocket)
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(TCPServerSocketParent)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(TCPServerSocketParent)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TCPServerSocketParent)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
void
|
||||
TCPServerSocketParent::ReleaseIPDLReference()
|
||||
{
|
||||
MOZ_ASSERT(mIPCOpen);
|
||||
mIPCOpen = false;
|
||||
this->Release();
|
||||
}
|
||||
|
||||
void
|
||||
TCPServerSocketParent::AddIPDLReference()
|
||||
{
|
||||
MOZ_ASSERT(!mIPCOpen);
|
||||
mIPCOpen = true;
|
||||
this->AddRef();
|
||||
}
|
||||
|
||||
TCPServerSocketParent::TCPServerSocketParent(PNeckoParent* neckoParent,
|
||||
uint16_t aLocalPort,
|
||||
uint16_t aBacklog,
|
||||
bool aUseArrayBuffers)
|
||||
: mNeckoParent(neckoParent)
|
||||
, mIPCOpen(false)
|
||||
{
|
||||
mServerSocket = new TCPServerSocket(nullptr, aLocalPort, aUseArrayBuffers, aBacklog);
|
||||
mServerSocket->SetServerBridgeParent(this);
|
||||
}
|
||||
|
||||
TCPServerSocketParent::~TCPServerSocketParent()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
TCPServerSocketParent::Init()
|
||||
{
|
||||
NS_ENSURE_SUCCESS_VOID(mServerSocket->Init());
|
||||
}
|
||||
|
||||
uint32_t
|
||||
TCPServerSocketParent::GetAppId()
|
||||
{
|
||||
const PContentParent *content = Manager()->Manager();
|
||||
if (PBrowserParent* browser = SingleManagedOrNull(content->ManagedPBrowserParent())) {
|
||||
TabParent *tab = TabParent::GetFrom(browser);
|
||||
return tab->OwnAppId();
|
||||
} else {
|
||||
return nsIScriptSecurityManager::UNKNOWN_APP_ID;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
TCPServerSocketParent::GetInIsolatedMozBrowser()
|
||||
{
|
||||
const PContentParent *content = Manager()->Manager();
|
||||
if (PBrowserParent* browser = SingleManagedOrNull(content->ManagedPBrowserParent())) {
|
||||
TabParent *tab = TabParent::GetFrom(browser);
|
||||
return tab->IsIsolatedMozBrowserElement();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
TCPServerSocketParent::SendCallbackAccept(TCPSocketParent *socket)
|
||||
{
|
||||
socket->AddIPDLReference();
|
||||
|
||||
nsresult rv;
|
||||
|
||||
nsString host;
|
||||
rv = socket->GetHost(host);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_ERROR("Failed to get host from nsITCPSocketParent");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
uint16_t port;
|
||||
rv = socket->GetPort(&port);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_ERROR("Failed to get port from nsITCPSocketParent");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (mNeckoParent) {
|
||||
if (mNeckoParent->SendPTCPSocketConstructor(socket, host, port)) {
|
||||
mozilla::Unused << PTCPServerSocketParent::SendCallbackAccept(socket);
|
||||
}
|
||||
else {
|
||||
NS_ERROR("Sending data from PTCPSocketParent was failed.");
|
||||
}
|
||||
}
|
||||
else {
|
||||
NS_ERROR("The member value for NeckoParent is wrong.");
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
TCPServerSocketParent::RecvClose()
|
||||
{
|
||||
NS_ENSURE_TRUE(mServerSocket, true);
|
||||
mServerSocket->Close();
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
TCPServerSocketParent::ActorDestroy(ActorDestroyReason why)
|
||||
{
|
||||
if (mServerSocket) {
|
||||
mServerSocket->Close();
|
||||
mServerSocket = nullptr;
|
||||
}
|
||||
mNeckoParent = nullptr;
|
||||
}
|
||||
|
||||
bool
|
||||
TCPServerSocketParent::RecvRequestDelete()
|
||||
{
|
||||
mozilla::Unused << Send__delete__(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
TCPServerSocketParent::OnConnect(TCPServerSocketEvent* event)
|
||||
{
|
||||
RefPtr<TCPSocket> socket = event->Socket();
|
||||
socket->SetAppIdAndBrowser(GetAppId(), GetInIsolatedMozBrowser());
|
||||
|
||||
RefPtr<TCPSocketParent> socketParent = new TCPSocketParent();
|
||||
socketParent->SetSocket(socket);
|
||||
|
||||
socket->SetSocketBridgeParent(socketParent);
|
||||
|
||||
SendCallbackAccept(socketParent);
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
60
dom/network/TCPServerSocketParent.h
Normal file
60
dom/network/TCPServerSocketParent.h
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_TCPServerSocketParent_h
|
||||
#define mozilla_dom_TCPServerSocketParent_h
|
||||
|
||||
#include "mozilla/net/PNeckoParent.h"
|
||||
#include "mozilla/net/PTCPServerSocketParent.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsCOMPtr.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class TCPServerSocket;
|
||||
class TCPServerSocketEvent;
|
||||
class TCPSocketParent;
|
||||
|
||||
class TCPServerSocketParent : public mozilla::net::PTCPServerSocketParent
|
||||
, public nsISupports
|
||||
{
|
||||
public:
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS(TCPServerSocketParent)
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
|
||||
TCPServerSocketParent(PNeckoParent* neckoParent, uint16_t aLocalPort,
|
||||
uint16_t aBacklog, bool aUseArrayBuffers);
|
||||
|
||||
void Init();
|
||||
|
||||
virtual bool RecvClose() override;
|
||||
virtual bool RecvRequestDelete() override;
|
||||
|
||||
uint32_t GetAppId();
|
||||
bool GetInIsolatedMozBrowser();
|
||||
|
||||
void AddIPDLReference();
|
||||
void ReleaseIPDLReference();
|
||||
|
||||
void OnConnect(TCPServerSocketEvent* event);
|
||||
|
||||
private:
|
||||
~TCPServerSocketParent();
|
||||
|
||||
nsresult SendCallbackAccept(TCPSocketParent *socket);
|
||||
|
||||
virtual void ActorDestroy(ActorDestroyReason why) override;
|
||||
|
||||
PNeckoParent* mNeckoParent;
|
||||
RefPtr<TCPServerSocket> mServerSocket;
|
||||
bool mIPCOpen;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_TCPServerSocketParent_h
|
||||
1252
dom/network/TCPSocket.cpp
Normal file
1252
dom/network/TCPSocket.cpp
Normal file
File diff suppressed because it is too large
Load diff
267
dom/network/TCPSocket.h
Normal file
267
dom/network/TCPSocket.h
Normal file
|
|
@ -0,0 +1,267 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_TCPSocket_h
|
||||
#define mozilla_dom_TCPSocket_h
|
||||
|
||||
#include "mozilla/dom/TCPSocketBinding.h"
|
||||
#include "mozilla/DOMEventTargetHelper.h"
|
||||
#include "nsITransport.h"
|
||||
#include "nsIStreamListener.h"
|
||||
#include "nsIAsyncInputStream.h"
|
||||
#include "nsISupportsImpl.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "nsWeakReference.h"
|
||||
#include "nsITCPSocketCallback.h"
|
||||
#include "js/RootingAPI.h"
|
||||
|
||||
class nsISocketTransport;
|
||||
class nsIInputStreamPump;
|
||||
class nsIScriptableInputStream;
|
||||
class nsIBinaryInputStream;
|
||||
class nsIMultiplexInputStream;
|
||||
class nsIAsyncStreamCopier;
|
||||
class nsIInputStream;
|
||||
class nsINetworkInfo;
|
||||
|
||||
namespace mozilla {
|
||||
class ErrorResult;
|
||||
namespace dom {
|
||||
|
||||
class DOMError;
|
||||
struct ServerSocketOptions;
|
||||
class TCPServerSocket;
|
||||
class TCPSocketChild;
|
||||
class TCPSocketParent;
|
||||
|
||||
// This interface is only used for legacy navigator.mozTCPSocket API compatibility.
|
||||
class LegacyMozTCPSocket : public nsISupports
|
||||
{
|
||||
public:
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS(LegacyMozTCPSocket)
|
||||
|
||||
explicit LegacyMozTCPSocket(nsPIDOMWindowInner* aWindow);
|
||||
|
||||
already_AddRefed<TCPServerSocket>
|
||||
Listen(uint16_t aPort,
|
||||
const ServerSocketOptions& aOptions,
|
||||
uint16_t aBacklog,
|
||||
ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<TCPSocket>
|
||||
Open(const nsAString& aHost,
|
||||
uint16_t aPort,
|
||||
const SocketOptions& aOptions,
|
||||
ErrorResult& aRv);
|
||||
|
||||
bool WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto,
|
||||
JS::MutableHandle<JSObject*> aReflector);
|
||||
|
||||
private:
|
||||
virtual ~LegacyMozTCPSocket();
|
||||
|
||||
nsCOMPtr<nsIGlobalObject> mGlobal;
|
||||
};
|
||||
|
||||
class TCPSocket final : public DOMEventTargetHelper
|
||||
, public nsIStreamListener
|
||||
, public nsITransportEventSink
|
||||
, public nsIInputStreamCallback
|
||||
, public nsIObserver
|
||||
, public nsSupportsWeakReference
|
||||
, public nsITCPSocketCallback
|
||||
{
|
||||
public:
|
||||
TCPSocket(nsIGlobalObject* aGlobal, const nsAString& aHost, uint16_t aPort,
|
||||
bool aSsl, bool aUseArrayBuffers);
|
||||
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(TCPSocket, DOMEventTargetHelper)
|
||||
NS_DECL_NSIREQUESTOBSERVER
|
||||
NS_DECL_NSISTREAMLISTENER
|
||||
NS_DECL_NSITRANSPORTEVENTSINK
|
||||
NS_DECL_NSIINPUTSTREAMCALLBACK
|
||||
NS_DECL_NSIOBSERVER
|
||||
NS_DECL_NSITCPSOCKETCALLBACK
|
||||
|
||||
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
static bool ShouldTCPSocketExist(JSContext* aCx, JSObject* aGlobal);
|
||||
|
||||
void GetHost(nsAString& aHost);
|
||||
uint32_t Port();
|
||||
bool Ssl();
|
||||
uint64_t BufferedAmount();
|
||||
void Suspend();
|
||||
void Resume(ErrorResult& aRv);
|
||||
void Close();
|
||||
void CloseImmediately();
|
||||
bool Send(JSContext* aCx, const nsACString& aData, ErrorResult& aRv);
|
||||
bool Send(JSContext* aCx,
|
||||
const ArrayBuffer& aData,
|
||||
uint32_t aByteOffset,
|
||||
const Optional<uint32_t>& aByteLength,
|
||||
ErrorResult& aRv);
|
||||
TCPReadyState ReadyState();
|
||||
TCPSocketBinaryType BinaryType();
|
||||
void UpgradeToSecure(ErrorResult& aRv);
|
||||
|
||||
static already_AddRefed<TCPSocket>
|
||||
Constructor(const GlobalObject& aGlobal,
|
||||
const nsAString& aHost,
|
||||
uint16_t aPort,
|
||||
const SocketOptions& aOptions,
|
||||
ErrorResult& aRv);
|
||||
|
||||
// Perform a send operation that's asssociated with a sequence number. Used in
|
||||
// IPC scenarios to track the number of bytes buffered at any given time.
|
||||
void SendWithTrackingNumber(const nsACString& aData,
|
||||
const uint32_t& aTrackingNumber,
|
||||
ErrorResult& aRv);
|
||||
void SendWithTrackingNumber(JSContext* aCx,
|
||||
const ArrayBuffer& aData,
|
||||
uint32_t aByteOffset,
|
||||
const Optional<uint32_t>& aByteLength,
|
||||
const uint32_t& aTrackingNumber,
|
||||
ErrorResult& aRv);
|
||||
// Create a TCPSocket object from an existing low-level socket connection.
|
||||
// Used by the TCPServerSocket implementation when a new connection is accepted.
|
||||
static already_AddRefed<TCPSocket>
|
||||
CreateAcceptedSocket(nsIGlobalObject* aGlobal, nsISocketTransport* aTransport, bool aUseArrayBuffers);
|
||||
// Create a TCPSocket object from an existing child-side IPC actor.
|
||||
// Used by the TCPServerSocketChild implementation when a new connection is accepted.
|
||||
static already_AddRefed<TCPSocket>
|
||||
CreateAcceptedSocket(nsIGlobalObject* aGlobal, TCPSocketChild* aSocketBridge, bool aUseArrayBuffers);
|
||||
|
||||
// Initialize this socket's associated app and browser information.
|
||||
void SetAppIdAndBrowser(uint32_t aAppId, bool aInBrowser);
|
||||
// Initialize this socket's associated IPC actor in the parent process.
|
||||
void SetSocketBridgeParent(TCPSocketParent* aBridgeParent);
|
||||
|
||||
static bool SocketEnabled();
|
||||
|
||||
IMPL_EVENT_HANDLER(open);
|
||||
IMPL_EVENT_HANDLER(drain);
|
||||
IMPL_EVENT_HANDLER(data);
|
||||
IMPL_EVENT_HANDLER(error);
|
||||
IMPL_EVENT_HANDLER(close);
|
||||
|
||||
nsresult Init();
|
||||
|
||||
// Inform this socket that a buffered send() has completed sending.
|
||||
void NotifyCopyComplete(nsresult aStatus);
|
||||
|
||||
// Initialize this socket from a low-level connection that hasn't connected yet
|
||||
// (called from RecvOpenBind() in TCPSocketParent).
|
||||
nsresult InitWithUnconnectedTransport(nsISocketTransport* aTransport);
|
||||
|
||||
private:
|
||||
~TCPSocket();
|
||||
|
||||
// Initialize this socket with an existing IPC actor.
|
||||
void InitWithSocketChild(TCPSocketChild* aBridge);
|
||||
// Initialize this socket from an existing low-level connection.
|
||||
nsresult InitWithTransport(nsISocketTransport* aTransport);
|
||||
// Initialize the input/output streams for this socket object.
|
||||
nsresult CreateStream();
|
||||
// Initialize the asynchronous read operation from this socket's input stream.
|
||||
nsresult CreateInputStreamPump();
|
||||
// Send the contents of the provided input stream, which is assumed to be the given length
|
||||
// for reporting and buffering purposes.
|
||||
bool Send(nsIInputStream* aStream, uint32_t aByteLength);
|
||||
// Begin an asynchronous copy operation if one is not already in progress.
|
||||
nsresult EnsureCopying();
|
||||
// Enable TLS on this socket.
|
||||
void ActivateTLS();
|
||||
// Dispatch an error event if necessary, then dispatch a "close" event.
|
||||
nsresult MaybeReportErrorAndCloseIfOpen(nsresult status);
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
// Store and reset any saved network stats for this socket.
|
||||
void SaveNetworkStats(bool aEnforce);
|
||||
#endif
|
||||
|
||||
// Helper for FireDataStringEvent/FireDataArrayEvent.
|
||||
nsresult FireDataEvent(JSContext* aCx, const nsAString& aType,
|
||||
JS::Handle<JS::Value> aData);
|
||||
// Helper for Close/CloseImmediately
|
||||
void CloseHelper(bool waitForUnsentData);
|
||||
|
||||
TCPReadyState mReadyState;
|
||||
// Whether to use strings or array buffers for the "data" event.
|
||||
bool mUseArrayBuffers;
|
||||
nsString mHost;
|
||||
uint16_t mPort;
|
||||
// Whether this socket is using a secure transport.
|
||||
bool mSsl;
|
||||
|
||||
// The associated IPC actor in a child process.
|
||||
RefPtr<TCPSocketChild> mSocketBridgeChild;
|
||||
// The associated IPC actor in a parent process.
|
||||
RefPtr<TCPSocketParent> mSocketBridgeParent;
|
||||
|
||||
// Raw socket streams
|
||||
nsCOMPtr<nsISocketTransport> mTransport;
|
||||
nsCOMPtr<nsIInputStream> mSocketInputStream;
|
||||
nsCOMPtr<nsIOutputStream> mSocketOutputStream;
|
||||
|
||||
// Input stream machinery
|
||||
nsCOMPtr<nsIInputStreamPump> mInputStreamPump;
|
||||
nsCOMPtr<nsIScriptableInputStream> mInputStreamScriptable;
|
||||
nsCOMPtr<nsIBinaryInputStream> mInputStreamBinary;
|
||||
|
||||
// Output stream machinery
|
||||
nsCOMPtr<nsIMultiplexInputStream> mMultiplexStream;
|
||||
nsCOMPtr<nsIAsyncStreamCopier> mMultiplexStreamCopier;
|
||||
|
||||
// Is there an async copy operation in progress?
|
||||
bool mAsyncCopierActive;
|
||||
// True if the buffer is full and a "drain" event is expected by the client.
|
||||
bool mWaitingForDrain;
|
||||
|
||||
// The id of the window that created this socket.
|
||||
uint64_t mInnerWindowID;
|
||||
|
||||
// The current number of buffered bytes. Only used in content processes when IPC is enabled.
|
||||
uint64_t mBufferedAmount;
|
||||
|
||||
// The number of times this socket has had `Suspend` called without a corresponding `Resume`.
|
||||
uint32_t mSuspendCount;
|
||||
|
||||
// The current sequence number (ie. number of send operations) that have been processed.
|
||||
// This is used in the IPC scenario by the child process to filter out outdated notifications
|
||||
// about the amount of buffered data present in the parent process.
|
||||
uint32_t mTrackingNumber;
|
||||
|
||||
// True if this socket has been upgraded to secure after the initial connection,
|
||||
// but the actual upgrade is waiting for an in-progress copy operation to complete.
|
||||
bool mWaitingForStartTLS;
|
||||
// The buffered data awaiting the TLS upgrade to finish.
|
||||
nsTArray<nsCOMPtr<nsIInputStream>> mPendingDataAfterStartTLS;
|
||||
|
||||
// The data to be sent while AsyncCopier is still active.
|
||||
nsTArray<nsCOMPtr<nsIInputStream>> mPendingDataWhileCopierActive;
|
||||
|
||||
bool mObserversActive;
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
// Number of bytes sent.
|
||||
uint32_t mTxBytes;
|
||||
// Number of bytes received.
|
||||
uint32_t mRxBytes;
|
||||
// The app that owns this socket.
|
||||
uint32_t mAppId;
|
||||
// Was this socket created inside of an isolated browser frame?
|
||||
bool mInIsolatedMozBrowser;
|
||||
// The name of the active network used by this socket.
|
||||
nsCOMPtr<nsINetworkInfo> mActiveNetworkInfo;
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_TCPSocket_h
|
||||
254
dom/network/TCPSocketChild.cpp
Normal file
254
dom/network/TCPSocketChild.cpp
Normal file
|
|
@ -0,0 +1,254 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* 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 <algorithm>
|
||||
#include "TCPSocketChild.h"
|
||||
#include "mozilla/Unused.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "mozilla/net/NeckoChild.h"
|
||||
#include "mozilla/dom/PBrowserChild.h"
|
||||
#include "mozilla/dom/TabChild.h"
|
||||
#include "nsITCPSocketCallback.h"
|
||||
#include "TCPSocket.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "jsapi.h"
|
||||
#include "jsfriendapi.h"
|
||||
|
||||
using mozilla::net::gNeckoChild;
|
||||
|
||||
namespace IPC {
|
||||
|
||||
bool
|
||||
DeserializeArrayBuffer(JSContext* cx,
|
||||
const InfallibleTArray<uint8_t>& aBuffer,
|
||||
JS::MutableHandle<JS::Value> aVal)
|
||||
{
|
||||
mozilla::UniquePtr<uint8_t[], JS::FreePolicy> data(js_pod_malloc<uint8_t>(aBuffer.Length()));
|
||||
if (!data)
|
||||
return false;
|
||||
memcpy(data.get(), aBuffer.Elements(), aBuffer.Length());
|
||||
|
||||
JSObject* obj = JS_NewArrayBufferWithContents(cx, aBuffer.Length(), data.get());
|
||||
if (!obj)
|
||||
return false;
|
||||
// If JS_NewArrayBufferWithContents returns non-null, the ownership of
|
||||
// the data is transfered to obj, so we release the ownership here.
|
||||
mozilla::Unused << data.release();
|
||||
|
||||
aVal.setObject(*obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace IPC
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(TCPSocketChildBase)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(TCPSocketChildBase)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSocket)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(TCPSocketChildBase)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mSocket)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(TCPSocketChildBase)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(TCPSocketChildBase)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(TCPSocketChildBase)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TCPSocketChildBase)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
TCPSocketChildBase::TCPSocketChildBase()
|
||||
: mIPCOpen(false)
|
||||
{
|
||||
mozilla::HoldJSObjects(this);
|
||||
}
|
||||
|
||||
TCPSocketChildBase::~TCPSocketChildBase()
|
||||
{
|
||||
mozilla::DropJSObjects(this);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP_(MozExternalRefCountType) TCPSocketChild::Release(void)
|
||||
{
|
||||
nsrefcnt refcnt = TCPSocketChildBase::Release();
|
||||
if (refcnt == 1 && mIPCOpen) {
|
||||
PTCPSocketChild::SendRequestDelete();
|
||||
return 1;
|
||||
}
|
||||
return refcnt;
|
||||
}
|
||||
|
||||
TCPSocketChild::TCPSocketChild(const nsAString& aHost, const uint16_t& aPort)
|
||||
: mHost(aHost)
|
||||
, mPort(aPort)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
TCPSocketChild::SendOpen(nsITCPSocketCallback* aSocket, bool aUseSSL, bool aUseArrayBuffers)
|
||||
{
|
||||
mSocket = aSocket;
|
||||
|
||||
AddIPDLReference();
|
||||
gNeckoChild->SendPTCPSocketConstructor(this, mHost, mPort);
|
||||
MOZ_ASSERT(mFilterName.IsEmpty()); // Currently nobody should use this
|
||||
PTCPSocketChild::SendOpen(mHost, mPort, aUseSSL, aUseArrayBuffers);
|
||||
}
|
||||
|
||||
void
|
||||
TCPSocketChild::SendWindowlessOpenBind(nsITCPSocketCallback* aSocket,
|
||||
const nsACString& aRemoteHost, uint16_t aRemotePort,
|
||||
const nsACString& aLocalHost, uint16_t aLocalPort,
|
||||
bool aUseSSL)
|
||||
{
|
||||
mSocket = aSocket;
|
||||
AddIPDLReference();
|
||||
gNeckoChild->SendPTCPSocketConstructor(this,
|
||||
NS_ConvertUTF8toUTF16(aRemoteHost),
|
||||
aRemotePort);
|
||||
PTCPSocketChild::SendOpenBind(nsCString(aRemoteHost), aRemotePort,
|
||||
nsCString(aLocalHost), aLocalPort,
|
||||
aUseSSL, true, mFilterName);
|
||||
}
|
||||
|
||||
void
|
||||
TCPSocketChildBase::ReleaseIPDLReference()
|
||||
{
|
||||
MOZ_ASSERT(mIPCOpen);
|
||||
mIPCOpen = false;
|
||||
mSocket = nullptr;
|
||||
this->Release();
|
||||
}
|
||||
|
||||
void
|
||||
TCPSocketChildBase::AddIPDLReference()
|
||||
{
|
||||
MOZ_ASSERT(!mIPCOpen);
|
||||
mIPCOpen = true;
|
||||
this->AddRef();
|
||||
}
|
||||
|
||||
TCPSocketChild::~TCPSocketChild()
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
TCPSocketChild::RecvUpdateBufferedAmount(const uint32_t& aBuffered,
|
||||
const uint32_t& aTrackingNumber)
|
||||
{
|
||||
mSocket->UpdateBufferedAmount(aBuffered, aTrackingNumber);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
TCPSocketChild::RecvCallback(const nsString& aType,
|
||||
const CallbackData& aData,
|
||||
const uint32_t& aReadyState)
|
||||
{
|
||||
mSocket->UpdateReadyState(aReadyState);
|
||||
|
||||
if (aData.type() == CallbackData::Tvoid_t) {
|
||||
mSocket->FireEvent(aType);
|
||||
|
||||
} else if (aData.type() == CallbackData::TTCPError) {
|
||||
const TCPError& err(aData.get_TCPError());
|
||||
mSocket->FireErrorEvent(err.name(), err.message());
|
||||
|
||||
} else if (aData.type() == CallbackData::TSendableData) {
|
||||
const SendableData& data = aData.get_SendableData();
|
||||
|
||||
if (data.type() == SendableData::TArrayOfuint8_t) {
|
||||
mSocket->FireDataArrayEvent(aType, data.get_ArrayOfuint8_t());
|
||||
} else if (data.type() == SendableData::TnsCString) {
|
||||
mSocket->FireDataStringEvent(aType, data.get_nsCString());
|
||||
} else {
|
||||
MOZ_CRASH("Invalid callback data type!");
|
||||
}
|
||||
} else {
|
||||
MOZ_CRASH("Invalid callback type!");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
TCPSocketChild::SendSend(const nsACString& aData, uint32_t aTrackingNumber)
|
||||
{
|
||||
SendData(nsCString(aData), aTrackingNumber);
|
||||
}
|
||||
|
||||
nsresult
|
||||
TCPSocketChild::SendSend(const ArrayBuffer& aData,
|
||||
uint32_t aByteOffset,
|
||||
uint32_t aByteLength,
|
||||
uint32_t aTrackingNumber)
|
||||
{
|
||||
uint32_t buflen = aData.Length();
|
||||
uint32_t offset = std::min(buflen, aByteOffset);
|
||||
uint32_t nbytes = std::min(buflen - aByteOffset, aByteLength);
|
||||
FallibleTArray<uint8_t> fallibleArr;
|
||||
if (!fallibleArr.InsertElementsAt(0, aData.Data() + offset, nbytes, fallible)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
InfallibleTArray<uint8_t> arr;
|
||||
arr.SwapElements(fallibleArr);
|
||||
SendData(arr, aTrackingNumber);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TCPSocketChild::SendSendArray(nsTArray<uint8_t>& aArray, uint32_t aTrackingNumber)
|
||||
{
|
||||
SendData(aArray, aTrackingNumber);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
TCPSocketChild::SetSocket(TCPSocket* aSocket)
|
||||
{
|
||||
mSocket = aSocket;
|
||||
}
|
||||
|
||||
void
|
||||
TCPSocketChild::GetHost(nsAString& aHost)
|
||||
{
|
||||
aHost = mHost;
|
||||
}
|
||||
|
||||
void
|
||||
TCPSocketChild::GetPort(uint16_t* aPort)
|
||||
{
|
||||
*aPort = mPort;
|
||||
}
|
||||
|
||||
nsresult
|
||||
TCPSocketChild::SetFilterName(const nsACString& aFilterName)
|
||||
{
|
||||
if (!mFilterName.IsEmpty()) {
|
||||
// filter name can only be set once.
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
mFilterName = aFilterName;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
TCPSocketChild::RecvRequestDelete()
|
||||
{
|
||||
mozilla::Unused << Send__delete__(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
90
dom/network/TCPSocketChild.h
Normal file
90
dom/network/TCPSocketChild.h
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_TCPSocketChild_h
|
||||
#define mozilla_dom_TCPSocketChild_h
|
||||
|
||||
#include "mozilla/dom/TypedArray.h"
|
||||
#include "mozilla/net/PTCPSocketChild.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "js/TypeDecls.h"
|
||||
|
||||
class nsITCPSocketCallback;
|
||||
|
||||
namespace IPC {
|
||||
bool
|
||||
DeserializeArrayBuffer(JSContext* cx,
|
||||
const InfallibleTArray<uint8_t>& aBuffer,
|
||||
JS::MutableHandle<JS::Value> aVal);
|
||||
}
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class TCPSocket;
|
||||
|
||||
class TCPSocketChildBase : public nsISupports {
|
||||
public:
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(TCPSocketChildBase)
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
|
||||
void AddIPDLReference();
|
||||
void ReleaseIPDLReference();
|
||||
|
||||
protected:
|
||||
TCPSocketChildBase();
|
||||
virtual ~TCPSocketChildBase();
|
||||
|
||||
nsCOMPtr<nsITCPSocketCallback> mSocket;
|
||||
bool mIPCOpen;
|
||||
};
|
||||
|
||||
class TCPSocketChild : public mozilla::net::PTCPSocketChild
|
||||
, public TCPSocketChildBase
|
||||
{
|
||||
public:
|
||||
NS_IMETHOD_(MozExternalRefCountType) Release() override;
|
||||
|
||||
TCPSocketChild(const nsAString& aHost, const uint16_t& aPort);
|
||||
~TCPSocketChild();
|
||||
|
||||
void SendOpen(nsITCPSocketCallback* aSocket, bool aUseSSL, bool aUseArrayBuffers);
|
||||
void SendWindowlessOpenBind(nsITCPSocketCallback* aSocket,
|
||||
const nsACString& aRemoteHost, uint16_t aRemotePort,
|
||||
const nsACString& aLocalHost, uint16_t aLocalPort,
|
||||
bool aUseSSL);
|
||||
NS_IMETHOD SendSendArray(nsTArray<uint8_t>& aArray,
|
||||
uint32_t aTrackingNumber);
|
||||
void SendSend(const nsACString& aData, uint32_t aTrackingNumber);
|
||||
nsresult SendSend(const ArrayBuffer& aData,
|
||||
uint32_t aByteOffset,
|
||||
uint32_t aByteLength,
|
||||
uint32_t aTrackingNumber);
|
||||
void SendSendArray(nsTArray<uint8_t>* arr,
|
||||
uint32_t trackingNumber);
|
||||
void SetSocket(TCPSocket* aSocket);
|
||||
|
||||
void GetHost(nsAString& aHost);
|
||||
void GetPort(uint16_t* aPort);
|
||||
|
||||
virtual bool RecvCallback(const nsString& aType,
|
||||
const CallbackData& aData,
|
||||
const uint32_t& aReadyState) override;
|
||||
virtual bool RecvRequestDelete() override;
|
||||
virtual bool RecvUpdateBufferedAmount(const uint32_t& aBufferred,
|
||||
const uint32_t& aTrackingNumber) override;
|
||||
nsresult SetFilterName(const nsACString& aFilterName);
|
||||
private:
|
||||
nsString mHost;
|
||||
uint16_t mPort;
|
||||
nsCString mFilterName;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
427
dom/network/TCPSocketParent.cpp
Normal file
427
dom/network/TCPSocketParent.cpp
Normal file
|
|
@ -0,0 +1,427 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* 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 "TCPSocketParent.h"
|
||||
#include "jsapi.h"
|
||||
#include "jsfriendapi.h"
|
||||
#include "nsJSUtils.h"
|
||||
#include "mozilla/Unused.h"
|
||||
#include "mozilla/AppProcessChecker.h"
|
||||
#include "mozilla/net/NeckoCommon.h"
|
||||
#include "mozilla/net/PNeckoParent.h"
|
||||
#include "mozilla/dom/ContentParent.h"
|
||||
#include "mozilla/dom/ScriptSettings.h"
|
||||
#include "mozilla/dom/TabParent.h"
|
||||
#include "mozilla/HoldDropJSObjects.h"
|
||||
#include "nsISocketTransportService.h"
|
||||
#include "nsISocketTransport.h"
|
||||
#include "nsIScriptSecurityManager.h"
|
||||
#include "nsNetUtil.h"
|
||||
|
||||
namespace IPC {
|
||||
|
||||
//Defined in TCPSocketChild.cpp
|
||||
extern bool
|
||||
DeserializeArrayBuffer(JSContext* aCx,
|
||||
const InfallibleTArray<uint8_t>& aBuffer,
|
||||
JS::MutableHandle<JS::Value> aVal);
|
||||
|
||||
} // namespace IPC
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
namespace net {
|
||||
//
|
||||
// set MOZ_LOG=TCPSocket:5
|
||||
//
|
||||
extern LazyLogModule gTCPSocketLog;
|
||||
#define TCPSOCKET_LOG(args) MOZ_LOG(gTCPSocketLog, LogLevel::Debug, args)
|
||||
#define TCPSOCKET_LOG_ENABLED() MOZ_LOG_TEST(gTCPSocketLog, LogLevel::Debug)
|
||||
} // namespace net
|
||||
|
||||
namespace dom {
|
||||
|
||||
static void
|
||||
FireInteralError(mozilla::net::PTCPSocketParent* aActor, uint32_t aLineNo)
|
||||
{
|
||||
mozilla::Unused <<
|
||||
aActor->SendCallback(NS_LITERAL_STRING("onerror"),
|
||||
TCPError(NS_LITERAL_STRING("InvalidStateError"), NS_LITERAL_STRING("Internal error")),
|
||||
static_cast<uint32_t>(TCPReadyState::Connecting));
|
||||
}
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TCPSocketParentBase)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION(TCPSocketParentBase, mSocket)
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(TCPSocketParentBase)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(TCPSocketParentBase)
|
||||
|
||||
TCPSocketParentBase::TCPSocketParentBase()
|
||||
: mIPCOpen(false)
|
||||
{
|
||||
}
|
||||
|
||||
TCPSocketParentBase::~TCPSocketParentBase()
|
||||
{
|
||||
}
|
||||
|
||||
uint32_t
|
||||
TCPSocketParent::GetAppId()
|
||||
{
|
||||
const PContentParent *content = Manager()->Manager();
|
||||
if (PBrowserParent* browser = SingleManagedOrNull(content->ManagedPBrowserParent())) {
|
||||
TabParent *tab = TabParent::GetFrom(browser);
|
||||
return tab->OwnAppId();
|
||||
} else {
|
||||
return nsIScriptSecurityManager::UNKNOWN_APP_ID;
|
||||
}
|
||||
};
|
||||
|
||||
bool
|
||||
TCPSocketParent::GetInIsolatedMozBrowser()
|
||||
{
|
||||
const PContentParent *content = Manager()->Manager();
|
||||
if (PBrowserParent* browser = SingleManagedOrNull(content->ManagedPBrowserParent())) {
|
||||
TabParent *tab = TabParent::GetFrom(browser);
|
||||
return tab->IsIsolatedMozBrowserElement();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TCPSocketParentBase::ReleaseIPDLReference()
|
||||
{
|
||||
MOZ_ASSERT(mIPCOpen);
|
||||
mIPCOpen = false;
|
||||
this->Release();
|
||||
}
|
||||
|
||||
void
|
||||
TCPSocketParentBase::AddIPDLReference()
|
||||
{
|
||||
MOZ_ASSERT(!mIPCOpen);
|
||||
mIPCOpen = true;
|
||||
this->AddRef();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP_(MozExternalRefCountType) TCPSocketParent::Release(void)
|
||||
{
|
||||
nsrefcnt refcnt = TCPSocketParentBase::Release();
|
||||
if (refcnt == 1 && mIPCOpen) {
|
||||
mozilla::Unused << PTCPSocketParent::SendRequestDelete();
|
||||
return 1;
|
||||
}
|
||||
return refcnt;
|
||||
}
|
||||
|
||||
bool
|
||||
TCPSocketParent::RecvOpen(const nsString& aHost, const uint16_t& aPort, const bool& aUseSSL,
|
||||
const bool& aUseArrayBuffers)
|
||||
{
|
||||
// We don't have browser actors in xpcshell, and hence can't run automated
|
||||
// tests without this loophole.
|
||||
if (net::UsingNeckoIPCSecurity() &&
|
||||
!AssertAppProcessPermission(Manager()->Manager(), "tcp-socket")) {
|
||||
FireInteralError(this, __LINE__);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Obtain App ID
|
||||
uint32_t appId = GetAppId();
|
||||
bool inIsolatedMozBrowser = GetInIsolatedMozBrowser();
|
||||
|
||||
mSocket = new TCPSocket(nullptr, aHost, aPort, aUseSSL, aUseArrayBuffers);
|
||||
mSocket->SetAppIdAndBrowser(appId, inIsolatedMozBrowser);
|
||||
mSocket->SetSocketBridgeParent(this);
|
||||
NS_ENSURE_SUCCESS(mSocket->Init(), true);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
TCPSocketParent::RecvOpenBind(const nsCString& aRemoteHost,
|
||||
const uint16_t& aRemotePort,
|
||||
const nsCString& aLocalAddr,
|
||||
const uint16_t& aLocalPort,
|
||||
const bool& aUseSSL,
|
||||
const bool& aUseArrayBuffers,
|
||||
const nsCString& aFilter)
|
||||
{
|
||||
if (net::UsingNeckoIPCSecurity() &&
|
||||
!AssertAppProcessPermission(Manager()->Manager(), "tcp-socket")) {
|
||||
FireInteralError(this, __LINE__);
|
||||
return true;
|
||||
}
|
||||
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsISocketTransportService> sts =
|
||||
do_GetService("@mozilla.org/network/socket-transport-service;1", &rv);
|
||||
if (NS_FAILED(rv)) {
|
||||
FireInteralError(this, __LINE__);
|
||||
return true;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsISocketTransport> socketTransport;
|
||||
rv = sts->CreateTransport(nullptr, 0,
|
||||
aRemoteHost, aRemotePort,
|
||||
nullptr, getter_AddRefs(socketTransport));
|
||||
if (NS_FAILED(rv)) {
|
||||
FireInteralError(this, __LINE__);
|
||||
return true;
|
||||
}
|
||||
|
||||
PRNetAddr prAddr;
|
||||
if (PR_SUCCESS != PR_InitializeNetAddr(PR_IpAddrAny, aLocalPort, &prAddr)) {
|
||||
FireInteralError(this, __LINE__);
|
||||
return true;
|
||||
}
|
||||
if (PR_SUCCESS != PR_StringToNetAddr(aLocalAddr.BeginReading(), &prAddr)) {
|
||||
FireInteralError(this, __LINE__);
|
||||
return true;
|
||||
}
|
||||
|
||||
mozilla::net::NetAddr addr;
|
||||
PRNetAddrToNetAddr(&prAddr, &addr);
|
||||
rv = socketTransport->Bind(&addr);
|
||||
if (NS_FAILED(rv)) {
|
||||
FireInteralError(this, __LINE__);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!aFilter.IsEmpty()) {
|
||||
nsAutoCString contractId(NS_NETWORK_TCP_SOCKET_FILTER_HANDLER_PREFIX);
|
||||
contractId.Append(aFilter);
|
||||
nsCOMPtr<nsISocketFilterHandler> filterHandler =
|
||||
do_GetService(contractId.get());
|
||||
if (!filterHandler) {
|
||||
NS_ERROR("Content doesn't have a valid filter");
|
||||
FireInteralError(this, __LINE__);
|
||||
return true;
|
||||
}
|
||||
rv = filterHandler->NewFilter(getter_AddRefs(mFilter));
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_ERROR("Cannot create filter that content specified");
|
||||
FireInteralError(this, __LINE__);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Obtain App ID
|
||||
uint32_t appId = nsIScriptSecurityManager::NO_APP_ID;
|
||||
bool inIsolatedMozBrowser = false;
|
||||
const PContentParent *content = Manager()->Manager();
|
||||
if (PBrowserParent* browser = SingleManagedOrNull(content->ManagedPBrowserParent())) {
|
||||
// appId's are for B2G only currently, where managees.Count() == 1
|
||||
// This is not guaranteed currently in Desktop, so skip this there.
|
||||
TabParent *tab = TabParent::GetFrom(browser);
|
||||
appId = tab->OwnAppId();
|
||||
inIsolatedMozBrowser = tab->IsIsolatedMozBrowserElement();
|
||||
}
|
||||
|
||||
mSocket = new TCPSocket(nullptr, NS_ConvertUTF8toUTF16(aRemoteHost), aRemotePort, aUseSSL, aUseArrayBuffers);
|
||||
mSocket->SetAppIdAndBrowser(appId, inIsolatedMozBrowser);
|
||||
mSocket->SetSocketBridgeParent(this);
|
||||
rv = mSocket->InitWithUnconnectedTransport(socketTransport);
|
||||
NS_ENSURE_SUCCESS(rv, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
TCPSocketParent::RecvStartTLS()
|
||||
{
|
||||
NS_ENSURE_TRUE(mSocket, true);
|
||||
ErrorResult rv;
|
||||
mSocket->UpgradeToSecure(rv);
|
||||
if (NS_WARN_IF(rv.Failed())) {
|
||||
rv.SuppressException();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
TCPSocketParent::RecvSuspend()
|
||||
{
|
||||
NS_ENSURE_TRUE(mSocket, true);
|
||||
mSocket->Suspend();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
TCPSocketParent::RecvResume()
|
||||
{
|
||||
NS_ENSURE_TRUE(mSocket, true);
|
||||
ErrorResult rv;
|
||||
mSocket->Resume(rv);
|
||||
if (NS_WARN_IF(rv.Failed())) {
|
||||
rv.SuppressException();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
TCPSocketParent::RecvData(const SendableData& aData,
|
||||
const uint32_t& aTrackingNumber)
|
||||
{
|
||||
ErrorResult rv;
|
||||
|
||||
if (mFilter) {
|
||||
mozilla::net::NetAddr addr; // dummy value
|
||||
bool allowed;
|
||||
MOZ_ASSERT(aData.type() == SendableData::TArrayOfuint8_t,
|
||||
"Unsupported data type for filtering");
|
||||
const InfallibleTArray<uint8_t>& data(aData.get_ArrayOfuint8_t());
|
||||
nsresult nsrv = mFilter->FilterPacket(&addr, data.Elements(),
|
||||
data.Length(),
|
||||
nsISocketFilter::SF_OUTGOING,
|
||||
&allowed);
|
||||
|
||||
// Reject sending of unallowed data
|
||||
if (NS_WARN_IF(NS_FAILED(nsrv)) || !allowed) {
|
||||
TCPSOCKET_LOG(("%s: Dropping outgoing TCP packet", __FUNCTION__));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
switch (aData.type()) {
|
||||
case SendableData::TArrayOfuint8_t: {
|
||||
AutoSafeJSContext autoCx;
|
||||
JS::Rooted<JS::Value> val(autoCx);
|
||||
const nsTArray<uint8_t>& buffer = aData.get_ArrayOfuint8_t();
|
||||
bool ok = IPC::DeserializeArrayBuffer(autoCx, buffer, &val);
|
||||
NS_ENSURE_TRUE(ok, true);
|
||||
RootedTypedArray<ArrayBuffer> data(autoCx);
|
||||
data.Init(&val.toObject());
|
||||
Optional<uint32_t> byteLength(buffer.Length());
|
||||
mSocket->SendWithTrackingNumber(autoCx, data, 0, byteLength, aTrackingNumber, rv);
|
||||
break;
|
||||
}
|
||||
|
||||
case SendableData::TnsCString: {
|
||||
const nsCString& strData = aData.get_nsCString();
|
||||
mSocket->SendWithTrackingNumber(strData, aTrackingNumber, rv);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
MOZ_CRASH("unexpected SendableData type");
|
||||
}
|
||||
NS_ENSURE_SUCCESS(rv.StealNSResult(), true);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
TCPSocketParent::RecvClose()
|
||||
{
|
||||
NS_ENSURE_TRUE(mSocket, true);
|
||||
mSocket->Close();
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
TCPSocketParent::FireErrorEvent(const nsAString& aName, const nsAString& aType, TCPReadyState aReadyState)
|
||||
{
|
||||
SendEvent(NS_LITERAL_STRING("error"), TCPError(nsString(aName), nsString(aType)), aReadyState);
|
||||
}
|
||||
|
||||
void
|
||||
TCPSocketParent::FireEvent(const nsAString& aType, TCPReadyState aReadyState)
|
||||
{
|
||||
return SendEvent(aType, mozilla::void_t(), aReadyState);
|
||||
}
|
||||
|
||||
void
|
||||
TCPSocketParent::FireArrayBufferDataEvent(nsTArray<uint8_t>& aBuffer, TCPReadyState aReadyState)
|
||||
{
|
||||
InfallibleTArray<uint8_t> arr;
|
||||
arr.SwapElements(aBuffer);
|
||||
|
||||
if (mFilter) {
|
||||
bool allowed;
|
||||
mozilla::net::NetAddr addr;
|
||||
nsresult nsrv = mFilter->FilterPacket(&addr, arr.Elements(), arr.Length(),
|
||||
nsISocketFilter::SF_INCOMING,
|
||||
&allowed);
|
||||
// receiving unallowed data, drop it.
|
||||
if (NS_WARN_IF(NS_FAILED(nsrv)) || !allowed) {
|
||||
TCPSOCKET_LOG(("%s: Dropping incoming TCP packet", __FUNCTION__));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
SendableData data(arr);
|
||||
SendEvent(NS_LITERAL_STRING("data"), data, aReadyState);
|
||||
}
|
||||
|
||||
void
|
||||
TCPSocketParent::FireStringDataEvent(const nsACString& aData, TCPReadyState aReadyState)
|
||||
{
|
||||
SendableData data((nsCString(aData)));
|
||||
|
||||
MOZ_ASSERT(!mFilter, "Socket filtering doesn't support nsCString");
|
||||
|
||||
SendEvent(NS_LITERAL_STRING("data"), data, aReadyState);
|
||||
}
|
||||
|
||||
void
|
||||
TCPSocketParent::SendEvent(const nsAString& aType, CallbackData aData, TCPReadyState aReadyState)
|
||||
{
|
||||
if (mIPCOpen) {
|
||||
mozilla::Unused << PTCPSocketParent::SendCallback(nsString(aType),
|
||||
aData,
|
||||
static_cast<uint32_t>(aReadyState));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TCPSocketParent::SetSocket(TCPSocket *socket)
|
||||
{
|
||||
mSocket = socket;
|
||||
}
|
||||
|
||||
nsresult
|
||||
TCPSocketParent::GetHost(nsAString& aHost)
|
||||
{
|
||||
if (!mSocket) {
|
||||
NS_ERROR("No internal socket instance mSocket!");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
mSocket->GetHost(aHost);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
TCPSocketParent::GetPort(uint16_t* aPort)
|
||||
{
|
||||
if (!mSocket) {
|
||||
NS_ERROR("No internal socket instance mSocket!");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
*aPort = mSocket->Port();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
TCPSocketParent::ActorDestroy(ActorDestroyReason why)
|
||||
{
|
||||
if (mSocket) {
|
||||
mSocket->Close();
|
||||
}
|
||||
mSocket = nullptr;
|
||||
}
|
||||
|
||||
bool
|
||||
TCPSocketParent::RecvRequestDelete()
|
||||
{
|
||||
mozilla::Unused << Send__delete__(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
91
dom/network/TCPSocketParent.h
Normal file
91
dom/network/TCPSocketParent.h
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_TCPSocketParent_h
|
||||
#define mozilla_dom_TCPSocketParent_h
|
||||
|
||||
#include "mozilla/dom/TCPSocketBinding.h"
|
||||
#include "mozilla/net/PTCPSocketParent.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsISocketFilter.h"
|
||||
#include "js/TypeDecls.h"
|
||||
|
||||
#define TCPSOCKETPARENT_CID \
|
||||
{ 0x4e7246c6, 0xa8b3, 0x426d, { 0x9c, 0x17, 0x76, 0xda, 0xb1, 0xe1, 0xe1, 0x4a } }
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class TCPSocket;
|
||||
|
||||
class TCPSocketParentBase : public nsISupports
|
||||
{
|
||||
public:
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS(TCPSocketParentBase)
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
|
||||
void AddIPDLReference();
|
||||
void ReleaseIPDLReference();
|
||||
|
||||
protected:
|
||||
TCPSocketParentBase();
|
||||
virtual ~TCPSocketParentBase();
|
||||
|
||||
RefPtr<TCPSocket> mSocket;
|
||||
bool mIPCOpen;
|
||||
};
|
||||
|
||||
class TCPSocketParent : public mozilla::net::PTCPSocketParent
|
||||
, public TCPSocketParentBase
|
||||
{
|
||||
public:
|
||||
NS_IMETHOD_(MozExternalRefCountType) Release() override;
|
||||
|
||||
TCPSocketParent() {}
|
||||
|
||||
virtual bool RecvOpen(const nsString& aHost, const uint16_t& aPort,
|
||||
const bool& useSSL, const bool& aUseArrayBuffers) override;
|
||||
|
||||
virtual bool RecvOpenBind(const nsCString& aRemoteHost,
|
||||
const uint16_t& aRemotePort,
|
||||
const nsCString& aLocalAddr,
|
||||
const uint16_t& aLocalPort,
|
||||
const bool& aUseSSL,
|
||||
const bool& aUseArrayBuffers,
|
||||
const nsCString& aFilter) override;
|
||||
|
||||
virtual bool RecvStartTLS() override;
|
||||
virtual bool RecvSuspend() override;
|
||||
virtual bool RecvResume() override;
|
||||
virtual bool RecvClose() override;
|
||||
virtual bool RecvData(const SendableData& aData,
|
||||
const uint32_t& aTrackingNumber) override;
|
||||
virtual bool RecvRequestDelete() override;
|
||||
bool GetInIsolatedMozBrowser();
|
||||
|
||||
void FireErrorEvent(const nsAString& aName, const nsAString& aType, TCPReadyState aReadyState);
|
||||
void FireEvent(const nsAString& aType, TCPReadyState aReadyState);
|
||||
void FireArrayBufferDataEvent(nsTArray<uint8_t>& aBuffer, TCPReadyState aReadyState);
|
||||
void FireStringDataEvent(const nsACString& aData, TCPReadyState aReadyState);
|
||||
|
||||
void SetSocket(TCPSocket *socket);
|
||||
nsresult GetHost(nsAString& aHost);
|
||||
nsresult GetPort(uint16_t* aPort);
|
||||
|
||||
private:
|
||||
virtual uint32_t GetAppId();
|
||||
virtual void ActorDestroy(ActorDestroyReason why) override;
|
||||
void SendEvent(const nsAString& aType, CallbackData aData, TCPReadyState aReadyState);
|
||||
nsresult SetFilter(const nsCString& aFilter);
|
||||
|
||||
nsCOMPtr<nsISocketFilter> mFilter;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
22
dom/network/Types.h
Normal file
22
dom/network/Types.h
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_network_Types_h
|
||||
#define mozilla_dom_network_Types_h
|
||||
|
||||
namespace mozilla {
|
||||
namespace hal {
|
||||
class NetworkInformation;
|
||||
} // namespace hal
|
||||
|
||||
template <class T>
|
||||
class Observer;
|
||||
|
||||
typedef Observer<hal::NetworkInformation> NetworkObserver;
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_network_Types_h
|
||||
762
dom/network/UDPSocket.cpp
Normal file
762
dom/network/UDPSocket.cpp
Normal file
|
|
@ -0,0 +1,762 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* 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 "UDPSocket.h"
|
||||
#include "mozilla/AsyncEventDispatcher.h"
|
||||
#include "mozilla/dom/File.h"
|
||||
#include "mozilla/dom/ErrorEvent.h"
|
||||
#include "mozilla/dom/UDPMessageEvent.h"
|
||||
#include "mozilla/dom/UDPSocketBinding.h"
|
||||
#include "mozilla/dom/UnionTypes.h"
|
||||
#include "mozilla/net/DNS.h"
|
||||
#include "nsComponentManagerUtils.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsINetAddr.h"
|
||||
#include "nsStringStream.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
NS_IMPL_ISUPPORTS(UDPSocket::ListenerProxy,
|
||||
nsIUDPSocketListener,
|
||||
nsIUDPSocketInternal)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(UDPSocket)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(UDPSocket, DOMEventTargetHelper)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOpened)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mClosed)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(UDPSocket, DOMEventTargetHelper)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mOpened)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mClosed)
|
||||
tmp->CloseWithReason(NS_OK);
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
NS_IMPL_ADDREF_INHERITED(UDPSocket, DOMEventTargetHelper)
|
||||
NS_IMPL_RELEASE_INHERITED(UDPSocket, DOMEventTargetHelper)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(UDPSocket)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIUDPSocketListener)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIUDPSocketInternal)
|
||||
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
|
||||
|
||||
/* static */ already_AddRefed<UDPSocket>
|
||||
UDPSocket::Constructor(const GlobalObject& aGlobal,
|
||||
const UDPOptions& aOptions,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
nsCOMPtr<nsPIDOMWindowInner> ownerWindow = do_QueryInterface(aGlobal.GetAsSupports());
|
||||
if (!ownerWindow) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool addressReuse = aOptions.mAddressReuse;
|
||||
bool loopback = aOptions.mLoopback;
|
||||
|
||||
nsCString remoteAddress;
|
||||
if (aOptions.mRemoteAddress.WasPassed()) {
|
||||
remoteAddress = NS_ConvertUTF16toUTF8(aOptions.mRemoteAddress.Value());
|
||||
} else {
|
||||
remoteAddress.SetIsVoid(true);
|
||||
}
|
||||
|
||||
Nullable<uint16_t> remotePort;
|
||||
if (aOptions.mRemotePort.WasPassed()) {
|
||||
remotePort.SetValue(aOptions.mRemotePort.Value());
|
||||
|
||||
if (remotePort.Value() == 0) {
|
||||
aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
nsString localAddress;
|
||||
if (aOptions.mLocalAddress.WasPassed()) {
|
||||
localAddress = aOptions.mLocalAddress.Value();
|
||||
|
||||
// check if localAddress is a valid IPv4/6 address
|
||||
NS_ConvertUTF16toUTF8 address(localAddress);
|
||||
PRNetAddr prAddr;
|
||||
PRStatus status = PR_StringToNetAddr(address.BeginReading(), &prAddr);
|
||||
if (status != PR_SUCCESS) {
|
||||
aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
|
||||
return nullptr;
|
||||
}
|
||||
} else {
|
||||
SetDOMStringToNull(localAddress);
|
||||
}
|
||||
|
||||
Nullable<uint16_t> localPort;
|
||||
if (aOptions.mLocalPort.WasPassed()) {
|
||||
localPort.SetValue(aOptions.mLocalPort.Value());
|
||||
|
||||
if (localPort.Value() == 0) {
|
||||
aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
RefPtr<UDPSocket> socket = new UDPSocket(ownerWindow, remoteAddress, remotePort);
|
||||
aRv = socket->Init(localAddress, localPort, addressReuse, loopback);
|
||||
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return socket.forget();
|
||||
}
|
||||
|
||||
UDPSocket::UDPSocket(nsPIDOMWindowInner* aOwner,
|
||||
const nsCString& aRemoteAddress,
|
||||
const Nullable<uint16_t>& aRemotePort)
|
||||
: DOMEventTargetHelper(aOwner)
|
||||
, mRemoteAddress(aRemoteAddress)
|
||||
, mRemotePort(aRemotePort)
|
||||
, mAddressReuse(false)
|
||||
, mLoopback(false)
|
||||
, mReadyState(SocketReadyState::Opening)
|
||||
{
|
||||
MOZ_ASSERT(aOwner);
|
||||
MOZ_ASSERT(aOwner->IsInnerWindow());
|
||||
|
||||
nsIDocument* aDoc = aOwner->GetExtantDoc();
|
||||
if (aDoc) {
|
||||
aDoc->DisallowBFCaching();
|
||||
}
|
||||
}
|
||||
|
||||
UDPSocket::~UDPSocket()
|
||||
{
|
||||
CloseWithReason(NS_OK);
|
||||
}
|
||||
|
||||
JSObject*
|
||||
UDPSocket::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return UDPSocketBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
void
|
||||
UDPSocket::DisconnectFromOwner()
|
||||
{
|
||||
DOMEventTargetHelper::DisconnectFromOwner();
|
||||
CloseWithReason(NS_OK);
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
UDPSocket::Close()
|
||||
{
|
||||
MOZ_ASSERT(mClosed);
|
||||
|
||||
RefPtr<Promise> promise = mClosed;
|
||||
|
||||
if (mReadyState == SocketReadyState::Closed) {
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
CloseWithReason(NS_OK);
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
void
|
||||
UDPSocket::CloseWithReason(nsresult aReason)
|
||||
{
|
||||
if (mReadyState == SocketReadyState::Closed) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mOpened) {
|
||||
if (mReadyState == SocketReadyState::Opening) {
|
||||
// reject openedPromise with AbortError if socket is closed without error
|
||||
nsresult openFailedReason = NS_FAILED(aReason) ? aReason : NS_ERROR_DOM_ABORT_ERR;
|
||||
mOpened->MaybeReject(openFailedReason);
|
||||
}
|
||||
}
|
||||
|
||||
mReadyState = SocketReadyState::Closed;
|
||||
|
||||
if (mListenerProxy) {
|
||||
mListenerProxy->Disconnect();
|
||||
mListenerProxy = nullptr;
|
||||
}
|
||||
|
||||
if (mSocket) {
|
||||
mSocket->Close();
|
||||
mSocket = nullptr;
|
||||
}
|
||||
|
||||
if (mSocketChild) {
|
||||
mSocketChild->Close();
|
||||
mSocketChild = nullptr;
|
||||
}
|
||||
|
||||
if (mClosed) {
|
||||
if (NS_SUCCEEDED(aReason)) {
|
||||
mClosed->MaybeResolveWithUndefined();
|
||||
} else {
|
||||
mClosed->MaybeReject(aReason);
|
||||
}
|
||||
}
|
||||
|
||||
mPendingMcastCommands.Clear();
|
||||
}
|
||||
|
||||
void
|
||||
UDPSocket::JoinMulticastGroup(const nsAString& aMulticastGroupAddress,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
if (mReadyState == SocketReadyState::Closed) {
|
||||
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mReadyState == SocketReadyState::Opening) {
|
||||
MulticastCommand joinCommand(MulticastCommand::Join, aMulticastGroupAddress);
|
||||
mPendingMcastCommands.AppendElement(joinCommand);
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mSocket || mSocketChild);
|
||||
|
||||
NS_ConvertUTF16toUTF8 address(aMulticastGroupAddress);
|
||||
|
||||
if (mSocket) {
|
||||
MOZ_ASSERT(!mSocketChild);
|
||||
|
||||
aRv = mSocket->JoinMulticast(address, EmptyCString());
|
||||
NS_WARNING_ASSERTION(!aRv.Failed(), "JoinMulticast failed");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mSocketChild);
|
||||
|
||||
aRv = mSocketChild->JoinMulticast(address, EmptyCString());
|
||||
NS_WARNING_ASSERTION(!aRv.Failed(), "JoinMulticast failed");
|
||||
}
|
||||
|
||||
void
|
||||
UDPSocket::LeaveMulticastGroup(const nsAString& aMulticastGroupAddress,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
if (mReadyState == SocketReadyState::Closed) {
|
||||
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mReadyState == SocketReadyState::Opening) {
|
||||
MulticastCommand leaveCommand(MulticastCommand::Leave, aMulticastGroupAddress);
|
||||
mPendingMcastCommands.AppendElement(leaveCommand);
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mSocket || mSocketChild);
|
||||
|
||||
nsCString address = NS_ConvertUTF16toUTF8(aMulticastGroupAddress);
|
||||
if (mSocket) {
|
||||
MOZ_ASSERT(!mSocketChild);
|
||||
|
||||
aRv = mSocket->LeaveMulticast(address, EmptyCString());
|
||||
NS_WARNING_ASSERTION(!aRv.Failed(), "mSocket->LeaveMulticast failed");
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mSocketChild);
|
||||
|
||||
aRv = mSocketChild->LeaveMulticast(address, EmptyCString());
|
||||
NS_WARNING_ASSERTION(!aRv.Failed(), "mSocketChild->LeaveMulticast failed");
|
||||
}
|
||||
|
||||
nsresult
|
||||
UDPSocket::DoPendingMcastCommand()
|
||||
{
|
||||
MOZ_ASSERT(mReadyState == SocketReadyState::Open, "Multicast command can only be executed after socket opened");
|
||||
|
||||
for (uint32_t i = 0; i < mPendingMcastCommands.Length(); ++i) {
|
||||
MulticastCommand& command = mPendingMcastCommands[i];
|
||||
ErrorResult rv;
|
||||
|
||||
switch (command.mCommand) {
|
||||
case MulticastCommand::Join: {
|
||||
JoinMulticastGroup(command.mAddress, rv);
|
||||
break;
|
||||
}
|
||||
case MulticastCommand::Leave: {
|
||||
LeaveMulticastGroup(command.mAddress, rv);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(rv.Failed())) {
|
||||
return rv.StealNSResult();
|
||||
}
|
||||
}
|
||||
|
||||
mPendingMcastCommands.Clear();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
UDPSocket::Send(const StringOrBlobOrArrayBufferOrArrayBufferView& aData,
|
||||
const Optional<nsAString>& aRemoteAddress,
|
||||
const Optional<Nullable<uint16_t>>& aRemotePort,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
if (mReadyState != SocketReadyState::Open) {
|
||||
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return false;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mSocket || mSocketChild);
|
||||
|
||||
// If the remote address and port were not specified in the constructor or as arguments,
|
||||
// throw InvalidAccessError.
|
||||
nsCString remoteAddress;
|
||||
if (aRemoteAddress.WasPassed()) {
|
||||
remoteAddress = NS_ConvertUTF16toUTF8(aRemoteAddress.Value());
|
||||
UDPSOCKET_LOG(("%s: Send to %s", __FUNCTION__, remoteAddress.get()));
|
||||
} else if (!mRemoteAddress.IsVoid()) {
|
||||
remoteAddress = mRemoteAddress;
|
||||
UDPSOCKET_LOG(("%s: Send to %s", __FUNCTION__, remoteAddress.get()));
|
||||
} else {
|
||||
aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
|
||||
return false;
|
||||
}
|
||||
|
||||
uint16_t remotePort;
|
||||
if (aRemotePort.WasPassed() && !aRemotePort.Value().IsNull()) {
|
||||
remotePort = aRemotePort.Value().Value();
|
||||
} else if (!mRemotePort.IsNull()) {
|
||||
remotePort = mRemotePort.Value();
|
||||
} else {
|
||||
aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
|
||||
return false;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIInputStream> stream;
|
||||
if (aData.IsBlob()) {
|
||||
Blob& blob = aData.GetAsBlob();
|
||||
|
||||
blob.GetInternalStream(getter_AddRefs(stream), aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIStringInputStream> strStream = do_CreateInstance(NS_STRINGINPUTSTREAM_CONTRACTID, &rv);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
aRv.Throw(rv);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (aData.IsString()) {
|
||||
NS_ConvertUTF16toUTF8 data(aData.GetAsString());
|
||||
aRv = strStream->SetData(data.BeginReading(), data.Length());
|
||||
} else if (aData.IsArrayBuffer()) {
|
||||
const ArrayBuffer& data = aData.GetAsArrayBuffer();
|
||||
data.ComputeLengthAndData();
|
||||
aRv = strStream->SetData(reinterpret_cast<const char*>(data.Data()), data.Length());
|
||||
} else {
|
||||
const ArrayBufferView& data = aData.GetAsArrayBufferView();
|
||||
data.ComputeLengthAndData();
|
||||
aRv = strStream->SetData(reinterpret_cast<const char*>(data.Data()), data.Length());
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
stream = strStream;
|
||||
}
|
||||
|
||||
if (mSocket) {
|
||||
aRv = mSocket->SendBinaryStream(remoteAddress, remotePort, stream);
|
||||
} else if (mSocketChild) {
|
||||
aRv = mSocketChild->SendBinaryStream(remoteAddress, remotePort, stream);
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
nsresult
|
||||
UDPSocket::InitLocal(const nsAString& aLocalAddress,
|
||||
const uint16_t& aLocalPort)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
nsCOMPtr<nsIUDPSocket> sock =
|
||||
do_CreateInstance("@mozilla.org/network/udp-socket;1", &rv);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetOwner(), &rv);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIPrincipal> principal = global->PrincipalOrNull();
|
||||
if (!principal) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (aLocalAddress.IsEmpty()) {
|
||||
rv = sock->Init(aLocalPort, /* loopback = */ false, principal,
|
||||
mAddressReuse, /* optionalArgc = */ 1);
|
||||
} else {
|
||||
PRNetAddr prAddr;
|
||||
PR_InitializeNetAddr(PR_IpAddrAny, aLocalPort, &prAddr);
|
||||
PR_StringToNetAddr(NS_ConvertUTF16toUTF8(aLocalAddress).BeginReading(), &prAddr);
|
||||
UDPSOCKET_LOG(("%s: %s:%u", __FUNCTION__, NS_ConvertUTF16toUTF8(aLocalAddress).get(), aLocalPort));
|
||||
|
||||
mozilla::net::NetAddr addr;
|
||||
PRNetAddrToNetAddr(&prAddr, &addr);
|
||||
rv = sock->InitWithAddress(&addr, principal, mAddressReuse,
|
||||
/* optionalArgc = */ 1);
|
||||
}
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = sock->SetMulticastLoopback(mLoopback);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
mSocket = sock;
|
||||
|
||||
// Get real local address and local port
|
||||
nsCOMPtr<nsINetAddr> localAddr;
|
||||
rv = mSocket->GetLocalAddr(getter_AddRefs(localAddr));
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsCString localAddress;
|
||||
rv = localAddr->GetAddress(localAddress);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
mLocalAddress = NS_ConvertUTF8toUTF16(localAddress);
|
||||
|
||||
uint16_t localPort;
|
||||
rv = localAddr->GetPort(&localPort);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
mLocalPort.SetValue(localPort);
|
||||
|
||||
mListenerProxy = new ListenerProxy(this);
|
||||
|
||||
rv = mSocket->AsyncListen(mListenerProxy);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
mReadyState = SocketReadyState::Open;
|
||||
rv = DoPendingMcastCommand();
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
mOpened->MaybeResolveWithUndefined();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
UDPSocket::InitRemote(const nsAString& aLocalAddress,
|
||||
const uint16_t& aLocalPort)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
nsCOMPtr<nsIUDPSocketChild> sock =
|
||||
do_CreateInstance("@mozilla.org/udp-socket-child;1", &rv);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
mListenerProxy = new ListenerProxy(this);
|
||||
|
||||
nsCOMPtr<nsIGlobalObject> obj = do_QueryInterface(GetOwner(), &rv);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIPrincipal> principal = obj->PrincipalOrNull();
|
||||
if (!principal) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
rv = sock->Bind(mListenerProxy,
|
||||
principal,
|
||||
NS_ConvertUTF16toUTF8(aLocalAddress),
|
||||
aLocalPort,
|
||||
mAddressReuse,
|
||||
mLoopback,
|
||||
0,
|
||||
0);
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
mSocketChild = sock;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
UDPSocket::Init(const nsString& aLocalAddress,
|
||||
const Nullable<uint16_t>& aLocalPort,
|
||||
const bool& aAddressReuse,
|
||||
const bool& aLoopback)
|
||||
{
|
||||
MOZ_ASSERT(!mSocket && !mSocketChild);
|
||||
|
||||
mLocalAddress = aLocalAddress;
|
||||
mLocalPort = aLocalPort;
|
||||
mAddressReuse = aAddressReuse;
|
||||
mLoopback = aLoopback;
|
||||
|
||||
ErrorResult rv;
|
||||
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetOwner());
|
||||
|
||||
mOpened = Promise::Create(global, rv);
|
||||
if (NS_WARN_IF(rv.Failed())) {
|
||||
return rv.StealNSResult();
|
||||
}
|
||||
|
||||
mClosed = Promise::Create(global, rv);
|
||||
if (NS_WARN_IF(rv.Failed())) {
|
||||
return rv.StealNSResult();
|
||||
}
|
||||
|
||||
class OpenSocketRunnable final : public Runnable
|
||||
{
|
||||
public:
|
||||
explicit OpenSocketRunnable(UDPSocket* aSocket) : mSocket(aSocket)
|
||||
{ }
|
||||
|
||||
NS_IMETHOD Run() override
|
||||
{
|
||||
MOZ_ASSERT(mSocket);
|
||||
|
||||
if (mSocket->mReadyState != SocketReadyState::Opening) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
uint16_t localPort = 0;
|
||||
if (!mSocket->mLocalPort.IsNull()) {
|
||||
localPort = mSocket->mLocalPort.Value();
|
||||
}
|
||||
|
||||
nsresult rv;
|
||||
if (!XRE_IsParentProcess()) {
|
||||
rv = mSocket->InitRemote(mSocket->mLocalAddress, localPort);
|
||||
} else {
|
||||
rv = mSocket->InitLocal(mSocket->mLocalAddress, localPort);
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
mSocket->CloseWithReason(NS_ERROR_DOM_NETWORK_ERR);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
RefPtr<UDPSocket> mSocket;
|
||||
};
|
||||
|
||||
nsCOMPtr<nsIRunnable> runnable = new OpenSocketRunnable(this);
|
||||
|
||||
return NS_DispatchToMainThread(runnable);
|
||||
}
|
||||
|
||||
void
|
||||
UDPSocket::HandleReceivedData(const nsACString& aRemoteAddress,
|
||||
const uint16_t& aRemotePort,
|
||||
const uint8_t* aData,
|
||||
const uint32_t& aDataLength)
|
||||
{
|
||||
if (mReadyState != SocketReadyState::Open) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (NS_FAILED(CheckInnerWindowCorrectness())) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (NS_FAILED(DispatchReceivedData(aRemoteAddress, aRemotePort, aData, aDataLength))) {
|
||||
CloseWithReason(NS_ERROR_TYPE_ERR);
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
UDPSocket::DispatchReceivedData(const nsACString& aRemoteAddress,
|
||||
const uint16_t& aRemotePort,
|
||||
const uint8_t* aData,
|
||||
const uint32_t& aDataLength)
|
||||
{
|
||||
AutoJSAPI jsapi;
|
||||
|
||||
if (NS_WARN_IF(!jsapi.Init(GetOwner()))) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
JSContext* cx = jsapi.cx();
|
||||
|
||||
// Copy packet data to ArrayBuffer
|
||||
JS::Rooted<JSObject*> arrayBuf(cx, ArrayBuffer::Create(cx, aDataLength, aData));
|
||||
|
||||
if (NS_WARN_IF(!arrayBuf)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
JS::Rooted<JS::Value> jsData(cx, JS::ObjectValue(*arrayBuf));
|
||||
|
||||
// Create DOM event
|
||||
RootedDictionary<UDPMessageEventInit> init(cx);
|
||||
init.mRemoteAddress = NS_ConvertUTF8toUTF16(aRemoteAddress);
|
||||
init.mRemotePort = aRemotePort;
|
||||
init.mData = jsData;
|
||||
|
||||
RefPtr<UDPMessageEvent> udpEvent =
|
||||
UDPMessageEvent::Constructor(this, NS_LITERAL_STRING("message"), init);
|
||||
|
||||
if (NS_WARN_IF(!udpEvent)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
udpEvent->SetTrusted(true);
|
||||
|
||||
RefPtr<AsyncEventDispatcher> asyncDispatcher = new AsyncEventDispatcher(this, udpEvent);
|
||||
|
||||
return asyncDispatcher->PostDOMEvent();
|
||||
}
|
||||
|
||||
// nsIUDPSocketListener
|
||||
|
||||
NS_IMETHODIMP
|
||||
UDPSocket::OnPacketReceived(nsIUDPSocket* aSocket, nsIUDPMessage* aMessage)
|
||||
{
|
||||
// nsIUDPSocketListener callbacks should be invoked on main thread.
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Not running on main thread");
|
||||
|
||||
// Create appropriate JS object for message
|
||||
FallibleTArray<uint8_t>& buffer = aMessage->GetDataAsTArray();
|
||||
|
||||
nsCOMPtr<nsINetAddr> addr;
|
||||
if (NS_WARN_IF(NS_FAILED(aMessage->GetFromAddr(getter_AddRefs(addr))))) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCString remoteAddress;
|
||||
if (NS_WARN_IF(NS_FAILED(addr->GetAddress(remoteAddress)))) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
uint16_t remotePort;
|
||||
if (NS_WARN_IF(NS_FAILED(addr->GetPort(&remotePort)))) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
HandleReceivedData(remoteAddress, remotePort, buffer.Elements(), buffer.Length());
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
UDPSocket::OnStopListening(nsIUDPSocket* aSocket, nsresult aStatus)
|
||||
{
|
||||
// nsIUDPSocketListener callbacks should be invoked on main thread.
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Not running on main thread");
|
||||
|
||||
CloseWithReason(aStatus);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// nsIUDPSocketInternal
|
||||
|
||||
NS_IMETHODIMP
|
||||
UDPSocket::CallListenerError(const nsACString& aMessage,
|
||||
const nsACString& aFilename,
|
||||
uint32_t aLineNumber)
|
||||
{
|
||||
CloseWithReason(NS_ERROR_DOM_NETWORK_ERR);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
UDPSocket::CallListenerReceivedData(const nsACString& aRemoteAddress,
|
||||
uint16_t aRemotePort,
|
||||
const uint8_t* aData,
|
||||
uint32_t aDataLength)
|
||||
{
|
||||
HandleReceivedData(aRemoteAddress, aRemotePort, aData, aDataLength);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
UDPSocket::CallListenerOpened()
|
||||
{
|
||||
if (mReadyState != SocketReadyState::Opening) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mSocketChild);
|
||||
|
||||
// Get real local address and local port
|
||||
nsCString localAddress;
|
||||
mSocketChild->GetLocalAddress(localAddress);
|
||||
mLocalAddress = NS_ConvertUTF8toUTF16(localAddress);
|
||||
|
||||
uint16_t localPort;
|
||||
mSocketChild->GetLocalPort(&localPort);
|
||||
mLocalPort.SetValue(localPort);
|
||||
|
||||
mReadyState = SocketReadyState::Open;
|
||||
nsresult rv = DoPendingMcastCommand();
|
||||
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
CloseWithReason(rv);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
mOpened->MaybeResolveWithUndefined();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
UDPSocket::CallListenerConnected()
|
||||
{
|
||||
// This shouldn't be called here.
|
||||
MOZ_CRASH();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
UDPSocket::CallListenerClosed()
|
||||
{
|
||||
CloseWithReason(NS_OK);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
232
dom/network/UDPSocket.h
Normal file
232
dom/network/UDPSocket.h
Normal file
|
|
@ -0,0 +1,232 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_UDPSocket_h__
|
||||
#define mozilla_dom_UDPSocket_h__
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/DOMEventTargetHelper.h"
|
||||
#include "mozilla/ErrorResult.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozilla/dom/SocketCommonBinding.h"
|
||||
#include "nsIUDPSocket.h"
|
||||
#include "nsIUDPSocketChild.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
struct JSContext;
|
||||
|
||||
//
|
||||
// set MOZ_LOG=UDPSocket:5
|
||||
//
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
extern LazyLogModule gUDPSocketLog;
|
||||
#define UDPSOCKET_LOG(args) MOZ_LOG(gUDPSocketLog, LogLevel::Debug, args)
|
||||
#define UDPSOCKET_LOG_ENABLED() MOZ_LOG_TEST(gUDPSocketLog, LogLevel::Debug)
|
||||
} // namespace net
|
||||
|
||||
namespace dom {
|
||||
|
||||
struct UDPOptions;
|
||||
class StringOrBlobOrArrayBufferOrArrayBufferView;
|
||||
|
||||
class UDPSocket final : public DOMEventTargetHelper
|
||||
, public nsIUDPSocketListener
|
||||
, public nsIUDPSocketInternal
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(UDPSocket, DOMEventTargetHelper)
|
||||
NS_DECL_NSIUDPSOCKETLISTENER
|
||||
NS_DECL_NSIUDPSOCKETINTERNAL
|
||||
NS_REALLY_FORWARD_NSIDOMEVENTTARGET(DOMEventTargetHelper)
|
||||
|
||||
public:
|
||||
nsPIDOMWindowInner*
|
||||
GetParentObject() const
|
||||
{
|
||||
return GetOwner();
|
||||
}
|
||||
|
||||
virtual JSObject*
|
||||
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
virtual void
|
||||
DisconnectFromOwner() override;
|
||||
|
||||
static already_AddRefed<UDPSocket>
|
||||
Constructor(const GlobalObject& aGlobal, const UDPOptions& aOptions, ErrorResult& aRv);
|
||||
|
||||
void
|
||||
GetLocalAddress(nsString& aRetVal) const
|
||||
{
|
||||
aRetVal = mLocalAddress;
|
||||
}
|
||||
|
||||
Nullable<uint16_t>
|
||||
GetLocalPort() const
|
||||
{
|
||||
return mLocalPort;
|
||||
}
|
||||
|
||||
void
|
||||
GetRemoteAddress(nsString& aRetVal) const
|
||||
{
|
||||
if (mRemoteAddress.IsVoid()) {
|
||||
SetDOMStringToNull(aRetVal);
|
||||
return;
|
||||
}
|
||||
|
||||
aRetVal = NS_ConvertUTF8toUTF16(mRemoteAddress);
|
||||
}
|
||||
|
||||
Nullable<uint16_t>
|
||||
GetRemotePort() const
|
||||
{
|
||||
return mRemotePort;
|
||||
}
|
||||
|
||||
bool
|
||||
AddressReuse() const
|
||||
{
|
||||
return mAddressReuse;
|
||||
}
|
||||
|
||||
bool
|
||||
Loopback() const
|
||||
{
|
||||
return mLoopback;
|
||||
}
|
||||
|
||||
SocketReadyState
|
||||
ReadyState() const
|
||||
{
|
||||
return mReadyState;
|
||||
}
|
||||
|
||||
Promise*
|
||||
Opened() const
|
||||
{
|
||||
return mOpened;
|
||||
}
|
||||
|
||||
Promise*
|
||||
Closed() const
|
||||
{
|
||||
return mClosed;
|
||||
}
|
||||
|
||||
IMPL_EVENT_HANDLER(message)
|
||||
|
||||
already_AddRefed<Promise>
|
||||
Close();
|
||||
|
||||
void
|
||||
JoinMulticastGroup(const nsAString& aMulticastGroupAddress, ErrorResult& aRv);
|
||||
|
||||
void
|
||||
LeaveMulticastGroup(const nsAString& aMulticastGroupAddress, ErrorResult& aRv);
|
||||
|
||||
bool
|
||||
Send(const StringOrBlobOrArrayBufferOrArrayBufferView& aData,
|
||||
const Optional<nsAString>& aRemoteAddress,
|
||||
const Optional<Nullable<uint16_t>>& aRemotePort,
|
||||
ErrorResult& aRv);
|
||||
|
||||
private:
|
||||
class ListenerProxy : public nsIUDPSocketListener
|
||||
, public nsIUDPSocketInternal
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_FORWARD_SAFE_NSIUDPSOCKETLISTENER(mSocket)
|
||||
NS_FORWARD_SAFE_NSIUDPSOCKETINTERNAL(mSocket)
|
||||
|
||||
explicit ListenerProxy(UDPSocket* aSocket)
|
||||
: mSocket(aSocket)
|
||||
{
|
||||
}
|
||||
|
||||
void Disconnect()
|
||||
{
|
||||
mSocket = nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
virtual ~ListenerProxy() {}
|
||||
|
||||
UDPSocket* mSocket;
|
||||
};
|
||||
|
||||
UDPSocket(nsPIDOMWindowInner* aOwner,
|
||||
const nsCString& aRemoteAddress,
|
||||
const Nullable<uint16_t>& aRemotePort);
|
||||
|
||||
virtual ~UDPSocket();
|
||||
|
||||
nsresult
|
||||
Init(const nsString& aLocalAddress,
|
||||
const Nullable<uint16_t>& aLocalPort,
|
||||
const bool& aAddressReuse,
|
||||
const bool& aLoopback);
|
||||
|
||||
nsresult
|
||||
InitLocal(const nsAString& aLocalAddress, const uint16_t& aLocalPort);
|
||||
|
||||
nsresult
|
||||
InitRemote(const nsAString& aLocalAddress, const uint16_t& aLocalPort);
|
||||
|
||||
void
|
||||
HandleReceivedData(const nsACString& aRemoteAddress,
|
||||
const uint16_t& aRemotePort,
|
||||
const uint8_t* aData,
|
||||
const uint32_t& aDataLength);
|
||||
|
||||
nsresult
|
||||
DispatchReceivedData(const nsACString& aRemoteAddress,
|
||||
const uint16_t& aRemotePort,
|
||||
const uint8_t* aData,
|
||||
const uint32_t& aDataLength);
|
||||
|
||||
void
|
||||
CloseWithReason(nsresult aReason);
|
||||
|
||||
nsresult
|
||||
DoPendingMcastCommand();
|
||||
|
||||
nsString mLocalAddress;
|
||||
Nullable<uint16_t> mLocalPort;
|
||||
nsCString mRemoteAddress;
|
||||
Nullable<uint16_t> mRemotePort;
|
||||
bool mAddressReuse;
|
||||
bool mLoopback;
|
||||
SocketReadyState mReadyState;
|
||||
RefPtr<Promise> mOpened;
|
||||
RefPtr<Promise> mClosed;
|
||||
|
||||
nsCOMPtr<nsIUDPSocket> mSocket;
|
||||
nsCOMPtr<nsIUDPSocketChild> mSocketChild;
|
||||
RefPtr<ListenerProxy> mListenerProxy;
|
||||
|
||||
struct MulticastCommand {
|
||||
enum CommandType { Join, Leave };
|
||||
|
||||
MulticastCommand(CommandType aCommand, const nsAString& aAddress)
|
||||
: mCommand(aCommand), mAddress(aAddress)
|
||||
{ }
|
||||
|
||||
CommandType mCommand;
|
||||
nsString mAddress;
|
||||
};
|
||||
|
||||
nsTArray<MulticastCommand> mPendingMcastCommands;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_UDPSocket_h__
|
||||
404
dom/network/UDPSocketChild.cpp
Normal file
404
dom/network/UDPSocketChild.cpp
Normal file
|
|
@ -0,0 +1,404 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* 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 "UDPSocketChild.h"
|
||||
#include "mozilla/Unused.h"
|
||||
#include "mozilla/ipc/InputStreamUtils.h"
|
||||
#include "mozilla/net/NeckoChild.h"
|
||||
#include "mozilla/dom/PermissionMessageUtils.h"
|
||||
#include "mozilla/ipc/BackgroundChild.h"
|
||||
#include "mozilla/ipc/PBackgroundChild.h"
|
||||
#include "mozilla/ipc/BackgroundUtils.h"
|
||||
#include "mozilla/ipc/PBackgroundSharedTypes.h"
|
||||
#include "nsIIPCBackgroundChildCreateCallback.h"
|
||||
|
||||
using mozilla::net::gNeckoChild;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
NS_IMPL_ISUPPORTS(UDPSocketChildBase, nsIUDPSocketChild)
|
||||
|
||||
UDPSocketChildBase::UDPSocketChildBase()
|
||||
: mIPCOpen(false)
|
||||
{
|
||||
}
|
||||
|
||||
UDPSocketChildBase::~UDPSocketChildBase()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
UDPSocketChildBase::ReleaseIPDLReference()
|
||||
{
|
||||
MOZ_ASSERT(mIPCOpen);
|
||||
mIPCOpen = false;
|
||||
mSocket = nullptr;
|
||||
this->Release();
|
||||
}
|
||||
|
||||
void
|
||||
UDPSocketChildBase::AddIPDLReference()
|
||||
{
|
||||
MOZ_ASSERT(!mIPCOpen);
|
||||
mIPCOpen = true;
|
||||
this->AddRef();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP_(MozExternalRefCountType) UDPSocketChild::Release(void)
|
||||
{
|
||||
nsrefcnt refcnt = UDPSocketChildBase::Release();
|
||||
if (refcnt == 1 && mIPCOpen) {
|
||||
PUDPSocketChild::SendRequestDelete();
|
||||
return 1;
|
||||
}
|
||||
return refcnt;
|
||||
}
|
||||
|
||||
UDPSocketChild::UDPSocketChild()
|
||||
:mBackgroundManager(nullptr)
|
||||
,mLocalPort(0)
|
||||
{
|
||||
}
|
||||
|
||||
UDPSocketChild::~UDPSocketChild()
|
||||
{
|
||||
}
|
||||
|
||||
class UDPSocketBackgroundChildCallback final :
|
||||
public nsIIPCBackgroundChildCreateCallback
|
||||
{
|
||||
bool* mDone;
|
||||
|
||||
public:
|
||||
explicit UDPSocketBackgroundChildCallback(bool* aDone)
|
||||
: mDone(aDone)
|
||||
{
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
MOZ_ASSERT(mDone);
|
||||
MOZ_ASSERT(!*mDone);
|
||||
}
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
private:
|
||||
~UDPSocketBackgroundChildCallback()
|
||||
{ }
|
||||
|
||||
virtual void
|
||||
ActorCreated(PBackgroundChild* aActor) override
|
||||
{
|
||||
*mDone = true;
|
||||
}
|
||||
|
||||
virtual void
|
||||
ActorFailed() override
|
||||
{
|
||||
*mDone = true;
|
||||
}
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(UDPSocketBackgroundChildCallback, nsIIPCBackgroundChildCreateCallback)
|
||||
|
||||
nsresult
|
||||
UDPSocketChild::CreatePBackgroundSpinUntilDone()
|
||||
{
|
||||
using mozilla::ipc::BackgroundChild;
|
||||
|
||||
// Spinning the event loop in MainThread would be dangerous
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
MOZ_ASSERT(!BackgroundChild::GetForCurrentThread());
|
||||
|
||||
bool done = false;
|
||||
nsCOMPtr<nsIIPCBackgroundChildCreateCallback> callback =
|
||||
new UDPSocketBackgroundChildCallback(&done);
|
||||
|
||||
if (NS_WARN_IF(!BackgroundChild::GetOrCreateForCurrentThread(callback))) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsIThread* thread = NS_GetCurrentThread();
|
||||
while (!done) {
|
||||
if (NS_WARN_IF(!NS_ProcessNextEvent(thread, true /* aMayWait */))) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(!BackgroundChild::GetForCurrentThread())) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// nsIUDPSocketChild Methods
|
||||
|
||||
NS_IMETHODIMP
|
||||
UDPSocketChild::SetBackgroundSpinsEvents()
|
||||
{
|
||||
using mozilla::ipc::BackgroundChild;
|
||||
|
||||
PBackgroundChild* existingBackgroundChild =
|
||||
BackgroundChild::GetForCurrentThread();
|
||||
// If it's not spun up yet, block until it is, and retry
|
||||
if (!existingBackgroundChild) {
|
||||
nsresult rv = CreatePBackgroundSpinUntilDone();
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
existingBackgroundChild =
|
||||
BackgroundChild::GetForCurrentThread();
|
||||
MOZ_ASSERT(existingBackgroundChild);
|
||||
}
|
||||
// By now PBackground is guaranteed to be/have-been up
|
||||
mBackgroundManager = existingBackgroundChild;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
UDPSocketChild::Bind(nsIUDPSocketInternal* aSocket,
|
||||
nsIPrincipal* aPrincipal,
|
||||
const nsACString& aHost,
|
||||
uint16_t aPort,
|
||||
bool aAddressReuse,
|
||||
bool aLoopback,
|
||||
uint32_t recvBufferSize,
|
||||
uint32_t sendBufferSize)
|
||||
{
|
||||
UDPSOCKET_LOG(("%s: %s:%u", __FUNCTION__, PromiseFlatCString(aHost).get(), aPort));
|
||||
|
||||
NS_ENSURE_ARG(aSocket);
|
||||
|
||||
mSocket = aSocket;
|
||||
AddIPDLReference();
|
||||
|
||||
if (mBackgroundManager) {
|
||||
// If we want to support a passed-in principal here we'd need to
|
||||
// convert it to a PrincipalInfo
|
||||
MOZ_ASSERT(!aPrincipal);
|
||||
mBackgroundManager->SendPUDPSocketConstructor(this, void_t(), mFilterName);
|
||||
} else {
|
||||
gNeckoChild->SendPUDPSocketConstructor(this, IPC::Principal(aPrincipal),
|
||||
mFilterName);
|
||||
}
|
||||
|
||||
SendBind(UDPAddressInfo(nsCString(aHost), aPort), aAddressReuse, aLoopback,
|
||||
recvBufferSize, sendBufferSize);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
UDPSocketChild::Connect(nsIUDPSocketInternal* aSocket, const nsACString & aHost, uint16_t aPort)
|
||||
{
|
||||
UDPSOCKET_LOG(("%s: %s:%u", __FUNCTION__, PromiseFlatCString(aHost).get(), aPort));
|
||||
|
||||
mSocket = aSocket;
|
||||
|
||||
SendConnect(UDPAddressInfo(nsCString(aHost), aPort));
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
UDPSocketChild::Close()
|
||||
{
|
||||
SendClose();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
UDPSocketChild::Send(const nsACString& aHost,
|
||||
uint16_t aPort,
|
||||
const uint8_t* aData,
|
||||
uint32_t aByteLength)
|
||||
{
|
||||
NS_ENSURE_ARG(aData);
|
||||
|
||||
UDPSOCKET_LOG(("%s: %s:%u - %u bytes", __FUNCTION__, PromiseFlatCString(aHost).get(), aPort, aByteLength));
|
||||
return SendDataInternal(UDPSocketAddr(UDPAddressInfo(nsCString(aHost), aPort)),
|
||||
aData, aByteLength);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
UDPSocketChild::SendWithAddr(nsINetAddr* aAddr,
|
||||
const uint8_t* aData,
|
||||
uint32_t aByteLength)
|
||||
{
|
||||
NS_ENSURE_ARG(aAddr);
|
||||
NS_ENSURE_ARG(aData);
|
||||
|
||||
NetAddr addr;
|
||||
aAddr->GetNetAddr(&addr);
|
||||
|
||||
UDPSOCKET_LOG(("%s: %u bytes", __FUNCTION__, aByteLength));
|
||||
return SendDataInternal(UDPSocketAddr(addr), aData, aByteLength);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
UDPSocketChild::SendWithAddress(const NetAddr* aAddr,
|
||||
const uint8_t* aData,
|
||||
uint32_t aByteLength)
|
||||
{
|
||||
NS_ENSURE_ARG(aAddr);
|
||||
NS_ENSURE_ARG(aData);
|
||||
|
||||
UDPSOCKET_LOG(("%s: %u bytes", __FUNCTION__, aByteLength));
|
||||
return SendDataInternal(UDPSocketAddr(*aAddr), aData, aByteLength);
|
||||
}
|
||||
|
||||
nsresult
|
||||
UDPSocketChild::SendDataInternal(const UDPSocketAddr& aAddr,
|
||||
const uint8_t* aData,
|
||||
const uint32_t aByteLength)
|
||||
{
|
||||
NS_ENSURE_ARG(aData);
|
||||
|
||||
FallibleTArray<uint8_t> fallibleArray;
|
||||
if (!fallibleArray.InsertElementsAt(0, aData, aByteLength, fallible)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
InfallibleTArray<uint8_t> array;
|
||||
array.SwapElements(fallibleArray);
|
||||
|
||||
SendOutgoingData(array, aAddr);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
UDPSocketChild::SendBinaryStream(const nsACString& aHost,
|
||||
uint16_t aPort,
|
||||
nsIInputStream* aStream)
|
||||
{
|
||||
NS_ENSURE_ARG(aStream);
|
||||
|
||||
OptionalInputStreamParams stream;
|
||||
nsTArray<mozilla::ipc::FileDescriptor> fds;
|
||||
SerializeInputStream(aStream, stream, fds);
|
||||
|
||||
MOZ_ASSERT(fds.IsEmpty());
|
||||
|
||||
UDPSOCKET_LOG(("%s: %s:%u", __FUNCTION__, PromiseFlatCString(aHost).get(), aPort));
|
||||
SendOutgoingData(UDPData(stream), UDPSocketAddr(UDPAddressInfo(nsCString(aHost), aPort)));
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
UDPSocketChild::JoinMulticast(const nsACString& aMulticastAddress,
|
||||
const nsACString& aInterface)
|
||||
{
|
||||
SendJoinMulticast(nsCString(aMulticastAddress), nsCString(aInterface));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
UDPSocketChild::LeaveMulticast(const nsACString& aMulticastAddress,
|
||||
const nsACString& aInterface)
|
||||
{
|
||||
SendLeaveMulticast(nsCString(aMulticastAddress), nsCString(aInterface));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
UDPSocketChild::GetLocalPort(uint16_t* aLocalPort)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aLocalPort);
|
||||
|
||||
*aLocalPort = mLocalPort;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
UDPSocketChild::GetLocalAddress(nsACString& aLocalAddress)
|
||||
{
|
||||
aLocalAddress = mLocalAddress;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
UDPSocketChild::SetFilterName(const nsACString& aFilterName)
|
||||
{
|
||||
if (!mFilterName.IsEmpty()) {
|
||||
// filter name can only be set once.
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
mFilterName = aFilterName;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
UDPSocketChild::GetFilterName(nsACString& aFilterName)
|
||||
{
|
||||
aFilterName = mFilterName;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// PUDPSocketChild Methods
|
||||
bool
|
||||
UDPSocketChild::RecvCallbackOpened(const UDPAddressInfo& aAddressInfo)
|
||||
{
|
||||
mLocalAddress = aAddressInfo.addr();
|
||||
mLocalPort = aAddressInfo.port();
|
||||
|
||||
UDPSOCKET_LOG(("%s: %s:%u", __FUNCTION__, mLocalAddress.get(), mLocalPort));
|
||||
nsresult rv = mSocket->CallListenerOpened();
|
||||
mozilla::Unused << NS_WARN_IF(NS_FAILED(rv));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// PUDPSocketChild Methods
|
||||
bool
|
||||
UDPSocketChild::RecvCallbackConnected(const UDPAddressInfo& aAddressInfo)
|
||||
{
|
||||
mLocalAddress = aAddressInfo.addr();
|
||||
mLocalPort = aAddressInfo.port();
|
||||
|
||||
UDPSOCKET_LOG(("%s: %s:%u", __FUNCTION__, mLocalAddress.get(), mLocalPort));
|
||||
nsresult rv = mSocket->CallListenerConnected();
|
||||
mozilla::Unused << NS_WARN_IF(NS_FAILED(rv));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
UDPSocketChild::RecvCallbackClosed()
|
||||
{
|
||||
nsresult rv = mSocket->CallListenerClosed();
|
||||
mozilla::Unused << NS_WARN_IF(NS_FAILED(rv));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
UDPSocketChild::RecvCallbackReceivedData(const UDPAddressInfo& aAddressInfo,
|
||||
InfallibleTArray<uint8_t>&& aData)
|
||||
{
|
||||
UDPSOCKET_LOG(("%s: %s:%u length %u", __FUNCTION__,
|
||||
aAddressInfo.addr().get(), aAddressInfo.port(), aData.Length()));
|
||||
nsresult rv = mSocket->CallListenerReceivedData(aAddressInfo.addr(), aAddressInfo.port(),
|
||||
aData.Elements(), aData.Length());
|
||||
mozilla::Unused << NS_WARN_IF(NS_FAILED(rv));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
UDPSocketChild::RecvCallbackError(const nsCString& aMessage,
|
||||
const nsCString& aFilename,
|
||||
const uint32_t& aLineNumber)
|
||||
{
|
||||
UDPSOCKET_LOG(("%s: %s:%s:%u", __FUNCTION__, aMessage.get(), aFilename.get(), aLineNumber));
|
||||
nsresult rv = mSocket->CallListenerError(aMessage, aFilename, aLineNumber);
|
||||
mozilla::Unused << NS_WARN_IF(NS_FAILED(rv));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
70
dom/network/UDPSocketChild.h
Normal file
70
dom/network/UDPSocketChild.h
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_UDPSocketChild_h__
|
||||
#define mozilla_dom_UDPSocketChild_h__
|
||||
|
||||
#include "mozilla/net/PUDPSocketChild.h"
|
||||
#include "nsIUDPSocketChild.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsCOMPtr.h"
|
||||
|
||||
#define UDPSOCKETCHILD_CID \
|
||||
{0xb47e5a0f, 0xd384, 0x48ef, { 0x88, 0x85, 0x42, 0x59, 0x79, 0x3d, 0x9c, 0xf0 }}
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class UDPSocketChildBase : public nsIUDPSocketChild {
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
void AddIPDLReference();
|
||||
void ReleaseIPDLReference();
|
||||
|
||||
protected:
|
||||
UDPSocketChildBase();
|
||||
virtual ~UDPSocketChildBase();
|
||||
nsCOMPtr<nsIUDPSocketInternal> mSocket;
|
||||
bool mIPCOpen;
|
||||
};
|
||||
|
||||
class UDPSocketChild : public mozilla::net::PUDPSocketChild
|
||||
, public UDPSocketChildBase
|
||||
{
|
||||
public:
|
||||
NS_DECL_NSIUDPSOCKETCHILD
|
||||
NS_IMETHOD_(MozExternalRefCountType) Release() override;
|
||||
|
||||
UDPSocketChild();
|
||||
virtual ~UDPSocketChild();
|
||||
|
||||
nsresult CreatePBackgroundSpinUntilDone();
|
||||
|
||||
virtual bool RecvCallbackOpened(const UDPAddressInfo& aAddressInfo) override;
|
||||
virtual bool RecvCallbackConnected(const UDPAddressInfo& aAddressInfo) override;
|
||||
virtual bool RecvCallbackClosed() override;
|
||||
virtual bool RecvCallbackReceivedData(const UDPAddressInfo& aAddressInfo,
|
||||
InfallibleTArray<uint8_t>&& aData) override;
|
||||
virtual bool RecvCallbackError(const nsCString& aMessage,
|
||||
const nsCString& aFilename,
|
||||
const uint32_t& aLineNumber) override;
|
||||
|
||||
private:
|
||||
nsresult SendDataInternal(const UDPSocketAddr& aAddr,
|
||||
const uint8_t* aData,
|
||||
const uint32_t aByteLength);
|
||||
|
||||
mozilla::ipc::PBackgroundChild* mBackgroundManager;
|
||||
uint16_t mLocalPort;
|
||||
nsCString mLocalAddress;
|
||||
nsCString mFilterName;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // !defined(mozilla_dom_UDPSocketChild_h__)
|
||||
606
dom/network/UDPSocketParent.cpp
Normal file
606
dom/network/UDPSocketParent.cpp
Normal file
|
|
@ -0,0 +1,606 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* 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 "nsIServiceManager.h"
|
||||
#include "UDPSocketParent.h"
|
||||
#include "nsComponentManagerUtils.h"
|
||||
#include "nsIUDPSocket.h"
|
||||
#include "nsINetAddr.h"
|
||||
#include "mozilla/AppProcessChecker.h"
|
||||
#include "mozilla/Unused.h"
|
||||
#include "mozilla/ipc/InputStreamUtils.h"
|
||||
#include "mozilla/net/DNS.h"
|
||||
#include "mozilla/net/NeckoCommon.h"
|
||||
#include "mozilla/net/PNeckoParent.h"
|
||||
#include "nsIPermissionManager.h"
|
||||
#include "nsIScriptSecurityManager.h"
|
||||
#include "mozilla/ipc/PBackgroundParent.h"
|
||||
#include "mtransport/runnable_utils.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
NS_IMPL_ISUPPORTS(UDPSocketParent, nsIUDPSocketListener)
|
||||
|
||||
UDPSocketParent::UDPSocketParent(PBackgroundParent* aManager)
|
||||
: mBackgroundManager(aManager)
|
||||
, mNeckoManager(nullptr)
|
||||
, mIPCOpen(true)
|
||||
{
|
||||
}
|
||||
|
||||
UDPSocketParent::UDPSocketParent(PNeckoParent* aManager)
|
||||
: mBackgroundManager(nullptr)
|
||||
, mNeckoManager(aManager)
|
||||
, mIPCOpen(true)
|
||||
{
|
||||
}
|
||||
|
||||
UDPSocketParent::~UDPSocketParent()
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
UDPSocketParent::Init(const IPC::Principal& aPrincipal,
|
||||
const nsACString& aFilter)
|
||||
{
|
||||
MOZ_ASSERT_IF(mBackgroundManager, !aPrincipal);
|
||||
// will be used once we move all UDPSocket to PBackground, or
|
||||
// if we add in Principal checking for mtransport
|
||||
Unused << mBackgroundManager;
|
||||
|
||||
mPrincipal = aPrincipal;
|
||||
if (net::UsingNeckoIPCSecurity() &&
|
||||
mPrincipal &&
|
||||
!ContentParent::IgnoreIPCPrincipal()) {
|
||||
if (mNeckoManager) {
|
||||
if (!AssertAppPrincipal(mNeckoManager->Manager(), mPrincipal)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// PBackground is (for now) using a STUN filter for verification
|
||||
// it's not being used for DoS
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIPermissionManager> permMgr =
|
||||
services::GetPermissionManager();
|
||||
if (!permMgr) {
|
||||
NS_WARNING("No PermissionManager available!");
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t permission = nsIPermissionManager::DENY_ACTION;
|
||||
permMgr->TestExactPermissionFromPrincipal(mPrincipal, "udp-socket",
|
||||
&permission);
|
||||
if (permission != nsIPermissionManager::ALLOW_ACTION) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!aFilter.IsEmpty()) {
|
||||
nsAutoCString contractId(NS_NETWORK_UDP_SOCKET_FILTER_HANDLER_PREFIX);
|
||||
contractId.Append(aFilter);
|
||||
nsCOMPtr<nsISocketFilterHandler> filterHandler =
|
||||
do_GetService(contractId.get());
|
||||
if (filterHandler) {
|
||||
nsresult rv = filterHandler->NewFilter(getter_AddRefs(mFilter));
|
||||
if (NS_FAILED(rv)) {
|
||||
printf_stderr("Cannot create filter that content specified. "
|
||||
"filter name: %s, error code: %u.", aFilter.BeginReading(), static_cast<uint32_t>(rv));
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
printf_stderr("Content doesn't have a valid filter. "
|
||||
"filter name: %s.", aFilter.BeginReading());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// We don't have browser actors in xpcshell, and hence can't run automated
|
||||
// tests without this loophole.
|
||||
if (net::UsingNeckoIPCSecurity() && !mFilter &&
|
||||
(!mPrincipal || ContentParent::IgnoreIPCPrincipal())) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// PUDPSocketParent methods
|
||||
|
||||
bool
|
||||
UDPSocketParent::RecvBind(const UDPAddressInfo& aAddressInfo,
|
||||
const bool& aAddressReuse, const bool& aLoopback,
|
||||
const uint32_t& recvBufferSize,
|
||||
const uint32_t& sendBufferSize)
|
||||
{
|
||||
UDPSOCKET_LOG(("%s: %s:%u", __FUNCTION__, aAddressInfo.addr().get(), aAddressInfo.port()));
|
||||
|
||||
if (NS_FAILED(BindInternal(aAddressInfo.addr(), aAddressInfo.port(),
|
||||
aAddressReuse, aLoopback, recvBufferSize,
|
||||
sendBufferSize))) {
|
||||
FireInternalError(__LINE__);
|
||||
return true;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsINetAddr> localAddr;
|
||||
mSocket->GetLocalAddr(getter_AddRefs(localAddr));
|
||||
|
||||
nsCString addr;
|
||||
if (NS_FAILED(localAddr->GetAddress(addr))) {
|
||||
FireInternalError(__LINE__);
|
||||
return true;
|
||||
}
|
||||
|
||||
uint16_t port;
|
||||
if (NS_FAILED(localAddr->GetPort(&port))) {
|
||||
FireInternalError(__LINE__);
|
||||
return true;
|
||||
}
|
||||
|
||||
UDPSOCKET_LOG(("%s: SendCallbackOpened: %s:%u", __FUNCTION__, addr.get(), port));
|
||||
mozilla::Unused << SendCallbackOpened(UDPAddressInfo(addr, port));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
nsresult
|
||||
UDPSocketParent::BindInternal(const nsCString& aHost, const uint16_t& aPort,
|
||||
const bool& aAddressReuse, const bool& aLoopback,
|
||||
const uint32_t& recvBufferSize,
|
||||
const uint32_t& sendBufferSize)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
UDPSOCKET_LOG(("%s: [this=%p] %s:%u addressReuse: %d loopback: %d recvBufferSize: %lu, sendBufferSize: %lu",
|
||||
__FUNCTION__, this, nsCString(aHost).get(), aPort,
|
||||
aAddressReuse, aLoopback, recvBufferSize, sendBufferSize));
|
||||
|
||||
nsCOMPtr<nsIUDPSocket> sock =
|
||||
do_CreateInstance("@mozilla.org/network/udp-socket;1", &rv);
|
||||
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (aHost.IsEmpty()) {
|
||||
rv = sock->Init(aPort, false, mPrincipal, aAddressReuse,
|
||||
/* optional_argc = */ 1);
|
||||
} else {
|
||||
PRNetAddr prAddr;
|
||||
PR_InitializeNetAddr(PR_IpAddrAny, aPort, &prAddr);
|
||||
PRStatus status = PR_StringToNetAddr(aHost.BeginReading(), &prAddr);
|
||||
if (status != PR_SUCCESS) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
mozilla::net::NetAddr addr;
|
||||
PRNetAddrToNetAddr(&prAddr, &addr);
|
||||
rv = sock->InitWithAddress(&addr, mPrincipal, aAddressReuse,
|
||||
/* optional_argc = */ 1);
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsINetAddr> laddr;
|
||||
rv = sock->GetLocalAddr(getter_AddRefs(laddr));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
uint16_t family;
|
||||
rv = laddr->GetFamily(&family);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
if (family == nsINetAddr::FAMILY_INET) {
|
||||
rv = sock->SetMulticastLoopback(aLoopback);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
// TODO: once bug 1252759 is fixed query buffer first and only increase
|
||||
if (recvBufferSize != 0) {
|
||||
rv = sock->SetRecvBufferSize(recvBufferSize);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
UDPSOCKET_LOG(("%s: [this=%p] %s:%u failed to set recv buffer size to: %lu", __FUNCTION__, this, nsCString(aHost).get(), aPort, recvBufferSize));
|
||||
}
|
||||
}
|
||||
if (sendBufferSize != 0) {
|
||||
rv = sock->SetSendBufferSize(sendBufferSize);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
UDPSOCKET_LOG(("%s: [this=%p] %s:%u failed to set send buffer size to: %lu", __FUNCTION__, this, nsCString(aHost).get(), aPort, sendBufferSize));
|
||||
}
|
||||
}
|
||||
|
||||
// register listener
|
||||
rv = sock->AsyncListen(this);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
mSocket = sock;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
static nsCOMPtr<nsIEventTarget> GetSTSThread()
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
nsCOMPtr<nsIEventTarget> sts_thread;
|
||||
|
||||
sts_thread = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
|
||||
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
||||
|
||||
return sts_thread;
|
||||
}
|
||||
|
||||
static void CheckSTSThread()
|
||||
{
|
||||
DebugOnly<nsCOMPtr<nsIEventTarget>> sts_thread = GetSTSThread();
|
||||
|
||||
ASSERT_ON_THREAD(sts_thread.value);
|
||||
}
|
||||
|
||||
|
||||
// Proxy the Connect() request to the STS thread, since it may block and
|
||||
// should be done there.
|
||||
bool
|
||||
UDPSocketParent::RecvConnect(const UDPAddressInfo& aAddressInfo)
|
||||
{
|
||||
nsCOMPtr<nsIEventTarget> thread(NS_GetCurrentThread());
|
||||
Unused <<
|
||||
NS_WARN_IF(NS_FAILED(GetSTSThread()->Dispatch(WrapRunnable(
|
||||
RefPtr<UDPSocketParent>(this),
|
||||
&UDPSocketParent::DoConnect,
|
||||
mSocket,
|
||||
thread,
|
||||
aAddressInfo),
|
||||
NS_DISPATCH_NORMAL)));
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
UDPSocketParent::DoSendConnectResponse(const UDPAddressInfo& aAddressInfo)
|
||||
{
|
||||
// can't use directly with WrapRunnable due to warnings
|
||||
mozilla::Unused << SendCallbackConnected(aAddressInfo);
|
||||
}
|
||||
|
||||
void
|
||||
UDPSocketParent::SendConnectResponse(nsIEventTarget *aThread,
|
||||
const UDPAddressInfo& aAddressInfo)
|
||||
{
|
||||
Unused <<
|
||||
NS_WARN_IF(NS_FAILED(aThread->Dispatch(WrapRunnable(
|
||||
RefPtr<UDPSocketParent>(this),
|
||||
&UDPSocketParent::DoSendConnectResponse,
|
||||
aAddressInfo),
|
||||
NS_DISPATCH_NORMAL)));
|
||||
}
|
||||
|
||||
// Runs on STS thread
|
||||
void
|
||||
UDPSocketParent::DoConnect(nsCOMPtr<nsIUDPSocket>& aSocket,
|
||||
nsCOMPtr<nsIEventTarget>& aReturnThread,
|
||||
const UDPAddressInfo& aAddressInfo)
|
||||
{
|
||||
UDPSOCKET_LOG(("%s: %s:%u", __FUNCTION__, aAddressInfo.addr().get(), aAddressInfo.port()));
|
||||
if (NS_FAILED(ConnectInternal(aAddressInfo.addr(), aAddressInfo.port()))) {
|
||||
SendInternalError(aReturnThread, __LINE__);
|
||||
return;
|
||||
}
|
||||
CheckSTSThread();
|
||||
|
||||
nsCOMPtr<nsINetAddr> localAddr;
|
||||
aSocket->GetLocalAddr(getter_AddRefs(localAddr));
|
||||
|
||||
nsCString addr;
|
||||
if (NS_FAILED(localAddr->GetAddress(addr))) {
|
||||
SendInternalError(aReturnThread, __LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
uint16_t port;
|
||||
if (NS_FAILED(localAddr->GetPort(&port))) {
|
||||
SendInternalError(aReturnThread, __LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
UDPSOCKET_LOG(("%s: SendConnectResponse: %s:%u", __FUNCTION__, addr.get(), port));
|
||||
SendConnectResponse(aReturnThread, UDPAddressInfo(addr, port));
|
||||
}
|
||||
|
||||
nsresult
|
||||
UDPSocketParent::ConnectInternal(const nsCString& aHost, const uint16_t& aPort)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
UDPSOCKET_LOG(("%s: %s:%u", __FUNCTION__, nsCString(aHost).get(), aPort));
|
||||
|
||||
if (!mSocket) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
PRNetAddr prAddr;
|
||||
PR_InitializeNetAddr(PR_IpAddrAny, aPort, &prAddr);
|
||||
PRStatus status = PR_StringToNetAddr(aHost.BeginReading(), &prAddr);
|
||||
if (status != PR_SUCCESS) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
mozilla::net::NetAddr addr;
|
||||
PRNetAddrToNetAddr(&prAddr, &addr);
|
||||
|
||||
rv = mSocket->Connect(&addr);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
UDPSocketParent::RecvOutgoingData(const UDPData& aData,
|
||||
const UDPSocketAddr& aAddr)
|
||||
{
|
||||
if (!mSocket) {
|
||||
NS_WARNING("sending socket is closed");
|
||||
FireInternalError(__LINE__);
|
||||
return true;
|
||||
}
|
||||
|
||||
nsresult rv;
|
||||
if (mFilter) {
|
||||
if (aAddr.type() != UDPSocketAddr::TNetAddr) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO, Packet filter doesn't support input stream yet.
|
||||
if (aData.type() != UDPData::TArrayOfuint8_t) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool allowed;
|
||||
const InfallibleTArray<uint8_t>& data(aData.get_ArrayOfuint8_t());
|
||||
rv = mFilter->FilterPacket(&aAddr.get_NetAddr(), data.Elements(),
|
||||
data.Length(), nsISocketFilter::SF_OUTGOING,
|
||||
&allowed);
|
||||
|
||||
// Sending unallowed data, kill content.
|
||||
if (NS_WARN_IF(NS_FAILED(rv)) || !allowed) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
switch(aData.type()) {
|
||||
case UDPData::TArrayOfuint8_t:
|
||||
Send(aData.get_ArrayOfuint8_t(), aAddr);
|
||||
break;
|
||||
case UDPData::TInputStreamParams:
|
||||
Send(aData.get_InputStreamParams(), aAddr);
|
||||
break;
|
||||
default:
|
||||
MOZ_ASSERT(false, "Invalid data type!");
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
UDPSocketParent::Send(const InfallibleTArray<uint8_t>& aData,
|
||||
const UDPSocketAddr& aAddr)
|
||||
{
|
||||
nsresult rv;
|
||||
uint32_t count;
|
||||
switch(aAddr.type()) {
|
||||
case UDPSocketAddr::TUDPAddressInfo: {
|
||||
const UDPAddressInfo& addrInfo(aAddr.get_UDPAddressInfo());
|
||||
rv = mSocket->Send(addrInfo.addr(), addrInfo.port(),
|
||||
aData.Elements(), aData.Length(), &count);
|
||||
break;
|
||||
}
|
||||
case UDPSocketAddr::TNetAddr: {
|
||||
const NetAddr& addr(aAddr.get_NetAddr());
|
||||
rv = mSocket->SendWithAddress(&addr, aData.Elements(),
|
||||
aData.Length(), &count);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
MOZ_ASSERT(false, "Invalid address type!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(NS_FAILED(rv)) || count == 0) {
|
||||
FireInternalError(__LINE__);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
UDPSocketParent::Send(const InputStreamParams& aStream,
|
||||
const UDPSocketAddr& aAddr)
|
||||
{
|
||||
nsTArray<mozilla::ipc::FileDescriptor> fds;
|
||||
nsCOMPtr<nsIInputStream> stream = DeserializeInputStream(aStream, fds);
|
||||
|
||||
if (NS_WARN_IF(!stream)) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsresult rv;
|
||||
switch(aAddr.type()) {
|
||||
case UDPSocketAddr::TUDPAddressInfo: {
|
||||
const UDPAddressInfo& addrInfo(aAddr.get_UDPAddressInfo());
|
||||
rv = mSocket->SendBinaryStream(addrInfo.addr(), addrInfo.port(), stream);
|
||||
break;
|
||||
}
|
||||
case UDPSocketAddr::TNetAddr: {
|
||||
const NetAddr& addr(aAddr.get_NetAddr());
|
||||
rv = mSocket->SendBinaryStreamWithAddress(&addr, stream);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
MOZ_ASSERT(false, "Invalid address type!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
FireInternalError(__LINE__);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
UDPSocketParent::RecvJoinMulticast(const nsCString& aMulticastAddress,
|
||||
const nsCString& aInterface)
|
||||
{
|
||||
nsresult rv = mSocket->JoinMulticast(aMulticastAddress, aInterface);
|
||||
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
FireInternalError(__LINE__);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
UDPSocketParent::RecvLeaveMulticast(const nsCString& aMulticastAddress,
|
||||
const nsCString& aInterface)
|
||||
{
|
||||
nsresult rv = mSocket->LeaveMulticast(aMulticastAddress, aInterface);
|
||||
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
FireInternalError(__LINE__);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
UDPSocketParent::RecvClose()
|
||||
{
|
||||
if (!mSocket) {
|
||||
return true;
|
||||
}
|
||||
|
||||
nsresult rv = mSocket->Close();
|
||||
mSocket = nullptr;
|
||||
|
||||
mozilla::Unused << NS_WARN_IF(NS_FAILED(rv));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
UDPSocketParent::RecvRequestDelete()
|
||||
{
|
||||
mozilla::Unused << Send__delete__(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
UDPSocketParent::ActorDestroy(ActorDestroyReason why)
|
||||
{
|
||||
MOZ_ASSERT(mIPCOpen);
|
||||
mIPCOpen = false;
|
||||
if (mSocket) {
|
||||
mSocket->Close();
|
||||
}
|
||||
mSocket = nullptr;
|
||||
}
|
||||
|
||||
// nsIUDPSocketListener
|
||||
|
||||
NS_IMETHODIMP
|
||||
UDPSocketParent::OnPacketReceived(nsIUDPSocket* aSocket, nsIUDPMessage* aMessage)
|
||||
{
|
||||
// receiving packet from remote host, forward the message content to child process
|
||||
if (!mIPCOpen) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
uint16_t port;
|
||||
nsCString ip;
|
||||
nsCOMPtr<nsINetAddr> fromAddr;
|
||||
aMessage->GetFromAddr(getter_AddRefs(fromAddr));
|
||||
fromAddr->GetPort(&port);
|
||||
fromAddr->GetAddress(ip);
|
||||
|
||||
nsCString data;
|
||||
aMessage->GetData(data);
|
||||
|
||||
const char* buffer = data.get();
|
||||
uint32_t len = data.Length();
|
||||
UDPSOCKET_LOG(("%s: %s:%u, length %u", __FUNCTION__, ip.get(), port, len));
|
||||
|
||||
if (mFilter) {
|
||||
bool allowed;
|
||||
mozilla::net::NetAddr addr;
|
||||
fromAddr->GetNetAddr(&addr);
|
||||
nsresult rv = mFilter->FilterPacket(&addr,
|
||||
(const uint8_t*)buffer, len,
|
||||
nsISocketFilter::SF_INCOMING,
|
||||
&allowed);
|
||||
// Receiving unallowed data, drop.
|
||||
if (NS_WARN_IF(NS_FAILED(rv)) || !allowed) {
|
||||
if (!allowed) {
|
||||
UDPSOCKET_LOG(("%s: not allowed", __FUNCTION__));
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
FallibleTArray<uint8_t> fallibleArray;
|
||||
if (!fallibleArray.InsertElementsAt(0, buffer, len, fallible)) {
|
||||
FireInternalError(__LINE__);
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
InfallibleTArray<uint8_t> infallibleArray;
|
||||
infallibleArray.SwapElements(fallibleArray);
|
||||
|
||||
// compose callback
|
||||
mozilla::Unused << SendCallbackReceivedData(UDPAddressInfo(ip, port), infallibleArray);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
UDPSocketParent::OnStopListening(nsIUDPSocket* aSocket, nsresult aStatus)
|
||||
{
|
||||
// underlying socket is dead, send state update to child process
|
||||
if (mIPCOpen) {
|
||||
mozilla::Unused << SendCallbackClosed();
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
UDPSocketParent::FireInternalError(uint32_t aLineNo)
|
||||
{
|
||||
if (!mIPCOpen) {
|
||||
return;
|
||||
}
|
||||
|
||||
mozilla::Unused << SendCallbackError(NS_LITERAL_CSTRING("Internal error"),
|
||||
NS_LITERAL_CSTRING(__FILE__), aLineNo);
|
||||
}
|
||||
|
||||
void
|
||||
UDPSocketParent::SendInternalError(nsIEventTarget *aThread,
|
||||
uint32_t aLineNo)
|
||||
{
|
||||
UDPSOCKET_LOG(("SendInternalError: %u", aLineNo));
|
||||
Unused <<
|
||||
NS_WARN_IF(NS_FAILED(aThread->Dispatch(WrapRunnable(
|
||||
RefPtr<UDPSocketParent>(this),
|
||||
&UDPSocketParent::FireInternalError,
|
||||
aLineNo),
|
||||
NS_DISPATCH_NORMAL)));
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
84
dom/network/UDPSocketParent.h
Normal file
84
dom/network/UDPSocketParent.h
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_UDPSocketParent_h__
|
||||
#define mozilla_dom_UDPSocketParent_h__
|
||||
|
||||
#include "mozilla/net/PUDPSocketParent.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIUDPSocket.h"
|
||||
#include "nsISocketFilter.h"
|
||||
#include "mozilla/dom/PermissionMessageUtils.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
class PNeckoParent;
|
||||
} // namespace net
|
||||
|
||||
namespace dom {
|
||||
|
||||
class UDPSocketParent : public mozilla::net::PUDPSocketParent
|
||||
, public nsIUDPSocketListener
|
||||
{
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSIUDPSOCKETLISTENER
|
||||
|
||||
explicit UDPSocketParent(PBackgroundParent* aManager);
|
||||
explicit UDPSocketParent(PNeckoParent* aManager);
|
||||
|
||||
bool Init(const IPC::Principal& aPrincipal, const nsACString& aFilter);
|
||||
|
||||
virtual bool RecvBind(const UDPAddressInfo& aAddressInfo,
|
||||
const bool& aAddressReuse, const bool& aLoopback,
|
||||
const uint32_t& recvBufferSize,
|
||||
const uint32_t& sendBufferSize) override;
|
||||
virtual bool RecvConnect(const UDPAddressInfo& aAddressInfo) override;
|
||||
void DoSendConnectResponse(const UDPAddressInfo& aAddressInfo);
|
||||
void SendConnectResponse(nsIEventTarget *aThread,
|
||||
const UDPAddressInfo& aAddressInfo);
|
||||
void DoConnect(nsCOMPtr<nsIUDPSocket>& aSocket,
|
||||
nsCOMPtr<nsIEventTarget>& aReturnThread,
|
||||
const UDPAddressInfo& aAddressInfo);
|
||||
|
||||
virtual bool RecvOutgoingData(const UDPData& aData, const UDPSocketAddr& aAddr) override;
|
||||
|
||||
virtual bool RecvClose() override;
|
||||
virtual bool RecvRequestDelete() override;
|
||||
virtual bool RecvJoinMulticast(const nsCString& aMulticastAddress,
|
||||
const nsCString& aInterface) override;
|
||||
virtual bool RecvLeaveMulticast(const nsCString& aMulticastAddress,
|
||||
const nsCString& aInterface) override;
|
||||
|
||||
private:
|
||||
virtual ~UDPSocketParent();
|
||||
|
||||
virtual void ActorDestroy(ActorDestroyReason why) override;
|
||||
void Send(const InfallibleTArray<uint8_t>& aData, const UDPSocketAddr& aAddr);
|
||||
void Send(const InputStreamParams& aStream, const UDPSocketAddr& aAddr);
|
||||
nsresult BindInternal(const nsCString& aHost, const uint16_t& aPort,
|
||||
const bool& aAddressReuse, const bool& aLoopback,
|
||||
const uint32_t& recvBufferSize,
|
||||
const uint32_t& sendBufferSize);
|
||||
nsresult ConnectInternal(const nsCString& aHost, const uint16_t& aPort);
|
||||
void FireInternalError(uint32_t aLineNo);
|
||||
void SendInternalError(nsIEventTarget *aThread,
|
||||
uint32_t aLineNo);
|
||||
|
||||
// One of these will be null and the other non-null.
|
||||
PBackgroundParent* mBackgroundManager;
|
||||
PNeckoParent* mNeckoManager;
|
||||
|
||||
bool mIPCOpen;
|
||||
nsCOMPtr<nsIUDPSocket> mSocket;
|
||||
nsCOMPtr<nsISocketFilter> mFilter;
|
||||
nsCOMPtr<nsIPrincipal> mPrincipal;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // !defined(mozilla_dom_UDPSocketParent_h__)
|
||||
19
dom/network/interfaces/moz.build
Normal file
19
dom/network/interfaces/moz.build
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
XPIDL_SOURCES += [
|
||||
'nsIMozNavigatorNetwork.idl',
|
||||
'nsITCPSocketCallback.idl',
|
||||
'nsIUDPSocketChild.idl',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
|
||||
XPIDL_SOURCES += [
|
||||
'nsIEthernetManager.idl',
|
||||
'nsINetworkStatsServiceProxy.idl',
|
||||
]
|
||||
|
||||
XPIDL_MODULE = 'dom_network'
|
||||
137
dom/network/interfaces/nsIEthernetManager.idl
Normal file
137
dom/network/interfaces/nsIEthernetManager.idl
Normal 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(81750c87-bb3b-4724-b955-834eafa53fd1)]
|
||||
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: DNS addresses.
|
||||
* .httpProxyHost: HTTP proxy host.
|
||||
* .httpProxyPort: HTTP proxy 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);
|
||||
};
|
||||
13
dom/network/interfaces/nsIMozNavigatorNetwork.idl
Normal file
13
dom/network/interfaces/nsIMozNavigatorNetwork.idl
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
/* 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"
|
||||
|
||||
interface nsINetworkProperties;
|
||||
|
||||
[uuid(7956523b-631e-4f80-94a5-3883bcfd6bf3)]
|
||||
interface nsIMozNavigatorNetwork : nsISupports
|
||||
{
|
||||
readonly attribute nsINetworkProperties properties;
|
||||
};
|
||||
64
dom/network/interfaces/nsINetworkStatsServiceProxy.idl
Normal file
64
dom/network/interfaces/nsINetworkStatsServiceProxy.idl
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
/* 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"
|
||||
|
||||
interface nsINetworkInfo;
|
||||
|
||||
[scriptable, function, uuid(5f821529-1d80-4ab5-a933-4e1b3585b6bc)]
|
||||
interface nsINetworkStatsServiceProxyCallback : nsISupports
|
||||
{
|
||||
/*
|
||||
* @param aResult callback result with boolean value
|
||||
* @param aMessage message
|
||||
*/
|
||||
void notify(in boolean aResult, in jsval aMessage);
|
||||
};
|
||||
|
||||
[scriptable, uuid(f4f3e901-e102-499d-9d37-dc9951f52df7)]
|
||||
interface nsINetworkStatsServiceProxy : nsISupports
|
||||
{
|
||||
/*
|
||||
* An interface used to record per-app traffic data.
|
||||
* @param aAppId app id
|
||||
* @param aIsInIsolatedMozBrowser
|
||||
* true if the frame is an isolated mozbrowser element. <iframe
|
||||
* mozbrowser mozapp> and <xul:browser> are not considered to be
|
||||
* mozbrowser elements. <iframe mozbrowser noisolation> does not count
|
||||
* as isolated since isolation is disabled. Isolation can only be
|
||||
* disabled if the containing document is chrome.
|
||||
* @param aNetworkInterface network
|
||||
* @param aTimeStamp time stamp
|
||||
* @param aRxBytes received data amount
|
||||
* @param aTxBytes transmitted data amount
|
||||
* @param aIsAccumulative is stats accumulative
|
||||
* @param aCallback an optional callback
|
||||
*/
|
||||
void saveAppStats(in unsigned long aAppId,
|
||||
in boolean aIsInIsolatedMozBrowser,
|
||||
in nsINetworkInfo aNetworkInfo,
|
||||
in unsigned long long aTimeStamp,
|
||||
in unsigned long long aRxBytes,
|
||||
in unsigned long long aTxBytes,
|
||||
in boolean aIsAccumulative,
|
||||
[optional] in nsINetworkStatsServiceProxyCallback aCallback);
|
||||
|
||||
/*
|
||||
* An interface used to record per-system service traffic data.
|
||||
* @param aServiceType system service type
|
||||
* @param aNetworkInterface network
|
||||
* @param aTimeStamp time stamp
|
||||
* @param aRxBytes received data amount
|
||||
* @param aTxBytes transmitted data amount
|
||||
* @param aIsAccumulative is stats accumulative
|
||||
* @param aCallback an optional callback
|
||||
*/
|
||||
void saveServiceStats(in string aServiceType,
|
||||
in nsINetworkInfo aNetworkInfo,
|
||||
in unsigned long long aTimeStamp,
|
||||
in unsigned long long aRxBytes,
|
||||
in unsigned long long aTxBytes,
|
||||
in boolean aIsAccumulative,
|
||||
[optional] in nsINetworkStatsServiceProxyCallback aCallback);
|
||||
};
|
||||
59
dom/network/interfaces/nsITCPSocketCallback.idl
Normal file
59
dom/network/interfaces/nsITCPSocketCallback.idl
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
/* 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/. */
|
||||
|
||||
/**
|
||||
* MozTCPSocket exposes a TCP client and server sockets
|
||||
* to highly privileged apps. It provides a buffered, non-blocking
|
||||
* interface for sending. For receiving, it uses an asynchronous,
|
||||
* event handler based interface.
|
||||
*/
|
||||
|
||||
#include "domstubs.idl"
|
||||
|
||||
%{C++
|
||||
#include "nsTArrayForwardDeclare.h"
|
||||
%}
|
||||
[ref] native nsUint8TArrayRef(InfallibleTArray<uint8_t>);
|
||||
[ptr] native JSContextPtr(JSContext);
|
||||
|
||||
|
||||
/*
|
||||
* This interface is implemented in TCPSocket.cpp as an internal interface
|
||||
* for use in cross-process socket implementation.
|
||||
* Needed to account for multiple possible types that can be provided to
|
||||
* the socket callbacks as arguments.
|
||||
*/
|
||||
[scriptable, uuid(ac2c4b69-cb79-4767-b1ce-bcf62945cd39)]
|
||||
interface nsITCPSocketCallback : nsISupports {
|
||||
// Limitation of TCPSocket's buffer size.
|
||||
const unsigned long BUFFER_SIZE = 65536;
|
||||
|
||||
// Dispatch an "error" event at this object with the given name and type.
|
||||
void fireErrorEvent(in AString name, in AString type);
|
||||
|
||||
// Dispatch a "data" event at this object with a string
|
||||
void fireDataStringEvent(in DOMString type, in ACString data);
|
||||
|
||||
// Dispatch a "data" event at this object with an Array
|
||||
void fireDataArrayEvent(in DOMString type, [const] in nsUint8TArrayRef data);
|
||||
|
||||
// Dispatch an event of the given type at this object.
|
||||
void fireEvent(in DOMString type);
|
||||
|
||||
// Update the DOM object's readyState.
|
||||
// @param readyState
|
||||
// new ready state
|
||||
void updateReadyState(in unsigned long readystate);
|
||||
|
||||
// Update the DOM object's bufferedAmount value with a tracking number to
|
||||
// to allow tracking of which writes are "in-flight"
|
||||
// @param bufferedAmount
|
||||
// TCPSocket parent's bufferedAmount.
|
||||
// @param trackingNumber
|
||||
// A number to ensure the bufferedAmount is updated after data
|
||||
// from child are sent to parent.
|
||||
void updateBufferedAmount(in uint32_t bufferedAmount,
|
||||
in uint32_t trackingNumber);
|
||||
};
|
||||
|
||||
78
dom/network/interfaces/nsIUDPSocketChild.idl
Normal file
78
dom/network/interfaces/nsIUDPSocketChild.idl
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
/* 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"
|
||||
#include "nsINetAddr.idl"
|
||||
|
||||
interface nsIUDPSocketInternal;
|
||||
interface nsIInputStream;
|
||||
interface nsIPrincipal;
|
||||
|
||||
%{ C++
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
union NetAddr;
|
||||
}
|
||||
}
|
||||
%}
|
||||
native NetAddr(mozilla::net::NetAddr);
|
||||
[ptr] native NetAddrPtr(mozilla::net::NetAddr);
|
||||
|
||||
[scriptable, uuid(1e6ad73b-6c05-4d78-9a88-2d357b88f58b)]
|
||||
interface nsIUDPSocketChild : nsISupports
|
||||
{
|
||||
readonly attribute unsigned short localPort;
|
||||
readonly attribute AUTF8String localAddress;
|
||||
attribute AUTF8String filterName;
|
||||
|
||||
// Allow hosting this over PBackground instead of PNecko
|
||||
[noscript] void setBackgroundSpinsEvents();
|
||||
|
||||
// Tell the chrome process to bind the UDP socket to a given local host and port
|
||||
void bind(in nsIUDPSocketInternal socket, in nsIPrincipal principal,
|
||||
in AUTF8String host, in unsigned short port,
|
||||
in bool addressReuse, in bool loopback, in uint32_t recvBufferSize,
|
||||
in uint32_t sendBufferSize);
|
||||
|
||||
// Tell the chrome process to connect the UDP socket to a given remote host and port
|
||||
void connect(in nsIUDPSocketInternal socket, in AUTF8String host, in unsigned short port);
|
||||
|
||||
// Tell the chrome process to perform equivalent operations to all following methods
|
||||
void send(in AUTF8String host, in unsigned short port,
|
||||
[const, array, size_is(byteLength)] in uint8_t bytes,
|
||||
in unsigned long byteLength);
|
||||
// Send without DNS query
|
||||
void sendWithAddr(in nsINetAddr addr,
|
||||
[const, array, size_is(byteLength)] in uint8_t bytes,
|
||||
in unsigned long byteLength);
|
||||
[noscript] void sendWithAddress([const] in NetAddrPtr addr,
|
||||
[const, array, size_is(byteLength)] in uint8_t bytes,
|
||||
in unsigned long byteLength);
|
||||
// Send input stream. This must be a buffered stream implementation.
|
||||
void sendBinaryStream(in AUTF8String host, in unsigned short port, in nsIInputStream stream);
|
||||
|
||||
void close();
|
||||
void joinMulticast(in AUTF8String multicastAddress, in AUTF8String iface);
|
||||
void leaveMulticast(in AUTF8String multicastAddress, in AUTF8String iface);
|
||||
};
|
||||
|
||||
/*
|
||||
* Internal interface for callback from chrome process
|
||||
*/
|
||||
[scriptable, uuid(613dd3ad-598b-4da9-ad63-bbda50c20098)]
|
||||
interface nsIUDPSocketInternal : nsISupports
|
||||
{
|
||||
// callback while socket is opened. localPort and localAddress is ready until this time.
|
||||
void callListenerOpened();
|
||||
// callback while socket is connected.
|
||||
void callListenerConnected();
|
||||
// callback while socket is closed.
|
||||
void callListenerClosed();
|
||||
// callback while incoming packet is received.
|
||||
void callListenerReceivedData(in AUTF8String host, in unsigned short port,
|
||||
[const, array, size_is(dataLength)] in uint8_t data,
|
||||
in unsigned long dataLength);
|
||||
// callback while any error happened.
|
||||
void callListenerError(in AUTF8String message, in AUTF8String filename, in uint32_t lineNumber);
|
||||
};
|
||||
76
dom/network/moz.build
Normal file
76
dom/network/moz.build
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
DIRS += ['interfaces']
|
||||
|
||||
MOCHITEST_CHROME_MANIFESTS += ['tests/chrome.ini']
|
||||
MOCHITEST_MANIFESTS += ['tests/mochitest.ini']
|
||||
|
||||
EXPORTS.mozilla.dom += [
|
||||
'TCPServerSocket.h',
|
||||
'TCPSocket.h',
|
||||
'UDPSocket.h',
|
||||
]
|
||||
|
||||
EXPORTS.mozilla.dom.network += [
|
||||
'Connection.h',
|
||||
'Constants.h',
|
||||
'TCPServerSocketChild.h',
|
||||
'TCPServerSocketParent.h',
|
||||
'TCPSocketChild.h',
|
||||
'TCPSocketParent.h',
|
||||
'Types.h',
|
||||
'UDPSocketChild.h',
|
||||
'UDPSocketParent.h',
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'Connection.cpp',
|
||||
'TCPServerSocket.cpp',
|
||||
'TCPServerSocketChild.cpp',
|
||||
'TCPServerSocketParent.cpp',
|
||||
'TCPSocket.cpp',
|
||||
'TCPSocketChild.cpp',
|
||||
'TCPSocketParent.cpp',
|
||||
'UDPSocket.cpp',
|
||||
'UDPSocketChild.cpp',
|
||||
'UDPSocketParent.cpp',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
|
||||
EXTRA_JS_MODULES += [
|
||||
'NetworkStatsDB.jsm',
|
||||
'NetworkStatsService.jsm',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
|
||||
EXTRA_COMPONENTS += [
|
||||
'EthernetManager.js',
|
||||
'EthernetManager.manifest',
|
||||
'NetworkStatsManager.js',
|
||||
'NetworkStatsManager.manifest',
|
||||
'NetworkStatsServiceProxy.js',
|
||||
'NetworkStatsServiceProxy.manifest',
|
||||
]
|
||||
EXPORTS.mozilla.dom.network += [
|
||||
'NetUtils.h',
|
||||
]
|
||||
UNIFIED_SOURCES += [
|
||||
'NetUtils.cpp',
|
||||
]
|
||||
|
||||
IPDL_SOURCES += [
|
||||
'PTCPServerSocket.ipdl',
|
||||
'PTCPSocket.ipdl',
|
||||
'PUDPSocket.ipdl',
|
||||
]
|
||||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
||||
if CONFIG['GNU_CXX']:
|
||||
CXXFLAGS += ['-Wno-error=shadow']
|
||||
83
dom/network/tests/add_task.js
Normal file
83
dom/network/tests/add_task.js
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
// Temporary implementation of add_task for mochitest-plain until bug 1078657 is
|
||||
// implemented.
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
(function(scope) {
|
||||
var pendingTasks = [];
|
||||
var pendingPromise = null;
|
||||
|
||||
// Strict spawn function that takes a known generatorFunc and assumes that
|
||||
// every yielded value will be a Promise. If nesting is desired, then yield*
|
||||
// should be used!
|
||||
function spawn(generatorFunc) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
try {
|
||||
var iterator = generatorFunc();
|
||||
}
|
||||
catch (ex) {
|
||||
ok(false, 'Problem invoking generator func: ' + ex + ': ' + ex.stack);
|
||||
return;
|
||||
}
|
||||
var stepResolved = function(result) {
|
||||
try {
|
||||
var iterStep = iterator.next(result);
|
||||
}
|
||||
catch (ex) {
|
||||
ok(false, 'Problem invoking iterator step: ' + ex + ': ' + ex.stack);
|
||||
return;
|
||||
}
|
||||
if (iterStep.done) {
|
||||
resolve(iterStep.value);
|
||||
return;
|
||||
}
|
||||
if (!iterStep.value || !iterStep.value.then) {
|
||||
ok(false, 'Iterator step returned non-Promise: ' + iterStep.value);
|
||||
}
|
||||
iterStep.value.then(stepResolved, generalErrback);
|
||||
};
|
||||
stepResolved();
|
||||
});
|
||||
}
|
||||
|
||||
function maybeSpawn(promiseOrGenerator) {
|
||||
if (promiseOrGenerator.then) {
|
||||
return promiseOrGenerator;
|
||||
}
|
||||
return spawn(promiseOrGenerator);
|
||||
}
|
||||
|
||||
scope.add_task = function(thing) {
|
||||
pendingTasks.push(thing);
|
||||
};
|
||||
|
||||
function generalErrback(ex) {
|
||||
ok(false,
|
||||
'A rejection happened: ' +
|
||||
(ex ? (ex + ': ' + ex.stack) : ''));
|
||||
}
|
||||
|
||||
function runNextTask() {
|
||||
if (pendingTasks.length) {
|
||||
pendingPromise = maybeSpawn(pendingTasks.shift());
|
||||
pendingPromise.then(runNextTask, generalErrback);
|
||||
} else {
|
||||
SimpleTest.finish();
|
||||
}
|
||||
}
|
||||
|
||||
// Trigger runNextTask after we think all JS files have been loaded.
|
||||
// The primary goal is that we can call SimpleTest.finish() after all test
|
||||
// code has been loaded and run. We gate this based on the document's
|
||||
// readyState.
|
||||
var running = false;
|
||||
function maybeStartRunning() {
|
||||
if (!running && document.readyState === 'complete') {
|
||||
running = true;
|
||||
document.removeEventListener('readystateChange', maybeStartRunning);
|
||||
// Defer to a subsequent turn of the event loop to let micro-tasks and any
|
||||
// other clever setTimeout(0) instances run first.
|
||||
window.setTimeout(runNextTask, 0);
|
||||
}
|
||||
}
|
||||
document.addEventListener('readystatechange', maybeStartRunning);
|
||||
maybeStartRunning();
|
||||
})(this);
|
||||
12
dom/network/tests/chrome.ini
Normal file
12
dom/network/tests/chrome.ini
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
[DEFAULT]
|
||||
support-files =
|
||||
tcpsocket_test.jsm
|
||||
test_tcpsocket_client_and_server_basics.js
|
||||
add_task.js
|
||||
file_udpsocket_iframe.html
|
||||
|
||||
[test_tcpsocket_jsm.html]
|
||||
[test_tcpsocket_client_and_server_basics.html]
|
||||
[test_tcpsocket_enabled_with_perm.html]
|
||||
[test_tcpsocket_legacy.html]
|
||||
[test_udpsocket.html]
|
||||
23
dom/network/tests/file_udpsocket_iframe.html
Normal file
23
dom/network/tests/file_udpsocket_iframe.html
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test UDPSocket BFCache</title>
|
||||
</head>
|
||||
<body>
|
||||
<script type="application/javascript;version=1.8">
|
||||
'use strict';
|
||||
window.addEventListener('load', function onload() {
|
||||
window.removeEventListener('load', onload);
|
||||
let remotePort = parseInt(window.location.search.substring(1), 10);
|
||||
let socket = new UDPSocket();
|
||||
socket.addEventListener('message', function () {
|
||||
socket.send('fail', '127.0.0.1', remotePort);
|
||||
});
|
||||
|
||||
socket.opened.then(function() {
|
||||
socket.send('ready', '127.0.0.1', remotePort);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
552
dom/network/tests/marionette/head.js
Normal file
552
dom/network/tests/marionette/head.js
Normal file
|
|
@ -0,0 +1,552 @@
|
|||
/* 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 expected.");
|
||||
} else {
|
||||
is(config.flag, INTERFACE_DOWN, "Interface is disabled as expected.");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 expected.");
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 expected.");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!gateway) {
|
||||
ok(true, "Default route is cleared.");
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: should we ok(false, ......) here?
|
||||
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 expected.");
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 + " succeeded.");
|
||||
is(message, "ok", "Message is as expected.");
|
||||
|
||||
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 + " succeeded.");
|
||||
is(message, "ok", "Message is as expected.");
|
||||
|
||||
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 + " succeeded.");
|
||||
is(message, "ok", "Message is as expected.");
|
||||
|
||||
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 + " succeeded.");
|
||||
is(message, "ok", "Message is as expected.");
|
||||
|
||||
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 expected.");
|
||||
|
||||
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 expected.");
|
||||
|
||||
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 expected.");
|
||||
|
||||
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 expected." + 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;
|
||||
})();
|
||||
13
dom/network/tests/marionette/manifest.ini
Normal file
13
dom/network/tests/marionette/manifest.ini
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
[DEFAULT]
|
||||
run-if = buildapp == 'b2g'
|
||||
|
||||
[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]
|
||||
16
dom/network/tests/marionette/test_ethernet_add_interface.js
Normal file
16
dom/network/tests/marionette/test_ethernet_add_interface.js
Normal 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));
|
||||
});
|
||||
|
|
@ -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));
|
||||
});
|
||||
|
|
@ -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));
|
||||
});
|
||||
17
dom/network/tests/marionette/test_ethernet_disable.js
Normal file
17
dom/network/tests/marionette/test_ethernet_disable.js
Normal 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));
|
||||
});
|
||||
25
dom/network/tests/marionette/test_ethernet_disconnect.js
Normal file
25
dom/network/tests/marionette/test_ethernet_disconnect.js
Normal 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));
|
||||
});
|
||||
17
dom/network/tests/marionette/test_ethernet_enable.js
Normal file
17
dom/network/tests/marionette/test_ethernet_enable.js
Normal 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));
|
||||
});
|
||||
43
dom/network/tests/marionette/test_ethernet_ip_mode_change.js
Normal file
43
dom/network/tests/marionette/test_ethernet_ip_mode_change.js
Normal 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));
|
||||
});
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
/* 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.makeInterfaceDisconnect(ETHERNET_INTERFACE_NAME))
|
||||
.then(() => gTestSuite.disableInterface(ETHERNET_INTERFACE_NAME))
|
||||
.then(() => gTestSuite.removeInterface(ETHERNET_INTERFACE_NAME));
|
||||
});
|
||||
|
|
@ -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));
|
||||
});
|
||||
|
|
@ -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));
|
||||
});
|
||||
8
dom/network/tests/mochitest.ini
Normal file
8
dom/network/tests/mochitest.ini
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
[DEFAULT]
|
||||
support-files =
|
||||
add_task.js
|
||||
|
||||
[test_network_basics.html]
|
||||
skip-if = toolkit == 'android'
|
||||
[test_tcpsocket_default_permissions.html]
|
||||
[test_tcpsocket_enabled_no_perm.html]
|
||||
20
dom/network/tests/tcpsocket_test.jsm
Normal file
20
dom/network/tests/tcpsocket_test.jsm
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
this.EXPORTED_SYMBOLS = [
|
||||
'createSocket', 'createServer', 'enablePrefsAndPermissions',
|
||||
'socketCompartmentInstanceOfArrayBuffer'];
|
||||
|
||||
this.createSocket = function(host, port, options) {
|
||||
return new TCPSocket(host, port, options);
|
||||
}
|
||||
|
||||
this.createServer = function(port, options, backlog) {
|
||||
return new TCPServerSocket(port, options, backlog);
|
||||
}
|
||||
|
||||
this.enablePrefsAndPermissions = function() {
|
||||
return false;
|
||||
}
|
||||
|
||||
// See test_tcpsocket_client_and_server_basics.html's version for rationale.
|
||||
this.socketCompartmentInstanceOfArrayBuffer = function(obj) {
|
||||
return obj instanceof ArrayBuffer;
|
||||
}
|
||||
38
dom/network/tests/test_network_basics.html
Normal file
38
dom/network/tests/test_network_basics.html
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test for Network API</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script type="application/javascript">
|
||||
|
||||
/** Test for Network Information API **/
|
||||
function test() {
|
||||
ok('connection' in navigator, "navigator.connection should exist");
|
||||
|
||||
ok(navigator.connection, "navigator.connection returns an object");
|
||||
|
||||
ok(navigator.connection instanceof EventTarget,
|
||||
"navigator.connection is a EventTarget object");
|
||||
|
||||
ok('type' in navigator.connection,
|
||||
"type should be a Connection attribute");
|
||||
is(navigator.connection.type, "none",
|
||||
"By default connection.type equals to none");
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SpecialPowers.pushPrefEnv({'set': [["dom.netinfo.enabled", true]]}, test);
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
Core tests for TCPSocket and TCPServerSocket that replace their previous
|
||||
separate xpcshell incarnations. This migration and cleanup occurred as part
|
||||
of bug 1084245 in order to get coverage of the tests from content.
|
||||
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1084245
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug 1084245</title>
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
|
||||
<script type="application/javascript" src="add_task.js"></script>
|
||||
<script type="application/javascript">
|
||||
function createServer(port, options, backlog) {
|
||||
return new TCPServerSocket(port, options, backlog);
|
||||
}
|
||||
|
||||
function createSocket(host, port, options) {
|
||||
return new TCPSocket(host, port, options);
|
||||
}
|
||||
|
||||
function enablePrefsAndPermissions() {
|
||||
return true;
|
||||
}
|
||||
|
||||
// In the JSM case, ArrayBuffers will be created in the compartment of the
|
||||
// JSM with different globals than the
|
||||
// test_tcpsocket_client_and_server_basics.js test logic sees, so we (and
|
||||
// tcpsocket_test.jsm) need to do something. To avoid complexity relating
|
||||
// to wrappers and the varying nuances of the module scope and global scope
|
||||
// in JSM's (they differ on B2G), we hardcode ArrayBuffer rather than taking
|
||||
// a string that we look up, etc.
|
||||
function socketCompartmentInstanceOfArrayBuffer(obj) {
|
||||
return obj instanceof ArrayBuffer;
|
||||
}
|
||||
</script>
|
||||
<script type="application/javascript;version=1.7" src="test_tcpsocket_client_and_server_basics.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1084245">Mozilla Bug 1084245</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
423
dom/network/tests/test_tcpsocket_client_and_server_basics.js
Normal file
423
dom/network/tests/test_tcpsocket_client_and_server_basics.js
Normal file
|
|
@ -0,0 +1,423 @@
|
|||
'use strict';
|
||||
|
||||
const SERVER_BACKLOG = -1;
|
||||
|
||||
const SOCKET_EVENTS = ['open', 'data', 'drain', 'error', 'close'];
|
||||
|
||||
function concatUint8Arrays(a, b) {
|
||||
let newArr = new Uint8Array(a.length + b.length);
|
||||
newArr.set(a, 0);
|
||||
newArr.set(b, a.length);
|
||||
return newArr;
|
||||
}
|
||||
|
||||
function assertUint8ArraysEqual(a, b, comparingWhat) {
|
||||
if (a.length !== b.length) {
|
||||
ok(false, comparingWhat + ' arrays do not have the same length; ' +
|
||||
a.length + ' versus ' + b.length);
|
||||
return;
|
||||
}
|
||||
for (let i = 0; i < a.length; i++) {
|
||||
if (a[i] !== b[i]) {
|
||||
ok(false, comparingWhat + ' arrays differ at index ' + i +
|
||||
a[i] + ' versus ' + b[i]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
ok(true, comparingWhat + ' arrays were equivalent.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to add event listeners to a socket and provide two Promise-returning
|
||||
* helpers (see below for docs on them). This *must* be called during the turn of
|
||||
* the event loop where TCPSocket's constructor is called or the onconnect method is being
|
||||
* invoked.
|
||||
*/
|
||||
function listenForEventsOnSocket(socket, socketType) {
|
||||
let wantDataLength = null;
|
||||
let wantDataAndClose = false;
|
||||
let pendingResolve = null;
|
||||
let receivedEvents = [];
|
||||
let receivedData = null;
|
||||
let handleGenericEvent = function(event) {
|
||||
dump('(' + socketType + ' event: ' + event.type + ')\n');
|
||||
if (pendingResolve && wantDataLength === null) {
|
||||
pendingResolve(event);
|
||||
pendingResolve = null;
|
||||
} else {
|
||||
receivedEvents.push(event);
|
||||
}
|
||||
};
|
||||
|
||||
socket.onopen = handleGenericEvent;
|
||||
socket.ondrain = handleGenericEvent;
|
||||
socket.onerror = handleGenericEvent;
|
||||
socket.onclose = function(event) {
|
||||
if (!wantDataAndClose) {
|
||||
handleGenericEvent(event);
|
||||
} else if (pendingResolve) {
|
||||
dump('(' + socketType + ' event: close)\n');
|
||||
pendingResolve(receivedData);
|
||||
pendingResolve = null;
|
||||
wantDataAndClose = false;
|
||||
}
|
||||
}
|
||||
socket.ondata = function(event) {
|
||||
dump('(' + socketType + ' event: ' + event.type + ' length: ' +
|
||||
event.data.byteLength + ')\n');
|
||||
ok(socketCompartmentInstanceOfArrayBuffer(event.data),
|
||||
'payload is ArrayBuffer');
|
||||
var arr = new Uint8Array(event.data);
|
||||
if (receivedData === null) {
|
||||
receivedData = arr;
|
||||
} else {
|
||||
receivedData = concatUint8Arrays(receivedData, arr);
|
||||
}
|
||||
if (wantDataLength !== null &&
|
||||
receivedData.length >= wantDataLength) {
|
||||
pendingResolve(receivedData);
|
||||
pendingResolve = null;
|
||||
receivedData = null;
|
||||
wantDataLength = null;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
return {
|
||||
/**
|
||||
* Return a Promise that will be resolved with the next (non-data) event
|
||||
* received by the socket. If there are queued events, the Promise will
|
||||
* be immediately resolved (but you won't see that until a future turn of
|
||||
* the event loop).
|
||||
*/
|
||||
waitForEvent: function() {
|
||||
if (pendingResolve) {
|
||||
throw new Error('only one wait allowed at a time.');
|
||||
}
|
||||
|
||||
if (receivedEvents.length) {
|
||||
return Promise.resolve(receivedEvents.shift());
|
||||
}
|
||||
|
||||
dump('(' + socketType + ' waiting for event)\n');
|
||||
return new Promise(function(resolve, reject) {
|
||||
pendingResolve = resolve;
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Return a Promise that will be resolved with a Uint8Array of at least the
|
||||
* given length. We buffer / accumulate received data until we have enough
|
||||
* data. Data is buffered even before you call this method, so be sure to
|
||||
* explicitly wait for any and all data sent by the other side.
|
||||
*/
|
||||
waitForDataWithAtLeastLength: function(length) {
|
||||
if (pendingResolve) {
|
||||
throw new Error('only one wait allowed at a time.');
|
||||
}
|
||||
if (receivedData && receivedData.length >= length) {
|
||||
let promise = Promise.resolve(receivedData);
|
||||
receivedData = null;
|
||||
return promise;
|
||||
}
|
||||
dump('(' + socketType + ' waiting for ' + length + ' bytes)\n');
|
||||
return new Promise(function(resolve, reject) {
|
||||
pendingResolve = resolve;
|
||||
wantDataLength = length;
|
||||
});
|
||||
},
|
||||
waitForAnyDataAndClose: function() {
|
||||
if (pendingResolve) {
|
||||
throw new Error('only one wait allowed at a time.');
|
||||
}
|
||||
|
||||
return new Promise(function(resolve, reject) {
|
||||
pendingResolve = resolve;
|
||||
// we may receive no data before getting close, in which case we want to
|
||||
// return an empty array
|
||||
receivedData = new Uint8Array();
|
||||
wantDataAndClose = true;
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a promise that is resolved when the server receives a connection. The
|
||||
* promise is resolved with { socket, queue } where `queue` is the result of
|
||||
* calling listenForEventsOnSocket(socket). This must be done because we need
|
||||
* to add the event listener during the connection.
|
||||
*/
|
||||
function waitForConnection(listeningServer) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
// Because of the event model of sockets, we can't use the
|
||||
// listenForEventsOnSocket mechanism; we need to hook up listeners during
|
||||
// the connect event.
|
||||
listeningServer.onconnect = function(event) {
|
||||
// Clobber the listener to get upset if it receives any more connections
|
||||
// after this.
|
||||
listeningServer.onconnect = function() {
|
||||
ok(false, 'Received a connection when not expecting one.');
|
||||
};
|
||||
ok(true, 'Listening server accepted socket');
|
||||
resolve({
|
||||
socket: event.socket,
|
||||
queue: listenForEventsOnSocket(event.socket, 'server')
|
||||
});
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function defer() {
|
||||
var deferred = {};
|
||||
deferred.promise = new Promise(function(resolve, reject) {
|
||||
deferred.resolve = resolve;
|
||||
deferred.reject = reject;
|
||||
});
|
||||
return deferred;
|
||||
}
|
||||
|
||||
|
||||
function* test_basics() {
|
||||
// See bug 903830; in e10s mode we never get to find out the localPort if we
|
||||
// let it pick a free port by choosing 0. This is the same port the xpcshell
|
||||
// test was using.
|
||||
let serverPort = 8085;
|
||||
|
||||
// - Start up a listening socket.
|
||||
let listeningServer = createServer(serverPort,
|
||||
{ binaryType: 'arraybuffer' },
|
||||
SERVER_BACKLOG);
|
||||
|
||||
let connectedPromise = waitForConnection(listeningServer);
|
||||
|
||||
// -- Open a connection to the server
|
||||
let clientSocket = createSocket('127.0.0.1', serverPort,
|
||||
{ binaryType: 'arraybuffer' });
|
||||
let clientQueue = listenForEventsOnSocket(clientSocket, 'client');
|
||||
|
||||
// (the client connects)
|
||||
is((yield clientQueue.waitForEvent()).type, 'open', 'got open event');
|
||||
is(clientSocket.readyState, 'open', 'client readyState is open');
|
||||
|
||||
// (the server connected)
|
||||
let { socket: serverSocket, queue: serverQueue } = yield connectedPromise;
|
||||
is(serverSocket.readyState, 'open', 'server readyState is open');
|
||||
|
||||
// -- Simple send / receive
|
||||
// - Send data from client to server
|
||||
// (But not so much we cross the drain threshold.)
|
||||
let smallUint8Array = new Uint8Array(256);
|
||||
for (let i = 0; i < smallUint8Array.length; i++) {
|
||||
smallUint8Array[i] = i;
|
||||
}
|
||||
is(clientSocket.send(smallUint8Array.buffer, 0, smallUint8Array.length), true,
|
||||
'Client sending less than 64k, buffer should not be full.');
|
||||
|
||||
let serverReceived = yield serverQueue.waitForDataWithAtLeastLength(256);
|
||||
assertUint8ArraysEqual(serverReceived, smallUint8Array,
|
||||
'Server received/client sent');
|
||||
|
||||
// - Send data from server to client
|
||||
// (But not so much we cross the drain threshold.)
|
||||
is(serverSocket.send(smallUint8Array.buffer, 0, smallUint8Array.length), true,
|
||||
'Server sending less than 64k, buffer should not be full.');
|
||||
|
||||
let clientReceived = yield clientQueue.waitForDataWithAtLeastLength(256);
|
||||
assertUint8ArraysEqual(clientReceived, smallUint8Array,
|
||||
'Client received/server sent');
|
||||
|
||||
// -- Perform sending multiple times with different buffer slices
|
||||
// - Send data from client to server
|
||||
// (But not so much we cross the drain threshold.)
|
||||
is(clientSocket.send(smallUint8Array.buffer, 0, 7),
|
||||
true, 'Client sending less than 64k, buffer should not be full.');
|
||||
is(clientSocket.send(smallUint8Array.buffer, 7, smallUint8Array.length - 7),
|
||||
true, 'Client sending less than 64k, buffer should not be full.');
|
||||
|
||||
serverReceived = yield serverQueue.waitForDataWithAtLeastLength(256);
|
||||
assertUint8ArraysEqual(serverReceived, smallUint8Array,
|
||||
'Server received/client sent');
|
||||
|
||||
// - Send data from server to client
|
||||
// (But not so much we cross the drain threshold.)
|
||||
is(serverSocket.send(smallUint8Array.buffer, 0, 7),
|
||||
true, 'Server sending less than 64k, buffer should not be full.');
|
||||
is(serverSocket.send(smallUint8Array.buffer, 7, smallUint8Array.length - 7),
|
||||
true, 'Server sending less than 64k, buffer should not be full.');
|
||||
|
||||
clientReceived = yield clientQueue.waitForDataWithAtLeastLength(256);
|
||||
assertUint8ArraysEqual(clientReceived, smallUint8Array,
|
||||
'Client received/server sent');
|
||||
|
||||
|
||||
// -- Send "big" data in both directions
|
||||
// (Enough to cross the buffering/drain threshold; 64KiB)
|
||||
let bigUint8Array = new Uint8Array(65536 + 3);
|
||||
for (let i = 0; i < bigUint8Array.length; i++) {
|
||||
bigUint8Array[i] = i % 256;
|
||||
}
|
||||
// Do this twice so we have confidence that the 'drain' event machinery
|
||||
// doesn't break after the first use.
|
||||
for (let iSend = 0; iSend < 2; iSend++) {
|
||||
// - Send "big" data from the client to the server
|
||||
is(clientSocket.send(bigUint8Array.buffer, 0, bigUint8Array.length), false,
|
||||
'Client sending more than 64k should result in the buffer being full.');
|
||||
is((yield clientQueue.waitForEvent()).type, 'drain',
|
||||
'The drain event should fire after a large send that indicated full.');
|
||||
|
||||
serverReceived = yield serverQueue.waitForDataWithAtLeastLength(
|
||||
bigUint8Array.length);
|
||||
assertUint8ArraysEqual(serverReceived, bigUint8Array,
|
||||
'server received/client sent');
|
||||
|
||||
// - Send "big" data from the server to the client
|
||||
is(serverSocket.send(bigUint8Array.buffer, 0, bigUint8Array.length), false,
|
||||
'Server sending more than 64k should result in the buffer being full.');
|
||||
is((yield serverQueue.waitForEvent()).type, 'drain',
|
||||
'The drain event should fire after a large send that indicated full.');
|
||||
|
||||
clientReceived = yield clientQueue.waitForDataWithAtLeastLength(
|
||||
bigUint8Array.length);
|
||||
assertUint8ArraysEqual(clientReceived, bigUint8Array,
|
||||
'client received/server sent');
|
||||
}
|
||||
|
||||
// -- Server closes the connection
|
||||
serverSocket.close();
|
||||
is(serverSocket.readyState, 'closing',
|
||||
'readyState should be closing immediately after calling close');
|
||||
|
||||
is((yield clientQueue.waitForEvent()).type, 'close',
|
||||
'The client should get a close event when the server closes.');
|
||||
is(clientSocket.readyState, 'closed',
|
||||
'client readyState should be closed after close event');
|
||||
is((yield serverQueue.waitForEvent()).type, 'close',
|
||||
'The server should get a close event when it closes itself.');
|
||||
is(serverSocket.readyState, 'closed',
|
||||
'server readyState should be closed after close event');
|
||||
|
||||
// -- Re-establish connection
|
||||
connectedPromise = waitForConnection(listeningServer);
|
||||
clientSocket = createSocket('127.0.0.1', serverPort,
|
||||
{ binaryType: 'arraybuffer' });
|
||||
clientQueue = listenForEventsOnSocket(clientSocket, 'client');
|
||||
is((yield clientQueue.waitForEvent()).type, 'open', 'got open event');
|
||||
|
||||
let connectedResult = yield connectedPromise;
|
||||
// destructuring assignment is not yet ES6 compliant, must manually unpack
|
||||
serverSocket = connectedResult.socket;
|
||||
serverQueue = connectedResult.queue;
|
||||
|
||||
// -- Client closes the connection
|
||||
clientSocket.close();
|
||||
is(clientSocket.readyState, 'closing',
|
||||
'client readyState should be losing immediately after calling close');
|
||||
|
||||
is((yield clientQueue.waitForEvent()).type, 'close',
|
||||
'The client should get a close event when it closes itself.');
|
||||
is(clientSocket.readyState, 'closed',
|
||||
'client readyState should be closed after the close event is received');
|
||||
is((yield serverQueue.waitForEvent()).type, 'close',
|
||||
'The server should get a close event when the client closes.');
|
||||
is(serverSocket.readyState, 'closed',
|
||||
'server readyState should be closed after the close event is received');
|
||||
|
||||
|
||||
// -- Re-establish connection
|
||||
connectedPromise = waitForConnection(listeningServer);
|
||||
clientSocket = createSocket('127.0.0.1', serverPort,
|
||||
{ binaryType: 'arraybuffer' });
|
||||
clientQueue = listenForEventsOnSocket(clientSocket, 'client');
|
||||
is((yield clientQueue.waitForEvent()).type, 'open', 'got open event');
|
||||
|
||||
connectedResult = yield connectedPromise;
|
||||
// destructuring assignment is not yet ES6 compliant, must manually unpack
|
||||
serverSocket = connectedResult.socket;
|
||||
serverQueue = connectedResult.queue;
|
||||
|
||||
// -- Call close after enqueueing a lot of data, make sure it goes through.
|
||||
// We'll have the client send and close.
|
||||
is(clientSocket.send(bigUint8Array.buffer, 0, bigUint8Array.length), false,
|
||||
'Client sending more than 64k should result in the buffer being full.');
|
||||
clientSocket.close();
|
||||
// The drain will still fire
|
||||
is((yield clientQueue.waitForEvent()).type, 'drain',
|
||||
'The drain event should fire after a large send that returned true.');
|
||||
// Then we'll get a close
|
||||
is((yield clientQueue.waitForEvent()).type, 'close',
|
||||
'The close event should fire after the drain event.');
|
||||
|
||||
// The server will get its data
|
||||
serverReceived = yield serverQueue.waitForDataWithAtLeastLength(
|
||||
bigUint8Array.length);
|
||||
assertUint8ArraysEqual(serverReceived, bigUint8Array,
|
||||
'server received/client sent');
|
||||
// And a close.
|
||||
is((yield serverQueue.waitForEvent()).type, 'close',
|
||||
'The drain event should fire after a large send that returned true.');
|
||||
|
||||
|
||||
// -- Re-establish connection
|
||||
connectedPromise = waitForConnection(listeningServer);
|
||||
clientSocket = createSocket('127.0.0.1', serverPort,
|
||||
{ binaryType: 'string' });
|
||||
clientQueue = listenForEventsOnSocket(clientSocket, 'client');
|
||||
is((yield clientQueue.waitForEvent()).type, 'open', 'got open event');
|
||||
|
||||
connectedResult = yield connectedPromise;
|
||||
// destructuring assignment is not yet ES6 compliant, must manually unpack
|
||||
serverSocket = connectedResult.socket;
|
||||
serverQueue = connectedResult.queue;
|
||||
|
||||
// -- Attempt to send non-string data.
|
||||
// Restore the original behavior by replacing toString with
|
||||
// Object.prototype.toString. (bug 1121938)
|
||||
bigUint8Array.toString = Object.prototype.toString;
|
||||
is(clientSocket.send(bigUint8Array), true,
|
||||
'Client sending a large non-string should only send a small string.');
|
||||
clientSocket.close();
|
||||
// The server will get its data
|
||||
serverReceived = yield serverQueue.waitForDataWithAtLeastLength(
|
||||
bigUint8Array.toString().length);
|
||||
// Then we'll get a close
|
||||
is((yield clientQueue.waitForEvent()).type, 'close',
|
||||
'The close event should fire after the drain event.');
|
||||
|
||||
// -- Re-establish connection (Test for Close Immediately)
|
||||
connectedPromise = waitForConnection(listeningServer);
|
||||
clientSocket = createSocket('127.0.0.1', serverPort,
|
||||
{ binaryType: 'arraybuffer' });
|
||||
clientQueue = listenForEventsOnSocket(clientSocket, 'client');
|
||||
is((yield clientQueue.waitForEvent()).type, 'open', 'got open event');
|
||||
|
||||
connectedResult = yield connectedPromise;
|
||||
// destructuring assignment is not yet ES6 compliant, must manually unpack
|
||||
serverSocket = connectedResult.socket;
|
||||
serverQueue = connectedResult.queue;
|
||||
|
||||
// -- Attempt to send two non-string data.
|
||||
is(clientSocket.send(bigUint8Array.buffer, 0, bigUint8Array.length), false,
|
||||
'Server sending more than 64k should result in the buffer being full.');
|
||||
is(clientSocket.send(bigUint8Array.buffer, 0, bigUint8Array.length), false,
|
||||
'Server sending more than 64k should result in the buffer being full.');
|
||||
clientSocket.closeImmediately();
|
||||
|
||||
serverReceived = yield serverQueue.waitForAnyDataAndClose();
|
||||
|
||||
is(serverReceived.length < (2 * bigUint8Array.length), true, 'Received array length less than sent array length');
|
||||
|
||||
// -- Close the listening server (and try to connect)
|
||||
// We want to verify that the server actually closes / stops listening when
|
||||
// we tell it to.
|
||||
listeningServer.close();
|
||||
|
||||
// - try and connect, get an error
|
||||
clientSocket = createSocket('127.0.0.1', serverPort,
|
||||
{ binaryType: 'arraybuffer' });
|
||||
clientQueue = listenForEventsOnSocket(clientSocket, 'client');
|
||||
is((yield clientQueue.waitForEvent()).type, 'error', 'fail to connect');
|
||||
is(clientSocket.readyState, 'closed',
|
||||
'client readyState should be closed after the failure to connect');
|
||||
}
|
||||
|
||||
add_task(test_basics);
|
||||
38
dom/network/tests/test_tcpsocket_default_permissions.html
Normal file
38
dom/network/tests/test_tcpsocket_default_permissions.html
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test to ensure TCPSocket permission is disabled by default</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script type="application/javascript">
|
||||
|
||||
/** Test to ensure TCPSocket permission is disabled by default **/
|
||||
|
||||
var caught = false;
|
||||
try {
|
||||
new TCPSocket("localhost", 80, {})
|
||||
} catch (e) {
|
||||
caught = true;
|
||||
}
|
||||
|
||||
ok(caught, "TCPSocket should not exist by default");
|
||||
|
||||
var caught = false;
|
||||
try {
|
||||
navigator.mozTCPSocket.open("localhost", 80, {})
|
||||
} catch (e) {
|
||||
caught = true;
|
||||
}
|
||||
|
||||
ok(caught, "navigator.mozTCPSocket.open should not exist by default");
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
30
dom/network/tests/test_tcpsocket_enabled_no_perm.html
Normal file
30
dom/network/tests/test_tcpsocket_enabled_no_perm.html
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test to ensure TCPSocket permission enabled and no tcp-socket perm does not allow open</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script type="application/javascript">
|
||||
|
||||
/** Test to ensure TCPSocket preference being turned on does not enable
|
||||
navigator.mozTCPSocket.
|
||||
**/
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SpecialPowers.pushPrefEnv({"set": [['dom.mozTCPSocket.enabled', true]]}, runTest);
|
||||
function runTest() {
|
||||
is('TCPSocket' in this, false, "TCPSocket should not be accessible if dom.mozTCPSocket.enabled is true");
|
||||
is('TCPServerSocket' in this, false, "TCPServerSocket should not be accessible if dom.mozTCPSocket.enabled is true");
|
||||
is('mozTCPSocket' in navigator, false, "mozTCPSocket should not be accessible if dom.mozTCPSocket.enabled is true");
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
35
dom/network/tests/test_tcpsocket_enabled_with_perm.html
Normal file
35
dom/network/tests/test_tcpsocket_enabled_with_perm.html
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test to ensure TCPSocket permission enabled and open works with tcp-socket perm</title>
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script type="application/javascript">
|
||||
|
||||
/** Test to ensure TCPSocket permission being turned on enables
|
||||
navigator.mozTCPSocket, and mozTCPSocket.open works when
|
||||
the tcp-socket permission has been granted.
|
||||
**/
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SpecialPowers.pushPrefEnv({"set": [['dom.mozTCPSocket.enabled', true]]}, runTest);
|
||||
|
||||
function runTest() {
|
||||
ok('TCPSocket' in this, "TCPSocket should be accessible if dom.mozTCPSocket.enabled is true");
|
||||
|
||||
ok(new TCPSocket('localhost', 80), "TCPSocket constructor should work for content that has the tcp-socket permission");
|
||||
ok(navigator.mozTCPSocket.open('localhost', 80), "navigator.mozTCPSocket.open should work for content that has the tcp-socket permission");
|
||||
// This just helps the test harness clean up quickly
|
||||
SpecialPowers.forceCC();
|
||||
SpecialPowers.forceGC();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
25
dom/network/tests/test_tcpsocket_jsm.html
Normal file
25
dom/network/tests/test_tcpsocket_jsm.html
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for 1207090</title>
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
|
||||
<script type="application/javascript">
|
||||
Components.utils.import("chrome://mochitests/content/chrome/dom/network/tests/tcpsocket_test.jsm");
|
||||
</script>
|
||||
<script type="application/javascript" src="add_task.js"></script>
|
||||
<script type="application/javascript;version=1.7" src="test_tcpsocket_client_and_server_basics.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1207090">Mozilla Bug 1207090</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
</div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
<div id="container"></div>
|
||||
</body>
|
||||
</html>
|
||||
59
dom/network/tests/test_tcpsocket_legacy.html
Normal file
59
dom/network/tests/test_tcpsocket_legacy.html
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
Test of legacy navigator interface for opening TCPSocket/TCPServerSocket.
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug 885982</title>
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1084245">Mozilla Bug 1084245</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
<script>
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SpecialPowers.pushPrefEnv(
|
||||
{ set: [ ['dom.mozTCPSocket.enabled', true] ] },
|
||||
runTest);
|
||||
|
||||
function runTest() {
|
||||
// See bug 903830; in e10s mode we never get to find out the localPort if we
|
||||
// let it pick a free port by choosing 0. This is the same port the xpcshell
|
||||
// test was using.
|
||||
var serverPort = 8085;
|
||||
|
||||
var listeningServer = navigator.mozTCPSocket.listen(serverPort,
|
||||
{ binaryType: 'arraybuffer' },
|
||||
-1);
|
||||
listeningServer.onconnect = function(ev) {
|
||||
ok(true, "got server connect");
|
||||
listeningServer.close();
|
||||
listeningServer = null;
|
||||
ev.socket.close()
|
||||
}
|
||||
|
||||
var clientSocket = navigator.mozTCPSocket.open('127.0.0.1', serverPort,
|
||||
{ binaryType: 'arraybuffer' });
|
||||
clientSocket.onopen = function() { ok(true, "got client open"); }
|
||||
clientSocket.onclose = function() {
|
||||
ok(true, "got client close");
|
||||
clientSocket.close();
|
||||
clientSocket = null;
|
||||
setTimeout(function() {
|
||||
// This just helps the test harness clean up quickly
|
||||
SpecialPowers.forceCC();
|
||||
SpecialPowers.forceGC();
|
||||
SimpleTest.finish();
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
405
dom/network/tests/test_udpsocket.html
Normal file
405
dom/network/tests/test_udpsocket.html
Normal file
|
|
@ -0,0 +1,405 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test UDPSocket API</title>
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
</div>
|
||||
<iframe id="iframe"></iframe>
|
||||
<pre id="test">
|
||||
<script type="application/javascript;version=1.8">
|
||||
'use strict';
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SimpleTest.requestFlakyTimeout("untriaged");
|
||||
|
||||
const HELLO_WORLD = 'hlo wrld. ';
|
||||
const DATA_ARRAY = [0, 255, 254, 0, 1, 2, 3, 0, 255, 255, 254, 0];
|
||||
const DATA_ARRAY_BUFFER = new ArrayBuffer(DATA_ARRAY.length);
|
||||
const TYPED_DATA_ARRAY = new Uint8Array(DATA_ARRAY_BUFFER);
|
||||
const BIG_ARRAY = new Array(4096);
|
||||
const BIG_ARRAY_BUFFER = new ArrayBuffer(BIG_ARRAY.length);
|
||||
const BIG_TYPED_ARRAY = new Uint8Array(BIG_ARRAY_BUFFER);
|
||||
|
||||
for (let i = 0; i < BIG_ARRAY.length; i++) {
|
||||
BIG_ARRAY[i] = Math.floor(Math.random() * 256);
|
||||
}
|
||||
|
||||
TYPED_DATA_ARRAY.set(DATA_ARRAY);
|
||||
BIG_TYPED_ARRAY.set(BIG_ARRAY);
|
||||
|
||||
function is_same_buffer(recv_data, expect_data) {
|
||||
let recv_dataview = new Uint8Array(recv_data);
|
||||
let expected_dataview = new Uint8Array(expect_data);
|
||||
|
||||
if (recv_dataview.length !== expected_dataview.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (let i = 0; i < recv_dataview.length; i++) {
|
||||
if (recv_dataview[i] != expected_dataview[i]) {
|
||||
info('discover byte differenct at ' + i);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function testOpen() {
|
||||
info('test for creating an UDP Socket');
|
||||
let socket = new UDPSocket();
|
||||
is(socket.localPort, null, 'expect no local port before socket opened');
|
||||
is(socket.localAddress, null, 'expect no local address before socket opened');
|
||||
is(socket.remotePort, null, 'expected no default remote port');
|
||||
is(socket.remoteAddress, null, 'expected no default remote address');
|
||||
is(socket.readyState, 'opening', 'expected ready state = opening');
|
||||
is(socket.loopback, false, 'expected no loopback');
|
||||
is(socket.addressReuse, true, 'expect to reuse address');
|
||||
|
||||
return socket.opened.then(function() {
|
||||
ok(true, 'expect openedPromise to be resolved after successful socket binding');
|
||||
ok(!(socket.localPort === 0), 'expect allocated a local port');
|
||||
is(socket.localAddress, '0.0.0.0', 'expect assigned to default address');
|
||||
is(socket.readyState, 'open', 'expected ready state = open');
|
||||
|
||||
return socket;
|
||||
});
|
||||
}
|
||||
|
||||
function testSendString(socket) {
|
||||
info('test for sending string data');
|
||||
|
||||
socket.send(HELLO_WORLD, '127.0.0.1', socket.localPort);
|
||||
|
||||
return new Promise(function(resolve, reject) {
|
||||
socket.addEventListener('message', function recv_callback(msg) {
|
||||
socket.removeEventListener('message', recv_callback);
|
||||
let recvData= String.fromCharCode.apply(null, new Uint8Array(msg.data));
|
||||
is(msg.remotePort, socket.localPort, 'expected packet from ' + socket.localPort);
|
||||
is(recvData, HELLO_WORLD, 'expected same string data');
|
||||
resolve(socket);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function testSendArrayBuffer(socket) {
|
||||
info('test for sending ArrayBuffer');
|
||||
|
||||
socket.send(DATA_ARRAY_BUFFER, '127.0.0.1', socket.localPort);
|
||||
|
||||
return new Promise(function(resolve, reject) {
|
||||
socket.addEventListener('message', function recv_callback(msg) {
|
||||
socket.removeEventListener('message', recv_callback);
|
||||
is(msg.remotePort, socket.localPort, 'expected packet from ' + socket.localPort);
|
||||
ok(is_same_buffer(msg.data, DATA_ARRAY_BUFFER), 'expected same buffer data');
|
||||
resolve(socket);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function testSendArrayBufferView(socket) {
|
||||
info('test for sending ArrayBufferView');
|
||||
|
||||
socket.send(TYPED_DATA_ARRAY, '127.0.0.1', socket.localPort);
|
||||
|
||||
return new Promise(function(resolve, reject) {
|
||||
socket.addEventListener('message', function recv_callback(msg) {
|
||||
socket.removeEventListener('message', recv_callback);
|
||||
is(msg.remotePort, socket.localPort, 'expected packet from ' + socket.localPort);
|
||||
ok(is_same_buffer(msg.data, TYPED_DATA_ARRAY), 'expected same buffer data');
|
||||
resolve(socket);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function testSendBlob(socket) {
|
||||
info('test for sending Blob');
|
||||
|
||||
let blob = new Blob([HELLO_WORLD], {type : 'text/plain'});
|
||||
socket.send(blob, '127.0.0.1', socket.localPort);
|
||||
|
||||
return new Promise(function(resolve, reject) {
|
||||
socket.addEventListener('message', function recv_callback(msg) {
|
||||
socket.removeEventListener('message', recv_callback);
|
||||
let recvData= String.fromCharCode.apply(null, new Uint8Array(msg.data));
|
||||
is(msg.remotePort, socket.localPort, 'expected packet from ' + socket.localPort);
|
||||
is(recvData, HELLO_WORLD, 'expected same string data');
|
||||
resolve(socket);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function testSendBigArray(socket) {
|
||||
info('test for sending Big ArrayBuffer');
|
||||
|
||||
socket.send(BIG_TYPED_ARRAY, '127.0.0.1', socket.localPort);
|
||||
|
||||
return new Promise(function(resolve, reject) {
|
||||
let byteReceived = 0;
|
||||
socket.addEventListener('message', function recv_callback(msg) {
|
||||
let byteBegin = byteReceived;
|
||||
byteReceived += msg.data.byteLength;
|
||||
is(msg.remotePort, socket.localPort, 'expected packet from ' + socket.localPort);
|
||||
ok(is_same_buffer(msg.data, BIG_TYPED_ARRAY.subarray(byteBegin, byteReceived)), 'expected same buffer data [' + byteBegin+ '-' + byteReceived + ']');
|
||||
if (byteReceived >= BIG_TYPED_ARRAY.length) {
|
||||
socket.removeEventListener('message', recv_callback);
|
||||
resolve(socket);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function testSendBigBlob(socket) {
|
||||
info('test for sending Big Blob');
|
||||
|
||||
let blob = new Blob([BIG_TYPED_ARRAY]);
|
||||
socket.send(blob, '127.0.0.1', socket.localPort);
|
||||
|
||||
return new Promise(function(resolve, reject) {
|
||||
let byteReceived = 0;
|
||||
socket.addEventListener('message', function recv_callback(msg) {
|
||||
let byteBegin = byteReceived;
|
||||
byteReceived += msg.data.byteLength;
|
||||
is(msg.remotePort, socket.localPort, 'expected packet from ' + socket.localPort);
|
||||
ok(is_same_buffer(msg.data, BIG_TYPED_ARRAY.subarray(byteBegin, byteReceived)), 'expected same buffer data [' + byteBegin+ '-' + byteReceived + ']');
|
||||
if (byteReceived >= BIG_TYPED_ARRAY.length) {
|
||||
socket.removeEventListener('message', recv_callback);
|
||||
resolve(socket);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function testUDPOptions(socket) {
|
||||
info('test for UDP init options');
|
||||
|
||||
let remoteSocket = new UDPSocket({addressReuse: false,
|
||||
loopback: true,
|
||||
localAddress: '127.0.0.1',
|
||||
remoteAddress: '127.0.0.1',
|
||||
remotePort: socket.localPort});
|
||||
is(remoteSocket.localAddress, '127.0.0.1', 'expected local address');
|
||||
is(remoteSocket.remoteAddress, '127.0.0.1', 'expected remote address');
|
||||
is(remoteSocket.remotePort, socket.localPort, 'expected remote port');
|
||||
is(remoteSocket.addressReuse, false, 'expected address not reusable');
|
||||
is(remoteSocket.loopback, true, 'expected loopback mode is on');
|
||||
|
||||
return remoteSocket.opened.then(function() {
|
||||
remoteSocket.send(HELLO_WORLD);
|
||||
return new Promise(function(resolve, reject) {
|
||||
socket.addEventListener('message', function recv_callback(msg) {
|
||||
socket.removeEventListener('message', recv_callback);
|
||||
let recvData= String.fromCharCode.apply(null, new Uint8Array(msg.data));
|
||||
is(msg.remotePort, remoteSocket.localPort, 'expected packet from ' + remoteSocket.localPort);
|
||||
is(recvData, HELLO_WORLD, 'expected same string data');
|
||||
resolve(socket);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function testClose(socket) {
|
||||
info('test for close');
|
||||
|
||||
socket.close();
|
||||
is(socket.readyState, 'closed', 'expect ready state to be "closed"');
|
||||
try {
|
||||
socket.send(HELLO_WORLD, '127.0.0.1', socket.localPort);
|
||||
ok(false, 'unexpect to send successfully');
|
||||
} catch (e) {
|
||||
ok(true, 'expected send fail after socket closed');
|
||||
}
|
||||
|
||||
return socket.closed.then(function() {
|
||||
ok(true, 'expected closedPromise is resolved after socket.close()');
|
||||
});
|
||||
}
|
||||
|
||||
function testMulticast() {
|
||||
info('test for multicast');
|
||||
|
||||
let socket = new UDPSocket({loopback: true});
|
||||
|
||||
const MCAST_ADDRESS = '224.0.0.255';
|
||||
socket.joinMulticastGroup(MCAST_ADDRESS);
|
||||
|
||||
return socket.opened.then(function() {
|
||||
socket.send(HELLO_WORLD, MCAST_ADDRESS, socket.localPort);
|
||||
|
||||
return new Promise(function(resolve, reject) {
|
||||
socket.addEventListener('message', function recv_callback(msg) {
|
||||
socket.removeEventListener('message', recv_callback);
|
||||
let recvData= String.fromCharCode.apply(null, new Uint8Array(msg.data));
|
||||
is(msg.remotePort, socket.localPort, 'expected packet from ' + socket.localPort);
|
||||
is(recvData, HELLO_WORLD, 'expected same string data');
|
||||
socket.leaveMulticastGroup(MCAST_ADDRESS);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function testInvalidUDPOptions() {
|
||||
info('test for invalid UDPOptions');
|
||||
try {
|
||||
let socket = new UDPSocket({localAddress: 'not-a-valid-address'});
|
||||
ok(false, 'should not create an UDPSocket with an invalid localAddress');
|
||||
} catch (e) {
|
||||
is(e.name, 'InvalidAccessError', 'expected InvalidAccessError will be thrown if localAddress is not a valid IPv4/6 address');
|
||||
}
|
||||
|
||||
try {
|
||||
let socket = new UDPSocket({localPort: 0});
|
||||
ok(false, 'should not create an UDPSocket with an invalid localPort');
|
||||
} catch (e) {
|
||||
is(e.name, 'InvalidAccessError', 'expected InvalidAccessError will be thrown if localPort is not a valid port number');
|
||||
}
|
||||
|
||||
try {
|
||||
let socket = new UDPSocket({remotePort: 0});
|
||||
ok(false, 'should not create an UDPSocket with an invalid remotePort');
|
||||
} catch (e) {
|
||||
is(e.name, 'InvalidAccessError', 'expected InvalidAccessError will be thrown if localPort is not a valid port number');
|
||||
}
|
||||
}
|
||||
|
||||
function testOpenFailed() {
|
||||
info('test for falied on open');
|
||||
|
||||
//according to RFC5737, address block 192.0.2.0/24 should not be used in both local and public contexts
|
||||
let socket = new UDPSocket({localAddress: '192.0.2.0'});
|
||||
|
||||
return socket.opened.then(function() {
|
||||
ok(false, 'should not resolve openedPromise while fail to bind socket');
|
||||
socket.close();
|
||||
}).catch(function(reason) {
|
||||
is(reason.name, 'NetworkError', 'expected openedPromise to be rejected while fail to bind socket');
|
||||
});
|
||||
}
|
||||
|
||||
function testSendBeforeOpen() {
|
||||
info('test for send before open');
|
||||
|
||||
let socket = new UDPSocket();
|
||||
|
||||
try {
|
||||
socket.send(HELLO_WORLD, '127.0.0.1', 9);
|
||||
ok(false, 'unexpect to send successfully');
|
||||
} catch (e) {
|
||||
ok(true, 'expected send fail before openedPromise is resolved');
|
||||
}
|
||||
|
||||
return socket.opened.then(function() {
|
||||
socket.close();
|
||||
});
|
||||
}
|
||||
|
||||
function testCloseBeforeOpened() {
|
||||
info('test for close socket before opened');
|
||||
|
||||
let socket = new UDPSocket();
|
||||
socket.opened.then(function() {
|
||||
ok(false, 'should not resolve openedPromise if it has already been closed');
|
||||
}).catch(function(reason) {
|
||||
is(reason.name, 'AbortError', 'expected openedPromise to be rejected while socket is closed during opening');
|
||||
});
|
||||
|
||||
return socket.close().then(function() {
|
||||
ok(true, 'expected closedPromise to be resolved');
|
||||
}).then(socket.opened);
|
||||
}
|
||||
|
||||
function testOpenWithoutClose() {
|
||||
info('test for open without close');
|
||||
|
||||
let closed = [];
|
||||
for (let i = 0; i < 50; i++) {
|
||||
let socket = new UDPSocket();
|
||||
closed.push(socket.closed);
|
||||
}
|
||||
|
||||
SpecialPowers.gc();
|
||||
info('all unrefereced socket should be closed right after GC');
|
||||
|
||||
return Promise.all(closed);
|
||||
}
|
||||
|
||||
function testBFCache() {
|
||||
info('test for bfcache behavior');
|
||||
|
||||
let socket = new UDPSocket();
|
||||
|
||||
return socket.opened.then(function() {
|
||||
let iframe = document.getElementById('iframe');
|
||||
SpecialPowers.wrap(iframe).mozbrowser = true;
|
||||
iframe.src = 'file_udpsocket_iframe.html?' + socket.localPort;
|
||||
|
||||
return new Promise(function(resolve, reject) {
|
||||
socket.addEventListener('message', function recv_callback(msg) {
|
||||
socket.removeEventListener('message', recv_callback);
|
||||
iframe.src = 'about:blank';
|
||||
iframe.addEventListener('load', function onload() {
|
||||
iframe.removeEventListener('load', onload);
|
||||
socket.send(HELLO_WORLD, '127.0.0.1', msg.remotePort);
|
||||
|
||||
function recv_again_callback(msg) {
|
||||
socket.removeEventListener('message', recv_again_callback);
|
||||
ok(false, 'should not receive packet after page unload');
|
||||
}
|
||||
|
||||
socket.addEventListener('message', recv_again_callback);
|
||||
|
||||
let timeout = setTimeout(function() {
|
||||
socket.removeEventListener('message', recv_again_callback);
|
||||
socket.close();
|
||||
resolve();
|
||||
}, 5000);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function runTest() {
|
||||
testOpen()
|
||||
.then(testSendString)
|
||||
.then(testSendArrayBuffer)
|
||||
.then(testSendArrayBufferView)
|
||||
.then(testSendBlob)
|
||||
.then(testSendBigArray)
|
||||
.then(testSendBigBlob)
|
||||
.then(testUDPOptions)
|
||||
.then(testClose)
|
||||
.then(testMulticast)
|
||||
.then(testInvalidUDPOptions)
|
||||
.then(testOpenFailed)
|
||||
.then(testSendBeforeOpen)
|
||||
.then(testCloseBeforeOpened)
|
||||
.then(testOpenWithoutClose)
|
||||
.then(testBFCache)
|
||||
.then(function() {
|
||||
info('test finished');
|
||||
SimpleTest.finish();
|
||||
})
|
||||
.catch(function(err) {
|
||||
ok(false, 'test failed due to: ' + err);
|
||||
SimpleTest.finish();
|
||||
});
|
||||
}
|
||||
|
||||
window.addEventListener('load', function () {
|
||||
SpecialPowers.pushPrefEnv({
|
||||
'set': [
|
||||
['dom.udpsocket.enabled', true],
|
||||
['browser.sessionhistory.max_total_viewers', 10]
|
||||
]
|
||||
}, runTest);
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
1093
dom/network/tests/unit_stats/test_networkstats_db.js
Normal file
1093
dom/network/tests/unit_stats/test_networkstats_db.js
Normal file
File diff suppressed because it is too large
Load diff
290
dom/network/tests/unit_stats/test_networkstats_service.js
Normal file
290
dom/network/tests/unit_stats/test_networkstats_service.js
Normal file
|
|
@ -0,0 +1,290 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
var {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
||||
|
||||
const NETWORK_STATUS_READY = 0;
|
||||
const NETWORK_STATUS_STANDBY = 1;
|
||||
const NETWORK_STATUS_AWAY = 2;
|
||||
|
||||
const QUEUE_TYPE_UPDATE_STATS = 0;
|
||||
|
||||
var wifiId = '00';
|
||||
|
||||
function getNetworks(callback) {
|
||||
NetworkStatsService._db.getAvailableNetworks(function onGetNetworks(aError, aResult) {
|
||||
callback(aError, aResult);
|
||||
});
|
||||
}
|
||||
|
||||
add_test(function test_clearDB() {
|
||||
getNetworks(function onGetNetworks(error, result) {
|
||||
do_check_eq(error, null);
|
||||
var networks = result;
|
||||
networks.forEach(function(network, index) {
|
||||
networks[index] = {network: network, networkId: NetworkStatsService.getNetworkId(network.id, network.type)};
|
||||
}, this);
|
||||
|
||||
NetworkStatsService._db.clearStats(networks, function onDBCleared(error, result) {
|
||||
do_check_eq(error, null);
|
||||
run_next_test();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function getNetworkId(callback) {
|
||||
getNetworks(function onGetNetworks(error, result) {
|
||||
do_check_eq(error, null);
|
||||
var netId = NetworkStatsService.getNetworkId(result[0].id, result[0].type);
|
||||
callback(null, netId);
|
||||
});
|
||||
}
|
||||
|
||||
add_test(function test_networkStatsAvailable_ok() {
|
||||
getNetworkId(function onGetId(error, result) {
|
||||
do_check_eq(error, null);
|
||||
var netId = result;
|
||||
NetworkStatsService.networkStatsAvailable(function (success, msg) {
|
||||
do_check_eq(success, true);
|
||||
run_next_test();
|
||||
}, netId, true, 1234, 4321, Date.now());
|
||||
});
|
||||
});
|
||||
|
||||
add_test(function test_networkStatsAvailable_failure() {
|
||||
getNetworkId(function onGetId(error, result) {
|
||||
do_check_eq(error, null);
|
||||
var netId = result;
|
||||
NetworkStatsService.networkStatsAvailable(function (success, msg) {
|
||||
do_check_eq(success, false);
|
||||
run_next_test();
|
||||
}, netId, false, 1234, 4321, Date.now());
|
||||
});
|
||||
});
|
||||
|
||||
add_test(function test_update_invalidNetwork() {
|
||||
NetworkStatsService.update(-1, function (success, msg) {
|
||||
do_check_eq(success, false);
|
||||
do_check_eq(msg, "Invalid network -1");
|
||||
run_next_test();
|
||||
});
|
||||
});
|
||||
|
||||
add_test(function test_update() {
|
||||
getNetworkId(function onGetId(error, result) {
|
||||
do_check_eq(error, null);
|
||||
var netId = result;
|
||||
NetworkStatsService.update(netId, function (success, msg) {
|
||||
do_check_eq(success, true);
|
||||
run_next_test();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
add_test(function test_updateQueueIndex() {
|
||||
NetworkStatsService.updateQueue = [{netId: 0, callbacks: null, queueType: QUEUE_TYPE_UPDATE_STATS},
|
||||
{netId: 1, callbacks: null, queueType: QUEUE_TYPE_UPDATE_STATS},
|
||||
{netId: 2, callbacks: null, queueType: QUEUE_TYPE_UPDATE_STATS},
|
||||
{netId: 3, callbacks: null, queueType: QUEUE_TYPE_UPDATE_STATS},
|
||||
{netId: 4, callbacks: null, queueType: QUEUE_TYPE_UPDATE_STATS}];
|
||||
var index = NetworkStatsService.updateQueueIndex(3);
|
||||
do_check_eq(index, 3);
|
||||
index = NetworkStatsService.updateQueueIndex(10);
|
||||
do_check_eq(index, -1);
|
||||
|
||||
NetworkStatsService.updateQueue = [];
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_updateAllStats() {
|
||||
NetworkStatsService._networks[wifiId].status = NETWORK_STATUS_READY;
|
||||
NetworkStatsService.updateAllStats(function(success, msg) {
|
||||
do_check_eq(success, true);
|
||||
NetworkStatsService._networks[wifiId].status = NETWORK_STATUS_STANDBY;
|
||||
NetworkStatsService.updateAllStats(function(success, msg) {
|
||||
do_check_eq(success, true);
|
||||
NetworkStatsService._networks[wifiId].status = NETWORK_STATUS_AWAY;
|
||||
NetworkStatsService.updateAllStats(function(success, msg) {
|
||||
do_check_eq(success, true);
|
||||
run_next_test();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
add_test(function test_updateStats_ok() {
|
||||
getNetworkId(function onGetId(error, result) {
|
||||
do_check_eq(error, null);
|
||||
var netId = result;
|
||||
NetworkStatsService.updateStats(netId, function(success, msg){
|
||||
do_check_eq(success, true);
|
||||
run_next_test();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
add_test(function test_updateStats_failure() {
|
||||
NetworkStatsService.updateStats(-1, function(success, msg){
|
||||
do_check_eq(success, false);
|
||||
run_next_test();
|
||||
});
|
||||
});
|
||||
|
||||
// Define Mockup function to simulate a request to netd
|
||||
function MockNetdRequest(aCallback) {
|
||||
var timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
||||
var event = {
|
||||
notify: function (timer) {
|
||||
aCallback();
|
||||
}
|
||||
};
|
||||
|
||||
timer.initWithCallback(event, 100, Ci.nsITimer.TYPE_ONE_SHOT);
|
||||
}
|
||||
|
||||
add_test(function test_queue() {
|
||||
|
||||
// Overwrite update function of NetworkStatsService to avoid netd errors due to use
|
||||
// fake interfaces. First, original function is stored to restore it at the end of the
|
||||
// test.
|
||||
var updateFunctionBackup = NetworkStatsService.update;
|
||||
|
||||
NetworkStatsService.update = function update(aNetId, aCallback) {
|
||||
MockNetdRequest(function () {
|
||||
if (aCallback) {
|
||||
aCallback(true, "ok");
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// Fill networks with fake network interfaces to enable netd async requests.
|
||||
var network = {id: "1234", type: Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE};
|
||||
var netId1 = NetworkStatsService.getNetworkId(network.id, network.type);
|
||||
NetworkStatsService._networks[netId1] = { network: network,
|
||||
interfaceName: "net1" };
|
||||
|
||||
network = {id: "5678", type: Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE};
|
||||
var netId2 = NetworkStatsService.getNetworkId(network.id, network.type);
|
||||
NetworkStatsService._networks[netId2] = { network: network,
|
||||
interfaceName: "net2" };
|
||||
|
||||
NetworkStatsService.updateStats(netId1);
|
||||
NetworkStatsService.updateStats(netId2);
|
||||
do_check_eq(NetworkStatsService.updateQueue.length, 2);
|
||||
do_check_eq(NetworkStatsService.updateQueue[0].callbacks.length, 1);
|
||||
|
||||
var i = 0;
|
||||
var updateCount = 0;
|
||||
var callback = function(success, msg) {
|
||||
i++;
|
||||
if (i >= updateCount) {
|
||||
NetworkStatsService.update = updateFunctionBackup;
|
||||
run_next_test();
|
||||
}
|
||||
};
|
||||
|
||||
NetworkStatsService.updateStats(netId1, callback);
|
||||
updateCount++;
|
||||
NetworkStatsService.updateStats(netId2, callback);
|
||||
updateCount++;
|
||||
|
||||
do_check_eq(NetworkStatsService.updateQueue.length, 2);
|
||||
do_check_eq(NetworkStatsService.updateQueue[0].callbacks.length, 2);
|
||||
do_check_eq(NetworkStatsService.updateQueue[0].callbacks[0], null);
|
||||
do_check_neq(NetworkStatsService.updateQueue[0].callbacks[1], null);
|
||||
});
|
||||
|
||||
add_test(function test_getAlarmQuota() {
|
||||
let alarm = { networkId: wifiId, absoluteThreshold: 10000 };
|
||||
|
||||
NetworkStatsService._getAlarmQuota(alarm, function onSet(error, quota){
|
||||
do_check_eq(error, null);
|
||||
do_check_neq(quota, undefined);
|
||||
do_check_eq(alarm.absoluteThreshold, alarm.relativeThreshold);
|
||||
run_next_test();
|
||||
});
|
||||
});
|
||||
|
||||
var testPageURL = "http://test.com";
|
||||
var testManifestURL = "http://test.com/manifest.webapp";
|
||||
|
||||
add_test(function test_setAlarm() {
|
||||
let alarm = { id: null,
|
||||
networkId: wifiId,
|
||||
threshold: 10000,
|
||||
absoluteThreshold: null,
|
||||
alarmStart: null,
|
||||
alarmEnd: null,
|
||||
data: null,
|
||||
pageURL: testPageURL,
|
||||
manifestURL: testManifestURL };
|
||||
|
||||
NetworkStatsService._setAlarm(alarm, function onSet(error, result) {
|
||||
do_check_eq(result, 1);
|
||||
run_next_test();
|
||||
});
|
||||
});
|
||||
|
||||
add_test(function test_setAlarm_invalid_threshold() {
|
||||
let alarm = { id: null,
|
||||
networkId: wifiId,
|
||||
threshold: -10000,
|
||||
absoluteThreshold: null,
|
||||
alarmStart: null,
|
||||
alarmEnd: null,
|
||||
data: null,
|
||||
pageURL: testPageURL,
|
||||
manifestURL: testManifestURL };
|
||||
|
||||
NetworkStatsService._networks[wifiId].status = NETWORK_STATUS_READY;
|
||||
|
||||
NetworkStatsService._setAlarm(alarm, function onSet(error, result) {
|
||||
do_check_eq(error, "InvalidStateError");
|
||||
run_next_test();
|
||||
});
|
||||
});
|
||||
|
||||
add_test(function test_fireAlarm() {
|
||||
// Add a fake alarm into database.
|
||||
let alarm = { id: null,
|
||||
networkId: wifiId,
|
||||
threshold: 10000,
|
||||
absoluteThreshold: null,
|
||||
alarmStart: null,
|
||||
alarmEnd: null,
|
||||
data: null,
|
||||
pageURL: testPageURL,
|
||||
manifestURL: testManifestURL };
|
||||
|
||||
// Set wifi status to standby to avoid connecting to netd when adding an alarm.
|
||||
NetworkStatsService._networks[wifiId].status = NETWORK_STATUS_STANDBY;
|
||||
|
||||
NetworkStatsService._db.addAlarm(alarm, function addSuccessCb(error, newId) {
|
||||
NetworkStatsService._db.getAlarms(Ci.nsINetworkInfo.NETWORK_TYPE_WIFI,
|
||||
testManifestURL, function onGet(error, result) {
|
||||
do_check_eq(error, null);
|
||||
do_check_eq(result.length, 1);
|
||||
|
||||
// Result of getAlarms is based on expected child's data format, so
|
||||
// some changes are needed to be able to use it.
|
||||
result[0].networkId = wifiId;
|
||||
result[0].pageURL = testPageURL;
|
||||
result[0].manifestURL = testManifestURL;
|
||||
|
||||
NetworkStatsService._fireAlarm(result[0], false);
|
||||
NetworkStatsService._db.getAlarms(Ci.nsINetworkInfo.NETWORK_TYPE_WIFI,
|
||||
testManifestURL, function onGet(error, result) {
|
||||
do_check_eq(error, undefined);
|
||||
do_check_eq(result.length, 0);
|
||||
run_next_test();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function run_test() {
|
||||
do_get_profile();
|
||||
|
||||
Cu.import("resource://gre/modules/NetworkStatsService.jsm");
|
||||
run_next_test();
|
||||
}
|
||||
233
dom/network/tests/unit_stats/test_networkstats_service_proxy.js
Normal file
233
dom/network/tests/unit_stats/test_networkstats_service_proxy.js
Normal file
|
|
@ -0,0 +1,233 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
var {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "nssProxy",
|
||||
"@mozilla.org/networkstatsServiceProxy;1",
|
||||
"nsINetworkStatsServiceProxy");
|
||||
|
||||
function mokConvertNetworkInfo() {
|
||||
NetworkStatsService.convertNetworkInfo = function(aNetworkInfo) {
|
||||
if (aNetworkInfo.type != Ci.nsINetworkInfo.NETWORK_TYPE_MOBILE &&
|
||||
aNetworkInfo.type != Ci.nsINetworkInfo.NETWORK_TYPE_WIFI) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let id = '0';
|
||||
if (aNetworkInfo.type == Ci.nsINetworkInfo.NETWORK_TYPE_MOBILE) {
|
||||
id = '1234'
|
||||
}
|
||||
|
||||
let netId = this.getNetworkId(id, aNetworkInfo.type);
|
||||
|
||||
if (!this._networks[netId]) {
|
||||
this._networks[netId] = Object.create(null);
|
||||
this._networks[netId].network = { id: id,
|
||||
type: aNetworkInfo.type };
|
||||
}
|
||||
|
||||
return netId;
|
||||
};
|
||||
}
|
||||
|
||||
add_test(function test_saveAppStats() {
|
||||
var cachedStats = NetworkStatsService.cachedStats;
|
||||
var timestamp = NetworkStatsService.cachedStatsDate.getTime();
|
||||
|
||||
// Create to fake nsINetworkInfos. As nsINetworkInfo can not be instantiated,
|
||||
// these two vars will emulate it by filling the properties that will be used.
|
||||
var wifi = {type: Ci.nsINetworkInfo.NETWORK_TYPE_WIFI, id: "0"};
|
||||
var mobile = {type: Ci.nsINetworkInfo.NETWORK_TYPE_MOBILE, id: "1234"};
|
||||
|
||||
// Insert fake mobile network info in NetworkStatsService
|
||||
var mobileNetId = NetworkStatsService.getNetworkId(mobile.id, mobile.type);
|
||||
|
||||
do_check_eq(Object.keys(cachedStats).length, 0);
|
||||
|
||||
nssProxy.saveAppStats(1, false, wifi, timestamp, 10, 20, false,
|
||||
function (success, message) {
|
||||
do_check_eq(success, true);
|
||||
nssProxy.saveAppStats(1, false, mobile, timestamp, 10, 20, false,
|
||||
function (success, message) {
|
||||
var key1 = 1 + "" + false + "" + NetworkStatsService.getNetworkId(wifi.id, wifi.type);
|
||||
var key2 = 1 + "" + false + "" + mobileNetId + "";
|
||||
|
||||
do_check_eq(Object.keys(cachedStats).length, 2);
|
||||
do_check_eq(cachedStats[key1].appId, 1);
|
||||
do_check_eq(cachedStats[key1].isInBrowser, false);
|
||||
do_check_eq(cachedStats[key1].serviceType.length, 0);
|
||||
do_check_eq(cachedStats[key1].networkId, wifi.id);
|
||||
do_check_eq(cachedStats[key1].networkType, wifi.type);
|
||||
do_check_eq(cachedStats[key1].date.getTime(), timestamp);
|
||||
do_check_eq(cachedStats[key1].rxBytes, 10);
|
||||
do_check_eq(cachedStats[key1].txBytes, 20);
|
||||
do_check_eq(cachedStats[key2].appId, 1);
|
||||
do_check_eq(cachedStats[key1].serviceType.length, 0);
|
||||
do_check_eq(cachedStats[key2].networkId, mobile.id);
|
||||
do_check_eq(cachedStats[key2].networkType, mobile.type);
|
||||
do_check_eq(cachedStats[key2].date.getTime(), timestamp);
|
||||
do_check_eq(cachedStats[key2].rxBytes, 10);
|
||||
do_check_eq(cachedStats[key2].txBytes, 20);
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
add_test(function test_saveServiceStats() {
|
||||
var timestamp = NetworkStatsService.cachedStatsDate.getTime();
|
||||
|
||||
// Create to fake nsINetworkInfos. As nsINetworkInfo can not be instantiated,
|
||||
// these two vars will emulate it by filling the properties that will be used.
|
||||
var wifi = {type: Ci.nsINetworkInfo.NETWORK_TYPE_WIFI, id: "0"};
|
||||
var mobile = {type: Ci.nsINetworkInfo.NETWORK_TYPE_MOBILE, id: "1234"};
|
||||
|
||||
// Insert fake mobile network info in NetworkStatsService
|
||||
var mobileNetId = NetworkStatsService.getNetworkId(mobile.id, mobile.type);
|
||||
|
||||
NetworkStatsService.updateCachedStats(function (success, msg) {
|
||||
do_check_eq(success, true);
|
||||
|
||||
var cachedStats = NetworkStatsService.cachedStats;
|
||||
do_check_eq(Object.keys(cachedStats).length, 0);
|
||||
|
||||
var serviceType = 'FakeType';
|
||||
nssProxy.saveServiceStats(serviceType, wifi, timestamp, 10, 20, false,
|
||||
function (success, message) {
|
||||
do_check_eq(success, true);
|
||||
nssProxy.saveServiceStats(serviceType, mobile, timestamp, 10, 20, false,
|
||||
function (success, message) {
|
||||
do_check_eq(success, true);
|
||||
var key1 = 0 + "" + false + "" + serviceType +
|
||||
NetworkStatsService.getNetworkId(wifi.id, wifi.type);
|
||||
var key2 = 0 + "" + false + "" + serviceType + mobileNetId + "";
|
||||
|
||||
do_check_eq(Object.keys(cachedStats).length, 2);
|
||||
do_check_eq(cachedStats[key1].appId, 0);
|
||||
do_check_eq(cachedStats[key1].isInBrowser, false);
|
||||
do_check_eq(cachedStats[key1].serviceType, serviceType);
|
||||
do_check_eq(cachedStats[key1].networkId, wifi.id);
|
||||
do_check_eq(cachedStats[key1].networkType, wifi.type);
|
||||
do_check_eq(cachedStats[key1].date.getTime(), timestamp);
|
||||
do_check_eq(cachedStats[key1].rxBytes, 10);
|
||||
do_check_eq(cachedStats[key1].txBytes, 20);
|
||||
do_check_eq(cachedStats[key2].appId, 0);
|
||||
do_check_eq(cachedStats[key1].serviceType, serviceType);
|
||||
do_check_eq(cachedStats[key2].networkId, mobile.id);
|
||||
do_check_eq(cachedStats[key2].networkType, mobile.type);
|
||||
do_check_eq(cachedStats[key2].date.getTime(), timestamp);
|
||||
do_check_eq(cachedStats[key2].rxBytes, 10);
|
||||
do_check_eq(cachedStats[key2].txBytes, 20);
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
add_test(function test_saveStatsWithDifferentDates() {
|
||||
var today = NetworkStatsService.cachedStatsDate;
|
||||
var tomorrow = new Date(today.getTime() + (24 * 60 * 60 * 1000));
|
||||
|
||||
var mobile = {type: Ci.nsINetworkInfo.NETWORK_TYPE_MOBILE, id: "1234"};
|
||||
|
||||
NetworkStatsService.updateCachedStats(function (success, message) {
|
||||
do_check_eq(success, true);
|
||||
|
||||
do_check_eq(Object.keys(NetworkStatsService.cachedStats).length, 0);
|
||||
nssProxy.saveAppStats(1, false, mobile, today.getTime(), 10, 20, false,
|
||||
function (success, message) {
|
||||
do_check_eq(success, true);
|
||||
nssProxy.saveAppStats(2, false, mobile, tomorrow.getTime(), 30, 40, false,
|
||||
function (success, message) {
|
||||
do_check_eq(success, true);
|
||||
|
||||
var cachedStats = NetworkStatsService.cachedStats;
|
||||
var key = 2 + "" + false + "" +
|
||||
NetworkStatsService.getNetworkId(mobile.id, mobile.type);
|
||||
do_check_eq(Object.keys(cachedStats).length, 1);
|
||||
do_check_eq(cachedStats[key].appId, 2);
|
||||
do_check_eq(cachedStats[key].isInBrowser, false);
|
||||
do_check_eq(cachedStats[key].networkId, mobile.id);
|
||||
do_check_eq(cachedStats[key].networkType, mobile.type);
|
||||
do_check_eq(cachedStats[key].date.getTime(), tomorrow.getTime());
|
||||
do_check_eq(cachedStats[key].rxBytes, 30);
|
||||
do_check_eq(cachedStats[key].txBytes, 40);
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
add_test(function test_saveStatsWithMaxCachedTraffic() {
|
||||
var timestamp = NetworkStatsService.cachedStatsDate.getTime();
|
||||
var maxtraffic = NetworkStatsService.maxCachedTraffic;
|
||||
var wifi = {type: Ci.nsINetworkInfo.NETWORK_TYPE_WIFI, id: "0"};
|
||||
|
||||
NetworkStatsService.updateCachedStats(function (success, message) {
|
||||
do_check_eq(success, true);
|
||||
|
||||
var cachedStats = NetworkStatsService.cachedStats;
|
||||
do_check_eq(Object.keys(cachedStats).length, 0);
|
||||
nssProxy.saveAppStats(1, false, wifi, timestamp, 10, 20, false,
|
||||
function (success, message) {
|
||||
do_check_eq(success, true);
|
||||
do_check_eq(Object.keys(cachedStats).length, 1);
|
||||
nssProxy.saveAppStats(1, false, wifi, timestamp, maxtraffic, 20, false,
|
||||
function (success, message) {
|
||||
do_check_eq(success, true);
|
||||
do_check_eq(Object.keys(cachedStats).length, 0);
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
add_test(function test_saveAppStats() {
|
||||
var cachedStats = NetworkStatsService.cachedStats;
|
||||
var timestamp = NetworkStatsService.cachedStatsDate.getTime();
|
||||
|
||||
// Create to fake nsINetworkInfo. As nsINetworkInfo can not
|
||||
// be instantiated, these two vars will emulate it by filling the properties
|
||||
// that will be used.
|
||||
var wifi = {type: Ci.nsINetworkInfo.NETWORK_TYPE_WIFI, id: "0"};
|
||||
var mobile = {type: Ci.nsINetworkInfo.NETWORK_TYPE_MOBILE, id: "1234"};
|
||||
|
||||
// Insert fake mobile network interface in NetworkStatsService
|
||||
var mobileNetId = NetworkStatsService.getNetworkId(mobile.id, mobile.type);
|
||||
|
||||
do_check_eq(Object.keys(cachedStats).length, 0);
|
||||
|
||||
nssProxy.saveAppStats(1, false, wifi, timestamp, 10, 20, false, { notify:
|
||||
function (success, message) {
|
||||
do_check_eq(success, true);
|
||||
var iterations = 10;
|
||||
var counter = 0;
|
||||
var callback = function (success, message) {
|
||||
if (counter == iterations - 1)
|
||||
run_next_test();
|
||||
counter++;
|
||||
};
|
||||
|
||||
for (var i = 0; i < iterations; i++) {
|
||||
nssProxy.saveAppStats(1, false, mobile, timestamp, 10, 20, false, callback);
|
||||
}
|
||||
}});
|
||||
});
|
||||
|
||||
function run_test() {
|
||||
do_get_profile();
|
||||
|
||||
Cu.import("resource://gre/modules/NetworkStatsService.jsm");
|
||||
|
||||
// Function convertNetworkInfo of NetworkStatsService causes errors when dealing
|
||||
// with RIL to get the iccid, so overwrite it.
|
||||
mokConvertNetworkInfo();
|
||||
|
||||
run_next_test();
|
||||
}
|
||||
7
dom/network/tests/unit_stats/xpcshell.ini
Normal file
7
dom/network/tests/unit_stats/xpcshell.ini
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
[DEFAULT]
|
||||
head =
|
||||
tail =
|
||||
|
||||
[test_networkstats_service.js]
|
||||
[test_networkstats_service_proxy.js]
|
||||
[test_networkstats_db.js]
|
||||
Loading…
Add table
Add a link
Reference in a new issue