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

Side by Side Diff: Source/modules/accessibility/InspectorAccessibilityAgent.cpp

Issue 1076453004: Show reasons why nodes are ignored in accessibility sidebar (Closed) Base URL: https://chromium.googlesource.com/chromium/blink.git@master
Patch Set: Created 5 years, 8 months 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
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "config.h" 5 #include "config.h"
6 6
7 #include "modules/accessibility/InspectorAccessibilityAgent.h" 7 #include "modules/accessibility/InspectorAccessibilityAgent.h"
8 8
9 #include "core/HTMLNames.h"
9 #include "core/dom/AXObjectCache.h" 10 #include "core/dom/AXObjectCache.h"
10 #include "core/dom/DOMNodeIds.h" 11 #include "core/dom/DOMNodeIds.h"
11 #include "core/dom/Element.h" 12 #include "core/dom/Element.h"
13 #include "core/dom/ElementTraversal.h"
14 #include "core/html/HTMLDialogElement.h"
15 #include "core/html/HTMLFrameOwnerElement.h"
16 #include "core/html/HTMLLabelElement.h"
12 #include "core/inspector/InspectorDOMAgent.h" 17 #include "core/inspector/InspectorDOMAgent.h"
13 #include "core/inspector/InspectorState.h" 18 #include "core/inspector/InspectorState.h"
14 #include "core/inspector/InspectorStyleSheet.h" 19 #include "core/inspector/InspectorStyleSheet.h"
20 #include "core/layout/LayoutBlockFlow.h"
21 #include "core/layout/LayoutBoxModelObject.h"
15 #include "core/page/Page.h" 22 #include "core/page/Page.h"
16 #include "modules/accessibility/AXObject.h" 23 #include "modules/accessibility/AXObject.h"
17 #include "modules/accessibility/AXObjectCacheImpl.h" 24 #include "modules/accessibility/AXObjectCacheImpl.h"
18 #include "platform/JSONValues.h" 25 #include "platform/JSONValues.h"
19 26
20 namespace blink { 27 namespace blink {
21 28
29 using HTMLNames::altAttr;
30 using HTMLNames::aria_describedbyAttr;
31 using HTMLNames::aria_helpAttr;
32 using HTMLNames::titleAttr;
22 using TypeBuilder::Accessibility::AXGlobalStates; 33 using TypeBuilder::Accessibility::AXGlobalStates;
34 using TypeBuilder::Accessibility::AXIgnoredNode;
35 using TypeBuilder::Accessibility::AXIgnoredReasons;
23 using TypeBuilder::Accessibility::AXLiveRegionAttributes; 36 using TypeBuilder::Accessibility::AXLiveRegionAttributes;
24 using TypeBuilder::Accessibility::AXNode; 37 using TypeBuilder::Accessibility::AXNode;
25 using TypeBuilder::Accessibility::AXNodeId; 38 using TypeBuilder::Accessibility::AXNodeId;
26 using TypeBuilder::Accessibility::AXProperty; 39 using TypeBuilder::Accessibility::AXProperty;
27 using TypeBuilder::Accessibility::AXValueType; 40 using TypeBuilder::Accessibility::AXValueType;
28 using TypeBuilder::Accessibility::AXRelatedNode; 41 using TypeBuilder::Accessibility::AXRelatedNode;
29 using TypeBuilder::Accessibility::AXRelationshipAttributes; 42 using TypeBuilder::Accessibility::AXRelationshipAttributes;
30 using TypeBuilder::Accessibility::AXValue; 43 using TypeBuilder::Accessibility::AXValue;
31 using TypeBuilder::Accessibility::AXWidgetAttributes; 44 using TypeBuilder::Accessibility::AXWidgetAttributes;
32 using TypeBuilder::Accessibility::AXWidgetStates; 45 using TypeBuilder::Accessibility::AXWidgetStates;
33 46
34 namespace { 47 namespace {
35 48
36 PassRefPtr<AXProperty> createProperty(String name, PassRefPtr<AXValue> value) 49 PassRefPtr<AXProperty> createProperty(String name, PassRefPtr<AXValue> value)
37 { 50 {
38 RefPtr<AXProperty> property = AXProperty::create().setName(name).setValue(va lue); 51 RefPtr<AXProperty> property = AXProperty::create().setName(name).setValue(va lue);
39 return property; 52 return property;
40 } 53 }
41 54
42 PassRefPtr<AXProperty> createProperty(AXGlobalStates::Enum name, PassRefPtr<AXVa lue> value) 55 PassRefPtr<AXProperty> createProperty(AXGlobalStates::Enum name, PassRefPtr<AXVa lue> value)
43 { 56 {
44 return createProperty(TypeBuilder::getEnumConstantValue(name), value); 57 return createProperty(TypeBuilder::getEnumConstantValue(name), value);
45 } 58 }
46 59
47 PassRefPtr<AXProperty> createProperty(AXLiveRegionAttributes::Enum name, PassRef Ptr<AXValue> value) 60 PassRefPtr<AXProperty> createProperty(AXLiveRegionAttributes::Enum name, PassRef Ptr<AXValue> value)
48 { 61 {
49 return createProperty(TypeBuilder::getEnumConstantValue(name), value); 62 return createProperty(TypeBuilder::getEnumConstantValue(name), value);
50 } 63 }
51 64
52
53 PassRefPtr<AXProperty> createProperty(AXRelationshipAttributes::Enum name, PassR efPtr<AXValue> value) 65 PassRefPtr<AXProperty> createProperty(AXRelationshipAttributes::Enum name, PassR efPtr<AXValue> value)
54 { 66 {
55 return createProperty(TypeBuilder::getEnumConstantValue(name), value); 67 return createProperty(TypeBuilder::getEnumConstantValue(name), value);
56 } 68 }
57 69
58 PassRefPtr<AXProperty> createProperty(AXWidgetAttributes::Enum name, PassRefPtr< AXValue> value) 70 PassRefPtr<AXProperty> createProperty(AXWidgetAttributes::Enum name, PassRefPtr< AXValue> value)
59 { 71 {
60 return createProperty(TypeBuilder::getEnumConstantValue(name), value); 72 return createProperty(TypeBuilder::getEnumConstantValue(name), value);
61 } 73 }
62 74
63 PassRefPtr<AXProperty> createProperty(AXWidgetStates::Enum name, PassRefPtr<AXVa lue> value) 75 PassRefPtr<AXProperty> createProperty(AXWidgetStates::Enum name, PassRefPtr<AXVa lue> value)
64 { 76 {
65 return createProperty(TypeBuilder::getEnumConstantValue(name), value); 77 return createProperty(TypeBuilder::getEnumConstantValue(name), value);
66 } 78 }
67 79
80 PassRefPtr<AXProperty> createProperty(AXIgnoredReasons::Enum name, PassRefPtr<AX Value> value)
81 {
82 return createProperty(TypeBuilder::getEnumConstantValue(name), value);
83 }
68 84
69 PassRefPtr<AXValue> createValue(String value, AXValueType::Enum type = AXValueTy pe::String) 85 PassRefPtr<AXValue> createValue(String value, AXValueType::Enum type = AXValueTy pe::String)
70 { 86 {
71 RefPtr<AXValue> axValue = AXValue::create().setType(type); 87 RefPtr<AXValue> axValue = AXValue::create().setType(type);
72 axValue->setValue(JSONString::create(value)); 88 axValue->setValue(JSONString::create(value));
73 return axValue; 89 return axValue;
74 } 90 }
75 91
76 PassRefPtr<AXValue> createValue(int value, AXValueType::Enum type = AXValueType: :Integer) 92 PassRefPtr<AXValue> createValue(int value, AXValueType::Enum type = AXValueType: :Integer)
77 { 93 {
(...skipping 296 matching lines...) Expand 10 before | Expand all | Expand 10 after
374 if (!results.isEmpty()) 390 if (!results.isEmpty())
375 properties->addItem(createProperty(AXRelationshipAttributes::Labelledby, createRelatedNodeListValue(results))); 391 properties->addItem(createProperty(AXRelationshipAttributes::Labelledby, createRelatedNodeListValue(results)));
376 results.clear(); 392 results.clear();
377 393
378 axObject->ariaOwnsElements(results); 394 axObject->ariaOwnsElements(results);
379 if (!results.isEmpty()) 395 if (!results.isEmpty())
380 properties->addItem(createProperty(AXRelationshipAttributes::Owns, creat eRelatedNodeListValue(results))); 396 properties->addItem(createProperty(AXRelationshipAttributes::Owns, creat eRelatedNodeListValue(results)));
381 results.clear(); 397 results.clear();
382 } 398 }
383 399
384 PassRefPtr<AXNode> buildObjectForNode(Node* node, AXObject* axObject, AXObjectCa cheImpl* cacheImpl, PassRefPtr<TypeBuilder::Array<AXProperty>> properties) 400 PassRefPtr<AXValue> createRoleNameValue(AccessibilityRole role)
385 { 401 {
386 AccessibilityRole role = axObject->roleValue();
387 AtomicString roleName = AXObject::roleName(role); 402 AtomicString roleName = AXObject::roleName(role);
388 RefPtr<AXValue> roleNameValue; 403 RefPtr<AXValue> roleNameValue;
389 if (!roleName.isNull()) { 404 if (!roleName.isNull()) {
390 roleNameValue = createValue(roleName, AXValueType::Role); 405 roleNameValue = createValue(roleName, AXValueType::Role);
391 } else { 406 } else {
392 roleNameValue = createValue(AXObject::internalRoleName(role), AXValueTyp e::InternalRole); 407 roleNameValue = createValue(AXObject::internalRoleName(role), AXValueTyp e::InternalRole);
393 } 408 }
394 RefPtr<AXNode> nodeObject = AXNode::create().setNodeId(String::number(axObje ct->axObjectID())).setRole(roleNameValue).setProperties(properties); 409 return roleNameValue;
410 }
411
412 PassRefPtr<TypeBuilder::Array<AXProperty>> buildIgnoredReasons(Node* node, AXObj ect* axObject, AXObjectCacheImpl* cacheImpl)
dmazzoni 2015/04/09 05:46:10 Should this be in AXLayoutObject? It feels like we
aboxhall 2015/04/09 20:15:36 Yeah, I considered that. It would certainly be cle
413 {
414 RefPtr<TypeBuilder::Array<AXProperty>> ignoredReasons = TypeBuilder::Array<A XProperty>::create();
415 // hidden
416 const AXObject* hiddenRoot = axObject->ariaHiddenRoot();
417 if (hiddenRoot) {
418 if (hiddenRoot == axObject)
419 ignoredReasons->addItem(createProperty(AXIgnoredReasons::AriaHidden, createBooleanValue(true)));
420 else
421 ignoredReasons->addItem(createProperty(AXIgnoredReasons::AriaHiddenR oot, createRelatedNodeValue(hiddenRoot)));
422 return ignoredReasons;
423 }
424
425 // inert
426 if (node->isInert()) {
427 HTMLDialogElement* activeModalDialog = node->document().activeModalDialo g();
428 while (!activeModalDialog) {
429 HTMLFrameOwnerElement* ownerElement = node->document().ownerElement( );
430 if (!ownerElement)
431 break;
432 if (ownerElement && ownerElement->isInert())
433 activeModalDialog = ownerElement->document().activeModalDialog() ;
434 }
435
436 if (activeModalDialog) {
437 AXObject* dialogObject = cacheImpl->getOrCreate(activeModalDialog);
438 if (dialogObject) {
439 ignoredReasons->addItem(createProperty(AXIgnoredReasons::ActiveM odalDialog, createRelatedNodeValue(dialogObject)));
440 return ignoredReasons;
441 }
442 }
443 }
444
445 // ancestor can't have children (incl menuitem/menubutton parent)
446 if (axObject->isDescendantOfBarrenParent()) {
447 AXObject* parent = axObject->parentObject();
448 while (parent && parent->canHaveChildren())
449 parent = parent->parentObject();
450 if (parent) {
451 ignoredReasons->addItem(createProperty(AXIgnoredReasons::AncestorDis allowsChild, createRelatedNodeValue(parent)));
452 return ignoredReasons;
453 }
454 }
455
456 // role == ignored || role == none || role == presentational
457 AccessibilityRole role = axObject->roleValue();
458 if (role == IgnoredRole || role == NoneRole || role == PresentationalRole) {
459 ignoredReasons->addItem(createProperty(AXIgnoredReasons::IgnoredRole, cr eateRoleNameValue(role)));
460 return ignoredReasons;
461 }
462
463 // inherits presentational role
464 if (axObject->hasInheritedPresentationalRole()) {
465 AXObject* parent = axObject->parentObject();
466 while (parent && !parent->isPresentational())
467 parent = parent->parentObject();
468 if (parent) {
469 ignoredReasons->addItem(createProperty(AXIgnoredReasons::InheritsPre sentation, createRelatedNodeValue(parent)));
470 ignoredReasons->addItem(createProperty(AXIgnoredReasons::Focusable, createBooleanValue(node->isElementNode() && toElement(node)->supportsFocus())));
471 return ignoredReasons;
472 }
473 }
474
475 // disallowed child of ARIA tree
476 AXObject* potentialTree = axObject->parentObject();
477 while (potentialTree && !potentialTree->isTree())
478 potentialTree = potentialTree->parentObject();
479 if (potentialTree && role != TreeItemRole && role != StaticTextRole) {
480 ignoredReasons->addItem(createProperty(AXIgnoredReasons::AncestorDisallo wsChild, createRelatedNodeValue(potentialTree)));
481 return ignoredReasons;
482 }
483
484 if (LayoutObject* layoutObject = axObject->layoutObject()) {
485 // Popup menu
486 for (LayoutObject* parent = layoutObject->parent(); parent; parent = par ent->parent()) {
487 if (parent->isBoxModelObject() && toLayoutBoxModelObject(parent)->is MenuList()) {
488 AXObject* parentAXObject = cacheImpl->getOrCreate(parent);
489 if (parentAXObject) {
490 ignoredReasons->addItem(createProperty(AXIgnoredReasons::Anc estorDisallowsChild, createRelatedNodeValue(parentAXObject)));
491 return ignoredReasons;
492 }
493 }
494 }
495
496 // static text inside MenuItem/MenuButton
497 if (layoutObject->isText()) {
498 AXObject* parent = axObject->parentObjectUnignored();
499 if (parent && (parent->ariaRoleAttribute() == MenuItemRadioRole || p arent->ariaRoleAttribute() == MenuButtonRole)) {
500 ignoredReasons->addItem(createProperty(AXIgnoredReasons::Ancesto rDisallowsChild, createRelatedNodeValue(parent)));
501 return ignoredReasons;
502 }
503 }
504
505 // Empty/whitespace only layoutText
506 LayoutText* layoutText = toLayoutText(layoutObject);
507 if (!layoutText->firstTextBox() || layoutText->text().impl()->containsOn lyWhitespace()) {
508 ignoredReasons->addItem(createProperty(AXIgnoredReasons::EmptyText, createBooleanValue(true)));
509 return ignoredReasons;
510 }
511
512 // Unfocusable layoutBlock
513 if (layoutObject->isLayoutBlockFlow() && layoutObject->childrenInline() && !axObject->canSetFocusAttribute() && (!toLayoutBlockFlow(layoutObject)->first LineBox())) {
514 ignoredReasons->addItem(createProperty(AXIgnoredReasons::Focusable, createBooleanValue(false)));
dmazzoni 2015/04/09 05:46:10 "not focusable" seems to be a strange reason to me
aboxhall 2015/04/09 20:15:36 As you say, it's more of a heuristic than a reason
515 return ignoredReasons;
516 }
517 }
518
519 // is [part of] label for checkbox or radio
520 if (!axObject->isControl() && !node->isLink()) {
521 HTMLLabelElement* label = Traversal<HTMLLabelElement>::firstAncestorOrSe lf(*node);
522 if (label) {
523 if (!label->isSameNode(node)) {
524 AXObject* labelAXObject = cacheImpl->getOrCreate(label);
525 ignoredReasons->addItem(createProperty(AXIgnoredReasons::LabelCo ntainer, createRelatedNodeValue(labelAXObject)));
526 return ignoredReasons;
527 }
528
529 HTMLElement* correspondingControl = label->control();
530 if (correspondingControl) {
531 AXObject* controlAXObject = cacheImpl->getOrCreate(corresponding Control);
532 if (controlAXObject) {
533 ignoredReasons->addItem(createProperty(AXIgnoredReasons::Lab elFor, createRelatedNodeValue(controlAXObject)));
534 return ignoredReasons;
535 }
536 }
537 }
538 }
539
540 // is <span>
541 if (isHTMLSpanElement(node)) {
542 Element* element = toElement(node);
543 ignoredReasons->addItem(createProperty(AXIgnoredReasons::IgnoredTagName, createValue(element->tagName())));
544 return ignoredReasons;
545 }
546
547 if (axObject->isImage()) {
548 if (!axObject->canSetFocusAttribute()) {
549 // is image with zero alt text
550 Element* element = toElement(node);
551 const AtomicString& alt = element->getAttribute(altAttr);
552 if (alt.string().containsOnlyWhitespace() && !alt.isNull()) {
553 ignoredReasons->addItem(createProperty(AXIgnoredReasons::Focusab le, createBooleanValue(false)));
554
555 ignoredReasons->addItem(createProperty(AXIgnoredReasons::Alt, cr eateValue(alt.string())));
556 return ignoredReasons;
557 }
558
559 // TODO(aboxhall): is spacer image
560 }
561 }
562
563 // is canvas with no fallback
564 if (axObject->isCanvas()) {
565 if (!ElementTraversal::firstChild(*node)) {
566 ignoredReasons->addItem(createProperty(AXIgnoredReasons::NoFallbackC ontent, createBooleanValue(true)));
567 return ignoredReasons;
568 }
569 }
570
571 // is other and has no text alternative
572 if (axObject->getAttribute(aria_helpAttr).isEmpty() && axObject->getAttribut e(aria_describedbyAttr).isEmpty() && axObject->getAttribute(altAttr).isEmpty() & & axObject->getAttribute(titleAttr).isEmpty() && axObject->accessibilityDescript ion().isEmpty()) {
573 ignoredReasons->addItem(createProperty(AXIgnoredReasons::NoTextAlternati ve, createBooleanValue(true)));
574 return ignoredReasons;
575 }
576 return ignoredReasons;
577 }
578
579 PassRefPtr<AXNode> buildObjectForIgnoredNode(Node* node, AXObject* axObject, AXO bjectCacheImpl* cacheImpl)
580 {
581 RefPtr<TypeBuilder::Array<AXProperty>> ignoredReasons = buildIgnoredReasons( node, axObject, cacheImpl);
582 RefPtr<AXNode> ignoredNodeObject = AXNode::create().setNodeId(String::numbe r(axObject->axObjectID())).setIgnored(true);
583 ignoredNodeObject->setIgnoredReasons(ignoredReasons);
584 return ignoredNodeObject;
585 }
586
587 PassRefPtr<AXNode> buildObjectForNode(Node* node, AXObject* axObject, AXObjectCa cheImpl* cacheImpl, PassRefPtr<TypeBuilder::Array<AXProperty>> properties)
588 {
589 AccessibilityRole role = axObject->roleValue();
590 RefPtr<AXNode> nodeObject = AXNode::create().setNodeId(String::number(axObje ct->axObjectID())).setIgnored(false);
591 nodeObject->setRole(createRoleNameValue(role));
592 nodeObject->setProperties(properties);
395 String computedName = cacheImpl->computedNameForNode(node); 593 String computedName = cacheImpl->computedNameForNode(node);
396 if (!computedName.isEmpty()) 594 if (!computedName.isEmpty())
397 nodeObject->setName(createValue(computedName)); 595 nodeObject->setName(createValue(computedName));
398 596
399 fillCoreProperties(axObject, nodeObject); 597 fillCoreProperties(axObject, nodeObject);
400 return nodeObject; 598 return nodeObject;
401 } 599 }
402 600
403 } // namespace 601 } // namespace
404 602
(...skipping 19 matching lines...) Expand all
424 Node* node = domAgent->assertNode(errorString, nodeId); 622 Node* node = domAgent->assertNode(errorString, nodeId);
425 if (!node) 623 if (!node)
426 return; 624 return;
427 Document& document = node->document(); 625 Document& document = node->document();
428 ScopedAXObjectCache cache(document); 626 ScopedAXObjectCache cache(document);
429 AXObjectCacheImpl* cacheImpl = toAXObjectCacheImpl(cache.get()); 627 AXObjectCacheImpl* cacheImpl = toAXObjectCacheImpl(cache.get());
430 AXObject* axObject = cacheImpl->getOrCreate(node); 628 AXObject* axObject = cacheImpl->getOrCreate(node);
431 if (!axObject) 629 if (!axObject)
432 return; 630 return;
433 631
632 if (axObject->accessibilityIsIgnored()) {
633 accessibilityNode = buildObjectForIgnoredNode(node, axObject, cacheImpl) ;
634 return;
635 }
636
434 RefPtr<TypeBuilder::Array<AXProperty>> properties = TypeBuilder::Array<AXPro perty>::create(); 637 RefPtr<TypeBuilder::Array<AXProperty>> properties = TypeBuilder::Array<AXPro perty>::create();
435 fillLiveRegionProperties(axObject, properties); 638 fillLiveRegionProperties(axObject, properties);
436 fillGlobalStates(axObject, properties); 639 fillGlobalStates(axObject, properties);
437 fillWidgetProperties(axObject, properties); 640 fillWidgetProperties(axObject, properties);
438 fillWidgetStates(axObject, properties); 641 fillWidgetStates(axObject, properties);
439 fillRelationships(axObject, properties); 642 fillRelationships(axObject, properties);
440 643
441 accessibilityNode = buildObjectForNode(node, axObject, cacheImpl, properties ); 644 accessibilityNode = buildObjectForNode(node, axObject, cacheImpl, properties );
442 } 645 }
443 646
444 DEFINE_TRACE(InspectorAccessibilityAgent) 647 DEFINE_TRACE(InspectorAccessibilityAgent)
445 { 648 {
446 visitor->trace(m_page); 649 visitor->trace(m_page);
447 InspectorBaseAgent::trace(visitor); 650 InspectorBaseAgent::trace(visitor);
448 } 651 }
449 652
450 } // namespace blink 653 } // namespace blink
OLDNEW
« Source/devtools/protocol.json ('K') | « Source/modules/accessibility/AXLayoutObject.cpp ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698