Title: [245328] trunk
Revision
245328
Author
[email protected]
Date
2019-05-15 10:40:01 -0700 (Wed, 15 May 2019)

Log Message

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.

Source/WebKit:

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):

Tools:

* 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):

Modified Paths

Added Paths

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);
+}
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to