WebPageProxy::exceededDatabaseQuota() needs to be serialized.
authormark.lam@apple.com <mark.lam@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 8 Mar 2013 01:31:19 +0000 (01:31 +0000)
committermark.lam@apple.com <mark.lam@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 8 Mar 2013 01:31:19 +0000 (01:31 +0000)
https://bugs.webkit.org/show_bug.cgi?id=111631.

Reviewed by Geoffrey Garen.

Previously, WebPageProxy::exceededDatabaseQuota() is called synchronously
with script execution in the WebProcess. Hence, it is never called in a
recursive manner.

In webkit2, we can have multiple WebProcesses concurrently triggering a
call to this function. While the function is waiting on feedback from a
UI dialog, the wait loop may re-enter the function to service a second
request to call this function from another WebProcess. This results in
problems where some of the WebProcesses will not get a proper reply, and
therefore hangs perpetually waiting for a non-forthcoming reply.

This changeset changes the function to queue the requests and ensure
that we do not recursively callback to the UI client.

* Platform/CoreIPC/HandleMessage.h:
(CoreIPC::callMemberFunction):
* UIProcess/WebPageProxy.cpp:
(ExceededDatabaseQuotaRecords):
(Record):
(WebKit::ExceededDatabaseQuotaRecords::areBeingProcessed):
(WebKit::ExceededDatabaseQuotaRecords::ExceededDatabaseQuotaRecords):
(WebKit::ExceededDatabaseQuotaRecords::~ExceededDatabaseQuotaRecords):
(WebKit::ExceededDatabaseQuotaRecords::shared):
(WebKit::ExceededDatabaseQuotaRecords::createRecord):
(WebKit::ExceededDatabaseQuotaRecords::add):
(WebKit::ExceededDatabaseQuotaRecords::next):
(WebKit::WebPageProxy::exceededDatabaseQuota):
* UIProcess/WebPageProxy.h:
* UIProcess/WebPageProxy.messages.in:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@145164 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/WebKit2/ChangeLog
Source/WebKit2/Platform/CoreIPC/HandleMessage.h
Source/WebKit2/UIProcess/WebPageProxy.cpp
Source/WebKit2/UIProcess/WebPageProxy.h
Source/WebKit2/UIProcess/WebPageProxy.messages.in

index 37372f1..abdb2bd 100644 (file)
@@ -1,3 +1,40 @@
+2013-03-07  Mark Lam  <mark.lam@apple.com>
+
+        WebPageProxy::exceededDatabaseQuota() needs to be serialized.
+        https://bugs.webkit.org/show_bug.cgi?id=111631.
+
+        Reviewed by Geoffrey Garen.
+
+        Previously, WebPageProxy::exceededDatabaseQuota() is called synchronously
+        with script execution in the WebProcess. Hence, it is never called in a
+        recursive manner.
+
+        In webkit2, we can have multiple WebProcesses concurrently triggering a
+        call to this function. While the function is waiting on feedback from a
+        UI dialog, the wait loop may re-enter the function to service a second
+        request to call this function from another WebProcess. This results in
+        problems where some of the WebProcesses will not get a proper reply, and
+        therefore hangs perpetually waiting for a non-forthcoming reply.
+
+        This changeset changes the function to queue the requests and ensure
+        that we do not recursively callback to the UI client.
+
+        * Platform/CoreIPC/HandleMessage.h:
+        (CoreIPC::callMemberFunction):
+        * UIProcess/WebPageProxy.cpp:
+        (ExceededDatabaseQuotaRecords):
+        (Record):
+        (WebKit::ExceededDatabaseQuotaRecords::areBeingProcessed):
+        (WebKit::ExceededDatabaseQuotaRecords::ExceededDatabaseQuotaRecords):
+        (WebKit::ExceededDatabaseQuotaRecords::~ExceededDatabaseQuotaRecords):
+        (WebKit::ExceededDatabaseQuotaRecords::shared):
+        (WebKit::ExceededDatabaseQuotaRecords::createRecord):
+        (WebKit::ExceededDatabaseQuotaRecords::add):
+        (WebKit::ExceededDatabaseQuotaRecords::next):
+        (WebKit::WebPageProxy::exceededDatabaseQuota):
+        * UIProcess/WebPageProxy.h:
+        * UIProcess/WebPageProxy.messages.in:
+
 2013-03-07  Jeffrey Pfau  <jpfau@apple.com>
 
         CFNetwork cache partitioning does not work properly on subdomains
