Bug 179785 - [Cocoa] First pass at implementing alternative presentation button element
Summary: [Cocoa] First pass at implementing alternative presentation button element
Status: RESOLVED FIXED
Alias: None
Product: WebKit
Classification: Unclassified
Component: WebCore Misc. (show other bugs)
Version: WebKit Local Build
Hardware: Unspecified Unspecified
: P2 Normal
Assignee: Daniel Bates
URL:
Keywords: InRadar
Depends on: 179692
Blocks:
  Show dependency treegraph
 
Reported: 2017-11-16 10:52 PST by Daniel Bates
Modified: 2017-11-28 13:21 PST (History)
7 users (show)

See Also:


Attachments
Patch and tests (106.55 KB, patch)
2017-11-16 12:11 PST, Daniel Bates
no flags Details | Formatted Diff | Diff
Patch (121.14 KB, patch)
2017-11-17 11:50 PST, Daniel Bates
no flags Details | Formatted Diff | Diff
Patch and tests (121.15 KB, patch)
2017-11-17 12:23 PST, Daniel Bates
no flags Details | Formatted Diff | Diff
Patch and tests (121.42 KB, patch)
2017-11-17 12:31 PST, Daniel Bates
no flags Details | Formatted Diff | Diff
Archive of layout-test-results from ews101 for mac-elcapitan (2.44 MB, application/zip)
2017-11-17 13:33 PST, EWS Watchlist
no flags Details
Archive of layout-test-results from ews107 for mac-elcapitan-wk2 (3.01 MB, application/zip)
2017-11-17 13:44 PST, EWS Watchlist
no flags Details
Patch and tests (121.42 KB, patch)
2017-11-17 14:35 PST, Daniel Bates
no flags Details | Formatted Diff | Diff
Archive of layout-test-results from ews123 for ios-simulator-wk2 (2.43 MB, application/zip)
2017-11-17 16:51 PST, EWS Watchlist
no flags Details
Patch and tests (129.00 KB, patch)
2017-11-17 16:57 PST, Daniel Bates
no flags Details | Formatted Diff | Diff
Patch and layout tests (134.93 KB, patch)
2017-11-27 15:03 PST, Daniel Bates
bfulgham: review+
Details | Formatted Diff | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Daniel Bates 2017-11-16 10:52:20 PST
First pass at implementing alternative presentation button element.

Part of <rdar://problem/34917108>
Comment 1 Daniel Bates 2017-11-16 12:11:01 PST
Created attachment 327094 [details]
Patch and tests
Comment 2 Daniel Bates 2017-11-17 11:50:13 PST
Created attachment 327205 [details]
Patch
Comment 3 EWS Watchlist 2017-11-17 11:51:59 PST Comment hidden (obsolete)
Comment 4 Daniel Bates 2017-11-17 12:23:22 PST
Created attachment 327212 [details]
Patch and tests
Comment 5 EWS Watchlist 2017-11-17 12:25:44 PST Comment hidden (obsolete)
Comment 6 Daniel Bates 2017-11-17 12:31:30 PST
Created attachment 327214 [details]
Patch and tests
Comment 7 EWS Watchlist 2017-11-17 12:33:32 PST Comment hidden (obsolete)
Comment 8 EWS Watchlist 2017-11-17 13:33:50 PST Comment hidden (obsolete)
Comment 9 EWS Watchlist 2017-11-17 13:33:51 PST Comment hidden (obsolete)
Comment 10 EWS Watchlist 2017-11-17 13:44:10 PST Comment hidden (obsolete)
Comment 11 EWS Watchlist 2017-11-17 13:44:12 PST Comment hidden (obsolete)
Comment 12 Daniel Bates 2017-11-17 14:35:46 PST
Created attachment 327239 [details]
Patch and tests
Comment 13 EWS Watchlist 2017-11-17 14:39:25 PST Comment hidden (obsolete)
Comment 14 EWS Watchlist 2017-11-17 16:51:06 PST Comment hidden (obsolete)
Comment 15 EWS Watchlist 2017-11-17 16:51:07 PST Comment hidden (obsolete)
Comment 16 Daniel Bates 2017-11-17 16:57:49 PST
Created attachment 327279 [details]
Patch and tests
Comment 17 EWS Watchlist 2017-11-17 17:01:49 PST
Attachment 327279 [details] did not pass style-queue:


ERROR: Source/WebCore/editing/cocoa/AlternativePresentationButtonSubstitution.cpp:47:  Code inside a namespace should not be indented.  [whitespace/indent] [4]
Total errors found: 1 in 57 files


