From 40a355a42d4a9444dc753c04c6608dade2f06a23 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 03:13:27 +0200 Subject: Adding upstream version 125.0.1. Signed-off-by: Daniel Baumann --- accessible/generic/BaseAccessibles.cpp | 3 +- accessible/generic/DocAccessible.cpp | 176 ++++++++++++++++++++++++++------- accessible/generic/DocAccessible.h | 59 ++++++++++- accessible/generic/LocalAccessible.cpp | 70 ++++++++++--- accessible/generic/LocalAccessible.h | 18 +++- accessible/generic/moz.build | 4 + 6 files changed, 276 insertions(+), 54 deletions(-) (limited to 'accessible/generic') diff --git a/accessible/generic/BaseAccessibles.cpp b/accessible/generic/BaseAccessibles.cpp index 520f54e96b..eaaad02296 100644 --- a/accessible/generic/BaseAccessibles.cpp +++ b/accessible/generic/BaseAccessibles.cpp @@ -120,8 +120,7 @@ const LocalAccessible* LinkableAccessible::ActionWalk(bool* aIsLink, } KeyBinding LinkableAccessible::AccessKey() const { - if (const LocalAccessible* actionAcc = - const_cast(this)->ActionWalk()) { + if (const LocalAccessible* actionAcc = ActionWalk()) { return actionAcc->AccessKey(); } diff --git a/accessible/generic/DocAccessible.cpp b/accessible/generic/DocAccessible.cpp index 28ed8bcbb4..0d84e8be9c 100644 --- a/accessible/generic/DocAccessible.cpp +++ b/accessible/generic/DocAccessible.cpp @@ -67,6 +67,9 @@ static nsStaticAtom* const kRelationAttrs[] = { static const uint32_t kRelationAttrsLen = ArrayLength(kRelationAttrs); +static nsStaticAtom* const kSingleElementRelationIdlAttrs[] = { + nsGkAtoms::popovertarget}; + //////////////////////////////////////////////////////////////////////////////// // Constructor/desctructor @@ -383,25 +386,25 @@ void DocAccessible::QueueCacheUpdate(LocalAccessible* aAcc, void DocAccessible::QueueCacheUpdateForDependentRelations( LocalAccessible* aAcc) { - if (!mIPCDoc || !aAcc || !aAcc->Elm() || !aAcc->IsInDocument() || - aAcc->IsDefunct()) { + if (!mIPCDoc || !aAcc || !aAcc->IsInDocument() || aAcc->IsDefunct()) { return; } - nsAutoString ID; - aAcc->DOMNodeID(ID); - if (AttrRelProviders* list = GetRelProviders(aAcc->Elm(), ID)) { - // We call this function when we've noticed an ID change, or when an acc - // is getting bound to its document. We need to ensure any existing accs - // that depend on this acc's ID have their rel cache entries updated. - for (const auto& provider : *list) { - LocalAccessible* relatedAcc = GetAccessible(provider->mContent); - if (!relatedAcc || relatedAcc->IsDefunct() || - !relatedAcc->IsInDocument() || - mInsertedAccessibles.Contains(relatedAcc)) { - continue; - } - QueueCacheUpdate(relatedAcc, CacheDomain::Relations); + dom::Element* el = aAcc->Elm(); + if (!el) { + return; + } + + // We call this function when we've noticed an ID change, or when an acc + // is getting bound to its document. We need to ensure any existing accs + // that depend on this acc's ID or Element have their relation cache entries + // updated. + RelatedAccIterator iter(this, el, nullptr); + while (LocalAccessible* relatedAcc = iter.Next()) { + if (relatedAcc->IsDefunct() || !relatedAcc->IsInDocument() || + mInsertedAccessibles.Contains(relatedAcc)) { + continue; } + QueueCacheUpdate(relatedAcc, CacheDomain::Relations); } } @@ -507,6 +510,7 @@ void DocAccessible::Shutdown() { } mDependentIDsHashes.Clear(); + mDependentElementsMap.Clear(); mNodeToAccessibleMap.Clear(); mAnchorJumpElm = nullptr; @@ -729,9 +733,26 @@ std::pair DocAccessible::ComputeScrollData( NS_IMPL_NSIDOCUMENTOBSERVER_CORE_STUB(DocAccessible) NS_IMPL_NSIDOCUMENTOBSERVER_LOAD_STUB(DocAccessible) +// When a reflected element IDL attribute changes, we might get the following +// synchronous calls: +// 1. AttributeWillChange for the element. +// 2. AttributeWillChange for the content attribute. +// 3. AttributeChanged for the content attribute. +// 4. AttributeChanged for the element. +// Since the content attribute value is "" for any element, we won't always get +// 2 or 3. Even if we do, they might occur after the element has already +// changed, which means we can't detect any relevant state changes there; e.g. +// mPrevStateBits. Thus, we need 1 and 4, and we must ignore 2 and 3. To +// facilitate this, sIsAttrElementChanging will be set to true for 2 and 3. +static bool sIsAttrElementChanging = false; + void DocAccessible::AttributeWillChange(dom::Element* aElement, int32_t aNameSpaceID, nsAtom* aAttribute, int32_t aModType) { + if (sIsAttrElementChanging) { + // See the comment above the definition of sIsAttrElementChanging. + return; + } LocalAccessible* accessible = GetAccessible(aElement); if (!accessible) { if (aElement != mContent) return; @@ -744,10 +765,11 @@ void DocAccessible::AttributeWillChange(dom::Element* aElement, // elements. if (aModType != dom::MutationEvent_Binding::ADDITION) { RemoveDependentIDsFor(accessible, aAttribute); + RemoveDependentElementsFor(accessible, aAttribute); } if (aAttribute == nsGkAtoms::id) { - if (accessible->IsActiveDescendant()) { + if (accessible->IsActiveDescendantId()) { RefPtr event = new AccStateChangeEvent(accessible, states::ACTIVE, false); FireDelayedEvent(event); @@ -778,6 +800,10 @@ void DocAccessible::AttributeChanged(dom::Element* aElement, int32_t aNameSpaceID, nsAtom* aAttribute, int32_t aModType, const nsAttrValue* aOldValue) { + if (sIsAttrElementChanging) { + // See the comment above the definition of sIsAttrElementChanging. + return; + } NS_ASSERTION(!IsDefunct(), "Attribute changed called on defunct document accessible!"); @@ -851,6 +877,7 @@ void DocAccessible::AttributeChanged(dom::Element* aElement, if (aModType == dom::MutationEvent_Binding::MODIFICATION || aModType == dom::MutationEvent_Binding::ADDITION) { AddDependentIDsFor(accessible, aAttribute); + AddDependentElementsFor(accessible, aAttribute); } } @@ -883,25 +910,23 @@ void DocAccessible::ARIAAttributeDefaultChanged(dom::Element* aElement, void DocAccessible::ARIAActiveDescendantChanged(LocalAccessible* aAccessible) { if (dom::Element* elm = aAccessible->Elm()) { nsAutoString id; - if (elm->GetAttr(nsGkAtoms::aria_activedescendant, id)) { - dom::Element* activeDescendantElm = IDRefsIterator::GetElem(elm, id); - if (activeDescendantElm) { - LocalAccessible* activeDescendant = GetAccessible(activeDescendantElm); - if (activeDescendant) { - RefPtr event = - new AccStateChangeEvent(activeDescendant, states::ACTIVE, true); - FireDelayedEvent(event); - if (aAccessible->IsActiveWidget()) { - FocusMgr()->ActiveItemChanged(activeDescendant, false); + if (dom::Element* activeDescendantElm = + nsCoreUtils::GetAriaActiveDescendantElement(elm)) { + LocalAccessible* activeDescendant = GetAccessible(activeDescendantElm); + if (activeDescendant) { + RefPtr event = + new AccStateChangeEvent(activeDescendant, states::ACTIVE, true); + FireDelayedEvent(event); + if (aAccessible->IsActiveWidget()) { + FocusMgr()->ActiveItemChanged(activeDescendant, false); #ifdef A11Y_LOG - if (logging::IsEnabled(logging::eFocus)) { - logging::ActiveItemChangeCausedBy("ARIA activedescedant changed", - activeDescendant); - } -#endif + if (logging::IsEnabled(logging::eFocus)) { + logging::ActiveItemChangeCausedBy("ARIA activedescedant changed", + activeDescendant); } - return; +#endif } + return; } } @@ -991,6 +1016,11 @@ void DocAccessible::ElementStateChanged(dom::Document* aDocument, new AccStateChangeEvent(accessible, states::DEFAULT); FireDelayedEvent(event); } + + if (aStateMask.HasState(dom::ElementState::INDETERMINATE)) { + RefPtr event = new AccStateChangeEvent(accessible, states::MIXED); + FireDelayedEvent(event); + } } void DocAccessible::CharacterDataWillChange(nsIContent* aContent, @@ -1150,6 +1180,7 @@ void DocAccessible::BindToDocument(LocalAccessible* aAccessible, if (aAccessible->HasOwnContent()) { AddDependentIDsFor(aAccessible); + AddDependentElementsFor(aAccessible); nsIContent* content = aAccessible->GetContent(); if (content->IsElement() && @@ -1769,6 +1800,61 @@ void DocAccessible::RemoveDependentIDsFor(LocalAccessible* aRelProvider, } } +void DocAccessible::AddDependentElementsFor(LocalAccessible* aRelProvider, + nsAtom* aRelAttr) { + dom::Element* providerEl = aRelProvider->Elm(); + if (!providerEl) { + return; + } + for (nsStaticAtom* attr : kSingleElementRelationIdlAttrs) { + if (aRelAttr && aRelAttr != attr) { + continue; + } + if (dom::Element* targetEl = + providerEl->GetExplicitlySetAttrElement(attr)) { + AttrRelProviders& providers = + mDependentElementsMap.LookupOrInsert(targetEl); + AttrRelProvider* provider = new AttrRelProvider(attr, providerEl); + providers.AppendElement(provider); + } + // If the relation attribute was given, we've already handled it. We don't + // have anything else to check. + if (aRelAttr) { + break; + } + } +} + +void DocAccessible::RemoveDependentElementsFor(LocalAccessible* aRelProvider, + nsAtom* aRelAttr) { + dom::Element* providerEl = aRelProvider->Elm(); + if (!providerEl) { + return; + } + for (nsStaticAtom* attr : kSingleElementRelationIdlAttrs) { + if (aRelAttr && aRelAttr != attr) { + continue; + } + if (dom::Element* targetEl = + providerEl->GetExplicitlySetAttrElement(attr)) { + if (auto providers = mDependentElementsMap.Lookup(targetEl)) { + providers.Data().RemoveElementsBy([attr, + providerEl](const auto& provider) { + return provider->mRelAttr == attr && provider->mContent == providerEl; + }); + if (providers.Data().IsEmpty()) { + providers.Remove(); + } + } + } + // If the relation attribute was given, we've already handled it. We don't + // have anything else to check. + if (aRelAttr) { + break; + } + } +} + bool DocAccessible::UpdateAccessibleOnAttrChange(dom::Element* aElement, nsAtom* aAttribute) { if (aAttribute == nsGkAtoms::role) { @@ -2003,7 +2089,7 @@ bool InsertIterator::Next() { return false; } -void DocAccessible::MaybeFireEventsForChangedPopover(LocalAccessible *aAcc) { +void DocAccessible::MaybeFireEventsForChangedPopover(LocalAccessible* aAcc) { dom::Element* el = aAcc->Elm(); if (!el || !el->IsHTMLElement() || !el->HasAttr(nsGkAtoms::popover)) { return; // Not a popover. @@ -2620,6 +2706,7 @@ void DocAccessible::UncacheChildrenInSubtree(LocalAccessible* aRoot) { MaybeFireEventsForChangedPopover(aRoot); aRoot->mStateFlags |= eIsNotInDocument; RemoveDependentIDsFor(aRoot); + RemoveDependentElementsFor(aRoot); // The parent of the removed subtree is about to be cleared, so we must do // this here rather than in LocalAccessible::UnbindFromParent because we need @@ -2757,7 +2844,7 @@ void DocAccessible::DispatchScrollingEvent(nsINode* aTarget, void DocAccessible::ARIAActiveDescendantIDMaybeMoved( LocalAccessible* aAccessible) { LocalAccessible* widget = nullptr; - if (aAccessible->IsActiveDescendant(&widget) && widget) { + if (aAccessible->IsActiveDescendantId(&widget) && widget) { // The active descendant might have just been inserted and may not be in the // tree yet. Therefore, schedule this async to ensure the tree is up to // date. @@ -2845,3 +2932,22 @@ void DocAccessible::MaybeHandleChangeToHiddenNameOrDescription( } } } + +void DocAccessible::AttrElementWillChange(dom::Element* aElement, + nsAtom* aAttr) { + MOZ_ASSERT(!sIsAttrElementChanging); + AttributeWillChange(aElement, kNameSpaceID_None, aAttr, + dom::MutationEvent_Binding::MODIFICATION); + // We might get notified about a related content attribute change. Ignore + // it. + sIsAttrElementChanging = true; +} + +void DocAccessible::AttrElementChanged(dom::Element* aElement, nsAtom* aAttr) { + MOZ_ASSERT(sIsAttrElementChanging); + // The element has changed and the content attribute change notifications + // (if any) have been sent. + sIsAttrElementChanging = false; + AttributeChanged(aElement, kNameSpaceID_None, aAttr, + dom::MutationEvent_Binding::MODIFICATION, nullptr); +} diff --git a/accessible/generic/DocAccessible.h b/accessible/generic/DocAccessible.h index 52cbdd68cf..791d09661e 100644 --- a/accessible/generic/DocAccessible.h +++ b/accessible/generic/DocAccessible.h @@ -121,7 +121,7 @@ class DocAccessible : public HyperTextAccessible, void QueueCacheUpdate(LocalAccessible* aAcc, uint64_t aNewDomain); /** - * Walks the mDependentIDsHashes list for the given accessible and + * Walks the dependent ids and elements maps for the given accessible and * queues a CacheDomain::Relations cache update fore each related acc. * We call this when we observe an ID mutation or when an acc is bound * to its document. @@ -410,6 +410,9 @@ class DocAccessible : public HyperTextAccessible, return mMovedAccessibles.Contains(aAcc); } + void AttrElementWillChange(dom::Element* aElement, nsAtom* aAttr); + void AttrElementChanged(dom::Element* aElement, nsAtom* aAttr); + protected: virtual ~DocAccessible(); @@ -485,6 +488,35 @@ class DocAccessible : public HyperTextAccessible, void RemoveDependentIDsFor(LocalAccessible* aRelProvider, nsAtom* aRelAttr = nullptr); + /** + * Add dependent elements targeted by a relation attribute on an accessible + * element to the dependent elements cache. This is used for reflected IDL + * attributes which return DOM elements and reflect a content attribute, where + * the IDL attribute has been set to an element. For example, if the + * .popoverTargetElement IDL attribute is set to an element using JS, the + * target element will be added to the dependent elements cache. If the + * relation attribute is not specified, then all relation attributes are + * checked. + * + * @param aRelProvider [in] the accessible with the relation IDL attribute. + * @param aRelAttr [in, optional] the name of the reflected content attribute. + * For example, for the popoverTargetElement IDL attribute, this would be + * "popovertarget". + */ + void AddDependentElementsFor(LocalAccessible* aRelProvider, + nsAtom* aRelAttr = nullptr); + + /** + * Remove dependent elements targeted by a relation attribute on an accessible + * element from the dependent elements cache. If the relation attribute is + * not specified, then all relation attributes are checked. + * + * @param aRelProvider [in] the accessible with the relation IDL attribute. + * @param aRelAttr [in, optional] the name of the reflected content attribute. + */ + void RemoveDependentElementsFor(LocalAccessible* aRelProvider, + nsAtom* aRelAttr = nullptr); + /** * Update or recreate an accessible depending on a changed attribute. * @@ -727,12 +759,35 @@ class DocAccessible : public HyperTextAccessible, void RemoveRelProvidersIfEmpty(dom::Element* aElement, const nsAString& aID); /** - * The cache of IDs pointed by relation attributes. + * A map used to look up the target node for an implicit reverse relation + * where the target of the explicit relation is specified as an id. + * For example: + *
Name:
+ * The div should get a LABEL_FOR relation targeting the input. To facilitate + * that, mDependentIDsHashes maps from "label" to an AttrRelProvider + * specifying aria-labelledby and the input. Because ids are scoped to the + * nearest ancestor document or shadow root, mDependentIDsHashes maps from the + * DocumentOrShadowRoot first. */ nsClassHashtable, DependentIDsHashtable> mDependentIDsHashes; + /** + * A map used to look up the target element for an implicit reverse relation + * where the target of the explicit relation is also specified as an element. + * This is similar to mDependentIDsHashes, except that this is used when a + * DOM property is used to set the relation target element directly, rather + * than using an id. For example: + *
Some info
+ * The button's .popoverTargetElement property is set to the div so that the + * button invokes the popover. + * To facilitate finding the invoker given the popover, mDependentElementsMap + * maps from the div to an AttrRelProvider specifying popovertarget and the + * button. + */ + nsTHashMap mDependentElementsMap; + friend class RelatedAccIterator; /** diff --git a/accessible/generic/LocalAccessible.cpp b/accessible/generic/LocalAccessible.cpp index f02f68faa6..a758164e2b 100644 --- a/accessible/generic/LocalAccessible.cpp +++ b/accessible/generic/LocalAccessible.cpp @@ -127,6 +127,7 @@ ENameValueFlag LocalAccessible::Name(nsString& aName) const { if (!aName.IsEmpty()) return eNameOK; ENameValueFlag nameFlag = NativeName(aName); + nsCoreUtils::TrimNonBreakingSpaces(aName); if (!aName.IsEmpty()) return nameFlag; // In the end get the name from tooltip. @@ -1840,10 +1841,40 @@ bool LocalAccessible::SetCurValue(double aValue) { kNameSpaceID_None, nsGkAtoms::aria_valuenow, strValue, true)); } +role LocalAccessible::FindNextValidARIARole( + std::initializer_list aRolesToSkip) const { + const nsRoleMapEntry* roleMapEntry = ARIARoleMap(); + if (roleMapEntry && mContent && mContent->IsElement()) { + dom::Element* elem = mContent->AsElement(); + if (!nsAccUtils::ARIAAttrValueIs(elem, nsGkAtoms::role, + roleMapEntry->roleAtom, eIgnoreCase)) { + // Get the next valid token that isn't in the list of roles to skip. + uint8_t roleMapIndex = + aria::GetFirstValidRoleMapIndexExcluding(elem, aRolesToSkip); + // If we don't find a valid token, fall back to the native role. + if (roleMapIndex == aria::NO_ROLE_MAP_ENTRY_INDEX || + roleMapIndex == aria::LANDMARK_ROLE_MAP_ENTRY_INDEX) { + return NativeRole(); + } + const nsRoleMapEntry* fallbackRoleMapEntry = + aria::GetRoleMapFromIndex(roleMapIndex); + if (!fallbackRoleMapEntry) { + return NativeRole(); + } + // Return the next valid role, but validate that first, too. + return ARIATransformRole(fallbackRoleMapEntry->role); + } + } + // Fall back to the native role. + return NativeRole(); +} + role LocalAccessible::ARIATransformRole(role aRole) const { // Beginning with ARIA 1.1, user agents are expected to use the native host - // language role of the element when the region role is used without a name. - // https://rawgit.com/w3c/aria/master/core-aam/core-aam.html#role-map-region + // language role of the element when the form or region roles are used without + // a name. Says the spec, "the user agent MUST treat such elements as if no + // role had been provided." + // https://w3c.github.io/aria/#document-handling_author-errors_roles // // XXX: While the name computation algorithm can be non-trivial in the general // case, it should not be especially bad here: If the author hasn't used the @@ -1851,10 +1882,18 @@ role LocalAccessible::ARIATransformRole(role aRole) const { // calculation rule excludes name from content. That said, this use case is // another example of why we should consider caching the accessible name. See: // https://bugzilla.mozilla.org/show_bug.cgi?id=1378235. - if (aRole == roles::REGION) { - nsAutoString name; - Name(name); - return name.IsEmpty() ? NativeRole() : aRole; + if (aRole == roles::REGION || aRole == roles::FORM) { + if (NameIsEmpty()) { + // If we have a "form" or "region" role, but no accessible name, we need + // to search for the next valid role. First, we search through the role + // attribute value string - there might be a valid fallback there. Skip + // all "form" or "region" attributes; we know they're not valid since + // there's no accessible name. If we find a valid role that's not "form" + // or "region", fall back to it (but run it through ARIATransformRole + // first). Otherwise, fall back to the element's native role. + return FindNextValidARIARole({nsGkAtoms::region, nsGkAtoms::form}); + } + return aRole; } // XXX: these unfortunate exceptions don't fit into the ARIA table. This is @@ -2510,6 +2549,10 @@ void LocalAccessible::Shutdown() { // LocalAccessible protected void LocalAccessible::ARIAName(nsString& aName) const { + // 'slot' elements should ignore aria-label and aria-labelledby. + if (mContent->IsHTMLElement(nsGkAtoms::slot)) { + return; + } // aria-labelledby now takes precedence over aria-label nsresult rv = nsTextEquivUtils::GetTextEquivFromIDRefs( this, nsGkAtoms::aria_labelledby, aName); @@ -3010,11 +3053,10 @@ LocalAccessible* LocalAccessible::CurrentItem() const { // For activedescendant, the ARIA spec does not require that the user agent // checks whether pointed node is actually a DOM descendant of the element // with the aria-activedescendant attribute. - nsAutoString id; - if (HasOwnContent() && mContent->IsElement() && - mContent->AsElement()->GetAttr(nsGkAtoms::aria_activedescendant, id)) { - dom::Element* activeDescendantElm = IDRefsIterator::GetElem(mContent, id); - if (activeDescendantElm) { + if (HasOwnContent() && mContent->IsElement()) { + if (dom::Element* activeDescendantElm = + nsCoreUtils::GetAriaActiveDescendantElement( + mContent->AsElement())) { if (mContent->IsInclusiveDescendantOf(activeDescendantElm)) { // Don't want a cyclical descendant relationship. That would be bad. return nullptr; @@ -3043,8 +3085,8 @@ LocalAccessible* LocalAccessible::ContainerWidget() const { parent = parent->LocalParent()) { nsIContent* parentContent = parent->GetContent(); if (parentContent && parentContent->IsElement() && - parentContent->AsElement()->HasAttr( - nsGkAtoms::aria_activedescendant)) { + nsCoreUtils::GetAriaActiveDescendantElement( + parentContent->AsElement())) { return parent; } @@ -3055,7 +3097,7 @@ LocalAccessible* LocalAccessible::ContainerWidget() const { return nullptr; } -bool LocalAccessible::IsActiveDescendant(LocalAccessible** aWidget) const { +bool LocalAccessible::IsActiveDescendantId(LocalAccessible** aWidget) const { if (!HasOwnContent() || !mContent->HasID()) { return false; } diff --git a/accessible/generic/LocalAccessible.h b/accessible/generic/LocalAccessible.h index a3620f4cbd..51c4cc9424 100644 --- a/accessible/generic/LocalAccessible.h +++ b/accessible/generic/LocalAccessible.h @@ -587,7 +587,12 @@ class LocalAccessible : public nsISupports, public Accessible { */ virtual LocalAccessible* ContainerWidget() const; - bool IsActiveDescendant(LocalAccessible** aWidget = nullptr) const; + /** + * Accessible's element ID is referenced as a aria-activedescendant in the + * document. This method is only used for ID changes and therefore does not + * need to work for direct element references via ariaActiveDescendantElement. + */ + bool IsActiveDescendantId(LocalAccessible** aWidget = nullptr) const; /** * Return true if the accessible is defunct. @@ -1010,6 +1015,17 @@ class LocalAccessible : public nsISupports, public Accessible { */ nsIFrame* FindNearestAccessibleAncestorFrame(); + /* + * This function assumes that the current role is not valid. It searches for a + * fallback role in the role attribute string, and returns it. If there is no + * valid fallback role in the role attribute string, the function returns the + * native role. The aRolesToSkip parameter will cause the function to skip any + * roles found in the role attribute string when searching for the next valid + * role. + */ + role FindNextValidARIARole( + std::initializer_list aRolesToSkip) const; + LocalAccessible* GetPopoverTargetDetailsRelation() const; }; diff --git a/accessible/generic/moz.build b/accessible/generic/moz.build index 06e229b999..6d34fe7d68 100644 --- a/accessible/generic/moz.build +++ b/accessible/generic/moz.build @@ -53,6 +53,10 @@ elif CONFIG["MOZ_WIDGET_TOOLKIT"] == "android": LOCAL_INCLUDES += [ "/accessible/android", ] +elif CONFIG["MOZ_WIDGET_TOOLKIT"] == "uikit": + LOCAL_INCLUDES += [ + "/accessible/ios", + ] else: LOCAL_INCLUDES += [ "/accessible/other", -- cgit v1.2.3