index fe996b1..82b0640 100644 (file)
@@ -216,6 +216,12 @@ void callMemberFunction(const Arguments2<P1, P2>& args, PassRefPtr<R> delayedRep
     (object->*function)(args.argument1, args.argument2, delayedReply);
 }
 
+template<typename C, typename MF, typename P1, typename P2, typename P3, typename P4, typename P5, typename P6, typename P7, typename P8, typename R>
+void callMemberFunction(const Arguments8<P1, P2, P3, P4, P5, P6, P7, P8>& args, PassRefPtr<R> delayedReply, C* object, MF function)
+{
+    (object->*function)(args.argument1, args.argument2, args.argument3, args.argument4, args.argument5, args.argument6, args.argument7, args.argument8, delayedReply);
+}
+
 // Dispatch functions with connection parameter.
 template<typename C, typename MF>
 void callMemberFunction(Connection* connection, const Arguments0&, C* object, MF function)
index 25796cd..d1067ba 100644 (file)
@@ -132,6 +132,77 @@ WKPageDebugPaintFlags WebPageProxy::s_debugPaintFlags = 0;
 
 DEFINE_DEBUG_ONLY_GLOBAL(WTF::RefCountedLeakCounter, webPageProxyCounter, ("WebPageProxy"));
 
+class ExceededDatabaseQuotaRecords {
+    WTF_MAKE_NONCOPYABLE(ExceededDatabaseQuotaRecords); WTF_MAKE_FAST_ALLOCATED;
+public:
+    struct Record {
+        uint64_t frameID;
+        String originIdentifier;
+        String databaseName;
+        String displayName;
+        uint64_t currentQuota;
+        uint64_t currentOriginUsage;
+        uint64_t currentDatabaseUsage;
+        uint64_t expectedUsage;
+        RefPtr<Messages::WebPageProxy::ExceededDatabaseQuota::DelayedReply> reply;
+    };
+
+    static ExceededDatabaseQuotaRecords& shared();
+
+    PassOwnPtr<Record> createRecord(uint64_t frameID, String originIdentifier,
+        String databaseName, String displayName, uint64_t currentQuota,
+        uint64_t currentOriginUsage, uint64_t currentDatabaseUsage, uint64_t expectedUsage, 
+        PassRefPtr<Messages::WebPageProxy::ExceededDatabaseQuota::DelayedReply>);
+
+    void add(PassOwnPtr<Record>);
+    bool areBeingProcessed() const { return m_currentRecord; }
+    Record* next();
+
+private:
+    ExceededDatabaseQuotaRecords() { }
+    ~ExceededDatabaseQuotaRecords() { }
+
+    Deque<OwnPtr<Record> > m_records;
+    OwnPtr<Record> m_currentRecord;
+};
+
+ExceededDatabaseQuotaRecords& ExceededDatabaseQuotaRecords::shared()
+{
+    DEFINE_STATIC_LOCAL(ExceededDatabaseQuotaRecords, records, ());
+    return records;
+}
+
+PassOwnPtr<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, PassRefPtr<Messages::WebPageProxy::ExceededDatabaseQuota::DelayedReply> reply)
+{
+    OwnPtr<Record> record = adoptPtr(new 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 = reply;
+    return record.release();
+}
+
+void ExceededDatabaseQuotaRecords::add(PassOwnPtr<ExceededDatabaseQuotaRecords::Record> record)
+{
+    m_records.append(record);
+}
+
+ExceededDatabaseQuotaRecords::Record* ExceededDatabaseQuotaRecords::next()
+{
+    m_currentRecord.clear();
+    if (!m_records.isEmpty())
+        m_currentRecord = m_records.takeFirst();
+    return m_currentRecord.get();
+}
+
 #if !LOG_DISABLED
 static const char* webKeyboardEventTypeString(WebEvent::Type type)
 {
@@ -3935,14 +4006,31 @@ void WebPageProxy::didReceiveAuthenticationChallengeProxy(uint64_t frameID, Pass
     m_loaderClient.didReceiveAuthenticationChallengeInFrame(this, frame, authenticationChallenge.get());
 }
 
-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, uint64_t& newQuota)
+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, PassRefPtr<Messages::WebPageProxy::ExceededDatabaseQuota::DelayedReply> reply)
 {
-    WebFrameProxy* frame = m_process->webFrame(frameID);
-    MESSAGE_CHECK(frame);
+    ExceededDatabaseQuotaRecords& records = ExceededDatabaseQuotaRecords::shared();
+    OwnPtr<ExceededDatabaseQuotaRecords::Record> newRecord =  records.createRecord(frameID,
+        originIdentifier, databaseName, displayName, currentQuota, currentOriginUsage,
+        currentDatabaseUsage, expectedUsage, reply);
+    records.add(newRecord.release());
 
-    RefPtr<WebSecurityOrigin> origin = WebSecurityOrigin::createFromDatabaseIdentifier(originIdentifier);
+    if (records.areBeingProcessed())
+        return;
+
+    ExceededDatabaseQuotaRecords::Record* record = records.next();
+    while (record) {
+        WebFrameProxy* frame = m_process->webFrame(record->frameID);
+        MESSAGE_CHECK(frame);
 
-    newQuota = m_uiClient.exceededDatabaseQuota(this, frame, origin.get(), databaseName, displayName, currentQuota, currentOriginUsage, currentDatabaseUsage, expectedUsage);
+        RefPtr<WebSecurityOrigin> origin = WebSecurityOrigin::createFromDatabaseIdentifier(record->originIdentifier);
+
+        uint64_t newQuota = m_uiClient.exceededDatabaseQuota(this, frame, origin.get(),
+            record->databaseName, record->displayName, record->currentQuota,
+            record->currentOriginUsage, record->currentDatabaseUsage, record->expectedUsage);
+
+        record->reply->send(newQuota);
+        record = records.next();
+    }
 }
 
 void WebPageProxy::requestGeolocationPermissionForFrame(uint64_t geolocationID, uint64_t frameID, String originIdentifier)
