Bug 1286598 - Make sure an accessible tree is updated on DOM tree removals. r=yzen, a=gchang
authorAlexander Surkov <surkov.alexander@gmail.com>
Wed, 20 Jul 2016 09:40:55 -0400
changeset 325649 f3790db5cbcbc2084db461e050b58203de5879b4
parent 325648 899a01cd98bdbd6e695ed3c46c918c27ac060736
child 325650 8d3761e5445cd1c60d96aca787a34e58127f83d5
push id9853
push userryanvm@gmail.com
push dateMon, 01 Aug 2016 04:32:09 +0000
treeherdermozilla-aurora@f3790db5cbcb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersyzen, gchang
bugs1286598
milestone49.0a2
Bug 1286598 - Make sure an accessible tree is updated on DOM tree removals. r=yzen, a=gchang
accessible/generic/DocAccessible.cpp
accessible/tests/mochitest/treeupdate/a11y.ini
accessible/tests/mochitest/treeupdate/test_bug1276857.html
--- a/accessible/generic/DocAccessible.cpp
+++ b/accessible/generic/DocAccessible.cpp
@@ -1125,20 +1125,36 @@ DocAccessible::CharacterDataChanged(nsID
 
 void
 DocAccessible::ContentInserted(nsIDocument* aDocument, nsIContent* aContainer,
                                nsIContent* aChild, int32_t /* unused */)
 {
 }
 
 void
-DocAccessible::ContentRemoved(nsIDocument* aDocument, nsIContent* aContainer,
-                              nsIContent* aChild, int32_t /* unused */,
-                              nsIContent* aPreviousSibling)
+DocAccessible::ContentRemoved(nsIDocument* aDocument,
+                              nsIContent* aContainerNode,
+                              nsIContent* aChildNode, int32_t /* unused */,
+                              nsIContent* aPreviousSiblingNode)
 {
+#ifdef A11Y_LOG
+  if (logging::IsEnabled(logging::eTree)) {
+    logging::MsgBegin("TREE", "DOM content removed; doc: %p", this);
+    logging::Node("container node", aContainerNode);
+    logging::Node("content node", aChildNode);
+    logging::MsgEnd();
+  }
+#endif
+  // This one and content removal notification from layout may result in
+  // double processing of same subtrees. If it pops up in profiling, then
+  // consider reusing a document node cache to reject these notifications early.
+  Accessible* container = GetAccessibleOrContainer(aContainerNode);
+  if (container) {
+    UpdateTreeOnRemoval(container, aChildNode);
+  }
 }
 
 void
 DocAccessible::ParentChainChanged(nsIContent* aContent)
 {
 }
 
 
--- a/accessible/tests/mochitest/treeupdate/a11y.ini
+++ b/accessible/tests/mochitest/treeupdate/a11y.ini
@@ -9,16 +9,17 @@ support-files =
 [test_bug852150.xhtml]
 [test_bug883708.xhtml]
 [test_bug884251.xhtml]
 [test_bug895082.html]
 [test_bug1040735.html]
 [test_bug1100602.html]
 [test_bug1175913.html]
 [test_bug1189277.html]
+[test_bug1276857.html]
 [test_canvas.html]
 [test_colorpicker.xul]
 [test_contextmenu.xul]
 [test_cssoverflow.html]
 [test_deck.xul]
 [test_doc.html]
 [test_gencontent.html]
 [test_general.html]
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/treeupdate/test_bug1276857.html
@@ -0,0 +1,143 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+  <title>DOM mutations test</title>
+  <link rel="stylesheet" type="text/css"
+        href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+  <script type="application/javascript"
+          src="../common.js"></script>
+  <script type="application/javascript"
+          src="../role.js"></script>
+  <script type="application/javascript"
+          src="../events.js"></script>
+
+  <script type="application/javascript">
+    function runTest()
+    {
+      // children change will recreate the table
+      this.eventSeq = [
+        new invokerChecker(EVENT_REORDER, getNode('c1'))
+      ];
+
+      this.invoke = function runTest_invoke() {
+        var tree = {
+          SECTION: [ // c1
+            { TEXT_LEAF: [] }, // Some text
+            { TEXT_CONTAINER: [
+              { TEXT_LEAF: [] } // something with ..
+            ] },
+            { TEXT_LEAF: [] } // More text
+          ]
+        };
+        testAccessibleTree('c1', tree);
+
+        getNode('c1_t').querySelector('span').remove();
+      };
+
+      this.finalCheck = function runTest_finalCheck() {
+        var tree = {
+          SECTION: [ // c1
+            { TEXT_LEAF: [] }, // Some text
+            { TEXT_LEAF: [] } // More text
+          ]
+        };
+        testAccessibleTree('c1', tree);
+      };
+
+      this.getID = function runTest_getID()
+      {
+        return 'child DOM node is removed before the layout notifies the a11y about parent removal/show';
+      };
+    }
+
+    function runShadowTest()
+    {
+      // children change will recreate the table
+      this.eventSeq = [
+        new invokerChecker(EVENT_REORDER, 'c2')
+      ];
+
+      this.invoke = function runShadowTest_invoke() {
+        var tree = {
+          SECTION: [ // c2
+            { TEXT_LEAF: [] }, // Some text
+            { TEXT_CONTAINER: [
+              { TEXT_LEAF: [] } // something with ..
+            ] },
+            { TEXT_LEAF: [] } // More text
+          ]
+        };
+        testAccessibleTree('c2', tree);
+
+        gShadowRoot.firstElementChild.querySelector('span').remove();
+      };
+
+      this.finalCheck = function runShadowTest_finalCheck() {
+        var tree = {
+          SECTION: [ // c2
+            { TEXT_LEAF: [] }, // Some text
+            { TEXT_LEAF: [] } // More text
+          ]
+        };
+        testAccessibleTree('c2', tree);
+      };
+
+      this.getID = function runShadowTest_getID() {
+        return 'child DOM node is removed before the layout notifies the a11y about parent removal/show in shadow DOM';
+      };
+    }
+
+    //enableLogging("tree");
+    //gA11yEventDumpToConsole = true;
+
+    var gQueue = null;
+    function doTest()
+    {
+      gQueue = new eventQueue();
+      gQueue.push(new runTest());
+      gQueue.push(new runShadowTest());
+      gQueue.invoke(); // will call SimpleTest.finish();
+    }
+
+    SimpleTest.waitForExplicitFinish();
+    addA11yLoadEvent(doTest);
+  </script>
+
+</head>
+
+<body>
+  <p id="display"></p>
+  <div id="content" style="display: none"></div>
+  <pre id="test">
+  </pre>
+
+  <div id="c1">
+    <div id="c1_t" style="display: table" role="presentation">
+    Some text
+    <span style="display: table-cell">something with accessibles goes here</span>
+    More text
+    </div>
+  </div>
+
+  <template id="tmpl">
+    <div style="display: table" role="presentation">
+    Some text
+    <span style="display: table-cell">something with accessibles goes here</span>
+    More text
+    </div>
+  </template>
+
+  <div id="c2"><div id="c2_c" role="presentation"></div></div>
+
+  <script>
+    var gShadowRoot = document.getElementById('c2_c').createShadowRoot();
+    var tmpl = document.getElementById('tmpl');
+    gShadowRoot.appendChild(document.importNode(tmpl.content, true));
+  </script>
+</body>
+</html>