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

Check dialog is modal flag instead of open attribute in 'check popover validity' algorithm #9344

Merged
merged 1 commit into from
Jun 6, 2023

Conversation

nt1m
Copy link
Member

@nt1m nt1m commented May 27, 2023

The following case currently does not fail:

dialog.showModal();
dialog.open = false;
dialog.showPopover();

even though it is a top layer conflict:

  1. showModal pushes to the top layer
  2. .open = false then hides the dialog, while keeping it in top layer
  3. showPopover pushes to the top layer again

The check for the open attribute is supposed to prevent top layer conflicts, since those conflicts can leave the element in a broken state, notably after hiding. For instance, the previous sequence of actions followed by dialog.hidePopover() will leave the is modal flag despite the dialog being completely removed from the top layer.

Also only remove from top layer in HTMLDialogElement.prototype.close() when the is modal flag is
true to prevent that method from removing popovers from the top layer.

Fixes #9335

(See WHATWG Working Mode: Changes for more details.)


/interactive-elements.html ( diff )
/popover.html ( diff )

@nt1m
Copy link
Member Author

nt1m commented May 29, 2023

@josepharhar @mfreed7 One side effect of this PR is that you can now run showPopover() on dialogs that have the open attribute, but aren't modal. This found a small bug in the HTMLDialogElement.prototype.close() algorithm, which was removing from the top layer even when the thing that pushed it into the top layer was showPopover() as opposed to showModal().

The alternative fix would be throwing an exception if you're trying to close a <dialog popover> with HTMLDialogElement.prototype.close(), which I'm also fine doing.

Any opinions?


<li><p>If <var>subject</var> is in its <code>Document</code>'s <span>top layer</span>, then <span
data-x="list remove">remove</span> it.</p></li>
data-x="dom-dialog-returnValue">returnValue</code> attribute to <var>result</var>.</p></li>
Copy link
Member

Choose a reason for hiding this comment

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

Nit: no additional indentation warranted here.

Copy link
Member Author

Choose a reason for hiding this comment

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

Suggested change
data-x="dom-dialog-returnValue">returnValue</code> attribute to <var>result</var>.</p></li>
data-x="dom-dialog-returnValue">returnValue</code> attribute to <var>result</var>.</p></li>

Copy link
Member Author

Choose a reason for hiding this comment

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

@annevk Fixed!

source Outdated
<li><p><var>element</var> is a <code>dialog</code> element and has an <code
data-x="attr-details-open">open</code> attribute</p></li>
<li><p><var>element</var> is a <code>dialog</code> element and its <span>is modal</span> flag
is set to true.</li>
Copy link
Member

Choose a reason for hiding this comment

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

This seems reasonable to me, but why wouldn't we check for either the open attribute or the is modal flag here?

Copy link
Member

Choose a reason for hiding this comment

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

We discussed this a bit more and it makes sense to me that we just care about the top layer conflicts here and not care about potential semantic mismatches.

Copy link
Contributor

Choose a reason for hiding this comment

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

Hmm, can you say more? I was leaning toward your original suggestion, check for either open or modal. It feels weird to "direct upgrade" a non-modal dialog to a popover, but maybe it's not so weird.

Copy link
Member

Choose a reason for hiding this comment

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

It doesn't violate an invariant we're trying to uphold. It's weird, but not really weirder than any of the other things that's possible and doesn't break anything. Whereas top layer conflicts do break things.

If we wanted to make the open attribute more meaningful we'd probably have to do things when folks remove it and such, but it seems a bit too late to start with that now.

@annevk annevk added normative change topic: popover The popover attribute and friends labels May 30, 2023
@nt1m nt1m force-pushed the is-modal-check-popover-validity branch from aaf0fa6 to 1d7a1a1 Compare May 30, 2023 16:31
@mfreed7
Copy link
Contributor

mfreed7 commented May 30, 2023

The alternative fix would be throwing an exception if you're trying to close a <dialog popover> with HTMLDialogElement.prototype.close(), which I'm also fine doing.

I like this approach also. I think generally we should throw for this type of "mixing and matching" where it doesn't seem to make sense.

