Bug 163021 - Support onbeforeinput event handling for the new InputEvent spec
Summary: Support onbeforeinput event handling for the new InputEvent spec
Status: RESOLVED FIXED
Alias: None
Product: WebKit
Classification: Unclassified
Component: UI Events (show other bugs)
Version: WebKit Nightly Build
Hardware: Unspecified Unspecified
: P2 Normal
Assignee: Wenson Hsieh
URL:
Keywords: InRadar
Depends on:
Blocks: 163112 163025
  Show dependency treegraph
 
Reported: 2016-10-06 09:49 PDT by Wenson Hsieh
Modified: 2016-10-08 11:14 PDT (History)
6 users (show)

See Also:


Attachments
Prototype (tests in progress) (24.85 KB, patch)
2016-10-06 09:58 PDT, Wenson Hsieh
no flags Details | Formatted Diff | Diff
Archive of layout-test-results from ews103 for mac-yosemite (967.38 KB, application/zip)
2016-10-06 11:13 PDT, Build Bot
no flags Details
Archive of layout-test-results from ews106 for mac-yosemite-wk2 (1.02 MB, application/zip)
2016-10-06 11:17 PDT, Build Bot
no flags Details
Archive of layout-test-results from ews117 for mac-yosemite (1.75 MB, application/zip)
2016-10-06 11:19 PDT, Build Bot
no flags Details
Archive of layout-test-results from ews122 for ios-simulator-elcapitan-wk2 (18.70 MB, application/zip)
2016-10-06 11:39 PDT, Build Bot
no flags Details
First pass (34.53 KB, patch)
2016-10-06 13:29 PDT, Wenson Hsieh
darin: review+
Details | Formatted Diff | Diff
Patch for landing (38.80 KB, patch)
2016-10-07 16:15 PDT, Wenson Hsieh
no flags Details | Formatted Diff | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Wenson Hsieh 2016-10-06 09:49:26 PDT
Support onbeforeinput event handling for the new InputEvent spec
Comment 1 Wenson Hsieh 2016-10-06 09:58:03 PDT
Created attachment 290826 [details]
Prototype (tests in progress)
Comment 2 Build Bot 2016-10-06 11:13:30 PDT
Comment on attachment 290826 [details]
Prototype (tests in progress)

Attachment 290826 [details] did not pass mac-ews (mac):
Output: http://webkit-queues.webkit.org/results/2232455

New failing tests:
editing/mac/spelling/accept-misspelled-candidate.html
editing/undo/undo-after-event-edited.html
Comment 3 Build Bot 2016-10-06 11:13:34 PDT
Created attachment 290833 [details]
Archive of layout-test-results from ews103 for mac-yosemite

The attached test failures were seen while running run-webkit-tests on the mac-ews.
Bot: ews103  Port: mac-yosemite  Platform: Mac OS X 10.10.5
Comment 4 Build Bot 2016-10-06 11:17:55 PDT
Comment on attachment 290826 [details]
Prototype (tests in progress)

Attachment 290826 [details] did not pass mac-wk2-ews (mac-wk2):
Output: http://webkit-queues.webkit.org/results/2232462

New failing tests:
editing/mac/spelling/accept-misspelled-candidate.html
editing/undo/undo-after-event-edited.html
Comment 5 Build Bot 2016-10-06 11:17:58 PDT
Created attachment 290835 [details]
Archive of layout-test-results from ews106 for mac-yosemite-wk2

The attached test failures were seen while running run-webkit-tests on the mac-wk2-ews.
Bot: ews106  Port: mac-yosemite-wk2  Platform: Mac OS X 10.10.5
Comment 6 Build Bot 2016-10-06 11:19:09 PDT
Comment on attachment 290826 [details]
Prototype (tests in progress)

Attachment 290826 [details] did not pass mac-debug-ews (mac):
Output: http://webkit-queues.webkit.org/results/2232454