If any of these errors are false positives, please file a bug against check-webkit-style.
Comment 18 Ryosuke Niwa 2017-11-20 16:52:48 PST
Comment on attachment 327279 [details]
Patch and tests

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

> Source/WebCore/editing/Editor.cpp:3834
> +    Ref<Element> firstElement = WTFMove(elements[0]);

I think it's better to call this variable elementForAlternativePresentation.
The fact it's the fist element is more of an implementation detail, not so much the semantics of what this element is.

> Source/WebCore/editing/Editor.h:600
> +    HashMap<Element*, std::unique_ptr<AlternativePresentationButtonSubstitution>> m_alternativePresentationButtonElementToSubstitutionMap;
> +    HashMap<String, Element*> m_alternativePresentationButtonIdentifierToElementMap;

What guarantees that these raw pointers to Element would always be cleared before they're deleted?

> Source/WebCore/editing/cocoa/AlternativePresentationButtonSubstitution.cpp:90
> +    // 1. Save appearance

I don't think these comments are adding any values. Please remove.

> Source/WebCore/editing/cocoa/AlternativePresentationButtonSubstitution.cpp:95
> +    // 2. Apply substitution

Ditto.

> Source/WebCore/html/HTMLInputElement.cpp:508
> +inline std::unique_ptr<InputType> HTMLInputElement::createInputType(const AtomicString& type)
> +{
> +#if ENABLE(ALTERNATIVE_PRESENTATION_BUTTON_ELEMENT)
> +    if (type == InputTypeNames::alternativePresentationButton())
> +        return std::make_unique<AlternativePresentationButtonInputType>(*this);
> +#endif
> +    return InputType::create(*this, type);
> +}

Why can't alternativePresentationButton just use InputType::create?
Adding more & more abstractions around input type is not great because input type is already a frequent source of security bugs.
It would make it much harder to reason what's happening.

> Source/WebCore/html/HTMLInputElement.cpp:721
> -        updateType();
> +        updateType(value);

What happens if the author script changes type while the presentation is being substituted?

