Skip to content

Commit 88af292

Browse files
mfreed7Chromium LUCI CQ
authored andcommitted
Implement basic structure for dialog light dismiss [1/N]
This CL puts the feature flag in place, adds (flag guarded) closedBy and requestClose() methods to <dialog>, connects the pointer events handling to a new dialog light dismiss method, and adds a basic set of tests. None of the actual functionality is here yet, this is just a shell. Subsequent CLs will flesh out the behavior. See spec PR for details: whatwg/html#10737 Here's the chromestatus: https://chromestatus.com/feature/5097714453577728 Bug: 376516550 Change-Id: I3727ca21476a2a3340fd18597970395d64ef7176 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5985491 Commit-Queue: Mason Freed <masonf@chromium.org> Reviewed-by: David Baron <dbaron@chromium.org> Cr-Commit-Position: refs/heads/main@{#1378633}
1 parent 65bc768 commit 88af292

File tree

17 files changed

+307
-13
lines changed

17 files changed

+307
-13
lines changed

third_party/blink/renderer/core/dom/document.cc

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8016,6 +8016,17 @@ void Document::SetPopoverPointerdownTarget(const HTMLElement* popover) {
80168016
popover_pointerdown_target_ = popover;
80178017
}
80188018

8019+
const HTMLDialogElement* Document::DialogPointerdownTarget() const {
8020+
CHECK(RuntimeEnabledFeatures::HTMLDialogLightDismissEnabled());
8021+
return dialog_pointerdown_target_.Get();
8022+
}
8023+
8024+
void Document::SetDialogPointerdownTarget(const HTMLDialogElement* dialog) {
8025+
CHECK(RuntimeEnabledFeatures::HTMLDialogLightDismissEnabled());
8026+
DCHECK(!dialog || dialog->IsOpen());
8027+
dialog_pointerdown_target_ = dialog;
8028+
}
8029+
80198030
void Document::exitPointerLock() {
80208031
if (!GetPage())
80218032
return;
@@ -8730,6 +8741,7 @@ void Document::Trace(Visitor* visitor) const {
87308741
visitor->Trace(popover_auto_stack_);
87318742
visitor->Trace(popover_hint_stack_);
87328743
visitor->Trace(popover_pointerdown_target_);
8744+
visitor->Trace(dialog_pointerdown_target_);
87338745
visitor->Trace(popovers_waiting_to_hide_);
87348746
visitor->Trace(all_open_popovers_);
87358747
visitor->Trace(document_part_root_);

third_party/blink/renderer/core/dom/document.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1651,6 +1651,8 @@ class CORE_EXPORT Document : public ContainerNode,
16511651
return popover_pointerdown_target_.Get();
16521652
}
16531653
void SetPopoverPointerdownTarget(const HTMLElement*);
1654+
const HTMLDialogElement* DialogPointerdownTarget() const;
1655+
void SetDialogPointerdownTarget(const HTMLDialogElement*);
16541656

16551657
// https://crbug.com/1453291
16561658
// The DOM Parts API:
@@ -2702,6 +2704,11 @@ class CORE_EXPORT Document : public ContainerNode,
27022704
HeapVector<Member<HTMLElement>> popover_hint_stack_;
27032705
// The popover (if any) that received the most recent pointerdown event.
27042706
Member<const HTMLElement> popover_pointerdown_target_;
2707+
// The dialog (if any) that received the most recent pointerdown event. This
2708+
// is distinct from popover_pointerdown_target_ because the same pointer
2709+
// action could trigger light dismiss on a containing popover and not a
2710+
// containing dialog, or vice versa.
2711+
Member<const HTMLDialogElement> dialog_pointerdown_target_;
27052712
// A set of popovers for which hidePopover() has been called, but animations
27062713
// are still running.
27072714
HeapHashSet<Member<HTMLElement>> popovers_waiting_to_hide_;

third_party/blink/renderer/core/html/closewatcher/close_watcher.cc

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,10 @@ bool CloseWatcher::requestClose() {
207207
if (IsClosed() || dispatching_cancel_ || !DomWindow()) {
208208
return true;
209209
}
210+
if (!enabled_) {
211+
CHECK(RuntimeEnabledFeatures::HTMLDialogLightDismissEnabled());
212+
return true;
213+
}
210214

211215
WatcherStack& stack = *DomWindow()->closewatcher_stack();
212216
Event& cancel_event =
@@ -231,10 +235,11 @@ bool CloseWatcher::requestClose() {
231235
}
232236

233237
void CloseWatcher::close() {
234-
if (IsClosed()) {
238+
if (IsClosed() || !DomWindow() || !DomWindow()->document()->IsActive()) {
235239
return;
236240
}
237-
if (!DomWindow() || !DomWindow()->document()->IsActive()) {
241+
if (!enabled_) {
242+
CHECK(RuntimeEnabledFeatures::HTMLDialogLightDismissEnabled());
238243
return;
239244
}
240245

third_party/blink/renderer/core/html/closewatcher/close_watcher.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ class CloseWatcher final : public EventTarget, public ExecutionContextClient {
9292
enum class State { kActive, kClosed };
9393
State state_ = State::kActive;
9494
bool dispatching_cancel_ = false;
95+
bool enabled_ = true;
9596
Member<AbortSignal::AlgorithmHandle> abort_handle_;
9697
};
9798

third_party/blink/renderer/core/html/html_attribute_names.json5

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
"class",
5151
"classid",
5252
"clear",
53+
"closedby",
5354
"code",
5455
"codebase",
5556
"codetype",

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

Lines changed: 78 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -146,19 +146,19 @@ void HTMLDialogElement::close(const String& return_value,
146146
}
147147
base::AutoReset<bool> reset_close(&is_closing_, true);
148148

149-
if (!ignore_open_attribute && !FastHasAttribute(html_names::kOpenAttr)) {
149+
if (!ignore_open_attribute && !IsOpen()) {
150150
return;
151151
}
152152

153153
Document& document = GetDocument();
154154
HTMLDialogElement* old_modal_dialog = document.ActiveModalDialog();
155155

156156
DispatchToggleEvents(/*opening=*/false);
157-
if (!ignore_open_attribute && !FastHasAttribute(html_names::kOpenAttr)) {
157+
if (!ignore_open_attribute && !IsOpen()) {
158158
return;
159159
}
160160
SetBooleanAttribute(html_names::kOpenAttr, false);
161-
bool was_modal = is_modal_;
161+
bool was_modal = IsModal();
162162
SetIsModal(false);
163163

164164
// If this dialog is open as a non-modal dialog and open as a popover at the
@@ -199,6 +199,44 @@ void HTMLDialogElement::close(const String& return_value,
199199
}
200200
}
201201

202+
void HTMLDialogElement::requestClose(const String& return_value) {
203+
CHECK(RuntimeEnabledFeatures::HTMLDialogLightDismissEnabled());
204+
// TODO(crbug.com/376516550): Implement this function.
205+
}
206+
207+
String HTMLDialogElement::closedBy() const {
208+
CHECK(RuntimeEnabledFeatures::HTMLDialogLightDismissEnabled());
209+
// TODO(crbug.com/376516550): This should be "limited to only known values".
210+
return "Unimplemented";
211+
}
212+
void HTMLDialogElement::setClosedBy(const String& new_value) {
213+
CHECK(RuntimeEnabledFeatures::HTMLDialogLightDismissEnabled());
214+
// TODO(crbug.com/376516550): This should be "limited to only known values".
215+
}
216+
217+
// static
218+
void HTMLDialogElement::HandleDialogLightDismiss(const Event& event,
219+
const Node& target_node) {
220+
// TODO(crbug.com/376516550): Implement spec behavior:
221+
// 1. Assert: event's isTrusted attribute is true.
222+
// 2. Let target be event's target.
223+
// 3. Let document be target's node document.
224+
// 4. If document's light dismissible dialog list is empty, then return.
225+
// 5. If event is a PointerEvent and event's type is "pointerdown", then: set
226+
// document's dialog pointerdown target to the result of running topmost
227+
// clicked dialog given target.
228+
// 6. If event is a PointerEvent and event's type is " pointerup ", then:
229+
// 1. Let clickedDialog be the result of running topmost clicked dialog given
230+
// target.
231+
// 2. Let topDialog be document's light dismissible dialog list's last
232+
// element.
233+
// 3. Let sameTarget be true if clickedDialog is dialog pointerdown target.
234+
// 4. Let clickedTopDialog be true if clickedDialog is topDialog ,
235+
// 5. Set document's dialog pointerdown target to null.
236+
// 6. If clickedTopDialog is true or sameTarget is false, then return.
237+
// 7. Perform request to close the dialog given topDialog.
238+
}
239+
202240
bool HTMLDialogElement::IsValidBuiltinCommand(HTMLElement& invoker,
203241
CommandEventType command) {
204242
return HTMLElement::IsValidBuiltinCommand(invoker, command) ||
@@ -223,7 +261,7 @@ bool HTMLDialogElement::HandleCommandInternal(HTMLElement& invoker,
223261
return false;
224262
}
225263

226-
bool open = FastHasAttribute(html_names::kOpenAttr);
264+
bool open = IsOpen();
227265

228266
if (command == CommandEventType::kClose) {
229267
if (open) {
@@ -263,8 +301,8 @@ void HTMLDialogElement::ScheduleCloseEvent() {
263301
}
264302

265303
void HTMLDialogElement::show(ExceptionState& exception_state) {
266-
if (FastHasAttribute(html_names::kOpenAttr)) {
267-
if (is_modal_) {
304+
if (IsOpen()) {
305+
if (IsModal()) {
268306
exception_state.ThrowDOMException(
269307
DOMExceptionCode::kInvalidStateError,
270308
"The dialog is already open as a modal dialog, and therefore "
@@ -278,6 +316,17 @@ void HTMLDialogElement::show(ExceptionState& exception_state) {
278316
}
279317
SetBooleanAttribute(html_names::kOpenAttr, true);
280318

319+
if (RuntimeEnabledFeatures::HTMLDialogLightDismissEnabled()) {
320+
// TODO(crbug.com/376516550): Implement spec behavior:
321+
// - Set this's close watcher to the result of establishing a close watcher
322+
// given this's relevant global object, with:
323+
// - cancelAction being to return the result of firing an event named
324+
// cancel at this, with the cancelable attribute initialized to true.
325+
// - closeAction being to close the dialog given this and null.
326+
// - enabled being true if this's closedby attribute state is Any or Close
327+
// Request; otherwise false.
328+
}
329+
281330
// The layout must be updated here because setFocusForDialog calls
282331
// Element::isFocusable, which requires an up-to-date layout.
283332
GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kJavaScript);
@@ -335,8 +384,8 @@ class DialogCloseWatcherEventListener : public NativeEventListener {
335384
};
336385

337386
void HTMLDialogElement::showModal(ExceptionState& exception_state) {
338-
if (FastHasAttribute(html_names::kOpenAttr)) {
339-
if (!is_modal_) {
387+
if (IsOpen()) {
388+
if (!IsModal()) {
340389
exception_state.ThrowDOMException(
341390
DOMExceptionCode::kInvalidStateError,
342391
"The dialog is already open as a non-modal dialog, and therefore "
@@ -376,9 +425,15 @@ void HTMLDialogElement::showModal(ExceptionState& exception_state) {
376425

377426
document.AddToTopLayer(this);
378427
SetBooleanAttribute(html_names::kOpenAttr, true);
379-
380428
SetIsModal(true);
381429

430+
if (RuntimeEnabledFeatures::HTMLDialogLightDismissEnabled()) {
431+
// TODO(crbug.com/376516550): Implement spec behavior:
432+
// Set this's close watcher to...
433+
// - enabled being true if this's closedby attribute is in the Any, Close
434+
// Request, or Auto state; otherwise false.
435+
}
436+
382437
// Refresh the AX cache first, because most of it is changing.
383438
InertSubtreesChanged(document, old_modal_dialog);
384439
document.UpdateStyleAndLayout(DocumentUpdateReason::kJavaScript);
@@ -461,7 +516,7 @@ void HTMLDialogElement::SetFocusForDialog() {
461516

462517
if (control->IsFocusable())
463518
control->Focus();
464-
else if (is_modal_) {
519+
else if (IsModal()) {
465520
control->GetDocument().ClearFocusedElement();
466521
}
467522

@@ -500,7 +555,7 @@ bool HTMLDialogElement::DispatchToggleEvents(bool opening, bool asModal) {
500555
return false;
501556
}
502557
if (opening) {
503-
if (FastHasAttribute(html_names::kOpenAttr)) {
558+
if (IsOpen()) {
504559
return false;
505560
}
506561
if (asModal &&
@@ -552,6 +607,18 @@ void HTMLDialogElement::ParseAttribute(
552607
close(/*return_value=*/String(), /*ignore_open_attribute=*/true);
553608
}
554609

610+
if (RuntimeEnabledFeatures::HTMLDialogLightDismissEnabled() &&
611+
params.name == html_names::kClosedbyAttr) {
612+
// TODO(crbug.com/376516550): Implement spec behavior:
613+
// 3. If element has no open attribute, then return.
614+
// 4. If oldValue and value are in the same state, then return.
615+
// 5. Assert : element's close watcher is not null.
616+
// 6. If value is in the Any state, or Close Request state, or Auto state
617+
// and element's is modal flag is true, then let enabled to true;
618+
// otherwise false.
619+
// 7. Set element's close watcher's enabled boolean to enabled.
620+
}
621+
555622
HTMLElement::ParseAttribute(params);
556623
}
557624

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,17 +48,25 @@ class CORE_EXPORT HTMLDialogElement final : public HTMLElement {
4848

4949
void close(const String& return_value = String(),
5050
bool ignore_open_attribute = false);
51+
void requestClose(const String& return_value = String());
5152
void show(ExceptionState&);
5253
void showModal(ExceptionState&);
5354
void RemovedFrom(ContainerNode&) override;
5455

5556
bool IsModal() const { return is_modal_; }
57+
bool IsOpen() const { return FastHasAttribute(html_names::kOpenAttr); }
5658

5759
String returnValue() const { return return_value_; }
5860
void setReturnValue(const String& return_value) {
5961
return_value_ = return_value;
6062
}
6163

64+
String closedBy() const;
65+
void setClosedBy(const String& return_value);
66+
67+
static void HandleDialogLightDismiss(const Event& event,
68+
const Node& target_node);
69+
6270
void CloseWatcherFiredCancel(Event*);
6371
void CloseWatcherFiredClose();
6472

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,9 @@
3030
] interface HTMLDialogElement : HTMLElement {
3131
[CEReactions, Reflect] attribute boolean open;
3232
attribute DOMString returnValue;
33+
[CEReactions,RuntimeEnabled=HTMLDialogLightDismiss] attribute DOMString closedBy;
3334
[CEReactions, Measure, RaisesException] void show();
3435
[CEReactions, Measure, RaisesException] void showModal();
3536
[CEReactions] void close(optional DOMString returnValue);
37+
[CEReactions,RuntimeEnabled=HTMLDialogLightDismiss] void requestClose(optional DOMString returnValue);
3638
};

third_party/blink/renderer/core/input/pointer_event_manager.cc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "third_party/blink/renderer/core/frame/local_frame.h"
2222
#include "third_party/blink/renderer/core/frame/local_frame_view.h"
2323
#include "third_party/blink/renderer/core/html/canvas/html_canvas_element.h"
24+
#include "third_party/blink/renderer/core/html/html_dialog_element.h"
2425
#include "third_party/blink/renderer/core/input/event_handler.h"
2526
#include "third_party/blink/renderer/core/input/event_handling_util.h"
2627
#include "third_party/blink/renderer/core/input/mouse_event_manager.h"
@@ -190,6 +191,7 @@ WebInputEventResult PointerEventManager::DispatchPointerEvent(
190191
if (Node* target_node = target->ToNode()) {
191192
if (event_type == event_type_names::kPointerdown ||
192193
event_type == event_type_names::kPointerup) {
194+
HTMLDialogElement::HandleDialogLightDismiss(*pointer_event, *target_node);
193195
HTMLElement::HandlePopoverLightDismiss(*pointer_event, *target_node);
194196
}
195197
}

third_party/blink/renderer/platform/runtime_enabled_features.json5

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2310,6 +2310,11 @@
23102310
name: "HTMLAnchorAttribute",
23112311
status: "experimental",
23122312
},
2313+
{
2314+
// TODO(crbug.com/376516550): Enables dialog light dismiss functionality.
2315+
name: "HTMLDialogLightDismiss",
2316+
status: "experimental",
2317+
},
23132318
// The `<embed>` should follow the specification, layout is not forced
23142319
// when the `type` attribute is set to `image`.
23152320
// https://html.spec.whatwg.org/C/#the-embed-element

0 commit comments

Comments
 (0)