Diff
Modified: trunk/Source/WebKit/ChangeLog (245327 => 245328)
--- trunk/Source/WebKit/ChangeLog 2019-05-15 16:35:58 UTC (rev 245327)
+++ trunk/Source/WebKit/ChangeLog 2019-05-15 17:40:01 UTC (rev 245328)
@@ -1,5 +1,45 @@
2019-05-15 Youenn Fablet <[email protected]>
+ Reuse existing WebPageProxy quota handler for NetworkProcessProxy quota requests
+ https://bugs.webkit.org/show_bug.cgi?id=197463
+ <rdar://problem/47403621>
+
+ Reviewed by Alex Christensen.
+
+ 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-15 Youenn Fablet <[email protected]>
+
Constant crashes under WebPage::isThrottleable() after r245299
https://bugs.webkit.org/show_bug.cgi?id=197902
<rdar://problem/50793796>
Modified: trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebsiteDataStore.mm (245327 => 245328)
--- trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebsiteDataStore.mm 2019-05-15 16:35:58 UTC (rev 245327)
+++ trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebsiteDataStore.mm 2019-05-15 17:40:01 UTC (rev 245328)
@@ -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)
Modified: trunk/Source/WebKit/UIProcess/Network/NetworkProcessProxy.cpp (245327 => 245328)
--- trunk/Source/WebKit/UIProcess/Network/NetworkProcessProxy.cpp 2019-05-15 16:35:58 UTC (rev 245327)
+++ trunk/Source/WebKit/UIProcess/Network/NetworkProcessProxy.cpp 2019-05-15 17:40:01 UTC (rev 245328)
@@ -1141,7 +1141,7 @@
}
#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 @@
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()
Modified: trunk/Source/WebKit/UIProcess/WebPageProxy.cpp (245327 => 245328)
--- trunk/Source/WebKit/UIProcess/WebPageProxy.cpp 2019-05-15 16:35:58 UTC (rev 245327)
+++ trunk/Source/WebKit/UIProcess/WebPageProxy.cpp 2019-05-15 17:40:01 UTC (rev 245328)
@@ -264,78 +264,45 @@
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 StorageRequests& singleton();
- static ExceededDatabaseQuotaRecords& singleton();
+ void processOrAppend(CompletionHandler<void()>&& completionHandler)
+ {
+ if (m_requestsAreBeingProcessed) {
+ m_requests.append(WTFMove(completionHandler));
+ return;
+ }
+ m_requestsAreBeingProcessed = true;
+ completionHandler();
+ }
- 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 processNextIfAny()
+ {
+ if (m_requests.isEmpty()) {
+ m_requestsAreBeingProcessed = false;
+ return;
+ }
+ m_requests.takeFirst()();
+ }
- void add(std::unique_ptr<Record>);
- bool areBeingProcessed() const { return !!m_currentRecord; }
- Record* next();
-
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_requestsAreBeingProcessed { false };
};
-ExceededDatabaseQuotaRecords& ExceededDatabaseQuotaRecords::singleton()
+StorageRequests& StorageRequests::singleton()
{
- static NeverDestroyed<ExceededDatabaseQuotaRecords> records;
- return records;
+ static NeverDestroyed<StorageRequests> requests;
+ return requests;
}
-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();
-}
-
#if !LOG_DISABLED
static const char* webMouseEventTypeString(WebEvent::Type type)
{
@@ -395,6 +362,25 @@
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::forWebPagesWithOrigin(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)));
@@ -4415,6 +4401,7 @@
#else
UNUSED_PARAM(frameID);
#endif
+ m_isQuotaIncreaseDenied = false;
}
void WebPageProxy::viewIsBecomingVisible()
@@ -7263,24 +7250,41 @@
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)
Modified: trunk/Source/WebKit/UIProcess/WebPageProxy.h (245327 => 245328)
--- trunk/Source/WebKit/UIProcess/WebPageProxy.h 2019-05-15 16:35:58 UTC (rev 245327)
+++ trunk/Source/WebKit/UIProcess/WebPageProxy.h 2019-05-15 17:40:01 UTC (rev 245328)
@@ -385,6 +385,8 @@
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 @@
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();
@@ -2076,6 +2080,8 @@
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;
@@ -2505,6 +2511,7 @@
};
Optional<SpeechSynthesisData> m_speechSynthesisData;
#endif
+ bool m_isQuotaIncreaseDenied { false };
};
} // namespace WebKit
Modified: trunk/Source/WebKit/UIProcess/WebProcessProxy.cpp (245327 => 245328)
--- trunk/Source/WebKit/UIProcess/WebProcessProxy.cpp 2019-05-15 16:35:58 UTC (rev 245327)
+++ trunk/Source/WebKit/UIProcess/WebProcessProxy.cpp 2019-05-15 17:40:01 UTC (rev 245328)
@@ -123,6 +123,15 @@
return pageMap;
}
+void WebProcessProxy::forWebPagesWithOrigin(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));
Modified: trunk/Source/WebKit/UIProcess/WebProcessProxy.h (245327 => 245328)
--- trunk/Source/WebKit/UIProcess/WebProcessProxy.h 2019-05-15 16:35:58 UTC (rev 245327)
+++ trunk/Source/WebKit/UIProcess/WebProcessProxy.h 2019-05-15 17:40:01 UTC (rev 245328)
@@ -110,6 +110,8 @@
static Ref<WebProcessProxy> create(WebProcessPool&, WebsiteDataStore*, IsPrewarmed, ShouldLaunchProcess = ShouldLaunchProcess::Yes);
~WebProcessProxy();
+ static void forWebPagesWithOrigin(PAL::SessionID, const WebCore::SecurityOriginData&, const Function<void(WebPageProxy&)>&);
+
WebConnection* webConnection() const { return m_webConnection.get(); }
unsigned suspendedPageCount() const { return m_suspendedPageCount; }
Modified: trunk/Tools/ChangeLog (245327 => 245328)
--- trunk/Tools/ChangeLog 2019-05-15 16:35:58 UTC (rev 245327)
+++ trunk/Tools/ChangeLog 2019-05-15 17:40:01 UTC (rev 245328)
@@ -1,5 +1,23 @@
2019-05-15 Youenn Fablet <[email protected]>
+ Reuse existing WebPageProxy quota handler for NetworkProcessProxy quota requests
+ https://bugs.webkit.org/show_bug.cgi?id=197463
+ <rdar://problem/47403621>
+
+ Reviewed by Alex Christensen.
+
+ * 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-15 Youenn Fablet <[email protected]>
+
Constant crashes under WebPage::isThrottleable() after r245299
https://bugs.webkit.org/show_bug.cgi?id=197902
<rdar://problem/50793796>
Modified: trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj (245327 => 245328)
--- trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj 2019-05-15 16:35:58 UTC (rev 245327)
+++ trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj 2019-05-15 17:40:01 UTC (rev 245328)
@@ -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 */,
Added: trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/StorageQuota.mm (0 => 245328)
--- trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/StorageQuota.mm (rev 0)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/StorageQuota.mm 2019-05-15 17:40:01 UTC (rev 245328)
@@ -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);
+}