1/* -*- Mode: Objective-C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2/* vim:expandtab:shiftwidth=2:tabstop=2: 3 */ 4/* This Source Code Form is subject to the terms of the Mozilla Public 5 * License, v. 2.0. If a copy of the MPL was not distributed with this 6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 7 8#import "mozTableAccessible.h" 9#import "nsCocoaUtils.h" 10#import "AccIterator.h" 11#import "TableAccessible.h" 12 13@implementation mozColumnContainer 14 15- (id)initWithIndex:(uint32_t)aIndex andParent:(mozAccessible*)aParent { 16 self = [super init]; 17 mIndex = aIndex; 18 mParent = aParent; 19 return self; 20} 21 22- (NSString*)moxRole { 23 return NSAccessibilityColumnRole; 24} 25 26- (NSString*)moxRoleDescription { 27 return NSAccessibilityRoleDescription(NSAccessibilityColumnRole, nil); 28} 29 30- (mozAccessible*)moxParent { 31 return mParent; 32} 33 34- (NSArray*)moxChildren { 35 if (mChildren) return mChildren; 36 37 mChildren = [[NSMutableArray alloc] init]; 38 39 if (Accessible* acc = [mParent geckoAccessible].AsAccessible()) { 40 TableAccessible* table = acc->AsTable(); 41 NSAssert(table, @"Got null table when fetching column children!"); 42 uint32_t numRows = table->RowCount(); 43 44 for (uint32_t j = 0; j < numRows; j++) { 45 Accessible* cell = table->CellAt(j, mIndex); 46 mozAccessible* nativeCell = cell ? GetNativeFromGeckoAccessible(cell) : nil; 47 if ([nativeCell isAccessibilityElement]) { 48 [mChildren addObject:nativeCell]; 49 } 50 } 51 52 } else if (ProxyAccessible* proxy = [mParent geckoAccessible].AsProxy()) { 53 uint32_t numRows = proxy->TableRowCount(); 54 55 for (uint32_t j = 0; j < numRows; j++) { 56 ProxyAccessible* cell = proxy->TableCellAt(j, mIndex); 57 mozAccessible* nativeCell = cell ? GetNativeFromGeckoAccessible(cell) : nil; 58 if ([nativeCell isAccessibilityElement]) { 59 [mChildren addObject:nativeCell]; 60 } 61 } 62 } 63 64 return mChildren; 65} 66 67- (void)dealloc { 68 NS_OBJC_BEGIN_TRY_ABORT_BLOCK; 69 70 [self invalidateChildren]; 71 [super dealloc]; 72 73 NS_OBJC_END_TRY_ABORT_BLOCK; 74} 75 76- (void)expire { 77 NS_OBJC_BEGIN_TRY_ABORT_BLOCK; 78 79 [self invalidateChildren]; 80 81 mParent = nil; 82 83 [super expire]; 84 85 NS_OBJC_END_TRY_ABORT_BLOCK; 86} 87 88- (BOOL)isExpired { 89 MOZ_ASSERT((mChildren == nil && mParent == nil) == mIsExpired); 90 91 return [super isExpired]; 92} 93 94- (void)invalidateChildren { 95 NS_OBJC_BEGIN_TRY_ABORT_BLOCK; 96 97 // make room for new children 98 if (mChildren) { 99 [mChildren release]; 100 mChildren = nil; 101 } 102 103 NS_OBJC_END_TRY_ABORT_BLOCK; 104} 105 106@end 107 108@implementation mozTablePartAccessible 109 110- (NSString*)moxTitle { 111 return @""; 112} 113 114- (NSString*)moxRole { 115 return [self isLayoutTablePart] ? NSAccessibilityGroupRole : [super moxRole]; 116} 117 118- (BOOL)isLayoutTablePart { 119 if (Accessible* acc = mGeckoAccessible.AsAccessible()) { 120 while (acc) { 121 if (acc->IsTable()) { 122 return acc->AsTable()->IsProbablyLayoutTable(); 123 } 124 acc = acc->Parent(); 125 } 126 return false; 127 } 128 129 if (ProxyAccessible* proxy = mGeckoAccessible.AsProxy()) { 130 while (proxy) { 131 if (proxy->IsTable()) { 132 return proxy->TableIsProbablyForLayout(); 133 } 134 proxy = proxy->Parent(); 135 } 136 } 137 138 return false; 139} 140 141@end 142 143@implementation mozTableAccessible 144 145- (void)handleAccessibleEvent:(uint32_t)eventType { 146 if (eventType == nsIAccessibleEvent::EVENT_REORDER) { 147 [self invalidateColumns]; 148 } 149 150 [super handleAccessibleEvent:eventType]; 151} 152 153- (void)dealloc { 154 NS_OBJC_BEGIN_TRY_ABORT_BLOCK; 155 156 [self invalidateColumns]; 157 [super dealloc]; 158 159 NS_OBJC_END_TRY_ABORT_BLOCK; 160} 161 162- (NSNumber*)moxRowCount { 163 MOZ_ASSERT(!mGeckoAccessible.IsNull()); 164 165 return mGeckoAccessible.IsAccessible() ? @(mGeckoAccessible.AsAccessible()->AsTable()->RowCount()) 166 : @(mGeckoAccessible.AsProxy()->TableRowCount()); 167} 168 169- (NSNumber*)moxColumnCount { 170 MOZ_ASSERT(!mGeckoAccessible.IsNull()); 171 172 return mGeckoAccessible.IsAccessible() ? @(mGeckoAccessible.AsAccessible()->AsTable()->ColCount()) 173 : @(mGeckoAccessible.AsProxy()->TableColumnCount()); 174} 175 176- (NSArray*)moxRows { 177 // Create a new array with the list of table rows. 178 return [[self moxChildren] 179 filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(mozAccessible* child, 180 NSDictionary* bindings) { 181 return [child isKindOfClass:[mozTableRowAccessible class]]; 182 }]]; 183} 184 185- (NSArray*)moxColumns { 186 MOZ_ASSERT(!mGeckoAccessible.IsNull()); 187 188 if (mColContainers) { 189 return mColContainers; 190 } 191 192 mColContainers = [[NSMutableArray alloc] init]; 193 uint32_t numCols = 0; 194 195 if (Accessible* acc = mGeckoAccessible.AsAccessible()) { 196 numCols = acc->AsTable()->ColCount(); 197 } else { 198 numCols = mGeckoAccessible.AsProxy()->TableColumnCount(); 199 } 200 201 for (uint32_t i = 0; i < numCols; i++) { 202 mozColumnContainer* container = [[mozColumnContainer alloc] initWithIndex:i andParent:self]; 203 [mColContainers addObject:container]; 204 } 205 206 return mColContainers; 207} 208 209- (NSArray*)moxChildren { 210 return [[super moxChildren] arrayByAddingObjectsFromArray:[self moxColumns]]; 211} 212 213- (void)invalidateColumns { 214 NS_OBJC_BEGIN_TRY_ABORT_BLOCK; 215 if (mColContainers) { 216 [mColContainers release]; 217 mColContainers = nil; 218 } 219 NS_OBJC_END_TRY_ABORT_BLOCK; 220} 221 222@end 223 224@implementation mozTableRowAccessible 225 226- (void)handleAccessibleEvent:(uint32_t)eventType { 227 if (eventType == nsIAccessibleEvent::EVENT_REORDER) { 228 id parent = [self moxParent]; 229 if ([parent isKindOfClass:[mozTableAccessible class]]) { 230 [parent invalidateColumns]; 231 } 232 } 233 234 [super handleAccessibleEvent:eventType]; 235} 236 237- (NSNumber*)moxIndex { 238 mozTableAccessible* parent = (mozTableAccessible*)[self moxParent]; 239 return @([[parent moxRows] indexOfObjectIdenticalTo:self]); 240} 241 242@end 243 244@implementation mozTableCellAccessible 245 246- (NSValue*)moxRowIndexRange { 247 MOZ_ASSERT(!mGeckoAccessible.IsNull()); 248 249 if (mGeckoAccessible.IsAccessible()) { 250 TableCellAccessible* cell = mGeckoAccessible.AsAccessible()->AsTableCell(); 251 return [NSValue valueWithRange:NSMakeRange(cell->RowIdx(), cell->RowExtent())]; 252 } else { 253 ProxyAccessible* proxy = mGeckoAccessible.AsProxy(); 254 return [NSValue valueWithRange:NSMakeRange(proxy->RowIdx(), proxy->RowExtent())]; 255 } 256} 257 258- (NSValue*)moxColumnIndexRange { 259 MOZ_ASSERT(!mGeckoAccessible.IsNull()); 260 261 if (mGeckoAccessible.IsAccessible()) { 262 TableCellAccessible* cell = mGeckoAccessible.AsAccessible()->AsTableCell(); 263 return [NSValue valueWithRange:NSMakeRange(cell->ColIdx(), cell->ColExtent())]; 264 } else { 265 ProxyAccessible* proxy = mGeckoAccessible.AsProxy(); 266 return [NSValue valueWithRange:NSMakeRange(proxy->ColIdx(), proxy->ColExtent())]; 267 } 268} 269 270- (NSArray*)moxRowHeaderUIElements { 271 MOZ_ASSERT(!mGeckoAccessible.IsNull()); 272 273 if (mGeckoAccessible.IsAccessible()) { 274 TableCellAccessible* cell = mGeckoAccessible.AsAccessible()->AsTableCell(); 275 AutoTArray<Accessible*, 10> headerCells; 276 cell->RowHeaderCells(&headerCells); 277 return utils::ConvertToNSArray(headerCells); 278 } else { 279 ProxyAccessible* proxy = mGeckoAccessible.AsProxy(); 280 nsTArray<ProxyAccessible*> headerCells; 281 proxy->RowHeaderCells(&headerCells); 282 return utils::ConvertToNSArray(headerCells); 283 } 284} 285 286- (NSArray*)moxColumnHeaderUIElements { 287 MOZ_ASSERT(!mGeckoAccessible.IsNull()); 288 289 if (mGeckoAccessible.IsAccessible()) { 290 TableCellAccessible* cell = mGeckoAccessible.AsAccessible()->AsTableCell(); 291 AutoTArray<Accessible*, 10> headerCells; 292 cell->ColHeaderCells(&headerCells); 293 return utils::ConvertToNSArray(headerCells); 294 } else { 295 ProxyAccessible* proxy = mGeckoAccessible.AsProxy(); 296 nsTArray<ProxyAccessible*> headerCells; 297 proxy->ColHeaderCells(&headerCells); 298 return utils::ConvertToNSArray(headerCells); 299 } 300} 301 302@end 303