Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(350)

Side by Side Diff: Source/core/accessibility/AXObjectCacheImpl.cpp

Issue 713933002: Create Source/modules/accessibility/ and move most of core/accessibility/* into it (Closed) Base URL: https://chromium.googlesource.com/chromium/blink.git@master
Patch Set: rebase Created 6 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 /*
2 * Copyright (C) 2014, Google 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 *
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14 * its contributors may be used to endorse or promote products derived
15 * from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include "config.h"
30
31 #include "core/accessibility/AXObjectCacheImpl.h"
32
33 #include "core/HTMLNames.h"
34 #include "core/accessibility/AXARIAGrid.h"
35 #include "core/accessibility/AXARIAGridCell.h"
36 #include "core/accessibility/AXARIAGridRow.h"
37 #include "core/accessibility/AXImageMapLink.h"
38 #include "core/accessibility/AXInlineTextBox.h"
39 #include "core/accessibility/AXList.h"
40 #include "core/accessibility/AXListBox.h"
41 #include "core/accessibility/AXListBoxOption.h"
42 #include "core/accessibility/AXMediaControls.h"
43 #include "core/accessibility/AXMenuList.h"
44 #include "core/accessibility/AXMenuListOption.h"
45 #include "core/accessibility/AXMenuListPopup.h"
46 #include "core/accessibility/AXProgressIndicator.h"
47 #include "core/accessibility/AXRenderObject.h"
48 #include "core/accessibility/AXSVGRoot.h"
49 #include "core/accessibility/AXScrollView.h"
50 #include "core/accessibility/AXScrollbar.h"
51 #include "core/accessibility/AXSlider.h"
52 #include "core/accessibility/AXSpinButton.h"
53 #include "core/accessibility/AXTable.h"
54 #include "core/accessibility/AXTableCell.h"
55 #include "core/accessibility/AXTableColumn.h"
56 #include "core/accessibility/AXTableHeaderContainer.h"
57 #include "core/accessibility/AXTableRow.h"
58 #include "core/dom/Document.h"
59 #include "core/frame/FrameView.h"
60 #include "core/frame/LocalFrame.h"
61 #include "core/frame/Settings.h"
62 #include "core/html/HTMLAreaElement.h"
63 #include "core/html/HTMLImageElement.h"
64 #include "core/html/HTMLInputElement.h"
65 #include "core/html/HTMLLabelElement.h"
66 #include "core/page/Chrome.h"
67 #include "core/page/ChromeClient.h"
68 #include "core/page/FocusController.h"
69 #include "core/page/Page.h"
70 #include "core/rendering/AbstractInlineTextBox.h"
71 #include "core/rendering/RenderListBox.h"
72 #include "core/rendering/RenderMenuList.h"
73 #include "core/rendering/RenderProgress.h"
74 #include "core/rendering/RenderSlider.h"
75 #include "core/rendering/RenderTable.h"
76 #include "core/rendering/RenderTableCell.h"
77 #include "core/rendering/RenderTableRow.h"
78 #include "core/rendering/RenderView.h"
79 #include "wtf/PassRefPtr.h"
80
81 namespace blink {
82
83 using namespace HTMLNames;
84
85 // static
86 AXObjectCache* AXObjectCache::create(Document& document)
87 {
88 return new AXObjectCacheImpl(document);
89 }
90
91 AXObjectCacheImpl::AXObjectCacheImpl(Document& document)
92 : m_document(document)
93 , m_modificationCount(0)
94 , m_notificationPostTimer(this, &AXObjectCacheImpl::notificationPostTimerFir ed)
95 {
96 }
97
98 AXObjectCacheImpl::~AXObjectCacheImpl()
99 {
100 m_notificationPostTimer.stop();
101
102 HashMap<AXID, RefPtr<AXObject> >::iterator end = m_objects.end();
103 for (HashMap<AXID, RefPtr<AXObject> >::iterator it = m_objects.begin(); it ! = end; ++it) {
104 AXObject* obj = (*it).value.get();
105 detachWrapper(obj);
106 obj->detach();
107 removeAXID(obj);
108 }
109 }
110
111 AXObject* AXObjectCacheImpl::getOrCreateAXObjectFromRenderView(RenderView* rende rView)
112 {
113 return getOrCreate(renderView);
114 }
115
116 AXObject* AXObjectCacheImpl::root()
117 {
118 return getOrCreate(&m_document);
119 }
120
121 AXObject* AXObjectCacheImpl::focusedImageMapUIElement(HTMLAreaElement* areaEleme nt)
122 {
123 // Find the corresponding accessibility object for the HTMLAreaElement. This should be
124 // in the list of children for its corresponding image.
125 if (!areaElement)
126 return 0;
127
128 HTMLImageElement* imageElement = areaElement->imageElement();
129 if (!imageElement)
130 return 0;
131
132 AXObject* axRenderImage = toAXObjectCacheImpl(areaElement->document().axObje ctCache())->getOrCreate(imageElement);
133 if (!axRenderImage)
134 return 0;
135
136 AXObject::AccessibilityChildrenVector imageChildren = axRenderImage->childre n();
137 unsigned count = imageChildren.size();
138 for (unsigned k = 0; k < count; ++k) {
139 AXObject* child = imageChildren[k].get();
140 if (!child->isImageMapLink())
141 continue;
142
143 if (toAXImageMapLink(child)->areaElement() == areaElement)
144 return child;
145 }
146
147 return 0;
148 }
149
150 AXObject* AXObjectCacheImpl::focusedUIElementForPage(const Page* page)
151 {
152 if (!page->settings().accessibilityEnabled())
153 return 0;
154
155 // Cross-process accessibility is not yet implemented.
156 if (!page->focusController().focusedOrMainFrame()->isLocalFrame())
157 return 0;
158
159 // get the focused node in the page
160 Document* focusedDocument = toLocalFrame(page->focusController().focusedOrMa inFrame())->document();
161 Node* focusedNode = focusedDocument->focusedElement();
162 if (!focusedNode)
163 focusedNode = focusedDocument;
164
165 if (isHTMLAreaElement(*focusedNode))
166 return focusedImageMapUIElement(toHTMLAreaElement(focusedNode));
167
168 AXObject* obj = toAXObjectCacheImpl(focusedNode->document().axObjectCache()) ->getOrCreate(focusedNode);
169 if (!obj)
170 return 0;
171
172 if (obj->shouldFocusActiveDescendant()) {
173 if (AXObject* descendant = obj->activeDescendant())
174 obj = descendant;
175 }
176
177 // the HTML element, for example, is focusable but has an AX object that is ignored
178 if (obj->accessibilityIsIgnored())
179 obj = obj->parentObjectUnignored();
180
181 return obj;
182 }
183
184 AXObject* AXObjectCacheImpl::get(Widget* widget)
185 {
186 if (!widget)
187 return 0;
188
189 AXID axID = m_widgetObjectMapping.get(widget);
190 ASSERT(!HashTraits<AXID>::isDeletedValue(axID));
191 if (!axID)
192 return 0;
193
194 return m_objects.get(axID);
195 }
196
197 AXObject* AXObjectCacheImpl::get(RenderObject* renderer)
198 {
199 if (!renderer)
200 return 0;
201
202 AXID axID = m_renderObjectMapping.get(renderer);
203 ASSERT(!HashTraits<AXID>::isDeletedValue(axID));
204 if (!axID)
205 return 0;
206
207 return m_objects.get(axID);
208 }
209
210 AXObject* AXObjectCacheImpl::get(Node* node)
211 {
212 if (!node)
213 return 0;
214
215 AXID renderID = node->renderer() ? m_renderObjectMapping.get(node->renderer( )) : 0;
216 ASSERT(!HashTraits<AXID>::isDeletedValue(renderID));
217
218 AXID nodeID = m_nodeObjectMapping.get(node);
219 ASSERT(!HashTraits<AXID>::isDeletedValue(nodeID));
220
221 if (node->renderer() && nodeID && !renderID) {
222 // This can happen if an AXNodeObject is created for a node that's not
223 // rendered, but later something changes and it gets a renderer (like if it's
224 // reparented).
225 remove(nodeID);
226 return 0;
227 }
228
229 if (renderID)
230 return m_objects.get(renderID);
231
232 if (!nodeID)
233 return 0;
234
235 return m_objects.get(nodeID);
236 }
237
238 AXObject* AXObjectCacheImpl::get(AbstractInlineTextBox* inlineTextBox)
239 {
240 if (!inlineTextBox)
241 return 0;
242
243 AXID axID = m_inlineTextBoxObjectMapping.get(inlineTextBox);
244 ASSERT(!HashTraits<AXID>::isDeletedValue(axID));
245 if (!axID)
246 return 0;
247
248 return m_objects.get(axID);
249 }
250
251 // FIXME: This probably belongs on Node.
252 // FIXME: This should take a const char*, but one caller passes nullAtom.
253 bool nodeHasRole(Node* node, const String& role)
254 {
255 if (!node || !node->isElementNode())
256 return false;
257
258 return equalIgnoringCase(toElement(node)->getAttribute(roleAttr), role);
259 }
260
261 static PassRefPtr<AXObject> createFromRenderer(RenderObject* renderer)
262 {
263 // FIXME: How could renderer->node() ever not be an Element?
264 Node* node = renderer->node();
265
266 // If the node is aria role="list" or the aria role is empty and its a
267 // ul/ol/dl type (it shouldn't be a list if aria says otherwise).
268 if (node && ((nodeHasRole(node, "list") || nodeHasRole(node, "directory"))
269 || (nodeHasRole(node, nullAtom) && (isHTMLUListElement(*node) || isHTMLO ListElement(*node) || isHTMLDListElement(*node)))))
270 return AXList::create(renderer);
271
272 // aria tables
273 if (nodeHasRole(node, "grid") || nodeHasRole(node, "treegrid"))
274 return AXARIAGrid::create(renderer);
275 if (nodeHasRole(node, "row"))
276 return AXARIAGridRow::create(renderer);
277 if (nodeHasRole(node, "gridcell") || nodeHasRole(node, "columnheader") || no deHasRole(node, "rowheader"))
278 return AXARIAGridCell::create(renderer);
279
280 // media controls
281 if (node && node->isMediaControlElement())
282 return AccessibilityMediaControl::create(renderer);
283
284 if (isHTMLOptionElement(node))
285 return AXListBoxOption::create(renderer);
286
287 if (renderer->isSVGRoot())
288 return AXSVGRoot::create(renderer);
289
290 if (renderer->isBoxModelObject()) {
291 RenderBoxModelObject* cssBox = toRenderBoxModelObject(renderer);
292 if (cssBox->isListBox())
293 return AXListBox::create(toRenderListBox(cssBox));
294 if (cssBox->isMenuList())
295 return AXMenuList::create(toRenderMenuList(cssBox));
296
297 // standard tables
298 if (cssBox->isTable())
299 return AXTable::create(toRenderTable(cssBox));
300 if (cssBox->isTableRow())
301 return AXTableRow::create(toRenderTableRow(cssBox));
302 if (cssBox->isTableCell())
303 return AXTableCell::create(toRenderTableCell(cssBox));
304
305 // progress bar
306 if (cssBox->isProgress())
307 return AXProgressIndicator::create(toRenderProgress(cssBox));
308
309 // input type=range
310 if (cssBox->isSlider())
311 return AXSlider::create(toRenderSlider(cssBox));
312 }
313
314 return AXRenderObject::create(renderer);
315 }
316
317 static PassRefPtr<AXObject> createFromNode(Node* node)
318 {
319 return AXNodeObject::create(node);
320 }
321
322 static PassRefPtr<AXObject> createFromInlineTextBox(AbstractInlineTextBox* inlin eTextBox)
323 {
324 return AXInlineTextBox::create(inlineTextBox);
325 }
326
327 AXObject* AXObjectCacheImpl::getOrCreate(Widget* widget)
328 {
329 if (!widget)
330 return 0;
331
332 if (AXObject* obj = get(widget))
333 return obj;
334
335 RefPtr<AXObject> newObj = nullptr;
336 if (widget->isFrameView())
337 newObj = AXScrollView::create(toFrameView(widget));
338 else if (widget->isScrollbar())
339 newObj = AXScrollbar::create(toScrollbar(widget));
340
341 // Will crash later if we have two objects for the same widget.
342 ASSERT(!get(widget));
343
344 // Catch the case if an (unsupported) widget type is used. Only FrameView an d ScrollBar are supported now.
345 ASSERT(newObj);
346 if (!newObj)
347 return 0;
348
349 getAXID(newObj.get());
350
351 m_widgetObjectMapping.set(widget, newObj->axObjectID());
352 m_objects.set(newObj->axObjectID(), newObj);
353 newObj->init();
354 attachWrapper(newObj.get());
355 return newObj.get();
356 }
357
358 AXObject* AXObjectCacheImpl::getOrCreate(Node* node)
359 {
360 if (!node)
361 return 0;
362
363 if (AXObject* obj = get(node))
364 return obj;
365
366 if (node->renderer())
367 return getOrCreate(node->renderer());
368
369 if (!node->parentElement())
370 return 0;
371
372 // It's only allowed to create an AXObject from a Node if it's in a canvas s ubtree.
373 // Or if it's a hidden element, but we still want to expose it because of ot her ARIA attributes.
374 bool inCanvasSubtree = node->parentElement()->isInCanvasSubtree();
375 bool isHidden = !node->renderer() && isNodeAriaVisible(node);
376 if (!inCanvasSubtree && !isHidden)
377 return 0;
378
379 RefPtr<AXObject> newObj = createFromNode(node);
380
381 // Will crash later if we have two objects for the same node.
382 ASSERT(!get(node));
383
384 getAXID(newObj.get());
385
386 m_nodeObjectMapping.set(node, newObj->axObjectID());
387 m_objects.set(newObj->axObjectID(), newObj);
388 newObj->init();
389 attachWrapper(newObj.get());
390 newObj->setLastKnownIsIgnoredValue(newObj->accessibilityIsIgnored());
391
392 return newObj.get();
393 }
394
395 AXObject* AXObjectCacheImpl::getOrCreate(RenderObject* renderer)
396 {
397 if (!renderer)
398 return 0;
399
400 if (AXObject* obj = get(renderer))
401 return obj;
402
403 RefPtr<AXObject> newObj = createFromRenderer(renderer);
404
405 // Will crash later if we have two objects for the same renderer.
406 ASSERT(!get(renderer));
407
408 getAXID(newObj.get());
409
410 m_renderObjectMapping.set(renderer, newObj->axObjectID());
411 m_objects.set(newObj->axObjectID(), newObj);
412 newObj->init();
413 attachWrapper(newObj.get());
414 newObj->setLastKnownIsIgnoredValue(newObj->accessibilityIsIgnored());
415
416 return newObj.get();
417 }
418
419 AXObject* AXObjectCacheImpl::getOrCreate(AbstractInlineTextBox* inlineTextBox)
420 {
421 if (!inlineTextBox)
422 return 0;
423
424 if (AXObject* obj = get(inlineTextBox))
425 return obj;
426
427 RefPtr<AXObject> newObj = createFromInlineTextBox(inlineTextBox);
428
429 // Will crash later if we have two objects for the same inlineTextBox.
430 ASSERT(!get(inlineTextBox));
431
432 getAXID(newObj.get());
433
434 m_inlineTextBoxObjectMapping.set(inlineTextBox, newObj->axObjectID());
435 m_objects.set(newObj->axObjectID(), newObj);
436 newObj->init();
437 attachWrapper(newObj.get());
438 newObj->setLastKnownIsIgnoredValue(newObj->accessibilityIsIgnored());
439
440 return newObj.get();
441 }
442
443 AXObject* AXObjectCacheImpl::rootObject()
444 {
445 if (!accessibilityEnabled())
446 return 0;
447
448 return getOrCreate(m_document.view());
449 }
450
451 AXObject* AXObjectCacheImpl::getOrCreate(AccessibilityRole role)
452 {
453 RefPtr<AXObject> obj = nullptr;
454
455 // will be filled in...
456 switch (role) {
457 case ImageMapLinkRole:
458 obj = AXImageMapLink::create();
459 break;
460 case ColumnRole:
461 obj = AXTableColumn::create();
462 break;
463 case TableHeaderContainerRole:
464 obj = AXTableHeaderContainer::create();
465 break;
466 case SliderThumbRole:
467 obj = AXSliderThumb::create();
468 break;
469 case MenuListPopupRole:
470 obj = AXMenuListPopup::create();
471 break;
472 case MenuListOptionRole:
473 obj = AXMenuListOption::create();
474 break;
475 case SpinButtonRole:
476 obj = AXSpinButton::create();
477 break;
478 case SpinButtonPartRole:
479 obj = AXSpinButtonPart::create();
480 break;
481 default:
482 obj = nullptr;
483 }
484
485 if (obj)
486 getAXID(obj.get());
487 else
488 return 0;
489
490 m_objects.set(obj->axObjectID(), obj);
491 obj->init();
492 attachWrapper(obj.get());
493 return obj.get();
494 }
495
496 void AXObjectCacheImpl::remove(AXID axID)
497 {
498 if (!axID)
499 return;
500
501 // first fetch object to operate some cleanup functions on it
502 AXObject* obj = m_objects.get(axID);
503 if (!obj)
504 return;
505
506 detachWrapper(obj);
507 obj->detach();
508 removeAXID(obj);
509
510 // finally remove the object
511 if (!m_objects.take(axID))
512 return;
513
514 ASSERT(m_objects.size() >= m_idsInUse.size());
515 }
516
517 void AXObjectCacheImpl::remove(RenderObject* renderer)
518 {
519 if (!renderer)
520 return;
521
522 AXID axID = m_renderObjectMapping.get(renderer);
523 remove(axID);
524 m_renderObjectMapping.remove(renderer);
525 }
526
527 void AXObjectCacheImpl::remove(Node* node)
528 {
529 if (!node)
530 return;
531
532 removeNodeForUse(node);
533
534 // This is all safe even if we didn't have a mapping.
535 AXID axID = m_nodeObjectMapping.get(node);
536 remove(axID);
537 m_nodeObjectMapping.remove(node);
538
539 if (node->renderer()) {
540 remove(node->renderer());
541 return;
542 }
543 }
544
545 void AXObjectCacheImpl::remove(Widget* view)
546 {
547 if (!view)
548 return;
549
550 AXID axID = m_widgetObjectMapping.get(view);
551 remove(axID);
552 m_widgetObjectMapping.remove(view);
553 }
554
555 void AXObjectCacheImpl::remove(AbstractInlineTextBox* inlineTextBox)
556 {
557 if (!inlineTextBox)
558 return;
559
560 AXID axID = m_inlineTextBoxObjectMapping.get(inlineTextBox);
561 remove(axID);
562 m_inlineTextBoxObjectMapping.remove(inlineTextBox);
563 }
564
565 // FIXME: Oilpan: Use a weak hashmap for this instead.
566 void AXObjectCacheImpl::clearWeakMembers(Visitor* visitor)
567 {
568 Vector<Node*> deadNodes;
569 for (HashMap<Node*, AXID>::iterator it = m_nodeObjectMapping.begin(); it != m_nodeObjectMapping.end(); ++it) {
570 if (!visitor->isAlive(it->key))
571 deadNodes.append(it->key);
572 }
573 for (unsigned i = 0; i < deadNodes.size(); ++i)
574 remove(deadNodes[i]);
575
576 Vector<Widget*> deadWidgets;
577 for (HashMap<Widget*, AXID>::iterator it = m_widgetObjectMapping.begin();
578 it != m_widgetObjectMapping.end(); ++it) {
579 if (!visitor->isAlive(it->key))
580 deadWidgets.append(it->key);
581 }
582 for (unsigned i = 0; i < deadWidgets.size(); ++i)
583 remove(deadWidgets[i]);
584 }
585
586 AXID AXObjectCacheImpl::platformGenerateAXID() const
587 {
588 static AXID lastUsedID = 0;
589
590 // Generate a new ID.
591 AXID objID = lastUsedID;
592 do {
593 ++objID;
594 } while (!objID || HashTraits<AXID>::isDeletedValue(objID) || m_idsInUse.con tains(objID));
595
596 lastUsedID = objID;
597
598 return objID;
599 }
600
601 AXID AXObjectCacheImpl::getAXID(AXObject* obj)
602 {
603 // check for already-assigned ID
604 AXID objID = obj->axObjectID();
605 if (objID) {
606 ASSERT(m_idsInUse.contains(objID));
607 return objID;
608 }
609
610 objID = platformGenerateAXID();
611
612 m_idsInUse.add(objID);
613 obj->setAXObjectID(objID);
614
615 return objID;
616 }
617
618 void AXObjectCacheImpl::removeAXID(AXObject* object)
619 {
620 if (!object)
621 return;
622
623 AXID objID = object->axObjectID();
624 if (!objID)
625 return;
626 ASSERT(!HashTraits<AXID>::isDeletedValue(objID));
627 ASSERT(m_idsInUse.contains(objID));
628 object->setAXObjectID(0);
629 m_idsInUse.remove(objID);
630 }
631
632 void AXObjectCacheImpl::selectionChanged(Node* node)
633 {
634 // Find the nearest ancestor that already has an accessibility object, since we
635 // might be in the middle of a layout.
636 while (node) {
637 if (AXObject* obj = get(node)) {
638 obj->selectionChanged();
639 return;
640 }
641 node = node->parentNode();
642 }
643 }
644
645 void AXObjectCacheImpl::textChanged(Node* node)
646 {
647 textChanged(getOrCreate(node));
648 }
649
650 void AXObjectCacheImpl::textChanged(RenderObject* renderer)
651 {
652 textChanged(getOrCreate(renderer));
653 }
654
655 void AXObjectCacheImpl::textChanged(AXObject* obj)
656 {
657 if (!obj)
658 return;
659
660 bool parentAlreadyExists = obj->parentObjectIfExists();
661 obj->textChanged();
662 postNotification(obj, obj->document(), AXObjectCacheImpl::AXTextChanged, tru e);
663 if (parentAlreadyExists)
664 obj->notifyIfIgnoredValueChanged();
665 }
666
667 void AXObjectCacheImpl::updateCacheAfterNodeIsAttached(Node* node)
668 {
669 // Calling get() will update the AX object if we had an AXNodeObject but now we need
670 // an AXRenderObject, because it was reparented to a location outside of a c anvas.
671 get(node);
672 }
673
674 void AXObjectCacheImpl::childrenChanged(Node* node)
675 {
676 childrenChanged(get(node));
677 }
678
679 void AXObjectCacheImpl::childrenChanged(RenderObject* renderer)
680 {
681 childrenChanged(get(renderer));
682 }
683
684 void AXObjectCacheImpl::childrenChanged(Widget* widget)
685 {
686 childrenChanged(get(widget));
687 }
688
689 void AXObjectCacheImpl::childrenChanged(AXObject* obj)
690 {
691 if (!obj)
692 return;
693
694 obj->childrenChanged();
695 }
696
697 void AXObjectCacheImpl::notificationPostTimerFired(Timer<AXObjectCacheImpl>*)
698 {
699 RefPtrWillBeRawPtr<Document> protectorForCacheOwner(m_document);
700
701 m_notificationPostTimer.stop();
702
703 unsigned i = 0, count = m_notificationsToPost.size();
704 for (i = 0; i < count; ++i) {
705 AXObject* obj = m_notificationsToPost[i].first.get();
706 if (!obj->axObjectID())
707 continue;
708
709 if (!obj->axObjectCache())
710 continue;
711
712 #if ENABLE(ASSERT)
713 // Make sure none of the render views are in the process of being layed out.
714 // Notifications should only be sent after the renderer has finished
715 if (obj->isAXRenderObject()) {
716 AXRenderObject* renderObj = toAXRenderObject(obj);
717 RenderObject* renderer = renderObj->renderer();
718 if (renderer && renderer->view())
719 ASSERT(!renderer->view()->layoutState());
720 }
721 #endif
722
723 AXNotification notification = m_notificationsToPost[i].second;
724 postPlatformNotification(obj, notification);
725
726 if (notification == AXChildrenChanged && obj->parentObjectIfExists() && obj->lastKnownIsIgnoredValue() != obj->accessibilityIsIgnored())
727 childrenChanged(obj->parentObject());
728 }
729
730 m_notificationsToPost.clear();
731 }
732
733 void AXObjectCacheImpl::postNotification(RenderObject* renderer, AXNotification notification, bool postToElement)
734 {
735 if (!renderer)
736 return;
737
738 m_modificationCount++;
739
740 // Get an accessibility object that already exists. One should not be create d here
741 // because a render update may be in progress and creating an AX object can re-trigger a layout
742 RefPtr<AXObject> object = get(renderer);
743 while (!object && renderer) {
744 renderer = renderer->parent();
745 object = get(renderer);
746 }
747
748 if (!renderer)
749 return;
750
751 postNotification(object.get(), &renderer->document(), notification, postToEl ement);
752 }
753
754 void AXObjectCacheImpl::postNotification(Node* node, AXNotification notification , bool postToElement)
755 {
756 if (!node)
757 return;
758
759 m_modificationCount++;
760
761 // Get an accessibility object that already exists. One should not be create d here
762 // because a render update may be in progress and creating an AX object can re-trigger a layout
763 RefPtr<AXObject> object = get(node);
764 while (!object && node) {
765 node = node->parentNode();
766 object = get(node);
767 }
768
769 if (!node)
770 return;
771
772 postNotification(object.get(), &node->document(), notification, postToElemen t);
773 }
774
775 void AXObjectCacheImpl::postNotification(AXObject* object, Document* document, A XNotification notification, bool postToElement)
776 {
777 m_modificationCount++;
778
779 if (object && !postToElement)
780 object = object->observableObject();
781
782 if (!object && document)
783 object = get(document->renderView());
784
785 if (!object)
786 return;
787
788 m_notificationsToPost.append(std::make_pair(object, notification));
789 if (!m_notificationPostTimer.isActive())
790 m_notificationPostTimer.startOneShot(0, FROM_HERE);
791 }
792
793 void AXObjectCacheImpl::checkedStateChanged(Node* node)
794 {
795 postNotification(node, AXObjectCacheImpl::AXCheckedStateChanged, true);
796 }
797
798 void AXObjectCacheImpl::selectedChildrenChanged(Node* node)
799 {
800 // postToElement is false so that you can pass in any child of an element an d it will go up the parent tree
801 // to find the container which should send out the notification.
802 postNotification(node, AXSelectedChildrenChanged, false);
803 }
804
805 void AXObjectCacheImpl::selectedChildrenChanged(RenderObject* renderer)
806 {
807 // postToElement is false so that you can pass in any child of an element an d it will go up the parent tree
808 // to find the container which should send out the notification.
809 postNotification(renderer, AXSelectedChildrenChanged, false);
810 }
811
812 void AXObjectCacheImpl::handleScrollbarUpdate(FrameView* view)
813 {
814 if (!view)
815 return;
816
817 // We don't want to create a scroll view from this method, only update an ex isting one.
818 if (AXObject* scrollViewObject = get(view)) {
819 m_modificationCount++;
820 scrollViewObject->updateChildrenIfNecessary();
821 }
822 }
823
824 void AXObjectCacheImpl::handleLayoutComplete(RenderObject* renderer)
825 {
826 if (!renderer)
827 return;
828
829 m_modificationCount++;
830
831 // Create the AXObject if it didn't yet exist - that's always safe at the en d of a layout, and it
832 // allows an AX notification to be sent when a page has its first layout, ra ther than when the
833 // document first loads.
834 if (AXObject* obj = getOrCreate(renderer))
835 postNotification(obj, obj->document(), AXLayoutComplete, true);
836 }
837
838 void AXObjectCacheImpl::handleAriaExpandedChange(Node* node)
839 {
840 if (AXObject* obj = getOrCreate(node))
841 obj->handleAriaExpandedChanged();
842 }
843
844 void AXObjectCacheImpl::handleActiveDescendantChanged(Node* node)
845 {
846 if (AXObject* obj = getOrCreate(node))
847 obj->handleActiveDescendantChanged();
848 }
849
850 void AXObjectCacheImpl::handleAriaRoleChanged(Node* node)
851 {
852 if (AXObject* obj = getOrCreate(node)) {
853 obj->updateAccessibilityRole();
854 m_modificationCount++;
855 obj->notifyIfIgnoredValueChanged();
856 }
857 }
858
859 void AXObjectCacheImpl::handleAttributeChanged(const QualifiedName& attrName, El ement* element)
860 {
861 if (attrName == roleAttr)
862 handleAriaRoleChanged(element);
863 else if (attrName == altAttr || attrName == titleAttr)
864 textChanged(element);
865 else if (attrName == forAttr && isHTMLLabelElement(*element))
866 labelChanged(element);
867
868 if (!attrName.localName().startsWith("aria-"))
869 return;
870
871 if (attrName == aria_activedescendantAttr)
872 handleActiveDescendantChanged(element);
873 else if (attrName == aria_valuenowAttr || attrName == aria_valuetextAttr)
874 postNotification(element, AXObjectCacheImpl::AXValueChanged, true);
875 else if (attrName == aria_labelAttr || attrName == aria_labeledbyAttr || att rName == aria_labelledbyAttr)
876 textChanged(element);
877 else if (attrName == aria_checkedAttr)
878 checkedStateChanged(element);
879 else if (attrName == aria_selectedAttr)
880 selectedChildrenChanged(element);
881 else if (attrName == aria_expandedAttr)
882 handleAriaExpandedChange(element);
883 else if (attrName == aria_hiddenAttr)
884 childrenChanged(element->parentNode());
885 else if (attrName == aria_invalidAttr)
886 postNotification(element, AXObjectCacheImpl::AXInvalidStatusChanged, tru e);
887 else
888 postNotification(element, AXObjectCacheImpl::AXAriaAttributeChanged, tru e);
889 }
890
891 void AXObjectCacheImpl::labelChanged(Element* element)
892 {
893 textChanged(toHTMLLabelElement(element)->control());
894 }
895
896 void AXObjectCacheImpl::recomputeIsIgnored(RenderObject* renderer)
897 {
898 if (AXObject* obj = get(renderer))
899 obj->notifyIfIgnoredValueChanged();
900 }
901
902 void AXObjectCacheImpl::inlineTextBoxesUpdated(RenderObject* renderer)
903 {
904 if (!inlineTextBoxAccessibilityEnabled())
905 return;
906
907 // Only update if the accessibility object already exists and it's
908 // not already marked as dirty.
909 if (AXObject* obj = get(renderer)) {
910 if (!obj->needsToUpdateChildren()) {
911 obj->setNeedsToUpdateChildren();
912 postNotification(renderer, AXChildrenChanged, true);
913 }
914 }
915 }
916
917 Settings* AXObjectCacheImpl::settings()
918 {
919 return m_document.settings();
920 }
921
922 bool AXObjectCacheImpl::accessibilityEnabled()
923 {
924 Settings* settings = this->settings();
925 if (!settings)
926 return false;
927 return settings->accessibilityEnabled();
928 }
929
930 bool AXObjectCacheImpl::inlineTextBoxAccessibilityEnabled()
931 {
932 Settings* settings = this->settings();
933 if (!settings)
934 return false;
935 return settings->inlineTextBoxAccessibilityEnabled();
936 }
937
938 const Element* AXObjectCacheImpl::rootAXEditableElement(const Node* node)
939 {
940 const Element* result = node->rootEditableElement();
941 const Element* element = node->isElementNode() ? toElement(node) : node->par entElement();
942
943 for (; element; element = element->parentElement()) {
944 if (nodeIsTextControl(element))
945 result = element;
946 }
947
948 return result;
949 }
950
951 bool AXObjectCacheImpl::nodeIsTextControl(const Node* node)
952 {
953 if (!node)
954 return false;
955
956 const AXObject* axObject = getOrCreate(const_cast<Node*>(node));
957 return axObject && axObject->isTextControl();
958 }
959
960 bool isNodeAriaVisible(Node* node)
961 {
962 if (!node)
963 return false;
964
965 if (!node->isElementNode())
966 return false;
967
968 return equalIgnoringCase(toElement(node)->getAttribute(aria_hiddenAttr), "fa lse");
969 }
970
971 void AXObjectCacheImpl::detachWrapper(AXObject* obj)
972 {
973 // In Chromium, AXObjects are not wrapped.
974 }
975
976 void AXObjectCacheImpl::attachWrapper(AXObject*)
977 {
978 // In Chromium, AXObjects are not wrapped.
979 }
980
981 void AXObjectCacheImpl::postPlatformNotification(AXObject* obj, AXNotification n otification)
982 {
983 if (obj && obj->isAXScrollbar() && notification == AXValueChanged) {
984 // Send document value changed on scrollbar value changed notification.
985 Scrollbar* scrollBar = toAXScrollbar(obj)->scrollbar();
986 if (!scrollBar || !scrollBar->parent() || !scrollBar->parent()->isFrameV iew())
987 return;
988 Document* document = toFrameView(scrollBar->parent())->frame().document( );
989 if (document != document->topDocument())
990 return;
991 obj = get(document->renderView());
992 }
993
994 if (!obj || !obj->document() || !obj->documentFrameView() || !obj->documentF rameView()->frame().page())
995 return;
996
997 ChromeClient& client = obj->document()->axObjectCacheOwner().page()->chrome( ).client();
998
999 if (notification == AXActiveDescendantChanged
1000 && obj->document()->focusedElement()
1001 && obj->node() == obj->document()->focusedElement()) {
1002 // Calling handleFocusedUIElementChanged will focus the new active
1003 // descendant and send the AXFocusedUIElementChanged notification.
1004 handleFocusedUIElementChanged(0, obj->document()->focusedElement());
1005 }
1006
1007 client.postAccessibilityNotification(obj, notification);
1008 }
1009
1010 void AXObjectCacheImpl::handleFocusedUIElementChanged(Node*, Node* newFocusedNod e)
1011 {
1012 if (!newFocusedNode)
1013 return;
1014
1015 Page* page = newFocusedNode->document().page();
1016 if (!page)
1017 return;
1018
1019 AXObject* focusedObject = focusedUIElementForPage(page);
1020 if (!focusedObject)
1021 return;
1022
1023 postPlatformNotification(focusedObject, AXFocusedUIElementChanged);
1024 }
1025
1026 void AXObjectCacheImpl::handleInitialFocus()
1027 {
1028 postNotification(&m_document, AXObjectCache::AXFocusedUIElementChanged, true );
1029 }
1030
1031 void AXObjectCacheImpl::handleEditableTextContentChanged(Node* node)
1032 {
1033 postNotification(node, AXObjectCache::AXValueChanged, false);
1034 }
1035
1036 void AXObjectCacheImpl::handleTextFormControlChanged(Node* node)
1037 {
1038 postNotification(node, AXObjectCache::AXValueChanged, false);
1039 }
1040
1041 void AXObjectCacheImpl::handleValueChanged(Node* node)
1042 {
1043 postNotification(node, AXObjectCache::AXValueChanged, true);
1044 }
1045
1046 void AXObjectCacheImpl::handleUpdateActiveMenuOption(RenderMenuList* menuList, i nt optionIndex)
1047 {
1048 if (AXMenuList* axMenuList = static_cast<AXMenuList*>(get(menuList)))
1049 axMenuList->didUpdateActiveOption(optionIndex);
1050 }
1051
1052 void AXObjectCacheImpl::handleLoadComplete(Document* document)
1053 {
1054 postNotification(getOrCreate(document), document, AXObjectCache::AXLoadCompl ete, true);
1055 }
1056
1057 void AXObjectCacheImpl::handleLayoutComplete(Document* document)
1058 {
1059 postNotification(getOrCreate(document), document, AXObjectCache::AXLayoutCom plete, true);
1060 }
1061
1062 void AXObjectCacheImpl::handleScrolledToAnchor(const Node* anchorNode)
1063 {
1064 // The anchor node may not be accessible. Post the notification for the
1065 // first accessible object.
1066 postPlatformNotification(AXObject::firstAccessibleObjectFromNode(anchorNode) , AXScrolledToAnchor);
1067 }
1068
1069 void AXObjectCacheImpl::handleScrollPositionChanged(FrameView* frameView)
1070 {
1071 // Prefer to fire the scroll position changed event on the frame view's chil d web area, if possible.
1072 AXObject* targetAXObject = getOrCreate(frameView);
1073 if (targetAXObject && !targetAXObject->children().isEmpty())
1074 targetAXObject = targetAXObject->children()[0].get();
1075 postPlatformNotification(targetAXObject, AXScrollPositionChanged);
1076 }
1077
1078 void AXObjectCacheImpl::handleScrollPositionChanged(RenderObject* renderObject)
1079 {
1080 postPlatformNotification(getOrCreate(renderObject), AXScrollPositionChanged) ;
1081 }
1082
1083 void AXObjectCacheImpl::setCanvasObjectBounds(Element* element, const LayoutRect & rect)
1084 {
1085 AXObject* obj = getOrCreate(element);
1086 if (!obj)
1087 return;
1088
1089 obj->setElementRect(rect);
1090 }
1091
1092 } // namespace blink
OLDNEW
« no previous file with comments | « Source/core/accessibility/AXObjectCacheImpl.h ('k') | Source/core/accessibility/AXProgressIndicator.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698