mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-30 00:01:50 +00:00
Merge mozilla-central and b2g-inbound
--HG-- rename : content/events/src/nsDOMEvent.h => dom/events/nsDOMEvent.h
This commit is contained in:
commit
c73e589fc3
@ -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
|
||||
|
10
b2g/chrome/content/desktop.js
Normal file
10
b2g/chrome/content/desktop.js
Normal 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();
|
||||
});
|
@ -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>
|
||||
|
@ -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
|
||||
|
@ -1,4 +1,4 @@
|
||||
{
|
||||
"revision": "25a3b96e9c5ff89b69b29007462bfd056ad5bf53",
|
||||
"revision": "ae5c954ca1b8047cfa932f905d6498b47bb44ac5",
|
||||
"repo_path": "/integration/gaia-central"
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -49,3 +49,4 @@ support-files =
|
||||
[test_window_extensible.html]
|
||||
[test_window_indexing.html]
|
||||
[test_writable-replaceable.html]
|
||||
[test_domwindowutils.html]
|
||||
|
84
dom/base/test/test_domwindowutils.html
Normal file
84
dom/base/test/test_domwindowutils.html
Normal 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>
|
@ -179,6 +179,11 @@ public:
|
||||
return mEvent->mFlags.mIsTrusted;
|
||||
}
|
||||
|
||||
bool IsSynthesized() const
|
||||
{
|
||||
return mEvent->mFlags.mIsSynthesizedForTests;
|
||||
}
|
||||
|
||||
uint64_t TimeStamp() const
|
||||
{
|
||||
return mEvent->time;
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -113,6 +113,7 @@ this.SystemMessagePermissionsTable = {
|
||||
"nfc-powerlevel-change": {
|
||||
"settings": ["read", "write"]
|
||||
},
|
||||
"wifip2p-pairing-request": { },
|
||||
};
|
||||
|
||||
this.SystemMessagePermissionsChecker = {
|
||||
|
@ -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({
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
|
@ -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!
|
||||
|
@ -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();
|
||||
};
|
||||
|
146
dom/webidl/MozWifiP2pManager.webidl
Normal file
146
dom/webidl/MozWifiP2pManager.webidl
Normal 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;
|
||||
};
|
16
dom/webidl/MozWifiP2pStatusChangeEvent.webidl
Normal file
16
dom/webidl/MozWifiP2pStatusChangeEvent.webidl
Normal 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 = "";
|
||||
};
|
@ -537,6 +537,8 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
|
||||
WEBIDL_FILES += [
|
||||
'MozSpeakerManager.webidl',
|
||||
'MozWifiConnectionInfoEvent.webidl',
|
||||
'MozWifiP2pManager.webidl',
|
||||
'MozWifiP2pStatusChangeEvent.webidl',
|
||||
'MozWifiStatusChangeEvent.webidl',
|
||||
]
|
||||
|
||||
|
323
dom/wifi/DOMWifiP2pManager.js
Normal file
323
dom/wifi/DOMWifiP2pManager.js
Normal 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]);
|
6
dom/wifi/DOMWifiP2pManager.manifest
Normal file
6
dom/wifi/DOMWifiP2pManager.manifest
Normal 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
205
dom/wifi/StateMachine.jsm
Normal 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;
|
||||
};
|
@ -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);
|
||||
|
@ -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
1597
dom/wifi/WifiP2pManager.jsm
Normal file
File diff suppressed because it is too large
Load Diff
303
dom/wifi/WifiP2pWorkerObserver.jsm
Normal file
303
dom/wifi/WifiP2pWorkerObserver.jsm
Normal 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.
|
||||
}
|
||||
};
|
||||
};
|
@ -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")) {
|
||||
|
@ -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
|
||||
|
@ -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':
|
||||
|
24
dom/wifi/nsIDOMMozWifiP2pStatusChangeEvent.idl
Normal file
24
dom/wifi/nsIDOMMozWifiP2pStatusChangeEvent.idl
Normal 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;
|
||||
};
|
@ -24,6 +24,7 @@ simple_events = [
|
||||
'StyleSheetChangeEvent',
|
||||
'StyleSheetApplicableStateChangeEvent',
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
'MozWifiP2pStatusChangeEvent',
|
||||
'MozWifiStatusChangeEvent',
|
||||
'MozWifiConnectionInfoEvent',
|
||||
#endif
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
},
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
});
|
||||
});
|
||||
|
@ -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,
|
||||
|
@ -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",
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -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) &&
|
||||
|
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user