wicg-inert 3.0.1 → 3.1.1

Sign up to get free protection for your applications and to get access to all the features.
package/src/inert.js CHANGED
@@ -3,730 +3,737 @@
3
3
  * (http://www.w3.org/Consortium/Legal/2015/copyright-software-and-document).
4
4
  */
5
5
 
6
- // Convenience function for converting NodeLists.
7
- /** @type {typeof Array.prototype.slice} */
8
- const slice = Array.prototype.slice;
6
+ (function() {
7
+ // Return early if we're not running inside of the browser.
8
+ if (typeof window === 'undefined') {
9
+ return;
10
+ }
9
11
 
10
- /**
11
- * IE has a non-standard name for "matches".
12
- * @type {typeof Element.prototype.matches}
13
- */
14
- const matches =
15
- Element.prototype.matches || Element.prototype.msMatchesSelector;
16
-
17
- /** @type {string} */
18
- const _focusableElementsString = ['a[href]',
19
- 'area[href]',
20
- 'input:not([disabled])',
21
- 'select:not([disabled])',
22
- 'textarea:not([disabled])',
23
- 'button:not([disabled])',
24
- 'details',
25
- 'summary',
26
- 'iframe',
27
- 'object',
28
- 'embed',
29
- '[contenteditable]'].join(',');
12
+ // Convenience function for converting NodeLists.
13
+ /** @type {typeof Array.prototype.slice} */
14
+ const slice = Array.prototype.slice;
30
15
 
31
- /**
32
- * `InertRoot` manages a single inert subtree, i.e. a DOM subtree whose root element has an `inert`
33
- * attribute.
34
- *
35
- * Its main functions are:
36
- *
37
- * - to create and maintain a set of managed `InertNode`s, including when mutations occur in the
38
- * subtree. The `makeSubtreeUnfocusable()` method handles collecting `InertNode`s via registering
39
- * each focusable node in the subtree with the singleton `InertManager` which manages all known
40
- * focusable nodes within inert subtrees. `InertManager` ensures that a single `InertNode`
41
- * instance exists for each focusable node which has at least one inert root as an ancestor.
42
- *
43
- * - to notify all managed `InertNode`s when this subtree stops being inert (i.e. when the `inert`
44
- * attribute is removed from the root node). This is handled in the destructor, which calls the
45
- * `deregister` method on `InertManager` for each managed inert node.
46
- */
47
- class InertRoot {
48
16
  /**
49
- * @param {!Element} rootElement The Element at the root of the inert subtree.
50
- * @param {!InertManager} inertManager The global singleton InertManager object.
17
+ * IE has a non-standard name for "matches".
18
+ * @type {typeof Element.prototype.matches}
51
19
  */
52
- constructor(rootElement, inertManager) {
53
- /** @type {!InertManager} */
54
- this._inertManager = inertManager;
55
-
56
- /** @type {!Element} */
57
- this._rootElement = rootElement;
58
-
59
- /**
60
- * @type {!Set<!InertNode>}
61
- * All managed focusable nodes in this InertRoot's subtree.
62
- */
63
- this._managedNodes = new Set();
64
-
65
- // Make the subtree hidden from assistive technology
66
- if (this._rootElement.hasAttribute('aria-hidden')) {
67
- /** @type {?string} */
68
- this._savedAriaHidden = this._rootElement.getAttribute('aria-hidden');
69
- } else {
70
- this._savedAriaHidden = null;
71
- }
72
- this._rootElement.setAttribute('aria-hidden', 'true');
73
-
74
- // Make all focusable elements in the subtree unfocusable and add them to _managedNodes
75
- this._makeSubtreeUnfocusable(this._rootElement);
76
-
77
- // Watch for:
78
- // - any additions in the subtree: make them unfocusable too
79
- // - any removals from the subtree: remove them from this inert root's managed nodes
80
- // - attribute changes: if `tabindex` is added, or removed from an intrinsically focusable
81
- // element, make that node a managed node.
82
- this._observer = new MutationObserver(this._onMutation.bind(this));
83
- this._observer.observe(this._rootElement, {attributes: true, childList: true, subtree: true});
84
- }
20
+ const matches =
21
+ Element.prototype.matches || Element.prototype.msMatchesSelector;
22
+
23
+ /** @type {string} */
24
+ const _focusableElementsString = ['a[href]',
25
+ 'area[href]',
26
+ 'input:not([disabled])',
27
+ 'select:not([disabled])',
28
+ 'textarea:not([disabled])',
29
+ 'button:not([disabled])',
30
+ 'details',
31
+ 'summary',
32
+ 'iframe',
33
+ 'object',
34
+ 'embed',
35
+ '[contenteditable]'].join(',');
85
36
 
86
37
  /**
87
- * Call this whenever this object is about to become obsolete. This unwinds all of the state
88
- * stored in this object and updates the state of all of the managed nodes.
38
+ * `InertRoot` manages a single inert subtree, i.e. a DOM subtree whose root element has an `inert`
39
+ * attribute.
40
+ *
41
+ * Its main functions are:
42
+ *
43
+ * - to create and maintain a set of managed `InertNode`s, including when mutations occur in the
44
+ * subtree. The `makeSubtreeUnfocusable()` method handles collecting `InertNode`s via registering
45
+ * each focusable node in the subtree with the singleton `InertManager` which manages all known
46
+ * focusable nodes within inert subtrees. `InertManager` ensures that a single `InertNode`
47
+ * instance exists for each focusable node which has at least one inert root as an ancestor.
48
+ *
49
+ * - to notify all managed `InertNode`s when this subtree stops being inert (i.e. when the `inert`
50
+ * attribute is removed from the root node). This is handled in the destructor, which calls the
51
+ * `deregister` method on `InertManager` for each managed inert node.
89
52
  */
90
- destructor() {
91
- this._observer.disconnect();
92
-
93
- if (this._rootElement) {
94
- if (this._savedAriaHidden !== null) {
95
- this._rootElement.setAttribute('aria-hidden', this._savedAriaHidden);
53
+ class InertRoot {
54
+ /**
55
+ * @param {!Element} rootElement The Element at the root of the inert subtree.
56
+ * @param {!InertManager} inertManager The global singleton InertManager object.
57
+ */
58
+ constructor(rootElement, inertManager) {
59
+ /** @type {!InertManager} */
60
+ this._inertManager = inertManager;
61
+
62
+ /** @type {!Element} */
63
+ this._rootElement = rootElement;
64
+
65
+ /**
66
+ * @type {!Set<!InertNode>}
67
+ * All managed focusable nodes in this InertRoot's subtree.
68
+ */
69
+ this._managedNodes = new Set();
70
+
71
+ // Make the subtree hidden from assistive technology
72
+ if (this._rootElement.hasAttribute('aria-hidden')) {
73
+ /** @type {?string} */
74
+ this._savedAriaHidden = this._rootElement.getAttribute('aria-hidden');
96
75
  } else {
97
- this._rootElement.removeAttribute('aria-hidden');
76
+ this._savedAriaHidden = null;
98
77
  }
78
+ this._rootElement.setAttribute('aria-hidden', 'true');
79
+
80
+ // Make all focusable elements in the subtree unfocusable and add them to _managedNodes
81
+ this._makeSubtreeUnfocusable(this._rootElement);
82
+
83
+ // Watch for:
84
+ // - any additions in the subtree: make them unfocusable too
85
+ // - any removals from the subtree: remove them from this inert root's managed nodes
86
+ // - attribute changes: if `tabindex` is added, or removed from an intrinsically focusable
87
+ // element, make that node a managed node.
88
+ this._observer = new MutationObserver(this._onMutation.bind(this));
89
+ this._observer.observe(this._rootElement, {attributes: true, childList: true, subtree: true});
99
90
  }
100
91
 
101
- this._managedNodes.forEach(function(inertNode) {
102
- this._unmanageNode(inertNode.node);
103
- }, this);
104
-
105
- // Note we cast the nulls to the ANY type here because:
106
- // 1) We want the class properties to be declared as non-null, or else we
107
- // need even more casts throughout this code. All bets are off if an
108
- // instance has been destroyed and a method is called.
109
- // 2) We don't want to cast "this", because we want type-aware optimizations
110
- // to know which properties we're setting.
111
- this._observer = /** @type {?} */ (null);
112
- this._rootElement = /** @type {?} */ (null);
113
- this._managedNodes = /** @type {?} */ (null);
114
- this._inertManager = /** @type {?} */ (null);
115
- }
92
+ /**
93
+ * Call this whenever this object is about to become obsolete. This unwinds all of the state
94
+ * stored in this object and updates the state of all of the managed nodes.
95
+ */
96
+ destructor() {
97
+ this._observer.disconnect();
98
+
99
+ if (this._rootElement) {
100
+ if (this._savedAriaHidden !== null) {
101
+ this._rootElement.setAttribute('aria-hidden', this._savedAriaHidden);
102
+ } else {
103
+ this._rootElement.removeAttribute('aria-hidden');
104
+ }
105
+ }
116
106
 
117
- /**
118
- * @return {!Set<!InertNode>} A copy of this InertRoot's managed nodes set.
119
- */
120
- get managedNodes() {
121
- return new Set(this._managedNodes);
122
- }
107
+ this._managedNodes.forEach(function(inertNode) {
108
+ this._unmanageNode(inertNode.node);
109
+ }, this);
110
+
111
+ // Note we cast the nulls to the ANY type here because:
112
+ // 1) We want the class properties to be declared as non-null, or else we
113
+ // need even more casts throughout this code. All bets are off if an
114
+ // instance has been destroyed and a method is called.
115
+ // 2) We don't want to cast "this", because we want type-aware optimizations
116
+ // to know which properties we're setting.
117
+ this._observer = /** @type {?} */ (null);
118
+ this._rootElement = /** @type {?} */ (null);
119
+ this._managedNodes = /** @type {?} */ (null);
120
+ this._inertManager = /** @type {?} */ (null);
121
+ }
123
122
 
124
- /** @return {boolean} */
125
- get hasSavedAriaHidden() {
126
- return this._savedAriaHidden !== null;
127
- }
123
+ /**
124
+ * @return {!Set<!InertNode>} A copy of this InertRoot's managed nodes set.
125
+ */
126
+ get managedNodes() {
127
+ return new Set(this._managedNodes);
128
+ }
128
129
 
129
- /** @param {?string} ariaHidden */
130
- set savedAriaHidden(ariaHidden) {
131
- this._savedAriaHidden = ariaHidden;
132
- }
130
+ /** @return {boolean} */
131
+ get hasSavedAriaHidden() {
132
+ return this._savedAriaHidden !== null;
133
+ }
133
134
 
134
- /** @return {?string} */
135
- get savedAriaHidden() {
136
- return this._savedAriaHidden;
137
- }
135
+ /** @param {?string} ariaHidden */
136
+ set savedAriaHidden(ariaHidden) {
137
+ this._savedAriaHidden = ariaHidden;
138
+ }
138
139
 
139
- /**
140
- * @param {!Node} startNode
141
- */
142
- _makeSubtreeUnfocusable(startNode) {
143
- composedTreeWalk(startNode, (node) => this._visitNode(node));
144
-
145
- let activeElement = document.activeElement;
146
-
147
- if (!document.body.contains(startNode)) {
148
- // startNode may be in shadow DOM, so find its nearest shadowRoot to get the activeElement.
149
- let node = startNode;
150
- /** @type {!ShadowRoot|undefined} */
151
- let root = undefined;
152
- while (node) {
153
- if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
154
- root = /** @type {!ShadowRoot} */ (node);
155
- break;
140
+ /** @return {?string} */
141
+ get savedAriaHidden() {
142
+ return this._savedAriaHidden;
143
+ }
144
+
145
+ /**
146
+ * @param {!Node} startNode
147
+ */
148
+ _makeSubtreeUnfocusable(startNode) {
149
+ composedTreeWalk(startNode, (node) => this._visitNode(node));
150
+
151
+ let activeElement = document.activeElement;
152
+
153
+ if (!document.body.contains(startNode)) {
154
+ // startNode may be in shadow DOM, so find its nearest shadowRoot to get the activeElement.
155
+ let node = startNode;
156
+ /** @type {!ShadowRoot|undefined} */
157
+ let root = undefined;
158
+ while (node) {
159
+ if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
160
+ root = /** @type {!ShadowRoot} */ (node);
161
+ break;
162
+ }
163
+ node = node.parentNode;
164
+ }
165
+ if (root) {
166
+ activeElement = root.activeElement;
156
167
  }
157
- node = node.parentNode;
158
168
  }
159
- if (root) {
160
- activeElement = root.activeElement;
169
+ if (startNode.contains(activeElement)) {
170
+ activeElement.blur();
171
+ // In IE11, if an element is already focused, and then set to tabindex=-1
172
+ // calling blur() will not actually move the focus.
173
+ // To work around this we call focus() on the body instead.
174
+ if (activeElement === document.activeElement) {
175
+ document.body.focus();
176
+ }
161
177
  }
162
178
  }
163
- if (startNode.contains(activeElement)) {
164
- activeElement.blur();
165
- // In IE11, if an element is already focused, and then set to tabindex=-1
166
- // calling blur() will not actually move the focus.
167
- // To work around this we call focus() on the body instead.
168
- if (activeElement === document.activeElement) {
169
- document.body.focus();
179
+
180
+ /**
181
+ * @param {!Node} node
182
+ */
183
+ _visitNode(node) {
184
+ if (node.nodeType !== Node.ELEMENT_NODE) {
185
+ return;
170
186
  }
171
- }
172
- }
187
+ const element = /** @type {!Element} */ (node);
173
188
 
174
- /**
175
- * @param {!Node} node
176
- */
177
- _visitNode(node) {
178
- if (node.nodeType !== Node.ELEMENT_NODE) {
179
- return;
180
- }
181
- const element = /** @type {!Element} */ (node);
189
+ // If a descendant inert root becomes un-inert, its descendants will still be inert because of
190
+ // this inert root, so all of its managed nodes need to be adopted by this InertRoot.
191
+ if (element !== this._rootElement && element.hasAttribute('inert')) {
192
+ this._adoptInertRoot(element);
193
+ }
182
194
 
183
- // If a descendant inert root becomes un-inert, its descendants will still be inert because of
184
- // this inert root, so all of its managed nodes need to be adopted by this InertRoot.
185
- if (element !== this._rootElement && element.hasAttribute('inert')) {
186
- this._adoptInertRoot(element);
195
+ if (matches.call(element, _focusableElementsString) || element.hasAttribute('tabindex')) {
196
+ this._manageNode(element);
197
+ }
187
198
  }
188
199
 
189
- if (matches.call(element, _focusableElementsString) || element.hasAttribute('tabindex')) {
190
- this._manageNode(element);
200
+ /**
201
+ * Register the given node with this InertRoot and with InertManager.
202
+ * @param {!Node} node
203
+ */
204
+ _manageNode(node) {
205
+ const inertNode = this._inertManager.register(node, this);
206
+ this._managedNodes.add(inertNode);
191
207
  }
192
- }
193
-
194
- /**
195
- * Register the given node with this InertRoot and with InertManager.
196
- * @param {!Node} node
197
- */
198
- _manageNode(node) {
199
- const inertNode = this._inertManager.register(node, this);
200
- this._managedNodes.add(inertNode);
201
- }
202
208
 
203
- /**
204
- * Unregister the given node with this InertRoot and with InertManager.
205
- * @param {!Node} node
206
- */
207
- _unmanageNode(node) {
208
- const inertNode = this._inertManager.deregister(node, this);
209
- if (inertNode) {
210
- this._managedNodes.delete(inertNode);
209
+ /**
210
+ * Unregister the given node with this InertRoot and with InertManager.
211
+ * @param {!Node} node
212
+ */
213
+ _unmanageNode(node) {
214
+ const inertNode = this._inertManager.deregister(node, this);
215
+ if (inertNode) {
216
+ this._managedNodes.delete(inertNode);
217
+ }
211
218
  }
212
- }
213
219
 
214
- /**
215
- * Unregister the entire subtree starting at `startNode`.
216
- * @param {!Node} startNode
217
- */
218
- _unmanageSubtree(startNode) {
219
- composedTreeWalk(startNode, (node) => this._unmanageNode(node));
220
- }
220
+ /**
221
+ * Unregister the entire subtree starting at `startNode`.
222
+ * @param {!Node} startNode
223
+ */
224
+ _unmanageSubtree(startNode) {
225
+ composedTreeWalk(startNode, (node) => this._unmanageNode(node));
226
+ }
221
227
 
222
- /**
223
- * If a descendant node is found with an `inert` attribute, adopt its managed nodes.
224
- * @param {!Element} node
225
- */
226
- _adoptInertRoot(node) {
227
- let inertSubroot = this._inertManager.getInertRoot(node);
228
+ /**
229
+ * If a descendant node is found with an `inert` attribute, adopt its managed nodes.
230
+ * @param {!Element} node
231
+ */
232
+ _adoptInertRoot(node) {
233
+ let inertSubroot = this._inertManager.getInertRoot(node);
234
+
235
+ // During initialisation this inert root may not have been registered yet,
236
+ // so register it now if need be.
237
+ if (!inertSubroot) {
238
+ this._inertManager.setInert(node, true);
239
+ inertSubroot = this._inertManager.getInertRoot(node);
240
+ }
228
241
 
229
- // During initialisation this inert root may not have been registered yet,
230
- // so register it now if need be.
231
- if (!inertSubroot) {
232
- this._inertManager.setInert(node, true);
233
- inertSubroot = this._inertManager.getInertRoot(node);
242
+ inertSubroot.managedNodes.forEach(function(savedInertNode) {
243
+ this._manageNode(savedInertNode.node);
244
+ }, this);
234
245
  }
235
246
 
236
- inertSubroot.managedNodes.forEach(function(savedInertNode) {
237
- this._manageNode(savedInertNode.node);
238
- }, this);
239
- }
240
-
241
- /**
242
- * Callback used when mutation observer detects subtree additions, removals, or attribute changes.
243
- * @param {!Array<!MutationRecord>} records
244
- * @param {!MutationObserver} self
245
- */
246
- _onMutation(records, self) {
247
- records.forEach(function(record) {
248
- const target = /** @type {!Element} */ (record.target);
249
- if (record.type === 'childList') {
250
- // Manage added nodes
251
- slice.call(record.addedNodes).forEach(function(node) {
252
- this._makeSubtreeUnfocusable(node);
253
- }, this);
254
-
255
- // Un-manage removed nodes
256
- slice.call(record.removedNodes).forEach(function(node) {
257
- this._unmanageSubtree(node);
258
- }, this);
259
- } else if (record.type === 'attributes') {
260
- if (record.attributeName === 'tabindex') {
261
- // Re-initialise inert node if tabindex changes
262
- this._manageNode(target);
263
- } else if (target !== this._rootElement &&
264
- record.attributeName === 'inert' &&
265
- target.hasAttribute('inert')) {
266
- // If a new inert root is added, adopt its managed nodes and make sure it knows about the
267
- // already managed nodes from this inert subroot.
268
- this._adoptInertRoot(target);
269
- const inertSubroot = this._inertManager.getInertRoot(target);
270
- this._managedNodes.forEach(function(managedNode) {
271
- if (target.contains(managedNode.node)) {
272
- inertSubroot._manageNode(managedNode.node);
273
- }
274
- });
247
+ /**
248
+ * Callback used when mutation observer detects subtree additions, removals, or attribute changes.
249
+ * @param {!Array<!MutationRecord>} records
250
+ * @param {!MutationObserver} self
251
+ */
252
+ _onMutation(records, self) {
253
+ records.forEach(function(record) {
254
+ const target = /** @type {!Element} */ (record.target);
255
+ if (record.type === 'childList') {
256
+ // Manage added nodes
257
+ slice.call(record.addedNodes).forEach(function(node) {
258
+ this._makeSubtreeUnfocusable(node);
259
+ }, this);
260
+
261
+ // Un-manage removed nodes
262
+ slice.call(record.removedNodes).forEach(function(node) {
263
+ this._unmanageSubtree(node);
264
+ }, this);
265
+ } else if (record.type === 'attributes') {
266
+ if (record.attributeName === 'tabindex') {
267
+ // Re-initialise inert node if tabindex changes
268
+ this._manageNode(target);
269
+ } else if (target !== this._rootElement &&
270
+ record.attributeName === 'inert' &&
271
+ target.hasAttribute('inert')) {
272
+ // If a new inert root is added, adopt its managed nodes and make sure it knows about the
273
+ // already managed nodes from this inert subroot.
274
+ this._adoptInertRoot(target);
275
+ const inertSubroot = this._inertManager.getInertRoot(target);
276
+ this._managedNodes.forEach(function(managedNode) {
277
+ if (target.contains(managedNode.node)) {
278
+ inertSubroot._manageNode(managedNode.node);
279
+ }
280
+ });
281
+ }
275
282
  }
276
- }
277
- }, this);
283
+ }, this);
284
+ }
278
285
  }
279
- }
280
286
 
281
- /**
282
- * `InertNode` initialises and manages a single inert node.
283
- * A node is inert if it is a descendant of one or more inert root elements.
284
- *
285
- * On construction, `InertNode` saves the existing `tabindex` value for the node, if any, and
286
- * either removes the `tabindex` attribute or sets it to `-1`, depending on whether the element
287
- * is intrinsically focusable or not.
288
- *
289
- * `InertNode` maintains a set of `InertRoot`s which are descendants of this `InertNode`. When an
290
- * `InertRoot` is destroyed, and calls `InertManager.deregister()`, the `InertManager` notifies the
291
- * `InertNode` via `removeInertRoot()`, which in turn destroys the `InertNode` if no `InertRoot`s
292
- * remain in the set. On destruction, `InertNode` reinstates the stored `tabindex` if one exists,
293
- * or removes the `tabindex` attribute if the element is intrinsically focusable.
294
- */
295
- class InertNode {
296
287
  /**
297
- * @param {!Node} node A focusable element to be made inert.
298
- * @param {!InertRoot} inertRoot The inert root element associated with this inert node.
288
+ * `InertNode` initialises and manages a single inert node.
289
+ * A node is inert if it is a descendant of one or more inert root elements.
290
+ *
291
+ * On construction, `InertNode` saves the existing `tabindex` value for the node, if any, and
292
+ * either removes the `tabindex` attribute or sets it to `-1`, depending on whether the element
293
+ * is intrinsically focusable or not.
294
+ *
295
+ * `InertNode` maintains a set of `InertRoot`s which are descendants of this `InertNode`. When an
296
+ * `InertRoot` is destroyed, and calls `InertManager.deregister()`, the `InertManager` notifies the
297
+ * `InertNode` via `removeInertRoot()`, which in turn destroys the `InertNode` if no `InertRoot`s
298
+ * remain in the set. On destruction, `InertNode` reinstates the stored `tabindex` if one exists,
299
+ * or removes the `tabindex` attribute if the element is intrinsically focusable.
299
300
  */
300
- constructor(node, inertRoot) {
301
- /** @type {!Node} */
302
- this._node = node;
303
-
304
- /** @type {boolean} */
305
- this._overrodeFocusMethod = false;
306
-
301
+ class InertNode {
307
302
  /**
308
- * @type {!Set<!InertRoot>} The set of descendant inert roots.
309
- * If and only if this set becomes empty, this node is no longer inert.
303
+ * @param {!Node} node A focusable element to be made inert.
304
+ * @param {!InertRoot} inertRoot The inert root element associated with this inert node.
310
305
  */
311
- this._inertRoots = new Set([inertRoot]);
306
+ constructor(node, inertRoot) {
307
+ /** @type {!Node} */
308
+ this._node = node;
312
309
 
313
- /** @type {?number} */
314
- this._savedTabIndex = null;
310
+ /** @type {boolean} */
311
+ this._overrodeFocusMethod = false;
315
312
 
316
- /** @type {boolean} */
317
- this._destroyed = false;
318
-
319
- // Save any prior tabindex info and make this node untabbable
320
- this.ensureUntabbable();
321
- }
313
+ /**
314
+ * @type {!Set<!InertRoot>} The set of descendant inert roots.
315
+ * If and only if this set becomes empty, this node is no longer inert.
316
+ */
317
+ this._inertRoots = new Set([inertRoot]);
322
318
 
323
- /**
324
- * Call this whenever this object is about to become obsolete.
325
- * This makes the managed node focusable again and deletes all of the previously stored state.
326
- */
327
- destructor() {
328
- this._throwIfDestroyed();
319
+ /** @type {?number} */
320
+ this._savedTabIndex = null;
329
321
 
330
- if (this._node && this._node.nodeType === Node.ELEMENT_NODE) {
331
- const element = /** @type {!Element} */ (this._node);
332
- if (this._savedTabIndex !== null) {
333
- element.setAttribute('tabindex', this._savedTabIndex);
334
- } else {
335
- element.removeAttribute('tabindex');
336
- }
322
+ /** @type {boolean} */
323
+ this._destroyed = false;
337
324
 
338
- // Use `delete` to restore native focus method.
339
- if (this._overrodeFocusMethod) {
340
- delete element.focus;
341
- }
325
+ // Save any prior tabindex info and make this node untabbable
326
+ this.ensureUntabbable();
342
327
  }
343
328
 
344
- // See note in InertRoot.destructor for why we cast these nulls to ANY.
345
- this._node = /** @type {?} */ (null);
346
- this._inertRoots = /** @type {?} */ (null);
347
- this._destroyed = true;
348
- }
329
+ /**
330
+ * Call this whenever this object is about to become obsolete.
331
+ * This makes the managed node focusable again and deletes all of the previously stored state.
332
+ */
333
+ destructor() {
334
+ this._throwIfDestroyed();
335
+
336
+ if (this._node && this._node.nodeType === Node.ELEMENT_NODE) {
337
+ const element = /** @type {!Element} */ (this._node);
338
+ if (this._savedTabIndex !== null) {
339
+ element.setAttribute('tabindex', this._savedTabIndex);
340
+ } else {
341
+ element.removeAttribute('tabindex');
342
+ }
349
343
 
350
- /**
351
- * @type {boolean} Whether this object is obsolete because the managed node is no longer inert.
352
- * If the object has been destroyed, any attempt to access it will cause an exception.
353
- */
354
- get destroyed() {
355
- return /** @type {!InertNode} */ (this)._destroyed;
356
- }
344
+ // Use `delete` to restore native focus method.
345
+ if (this._overrodeFocusMethod) {
346
+ delete element.focus;
347
+ }
348
+ }
357
349
 
358
- /**
359
- * Throw if user tries to access destroyed InertNode.
360
- */
361
- _throwIfDestroyed() {
362
- if (this.destroyed) {
363
- throw new Error('Trying to access destroyed InertNode');
350
+ // See note in InertRoot.destructor for why we cast these nulls to ANY.
351
+ this._node = /** @type {?} */ (null);
352
+ this._inertRoots = /** @type {?} */ (null);
353
+ this._destroyed = true;
364
354
  }
365
- }
366
355
 
367
- /** @return {boolean} */
368
- get hasSavedTabIndex() {
369
- return this._savedTabIndex !== null;
370
- }
356
+ /**
357
+ * @type {boolean} Whether this object is obsolete because the managed node is no longer inert.
358
+ * If the object has been destroyed, any attempt to access it will cause an exception.
359
+ */
360
+ get destroyed() {
361
+ return /** @type {!InertNode} */ (this)._destroyed;
362
+ }
371
363
 
372
- /** @return {!Node} */
373
- get node() {
374
- this._throwIfDestroyed();
375
- return this._node;
376
- }
364
+ /**
365
+ * Throw if user tries to access destroyed InertNode.
366
+ */
367
+ _throwIfDestroyed() {
368
+ if (this.destroyed) {
369
+ throw new Error('Trying to access destroyed InertNode');
370
+ }
371
+ }
377
372
 
378
- /** @param {?number} tabIndex */
379
- set savedTabIndex(tabIndex) {
380
- this._throwIfDestroyed();
381
- this._savedTabIndex = tabIndex;
382
- }
373
+ /** @return {boolean} */
374
+ get hasSavedTabIndex() {
375
+ return this._savedTabIndex !== null;
376
+ }
383
377
 
384
- /** @return {?number} */
385
- get savedTabIndex() {
386
- this._throwIfDestroyed();
387
- return this._savedTabIndex;
388
- }
378
+ /** @return {!Node} */
379
+ get node() {
380
+ this._throwIfDestroyed();
381
+ return this._node;
382
+ }
389
383
 
390
- /** Save the existing tabindex value and make the node untabbable and unfocusable */
391
- ensureUntabbable() {
392
- if (this.node.nodeType !== Node.ELEMENT_NODE) {
393
- return;
384
+ /** @param {?number} tabIndex */
385
+ set savedTabIndex(tabIndex) {
386
+ this._throwIfDestroyed();
387
+ this._savedTabIndex = tabIndex;
388
+ }
389
+
390
+ /** @return {?number} */
391
+ get savedTabIndex() {
392
+ this._throwIfDestroyed();
393
+ return this._savedTabIndex;
394
394
  }
395
- const element = /** @type {!Element} */ (this.node);
396
- if (matches.call(element, _focusableElementsString)) {
397
- if (/** @type {!HTMLElement} */ (element).tabIndex === -1 &&
398
- this.hasSavedTabIndex) {
395
+
396
+ /** Save the existing tabindex value and make the node untabbable and unfocusable */
397
+ ensureUntabbable() {
398
+ if (this.node.nodeType !== Node.ELEMENT_NODE) {
399
399
  return;
400
400
  }
401
+ const element = /** @type {!Element} */ (this.node);
402
+ if (matches.call(element, _focusableElementsString)) {
403
+ if (/** @type {!HTMLElement} */ (element).tabIndex === -1 &&
404
+ this.hasSavedTabIndex) {
405
+ return;
406
+ }
401
407
 
402
- if (element.hasAttribute('tabindex')) {
408
+ if (element.hasAttribute('tabindex')) {
409
+ this._savedTabIndex = /** @type {!HTMLElement} */ (element).tabIndex;
410
+ }
411
+ element.setAttribute('tabindex', '-1');
412
+ if (element.nodeType === Node.ELEMENT_NODE) {
413
+ element.focus = function() {};
414
+ this._overrodeFocusMethod = true;
415
+ }
416
+ } else if (element.hasAttribute('tabindex')) {
403
417
  this._savedTabIndex = /** @type {!HTMLElement} */ (element).tabIndex;
418
+ element.removeAttribute('tabindex');
404
419
  }
405
- element.setAttribute('tabindex', '-1');
406
- if (element.nodeType === Node.ELEMENT_NODE) {
407
- element.focus = function() {};
408
- this._overrodeFocusMethod = true;
409
- }
410
- } else if (element.hasAttribute('tabindex')) {
411
- this._savedTabIndex = /** @type {!HTMLElement} */ (element).tabIndex;
412
- element.removeAttribute('tabindex');
413
420
  }
414
- }
415
421
 
416
- /**
417
- * Add another inert root to this inert node's set of managing inert roots.
418
- * @param {!InertRoot} inertRoot
419
- */
420
- addInertRoot(inertRoot) {
421
- this._throwIfDestroyed();
422
- this._inertRoots.add(inertRoot);
423
- }
422
+ /**
423
+ * Add another inert root to this inert node's set of managing inert roots.
424
+ * @param {!InertRoot} inertRoot
425
+ */
426
+ addInertRoot(inertRoot) {
427
+ this._throwIfDestroyed();
428
+ this._inertRoots.add(inertRoot);
429
+ }
424
430
 
425
- /**
426
- * Remove the given inert root from this inert node's set of managing inert roots.
427
- * If the set of managing inert roots becomes empty, this node is no longer inert,
428
- * so the object should be destroyed.
429
- * @param {!InertRoot} inertRoot
430
- */
431
- removeInertRoot(inertRoot) {
432
- this._throwIfDestroyed();
433
- this._inertRoots.delete(inertRoot);
434
- if (this._inertRoots.size === 0) {
435
- this.destructor();
431
+ /**
432
+ * Remove the given inert root from this inert node's set of managing inert roots.
433
+ * If the set of managing inert roots becomes empty, this node is no longer inert,
434
+ * so the object should be destroyed.
435
+ * @param {!InertRoot} inertRoot
436
+ */
437
+ removeInertRoot(inertRoot) {
438
+ this._throwIfDestroyed();
439
+ this._inertRoots.delete(inertRoot);
440
+ if (this._inertRoots.size === 0) {
441
+ this.destructor();
442
+ }
436
443
  }
437
444
  }
438
- }
439
445
 
440
- /**
441
- * InertManager is a per-document singleton object which manages all inert roots and nodes.
442
- *
443
- * When an element becomes an inert root by having an `inert` attribute set and/or its `inert`
444
- * property set to `true`, the `setInert` method creates an `InertRoot` object for the element.
445
- * The `InertRoot` in turn registers itself as managing all of the element's focusable descendant
446
- * nodes via the `register()` method. The `InertManager` ensures that a single `InertNode` instance
447
- * is created for each such node, via the `_managedNodes` map.
448
- */
449
- class InertManager {
450
446
  /**
451
- * @param {!Document} document
447
+ * InertManager is a per-document singleton object which manages all inert roots and nodes.
448
+ *
449
+ * When an element becomes an inert root by having an `inert` attribute set and/or its `inert`
450
+ * property set to `true`, the `setInert` method creates an `InertRoot` object for the element.
451
+ * The `InertRoot` in turn registers itself as managing all of the element's focusable descendant
452
+ * nodes via the `register()` method. The `InertManager` ensures that a single `InertNode` instance
453
+ * is created for each such node, via the `_managedNodes` map.
452
454
  */
453
- constructor(document) {
454
- if (!document) {
455
- throw new Error('Missing required argument; InertManager needs to wrap a document.');
456
- }
455
+ class InertManager {
456
+ /**
457
+ * @param {!Document} document
458
+ */
459
+ constructor(document) {
460
+ if (!document) {
461
+ throw new Error('Missing required argument; InertManager needs to wrap a document.');
462
+ }
457
463
 
458
- /** @type {!Document} */
459
- this._document = document;
464
+ /** @type {!Document} */
465
+ this._document = document;
466
+
467
+ /**
468
+ * All managed nodes known to this InertManager. In a map to allow looking up by Node.
469
+ * @type {!Map<!Node, !InertNode>}
470
+ */
471
+ this._managedNodes = new Map();
472
+
473
+ /**
474
+ * All inert roots known to this InertManager. In a map to allow looking up by Node.
475
+ * @type {!Map<!Node, !InertRoot>}
476
+ */
477
+ this._inertRoots = new Map();
478
+
479
+ /**
480
+ * Observer for mutations on `document.body`.
481
+ * @type {!MutationObserver}
482
+ */
483
+ this._observer = new MutationObserver(this._watchForInert.bind(this));
484
+
485
+ // Add inert style.
486
+ addInertStyle(document.head || document.body || document.documentElement);
487
+
488
+ // Wait for document to be loaded.
489
+ if (document.readyState === 'loading') {
490
+ document.addEventListener('DOMContentLoaded', this._onDocumentLoaded.bind(this));
491
+ } else {
492
+ this._onDocumentLoaded();
493
+ }
494
+ }
460
495
 
461
496
  /**
462
- * All managed nodes known to this InertManager. In a map to allow looking up by Node.
463
- * @type {!Map<!Node, !InertNode>}
497
+ * Set whether the given element should be an inert root or not.
498
+ * @param {!Element} root
499
+ * @param {boolean} inert
464
500
  */
465
- this._managedNodes = new Map();
501
+ setInert(root, inert) {
502
+ if (inert) {
503
+ if (this._inertRoots.has(root)) { // element is already inert
504
+ return;
505
+ }
506
+
507
+ const inertRoot = new InertRoot(root, this);
508
+ root.setAttribute('inert', '');
509
+ this._inertRoots.set(root, inertRoot);
510
+ // If not contained in the document, it must be in a shadowRoot.
511
+ // Ensure inert styles are added there.
512
+ if (!this._document.body.contains(root)) {
513
+ let parent = root.parentNode;
514
+ while (parent) {
515
+ if (parent.nodeType === 11) {
516
+ addInertStyle(parent);
517
+ }
518
+ parent = parent.parentNode;
519
+ }
520
+ }
521
+ } else {
522
+ if (!this._inertRoots.has(root)) { // element is already non-inert
523
+ return;
524
+ }
525
+
526
+ const inertRoot = this._inertRoots.get(root);
527
+ inertRoot.destructor();
528
+ this._inertRoots.delete(root);
529
+ root.removeAttribute('inert');
530
+ }
531
+ }
466
532
 
467
533
  /**
468
- * All inert roots known to this InertManager. In a map to allow looking up by Node.
469
- * @type {!Map<!Node, !InertRoot>}
534
+ * Get the InertRoot object corresponding to the given inert root element, if any.
535
+ * @param {!Node} element
536
+ * @return {!InertRoot|undefined}
470
537
  */
471
- this._inertRoots = new Map();
538
+ getInertRoot(element) {
539
+ return this._inertRoots.get(element);
540
+ }
472
541
 
473
542
  /**
474
- * Observer for mutations on `document.body`.
475
- * @type {!MutationObserver}
543
+ * Register the given InertRoot as managing the given node.
544
+ * In the case where the node has a previously existing inert root, this inert root will
545
+ * be added to its set of inert roots.
546
+ * @param {!Node} node
547
+ * @param {!InertRoot} inertRoot
548
+ * @return {!InertNode} inertNode
476
549
  */
477
- this._observer = new MutationObserver(this._watchForInert.bind(this));
550
+ register(node, inertRoot) {
551
+ let inertNode = this._managedNodes.get(node);
552
+ if (inertNode !== undefined) { // node was already in an inert subtree
553
+ inertNode.addInertRoot(inertRoot);
554
+ } else {
555
+ inertNode = new InertNode(node, inertRoot);
556
+ }
478
557
 
479
- // Add inert style.
480
- addInertStyle(document.head || document.body || document.documentElement);
558
+ this._managedNodes.set(node, inertNode);
481
559
 
482
- // Wait for document to be loaded.
483
- if (document.readyState === 'loading') {
484
- document.addEventListener('DOMContentLoaded', this._onDocumentLoaded.bind(this));
485
- } else {
486
- this._onDocumentLoaded();
560
+ return inertNode;
487
561
  }
488
- }
489
562
 
490
- /**
491
- * Set whether the given element should be an inert root or not.
492
- * @param {!Element} root
493
- * @param {boolean} inert
494
- */
495
- setInert(root, inert) {
496
- if (inert) {
497
- if (this._inertRoots.has(root)) { // element is already inert
498
- return;
563
+ /**
564
+ * De-register the given InertRoot as managing the given inert node.
565
+ * Removes the inert root from the InertNode's set of managing inert roots, and remove the inert
566
+ * node from the InertManager's set of managed nodes if it is destroyed.
567
+ * If the node is not currently managed, this is essentially a no-op.
568
+ * @param {!Node} node
569
+ * @param {!InertRoot} inertRoot
570
+ * @return {?InertNode} The potentially destroyed InertNode associated with this node, if any.
571
+ */
572
+ deregister(node, inertRoot) {
573
+ const inertNode = this._managedNodes.get(node);
574
+ if (!inertNode) {
575
+ return null;
499
576
  }
500
577
 
501
- const inertRoot = new InertRoot(root, this);
502
- root.setAttribute('inert', '');
503
- this._inertRoots.set(root, inertRoot);
504
- // If not contained in the document, it must be in a shadowRoot.
505
- // Ensure inert styles are added there.
506
- if (!this._document.body.contains(root)) {
507
- let parent = root.parentNode;
508
- while (parent) {
509
- if (parent.nodeType === 11) {
510
- addInertStyle(parent);
511
- }
512
- parent = parent.parentNode;
513
- }
514
- }
515
- } else {
516
- if (!this._inertRoots.has(root)) { // element is already non-inert
517
- return;
578
+ inertNode.removeInertRoot(inertRoot);
579
+ if (inertNode.destroyed) {
580
+ this._managedNodes.delete(node);
518
581
  }
519
582
 
520
- const inertRoot = this._inertRoots.get(root);
521
- inertRoot.destructor();
522
- this._inertRoots.delete(root);
523
- root.removeAttribute('inert');
583
+ return inertNode;
524
584
  }
525
- }
526
585
 
527
- /**
528
- * Get the InertRoot object corresponding to the given inert root element, if any.
529
- * @param {!Node} element
530
- * @return {!InertRoot|undefined}
531
- */
532
- getInertRoot(element) {
533
- return this._inertRoots.get(element);
534
- }
535
-
536
- /**
537
- * Register the given InertRoot as managing the given node.
538
- * In the case where the node has a previously existing inert root, this inert root will
539
- * be added to its set of inert roots.
540
- * @param {!Node} node
541
- * @param {!InertRoot} inertRoot
542
- * @return {!InertNode} inertNode
543
- */
544
- register(node, inertRoot) {
545
- let inertNode = this._managedNodes.get(node);
546
- if (inertNode !== undefined) { // node was already in an inert subtree
547
- inertNode.addInertRoot(inertRoot);
548
- } else {
549
- inertNode = new InertNode(node, inertRoot);
586
+ /**
587
+ * Callback used when document has finished loading.
588
+ */
589
+ _onDocumentLoaded() {
590
+ // Find all inert roots in document and make them actually inert.
591
+ const inertElements = slice.call(this._document.querySelectorAll('[inert]'));
592
+ inertElements.forEach(function(inertElement) {
593
+ this.setInert(inertElement, true);
594
+ }, this);
595
+
596
+ // Comment this out to use programmatic API only.
597
+ this._observer.observe(this._document.body || this._document.documentElement, {attributes: true, subtree: true, childList: true});
550
598
  }
551
599
 
552
- this._managedNodes.set(node, inertNode);
553
-
554
- return inertNode;
600
+ /**
601
+ * Callback used when mutation observer detects attribute changes.
602
+ * @param {!Array<!MutationRecord>} records
603
+ * @param {!MutationObserver} self
604
+ */
605
+ _watchForInert(records, self) {
606
+ const _this = this;
607
+ records.forEach(function(record) {
608
+ switch (record.type) {
609
+ case 'childList':
610
+ slice.call(record.addedNodes).forEach(function(node) {
611
+ if (node.nodeType !== Node.ELEMENT_NODE) {
612
+ return;
613
+ }
614
+ const inertElements = slice.call(node.querySelectorAll('[inert]'));
615
+ if (matches.call(node, '[inert]')) {
616
+ inertElements.unshift(node);
617
+ }
618
+ inertElements.forEach(function(inertElement) {
619
+ this.setInert(inertElement, true);
620
+ }, _this);
621
+ }, _this);
622
+ break;
623
+ case 'attributes':
624
+ if (record.attributeName !== 'inert') {
625
+ return;
626
+ }
627
+ const target = /** @type {!Element} */ (record.target);
628
+ const inert = target.hasAttribute('inert');
629
+ _this.setInert(target, inert);
630
+ break;
631
+ }
632
+ }, this);
633
+ }
555
634
  }
556
635
 
557
636
  /**
558
- * De-register the given InertRoot as managing the given inert node.
559
- * Removes the inert root from the InertNode's set of managing inert roots, and remove the inert
560
- * node from the InertManager's set of managed nodes if it is destroyed.
561
- * If the node is not currently managed, this is essentially a no-op.
637
+ * Recursively walk the composed tree from |node|.
562
638
  * @param {!Node} node
563
- * @param {!InertRoot} inertRoot
564
- * @return {?InertNode} The potentially destroyed InertNode associated with this node, if any.
639
+ * @param {(function (!Element))=} callback Callback to be called for each element traversed,
640
+ * before descending into child nodes.
641
+ * @param {?ShadowRoot=} shadowRootAncestor The nearest ShadowRoot ancestor, if any.
565
642
  */
566
- deregister(node, inertRoot) {
567
- const inertNode = this._managedNodes.get(node);
568
- if (!inertNode) {
569
- return null;
570
- }
571
-
572
- inertNode.removeInertRoot(inertRoot);
573
- if (inertNode.destroyed) {
574
- this._managedNodes.delete(node);
575
- }
576
-
577
- return inertNode;
578
- }
643
+ function composedTreeWalk(node, callback, shadowRootAncestor) {
644
+ if (node.nodeType == Node.ELEMENT_NODE) {
645
+ const element = /** @type {!Element} */ (node);
646
+ if (callback) {
647
+ callback(element);
648
+ }
579
649
 
580
- /**
581
- * Callback used when document has finished loading.
582
- */
583
- _onDocumentLoaded() {
584
- // Find all inert roots in document and make them actually inert.
585
- const inertElements = slice.call(this._document.querySelectorAll('[inert]'));
586
- inertElements.forEach(function(inertElement) {
587
- this.setInert(inertElement, true);
588
- }, this);
589
-
590
- // Comment this out to use programmatic API only.
591
- this._observer.observe(this._document.body, {attributes: true, subtree: true, childList: true});
592
- }
650
+ // Descend into node:
651
+ // If it has a ShadowRoot, ignore all child elements - these will be picked
652
+ // up by the <content> or <shadow> elements. Descend straight into the
653
+ // ShadowRoot.
654
+ const shadowRoot = /** @type {!HTMLElement} */ (element).shadowRoot;
655
+ if (shadowRoot) {
656
+ composedTreeWalk(shadowRoot, callback, shadowRoot);
657
+ return;
658
+ }
593
659
 
594
- /**
595
- * Callback used when mutation observer detects attribute changes.
596
- * @param {!Array<!MutationRecord>} records
597
- * @param {!MutationObserver} self
598
- */
599
- _watchForInert(records, self) {
600
- const _this = this;
601
- records.forEach(function(record) {
602
- switch (record.type) {
603
- case 'childList':
604
- slice.call(record.addedNodes).forEach(function(node) {
605
- if (node.nodeType !== Node.ELEMENT_NODE) {
606
- return;
607
- }
608
- const inertElements = slice.call(node.querySelectorAll('[inert]'));
609
- if (matches.call(node, '[inert]')) {
610
- inertElements.unshift(node);
611
- }
612
- inertElements.forEach(function(inertElement) {
613
- this.setInert(inertElement, true);
614
- }, _this);
615
- }, _this);
616
- break;
617
- case 'attributes':
618
- if (record.attributeName !== 'inert') {
619
- return;
660
+ // If it is a <content> element, descend into distributed elements - these
661
+ // are elements from outside the shadow root which are rendered inside the
662
+ // shadow DOM.
663
+ if (element.localName == 'content') {
664
+ const content = /** @type {!HTMLContentElement} */ (element);
665
+ // Verifies if ShadowDom v0 is supported.
666
+ const distributedNodes = content.getDistributedNodes ?
667
+ content.getDistributedNodes() : [];
668
+ for (let i = 0; i < distributedNodes.length; i++) {
669
+ composedTreeWalk(distributedNodes[i], callback, shadowRootAncestor);
620
670
  }
621
- const target = /** @type {!Element} */ (record.target);
622
- const inert = target.hasAttribute('inert');
623
- _this.setInert(target, inert);
624
- break;
671
+ return;
625
672
  }
626
- }, this);
627
- }
628
- }
629
-
630
- /**
631
- * Recursively walk the composed tree from |node|.
632
- * @param {!Node} node
633
- * @param {(function (!Element))=} callback Callback to be called for each element traversed,
634
- * before descending into child nodes.
635
- * @param {?ShadowRoot=} shadowRootAncestor The nearest ShadowRoot ancestor, if any.
636
- */
637
- function composedTreeWalk(node, callback, shadowRootAncestor) {
638
- if (node.nodeType == Node.ELEMENT_NODE) {
639
- const element = /** @type {!Element} */ (node);
640
- if (callback) {
641
- callback(element);
642
- }
643
-
644
- // Descend into node:
645
- // If it has a ShadowRoot, ignore all child elements - these will be picked
646
- // up by the <content> or <shadow> elements. Descend straight into the
647
- // ShadowRoot.
648
- const shadowRoot = /** @type {!HTMLElement} */ (element).shadowRoot;
649
- if (shadowRoot) {
650
- composedTreeWalk(shadowRoot, callback, shadowRoot);
651
- return;
652
- }
653
673
 
654
- // If it is a <content> element, descend into distributed elements - these
655
- // are elements from outside the shadow root which are rendered inside the
656
- // shadow DOM.
657
- if (element.localName == 'content') {
658
- const content = /** @type {!HTMLContentElement} */ (element);
659
- // Verifies if ShadowDom v0 is supported.
660
- const distributedNodes = content.getDistributedNodes ?
661
- content.getDistributedNodes() : [];
662
- for (let i = 0; i < distributedNodes.length; i++) {
663
- composedTreeWalk(distributedNodes[i], callback, shadowRootAncestor);
674
+ // If it is a <slot> element, descend into assigned nodes - these
675
+ // are elements from outside the shadow root which are rendered inside the
676
+ // shadow DOM.
677
+ if (element.localName == 'slot') {
678
+ const slot = /** @type {!HTMLSlotElement} */ (element);
679
+ // Verify if ShadowDom v1 is supported.
680
+ const distributedNodes = slot.assignedNodes ?
681
+ slot.assignedNodes({flatten: true}) : [];
682
+ for (let i = 0; i < distributedNodes.length; i++) {
683
+ composedTreeWalk(distributedNodes[i], callback, shadowRootAncestor);
684
+ }
685
+ return;
664
686
  }
665
- return;
666
687
  }
667
688
 
668
- // If it is a <slot> element, descend into assigned nodes - these
669
- // are elements from outside the shadow root which are rendered inside the
670
- // shadow DOM.
671
- if (element.localName == 'slot') {
672
- const slot = /** @type {!HTMLSlotElement} */ (element);
673
- // Verify if ShadowDom v1 is supported.
674
- const distributedNodes = slot.assignedNodes ?
675
- slot.assignedNodes({flatten: true}) : [];
676
- for (let i = 0; i < distributedNodes.length; i++) {
677
- composedTreeWalk(distributedNodes[i], callback, shadowRootAncestor);
678
- }
679
- return;
689
+ // If it is neither the parent of a ShadowRoot, a <content> element, a <slot>
690
+ // element, nor a <shadow> element recurse normally.
691
+ let child = node.firstChild;
692
+ while (child != null) {
693
+ composedTreeWalk(child, callback, shadowRootAncestor);
694
+ child = child.nextSibling;
680
695
  }
681
696
  }
682
697
 
683
- // If it is neither the parent of a ShadowRoot, a <content> element, a <slot>
684
- // element, nor a <shadow> element recurse normally.
685
- let child = node.firstChild;
686
- while (child != null) {
687
- composedTreeWalk(child, callback, shadowRootAncestor);
688
- child = child.nextSibling;
689
- }
690
- }
691
-
692
- /**
693
- * Adds a style element to the node containing the inert specific styles
694
- * @param {!Node} node
695
- */
696
- function addInertStyle(node) {
697
- if (node.querySelector('style#inert-style')) {
698
- return;
699
- }
700
- const style = document.createElement('style');
701
- style.setAttribute('id', 'inert-style');
702
- style.textContent = '\n'+
703
- '[inert] {\n' +
704
- ' pointer-events: none;\n' +
705
- ' cursor: default;\n' +
706
- '}\n' +
707
- '\n' +
708
- '[inert], [inert] * {\n' +
709
- ' user-select: none;\n' +
710
- ' -webkit-user-select: none;\n' +
711
- ' -moz-user-select: none;\n' +
712
- ' -ms-user-select: none;\n' +
713
- '}\n';
714
- node.appendChild(style);
715
- }
716
-
717
- /** @type {!InertManager} */
718
- const inertManager = new InertManager(document);
719
-
720
- if (!Element.prototype.hasOwnProperty('inert')) {
721
- Object.defineProperty(Element.prototype, 'inert', {
722
- enumerable: true,
723
- /** @this {!Element} */
724
- get: function() {
725
- return this.hasAttribute('inert');
726
- },
727
- /** @this {!Element} */
728
- set: function(inert) {
729
- inertManager.setInert(this, inert);
730
- },
731
- });
732
- }
698
+ /**
699
+ * Adds a style element to the node containing the inert specific styles
700
+ * @param {!Node} node
701
+ */
702
+ function addInertStyle(node) {
703
+ if (node.querySelector('style#inert-style, link#inert-style')) {
704
+ return;
705
+ }
706
+ const style = document.createElement('style');
707
+ style.setAttribute('id', 'inert-style');
708
+ style.textContent = '\n'+
709
+ '[inert] {\n' +
710
+ ' pointer-events: none;\n' +
711
+ ' cursor: default;\n' +
712
+ '}\n' +
713
+ '\n' +
714
+ '[inert], [inert] * {\n' +
715
+ ' -webkit-user-select: none;\n' +
716
+ ' -moz-user-select: none;\n' +
717
+ ' -ms-user-select: none;\n' +
718
+ ' user-select: none;\n' +
719
+ '}\n';
720
+ node.appendChild(style);
721
+ }
722
+
723
+ if (!Element.prototype.hasOwnProperty('inert')) {
724
+ /** @type {!InertManager} */
725
+ const inertManager = new InertManager(document);
726
+
727
+ Object.defineProperty(Element.prototype, 'inert', {
728
+ enumerable: true,
729
+ /** @this {!Element} */
730
+ get: function() {
731
+ return this.hasAttribute('inert');
732
+ },
733
+ /** @this {!Element} */
734
+ set: function(inert) {
735
+ inertManager.setInert(this, inert);
736
+ },
737
+ });
738
+ }
739
+ })();