Bug 1627765 - Remove mac heirarchy cache. r=morgan
authorEitan Isaacson <eitan@monotonous.org>
Fri, 10 Apr 2020 17:52:58 +0000
changeset 523422 33870350e2bcd6bd419ab0713c096cd687a189e1
parent 523421 3987a054cda9f108e2dd66694b8baafaebf215cf
child 523423 c6e827e0ce79960b7f3e9ac1a04f13eb80b92be5
push id37302
push usercbrindusan@mozilla.com
push dateSat, 11 Apr 2020 09:34:41 +0000
treeherdermozilla-central@06ee15775ba8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmorgan
bugs1627765
milestone77.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 1627765 - Remove mac heirarchy cache. r=morgan Differential Revision: https://phabricator.services.mozilla.com/D69880
accessible/mac/AccessibleWrap.h
accessible/mac/AccessibleWrap.mm
accessible/mac/Platform.mm
accessible/mac/mozAccessible.h
accessible/mac/mozAccessible.mm
--- a/accessible/mac/AccessibleWrap.h
+++ b/accessible/mac/AccessibleWrap.h
@@ -42,19 +42,16 @@ class AccessibleWrap : public Accessible
    * The objective-c |Class| type that this accessible's native object
    * should be instantied with.   used on runtime to determine the
    * right type for this accessible's associated native object.
    */
   virtual Class GetNativeType();
 
   virtual void Shutdown() override;
 
-  virtual bool InsertChildAt(uint32_t aIdx, Accessible* aChild) override;
-  virtual bool RemoveChild(Accessible* aAccessible) override;
-
   virtual nsresult HandleAccEvent(AccEvent* aEvent) override;
 
  protected:
   friend class xpcAccessibleMacInterface;
 
   /**
    * Return true if the parent doesn't have children to expose to AT.
    */
--- a/accessible/mac/AccessibleWrap.mm
+++ b/accessible/mac/AccessibleWrap.mm
@@ -149,31 +149,16 @@ nsresult AccessibleWrap::HandleAccEvent(
     [nativeAcc handleAccessibleEvent:eventType];
   }
 
   return NS_OK;
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
 }
 
