c51b18534180dba34dd1f5deed57029cdd9741db
[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 ENABLE(ACCESSIBILITY) && PLATFORM(MAC)
30
31 #import "AXIsolatedObject.h"
32 #import "AccessibilityObject.h"
33 #import "AccessibilityTable.h"
34 #import "RenderObject.h"
35 #import "WebAccessibilityObjectWrapperMac.h"
36 #import <pal/spi/cocoa/NSAccessibilitySPI.h>
37 #import <pal/spi/mac/HIServicesSPI.h>
38
39 #if USE(APPLE_INTERNAL_SDK)
40 #include <ApplicationServices/ApplicationServicesPriv.h>
41 #endif
42
43 #ifndef NSAccessibilityLiveRegionChangedNotification
44 #define NSAccessibilityLiveRegionChangedNotification @"AXLiveRegionChanged"
45 #endif
46
47 #ifndef NSAccessibilityLiveRegionCreatedNotification 
48 #define NSAccessibilityLiveRegionCreatedNotification @"AXLiveRegionCreated"
49 #endif
50
51 #ifndef NSAccessibilityTextStateChangeTypeKey
52 #define NSAccessibilityTextStateChangeTypeKey @"AXTextStateChangeType"
53 #endif
54
55 #ifndef NSAccessibilityTextStateSyncKey
56 #define NSAccessibilityTextStateSyncKey @"AXTextStateSync"
57 #endif
58
59 #ifndef NSAccessibilityTextSelectionDirection
60 #define NSAccessibilityTextSelectionDirection @"AXTextSelectionDirection"
61 #endif
62
63 #ifndef NSAccessibilityTextSelectionGranularity
64 #define NSAccessibilityTextSelectionGranularity @"AXTextSelectionGranularity"
65 #endif
66
67 #ifndef NSAccessibilityTextSelectionChangedFocus
68 #define NSAccessibilityTextSelectionChangedFocus @"AXTextSelectionChangedFocus"
69 #endif
70
71 #ifndef NSAccessibilityTextEditType
72 #define NSAccessibilityTextEditType @"AXTextEditType"
73 #endif
74
75 #ifndef NSAccessibilityTextChangeValues
76 #define NSAccessibilityTextChangeValues @"AXTextChangeValues"
77 #endif
78
79 #ifndef NSAccessibilityTextChangeValue
80 #define NSAccessibilityTextChangeValue @"AXTextChangeValue"
81 #endif
82
83 #ifndef NSAccessibilityTextChangeValueLength
84 #define NSAccessibilityTextChangeValueLength @"AXTextChangeValueLength"
85 #endif
86
87 #ifndef NSAccessibilityTextChangeValueStartMarker
88 #define NSAccessibilityTextChangeValueStartMarker @"AXTextChangeValueStartMarker"
89 #endif
90
91 #ifndef NSAccessibilityTextChangeElement
92 #define NSAccessibilityTextChangeElement @"AXTextChangeElement"
93 #endif
94
95 #ifndef NSAccessibilitySelectedTextMarkerRangeAttribute
96 #define NSAccessibilitySelectedTextMarkerRangeAttribute @"AXSelectedTextMarkerRange"
97 #endif
98
99 // 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.
100 static const NSUInteger AXValueChangeTruncationLength = 1000;
101
102 // Check if platform provides enums for text change notifications
103 #ifndef AXTextStateChangeDefined
104 #define AXTextStateChangeDefined
105
106 typedef CF_ENUM(UInt32, AXTextStateChangeType)
107 {
108     kAXTextStateChangeTypeUnknown,
109     kAXTextStateChangeTypeEdit,
110     kAXTextStateChangeTypeSelectionMove,
111     kAXTextStateChangeTypeSelectionExtend,
112     kAXTextStateChangeTypeSelectionBoundary
113 };
114
115 typedef CF_ENUM(UInt32, AXTextEditType)
116 {
117     kAXTextEditTypeUnknown,
118     kAXTextEditTypeDelete,
119     kAXTextEditTypeInsert,
120     kAXTextEditTypeTyping,
121     kAXTextEditTypeDictation,
122     kAXTextEditTypeCut,
123     kAXTextEditTypePaste,
124     kAXTextEditTypeAttributesChange
125 };
126
127 typedef CF_ENUM(UInt32, AXTextSelectionDirection)
128 {
129     kAXTextSelectionDirectionUnknown = 0,
130     kAXTextSelectionDirectionBeginning,
131     kAXTextSelectionDirectionEnd,
132     kAXTextSelectionDirectionPrevious,
133     kAXTextSelectionDirectionNext,
134     kAXTextSelectionDirectionDiscontiguous
135 };
136
137 typedef CF_ENUM(UInt32, AXTextSelectionGranularity)
138 {
139     kAXTextSelectionGranularityUnknown,
140     kAXTextSelectionGranularityCharacter,
141     kAXTextSelectionGranularityWord,
142     kAXTextSelectionGranularityLine,
143     kAXTextSelectionGranularitySentence,
144     kAXTextSelectionGranularityParagraph,
145     kAXTextSelectionGranularityPage,
146     kAXTextSelectionGranularityDocument,
147     kAXTextSelectionGranularityAll
148 };
149
150 #endif // AXTextStateChangeDefined
151
152 static AXTextStateChangeType platformChangeTypeForWebCoreChangeType(WebCore::AXTextStateChangeType changeType)
153 {
154     switch (changeType) {
155     case WebCore::AXTextStateChangeTypeUnknown:
156         return kAXTextStateChangeTypeUnknown;
157     case WebCore::AXTextStateChangeTypeEdit:
158         return kAXTextStateChangeTypeEdit;
159     case WebCore::AXTextStateChangeTypeSelectionMove:
160         return kAXTextStateChangeTypeSelectionMove;
161     case WebCore::AXTextStateChangeTypeSelectionExtend:
162         return kAXTextStateChangeTypeSelectionExtend;
163     case WebCore::AXTextStateChangeTypeSelectionBoundary:
164         return kAXTextStateChangeTypeSelectionBoundary;
165     }
166 }
167
168 static AXTextEditType platformEditTypeForWebCoreEditType(WebCore::AXTextEditType changeType)
169 {
170     switch (changeType) {
171     case WebCore::AXTextEditTypeUnknown:
172         return kAXTextEditTypeUnknown;
173     case WebCore::AXTextEditTypeDelete:
174         return kAXTextEditTypeDelete;
175     case WebCore::AXTextEditTypeInsert:
176         return kAXTextEditTypeInsert;
177     case WebCore::AXTextEditTypeTyping:
178         return kAXTextEditTypeTyping;
179     case WebCore::AXTextEditTypeDictation:
180         return kAXTextEditTypeDictation;
181     case WebCore::AXTextEditTypeCut:
182         return kAXTextEditTypeCut;
183     case WebCore::AXTextEditTypePaste:
184         return kAXTextEditTypePaste;
185     case WebCore::AXTextEditTypeAttributesChange:
186         return kAXTextEditTypeAttributesChange;
187     }
188 }
189
190 static AXTextSelectionDirection platformDirectionForWebCoreDirection(WebCore::AXTextSelectionDirection direction)
191 {
192     switch (direction) {
193     case WebCore::AXTextSelectionDirectionUnknown:
194         return kAXTextSelectionDirectionUnknown;
195     case WebCore::AXTextSelectionDirectionBeginning:
196         return kAXTextSelectionDirectionBeginning;
197     case WebCore::AXTextSelectionDirectionEnd:
198         return kAXTextSelectionDirectionEnd;
199     case WebCore::AXTextSelectionDirectionPrevious:
200         return kAXTextSelectionDirectionPrevious;
201     case WebCore::AXTextSelectionDirectionNext:
202         return kAXTextSelectionDirectionNext;
203     case WebCore::AXTextSelectionDirectionDiscontiguous:
204         return kAXTextSelectionDirectionDiscontiguous;
205     }
206 }
207
208 static AXTextSelectionGranularity platformGranularityForWebCoreGranularity(WebCore::AXTextSelectionGranularity granularity)
209 {
210     switch (granularity) {
211     case WebCore::AXTextSelectionGranularityUnknown:
212         return kAXTextSelectionGranularityUnknown;
213     case WebCore::AXTextSelectionGranularityCharacter:
214         return kAXTextSelectionGranularityCharacter;
215     case WebCore::AXTextSelectionGranularityWord:
216         return kAXTextSelectionGranularityWord;
217     case WebCore::AXTextSelectionGranularityLine:
218         return kAXTextSelectionGranularityLine;
219     case WebCore::AXTextSelectionGranularitySentence:
220         return kAXTextSelectionGranularitySentence;
221     case WebCore::AXTextSelectionGranularityParagraph:
222         return kAXTextSelectionGranularityParagraph;
223     case WebCore::AXTextSelectionGranularityPage:
224         return kAXTextSelectionGranularityPage;
225     case WebCore::AXTextSelectionGranularityDocument:
226         return kAXTextSelectionGranularityDocument;
227     case WebCore::AXTextSelectionGranularityAll:
228         return kAXTextSelectionGranularityAll;
229     }
230 }
231
232 // The simple Cocoa calls in this file don't throw exceptions.
233
234 namespace WebCore {
235
236 void AXObjectCache::attachWrapper(AXCoreObject* obj)
237 {
238     RetainPtr<WebAccessibilityObjectWrapper> wrapper = adoptNS([[WebAccessibilityObjectWrapper alloc] initWithAccessibilityObject:obj]);
239     obj->setWrapper(wrapper.get());
240 }
241
242 static BOOL axShouldRepostNotificationsForTests = false;
243
244 void AXObjectCache::setShouldRepostNotificationsForTests(bool value)
245 {
246     axShouldRepostNotificationsForTests = value;
247 }
248
249 static void AXPostNotificationWithUserInfo(AccessibilityObjectWrapper *object, NSString *notification, id userInfo, bool skipSystemNotification = false)
250 {
251     if (id associatedPluginParent = [object associatedPluginParent])
252         object = associatedPluginParent;
253
254     // 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
255     if (UNLIKELY(axShouldRepostNotificationsForTests))
256         [object accessibilityPostedNotification:notification userInfo:userInfo];
257
258     if (skipSystemNotification)
259         return;
260
261     NSAccessibilityPostNotificationWithUserInfo(object, notification, userInfo);
262 }
263
264 void AXObjectCache::postPlatformNotification(AXCoreObject* obj, AXNotification notification)
265 {
266     if (!obj)
267         return;
268
269     bool skipSystemNotification = false;
270     // Some notifications are unique to Safari and do not have NSAccessibility equivalents.
271     NSString *macNotification;
272     switch (notification) {
273         case AXActiveDescendantChanged:
274             // An active descendant change for trees means a selected rows change.
275             if (obj->isTree() || obj->isTable())
276                 macNotification = NSAccessibilitySelectedRowsChangedNotification;
277             
278             // When a combobox uses active descendant, it means the selected item in its associated
279             // list has changed. In these cases we should use selected children changed, because
280             // we don't want the focus to change away from the combobox where the user is typing.
281             else if (obj->isComboBox() || obj->isList() || obj->isListBox())
282                 macNotification = NSAccessibilitySelectedChildrenChangedNotification;
283             else
284                 macNotification = NSAccessibilityFocusedUIElementChangedNotification;                
285             break;
286         case AXAutocorrectionOccured:
287             macNotification = @"AXAutocorrectionOccurred";
288             break;
289         case AXFocusedUIElementChanged:
290             macNotification = NSAccessibilityFocusedUIElementChangedNotification;
291             break;
292         case AXLayoutComplete:
293             macNotification = @"AXLayoutComplete";
294             break;
295         case AXLoadComplete:
296             macNotification = @"AXLoadComplete";
297             // Frame loading events are handled by the UIProcess on macOS to improve reliability.
298             // On macOS, before notifications are allowed by AppKit to be sent to clients, you need to have a client (e.g. VoiceOver)
299             // register for that notification. Because these new processes appear before VO has a chance to register, it will often
300             // miss AXLoadComplete notifications. By moving them to the UIProcess, we can eliminate that issue.
301             skipSystemNotification = true;
302             break;
303         case AXInvalidStatusChanged:
304             macNotification = @"AXInvalidStatusChanged";
305             break;
306         case AXSelectedChildrenChanged:
307             if (obj->isTable() && obj->isExposable())
308                 macNotification = NSAccessibilitySelectedRowsChangedNotification;
309             else
310                 macNotification = NSAccessibilitySelectedChildrenChangedNotification;
311             break;
312         case AXSelectedTextChanged:
313             macNotification = NSAccessibilitySelectedTextChangedNotification;
314             break;
315         case AXCheckedStateChanged:
316         case AXValueChanged:
317             macNotification = NSAccessibilityValueChangedNotification;
318             break;
319         case AXLiveRegionCreated:
320             macNotification = NSAccessibilityLiveRegionCreatedNotification;
321             break;
322         case AXLiveRegionChanged:
323             macNotification = NSAccessibilityLiveRegionChangedNotification;
324             break;
325         case AXRowCountChanged:
326             macNotification = NSAccessibilityRowCountChangedNotification;
327             break;
328         case AXRowExpanded:
329             macNotification = NSAccessibilityRowExpandedNotification;
330             break;
331         case AXRowCollapsed:
332             macNotification = NSAccessibilityRowCollapsedNotification;
333             break;
334         case AXElementBusyChanged:
335             macNotification = @"AXElementBusyChanged";
336             break;
337         case AXExpandedChanged:
338             macNotification = @"AXExpandedChanged";
339             break;
340         case AXMenuClosed:
341             macNotification = (id)kAXMenuClosedNotification;
342             break;
343         case AXMenuListItemSelected:
344             macNotification = (id)kAXMenuItemSelectedNotification;
345             break;
346         case AXPressDidSucceed:
347             macNotification = @"AXPressDidSucceed";
348             break;
349         case AXPressDidFail:
350             macNotification = @"AXPressDidFail";
351             break;
352         case AXMenuOpened:
353             macNotification = (id)kAXMenuOpenedNotification;
354             break;
355         default:
356             return;
357     }
358     
359     // NSAccessibilityPostNotification will call this method, (but not when running DRT), so ASSERT here to make sure it does not crash.
360     // https://bugs.webkit.org/show_bug.cgi?id=46662
361     ALLOW_DEPRECATED_DECLARATIONS_BEGIN
362     ASSERT([obj->wrapper() accessibilityIsIgnored] || true);
363     ALLOW_DEPRECATED_DECLARATIONS_END
364
365     AXPostNotificationWithUserInfo(obj->wrapper(), macNotification, nil, skipSystemNotification);
366 }
367
368 void AXObjectCache::postTextStateChangePlatformNotification(AXCoreObject* object, const AXTextStateChangeIntent& intent, const VisibleSelection& selection)
369 {
370     if (!object)
371         object = rootWebArea();
372
373     if (!object)
374         return;
375
376     NSMutableDictionary *userInfo = [[NSMutableDictionary alloc] initWithCapacity:5];
377     if (m_isSynchronizingSelection)
378         [userInfo setObject:@YES forKey:NSAccessibilityTextStateSyncKey];
379     if (intent.type != AXTextStateChangeTypeUnknown) {
380         [userInfo setObject:@(platformChangeTypeForWebCoreChangeType(intent.type)) forKey:NSAccessibilityTextStateChangeTypeKey];
381         switch (intent.type) {
382         case AXTextStateChangeTypeSelectionMove:
383         case AXTextStateChangeTypeSelectionExtend:
384         case AXTextStateChangeTypeSelectionBoundary:
385             [userInfo setObject:@(platformDirectionForWebCoreDirection(intent.selection.direction)) forKey:NSAccessibilityTextSelectionDirection];
386             switch (intent.selection.direction) {
387             case AXTextSelectionDirectionUnknown:
388                 break;
389             case AXTextSelectionDirectionBeginning:
390             case AXTextSelectionDirectionEnd:
391             case AXTextSelectionDirectionPrevious:
392             case AXTextSelectionDirectionNext:
393                 [userInfo setObject:@(platformGranularityForWebCoreGranularity(intent.selection.granularity)) forKey:NSAccessibilityTextSelectionGranularity];
394                 break;
395             case AXTextSelectionDirectionDiscontiguous:
396                 break;
397             }
398             if (intent.selection.focusChange)
399                 [userInfo setObject:@(intent.selection.focusChange) forKey:NSAccessibilityTextSelectionChangedFocus];
400             break;
401         case AXTextStateChangeTypeUnknown:
402         case AXTextStateChangeTypeEdit:
403             break;
404         }
405     }
406     if (!selection.isNone()) {
407         if (id textMarkerRange = textMarkerRangeFromVisiblePositions(this, selection.visibleStart(), selection.visibleEnd()))
408             [userInfo setObject:textMarkerRange forKey:NSAccessibilitySelectedTextMarkerRangeAttribute];
409     }
410
411     if (id wrapper = object->wrapper())
412         [userInfo setObject:wrapper forKey:NSAccessibilityTextChangeElement];
413
414     if (auto root = rootWebArea()) {
415         AXPostNotificationWithUserInfo(rootWebArea()->wrapper(), NSAccessibilitySelectedTextChangedNotification, userInfo);
416         if (root->wrapper() != object->wrapper())
417             AXPostNotificationWithUserInfo(object->wrapper(), NSAccessibilitySelectedTextChangedNotification, userInfo);
418     }
419
420     [userInfo release];
421 }
422
423 static void addTextMarkerFor(NSMutableDictionary* change, AXCoreObject& object, const VisiblePosition& position)
424 {
425     if (position.isNull())
426         return;
427     if (id textMarker = [object.wrapper() textMarkerForVisiblePosition:position])
428         [change setObject:textMarker forKey:NSAccessibilityTextChangeValueStartMarker];
429 }
430
431 static void addTextMarkerFor(NSMutableDictionary* change, AXCoreObject& object, HTMLTextFormControlElement& textControl)
432 {
433     if (id textMarker = [object.wrapper() textMarkerForFirstPositionInTextControl:textControl])
434         [change setObject:textMarker forKey:NSAccessibilityTextChangeValueStartMarker];
435 }
436
437 template <typename TextMarkerTargetType>
438 static NSDictionary *textReplacementChangeDictionary(AXCoreObject& object, AXTextEditType type, const String& string, TextMarkerTargetType& markerTarget)
439 {
440     NSString *text = (NSString *)string;
441     NSUInteger length = [text length];
442     if (!length)
443         return nil;
444     NSMutableDictionary *change = [[NSMutableDictionary alloc] initWithCapacity:4];
445     [change setObject:@(platformEditTypeForWebCoreEditType(type)) forKey:NSAccessibilityTextEditType];
446     if (length > AXValueChangeTruncationLength) {
447         [change setObject:[NSNumber numberWithInt:length] forKey:NSAccessibilityTextChangeValueLength];
448         text = [text substringToIndex:AXValueChangeTruncationLength];
449     }
450     [change setObject:text forKey:NSAccessibilityTextChangeValue];
451     addTextMarkerFor(change, object, markerTarget);
452     return [change autorelease];
453 }
454
455 void AXObjectCache::postTextStateChangePlatformNotification(AccessibilityObject* object, AXTextEditType type, const String& text, const VisiblePosition& position)
456 {
457     if (!text.length())
458         return;
459
460     postTextReplacementPlatformNotification(object, AXTextEditTypeUnknown, emptyString(), type, text, position);
461 }
462
463 static void postUserInfoForChanges(AXCoreObject& rootWebArea, AXCoreObject& object, NSMutableArray* changes)
464 {
465     NSMutableDictionary *userInfo = [[NSMutableDictionary alloc] initWithCapacity:4];
466     [userInfo setObject:@(platformChangeTypeForWebCoreChangeType(AXTextStateChangeTypeEdit)) forKey:NSAccessibilityTextStateChangeTypeKey];
467     if (changes.count)
468         [userInfo setObject:changes forKey:NSAccessibilityTextChangeValues];
469
470     if (id wrapper = object.wrapper())
471         [userInfo setObject:wrapper forKey:NSAccessibilityTextChangeElement];
472
473     AXPostNotificationWithUserInfo(rootWebArea.wrapper(), NSAccessibilityValueChangedNotification, userInfo);
474     if (rootWebArea.wrapper() != object.wrapper())
475         AXPostNotificationWithUserInfo(object.wrapper(), NSAccessibilityValueChangedNotification, userInfo);
476
477     [userInfo release];
478 }
479
480 void AXObjectCache::postTextReplacementPlatformNotification(AXCoreObject* object, AXTextEditType deletionType, const String& deletedText, AXTextEditType insertionType, const String& insertedText, const VisiblePosition& position)
481 {
482     if (!object)
483         object = rootWebArea();
484
485     if (!object)
486         return;
487
488     NSMutableArray *changes = [[NSMutableArray alloc] initWithCapacity:2];
489     if (NSDictionary *change = textReplacementChangeDictionary(*object, deletionType, deletedText, position))
490         [changes addObject:change];
491     if (NSDictionary *change = textReplacementChangeDictionary(*object, insertionType, insertedText, position))
492         [changes addObject:change];
493     postUserInfoForChanges(*rootWebArea(), *object, changes);
494     [changes release];
495 }
496
497 void AXObjectCache::postTextReplacementPlatformNotificationForTextControl(AXCoreObject* object, const String& deletedText, const String& insertedText, HTMLTextFormControlElement& textControl)
498 {
499     if (!object)
500         object = rootWebArea();
501
502     if (!object)
503         return;
504
505     NSMutableArray *changes = [[NSMutableArray alloc] initWithCapacity:2];
506     if (NSDictionary *change = textReplacementChangeDictionary(*object, AXTextEditTypeDelete, deletedText, textControl))
507         [changes addObject:change];
508     if (NSDictionary *change = textReplacementChangeDictionary(*object, AXTextEditTypeInsert, insertedText, textControl))
509         [changes addObject:change];
510     postUserInfoForChanges(*rootWebArea(), *object, changes);
511     [changes release];
512 }
513
514 void AXObjectCache::frameLoadingEventPlatformNotification(AccessibilityObject* axFrameObject, AXLoadingEvent loadingEvent)
515 {
516     if (!axFrameObject)
517         return;
518     
519     if (loadingEvent == AXLoadingFinished && axFrameObject->document() == axFrameObject->topDocument())
520         postPlatformNotification(axFrameObject, AXLoadComplete);
521 }
522
523 void AXObjectCache::platformHandleFocusedUIElementChanged(Node*, Node*)
524 {
525     NSAccessibilityHandleFocusChanged();
526     // AXFocusChanged is a test specific notification name and not something a real AT will be listening for
527     if (UNLIKELY(!axShouldRepostNotificationsForTests))
528         return;
529
530     auto* rootWebArea = this->rootWebArea();
531     if (!rootWebArea)
532         return;
533
534     [rootWebArea->wrapper() accessibilityPostedNotification:@"AXFocusChanged" userInfo:nil];
535 }
536
537 void AXObjectCache::handleScrolledToAnchor(const Node*)
538 {
539 }
540
541 void AXObjectCache::platformPerformDeferredCacheUpdate()
542 {
543 }
544
545 // TextMarker utility functions.
546
547 static id AXTextMarkerRange(id startMarker, id endMarker)
548 {
549     ASSERT(startMarker);
550     ASSERT(endMarker);
551     ASSERT(CFGetTypeID((__bridge CFTypeRef)startMarker) == AXTextMarkerGetTypeID());
552     ASSERT(CFGetTypeID((__bridge CFTypeRef)endMarker) == AXTextMarkerGetTypeID());
553     return CFBridgingRelease(AXTextMarkerRangeCreate(kCFAllocatorDefault, (AXTextMarkerRef)startMarker, (AXTextMarkerRef)endMarker));
554 }
555
556 id textMarkerRangeFromMarkers(id textMarker1, id textMarker2)
557 {
558     if (!textMarker1 || !textMarker2)
559         return nil;
560
561     return AXTextMarkerRange(textMarker1, textMarker2);
562 }
563
564 id textMarkerForVisiblePosition(AXObjectCache* cache, const VisiblePosition& visiblePos)
565 {
566     ASSERT(cache);
567     if (!cache)
568         return nil;
569
570     auto textMarkerData = cache->textMarkerDataForVisiblePosition(visiblePos);
571     if (!textMarkerData)
572         return nil;
573
574     return CFBridgingRelease(AXTextMarkerCreate(kCFAllocatorDefault, (const UInt8*)&textMarkerData.value(), sizeof(textMarkerData.value())));
575 }
576
577 id textMarkerRangeFromVisiblePositions(AXObjectCache* cache, const VisiblePosition& startPosition, const VisiblePosition& endPosition)
578 {
579     if (!cache)
580         return nil;
581
582     id startTextMarker = textMarkerForVisiblePosition(cache, startPosition);
583     id endTextMarker = textMarkerForVisiblePosition(cache, endPosition);
584     return textMarkerRangeFromMarkers(startTextMarker, endTextMarker);
585 }
586
587 }
588
589 #endif // ENABLE(ACCESSIBILITY) && PLATFORM(MAC)