Title: [284261] trunk
Revision
284261
Author
[email protected]
Date
2021-10-15 12:12:06 -0700 (Fri, 15 Oct 2021)

Log Message

REGRESSION (r284079): [Debug] IPCTestingAPI.CanReceiveSharedMemory is timing out
https://bugs.webkit.org/show_bug.cgi?id=231701
<rdar://problem/84219000>

Reviewed by Tim Horton.

Source/WebKit:

Implement two new methods on JSIPCStreamClientConnection: `sendMessage()` and `sendSyncMessage()`, which can be
used to send IPC messages through the IPC stream client connection. To do this, we match the existing methods of
the same names on JSIPC by creating a message encoder and sending it over the stream connection's IPC
connection, effectively treating all IPC messages as out-of-line.

In the future, this should be changed so that we only try to send out-of-line messages if the message itself is
not stream encodable; for the time being, this at least allows us to fix the failing API test, and still makes
it possible to test sending arbitrary IPC through the connection stream, such that we'll be able to exercise
WebGL and display list IPC via these special JS IPC bindings.

* Platform/IPC/StreamClientConnection.h:
* WebProcess/WebPage/IPCTestingAPI.cpp:

Declare several static helper functions near the top of this source file, so that we can use them in
JSIPCStreamClientConnection below.

(WebKit::IPCTestingAPI::JSIPCStreamClientConnection::create):
(WebKit::IPCTestingAPI::JSIPCStreamClientConnection::JSIPCStreamClientConnection):
(WebKit::IPCTestingAPI::JSIPCStreamClientConnection::staticFunctions):

Drive-by fix: make the pointer from JSIPCStreamConnectionBuffer to JSIPCStreamClientConnection a WeakPtr, to
prevent the stream connection buffer from keeping the client connection alive for longer than necessary. The
JSIPCStreamConnectionBuffer is always intended to be outlived by JSIPCStreamClientConnection when using JS IPC.

(WebKit::IPCTestingAPI::extractIPCStreamMessageInfo):

Pull logic for extracting a destination ID, message name, and IPC timeout into a common helper method.

(WebKit::IPCTestingAPI::JSIPCStreamClientConnection::prepareToSendOutOfStreamMessage):

Add a helper method that encodes the given message and prepares the stream connection for sending this as an
out-of-stream message by setting the destination ID (if needed) and appending the `ProcessOutOfStreamMessage`
message to indicate that the recipient should expect an out-of-stream message.

(WebKit::IPCTestingAPI::JSIPCStreamClientConnection::sendMessage):
(WebKit::IPCTestingAPI::JSIPCStreamClientConnection::sendSyncMessage):
(WebKit::IPCTestingAPI::JSIPC::createStreamClientConnection):

Tools:

Fix the failing API test by using the new `sendSyncMessage()` method on JSIPCStreamClientConnection to send
the "RemoteRenderingBackend::UpdateSharedMemoryForGetPixelBuffer" stream message and decode the result into a
buffer of shared memory.

* TestWebKitAPI/Tests/WebKitCocoa/IPCTestingAPI.mm:
(TEST):

Modified Paths

Diff

Modified: trunk/Source/WebKit/ChangeLog (284260 => 284261)


