Merge m-c to inbound.

This commit is contained in:
Ryan VanderMeulen 2013-08-14 17:11:30 -04:00
commit 8b8be2a46a
48 changed files with 1597 additions and 294 deletions

View File

@ -35,7 +35,7 @@ RUN_TIMEOUT = 1.5 * 60 * 60 # 1.5 Hour
# Maximum time we'll wait for tests to emit output, in seconds.
# The purpose of this timeout is to recover from hangs. It should be longer
# than the amount of time any test takes to report results.
OUTPUT_TIMEOUT = 60 # one minute
OUTPUT_TIMEOUT = 60 * 5 # five minutes
def follow_file(filename):
"""

View File

@ -30,27 +30,37 @@ if (app.is('Firefox')) {
url: 'about:addons',
onReady: function(tab) {
tab.attach({
contentScript: 'AddonManager.getAddonByID("' + self.id + '", function(aAddon) {\n' +
'unsafeWindow.gViewController.viewObjects.detail.node.addEventListener("ViewChanged", function whenViewChanges() {\n' +
'unsafeWindow.gViewController.viewObjects.detail.node.removeEventListener("ViewChanged", whenViewChanges, false);\n' +
'setTimeout(function() {\n' + // TODO: figure out why this is necessary..
'self.postMessage({\n' +
'somePreference: getAttributes(unsafeWindow.document.querySelector("setting[title=\'some-title\']")),\n' +
'myInteger: getAttributes(unsafeWindow.document.querySelector("setting[title=\'my-int\']")),\n' +
'myHiddenInt: getAttributes(unsafeWindow.document.querySelector("setting[title=\'hidden-int\']"))\n' +
'});\n' +
'}, 250);\n' +
'}, false);\n' +
'unsafeWindow.gViewController.commands.cmd_showItemDetails.doCommand(aAddon, true);\n' +
'});\n' +
'function getAttributes(ele) {\n' +
'if (!ele) return {};\n' +
'return {\n' +
'pref: ele.getAttribute("pref"),\n' +
'type: ele.getAttribute("type"),\n' +
'title: ele.getAttribute("title"),\n' +
'desc: ele.getAttribute("desc")\n' +
contentScriptWhen: 'end',
contentScript: 'function onLoad() {\n' +
'unsafeWindow.removeEventListener("load", onLoad, false);\n' +
'AddonManager.getAddonByID("' + self.id + '", function(aAddon) {\n' +
'unsafeWindow.gViewController.viewObjects.detail.node.addEventListener("ViewChanged", function whenViewChanges() {\n' +
'unsafeWindow.gViewController.viewObjects.detail.node.removeEventListener("ViewChanged", whenViewChanges, false);\n' +
'setTimeout(function() {\n' + // TODO: figure out why this is necessary..
'self.postMessage({\n' +
'somePreference: getAttributes(unsafeWindow.document.querySelector("setting[title=\'some-title\']")),\n' +
'myInteger: getAttributes(unsafeWindow.document.querySelector("setting[title=\'my-int\']")),\n' +
'myHiddenInt: getAttributes(unsafeWindow.document.querySelector("setting[title=\'hidden-int\']"))\n' +
'});\n' +
'}, 250);\n' +
'}, false);\n' +
'unsafeWindow.gViewController.commands.cmd_showItemDetails.doCommand(aAddon, true);\n' +
'});\n' +
'function getAttributes(ele) {\n' +
'if (!ele) return {};\n' +
'return {\n' +
'pref: ele.getAttribute("pref"),\n' +
'type: ele.getAttribute("type"),\n' +
'title: ele.getAttribute("title"),\n' +
'desc: ele.getAttribute("desc")\n' +
'}\n' +
'}\n' +
'}\n' +
// Wait for the load event ?
'if (document.readyState == "complete") {\n' +
'onLoad()\n' +
'} else {\n' +
'unsafeWindow.addEventListener("load", onLoad, false);\n' +
'}\n',
onMessage: function(msg) {
// test somePreference

View File

@ -834,18 +834,29 @@ exports.testAttachUnwrapped = function (test) {
exports['test window focus changes active tab'] = function(test) {
test.waitUntilDone();
let url1 = "data:text/html;charset=utf-8," + encodeURIComponent("test window focus changes active tab</br><h1>Window #1");
let win1 = openBrowserWindow(function() {
test.pass("window 1 is open");
let win2 = openBrowserWindow(function() {
tabs.on("activate", function onActivate() {
test.pass("window 2 is open");
tabs.on("activate", function onActivate(tab) {
tabs.removeListener("activate", onActivate);
test.pass("activate was called on windows focus change.");
closeBrowserWindow(win1, function() {
closeBrowserWindow(win2, function() { test.done(); });
});
test.assertEqual(tab.url, url1, 'the activated tab url is correct');
close(win2).then(function() {
test.pass('window 2 was closed');
return close(win1);
}).then(test.done.bind(test));
});
win1.focus();
}, "data:text/html;charset=utf-8,test window focus changes active tab</br><h1>Window #2");
}, "data:text/html;charset=utf-8,test window focus changes active tab</br><h1>Window #1");
}, url1);
};
exports['test ready event on new window tab'] = function(test) {

View File

@ -129,11 +129,12 @@ exports["test Show Hide Panel"] = function(assert, done) {
exports["test Document Reload"] = function(assert, done) {
const { Panel } = require('sdk/panel');
let url2 = "data:text/html;charset=utf-8,page2";
let content =
"<script>" +
"window.onload = function() {" +
" setTimeout(function () {" +
" window.location = 'about:blank';" +
" window.location = '" + url2 + "';" +
" }, 0);" +
"}" +
"</script>";
@ -144,11 +145,13 @@ exports["test Document Reload"] = function(assert, done) {
contentScript: "self.postMessage(window.location.href)",
onMessage: function (message) {
messageCount++;
assert.notEqual(message, 'about:blank', 'about:blank is not a message ' + messageCount);
if (messageCount == 1) {
assert.ok(/data:text\/html/.test(message), "First document had a content script " + message);
}
else if (messageCount == 2) {
assert.equal(message, "about:blank", "Second document too");
assert.equal(message, url2, "Second document too");
panel.destroy();
done();
}

View File

@ -301,7 +301,10 @@ let FormAssistant = {
break;
}
if (target instanceof HTMLDocument || target == content) {
if (target instanceof HTMLDocument ||
// Bug 811177, we don't support editing the entire document.
target instanceof HTMLBodyElement ||
target == content) {
break;
}
@ -335,12 +338,20 @@ let FormAssistant = {
break;
case 'mousedown':
if (!this.focusedElement) {
break;
}
// We only listen for this event on the currently focused element.
// When the mouse goes down, note the cursor/selection position
this.updateSelection();
break;
case 'mouseup':
if (!this.focusedElement) {
break;
}
// We only listen for this event on the currently focused element.
// When the mouse goes up, see if the cursor has moved (or the
// selection changed) since the mouse went down. If it has, we
@ -524,11 +535,8 @@ let FormAssistant = {
}
case "Forms:GetText": {
let isPlainTextField = target instanceof HTMLInputElement ||
target instanceof HTMLTextAreaElement;
let value = isPlainTextField ?
target.value :
getContentEditableText(target);
let value = isContentEditable(target) ? getContentEditableText(target)
: target.value;
if (json.offset && json.length) {
value = value.substr(json.offset, json.length);
@ -633,12 +641,8 @@ let FormAssistant = {
let element = this.focusedElement;
let range = getSelectionRange(element);
let isPlainTextField = element instanceof HTMLInputElement ||
element instanceof HTMLTextAreaElement;
let text = isPlainTextField ?
element.value :
getContentEditableText(element);
let text = isContentEditable(element) ? getContentEditableText(element)
: element.value;
let textAround = getTextAroundCursor(text, range);
@ -691,6 +695,15 @@ function isContentEditable(element) {
return element.ownerDocument && element.ownerDocument.designMode == "on";
}
function isPlainTextField(element) {
if (!element) {
return false;
}
return element instanceof HTMLInputElement ||
element instanceof HTMLTextAreaElement;
}
function getJSON(element, focusCounter) {
let type = element.type || "";
let value = element.value || "";
@ -832,7 +845,7 @@ function getDocumentEncoder(element) {
// Get the visible content text of a content editable element
function getContentEditableText(element) {
if (!element) {
if (!element || !isContentEditable(element)) {
return null;
}
@ -847,8 +860,7 @@ function getContentEditableText(element) {
function getSelectionRange(element) {
let start = 0;
let end = 0;
if (element instanceof HTMLInputElement ||
element instanceof HTMLTextAreaElement) {
if (isPlainTextField(element)) {
// Get the selection range of <input> and <textarea> elements
start = element.selectionStart;
end = element.selectionEnd;
@ -856,8 +868,12 @@ function getSelectionRange(element) {
// Get the selection range of contenteditable elements
let win = element.ownerDocument.defaultView;
let sel = win.getSelection();
start = getContentEditableSelectionStart(element, sel);
end = start + getContentEditableSelectionLength(element, sel);
if (sel) {
start = getContentEditableSelectionStart(element, sel);
end = start + getContentEditableSelectionLength(element, sel);
} else {
dump("Failed to get window.getSelection()\n");
}
}
return [start, end];
}
@ -879,18 +895,17 @@ function getContentEditableSelectionLength(element, selection) {
}
function setSelectionRange(element, start, end) {
let isPlainTextField = element instanceof HTMLInputElement ||
element instanceof HTMLTextAreaElement;
let isTextField = isPlainTextField(element);
// Check the parameters
if (!isPlainTextField && !isContentEditable(element)) {
if (!isTextField && !isContentEditable(element)) {
// Skip HTMLOptionElement and HTMLSelectElement elements, as they don't
// support the operation of setSelectionRange
return;
}
let text = isPlainTextField ? element.value : getContentEditableText(element);
let text = isTextField ? element.value : getContentEditableText(element);
let length = text.length;
if (start < 0) {
start = 0;
@ -902,7 +917,7 @@ function setSelectionRange(element, start, end) {
start = end;
}
if (isPlainTextField) {
if (isTextField) {
// Set the selection range of <input> and <textarea> elements
element.setSelectionRange(start, end, "forward");
} else {
@ -936,8 +951,7 @@ function setSelectionRange(element, start, end) {
function getPlaintextEditor(element) {
let editor = null;
// Get nsIEditor
if (element instanceof HTMLInputElement ||
element instanceof HTMLTextAreaElement) {
if (isPlainTextField(element)) {
// Get from the <input> and <textarea> elements
editor = element.QueryInterface(Ci.nsIDOMNSEditableElement).editor;
} else if (isContentEditable(element)) {

View File

@ -1,4 +1,4 @@
{
"revision": "47daabb93939a38f1f8efa7fdb1cd22ef8445600",
"revision": "58c6fafc2ed4884ccc1617896f3371bc2762c75e",
"repo_path": "/integration/gaia-central"
}

View File

@ -7,9 +7,7 @@
}
.highlighter-controls {
position: absolute;
top: 0;
left: 0;
position: relative;
}
.highlighter-outline-container {

View File

@ -222,8 +222,9 @@ if test "$CPU_ARCH" = "arm"; then
dnl This matches media/webrtc/trunk/webrtc/build/common.gypi.
if test -n "$ARM_ARCH"; then
if test "$ARM_ARCH" -lt 7; then
BUILD_ARM_NEON=0
BUILD_ARM_NEON=
else
AC_DEFINE(BUILD_ARM_NEON)
BUILD_ARM_NEON=1
fi
fi

View File

@ -706,6 +706,7 @@ GK_ATOM(ondragover, "ondragover")
GK_ATOM(ondragstart, "ondragstart")
GK_ATOM(ondrop, "ondrop")
GK_ATOM(onenabled, "onenabled")
GK_ATOM(onemergencycbmodechange, "onemergencycbmodechange")
GK_ATOM(onerror, "onerror")
GK_ATOM(onfailed, "onfailed")
GK_ATOM(onfocus, "onfocus")

View File

@ -1544,35 +1544,6 @@ nsEventStateManager::GetChildProcessOffset(nsFrameLoader* aFrameLoader,
return LayoutDeviceIntPoint::FromAppUnitsToNearest(pt, presContext->AppUnitsPerDevPixel());
}
/*static*/ void
nsEventStateManager::MapEventCoordinatesForChildProcess(
const LayoutDeviceIntPoint& aOffset, nsEvent* aEvent)
{
if (aEvent->eventStructType != NS_TOUCH_EVENT) {
aEvent->refPoint = aOffset;
} else {
aEvent->refPoint = LayoutDeviceIntPoint();
nsTouchEvent* touchEvent = static_cast<nsTouchEvent*>(aEvent);
// Then offset all the touch points by that distance, to put them
// in the space where top-left is 0,0.
const nsTArray< nsRefPtr<Touch> >& touches = touchEvent->touches;
for (uint32_t i = 0; i < touches.Length(); ++i) {
Touch* touch = touches[i];
if (touch) {
touch->mRefPoint += LayoutDeviceIntPoint::ToUntyped(aOffset);
}
}
}
}
/*static*/ void
nsEventStateManager::MapEventCoordinatesForChildProcess(nsFrameLoader* aFrameLoader,
nsEvent* aEvent)
{
LayoutDeviceIntPoint offset = GetChildProcessOffset(aFrameLoader, *aEvent);
MapEventCoordinatesForChildProcess(offset, aEvent);
}
bool
CrossProcessSafeEvent(const nsEvent& aEvent)
{
@ -1686,8 +1657,6 @@ nsEventStateManager::HandleCrossProcessEvent(nsEvent *aEvent,
continue;
}
MapEventCoordinatesForChildProcess(frameLoader, aEvent);
dispatched |= DispatchCrossProcessEvent(aEvent, frameLoader, aStatus);
}
return dispatched;

View File

@ -197,12 +197,6 @@ public:
static LayoutDeviceIntPoint GetChildProcessOffset(nsFrameLoader* aFrameLoader,
const nsEvent& aEvent);
static void MapEventCoordinatesForChildProcess(nsFrameLoader* aFrameLoader,
nsEvent* aEvent);
static void MapEventCoordinatesForChildProcess(const LayoutDeviceIntPoint& aOffset,
nsEvent* aEvent);
// Holds the point in screen coords that a mouse event was dispatched to,
// before we went into pointer lock mode. This is constantly updated while
// the pointer is not locked, but we don't update it while the pointer is

View File

@ -4,7 +4,11 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/arm.h"
#include "AudioNodeEngine.h"
#ifdef BUILD_ARM_NEON
#include "AudioNodeEngineNEON.h"
#endif
namespace mozilla {
@ -58,6 +62,12 @@ void AudioBufferAddWithScale(const float* aInput,
float* aOutput,
uint32_t aSize)
{
#ifdef BUILD_ARM_NEON
if (mozilla::supports_neon()) {
AudioBufferAddWithScale_NEON(aInput, aScale, aOutput, aSize);
return;
}
#endif
if (aScale == 1.0f) {
for (uint32_t i = 0; i < aSize; ++i) {
aOutput[i] += aInput[i];
@ -85,6 +95,12 @@ AudioBlockCopyChannelWithScale(const float* aInput,
if (aScale == 1.0f) {
memcpy(aOutput, aInput, WEBAUDIO_BLOCK_SIZE*sizeof(float));
} else {
#ifdef BUILD_ARM_NEON
if (mozilla::supports_neon()) {
AudioBlockCopyChannelWithScale_NEON(aInput, aScale, aOutput);
return;
}
#endif
for (uint32_t i = 0; i < WEBAUDIO_BLOCK_SIZE; ++i) {
aOutput[i] = aInput[i]*aScale;
}
@ -114,6 +130,12 @@ AudioBlockCopyChannelWithScale(const float aInput[WEBAUDIO_BLOCK_SIZE],
const float aScale[WEBAUDIO_BLOCK_SIZE],
float aOutput[WEBAUDIO_BLOCK_SIZE])
{
#ifdef BUILD_ARM_NEON
if (mozilla::supports_neon()) {
AudioBlockCopyChannelWithScale_NEON(aInput, aScale, aOutput);
return;
}
#endif
for (uint32_t i = 0; i < WEBAUDIO_BLOCK_SIZE; ++i) {
aOutput[i] = aInput[i]*aScale[i];
}
@ -136,6 +158,12 @@ AudioBufferInPlaceScale(float* aBlock,
if (aScale == 1.0f) {
return;
}
#ifdef BUILD_ARM_NEON
if (mozilla::supports_neon()) {
AudioBufferInPlaceScale_NEON(aBlock, aChannelCount, aScale, aSize);
return;
}
#endif
for (uint32_t i = 0; i < aSize * aChannelCount; ++i) {
*aBlock++ *= aScale;
}
@ -158,6 +186,15 @@ AudioBlockPanStereoToStereo(const float aInputL[WEBAUDIO_BLOCK_SIZE],
float aOutputL[WEBAUDIO_BLOCK_SIZE],
float aOutputR[WEBAUDIO_BLOCK_SIZE])
{
#ifdef BUILD_ARM_NEON
if (mozilla::supports_neon()) {
AudioBlockPanStereoToStereo_NEON(aInputL, aInputR,
aGainL, aGainR, aIsOnTheLeft,
aOutputL, aOutputR);
return;
}
#endif
uint32_t i;
if (aIsOnTheLeft) {

View File

@ -0,0 +1,250 @@
/* -*- mode: c++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* this source code form is subject to the terms of the mozilla public
* license, v. 2.0. if a copy of the mpl was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "AudioNodeEngineNEON.h"
#include <arm_neon.h>
#ifdef DEBUG
#define ASSERT_ALIGNED(ptr) \
MOZ_ASSERT((((uintptr_t)ptr + 15) & ~0x0F) == (uintptr_t)ptr, \
#ptr " has to be aligned 16-bytes aligned.");
#else
#define ASSERT_ALIGNED(ptr)
#endif
#define ADDRESS_OF(array, index) ((float32_t*)&array[index])
namespace mozilla {
void AudioBufferAddWithScale_NEON(const float* aInput,
float aScale,
float* aOutput,
uint32_t aSize)
{
ASSERT_ALIGNED(aInput);
ASSERT_ALIGNED(aOutput);
float32x4_t vin0, vin1, vin2, vin3;
float32x4_t vout0, vout1, vout2, vout3;
float32x4_t vscale = vmovq_n_f32(aScale);
uint32_t dif = aSize % 16;
aSize -= dif;
unsigned i = 0;
for (; i < aSize; i+=16) {
vin0 = vld1q_f32(ADDRESS_OF(aInput, i));
vin1 = vld1q_f32(ADDRESS_OF(aInput, i+4));
vin2 = vld1q_f32(ADDRESS_OF(aInput, i+8));
vin3 = vld1q_f32(ADDRESS_OF(aInput, i+12));
vout0 = vld1q_f32(ADDRESS_OF(aOutput, i));
vout1 = vld1q_f32(ADDRESS_OF(aOutput, i+4));
vout2 = vld1q_f32(ADDRESS_OF(aOutput, i+8));
vout3 = vld1q_f32(ADDRESS_OF(aOutput, i+12));
vout0 = vmlaq_f32(vout0, vin0, vscale);
vout1 = vmlaq_f32(vout1, vin1, vscale);
vout2 = vmlaq_f32(vout2, vin2, vscale);
vout3 = vmlaq_f32(vout3, vin3, vscale);
vst1q_f32(ADDRESS_OF(aOutput, i), vout0);
vst1q_f32(ADDRESS_OF(aOutput, i+4), vout1);
vst1q_f32(ADDRESS_OF(aOutput, i+8), vout2);
vst1q_f32(ADDRESS_OF(aOutput, i+12), vout3);
}
for (unsigned j = 0; j < dif; ++i, ++j) {
aOutput[i] += aInput[i]*aScale;
}
}
void
AudioBlockCopyChannelWithScale_NEON(const float* aInput,
float aScale,
float* aOutput)
{
ASSERT_ALIGNED(aInput);
ASSERT_ALIGNED(aOutput);
float32x4_t vin0, vin1, vin2, vin3;
float32x4_t vout0, vout1, vout2, vout3;
float32x4_t vscale = vmovq_n_f32(aScale);
for (uint32_t i = 0; i < WEBAUDIO_BLOCK_SIZE; i+=16) {
vin0 = vld1q_f32(ADDRESS_OF(aInput, i));
vin1 = vld1q_f32(ADDRESS_OF(aInput, i+4));
vin2 = vld1q_f32(ADDRESS_OF(aInput, i+8));
vin3 = vld1q_f32(ADDRESS_OF(aInput, i+12));
vout0 = vmulq_f32(vin0, vscale);
vout1 = vmulq_f32(vin1, vscale);
vout2 = vmulq_f32(vin2, vscale);
vout3 = vmulq_f32(vin3, vscale);
vst1q_f32(ADDRESS_OF(aOutput, i), vout0);
vst1q_f32(ADDRESS_OF(aOutput, i+4), vout1);
vst1q_f32(ADDRESS_OF(aOutput, i+8), vout2);
vst1q_f32(ADDRESS_OF(aOutput, i+12), vout3);
}
}
void
AudioBlockCopyChannelWithScale_NEON(const float aInput[WEBAUDIO_BLOCK_SIZE],
const float aScale[WEBAUDIO_BLOCK_SIZE],
float aOutput[WEBAUDIO_BLOCK_SIZE])
{
ASSERT_ALIGNED(aInput);
ASSERT_ALIGNED(aScale);
ASSERT_ALIGNED(aOutput);
float32x4_t vin0, vin1, vin2, vin3;
float32x4_t vout0, vout1, vout2, vout3;
float32x4_t vscale0, vscale1, vscale2, vscale3;
for (uint32_t i = 0; i < WEBAUDIO_BLOCK_SIZE; i+=16) {
vin0 = vld1q_f32(ADDRESS_OF(aInput, i));
vin1 = vld1q_f32(ADDRESS_OF(aInput, i+4));
vin2 = vld1q_f32(ADDRESS_OF(aInput, i+8));
vin3 = vld1q_f32(ADDRESS_OF(aInput, i+12));
vscale0 = vld1q_f32(ADDRESS_OF(aScale, i));
vscale1 = vld1q_f32(ADDRESS_OF(aScale, i+4));
vscale2 = vld1q_f32(ADDRESS_OF(aScale, i+8));
vscale3 = vld1q_f32(ADDRESS_OF(aScale, i+12));
vout0 = vmulq_f32(vin0, vscale0);
vout1 = vmulq_f32(vin1, vscale1);
vout2 = vmulq_f32(vin2, vscale2);
vout3 = vmulq_f32(vin3, vscale3);
vst1q_f32(ADDRESS_OF(aOutput, i), vout0);
vst1q_f32(ADDRESS_OF(aOutput, i+4), vout1);
vst1q_f32(ADDRESS_OF(aOutput, i+8), vout2);
vst1q_f32(ADDRESS_OF(aOutput, i+12), vout3);
}
}
void
AudioBufferInPlaceScale_NEON(float* aBlock,
uint32_t aChannelCount,
float aScale,
uint32_t aSize)
{
ASSERT_ALIGNED(aBlock);
float32x4_t vin0, vin1, vin2, vin3;
float32x4_t vout0, vout1, vout2, vout3;
float32x4_t vscale = vmovq_n_f32(aScale);
uint32_t totalSize = aSize * aChannelCount;
uint32_t dif = totalSize % 16;
totalSize -= dif;
uint32_t i = 0;
for (; i < totalSize; i+=16) {
vin0 = vld1q_f32(ADDRESS_OF(aBlock, i));
vin1 = vld1q_f32(ADDRESS_OF(aBlock, i+4));
vin2 = vld1q_f32(ADDRESS_OF(aBlock, i+8));
vin3 = vld1q_f32(ADDRESS_OF(aBlock, i+12));
vout0 = vmulq_f32(vin0, vscale);
vout1 = vmulq_f32(vin1, vscale);
vout2 = vmulq_f32(vin2, vscale);
vout3 = vmulq_f32(vin3, vscale);
vst1q_f32(ADDRESS_OF(aBlock, i), vout0);
vst1q_f32(ADDRESS_OF(aBlock, i+4), vout1);
vst1q_f32(ADDRESS_OF(aBlock, i+8), vout2);
vst1q_f32(ADDRESS_OF(aBlock, i+12), vout3);
}
for (unsigned j = 0; j < dif; ++i, ++j) {
aBlock[i] *= aScale;
}
}
void
AudioBlockPanStereoToStereo_NEON(const float aInputL[WEBAUDIO_BLOCK_SIZE],
const float aInputR[WEBAUDIO_BLOCK_SIZE],
float aGainL, float aGainR, bool aIsOnTheLeft,
float aOutputL[WEBAUDIO_BLOCK_SIZE],
float aOutputR[WEBAUDIO_BLOCK_SIZE])
{
ASSERT_ALIGNED(aInputL);
ASSERT_ALIGNED(aInputR);
ASSERT_ALIGNED(aOutputL);
ASSERT_ALIGNED(aOutputR);
float32x4_t vinL0, vinL1, vinL2, vinL3;
float32x4_t vinR0, vinR1, vinR2, vinR3;
float32x4_t voutL0, voutL1, voutL2, voutL3;
float32x4_t voutR0, voutR1, voutR2, voutR3;
float32x4_t vscaleL = vmovq_n_f32(aGainL);
float32x4_t vscaleR = vmovq_n_f32(aGainR);
if (aIsOnTheLeft) {
for (uint32_t i = 0; i < WEBAUDIO_BLOCK_SIZE; i+=16) {
vinL0 = vld1q_f32(ADDRESS_OF(aInputL, i));
vinL1 = vld1q_f32(ADDRESS_OF(aInputL, i+4));
vinL2 = vld1q_f32(ADDRESS_OF(aInputL, i+8));
vinL3 = vld1q_f32(ADDRESS_OF(aInputL, i+12));
vinR0 = vld1q_f32(ADDRESS_OF(aInputR, i));
vinR1 = vld1q_f32(ADDRESS_OF(aInputR, i+4));
vinR2 = vld1q_f32(ADDRESS_OF(aInputR, i+8));
vinR3 = vld1q_f32(ADDRESS_OF(aInputR, i+12));
voutL0 = vmlaq_f32(vinL0, vinR0, vscaleL);
voutL1 = vmlaq_f32(vinL1, vinR1, vscaleL);
voutL2 = vmlaq_f32(vinL2, vinR2, vscaleL);
voutL3 = vmlaq_f32(vinL3, vinR3, vscaleL);
vst1q_f32(ADDRESS_OF(aOutputL, i), voutL0);
vst1q_f32(ADDRESS_OF(aOutputL, i+4), voutL1);
vst1q_f32(ADDRESS_OF(aOutputL, i+8), voutL2);
vst1q_f32(ADDRESS_OF(aOutputL, i+12), voutL3);
voutR0 = vmulq_f32(vinR0, vscaleR);
voutR1 = vmulq_f32(vinR1, vscaleR);
voutR2 = vmulq_f32(vinR2, vscaleR);
voutR3 = vmulq_f32(vinR3, vscaleR);
vst1q_f32(ADDRESS_OF(aOutputR, i), voutR0);
vst1q_f32(ADDRESS_OF(aOutputR, i+4), voutR1);
vst1q_f32(ADDRESS_OF(aOutputR, i+8), voutR2);
vst1q_f32(ADDRESS_OF(aOutputR, i+12), voutR3);
}
} else {
for (uint32_t i = 0; i < WEBAUDIO_BLOCK_SIZE; i+=16) {
vinL0 = vld1q_f32(ADDRESS_OF(aInputL, i));
vinL1 = vld1q_f32(ADDRESS_OF(aInputL, i+4));
vinL2 = vld1q_f32(ADDRESS_OF(aInputL, i+8));
vinL3 = vld1q_f32(ADDRESS_OF(aInputL, i+12));
vinR0 = vld1q_f32(ADDRESS_OF(aInputR, i));
vinR1 = vld1q_f32(ADDRESS_OF(aInputR, i+4));
vinR2 = vld1q_f32(ADDRESS_OF(aInputR, i+8));
vinR3 = vld1q_f32(ADDRESS_OF(aInputR, i+12));
voutL0 = vmulq_f32(vinL0, vscaleL);
voutL1 = vmulq_f32(vinL1, vscaleL);
voutL2 = vmulq_f32(vinL2, vscaleL);
voutL3 = vmulq_f32(vinL3, vscaleL);
vst1q_f32(ADDRESS_OF(aOutputL, i), voutL0);
vst1q_f32(ADDRESS_OF(aOutputL, i+4), voutL1);
vst1q_f32(ADDRESS_OF(aOutputL, i+8), voutL2);
vst1q_f32(ADDRESS_OF(aOutputL, i+12), voutL3);
voutR0 = vmlaq_f32(vinR0, vinL0, vscaleR);
voutR1 = vmlaq_f32(vinR1, vinL1, vscaleR);
voutR2 = vmlaq_f32(vinR2, vinL2, vscaleR);
voutR3 = vmlaq_f32(vinR3, vinL3, vscaleR);
vst1q_f32(ADDRESS_OF(aOutputR, i), voutR0);
vst1q_f32(ADDRESS_OF(aOutputR, i+4), voutR1);
vst1q_f32(ADDRESS_OF(aOutputR, i+8), voutR2);
vst1q_f32(ADDRESS_OF(aOutputR, i+12), voutR3);
}
}
}
}

View File

@ -0,0 +1,41 @@
/* -*- mode: c++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* this source code form is subject to the terms of the mozilla public
* license, v. 2.0. if a copy of the mpl was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef MOZILLA_AUDIONODEENGINENEON_H_
#define MOZILLA_AUDIONODEENGINENEON_H_
#include "AudioNodeEngine.h"
namespace mozilla {
void AudioBufferAddWithScale_NEON(const float* aInput,
float aScale,
float* aOutput,
uint32_t aSize);
void
AudioBlockCopyChannelWithScale_NEON(const float* aInput,
float aScale,
float* aOutput);
void
AudioBlockCopyChannelWithScale_NEON(const float aInput[WEBAUDIO_BLOCK_SIZE],
const float aScale[WEBAUDIO_BLOCK_SIZE],
float aOutput[WEBAUDIO_BLOCK_SIZE]);
void
AudioBufferInPlaceScale_NEON(float* aBlock,
uint32_t aChannelCount,
float aScale,
uint32_t aSize);
void
AudioBlockPanStereoToStereo_NEON(const float aInputL[WEBAUDIO_BLOCK_SIZE],
const float aInputR[WEBAUDIO_BLOCK_SIZE],
float aGainL, float aGainR, bool aIsOnTheLeft,
float aOutputL[WEBAUDIO_BLOCK_SIZE],
float aOutputR[WEBAUDIO_BLOCK_SIZE]);
}
#endif /* MOZILLA_AUDIONODEENGINENEON_H_ */

View File

@ -29,3 +29,5 @@ endif
CFLAGS += $(GSTREAMER_CFLAGS)
CXXFLAGS += $(GSTREAMER_CFLAGS)
AudioNodeEngineNEON.$(OBJ_SUFFIX): CXXFLAGS += -mfpu=neon

View File

@ -272,11 +272,10 @@ OpusTrackEncoder::GetEncodedTrack(nsTArray<uint8_t>* aOutput,
if (!chunk.IsNull()) {
// Append the interleaved data to the end of pcm buffer.
InterleaveTrackData(chunk, frameToCopy, mChannels,
pcm.Elements() + frameCopied);
pcm.Elements() + frameCopied * mChannels);
} else {
for (int i = 0; i < frameToCopy * mChannels; i++) {
pcm.AppendElement(0);
}
memset(pcm.Elements() + frameCopied * mChannels, 0,
frameToCopy * mChannels * sizeof(AudioDataValue));
}
frameCopied += frameToCopy;
@ -301,9 +300,8 @@ OpusTrackEncoder::GetEncodedTrack(nsTArray<uint8_t>* aOutput,
// Append null data to pcm buffer if the leftover data is not enough for
// opus encoder.
if (frameCopied < GetPacketDuration() && mEndOfStream) {
for (int i = frameCopied * mChannels; i < GetPacketDuration() * mChannels; i++) {
pcm.AppendElement(0);
}
memset(pcm.Elements() + frameCopied * mChannels, 0,
(GetPacketDuration()-frameCopied)*mChannels*sizeof(AudioDataValue));
}
// Encode the data with Opus Encoder.

View File

@ -130,3 +130,8 @@ CPP_SOURCES += [
'VideoUtils.cpp',
'WebVTTLoadListener.cpp',
]
if CONFIG['CPU_ARCH'] == 'arm' and CONFIG['BUILD_ARM_NEON']:
CPP_SOURCES += [
'AudioNodeEngineNEON.cpp',
]

View File

@ -599,6 +599,39 @@ TabParent::SendKeyEvent(const nsAString& aType,
}
}
bool
TabParent::MapEventCoordinatesForChildProcess(nsEvent* aEvent)
{
nsRefPtr<nsFrameLoader> frameLoader = GetFrameLoader();
if (!frameLoader) {
return false;
}
LayoutDeviceIntPoint offset = nsEventStateManager::GetChildProcessOffset(frameLoader, *aEvent);
MapEventCoordinatesForChildProcess(offset, aEvent);
return true;
}
void
TabParent::MapEventCoordinatesForChildProcess(
const LayoutDeviceIntPoint& aOffset, nsEvent* aEvent)
{
if (aEvent->eventStructType != NS_TOUCH_EVENT) {
aEvent->refPoint = aOffset;
} else {
aEvent->refPoint = LayoutDeviceIntPoint();
nsTouchEvent* touchEvent = static_cast<nsTouchEvent*>(aEvent);
// Then offset all the touch points by that distance, to put them
// in the space where top-left is 0,0.
const nsTArray< nsRefPtr<Touch> >& touches = touchEvent->touches;
for (uint32_t i = 0; i < touches.Length(); ++i) {
Touch* touch = touches[i];
if (touch) {
touch->mRefPoint += LayoutDeviceIntPoint::ToUntyped(aOffset);
}
}
}
}
bool TabParent::SendRealMouseEvent(nsMouseEvent& event)
{
if (mIsDestroyed) {
@ -606,6 +639,9 @@ bool TabParent::SendRealMouseEvent(nsMouseEvent& event)
}
nsMouseEvent e(event);
MaybeForwardEventToRenderFrame(event, &e);
if (!MapEventCoordinatesForChildProcess(&e)) {
return false;
}
return PBrowserParent::SendRealMouseEvent(e);
}
@ -616,6 +652,9 @@ bool TabParent::SendMouseWheelEvent(WheelEvent& event)
}
WheelEvent e(event);
MaybeForwardEventToRenderFrame(event, &e);
if (!MapEventCoordinatesForChildProcess(&e)) {
return false;
}
return PBrowserParent::SendMouseWheelEvent(event);
}
@ -626,6 +665,9 @@ bool TabParent::SendRealKeyEvent(nsKeyEvent& event)
}
nsKeyEvent e(event);
MaybeForwardEventToRenderFrame(event, &e);
if (!MapEventCoordinatesForChildProcess(&e)) {
return false;
}
return PBrowserParent::SendRealKeyEvent(e);
}
@ -668,6 +710,9 @@ bool TabParent::SendRealTouchEvent(nsTouchEvent& event)
}
MaybeForwardEventToRenderFrame(event, &e);
MapEventCoordinatesForChildProcess(mChildProcessOffsetAtTouchStart, &e);
return (e.message == NS_TOUCH_MOVE) ?
PBrowserParent::SendRealTouchMoveEvent(e) :
PBrowserParent::SendRealTouchEvent(e);
@ -704,9 +749,6 @@ TabParent::TryCapture(const nsGUIEvent& aEvent)
return false;
}
nsEventStateManager::MapEventCoordinatesForChildProcess(
mChildProcessOffsetAtTouchStart, &event);
SendRealTouchEvent(event);
return true;
}

View File

@ -187,6 +187,10 @@ public:
void Activate();
void Deactivate();
bool MapEventCoordinatesForChildProcess(nsEvent* aEvent);
void MapEventCoordinatesForChildProcess(const LayoutDeviceIntPoint& aOffset,
nsEvent* aEvent);
void SendMouseEvent(const nsAString& aType, float aX, float aY,
int32_t aButton, int32_t aClickCount,
int32_t aModifiers, bool aIgnoreRootScrollFrame);

View File

@ -21,6 +21,7 @@ if CONFIG['MOZ_B2G_RIL']:
XPIDL_SOURCES += [
'nsIDOMCFStateChangeEvent.idl',
'nsIDOMMobileConnection.idl',
'nsIDOMMozEmergencyCbModeEvent.idl',
'nsIDOMNetworkStats.idl',
'nsIDOMNetworkStatsManager.idl',
'nsIMobileConnectionProvider.idl',

View File

@ -11,7 +11,7 @@ interface nsIDOMMozMobileNetworkInfo;
interface nsIDOMMozMobileCellInfo;
interface nsIDOMMozMobileCFInfo;
[scriptable, builtinclass, uuid(ae82dac0-d1a0-11e2-8b8b-0800200c9a66)]
[scriptable, builtinclass, uuid(35fbab20-ee07-454d-943d-090a9378f5cf)]
interface nsIDOMMozMobileConnection : nsIDOMEventTarget
{
const long ICC_SERVICE_CLASS_VOICE = (1 << 0);
@ -268,7 +268,7 @@ interface nsIDOMMozMobileConnection : nsIDOMEventTarget
*
* @param clirMode
* Is one of the CLIR_* constants.
*
*
* If successful, the request's onsuccess will be called.
*
* Otherwise, the request's onerror will be called, and the request's error
@ -289,6 +289,16 @@ interface nsIDOMMozMobileConnection : nsIDOMEventTarget
*/
nsIDOMDOMRequest getCallingLineIdRestriction();
/**
* Exit emergency callback mode.
*
* If successful, the request's onsuccess will be called.
*
* Otherwise, the request's onerror will be called, and the request's error
* will be either 'RequestNotSupported' or 'GenericFailure'.
*/
nsIDOMDOMRequest exitEmergencyCbMode();
/**
* The 'voicechange' event is notified whenever the voice connection object
* changes.
@ -318,6 +328,12 @@ interface nsIDOMMozMobileConnection : nsIDOMEventTarget
* state changes.
*/
[implicit_jscontext] attribute jsval oncfstatechange;
/**
* The 'emergencycbmodechange' event is notified whenever the emergency
* callback mode changes.
*/
[implicit_jscontext] attribute jsval onemergencycbmodechange;
};
[scriptable, uuid(49706beb-a160-40b7-b745-50f62e389a2c)]

View File

@ -0,0 +1,31 @@
/* 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(15cc1010-f051-11e2-945c-60a44c237d2b)]
interface nsIDOMMozEmergencyCbModeEvent : nsIDOMEvent
{
/**
* Whether the mode is activated.
*/
readonly attribute boolean active;
/**
* Automatically exit the mode after the timeoutMs ms.
*/
readonly attribute unsigned long timeoutMs;
[noscript] void initMozEmergencyCbModeEvent(in DOMString aType,
in boolean aCanBubble,
in boolean aCancelable,
in boolean aActive,
in unsigned long aTimeoutMs);
};
dictionary MozEmergencyCbModeEventInit : EventInit
{
boolean active;
unsigned long timeoutMs;
};

View File

@ -10,7 +10,7 @@ interface nsIDOMMozMobileConnectionInfo;
interface nsIDOMMozMobileNetworkInfo;
interface nsIDOMWindow;
[scriptable, uuid(74361840-c913-11e2-8b8b-0800200c9a66)]
[scriptable, uuid(7da2d9f6-eba1-4339-bde1-dc6732d42cdf)]
interface nsIMobileConnectionListener : nsISupports
{
void notifyVoiceChanged();
@ -24,13 +24,15 @@ interface nsIMobileConnectionListener : nsISupports
in DOMString number,
in unsigned short timeSeconds,
in unsigned short serviceClass);
void notifyEmergencyCbModeChanged(in boolean active,
in unsigned long timeoutMs);
};
/**
* XPCOM component (in the content process) that provides the mobile
* network information.
*/
[scriptable, uuid(12705160-d1b6-11e2-8b8b-0800200c9a66)]
[scriptable, uuid(a16681a5-a7f1-4340-a478-e0326a49e988)]
interface nsIMobileConnectionProvider : nsISupports
{
/**
@ -73,4 +75,6 @@ interface nsIMobileConnectionProvider : nsISupports
nsIDOMDOMRequest setCallingLineIdRestriction(in nsIDOMWindow window,
in unsigned short clirMode);
nsIDOMDOMRequest getCallingLineIdRestriction(in nsIDOMWindow window);
nsIDOMDOMRequest exitEmergencyCbMode(in nsIDOMWindow window);
};

View File

@ -3,14 +3,16 @@
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "MobileConnection.h"
#include "nsIDOMDOMRequest.h"
#include "nsIDOMClassInfo.h"
#include "nsDOMEvent.h"
#include "nsIDOMUSSDReceivedEvent.h"
#include "nsIDOMDataErrorEvent.h"
#include "nsIDOMCFStateChangeEvent.h"
#include "GeneratedEvents.h"
#include "mozilla/Preferences.h"
#include "nsDOMEvent.h"
#include "nsIDOMCFStateChangeEvent.h"
#include "nsIDOMClassInfo.h"
#include "nsIDOMDOMRequest.h"
#include "nsIDOMDataErrorEvent.h"
#include "nsIDOMMozEmergencyCbModeEvent.h"
#include "nsIDOMUSSDReceivedEvent.h"
#include "nsIPermissionManager.h"
#include "nsJSUtils.h"
@ -73,6 +75,7 @@ NS_IMPL_EVENT_HANDLER(MobileConnection, datachange)
NS_IMPL_EVENT_HANDLER(MobileConnection, ussdreceived)
NS_IMPL_EVENT_HANDLER(MobileConnection, dataerror)
NS_IMPL_EVENT_HANDLER(MobileConnection, cfstatechange)
NS_IMPL_EVENT_HANDLER(MobileConnection, emergencycbmodechange)
MobileConnection::MobileConnection()
{
@ -437,6 +440,22 @@ MobileConnection::SetCallingLineIdRestriction(unsigned short aClirMode,
return mProvider->SetCallingLineIdRestriction(GetOwner(), aClirMode, aRequest);
}
NS_IMETHODIMP
MobileConnection::ExitEmergencyCbMode(nsIDOMDOMRequest** aRequest)
{
*aRequest = nullptr;
if (!CheckPermission("mobileconnection")) {
return NS_OK;
}
if (!mProvider) {
return NS_ERROR_FAILURE;
}
return mProvider->ExitEmergencyCbMode(GetOwner(), aRequest);
}
// nsIMobileConnectionListener
NS_IMETHODIMP
@ -521,3 +540,25 @@ MobileConnection::NotifyCFStateChange(bool aSuccess,
return DispatchTrustedEvent(ce);
}
NS_IMETHODIMP
MobileConnection::NotifyEmergencyCbModeChanged(bool aActive,
uint32_t aTimeoutMs)
{
if (!CheckPermission("mobileconnection")) {
return NS_OK;
}
nsCOMPtr<nsIDOMEvent> event;
NS_NewDOMMozEmergencyCbModeEvent(getter_AddRefs(event), this, nullptr,
nullptr);
MOZ_ASSERT(event);
nsCOMPtr<nsIDOMMozEmergencyCbModeEvent> ce = do_QueryInterface(event);
nsresult rv = ce->InitMozEmergencyCbModeEvent(
NS_LITERAL_STRING("emergencycbmodechange"), false, false,
aActive, aTimeoutMs);
NS_ENSURE_SUCCESS(rv, rv);
return DispatchTrustedEvent(ce);
}

View File

@ -97,6 +97,8 @@ function testUnregistered() {
is(connection.voice.emergencyCallsOnly, false);
is(connection.voice.roaming, false);
is(connection.voice.cell, null);
is(connection.voice.signalStrength, null);
is(connection.voice.relSignalStrength, null);
testSearching();
});
@ -113,6 +115,8 @@ function testSearching() {
is(connection.voice.emergencyCallsOnly, false);
is(connection.voice.roaming, false);
is(connection.voice.cell, null);
is(connection.voice.signalStrength, null);
is(connection.voice.relSignalStrength, null);
testDenied();
});
@ -129,6 +133,8 @@ function testDenied() {
is(connection.voice.emergencyCallsOnly, false);
is(connection.voice.roaming, false);
is(connection.voice.cell, null);
is(connection.voice.signalStrength, null);
is(connection.voice.relSignalStrength, null);
testRoaming();
});
@ -145,6 +151,10 @@ function testRoaming() {
is(connection.voice.emergencyCallsOnly, false);
is(connection.voice.roaming, true);
// Android emulator initializes the signal strength to -99 dBm
is(connection.voice.signalStrength, -99);
is(connection.voice.relSignalStrength, 44);
testHome();
});
}
@ -160,6 +170,10 @@ function testHome() {
is(connection.voice.emergencyCallsOnly, false);
is(connection.voice.roaming, false);
// Android emulator initializes the signal strength to -99 dBm
is(connection.voice.signalStrength, -99);
is(connection.voice.relSignalStrength, 44);
cleanUp();
});
}

View File

@ -75,6 +75,7 @@ const RIL_IPC_MSG_NAMES = [
"RIL:SelectNetwork",
"RIL:SelectNetworkAuto",
"RIL:CallStateChanged",
"RIL:EmergencyCbModeChanged",
"RIL:VoicemailNotification",
"RIL:VoicemailInfoChanged",
"RIL:CallError",
@ -104,7 +105,8 @@ const RIL_IPC_MSG_NAMES = [
"RIL:UpdateIccContact",
"RIL:SetRoamingPreference",
"RIL:GetRoamingPreference",
"RIL:CdmaCallWaiting"
"RIL:CdmaCallWaiting",
"RIL:ExitEmergencyCbMode"
];
XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
@ -1136,6 +1138,24 @@ RILContentHelper.prototype = {
return request;
},
exitEmergencyCbMode: function exitEmergencyCbMode(window) {
if (window == null) {
throw Components.Exception("Can't get window object",
Cr.NS_ERROR_UNEXPECTED);
}
let request = Services.DOMRequest.createRequest(window);
let requestId = this.getRequestId(request);
cpmm.sendAsyncMessage("RIL:ExitEmergencyCbMode", {
clientId: 0,
data: {
requestId: requestId,
}
});
return request;
},
_mobileConnectionListeners: null,
_telephonyListeners: null,
_cellBroadcastListeners: null,
@ -1606,6 +1626,15 @@ RILContentHelper.prototype = {
"notifyCdmaCallWaiting",
[msg.json.data]);
break;
case "RIL:ExitEmergencyCbMode":
this.handleExitEmergencyCbMode(msg.json);
break;
case "RIL:EmergencyCbModeChanged":
let data = msg.json.data;
this._deliverEvent("_mobileConnectionListeners",
"notifyEmergencyCbModeChanged",
[data.active, data.timeoutMs]);
break;
}
},
@ -1791,6 +1820,20 @@ RILContentHelper.prototype = {
this.fireRequestSuccess(message.requestId, status);
},
handleExitEmergencyCbMode: function handleExitEmergencyCbMode(message) {
let requestId = message.requestId;
let request = this.takeRequest(requestId);
if (!request) {
return;
}
if (!message.success) {
Services.DOMRequest.fireError(request, message.errorMsg);
return;
}
Services.DOMRequest.fireSuccess(request, null);
},
handleSendCancelMMI: function handleSendCancelMMI(message) {
debug("handleSendCancelMMI " + JSON.stringify(message));
let request = this.takeRequest(message.requestId);

View File

@ -110,7 +110,8 @@ const RIL_IPC_MOBILECONNECTION_MSG_NAMES = [
"RIL:SetCallingLineIdRestriction",
"RIL:GetCallingLineIdRestriction",
"RIL:SetRoamingPreference",
"RIL:GetRoamingPreference"
"RIL:GetRoamingPreference",
"RIL:ExitEmergencyCbMode"
];
const RIL_IPC_ICCMANAGER_MSG_NAMES = [
@ -856,6 +857,27 @@ RadioInterface.prototype = {
}
},
/**
* A utility function to compare objects. The srcInfo may contain
* 'rilMessageType', should ignore it.
*/
isInfoChanged: function isInfoChanged(srcInfo, destInfo) {
if (!destInfo) {
return true;
}
for (let key in srcInfo) {
if (key === 'rilMessageType') {
continue;
}
if (srcInfo[key] !== destInfo[key]) {
return true;
}
}
return false;
},
/**
* Process a message from the content process.
*/
@ -988,6 +1010,9 @@ RadioInterface.prototype = {
case "RIL:GetCallingLineIdRestriction":
this.workerMessenger.sendWithIPCMessage(msg, "getCLIR");
break;
case "RIL:ExitEmergencyCbMode":
this.workerMessenger.sendWithIPCMessage(msg, "exitEmergencyCbMode");
break;
case "RIL:GetVoicemailInfo":
// This message is sync.
return this.voicemailInfo;
@ -1023,6 +1048,9 @@ RadioInterface.prototype = {
case "suppSvcNotification":
this.handleSuppSvcNotification(message);
break;
case "emergencyCbModeChange":
this.handleEmergencyCbModeChange(message);
break;
case "networkinfochanged":
this.updateNetworkInfo(message);
break;
@ -1095,6 +1123,9 @@ RadioInterface.prototype = {
let lock = gSettingsService.createLock();
lock.set("ril.radio.disabled", !message.on, null, null);
break;
case "exitEmergencyCbMode":
this.handleExitEmergencyCbMode(message);
break;
default:
throw new Error("Don't know about this message type: " +
message.rilMessageType);
@ -1118,6 +1149,7 @@ RadioInterface.prototype = {
let dataMessage = message[RIL.NETWORK_INFO_DATA_REGISTRATION_STATE];
let operatorMessage = message[RIL.NETWORK_INFO_OPERATOR];
let selectionMessage = message[RIL.NETWORK_INFO_NETWORK_SELECTION_MODE];
let signalMessage = message[RIL.NETWORK_INFO_SIGNAL];
// Batch the *InfoChanged messages together
if (voiceMessage) {
@ -1132,17 +1164,21 @@ RadioInterface.prototype = {
this.handleOperatorChange(operatorMessage, true);
}
if (signalMessage) {
this.handleSignalStrengthChange(signalMessage, true);
}
let voice = this.rilContext.voice;
let data = this.rilContext.data;
this.checkRoamingBetweenOperators(voice);
this.checkRoamingBetweenOperators(data);
if (voiceMessage || operatorMessage) {
if (voiceMessage || operatorMessage || signalMessage) {
gMessageManager.sendMobileConnectionMessage("RIL:VoiceInfoChanged",
this.clientId, voice);
}
if (dataMessage || operatorMessage) {
if (dataMessage || operatorMessage || signalMessage) {
gMessageManager.sendMobileConnectionMessage("RIL:DataInfoChanged",
this.clientId, data);
}
@ -1325,34 +1361,37 @@ RadioInterface.prototype = {
}).bind(this));
},
handleSignalStrengthChange: function handleSignalStrengthChange(message) {
/**
* Handle signal strength changes.
*
* @param message The new signal strength.
* @param batch When batch is true, the RIL:VoiceInfoChanged and
* RIL:DataInfoChanged message will not be sent.
*/
handleSignalStrengthChange: function handleSignalStrengthChange(message, batch) {
let voiceInfo = this.rilContext.voice;
if (voiceInfo.signalStrength != message.voice.signalStrength ||
voiceInfo.relSignalStrength != message.voice.relSignalStrength) {
voiceInfo.signalStrength = message.voice.signalStrength;
voiceInfo.relSignalStrength = message.voice.relSignalStrength;
gMessageManager.sendMobileConnectionMessage("RIL:VoiceInfoChanged",
this.clientId, voiceInfo);
// If the voice is not registered, need not to update signal information.
if (voiceInfo.state === RIL.GECKO_MOBILE_CONNECTION_STATE_REGISTERED &&
this.isInfoChanged(message.voice, voiceInfo)) {
this.updateInfo(message.voice, voiceInfo);
if (!batch) {
gMessageManager.sendMobileConnectionMessage("RIL:VoiceInfoChanged",
this.clientId, voiceInfo);
}
}
let dataInfo = this.rilContext.data;
if (dataInfo.signalStrength != message.data.signalStrength ||
dataInfo.relSignalStrength != message.data.relSignalStrength) {
dataInfo.signalStrength = message.data.signalStrength;
dataInfo.relSignalStrength = message.data.relSignalStrength;
gMessageManager.sendMobileConnectionMessage("RIL:DataInfoChanged",
this.clientId, dataInfo);
// If the data is not registered, need not to update signal information.
if (dataInfo.state === RIL.GECKO_MOBILE_CONNECTION_STATE_REGISTERED &&
this.isInfoChanged(message.data, dataInfo)) {
this.updateInfo(message.data, dataInfo);
if (!batch) {
gMessageManager.sendMobileConnectionMessage("RIL:DataInfoChanged",
this.clientId, dataInfo);
}
}
},
networkChanged: function networkChanged(srcNetwork, destNetwork) {
return !destNetwork ||
destNetwork.longName != srcNetwork.longName ||
destNetwork.shortName != srcNetwork.shortName ||
destNetwork.mnc != srcNetwork.mnc ||
destNetwork.mcc != srcNetwork.mcc;
},
/**
* Handle operator information changes.
*
@ -1365,7 +1404,7 @@ RadioInterface.prototype = {
let voice = this.rilContext.voice;
let data = this.rilContext.data;
if (this.networkChanged(message, operatorInfo)) {
if (this.isInfoChanged(message, operatorInfo)) {
this.updateInfo(message, operatorInfo);
// Update lastKnownNetwork
@ -1738,6 +1777,15 @@ RadioInterface.prototype = {
this.clientId, message);
},
/**
* Handle emergency callback mode change.
*/
handleEmergencyCbModeChange: function handleEmergencyCbModeChange(message) {
if (DEBUG) this.debug("handleEmergencyCbModeChange: " + JSON.stringify(message));
gMessageManager.sendMobileConnectionMessage("RIL:EmergencyCbModeChanged",
this.clientId, message);
},
/**
* Handle call error.
*/
@ -2097,6 +2145,11 @@ RadioInterface.prototype = {
gMessageManager.sendIccMessage("RIL:StkCommand", this.clientId, message);
},
handleExitEmergencyCbMode: function handleExitEmergencyCbMode(message) {
if (DEBUG) this.debug("handleExitEmergencyCbMode: " + JSON.stringify(message));
gMessageManager.sendRequestResults("RIL:ExitEmergencyCbMode", message);
},
// nsIObserver
observe: function observe(subject, topic, data) {
@ -2141,7 +2194,7 @@ RadioInterface.prototype = {
}
break;
case kScreenStateChangedTopic:
this.workerMessenger.send("setScreenState", { on: (state === "on") });
this.workerMessenger.send("setScreenState", { on: (data === "on") });
break;
}
},

View File

@ -419,13 +419,23 @@ function nextNetdCommand() {
function setInterfaceUp(params, callback) {
let command = "interface setcfg " + params.ifname + " " + params.ip + " " +
params.prefix + " " + "[" + params.link + "]";
params.prefix + " ";
if (SDK_VERSION >= 16) {
command += params.link;
} else {
command += "[" + params.link + "]";
}
return doCommand(command, callback);
}
function setInterfaceDown(params, callback) {
let command = "interface setcfg " + params.ifname + " " + params.ip + " " +
params.prefix + " " + "[" + params.link + "]";
params.prefix + " ";
if (SDK_VERSION >= 16) {
command += params.link;
} else {
command += "[" + params.link + "]";
}
return doCommand(command, callback);
}

View File

@ -382,11 +382,13 @@ this.NETWORK_INFO_VOICE_REGISTRATION_STATE = "voiceRegistrationState";
this.NETWORK_INFO_DATA_REGISTRATION_STATE = "dataRegistrationState";
this.NETWORK_INFO_OPERATOR = "operator";
this.NETWORK_INFO_NETWORK_SELECTION_MODE = "networkSelectionMode";
this.NETWORK_INFO_SIGNAL = "signal";
this.NETWORK_INFO_MESSAGE_TYPES = [
NETWORK_INFO_VOICE_REGISTRATION_STATE,
NETWORK_INFO_DATA_REGISTRATION_STATE,
NETWORK_INFO_OPERATOR,
NETWORK_INFO_NETWORK_SELECTION_MODE
NETWORK_INFO_NETWORK_SELECTION_MODE,
NETWORK_INFO_SIGNAL
];
this.GECKO_PREFERRED_NETWORK_TYPE_WCDMA_GSM = "wcdma/gsm";

View File

@ -62,6 +62,9 @@ const PDU_HEX_OCTET_SIZE = 4;
const DEFAULT_EMERGENCY_NUMBERS = ["112", "911"];
// Timeout value for emergency callback mode.
const EMERGENCY_CB_MODE_TIMEOUT_MS = 300000; // 5 mins = 300000 ms.
const ICC_MAX_LINEAR_FIXED_RECORDS = 0xfe;
// MMI match groups
@ -786,6 +789,11 @@ let RIL = {
*/
this._isCdma = false;
/**
* True if we are in emergency callback mode.
*/
this._isInEmergencyCbMode = false;
/**
* Set when radio is ready but radio tech is unknown. That is, we are
* waiting for REQUEST_VOICE_RADIO_TECH
@ -1450,6 +1458,58 @@ let RIL = {
Buf.sendParcel();
},
/**
* Query call waiting status via MMI.
*/
_handleQueryMMICallWaiting: function _handleQueryMMICallWaiting(options) {
function callback(options) {
options.length = Buf.readUint32();
options.enabled = (Buf.readUint32() === 1);
let services = Buf.readUint32();
if (options.enabled) {
options.statusMessage = MMI_SM_KS_SERVICE_ENABLED_FOR;
let serviceClass = [];
for (let serviceClassMask = 1;
serviceClassMask <= ICC_SERVICE_CLASS_MAX;
serviceClassMask <<= 1) {
if ((serviceClassMask & services) !== 0) {
serviceClass.push(MMI_KS_SERVICE_CLASS_MAPPING[serviceClassMask]);
}
}
options.additionalInformation = serviceClass;
} else {
options.statusMessage = MMI_SM_KS_SERVICE_DISABLED;
}
// Prevent DataCloneError when sending chrome messages.
delete options.callback;
this.sendChromeMessage(options);
}
options.callback = callback;
this.queryCallWaiting(options);
},
/**
* Set call waiting status via MMI.
*/
_handleSetMMICallWaiting: function _handleSetMMICallWaiting(options) {
function callback(options) {
if (options.enabled) {
options.statusMessage = MMI_SM_KS_SERVICE_ENABLED;
} else {
options.statusMessage = MMI_SM_KS_SERVICE_DISABLED;
}
// Prevent DataCloneError when sending chrome messages.
delete options.callback;
this.sendChromeMessage(options);
}
options.callback = callback;
this.setCallWaiting(options);
},
/**
* Query call waiting status.
*
@ -1473,7 +1533,8 @@ let RIL = {
Buf.newParcel(REQUEST_SET_CALL_WAITING, options);
Buf.writeUint32(2);
Buf.writeUint32(options.enabled ? 1 : 0);
Buf.writeUint32(ICC_SERVICE_CLASS_VOICE);
Buf.writeUint32(options.serviceClass !== undefined ?
options.serviceClass : ICC_SERVICE_CLASS_VOICE);
Buf.sendParcel();
},
@ -1606,6 +1667,7 @@ let RIL = {
this.getDataRegistrationState(); //TODO only GSM
this.getOperator();
this.getNetworkSelectionMode();
this.getSignalStrength();
},
/**
@ -1758,6 +1820,21 @@ let RIL = {
Buf.simpleRequest(REQUEST_BASEBAND_VERSION);
},
sendExitEmergencyCbModeRequest: function sendExitEmergencyCbModeRequest(options) {
Buf.simpleRequest(REQUEST_EXIT_EMERGENCY_CALLBACK_MODE, options);
},
exitEmergencyCbMode: function exitEmergencyCbMode(options) {
// The function could be called by an API from RadioInterfaceLayer or by
// ril_worker itself. From ril_worker, we won't pass the parameter
// 'options'. In this case, it is marked as internal.
if (!options) {
options = {internal: true};
}
this._cancelEmergencyCbModeTimeout();
this.sendExitEmergencyCbModeRequest(options);
},
/**
* Cache the request for making an emergency call when radio is off. The
* request shall include two types of callback functions. 'callback' is
@ -1812,6 +1889,11 @@ let RIL = {
return;
}
// Exit emergency callback mode when user dial a non-emergency call.
if (this._isInEmergencyCbMode) {
this.exitEmergencyCbMode();
}
options.request = REQUEST_DIAL;
this.sendDialRequest(options);
},
@ -2662,9 +2744,31 @@ let RIL = {
}
this.setICCFacilityLock(options);
return;
// Call waiting
case MMI_SC_CALL_WAITING:
_sendMMIError(MMI_ERROR_KS_NOT_SUPPORTED);
if (!_isRadioAvailable(MMI_KS_SC_CALL_WAITING)) {
return;
}
options.mmiServiceCode = MMI_KS_SC_CALL_WAITING;
if (mmi.procedure === MMI_PROCEDURE_INTERROGATION) {
this._handleQueryMMICallWaiting(options);
return;
}
if (mmi.procedure === MMI_PROCEDURE_ACTIVATION) {
options.enabled = true;
} else if (mmi.procedure === MMI_PROCEDURE_DEACTIVATION) {
options.enabled = false;
} else {
_sendMMIError(MMI_ERROR_KS_NOT_SUPPORTED, MMI_KS_SC_CALL_WAITING);
return;
}
options.serviceClass = this._siToServiceClass(mmi.sia);
this._handleSetMMICallWaiting(options);
return;
}
@ -3325,7 +3429,6 @@ let RIL = {
// This was moved down from CARD_APPSTATE_READY
this.requestNetworkInfo();
this.getSignalStrength();
if (newCardState == GECKO_CARDSTATE_READY) {
// For type SIM, we need to check EF_phase first.
// Other types of ICC we can send Terminal_Profile immediately.
@ -3568,7 +3671,7 @@ let RIL = {
}
info.rilMessageType = "signalstrengthchange";
this.sendChromeMessage(info);
this._sendNetworkInfoMessage(NETWORK_INFO_SIGNAL, info);
if (this.cachedDialRequest && info.voice.signalStrength) {
// Radio is ready for making the cached emergency call.
@ -3981,6 +4084,39 @@ let RIL = {
this.sendChromeMessage(message);
},
_cancelEmergencyCbModeTimeout: function _cancelEmergencyCbModeTimeout() {
if (this._exitEmergencyCbModeTimeoutID) {
clearTimeout(this._exitEmergencyCbModeTimeoutID);
this._exitEmergencyCbModeTimeoutID = null;
}
},
_handleChangedEmergencyCbMode: function _handleChangedEmergencyCbMode(active) {
if (this._isInEmergencyCbMode === active) {
return;
}
if (active) {
// Start a new timeout event when enter the mode.
let ril = this;
this._cancelEmergencyCbModeTimeout();
this._exitEmergencyCbModeTimeoutID = setTimeout(function() {
ril.exitEmergencyCbMode();
}, EMERGENCY_CB_MODE_TIMEOUT_MS);
} else {
// Clear the timeout event when exit mode.
this._cancelEmergencyCbModeTimeout();
}
// Keep current mode and write to property.
this._isInEmergencyCbMode = active;
let message = {rilMessageType: "emergencyCbModeChange",
active: active,
timeoutMs: EMERGENCY_CB_MODE_TIMEOUT_MS};
this.sendChromeMessage(message);
},
_processNetworks: function _processNetworks() {
let strings = Buf.readStringList();
let networks = [];
@ -5148,6 +5284,8 @@ RIL[REQUEST_LAST_CALL_FAIL_CAUSE] = function REQUEST_LAST_CALL_FAIL_CAUSE(length
}
};
RIL[REQUEST_SIGNAL_STRENGTH] = function REQUEST_SIGNAL_STRENGTH(length, options) {
this._receivedNetworkInfo(NETWORK_INFO_SIGNAL);
if (options.rilRequestError) {
return;
}
@ -5483,6 +5621,12 @@ RIL[REQUEST_QUERY_CALL_WAITING] =
this.sendChromeMessage(options);
return;
}
if (options.callback) {
options.callback.call(this, options);
return;
}
options.length = Buf.readUint32();
options.enabled = ((Buf.readUint32() == 1) &&
((Buf.readUint32() & ICC_SERVICE_CLASS_VOICE) == 0x01));
@ -5493,7 +5637,15 @@ RIL[REQUEST_SET_CALL_WAITING] = function REQUEST_SET_CALL_WAITING(length, option
options.success = (options.rilRequestError === 0);
if (!options.success) {
options.errorMsg = RIL_ERROR_TO_GECKO_ERROR[options.rilRequestError];
this.sendChromeMessage(options);
return;
}
if (options.callback) {
options.callback.call(this, options);
return;
}
this.sendChromeMessage(options);
};
RIL[REQUEST_SMS_ACKNOWLEDGE] = null;
@ -5917,7 +6069,17 @@ RIL[REQUEST_DEVICE_IDENTITY] = function REQUEST_DEVICE_IDENTITY(length, options)
this.ESN = result[2];
this.MEID = result[3];
};
RIL[REQUEST_EXIT_EMERGENCY_CALLBACK_MODE] = null;
RIL[REQUEST_EXIT_EMERGENCY_CALLBACK_MODE] = function REQUEST_EXIT_EMERGENCY_CALLBACK_MODE(length, options) {
if (options.internal) {
return;
}
options.success = (options.rilRequestError === 0);
if (!options.success) {
options.errorMsg = RIL_ERROR_TO_GECKO_ERROR[options.rilRequestError];
}
this.sendChromeMessage(options);
};
RIL[REQUEST_GET_SMSC_ADDRESS] = function REQUEST_GET_SMSC_ADDRESS(length, options) {
if (options.rilRequestError) {
return;
@ -6227,7 +6389,9 @@ RIL[UNSOLICITED_RESPONSE_NEW_BROADCAST_SMS] = function UNSOLICITED_RESPONSE_NEW_
};
RIL[UNSOLICITED_CDMA_RUIM_SMS_STORAGE_FULL] = null;
RIL[UNSOLICITED_RESTRICTED_STATE_CHANGED] = null;
RIL[UNSOLICITED_ENTER_EMERGENCY_CALLBACK_MODE] = null;
RIL[UNSOLICITED_ENTER_EMERGENCY_CALLBACK_MODE] = function UNSOLICITED_ENTER_EMERGENCY_CALLBACK_MODE() {
this._handleChangedEmergencyCbMode(true);
};
RIL[UNSOLICITED_CDMA_CALL_WAITING] = function UNSOLICITED_CDMA_CALL_WAITING(length) {
let call = {};
call.number = Buf.readString();
@ -6246,6 +6410,9 @@ RIL[UNSOLICITED_CDMA_INFO_REC] = null;
RIL[UNSOLICITED_OEM_HOOK_RAW] = null;
RIL[UNSOLICITED_RINGBACK_TONE] = null;
RIL[UNSOLICITED_RESEND_INCALL_MUTE] = null;
RIL[UNSOLICITED_EXIT_EMERGENCY_CALLBACK_MODE] = function UNSOLICITED_EXIT_EMERGENCY_CALLBACK_MODE() {
this._handleChangedEmergencyCbMode(false);
};
RIL[UNSOLICITED_RIL_CONNECTED] = function UNSOLICITED_RIL_CONNECTED(length) {
// Prevent response id collision between UNSOLICITED_RIL_CONNECTED and
// UNSOLICITED_VOICE_RADIO_TECH_CHANGED for Akami on gingerbread branch.
@ -6261,6 +6428,8 @@ RIL[UNSOLICITED_RIL_CONNECTED] = function UNSOLICITED_RIL_CONNECTED(length) {
}
this.initRILState();
// Always ensure that we are not in emergency callback mode when init.
this.exitEmergencyCbMode();
};
/**

View File

@ -27,7 +27,7 @@ function add_test_incoming_parcel(parcel, handler) {
if (!parcel) {
parcel = newIncomingParcel(-1,
worker.RESPONSE_TYPE_UNSOLICITED,
worker.REQUEST_REGISTRATION_STATE,
worker.REQUEST_VOICE_REGISTRATION_STATE,
[0, 0, 0, 0]);
}
@ -106,7 +106,7 @@ add_test(function test_incoming_parcel_buffer_overwritten() {
}
// Do nothing in handleParcel().
let request = worker.REQUEST_REGISTRATION_STATE;
let request = worker.REQUEST_VOICE_REGISTRATION_STATE;
worker.RIL[request] = null;
// Prepare two parcels, whose sizes are both smaller than the incoming buffer

View File

@ -0,0 +1,179 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
subscriptLoader.loadSubScript("resource://gre/modules/ril_consts.js", this);
function run_test() {
run_next_test();
}
function _getWorker() {
let _postedMessage;
let _worker = newWorker({
postRILMessage: function fakePostRILMessage(data) {
},
postMessage: function fakePostMessage(message) {
_postedMessage = message;
}
});
return {
get postedMessage() {
return _postedMessage;
},
get worker() {
return _worker;
}
};
}
var timeoutCallback = null;
var timeoutDelayMs = 0;
var TIMER_ID = 1234;
// No window in xpcshell-test. Create our own timer mechanism.
function setTimeout(callback, timeoutMs) {
timeoutCallback = callback;
timeoutDelayMs = timeoutMs;
do_check_eq(timeoutMs, 300000); // 5 mins.
return TIMER_ID;
}
function clearTimeout(timeoutId) {
do_check_eq(timeoutId, TIMER_ID);
timeoutCallback = null;
}
function fireTimeout() {
do_check_neq(timeoutCallback, null);
if (timeoutCallback) {
timeoutCallback();
timeoutCallback = null;
}
}
add_test(function test_enter_emergencyCbMode() {
let workerHelper = _getWorker();
let worker = workerHelper.worker;
worker.RIL[UNSOLICITED_ENTER_EMERGENCY_CALLBACK_MODE]();
let postedMessage = workerHelper.postedMessage;
// Should store the mode.
do_check_eq(worker.RIL._isInEmergencyCbMode, true);
// Should notify change.
do_check_eq(postedMessage.rilMessageType, "emergencyCbModeChange");
do_check_eq(postedMessage.active, true);
do_check_eq(postedMessage.timeoutMs, 300000);
// Should start timer.
do_check_eq(worker.RIL._exitEmergencyCbModeTimeoutID, TIMER_ID);
run_next_test();
});
add_test(function test_exit_emergencyCbMode() {
let workerHelper = _getWorker();
let worker = workerHelper.worker;
worker.RIL[UNSOLICITED_ENTER_EMERGENCY_CALLBACK_MODE]();
worker.RIL[UNSOLICITED_EXIT_EMERGENCY_CALLBACK_MODE]();
let postedMessage = workerHelper.postedMessage;
// Should store the mode.
do_check_eq(worker.RIL._isInEmergencyCbMode, false);
// Should notify change.
do_check_eq(postedMessage.rilMessageType, "emergencyCbModeChange");
do_check_eq(postedMessage.active, false);
// Should clear timer.
do_check_eq(worker.RIL._exitEmergencyCbModeTimeoutID, null);
run_next_test();
});
add_test(function test_request_exit_emergencyCbMode_when_timeout() {
let workerHelper = _getWorker();
let worker = workerHelper.worker;
worker.RIL[UNSOLICITED_ENTER_EMERGENCY_CALLBACK_MODE]();
do_check_eq(worker.RIL._isInEmergencyCbMode, true);
do_check_eq(worker.RIL._exitEmergencyCbModeTimeoutID, TIMER_ID);
let parcelTypes = [];
worker.Buf.newParcel = function(type, options) {
parcelTypes.push(type);
};
// Timeout.
fireTimeout();
// Should clear timeout event.
do_check_eq(worker.RIL._exitEmergencyCbModeTimeoutID, null);
// Check indeed sent out REQUEST_EXIT_EMERGENCY_CALLBACK_MODE.
do_check_neq(parcelTypes.indexOf(REQUEST_EXIT_EMERGENCY_CALLBACK_MODE), -1);
run_next_test();
});
add_test(function test_request_exit_emergencyCbMode_when_dial() {
let workerHelper = _getWorker();
let worker = workerHelper.worker;
worker.RIL[UNSOLICITED_ENTER_EMERGENCY_CALLBACK_MODE]();
do_check_eq(worker.RIL._isInEmergencyCbMode, true);
do_check_eq(worker.RIL._exitEmergencyCbModeTimeoutID, TIMER_ID);
let parcelTypes = [];
worker.Buf.newParcel = function(type, options) {
parcelTypes.push(type);
};
// Dial non-emergency call.
worker.RIL.dial({number: "0912345678",
isDialEmergency: false});
// Should clear timeout event.
do_check_eq(worker.RIL._exitEmergencyCbModeTimeoutID, null);
// Check indeed sent out REQUEST_EXIT_EMERGENCY_CALLBACK_MODE.
do_check_neq(parcelTypes.indexOf(REQUEST_EXIT_EMERGENCY_CALLBACK_MODE), -1);
run_next_test();
});
add_test(function test_request_exit_emergencyCbMode_explicitly() {
let workerHelper = _getWorker();
let worker = workerHelper.worker;
worker.RIL[UNSOLICITED_ENTER_EMERGENCY_CALLBACK_MODE]();
do_check_eq(worker.RIL._isInEmergencyCbMode, true);
do_check_eq(worker.RIL._exitEmergencyCbModeTimeoutID, TIMER_ID);
let parcelTypes = [];
worker.Buf.newParcel = function(type, options) {
parcelTypes.push(type);
};
worker.RIL.handleChromeMessage({rilMessageType: "exitEmergencyCbMode"});
worker.RIL[REQUEST_EXIT_EMERGENCY_CALLBACK_MODE](1, {
rilMessageType: "exitEmergencyCbMode",
rilRequestError: ERROR_SUCCESS
});
let postedMessage = workerHelper.postedMessage;
// Should clear timeout event.
do_check_eq(worker.RIL._exitEmergencyCbModeTimeoutID, null);
// Check indeed sent out REQUEST_EXIT_EMERGENCY_CALLBACK_MODE.
do_check_neq(parcelTypes.indexOf(REQUEST_EXIT_EMERGENCY_CALLBACK_MODE), -1);
// Send back the response.
do_check_eq(postedMessage.rilMessageType, "exitEmergencyCbMode");
run_next_test();
});

View File

@ -46,7 +46,7 @@ function testSendMMI(mmi, error) {
do_print("worker.postMessage " + worker.postMessage);
worker.RIL.radioState = GECKO_RADIOSTATE_READY;
worker.RIL.sendMMI({mmi: mmi});
worker.RIL.sendMMI({rilMessageType: "sendMMI", mmi: mmi});
let postedMessage = workerhelper.postedMessage;
@ -380,7 +380,7 @@ add_test(function test_sendMMI_short_code() {
rilRequestError: ERROR_SUCCESS
});
}
};
worker.RIL.radioState = GECKO_RADIOSTATE_READY;
worker.RIL.sendMMI({mmi: "**"});
@ -549,7 +549,7 @@ add_test(function test_sendMMI_change_PIN() {
worker.RIL[REQUEST_ENTER_SIM_PIN](0, {
rilRequestError: ERROR_SUCCESS
});
}
};
worker.RIL.radioState = GECKO_RADIOSTATE_READY;
worker.RIL.sendMMI({mmi: "**04*1234*4567*4567#"});
@ -594,7 +594,7 @@ add_test(function test_sendMMI_change_PIN2() {
worker.RIL[REQUEST_ENTER_SIM_PIN2](0, {
rilRequestError: ERROR_SUCCESS
});
}
};
worker.RIL.radioState = GECKO_RADIOSTATE_READY;
worker.RIL.sendMMI({mmi: "**042*1234*4567*4567#"});
@ -639,7 +639,7 @@ add_test(function test_sendMMI_unblock_PIN() {
worker.RIL[REQUEST_ENTER_SIM_PUK](0, {
rilRequestError: ERROR_SUCCESS
});
}
};
worker.RIL.radioState = GECKO_RADIOSTATE_READY;
worker.RIL.sendMMI({mmi: "**05*1234*4567*4567#"});
@ -684,7 +684,7 @@ add_test(function test_sendMMI_unblock_PIN2() {
worker.RIL[REQUEST_ENTER_SIM_PUK2](0, {
rilRequestError: ERROR_SUCCESS
});
}
};
worker.RIL.radioState = GECKO_RADIOSTATE_READY;
worker.RIL.sendMMI({mmi: "**052*1234*4567*4567#"});
@ -731,13 +731,13 @@ add_test(function test_sendMMI_get_IMEI() {
worker.RIL[REQUEST_SEND_USSD](0, {
rilRequestError: ERROR_SUCCESS,
});
}
};
worker.RIL.sendMMI({mmi: "*#06#"});
let postedMessage = workerhelper.postedMessage;
do_check_true(mmiOptions.mmi);
do_check_neq(mmiOptions.mmi, null);
do_check_eq (postedMessage.errorMsg, GECKO_ERROR_SUCCESS);
do_check_true(postedMessage.success);
@ -754,13 +754,13 @@ add_test(function test_sendMMI_get_IMEI_error() {
worker.RIL[REQUEST_SEND_USSD](0, {
rilRequestError: ERROR_RADIO_NOT_AVAILABLE,
});
}
};
worker.RIL.sendMMI({mmi: "*#06#"});
let postedMessage = workerhelper.postedMessage;
do_check_true(mmiOptions.mmi);
do_check_neq(mmiOptions.mmi, null);
do_check_eq (postedMessage.errorMsg, GECKO_ERROR_RADIO_NOT_AVAILABLE);
do_check_false(postedMessage.success);
@ -781,7 +781,7 @@ add_test(function test_sendMMI_call_barring_BAIC_interrogation_voice() {
rilMessageType: "sendMMI",
rilRequestError: ERROR_SUCCESS
});
}
};
worker.RIL.radioState = GECKO_RADIOSTATE_READY;
worker.RIL.sendMMI({mmi: "*#33#"});
@ -810,7 +810,7 @@ add_test(function test_sendMMI_call_barring_BAIC_activation() {
procedure: MMI_PROCEDURE_ACTIVATION,
rilRequestError: ERROR_SUCCESS
});
}
};
worker.RIL.radioState = GECKO_RADIOSTATE_READY;
worker.RIL.sendMMI({mmi: "*33#"});
@ -837,7 +837,7 @@ add_test(function test_sendMMI_call_barring_BAIC_deactivation() {
procedure: MMI_PROCEDURE_DEACTIVATION,
rilRequestError: ERROR_SUCCESS
});
}
};
worker.RIL.radioState = GECKO_RADIOSTATE_READY;
worker.RIL.sendMMI({mmi: "#33#"});
@ -857,12 +857,6 @@ add_test(function test_sendMMI_call_barring_BAIC_procedure_not_supported() {
run_next_test();
});
add_test(function test_sendMMI_call_waiting() {
testSendMMI("*43#", MMI_ERROR_KS_NOT_SUPPORTED);
run_next_test();
});
add_test(function test_sendMMI_USSD() {
let workerhelper = getWorker();
let worker = workerhelper.worker;
@ -873,7 +867,7 @@ add_test(function test_sendMMI_USSD() {
worker.RIL[REQUEST_SEND_USSD](0, {
rilRequestError: ERROR_SUCCESS
});
}
};
worker.RIL.radioState = GECKO_RADIOSTATE_READY;
worker.RIL.sendMMI({mmi: "*123#"});
@ -898,7 +892,7 @@ add_test(function test_sendMMI_USSD_error() {
worker.RIL[REQUEST_SEND_USSD](0, {
rilRequestError: ERROR_GENERIC_FAILURE
});
}
};
worker.RIL.radioState = GECKO_RADIOSTATE_READY;
worker.RIL.sendMMI({mmi: "*123#"});
@ -912,3 +906,77 @@ add_test(function test_sendMMI_USSD_error() {
run_next_test();
});
function setCallWaitingSuccess(mmi) {
let workerhelper = getWorker();
let worker = workerhelper.worker;
worker.RIL.setCallWaiting = function fakeSetCallWaiting(options) {
worker.RIL[REQUEST_SET_CALL_WAITING](0, {
rilRequestError: ERROR_SUCCESS
});
};
worker.RIL.radioState = GECKO_RADIOSTATE_READY;
worker.RIL.sendMMI({mmi: mmi});
let postedMessage = workerhelper.postedMessage;
do_check_eq(postedMessage.errorMsg, GECKO_ERROR_SUCCESS);
do_check_true(postedMessage.success);
}
add_test(function test_sendMMI_call_waiting_activation() {
setCallWaitingSuccess("*43*10#");
run_next_test();
});
add_test(function test_sendMMI_call_waiting_deactivation() {
setCallWaitingSuccess("#43#");
run_next_test();
});
add_test(function test_sendMMI_call_waiting_registration() {
testSendMMI("**43#", MMI_ERROR_KS_NOT_SUPPORTED);
run_next_test();
});
add_test(function test_sendMMI_call_waiting_erasure() {
testSendMMI("##43#", MMI_ERROR_KS_NOT_SUPPORTED);
run_next_test();
});
add_test(function test_sendMMI_call_waiting_interrogation() {
let workerhelper = getWorker();
let worker = workerhelper.worker;
worker.Buf.readUint32 = function fakeReadUint32() {
return worker.Buf.int32Array.pop();
};
worker.RIL.queryCallWaiting = function fakeQueryCallWaiting(options) {
worker.Buf.int32Array = [
7, // serviceClass
1, // enabled
2 // length
];
worker.RIL[REQUEST_QUERY_CALL_WAITING](1, {
rilRequestError: ERROR_SUCCESS
});
};
worker.RIL.radioState = GECKO_RADIOSTATE_READY;
worker.RIL.sendMMI({mmi: "*#43#"});
let postedMessage = workerhelper.postedMessage;
do_check_eq(postedMessage.errorMsg, GECKO_ERROR_SUCCESS);
do_check_true(postedMessage.success);
do_check_eq(postedMessage.length, 2);
do_check_true(postedMessage.enabled);
run_next_test();
});

View File

@ -14,3 +14,4 @@ tail =
[test_ril_worker_clir.js]
[test_ril_worker_clip.js]
[test_ril_worker_ssn.js]
[test_ril_worker_ecm.js]

View File

@ -0,0 +1,20 @@
/* -*- 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 MozEmergencyCbModeEventInit eventInitDict),
HeaderFile="GeneratedEventClasses.h"]
interface MozEmergencyCbModeEvent : Event
{
readonly attribute boolean active;
readonly attribute unsigned long timeoutMs;
};
dictionary MozEmergencyCbModeEventInit : EventInit
{
boolean active = false;
unsigned long timeoutMs = 0;
};

View File

@ -492,6 +492,7 @@ webidl_files += \
DataErrorEvent.webidl \
IccCardLockErrorEvent.webidl \
MozCellBroadcastEvent.webidl \
MozEmergencyCbModeEvent.webidl \
MozVoicemailEvent.webidl \
MozWifiConnectionInfoEvent.webidl \
MozWifiStatusChangeEvent.webidl \

View File

@ -87,9 +87,6 @@ APZCTreeManager::UpdatePanZoomControllerTree(CompositorParent* aCompositor,
bool aIsFirstPaint, uint64_t aFirstPaintLayersId,
nsTArray< nsRefPtr<AsyncPanZoomController> >* aApzcsToDestroy)
{
// Accumulate the CSS transform between layers that have an APZC
aTransform = aTransform * aLayer->GetTransform();
ContainerLayer* container = aLayer->AsContainerLayer();
AsyncPanZoomController* controller = nullptr;
if (container) {
@ -121,9 +118,7 @@ APZCTreeManager::UpdatePanZoomControllerTree(CompositorParent* aCompositor,
aIsFirstPaint && (aLayersId == aFirstPaintLayersId));
LayerRect visible = container->GetFrameMetrics().mViewport * container->GetFrameMetrics().LayersPixelsPerCSSPixel();
controller->SetLayerHitTestData(visible, aTransform);
// Reset the accumulated transform once we hit a layer with an APZC
aTransform = gfx3DMatrix();
controller->SetLayerHitTestData(visible, aTransform, aLayer->GetTransform());
APZC_LOG("Setting rect(%f %f %f %f) as visible region for APZC %p\n", visible.x, visible.y,
visible.width, visible.height,
controller);
@ -145,6 +140,15 @@ APZCTreeManager::UpdatePanZoomControllerTree(CompositorParent* aCompositor,
container->SetAsyncPanZoomController(controller);
}
// Accumulate the CSS transform between layers that have an APZC, but exclude any
// any layers that do have an APZC, and reset the accumulation at those layers.
if (controller) {
aTransform = gfx3DMatrix();
} else {
// Multiply child layer transforms on the left so they get applied first
aTransform = aLayer->GetTransform() * aTransform;
}
uint64_t childLayersId = (aLayer->AsRefLayer() ? aLayer->AsRefLayer()->GetReferentId() : aLayersId);
AsyncPanZoomController* next = nullptr;
for (Layer* child = aLayer->GetLastChild(); child; child = child->GetPrevSibling()) {
@ -165,31 +169,69 @@ APZCTreeManager::UpdatePanZoomControllerTree(CompositorParent* aCompositor,
return aNextSibling;
}
/*static*/ template<class T> void
ApplyTransform(gfx::PointTyped<T>* aPoint, const gfx3DMatrix& aMatrix)
{
gfxPoint result = aMatrix.Transform(gfxPoint(aPoint->x, aPoint->y));
aPoint->x = result.x;
aPoint->y = result.y;
}
/*static*/ template<class T> void
ApplyTransform(gfx::IntPointTyped<T>* aPoint, const gfx3DMatrix& aMatrix)
{
gfxPoint result = aMatrix.Transform(gfxPoint(aPoint->x, aPoint->y));
aPoint->x = NS_lround(result.x);
aPoint->y = NS_lround(result.y);
}
/*static*/ void
ApplyTransform(nsIntPoint* aPoint, const gfx3DMatrix& aMatrix)
{
gfxPoint result = aMatrix.Transform(gfxPoint(aPoint->x, aPoint->y));
aPoint->x = NS_lround(result.x);
aPoint->y = NS_lround(result.y);
}
nsEventStatus
APZCTreeManager::ReceiveInputEvent(const InputData& aEvent)
{
nsRefPtr<AsyncPanZoomController> apzc;
gfx3DMatrix transformToApzc;
gfx3DMatrix transformToScreen;
switch (aEvent.mInputType) {
case MULTITOUCH_INPUT: {
const MultiTouchInput& multiTouchInput = aEvent.AsMultiTouchInput();
apzc = GetTargetAPZC(ScreenPoint(multiTouchInput.mTouches[0].mScreenPoint));
apzc = GetTargetAPZC(ScreenPoint(multiTouchInput.mTouches[0].mScreenPoint),
transformToApzc, transformToScreen);
if (apzc) {
MultiTouchInput inputForApzc(multiTouchInput);
for (int i = inputForApzc.mTouches.Length() - 1; i >= 0; i--) {
ApplyTransform(&(inputForApzc.mTouches[i].mScreenPoint), transformToApzc);
}
apzc->ReceiveInputEvent(inputForApzc);
}
break;
} case PINCHGESTURE_INPUT: {
const PinchGestureInput& pinchInput = aEvent.AsPinchGestureInput();
apzc = GetTargetAPZC(pinchInput.mFocusPoint);
apzc = GetTargetAPZC(pinchInput.mFocusPoint, transformToApzc, transformToScreen);
if (apzc) {
PinchGestureInput inputForApzc(pinchInput);
ApplyTransform(&(inputForApzc.mFocusPoint), transformToApzc);
apzc->ReceiveInputEvent(inputForApzc);
}
break;
} case TAPGESTURE_INPUT: {
const TapGestureInput& tapInput = aEvent.AsTapGestureInput();
apzc = GetTargetAPZC(ScreenPoint(tapInput.mPoint));
break;
} default: {
// leave apzc as nullptr
apzc = GetTargetAPZC(ScreenPoint(tapInput.mPoint), transformToApzc, transformToScreen);
if (apzc) {
TapGestureInput inputForApzc(tapInput);
ApplyTransform(&(inputForApzc.mPoint), transformToApzc);
apzc->ReceiveInputEvent(inputForApzc);
}
break;
}
}
if (apzc) {
return apzc->ReceiveInputEvent(aEvent);
}
return nsEventStatus_eIgnore;
}
@ -200,27 +242,51 @@ APZCTreeManager::ReceiveInputEvent(const nsInputEvent& aEvent,
MOZ_ASSERT(NS_IsMainThread());
nsRefPtr<AsyncPanZoomController> apzc;
gfx3DMatrix transformToApzc;
gfx3DMatrix transformToScreen;
switch (aEvent.eventStructType) {
case NS_TOUCH_EVENT: {
const nsTouchEvent& touchEvent = static_cast<const nsTouchEvent&>(aEvent);
if (touchEvent.touches.Length() > 0) {
nsIntPoint point = touchEvent.touches[0]->mRefPoint;
apzc = GetTargetAPZC(ScreenPoint::FromUnknownPoint(gfx::Point(point.x, point.y)));
apzc = GetTargetAPZC(ScreenPoint::FromUnknownPoint(gfx::Point(point.x, point.y)),
transformToApzc, transformToScreen);
if (apzc) {
MultiTouchInput inputForApzc(touchEvent);
for (int i = inputForApzc.mTouches.Length() - 1; i >= 0; i--) {
ApplyTransform(&(inputForApzc.mTouches[i].mScreenPoint), transformToApzc);
}
gfx3DMatrix outTransform = transformToApzc * transformToScreen;
nsTouchEvent* outEvent = static_cast<nsTouchEvent*>(aOutEvent);
for (int i = outEvent->touches.Length() - 1; i >= 0; i--) {
ApplyTransform(&(outEvent->touches[i]->mRefPoint), outTransform);
}
return apzc->ReceiveInputEvent(inputForApzc);
}
}
break;
} case NS_MOUSE_EVENT: {
const nsMouseEvent& mouseEvent = static_cast<const nsMouseEvent&>(aEvent);
apzc = GetTargetAPZC(ScreenPoint::FromUnknownPoint(gfx::Point(mouseEvent.refPoint.x,
mouseEvent.refPoint.y)));
mouseEvent.refPoint.y)),
transformToApzc, transformToScreen);
if (apzc) {
MultiTouchInput inputForApzc(mouseEvent);
ApplyTransform(&(inputForApzc.mTouches[0].mScreenPoint), transformToApzc);
gfx3DMatrix outTransform = transformToApzc * transformToScreen;
ApplyTransform(&(static_cast<nsMouseEvent*>(aOutEvent)->refPoint), outTransform);
return apzc->ReceiveInputEvent(inputForApzc);
}
break;
} default: {
// leave apzc as nullptr
// Ignore other event types
break;
}
}
if (apzc) {
return apzc->ReceiveInputEvent(aEvent, aOutEvent);
}
return nsEventStatus_eIgnore;
}
@ -334,15 +400,84 @@ APZCTreeManager::GetTargetAPZC(const ScrollableLayerGuid& aGuid)
return target.forget();
}
/* This function returns the AsyncPanZoomController instance that hit testing determines
is under the given ScreenPoint.
In addition, the aTransformToApzcOut and aTransformToScreenOut out-parameters are filled
with some useful transformations that input events may need applied. This is best
illustrated with an example. Consider a chain of layers, L, M, N, O, P, Q, R. Layer L
is the layer that corresponds to the returned APZC instance, and layer R is the root
of the layer tree. Layer M is the parent of L, N is the parent of M, and so on.
When layer L is displayed to the screen by the compositor, the set of transforms that
are applied to L are (in order from top to bottom):
L's CSS transform (hereafter referred to as transform matrix LC)
L's async transform (hereafter referred to as transform matrix LA)
M's CSS transform (hereafter referred to as transform matrix MC)
M's async transform (hereafter referred to as transform matrix MA)
...
R's CSS transform (hereafter referred to as transform matrix RC)
R's async transform (hereafter referred to as transform matrix RA)
Therefore, if we want user input to modify L's async transform, we have to first convert
user input from screen space to the coordinate space of L's async transform. Doing this
involves applying the following transforms (in order from top to bottom):
RA.Inverse()
RC.Inverse()
...
MA.Inverse()
MC.Inverse()
This combined transformation is returned in the aTransformToApzcOut out-parameter.
Next, if we want user inputs sent to gecko for event-dispatching, we will need to strip
out all of the async transforms that are involved in this chain. This is because async
transforms are stored only in the compositor and gecko does not account for them when
doing display-list-based hit-testing for event dispatching. Therefore, given a user input
in screen space, the following transforms need to be applied (in order from top to bottom):
RA.Inverse()
RC.Inverse()
...
MA.Inverse()
MC.Inverse()
LA.Inverse()
LC.Inverse()
LC
MC
...
RC
This sequence can be simplified and refactored to the following:
aTransformToApzcOut
LA.Inverse()
MC
...
RC
Since aTransformToApzcOut is already one of the out-parameters, we set aTransformToScreenOut
to the remaining transforms (LA.Inverse() * MC * ... * RC), so that the caller code can
combine it with aTransformToApzcOut to get the final transform required in this case.
Note that for many of these layers, there will be no AsyncPanZoomController attached, and
so the async transform will be the identity transform. So, in the example above, if layers
L and P have APZC instances attached, MA, NA, OA, QA, and RA will be identity transforms.
Additionally, for space-saving purposes, each APZC instance stores its layers individual
CSS transform and the accumulation of CSS transforms to its parent APZC. So the APZC for
layer L would store LC and (MC * NC * OC), and the layer P would store PC and (QC * RC).
The APZCs also obviously have LA and PA, so all of the above transformation combinations
required can be generated.
Note that this function may return null, in which case the matrix out-parameters are
left unmodified.
*/
already_AddRefed<AsyncPanZoomController>
APZCTreeManager::GetTargetAPZC(const ScreenPoint& aPoint)
APZCTreeManager::GetTargetAPZC(const ScreenPoint& aPoint,
gfx3DMatrix& aTransformToApzcOut,
gfx3DMatrix& aTransformToScreenOut)
{
MonitorAutoLock lock(mTreeLock);
nsRefPtr<AsyncPanZoomController> target;
// The root may have siblings, so check those too
gfxPoint point(aPoint.x, aPoint.y);
for (AsyncPanZoomController* apzc = mRootApzc; apzc; apzc = apzc->GetPrevSibling()) {
target = GetAPZCAtPoint(apzc, point);
target = GetAPZCAtPoint(apzc, point, aTransformToApzcOut, aTransformToScreenOut);
if (target) {
break;
}
@ -368,23 +503,52 @@ APZCTreeManager::FindTargetAPZC(AsyncPanZoomController* aApzc, const ScrollableL
}
AsyncPanZoomController*
APZCTreeManager::GetAPZCAtPoint(AsyncPanZoomController* aApzc, gfxPoint aHitTestPoint)
APZCTreeManager::GetAPZCAtPoint(AsyncPanZoomController* aApzc, const gfxPoint& aHitTestPoint,
gfx3DMatrix& aTransformToApzcOut, gfx3DMatrix& aTransformToScreenOut)
{
gfx3DMatrix transform = gfx3DMatrix(aApzc->GetCurrentAsyncTransform()) * aApzc->GetCSSTransform();
gfx3DMatrix untransform = transform.Inverse();
gfxPoint untransformed = untransform.ProjectPoint(aHitTestPoint);
// The comments below assume there is a chain of layers L..R with L and P having APZC instances as
// explained in the comment on GetTargetAPZC. This function will recurse with aApzc at L and P, and the
// comments explain what values are stored in the variables at these two levels. All the comments
// use standard matrix notation where the leftmost matrix in a multiplication is applied first.
// ancestorUntransform is OC.Inverse() * NC.Inverse() * MC.Inverse() at recursion level for L,
// and RC.Inverse() * QC.Inverse() at recursion level for P.
gfx3DMatrix ancestorUntransform = aApzc->GetAncestorTransform().Inverse();
// asyncUntransform is LA.Inverse() at recursion level for L,
// and PA.Inverse() at recursion level for P.
gfx3DMatrix asyncUntransform = gfx3DMatrix(aApzc->GetCurrentAsyncTransform()).Inverse();
// untransformSinceLastApzc is OC.Inverse() * NC.Inverse() * MC.Inverse() * LA.Inverse() * LC.Inverse() at L,
// and RC.Inverse() * QC.Inverse() * PA.Inverse() * PC.Inverse() at P.
gfx3DMatrix untransformSinceLastApzc = ancestorUntransform * asyncUntransform * aApzc->GetCSSTransform().Inverse();
// untransformed is the user input in L's layer space at L,
// and in P's layer space at P.
gfxPoint untransformed = untransformSinceLastApzc.ProjectPoint(aHitTestPoint);
APZC_LOG("Untransformed %f %f to %f %f for APZC %p\n", aHitTestPoint.x, aHitTestPoint.y, untransformed.x, untransformed.y, aApzc);
// This walks the tree in depth-first, reverse order, so that it encounters
// APZCs front-to-back on the screen.
for (AsyncPanZoomController* child = aApzc->GetLastChild(); child; child = child->GetPrevSibling()) {
AsyncPanZoomController* match = GetAPZCAtPoint(child, untransformed);
AsyncPanZoomController* match = GetAPZCAtPoint(child, untransformed, aTransformToApzcOut, aTransformToScreenOut);
if (match) {
// This code is not run in the recursion at layer L.
// aTransformToApzcOut is RC.Inverse() * QC.Inverse() * PA.Inverse() * PC.Inverse() * OC.Inverse() * NC.Inverse() * MC.Inverse()
// at recursion level for P
aTransformToApzcOut = untransformSinceLastApzc * aTransformToApzcOut;
// aTransformToScreenOut is LA.Inverse() * MC * NC * OC * PC * QC * RC at recursion level for P
aTransformToScreenOut = aTransformToScreenOut * aApzc->GetCSSTransform() * aApzc->GetAncestorTransform();
// The above values for aTransformToApzcOut and aTransformToScreenOut at recursion level for P match
// the required output as explained in the comment above GetTargetAPZC. Note that any missing terms
// are async transforms that are guaranteed to be identity transforms.
return match;
}
}
if (aApzc->VisibleRegionContains(LayerPoint(untransformed.x, untransformed.y))) {
APZC_LOG("Successfully matched untransformed point %f %f to visible region for APZC %p\n", untransformed.x, untransformed.y, aApzc);
// This code is not run in the recursion at layer P.
// aTransformToApzcOut is OC.Inverse() * NC.Inverse() * MC.Inverse() at recursion level for L.
aTransformToApzcOut = ancestorUntransform;
// aTransformToScreenOut is LA.Inverse() * MC * NC * OC at recursion level for L.
aTransformToScreenOut = asyncUntransform * aApzc->GetAncestorTransform();
return aApzc;
}
return nullptr;

View File

@ -236,11 +236,13 @@ public:
used by other production code.
*/
already_AddRefed<AsyncPanZoomController> GetTargetAPZC(const ScrollableLayerGuid& aGuid);
already_AddRefed<AsyncPanZoomController> GetTargetAPZC(const ScreenPoint& aPoint);
already_AddRefed<AsyncPanZoomController> GetTargetAPZC(const ScreenPoint& aPoint, gfx3DMatrix& aTransformToApzcOut,
gfx3DMatrix& aTransformToScreenOut);
private:
/* Recursive helpers */
AsyncPanZoomController* FindTargetAPZC(AsyncPanZoomController* aApzc, const ScrollableLayerGuid& aGuid);
AsyncPanZoomController* GetAPZCAtPoint(AsyncPanZoomController* aApzc, gfxPoint aHitTestPoint);
AsyncPanZoomController* GetAPZCAtPoint(AsyncPanZoomController* aApzc, const gfxPoint& aHitTestPoint,
gfx3DMatrix& aTransformToApzcOut, gfx3DMatrix& aTransformToScreenOut);
/**
* Recursive helper function to build the APZC tree. The tree of APZC instances has

View File

@ -247,64 +247,6 @@ WidgetSpaceToCompensatedViewportSpace(const ScreenPoint& aPoint,
return aPoint / aCurrentZoom;
}
nsEventStatus
AsyncPanZoomController::ReceiveInputEvent(const nsInputEvent& aEvent,
nsInputEvent* aOutEvent)
{
CSSToScreenScale currentResolution;
{
ReentrantMonitorAutoEnter lock(mMonitor);
currentResolution = mFrameMetrics.CalculateResolution();
}
nsEventStatus status;
switch (aEvent.eventStructType) {
case NS_TOUCH_EVENT: {
MultiTouchInput event(static_cast<const nsTouchEvent&>(aEvent));
status = ReceiveInputEvent(event);
break;
}
case NS_MOUSE_EVENT: {
MultiTouchInput event(static_cast<const nsMouseEvent&>(aEvent));
status = ReceiveInputEvent(event);
break;
}
default:
status = nsEventStatus_eIgnore;
break;
}
switch (aEvent.eventStructType) {
case NS_TOUCH_EVENT: {
nsTouchEvent* touchEvent = static_cast<nsTouchEvent*>(aOutEvent);
const nsTArray< nsRefPtr<dom::Touch> >& touches = touchEvent->touches;
for (uint32_t i = 0; i < touches.Length(); ++i) {
dom::Touch* touch = touches[i];
if (touch) {
CSSPoint refCSSPoint = WidgetSpaceToCompensatedViewportSpace(
ScreenPoint::FromUnknownPoint(gfx::Point(
touch->mRefPoint.x, touch->mRefPoint.y)),
currentResolution);
LayoutDevicePoint refPoint = refCSSPoint * mFrameMetrics.mDevPixelsPerCSSPixel;
touch->mRefPoint = nsIntPoint(refPoint.x, refPoint.y);
}
}
break;
}
default: {
CSSPoint refCSSPoint = WidgetSpaceToCompensatedViewportSpace(
ScreenPoint::FromUnknownPoint(gfx::Point(
aOutEvent->refPoint.x, aOutEvent->refPoint.y)),
currentResolution);
LayoutDevicePoint refPoint = refCSSPoint * mFrameMetrics.mDevPixelsPerCSSPixel;
aOutEvent->refPoint = LayoutDeviceIntPoint(refPoint.x, refPoint.y);
break;
}
}
return status;
}
nsEventStatus AsyncPanZoomController::ReceiveInputEvent(const InputData& aEvent) {
// If we may have touch listeners, we enable the machinery that allows touch
// listeners to preventDefault any touch inputs. This should not happen unless

View File

@ -100,19 +100,6 @@ public:
*/
nsEventStatus ReceiveInputEvent(const InputData& aEvent);
/**
* Special handler for nsInputEvents. Also sets |aOutEvent| (which is assumed
* to be an already-existing instance of an nsInputEvent which may be an
* nsTouchEvent) to have its touch points in DOM space. This is so that the
* touches can be passed through the DOM and content can handle them.
*
* NOTE: Be careful of invoking the nsInputEvent variant. This can only be
* called on the main thread. See widget/InputData.h for more information on
* why we have InputData and nsInputEvent separated.
*/
nsEventStatus ReceiveInputEvent(const nsInputEvent& aEvent,
nsInputEvent* aOutEvent);
/**
* Updates the composition bounds, i.e. the dimensions of the final size of
* the frame this is tied to during composition onto, in device pixels. In
@ -642,9 +629,15 @@ private:
* hit-testing to see which APZC instance should handle touch events.
*/
public:
void SetLayerHitTestData(const LayerRect& aRect, const gfx3DMatrix& aTransform) {
void SetLayerHitTestData(const LayerRect& aRect, const gfx3DMatrix& aTransformToLayer,
const gfx3DMatrix& aTransformForLayer) {
mVisibleRect = aRect;
mCSSTransform = aTransform;
mAncestorTransform = aTransformToLayer;
mCSSTransform = aTransformForLayer;
}
gfx3DMatrix GetAncestorTransform() const {
return mAncestorTransform;
}
gfx3DMatrix GetCSSTransform() const {
@ -661,8 +654,10 @@ private:
* applied to any layers, whether they are CSS transforms or async
* transforms. */
LayerRect mVisibleRect;
/* This is the cumulative layer transform from the parent APZC down to this
* one. */
/* This is the cumulative CSS transform for all the layers between the parent
* APZC and this one (not inclusive) */
gfx3DMatrix mAncestorTransform;
/* This is the CSS transform for this APZC's layer. */
gfx3DMatrix mCSSTransform;
};

View File

@ -376,16 +376,17 @@ TEST(APZCTreeManager, GetAPZCAtPoint) {
ScopedLayerTreeRegistration controller(0, root, mcc);
nsRefPtr<APZCTreeManager> manager = new TestAPZCTreeManager();
gfx3DMatrix matrix;
// No APZC attached so hit testing will return no APZC at (20,20)
nsRefPtr<AsyncPanZoomController> hit = manager->GetTargetAPZC(ScreenPoint(20, 20));
nsRefPtr<AsyncPanZoomController> hit = manager->GetTargetAPZC(ScreenPoint(20, 20), matrix, matrix);
AsyncPanZoomController* nullAPZC = nullptr;
EXPECT_EQ(nullAPZC, hit.get());
// Now we have a root APZC that will match the page
SetScrollableFrameMetrics(root, FrameMetrics::ROOT_SCROLL_ID, mcc);
manager->UpdatePanZoomControllerTree(nullptr, root, 0, false);
hit = manager->GetTargetAPZC(ScreenPoint(15, 15));
hit = manager->GetTargetAPZC(ScreenPoint(15, 15), matrix, matrix);
EXPECT_EQ(root->AsContainerLayer()->GetAsyncPanZoomController(), hit.get());
// expect hit point at LayerIntPoint(15, 15)
@ -393,28 +394,28 @@ TEST(APZCTreeManager, GetAPZCAtPoint) {
SetScrollableFrameMetrics(layers[3], FrameMetrics::START_SCROLL_ID, mcc);
manager->UpdatePanZoomControllerTree(nullptr, root, 0, false);
EXPECT_NE(root->AsContainerLayer()->GetAsyncPanZoomController(), layers[3]->AsContainerLayer()->GetAsyncPanZoomController());
hit = manager->GetTargetAPZC(ScreenPoint(15, 15));
hit = manager->GetTargetAPZC(ScreenPoint(15, 15), matrix, matrix);
EXPECT_EQ(layers[3]->AsContainerLayer()->GetAsyncPanZoomController(), hit.get());
// expect hit point at LayerIntPoint(15, 15)
// Now test hit testing when we have two scrollable layers
hit = manager->GetTargetAPZC(ScreenPoint(15, 15));
hit = manager->GetTargetAPZC(ScreenPoint(15, 15), matrix, matrix);
EXPECT_EQ(layers[3]->AsContainerLayer()->GetAsyncPanZoomController(), hit.get());
SetScrollableFrameMetrics(layers[4], FrameMetrics::START_SCROLL_ID + 1, mcc);
manager->UpdatePanZoomControllerTree(nullptr, root, 0, false);
hit = manager->GetTargetAPZC(ScreenPoint(15, 15));
hit = manager->GetTargetAPZC(ScreenPoint(15, 15), matrix, matrix);
EXPECT_EQ(layers[4]->AsContainerLayer()->GetAsyncPanZoomController(), hit.get());
// expect hit point at LayerIntPoint(15, 15)
// Hit test ouside the reach of layer[3,4] but inside root
hit = manager->GetTargetAPZC(ScreenPoint(90, 90));
hit = manager->GetTargetAPZC(ScreenPoint(90, 90), matrix, matrix);
EXPECT_EQ(root->AsContainerLayer()->GetAsyncPanZoomController(), hit.get());
// expect hit point at LayerIntPoint(90, 90)
// Hit test ouside the reach of any layer
hit = manager->GetTargetAPZC(ScreenPoint(1000, 10));
hit = manager->GetTargetAPZC(ScreenPoint(1000, 10), matrix, matrix);
EXPECT_EQ(nullAPZC, hit.get());
hit = manager->GetTargetAPZC(ScreenPoint(-1000, 10));
hit = manager->GetTargetAPZC(ScreenPoint(-1000, 10), matrix, matrix);
EXPECT_EQ(nullAPZC, hit.get());
// Test layer transform
@ -422,12 +423,12 @@ TEST(APZCTreeManager, GetAPZCAtPoint) {
transform.ScalePost(0.1, 0.1, 1);
root->SetBaseTransform(transform);
manager->UpdatePanZoomControllerTree(nullptr, root, 0, false);
hit = manager->GetTargetAPZC(ScreenPoint(50, 50)); // This point is now outside the root layer
hit = manager->GetTargetAPZC(ScreenPoint(50, 50), matrix, matrix); // This point is now outside the root layer
EXPECT_EQ(nullAPZC, hit.get());
// This hit test will hit both layers[3] and layers[4]; layers[4] is later in the tree so
// it is a better match
hit = manager->GetTargetAPZC(ScreenPoint(2, 2));
hit = manager->GetTargetAPZC(ScreenPoint(2, 2), matrix, matrix);
EXPECT_EQ(layers[4]->AsContainerLayer()->GetAsyncPanZoomController(), hit.get());
// expect hit point at LayerPoint(20, 20)
@ -436,7 +437,7 @@ TEST(APZCTreeManager, GetAPZCAtPoint) {
// layer 4 effective visible screenrect: (0.05, 0.05, 0.2, 0.2)
// Does not contain (2, 2)
manager->UpdatePanZoomControllerTree(nullptr, root, 0, false);
hit = manager->GetTargetAPZC(ScreenPoint(2, 2));
hit = manager->GetTargetAPZC(ScreenPoint(2, 2), matrix, matrix);
EXPECT_EQ(layers[3]->AsContainerLayer()->GetAsyncPanZoomController(), hit.get());
// expect hit point at LayerPoint(20, 20)
@ -457,7 +458,7 @@ TEST(APZCTreeManager, GetAPZCAtPoint) {
manager->UpdatePanZoomControllerTree(nullptr, root, 0, false);
// layer 7 effective visible screenrect (0,16,4,60) but clipped by parent layers
hit = manager->GetTargetAPZC(ScreenPoint(1, 45));
hit = manager->GetTargetAPZC(ScreenPoint(1, 45), matrix, matrix);
EXPECT_EQ(layers[7]->AsContainerLayer()->GetAsyncPanZoomController(), hit.get());
// expect hit point at LayerPoint(20, 29)

View File

@ -222,8 +222,9 @@ if test "$CPU_ARCH" = "arm"; then
dnl This matches media/webrtc/trunk/webrtc/build/common.gypi.
if test -n "$ARM_ARCH"; then
if test "$ARM_ARCH" -lt 7; then
BUILD_ARM_NEON=0
BUILD_ARM_NEON=
else
AC_DEFINE(BUILD_ARM_NEON)
BUILD_ARM_NEON=1
fi
fi

View File

@ -35,6 +35,7 @@ simple_events = [
'CFStateChangeEvent',
'DataErrorEvent',
'IccCardLockErrorEvent',
'MozEmergencyCbModeEvent',
'MozWifiStatusChangeEvent',
'MozWifiConnectionInfoEvent',
'MozCellBroadcastEvent',

View File

@ -163,7 +163,7 @@ public final class GeckoProfile {
private static File getGuestDir(Context context) {
if (mGuestDir == null) {
mGuestDir = context.getDir("guest", Context.MODE_PRIVATE);
mGuestDir = context.getFileStreamPath("guest");
}
return mGuestDir;
}
@ -181,8 +181,12 @@ public final class GeckoProfile {
public static boolean maybeCleanupGuestProfile(final Context context) {
// Don't use profile.getDir() here, so that we don't accidently create the dir
File guestDir = getGuestDir(context);
if (!guestDir.exists()) {
return false;
}
final GeckoProfile profile = getGuestProfile(context);
if (guestDir.exists() && !profile.locked()) {
if (!profile.locked()) {
// if the guest dir exists, but its unlocked, delete it
ThreadUtils.postToBackgroundThread(new Runnable() {
@Override

View File

@ -57,7 +57,7 @@ class MarionetteClient(object):
"socket closed?",
status=ErrorCodes.INVALID_RESPONSE)
def connect(self, timeout=180.0):
def connect(self, timeout=240.0):
""" Connect to the server and process the hello message we expect
to receive in response.
"""

View File

@ -0,0 +1,74 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Check that we source map frame locations for the frame we are paused at.
*/
var gDebuggee;
var gClient;
var gThreadClient;
const promise = devtools.require("sdk/core/promise");
Components.utils.import('resource:///modules/devtools/SourceMap.jsm');
function run_test() {
initTestDebuggerServer();
gDebuggee = addTestGlobal("test-source-map");
gClient = new DebuggerClient(DebuggerServer.connectPipe());
gClient.connect(function() {
attachTestTabAndResume(gClient, "test-source-map", function(aResponse, aTabClient, aThreadClient) {
gThreadClient = aThreadClient;
promise.resolve(define_code())
.then(run_code)
.then(test_frame_location)
.then(null, error => {
dump(error + "\n");
dump(error.stack);
do_check_true(false);
})
.then(() => {
finishClient(gClient);
});
});
});
do_test_pending();
}
function define_code() {
let { code, map } = (new SourceNode(null, null, null, [
new SourceNode(1, 0, "a.js", "function a() {\n"),
new SourceNode(2, 0, "a.js", " b();\n"),
new SourceNode(3, 0, "a.js", "}\n"),
new SourceNode(1, 0, "b.js", "function b() {\n"),
new SourceNode(2, 0, "b.js", " c();\n"),
new SourceNode(3, 0, "b.js", "}\n"),
new SourceNode(1, 0, "c.js", "function c() {\n"),
new SourceNode(2, 0, "c.js", " debugger;\n"),
new SourceNode(3, 0, "c.js", "}\n"),
])).toStringWithSourceMap({
file: "abc.js",
sourceRoot: "http://example.com/www/js/"
});
code += "//# sourceMappingURL=data:text/json," + map.toString();
Components.utils.evalInSandbox(code, gDebuggee, "1.8",
"http://example.com/www/js/abc.js", 1);
}
function run_code() {
const d = promise.defer();
gClient.addOneTimeListener("paused", function (aEvent, aPacket) {
d.resolve(aPacket);
gThreadClient.resume();
});
gDebuggee.a();
return d.promise;
}
function test_frame_location({ frame: { where: { url, line, column } } }) {
do_check_eq(url, "http://example.com/www/js/c.js");
do_check_eq(line, 2);
do_check_eq(column, 0);
}

View File

@ -0,0 +1,84 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Check that we source map frame locations returned by "frames" requests.
*/
var gDebuggee;
var gClient;
var gThreadClient;
const promise = devtools.require("sdk/core/promise");
Components.utils.import('resource:///modules/devtools/SourceMap.jsm');
function run_test() {
initTestDebuggerServer();
gDebuggee = addTestGlobal("test-source-map");
gClient = new DebuggerClient(DebuggerServer.connectPipe());
gClient.connect(function() {
attachTestTabAndResume(gClient, "test-source-map", function(aResponse, aTabClient, aThreadClient) {
gThreadClient = aThreadClient;
promise.resolve(define_code())
.then(run_code)
.then(test_frames)
.then(null, error => {
dump(error + "\n");
dump(error.stack);
do_check_true(false);
})
.then(() => {
finishClient(gClient);
});
});
});
do_test_pending();
}
function define_code() {
let { code, map } = (new SourceNode(null, null, null, [
new SourceNode(1, 0, "a.js", "function a() {\n"),
new SourceNode(2, 0, "a.js", " b();\n"),
new SourceNode(3, 0, "a.js", "}\n"),
new SourceNode(1, 0, "b.js", "function b() {\n"),
new SourceNode(2, 0, "b.js", " c();\n"),
new SourceNode(3, 0, "b.js", "}\n"),
new SourceNode(1, 0, "c.js", "function c() {\n"),
new SourceNode(2, 0, "c.js", " debugger;\n"),
new SourceNode(3, 0, "c.js", "}\n"),
])).toStringWithSourceMap({
file: "abc.js",
sourceRoot: "http://example.com/www/js/"
});
code += "//# sourceMappingURL=data:text/json," + map.toString();
Components.utils.evalInSandbox(code, gDebuggee, "1.8",
"http://example.com/www/js/abc.js", 1);
}
function run_code() {
const d = promise.defer();
gClient.addOneTimeListener("paused", function () {
gThreadClient.getFrames(0, 3, function (aResponse) {
d.resolve(aResponse);
gThreadClient.resume();
})
});
gDebuggee.a();
return d.promise;
}
function test_frames({ error, frames }) {
do_check_true(!error);
do_check_eq(frames.length, 3);
check_frame(frames[0], "http://example.com/www/js/c.js");
check_frame(frames[1], "http://example.com/www/js/b.js");
check_frame(frames[2], "http://example.com/www/js/a.js");
}
function check_frame({ where: { url, line, column } }, aExpectedUrl) {
do_check_eq(url, aExpectedUrl);
do_check_eq(line, 2);
do_check_eq(column, 0);
}

View File

@ -113,6 +113,8 @@ skip-if = toolkit == "gonk"
reason = bug 820380
[test_sourcemaps-08.js]
[test_sourcemaps-09.js]
[test_sourcemaps-10.js]
[test_sourcemaps-11.js]
[test_objectgrips-01.js]
[test_objectgrips-02.js]
[test_objectgrips-03.js]