Bug 202704 - Web Inspector: notify inspector when provisional page is created, committed and destroyed
Summary: Web Inspector: notify inspector when provisional page is created, committed a...
Status: RESOLVED FIXED
Alias: None
Product: WebKit
Classification: Unclassified
Component: Web Inspector (show other bugs)
Version: WebKit Nightly Build
Hardware: All All
: P2 Normal
Assignee: Yury Semikhatsky
URL:
Keywords: InRadar
Depends on: 203262
Blocks: 202219
  Show dependency treegraph
 
Reported: 2019-10-08 13:36 PDT by Yury Semikhatsky
Modified: 2019-11-07 09:53 PST (History)
12 users (show)

See Also:


Attachments
Patch (79.36 KB, patch)
2019-10-08 15:03 PDT, Yury Semikhatsky
no flags Details | Formatted Diff | Diff
Patch (80.23 KB, patch)
2019-10-08 16:57 PDT, Yury Semikhatsky
no flags Details | Formatted Diff | Diff
Patch (82.33 KB, patch)
2019-10-09 11:45 PDT, Yury Semikhatsky
no flags Details | Formatted Diff | Diff
Patch (83.92 KB, patch)
2019-10-14 11:17 PDT, Yury Semikhatsky
no flags Details | Formatted Diff | Diff
Patch (84.77 KB, patch)
2019-10-14 16:25 PDT, Yury Semikhatsky
no flags Details | Formatted Diff | Diff
Patch (85.34 KB, patch)
2019-10-21 11:14 PDT, Yury Semikhatsky
no flags Details | Formatted Diff | Diff
Patch for landing (84.14 KB, patch)
2019-10-23 11:12 PDT, Yury Semikhatsky
no flags Details | Formatted Diff | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Yury Semikhatsky 2019-10-08 13:36:51 PDT
This is first step to attaching to provisional pages early during process swap on navigation (PSON). New (provisional) target should be created for provisional pages so that inspector front-end can start inspecting it earlier.
Comment 1 Yury Semikhatsky 2019-10-08 15:03:50 PDT
Created attachment 380469 [details]
Patch
Comment 2 EWS Watchlist 2019-10-08 15:04:25 PDT
This patch modifies the inspector protocol. Please ensure that any frontend changes appropriately use feature checks for new protocol features.
Comment 3 Yury Semikhatsky 2019-10-08 16:57:45 PDT
Created attachment 380480 [details]
Patch
Comment 4 Yury Semikhatsky 2019-10-09 11:43:17 PDT
View in context: https://bugs.webkit.org/attachment.cgi?id=379967&action=review

