[go: nahoru, domu]

blob: f9d8ca7339e5f4670c9d9d57675bdf834c7af985 [file] [log] [blame]
Blink Reformat4c46d092018-04-07 15:32:371/*
2 * Copyright (C) 2012 Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
Tim van der Lippeaa76aa22020-02-14 14:38:2431import * as ARIAUtils from './ARIAUtils.js';
Paul Lewis9950e182019-12-16 16:06:0732import {GlassPane, PointerEventsBehavior} from './GlassPane.js';
Tim van der Lippe80d82652020-08-27 13:53:4433import {InspectorView} from './InspectorView.js';
Paul Lewis9950e182019-12-16 16:06:0734import {KeyboardShortcut, Keys} from './KeyboardShortcut.js';
Tim van der Lippe877f04a2021-07-23 11:02:5435import type {SplitWidget} from './SplitWidget.js';
36import type {DevToolsCloseButton} from './UIUtils.js';
Jack Franklina75ae7c2021-05-11 13:22:5437import type {WidgetElement} from './Widget.js';
38import {WidgetFocusRestorer} from './Widget.js';
Paul Lewis9950e182019-12-16 16:06:0739
40export class Dialog extends GlassPane {
Jan Scheffler01eab3c2021-08-16 17:18:0741 private tabIndexBehavior: OutsideTabIndexBehavior;
42 private tabIndexMap: Map<HTMLElement, number>;
43 private focusRestorer: WidgetFocusRestorer|null;
44 private closeOnEscape: boolean;
45 private targetDocument!: Document|null;
46 private readonly targetDocumentKeyDownHandler: (event: Event) => void;
47 private escapeKeyCallback: ((arg0: Event) => void)|null;
Jan Scheffler26b955e2021-03-29 09:15:0148
John Emau83763642019-07-16 23:56:4249 constructor() {
Blink Reformat4c46d092018-04-07 15:32:3750 super();
Jack Franklin84442c62021-06-28 15:02:0651 this.registerRequiredCSS('ui/legacy/dialog.css');
Blink Reformat4c46d092018-04-07 15:32:3752 this.contentElement.tabIndex = 0;
53 this.contentElement.addEventListener('focus', () => this.widget().focus(), false);
Blink Reformat4c46d092018-04-07 15:32:3754 this.widget().setDefaultFocusedElement(this.contentElement);
Paul Lewis9950e182019-12-16 16:06:0755 this.setPointerEventsBehavior(PointerEventsBehavior.BlockedByGlassPane);
Blink Reformat4c46d092018-04-07 15:32:3756 this.setOutsideClickCallback(event => {
57 this.hide();
58 event.consume(true);
59 });
Tim van der Lippeaa76aa22020-02-14 14:38:2460 ARIAUtils.markAsModalDialog(this.contentElement);
Jan Scheffler01eab3c2021-08-16 17:18:0761 this.tabIndexBehavior = OutsideTabIndexBehavior.DisableAllOutsideTabIndex;
62 this.tabIndexMap = new Map();
63 this.focusRestorer = null;
64 this.closeOnEscape = true;
65 this.targetDocumentKeyDownHandler = this.onKeyDown.bind(this);
66 this.escapeKeyCallback = null;
Blink Reformat4c46d092018-04-07 15:32:3767 }
68
Jan Scheffler26b955e2021-03-29 09:15:0169 static hasInstance(): boolean {
Jan Scheffler01eab3c2021-08-16 17:18:0770 return Boolean(Dialog.instance);
Blink Reformat4c46d092018-04-07 15:32:3771 }
72
Jan Scheffler26b955e2021-03-29 09:15:0173 show(where?: Document | Element): void {
74 const document = (where instanceof Document ? where : (where || InspectorView.instance().element).ownerDocument as Document);
Jan Scheffler01eab3c2021-08-16 17:18:0775 this.targetDocument = document;
76 this.targetDocument.addEventListener('keydown', this.targetDocumentKeyDownHandler, true);
Brian Cui8fdb1482019-12-04 21:41:4677
Jan Scheffler01eab3c2021-08-16 17:18:0778 if (Dialog.instance) {
79 Dialog.instance.hide();
Tim van der Lippe1d6e57a2019-09-30 11:55:3480 }
Jan Scheffler01eab3c2021-08-16 17:18:0781 Dialog.instance = this;
82 this.disableTabIndexOnElements(document);
Blink Reformat4c46d092018-04-07 15:32:3783 super.show(document);
Jan Scheffler01eab3c2021-08-16 17:18:0784 this.focusRestorer = new WidgetFocusRestorer(this.widget());
Blink Reformat4c46d092018-04-07 15:32:3785 }
86
Jan Scheffler26b955e2021-03-29 09:15:0187 hide(): void {
Jan Scheffler01eab3c2021-08-16 17:18:0788 if (this.focusRestorer) {
89 this.focusRestorer.restore();
Simon Zündedf00ce2020-11-20 06:50:5290 }
Blink Reformat4c46d092018-04-07 15:32:3791 super.hide();
Brian Cui8fdb1482019-12-04 21:41:4692
Jan Scheffler01eab3c2021-08-16 17:18:0793 if (this.targetDocument) {
94 this.targetDocument.removeEventListener('keydown', this.targetDocumentKeyDownHandler, true);
Brian Cui8fdb1482019-12-04 21:41:4695 }
Jan Scheffler01eab3c2021-08-16 17:18:0796 this.restoreTabIndexOnElements();
Rob Paveza9e33bc92020-07-13 21:49:3397 this.dispatchEventToListeners('hidden');
Jan Scheffler01eab3c2021-08-16 17:18:0798 Dialog.instance = null;
Blink Reformat4c46d092018-04-07 15:32:3799 }
100
Jan Scheffler26b955e2021-03-29 09:15:01101 setCloseOnEscape(close: boolean): void {
Jan Scheffler01eab3c2021-08-16 17:18:07102 this.closeOnEscape = close;
Blink Reformat4c46d092018-04-07 15:32:37103 }
104
Jan Scheffler26b955e2021-03-29 09:15:01105 setEscapeKeyCallback(callback: (arg0: Event) => void): void {
Jan Scheffler01eab3c2021-08-16 17:18:07106 this.escapeKeyCallback = callback;
Jack Lynchca3683d2020-09-30 22:54:57107 }
108
Jan Scheffler26b955e2021-03-29 09:15:01109 addCloseButton(): void {
110 const closeButton =
111 (this.contentElement.createChild('div', 'dialog-close-button', 'dt-close-button') as DevToolsCloseButton);
Blink Reformat4c46d092018-04-07 15:32:37112 closeButton.gray = true;
113 closeButton.addEventListener('click', () => this.hide(), false);
114 }
115
Jan Scheffler26b955e2021-03-29 09:15:01116 setOutsideTabIndexBehavior(tabIndexBehavior: OutsideTabIndexBehavior): void {
Jan Scheffler01eab3c2021-08-16 17:18:07117 this.tabIndexBehavior = tabIndexBehavior;
Brian Cui8fdb1482019-12-04 21:41:46118 }
119
Jan Scheffler01eab3c2021-08-16 17:18:07120 private disableTabIndexOnElements(document: Document): void {
121 if (this.tabIndexBehavior === OutsideTabIndexBehavior.PreserveTabIndex) {
Brian Cui8fdb1482019-12-04 21:41:46122 return;
123 }
124
Jan Scheffler26b955e2021-03-29 09:15:01125 let exclusionSet: Set<HTMLElement>|(Set<HTMLElement>| null) = (null as Set<HTMLElement>| null);
Jan Scheffler01eab3c2021-08-16 17:18:07126 if (this.tabIndexBehavior === OutsideTabIndexBehavior.PreserveMainViewTabIndex) {
127 exclusionSet = this.getMainWidgetTabIndexElements(InspectorView.instance().ownerSplit());
Brian Cui8fdb1482019-12-04 21:41:46128 }
129
Jan Scheffler01eab3c2021-08-16 17:18:07130 this.tabIndexMap.clear();
Jan Scheffler26b955e2021-03-29 09:15:01131 let node: (Node|null)|Document = document;
Simon Zündedf00ce2020-11-20 06:50:52132 for (; node; node = node.traverseNextNode(document)) {
Blink Reformat4c46d092018-04-07 15:32:37133 if (node instanceof HTMLElement) {
Jan Scheffler26b955e2021-03-29 09:15:01134 const element = (node as HTMLElement);
Blink Reformat4c46d092018-04-07 15:32:37135 const tabIndex = element.tabIndex;
Brian Cui8fdb1482019-12-04 21:41:46136 if (tabIndex >= 0 && (!exclusionSet || !exclusionSet.has(element))) {
Jan Scheffler01eab3c2021-08-16 17:18:07137 this.tabIndexMap.set(element, tabIndex);
Blink Reformat4c46d092018-04-07 15:32:37138 element.tabIndex = -1;
139 }
140 }
141 }
142 }
143
Jan Scheffler01eab3c2021-08-16 17:18:07144 private getMainWidgetTabIndexElements(splitWidget: SplitWidget|null): Set<HTMLElement> {
Jan Scheffler26b955e2021-03-29 09:15:01145 const elementSet = (new Set() as Set<HTMLElement>);
Brian Cui8fdb1482019-12-04 21:41:46146 if (!splitWidget) {
147 return elementSet;
148 }
149
150 const mainWidget = splitWidget.mainWidget();
151 if (!mainWidget || !mainWidget.element) {
152 return elementSet;
153 }
154
Jan Scheffler26b955e2021-03-29 09:15:01155 let node: Node|null|WidgetElement = mainWidget.element;
Simon Zündedf00ce2020-11-20 06:50:52156 for (; node; node = node.traverseNextNode(mainWidget.element)) {
Brian Cui8fdb1482019-12-04 21:41:46157 if (!(node instanceof HTMLElement)) {
158 continue;
159 }
160
Jan Scheffler26b955e2021-03-29 09:15:01161 const element = (node as HTMLElement);
Brian Cui8fdb1482019-12-04 21:41:46162 const tabIndex = element.tabIndex;
163 if (tabIndex < 0) {
164 continue;
165 }
166
167 elementSet.add(element);
168 }
169
170 return elementSet;
171 }
172
Jan Scheffler01eab3c2021-08-16 17:18:07173 private restoreTabIndexOnElements(): void {
174 for (const element of this.tabIndexMap.keys()) {
175 element.tabIndex = (this.tabIndexMap.get(element) as number);
Tim van der Lippe1d6e57a2019-09-30 11:55:34176 }
Jan Scheffler01eab3c2021-08-16 17:18:07177 this.tabIndexMap.clear();
Blink Reformat4c46d092018-04-07 15:32:37178 }
179
Jan Scheffler01eab3c2021-08-16 17:18:07180 private onKeyDown(event: Event): void {
Jan Scheffler26b955e2021-03-29 09:15:01181 const keyboardEvent = (event as KeyboardEvent);
Simon Zündedf00ce2020-11-20 06:50:52182 if (keyboardEvent.keyCode === Keys.Esc.code && KeyboardShortcut.hasNoModifiers(event)) {
Jan Scheffler01eab3c2021-08-16 17:18:07183 if (this.escapeKeyCallback) {
184 this.escapeKeyCallback(event);
Jack Lynchca3683d2020-09-30 22:54:57185 }
186
187 if (event.handled) {
188 return;
189 }
190
Jan Scheffler01eab3c2021-08-16 17:18:07191 if (this.closeOnEscape) {
Jack Lynchca3683d2020-09-30 22:54:57192 event.consume(true);
193 this.hide();
194 }
Blink Reformat4c46d092018-04-07 15:32:37195 }
196 }
Jan Scheffler26b955e2021-03-29 09:15:01197
Jan Scheffler01eab3c2021-08-16 17:18:07198 private static instance: Dialog|null = null;
Tim van der Lippe0830b3d2019-10-03 13:20:07199}
200
Jan Scheffler26b955e2021-03-29 09:15:01201// TODO(crbug.com/1167717): Make this a const enum again
202// eslint-disable-next-line rulesdir/const_enum
203export enum OutsideTabIndexBehavior {
204 DisableAllOutsideTabIndex = 'DisableAllTabIndex',
205 PreserveMainViewTabIndex = 'PreserveMainViewTabIndex',
206 PreserveTabIndex = 'PreserveTabIndex',
207}