Use SecurityOriginData more consistently in Service Worker code
[WebKit-https.git] / Source / WebKit / WebProcess / WebStorage / StorageAreaMap.cpp
1 /*
2  * Copyright (C) 2013 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "StorageAreaMap.h"
28
29 #include "StorageAreaImpl.h"
30 #include "StorageAreaMapMessages.h"
31 #include "StorageManagerMessages.h"
32 #include "StorageNamespaceImpl.h"
33 #include "WebPage.h"
34 #include "WebPageGroupProxy.h"
35 #include "WebProcess.h"
36 #include <WebCore/DOMWindow.h>
37 #include <WebCore/Document.h>
38 #include <WebCore/MainFrame.h>
39 #include <WebCore/Page.h>
40 #include <WebCore/PageGroup.h>
41 #include <WebCore/SecurityOriginData.h>
42 #include <WebCore/Storage.h>
43 #include <WebCore/StorageEventDispatcher.h>
44 #include <WebCore/StorageMap.h>
45 #include <WebCore/StorageType.h>
46
47 using namespace WebCore;
48
49 namespace WebKit {
50
51 static uint64_t generateStorageMapID()
52 {
53     static uint64_t storageMapID;
54     return ++storageMapID;
55 }
56
57 Ref<StorageAreaMap> StorageAreaMap::create(StorageNamespaceImpl* storageNamespace, Ref<WebCore::SecurityOrigin>&& securityOrigin)
58 {
59     return adoptRef(*new StorageAreaMap(storageNamespace, WTFMove(securityOrigin)));
60 }
61
62 StorageAreaMap::StorageAreaMap(StorageNamespaceImpl* storageNamespace, Ref<WebCore::SecurityOrigin>&& securityOrigin)
63     : m_storageNamespace(*storageNamespace)
64     , m_storageMapID(generateStorageMapID())
65     , m_storageType(storageNamespace->storageType())
66     , m_storageNamespaceID(storageNamespace->storageNamespaceID())
67     , m_quotaInBytes(storageNamespace->quotaInBytes())
68     , m_securityOrigin(WTFMove(securityOrigin))
69     , m_currentSeed(0)
70     , m_hasPendingClear(false)
71     , m_hasPendingGetValues(false)
72 {
73     switch (m_storageType) {
74     case StorageType::Local:
75     case StorageType::TransientLocal:
76         if (SecurityOrigin* topLevelOrigin = storageNamespace->topLevelOrigin())
77             WebProcess::singleton().parentProcessConnection()->send(Messages::StorageManager::CreateTransientLocalStorageMap(m_storageMapID, storageNamespace->storageNamespaceID(), topLevelOrigin->data(), m_securityOrigin->data()), 0);
78         else
79             WebProcess::singleton().parentProcessConnection()->send(Messages::StorageManager::CreateLocalStorageMap(m_storageMapID, storageNamespace->storageNamespaceID(), m_securityOrigin->data()), 0);
80
81         break;
82
83     case StorageType::Session:
84         WebProcess::singleton().parentProcessConnection()->send(Messages::StorageManager::CreateSessionStorageMap(m_storageMapID, storageNamespace->storageNamespaceID(), m_securityOrigin->data()), 0);
85         break;
86
87     case StorageType::EphemeralLocal:
88         // The UI process is not involved for EphemeralLocal storages.
89         return;
90     }
91
92     WebProcess::singleton().addMessageReceiver(Messages::StorageAreaMap::messageReceiverName(), m_storageMapID, *this);
93 }
94
95 StorageAreaMap::~StorageAreaMap()
96 {
97     if (m_storageType != StorageType::EphemeralLocal) {
98         WebProcess::singleton().parentProcessConnection()->send(Messages::StorageManager::DestroyStorageMap(m_storageMapID), 0);
99         WebProcess::singleton().removeMessageReceiver(Messages::StorageAreaMap::messageReceiverName(), m_storageMapID);
100     }
101
102     m_storageNamespace->didDestroyStorageAreaMap(*this);
103 }
104
105 unsigned StorageAreaMap::length()
106 {
107     loadValuesIfNeeded();
108
109     return m_storageMap->length();
110 }
111
112 String StorageAreaMap::key(unsigned index)
113 {
114     loadValuesIfNeeded();
115
116     return m_storageMap->key(index);
117 }
118
119 String StorageAreaMap::item(const String& key)
120 {
121     loadValuesIfNeeded();
122
123     return m_storageMap->getItem(key);
124 }
125
126 void StorageAreaMap::setItem(Frame* sourceFrame, StorageAreaImpl* sourceArea, const String& key, const String& value, bool& quotaException)
127 {
128     loadValuesIfNeeded();
129
130     ASSERT(m_storageMap->hasOneRef());
131
132     String oldValue;
133     quotaException = false;
134     m_storageMap->setItem(key, value, oldValue, quotaException);
135     if (quotaException)
136         return;
137
138     if (oldValue == value)
139         return;
140
141     m_pendingValueChanges.add(key);
142
143     WebProcess::singleton().parentProcessConnection()->send(Messages::StorageManager::SetItem(m_storageMapID, sourceArea->storageAreaID(), m_currentSeed, key, value, sourceFrame->document()->url()), 0);
144 }
145
146 void StorageAreaMap::removeItem(WebCore::Frame* sourceFrame, StorageAreaImpl* sourceArea, const String& key)
147 {
148     loadValuesIfNeeded();
149     ASSERT(m_storageMap->hasOneRef());
150
151     String oldValue;
152     m_storageMap->removeItem(key, oldValue);
153
154     if (oldValue.isNull())
155         return;
156
157     m_pendingValueChanges.add(key);
158
159     WebProcess::singleton().parentProcessConnection()->send(Messages::StorageManager::RemoveItem(m_storageMapID, sourceArea->storageAreaID(), m_currentSeed, key, sourceFrame->document()->url()), 0);
160 }
161
162 void StorageAreaMap::clear(WebCore::Frame* sourceFrame, StorageAreaImpl* sourceArea)
163 {
164     resetValues();
165
166     m_hasPendingClear = true;
167     m_storageMap = StorageMap::create(m_quotaInBytes);
168     WebProcess::singleton().parentProcessConnection()->send(Messages::StorageManager::Clear(m_storageMapID, sourceArea->storageAreaID(), m_currentSeed, sourceFrame->document()->url()), 0);
169 }
170
171 bool StorageAreaMap::contains(const String& key)
172 {
173     loadValuesIfNeeded();
174
175     return m_storageMap->contains(key);
176 }
177
178 void StorageAreaMap::resetValues()
179 {
180     m_storageMap = nullptr;
181
182     m_pendingValueChanges.clear();
183     m_hasPendingClear = false;
184     m_hasPendingGetValues = false;
185     m_currentSeed++;
186 }
187
188 void StorageAreaMap::loadValuesIfNeeded()
189 {
190     if (m_storageMap)
191         return;
192
193     HashMap<String, String> values;
194     // FIXME: This should use a special sendSync flag to indicate that we don't want to process incoming messages while waiting for a reply.
195     // (This flag does not yet exist). Since loadValuesIfNeeded() ends up being called from within JavaScript code, processing incoming synchronous messages
196     // could lead to weird reentrency bugs otherwise.
197     WebProcess::singleton().parentProcessConnection()->sendSync(Messages::StorageManager::GetValues(m_storageMapID, m_currentSeed), Messages::StorageManager::GetValues::Reply(values), 0);
198
199     m_storageMap = StorageMap::create(m_quotaInBytes);
200     m_storageMap->importItems(values);
201
202     // We want to ignore all changes until we get the DidGetValues message.
203     m_hasPendingGetValues = true;
204 }
205
206 void StorageAreaMap::didGetValues(uint64_t storageMapSeed)
207 {
208     if (m_currentSeed != storageMapSeed)
209         return;
210
211     ASSERT(m_hasPendingGetValues);
212     m_hasPendingGetValues = false;
213 }
214
215 void StorageAreaMap::didSetItem(uint64_t storageMapSeed, const String& key, bool quotaError)
216 {
217     if (m_currentSeed != storageMapSeed)
218         return;
219
220     ASSERT(m_pendingValueChanges.contains(key));
221
222     if (quotaError) {
223         resetValues();
224         return;
225     }
226
227     m_pendingValueChanges.remove(key);
228 }
229
230 void StorageAreaMap::didRemoveItem(uint64_t storageMapSeed, const String& key)
231 {
232     if (m_currentSeed != storageMapSeed)
233         return;
234
235     ASSERT(m_pendingValueChanges.contains(key));
236     m_pendingValueChanges.remove(key);
237 }
238
239 void StorageAreaMap::didClear(uint64_t storageMapSeed)
240 {
241     if (m_currentSeed != storageMapSeed)
242         return;
243
244     ASSERT(m_hasPendingClear);
245     m_hasPendingClear = false;
246 }
247
248 bool StorageAreaMap::shouldApplyChangeForKey(const String& key) const
249 {
250     // We have not yet loaded anything from this storage map.
251     if (!m_storageMap)
252         return false;
253
254     // Check if this storage area is currently waiting for the storage manager to update the given key.
255     // If that is the case, we don't want to apply any changes made by other storage areas, since
256     // our change was made last.
257     if (m_pendingValueChanges.contains(key))
258         return false;
259
260     return true;
261 }
262
263 void StorageAreaMap::applyChange(const String& key, const String& newValue)
264 {
265     ASSERT(!m_storageMap || m_storageMap->hasOneRef());
266
267     // There's a clear pending or getValues pending we don't want to apply any changes until we get the corresponding DidClear/DidGetValues messages.
268     if (m_hasPendingClear || m_hasPendingGetValues)
269         return;
270
271     if (!key) {
272         // A null key means clear.
273         auto newStorageMap = StorageMap::create(m_quotaInBytes);
274
275         // Any changes that were made locally after the clear must still be kept around in the new map.
276         for (auto it = m_pendingValueChanges.begin().keys(), end = m_pendingValueChanges.end().keys(); it != end; ++it) {
277             const String& key = *it;
278
279             String value = m_storageMap->getItem(key);
280             if (!value) {
281                 // This change must have been a pending remove, ignore it.
282                 continue;
283             }
284
285             String oldValue;
286             newStorageMap->setItemIgnoringQuota(key, oldValue);
287         }
288
289         m_storageMap = WTFMove(newStorageMap);
290         return;
291     }
292
293     if (!shouldApplyChangeForKey(key))
294         return;
295
296     if (!newValue) {
297         // A null new value means that the item should be removed.
298         String oldValue;
299         m_storageMap->removeItem(key, oldValue);
300         return;
301     }
302
303     m_storageMap->setItemIgnoringQuota(key, newValue);
304 }
305
306 void StorageAreaMap::dispatchStorageEvent(uint64_t sourceStorageAreaID, const String& key, const String& oldValue, const String& newValue, const String& urlString)
307 {
308     if (!sourceStorageAreaID) {
309         // This storage event originates from another process so we need to apply the change to our storage area map.
310         applyChange(key, newValue);
311     }
312
313     if (storageType() == StorageType::Session)
314         dispatchSessionStorageEvent(sourceStorageAreaID, key, oldValue, newValue, urlString);
315     else
316         dispatchLocalStorageEvent(sourceStorageAreaID, key, oldValue, newValue, urlString);
317 }
318
319 void StorageAreaMap::clearCache()
320 {
321     resetValues();
322 }
323
324 void StorageAreaMap::dispatchSessionStorageEvent(uint64_t sourceStorageAreaID, const String& key, const String& oldValue, const String& newValue, const String& urlString)
325 {
326     ASSERT(storageType() == StorageType::Session);
327
328     // Namespace IDs for session storage namespaces are equivalent to web page IDs
329     // so we can get the right page here.
330     WebPage* webPage = WebProcess::singleton().webPage(m_storageNamespaceID);
331     if (!webPage)
332         return;
333
334     Vector<RefPtr<Frame>> frames;
335
336     Page* page = webPage->corePage();
337     for (Frame* frame = &page->mainFrame(); frame; frame = frame->tree().traverseNext()) {
338         Document* document = frame->document();
339         if (!document->securityOrigin().equal(m_securityOrigin.ptr()))
340             continue;
341
342         Storage* storage = document->domWindow()->optionalSessionStorage();
343         if (!storage)
344             continue;
345
346         StorageAreaImpl& storageArea = static_cast<StorageAreaImpl&>(storage->area());
347         if (storageArea.storageAreaID() == sourceStorageAreaID) {
348             // This is the storage area that caused the event to be dispatched.
349             continue;
350         }
351
352         frames.append(frame);
353     }
354
355     StorageEventDispatcher::dispatchSessionStorageEventsToFrames(*page, frames, key, oldValue, newValue, urlString, m_securityOrigin->data());
356 }
357
358 void StorageAreaMap::dispatchLocalStorageEvent(uint64_t sourceStorageAreaID, const String& key, const String& oldValue, const String& newValue, const String& urlString)
359 {
360     ASSERT(isLocalStorage(storageType()));
361
362     Vector<RefPtr<Frame>> frames;
363
364     PageGroup& pageGroup = *WebProcess::singleton().webPageGroup(m_storageNamespaceID)->corePageGroup();
365     const HashSet<Page*>& pages = pageGroup.pages();
366     for (HashSet<Page*>::const_iterator it = pages.begin(), end = pages.end(); it != end; ++it) {
367         for (Frame* frame = &(*it)->mainFrame(); frame; frame = frame->tree().traverseNext()) {
368             Document* document = frame->document();
369             if (!document->securityOrigin().equal(m_securityOrigin.ptr()))
370                 continue;
371
372             Storage* storage = document->domWindow()->optionalLocalStorage();
373             if (!storage)
374                 continue;
375
376             StorageAreaImpl& storageArea = static_cast<StorageAreaImpl&>(storage->area());
377             if (storageArea.storageAreaID() == sourceStorageAreaID) {
378                 // This is the storage area that caused the event to be dispatched.
379                 continue;
380             }
381
382             frames.append(frame);
383         }
384     }
385
386     StorageEventDispatcher::dispatchLocalStorageEventsToFrames(pageGroup, frames, key, oldValue, newValue, urlString, m_securityOrigin->data());
387 }
388
389 } // namespace WebKit