Bug 195319 - Web Inspector: TimelineOverview clicks do not always behave as expected
Summary: Web Inspector: TimelineOverview clicks do not always behave as expected
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: Joseph Pecoraro
URL:
Keywords: InRadar
Depends on:
Blocks:
 
Reported: 2019-03-05 01:09 PST by Joseph Pecoraro
Modified: 2019-03-08 11:19 PST (History)
5 users (show)

See Also:


Attachments
[PATCH] Proposed Fix (3.33 KB, patch)
2019-03-05 01:12 PST, Joseph Pecoraro
ews: commit-queue-
Details | Formatted Diff | Diff
Archive of layout-test-results from ews126 for ios-simulator-wk2 (3.06 MB, application/zip)
2019-03-05 03:19 PST, Build Bot
no flags Details
[PATCH] Proposed Fix (3.09 KB, patch)
2019-03-06 13:29 PST, Joseph Pecoraro
drousso: review+
Details | Formatted Diff | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Joseph Pecoraro 2019-03-05 01:09:29 PST
TimelineOverview clicks do not always behave as expected

- Very short clicks sometimes produce 1px drags
- __timelineRecordBarClick doesn't propagate as expected

Do a little better at differentiating between a drag and a click.
Comment 1 Joseph Pecoraro 2019-03-05 01:12:42 PST
Created attachment 363618 [details]
[PATCH] Proposed Fix
Comment 2 Devin Rousso 2019-03-05 02:08:51 PST
Comment on attachment 363618 [details]
[PATCH] Proposed Fix

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

> Source/WebInspectorUI/UserInterface/Views/TimelineRuler.js:759
> +        if (pixels <= 4)

If the user has moved the mouse more than 4px (e.g. they jerk their wrist), should that be considered a pass?

> Source/WebInspectorUI/UserInterface/Views/TimelineRuler.js:781
> +            let subEvent = new event.constructor(event.type, event);

I'd name this `newEvent` (it matches `newTarget`).

> Source/WebInspectorUI/UserInterface/Views/TimelineRuler.js:784
> +                event.stop();

Do we want to `stop`, or just set `__timelineRecordClick` on `newEvent` if it had previously been set on `event`?  Also, considering that we're trying to dispatch the event on a new target, shouldn't we `stop` the old event regardless?

    let newEvent = new event.constructor(event.type, event);
    if (event.__timlineRecordBarClick)
        newEvent.__timelineRecordBarClick = event.__timelineRecordBarClick;

    event.stop();

    newTargete.dispatchEvent(newEvent);
Comment 3 Build Bot 2019-03-05 03:19:41 PST
Comment on attachment 363618 [details]
[PATCH] Proposed Fix

Attachment 363618 [details] did not pass ios-sim-ews (ios-simulator-wk2):
Output: https://webkit-queues.webkit.org/results/11375690

New failing tests:
fast/events/beforeunload-alert-user-interaction2.html
fast/events/clipboard-event-constructor.html
fast/events/click-handler-on-body-simple.html
fast/forms/datalist/datalist-textinput-suggestions-order.html
Comment 4 Build Bot 2019-03-05 03:19:42 PST
Created attachment 363632 [details]
Archive of layout-test-results from ews126 for ios-simulator-wk2

The attached test failures were seen while running run-webkit-tests on the ios-sim-ews.
Bot: ews126  Port: ios-simulator-wk2  Platform: Mac OS X 10.13.6
Comment 5 Joseph Pecoraro 2019-03-05 13:08:46 PST
Comment on attachment 363618 [details]
[PATCH] Proposed Fix

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

>> Source/WebInspectorUI/UserInterface/Views/TimelineRuler.js:759
>> +        if (pixels <= 4)
> 
> If the user has moved the mouse more than 4px (e.g. they jerk their wrist), should that be considered a pass?

If the jerk took less than 25ms then it would be ignored. But otherwise I'd consider it a real drag. That 25 is probably not meaningful so I may drop it.

>> Source/WebInspectorUI/UserInterface/Views/TimelineRuler.js:784
>> +                event.stop();
> 
> Do we want to `stop`, or just set `__timelineRecordClick` on `newEvent` if it had previously been set on `event`?  Also, considering that we're trying to dispatch the event on a new target, shouldn't we `stop` the old event regardless?
> 
>     let newEvent = new event.constructor(event.type, event);
>     if (event.__timlineRecordBarClick)
>         newEvent.__timelineRecordBarClick = event.__timelineRecordBarClick;
> 
>     event.stop();
> 
>     newTargete.dispatchEvent(newEvent);

I'll try just propagating the __timelineRecordClick.

I don't think we want to always stop(), because then the TimelineOverview would never get the click event even if something down below didn't want it.

Also it is the dispatchEvent that triggers adding the __property, it would not be set prior to the dispatchEvent because there is only a single one followed by a return.
Comment 6 Joseph Pecoraro 2019-03-06 13:27:50 PST
Comment on attachment 363618 [details]
[PATCH] Proposed Fix

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

>>> Source/WebInspectorUI/UserInterface/Views/TimelineRuler.js:759
>>> +        if (pixels <= 4)
>> 
>> If the user has moved the mouse more than 4px (e.g. they jerk their wrist), should that be considered a pass?
> 
> If the jerk took less than 25ms then it would be ignored. But otherwise I'd consider it a real drag. That 25 is probably not meaningful so I may drop it.

