Skip to content

Commit 69a2d95

Browse files
josepharharChromium LUCI CQ
authored and
Chromium LUCI CQ
committed
Don't throw when popovers and dialogs are in requested state
This is being changed in the HTML spec here: whatwg/html#9142 Change-Id: Ib8aaaf314c2a1de5d082494e5172e029d531c8e8 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4494823 Reviewed-by: Mason Freed <[email protected]> Commit-Queue: Joey Arhar <[email protected]> Cr-Commit-Position: refs/heads/main@{#1144952}
1 parent 7d0ccaa commit 69a2d95

File tree

8 files changed

+133
-42
lines changed

8 files changed

+133
-42
lines changed

third_party/blink/renderer/core/html/html_dialog_element.cc

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -188,8 +188,21 @@ void HTMLDialogElement::ScheduleCloseEvent() {
188188
}
189189

190190
void HTMLDialogElement::show(ExceptionState& exception_state) {
191-
if (FastHasAttribute(html_names::kOpenAttr))
192-
return;
191+
if (RuntimeEnabledFeatures::PopoverDialogDontThrowEnabled()) {
192+
if (FastHasAttribute(html_names::kOpenAttr)) {
193+
if (is_modal_) {
194+
exception_state.ThrowDOMException(
195+
DOMExceptionCode::kInvalidStateError,
196+
"The dialog is already open as a modal dialog, and therefore "
197+
"cannot be opened as a non-modal dialog.");
198+
}
199+
return;
200+
}
201+
} else {
202+
if (FastHasAttribute(html_names::kOpenAttr)) {
203+
return;
204+
}
205+
}
193206

194207
if (RuntimeEnabledFeatures::HTMLPopoverAttributeEnabled(
195208
GetDocument().GetExecutionContext()) &&
@@ -247,12 +260,24 @@ class DialogCloseWatcherEventListener : public NativeEventListener {
247260
};
248261

249262
void HTMLDialogElement::showModal(ExceptionState& exception_state) {
250-
if (FastHasAttribute(html_names::kOpenAttr)) {
251-
return exception_state.ThrowDOMException(
252-
DOMExceptionCode::kInvalidStateError,
253-
"The element already has an 'open' "
254-
"attribute, and therefore cannot be "
255-
"opened modally.");
263+
if (RuntimeEnabledFeatures::PopoverDialogDontThrowEnabled()) {
264+
if (FastHasAttribute(html_names::kOpenAttr)) {
265+
if (!is_modal_) {
266+
exception_state.ThrowDOMException(
267+
DOMExceptionCode::kInvalidStateError,
268+
"The dialog is already open as a non-modal dialog, and therefore "
269+
"cannot be opened as a modal dialog.");
270+
}
271+
return;
272+
}
273+
} else {
274+
if (FastHasAttribute(html_names::kOpenAttr)) {
275+
return exception_state.ThrowDOMException(
276+
DOMExceptionCode::kInvalidStateError,
277+
"The element already has an 'open' "
278+
"attribute, and therefore cannot be "
279+
"opened modally.");
280+
}
256281
}
257282
if (!isConnected()) {
258283
return exception_state.ThrowDOMException(

third_party/blink/renderer/core/html/html_element.cc

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1330,31 +1330,35 @@ bool HTMLElement::IsPopoverReady(PopoverTriggerAction action,
13301330
"value for the 'popover' attribute.");
13311331
return false;
13321332
}
1333-
if (!isConnected()) {
1334-
maybe_throw_exception(DOMExceptionCode::kInvalidStateError,
1335-
"Invalid on disconnected popover elements.");
1336-
return false;
1337-
}
1338-
if (expected_document && &GetDocument() != expected_document) {
1339-
maybe_throw_exception(DOMExceptionCode::kInvalidStateError,
1340-
"Invalid when the document changes while showing or "
1341-
"hiding a popover element.");
1342-
return false;
1343-
}
13441333
if (action == PopoverTriggerAction::kShow &&
13451334
GetPopoverData()->visibilityState() != PopoverVisibilityState::kHidden) {
1346-
maybe_throw_exception(DOMExceptionCode::kInvalidStateError,
1347-
"Invalid on popover elements which aren't hidden.");
1335+
if (!RuntimeEnabledFeatures::PopoverDialogDontThrowEnabled()) {
1336+
maybe_throw_exception(DOMExceptionCode::kInvalidStateError,
1337+
"Invalid on popover elements which aren't hidden.");
1338+
}
13481339
return false;
13491340
}
13501341
if (action == PopoverTriggerAction::kHide &&
13511342
GetPopoverData()->visibilityState() != PopoverVisibilityState::kShowing) {
13521343
// Important to check that visibility is not kShowing (rather than
13531344
// popoverOpen()), because a hide transition might have been started on this
13541345
// popover already, and we don't want to allow a double-hide.
1355-
maybe_throw_exception(
1356-
DOMExceptionCode::kInvalidStateError,
1357-
"Invalid on popover elements that aren't already showing.");
1346+
if (!RuntimeEnabledFeatures::PopoverDialogDontThrowEnabled()) {
1347+
maybe_throw_exception(
1348+
DOMExceptionCode::kInvalidStateError,
1349+
"Invalid on popover elements that aren't already showing.");
1350+
}
1351+
return false;
1352+
}
1353+
if (!isConnected()) {
1354+
maybe_throw_exception(DOMExceptionCode::kInvalidStateError,
1355+
"Invalid on disconnected popover elements.");
1356+
return false;
1357+
}
1358+
if (expected_document && &GetDocument() != expected_document) {
1359+
maybe_throw_exception(DOMExceptionCode::kInvalidStateError,
1360+
"Invalid when the document changes while showing or "
1361+
"hiding a popover element.");
13581362
return false;
13591363
}
13601364
if (action == PopoverTriggerAction::kShow && IsA<HTMLDialogElement>(this) &&

third_party/blink/renderer/platform/runtime_enabled_features.json5

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2697,6 +2697,13 @@
26972697
name: "PointerEventDeviceId",
26982698
status: "test",
26992699
},
2700+
// PopoverDialogDontThrow makes popover and dialog elements stop throwing
2701+
// exceptions when calling their show and hide methods when they are
2702+
// already in their requested state. See https://github.com/whatwg/html/pull/9142
2703+
{
2704+
name: "PopoverDialogDontThrow",
2705+
status: "stable",
2706+
},
27002707
{
27012708
name: "Portals",
27022709
// Portals must be enabled by blink::features::kPortals as we require the
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<!DOCTYPE html>
2+
<link rel=author href="mailto:[email protected]">
3+
<link rel=help href="https://github.com/whatwg/html/pull/9142">
4+
<script src="/resources/testharness.js"></script>
5+
<script src="/resources/testharnessreport.js"></script>
6+
7+
<dialog>hello</dialog>
8+
9+
<script>
10+
test(() => {
11+
const dialog = document.querySelector('dialog');
12+
13+
// calling close() on a dialog that is already closed should not throw.
14+
dialog.close();
15+
16+
dialog.show();
17+
// calling show() on a dialog that is already showing non-modal should not throw.
18+
dialog.show();
19+
assert_throws_dom('InvalidStateError', () => dialog.showModal(),
20+
'Calling showModal() on a dialog that is already showing non-modal should throw.');
21+
dialog.close();
22+
23+
dialog.showModal();
24+
assert_throws_dom('InvalidStateError', () => dialog.show(),
25+
'Calling show() on a dialog that is already showing modal should throw.');
26+
// calling showModal() on a dialog that is already showing modal should not throw.
27+
dialog.showModal();
28+
});
29+
</script>

third_party/blink/web_tests/external/wpt/html/semantics/popovers/popover-attribute-basic.html

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -254,11 +254,21 @@
254254
},{once: true});
255255
assert_true(popover.matches(':popover-open'));
256256
assert_true(other_popover.matches(':popover-open'));
257-
assert_throws_dom('InvalidStateError', () => popover.hidePopover());
257+
popover.hidePopover(); // Calling hidePopover on a hidden popover should not throw.
258258
assert_false(other_popover.matches(':popover-open'),'unrelated popover is hidden');
259259
assert_false(popover.matches(':popover-open'),'popover is still hidden if its type changed during hide event');
260-
assert_throws_dom("InvalidStateError",() => other_popover.hidePopover(),'Nested popover should already be hidden');
261-
},`Changing the popover type in a "beforetoggle" event handler should throw an exception (during hidePopover())`);
260+
other_popover.hidePopover(); // Calling hidePopover on a hidden popover should not throw.
261+
},`Changing the popover type in a "beforetoggle" event handler during hidePopover() should not throw an exception`);
262+
263+
test(t => {
264+
const popover = document.createElement('div');
265+
assert_throws_dom('NotSupportedError', () => popover.hidePopover(),
266+
'Calling hidePopover on an element without a popover attribute should throw.');
267+
popover.setAttribute('popover', 'auto');
268+
popover.hidePopover(); // Calling hidePopover on a disconnected popover should not throw.
269+
assert_throws_dom('InvalidStateError', () => popover.showPopover(),
270+
'Calling showPopover on a disconnected popover should throw.');
271+
},'Calling hidePopover on a disconnected popover should not throw.');
262272

