xref: /aosp_15_r20/external/cronet/base/ios/crb_protocol_observers.mm (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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