New failing tests:
editing/mac/spelling/accept-misspelled-candidate.html
editing/undo/undo-after-event-edited.html
Comment 7 Build Bot 2016-10-06 11:19:13 PDT
Created attachment 290836 [details]
Archive of layout-test-results from ews117 for mac-yosemite

The attached test failures were seen while running run-webkit-tests on the mac-debug-ews.
Bot: ews117  Port: mac-yosemite  Platform: Mac OS X 10.10.5
Comment 8 Build Bot 2016-10-06 11:39:10 PDT
Comment on attachment 290826 [details]
Prototype (tests in progress)

Attachment 290826 [details] did not pass ios-sim-ews (ios-simulator-wk2):
Output: http://webkit-queues.webkit.org/results/2232505

New failing tests:
editing/undo/undo-after-event-edited.html
Comment 9 Build Bot 2016-10-06 11:39:14 PDT
Created attachment 290838 [details]
Archive of layout-test-results from ews122 for ios-simulator-elcapitan-wk2

The attached test failures were seen while running run-webkit-tests on the ios-sim-ews.
Bot: ews122  Port: ios-simulator-elcapitan-wk2  Platform: Mac OS X 10.11.6
Comment 10 Wenson Hsieh 2016-10-06 13:29:31 PDT
Created attachment 290858 [details]
First pass
Comment 11 Wenson Hsieh 2016-10-06 13:50:52 PDT
<rdar://problem/28658073>
Comment 12 Wenson Hsieh 2016-10-06 14:05:12 PDT
Comment on attachment 290858 [details]
First pass

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

> Source/WebCore/ChangeLog:14
> +        Tweaks existing layout tests and adds new unit tests.

self nit - s/unit/layout/
Comment 13 Darin Adler 2016-10-07 11:42:11 PDT
Comment on attachment 290858 [details]
First pass

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

There are many different special cases in the code, but the tests don’t seem to cover them.

> Source/WebCore/ChangeLog:9
> +        `onbeforeinput` InputEvents to the page. To do this, we introduce two new virtual methods:

Terminology here is wrong. There is no such thing as an "onbeforeinput" event. The event name would be "beforeinput". The string "onbeforeinput" would be the event handler attribute name for that event.

> Source/WebCore/dom/Node.cpp:2207
> +    if (!document().settings()->inputEventsEnabled())

What guarantees settings won’t be nullptr?

> Source/WebCore/dom/Node.cpp:2213
> +    auto beforeInputEvent = InputEvent::create(eventNames().beforeinputEvent, inputType, true, true, document().defaultView(), 0);
> +    dispatchScopedEvent(beforeInputEvent);
> +
> +    return !beforeInputEvent->defaultPrevented();

I suggest just calling this "event", instead of calling it beforeInputEvent. Despite that fact that the other functions above use that different terminology.

