⚠ Archived content — this site is no longer maintained.   Current WebKit documentation is at docs.webkit.org.

Changeset 268848 in webkit


Ignore:
Timestamp:
Oct 21, 2020, 5:41:50 PM (6 years ago)
Author:
rniwa@webkit.org
Message:

IPC testing API should have the capability to observe messages being sent and received
https://bugs.webkit.org/show_bug.cgi?id=217870

Reviewed by Darin Adler.

Source/WebKit:

Added IPC.addIncomingMessageListener and IPC.addOutgoingMessageListener which allows JavaScript
to observe IPC messages being sent or received by WebContent process. We use the generated code
added in r268503 to decode the IPC arguments.

Tests: TestWebKitAPI.IPCTestingAPI.CanInterceptAlert

TestWebKitAPI.IPCTestingAPI.CanInterceptHasStorageAccess
TestWebKitAPI.IPCTestingAPI.CanInterceptFindString

  • Platform/IPC/Connection.cpp:

(IPC::Connection::sendMessage): Added the code to invoke MessageObserver::willSendMessage.
Also remove any stale MessageObserver as neded.
(IPC::Connection::addMessageObserver): Added.
(IPC::Connection::dispatchMessage): Added the code to invoke MessageObserver::didReceiveMessage.
Also remove any stale MessageObserver as neded.

  • Platform/IPC/Connection.h:

(IPC::Connection::MessageObserver): Added. A pure virtual interface for observing IPC messages.

  • Platform/IPC/JSIPCBinding.h:

(IPC::jsValueForDecodedStringArgumentValue): Extracted from jsValueForDecodedArgumentValue<String>.
Now takes the type name as an argument.
(IPC::jsValueForDecodedArgumentValue<URL>): Use "URL" as the type name.
(IPC::jsValueForDecodedArgumentValue<RegistrableDomain>): Use "RegistrableDomain" as the type name.
(IPC::jsValueForDecodedArgumentValue<OptionSet<U>>): Added. Specializations for OptionSet<U>

  • WebProcess/WebPage/IPCTestingAPI.cpp:

(WebKit::IPCTestingAPI::JSMessageListener): Added. Implements IPC::MessageObserver.
(WebKit::IPCTestingAPI::JSIPC::staticFunctions):
(WebKit::IPCTestingAPI::createTypeError): Moved.
(WebKit::IPCTestingAPI::JSIPC::addMessageListener): Added.
(WebKit::IPCTestingAPI::JSIPC::addIncomingMessageListener): Added.
(WebKit::IPCTestingAPI::JSIPC::addOutgoingMessageListener): Added.
(WebKit::IPCTestingAPI::JSMessageListener::JSMessageListener): Added.
(WebKit::IPCTestingAPI::JSMessageListener::didReceiveMessage): Added.
(WebKit::IPCTestingAPI::JSMessageListener::willSendMessage): Added.
(WebKit::IPCTestingAPI::JSMessageListener::jsDescriptionFromDecoder): Added.

Tools:

Added tests to intercept IPC messages sent and received by WebContent process.

  • TestWebKitAPI/Tests/WebKitCocoa/IPCTestingAPI.mm:

(IPCTestingAPI.CanInterceptAlert):
(IPCTestingAPI.CanInterceptHasStorageAccess):
(IPCTestingAPI.CanInterceptFindString):

