Roll src/third_party/WebKit b0b593a:a9a555c (svn 202065:202066)
[chromium-blink-merge.git] / base / ios / crb_protocol_observers.mm
blob90a277ba3f455fdb6a54c40ccaea4553296aca90
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #import "base/ios/crb_protocol_observers.h"
7 #include <objc/runtime.h>
8 #include <algorithm>
9 #include <vector>
11 #include "base/logging.h"
12 #include "base/mac/scoped_nsobject.h"
14 @interface CRBProtocolObservers () {
15   base::scoped_nsobject<Protocol> _protocol;
16   // ivars declared here are private to the implementation but must be
17   // public for allowing the C++ |Iterator| class access to those ivars.
18  @public
19   // vector of weak pointers to observers.
20   std::vector<__unsafe_unretained id> _observers;
21   // The nested level of observer iteration.
22   // A depth of 0 means nobody is currently iterating on the list of observers.
23   int _invocationDepth;
26 // Removes nil observers from the list and is called when the
27 // |_invocationDepth| reaches 0.
28 - (void)compact;
30 @end
32 namespace {
34 class Iterator {
35  public:
36   explicit Iterator(CRBProtocolObservers* protocol_observers);
37   ~Iterator();
38   id GetNext();
40  private:
41   CRBProtocolObservers* protocol_observers_;
42   size_t index_;
43   size_t max_index_;
46 Iterator::Iterator(CRBProtocolObservers* protocol_observers)
47     : protocol_observers_(protocol_observers),
48       index_(0),
49       max_index_(protocol_observers->_observers.size()) {
50   DCHECK(protocol_observers_);
51   ++protocol_observers->_invocationDepth;
54 Iterator::~Iterator() {
55   if (protocol_observers_ && --protocol_observers_->_invocationDepth == 0)
56     [protocol_observers_ compact];
59 id Iterator::GetNext() {
60   if (!protocol_observers_)
61     return nil;
62   auto& observers = protocol_observers_->_observers;
63   // Skip nil elements.
64   size_t max_index = std::min(max_index_, observers.size());
65   while (index_ < max_index && !observers[index_])
66     ++index_;
67   return index_ < max_index ? observers[index_++] : nil;
71 @interface CRBProtocolObservers ()
73 // Designated initializer.
74 - (id)initWithProtocol:(Protocol*)protocol;
76 @end
78 @implementation CRBProtocolObservers
80 + (instancetype)observersWithProtocol:(Protocol*)protocol {
81   return [[[self alloc] initWithProtocol:protocol] autorelease];
84 - (id)init {
85   NOTREACHED();
86   return nil;
89 - (id)initWithProtocol:(Protocol*)protocol {
90   self = [super init];
91   if (self) {
92     _protocol.reset([protocol retain]);
93   }
94   return self;
97 - (Protocol*)protocol {
98   return _protocol.get();
101 - (void)addObserver:(id)observer {
102   DCHECK(observer);
103   DCHECK([observer conformsToProtocol:self.protocol]);
105   if (std::find(_observers.begin(), _observers.end(), observer) !=
106       _observers.end())
107     return;
109   _observers.push_back(observer);
112 - (void)removeObserver:(id)observer {
113   DCHECK(observer);
114   auto it = std::find(_observers.begin(), _observers.end(), observer);
115   if (it != _observers.end()) {
116     if (_invocationDepth)
117       *it = nil;
118     else
119       _observers.erase(it);
120   }
123 - (BOOL)empty {
124   int count = 0;
125   for (id observer : _observers) {
126     if (observer != nil)
127       ++count;
128   }
129   return count == 0;
132 #pragma mark - NSObject
134 - (NSMethodSignature*)methodSignatureForSelector:(SEL)selector {
135   NSMethodSignature* signature = [super methodSignatureForSelector:selector];
136   if (signature)
137     return signature;
139   // Look for a required method in the protocol. protocol_getMethodDescription
140   // returns a struct whose fields are null if a method for the selector was
141   // not found.
142   struct objc_method_description description =
143       protocol_getMethodDescription(self.protocol, selector, YES, YES);
144   if (description.types)
145     return [NSMethodSignature signatureWithObjCTypes:description.types];
147   // Look for an optional method in the protocol.
148   description = protocol_getMethodDescription(self.protocol, selector, NO, YES);
149   if (description.types)
150     return [NSMethodSignature signatureWithObjCTypes:description.types];
152   // There is neither a required nor optional method with this selector in the
153   // protocol, so invoke -[NSObject doesNotRecognizeSelector:] to raise
154   // NSInvalidArgumentException.
155   [self doesNotRecognizeSelector:selector];
156   return nil;
159 - (void)forwardInvocation:(NSInvocation*)invocation {
160   DCHECK(invocation);
161   if (_observers.empty())
162     return;
163   SEL selector = [invocation selector];
164   Iterator it(self);
165   id observer;
166   while ((observer = it.GetNext()) != nil) {
167     if ([observer respondsToSelector:selector])
168       [invocation invokeWithTarget:observer];
169   }
172 - (void)executeOnObservers:(ExecutionWithObserverBlock)callback {
173   DCHECK(callback);
174   if (_observers.empty())
175     return;
176   Iterator it(self);
177   id observer;
178   while ((observer = it.GetNext()) != nil)
179     callback(observer);
182 #pragma mark - Private
184 - (void)compact {
185   DCHECK(!_invocationDepth);
186   _observers.erase(std::remove(_observers.begin(), _observers.end(), nil),
187                    _observers.end());
190 @end