webdriver icon indicating copy to clipboard operation
webdriver copied to clipboard

Update atoms sha

Open AutomatedTester opened this issue 2 years ago • 12 comments


Preview | Diff

AutomatedTester avatar Jul 08 '22 08:07 AutomatedTester

I think it would be good to say that this change is required to get the ShadowRoot support into the exported atoms.

@jgraham and @shs96c do you have any objections?

whimboo avatar Jul 08 '22 08:07 whimboo

diff --git a/javascript/atoms/dom.js b/javascript/atoms/dom.js
index f0a3b84da5..d4a9aae978 100644
--- a/javascript/atoms/dom.js
+++ b/javascript/atoms/dom.js
@@ -48,9 +48,9 @@ bot.dom.IS_SHADOW_DOM_ENABLED = (typeof ShadowRoot === 'function');
 
 /**
  * Retrieves the active element for a node's owner document.
- * @param {!(Node|Window)} nodeOrWindow The node whose owner document to get
+ * @param {(!Node|!Window)} nodeOrWindow The node whose owner document to get
  *     the active element for.
- * @return {Element} The active element, if any.
+ * @return {?Element} The active element, if any.
  */
 bot.dom.getActiveElement = function(nodeOrWindow) {
   var active = goog.dom.getActiveElement(
@@ -166,7 +166,7 @@ bot.dom.getAttribute = bot.dom.core.getAttribute;
 /**
  * List of elements that support the "disabled" attribute, as defined by the
  * HTML 4.01 specification.
- * @private {!Array.<goog.dom.TagName>}
+ * @private {!Array.<!goog.dom.TagName>}
  * @const
  * @see http://www.w3.org/TR/html401/interact/forms.html#h-17.12.1
  */
@@ -290,6 +290,21 @@ bot.dom.isFileInput = function(element) {
 };
 
 
+/**
+ * @param {!Element} element The element to check.
+ * @param {string} inputType The type of input to check.
+ * @return {boolean} Whether the element is an input with specified type.
+ */
+bot.dom.isInputType = function(element, inputType) {
+  if (bot.dom.isElement(element, goog.dom.TagName.INPUT)) {
+    var type = element.type.toLowerCase();
+    return type == inputType;
+  }
+
+  return false;
+};
+
+
 /**
  * @param {!Element} element The element to check.
  * @return {boolean} Whether the element is contentEditable.
@@ -331,7 +346,15 @@ bot.dom.isContentEditable = function(element) {
  * @return {boolean} Whether the element accepts user-typed text.
  */
 bot.dom.isEditable = function(element) {
-  return (bot.dom.isTextual(element) || bot.dom.isFileInput(element)) &&
+  return (bot.dom.isTextual(element) ||
+          bot.dom.isFileInput(element) ||
+          bot.dom.isInputType(element, 'range') ||
+          bot.dom.isInputType(element, 'date') ||
+          bot.dom.isInputType(element, 'month') ||
+          bot.dom.isInputType(element, 'week') ||
+          bot.dom.isInputType(element, 'time') ||
+          bot.dom.isInputType(element, 'datetime-local') ||
+          bot.dom.isInputType(element, 'color')) &&
       !bot.dom.getProperty(element, 'readOnly');
 };
 
@@ -548,45 +571,47 @@ bot.dom.isShown_ = function(elem, ignoreOpacity, parentsDisplayedFn) {
  * @return {boolean} Whether or not the element is visible.
  */
 bot.dom.isShown = function(elem, opt_ignoreOpacity) {
-  var displayed;
-
-  if (bot.dom.IS_SHADOW_DOM_ENABLED) {
-    // Any element with a display style equal to 'none' or that has an ancestor
-    // with display style equal to 'none' is not shown.
-    displayed = function(e) {
-      if (bot.dom.getEffectiveStyle(e, 'display') == 'none') {
+  /**
+   * Determines whether an element or its parents have `display: none` set
+   * @param {!Node} e the element
+   * @return {!boolean}
+   */
+  function displayed(e) {
+    if (bot.dom.isElement(e)) {
+      var elem = /** @type {!Element} */ (e);
+      if (bot.dom.getEffectiveStyle(elem, 'display') == 'none') {
         return false;
       }
-      var parent;
-      do {
-        parent = bot.dom.getParentNodeInComposedDom(e);
-        if (parent instanceof ShadowRoot) {
-          if (parent.host.shadowRoot != parent) {
-            // There is a younger shadow root, which will take precedence over
-            // the shadow this element is in, thus this element won't be
-            // displayed.
-            return false;
-          } else {
-            parent = parent.host;
-          }
-        } else if (parent && (parent.nodeType == goog.dom.NodeType.DOCUMENT ||
-            parent.nodeType == goog.dom.NodeType.DOCUMENT_FRAGMENT)) {
-          parent = null;
-        }
-      } while (elem && elem.nodeType != goog.dom.NodeType.ELEMENT);
-      return !parent || displayed(parent);
-    };
-  } else {
-    // Any element with a display style equal to 'none' or that has an ancestor
-    // with display style equal to 'none' is not shown.
-    displayed =  function(e) {
-      if (bot.dom.getEffectiveStyle(e, 'display') == 'none') {
+    }
+
+    var parent = bot.dom.getParentNodeInComposedDom(e);
+
+    if (bot.dom.IS_SHADOW_DOM_ENABLED && (parent instanceof ShadowRoot)) {
+      if (parent.host.shadowRoot !== parent) {
+        // There is a younger shadow root, which will take precedence over
+        // the shadow this element is in, thus this element won't be
+        // displayed.
         return false;
+      } else {
+        parent = parent.host;
       }
-      var parent = bot.dom.getParentElement(e);
-      return !parent || displayed(parent);
-    };
+    }
+
+    if (parent && (parent.nodeType == goog.dom.NodeType.DOCUMENT ||
+        parent.nodeType == goog.dom.NodeType.DOCUMENT_FRAGMENT)) {
+      return true;
+    }
+
+    // Child of DETAILS element is not shown unless the DETAILS element is open
+    // or the child is a SUMMARY element.
+    if (parent && bot.dom.isElement(parent, goog.dom.TagName.DETAILS) &&
+        !parent.open && !bot.dom.isElement(e, goog.dom.TagName.SUMMARY)) {
+      return false;
+    }
+
+    return !!parent && displayed(parent);
   }
+
   return bot.dom.isShown_(elem, !!opt_ignoreOpacity, displayed);
 };
 
@@ -645,10 +670,11 @@ bot.dom.getOverflowState = function(elem, opt_region) {
       if (container == htmlElem) {
         return true;
       }
-      // An element cannot overflow an element with an inline display style.
+      // An element cannot overflow an element with an inline or contents display style.
       var containerDisplay = /** @type {string} */ (
           bot.dom.getEffectiveStyle(container, 'display'));
-      if (goog.string.startsWith(containerDisplay, 'inline')) {
+      if (goog.string.startsWith(containerDisplay, 'inline') ||
+          (containerDisplay == 'contents')) {
         return false;
       }
       // An absolute-positioned element cannot overflow a static-positioned one.
@@ -1149,7 +1175,9 @@ bot.dom.appendVisibleTextLinesFromTextNode_ = function(textNode, lines,
   }
 
   if (textTransform == 'capitalize') {
-    text = text.replace(/(^|\s)(\S)/g, function() {
+    // the unicode regex ending with /gu does not work in IE
+    var re = goog.userAgent.IE ? /(^|\s|\b)(\S)/g : /(^|[^\d\p{L}\p{S}])([\p{Ll}|\p{S}])/gu;
+    text = text.replace(re, function() {
       return arguments[1] + arguments[2].toUpperCase();
     });
   } else if (textTransform == 'uppercase') {
@@ -1243,12 +1271,22 @@ bot.dom.getOpacityNonIE_ = function(elem) {
  */
 bot.dom.getParentNodeInComposedDom = function(node) {
   var /**@type {Node}*/ parent = node.parentNode;
+
+  // Shadow DOM v1
+  if (parent && parent.shadowRoot && node.assignedSlot !== undefined) {
+    // Can be null on purpose, meaning it has no parent as
+    // it hasn't yet been slotted
+    return node.assignedSlot ? node.assignedSlot.parentNode : null;
+  }
+
+  // Shadow DOM V0 (deprecated)
   if (node.getDestinationInsertionPoints) {
     var destinations = node.getDestinationInsertionPoints();
     if (destinations.length > 0) {
-      parent = destinations[destinations.length - 1];
+      return destinations[destinations.length - 1];
     }
   }
+
   return parent;
 };
 
@@ -1272,16 +1310,22 @@ bot.dom.appendVisibleTextLinesFromNodeInComposedDom_ = function(
   } else if (bot.dom.isElement(node)) {
     var castElem = /** @type {!Element} */ (node);
 
-    if (bot.dom.isElement(node, 'CONTENT')) {
+    if (bot.dom.isElement(node, 'CONTENT') || bot.dom.isElement(node, 'SLOT')) {
       var parentNode = node;
       while (parentNode.parentNode) {
         parentNode = parentNode.parentNode;
       }
       if (parentNode instanceof ShadowRoot) {
-        // If the element is <content> and we're inside a shadow DOM then just 
+        // If the element is <content> and we're inside a shadow DOM then just
         // append the contents of the nodes that have been distributed into it.
         var contentElem = /** @type {!Object} */ (node);
-        goog.array.forEach(contentElem.getDistributedNodes(), function(node) {
+        var shadowChildren;
+        if (bot.dom.isElement(node, 'CONTENT')) {
+          shadowChildren = contentElem.getDistributedNodes();
+        } else {
+          shadowChildren = contentElem.assignedNodes();
+        }
+        goog.array.forEach(shadowChildren, function(node) {
           bot.dom.appendVisibleTextLinesFromNodeInComposedDom_(
               node, lines, shown, whitespace, textTransform);
         });
@@ -1336,8 +1380,10 @@ bot.dom.isNodeDistributedIntoShadowDom = function(node) {
     elemOrText = /** @type {!Text} */ (node);
   }
   return elemOrText != null &&
-      elemOrText.getDestinationInsertionPoints &&
-      elemOrText.getDestinationInsertionPoints().length > 0;
+      (elemOrText.assignedSlot != null ||
+        (elemOrText.getDestinationInsertionPoints &&
+        elemOrText.getDestinationInsertionPoints().length > 0)
+      );
 };

jgraham avatar Jul 11 '22 12:07 jgraham

ping @sadym-chromium for getting feedback from Google.

whimboo avatar Aug 09 '22 19:08 whimboo

Adding @vladimir-nechaev who is working on ChromeDriver Classic

sadym-chromium avatar Aug 10 '22 11:08 sadym-chromium

diff --git a/javascript/atoms/dom.js b/javascript/atoms/dom.js

Thanks for providing the diff!

sadym-chromium avatar Aug 10 '22 11:08 sadym-chromium

I have updated the atoms to a6b161a159c3d581b130f03a2e6e35f577f38dec and run the code through the unit and integration tests. Looks to be working fine: https://chromium-review.googlesource.com/c/chromium/src/+/3827746 Currently running WPT.

vladimir-nechaev avatar Aug 12 '22 16:08 vladimir-nechaev

WPT show no difference. Looks to be ok.

vladimir-nechaev avatar Aug 12 '22 19:08 vladimir-nechaev

@gsnedders do you know who is working on safaridriver these days and could review this change as well?

whimboo avatar Aug 12 '22 19:08 whimboo

ping @gsnedders and @shs96c. Thanks!

whimboo avatar Aug 26 '22 20:08 whimboo

Looks like @patrickangle is the one to ask here for the safaridriver related update. Please let us know if that update is fine with you. Thanks!

whimboo avatar Aug 31 '22 12:08 whimboo

@AutomatedTester mind updating your PR regarding the recent review comments?

whimboo avatar Sep 09 '22 07:09 whimboo

Hi @jgraham as it looks like everyone is fine with the update of the atoms. Can we get it merged?

whimboo avatar Sep 23 '22 06:09 whimboo

Thanks James! I've triggered a try build in our CI with the proposed changes of the atoms included. If all tests pass we can merge this PR:

https://treeherder.mozilla.org/jobs?repo=try&revision=efddca836d3e1fda59ea3f8cf5bf0d9b3497a576

whimboo avatar Oct 13 '22 07:10 whimboo

Thanks James! I've triggered a try build in our CI with the proposed changes of the atoms included. If all tests pass we can merge this PR:

https://treeherder.mozilla.org/jobs?repo=try&revision=efddca836d3e1fda59ea3f8cf5bf0d9b3497a576

Everything is fine here. I'm going ahead and merge the PR. Thanks @AutomatedTester!

whimboo avatar Oct 13 '22 08:10 whimboo