| // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "webkit/glue/webaccessibility.h" |
| |
| #include "third_party/WebKit/WebKit/chromium/public/WebAccessibilityCache.h" |
| #include "third_party/WebKit/WebKit/chromium/public/WebAccessibilityObject.h" |
| #include "third_party/WebKit/WebKit/chromium/public/WebAccessibilityRole.h" |
| #include "third_party/WebKit/WebKit/chromium/public/WebPoint.h" |
| #include "third_party/WebKit/WebKit/chromium/public/WebRect.h" |
| #include "third_party/WebKit/WebKit/chromium/public/WebString.h" |
| |
| using WebKit::WebAccessibilityCache; |
| using WebKit::WebAccessibilityRole; |
| using WebKit::WebAccessibilityObject; |
| using WebKit::WebPoint; |
| using WebKit::WebRect; |
| using WebKit::WebString; |
| |
| namespace webkit_glue { |
| |
| // Provides a conversion between the WebKit::WebAccessibilityRole and a role |
| // supported on the Browser side. Listed alphabetically by the |
| // WebAccessibilityRole (except for default role). |
| WebAccessibility::Role ConvertRole(WebKit::WebAccessibilityRole role) { |
| switch (role) { |
| case WebKit::WebAccessibilityRoleLandmarkApplication: |
| return WebAccessibility::ROLE_APPLICATION; |
| case WebKit::WebAccessibilityRoleCell: |
| return WebAccessibility::ROLE_CELL; |
| case WebKit::WebAccessibilityRoleCheckBox: |
| return WebAccessibility::ROLE_CHECKBUTTON; |
| case WebKit::WebAccessibilityRoleColumn: |
| return WebAccessibility::ROLE_COLUMN; |
| case WebKit::WebAccessibilityRoleColumnHeader: |
| return WebAccessibility::ROLE_COLUMNHEADER; |
| case WebKit::WebAccessibilityRoleDocumentArticle: |
| case WebKit::WebAccessibilityRoleWebArea: |
| return WebAccessibility::ROLE_DOCUMENT; |
| case WebKit::WebAccessibilityRoleImageMap: |
| case WebKit::WebAccessibilityRoleImage: |
| return WebAccessibility::ROLE_GRAPHIC; |
| case WebKit::WebAccessibilityRoleDocumentRegion: |
| case WebKit::WebAccessibilityRoleRadioGroup: |
| case WebKit::WebAccessibilityRoleGroup: |
| return WebAccessibility::ROLE_GROUPING; |
| case WebKit::WebAccessibilityRoleLink: |
| case WebKit::WebAccessibilityRoleWebCoreLink: |
| return WebAccessibility::ROLE_LINK; |
| case WebKit::WebAccessibilityRoleList: |
| return WebAccessibility::ROLE_LIST; |
| case WebKit::WebAccessibilityRoleListBox: |
| return WebAccessibility::ROLE_LISTBOX; |
| case WebKit::WebAccessibilityRoleListBoxOption: |
| return WebAccessibility::ROLE_LISTITEM; |
| case WebKit::WebAccessibilityRoleMenuBar: |
| return WebAccessibility::ROLE_MENUBAR; |
| case WebKit::WebAccessibilityRoleMenuButton: |
| case WebKit::WebAccessibilityRoleMenuItem: |
| return WebAccessibility::ROLE_MENUITEM; |
| case WebKit::WebAccessibilityRoleMenu: |
| return WebAccessibility::ROLE_MENUPOPUP; |
| case WebKit::WebAccessibilityRoleOutline: |
| return WebAccessibility::ROLE_OUTLINE; |
| case WebKit::WebAccessibilityRoleTabGroup: |
| return WebAccessibility::ROLE_PAGETABLIST; |
| case WebKit::WebAccessibilityRoleProgressIndicator: |
| return WebAccessibility::ROLE_PROGRESSBAR; |
| case WebKit::WebAccessibilityRoleButton: |
| return WebAccessibility::ROLE_PUSHBUTTON; |
| case WebKit::WebAccessibilityRoleRadioButton: |
| return WebAccessibility::ROLE_RADIOBUTTON; |
| case WebKit::WebAccessibilityRoleRow: |
| return WebAccessibility::ROLE_ROW; |
| case WebKit::WebAccessibilityRoleRowHeader: |
| return WebAccessibility::ROLE_ROWHEADER; |
| case WebKit::WebAccessibilityRoleSplitter: |
| return WebAccessibility::ROLE_SEPARATOR; |
| case WebKit::WebAccessibilityRoleSlider: |
| return WebAccessibility::ROLE_SLIDER; |
| case WebKit::WebAccessibilityRoleStaticText: |
| return WebAccessibility::ROLE_STATICTEXT; |
| case WebKit::WebAccessibilityRoleApplicationStatus: |
| return WebAccessibility::ROLE_STATUSBAR; |
| case WebKit::WebAccessibilityRoleTable: |
| return WebAccessibility::ROLE_TABLE; |
| case WebKit::WebAccessibilityRoleListMarker: |
| case WebKit::WebAccessibilityRoleTextField: |
| case WebKit::WebAccessibilityRoleTextArea: |
| return WebAccessibility::ROLE_TEXT; |
| case WebKit::WebAccessibilityRoleToolbar: |
| return WebAccessibility::ROLE_TOOLBAR; |
| case WebKit::WebAccessibilityRoleUserInterfaceTooltip: |
| return WebAccessibility::ROLE_TOOLTIP; |
| case WebKit::WebAccessibilityRoleDocument: |
| case WebKit::WebAccessibilityRoleUnknown: |
| default: |
| // This is the default role. |
| return WebAccessibility::ROLE_CLIENT; |
| } |
| } |
| |
| long ConvertState(const WebAccessibilityObject& o) { |
| long state = 0; |
| if (o.isChecked()) |
| state |= static_cast<long>(1 << WebAccessibility::STATE_CHECKED); |
| |
| if (o.canSetFocusAttribute()) |
| state |= static_cast<long>(1 << WebAccessibility::STATE_FOCUSABLE); |
| |
| if (o.isFocused()) |
| state |= static_cast<long>(1 << WebAccessibility::STATE_FOCUSED); |
| |
| if (o.isHovered()) |
| state |= static_cast<long>(1 << WebAccessibility::STATE_HOTTRACKED); |
| |
| if (o.isIndeterminate()) |
| state |= static_cast<long>(1 << WebAccessibility::STATE_INDETERMINATE); |
| |
| if (o.isAnchor()) |
| state |= static_cast<long>(1 << WebAccessibility::STATE_LINKED); |
| |
| if (o.isMultiSelectable()) |
| state |= static_cast<long>(1 << WebAccessibility::STATE_MULTISELECTABLE); |
| |
| if (o.isOffScreen()) |
| state |= static_cast<long>(1 << WebAccessibility::STATE_OFFSCREEN); |
| |
| if (o.isPressed()) |
| state |= static_cast<long>(1 << WebAccessibility::STATE_PRESSED); |
| |
| if (o.isPasswordField()) |
| state |= static_cast<long>(1 << WebAccessibility::STATE_PROTECTED); |
| |
| if (o.isReadOnly()) |
| state |= static_cast<long>(1 << WebAccessibility::STATE_READONLY); |
| |
| if (o.isVisited()) |
| state |= static_cast<long>(1 << WebAccessibility::STATE_TRAVERSED); |
| |
| if (!o.isEnabled()) |
| state |= static_cast<long>(1 << WebAccessibility::STATE_UNAVAILABLE); |
| |
| return state; |
| } |
| |
| int32 WebAccessibility::GetAccObjInfo(WebAccessibilityCache* cache, |
| const WebAccessibility::InParams& in_params, |
| WebAccessibility::OutParams* out_params) { |
| // Find object requested by |object_id|. |
| WebAccessibilityObject active_acc_obj; |
| |
| // Since ids assigned by Chrome starts at 1000, whereas platform-specific ids |
| // used to reference a child will be in a wholly different range, we know |
| // that any id that high should be treated as a non-direct descendant. |
| bool local_child = false; |
| if (cache->isValidId(in_params.child_id)) { |
| // Object is not a direct child, re-map the input parameters accordingly. |
| // The object to be retrieved is referred to by the |in_params.child_id|, as |
| // a result of e.g. a focus event. |
| active_acc_obj = cache->getObjectById(in_params.child_id); |
| } else { |
| local_child = true; |
| |
| active_acc_obj = cache->getObjectById(in_params.object_id); |
| if (active_acc_obj.isNull()) |
| return RETURNCODE_FAIL; |
| |
| // child_id == 0 means self. Otherwise, it's a local child - 1. |
| if (in_params.child_id > 0) { |
| unsigned index = in_params.child_id - 1; |
| if (index >= active_acc_obj.childCount()) |
| return RETURNCODE_FAIL; |
| |
| active_acc_obj = active_acc_obj.childAt(index); |
| } |
| } |
| |
| if (active_acc_obj.isNull()) |
| return RETURNCODE_FAIL; |
| |
| // Temp paramters for holding output information. |
| WebAccessibilityObject out_acc_obj; |
| string16 out_string; |
| |
| switch (in_params.function_id) { |
| case WebAccessibility::FUNCTION_DODEFAULTACTION: { |
| if (!active_acc_obj.performDefaultAction()) |
| return RETURNCODE_FALSE; |
| break; |
| } |
| case WebAccessibility::FUNCTION_HITTEST: { |
| WebPoint point(in_params.input_long1, in_params.input_long2); |
| out_acc_obj = active_acc_obj.hitTest(point); |
| if (out_acc_obj.isNull()) |
| return RETURNCODE_FALSE; |
| break; |
| } |
| case WebAccessibility::FUNCTION_LOCATION: { |
| WebRect rect = active_acc_obj.boundingBoxRect(); |
| out_params->output_long1 = rect.x; |
| out_params->output_long2 = rect.y; |
| out_params->output_long3 = rect.width; |
| out_params->output_long4 = rect.height; |
| break; |
| } |
| case WebAccessibility::FUNCTION_NAVIGATE: { |
| WebAccessibility::Direction dir = |
| static_cast<WebAccessibility::Direction>(in_params.input_long1); |
| switch (dir) { |
| case WebAccessibility::DIRECTION_DOWN: |
| case WebAccessibility::DIRECTION_UP: |
| case WebAccessibility::DIRECTION_LEFT: |
| case WebAccessibility::DIRECTION_RIGHT: |
| // These directions are not implemented, matching Mozilla and IE. |
| return RETURNCODE_FALSE; |
| case WebAccessibility::DIRECTION_LASTCHILD: |
| case WebAccessibility::DIRECTION_FIRSTCHILD: |
| // MSDN states that navigating to first/last child can only be from |
| // self. |
| if (!local_child) |
| return RETURNCODE_FALSE; |
| |
| if (dir == WebAccessibility::DIRECTION_FIRSTCHILD) { |
| out_acc_obj = active_acc_obj.firstChild(); |
| } else { |
| out_acc_obj = active_acc_obj.lastChild(); |
| } |
| break; |
| case WebAccessibility::DIRECTION_NEXT: |
| case WebAccessibility::DIRECTION_PREVIOUS: { |
| if (dir == WebAccessibility::DIRECTION_NEXT) { |
| out_acc_obj = active_acc_obj.nextSibling(); |
| } else { |
| out_acc_obj = active_acc_obj.previousSibling(); |
| } |
| break; |
| } |
| default: |
| return RETURNCODE_FALSE; |
| } |
| |
| if (out_acc_obj.isNull()) |
| return RETURNCODE_FALSE; |
| |
| break; |
| } |
| case WebAccessibility::FUNCTION_GETCHILD: { |
| out_params->object_id = in_params.object_id; |
| out_acc_obj = active_acc_obj; |
| break; |
| } |
| case WebAccessibility::FUNCTION_CHILDCOUNT: { |
| out_params->output_long1 = active_acc_obj.childCount(); |
| break; |
| } |
| case WebAccessibility::FUNCTION_DEFAULTACTION: { |
| out_string = active_acc_obj.actionVerb(); |
| if (out_string.empty()) |
| return RETURNCODE_FALSE; |
| break; |
| } |
| case WebAccessibility::FUNCTION_DESCRIPTION: { |
| out_string = active_acc_obj.accessibilityDescription(); |
| if (out_string.empty()) |
| return RETURNCODE_FALSE; |
| // From the Mozilla MSAA implementation: |
| // "Signal to screen readers that this description is speakable and is not |
| // a formatted positional information description. Don't localize the |
| // 'Description: ' part of this string, it will be parsed out by assistive |
| // technologies." |
| out_string = L"Description: " + out_string; |
| break; |
| } |
| case WebAccessibility::FUNCTION_GETFOCUSEDCHILD: { |
| out_acc_obj = active_acc_obj.focusedChild(); |
| if (out_acc_obj.isNull()) |
| return RETURNCODE_FALSE; |
| break; |
| } |
| case WebAccessibility::FUNCTION_HELPTEXT: { |
| out_string = active_acc_obj.helpText(); |
| if (out_string.empty()) |
| return RETURNCODE_FALSE; |
| break; |
| } |
| case WebAccessibility::FUNCTION_KEYBOARDSHORTCUT: { |
| out_string = active_acc_obj.keyboardShortcut(); |
| if (out_string.empty()) |
| return RETURNCODE_FALSE; |
| break; |
| } |
| case WebAccessibility::FUNCTION_NAME: { |
| out_string = active_acc_obj.title(); |
| if (out_string.empty()) |
| return RETURNCODE_FALSE; |
| break; |
| } |
| case WebAccessibility::FUNCTION_GETPARENT: { |
| out_acc_obj = active_acc_obj.parentObject(); |
| if (out_acc_obj.isNull()) |
| return RETURNCODE_FALSE; |
| break; |
| } |
| case WebAccessibility::FUNCTION_ROLE: { |
| out_params->output_long1 = ConvertRole(active_acc_obj.roleValue()); |
| break; |
| } |
| case WebAccessibility::FUNCTION_STATE: { |
| out_params->output_long1 = ConvertState(active_acc_obj); |
| break; |
| } |
| case WebAccessibility::FUNCTION_VALUE: { |
| out_string = active_acc_obj.stringValue(); |
| if (out_string.empty()) |
| return RETURNCODE_FALSE; |
| break; |
| } |
| default: |
| // Non-supported function id. |
| return RETURNCODE_FAIL; |
| } |
| |
| // Output and hashmap assignments, as appropriate. |
| if (!out_string.empty()) |
| out_params->output_string = out_string; |
| |
| if (out_acc_obj.isNull()) |
| return RETURNCODE_TRUE; |
| |
| int id = cache->addOrGetId(out_acc_obj); |
| out_params->object_id = id; |
| out_params->output_long1 = -1; |
| |
| // TODO(ctguil): Handle simple objects returned. |
| return RETURNCODE_TRUE; |
| } |
| |
| } // namespace webkit_glue |