Remove Cocoa CFURLConnection loading code
[WebKit-https.git] / Source / WebCore / platform / network / cocoa / ResourceResponseCocoa.mm
index 93579ad..25ff75f 100644 (file)
 
 #if PLATFORM(COCOA)
 
-#import "CFNetworkSPI.h"
 #import "HTTPParsers.h"
 #import "WebCoreURLResponse.h"
 #import <Foundation/Foundation.h>
 #import <limits>
+#import <pal/spi/cf/CFNetworkSPI.h>
+#import <wtf/AutodrainedPool.h>
+#import <wtf/NeverDestroyed.h>
 #import <wtf/StdLibExtras.h>
+#import <wtf/text/StringView.h>
 
 namespace WebCore {
 
@@ -65,15 +68,15 @@ void ResourceResponse::initNSURLResponse() const
     [m_nsResponse.get() _setMIMEType:(NSString *)m_mimeType];
 }
 
+void ResourceResponse::disableLazyInitialization()
+{
+    lazyInit(AllFields);
+}
+
 CertificateInfo ResourceResponse::platformCertificateInfo() const
 {
-#if USE(CFNETWORK)
-    ASSERT(m_cfResponse);
-    CFURLResponseRef cfResponse = m_cfResponse.get();
-#else
-    ASSERT(m_nsResponse);
+    ASSERT(m_nsResponse || source() == Source::ServiceWorker);
     CFURLResponseRef cfResponse = [m_nsResponse _CFURLResponse];
-#endif
 
     if (!cfResponse)
         return { };
@@ -106,118 +109,90 @@ CertificateInfo ResourceResponse::platformCertificateInfo() const
 #endif
 }
 
-#if USE(CFNETWORK)
+static NSString* const commonHeaderFields[] = {
+    @"Age", @"Cache-Control", @"Content-Type", @"Date", @"Etag", @"Expires", @"Last-Modified", @"Pragma"
+};
 
 NSURLResponse *ResourceResponse::nsURLResponse() const
 {
-    if (!m_nsResponse && !m_cfResponse && !m_isNull) {
+    if (!m_nsResponse && !m_isNull)
         initNSURLResponse();
-        m_cfResponse = [m_nsResponse.get() _CFURLResponse];
-        return m_nsResponse.get();
-    }
-
-    if (!m_cfResponse)
-        return nil;
-
-    if (!m_nsResponse)
-        m_nsResponse = [NSURLResponse _responseWithCFURLResponse:m_cfResponse.get()];
-
     return m_nsResponse.get();
 }
 
-ResourceResponse::ResourceResponse(NSURLResponse* nsResponse)
-    : m_initLevel(Uninitialized)
-    , m_cfResponse([nsResponse _CFURLResponse])
-    , m_nsResponse(nsResponse)
+static void addToHTTPHeaderMap(const void* key, const void* value, void* context)
 {
-    m_isNull = !nsResponse;
+    HTTPHeaderMap* httpHeaderMap = (HTTPHeaderMap*)context;
+    httpHeaderMap->set((CFStringRef)key, (CFStringRef)value);
 }
 
-#else
+static inline AtomicString stripLeadingAndTrailingDoubleQuote(const String& value)
+{
+    unsigned length = value.length();
+    if (length < 2 || value[0u] != '"' || value[length - 1] != '"')
+        return value;
 
-static NSString* const commonHeaderFields[] = {
-    @"Age", @"Cache-Control", @"Content-Type", @"Date", @"Etag", @"Expires", @"Last-Modified", @"Pragma"
-};
+    return StringView(value).substring(1, length - 2).toAtomicString();
+}
 
-NSURLResponse *ResourceResponse::nsURLResponse() const
+enum class OnlyCommonHeaders { No, Yes };
+static inline void initializeHTTPHeaders(OnlyCommonHeaders onlyCommonHeaders, NSHTTPURLResponse *httpResponse, HTTPHeaderMap& headersMap)
 {
-    if (!m_nsResponse && !m_isNull)
-        initNSURLResponse();
-    return m_nsResponse.get();
+    headersMap.clear();
+    auto messageRef = CFURLResponseGetHTTPResponse([httpResponse _CFURLResponse]);
+
+    // Avoid calling [NSURLResponse allHeaderFields] to minimize copying (<rdar://problem/26778863>).
+    auto headers = adoptCF(CFHTTPMessageCopyAllHeaderFields(messageRef));
+    if (onlyCommonHeaders == OnlyCommonHeaders::Yes) {
+        for (auto& commonHeader : commonHeaderFields) {
+            const void* value;
+            if (CFDictionaryGetValueIfPresent(headers.get(), commonHeader, &value))
+                headersMap.set(commonHeader, (CFStringRef) value);
+        }
+        return;
+    }
+    CFDictionaryApplyFunction(headers.get(), addToHTTPHeaderMap, &headersMap);
 }
