Merge m-c to inbound a=merge CLOSED TREE

This commit is contained in:
Wes Kocher 2015-03-23 16:51:22 -07:00
commit 8794504c9f
60 changed files with 1833 additions and 134 deletions

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="ef937d1aca7c4cf89ecb5cc43ae8c21c2000a9db">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="8eac260ee81a8aca05770d18c5736536d44ee7a7"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="efebbafd12fc42ddcd378948b683a51106517660"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="86cd7486d8e50eaac8ef6fe2f51f09d25194577b"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

View File

@ -19,7 +19,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="8eac260ee81a8aca05770d18c5736536d44ee7a7"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="efebbafd12fc42ddcd378948b683a51106517660"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="86cd7486d8e50eaac8ef6fe2f51f09d25194577b"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="93f9ba577f68d772093987c2f1c0a4ae293e1802"/>

View File

@ -17,7 +17,7 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="8eac260ee81a8aca05770d18c5736536d44ee7a7"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="efebbafd12fc42ddcd378948b683a51106517660"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="86cd7486d8e50eaac8ef6fe2f51f09d25194577b"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="b685e3aab4fde7624d78993877a8f7910f2a5f06"/>

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="ef937d1aca7c4cf89ecb5cc43ae8c21c2000a9db">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="8eac260ee81a8aca05770d18c5736536d44ee7a7"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="efebbafd12fc42ddcd378948b683a51106517660"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="86cd7486d8e50eaac8ef6fe2f51f09d25194577b"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="52775e03a2d8532429dff579cb2cd56718e488c3">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="8eac260ee81a8aca05770d18c5736536d44ee7a7"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="efebbafd12fc42ddcd378948b683a51106517660"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="86cd7486d8e50eaac8ef6fe2f51f09d25194577b"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

View File

@ -19,7 +19,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="8eac260ee81a8aca05770d18c5736536d44ee7a7"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="efebbafd12fc42ddcd378948b683a51106517660"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="86cd7486d8e50eaac8ef6fe2f51f09d25194577b"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="93f9ba577f68d772093987c2f1c0a4ae293e1802"/>

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="ef937d1aca7c4cf89ecb5cc43ae8c21c2000a9db">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="8eac260ee81a8aca05770d18c5736536d44ee7a7"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="efebbafd12fc42ddcd378948b683a51106517660"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="86cd7486d8e50eaac8ef6fe2f51f09d25194577b"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

View File

@ -17,7 +17,7 @@
</project>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="8eac260ee81a8aca05770d18c5736536d44ee7a7"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="efebbafd12fc42ddcd378948b683a51106517660"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="86cd7486d8e50eaac8ef6fe2f51f09d25194577b"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="b685e3aab4fde7624d78993877a8f7910f2a5f06"/>

View File

@ -1,9 +1,9 @@
{
"git": {
"git_revision": "8eac260ee81a8aca05770d18c5736536d44ee7a7",
"git_revision": "efebbafd12fc42ddcd378948b683a51106517660",
"remote": "https://git.mozilla.org/releases/gaia.git",
"branch": ""
},
"revision": "1400d176ecef76d06b012fb082c246eb17d1d30f",
"revision": "d8e53e5d917b1ce79aea842e8340ce82799cac3e",
"repo_path": "integration/gaia-central"
}

View File

@ -17,7 +17,7 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="8eac260ee81a8aca05770d18c5736536d44ee7a7"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="efebbafd12fc42ddcd378948b683a51106517660"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="86cd7486d8e50eaac8ef6fe2f51f09d25194577b"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="b685e3aab4fde7624d78993877a8f7910f2a5f06"/>

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="52775e03a2d8532429dff579cb2cd56718e488c3">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="8eac260ee81a8aca05770d18c5736536d44ee7a7"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="efebbafd12fc42ddcd378948b683a51106517660"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="86cd7486d8e50eaac8ef6fe2f51f09d25194577b"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

View File

@ -59,6 +59,7 @@ support-files =
doc_event-listeners-01.html
doc_event-listeners-02.html
doc_event-listeners-03.html
doc_event-listeners-04.html
doc_frame-parameters.html
doc_function-display-name.html
doc_function-search.html
@ -134,6 +135,8 @@ skip-if = e10s || true # bug 1113935
skip-if = e10s || os == "mac" || e10s # Bug 895426
[browser_dbg_break-on-dom-event-02.js]
skip-if = e10s # TODO
[browser_dbg_break-on-dom-event-03.js]
skip-if = e10s # TODO
[browser_dbg_breakpoints-actual-location.js]
[browser_dbg_breakpoints-actual-location2.js]
[browser_dbg_breakpoints-break-on-last-line-of-script-on-reload.js]

View File

@ -0,0 +1,101 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Tests that the break-on-dom-events request works for load event listeners.
*/
const TAB_URL = EXAMPLE_URL + "doc_event-listeners-04.html";
let gClient, gThreadClient;
function test() {
if (!DebuggerServer.initialized) {
DebuggerServer.init();
DebuggerServer.addBrowserActors();
}
let transport = DebuggerServer.connectPipe();
gClient = new DebuggerClient(transport);
gClient.connect((aType, aTraits) => {
is(aType, "browser",
"Root actor should identify itself as a browser.");
addTab(TAB_URL)
.then(() => attachThreadActorForUrl(gClient, TAB_URL))
.then(aThreadClient => gThreadClient = aThreadClient)
.then(pauseDebuggee)
.then(testBreakOnLoad)
.then(closeConnection)
.then(finish)
.then(null, aError => {
ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
});
});
}
function pauseDebuggee() {
let deferred = promise.defer();
gClient.addOneTimeListener("paused", (aEvent, aPacket) => {
is(aPacket.type, "paused",
"We should now be paused.");
is(aPacket.why.type, "debuggerStatement",
"The debugger statement was hit.");
gThreadClient.resume(deferred.resolve);
});
// Spin the event loop before causing the debuggee to pause, to allow
// this function to return first.
executeSoon(() => triggerButtonClick());
return deferred.promise;
}
// Test pause on a load event.
function testBreakOnLoad() {
let deferred = promise.defer();
// Test calling pauseOnDOMEvents from a running state.
gThreadClient.pauseOnDOMEvents(["load"], (aPacket) => {
is(aPacket.error, undefined,
"The pause-on-load request completed successfully.");
let handlers = ["loadHandler"];
gClient.addListener("paused", function tester(aEvent, aPacket) {
is(aPacket.why.type, "pauseOnDOMEvents",
"A hidden breakpoint was hit.");
is(aPacket.frame.where.line, 15, "Found the load event listener.");
gClient.removeListener("paused", tester);
deferred.resolve();
gThreadClient.resume(() => triggerButtonClick(handlers.slice(-1)));
});
getTabActorForUrl(gClient, TAB_URL).then(aGrip => {
gClient.attachTab(aGrip.actor, (aResponse, aTabClient) => {
aTabClient.reload();
});
});
});
return deferred.promise;
}
function triggerButtonClick() {
let button = content.document.querySelector("button");
EventUtils.sendMouseEvent({ type: "click" }, button);
}
function closeConnection() {
let deferred = promise.defer();
gClient.close(deferred.resolve);
return deferred.promise;
}
registerCleanupFunction(function() {
gClient = null;
gThreadClient = null;
});

View File

