Cache API and IDB space usages should be initialized on first quota check
[WebKit-https.git] / Source / WebCore / storage / StorageQuotaManager.cpp
1 /*
2  * Copyright (C) 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. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "StorageQuotaManager.h"
28
29 #include "StorageQuotaUser.h"
30
31 namespace WebCore {
32
33 StorageQuotaManager::~StorageQuotaManager()
34 {
35     while (!m_pendingRequests.isEmpty())
36         m_pendingRequests.takeFirst().callback(Decision::Deny);
37 }
38
39 uint64_t StorageQuotaManager::spaceUsage() const
40 {
41     uint64_t usage = 0;
42     for (auto& user : m_users)
43         usage += user->spaceUsed();
44     return usage;
45 }
46
47 void StorageQuotaManager::updateQuotaBasedOnSpaceUsage()
48 {
49     if (!m_quota)
50         return;
51
52     auto defaultQuotaStep = m_quota / 10;
53     m_quota = std::max(m_quota, defaultQuotaStep * ((spaceUsage() / defaultQuotaStep) + 1));
54 }
55
56 void StorageQuotaManager::addUser(StorageQuotaUser& user)
57 {
58     ASSERT(!m_pendingInitializationUsers.contains(&user));
59     ASSERT(!m_users.contains(&user));
60     m_pendingInitializationUsers.add(&user);
61     user.whenInitialized([this, &user, weakThis = makeWeakPtr(this)]() {
62         if (!weakThis)
63             return;
64
65         if (m_pendingInitializationUsers.remove(&user))
66             m_users.add(&user);
67
68         if (!m_pendingInitializationUsers.isEmpty())
69             return;
70
71         updateQuotaBasedOnSpaceUsage();
72         processPendingRequests({ }, ShouldDequeueFirstPendingRequest::No);
73     });
74 }
75
76 bool StorageQuotaManager::shouldAskForMoreSpace(uint64_t spaceIncrease) const
77 {
78     if (!spaceIncrease)
79         return false;
80
81     return spaceUsage() + spaceIncrease > m_quota;
82 }
83
84 void StorageQuotaManager::removeUser(StorageQuotaUser& user)
85 {
86     ASSERT(m_users.contains(&user) || m_pendingInitializationUsers.contains(&user));
87     m_users.remove(&user);
88     if (m_pendingInitializationUsers.remove(&user) && m_pendingInitializationUsers.isEmpty())
89         processPendingRequests({ }, ShouldDequeueFirstPendingRequest::No);
90 }
91
92 void StorageQuotaManager::requestSpace(uint64_t spaceIncrease, RequestCallback&& callback)
93 {
94     if (!m_pendingRequests.isEmpty() || !m_pendingInitializationUsers.isEmpty()) {
95         m_pendingRequests.append({ spaceIncrease, WTFMove(callback) });
96         return;
97     }
98
99     if (shouldAskForMoreSpace(spaceIncrease)) {
100         m_pendingRequests.append({ spaceIncrease, WTFMove(callback) });
101         askForMoreSpace(spaceIncrease);
102         return;
103     }
104     callback(Decision::Grant);
105 }
106
107 void StorageQuotaManager::askForMoreSpace(uint64_t spaceIncrease)
108 {
109     ASSERT(shouldAskForMoreSpace(spaceIncrease));
110     ASSERT(!m_isWaitingForSpaceIncreaseResponse);
111     m_isWaitingForSpaceIncreaseResponse = true;
112     m_spaceIncreaseRequester(m_quota, spaceUsage(), spaceIncrease, [this, weakThis = makeWeakPtr(*this)](Optional<uint64_t> newQuota) {
113         if (!weakThis)
114             return;
115         m_isWaitingForSpaceIncreaseResponse = false;
116         processPendingRequests(newQuota, ShouldDequeueFirstPendingRequest::Yes);
117     });
118 }
119
120 void StorageQuotaManager::processPendingRequests(Optional<uint64_t> newQuota, ShouldDequeueFirstPendingRequest shouldDequeueFirstPendingRequest)
121 {
122     if (m_pendingRequests.isEmpty())
123         return;
124
125     if (newQuota)
126         m_quota = *newQuota;
127
128     if (m_isWaitingForSpaceIncreaseResponse)
129         return;
130
131     if (!m_pendingInitializationUsers.isEmpty())
132         return;
133
134     if (shouldDequeueFirstPendingRequest == ShouldDequeueFirstPendingRequest::Yes) {
135         auto request = m_pendingRequests.takeFirst();
136         auto decision = shouldAskForMoreSpace(request.spaceIncrease) ? Decision::Deny : Decision::Grant;
137         request.callback(decision);
138     }
139
140     while (!m_pendingRequests.isEmpty()) {
141         auto& request = m_pendingRequests.first();
142
143         if (shouldAskForMoreSpace(request.spaceIncrease)) {
144             uint64_t spaceIncrease = 0;
145             for (auto& request : m_pendingRequests)
146                 spaceIncrease += request.spaceIncrease;
147             askForMoreSpace(spaceIncrease);
148             return;
149         }
150
151         m_pendingRequests.takeFirst().callback(Decision::Grant);
152     }
153 }
154
155 } // namespace WebCore