Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: apply proxy to ElementInternals object #3742

Merged
merged 8 commits into from
Sep 29, 2023

Conversation

jmsjtu
Copy link
Member

@jmsjtu jmsjtu commented Sep 26, 2023

Details

This PR wraps the ElementInternals object in a proxy object to prevent LWCs that do not set the formAssociated value from being form-associated when placed inside a form.

See comment for more background.

Does this pull request introduce a breaking change?

  • ✅ No, it does not introduce a breaking change.

Does this pull request introduce an observable change?

  • ✅ No, it does not introduce an observable change.

GUS work item

W-13818672

@jmsjtu jmsjtu requested a review from a team as a code owner September 26, 2023 22:06
Comment on lines +348 to +350
return typeof internalsPropertyValue === 'function'
? internalsPropertyValue.bind(target)
: internalsPropertyValue;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is so function invocations like ElementInternals.setFormValue('on', 'checked') is able to be invoked with the correct context (ElementInternals instance).

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TIL. I assumed Proxies just handled the binding for you when you do foo.bar().

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I looked into it, and it seems proxies should handle the this properly.

I think what's happening here is that the browser has some internal logic that is throwing TypeError: Illegal invocation when using the proxy. The bind is a nice workaround for this, although technically it does change the semantics. (E.g. const foo = internals.checkValidity && foo() should not work.) However, this is definitely an edge case.

Googling didn't turn up much, but yeah, seems like bind is the typical workaround.

return attachInternals(elm);
const internals = attachInternals(elm);
// #TODO[2970]: remove proxy once `UpgradeableConstructor` has been removed
return createElementInternalsProxy(internals, Boolean(formAssociated));
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note Boolean(formAssociated) will return true for a false string but that's how it works with vanilla web components too.


if (typeof ElementInternals !== 'undefined') {
// Verify ElementInternals proxy getter throws error.
it('form-related operations and attributes should throw DOMException for non-form-associated custom elements.', () => {
Copy link
Member Author

@jmsjtu jmsjtu Sep 26, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added these sanity tests just to confirm that form-associated behavior works as expected with the proxy.

Copy link
Collaborator

@nolanlawson nolanlawson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This overall LGTM, I will also take another look tomorrow

throw new DOMException(
`Failed to execute '${key}' on 'ElementInternals': The target element is not a form-associated custom element.`
);
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This error may be worth extracting out into a shared utility.

Comment on lines +348 to +350
return typeof internalsPropertyValue === 'function'
? internalsPropertyValue.bind(target)
: internalsPropertyValue;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TIL. I assumed Proxies just handled the binding for you when you do foo.bar().

// Firefox does not support ARIAMixin inside ElementInternals.
// Check to see if ARIAMixin value is defined on ElementInternals before
// testing accessibility.
if (Object.prototype.hasOwnProperty.call(window.ElementInternals.prototype, 'ariaAtomic')) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This may change with Firefox 119. Might be worth testing this in Firefox Nightly.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right it's there in Firefox Nightly! Added a TODO to remove this check once it's officially supported.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm more worried about Fiefox having partial support and it breaking some of our tests.

Do they pass our tests fully?

Copy link
Member Author

@jmsjtu jmsjtu Sep 28, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll need to take a deeper look but at first glance it looks like Firefox nightly breaks all the aria polyfill tests.

The ARIAMixin tests on ElementInternals pass though!

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Opened an issue: #3753

@jmsjtu jmsjtu merged commit 16850ca into jtu/face-callbacks Sep 29, 2023
1 check passed
@jmsjtu jmsjtu deleted the jtu/element-internals-proxy branch September 29, 2023 20:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants