Bug 1469108: Propagate directionality to the shadow tree. r=smaug
authorEmilio Cobos Álvarez <emilio@crisal.io>
Fri, 15 Jun 2018 19:16:16 -0700
changeset 808205 0398e981e6a0958bc1788f897ae960d37e8ea2f8
parent 808204 fe329c5d7e3928512600219f891199a4d0a985e4
child 808206 23a7d0003ef8957f1948c90955193fcba3c1ac17
child 808223 ca7b77362ad3f1740eb866e3435cd3acddfe3ce9
child 808264 1e2c9151a09e43613a79daa8d4a94dc3e314020c
push id113309
push userbmo:gl@mozilla.com
push dateMon, 18 Jun 2018 17:04:13 +0000
reviewerssmaug
bugs1469108
milestone62.0a1
Bug 1469108: Propagate directionality to the shadow tree. r=smaug Make it so that directionality of the ShadowRoot descendants is computed based on the host and it's ancestors, but don't touch the dir=auto code, since I don't think anybody agrees with what needs to happen there, and I think in general it shouldn't be accounted for. MozReview-Commit-ID: AZMBZ5m1SQf
dom/base/DirectionalityUtils.cpp
testing/web-platform/meta/MANIFEST.json
testing/web-platform/tests/css/css-scoping/shadow-directionality-001.tentative.html
testing/web-platform/tests/css/css-scoping/shadow-directionality-002.tentative.html
testing/web-platform/tests/shadow-dom/directionality-001-ref.html
testing/web-platform/tests/shadow-dom/directionality-001.tentative.html
--- a/dom/base/DirectionalityUtils.cpp
+++ b/dom/base/DirectionalityUtils.cpp
@@ -208,25 +208,27 @@
 
 #include "nsINode.h"
 #include "nsIContent.h"
 #include "nsIContentInlines.h"
 #include "nsIDocument.h"
 #include "mozilla/AutoRestore.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/dom/Element.h"
