blob: 1af91d60e1be81491e7f4f29e47b1def00995283 [file] [log] [blame]
// 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