-bool AccessibleWrap::InsertChildAt(uint32_t aIdx, Accessible* aAccessible) {
-  bool inserted = Accessible::InsertChildAt(aIdx, aAccessible);
-  if (inserted && mNativeObject) [mNativeObject appendChild:aAccessible];
-
-  return inserted;
-}
-
-bool AccessibleWrap::RemoveChild(Accessible* aAccessible) {
-  bool removed = Accessible::RemoveChild(aAccessible);
-
-  if (removed && mNativeObject) [mNativeObject invalidateChildren];
-
-  return removed;
-}
-
 ////////////////////////////////////////////////////////////////////////////////
 // AccessibleWrap protected
 
 bool AccessibleWrap::AncestorIsFlat() {
   // We don't create a native object if we're child of a "flat" accessible;
   // for example, on OS X buttons shouldn't have any children, because that
   // makes the OS confused.
   //
--- a/accessible/mac/Platform.mm
+++ b/accessible/mac/Platform.mm
@@ -41,86 +41,26 @@ void ProxyCreated(ProxyAccessible* aProx
   else if (aProxy->IsTableCell())
     type = [mozTableCellAccessible class];
   else
     type = GetTypeFromRole(aProxy->Role());
 
   uintptr_t accWrap = reinterpret_cast<uintptr_t>(aProxy) | IS_PROXY;
   mozAccessible* mozWrapper = [[type alloc] initWithAccessible:accWrap];
   aProxy->SetWrapper(reinterpret_cast<uintptr_t>(mozWrapper));
-
-  mozAccessible* nativeParent = nullptr;
-  if (aProxy->IsDoc() && aProxy->AsDoc()->IsTopLevel()) {
-    // If proxy is top level, the parent we need to invalidate the children of
-    // will be a non-remote accessible.
-    Accessible* outerDoc = aProxy->OuterDocOfRemoteBrowser();
-    if (outerDoc) {
-      nativeParent = GetNativeFromGeckoAccessible(outerDoc);
-    }
-  } else {
-    // Non-top level proxies need proxy parents' children invalidated.
-    ProxyAccessible* parent = aProxy->Parent();
-    MOZ_ASSERT(parent ||
-                   // It's expected that an OOP iframe might not have a parent yet.
-                   (aProxy->IsDoc() && aProxy->AsDoc()->IsTopLevelInContentProcess()),
-               "a non-top-level proxy is missing a parent?");
-    nativeParent = parent ? GetNativeFromProxy(parent) : nullptr;
-  }
-
-  if (nativeParent) {
-    [nativeParent invalidateChildren];
-  }
 }
 
 void ProxyDestroyed(ProxyAccessible* aProxy) {
-  mozAccessible* nativeParent = nil;
-  if (aProxy->IsDoc() && aProxy->AsDoc()->IsTopLevel()) {
-    // Invalidate native parent in parent process's children on proxy destruction
-    Accessible* outerDoc = aProxy->OuterDocOfRemoteBrowser();
-    if (outerDoc) {
-      nativeParent = GetNativeFromGeckoAccessible(outerDoc);
-    }
-  } else {
-    if (!aProxy->Document()->IsShutdown()) {
-      // Only do if the document has not been shut down, else parent will return
-      // garbage since we don't shut down children from top down.
-      ProxyAccessible* parent = aProxy->Parent();
-      // Invalidate proxy parent's children.
-      if (parent) {
-        nativeParent = GetNativeFromProxy(parent);
-      }
-    }
-  }
-
   mozAccessible* wrapper = GetNativeFromProxy(aProxy);
   [wrapper expire];
   [wrapper release];
   aProxy->SetWrapper(0);
-
-  if (nativeParent) {
-    [nativeParent invalidateChildren];
-  }
 }
 
 void ProxyEvent(ProxyAccessible* aProxy, uint32_t aEventType) {
-  if (aEventType == nsIAccessibleEvent::EVENT_REORDER && aProxy->ChildrenCount() == 1 &&
-      aProxy->ChildAt(0)->IsDoc()) {
-    // This is a remote OuterDocAccessible. The reorder event indicates that Its
-    // embedded document has been added or changed. If the document itself is
-    // an existing Accessible, ProxyCreated won't have been called, so we won't
-    // have invalidated native children. This can happen for in-process iframes
-    // if the OuterDocAccessible is re-created (e.g. due to layout reflow).
-    // It always happens for out-of-process iframes, as we must always call
-    // ProxyCreated before DocAccessibleParent::AddChildDoc for those.
-    mozAccessible* wrapper = GetNativeFromProxy(aProxy);
-    if (wrapper) {
-      [wrapper invalidateChildren];
-    }
-  }
-
   // ignore everything but focus-changed, value-changed, caret and selection
   // events for now.
   if (aEventType != nsIAccessibleEvent::EVENT_FOCUS &&
       aEventType != nsIAccessibleEvent::EVENT_VALUE_CHANGE &&
       aEventType != nsIAccessibleEvent::EVENT_TEXT_VALUE_CHANGE &&
       aEventType != nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED &&
       aEventType != nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED &&
       aEventType != nsIAccessibleEvent::EVENT_REORDER)
--- a/accessible/mac/mozAccessible.h
+++ b/accessible/mac/mozAccessible.h
@@ -44,26 +44,16 @@ static const uintptr_t IS_PROXY = 1;
 
 @interface mozAccessible : NSObject <mozAccessible> {
   /**
    * Weak reference; it owns us.
    */
   uintptr_t mGeckoAccessible;
 
   /**
-   * Strong ref to array of children
-   */
-  NSMutableArray* mChildren;
-
-  /**
-   * Weak reference to the parent
-   */
-  mozAccessible* mParent;
-
-  /**
    * The role of our gecko accessible.
    */
   mozilla::a11y::role mRole;
 
   /**
    * A cache of a subset of our states.
    */
   uint64_t mCachedState;
@@ -143,24 +133,16 @@ static const uintptr_t IS_PROXY = 1;
 // Notify of a state change, so the cache can be altered.
 - (void)stateChanged:(uint64_t)state isEnabled:(BOOL)enabled;
 
 // Invalidate cached state.
 - (void)invalidateState;
 
 #pragma mark -
 
-// invalidates and removes all our children from our cached array.
-- (void)invalidateChildren;
-
-/**
- * Append a child if they are already cached.
- */
-- (void)appendChild:(mozilla::a11y::Accessible*)aAccessible;
-
 // makes ourselves "expired". after this point, we might be around if someone
 // has retained us (e.g., a third-party), but we really contain no information.
 - (void)expire;
 - (BOOL)isExpired;
 
 #ifdef DEBUG
 - (void)printHierarchy;
 - (void)printHierarchyWithLevel:(unsigned)numSpaces;
@@ -183,12 +165,17 @@ static const uintptr_t IS_PROXY = 1;
 
 // a mozAccessible needs to at least provide links to its parent and
 // children.
 - (NSArray*)accessibilityAttributeNames;
 
 // value for the specified attribute
 - (id)accessibilityAttributeValue:(NSString*)attribute;
 
+// gets the array length of specified attribute values. Added
+// here to have a lighter way to get the child count instead
+// of constructing a full array.
+- (NSUInteger)accessibilityArrayAttributeCount:(NSString*)attribute;
+
 - (BOOL)accessibilityIsAttributeSettable:(NSString*)attribute;
 - (void)accessibilitySetValue:(id)value forAttribute:(NSString*)attribute;
 
 @end
--- a/accessible/mac/mozAccessible.mm
+++ b/accessible/mac/mozAccessible.mm
@@ -103,17 +103,16 @@ static inline NSMutableArray* ConvertToN
   return self;
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
 }
 
 - (void)dealloc {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
 
-  [mChildren release];
   [super dealloc];
 
   NS_OBJC_END_TRY_ABORT_BLOCK;
 }
 
 - (mozilla::a11y::AccessibleWrap*)getGeckoAccessible {
   // Check if mGeckoAccessible points at a proxy
   if (mGeckoAccessible & IS_PROXY) return nil;
@@ -292,16 +291,34 @@ static const uint64_t kCacheInitialized 
     mCachedState &= ~state;
   }
 }
 
 - (void)invalidateState {
   mCachedState = 0;
 }
 
+- (NSUInteger)accessibilityArrayAttributeCount:(NSString*)attribute {
+  AccessibleWrap* accWrap = [self getGeckoAccessible];
+  ProxyAccessible* proxy = [self getProxyAccessible];
+  if (!accWrap && !proxy) return 0;
+
+  // By default this calls -[[mozAccessible children] count].
+  // Since we don't cache mChildren. This is faster.
+  if ([attribute isEqualToString:NSAccessibilityChildrenAttribute]) {
+    if (accWrap) return accWrap->ChildCount();
+
+    return proxy->ChildrenCount();
+  }
+
+  id array = [self accessibilityAttributeValue:attribute];
+
+  return [array isKindOfClass:[NSArray class]] ? [array count] : 0;
+}
+
 - (id)accessibilityAttributeValue:(NSString*)attribute {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
 
   AccessibleWrap* accWrap = [self getGeckoAccessible];
   ProxyAccessible* proxy = [self getProxyAccessible];
   if (!accWrap && !proxy) return nil;
 
 #if DEBUG
@@ -740,51 +757,47 @@ static const uint64_t kCacheInitialized 
   return nil;
 }
 
 - (BOOL)isRoot {
   return NO;
 }
 
 // gets our native children lazily.
-// returns nil when there are no children.
 - (NSArray*)children {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
 
-  if (mChildren) return mChildren;
-
-  // get the array of children.
-  mChildren = [[NSMutableArray alloc] init];
+  NSMutableArray* children = [NSMutableArray new];
 
   AccessibleWrap* accWrap = [self getGeckoAccessible];
   if (accWrap) {
     uint32_t childCount = accWrap->ChildCount();
     for (uint32_t childIdx = 0; childIdx < childCount; childIdx++) {
       mozAccessible* nativeChild = GetNativeFromGeckoAccessible(accWrap->GetChildAt(childIdx));
-      if (nativeChild) [mChildren addObject:nativeChild];
+      if (nativeChild) [children addObject:nativeChild];
     }
 
     // children from child if this is an outerdoc
     OuterDocAccessible* docOwner = accWrap->AsOuterDoc();
     if (docOwner) {
       if (ProxyAccessible* proxyDoc = docOwner->RemoteChildDoc()) {
         mozAccessible* nativeRemoteChild = GetNativeFromProxy(proxyDoc);
-        [mChildren insertObject:nativeRemoteChild atIndex:0];
+        [children insertObject:nativeRemoteChild atIndex:0];
         NSAssert1(nativeRemoteChild, @"%@ found a child remote doc missing a native\n", self);
       }
     }
   } else if (ProxyAccessible* proxy = [self getProxyAccessible]) {
     uint32_t childCount = proxy->ChildrenCount();
     for (uint32_t childIdx = 0; childIdx < childCount; childIdx++) {
       mozAccessible* nativeChild = GetNativeFromProxy(proxy->ChildAt(childIdx));
-      if (nativeChild) [mChildren addObject:nativeChild];
+      if (nativeChild) [children addObject:nativeChild];
     }
   }
 
-  return mChildren;
+  return children;
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
 }
 
 - (NSValue*)position {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
 
   nsIntRect rect;
@@ -1262,45 +1275,23 @@ struct RoleDescrComparator {
   if (docAcc) nativeWindow = static_cast<NSWindow*>(docAcc->GetNativeWindow());
 
   NSAssert1(nativeWindow, @"Could not get native window for %@", self);
   return nativeWindow;
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
 }
 
-- (void)invalidateChildren {
-  NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
-
-  // make room for new children
-  if (mChildren) {
-    [mChildren release];
-    mChildren = nil;
-  }
-
-  NS_OBJC_END_TRY_ABORT_BLOCK;
-}
-
-- (void)appendChild:(Accessible*)aAccessible {
-  // if mChildren is nil, then we don't even need to bother
-  if (!mChildren) return;
-
-  mozAccessible* curNative = GetNativeFromGeckoAccessible(aAccessible);
-  if (curNative) [mChildren addObject:curNative];
-}
-
 - (BOOL)accessibilityNotifiesWhenDestroyed {
   return YES;
 }
 
 - (void)expire {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
 
-  [self invalidateChildren];
-
   [self invalidateState];
 
   mGeckoAccessible = 0;
 
   [self postNotification:NSAccessibilityUIElementDestroyedNotification];
 
   NS_OBJC_END_TRY_ABORT_BLOCK;
 }