Bug 1390409 part 3 - Send a11y notification for visibility changes. r?emilio draft
authorXidorn Quan <me@upsuper.org>
Mon, 28 Aug 2017 16:58:25 +1000
changeset 654448 e6ffffec915bc1d44ece11a40c04ff4eef64d976
parent 654221 56964921fb1f6b386bf6ed7faab88d623bbb0d1e
child 654449 4b3072bb4f9a3eac9b1b1435df3defe5cbb73813
push id76594
push userxquan@mozilla.com
push dateMon, 28 Aug 2017 22:40:34 +0000
reviewersemilio
bugs1390409
milestone57.0a1
Bug 1390409 part 3 - Send a11y notification for visibility changes. r?emilio MozReview-Commit-ID: I48OhzekXvD
layout/base/ServoRestyleManager.cpp
--- a/layout/base/ServoRestyleManager.cpp
+++ b/layout/base/ServoRestyleManager.cpp
@@ -645,20 +645,88 @@ UpdateFramePseudoElementStyles(nsIFrame*
     aFrame, aRestyleState.StyleSet(), aRestyleState.ChangeList());
 }
 
 enum class ServoPostTraversalFlags : uint32_t
 {
   Empty = 0,
   // Whether parent was restyled.
   ParentWasRestyled = 1 << 0,
+  // Skip sending accessibility notifications for all descendants.
+  SkipA11yNotifications = 1 << 1,
+  // Always send accessibility notifications if the element is shown.
+  // The SkipA11yNotifications flag above overrides this flag.
+  SendA11yNotificationsIfShown = 1 << 2,
 };
 
 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(ServoPostTraversalFlags)
 
+// Send proper accessibility notifications and return post traversal
+// flags for kids.
+static ServoPostTraversalFlags
+SendA11yNotifications(nsPresContext* aPresContext,
+                      Element* aElement,
+                      nsStyleContext* aOldStyleContext,
+                      nsStyleContext* aNewStyleContext,
+                      ServoPostTraversalFlags aFlags)
+{
+  using Flags = ServoPostTraversalFlags;
+  MOZ_ASSERT(!(aFlags & Flags::SkipA11yNotifications) ||
+             !(aFlags & Flags::SendA11yNotificationsIfShown),
+             "The two a11y flags should never be set together");
+
+#ifdef ACCESSIBILITY
+  nsAccessibilityService* accService = GetAccService();
+  if (!accService) {
+    // If we don't have accessibility service, accessibility is not
+    // enabled. Just skip everything.
+    return Flags::Empty;
+  }
+  if (aFlags & Flags::SkipA11yNotifications) {
+    // Propogate the skipping flag to descendants.
+    return Flags::SkipA11yNotifications;
+  }
+
+  bool needsNotify = false;
+  bool isVisible = aNewStyleContext->StyleVisibility()->IsVisible();
+  if (aFlags & Flags::SendA11yNotificationsIfShown) {
+    if (!isVisible) {
+      // Propagate the sending-if-shown flag to descendants.
+      return Flags::SendA11yNotificationsIfShown;
+    }
+    // We have asked accessibility service to remove the whole subtree
+    // of element which becomes invisible from the accessible tree, but
+    // this element is visible, so we need to add it back.
+    needsNotify = true;
+  } else {
+    // If we shouldn't skip in any case, we need to check whether our
+    // own visibility has changed.
+    bool wasVisible = aOldStyleContext->StyleVisibility()->IsVisible();
+    needsNotify = wasVisible != isVisible;
+  }
+
+  if (needsNotify) {
+    nsIPresShell* presShell = aPresContext->PresShell();
+    if (isVisible) {
+      accService->ContentRangeInserted(presShell, aElement->GetParent(),
+                                       aElement, aElement->GetNextSibling());
+      // We are adding the subtree. Accessibility service would handle
+      // descendants, so we should just skip them from notifying.
+      return Flags::SkipA11yNotifications;
+    }
+    // Remove the subtree of this invisible element, and ask any shown
+    // descendant to add themselves back.
+    accService->ContentRemoved(presShell, aElement);
+    return Flags::SendA11yNotificationsIfShown;
+  }
+#endif
+
+  return Flags::Empty;
+}
+
 bool
 ServoRestyleManager::ProcessPostTraversal(
   Element* aElement,
   ServoStyleContext* aParentContext,
   ServoRestyleState& aRestyleState,
   ServoPostTraversalFlags aFlags)
 {
   nsIFrame* styleFrame = nsLayoutUtils::GetStyleFrame(aElement);
@@ -762,16 +830,20 @@ ServoRestyleManager::ProcessPostTraversa
   ServoRestyleState& childrenRestyleState =
     thisFrameRestyleState ? *thisFrameRestyleState : aRestyleState;
 
   RefPtr<ServoStyleContext> upToDateContext =
     wasRestyled
       ? aRestyleState.StyleSet().ResolveServoStyle(aElement)
       : oldStyleContext;
 
+  ServoPostTraversalFlags childrenFlags =
+    wasRestyled ? ServoPostTraversalFlags::ParentWasRestyled
+                : ServoPostTraversalFlags::Empty;
+
   if (wasRestyled && oldStyleContext) {
     MOZ_ASSERT(styleFrame || displayContentsStyle);
     MOZ_ASSERT(oldStyleContext->ComputedData() != upToDateContext->ComputedData());
 
     upToDateContext->ResolveSameStructsAs(oldStyleContext);
 
     // We want to walk all the continuations here, even the ones with different
     // styles.  In practice, the only reason we get continuations with different
@@ -815,26 +887,27 @@ ServoRestyleManager::ProcessPostTraversa
     // requires to update the animation on the layer.
     //
     // We can sometimes reach this when the animated style is being removed.
     // Since AddLayerChangesForAnimation checks if |styleFrame| has a transform
     // style or not, we need to call it *after* setting |newContext| to
     // |styleFrame| to ensure the animated transform has been removed first.
     AddLayerChangesForAnimation(
       styleFrame, aElement, aRestyleState.ChangeList());
+
+    childrenFlags |= SendA11yNotifications(mPresContext, aElement,
+                                           oldStyleContext,
+                                           upToDateContext, aFlags);
   }
 
   const bool traverseElementChildren =
     aElement->HasAnyOfFlags(Element::kAllServoDescendantBits);
   const bool traverseTextChildren =
     wasRestyled || aElement->HasFlag(NODE_DESCENDANTS_NEED_FRAMES);
   bool recreatedAnyContext = wasRestyled;
-  ServoPostTraversalFlags childrenFlags =
-    wasRestyled ? ServoPostTraversalFlags::ParentWasRestyled
-                : ServoPostTraversalFlags::Empty;
   if (traverseElementChildren || traverseTextChildren) {
     StyleChildrenIterator it(aElement);
     TextPostTraversalState textState(*aElement,
                                      upToDateContext,
                                      displayContentsStyle && wasRestyled,
                                      childrenRestyleState);
     for (nsIContent* n = it.GetNextChild(); n; n = it.GetNextChild()) {
       if (traverseElementChildren && n->IsElement()) {