-    
-static NSString *copyNSURLResponseStatusLine(NSURLResponse *response)
-{
-    CFURLResponseRef cfResponse = [response _CFURLResponse];
-    if (!cfResponse)
-        return nil;
 
-    CFHTTPMessageRef cfHTTPMessage = CFURLResponseGetHTTPResponse(cfResponse);
-    if (!cfHTTPMessage)
-        return nil;
+static inline AtomicString extractHTTPStatusText(CFHTTPMessageRef messageRef)
+{
+    if (auto httpStatusLine = adoptCF(CFHTTPMessageCopyResponseStatusLine(messageRef)))
+        return extractReasonPhraseFromHTTPStatusLine(httpStatusLine.get());
 
-    return (NSString *)CFHTTPMessageCopyResponseStatusLine(cfHTTPMessage);
+    static NeverDestroyed<AtomicString> defaultStatusText("OK", AtomicString::ConstructFromLiteral);
+    return defaultStatusText;
 }
 
 void ResourceResponse::platformLazyInit(InitLevel initLevel)
 {
+    ASSERT(initLevel >= CommonFieldsOnly);
+
     if (m_initLevel >= initLevel)
         return;
 
     if (m_isNull || !m_nsResponse)
         return;
     
-    if (m_initLevel < CommonFieldsOnly && initLevel >= CommonFieldsOnly) {
-        NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
+    AutodrainedPool pool;
+
+    NSHTTPURLResponse *httpResponse = [m_nsResponse.get() isKindOfClass:[NSHTTPURLResponse class]] ? (NSHTTPURLResponse *)m_nsResponse.get() : nullptr;
 
-        m_httpHeaderFields.clear();
+    if (m_initLevel < CommonFieldsOnly) {
         m_url = [m_nsResponse.get() URL];
         m_mimeType = [m_nsResponse.get() MIMEType];
         m_expectedContentLength = [m_nsResponse.get() expectedContentLength];
-        m_textEncodingName = [m_nsResponse.get() textEncodingName];
-
-        // Workaround for <rdar://problem/8757088>, can be removed once that is fixed.
-        unsigned textEncodingNameLength = m_textEncodingName.length();
-        if (textEncodingNameLength >= 2 && m_textEncodingName[0U] == '"' && m_textEncodingName[textEncodingNameLength - 1] == '"')
-            m_textEncodingName = m_textEncodingName.string().substring(1, textEncodingNameLength - 2);
-
-        if ([m_nsResponse.get() isKindOfClass:[NSHTTPURLResponse class]]) {
-            NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)m_nsResponse.get();
-
-            m_httpStatusCode = [httpResponse statusCode];
-            
-            if (initLevel < AllFields) {
-                NSDictionary *headers = [httpResponse allHeaderFields];
-                for (NSString *name : commonHeaderFields) {
-                    if (NSString* headerValue = [headers objectForKey:name])
-                        m_httpHeaderFields.set(name, headerValue);
-                }
-            }
-        } else
-            m_httpStatusCode = 0;
-        
-        [pool drain];
+        // Stripping double quotes as a workaround for <rdar://problem/8757088>, can be removed once that is fixed.
+        m_textEncodingName = stripLeadingAndTrailingDoubleQuote([m_nsResponse.get() textEncodingName]);
+        m_httpStatusCode = httpResponse ? [httpResponse statusCode] : 0;
     }
-
-    if (m_initLevel < AllFields && initLevel == AllFields) {
-        if ([m_nsResponse.get() isKindOfClass:[NSHTTPURLResponse class]]) {
-            NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
-
-            NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)m_nsResponse.get();
-            if (RetainPtr<NSString> httpStatusLine = adoptNS(copyNSURLResponseStatusLine(httpResponse)))
-                m_httpStatusText = extractReasonPhraseFromHTTPStatusLine(httpStatusLine.get());
-            else
-                m_httpStatusText = AtomicString("OK", AtomicString::ConstructFromLiteral);
-
-            CFHTTPMessageRef messageRef = CFURLResponseGetHTTPResponse([httpResponse _CFURLResponse]);
+    if (httpResponse) {
+        if (initLevel == AllFields) {
+            auto messageRef = CFURLResponseGetHTTPResponse([httpResponse _CFURLResponse]);
+            m_httpStatusText = extractHTTPStatusText(messageRef);
             m_httpVersion = String(adoptCF(CFHTTPMessageCopyVersion(messageRef)).get()).convertToASCIIUppercase();
-
-            NSDictionary *headers = [httpResponse allHeaderFields];
-            for (NSString *name in headers)
-                m_httpHeaderFields.set(name, [headers objectForKey:name]);
-            
-            [pool drain];
-        }
+            initializeHTTPHeaders(OnlyCommonHeaders::No, httpResponse, m_httpHeaderFields);
+        } else
+            initializeHTTPHeaders(OnlyCommonHeaders::Yes, httpResponse, m_httpHeaderFields);
     }
 
     m_initLevel = initLevel;
@@ -233,8 +208,6 @@ bool ResourceResponse::platformCompare(const ResourceResponse& a, const Resource
     return a.nsURLResponse() == b.nsURLResponse();
 }
 
-#endif // USE(CFNETWORK)
-
 } // namespace WebCore
 
 #endif // PLATFORM(COCOA)