Location:
trunk
Files:
7 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebKit/ChangeLog

    r268842 r268848  
     12020-10-21  Ryosuke Niwa  <rniwa@webkit.org>
     2
     3        IPC testing API should have the capability to observe messages being sent and received
     4        https://bugs.webkit.org/show_bug.cgi?id=217870
     5
     6        Reviewed by Darin Adler.
     7
     8        Added IPC.addIncomingMessageListener and IPC.addOutgoingMessageListener which allows JavaScript
     9        to observe IPC messages being sent or received by WebContent process. We use the generated code
     10        added in r268503 to decode the IPC arguments.
     11
     12        Tests: TestWebKitAPI.IPCTestingAPI.CanInterceptAlert
     13               TestWebKitAPI.IPCTestingAPI.CanInterceptHasStorageAccess
     14               TestWebKitAPI.IPCTestingAPI.CanInterceptFindString
     15
     16        * Platform/IPC/Connection.cpp:
     17        (IPC::Connection::sendMessage): Added the code to invoke MessageObserver::willSendMessage.
     18        Also remove any stale MessageObserver as neded.
     19        (IPC::Connection::addMessageObserver): Added.
     20        (IPC::Connection::dispatchMessage): Added the code to invoke MessageObserver::didReceiveMessage.
     21        Also remove any stale MessageObserver as neded.
     22        * Platform/IPC/Connection.h:
     23        (IPC::Connection::MessageObserver): Added. A pure virtual interface for observing IPC messages.
     24        * Platform/IPC/JSIPCBinding.h:
     25        (IPC::jsValueForDecodedStringArgumentValue): Extracted from jsValueForDecodedArgumentValue<String>.
     26        Now takes the type name as an argument.
     27        (IPC::jsValueForDecodedArgumentValue<URL>): Use "URL" as the type name.
     28        (IPC::jsValueForDecodedArgumentValue<RegistrableDomain>): Use "RegistrableDomain" as the type name.
     29        (IPC::jsValueForDecodedArgumentValue<OptionSet<U>>): Added. Specializations for OptionSet<U>
     30        * WebProcess/WebPage/IPCTestingAPI.cpp:
     31        (WebKit::IPCTestingAPI::JSMessageListener): Added. Implements IPC::MessageObserver.
     32        (WebKit::IPCTestingAPI::JSIPC::staticFunctions):
     33        (WebKit::IPCTestingAPI::createTypeError): Moved.
     34        (WebKit::IPCTestingAPI::JSIPC::addMessageListener): Added.
     35        (WebKit::IPCTestingAPI::JSIPC::addIncomingMessageListener): Added.
     36        (WebKit::IPCTestingAPI::JSIPC::addOutgoingMessageListener): Added.
     37        (WebKit::IPCTestingAPI::JSMessageListener::JSMessageListener): Added.
     38        (WebKit::IPCTestingAPI::JSMessageListener::didReceiveMessage): Added.
     39        (WebKit::IPCTestingAPI::JSMessageListener::willSendMessage): Added.
     40        (WebKit::IPCTestingAPI::JSMessageListener::jsDescriptionFromDecoder): Added.
     41
    1422020-10-21  Aditya Keerthi  <akeerthi@apple.com>
    243
  • trunk/Source/WebKit/Platform/IPC/Connection.cpp

    r268504 r268848  
    454454        return false;
    455455
     456#if ENABLE(IPC_TESTING_API)
     457    if (isMainThread()) {
     458        bool hasDeadObservers = false;
     459        for (auto& observerWeakPtr : m_messageObservers) {
     460            if (auto* observer = observerWeakPtr.get())
     461                observer->willSendMessage(*encoder, sendOptions);
     462            else
     463                hasDeadObservers = true;
     464        }
     465        if (hasDeadObservers)
     466            m_messageObservers.removeAllMatching([](auto& observer) { return !observer; });
     467    }
     468#endif
     469
    456470    if (isMainThread() && m_inDispatchMessageMarkedToUseFullySynchronousModeForTesting && !encoder->isSyncMessage() && !(encoder->messageReceiverName() == ReceiverName::IPC) && !sendOptions.contains(SendOption::IgnoreFullySynchronousMode)) {
    457471        uint64_t syncRequestID;
     
    468482    else if (sendOptions.contains(SendOption::DispatchMessageEvenWhenWaitingForUnboundedSyncReply))
    469483        encoder->setShouldDispatchMessageWhenWaitingForSyncReply(ShouldDispatchWhenWaitingForSyncReply::YesDuringUnboundedIPC);
     484
     485#if ENABLE(IPC_TESTING_API)
     486#endif
    470487
    471488    {
     
    823840}
    824841
     842#if ENABLE(IPC_TESTING_API)
     843void Connection::addMessageObserver(const MessageObserver& observer)
     844{
     845    m_messageObservers.append(makeWeakPtr(observer));
     846}
     847#endif
     848
    825849void Connection::postConnectionDidCloseOnConnectionWorkQueue()
    826850{
     
    10071031        return;
    10081032    }
     1033
     1034#if ENABLE(IPC_TESTING_API)
     1035    if (isMainThread()) {
     1036        bool hasDeadObservers = false;
     1037        for (auto& observerWeakPtr : m_messageObservers) {
     1038            if (auto* observer = observerWeakPtr.get())
     1039                observer->didReceiveMessage(decoder);
     1040            else
     1041                hasDeadObservers = true;
     1042        }
     1043        if (hasDeadObservers)
     1044            m_messageObservers.removeAllMatching([](auto& observer) { return !observer; });
     1045    }
     1046#endif
    10091047
    10101048    m_client.didReceiveMessage(*this, decoder);
  • trunk/Source/WebKit/Platform/IPC/Connection.h

    r268690 r268848  
    140140    };
    141141
     142#if ENABLE(IPC_TESTING_API)
     143    class MessageObserver : public CanMakeWeakPtr<MessageObserver> {
     144    public:
     145        virtual ~MessageObserver() = default;
     146        virtual void willSendMessage(const Encoder&, OptionSet<SendOption>) = 0;
     147        virtual void didReceiveMessage(const Decoder&) = 0;
     148    };
     149#endif
     150
    142151#if USE(UNIX_DOMAIN_SOCKETS)
    143152    typedef int Identifier;
     
    289298
    290299#if ENABLE(IPC_TESTING_API)
     300    void addMessageObserver(const MessageObserver&);
     301
    291302    void setIgnoreInvalidMessageForTesting() { m_ignoreInvalidMessageForTesting = true; }
    292303    bool ignoreInvalidMessageForTesting() const { return m_ignoreInvalidMessageForTesting; }
     
    419430
    420431#if ENABLE(IPC_TESTING_API)
     432    Vector<WeakPtr<MessageObserver>> m_messageObservers;
    421433    bool m_ignoreInvalidMessageForTesting { false };
    422434#endif
  • trunk/Source/WebKit/Platform/IPC/JSIPCBinding.h

    r268503 r268848  
    3939#include <wtf/text/WTFString.h>
    4040
     41namespace IPC {
     42
    4143template<typename T, std::enable_if_t<!std::is_arithmetic<T>::value && !std::is_enum<T>::value>* = nullptr>
    4244JSC::JSValue jsValueForDecodedArgumentValue(JSC::JSGlobalObject*, const T&)
     
    4547}
    4648
     49inline JSC::JSValue jsValueForDecodedStringArgumentValue(JSC::JSGlobalObject* globalObject, const String& value, ASCIILiteral type)
     50{
     51    auto& vm = globalObject->vm();
     52    auto scope = DECLARE_THROW_SCOPE(vm);
     53    auto* object = JSC::constructEmptyObject(globalObject, globalObject->objectPrototype());
     54    RETURN_IF_EXCEPTION(scope, JSC::JSValue());
     55    object->putDirect(vm, JSC::Identifier::fromString(vm, "type"_s), JSC::jsNontrivialString(vm, type));
     56    RETURN_IF_EXCEPTION(scope, JSC::JSValue());
     57    object->putDirect(vm, JSC::Identifier::fromString(vm, "value"_s), value.isNull() ? JSC::jsNull() : JSC::jsString(vm, value));
     58    RETURN_IF_EXCEPTION(scope, JSC::JSValue());
     59    return object;
     60}
     61
    4762template<>
    4863JSC::JSValue jsValueForDecodedArgumentValue(JSC::JSGlobalObject* globalObject, const String& value)
    4964{
    50     auto& vm = globalObject->vm();
    51     auto scope = DECLARE_THROW_SCOPE(vm);
    52     auto* object = JSC::constructEmptyObject(globalObject, globalObject->objectPrototype());
    53     RETURN_IF_EXCEPTION(scope, JSC::JSValue());
    54     object->putDirect(vm, JSC::Identifier::fromString(vm, "type"_s), JSC::jsNontrivialString(vm, "String"_s));
    55     RETURN_IF_EXCEPTION(scope, JSC::JSValue());
    56     object->putDirect(vm, JSC::Identifier::fromString(vm, "value"_s), JSC::jsNontrivialString(vm, value));
    57     RETURN_IF_EXCEPTION(scope, JSC::JSValue());
    58     return object;
     65    return jsValueForDecodedStringArgumentValue(globalObject, value, "String"_s);
    5966}
    6067
     
    6269JSC::JSValue jsValueForDecodedArgumentValue(JSC::JSGlobalObject* globalObject, const URL& value)
    6370{
    64     return jsValueForDecodedArgumentValue(globalObject, value.string());
     71    return jsValueForDecodedStringArgumentValue(globalObject, value.string(), "URL"_s);
    6572}
    6673
     
    6875JSC::JSValue jsValueForDecodedArgumentValue(JSC::JSGlobalObject* globalObject, const WebCore::RegistrableDomain& value)
    6976{
    70     return jsValueForDecodedArgumentValue(globalObject, value.string());
     77    return jsValueForDecodedStringArgumentValue(globalObject, value.string(), "RegistrableDomain"_s);
    7178}
    7279
     
    206213{
    207214    return jsValueForDecodedArgumentRect(globalObject, value, "FloatRect");
     215}
     216
     217template<typename U>
     218JSC::JSValue jsValueForDecodedArgumentValue(JSC::JSGlobalObject* globalObject, const OptionSet<U>& value)
     219{   
     220    auto& vm = globalObject->vm();
     221    auto scope = DECLARE_THROW_SCOPE(vm);
     222    auto result = jsValueForDecodedArgumentValue(globalObject, value.toRaw());
     223    RETURN_IF_EXCEPTION(scope, JSC::JSValue());
     224    result.getObject()->putDirect(vm, JSC::Identifier::fromString(vm, "isOptionSet"_s), JSC::jsBoolean(true));
     225    RETURN_IF_EXCEPTION(scope, JSC::JSValue());
     226    return result;
    208227}
    209228
     
    254273    return jsValueForArgumentTuple(globalObject, *arguments);
    255274}
     275
     276}
  • trunk/Source/WebKit/WebProcess/WebPage/IPCTestingAPI.cpp

    r268633 r268848  
    5555namespace IPCTestingAPI {
    5656
    57 class JSIPC : public RefCounted<JSIPC> {
     57class JSIPC;
     58
     59class JSMessageListener final : public IPC::Connection::MessageObserver {
     60    WTF_MAKE_FAST_ALLOCATED;
     61public:
     62    enum class Type { Incoming, Outgoing };
     63
     64    JSMessageListener(JSIPC&, Type, JSContextRef, JSObjectRef callback);
     65
     66private:
     67    void willSendMessage(const IPC::Encoder&, OptionSet<IPC::SendOption>) override;
     68    void didReceiveMessage(const IPC::Decoder&) override;
     69    JSC::JSObject* jsDescriptionFromDecoder(JSC::JSGlobalObject*, IPC::Decoder&);
     70
     71    WeakPtr<JSIPC> m_jsIPC;
     72    Type m_type;
     73    JSContextRef m_context;
     74    JSObjectRef m_callback;
     75};
     76
     77class JSIPC : public RefCounted<JSIPC>, public CanMakeWeakPtr<JSIPC> {
    5878public:
    5979    static Ref<JSIPC> create(WebPage& webPage, WebFrame& webFrame)
     
    7999    static const JSStaticValue* staticValues();
    80100
     101    static void addMessageListener(JSMessageListener::Type, JSContextRef, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception);
     102    static JSValueRef addIncomingMessageListener(JSContextRef, JSObjectRef, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception);
     103    static JSValueRef addOutgoingMessageListener(JSContextRef, JSObjectRef, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception);
     104
    81105    static JSValueRef sendMessage(JSContextRef, JSObjectRef, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception);
    82106    static JSValueRef sendSyncMessage(JSContextRef, JSObjectRef, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception);
     
    93117    WeakPtr<WebPage> m_webPage;
    94118    WeakPtr<WebFrame> m_webFrame;
     119    Vector<UniqueRef<JSMessageListener>> m_messageListeners;
    95120};
    96121
     
    136161{
    137162    static const JSStaticFunction functions[] = {
     163        { "addIncomingMessageListener", addIncomingMessageListener, kJSPropertyAttributeDontDelete | kJSPropertyAttributeReadOnly },
     164        { "addOutgoingMessageListener", addOutgoingMessageListener, kJSPropertyAttributeDontDelete | kJSPropertyAttributeReadOnly },
    138165        { "sendMessage", sendMessage, kJSPropertyAttributeDontDelete | kJSPropertyAttributeReadOnly },
    139166        { "sendSyncMessage", sendSyncMessage, kJSPropertyAttributeDontDelete | kJSPropertyAttributeReadOnly },
     
    170197}
    171198
     199static JSValueRef createTypeError(JSContextRef context, const String& message)
     200{
     201    JSC::JSLockHolder lock(toJS(context)->vm());
     202    return toRef(JSC::createTypeError(toJS(context), message));
     203}
     204
    172205static RefPtr<IPC::Connection> processTargetFromArgument(JSC::JSGlobalObject* globalObject, JSValueRef valueRef, JSValueRef* exception)
    173206{
     
    190223}
    191224
     225void JSIPC::addMessageListener(JSMessageListener::Type type, JSContextRef context, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
     226{
     227    auto* globalObject = toJS(context);
     228    JSC::JSLockHolder lock(globalObject->vm());
     229    auto jsIPC = makeRefPtr(toWrapped(context, thisObject));
     230    if (!jsIPC) {
     231        *exception = createTypeError(context, "Wrong type"_s);
     232        return;
     233    }
     234
     235    if (argumentCount < 1) {
     236        *exception = createTypeError(context, "Must specify the target process as the first argument"_s);
     237        return;
     238    }
     239
     240    auto connection = processTargetFromArgument(globalObject, arguments[0], exception);
     241    if (!connection)
     242        return;
     243
     244    std::unique_ptr<JSMessageListener> listener;
     245    if (argumentCount >= 2 && JSValueIsObject(context, arguments[1])) {
     246        auto listenerObjectRef = JSValueToObject(context, arguments[1], exception);
     247        if (JSObjectIsFunction(context, listenerObjectRef))
     248            listener = makeUnique<JSMessageListener>(*jsIPC, type, context, listenerObjectRef);
     249    }
     250
     251    if (!listener) {
     252        *exception = createTypeError(context, "Must specify a callback function as the second argument"_s);
     253        return;
     254    }
     255
     256    connection->addMessageObserver(*listener);
     257    jsIPC->m_messageListeners.append(makeUniqueRefFromNonNullUniquePtr(WTFMove(listener)));
     258}
     259
     260JSValueRef JSIPC::addIncomingMessageListener(JSContextRef context, JSObjectRef, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
     261{
     262    addMessageListener(JSMessageListener::Type::Incoming, context, thisObject, argumentCount, arguments, exception);
     263    return JSValueMakeUndefined(context);
     264}
     265
     266JSValueRef JSIPC::addOutgoingMessageListener(JSContextRef context, JSObjectRef, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
     267{
     268    addMessageListener(JSMessageListener::Type::Outgoing, context, thisObject, argumentCount, arguments, exception);
     269    return JSValueMakeUndefined(context);
     270}
     271
    192272static Optional<uint64_t> destinationIDFromArgument(JSC::JSGlobalObject* globalObject, JSValueRef valueRef, JSValueRef* exception)
    193273{
     
    234314    encoder.encodeFixedLengthData(reinterpret_cast<const uint8_t*>(buffer), bufferSize, 1);
    235315    return true;
    236 }
    237 
    238 static JSValueRef createTypeError(JSContextRef context, const String& message)
    239 {
    240     JSC::JSLockHolder lock(toJS(context)->vm());
    241     return toRef(JSC::createTypeError(toJS(context), message));
    242316}
    243317
     
    805879}
    806880
     881JSMessageListener::JSMessageListener(JSIPC& jsIPC, Type type, JSContextRef context, JSObjectRef callback)
     882    : m_jsIPC(makeWeakPtr(jsIPC))
     883    , m_type(type)
     884    , m_context(context)
     885    , m_callback(callback)
     886{
     887    auto* globalObject = toJS(context);
     888    auto& vm = globalObject->vm();
     889    JSC::JSLockHolder lock(vm);
     890
     891    auto catchScope = DECLARE_CATCH_SCOPE(vm);
     892
     893    // We can't retain the global context here as that would cause a leak
     894    // since this object is supposed to live as long as the global object is alive.
     895    JSC::PrivateName uniquePrivateName;
     896    globalObject->putDirect(vm, uniquePrivateName, toJS(globalObject, callback));
     897}
     898
     899void JSMessageListener::didReceiveMessage(const IPC::Decoder& decoder)
     900{
     901    if (m_type != Type::Incoming)
     902        return;
     903
     904    RELEASE_ASSERT(m_jsIPC);
     905    auto protectOwnerOfThis = makeRef(*m_jsIPC);
     906    auto* globalObject = toJS(m_context);
     907    JSC::JSLockHolder lock(globalObject->vm());
     908
     909    auto mutableDecoder = IPC::Decoder::create(decoder.buffer(), decoder.length(), nullptr, { });
     910    auto* description = jsDescriptionFromDecoder(globalObject, *mutableDecoder);
     911
     912    JSValueRef arguments[] = { description ? toRef(globalObject, description) : JSValueMakeUndefined(m_context) };
     913    JSObjectCallAsFunction(m_context, m_callback, m_callback, std::size(arguments), arguments, nullptr);
     914}
     915
     916void JSMessageListener::willSendMessage(const IPC::Encoder& encoder, OptionSet<IPC::SendOption>)
     917{
     918    if (m_type != Type::Outgoing)
     919        return;
     920
     921    RELEASE_ASSERT(m_jsIPC);
     922    auto protectOwnerOfThis = makeRef(*m_jsIPC);
     923    auto* globalObject = toJS(m_context);
     924    JSC::JSLockHolder lock(globalObject->vm());
     925
     926    auto decoder = IPC::Decoder::create(encoder.buffer(), encoder.bufferSize(), nullptr, { });
     927    auto* description = jsDescriptionFromDecoder(globalObject, *decoder);
     928
     929    JSValueRef arguments[] = { description ? toRef(globalObject, description) : JSValueMakeUndefined(m_context) };
     930    JSObjectCallAsFunction(m_context, m_callback, m_callback, WTF_ARRAY_LENGTH(arguments), arguments, nullptr);
     931}
     932
     933JSC::JSObject* JSMessageListener::jsDescriptionFromDecoder(JSC::JSGlobalObject* globalObject, IPC::Decoder& decoder)
     934{
     935    auto& vm = globalObject->vm();
     936    auto scope = DECLARE_CATCH_SCOPE(vm);
     937
     938    auto* jsResult = constructEmptyObject(globalObject, globalObject->objectPrototype());
     939    RETURN_IF_EXCEPTION(scope, nullptr);
     940
     941    jsResult->putDirect(vm, JSC::Identifier::fromString(vm, "name"), JSC::JSValue(static_cast<unsigned>(decoder.messageName())));
     942    RETURN_IF_EXCEPTION(scope, nullptr);
     943
     944    jsResult->putDirect(vm, JSC::Identifier::fromString(vm, "description"), JSC::jsString(vm, IPC::description(decoder.messageName())));
     945    RETURN_IF_EXCEPTION(scope, nullptr);
     946
     947    jsResult->putDirect(vm, JSC::Identifier::fromString(vm, "destinationID"), JSC::JSValue(decoder.destinationID()));
     948    RETURN_IF_EXCEPTION(scope, nullptr);
     949
     950    if (decoder.isSyncMessage()) {
     951        if (uint64_t syncRequestID = 0; decoder.decode(syncRequestID)) {
     952            jsResult->putDirect(vm, JSC::Identifier::fromString(vm, "syncRequestID"), JSC::JSValue(syncRequestID));
     953            RETURN_IF_EXCEPTION(scope, nullptr);
     954        }
     955    } else if (messageReplyArgumentDescriptions(decoder.messageName())) {
     956        if (uint64_t listenerID = 0; decoder.decode(listenerID)) {
     957            jsResult->putDirect(vm, JSC::Identifier::fromString(vm, "listenerID"), JSC::JSValue(listenerID));
     958            RETURN_IF_EXCEPTION(scope, nullptr);
     959        }
     960    }
     961
     962    auto arrayBuffer = JSC::ArrayBuffer::create(decoder.buffer(), decoder.length());
     963    if (auto* structure = globalObject->arrayBufferStructure(arrayBuffer->sharingMode())) {
     964        if (auto* jsArrayBuffer = JSC::JSArrayBuffer::create(vm, structure, WTFMove(arrayBuffer))) {
     965            jsResult->putDirect(vm, JSC::Identifier::fromString(vm, "buffer"), jsArrayBuffer);
     966            RETURN_IF_EXCEPTION(scope, nullptr);
     967        }
     968    }
     969
     970    auto jsReplyArguments = jsValueForArguments(globalObject, decoder.messageName(), decoder);
     971    if (jsReplyArguments) {
     972        jsResult->putDirect(vm, vm.propertyNames->arguments, jsReplyArguments->isEmpty() ? JSC::jsNull() : *jsReplyArguments);
     973        RETURN_IF_EXCEPTION(scope, nullptr);
     974    }
     975
     976    return jsResult;
     977}
     978
    807979void inject(WebPage& webPage, WebFrame& webFrame, WebCore::DOMWrapperWorld& world)
    808980{
  • trunk/Tools/ChangeLog

    r268844 r268848  
     12020-10-21  Ryosuke Niwa  <rniwa@webkit.org>
     2
     3        IPC testing API should have the capability to observe messages being sent and received
     4        https://bugs.webkit.org/show_bug.cgi?id=217870
     5
     6        Reviewed by Darin Adler.
     7
     8        Added tests to intercept IPC messages sent and received by WebContent process.
     9
     10        * TestWebKitAPI/Tests/WebKitCocoa/IPCTestingAPI.mm:
     11        (IPCTestingAPI.CanInterceptAlert):
     12        (IPCTestingAPI.CanInterceptHasStorageAccess):
     13        (IPCTestingAPI.CanInterceptFindString):
     14
    1152020-10-21  Jonathan Bedard  <jbedard@apple.com>
    216
  • trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/IPCTestingAPI.mm

    r268633 r268848  
    209209}
    210210
     211TEST(IPCTestingAPI, CanInterceptAlert)
     212{
     213    auto webView = createWebViewWithIPCTestingAPI();
     214
     215    auto delegate = adoptNS([[IPCTestingAPIDelegate alloc] init]);
     216    [webView setUIDelegate:delegate.get()];
     217
     218    done = false;
     219    [webView synchronouslyLoadHTMLString:@"<!DOCTYPE html><script>messages = []; IPC.addOutgoingMessageListener('UI', (message) => messages.push(message)); alert('ok');</script>"];
     220    TestWebKitAPI::Util::run(&done);
     221
     222    EXPECT_STREQ([alertMessage UTF8String], "ok");
     223    EXPECT_STREQ([webView stringByEvaluatingJavaScript:@"messages = messages.filter((message) => message.name == IPC.messages.WebPageProxy_RunJavaScriptAlert.name); messages.length"].UTF8String, "1");
     224    EXPECT_STREQ([webView stringByEvaluatingJavaScript:@"messages[0].description"].UTF8String, "WebPageProxy_RunJavaScriptAlert");
     225    EXPECT_EQ([webView stringByEvaluatingJavaScript:@"messages[0].arguments.length"].intValue, 3);
     226    EXPECT_STREQ([webView stringByEvaluatingJavaScript:@"typeof(messages[0].syncRequestID)"].UTF8String, "number");
     227    EXPECT_EQ([webView stringByEvaluatingJavaScript:@"messages[0].destinationID"].intValue,
     228        [webView stringByEvaluatingJavaScript:@"IPC.webPageProxyID.toString()"].intValue);
     229}
     230
     231#if ENABLE(RESOURCE_LOAD_STATISTICS)
     232TEST(IPCTestingAPI, CanInterceptHasStorageAccess)
     233{
     234    auto webView = createWebViewWithIPCTestingAPI();
     235
     236    auto delegate = adoptNS([[IPCTestingAPIDelegate alloc] init]);
     237    [webView setUIDelegate:delegate.get()];
     238
     239    done = false;
     240    promptResult = @"foo";
     241    [webView synchronouslyLoadHTMLString:@"<!DOCTYPE html><script>let targetMessage = {}; const messageName = IPC.messages.NetworkConnectionToWebProcess_HasStorageAccess.name;"
     242        "IPC.addOutgoingMessageListener('Networking', (currentMessage) => { if (currentMessage.name == messageName) targetMessage = currentMessage; });"
     243        "IPC.sendMessage('Networking', 0, messageName, [{type: 'RegistrableDomain', value: 'https://ipctestingapi.com'}, {type: 'RegistrableDomain', value: 'https://webkit.org'},"
     244        "{type: 'uint64_t', value: IPC.frameID}, {type: 'uint64_t', value: IPC.pageID}]).then((result) => alert(JSON.stringify(result.arguments)));</script>"];
     245    TestWebKitAPI::Util::run(&done);
     246
     247    EXPECT_STREQ([alertMessage UTF8String], "[{\"type\":\"bool\",\"value\":false}]");
     248    EXPECT_STREQ([webView stringByEvaluatingJavaScript:@"targetMessage.description"].UTF8String, "NetworkConnectionToWebProcess_HasStorageAccess");
     249    EXPECT_EQ([webView stringByEvaluatingJavaScript:@"targetMessage.arguments.length"].intValue, 4);
     250    EXPECT_STREQ([webView stringByEvaluatingJavaScript:@"targetMessage.arguments[0].type"].UTF8String, "RegistrableDomain");
     251    EXPECT_STREQ([webView stringByEvaluatingJavaScript:@"targetMessage.arguments[0].value"].UTF8String, "ipctestingapi.com");
     252    EXPECT_STREQ([webView stringByEvaluatingJavaScript:@"targetMessage.arguments[1].type"].UTF8String, "RegistrableDomain");
     253    EXPECT_STREQ([webView stringByEvaluatingJavaScript:@"targetMessage.arguments[1].value"].UTF8String, "webkit.org");
     254    EXPECT_STREQ([webView stringByEvaluatingJavaScript:@"targetMessage.arguments[2].type"].UTF8String, "uint64_t");
     255    EXPECT_STREQ([webView stringByEvaluatingJavaScript:@"targetMessage.arguments[2].value"].UTF8String, [webView stringByEvaluatingJavaScript:@"IPC.frameID.toString()"].UTF8String);
     256    EXPECT_STREQ([webView stringByEvaluatingJavaScript:@"targetMessage.arguments[3].type"].UTF8String, "uint64_t");
     257    EXPECT_EQ([webView stringByEvaluatingJavaScript:@"targetMessage.arguments[3].value"].intValue, [webView stringByEvaluatingJavaScript:@"IPC.pageID.toString()"].intValue);
     258    EXPECT_STREQ([webView stringByEvaluatingJavaScript:@"typeof(targetMessage.syncRequestID)"].UTF8String, "undefined");
     259    EXPECT_EQ([webView stringByEvaluatingJavaScript:@"targetMessage.destinationID"].intValue, 0);
     260}
    211261#endif
     262
     263TEST(IPCTestingAPI, CanInterceptFindString)
     264{
     265    auto webView = createWebViewWithIPCTestingAPI();
     266
     267    auto delegate = adoptNS([[IPCTestingAPIDelegate alloc] init]);
     268    [webView setUIDelegate:delegate.get()];
     269
     270    [webView synchronouslyLoadHTMLString:@"<!DOCTYPE html><body><p>hello</p><script>messages = []; IPC.addIncomingMessageListener('UI', (message) => messages.push(message));</script>"];
     271
     272    done = false;
     273    auto findConfiguration = adoptNS([[WKFindConfiguration alloc] init]);
     274    [webView findString:@"hello" withConfiguration:findConfiguration.get() completionHandler:^(WKFindResult *result) {
     275        EXPECT_TRUE(result.matchFound);
     276        EXPECT_TRUE([webView selectionRangeHasStartOffset:0 endOffset:5]);
     277        done = true;
     278    }];
     279    TestWebKitAPI::Util::run(&done);
     280
     281    EXPECT_STREQ([webView stringByEvaluatingJavaScript:@"messages = messages.filter((message) => message.name == IPC.messages.WebPage_FindString.name); messages.length"].UTF8String, "1");
     282    EXPECT_STREQ([webView stringByEvaluatingJavaScript:@"messages[0].description"].UTF8String, "WebPage_FindString");
     283    EXPECT_EQ([webView stringByEvaluatingJavaScript:@"args = messages[0].arguments; args.length"].intValue, 3);
     284    EXPECT_STREQ([webView stringByEvaluatingJavaScript:@"args[0].type"].UTF8String, "String");
     285    EXPECT_STREQ([webView stringByEvaluatingJavaScript:@"args[0].value"].UTF8String, "hello");
     286    EXPECT_STREQ([webView stringByEvaluatingJavaScript:@"args[1].type"].UTF8String, "uint16_t");
     287    EXPECT_EQ([webView stringByEvaluatingJavaScript:@"args[1].value"].intValue, 0x11);
     288    EXPECT_EQ([webView stringByEvaluatingJavaScript:@"args[1].isOptionSet"].boolValue, YES);
     289    EXPECT_STREQ([webView stringByEvaluatingJavaScript:@"args[2].type"].UTF8String, "uint32_t");
     290    EXPECT_EQ([webView stringByEvaluatingJavaScript:@"args[2].value"].intValue, 1);
     291    EXPECT_STREQ([webView stringByEvaluatingJavaScript:@"typeof(messages[0].syncRequestID)"].UTF8String, "undefined");
     292    EXPECT_EQ([webView stringByEvaluatingJavaScript:@"messages[0].destinationID"].intValue,
     293        [webView stringByEvaluatingJavaScript:@"IPC.webPageProxyID.toString()"].intValue);
     294}
     295
     296#endif
Note: See TracChangeset for help on using the changeset viewer.