Bug 217835

Summary: Web Inspector: move InspectorFrontendAPIDispatcher to WebCore, clean up uses
Product: WebKit Reporter: Blaze Burg <bburg>
Component: Web InspectorAssignee: Blaze Burg <bburg>
Status: RESOLVED FIXED    
Severity: Normal CC: annulen, bburg, ews-watchlist, gyuyoung.kim, hi, inspector-bugzilla-changes, joepeck, ryuan.choi, sergio, webkit-bug-importer
Priority: P2 Keywords: InRadar
Version: WebKit Nightly Build   
Hardware: All   
OS: All   
Attachments:
Description Flags
Patch v1
none
Patch (with debug logging)
none
InspectorFrontendAPIDispatcher protocol logging w/ backtrace
none
Patch v2.1 (for EWS)
none
Patch v2.2 - fix some protocol test bugs
none
Patch v2.3 - for EWS
none
Patch v2.4 - for EWS
none
Patch v2.5 - for EWS
none
Patch v2.6 - for EWS none

Description Blaze Burg 2020-10-16 11:05:31 PDT
Splitting this off from 217783.
Comment 1 Radar WebKit Bug Importer 2020-10-16 11:05:43 PDT
<rdar://problem/70384407>
Comment 2 Blaze Burg 2020-10-16 14:03:20 PDT
Created attachment 411613 [details]
Patch v1

This will probably take some iteration with EWS for non-Cocoa ports.
Comment 3 Devin Rousso 2020-10-16 14:59:06 PDT
Comment on attachment 411613 [details]
Patch v1

View in context: https://bugs.webkit.org/attachment.cgi?id=411613&action=review

r=me this looks fine to me, so I'm gonna preemptively r+ on the assumption that you'll fix build/test failures :)

please let me know if anything significant changes while doing so and I can take another look

> Source/WebCore/inspector/InspectorFrontendAPIDispatcher.h:45
> +    void dispatchCommand(const String& command, Vector<Ref<JSON::Value>>&& arguments);

How would you feel about using a different name?  Maybe something like `invoke` or `evaluate`?  Using "command" makes me thing this is related to protocol commands (which I think go through `dispatchMessageAsync`).

> Source/WebCore/inspector/InspectorFrontendClientLocal.cpp:396
>  bool InspectorFrontendClientLocal::evaluateAsBoolean(const String& expression)

Why not also move this to `InspectorFrontendAPIDispatcher::evaluateAsBoolean` (or with a different/better name)?

> Source/WebCore/inspector/InspectorFrontendClientLocal.cpp:-433
> -    JSC::SuspendExceptionScope scope(&m_frontendPage->inspectorController().vm());

Do we still need this somewhere?

> Source/WebKit/WebProcess/Inspector/RemoteWebInspectorUI.cpp:75
>      StringBuilder builder;
>      JSON::Value::escapeString(builder, findString);

I think we can drop this now because you added a `writeJSON` call, which calls `escapeString`.

Actually, keeping this may result in over-escaping 😳

> Source/WebKit/WebProcess/Inspector/RemoteWebInspectorUI.h:95
> +    WebCore::InspectorFrontendAPIDispatcher& frontendAPIDispatcher() final { return m_frontendAPIDispatcher; }

NIT: the `final` isn't necessary since this class is already `final`

> Source/WebKit/WebProcess/Inspector/WebInspectorUI.cpp:247
> +    m_frontendAPIDispatcher.dispatchCommand("setDockSide"_s, { JSON::Value::create(String(dockSideString)) });

NIT: is the `String` necessary?

> Source/WebKit/WebProcess/Inspector/WebInspectorUI.cpp:267
>      StringBuilder builder;
>      JSON::Value::escapeString(builder, findString);

ditto (RemoteWebInspectorUI.cpp:74)

> Source/WebKit/WebProcess/Inspector/WebInspectorUI.cpp:334
> +    m_frontendAPIDispatcher.dispatchCommand("showConsole"_s, { });

you can add `= { }` to the declaration of `dispatchCommand` to avoid having to do this at callsites
Comment 4 Joseph Pecoraro 2020-10-19 14:59:52 PDT
Comment on attachment 411613 [details]
Patch v1