index d8531e2..7b7db44 100644 (file)
 #include "WebHitTestResult.h"
 #include "WebLoaderClient.h"
 #include "WebPageContextMenuClient.h"
+#include <WebCore/AlternativeTextClient.h> // FIXME: Needed by WebPageProxyMessages.h for DICTATION_ALTERNATIVES.
+#include "WebPageProxyMessages.h"
 #include "WebPolicyClient.h"
 #include "WebPopupMenuProxy.h"
 #include "WebUIClient.h"
-#include <WebCore/AlternativeTextClient.h>
 #include <WebCore/Color.h>
 #include <WebCore/DragActions.h>
 #include <WebCore/DragSession.h>
@@ -857,7 +858,7 @@ private:
     void pageDidScroll();
     void runOpenPanel(uint64_t frameID, const WebCore::FileChooserSettings&);
     void printFrame(uint64_t frameID);
-    void 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, uint64_t& newQuota);
+    void 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, PassRefPtr<Messages::WebPageProxy::ExceededDatabaseQuota::DelayedReply>);
     void requestGeolocationPermissionForFrame(uint64_t geolocationID, uint64_t frameID, String originIdentifier);
     void runModal();
     void notifyScrollerThumbIsVisibleInRect(const WebCore::IntRect&);
index 64c57ed..ebc44c5 100644 (file)
@@ -212,7 +212,7 @@ messages -> WebPageProxy {
     DidReceiveAuthenticationChallenge(uint64_t frameID, WebCore::AuthenticationChallenge challenge, uint64_t challengeID)
 
     # Database messages
-    ExceededDatabaseQuota(uint64_t frameID, WTF::String originIdentifier, WTF::String databaseName, WTF::String databaseDisplayName, uint64_t currentQuota, uint64_t currentOriginUsage, uint64_t currentDatabaseUsage, uint64_t expectedUsage) -> (uint64_t newQuota)
+    ExceededDatabaseQuota(uint64_t frameID, WTF::String originIdentifier, WTF::String databaseName, WTF::String databaseDisplayName, uint64_t currentQuota, uint64_t currentOriginUsage, uint64_t currentDatabaseUsage, uint64_t expectedUsage) -> (uint64_t newQuota) Delayed
 
     # Geolocation messages
     RequestGeolocationPermissionForFrame(uint64_t geolocationID, uint64_t frameID, WTF::String originIdentifier)