WebKit Bugzilla
Attachment 369902 Details for
Bug 197463
: Reuse existing WebPageProxy quota handler for NetworkProcessProxy quota requests
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
[patch]
Patch
bug-197463-20190514154827.patch (text/plain), 36.26 KB, created by
youenn fablet
on 2019-05-14 15:48:27 PDT
(
hide
)
Description:
Patch
Filename:
MIME Type:
Creator:
youenn fablet
Created:
2019-05-14 15:48:27 PDT
Size:
36.26 KB
patch
obsolete
>Subversion Revision: 245233 >diff --git a/Source/WebKit/ChangeLog b/Source/WebKit/ChangeLog >index 6ff2f0ee2776e35b45fb586e1c2719c2c31c674e..b34db57a58c1194b5ea1494ab9f0503677ba8309 100644 >--- a/Source/WebKit/ChangeLog >+++ b/Source/WebKit/ChangeLog >@@ -1,3 +1,43 @@ >+2019-05-14 Youenn Fablet <youenn@apple.com> >+ >+ Reuse existing WebPageProxy quota handler for NetworkProcessProxy quota requests >+ https://bugs.webkit.org/show_bug.cgi?id=197463 >+ <rdar://problem/47403621> >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ Add a getter to know whether websitedatastore client implements the quota delegate. >+ If not, find the most visible page that is the same origin as the quota request >+ and reuse the existing exceededDatabasQuota delegate. >+ This approach allows to call the delegate even if the quota request comes from a service worker. >+ If no such page is found, the quota will not be increased. >+ >+ Refactoring to make sure we are calling the delegate once a previous call to that delegate is completed. >+ Covered by API test. >+ >+ * UIProcess/API/Cocoa/WKWebsiteDataStore.mm: >+ * UIProcess/Network/NetworkProcessProxy.cpp: >+ (WebKit::NetworkProcessProxy::requestStorageSpace): >+ * UIProcess/WebPageProxy.cpp: >+ (WebKit::StorageRequests::add): >+ (WebKit::StorageRequests::processNext): >+ (WebKit::StorageRequests::areBeingProcessed const): >+ (WebKit::StorageRequests::setAreBeingProcessed): >+ (WebKit::StorageRequests::StorageRequests): >+ (WebKit::StorageRequests::~StorageRequests): >+ (WebKit::StorageRequests::singleton): >+ (WebKit::WebPageProxy::forMostVisibleWebPageIfAny): >+ (WebKit::WebPageProxy::didChangeMainDocument): >+ (WebKit::WebPageProxy::exceededDatabaseQuota): >+ (WebKit::WebPageProxy::requestStorageSpace): >+ (WebKit::WebPageProxy::makeStorageSpaceRequest): >+ * UIProcess/WebPageProxy.h: >+ * UIProcess/WebProcessProxy.cpp: >+ (WebKit::WebProcessProxy::forWebPages): >+ * UIProcess/WebProcessProxy.h: >+ * UIProcess/WebsiteData/WebsiteDataStoreClient.h: >+ (WebKit::WebsiteDataStoreClient::implementsRequestStorageSpaceHandler const): >+ > 2019-05-14 Youenn Fablet <youenn@apple.com> > > A service worker process should app nap when all its clients app nap >diff --git a/Source/WebKit/UIProcess/API/Cocoa/WKWebsiteDataStore.mm b/Source/WebKit/UIProcess/API/Cocoa/WKWebsiteDataStore.mm >index 7dd4119c97b193b2843655e523ed8b7dc7e592b3..26298ebcec5774e340661ea9fb1a4b28ea50a343 100644 >--- a/Source/WebKit/UIProcess/API/Cocoa/WKWebsiteDataStore.mm >+++ b/Source/WebKit/UIProcess/API/Cocoa/WKWebsiteDataStore.mm >@@ -43,7 +43,7 @@ > #import <wtf/URL.h> > #import <wtf/WeakObjCPtr.h> > >-class WebsiteDataStoreClient : public WebKit::WebsiteDataStoreClient { >+class WebsiteDataStoreClient final : public WebKit::WebsiteDataStoreClient { > public: > explicit WebsiteDataStoreClient(id <_WKWebsiteDataStoreDelegate> delegate) > : m_delegate(delegate) >diff --git a/Source/WebKit/UIProcess/Network/NetworkProcessProxy.cpp b/Source/WebKit/UIProcess/Network/NetworkProcessProxy.cpp >index af859aec0362e91f930b286b8f42fc20fa883043..0ee81be84c159e85a83e5f27a1181ae7da14fb5e 100644 >--- a/Source/WebKit/UIProcess/Network/NetworkProcessProxy.cpp >+++ b/Source/WebKit/UIProcess/Network/NetworkProcessProxy.cpp >@@ -1141,7 +1141,7 @@ void NetworkProcessProxy::establishWorkerContextConnectionToNetworkProcessForExp > } > #endif > >-void NetworkProcessProxy::requestStorageSpace(PAL::SessionID sessionID, const WebCore::ClientOrigin& origin, uint64_t quota, uint64_t currentSize, uint64_t spaceRequired, CompletionHandler<void(Optional<uint64_t> quota)>&& completionHandler) >+void NetworkProcessProxy::requestStorageSpace(PAL::SessionID sessionID, const WebCore::ClientOrigin& origin, uint64_t currentQuota, uint64_t currentSize, uint64_t spaceRequired, CompletionHandler<void(Optional<uint64_t> quota)>&& completionHandler) > { > auto* store = websiteDataStoreFromSessionID(sessionID); > >@@ -1150,7 +1150,28 @@ void NetworkProcessProxy::requestStorageSpace(PAL::SessionID sessionID, const We > return; > } > >- store->client().requestStorageSpace(origin.topOrigin, origin.clientOrigin, quota, currentSize, spaceRequired, WTFMove(completionHandler)); >+ store->client().requestStorageSpace(origin.topOrigin, origin.clientOrigin, currentQuota, currentSize, spaceRequired, [sessionID, origin, currentQuota, currentSize, spaceRequired, completionHandler = WTFMove(completionHandler)](auto quota) mutable { >+ if (quota) { >+ completionHandler(quota); >+ return; >+ } >+ >+ if (origin.topOrigin != origin.clientOrigin) { >+ completionHandler({ }); >+ return; >+ } >+ >+ WebPageProxy::forMostVisibleWebPageIfAny(sessionID, origin.topOrigin, [completionHandler = WTFMove(completionHandler), origin, currentQuota, currentSize, spaceRequired](auto* page) mutable { >+ if (!page) { >+ completionHandler({ }); >+ return; >+ } >+ String name = makeString(FileSystem::encodeForFileName(origin.topOrigin.host), " content"); >+ page->requestStorageSpace(page->mainFrame()->frameID(), origin.topOrigin.databaseIdentifier(), name, name, currentQuota, currentSize, currentSize, spaceRequired, [completionHandler = WTFMove(completionHandler)](auto quota) mutable { >+ completionHandler(quota); >+ }); >+ }); >+ }); > } > > void NetworkProcessProxy::takeUploadAssertion() >diff --git a/Source/WebKit/UIProcess/WebPageProxy.cpp b/Source/WebKit/UIProcess/WebPageProxy.cpp >index 1b6b5168609d675d74a8ee1ae991ba5428fa24d1..29e01070a939972e3f1ea951ceb31a45b0b309cd 100644 >--- a/Source/WebKit/UIProcess/WebPageProxy.cpp >+++ b/Source/WebKit/UIProcess/WebPageProxy.cpp >@@ -264,76 +264,43 @@ using namespace WebCore; > > DEFINE_DEBUG_ONLY_GLOBAL(WTF::RefCountedLeakCounter, webPageProxyCounter, ("WebPageProxy")); > >-class ExceededDatabaseQuotaRecords { >- WTF_MAKE_NONCOPYABLE(ExceededDatabaseQuotaRecords); WTF_MAKE_FAST_ALLOCATED; >- friend NeverDestroyed<ExceededDatabaseQuotaRecords>; >+class StorageRequests { >+ WTF_MAKE_NONCOPYABLE(StorageRequests); WTF_MAKE_FAST_ALLOCATED; >+ friend NeverDestroyed<StorageRequests>; > public: >- struct Record { >- uint64_t frameID; >- String originIdentifier; >- String databaseName; >- String displayName; >- uint64_t currentQuota; >- uint64_t currentOriginUsage; >- uint64_t currentDatabaseUsage; >- uint64_t expectedUsage; >- Messages::WebPageProxy::ExceededDatabaseQuota::DelayedReply reply; >- }; >- >- static ExceededDatabaseQuotaRecords& singleton(); >+ static StorageRequests& singleton(); > >- std::unique_ptr<Record> createRecord(uint64_t frameID, String originIdentifier, >- String databaseName, String displayName, uint64_t currentQuota, >- uint64_t currentOriginUsage, uint64_t currentDatabaseUsage, uint64_t expectedUsage, >- Messages::WebPageProxy::ExceededDatabaseQuota::DelayedReply&&); >+ void processOrAppend(CompletionHandler<void()>&& completionHandler) >+ { >+ if (m_areBeingProcessing) { >+ m_requests.append(WTFMove(completionHandler)); >+ return; >+ } >+ m_areBeingProcessing = true; >+ completionHandler(); >+ } > >- void add(std::unique_ptr<Record>); >- bool areBeingProcessed() const { return !!m_currentRecord; } >- Record* next(); >+ void processNextIfAny() >+ { >+ if (m_requests.isEmpty()) { >+ m_areBeingProcessing = false; >+ return; >+ } >+ m_requests.takeFirst()(); >+ } > > private: >- ExceededDatabaseQuotaRecords() { } >- ~ExceededDatabaseQuotaRecords() { } >+ StorageRequests() { } >+ ~StorageRequests() { } > >- Deque<std::unique_ptr<Record>> m_records; >- std::unique_ptr<Record> m_currentRecord; >+ Deque<CompletionHandler<void()>> m_requests; >+ bool m_areBeingProcessing { false }; > }; > >-ExceededDatabaseQuotaRecords& ExceededDatabaseQuotaRecords::singleton() >+StorageRequests& StorageRequests::singleton() > { >- static NeverDestroyed<ExceededDatabaseQuotaRecords> records; >- return records; >-} >- >-std::unique_ptr<ExceededDatabaseQuotaRecords::Record> ExceededDatabaseQuotaRecords::createRecord( >- uint64_t frameID, String originIdentifier, String databaseName, String displayName, >- uint64_t currentQuota, uint64_t currentOriginUsage, uint64_t currentDatabaseUsage, >- uint64_t expectedUsage, Messages::WebPageProxy::ExceededDatabaseQuota::DelayedReply&& reply) >-{ >- auto record = std::make_unique<Record>(); >- record->frameID = frameID; >- record->originIdentifier = originIdentifier; >- record->databaseName = databaseName; >- record->displayName = displayName; >- record->currentQuota = currentQuota; >- record->currentOriginUsage = currentOriginUsage; >- record->currentDatabaseUsage = currentDatabaseUsage; >- record->expectedUsage = expectedUsage; >- record->reply = WTFMove(reply); >- return record; >-} >- >-void ExceededDatabaseQuotaRecords::add(std::unique_ptr<ExceededDatabaseQuotaRecords::Record> record) >-{ >- m_records.append(WTFMove(record)); >-} >- >-ExceededDatabaseQuotaRecords::Record* ExceededDatabaseQuotaRecords::next() >-{ >- m_currentRecord = nullptr; >- if (!m_records.isEmpty()) >- m_currentRecord = m_records.takeFirst(); >- return m_currentRecord.get(); >+ static NeverDestroyed<StorageRequests> requests; >+ return requests; > } > > #if !LOG_DISABLED >@@ -395,6 +362,25 @@ private: > WeakPtr<PageClient> m_pageClient; > }; > >+void WebPageProxy::forMostVisibleWebPageIfAny(PAL::SessionID sessionID, const SecurityOriginData& origin, CompletionHandler<void(WebPageProxy*)>&& completionHandler) >+{ >+ // FIXME: If not finding right away a visible page, we might want to try again for a given period of time when there is a change of visibility. >+ WebPageProxy* selectedPage = nullptr; >+ WebProcessProxy::forWebPages(sessionID, origin, [&](auto& page) { >+ if (!page.mainFrame()) >+ return; >+ if (page.isViewVisible() && (!selectedPage || !selectedPage->isViewVisible())) { >+ selectedPage = &page; >+ return; >+ } >+ if (page.isViewFocused() && (!selectedPage || !selectedPage->isViewFocused())) { >+ selectedPage = &page; >+ return; >+ } >+ }); >+ completionHandler(selectedPage); >+} >+ > Ref<WebPageProxy> WebPageProxy::create(PageClient& pageClient, WebProcessProxy& process, uint64_t pageID, Ref<API::PageConfiguration>&& configuration) > { > return adoptRef(*new WebPageProxy(pageClient, process, pageID, WTFMove(configuration))); >@@ -4414,6 +4400,7 @@ void WebPageProxy::didChangeMainDocument(uint64_t frameID) > #else > UNUSED_PARAM(frameID); > #endif >+ m_isQuotaIncreaseDenied = false; > } > > void WebPageProxy::viewIsBecomingVisible() >@@ -7260,24 +7247,41 @@ void WebPageProxy::didReceiveAuthenticationChallengeProxy(uint64_t, Ref<Authenti > > void WebPageProxy::exceededDatabaseQuota(uint64_t frameID, const String& originIdentifier, const String& databaseName, const String& displayName, uint64_t currentQuota, uint64_t currentOriginUsage, uint64_t currentDatabaseUsage, uint64_t expectedUsage, Messages::WebPageProxy::ExceededDatabaseQuota::DelayedReply&& reply) > { >- ExceededDatabaseQuotaRecords& records = ExceededDatabaseQuotaRecords::singleton(); >- std::unique_ptr<ExceededDatabaseQuotaRecords::Record> newRecord = records.createRecord(frameID, >- originIdentifier, databaseName, displayName, currentQuota, currentOriginUsage, >- currentDatabaseUsage, expectedUsage, WTFMove(reply)); >- records.add(WTFMove(newRecord)); >+ requestStorageSpace(frameID, originIdentifier, databaseName, displayName, currentQuota, currentOriginUsage, currentDatabaseUsage, expectedUsage, [reply = WTFMove(reply)](auto quota) mutable { >+ reply(quota); >+ }); >+} > >- if (records.areBeingProcessed()) >+void WebPageProxy::requestStorageSpace(uint64_t frameID, const String& originIdentifier, const String& databaseName, const String& displayName, uint64_t currentQuota, uint64_t currentOriginUsage, uint64_t currentDatabaseUsage, uint64_t expectedUsage, CompletionHandler<void(uint64_t)>&& completionHandler) >+{ >+ StorageRequests::singleton().processOrAppend([this, protectedThis = makeRef(*this), pageURL = currentURL(), frameID, originIdentifier, databaseName, displayName, currentQuota, currentOriginUsage, currentDatabaseUsage, expectedUsage, completionHandler = WTFMove(completionHandler)]() mutable { >+ this->makeStorageSpaceRequest(frameID, originIdentifier, databaseName, displayName, currentQuota, currentOriginUsage, currentDatabaseUsage, expectedUsage, [this, protectedThis = WTFMove(protectedThis), pageURL = WTFMove(pageURL), completionHandler = WTFMove(completionHandler), currentQuota](auto quota) mutable { >+ if (quota <= currentQuota && this->currentURL() == pageURL) >+ m_isQuotaIncreaseDenied = true; >+ completionHandler(quota); >+ StorageRequests::singleton().processNextIfAny(); >+ }); >+ }); >+} >+ >+void WebPageProxy::makeStorageSpaceRequest(uint64_t frameID, const String& originIdentifier, const String& databaseName, const String& displayName, uint64_t currentQuota, uint64_t currentOriginUsage, uint64_t currentDatabaseUsage, uint64_t expectedUsage, CompletionHandler<void(uint64_t)>&& completionHandler) >+{ >+ if (m_isQuotaIncreaseDenied) { >+ completionHandler(currentQuota); > return; >+ } > >- ExceededDatabaseQuotaRecords::Record* record = records.next(); >- while (record) { >- WebFrameProxy* frame = m_process->webFrame(record->frameID); >- MESSAGE_CHECK(m_process, frame); >+ WebFrameProxy* frame = m_process->webFrame(frameID); >+ MESSAGE_CHECK(m_process, frame); > >- auto origin = API::SecurityOrigin::create(SecurityOriginData::fromDatabaseIdentifier(record->originIdentifier)->securityOrigin()); >- m_uiClient->exceededDatabaseQuota(this, frame, origin.ptr(), record->databaseName, record->displayName, record->currentQuota, record->currentOriginUsage, record->currentDatabaseUsage, record->expectedUsage, WTFMove(record->reply)); >- record = records.next(); >+ auto originData = SecurityOriginData::fromDatabaseIdentifier(originIdentifier); >+ if (originData != SecurityOriginData::fromURL(URL { { }, currentURL() })) { >+ completionHandler(currentQuota); >+ return; > } >+ >+ auto origin = API::SecurityOrigin::create(originData->securityOrigin()); >+ m_uiClient->exceededDatabaseQuota(this, frame, origin.ptr(), databaseName, displayName, currentQuota, currentOriginUsage, currentDatabaseUsage, expectedUsage, WTFMove(completionHandler)); > } > > void WebPageProxy::reachedApplicationCacheOriginQuota(const String& originIdentifier, uint64_t currentQuota, uint64_t totalBytesNeeded, Messages::WebPageProxy::ReachedApplicationCacheOriginQuota::DelayedReply&& reply) >diff --git a/Source/WebKit/UIProcess/WebPageProxy.h b/Source/WebKit/UIProcess/WebPageProxy.h >index 8d8fe812238d356497d48789eabf1e2f4735a48b..1cee8e2ba71381b7d297e0b32f0caf0dbf74493d 100644 >--- a/Source/WebKit/UIProcess/WebPageProxy.h >+++ b/Source/WebKit/UIProcess/WebPageProxy.h >@@ -385,6 +385,8 @@ public: > static Ref<WebPageProxy> create(PageClient&, WebProcessProxy&, uint64_t pageID, Ref<API::PageConfiguration>&&); > virtual ~WebPageProxy(); > >+ static void forMostVisibleWebPageIfAny(PAL::SessionID, const WebCore::SecurityOriginData&, CompletionHandler<void(WebPageProxy*)>&&); >+ > const API::PageConfiguration& configuration() const; > > uint64_t pageID() const { return m_pageID; } >@@ -1542,6 +1544,8 @@ WEBPAGEPROXY_LOADOPTIMIZER_ADDITIONS_1 > void didReceiveMessage(IPC::Connection&, IPC::Decoder&) override; > void didReceiveSyncMessage(IPC::Connection&, IPC::Decoder&, std::unique_ptr<IPC::Encoder>&) override; > >+ void requestStorageSpace(uint64_t frameID, const String& originIdentifier, const String& databaseName, const String& displayName, uint64_t currentQuota, uint64_t currentOriginUsage, uint64_t currentDatabaseUsage, uint64_t expectedUsage, WTF::CompletionHandler<void(uint64_t)>&&); >+ > private: > WebPageProxy(PageClient&, WebProcessProxy&, uint64_t pageID, Ref<API::PageConfiguration>&&); > void platformInitialize(); >@@ -2078,6 +2082,8 @@ private: > static bool isInHardwareKeyboardMode(); > #endif > >+ void makeStorageSpaceRequest(uint64_t frameID, const String& originIdentifier, const String& databaseName, const String& displayName, uint64_t currentQuota, uint64_t currentOriginUsage, uint64_t currentDatabaseUsage, uint64_t expectedUsage, CompletionHandler<void(uint64_t)>&&); >+ > WeakPtr<PageClient> m_pageClient; > Ref<API::PageConfiguration> m_configuration; > >@@ -2507,6 +2513,7 @@ WEBPAGEPROXY_LOADOPTIMIZER_ADDITIONS_2 > }; > Optional<SpeechSynthesisData> m_speechSynthesisData; > #endif >+ bool m_isQuotaIncreaseDenied { false }; > }; > > } // namespace WebKit >diff --git a/Source/WebKit/UIProcess/WebProcessProxy.cpp b/Source/WebKit/UIProcess/WebProcessProxy.cpp >index bfedee5479e07ec1d2d0560c2627e11be7918e69..2458750e9112af92adfb5428c0220a5f60372b98 100644 >--- a/Source/WebKit/UIProcess/WebProcessProxy.cpp >+++ b/Source/WebKit/UIProcess/WebProcessProxy.cpp >@@ -122,6 +122,15 @@ static WebProcessProxy::WebPageProxyMap& globalPageMap() > return pageMap; > } > >+void WebProcessProxy::forWebPages(PAL::SessionID sessionID, const SecurityOriginData& origin, const Function<void(WebPageProxy&)>& callback) >+{ >+ for (auto* page : globalPageMap().values()) { >+ if (page->sessionID() != sessionID || SecurityOriginData::fromURL(URL { { }, page->currentURL() }) != origin) >+ continue; >+ callback(*page); >+ } >+} >+ > Ref<WebProcessProxy> WebProcessProxy::create(WebProcessPool& processPool, WebsiteDataStore* websiteDataStore, IsPrewarmed isPrewarmed, ShouldLaunchProcess shouldLaunchProcess) > { > auto proxy = adoptRef(*new WebProcessProxy(processPool, websiteDataStore, isPrewarmed)); >diff --git a/Source/WebKit/UIProcess/WebProcessProxy.h b/Source/WebKit/UIProcess/WebProcessProxy.h >index 1284aea5187c1f884acf255ca030ed93cadeff85..dc7223b1eb7f86f16779721f291e7dcc36c7d742 100644 >--- a/Source/WebKit/UIProcess/WebProcessProxy.h >+++ b/Source/WebKit/UIProcess/WebProcessProxy.h >@@ -110,6 +110,8 @@ public: > static Ref<WebProcessProxy> create(WebProcessPool&, WebsiteDataStore*, IsPrewarmed, ShouldLaunchProcess = ShouldLaunchProcess::Yes); > ~WebProcessProxy(); > >+ static void forWebPages(PAL::SessionID, const WebCore::SecurityOriginData&, const Function<void(WebPageProxy&)>&); >+ > WebConnection* webConnection() const { return m_webConnection.get(); } > > unsigned suspendedPageCount() const { return m_suspendedPageCount; } >diff --git a/Tools/ChangeLog b/Tools/ChangeLog >index 410fb6518e81635d06fc9196e2d6fd9b9d740bd1..aba3a24f8cea80f6813896c50231316c57e881c9 100644 >--- a/Tools/ChangeLog >+++ b/Tools/ChangeLog >@@ -1,3 +1,21 @@ >+2019-05-14 Youenn Fablet <youenn@apple.com> >+ >+ Reuse existing WebPageProxy quota handler for NetworkProcessProxy quota requests >+ https://bugs.webkit.org/show_bug.cgi?id=197463 >+ <rdar://problem/47403621> >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ * TestWebKitAPI/Tests/WebKitCocoa/StorageQuota.mm: Added. >+ (-[QuotaDelegate init]): >+ (-[QuotaDelegate _webView:decideDatabaseQuotaForSecurityOrigin:currentQuota:currentOriginUsage:currentDatabaseUsage:expectedUsage:decisionHandler:]): >+ (-[QuotaDelegate quotaDelegateCalled]): >+ (-[QuotaDelegate grantQuota]): >+ (-[StorageSchemes webView:startURLSchemeTask:]): >+ (-[StorageSchemes webView:stopURLSchemeTask:]): >+ (-[QuotaMessageHandler userContentController:didReceiveScriptMessage:]): >+ (doTest): >+ > 2019-05-14 Youenn Fablet <youenn@apple.com> > > A service worker process should app nap when all its clients app nap >diff --git a/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj b/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj >index 1e8eb8cb7de5c19e28322486974390cb3d8ad725..df27f125c293d390c47703960b243293f7f6f055 100644 >--- a/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj >+++ b/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj >@@ -180,6 +180,7 @@ > 3FCC4FE81EC4E8CA0076E37C /* PictureInPictureDelegate.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 3FCC4FE61EC4E87E0076E37C /* PictureInPictureDelegate.html */; }; > 4135FB842011FAA700332139 /* InjectInternals_Bundle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4135FB832011FAA300332139 /* InjectInternals_Bundle.cpp */; }; > 4135FB852011FABF00332139 /* libWebCoreTestSupport.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 4135FB862011FABF00332139 /* libWebCoreTestSupport.dylib */; }; >+ 414AD6862285D1C000777F2D /* StorageQuota.mm in Sources */ = {isa = PBXBuildFile; fileRef = 414AD6852285D1B000777F2D /* StorageQuota.mm */; }; > 41882F0321010C0D002FF288 /* ProcessPreWarming.mm in Sources */ = {isa = PBXBuildFile; fileRef = 41882F0221010A70002FF288 /* ProcessPreWarming.mm */; }; > 4433A396208044140091ED57 /* SynchronousTimeoutTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4433A395208044130091ED57 /* SynchronousTimeoutTests.mm */; }; > 44817A2F1F0486BF00003810 /* WKRequestActivatedElementInfo.mm in Sources */ = {isa = PBXBuildFile; fileRef = 44817A2E1F0486BF00003810 /* WKRequestActivatedElementInfo.mm */; }; >@@ -1579,6 +1580,7 @@ > 3FCC4FE61EC4E87E0076E37C /* PictureInPictureDelegate.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = PictureInPictureDelegate.html; sourceTree = "<group>"; }; > 4135FB832011FAA300332139 /* InjectInternals_Bundle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = InjectInternals_Bundle.cpp; path = Tests/InjectInternals_Bundle.cpp; sourceTree = SOURCE_ROOT; }; > 4135FB862011FABF00332139 /* libWebCoreTestSupport.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; path = libWebCoreTestSupport.dylib; sourceTree = BUILT_PRODUCTS_DIR; }; >+ 414AD6852285D1B000777F2D /* StorageQuota.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = StorageQuota.mm; sourceTree = "<group>"; }; > 41882F0221010A70002FF288 /* ProcessPreWarming.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ProcessPreWarming.mm; sourceTree = "<group>"; }; > 41973B5C1AF22875006C7B36 /* SharedBuffer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SharedBuffer.cpp; sourceTree = "<group>"; }; > 442BBF681C91CAD90017087F /* RefLogger.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RefLogger.cpp; sourceTree = "<group>"; }; >@@ -2688,6 +2690,7 @@ > 2D9A53AE1B31FA8D0074D5AA /* ShrinkToFit.mm */, > 2DFF7B6C1DA487AF00814614 /* SnapshotStore.mm */, > CDC0932D21C993440030C4B0 /* StopSuspendResumeAllMedia.mm */, >+ 414AD6852285D1B000777F2D /* StorageQuota.mm */, > 515BE1701D428BD100DD7C68 /* StoreBlobThenDelete.mm */, > 1C734B5220788C4800F430EA /* SystemColors.mm */, > 2D70059521EDA0C6003463CB /* TabOutOfWebView.mm */, >@@ -4369,6 +4372,7 @@ > 7CCE7ECE1A411A7E00447C4C /* StopLoadingFromDidFinishLoading.mm in Sources */, > 7CCE7ECF1A411A7E00447C4C /* StopLoadingFromDidReceiveResponse.mm in Sources */, > CDC0932E21C993440030C4B0 /* StopSuspendResumeAllMedia.mm in Sources */, >+ 414AD6862285D1C000777F2D /* StorageQuota.mm in Sources */, > 515BE1711D428E4B00DD7C68 /* StoreBlobThenDelete.mm in Sources */, > 7CCE7ED01A411A7E00447C4C /* StringByEvaluatingJavaScriptFromString.mm in Sources */, > 7CCE7ED11A411A7E00447C4C /* StringTruncator.mm in Sources */, >diff --git a/Tools/TestWebKitAPI/Tests/WebKitCocoa/StorageQuota.mm b/Tools/TestWebKitAPI/Tests/WebKitCocoa/StorageQuota.mm >new file mode 100644 >index 0000000000000000000000000000000000000000..ef3161b235b4179e777ad9ae702f895d2cc2637f >--- /dev/null >+++ b/Tools/TestWebKitAPI/Tests/WebKitCocoa/StorageQuota.mm >@@ -0,0 +1,351 @@ >+/* >+ * Copyright (C) 2019 Apple Inc. All rights reserved. >+ * >+ * Redistribution and use in source and binary forms, with or without >+ * modification, are permitted provided that the following conditions >+ * are met: >+ * 1. Redistributions of source code must retain the above copyright >+ * notice, this list of conditions and the following disclaimer. >+ * 2. Redistributions in binary form must reproduce the above copyright >+ * notice, this list of conditions and the following disclaimer in the >+ * documentation and/or other materials provided with the distribution. >+ * >+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' >+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, >+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR >+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS >+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR >+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF >+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS >+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN >+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) >+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF >+ * THE POSSIBILITY OF SUCH DAMAGE. >+ */ >+ >+#import "config.h" >+#import <WebKit/WebKit.h> >+ >+#import "PlatformUtilities.h" >+#import "Test.h" >+#import "TestWKWebView.h" >+#import <WebKit/WKPreferencesPrivate.h> >+#import <WebKit/WKProcessPoolPrivate.h> >+#import <WebKit/WKURLSchemeHandler.h> >+#import <WebKit/WKURLSchemeTaskPrivate.h> >+#import <WebKit/WKWebViewConfigurationPrivate.h> >+#import <WebKit/WKWebsiteDataStorePrivate.h> >+#import <WebKit/WKWebsiteDataStoreRef.h> >+#import <WebKit/_WKWebsiteDataStoreConfiguration.h> >+#import <wtf/BlockPtr.h> >+#import <wtf/HashMap.h> >+#import <wtf/RetainPtr.h> >+#import <wtf/Vector.h> >+#import <wtf/text/StringHash.h> >+#import <wtf/text/WTFString.h> >+ >+using namespace TestWebKitAPI; >+ >+@interface QuotaDelegate : NSObject <WKUIDelegate> >+-(bool)quotaDelegateCalled; >+-(void)grantQuota; >+-(void)denyQuota; >+@end >+ >+static bool receivedQuotaDelegateCalled; >+ >+@implementation QuotaDelegate { >+ bool _quotaDelegateCalled; >+ unsigned long long _currentQuota; >+ unsigned long long _expectedUsage; >+ BlockPtr<void(unsigned long long newQuota)> _decisionHandler; >+} >+ >+- (instancetype)init >+{ >+ if (!(self = [super init])) >+ return nil; >+ >+ _quotaDelegateCalled = false; >+ _expectedUsage = 0; >+ _currentQuota = 0; >+ >+ return self; >+} >+ >+- (void)_webView:(WKWebView *)webView decideDatabaseQuotaForSecurityOrigin:(WKSecurityOrigin *)securityOrigin currentQuota:(unsigned long long)currentQuota currentOriginUsage:(unsigned long long)currentOriginUsage currentDatabaseUsage:(unsigned long long)currentUsage expectedUsage:(unsigned long long)expectedUsage decisionHandler:(void (^)(unsigned long long newQuota))decisionHandler >+{ >+ receivedQuotaDelegateCalled = true; >+ _quotaDelegateCalled = true; >+ _currentQuota = currentQuota; >+ _expectedUsage = expectedUsage; >+ _decisionHandler = decisionHandler; >+} >+ >+-(bool)quotaDelegateCalled { >+ return _quotaDelegateCalled; >+} >+ >+-(void)grantQuota { >+ if (_quotaDelegateCalled) >+ _decisionHandler(_expectedUsage); >+ _quotaDelegateCalled = false; >+} >+ >+-(void)denyQuota { >+ if (_quotaDelegateCalled) >+ _decisionHandler(_currentQuota); >+ _quotaDelegateCalled = false; >+} >+ >+@end >+ >+struct ResourceInfo { >+ RetainPtr<NSString> mimeType; >+ const char* data; >+}; >+ >+@interface StorageSchemes : NSObject <WKURLSchemeHandler> { >+@public >+ HashMap<String, ResourceInfo> resources; >+} >+@end >+ >+@implementation StorageSchemes { >+} >+ >+- (void)webView:(WKWebView *)webView startURLSchemeTask:(id <WKURLSchemeTask>)task >+{ >+ auto entry = resources.find([task.request.URL absoluteString]); >+ if (entry == resources.end()) { >+ NSLog(@"Did not find resource entry for URL %@", task.request.URL); >+ return; >+ } >+ >+ RetainPtr<NSURLResponse> response = adoptNS([[NSURLResponse alloc] initWithURL:task.request.URL MIMEType:entry->value.mimeType.get() expectedContentLength:1 textEncodingName:nil]); >+ [task didReceiveResponse:response.get()]; >+ >+ [task didReceiveData:[NSData dataWithBytesNoCopy:(void*)entry->value.data length:strlen(entry->value.data) freeWhenDone:NO]]; >+ [task didFinish]; >+} >+ >+- (void)webView:(WKWebView *)webView stopURLSchemeTask:(id <WKURLSchemeTask>)task >+{ >+} >+ >+@end >+ >+static bool receivedMessage; >+ >+@interface QuotaMessageHandler : NSObject <WKScriptMessageHandler> >+-(void)setExpectedMessage:(NSString *)message; >+@end >+ >+@implementation QuotaMessageHandler { >+ NSString *_expectedMessage; >+} >+ >+- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message >+{ >+ if (_expectedMessage) { >+ EXPECT_TRUE([[message body] isEqualToString:_expectedMessage]); >+ _expectedMessage = nil; >+ } >+ receivedMessage = true; >+} >+ >+-(void)setExpectedMessage:(NSString *)message { >+ _expectedMessage = message; >+} >+@end >+ >+static const char* TestBytes = R"SWRESOURCE( >+<script> >+ >+async function doTest() >+{ >+ const cache = await window.caches.open("mycache"); >+ const promise = cache.put("http://example.org/test", new Response(new ArrayBuffer(1024 * 500))); >+ window.webkit.messageHandlers.qt.postMessage("start"); >+ promise.then(() => { >+ window.webkit.messageHandlers.qt.postMessage("pass"); >+ }, () => { >+ window.webkit.messageHandlers.qt.postMessage("fail"); >+ }); >+} >+doTest(); >+ >+function doTestAgain() >+{ >+ doTest(); >+} >+</script> >+)SWRESOURCE"; >+ >+static bool done; >+ >+static inline void setVisible(TestWKWebView *webView) >+{ >+#if PLATFORM(MAC) >+ [webView.window setIsVisible:YES]; >+#else >+ webView.window.hidden = NO; >+#endif >+} >+ >+TEST(WebKit, QuotaDelegate) >+{ >+ done = false; >+ [[WKWebsiteDataStore defaultDataStore] removeDataOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] modifiedSince:[NSDate distantPast] completionHandler:^() { >+ done = true; >+ }]; >+ TestWebKitAPI::Util::run(&done); >+ >+ auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]); >+ >+ [[configuration websiteDataStore] _setPerOriginStorageQuota: 1024 * 400]; >+ >+ auto messageHandler = adoptNS([[QuotaMessageHandler alloc] init]); >+ [[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"qt"]; >+ >+ auto handler1 = adoptNS([[StorageSchemes alloc] init]); >+ handler1->resources.set("qt1://test1.html", ResourceInfo { @"text/html", TestBytes }); >+ [configuration setURLSchemeHandler:handler1.get() forURLScheme:@"QT1"]; >+ [configuration.get().processPool _registerURLSchemeServiceWorkersCanHandle:@"qt1"]; >+ >+ auto handler2 = adoptNS([[StorageSchemes alloc] init]); >+ handler2->resources.set("qt2://test2.html", ResourceInfo { @"text/html", TestBytes }); >+ [configuration setURLSchemeHandler:handler2.get() forURLScheme:@"QT2"]; >+ [configuration.get().processPool _registerURLSchemeServiceWorkersCanHandle:@"qt2"]; >+ >+ auto webView1 = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get() addToWindow:YES]); >+ auto delegate1 = adoptNS([[QuotaDelegate alloc] init]); >+ [webView1 setUIDelegate:delegate1.get()]; >+ setVisible(webView1.get()); >+ >+ receivedQuotaDelegateCalled = false; >+ [webView1 loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"qt1://test1.html"]]]; >+ Util::run(&receivedQuotaDelegateCalled); >+ >+ auto webView2 = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get() addToWindow:YES]); >+ auto delegate2 = adoptNS([[QuotaDelegate alloc] init]); >+ [webView2 setUIDelegate:delegate2.get()]; >+ setVisible(webView2.get()); >+ >+ receivedMessage = false; >+ [webView2 loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"qt2://test2.html"]]]; >+ [messageHandler setExpectedMessage: @"start"]; >+ Util::run(&receivedMessage); >+ >+ EXPECT_FALSE(delegate2.get().quotaDelegateCalled); >+ [delegate1 grantQuota]; >+ >+ [messageHandler setExpectedMessage: @"pass"]; >+ receivedMessage = false; >+ Util::run(&receivedMessage); >+ >+ while (!delegate2.get().quotaDelegateCalled) >+ TestWebKitAPI::Util::sleep(0.1); >+ >+ [delegate2 denyQuota]; >+ >+ [messageHandler setExpectedMessage: @"fail"]; >+ receivedMessage = false; >+ Util::run(&receivedMessage); >+} >+ >+TEST(WebKit, QuotaDelegateReload) >+{ >+ done = false; >+ [[WKWebsiteDataStore defaultDataStore] removeDataOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] modifiedSince:[NSDate distantPast] completionHandler:^() { >+ done = true; >+ }]; >+ TestWebKitAPI::Util::run(&done); >+ >+ auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]); >+ >+ [[configuration websiteDataStore] _setPerOriginStorageQuota: 1024 * 400]; >+ >+ auto messageHandler = adoptNS([[QuotaMessageHandler alloc] init]); >+ [[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"qt"]; >+ >+ auto handler = adoptNS([[StorageSchemes alloc] init]); >+ handler->resources.set("qt://test1.html", ResourceInfo { @"text/html", TestBytes }); >+ [configuration setURLSchemeHandler:handler.get() forURLScheme:@"QT"]; >+ [configuration.get().processPool _registerURLSchemeServiceWorkersCanHandle:@"qt"]; >+ >+ auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get() addToWindow:YES]); >+ auto delegate = adoptNS([[QuotaDelegate alloc] init]); >+ [webView setUIDelegate:delegate.get()]; >+ setVisible(webView.get()); >+ >+ receivedQuotaDelegateCalled = false; >+ [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"qt://test1.html"]]]; >+ Util::run(&receivedQuotaDelegateCalled); >+ >+ [delegate denyQuota]; >+ >+ [messageHandler setExpectedMessage: @"fail"]; >+ receivedMessage = false; >+ Util::run(&receivedMessage); >+ >+ receivedQuotaDelegateCalled = false; >+ [webView reload]; >+ Util::run(&receivedQuotaDelegateCalled); >+ >+ [delegate grantQuota]; >+ >+ [messageHandler setExpectedMessage: @"pass"]; >+ receivedMessage = false; >+ Util::run(&receivedMessage); >+} >+ >+TEST(WebKit, QuotaDelegateNavigateFragment) >+{ >+ done = false; >+ [[WKWebsiteDataStore defaultDataStore] removeDataOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] modifiedSince:[NSDate distantPast] completionHandler:^() { >+ done = true; >+ }]; >+ TestWebKitAPI::Util::run(&done); >+ >+ auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]); >+ >+ [[configuration websiteDataStore] _setPerOriginStorageQuota: 1024 * 400]; >+ >+ auto messageHandler = adoptNS([[QuotaMessageHandler alloc] init]); >+ [[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"qt"]; >+ >+ auto handler = adoptNS([[StorageSchemes alloc] init]); >+ handler->resources.set("qt://test1.html", ResourceInfo { @"text/html", TestBytes }); >+ [configuration setURLSchemeHandler:handler.get() forURLScheme:@"QT"]; >+ [configuration.get().processPool _registerURLSchemeServiceWorkersCanHandle:@"qt"]; >+ >+ auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get() addToWindow:YES]); >+ auto delegate = adoptNS([[QuotaDelegate alloc] init]); >+ [webView setUIDelegate:delegate.get()]; >+ setVisible(webView.get()); >+ >+ receivedQuotaDelegateCalled = false; >+ [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"qt://test1.html"]]]; >+ Util::run(&receivedQuotaDelegateCalled); >+ >+ [delegate denyQuota]; >+ >+ [messageHandler setExpectedMessage: @"fail"]; >+ receivedMessage = false; >+ Util::run(&receivedMessage); >+ >+ receivedQuotaDelegateCalled = false; >+ [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"qt://test1.html#fragment"]]]; >+ [webView stringByEvaluatingJavaScript:@"doTestAgain()"]; >+ >+ [messageHandler setExpectedMessage: @"start"]; >+ receivedMessage = false; >+ Util::run(&receivedMessage); >+ >+ [messageHandler setExpectedMessage: @"fail"]; >+ receivedMessage = false; >+ Util::run(&receivedMessage); >+ >+ EXPECT_FALSE(receivedQuotaDelegateCalled); >+}
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Formatted Diff
|
Diff
Attachments on
bug 197463
:
368677
|
369439
|
369449
|
369591
|
369883
|
369897
|
369902
|
369916
|
369960