@ -89,11 +89,11 @@ function testSetBreakpoint() {
let sourceForm = getSourceForm(gSources, JS_URL);
let source = gDebugger.gThreadClient.source(sourceForm);
source.setBreakpoint({ line: 3, column: 61 }, aResponse => {
source.setBreakpoint({ line: 3, column: 18 }, aResponse => {
ok(!aResponse.error,
"Should be able to set a breakpoint in a js file.");
ok(!aResponse.actualLocation,
"Should be able to set a breakpoint on line 3 and column 61.");
"Should be able to set a breakpoint on line 3 and column 18.");
deferred.resolve();
});

View File

@ -0,0 +1,23 @@
<!-- Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ -->
<!doctype html>
<html>
<head>
<meta charset="utf-8"/>
<title>Debugger test page</title>
</head>
<body>
<button>Click me!</button>
<script type="text/javascript">
window.addEventListener("load", function onload() {
var button = document.querySelector("button");
button.onclick = function () {
debugger;
};
});
</script>
</body>
</html>

View File

@ -189,13 +189,17 @@ function Tooltip(doc, options) {
// Used for namedTimeouts in the mouseover handling
this.uid = "tooltip-" + Date.now();
// Emit show/hide events
for (let event of POPUP_EVENTS) {
this["_onPopup" + event] = ((e) => {
return () => this.emit(e);
})(event);
this.panel.addEventListener("popup" + event,
this["_onPopup" + event], false);
// Emit show/hide events when the panel does.
for (let eventName of POPUP_EVENTS) {
this["_onPopup" + eventName] = (name => {
return e => {
if (e.target === this.panel) {
this.emit(name);
}
};
})(eventName);
this.panel.addEventListener("popup" + eventName,
this["_onPopup" + eventName], false);
}
// Listen to keypress events to close the tooltip if configured to do so
@ -303,9 +307,9 @@ Tooltip.prototype = {
destroy: function () {
this.hide();
for (let event of POPUP_EVENTS) {
this.panel.removeEventListener("popup" + event,
this["_onPopup" + event], false);
for (let eventName of POPUP_EVENTS) {
this.panel.removeEventListener("popup" + eventName,
this["_onPopup" + eventName], false);
}
let win = this.doc.querySelector("window");

View File

@ -159,6 +159,18 @@ DOMInterfaces = {
'nativeType': 'mozilla::dom::bluetooth::BluetoothGatt',
},
'BluetoothGattCharacteristic': {
'nativeType': 'mozilla::dom::bluetooth::BluetoothGattCharacteristic',
},
'BluetoothGattDescriptor': {
'nativeType': 'mozilla::dom::bluetooth::BluetoothGattDescriptor',
},
'BluetoothGattService': {
'nativeType': 'mozilla::dom::bluetooth::BluetoothGattService',
},
'BluetoothManager': {
'nativeType': 'mozilla::dom::bluetooth::BluetoothManager',
},

View File

@ -285,6 +285,16 @@ enum BluetoothSspVariant {
struct BluetoothUuid {
uint8_t mUuid[16];
bool operator==(const BluetoothUuid& aOther) const
{
for (uint8_t i = 0; i < sizeof(mUuid); i++) {
if (mUuid[i] != aOther.mUuid[i]) {
return false;
}
}
return true;
}
};
struct BluetoothServiceRecord {
@ -547,11 +557,21 @@ struct BluetoothGattAdvData {
struct BluetoothGattId {
BluetoothUuid mUuid;
uint8_t mInstanceId;
bool operator==(const BluetoothGattId& aOther) const
{
return mUuid == aOther.mUuid && mInstanceId == aOther.mInstanceId;
}
};
struct BluetoothGattServiceId {
BluetoothGattId mId;
uint8_t mIsPrimary;
bool operator==(const BluetoothGattServiceId& aOther) const
{
return mId == aOther.mId && mIsPrimary == aOther.mIsPrimary;
}
};
struct BluetoothGattReadParam {

View File

@ -4,10 +4,10 @@
* 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 "BluetoothReplyRunnable.h"
#include "BluetoothService.h"
#include "BluetoothUtils.h"
#include "mozilla/dom/bluetooth/BluetoothCommon.h"
#include "mozilla/dom/bluetooth/BluetoothGatt.h"
#include "mozilla/dom/bluetooth/BluetoothTypes.h"
#include "mozilla/dom/BluetoothGattBinding.h"
@ -20,14 +20,9 @@ using namespace mozilla::dom;
USING_BLUETOOTH_NAMESPACE
NS_IMPL_CYCLE_COLLECTION_CLASS(BluetoothGatt)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(BluetoothGatt,
DOMEventTargetHelper)
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(BluetoothGatt,
DOMEventTargetHelper)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_INHERITED(BluetoothGatt,
DOMEventTargetHelper,
mServices)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(BluetoothGatt)
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
@ -42,6 +37,7 @@ BluetoothGatt::BluetoothGatt(nsPIDOMWindow* aWindow,
, mClientIf(0)
, mConnectionState(BluetoothConnectionState::Disconnected)
, mDeviceAddr(aDeviceAddr)
, mDiscoveringServices(false)
{
MOZ_ASSERT(aWindow);
MOZ_ASSERT(!mDeviceAddr.IsEmpty());
@ -216,6 +212,36 @@ BluetoothGatt::ReadRemoteRssi(ErrorResult& aRv)
return promise.forget();
}
already_AddRefed<Promise>
BluetoothGatt::DiscoverServices(ErrorResult& aRv)
{
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetParentObject());
if (!global) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
nsRefPtr<Promise> promise = Promise::Create(global, aRv);
NS_ENSURE_TRUE(!aRv.Failed(), nullptr);
BT_ENSURE_TRUE_REJECT(
mConnectionState == BluetoothConnectionState::Connected &&
!mDiscoveringServices,
NS_ERROR_DOM_INVALID_STATE_ERR);
BluetoothService* bs = BluetoothService::Get();
BT_ENSURE_TRUE_REJECT(bs, NS_ERROR_NOT_AVAILABLE);
mDiscoveringServices = true;
nsRefPtr<BluetoothReplyRunnable> result =
new BluetoothVoidReplyRunnable(nullptr /* DOMRequest */,
promise,
NS_LITERAL_STRING("DiscoverGattServices"));
bs->DiscoverGattServicesInternal(mAppUuid, result);
return promise.forget();
}
void
BluetoothGatt::UpdateConnectionState(BluetoothConnectionState aState)
{
@ -235,6 +261,22 @@ BluetoothGatt::UpdateConnectionState(BluetoothConnectionState aState)
DispatchTrustedEvent(event);
}
void
BluetoothGatt::HandleServicesDiscovered(const BluetoothValue& aValue)
{
MOZ_ASSERT(aValue.type() == BluetoothValue::TArrayOfBluetoothGattServiceId);
const InfallibleTArray<BluetoothGattServiceId>& serviceIds =
aValue.get_ArrayOfBluetoothGattServiceId();
for (uint32_t i = 0; i < serviceIds.Length(); i++) {
mServices.AppendElement(new BluetoothGattService(
GetParentObject(), mAppUuid, serviceIds[i]));
}
BluetoothGattBinding::ClearCachedServicesValue(this);
}
void
BluetoothGatt::Notify(const BluetoothSignal& aData)
{
@ -253,6 +295,18 @@ BluetoothGatt::Notify(const BluetoothSignal& aData)
v.get_bool() ? BluetoothConnectionState::Connected
: BluetoothConnectionState::Disconnected;
UpdateConnectionState(state);
} else if (aData.name().EqualsLiteral("ServicesDiscovered")) {
HandleServicesDiscovered(v);
} else if (aData.name().EqualsLiteral("DiscoverCompleted")) {
MOZ_ASSERT(v.type() == BluetoothValue::Tbool);
bool isDiscoverSuccess = v.get_bool();
if (!isDiscoverSuccess) { // Clean all discovered attributes if failed
mServices.Clear();
BluetoothGattBinding::ClearCachedServicesValue(this);
}
mDiscoveringServices = false;
} else {
BT_WARNING("Not handling GATT signal: %s",
NS_ConvertUTF16toUTF8(aData.name()).get());

View File

@ -11,6 +11,7 @@
#include "mozilla/DOMEventTargetHelper.h"
#include "mozilla/dom/BluetoothGattBinding.h"
#include "mozilla/dom/bluetooth/BluetoothCommon.h"
#include "mozilla/dom/bluetooth/BluetoothGattService.h"
#include "nsCOMPtr.h"
namespace mozilla {
@ -41,6 +42,11 @@ public:
return mConnectionState;
}
void GetServices(nsTArray<nsRefPtr<BluetoothGattService>>& aServices) const
{
aServices = mServices;
}
/****************************************************************************
* Event Handlers
***************************************************************************/
@ -51,6 +57,7 @@ public:
***************************************************************************/
already_AddRefed<Promise> Connect(ErrorResult& aRv);
already_AddRefed<Promise> Disconnect(ErrorResult& aRv);
already_AddRefed<Promise> DiscoverServices(ErrorResult& aRv);
already_AddRefed<Promise> ReadRemoteRssi(ErrorResult& aRv);
/****************************************************************************
@ -87,6 +94,15 @@ private:
*/
void GenerateUuid(nsAString &aUuidString);
/**
* Add newly discovered GATT services into mServices and update the cache
* value of mServices.
*
* @param aValue [in] BluetoothValue which contains an array of
* BluetoothGattServiceId of all discovered services.
*/
void HandleServicesDiscovered(const BluetoothValue& aValue);
/****************************************************************************
* Variables
***************************************************************************/
@ -110,6 +126,16 @@ private:
* Address of the remote device.
*/
nsString mDeviceAddr;
/**
* Array of discovered services from the remote GATT server.
*/
nsTArray<nsRefPtr<BluetoothGattService>> mServices;
/**
* Indicate whether there is ongoing discoverServices request or not.
*/
bool mDiscoveringServices;
};
END_BLUETOOTH_NAMESPACE

View File

@ -0,0 +1,97 @@
/* -*- 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 "BluetoothService.h"
#include "BluetoothUtils.h"
#include "mozilla/dom/BluetoothGattCharacteristicBinding.h"
#include "mozilla/dom/bluetooth/BluetoothCommon.h"
#include "mozilla/dom/bluetooth/BluetoothGattCharacteristic.h"
#include "mozilla/dom/bluetooth/BluetoothGattDescriptor.h"
#include "mozilla/dom/bluetooth/BluetoothGattService.h"
#include "mozilla/dom/bluetooth/BluetoothTypes.h"
using namespace mozilla;
using namespace mozilla::dom;
USING_BLUETOOTH_NAMESPACE
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(
BluetoothGattCharacteristic, mOwner, mService, mDescriptors)
NS_IMPL_CYCLE_COLLECTING_ADDREF(BluetoothGattCharacteristic)
NS_IMPL_CYCLE_COLLECTING_RELEASE(BluetoothGattCharacteristic)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(BluetoothGattCharacteristic)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
BluetoothGattCharacteristic::BluetoothGattCharacteristic(
nsPIDOMWindow* aOwner,
BluetoothGattService* aService,
const BluetoothGattId& aCharId)
: mOwner(aOwner)
, mService(aService)
, mCharId(aCharId)
{
MOZ_ASSERT(aOwner);
MOZ_ASSERT(mService);
BluetoothService* bs = BluetoothService::Get();
NS_ENSURE_TRUE_VOID(bs);
// Generate bluetooth signal path and a string representation to provide uuid
// of this characteristic to applications
nsString path;
GeneratePathFromGattId(mCharId, path, mUuidStr);
bs->RegisterBluetoothSignalHandler(path, this);
}
BluetoothGattCharacteristic::~BluetoothGattCharacteristic()
{
BluetoothService* bs = BluetoothService::Get();
NS_ENSURE_TRUE_VOID(bs);
nsString path;
GeneratePathFromGattId(mCharId, path);
bs->UnregisterBluetoothSignalHandler(path, this);
}
void
BluetoothGattCharacteristic::HandleDescriptorsDiscovered(
const BluetoothValue& aValue)
{
MOZ_ASSERT(aValue.type() == BluetoothValue::TArrayOfBluetoothGattId);
const InfallibleTArray<BluetoothGattId>& descriptorIds =
aValue.get_ArrayOfBluetoothGattId();
for (uint32_t i = 0; i < descriptorIds.Length(); i++) {
mDescriptors.AppendElement(new BluetoothGattDescriptor(
GetParentObject(), this, descriptorIds[i]));
}
BluetoothGattCharacteristicBinding::ClearCachedDescriptorsValue(this);
}
void
BluetoothGattCharacteristic::Notify(const BluetoothSignal& aData)
{
BT_LOGD("[D] %s", NS_ConvertUTF16toUTF8(aData.name()).get());
BluetoothValue v = aData.value();
if (aData.name().EqualsLiteral("DescriptorsDiscovered")) {
HandleDescriptorsDiscovered(v);
} else {
BT_WARNING("Not handling GATT Characteristic signal: %s",
NS_ConvertUTF16toUTF8(aData.name()).get());
}
}
JSObject*
BluetoothGattCharacteristic::WrapObject(JSContext* aContext,
JS::Handle<JSObject*> aGivenProto)
{
return BluetoothGattCharacteristicBinding::Wrap(aContext, this, aGivenProto);
}

View File

@ -0,0 +1,120 @@
/* -*- 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_bluetoothgattcharacteristic_h__
#define mozilla_dom_bluetooth_bluetoothgattcharacteristic_h__
#include "mozilla/Attributes.h"
#include "mozilla/dom/BluetoothGattCharacteristicBinding.h"
#include "mozilla/dom/bluetooth/BluetoothCommon.h"
#include "mozilla/dom/bluetooth/BluetoothGattDescriptor.h"
#include "nsCOMPtr.h"
#include "nsWrapperCache.h"
#include "nsPIDOMWindow.h"
BEGIN_BLUETOOTH_NAMESPACE
class BluetoothGattService;
class BluetoothSignal;
class BluetoothValue;
class BluetoothGattCharacteristic final : public nsISupports
, public nsWrapperCache
, public BluetoothSignalObserver
{
public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(BluetoothGattCharacteristic)
/****************************************************************************
* Attribute Getters
***************************************************************************/
BluetoothGattService* Service() const
{
return mService;
}
void GetDescriptors(
nsTArray<nsRefPtr<BluetoothGattDescriptor>>& aDescriptors) const
{
aDescriptors = mDescriptors;
}
void GetUuid(nsString& aUuidStr) const
{
aUuidStr = mUuidStr;
}
int InstanceId() const
{
return mCharId.mInstanceId;
}
/****************************************************************************
* Others
***************************************************************************/
const BluetoothGattId& GetCharacteristicId() const
{
return mCharId;
}
void Notify(const BluetoothSignal& aData); // BluetoothSignalObserver
nsPIDOMWindow* GetParentObject() const
{
return mOwner;
}
virtual JSObject* WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) override;
BluetoothGattCharacteristic(nsPIDOMWindow* aOwner,
BluetoothGattService* aService,
const BluetoothGattId& aCharId);
private:
~BluetoothGattCharacteristic();
/**
* Add newly discovered GATT descriptors into mDescriptors and update the
* cache value of mDescriptors.
*
* @param aValue [in] BluetoothValue which contains an array of
* BluetoothGattId of all discovered descriptors.
*/
void HandleDescriptorsDiscovered(const BluetoothValue& aValue);
/****************************************************************************
* Variables
***************************************************************************/
nsCOMPtr<nsPIDOMWindow> mOwner;
/**
* Service that this characteristic belongs to.
*/
nsRefPtr<BluetoothGattService> mService;
/**
* Array of discovered descriptors for this characteristic.
*/
nsTArray<nsRefPtr<BluetoothGattDescriptor>> mDescriptors;
/**
* GattId of this GATT characteristic which contains
* 1) mUuid: UUID of this characteristic in byte array format.
* 2) mInstanceId: Instance id of this characteristic.
*/
BluetoothGattId mCharId;
/**
* UUID string of this GATT characteristic.
*/
nsString mUuidStr;
};
END_BLUETOOTH_NAMESPACE
#endif

View File

@ -0,0 +1,54 @@
/* -*- 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 "BluetoothService.h"
#include "BluetoothUtils.h"
#include "mozilla/dom/BluetoothGattDescriptorBinding.h"
#include "mozilla/dom/bluetooth/BluetoothCommon.h"
#include "mozilla/dom/bluetooth/BluetoothGattCharacteristic.h"
#include "mozilla/dom/bluetooth/BluetoothGattDescriptor.h"
#include "mozilla/dom/bluetooth/BluetoothTypes.h"
using namespace mozilla;
using namespace mozilla::dom;
USING_BLUETOOTH_NAMESPACE
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(
BluetoothGattDescriptor, mOwner, mCharacteristic)
NS_IMPL_CYCLE_COLLECTING_ADDREF(BluetoothGattDescriptor)
NS_IMPL_CYCLE_COLLECTING_RELEASE(BluetoothGattDescriptor)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(BluetoothGattDescriptor)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
BluetoothGattDescriptor::BluetoothGattDescriptor(
nsPIDOMWindow* aOwner,
BluetoothGattCharacteristic* aCharacteristic,
const BluetoothGattId& aDescriptorId)
: mOwner(aOwner)
, mCharacteristic(aCharacteristic)
, mDescriptorId(aDescriptorId)
{
MOZ_ASSERT(aOwner);
MOZ_ASSERT(aCharacteristic);
// Generate a string representation to provide uuid of this descriptor to
// applications
ReversedUuidToString(aDescriptorId.mUuid, mUuidStr);
}
BluetoothGattDescriptor::~BluetoothGattDescriptor()
{
}
JSObject*
BluetoothGattDescriptor::WrapObject(JSContext* aContext,
JS::Handle<JSObject*> aGivenProto)
{
return BluetoothGattDescriptorBinding::Wrap(aContext, this, aGivenProto);
}

View File

@ -0,0 +1,88 @@
/* -*- 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_bluetoothgattdescriptor_h__
#define mozilla_dom_bluetooth_bluetoothgattdescriptor_h__
#include "mozilla/Attributes.h"
#include "mozilla/dom/BluetoothGattDescriptorBinding.h"
#include "mozilla/dom/bluetooth/BluetoothCommon.h"
#include "nsCOMPtr.h"
#include "nsWrapperCache.h"
#include "nsPIDOMWindow.h"
BEGIN_BLUETOOTH_NAMESPACE
class BluetoothGattCharacteristic;
class BluetoothSignal;
class BluetoothValue;
class BluetoothGattDescriptor final : public nsISupports
, public nsWrapperCache
{
public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(BluetoothGattDescriptor)
/****************************************************************************
* Attribute Getters
***************************************************************************/
BluetoothGattCharacteristic* Characteristic() const
{
return mCharacteristic;
}
void GetUuid(nsString& aUuidStr) const
{
aUuidStr = mUuidStr;
}
/****************************************************************************
* Others
***************************************************************************/
void Notify(const BluetoothSignal& aData); // BluetoothSignalObserver
nsPIDOMWindow* GetParentObject() const
{
return mOwner;
}
virtual JSObject* WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) override;
BluetoothGattDescriptor(nsPIDOMWindow* aOwner,
BluetoothGattCharacteristic* aCharacteristic,
const BluetoothGattId& aDescriptorId);
private:
~BluetoothGattDescriptor();
/****************************************************************************
* Variables
***************************************************************************/
nsCOMPtr<nsPIDOMWindow> mOwner;
/**
* Characteristic that this descriptor belongs to.
*/
nsRefPtr<BluetoothGattCharacteristic> mCharacteristic;
/**
* GattId of this GATT descriptor which contains
* 1) mUuid: UUID of this descriptor in byte array format.
* 2) mInstanceId: Instance id of this descriptor.
*/
BluetoothGattId mDescriptorId;
/**
* UUID string of this GATT descriptor.
*/
nsString mUuidStr;
};
END_BLUETOOTH_NAMESPACE
#endif

View File

@ -0,0 +1,114 @@
/* -*- 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 "BluetoothService.h"
#include "BluetoothUtils.h"
#include "mozilla/dom/BluetoothGattServiceBinding.h"
#include "mozilla/dom/bluetooth/BluetoothCommon.h"
#include "mozilla/dom/bluetooth/BluetoothGattCharacteristic.h"
#include "mozilla/dom/bluetooth/BluetoothGattService.h"
#include "mozilla/dom/bluetooth/BluetoothTypes.h"
using namespace mozilla;
using namespace mozilla::dom;
USING_BLUETOOTH_NAMESPACE
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(
BluetoothGattService, mOwner, mIncludedServices, mCharacteristics)
NS_IMPL_CYCLE_COLLECTING_ADDREF(BluetoothGattService)
NS_IMPL_CYCLE_COLLECTING_RELEASE(BluetoothGattService)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(BluetoothGattService)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
BluetoothGattService::BluetoothGattService(
nsPIDOMWindow* aOwner, const nsAString& aAppUuid,
const BluetoothGattServiceId& aServiceId)
: mOwner(aOwner)
, mAppUuid(aAppUuid)
, mServiceId(aServiceId)
{
MOZ_ASSERT(aOwner);
MOZ_ASSERT(!mAppUuid.IsEmpty());
BluetoothService* bs = BluetoothService::Get();
NS_ENSURE_TRUE_VOID(bs);
// Generate bluetooth signal path and a string representation to provide
// uuid of this service to applications
nsString path;
GeneratePathFromGattId(mServiceId.mId, path, mUuidStr);
bs->RegisterBluetoothSignalHandler(path, this);
}
BluetoothGattService::~BluetoothGattService()
{
BluetoothService* bs = BluetoothService::Get();
NS_ENSURE_TRUE_VOID(bs);
nsString path;
GeneratePathFromGattId(mServiceId.mId, path);
bs->UnregisterBluetoothSignalHandler(path, this);
}
void
BluetoothGattService::HandleIncludedServicesDiscovered(
const BluetoothValue& aValue)
{
MOZ_ASSERT(aValue.type() == BluetoothValue::TArrayOfBluetoothGattServiceId);
const InfallibleTArray<BluetoothGattServiceId>& includedServIds =
aValue.get_ArrayOfBluetoothGattServiceId();
for (uint32_t i = 0; i < includedServIds.Length(); i++) {
mIncludedServices.AppendElement(new BluetoothGattService(
GetParentObject(), mAppUuid, includedServIds[i]));
}
BluetoothGattServiceBinding::ClearCachedIncludedServicesValue(this);
}
void
BluetoothGattService::HandleCharacteristicsDiscovered(
const BluetoothValue& aValue)
{
MOZ_ASSERT(aValue.type() == BluetoothValue::TArrayOfBluetoothGattId);
const InfallibleTArray<BluetoothGattId>& characteristicIds =
aValue.get_ArrayOfBluetoothGattId();
for (uint32_t i = 0; i < characteristicIds.Length(); i++) {
mCharacteristics.AppendElement(new BluetoothGattCharacteristic(
GetParentObject(), this, characteristicIds[i]));
}
BluetoothGattServiceBinding::ClearCachedCharacteristicsValue(this);
}
void
BluetoothGattService::Notify(const BluetoothSignal& aData)
{
BT_LOGD("[D] %s", NS_ConvertUTF16toUTF8(aData.name()).get());
BluetoothValue v = aData.value();
if (aData.name().EqualsLiteral("IncludedServicesDiscovered")) {
HandleIncludedServicesDiscovered(v);
} else if (aData.name().EqualsLiteral("CharacteristicsDiscovered")) {
HandleCharacteristicsDiscovered(v);
} else {
BT_WARNING("Not handling GATT Service signal: %s",
NS_ConvertUTF16toUTF8(aData.name()).get());
}
}
JSObject*
BluetoothGattService::WrapObject(JSContext* aContext,
JS::Handle<JSObject*> aGivenProto)
{
return BluetoothGattServiceBinding::Wrap(aContext, this, aGivenProto);
}

View File

@ -0,0 +1,146 @@
/* -*- 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_bluetoothgattservice_h__
#define mozilla_dom_bluetooth_bluetoothgattservice_h__
#include "mozilla/Attributes.h"
#include "mozilla/dom/BluetoothGattServiceBinding.h"
#include "mozilla/dom/bluetooth/BluetoothCommon.h"
#include "mozilla/dom/bluetooth/BluetoothGattCharacteristic.h"
#include "nsCOMPtr.h"
#include "nsWrapperCache.h"
#include "nsPIDOMWindow.h"
BEGIN_BLUETOOTH_NAMESPACE
class BluetoothSignal;
class BluetoothValue;
class BluetoothGattService final : public nsISupports
, public nsWrapperCache
, public BluetoothSignalObserver
{
public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(BluetoothGattService)
/****************************************************************************
* Attribute Getters
***************************************************************************/
bool IsPrimary() const
{
return mServiceId.mIsPrimary;
}
void GetUuid(nsString& aUuidStr) const
{
aUuidStr = mUuidStr;
}
int InstanceId() const
{
return mServiceId.mId.mInstanceId;
}
void GetIncludedServices(
nsTArray<nsRefPtr<BluetoothGattService>>& aIncludedServices) const
{
aIncludedServices = mIncludedServices;
}
void GetCharacteristics(
nsTArray<nsRefPtr<BluetoothGattCharacteristic>>& aCharacteristics) const
{
aCharacteristics = mCharacteristics;
}
/****************************************************************************
* Others
***************************************************************************/
const nsAString& GetAppUuid() const
{
return mAppUuid;
}
const BluetoothGattServiceId& GetServiceId() const
{
return mServiceId;
}
void Notify(const BluetoothSignal& aData); // BluetoothSignalObserver
nsPIDOMWindow* GetParentObject() const
{
return mOwner;
}
virtual JSObject* WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) override;
BluetoothGattService(nsPIDOMWindow* aOwner,
const nsAString& aAppUuid,
const BluetoothGattServiceId& aServiceId);
private:
~BluetoothGattService();
/**
* Add newly discovered GATT included services into mIncludedServices and
* update the cache value of mIncludedServices.
*
* @param aValue [in] BluetoothValue which contains an array of
* BluetoothGattServiceId of all discovered included
* services.
*/
void HandleIncludedServicesDiscovered(const BluetoothValue& aValue);
/**
* Add newly discovered GATT characteristics into mCharacteristics and
* update the cache value of mCharacteristics.
*
* @param aValue [in] BluetoothValue which contains an array of
* BluetoothGattId of all discovered characteristics.
*/
void HandleCharacteristicsDiscovered(const BluetoothValue& aValue);
/****************************************************************************
* Variables
***************************************************************************/
nsCOMPtr<nsPIDOMWindow> mOwner;
/**
* UUID of the GATT client.
*/
nsString mAppUuid;
/**
* ServiceId of this GATT service which contains
* 1) mId.mUuid: UUID of this service in byte array format.
* 2) mId.mInstanceId: Instance id of this service.
* 3) mIsPrimary: Indicate whether this is a primary service or not.
*/
BluetoothGattServiceId mServiceId;
/**
* UUID string of this GATT service.
*/
nsString mUuidStr;
/**
* Array of discovered included services for this service.
*/
nsTArray<nsRefPtr<BluetoothGattService>> mIncludedServices;
/**
* Array of discovered characteristics for this service.
*/
nsTArray<nsRefPtr<BluetoothGattCharacteristic>> mCharacteristics;
};
END_BLUETOOTH_NAMESPACE
#endif

View File

@ -691,20 +691,24 @@ public:
/* Enumerate Attributes */
virtual void SearchService(int aConnId,
bool aSearchAll,
const BluetoothUuid& aUuid,
BluetoothGattClientResultHandler* aRes) = 0;
virtual void GetIncludedService(
int aConnId,
const BluetoothGattServiceId& aServiceId,
bool aFirst,
const BluetoothGattServiceId& aStartServiceId,
BluetoothGattClientResultHandler* aRes) = 0;
virtual void GetCharacteristic(int aConnId,
const BluetoothGattServiceId& aServiceId,
bool aFirst,
const BluetoothGattId& aStartCharId,
BluetoothGattClientResultHandler* aRes) = 0;
virtual void GetDescriptor(int aConnId,
const BluetoothGattServiceId& aServiceId,
const BluetoothGattId& aCharId,
bool aFirst,
const BluetoothGattId& aDescriptorId,
BluetoothGattClientResultHandler* aRes) = 0;

View File

@ -342,6 +342,14 @@ public:
const nsAString& aDeviceAddress,
BluetoothReplyRunnable* aRunnable) = 0;
/**
* Discover GATT services, characteristic, descriptors from a remote GATT
* server. (platform specific implementation)
*/
virtual void
DiscoverGattServicesInternal(const nsAString& aAppUuid,
BluetoothReplyRunnable* aRunnable) = 0;
/**
* Unregister a GATT client. (platform specific implementation)
*/

View File

@ -40,6 +40,17 @@ UuidToString(const BluetoothUuid& aUuid, nsAString& aString)
aString.AssignLiteral(uuidStr);
}
void
ReversedUuidToString(const BluetoothUuid& aUuid, nsAString& aString)
{
BluetoothUuid uuid;
for (uint8_t i = 0; i < 16; i++) {
uuid.mUuid[i] = aUuid.mUuid[15 - i];
}
UuidToString(uuid, aString);
}
void
StringToUuid(const char* aString, BluetoothUuid& aUuid)
{
@ -64,6 +75,26 @@ StringToUuid(const char* aString, BluetoothUuid& aUuid)
memcpy(&aUuid.mUuid[14], &uuid5, sizeof(uint16_t));
}
void
GeneratePathFromGattId(const BluetoothGattId& aId,
nsAString& aPath,
nsAString& aUuidStr)
{
ReversedUuidToString(aId.mUuid, aUuidStr);
aPath.Assign(aUuidStr);
aPath.AppendLiteral("_");
aPath.AppendInt(aId.mInstanceId);
}
void
GeneratePathFromGattId(const BluetoothGattId& aId,
nsAString& aPath)
{
nsString uuidStr;
GeneratePathFromGattId(aId, aPath, uuidStr);
}
/**
* |SetJsObject| is an internal function used by |BroadcastSystemMessage| only
*/

View File

@ -22,20 +22,61 @@ class BluetoothValue;
/**
* Convert BluetoothUuid object to xxxxxxxx-xxxx-xxxx-xxxxxxxxxxxx uuid string.
* This utility function is used by gecko internal only to convert BluetoothUuid
* created by bluetooth stack to uuid string representation.
*
* Note: This utility function is used by gecko internal only to convert
* BluetoothUuid created by bluetooth stack to uuid string representation.
*/
void
UuidToString(const BluetoothUuid& aUuid, nsAString& aString);
/**
* Convert BluetoothUuid object in a reversed byte order to
* xxxxxxxx-xxxx-xxxx-xxxxxxxxxxxx uuid string.
* Bluedroid stack reports the BluetoothUuid in a reversed byte order for
* GATT service, characteristic, descriptor uuids.
*
* Note: This utility function is used by gecko internal only to convert
* BluetoothUuid in a reversed byte order created by bluetooth stack to uuid
* string representation.
*/
void
ReversedUuidToString(const BluetoothUuid& aUuid, nsAString& aString);
/**
* Convert xxxxxxxx-xxxx-xxxx-xxxxxxxxxxxx uuid string to BluetoothUuid object.
* This utility function is used by gecko internal only to convert uuid string
* created by gecko back to BluetoothUuid representation.
*
* Note: This utility function is used by gecko internal only to convert uuid
* string created by gecko back to BluetoothUuid representation.
*/
void
StringToUuid(const char* aString, BluetoothUuid& aUuid);
//
// Generate bluetooth signal path from GattId
//
/**
* Generate bluetooth signal path and UUID string from a GattId.
*
* @param aId [in] GattId value to convert.
* @param aPath [out] Bluetooth signal path generated from aId.
* @param aUuidStr [out] UUID string generated from aId.
*/
void
GeneratePathFromGattId(const BluetoothGattId& aId,
nsAString& aPath,
nsAString& aUuidStr);
/**
* Generate bluetooth signal path from a GattId.
*
* @param aId [in] GattId value to convert.
* @param aPath [out] Bluetooth signal path generated from aId.
*/
void
GeneratePathFromGattId(const BluetoothGattId& aId,
nsAString& aPath);
//
// Broadcast system message
//

View File

@ -621,14 +621,16 @@ BluetoothGattClientHALInterface::Refresh(
void
BluetoothGattClientHALInterface::SearchService(
int aConnId, const BluetoothUuid& aUuid,
int aConnId, bool aSearchAll, const BluetoothUuid& aUuid,
BluetoothGattClientResultHandler* aRes)
{
bt_status_t status;
#if ANDROID_VERSION >= 19
bt_uuid_t uuid;
if (NS_SUCCEEDED(Convert(aUuid, uuid))) {
if (aSearchAll) {
status = mInterface->search_service(aConnId, 0);
} else if (NS_SUCCEEDED(Convert(aUuid, uuid))) {
status = mInterface->search_service(aConnId, &uuid);
} else {
status = BT_STATUS_PARM_INVALID;
@ -647,7 +649,7 @@ BluetoothGattClientHALInterface::SearchService(
void
BluetoothGattClientHALInterface::GetIncludedService(
int aConnId, const BluetoothGattServiceId& aServiceId,
const BluetoothGattServiceId& aStartServiceId,
bool aFirst, const BluetoothGattServiceId& aStartServiceId,
BluetoothGattClientResultHandler* aRes)
{
bt_status_t status;
@ -655,8 +657,10 @@ BluetoothGattClientHALInterface::GetIncludedService(
btgatt_srvc_id_t serviceId;
btgatt_srvc_id_t startServiceId;
if (NS_SUCCEEDED(Convert(aServiceId, serviceId)) &&
NS_SUCCEEDED(Convert(aStartServiceId, startServiceId))) {
if (aFirst && NS_SUCCEEDED(Convert(aServiceId, serviceId))) {
status = mInterface->get_included_service(aConnId, &serviceId, 0);
} else if (NS_SUCCEEDED(Convert(aServiceId, serviceId)) &&
NS_SUCCEEDED(Convert(aStartServiceId, startServiceId))) {
status = mInterface->get_included_service(aConnId, &serviceId,
&startServiceId);
} else {
@ -676,7 +680,7 @@ BluetoothGattClientHALInterface::GetIncludedService(
void
BluetoothGattClientHALInterface::GetCharacteristic(
int aConnId, const BluetoothGattServiceId& aServiceId,
const BluetoothGattId& aStartCharId,
bool aFirst, const BluetoothGattId& aStartCharId,
BluetoothGattClientResultHandler* aRes)
{
bt_status_t status;
@ -684,8 +688,10 @@ BluetoothGattClientHALInterface::GetCharacteristic(
btgatt_srvc_id_t serviceId;
btgatt_gatt_id_t startCharId;
if (NS_SUCCEEDED(Convert(aServiceId, serviceId)) &&
NS_SUCCEEDED(Convert(aStartCharId, startCharId))) {
if (aFirst && NS_SUCCEEDED(Convert(aServiceId, serviceId))) {
status = mInterface->get_characteristic(aConnId, &serviceId, 0);
} else if (NS_SUCCEEDED(Convert(aServiceId, serviceId)) &&
NS_SUCCEEDED(Convert(aStartCharId, startCharId))) {
status = mInterface->get_characteristic(aConnId, &serviceId, &startCharId);
} else {
status = BT_STATUS_PARM_INVALID;
@ -704,9 +710,8 @@ BluetoothGattClientHALInterface::GetCharacteristic(
void
BluetoothGattClientHALInterface::GetDescriptor(
int aConnId, const BluetoothGattServiceId& aServiceId,
const BluetoothGattId& aCharId,
const BluetoothGattId& aDescriptorId,
BluetoothGattClientResultHandler* aRes)
const BluetoothGattId& aCharId, bool aFirst,
const BluetoothGattId& aDescriptorId, BluetoothGattClientResultHandler* aRes)
{
bt_status_t status;
#if ANDROID_VERSION >= 19
@ -714,9 +719,13 @@ BluetoothGattClientHALInterface::GetDescriptor(
btgatt_gatt_id_t charId;
btgatt_gatt_id_t descriptorId;
if (NS_SUCCEEDED(Convert(aServiceId, serviceId)) &&
NS_SUCCEEDED(Convert(aCharId, charId)) &&
NS_SUCCEEDED(Convert(aDescriptorId, descriptorId))) {
if (aFirst &&
NS_SUCCEEDED(Convert(aServiceId, serviceId)) &&
NS_SUCCEEDED(Convert(aCharId, charId))) {
status = mInterface->get_descriptor(aConnId, &serviceId, &charId, 0);
} else if (NS_SUCCEEDED(Convert(aServiceId, serviceId)) &&
NS_SUCCEEDED(Convert(aCharId, charId)) &&
NS_SUCCEEDED(Convert(aDescriptorId, descriptorId))) {
status = mInterface->get_descriptor(aConnId, &serviceId, &charId,
&descriptorId);
} else {

View File

@ -56,19 +56,23 @@ public:
/* Enumerate Attributes */
void SearchService(int aConnId,
bool aSearchAll,
const BluetoothUuid& aUuid,
BluetoothGattClientResultHandler* aRes);
void GetIncludedService(int aConnId,
const BluetoothGattServiceId& aServiceId,
bool aFirst,
const BluetoothGattServiceId& aStartServiceId,
BluetoothGattClientResultHandler* aRes);
void GetCharacteristic(int aConnId,
const BluetoothGattServiceId& aServiceId,
bool aFirst,
const BluetoothGattId& aStartCharId,
BluetoothGattClientResultHandler* aRes);
void GetDescriptor(int aConnId,
const BluetoothGattServiceId& aServiceId,
const BluetoothGattId& aCharId,
bool aFirst,
const BluetoothGattId& aDescriptorId,
BluetoothGattClientResultHandler* aRes);

View File

@ -6,12 +6,12 @@
#include "BluetoothGattManager.h"
#include "BluetoothCommon.h"
#include "BluetoothInterface.h"
#include "BluetoothReplyRunnable.h"
#include "BluetoothService.h"
#include "BluetoothUtils.h"
#include "MainThreadUtils.h"
#include "mozilla/dom/bluetooth/BluetoothCommon.h"
#include "mozilla/Services.h"
#include "mozilla/StaticPtr.h"
#include "nsIObserverService.h"
@ -37,10 +37,9 @@ namespace {
bool BluetoothGattManager::mInShutdown = false;
class BluetoothGattClient;
static StaticAutoPtr<nsTArray<nsRefPtr<BluetoothGattClient> > > sClients;
class BluetoothGattClient final : public nsISupports
class mozilla::dom::bluetooth::BluetoothGattClient final : public nsISupports
{
public:
NS_DECL_ISUPPORTS
@ -56,18 +55,59 @@ public:
{
mConnectRunnable = nullptr;
mDisconnectRunnable = nullptr;
mDiscoverRunnable = nullptr;
mUnregisterClientRunnable = nullptr;
mReadRemoteRssiRunnable = nullptr;
}
void NotifyDiscoverCompleted(bool aSuccess)
{
MOZ_ASSERT(!mAppUuid.IsEmpty());
MOZ_ASSERT(mDiscoverRunnable);
BluetoothService* bs = BluetoothService::Get();
NS_ENSURE_TRUE_VOID(bs);
// Notify application to clear the cache values of
// service/characteristic/descriptor.
bs->DistributeSignal(NS_LITERAL_STRING("DiscoverCompleted"),
mAppUuid,
BluetoothValue(aSuccess));
// Resolve/Reject the Promise.
if (aSuccess) {
DispatchReplySuccess(mDiscoverRunnable);
} else {
DispatchReplyError(mDiscoverRunnable,
NS_LITERAL_STRING("Discover failed"));
}
// Cleanup
mServices.Clear();
mIncludedServices.Clear();
mCharacteristics.Clear();
mDescriptors.Clear();
mDiscoverRunnable = nullptr;
}
nsString mAppUuid;
nsString mDeviceAddr;
int mClientIf;
int mConnId;
nsRefPtr<BluetoothReplyRunnable> mConnectRunnable;
nsRefPtr<BluetoothReplyRunnable> mDisconnectRunnable;
nsRefPtr<BluetoothReplyRunnable> mUnregisterClientRunnable;
nsRefPtr<BluetoothReplyRunnable> mDiscoverRunnable;
nsRefPtr<BluetoothReplyRunnable> mReadRemoteRssiRunnable;
nsRefPtr<BluetoothReplyRunnable> mUnregisterClientRunnable;
/**
* These temporary arrays are used only during discover operations.
* All of them are empty if there are no ongoing discover operations.
*/
nsTArray<BluetoothGattServiceId> mServices;
nsTArray<BluetoothGattServiceId> mIncludedServices;
nsTArray<BluetoothGattId> mCharacteristics;
nsTArray<BluetoothGattId> mDescriptors;
};
NS_IMPL_ISUPPORTS0(BluetoothGattClient)
@ -92,6 +132,16 @@ public:
}
};
class ConnIdComparator
{
public:
bool Equals(const nsRefPtr<BluetoothGattClient>& aClient,
int aConnId) const
{
return aClient->mConnId == aConnId;
}
};
BluetoothGattManager*
BluetoothGattManager::Get()
{
@ -338,13 +388,7 @@ BluetoothGattManager::UnregisterClient(int aClientIf,
size_t index = sClients->IndexOf(aClientIf, 0 /* Start */,
ClientIfComparator());
// Reject the unregister request if the client is not found
if (index == sClients->NoIndex) {
DispatchReplyError(aRunnable,
NS_LITERAL_STRING("Unregister GATT client failed"));
return;
}
MOZ_ASSERT(index != sClients->NoIndex);
nsRefPtr<BluetoothGattClient> client = sClients->ElementAt(index);
client->mUnregisterClientRunnable = aRunnable;
@ -469,12 +513,7 @@ BluetoothGattManager::Disconnect(const nsAString& aAppUuid,
ENSURE_GATT_CLIENT_INTF_IS_READY_VOID(aRunnable);
size_t index = sClients->IndexOf(aAppUuid, 0 /* Start */, UuidComparator());
// Reject the disconnect request if the client is not found
if (index == sClients->NoIndex) {
DispatchReplyError(aRunnable, NS_LITERAL_STRING("Disconnect failed"));
return;
}
MOZ_ASSERT(index != sClients->NoIndex);
nsRefPtr<BluetoothGattClient> client = sClients->ElementAt(index);
client->mDisconnectRunnable = aRunnable;
@ -486,6 +525,66 @@ BluetoothGattManager::Disconnect(const nsAString& aAppUuid,
new DisconnectResultHandler(client));
}
class BluetoothGattManager::DiscoverResultHandler final
: public BluetoothGattClientResultHandler
{
public:
DiscoverResultHandler(BluetoothGattClient* aClient)
: mClient(aClient)
{
MOZ_ASSERT(mClient);
}
void OnError(BluetoothStatus aStatus) override
{
BT_WARNING("BluetoothGattClientInterface::Discover failed: %d",
(int)aStatus);
mClient->NotifyDiscoverCompleted(false);
}
private:
nsRefPtr<BluetoothGattClient> mClient;
};
void
BluetoothGattManager::Discover(const nsAString& aAppUuid,
BluetoothReplyRunnable* aRunnable)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aRunnable);
ENSURE_GATT_CLIENT_INTF_IS_READY_VOID(aRunnable);
size_t index = sClients->IndexOf(aAppUuid, 0 /* Start */, UuidComparator());
MOZ_ASSERT(index != sClients->NoIndex);
nsRefPtr<BluetoothGattClient> client = sClients->ElementAt(index);
MOZ_ASSERT(client->mConnId > 0);
MOZ_ASSERT(!client->mDiscoverRunnable);
client->mDiscoverRunnable = aRunnable;
/**
* Discover all services/characteristics/descriptors offered by the remote
* GATT server.
*
* The discover procesure includes following steps.
* 1) Discover all services.
* 2) After all services are discovered, for each service S, we will do
* following actions.
* 2-1) Discover all included services of service S.
* 2-2) Discover all characteristics of service S.
* 2-3) Discover all descriptors of those characteristics discovered in
* 2-2).
*/
sBluetoothGattClientInterface->SearchService(
client->mConnId,
true, // search all services
BluetoothUuid(),
new DiscoverResultHandler(client));
}
class BluetoothGattManager::ReadRemoteRssiResultHandler final
: public BluetoothGattClientResultHandler
{
@ -527,13 +626,7 @@ BluetoothGattManager::ReadRemoteRssi(int aClientIf,
size_t index = sClients->IndexOf(aClientIf, 0 /* Start */,
ClientIfComparator());
// Reject the read remote rssi request if the client is not found
if (index == sClients->NoIndex) {
DispatchReplyError(aRunnable,
NS_LITERAL_STRING("Read remote RSSI failed"));
return;
}
MOZ_ASSERT(index != sClients->NoIndex);
nsRefPtr<BluetoothGattClient> client = sClients->ElementAt(index);
client->mReadRemoteRssiRunnable = aRunnable;
@ -558,7 +651,7 @@ BluetoothGattManager::RegisterClientNotification(BluetoothGattStatus aStatus,
UuidToString(aAppUuid, uuid);
size_t index = sClients->IndexOf(uuid, 0 /* Start */, UuidComparator());
NS_ENSURE_TRUE_VOID(index != sClients->NoIndex);
MOZ_ASSERT(index != sClients->NoIndex);
nsRefPtr<BluetoothGattClient> client = sClients->ElementAt(index);
BluetoothService* bs = BluetoothService::Get();
@ -621,7 +714,7 @@ BluetoothGattManager::ConnectNotification(int aConnId,
size_t index = sClients->IndexOf(aClientIf, 0 /* Start */,
ClientIfComparator());
NS_ENSURE_TRUE_VOID(index != sClients->NoIndex);
MOZ_ASSERT(index != sClients->NoIndex);
nsRefPtr<BluetoothGattClient> client = sClients->ElementAt(index);
if (aStatus != GATT_STATUS_SUCCESS) {
@ -673,7 +766,7 @@ BluetoothGattManager::DisconnectNotification(int aConnId,
size_t index = sClients->IndexOf(aClientIf, 0 /* Start */,
ClientIfComparator());
NS_ENSURE_TRUE_VOID(index != sClients->NoIndex);
MOZ_ASSERT(index != sClients->NoIndex);
nsRefPtr<BluetoothGattClient> client = sClients->ElementAt(index);
if (aStatus != GATT_STATUS_SUCCESS) {
@ -711,12 +804,57 @@ BluetoothGattManager::DisconnectNotification(int aConnId,
void
BluetoothGattManager::SearchCompleteNotification(int aConnId,
BluetoothGattStatus aStatus)
{ }
{
MOZ_ASSERT(NS_IsMainThread());
BluetoothService* bs = BluetoothService::Get();
NS_ENSURE_TRUE_VOID(bs);
size_t index = sClients->IndexOf(aConnId, 0 /* Start */,
ConnIdComparator());
MOZ_ASSERT(index != sClients->NoIndex);
nsRefPtr<BluetoothGattClient> client = sClients->ElementAt(index);
MOZ_ASSERT(client->mDiscoverRunnable);
if (aStatus != GATT_STATUS_SUCCESS) {
client->NotifyDiscoverCompleted(false);
return;
}
// Notify BluetoothGatt to create all services
bs->DistributeSignal(NS_LITERAL_STRING("ServicesDiscovered"),
client->mAppUuid,
BluetoothValue(client->mServices));
// All services are discovered, continue to search included services of each
// service if existed, otherwise, notify application that discover completed
if (!client->mServices.IsEmpty()) {
sBluetoothGattClientInterface->GetIncludedService(
aConnId,
client->mServices[0], // start from first service
true, // first included service
BluetoothGattServiceId(),
new DiscoverResultHandler(client));
} else {
client->NotifyDiscoverCompleted(true);
}
}
void
BluetoothGattManager::SearchResultNotification(
int aConnId, const BluetoothGattServiceId& aServiceId)
{ }
{
MOZ_ASSERT(NS_IsMainThread());
size_t index = sClients->IndexOf(aConnId, 0 /* Start */,
ConnIdComparator());
MOZ_ASSERT(index != sClients->NoIndex);
// Save to mServices for distributing to application and discovering
// included services, characteristics of this service later
sClients->ElementAt(index)->mServices.AppendElement(aServiceId);
}
void
BluetoothGattManager::GetCharacteristicNotification(
@ -724,7 +862,43 @@ BluetoothGattManager::GetCharacteristicNotification(
const BluetoothGattServiceId& aServiceId,
const BluetoothGattId& aCharId,
int aCharProperty)
{ }
{
MOZ_ASSERT(NS_IsMainThread());
BluetoothService* bs = BluetoothService::Get();
NS_ENSURE_TRUE_VOID(bs);
size_t index = sClients->IndexOf(aConnId, 0 /* Start */,
ConnIdComparator());
MOZ_ASSERT(index != sClients->NoIndex);
nsRefPtr<BluetoothGattClient> client = sClients->ElementAt(index);
MOZ_ASSERT(client->mDiscoverRunnable);
if (aStatus == GATT_STATUS_SUCCESS) {
// Save to mCharacteristics for distributing to applications and
// discovering descriptors of this characteristic later
client->mCharacteristics.AppendElement(aCharId);
// Get next characteristic of this service
sBluetoothGattClientInterface->GetCharacteristic(
aConnId,
aServiceId,
false,
aCharId,
new DiscoverResultHandler(client));
} else { // all characteristics of this service are discovered
// Notify BluetoothGattService to create characteristics then proceed
nsString path;
GeneratePathFromGattId(aServiceId.mId, path);
bs->DistributeSignal(NS_LITERAL_STRING("CharacteristicsDiscovered"),
path,
BluetoothValue(client->mCharacteristics));
ProceedDiscoverProcess(client, aServiceId);
}
}
void
BluetoothGattManager::GetDescriptorNotification(
@ -732,14 +906,93 @@ BluetoothGattManager::GetDescriptorNotification(
const BluetoothGattServiceId& aServiceId,
const BluetoothGattId& aCharId,
const BluetoothGattId& aDescriptorId)
{ }
{
MOZ_ASSERT(NS_IsMainThread());
BluetoothService* bs = BluetoothService::Get();
NS_ENSURE_TRUE_VOID(bs);
size_t index = sClients->IndexOf(aConnId, 0 /* Start */,
ConnIdComparator());
MOZ_ASSERT(index != sClients->NoIndex);
nsRefPtr<BluetoothGattClient> client = sClients->ElementAt(index);
MOZ_ASSERT(client->mDiscoverRunnable);
if (aStatus == GATT_STATUS_SUCCESS) {
// Save to mDescriptors for distributing to applications
client->mDescriptors.AppendElement(aDescriptorId);
// Get next descriptor of this characteristic
sBluetoothGattClientInterface->GetDescriptor(
aConnId,
aServiceId,
aCharId,
false,
aDescriptorId,
new DiscoverResultHandler(client));
} else { // all descriptors of this characteristic are discovered
// Notify BluetoothGattCharacteristic to create descriptors then proceed
nsString path;
GeneratePathFromGattId(aCharId, path);
bs->DistributeSignal(NS_LITERAL_STRING("DescriptorsDiscovered"),
path,
BluetoothValue(client->mDescriptors));
client->mDescriptors.Clear();
ProceedDiscoverProcess(client, aServiceId);
}
}
void
BluetoothGattManager::GetIncludedServiceNotification(
int aConnId, BluetoothGattStatus aStatus,
const BluetoothGattServiceId& aServiceId,
const BluetoothGattServiceId& aIncludedServId)
{ }
{
MOZ_ASSERT(NS_IsMainThread());
BluetoothService* bs = BluetoothService::Get();
NS_ENSURE_TRUE_VOID(bs);
size_t index = sClients->IndexOf(aConnId, 0 /* Start */,
ConnIdComparator());
MOZ_ASSERT(index != sClients->NoIndex);
nsRefPtr<BluetoothGattClient> client = sClients->ElementAt(index);
MOZ_ASSERT(client->mDiscoverRunnable);
if (aStatus == GATT_STATUS_SUCCESS) {
// Save to mIncludedServices for distributing to applications
client->mIncludedServices.AppendElement(aIncludedServId);
// Get next included service of this service
sBluetoothGattClientInterface->GetIncludedService(
aConnId,
aServiceId,
false,
aIncludedServId,
new DiscoverResultHandler(client));
} else { // all included services of this service are discovered
// Notify BluetoothGattService to create included services
nsString path;
GeneratePathFromGattId(aServiceId.mId, path);
bs->DistributeSignal(NS_LITERAL_STRING("IncludedServicesDiscovered"),
path,
BluetoothValue(client->mIncludedServices));
client->mIncludedServices.Clear();
// Start to discover characteristics of this service
sBluetoothGattClientInterface->GetCharacteristic(
aConnId,
aServiceId,
true, // first characteristic
BluetoothGattId(),
new DiscoverResultHandler(client));
}
}
void
BluetoothGattManager::RegisterNotificationNotification(
@ -863,4 +1116,45 @@ BluetoothGattManager::HandleShutdown()
sBluetoothGattManager = nullptr;
}
void
BluetoothGattManager::ProceedDiscoverProcess(
BluetoothGattClient* aClient,
const BluetoothGattServiceId& aServiceId)
{
/**
* This function will be called to decide how to proceed the discover process
* after discovering all characteristics of a given service, or after
* discovering all descriptors of a given characteristic.
*
* There are three cases here,
* 1) mCharacteristics is not empty:
* Proceed to discover descriptors of the first saved characteristic.
* 2) mCharacteristics is empty but mServices is not empty:
* This service does not have any saved characteristics left, proceed to
* discover included services of the next service.
* 3) Both arrays are already empty:
* Discover is done, notify application.
*/
if (!aClient->mCharacteristics.IsEmpty()) {
sBluetoothGattClientInterface->GetDescriptor(
aClient->mConnId,
aServiceId,
aClient->mCharacteristics[0],
true, // first descriptor
BluetoothGattId(),
new DiscoverResultHandler(aClient));
aClient->mCharacteristics.RemoveElementAt(0);
} else if (!aClient->mServices.IsEmpty()) {
sBluetoothGattClientInterface->GetIncludedService(
aClient->mConnId,
aClient->mServices[0],
true, // first included service
BluetoothGattServiceId(),
new DiscoverResultHandler(aClient));
aClient->mServices.RemoveElementAt(0);
} else {
aClient->NotifyDiscoverCompleted(true);
}
}
NS_IMPL_ISUPPORTS(BluetoothGattManager, nsIObserver)

View File

@ -13,6 +13,7 @@
BEGIN_BLUETOOTH_NAMESPACE
class BluetoothGattClient;
class BluetoothReplyRunnable;
class BluetoothGattManager final : public nsIObserver
@ -35,6 +36,9 @@ public:
const nsAString& aDeviceAddr,
BluetoothReplyRunnable* aRunnable);
void Discover(const nsAString& aAppUuid,
BluetoothReplyRunnable* aRunnable);
void UnregisterClient(int aClientIf,
BluetoothReplyRunnable* aRunnable);
@ -50,6 +54,7 @@ private:
class UnregisterClientResultHandler;
class ConnectResultHandler;
class DisconnectResultHandler;
class DiscoverResultHandler;
class ReadRemoteRssiResultHandler;
BluetoothGattManager();
@ -137,6 +142,9 @@ private:
void ListenNotification(BluetoothGattStatus aStatus,
int aServerIf) override;
void ProceedDiscoverProcess(BluetoothGattClient* aClient,
const BluetoothGattServiceId& aServiceId);
static bool mInShutdown;
};

View File

@ -1117,6 +1117,20 @@ BluetoothServiceBluedroid::DisconnectGattClientInternal(
gatt->Disconnect(aAppUuid, aDeviceAddress, aRunnable);
}
void
BluetoothServiceBluedroid::DiscoverGattServicesInternal(
const nsAString& aAppUuid, BluetoothReplyRunnable* aRunnable)
{
MOZ_ASSERT(NS_IsMainThread());
ENSURE_BLUETOOTH_IS_READY_VOID(aRunnable);
BluetoothGattManager* gatt = BluetoothGattManager::Get();
ENSURE_GATT_MGR_IS_READY_VOID(gatt, aRunnable);
gatt->Discover(aAppUuid, aRunnable);
}
void
BluetoothServiceBluedroid::UnregisterGattClientInternal(
int aClientIf, BluetoothReplyRunnable* aRunnable)

View File

@ -184,6 +184,10 @@ public:
const nsAString& aDeviceAddress,
BluetoothReplyRunnable* aRunnable) override;
virtual void
DiscoverGattServicesInternal(const nsAString& aAppUuid,
BluetoothReplyRunnable* aRunnable) override;
virtual void
UnregisterGattClientInternal(int aClientIf,
BluetoothReplyRunnable* aRunnable) override;

View File

@ -4291,6 +4291,12 @@ BluetoothDBusService::DisconnectGattClientInternal(
{
}
void
BluetoothDBusService::DiscoverGattServicesInternal(
const nsAString& aAppUuid, BluetoothReplyRunnable* aRunnable)
{
}
void
BluetoothDBusService::UnregisterGattClientInternal(
int aClientIf, BluetoothReplyRunnable* aRunnable)

View File

@ -194,6 +194,11 @@ public:
DisconnectGattClientInternal(const nsAString& aAppUuid,
const nsAString& aDeviceAddress,
BluetoothReplyRunnable* aRunnable) override;
virtual void
DiscoverGattServicesInternal(const nsAString& aAppUuid,
BluetoothReplyRunnable* aRunnable) override;
virtual void
UnregisterGattClientInternal(int aClientIf,
BluetoothReplyRunnable* aRunnable) override;

View File

@ -28,6 +28,73 @@ struct ParamTraits<mozilla::dom::bluetooth::BluetoothStatus>
mozilla::dom::bluetooth::STATUS_RMT_DEV_DOWN>
{ };
template <>
struct ParamTraits<mozilla::dom::bluetooth::BluetoothUuid>
{
typedef mozilla::dom::bluetooth::BluetoothUuid paramType;
static void Write(Message* aMsg, const paramType& aParam)
{
for (uint8_t i = 0; i < 16; i++) {
WriteParam(aMsg, aParam.mUuid[i]);
}
}
static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
{
for (uint8_t i = 0; i < 16; i++) {
if (!ReadParam(aMsg, aIter, &(aResult->mUuid[i]))) {
return false;
}
}
return true;
}
};
template <>
struct ParamTraits<mozilla::dom::bluetooth::BluetoothGattId>
{
typedef mozilla::dom::bluetooth::BluetoothGattId paramType;
static void Write(Message* aMsg, const paramType& aParam)
{
WriteParam(aMsg, aParam.mUuid);
WriteParam(aMsg, aParam.mInstanceId);
}
static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
{
if (!ReadParam(aMsg, aIter, &(aResult->mUuid)) ||
!ReadParam(aMsg, aIter, &(aResult->mInstanceId))) {
return false;
}
return true;
}
};
template <>
struct ParamTraits<mozilla::dom::bluetooth::BluetoothGattServiceId>
{
typedef mozilla::dom::bluetooth::BluetoothGattServiceId paramType;
static void Write(Message* aMsg, const paramType& aParam)
{
WriteParam(aMsg, aParam.mId);
WriteParam(aMsg, aParam.mIsPrimary);
}
static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
{
if (!ReadParam(aMsg, aIter, &(aResult->mId)) ||
!ReadParam(aMsg, aIter, &(aResult->mIsPrimary))) {
return false;
}
return true;
}
};
} // namespace IPC
#endif // mozilla_dom_bluetooth_ipc_bluetoothmessageutils_h__

View File

@ -254,6 +254,8 @@ BluetoothParent::RecvPBluetoothRequestConstructor(
return actor->DoRequest(aRequest.get_ConnectGattClientRequest());
case Request::TDisconnectGattClientRequest:
return actor->DoRequest(aRequest.get_DisconnectGattClientRequest());
case Request::TDiscoverGattServicesRequest:
return actor->DoRequest(aRequest.get_DiscoverGattServicesRequest());
case Request::TUnregisterGattClientRequest:
return actor->DoRequest(aRequest.get_UnregisterGattClientRequest());
case Request::TGattClientReadRemoteRssiRequest:
@ -719,6 +721,18 @@ BluetoothRequestParent::DoRequest(const DisconnectGattClientRequest& aRequest)
return true;
}
bool
BluetoothRequestParent::DoRequest(const DiscoverGattServicesRequest& aRequest)
{
MOZ_ASSERT(mService);
MOZ_ASSERT(mRequestType == Request::TDiscoverGattServicesRequest);
mService->DiscoverGattServicesInternal(aRequest.appUuid(),
mReplyRunnable.get());
return true;
}
bool
BluetoothRequestParent::DoRequest(const UnregisterGattClientRequest& aRequest)
{

View File

@ -223,6 +223,9 @@ protected:
bool
DoRequest(const DisconnectGattClientRequest& aRequest);
bool
DoRequest(const DiscoverGattServicesRequest& aRequest);
bool
DoRequest(const UnregisterGattClientRequest& aRequest);

View File

@ -397,6 +397,14 @@ BluetoothServiceChildProcess::DisconnectGattClientInternal(
DisconnectGattClientRequest(nsString(aAppUuid), nsString(aDeviceAddress)));
}
void
BluetoothServiceChildProcess::DiscoverGattServicesInternal(
const nsAString& aAppUuid, BluetoothReplyRunnable* aRunnable)
{
SendRequest(aRunnable,
DiscoverGattServicesRequest(nsString(aAppUuid)));
}
void
BluetoothServiceChildProcess::UnregisterGattClientInternal(
int aClientIf, BluetoothReplyRunnable* aRunnable)

View File

@ -202,6 +202,10 @@ public:
const nsAString& aDeviceAddress,
BluetoothReplyRunnable* aRunnable) override;
virtual void
DiscoverGattServicesInternal(const nsAString& aAppUuid,
BluetoothReplyRunnable* aRunnable) override;
virtual void
UnregisterGattClientInternal(int aClientIf,
BluetoothReplyRunnable* aRunnable) override;

View File

@ -4,7 +4,12 @@
* 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/. */
using mozilla::dom::bluetooth::BluetoothStatus from "mozilla/dom/bluetooth/BluetoothCommon.h";
using mozilla::dom::bluetooth::BluetoothGattId
from "mozilla/dom/bluetooth/BluetoothCommon.h";
using mozilla::dom::bluetooth::BluetoothGattServiceId
from "mozilla/dom/bluetooth/BluetoothCommon.h";
using mozilla::dom::bluetooth::BluetoothStatus
from "mozilla/dom/bluetooth/BluetoothCommon.h";
namespace mozilla {
namespace dom {
@ -23,6 +28,8 @@ union BluetoothValue
nsString[];
uint8_t[];
BluetoothNamedValue[];
BluetoothGattId[];
BluetoothGattServiceId[];
};
/**

View File

@ -188,6 +188,11 @@ struct DisconnectGattClientRequest
nsString deviceAddress;
};
struct DiscoverGattServicesRequest
{
nsString appUuid;
};
struct UnregisterGattClientRequest
{
int clientIf;
@ -233,6 +238,7 @@ union Request
SendPlayStatusRequest;
ConnectGattClientRequest;
DisconnectGattClientRequest;
DiscoverGattServicesRequest;
UnregisterGattClientRequest;
GattClientReadRemoteRssiRequest;
};

View File

@ -11,6 +11,9 @@ if CONFIG['MOZ_B2G_BT']:
'BluetoothDevice.cpp',
'BluetoothDiscoveryHandle.cpp',
'BluetoothGatt.cpp',
'BluetoothGattCharacteristic.cpp',
'BluetoothGattDescriptor.cpp',
'BluetoothGattService.cpp',
'BluetoothHidManager.cpp',
'BluetoothInterface.cpp',
'BluetoothManager.cpp',
@ -124,6 +127,9 @@ EXPORTS.mozilla.dom.bluetooth += [
'BluetoothDevice.h',
'BluetoothDiscoveryHandle.h',
'BluetoothGatt.h',
'BluetoothGattCharacteristic.h',
'BluetoothGattDescriptor.h',
'BluetoothGattService.h',
'BluetoothManager.h',
'BluetoothPairingHandle.h',
'BluetoothPairingListener.h',

View File

@ -524,7 +524,6 @@ skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' || e1
skip-if = toolkit == 'android' || (toolkit == 'gonk' && debug) #bug 871015, bug 881443
[test_input_files_not_nsIFile.html]
[test_ignoreuserfocus.html]
skip-if = (toolkit == 'gonk' && debug)
[test_fragment_form_pointer.html]
[test_bug1682.html]
[test_bug1823.html]

View File

@ -7,6 +7,8 @@
[CheckPermissions="bluetooth"]
interface BluetoothGatt : EventTarget
{
[Cached, Pure]
readonly attribute sequence<BluetoothGattService> services;
readonly attribute BluetoothConnectionState connectionState;
// Fired when attribute connectionState changed
@ -28,6 +30,14 @@ interface BluetoothGatt : EventTarget
[NewObject]
Promise<void> disconnect();
/**
* Discover services, characteristics, descriptors offered by the remote GATT
* server. The promise will be rejected if the connState is not connected or
* operation fails.
*/
[NewObject]
Promise<void> discoverServices();
/**
* Read RSSI for the remote BLE device if the connectState is connected.
* Otherwise, the Promise will be rejected directly.

View File

@ -0,0 +1,16 @@
/* -*- 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/. */
[CheckPermissions="bluetooth"]
interface BluetoothGattCharacteristic
{
readonly attribute BluetoothGattService service;
[Cached, Pure]
readonly attribute sequence<BluetoothGattDescriptor> descriptors;
readonly attribute DOMString uuid;
readonly attribute unsigned short instanceId;
};

View File

@ -0,0 +1,12 @@
/* -*- 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/. */
[CheckPermissions="bluetooth"]
interface BluetoothGattDescriptor
{
readonly attribute BluetoothGattCharacteristic characteristic;
readonly attribute DOMString uuid;
};

View File

@ -0,0 +1,18 @@
/* -*- 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/. */
[CheckPermissions="bluetooth"]
interface BluetoothGattService
{
[Cached, Pure]
readonly attribute sequence<BluetoothGattCharacteristic> characteristics;
[Cached, Pure]
readonly attribute sequence<BluetoothGattService> includedServices;
readonly attribute boolean isPrimary;
readonly attribute DOMString uuid;
readonly attribute unsigned short instanceId;
};

View File

@ -637,6 +637,9 @@ if CONFIG['MOZ_B2G_BT']:
'BluetoothDevice2.webidl',
'BluetoothDiscoveryHandle.webidl',
'BluetoothGatt.webidl',
'BluetoothGattCharacteristic.webidl',
'BluetoothGattDescriptor.webidl',
'BluetoothGattService.webidl',
'BluetoothManager2.webidl',
'BluetoothPairingHandle.webidl',
'BluetoothPairingListener.webidl',

View File

@ -106,7 +106,8 @@ public class ShareDialog extends Locales.LocaleAwareActivity implements SendTabT
// Escape hatch: we don't show the option to open this dialog in this state so this should
// never be run. However, due to potential inconsistencies in synced client state
// (e.g. bug 1122302 comment 47), we might fail.
if (state == State.DEVICES_ONLY && clientrecords.length == 0) {
if (state == State.DEVICES_ONLY &&
(clientrecords == null || clientrecords.length == 0)) {
Log.e(LOGTAG, "In state: " + State.DEVICES_ONLY + " and received 0 synced clients. Finishing...");
Toast.makeText(this, getResources().getText(R.string.overlay_no_synced_devices), Toast.LENGTH_SHORT)
.show();

View File

@ -28,6 +28,7 @@ import org.mozilla.gecko.GeckoProfile;
import org.mozilla.gecko.fxa.FirefoxAccounts;
import org.mozilla.gecko.overlays.ui.ShareDialog;
import org.mozilla.gecko.sync.repositories.android.ClientsDatabaseAccessor;
import org.mozilla.gecko.sync.setup.SyncAccounts;
import org.mozilla.gecko.R;
import java.io.File;
@ -1303,6 +1304,13 @@ public class ActivityChooserModel extends DataSetObservable {
* Mozilla: Return whether or not there are other synced clients.
*/
private boolean hasOtherSyncClients() {
// ClientsDatabaseAccessor returns stale data (bug 1145896) so we work around this by
// checking if we have accounts set up - if not, we can't have any clients.
if (!FirefoxAccounts.firefoxAccountsExist(mContext) &&
!SyncAccounts.syncAccountsExist(mContext)) {
return false;
}
final ClientsDatabaseAccessor db = new ClientsDatabaseAccessor(mContext);
return db.clientsCount() > 0;
}

View File

@ -106,12 +106,10 @@ static size_t getPageSize(void) {
}
/**
* The stack size is chosen carefully so the frozen threads doesn't consume too
* much memory in the Nuwa process. The threads shouldn't run deep recursive
* methods or do large allocations on the stack to avoid stack overflow.
* Use 1 MiB stack size as Android does.
*/
#ifndef NUWA_STACK_SIZE
#define NUWA_STACK_SIZE (1024 * 128)
#define NUWA_STACK_SIZE (1024 * 1024)
#endif
#define NATIVE_THREAD_NAME_LENGTH 16
@ -173,6 +171,8 @@ struct thread_info : public mozilla::LinkedListElement<thread_info> {
pthread_mutex_t *condMutex;
bool condMutexNeedsBalancing;
size_t stackSize;
size_t guardSize;
void *stk;
pid_t origNativeThreadID;
@ -546,20 +546,10 @@ thread_info_new(void) {
tinfo->recreatedNativeThreadID = 0;
tinfo->condMutex = nullptr;
tinfo->condMutexNeedsBalancing = false;
tinfo->stk = MozTaggedAnonymousMmap(nullptr,
NUWA_STACK_SIZE + getPageSize(),
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS,
/* fd */ -1,
/* offset */ 0,
"nuwa-thread-stack");
// We use a smaller stack size. Add protection to stack overflow: mprotect()
// stack top (the page at the lowest address) so we crash instead of corrupt
// other content that is malloc()'d.
mprotect(tinfo->stk, getPageSize(), PROT_NONE);
pthread_attr_init(&tinfo->threadAttr);
// Default stack and guard size.
tinfo->stackSize = NUWA_STACK_SIZE;
tinfo->guardSize = getPageSize();
REAL(pthread_mutex_lock)(&sThreadCountLock);
// Insert to the tail.
@ -572,6 +562,44 @@ thread_info_new(void) {
return tinfo;
}
static void
thread_attr_init(thread_info_t *tinfo, const pthread_attr_t *tattr)
{
pthread_attr_init(&tinfo->threadAttr);
if (tattr) {
// Override default thread stack and guard size with tattr.
pthread_attr_getstacksize(tattr, &tinfo->stackSize);
pthread_attr_getguardsize(tattr, &tinfo->guardSize);
size_t pageSize = getPageSize();
tinfo->stackSize = (tinfo->stackSize + pageSize - 1) % pageSize;
tinfo->guardSize = (tinfo->guardSize + pageSize - 1) % pageSize;
int detachState = 0;
pthread_attr_getdetachstate(tattr, &detachState);
pthread_attr_setdetachstate(&tinfo->threadAttr, detachState);
}
tinfo->stk = MozTaggedAnonymousMmap(nullptr,
tinfo->stackSize + tinfo->guardSize,
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS,
/* fd */ -1,
/* offset */ 0,
"nuwa-thread-stack");
// Add protection to stack overflow: mprotect() stack top (the page at the
// lowest address) so we crash instead of corrupt other content that is
// malloc()'d.
mprotect(tinfo->stk, tinfo->guardSize, PROT_NONE);
pthread_attr_setstack(&tinfo->threadAttr,
(char*)tinfo->stk + tinfo->guardSize,
tinfo->stackSize);
}
static void
thread_info_cleanup(void *arg) {
if (sNuwaForking) {
@ -582,7 +610,7 @@ thread_info_cleanup(void *arg) {
thread_info_t *tinfo = (thread_info_t *)arg;
pthread_attr_destroy(&tinfo->threadAttr);
munmap(tinfo->stk, NUWA_STACK_SIZE + getPageSize());
munmap(tinfo->stk, tinfo->stackSize + tinfo->guardSize);
REAL(pthread_mutex_lock)(&sThreadCountLock);
/* unlink tinfo from sAllThreads */
@ -741,11 +769,9 @@ __wrap_pthread_create(pthread_t *thread,
}
thread_info_t *tinfo = thread_info_new();
thread_attr_init(tinfo, attr);
tinfo->startupFunc = start_routine;
tinfo->startupArg = arg;
pthread_attr_setstack(&tinfo->threadAttr,
(char*)tinfo->stk + getPageSize(),
NUWA_STACK_SIZE);
int rv = REAL(pthread_create)(thread,
&tinfo->threadAttr,

View File

@ -154,6 +154,7 @@ SandboxFilterImplContent::Build() {
Allow(SYSCALL(munmap));
Allow(SYSCALL(mprotect));
Allow(SYSCALL(writev));
Allow(SYSCALL(pread64));
AllowThreadClone();
Allow(SYSCALL(brk));
#if SYSCALL_EXISTS(set_thread_area)

View File

@ -17,6 +17,8 @@ def parse_test_opts(input_str):
tests.insert(0, cur_test)
def add_platform(value):
# Ensure platforms exists...
cur_test['platforms'] = cur_test.get('platforms', [])
cur_test['platforms'].insert(0, value.strip())
# This might be somewhat confusing but we parse the string _backwards_ so
@ -45,7 +47,6 @@ def parse_test_opts(input_str):
elif char == ']':
# Entering platform state.
in_platforms = True
cur_test['platforms'] = []
else:
# Accumulator.
token = char + token

View File

@ -29,6 +29,18 @@ class TryTestParserTest(unittest.TestCase):
]
)
self.assertEquals(
parse_test_opts('mochitest-3[Ubuntu,10.6,10.8,Windows XP,Windows 7,Windows 8]'),
[
{
'test': 'mochitest-3',
'platforms': [
'Ubuntu', '10.6', '10.8', 'Windows XP', 'Windows 7', 'Windows 8'
]
}
]
)
self.assertEquals(
parse_test_opts(''),
[]

View File

@ -29,6 +29,7 @@ loader.lazyGetter(this, "Debugger", () => {
loader.lazyRequireGetter(this, "SourceMapConsumer", "source-map", true);
loader.lazyRequireGetter(this, "SourceMapGenerator", "source-map", true);
loader.lazyRequireGetter(this, "CssLogic", "devtools/styleinspector/css-logic", true);
loader.lazyRequireGetter(this, "events", "sdk/event/core");
let TYPED_ARRAY_CLASSES = ["Uint8Array", "Uint8ClampedArray", "Uint16Array",
"Uint32Array", "Int8Array", "Int16Array", "Int32Array", "Float32Array",
@ -444,6 +445,7 @@ function ThreadActor(aParent, aGlobal)
this._tabClosed = false;
this._scripts = null;
this._sources = null;
this._pauseOnDOMEvents = null;
this._options = {
useSourceMaps: false,
@ -467,6 +469,8 @@ function ThreadActor(aParent, aGlobal)
this.uncaughtExceptionHook = this.uncaughtExceptionHook.bind(this);
this.onDebuggerStatement = this.onDebuggerStatement.bind(this);
this.onNewScript = this.onNewScript.bind(this);
this._onWindowReady = this._onWindowReady.bind(this);
events.on(this._parent, "window-ready", this._onWindowReady);
// Set a wrappedJSObject property so |this| can be sent via the observer svc
// for the xpcshell harness.
this.wrappedJSObject = this;
@ -620,6 +624,7 @@ ThreadActor.prototype = {
// things like breakpoints across connections.
this._sourceActorStore = null;
events.off(this._parent, "window-ready", this._onWindowReady);
this.clearDebuggees();
this.conn.removeActorPool(this._threadLifetimePool);
this._threadLifetimePool = null;
@ -1009,6 +1014,16 @@ ThreadActor.prototype = {
}
},
/**
* If we are tasked with breaking on the load event, we have to add the
* listener early enough.
*/
_onWindowReady: function () {
this._maybeListenToEvents({
pauseOnDOMEvents: this._pauseOnDOMEvents
});
},
/**
* Handle a protocol request to resume execution of the debuggee.
*/
@ -2791,21 +2806,16 @@ SourceActor.prototype = {
* Ensure the given BreakpointActor is set as a breakpoint handler on all
* scripts that match its location in the original source.
*
* It is possible that no scripts match the given location, because they have
* all been garbage collected. In that case, the BreakpointActor is not set as
* a breakpoint handler for any script, but is still inserted in the
* BreakpointActorMap as a pending breakpoint. Whenever a new script is
* introduced, we call this method again to see if there are now any scripts
* that matches the given location.
* If there are no scripts that match the location of the BreakpointActor,
* we slide its location to the next closest line (for line breakpoints) or
* column (for column breakpoint) that does.
*
* The first time we find one or more scripts that matches the given location,
* we check if any of these scripts has any entry points for the given
* location. If not, we assume that the given location does not have any code.
*
* If the given location does not contain any code, we slide the breakpoint
* down to the next closest line that does, and update the BreakpointActorMap
* accordingly. Note that we only do so if the BreakpointActor is still
* pending (i.e. is not set as a breakpoint handler for any script).
* If breakpoint sliding fails, then either there are no scripts that contain
* any code for the given location, or they were all garbage collected before
* the debugger started running. We cannot distinguish between these two
* cases, so we insert the BreakpointActor in the BreakpointActorMap as
* a pending breakpoint. Whenever a new script is introduced, this method is
* called again for each pending breakpoint.
*
* @param BreakpointActor actor
* The BreakpointActor to be set as a breakpoint handler.
@ -2813,21 +2823,125 @@ SourceActor.prototype = {
* @returns A Promise that resolves to the given BreakpointActor.
*/
_setBreakpointForActor: function (actor) {
let { originalLocation } = actor;
if (this.isSourceMapped) {
return this.threadActor.sources.getGeneratedLocation(
actor.originalLocation
).then((generatedLocation) => {
// TODO: Refactor breakpoint sliding for source mapped sources.
return this.threadActor.sources.getGeneratedLocation(originalLocation)
.then((generatedLocation) => {
return generatedLocation.generatedSourceActor
._setBreakpointForActorAtLocation(
._setBreakpointForActorAtLocationWithSliding(
actor,
generatedLocation
);
});
} else {
return Promise.resolve(this._setBreakpointForActorAtLocation(
actor,
GeneratedLocation.fromOriginalLocation(actor.originalLocation)
));
// If this is a non-source mapped source, the original location and
// generated location are the same, so we can safely convert between them.
let generatedLocation = GeneratedLocation.fromOriginalLocation(originalLocation);
let { generatedColumn } = generatedLocation;
// Try to set the breakpoint on the generated location directly. If this
// succeeds, we can avoid the more expensive breakpoint sliding algorithm
// below.
if (this._setBreakpointForActorAtLocation(actor, generatedLocation)) {
return Promise.resolve(actor);
}
// There were no scripts that matched the given location, so we need to
// perform breakpoint sliding.
if (generatedColumn === undefined) {
// To perform breakpoint sliding for line breakpoints, we need to build
// a map from line numbers to a list of entry points for each line,
// implemented as a sparse array. An entry point is a (script, offsets)
// pair, and represents all offsets in that script that are entry points
// for the corresponding line.
let lineToEntryPointsMap = [];
// Iterate over all scripts that correspond to this source actor.
let scripts = this.scripts.getScriptsBySourceActor(this);
for (let script of scripts) {
// Get all offsets for each line in the current script. This returns
// a map from line numbers fo a list of offsets for each line,
// implemented as a sparse array.
let lineToOffsetsMap = script.getAllOffsets();
// Iterate over each line, and add their list of offsets to the map
// from line numbers to entry points by forming a (script, offsets)
// pair, where script is the current script, and offsets is the list
// of offsets for the current line.
for (let line = 0; line < lineToOffsetsMap.length; ++line) {
let offsets = lineToOffsetsMap[line];
if (offsets) {
let entryPoints = lineToEntryPointsMap[line];
if (!entryPoints) {
// We dont have a list of entry points for the current line
// number yet, so create it and add it to the map.
entryPoints = [];
lineToEntryPointsMap[line] = entryPoints;
}
entryPoints.push({ script, offsets });
}
}
}
let {
originalSourceActor,
originalLine,
originalColumn
} = originalLocation;
// Now that we have a map from line numbers to a list of entry points
// for each line, we can use it to perform breakpoint sliding. Start
// at the original line of the breakpoint actor, and keep incrementing
// it by one, until either we find a line that has at least one entry
// point, or we go past the last line in the map.
//
// Note that by computing the entire map up front, and implementing it
// as a sparse array, we can easily tell when we went past the last line
// in the map.
let actualLine = originalLine;
while (actualLine < lineToEntryPointsMap.length) {
let entryPoints = lineToEntryPointsMap[actualLine];
if (entryPoints) {
setBreakpointForActorAtEntryPoints(actor, entryPoints);
break;
}
++actualLine;
}
if (actualLine === lineToEntryPointsMap.length) {
// We went past the last line in the map, so breakpoint sliding
// failed. Keep the BreakpointActor in the BreakpointActorMap as a
// pending breakpoint, so we can try again whenever a new script is
// introduced.
return Promise.resolve(actor);
}
// If the actual line on which the BreakpointActor was set differs from
// the original line that was requested, the BreakpointActor and the
// BreakpointActorMap need to be updated accordingly.
if (actualLine !== originalLine) {
let actualLocation = new OriginalLocation(
originalSourceActor,
actualLine
);
let existingActor = this.breakpointActorMap.getActor(actualLocation);
if (existingActor) {
actor.onDelete();
this.breakpointActorMap.deleteActor(originalLocation);
actor = existingActor;
} else {
this.breakpointActorMap.deleteActor(originalLocation);
actor.originalLocation = actualLocation;
this.breakpointActorMap.setActor(actualLocation, actor);
}
}
return Promise.resolve(actor);
} else {
// TODO: Implement breakpoint sliding for column breakpoints
return Promise.resolve(actor);
}
}
},
@ -2841,8 +2955,79 @@ SourceActor.prototype = {
* A GeneratedLocation representing the location in the generated
* source for which the given BreakpointActor is to be set as a
* breakpoint handler.
*
* @returns A Boolean that is true if the BreakpointActor was set as a
* breakpoint handler on at least one script, and false otherwise.
*/
_setBreakpointForActorAtLocation: function (actor, generatedLocation) {
let { generatedLine, generatedColumn } = generatedLocation;
// Find all scripts that match the given source actor and line number.
let scripts = this.scripts.getScriptsBySourceActorAndLine(
this,
generatedLine
).filter((script) => !actor.hasScript(script));
// Find all entry points that correspond to the given location.
let entryPoints = [];
if (generatedColumn === undefined) {
// This is a line breakpoint, so we are interested in all offsets
// that correspond to the given line number.
for (let script of scripts) {
let offsets = script.getLineOffsets(generatedLine);
if (offsets.length > 0) {
entryPoints.push({ script, offsets });
}
}
} else {
// This is a column breakpoint, so we are interested in all column
// offsets that correspond to the given line *and* column number.
for (let script of scripts) {
let columnToOffsetMap = script.getAllColumnOffsets()
.filter(({ lineNumber }) => {
return lineNumber === generatedLine;
});
for (let { columnNumber: column, offset } of columnToOffsetMap) {
// TODO: What we are actually interested in here is a range of
// columns, rather than a single one.
if (column == generatedColumn) {
entryPoints.push({ script, offsets: [offset] });
}
}
}
}
if (entryPoints.length === 0) {
return false;
}
setBreakpointForActorAtEntryPoints(actor, entryPoints);
return true;
},
/*
* Ensure the given BreakpointActor is set as breakpoint handler on all
* scripts that match the given location in the generated source.
*
* TODO: This method is bugged, because it performs breakpoint sliding on
* generated locations. Breakpoint sliding should be performed on original
* locations, because there is no guarantee that the next line in the
* generated source corresponds to the next line in an original source.
*
* The only place this method is still used is from setBreakpointForActor
* when called for a source mapped source. Once that code has been refactored,
* this method can be removed.
*
* @param BreakpointActor actor
* The BreakpointActor to be set as a breakpoint handler.
* @param GeneratedLocation generatedLocation
* A GeneratedLocation representing the location in the generated
* source for which the given BreakpointActor is to be set as a
* breakpoint handler.
*
* @returns A Boolean that is true if the BreakpointActor was set as a
* breakpoint handler on at least one script, and false otherwise.
*/
_setBreakpointForActorAtLocationWithSliding: function (actor, generatedLocation) {
let originalLocation = actor.originalLocation;
let { generatedLine, generatedColumn } = generatedLocation;
@ -2967,10 +3152,6 @@ SourceActor.prototype = {
}
actor.addScript(script, this.threadActor);
}
return {
actor: actor.actorID
};
},
/**

View File

@ -79,6 +79,12 @@ ScriptStore.prototype = {
return this._scripts.items;
},
getScriptsBySourceActor(sourceActor) {
return sourceActor.source ?
this.getScriptsBySource(sourceActor.source) :
this.getScriptsByURL(sourceActor._originalUrl);
},
getScriptsBySourceActorAndLine(sourceActor, line) {
return sourceActor.source ?
this.getScriptsBySourceAndLine(sourceActor.source, line) :