[ATK] Defer the emision of AtkObject::children-changed signal after layout is done
[WebKit-https.git] / Source / WebCore / accessibility / mac / AXObjectCacheMac.mm
1 /*
2  * Copyright (C) 2003, 2004, 2005, 2006, 2008, 2012 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #import "config.h"
27 #import "AXObjectCache.h"
28
29 #if HAVE(ACCESSIBILITY) && PLATFORM(MAC)
30
31 #import "AXIsolatedTreeNode.h"
32 #import "AccessibilityObject.h"
33 #import "AccessibilityTable.h"
34 #import "RenderObject.h"
35 #import "WebAccessibilityObjectWrapperMac.h"
36 #import <pal/spi/mac/NSAccessibilitySPI.h>
37
38 #if USE(APPLE_INTERNAL_SDK)
39 #include <ApplicationServices/ApplicationServicesPriv.h>
40 #endif
41
42 #ifndef NSAccessibilityLiveRegionChangedNotification
43 #define NSAccessibilityLiveRegionChangedNotification @"AXLiveRegionChanged"
44 #endif
45
46 #ifndef NSAccessibilityLiveRegionCreatedNotification 
47 #define NSAccessibilityLiveRegionCreatedNotification @"AXLiveRegionCreated"
48 #endif
49
50 #ifndef NSAccessibilityTextStateChangeTypeKey
51 #define NSAccessibilityTextStateChangeTypeKey @"AXTextStateChangeType"
52 #endif
53
54 #ifndef NSAccessibilityTextStateSyncKey
55 #define NSAccessibilityTextStateSyncKey @"AXTextStateSync"
56 #endif
57
58 #ifndef NSAccessibilityTextSelectionDirection
59 #define NSAccessibilityTextSelectionDirection @"AXTextSelectionDirection"
60 #endif
61
62 #ifndef NSAccessibilityTextSelectionGranularity
63 #define NSAccessibilityTextSelectionGranularity @"AXTextSelectionGranularity"
64 #endif
65
66 #ifndef NSAccessibilityTextSelectionChangedFocus
67 #define NSAccessibilityTextSelectionChangedFocus @"AXTextSelectionChangedFocus"
68 #endif
69
70 #ifndef NSAccessibilityTextEditType
71 #define NSAccessibilityTextEditType @"AXTextEditType"
72 #endif
73
74 #ifndef NSAccessibilityTextChangeValues
75 #define NSAccessibilityTextChangeValues @"AXTextChangeValues"
76 #endif
77
78 #ifndef NSAccessibilityTextChangeValue
79 #define NSAccessibilityTextChangeValue @"AXTextChangeValue"
80 #endif
81
82 #ifndef NSAccessibilityTextChangeValueLength
83 #define NSAccessibilityTextChangeValueLength @"AXTextChangeValueLength"
84 #endif
85
86 #ifndef NSAccessibilityTextChangeValueStartMarker
87 #define NSAccessibilityTextChangeValueStartMarker @"AXTextChangeValueStartMarker"
88 #endif
89
90 #ifndef NSAccessibilityTextChangeElement
91 #define NSAccessibilityTextChangeElement @"AXTextChangeElement"
92 #endif
93
94 #ifndef NSAccessibilitySelectedTextMarkerRangeAttribute
95 #define NSAccessibilitySelectedTextMarkerRangeAttribute @"AXSelectedTextMarkerRange"
96 #endif
97
98 // Very large strings can negatively impact the performance of notifications, so this length is chosen to try to fit an average paragraph or line of text, but not allow strings to be large enough to hurt performance.
99 static const NSUInteger AXValueChangeTruncationLength = 1000;
100
101 // Check if platform provides enums for text change notifications
102 #ifndef AXTextStateChangeDefined
103 #define AXTextStateChangeDefined
104
105 typedef CF_ENUM(UInt32, AXTextStateChangeType)
106 {
107     kAXTextStateChangeTypeUnknown,
108     kAXTextStateChangeTypeEdit,
109     kAXTextStateChangeTypeSelectionMove,
110     kAXTextStateChangeTypeSelectionExtend,
111     kAXTextStateChangeTypeSelectionBoundary
112 };
113
114 typedef CF_ENUM(UInt32, AXTextEditType)
115 {
116     kAXTextEditTypeUnknown,
117     kAXTextEditTypeDelete,
118     kAXTextEditTypeInsert,
119     kAXTextEditTypeTyping,
120     kAXTextEditTypeDictation,
121     kAXTextEditTypeCut,
122     kAXTextEditTypePaste,
123     kAXTextEditTypeAttributesChange
124 };
125
126 typedef CF_ENUM(UInt32, AXTextSelectionDirection)
127 {
128     kAXTextSelectionDirectionUnknown = 0,
129     kAXTextSelectionDirectionBeginning,
130     kAXTextSelectionDirectionEnd,
131     kAXTextSelectionDirectionPrevious,
132     kAXTextSelectionDirectionNext,
133     kAXTextSelectionDirectionDiscontiguous
134 };
135
136 typedef CF_ENUM(UInt32, AXTextSelectionGranularity)
137 {
138     kAXTextSelectionGranularityUnknown,
139     kAXTextSelectionGranularityCharacter,
140     kAXTextSelectionGranularityWord,
141     kAXTextSelectionGranularityLine,
142     kAXTextSelectionGranularitySentence,
143     kAXTextSelectionGranularityParagraph,
144     kAXTextSelectionGranularityPage,
145     kAXTextSelectionGranularityDocument,
146     kAXTextSelectionGranularityAll
147 };
148
149 #endif // AXTextStateChangeDefined
150
151 static AXTextStateChangeType platformChangeTypeForWebCoreChangeType(WebCore::AXTextStateChangeType changeType)
152 {
153     switch (changeType) {
154     case WebCore::AXTextStateChangeTypeUnknown:
155         return kAXTextStateChangeTypeUnknown;
156     case WebCore::AXTextStateChangeTypeEdit:
157         return kAXTextStateChangeTypeEdit;
158     case WebCore::AXTextStateChangeTypeSelectionMove:
159         return kAXTextStateChangeTypeSelectionMove;
160     case WebCore::AXTextStateChangeTypeSelectionExtend:
161         return kAXTextStateChangeTypeSelectionExtend;
162     case WebCore::AXTextStateChangeTypeSelectionBoundary:
163         return kAXTextStateChangeTypeSelectionBoundary;
164     }
165 }
166
167 static AXTextEditType platformEditTypeForWebCoreEditType(WebCore::AXTextEditType changeType)
168 {
169     switch (changeType) {
170     case WebCore::AXTextEditTypeUnknown:
171         return kAXTextEditTypeUnknown;
172     case WebCore::AXTextEditTypeDelete:
173         return kAXTextEditTypeDelete;
174     case WebCore::AXTextEditTypeInsert:
175         return kAXTextEditTypeInsert;
176     case WebCore::AXTextEditTypeTyping:
177         return kAXTextEditTypeTyping;
178     case WebCore::AXTextEditTypeDictation:
179         return kAXTextEditTypeDictation;
180     case WebCore::AXTextEditTypeCut:
181         return kAXTextEditTypeCut;
182     case WebCore::AXTextEditTypePaste:
183         return kAXTextEditTypePaste;
184     case WebCore::AXTextEditTypeAttributesChange:
185         return kAXTextEditTypeAttributesChange;
186     }
187 }
188
189 static AXTextSelectionDirection platformDirectionForWebCoreDirection(WebCore::AXTextSelectionDirection direction)
190 {
191     switch (direction) {
192     case WebCore::AXTextSelectionDirectionUnknown:
193         return kAXTextSelectionDirectionUnknown;
194     case WebCore::AXTextSelectionDirectionBeginning:
195         return kAXTextSelectionDirectionBeginning;
196     case WebCore::AXTextSelectionDirectionEnd:
197         return kAXTextSelectionDirectionEnd;
198     case WebCore::AXTextSelectionDirectionPrevious:
199         return kAXTextSelectionDirectionPrevious;
200     case WebCore::AXTextSelectionDirectionNext:
201         return kAXTextSelectionDirectionNext;
202     case WebCore::AXTextSelectionDirectionDiscontiguous:
203         return kAXTextSelectionDirectionDiscontiguous;
204     }
205 }
206
207 static AXTextSelectionGranularity platformGranularityForWebCoreGranularity(WebCore::AXTextSelectionGranularity granularity)
208 {
209     switch (granularity) {
210     case WebCore::AXTextSelectionGranularityUnknown:
211         return kAXTextSelectionGranularityUnknown;
212     case WebCore::AXTextSelectionGranularityCharacter:
213         return kAXTextSelectionGranularityCharacter;
214     case WebCore::AXTextSelectionGranularityWord:
215         return kAXTextSelectionGranularityWord;
216     case WebCore::AXTextSelectionGranularityLine:
217         return kAXTextSelectionGranularityLine;
218     case WebCore::AXTextSelectionGranularitySentence:
219         return kAXTextSelectionGranularitySentence;
220     case WebCore::AXTextSelectionGranularityParagraph:
221         return kAXTextSelectionGranularityParagraph;
222     case WebCore::AXTextSelectionGranularityPage:
223         return kAXTextSelectionGranularityPage;
224     case WebCore::AXTextSelectionGranularityDocument:
225         return kAXTextSelectionGranularityDocument;
226     case WebCore::AXTextSelectionGranularityAll:
227         return kAXTextSelectionGranularityAll;
228     }
229 }
230
231 // The simple Cocoa calls in this file don't throw exceptions.
232
233 namespace WebCore {
234
235 void AXObjectCache::detachWrapper(AccessibilityObject* obj, AccessibilityDetachmentType)
236 {
237     [obj->wrapper() detach];
238     obj->setWrapper(nullptr);
239 }
240
241 #if ENABLE(ACCESSIBILITY_ISOLATED_TREE)
242 void AXObjectCache::associateIsolatedTreeNode(AccessibilityObject& object, AXIsolatedTreeNode& node, AXIsolatedTreeID treeID)
243 {
244     auto wrapper = object.wrapper();
245     ASSERT(wrapper);
246     wrapper.isolatedTreeIdentifier = treeID;
247     node.setWrapper(wrapper);
248 }
249 #endif
250
251 void AXObjectCache::attachWrapper(AccessibilityObject* obj)
252 {
253     RetainPtr<WebAccessibilityObjectWrapper> wrapper = adoptNS([[WebAccessibilityObjectWrapper alloc] initWithAccessibilityObject:obj]);
254     obj->setWrapper(wrapper.get());
255 }
256
257 static BOOL axShouldRepostNotificationsForTests = false;
258
259 void AXObjectCache::setShouldRepostNotificationsForTests(bool value)
260 {
261     axShouldRepostNotificationsForTests = value;
262 }
263
264 static void AXPostNotificationWithUserInfo(AccessibilityObjectWrapper *object, NSString *notification, id userInfo, bool skipSystemNotification = false)
265 {
266     if (id associatedPluginParent = [object associatedPluginParent])
267         object = associatedPluginParent;
268
269     // To simplify monitoring for notifications in tests, repost as a simple NSNotification instead of forcing test infrastucture to setup an IPC client and do all the translation between WebCore types and platform specific IPC types and back
270     if (UNLIKELY(axShouldRepostNotificationsForTests))
271         [object accessibilityPostedNotification:notification userInfo:userInfo];
272
273     if (skipSystemNotification)
274         return;
275
276     NSAccessibilityPostNotificationWithUserInfo(object, notification, userInfo);
277 }
278
279 void AXObjectCache::postPlatformNotification(AccessibilityObject* obj, AXNotification notification)
280 {
281     if (!obj)
282         return;
283
284     bool skipSystemNotification = false;
285     // Some notifications are unique to Safari and do not have NSAccessibility equivalents.
286     NSString *macNotification;
287     switch (notification) {
288         case AXActiveDescendantChanged:
289             // An active descendant change for trees means a selected rows change.
290             if (obj->isTree() || obj->isTable())
291                 macNotification = NSAccessibilitySelectedRowsChangedNotification;
292             
293             // When a combobox uses active descendant, it means the selected item in its associated
294             // list has changed. In these cases we should use selected children changed, because
295             // we don't want the focus to change away from the combobox where the user is typing.
296             else if (obj->isComboBox() || obj->isList() || obj->isListBox())
297                 macNotification = NSAccessibilitySelectedChildrenChangedNotification;
298             else
299                 macNotification = NSAccessibilityFocusedUIElementChangedNotification;                
300             break;
301         case AXAutocorrectionOccured:
302             macNotification = @"AXAutocorrectionOccurred";
303             break;
304         case AXFocusedUIElementChanged:
305             macNotification = NSAccessibilityFocusedUIElementChangedNotification;
306             break;
307         case AXLayoutComplete:
308             macNotification = @"AXLayoutComplete";
309             break;
310         case AXLoadComplete:
311             macNotification = @"AXLoadComplete";
312             // Frame loading events are handled by the UIProcess on macOS to improve reliability.
313             // On macOS, before notifications are allowed by AppKit to be sent to clients, you need to have a client (e.g. VoiceOver)
314             // register for that notification. Because these new processes appear before VO has a chance to register, it will often
315             // miss AXLoadComplete notifications. By moving them to the UIProcess, we can eliminate that issue.
316             skipSystemNotification = true;
317             break;
318         case AXInvalidStatusChanged:
319             macNotification = @"AXInvalidStatusChanged";
320             break;
321         case AXSelectedChildrenChanged:
322             if (is<AccessibilityTable>(*obj) && downcast<AccessibilityTable>(*obj).isExposableThroughAccessibility())
323                 macNotification = NSAccessibilitySelectedRowsChangedNotification;
324             else
325                 macNotification = NSAccessibilitySelectedChildrenChangedNotification;
326             break;
327         case AXSelectedTextChanged:
328             macNotification = NSAccessibilitySelectedTextChangedNotification;
329             break;
330         case AXCheckedStateChanged:
331         case AXValueChanged:
332             macNotification = NSAccessibilityValueChangedNotification;
333             break;
334         case AXLiveRegionCreated:
335             macNotification = NSAccessibilityLiveRegionCreatedNotification;
336             break;
337         case AXLiveRegionChanged:
338             macNotification = NSAccessibilityLiveRegionChangedNotification;
339             break;
340         case AXRowCountChanged:
341             macNotification = NSAccessibilityRowCountChangedNotification;
342             break;
343         case AXRowExpanded:
344             macNotification = NSAccessibilityRowExpandedNotification;
345             break;
346         case AXRowCollapsed:
347             macNotification = NSAccessibilityRowCollapsedNotification;
348             break;
349         case AXElementBusyChanged:
350             macNotification = @"AXElementBusyChanged";
351             break;
352         case AXExpandedChanged:
353             macNotification = @"AXExpandedChanged";
354             break;
355         case AXMenuClosed:
356             macNotification = (id)kAXMenuClosedNotification;
357             break;
358         case AXMenuListItemSelected:
359             macNotification = (id)kAXMenuItemSelectedNotification;
360             break;
361         case AXPressDidSucceed:
362             macNotification = @"AXPressDidSucceed";
363             break;
364         case AXPressDidFail:
365             macNotification = @"AXPressDidFail";
366             break;
367         case AXMenuOpened:
368             macNotification = (id)kAXMenuOpenedNotification;
369             break;
370         default:
371             return;
372     }
373     
374     // NSAccessibilityPostNotification will call this method, (but not when running DRT), so ASSERT here to make sure it does not crash.
375     // https://bugs.webkit.org/show_bug.cgi?id=46662
376     ALLOW_DEPRECATED_DECLARATIONS_BEGIN
377     ASSERT([obj->wrapper() accessibilityIsIgnored] || true);
378     ALLOW_DEPRECATED_DECLARATIONS_END
379
380     AXPostNotificationWithUserInfo(obj->wrapper(), macNotification, nil, skipSystemNotification);
381 }
382
383 void AXObjectCache::postTextStateChangePlatformNotification(AccessibilityObject* object, const AXTextStateChangeIntent& intent, const VisibleSelection& selection)
384 {
385     if (!object)
386         object = rootWebArea();
387
388     if (!object)
389         return;
390
391     NSMutableDictionary *userInfo = [[NSMutableDictionary alloc] initWithCapacity:5];
392     if (m_isSynchronizingSelection)
393         [userInfo setObject:@YES forKey:NSAccessibilityTextStateSyncKey];
394     if (intent.type != AXTextStateChangeTypeUnknown) {
395         [userInfo setObject:@(platformChangeTypeForWebCoreChangeType(intent.type)) forKey:NSAccessibilityTextStateChangeTypeKey];
396         switch (intent.type) {
397         case AXTextStateChangeTypeSelectionMove:
398         case AXTextStateChangeTypeSelectionExtend:
399         case AXTextStateChangeTypeSelectionBoundary:
400             [userInfo setObject:@(platformDirectionForWebCoreDirection(intent.selection.direction)) forKey:NSAccessibilityTextSelectionDirection];
401             switch (intent.selection.direction) {
402             case AXTextSelectionDirectionUnknown:
403                 break;
404             case AXTextSelectionDirectionBeginning:
405             case AXTextSelectionDirectionEnd:
406             case AXTextSelectionDirectionPrevious:
407             case AXTextSelectionDirectionNext:
408                 [userInfo setObject:@(platformGranularityForWebCoreGranularity(intent.selection.granularity)) forKey:NSAccessibilityTextSelectionGranularity];
409                 break;
410             case AXTextSelectionDirectionDiscontiguous:
411                 break;
412             }
413             if (intent.selection.focusChange)
414                 [userInfo setObject:@(intent.selection.focusChange) forKey:NSAccessibilityTextSelectionChangedFocus];
415             break;
416         case AXTextStateChangeTypeUnknown:
417         case AXTextStateChangeTypeEdit:
418             break;
419         }
420     }
421     if (!selection.isNone()) {
422         if (id textMarkerRange = [object->wrapper() textMarkerRangeFromVisiblePositions:selection.visibleStart() endPosition:selection.visibleEnd()])
423             [userInfo setObject:textMarkerRange forKey:NSAccessibilitySelectedTextMarkerRangeAttribute];
424     }
425
426     if (id wrapper = object->wrapper())
427         [userInfo setObject:wrapper forKey:NSAccessibilityTextChangeElement];
428
429     if (auto root = rootWebArea()) {
430         AXPostNotificationWithUserInfo(rootWebArea()->wrapper(), NSAccessibilitySelectedTextChangedNotification, userInfo);
431         if (root->wrapper() != object->wrapper())
432             AXPostNotificationWithUserInfo(object->wrapper(), NSAccessibilitySelectedTextChangedNotification, userInfo);
433     }
434
435     [userInfo release];
436 }
437
438 static void addTextMarkerFor(NSMutableDictionary* change, AccessibilityObject& object, const VisiblePosition& position)
439 {
440     if (position.isNull())
441         return;
442     if (id textMarker = [object.wrapper() textMarkerForVisiblePosition:position])
443         [change setObject:textMarker forKey:NSAccessibilityTextChangeValueStartMarker];
444 }
445
446 static void addTextMarkerFor(NSMutableDictionary* change, AccessibilityObject& object, HTMLTextFormControlElement& textControl)
447 {
448     if (id textMarker = [object.wrapper() textMarkerForFirstPositionInTextControl:textControl])
449         [change setObject:textMarker forKey:NSAccessibilityTextChangeValueStartMarker];
450 }
451
452 template <typename TextMarkerTargetType>
453 static NSDictionary *textReplacementChangeDictionary(AccessibilityObject& object, AXTextEditType type, const String& string, TextMarkerTargetType& markerTarget)
454 {
455     NSString *text = (NSString *)string;
456     NSUInteger length = [text length];
457     if (!length)
458         return nil;
459     NSMutableDictionary *change = [[NSMutableDictionary alloc] initWithCapacity:4];
460     [change setObject:@(platformEditTypeForWebCoreEditType(type)) forKey:NSAccessibilityTextEditType];
461     if (length > AXValueChangeTruncationLength) {
462         [change setObject:[NSNumber numberWithInt:length] forKey:NSAccessibilityTextChangeValueLength];
463         text = [text substringToIndex:AXValueChangeTruncationLength];
464     }
465     [change setObject:text forKey:NSAccessibilityTextChangeValue];
466     addTextMarkerFor(change, object, markerTarget);
467     return [change autorelease];
468 }
469
470 void AXObjectCache::postTextStateChangePlatformNotification(AccessibilityObject* object, AXTextEditType type, const String& text, const VisiblePosition& position)
471 {
472     if (!text.length())
473         return;
474
475     postTextReplacementPlatformNotification(object, AXTextEditTypeUnknown, emptyString(), type, text, position);
476 }
477
478 static void postUserInfoForChanges(AccessibilityObject& rootWebArea, AccessibilityObject& object, NSMutableArray* changes)
479 {
480     NSMutableDictionary *userInfo = [[NSMutableDictionary alloc] initWithCapacity:4];
481     [userInfo setObject:@(platformChangeTypeForWebCoreChangeType(AXTextStateChangeTypeEdit)) forKey:NSAccessibilityTextStateChangeTypeKey];
482     if (changes.count)
483         [userInfo setObject:changes forKey:NSAccessibilityTextChangeValues];
484
485     if (id wrapper = object.wrapper())
486         [userInfo setObject:wrapper forKey:NSAccessibilityTextChangeElement];
487
488     AXPostNotificationWithUserInfo(rootWebArea.wrapper(), NSAccessibilityValueChangedNotification, userInfo);
489     if (rootWebArea.wrapper() != object.wrapper())
490         AXPostNotificationWithUserInfo(object.wrapper(), NSAccessibilityValueChangedNotification, userInfo);
491
492     [userInfo release];
493 }
494
495 void AXObjectCache::postTextReplacementPlatformNotification(AccessibilityObject* object, AXTextEditType deletionType, const String& deletedText, AXTextEditType insertionType, const String& insertedText, const VisiblePosition& position)
496 {
497     if (!object)
498         object = rootWebArea();
499
500     if (!object)
501         return;
502
503     NSMutableArray *changes = [[NSMutableArray alloc] initWithCapacity:2];
504     if (NSDictionary *change = textReplacementChangeDictionary(*object, deletionType, deletedText, position))
505         [changes addObject:change];
506     if (NSDictionary *change = textReplacementChangeDictionary(*object, insertionType, insertedText, position))
507         [changes addObject:change];
508     postUserInfoForChanges(*rootWebArea(), *object, changes);
509     [changes release];
510 }
511
512 void AXObjectCache::postTextReplacementPlatformNotificationForTextControl(AccessibilityObject* object, const String& deletedText, const String& insertedText, HTMLTextFormControlElement& textControl)
513 {
514     if (!object)
515         object = rootWebArea();
516
517     if (!object)
518         return;
519
520     NSMutableArray *changes = [[NSMutableArray alloc] initWithCapacity:2];
521     if (NSDictionary *change = textReplacementChangeDictionary(*object, AXTextEditTypeDelete, deletedText, textControl))
522         [changes addObject:change];
523     if (NSDictionary *change = textReplacementChangeDictionary(*object, AXTextEditTypeInsert, insertedText, textControl))
524         [changes addObject:change];
525     postUserInfoForChanges(*rootWebArea(), *object, changes);
526     [changes release];
527 }
528
529 void AXObjectCache::frameLoadingEventPlatformNotification(AccessibilityObject* axFrameObject, AXLoadingEvent loadingEvent)
530 {
531     if (!axFrameObject)
532         return;
533     
534     if (loadingEvent == AXLoadingFinished && axFrameObject->document() == axFrameObject->topDocument())
535         postPlatformNotification(axFrameObject, AXLoadComplete);
536 }
537
538 void AXObjectCache::platformHandleFocusedUIElementChanged(Node*, Node*)
539 {
540     NSAccessibilityHandleFocusChanged();
541     // AXFocusChanged is a test specific notification name and not something a real AT will be listening for
542     if (UNLIKELY(axShouldRepostNotificationsForTests))
543         [rootWebArea()->wrapper() accessibilityPostedNotification:@"AXFocusChanged" userInfo:nil];
544 }
545
546 void AXObjectCache::handleScrolledToAnchor(const Node*)
547 {
548 }
549
550 void AXObjectCache::platformPerformDeferredCacheUpdate()
551 {
552 }
553
554 }
555
556 #endif // HAVE(ACCESSIBILITY) && PLATFORM(MAC)