Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2012 The Chromium Authors. All rights reserved. | 1 // Copyright 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #import "ios/net/crn_http_protocol_handler_proxy_with_client_thread.h" | 5 #import "ios/net/crn_http_protocol_handler_proxy_with_client_thread.h" |
| 6 | 6 |
| 7 #include "base/logging.h" | 7 #include "base/logging.h" |
| 8 #import "base/mac/scoped_nsobject.h" | 8 #import "base/mac/scoped_nsobject.h" |
| 9 #include "base/time/time.h" | 9 #include "base/time/time.h" |
| 10 #import "ios/net/protocol_handler_util.h" | 10 #import "ios/net/protocol_handler_util.h" |
| (...skipping 18 matching lines...) Expand all Loading... | |
| 29 __weak NSThread* _clientThread; | 29 __weak NSThread* _clientThread; |
| 30 // The run loop modes to use when posting tasks to |clientThread_|. | 30 // The run loop modes to use when posting tasks to |clientThread_|. |
| 31 base::scoped_nsobject<NSArray> _runLoopModes; | 31 base::scoped_nsobject<NSArray> _runLoopModes; |
| 32 // The request URL. | 32 // The request URL. |
| 33 base::scoped_nsobject<NSString> _url; | 33 base::scoped_nsobject<NSString> _url; |
| 34 // The creation time of the request. | 34 // The creation time of the request. |
| 35 base::Time _creationTime; | 35 base::Time _creationTime; |
| 36 // |requestComplete_| is used in debug to check that the client is not called | 36 // |requestComplete_| is used in debug to check that the client is not called |
| 37 // after completion. | 37 // after completion. |
| 38 BOOL _requestComplete; | 38 BOOL _requestComplete; |
| 39 BOOL _paused; | |
| 40 | |
| 41 base::scoped_nsobject<NSMutableArray> _queuedInvocations; | |
| 39 } | 42 } |
| 40 | 43 |
| 41 // Performs the selector on |clientThread_| using |runLoopModes_|. | 44 // Performs the selector on |clientThread_| using |runLoopModes_|. |
| 45 - (void)runInvocationQueueOnClientThread; | |
| 46 - (void)enqueueInvocationOnClientThread:(NSInvocation*)invocation; | |
| 42 - (void)performSelectorOnClientThread:(SEL)aSelector withObject:(id)arg; | 47 - (void)performSelectorOnClientThread:(SEL)aSelector withObject:(id)arg; |
| 43 // These functions are just wrappers around the corresponding | 48 // These functions are just wrappers around the corresponding |
| 44 // NSURLProtocolClient methods, used for task posting. | 49 // NSURLProtocolClient methods, used for task posting. |
| 45 - (void)didFailWithErrorOnClientThread:(NSError*)error; | 50 - (void)didFailWithErrorOnClientThread:(NSError*)error; |
| 46 - (void)didLoadDataOnClientThread:(NSData*)data; | 51 - (void)didLoadDataOnClientThread:(NSData*)data; |
| 47 - (void)didReceiveResponseOnClientThread:(NSURLResponse*)response; | 52 - (void)didReceiveResponseOnClientThread:(NSURLResponse*)response; |
| 48 - (void)wasRedirectedToRequestOnClientThread:(NSArray*)params; | 53 - (void)wasRedirectedToRequestOnClientThread:(NSArray*)params; |
| 49 - (void)didFinishLoadingOnClientThread; | 54 - (void)didFinishLoadingOnClientThread; |
| 50 @end | 55 @end |
| 51 | 56 |
| 52 @implementation CRNHTTPProtocolHandlerProxyWithClientThread | 57 @implementation CRNHTTPProtocolHandlerProxyWithClientThread |
| 53 | 58 |
| 54 - (instancetype)initWithProtocol:(NSURLProtocol*)protocol | 59 - (instancetype)initWithProtocol:(NSURLProtocol*)protocol |
| 55 clientThread:(NSThread*)clientThread | 60 clientThread:(NSThread*)clientThread |
| 56 runLoopMode:(NSString*)mode { | 61 runLoopMode:(NSString*)mode { |
| 57 DCHECK(protocol); | 62 DCHECK(protocol); |
| 58 DCHECK(clientThread); | 63 DCHECK(clientThread); |
| 59 if ((self = [super init])) { | 64 if ((self = [super init])) { |
| 60 _protocol = protocol; | 65 _protocol = protocol; |
| 61 _url.reset([[[[protocol request] URL] absoluteString] copy]); | 66 _url.reset([[[[protocol request] URL] absoluteString] copy]); |
| 62 _creationTime = base::Time::Now(); | 67 _creationTime = base::Time::Now(); |
| 63 _clientThread = clientThread; | 68 _clientThread = clientThread; |
| 64 // Use the common run loop mode in addition to the client thread mode, in | 69 // Use the common run loop mode in addition to the client thread mode, in |
| 65 // hope that our tasks are executed even if the client thread changes mode | 70 // hope that our tasks are executed even if the client thread changes mode |
| 66 // later on. | 71 // later on. |
| 67 if ([mode isEqualToString:NSRunLoopCommonModes]) | 72 if ([mode isEqualToString:NSRunLoopCommonModes]) |
| 68 _runLoopModes.reset([@[ NSRunLoopCommonModes ] retain]); | 73 _runLoopModes.reset([@[ NSRunLoopCommonModes ] retain]); |
| 69 else | 74 else |
| 70 _runLoopModes.reset([@[ mode, NSRunLoopCommonModes ] retain]); | 75 _runLoopModes.reset([@[ mode, NSRunLoopCommonModes ] retain]); |
| 76 _paused = NO; | |
|
droger
2015/05/27 17:09:00
Nit: in objective-C, instance variables are auto-z
Elly Fong-Jones
2015/06/10 17:06:35
Done.
| |
| 77 _queuedInvocations.reset([[NSMutableArray alloc] init]); | |
| 71 } | 78 } |
| 72 return self; | 79 return self; |
| 73 } | 80 } |
| 74 | 81 |
| 75 - (void)invalidate { | 82 - (void)invalidate { |
| 76 DCHECK([NSThread currentThread] == _clientThread); | 83 DCHECK([NSThread currentThread] == _clientThread); |
| 77 _protocol = nil; | 84 _protocol = nil; |
| 78 _requestComplete = YES; | 85 _requestComplete = YES; |
| 86 // TODO(ellyjones): This DCHECK fails sometimes. What this implies is that a | |
| 87 // callback is coming from the chrome network stack, being enqueued, and then | |
| 88 // this proxy is being invalidated before that callback has ever been | |
| 89 // delivered. Disturbingly, in practice these seem to be didLoadData and | |
| 90 // similar... | |
| 91 // DCHECK(_queuedInvocations.get().count == 0); | |
|
droger
2015/05/27 17:09:01
This might be caused by the problem with |_paused|
Elly Fong-Jones
2015/06/10 17:06:35
Done.
| |
| 92 } | |
| 93 | |
| 94 - (void)enqueueInvocationOnClientThread:(NSInvocation*)inv { | |
| 95 DCHECK([NSThread currentThread] == _clientThread); | |
| 96 DCHECK(!_requestComplete || !_protocol); | |
| 97 [_queuedInvocations addObject:inv]; | |
| 98 // This function receives these two references from | |
| 99 // |performSelectorOnClientThread:| below. | |
| 100 [inv release]; | |
| 101 [self release]; | |
| 102 } | |
| 103 | |
| 104 - (void)runInvocationQueueOnClientThread { | |
| 105 DCHECK([NSThread currentThread] == _clientThread); | |
| 106 DCHECK(!_requestComplete || !_protocol); | |
| 107 while (!_paused && _queuedInvocations.get().count > 0) { | |
| 108 NSInvocation* inv = [_queuedInvocations objectAtIndex:0]; | |
| 109 // Since |_queuedInvocations| owns the only reference to each queued | |
| 110 // invocation, this function has to retain another reference before removing | |
| 111 // the queued invocation from the array. | |
| 112 [inv retain]; | |
| 113 [_queuedInvocations removeObjectAtIndex:0]; | |
| 114 [inv invoke]; | |
| 115 [inv release]; | |
| 116 } | |
| 79 } | 117 } |
| 80 | 118 |
| 81 - (void)performSelectorOnClientThread:(SEL)aSelector withObject:(id)arg { | 119 - (void)performSelectorOnClientThread:(SEL)aSelector withObject:(id)arg { |
| 82 [self performSelector:aSelector | 120 if (!_paused) { |
|
droger
2015/05/27 17:09:00
I don't think it is safe to access _paused here.
Elly Fong-Jones
2015/06/10 17:06:35
Done.
| |
| 83 onThread:_clientThread | 121 [self performSelector:aSelector |
| 84 withObject:arg | 122 onThread:_clientThread |
| 85 waitUntilDone:NO | 123 withObject:arg |
| 86 modes:_runLoopModes]; | 124 waitUntilDone:NO |
| 125 modes:_runLoopModes]; | |
| 126 } else { | |
| 127 // The memory management for this case is tricky. The |inv| NSInvocation | |
| 128 // object created here is autoreleased, so it would be destroyed by the | |
| 129 // autorelease pool if this function did not retain another reference to it. | |
| 130 // That reference is given to |enqueueInvocationOnClientThread:| via | |
| 131 // |performSelector:|, and |enqueueInvocationOnClientThread:| is responsible | |
| 132 // for releasing it. | |
| 133 // | |
| 134 // This function also needs an extra reference to |self|. Otherwise, this | |
| 135 // object can be destroyed by the time the |enqueueInvocationOnClientThread| | |
| 136 // function runs, with disastrous results. |enqueueInvocationOnClientThread| | |
| 137 // is responsible for releasing this extra reference as well; it receives | |
| 138 // ownership of it through |performSelector|. | |
| 139 NSMethodSignature* sig = [self methodSignatureForSelector:aSelector]; | |
| 140 DCHECK(sig != nil); | |
| 141 NSInvocation* inv = [NSInvocation invocationWithMethodSignature:sig]; | |
| 142 [inv setTarget:self]; | |
| 143 if (sig.numberOfArguments > 2) { | |
| 144 [inv setArgument:&arg atIndex:2]; | |
| 145 } | |
| 146 [inv setSelector:aSelector]; | |
| 147 [inv retainArguments]; | |
| 148 [inv retain]; | |
| 149 | |
| 150 [self retain]; | |
| 151 [self performSelector:@selector(enqueueInvocationOnClientThread:) | |
| 152 onThread:_clientThread | |
| 153 withObject:inv | |
| 154 waitUntilDone:NO | |
| 155 modes:_runLoopModes]; | |
| 156 } | |
| 87 } | 157 } |
| 88 | 158 |
| 89 #pragma mark Proxy methods called from any thread. | 159 #pragma mark Proxy methods called from any thread. |
| 90 | 160 |
| 91 - (void)didFailWithNSErrorCode:(NSInteger)nsErrorCode | 161 - (void)didFailWithNSErrorCode:(NSInteger)nsErrorCode |
| 92 netErrorCode:(int)netErrorCode { | 162 netErrorCode:(int)netErrorCode { |
| 93 DCHECK(_clientThread); | 163 DCHECK(_clientThread); |
| 94 if (!_protocol) | 164 if (!_protocol) |
| 95 return; | 165 return; |
| 96 NSError* error = | 166 NSError* error = |
| (...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 191 redirectResponse:params[1]]; | 261 redirectResponse:params[1]]; |
| 192 } | 262 } |
| 193 | 263 |
| 194 - (void)didFinishLoadingOnClientThread { | 264 - (void)didFinishLoadingOnClientThread { |
| 195 DCHECK([NSThread currentThread] == _clientThread); | 265 DCHECK([NSThread currentThread] == _clientThread); |
| 196 DCHECK(!_requestComplete || !_protocol); | 266 DCHECK(!_requestComplete || !_protocol); |
| 197 _requestComplete = YES; | 267 _requestComplete = YES; |
| 198 [[_protocol client] URLProtocolDidFinishLoading:_protocol]; | 268 [[_protocol client] URLProtocolDidFinishLoading:_protocol]; |
| 199 } | 269 } |
| 200 | 270 |
| 271 - (void)pause { | |
| 272 _paused = YES; | |
|
droger
2015/05/27 17:09:01
We probably need thread checks here
DCHECK([NSThre
Elly Fong-Jones
2015/06/10 17:06:35
Done.
| |
| 273 } | |
| 274 | |
| 275 - (void)resume { | |
| 276 _paused = NO; | |
|
droger
2015/05/27 17:09:01
Thread checks.
Elly Fong-Jones
2015/06/10 17:06:35
Done.
| |
| 277 [self runInvocationQueueOnClientThread]; | |
| 278 } | |
| 279 | |
| 201 @end | 280 @end |
| OLD | NEW |