@annevk
Copy link
Member

annevk commented May 30, 2023

FWIW, I think the currently proposed approach is nicer as it doesn't bake more of popover into the dialog element.

@nt1m
Copy link
Member Author

nt1m commented May 30, 2023

The alternative fix would be throwing an exception if you're trying to close a <dialog popover> with HTMLDialogElement.prototype.close(), which I'm also fine doing.

I like this approach also. I think generally we should throw for this type of "mixing and matching" where it doesn't seem to make sense.

Thinking more, this is tricky, because if you have the sequence:

dialog.show();
dialog.showPopover();
dialog.close();

It wouldn't be unreasonable for dialog.close() to remove the open attribute but keep the dialog in the top layer.

@mfreed7
Copy link
Contributor

mfreed7 commented May 30, 2023

Thinking more, this is tricky, because if you have the sequence:

dialog.show();
dialog.showPopover();
dialog.close();

It wouldn't be unreasonable for dialog.close() to remove the open attribute but keep the dialog in the top layer.

I guess I don't have strong opinions, and I think that's because I think we should eventually deprecate non-modal <dialog> which will clean a lot of this up. But the example above feels quite confusing for developers. I can see this ending up as an "HTML quiz" question: "what state is the dialog in at the end of this code?".

Part 2 is:

dialog.show();
dialog.showPopover();
dialog.close();
dialog.show(); // What happens here?

I think the answer to part 2 is that show() throws, right?

@nt1m
Copy link
Member Author

nt1m commented May 30, 2023

Thinking more, this is tricky, because if you have the sequence:

dialog.show();
dialog.showPopover();
dialog.close();

It wouldn't be unreasonable for dialog.close() to remove the open attribute but keep the dialog in the top layer.

I guess I don't have strong opinions, and I think that's because I think we should eventually deprecate non-modal <dialog> which will clean a lot of this up. But the example above feels quite confusing for developers. I can see this ending up on one of the many "HTML quiz" questions: "what state is the dialog in at the end of this code?". Part 2 is:

dialog.show();
dialog.showPopover();
dialog.close();
dialog.show(); // What happens here?

I think the answer to part 2 is that show() throws, right?

Hmm, yeah, it does throw in that situation. I guess symmetry with show() does make sense. The other part that makes this tricky is that dialogs can be closed via non-JS (via <form method=dialog>), so not sure we can throw an exception when that codepath is invoked.

@mfreed7
Copy link
Contributor

mfreed7 commented May 30, 2023

Hmm, yeah, it does throw in that situation. I guess symmetry with show() does make sense. The other part that makes this tricky is that dialogs can be closed via non-JS (via <form method=dialog>), so not sure we can throw an exception when that codepath is invoked.

Right - I was wondering about <form method=dialog> also. We can't throw on the closing operation, but we can prevent that situation by throwing at showPopover(), right?

@nt1m
Copy link
Member Author

nt1m commented May 30, 2023

@annevk @rwlbuis Do you have a preference here?

@annevk
Copy link
Member

annevk commented Jun 1, 2023

I think the current PR still makes sense.

In addition I would suggest that we remove

If this is in the popover showing state, then throw an "InvalidStateError" DOMException.

from dialog's show() as it's not fundamentally incompatible with also being a popover. As we demonstrate in this PR the salient piece of information is really top layer participation and show() doesn't do that so it should not introduce unnecessary coupling.

@mfreed7
Copy link
Contributor

mfreed7 commented Jun 1, 2023

I think the current PR still makes sense.

In addition I would suggest that we remove

If this is in the popover showing state, then throw an "InvalidStateError" DOMException.

from dialog's show() as it's not fundamentally incompatible with also being a popover. As we demonstrate in this PR the salient piece of information is really top layer participation and show() doesn't do that so it should not introduce unnecessary coupling.

What about these questions? #9344 (comment)

@annevk
Copy link
Member

annevk commented Jun 2, 2023

@mfreed7 there's no question there? If you mean what would happen with the last show() call in the quoted text: if we adopt my suggestion of it not caring about popover that would no longer throw. Which seems good.