--- trunk/Source/WebKit/ChangeLog	2021-10-15 19:10:40 UTC (rev 284260)
+++ trunk/Source/WebKit/ChangeLog	2021-10-15 19:12:06 UTC (rev 284261)
@@ -1,3 +1,49 @@
+2021-10-15  Wenson Hsieh  <[email protected]>
+
+        REGRESSION (r284079): [Debug] IPCTestingAPI.CanReceiveSharedMemory is timing out
+        https://bugs.webkit.org/show_bug.cgi?id=231701
+        <rdar://problem/84219000>
+
+        Reviewed by Tim Horton.
+
+        Implement two new methods on JSIPCStreamClientConnection: `sendMessage()` and `sendSyncMessage()`, which can be
+        used to send IPC messages through the IPC stream client connection. To do this, we match the existing methods of
+        the same names on JSIPC by creating a message encoder and sending it over the stream connection's IPC
+        connection, effectively treating all IPC messages as out-of-line.
+
+        In the future, this should be changed so that we only try to send out-of-line messages if the message itself is
+        not stream encodable; for the time being, this at least allows us to fix the failing API test, and still makes
+        it possible to test sending arbitrary IPC through the connection stream, such that we'll be able to exercise
+        WebGL and display list IPC via these special JS IPC bindings.
+
+        * Platform/IPC/StreamClientConnection.h:
+        * WebProcess/WebPage/IPCTestingAPI.cpp:
+
+        Declare several static helper functions near the top of this source file, so that we can use them in
+        JSIPCStreamClientConnection below.
+
+        (WebKit::IPCTestingAPI::JSIPCStreamClientConnection::create):
+        (WebKit::IPCTestingAPI::JSIPCStreamClientConnection::JSIPCStreamClientConnection):
+        (WebKit::IPCTestingAPI::JSIPCStreamClientConnection::staticFunctions):
+
+        Drive-by fix: make the pointer from JSIPCStreamConnectionBuffer to JSIPCStreamClientConnection a WeakPtr, to
+        prevent the stream connection buffer from keeping the client connection alive for longer than necessary. The
+        JSIPCStreamConnectionBuffer is always intended to be outlived by JSIPCStreamClientConnection when using JS IPC.
+
+        (WebKit::IPCTestingAPI::extractIPCStreamMessageInfo):
+
+        Pull logic for extracting a destination ID, message name, and IPC timeout into a common helper method.
+
+        (WebKit::IPCTestingAPI::JSIPCStreamClientConnection::prepareToSendOutOfStreamMessage):
+
+        Add a helper method that encodes the given message and prepares the stream connection for sending this as an
+        out-of-stream message by setting the destination ID (if needed) and appending the `ProcessOutOfStreamMessage`
+        message to indicate that the recipient should expect an out-of-stream message.
+
+        (WebKit::IPCTestingAPI::JSIPCStreamClientConnection::sendMessage):
+        (WebKit::IPCTestingAPI::JSIPCStreamClientConnection::sendSyncMessage):
+        (WebKit::IPCTestingAPI::JSIPC::createStreamClientConnection):
+
 2021-10-15  Brent Fulgham  <[email protected]>
 
         [macOS] Update sandboxes to support finer-grained XPC services in the media stack

Modified: trunk/Source/WebKit/Platform/IPC/StreamClientConnection.h (284260 => 284261)


--- trunk/Source/WebKit/Platform/IPC/StreamClientConnection.h	2021-10-15 19:10:40 UTC (rev 284260)
+++ trunk/Source/WebKit/Platform/IPC/StreamClientConnection.h	2021-10-15 19:12:06 UTC (rev 284261)
@@ -34,6 +34,13 @@
 #include "StreamConnectionEncoder.h"
 #include <wtf/MonotonicTime.h>
 #include <wtf/Threading.h>
