Convert ui/ to ESM

- ViewManager has been extracted from View
- Action has been extracted from ActionRegistry
- ContextFlavorListener has been extracted from Context

Bug: 1006759
Change-Id: Iff41f3a7db4dcbd6da90fd5752923e32af03bac8
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1827294
Commit-Queue: Tim Van der Lippe <[email protected]>
Reviewed-by: Yang Guo <[email protected]>
Cr-Original-Commit-Position: refs/heads/master@{#702390}
Cr-Mirrored-From: https://chromium.googlesource.com/chromium/src
Cr-Mirrored-Commit: 0ffd51ccac853da7c8ec3d91ae868bc61626b29b
diff --git a/front_end/ui/PopoverHelper.js b/front_end/ui/PopoverHelper.js
new file mode 100644
index 0000000..5f8fd76
--- /dev/null
+++ b/front_end/ui/PopoverHelper.js
@@ -0,0 +1,291 @@
+/*
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+export default class PopoverHelper {
+  /**
+   * @param {!Element} container
+   * @param {function(!MouseEvent):?UI.PopoverRequest} getRequest
+   */
+  constructor(container, getRequest) {
+    this._disableOnClick = false;
+    this._hasPadding = false;
+    this._getRequest = getRequest;
+    this._scheduledRequest = null;
+    /** @type {?function()} */
+    this._hidePopoverCallback = null;
+    this._container = container;
+    this._showTimeout = 0;
+    this._hideTimeout = 0;
+    /** @type {?number} */
+    this._hidePopoverTimer = null;
+    /** @type {?number} */
+    this._showPopoverTimer = null;
+    this._boundMouseDown = this._mouseDown.bind(this);
+    this._boundMouseMove = this._mouseMove.bind(this);
+    this._boundMouseOut = this._mouseOut.bind(this);
+    this._container.addEventListener('mousedown', this._boundMouseDown, false);
+    this._container.addEventListener('mousemove', this._boundMouseMove, false);
+    this._container.addEventListener('mouseout', this._boundMouseOut, false);
+    this.setTimeout(1000);
+  }
+
+  /**
+   * @param {number} showTimeout
+   * @param {number=} hideTimeout
+   */
+  setTimeout(showTimeout, hideTimeout) {
+    this._showTimeout = showTimeout;
+    this._hideTimeout = typeof hideTimeout === 'number' ? hideTimeout : showTimeout / 2;
+  }
+
+  /**
+   * @param {boolean} hasPadding
+   */
+  setHasPadding(hasPadding) {
+    this._hasPadding = hasPadding;
+  }
+
+  /**
+   * @param {boolean} disableOnClick
+   */
+  setDisableOnClick(disableOnClick) {
+    this._disableOnClick = disableOnClick;
+  }
+
+  /**
+   * @param {!Event} event
+   * @return {boolean}
+   */
+  _eventInScheduledContent(event) {
+    return this._scheduledRequest ? this._scheduledRequest.box.contains(event.clientX, event.clientY) : false;
+  }
+
+  /**
+   * @param {!Event} event
+   */
+  _mouseDown(event) {
+    if (this._disableOnClick) {
+      this.hidePopover();
+      return;
+    }
+    if (this._eventInScheduledContent(event)) {
+      return;
+    }
+
+    this._startHidePopoverTimer(0);
+    this._stopShowPopoverTimer();
+    this._startShowPopoverTimer(/** @type {!MouseEvent} */ (event), 0);
+  }
+
+  /**
+   * @param {!Event} event
+   */
+  _mouseMove(event) {
+    // Pretend that nothing has happened.
+    if (this._eventInScheduledContent(event)) {
+      return;
+    }
+
+    this._startHidePopoverTimer(this._hideTimeout);
+    this._stopShowPopoverTimer();
+    if (event.which && this._disableOnClick) {
+      return;
+    }
+    this._startShowPopoverTimer(
+        /** @type {!MouseEvent} */ (event), this.isPopoverVisible() ? this._showTimeout * 0.6 : this._showTimeout);
+  }
+
+  /**
+   * @param {!Event} event
+   */
+  _popoverMouseMove(event) {
+    this._stopHidePopoverTimer();
+  }
+
+  /**
+   * @param {!UI.GlassPane} popover
+   * @param {!Event} event
+   */
+  _popoverMouseOut(popover, event) {
+    if (!popover.isShowing()) {
+      return;
+    }
+    if (event.relatedTarget && !event.relatedTarget.isSelfOrDescendant(popover.contentElement)) {
+      this._startHidePopoverTimer(this._hideTimeout);
+    }
+  }
+
+  /**
+   * @param {!Event} event
+   */
+  _mouseOut(event) {
+    if (!this.isPopoverVisible()) {
+      return;
+    }
+    if (!this._eventInScheduledContent(event)) {
+      this._startHidePopoverTimer(this._hideTimeout);
+    }
+  }
+
+  /**
+   * @param {number} timeout
+   */
+  _startHidePopoverTimer(timeout) {
+    // User has |timeout| ms to reach the popup.
+    if (!this._hidePopoverCallback || this._hidePopoverTimer) {
+      return;
+    }
+
+    this._hidePopoverTimer = setTimeout(() => {
+      this._hidePopover();
+      this._hidePopoverTimer = null;
+    }, timeout);
+  }
+
+  /**
+   * @param {!MouseEvent} event
+   * @param {number} timeout
+   */
+  _startShowPopoverTimer(event, timeout) {
+    this._scheduledRequest = this._getRequest.call(null, event);
+    if (!this._scheduledRequest) {
+      return;
+    }
+
+    this._showPopoverTimer = setTimeout(() => {
+      this._showPopoverTimer = null;
+      this._stopHidePopoverTimer();
+      this._hidePopover();
+      this._showPopover(event.target.ownerDocument);
+    }, timeout);
+  }
+
+  _stopShowPopoverTimer() {
+    if (!this._showPopoverTimer) {
+      return;
+    }
+    clearTimeout(this._showPopoverTimer);
+    this._showPopoverTimer = null;
+  }
+
+  /**
+   * @return {boolean}
+   */
+  isPopoverVisible() {
+    return !!this._hidePopoverCallback;
+  }
+
+  hidePopover() {
+    this._stopShowPopoverTimer();
+    this._hidePopover();
+  }
+
+  _hidePopover() {
+    if (!this._hidePopoverCallback) {
+      return;
+    }
+    this._hidePopoverCallback.call(null);
+    this._hidePopoverCallback = null;
+  }
+
+  /**
+   * @param {!Document} document
+   */
+  _showPopover(document) {
+    const popover = new UI.GlassPane();
+    popover.registerRequiredCSS('ui/popover.css');
+    popover.setSizeBehavior(UI.GlassPane.SizeBehavior.MeasureContent);
+    popover.setMarginBehavior(UI.GlassPane.MarginBehavior.Arrow);
+    const request = this._scheduledRequest;
+    request.show.call(null, popover).then(success => {
+      if (!success) {
+        return;
+      }
+
+      if (this._scheduledRequest !== request) {
+        if (request.hide) {
+          request.hide.call(null);
+        }
+        return;
+      }
+
+      // This should not happen, but we hide previous popover to be on the safe side.
+      if (PopoverHelper._popoverHelper) {
+        console.error('One popover is already visible');
+        PopoverHelper._popoverHelper.hidePopover();
+      }
+      PopoverHelper._popoverHelper = this;
+
+      popover.contentElement.classList.toggle('has-padding', this._hasPadding);
+      popover.contentElement.addEventListener('mousemove', this._popoverMouseMove.bind(this), true);
+      popover.contentElement.addEventListener('mouseout', this._popoverMouseOut.bind(this, popover), true);
+      popover.setContentAnchorBox(request.box);
+      popover.show(document);
+
+      this._hidePopoverCallback = () => {
+        if (request.hide) {
+          request.hide.call(null);
+        }
+        popover.hide();
+        delete PopoverHelper._popoverHelper;
+      };
+    });
+  }
+
+  _stopHidePopoverTimer() {
+    if (!this._hidePopoverTimer) {
+      return;
+    }
+    clearTimeout(this._hidePopoverTimer);
+    this._hidePopoverTimer = null;
+
+    // We know that we reached the popup, but we might have moved over other elements.
+    // Discard pending command.
+    this._stopShowPopoverTimer();
+  }
+
+  dispose() {
+    this._container.removeEventListener('mousedown', this._boundMouseDown, false);
+    this._container.removeEventListener('mousemove', this._boundMouseMove, false);
+    this._container.removeEventListener('mouseout', this._boundMouseOut, false);
+  }
+}
+
+/** @typedef {{box: !AnchorBox, show:(function(!UI.GlassPane):!Promise<boolean>), hide:(function()|undefined)}} */
+UI.PopoverRequest;
+
+/* Legacy exported object*/
+self.UI = self.UI || {};
+
+/* Legacy exported object*/
+UI = UI || {};
+
+/** @constructor */
+UI.PopoverHelper = PopoverHelper;