Changeset 64398 in webkit
- Timestamp:
- Jul 30, 2010 7:22:16 PM (14 years ago)
- Location:
- trunk/WebCore
- Files:
-
- 5 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/WebCore/ChangeLog
r64397 r64398 1 2010-07-30 Joseph Pecoraro <joepeck@webkit.org> 2 3 Reviewed by David Kilzer. 4 5 Limit ApplicationCache Total and Per-Origin Storage Capacity (Quotas) 6 https://bugs.webkit.org/show_bug.cgi?id=40627 7 8 Part 2 - Update Schema and enforce Per-Origin Quotas 9 10 Added an "Origins" table to the application cache databases. 11 This, like the Database's Origins table, is a list of origin 12 and quota pairs. Origins records are added as soon as they are 13 needed, and deleted only when the ApplicationCacheStorage is 14 emptied. This means Origins records persist even after all 15 caches for that origin may be deleted. The "CacheGroups" table 16 now has a foreign key column "origin" which relates to the 17 "Origins" table. 18 19 To enforce the quotas, remaining quota space is checked at 20 the start of update as an estimate and at the end before 21 inserting. Currently, reaching the quota limit will simply 22 cause an update error. A later part will provide a 23 notification to the client to allow an action, and refactor 24 the final quota limit check into a transaction. 25 26 Respect the quota during the update process. And cause 27 the update process to fail when the quota is reached. 28 29 * loader/appcache/ApplicationCacheGroup.cpp: added loading counter, counts bytes as they load 30 (WebCore::ApplicationCacheGroup::ApplicationCacheGroup): 31 (WebCore::ApplicationCacheGroup::didReceiveData): 32 (WebCore::ApplicationCacheGroup::didFinishLoading): 33 (WebCore::ApplicationCacheGroup::checkIfLoadIsComplete): 34 * loader/appcache/ApplicationCacheGroup.h: added security origin, based on the manifest URL 35 (WebCore::ApplicationCacheGroup::origin): accessor 36 37 Updates the schema of the database tables as described 38 above. Handle other SQL operations such as checking the 39 remaining space and inserting and deleting Origins records. 40 41 * loader/appcache/ApplicationCacheStorage.cpp: 42 (WebCore::ApplicationCacheStorage::quotaForOrigin): query for the quota of an origin, may return the default origin quota if it didn't exist. 43 (WebCore::ApplicationCacheStorage::remainingSizeForOriginExcludingCache): calculate the remaining size in a quota for an origin, possibly excluding a cache. 44 (WebCore::ApplicationCacheStorage::storeUpdatedQuotaForOrigin): persistent update. 45 (WebCore::ApplicationCacheStorage::openDatabase): updated schema for CachesGroups, added new table Origins. 46 (WebCore::ApplicationCacheStorage::empty): wipe Origins table as well. 47 (WebCore::ApplicationCacheStorage::unknownQuota): constant to mean unknown quota 48 1 49 2010-07-30 Joseph Pecoraro <joepeck@webkit.org> 2 50 -
trunk/WebCore/loader/appcache/ApplicationCacheGroup.cpp
r63753 r64398 43 43 #include "ManifestParser.h" 44 44 #include "Page.h" 45 #include "SecurityOrigin.h" 45 46 #include "Settings.h" 46 47 #include <wtf/HashMap.h> … … 58 59 ApplicationCacheGroup::ApplicationCacheGroup(const KURL& manifestURL, bool isCopy) 59 60 : m_manifestURL(manifestURL) 61 , m_origin(SecurityOrigin::create(manifestURL)) 60 62 , m_updateStatus(Idle) 61 63 , m_downloadingPendingMasterResourceLoadersCount(0) … … 68 70 , m_isCopy(isCopy) 69 71 , m_calledReachedMaxAppCacheSize(false) 72 , m_loadedSize(0) 73 , m_availableSpaceInQuota(ApplicationCacheStorage::unknownQuota()) 70 74 { 71 75 } … … 593 597 ASSERT(m_currentResource); 594 598 m_currentResource->data()->append(data, length); 599 600 m_loadedSize += length; 595 601 } 596 602 … … 606 612 return; 607 613 } 608 614 615 // After finishing the loading of any resource, we check if it will 616 // fit in our last known quota limit. 617 if (m_availableSpaceInQuota == ApplicationCacheStorage::unknownQuota()) { 618 // Failed to determine what is left in the quota. Fallback to allowing anything. 619 if (!cacheStorage().remainingSizeForOriginExcludingCache(m_origin.get(), m_newestCache.get(), m_availableSpaceInQuota)) 620 m_availableSpaceInQuota = ApplicationCacheStorage::noQuota(); 621 } 622 623 // Check each resource, as it loads, to see if it would fit in our 624 // idea of the available quota space. 625 if (m_availableSpaceInQuota < m_loadedSize) { 626 m_currentResource = 0; 627 cacheUpdateFailed(); 628 return; 629 } 630 609 631 ASSERT(m_currentHandle == handle); 610 632 ASSERT(m_pendingEntries.contains(handle->firstRequest().url())); … … 860 882 861 883 RefPtr<ApplicationCache> oldNewestCache = (m_newestCache == m_cacheBeingUpdated) ? 0 : m_newestCache; 884 885 // Check one more time, before committing to the new cache, if the cache will fit in the quota. 886 int64_t remainingSpaceInOrigin; 887 if (cacheStorage().remainingSizeForOriginExcludingCache(m_origin.get(), oldNewestCache.get(), remainingSpaceInOrigin)) { 888 if (m_cacheBeingUpdated->estimatedSizeInStorage() > remainingSpaceInOrigin) { 889 cacheUpdateFailed(); 890 break; 891 } 892 } 862 893 863 894 setNewestCache(m_cacheBeingUpdated.release()); … … 923 954 setUpdateStatus(Idle); 924 955 m_frame = 0; 956 m_loadedSize = 0; 957 m_availableSpaceInQuota = ApplicationCacheStorage::unknownQuota(); 925 958 m_calledReachedMaxAppCacheSize = false; 926 959 } -
trunk/WebCore/loader/appcache/ApplicationCacheGroup.h
r62957 r64398 1 1 /* 2 * Copyright (C) 2008, 2009 Apple Inc. All Rights Reserved.2 * Copyright (C) 2008, 2009, 2010 Apple Inc. All Rights Reserved. 3 3 * 4 4 * Redistribution and use in source and binary forms, with or without … … 29 29 #if ENABLE(OFFLINE_WEB_APPLICATIONS) 30 30 31 #include <wtf/Noncopyable.h>32 #include <wtf/HashMap.h>33 #include <wtf/HashSet.h>34 35 31 #include "DOMApplicationCache.h" 36 32 #include "KURL.h" … … 40 36 #include "SharedBuffer.h" 41 37 38 #include <wtf/Noncopyable.h> 39 #include <wtf/HashMap.h> 40 #include <wtf/HashSet.h> 41 42 42 namespace WebCore { 43 43 … … 47 47 class DocumentLoader; 48 48 class Frame; 49 class SecurityOrigin; 49 50 50 51 enum ApplicationCacheUpdateOption { … … 67 68 68 69 const KURL& manifestURL() const { return m_manifestURL; } 70 const SecurityOrigin* origin() const { return m_origin.get(); } 69 71 UpdateStatus updateStatus() const { return m_updateStatus; } 70 72 void setUpdateStatus(UpdateStatus status); … … 132 134 133 135 KURL m_manifestURL; 136 RefPtr<SecurityOrigin> m_origin; 134 137 UpdateStatus m_updateStatus; 135 138 … … 195 198 RefPtr<ResourceHandle> m_manifestHandle; 196 199 200 int64_t m_loadedSize; 201 int64_t m_availableSpaceInQuota; 202 197 203 friend class ChromeClientCallbackTimer; 198 204 }; -
trunk/WebCore/loader/appcache/ApplicationCacheStorage.cpp
r64397 r64398 1 1 /* 2 * Copyright (C) 2008, 2009 Apple Inc. All Rights Reserved.2 * Copyright (C) 2008, 2009, 2010 Apple Inc. All Rights Reserved. 3 3 * 4 4 * Redistribution and use in source and binary forms, with or without … … 30 30 31 31 #include "ApplicationCache.h" 32 #include "ApplicationCacheGroup.h" 32 33 #include "ApplicationCacheHost.h" 33 #include "ApplicationCacheGroup.h"34 34 #include "ApplicationCacheResource.h" 35 35 #include "FileSystem.h" … … 37 37 #include "SQLiteStatement.h" 38 38 #include "SQLiteTransaction.h" 39 #include "SecurityOrigin.h" 39 40 #include <wtf/text/CString.h> 40 41 #include <wtf/StdLibExtras.h> … … 420 421 } 421 422 423 bool ApplicationCacheStorage::quotaForOrigin(const SecurityOrigin* origin, int64_t& quota) 424 { 425 // If an Origin record doesn't exist, then the COUNT will be 0 and quota will be 0. 426 // Using the count to determine if a record existed or not is a safe way to determine 427 // if a quota of 0 is real, from the record, or from null. 428 SQLiteStatement statement(m_database, "SELECT COUNT(quota), quota FROM Origins WHERE origin=?"); 429 if (statement.prepare() != SQLResultOk) 430 return false; 431 432 statement.bindText(1, origin->databaseIdentifier()); 433 int result = statement.step(); 434 435 // Return the quota, or if it was null the default. 436 if (result == SQLResultRow) { 437 bool wasNoRecord = statement.getColumnInt64(0) == 0; 438 quota = wasNoRecord ? m_defaultOriginQuota : statement.getColumnInt64(1); 439 return true; 440 } 441 442 LOG_ERROR("Could not get the quota of an origin, error \"%s\"", m_database.lastErrorMsg()); 443 return false; 444 } 445 446 bool ApplicationCacheStorage::remainingSizeForOriginExcludingCache(const SecurityOrigin* origin, ApplicationCache* cache, int64_t& remainingSize) 447 { 448 openDatabase(false); 449 if (!m_database.isOpen()) 450 return false; 451 452 // Remaining size = total origin quota - size of all caches with origin excluding the provided cache. 453 // Keep track of the number of caches so we can tell if the result was a calculation or not. 454 const char* query; 455 int64_t excludingCacheIdentifier = cache ? cache->storageID() : 0; 456 if (excludingCacheIdentifier != 0) { 457 query = "SELECT COUNT(Caches.size), Origins.quota - SUM(Caches.size)" 458 " FROM CacheGroups" 459 " INNER JOIN Origins ON CacheGroups.origin = Origins.origin" 460 " INNER JOIN Caches ON CacheGroups.id = Caches.cacheGroup" 461 " WHERE Origins.origin=?" 462 " AND Caches.id!=?"; 463 } else { 464 query = "SELECT COUNT(Caches.size), Origins.quota - SUM(Caches.size)" 465 " FROM CacheGroups" 466 " INNER JOIN Origins ON CacheGroups.origin = Origins.origin" 467 " INNER JOIN Caches ON CacheGroups.id = Caches.cacheGroup" 468 " WHERE Origins.origin=?"; 469 } 470 471 SQLiteStatement statement(m_database, query); 472 if (statement.prepare() != SQLResultOk) 473 return false; 474 475 statement.bindText(1, origin->databaseIdentifier()); 476 if (excludingCacheIdentifier != 0) 477 statement.bindInt64(2, excludingCacheIdentifier); 478 int result = statement.step(); 479 480 // If the count was 0 that then we have to query the origin table directly 481 // for its quota. Otherwise we can use the calculated value. 482 if (result == SQLResultRow) { 483 int64_t numberOfCaches = statement.getColumnInt64(0); 484 if (numberOfCaches == 0) 485 quotaForOrigin(origin, remainingSize); 486 else 487 remainingSize = statement.getColumnInt64(1); 488 return true; 489 } 490 491 LOG_ERROR("Could not get the remaining size of an origin's quota, error \"%s\"", m_database.lastErrorMsg()); 492 return false; 493 } 494 495 bool ApplicationCacheStorage::storeUpdatedQuotaForOrigin(const SecurityOrigin* origin, int64_t quota) 496 { 497 openDatabase(false); 498 if (!m_database.isOpen()) 499 return false; 500 501 SQLiteStatement updateStatement(m_database, "UPDATE Origins SET quota=? WHERE origin=?"); 502 if (updateStatement.prepare() != SQLResultOk) 503 return false; 504 505 updateStatement.bindInt64(1, quota); 506 updateStatement.bindText(2, origin->databaseIdentifier()); 507 508 return executeStatement(updateStatement); 509 } 510 422 511 bool ApplicationCacheStorage::executeSQLCommand(const String& sql) 423 512 { … … 432 521 } 433 522 434 static const int schemaVersion = 5; 523 // Update the schemaVersion when the schema of any the Application Cache 524 // SQLite tables changes. This allows the database to be rebuilt when 525 // a new, incompatible change has been introduced to the database schema. 526 static const int schemaVersion = 6; 435 527 436 528 void ApplicationCacheStorage::verifySchemaVersion() … … 481 573 // Create tables 482 574 executeSQLCommand("CREATE TABLE IF NOT EXISTS CacheGroups (id INTEGER PRIMARY KEY AUTOINCREMENT, " 483 "manifestHostHash INTEGER NOT NULL ON CONFLICT FAIL, manifestURL TEXT UNIQUE ON CONFLICT FAIL, newestCache INTEGER )");575 "manifestHostHash INTEGER NOT NULL ON CONFLICT FAIL, manifestURL TEXT UNIQUE ON CONFLICT FAIL, newestCache INTEGER, origin TEXT)"); 484 576 executeSQLCommand("CREATE TABLE IF NOT EXISTS Caches (id INTEGER PRIMARY KEY AUTOINCREMENT, cacheGroup INTEGER, size INTEGER)"); 485 577 executeSQLCommand("CREATE TABLE IF NOT EXISTS CacheWhitelistURLs (url TEXT NOT NULL ON CONFLICT FAIL, cache INTEGER NOT NULL ON CONFLICT FAIL)"); … … 491 583 "statusCode INTEGER NOT NULL, responseURL TEXT NOT NULL, mimeType TEXT, textEncodingName TEXT, headers TEXT, data INTEGER NOT NULL ON CONFLICT FAIL)"); 492 584 executeSQLCommand("CREATE TABLE IF NOT EXISTS CacheResourceData (id INTEGER PRIMARY KEY AUTOINCREMENT, data BLOB)"); 585 executeSQLCommand("CREATE TABLE IF NOT EXISTS Origins (origin TEXT UNIQUE ON CONFLICT IGNORE, quota INTEGER NOT NULL ON CONFLICT FAIL)"); 493 586 494 587 // When a cache is deleted, all its entries and its whitelist should be deleted. … … 529 622 ASSERT(journal); 530 623 531 SQLiteStatement statement(m_database, "INSERT INTO CacheGroups (manifestHostHash, manifestURL ) VALUES (?, ?)");624 SQLiteStatement statement(m_database, "INSERT INTO CacheGroups (manifestHostHash, manifestURL, origin) VALUES (?, ?, ?)"); 532 625 if (statement.prepare() != SQLResultOk) 533 626 return false; 627 628 String originIdentifier = group->origin()->databaseIdentifier(); 534 629 535 630 statement.bindInt64(1, urlHostHash(group->manifestURL())); 536 631 statement.bindText(2, group->manifestURL()); 632 statement.bindText(3, originIdentifier); 537 633 538 634 if (!executeStatement(statement)) 539 635 return false; 636 637 // Create Origin if needed. 638 { 639 SQLiteStatement insertOriginStatement(m_database, "INSERT INTO Origins (origin, quota) VALUES (?, ?)"); 640 if (insertOriginStatement.prepare() != SQLResultOk) 641 return false; 642 643 insertOriginStatement.bindText(1, originIdentifier); 644 insertOriginStatement.bindInt64(2, m_defaultOriginQuota); 645 if (!executeStatement(insertOriginStatement)) 646 return false; 647 } 540 648 541 649 group->setStorageID(static_cast<unsigned>(m_database.lastInsertRowID())); … … 970 1078 return; 971 1079 972 // Clear cache groups, caches and cache resources.1080 // Clear cache groups, caches, cache resources, and origins. 973 1081 executeSQLCommand("DELETE FROM CacheGroups"); 974 1082 executeSQLCommand("DELETE FROM Caches"); 1083 executeSQLCommand("DELETE FROM Origins"); 975 1084 976 1085 // Clear the storage IDs for the caches in memory. -
trunk/WebCore/loader/appcache/ApplicationCacheStorage.h
r64397 r64398 1 1 /* 2 * Copyright (C) 2008 Apple Inc. All Rights Reserved.2 * Copyright (C) 2008, 2010 Apple Inc. All Rights Reserved. 3 3 * 4 4 * Redistribution and use in source and binary forms, with or without … … 38 38 39 39 class ApplicationCache; 40 class ApplicationCacheGroup; 40 41 class ApplicationCacheHost; 41 class ApplicationCacheGroup;42 42 class ApplicationCacheResource; 43 43 class KURL; 44 44 template <class T> 45 45 class StorageIDJournal; 46 class SecurityOrigin; 46 47 47 48 class ApplicationCacheStorage : public Noncopyable { … … 57 58 int64_t defaultOriginQuota() const { return m_defaultOriginQuota; } 58 59 void setDefaultOriginQuota(int64_t quota); 60 bool quotaForOrigin(const SecurityOrigin*, int64_t& quota); 61 bool remainingSizeForOriginExcludingCache(const SecurityOrigin*, ApplicationCache*, int64_t& remainingSize); 62 bool storeUpdatedQuotaForOrigin(const SecurityOrigin*, int64_t quota); 59 63 60 64 ApplicationCacheGroup* cacheGroupForURL(const KURL&); // Cache to load a main resource from. … … 81 85 void vacuumDatabaseFile(); 82 86 87 static int64_t unknownQuota() { return -1; } 83 88 static int64_t noQuota() { return std::numeric_limits<int64_t>::max(); } 84 89 private:
Note: See TracChangeset
for help on using the changeset viewer.