> Source/WebCore/html/shadow/cocoa/AlternativePresentationButtonElement.cpp:121
> +void AlternativePresentationButtonElement::defaultEventHandler(Event& event)
> +{

Can an user trigger this element purely with keyboards? i.e. What's the accessibility story?

> Source/WebCore/html/shadow/cocoa/AlternativePresentationButtonElement.h:37
> +class AlternativePresentationButtonElement final : public HTMLDivElement {

There is no point ing making this inherit from HTMLDivElement. Just make it inherit from HTMLElement directly.

> Source/WebCore/html/shadow/cocoa/AlternativePresentationButtonInputType.h:43
> +    // InputType

This comment serves no purpose. Please remove.

> Source/WebKit/UIProcess/API/C/WKPageUIClient.h:1033
> +    // Version 11.
> +    WKPageDidClickAlternativePresentationButtonCallback                 didClickAlternativePresentationButton;

Why are we adding a new C API?? Can't we add it to modern objective-C API?
Or are you doing that for testing purposes?
Comment 19 Daniel Bates 2017-11-27 12:28:21 PST
(In reply to Ryosuke Niwa from comment #18)
> > Source/WebCore/editing/Editor.cpp:3834
> > +    Ref<Element> firstElement = WTFMove(elements[0]);
> 
> I think it's better to call this variable elementForAlternativePresentation.
> The fact it's the fist element is more of an implementation detail, not so
> much the semantics of what this element is.
> 

Will rename.

> > Source/WebCore/editing/Editor.h:600
> > +    HashMap<Element*, std::unique_ptr<AlternativePresentationButtonSubstitution>> m_alternativePresentationButtonElementToSubstitutionMap;
> > +    HashMap<String, Element*> m_alternativePresentationButtonIdentifierToElementMap;
> 
> What guarantees that these raw pointers to Element would always be cleared
> before they're deleted?
>

Notice that these maps only hold pointers to AlternativePresentationButtonElement elements. These pointers are guaranteed to be valid as long as they are in the maps because these pointers are added and removed from the hash maps whenever an AlternativePresentationButtonElement element is inserted into- (AlternativePresentationButtonElement::insertedIntoAncestor()) and removed from- the document (AlternativePresentationButtonElement::removedFromAncestor()), respectively.

Will change Element* to std::reference_wrapper<AlternativePresentationButtonElement> to make it clear that these hash maps hold references to AlternativePresentationButtonElement elements. That is, they do not hold references to arbitrary elements.

> > Source/WebCore/editing/cocoa/AlternativePresentationButtonSubstitution.cpp:90
> > +    // 1. Save appearance
> 
> I don't think these comments are adding any values. Please remove.
> 

Will remove.


> > Source/WebCore/editing/cocoa/AlternativePresentationButtonSubstitution.cpp:95
> > +    // 2. Apply substitution
> 
> Ditto.
> 

Will remove.

> > Source/WebCore/html/HTMLInputElement.cpp:508
> > +inline std::unique_ptr<InputType> HTMLInputElement::createInputType(const AtomicString& type)
> > +{
> > +#if ENABLE(ALTERNATIVE_PRESENTATION_BUTTON_ELEMENT)
> > +    if (type == InputTypeNames::alternativePresentationButton())
> > +        return std::make_unique<AlternativePresentationButtonInputType>(*this);
> > +#endif
> > +    return InputType::create(*this, type);
> > +}
> 
> Why can't alternativePresentationButton just use InputType::create?

We can teach InputType::create() to instantiate AlternativePresentationButtonInputType so long as we also teach it to instantiate this type only when triggered by SPI or the Internals JavaScript object (for testing purposes) (*). That is, I do not want to expose this input type to the web (at least at this time). When I wrote this patch I was unclear whether we will want to have a dedicated input type for the Alternative Presentation Button and hence chose to implement these usage restrictions by creating a helper function that knew about the AlternativePresentationButtonInputType type. Let me know if it would be preferred to take the approach listed at the end of the remarks below or if you have other ideas. 

Additional remarks:

Currently the logic in <https://trac.webkit.org/browser/trunk/Source/WebCore/html/InputType.cpp?rev=225177> only supports conditioning input types on whether a runtime feature is enabled when it builds the input type factory map: <https://trac.webkit.org/browser/trunk/Source/WebCore/html/InputType.cpp?rev=225177#L146>. And this map is built once per WebProcess process when we process the first <input> on some page loaded in the process by <https://trac.webkit.org/browser/trunk/Source/WebCore/html/InputType.cpp?rev=225177#L155> and <https://trac.webkit.org/browser/trunk/Source/WebCore/html/HTMLInputElement.cpp?rev=225177#L485> OR <https://trac.webkit.org/browser/trunk/Source/WebCore/html/HTMLInputElement.cpp?rev=225177#L661>. One idea is to add state to HTMLInputElement to track whether the SPI/Internals function was invoked to use the Alternative Presentation Button and have InputType::create() query for this state before instantiating and returning a AlternativePresentationButtonInputType object.

> Adding more & more abstractions around input type is not great because input
> type is already a frequent source of security bugs.
> It would make it much harder to reason what's happening.
> 
> > Source/WebCore/html/HTMLInputElement.cpp:721
> > -        updateType();
> > +        updateType(value);
> 
> What happens if the author script changes type while the presentation is
> being substituted?
> 

Do you foresee an issue? If so, please provide an example.

This will behave just as any other input type change and the shadow subtree for the old input type (AlternativePresentationButtonInputType) will be destroyed before creating the new input type and attaching its shadow subtree.

> > Source/WebCore/html/shadow/cocoa/AlternativePresentationButtonElement.cpp:121
> > +void AlternativePresentationButtonElement::defaultEventHandler(Event& event)
> > +{
> 
> Can an user trigger this element purely with keyboards?

Yes, using accessibility commands (VoiceOver on Mac/iOS). We should take a similar approach as HTMLButtonElement and support key events for '\r' and ' '. Unless you feel strongly that such support should be part of this patch, I will do this work in a follow up bug.

> i.e. What's the accessibility story?
> 

We want this button to be as accessible as any other button. When the button is created it adds the appropriate ARIA attributes to itself so as to register as a button type with the accessibility machinery and its accessibility description is a localized string.

> > Source/WebCore/html/shadow/cocoa/AlternativePresentationButtonElement.h:37
> > +class AlternativePresentationButtonElement final : public HTMLDivElement {
> 
> There is no point ing making this inherit from HTMLDivElement. Just make it
> inherit from HTMLElement directly.
> 

Notice that to instantiate an HTMLElement you need a tag name. And I did not add a new tag name for this element. What tag name do you suggest I use?

> > Source/WebCore/html/shadow/cocoa/AlternativePresentationButtonInputType.h:43
> > +    // InputType
> 
> This comment serves no purpose. Please remove.
> 

I find this comment helpful to demarcate the overrides of an interface and we have similar comments throughout the codebase, including XMLHttpRequest [1], RenderLayer [2] and ApplePayValidateMerchantEvent [3].

[1] <https://trac.webkit.org/browser/trunk/Source/WebCore/xml/XMLHttpRequest.h?rev=225177#L122>
[2] <https://trac.webkit.org/browser/trunk/Source/WebCore/rendering/RenderLayer.h?rev=225177#L233>
[3] <https://trac.webkit.org/browser/trunk/Source/WebCore/Modules/applepay/ApplePayValidateMerchantEvent.h?rev=225177#L49>.

> > Source/WebKit/UIProcess/API/C/WKPageUIClient.h:1033
> > +    // Version 11.
> > +    WKPageDidClickAlternativePresentationButtonCallback                 didClickAlternativePresentationButton;
> 
> Why are we adding a new C API?? 

At the time of writing Safari on Mac uses the C API. And we want to make use of this SPI in Safari on Mac.

> Can't we add it to modern objective-C API? Or are you doing that for testing purposes?

Notice that this patch adds an equivalent modern Objective-C SPI in file WKUIDelegatePrivate.h.
Comment 20 Daniel Bates 2017-11-27 15:03:28 PST
Created attachment 327690 [details]
Patch and layout tests

Updated patch based on feedback from Ryosuke Niwa. Also added some accessibility tests. See LayoutTests change log description for more details.
Comment 21 EWS Watchlist 2017-11-27 15:06:10 PST
Attachment 327690 [details] did not pass style-queue:


ERROR: Source/WebCore/editing/cocoa/AlternativePresentationButtonSubstitution.cpp:47:  Code inside a namespace should not be indented.  [whitespace/indent] [4]
Total errors found: 1 in 61 files


If any of these errors are false positives, please file a bug against check-webkit-style.
Comment 22 Brent Fulgham 2017-11-27 16:12:42 PST
Comment on attachment 327690 [details]
Patch and layout tests

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

r=me

> Source/WebCore/editing/Editor.cpp:3841
> +        // FIXME: This substitution is only safe to do if and only if firstElement can support a user-agent

Can you please provide a Bugzilla/Radar for this follow-up work?

> Source/WebCore/html/HTMLInputElement.h:151
> +    WEBCORE_EXPORT HTMLElement* alternativePresentationButtonElement() const;

We need to make sure this doesn't get stored anywhere. I know this is the first part of this set of patches, so we don't have to address it here, but we need a mechanism where we can avoid this pointer being stored someplace.

Could this be an Optional reference?

> Source/WebCore/html/shadow/cocoa/AlternativePresentationButtonElement.cpp:100
> +    iconContainer->appendChild(icon);

This could probably be an WTFMove(icon). But not necessary to make this change.

> Source/WebCore/html/shadow/cocoa/AlternativePresentationButtonElement.cpp:110
> +    textContainer->appendChild(text);

Ditto WTFMove(text)

> Source/WebCore/html/shadow/cocoa/AlternativePresentationButtonElement.cpp:115
> +    textContainer->appendChild(subtext);

Ditto WTFMove(subtext).
Comment 23 Daniel Bates 2017-11-28 10:07:25 PST
(In reply to Brent Fulgham from comment #22)
> 
> > Source/WebCore/editing/Editor.cpp:3841
> > +        // FIXME: This substitution is only safe to do if and only if firstElement can support a user-agent
> 
> Can you please provide a Bugzilla/Radar for this follow-up work?
> 

Filed bug #180086. Will update comment to reference this bug as well as substitute elementForAlternativePresentation for firstElement or otherwise make this comment read well.

> > Source/WebCore/html/HTMLInputElement.h:151
> > +    WEBCORE_EXPORT HTMLElement* alternativePresentationButtonElement() const;
> 
> We need to make sure this doesn't get stored anywhere. I know this is the
> first part of this set of patches, so we don't have to address it here, but
> we need a mechanism where we can avoid this pointer being stored someplace.
> 
> Could this be an Optional reference?
> 

I am unclear how this would help solve the problem you described above.

> > Source/WebCore/html/shadow/cocoa/AlternativePresentationButtonElement.cpp:100
> > +    iconContainer->appendChild(icon);
> 
> This could probably be an WTFMove(icon). But not necessary to make this
> change.
> 
> > Source/WebCore/html/shadow/cocoa/AlternativePresentationButtonElement.cpp:110
> > +    textContainer->appendChild(text);
> 
> Ditto WTFMove(text)
> 

We cannot move this value because Node::appendChild() takes a non-const lvalue reference as an argument and hence does not bind to an rvalue reference. We could look to support passing an rvalue reference to Node::appendChild() as well as other DOM operations. I suggest that we do this in a separate bug.

> > Source/WebCore/html/shadow/cocoa/AlternativePresentationButtonElement.cpp:115
> > +    textContainer->appendChild(subtext);
> 
> Ditto WTFMove(subtext).

See my remark above.
Comment 24 Daniel Bates 2017-11-28 10:18:19 PST
(In reply to Daniel Bates from comment #19)
> > > Source/WebCore/editing/Editor.h:600
> > > +    HashMap<Element*, std::unique_ptr<AlternativePresentationButtonSubstitution>> m_alternativePresentationButtonElementToSubstitutionMap;
> > > +    HashMap<String, Element*> m_alternativePresentationButtonIdentifierToElementMap;
> > 
> > What guarantees that these raw pointers to Element would always be cleared
> > before they're deleted?
> >
> 
> [...]
> Will change Element* to
> std::reference_wrapper<AlternativePresentationButtonElement> to make it
> clear that these hash maps hold references to
> AlternativePresentationButtonElement elements. That is, they do not hold
> references to arbitrary elements.
> 

Actually we need to add traits to support storing std::reference_wrapper objects in HashMap. Filed bug #180091. For now, I will change Element* to AlternativePresentationButtonElement*.
Comment 25 Daniel Bates 2017-11-28 10:31:58 PST
Committed r225223: <https://trac.webkit.org/changeset/225223>
Comment 26 Matt Lewis 2017-11-28 12:48:30 PST
The test fast/forms/alternative-presentation-button/replacement.html added in this revision is consistently failing on Sierra and High Sierra

https://build.webkit.org/results/Apple%20Sierra%20Release%20WK2%20(Tests)/r225224%20(5987)/results.html

diff:
--- /Volumes/Data/slave/sierra-release-tests-wk2/build/layout-test-results/fast/forms/alternative-presentation-button/replacement-expected.txt
+++ /Volumes/Data/slave/sierra-release-tests-wk2/build/layout-test-results/fast/forms/alternative-presentation-button/replacement-actual.txt
@@ -12,9 +12,9 @@
                   RenderBlock {DIV} at (0,0) size 205x0
                 RenderBlock {DIV} at (0,0) size 205x26
                   RenderBlock (anonymous) at (0,0) size 205x13
-                    RenderInline {SPAN} at (0,0) size 185x13
-                      RenderText {#text} at (0,0) size 185x13
-                        text run at (0,0) width 185: "alternative presentation button title"
+                    RenderInline {SPAN} at (0,0) size 186x13
+                      RenderText {#text} at (0,0) size 186x13
+                        text run at (0,0) width 186: "alternative presentation button title"
                   RenderBlock {DIV} at (0,13) size 205x13
                     RenderText {#text} at (0,0) size 205x13
                       text run at (0,0) width 205: "alternative presentation button subtitle"
Comment 27 Daniel Bates 2017-11-28 13:21:01 PST
(In reply to Matt Lewis from comment #26)
> The test fast/forms/alternative-presentation-button/replacement.html added
> in this revision is consistently failing on Sierra and High Sierra
> 
> https://build.webkit.org/results/Apple%20Sierra%20Release%20WK2%20(Tests)/
> r225224%20(5987)/results.html
> 
> diff:
> ---
> /Volumes/Data/slave/sierra-release-tests-wk2/build/layout-test-results/fast/
> forms/alternative-presentation-button/replacement-expected.txt
> +++
> /Volumes/Data/slave/sierra-release-tests-wk2/build/layout-test-results/fast/
> forms/alternative-presentation-button/replacement-actual.txt
> @@ -12,9 +12,9 @@
>                    RenderBlock {DIV} at (0,0) size 205x0
>                  RenderBlock {DIV} at (0,0) size 205x26
>                    RenderBlock (anonymous) at (0,0) size 205x13
> -                    RenderInline {SPAN} at (0,0) size 185x13
> -                      RenderText {#text} at (0,0) size 185x13
> -                        text run at (0,0) width 185: "alternative
> presentation button title"
> +                    RenderInline {SPAN} at (0,0) size 186x13
> +                      RenderText {#text} at (0,0) size 186x13
> +                        text run at (0,0) width 186: "alternative
> presentation button title"
>                    RenderBlock {DIV} at (0,13) size 205x13
>                      RenderText {#text} at (0,0) size 205x13
>                        text run at (0,0) width 205: "alternative
> presentation button subtitle"

Committed updated result in <https://trac.webkit.org/changeset/225237>.