Start of support for multiple WebsiteDataStore/SessionIDs per process
[WebKit-https.git] / Source / WebCore / platform / network / cf / NetworkStorageSessionCFNet.cpp
1 /*
2  * Copyright (C) 2012-2017 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 #include "config.h"
27 #include "NetworkStorageSession.h"
28
29 #include <wtf/MainThread.h>
30 #include <wtf/NeverDestroyed.h>
31 #include <wtf/ProcessID.h>
32
33 #if PLATFORM(COCOA)
34 #include "PublicSuffix.h"
35 #include "ResourceRequest.h"
36 #include "WebCoreSystemInterface.h"
37 #else
38 #include <WebKitSystemInterface/WebKitSystemInterface.h>
39 #endif
40
41 namespace WebCore {
42
43 static bool cookieStoragePartitioningEnabled;
44
45 static RetainPtr<CFURLStorageSessionRef> createCFStorageSessionForIdentifier(CFStringRef identifier)
46 {
47     auto storageSession = adoptCF(_CFURLStorageSessionCreate(kCFAllocatorDefault, identifier, nullptr));
48
49     if (!storageSession)
50         return nullptr;
51
52     auto cache = adoptCF(_CFURLStorageSessionCopyCache(kCFAllocatorDefault, storageSession.get()));
53     if (!cache)
54         return nullptr;
55
56     CFURLCacheSetDiskCapacity(cache.get(), 0);
57
58     auto sharedCache = adoptCF(CFURLCacheCopySharedURLCache());
59     CFURLCacheSetMemoryCapacity(cache.get(), CFURLCacheMemoryCapacity(sharedCache.get()));
60
61     auto cookieStorage = adoptCF(_CFURLStorageSessionCopyCookieStorage(kCFAllocatorDefault, storageSession.get()));
62     if (!cookieStorage)
63         return nullptr;
64
65     auto sharedCookieStorage = _CFHTTPCookieStorageGetDefault(kCFAllocatorDefault);
66     auto sharedPolicy = CFHTTPCookieStorageGetCookieAcceptPolicy(sharedCookieStorage);
67     CFHTTPCookieStorageSetCookieAcceptPolicy(cookieStorage.get(), sharedPolicy);
68
69     return storageSession;
70 }
71
72 NetworkStorageSession::NetworkStorageSession(SessionID sessionID, RetainPtr<CFURLStorageSessionRef>&& platformSession, RetainPtr<CFHTTPCookieStorageRef>&& platformCookieStorage)
73     : m_sessionID(sessionID)
74     , m_platformSession(WTFMove(platformSession))
75 {
76     m_platformCookieStorage = platformCookieStorage ? WTFMove(platformCookieStorage) : cookieStorage();
77 }
78
79 static std::unique_ptr<NetworkStorageSession>& defaultNetworkStorageSession()
80 {
81     ASSERT(isMainThread());
82     static NeverDestroyed<std::unique_ptr<NetworkStorageSession>> session;
83     return session;
84 }
85
86 void NetworkStorageSession::switchToNewTestingSession()
87 {
88     // Session name should be short enough for shared memory region name to be under the limit, otehrwise sandbox rules won't work (see <rdar://problem/13642852>).
89     String sessionName = String::format("WebKit Test-%u", static_cast<uint32_t>(getCurrentProcessID()));
90
91     RetainPtr<CFURLStorageSessionRef> session;
92 #if PLATFORM(COCOA)
93     session = adoptCF(wkCreatePrivateStorageSession(sessionName.createCFString().get()));
94 #else
95     session = adoptCF(wkCreatePrivateStorageSession(sessionName.createCFString().get(), defaultNetworkStorageSession()->platformSession()));
96 #endif
97
98     RetainPtr<CFHTTPCookieStorageRef> cookieStorage;
99     if (session)
100         cookieStorage = adoptCF(_CFURLStorageSessionCopyCookieStorage(kCFAllocatorDefault, session.get()));
101
102     defaultNetworkStorageSession() = std::make_unique<NetworkStorageSession>(SessionID::defaultSessionID(), WTFMove(session), WTFMove(cookieStorage));
103 }
104
105 NetworkStorageSession& NetworkStorageSession::defaultStorageSession()
106 {
107     if (!defaultNetworkStorageSession())
108         defaultNetworkStorageSession() = std::make_unique<NetworkStorageSession>(SessionID::defaultSessionID(), nullptr, nullptr);
109     return *defaultNetworkStorageSession();
110 }
111
112 void NetworkStorageSession::ensurePrivateBrowsingSession(SessionID sessionID, const String& identifierBase)
113 {
114     ASSERT(sessionID.isEphemeral());
115     ensureSession(sessionID, identifierBase);
116 }
117
118 void NetworkStorageSession::ensureSession(SessionID sessionID, const String& identifierBase, RetainPtr<CFHTTPCookieStorageRef>&& cookieStorage)
119 {
120     auto addResult = globalSessionMap().add(sessionID, nullptr);
121     if (!addResult.isNewEntry)
122         return;
123
124     RetainPtr<CFStringRef> cfIdentifier = String(identifierBase + ".PrivateBrowsing").createCFString();
125
126     RetainPtr<CFURLStorageSessionRef> storageSession;
127     if (sessionID.isEphemeral()) {
128 #if PLATFORM(COCOA)
129         storageSession = adoptCF(wkCreatePrivateStorageSession(cfIdentifier.get()));
130 #else
131         storageSession = adoptCF(wkCreatePrivateStorageSession(cfIdentifier.get(), defaultNetworkStorageSession()->platformSession()));
132 #endif
133     } else
134         storageSession = createCFStorageSessionForIdentifier(cfIdentifier.get());
135
136     if (!cookieStorage && storageSession)
137         cookieStorage = adoptCF(_CFURLStorageSessionCopyCookieStorage(kCFAllocatorDefault, storageSession.get()));
138
139     addResult.iterator->value = std::make_unique<NetworkStorageSession>(sessionID, WTFMove(storageSession), WTFMove(cookieStorage));
140 }
141
142 void NetworkStorageSession::ensureSession(SessionID sessionID, const String& identifierBase)
143 {
144     ensureSession(sessionID, identifierBase, nullptr);
145 }
146
147 RetainPtr<CFHTTPCookieStorageRef> NetworkStorageSession::cookieStorage() const
148 {
149     if (m_platformCookieStorage)
150         return m_platformCookieStorage;
151
152     if (m_platformSession)
153         return adoptCF(_CFURLStorageSessionCopyCookieStorage(kCFAllocatorDefault, m_platformSession.get()));
154
155 #if USE(CFURLCONNECTION)
156     return _CFHTTPCookieStorageGetDefault(kCFAllocatorDefault);
157 #else
158     // When using NSURLConnection, we also use its shared cookie storage.
159     return nullptr;
160 #endif
161 }
162
163 void NetworkStorageSession::setCookieStoragePartitioningEnabled(bool enabled)
164 {
165     cookieStoragePartitioningEnabled = enabled;
166 }
167
168 #if HAVE(CFNETWORK_STORAGE_PARTITIONING)
169
170 String NetworkStorageSession::cookieStoragePartition(const ResourceRequest& request) const
171 {
172     return cookieStoragePartition(request.firstPartyForCookies(), request.url());
173 }
174
175 static inline String getPartitioningDomain(const URL& url) 
176 {
177 #if ENABLE(PUBLIC_SUFFIX_LIST)
178     auto domain = topPrivatelyControlledDomain(url.host());
179     if (domain.isEmpty())
180         domain = url.host();
181 #else
182     auto domain = url.host();
183 #endif
184     return domain;
185 }
186
187 String NetworkStorageSession::cookieStoragePartition(const URL& firstPartyForCookies, const URL& resource) const
188 {
189     if (!cookieStoragePartitioningEnabled)
190         return emptyString();
191     
192     auto resourceDomain = getPartitioningDomain(resource);
193     if (!shouldPartitionCookies(resourceDomain))
194         return emptyString();
195
196     auto firstPartyDomain = getPartitioningDomain(firstPartyForCookies);
197     if (firstPartyDomain == resourceDomain)
198         return emptyString();
199
200     return firstPartyDomain;
201 }
202
203 bool NetworkStorageSession::shouldPartitionCookies(const String& topPrivatelyControlledDomain) const
204 {
205     if (topPrivatelyControlledDomain.isEmpty())
206         return false;
207
208     return m_topPrivatelyControlledDomainsForCookiePartitioning.contains(topPrivatelyControlledDomain);
209 }
210
211 void NetworkStorageSession::setShouldPartitionCookiesForHosts(const Vector<String>& domainsToRemove, const Vector<String>& domainsToAdd, bool clearFirst)
212 {
213     if (clearFirst)
214         m_topPrivatelyControlledDomainsForCookiePartitioning.clear();
215
216     if (!domainsToRemove.isEmpty()) {
217         for (auto& domain : domainsToRemove)
218             m_topPrivatelyControlledDomainsForCookiePartitioning.remove(domain);
219     }
220         
221     if (!domainsToAdd.isEmpty())
222         m_topPrivatelyControlledDomainsForCookiePartitioning.add(domainsToAdd.begin(), domainsToAdd.end());
223 }
224
225 #endif // HAVE(CFNETWORK_STORAGE_PARTITIONING)
226
227 #if !PLATFORM(COCOA)
228 void NetworkStorageSession::setCookies(const Vector<Cookie>&, const URL&, const URL&)
229 {
230     // FIXME: Implement this. <https://webkit.org/b/156298>
231 }
232 #endif
233
234 }