Index: content/renderer/bluetooth/bluetooth_dispatcher.cc |
diff --git a/content/renderer/bluetooth/bluetooth_dispatcher.cc b/content/renderer/bluetooth/bluetooth_dispatcher.cc |
index 1a4679e6d63291c6cb450c564ed1cd8f98f109ac..2e83e4fd3e49219fdf56ffdc9e82a63438554c0c 100644 |
--- a/content/renderer/bluetooth/bluetooth_dispatcher.cc |
+++ b/content/renderer/bluetooth/bluetooth_dispatcher.cc |
@@ -15,6 +15,7 @@ |
#include "third_party/WebKit/public/platform/modules/bluetooth/WebBluetoothDevice.h" |
#include "third_party/WebKit/public/platform/modules/bluetooth/WebBluetoothError.h" |
#include "third_party/WebKit/public/platform/modules/bluetooth/WebBluetoothGATTCharacteristic.h" |
+#include "third_party/WebKit/public/platform/modules/bluetooth/WebBluetoothGATTCharacteristicDelegate.h" |
#include "third_party/WebKit/public/platform/modules/bluetooth/WebBluetoothGATTRemoteServer.h" |
#include "third_party/WebKit/public/platform/modules/bluetooth/WebBluetoothGATTService.h" |
#include "third_party/WebKit/public/platform/modules/bluetooth/WebRequestDeviceOptions.h" |
@@ -62,6 +63,25 @@ struct BluetoothCharacteristicRequest { |
scoped_ptr<blink::WebBluetoothGetCharacteristicCallbacks> callbacks; |
}; |
+struct BluetoothNotificationsRequest { |
+ BluetoothNotificationsRequest( |
+ blink::WebString characteristic_instance_id, |
+ blink::WebBluetoothGATTCharacteristicDelegate* delegate, |
+ blink::WebBluetoothNotificationsCallbacks* callbacks) |
+ : characteristic_instance_id(characteristic_instance_id), |
+ delegate(delegate), |
+ callbacks(callbacks) {} |
+ ~BluetoothNotificationsRequest() {} |
+ |
+ blink::WebString characteristic_instance_id; |
+ // Note that the delegate is owned by the execution context on the blink |
+ // side which can destroy the delegate at any point. Since the delegate |
+ // implements ActiveDOMObject we do get notify when it's being destroyed, |
+ // so we can remove any references to it. |
+ blink::WebBluetoothGATTCharacteristicDelegate* delegate; |
+ scoped_ptr<blink::WebBluetoothNotificationsCallbacks> callbacks; |
+}; |
+ |
namespace content { |
namespace { |
@@ -143,6 +163,12 @@ void BluetoothDispatcher::OnMessageReceived(const IPC::Message& msg) { |
OnWriteValueSuccess); |
IPC_MESSAGE_HANDLER(BluetoothMsg_WriteCharacteristicValueError, |
OnWriteValueError); |
+ IPC_MESSAGE_HANDLER(BluetoothMsg_StartNotificationsSuccess, |
+ OnStartNotificationsSuccess) |
+ IPC_MESSAGE_HANDLER(BluetoothMsg_StartNotificationsError, |
+ OnStartNotificationsError) |
+ IPC_MESSAGE_HANDLER(BluetoothMsg_StopNotificationsSuccess, |
+ OnStopNotificationsSuccess) |
IPC_MESSAGE_UNHANDLED(handled = false) |
IPC_END_MESSAGE_MAP() |
DCHECK(handled) << "Unhandled message:" << msg.type(); |
@@ -225,10 +251,145 @@ void BluetoothDispatcher::writeValue( |
CurrentWorkerId(), request_id, characteristic_instance_id.utf8(), value)); |
} |
+void BluetoothDispatcher::startNotifications( |
+ const blink::WebString& characteristic_instance_id, |
+ blink::WebBluetoothGATTCharacteristicDelegate* delegate, |
+ blink::WebBluetoothNotificationsCallbacks* callbacks) { |
+ // If an object is already subcribed to notifications from the characteristic, |
+ // no need to subscribe to the dispatcher host again. |
+ if (HasActiveCharacteristicNotification(characteristic_instance_id)) { |
+ AddToActiveNotificationsMap(characteristic_instance_id, delegate); |
+ callbacks->onSuccess(); |
+ return; |
+ } |
+ |
+ int request_id = pending_start_notifications_requests_.Add( |
+ new BluetoothNotificationsRequest(characteristic_instance_id, delegate, |
+ callbacks)); |
+ |
+ Send(new BluetoothHostMsg_StartNotifications( |
+ CurrentWorkerId(), request_id, characteristic_instance_id.utf8())); |
+} |
+ |
+void BluetoothDispatcher::stopNotifications( |
+ const blink::WebString& characteristic_instance_id, |
+ blink::WebBluetoothGATTCharacteristicDelegate* delegate, |
+ blink::WebBluetoothNotificationsCallbacks* callbacks) { |
+ // If this is not an active characteristic then no need to unsubscribe. |
+ if (!HasActiveCharacteristicNotification(characteristic_instance_id)) { |
+ callbacks->onSuccess(); |
+ return; |
+ } |
+ |
+ if (WillBeActiveNotificationWhenRemoved(characteristic_instance_id, |
+ delegate)) { |
+ RemoveFromActiveNotificationsMap(characteristic_instance_id, delegate); |
+ callbacks->onSuccess(); |
+ return; |
+ } |
+ |
+ int request_id = pending_stop_notifications_requests_.Add( |
+ new BluetoothNotificationsRequest(characteristic_instance_id, delegate, |
+ callbacks)); |
+ |
+ Send(new BluetoothHostMsg_StopNotifications( |
+ CurrentWorkerId(), request_id, characteristic_instance_id.utf8())); |
+} |
+ |
+void BluetoothDispatcher::characteristicDelegateRemoved( |
+ const blink::WebString& characteristic_instance_id, |
+ blink::WebBluetoothGATTCharacteristicDelegate* delegate) { |
+ RemoveFromActiveNotificationsMap(characteristic_instance_id, delegate); |
+ |
+ // If the characteristic is still active no need to unsubscribe. |
+ if (HasActiveCharacteristicNotification(characteristic_instance_id)) { |
+ return; |
+ } |
+ |
+ // There could be a request pending on a delegate so we need to check |
+ // the requests map. We ignore the stop request since we don't use |
+ // the delegate in those. |
+ for (IDMap<BluetoothNotificationsRequest, IDMapOwnPointer>::iterator iter( |
+ &pending_start_notifications_requests_); |
+ !iter.IsAtEnd(); iter.Advance()) { |
+ iter.GetCurrentValue()->delegate = nullptr; |
+ } |
+} |
+ |
void BluetoothDispatcher::WillStopCurrentWorkerThread() { |
delete this; |
} |
+bool BluetoothDispatcher::HasActiveCharacteristicNotification( |
+ const blink::WebString& characteristic_instance_id) { |
+ auto char_iter = active_characteristic_notifications.find( |
+ characteristic_instance_id.utf8()); |
+ |
+ // An active characteristic will point to a set of delegates. |
+ if (char_iter == active_characteristic_notifications.end()) { |
+ return false; |
+ } |
+ |
+ return true; |
+} |
+ |
+void BluetoothDispatcher::AddToActiveNotificationsMap( |
+ const blink::WebString& characteristic_instance_id, |
+ blink::WebBluetoothGATTCharacteristicDelegate* delegate) { |
+ auto char_iter = active_characteristic_notifications.find( |
+ characteristic_instance_id.utf8()); |
+ |
+ // First object that subscribes to notifications for the characteristic. |
+ if (char_iter == active_characteristic_notifications.end()) { |
+ std::set<blink::WebBluetoothGATTCharacteristicDelegate*> delegates; |
+ delegates.insert(delegate); |
+ active_characteristic_notifications.insert( |
+ std::make_pair(characteristic_instance_id.utf8(), delegates)); |
+ return; |
+ } |
+ // Otherwise add to already existing set. |
+ char_iter->second.insert(delegate); |
+} |
+ |
+bool BluetoothDispatcher::WillBeActiveNotificationWhenRemoved( |
+ const blink::WebString& characteristic_instance_id, |
+ blink::WebBluetoothGATTCharacteristicDelegate* delegate) { |
+ auto char_iter = active_characteristic_notifications.find( |
+ characteristic_instance_id.utf8()); |
+ |
+ // The characteristic will point to a set as long as there is at least |
+ // one delegate subscribed to notifications. |
+ if (char_iter == active_characteristic_notifications.end()) { |
+ return false; |
+ } |
+ const std::set<blink::WebBluetoothGATTCharacteristicDelegate*>& delegate_set = |
+ char_iter->second; |
+ // If we find the delegate and the size of the set is one then removing |
+ // the delegate will make the notification inactive. |
+ if (delegate_set.find(delegate) != delegate_set.end() && |
+ delegate_set.size() == 1) { |
+ return false; |
+ } |
+ return true; |
+} |
+ |
+void BluetoothDispatcher::RemoveFromActiveNotificationsMap( |
+ const blink::WebString& characteristic_instance_id, |
+ blink::WebBluetoothGATTCharacteristicDelegate* delegate) { |
+ auto char_iter = active_characteristic_notifications.find( |
+ characteristic_instance_id.utf8()); |
+ |
+ if (char_iter == active_characteristic_notifications.end()) { |
+ return; |
+ } |
+ |
+ char_iter->second.erase(delegate); |
+ |
+ if (char_iter->second.size() == 0) { |
+ active_characteristic_notifications.erase(char_iter); |
+ } |
+} |
+ |
void BluetoothDispatcher::OnRequestDeviceSuccess( |
int thread_id, |
int request_id, |
@@ -386,4 +547,52 @@ void BluetoothDispatcher::OnWriteValueError(int thread_id, |
pending_write_value_requests_.Remove(request_id); |
} |
+void BluetoothDispatcher::OnStartNotificationsSuccess(int thread_id, |
+ int request_id) { |
+ DCHECK(pending_start_notifications_requests_.Lookup(request_id)) |
+ << request_id; |
+ |
+ BluetoothNotificationsRequest* request = |
+ pending_start_notifications_requests_.Lookup(request_id); |
+ |
+ // The object requesting the notification could have been destroyed |
+ // while waiting for the subscription. characteristicDelegateRemoved |
+ // nulls the delegate when the delegate gets destroyed. |
+ if (request->delegate != nullptr) { |
ortuno
2015/09/16 16:30:08
Argh. This doesn't work. If the delegate gets dest
|
+ AddToActiveNotificationsMap(request->characteristic_instance_id, |
+ request->delegate); |
+ } |
+ request->callbacks->onSuccess(); |
+ |
+ pending_start_notifications_requests_.Remove(request_id); |
+} |
+ |
+void BluetoothDispatcher::OnStartNotificationsError(int thread_id, |
+ int request_id, |
+ WebBluetoothError error) { |
+ DCHECK(pending_start_notifications_requests_.Lookup(request_id)) |
+ << request_id; |
+ |
+ pending_start_notifications_requests_.Lookup(request_id) |
+ ->callbacks->onError(error); |
+ |
+ pending_start_notifications_requests_.Remove(request_id); |
+} |
+ |
+void BluetoothDispatcher::OnStopNotificationsSuccess(int thread_id, |
+ int request_id) { |
+ DCHECK(pending_stop_notifications_requests_.Lookup(request_id)) << request_id; |
+ |
+ BluetoothNotificationsRequest* request = |
+ pending_stop_notifications_requests_.Lookup(request_id); |
+ |
+ RemoveFromActiveNotificationsMap(request->characteristic_instance_id, |
+ request->delegate); |
+ |
+ pending_stop_notifications_requests_.Lookup(request_id) |
+ ->callbacks->onSuccess(); |
+ |
+ pending_stop_notifications_requests_.Remove(request_id); |
+} |
+ |
} // namespace content |