Changeset 276689 in webkit


Ignore:
Timestamp:
Apr 27, 2021 9:43:38 PM (15 months ago)
Author:
Chris Dumez
Message:

Improve local storage size estimation for quota limitation
https://bugs.webkit.org/show_bug.cgi?id=225123

Reviewed by Alex Christensen.

Source/WebCore:

Improve local storage size estimation for quota limitation:

  • Rely on String::sizeInBytes() to compute the String size, instead of using String::length() * sizeof(UChar)
  • Make estimation consistent between StorageMap & LocalStorageDatabase
  • storage/StorageMap.cpp:

(WebCore::StorageMap::setItem):
(WebCore::StorageMap::setItemIgnoringQuota):
(WebCore::StorageMap::removeItem):
(WebCore::StorageMap::clear):
(WebCore::StorageMap::importItems):
(WebCore::StorageMap::Impl::copy const):

  • storage/StorageMap.h:

Source/WebKit:

Improve local storage size estimation for quota limitation:

  • Rely on String::sizeInBytes() to compute the String size, instead of using String::length() * sizeof(UChar)
  • Make estimation consistent between StorageMap & LocalStorageDatabase
  • NetworkProcess/WebStorage/LocalStorageDatabase.cpp:

(WebKit::LocalStorageDatabase::removeItem):
(WebKit::LocalStorageDatabase::setItem):
(WebKit::estimateEntrySize): Deleted.

  • NetworkProcess/WebStorage/LocalStorageDatabase.h:

LayoutTests:

Update test to use unicode in the Strings so that the file reaches the quota without
changing the test too much. The test was using ASCII and was thus able to store all
the strings without reaching the quota due to our updated String size calculation.

  • storage/domstorage/quota.html:
