1*6777b538SAndroid Build Coastguard Worker// Copyright 2014 The Chromium Authors 2*6777b538SAndroid Build Coastguard Worker// Use of this source code is governed by a BSD-style license that can be 3*6777b538SAndroid Build Coastguard Worker// found in the LICENSE file. 4*6777b538SAndroid Build Coastguard Worker 5*6777b538SAndroid Build Coastguard Worker#import "base/ios/crb_protocol_observers.h" 6*6777b538SAndroid Build Coastguard Worker 7*6777b538SAndroid Build Coastguard Worker#include <objc/runtime.h> 8*6777b538SAndroid Build Coastguard Worker#include <stddef.h> 9*6777b538SAndroid Build Coastguard Worker 10*6777b538SAndroid Build Coastguard Worker#include <vector> 11*6777b538SAndroid Build Coastguard Worker 12*6777b538SAndroid Build Coastguard Worker#include "base/check.h" 13*6777b538SAndroid Build Coastguard Worker#include "base/containers/contains.h" 14*6777b538SAndroid Build Coastguard Worker#include "base/notreached.h" 15*6777b538SAndroid Build Coastguard Worker#include "base/ranges/algorithm.h" 16*6777b538SAndroid Build Coastguard Worker 17*6777b538SAndroid Build Coastguard Worker@interface CRBProtocolObservers () { 18*6777b538SAndroid Build Coastguard Worker Protocol* _protocol; 19*6777b538SAndroid Build Coastguard Worker // ivars declared here are private to the implementation but must be 20*6777b538SAndroid Build Coastguard Worker // public for allowing the C++ |Iterator| class access to those ivars. 21*6777b538SAndroid Build Coastguard Worker @public 22*6777b538SAndroid Build Coastguard Worker // vector of weak pointers to observers. 23*6777b538SAndroid Build Coastguard Worker std::vector<__weak id> _observers; 24*6777b538SAndroid Build Coastguard Worker // The nested level of observer iteration. 25*6777b538SAndroid Build Coastguard Worker // A depth of 0 means nobody is currently iterating on the list of observers. 26*6777b538SAndroid Build Coastguard Worker int _invocationDepth; 27*6777b538SAndroid Build Coastguard Worker} 28*6777b538SAndroid Build Coastguard Worker 29*6777b538SAndroid Build Coastguard Worker// Removes nil observers from the list and is called when the 30*6777b538SAndroid Build Coastguard Worker// |_invocationDepth| reaches 0. 31*6777b538SAndroid Build Coastguard Worker- (void)compact; 32*6777b538SAndroid Build Coastguard Worker 33*6777b538SAndroid Build Coastguard Worker@end 34*6777b538SAndroid Build Coastguard Worker 35*6777b538SAndroid Build Coastguard Workernamespace { 36*6777b538SAndroid Build Coastguard Worker 37*6777b538SAndroid Build Coastguard Workerclass Iterator { 38*6777b538SAndroid Build Coastguard Worker public: 39*6777b538SAndroid Build Coastguard Worker explicit Iterator(CRBProtocolObservers* protocol_observers); 40*6777b538SAndroid Build Coastguard Worker ~Iterator(); 41*6777b538SAndroid Build Coastguard Worker id GetNext(); 42*6777b538SAndroid Build Coastguard Worker 43*6777b538SAndroid Build Coastguard Worker private: 44*6777b538SAndroid Build Coastguard Worker CRBProtocolObservers* protocol_observers_; 45*6777b538SAndroid Build Coastguard Worker size_t index_; 46*6777b538SAndroid Build Coastguard Worker size_t max_index_; 47*6777b538SAndroid Build Coastguard Worker}; 48*6777b538SAndroid Build Coastguard Worker 49*6777b538SAndroid Build Coastguard WorkerIterator::Iterator(CRBProtocolObservers* protocol_observers) 50*6777b538SAndroid Build Coastguard Worker : protocol_observers_(protocol_observers), 51*6777b538SAndroid Build Coastguard Worker index_(0), 52*6777b538SAndroid Build Coastguard Worker max_index_(protocol_observers->_observers.size()) { 53*6777b538SAndroid Build Coastguard Worker DCHECK(protocol_observers_); 54*6777b538SAndroid Build Coastguard Worker ++protocol_observers->_invocationDepth; 55*6777b538SAndroid Build Coastguard Worker} 56*6777b538SAndroid Build Coastguard Worker 57*6777b538SAndroid Build Coastguard WorkerIterator::~Iterator() { 58*6777b538SAndroid Build Coastguard Worker if (protocol_observers_ && --protocol_observers_->_invocationDepth == 0) 59*6777b538SAndroid Build Coastguard Worker [protocol_observers_ compact]; 60*6777b538SAndroid Build Coastguard Worker} 61*6777b538SAndroid Build Coastguard Worker 62*6777b538SAndroid Build Coastguard Workerid Iterator::GetNext() { 63*6777b538SAndroid Build Coastguard Worker if (!protocol_observers_) 64*6777b538SAndroid Build Coastguard Worker return nil; 65*6777b538SAndroid Build Coastguard Worker auto& observers = protocol_observers_->_observers; 66*6777b538SAndroid Build Coastguard Worker // Skip nil elements. 67*6777b538SAndroid Build Coastguard Worker size_t max_index = std::min(max_index_, observers.size()); 68*6777b538SAndroid Build Coastguard Worker while (index_ < max_index && !observers[index_]) 69*6777b538SAndroid Build Coastguard Worker ++index_; 70*6777b538SAndroid Build Coastguard Worker return index_ < max_index ? observers[index_++] : nil; 71*6777b538SAndroid Build Coastguard Worker} 72*6777b538SAndroid Build Coastguard Worker} 73*6777b538SAndroid Build Coastguard Worker 74*6777b538SAndroid Build Coastguard Worker@interface CRBProtocolObservers () 75*6777b538SAndroid Build Coastguard Worker 76*6777b538SAndroid Build Coastguard Worker// Designated initializer. 77*6777b538SAndroid Build Coastguard Worker- (instancetype)initWithProtocol:(Protocol*)protocol; 78*6777b538SAndroid Build Coastguard Worker 79*6777b538SAndroid Build Coastguard Worker@end 80*6777b538SAndroid Build Coastguard Worker 81*6777b538SAndroid Build Coastguard Worker@implementation CRBProtocolObservers 82*6777b538SAndroid Build Coastguard Worker 83*6777b538SAndroid Build Coastguard Worker+ (instancetype)observersWithProtocol:(Protocol*)protocol { 84*6777b538SAndroid Build Coastguard Worker return [[self alloc] initWithProtocol:protocol]; 85*6777b538SAndroid Build Coastguard Worker} 86*6777b538SAndroid Build Coastguard Worker 87*6777b538SAndroid Build Coastguard Worker- (id)init { 88*6777b538SAndroid Build Coastguard Worker NOTREACHED(); 89*6777b538SAndroid Build Coastguard Worker return nil; 90*6777b538SAndroid Build Coastguard Worker} 91*6777b538SAndroid Build Coastguard Worker 92*6777b538SAndroid Build Coastguard Worker- (id)initWithProtocol:(Protocol*)protocol { 93*6777b538SAndroid Build Coastguard Worker self = [super init]; 94*6777b538SAndroid Build Coastguard Worker if (self) { 95*6777b538SAndroid Build Coastguard Worker _protocol = protocol; 96*6777b538SAndroid Build Coastguard Worker } 97*6777b538SAndroid Build Coastguard Worker return self; 98*6777b538SAndroid Build Coastguard Worker} 99*6777b538SAndroid Build Coastguard Worker 100*6777b538SAndroid Build Coastguard Worker- (Protocol*)protocol { 101*6777b538SAndroid Build Coastguard Worker return _protocol; 102*6777b538SAndroid Build Coastguard Worker} 103*6777b538SAndroid Build Coastguard Worker 104*6777b538SAndroid Build Coastguard Worker- (void)addObserver:(id)observer { 105*6777b538SAndroid Build Coastguard Worker DCHECK(observer); 106*6777b538SAndroid Build Coastguard Worker DCHECK([observer conformsToProtocol:self.protocol]); 107*6777b538SAndroid Build Coastguard Worker 108*6777b538SAndroid Build Coastguard Worker if (base::Contains(_observers, observer)) 109*6777b538SAndroid Build Coastguard Worker return; 110*6777b538SAndroid Build Coastguard Worker 111*6777b538SAndroid Build Coastguard Worker _observers.push_back(observer); 112*6777b538SAndroid Build Coastguard Worker} 113*6777b538SAndroid Build Coastguard Worker 114*6777b538SAndroid Build Coastguard Worker- (void)removeObserver:(id)observer { 115*6777b538SAndroid Build Coastguard Worker DCHECK(observer); 116*6777b538SAndroid Build Coastguard Worker auto it = base::ranges::find(_observers, observer); 117*6777b538SAndroid Build Coastguard Worker if (it != _observers.end()) { 118*6777b538SAndroid Build Coastguard Worker if (_invocationDepth) 119*6777b538SAndroid Build Coastguard Worker *it = nil; 120*6777b538SAndroid Build Coastguard Worker else 121*6777b538SAndroid Build Coastguard Worker _observers.erase(it); 122*6777b538SAndroid Build Coastguard Worker } 123*6777b538SAndroid Build Coastguard Worker} 124*6777b538SAndroid Build Coastguard Worker 125*6777b538SAndroid Build Coastguard Worker- (BOOL)empty { 126*6777b538SAndroid Build Coastguard Worker int count = 0; 127*6777b538SAndroid Build Coastguard Worker for (id observer : _observers) { 128*6777b538SAndroid Build Coastguard Worker if (observer != nil) 129*6777b538SAndroid Build Coastguard Worker ++count; 130*6777b538SAndroid Build Coastguard Worker } 131*6777b538SAndroid Build Coastguard Worker return count == 0; 132*6777b538SAndroid Build Coastguard Worker} 133*6777b538SAndroid Build Coastguard Worker 134*6777b538SAndroid Build Coastguard Worker#pragma mark - NSObject 135*6777b538SAndroid Build Coastguard Worker 136*6777b538SAndroid Build Coastguard Worker- (NSMethodSignature*)methodSignatureForSelector:(SEL)selector { 137*6777b538SAndroid Build Coastguard Worker NSMethodSignature* signature = [super methodSignatureForSelector:selector]; 138*6777b538SAndroid Build Coastguard Worker if (signature) 139*6777b538SAndroid Build Coastguard Worker return signature; 140*6777b538SAndroid Build Coastguard Worker 141*6777b538SAndroid Build Coastguard Worker // Look for a required method in the protocol. protocol_getMethodDescription 142*6777b538SAndroid Build Coastguard Worker // returns a struct whose fields are null if a method for the selector was 143*6777b538SAndroid Build Coastguard Worker // not found. 144*6777b538SAndroid Build Coastguard Worker struct objc_method_description description = 145*6777b538SAndroid Build Coastguard Worker protocol_getMethodDescription(self.protocol, selector, YES, YES); 146*6777b538SAndroid Build Coastguard Worker if (description.types) 147*6777b538SAndroid Build Coastguard Worker return [NSMethodSignature signatureWithObjCTypes:description.types]; 148*6777b538SAndroid Build Coastguard Worker 149*6777b538SAndroid Build Coastguard Worker // Look for an optional method in the protocol. 150*6777b538SAndroid Build Coastguard Worker description = protocol_getMethodDescription(self.protocol, selector, NO, YES); 151*6777b538SAndroid Build Coastguard Worker if (description.types) 152*6777b538SAndroid Build Coastguard Worker return [NSMethodSignature signatureWithObjCTypes:description.types]; 153*6777b538SAndroid Build Coastguard Worker 154*6777b538SAndroid Build Coastguard Worker // There is neither a required nor optional method with this selector in the 155*6777b538SAndroid Build Coastguard Worker // protocol, so invoke -[NSObject doesNotRecognizeSelector:] to raise 156*6777b538SAndroid Build Coastguard Worker // NSInvalidArgumentException. 157*6777b538SAndroid Build Coastguard Worker [self doesNotRecognizeSelector:selector]; 158*6777b538SAndroid Build Coastguard Worker return nil; 159*6777b538SAndroid Build Coastguard Worker} 160*6777b538SAndroid Build Coastguard Worker 161*6777b538SAndroid Build Coastguard Worker- (void)forwardInvocation:(NSInvocation*)invocation { 162*6777b538SAndroid Build Coastguard Worker DCHECK(invocation); 163*6777b538SAndroid Build Coastguard Worker if (_observers.empty()) 164*6777b538SAndroid Build Coastguard Worker return; 165*6777b538SAndroid Build Coastguard Worker SEL selector = [invocation selector]; 166*6777b538SAndroid Build Coastguard Worker Iterator it(self); 167*6777b538SAndroid Build Coastguard Worker id observer; 168*6777b538SAndroid Build Coastguard Worker while ((observer = it.GetNext()) != nil) { 169*6777b538SAndroid Build Coastguard Worker if ([observer respondsToSelector:selector]) 170*6777b538SAndroid Build Coastguard Worker [invocation invokeWithTarget:observer]; 171*6777b538SAndroid Build Coastguard Worker } 172*6777b538SAndroid Build Coastguard Worker} 173*6777b538SAndroid Build Coastguard Worker 174*6777b538SAndroid Build Coastguard Worker- (void)executeOnObservers:(ExecutionWithObserverBlock)callback { 175*6777b538SAndroid Build Coastguard Worker DCHECK(callback); 176*6777b538SAndroid Build Coastguard Worker if (_observers.empty()) 177*6777b538SAndroid Build Coastguard Worker return; 178*6777b538SAndroid Build Coastguard Worker Iterator it(self); 179*6777b538SAndroid Build Coastguard Worker id observer; 180*6777b538SAndroid Build Coastguard Worker while ((observer = it.GetNext()) != nil) 181*6777b538SAndroid Build Coastguard Worker callback(observer); 182*6777b538SAndroid Build Coastguard Worker} 183*6777b538SAndroid Build Coastguard Worker 184*6777b538SAndroid Build Coastguard Worker#pragma mark - Private 185*6777b538SAndroid Build Coastguard Worker 186*6777b538SAndroid Build Coastguard Worker- (void)compact { 187*6777b538SAndroid Build Coastguard Worker DCHECK(!_invocationDepth); 188*6777b538SAndroid Build Coastguard Worker _observers.erase(std::remove(_observers.begin(), _observers.end(), nil), 189*6777b538SAndroid Build Coastguard Worker _observers.end()); 190*6777b538SAndroid Build Coastguard Worker} 191*6777b538SAndroid Build Coastguard Worker 192*6777b538SAndroid Build Coastguard Worker@end 193