263273
function interpretedType(typeString,method) {
264274
if (validTypes.includes(typeString))

third_party/blink/web_tests/external/wpt/html/semantics/popovers/popover-light-dismiss.html

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -532,7 +532,7 @@
532532
p14.hidePopover();
533533
},{once:true});
534534
assert_true(p13.matches(':popover-open') && p14.matches(':popover-open') && p15.matches(':popover-open'),'all three should be open');
535-
assert_throws_dom('InvalidStateError',() => p14.hidePopover(),'should throw because the event listener has already hidden the popover');
535+
p14.hidePopover();
536536
assert_true(p13.matches(':popover-open'),'p13 should still be open');
537537
assert_false(p14.matches(':popover-open'));
538538
assert_false(p15.matches(':popover-open'));
@@ -579,10 +579,7 @@
579579
p20.showPopover();
580580
});
581581
p20.addEventListener('beforetoggle', logEvents);
582-
// Because the `beforetoggle` handler shows a different popover,
583-
// and that action closes the p19 popover, the call to hidePopover()
584-
// will result in an exception.
585-
assert_throws_dom('InvalidStateError',() => p19.hidePopover());
582+
p19.hidePopover();
586583
assert_array_equals(events,['hide p19','show p20'],'There should not be a second hide event for 19');
587584
assert_false(p19.matches(':popover-open'));
588585
assert_true(p20.matches(':popover-open'));

