Merge mozilla-central and b2g-inbound

--HG--
rename : content/events/src/nsDOMEvent.h => dom/events/nsDOMEvent.h
This commit is contained in:
Ed Morley 2014-01-15 18:09:12 +00:00
commit c73e589fc3
54 changed files with 3359 additions and 244 deletions

View File

@ -307,12 +307,10 @@ pref("image.onload.decode.limit", 24); /* don't decode more than 24 images eager
// XXX this isn't a good check for "are touch events supported", but
// we don't really have a better one at the moment.
#ifdef MOZ_WIDGET_GONK
// enable touch events interfaces
pref("dom.w3c_touch_events.enabled", 1);
pref("dom.w3c_touch_events.safetyX", 0); // escape borders in units of 1/240"
pref("dom.w3c_touch_events.safetyY", 120); // escape borders in units of 1/240"
#endif
#ifdef MOZ_SAFE_BROWSING
// Safe browsing does nothing unless this pref is set

View File

@ -0,0 +1,10 @@
window.addEventListener("ContentStart", function(evt) {
// Enable touch event shim on desktop that translates mouse events
// into touch ones
let require = Cu.import("resource://gre/modules/devtools/Loader.jsm", {})
.devtools.require;
let { TouchEventHandler } = require("devtools/touch-events");
let touchEventHandler = new TouchEventHandler(shell.contentBrowser);
touchEventHandler.start();
});

View File

@ -19,7 +19,9 @@
src="chrome://browser/content/shell.js"> </script>
#ifndef MOZ_WIDGET_GONK
<!-- various task that has to happen only on desktop -->
<script type="application/javascript;version=1.8"
src="chrome://browser/content/desktop.js"> </script>
<!-- this script handles the screen argument for desktop builds -->
<script type="application/javascript;version=1.8"
src="chrome://browser/content/screen.js"> </script>

View File

@ -14,6 +14,7 @@ chrome.jar:
* content/shell.html (content/shell.html)
* content/shell.js (content/shell.js)
#ifndef ANDROID
content/desktop.js (content/desktop.js)
content/screen.js (content/screen.js)
content/runapp.js (content/runapp.js)
#endif

View File

@ -1,4 +1,4 @@
{
"revision": "25a3b96e9c5ff89b69b29007462bfd056ad5bf53",
"revision": "ae5c954ca1b8047cfa932f905d6498b47bb44ac5",
"repo_path": "/integration/gaia-central"
}

View File

@ -402,6 +402,8 @@
#ifdef MOZ_WIDGET_GONK
@BINPATH@/components/DOMWifiManager.js
@BINPATH@/components/DOMWifiManager.manifest
@BINPATH@/components/DOMWifiP2pManager.js
@BINPATH@/components/DOMWifiP2pManager.manifest
@BINPATH@/components/NetworkInterfaceListService.js
@BINPATH@/components/NetworkInterfaceListService.manifest
@BINPATH@/components/NetworkManager.js

View File

@ -25,11 +25,11 @@ function test() {
function testWithNoTouch() {
let div = content.document.querySelector("div");
let x = 2, y = 2;
EventUtils.synthesizeMouse(div, x, y, {type: "mousedown"}, content);
EventUtils.synthesizeMouse(div, x, y, {type: "mousedown", isSynthesized: false}, content);
x += 20; y += 10;
EventUtils.synthesizeMouse(div, x, y, {type: "mousemove"}, content);
EventUtils.synthesizeMouse(div, x, y, {type: "mousemove", isSynthesized: false}, content);
is(div.style.transform, "", "touch didn't work");
EventUtils.synthesizeMouse(div, x, y, {type: "mouseup"}, content);
EventUtils.synthesizeMouse(div, x, y, {type: "mouseup", isSynthesized: false}, content);
testWithTouch();
}
@ -37,11 +37,11 @@ function test() {
gBrowser.selectedTab.__responsiveUI.enableTouch();
let div = content.document.querySelector("div");
let x = 2, y = 2;
EventUtils.synthesizeMouse(div, x, y, {type: "mousedown"}, content);
EventUtils.synthesizeMouse(div, x, y, {type: "mousedown", isSynthesized: false}, content);
x += 20; y += 10;
EventUtils.synthesizeMouse(div, x, y, {type: "mousemove"}, content);
EventUtils.synthesizeMouse(div, x, y, {type: "mousemove", isSynthesized: false}, content);
is(div.style.transform, "translate(20px, 10px)", "touch worked");
EventUtils.synthesizeMouse(div, x, y, {type: "mouseup"}, content);
EventUtils.synthesizeMouse(div, x, y, {type: "mouseup", isSynthesized: false}, content);
is(div.style.transform, "none", "end event worked");
mgr.toggle(window, gBrowser.selectedTab);
}
@ -50,11 +50,11 @@ function test() {
gBrowser.selectedTab.__responsiveUI.disableTouch();
let div = content.document.querySelector("div");
let x = 2, y = 2;
EventUtils.synthesizeMouse(div, x, y, {type: "mousedown"}, content);
EventUtils.synthesizeMouse(div, x, y, {type: "mousedown", isSynthesized: false}, content);
x += 20; y += 10;
EventUtils.synthesizeMouse(div, x, y, {type: "mousemove"}, content);
EventUtils.synthesizeMouse(div, x, y, {type: "mousemove", isSynthesized: false}, content);
is(div.style.transform, "", "touch didn't work");
EventUtils.synthesizeMouse(div, x, y, {type: "mouseup"}, content);
EventUtils.synthesizeMouse(div, x, y, {type: "mouseup", isSynthesized: false}, content);
finishUp();
}

View File

@ -82,6 +82,7 @@ tests.push(function test_redirect_to_file_uri() {
function runNextTest() {
if (!tests.length) {
SimpleTest.finish();
return;
}
tests.shift()();
@ -89,14 +90,8 @@ function runNextTest() {
function runTests() {
SimpleTest.waitForExplicitFinish();
SpecialPowers.addPermission("systemXHR", true, document);
tests.push(function tearDown() {
SpecialPowers.removePermission("systemXHR", document);
SimpleTest.finish();
});
runNextTest();
SpecialPowers.pushPermissions([{'type': 'systemXHR', 'allow': true, 'context': document}], runNextTest);
}
</script>

View File

@ -7,6 +7,7 @@
#include "nsIPrincipal.h"
#include "nsMimeTypes.h"
#include "prlog.h"
#include "mozilla/Preferences.h"
#ifdef MOZ_OGG
#include "OggWriter.h"
@ -92,7 +93,7 @@ MediaEncoder::CreateEncoder(const nsAString& aMIMEType, uint8_t aTrackTypes)
return nullptr;
}
#ifdef MOZ_WEBM_ENCODER
else if (MediaDecoder::IsWebMEnabled() &&
else if (MediaEncoder::IsWebMEncoderEnabled() &&
(aMIMEType.EqualsLiteral(VIDEO_WEBM) ||
(aTrackTypes & ContainerWriter::HAS_VIDEO))) {
if (aTrackTypes & ContainerWriter::HAS_AUDIO) {
@ -107,8 +108,9 @@ MediaEncoder::CreateEncoder(const nsAString& aMIMEType, uint8_t aTrackTypes)
}
#endif //MOZ_WEBM_ENCODER
#ifdef MOZ_OMX_ENCODER
else if (aMIMEType.EqualsLiteral(VIDEO_MP4) ||
(aTrackTypes & ContainerWriter::HAS_VIDEO)) {
else if (MediaEncoder::IsOMXEncoderEnabled() &&
(aMIMEType.EqualsLiteral(VIDEO_MP4) ||
(aTrackTypes & ContainerWriter::HAS_VIDEO))) {
if (aTrackTypes & ContainerWriter::HAS_AUDIO) {
audioEncoder = new OmxAudioTrackEncoder();
NS_ENSURE_TRUE(audioEncoder, nullptr);
@ -296,4 +298,20 @@ MediaEncoder::CopyMetadataToMuxer(TrackEncoder *aTrackEncoder)
return rv;
}
#ifdef MOZ_WEBM_ENCODER
bool
MediaEncoder::IsWebMEncoderEnabled()
{
return Preferences::GetBool("media.encoder.webm.enabled");
}
#endif
#ifdef MOZ_OMX_ENCODER
bool
MediaEncoder::IsOMXEncoderEnabled()
{
return Preferences::GetBool("media.encoder.omx.enabled");
}
#endif
}

View File

@ -128,6 +128,14 @@ public :
return mState == ENCODE_ERROR;
}
#ifdef MOZ_WEBM_ENCODER
static bool IsWebMEncoderEnabled();
#endif
#ifdef MOZ_OMX_ENCODER
static bool IsOMXEncoderEnabled();
#endif
private:
// Get encoded data from trackEncoder and write to muxer
nsresult WriteEncodedDataToMuxer(TrackEncoder *aTrackEncoder);

View File

@ -1778,6 +1778,26 @@ Navigator::HasIccManagerSupport(JSContext* /* unused */,
nsCOMPtr<nsPIDOMWindow> win = GetWindowFromGlobal(aGlobal);
return win && CheckPermission(win, "mobileconnection");
}
/* static */
bool
Navigator::HasWifiManagerSupport(JSContext* /* unused */,
JSObject* aGlobal)
{
// On XBL scope, the global object is NOT |window|. So we have
// to use nsContentUtils::GetObjectPrincipal to get the principal
// and test directly with permission manager.
nsIPrincipal* principal = nsContentUtils::GetObjectPrincipal(aGlobal);
nsCOMPtr<nsIPermissionManager> permMgr =
do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
NS_ENSURE_TRUE(permMgr, false);
uint32_t permission = nsIPermissionManager::DENY_ACTION;
permMgr->TestPermissionFromPrincipal(principal, "wifi-manage", &permission);
return nsIPermissionManager::ALLOW_ACTION == permission;
}
#endif // MOZ_B2G_RIL
#ifdef MOZ_B2G_BT

View File

@ -264,6 +264,8 @@ public:
JSObject* aGlobal);
static bool HasIccManagerSupport(JSContext* /* unused */,
JSObject* aGlobal);
static bool HasWifiManagerSupport(JSContext* /* unused */,
JSObject* aGlobal);
#endif // MOZ_B2G_RIL
#ifdef MOZ_B2G_BT
static bool HasBluetoothSupport(JSContext* /* unused */, JSObject* aGlobal);

View File