+#include "mozilla/dom/ShadowRoot.h"
 #include "nsUnicodeProperties.h"
 #include "nsTextFragment.h"
 #include "nsAttrValue.h"
 #include "nsTextNode.h"
 #include "nsCheapSets.h"
 
 namespace mozilla {
 
 using mozilla::dom::Element;
+using mozilla::dom::ShadowRoot;
 
 /**
  * Returns true if aElement is one of the elements whose text content should not
  * affect its own direction, nor the direction of ancestors with dir=auto.
  *
  * Note that this does not include <bdi>, whose content does affect its own
  * direction when it has dir=auto (which it has by default), so one needs to
  * test for it separately, e.g. with DoesNotAffectDirectionOfAncestors.
@@ -271,16 +273,18 @@ GetDirectionFromChar(uint32_t ch)
     case eCharType_LeftToRight:
       return eDir_LTR;
 
     default:
       return eDir_NotSet;
   }
 }
 
+// FIXME(bug 1100912): Should ShadowRoot children affect the host if it's
+// dir=auto? Probably not at least in closed mode.
 inline static bool
 NodeAffectsDirAutoAncestor(nsINode* aTextNode)
 {
   Element* parent = aTextNode->GetParentElement();
   return (parent &&
           !DoesNotParticipateInAutoDirection(parent) &&
           parent->NodeOrAncestorHasDirAuto() &&
           !aTextNode->IsInAnonymousSubtree());
@@ -620,61 +624,79 @@ public:
 };
 
 Directionality
 RecomputeDirectionality(Element* aElement, bool aNotify)
 {
   MOZ_ASSERT(!aElement->HasDirAuto(),
              "RecomputeDirectionality called with dir=auto");
 
-  Directionality dir = eDir_LTR;
+  if (aElement->HasValidDir()) {
+    return aElement->GetDirectionality();
+  }
 
-  if (aElement->HasValidDir()) {
-    dir = aElement->GetDirectionality();
-  } else {
-    Element* parent = aElement->GetParentElement();
-    if (parent) {
-      // If the element doesn't have an explicit dir attribute with a valid
-      // value, the directionality is the same as the parent element (but
-      // don't propagate the parent directionality if it isn't set yet).
-      Directionality parentDir = parent->GetDirectionality();
+  Directionality dir = eDir_LTR;
+  if (nsINode* parent = aElement->GetParentNode()) {
+    if (ShadowRoot* shadow = ShadowRoot::FromNode(parent)) {
+      parent = shadow->GetHost();
+    }
+
+    if (parent && parent->IsElement()) {
+      // If the node doesn't have an explicit dir attribute with a valid value,
+      // the directionality is the same as the parent element (but don't propagate
+      // the parent directionality if it isn't set yet).
+      Directionality parentDir = parent->AsElement()->GetDirectionality();
       if (parentDir != eDir_NotSet) {
         dir = parentDir;
       }
-    } else {
-      // If there is no parent element and no dir attribute, the directionality
-      // is LTR.
-      dir = eDir_LTR;
     }
+  }
 
-    aElement->SetDirectionality(dir, aNotify);
-  }
+  aElement->SetDirectionality(dir, aNotify);
   return dir;
 }
 
-void
-SetDirectionalityOnDescendants(Element* aElement, Directionality aDir,
-                               bool aNotify)
+static void
+SetDirectionalityOnDescendantsInternal(nsINode* aNode,
+                                       Directionality aDir,
+                                       bool aNotify)
 {
-  for (nsIContent* child = aElement->GetFirstChild(); child; ) {
+  if (Element* element = Element::FromNode(aNode)) {
+    if (ShadowRoot* shadow = element->GetShadowRoot()) {
+      SetDirectionalityOnDescendantsInternal(shadow, aDir, aNotify);
+    }
+  }
+
+  for (nsIContent* child = aNode->GetFirstChild(); child; ) {
     if (!child->IsElement()) {
-      child = child->GetNextNode(aElement);
+      child = child->GetNextNode(aNode);
       continue;
     }
 
     Element* element = child->AsElement();
     if (element->HasValidDir() || element->HasDirAuto()) {
-      child = child->GetNextNonChildNode(aElement);
+      child = child->GetNextNonChildNode(aNode);
       continue;
     }
+    if (ShadowRoot* shadow = element->GetShadowRoot()) {
+      SetDirectionalityOnDescendantsInternal(shadow, aDir, aNotify);
+    }
     element->SetDirectionality(aDir, aNotify);
-    child = child->GetNextNode(aElement);
+    child = child->GetNextNode(aNode);
   }
 }
 
+// We want the public version of this only to acc
+void
+SetDirectionalityOnDescendants(Element* aElement, Directionality aDir,
+                               bool aNotify)
+{
+  return SetDirectionalityOnDescendantsInternal(aElement, aDir, aNotify);
+}
+
 /**
  * Walk the parent chain of a text node whose dir attribute has been removed and
  * reset the direction of any of its ancestors which have dir=auto and whose
  * directionality is determined by a text node descendant.
  */
 void
 WalkAncestorsResetAutoDirection(Element* aElement, bool aNotify)
 {
--- a/testing/web-platform/meta/MANIFEST.json
+++ b/testing/web-platform/meta/MANIFEST.json
@@ -129326,16 +129326,40 @@
       [
        "/css/css-scoping/reference/green-box.html",
        "=="
       ]
      ],
      {}
     ]
    ],
