Merge b2g-inbound to m-c a=merge

This commit is contained in:
Wes Kocher 2014-12-11 16:48:00 -08:00
commit ccea819af8
62 changed files with 1325 additions and 2881 deletions

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="3ab0d9c70f0b2e1ededc679112c392303f037361">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="1a956437210d2b16988d2ddbf40c9a38d8474435"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="1d9ae9cca415ad093beba9521c429350e1f2b14d"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
@ -23,7 +23,7 @@
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="a3e4614c7fb0a6ffa8c748bf5d49b34612c9d6d4"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="58aee9864c193acfd03fa196af399d0659ba506d"/>
<!-- Stock Android things -->
<project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="95bb5b66b3ec5769c3de8d3f25d681787418e7d2"/>
<project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="ebdad82e61c16772f6cd47e9f11936bf6ebe9aa0"/>

View File

@ -19,13 +19,13 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="1a956437210d2b16988d2ddbf40c9a38d8474435"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="1d9ae9cca415ad093beba9521c429350e1f2b14d"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="d5d3f93914558b6f168447b805cd799c8233e300"/>
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="6fa7a4936414ceb4055fd27f7a30e76790f834fb"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="a3e4614c7fb0a6ffa8c748bf5d49b34612c9d6d4"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="58aee9864c193acfd03fa196af399d0659ba506d"/>
<!-- Stock Android things -->
<project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
<project name="platform/bionic" path="bionic" revision="c72b8f6359de7ed17c11ddc9dfdde3f615d188a9"/>

View File

@ -17,10 +17,10 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="1a956437210d2b16988d2ddbf40c9a38d8474435"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="1d9ae9cca415ad093beba9521c429350e1f2b14d"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="a3e4614c7fb0a6ffa8c748bf5d49b34612c9d6d4"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="58aee9864c193acfd03fa196af399d0659ba506d"/>
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
<!-- Stock Android things -->

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="3ab0d9c70f0b2e1ededc679112c392303f037361">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="1a956437210d2b16988d2ddbf40c9a38d8474435"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="1d9ae9cca415ad093beba9521c429350e1f2b14d"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
@ -23,7 +23,7 @@
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="a3e4614c7fb0a6ffa8c748bf5d49b34612c9d6d4"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="58aee9864c193acfd03fa196af399d0659ba506d"/>
<!-- Stock Android things -->
<project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="f92a936f2aa97526d4593386754bdbf02db07a12"/>
<project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="6e47ff2790f5656b5b074407829ceecf3e6188c4"/>

View File

@ -19,13 +19,13 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="1a956437210d2b16988d2ddbf40c9a38d8474435"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="1d9ae9cca415ad093beba9521c429350e1f2b14d"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="d5d3f93914558b6f168447b805cd799c8233e300"/>
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="6fa7a4936414ceb4055fd27f7a30e76790f834fb"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="a3e4614c7fb0a6ffa8c748bf5d49b34612c9d6d4"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="58aee9864c193acfd03fa196af399d0659ba506d"/>
<!-- Stock Android things -->
<project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
<project name="platform/bionic" path="bionic" revision="c72b8f6359de7ed17c11ddc9dfdde3f615d188a9"/>

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="3ab0d9c70f0b2e1ededc679112c392303f037361">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="1a956437210d2b16988d2ddbf40c9a38d8474435"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="1d9ae9cca415ad093beba9521c429350e1f2b14d"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
@ -23,7 +23,7 @@
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="a3e4614c7fb0a6ffa8c748bf5d49b34612c9d6d4"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="58aee9864c193acfd03fa196af399d0659ba506d"/>
<!-- Stock Android things -->
<project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="95bb5b66b3ec5769c3de8d3f25d681787418e7d2"/>
<project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="ebdad82e61c16772f6cd47e9f11936bf6ebe9aa0"/>

View File

@ -17,10 +17,10 @@
</project>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="1a956437210d2b16988d2ddbf40c9a38d8474435"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="1d9ae9cca415ad093beba9521c429350e1f2b14d"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="a3e4614c7fb0a6ffa8c748bf5d49b34612c9d6d4"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="58aee9864c193acfd03fa196af399d0659ba506d"/>
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
<!-- Stock Android things -->

View File

@ -4,6 +4,6 @@
"remote": "",
"branch": ""
},
"revision": "e832f7bc675f2a5f4903400f11a8429f916aa20b",
"revision": "f1f983cfb1e42dbc6723c192f73c16bf098b6b4b",
"repo_path": "integration/gaia-central"
}

View File

@ -17,11 +17,11 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="1a956437210d2b16988d2ddbf40c9a38d8474435"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="1d9ae9cca415ad093beba9521c429350e1f2b14d"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="a3e4614c7fb0a6ffa8c748bf5d49b34612c9d6d4"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="58aee9864c193acfd03fa196af399d0659ba506d"/>
<!-- Stock Android things -->
<project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
<project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/>

View File

@ -15,7 +15,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="1a956437210d2b16988d2ddbf40c9a38d8474435"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="1d9ae9cca415ad093beba9521c429350e1f2b14d"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>

View File

@ -17,10 +17,10 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="1a956437210d2b16988d2ddbf40c9a38d8474435"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="1d9ae9cca415ad093beba9521c429350e1f2b14d"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="a3e4614c7fb0a6ffa8c748bf5d49b34612c9d6d4"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="58aee9864c193acfd03fa196af399d0659ba506d"/>
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
<!-- Stock Android things -->

View File

@ -17,12 +17,12 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="1a956437210d2b16988d2ddbf40c9a38d8474435"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="1d9ae9cca415ad093beba9521c429350e1f2b14d"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="a3e4614c7fb0a6ffa8c748bf5d49b34612c9d6d4"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="58aee9864c193acfd03fa196af399d0659ba506d"/>
<project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
<!-- Stock Android things -->
<project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>

View File

@ -0,0 +1,429 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "BluetoothDaemonA2dpInterface.h"
#include "BluetoothDaemonSetupInterface.h"
#include "mozilla/unused.h"
BEGIN_BLUETOOTH_NAMESPACE
//
// A2DP module
//
BluetoothA2dpNotificationHandler*
BluetoothDaemonA2dpModule::sNotificationHandler;
void
BluetoothDaemonA2dpModule::SetNotificationHandler(
BluetoothA2dpNotificationHandler* aNotificationHandler)
{
sNotificationHandler = aNotificationHandler;
}
nsresult
BluetoothDaemonA2dpModule::Send(BluetoothDaemonPDU* aPDU,
BluetoothA2dpResultHandler* aRes)
{
aRes->AddRef(); // Keep reference for response
return Send(aPDU, static_cast<void*>(aRes));
}
void
BluetoothDaemonA2dpModule::HandleSvc(const BluetoothDaemonPDUHeader& aHeader,
BluetoothDaemonPDU& aPDU, void* aUserData)
{
static void (BluetoothDaemonA2dpModule::* const HandleOp[])(
const BluetoothDaemonPDUHeader&, BluetoothDaemonPDU&, void*) = {
INIT_ARRAY_AT(0, &BluetoothDaemonA2dpModule::HandleRsp),
INIT_ARRAY_AT(1, &BluetoothDaemonA2dpModule::HandleNtf),
};
MOZ_ASSERT(!NS_IsMainThread());
// negate twice to map bit to 0/1
unsigned int isNtf = !!(aHeader.mOpcode & 0x80);
(this->*(HandleOp[isNtf]))(aHeader, aPDU, aUserData);
}
// Commands
//
nsresult
BluetoothDaemonA2dpModule::ConnectCmd(
const nsAString& aRemoteAddr, BluetoothA2dpResultHandler* aRes)
{
MOZ_ASSERT(NS_IsMainThread());
nsAutoPtr<BluetoothDaemonPDU> pdu(new BluetoothDaemonPDU(SERVICE_ID,
OPCODE_CONNECT,
6)); // Address
nsresult rv = PackPDU(
PackConversion<nsAString, BluetoothAddress>(aRemoteAddr), *pdu);
if (NS_FAILED(rv)) {
return rv;
}
rv = Send(pdu, aRes);
if (NS_FAILED(rv)) {
return rv;
}
unused << pdu.forget();
return NS_OK;
}
nsresult
BluetoothDaemonA2dpModule::DisconnectCmd(
const nsAString& aRemoteAddr, BluetoothA2dpResultHandler* aRes)
{
MOZ_ASSERT(NS_IsMainThread());
nsAutoPtr<BluetoothDaemonPDU> pdu(new BluetoothDaemonPDU(SERVICE_ID,
OPCODE_DISCONNECT,
6)); // Address
nsresult rv = PackPDU(
PackConversion<nsAString, BluetoothAddress>(aRemoteAddr), *pdu);
if (NS_FAILED(rv)) {
return rv;
}
rv = Send(pdu, aRes);
if (NS_FAILED(rv)) {
return rv;
}
unused << pdu.forget();
return NS_OK;
}
// Responses
//
void
BluetoothDaemonA2dpModule::ErrorRsp(
const BluetoothDaemonPDUHeader& aHeader,
BluetoothDaemonPDU& aPDU, BluetoothA2dpResultHandler* aRes)
{
ErrorRunnable::Dispatch(
aRes, &BluetoothA2dpResultHandler::OnError, UnpackPDUInitOp(aPDU));
}
void
BluetoothDaemonA2dpModule::ConnectRsp(
const BluetoothDaemonPDUHeader& aHeader, BluetoothDaemonPDU& aPDU,
BluetoothA2dpResultHandler* aRes)
{
ResultRunnable::Dispatch(
aRes, &BluetoothA2dpResultHandler::Connect, UnpackPDUInitOp(aPDU));
}
void
BluetoothDaemonA2dpModule::DisconnectRsp(
const BluetoothDaemonPDUHeader& aHeader, BluetoothDaemonPDU& aPDU,
BluetoothA2dpResultHandler* aRes)
{
ResultRunnable::Dispatch(
aRes, &BluetoothA2dpResultHandler::Disconnect, UnpackPDUInitOp(aPDU));
}
void
BluetoothDaemonA2dpModule::HandleRsp(
const BluetoothDaemonPDUHeader& aHeader, BluetoothDaemonPDU& aPDU,
void* aUserData)
{
static void (BluetoothDaemonA2dpModule::* const HandleRsp[])(
const BluetoothDaemonPDUHeader&,
BluetoothDaemonPDU&,
BluetoothA2dpResultHandler*) = {
INIT_ARRAY_AT(OPCODE_ERROR,
&BluetoothDaemonA2dpModule::ErrorRsp),
INIT_ARRAY_AT(OPCODE_CONNECT,
&BluetoothDaemonA2dpModule::ConnectRsp),
INIT_ARRAY_AT(OPCODE_DISCONNECT,
&BluetoothDaemonA2dpModule::DisconnectRsp),
};
MOZ_ASSERT(!NS_IsMainThread()); // I/O thread
if (NS_WARN_IF(!(aHeader.mOpcode < MOZ_ARRAY_LENGTH(HandleRsp))) ||
NS_WARN_IF(!HandleRsp[aHeader.mOpcode])) {
return;
}
nsRefPtr<BluetoothA2dpResultHandler> res =
already_AddRefed<BluetoothA2dpResultHandler>(
static_cast<BluetoothA2dpResultHandler*>(aUserData));
if (!res) {
return; // Return early if no result handler has been set for response
}
(this->*(HandleRsp[aHeader.mOpcode]))(aHeader, aPDU, res);
}
// Notifications
//
// Returns the current notification handler to a notification runnable
class BluetoothDaemonA2dpModule::NotificationHandlerWrapper MOZ_FINAL
{
public:
typedef BluetoothA2dpNotificationHandler ObjectType;
static ObjectType* GetInstance()
{
MOZ_ASSERT(NS_IsMainThread());
return sNotificationHandler;
}
};
// Init operator class for ConnectionStateNotification
class BluetoothDaemonA2dpModule::ConnectionStateInitOp MOZ_FINAL
: private PDUInitOp
{
public:
ConnectionStateInitOp(BluetoothDaemonPDU& aPDU)
: PDUInitOp(aPDU)
{ }
nsresult
operator () (BluetoothA2dpConnectionState& aArg1, nsString& aArg2) const
{
BluetoothDaemonPDU& pdu = GetPDU();
/* Read state */
nsresult rv = UnpackPDU(pdu, aArg1);
if (NS_FAILED(rv)) {
return rv;
}
/* Read address */
rv = UnpackPDU(
pdu, UnpackConversion<BluetoothAddress, nsAString>(aArg2));
if (NS_FAILED(rv)) {
return rv;
}
WarnAboutTrailingData();
return NS_OK;
}
};
void
BluetoothDaemonA2dpModule::ConnectionStateNtf(
const BluetoothDaemonPDUHeader& aHeader, BluetoothDaemonPDU& aPDU)
{
ConnectionStateNotification::Dispatch(
&BluetoothA2dpNotificationHandler::ConnectionStateNotification,
ConnectionStateInitOp(aPDU));
}
// Init operator class for AudioStateNotification
class BluetoothDaemonA2dpModule::AudioStateInitOp MOZ_FINAL
: private PDUInitOp
{
public:
AudioStateInitOp(BluetoothDaemonPDU& aPDU)
: PDUInitOp(aPDU)
{ }
nsresult
operator () (BluetoothA2dpAudioState& aArg1,
nsString& aArg2) const
{
BluetoothDaemonPDU& pdu = GetPDU();
/* Read state */
nsresult rv = UnpackPDU(pdu, aArg1);
if (NS_FAILED(rv)) {
return rv;
}
/* Read address */
rv = UnpackPDU(
pdu, UnpackConversion<BluetoothAddress, nsAString>(aArg2));
if (NS_FAILED(rv)) {
return rv;
}
WarnAboutTrailingData();
return NS_OK;
}
};
void
BluetoothDaemonA2dpModule::AudioStateNtf(
const BluetoothDaemonPDUHeader& aHeader, BluetoothDaemonPDU& aPDU)
{
AudioStateNotification::Dispatch(
&BluetoothA2dpNotificationHandler::AudioStateNotification,
AudioStateInitOp(aPDU));
}
void
BluetoothDaemonA2dpModule::HandleNtf(
const BluetoothDaemonPDUHeader& aHeader, BluetoothDaemonPDU& aPDU,
void* aUserData)
{
static void (BluetoothDaemonA2dpModule::* const HandleNtf[])(
const BluetoothDaemonPDUHeader&, BluetoothDaemonPDU&) = {
INIT_ARRAY_AT(0, &BluetoothDaemonA2dpModule::ConnectionStateNtf),
INIT_ARRAY_AT(1, &BluetoothDaemonA2dpModule::AudioStateNtf),
};
MOZ_ASSERT(!NS_IsMainThread());
uint8_t index = aHeader.mOpcode - 0x81;
if (NS_WARN_IF(!(index < MOZ_ARRAY_LENGTH(HandleNtf))) ||
NS_WARN_IF(!HandleNtf[index])) {
return;
}
(this->*(HandleNtf[index]))(aHeader, aPDU);
}
//
// A2DP interface
//
BluetoothDaemonA2dpInterface::BluetoothDaemonA2dpInterface(
BluetoothDaemonA2dpModule* aModule)
: mModule(aModule)
{ }
BluetoothDaemonA2dpInterface::~BluetoothDaemonA2dpInterface()
{ }
class BluetoothDaemonA2dpInterface::InitResultHandler MOZ_FINAL
: public BluetoothSetupResultHandler
{
public:
InitResultHandler(BluetoothA2dpResultHandler* aRes)
: mRes(aRes)
{
MOZ_ASSERT(mRes);
}
void OnError(BluetoothStatus aStatus) MOZ_OVERRIDE
{
MOZ_ASSERT(NS_IsMainThread());
mRes->OnError(aStatus);
}
void RegisterModule() MOZ_OVERRIDE
{
MOZ_ASSERT(NS_IsMainThread());
mRes->Init();
}
private:
nsRefPtr<BluetoothA2dpResultHandler> mRes;
};
void
BluetoothDaemonA2dpInterface::Init(
BluetoothA2dpNotificationHandler* aNotificationHandler,
BluetoothA2dpResultHandler* aRes)
{
// Set notification handler _before_ registering the module. It could
// happen that we receive notifications, before the result handler runs.
mModule->SetNotificationHandler(aNotificationHandler);
InitResultHandler* res;
if (aRes) {
res = new InitResultHandler(aRes);
} else {
// We don't need a result handler if the caller is not interested.
res = nullptr;
}
nsresult rv = mModule->RegisterModule(BluetoothDaemonA2dpModule::SERVICE_ID,
0x00, res);
if (NS_FAILED(rv) && aRes) {
DispatchError(aRes, STATUS_FAIL);
}
}
class BluetoothDaemonA2dpInterface::CleanupResultHandler MOZ_FINAL
: public BluetoothSetupResultHandler
{
public:
CleanupResultHandler(BluetoothDaemonA2dpModule* aModule,
BluetoothA2dpResultHandler* aRes)
: mModule(aModule)
, mRes(aRes)
{
MOZ_ASSERT(mModule);
}
void OnError(BluetoothStatus aStatus) MOZ_OVERRIDE
{
MOZ_ASSERT(NS_IsMainThread());
if (mRes) {
mRes->OnError(aStatus);
}
}
void UnregisterModule() MOZ_OVERRIDE
{
MOZ_ASSERT(NS_IsMainThread());
// Clear notification handler _after_ module has been
// unregistered. While unregistering the module, we might
// still receive notifications.
mModule->SetNotificationHandler(nullptr);
if (mRes) {
mRes->Cleanup();
}
}
private:
BluetoothDaemonA2dpModule* mModule;
nsRefPtr<BluetoothA2dpResultHandler> mRes;
};
void
BluetoothDaemonA2dpInterface::Cleanup(
BluetoothA2dpResultHandler* aRes)
{
mModule->UnregisterModule(BluetoothDaemonA2dpModule::SERVICE_ID,
new CleanupResultHandler(mModule, aRes));
}
/* Connect / Disconnect */
void
BluetoothDaemonA2dpInterface::Connect(
const nsAString& aBdAddr, BluetoothA2dpResultHandler* aRes)
{
MOZ_ASSERT(mModule);
mModule->ConnectCmd(aBdAddr, aRes);
}
void
BluetoothDaemonA2dpInterface::Disconnect(
const nsAString& aBdAddr, BluetoothA2dpResultHandler* aRes)
{
MOZ_ASSERT(mModule);
mModule->DisconnectCmd(aBdAddr, aRes);
}
void
BluetoothDaemonA2dpInterface::DispatchError(
BluetoothA2dpResultHandler* aRes, BluetoothStatus aStatus)
{
BluetoothResultRunnable1<BluetoothA2dpResultHandler, void,
BluetoothStatus, BluetoothStatus>::Dispatch(
aRes, &BluetoothA2dpResultHandler::OnError,
ConstantInitOp1<BluetoothStatus>(aStatus));
}
END_BLUETOOTH_NAMESPACE

View File

@ -0,0 +1,152 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_bluetooth_bluetoothdaemona2dpinterface_h
#define mozilla_dom_bluetooth_bluetoothdaemona2dpinterface_h
#include "BluetoothDaemonHelpers.h"
#include "BluetoothInterface.h"
#include "BluetoothInterfaceHelpers.h"
BEGIN_BLUETOOTH_NAMESPACE
class BluetoothSetupResultHandler;
class BluetoothDaemonA2dpModule
{
public:
enum {
SERVICE_ID = 0x06
};
enum {
OPCODE_ERROR = 0x00,
OPCODE_CONNECT = 0x01,
OPCODE_DISCONNECT = 0x02
};
virtual nsresult Send(BluetoothDaemonPDU* aPDU, void* aUserData) = 0;
virtual nsresult RegisterModule(uint8_t aId, uint8_t aMode,
BluetoothSetupResultHandler* aRes) = 0;
virtual nsresult UnregisterModule(uint8_t aId,
BluetoothSetupResultHandler* aRes) = 0;
void SetNotificationHandler(
BluetoothA2dpNotificationHandler* aNotificationHandler);
//
// Commands
//
nsresult ConnectCmd(const nsAString& aBdAddr,
BluetoothA2dpResultHandler* aRes);
nsresult DisconnectCmd(const nsAString& aBdAddr,
BluetoothA2dpResultHandler* aRes);
protected:
nsresult Send(BluetoothDaemonPDU* aPDU,
BluetoothA2dpResultHandler* aRes);
void HandleSvc(const BluetoothDaemonPDUHeader& aHeader,
BluetoothDaemonPDU& aPDU, void* aUserData);
//
// Responses
//
typedef BluetoothResultRunnable0<BluetoothA2dpResultHandler, void>
ResultRunnable;
typedef BluetoothResultRunnable1<BluetoothA2dpResultHandler, void,
BluetoothStatus, BluetoothStatus>
ErrorRunnable;
void ErrorRsp(const BluetoothDaemonPDUHeader& aHeader,
BluetoothDaemonPDU& aPDU,
BluetoothA2dpResultHandler* aRes);
void ConnectRsp(const BluetoothDaemonPDUHeader& aHeader,
BluetoothDaemonPDU& aPDU,
BluetoothA2dpResultHandler* aRes);
void DisconnectRsp(const BluetoothDaemonPDUHeader& aHeader,
BluetoothDaemonPDU& aPDU,
BluetoothA2dpResultHandler* aRes);
void HandleRsp(const BluetoothDaemonPDUHeader& aHeader,
BluetoothDaemonPDU& aPDU,
void* aUserData);
//
// Notifications
//
class NotificationHandlerWrapper;
typedef BluetoothNotificationRunnable2<NotificationHandlerWrapper, void,
BluetoothA2dpConnectionState,
nsString,
BluetoothA2dpConnectionState,
const nsAString&>
ConnectionStateNotification;
typedef BluetoothNotificationRunnable2<NotificationHandlerWrapper, void,
BluetoothA2dpAudioState,
nsString,
BluetoothA2dpAudioState,
const nsAString&>
AudioStateNotification;
class AudioStateInitOp;
class ConnectionStateInitOp;
void ConnectionStateNtf(const BluetoothDaemonPDUHeader& aHeader,
BluetoothDaemonPDU& aPDU);
void AudioStateNtf(const BluetoothDaemonPDUHeader& aHeader,
BluetoothDaemonPDU& aPDU);
void HandleNtf(const BluetoothDaemonPDUHeader& aHeader,
BluetoothDaemonPDU& aPDU,
void* aUserData);
static BluetoothA2dpNotificationHandler* sNotificationHandler;
};
class BluetoothDaemonA2dpInterface MOZ_FINAL
: public BluetoothA2dpInterface
{
class CleanupResultHandler;
class InitResultHandler;
public:
BluetoothDaemonA2dpInterface(BluetoothDaemonA2dpModule* aModule);
~BluetoothDaemonA2dpInterface();
void Init(
BluetoothA2dpNotificationHandler* aNotificationHandler,
BluetoothA2dpResultHandler* aRes);
void Cleanup(BluetoothA2dpResultHandler* aRes);
/* Connect / Disconnect */
void Connect(const nsAString& aBdAddr,
BluetoothA2dpResultHandler* aRes);
void Disconnect(const nsAString& aBdAddr,
BluetoothA2dpResultHandler* aRes);
private:
void DispatchError(BluetoothA2dpResultHandler* aRes,
BluetoothStatus aStatus);
BluetoothDaemonA2dpModule* mModule;
};
END_BLUETOOTH_NAMESPACE
#endif