@mfreed7
Copy link
Contributor

mfreed7 commented Jun 2, 2023

@mfreed7 there's no question there? If you mean what would happen with the last show() call in the quoted text: if we adopt my suggestion of it not caring about popover that would no longer throw. Which seems good.

Right - that question. Ok, so the proposal is:

  1. dialog.show() just adds the open attribute, which makes it non-display:none, which is a no-op if the dialog is already a showing popover.
  2. dialog.close(), if dialog is non-modal, just removes the open attribute, and re-applies display:none only if the dialog isn't a showing popover.
  3. dialog.showPopover() and dialog.hidePopover() basically ignore the open attribute, and just check whether dialog "is modal". If so, they throw. If not, they do the normal thing, ignoring that the dialog was already showing.

I think that also sounds reasonable. There might be weird cases when trying to use CSS transitions during popover show/hide, but if you're mixing/matching showPopover() and show() like this, you're likely doing something wrong anyway.

So ok by me, I think.

…r validity' algorithm

The following case currently does not fail:

```
dialog.showModal();
dialog.open = false;
dialog.showPopover();
```

even though it is a top layer conflict:

1. `showModal` pushes to the top layer
2. `.open = false` then hides the dialog, while keeping it in top layer
3. `showPopover` pushes to the top layer again

The check for the open attribute is supposed to prevent top layer conflicts, since those conflicts
can leave the element in a broken state, notably after hiding. For instance, the previous sequence
of actions followed by `dialog.hidePopover()` will leave the `is modal` flag despite the dialog
being completely removed from the top layer.

Also only remove from top layer in `HTMLDialogElement.prototype.close()` when the is modal flag is
true to prevent that method from removing popovers from the top layer.

For `dialog.close()` to be symmetric & consistent with `dialog.show()`, we also stop throwing when
`show()` is called on a dialog in the popover showing state.

Fixes whatwg#9335
@nt1m nt1m force-pushed the is-modal-check-popover-validity branch from 1d7a1a1 to b600242 Compare June 5, 2023 03:45
@nt1m
Copy link
Member Author

nt1m commented Jun 5, 2023

I updated the PR with @annevk's suggestion applied.

Copy link
Member

@annevk annevk left a comment

Choose a reason for hiding this comment

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

Do we have tests for this change? If OP could point to them that'd be great.

@nt1m
Copy link
Member Author

nt1m commented Jun 5, 2023

@annevk The WebKit export for WebKit/WebKit#14454 should take care of it.

Copy link
Contributor

@mfreed7 mfreed7 left a comment

Choose a reason for hiding this comment

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

LGTM, thanks!

webkit-commit-queue pushed a commit to rwlbuis/WebKit that referenced this pull request Jun 5, 2023
https://bugs.webkit.org/show_bug.cgi?id=257412

Reviewed by Tim Nguyen.

Check for modal flag instead of open attribute in checkPopoverValidity check:
whatwg/html#9344

* LayoutTests/imported/w3c/web-platform-tests/html/semantics/popovers/popover-top-layer-combinations.html:
* Source/WebCore/html/HTMLDialogElement.cpp:
(WebCore::HTMLDialogElement::show):
(WebCore::HTMLDialogElement::close):
* Source/WebCore/html/HTMLElement.cpp:
(WebCore::checkPopoverValidity):

Canonical link: https://commits.webkit.org/264876@main
@annevk annevk merged commit 278116c into whatwg:main Jun 6, 2023
rwlbuis added a commit to web-platform-tests/wpt that referenced this pull request Jun 6, 2023
Adjust test after issue 9344 is merged:
whatwg/html#9344
rwlbuis added a commit to web-platform-tests/wpt that referenced this pull request Jun 6, 2023
Adjust test after dialog check changed in 'check popover validity':
whatwg/html#9344
rwlbuis added a commit to web-platform-tests/wpt that referenced this pull request Jun 6, 2023
…40394)

Adjust test after dialog check changed in 'check popover validity':
whatwg/html#9344
@nt1m nt1m deleted the is-modal-check-popover-validity branch June 12, 2023 06:54
moz-v2v-gh pushed a commit to mozilla/gecko-dev that referenced this pull request Jun 13, 2023
…a=testonly

