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