blob: 9573639144fc6afd57f05c7f8877cc33ad331e4b [file] [log] [blame]
Blink Reformat4c46d092018-04-07 15:32:371/*
2 * Copyright (C) 2008 Apple Inc. All Rights Reserved.
3 * Copyright (C) 2011 Google Inc. All Rights Reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
Tim van der Lippeee97fa32020-04-23 15:20:5627// @ts-nocheck
28// TODO(crbug.com/1011811): Enable TypeScript compiler checks
29
Paul Lewis17e384e2020-01-08 15:46:5130import * as Common from '../common/common.js';
Paul Lewis9950e182019-12-16 16:06:0731import {Constraints, Size} from './Geometry.js';
32import {appendStyle} from './utils/append-style.js';
33import {createShadowRootWithCoreStyles} from './utils/create-shadow-root-with-core-styles.js';
34import {XWidget} from './XWidget.js';
35
Blink Reformat4c46d092018-04-07 15:32:3736/**
37 * @unrestricted
38 */
Paul Lewis17e384e2020-01-08 15:46:5139export class Widget extends Common.ObjectWrapper.ObjectWrapper {
Blink Reformat4c46d092018-04-07 15:32:3740 /**
41 * @param {boolean=} isWebComponent
Joel Einbinder7fbe24c2019-01-24 05:19:0142 * @param {boolean=} delegatesFocus
Blink Reformat4c46d092018-04-07 15:32:3743 */
Joel Einbinder7fbe24c2019-01-24 05:19:0144 constructor(isWebComponent, delegatesFocus) {
Blink Reformat4c46d092018-04-07 15:32:3745 super();
Tim van der Lippef49e2322020-05-01 15:03:0946 this.contentElement = document.createElement('div');
47 this.contentElement.classList.add('widget');
Blink Reformat4c46d092018-04-07 15:32:3748 if (isWebComponent) {
49 this.element = createElementWithClass('div', 'vbox flex-auto');
Paul Lewis9950e182019-12-16 16:06:0750 this._shadowRoot = createShadowRootWithCoreStyles(this.element, undefined, delegatesFocus);
Blink Reformat4c46d092018-04-07 15:32:3751 this._shadowRoot.appendChild(this.contentElement);
52 } else {
53 this.element = this.contentElement;
54 }
55 this._isWebComponent = isWebComponent;
56 this.element.__widget = this;
57 this._visible = false;
58 this._isRoot = false;
59 this._isShowing = false;
60 this._children = [];
61 this._hideOnDetach = false;
62 this._notificationDepth = 0;
63 this._invalidationsSuspended = 0;
64 this._defaultFocusedChild = null;
65 }
66
67 static _incrementWidgetCounter(parentElement, childElement) {
68 const count = (childElement.__widgetCounter || 0) + (childElement.__widget ? 1 : 0);
Tim van der Lippe1d6e57a2019-09-30 11:55:3469 if (!count) {
Blink Reformat4c46d092018-04-07 15:32:3770 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:3471 }
Blink Reformat4c46d092018-04-07 15:32:3772
73 while (parentElement) {
74 parentElement.__widgetCounter = (parentElement.__widgetCounter || 0) + count;
75 parentElement = parentElement.parentElementOrShadowHost();
76 }
77 }
78
79 static _decrementWidgetCounter(parentElement, childElement) {
80 const count = (childElement.__widgetCounter || 0) + (childElement.__widget ? 1 : 0);
Tim van der Lippe1d6e57a2019-09-30 11:55:3481 if (!count) {
Blink Reformat4c46d092018-04-07 15:32:3782 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:3483 }
Blink Reformat4c46d092018-04-07 15:32:3784
85 while (parentElement) {
86 parentElement.__widgetCounter -= count;
87 parentElement = parentElement.parentElementOrShadowHost();
88 }
89 }
90
91 static __assert(condition, message) {
Tim van der Lippe1d6e57a2019-09-30 11:55:3492 if (!condition) {
Blink Reformat4c46d092018-04-07 15:32:3793 throw new Error(message);
Tim van der Lippe1d6e57a2019-09-30 11:55:3494 }
Blink Reformat4c46d092018-04-07 15:32:3795 }
96
Blink Reformat4c46d092018-04-07 15:32:3797 markAsRoot() {
Tim van der Lippe0830b3d2019-10-03 13:20:0798 Widget.__assert(!this.element.parentElement, 'Attempt to mark as root attached node');
Blink Reformat4c46d092018-04-07 15:32:3799 this._isRoot = true;
100 }
101
102 /**
Tim van der Lippe0830b3d2019-10-03 13:20:07103 * @return {?Widget}
Blink Reformat4c46d092018-04-07 15:32:37104 */
105 parentWidget() {
106 return this._parentWidget;
107 }
108
109 /**
Tim van der Lippe0830b3d2019-10-03 13:20:07110 * @return {!Array.<!Widget>}
Blink Reformat4c46d092018-04-07 15:32:37111 */
112 children() {
113 return this._children;
114 }
115
116 /**
Tim van der Lippe0830b3d2019-10-03 13:20:07117 * @param {!Widget} widget
Blink Reformat4c46d092018-04-07 15:32:37118 * @protected
119 */
120 childWasDetached(widget) {
121 }
122
123 /**
124 * @return {boolean}
125 */
126 isShowing() {
127 return this._isShowing;
128 }
129
130 /**
131 * @return {boolean}
132 */
133 shouldHideOnDetach() {
Tim van der Lippe1d6e57a2019-09-30 11:55:34134 if (!this.element.parentElement) {
Blink Reformat4c46d092018-04-07 15:32:37135 return false;
Tim van der Lippe1d6e57a2019-09-30 11:55:34136 }
137 if (this._hideOnDetach) {
Blink Reformat4c46d092018-04-07 15:32:37138 return true;
Tim van der Lippe1d6e57a2019-09-30 11:55:34139 }
Blink Reformat4c46d092018-04-07 15:32:37140 for (const child of this._children) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34141 if (child.shouldHideOnDetach()) {
Blink Reformat4c46d092018-04-07 15:32:37142 return true;
Tim van der Lippe1d6e57a2019-09-30 11:55:34143 }
Blink Reformat4c46d092018-04-07 15:32:37144 }
145 return false;
146 }
147
148 setHideOnDetach() {
149 this._hideOnDetach = true;
150 }
151
152 /**
153 * @return {boolean}
154 */
155 _inNotification() {
156 return !!this._notificationDepth || (this._parentWidget && this._parentWidget._inNotification());
157 }
158
159 _parentIsShowing() {
Tim van der Lippe1d6e57a2019-09-30 11:55:34160 if (this._isRoot) {
Blink Reformat4c46d092018-04-07 15:32:37161 return true;
Tim van der Lippe1d6e57a2019-09-30 11:55:34162 }
Blink Reformat4c46d092018-04-07 15:32:37163 return !!this._parentWidget && this._parentWidget.isShowing();
164 }
165
166 /**
Tim van der Lippe0830b3d2019-10-03 13:20:07167 * @param {function(this:Widget)} method
Blink Reformat4c46d092018-04-07 15:32:37168 */
169 _callOnVisibleChildren(method) {
170 const copy = this._children.slice();
171 for (let i = 0; i < copy.length; ++i) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34172 if (copy[i]._parentWidget === this && copy[i]._visible) {
Blink Reformat4c46d092018-04-07 15:32:37173 method.call(copy[i]);
Tim van der Lippe1d6e57a2019-09-30 11:55:34174 }
Blink Reformat4c46d092018-04-07 15:32:37175 }
176 }
177
178 _processWillShow() {
179 this._callOnVisibleChildren(this._processWillShow);
180 this._isShowing = true;
181 }
182
183 _processWasShown() {
Tim van der Lippe1d6e57a2019-09-30 11:55:34184 if (this._inNotification()) {
Blink Reformat4c46d092018-04-07 15:32:37185 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34186 }
Blink Reformat4c46d092018-04-07 15:32:37187 this.restoreScrollPositions();
188 this._notify(this.wasShown);
189 this._callOnVisibleChildren(this._processWasShown);
190 }
191
192 _processWillHide() {
Tim van der Lippe1d6e57a2019-09-30 11:55:34193 if (this._inNotification()) {
Blink Reformat4c46d092018-04-07 15:32:37194 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34195 }
Blink Reformat4c46d092018-04-07 15:32:37196 this.storeScrollPositions();
197
198 this._callOnVisibleChildren(this._processWillHide);
199 this._notify(this.willHide);
200 this._isShowing = false;
201 }
202
203 _processWasHidden() {
204 this._callOnVisibleChildren(this._processWasHidden);
205 }
206
207 _processOnResize() {
Tim van der Lippe1d6e57a2019-09-30 11:55:34208 if (this._inNotification()) {
Blink Reformat4c46d092018-04-07 15:32:37209 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34210 }
211 if (!this.isShowing()) {
Blink Reformat4c46d092018-04-07 15:32:37212 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34213 }
Blink Reformat4c46d092018-04-07 15:32:37214 this._notify(this.onResize);
215 this._callOnVisibleChildren(this._processOnResize);
216 }
217
218 /**
Tim van der Lippe0830b3d2019-10-03 13:20:07219 * @param {function(this:Widget)} notification
Blink Reformat4c46d092018-04-07 15:32:37220 */
221 _notify(notification) {
222 ++this._notificationDepth;
223 try {
224 notification.call(this);
225 } finally {
226 --this._notificationDepth;
227 }
228 }
229
230 wasShown() {
231 }
232
233 willHide() {
234 }
235
236 onResize() {
237 }
238
239 onLayout() {
240 }
241
242 ownerViewDisposed() {
243 }
244
245 /**
246 * @param {!Element} parentElement
247 * @param {?Node=} insertBefore
248 */
249 show(parentElement, insertBefore) {
Tim van der Lippe0830b3d2019-10-03 13:20:07250 Widget.__assert(parentElement, 'Attempt to attach widget with no parent element');
Blink Reformat4c46d092018-04-07 15:32:37251
252 if (!this._isRoot) {
253 // Update widget hierarchy.
254 let currentParent = parentElement;
Tim van der Lippe1d6e57a2019-09-30 11:55:34255 while (currentParent && !currentParent.__widget) {
Blink Reformat4c46d092018-04-07 15:32:37256 currentParent = currentParent.parentElementOrShadowHost();
Tim van der Lippe1d6e57a2019-09-30 11:55:34257 }
Tim van der Lippe0830b3d2019-10-03 13:20:07258 Widget.__assert(currentParent, 'Attempt to attach widget to orphan node');
Blink Reformat4c46d092018-04-07 15:32:37259 this._attach(currentParent.__widget);
260 }
261
262 this._showWidget(parentElement, insertBefore);
263 }
264
265 /**
Tim van der Lippe0830b3d2019-10-03 13:20:07266 * @param {!Widget} parentWidget
Blink Reformat4c46d092018-04-07 15:32:37267 */
268 _attach(parentWidget) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34269 if (parentWidget === this._parentWidget) {
Blink Reformat4c46d092018-04-07 15:32:37270 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34271 }
272 if (this._parentWidget) {
Blink Reformat4c46d092018-04-07 15:32:37273 this.detach();
Tim van der Lippe1d6e57a2019-09-30 11:55:34274 }
Blink Reformat4c46d092018-04-07 15:32:37275 this._parentWidget = parentWidget;
276 this._parentWidget._children.push(this);
277 this._isRoot = false;
278 }
279
280 showWidget() {
Tim van der Lippe1d6e57a2019-09-30 11:55:34281 if (this._visible) {
Blink Reformat4c46d092018-04-07 15:32:37282 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34283 }
Tim van der Lippe0830b3d2019-10-03 13:20:07284 Widget.__assert(this.element.parentElement, 'Attempt to show widget that is not hidden using hideWidget().');
Blink Reformat4c46d092018-04-07 15:32:37285 this._showWidget(/** @type {!Element} */ (this.element.parentElement), this.element.nextSibling);
286 }
287
288 /**
289 * @param {!Element} parentElement
290 * @param {?Node=} insertBefore
291 */
292 _showWidget(parentElement, insertBefore) {
293 let currentParent = parentElement;
Tim van der Lippe1d6e57a2019-09-30 11:55:34294 while (currentParent && !currentParent.__widget) {
Blink Reformat4c46d092018-04-07 15:32:37295 currentParent = currentParent.parentElementOrShadowHost();
Tim van der Lippe1d6e57a2019-09-30 11:55:34296 }
Blink Reformat4c46d092018-04-07 15:32:37297
298 if (this._isRoot) {
Tim van der Lippe0830b3d2019-10-03 13:20:07299 Widget.__assert(!currentParent, 'Attempt to show root widget under another widget');
Blink Reformat4c46d092018-04-07 15:32:37300 } else {
Tim van der Lippe0830b3d2019-10-03 13:20:07301 Widget.__assert(
Blink Reformat4c46d092018-04-07 15:32:37302 currentParent && currentParent.__widget === this._parentWidget,
303 'Attempt to show under node belonging to alien widget');
304 }
305
306 const wasVisible = this._visible;
Tim van der Lippe1d6e57a2019-09-30 11:55:34307 if (wasVisible && this.element.parentElement === parentElement) {
Blink Reformat4c46d092018-04-07 15:32:37308 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34309 }
Blink Reformat4c46d092018-04-07 15:32:37310
311 this._visible = true;
312
Tim van der Lippe1d6e57a2019-09-30 11:55:34313 if (!wasVisible && this._parentIsShowing()) {
Blink Reformat4c46d092018-04-07 15:32:37314 this._processWillShow();
Tim van der Lippe1d6e57a2019-09-30 11:55:34315 }
Blink Reformat4c46d092018-04-07 15:32:37316
317 this.element.classList.remove('hidden');
318
319 // Reparent
320 if (this.element.parentElement !== parentElement) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34321 if (!this._externallyManaged) {
Tim van der Lippe0830b3d2019-10-03 13:20:07322 Widget._incrementWidgetCounter(parentElement, this.element);
Tim van der Lippe1d6e57a2019-09-30 11:55:34323 }
324 if (insertBefore) {
Tim van der Lippe0830b3d2019-10-03 13:20:07325 Widget._originalInsertBefore.call(parentElement, this.element, insertBefore);
Tim van der Lippe1d6e57a2019-09-30 11:55:34326 } else {
Tim van der Lippe0830b3d2019-10-03 13:20:07327 Widget._originalAppendChild.call(parentElement, this.element);
Tim van der Lippe1d6e57a2019-09-30 11:55:34328 }
Blink Reformat4c46d092018-04-07 15:32:37329 }
330
Tim van der Lippe1d6e57a2019-09-30 11:55:34331 if (!wasVisible && this._parentIsShowing()) {
Blink Reformat4c46d092018-04-07 15:32:37332 this._processWasShown();
Tim van der Lippe1d6e57a2019-09-30 11:55:34333 }
Blink Reformat4c46d092018-04-07 15:32:37334
Tim van der Lippe1d6e57a2019-09-30 11:55:34335 if (this._parentWidget && this._hasNonZeroConstraints()) {
Blink Reformat4c46d092018-04-07 15:32:37336 this._parentWidget.invalidateConstraints();
Tim van der Lippe1d6e57a2019-09-30 11:55:34337 } else {
Blink Reformat4c46d092018-04-07 15:32:37338 this._processOnResize();
Tim van der Lippe1d6e57a2019-09-30 11:55:34339 }
Blink Reformat4c46d092018-04-07 15:32:37340 }
341
342 hideWidget() {
Tim van der Lippe1d6e57a2019-09-30 11:55:34343 if (!this._visible) {
Blink Reformat4c46d092018-04-07 15:32:37344 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34345 }
Blink Reformat4c46d092018-04-07 15:32:37346 this._hideWidget(false);
347 }
348
349 /**
350 * @param {boolean} removeFromDOM
351 */
352 _hideWidget(removeFromDOM) {
353 this._visible = false;
354 const parentElement = this.element.parentElement;
355
Tim van der Lippe1d6e57a2019-09-30 11:55:34356 if (this._parentIsShowing()) {
Blink Reformat4c46d092018-04-07 15:32:37357 this._processWillHide();
Tim van der Lippe1d6e57a2019-09-30 11:55:34358 }
Blink Reformat4c46d092018-04-07 15:32:37359
360 if (removeFromDOM) {
361 // Force legal removal
Tim van der Lippe0830b3d2019-10-03 13:20:07362 Widget._decrementWidgetCounter(parentElement, this.element);
363 Widget._originalRemoveChild.call(parentElement, this.element);
Blink Reformat4c46d092018-04-07 15:32:37364 } else {
365 this.element.classList.add('hidden');
366 }
367
Tim van der Lippe1d6e57a2019-09-30 11:55:34368 if (this._parentIsShowing()) {
Blink Reformat4c46d092018-04-07 15:32:37369 this._processWasHidden();
Tim van der Lippe1d6e57a2019-09-30 11:55:34370 }
371 if (this._parentWidget && this._hasNonZeroConstraints()) {
Blink Reformat4c46d092018-04-07 15:32:37372 this._parentWidget.invalidateConstraints();
Tim van der Lippe1d6e57a2019-09-30 11:55:34373 }
Blink Reformat4c46d092018-04-07 15:32:37374 }
375
376 /**
377 * @param {boolean=} overrideHideOnDetach
378 */
379 detach(overrideHideOnDetach) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34380 if (!this._parentWidget && !this._isRoot) {
Blink Reformat4c46d092018-04-07 15:32:37381 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34382 }
Blink Reformat4c46d092018-04-07 15:32:37383
384 // hideOnDetach means that we should never remove element from dom - content
385 // has iframes and detaching it will hurt.
386 //
387 // overrideHideOnDetach will override hideOnDetach and the client takes
388 // responsibility for the consequences.
389 const removeFromDOM = overrideHideOnDetach || !this.shouldHideOnDetach();
390 if (this._visible) {
391 this._hideWidget(removeFromDOM);
392 } else if (removeFromDOM && this.element.parentElement) {
393 const parentElement = this.element.parentElement;
394 // Force kick out from DOM.
Tim van der Lippe0830b3d2019-10-03 13:20:07395 Widget._decrementWidgetCounter(parentElement, this.element);
396 Widget._originalRemoveChild.call(parentElement, this.element);
Blink Reformat4c46d092018-04-07 15:32:37397 }
398
399 // Update widget hierarchy.
400 if (this._parentWidget) {
401 const childIndex = this._parentWidget._children.indexOf(this);
Tim van der Lippe0830b3d2019-10-03 13:20:07402 Widget.__assert(childIndex >= 0, 'Attempt to remove non-child widget');
Blink Reformat4c46d092018-04-07 15:32:37403 this._parentWidget._children.splice(childIndex, 1);
Tim van der Lippe1d6e57a2019-09-30 11:55:34404 if (this._parentWidget._defaultFocusedChild === this) {
Blink Reformat4c46d092018-04-07 15:32:37405 this._parentWidget._defaultFocusedChild = null;
Tim van der Lippe1d6e57a2019-09-30 11:55:34406 }
Blink Reformat4c46d092018-04-07 15:32:37407 this._parentWidget.childWasDetached(this);
408 this._parentWidget = null;
409 } else {
Tim van der Lippe0830b3d2019-10-03 13:20:07410 Widget.__assert(this._isRoot, 'Removing non-root widget from DOM');
Blink Reformat4c46d092018-04-07 15:32:37411 }
412 }
413
414 detachChildWidgets() {
415 const children = this._children.slice();
Tim van der Lippe1d6e57a2019-09-30 11:55:34416 for (let i = 0; i < children.length; ++i) {
Blink Reformat4c46d092018-04-07 15:32:37417 children[i].detach();
Tim van der Lippe1d6e57a2019-09-30 11:55:34418 }
Blink Reformat4c46d092018-04-07 15:32:37419 }
420
421 /**
422 * @return {!Array.<!Element>}
423 */
424 elementsToRestoreScrollPositionsFor() {
425 return [this.element];
426 }
427
428 storeScrollPositions() {
429 const elements = this.elementsToRestoreScrollPositionsFor();
430 for (let i = 0; i < elements.length; ++i) {
431 const container = elements[i];
432 container._scrollTop = container.scrollTop;
433 container._scrollLeft = container.scrollLeft;
434 }
435 }
436
437 restoreScrollPositions() {
438 const elements = this.elementsToRestoreScrollPositionsFor();
439 for (let i = 0; i < elements.length; ++i) {
440 const container = elements[i];
Tim van der Lippe1d6e57a2019-09-30 11:55:34441 if (container._scrollTop) {
Blink Reformat4c46d092018-04-07 15:32:37442 container.scrollTop = container._scrollTop;
Tim van der Lippe1d6e57a2019-09-30 11:55:34443 }
444 if (container._scrollLeft) {
Blink Reformat4c46d092018-04-07 15:32:37445 container.scrollLeft = container._scrollLeft;
Tim van der Lippe1d6e57a2019-09-30 11:55:34446 }
Blink Reformat4c46d092018-04-07 15:32:37447 }
448 }
449
450 doResize() {
Tim van der Lippe1d6e57a2019-09-30 11:55:34451 if (!this.isShowing()) {
Blink Reformat4c46d092018-04-07 15:32:37452 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34453 }
Blink Reformat4c46d092018-04-07 15:32:37454 // No matter what notification we are in, dispatching onResize is not needed.
Tim van der Lippe1d6e57a2019-09-30 11:55:34455 if (!this._inNotification()) {
Blink Reformat4c46d092018-04-07 15:32:37456 this._callOnVisibleChildren(this._processOnResize);
Tim van der Lippe1d6e57a2019-09-30 11:55:34457 }
Blink Reformat4c46d092018-04-07 15:32:37458 }
459
460 doLayout() {
Tim van der Lippe1d6e57a2019-09-30 11:55:34461 if (!this.isShowing()) {
Blink Reformat4c46d092018-04-07 15:32:37462 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34463 }
Blink Reformat4c46d092018-04-07 15:32:37464 this._notify(this.onLayout);
465 this.doResize();
466 }
467
468 /**
469 * @param {string} cssFile
470 */
471 registerRequiredCSS(cssFile) {
Paul Lewis9950e182019-12-16 16:06:07472 appendStyle(this._isWebComponent ? this._shadowRoot : this.element, cssFile);
Blink Reformat4c46d092018-04-07 15:32:37473 }
474
475 printWidgetHierarchy() {
476 const lines = [];
477 this._collectWidgetHierarchy('', lines);
478 console.log(lines.join('\n')); // eslint-disable-line no-console
479 }
480
481 _collectWidgetHierarchy(prefix, lines) {
482 lines.push(prefix + '[' + this.element.className + ']' + (this._children.length ? ' {' : ''));
483
Tim van der Lippe1d6e57a2019-09-30 11:55:34484 for (let i = 0; i < this._children.length; ++i) {
Blink Reformat4c46d092018-04-07 15:32:37485 this._children[i]._collectWidgetHierarchy(prefix + ' ', lines);
Tim van der Lippe1d6e57a2019-09-30 11:55:34486 }
Blink Reformat4c46d092018-04-07 15:32:37487
Tim van der Lippe1d6e57a2019-09-30 11:55:34488 if (this._children.length) {
Blink Reformat4c46d092018-04-07 15:32:37489 lines.push(prefix + '}');
Tim van der Lippe1d6e57a2019-09-30 11:55:34490 }
Blink Reformat4c46d092018-04-07 15:32:37491 }
492
493 /**
494 * @param {?Element} element
495 */
496 setDefaultFocusedElement(element) {
497 this._defaultFocusedElement = element;
498 }
499
500 /**
Tim van der Lippe0830b3d2019-10-03 13:20:07501 * @param {!Widget} child
Blink Reformat4c46d092018-04-07 15:32:37502 */
503 setDefaultFocusedChild(child) {
Tim van der Lippe0830b3d2019-10-03 13:20:07504 Widget.__assert(child._parentWidget === this, 'Attempt to set non-child widget as default focused.');
Blink Reformat4c46d092018-04-07 15:32:37505 this._defaultFocusedChild = child;
506 }
507
508 focus() {
Tim van der Lippe1d6e57a2019-09-30 11:55:34509 if (!this.isShowing()) {
Blink Reformat4c46d092018-04-07 15:32:37510 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34511 }
Blink Reformat4c46d092018-04-07 15:32:37512
513 const element = this._defaultFocusedElement;
514 if (element) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34515 if (!element.hasFocus()) {
Blink Reformat4c46d092018-04-07 15:32:37516 element.focus();
Tim van der Lippe1d6e57a2019-09-30 11:55:34517 }
Blink Reformat4c46d092018-04-07 15:32:37518 return;
519 }
520
521 if (this._defaultFocusedChild && this._defaultFocusedChild._visible) {
522 this._defaultFocusedChild.focus();
523 } else {
524 for (const child of this._children) {
525 if (child._visible) {
526 child.focus();
527 return;
528 }
529 }
530 let child = this.contentElement.traverseNextNode(this.contentElement);
531 while (child) {
Paul Lewis9950e182019-12-16 16:06:07532 if (child instanceof XWidget) {
Blink Reformat4c46d092018-04-07 15:32:37533 child.focus();
534 return;
535 }
536 child = child.traverseNextNode(this.contentElement);
537 }
538 }
539 }
540
541 /**
542 * @return {boolean}
543 */
544 hasFocus() {
545 return this.element.hasFocus();
546 }
547
548 /**
Paul Lewis9950e182019-12-16 16:06:07549 * @return {!Constraints}
Blink Reformat4c46d092018-04-07 15:32:37550 */
551 calculateConstraints() {
Paul Lewis9950e182019-12-16 16:06:07552 return new Constraints();
Blink Reformat4c46d092018-04-07 15:32:37553 }
554
555 /**
Paul Lewis9950e182019-12-16 16:06:07556 * @return {!Constraints}
Blink Reformat4c46d092018-04-07 15:32:37557 */
558 constraints() {
Tim van der Lippe1d6e57a2019-09-30 11:55:34559 if (typeof this._constraints !== 'undefined') {
Blink Reformat4c46d092018-04-07 15:32:37560 return this._constraints;
Tim van der Lippe1d6e57a2019-09-30 11:55:34561 }
562 if (typeof this._cachedConstraints === 'undefined') {
Blink Reformat4c46d092018-04-07 15:32:37563 this._cachedConstraints = this.calculateConstraints();
Tim van der Lippe1d6e57a2019-09-30 11:55:34564 }
Blink Reformat4c46d092018-04-07 15:32:37565 return this._cachedConstraints;
566 }
567
568 /**
569 * @param {number} width
570 * @param {number} height
571 * @param {number} preferredWidth
572 * @param {number} preferredHeight
573 */
574 setMinimumAndPreferredSizes(width, height, preferredWidth, preferredHeight) {
Paul Lewis9950e182019-12-16 16:06:07575 this._constraints = new Constraints(new Size(width, height), new Size(preferredWidth, preferredHeight));
Blink Reformat4c46d092018-04-07 15:32:37576 this.invalidateConstraints();
577 }
578
579 /**
580 * @param {number} width
581 * @param {number} height
582 */
583 setMinimumSize(width, height) {
Paul Lewis9950e182019-12-16 16:06:07584 this._constraints = new Constraints(new Size(width, height));
Blink Reformat4c46d092018-04-07 15:32:37585 this.invalidateConstraints();
586 }
587
588 /**
589 * @return {boolean}
590 */
591 _hasNonZeroConstraints() {
592 const constraints = this.constraints();
593 return !!(
594 constraints.minimum.width || constraints.minimum.height || constraints.preferred.width ||
595 constraints.preferred.height);
596 }
597
598 suspendInvalidations() {
599 ++this._invalidationsSuspended;
600 }
601
602 resumeInvalidations() {
603 --this._invalidationsSuspended;
Tim van der Lippe1d6e57a2019-09-30 11:55:34604 if (!this._invalidationsSuspended && this._invalidationsRequested) {
Blink Reformat4c46d092018-04-07 15:32:37605 this.invalidateConstraints();
Tim van der Lippe1d6e57a2019-09-30 11:55:34606 }
Blink Reformat4c46d092018-04-07 15:32:37607 }
608
609 invalidateConstraints() {
610 if (this._invalidationsSuspended) {
611 this._invalidationsRequested = true;
612 return;
613 }
614 this._invalidationsRequested = false;
615 const cached = this._cachedConstraints;
616 delete this._cachedConstraints;
617 const actual = this.constraints();
Tim van der Lippe1d6e57a2019-09-30 11:55:34618 if (!actual.isEqual(cached) && this._parentWidget) {
Blink Reformat4c46d092018-04-07 15:32:37619 this._parentWidget.invalidateConstraints();
Tim van der Lippe1d6e57a2019-09-30 11:55:34620 } else {
Blink Reformat4c46d092018-04-07 15:32:37621 this.doLayout();
Tim van der Lippe1d6e57a2019-09-30 11:55:34622 }
Blink Reformat4c46d092018-04-07 15:32:37623 }
Olivia Flynn1d938e42019-09-23 08:13:40624
625 // Excludes the widget from being tracked by its parents/ancestors via
626 // __widgetCounter because the widget is being handled by external code.
627 // Widgets marked as being externally managed are responsible for
628 // finishing out their own lifecycle (i.e. calling detach() before being
629 // removed from the DOM). This is e.g. used for CodeMirror.
630 //
631 // Also note that this must be called before the widget is shown so that
632 // so that its ancestor's __widgetCounter is not incremented.
633 markAsExternallyManaged() {
Tim van der Lippe0830b3d2019-10-03 13:20:07634 Widget.__assert(!this._parentWidget, 'Attempt to mark widget as externally managed after insertion to the DOM');
Olivia Flynn1d938e42019-09-23 08:13:40635 this._externallyManaged = true;
636 }
Tim van der Lippe0830b3d2019-10-03 13:20:07637}
Blink Reformat4c46d092018-04-07 15:32:37638
Tim van der Lippe0830b3d2019-10-03 13:20:07639export const _originalAppendChild = Element.prototype.appendChild;
640export const _originalInsertBefore = Element.prototype.insertBefore;
641export const _originalRemoveChild = Element.prototype.removeChild;
642export const _originalRemoveChildren = Element.prototype.removeChildren;
Blink Reformat4c46d092018-04-07 15:32:37643
644
645/**
646 * @unrestricted
647 */
Tim van der Lippe0830b3d2019-10-03 13:20:07648export class VBox extends Widget {
Blink Reformat4c46d092018-04-07 15:32:37649 /**
650 * @param {boolean=} isWebComponent
Joel Einbinder7fbe24c2019-01-24 05:19:01651 * @param {boolean=} delegatesFocus
Blink Reformat4c46d092018-04-07 15:32:37652 */
Joel Einbinder7fbe24c2019-01-24 05:19:01653 constructor(isWebComponent, delegatesFocus) {
654 super(isWebComponent, delegatesFocus);
Blink Reformat4c46d092018-04-07 15:32:37655 this.contentElement.classList.add('vbox');
656 }
657
658 /**
659 * @override
Paul Lewis9950e182019-12-16 16:06:07660 * @return {!Constraints}
Blink Reformat4c46d092018-04-07 15:32:37661 */
662 calculateConstraints() {
Paul Lewis9950e182019-12-16 16:06:07663 let constraints = new Constraints();
Blink Reformat4c46d092018-04-07 15:32:37664
665 /**
Tim van der Lippe0830b3d2019-10-03 13:20:07666 * @this {!Widget}
Blink Reformat4c46d092018-04-07 15:32:37667 * @suppressReceiverCheck
668 */
669 function updateForChild() {
670 const child = this.constraints();
671 constraints = constraints.widthToMax(child);
672 constraints = constraints.addHeight(child);
673 }
674
675 this._callOnVisibleChildren(updateForChild);
676 return constraints;
677 }
Tim van der Lippe0830b3d2019-10-03 13:20:07678}
Blink Reformat4c46d092018-04-07 15:32:37679
680/**
681 * @unrestricted
682 */
Tim van der Lippe0830b3d2019-10-03 13:20:07683export class HBox extends Widget {
Blink Reformat4c46d092018-04-07 15:32:37684 /**
685 * @param {boolean=} isWebComponent
686 */
687 constructor(isWebComponent) {
688 super(isWebComponent);
689 this.contentElement.classList.add('hbox');
690 }
691
692 /**
693 * @override
Paul Lewis9950e182019-12-16 16:06:07694 * @return {!Constraints}
Blink Reformat4c46d092018-04-07 15:32:37695 */
696 calculateConstraints() {
Paul Lewis9950e182019-12-16 16:06:07697 let constraints = new Constraints();
Blink Reformat4c46d092018-04-07 15:32:37698
699 /**
Tim van der Lippe0830b3d2019-10-03 13:20:07700 * @this {!Widget}
Blink Reformat4c46d092018-04-07 15:32:37701 * @suppressReceiverCheck
702 */
703 function updateForChild() {
704 const child = this.constraints();
705 constraints = constraints.addWidth(child);
706 constraints = constraints.heightToMax(child);
707 }
708
709 this._callOnVisibleChildren(updateForChild);
710 return constraints;
711 }
Tim van der Lippe0830b3d2019-10-03 13:20:07712}
Blink Reformat4c46d092018-04-07 15:32:37713
714/**
715 * @unrestricted
716 */
Tim van der Lippe0830b3d2019-10-03 13:20:07717export class VBoxWithResizeCallback extends VBox {
Blink Reformat4c46d092018-04-07 15:32:37718 /**
719 * @param {function()} resizeCallback
720 */
721 constructor(resizeCallback) {
722 super();
723 this._resizeCallback = resizeCallback;
724 }
725
726 /**
727 * @override
728 */
729 onResize() {
730 this._resizeCallback();
731 }
Tim van der Lippe0830b3d2019-10-03 13:20:07732}
Blink Reformat4c46d092018-04-07 15:32:37733
734/**
735 * @unrestricted
736 */
Tim van der Lippe0830b3d2019-10-03 13:20:07737export class WidgetFocusRestorer {
Blink Reformat4c46d092018-04-07 15:32:37738 /**
Tim van der Lippe0830b3d2019-10-03 13:20:07739 * @param {!Widget} widget
Blink Reformat4c46d092018-04-07 15:32:37740 */
741 constructor(widget) {
742 this._widget = widget;
743 this._previous = widget.element.ownerDocument.deepActiveElement();
744 widget.focus();
745 }
746
747 restore() {
Tim van der Lippe1d6e57a2019-09-30 11:55:34748 if (!this._widget) {
Blink Reformat4c46d092018-04-07 15:32:37749 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34750 }
751 if (this._widget.hasFocus() && this._previous) {
Blink Reformat4c46d092018-04-07 15:32:37752 this._previous.focus();
Tim van der Lippe1d6e57a2019-09-30 11:55:34753 }
Blink Reformat4c46d092018-04-07 15:32:37754 this._previous = null;
755 this._widget = null;
756 }
Tim van der Lippe0830b3d2019-10-03 13:20:07757}
Blink Reformat4c46d092018-04-07 15:32:37758
759/**
760 * @override
761 * @param {?Node} child
762 * @return {!Node}
763 * @suppress {duplicate}
764 */
765Element.prototype.appendChild = function(child) {
Tim van der Lippe0830b3d2019-10-03 13:20:07766 Widget.__assert(!child.__widget || child.parentElement === this, 'Attempt to add widget via regular DOM operation.');
767 return Widget._originalAppendChild.call(this, child);
Blink Reformat4c46d092018-04-07 15:32:37768};
769
770/**
771 * @override
772 * @param {?Node} child
773 * @param {?Node} anchor
774 * @return {!Node}
775 * @suppress {duplicate}
776 */
777Element.prototype.insertBefore = function(child, anchor) {
Tim van der Lippe0830b3d2019-10-03 13:20:07778 Widget.__assert(!child.__widget || child.parentElement === this, 'Attempt to add widget via regular DOM operation.');
779 return Widget._originalInsertBefore.call(this, child, anchor);
Blink Reformat4c46d092018-04-07 15:32:37780};
781
782/**
783 * @override
784 * @param {?Node} child
785 * @return {!Node}
786 * @suppress {duplicate}
787 */
788Element.prototype.removeChild = function(child) {
Tim van der Lippe0830b3d2019-10-03 13:20:07789 Widget.__assert(
Blink Reformat4c46d092018-04-07 15:32:37790 !child.__widgetCounter && !child.__widget,
791 'Attempt to remove element containing widget via regular DOM operation');
Tim van der Lippe0830b3d2019-10-03 13:20:07792 return Widget._originalRemoveChild.call(this, child);
Blink Reformat4c46d092018-04-07 15:32:37793};
794
795Element.prototype.removeChildren = function() {
Tim van der Lippe0830b3d2019-10-03 13:20:07796 Widget.__assert(!this.__widgetCounter, 'Attempt to remove element containing widget via regular DOM operation');
797 Widget._originalRemoveChildren.call(this);
Blink Reformat4c46d092018-04-07 15:32:37798};
Tim van der Lippe0830b3d2019-10-03 13:20:07799
Tim van der Lippe0830b3d2019-10-03 13:20:07800Widget._originalAppendChild = _originalAppendChild;
801Widget._originalInsertBefore = _originalInsertBefore;
802Widget._originalRemoveChild = _originalRemoveChild;
803Widget._originalRemoveChildren = _originalRemoveChildren;