>>>>>>> Source/WebKit/WebProcess/WebPage/WebPageInspectorTargetFrontendChannel.h:41
>>>>>>> +    ~WebPageInspectorTargetFrontendChannel() override = default;
>>>>>> 
>>>>>> `virtual ~WebPageInspectorTargetFrontendChannel() = default;`
>>>>> 
>>>>> Why? It overrides virtual destructor in its superclass.
>>>> 
>>>> We have never done this anywhere in WebKit.  It's also a bit weird, because this doesn't "technically" override the destructor, as the super class' destructor will still get called.
>>> 
>>> Well, overriding method in general is free to call super class's implementation I don't see any issues with that.
>>> 
>>> Here is an excerpt from the WebKit style guide (https://webkit.org/code-style-guidelines/#override-methods) which clearly says that overriding methods should be annotated as either override or final:
>>>  "All subclasses of that class must either specify the override keyword when overriding the virtual method or the final keyword when overriding the virtual method and requiring that no further subclasses can override it."
>>> 
>>> I believe the reason there is still a lot of code that violates the rule is that they predate c++11 usage in WebKit and nobody bothered updating them. Maybe worth bringing this up on webkit-dev. Annotating the destructor with override protects against a nasty error when super destructor accidentally not not marked as virtual but the clients try to destroy the object using its superclass interface.
>> 
>> That may be the case, but for now I'd ask you to use `virtual`, as it seems like that's what we use everywhere else in WebKit.  Personally, I would also argue that constructors and destructors are considered a different "type" of method than other member functions, so that affects my stance on this too.  Feel free to broach the subject on webkit-dev.
> 
> Raised this on webkit-dev, let's discuss there and I'll update the patch to follow the conclusion.

Would be nice if you posted your arguments over there. I don't see enough interest from the community in fixing this, I also don't want this to be a blocker, so changing to
 virtual ~WebPageInspectorTargetFrontendChannel() = default;
Comment 5 Yury Semikhatsky 2019-10-09 11:45:44 PDT
Created attachment 380548 [details]
Patch
Comment 6 Yury Semikhatsky 2019-10-09 11:47:27 PDT
(In reply to Yury Semikhatsky from comment #5)
> Created attachment 380548 [details]
> Patch

Fixed compilation error on Apple platforms:

/Volumes/Data/worker/macOS-High-Sierra-Release-Build-EWS/build/Source/WebKit/UIProcess/API/APIWebAuthenticationPanel.cpp:41:7: error: no matching constructor for initialization of 'WTF::String'
    : m_rpId(rpId)
      ^      ~~~~

Unrelated to this patch but somehow triggered through transitive dependencies and unified build I believe.
Comment 7 Devin Rousso 2019-10-11 17:26:53 PDT
Comment on attachment 380469 [details]
Patch

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

This is looking really good!  I'd like to read it over again to make sure I'm really understanding it, but I think we're basically there (minus the build failures) :)

> Source/JavaScriptCore/inspector/InspectorTarget.h:51
> +    virtual String previousTargetID() const { return String(); }

Style: we normally use "Id", not "ID"

> Source/JavaScriptCore/inspector/agents/InspectorTargetAgent.cpp:106
> +        result->setPreviousTargetId(target.previousTargetID());

We should only bother sending this to the frontend when the frontend actually needs it, which is in `Target.event.didCommitProvisionalTarget`.  Otherwise, we're wasting bandwidth.

I know we discussed this in <https://webkit.org/b/202219>, but I'd prefer you keep it as a parameter on `Target.event.didCommitProvisionalTarget` so it's clearer where it's actually used, rather than an optional parameter that may always be set.

> Source/JavaScriptCore/inspector/agents/InspectorTargetAgent.cpp:146
> +    targetInfo->setPreviousTargetId(oldTargetID);

Will `oldTargetId` ever be different from `target.previousTargetID()`?  You're overriding the call to `setPreviousTargetId` in `buildTargetInfoObject`, which is odd.

> Source/WebCore/inspector/InspectorController.cpp:347
>  void InspectorController::setIsUnderTest(bool value)

I thought you were going to move this to 'InspectorController.h'?

> Source/WebInspectorUI/UserInterface/Controllers/TargetManager.js:144
> +        this._provisionalTargetIds.delete(targetInfo.targetId);

NIT: I'd move this above the `targetCreated` call so we can add a `console.assert(!this._provisionalTargetIds.has(targetInfo.targetId));` inside `targetCreated`

> Source/WebInspectorUI/UserInterface/Controllers/TargetManager.js:297
> +            if (WI.quickConsole)
> +                WI.quickConsole.initializeMainExecutionContextPathComponent();

This is a layering violation.  `WI.quickConsole` is a view object, whereas this is in the model/controller layer.  Also, this won't exist for tests, so we probably should avoid even making references to it.

Thinking more about this, given how much these functions touch other managers and global state, perhaps it was better to keep it inside 'Main.js' and just duplicate it inside 'Test.js'.  I'm fine with either approach (minus the layering violation), so I'll leave the choice up to you.

> Source/WebInspectorUI/UserInterface/Protocol/Target.js:208
> +    isDestroyed()

Style: we can make this into a one-line getter
```
    get isDestroyed() { return this._isDestroyed; }
```

> Source/WebInspectorUI/UserInterface/Protocol/TargetObserver.js:29
> +    constructor()

This isn't needed

> Source/WebKit/UIProcess/InspectorTargetProxy.cpp:100
> +    return !!m_provisionalPage;

You should be able to drop the `!!`.

> Source/WebKit/UIProcess/InspectorTargetProxy.cpp:103
> +String InspectorTargetProxy::previousTargetID() const

So if I understand correctly, the `provisionalPageProxy.webPageID()` is the "current" target, and the `provisionalPageProxy.page().webPageID()` is the "previous" target?  As I mentioned earlier (InspectorTargetAgent.cpp:145), will this ever be different than the `oldTargetId` that's given to `InspectorTargetAgent::didCommitProvisionalTarget`?

> Source/WebKit/UIProcess/WebPageInspectorController.cpp:44
> +static String getTargetID(const ProvisionalPageProxy& provisionalPage)

This is only used twice, so I think we should just inline this in both places.  It's fairly straightforward anyways.

> Source/WebKit/UIProcess/WebPageInspectorController.cpp:-46
> -    auto targetAgent = makeUnique<WebPageInspectorTargetAgent>(m_frontendRouter.get(), m_backendDispatcher.get());

I think we can delay the creation of `m_targetAgent` until inside `WebPageInspectorController::init`.  It's probably not that big of a difference since we create the `WebPageInspectorController` in the same function as we call `WebPageInspectorController::init `, but it can't hurt.

> Source/WebKit/UIProcess/WebPageInspectorController.cpp:191
> +    // FIXME: do not destroy targets that belong to the committed page.

Please make a bug for this and include it's URL here

> LayoutTests/http/tests/inspector/target/target-events-for-provisional-page.html:18
> +            TargetAgent.dispatcher = new Proxy(TargetAgent.dispatcher, {

I'm a bit confused as to why you can't use a protocol test for this.  It seems like all you really need to do is check the event payloads, and not necessarily do much else.  Can you explain why we can't use a protocol test?
Comment 8 Devin Rousso 2019-10-11 17:27:42 PDT
(In reply to Devin Rousso from comment #7)
> This is looking really good!  I'd like to read it over again to make sure I'm really understanding it, but I think we're basically there (minus the build failures) :)
I think I was looking at an outdated run, because there are no more build failures :P
Comment 9 Yury Semikhatsky 2019-10-14 11:16:30 PDT
Comment on attachment 380469 [details]
Patch

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

>> Source/JavaScriptCore/inspector/InspectorTarget.h:51
>> +    virtual String previousTargetID() const { return String(); }
> 
> Style: we normally use "Id", not "ID"

From WebKit style guide (https://webkit.org/code-style-guidelines/#names-basic): "Capitalize the first letter, including all letters in an acronym". Look for ID in WebPageProxy for example. I believe there is some divergence from that rule in front-end code where 'Id' is used more often but in C++ the rule is unambiguous.

>> Source/JavaScriptCore/inspector/agents/InspectorTargetAgent.cpp:106
>> +        result->setPreviousTargetId(target.previousTargetID());
> 
> We should only bother sending this to the frontend when the frontend actually needs it, which is in `Target.event.didCommitProvisionalTarget`.  Otherwise, we're wasting bandwidth.
> 
> I know we discussed this in <https://webkit.org/b/202219>, but I'd prefer you keep it as a parameter on `Target.event.didCommitProvisionalTarget` so it's clearer where it's actually used, rather than an optional parameter that may always be set.

This event is sent only once per target life cycle events, it won't save you anything. If bandwidth is a concern there are better ways to improve it than doing premature optimizations.

The reason I want to have it here is that once client attaches to the backend and requests all existing targets it will have to learn which of the targets are provisional and for what parent pages. So this data structure actually describes current state of a target. Or do you suggest I add them as a separate parameter to each of targetCreated and didCommitProvisionalTarget be events? What are we saving then?

>> Source/JavaScriptCore/inspector/agents/InspectorTargetAgent.cpp:146
>> +    targetInfo->setPreviousTargetId(oldTargetID);
> 
> Will `oldTargetId` ever be different from `target.previousTargetID()`?  You're overriding the call to `setPreviousTargetId` in `buildTargetInfoObject`, which is odd.

By this time target.previousTargetID has already been cleared as the target is committed. Alternatively, we could store a copy of previousTargetID after the commit. I didn't quite like that but if you think it'd be better I can ad InspectedTargetProxy::m_previousTargetID. I'm open to suggestions.

>> Source/WebCore/inspector/InspectorController.cpp:347
>>  void InspectorController::setIsUnderTest(bool value)
> 
> I thought you were going to move this to 'InspectorController.h'?

Hmm, in my local state it is in the header. Probably I haven't uploaded the most recent version. Done.

>> Source/WebInspectorUI/UserInterface/Controllers/TargetManager.js:144
>> +        this._provisionalTargetIds.delete(targetInfo.targetId);
> 
> NIT: I'd move this above the `targetCreated` call so we can add a `console.assert(!this._provisionalTargetIds.has(targetInfo.targetId));` inside `targetCreated`

Done.

>> Source/WebInspectorUI/UserInterface/Controllers/TargetManager.js:297
>> +                WI.quickConsole.initializeMainExecutionContextPathComponent();
> 
> This is a layering violation.  `WI.quickConsole` is a view object, whereas this is in the model/controller layer.  Also, this won't exist for tests, so we probably should avoid even making references to it.
> 
> Thinking more about this, given how much these functions touch other managers and global state, perhaps it was better to keep it inside 'Main.js' and just duplicate it inside 'Test.js'.  I'm fine with either approach (minus the layering violation), so I'll leave the choice up to you.

Done. Moved the code to a listener in QuickConsole. I think it's a positive change to reduce code duplication between the Test.js and Main.js. As a next step these methods can be turned into listeners of the target manager. I'd much rather do it in a separate change to keep this one focused on the primary goal.

>> Source/WebInspectorUI/UserInterface/Protocol/Target.js:208
>> +    isDestroyed()
> 
> Style: we can make this into a one-line getter
> ```
>     get isDestroyed() { return this._isDestroyed; }
> ```

Done.

>> Source/WebInspectorUI/UserInterface/Protocol/TargetObserver.js:29
>> +    constructor()
> 
> This isn't needed

Removed.

>> Source/WebKit/UIProcess/InspectorTargetProxy.cpp:100
>> +    return !!m_provisionalPage;
> 
> You should be able to drop the `!!`.

bool() operator on WeakPtr is explicit, so it'd cause compilation error.

>> Source/WebKit/UIProcess/InspectorTargetProxy.cpp:103
>> +String InspectorTargetProxy::previousTargetID() const
> 
> So if I understand correctly, the `provisionalPageProxy.webPageID()` is the "current" target, and the `provisionalPageProxy.page().webPageID()` is the "previous" target?  As I mentioned earlier (InspectorTargetAgent.cpp:145), will this ever be different than the `oldTargetId` that's given to `InspectorTargetAgent::didCommitProvisionalTarget`?

After commit provisional page becomes current page and the old page id is overwritten.

>> Source/WebKit/UIProcess/WebPageInspectorController.cpp:44
>> +static String getTargetID(const ProvisionalPageProxy& provisionalPage)
> 
> This is only used twice, so I think we should just inline this in both places.  It's fairly straightforward anyways.

In addition to reducing code duplication it makes the call sites much shorter and more readable so I'd rather leave it this way.

>> Source/WebKit/UIProcess/WebPageInspectorController.cpp:-46
>> -    auto targetAgent = makeUnique<WebPageInspectorTargetAgent>(m_frontendRouter.get(), m_backendDispatcher.get());
> 
> I think we can delay the creation of `m_targetAgent` until inside `WebPageInspectorController::init`.  It's probably not that big of a difference since we create the `WebPageInspectorController` in the same function as we call `WebPageInspectorController::init `, but it can't hurt.

Done.

>> Source/WebKit/UIProcess/WebPageInspectorController.cpp:191
>> +    // FIXME: do not destroy targets that belong to the committed page.
> 
> Please make a bug for this and include it's URL here

Filed webtkit.org/b/202937

>> LayoutTests/http/tests/inspector/target/target-events-for-provisional-page.html:18
>> +            TargetAgent.dispatcher = new Proxy(TargetAgent.dispatcher, {
> 
> I'm a bit confused as to why you can't use a protocol test for this.  It seems like all you really need to do is check the event payloads, and not necessarily do much else.  Can you explain why we can't use a protocol test?

Protocol tests use a shortcut when connecting to the inspected page: they are opened in a dummy front-end page (openDummyInspectorFrontend) and all messages are routed directly between the two pages.
From the inspected page to front-end via window.postMessage and via
InspectorFrontendClientLocal::sendMessageToBackend in the reverse direction.
These messages don't utilize Target domain at all and my goal is to test Target domain.


This is why I say it'd be nice to get rid of the dummy inspector front-end altogether and host protocol tests' front-end in the same inspector window as inspector UI tests. It would route all messages through the UIProcess and its target agent in case of WK2.
Comment 10 Yury Semikhatsky 2019-10-14 11:16:34 PDT
View in context: https://bugs.webkit.org/attachment.cgi?id=380469&action=review

>> Source/JavaScriptCore/inspector/InspectorTarget.h:51
>> +    virtual String previousTargetID() const { return String(); }
> 
> Style: we normally use "Id", not "ID"

From WebKit style guide (https://webkit.org/code-style-guidelines/#names-basic): "Capitalize the first letter, including all letters in an acronym". Look for ID in WebPageProxy for example. I believe there is some divergence from that rule in front-end code where 'Id' is used more often but in C++ the rule is unambiguous.

>> Source/JavaScriptCore/inspector/agents/InspectorTargetAgent.cpp:106
>> +        result->setPreviousTargetId(target.previousTargetID());
> 
> We should only bother sending this to the frontend when the frontend actually needs it, which is in `Target.event.didCommitProvisionalTarget`.  Otherwise, we're wasting bandwidth.
> 
> I know we discussed this in <https://webkit.org/b/202219>, but I'd prefer you keep it as a parameter on `Target.event.didCommitProvisionalTarget` so it's clearer where it's actually used, rather than an optional parameter that may always be set.

This event is sent only once per target life cycle events, it won't save you anything. If bandwidth is a concern there are better ways to improve it than doing premature optimizations.

The reason I want to have it here is that once client attaches to the backend and requests all existing targets it will have to learn which of the targets are provisional and for what parent pages. So this data structure actually describes current state of a target. Or do you suggest I add them as a separate parameter to each of targetCreated and didCommitProvisionalTarget be events? What are we saving then?

>> Source/JavaScriptCore/inspector/agents/InspectorTargetAgent.cpp:146
>> +    targetInfo->setPreviousTargetId(oldTargetID);
> 
> Will `oldTargetId` ever be different from `target.previousTargetID()`?  You're overriding the call to `setPreviousTargetId` in `buildTargetInfoObject`, which is odd.

By this time target.previousTargetID has already been cleared as the target is committed. Alternatively, we could store a copy of previousTargetID after the commit. I didn't quite like that but if you think it'd be better I can ad InspectedTargetProxy::m_previousTargetID. I'm open to suggestions.

>> Source/WebCore/inspector/InspectorController.cpp:347
>>  void InspectorController::setIsUnderTest(bool value)
> 
> I thought you were going to move this to 'InspectorController.h'?

Hmm, in my local state it is in the header. Probably I haven't uploaded the most recent version. Done.

>> Source/WebInspectorUI/UserInterface/Controllers/TargetManager.js:144
>> +        this._provisionalTargetIds.delete(targetInfo.targetId);
> 
> NIT: I'd move this above the `targetCreated` call so we can add a `console.assert(!this._provisionalTargetIds.has(targetInfo.targetId));` inside `targetCreated`

Done.

>> Source/WebInspectorUI/UserInterface/Controllers/TargetManager.js:297
>> +                WI.quickConsole.initializeMainExecutionContextPathComponent();
> 
> This is a layering violation.  `WI.quickConsole` is a view object, whereas this is in the model/controller layer.  Also, this won't exist for tests, so we probably should avoid even making references to it.
> 
> Thinking more about this, given how much these functions touch other managers and global state, perhaps it was better to keep it inside 'Main.js' and just duplicate it inside 'Test.js'.  I'm fine with either approach (minus the layering violation), so I'll leave the choice up to you.

Done. Moved the code to a listener in QuickConsole. I think it's a positive change to reduce code duplication between the Test.js and Main.js. As a next step these methods can be turned into listeners of the target manager. I'd much rather do it in a separate change to keep this one focused on the primary goal.

>> Source/WebInspectorUI/UserInterface/Protocol/Target.js:208
>> +    isDestroyed()
> 
> Style: we can make this into a one-line getter
> ```
>     get isDestroyed() { return this._isDestroyed; }
> ```

Done.

>> Source/WebInspectorUI/UserInterface/Protocol/TargetObserver.js:29
>> +    constructor()
> 
> This isn't needed

Removed.

>> Source/WebKit/UIProcess/InspectorTargetProxy.cpp:100
>> +    return !!m_provisionalPage;
> 
> You should be able to drop the `!!`.

bool() operator on WeakPtr is explicit, so it'd cause compilation error.

>> Source/WebKit/UIProcess/InspectorTargetProxy.cpp:103
>> +String InspectorTargetProxy::previousTargetID() const
> 
> So if I understand correctly, the `provisionalPageProxy.webPageID()` is the "current" target, and the `provisionalPageProxy.page().webPageID()` is the "previous" target?  As I mentioned earlier (InspectorTargetAgent.cpp:145), will this ever be different than the `oldTargetId` that's given to `InspectorTargetAgent::didCommitProvisionalTarget`?

After commit provisional page becomes current page and the old page id is overwritten.

>> Source/WebKit/UIProcess/WebPageInspectorController.cpp:44
>> +static String getTargetID(const ProvisionalPageProxy& provisionalPage)
> 
> This is only used twice, so I think we should just inline this in both places.  It's fairly straightforward anyways.

In addition to reducing code duplication it makes the call sites much shorter and more readable so I'd rather leave it this way.

>> Source/WebKit/UIProcess/WebPageInspectorController.cpp:-46
>> -    auto targetAgent = makeUnique<WebPageInspectorTargetAgent>(m_frontendRouter.get(), m_backendDispatcher.get());
> 
> I think we can delay the creation of `m_targetAgent` until inside `WebPageInspectorController::init`.  It's probably not that big of a difference since we create the `WebPageInspectorController` in the same function as we call `WebPageInspectorController::init `, but it can't hurt.

Done.

>> Source/WebKit/UIProcess/WebPageInspectorController.cpp:191
>> +    // FIXME: do not destroy targets that belong to the committed page.
> 
> Please make a bug for this and include it's URL here

Filed webtkit.org/b/202937

>> LayoutTests/http/tests/inspector/target/target-events-for-provisional-page.html:18
>> +            TargetAgent.dispatcher = new Proxy(TargetAgent.dispatcher, {
> 
> I'm a bit confused as to why you can't use a protocol test for this.  It seems like all you really need to do is check the event payloads, and not necessarily do much else.  Can you explain why we can't use a protocol test?

Protocol tests use a shortcut when connecting to the inspected page: they are opened in a dummy front-end page (openDummyInspectorFrontend) and all messages are routed directly between the two pages.
From the inspected page to front-end via window.postMessage and via
InspectorFrontendClientLocal::sendMessageToBackend in the reverse direction.
These messages don't utilize Target domain at all and my goal is to test Target domain.


This is why I say it'd be nice to get rid of the dummy inspector front-end altogether and host protocol tests' front-end in the same inspector window as inspector UI tests. It would route all messages through the UIProcess and its target agent in case of WK2.
Comment 11 Yury Semikhatsky 2019-10-14 11:17:34 PDT
Created attachment 380898 [details]
Patch
Comment 12 Devin Rousso 2019-10-14 12:51:51 PDT
Comment on attachment 380469 [details]
Patch

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

>>> Source/JavaScriptCore/inspector/agents/InspectorTargetAgent.cpp:106
>>> +        result->setPreviousTargetId(target.previousTargetID());
>> 
>> We should only bother sending this to the frontend when the frontend actually needs it, which is in `Target.event.didCommitProvisionalTarget`.  Otherwise, we're wasting bandwidth.
>> 
>> I know we discussed this in <https://webkit.org/b/202219>, but I'd prefer you keep it as a parameter on `Target.event.didCommitProvisionalTarget` so it's clearer where it's actually used, rather than an optional parameter that may always be set.
> 
> This event is sent only once per target life cycle events, it won't save you anything. If bandwidth is a concern there are better ways to improve it than doing premature optimizations.
> 
> The reason I want to have it here is that once client attaches to the backend and requests all existing targets it will have to learn which of the targets are provisional and for what parent pages. So this data structure actually describes current state of a target. Or do you suggest I add them as a separate parameter to each of targetCreated and didCommitProvisionalTarget be events? What are we saving then?

I was referring specifically to `previousTargetId`.  I completely agree that we should have `isProvisional` be part of `Target.TargetInfo`.  Unless you have future plans for it (which I am unaware of), it looks like the only time `previousTargetId` is ever used is when we're committing a provisional target, so in your example of needing to get the current state of all targets when Web Inspector connects, it wouldn't be needed as nothing is being "committed" during that initial broadcast.

This isn't a "premature optimization" so much as it's a code cleanliness.  We should strive to keep things as localized and concise as possible.

>>> Source/JavaScriptCore/inspector/agents/InspectorTargetAgent.cpp:146
>>> +    targetInfo->setPreviousTargetId(oldTargetID);
>> 
>> Will `oldTargetId` ever be different from `target.previousTargetID()`?  You're overriding the call to `setPreviousTargetId` in `buildTargetInfoObject`, which is odd.
> 
> By this time target.previousTargetID has already been cleared as the target is committed. Alternatively, we could store a copy of previousTargetID after the commit. I didn't quite like that but if you think it'd be better I can ad InspectedTargetProxy::m_previousTargetID. I'm open to suggestions.

If it's been cleared by this point then we shouldn't even be using it at all.  I'm fine with passing in the `oldTargetID` to this function, I just don't want to see two different calls to `setPreviousTargetId` one after the other.

>>> Source/WebInspectorUI/UserInterface/Controllers/TargetManager.js:297
>>> +                WI.quickConsole.initializeMainExecutionContextPathComponent();
>> 
>> This is a layering violation.  `WI.quickConsole` is a view object, whereas this is in the model/controller layer.  Also, this won't exist for tests, so we probably should avoid even making references to it.
>> 
>> Thinking more about this, given how much these functions touch other managers and global state, perhaps it was better to keep it inside 'Main.js' and just duplicate it inside 'Test.js'.  I'm fine with either approach (minus the layering violation), so I'll leave the choice up to you.
> 
> Done. Moved the code to a listener in QuickConsole. I think it's a positive change to reduce code duplication between the Test.js and Main.js. As a next step these methods can be turned into listeners of the target manager. I'd much rather do it in a separate change to keep this one focused on the primary goal.

Nice!  One thing to keep in mind here is that `WI.Object`'s event listeners are fired in the order that they're added.  We _should_ avoid any situation where that ordering matters, but there may be some subtle bugs as a result of this.

>>> LayoutTests/http/tests/inspector/target/target-events-for-provisional-page.html:18
>>> +            TargetAgent.dispatcher = new Proxy(TargetAgent.dispatcher, {
>> 
>> I'm a bit confused as to why you can't use a protocol test for this.  It seems like all you really need to do is check the event payloads, and not necessarily do much else.  Can you explain why we can't use a protocol test?
> 
> Protocol tests use a shortcut when connecting to the inspected page: they are opened in a dummy front-end page (openDummyInspectorFrontend) and all messages are routed directly between the two pages.
> From the inspected page to front-end via window.postMessage and via
> InspectorFrontendClientLocal::sendMessageToBackend in the reverse direction.
> These messages don't utilize Target domain at all and my goal is to test Target domain.
> 
> 
> This is why I say it'd be nice to get rid of the dummy inspector front-end altogether and host protocol tests' front-end in the same inspector window as inspector UI tests. It would route all messages through the UIProcess and its target agent in case of WK2.

Aaahhh, right the dummy frontend.  We should get rid of that at some point.

My concern with this approach is that you're only modifying the Target dispatcher for the main target, not every target.  This was why I'd suggested modifying `WI.TargetManager` in previous reviews, as that _would_ handle every target, and not require the use of a `Proxy` or any other "hacky" behavior.  I'd rather have a more comprehensive test that handles _all_ target messages, instead of just for the main target (I realize that we won't have a Target domain for any other target, but the principle of the test should be to get as much coverage, now and in the future, as possible).

Regardless, what you have here is valid, so I'm not disagreeing with the concept of the test, or even what it's testing, just how it goes about doing that.
Comment 13 Yury Semikhatsky 2019-10-14 16:24:59 PDT
Comment on attachment 380469 [details]
Patch

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

>>>> Source/JavaScriptCore/inspector/agents/InspectorTargetAgent.cpp:106
>>>> +        result->setPreviousTargetId(target.previousTargetID());
>>> 
>>> We should only bother sending this to the frontend when the frontend actually needs it, which is in `Target.event.didCommitProvisionalTarget`.  Otherwise, we're wasting bandwidth.
>>> 
>>> I know we discussed this in <https://webkit.org/b/202219>, but I'd prefer you keep it as a parameter on `Target.event.didCommitProvisionalTarget` so it's clearer where it's actually used, rather than an optional parameter that may always be set.
>> 
>> This event is sent only once per target life cycle events, it won't save you anything. If bandwidth is a concern there are better ways to improve it than doing premature optimizations.
>> 
>> The reason I want to have it here is that once client attaches to the backend and requests all existing targets it will have to learn which of the targets are provisional and for what parent pages. So this data structure actually describes current state of a target. Or do you suggest I add them as a separate parameter to each of targetCreated and didCommitProvisionalTarget be events? What are we saving then?
> 
> I was referring specifically to `previousTargetId`.  I completely agree that we should have `isProvisional` be part of `Target.TargetInfo`.  Unless you have future plans for it (which I am unaware of), it looks like the only time `previousTargetId` is ever used is when we're committing a provisional target, so in your example of needing to get the current state of all targets when Web Inspector connects, it wouldn't be needed as nothing is being "committed" during that initial broadcast.
> 
> This isn't a "premature optimization" so much as it's a code cleanliness.  We should strive to keep things as localized and concise as possible.

Consider the world where target domain is per inspected App rather than per tab as we discussed some time ago on irc. In that case the client would connect to the browser, receive all target events and then be able to pick and choose which of them to inspect. In that world if the client observes targetCreated event for a provisional target it has to know whether it's a navigation in one of the pages it's currently inspecting or it's irrelevant and safe to ignore. Without previousTargetId it would have to conservatively assume that each provisional target could be relevant to the inspected pages. This becomes even more important if you may have more than one provisional target for a page because of e.g. preloads.

previousTargetId could be passed a separate parameter in didCommitProvisionalTarget for now but then it means we'd have to add it to targetCreated event later and overall the result IMO is worse because we'd end up passing the information separately from TargetInfo in each instance.

Please let me know what you think about this. If you feel like it's better to add it later I will change the code to pass old target id as a separate parameter in didCommitProvisionalTarget.

>>>> Source/JavaScriptCore/inspector/agents/InspectorTargetAgent.cpp:146
>>>> +    targetInfo->setPreviousTargetId(oldTargetID);
>>> 
>>> Will `oldTargetId` ever be different from `target.previousTargetID()`?  You're overriding the call to `setPreviousTargetId` in `buildTargetInfoObject`, which is odd.
>> 
>> By this time target.previousTargetID has already been cleared as the target is committed. Alternatively, we could store a copy of previousTargetID after the commit. I didn't quite like that but if you think it'd be better I can ad InspectedTargetProxy::m_previousTargetID. I'm open to suggestions.
> 
> If it's been cleared by this point then we shouldn't even be using it at all.  I'm fine with passing in the `oldTargetID` to this function, I just don't want to see two different calls to `setPreviousTargetId` one after the other.

oldTargetID is only set if the target is not provisional, I moved the logic inside the buildTargetInfoObject.

>>>> Source/WebInspectorUI/UserInterface/Controllers/TargetManager.js:297
>>>> +                WI.quickConsole.initializeMainExecutionContextPathComponent();
>>> 
>>> This is a layering violation.  `WI.quickConsole` is a view object, whereas this is in the model/controller layer.  Also, this won't exist for tests, so we probably should avoid even making references to it.
>>> 
>>> Thinking more about this, given how much these functions touch other managers and global state, perhaps it was better to keep it inside 'Main.js' and just duplicate it inside 'Test.js'.  I'm fine with either approach (minus the layering violation), so I'll leave the choice up to you.
>> 
>> Done. Moved the code to a listener in QuickConsole. I think it's a positive change to reduce code duplication between the Test.js and Main.js. As a next step these methods can be turned into listeners of the target manager. I'd much rather do it in a separate change to keep this one focused on the primary goal.
> 
> Nice!  One thing to keep in mind here is that `WI.Object`'s event listeners are fired in the order that they're added.  We _should_ avoid any situation where that ordering matters, but there may be some subtle bugs as a result of this.

At first glance initializeMainExecutionContextPathComponent() doesn't depend on runtimeManager.activeExecutionContex, am I missing something?

Do you have a concrete suggestion how to improve the code or current approach is fine? Please clarify.

>>>> LayoutTests/http/tests/inspector/target/target-events-for-provisional-page.html:18
>>>> +            TargetAgent.dispatcher = new Proxy(TargetAgent.dispatcher, {
>>> 
>>> I'm a bit confused as to why you can't use a protocol test for this.  It seems like all you really need to do is check the event payloads, and not necessarily do much else.  Can you explain why we can't use a protocol test?
>> 
>> Protocol tests use a shortcut when connecting to the inspected page: they are opened in a dummy front-end page (openDummyInspectorFrontend) and all messages are routed directly between the two pages.
>> From the inspected page to front-end via window.postMessage and via
>> InspectorFrontendClientLocal::sendMessageToBackend in the reverse direction.
>> These messages don't utilize Target domain at all and my goal is to test Target domain.
>> 
>> 
>> This is why I say it'd be nice to get rid of the dummy inspector front-end altogether and host protocol tests' front-end in the same inspector window as inspector UI tests. It would route all messages through the UIProcess and its target agent in case of WK2.
> 
> Aaahhh, right the dummy frontend.  We should get rid of that at some point.
> 
> My concern with this approach is that you're only modifying the Target dispatcher for the main target, not every target.  This was why I'd suggested modifying `WI.TargetManager` in previous reviews, as that _would_ handle every target, and not require the use of a `Proxy` or any other "hacky" behavior.  I'd rather have a more comprehensive test that handles _all_ target messages, instead of just for the main target (I realize that we won't have a Target domain for any other target, but the principle of the test should be to get as much coverage, now and in the future, as possible).
> 
> Regardless, what you have here is valid, so I'm not disagreeing with the concept of the test, or even what it's testing, just how it goes about doing that.

I've added events on TargetManager for each of the protocol events as you suggested. Tried to replace TargetManaget with a test subclass but it didn't quite work: first of all we'd have to copy current state of existing instance TargetManager, then it may already be added as a listener to some objects. Having explicit events looks cleaner.
Comment 14 Yury Semikhatsky 2019-10-14 16:25:44 PDT
Created attachment 380930 [details]
Patch
Comment 15 Devin Rousso 2019-10-17 21:26:24 PDT
Comment on attachment 380469 [details]
Patch

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

Now that <https://webkit.org/b/200384> has landed, this patch will need a rebase.  Sorry I couldn't get to reviewing this again sooner :(

>>>>> Source/JavaScriptCore/inspector/agents/InspectorTargetAgent.cpp:106
>>>>> +        result->setPreviousTargetId(target.previousTargetID());
>>>> 
>>>> We should only bother sending this to the frontend when the frontend actually needs it, which is in `Target.event.didCommitProvisionalTarget`.  Otherwise, we're wasting bandwidth.
>>>> 
>>>> I know we discussed this in <https://webkit.org/b/202219>, but I'd prefer you keep it as a parameter on `Target.event.didCommitProvisionalTarget` so it's clearer where it's actually used, rather than an optional parameter that may always be set.
>>> 
>>> This event is sent only once per target life cycle events, it won't save you anything. If bandwidth is a concern there are better ways to improve it than doing premature optimizations.
>>> 
>>> The reason I want to have it here is that once client attaches to the backend and requests all existing targets it will have to learn which of the targets are provisional and for what parent pages. So this data structure actually describes current state of a target. Or do you suggest I add them as a separate parameter to each of targetCreated and didCommitProvisionalTarget be events? What are we saving then?
>> 
>> I was referring specifically to `previousTargetId`.  I completely agree that we should have `isProvisional` be part of `Target.TargetInfo`.  Unless you have future plans for it (which I am unaware of), it looks like the only time `previousTargetId` is ever used is when we're committing a provisional target, so in your example of needing to get the current state of all targets when Web Inspector connects, it wouldn't be needed as nothing is being "committed" during that initial broadcast.
>> 
>> This isn't a "premature optimization" so much as it's a code cleanliness.  We should strive to keep things as localized and concise as possible.
> 
> Consider the world where target domain is per inspected App rather than per tab as we discussed some time ago on irc. In that case the client would connect to the browser, receive all target events and then be able to pick and choose which of them to inspect. In that world if the client observes targetCreated event for a provisional target it has to know whether it's a navigation in one of the pages it's currently inspecting or it's irrelevant and safe to ignore. Without previousTargetId it would have to conservatively assume that each provisional target could be relevant to the inspected pages. This becomes even more important if you may have more than one provisional target for a page because of e.g. preloads.
> 
> previousTargetId could be passed a separate parameter in didCommitProvisionalTarget for now but then it means we'd have to add it to targetCreated event later and overall the result IMO is worse because we'd end up passing the information separately from TargetInfo in each instance.
> 
> Please let me know what you think about this. If you feel like it's better to add it later I will change the code to pass old target id as a separate parameter in didCommitProvisionalTarget.

As we discussed on IRC, this is not the direction we'd like to take Web Inspector.  If you want to connect to a browser, there should be a separate `Browser` domain, preferably behind a build flag.  The `Target` domain is meant for inspecting a single web page (and any associated targets).

Furthermore, what you're describing seems more to me like a `parentTargetId` or `relatedTargetId`, not `previousTargetId`.  I'd be fine having a `relatedTargetId` as part of `Target.TargetInfo`, but not `previousTargetId` as the only time that the `targetId`  should be "moved" to the `previousTargetId` is when we're transitioning a provisional target, which is why it should be a second parameter to the event, not a generalized item that could be part of every `Target.TargetInfo`.

>>>>> Source/WebInspectorUI/UserInterface/Controllers/TargetManager.js:297
>>>>> +                WI.quickConsole.initializeMainExecutionContextPathComponent();
>>>> 
>>>> This is a layering violation.  `WI.quickConsole` is a view object, whereas this is in the model/controller layer.  Also, this won't exist for tests, so we probably should avoid even making references to it.
>>>> 
>>>> Thinking more about this, given how much these functions touch other managers and global state, perhaps it was better to keep it inside 'Main.js' and just duplicate it inside 'Test.js'.  I'm fine with either approach (minus the layering violation), so I'll leave the choice up to you.
>>> 
>>> Done. Moved the code to a listener in QuickConsole. I think it's a positive change to reduce code duplication between the Test.js and Main.js. As a next step these methods can be turned into listeners of the target manager. I'd much rather do it in a separate change to keep this one focused on the primary goal.
>> 
>> Nice!  One thing to keep in mind here is that `WI.Object`'s event listeners are fired in the order that they're added.  We _should_ avoid any situation where that ordering matters, but there may be some subtle bugs as a result of this.
> 
> At first glance initializeMainExecutionContextPathComponent() doesn't depend on runtimeManager.activeExecutionContex, am I missing something?
> 
> Do you have a concrete suggestion how to improve the code or current approach is fine? Please clarify.

I was making a more general comment, not necessarily specific to this situation.  If you created an entirely new event, there will be no problem (especially since our event listeners fire synchronously).  If you're using an existing event, there _may_ be issues with the ordering as I mentioned, but I'm not saying there are for sure as I'd have to look.  Ideally, the order in which event listeners are fired shouldn't matter (excluding `stopPropagation`), but reality isn't always ideal :(
Comment 16 Yury Semikhatsky 2019-10-21 11:12:32 PDT
Comment on attachment 380469 [details]
Patch

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

>>>>>> Source/JavaScriptCore/inspector/agents/InspectorTargetAgent.cpp:106
>>>>>> +        result->setPreviousTargetId(target.previousTargetID());
>>>>> 
>>>>> We should only bother sending this to the frontend when the frontend actually needs it, which is in `Target.event.didCommitProvisionalTarget`.  Otherwise, we're wasting bandwidth.
>>>>> 
>>>>> I know we discussed this in <https://webkit.org/b/202219>, but I'd prefer you keep it as a parameter on `Target.event.didCommitProvisionalTarget` so it's clearer where it's actually used, rather than an optional parameter that may always be set.
>>>> 
>>>> This event is sent only once per target life cycle events, it won't save you anything. If bandwidth is a concern there are better ways to improve it than doing premature optimizations.
>>>> 
>>>> The reason I want to have it here is that once client attaches to the backend and requests all existing targets it will have to learn which of the targets are provisional and for what parent pages. So this data structure actually describes current state of a target. Or do you suggest I add them as a separate parameter to each of targetCreated and didCommitProvisionalTarget be events? What are we saving then?
>>> 
>>> I was referring specifically to `previousTargetId`.  I completely agree that we should have `isProvisional` be part of `Target.TargetInfo`.  Unless you have future plans for it (which I am unaware of), it looks like the only time `previousTargetId` is ever used is when we're committing a provisional target, so in your example of needing to get the current state of all targets when Web Inspector connects, it wouldn't be needed as nothing is being "committed" during that initial broadcast.
>>> 
>>> This isn't a "premature optimization" so much as it's a code cleanliness.  We should strive to keep things as localized and concise as possible.
>> 
>> Consider the world where target domain is per inspected App rather than per tab as we discussed some time ago on irc. In that case the client would connect to the browser, receive all target events and then be able to pick and choose which of them to inspect. In that world if the client observes targetCreated event for a provisional target it has to know whether it's a navigation in one of the pages it's currently inspecting or it's irrelevant and safe to ignore. Without previousTargetId it would have to conservatively assume that each provisional target could be relevant to the inspected pages. This becomes even more important if you may have more than one provisional target for a page because of e.g. preloads.
>> 
>> previousTargetId could be passed a separate parameter in didCommitProvisionalTarget for now but then it means we'd have to add it to targetCreated event later and overall the result IMO is worse because we'd end up passing the information separately from TargetInfo in each instance.
>> 
>> Please let me know what you think about this. If you feel like it's better to add it later I will change the code to pass old target id as a separate parameter in didCommitProvisionalTarget.
> 
> As we discussed on IRC, this is not the direction we'd like to take Web Inspector.  If you want to connect to a browser, there should be a separate `Browser` domain, preferably behind a build flag.  The `Target` domain is meant for inspecting a single web page (and any associated targets).
> 
> Furthermore, what you're describing seems more to me like a `parentTargetId` or `relatedTargetId`, not `previousTargetId`.  I'd be fine having a `relatedTargetId` as part of `Target.TargetInfo`, but not `previousTargetId` as the only time that the `targetId`  should be "moved" to the `previousTargetId` is when we're transitioning a provisional target, which is why it should be a second parameter to the event, not a generalized item that could be part of every `Target.TargetInfo`.

Done.

>>>>>> Source/WebInspectorUI/UserInterface/Controllers/TargetManager.js:297
>>>>>> +                WI.quickConsole.initializeMainExecutionContextPathComponent();
>>>>> 
>>>>> This is a layering violation.  `WI.quickConsole` is a view object, whereas this is in the model/controller layer.  Also, this won't exist for tests, so we probably should avoid even making references to it.
>>>>> 
>>>>> Thinking more about this, given how much these functions touch other managers and global state, perhaps it was better to keep it inside 'Main.js' and just duplicate it inside 'Test.js'.  I'm fine with either approach (minus the layering violation), so I'll leave the choice up to you.
>>>> 
>>>> Done. Moved the code to a listener in QuickConsole. I think it's a positive change to reduce code duplication between the Test.js and Main.js. As a next step these methods can be turned into listeners of the target manager. I'd much rather do it in a separate change to keep this one focused on the primary goal.
>>> 
>>> Nice!  One thing to keep in mind here is that `WI.Object`'s event listeners are fired in the order that they're added.  We _should_ avoid any situation where that ordering matters, but there may be some subtle bugs as a result of this.
>> 
>> At first glance initializeMainExecutionContextPathComponent() doesn't depend on runtimeManager.activeExecutionContex, am I missing something?
>> 
>> Do you have a concrete suggestion how to improve the code or current approach is fine? Please clarify.
> 
> I was making a more general comment, not necessarily specific to this situation.  If you created an entirely new event, there will be no problem (especially since our event listeners fire synchronously).  If you're using an existing event, there _may_ be issues with the ordering as I mentioned, but I'm not saying there are for sure as I'd have to look.  Ideally, the order in which event listeners are fired shouldn't matter (excluding `stopPropagation`), but reality isn't always ideal :(

OK, let me know if you are concerned about some particular flow and we can rearrange the code, TransitionPageTarget in this case seems safe to me.

Also if listeners order matters they'd better be delegates with predictable invocation sequence. In this particular case we don't rely on the order in which the notification listeners are called.
Comment 17 Yury Semikhatsky 2019-10-21 11:14:28 PDT
Created attachment 381421 [details]
Patch
Comment 18 Devin Rousso 2019-10-21 17:53:59 PDT
Comment on attachment 381421 [details]
Patch

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

r=me, awesome work!

> Source/JavaScriptCore/inspector/agents/InspectorTargetAgent.cpp:135
> +    m_frontendDispatcher->didCommitProvisionalTarget(oldTargetID, buildTargetInfoObject(*target));

Would we ever expect the `type` to change?  If not, couldn't we simplify this to just send the `oldTargetID` and `newTargetID` and have the frontend reuse the same `WI.Target` by changing it's `_identifier`?

> Source/JavaScriptCore/inspector/protocol/Target.json:13
> +                { "name": "isProvisional", "type": "boolean", "optional": true, "description": "Whether this is a provisional page target." }

This doesn't explain what a "provisional" target is.  I'd expect some explanation of what that is.

> Source/JavaScriptCore/inspector/protocol/Target.json:43
> +                { "name": "previousTargetId", "type": "string", "description": "ID of the target committed target is swapped with." },

This is an awkward sentence description.  Please rephrase it.

> Source/JavaScriptCore/inspector/protocol/Target.json:44
> +                { "name": "targetInfo", "$ref": "TargetInfo", "description": "Committed target info." }

These types of descriptions aren't very useful.  I'd either add more information or remove it.

> Source/WebInspectorUI/UserInterface/Controllers/TargetManager.js:125
> +        this.dispatchEventToListeners(WI.TargetManager.Event.TargetCreated, {targetInfo});

Rather than pass the `targetInfo`, can we instead pass the new `WI.Target`?  That way, we could use it in non-test code as well (if needed).  We could pass `isProvisional` as a separate argument in the object since it's not saved on `WI.Target`.

NIT: you can drop the `WI` from `WI.TargetManager` since it's in the same class.

> Source/WebInspectorUI/UserInterface/Controllers/TargetManager.js:131
> +        this._provisionalTargetIds.delete(targetInfo.targetId);

We should assert that it was previously in the `Set`

> Source/WebInspectorUI/UserInterface/Controllers/TargetManager.js:133
> +        this._swappedTargetIds.add(previousTargetId);

We should assert that it wasn't already in the `Set`

> Source/WebInspectorUI/UserInterface/Controllers/TargetManager.js:135
> +        this.dispatchEventToListeners(WI.TargetManager.Event.DidCommitProvisionalTarget, {previousTargetId, targetInfo});

Ditto (125)

> Source/WebInspectorUI/UserInterface/Controllers/TargetManager.js:142
> +        this.dispatchEventToListeners(WI.TargetManager.Event.TargetDestroyed, {targetId});

To test even more correctness, we could keep a copy of the `WI.Target` and pass it in here instead of `targetId` so the test can confirm that `isDestroyed` is the right value.

Although we may not always have a `WI.Target`, such as in the case of `didCommitProvisionalTarget`, so maybe not.

> Source/WebInspectorUI/UserInterface/Controllers/TargetManager.js:145
> +    _targetCreated(parentTarget, targetInfo)

We should name this function and `_targetDestroyed` to be more active instead of looking/sounding like an event listener callback:
 - `_targetCreated` => `_createTarget` (merging with the existing `_createTarget`)
 - `_targetDestroyed` => `_destroyTarget`
This way, it also won't be as confusing trying to differentiate between `targetCreated` and `_targetCreated`.

Also, this function and `_destroyTarget` should be moved into the `// Private` section below.

> Source/WebInspectorUI/UserInterface/Controllers/TargetManager.js:152
> +        console.assert(!this._provisionalTargetIds.has(targetInfo.targetId));

I would actually move this assertion before the `if`, because we wouldn't want the same provisional `targetId` to be added twice.

> Source/WebInspectorUI/UserInterface/Controllers/TargetManager.js:168
> +            return;
>          let target = this._targets.get(targetId);

Style: missing newline

> Source/WebInspectorUI/UserInterface/Controllers/TargetManager.js:177
> +            return;
>          let target = this._targets.get(targetId);

Style: missing newline

> Source/WebInspectorUI/UserInterface/Controllers/TargetManager.js:310
> +    // Events below are triggered only by corresponding protocol events.

This comment isn't really necessary.

> Source/WebInspectorUI/UserInterface/Controllers/TargetManager.js:313
> +    TargetCreated: Symbol("target-manager-target-created"),
> +    DidCommitProvisionalTarget: Symbol("target-manager-provisional-target-committed"),
> +    TargetDestroyed: Symbol("target-manager-target-detroyed"),

NIT: I know that the other events are `Symbol`, but we really don't need to do that, so please just make these strings.

> Source/WebInspectorUI/UserInterface/Protocol/Connection.js:324
> +            // Command may be sent after target is already destroyed on backend,
> +            // ignore such errors.

We can simplify this to be on one line
```
    // Ignore errors if the target was destroyed after the command was dispatched.
```

> Source/WebInspectorUI/UserInterface/Protocol/Target.js:165
> +    destroy() { this._isDestroyed = true; }

Style: we only put functions onto a single line if they're simple getters.  Since this is a function, please put the body on separate lines and move it to be below all of the rest of the getters (I'd put this between `addScript` and `hasDomain`).

> Source/WebInspectorUI/UserInterface/Views/QuickConsole.js:115
> +    _pageTargetTransisioned()

Typo: `_pageTargetTransisioned` => `_pageTargetTransitioned`

> Source/WebKit/UIProcess/API/APIWebAuthenticationPanel.cpp:37
> +Ref<WebAuthenticationPanel> WebAuthenticationPanel::create(const AuthenticatorManager& manager, const WTF::String& rpId)

This should definitely be in a separate change.  Also, I think the more "correct" solution would be to add an `#include <wtf/text/WTFString.h>` in the .cpp and `#include <wtf/Forward.h>` in the .h

> Source/WebKit/UIProcess/WebPageInspectorController.cpp:191
> +    // FIXME(webkit.org/b/202937): do not destroy targets that belong to the committed page.

NIT: our usual style is `// FIXME: <https://webkit.org/b/202937> do not destroy targets that belong to the committed page`

> Source/WebKit/WebKit.xcodeproj/project.pbxproj:6271
> +				37B47E2C1D64DB76005F4EFF /* objcSPI.h */,

Where did this come from?

> Source/WebKit/WebKit.xcodeproj/project.pbxproj:-9412
> -				7AA746D523593D8100095050 /* SecItemSPI.h in Headers */,

Ditto (6271)

> LayoutTests/http/tests/inspector/target/target-events-for-provisional-page.html:21
> +                let targetInfo = event.data.targetInfo;

NIT: you can destructure this as `let {targetInfo} = event.data;` as you do below

> LayoutTests/http/tests/inspector/target/target-events-for-provisional-page.html:35
> +                let targetId = event.data.targetId;

Ditto (21)

> LayoutTests/http/tests/inspector/target/target-events-for-provisional-page.html:39
> +                // Wait for page reload event to avoid race between test results flushing and the test completion.
> +                reloadPromise.then(resolve);

Nice catch!

NIT: I'd add a newline before the comment.
Comment 19 Yury Semikhatsky 2019-10-23 11:11:45 PDT
Comment on attachment 381421 [details]
Patch

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

>> Source/JavaScriptCore/inspector/agents/InspectorTargetAgent.cpp:135
>> +    m_frontendDispatcher->didCommitProvisionalTarget(oldTargetID, buildTargetInfoObject(*target));
> 
> Would we ever expect the `type` to change?  If not, couldn't we simplify this to just send the `oldTargetID` and `newTargetID` and have the frontend reuse the same `WI.Target` by changing it's `_identifier`?

Currently I can't think of any cases where the type would have to change. Changed the event to pass newTargetId instead and reusing TargetInfo from preceding targetCreated events.

I don't want to reuse WI.Target instance though as in the next step we'll want to have agents talking to the provisional page before it is committed.

>> Source/JavaScriptCore/inspector/protocol/Target.json:13
>> +                { "name": "isProvisional", "type": "boolean", "optional": true, "description": "Whether this is a provisional page target." }
> 
> This doesn't explain what a "provisional" target is.  I'd expect some explanation of what that is.

Done.

>> Source/JavaScriptCore/inspector/protocol/Target.json:43
>> +                { "name": "previousTargetId", "type": "string", "description": "ID of the target committed target is swapped with." },
> 
> This is an awkward sentence description.  Please rephrase it.

Done.

>> Source/JavaScriptCore/inspector/protocol/Target.json:44
>> +                { "name": "targetInfo", "$ref": "TargetInfo", "description": "Committed target info." }
> 
> These types of descriptions aren't very useful.  I'd either add more information or remove it.

Changed this to newTargetId instead.

>> Source/WebInspectorUI/UserInterface/Controllers/TargetManager.js:125
>> +        this.dispatchEventToListeners(WI.TargetManager.Event.TargetCreated, {targetInfo});
> 
> Rather than pass the `targetInfo`, can we instead pass the new `WI.Target`?  That way, we could use it in non-test code as well (if needed).  We could pass `isProvisional` as a separate argument in the object since it's not saved on `WI.Target`.
> 
> NIT: you can drop the `WI` from `WI.TargetManager` since it's in the same class.

We don't create WI.Target for provisional pages yet so there is nothing to pass for them at the moment. We can change the event arguments later depending on the use cases we have.


As for WI.TargetManager. prefix, I don't think we can drop it even being in the same class, reference resolution would fail.

>> Source/WebInspectorUI/UserInterface/Controllers/TargetManager.js:131
>> +        this._provisionalTargetIds.delete(targetInfo.targetId);
> 
> We should assert that it was previously in the `Set`

Done.

>> Source/WebInspectorUI/UserInterface/Controllers/TargetManager.js:133
>> +        this._swappedTargetIds.add(previousTargetId);
> 
> We should assert that it wasn't already in the `Set`

Done.

>> Source/WebInspectorUI/UserInterface/Controllers/TargetManager.js:135
>> +        this.dispatchEventToListeners(WI.TargetManager.Event.DidCommitProvisionalTarget, {previousTargetId, targetInfo});
> 
> Ditto (125)

It wouldn't work. See above.

>> Source/WebInspectorUI/UserInterface/Controllers/TargetManager.js:142
>> +        this.dispatchEventToListeners(WI.TargetManager.Event.TargetDestroyed, {targetId});
> 
> To test even more correctness, we could keep a copy of the `WI.Target` and pass it in here instead of `targetId` so the test can confirm that `isDestroyed` is the right value.
> 
> Although we may not always have a `WI.Target`, such as in the case of `didCommitProvisionalTarget`, so maybe not.

Yeah, the problem with it is that sometimes we don't have target instance here.

>> Source/WebInspectorUI/UserInterface/Controllers/TargetManager.js:145
>> +    _targetCreated(parentTarget, targetInfo)
> 
> We should name this function and `_targetDestroyed` to be more active instead of looking/sounding like an event listener callback:
>  - `_targetCreated` => `_createTarget` (merging with the existing `_createTarget`)
>  - `_targetDestroyed` => `_destroyTarget`
> This way, it also won't be as confusing trying to differentiate between `targetCreated` and `_targetCreated`.
> 
> Also, this function and `_destroyTarget` should be moved into the `// Private` section below.

Good point. Done.

>> Source/WebInspectorUI/UserInterface/Controllers/TargetManager.js:152
>> +        console.assert(!this._provisionalTargetIds.has(targetInfo.targetId));
> 
> I would actually move this assertion before the `if`, because we wouldn't want the same provisional `targetId` to be added twice.

Good point. Done.

>> Source/WebInspectorUI/UserInterface/Controllers/TargetManager.js:168
>>          let target = this._targets.get(targetId);
> 
> Style: missing newline

Done.

>> Source/WebInspectorUI/UserInterface/Controllers/TargetManager.js:177
>>          let target = this._targets.get(targetId);
> 
> Style: missing newline

Done.

>> Source/WebInspectorUI/UserInterface/Controllers/TargetManager.js:310
>> +    // Events below are triggered only by corresponding protocol events.
> 
> This comment isn't really necessary.

Removed.

>> Source/WebInspectorUI/UserInterface/Controllers/TargetManager.js:313
>> +    TargetDestroyed: Symbol("target-manager-target-detroyed"),
> 
> NIT: I know that the other events are `Symbol`, but we really don't need to do that, so please just make these strings.

Removed. I'm curious what's the rationale for not using symbols, can you elaborate?

Also should we change other event names in WI.TargetManager.Event to be strings for consistency?

One advantage of symbols that I see is that it forces clients to use the named constants rather than raw event names when adding listeners.

>> Source/WebInspectorUI/UserInterface/Protocol/Connection.js:324
>> +            // ignore such errors.
> 
> We can simplify this to be on one line
> ```
>     // Ignore errors if the target was destroyed after the command was dispatched.
> ```

Done.

>> Source/WebInspectorUI/UserInterface/Protocol/Target.js:165
>> +    destroy() { this._isDestroyed = true; }
> 
> Style: we only put functions onto a single line if they're simple getters.  Since this is a function, please put the body on separate lines and move it to be below all of the rest of the getters (I'd put this between `addScript` and `hasDomain`).

Done.

>> Source/WebInspectorUI/UserInterface/Views/QuickConsole.js:115
>> +    _pageTargetTransisioned()
> 
> Typo: `_pageTargetTransisioned` => `_pageTargetTransitioned`

Done.

>> Source/WebKit/UIProcess/API/APIWebAuthenticationPanel.cpp:37
>> +Ref<WebAuthenticationPanel> WebAuthenticationPanel::create(const AuthenticatorManager& manager, const WTF::String& rpId)
> 
> This should definitely be in a separate change.  Also, I think the more "correct" solution would be to add an `#include <wtf/text/WTFString.h>` in the .cpp and `#include <wtf/Forward.h>` in the .h

It is part of this change because otherwise WebKit would fail to compile and it builds fine without my change (so it'd make less sense in isolation).  The error is a consequence of WebKit C++ relying on transitive includes.

In this case `#include <wtf/Forward.h>` cannot be used in the header because there is a member of type WTF::String in the class. The compiler needs to know full declaration of the string class for the declaration of WebAuthenticationPanel.

>> Source/WebKit/UIProcess/WebPageInspectorController.cpp:191
>> +    // FIXME(webkit.org/b/202937): do not destroy targets that belong to the committed page.
> 
> NIT: our usual style is `// FIXME: <https://webkit.org/b/202937> do not destroy targets that belong to the committed page`

Done.

>> Source/WebKit/WebKit.xcodeproj/project.pbxproj:6271
>> +				37B47E2C1D64DB76005F4EFF /* objcSPI.h */,
> 
> Where did this come from?

Dunno. Current diff with master doesn't have this change.

>> Source/WebKit/WebKit.xcodeproj/project.pbxproj:-9412
>> -				7AA746D523593D8100095050 /* SecItemSPI.h in Headers */,
> 
> Ditto (6271)

Reverted.Done.

>> LayoutTests/http/tests/inspector/target/target-events-for-provisional-page.html:21
>> +                let targetInfo = event.data.targetInfo;
> 
> NIT: you can destructure this as `let {targetInfo} = event.data;` as you do below

Done.

>> LayoutTests/http/tests/inspector/target/target-events-for-provisional-page.html:35
>> +                let targetId = event.data.targetId;
> 
> Ditto (21)

Done.

>> LayoutTests/http/tests/inspector/target/target-events-for-provisional-page.html:39
>> +                reloadPromise.then(resolve);
> 
> Nice catch!
> 
> NIT: I'd add a newline before the comment.

Done.
Comment 20 Yury Semikhatsky 2019-10-23 11:12:55 PDT
Created attachment 381701 [details]
Patch for landing
Comment 21 Yury Semikhatsky 2019-10-23 11:15:40 PDT
(In reply to Yury Semikhatsky from comment #20)
> Created attachment 381701 [details]
> Patch for landing

I'll wait for https://webkit.org/b/203262 to land before committing this change.
Comment 22 Devin Rousso 2019-10-23 11:29:28 PDT
Comment on attachment 381421 [details]
Patch

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

>>> Source/WebInspectorUI/UserInterface/Controllers/TargetManager.js:313
>>> +    TargetDestroyed: Symbol("target-manager-target-detroyed"),
>> 
>> NIT: I know that the other events are `Symbol`, but we really don't need to do that, so please just make these strings.
> 
> Removed. I'm curious what's the rationale for not using symbols, can you elaborate?
> 
> Also should we change other event names in WI.TargetManager.Event to be strings for consistency?
> 
> One advantage of symbols that I see is that it forces clients to use the named constants rather than raw event names when adding listeners.

There's no real need to add the extra overhead of constructing a `Symbol`, especially since all of these are evaluated when Web Inspector first opens.  We already force clients to use the named constants through our (informal) style guide, so there's no real benefit there either.
Comment 23 WebKit Commit Bot 2019-10-23 13:14:59 PDT
The commit-queue encountered the following flaky tests while processing attachment 381701 [details]:

media/modern-media-controls/compact-media-controls/compact-media-controls-constructor.html bug 193587 (author: graouts@apple.com)
The commit-queue is continuing to process your patch.
Comment 24 WebKit Commit Bot 2019-10-23 13:16:08 PDT
Comment on attachment 381701 [details]
Patch for landing

Clearing flags on attachment: 381701

Committed r251494: <https://trac.webkit.org/changeset/251494>
Comment 25 WebKit Commit Bot 2019-10-23 13:16:11 PDT
All reviewed patches have been landed.  Closing bug.
Comment 26 Radar WebKit Bug Importer 2019-10-23 13:17:15 PDT
<rdar://problem/56551422>
Comment 27 Truitt Savell 2019-11-04 16:01:25 PST
The new test http/tests/inspector/target/target-events-for-provisional-page.html

added in https://trac.webkit.org/changeset/251494/webkit

is a flakey failure: https://results.webkit.org/?suite=layout-tests&test=http%2Ftests%2Finspector%2Ftarget%2Ftarget-events-for-provisional-page.html

Diff:
--- /Volumes/Data/slave/mojave-release-tests-wk2/build/layout-test-results/http/tests/inspector/target/target-events-for-provisional-page-expected.txt
+++ /Volumes/Data/slave/mojave-release-tests-wk2/build/layout-test-results/http/tests/inspector/target/target-events-for-provisional-page-actual.txt
@@ -6,11 +6,5 @@
 
 == Running test suite: Target.PSON
 -- Running test case: ProvisionalPageTarget
-PASS: Should receive targetCreated event.
-PASS: Target should be provisional.
-PASS: Should receive didCommitProvisionalTarget event.
-PASS: Previous target should be the current one.
-PASS: Committed target should match provisional target.
-PASS: Should receive targetDestroyed event.
-PASS: Destroyed target should be previous target.
+!! TIMEOUT: took longer than 10000ms
Comment 28 Yury Semikhatsky 2019-11-07 09:53:59 PST
(In reply to Truitt Savell from comment #27)
> The new test
> http/tests/inspector/target/target-events-for-provisional-page.html
> 
> added in https://trac.webkit.org/changeset/251494/webkit
> 
> is a flakey failure:
> https://results.webkit.org/?suite=layout-
> tests&test=http%2Ftests%2Finspector%2Ftarget%2Ftarget-events-for-provisional-
> page.html
> 
> Diff:
> ---
> /Volumes/Data/slave/mojave-release-tests-wk2/build/layout-test-results/http/
> tests/inspector/target/target-events-for-provisional-page-expected.txt
> +++
> /Volumes/Data/slave/mojave-release-tests-wk2/build/layout-test-results/http/
> tests/inspector/target/target-events-for-provisional-page-actual.txt
> @@ -6,11 +6,5 @@
>  
>  == Running test suite: Target.PSON
>  -- Running test case: ProvisionalPageTarget
> -PASS: Should receive targetCreated event.
> -PASS: Target should be provisional.
> -PASS: Should receive didCommitProvisionalTarget event.
> -PASS: Previous target should be the current one.
> -PASS: Committed target should match provisional target.
> -PASS: Should receive targetDestroyed event.
> -PASS: Destroyed target should be previous target.
> +!! TIMEOUT: took longer than 10000ms

Filed https://bugs.webkit.org/show_bug.cgi?id=203965 for tracking this. Looks like one of the previous tests disables PSON in the inspected page.