Location:
trunk
Files:
8 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r276688 r276689  
     12021-04-27  Chris Dumez  <cdumez@apple.com>
     2
     3        Improve local storage size estimation for quota limitation
     4        https://bugs.webkit.org/show_bug.cgi?id=225123
     5
     6        Reviewed by Alex Christensen.
     7
     8        Update test to use unicode in the Strings so that the file reaches the quota without
     9        changing the test too much. The test was using ASCII and was thus able to store all
     10        the strings without reaching the quota due to our updated String size calculation.
     11
     12        * storage/domstorage/quota.html:
     13
    1142021-04-27  Wenson Hsieh  <wenson_hsieh@apple.com>
    215
  • trunk/LayoutTests/storage/domstorage/quota.html

    r217390 r276689  
    2323
    2424    debug("Creating 'data' which contains 64K of data");
    25     data = "X";
     25    data = "\u03BB";
    2626    for (var i=0; i<16; i++)
    2727        data += data;
  • trunk/Source/WebCore/ChangeLog

    r276688 r276689  
     12021-04-27  Chris Dumez  <cdumez@apple.com>
     2
     3        Improve local storage size estimation for quota limitation
     4        https://bugs.webkit.org/show_bug.cgi?id=225123
     5
     6        Reviewed by Alex Christensen.
     7
     8        Improve local storage size estimation for quota limitation:
     9        - Rely on String::sizeInBytes() to compute the String size, instead of using
     10          String::length() * sizeof(UChar)
     11        - Make estimation consistent between StorageMap & LocalStorageDatabase
     12
     13        * storage/StorageMap.cpp:
     14        (WebCore::StorageMap::setItem):
     15        (WebCore::StorageMap::setItemIgnoringQuota):
     16        (WebCore::StorageMap::removeItem):
     17        (WebCore::StorageMap::clear):
     18        (WebCore::StorageMap::importItems):
     19        (WebCore::StorageMap::Impl::copy const):
     20        * storage/StorageMap.h:
     21
    1222021-04-27  Wenson Hsieh  <wenson_hsieh@apple.com>
    223
  • trunk/Source/WebCore/storage/StorageMap.cpp

    r276659 r276689  
    2727#include "StorageMap.h"
    2828
     29#include <wtf/CheckedArithmetic.h>
    2930#include <wtf/SetForScope.h>
    3031
     
    9495        m_impl = m_impl->copy();
    9596
    96     // Quota tracking.  This is done in a couple of steps to keep the overflow tracking simple.
    97     unsigned newLength = m_impl->currentLength;
    98     bool overflow = newLength + value.length() < newLength;
    99     newLength += value.length();
     97    oldValue = m_impl->map.get(key);
    10098
    101     oldValue = m_impl->map.get(key);
    102     overflow |= newLength - oldValue.length() > newLength;
    103     newLength -= oldValue.length();
    104 
    105     unsigned adjustedKeyLength = oldValue.isNull() ? key.length() : 0;
    106     overflow |= newLength + adjustedKeyLength < newLength;
    107     newLength += adjustedKeyLength;
    108 
    109     ASSERT(!overflow);  // Overflow is bad...even if quotas are off.
    110     bool overQuota = newLength > m_quotaSize / sizeof(UChar);
    111     if (m_quotaSize != noQuota && (overflow || overQuota)) {
     99    // Quota tracking. This is done in a couple of steps to keep the overflow tracking simple.
     100    CheckedUint32 newSize = m_impl->currentSize;
     101    newSize -= oldValue.sizeInBytes();
     102    newSize += value.sizeInBytes();
     103    if (oldValue.isNull())
     104        newSize += key.sizeInBytes();
     105    if (m_quotaSize != noQuota && (newSize.hasOverflowed() || newSize.unsafeGet() > m_quotaSize)) {
    112106        quotaException = true;
    113107        return;
    114108    }
    115     m_impl->currentLength = newLength;
     109    m_impl->currentSize = newSize.unsafeGet();
    116110
    117111    HashMap<String, String>::AddResult addResult = m_impl->map.add(key, value);
     
    124118void StorageMap::setItemIgnoringQuota(const String& key, const String& value)
    125119{
    126     SetForScope<unsigned> quotaSizeChange(m_quotaSize, static_cast<unsigned>(noQuota));
     120    SetForScope<unsigned> quotaSizeChange(m_quotaSize, noQuota);
    127121
    128122    String oldValue;
     
    139133
    140134    oldValue = m_impl->map.take(key);
    141     if (!oldValue.isNull()) {
    142         invalidateIterator();
    143         ASSERT(m_impl->currentLength - key.length() <= m_impl->currentLength);
    144         m_impl->currentLength -= key.length();
    145     }
    146     ASSERT(m_impl->currentLength - oldValue.length() <= m_impl->currentLength);
    147     m_impl->currentLength -= oldValue.length();
     135    if (oldValue.isNull())
     136        return;
     137
     138    invalidateIterator();
     139    ASSERT(m_impl->currentSize - key.sizeInBytes() <= m_impl->currentSize);
     140    m_impl->currentSize -= key.sizeInBytes();
     141    ASSERT(m_impl->currentSize - oldValue.sizeInBytes() <= m_impl->currentSize);
     142    m_impl->currentSize -= oldValue.sizeInBytes();
    148143}
    149144
     
    155150    }
    156151    m_impl->map.clear();
    157     m_impl->currentLength = 0;
     152    m_impl->currentSize = 0;
    158153}
    159154
     
    169164        m_impl->map = WTFMove(items);
    170165        for (auto& pair : m_impl->map) {
    171             ASSERT(m_impl->currentLength + pair.key.length() + pair.value.length() >= m_impl->currentLength);
    172             m_impl->currentLength += (pair.key.length() + pair.value.length());
     166            ASSERT(m_impl->currentSize + pair.key.sizeInBytes() + pair.value.sizeInBytes() >= m_impl->currentSize);
     167            m_impl->currentSize += (pair.key.sizeInBytes() + pair.value.sizeInBytes());
    173168        }
    174169        return;
     
    179174        auto& value = item.value;
    180175
    181         ASSERT(m_impl->currentLength + key.length() + value.length() >= m_impl->currentLength);
    182         m_impl->currentLength += (key.length() + value.length());
    183        
     176        ASSERT(m_impl->currentSize + key.sizeInBytes() + value.sizeInBytes() >= m_impl->currentSize);
     177        m_impl->currentSize += (key.sizeInBytes() + value.sizeInBytes());
     178
    184179        auto result = m_impl->map.add(WTFMove(key), WTFMove(value));
    185180        ASSERT_UNUSED(result, result.isNewEntry); // True if the key didn't exist previously.
     
    191186    auto copy = Impl::create();
    192187    copy->map = map;
    193     copy->currentLength = currentLength;
     188    copy->currentSize = currentSize;
    194189    return copy;
    195190}
  • trunk/Source/WebCore/storage/StorageMap.h

    r276659 r276689  
    5757    bool isShared() const { return !m_impl->hasOneRef(); }
    5858
    59     static constexpr unsigned noQuota = UINT_MAX;
     59    static constexpr unsigned noQuota = std::numeric_limits<unsigned>::max();
    6060
    6161private:
     
    7474        HashMap<String, String>::iterator iterator { map.end() };
    7575        unsigned iteratorIndex { std::numeric_limits<unsigned>::max() };
    76         unsigned currentLength { 0 }; // Measured in UChars.
     76        unsigned currentSize { 0 };
    7777    };
    7878
  • trunk/Source/WebKit/ChangeLog

    r276688 r276689  
     12021-04-27  Chris Dumez  <cdumez@apple.com>
     2
     3        Improve local storage size estimation for quota limitation
     4        https://bugs.webkit.org/show_bug.cgi?id=225123
     5
     6        Reviewed by Alex Christensen.
     7
     8        Improve local storage size estimation for quota limitation:
     9        - Rely on String::sizeInBytes() to compute the String size, instead of using
     10          String::length() * sizeof(UChar)
     11        - Make estimation consistent between StorageMap & LocalStorageDatabase
     12
     13        * NetworkProcess/WebStorage/LocalStorageDatabase.cpp:
     14        (WebKit::LocalStorageDatabase::removeItem):
     15        (WebKit::LocalStorageDatabase::setItem):
     16        (WebKit::estimateEntrySize): Deleted.
     17        * NetworkProcess/WebStorage/LocalStorageDatabase.h:
     18
    1192021-04-27  Wenson Hsieh  <wenson_hsieh@apple.com>
    220
  • trunk/Source/WebKit/NetworkProcess/WebStorage/LocalStorageDatabase.cpp

    r276653 r276689  
    4848static const char getItemsQueryString[] = "SELECT key, value FROM ItemTable";
    4949
    50 static CheckedUint64 estimateEntrySize(const String& key, const String& value)
    51 {
    52     CheckedUint64 entrySize;
    53     entrySize += key.length() * sizeof(UChar);
    54     entrySize += value.length() * sizeof(UChar);
    55     return entrySize;
    56 }
    57 
    5850Ref<LocalStorageDatabase> LocalStorageDatabase::create(Ref<WorkQueue>&& queue, Ref<LocalStorageDatabaseTracker>&& tracker, const SecurityOriginData& securityOrigin, unsigned quotaInBytes)
    5951{
     
    218210
    219211    if (m_databaseSize) {
    220         CheckedUint64 entrySize = estimateEntrySize(key, oldValue);
    221         if (entrySize.hasOverflowed() || entrySize.unsafeGet() >= *m_databaseSize)
     212        auto sizeDecrease = key.sizeInBytes() + oldValue.sizeInBytes();
     213        if (sizeDecrease >= *m_databaseSize)
    222214            *m_databaseSize = 0;
    223215        else
    224             *m_databaseSize -= entrySize.unsafeGet();
     216            *m_databaseSize -= sizeDecrease;
    225217    }
    226218}
     
    255247        return;
    256248
     249    oldValue = item(key);
     250
    257251    if (m_quotaInBytes != WebCore::StorageMap::noQuota) {
    258252        if (!m_databaseSize)
    259253            m_databaseSize = SQLiteFileSystem::getDatabaseFileSize(m_databasePath);
    260         if (*m_databaseSize >= m_quotaInBytes) {
    261             quotaException = true;
    262             return;
    263         }
    264         CheckedUint64 newDatabaseSize = *m_databaseSize;
    265         newDatabaseSize += estimateEntrySize(key, value);
     254        CheckedUint32 newDatabaseSize = *m_databaseSize;
     255        newDatabaseSize -= oldValue.sizeInBytes();
     256        newDatabaseSize += value.sizeInBytes();
     257        if (oldValue.isNull())
     258            newDatabaseSize += key.sizeInBytes();
    266259        if (newDatabaseSize.hasOverflowed() || newDatabaseSize.unsafeGet() > m_quotaInBytes) {
    267260            quotaException = true;
     
    271264    }
    272265
    273     oldValue = item(key);
    274 
    275266    auto insertStatement = scopedStatement(m_insertStatement, "INSERT INTO ItemTable VALUES (?, ?)"_s);
    276267    if (!insertStatement) {
  • trunk/Source/WebKit/NetworkProcess/WebStorage/LocalStorageDatabase.h

    r276653 r276689  
    7676    String m_databasePath;
    7777    mutable WebCore::SQLiteDatabase m_database;
    78     unsigned m_quotaInBytes { 0 };
     78    const unsigned m_quotaInBytes { 0 };
    7979    bool m_failedToOpenDatabase { false };
    8080    bool m_isClosed { false };
    81     Optional<uint64_t> m_databaseSize;
     81    Optional<unsigned> m_databaseSize;
    8282
    8383    std::unique_ptr<WebCore::SuddenTerminationDisabler> m_disableSuddenTerminationWhileWritingToLocalStorage;
Note: See TracChangeset for help on using the changeset viewer.