View File

@ -97,6 +97,37 @@ Convert(uint8_t aIn, int& aOut)
return NS_OK;
}
nsresult
Convert(uint8_t aIn, BluetoothA2dpAudioState& aOut)
{
static const BluetoothA2dpAudioState sAudioState[] = {
CONVERT(0x00, A2DP_AUDIO_STATE_REMOTE_SUSPEND),
CONVERT(0x01, A2DP_AUDIO_STATE_STOPPED),
CONVERT(0x02, A2DP_AUDIO_STATE_STARTED)
};
if (NS_WARN_IF(aIn >= MOZ_ARRAY_LENGTH(sAudioState))) {
return NS_ERROR_ILLEGAL_VALUE;
}
aOut = sAudioState[aIn];
return NS_OK;
}
nsresult
Convert(uint8_t aIn, BluetoothA2dpConnectionState& aOut)
{
static const BluetoothA2dpConnectionState sConnectionState[] = {
CONVERT(0x00, A2DP_CONNECTION_STATE_DISCONNECTED),
CONVERT(0x01, A2DP_CONNECTION_STATE_CONNECTING),
CONVERT(0x02, A2DP_CONNECTION_STATE_CONNECTED),
CONVERT(0x03, A2DP_CONNECTION_STATE_DISCONNECTING)
};
if (NS_WARN_IF(aIn >= MOZ_ARRAY_LENGTH(sConnectionState))) {
return NS_ERROR_ILLEGAL_VALUE;
}
aOut = sConnectionState[aIn];
return NS_OK;
}
nsresult
Convert(uint8_t aIn, BluetoothAclState& aOut)
{
@ -934,6 +965,20 @@ UnpackPDU(BluetoothDaemonPDU& aPDU, char& aOut)
return UnpackPDU(aPDU, UnpackConversion<uint8_t, char>(aOut));
}
nsresult
UnpackPDU(BluetoothDaemonPDU& aPDU, BluetoothA2dpAudioState& aOut)
{
return UnpackPDU(
aPDU, UnpackConversion<uint8_t, BluetoothA2dpAudioState>(aOut));
}
nsresult
UnpackPDU(BluetoothDaemonPDU& aPDU, BluetoothA2dpConnectionState& aOut)
{
return UnpackPDU(
aPDU, UnpackConversion<uint8_t, BluetoothA2dpConnectionState>(aOut));
}
nsresult
UnpackPDU(BluetoothDaemonPDU& aPDU, BluetoothAclState& aOut)
{

View File

@ -118,6 +118,12 @@ Convert(uint8_t aIn, char& aOut);
nsresult
Convert(uint8_t aIn, int& aOut);
nsresult
Convert(uint8_t aIn, BluetoothA2dpAudioState& aOut);
nsresult
Convert(uint8_t aIn, BluetoothA2dpConnectionState& aOut);
nsresult
Convert(uint8_t aIn, BluetoothAclState& aOut);
@ -554,6 +560,12 @@ UnpackPDU(BluetoothDaemonPDU& aPDU, bool& aOut);
nsresult
UnpackPDU(BluetoothDaemonPDU& aPDU, char& aOut);
nsresult
UnpackPDU(BluetoothDaemonPDU& aPDU, BluetoothA2dpAudioState& aOut);
nsresult
UnpackPDU(BluetoothDaemonPDU& aPDU, BluetoothA2dpConnectionState& aOut);
nsresult
UnpackPDU(BluetoothDaemonPDU& aPDU, BluetoothAclState& aOut);

View File

@ -5,6 +5,7 @@
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "BluetoothDaemonInterface.h"
#include "BluetoothDaemonA2dpInterface.h"
#include "BluetoothDaemonHandsfreeInterface.h"
#include "BluetoothDaemonHelpers.h"
#include "BluetoothDaemonSetupInterface.h"
@ -1361,6 +1362,7 @@ class BluetoothDaemonProtocol MOZ_FINAL
, public BluetoothDaemonCoreModule
, public BluetoothDaemonSocketModule
, public BluetoothDaemonHandsfreeModule
, public BluetoothDaemonA2dpModule
{
public:
BluetoothDaemonProtocol(BluetoothDaemonConnection* aConnection);
@ -1394,6 +1396,8 @@ private:
BluetoothDaemonPDU& aPDU, void* aUserData);
void HandleHandsfreeSvc(const BluetoothDaemonPDUHeader& aHeader,
BluetoothDaemonPDU& aPDU, void* aUserData);
void HandleA2dpSvc(const BluetoothDaemonPDUHeader& aHeader,
BluetoothDaemonPDU& aPDU, void* aUserData);
BluetoothDaemonConnection* mConnection;
nsTArray<void*> mUserDataQ;
@ -1462,6 +1466,14 @@ BluetoothDaemonProtocol::HandleHandsfreeSvc(
BluetoothDaemonHandsfreeModule::HandleSvc(aHeader, aPDU, aUserData);
}
void
BluetoothDaemonProtocol::HandleA2dpSvc(
const BluetoothDaemonPDUHeader& aHeader, BluetoothDaemonPDU& aPDU,
void* aUserData)
{
BluetoothDaemonA2dpModule::HandleSvc(aHeader, aPDU, aUserData);
}
void
BluetoothDaemonProtocol::Handle(BluetoothDaemonPDU& aPDU)
{
@ -1473,7 +1485,9 @@ BluetoothDaemonProtocol::Handle(BluetoothDaemonPDU& aPDU)
INIT_ARRAY_AT(0x03, nullptr), // HID host
INIT_ARRAY_AT(0x04, nullptr), // PAN
INIT_ARRAY_AT(BluetoothDaemonHandsfreeModule::SERVICE_ID,
&BluetoothDaemonProtocol::HandleHandsfreeSvc)
&BluetoothDaemonProtocol::HandleHandsfreeSvc),
INIT_ARRAY_AT(BluetoothDaemonA2dpModule::SERVICE_ID,
&BluetoothDaemonProtocol::HandleA2dpSvc)
};
BluetoothDaemonPDUHeader header;
@ -2053,7 +2067,13 @@ BluetoothDaemonInterface::GetBluetoothHandsfreeInterface()
BluetoothA2dpInterface*
BluetoothDaemonInterface::GetBluetoothA2dpInterface()
{
return nullptr;
if (mA2dpInterface) {
return mA2dpInterface;
}
mA2dpInterface = new BluetoothDaemonA2dpInterface(mProtocol);
return mA2dpInterface;
}
BluetoothAvrcpInterface*

View File

@ -12,6 +12,7 @@
BEGIN_BLUETOOTH_NAMESPACE
class BluetoothDaemonChannel;
class BluetoothDaemonA2dpInterface;
class BluetoothDaemonHandsfreeInterface;
class BluetoothDaemonProtocol;
class BluetoothDaemonSocketInterface;
@ -127,6 +128,7 @@ private:
nsAutoPtr<BluetoothDaemonSocketInterface> mSocketInterface;
nsAutoPtr<BluetoothDaemonHandsfreeInterface> mHandsfreeInterface;
nsAutoPtr<BluetoothDaemonA2dpInterface> mA2dpInterface;
};
END_BLUETOOTH_NAMESPACE

View File

@ -48,6 +48,7 @@ if CONFIG['MOZ_B2G_BT']:
'bluedroid/BluetoothA2dpHALInterface.cpp',
'bluedroid/BluetoothA2dpManager.cpp',
'bluedroid/BluetoothAvrcpHALInterface.cpp',
'bluedroid/BluetoothDaemonA2dpInterface.cpp',
'bluedroid/BluetoothDaemonHandsfreeInterface.cpp',
'bluedroid/BluetoothDaemonHelpers.cpp',
'bluedroid/BluetoothDaemonInterface.cpp',

View File

@ -657,6 +657,7 @@ BrowserElementChild.prototype = {
zoomFactor: zoomFactor,
states: e.states,
isCollapsed: (e.selectedText.length == 0),
visible: e.visible,
};
// Get correct geometry information if we have nested iframe.

View File

