blob: c11be4e8dc2b6aae285d8685dc0cc814255e0ea0 [file] [log] [blame]
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/events/devices/input_device_observer_ios.h"
#import <Foundation/Foundation.h>
#include "base/check.h"
#include "base/files/file_path.h"
#include "base/memory/singleton.h"
#include "base/scoped_native_library.h"
#include "build/blink_buildflags.h"
#if !BUILDFLAG(USE_BLINK)
#error File can only be included when USE_BLINK is true
#endif
@class BKSMousePointerDevice;
// The BKSMousePointerDeviceObserver and BKSMousePointerService classes are part
// of the private BackBoardServices.framework. The declarations are sourced from
// WebKit/Source/WebKit/Platform/spi/ios/BackBoardServicesSPI.h.
@protocol BKSMousePointerDeviceObserver <NSObject>
@optional
- (void)mousePointerDevicesDidChange:
(NSSet<BKSMousePointerDevice*>*)mousePointerDevices;
@end
@interface BKSMousePointerService : NSObject
+ (BKSMousePointerService*)sharedInstance;
- (id)addPointerDeviceObserver:(id<BKSMousePointerDeviceObserver>)observer;
@end
// The MouseDeviceObserverIOS class is implemented in WebKit's
// Source/WebKit/UIProcess/ios/WKMouseDeviceObserver.mm and relies on the
// private BackBoardServices.framework. As a result, the current version is not
// shippable until it is replaced with a public API that Apple will release, as
// noted in crbug.com/379764624.
@interface MouseDeviceObserverIOS : NSObject <BKSMousePointerDeviceObserver>
+ (MouseDeviceObserverIOS*)sharedInstance;
+ (instancetype)new NS_UNAVAILABLE;
- (instancetype)init NS_UNAVAILABLE;
- (void)start;
- (void)stop;
@end
@implementation MouseDeviceObserverIOS {
BOOL _hasMouseDevice;
size_t _startCount;
__strong id _token;
dispatch_queue_t _deviceObserverTokenQueue;
}
+ (MouseDeviceObserverIOS*)sharedInstance {
static MouseDeviceObserverIOS* instance = nil;
static dispatch_once_t onceToken = 0;
dispatch_once(&onceToken, ^{
instance = [[MouseDeviceObserverIOS alloc] init];
});
return instance;
}
- (instancetype)init {
if ((self = [super init])) {
_deviceObserverTokenQueue = dispatch_queue_create(
"MouseDeviceObserverIOS _deviceObserverTokenQueue",
DISPATCH_QUEUE_SERIAL);
}
return self;
}
- (void)start {
if (++_startCount > 1) {
return;
}
__weak MouseDeviceObserverIOS* weakSelf = self;
dispatch_async(_deviceObserverTokenQueue, ^{
__strong MouseDeviceObserverIOS* strongSelf = weakSelf;
if (!strongSelf) {
return;
}
base::ScopedNativeLibrary library = base::ScopedNativeLibrary(
base::FilePath("/System/Library/PrivateFrameworks/"
"BackBoardServices.framework/BackBoardServices"));
DCHECK(library.is_valid());
Class serviceClass = NSClassFromString(@"BKSMousePointerService");
DCHECK(serviceClass);
BKSMousePointerService* mousePointerInstance =
(BKSMousePointerService*)[serviceClass sharedInstance];
DCHECK(mousePointerInstance);
strongSelf->_token =
[mousePointerInstance addPointerDeviceObserver:strongSelf];
});
}
- (void)stop {
if (!_startCount || --_startCount) {
return;
}
__weak MouseDeviceObserverIOS* weakSelf = self;
dispatch_async(_deviceObserverTokenQueue, ^{
__strong MouseDeviceObserverIOS* strongSelf = weakSelf;
if (!strongSelf) {
return;
}
if (strongSelf->_token) {
if ([strongSelf->_token respondsToSelector:@selector(invalidate)]) {
[strongSelf->_token invalidate];
}
strongSelf->_token = nil;
}
});
}
#pragma mark - BKSMousePointerDeviceObserver handlers
- (void)mousePointerDevicesDidChange:
(NSSet<BKSMousePointerDevice*>*)mousePointerDevices {
BOOL hasMouseDevice = mousePointerDevices.count > 0;
if (hasMouseDevice == _hasMouseDevice) {
return;
}
_hasMouseDevice = hasMouseDevice;
ui::InputDeviceObserverIOS::GetInstance()
->NotifyObserversDeviceConfigurationChanged(hasMouseDevice);
}
@end
namespace ui {
InputDeviceObserverIOS::InputDeviceObserverIOS() = default;
InputDeviceObserverIOS::~InputDeviceObserverIOS() = default;
InputDeviceObserverIOS* InputDeviceObserverIOS::GetInstance() {
return base::Singleton<
InputDeviceObserverIOS,
base::LeakySingletonTraits<InputDeviceObserverIOS>>::get();
}
void InputDeviceObserverIOS::AddObserver(
ui::InputDeviceEventObserver* observer) {
if (observers_.empty()) {
[[MouseDeviceObserverIOS sharedInstance] start];
}
observers_.AddObserver(observer);
}
void InputDeviceObserverIOS::RemoveObserver(
ui::InputDeviceEventObserver* observer) {
observers_.RemoveObserver(observer);
if (observers_.empty()) {
[[MouseDeviceObserverIOS sharedInstance] stop];
}
}
void InputDeviceObserverIOS::NotifyObserversDeviceConfigurationChanged(
bool has_mouse_device) {
if (has_mouse_device_ == has_mouse_device) {
return;
}
has_mouse_device_ = has_mouse_device;
observers_.Notify(
&ui::InputDeviceEventObserver::OnInputDeviceConfigurationChanged,
InputDeviceEventObserver::kMouse);
}
} // namespace ui