1/*
2 * Copyright (C) 2013-2019 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 "StorageManager.h"
28
29#include "LocalStorageDatabase.h"
30#include "LocalStorageDatabaseTracker.h"
31#include "StorageAreaMapMessages.h"
32#include "StorageManagerMessages.h"
33#include "WebProcessProxy.h"
34#include <WebCore/SecurityOriginData.h>
35#include <WebCore/SecurityOriginHash.h>
36#include <WebCore/StorageMap.h>
37#include <WebCore/TextEncoding.h>
38#include <memory>
39#include <wtf/WorkQueue.h>
40#include <wtf/threads/BinarySemaphore.h>
41
42namespace WebKit {
43using namespace WebCore;
44
45class StorageManager::StorageArea : public ThreadSafeRefCounted<StorageManager::StorageArea> {
46public:
47 static Ref<StorageArea> create(LocalStorageNamespace*, const SecurityOriginData&, unsigned quotaInBytes);
48 ~StorageArea();
49
50 const WebCore::SecurityOriginData& securityOrigin() const { return m_securityOrigin; }
51
52 void addListener(IPC::Connection::UniqueID, uint64_t storageMapID);
53 void removeListener(IPC::Connection::UniqueID, uint64_t storageMapID);
54 bool hasListener(IPC::Connection::UniqueID, uint64_t storageMapID) const;
55
56 Ref<StorageArea> clone() const;
57
58 void setItem(IPC::Connection::UniqueID sourceConnection, uint64_t sourceStorageAreaID, const String& key, const String& value, const String& urlString, bool& quotaException);
59 void setItems(const HashMap<String, String>&);
60 void removeItem(IPC::Connection::UniqueID sourceConnection, uint64_t sourceStorageAreaID, const String& key, const String& urlString);
61 void clear(IPC::Connection::UniqueID sourceConnection, uint64_t sourceStorageAreaID, const String& urlString);
62
63 const HashMap<String, String>& items() const;
64 void clear();
65
66 bool isEphemeral() const { return !m_localStorageNamespace; }
67
68private:
69 explicit StorageArea(LocalStorageNamespace*, const SecurityOriginData&, unsigned quotaInBytes);
70
71 void openDatabaseAndImportItemsIfNeeded() const;
72
73 void dispatchEvents(IPC::Connection::UniqueID sourceConnection, uint64_t sourceStorageAreaID, const String& key, const String& oldValue, const String& newValue, const String& urlString) const;
74
75 // Will be null if the storage area belongs to a session storage namespace or the storage area is in an ephemeral session.
76 LocalStorageNamespace* m_localStorageNamespace;
77 mutable RefPtr<LocalStorageDatabase> m_localStorageDatabase;
78 mutable bool m_didImportItemsFromDatabase { false };
79
80 SecurityOriginData m_securityOrigin;
81 unsigned m_quotaInBytes;
82
83 RefPtr<StorageMap> m_storageMap;
84 HashSet<std::pair<IPC::Connection::UniqueID, uint64_t>> m_eventListeners;
85};
86
87class StorageManager::LocalStorageNamespace : public ThreadSafeRefCounted<LocalStorageNamespace> {
88public:
89 static Ref<LocalStorageNamespace> create(StorageManager&, uint64_t storageManagerID);
90 ~LocalStorageNamespace();
91
92 StorageManager* storageManager() const { return &m_storageManager; }
93
94 enum class IsEphemeral : bool { No, Yes };
95 Ref<StorageArea> getOrCreateStorageArea(SecurityOriginData&&, IsEphemeral);
96 void didDestroyStorageArea(StorageArea*);
97
98 void clearStorageAreasMatchingOrigin(const SecurityOriginData&);
99 void clearAllStorageAreas();
100
101 Vector<SecurityOriginData> ephemeralOrigins() const;
102 void cloneTo(LocalStorageNamespace& newLocalStorageNamespace);
103
104private:
105 LocalStorageNamespace(StorageManager&, uint64_t storageManagerID);
106
107 StorageManager& m_storageManager;
108 uint64_t m_storageNamespaceID;
109 unsigned m_quotaInBytes;
110
111 HashMap<SecurityOriginData, RefPtr<StorageArea>> m_storageAreaMap;
112};
113
114// Suggested by https://www.w3.org/TR/webstorage/#disk-space
115const unsigned localStorageDatabaseQuotaInBytes = 5 * 1024 * 1024;
116
117class StorageManager::TransientLocalStorageNamespace : public ThreadSafeRefCounted<TransientLocalStorageNamespace> {
118public:
119 static Ref<TransientLocalStorageNamespace> create()
120 {
121 return adoptRef(*new TransientLocalStorageNamespace());
122 }
123
124 ~TransientLocalStorageNamespace()
125 {
126 }
127
128 Ref<StorageArea> getOrCreateStorageArea(SecurityOriginData&& securityOrigin)
129 {
130 return *m_storageAreaMap.ensure(securityOrigin, [this, &securityOrigin]() mutable {
131 return StorageArea::create(nullptr, WTFMove(securityOrigin), m_quotaInBytes);
132 }).iterator->value.copyRef();
133 }
134
135 Vector<SecurityOriginData> origins() const
136 {
137 Vector<SecurityOriginData> origins;
138
139 for (const auto& storageArea : m_storageAreaMap.values()) {
140 if (!storageArea->items().isEmpty())
141 origins.append(storageArea->securityOrigin());
142 }
143
144 return origins;
145 }
146
147 void clearStorageAreasMatchingOrigin(const SecurityOriginData& securityOrigin)
148 {
149 auto originAndStorageArea = m_storageAreaMap.find(securityOrigin);
150 if (originAndStorageArea != m_storageAreaMap.end())
151 originAndStorageArea->value->clear();
152 }
153
154 void clearAllStorageAreas()
155 {
156 for (auto& storageArea : m_storageAreaMap.values())
157 storageArea->clear();
158 }
159
160private:
161 explicit TransientLocalStorageNamespace()
162 {
163 }
164
165 const unsigned m_quotaInBytes = localStorageDatabaseQuotaInBytes;
166
167 HashMap<SecurityOriginData, RefPtr<StorageArea>> m_storageAreaMap;
168};
169
170auto StorageManager::StorageArea::create(LocalStorageNamespace* localStorageNamespace, const SecurityOriginData& securityOrigin, unsigned quotaInBytes) -> Ref<StorageManager::StorageArea>
171{
172 return adoptRef(*new StorageArea(localStorageNamespace, securityOrigin, quotaInBytes));
173}
174
175StorageManager::StorageArea::StorageArea(LocalStorageNamespace* localStorageNamespace, const SecurityOriginData& securityOrigin, unsigned quotaInBytes)
176 : m_localStorageNamespace(localStorageNamespace)
177 , m_securityOrigin(securityOrigin)
178 , m_quotaInBytes(quotaInBytes)
179 , m_storageMap(StorageMap::create(m_quotaInBytes))
180{
181}
182
183StorageManager::StorageArea::~StorageArea()
184{
185 ASSERT(m_eventListeners.isEmpty());
186
187 if (m_localStorageDatabase)
188 m_localStorageDatabase->close();
189
190 if (m_localStorageNamespace)
191 m_localStorageNamespace->didDestroyStorageArea(this);
192}
193
194void StorageManager::StorageArea::addListener(IPC::Connection::UniqueID connectionID, uint64_t storageMapID)
195{
196 ASSERT(!m_eventListeners.contains(std::make_pair(connectionID, storageMapID)));
197 m_eventListeners.add(std::make_pair(connectionID, storageMapID));
198}
199
200void StorageManager::StorageArea::removeListener(IPC::Connection::UniqueID connectionID, uint64_t storageMapID)
201{
202 ASSERT(isEphemeral() || m_eventListeners.contains(std::make_pair(connectionID, storageMapID)));
203 m_eventListeners.remove(std::make_pair(connectionID, storageMapID));
204}
205
206bool StorageManager::StorageArea::hasListener(IPC::Connection::UniqueID connectionID, uint64_t storageMapID) const
207{
208 return m_eventListeners.contains(std::make_pair(connectionID, storageMapID));
209}
210
211Ref<StorageManager::StorageArea> StorageManager::StorageArea::clone() const
212{
213 ASSERT(!m_localStorageNamespace);
214
215 auto storageArea = StorageArea::create(nullptr, m_securityOrigin, m_quotaInBytes);
216 storageArea->m_storageMap = m_storageMap;
217
218 return storageArea;
219}
220
221void StorageManager::StorageArea::setItem(IPC::Connection::UniqueID sourceConnection, uint64_t sourceStorageAreaID, const String& key, const String& value, const String& urlString, bool& quotaException)
222{
223 openDatabaseAndImportItemsIfNeeded();
224
225 String oldValue;
226
227 auto newStorageMap = m_storageMap->setItem(key, value, oldValue, quotaException);
228 if (newStorageMap)
229 m_storageMap = WTFMove(newStorageMap);
230
231 if (quotaException)
232 return;
233
234 if (m_localStorageDatabase)
235 m_localStorageDatabase->setItem(key, value);
236
237 dispatchEvents(sourceConnection, sourceStorageAreaID, key, oldValue, value, urlString);
238}
239
240void StorageManager::StorageArea::setItems(const HashMap<String, String>& items)
241{
242 // Import items from web process if items are not stored on disk.
243 if (!isEphemeral())
244 return;
245
246 for (auto& item : items) {
247 String oldValue;
248 bool quotaException;
249 auto newStorageMap = m_storageMap->setItem(item.key, item.value, oldValue, quotaException);
250 if (newStorageMap)
251 m_storageMap = WTFMove(newStorageMap);
252
253 if (quotaException)
254 return;
255 }
256}
257
258void StorageManager::StorageArea::removeItem(IPC::Connection::UniqueID sourceConnection, uint64_t sourceStorageAreaID, const String& key, const String& urlString)
259{
260 openDatabaseAndImportItemsIfNeeded();
261
262 String oldValue;
263 auto newStorageMap = m_storageMap->removeItem(key, oldValue);
264 if (newStorageMap)
265 m_storageMap = WTFMove(newStorageMap);
266
267 if (oldValue.isNull())
268 return;
269
270 if (m_localStorageDatabase)
271 m_localStorageDatabase->removeItem(key);
272
273 dispatchEvents(sourceConnection, sourceStorageAreaID, key, oldValue, String(), urlString);
274}
275
276void StorageManager::StorageArea::clear(IPC::Connection::UniqueID sourceConnection, uint64_t sourceStorageAreaID, const String& urlString)
277{
278 openDatabaseAndImportItemsIfNeeded();
279
280 if (!m_storageMap->length())
281 return;
282
283 m_storageMap = StorageMap::create(m_quotaInBytes);
284
285 if (m_localStorageDatabase)
286 m_localStorageDatabase->clear();
287
288 dispatchEvents(sourceConnection, sourceStorageAreaID, String(), String(), String(), urlString);
289}
290
291const HashMap<String, String>& StorageManager::StorageArea::items() const
292{
293 openDatabaseAndImportItemsIfNeeded();
294
295 return m_storageMap->items();
296}
297
298void StorageManager::StorageArea::clear()
299{
300 m_storageMap = StorageMap::create(m_quotaInBytes);
301
302 if (m_localStorageDatabase) {
303 m_localStorageDatabase->close();
304 m_localStorageDatabase = nullptr;
305 }
306
307 for (auto it = m_eventListeners.begin(), end = m_eventListeners.end(); it != end; ++it) {
308 RunLoop::main().dispatch([connectionID = it->first, destinationStorageAreaID = it->second] {
309 if (auto* connection = IPC::Connection::connection(connectionID))
310 connection->send(Messages::StorageAreaMap::ClearCache(), destinationStorageAreaID);
311 });
312 }
313}
314
315void StorageManager::StorageArea::openDatabaseAndImportItemsIfNeeded() const
316{
317 if (!m_localStorageNamespace)
318 return;
319
320 ASSERT(m_localStorageNamespace->storageManager()->m_localStorageDatabaseTracker);
321 // We open the database here even if we've already imported our items to ensure that the database is open if we need to write to it.
322 if (!m_localStorageDatabase)
323 m_localStorageDatabase = LocalStorageDatabase::create(m_localStorageNamespace->storageManager()->m_queue.copyRef(), *m_localStorageNamespace->storageManager()->m_localStorageDatabaseTracker, m_securityOrigin);
324
325 if (m_didImportItemsFromDatabase)
326 return;
327
328 m_localStorageDatabase->importItems(*m_storageMap);
329 m_didImportItemsFromDatabase = true;
330}
331
332void StorageManager::StorageArea::dispatchEvents(IPC::Connection::UniqueID sourceConnection, uint64_t sourceStorageAreaID, const String& key, const String& oldValue, const String& newValue, const String& urlString) const
333{
334 for (auto it = m_eventListeners.begin(), end = m_eventListeners.end(); it != end; ++it) {
335 sourceStorageAreaID = it->first == sourceConnection ? sourceStorageAreaID : 0;
336
337 RunLoop::main().dispatch([connectionID = it->first, sourceStorageAreaID, destinationStorageAreaID = it->second, key = key.isolatedCopy(), oldValue = oldValue.isolatedCopy(), newValue = newValue.isolatedCopy(), urlString = urlString.isolatedCopy()] {
338 if (auto* connection = IPC::Connection::connection(connectionID))
339 connection->send(Messages::StorageAreaMap::DispatchStorageEvent(sourceStorageAreaID, key, oldValue, newValue, urlString), destinationStorageAreaID);
340 });
341 }
342}
343
344Ref<StorageManager::LocalStorageNamespace> StorageManager::LocalStorageNamespace::create(StorageManager& storageManager, uint64_t storageNamespaceID)
345{
346 return adoptRef(*new LocalStorageNamespace(storageManager, storageNamespaceID));
347}
348
349// FIXME: The quota value is copied from GroupSettings.cpp.
350// We should investigate a way to share it with WebCore.
351StorageManager::LocalStorageNamespace::LocalStorageNamespace(StorageManager& storageManager, uint64_t storageNamespaceID)
352 : m_storageManager(storageManager)
353 , m_storageNamespaceID(storageNamespaceID)
354 , m_quotaInBytes(localStorageDatabaseQuotaInBytes)
355{
356}
357
358StorageManager::LocalStorageNamespace::~LocalStorageNamespace()
359{
360}
361
362auto StorageManager::LocalStorageNamespace::getOrCreateStorageArea(SecurityOriginData&& securityOrigin, IsEphemeral isEphemeral) -> Ref<StorageArea>
363{
364 RefPtr<StorageArea> protectedStorageArea;
365 return *m_storageAreaMap.ensure(securityOrigin, [&]() mutable {
366 protectedStorageArea = StorageArea::create(isEphemeral == IsEphemeral::Yes ? nullptr : this, WTFMove(securityOrigin), m_quotaInBytes);
367 return protectedStorageArea.get();
368 }).iterator->value;
369}
370
371void StorageManager::LocalStorageNamespace::didDestroyStorageArea(StorageArea* storageArea)
372{
373 ASSERT(m_storageAreaMap.contains(storageArea->securityOrigin()));
374
375 m_storageAreaMap.remove(storageArea->securityOrigin());
376 if (!m_storageAreaMap.isEmpty())
377 return;
378
379 ASSERT(m_storageManager.m_localStorageNamespaces.contains(m_storageNamespaceID));
380 m_storageManager.m_localStorageNamespaces.remove(m_storageNamespaceID);
381}
382
383void StorageManager::LocalStorageNamespace::clearStorageAreasMatchingOrigin(const SecurityOriginData& securityOrigin)
384{
385 auto originAndStorageArea = m_storageAreaMap.find(securityOrigin);
386 if (originAndStorageArea != m_storageAreaMap.end())
387 originAndStorageArea->value->clear();
388}
389
390void StorageManager::LocalStorageNamespace::clearAllStorageAreas()
391{
392 for (auto storageArea : m_storageAreaMap.values())
393 storageArea->clear();
394}
395
396Vector<SecurityOriginData> StorageManager::LocalStorageNamespace::ephemeralOrigins() const
397{
398 Vector<SecurityOriginData> origins;
399 for (const auto& storageArea : m_storageAreaMap.values()) {
400 if (!storageArea->items().isEmpty())
401 origins.append(storageArea->securityOrigin());
402 }
403 return origins;
404}
405
406void StorageManager::LocalStorageNamespace::cloneTo(LocalStorageNamespace& newLocalStorageNamespace)
407{
408 for (auto& pair : m_storageAreaMap)
409 newLocalStorageNamespace.m_storageAreaMap.add(pair.key, pair.value->clone());
410}
411
412class StorageManager::SessionStorageNamespace : public ThreadSafeRefCounted<SessionStorageNamespace> {
413public:
414 static Ref<SessionStorageNamespace> create(unsigned quotaInBytes);
415 ~SessionStorageNamespace();
416
417 bool isEmpty() const { return m_storageAreaMap.isEmpty(); }
418
419 HashSet<IPC::Connection::UniqueID> allowedConnections() const { return m_allowedConnections; }
420 void addAllowedConnection(IPC::Connection::UniqueID);
421 void removeAllowedConnection(IPC::Connection::UniqueID);
422
423 Ref<StorageArea> getOrCreateStorageArea(SecurityOriginData&&);
424
425 void cloneTo(SessionStorageNamespace& newSessionStorageNamespace);
426
427 Vector<SecurityOriginData> origins() const
428 {
429 Vector<SecurityOriginData> origins;
430
431 for (const auto& storageArea : m_storageAreaMap.values()) {
432 if (!storageArea->items().isEmpty())
433 origins.append(storageArea->securityOrigin());
434 }
435
436 return origins;
437 }
438
439 void clearStorageAreasMatchingOrigin(const SecurityOriginData& securityOrigin)
440 {
441 auto originAndStorageArea = m_storageAreaMap.find(securityOrigin);
442 if (originAndStorageArea != m_storageAreaMap.end())
443 originAndStorageArea->value->clear();
444 }
445
446 void clearAllStorageAreas()
447 {
448 for (auto& storageArea : m_storageAreaMap.values())
449 storageArea->clear();
450 }
451
452private:
453 explicit SessionStorageNamespace(unsigned quotaInBytes);
454
455 HashSet<IPC::Connection::UniqueID> m_allowedConnections;
456 unsigned m_quotaInBytes;
457
458 HashMap<SecurityOriginData, RefPtr<StorageArea>> m_storageAreaMap;
459};
460
461Ref<StorageManager::SessionStorageNamespace> StorageManager::SessionStorageNamespace::create(unsigned quotaInBytes)
462{
463 return adoptRef(*new SessionStorageNamespace(quotaInBytes));
464}
465
466StorageManager::SessionStorageNamespace::SessionStorageNamespace(unsigned quotaInBytes)
467 : m_quotaInBytes(quotaInBytes)
468{
469}
470
471StorageManager::SessionStorageNamespace::~SessionStorageNamespace()
472{
473}
474
475void StorageManager::SessionStorageNamespace::addAllowedConnection(IPC::Connection::UniqueID allowedConnection)
476{
477 m_allowedConnections.add(allowedConnection);
478}
479
480
481void StorageManager::SessionStorageNamespace::removeAllowedConnection(IPC::Connection::UniqueID allowedConnection)
482{
483 ASSERT(m_allowedConnections.contains(allowedConnection));
484 m_allowedConnections.remove(allowedConnection);
485}
486auto StorageManager::SessionStorageNamespace::getOrCreateStorageArea(SecurityOriginData&& securityOrigin) -> Ref<StorageArea>
487{
488 return *m_storageAreaMap.ensure(securityOrigin, [this, &securityOrigin]() mutable {
489 return StorageArea::create(nullptr, WTFMove(securityOrigin), m_quotaInBytes);
490 }).iterator->value.copyRef();
491}
492
493void StorageManager::SessionStorageNamespace::cloneTo(SessionStorageNamespace& newSessionStorageNamespace)
494{
495 ASSERT_UNUSED(newSessionStorageNamespace, newSessionStorageNamespace.isEmpty());
496
497 for (auto& pair : m_storageAreaMap)
498 newSessionStorageNamespace.m_storageAreaMap.add(pair.key, pair.value->clone());
499}
500
501Ref<StorageManager> StorageManager::create(const String& localStorageDirectory)
502{
503 return adoptRef(*new StorageManager(localStorageDirectory));
504}
505
506StorageManager::StorageManager(const String& localStorageDirectory)
507 : m_queue(WorkQueue::create("com.apple.WebKit.StorageManager"))
508{
509 // Make sure the encoding is initialized before we start dispatching things to the queue.
510 UTF8Encoding();
511 if (!localStorageDirectory.isNull())
512 m_localStorageDatabaseTracker = LocalStorageDatabaseTracker::create(m_queue.copyRef(), localStorageDirectory);
513}
514
515StorageManager::~StorageManager()
516{
517}
518
519void StorageManager::createSessionStorageNamespace(uint64_t storageNamespaceID, unsigned quotaInBytes)
520{
521 m_queue->dispatch([this, protectedThis = makeRef(*this), storageNamespaceID, quotaInBytes]() mutable {
522 if (m_sessionStorageNamespaces.contains(storageNamespaceID))
523 return;
524
525 m_sessionStorageNamespaces.set(storageNamespaceID, SessionStorageNamespace::create(quotaInBytes));
526 });
527}
528
529void StorageManager::destroySessionStorageNamespace(uint64_t storageNamespaceID)
530{
531 m_queue->dispatch([this, protectedThis = makeRef(*this), storageNamespaceID] {
532 ASSERT(m_sessionStorageNamespaces.contains(storageNamespaceID));
533 if (m_sessionStorageNamespaces.get(storageNamespaceID)->allowedConnections().isEmpty())
534 m_sessionStorageNamespaces.remove(storageNamespaceID);
535 });
536}
537
538void StorageManager::addAllowedSessionStorageNamespaceConnection(uint64_t storageNamespaceID, IPC::Connection& allowedConnection)
539{
540 auto allowedConnectionID = allowedConnection.uniqueID();
541 m_queue->dispatch([this, protectedThis = makeRef(*this), allowedConnectionID, storageNamespaceID]() mutable {
542 ASSERT(m_sessionStorageNamespaces.contains(storageNamespaceID));
543
544 m_sessionStorageNamespaces.get(storageNamespaceID)->addAllowedConnection(allowedConnectionID);
545 });
546}
547
548void StorageManager::removeAllowedSessionStorageNamespaceConnection(uint64_t storageNamespaceID, IPC::Connection& allowedConnection)
549{
550 auto allowedConnectionID = allowedConnection.uniqueID();
551 m_queue->dispatch([this, protectedThis = makeRef(*this), allowedConnectionID, storageNamespaceID]() mutable {
552 ASSERT(m_sessionStorageNamespaces.contains(storageNamespaceID));
553
554 m_sessionStorageNamespaces.get(storageNamespaceID)->removeAllowedConnection(allowedConnectionID);
555 });
556}
557
558void StorageManager::cloneSessionStorageNamespace(uint64_t storageNamespaceID, uint64_t newStorageNamespaceID)
559{
560 m_queue->dispatch([this, protectedThis = makeRef(*this), storageNamespaceID, newStorageNamespaceID] {
561 SessionStorageNamespace* sessionStorageNamespace = m_sessionStorageNamespaces.get(storageNamespaceID);
562 if (!sessionStorageNamespace) {
563 // FIXME: We can get into this situation if someone closes the originating page from within a
564 // createNewPage callback. We bail for now, but we should really find a way to keep the session storage alive
565 // so we we'll clone the session storage correctly.
566 return;
567 }
568
569 SessionStorageNamespace* newSessionStorageNamespace = m_sessionStorageNamespaces.get(newStorageNamespaceID);
570 ASSERT(newSessionStorageNamespace);
571
572 sessionStorageNamespace->cloneTo(*newSessionStorageNamespace);
573
574 if (!m_localStorageDatabaseTracker) {
575 if (auto* localStorageNamespace = m_localStorageNamespaces.get(storageNamespaceID)) {
576 LocalStorageNamespace* newlocalStorageNamespace = getOrCreateLocalStorageNamespace(newStorageNamespaceID);
577 localStorageNamespace->cloneTo(*newlocalStorageNamespace);
578 }
579 }
580 });
581}
582
583void StorageManager::processDidCloseConnection(IPC::Connection& connection)
584{
585 m_queue->dispatch([this, protectedThis = makeRef(*this), connectionID = connection.uniqueID()]() mutable {
586 Vector<std::pair<IPC::Connection::UniqueID, uint64_t>> connectionAndStorageMapIDPairsToRemove;
587 for (auto& storageArea : m_storageAreasByConnection) {
588 if (storageArea.key.first != connectionID)
589 continue;
590
591 storageArea.value->removeListener(storageArea.key.first, storageArea.key.second);
592 connectionAndStorageMapIDPairsToRemove.append(storageArea.key);
593 }
594
595 for (auto& pair : connectionAndStorageMapIDPairsToRemove)
596 m_storageAreasByConnection.remove(pair);
597
598 Vector<uint64_t> sessionStorageNameSpaceIDsToRemove;
599 for (auto& sessionStorageNamespace : m_sessionStorageNamespaces) {
600 if (sessionStorageNamespace.value->allowedConnections().contains(connectionID))
601 sessionStorageNamespace.value->removeAllowedConnection(connectionID);
602
603 if (sessionStorageNamespace.value->allowedConnections().isEmpty())
604 sessionStorageNameSpaceIDsToRemove.append(sessionStorageNamespace.key);
605 }
606
607 for (auto id : sessionStorageNameSpaceIDsToRemove)
608 m_sessionStorageNamespaces.remove(id);
609 });
610}
611
612void StorageManager::getSessionStorageOrigins(Function<void(HashSet<WebCore::SecurityOriginData>&&)>&& completionHandler)
613{
614 m_queue->dispatch([this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)]() mutable {
615 HashSet<SecurityOriginData> origins;
616
617 for (const auto& sessionStorageNamespace : m_sessionStorageNamespaces.values()) {
618 for (auto& origin : sessionStorageNamespace->origins())
619 origins.add(origin);
620 }
621
622 RunLoop::main().dispatch([origins = WTFMove(origins), completionHandler = WTFMove(completionHandler)]() mutable {
623 completionHandler(WTFMove(origins));
624 });
625 });
626}
627
628void StorageManager::deleteSessionStorageOrigins(Function<void()>&& completionHandler)
629{
630 m_queue->dispatch([this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)]() mutable {
631 for (auto& sessionStorageNamespace : m_sessionStorageNamespaces.values())
632 sessionStorageNamespace->clearAllStorageAreas();
633
634 RunLoop::main().dispatch(WTFMove(completionHandler));
635 });
636}
637
638void StorageManager::deleteSessionStorageEntriesForOrigins(const Vector<WebCore::SecurityOriginData>& origins, Function<void()>&& completionHandler)
639{
640 Vector<WebCore::SecurityOriginData> copiedOrigins;
641 copiedOrigins.reserveInitialCapacity(origins.size());
642
643 for (auto& origin : origins)
644 copiedOrigins.uncheckedAppend(origin.isolatedCopy());
645
646 m_queue->dispatch([this, protectedThis = makeRef(*this), copiedOrigins = WTFMove(copiedOrigins), completionHandler = WTFMove(completionHandler)]() mutable {
647 for (auto& origin : copiedOrigins) {
648 for (auto& sessionStorageNamespace : m_sessionStorageNamespaces.values())
649 sessionStorageNamespace->clearStorageAreasMatchingOrigin(origin);
650 }
651
652 RunLoop::main().dispatch(WTFMove(completionHandler));
653 });
654}
655
656void StorageManager::getLocalStorageOrigins(Function<void(HashSet<WebCore::SecurityOriginData>&&)>&& completionHandler)
657{
658 m_queue->dispatch([this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)]() mutable {
659 HashSet<SecurityOriginData> origins;
660
661 if (m_localStorageDatabaseTracker) {
662 for (auto& origin : m_localStorageDatabaseTracker->origins())
663 origins.add(origin);
664 } else {
665 for (const auto& localStorageNameSpace : m_localStorageNamespaces.values()) {
666 for (auto& origin : localStorageNameSpace->ephemeralOrigins())
667 origins.add(origin);
668 }
669 }
670
671 for (auto& transientLocalStorageNamespace : m_transientLocalStorageNamespaces.values()) {
672 for (auto& origin : transientLocalStorageNamespace->origins())
673 origins.add(origin);
674 }
675
676 RunLoop::main().dispatch([origins = WTFMove(origins), completionHandler = WTFMove(completionHandler)]() mutable {
677 completionHandler(WTFMove(origins));
678 });
679 });
680}
681
682void StorageManager::getLocalStorageOriginDetails(Function<void(Vector<LocalStorageDatabaseTracker::OriginDetails>&&)>&& completionHandler)
683{
684 m_queue->dispatch([this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)]() mutable {
685 Vector<LocalStorageDatabaseTracker::OriginDetails> originDetails;
686 if (m_localStorageDatabaseTracker)
687 originDetails = m_localStorageDatabaseTracker->originDetails();
688
689 RunLoop::main().dispatch([originDetails = WTFMove(originDetails), completionHandler = WTFMove(completionHandler)]() mutable {
690 completionHandler(WTFMove(originDetails));
691 });
692 });
693}
694
695void StorageManager::deleteLocalStorageEntriesForOrigin(const SecurityOriginData& securityOrigin)
696{
697 m_queue->dispatch([this, protectedThis = makeRef(*this), copiedOrigin = securityOrigin.isolatedCopy()]() mutable {
698 for (auto& localStorageNamespace : m_localStorageNamespaces.values())
699 localStorageNamespace->clearStorageAreasMatchingOrigin(copiedOrigin);
700
701 for (auto& transientLocalStorageNamespace : m_transientLocalStorageNamespaces.values())
702 transientLocalStorageNamespace->clearStorageAreasMatchingOrigin(copiedOrigin);
703
704 if (m_localStorageDatabaseTracker)
705 m_localStorageDatabaseTracker->deleteDatabaseWithOrigin(copiedOrigin);
706 });
707}
708
709void StorageManager::deleteLocalStorageOriginsModifiedSince(WallTime time, Function<void()>&& completionHandler)
710{
711 m_queue->dispatch([this, protectedThis = makeRef(*this), time, completionHandler = WTFMove(completionHandler)]() mutable {
712 if (m_localStorageDatabaseTracker) {
713 auto originsToDelete = m_localStorageDatabaseTracker->databasesModifiedSince(time);
714
715 for (auto& transientLocalStorageNamespace : m_transientLocalStorageNamespaces.values())
716 transientLocalStorageNamespace->clearAllStorageAreas();
717
718 for (const auto& origin : originsToDelete) {
719 for (auto& localStorageNamespace : m_localStorageNamespaces.values())
720 localStorageNamespace->clearStorageAreasMatchingOrigin(origin);
721
722 m_localStorageDatabaseTracker->deleteDatabaseWithOrigin(origin);
723 }
724 } else {
725 for (auto& localStorageNamespace : m_localStorageNamespaces.values())
726 localStorageNamespace->clearAllStorageAreas();
727 }
728
729 RunLoop::main().dispatch(WTFMove(completionHandler));
730 });
731}
732
733void StorageManager::deleteLocalStorageEntriesForOrigins(const Vector<WebCore::SecurityOriginData>& origins, Function<void()>&& completionHandler)
734{
735 Vector<SecurityOriginData> copiedOrigins;
736 copiedOrigins.reserveInitialCapacity(origins.size());
737
738 for (auto& origin : origins)
739 copiedOrigins.uncheckedAppend(origin.isolatedCopy());
740
741 m_queue->dispatch([this, protectedThis = makeRef(*this), copiedOrigins = WTFMove(copiedOrigins), completionHandler = WTFMove(completionHandler)]() mutable {
742 for (auto& origin : copiedOrigins) {
743 for (auto& localStorageNamespace : m_localStorageNamespaces.values())
744 localStorageNamespace->clearStorageAreasMatchingOrigin(origin);
745
746 for (auto& transientLocalStorageNamespace : m_transientLocalStorageNamespaces.values())
747 transientLocalStorageNamespace->clearStorageAreasMatchingOrigin(origin);
748
749 if (m_localStorageDatabaseTracker)
750 m_localStorageDatabaseTracker->deleteDatabaseWithOrigin(origin);
751 }
752
753 RunLoop::main().dispatch(WTFMove(completionHandler));
754 });
755}
756
757void StorageManager::createLocalStorageMap(IPC::Connection& connection, uint64_t storageMapID, uint64_t storageNamespaceID, SecurityOriginData&& securityOriginData)
758{
759 m_queue->dispatch([this, protectedThis = makeRef(*this), connectionID = connection.uniqueID(), storageMapID, storageNamespaceID, securityOriginData = securityOriginData.isolatedCopy()]() mutable {
760 std::pair<IPC::Connection::UniqueID, uint64_t> connectionAndStorageMapIDPair(connectionID, storageMapID);
761
762 ASSERT((HashMap<std::pair<IPC::Connection::UniqueID, uint64_t>, RefPtr<StorageArea>>::isValidKey(connectionAndStorageMapIDPair)));
763
764 auto result = m_storageAreasByConnection.add(connectionAndStorageMapIDPair, nullptr);
765 ASSERT(result.isNewEntry);
766 ASSERT((HashMap<uint64_t, RefPtr<LocalStorageNamespace>>::isValidKey(storageNamespaceID)));
767
768 LocalStorageNamespace* localStorageNamespace = getOrCreateLocalStorageNamespace(storageNamespaceID);
769 ASSERT(localStorageNamespace);
770
771 auto storageArea = localStorageNamespace->getOrCreateStorageArea(WTFMove(securityOriginData), m_localStorageDatabaseTracker ? StorageManager::LocalStorageNamespace::IsEphemeral::No : StorageManager::LocalStorageNamespace::IsEphemeral::Yes);
772 storageArea->addListener(connectionID, storageMapID);
773
774 result.iterator->value = WTFMove(storageArea);
775 });
776}
777
778void StorageManager::createTransientLocalStorageMap(IPC::Connection& connection, uint64_t storageMapID, uint64_t storageNamespaceID, SecurityOriginData&& topLevelOriginData, SecurityOriginData&& origin)
779{
780 m_queue->dispatch([this, protectedThis = makeRef(*this), connectionID = connection.uniqueID(), storageMapID, storageNamespaceID, topLevelOriginData = topLevelOriginData.isolatedCopy(), origin = origin.isolatedCopy()]() mutable {
781 ASSERT(m_storageAreasByConnection.isValidKey({ connectionID, storageMapID }));
782
783 // See if we already have session storage for this connection/origin combo.
784 // If so, update the map with the new ID, otherwise keep on trucking.
785 for (auto it = m_storageAreasByConnection.begin(), end = m_storageAreasByConnection.end(); it != end; ++it) {
786 if (it->key.first != connectionID)
787 continue;
788 Ref<StorageArea> area = *it->value;
789 if (!area->isEphemeral())
790 continue;
791 if (!origin.securityOrigin()->isSameSchemeHostPort(area->securityOrigin().securityOrigin().get()))
792 continue;
793 area->addListener(connectionID, storageMapID);
794 // If the storageMapID used as key in m_storageAreasByConnection is no longer one of the StorageArea's listeners, then this means
795 // that destroyStorageMap() was already called for that storageMapID but it decided not to remove it from m_storageAreasByConnection
796 // so that we could reuse it later on for the same connection/origin combo. In this case, it is safe to remove the previous
797 // storageMapID from m_storageAreasByConnection.
798 if (!area->hasListener(connectionID, it->key.second))
799 m_storageAreasByConnection.remove(it);
800 m_storageAreasByConnection.add({ connectionID, storageMapID }, WTFMove(area));
801 return;
802 }
803
804 auto& slot = m_storageAreasByConnection.add({ connectionID, storageMapID }, nullptr).iterator->value;
805 ASSERT(!slot);
806
807 auto* transientLocalStorageNamespace = getOrCreateTransientLocalStorageNamespace(storageNamespaceID, WTFMove(topLevelOriginData));
808
809 auto storageArea = transientLocalStorageNamespace->getOrCreateStorageArea(WTFMove(origin));
810 storageArea->addListener(connectionID, storageMapID);
811
812 slot = WTFMove(storageArea);
813 });
814}
815
816void StorageManager::createSessionStorageMap(IPC::Connection& connection, uint64_t storageMapID, uint64_t storageNamespaceID, SecurityOriginData&& securityOriginData)
817{
818 m_queue->dispatch([this, protectedThis = makeRef(*this), connectionID = connection.uniqueID(), storageMapID, storageNamespaceID, securityOriginData = securityOriginData.isolatedCopy()]() mutable {
819 ASSERT(m_sessionStorageNamespaces.isValidKey(storageNamespaceID));
820
821 SessionStorageNamespace* sessionStorageNamespace = m_sessionStorageNamespaces.get(storageNamespaceID);
822 if (!sessionStorageNamespace) {
823 // We're getting an incoming message from the web process that's for session storage for a web page
824 // that has already been closed, just ignore it.
825 return;
826 }
827
828 ASSERT(m_storageAreasByConnection.isValidKey({ connectionID, storageMapID }));
829
830 auto& slot = m_storageAreasByConnection.add({ connectionID, storageMapID }, nullptr).iterator->value;
831 ASSERT(!slot);
832 ASSERT(sessionStorageNamespace->allowedConnections().contains(connectionID));
833
834 auto storageArea = sessionStorageNamespace->getOrCreateStorageArea(WTFMove(securityOriginData));
835 storageArea->addListener(connectionID, storageMapID);
836
837 slot = WTFMove(storageArea);
838 });
839}
840
841void StorageManager::destroyStorageMap(IPC::Connection& connection, uint64_t storageMapID)
842{
843 m_queue->dispatch([this, protectedThis = makeRef(*this), connectionID = connection.uniqueID(), storageMapID]() mutable {
844 std::pair<IPC::Connection::UniqueID, uint64_t> connectionAndStorageMapIDPair(connectionID, storageMapID);
845 ASSERT(m_storageAreasByConnection.isValidKey(connectionAndStorageMapIDPair));
846
847 auto it = m_storageAreasByConnection.find(connectionAndStorageMapIDPair);
848 if (it == m_storageAreasByConnection.end()) {
849 // The connection has been removed because the last page was closed.
850 return;
851 }
852
853 it->value->removeListener(connectionID, storageMapID);
854
855 // Don't remove session storage maps. The web process may reconnect and expect the data to still be around.
856 if (it->value->isEphemeral())
857 return;
858
859 m_storageAreasByConnection.remove(connectionAndStorageMapIDPair);
860 });
861}
862
863static void didGetValues(IPC::Connection& connection, uint64_t storageMapID, const HashMap<String, String>& items, GetValuesCallback&& completionHandler)
864{
865 RunLoop::main().dispatch([items = crossThreadCopy(items), completionHandler = WTFMove(completionHandler)]() mutable {
866 completionHandler(items);
867 });
868}
869
870void StorageManager::getValues(IPC::Connection& connection, WebCore::SecurityOriginData&& securityOriginData, uint64_t storageMapID, uint64_t storageMapSeed, GetValuesCallback&& completionHandler)
871{
872 m_queue->dispatch([this, protectedThis = makeRef(*this), connection = makeRef(connection), securityOriginData = securityOriginData.isolatedCopy(), storageMapID, storageMapSeed, completionHandler = WTFMove(completionHandler)]() mutable {
873 auto* storageArea = findStorageArea(connection.get(), storageMapID);
874
875 // This is a session storage area for a page that has already been closed. Ignore it.
876 if (!storageArea)
877 return didGetValues(connection.get(), storageMapID, { }, WTFMove(completionHandler));
878
879 didGetValues(connection.get(), storageMapID, storageArea->items(), WTFMove(completionHandler));
880 connection->send(Messages::StorageAreaMap::DidGetValues(storageMapSeed), storageMapID);
881 });
882}
883
884void StorageManager::setItem(IPC::Connection& connection, WebCore::SecurityOriginData&& securityOriginData, uint64_t storageMapID, uint64_t sourceStorageAreaID, uint64_t storageMapSeed, const String& key, const String& value, const String& urlString)
885{
886 m_queue->dispatch([this, protectedThis = makeRef(*this), connection = makeRef(connection), securityOriginData = securityOriginData.isolatedCopy(), storageMapID, sourceStorageAreaID, storageMapSeed, key = key.isolatedCopy(), value = value.isolatedCopy(), urlString = urlString.isolatedCopy()]() mutable {
887 auto* storageArea = findStorageArea(connection.get(), storageMapID);
888
889 // This is a session storage area for a page that has already been closed. Ignore it.
890 if (!storageArea)
891 return;
892
893 bool quotaError;
894 storageArea->setItem(connection->uniqueID(), sourceStorageAreaID, key, value, urlString, quotaError);
895 connection->send(Messages::StorageAreaMap::DidSetItem(storageMapSeed, key, quotaError), storageMapID);
896 });
897}
898
899void StorageManager::setItems(IPC::Connection& connection, uint64_t storageMapID, const HashMap<String, String>& items)
900{
901 m_queue->dispatch([this, protectedThis = makeRef(*this), connection = makeRef(connection), storageMapID, items = crossThreadCopy(items)]() mutable {
902 if (auto* storageArea = findStorageArea(connection.get(), storageMapID))
903 storageArea->setItems(items);
904 });
905}
906
907void StorageManager::removeItem(IPC::Connection& connection, WebCore::SecurityOriginData&& securityOriginData, uint64_t storageMapID, uint64_t sourceStorageAreaID, uint64_t storageMapSeed, const String& key, const String& urlString)
908{
909 m_queue->dispatch([this, protectedThis = makeRef(*this), connection = makeRef(connection), securityOriginData = securityOriginData.isolatedCopy(), storageMapID, sourceStorageAreaID, storageMapSeed, key = key.isolatedCopy(), urlString = urlString.isolatedCopy()]() mutable {
910 auto* storageArea = findStorageArea(connection.get(), storageMapID);
911
912 // This is a session storage area for a page that has already been closed. Ignore it.
913 if (!storageArea)
914 return;
915
916 storageArea->removeItem(connection->uniqueID(), sourceStorageAreaID, key, urlString);
917 connection->send(Messages::StorageAreaMap::DidRemoveItem(storageMapSeed, key), storageMapID);
918 });
919}
920
921void StorageManager::clear(IPC::Connection& connection, WebCore::SecurityOriginData&& securityOriginData, uint64_t storageMapID, uint64_t sourceStorageAreaID, uint64_t storageMapSeed, const String& urlString)
922{
923 m_queue->dispatch([this, protectedThis = makeRef(*this), connection = makeRef(connection), securityOriginData = securityOriginData.isolatedCopy(), storageMapID, sourceStorageAreaID, storageMapSeed, urlString = urlString.isolatedCopy()]() mutable {
924 auto* storageArea = findStorageArea(connection.get(), storageMapID);
925
926 // This is a session storage area for a page that has already been closed. Ignore it.
927 if (!storageArea)
928 return;
929
930 storageArea->clear(connection->uniqueID(), sourceStorageAreaID, urlString);
931 connection->send(Messages::StorageAreaMap::DidClear(storageMapSeed), storageMapID);
932 });
933}
934
935void StorageManager::waitUntilWritesFinished()
936{
937 BinarySemaphore semaphore;
938 m_queue->dispatch([this, &semaphore] {
939 Vector<std::pair<IPC::Connection::UniqueID, uint64_t>> connectionAndStorageMapIDPairsToRemove;
940 for (auto& connectionStorageAreaPair : m_storageAreasByConnection) {
941 connectionStorageAreaPair.value->removeListener(connectionStorageAreaPair.key.first, connectionStorageAreaPair.key.second);
942 connectionAndStorageMapIDPairsToRemove.append(connectionStorageAreaPair.key);
943 }
944
945 for (auto& connectionStorageAreaPair : connectionAndStorageMapIDPairsToRemove)
946 m_storageAreasByConnection.remove(connectionStorageAreaPair);
947
948 semaphore.signal();
949 });
950 semaphore.wait();
951}
952
953void StorageManager::suspend(CompletionHandler<void()>&& completionHandler)
954{
955 if (!m_localStorageDatabaseTracker)
956 return;
957
958 Locker<Lock> stateLocker(m_stateLock);
959 if (m_state != State::Running)
960 return;
961 m_state = State::WillSuspend;
962
963 m_queue->dispatch([this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)] () mutable {
964 Locker<Lock> stateLocker(m_stateLock);
965 ASSERT(m_state != State::Suspended);
966
967 completionHandler();
968
969 if (m_state != State::WillSuspend)
970 return;
971 m_state = State::Suspended;
972 while (m_state == State::Suspended)
973 m_stateChangeCondition.wait(m_stateLock);
974 ASSERT(m_state == State::Running);
975 });
976}
977
978void StorageManager::resume()
979{
980 if (!m_localStorageDatabaseTracker)
981 return;
982
983 Locker<Lock> stateLocker(m_stateLock);
984 auto previousState = m_state;
985 m_state = State::Running;
986 if (previousState == State::Suspended)
987 m_stateChangeCondition.notifyOne();
988}
989
990StorageManager::StorageArea* StorageManager::findStorageArea(IPC::Connection& connection, uint64_t storageMapID) const
991{
992 std::pair<IPC::Connection::UniqueID, uint64_t> connectionAndStorageMapIDPair(connection.uniqueID(), storageMapID);
993
994 if (!m_storageAreasByConnection.isValidKey(connectionAndStorageMapIDPair))
995 return nullptr;
996
997 return m_storageAreasByConnection.get(connectionAndStorageMapIDPair);
998}
999
1000StorageManager::LocalStorageNamespace* StorageManager::getOrCreateLocalStorageNamespace(uint64_t storageNamespaceID)
1001{
1002 if (!m_localStorageNamespaces.isValidKey(storageNamespaceID))
1003 return nullptr;
1004
1005 return m_localStorageNamespaces.ensure(storageNamespaceID, [this, storageNamespaceID]() {
1006 return LocalStorageNamespace::create(*this, storageNamespaceID);
1007 }).iterator->value.get();
1008}
1009
1010StorageManager::TransientLocalStorageNamespace* StorageManager::getOrCreateTransientLocalStorageNamespace(uint64_t storageNamespaceID, WebCore::SecurityOriginData&& topLevelOrigin)
1011{
1012 if (!m_transientLocalStorageNamespaces.isValidKey({ storageNamespaceID, topLevelOrigin }))
1013 return nullptr;
1014
1015 return m_transientLocalStorageNamespaces.ensure({ storageNamespaceID, WTFMove(topLevelOrigin) }, [](){
1016 return TransientLocalStorageNamespace::create();
1017 }).iterator->value.get();
1018}
1019
1020} // namespace WebKit
1021