View in context: https://bugs.webkit.org/attachment.cgi?id=411613&action=review

> Source/WebCore/inspector/InspectorFrontendAPIDispatcher.cpp:78
> +    expression.appendLiteral("\"");

I'd love to see this just be `append('"')`!

> Source/WebCore/inspector/InspectorFrontendAPIDispatcher.cpp:91
> +    evaluateOrQueueExpression(makeString("InspectorFrontendAPI.dispatchMessageAsync(", message, ")"));

Likewise ")" => ')'
Comment 5 Blaze Burg 2020-10-22 14:40:18 PDT
Created attachment 412135 [details]
Patch (with debug logging)
Comment 6 Blaze Burg 2020-10-28 16:32:57 PDT
Upon investigation, some evaluations are getting stuck because we suspend as the inspected page (i.e, also the inspector frontend page) pauses.

Prior to this patch, pagePaused() did not call suspend(), so I think for WK1 purposes we have to be okay with evaluating message dispatches in the frontend page while it is paused.

This seems gross and we should really get rid of this in-process stuff altogether.
Comment 7 Blaze Burg 2020-10-28 16:33:59 PDT
Created attachment 412587 [details]
InspectorFrontendAPIDispatcher protocol logging w/ backtrace
Comment 8 Blaze Burg 2020-10-29 11:17:21 PDT
(In reply to Brian Burg from comment #6)
> Upon investigation, some evaluations are getting stuck because we suspend as
> the inspected page (i.e, also the inspector frontend page) pauses.
> 
> Prior to this patch, pagePaused() did not call suspend(), so I think for WK1
> purposes we have to be okay with evaluating message dispatches in the
> frontend page while it is paused.
> 
> This seems gross and we should really get rid of this in-process stuff
> altogether.

I fixed the suspend/unsuspend behavior and related assertions. It still seems like the frontend is getting hung / evaluations aren't happening:

Test code:

 InspectorProtocol.sendCommand("Runtime.evaluate", {expression: "this.should.trigger.an.exception"}, function(messageObject) { ...

Logging:

PASS: Paused in debugger: reason = "Breakpoint"

BJ>> Okay, it got to onBreakpointHit and its sending the Runtime.evaluate command on the bad expression

frontend: {"method":"Runtime.evaluate","params":{"expression":"TestPage.log(unescape(\"PASS%3A%20Paused%20in%20debugger%3A%20reason%20%3D%20%22Breakpoint%22\"));"},"id":6}
frontend: {"method":"Runtime.evaluate","params":{"expression":"this.should.trigger.an.exception"},"id":7}
FrontendAPIDispatcher[0x351c7fa78, queue depth=0] evaluating: InspectorFrontendAPI.dispatchMessageAsync({"method":"Debugger.scriptParsed","params":{"scriptId":"9","url":"","startLine":0,"startColumn":0,"endLine":0,"endColumn":96,"isContentScript":false,"module":false}})

BJ>> Hmm, we should see 'backend: <the same message>' being logged for every InspectorFrontendAPI.dispatchMessageAsync, but from here onwards I don't see it:

FrontendAPIDispatcher[0x351c7fa78, queue depth=0] evaluating: InspectorFrontendAPI.dispatchMessageAsync({"result":{"result":{"type":"undefined"},"wasThrown":false},"id":6})
FrontendAPIDispatcher[0x351c7fa78, queue depth=0] evaluating: InspectorFrontendAPI.dispatchMessageAsync({"method":"Debugger.scriptParsed","params":{"scriptId":"10","url":"","startLine":0,"startColumn":0,"endLine":0,"endColumn":32,"isContentScript":false,"module":false}})
FrontendAPIDispatcher[0x351c7fa78, queue depth=0] evaluating: InspectorFrontendAPI.dispatchMessageAsync({"result":{"result":{"type":"object","objectId":"{\"injectedScriptId\":1,\"id\":65}","subtype":"error","className":"TypeError","description":"TypeError: undefined is not an object (evaluating 'this.should.trigger')"},"wasThrown":true},"id":7})

BJ>> At this point, three messages have been 'evaluated' with nothing actually happening. Hmm.. Maybe I messed up the evaluation worlds?
Comment 9 Blaze Burg 2020-10-29 12:26:56 PDT
Created attachment 412672 [details]
Patch v2.1 (for EWS)
Comment 10 Devin Rousso 2020-10-29 14:36:48 PDT
Comment on attachment 412672 [details]
Patch v2.1 (for EWS)

View in context: https://bugs.webkit.org/attachment.cgi?id=412672&action=review

> Source/WebKit/WebProcess/Inspector/RemoteWebInspectorUI.cpp:75
> +    m_frontendAPIDispatcher->dispatchCommand("updateFindString"_s, { JSON::Value::create(builder.toString()) });

btw this should use `findString` (kinda shocked that this isn't throwing an error about "unused variable `findString" 🤔) and drop `builder` altogether

> Source/WebKit/WebProcess/Inspector/WebInspectorUI.cpp:267
> +    m_frontendAPIDispatcher->dispatchCommand("updateFindString"_s, { JSON::Value::create(builder.toString()) });

ditto (RemoteWebInspectorUI.cpp:75)
Comment 11 Blaze Burg 2020-10-29 15:27:00 PDT
Created attachment 412692 [details]
Patch v2.2 - fix some protocol test bugs

This patch addresses all known issues, want to see what the bots do.
Comment 12 Blaze Burg 2020-10-29 23:12:31 PDT
Created attachment 412716 [details]
Patch v2.3 - for EWS
Comment 13 Blaze Burg 2020-10-30 10:03:31 PDT
Created attachment 412761 [details]
Patch v2.4 - for EWS
Comment 14 Devin Rousso 2020-10-30 10:59:02 PDT
Comment on attachment 412761 [details]
Patch v2.4 - for EWS

View in context: https://bugs.webkit.org/attachment.cgi?id=412761&action=review

r=me, with two fixes for `updateFindString` (I still don't know how that's not throwing a compiler error because of the unused variable o_O)

> Source/WebCore/inspector/InspectorFrontendAPIDispatcher.cpp:85
> +            if (!protectedThis->isSuspended())
> +                return;

NIT: this check seems unnecessary since it's repeated in `unsuspend`

> Source/WebCore/inspector/InspectorFrontendAPIDispatcher.h:65
> +    EvaluationResult dispatchCommandSync(const String& command, Vector<Ref<JSON::Value>>&& arguments = { });

NIT: wouldn't it be more accurate to call this `dispatchCommandWithResultSync`?

> Source/WebCore/inspector/InspectorFrontendAPIDispatcher.h:66
> +    WEBCORE_EXPORT void dispatchCommandWithResult(const String& command, Vector<Ref<JSON::Value>>&& arguments, EvaluationResultHandler&&);

Seeing as `dispatchCommand` and `dispatchCommandWithResult` basically have the same logic, could we combine them and avoid the repeated logic?
```
    EvaluationResult dispatchCommandWithResultSync(const String& command, Vector<Ref<JSON::Value>>&& arguments = { });
    WEBCORE_EXPORT void dispatchCommandWithResultAsync(const String& command, Vector<Ref<JSON::Value>>&& arguments = { }, EvaluationResultHandler&& = { });
```

> Source/WebCore/inspector/InspectorFrontendAPIDispatcher.h:79
> +    void evaluateOrQueueExpression(const String&, WTF::Optional<EvaluationResultHandler>&& = WTF::nullopt);

it shouldn't be necessary to use `Optional` as `CompletionHandler` already has a way to check "not set"

> Source/WebCore/inspector/InspectorFrontendAPIDispatcher.h:85
> +    Vector<std::pair<String, Optional<EvaluationResultHandler>>> m_queuedEvaluations;

ditto (:79)

> Source/WebCore/inspector/InspectorFrontendClientLocal.cpp:-188
> -    m_frontendLoaded = true;

Don't we still need this?  It's still used later.

> Source/WebCore/inspector/InspectorFrontendClientLocal.cpp:331
> +    auto result = m_frontendAPIDispatcher->dispatchCommandSync("isDebuggingEnabled"_s, { });

NIT: don't need the `{ }`

> Source/WebCore/inspector/InspectorFrontendClientLocal.cpp:348
> +    auto result = m_frontendAPIDispatcher->dispatchCommandSync("isTimelineProfilingEnabled"_s, { });

ditto (:331)

> Source/WebCore/inspector/InspectorFrontendClientLocal.cpp:369
> +    if (!result || !result.value().toBoolean(m_frontendAPIDispatcher->frontendGlobalObject()))
> +        return false;
> +
> +    return true;

NIT: you could simplify this
```
    return result && result.value().toBoolean(m_frontendAPIDispatcher->frontendGlobalObject());
```

> Source/WebKit/WebProcess/Inspector/RemoteWebInspectorUI.cpp:75
> +    m_frontendAPIDispatcher->dispatchCommand("updateFindString"_s, { JSON::Value::create(builder.toString()) });

this should use `findString`, not `builder`

> Source/WebKit/WebProcess/Inspector/WebInspectorUI.cpp:267
> +    m_frontendAPIDispatcher->dispatchCommand("updateFindString"_s, { JSON::Value::create(builder.toString()) });

this should use `findString`, not `builder`

> LayoutTests/inspector/dom/dom-remove-events.html:74
> +            delete InspectorProtocol.eventHandler["DOM.setChildNodes"];

I wonder if we should just change `ProtocolTest.completeTest` to automatically nuke `InspectorProtocol.eventHandler` and dispatch `disable` commands to every domain 🤔
Comment 15 Blaze Burg 2020-10-30 11:43:16 PDT
Comment on attachment 412761 [details]
Patch v2.4 - for EWS

View in context: https://bugs.webkit.org/attachment.cgi?id=412761&action=review

>> Source/WebCore/inspector/InspectorFrontendAPIDispatcher.cpp:85
>> +                return;
> 
> NIT: this check seems unnecessary since it's repeated in `unsuspend`

Ah right. I think it was added when unsuspend() was more strict and asserted a value change.

>> Source/WebCore/inspector/InspectorFrontendAPIDispatcher.h:79
>> +    void evaluateOrQueueExpression(const String&, WTF::Optional<EvaluationResultHandler>&& = WTF::nullopt);
> 
> it shouldn't be necessary to use `Optional` as `CompletionHandler` already has a way to check "not set"

Oh. :til:

>> Source/WebCore/inspector/InspectorFrontendClientLocal.cpp:-188
>> -    m_frontendLoaded = true;
> 
> Don't we still need this?  It's still used later.

It's only used to guard calls to isProfilingEnabled and the like. Since frontendAPIDispatcher will check for m_frontendLoaded and return an error if it's not loaded, I think this can be removed entirely.

>> LayoutTests/inspector/dom/dom-remove-events.html:74
>> +            delete InspectorProtocol.eventHandler["DOM.setChildNodes"];
> 
> I wonder if we should just change `ProtocolTest.completeTest` to automatically nuke `InspectorProtocol.eventHandler` and dispatch `disable` commands to every domain 🤔

It would be better to nuke Protocol tests altogether, but alas, I have no more time to fiddle with old tests.
Comment 16 Blaze Burg 2020-10-30 11:43:57 PDT
Created attachment 412780 [details]
Patch v2.5 - for EWS
Comment 17 Blaze Burg 2020-10-30 13:44:54 PDT
Created attachment 412793 [details]
Patch v2.6 - for EWS
Comment 18 EWS 2020-10-30 16:30:13 PDT
ChangeLog entry in LayoutTests/ChangeLog contains OOPS!.
Comment 19 Devin Rousso 2020-10-30 16:30:38 PDT
Comment on attachment 412793 [details]
Patch v2.6 - for EWS

r=me
Comment 20 EWS 2020-10-30 16:56:30 PDT
Committed r269210: <https://trac.webkit.org/changeset/269210>

All reviewed patches have been landed. Closing bug and clearing flags on attachment 412793 [details].