@ -604,11 +604,14 @@ nsDOMWindowUtils::SendMouseEvent(const nsAString& aType,
bool aIgnoreRootScrollFrame,
float aPressure,
unsigned short aInputSourceArg,
bool aIsSynthesized,
uint8_t aOptionalArgCount,
bool *aPreventDefault)
{
return SendMouseEventCommon(aType, aX, aY, aButton, aClickCount, aModifiers,
aIgnoreRootScrollFrame, aPressure,
aInputSourceArg, false, aPreventDefault);
aInputSourceArg, false, aPreventDefault,
aOptionalArgCount >= 4 ? aIsSynthesized : true);
}
NS_IMETHODIMP
@ -620,12 +623,15 @@ nsDOMWindowUtils::SendMouseEventToWindow(const nsAString& aType,
int32_t aModifiers,
bool aIgnoreRootScrollFrame,
float aPressure,
unsigned short aInputSourceArg)
unsigned short aInputSourceArg,
bool aIsSynthesized,
uint8_t aOptionalArgCount)
{
PROFILER_LABEL("nsDOMWindowUtils", "SendMouseEventToWindow");
return SendMouseEventCommon(aType, aX, aY, aButton, aClickCount, aModifiers,
aIgnoreRootScrollFrame, aPressure,
aInputSourceArg, true, nullptr);
aInputSourceArg, true, nullptr,
aOptionalArgCount >= 4 ? aIsSynthesized : true);
}
static LayoutDeviceIntPoint
@ -668,7 +674,8 @@ nsDOMWindowUtils::SendMouseEventCommon(const nsAString& aType,
float aPressure,
unsigned short aInputSourceArg,
bool aToWindow,
bool *aPreventDefault)
bool *aPreventDefault,
bool aIsSynthesized)
{
if (!nsContentUtils::IsCallerChrome()) {
return NS_ERROR_DOM_SECURITY_ERR;
@ -715,7 +722,7 @@ nsDOMWindowUtils::SendMouseEventCommon(const nsAString& aType,
event.inputSource = aInputSourceArg;
event.clickCount = aClickCount;
event.time = PR_IntervalNow();
event.mFlags.mIsSynthesizedForTests = true;
event.mFlags.mIsSynthesizedForTests = aIsSynthesized;
nsPresContext* presContext = GetPresContext();
if (!presContext)

View File

@ -46,7 +46,8 @@ protected:
float aPressure,
unsigned short aInputSourceArg,
bool aToWindow,
bool *aPreventDefault);
bool *aPreventDefault,
bool aIsSynthesized);
NS_IMETHOD SendTouchEventCommon(const nsAString& aType,
uint32_t* aIdentifiers,

View File

@ -49,3 +49,4 @@ support-files =
[test_window_extensible.html]
[test_window_indexing.html]
[test_writable-replaceable.html]
[test_domwindowutils.html]

View File

@ -0,0 +1,84 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Test for DOMWindowUtils</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<div id="content" style="display: none"></div>
<pre id="test">
<script type="application/javascript">
SimpleTest.waitForExplicitFinish();
var utils = SpecialPowers.getDOMWindowUtils(window);
function test_sendMouseEventDefaults() {
var x = 1, y = 2, button = 1, clickCount = 2,
modifiers = SpecialPowers.Ci.nsIDOMNSEvent.SHIFT_MASK;
window.addEventListener("mousedown", function listener(evt) {
window.removeEventListener("mousedown", listener);
// Mandatory args
is(evt.clientX, x, "check x");
is(evt.clientY, y, "check y");
is(evt.button, button, "check button");
is(evt.detail, clickCount, "check click count");
is(evt.getModifierState("Shift"), true, "check modifiers");
// Default value for optionals
is(evt.mozPressure, 0, "check pressure");
is(evt.mozInputSource, SpecialPowers.Ci.nsIDOMMouseEvent.MOZ_SOURCE_MOUSE, "check input source");
is(evt.isSynthesized, undefined, "check isSynthesized is undefined in content");
is(SpecialPowers.wrap(evt).isSynthesized, true, "check isSynthesized is true from chrome");
next();
});
// Only pass mandatory arguments and check default values
utils.sendMouseEvent("mousedown", x, y, button, clickCount, modifiers);
}
function test_sendMouseEventOptionals() {
var x = 1, y = 2, button = 1, clickCount = 3,
modifiers = SpecialPowers.Ci.nsIDOMNSEvent.SHIFT_MASK,
pressure = 0.5,
source = SpecialPowers.Ci.nsIDOMMouseEvent.MOZ_SOURCE_KEYBOARD;
window.addEventListener("mouseup", function listener(evt) {
window.removeEventListener("mouseup", listener);
is(evt.mozInputSource, source, "explicit input source is valid");
is(SpecialPowers.wrap(evt).isSynthesized, false, "we can dispatch event that don't look synthesized");
next();
});
// Check explicit value for optional args
utils.sendMouseEvent("mouseup", x, y, button, clickCount, modifiers,
false, pressure, source, false);
}
var tests = [
test_sendMouseEventDefaults,
test_sendMouseEventOptionals
];
function next() {
if (!tests.length) {
SimpleTest.finish();
return;
}
var test = tests.shift();
test();
}
function start() {
SimpleTest.waitForExplicitFinish();
SimpleTest.executeSoon(next);
}
window.addEventListener("load", start);
</script>
</pre>
</body>
</html>

View File

@ -179,6 +179,11 @@ public:
return mEvent->mFlags.mIsTrusted;
}
bool IsSynthesized() const
{
return mEvent->mFlags.mIsSynthesizedForTests;
}
uint64_t TimeStamp() const
{
return mEvent->time;

View File

@ -118,11 +118,8 @@ function test_setComposition() {
function test_endComposition() {
gContext.endComposition('2013').then(function() {
if (gContext.textBeforeCursor + gContext.textAfterCursor == 'Xulei2013') {
ok(true, 'endComposition changed the input field correctly.');
} else {
todo(false, 'endComposition changed the input field incorrectly.');
}
is(gContext.textBeforeCursor + gContext.textAfterCursor, 'Xulei2013',
'endComposition changed the input field correctly.');
test_onSelectionChange();
}, function (e) {
ok(false, 'endComposition failed: ' + e.name);

View File

@ -43,7 +43,7 @@ interface nsIDOMEventTarget;
interface nsIRunnable;
interface nsICompositionStringSynthesizer;
[scriptable, uuid(c6efd629-7282-4f0d-9db8-0fa59c191dd5)]
[scriptable, uuid(fa0fe174-7c07-11e3-a5ba-000c290c393e)]
interface nsIDOMWindowUtils : nsISupports {
/**
@ -249,9 +249,13 @@ interface nsIDOMWindowUtils : nsISupports {
* @param aPressure touch input pressure: 0.0 -> 1.0
* @param aInputSourceArg input source, see nsIDOMMouseEvent for values,
* defaults to mouse input.
* @param aIsSynthesized controls nsIDOMEvent.isSynthesized value
* that helps identifying test related events,
* defaults to true
*
* returns true if the page called prevent default on this event
*/
[optional_argc]
boolean sendMouseEvent(in AString aType,
in float aX,
in float aY,
@ -260,7 +264,8 @@ interface nsIDOMWindowUtils : nsISupports {
in long aModifiers,
[optional] in boolean aIgnoreRootScrollFrame,
[optional] in float aPressure,
[optional] in unsigned short aInputSourceArg);
[optional] in unsigned short aInputSourceArg,
[optional] in boolean aIsSynthesized);
/** Synthesize a touch event. The event types supported are:
* touchstart, touchend, touchmove, and touchcancel
@ -303,6 +308,7 @@ interface nsIDOMWindowUtils : nsISupports {
/** The same as sendMouseEvent but ensures that the event is dispatched to
* this DOM window or one of its children.
*/
[optional_argc]
void sendMouseEventToWindow(in AString aType,
in float aX,
in float aY,
@ -311,7 +317,8 @@ interface nsIDOMWindowUtils : nsISupports {
in long aModifiers,
[optional] in boolean aIgnoreRootScrollFrame,
[optional] in float aPressure,
[optional] in unsigned short aInputSourceArg);
[optional] in unsigned short aInputSourceArg,
[optional] in boolean aIsSynthesized);
/** The same as sendTouchEvent but ensures that the event is dispatched to
* this DOM window or one of its children.

View File

@ -2387,7 +2387,7 @@ TabChild::DispatchMouseEvent(const nsString& aType,
bool defaultPrevented = false;
utils->SendMouseEvent(aType, aPoint.x, aPoint.y, aButton, aClickCount, aModifiers,
aIgnoreRootScrollFrame, 0, aInputSourceArg, &defaultPrevented);
aIgnoreRootScrollFrame, 0, aInputSourceArg, false, 4, &defaultPrevented);
return defaultPrevented;
}

View File

@ -1116,7 +1116,7 @@ TabParent::SendCompositionEvent(WidgetCompositionEvent& event)
mIMECompositionStart = std::min(mIMESelectionAnchor, mIMESelectionFocus);
if (mIMECompositionEnding)
return true;
event.seqno = ++mIMESeqno;
event.mSeqno = ++mIMESeqno;
return PBrowserParent::SendCompositionEvent(event);
}
@ -1147,7 +1147,7 @@ TabParent::SendTextEvent(WidgetTextEvent& event)
mIMESelectionAnchor = mIMESelectionFocus =
mIMECompositionStart + event.theText.Length();
event.seqno = ++mIMESeqno;
event.mSeqno = ++mIMESeqno;
return PBrowserParent::SendTextEvent(event);
}
@ -1159,7 +1159,7 @@ TabParent::SendSelectionEvent(WidgetSelectionEvent& event)
}
mIMESelectionAnchor = event.mOffset + (event.mReversed ? event.mLength : 0);
mIMESelectionFocus = event.mOffset + (!event.mReversed ? event.mLength : 0);
event.seqno = ++mIMESeqno;
event.mSeqno = ++mIMESeqno;
return PBrowserParent::SendSelectionEvent(event);
}

View File

@ -113,6 +113,7 @@ this.SystemMessagePermissionsTable = {
"nfc-powerlevel-change": {
"settings": ["read", "write"]
},
"wifip2p-pairing-request": { },
};
this.SystemMessagePermissionsChecker = {

View File

@ -46,6 +46,11 @@ function debug(s) {
let RILQUIRKS_DATA_REGISTRATION_ON_DEMAND =
libcutils.property_get("ro.moz.ril.data_reg_on_demand", "false") == "true";
// Ril quirk to always turn the radio off for the client without SIM card
// except hw default client.
let RILQUIRKS_RADIO_OFF_WO_CARD =
libcutils.property_get("ro.moz.ril.radio_off_wo_card", "false") == "true";
const RADIOINTERFACELAYER_CID =
Components.ID("{2d831c8d-6017-435b-a80c-e5d422810cea}");
const RADIOINTERFACE_CID =
@ -90,6 +95,7 @@ const DOM_MOBILE_MESSAGE_DELIVERY_ERROR = "error";
const RADIO_POWER_OFF_TIMEOUT = 30000;
const SMS_HANDLED_WAKELOCK_TIMEOUT = 5000;
const HW_DEFAULT_CLIENT_ID = 0;
const RIL_IPC_MOBILECONNECTION_MSG_NAMES = [
"RIL:GetRilContext",
@ -502,6 +508,8 @@ XPCOMUtils.defineLazyGetter(this, "gMessageManager", function() {
XPCOMUtils.defineLazyGetter(this, "gRadioEnabledController", function() {
return {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
ril: null,
pendingMessages: [], // For queueing "RIL:SetRadioEnabled" messages.
timer: null,
@ -510,6 +518,7 @@ XPCOMUtils.defineLazyGetter(this, "gRadioEnabledController", function() {
init: function(ril) {
this.ril = ril;
Services.obs.addObserver(this, kSysMsgListenerReadyObserverTopic, false);
},
receiveMessage: function(msg) {
@ -541,9 +550,47 @@ XPCOMUtils.defineLazyGetter(this, "gRadioEnabledController", function() {
this._handleMessage(msg);
},
_getNumCards: function() {
let numCards = 0;
for (let i = 0, N = this.ril.numRadioInterfaces; i < N; ++i) {
if (this._isCardPresentAtClient(i)) {
numCards++;
}
}
return numCards;
},
_isCardPresentAtClient: function(clientId) {
let cardState = this.ril.getRadioInterface(clientId).rilContext.cardState;
return cardState !== RIL.GECKO_CARDSTATE_UNDETECTED &&
cardState !== RIL.GECKO_CARDSTATE_UNKNOWN;
},
_isRadioAbleToEnableAtClient: function(clientId, numCards) {
if (!RILQUIRKS_RADIO_OFF_WO_CARD) {
return true;
}
// We could only turn on the radio for clientId if
// 1. a SIM card is presented or
// 2. it is the default clientId and there is no any SIM card at any client.
if (this._isCardPresentAtClient(clientId)) {
return true;
}
numCards = numCards == null ? this._getNumCards() : numCards;
if (clientId === HW_DEFAULT_CLIENT_ID && numCards === 0) {
return true;
}
return false;
},
_handleMessage: function(msg) {
if (DEBUG) debug("setRadioEnabled: handleMessage: " + JSON.stringify(msg));
let radioInterface = this.ril.getRadioInterface(msg.json.clientId || 0);
let clientId = msg.json.clientId || 0;
let radioInterface = this.ril.getRadioInterface(clientId);
if (!radioInterface.isValidStateForSetRadioEnabled()) {
radioInterface.setRadioEnabledResponse(msg.target, msg.json.data,
@ -559,7 +606,13 @@ XPCOMUtils.defineLazyGetter(this, "gRadioEnabledController", function() {
}
if (msg.json.data.enabled) {
radioInterface.receiveMessage(msg);
if (this._isRadioAbleToEnableAtClient(clientId)) {
radioInterface.receiveMessage(msg);
} else {
// Not really do it but respond success.
radioInterface.setRadioEnabledResponse(msg.target, msg.json.data);
}
this._processNextMessage();
} else {
this.request = (function() {
@ -620,6 +673,27 @@ XPCOMUtils.defineLazyGetter(this, "gRadioEnabledController", function() {
this.request = null;
}
this._processNextMessage();
},
/**
* nsIObserver interface methods.
*/
observe: function observe(subject, topic, data) {
switch (topic) {
case kSysMsgListenerReadyObserverTopic:
Services.obs.removeObserver(this, kSysMsgListenerReadyObserverTopic);
let numCards = this._getNumCards();
for (let i = 0, N = this.ril.numRadioInterfaces; i < N; ++i) {
if (this._isRadioAbleToEnableAtClient(i, numCards)) {
let radioInterface = this.ril.getRadioInterface(i);
radioInterface.setRadioEnabledInternal({enabled: true}, null);
}
}
break;
default:
break;
}
}
};
});
@ -1181,7 +1255,6 @@ function RadioInterface(options) {
Services.obs.addObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
Services.obs.addObserver(this, kMozSettingsChangedObserverTopic, false);
Services.obs.addObserver(this, kSysMsgListenerReadyObserverTopic, false);
Services.obs.addObserver(this, kSysClockChangeObserverTopic, false);
Services.obs.addObserver(this, kScreenStateChangedTopic, false);
@ -2659,9 +2732,6 @@ RadioInterface.prototype = {
observe: function(subject, topic, data) {
switch (topic) {
case kSysMsgListenerReadyObserverTopic:
this.setRadioEnabledInternal({enabled: true}, null);
break;
case kMozSettingsChangedObserverTopic:
let setting = JSON.parse(data);
this.handleSettingsChange(setting.key, setting.value, setting.message);
@ -3248,6 +3318,15 @@ RadioInterface.prototype = {
_fragmentText7Bit: function(text, langTable, langShiftTable, segmentSeptets, strict7BitEncoding) {
let ret = [];
let body = "", len = 0;
// If the message is empty, we only push the empty message to ret.
if (text.length == 0) {
ret.push({
body: text,
encodedBodyLength: text.length,
});
return ret;
}
for (let i = 0, inc = 0; i < text.length; i++) {
let c = text.charAt(i);
if (strict7BitEncoding) {
@ -3317,6 +3396,15 @@ RadioInterface.prototype = {
*/
_fragmentTextUCS2: function(text, segmentChars) {
let ret = [];
// If the message is empty, we only push the empty message to ret.
if (text.length == 0) {
ret.push({
body: text,
encodedBodyLength: text.length,
});
return ret;
}
for (let offset = 0; offset < text.length; offset += segmentChars) {
let str = text.substr(offset, segmentChars);
ret.push({

View File

@ -9,7 +9,7 @@ interface nsIWifiTetheringCallback;
/**
* Information about networks that is exposed to network manager API consumers.
*/
[scriptable, uuid(f4cf9d88-f962-4d29-9baa-fb295dad387b)]
[scriptable, uuid(e2f5c6e0-4203-11e3-aa6e-0800200c9a66)]
interface nsINetworkInterface : nsISupports
{
const long NETWORK_STATE_UNKNOWN = -1;
@ -31,6 +31,7 @@ interface nsINetworkInterface : nsISupports
const long NETWORK_TYPE_MOBILE = 1;
const long NETWORK_TYPE_MOBILE_MMS = 2;
const long NETWORK_TYPE_MOBILE_SUPL = 3;
const long NETWORK_TYPE_WIFI_P2P = 4;
/**
* Network type. One of the NETWORK_TYPE_* constants.

View File

@ -697,11 +697,11 @@ Telephony::NotifyError(uint32_t aServiceId,
NS_IMETHODIMP
Telephony::NotifyCdmaCallWaiting(uint32_t aServiceId, const nsAString& aNumber)
{
MOZ_ASSERT(mActiveCall &&
mActiveCall->ServiceId() == aServiceId &&
mActiveCall->CallState() == nsITelephonyProvider::CALL_STATE_CONNECTED);
MOZ_ASSERT(mCalls.Length() == 1);
nsRefPtr<TelephonyCall> callToNotify = mCalls[0];
MOZ_ASSERT(callToNotify && callToNotify->ServiceId() == aServiceId);
nsRefPtr<TelephonyCall> callToNotify = mActiveCall;
callToNotify->UpdateSecondNumber(aNumber);
DispatchCallEvent(NS_LITERAL_STRING("callschanged"), callToNotify);
return NS_OK;

View File

@ -692,6 +692,12 @@ var interfaceNamesInGlobalScope =
"MozWakeLock",
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "MozWifiConnectionInfoEvent", b2g: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "MozWifiP2pGroupOwner", b2g: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "MozWifiP2pManager", b2g: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "MozWifiP2pStatusChangeEvent", b2g: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "MozWifiStatusChangeEvent", b2g: true},
// IMPORTANT: Do not change this list without review from a DOM peer!

View File

@ -56,6 +56,7 @@ partial interface Event {
readonly attribute EventTarget? originalTarget;
readonly attribute EventTarget? explicitOriginalTarget;
[ChromeOnly] readonly attribute boolean multipleActionsPrevented;
[ChromeOnly] readonly attribute boolean isSynthesized;
boolean getPreventDefault();
};

View File

@ -0,0 +1,146 @@
/* 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/. */
enum WPSMethod {
"pbc",
"keypad",
"display"
};
dictionary WPSInfo {
WPSMethod method;
DOMString pin;
};
[JSImplementation="@mozilla.org/wifip2pgroupowner;1"]
interface MozWifiP2pGroupOwner {
readonly attribute DOMString groupName;
readonly attribute DOMString macAddress;
readonly attribute DOMString ipAddress;
readonly attribute DOMString passphrase;
readonly attribute DOMString ssid;
readonly attribute any wpsCapabilities;
readonly attribute unsigned long freq;
readonly attribute boolean isLocal;
};
[JSImplementation="@mozilla.org/wifip2pmanager;1",
NavigatorProperty="mozWifiP2pManager",
Func="Navigator::HasWifiManagerSupport"]
interface MozWifiP2pManager : EventTarget
{
/**
* Enable/Disable wifi direct scan.
*
* onsuccess: Succeeded in starting/stopping wifi direct scan.
* onerror: Failed to start/stop wifi direct scan.
*
*/
DOMRequest setScanEnabled(boolean enabled);
/**
* Connect to a peer with given configuration.
*
* @param address The peer MAC address we are going to connect.
* @param wpsMethod The WPS method we want to use.
* @param goIntent Number from 0 ~ 15 to indicate how much we want to be
* the group owner.
*
* onsuccess: Succeeded in issueing a 'connect' request. It doesn't mean we
* have connected to the peer.
*
* onerror: Failed to issue a 'connect' request, probably due to an
* invalid peer address, unsupported wps method or any
* preliminary error.
*
**/
DOMRequest connect(DOMString address, WPSMethod wpsMethod, optional byte goIntent);
/**
* Disconnect with a peer.
*
* @param address The mac address of the peer.
*
* onsuccess: Succeeded to issue a 'disconnect' request. It doesn't mean we
* have disconnected with the peer.
*
* onerror: Failed to issue a 'disconnect' request, probably due to the
* invalid peer address or any preliminary error.
*
*/
DOMRequest disconnect(DOMString address);
/**
* Get peer list
*
* onsuccess: Command success, req.result contains an array of peer objects.
* onerror: Command failed.
*
* Peer object format:
* .address MAC address of the peer (string)
* .name the peer's device name (string)
* .isGroupOwner if the peer is the group owner (boolean)
* .wpsCapabilities array of the supported |WPSMethod|
* .connectionStatus one of { "disconnected", "connecting", "connected", "disconnecting" }
*
*/
DOMRequest getPeerList();
/**
* Set pairing confirmation result.
*
* @param accepted Boolean to indicate whether we accepted the request or not.
* @param pin The user input pin number if the wps method is keypad.
*
* onsuccess: Command succeeded.
* onerror: Command failed.
*
*/
DOMRequest setPairingConfirmation(boolean accepted, optional DOMString pin);
/**
* Set device name.
*
* @param devieName The new device name we are going to set.
*
* onsuccess: Command succeeded.
* onerror: Command failed.
*
*/
DOMRequest setDeviceName(DOMString deviceName);
/**
* Returns if Wifi Direct is enabled.
*
*/
readonly attribute boolean enabled;
/**
* The current group owner, null if none.
*/
readonly attribute MozWifiP2pGroupOwner? groupOwner;
/**
* An event listener that is called whenever the Wifi Direct peer list is
* updated. Use getPeerList() to get the up-to-date peer list.
*/
attribute EventHandler onpeerinfoupdate;
/**
* An event listener that is called whenever Wifi Direct status changed.
* The address of the changed peer will be stored in event.peerList.
* See MozWifiP2pStatusChangeEvent.webidl.
*/
attribute EventHandler onstatuschange;
/**
* An event listener that is called whenever Wifi Direct is enabled.
*/
attribute EventHandler onenabled;
/**
* An event listener that is called whenever Wifi Direct is disabled.
*/
attribute EventHandler ondisabled;
};

View File

@ -0,0 +1,16 @@
/* -*- Mode: IDL; 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/.
*/
[Constructor(DOMString type, optional MozWifiP2pStatusChangeEventInit eventInitDict), HeaderFile="GeneratedEventClasses.h"]
interface MozWifiP2pStatusChangeEvent : Event
{
readonly attribute DOMString peerAddress;
};
dictionary MozWifiP2pStatusChangeEventInit : EventInit
{
DOMString peerAddress = "";
};

View File

@ -537,6 +537,8 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
WEBIDL_FILES += [
'MozSpeakerManager.webidl',
'MozWifiConnectionInfoEvent.webidl',
'MozWifiP2pManager.webidl',
'MozWifiP2pStatusChangeEvent.webidl',
'MozWifiStatusChangeEvent.webidl',
]

View File

@ -0,0 +1,323 @@
/* -*- 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/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/DOMRequestHelper.jsm");
const DEBUG = false;
// interface MozWifiP2pGroupOwner implementation.
function MozWifiP2pGroupOwner(aGo) {
this.groupName = aGo.groupName;
this.macAddress = aGo.macAddress;
this.ipAddress = aGo.ipAddress;
this.passphrase = aGo.passphrase;
this.ssid = aGo.ssid;
this.wpsCapabilities = aGo.wpsCapabilities;
this.freq = aGo.freq;
this.isLocal = aGo.isLocal;
}
MozWifiP2pGroupOwner.prototype = {
classID: Components.ID("{a9b81450-349d-11e3-aa6e-0800200c9a66}"),
contractID: "@mozilla.org/wifip2pgroupowner;1",
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports])
};
// interface MozWifiP2pManager implementation.
const MOZ_WIFIP2PMANAGER_CONTRACTID = "@mozilla.org/wifip2pmanager;1";
const MOZ_WIFIP2PMANAGER_CID = Components.ID("{8d9125a0-3498-11e3-aa6e-0800200c9a66}");
function MozWifiP2pManager() {
this.defineEventHandlerGetterSetter("onstatuschange");
this.defineEventHandlerGetterSetter("onpeerinfoupdate");
this.defineEventHandlerGetterSetter("onenabled");
this.defineEventHandlerGetterSetter("ondisabled");
this.currentPeer = null;
this.enabled = false;
this.groupOwner = null;
}
// For smaller, read-only APIs, we expose any property that doesn't begin with
// an underscore.
function exposeReadOnly(obj) {
let exposedProps = {};
for (let i in obj) {
if (i[0] === "_") {
continue;
}
exposedProps[i] = "r";
}
obj.__exposedProps__ = exposedProps;
return obj;
}
function debug(msg) {
if (DEBUG) {
dump('-------------- MozWifiP2pManager: ' + msg);
}
}
MozWifiP2pManager.prototype = {
__proto__: DOMRequestIpcHelper.prototype,
classID: MOZ_WIFIP2PMANAGER_CID,
contractID: MOZ_WIFIP2PMANAGER_CONTRACTID,
QueryInterface: XPCOMUtils.generateQI([Ci.nsIDOMGlobalPropertyInitializer,
Ci.nsISupportsWeakReference,
Ci.nsIObserver,
Ci.nsISupports]),
//
// nsIDOMGlobalPropertyInitializer implementation.
//
init: function(aWindow) {
const messages = ["WifiP2pManager:setScanEnabled:Return:OK",
"WifiP2pManager:setScanEnabled:Return:NO",
"WifiP2pManager:getPeerList:Return:OK",
"WifiP2pManager:getPeerList:Return:NO",
"WifiP2pManager:connect:Return:OK",
"WifiP2pManager:connect:Return:NO",
"WifiP2pManager:disconnect:Return:OK",
"WifiP2pManager:disconnect:Return:NO",
"WifiP2pManager:setPairingConfirmation:Return",
"WifiP2pManager:setDeviceName:Return:OK",
"WifiP2pManager:setDeviceName:Return:NO",
"WifiP2pManager:p2pDown",
"WifiP2pManager:p2pUp",
"WifiP2pManager:onconnecting",
"WifiP2pManager:onconnected",
"WifiP2pManager:ondisconnected",
"WifiP2pManager:ongroupnstop",
"WifiP2pManager:onconnectingfailed",
"WifiP2pManager:onwpstimeout",
"WifiP2pManager:onwpsfail",
"WifiP2pManager:onpeerinfoupdate",
];
this.initDOMRequestHelper(aWindow, messages);
this._mm = Cc["@mozilla.org/childprocessmessagemanager;1"].getService(Ci.nsISyncMessageSender);
// Notify the internal a new DOM mananger is created.
let state = this._mm.sendSyncMessage("WifiP2pManager:getState")[0];
if (state) {
debug('State: ' + JSON.stringify(state));
} else {
debug('Failed to get state');
}
},
uninit: function() {
},
_sendMessageForRequest: function(name, data, request) {
let id = this.getRequestId(request);
this._mm.sendAsyncMessage(name, { data: data, rid: id, mid: this._id });
},
receiveMessage: function(aMessage) {
let msg = aMessage.json;
if (msg.mid && msg.mid !== this._id) {
return;
}
let request;
switch (aMessage.name) {
case "WifiP2pManager:setScanEnabled:Return:OK":
request = this.takeRequest(msg.rid);
Services.DOMRequest.fireSuccess(request, exposeReadOnly(msg.data));
break;
case "WifiP2pManager:setScanEnabled:Return:NO":
request = this.takeRequest(msg.rid);
Services.DOMRequest.fireError(request, "Unable to enable/disable Wifi P2P peer discovery.");
break;
case "WifiP2pManager:getPeerList:Return:OK":
request = this.takeRequest(msg.rid);
Services.DOMRequest.fireSuccess(request, msg.data);
break;
case "WifiP2pManager:getPeerList:Return:NO":
request = this.takeRequest(msg.rid);
Services.DOMRequest.fireError(request, "Unable to disable Wifi P2P peer discovery.");
break;
case "WifiP2pManager:connect:Return:OK":
request = this.takeRequest(msg.rid);
Services.DOMRequest.fireSuccess(request, exposeReadOnly(msg.data));
break;
case "WifiP2pManager:connect:Return:NO":
request = this.takeRequest(msg.rid);
Services.DOMRequest.fireError(request, "Unable to connect to Wifi P2P peer.");
break;
case "WifiP2pManager:disconnect:Return:OK":
request = this.takeRequest(msg.rid);
Services.DOMRequest.fireSuccess(request, exposeReadOnly(msg.data));
break;
case "WifiP2pManager:disconnect:Return:NO":
request = this.takeRequest(msg.rid);
Services.DOMRequest.fireError(request, "Unable to disconnect to Wifi P2P peer.");
break;
case "WifiP2pManager:setDeviceName:Return:OK":
request = this.takeRequest(msg.rid);
Services.DOMRequest.fireSuccess(request, exposeReadOnly(msg.data));
break;
case "WifiP2pManager:setDeviceName:Return:NO":
request = this.takeRequest(msg.rid);
Services.DOMRequest.fireError(request, "Unable to set device name.");
break;
case "WifiP2pManager:p2pDown":
this.enabled = false;
this.currentPeer = null;
this._fireEnabledOrDisabled(false);
break;
case "WifiP2pManager:p2pUp":
this.enabled = true;
this._fireEnabledOrDisabled(true);
break;
case "WifiP2pManager:onconnecting":
debug('onconnecting with peer: ' + JSON.stringify(msg.peer));
this.currentPeer = msg.peer;
this._fireStatusChangeEvent(msg.peer.address);
break;
case "WifiP2pManager:onconnected":
debug('onconnected with peer: ' + JSON.stringify(msg.peer));
this.currentPeer = msg.peer;
this.groupOwner = new MozWifiP2pGroupOwner(msg.groupOwner);
this._fireStatusChangeEvent(msg.peer.address);
break;
case "WifiP2pManager:ondisconnected":
debug('ondisconnected with peer: ' + JSON.stringify(msg.peer));
this.currentPeer = null;
this.groupOwner = null;
this._fireStatusChangeEvent(msg.peer.address);
break;
case "WifiP2pManager:onconnectingfailed":
this._fireStatusChangeEvent(null);
break;
case "WifiP2pManager:onwpstimeout":
this._fireStatusChangeEvent(null);
break;
case "WifiP2pManager:onwpsfail":
this._fireStatusChangeEvent(null);
break;
case "WifiP2pManager:onpeerinfoupdate":
this._firePeerInfoUpdateEvent();
break;
}
},
_firePeerInfoUpdateEvent: function PeerInfoUpdate() {
let evt = new this._window.Event("peerinfoupdate");
this.__DOM_IMPL__.dispatchEvent(evt);
},
_fireStatusChangeEvent: function WifiP2pStatusChange(peerAddress) {
let evt = new this._window.MozWifiP2pStatusChangeEvent("statuschange",
{ peerAddress: peerAddress });
this.__DOM_IMPL__.dispatchEvent(evt);
},
_fireEnabledOrDisabled: function enabledDisabled(enabled) {
let evt = new this._window.Event(enabled ? "enabled" : "disabled");
this.__DOM_IMPL__.dispatchEvent(evt);
},
//
// WifiP2pManager.webidl implementation.
//
enableScan: function () {
let request = this.createRequest();
this._sendMessageForRequest("WifiP2pManager:enableScan", null, request);
return request;
},
disableScan: function () {
let request = this.createRequest();
this._sendMessageForRequest("WifiP2pManager:disableScan", null, request);
return request;
},
setScanEnabled: function(enabled) {
let request = this.createRequest();
this._sendMessageForRequest("WifiP2pManager:setScanEnabled", enabled, request);
return request;
},
connect: function (address, wpsMethod, goIntent) {
let request = this.createRequest();
let connectionInfo = { address: address, wpsMethod: wpsMethod, goIntent: goIntent };
this._sendMessageForRequest("WifiP2pManager:connect", connectionInfo, request);
return request;
},
disconnect: function (address) {
let request = this.createRequest();
this._sendMessageForRequest("WifiP2pManager:disconnect", address, request);
return request;
},
getPeerList: function () {
let request = this.createRequest();
this._sendMessageForRequest("WifiP2pManager:getPeerList", null, request);
return request;
},
setPairingConfirmation: function (accepted, pin) {
let request = this.createRequest();
let result = { accepted: accepted, pin: pin };
this._sendMessageForRequest("WifiP2pManager:setPairingConfirmation", result, request);
return request;
},
setDeviceName: function(newDeviceName) {
let request = this.createRequest();
this._sendMessageForRequest("WifiP2pManager:setDeviceName", newDeviceName, request);
return request;
},
// Helpers.
defineEventHandlerGetterSetter: function(event) {
Object.defineProperty(this, event, {
get: function() {
return this.__DOM_IMPL__.getEventHandler(event);
},
set: function(handler) {
this.__DOM_IMPL__.setEventHandler(event, handler);
}
});
},
};
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([MozWifiP2pManager, MozWifiP2pGroupOwner]);

View File

@ -0,0 +1,6 @@
# DOMWifiP2pManager.js
component {8d9125a0-3498-11e3-aa6e-0800200c9a66} DOMWifiP2pManager.js
contract @mozilla.org/wifip2pmanager;1 {8d9125a0-3498-11e3-aa6e-0800200c9a66}
component {a9b81450-349d-11e3-aa6e-0800200c9a66} DOMWifiP2pManager.js
contract @mozilla.org/wifip2pgroupowner;1 {a9b81450-349d-11e3-aa6e-0800200c9a66}

205
dom/wifi/StateMachine.jsm Normal file
View File

@ -0,0 +1,205 @@
/* -*- 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");
this.EXPORTED_SYMBOLS = ["StateMachine"];
const DEBUG = false;
this.StateMachine = function(aDebugTag) {
function debug(aMsg) {
dump('-------------- StateMachine:' + aDebugTag + ': ' + aMsg);
}
var sm = {};
var _initialState;
var _curState;
var _prevState;
var _paused;
var _eventQueue = [];
var _deferredEventQueue = [];
var _defaultEventHandler;
// Public interfaces.
sm.setDefaultEventHandler = function(aDefaultEventHandler) {
_defaultEventHandler = aDefaultEventHandler;
};
sm.start = function(aInitialState) {
_initialState = aInitialState;
sm.gotoState(_initialState);
};
sm.sendEvent = function (aEvent) {
if (!_initialState) {
if (DEBUG) {
debug('StateMachine is not running. Call StateMachine.start() first.');
}
return;
}
_eventQueue.push(aEvent);
asyncCall(handleFirstEvent);
};
sm.getPreviousState = function() {
return _prevState;
};
sm.getCurrentState = function() {
return _curState;
};
// State object maker.
// @param aName string for this state's name.
// @param aDelegate object:
// .handleEvent: required.
// .enter: called before entering this state (optional).
// .exit: called before exiting this state (optional).
sm.makeState = function (aName, aDelegate) {
if (!aDelegate.handleEvent) {
throw "handleEvent is a required delegate function.";
}
var nop = function() {};
return {
name: aName,
enter: (aDelegate.enter || nop),
exit: (aDelegate.exit || nop),
handleEvent: aDelegate.handleEvent
};
};
sm.deferEvent = function (aEvent) {
// The definition of a 'deferred event' is:
// We are not able to handle this event now but after receiving
// certain event or entering a new state, we might be able to handle
// it. For example, we couldn't handle CONNECT_EVENT in the
// diconnecting state. But once we finish doing "disconnecting", we
// could then handle CONNECT_EVENT!
//
// So, the deferred event may be handled in the following cases:
// 1. Once we entered a new state.
// 2. Once we handled a regular event.
if (DEBUG) {
debug('Deferring event: ' + JSON.stringify(aEvent));
}
_deferredEventQueue.push(aEvent);
};
// Goto the new state. If the current state is null, the exit
// function won't be called.
sm.gotoState = function (aNewState) {
if (_curState) {
if (DEBUG) {
debug("exiting state: " + _curState.name);
}
_curState.exit();
}
_prevState = _curState;
_curState = aNewState;
if (DEBUG) {
debug("entering state: " + _curState.name);
}
_curState.enter();
// We are in the new state now. We got a chance to handle the
// deferred events.
handleDeferredEvents();
sm.resume();
};
// No incoming event will be handled after you call pause().
// (But they will be queued.)
sm.pause = function() {
_paused = true;
};
// Continue to handle incoming events.
sm.resume = function() {
_paused = false;
asyncCall(handleFirstEvent);
};
//----------------------------------------------------------
// Private stuff
//----------------------------------------------------------
function asyncCall(f) {
Services.tm.currentThread.dispatch(f, Ci.nsIThread.DISPATCH_NORMAL);
}
function handleFirstEvent() {
var hadDeferredEvents;
if (0 === _eventQueue.length) {
return;
}
if (_paused) {
return; // The state machine is paused now.
}
hadDeferredEvents = _deferredEventQueue.length > 0;
handleOneEvent(_eventQueue.shift()); // The handler may defer this event.
// We've handled one event. If we had deferred events before, now is
// a good chance to handle them.
if (hadDeferredEvents) {
handleDeferredEvents();
}
// Continue to handle the next regular event.
handleFirstEvent();
}
function handleDeferredEvents() {
if (_deferredEventQueue.length && DEBUG) {
debug('Handle deferred events: ' + _deferredEventQueue.length);
}
for (let i = 0; i < _deferredEventQueue.length; i++) {
handleOneEvent(_deferredEventQueue.shift());
}
}
function handleOneEvent(aEvent)
{
if (DEBUG) {
debug('Handling event: ' + JSON.stringify(aEvent));
}
var handled = _curState.handleEvent(aEvent);
if (undefined === handled) {
throw "handleEvent returns undefined: " + _curState.name;
}
if (!handled) {
// Event is not handled in the current state. Try handleEventCommon().
handled = (_defaultEventHandler ? _defaultEventHandler(aEvent) : handled);
}
if (undefined === handled) {
throw "handleEventCommon returns undefined: " + _curState.name;
}
if (!handled) {
if (DEBUG) {
debug('!!!!!!!!! FIXME !!!!!!!!! Event not handled: ' + JSON.stringify(aEvent));
}
}
return handled;
}
return sm;
};

View File

@ -14,8 +14,15 @@ Cu.import("resource://gre/modules/systemlibs.js");
const SUPP_PROP = "init.svc.wpa_supplicant";
const WPA_SUPPLICANT = "wpa_supplicant";
const DEBUG = false;
this.WifiCommand = function(aControlMessage, aInterface) {
function debug(msg) {
if (DEBUG) {
dump('-------------- WifiCommand: ' + msg);
}
}
var command = {};
//-------------------------------------------------
@ -135,14 +142,16 @@ this.WifiCommand = function(aControlMessage, aInterface) {
doStringCommand("LOG_LEVEL", callback);
};
command.wpsPbc = function (callback) {
doBooleanCommand("WPS_PBC", "OK", callback);
command.wpsPbc = function (iface, callback) {
doBooleanCommand("WPS_PBC" + (iface ? (" interface=" + iface) : ""),
"OK", callback);
};
command.wpsPin = function (detail, callback) {
doStringCommand("WPS_PIN " +
(detail.bssid === undefined ? "any" : detail.bssid) +
(detail.pin === undefined ? "" : (" " + detail.pin)),
(detail.pin === undefined ? "" : (" " + detail.pin)) +
(detail.iface ? (" interface=" + detail.iface) : ""),
callback);
};
@ -337,9 +346,89 @@ this.WifiCommand = function(aControlMessage, aInterface) {
});
};
//--------------------------------------------------
// Helper functions.
//--------------------------------------------------
command.setDeviceName = function(deviceName, callback) {
doBooleanCommand("SET device_name " + deviceName, "OK", callback);
};
//-------------------------------------------------
// P2P commands.
//-------------------------------------------------
command.p2pProvDiscovery = function(address, wpsMethod, callback) {
var command = "P2P_PROV_DISC " + address + " " + wpsMethod;
doBooleanCommand(command, "OK", callback);
};
command.p2pConnect = function(config, callback) {
var command = "P2P_CONNECT " + config.address + " " + config.wpsMethodWithPin + " ";
if (config.joinExistingGroup) {
command += "join";
} else {
command += "go_intent=" + config.goIntent;
}
debug('P2P connect command: ' + command);
doBooleanCommand(command, "OK", callback);
};
command.p2pGroupRemove = function(iface, callback) {
debug("groupRemove()");
doBooleanCommand("P2P_GROUP_REMOVE " + iface, "OK", callback);
};
command.p2pEnable = function(detail, callback) {
var commandChain = ["SET device_name " + detail.deviceName,
"SET device_type " + detail.deviceType,
"SET config_methods " + detail.wpsMethods,
"P2P_SET conc_pref sta",
"P2P_FLUSH"];
doBooleanCommandChain(commandChain, callback);
};
command.p2pDisable = function(callback) {
doBooleanCommand("P2P_SET disabled 1", "OK", callback);
};
command.p2pEnableScan = function(timeout, callback) {
doBooleanCommand("P2P_FIND " + timeout, "OK", callback);
};
command.p2pDisableScan = function(callback) {
doBooleanCommand("P2P_STOP_FIND", "OK", callback);
};
command.p2pGetGroupCapab = function(address, callback) {
command.p2pPeer(address, function(reply) {
debug('p2p_peer reply: ' + reply);
if (!reply) {
callback(0);
return;
}
var capab = /group_capab=0x([0-9a-fA-F]+)/.exec(reply)[1];
if (!capab) {
callback(0);
} else {
callback(parseInt(capab, 16));
}
});
};
command.p2pPeer = function(address, callback) {
doStringCommand("P2P_PEER " + address, callback);
};
command.p2pGroupAdd = function(netId, callback) {
doBooleanCommand("P2P_GROUP_ADD persistent=" + netId, callback);
};
command.p2pReinvoke = function(netId, address, callback) {
doBooleanCommand("P2P_INVITE persistent=" + netId + " peer=" + address, "OK", callback);
};
//----------------------------------------------------------
// Private stuff.
//----------------------------------------------------------
function voidControlMessage(cmd, callback) {
aControlMessage({ cmd: cmd, iface: aInterface }, function (data) {
@ -391,6 +480,10 @@ this.WifiCommand = function(aControlMessage, aInterface) {
});
}
//--------------------------------------------------
// Helper functions.
//--------------------------------------------------
function stopProcess(service, process, callback) {
var count = 0;
var timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);

View File

@ -11,16 +11,23 @@ const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/systemlibs.js");
XPCOMUtils.defineLazyServiceGetter(this, "gNetworkManager",
"@mozilla.org/network/manager;1",
"nsINetworkManager");
XPCOMUtils.defineLazyServiceGetter(this, "gNetworkService",
"@mozilla.org/network/service;1",
"nsINetworkService");
this.EXPORTED_SYMBOLS = ["WifiNetUtil"];
const DHCP_PROP = "init.svc.dhcpcd";
const DHCP = "dhcpcd";
const DEBUG = false;
this.WifiNetUtil = function(controlMessage) {
function debug(msg) {
if (DEBUG) {
dump('-------------- NetUtil: ' + msg);
}
}
var util = {};
util.configureInterface = function(cfg, callback) {
@ -67,14 +74,14 @@ this.WifiNetUtil = function(controlMessage) {
});
};
util.startDhcpServer = function (range, callback) {
gNetworkManager.setDhcpServer(true, range, function (error) {
util.startDhcpServer = function (config, callback) {
gNetworkService.setDhcpServer(true, config, function (error) {
callback(!error);
});
};
util.stopDhcpServer = function (callback) {
gNetworkManager.setDhcpServer(false, null, function (error) {
gNetworkService.setDhcpServer(false, null, function (error) {
callback(!error);
});
};
@ -135,6 +142,7 @@ this.WifiNetUtil = function(controlMessage) {
util.runIpConfig = function (name, data, callback) {
if (!data) {
debug("IP config failed to run");
callback({ info: data });
return;
}
@ -142,16 +150,19 @@ this.WifiNetUtil = function(controlMessage) {
setProperty("net." + name + ".dns1", ipToString(data.dns1),
function(ok) {
if (!ok) {
debug("Unable to set net.<ifname>.dns1");
return;
}
setProperty("net." + name + ".dns2", ipToString(data.dns2),
function(ok) {
if (!ok) {
debug("Unable to set net.<ifname>.dns2");
return;
}
setProperty("net." + name + ".gw", ipToString(data.gateway),
function(ok) {
if (!ok) {
debug("Unable to set net.<ifname>.gw");
return;
}
callback({ info: data });

1597
dom/wifi/WifiP2pManager.jsm Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,303 @@
/* -*- 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;
const CONNECTION_STATUS_DISCONNECTED = "disconnected";
const CONNECTION_STATUS_CONNECTING = "connecting";
const CONNECTION_STATUS_CONNECTED = "connected";
const CONNECTION_STATUS_DISCONNECTING = "disconnecting";
const DEBUG = false;
this.EXPORTED_SYMBOLS = ["WifiP2pWorkerObserver"];
// WifiP2pWorkerObserver resides in WifiWorker to handle DOM message
// by either 1) returning internally maintained information or
// 2) delegating to aDomMsgResponder. It is also responsible
// for observing events from WifiP2pManager and dispatch to DOM.
//
// @param aDomMsgResponder handles DOM messages, including
// - setScanEnabled
// - connect
// - disconnect
// - setPairingConfirmation
// The instance is actually WifiP2pManager.
this.WifiP2pWorkerObserver = function(aDomMsgResponder) {
function debug(aMsg) {
if (DEBUG) {
dump('-------------- WifiP2pWorkerObserver: ' + aMsg);
}
}
// Private member variables.
let _localDevice;
let _peerList = {}; // List of P2pDevice.
let _domManagers = [];
// Constructor of P2pDevice. It will be exposed to DOM.
//
// @param aPeer object representing a P2P device:
// .name: string for the device name.
// .address: Mac address.
// .isGroupOwner: boolean to indicate if this device is the group owner.
// .wpsCapabilities: array of string of {"pbc", "display", "keypad"}.
function P2pDevice(aPeer) {
this.address = aPeer.address;
this.name = (aPeer.name ? aPeer.name : aPeer.address);
this.isGroupOwner = aPeer.isGroupOwner;
this.wpsCapabilities = aPeer.wpsCapabilities;
this.connectionStatus = CONNECTION_STATUS_DISCONNECTED;
// Since this object will be exposed to web, defined the exposed
// properties here.
this.__exposedProps__ = {
address: "r",
name: "r",
isGroupOwner: "r",
wpsCapabilities: "r",
connectionStatus: "r"
};
}
// Constructor of P2pGroupOwner.
//
// @param aGroupOwner:
// .macAddress
// .ipAddress
// .passphrase
// .ssid
// .freq
// .isLocal
function P2pGroupOwner(aGroupOwner) {
this.macAddress = aGroupOwner.macAddress; // The identifier to get further information.
this.ipAddress = aGroupOwner.ipAddress;
this.passphrase = aGroupOwner.passphrase;
this.ssid = aGroupOwner.ssid; // e.g. DIRECT-xy.
this.freq = aGroupOwner.freq;
this.isLocal = aGroupOwner.isLocal;
let detail = _peerList[aGroupOwner.macAddress];
if (detail) {
this.name = detail.name;
this.wpsCapabilities = detail.wpsCapabilities;
} else if (_localDevice.address === this.macAddress) {
this.name = _localDevice.name;
this.wpsCapabilities = _localDevice.wpsCapabilities;
} else {
debug("We don't know this group owner: " + aGroupOwner.macAddress);
this.name = aGroupOwner.macAddress;
this.wpsCapabilities = [];
}
}
function fireEvent(aMessage, aData) {
debug('domManager: ' + JSON.stringify(_domManagers));
_domManagers.forEach(function(manager) {
// Note: We should never have a dead message manager here because we
// observe our child message managers shutting down below.
manager.sendAsyncMessage("WifiP2pManager:" + aMessage, aData);
});
}
function addDomManager(aMsg) {
if (-1 === _domManagers.indexOf(aMsg.manager)) {
_domManagers.push(aMsg.manager);
}
}
function returnMessage(aMessage, aSuccess, aData, aMsg) {
let rMsg = aMessage + ":Return:" + (aSuccess ? "OK" : "NO");
aMsg.manager.sendAsyncMessage(rMsg,
{ data: aData, rid: aMsg.rid, mid: aMsg.mid });
}
function handlePeerListUpdated() {
fireEvent("onpeerinfoupdate", {});
}
// Return a literal object as the constructed object.
return {
onLocalDeviceChanged: function(aDevice) {
_localDevice = aDevice;
debug('Local device updated to: ' + JSON.stringify(_localDevice));
},
onEnabled: function() {
_peerList = [];
fireEvent("p2pUp", {});
},
onDisbaled: function() {
fireEvent("p2pDown", {});
},
onPeerFound: function(aPeer) {
let newFoundPeer = new P2pDevice(aPeer);
let origianlPeer = _peerList[aPeer.address];
_peerList[aPeer.address] = newFoundPeer;
if (origianlPeer) {
newFoundPeer.connectionStatus = origianlPeer.connectionStatus;
}
handlePeerListUpdated();
},
onPeerLost: function(aPeer) {
let lostPeer = _peerList[aPeer.address];
if (!lostPeer) {
debug('Unknown peer lost: ' + aPeer.address);
return;
}
delete _peerList[aPeer.address];
handlePeerListUpdated();
},
onConnecting: function(aPeer) {
let peer = _peerList[aPeer.address];
if (!peer) {
debug('Unknown peer connecting: ' + aPeer.address);
peer = new P2pDevice(aPeer);
_peerList[aPeer.address] = peer;
handlePeerListUpdated();
}
peer.connectionStatus = CONNECTION_STATUS_CONNECTING;
fireEvent('onconnecting', { peer: peer });
},
onConnected: function(aGroupOwner, aPeer) {
let go = new P2pGroupOwner(aGroupOwner);
let peer = _peerList[aPeer.address];
if (!peer) {
debug('Unknown peer connected: ' + aPeer.address);
peer = new P2pDevice(aPeer);
_peerList[aPeer.address] = peer;
handlePeerListUpdated();
}
peer.connectionStatus = CONNECTION_STATUS_CONNECTED;
peer.isGroupOwner = (aPeer.address === aGroupOwner.address);
fireEvent('onconnected', { groupOwner: go, peer: peer });
},
onDisconnected: function(aPeer) {
let peer = _peerList[aPeer.address];
if (!peer) {
debug('Unknown peer disconnected: ' + aPeer.address);
return;
}
peer.connectionStatus = CONNECTION_STATUS_DISCONNECTED;
fireEvent('ondisconnected', { peer: peer });
},
getObservedDOMMessages: function() {
return [
"WifiP2pManager:getState",
"WifiP2pManager:getPeerList",
"WifiP2pManager:setScanEnabled",
"WifiP2pManager:connect",
"WifiP2pManager:disconnect",
"WifiP2pManager:setPairingConfirmation",
"WifiP2pManager:setDeviceName"
];
},
onDOMMessage: function(aMessage) {
let msg = aMessage.data || {};
msg.manager = aMessage.target;
if ("child-process-shutdown" === aMessage.name) {
let i;
if (-1 !== (i = _domManagers.indexOf(msg.manager))) {
_domManagers.splice(i, 1);
}
return;
}
if (!aMessage.target.assertPermission("wifi-manage")) {
return;
}
switch (aMessage.name) {
case "WifiP2pManager:getState": // A new DOM manager is created.
addDomManager(msg);
return { peerList: _peerList, }; // Synchronous call. Simply return it.
case "WifiP2pManager:setScanEnabled":
{
let enabled = msg.data;
aDomMsgResponder.setScanEnabled(enabled, function(success) {
returnMessage(aMessage.name, success, (success ? true : "ERROR"), msg);
});
}
break;
case "WifiP2pManager:getPeerList":
{
// Convert the object to an array.
let peerArray = [];
for (let key in _peerList) {
if (_peerList.hasOwnProperty(key)) {
peerArray.push(_peerList[key]);
}
}
returnMessage(aMessage.name, true, peerArray, msg);
}
break;
case "WifiP2pManager:connect":
{
let peer = msg.data;
let onDoConnect = function(success) {
returnMessage(aMessage.name, success, (success ? true : "ERROR"), msg);
};
aDomMsgResponder.connect(peer.address, peer.wpsMethod,
peer.goIntent, onDoConnect);
}
break;
case "WifiP2pManager:disconnect":
{
let address = msg.data;
aDomMsgResponder.disconnect(address, function(success) {
returnMessage(aMessage.name, success, (success ? true : "ERROR"), msg);
});
}
break;
case "WifiP2pManager:setPairingConfirmation":
{
let result = msg.data;
aDomMsgResponder.setPairingConfirmation(result);
returnMessage(aMessage.name, true, true, msg);
}
break;
case "WifiP2pManager:setDeviceName":
{
let newDeviceName = msg.data;
aDomMsgResponder.setDeviceName(newDeviceName, function(success) {
returnMessage(aMessage.name, success, (success ? true : "ERROR"), msg);
});
}
break;
default:
if (0 === aMessage.name.indexOf("WifiP2pManager:")) {
debug("DOM WifiP2pManager message not handled: " + aMessage.name);
}
} // End of switch.
}
};
};

View File

@ -5,6 +5,7 @@
#include "WifiUtils.h"
#include <dlfcn.h>
#include <errno.h>
#include <cutils/properties.h>
#include "prinit.h"
#include "js/CharacterEncoding.h"
#include "NetUtils.h"
@ -34,6 +35,14 @@ GetSharedLibrary()
return sWifiLib;
}
static bool
GetWifiP2pSupported()
{
char propP2pSupported[PROPERTY_VALUE_MAX];
property_get("ro.moz.wifi.p2p_supported", propP2pSupported, "0");
return (0 == strcmp(propP2pSupported, "1"));
}
// This is the same algorithm as in InflateUTF8StringToBuffer with Copy and
// while ignoring invalids.
// https://mxr.mozilla.org/mozilla-central/source/js/src/vm/CharacterEncoding.cpp#231
@ -307,7 +316,7 @@ bool WpaSupplicant::ExecuteCommand(CommandOptions aOptions,
} else if (aOptions.mCmd.EqualsLiteral("unload_driver")) {
aResult.mStatus = mImpl->do_wifi_unload_driver();
} else if (aOptions.mCmd.EqualsLiteral("start_supplicant")) {
aResult.mStatus = mImpl->do_wifi_start_supplicant(0);
aResult.mStatus = mImpl->do_wifi_start_supplicant(GetWifiP2pSupported() ? 1 : 0);
} else if (aOptions.mCmd.EqualsLiteral("stop_supplicant")) {
aResult.mStatus = mImpl->do_wifi_stop_supplicant(0);
} else if (aOptions.mCmd.EqualsLiteral("connect_to_supplicant")) {

View File

@ -13,6 +13,8 @@ Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/systemlibs.js");
Cu.import("resource://gre/modules/WifiCommand.jsm");
Cu.import("resource://gre/modules/WifiNetUtil.jsm");
Cu.import("resource://gre/modules/WifiP2pManager.jsm");
Cu.import("resource://gre/modules/WifiP2pWorkerObserver.jsm");
var DEBUG = false; // set to true to show debug messages.
@ -108,16 +110,30 @@ var WifiManager = (function() {
unloadDriverEnabled: libcutils.property_get("ro.moz.wifi.unloaddriver") === "1",
schedScanRecovery: libcutils.property_get("ro.moz.wifi.sched_scan_recover") === "false" ? false : true,
driverDelay: libcutils.property_get("ro.moz.wifi.driverDelay"),
p2pSupported: libcutils.property_get("ro.moz.wifi.p2p_supported") === "1",
ifname: libcutils.property_get("wifi.interface")
};
}
let {sdkVersion, unloadDriverEnabled, schedScanRecovery, driverDelay, ifname} = getStartupPrefs();
let {sdkVersion, unloadDriverEnabled, schedScanRecovery, driverDelay, p2pSupported, ifname} = getStartupPrefs();
let wifiListener = {
onWaitEvent: function(event, iface) {
if (manager.ifname === iface && handleEvent(event)) {
waitForEvent(iface);
} else if (p2pSupported) {
if (WifiP2pManager.INTERFACE_NAME === iface) {
// If the connection is closed, wifi.c::wifi_wait_for_event()
// will still return 'CTRL-EVENT-TERMINATING - connection closed'
// rather than blocking. So when we see this special event string,
// just return immediately.
const TERMINATED_EVENT = 'CTRL-EVENT-TERMINATING - connection closed';
if (-1 !== event.indexOf(TERMINATED_EVENT)) {
return;
}
p2pManager.handleEvent(event);
waitForEvent(iface);
}
}
},
@ -135,18 +151,29 @@ var WifiManager = (function() {
manager.schedScanRecovery = schedScanRecovery;
manager.driverDelay = driverDelay ? parseInt(driverDelay, 10) : DRIVER_READY_WAIT;
// Regular Wifi stuff.
var netUtil = WifiNetUtil(controlMessage);
var wifiCommand = WifiCommand(controlMessage, manager.ifname);
// Wifi P2P stuff
var p2pManager;
if (p2pSupported) {
let p2pCommand = WifiCommand(controlMessage, WifiP2pManager.INTERFACE_NAME);
p2pManager = WifiP2pManager(p2pCommand, netUtil);
}
let wifiService = Cc["@mozilla.org/wifi/service;1"];
if (wifiService) {
wifiService = wifiService.getService(Ci.nsIWifiProxyService);
let interfaces = [manager.ifname];
if (p2pSupported) {
interfaces.push(WifiP2pManager.INTERFACE_NAME);
}
wifiService.start(wifiListener, interfaces, interfaces.length);
} else {
debug("No wifi service component available!");
}
var wifiCommand = WifiCommand(controlMessage, manager.ifname);
var netUtil = WifiNetUtil(controlMessage);
// Callbacks to invoke when a reply arrives from the wifi service.
var controlCallbacks = Object.create(null);
var idgen = 0;
@ -244,6 +271,7 @@ var WifiManager = (function() {
wifiCommand.doSetScanMode(true, function(ignore) {
setBackgroundScan("OFF", function(turned, ignore) {
reEnableBackgroundScan = turned;
manager.handlePreWifiScan();
wifiCommand.scan(function(ok) {
wifiCommand.doSetScanMode(false, function(ignore) {
// The result of scanCommand is the result of the actual SCAN
@ -255,6 +283,7 @@ var WifiManager = (function() {
});
return;
}
manager.handlePreWifiScan();
wifiCommand.scan(callback);
}
@ -267,6 +296,7 @@ var WifiManager = (function() {
if (ok)
debugEnabled = wanted;
});
p2pManager.setDebug(DEBUG);
}
}
@ -755,6 +785,7 @@ var WifiManager = (function() {
reEnableBackgroundScan = false;
setBackgroundScan("ON", function() {});
}
manager.handlePostWifiScan();
notify("scanresultsavailable");
return true;
}
@ -786,6 +817,10 @@ var WifiManager = (function() {
notify("supplicantconnection");
callback();
});
if (p2pSupported) {
manager.enableP2p(function(success) {});
}
}
function prepareForStartup(callback) {
@ -911,19 +946,27 @@ var WifiManager = (function() {
// Note these following calls ignore errors. If we fail to kill the
// supplicant gracefully, then we need to continue telling it to die
// until it does.
manager.state = "DISABLING";
wifiCommand.terminateSupplicant(function (ok) {
manager.connectionDropped(function () {
wifiCommand.stopSupplicant(function (status) {
wifiCommand.closeSupplicantConnection(function () {
manager.state = "UNINITIALIZED";
netUtil.disableInterface(manager.ifname, function (ok) {
unloadDriver(WIFI_FIRMWARE_STATION, callback);
let doDisableWifi = function() {
manager.state = "DISABLING";
wifiCommand.terminateSupplicant(function (ok) {
manager.connectionDropped(function () {
wifiCommand.stopSupplicant(function (status) {
wifiCommand.closeSupplicantConnection(function () {
manager.state = "UNINITIALIZED";
netUtil.disableInterface(manager.ifname, function (ok) {
unloadDriver(WIFI_FIRMWARE_STATION, callback);
});
});
});
});
});
});
}
if (p2pSupported) {
p2pManager.setEnabled(false, { onDisabled: doDisableWifi });
} else {
doDisableWifi();
}
}
}
@ -1135,6 +1178,11 @@ var WifiManager = (function() {
wifiCommand.saveConfig(callback);
}
manager.enableNetwork = function(netId, disableOthers, callback) {
if (p2pSupported) {
// We have to stop wifi direct scan before associating to an AP.
// Otherwise we will get a "REJECT" wpa supplicant event.
p2pManager.setScanEnabled(false, function(success) {});
}
wifiCommand.enableNetwork(netId, disableOthers, callback);
}
manager.disableNetwork = function(netId, callback) {
@ -1215,6 +1263,46 @@ var WifiManager = (function() {
}
}
manager.handlePreWifiScan = function() {
if (p2pSupported) {
// Before doing regular wifi scan, we have to disable wifi direct
// scan first. Otherwise we will never get the scan result.
p2pManager.blockScan();
}
};
manager.handlePostWifiScan = function() {
if (p2pSupported) {
// After regular wifi scanning, we should restore the restricted
// wifi direct scan.
p2pManager.unblockScan();
}
};
//
// Public APIs for P2P.
//
manager.p2pSupported = function() {
return p2pSupported;
};
manager.getP2pManager = function() {
return p2pManager;
};
manager.enableP2p = function(callback) {
p2pManager.setEnabled(true, {
onSupplicantConnected: function() {
wifiService.waitForEvent(WifiP2pManager.INTERFACE_NAME);
},
onEnabled: function(success) {
callback(success);
}
});
};
return manager;
})();
@ -1497,6 +1585,17 @@ function WifiWorker() {
this._connectionInfoTimer = null;
this._reconnectOnDisconnect = false;
// Create p2pObserver and assign to p2pManager.
if (WifiManager.p2pSupported()) {
this._p2pObserver = WifiP2pWorkerObserver(WifiManager.getP2pManager());
WifiManager.getP2pManager().setObserver(this._p2pObserver);
// Add DOM message observerd by p2pObserver to the message listener as well.
this._p2pObserver.getObservedDOMMessages().forEach((function(msgName) {
this._mm.addMessageListener(msgName, this);
}).bind(this));
}
// Users of instances of nsITimer should keep a reference to the timer until
// it is no longer needed in order to assure the timer is fired.
this._callbackTimer = null;
@ -1529,6 +1628,7 @@ function WifiWorker() {
// wait for our next command) ensure that background scanning is on and
// then try again.
debug("Determined that scanning is stuck, turning on background scanning!");
WifiManager.handlePostWifiScan();
WifiManager.disconnect(function(ok) {});
self._turnOnBackgroundScan = true;
}
@ -2304,6 +2404,15 @@ WifiWorker.prototype = {
let msg = aMessage.data || {};
msg.manager = aMessage.target;
if (WifiManager.p2pSupported()) {
// If p2pObserver returns something truthy, return it!
// Otherwise, continue to do the rest of tasks.
var p2pRet = this._p2pObserver.onDOMMessage(aMessage);
if (p2pRet) {
return p2pRet;
}
}
// Note: By the time we receive child-process-shutdown, the child process
// has already forgotten its permissions so we do this before the
// permissions check.
@ -2393,79 +2502,6 @@ WifiWorker.prototype = {
}).bind(this));
},
getWifiScanResults: function(callback) {
var count = 0;
var timer = null;
var self = this;
self.waitForScan(waitForScanCallback);
doScan();
function doScan() {
WifiManager.scan(true, function (ok) {
if (!ok) {
if (!timer) {
count = 0;
timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
}
if (count++ >= 3) {
timer = null;
this.wantScanResults.splice(this.wantScanResults.indexOf(waitForScanCallback), 1);
callback.onfailure();
return;
}
// Else it's still running, continue waiting.
timer.initWithCallback(doScan, 10000, Ci.nsITimer.TYPE_ONE_SHOT);
return;
}
});
}
function waitForScanCallback(networks) {
if (networks === null) {
callback.onfailure();
return;
}
var wifiScanResults = new Array();
var net;
for (let net in networks) {
let value = networks[net];
wifiScanResults.push(transformResult(value));
}
callback.onready(wifiScanResults.length, wifiScanResults);
}
function transformResult(element) {
var result = new WifiScanResult();
result.connected = false;
for (let id in element) {
if (id === "__exposedProps__") {
continue;
}
if (id === "security") {
result[id] = 0;
var security = element[id];
for (let j = 0; j < security.length; j++) {
if (security[j] === "WPA-PSK") {
result[id] |= Ci.nsIWifiScanResult.WPA_PSK;
} else if (security[j] === "WPA-EAP") {
result[id] |= Ci.nsIWifiScanResult.WPA_EAP;
} else if (security[j] === "WEP") {
result[id] |= Ci.nsIWifiScanResult.WEP;
} else {
result[id] = 0;
}
}
} else {
result[id] = element[id];
}
}
return result;
}
},
getKnownNetworks: function(msg) {
const message = "WifiManager:getKnownNetworks:Return";
if (!WifiManager.enabled) {
@ -2722,7 +2758,7 @@ WifiWorker.prototype = {
let self = this;
let detail = msg.data;
if (detail.method === "pbc") {
WifiManager.wpsPbc(function(ok) {
WifiManager.wpsPbc(WifiManager.ifname, function(ok) {
if (ok)
self._sendMessage(message, true, true, msg);
else

View File

@ -6,6 +6,7 @@
XPIDL_SOURCES += [
'nsIDOMMozWifiConnectionInfoEvent.idl',
'nsIDOMMozWifiP2pStatusChangeEvent.idl',
'nsIDOMMozWifiStatusChangeEvent.idl',
'nsIWifi.idl',
'nsIWifiService.idl',
@ -16,13 +17,18 @@ XPIDL_MODULE = 'dom_wifi'
EXTRA_COMPONENTS += [
'DOMWifiManager.js',
'DOMWifiManager.manifest',
'DOMWifiP2pManager.js',
'DOMWifiP2pManager.manifest',
'WifiWorker.js',
'WifiWorker.manifest',
]
EXTRA_JS_MODULES += [
'StateMachine.jsm',
'WifiCommand.jsm',
'WifiNetUtil.jsm',
'WifiP2pManager.jsm',
'WifiP2pWorkerObserver.jsm',
]
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':

View File

@ -0,0 +1,24 @@
/* 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 "nsIDOMEvent.idl"
[scriptable, builtinclass, uuid(82cad910-2019-11e3-8224-0800200c9a66)]
interface nsIDOMMozWifiP2pStatusChangeEvent : nsIDOMEvent
{
/**
* The mac address of the peer whose status has just changed.
*/
readonly attribute DOMString peerAddress;
[noscript] void initMozWifiP2pStatusChangeEvent(in DOMString aType,
in boolean aCanBubble,
in boolean aCancelable,
in DOMString aPeerAddress);
};
dictionary MozWifiP2pStatusChangeEventInit : EventInit
{
DOMString peerAddress;
};

View File

@ -24,6 +24,7 @@ simple_events = [
'StyleSheetChangeEvent',
'StyleSheetApplicableStateChangeEvent',
#ifdef MOZ_WIDGET_GONK
'MozWifiP2pStatusChangeEvent',
'MozWifiStatusChangeEvent',
'MozWifiConnectionInfoEvent',
#endif

View File

@ -272,6 +272,12 @@ pref("media.mediasource.enabled", false);
#ifdef MOZ_WEBSPEECH
pref("media.webspeech.recognition.enable", false);
#endif
#ifdef MOZ_WEBM_ENCODER
pref("media.encoder.webm.enabled", true);
#endif
#ifdef MOZ_OMX_ENCODER
pref("media.encoder.omx.enabled", true);
#endif
// Whether to enable Web Audio support
pref("media.webaudio.enabled", true);

View File

@ -111,7 +111,7 @@ InternalMethods.prototype = {
this.abortExistingFlow();
this.signedInUser = null; // clear in-memory cache
return this.signedInUserStorage.set(null).then(() => {
this.notifyObservers("fxaccounts:onlogout");
this.notifyObservers(ONLOGOUT_NOTIFICATION);
});
},
@ -198,7 +198,7 @@ InternalMethods.prototype = {
// We are now ready for business. This should only be invoked once
// per setSignedInUser(), regardless of whether we've rebooted since
// setSignedInUser() was called.
internal.notifyObservers("fxaccounts:onlogin");
internal.notifyObservers(ONLOGIN_NOTIFICATION);
return data;
}.bind(this));
},
@ -347,7 +347,7 @@ InternalMethods.prototype = {
},
notifyObservers: function(topic) {
log.debug("Notifying observers of user login");
log.debug("Notifying observers of " + topic);
Services.obs.notifyObservers(null, topic, null);
},

View File

@ -41,6 +41,10 @@ this.KEY_LIFETIME = 1000 * 3600 * 12; // 12 hours
this.POLL_SESSION = 1000 * 60 * 5; // 5 minutes
this.POLL_STEP = 1000 * 3; // 3 seconds
// Observer notifications.
this.ONLOGIN_NOTIFICATION = "fxaccounts:onlogin";
this.ONLOGOUT_NOTIFICATION = "fxaccounts:onlogout";
// Server errno.
// From https://github.com/mozilla/fxa-auth-server/blob/master/docs/api.md#response-format
this.ERRNO_ACCOUNT_ALREADY_EXISTS = 101;

View File

@ -26,6 +26,19 @@ XPCOMUtils.defineLazyModuleGetter(this, "FxAccountsClient",
this.FxAccountsManager = {
init: function() {
Services.obs.addObserver(this, ONLOGOUT_NOTIFICATION, false);
},
observe: function(aSubject, aTopic, aData) {
if (aTopic !== ONLOGOUT_NOTIFICATION) {
return;
}
// Remove the cached session if we get a logout notification.
this._activeSession = null;
},
// We don't really need to save fxAccounts instance but this way we allow
// to mock FxAccounts from tests.
_fxAccounts: fxAccounts,
@ -156,22 +169,25 @@ this.FxAccountsManager = {
return Promise.resolve();
}
return this._fxAccounts.signOut(this._activeSession.sessionToken).then(
// We clear the local session cache as soon as we get the onlogout
// notification triggered within FxAccounts.signOut, so we save the
// session token value to be able to remove the remote server session
// in case that we have network connection.
let sessionToken = this._activeSession.sessionToken;
return this._fxAccounts.signOut(sessionToken).then(
() => {
// If there is no connection, removing the local session should be
// enough. The client can create new sessions up to the limit (100?).
// At this point the local session should already be removed.
// The client can create new sessions up to the limit (100?).
// Orphaned tokens on the server will eventually be garbage collected.
if (Services.io.offline) {
this._activeSession = null;
return Promise.resolve();
}
// Otherwise, we try to remove the remote session.
let client = this._createFxAccountsClient();
return client.signOut(this._activeSession.sessionToken).then(
return client.signOut(sessionToken).then(
result => {
// Even if there is a remote server error, we remove the local
// session.
this._activeSession = null;
let error = this._getError(result);
if (error) {
return Promise.reject({
@ -183,9 +199,6 @@ this.FxAccountsManager = {
return Promise.resolve();
},
reason => {
// Even if there is a remote server error, we remove the local
// session.
this._activeSession = null;
return this._serverError(reason);
}
);
@ -413,3 +426,5 @@ this.FxAccountsManager = {
);
}
};
FxAccountsManager.init();

View File

@ -121,6 +121,7 @@ FxAccountsManager._fxAccounts = {
signOut: function() {
let deferred = Promise.defer();
this._signedInUser = null;
Services.obs.notifyObservers(null, ONLOGOUT_NOTIFICATION, null);
deferred.resolve();
return deferred.promise;
}
@ -581,3 +582,13 @@ add_test(function(test_queryAccount_no_accountId) {
}
);
});
add_test(function() {
do_print("= Test 23 | fxaccounts:onlogout notification =");
do_check_true(FxAccountsManager._activeSession != null);
Services.obs.notifyObservers(null, ONLOGOUT_NOTIFICATION, null);
do_execute_soon(function() {
do_check_null(FxAccountsManager._activeSession);
run_next_test();
});
});

View File

@ -40,6 +40,7 @@ let curFrame = content;
let previousFrame = null;
let elementManager = new ElementManager([]);
let importedScripts = null;
let inputSource = null;
// The sandbox we execute test scripts in. Gets lazily created in
// createExecuteContentSandbox().
@ -189,6 +190,12 @@ function newSession(msg) {
resetValues();
if (isB2G) {
readyStateTimer.initWithCallback(waitForReady, 100, Ci.nsITimer.TYPE_ONE_SHOT);
// We have to set correct mouse event source to MOZ_SOURCE_TOUCH
// to offer a way for event listeners to differentiate
// events being the result of a physical mouse action.
// This is especially important for the touch event shim,
// in order to prevent creating touch event for these fake mouse events.
inputSource = Ci.nsIDOMMouseEvent.MOZ_SOURCE_TOUCH;
}
}
@ -664,7 +671,7 @@ function emitTouchEvent(type, touch) {
marionetteLogObj.clearLogs();
*/
let domWindowUtils = curFrame.QueryInterface(Components.interfaces.nsIInterfaceRequestor).getInterface(Components.interfaces.nsIDOMWindowUtils);
domWindowUtils.sendTouchEvent(type, [touch.identifier], [touch.screenX], [touch.screenY], [touch.radiusX], [touch.radiusY], [touch.rotationAngle], [touch.force], 1, 0);
domWindowUtils.sendTouchEvent(type, [touch.identifier], [touch.clientX], [touch.clientY], [touch.radiusX], [touch.radiusY], [touch.rotationAngle], [touch.force], 1, 0);
}
}
@ -672,10 +679,10 @@ function emitTouchEvent(type, touch) {
* This function emit mouse event
* @param: doc is the current document
* type is the type of event to dispatch
* detail is the number of clicks, button notes the mouse button
* clickCount is the number of clicks, button notes the mouse button
* elClientX and elClientY are the coordinates of the mouse relative to the viewport
*/
function emitMouseEvent(doc, type, elClientX, elClientY, detail, button) {
function emitMouseEvent(doc, type, elClientX, elClientY, clickCount, button) {
if (!wasInterrupted()) {
let loggingInfo = "emitting Mouse event of type " + type + " at coordinates (" + elClientX + ", " + elClientY + ") relative to the viewport";
dumpLog(loggingInfo);
@ -686,11 +693,9 @@ function emitMouseEvent(doc, type, elClientX, elClientY, detail, button) {
{log: elementManager.wrapValue(marionetteLogObj.getLogs())});
marionetteLogObj.clearLogs();
*/
detail = detail || 1;
button = button || 0;
let win = doc.defaultView;
// Figure out the element the mouse would be over at (x, y)
utils.synthesizeMouseAtPoint(elClientX, elClientY, {type: type, button: button, clickCount: detail}, win);
let domUtils = win.QueryInterface(Components.interfaces.nsIInterfaceRequestor).getInterface(Components.interfaces.nsIDOMWindowUtils);
domUtils.sendMouseEvent(type, elClientX, elClientY, button || 0, clickCount || 1, 0, false, 0, inputSource);
}
}
@ -1029,7 +1034,7 @@ function emitMultiEvents(type, touch, touches) {
// Create changed touches
let changedTouches = doc.createTouchList(touch);
// Create the event object
let event = curFrame.document.createEvent('TouchEvent');
let event = doc.createEvent('TouchEvent');
event.initTouchEvent(type,
true,
true,

View File

@ -97,7 +97,6 @@
"content/base/test/test_XHRSendData.html":"seems to stall",
"content/base/test/test_XHR_parameters.html":"86 total, 4 failing - testing mozAnon - got false, expected true",
"content/base/test/test_XHR_system.html":"12 total, 2 failing - .mozSystem == true - got false, expected true + ",
"content/base/test/test_bug431701.html":"xmlhttprequest causes crash, bug 902271",
"content/base/test/test_bug422537.html":"xmlhttprequest causes crash, bug 902271",
@ -238,18 +237,13 @@
"content/base/test/test_bug424359-2.html":"",
"content/base/test/test_mixed_content_blocker_bug803225.html":"",
"content/html/document/test/test_non-ascii-cookie.html":"",
"content/html/document/test/test_document.watch.html":"expects document.cookie setting to work",
"docshell/test/navigation/test_bug13871.html":"",
"docshell/test/navigation/test_bug270414.html":"",
"docshell/test/navigation/test_bug344861.html":"",
"docshell/test/navigation/test_bug386782.html":"",
"docshell/test/navigation/test_not-opener.html":"",
"docshell/test/navigation/test_reserved.html":"",
"docshell/test/test_bug413310.html":"",
"dom/imptests/html/webgl":"",
"dom/battery/test/test_battery_basics.html":"",
"dom/browser-element/mochitest/test_browserElement_inproc_Alert.html":"",
"dom/browser-element/mochitest/test_browserElement_inproc_AppFramePermission.html":"",
@ -341,16 +335,10 @@
"dom/media/tests/mochitest/test_peerConnection_throwInCallbacks.html":"",
"dom/media/tests/ipc/test_ipc.html":"nested ipc not working",
"dom/network/tests/test_networkstats_basics.html":"Will be fixed in bug 858005",
"dom/permission/tests/test_permission_basics.html":"https not working, bug 907770",
"dom/tests/mochitest/bugs/test_bug335976.xhtml":"",
"dom/tests/mochitest/bugs/test_bug369306.html":"test timed out, can't focus back from popup window to opener?",
"dom/tests/mochitest/bugs/test_bug396843.html":"",
"dom/tests/mochitest/bugs/test_bug406375.html":"",
"dom/tests/mochitest/bugs/test_bug427744.html":"",
"dom/tests/mochitest/bugs/test_bug641552.html":"",
"dom/tests/mochitest/bugs/test_sizetocontent_clamp.html": "Windows can't change size on B2G",
"dom/tests/mochitest/bugs/test_resize_move_windows.html": "Windows can't change size and position on B2G",
"dom/tests/mochitest/bugs/test_window_bar.html":"",
@ -398,12 +386,10 @@
"dom/tests/mochitest/pointerlock/test_pointerlock-api.html":"window.open focus issues (using fullscreen)",
"dom/tests/mochitest/webapps/test_bug_779982.html":"",
"dom/tests/mochitest/whatwg/test_postMessage_closed.html":"bug 894914 - wrong data - got FAIL, expected message",
"dom/workers/test/test_suspend.html":"test timed out, might need more time",
"dom/workers/test/test_xhr_parameters.html":"",
"dom/workers/test/test_xhr_system.html":"",
"dom/workers/test/test_closeOnGC.html": "times out",
"dom/workers/test/test_errorPropagation.html":"times out",
"dom/workers/test/test_errorwarning.html":"Failed to load script: errorwarning_worker.js",
"dom/workers/test/test_fibonacci.html":"Failed to load script: fibonacci_worker.js",
@ -415,22 +401,16 @@
"layout/base/tests/test_mozPaintCount.html":"depends on plugins support",
"layout/forms/test/test_bug348236.html":"select form control popup",
"layout/forms/test/test_bug446663.html":"needs copy support",
"layout/forms/test/test_bug564115.html":"times out on window.open and focus event",
"layout/forms/test/test_bug571352.html":"shift-click multi-select not working?",
"layout/forms/test/test_textarea_resize.html":"resizing textarea not available in b2g",
"layout/forms/test/test_bug903715.html":"select elements don't use an in-page popup in B2G",
"layout/forms/test/test_listcontrol_search.html" : "select elements don't use an in-page popup in B2G",
"layout/generic/test/test_bug392746.html":"ctrl mouse select not working in b2g",
"layout/generic/test/test_bug470212.html":"shift mouse select not working in b2g",
"layout/generic/test/test_bug514732.html":"times out, also on Android",
"layout/generic/test/test_bug791616.html":"Target should not have scrolled - got 114.10000610351562, expected 115.39999389648438",
"layout/generic/test/test_invalidate_during_plugin_paint.html":"plugins not supported",
"layout/generic/test/test_page_scroll_with_fixed_pos.html":"opened window too small?",
"layout/generic/test/test_plugin_focus.html":"plugins not supported",
"layout/generic/test/test_plugin_mouse_coords.html":"plugins not supported",
"layout/generic/test/test_selection_expanding.html":"mouse selection not working",
"layout/style/test/test_rule_insertion.html":"monospace and serif text have sufficiently different widths",
"layout/style/test/test_transitions_per_property.html":"times out, needs more time + various failures",
"content/html/content/test/test_bug209275.xhtml":"timed out, 47 tests, bug 870262, :visited support",

View File

@ -249,9 +249,13 @@ function synthesizeMouseAtPoint(left, top, aEvent, aWindow)
var modifiers = _parseModifiers(aEvent);
var pressure = ("pressure" in aEvent) ? aEvent.pressure : 0;
var inputSource = ("inputSource" in aEvent) ? aEvent.inputSource : 0;
var synthesized = ("isSynthesized" in aEvent) ? aEvent.isSynthesized : true;
if (("type" in aEvent) && aEvent.type) {
defaultPrevented = utils.sendMouseEvent(aEvent.type, left, top, button, clickCount, modifiers, false, pressure, inputSource);
defaultPrevented = utils.sendMouseEvent(aEvent.type, left, top, button,
clickCount, modifiers, false,
pressure, inputSource,
synthesized);
}
else {
utils.sendMouseEvent("mousedown", left, top, button, clickCount, modifiers, false, pressure, inputSource);

View File

@ -11,14 +11,19 @@ let handlerCount = 0;
let orig_w3c_touch_events = Services.prefs.getIntPref('dom.w3c_touch_events.enabled');
let trackedWindows = new WeakMap();
// =================== Touch ====================
// Simulate touch events on desktop
function TouchEventHandler (window) {
// Returns an already instanciated handler for this window
let cached = trackedWindows.get(window);
if (cached) {
return cached;
}
let contextMenuTimeout = 0;
// This guard is used to not re-enter the events processing loop for
// self dispatched events
let ignoreEvents = false;
let threshold = 25;
try {
@ -32,30 +37,35 @@ function TouchEventHandler (window) {
let TouchEventHandler = {
enabled: false,
events: ['mousedown', 'mousemove', 'mouseup', 'click'],
events: ['mousedown', 'mousemove', 'mouseup'],
start: function teh_start() {
let isReloadNeeded = Services.prefs.getIntPref('dom.w3c_touch_events.enabled') != 1;
handlerCount++;
Services.prefs.setIntPref('dom.w3c_touch_events.enabled', 1);
if (this.enabled)
return false;
this.enabled = true;
let isReloadNeeded = Services.prefs.getIntPref('dom.w3c_touch_events.enabled') != 1;
Services.prefs.setIntPref('dom.w3c_touch_events.enabled', 1);
this.events.forEach((function(evt) {
window.addEventListener(evt, this, true);
// Only listen trusted events to prevent messing with
// event dispatched manually within content documents
window.addEventListener(evt, this, true, false);
}).bind(this));
return isReloadNeeded;
},
stop: function teh_stop() {
handlerCount--;
if (handlerCount == 0)
Services.prefs.setIntPref('dom.w3c_touch_events.enabled', orig_w3c_touch_events);
if (!this.enabled)
return;
this.enabled = false;
Services.prefs.setIntPref('dom.w3c_touch_events.enabled', orig_w3c_touch_events);
this.events.forEach((function(evt) {
window.removeEventListener(evt, this, true);
}).bind(this));
},
handleEvent: function teh_handleEvent(evt) {
if (evt.button || ignoreEvents ||
evt.mozInputSource == Ci.nsIDOMMouseEvent.MOZ_SOURCE_UNKNOWN)
// Ignore all but real mouse event coming from physical mouse
// (especially ignore mouse event being dispatched from a touch event)
if (evt.button || evt.mozInputSource != Ci.nsIDOMMouseEvent.MOZ_SOURCE_MOUSE || evt.isSynthesized) {
return;
}
// The gaia system window use an hybrid system even on the device which is
// a mix of mouse/touch events. So let's not cancel *all* mouse events
@ -105,6 +115,13 @@ function TouchEventHandler (window) {
content.clearTimeout(contextMenuTimeout);
type = 'touchend';
// Only register click listener after mouseup to ensure
// catching only real user click. (Especially ignore click
// being dispatched on form submit)
if (evt.detail == 1) {
window.addEventListener('click', this, true, false);
}
break;
case 'click':
@ -113,6 +130,8 @@ function TouchEventHandler (window) {
evt.preventDefault();
evt.stopImmediatePropagation();
window.removeEventListener('click', this, true, false);
if (this.cancelClick)
return;
@ -141,7 +160,7 @@ function TouchEventHandler (window) {
let content = this.getContent(evt.target);
var utils = content.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
utils.sendMouseEvent(type, evt.clientX, evt.clientY, 0, 1, 0, true);
utils.sendMouseEvent(type, evt.clientX, evt.clientY, 0, 1, 0, true, 0, Ci.nsIDOMMouseEvent.MOZ_SOURCE_TOUCH);
},
sendContextMenu: function teh_sendContextMenu(target, x, y, delay) {
let doc = target.ownerDocument;
@ -159,6 +178,26 @@ function TouchEventHandler (window) {
return timeout;
},
sendTouchEvent: function teh_sendTouchEvent(evt, target, name) {
// When running OOP b2g desktop, we need to send the touch events
// using the mozbrowser api on the unwrapped frame.
if (target.localName == "iframe" && target.mozbrowser === true) {
if (name == "touchstart") {
this.touchstartTime = Date.now();
} else if (name == "touchend") {
// If we have a 'fast' tap, don't send a click as both will be turned
// into a click and that breaks eg. checkboxes.
if (Date.now() - this.touchstartTime < delay) {
this.cancelClick = true;
}
}
let unwraped = XPCNativeWrapper.unwrap(target);
unwraped.sendTouchEvent(name, [0], // event type, id
[evt.clientX], [evt.clientY], // x, y
[1], [1], // rx, ry
[0], [0], // rotation, force
1); // count
return;
}
let document = target.ownerDocument;
let content = this.getContent(target);
@ -179,15 +218,10 @@ function TouchEventHandler (window) {
},
getContent: function teh_getContent(target) {
let win = target.ownerDocument.defaultView;
let docShell = win.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShellTreeItem);
let topDocShell = docShell.sameTypeRootTreeItem;
topDocShell.QueryInterface(Ci.nsIDocShell);
let top = topDocShell.contentViewer.DOMDocument.defaultView;
return top;
return win;
}
};
trackedWindows.set(window, TouchEventHandler);
return TouchEventHandler;
}

View File

@ -32,6 +32,8 @@ enum
#undef NS_DEFINE_VK
#define kLatestSeqno UINT32_MAX
namespace mozilla {
namespace dom {
@ -166,18 +168,25 @@ private:
friend class plugins::PPluginInstanceChild;
WidgetTextEvent()
: mSeqno(kLatestSeqno)
, rangeCount(0)
, rangeArray(nullptr)
, isChar(false)
{
}
public:
uint32_t seqno;
uint32_t mSeqno;
public:
virtual WidgetTextEvent* AsTextEvent() MOZ_OVERRIDE { return this; }
WidgetTextEvent(bool aIsTrusted, uint32_t aMessage, nsIWidget* aWidget) :
WidgetGUIEvent(aIsTrusted, aMessage, aWidget, NS_TEXT_EVENT),
rangeCount(0), rangeArray(nullptr), isChar(false)
WidgetTextEvent(bool aIsTrusted, uint32_t aMessage, nsIWidget* aWidget)
: WidgetGUIEvent(aIsTrusted, aMessage, aWidget, NS_TEXT_EVENT)
, mSeqno(kLatestSeqno)
, rangeCount(0)
, rangeArray(nullptr)
, isChar(false)
{
}
@ -217,11 +226,12 @@ private:
friend class mozilla::dom::PBrowserChild;
WidgetCompositionEvent()
: mSeqno(kLatestSeqno)
{
}
public:
uint32_t seqno;
uint32_t mSeqno;
public:
virtual WidgetCompositionEvent* AsCompositionEvent() MOZ_OVERRIDE
@ -230,8 +240,9 @@ public:
}
WidgetCompositionEvent(bool aIsTrusted, uint32_t aMessage,
nsIWidget* aWidget) :
WidgetGUIEvent(aIsTrusted, aMessage, aWidget, NS_COMPOSITION_EVENT)
nsIWidget* aWidget)
: WidgetGUIEvent(aIsTrusted, aMessage, aWidget, NS_COMPOSITION_EVENT)
, mSeqno(kLatestSeqno)
{
// XXX compositionstart is cancelable in draft of DOM3 Events.
// However, it doesn't make sense for us, we cannot cancel composition
@ -376,12 +387,18 @@ private:
friend class mozilla::dom::PBrowserChild;
WidgetSelectionEvent()
: mSeqno(kLatestSeqno)
, mOffset(0)
, mLength(0)
, mReversed(false)
, mExpandToClusterBoundary(true)
, mSucceeded(false)
{
MOZ_CRASH("WidgetSelectionEvent is created without proper arguments");
}
public:
uint32_t seqno;
uint32_t mSeqno;
public:
virtual WidgetSelectionEvent* AsSelectionEvent() MOZ_OVERRIDE
@ -389,9 +406,14 @@ public:
return this;
}
WidgetSelectionEvent(bool aIsTrusted, uint32_t aMessage, nsIWidget* aWidget) :
WidgetGUIEvent(aIsTrusted, aMessage, aWidget, NS_SELECTION_EVENT),
mExpandToClusterBoundary(true), mSucceeded(false)
WidgetSelectionEvent(bool aIsTrusted, uint32_t aMessage, nsIWidget* aWidget)
: WidgetGUIEvent(aIsTrusted, aMessage, aWidget, NS_SELECTION_EVENT)
, mSeqno(kLatestSeqno)
, mOffset(0)
, mLength(0)
, mReversed(false)
, mExpandToClusterBoundary(true)
, mSucceeded(false)
{
}

View File

@ -386,7 +386,7 @@ struct ParamTraits<mozilla::WidgetTextEvent>
static void Write(Message* aMsg, const paramType& aParam)
{
WriteParam(aMsg, static_cast<mozilla::WidgetGUIEvent>(aParam));
WriteParam(aMsg, aParam.seqno);
WriteParam(aMsg, aParam.mSeqno);
WriteParam(aMsg, aParam.theText);
WriteParam(aMsg, aParam.isChar);
WriteParam(aMsg, aParam.rangeCount);
@ -398,7 +398,7 @@ struct ParamTraits<mozilla::WidgetTextEvent>
{
if (!ReadParam(aMsg, aIter,
static_cast<mozilla::WidgetGUIEvent*>(aResult)) ||
!ReadParam(aMsg, aIter, &aResult->seqno) ||
!ReadParam(aMsg, aIter, &aResult->mSeqno) ||
!ReadParam(aMsg, aIter, &aResult->theText) ||
!ReadParam(aMsg, aIter, &aResult->isChar) ||
!ReadParam(aMsg, aIter, &aResult->rangeCount))
@ -436,7 +436,7 @@ struct ParamTraits<mozilla::WidgetCompositionEvent>
static void Write(Message* aMsg, const paramType& aParam)
{
WriteParam(aMsg, static_cast<mozilla::WidgetGUIEvent>(aParam));
WriteParam(aMsg, aParam.seqno);
WriteParam(aMsg, aParam.mSeqno);
WriteParam(aMsg, aParam.data);
}
@ -444,7 +444,7 @@ struct ParamTraits<mozilla::WidgetCompositionEvent>
{
return ReadParam(aMsg, aIter,
static_cast<mozilla::WidgetGUIEvent*>(aResult)) &&
ReadParam(aMsg, aIter, &aResult->seqno) &&
ReadParam(aMsg, aIter, &aResult->mSeqno) &&
ReadParam(aMsg, aIter, &aResult->data);
}
};
@ -493,7 +493,7 @@ struct ParamTraits<mozilla::WidgetSelectionEvent>
static void Write(Message* aMsg, const paramType& aParam)
{
WriteParam(aMsg, static_cast<mozilla::WidgetGUIEvent>(aParam));
WriteParam(aMsg, aParam.seqno);
WriteParam(aMsg, aParam.mSeqno);
WriteParam(aMsg, aParam.mOffset);
WriteParam(aMsg, aParam.mLength);
WriteParam(aMsg, aParam.mReversed);
@ -505,7 +505,7 @@ struct ParamTraits<mozilla::WidgetSelectionEvent>
{
return ReadParam(aMsg, aIter,
static_cast<mozilla::WidgetGUIEvent*>(aResult)) &&
ReadParam(aMsg, aIter, &aResult->seqno) &&
ReadParam(aMsg, aIter, &aResult->mSeqno) &&
ReadParam(aMsg, aIter, &aResult->mOffset) &&
ReadParam(aMsg, aIter, &aResult->mLength) &&
ReadParam(aMsg, aIter, &aResult->mReversed) &&

View File

@ -273,25 +273,26 @@ PuppetWidget::DispatchEvent(WidgetGUIEvent* event, nsEventStatus& aStatus)
if (event->message == NS_COMPOSITION_START) {
mIMEComposing = true;
}
uint32_t seqno = kLatestSeqno;
switch (event->eventStructType) {
case NS_COMPOSITION_EVENT:
mIMELastReceivedSeqno = event->AsCompositionEvent()->seqno;
if (mIMELastReceivedSeqno < mIMELastBlurSeqno)
return NS_OK;
seqno = event->AsCompositionEvent()->mSeqno;
break;
case NS_TEXT_EVENT:
mIMELastReceivedSeqno = event->AsTextEvent()->seqno;
if (mIMELastReceivedSeqno < mIMELastBlurSeqno)
return NS_OK;
seqno = event->AsTextEvent()->mSeqno;
break;
case NS_SELECTION_EVENT:
mIMELastReceivedSeqno = event->AsSelectionEvent()->seqno;
if (mIMELastReceivedSeqno < mIMELastBlurSeqno)
return NS_OK;
seqno = event->AsSelectionEvent()->mSeqno;
break;
default:
break;
}
if (seqno != kLatestSeqno) {
mIMELastReceivedSeqno = seqno;
if (mIMELastReceivedSeqno < mIMELastBlurSeqno) {
return NS_OK;
}
}
if (mAttachedWidgetListener) {
aStatus = mAttachedWidgetListener->HandleEvent(event, mUseAttachedEvents);
@ -352,7 +353,7 @@ PuppetWidget::IMEEndComposition(bool aCancel)
nsEventStatus status;
WidgetTextEvent textEvent(true, NS_TEXT_TEXT, this);
InitEvent(textEvent, nullptr);
textEvent.seqno = mIMELastReceivedSeqno;
textEvent.mSeqno = mIMELastReceivedSeqno;
// SendEndIMEComposition is always called since ResetInputState
// should always be called even if we aren't composing something.
if (!mTabChild ||
@ -367,7 +368,7 @@ PuppetWidget::IMEEndComposition(bool aCancel)
WidgetCompositionEvent compEvent(true, NS_COMPOSITION_END, this);
InitEvent(compEvent, nullptr);
compEvent.seqno = mIMELastReceivedSeqno;
compEvent.mSeqno = mIMELastReceivedSeqno;
DispatchEvent(&compEvent, status);
return NS_OK;
}