Skip to content

Commit

Permalink
Base Revision
Browse files Browse the repository at this point in the history
  • Loading branch information
Sid Janga committed Mar 20, 2018
0 parents commit 8eb1d93
Show file tree
Hide file tree
Showing 76 changed files with 7,814 additions and 0 deletions.
23 changes: 23 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# How to Contribute

We'd love to accept your patches and contributions to this project. There are
just a few small guidelines you need to follow.

## Contributor License Agreement

Contributions to this project must be accompanied by a Contributor License
Agreement. You (or your employer) retain the copyright to your contribution,
this simply gives us permission to use and redistribute your contributions as
part of the project. Head over to <https://cla.developers.google.com/> to see
your current agreements on file or to sign a new one.

You generally only need to submit a CLA once, so if you've already submitted one
(even if it was for a different project), you probably don't need to do it
again.

## Code reviews

All submissions, including submissions by project members, require review. We
use GitHub pull requests for this purpose. Consult
[GitHub Help](https://help.github.com/articles/about-pull-requests/) for more
information on using pull requests.
44 changes: 44 additions & 0 deletions Classes/GTAxe.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
//
// Copyright 2018 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

/**
* Umbrella header for public GTX APIs.
*/
#import <UIKit/UIKit.h>

//! Project version number for GTAxe.
FOUNDATION_EXPORT double GTAxeVersionNumber;

//! Project version string for GTAxe.
FOUNDATION_EXPORT const unsigned char GTAxeVersionString[];

#import <GTAxe/GTXAccessibilityTree.h>
#import <GTAxe/GTXAnalytics.h>
#import <GTAxe/GTXAnalyticsUtils.h>
#import <GTAxe/GTXAssertions.h>
#import <GTAxe/GTXAxeCore.h>
#import <GTAxe/GTXCheckBlock.h>
#import <GTAxe/GTXChecksCollection.h>
#import <GTAxe/GTXCommon.h>
#import <GTAxe/GTXElementBlacklist.h>
#import <GTAxe/GTXErrorReporter.h>
#import <GTAxe/GTXImageRGBAData.h>
#import <GTAxe/GTXImageAndColorUtils.h>
#import <GTAxe/GTXLogging.h>
#import <GTAxe/GTXPluginXCTestCase.h>
#import <GTAxe/GTXToolKit.h>
#import <GTAxe/GTXTestSuite.h>
#import <GTAxe/NSError+GTXAdditions.h>
33 changes: 33 additions & 0 deletions Classes/GTXAccessibilityTree.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//
// Copyright 2018 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

/**
* An enumerator for the accessibility trees navigated by accessibility services like VoiceOver.
*/
@interface GTXAccessibilityTree : NSEnumerator

/**
* Creates a new accessibility tree whose traversals will include the given @c rootElements.
*/
- (instancetype)initWithRootElements:(NSArray *)rootElements;

@end

NS_ASSUME_NONNULL_END
201 changes: 201 additions & 0 deletions Classes/GTXAccessibilityTree.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
//
// Copyright 2018 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

#import <UIKit/UIKit.h>

#import "GTXAccessibilityTree.h"

/**
* There seems to be errors in accessibility children reported by some UIKit classes especially
* UITextEffectsWindow which reports 9223372036854775807 possibly due to internal type conversions
* with -1, we use this bounds value to detect that case..
*/
const NSInteger kAccessibilityChildrenUpperBound = 50000;

@implementation GTXAccessibilityTree {
// A queue of elements to be visited.
NSMutableArray *_queue;
// A queue of elements already visited.
NSMutableSet *_visitedElements;
}

- (instancetype)initWithRootElements:(NSArray *)rootElements {
self = [super init];
if (self) {
_queue = [[NSMutableArray alloc] initWithArray:rootElements];
_visitedElements = [[NSMutableSet alloc] init];
}
return self;
}

#pragma mark - NSEnumerator

- (id)nextObject {
if ([_queue count] == 0) {
return nil;
}

id nextInQueue;
// Get the next "unvisited" element.
do {
id candidateNext = [_queue firstObject];
[_queue removeObjectAtIndex:0];
if (![_visitedElements containsObject:candidateNext]) {
if (![self gtx_isAccessibilityHiddenElement:candidateNext]) {
nextInQueue = candidateNext;
}
}
} while ([_queue count] > 0 && !nextInQueue);
if (!nextInQueue) {
return nil;
}

[_visitedElements addObject:nextInQueue];
if ([nextInQueue respondsToSelector:@selector(isAccessibilityElement)]) {
if (![nextInQueue isAccessibilityElement]) {
// nextInQueue could be an accessibility container, if so enqueue its children.
// There are two ways of getting the children of an accessibility container:
// First, using @selector(accessibilityElements)
NSArray *axElements = [self gtx_accessibilityElementsOfElement:nextInQueue];

// Second, using @selector(accessibilityElementAtIndex:)
NSArray *axElementsFromIndices =
[self gtx_accessibilityElementsFromIndicesOfElement:nextInQueue];

// Ensure that either the children are available only through one method or elements via both
// are the same. Otherwise we must fail as the the accessibility tree is inconsistent.
if (axElements && axElementsFromIndices) {
NSSet *accessibilityElementsSet = [NSSet setWithArray:axElements];
NSSet *accessibilityElementsFromIndicesSet = [NSSet setWithArray:axElementsFromIndices];
NSAssert([accessibilityElementsSet isEqualToSet:accessibilityElementsFromIndicesSet],
@"Accessibility elements obtained from -accessibilityElements and"
@" -accessibilityElementAtIndex: are different - they must not be. Either provide"
@" elements via one method or provide the same elements.\nDetails:\nElements via"
@" accessibilityElements:%@\nElements via accessibilityElementAtIndex:\n"
@"accessibilityElementCount:%@\nElements:%@",
accessibilityElementsSet,
@([nextInQueue accessibilityElementCount]),
accessibilityElementsFromIndicesSet);
} else {
// Set accessibilityElements to whichever is non nil or leave it as is.
axElements = axElementsFromIndices ? axElementsFromIndices : axElements;
}
if (![nextInQueue respondsToSelector:@selector(accessibilityElementsHidden)] ||
![nextInQueue accessibilityElementsHidden]) {
for (id element in axElements) {
if (![_visitedElements containsObject:element]) {
[_queue addObject:element];
}
}
}

// nextInQueue could be a UIView subclass, if so enqueue its subviews.
NSArray *subViews;
if ([nextInQueue isKindOfClass:[UITableViewCell class]] ||
[nextInQueue isKindOfClass:[UICollectionViewCell class]]) {
subViews = [nextInQueue contentView].subviews;
} else if ([nextInQueue respondsToSelector:@selector(subviews)]) {
subViews = [nextInQueue subviews];
}
if ([nextInQueue respondsToSelector:@selector(isHidden)] &&
![nextInQueue isHidden]) {
for (id child in subViews) {
if (![_visitedElements containsObject:child]) {
[_queue addObject:child];
}
}
}
}
}
return nextInQueue;
}

#pragma mark - NSExtendedEnumerator

- (NSArray *)allObjects {
NSMutableArray *remainingObjects = [[NSMutableArray alloc] init];
id nextObject;
while ((nextObject = [self nextObject])) {
[remainingObjects addObject:nextObject];
}
return remainingObjects;
}

#pragma mark - Private


/**
* @return An array of accessible children of the given @c element as reported by the selector
* -[NSObject(UIAccessibility) accessibilityElements].
*/
- (NSArray *)gtx_accessibilityElementsOfElement:(id)element {
if ([element respondsToSelector:@selector(accessibilityElements)]) {
return [element accessibilityElements];
}
return nil;
}

/**
* @return An array of accessible children of the given @c element as reported by the selector
* -[NSObject(UIAccessibility) accessibilityElementAtIndex:].
*/
- (NSArray *)gtx_accessibilityElementsFromIndicesOfElement:(id)element {
NSMutableArray *axElementsFromIndices;
if ([element respondsToSelector:@selector(accessibilityElementAtIndex:)] &&
[element respondsToSelector:@selector(accessibilityElementCount)]) {
NSInteger childrenCount = [element accessibilityElementCount];
// This is a workaround to deal with UIKit classes that are reporting incorrect
// accessibilityElementCount, see kAccessibilityChildrenUpperBound.
if (childrenCount > 0 && childrenCount < kAccessibilityChildrenUpperBound) {
axElementsFromIndices = [[NSMutableArray alloc] initWithCapacity:(NSUInteger)childrenCount];
for (NSInteger index = 0; index < childrenCount; index++) {
[axElementsFromIndices addObject:[element accessibilityElementAtIndex:index]];
}
}
}
return axElementsFromIndices;
}

/**
* Elements are hidden from accessibility trees
*
* @return @c YES if the element is hidden from accessibility tree @c NO otherwise.
*/
- (BOOL)gtx_isAccessibilityHiddenElement:(id)element {
BOOL isHidden = NO;
BOOL isAccessibilityHidden = NO;
BOOL isHiddenDueToAccessibilityFrame = NO;
BOOL isHiddenDueToFrame = NO;
if ([element respondsToSelector:@selector(isHidden)]) {
isHidden = [element isHidden];
}
if ([element respondsToSelector:@selector(accessibilityElementsHidden)]) {
isAccessibilityHidden = [element accessibilityElementsHidden];
}
if ([element respondsToSelector:@selector(accessibilityFrame)]) {
CGRect accessibilityFrame = [element accessibilityFrame];
isHiddenDueToAccessibilityFrame = (accessibilityFrame.size.width == 0 ||
accessibilityFrame.size.height == 0);
}
if ([element respondsToSelector:@selector(frame)]) {
CGRect frame = [element frame];
isHiddenDueToFrame = frame.size.width == 0 || frame.size.height == 0;
}
return (isHidden || isAccessibilityHidden ||
(isHiddenDueToFrame && isHiddenDueToAccessibilityFrame));
}

@end
74 changes: 74 additions & 0 deletions Classes/GTXAnalytics.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
//
// Copyright 2018 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

#import <UIKit/UIKit.h>

#import "GTXChecking.h"
#import "GTXCheckBlock.h"

NS_ASSUME_NONNULL_BEGIN


/**
Enum of all possible analytics events handleed by GTAxe.
*/
typedef NS_ENUM(NSUInteger, GTXAnalyticsEvent) {
/**
Enum for GTAxe checks being invoked.
*/
GTXAnalyticsEventChecksPerformed,

/**
Enum for GTAxe checks failure detection.
*/
GTXAnalyticsEventChecksFailed,
};


/**
Typedef for Analytics handler.
@param event The analytics event to be handled.
*/
typedef void(^GTXAnalyticsHandlerBlock)(GTXAnalyticsEvent event);


/**
Class that handles all analytics in GTAxe.
*/
@interface GTXAnalytics : NSObject

/**
Boolean property that specifies if analytics is enabled or not.
*/
@property (class, nonatomic, assign) BOOL enabled;

/**
Current analytics handler, users can override this for custom handling of analytics events.
*/
@property (class, nonatomic) GTXAnalyticsHandlerBlock handler;


/**
Feeds an analytics event to be handled.
@param event The event to be handled.
*/
+ (void)invokeAnalyticsEvent:(GTXAnalyticsEvent)event;

@end

NS_ASSUME_NONNULL_END
Loading

0 comments on commit 8eb1d93

Please sign in to comment.