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