Speculative crash fix; check for a null focused view before derefing.
[chromium-blink-merge.git] / ios / net / crn_http_protocol_handler_proxy_with_client_thread.mm
blob0289e81d630f848a358f9dac2a4aae8fb0025f61
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
3 // found in the LICENSE file.
5 #import "ios/net/crn_http_protocol_handler_proxy_with_client_thread.h"
7 #include "base/logging.h"
8 #import "base/mac/scoped_nsobject.h"
9 #include "base/time/time.h"
10 #import "ios/net/protocol_handler_util.h"
11 #include "net/base/auth.h"
12 #include "net/url_request/url_request.h"
14 // When the protocol is invalidated, no synchronization (lock) is needed:
15 // - The actual calls to the protocol and its invalidation are all done on
16 //   clientThread_ and thus are serialized.
17 // - When a proxy method is called, the protocol is compared to nil. There may
18 //   be a conflict at this point, in the case the protocol is being invalidated
19 //   during this comparison. However, in such a case, the actual value of the
20 //   pointer does not matter: an invalid pointer will behave as a valid one and
21 //   post a task on the clientThread_, and that task will be handled correctly,
22 //   as described by the item above.
24 @interface CRNHTTPProtocolHandlerProxyWithClientThread () {
25   __weak NSURLProtocol* _protocol;
26   // Thread used to call the client back.
27   // This thread does not have a base::MessageLoop, and thus does not work with
28   // the usual task posting functions.
29   __weak NSThread* _clientThread;
30   // The run loop modes to use when posting tasks to |clientThread_|.
31   base::scoped_nsobject<NSArray> _runLoopModes;
32   // The request URL.
33   base::scoped_nsobject<NSString> _url;
34   // The creation time of the request.
35   base::Time _creationTime;
36   // |requestComplete_| is used in debug to check that the client is not called
37   // after completion.
38   BOOL _requestComplete;
41 // Performs the selector on |clientThread_| using |runLoopModes_|.
42 - (void)performSelectorOnClientThread:(SEL)aSelector withObject:(id)arg;
43 // These functions are just wrappers around the corresponding
44 // NSURLProtocolClient methods, used for task posting.
45 - (void)didFailWithErrorOnClientThread:(NSError*)error;
46 - (void)didLoadDataOnClientThread:(NSData*)data;
47 - (void)didReceiveResponseOnClientThread:(NSURLResponse*)response;
48 - (void)wasRedirectedToRequestOnClientThread:(NSArray*)params;
49 - (void)didFinishLoadingOnClientThread;
50 @end
52 @implementation CRNHTTPProtocolHandlerProxyWithClientThread
54 - (instancetype)initWithProtocol:(NSURLProtocol*)protocol
55                     clientThread:(NSThread*)clientThread
56                      runLoopMode:(NSString*)mode {
57   DCHECK(protocol);
58   DCHECK(clientThread);
59   if ((self = [super init])) {
60     _protocol = protocol;
61     _url.reset([[[[protocol request] URL] absoluteString] copy]);
62     _creationTime = base::Time::Now();
63     _clientThread = clientThread;
64     // 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
66     // later on.
67     if ([mode isEqualToString:NSRunLoopCommonModes])
68       _runLoopModes.reset([@[ NSRunLoopCommonModes ] retain]);
69     else
70       _runLoopModes.reset([@[ mode, NSRunLoopCommonModes ] retain]);
71   }
72   return self;
75 - (void)invalidate {
76   DCHECK([NSThread currentThread] == _clientThread);
77   _protocol = nil;
78   _requestComplete = YES;
81 - (void)performSelectorOnClientThread:(SEL)aSelector withObject:(id)arg {
82   [self performSelector:aSelector
83                onThread:_clientThread
84              withObject:arg
85           waitUntilDone:NO
86                   modes:_runLoopModes];
89 #pragma mark Proxy methods called from any thread.
91 - (void)didFailWithNSErrorCode:(NSInteger)nsErrorCode
92                   netErrorCode:(int)netErrorCode {
93   DCHECK(_clientThread);
94   if (!_protocol)
95     return;
96   NSError* error =
97       net::GetIOSError(nsErrorCode, netErrorCode, _url, _creationTime);
98   [self performSelectorOnClientThread:@selector(didFailWithErrorOnClientThread:)
99                            withObject:error];
102 - (void)didLoadData:(NSData*)data {
103   DCHECK(_clientThread);
104   if (!_protocol)
105     return;
106   [self performSelectorOnClientThread:@selector(didLoadDataOnClientThread:)
107                            withObject:data];
110 - (void)didReceiveResponse:(NSURLResponse*)response {
111   DCHECK(_clientThread);
112   if (!_protocol)
113     return;
114   [self
115       performSelectorOnClientThread:@selector(didReceiveResponseOnClientThread:)
116                          withObject:response];
119 - (void)wasRedirectedToRequest:(NSURLRequest*)request
120                  nativeRequest:(net::URLRequest*)nativeRequest
121               redirectResponse:(NSURLResponse*)redirectResponse {
122   DCHECK(_clientThread);
123   if (!_protocol)
124     return;
125   [self performSelectorOnClientThread:@selector(
126                                           wasRedirectedToRequestOnClientThread:)
127                            withObject:@[ request, redirectResponse ]];
130 - (void)didFinishLoading {
131   DCHECK(_clientThread);
132   if (!_protocol)
133     return;
134   [self performSelectorOnClientThread:@selector(didFinishLoadingOnClientThread)
135                            withObject:nil];
138 // Feature support methods that don't forward to the NSURLProtocolClient.
139 - (void)didCreateNativeRequest:(net::URLRequest*)nativeRequest {
140   // no-op.
143 - (void)didRecieveAuthChallenge:(net::AuthChallengeInfo*)authInfo
144                   nativeRequest:(const net::URLRequest&)nativeRequest
145                        callback:(const network_client::AuthCallback&)callback {
146   // If we get this far, authentication has failed.
147   base::string16 empty;
148   callback.Run(false, empty, empty);
151 - (void)cancelAuthRequest {
152   // no-op.
155 - (void)setUnderlyingClient:(id<CRNNetworkClientProtocol>)underlyingClient {
156   // This is the lowest level.
157   DCHECK(!underlyingClient);
160 #pragma mark Proxy methods called from the client thread.
162 - (void)didFailWithErrorOnClientThread:(NSError*)error {
163   DCHECK([NSThread currentThread] == _clientThread);
164   DCHECK(!_requestComplete || !_protocol);
165   _requestComplete = YES;
166   [[_protocol client] URLProtocol:_protocol didFailWithError:error];
169 - (void)didLoadDataOnClientThread:(NSData*)data {
170   DCHECK([NSThread currentThread] == _clientThread);
171   DCHECK(!_requestComplete || !_protocol);
172   [[_protocol client] URLProtocol:_protocol didLoadData:data];
175 - (void)didReceiveResponseOnClientThread:(NSURLResponse*)response {
176   DCHECK([NSThread currentThread] == _clientThread);
177   DCHECK(!_requestComplete || !_protocol);
178   [[_protocol client] URLProtocol:_protocol
179                didReceiveResponse:response
180                cacheStoragePolicy:NSURLCacheStorageNotAllowed];
183 - (void)wasRedirectedToRequestOnClientThread:(NSArray*)params {
184   DCHECK([NSThread currentThread] == _clientThread);
185   DCHECK_EQ(2u, [params count]);
186   DCHECK([params[0] isKindOfClass:[NSURLRequest class]]);
187   DCHECK([params[1] isKindOfClass:[NSURLResponse class]]);
188   DCHECK(!_requestComplete || !_protocol);
189   [[_protocol client] URLProtocol:_protocol
190            wasRedirectedToRequest:params[0]
191                  redirectResponse:params[1]];
194 - (void)didFinishLoadingOnClientThread {
195   DCHECK([NSThread currentThread] == _clientThread);
196   DCHECK(!_requestComplete || !_protocol);
197   _requestComplete = YES;
198   [[_protocol client] URLProtocolDidFinishLoading:_protocol];
201 @end