> Source/WebCore/dom/Node.cpp:-2276
> -    } else if (event.type() == eventNames().webkitEditableContentChangedEvent) {
> -        dispatchInputEvent(emptyString());

What’s the rationale for removing this? Change log does not say. In fact, there are no per-function comments in the change log at all!

> Source/WebCore/dom/Node.h:528
> +    virtual bool dispatchBeforeInputEvent(const AtomicString& inputType);

We should not be adding this function to the Node class. This is an event we only dispatch to an Element, so the argument should be an Element&.

This should not be a virtual function. Unless there is some reason it is going to need to be overridden in the future. But at this time there is no abstraction here.

This function does not need to be a member function of Element class. I suggest having it be a non-member function, perhaps in the Editor.cpp file or somewhere else where we collect related code about editing events.

> Source/WebCore/editing/CompositeEditCommand.h:120
> +    virtual bool willApplyCommand();

Should comment what the bool return value means. Unclear.

> Source/WebCore/editing/Editor.cpp:1031
> +    bool continueWithDefaultBehavior = true;

Paragraphing in this function is peculiar. I think it would read more clearly without any blank lines.

> Source/WebCore/editing/Editor.cpp:1036
> +    if (endRoot && endRoot != startRoot)
> +        continueWithDefaultBehavior &= endRoot->dispatchBeforeInputEvent(inputTypeName);

Need a test covering the behavior that even if the start root beforeinput event prevents the default the end root gets the event too.

> Source/WebCore/editing/Editor.cpp:1041
> +static void dispatchInputEvents(PassRefPtr<Element> prpStartRoot, PassRefPtr<Element> prpEndRoot, const AtomicString& inputTypeName)

If we are renaming this function, then we should also get rid of the archaic use of PassRefPtr here. Can just change these to RefPtr or RefPtr&&.

> Source/WebCore/editing/Editor.cpp:1057
> +    return dispatchBeforeInputEvents(composition->startingRootEditableElement(), composition->endingRootEditableElement(), emptyString());

Why is empty string OK here? Do we have tests that cover this?

> Source/WebCore/editing/Editor.cpp:1076
> +    dispatchInputEvents(composition->startingRootEditableElement(), composition->endingRootEditableElement(), emptyString());

Why is empty string OK here? Do we have tests that cover this?

> Source/WebCore/editing/Editor.cpp:1101
> +    return dispatchBeforeInputEvents(composition.startingRootEditableElement(), composition.endingRootEditableElement(), emptyString());

Why is empty string OK here? Do we have tests that cover this?

> Source/WebCore/editing/Editor.cpp:3088
> +    if (element && !element->dispatchBeforeInputEvent(emptyString()))

Why is empty string OK here? Do we have tests that cover this?

> Source/WebCore/editing/Editor.cpp:3105
> +        element->dispatchInputEvent(emptyString());

Why is empty string OK here? Do we have tests that cover this?

> Source/WebCore/editing/TypingCommand.cpp:272
> +    if (m_isHandlingInitialTypingCommand)
> +        return CompositeEditCommand::willApplyCommand();
> +
> +    // If this is not the initial typing command, then the TypingCommand will handle the willApplyCommand logic separately
> +    // in TypingCommand::willAddTypingToOpenCommand
> +    return true;

I think we should write this the other way around. First, the unusual case, and then the normal case.

    if (!m_isHandlingInitialTypingCommand) {
        // The TypingCommand will handle the willApplyCommand logic separately in TypingCommand::willAddTypingToOpenCommand.
        return true;
    }

    return CompositeEditCommand::willApplyCommand();

> Source/WebCore/editing/TypingCommand.cpp:377
> +    // TODO: Use the newly added typing command and granularity to ensure that an InputEvent with the
> +    // correct inputType is dispatched.

In our project we use FIXME, and not TODO.

> Source/WebCore/editing/TypingCommand.h:89
> +protected:
> +    bool willApplyCommand() override;
> +    void didApplyCommand() override;

These should be private and final, rather than protected and override, unless I am missing some case where a derived class wants to call or override these.

> Source/WebCore/editing/TypingCommand.h:114
> +    void doApply() override;
> +    bool isTypingCommand() const override;
> +    bool preservesTypingStyle() const override { return m_preservesTypingStyle; }
> +    bool shouldRetainAutocorrectionIndicator() const override

These should be final rather than override unless they need to be overridden further.

> Source/WebCore/editing/TypingCommand.h:120
> +    void setShouldRetainAutocorrectionIndicator(bool retain) override { m_shouldRetainAutocorrectionIndicator = retain; }
> +    bool shouldStopCaretBlinking() const override { return true; }

Ditto.
Comment 14 Ryosuke Niwa 2016-10-07 12:12:21 PDT
Comment on attachment 290858 [details]
First pass

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

>> Source/WebCore/dom/Node.cpp:-2276
>> -        dispatchInputEvent(emptyString());
> 
> What’s the rationale for removing this? Change log does not say. In fact, there are no per-function comments in the change log at all!

Yeah, we should definitely explain this behavioral change in the change log.

>> Source/WebCore/editing/Editor.cpp:3088
>> +    if (element && !element->dispatchBeforeInputEvent(emptyString()))
> 
> Why is empty string OK here? Do we have tests that cover this?

I think the idea is to specify input event type in a future patch.

>> Source/WebCore/editing/TypingCommand.h:114
>> +    bool shouldRetainAutocorrectionIndicator() const override
> 
> These should be final rather than override unless they need to be overridden further.

Or maybe we can make TypingCommand final if nothing inherits from it?
Comment 15 Wenson Hsieh 2016-10-07 16:15:28 PDT
Created attachment 290983 [details]
Patch for landing
Comment 16 Wenson Hsieh 2016-10-07 16:25:39 PDT
Comment on attachment 290858 [details]
First pass

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

>> Source/WebCore/ChangeLog:9
>> +        `onbeforeinput` InputEvents to the page. To do this, we introduce two new virtual methods:
> 
> Terminology here is wrong. There is no such thing as an "onbeforeinput" event. The event name would be "beforeinput". The string "onbeforeinput" would be the event handler attribute name for that event.

Got it -- fixed!

>> Source/WebCore/dom/Node.cpp:2207
>> +    if (!document().settings()->inputEventsEnabled())
> 
> What guarantees settings won’t be nullptr?

Good catch! Added a check, since this does not appear to be guaranteed.

>> Source/WebCore/dom/Node.cpp:2213
>> +    return !beforeInputEvent->defaultPrevented();
> 
> I suggest just calling this "event", instead of calling it beforeInputEvent. Despite that fact that the other functions above use that different terminology.

Sounds good -- done.

>>> Source/WebCore/dom/Node.cpp:-2276
>>> -        dispatchInputEvent(emptyString());
>> 
>> What’s the rationale for removing this? Change log does not say. In fact, there are no per-function comments in the change log at all!
> 
> Yeah, we should definitely explain this behavioral change in the change log.

Good point. Ryosuke and I discussed this and agreed that we can remove webkitEditableContentChangedEvent and just dispatch the input event directly. Also added more comments in the per-function section describing other changes.

>> Source/WebCore/dom/Node.h:528
>> +    virtual bool dispatchBeforeInputEvent(const AtomicString& inputType);
> 
> We should not be adding this function to the Node class. This is an event we only dispatch to an Element, so the argument should be an Element&.
> 
> This should not be a virtual function. Unless there is some reason it is going to need to be overridden in the future. But at this time there is no abstraction here.
> 
> This function does not need to be a member function of Element class. I suggest having it be a non-member function, perhaps in the Editor.cpp file or somewhere else where we collect related code about editing events.

I see -- this makes sense. Moved this to be a static helper in Editor.cpp.

>> Source/WebCore/editing/CompositeEditCommand.h:120
>> +    virtual bool willApplyCommand();
> 
> Should comment what the bool return value means. Unclear.

Sounds good -- added a comment: 'If willApplyCommand returns false, we won't proceed with applying the command.'

>> Source/WebCore/editing/Editor.cpp:1031
>> +    bool continueWithDefaultBehavior = true;
> 
> Paragraphing in this function is peculiar. I think it would read more clearly without any blank lines.

Done.

>> Source/WebCore/editing/Editor.cpp:1036
>> +        continueWithDefaultBehavior &= endRoot->dispatchBeforeInputEvent(inputTypeName);
> 
> Need a test covering the behavior that even if the start root beforeinput event prevents the default the end root gets the event too.

Ok -- I'll add one in this patch.

>> Source/WebCore/editing/Editor.cpp:1041
>> +static void dispatchInputEvents(PassRefPtr<Element> prpStartRoot, PassRefPtr<Element> prpEndRoot, const AtomicString& inputTypeName)
> 
> If we are renaming this function, then we should also get rid of the archaic use of PassRefPtr here. Can just change these to RefPtr or RefPtr&&.

Ok! Changed to use RefPtr.

>> Source/WebCore/editing/Editor.cpp:1057
>> +    return dispatchBeforeInputEvents(composition->startingRootEditableElement(), composition->endingRootEditableElement(), emptyString());
> 
> Why is empty string OK here? Do we have tests that cover this?

This is a stub until we implement InputEvent.inputType. I will add tests to verify that we have the proper inputType when I land support for this attribute in the next patch.

>> Source/WebCore/editing/Editor.cpp:1076
>> +    dispatchInputEvents(composition->startingRootEditableElement(), composition->endingRootEditableElement(), emptyString());
> 
> Why is empty string OK here? Do we have tests that cover this?

Please see above.

>> Source/WebCore/editing/Editor.cpp:1101
>> +    return dispatchBeforeInputEvents(composition.startingRootEditableElement(), composition.endingRootEditableElement(), emptyString());
> 
> Why is empty string OK here? Do we have tests that cover this?

Please see above.

>> Source/WebCore/editing/Editor.cpp:3105
>> +        element->dispatchInputEvent(emptyString());
> 
> Why is empty string OK here? Do we have tests that cover this?

Please see above.

>> Source/WebCore/editing/TypingCommand.cpp:272
>> +    return true;
> 
> I think we should write this the other way around. First, the unusual case, and then the normal case.
> 
>     if (!m_isHandlingInitialTypingCommand) {
>         // The TypingCommand will handle the willApplyCommand logic separately in TypingCommand::willAddTypingToOpenCommand.
>         return true;
>     }
> 
>     return CompositeEditCommand::willApplyCommand();

Sounds good -- changed.

>> Source/WebCore/editing/TypingCommand.cpp:377
>> +    // correct inputType is dispatched.
> 
> In our project we use FIXME, and not TODO.

Good catch -- s/TODO/FIXME/.

>> Source/WebCore/editing/TypingCommand.h:89
>> +    void didApplyCommand() override;
> 
> These should be private and final, rather than protected and override, unless I am missing some case where a derived class wants to call or override these.

Done.

>>> Source/WebCore/editing/TypingCommand.h:114
>>> +    bool shouldRetainAutocorrectionIndicator() const override
>> 
>> These should be final rather than override unless they need to be overridden further.
> 
> Or maybe we can make TypingCommand final if nothing inherits from it?

Done. (made TypingCommand final).

>> Source/WebCore/editing/TypingCommand.h:120
>> +    bool shouldStopCaretBlinking() const override { return true; }
> 
> Ditto.

Made TypingCommand final.
Comment 17 WebKit Commit Bot 2016-10-07 16:49:49 PDT
Comment on attachment 290983 [details]
Patch for landing

Clearing flags on attachment: 290983

Committed r206944: <http://trac.webkit.org/changeset/206944>
Comment 18 Darin Adler 2016-10-07 20:29:03 PDT
Comment on attachment 290858 [details]
First pass

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

>>>> Source/WebCore/editing/TypingCommand.h:114
>>>> +    bool shouldRetainAutocorrectionIndicator() const override
>>> 
>>> These should be final rather than override unless they need to be overridden further.
>> 
>> Or maybe we can make TypingCommand final if nothing inherits from it?
> 
> Done. (made TypingCommand final).

Making TypingCommand final was a good idea. But now these functions are marked neither override nor final, and thus there is nothing asserting that these override a function from the base class. It would be good to mark each of these functions final, that way we’ll get an error if they no longer override a function from the base class.
Comment 19 Wenson Hsieh 2016-10-07 20:45:32 PDT
(In reply to comment #18)
> Comment on attachment 290858 [details]
> First pass
> 
> View in context:
> https://bugs.webkit.org/attachment.cgi?id=290858&action=review
> 
> >>>> Source/WebCore/editing/TypingCommand.h:114
> >>>> +    bool shouldRetainAutocorrectionIndicator() const override
> >>> 
> >>> These should be final rather than override unless they need to be overridden further.
> >> 
> >> Or maybe we can make TypingCommand final if nothing inherits from it?
> > 
> > Done. (made TypingCommand final).
> 
> Making TypingCommand final was a good idea. But now these functions are
> marked neither override nor final, and thus there is nothing asserting that
> these override a function from the base class. It would be good to mark each
> of these functions final, that way we’ll get an error if they no longer
> override a function from the base class.

This is true. I'll put together a follow-up for this.