+
+namespace WebKit {
+namespace IPCTestingAPI {
+class JSIPCStreamClientConnection;
+}
+}
+
 namespace IPC {
 
 // A message stream is a half-duplex two-way stream of messages to a between the client and the
@@ -69,6 +76,8 @@
     SendSyncResult sendSync(T&& message, typename T::Reply&&, ObjectIdentifier<U> destinationID, Timeout);
 
 private:
+    friend class WebKit::IPCTestingAPI::JSIPCStreamClientConnection;
+
     struct Span {
         uint8_t* data;
         size_t size;

Modified: trunk/Source/WebKit/WebProcess/WebPage/IPCTestingAPI.cpp (284260 => 284261)


--- trunk/Source/WebKit/WebProcess/WebPage/IPCTestingAPI.cpp	2021-10-15 19:10:40 UTC (rev 284260)
+++ trunk/Source/WebKit/WebProcess/WebPage/IPCTestingAPI.cpp	2021-10-15 19:12:06 UTC (rev 284261)
@@ -61,6 +61,11 @@
 
 namespace IPCTestingAPI {
 
+static std::optional<uint64_t> destinationIDFromArgument(JSC::JSGlobalObject*, JSValueRef, JSValueRef*);
+static std::optional<uint64_t> messageIDFromArgument(JSC::JSGlobalObject*, JSValueRef, JSValueRef*);
+static JSC::JSObject* jsResultFromReplyDecoder(JSC::JSGlobalObject*, IPC::MessageName, IPC::Decoder&);
+static bool encodeArgument(IPC::Encoder&, JSIPC&, JSContextRef, JSValueRef, JSValueRef* exception);
+
 class JSIPCSemaphore : public RefCounted<JSIPCSemaphore> {
 public:
     static Ref<JSIPCSemaphore> create(IPC::Semaphore&& semaphore = { })
@@ -95,11 +100,11 @@
     IPC::Semaphore m_semaphore;
 };
 
-class JSIPCStreamClientConnection : public RefCounted<JSIPCStreamClientConnection> {
+class JSIPCStreamClientConnection : public RefCounted<JSIPCStreamClientConnection>, public CanMakeWeakPtr<JSIPCStreamClientConnection> {
 public:
-    static Ref<JSIPCStreamClientConnection> create(IPC::Connection& connection, size_t bufferSize)
+    static Ref<JSIPCStreamClientConnection> create(JSIPC& jsIPC, IPC::Connection& connection, size_t bufferSize)
     {
-        return adoptRef(*new JSIPCStreamClientConnection(connection, bufferSize));
+        return adoptRef(*new JSIPCStreamClientConnection(jsIPC, connection, bufferSize));
     }
 
     JSObjectRef createJSWrapper(JSContextRef);
@@ -108,8 +113,9 @@
     IPC::StreamClientConnection& connection() { return m_streamConnection; }
 
 private:
-    JSIPCStreamClientConnection(IPC::Connection& connection, size_t bufferSize)
-        : m_streamConnection { connection, bufferSize }
+    JSIPCStreamClientConnection(JSIPC& jsIPC, IPC::Connection& connection, size_t bufferSize)
+        : m_jsIPC(jsIPC)
+        , m_streamConnection { connection, bufferSize }
     { }
 
     void setWakeUpSemaphore(JSIPCSemaphore& jsSemaphore) { m_streamConnection.setWakeUpSemaphore(jsSemaphore.exchange()); }
@@ -119,10 +125,15 @@
     static void initialize(JSContextRef, JSObjectRef);
     static void finalize(JSObjectRef);
 
+    static bool prepareToSendOutOfStreamMessage(JSContextRef, size_t argumentCount, const JSValueRef arguments[], JSIPC&, IPC::StreamClientConnection&, IPC::Encoder&, uint64_t destinationID, IPC::Timeout, JSValueRef* exception);
+
     static const JSStaticFunction* staticFunctions();
     static JSValueRef streamBuffer(JSContextRef, JSObjectRef, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception);
     static JSValueRef setWakeUpSemaphore(JSContextRef, JSObjectRef, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception);
+    static JSValueRef sendMessage(JSContextRef, JSObjectRef, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception);
+    static JSValueRef sendSyncMessage(JSContextRef, JSObjectRef, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception);
 
+    WeakPtr<JSIPC> m_jsIPC;
     IPC::StreamClientConnection m_streamConnection;
 };
 
@@ -150,7 +161,7 @@
 
     static const JSStaticFunction* staticFunctions();
 
-    Ref<JSIPCStreamClientConnection> m_streamConnection;
+    WeakPtr<JSIPCStreamClientConnection> m_streamConnection;
 };
 
 class JSSharedMemory : public RefCounted<JSSharedMemory> {
@@ -397,6 +408,8 @@
     static const JSStaticFunction functions[] = {
         { "streamBuffer", streamBuffer, kJSPropertyAttributeDontDelete | kJSPropertyAttributeReadOnly },
         { "setWakeUpSemaphore", setWakeUpSemaphore, kJSPropertyAttributeDontDelete | kJSPropertyAttributeReadOnly },
+        { "sendMessage", sendMessage, kJSPropertyAttributeDontDelete | kJSPropertyAttributeReadOnly },
+        { "sendSyncMessage", sendSyncMessage, kJSPropertyAttributeDontDelete | kJSPropertyAttributeReadOnly },
         { 0, 0, 0 }
     };
     return functions;
@@ -440,6 +453,123 @@
     return JSValueMakeUndefined(context);
 }
 
+struct IPCStreamMessageInfo {
+    uint64_t destinationID;
+    IPC::MessageName messageName;
+    IPC::Timeout timeout;
+};
+
+static std::optional<IPCStreamMessageInfo> extractIPCStreamMessageInfo(JSContextRef context, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+    if (argumentCount < 3) {
+        *exception = createTypeError(context, "Must specify destination ID, message ID, and timeout as the first three arguments"_s);
+        return std::nullopt;
+    }
+
+    auto* globalObject = toJS(context);
+    auto destinationID = destinationIDFromArgument(globalObject, arguments[0], exception);
+    if (!destinationID)
+        return std::nullopt;
+
+    auto messageID = messageIDFromArgument(globalObject, arguments[1], exception);
+    if (!messageID)
+        return std::nullopt;
+
+    Seconds timeoutDuration;
+    {
+        auto jsValue = toJS(globalObject, arguments[2]);
+        if (!jsValue.isNumber()) {
+            *exception = createTypeError(context, "Timeout must be a number"_s);
+            return std::nullopt;
+        }
+        timeoutDuration = Seconds { jsValue.asNumber() };
+    }
+
+    return { { *destinationID, static_cast<IPC::MessageName>(*messageID), { timeoutDuration } } };
+}
+
+bool JSIPCStreamClientConnection::prepareToSendOutOfStreamMessage(JSContextRef context, size_t argumentCount, const JSValueRef arguments[], JSIPC& jsIPC, IPC::StreamClientConnection& streamConnection, IPC::Encoder& encoder, uint64_t destinationID, IPC::Timeout timeout, JSValueRef* exception)
+{
+    // FIXME: Add support for sending in-stream IPC messages when appropriate.
+    if (argumentCount > 3) {
+        if (!encodeArgument(encoder, jsIPC, context, arguments[3], exception))
+            return false;
+    }
+
+    if (!streamConnection.trySendDestinationIDIfNeeded(destinationID, timeout))
+        return false;
+
+    auto span = streamConnection.tryAcquire(timeout);
+    if (!span)
+        return false;
+
+    streamConnection.sendProcessOutOfStreamMessage(WTFMove(*span));
+    return true;
+}
+
+JSValueRef JSIPCStreamClientConnection::sendMessage(JSContextRef context, JSObjectRef, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+    auto* globalObject = toJS(context);
+    JSC::JSLockHolder lock(globalObject->vm());
+
+    auto returnValue = JSValueMakeUndefined(context);
+    RefPtr jsStreamConnection = toWrapped(context, thisObject);
+    if (!jsStreamConnection) {
+        *exception = createTypeError(context, "Wrong type"_s);
+        return returnValue;
+    }
+
+    auto info = extractIPCStreamMessageInfo(context, argumentCount, arguments, exception);
+    if (!info)
+        return returnValue;
+
+    auto [destinationID, messageName, timeout] = *info;
+    auto& streamConnection = jsStreamConnection->connection();
+
+    auto encoder = makeUniqueRef<IPC::Encoder>(messageName, destinationID);
+    if (prepareToSendOutOfStreamMessage(context, argumentCount, arguments, *jsStreamConnection->m_jsIPC, streamConnection, encoder.get(), destinationID, timeout, exception))
+        streamConnection.m_connection.sendMessage(WTFMove(encoder), { });
+
+    return returnValue;
+}
+
+JSValueRef JSIPCStreamClientConnection::sendSyncMessage(JSContextRef context, JSObjectRef, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+    auto* globalObject = toJS(context);
+    JSC::JSLockHolder lock(globalObject->vm());
+
+    RefPtr jsStreamConnection = toWrapped(context, thisObject);
+    if (!jsStreamConnection) {
+        *exception = createTypeError(context, "Wrong type"_s);
+        return JSValueMakeUndefined(context);
+    }
+
+    auto info = extractIPCStreamMessageInfo(context, argumentCount, arguments, exception);
+    if (!info)
+        return JSValueMakeUndefined(context);
+
+    auto [destinationID, messageName, timeout] = *info;
+    auto& streamConnection = jsStreamConnection->connection();
+
+    IPC::Connection::SyncRequestID syncRequestID;
+    auto encoder = streamConnection.m_connection.createSyncMessageEncoder(messageName, destinationID, syncRequestID);
+    if (!prepareToSendOutOfStreamMessage(context, argumentCount, arguments, *jsStreamConnection->m_jsIPC, streamConnection, encoder.get(), destinationID, timeout, exception))
+        return JSValueMakeUndefined(context);
+
+    if (auto replyDecoder = streamConnection.m_connection.sendSyncMessage(syncRequestID, WTFMove(encoder), timeout, { })) {
+        auto scope = DECLARE_CATCH_SCOPE(globalObject->vm());
+        auto* jsResult = jsResultFromReplyDecoder(globalObject, messageName, *replyDecoder);
+        if (scope.exception()) {
+            *exception = toRef(globalObject, scope.exception());
+            scope.clearException();
+            return JSValueMakeUndefined(context);
+        }
+        return toRef(globalObject, jsResult);
+    }
+
+    return JSValueMakeUndefined(context);
+}
+
 JSObjectRef JSIPCStreamConnectionBuffer::createJSWrapper(JSContextRef context)
 {
     auto* globalObject = toJS(context);
@@ -1050,8 +1180,6 @@
     return true;
 }
 
-static bool encodeArgument(IPC::Encoder&, JSIPC&, JSContextRef, JSValueRef, JSValueRef* exception);
-
 struct VectorEncodeHelper {
     Ref<JSIPC> jsIPC;
     JSContextRef context;
@@ -1552,7 +1680,7 @@
         return JSValueMakeUndefined(context);
     }
 
-    return JSIPCStreamClientConnection::create(*connection, *bufferSize)->createJSWrapper(context);
+    return JSIPCStreamClientConnection::create(*jsIPC, *connection, *bufferSize)->createJSWrapper(context);
 }
 
 JSValueRef JSIPC::createSemaphore(JSContextRef context, JSObjectRef, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)

Modified: trunk/Tools/ChangeLog (284260 => 284261)


--- trunk/Tools/ChangeLog	2021-10-15 19:10:40 UTC (rev 284260)
+++ trunk/Tools/ChangeLog	2021-10-15 19:12:06 UTC (rev 284261)
@@ -1,3 +1,18 @@
+2021-10-15  Wenson Hsieh  <[email protected]>
+
+        REGRESSION (r284079): [Debug] IPCTestingAPI.CanReceiveSharedMemory is timing out
+        https://bugs.webkit.org/show_bug.cgi?id=231701
+        <rdar://problem/84219000>
+
+        Reviewed by Tim Horton.
+
+        Fix the failing API test by using the new `sendSyncMessage()` method on JSIPCStreamClientConnection to send
+        the "RemoteRenderingBackend::UpdateSharedMemoryForGetPixelBuffer" stream message and decode the result into a
+        buffer of shared memory.
+
+        * TestWebKitAPI/Tests/WebKitCocoa/IPCTestingAPI.mm:
+        (TEST):
+
 2021-10-14  John Wilander  <[email protected]>
 
         TestWebKitAPI.PrivateClickMeasurement.FraudPrevention is a constant timeout

Modified: trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/IPCTestingAPI.mm (284260 => 284261)


--- trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/IPCTestingAPI.mm	2021-10-15 19:10:40 UTC (rev 284260)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/IPCTestingAPI.mm	2021-10-15 19:12:06 UTC (rev 284261)
@@ -228,6 +228,7 @@
 }
 
 #if ENABLE(GPU_PROCESS)
+
 TEST(IPCTestingAPI, CanSendSyncMessageToGPUProcess)
 {
     auto webView = createWebViewWithIPCTestingAPI();
@@ -325,7 +326,7 @@
         "]);"
         "const arguments = IPC.waitForMessage('GPU', 123, IPC.messages.RemoteRenderingBackendProxy_DidCreateWakeUpSemaphoreForDisplayListStream.name, 100);"
         "streamConnection.setWakeUpSemaphore(arguments[0].value);"
-        "const result = IPC.sendSyncMessage('GPU', 123, IPC.messages.RemoteRenderingBackend_UpdateSharedMemoryForGetPixelBuffer.name, 100, [{type: 'uint32_t', value: 8}]);"
+        "const result = streamConnection.sendSyncMessage(123, IPC.messages.RemoteRenderingBackend_UpdateSharedMemoryForGetPixelBuffer.name, 100, [{type: 'uint32_t', value: 8}]);"
         "alert(result.arguments.length);"
         "</script>";
 
@@ -337,10 +338,10 @@
     EXPECT_STREQ([webView stringByEvaluatingJavaScript:@"firstReply = result.arguments[0]; firstReply.type"].UTF8String, "SharedMemory");
     EXPECT_STREQ([webView stringByEvaluatingJavaScript:@"firstReply.protection"].UTF8String, "ReadOnly");
     EXPECT_STREQ([webView stringByEvaluatingJavaScript:@"Array.from(new Uint8Array(firstReply.value.readBytes(0, 8))).toString()"].UTF8String, "0,0,0,0,0,0,0,0");
-
 }
-#endif
 
+#endif // ENABLE(GPU_PROCESS)
+
 TEST(IPCTestingAPI, CanCreateIPCSemaphore)
 {
     auto webView = createWebViewWithIPCTestingAPI();
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to