@ -68,26 +68,7 @@ NS_IMPL_CYCLE_COLLECTION_INHERITED(nsDOMCameraControl, DOMMediaStream,
mTakePicturePromise,
mStartRecordingPromise,
mReleasePromise,
mSetConfigurationPromise,
mGetCameraOnSuccessCb,
mGetCameraOnErrorCb,
mAutoFocusOnSuccessCb,
mAutoFocusOnErrorCb,
mTakePictureOnSuccessCb,
mTakePictureOnErrorCb,
mStartRecordingOnSuccessCb,
mStartRecordingOnErrorCb,
mReleaseOnSuccessCb,
mReleaseOnErrorCb,
mSetConfigurationOnSuccessCb,
mSetConfigurationOnErrorCb,
mOnShutterCb,
mOnClosedCb,
mOnRecorderStateChangeCb,
mOnPreviewStateChangeCb,
mOnAutoFocusMovingCb,
mOnAutoFocusCompletedCb,
mOnFacesDetectedCb)
mSetConfigurationPromise)
/* static */
bool
@ -207,33 +188,12 @@ nsDOMCameraControl::DiscardCachedCameraInstance(nsITimer* aTimer, void* aClosure
nsDOMCameraControl::nsDOMCameraControl(uint32_t aCameraId,
const CameraConfiguration& aInitialConfig,
GetCameraCallback* aOnSuccess,
CameraErrorCallback* aOnError,
Promise* aPromise,
nsPIDOMWindow* aWindow)
: DOMMediaStream()
, mCameraControl(nullptr)
, mAudioChannelAgent(nullptr)
, mGetCameraPromise(aPromise)
, mGetCameraOnSuccessCb(aOnSuccess)
, mGetCameraOnErrorCb(aOnError)
, mAutoFocusOnSuccessCb(nullptr)
, mAutoFocusOnErrorCb(nullptr)
, mTakePictureOnSuccessCb(nullptr)
, mTakePictureOnErrorCb(nullptr)
, mStartRecordingOnSuccessCb(nullptr)
, mStartRecordingOnErrorCb(nullptr)
, mReleaseOnSuccessCb(nullptr)
, mReleaseOnErrorCb(nullptr)
, mSetConfigurationOnSuccessCb(nullptr)
, mSetConfigurationOnErrorCb(nullptr)
, mOnShutterCb(nullptr)
, mOnClosedCb(nullptr)
, mOnRecorderStateChangeCb(nullptr)
, mOnPreviewStateChangeCb(nullptr)
, mOnAutoFocusMovingCb(nullptr)
, mOnAutoFocusCompletedCb(nullptr)
, mOnFacesDetectedCb(nullptr)
, mWindow(aWindow)
, mPreviewState(CameraControlListener::kPreviewStopped)
{
@ -665,85 +625,6 @@ nsDOMCameraControl::SensorAngle()
return angle;
}
// Callback attributes
CameraShutterCallback*
nsDOMCameraControl::GetOnShutter()
{
return mOnShutterCb;
}
void
nsDOMCameraControl::SetOnShutter(CameraShutterCallback* aCb)
{
mOnShutterCb = aCb;
}
CameraClosedCallback*
nsDOMCameraControl::GetOnClosed()
{
return mOnClosedCb;
}
void
nsDOMCameraControl::SetOnClosed(CameraClosedCallback* aCb)
{
mOnClosedCb = aCb;
}
CameraRecorderStateChange*
nsDOMCameraControl::GetOnRecorderStateChange()
{
return mOnRecorderStateChangeCb;
}
void
nsDOMCameraControl::SetOnRecorderStateChange(CameraRecorderStateChange* aCb)
{
mOnRecorderStateChangeCb = aCb;
}
CameraPreviewStateChange*
nsDOMCameraControl::GetOnPreviewStateChange()
{
return mOnPreviewStateChangeCb;
}
void
nsDOMCameraControl::SetOnPreviewStateChange(CameraPreviewStateChange* aCb)
{
mOnPreviewStateChangeCb = aCb;
}
CameraAutoFocusMovingCallback*
nsDOMCameraControl::GetOnAutoFocusMoving()
{
return mOnAutoFocusMovingCb;
}
void
nsDOMCameraControl::SetOnAutoFocusMoving(CameraAutoFocusMovingCallback* aCb)
{
mOnAutoFocusMovingCb = aCb;
}
CameraAutoFocusCallback*
nsDOMCameraControl::GetOnAutoFocusCompleted()
{
return mOnAutoFocusCompletedCb;
}
void
nsDOMCameraControl::SetOnAutoFocusCompleted(CameraAutoFocusCallback* aCb)
{
mOnAutoFocusCompletedCb = aCb;
}
CameraFaceDetectionCallback*
nsDOMCameraControl::GetOnFacesDetected()
{
return mOnFacesDetectedCb;
}
void
nsDOMCameraControl::SetOnFacesDetected(CameraFaceDetectionCallback* aCb)
{
mOnFacesDetectedCb = aCb;
}
already_AddRefed<dom::CameraCapabilities>
nsDOMCameraControl::Capabilities()
{
@ -757,35 +638,11 @@ nsDOMCameraControl::Capabilities()
return caps.forget();
}
class ImmediateErrorCallback : public nsRunnable
{
public:
ImmediateErrorCallback(CameraErrorCallback* aCallback, const nsAString& aMessage)
: mCallback(aCallback)
, mMessage(aMessage)
{ }
NS_IMETHODIMP
Run()
{
MOZ_ASSERT(NS_IsMainThread());
ErrorResult ignored;
mCallback->Call(mMessage, ignored);
return NS_OK;
}
protected:
nsRefPtr<CameraErrorCallback> mCallback;
nsString mMessage;
};
// Methods.
already_AddRefed<Promise>
nsDOMCameraControl::StartRecording(const CameraStartRecordingOptions& aOptions,
nsDOMDeviceStorage& aStorageArea,
const nsAString& aFilename,
const Optional<OwningNonNull<CameraStartRecordingCallback> >& aOnSuccess,
const Optional<OwningNonNull<CameraErrorCallback> >& aOnError,
ErrorResult& aRv)
{
MOZ_ASSERT(mCameraControl);
@ -797,11 +654,6 @@ nsDOMCameraControl::StartRecording(const CameraStartRecordingOptions& aOptions,
if (mStartRecordingPromise) {
promise->MaybeReject(NS_ERROR_IN_PROGRESS);
if (aOnError.WasPassed()) {
DOM_CAMERA_LOGT("%s:onError WasPassed\n", __func__);
NS_DispatchToMainThread(new ImmediateErrorCallback(&aOnError.Value(),
NS_LITERAL_STRING("StartRecordingInProgress")));
}
return promise.forget();
}
@ -830,14 +682,6 @@ nsDOMCameraControl::StartRecording(const CameraStartRecordingOptions& aOptions,
mStartRecordingPromise = promise;
mOptions = aOptions;
mStartRecordingOnSuccessCb = nullptr;
if (aOnSuccess.WasPassed()) {
mStartRecordingOnSuccessCb = &aOnSuccess.Value();
}
mStartRecordingOnErrorCb = nullptr;
if (aOnError.WasPassed()) {
mStartRecordingOnErrorCb = &aOnError.Value();
}
nsCOMPtr<nsIDOMEventListener> listener = new StartRecordingHelper(this);
request->AddEventListener(NS_LITERAL_STRING("success"), listener, false);
@ -899,8 +743,6 @@ nsDOMCameraControl::ResumePreview(ErrorResult& aRv)
already_AddRefed<Promise>
nsDOMCameraControl::SetConfiguration(const CameraConfiguration& aConfiguration,
const Optional<OwningNonNull<CameraSetConfigurationCallback> >& aOnSuccess,
const Optional<OwningNonNull<CameraErrorCallback> >& aOnError,
ErrorResult& aRv)
{
MOZ_ASSERT(mCameraControl);
@ -911,14 +753,8 @@ nsDOMCameraControl::SetConfiguration(const CameraConfiguration& aConfiguration,
}
if (mTakePicturePromise) {
promise->MaybeReject(NS_ERROR_IN_PROGRESS);
// We're busy taking a picture, can't change modes right now.
if (aOnError.WasPassed()) {
// There is already a call to TakePicture() in progress, abort this
// call and invoke the error callback (if one was passed in).
NS_DispatchToMainThread(new ImmediateErrorCallback(&aOnError.Value(),
NS_LITERAL_STRING("TakePictureInProgress")));
}
promise->MaybeReject(NS_ERROR_IN_PROGRESS);
return promise.forget();
}
@ -937,21 +773,11 @@ nsDOMCameraControl::SetConfiguration(const CameraConfiguration& aConfiguration,
}
mSetConfigurationPromise = promise;
mSetConfigurationOnSuccessCb = nullptr;
if (aOnSuccess.WasPassed()) {
mSetConfigurationOnSuccessCb = &aOnSuccess.Value();
}
mSetConfigurationOnErrorCb = nullptr;
if (aOnError.WasPassed()) {
mSetConfigurationOnErrorCb = &aOnError.Value();
}
return promise.forget();
}
already_AddRefed<Promise>
nsDOMCameraControl::AutoFocus(const Optional<OwningNonNull<CameraAutoFocusCallback> >& aOnSuccess,
const Optional<OwningNonNull<CameraErrorCallback> >& aOnError,
ErrorResult& aRv)
nsDOMCameraControl::AutoFocus(ErrorResult& aRv)
{
MOZ_ASSERT(mCameraControl);
@ -960,12 +786,6 @@ nsDOMCameraControl::AutoFocus(const Optional<OwningNonNull<CameraAutoFocusCallba
// There is already a call to AutoFocus() in progress, cancel it and
// invoke the error callback (if one was passed in).
promise->MaybeReject(NS_ERROR_IN_PROGRESS);
mAutoFocusOnSuccessCb = nullptr;
nsRefPtr<CameraErrorCallback> ecb = mAutoFocusOnErrorCb.forget();
if (ecb) {
NS_DispatchToMainThread(new ImmediateErrorCallback(ecb,
NS_LITERAL_STRING("AutoFocusInterrupted")));
}
}
promise = CreatePromise(aRv);
@ -981,14 +801,6 @@ nsDOMCameraControl::AutoFocus(const Optional<OwningNonNull<CameraAutoFocusCallba
DispatchStateEvent(NS_LITERAL_STRING("focus"), NS_LITERAL_STRING("focusing"));
mAutoFocusPromise = promise;
mAutoFocusOnSuccessCb = nullptr;
if (aOnSuccess.WasPassed()) {
mAutoFocusOnSuccessCb = &aOnSuccess.Value();
}
mAutoFocusOnErrorCb = nullptr;
if (aOnError.WasPassed()) {
mAutoFocusOnErrorCb = &aOnError.Value();
}
return promise.forget();
}
@ -1008,8 +820,6 @@ nsDOMCameraControl::StopFaceDetection(ErrorResult& aRv)
already_AddRefed<Promise>
nsDOMCameraControl::TakePicture(const CameraPictureOptions& aOptions,
const Optional<OwningNonNull<CameraTakePictureCallback> >& aOnSuccess,
const Optional<OwningNonNull<CameraErrorCallback> >& aOnError,
ErrorResult& aRv)
{
MOZ_ASSERT(mCameraControl);
@ -1023,10 +833,6 @@ nsDOMCameraControl::TakePicture(const CameraPictureOptions& aOptions,
// There is already a call to TakePicture() in progress, abort this new
// one and invoke the error callback (if one was passed in).
promise->MaybeReject(NS_ERROR_IN_PROGRESS);
if (aOnError.WasPassed()) {
NS_DispatchToMainThread(new ImmediateErrorCallback(&aOnError.Value(),
NS_LITERAL_STRING("TakePictureAlreadyInProgress")));
}
return promise.forget();
}
@ -1059,21 +865,11 @@ nsDOMCameraControl::TakePicture(const CameraPictureOptions& aOptions,
}
mTakePicturePromise = promise;
mTakePictureOnSuccessCb = nullptr;
if (aOnSuccess.WasPassed()) {
mTakePictureOnSuccessCb = &aOnSuccess.Value();
}
mTakePictureOnErrorCb = nullptr;
if (aOnError.WasPassed()) {
mTakePictureOnErrorCb = &aOnError.Value();
}
return promise.forget();
}
already_AddRefed<Promise>
nsDOMCameraControl::ReleaseHardware(const Optional<OwningNonNull<CameraReleaseCallback> >& aOnSuccess,
const Optional<OwningNonNull<CameraErrorCallback> >& aOnError,
ErrorResult& aRv)
nsDOMCameraControl::ReleaseHardware(ErrorResult& aRv)
{
MOZ_ASSERT(mCameraControl);
@ -1088,14 +884,6 @@ nsDOMCameraControl::ReleaseHardware(const Optional<OwningNonNull<CameraReleaseCa
}
mReleasePromise = promise;
mReleaseOnSuccessCb = nullptr;
if (aOnSuccess.WasPassed()) {
mReleaseOnSuccessCb = &aOnSuccess.Value();
}
mReleaseOnErrorCb = nullptr;
if (aOnError.WasPassed()) {
mReleaseOnErrorCb = &aOnError.Value();
}
return promise.forget();
}
@ -1121,27 +909,6 @@ nsDOMCameraControl::Shutdown()
AbortPromise(mStartRecordingPromise);
AbortPromise(mReleasePromise);
AbortPromise(mSetConfigurationPromise);
mGetCameraOnSuccessCb = nullptr;
mGetCameraOnErrorCb = nullptr;
mAutoFocusOnSuccessCb = nullptr;
mAutoFocusOnErrorCb = nullptr;
mTakePictureOnSuccessCb = nullptr;
mTakePictureOnErrorCb = nullptr;
mStartRecordingOnSuccessCb = nullptr;
mStartRecordingOnErrorCb = nullptr;
mReleaseOnSuccessCb = nullptr;
mReleaseOnErrorCb = nullptr;
mSetConfigurationOnSuccessCb = nullptr;
mSetConfigurationOnErrorCb = nullptr;
// Remove all of the unsolicited event handlers too.
mOnShutterCb = nullptr;
mOnClosedCb = nullptr;
mOnRecorderStateChangeCb = nullptr;
mOnPreviewStateChangeCb = nullptr;
mOnAutoFocusMovingCb = nullptr;
mOnAutoFocusCompletedCb = nullptr;
mOnFacesDetectedCb = nullptr;
mCameraControl->Shutdown();
}
@ -1236,12 +1003,6 @@ nsDOMCameraControl::OnHardwareStateChange(CameraControlListener::HardwareState a
data.mConfiguration = *mCurrentConfiguration;
promise->MaybeResolve(data);
}
nsRefPtr<GetCameraCallback> cb = mGetCameraOnSuccessCb.forget();
mGetCameraOnErrorCb = nullptr;
if (cb) {
ErrorResult ignored;
cb->Call(*this, *mCurrentConfiguration, ignored);
}
}
break;
@ -1253,13 +1014,6 @@ nsDOMCameraControl::OnHardwareStateChange(CameraControlListener::HardwareState a
promise->MaybeResolve(JS::UndefinedHandleValue);
}
nsRefPtr<CameraReleaseCallback> rcb = mReleaseOnSuccessCb.forget();
mReleaseOnErrorCb = nullptr;
if (rcb) {
ErrorResult ignored;
rcb->Call(ignored);
}
CameraClosedEventInit eventInit;
switch (aReason) {
case NS_OK:
@ -1281,10 +1035,6 @@ nsDOMCameraControl::OnHardwareStateChange(CameraControlListener::HardwareState a
break;
}
nsRefPtr<CameraClosedCallback> cb = mOnClosedCb;
if (cb) {
cb->Call(eventInit.mReason, ignored);
}
nsRefPtr<CameraClosedEvent> event =
CameraClosedEvent::Constructor(this,
NS_LITERAL_STRING("close"),
@ -1311,13 +1061,6 @@ nsDOMCameraControl::OnShutter()
MOZ_ASSERT(NS_IsMainThread());
DOM_CAMERA_LOGI("DOM ** SNAP **\n");
nsRefPtr<CameraShutterCallback> cb = mOnShutterCb;
if (cb) {
ErrorResult ignored;
cb->Call(ignored);
}
DispatchTrustedEvent(NS_LITERAL_STRING("shutter"));
}
@ -1338,12 +1081,6 @@ nsDOMCameraControl::OnPreviewStateChange(CameraControlListener::PreviewState aSt
break;
}
nsRefPtr<CameraPreviewStateChange> cb = mOnPreviewStateChangeCb;
if (cb) {
ErrorResult ignored;
cb->Call(state, ignored);
}
DispatchPreviewStateEvent(aState);
}
@ -1365,11 +1102,6 @@ nsDOMCameraControl::OnRecorderStateChange(CameraControlListener::RecorderState a
promise->MaybeResolve(JS::UndefinedHandleValue);
}
nsRefPtr<CameraStartRecordingCallback> scb = mStartRecordingOnSuccessCb.forget();
mStartRecordingOnErrorCb = nullptr;
if (scb) {
scb->Call(ignored);
}
state = NS_LITERAL_STRING("Started");
}
break;
@ -1410,11 +1142,6 @@ nsDOMCameraControl::OnRecorderStateChange(CameraControlListener::RecorderState a
return;
}
nsRefPtr<CameraRecorderStateChange> cb = mOnRecorderStateChangeCb;
if (cb) {
cb->Call(state, ignored);
}
DispatchStateEvent(NS_LITERAL_STRING("recorderstatechange"), state);
}
@ -1444,13 +1171,6 @@ nsDOMCameraControl::OnConfigurationChange(DOMCameraConfiguration* aConfiguration
promise->MaybeResolve(*aConfiguration);
}
nsRefPtr<CameraSetConfigurationCallback> cb = mSetConfigurationOnSuccessCb.forget();
mSetConfigurationOnErrorCb = nullptr;
if (cb) {
ErrorResult ignored;
cb->Call(*mCurrentConfiguration, ignored);
}
CameraConfigurationEventInit eventInit;
eventInit.mMode = mCurrentConfiguration->mMode;
eventInit.mRecorderProfile = mCurrentConfiguration->mRecorderProfile;
@ -1476,19 +1196,6 @@ nsDOMCameraControl::OnAutoFocusComplete(bool aAutoFocusSucceeded)
promise->MaybeResolve(aAutoFocusSucceeded);
}
nsRefPtr<CameraAutoFocusCallback> cb = mAutoFocusOnSuccessCb.forget();
mAutoFocusOnErrorCb = nullptr;
if (cb) {
ErrorResult ignored;
cb->Call(aAutoFocusSucceeded, ignored);
}
cb = mOnAutoFocusCompletedCb;
if (cb) {
ErrorResult ignored;
cb->Call(aAutoFocusSucceeded, ignored);
}
if (aAutoFocusSucceeded) {
DispatchStateEvent(NS_LITERAL_STRING("focus"), NS_LITERAL_STRING("focused"));
} else {
@ -1501,12 +1208,6 @@ nsDOMCameraControl::OnAutoFocusMoving(bool aIsMoving)
{
MOZ_ASSERT(NS_IsMainThread());
nsRefPtr<CameraAutoFocusMovingCallback> cb = mOnAutoFocusMovingCb;
if (cb) {
ErrorResult ignored;
cb->Call(aIsMoving, ignored);
}
if (aIsMoving) {
DispatchStateEvent(NS_LITERAL_STRING("focus"), NS_LITERAL_STRING("focusing"));
}
@ -1529,12 +1230,6 @@ nsDOMCameraControl::OnFacesDetected(const nsTArray<ICameraControl::Face>& aFaces
}
}
nsRefPtr<CameraFaceDetectionCallback> cb = mOnFacesDetectedCb;
if (cb) {
ErrorResult ignored;
cb->Call(faces, ignored);
}
CameraFacesDetectedEventInit eventInit;
eventInit.mFaces.SetValue(faces);
@ -1559,14 +1254,6 @@ nsDOMCameraControl::OnTakePictureComplete(nsIDOMBlob* aPicture)
}
nsRefPtr<File> blob = static_cast<File*>(aPicture);
nsRefPtr<CameraTakePictureCallback> cb = mTakePictureOnSuccessCb.forget();
mTakePictureOnErrorCb = nullptr;
if (cb) {
ErrorResult ignored;
cb->Call(*blob, ignored);
}
BlobEventInit eventInit;
eventInit.mData = blob;
@ -1580,21 +1267,18 @@ nsDOMCameraControl::OnTakePictureComplete(nsIDOMBlob* aPicture)
void
nsDOMCameraControl::OnUserError(CameraControlListener::UserContext aContext, nsresult aError)
{
DOM_CAMERA_LOGI("DOM OnUserError aContext=%u, aError=0x%x\n", aContext, aError);
MOZ_ASSERT(NS_IsMainThread());
nsRefPtr<Promise> promise;
nsRefPtr<CameraErrorCallback> errorCb;
switch (aContext) {
case CameraControlListener::kInStartCamera:
promise = mGetCameraPromise.forget();
mGetCameraOnSuccessCb = nullptr;
errorCb = mGetCameraOnErrorCb.forget();
break;
case CameraControlListener::kInStopCamera:
promise = mReleasePromise.forget();
errorCb = mReleaseOnErrorCb.forget();
if (aError == NS_ERROR_NOT_INITIALIZED) {
// This value indicates that the hardware is already closed; which for
// kInStopCamera, is not actually an error.
@ -1602,41 +1286,25 @@ nsDOMCameraControl::OnUserError(CameraControlListener::UserContext aContext, nsr
promise->MaybeResolve(JS::UndefinedHandleValue);
}
nsRefPtr<CameraReleaseCallback> cb = mReleaseOnSuccessCb.forget();
mReleaseOnErrorCb = nullptr;
if (cb) {
ErrorResult ignored;
cb->Call(ignored);
}
return;
}
mReleaseOnSuccessCb = nullptr;
break;
case CameraControlListener::kInSetConfiguration:
promise = mSetConfigurationPromise.forget();
mSetConfigurationOnSuccessCb = nullptr;
errorCb = mSetConfigurationOnErrorCb.forget();
break;
case CameraControlListener::kInAutoFocus:
promise = mAutoFocusPromise.forget();
mAutoFocusOnSuccessCb = nullptr;
errorCb = mAutoFocusOnErrorCb.forget();
DispatchStateEvent(NS_LITERAL_STRING("focus"), NS_LITERAL_STRING("unfocused"));
break;
case CameraControlListener::kInTakePicture:
promise = mTakePicturePromise.forget();
mTakePictureOnSuccessCb = nullptr;
errorCb = mTakePictureOnErrorCb.forget();
break;
case CameraControlListener::kInStartRecording:
promise = mStartRecordingPromise.forget();
mStartRecordingOnSuccessCb = nullptr;
errorCb = mStartRecordingOnErrorCb.forget();
break;
case CameraControlListener::kInStartFaceDetection:
@ -1696,57 +1364,11 @@ nsDOMCameraControl::OnUserError(CameraControlListener::UserContext aContext, nsr
return;
}
if (!promise && !errorCb) {
if (!promise) {
DOM_CAMERA_LOGW("DOM No error handler for aError=0x%x in aContext=%u\n",
aError, aContext);
return;
}
DOM_CAMERA_LOGI("DOM OnUserError aContext=%u, aError=0x%x\n", aContext, aError);
if (promise) {
promise->MaybeReject(aError);
}
if (errorCb) {
nsString error;
switch (aError) {
case NS_ERROR_INVALID_ARG:
error = NS_LITERAL_STRING("InvalidArgument");
break;
case NS_ERROR_NOT_AVAILABLE:
error = NS_LITERAL_STRING("NotAvailable");
break;
case NS_ERROR_NOT_IMPLEMENTED:
error = NS_LITERAL_STRING("NotImplemented");
break;
case NS_ERROR_NOT_INITIALIZED:
error = NS_LITERAL_STRING("HardwareClosed");
break;
case NS_ERROR_ALREADY_INITIALIZED:
error = NS_LITERAL_STRING("HardwareAlreadyOpen");
break;
case NS_ERROR_OUT_OF_MEMORY:
error = NS_LITERAL_STRING("OutOfMemory");
break;
default:
{
nsPrintfCString msg("Reporting aError=0x%x as generic\n", aError);
NS_WARNING(msg.get());
}
// fallthrough
case NS_ERROR_FAILURE:
error = NS_LITERAL_STRING("GeneralFailure");
break;
}
ErrorResult ignored;
errorCb->Call(error, ignored);
}
promise->MaybeReject(aError);
}

View File

@ -55,8 +55,6 @@ public:
nsDOMCameraControl(uint32_t aCameraId,
const dom::CameraConfiguration& aInitialConfig,
dom::GetCameraCallback* aOnSuccess,
dom::CameraErrorCallback* aOnError,
dom::Promise* aPromise,
nsPIDOMWindow* aWindow);
@ -90,26 +88,8 @@ public:
double GetPictureQuality(ErrorResult& aRv);
void SetPictureQuality(double aQuality, ErrorResult& aRv);
// Unsolicited event handlers.
dom::CameraShutterCallback* GetOnShutter();
void SetOnShutter(dom::CameraShutterCallback* aCb);
dom::CameraClosedCallback* GetOnClosed();
void SetOnClosed(dom::CameraClosedCallback* aCb);
dom::CameraRecorderStateChange* GetOnRecorderStateChange();
void SetOnRecorderStateChange(dom::CameraRecorderStateChange* aCb);
dom::CameraPreviewStateChange* GetOnPreviewStateChange();
void SetOnPreviewStateChange(dom::CameraPreviewStateChange* aCb);
dom::CameraAutoFocusMovingCallback* GetOnAutoFocusMoving();
void SetOnAutoFocusMoving(dom::CameraAutoFocusMovingCallback* aCb);
dom::CameraAutoFocusCallback* GetOnAutoFocusCompleted();
void SetOnAutoFocusCompleted(dom::CameraAutoFocusCallback* aCb);
dom::CameraFaceDetectionCallback* GetOnFacesDetected();
void SetOnFacesDetected(dom::CameraFaceDetectionCallback* aCb);
// Methods.
already_AddRefed<dom::Promise> SetConfiguration(const dom::CameraConfiguration& aConfiguration,
const dom::Optional<dom::OwningNonNull<dom::CameraSetConfigurationCallback> >& aOnSuccess,
const dom::Optional<dom::OwningNonNull<dom::CameraErrorCallback> >& aOnError,
ErrorResult& aRv);
void GetMeteringAreas(nsTArray<dom::CameraRegion>& aAreas, ErrorResult& aRv);
void SetMeteringAreas(const dom::Optional<dom::Sequence<dom::CameraRegion> >& aAreas, ErrorResult& aRv);
@ -119,26 +99,18 @@ public:
void SetPictureSize(const dom::CameraSize& aSize, ErrorResult& aRv);
void GetThumbnailSize(dom::CameraSize& aSize, ErrorResult& aRv);
void SetThumbnailSize(const dom::CameraSize& aSize, ErrorResult& aRv);
already_AddRefed<dom::Promise> AutoFocus(const dom::Optional<dom::OwningNonNull<dom::CameraAutoFocusCallback> >& aOnSuccess,
const dom::Optional<dom::OwningNonNull<dom::CameraErrorCallback> >& aOnError,
ErrorResult& aRv);
already_AddRefed<dom::Promise> AutoFocus(ErrorResult& aRv);
void StartFaceDetection(ErrorResult& aRv);
void StopFaceDetection(ErrorResult& aRv);
already_AddRefed<dom::Promise> TakePicture(const dom::CameraPictureOptions& aOptions,
const dom::Optional<dom::OwningNonNull<dom::CameraTakePictureCallback> >& aOnSuccess,
const dom::Optional<dom::OwningNonNull<dom::CameraErrorCallback> >& aOnError,
ErrorResult& aRv);
already_AddRefed<dom::Promise> StartRecording(const dom::CameraStartRecordingOptions& aOptions,
nsDOMDeviceStorage& storageArea,
const nsAString& filename,
const dom::Optional<dom::OwningNonNull<dom::CameraStartRecordingCallback> >& aOnSuccess,
const dom::Optional<dom::OwningNonNull<dom::CameraErrorCallback> >& aOnError,
ErrorResult& aRv);
void StopRecording(ErrorResult& aRv);
void ResumePreview(ErrorResult& aRv);
already_AddRefed<dom::Promise> ReleaseHardware(const dom::Optional<dom::OwningNonNull<dom::CameraReleaseCallback> >& aOnSuccess,
const dom::Optional<dom::OwningNonNull<dom::CameraErrorCallback> >& aOnError,
ErrorResult& aRv);
already_AddRefed<dom::Promise> ReleaseHardware(ErrorResult& aRv);
void ResumeContinuousFocus(ErrorResult& aRv);
virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
@ -223,29 +195,6 @@ protected:
nsRefPtr<dom::Promise> mReleasePromise;
nsRefPtr<dom::Promise> mSetConfigurationPromise;
// solicited camera control event handlers
nsRefPtr<dom::GetCameraCallback> mGetCameraOnSuccessCb;
nsRefPtr<dom::CameraErrorCallback> mGetCameraOnErrorCb;
nsRefPtr<dom::CameraAutoFocusCallback> mAutoFocusOnSuccessCb;
nsRefPtr<dom::CameraErrorCallback> mAutoFocusOnErrorCb;
nsRefPtr<dom::CameraTakePictureCallback> mTakePictureOnSuccessCb;
nsRefPtr<dom::CameraErrorCallback> mTakePictureOnErrorCb;
nsRefPtr<dom::CameraStartRecordingCallback> mStartRecordingOnSuccessCb;
nsRefPtr<dom::CameraErrorCallback> mStartRecordingOnErrorCb;
nsRefPtr<dom::CameraReleaseCallback> mReleaseOnSuccessCb;
nsRefPtr<dom::CameraErrorCallback> mReleaseOnErrorCb;
nsRefPtr<dom::CameraSetConfigurationCallback> mSetConfigurationOnSuccessCb;
nsRefPtr<dom::CameraErrorCallback> mSetConfigurationOnErrorCb;
// unsolicited event handlers
nsRefPtr<dom::CameraShutterCallback> mOnShutterCb;
nsRefPtr<dom::CameraClosedCallback> mOnClosedCb;
nsRefPtr<dom::CameraRecorderStateChange> mOnRecorderStateChangeCb;
nsRefPtr<dom::CameraPreviewStateChange> mOnPreviewStateChangeCb;
nsRefPtr<dom::CameraAutoFocusMovingCallback> mOnAutoFocusMovingCb;
nsRefPtr<dom::CameraAutoFocusCallback> mOnAutoFocusCompletedCb;
nsRefPtr<dom::CameraFaceDetectionCallback> mOnFacesDetectedCb;
// Camera event listener; we only need this weak reference so that
// we can remove the listener from the camera when we're done
// with it.

View File

@ -144,24 +144,17 @@ public:
nsRefPtr<nsDOMCameraManager> aManager,
uint32_t aCameraId,
const CameraConfiguration& aInitialConfig,
nsRefPtr<GetCameraCallback> aOnSuccess,
nsRefPtr<CameraErrorCallback> aOnError,
nsRefPtr<Promise> aPromise)
: mPrincipal(aPrincipal)
, mWindow(aWindow)
, mCameraManager(aManager)
, mCameraId(aCameraId)
, mInitialConfig(aInitialConfig)
, mOnSuccess(aOnSuccess)
, mOnError(aOnError)
, mPromise(aPromise)
{
}
{ }
protected:
virtual ~CameraPermissionRequest()
{
}
virtual ~CameraPermissionRequest() { }
nsresult DispatchCallback(uint32_t aPermission);
void CallAllow();
@ -171,15 +164,10 @@ protected:
nsRefPtr<nsDOMCameraManager> mCameraManager;
uint32_t mCameraId;
CameraConfiguration mInitialConfig;
nsRefPtr<GetCameraCallback> mOnSuccess;
nsRefPtr<CameraErrorCallback> mOnError;
nsRefPtr<Promise> mPromise;
};
NS_IMPL_CYCLE_COLLECTION(CameraPermissionRequest, mWindow,
mOnSuccess,
mOnError,
mPromise)
NS_IMPL_CYCLE_COLLECTION(CameraPermissionRequest, mWindow, mPromise)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CameraPermissionRequest)
NS_INTERFACE_MAP_ENTRY(nsIContentPermissionRequest)
@ -245,13 +233,13 @@ CameraPermissionRequest::DispatchCallback(uint32_t aPermission)
void
CameraPermissionRequest::CallAllow()
{
mCameraManager->PermissionAllowed(mCameraId, mInitialConfig, mOnSuccess, mOnError, mPromise);
mCameraManager->PermissionAllowed(mCameraId, mInitialConfig, mPromise);
}
void
CameraPermissionRequest::CallCancel()
{
mCameraManager->PermissionCancelled(mCameraId, mInitialConfig, mOnSuccess, mOnError, mPromise);
mCameraManager->PermissionCancelled(mCameraId, mInitialConfig, mPromise);
}
NS_IMETHODIMP
@ -275,8 +263,6 @@ nsDOMCameraManager::PreinitCameraHardware()
already_AddRefed<Promise>
nsDOMCameraManager::GetCamera(const nsAString& aCamera,
const CameraConfiguration& aInitialConfig,
const OptionalNonNullGetCameraCallback& aOnSuccess,
const OptionalNonNullCameraErrorCallback& aOnError,
ErrorResult& aRv)
{
DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
@ -297,18 +283,8 @@ nsDOMCameraManager::GetCamera(const nsAString& aCamera,
return nullptr;
}
nsRefPtr<GetCameraCallback> successCallback;
if (aOnSuccess.WasPassed()) {
successCallback = &aOnSuccess.Value();
}
nsRefPtr<CameraErrorCallback> errorCallback;
if (aOnError.WasPassed()) {
errorCallback = &aOnError.Value();
}
if (mPermission == nsIPermissionManager::ALLOW_ACTION) {
PermissionAllowed(cameraId, aInitialConfig, successCallback, errorCallback, promise);
PermissionAllowed(cameraId, aInitialConfig, promise);
return promise.forget();
}
@ -324,13 +300,13 @@ nsDOMCameraManager::GetCamera(const nsAString& aCamera,
uint16_t status = nsIPrincipal::APP_STATUS_NOT_INSTALLED;
principal->GetAppStatus(&status);
if (status == nsIPrincipal::APP_STATUS_CERTIFIED && CheckPermission(mWindow)) {
PermissionAllowed(cameraId, aInitialConfig, successCallback, errorCallback, promise);
PermissionAllowed(cameraId, aInitialConfig, promise);
return promise.forget();
}
nsCOMPtr<nsIRunnable> permissionRequest =
new CameraPermissionRequest(principal, mWindow, this, cameraId, aInitialConfig,
successCallback, errorCallback, promise);
new CameraPermissionRequest(principal, mWindow, this, cameraId,
aInitialConfig, promise);
NS_DispatchToMainThread(permissionRequest);
return promise.forget();
@ -339,8 +315,6 @@ nsDOMCameraManager::GetCamera(const nsAString& aCamera,
void
nsDOMCameraManager::PermissionAllowed(uint32_t aCameraId,
const CameraConfiguration& aInitialConfig,
GetCameraCallback* aOnSuccess,
CameraErrorCallback* aOnError,
Promise* aPromise)
{
mPermission = nsIPermissionManager::ALLOW_ACTION;
@ -348,7 +322,7 @@ nsDOMCameraManager::PermissionAllowed(uint32_t aCameraId,
// Creating this object will trigger the aOnSuccess callback
// (or the aOnError one, if it fails).
nsRefPtr<nsDOMCameraControl> cameraControl =
new nsDOMCameraControl(aCameraId, aInitialConfig, aOnSuccess, aOnError, aPromise, mWindow);
new nsDOMCameraControl(aCameraId, aInitialConfig, aPromise, mWindow);
Register(cameraControl);
}
@ -356,17 +330,10 @@ nsDOMCameraManager::PermissionAllowed(uint32_t aCameraId,
void
nsDOMCameraManager::PermissionCancelled(uint32_t aCameraId,
const CameraConfiguration& aInitialConfig,
GetCameraCallback* aOnSuccess,
CameraErrorCallback* aOnError,
Promise* aPromise)
{
mPermission = nsIPermissionManager::DENY_ACTION;
aPromise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
if (aOnError) {
ErrorResult ignored;
aOnError->Call(NS_LITERAL_STRING("Permission denied."), ignored);
}
}
void

View File

@ -26,17 +26,11 @@ namespace mozilla {
class nsDOMCameraControl;
namespace dom {
struct CameraConfiguration;
class GetCameraCallback;
class CameraErrorCallback;
}
}
typedef nsTArray<nsRefPtr<mozilla::nsDOMCameraControl> > CameraControls;
typedef nsClassHashtable<nsUint64HashKey, CameraControls> WindowTable;
typedef mozilla::dom::Optional<mozilla::dom::OwningNonNull<mozilla::dom::GetCameraCallback> >
OptionalNonNullGetCameraCallback;
typedef mozilla::dom::Optional<mozilla::dom::OwningNonNull<mozilla::dom::CameraErrorCallback> >
OptionalNonNullCameraErrorCallback;
class nsDOMCameraManager MOZ_FINAL
: public nsIObserver
@ -66,28 +60,21 @@ public:
void PermissionAllowed(uint32_t aCameraId,
const mozilla::dom::CameraConfiguration& aOptions,
mozilla::dom::GetCameraCallback* aOnSuccess,
mozilla::dom::CameraErrorCallback* aOnError,
mozilla::dom::Promise* aPromise);
void PermissionCancelled(uint32_t aCameraId,
const mozilla::dom::CameraConfiguration& aOptions,
mozilla::dom::GetCameraCallback* aOnSuccess,
mozilla::dom::CameraErrorCallback* aOnError,
mozilla::dom::Promise* aPromise);
// WebIDL
already_AddRefed<mozilla::dom::Promise>
GetCamera(const nsAString& aCamera,
const mozilla::dom::CameraConfiguration& aOptions,
const OptionalNonNullGetCameraCallback& aOnSuccess,
const OptionalNonNullCameraErrorCallback& aOnError,
mozilla::ErrorResult& aRv);
void GetListOfCameras(nsTArray<nsString>& aList, mozilla::ErrorResult& aRv);
nsPIDOMWindow* GetParentObject() const { return mWindow; }
virtual JSObject* WrapObject(JSContext* aCx)
MOZ_OVERRIDE;
virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
#ifdef MOZ_WIDGET_GONK
static void PreinitCameraHardware();

View File

@ -1,97 +0,0 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test for bug 1022766</title>
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="../camera_common.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<video id="viewfinder" width="200" height="200" autoplay></video>
<img src="#" alt="This image is going to load" id="testimage"/>
<script class="testbody" type="text/javascript;version=1.7">
var whichCamera = navigator.mozCameras.getListOfCameras()[0];
var config = {
mode: 'picture',
recorderProfile: 'cif',
previewSize: {
width: 352,
height: 288
}
};
function onError(e) {
ok(false, "Error" + JSON.stringify(e));
}
var Camera = {
cameraObj: null,
_otherPictureSize: null,
get viewfinder() {
return document.getElementById('viewfinder');
},
firstCallFailed: false,
secondCallSucceeded: false,
checkForDone: function test_checkForDone() {
if (Camera.firstCallFailed && Camera.secondCallSucceeded) {
Camera.cameraObj.release();
Camera.cameraObj = null;
CameraTest.end();
}
},
successOne: function test_successOne(focused) {
ok(false, "First call to autoFocus() succeeded unexpectedly");
},
failureOne: function test_failureOne(error) {
ok(error == "AutoFocusInterrupted", "First call to autoFocus() failed with: "
+ error);
Camera.firstCallFailed = true;
Camera.checkForDone();
},
successTwo: function test_successTwo(focused) {
ok(true, "Second call to autoFocus() succeeded");
Camera.secondCallSucceeded = true;
Camera.checkForDone();
},
failureTwo: function test_failureTwo(error) {
ok(false, "Second call to autoFocus() failed unexpectedly with: " + error);
},
start: function test_start() {
function onSuccess(camera, config) {
Camera.cameraObj = camera;
Camera.viewfinder.mozSrcObject = camera;
Camera.viewfinder.play();
// It doesn't matter if the emulator supports focus or not;
// this is just testing the sequencing.
camera.autoFocus(Camera.successOne, Camera.failureOne);
camera.autoFocus(Camera.successTwo, Camera.failureTwo);
};
navigator.mozCameras.getCamera(whichCamera, config, onSuccess, onError);
}
}
window.addEventListener('beforeunload', function() {
Camera.viewfinder.mozSrcObject = null;
if (Camera.cameraObj) {
Camera.cameraObj.release();
Camera.cameraObj = null;
}
});
CameraTest.begin("hardware", function(test) {
test.set("auto-focus-process-failure", function() {
Camera.start();
})
});
</script>
</body>
</html>

View File

@ -1,107 +0,0 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test for bug 1099390</title>
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="camera_common.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<video id="viewfinder" width="200" height="200" autoplay></video>
<img src="#" alt="This image is going to load" id="testimage"/>
<script class="testbody" type="text/javascript;version=1.7">
var whichCamera = navigator.mozCameras.getListOfCameras()[0];
var config = {
mode: 'picture',
recorderProfile: 'cif',
previewSize: {
width: 352,
height: 288
}
};
function onError(e) {
ok(false, "Error " + e);
}
var Camera = {
_cameraObj: null,
get viewfinder() {
return document.getElementById('viewfinder');
},
release: function release() {
viewfinder.mozSrcObject = null;
if (Camera._cameraObj) {
Camera._cameraObj.release();
Camera._cameraObj = null;
}
},
test: function test(cam) {
var gotCloseEvent = false;
var gotReleaseCallback = false;
function gotAll() {
var all = gotCloseEvent && gotReleaseCallback;
if (all) {
info("Got all expected notifications");
}
return all;
};
cam.onClosed = function(reason) {
cam.onClosed = null;
ok(!gotCloseEvent, "gotCloseEvent was " + gotCloseEvent);
ok(reason === "HardwareReleased", "onClosed reason is: " + reason);
gotCloseEvent = true;
if (gotAll()) {
SimpleTest.finish();
}
};
var onSuccess = function() {
ok(!gotReleaseCallback, "gotReleaseCallback was " + gotReleaseCallback);
gotReleaseCallback = true;
if (gotAll()) {
SimpleTest.finish();
}
};
cam.release(onSuccess, onError);
}, // test()
start: function start() {
function onSuccess(cam) {
Camera._cameraObj = cam;
Camera.viewfinder.mozSrcObject = cam;
Camera.viewfinder.play();
var onPreviewStateChange = function(e) {
if (e.newState === 'started') {
cam.removeEventListener('previewstatechange', onPreviewStateChange);
Camera.test(cam);
}
}; // onPreviewStateChange
cam.addEventListener('previewstatechange', onPreviewStateChange);
}; // onSuccess()
navigator.mozCameras.getCamera(whichCamera, config, onSuccess, onError);
}, // start()
}
SimpleTest.waitForExplicitFinish();
window.addEventListener('beforeunload', function() {
Camera.release();
});
Camera.start();
</script>
</body>
</html>

View File

@ -1,223 +0,0 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test for bug 975472</title>
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="../camera_common.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<video id="viewfinder" width="200" height="200" autoplay></video>
<img src="#" alt="This image is going to load" id="testimage"/>
<script class="testbody" type="text/javascript;version=1.7">
var whichCamera = navigator.mozCameras.getListOfCameras()[0];
var config = {
mode: 'picture',
recorderProfile: 'cif',
previewSize: {
width: 352,
height: 288
}
};
var options = {
rotation: 0,
position: {
latitude: 43.645687,
longitude: -79.393661
},
dateTime: Date.now()
};
function onError(e) {
ok(false, "Error" + JSON.stringify(e));
}
function next() {
Camera.nextTest();
}
// The array of tests
var tests = [
{
key: "release-after-release",
func: function testAutoFocus(camera) {
function onSuccess(success) {
ok(true, "release() succeeded");
next();
}
function onError(error) {
ok(false, "release() failed with: " + error);
}
camera.release(onSuccess, onError);
}
},
{
key: "set-picture-size-after-release",
func: function testSetPictureSize(camera) {
camera.setPictureSize({ width: 0, height: 0 });
next();
}
},
{
key: "set-thumbnail-size-after-release",
func: function testSetThumbnailSize(camera) {
camera.setThumbnailSize({ width: 0, height: 0 });
next();
}
},
{
key: "get-sensor-angle-after-release",
func: function testGetSensorAngle(camera) {
ok(camera.sensorAngle == 0, "camera.sensorAngle = " + camera.sensorAngle);
next();
}
},
{
key: "resume-preview-after-release",
func: function testResumePreview(camera) {
camera.resumePreview();
next();
}
},
{
key: "auto-focus-after-release",
func: function testAutoFocus(camera) {
function onSuccess(success) {
ok(false, "autoFocus() succeeded incorrectly");
}
function onError(error) {
ok(error === "HardwareClosed", "autoFocus() failed with: " + error);
next();
}
camera.autoFocus(onSuccess, onError);
}
},
{
key: "take-picture-after-release",
func: function testTakePicture(camera) {
function onSuccess(picture) {
ok(false, "takePicture() succeeded incorrectly");
}
function onError(error) {
ok(error === "HardwareClosed", "takePicture() failed with: " + error);
next();
}
camera.takePicture(null, onSuccess, onError);
}
},
{
key: "start-recording-after-release",
func: function testStartRecording(camera) {
function onSuccess(picture) {
ok(false, "startRecording() process succeeded incorrectly");
}
function onError(error) {
ok(error === "GeneralFailure", "startRecording() failed with: " + error);
next();
}
var recordingOptions = {
profile: 'cif',
rotation: 0
};
camera.startRecording(recordingOptions,
navigator.getDeviceStorage('videos'),
'bug975472.mp4',
onSuccess, onError);
}
},
{
key: "stop-recording-after-release",
func: function testStopRecording(camera) {
camera.stopRecording();
next();
}
},
{
key: "set-configuration-after-release",
func: function testSetConfiguration(camera) {
function onSuccess(picture) {
ok(false, "setConfiguration() process succeeded incorrectly");
}
function onError(error) {
ok(error === "HardwareClosed", "setConfiguration() failed with: " + error);
next();
}
camera.setConfiguration(config, onSuccess, onError);
}
},
];
var testGenerator = function() {
for (var i = 0; i < tests.length; ++i ) {
yield tests[i];
}
}();
var Camera = {
cameraObj: null,
_otherPictureSize: null,
get viewfinder() {
return document.getElementById('viewfinder');
},
onCameraReady: function () {
Camera.nextTest = function() {
try {
var t = testGenerator.next();
info("test: " + t.key);
t.func(Camera.cameraObj);
} catch(e) {
if (e instanceof StopIteration) {
CameraTest.end();
} else {
throw e;
}
}
};
// Release the camera hardware, and call all of the asynchronous methods
// to make sure they properly handle being in this state.
Camera.cameraObj.release();
next();
},
release: function release() {
cameraObj = null;
},
start: function run_test() {
function onSuccess(camera, config) {
Camera.cameraObj = camera;
Camera.viewfinder.mozSrcObject = camera;
Camera.viewfinder.play();
ok(camera.capabilities.pictureSizes.length > 0,
"capabilities.pictureSizes.length = " +
camera.capabilities.pictureSizes.length);
Camera._otherPictureSize = camera.capabilities.pictureSizes.slice(-1)[0];
camera.setPictureSize(camera.capabilities.pictureSizes[0]);
options.pictureSize = Camera._otherPictureSize;
options.fileFormat = camera.capabilities.fileFormats[0];
info("getCamera callback, setting pictureSize = " + options.pictureSize.toSource());
Camera.cameraObj.onPreviewStateChange = function(state) {
if (state === 'started') {
info("viewfinder is ready and playing");
Camera.cameraObj.onPreviewStateChange = null;
Camera.onCameraReady();
}
};
};
navigator.mozCameras.getCamera(whichCamera, config, onSuccess, onError);
}
}
SimpleTest.waitForExplicitFinish();
window.addEventListener('beforeunload', function() {
Camera.viewfinder.mozSrcObject = null;
Camera.cameraObj.release();
Camera.cameraObj = null;
});
Camera.start();
</script>
</body>
</html>

View File

@ -1,242 +0,0 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test for mozCameras.getCamera() with separate .setConfiguration() call</title>
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<video id="viewfinder" width="200" height="200" autoplay></video>
<img src="#" alt="This image is going to load" id="testimage"/>
<script class="testbody" type="text/javascript;version=1.7">
var whichCamera = navigator.mozCameras.getListOfCameras()[0];
var options = {
mode: 'picture',
recorderProfile: 'cif',
previewSize: {
width: 352,
height: 288
}
};
var config = {
dateTime: Date.now() / 1000,
pictureSize: null,
fileFormat: 'jpeg',
rotation: 90
};
function onError(e) {
ok(false, "Error" + JSON.stringify(e));
}
var capabilities = [ 'previewSizes', 'pictureSizes', 'fileFormats', 'maxFocusAreas', 'minExposureCompensation',
'maxExposureCompensation', 'stepExposureCompensation', 'maxMeteringAreas', 'videoSizes',
'recorderProfiles', 'zoomRatios', 'isoModes'];
var Camera = {
cameraObj: null,
_recording: false,
_currentTest: null,
_autoFocusSupported: 0,
_manuallyFocused: false,
_flashmodes: null,
_pictureSizes: null,
_previewSizes: null,
_whiteBalanceModes: null,
_zoomRatios: null,
_sceneModes: null,
_focusModes: null,
_zoomRatios: null,
_testsCompleted: 0,
_shutter: 0,
_config: {
dateTime: Date.now() / 1000,
pictureSize: null,
fileFormat: 'jpeg',
rotation: 90
},
_tests: null,
get viewfinder() {
return document.getElementById('viewfinder');
},
setFlashMode: function camera_setFlash(mode) {
this.cameraObj.flashMode = mode;
},
setFocus: function camera_setfocus(mode) {
this.cameraObj.focus = mode;
},
setZoom: function camera_setZoom(zoom) {
this.cameraObj.zoom = zoom;
},
getZoom: function camera_getZoom() {
return this.cameraObj.zoom;
},
getFileFormats: function camera_formats() {
this._fileFormats = this.cameraObj.capabilities.fileFormats;
},
getFlashModes: function camera_getFlash() {
this._flashmodes = this.cameraObj.capabilities.flashModes;
},
getFocusModes: function camera_getFocus() {
this._focusModes = this.cameraObj.capabilities.focusModes;
},
getSceneModes: function camera_getScene() {
this._sceneModes = this.cameraObj.capabilities.sceneModes;
},
getZoomRatios: function camera_getZoom() {
this._zoomRatios = this.cameraObj.capabilities.zoomRatios;
},
getWhiteBalance: function camera_white() {
this._whitebalanceModes = this.cameraObj.capabilities.whiteBalanceModes;
},
getPictureSizes: function camera_sizes() {
this._pictureSizes = this.cameraObj.capabilities.pictureSizes;
},
getPreviewSizes: function camera_preview() {
this._previewSizes = this.cameraObj.capabilities.previewSizes;
},
getZoomRatios: function camera_preview() {
this._zoomRatios = this.cameraObj.capabilities.zoomRatios;
},
takePictureSuccess: function taken_foto(blob) {
var img = new Image();
var test = this._currentTest;
img.onload = function Imgsize() {
ok(this.width == test.pictureSize.width, "The image taken has the width " +
this.width + " pictureSize width = " + test.pictureSize.width);
ok(this.height == test.pictureSize.height, "The image taken has the height " +
this.height + " picturesize height = " + test.pictureSize.height);
Camera._testsCompleted++;
if(Camera._testsCompleted == Camera._tests.length) {
ok(true, "test finishing");
SimpleTest.finish();
} else {
Camera.runTests();
}
}
ok(blob.size > 100 , "Blob Size Gathered = " + blob.size);
ok("image/" + test.fileFormat == blob.type, "Blob Type = " + blob.type);
img.src = window.URL.createObjectURL(blob);
},
shutter: function onShutter () {
Camera._shutter++;
ok(Camera._shutter == (Camera._testsCompleted + 1), "on Shutter has been called " +
Camera._shutter + " times");
},
onReady: function onReady() {
var camcap = Camera.cameraObj.capabilities;
var tests = {};
for (var prop in capabilities) {
prop = capabilities[prop];
ok(camcap[prop] || isFinite(camcap[prop]) || camcap[prop] == null, "Camera Capability: " +
prop + " is exposed, value = " + JSON.stringify(camcap[prop]));
}
ok(camcap.maxMeteringAreas >= 0, "maxMeteringAreas = " + camcap.maxMeteringAreas);
ok(camcap.maxFocusAreas >= 0, "maxFocusAreas = " + camcap.maxFocusAreas);
for (var prop in camcap) {
if(camcap[prop] && camcap[prop].length > 1) {
tests[prop] = camcap[prop];
}
}
Camera.getPictureSizes();
Camera.getPreviewSizes();
Camera.getFileFormats();
Camera.getFocusModes();
Camera.getZoomRatios();
ok(Camera._previewSizes.length > 0, "previewSizes length = " + Camera._previewSizes.length);
ok(Camera._pictureSizes.length > 0, "picturesizes length = " + Camera._pictureSizes.length);
ok(Camera._fileFormats.length > 0, "file formats length = " + Camera._fileFormats.length);
ok(camcap.isoModes.length == 0, "ISO modes length = " + camcap.isoModes.length);
// The emulator doesn't support zoom, so these parameters will be very constrained
// For more ambitious tests, see test_camera_fake_parameters.html
ok(Camera._zoomRatios.length == 1, "zoom ratios length = " + Camera._zoomRatios.length);
ok(Camera.cameraObj.zoom == 1.0, "zoom = " + Camera.cameraObj.zoom);
// Test snapping to supported values
Camera.cameraObj.zoom = 0.9;
ok(Camera.cameraObj.zoom == 1.0, "zoom (lower limit) = " + Camera.cameraObj.zoom);
Camera.cameraObj.zoom = 1.1;
ok(Camera.cameraObj.zoom == 1.0, "zoom (upper limit) = " + Camera.cameraObj.zoom);
// Check image quality handling
Camera.cameraObj.pictureQuality = 0.0;
ok(Camera.cameraObj.pictureQuality == 0.0, "picture quality = " + Camera.cameraObj.pictureQuality);
Camera.cameraObj.pictureQuality = -0.1;
ok(Camera.cameraObj.pictureQuality == 0.0, "picture quality (minimum limit) = " + Camera.cameraObj.pictureQuality);
Camera.cameraObj.pictureQuality = -Math.pow(2, 80);
ok(Camera.cameraObj.pictureQuality == 0.0, "picture quality (BIG negative) = " + Camera.cameraObj.pictureQuality);
Camera.cameraObj.pictureQuality = 1.0;
ok(Camera.cameraObj.pictureQuality == 1.0, "picture quality = " + Camera.cameraObj.pictureQuality);
Camera.cameraObj.pictureQuality = 1.1;
ok(Camera.cameraObj.pictureQuality == 1.0, "picture quality (maximum limit) = " + Camera.cameraObj.pictureQuality);
Camera.cameraObj.pictureQuality = Math.pow(2, 80);
ok(Camera.cameraObj.pictureQuality == 1.0, "picture quality (BIG positive) = " + Camera.cameraObj.pictureQuality);
Camera._tests = new Array();
for (var i in Camera._pictureSizes) {
for (var l in Camera._fileFormats) {
var config = {
pictureSize: Camera._pictureSizes[i],
fileFormat: Camera._fileFormats[l]
};
Camera._tests.push(config);
}
}
Camera.runTests();
},
runTests: function run_tests() {
var test = this._tests[this._testsCompleted];
this._currentTest = test;
Camera.setFlashMode(test.flashMode);
config.fileFormat = test.fileFormat;
config.pictureSize = test.pictureSize;
ok(true, "testing picture size " + JSON.stringify(config.pictureSize));
Camera.cameraObj.takePicture(config, this.takePictureSuccess.bind(this), onError);
},
onConfigChange: function onConfigChange(config) {
ok(config.mode === options.mode, "configuration mode = " + config.mode);
ok(config.recorderProfile === options.recorderProfile, "recorder profile = " + config.recorderProfile);
ok(config.previewSize.width === options.previewSize.width &&
config.previewSize.height === options.previewSize.height,
"preview size (w x h) = " + config.previewSize.width + " x " + config.previewSize.height);
},
setUp: function setup_tests() {
function onSuccess(camera) {
Camera.cameraObj = camera;
Camera.viewfinder.mozSrcObject = camera;
Camera.viewfinder.play();
Camera.cameraObj.onPreviewStateChange = function(state) {
if (state === 'started') {
ok(true, "viewfinder is ready and playing");
Camera.cameraObj.onPreviewStateChange = null;
Camera.onReady();
}
};
SimpleTest.expectAssertions(0);
ok(true, "Camera Control object has been successfully initialized");
Camera.cameraObj.setConfiguration(options, Camera.onConfigChange, onError);
Camera.cameraObj.onShutter = Camera.shutter;
};
navigator.mozCameras.getCamera(whichCamera, null, onSuccess, onError);
}
}
SimpleTest.waitForExplicitFinish();
window.addEventListener('beforeunload', function() {
Camera.viewfinder.mozSrcObject = null;
Camera.cameraObj.release();
Camera.cameraObj = null;
});
Camera.setUp();
</script>
</body>
</html>

View File

@ -1,200 +0,0 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test for mozCameras.getCamera() using an initial configuration</title>
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<video id="viewfinder" width="200" height="200" autoplay></video>
<img src="#" alt="This image is going to load" id="testimage"/>
<script class="testbody" type="text/javascript;version=1.7">
var whichCamera = navigator.mozCameras.getListOfCameras()[0];
var options = {
mode: 'picture',
recorderProfile: 'cif',
previewSize: {
width: 352,
height: 288
}
};
var config = {
dateTime: Date.now() / 1000,
pictureSize: null,
fileFormat: 'jpeg',
rotation: 90
};
function onError(e) {
ok(false, "Error" + JSON.stringify(e));
}
var capabilities = [ 'previewSizes', 'pictureSizes', 'fileFormats', 'maxFocusAreas', 'minExposureCompensation',
'maxExposureCompensation', 'stepExposureCompensation', 'maxMeteringAreas', 'videoSizes',
'recorderProfiles'];
var Camera = {
cameraObj: null,
_recording: false,
_currentTest: null,
_autoFocusSupported: 0,
_manuallyFocused: false,
_flashmodes: null,
_pictureSizes: null,
_previewSizes: null,
_whiteBalanceModes: null,
_zoomRatios: null,
_sceneModes: null,
_focusModes: null,
_testsCompleted: 0,
_shutter: 0,
_config: {
dateTime: Date.now() / 1000,
pictureSize: null,
fileFormat: 'jpeg',
rotation: 90
},
_tests: null,
get viewfinder() {
return document.getElementById('viewfinder');
},
setFlashMode: function camera_setFlash(mode) {
this.cameraObj.flashMode = mode;
},
setFocus: function camera_setfocus(mode) {
this.cameraObj.focus = mode;
},
getFileFormats: function camera_formats() {
this._fileFormats = this.cameraObj.capabilities.fileFormats;
},
getFlashModes: function camera_getFlash() {
this._flashmodes = this.cameraObj.capabilities.flashModes;
},
getFocusModes: function camera_getFocus() {
this._focusModes = this.cameraObj.capabilities.focusModes;
},
getSceneModes: function camera_getScene() {
this._sceneModes = this.cameraObj.capabilities.sceneModes;
},
getZoomRatios: function camera_getZoom() {
this._zoomRatios = this.cameraObj.capabilities.zoomRatios;
},
getWhiteBalance: function camera_white() {
this._whitebalanceModes = this.cameraObj.capabilities.whiteBalanceModes;
},
getPictureSizes: function camera_sizes() {
this._pictureSizes = this.cameraObj.capabilities.pictureSizes;
},
getPreviewSizes: function camera_preview() {
this._previewSizes = this.cameraObj.capabilities.previewSizes;
},
takePictureSuccess: function taken_foto(blob) {
var img = new Image();
var test = this._currentTest;
img.onload = function Imgsize() {
ok(this.width == test.pictureSize.width, "The image taken has the width " +
this.width + " pictureSize width = " + test.pictureSize.width);
ok(this.height == test.pictureSize.height, "The image taken has the height " +
this.height + " picturesize height = " + test.pictureSize.height);
Camera._testsCompleted++;
if(Camera._testsCompleted == Camera._tests.length) {
ok(true, "test finishing");
SimpleTest.finish();
} else {
Camera.runTests();
}
}
ok(blob.size > 100 , "Blob Size Gathered = " + blob.size);
ok("image/" + test.fileFormat == blob.type, "Blob Type = " + blob.type);
img.src = window.URL.createObjectURL(blob);
},
shutter: function onShutter () {
Camera._shutter++;
ok(Camera._shutter == (Camera._testsCompleted + 1), "on Shutter has been called " +
Camera._shutter + " times");
},
onReady: function onReady() {
var camcap = Camera.cameraObj.capabilities;
var tests = {};
for (var prop in capabilities) {
prop = capabilities[prop];
ok(camcap[prop] || isFinite(camcap[prop]) || camcap[prop] == null, "Camera Capability: " +
prop + " is exposed, value = " + JSON.stringify(camcap[prop]));
}
for (var prop in camcap) {
if(camcap[prop] && camcap[prop].length > 1) {
tests[prop] = camcap[prop];
}
}
Camera.getPictureSizes();
Camera.getPreviewSizes();
Camera.getFileFormats();
Camera.getFocusModes();
ok(Camera._previewSizes.length > 0, "previewSizes length = " + Camera._previewSizes.length);
ok(Camera._pictureSizes.length > 0, "picturesizes length = " + Camera._pictureSizes.length);
ok(Camera._fileFormats.length > 0, "file formats length = " + Camera._fileFormats.length);
Camera._tests = new Array();
for (var i in Camera._pictureSizes) {
for (var l in Camera._fileFormats) {
var config = {
pictureSize: Camera._pictureSizes[i],
fileFormat: Camera._fileFormats[l]
};
Camera._tests.push(config);
}
}
Camera.runTests();
},
runTests: function run_tests() {
var test = this._tests[this._testsCompleted];
this._currentTest = test;
Camera.setFlashMode(test.flashMode);
config.fileFormat = test.fileFormat;
config.pictureSize = test.pictureSize;
ok(true, "testing picture size " + JSON.stringify(config.pictureSize));
Camera.cameraObj.takePicture(config, this.takePictureSuccess.bind(this), onError);
},
setUp: function setup_tests() {
function onSuccess(camera, config) {
ok(true, "Camera Control object has been successfully initialized");
ok(config.mode === options.mode, "configuration mode = " + config.mode);
ok(config.recorderProfile === options.recorderProfile, "recorder profile = " + config.recorderProfile);
ok(config.previewSize.width === options.previewSize.width &&
config.previewSize.height === options.previewSize.height,
"preview size (w x h) = " + config.previewSize.width + " x " + config.previewSize.height);
Camera.cameraObj = camera;
Camera.viewfinder.mozSrcObject = camera;
Camera.viewfinder.play();
Camera.cameraObj.onPreviewStateChange = function(state) {
if (state === 'started') {
ok(true, "viewfinder is ready and playing");
Camera.cameraObj.onPreviewStateChange = null;
Camera.onReady();
}
};
SimpleTest.expectAssertions(0);
Camera.cameraObj.onShutter = Camera.shutter;
};
navigator.mozCameras.getCamera(whichCamera, options, onSuccess, onError);
}
}
SimpleTest.waitForExplicitFinish();
window.addEventListener('beforeunload', function() {
Camera.viewfinder.mozSrcObject = null;
Camera.cameraObj.release();
Camera.cameraObj = null;
});
Camera.setUp();
</script>
</body>
</html>

View File

@ -1,76 +0,0 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test for multiple calls to mozCameras.getCamera()</title>
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<video id="viewfinder" width="200" height="200" autoplay></video>
<img src="#" alt="This image is going to load" id="testimage"/>
<script class="testbody" type="text/javascript;version=1.7">
var whichCamera = navigator.mozCameras.getListOfCameras()[0];
var options = {
mode: 'picture',
recorderProfile: 'cif',
previewSize: {
width: 352,
height: 288
}
};
function onError(e) {
ok(false, "Error" + JSON.stringify(e));
}
var Camera = {
cameraObj: null,
get viewfinder() {
return document.getElementById('viewfinder');
},
onReady: function take_two() {
function onSuccess(camera, config) {
ok(false, "Unexpectedly got second camera instance: " + config.toSource);
}
function onFailure(error) {
ok(true, "Correctly failed to get camera again");
SimpleTest.finish();
}
navigator.mozCameras.getCamera(whichCamera, options, onSuccess, onFailure);
},
release: function release() {
cameraObj = null;
},
start: function run_test() {
function onSuccess(camera, config) {
Camera.cameraObj = camera;
Camera.viewfinder.mozSrcObject = camera;
Camera.viewfinder.play();
Camera.cameraObj.onPreviewStateChange = function(state) {
if (state === 'started') {
ok(true, "viewfinder is ready and playing");
Camera.cameraObj.onPreviewStateChange = null;
Camera.onReady();
}
};
};
navigator.mozCameras.getCamera(whichCamera, options, onSuccess, onError);
}
}
SimpleTest.waitForExplicitFinish();
window.addEventListener('beforeunload', function() {
Camera.viewfinder.mozSrcObject = null;
Camera.cameraObj.release();
Camera.cameraObj = null;
});
Camera.start();
</script>
</body>
</html>

View File

@ -1,131 +0,0 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=965421
-->
<head>
<title>Bug 965421 - Test camera hardware API for Auto focus moving Callback</title>
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="../camera_common.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=965421">Mozilla Bug 965421</a>
<video id="viewfinder" width = "200" height = "200" autoplay></video>
<img src="#" alt="This image is going to load" id="testimage"/>
<script class="testbody" type="text/javascript;version=1.7">
var whichCamera = navigator.mozCameras.getListOfCameras()[0];
var initialConfig = {
mode: 'picture',
recorderProfile: 'cif',
previewSize: {
width: 352,
height: 288
}
};
const PREF_AUTOFOCUSCALLBACK_ENABLED = "camera.control.autofocus_moving_callback.enabled";
var cameraObj;
var oldPref;
// Shorthand functions
function end() {
function reallyEnd() {
CameraTest.end();
}
if (oldPref) {
SpecialPowers.pushPrefEnv(
{'set': [[PREF_AUTOFOCUSCALLBACK_ENABLED, oldPref]]}, reallyEnd);
} else {
SpecialPowers.pushPrefEnv(
{'clear': [[PREF_AUTOFOCUSCALLBACK_ENABLED]]}, reallyEnd);
}
}
function next() {
CameraTest.next();
}
var tests = [
{
key: "autofocus-moving-true",
func: function testAutoFocusMovingIsTrue(camera) {
camera.onAutoFocusMoving = function(aIsMoving) {
ok(aIsMoving == true,"onAutoFocusMoving callback received true correctly");
camera.focusMode = 'auto';
next();
}
camera.focusMode = 'continuous-picture';
}
},
{
key: "autofocus-moving-false",
func: function testAutoFocusMovingIsFalse(camera) {
camera.onAutoFocusMoving = function(aIsMoving) {
ok(aIsMoving == false, "onAutoFocusMoving callback received false correctly");
camera.focusMode = 'auto';
end();
}
camera.focusMode = 'continuous-video';
}
},
];
var testGenerator = function() {
for (var i = 0; i < tests.length; ++i ) {
yield tests[i];
}
}();
window.addEventListener('beforeunload', function() {
document.getElementById('viewfinder').mozSrcObject = null;
cameraObj.release();
cameraObj = null;
});
// Must call CameraTest.begin() before any other async methods.
CameraTest.begin("hardware", function(test) {
// If the pref doesn't exist, this get will fail; catch it and continue.
try {
oldPref = SpecialPowers.getBoolPref(PREF_AUTOFOCUSCALLBACK_ENABLED);
} catch(e) { }
SpecialPowers.pushPrefEnv({'set': [[PREF_AUTOFOCUSCALLBACK_ENABLED, true]]}, function() {
var enabled;
try {
enabled = SpecialPowers.getBoolPref(PREF_AUTOFOCUSCALLBACK_ENABLED);
} catch(e) { }
ok(enabled, PREF_AUTOFOCUSCALLBACK_ENABLED + " is " + enabled);
function onSuccess(camera, config) {
document.getElementById('viewfinder').mozSrcObject = camera;
cameraObj = camera;
CameraTest.next = function() {
try {
var t = testGenerator.next();
test.set(t.key, t.func.bind(undefined, camera));
} catch(e) {
if (e instanceof StopIteration) {
end();
} else {
throw e;
}
}
};
next();
}
function onError(error) {
ok(false, "getCamera() failed with: " + error);
end();
}
navigator.mozCameras.getCamera(whichCamera, initialConfig, onSuccess, onError);
})
});
</script>
</body>
</html>

View File

@ -1,320 +0,0 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=965420
-->
<head>
<title>Bug 965420 - Test camera hardware API for face detection</title>
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="../camera_common.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=965420">Mozilla Bug 965420</a>
<video id="viewfinder" width = "200" height = "200" autoplay></video>
<img src="#" alt="This image is going to load" id="testimage"/>
<script class="testbody" type="text/javascript;version=1.7">
var whichCamera = navigator.mozCameras.getListOfCameras()[0];
var initialConfig = {
mode: 'picture',
recorderProfile: 'cif',
previewSize: {
width: 352,
height: 288
}
};
const PREF_FACEDETECTION_ENABLED = "camera.control.face_detection.enabled";
var cameraObj;
var oldPref;
// Shorthand functions
function end() {
function reallyEnd() {
CameraTest.end();
}
if (oldPref) {
SpecialPowers.pushPrefEnv(
{'set': [[PREF_FACEDETECTION_ENABLED, oldPref]]}, reallyEnd);
} else {
SpecialPowers.pushPrefEnv(
{'clear': [[PREF_FACEDETECTION_ENABLED]]}, reallyEnd);
}
}
function next() {
CameraTest.next();
}
function compareFaces(aFaces, expected)
{
ok(aFaces, "have detected faces object");
ok(aFaces.length == expected.faces.length,
"expected=" + expected.faces.length + ", got=" + aFaces.length);
aFaces.forEach(function (face, index) {
let result = compareFace(face, expected.faces[index]);
ok(result === "ok", "face check: " + result);
if (result !== "ok") {
return false;
}
});
return true;
}
function compareFace(aFace, expected)
{
if (aFace.id != expected.id) {
return "expected face.id=" + expected.id + ", got=" + aFace.id;
}
if (aFace.score != expected.score) {
return "expected face.score=" + expected.score + ", got=" + aFace.score;
}
if (!aFace.bounds) {
return "face.bounds is missing";
}
if (aFace.bounds.left != expected.bounds.left ||
aFace.bounds.top != expected.bounds.top ||
aFace.bounds.right != expected.bounds.right ||
aFace.bounds.bottom != expected.bounds.bottom) {
return "expected face.bounds=" + expected.bounds.toSource() +
", got=({left:" + aFace.bounds.left + ", top:" + aFace.bounds.top + ", right:" + aFace.bounds.right + ", bottom:" + aFace.bounds.bottom + "})";
}
if (aFace.leftEye && !expected.leftEye) {
return "expected null face.leftEye, got=({x:" + aFace.leftEye.x + ", y:" + aFace.leftEye.y + "})";
}
if (!aFace.leftEye && expected.leftEye) {
return "expected face.leftEye=" + expected.leftEye.toSource() + ", got null leftEye";
}
if (aFace.leftEye && expected.leftEye &&
(aFace.leftEye.x != expected.leftEye.x || aFace.leftEye.y != expected.leftEye.y)) {
return "expected face.leftEye=" + expected.leftEye.toSource() +
", got=({x:" + aFace.leftEye.x + ", y:" + aFace.leftEye.y + "})";
}
if (aFace.rightEye && !expected.rightEye) {
return "expected null face.rightEye, got=({x:" + aFace.rightEye.x + ", y:" + aFace.rightEye.y + "})";
}
if (!aFace.rightEye && expected.rightEye) {
return "expected face.rightEye=" + expected.rightEye.toSource() + ", got null rightEye";
}
if (aFace.rightEye && expected.rightEye &&
(aFace.rightEye.x != expected.rightEye.x || aFace.rightEye.y != expected.rightEye.y)) {
return "expected face.rightEye=" + expected.rightEye.toSource() +
", got=({x:" + aFace.rightEye.x + ", y:" + aFace.rightEye.y + "})";
}
if (aFace.mouth && !expected.mouth) {
return "expected null face.mouth, got=({x:" + aFace.mouth.x + ", y:" + aFace.mouth.y + "})";
}
if (!aFace.mouth && expected.mouth) {
return "expected face.mouth=" + expected.mouth.toSource() + ", got null mouth";
}
if (aFace.mouth && expected.mouth &&
(aFace.mouth.x != expected.mouth.x || aFace.mouth.y != expected.mouth.y)) {
return "expected face.mouth=" + expected.mouth.toSource() +
", got=({x:" + aFace.mouth.x + ", y:" + aFace.mouth.y + "})";
}
return "ok";
}
var tests = [
{
key: "face-detection-detected-one-face",
func: function testFaceDetectionFoundOneFace(camera) {
var expected = {
faces: [ {
id: 1,
score: 2,
bounds: {
left: 3,
top: 4,
right: 5,
bottom: 6
},
leftEye: {
x: 7,
y: 8
},
rightEye: {
x: 9,
y: 10
},
mouth: {
x: 11,
y: 12
}
} ]
};
camera.onFacesDetected = function(aFaces) {
ok(compareFaces(aFaces, expected),
"onFaceDetected received the detected faces correctly");
camera.stopFaceDetection();
next();
}
camera.startFaceDetection();
}
},
{
key: "face-detection-detected-two-faces",
func: function testFaceDetectionFoundTwoFace(camera) {
var expected = {
faces: [ {
id: 1,
score: 2,
bounds: {
left: 3,
top: 4,
right: 5,
bottom: 6
},
leftEye: {
x: 7,
y: 8
},
rightEye: {
x: 9,
y: 10
},
mouth: {
x: 11,
y: 12
}
},
{
id: 13,
score: 14,
bounds: {
left: 15,
top: 16,
right: 17,
bottom: 18
},
leftEye: {
x: 19,
y: 20
},
rightEye: {
x: 21,
y: 22
},
mouth: {
x: 23,
y: 24
}
} ]
};
camera.onFacesDetected = function(aFaces) {
ok(compareFaces(aFaces, expected),
"onFaceDetected received the detected faces correctly");
camera.stopFaceDetection();
next();
}
camera.startFaceDetection();
}
},
{
key: "face-detection-detected-one-face-no-features",
func: function (camera) {
var expected = {
faces: [ {
id: 1,
score: 100,
bounds: {
left: 3,
top: 4,
right: 5,
bottom: 6
},
leftEye: null,
rightEye: null,
mouth: null
} ]
};
camera.onFacesDetected = function(aFaces) {
ok(compareFaces(aFaces, expected),
"onFaceDetected received the detected faces correctly");
camera.stopFaceDetection();
next();
}
camera.startFaceDetection();
}
},
{
key: "face-detection-no-faces-detected",
func: function (camera) {
var expected = {
faces: []
};
camera.onFacesDetected = function(aFaces) {
ok(compareFaces(aFaces, expected),
"onFaceDetected received the detected faces correctly");
camera.stopFaceDetection();
next();
}
camera.startFaceDetection();
}
},
];
var testGenerator = function() {
for (var i = 0; i < tests.length; ++i ) {
yield tests[i];
}
}();
window.addEventListener('beforeunload', function() {
document.getElementById('viewfinder').mozSrcObject = null;
if (cameraObj) {
cameraObj.release();
cameraObj = null;
}
});
// Must call CameraTest.begin() before any other async methods.
CameraTest.begin("hardware", function(test) {
// If the pref doesn't exist, this get will fail; catch it and continue.
try {
oldPref = SpecialPowers.getBoolPref(PREF_FACEDETECTION_ENABLED);
} catch(e) { }
SpecialPowers.pushPrefEnv({'set': [[PREF_FACEDETECTION_ENABLED, true]]}, function() {
var enabled;
try {
enabled = SpecialPowers.getBoolPref(PREF_FACEDETECTION_ENABLED);
} catch(e) { }
ok(enabled, PREF_FACEDETECTION_ENABLED + " is " + enabled);
function onSuccess(camera, config) {
document.getElementById('viewfinder').mozSrcObject = camera;
cameraObj = camera;
CameraTest.next = function() {
try {
var t = testGenerator.next();
test.set(t.key, t.func.bind(undefined, camera));
} catch(e) {
if (e instanceof StopIteration) {
end();
} else {
throw e;
}
}
};
next();
}
function onError(error) {
ok(false, "getCamera() failed with: " + error);
end();
}
navigator.mozCameras.getCamera(whichCamera, initialConfig, onSuccess, onError);
})
});
</script>
</body>
</html>

View File

@ -1,145 +0,0 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=940424
-->
<head>
<title>Bug 940424 - Test camera hardware API failure handling</title>
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="../camera_common.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=940424">Mozilla Bug 940424</a>
<video id="viewfinder" width = "200" height = "200" autoplay></video>
<img src="#" alt="This image is going to load" id="testimage"/>
<script class="testbody" type="text/javascript;version=1.7">
var whichCamera = navigator.mozCameras.getListOfCameras()[0];
var initialConfig = {
mode: 'picture',
recorderProfile: 'cif',
previewSize: {
width: 352,
height: 288
}
};
var cameraObj;
// Shorthand functions
function end() {
CameraTest.end();
}
function next() {
CameraTest.next();
}
// The array of tests
var tests = [
{
key: "auto-focus-failure",
func: function testAutoFocusApiFailure(camera) {
function onSuccess(success) {
ok(false, "autoFocus() succeeded incorrectly");
end();
}
function onError(error) {
ok(true, "autoFocus() failed correctly with: " + error);
next();
}
camera.autoFocus(onSuccess, onError);
}
},
{
key: "auto-focus-process-failure",
func: function testAutoFocusProcessFailure(camera) {
function onSuccess(success) {
if (success) {
ok(false, "autoFocus() process succeeded incorrectly");
end();
} else {
ok(true, "autoFocus() process failed correctly");
next();
}
}
function onError(error) {
ok(false, "autoFocus() process failed incorrectly with: " + error);
end();
}
camera.autoFocus(onSuccess, onError);
}
},
{
key: "take-picture-failure",
func: function testTakePictureApiFailure(camera) {
function onSuccess(picture) {
ok(false, "takePicture() succeeded incorrectly");
end();
}
function onError(error) {
ok(true, "takePicture() failed correctly with: " + error);
next();
}
camera.takePicture(null, onSuccess, onError);
}
},
{
key: "take-picture-process-failure",
func: function testTakePictureProcessFailure(camera) {
function onSuccess(picture) {
ok(false, "takePicture() process succeeded incorrectly");
end();
}
function onError(error) {
ok(true, "takePicture() process failed correctly with: " + error);
next();
}
camera.takePicture(null, onSuccess, onError);
}
},
];
var testGenerator = function() {
for (var i = 0; i < tests.length; ++i ) {
yield tests[i];
}
}();
window.addEventListener('beforeunload', function() {
document.getElementById('viewfinder').mozSrcObject = null;
cameraObj.release();
cameraObj = null;
});
CameraTest.begin("hardware", function(test) {
function onSuccess(camera, config) {
document.getElementById('viewfinder').mozSrcObject = camera;
cameraObj = camera;
CameraTest.next = function() {
try {
var t = testGenerator.next();
test.set(t.key, t.func.bind(undefined, camera));
} catch(e) {
if (e instanceof StopIteration) {
end();
} else {
throw e;
}
}
};
next();
}
function onError(error) {
ok(false, "getCamera() failed with: " + error);
end();
}
navigator.mozCameras.getCamera(whichCamera, initialConfig, onSuccess, onError);
});
</script>
</body>
</html>

View File

@ -1,149 +0,0 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=940424
-->
<head>
<title>Bug 940424 - Test camera hardware init failure handling</title>
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="../camera_common.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=940424">Mozilla Bug 940424</a>
<video id="viewfinder" width="200" height="200" autoplay></video>
<img src="#" alt="This image is going to load" id="testimage"/>
<script class="testbody" type="text/javascript;version=1.7">
SimpleTest.waitForExplicitFinish();
var whichCamera = navigator.mozCameras.getListOfCameras()[0];
var initialConfig = {
mode: 'picture',
recorderProfile: 'cif',
previewSize: {
width: 352,
height: 288
}
};
var tests = [
{
name: "init-failure",
key: "init-failure",
func: function testInitFailure(test) {
function onSuccess(camera, config) {
ok(false, "onSuccess called incorrectly");
camera.release();
test.next();
}
function onError(error) {
ok(true, "onError called correctly on init failure");
test.next();
}
info("Running test: init-failure");
navigator.mozCameras.getCamera(whichCamera, initialConfig, onSuccess, onError);
}
},
/* This test case (init-success) *must* follow the preceeding test case
(init-failure) in order for the desired condition to be verified */
{
name: "init-success",
key: "",
func: function(test) {
function onSuccess(camera, config) {
ok(true, "onSuccess called correctly");
camera.release();
test.next();
}
function onError(error) {
ok(false, "onError called incorrectly: " + error);
test.next();
}
info("Running test: init-success");
navigator.mozCameras.getCamera(whichCamera, initialConfig, onSuccess, onError)
}
},
/* Test for bug 1099390 to make sure events related to the underlying
platform failing are generated and handled properly. */
{
name: "post-init-system-failure",
key: "post-init-system-failure",
func: function(test) {
var gotReleaseCallback = false;
var gotCloseCallback = false;
function gotAll() {
var all = gotReleaseCallback && gotCloseCallback;
if (all) {
info("Got all expected notifications");
}
return all;
}
function onSuccess(camera, config) {
camera.onClosed = function(reason) {
camera.onClosed = null;
ok(reason === "SystemFailure", "reason is: " + reason);
ok(!gotCloseCallback, "gotCloseCallback was " + gotCloseCallback);
gotCloseCallback = true;
if (gotAll()) {
test.next();
}
camera.release(
function success() {
ok(true, "Got release() success callback");
ok(!gotReleaseCallback, "gotReleaseCallback was " + gotReleaseCallback);
gotReleaseCallback = true;
if (gotAll()) {
test.next();
}
},
function error(e) {
ok(false, "Unexpected release() onError callback: " + e);
test.next();
}
); // release()
} // onClosed
} // onSuccess
function onError(error) {
ok(false, "onError called incorrectly: " + error);
test.next();
}
info("Running test: post-init-system-failure");
navigator.mozCameras.getCamera(whichCamera, initialConfig, onSuccess, onError);
}
},
];
var testGenerator = function() {
for (var i = 0; i < tests.length; ++i) {
yield tests[i];
}
}();
CameraTest.begin("hardware", function(test) {
CameraTest.next = function() {
try {
var t = testGenerator.next();
test.set(t.key, t.func.bind(undefined, CameraTest));
} catch(e) {
if (e instanceof StopIteration) {
CameraTest.end();
} else {
throw e;
}
}
};
CameraTest.next();
});
</script>
</body>
</html>

View File

@ -1,16 +1,6 @@
[DEFAULT]
support-files = camera_common.js
[callback/test_camera.html]
[callback/test_camera_2.html]
[callback/test_camera_3.html]
[callback/test_camera_hardware_init_failure.html]
[callback/test_camera_hardware_failures.html]
[callback/test_bug975472.html]
[callback/test_camera_hardware_face_detection.html]
[callback/test_camera_hardware_auto_focus_moving_cb.html]
[callback/test_bug1022766.html]
[callback/test_bug1099390.html]
[test_camera.html]
[test_camera_2.html]
[test_camera_3.html]

View File

@ -38,7 +38,7 @@ var Camera = {
callbackTriggered: false,
checkForDone: function test_checkForDone() {
if (Camera.firstCallFailed && Camera.secondCallSucceeded) {
ok(Camera.callbackTriggered, "Async callback triggered");
ok(Camera.callbackTriggered, "Async callback triggered?");
Camera.cameraObj.release();
Camera.cameraObj = null;
CameraTest.end();
@ -50,7 +50,7 @@ var Camera = {
},
failureOne: function test_failureOne(error) {
ok(error.name == "NS_ERROR_IN_PROGRESS", "First call to autoFocus() failed with: "
+ error);
+ error.name);
Camera.firstCallFailed = true;
Camera.checkForDone();
},
@ -62,7 +62,8 @@ var Camera = {
failureTwo: function test_failureTwo(error) {
ok(false, "Second call to autoFocus() failed unexpectedly with: " + error);
},
callback: function test_callback(focused) {
callback: function test_callback(e) {
Camera.cameraObj.removeEventListener('focus', Camera.callback);
Camera.callbackTriggered = true;
},
@ -75,7 +76,7 @@ var Camera = {
// It doesn't matter if the emulator supports focus or not;
// this is just testing the sequencing.
camera.onAutoFocusCompleted = Camera.callback;
camera.addEventListener('focus', Camera.callback);
camera.autoFocus().then(Camera.successOne, Camera.failureOne);
camera.autoFocus().then(Camera.successTwo, Camera.failureTwo);
};

View File

@ -103,7 +103,7 @@ var Camera = {
},
takePictureSuccess: function taken_foto(blob) {
ok(blob.size > 100 , "Blob Size Gathered = " + blob.size);
ok("image/" + test.fileFormat == blob.type, "Blob Type = " + blob.type);
ok("image/" + this._currentTest.fileFormat === blob.type, "Blob Type = " + blob.type);
},
takePictureEvent: function taken_foto_evt(e) {
var blob = e.data;

View File

@ -36,7 +36,7 @@ function end() {
}
function next() {
if (cameraObj) {
cameraObj.release(
cameraObj.release().then(
function success() {
CameraTest.next();
},
@ -345,7 +345,7 @@ var tests = [
run();
});
},
test: function testFakeFocusAreas(cam, cap) {
test: function testFakePictureSizes(cam, cap) {
// validate the capability attribute
ok(cap.pictureSizes.length == 3, "pictureSizes.length = " + cap.pictureSizes.length);
var found = 0;
@ -368,7 +368,7 @@ var tests = [
// settings are applied. Yes--this is an ugly hack.
cam.setConfiguration({ mode: 'video',
recorderProfile: 'weird-unsupported-profile'
}, resolve, resolve);
}).then(resolve, resolve);
});
var sizeGenerator = function() {
var sizes = [ { height: 3264, width: 1836 },

View File

@ -49,7 +49,7 @@ public:
// ChannelMediaResource, it has a "cache" that can store the whole streaming
// data so the |GetBuffered| function can retrieve useful time ranges.
virtual nsresult GetBuffered(dom::TimeRanges* aBuffered) MOZ_OVERRIDE {
return NS_OK;
return NS_ERROR_NOT_IMPLEMENTED;
}
virtual void SetIdle() MOZ_OVERRIDE;

View File

@ -59,7 +59,7 @@ public:
// ChannelMediaResource, it has a "cache" that can store the whole streaming
// data so the |GetBuffered| function can retrieve useful time ranges.
virtual nsresult GetBuffered(mozilla::dom::TimeRanges* aBuffered) MOZ_FINAL MOZ_OVERRIDE {
return NS_OK;
return NS_ERROR_NOT_IMPLEMENTED;
}
virtual void SetIdle() MOZ_OVERRIDE;

View File

@ -2876,6 +2876,7 @@ this.MMI_KS_SC_PUK = "scPuk";
this.MMI_KS_SC_PUK2 = "scPuk2";
this.MMI_KS_SC_IMEI = "scImei";
this.MMI_KS_SC_USSD = "scUssd";
this.MMI_KS_SC_CALL = "scCall";
// MMI error messages key strings.
this.MMI_ERROR_KS_ERROR = "emMmiError";
@ -2905,6 +2906,7 @@ this.MMI_SM_KS_CLIR_DEFAULT_ON_NEXT_CALL_ON = "smClirDefaultOnNextCallOn";
this.MMI_SM_KS_CLIR_DEFAULT_ON_NEXT_CALL_OFF = "smClirDefaultOnNextCallOff";
this.MMI_SM_KS_CLIR_DEFAULT_OFF_NEXT_CALL_ON = "smClirDefaultOffNextCallOn";
this.MMI_SM_KS_CLIR_DEFAULT_OFF_NEXT_CALL_OFF = "smClirDefaultOffNextCallOff";
this.MMI_SM_KS_CALL_CONTROL = "smCallControl";
// MMI Service class
this.MMI_KS_SERVICE_CLASS_VOICE = "serviceClassVoice";

View File

@ -222,10 +222,9 @@ const TELEPHONY_REQUESTS = [
REQUEST_UDUB
];
function TelephonyRequestEntry(request, action, options) {
function TelephonyRequestEntry(request, callback) {
this.request = request;
this.action = action;
this.options = options;
this.callback = callback;
}
function TelephonyRequestQueue(ril) {
@ -274,7 +273,7 @@ TelephonyRequestQueue.prototype = {
_executeEntry: function(entry) {
if (DEBUG) this.debug("execute " + this._getRequestName(entry.request));
entry.action.call(this.ril, entry.options);
entry.callback();
},
_getRequestName: function(request) {
@ -290,7 +289,7 @@ TelephonyRequestQueue.prototype = {
return TELEPHONY_REQUESTS.indexOf(request) !== -1;
},
push: function(request, action, options) {
push: function(request, callback) {
if (!this.isValidRequest(request)) {
if (DEBUG) {
this.debug("Error: " + this._getRequestName(request) +
@ -300,7 +299,7 @@ TelephonyRequestQueue.prototype = {
}
if (DEBUG) this.debug("push " + this._getRequestName(request));
let entry = new TelephonyRequestEntry(request, action, options);
let entry = new TelephonyRequestEntry(request, callback);
let queue = this._getQueue(request);
queue.push(entry);
@ -1545,18 +1544,6 @@ RilObject.prototype = {
Buf.sendParcel();
},
/**
* Get current calls.
*/
getCurrentCalls: function() {
this.telephonyRequestQueue.push(REQUEST_GET_CURRENT_CALLS,
this.sendRilRequestGetCurrentCalls, null);
},
sendRilRequestGetCurrentCalls: function() {
this.context.Buf.simpleRequest(REQUEST_GET_CURRENT_CALLS);
},
/**
* Get the signal strength.
*/
@ -1631,20 +1618,24 @@ RilObject.prototype = {
let isRadioOff = (this.radioState === GECKO_RADIOSTATE_DISABLED);
if (options.isEmergency) {
options.request = RILQUIRKS_REQUEST_USE_DIAL_EMERGENCY_CALL ?
REQUEST_DIAL_EMERGENCY_CALL : REQUEST_DIAL;
if (isRadioOff) {
if (DEBUG) {
this.context.debug("Automatically enable radio for an emergency call.");
}
this.cachedDialRequest = {
callback: this.dialEmergencyNumber.bind(this, options),
callback: this.dialInternal.bind(this, options),
onerror: onerror
};
this.setRadioEnabled({enabled: true});
return;
}
this.dialEmergencyNumber(options);
this.dialInternal(options);
} else {
// Notify error in establishing the call without radio.
if (isRadioOff) {
@ -1658,70 +1649,39 @@ RilObject.prototype = {
return;
}
this.dialNonEmergencyNumber(options);
// Exit emergency callback mode when user dial a non-emergency call.
if (this._isInEmergencyCbMode) {
this.exitEmergencyCbMode();
}
options.request = REQUEST_DIAL;
this.dialInternal(options);
}
},
/**
* Dial a non-emergency number.
*
* @param number
* String containing the number to dial.
* @param clirMode
* Integer for showing/hidding the caller Id to the called party.
* @param uusInfo
* Integer doing something XXX TODO
*/
dialNonEmergencyNumber: function(options) {
// Exit emergency callback mode when user dial a non-emergency call.
if (this._isInEmergencyCbMode) {
this.exitEmergencyCbMode();
}
options.request = REQUEST_DIAL;
this.sendDialRequest(options);
},
/**
* Dial an emergency number.
*
* @param number
* String containing the number to dial.
* @param clirMode
* Integer for showing/hidding the caller Id to the called party.
* @param uusInfo
* Integer doing something XXX TODO
*/
dialEmergencyNumber: function(options) {
options.request = RILQUIRKS_REQUEST_USE_DIAL_EMERGENCY_CALL ?
REQUEST_DIAL_EMERGENCY_CALL : REQUEST_DIAL;
this.sendDialRequest(options);
},
sendDialRequest: function(options) {
dialInternal: function(options) {
// Make a Cdma 3way call.
if (this._isCdma && Object.keys(this.currentCalls).length == 1) {
// Make a Cdma 3way call.
options.featureStr = options.number;
this.sendCdmaFlashCommand(options);
} else {
this.telephonyRequestQueue.push(options.request, this.sendRilRequestDial,
options);
this.cdmaFlash(options);
return;
}
this.telephonyRequestQueue.push(options.request, () => {
let Buf = this.context.Buf;
Buf.newParcel(options.request, options);
Buf.writeString(options.number);
Buf.writeInt32(options.clirMode || 0);
Buf.writeInt32(options.uusInfo || 0);
// TODO Why do we need this extra 0? It was put it in to make this
// match the format of the binary message.
Buf.writeInt32(0);
Buf.sendParcel();
});
},
sendRilRequestDial: function(options) {
let Buf = this.context.Buf;
Buf.newParcel(options.request, options);
Buf.writeString(options.number);
Buf.writeInt32(options.clirMode || 0);
Buf.writeInt32(options.uusInfo || 0);
// TODO Why do we need this extra 0? It was put it in to make this
// match the format of the binary message.
Buf.writeInt32(0);
Buf.sendParcel();
},
sendCdmaFlashCommand: function(options) {
cdmaFlash: function(options) {
let Buf = this.context.Buf;
options.isCdma = true;
options.request = REQUEST_CDMA_FLASH;
@ -1753,58 +1713,43 @@ RilObject.prototype = {
call.hangUpLocal = true;
if (call.state === CALL_STATE_HOLDING) {
this.sendHangUpBackgroundRequest();
this.hangUpBackground(options);
} else {
this.sendHangUpRequest(options);
this.telephonyRequestQueue.push(REQUEST_HANGUP, () => {
let Buf = this.context.Buf;
Buf.newParcel(REQUEST_HANGUP, options);
Buf.writeInt32(1);
Buf.writeInt32(options.callIndex);
Buf.sendParcel();
});
}
},
sendHangUpRequest: function(options) {
this.telephonyRequestQueue.push(REQUEST_HANGUP, this.sendRilRequestHangUp,
options);
hangUpForeground: function(options) {
this.telephonyRequestQueue.push(REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND, () => {
this.context.Buf.simpleRequest(REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND,
options);
});
},
sendRilRequestHangUp: function(options) {
let Buf = this.context.Buf;
Buf.newParcel(REQUEST_HANGUP, options);
Buf.writeInt32(1);
Buf.writeInt32(options.callIndex);
Buf.sendParcel();
hangUpBackground: function(options) {
this.telephonyRequestQueue.push(REQUEST_HANGUP_WAITING_OR_BACKGROUND, () => {
this.context.Buf.simpleRequest(REQUEST_HANGUP_WAITING_OR_BACKGROUND,
options);
});
},
sendHangUpForegroundRequest: function(options) {
this.telephonyRequestQueue.push(REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND,
this.sendRilRequestHangUpForeground,
options);
switchActiveCall: function(options) {
this.telephonyRequestQueue.push(REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE, () => {
this.context.Buf.simpleRequest(REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE,
options);
});
},
sendRilRequestHangUpForeground: function(options) {
this.context.Buf.simpleRequest(REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND,
options);
},
sendHangUpBackgroundRequest: function(options) {
this.telephonyRequestQueue.push(REQUEST_HANGUP_WAITING_OR_BACKGROUND,
this.sendRilRequestHangUpWaiting, options);
},
sendRilRequestHangUpWaiting: function(options) {
this.context.Buf.simpleRequest(REQUEST_HANGUP_WAITING_OR_BACKGROUND,
options);
},
/**
* Mute or unmute the radio.
*
* @param mute
* Boolean to indicate whether to mute or unmute the radio.
*/
setMute: function(options) {
let Buf = this.context.Buf;
Buf.newParcel(REQUEST_SET_MUTE);
Buf.writeInt32(1);
Buf.writeInt32(options.muted ? 1 : 0);
Buf.sendParcel();
udub: function(options) {
this.telephonyRequestQueue.push(REQUEST_UDUB, () => {
this.context.Buf.simpleRequest(REQUEST_UDUB, options);
});
},
/**
@ -1814,40 +1759,28 @@ RilObject.prototype = {
* Call index of the call to answer.
*/
answerCall: function(options) {
// Check for races. Since we dispatched the incoming/waiting call
// notification the incoming/waiting call may have changed. The main
// thread thinks that it is answering the call with the given index,
// so only answer if that is still incoming/waiting.
let call = this.currentCalls[options.callIndex];
if (!call) {
return;
}
// Check for races. Since we dispatched the incoming/waiting call
// notification the incoming/waiting call may have changed. The main
// thread thinks that it is answering the call with the given index,
// so only answer if that is still incoming/waiting.
switch (call.state) {
case CALL_STATE_INCOMING:
this.telephonyRequestQueue.push(REQUEST_ANSWER, this.sendRilRequestAnswer,
null);
this.telephonyRequestQueue.push(REQUEST_ANSWER, () => {
this.context.Buf.simpleRequest(REQUEST_ANSWER);
});
break;
case CALL_STATE_WAITING:
// Answer the waiting (second) call, and hold the first call.
this.sendSwitchWaitingRequest();
this.switchActiveCall(options);
break;
}
},
sendRilRequestAnswer: function() {
this.context.Buf.simpleRequest(REQUEST_ANSWER);
},
sendSwitchWaitingRequest: function() {
this.telephonyRequestQueue.push(REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE,
this.sendRilRequestSwitch, null);
},
sendRilRequestSwitch: function() {
this.context.Buf.simpleRequest(REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE);
},
/**
* Reject an incoming/waiting call.
*
@ -1868,26 +1801,21 @@ RilObject.prototype = {
if (this._isCdma) {
// AT+CHLD=0 means "release held or UDUB."
this.sendHangUpBackgroundRequest();
this.hangUpBackground(options);
return;
}
switch (call.state) {
case CALL_STATE_INCOMING:
this.telephonyRequestQueue.push(REQUEST_UDUB, this.sendRilRequestUdub,
null);
this.udub(options);
break;
case CALL_STATE_WAITING:
// Reject the waiting (second) call, and remain the first call.
this.sendHangUpBackgroundRequest();
this.hangUpBackground(options);
break;
}
},
sendRilRequestUdub: function() {
this.context.Buf.simpleRequest(REQUEST_UDUB);
},
holdCall: function(options) {
let call = this.currentCalls[options.callIndex];
if (!call) {
@ -1900,9 +1828,9 @@ RilObject.prototype = {
let Buf = this.context.Buf;
if (this._isCdma) {
options.featureStr = "";
this.sendCdmaFlashCommand(options);
this.cdmaFlash(options);
} else if (call.state == CALL_STATE_ACTIVE) {
this.sendSwitchWaitingRequest();
this.switchActiveCall(options);
}
},
@ -1918,24 +1846,22 @@ RilObject.prototype = {
let Buf = this.context.Buf;
if (this._isCdma) {
options.featureStr = "";
this.sendCdmaFlashCommand(options);
this.cdmaFlash(options);
} else if (call.state == CALL_STATE_HOLDING) {
this.sendSwitchWaitingRequest();
this.switchActiveCall(options);
}
},
conferenceCall: function(options) {
if (this._isCdma) {
options.featureStr = "";
this.sendCdmaFlashCommand(options);
} else {
this.telephonyRequestQueue.push(REQUEST_CONFERENCE,
this.sendRilRequestConference, options);
this.cdmaFlash(options);
return;
}
},
sendRilRequestConference: function(options) {
this.context.Buf.simpleRequest(REQUEST_CONFERENCE, options);
this.telephonyRequestQueue.push(REQUEST_CONFERENCE, () => {
this.context.Buf.simpleRequest(REQUEST_CONFERENCE, options);
});
},
separateCall: function(options) {
@ -1950,20 +1876,17 @@ RilObject.prototype = {
if (this._isCdma) {
options.featureStr = "";
this.sendCdmaFlashCommand(options);
} else {
this.telephonyRequestQueue.push(REQUEST_SEPARATE_CONNECTION,
this.sendRilRequestSeparateConnection,
options);
this.cdmaFlash(options);
return;
}
},
sendRilRequestSeparateConnection: function(options) {
let Buf = this.context.Buf;
Buf.newParcel(REQUEST_SEPARATE_CONNECTION, options);
Buf.writeInt32(1);
Buf.writeInt32(options.callIndex);
Buf.sendParcel();
this.telephonyRequestQueue.push(REQUEST_SEPARATE_CONNECTION, () => {
let Buf = this.context.Buf;
Buf.newParcel(REQUEST_SEPARATE_CONNECTION, options);
Buf.writeInt32(1);
Buf.writeInt32(options.callIndex);
Buf.sendParcel();
});
},
hangUpConference: function(options) {
@ -1976,31 +1899,56 @@ RilObject.prototype = {
this.sendChromeMessage(options);
return;
}
call.hangUpLocal = true;
this.sendHangUpRequest(1);
options.callIndex = 1;
this.hangUp(options);
return;
}
if (this.currentConferenceState === CALL_STATE_ACTIVE) {
this.hangUpForeground(options);
} else {
if (this.currentConferenceState === CALL_STATE_ACTIVE) {
this.sendHangUpForegroundRequest(options);
} else {
this.sendHangUpBackgroundRequest(options);
}
this.hangUpBackground(options);
}
},
holdConference: function() {
holdConference: function(options) {
if (this._isCdma) {
return;
}
this.sendSwitchWaitingRequest();
this.switchActiveCall(options);
},
resumeConference: function() {
resumeConference: function(options) {
if (this._isCdma) {
return;
}
this.sendSwitchWaitingRequest();
this.switchActiveCall(options);
},
/**
* Get current calls.
*/
getCurrentCalls: function() {
this.telephonyRequestQueue.push(REQUEST_GET_CURRENT_CALLS, () => {
this.context.Buf.simpleRequest(REQUEST_GET_CURRENT_CALLS);
});
},
/**
* Mute or unmute the radio.
*
* @param mute
* Boolean to indicate whether to mute or unmute the radio.
*/
setMute: function(options) {
let Buf = this.context.Buf;
Buf.newParcel(REQUEST_SET_MUTE);
Buf.writeInt32(1);
Buf.writeInt32(options.muted ? 1 : 0);
Buf.sendParcel();
},
/**
@ -2687,10 +2635,6 @@ RilObject.prototype = {
return;
}
this.sendRilRequestSendUSSD(options);
},
sendRilRequestSendUSSD: function(options) {
let Buf = this.context.Buf;
Buf.newParcel(REQUEST_SEND_USSD, options);
Buf.writeString(options.ussd);
@ -3936,6 +3880,8 @@ RilObject.prototype = {
* Helpers for processing call state changes.
*/
_processCalls: function(newCalls, failCause) {
if (DEBUG) this.context.debug("_processCalls: " + JSON.stringify(newCalls));
// Let's get the failCause first if there are removed calls. Otherwise, we
// need to trigger another async request when removing call and it cause
// the order of callDisconnected and conferenceCallStateChanged
@ -5464,7 +5410,7 @@ RilObject.prototype[REQUEST_GET_IMSI] = function REQUEST_GET_IMSI(length, option
this.sendChromeMessage(options);
};
RilObject.prototype[REQUEST_HANGUP] = function REQUEST_HANGUP(length, options) {
options.success = options.rilRequestError === 0;
options.success = (options.rilRequestError === 0);
options.errorMsg = RIL_ERROR_TO_GECKO_ERROR[options.rilRequestError];
this.sendChromeMessage(options);
};
@ -5475,6 +5421,9 @@ RilObject.prototype[REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND] = function REQU
RilObject.prototype[REQUEST_HANGUP].call(this, length, options);
};
RilObject.prototype[REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE] = function REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE(length, options) {
options.success = (options.rilRequestError === 0);
options.errorMsg = RIL_ERROR_TO_GECKO_ERROR[options.rilRequestError];
this.sendChromeMessage(options);
};
RilObject.prototype[REQUEST_CONFERENCE] = function REQUEST_CONFERENCE(length, options) {
options.success = (options.rilRequestError === 0);

View File

@ -122,9 +122,9 @@ add_test(function test_request_exit_emergencyCbMode_when_dial() {
};
// Dial non-emergency call.
context.RIL.dialNonEmergencyNumber({number: "0912345678",
isEmergency: false,
isDialEmergency: false});
context.RIL.dial({number: "0912345678",
isEmergency: false,
isDialEmergency: false});
// Should clear timeout event.
do_check_eq(context.RIL._exitEmergencyCbModeTimeoutID, null);
@ -167,4 +167,3 @@ add_test(function test_request_exit_emergencyCbMode_explicitly() {
run_next_test();
});

View File

@ -515,24 +515,59 @@ TelephonyService.prototype = {
return;
}
let mmi = this._parseMMI(aNumber, this._hasCalls(aClientId));
if (!mmi) {
this._dialCall(aClientId,
{ number: aNumber,
isDialEmergency: aIsDialEmergency }, aCallback);
} else if (this._isTemporaryCLIR(mmi)) {
this._dialCall(aClientId,
{ number: mmi.dialNumber,
clirMode: this._getTemporaryCLIRMode(mmi.procedure),
isDialEmergency: aIsDialEmergency }, aCallback);
} else {
// Reject MMI code from dialEmergency api.
if (aIsDialEmergency) {
aCallback.notifyError(DIAL_ERROR_BAD_NUMBER);
return;
}
if (this._hasCalls(aClientId)) {
// 3GPP TS 22.030 6.5.5
// Handling of supplementary services within a call.
this._dialMMI(aClientId, mmi, aCallback, true);
let mmiCallback = response => {
aCallback.notifyDialMMI(RIL.MMI_KS_SC_CALL);
if (!response.success) {
aCallback.notifyDialMMIError(RIL.MMI_ERROR_KS_ERROR);
} else {
aCallback.notifyDialMMISuccess(RIL.MMI_SM_KS_CALL_CONTROL);
}
};
if (aNumber === "0") {
this._sendToRilWorker(aClientId, "hangUpBackground", null, mmiCallback);
} else if (aNumber === "1") {
this._sendToRilWorker(aClientId, "hangUpForeground", null, mmiCallback);
} else if (aNumber[0] === "1" && aNumber.length === 2) {
this._sendToRilWorker(aClientId, "hangUp",
{ callIndex: parseInt(aNumber[1]) }, mmiCallback);
} else if (aNumber === "2") {
this._sendToRilWorker(aClientId, "switchActiveCall", null, mmiCallback);
} else if (aNumber[0] === "2" && aNumber.length === 2) {
this._sendToRilWorker(aClientId, "separateCall",
{ callIndex: parseInt(aNumber[1]) }, mmiCallback);
} else if (aNumber === "3") {
this._sendToRilWorker(aClientId, "conferenceCall", null, mmiCallback);
} else {
// Entering "Directory Number"
this._dialCall(aClientId,
{ number: aNumber,
isDialEmergency: aIsDialEmergency }, aCallback);
}
} else {
let mmi = this._parseMMI(aNumber);
if (!mmi) {
this._dialCall(aClientId,
{ number: aNumber,
isDialEmergency: aIsDialEmergency }, aCallback);
} else if (this._isTemporaryCLIR(mmi)) {
this._dialCall(aClientId,
{ number: mmi.dialNumber,
clirMode: this._getTemporaryCLIRMode(mmi.procedure),
isDialEmergency: aIsDialEmergency }, aCallback);
} else {
// Reject MMI code from dialEmergency api.
if (aIsDialEmergency) {
aCallback.notifyError(DIAL_ERROR_BAD_NUMBER);
return;
}
this._dialMMI(aClientId, mmi, aCallback, true);
}
}
},
@ -682,7 +717,7 @@ TelephonyService.prototype = {
}
// No additional information
if (response.additionalInformation == undefined) {
if (response.additionalInformation === undefined) {
aCallback.notifyDialMMISuccess(response.statusMessage);
return;
}
@ -791,15 +826,11 @@ TelephonyService.prototype = {
/**
* Helper to parse short string. TS.22.030 Figure 3.5.3.2.
*/
_isShortString: function(aMmiString, hasCalls) {
_isShortString: function(aMmiString) {
if (aMmiString.length > 2) {
return false;
}
if (hasCalls) {
return true;
}
// Input string is
// - emergency number or
// - 2 digits starting with a "1"
@ -814,7 +845,7 @@ TelephonyService.prototype = {
/**
* Helper to parse MMI/USSD string. TS.22.030 Figure 3.5.3.2.
*/
_parseMMI: function(aMmiString, hasCalls) {
_parseMMI: function(aMmiString) {
if (!aMmiString) {
return null;
}
@ -833,8 +864,7 @@ TelephonyService.prototype = {
};
}
if (this._isPoundString(aMmiString) ||
this._isShortString(aMmiString, hasCalls)) {
if (this._isPoundString(aMmiString) || this._isShortString(aMmiString)) {
return {
fullMMI: aMmiString
};

View File

@ -1025,6 +1025,9 @@ let emulator = (function() {
this.gDelay = delay;
this.gWaitForEvent = waitForEvent;
this.gWaitForCallsChangedEvent = waitForCallsChangedEvent;
this.gWaitForNamedStateEvent = waitForNamedStateEvent;
this.gWaitForStateChangeEvent = waitForStateChangeEvent;
this.gCheckInitialState = checkInitialState;
this.gClearCalls = clearCalls;
this.gOutCallStrPool = outCallStrPool;

View File

@ -67,3 +67,6 @@ disabled = Bug 821958
[test_mmi.js]
[test_mmi_change_pin.js]
[test_mmi_call_forwarding.js]
[test_incall_mmi_call_waiting.js]
[test_incall_mmi_call_hold.js]
[test_incall_mmi_conference.js]

View File

@ -0,0 +1,88 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
MARIONETTE_TIMEOUT = 60000;
MARIONETTE_HEAD_JS = 'head.js';
const call1Number = "0900000001";
const call2Number = "0900000002";
const call1Info = gOutCallStrPool(call1Number);
const call2Info = gOutCallStrPool(call2Number);
// call1 is held, call2 is active
function setupTwoCalls() {
let call1;
let call2;
return gDial(call1Number)
.then(call => call1 = call)
.then(() => gRemoteAnswer(call1))
.then(() => gDial(call2Number))
.then(call => call2 = call)
.then(() => gRemoteAnswer(call2))
.then(() => gCheckAll(call2, [call1, call2], "", [],
[call1Info.held, call2Info.active]))
.then(() => [call1, call2]);
}
function testInCallMMI_0() {
log('= testInCallMMI_0 =');
return setupTwoCalls()
.then(calls => [call1, call2] = calls)
// Hangup held call.
.then(() => gSendMMI("0"))
.then(() => gWaitForNamedStateEvent(call1, "disconnected"))
.then(() => gCheckAll(call2, [call2], "", [], [call2Info.active]))
.then(() => gRemoteHangUpCalls([call2]));
}
function testInCallMMI_1() {
log('= testInCallMMI_1 =');
return setupTwoCalls()
.then(calls => [call1, call2] = calls)
// Hangup current call, resume held call.
.then(() => gSendMMI("1"))
.then(() => {
let p1 = gWaitForNamedStateEvent(call1, "connected");
let p2 = gWaitForNamedStateEvent(call2, "disconnected");
return Promise.all([p1, p2]);
})
.then(() => gCheckAll(call1, [call1], "", [], [call1Info.active]))
.then(() => gRemoteHangUpCalls([call1]));
}
function testInCallMMI_2() {
log('= testInCallMMI_2 =');
return setupTwoCalls()
.then(calls => [call1, call2] = calls)
// Hold current call, resume held call.
.then(() => gSendMMI("2"))
.then(() => {
let p1 = gWaitForNamedStateEvent(call1, "connected");
let p2 = gWaitForNamedStateEvent(call2, "held");
return Promise.all([p1, p2]);
})
.then(() => gCheckAll(call1, [call1, call2], "", [],
[call1Info.active, call2Info.held]))
// Hold current call, resume held call.
.then(() => gSendMMI("2"))
.then(() => {
let p1 = gWaitForNamedStateEvent(call1, "held");
let p2 = gWaitForNamedStateEvent(call2, "connected");
return Promise.all([p1, p2]);
})
.then(() => gCheckAll(call2, [call1, call2], "", [],
[call1Info.held, call2Info.active]))
.then(() => gRemoteHangUpCalls([call1, call2]));
}
startTest(function() {
testInCallMMI_0()
.then(() => testInCallMMI_1())
.then(() => testInCallMMI_2())
.catch(error => ok(false, "Promise reject: " + error))
.then(finish);
});

View File

@ -0,0 +1,77 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
MARIONETTE_TIMEOUT = 60000;
MARIONETTE_HEAD_JS = 'head.js';
const outNumber = "0900000001";
const inNumber = "0900000002";
const outInfo = gOutCallStrPool(outNumber);
const inInfo = gInCallStrPool(inNumber);
function setupTwoCalls() {
let outCall;
let inCall;
return gDial(outNumber)
.then(call => outCall = call)
.then(() => gRemoteAnswer(outCall))
.then(() => gRemoteDial(inNumber))
.then(call => inCall = call)
.then(() => gCheckAll(outCall, [outCall, inCall], "", [],
[outInfo.active, inInfo.waiting]))
.then(() => [outCall, inCall]);
}
function testInCallMMI_0() {
log('= testInCallMMI_0 =');
return setupTwoCalls()
.then(calls => [outCall, inCall] = calls)
// Hangup waiting call.
.then(() => gSendMMI("0"))
.then(() => gWaitForNamedStateEvent(inCall, "disconnected"))
.then(() => gCheckAll(outCall, [outCall], "", [], [outInfo.active]))
.then(() => gRemoteHangUpCalls([outCall]));
}
function testInCallMMI_1() {
log('= testInCallMMI_1 =');
return setupTwoCalls()
.then(calls => [outCall, inCall] = calls)
// Hangup current call, accept waiting call.
.then(() => gSendMMI("1"))
.then(() => {
let p1 = gWaitForNamedStateEvent(outCall, "disconnected");
let p2 = gWaitForNamedStateEvent(inCall, "connected");
return Promise.all([p1, p2]);
})
.then(() => gCheckAll(inCall, [inCall], "", [], [inInfo.active]))
.then(() => gRemoteHangUpCalls([inCall]));
}
function testInCallMMI_2() {
log('= testInCallMMI_2 =');
return setupTwoCalls()
.then(calls => [outCall, inCall] = calls)
// Hold current call, accept waiting call.
.then(() => gSendMMI("2"))
.then(() => {
let p1 = gWaitForNamedStateEvent(outCall, "held");
let p2 = gWaitForNamedStateEvent(inCall, "connected");
return Promise.all([p1, p2]);
})
.then(() => gCheckAll(inCall, [outCall, inCall], "", [],
[outInfo.held, inInfo.active]))
.then(() => gRemoteHangUpCalls([outCall, inCall]));
}
startTest(function() {
testInCallMMI_0()
.then(() => testInCallMMI_1())
.then(() => testInCallMMI_2())
.catch(error => ok(false, "Promise reject: " + error))
.then(finish);
});

View File

@ -0,0 +1,90 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
MARIONETTE_TIMEOUT = 60000;
MARIONETTE_HEAD_JS = 'head.js';
const call1Number = "0900000001";
const call2Number = "0900000002";
const call3Number = "0900000003";
const call1Info = gOutCallStrPool(call1Number);
const call2Info = gOutCallStrPool(call2Number);
const call3Info = gOutCallStrPool(call3Number);
function setupTwoCalls() {
let call1;
let call2;
return gDial(call1Number)
.then(call => call1 = call)
.then(() => gRemoteAnswer(call1))
.then(() => gDial(call2Number))
.then(call => call2 = call)
.then(() => gRemoteAnswer(call2))
.then(() => gCheckAll(call2, [call1, call2], "", [],
[call1Info.held, call2Info.active]))
.then(() => [call1, call2]);
}
function testInCallMMI() {
log('= testInCallMMI =');
return setupTwoCalls()
.then(calls => [call1, call2] = calls)
// Conference two calls.
.then(() => log("- Conference two calls."))
.then(() => gSendMMI("3"))
.then(() => gWaitForNamedStateEvent(conference, "connected"))
.then(() => gCheckAll(conference, [], "connected", [call1, call2],
[call1Info.active, call2Info.active]))
// Make third call.
.then(() => log("- Make third call."))
.then(() => gDial(call3Number))
.then(call => call3 = call)
.then(() => gRemoteAnswer(call3))
.then(() => gCheckAll(call3, [call3], "held", [call1, call2],
[call1Info.held, call2Info.held, call3Info.active]))
// Enlarge the conference.
.then(() => log("- Enlarge the conference."))
.then(() => gSendMMI("3"))
.then(() => gWaitForNamedStateEvent(conference, "connected"))
.then(() => gCheckAll(conference, [], "connected", [call1, call2, call3],
[call1Info.active, call2Info.active, call3Info.active]))
// Separate call 2.
.then(() => log("- Separate call 2."))
.then(() => gSendMMI("22"))
.then(() => gWaitForNamedStateEvent(conference, "held"))
.then(() => gCheckAll(call2, [call2], "held", [call1, call3],
[call1Info.held, call2Info.active, call3Info.held]))
// Switch active.
.then(() => log("- Switch active."))
.then(() => gSendMMI("2"))
.then(() => gWaitForNamedStateEvent(conference, "connected"))
.then(() => gCheckAll(conference, [call2], "connected", [call1, call3],
[call1Info.active, call2Info.held, call3Info.active]))
// Release call 2.
.then(() => log("- Release call 2."))
.then(() => gSendMMI("12"))
.then(() => gWaitForNamedStateEvent(call2, "disconnected"))
.then(() => gCheckAll(conference, [], "connected", [call1, call3],
[call1Info.active, call3Info.active]))
// Release call 1.
.then(() => log("- Release call 1."))
.then(() => gSendMMI("11"))
.then(() => gWaitForStateChangeEvent(conference, ""))
.then(() => gCheckAll(call3, [call3], "", [], [call3Info.active]))
.then(() => gRemoteHangUpCalls([call3]));
}
startTest(function() {
testInCallMMI()
.catch(error => ok(false, "Promise reject: " + error))
.then(finish);
});

View File

@ -12,7 +12,7 @@ function run_test() {
}
function parseMMI(mmiString) {
return NS.TelephonyService.prototype._parseMMI(mmiString, false);
return NS.TelephonyService.prototype._parseMMI(mmiString);
}
add_test(function test_parseMMI_empty() {

View File

@ -111,17 +111,6 @@ dictionary CameraStartRecordingOptions
boolean autoEnableLowLightTorch = false;
};
callback CameraSetConfigurationCallback = void (CameraConfiguration configuration);
callback CameraAutoFocusCallback = void (boolean focused);
callback CameraTakePictureCallback = void (Blob picture);
callback CameraStartRecordingCallback = void ();
callback CameraShutterCallback = void ();
callback CameraClosedCallback = void (DOMString reason);
callback CameraReleaseCallback = void ();
callback CameraRecorderStateChange = void (DOMString newState);
callback CameraPreviewStateChange = void (DOMString newState);
callback CameraAutoFocusMovingCallback = void (boolean isMoving);
/*
attributes here affect the preview, any pictures taken, and/or
any video recorded by the camera.
@ -231,26 +220,11 @@ interface CameraControl : MediaStream
[Throws]
attribute DOMString isoMode;
/* the function to call on the camera's shutter event, to trigger
a shutter sound and/or a visual shutter indicator. */
attribute CameraShutterCallback? onShutter;
/* the event dispatched on the camera's shutter event, to trigger
a shutter sound and/or a visual shutter indicator.
contains no event-specific data. */
attribute EventHandler onshutter;
/* the function to call when the camera hardware is closed; this may
be due to a system failure, another process taking over the camera,
or a call to release().
The 'reason' will be one of the following string values:
- SystemFailure : the camera subsystem failed and was closed;
- HardwareReleased : a call to release() was successful;
- NotAvailable : the camera hardware is in use by another process.
*/
attribute CameraClosedCallback? onClosed;
attribute EventHandler onshutter;
/* the event dispatched when the camera hardware is closed; this may
be due to a system failure, another process taking over the camera,
@ -262,12 +236,7 @@ interface CameraControl : MediaStream
- HardwareReleased : a call to release() was successful;
- NotAvailable : the camera hardware is in use by another process.
*/
attribute EventHandler onclose;
/* the function to call when the recorder changes state, either because
the recording process encountered an error, or because one of the
recording limits (see CameraStartRecordingOptions) was reached. */
attribute CameraRecorderStateChange? onRecorderStateChange;
attribute EventHandler onclose;
/* the event dispatched when the recorder changes state, either because
the recording process encountered an error, or because one of the
@ -275,18 +244,14 @@ interface CameraControl : MediaStream
event type is CameraStateChangeEvent where:
'newState' is the new recorder state */
attribute EventHandler onrecorderstatechange;
/* the function to call when the viewfinder stops or starts,
useful for synchronizing other UI elements. */
attribute CameraPreviewStateChange? onPreviewStateChange;
attribute EventHandler onrecorderstatechange;
/* the event dispatched when the viewfinder stops or starts,
useful for synchronizing other UI elements.
event type is CameraStateChangeEvent where:
'newState' is the new preview state */
attribute EventHandler onpreviewstatechange;
attribute EventHandler onpreviewstatechange;
/* the size of the picture to be returned by a call to takePicture();
an object with 'height' and 'width' properties that corresponds to
@ -304,7 +269,7 @@ interface CameraControl : MediaStream
'get' the exact value that was previously 'set'. If this setting is
not supported, it is ignored. */
[Throws]
attribute double pictureQuality;
attribute double pictureQuality;
/* the size of the thumbnail to be included in the picture returned
by a call to takePicture(), assuming the chosen fileFormat supports
@ -326,8 +291,7 @@ interface CameraControl : MediaStream
/* tell the camera to attempt to focus the image */
[Throws]
Promise<boolean> autoFocus(optional CameraAutoFocusCallback onSuccess,
optional CameraErrorCallback onError);
Promise<boolean> autoFocus();
/* the event dispatched whenever the focus state changes due to calling
autoFocus or due to continuous autofocus.
@ -344,45 +308,28 @@ interface CameraControl : MediaStream
'focused' if the focus is now set
'focusing' if the focus is moving
'unfocused' if last attempt to focus failed */
attribute EventHandler onfocus;
/* if continuous autofocus is supported and focusMode is set to enable it,
then this function is called whenever the camera decides to start and
stop moving the focus position; it can be used to update a UI element to
indicate that the camera is still trying to focus, or has finished. Some
platforms do not support this event, in which case the callback is never
invoked. */
[Pref="camera.control.autofocus_moving_callback.enabled"]
attribute CameraAutoFocusMovingCallback? onAutoFocusMoving;
/* this function is called whenever auto focus completes, due to continuous
autofocus or a solicited auto focus. */
attribute CameraAutoFocusCallback? onAutoFocusCompleted;
attribute EventHandler onfocus;
/* capture an image and return it as a blob to the 'onSuccess' callback;
if the camera supports it, this may be invoked while the camera is
already recording video.
invoking this function will stop the preview stream, which must be
manually restarted (e.g. by calling .play() on it). */
manually restarted by calling resumePreview(). */
[Throws]
Promise<Blob> takePicture(optional CameraPictureOptions aOptions,
optional CameraTakePictureCallback onSuccess,
optional CameraErrorCallback onError);
Promise<Blob> takePicture(optional CameraPictureOptions options);
/* the event dispatched when a picture is successfully taken; it is of the
type BlobEvent, where the data attribute contains the picture. */
attribute EventHandler onpicture;
attribute EventHandler onpicture;
/* start recording video; 'aOptions' is a CameraStartRecordingOptions object.
/* start recording video; 'options' is a CameraStartRecordingOptions object.
If the success/error callbacks are not used, one may determine success by
waiting for the recorderstatechange event. */
[Throws]
Promise<void> startRecording(CameraStartRecordingOptions aOptions,
Promise<void> startRecording(CameraStartRecordingOptions options,
DeviceStorage storageArea,
DOMString filename,
optional CameraStartRecordingCallback onSuccess,
optional CameraErrorCallback onError);
DOMString filename);
/* stop precording video. */
[Throws]
@ -397,27 +344,14 @@ interface CameraControl : MediaStream
probably call this whenever the camera is not longer in the foreground
(depending on your usage model).
the callbacks are optional, unless you really need to know when
the hardware is ultimately released.
once this is called, the camera control object is to be considered
defunct; a new instance will need to be created to access the camera. */
[Throws]
Promise<void> release(optional CameraReleaseCallback onSuccess,
optional CameraErrorCallback onError);
Promise<void> release();
/* changes the camera configuration on the fly;
'configuration' is of type CameraConfiguration.
XXXmikeh the 'configuration' argument needs to be optional, else
the WebIDL compiler throws: "WebIDL.WebIDLError: error: Dictionary
argument or union argument containing a dictionary not followed by
a required argument must be optional"
*/
/* changes the camera configuration on the fly. */
[Throws]
Promise<CameraConfiguration> setConfiguration(optional CameraConfiguration configuration,
optional CameraSetConfigurationCallback onSuccess,
optional CameraErrorCallback onError);
Promise<CameraConfiguration> setConfiguration(optional CameraConfiguration configuration);
/* the event dispatched when the camera is successfully configured.
@ -524,12 +458,7 @@ partial interface CameraControl
[Throws, Pref="camera.control.face_detection.enabled"]
void stopFaceDetection();
/* Callback for faces detected in the preview frame. If no faces are
detected, the callback is invoked with an empty sequence. */
[Pref="camera.control.face_detection.enabled"]
attribute CameraFaceDetectionCallback? onFacesDetected;
/* CameraFacesDetectedEvent */
[Pref="camera.control.face_detection.enabled"]
attribute EventHandler onfacesdetected;
attribute EventHandler onfacesdetected;
};

View File

@ -24,11 +24,6 @@ dictionary CameraConfiguration
// CameraControl.capabilities.recorderProfiles
};
callback CameraErrorCallback = void (DOMString error);
callback GetCameraCallback = void (CameraControl camera,
CameraConfiguration configuration);
[Func="nsDOMCameraManager::HasSupport"]
interface CameraManager
{
@ -37,9 +32,7 @@ interface CameraManager
*/
[Throws]
Promise<CameraGetPromiseData> getCamera(DOMString camera,
optional CameraConfiguration initialConfiguration,
optional GetCameraCallback callback,
optional CameraErrorCallback errorCallback);
optional CameraConfiguration initialConfiguration);
/* return an array of camera identifiers, e.g.
[ "front", "back" ]

View File

@ -12,10 +12,12 @@ enum SelectionState {
"selectall",
"collapsetostart",
"collapsetoend",
"blur"
"blur",
"updateposition"
};
dictionary SelectionStateChangedEventInit : EventInit {
boolean visible = true;
DOMString selectedText = "";
DOMRectReadOnly? boundingClientRect = null;
sequence<SelectionState> states = [];
@ -24,6 +26,7 @@ dictionary SelectionStateChangedEventInit : EventInit {
[Constructor(DOMString type, optional SelectionStateChangedEventInit eventInit),
ChromeOnly]
interface SelectionStateChangedEvent : Event {
readonly attribute boolean visible;
readonly attribute DOMString selectedText;
readonly attribute DOMRectReadOnly? boundingClientRect;
[Cached, Pure] readonly attribute sequence<SelectionState> states;

View File

@ -1,247 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set sw=2 ts=8 et tw=80 : */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_layers_InputBlockState_h
#define mozilla_layers_InputBlockState_h
#include "nsTArray.h" // for nsTArray
#include "InputData.h" // for MultiTouchInput
#include "nsAutoPtr.h"
namespace mozilla {
namespace layers {
class AsyncPanZoomController;
class OverscrollHandoffChain;
class CancelableBlockState;
class TouchBlockState;
class WheelBlockState;
/**
* A base class that stores state common to various input blocks.
* Currently, it just stores the overscroll handoff chain.
*/
class InputBlockState
{
public:
static const uint64_t NO_BLOCK_ID = 0;
explicit InputBlockState(const nsRefPtr<AsyncPanZoomController>& aTargetApzc,
bool aTargetConfirmed);
virtual ~InputBlockState()
{}
bool SetConfirmedTargetApzc(const nsRefPtr<AsyncPanZoomController>& aTargetApzc);
const nsRefPtr<AsyncPanZoomController>& GetTargetApzc() const;
const nsRefPtr<const OverscrollHandoffChain>& GetOverscrollHandoffChain() const;
uint64_t GetBlockId() const;
bool IsTargetConfirmed() const;
private:
nsRefPtr<AsyncPanZoomController> mTargetApzc;
nsRefPtr<const OverscrollHandoffChain> mOverscrollHandoffChain;
bool mTargetConfirmed;
const uint64_t mBlockId;
};
/**
* This class represents a set of events that can be cancelled by web content
* via event listeners.
*
* Each cancelable input block can be cancelled by web content, and
* this information is stored in the mPreventDefault flag. Because web
* content runs on the Gecko main thread, we cannot always wait for web content's
* response. Instead, there is a timeout that sets this flag in the case
* where web content doesn't respond in time. The mContentResponded
* and mContentResponseTimerExpired flags indicate which of these scenarios
* occurred.
*/
class CancelableBlockState : public InputBlockState
{
public:
CancelableBlockState(const nsRefPtr<AsyncPanZoomController>& aTargetApzc,
bool aTargetConfirmed);
virtual TouchBlockState *AsTouchBlock() {
return nullptr;
}
/**
* Record whether or not content cancelled this block of events.
* @param aPreventDefault true iff the block is cancelled.
* @return false if this block has already received a response from
* web content, true if not.
*/
bool SetContentResponse(bool aPreventDefault);
/**
* Record that content didn't respond in time.
* @return false if this block already timed out, true if not.
*/
bool TimeoutContentResponse();
/**
* @return true iff web content cancelled this block of events.
*/
bool IsDefaultPrevented() const;
/**
* @return true iff this block has received all the information needed
* to properly dispatch the events in the block.
*/
virtual bool IsReadyForHandling() const;
<<<<<<< HEAD
private:
=======
/**
* Returns whether or not this block has pending events.
*/
virtual bool HasEvents() const = 0;
/**
* Throw away all the events in this input block.
*/
virtual void DropEvents() = 0;
/**
* Process all events given an apzc, leaving ths block depleted.
*/
virtual void HandleEvents(const nsRefPtr<AsyncPanZoomController>& aTarget) = 0;
/**
* Return true if this input block must stay active if it would otherwise
* be removed as the last item in the pending queue.
*/
virtual bool MustStayActive() = 0;
/**
* Return a descriptive name for the block kind.
*/
virtual const char* Type() = 0;
private:
>>>>>>> 80325be... Refactor InputQueue to hold more than touch events. (bug 1013432 part 2, r=kats)
bool mPreventDefault;
bool mContentResponded;
bool mContentResponseTimerExpired;
};
/**
* This class represents a single touch block. A touch block is
* a set of touch events that can be cancelled by web content via
* touch event listeners.
*
* Every touch-start event creates a new touch block. In this case, the
* touch block consists of the touch-start, followed by all touch events
* up to but not including the next touch-start (except in the case where
* a long-tap happens, see below). Note that in particular we cannot know
* when a touch block ends until the next one is started. Most touch
* blocks are created by receipt of a touch-start event.
*
* Every long-tap event also creates a new touch block, since it can also
* be consumed by web content. In this case, when the long-tap event is
* dispatched to web content, a new touch block is started to hold the remaining
* touch events, up to but not including the next touch start (or long-tap).
*
* Additionally, if touch-action is enabled, each touch block should
* have a set of allowed touch behavior flags; one for each touch point.
* This also requires running code on the Gecko main thread, and so may
* be populated with some latency. The mAllowedTouchBehaviorSet and
* mAllowedTouchBehaviors variables track this information.
*/
class TouchBlockState : public CancelableBlockState
{
public:
typedef uint32_t TouchBehaviorFlags;
explicit TouchBlockState(const nsRefPtr<AsyncPanZoomController>& aTargetApzc,
bool aTargetConfirmed);
TouchBlockState *AsTouchBlock() MOZ_OVERRIDE {
return this;
}
/**
* Set the allowed touch behavior flags for this block.
* @return false if this block already has these flags set, true if not.
*/
bool SetAllowedTouchBehaviors(const nsTArray<TouchBehaviorFlags>& aBehaviors);
/**
* Copy the allowed touch behavior flags from another block.
* @return false if this block already has these flags set, true if not.
*/
bool CopyAllowedTouchBehaviorsFrom(const TouchBlockState& aOther);
/**
* @return true iff this block has received all the information needed
* to properly dispatch the events in the block.
*/
bool IsReadyForHandling() const MOZ_OVERRIDE;
/**
* Set a flag that disables setting the single-tap flag on this block.
*/
void DisallowSingleTap();
/**
* Set a flag that indicates that this touch block triggered a single tap event.
* @return true iff DisallowSingleTap was not previously called.
*/
bool SetSingleTapOccurred();
/**
* @return true iff SetSingleTapOccurred was previously called on this block.
*/
bool SingleTapOccurred() const;
/**
* Add a new touch event to the queue of events in this input block.
*/
void AddEvent(const MultiTouchInput& aEvent);
/**
* @return false iff touch-action is enabled and the allowed touch behaviors for
* this touch block do not allow pinch-zooming.
*/
bool TouchActionAllowsPinchZoom() const;
/**
* @return false iff touch-action is enabled and the allowed touch behaviors for
* this touch block do not allow double-tap zooming.
*/
bool TouchActionAllowsDoubleTapZoom() const;
/**
* @return false iff touch-action is enabled and the allowed touch behaviors for
* the first touch point do not allow panning in the specified direction(s).
*/
bool TouchActionAllowsPanningX() const;
bool TouchActionAllowsPanningY() const;
bool TouchActionAllowsPanningXY() const;
bool HasEvents() const MOZ_OVERRIDE;
void DropEvents() MOZ_OVERRIDE;
void HandleEvents(const nsRefPtr<AsyncPanZoomController>& aTarget) MOZ_OVERRIDE;
bool MustStayActive() MOZ_OVERRIDE;
const char* Type() MOZ_OVERRIDE;
private:
/**
* @return the first event in the queue. The event is removed from the queue
* before it is returned.
*/
MultiTouchInput RemoveFirstEvent();
private:
nsTArray<TouchBehaviorFlags> mAllowedTouchBehaviors;
bool mAllowedTouchBehaviorSet;
bool mSingleTapDisallowed;
bool mSingleTapOccurred;
nsTArray<MultiTouchInput> mEvents;
};
} // namespace layers
} // namespace mozilla
#endif // mozilla_layers_InputBlockState_h

View File

@ -28,7 +28,6 @@
#include "mozilla/dom/DOMRect.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/ScrollViewChangeEvent.h"
#include "mozilla/dom/SelectionStateChangedEvent.h"
#include "mozilla/dom/Selection.h"
#include "mozilla/dom/TreeWalker.h"
#include "mozilla/Preferences.h"
@ -88,6 +87,7 @@ SelectionCarets::SelectionCarets(nsIPresShell* aPresShell)
, mAsyncPanZoomEnabled(false)
, mEndCaretVisible(false)
, mStartCaretVisible(false)
, mSelectionVisibleInScrollFrames(true)
, mVisible(false)
{
MOZ_ASSERT(NS_IsMainThread());
@ -488,43 +488,40 @@ SelectionCarets::UpdateSelectionCarets()
}
mPresShell->FlushPendingNotifications(Flush_Layout);
nsRect firstRectInRootFrame =
// If the selection is not visible, we should dispatch a event.
nsIFrame* commonAncestorFrame =
nsLayoutUtils::FindNearestCommonAncestorFrame(startFrame, endFrame);
nsRect selectionRectInRootFrame = GetSelectionBoundingRect(selection);
nsRect selectionRectInCommonAncestorFrame = selectionRectInRootFrame;
nsLayoutUtils::TransformRect(rootFrame, commonAncestorFrame,
selectionRectInCommonAncestorFrame);
mSelectionVisibleInScrollFrames =
nsLayoutUtils::IsRectVisibleInScrollFrames(commonAncestorFrame,
selectionRectInCommonAncestorFrame);
SELECTIONCARETS_LOG("Selection visibility %s",
(mSelectionVisibleInScrollFrames ? "shown" : "hidden"));
nsRect firstRectInStartFrame =
nsCaret::GetGeometryForFrame(startFrame, startOffset, nullptr);
nsRect lastRectInRootFrame =
nsRect lastRectInEndFrame =
nsCaret::GetGeometryForFrame(endFrame, endOffset, nullptr);
// GetGeometryForFrame may return a rect that outside frame's rect. So
// constrain rect inside frame's rect.
firstRectInRootFrame = firstRectInRootFrame.ForceInside(startFrame->GetRectRelativeToSelf());
lastRectInRootFrame = lastRectInRootFrame.ForceInside(endFrame->GetRectRelativeToSelf());
nsRect firstRectInCanvasFrame = firstRectInRootFrame;
nsRect lastRectInCanvasFrame =lastRectInRootFrame;
nsLayoutUtils::TransformRect(startFrame, rootFrame, firstRectInRootFrame);
nsLayoutUtils::TransformRect(endFrame, rootFrame, lastRectInRootFrame);
bool startFrameVisible =
nsLayoutUtils::IsRectVisibleInScrollFrames(startFrame, firstRectInStartFrame);
bool endFrameVisible =
nsLayoutUtils::IsRectVisibleInScrollFrames(endFrame, lastRectInEndFrame);
nsRect firstRectInCanvasFrame = firstRectInStartFrame;
nsRect lastRectInCanvasFrame = lastRectInEndFrame;
nsLayoutUtils::TransformRect(startFrame, canvasFrame, firstRectInCanvasFrame);
nsLayoutUtils::TransformRect(endFrame, canvasFrame, lastRectInCanvasFrame);
firstRectInRootFrame.Inflate(AppUnitsPerCSSPixel(), 0);
lastRectInRootFrame.Inflate(AppUnitsPerCSSPixel(), 0);
nsAutoTArray<nsIFrame*, 16> hitFramesInFirstRect;
nsLayoutUtils::GetFramesForArea(rootFrame,
firstRectInRootFrame,
hitFramesInFirstRect,
nsLayoutUtils::IGNORE_PAINT_SUPPRESSION |
nsLayoutUtils::IGNORE_CROSS_DOC |
nsLayoutUtils::IGNORE_ROOT_SCROLL_FRAME);
nsAutoTArray<nsIFrame*, 16> hitFramesInLastRect;
nsLayoutUtils::GetFramesForArea(rootFrame,
lastRectInRootFrame,
hitFramesInLastRect,
nsLayoutUtils::IGNORE_PAINT_SUPPRESSION |
nsLayoutUtils::IGNORE_CROSS_DOC |
nsLayoutUtils::IGNORE_ROOT_SCROLL_FRAME);
SetStartFrameVisibility(hitFramesInFirstRect.Contains(startFrame));
SetEndFrameVisibility(hitFramesInLastRect.Contains(endFrame));
SetStartFrameVisibility(startFrameVisible);
SetEndFrameVisibility(endFrameVisible);
SetStartFramePos(firstRectInCanvasFrame.BottomLeft());
SetEndFramePos(lastRectInCanvasFrame.BottomRight());
@ -936,7 +933,15 @@ SelectionCarets::GetFrameSelection()
if (!focusFrame) {
return nullptr;
}
return focusFrame->GetFrameSelection();
// Prevent us from touching the nsFrameSelection associated to other
// PresShell.
nsRefPtr<nsFrameSelection> fs = focusFrame->GetFrameSelection();
if (!fs || fs->GetShell() != mPresShell) {
return nullptr;
}
return fs.forget();
} else {
return mPresShell->FrameSelection();
}
@ -970,15 +975,14 @@ GetSelectionStates(int16_t aReason)
return states;
}
static nsRect
GetSelectionBoundingRect(Selection* aSel, nsIPresShell* aShell)
nsRect
SelectionCarets::GetSelectionBoundingRect(Selection* aSel)
{
nsRect res;
// Bounding client rect may be empty after calling GetBoundingClientRect
// when range is collapsed. So we get caret's rect when range is
// collapsed.
if (aSel->IsCollapsed()) {
aShell->FlushPendingNotifications(Flush_Layout);
nsIFrame* frame = nsCaret::GetGeometry(aSel, &res);
if (frame) {
nsIFrame* relativeTo =
@ -1002,27 +1006,37 @@ GetSelectionBoundingRect(Selection* aSel, nsIPresShell* aShell)
return res;
}
static void
DispatchSelectionStateChangedEvent(nsIPresShell* aPresShell,
nsISelection* aSel,
const dom::Sequence<SelectionState>& aStates)
void
SelectionCarets::DispatchSelectionStateChangedEvent(Selection* aSelection,
SelectionState aState)
{
nsIDocument* doc = aPresShell->GetDocument();
dom::Sequence<SelectionState> state;
state.AppendElement(aState);
DispatchSelectionStateChangedEvent(aSelection, state);
}
void
SelectionCarets::DispatchSelectionStateChangedEvent(Selection* aSelection,
const Sequence<SelectionState>& aStates)
{
nsIDocument* doc = mPresShell->GetDocument();
MOZ_ASSERT(doc);
SelectionStateChangedEventInit init;
init.mBubbles = true;
if (aSel) {
Selection* selection = static_cast<Selection*>(aSel);
nsRect rect = GetSelectionBoundingRect(selection, doc->GetShell());
if (aSelection) {
// XXX: Do we need to flush layout?
mPresShell->FlushPendingNotifications(Flush_Layout);
nsRect rect = GetSelectionBoundingRect(aSelection);
nsRefPtr<DOMRect>domRect = new DOMRect(ToSupports(doc));
domRect->SetLayoutRect(rect);
init.mBoundingClientRect = domRect;
init.mVisible = mSelectionVisibleInScrollFrames;
selection->Stringify(init.mSelectedText);
aSelection->Stringify(init.mSelectedText);
}
init.mStates = aStates;
@ -1039,10 +1053,7 @@ void
SelectionCarets::NotifyBlur()
{
SetVisibility(false);
dom::Sequence<SelectionState> state;
state.AppendElement(dom::SelectionState::Blur);
DispatchSelectionStateChangedEvent(mPresShell, nullptr, state);
DispatchSelectionStateChangedEvent(nullptr, SelectionState::Blur);
}
nsresult
@ -1059,7 +1070,8 @@ SelectionCarets::NotifySelectionChanged(nsIDOMDocument* aDoc,
UpdateSelectionCarets();
}
DispatchSelectionStateChangedEvent(mPresShell, aSel, GetSelectionStates(aReason));
DispatchSelectionStateChangedEvent(static_cast<Selection*>(aSel),
GetSelectionStates(aReason));
return NS_OK;
}
@ -1097,10 +1109,16 @@ SelectionCarets::AsyncPanZoomStarted(const mozilla::CSSIntPoint aScrollPos)
void
SelectionCarets::AsyncPanZoomStopped(const mozilla::CSSIntPoint aScrollPos)
{
SELECTIONCARETS_LOG("Update selection carets after APZ is stopped!");
UpdateSelectionCarets();
// SelectionStateChangedEvent should be dispatched before ScrollViewChangeEvent.
DispatchSelectionStateChangedEvent(GetSelection(),
SelectionState::Updateposition);
SELECTIONCARETS_LOG("Dispatch scroll stopped with position x=%d, y=%d",
aScrollPos.x, aScrollPos.y);
DispatchScrollViewChangeEvent(mPresShell, dom::ScrollState::Stopped, aScrollPos);
}
@ -1190,6 +1208,8 @@ SelectionCarets::FireScrollEnd(nsITimer* aTimer, void* aSelectionCarets)
SELECTIONCARETS_LOG_STATIC("Update selection carets!");
self->SetVisibility(true);
self->UpdateSelectionCarets();
self->DispatchSelectionStateChangedEvent(self->GetSelection(),
SelectionState::Updateposition);
}
NS_IMETHODIMP
@ -1198,6 +1218,9 @@ SelectionCarets::Reflow(DOMHighResTimeStamp aStart, DOMHighResTimeStamp aEnd)
if (mVisible) {
SELECTIONCARETS_LOG("Update selection carets after reflow!");
UpdateSelectionCarets();
DispatchSelectionStateChangedEvent(GetSelection(),
SelectionState::Updateposition);
}
return NS_OK;
}

View File

@ -13,6 +13,7 @@
#include "nsWeakPtr.h"
#include "nsWeakReference.h"
#include "Units.h"
#include "mozilla/dom/SelectionStateChangedEvent.h"
#include "mozilla/EventForwards.h"
#include "mozilla/WeakPtr.h"
@ -198,10 +199,15 @@ private:
*/
void SetTilted(bool aIsTilt);
// Utility function
// Utility functions
dom::Selection* GetSelection();
already_AddRefed<nsFrameSelection> GetFrameSelection();
nsIContent* GetFocusedContent();
void DispatchSelectionStateChangedEvent(dom::Selection* aSelection,
dom::SelectionState aState);
void DispatchSelectionStateChangedEvent(dom::Selection* aSelection,
const dom::Sequence<dom::SelectionState>& aStates);
nsRect GetSelectionBoundingRect(dom::Selection* aSel);
/**
* Detecting long tap using timer
@ -242,6 +248,7 @@ private:
bool mEndCaretVisible;
bool mStartCaretVisible;
bool mSelectionVisibleInScrollFrames;
bool mVisible;
// Preference

View File

@ -458,7 +458,7 @@ TouchCaret::IsDisplayable()
return false;
}
if (!IsCaretShowingInScrollFrame()) {
if (!nsLayoutUtils::IsRectVisibleInScrollFrames(focusFrame, focusRect)) {
TOUCHCARET_LOG("Caret does not show in the scrollable frame!");
return false;
}
@ -494,37 +494,6 @@ TouchCaret::GetTouchCaretPosition()
return pos;
}
bool
TouchCaret::IsCaretShowingInScrollFrame()
{
nsRect caretRect;
nsIFrame* caretFrame = GetCaretFocusFrame(&caretRect);
nsIFrame* closestScrollFrame =
nsLayoutUtils::GetClosestFrameOfType(caretFrame, nsGkAtoms::scrollFrame);
while (closestScrollFrame) {
nsIScrollableFrame* sf = do_QueryFrame(closestScrollFrame);
nsRect scrollPortRect = sf->GetScrollPortRect();
nsRect caretRectRelativeToScrollFrame = caretRect;
nsLayoutUtils::TransformRect(caretFrame, closestScrollFrame,
caretRectRelativeToScrollFrame);
// Check whether nsCaret appears in the scroll frame or not.
if (!scrollPortRect.Intersects(caretRectRelativeToScrollFrame)) {
return false;
}
// Get next ancestor scroll frame.
closestScrollFrame =
nsLayoutUtils::GetClosestFrameOfType(closestScrollFrame->GetParent(),
nsGkAtoms::scrollFrame);
}
return true;
}
nsPoint
TouchCaret::ClampPositionToScrollFrame(const nsPoint& aPosition)
{

View File

@ -117,12 +117,6 @@ private:
*/
nsPoint GetTouchCaretPosition();
/**
* Check whether nsCaret shows in the scroll frame boundary, i.e. its rect
* intersects scroll frame's rect.
*/
bool IsCaretShowingInScrollFrame();
/**
* Clamp the position of the touch caret to the scroll frame boundary.
* The returned point is relative to the canvas frame.

View File

@ -2295,8 +2295,8 @@ nsLayoutUtils::GetTransformToAncestorScale(nsIFrame* aFrame)
}
static nsIFrame*
FindNearestCommonAncestorFrame(nsIFrame* aFrame1, nsIFrame* aFrame2)
nsIFrame*
nsLayoutUtils::FindNearestCommonAncestorFrame(nsIFrame* aFrame1, nsIFrame* aFrame2)
{
nsAutoTArray<nsIFrame*,100> ancestors1;
nsAutoTArray<nsIFrame*,100> ancestors2;
@ -2452,6 +2452,34 @@ nsLayoutUtils::ContainsPoint(const nsRect& aRect, const nsPoint& aPoint,
return rect.Contains(aPoint);
}
bool
nsLayoutUtils::IsRectVisibleInScrollFrames(nsIFrame* aFrame, const nsRect& aRect)
{
nsIFrame* closestScrollFrame =
nsLayoutUtils::GetClosestFrameOfType(aFrame, nsGkAtoms::scrollFrame);
while (closestScrollFrame) {
nsIScrollableFrame* sf = do_QueryFrame(closestScrollFrame);
nsRect scrollPortRect = sf->GetScrollPortRect();
nsRect rectRelativeToScrollFrame = aRect;
nsLayoutUtils::TransformRect(aFrame, closestScrollFrame,
rectRelativeToScrollFrame);
// Check whether aRect is visible in the scroll frame or not.
if (!scrollPortRect.Intersects(rectRelativeToScrollFrame)) {
return false;
}
// Get next ancestor scroll frame.
closestScrollFrame =
nsLayoutUtils::GetClosestFrameOfType(closestScrollFrame->GetParent(),
nsGkAtoms::scrollFrame);
}
return true;
}
bool
nsLayoutUtils::GetLayerTransformForFrame(nsIFrame* aFrame,
Matrix4x4* aTransform)

View File

@ -794,6 +794,13 @@ public:
*/
static gfxSize GetTransformToAncestorScale(nsIFrame* aFrame);
/**
* Find the nearest common ancestor frame for aFrame1 and aFrame2. The
* ancestor frame could be cross-doc.
*/
static nsIFrame* FindNearestCommonAncestorFrame(nsIFrame* aFrame1,
nsIFrame* aFrame2);
/**
* Transforms a list of CSSPoints from aFromFrame to aToFrame, taking into
* account all relevant transformations on the frames up to (but excluding)
@ -841,6 +848,13 @@ public:
static bool ContainsPoint(const nsRect& aRect, const nsPoint& aPoint,
nscoord aInflateSize);
/**
* Check whether aRect is visible in the boundary of the scroll frames
* boundary.
*/
static bool IsRectVisibleInScrollFrames(nsIFrame* aFrame,
const nsRect& aRect);
/**
* Return true if a "layer transform" could be computed for aFrame,
* and optionally return the computed transform. The returned

View File

@ -4390,7 +4390,6 @@ pref("beacon.enabled", true);
#endif
// Camera prefs
pref("camera.control.autofocus_moving_callback.enabled", true);
pref("camera.control.face_detection.enabled", true);
// Fetch API.

View File

@ -50,10 +50,13 @@ config = {
"--testvars=%(testvars)s",
"--profile=%(profile)s",
"--symbols-path=%(symbols_path)s",
"--gecko-log=%(gecko_log)s",
"--xml-output=%(xml_output)s",
"--html-output=%(html_output)s",
"--log-raw=%(raw_log_file)s",
"--binary=%(binary)s",
"--address=%(address)s",
"--total-chunks=%(total_chunks)s",
"--this-chunk=%(this_chunk)s",
],
}