Unreviewed, roll out http://trac.webkit.org/changeset/187972.
[WebKit-https.git] / Source / WebKit2 / Shared / Network / CustomProtocols / Cocoa / CustomProtocolManagerCocoa.mm
1 /*
2  * Copyright (C) 2012, 2013 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #import "config.h"
27 #import "CustomProtocolManager.h"
28
29 #import "ChildProcess.h"
30 #import "CustomProtocolManagerMessages.h"
31 #import "CustomProtocolManagerProxyMessages.h"
32 #import "DataReference.h"
33 #import "WebCoreArgumentCoders.h"
34 #import "WebProcessCreationParameters.h"
35 #import <WebCore/URL.h>
36 #import <WebCore/ResourceError.h>
37 #import <WebCore/ResourceRequest.h>
38 #import <WebCore/ResourceResponse.h>
39
40 #if ENABLE(NETWORK_PROCESS)
41 #import "NetworkProcessCreationParameters.h"
42 #endif
43
44 #ifdef __has_include
45 #if __has_include(<Foundation/NSURLConnectionPrivate.h>)
46 #import <Foundation/NSURLConnectionPrivate.h>
47 #endif
48 #endif
49
50 @interface NSURLConnection ()
51 + (CFRunLoopRef)resourceLoaderRunLoop;
52 @end
53
54 using namespace WebKit;
55
56 namespace WebKit {
57
58 static CustomProtocolManager* sharedCustomProtocolManager;
59
60 static uint64_t generateCustomProtocolID()
61 {
62     static uint64_t uniqueCustomProtocolID = 0;
63     return ++uniqueCustomProtocolID;
64 }
65
66 } // namespace WebKit
67
68 @interface WKCustomProtocol : NSURLProtocol {
69 @private
70     uint64_t _customProtocolID;
71 }
72 @property (nonatomic, readonly) uint64_t customProtocolID;
73 @end
74
75 @implementation WKCustomProtocol
76
77 @synthesize customProtocolID = _customProtocolID;
78
79 + (BOOL)canInitWithRequest:(NSURLRequest *)request
80 {
81     return sharedCustomProtocolManager->supportsScheme([[[request URL] scheme] lowercaseString]);
82 }
83
84 + (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request
85 {
86     return request;
87 }
88
89 + (BOOL)requestIsCacheEquivalent:(NSURLRequest *)a toRequest:(NSURLRequest *)b
90 {
91     return NO;
92 }
93
94 - (id)initWithRequest:(NSURLRequest *)request cachedResponse:(NSCachedURLResponse *)cachedResponse client:(id<NSURLProtocolClient>)client
95 {
96     self = [super initWithRequest:request cachedResponse:cachedResponse client:client];
97     if (!self)
98         return nil;
99     
100     _customProtocolID = generateCustomProtocolID();
101     sharedCustomProtocolManager->addCustomProtocol(self);
102     return self;
103 }
104
105 - (void)startLoading
106 {
107     sharedCustomProtocolManager->childProcess()->send(Messages::CustomProtocolManagerProxy::StartLoading(self.customProtocolID, [self request]), 0);
108 }
109
110 - (void)stopLoading
111 {
112     sharedCustomProtocolManager->childProcess()->send(Messages::CustomProtocolManagerProxy::StopLoading(self.customProtocolID), 0);
113     sharedCustomProtocolManager->removeCustomProtocol(self);
114 }
115
116 @end
117
118 namespace WebKit {
119
120 const char* CustomProtocolManager::supplementName()
121 {
122     return "CustomProtocolManager";
123 }
124
125 CustomProtocolManager::CustomProtocolManager(ChildProcess* childProcess)
126     : m_childProcess(childProcess)
127     , m_messageQueue(WorkQueue::create("com.apple.WebKit.CustomProtocolManager"))
128 {
129     ASSERT(!sharedCustomProtocolManager);
130     sharedCustomProtocolManager = this;
131 }
132
133 void CustomProtocolManager::initializeConnection(IPC::Connection* connection)
134 {
135     connection->addWorkQueueMessageReceiver(Messages::CustomProtocolManager::messageReceiverName(), m_messageQueue.get(), this);
136 }
137
138 void CustomProtocolManager::initialize(const WebProcessCreationParameters& parameters)
139 {
140 #if ENABLE(NETWORK_PROCESS)
141     ASSERT(parameters.urlSchemesRegisteredForCustomProtocols.isEmpty() || !parameters.usesNetworkProcess);
142     if (parameters.usesNetworkProcess) {
143         m_childProcess->parentProcessConnection()->removeWorkQueueMessageReceiver(Messages::CustomProtocolManager::messageReceiverName());
144         m_messageQueue = nullptr;
145         return;
146     }
147 #endif
148
149     [NSURLProtocol registerClass:[WKCustomProtocol class]];
150
151     for (size_t i = 0; i < parameters.urlSchemesRegisteredForCustomProtocols.size(); ++i)
152         registerScheme(parameters.urlSchemesRegisteredForCustomProtocols[i]);
153 }
154
155 #if ENABLE(NETWORK_PROCESS)
156 void CustomProtocolManager::initialize(const NetworkProcessCreationParameters& parameters)
157 {
158     [NSURLProtocol registerClass:[WKCustomProtocol class]];
159
160     for (size_t i = 0; i < parameters.urlSchemesRegisteredForCustomProtocols.size(); ++i)
161         registerScheme(parameters.urlSchemesRegisteredForCustomProtocols[i]);
162 }
163 #endif
164
165 void CustomProtocolManager::addCustomProtocol(WKCustomProtocol *customProtocol)
166 {
167     ASSERT(customProtocol);
168     MutexLocker locker(m_customProtocolMapMutex);
169     m_customProtocolMap.add(customProtocol.customProtocolID, customProtocol);
170 }
171
172 void CustomProtocolManager::removeCustomProtocol(WKCustomProtocol *customProtocol)
173 {
174     ASSERT(customProtocol);
175     MutexLocker locker(m_customProtocolMapMutex);
176     m_customProtocolMap.remove(customProtocol.customProtocolID);
177 }
178     
179 void CustomProtocolManager::registerScheme(const String& scheme)
180 {
181     ASSERT(!scheme.isNull());
182     MutexLocker locker(m_registeredSchemesMutex);
183     m_registeredSchemes.add(scheme);
184 }
185     
186 void CustomProtocolManager::unregisterScheme(const String& scheme)
187 {
188     ASSERT(!scheme.isNull());
189     MutexLocker locker(m_registeredSchemesMutex);
190     m_registeredSchemes.remove(scheme);
191 }
192
193 bool CustomProtocolManager::supportsScheme(const String& scheme)
194 {
195     if (scheme.isNull())
196         return false;
197
198     MutexLocker locker(m_registeredSchemesMutex);
199     return m_registeredSchemes.contains(scheme);
200 }
201
202 static inline void dispatchOnResourceLoaderRunLoop(void (^block)())
203 {
204     CFRunLoopPerformBlock([NSURLConnection resourceLoaderRunLoop], kCFRunLoopDefaultMode, block);
205     CFRunLoopWakeUp([NSURLConnection resourceLoaderRunLoop]);
206 }
207
208 void CustomProtocolManager::didFailWithError(uint64_t customProtocolID, const WebCore::ResourceError& error)
209 {
210     RetainPtr<WKCustomProtocol> protocol = protocolForID(customProtocolID);
211     if (!protocol)
212         return;
213
214     RetainPtr<NSError> nsError = error.nsError();
215
216     dispatchOnResourceLoaderRunLoop(^ {
217         [[protocol client] URLProtocol:protocol.get() didFailWithError:nsError.get()];
218     });
219
220     removeCustomProtocol(protocol.get());
221 }
222
223 void CustomProtocolManager::didLoadData(uint64_t customProtocolID, const IPC::DataReference& data)
224 {
225     RetainPtr<WKCustomProtocol> protocol = protocolForID(customProtocolID);
226     if (!protocol)
227         return;
228
229     RetainPtr<NSData> nsData = adoptNS([[NSData alloc] initWithBytes:data.data() length:data.size()]);
230
231     dispatchOnResourceLoaderRunLoop(^ {
232         [[protocol client] URLProtocol:protocol.get() didLoadData:nsData.get()];
233     });
234 }
235
236 void CustomProtocolManager::didReceiveResponse(uint64_t customProtocolID, const WebCore::ResourceResponse& response, uint32_t cacheStoragePolicy)
237 {
238     RetainPtr<WKCustomProtocol> protocol = protocolForID(customProtocolID);
239     if (!protocol)
240         return;
241
242     RetainPtr<NSURLResponse> nsResponse = response.nsURLResponse();
243
244     dispatchOnResourceLoaderRunLoop(^ {
245         [[protocol client] URLProtocol:protocol.get() didReceiveResponse:nsResponse.get() cacheStoragePolicy:static_cast<NSURLCacheStoragePolicy>(cacheStoragePolicy)];
246     });
247 }
248
249 void CustomProtocolManager::didFinishLoading(uint64_t customProtocolID)
250 {
251     RetainPtr<WKCustomProtocol> protocol = protocolForID(customProtocolID);
252     if (!protocol)
253         return;
254
255     dispatchOnResourceLoaderRunLoop(^ {
256         [[protocol client] URLProtocolDidFinishLoading:protocol.get()];
257     });
258
259     removeCustomProtocol(protocol.get());
260 }
261
262 RetainPtr<WKCustomProtocol> CustomProtocolManager::protocolForID(uint64_t customProtocolID)
263 {
264     MutexLocker locker(m_customProtocolMapMutex);
265
266     CustomProtocolMap::const_iterator it = m_customProtocolMap.find(customProtocolID);
267     if (it == m_customProtocolMap.end())
268         return nil;
269     
270     ASSERT(it->value);
271     return it->value;
272 }
273
274 } // namespace WebKit