Automatic update from web-platform-tests
Adjust test after dialog check changed in 'check popover validity' (#40394)

Adjust test after dialog check changed in 'check popover validity':
whatwg/html#9344
--

wpt-commits: 37d0fc507b5bd89960107d13500ea2e167f1d3e8
wpt-pr: 40394
ErichDonGubler pushed a commit to erichdongubler-mozilla/firefox that referenced this pull request Jun 14, 2023
…a=testonly

Automatic update from web-platform-tests
Adjust test after dialog check changed in 'check popover validity' (#40394)

Adjust test after dialog check changed in 'check popover validity':
whatwg/html#9344
--

wpt-commits: 37d0fc507b5bd89960107d13500ea2e167f1d3e8
wpt-pr: 40394
gecko-dev-updater pushed a commit to marco-c/gecko-dev-comments-removed that referenced this pull request Jun 16, 2023
…a=testonly

Automatic update from web-platform-tests
Adjust test after dialog check changed in 'check popover validity' (#40394)

Adjust test after dialog check changed in 'check popover validity':
whatwg/html#9344
--

wpt-commits: 37d0fc507b5bd89960107d13500ea2e167f1d3e8
wpt-pr: 40394

UltraBlame original commit: 4f4e5646bb7993ea1dbf9008632c73392dec0461
gecko-dev-updater pushed a commit to marco-c/gecko-dev-wordified-and-comments-removed that referenced this pull request Jun 16, 2023
…a=testonly

Automatic update from web-platform-tests
Adjust test after dialog check changed in 'check popover validity' (#40394)

Adjust test after dialog check changed in 'check popover validity':
whatwg/html#9344
--

wpt-commits: 37d0fc507b5bd89960107d13500ea2e167f1d3e8
wpt-pr: 40394

UltraBlame original commit: 4f4e5646bb7993ea1dbf9008632c73392dec0461
gecko-dev-updater pushed a commit to marco-c/gecko-dev-wordified that referenced this pull request Jun 16, 2023
…a=testonly

Automatic update from web-platform-tests
Adjust test after dialog check changed in 'check popover validity' (#40394)

Adjust test after dialog check changed in 'check popover validity':
whatwg/html#9344
--

wpt-commits: 37d0fc507b5bd89960107d13500ea2e167f1d3e8
wpt-pr: 40394

UltraBlame original commit: 4f4e5646bb7993ea1dbf9008632c73392dec0461
mnutt pushed a commit to movableink/webkit that referenced this pull request Jun 28, 2023
https://bugs.webkit.org/show_bug.cgi?id=257412

Reviewed by Tim Nguyen.

Check for modal flag instead of open attribute in checkPopoverValidity check:
whatwg/html#9344

* LayoutTests/imported/w3c/web-platform-tests/html/semantics/popovers/popover-top-layer-combinations.html:
* Source/WebCore/html/HTMLDialogElement.cpp:
(WebCore::HTMLDialogElement::show):
(WebCore::HTMLDialogElement::close):
* Source/WebCore/html/HTMLElement.cpp:
(WebCore::checkPopoverValidity):

Canonical link: https://commits.webkit.org/264876@main
aarongable pushed a commit to chromium/chromium that referenced this pull request Aug 14, 2023
- Replaces the dialog open attribute check in "check popover validity"
  with an open attribute + isModal check
- Removes the exception in dialog.show() which looks at the popover
  state
- Fixes a bug this uncovered in ScheduleForTopLayerRemoval which allowed
  on element to be listed in the removals multiple times

HTML spec PR: whatwg/html#9344

Fixed: 1449442
Change-Id: I74e6189070a1a416d486cbc607f92f964a106d66
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4616893
Commit-Queue: Joey Arhar <[email protected]>
Reviewed-by: Mason Freed <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1183314}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
normative change topic: popover The popover attribute and friends
Development

Successfully merging this pull request may close these issues.

"check popover algorithm" check for open dialogs is weak
3 participants