Bug 1587627 - [marionette] Remove `Anon` and `AnonAttribute` strategies from "WebDriver:FindElement" and "WebDriver:FindElements" command. r=webdriver-reviewers,maja_zf
authorHenrik Skupin <mail@hskupin.info>
Tue, 29 Oct 2019 12:55:35 +0000
changeset 499649 f77279b36116704bc63db97b0c2906295688a908
parent 499648 6731460a1d5c5c23db99e82796050d379e8af862
child 499650 6a2b2f52d15eec99d18242f08407935b137dbf70
push id114161
push userncsoregi@mozilla.com
push dateTue, 29 Oct 2019 21:34:24 +0000
treeherdermozilla-inbound@25bf8e097e60 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerswebdriver-reviewers, maja_zf
bugs1587627
milestone72.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1587627 - [marionette] Remove `Anon` and `AnonAttribute` strategies from "WebDriver:FindElement" and "WebDriver:FindElements" command. r=webdriver-reviewers,maja_zf Differential Revision: https://phabricator.services.mozilla.com/D50804
browser/base/content/test/static/browser_all_files_referenced.js
testing/marionette/chrome/test_anonymous_content.xul
testing/marionette/client/docs/advanced/findelement.rst
testing/marionette/client/marionette_driver/by.py
testing/marionette/client/marionette_driver/marionette.py
testing/marionette/doc/internals/element.rst
testing/marionette/driver.js
testing/marionette/element.js
testing/marionette/harness/marionette_harness/tests/unit/test_anonymous_content.py
testing/marionette/harness/marionette_harness/tests/unit/unit-tests.ini
testing/marionette/jar.mn
testing/marionette/reftest.js
--- a/browser/base/content/test/static/browser_all_files_referenced.js
+++ b/browser/base/content/test/static/browser_all_files_referenced.js
@@ -167,17 +167,17 @@ var whitelist = [
   // Bug 1339424 (wontfix?)
   {
     file: "chrome://browser/locale/taskbar.properties",
     platforms: ["linux", "macosx"],
   },
   // Bug 1356031 (only used by devtools)
   { file: "chrome://global/skin/icons/error-16.png" },
   // Bug 1344267
-  { file: "chrome://marionette/content/test_anonymous_content.xul" },
+  { file: "chrome://marionette/content/test.xul" },
   { file: "chrome://marionette/content/test_dialog.properties" },
   { file: "chrome://marionette/content/test_dialog.xul" },
   // Bug 1348533
   {
     file: "chrome://mozapps/skin/downloads/buttons.png",
     platforms: ["macosx"],
   },
   {
deleted file mode 100644
--- a/testing/marionette/chrome/test_anonymous_content.xul
+++ /dev/null
@@ -1,49 +0,0 @@
-<?xml version="1.0"?>
-<!-- This Source Code Form is subject to the terms of the Mozilla Public
-   - License, v. 2.0. If a copy of the MPL was not distributed with this
-   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-
-<!DOCTYPE dialog [
-]>
-
-<dialog id="testDialogAnonymousNode"
-          buttons="accept, cancel"
-          xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
-
-  <bindings id="testBindings" xmlns="http://www.mozilla.org/xbl"
-            xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
-    <binding id="framebox">
-      <content orient="vertical" mousethrough="never">
-        <xul:browser anonid="content" id="browser" flex="1"
-                     context="contentAreaContextMenu"
-                     src="test.xul"
-                     type="content"/>
-      </content>
-    </binding>
-
-    <binding id="iframebox">
-      <content>
-        <xul:box>
-          <xul:iframe anonid="iframe" src="chrome://marionette/content/test.xul"></xul:iframe>
-        </xul:box>
-      </content>
-    </binding>
-
-    <binding id="buttonbox">
-      <content>
-        <xul:box anonid="buttons">
-          <xul:button dlgtype="cancel" class="dialog-button"/>
-          <xul:button dlgtype="accept" class="dialog-button"/>
-        </xul:box>
-        <xul:vbox></xul:vbox>
-        <xul:vbox></xul:vbox>
-      </content>
-    </binding>
-  </bindings>
-
-  <hbox id="testAnonymousContentBox"/>
-  <hbox id="container" style="-moz-binding: url('#testBindings');"/>
-  <hbox id="container2" style="-moz-binding: url('#iframebox');"/>
-  <hbox id="container3" style="-moz-binding: url('#buttonbox');"/>
-
-</dialog>
--- a/testing/marionette/client/docs/advanced/findelement.rst
+++ b/testing/marionette/client/docs/advanced/findelement.rst
@@ -80,47 +80,8 @@ Consider the following html snippet::
 
 Doing the following will work::
 
     client.find_element(By.ID, 'container').find_element(By.ID, 'main')
 
 But this will raise a `NoSuchElementException`::
 
     client.find_element(By.ID, 'container').find_element(By.ID, 'footer')
-
-
-Finding Anonymous Nodes
------------------------
-
-When working in chrome scope, for example manipulating the Firefox user
-interface, you may run into something called an anonymous node.
-
-Firefox uses a markup language called XUL_ for its interface. XUL is similar
-to HTML in that it has a DOM and tags that render controls on the display. One
-ability of XUL is to create re-useable widgets that are made up out of several
-smaller XUL elements. These widgets can be bound to the DOM using something
-called the `XML binding language (XBL)`_.
-
-The end result is that the DOM sees the widget as a single entity. It doesn't
-know anything about how that widget is made up. All of the smaller XUL elements
-that make up the widget are called `anonymous content`_. It is not possible to
-query such elements using traditional DOM methods like `getElementById`.
-
-Marionette provides two special strategies used for finding anonymous content.
-Unlike normal elements, anonymous nodes can only be seen by their parent. So
-it's necessary to first find the parent element and then search for the
-anonymous children from there.
-
-* `anon` - Finds all anonymous children of the element, there is no search term
-  so `None` must be passed in::
-
-    anon_children = client.find_element('id', 'parent').find_elements('anon', None)
-
-* `anon attribute` - Find an anonymous child based on an attribute. An
-  unofficial convention is for anonymous nodes to have an
-  `anonid` attribute::
-
-    anon_child = client.find_element('id', 'parent').find_element('anon attribute', {'anonid': 'container'})
-
-
-.. _XUL: https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XUL
-.. _XML binding language (XBL): https://developer.mozilla.org/en-US/docs/XBL
-.. _anonymous content: https://developer.mozilla.org/en-US/docs/XBL/XBL_1.0_Reference/Anonymous_Content
--- a/testing/marionette/client/marionette_driver/by.py
+++ b/testing/marionette/client/marionette_driver/by.py
@@ -20,10 +20,8 @@ class By(object):
     ID = "id"
     XPATH = "xpath"
     LINK_TEXT = "link text"
     PARTIAL_LINK_TEXT = "partial link text"
     NAME = "name"
     TAG_NAME = "tag name"
     CLASS_NAME = "class name"
     CSS_SELECTOR = "css selector"
-    ANON_ATTRIBUTE = "anon attribute"
-    ANON = "anon"
--- a/testing/marionette/client/marionette_driver/marionette.py
+++ b/testing/marionette/client/marionette_driver/marionette.py
@@ -1672,19 +1672,19 @@ class Marionette(object):
         is immediately found, the attempt to locate an element will be repeated
         for up to the amount of time set by
         :attr:`marionette_driver.timeout.Timeouts.implicit`. If multiple
         elements match the given criteria, only the first is returned. If no
         element matches, a ``NoSuchElementException`` will be raised.
 
         :param method: The method to use to locate the element; one of:
             "id", "name", "class name", "tag name", "css selector",
-            "link text", "partial link text", "xpath", "anon" and "anon
-            attribute". Note that the "name", "link text" and "partial
-            link test" methods are not supported in the chrome DOM.
+            "link text", "partial link text" and "xpath".
+            Note that the "name", "link text" and "partial link test"
+            methods are not supported in the chrome DOM.
         :param target: The target of the search.  For example, if method =
             "tag", target might equal "div".  If method = "id", target would
             be an element id.
         :param id: If specified, search for elements only inside the element
             with the specified id.
         """
         body = {"value": target, "using": method}
         if id:
@@ -1702,19 +1702,19 @@ class Marionette(object):
         used to call other methods on the element, such as
         :func:`~marionette_driver.marionette.HTMLElement.click`.  If no element
         is immediately found, the attempt to locate an element will be repeated
         for up to the amount of time set by
         :attr:`marionette_driver.timeout.Timeouts.implicit`.
 
         :param method: The method to use to locate the elements; one
             of: "id", "name", "class name", "tag name", "css selector",
-            "link text", "partial link text", "xpath", "anon" and "anon
-            attribute". Note that the "name", "link text" and "partial link
-            test" methods are not supported in the chrome DOM.
+            "link text", "partial link text" and "xpath".
+            Note that the "name", "link text" and "partial link test"
+            methods are not supported in the chrome DOM.
         :param target: The target of the search.  For example, if method =
             "tag", target might equal "div".  If method = "id", target would be
             an element id.
         :param id: If specified, search for elements only inside the element
             with the specified id.
         """
         body = {"value": target, "using": method}
         if id:
--- a/testing/marionette/doc/internals/element.rst
+++ b/testing/marionette/doc/internals/element.rst
@@ -21,20 +21,16 @@ element.findByXPathAll
 element.findByLinkText
 ----------------------
 .. js:autofunction:: element.findByLinkText
 
 element.findByPartialLinkText
 -----------------------------
 .. js:autofunction:: element.findByPartialLinkText
 
-element.findAnonymousNodes
-----------------------------
-.. js:autofunction:: element.findAnonymousNodes
-
 element.findClosest
 -------------------
 .. js:autofunction:: element.findClosest
 
 element.isCollection
 --------------------
 .. js:autofunction:: element.isCollection
 
--- a/testing/marionette/driver.js
+++ b/testing/marionette/driver.js
@@ -92,18 +92,16 @@ const FRAME_SCRIPT = "chrome://marionett
 const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 
 const SUPPORTED_STRATEGIES = new Set([
   element.Strategy.ClassName,
   element.Strategy.Selector,
   element.Strategy.ID,
   element.Strategy.TagName,
   element.Strategy.XPath,
-  element.Strategy.Anon,
-  element.Strategy.AnonAttribute,
 ]);
 
 // Timeout used to abort fullscreen, maximize, and minimize
 // commands if no window manager is present.
 const TIMEOUT_NO_WINDOW_MANAGER = 5000;
 
 const globalMessageManager = Services.mm;
 
@@ -1799,47 +1797,16 @@ GeckoDriver.prototype.switchToFrame = as
         checkTimer.initWithCallback(
           checkLoad.bind(this),
           100,
           Ci.nsITimer.TYPE_ONE_SHOT
         );
         return;
       }
 
-      // Check if the frame is XBL anonymous
-      let parent = curWindow.document.getBindingParent(wantedFrame);
-      // Shadow nodes also show up in getAnonymousNodes, we should
-      // ignore them.
-      if (
-        parent &&
-        !(parent.shadowRoot && parent.shadowRoot.contains(wantedFrame))
-      ) {
-        const doc = curWindow.document;
-        let anonNodes = [...(doc.getAnonymousNodes(parent) || [])];
-        if (anonNodes.length > 0) {
-          let el = wantedFrame;
-          while (el) {
-            if (anonNodes.indexOf(el) > -1) {
-              curWindow = wantedFrame.contentWindow;
-              this.curFrame = curWindow;
-              if (focus) {
-                this.curFrame.focus();
-              }
-              checkTimer.initWithCallback(
-                checkLoad.bind(this),
-                100,
-                Ci.nsITimer.TYPE_ONE_SHOT
-              );
-              return;
-            }
-            el = el.parentNode;
-          }
-        }
-      }
-
       // else, assume iframe
       let frames = curWindow.document.getElementsByTagName("iframe");
       let numFrames = frames.length;
       for (let i = 0; i < numFrames; i++) {
         let wrappedEl = new XPCNativeWrapper(frames[i]);
         let wrappedWanted = new XPCNativeWrapper(wantedFrame);
         if (wrappedEl == wrappedWanted) {
           curWindow = frames[i].contentWindow;
--- a/testing/marionette/element.js
+++ b/testing/marionette/element.js
@@ -77,18 +77,16 @@ element.Strategy = {
   ClassName: "class name",
   Selector: "css selector",
   ID: "id",
   Name: "name",
   LinkText: "link text",
   PartialLinkText: "partial link text",
   TagName: "tag name",
   XPath: "xpath",
-  Anon: "anon",
-  AnonAttribute: "anon attribute",
 };
 
 /**
  * Stores known/seen elements and their associated web element
  * references.
  *
  * Elements are added by calling {@link #add()} or {@link addAll()},
  * and may be queried by their web element reference using {@link get()}.
@@ -324,27 +322,17 @@ element.find = function(container, strat
       },
       { timeout }
     );
 
     findElements.then(foundEls => {
       // the following code ought to be moved into findElement
       // and findElements when bug 1254486 is addressed
       if (!opts.all && (!foundEls || foundEls.length == 0)) {
-        let msg;
-        switch (strategy) {
-          case element.Strategy.AnonAttribute:
-            msg =
-              "Unable to locate anonymous element: " + JSON.stringify(selector);
-            break;
-
-          default:
-            msg = "Unable to locate element: " + selector;
-        }
-
+        let msg = `Unable to locate element: ${selector}`;
         reject(new NoSuchElementError(msg));
       }
 
       if (opts.all) {
         resolve(foundEls);
       }
       resolve(foundEls[0]);
     }, reject);
@@ -356,29 +344,17 @@ function find_(
   strategy,
   selector,
   searchFn,
   { startNode = null, all = false } = {}
 ) {
   let rootNode = container.shadowRoot || container.frame.document;
 
   if (!startNode) {
-    switch (strategy) {
-      // For anonymous nodes the start node needs to be of type
-      // DOMElement, which will refer to :root in case of a DOMDocument.
-      case element.Strategy.Anon:
-      case element.Strategy.AnonAttribute:
-        if (rootNode.nodeType == rootNode.DOCUMENT_NODE) {
-          startNode = rootNode.documentElement;
-        }
-        break;
-
-      default:
-        startNode = rootNode;
-    }
+    startNode = rootNode;
   }
 
   let res;
   try {
     res = searchFn(strategy, selector, rootNode, startNode);
   } catch (e) {
     throw new InvalidSelectorError(
       `Given ${strategy} expression "${selector}" is invalid: ${e}`
@@ -480,34 +456,16 @@ element.findByLinkText = function(startN
  */
 element.findByPartialLinkText = function(startNode, linkText) {
   return filterLinks(startNode, link =>
     atom.getElementText(link).includes(linkText)
   );
 };
 
 /**
- * Find anonymous nodes of <var>node</var>.
- *
- * @param {HTMLDocument} document
- *     Root node of the document.
- * @param {XULElement} node
- *     Where in the DOM hierarchy to begin searching.
- *
- * @return {Iterable.<XULElement>}
- *     Iterator over anonymous elements.
- */
-element.findAnonymousNodes = function*(document, node) {
-  let anons = document.getAnonymousNodes(node) || [];
-  for (let node of anons) {
-    yield node;
-  }
-};
-
-/**
  * Filters all hyperlinks that are descendant of <var>startNode</var>
  * by <var>predicate</var>.
  *
  * @param {Element} startNode
  *     Where in the DOM hierarchy to begin searching.
  * @param {function(HTMLAnchorElement): boolean} predicate
  *     Function that determines if given link should be included in
  *     return value or filtered away.
@@ -587,27 +545,16 @@ function findElement(strategy, selector,
       return undefined;
 
     case element.Strategy.Selector:
       try {
         return startNode.querySelector(selector);
       } catch (e) {
         throw new InvalidSelectorError(`${e.message}: "${selector}"`);
       }
-
-    case element.Strategy.Anon:
-      return element.findAnonymousNodes(document, startNode).next().value;
-
-    case element.Strategy.AnonAttribute:
-      let attr = Object.keys(selector)[0];
-      return document.getAnonymousElementByAttribute(
-        startNode,
-        attr,
-        selector[attr]
-      );
   }
 
   throw new InvalidSelectorError(`No such strategy: ${strategy}`);
 }
 
 /**
  * Find multiple elements.
  *
@@ -659,31 +606,16 @@ function findElements(strategy, selector
       return [...element.findByLinkText(startNode, selector)];
 
     case element.Strategy.PartialLinkText:
       return [...element.findByPartialLinkText(startNode, selector)];
 
     case element.Strategy.Selector:
       return startNode.querySelectorAll(selector);
 
-    case element.Strategy.Anon:
-      return [...element.findAnonymousNodes(document, startNode)];
-
-    case element.Strategy.AnonAttribute:
-      let attr = Object.keys(selector)[0];
-      let el = document.getAnonymousElementByAttribute(
-        startNode,
-        attr,
-        selector[attr]
-      );
-      if (el) {
-        return [el];
-      }
-      return [];
-
     default:
       throw new InvalidSelectorError(`No such strategy: ${strategy}`);
   }
 }
 
 /**
  * Finds the closest parent node of <var>startNode</var> by CSS a
  * <var>selector</var> expression.
deleted file mode 100644
--- a/testing/marionette/harness/marionette_harness/tests/unit/test_anonymous_content.py
+++ /dev/null
@@ -1,78 +0,0 @@
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-from __future__ import absolute_import
-
-from marionette_driver.by import By
-from marionette_driver.errors import NoSuchElementException
-from marionette_driver.marionette import HTMLElement
-
-from marionette_harness import MarionetteTestCase, WindowManagerMixin
-
-
-class TestAnonymousNodes(WindowManagerMixin, MarionetteTestCase):
-
-    def setUp(self):
-        super(TestAnonymousNodes, self).setUp()
-        self.marionette.set_context("chrome")
-
-        url = "chrome://marionette/content/test_anonymous_content.xul"
-        new_window = self.open_chrome_window(url)
-        self.marionette.switch_to_window(new_window)
-
-    def tearDown(self):
-        self.close_all_windows()
-
-        super(TestAnonymousNodes, self).tearDown()
-
-    def test_switch_to_anonymous_frame(self):
-        self.marionette.find_element(By.ID, "testAnonymousContentBox")
-        anon_browser_el = self.marionette.find_element(By.ID, "browser")
-        self.assertTrue("test_anonymous_content.xul" in self.marionette.get_url())
-        self.marionette.switch_to_frame(anon_browser_el)
-        self.assertTrue("test.xul" in self.marionette.get_url())
-        self.marionette.find_element(By.ID, "testXulBox")
-        self.assertRaises(NoSuchElementException,
-                          self.marionette.find_element, By.ID, "testAnonymousContentBox")
-
-    def test_switch_to_anonymous_iframe(self):
-        self.marionette.find_element(By.ID, "testAnonymousContentBox")
-        el = self.marionette.find_element(By.ID, "container2")
-        anon_iframe_el = el.find_element(By.ANON_ATTRIBUTE, {"anonid": "iframe"})
-        self.marionette.switch_to_frame(anon_iframe_el)
-        self.assertTrue("test.xul" in self.marionette.get_url())
-        self.marionette.find_element(By.ID, "testXulBox")
-        self.assertRaises(NoSuchElementException, self.marionette.find_element, By.ID,
-                          "testAnonymousContentBox")
-
-    def test_find_anonymous_element_by_attribute(self):
-        accept_button = (By.ANON_ATTRIBUTE, {"dlgtype": "accept"},)
-        not_existent = (By.ANON_ATTRIBUTE, {"anonid": "notexistent"},)
-
-        # By using the window document element
-        start_node = self.marionette.find_element(By.ID, "container3")
-        button = start_node.find_element(*accept_button)
-        self.assertEquals(HTMLElement, type(button))
-        with self.assertRaises(NoSuchElementException):
-            start_node.find_element(*not_existent)
-
-    def test_find_anonymous_elements_by_attribute(self):
-        dialog_buttons = (By.ANON_ATTRIBUTE, {"anonid": "buttons"},)
-        not_existent = (By.ANON_ATTRIBUTE, {"anonid": "notexistent"},)
-
-        start_node = self.marionette.find_element(By.ID, "container3")
-        buttons = start_node.find_elements(*dialog_buttons)
-        self.assertEquals(1, len(buttons))
-        self.assertEquals(HTMLElement, type(buttons[0]))
-        self.assertListEqual([], start_node.find_elements(*not_existent))
-
-    def test_find_anonymous_children(self):
-        start_node = self.marionette.find_element(By.ID, "container3")
-        self.assertEquals(HTMLElement, type(start_node.find_element(By.ANON, None)))
-        self.assertEquals(3, len(start_node.find_elements(By.ANON, None)))
-
-        frame = self.marionette.find_element(By.ID, "framebox")
-        with self.assertRaises(NoSuchElementException):
-            frame.find_element(By.ANON, None)
-        self.assertListEqual([], frame.find_elements(By.ANON, None))
--- a/testing/marionette/harness/marionette_harness/tests/unit/unit-tests.ini
+++ b/testing/marionette/harness/marionette_harness/tests/unit/unit-tests.ini
@@ -29,18 +29,16 @@ expected = fail
 [test_execute_script.py]
 [test_element_retrieval.py]
 [test_findelement_chrome.py]
 
 [test_get_current_url_chrome.py]
 [test_navigation.py]
 [test_timeouts.py]
 
-[test_anonymous_content.py]
-skip-if = !xbl
 [test_switch_frame.py]
 [test_switch_frame_chrome.py]
 [test_switch_window_chrome.py]
 [test_switch_window_content.py]
 
 [test_pagesource.py]
 [test_pagesource_chrome.py]
 
--- a/testing/marionette/jar.mn
+++ b/testing/marionette/jar.mn
@@ -34,19 +34,18 @@ marionette.jar:
   content/proxy.js (proxy.js)
   content/reftest.js (reftest.js)
   content/reftest.xul (reftest.xul)
   content/server.js (server.js)
   content/stream-utils.js (stream-utils.js)
   content/sync.js (sync.js)
   content/transport.js (transport.js)
 #ifdef ENABLE_TESTS
+  content/test.xul (chrome/test.xul)
   content/test2.xul (chrome/test2.xul)
-  content/test_anonymous_content.xul (chrome/test_anonymous_content.xul)
   content/test_dialog.dtd (chrome/test_dialog.dtd)
   content/test_dialog.properties (chrome/test_dialog.properties)
   content/test_dialog.xul (chrome/test_dialog.xul)
   content/test_nested_iframe.xul (chrome/test_nested_iframe.xul)
-  content/test.xul (chrome/test.xul)
 #ifdef MOZ_CODE_COVERAGE
   content/PerTestCoverageUtils.jsm (../../tools/code-coverage/PerTestCoverageUtils.jsm)
 #endif
 #endif
--- a/testing/marionette/reftest.js
+++ b/testing/marionette/reftest.js
@@ -152,17 +152,16 @@ reftest.Runner = class {
     let browser;
     if (Services.appinfo.OS === "Android") {
       browser = reftestWin.document.getElementsByTagName("browser")[0];
       browser.setAttribute("remote", "false");
     } else {
       browser = reftestWin.document.createElementNS(XUL_NS, "xul:browser");
       browser.permanentKey = {};
       browser.setAttribute("id", "browser");
-      browser.setAttribute("anonid", "initialBrowser");
       browser.setAttribute("type", "content");
       browser.setAttribute("primary", "true");
       if (this.remote) {
         browser.setAttribute("remote", "true");
         browser.setAttribute("remoteType", "web");
       } else {
         browser.setAttribute("remote", "false");
       }