I've removed this. 25ms is so short anyways I'm not sure that is possible with most hardware!

>>> Source/WebInspectorUI/UserInterface/Views/TimelineRuler.js:784
>>> +                event.stop();
>> 
>> Do we want to `stop`, or just set `__timelineRecordClick` on `newEvent` if it had previously been set on `event`?  Also, considering that we're trying to dispatch the event on a new target, shouldn't we `stop` the old event regardless?
>> 
>>     let newEvent = new event.constructor(event.type, event);
>>     if (event.__timlineRecordBarClick)
>>         newEvent.__timelineRecordBarClick = event.__timelineRecordBarClick;
>> 
>>     event.stop();
>> 
>>     newTargete.dispatchEvent(newEvent);
> 
> I'll try just propagating the __timelineRecordClick.
> 
> I don't think we want to always stop(), because then the TimelineOverview would never get the click event even if something down below didn't want it.
> 
> Also it is the dispatchEvent that triggers adding the __property, it would not be set prior to the dispatchEvent because there is only a single one followed by a return.

The stop() seems necessary for the CPU graph case. I'm not sure how this works for the recordBar cases today. I suspect it has to do with the weird z-indexes of the record bars today (which prevent timeline range selection drags on them, an issue the CPU overview graph does not have). Logically the stop() is what I would expect once something has decided to handle the event.

What I was seeing in the CPU case was if you had a column already selected and then selected a different column, the first click would appear to remove all selection and the 2nd click would select the new bar. With this change the first click selects the new bar. In code I was seeing the first click actually set the selectedRecord correctly but later the TimelineOverview's click handler `_handleGraphsContainerClick` immediately clears the selectedRecord.
Comment 7 Joseph Pecoraro 2019-03-06 13:29:06 PST
Created attachment 363785 [details]
[PATCH] Proposed Fix
Comment 8 Devin Rousso 2019-03-06 13:58:19 PST
Comment on attachment 363785 [details]
[PATCH] Proposed Fix

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

r=me

> Source/WebInspectorUI/UserInterface/Views/TimelineRuler.js:755
> +        let pixels = Math.abs(event.pageX - this._mouseStartX);
> +        if (pixels <= 4)

NIT: this is obvious enough that it is a pixel value, so I'd just inline it.

> Source/WebInspectorUI/UserInterface/Views/TimelineRuler.js:780
> +            if (newEvent.__timelineRecordBarClick)
> +                event.stop();

I don't think we really want to be calling `stop`.  I think we should instead always be setting `__timelineRecordBarClick` on `newEvent` before it is dispatched if `event. __timelineRecordBarClick` had already been set.  Calling `event.stop()` won't prevent us from iterating over `elements` and dispatching, meaning that each `newEvent` won't have the property set.  `dispatchEvent` doesn't propagate the event up the DOM, which is why we use this loop (which basically emulates it).  Worst case, we set an extra property on an event that is never used.

For what it's worth, the only place that I've even been able to get this event to fire is to click on empty space or one of the records that is "behind" the ruler (e.g. CPU and Memory timelines).  I know you added support for clicking CPU "columns" in <https://webkit.org/b/195321>, so is this directly to support that, or is it even necessary once that lands with the `z-index` change?
Comment 9 Joseph Pecoraro 2019-03-06 14:18:43 PST
> > Source/WebInspectorUI/UserInterface/Views/TimelineRuler.js:780
> > +            if (newEvent.__timelineRecordBarClick)
> > +                event.stop();
> 
> I don't think we really want to be calling `stop`.  I think we should
> instead always be setting `__timelineRecordBarClick` on `newEvent` before it
> is dispatched if `event. __timelineRecordBarClick` had already been set. 
> Calling `event.stop()` won't prevent us from iterating over `elements` and
> dispatching, meaning that each `newEvent` won't have the property set. 
> `dispatchEvent` doesn't propagate the event up the DOM, which is why we use
> this loop (which basically emulates it).  Worst case, we set an extra
> property on an event that is never used.
>
> For what it's worth, the only place that I've even been able to get this
> event to fire is to click on empty space or one of the records that is
> "behind" the ruler (e.g. CPU and Memory timelines).  I know you added
> support for clicking CPU "columns" in <https://webkit.org/b/195321>, so is
> this directly to support that, or is it even necessary once that lands with
> the `z-index` change?

Correct, the TimelineRuler path doesn't even happen when clicking record bars, so __timelineRecordBarClick would never have been set.

The micro clicks part is generic, but this case here does seem to be specifically in support of CPU (I had thought it was generic when I put up this patch). I'll rename this specific case to something generic, like __timelineRecordClickHandled for this generic case.

That z-index can be removed. The z-index didn't seem to help and was something I had tried (but it of course changed how the markers appear.
Comment 10 Joseph Pecoraro 2019-03-06 14:21:36 PST
And because this snippet was specific to CPU handling I'll move it to the other patch so it is grouped appropriately with that.
Comment 11 Joseph Pecoraro 2019-03-06 14:34:56 PST
https://trac.webkit.org/r242566
Comment 12 Radar WebKit Bug Importer 2019-03-08 11:19:38 PST
<rdar://problem/48719185>