+   "css/css-scoping/shadow-directionality-001.tentative.html": [
+    [
+     "/css/css-scoping/shadow-directionality-001.tentative.html",
+     [
+      [
+       "/css/css-scoping/reference/green-box.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-scoping/shadow-directionality-002.tentative.html": [
+    [
+     "/css/css-scoping/shadow-directionality-002.tentative.html",
+     [
+      [
+       "/css/css-scoping/reference/green-box.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/css-scoping/shadow-disabled-sheet-001.html": [
     [
      "/css/css-scoping/shadow-disabled-sheet-001.html",
      [
       [
        "/css/css-scoping/reference/green-box.html",
        "=="
       ]
@@ -182910,16 +182934,28 @@
       [
        "/service-workers/service-worker/resources/svg-target-reftest-001.html",
        "=="
       ]
      ],
      {}
     ]
    ],
+   "shadow-dom/directionality-001.tentative.html": [
+    [
+     "/shadow-dom/directionality-001.tentative.html",
+     [
+      [
+       "/shadow-dom/directionality-001-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "shadow-dom/layout-slot-no-longer-assigned.html": [
     [
      "/shadow-dom/layout-slot-no-longer-assigned.html",
      [
       [
        "/shadow-dom/reference/empty.html",
        "=="
       ]
@@ -295381,16 +295417,21 @@
      {}
     ]
    ],
    "shadow-dom/OWNERS": [
     [
      {}
     ]
    ],
+   "shadow-dom/directionality-001-ref.html": [
+    [
+     {}
+    ]
+   ],
    "shadow-dom/reference/empty.html": [
     [
      {}
     ]
    ],
    "shadow-dom/resources/Document-prototype-currentScript-helper.js": [
     [
      {}
@@ -522936,16 +522977,24 @@
   "css/css-scoping/shadow-at-import.html": [
    "67295000ad3c24c2d9ab0ac556d34758f3ce654c",
    "reftest"
   ],
   "css/css-scoping/shadow-cascade-order-001.html": [
    "46913ea7e47811b11be898de5c3bd0a330ea6637",
    "testharness"
   ],
+  "css/css-scoping/shadow-directionality-001.tentative.html": [
+   "51cf8c6780bb66f64082a0054f24f64c09b0258f",
+   "reftest"
+  ],
+  "css/css-scoping/shadow-directionality-002.tentative.html": [
+   "c5cc9738b5b81a728c7cc16569360bd65b857ab3",
+   "reftest"
+  ],
   "css/css-scoping/shadow-disabled-sheet-001.html": [
    "3de2d23c1b3339b964ec2c009832a3207a3b9dc4",
    "reftest"
   ],
   "css/css-scoping/shadow-fallback-dynamic-001.html": [
    "741cd9e29067a4634aa5beb6bd06afa540895d22",
    "reftest"
   ],
@@ -523373,17 +523422,17 @@
    "911a6f200540f78d662fd775899aa0249fb9a30d",
    "reftest"
   ],
   "css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-003.html": [
    "7338abdd6edb1027dc2e12b001773ce5ad114286",
    "reftest"
   ],
   "css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-004.html": [
-   "1e5377c120916557dc1525b38c9cf7eb86ae0151",
+   "3740c52d7bd26a3353721931ae2a6299db395968",
    "reftest"
   ],
   "css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-005.html": [
    "7d6563a162ba640b77801c5c8280078d4b087649",
    "reftest"
   ],
   "css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-006.html": [
    "72267d3bfbdbeb438f32266334bb5e4a9c91c1bf",
@@ -539597,17 +539646,17 @@
    "08bbc95f3078421a489e1e93cc7a4f035af40d5b",
    "support"
   ],
   "css/css-values/calc-rem-lang.html": [
    "6fa668d2bcaf01f5c4680e3e14a0e86160d1b5d5",
    "reftest"
   ],
   "css/css-values/calc-rounding-001.html": [
-   "c3071454184ca1bd97443cfb6298447d16d79b2f",
+   "a74b631cd97db18ef120a0a5e7132c9e14b67f81",
    "testharness"
   ],
   "css/css-values/calc-serialization.html": [
    "d0bcbd402cb78e704dabc7f1665d40ba163e30eb",
    "testharness"
   ],
   "css/css-values/calc-unit-analysis.html": [
    "c5fd567b4fa257ce53c48ebf8c444bf382459fec",
@@ -547357,17 +547406,17 @@
    "c9ed57c7ef7a035c25feff4ea60547a57d727f31",
    "testharness"
   ],
   "css/cssom/font-shorthand-serialization.html": [
    "9d0bce1c0d74bf90aca1eb8ee6aa2e2ed2b92b30",
    "testharness"
   ],
   "css/cssom/getComputedStyle-detached-subtree.html": [
-   "01978ca7ea08cbf61b28e9d77753fe5852bcbff9",
+   "886f72b4eaa82d3aeb4de5c5b27f71369dbe0186",
    "testharness"
   ],
   "css/cssom/getComputedStyle-dynamic-subdoc.html": [
    "3f379487727d9730de9e3569b26632c35d602d9d",
    "testharness"
   ],
   "css/cssom/getComputedStyle-pseudo.html": [
    "d3ef09fb6092078562f8923879b9ece97938df47",
@@ -610372,16 +610421,24 @@
   "shadow-dom/ShadowRoot-interface.html": [
    "8c39afc1c5648c3e95fe69c4ea5003958f7734b7",
    "testharness"
   ],
   "shadow-dom/Slotable-interface.html": [
    "61f7da763fa4eb6f21077868caf0a07a4a9e44ae",
    "testharness"
   ],
+  "shadow-dom/directionality-001-ref.html": [
+   "818d966e37de205936380fd47605f13b5aa505d7",
+   "support"
+  ],
+  "shadow-dom/directionality-001.tentative.html": [
+   "763fd90e8ed83fb616379997735a5c283f0bd869",
+   "reftest"
+  ],
   "shadow-dom/event-composed-path-after-dom-mutation.html": [
    "69ea3efc8230a0ed31968f24379289c6691d77d1",
    "testharness"
   ],
   "shadow-dom/event-composed-path-with-related-target.html": [
    "a4c58227f937a943b3845ed3f672419b62a8caad",
    "testharness"
   ],
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-scoping/shadow-directionality-001.tentative.html
@@ -0,0 +1,34 @@
+<!doctype html>
+<title>CSS Test: directionality propagation in Shadow DOM.</title>
+<link rel="author" href="mailto:emilio@crisal.io" title="Emilio Cobos Álvarez">
+<link rel="help" href="https://html.spec.whatwg.org/#the-dir-attribute">
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<link rel="match" href="reference/green-box.html">
+<style>
+  div { width: 100px; }
+</style>
+<p>Test passes if you see a single 100px by 100px green box below.</p>
+<div id="host1"></div>
+<div id="host2" dir="rtl"></div>
+<div id="host3"></div>
+<div id="host4" dir="rtl"></div>
+<script>
+  host1.attachShadow({ mode: "open" }).innerHTML = `
+    <style>:dir(ltr) { background: green; height: 25px; }</style>
+    <div></div>
+  `;
+  host2.attachShadow({ mode: "open" }).innerHTML = `
+    <style>:dir(rtl) { background: green; height: 25px; }</style>
+    <div></div>
+  `;
+  host3.attachShadow({ mode: "open" }).innerHTML = `
+    <style>:dir(rtl) { background: green; height: 25px; }</style>
+    <div></div>
+  `;
+  host4.attachShadow({ mode: "open" }).innerHTML = `
+    <style>span:dir(ltr) { display: block; background: green; height: 25px; }</style>
+    <div dir="ltr"><span></span></div>
+  `;
+  document.body.offsetTop;
+  host3.setAttribute("dir", "rtl");
+</script>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-scoping/shadow-directionality-002.tentative.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<title>CSS Test: directionality propagation in Shadow DOM, appending a shadow host.</title>
+<link rel="author" href="mailto:emilio@crisal.io" title="Emilio Cobos Álvarez">
+<link rel="help" href="https://html.spec.whatwg.org/#the-dir-attribute">
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<link rel="match" href="reference/green-box.html">
+<p>Test passes if you see a single 100px by 100px green box below.</p>
+<style>
+  div { width: 100px; }
+</style>
+<div id="host-parent" dir="rtl"></div>
+<script>
+  let host = document.createElement("div");
+  host.attachShadow({ mode: "open" }).innerHTML = `
+    <style>:dir(rtl) { background: green; height: 100px; width: 100px; }</style>
+    <div></div>
+  `;
+  document.getElementById("host-parent").appendChild(host);
+</script>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/shadow-dom/directionality-001-ref.html
@@ -0,0 +1,6 @@
+<!doctype html>
+<title>CSS Test Reference</title>
+<link rel="author" href="mailto:emilio@crisal.io" title="Emilio Cobos Álvarez">
+<div dir="rtl"> 123 456 <span><span> 789 101112 </span></span></div>
+<div dir="rtl"> 123 456 <span dir="ltr"><span> 789 101112 </span></span></div>
+<div dir="rtl"> 123 456 <span><span> 789 101112 </span></span></div>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/shadow-dom/directionality-001.tentative.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<title>Test: directionality propagation in Shadow DOM.</title>
+<link rel="author" href="mailto:emilio@crisal.io" title="Emilio Cobos Álvarez">
+<link rel="help" href="https://html.spec.whatwg.org/#the-dir-attribute">
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<link rel="match" href="directionality-001-ref.html">
+<div id="host0" dir="rtl"><span> 789 101112 </span></div>
+<div id="host1" dir="rtl"><span> 789 101112 </span></div>
+<div id="host2" dir="rtl"><span> 789 101112 </span></div>
+<script>
+  host0.attachShadow({mode: 'closed'}).innerHTML =
+    '<div> 123 456 <span><slot></slot></span></div>';
+
+  host1.attachShadow({mode: 'closed'}).innerHTML =
+    '<div> 123 456 <span dir="ltr"><slot></slot></span></div>';
+
+  host2.attachShadow({mode: 'closed'}).innerHTML =
+    '<div> 123 456 <span><slot dir="ltr"></slot></span></div>';
+</script>