third_party/blink/web_tests/external/wpt/html/semantics/popovers/popover-move-documents.html

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,30 @@
44
<script src="/resources/testharness.js"></script>
55
<script src="/resources/testharnessreport.js"></script>
66

7+
<script>
8+
async function iframeLoaded(iframe) {
9+
return new Promise(resolve => {
10+
if (iframe.contentWindow.document.readyState == 'complete') {
11+
resolve();
12+
} else {
13+
iframe.onload = resolve;
14+
}
15+
});
16+
}
17+
</script>
18+
719
<iframe id=myframe srcdoc="<p>iframe</p>"></iframe>
820
<div id=p1 popover=auto>p1</div>
921
<script>
10-
test(() => {
22+
promise_test(async () => {
23+
await iframeLoaded(myframe);
24+
await new Promise(resolve => {
25+
if (myframe.contentWindow.document.readyState == 'complete') {
26+
resolve();
27+
} else {
28+
29+
}
30+
});
1131
p1.addEventListener('beforetoggle', () => {
1232
myframe.contentWindow.document.body.appendChild(p1);
1333
});
@@ -18,7 +38,8 @@
1838
<iframe id=myframe2 srcdoc="<p>iframe</p>"></iframe>
1939
<div id=p2 popover=auto>p2</div>
2040
<script>
21-
test(() => {
41+
promise_test(async () => {
42+
await iframeLoaded(myframe2);
2243
const p2 = document.getElementById('p2');
2344
p2.showPopover();
2445
p2.addEventListener('beforetoggle', () => {
@@ -27,10 +48,7 @@
2748
assert_true(p2.matches(':popover-open'),
2849
'The popover should be open after calling showPopover()');
2950

30-
// Because the `beforetoggle` handler changes the document,
31-
// and that action closes the popover, the call to hidePopover()
32-
// will result in an exception.
33-
assert_throws_dom('InvalidStateError',() => p2.hidePopover());
51+
p2.hidePopover();
3452
assert_false(p2.matches(':popover-open'),
3553
'The popover should be closed after moving it between documents.');
3654
}, 'Moving popovers between documents while hiding should not throw an exception.');
@@ -43,7 +61,8 @@
4361
<div id=p5 popover=auto>p5</div>
4462
</div>
4563
<script>
46-
test(() => {
64+
promise_test(async () => {
65+
await iframeLoaded(myframe3);
4766
p3.showPopover();
4867
p4.showPopover();
4968
p4.addEventListener('beforetoggle', event => {

third_party/blink/web_tests/external/wpt/html/semantics/popovers/resources/popover-utils.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -153,10 +153,10 @@ function assertIsFunctionalPopover(popover, checkVisibility) {
153153
assertPopoverVisibility(popover, /*isPopover*/true, /*expectedVisibility*/false, 'A popover should start out hidden');
154154
popover.showPopover();
155155
if (checkVisibility) assertPopoverVisibility(popover, /*isPopover*/true, /*expectedVisibility*/true, 'After showPopover(), a popover should be visible');
156-
assert_throws_dom("InvalidStateError",() => popover.showPopover(),'Calling showPopover on a showing popover should throw InvalidStateError');
156+
popover.showPopover(); // Calling showPopover on a showing popover should not throw.
157157
popover.hidePopover();
158158
if (checkVisibility) assertPopoverVisibility(popover, /*isPopover*/true, /*expectedVisibility*/false, 'After hidePopover(), a popover should be hidden');
159-
assert_throws_dom("InvalidStateError",() => popover.hidePopover(),'Calling hidePopover on a hidden popover should throw InvalidStateError');
159+
popover.hidePopover(); // Calling hidePopover on a hidden popover should not throw.
160160
popover.togglePopover();
161161
if (checkVisibility) assertPopoverVisibility(popover, /*isPopover*/true, /*expectedVisibility*/true, 'After togglePopover() on hidden popover, it should be visible');
162162
popover.togglePopover();
@@ -172,7 +172,7 @@ function assertIsFunctionalPopover(popover, checkVisibility) {
172172
const parent = popover.parentElement;
173173
popover.remove();
174174
assert_throws_dom("InvalidStateError",() => popover.showPopover(),'Calling showPopover on a disconnected popover should throw InvalidStateError');
175-
assert_throws_dom("InvalidStateError",() => popover.hidePopover(),'Calling hidePopover on a disconnected popover should throw InvalidStateError');
175+
popover.hidePopover(); // Calling hidePopover on a disconnected popover should not throw.
176176
assert_throws_dom("InvalidStateError",() => popover.togglePopover(),'Calling hidePopover on a disconnected popover should throw InvalidStateError');
177177
parent.appendChild(popover);
178178
}

0 commit comments

Comments
 (0)