Index: webkit/glue/webaccessibility.cc |
=================================================================== |
--- webkit/glue/webaccessibility.cc (revision 30762) |
+++ webkit/glue/webaccessibility.cc (working copy) |
@@ -2,288 +2,339 @@ |
// Use of this source code is governed by a BSD-style license that can be |
// found in the LICENSE file. |
-#include "config.h" |
+#include "webkit/glue/webaccessibility.h" |
-#include "AXObjectCache.h" |
-#include "Document.h" |
-#include "Frame.h" |
-#include "RefPtr.h" |
-#undef LOG |
- |
-#include "webkit/glue/webaccessibilitymanager_impl.h" |
- |
+#include "webkit/api/public/WebAccessibilityCache.h" |
#include "webkit/api/public/WebAccessibilityObject.h" |
-#include "webkit/api/src/WebFrameImpl.h" |
-#include "webkit/api/src/WebViewImpl.h" |
-#include "webkit/glue/glue_accessibility_object.h" |
-#include "webkit/glue/glue_util.h" |
+#include "webkit/api/public/WebAccessibilityRole.h" |
+#include "webkit/api/public/WebPoint.h" |
+#include "webkit/api/public/WebRect.h" |
+#include "webkit/api/public/WebString.h" |
+using WebKit::WebAccessibilityCache; |
+using WebKit::WebAccessibilityRole; |
using WebKit::WebAccessibilityObject; |
-using WebKit::WebFrameImpl; |
-using WebKit::WebView; |
+using WebKit::WebPoint; |
+using WebKit::WebRect; |
+using WebKit::WebString; |
namespace webkit_glue { |
-// struct WebAccessibilityManagerImpl::GlueAccessibilityObjectRoot |
-struct WebAccessibilityManagerImpl::GlueAccessibilityObjectRoot { |
- GlueAccessibilityObjectRoot() {} |
+// 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; |
+ } |
+} |
- // Root of the WebKit AccessibilityObject tree. |
- RefPtr<GlueAccessibilityObject> acc_obj_root_; |
-}; |
+long ConvertState(const WebAccessibilityObject& o) { |
+ long state = 0; |
+ if (o.isChecked()) |
+ state |= static_cast<long>(1 << WebAccessibility::STATE_CHECKED); |
-/*static*/ |
-WebAccessibilityManager* WebAccessibilityManager::Create() { |
- return new WebAccessibilityManagerImpl(); |
-} |
+ if (o.canSetFocusAttribute()) |
+ state |= static_cast<long>(1 << WebAccessibility::STATE_FOCUSABLE); |
-// class WebAccessibilityManagerImpl |
-WebAccessibilityManagerImpl::WebAccessibilityManagerImpl() |
- : root_(new GlueAccessibilityObjectRoot), |
- acc_obj_id_(1000) { |
-} |
+ if (o.isFocused()) |
+ state |= static_cast<long>(1 << WebAccessibility::STATE_FOCUSED); |
-WebAccessibilityManagerImpl::~WebAccessibilityManagerImpl() { |
- int_to_glue_acc_obj_map_.clear(); |
- acc_obj_to_int_map_.clear(); |
+ 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.isMultiSelect()) |
+ 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; |
} |
-bool WebAccessibilityManagerImpl::GetAccObjInfo(WebView* view, |
+ |
+bool WebAccessibility::GetAccObjInfo(WebAccessibilityCache* cache, |
const WebAccessibility::InParams& in_params, |
WebAccessibility::OutParams* out_params) { |
- if (!root_->acc_obj_root_ && !InitAccObjRoot(view)) { |
- // Failure in retrieving or initializing the root. |
- return false; |
- } |
- // Function input parameters. |
- int object_id = in_params.object_id; |
- int child_id = in_params.child_id; |
+ // 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. |
- if (in_params.child_id >= 1000) { |
+ 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. The local |child_id| is set to 0, to |
- // indicate that any function call should refer to the object itself. |
- object_id = in_params.child_id; |
- child_id = 0; |
+ // 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 false; |
+ |
+ // 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 false; |
+ |
+ active_acc_obj = active_acc_obj.childAt(index); |
+ } |
} |
- // Find GlueAccessibilityObject requested by |object_id|. |
- IntToGlueAccObjMap::iterator it = |
- int_to_glue_acc_obj_map_.find(object_id); |
- if (it == int_to_glue_acc_obj_map_.end() || !it->second) { |
- // Map did not contain a valid instance of the data requested. |
+ if (active_acc_obj.isNull()) |
return false; |
- } |
- RefPtr<GlueAccessibilityObject> active_acc_obj = it->second; |
// Temp paramters for holding output information. |
- RefPtr<GlueAccessibilityObject> out_acc_obj = NULL; |
- WebCore::String out_string; |
+ WebAccessibilityObject out_acc_obj; |
+ string16 out_string; |
switch (in_params.function_id) { |
- case WebAccessibility::FUNCTION_DODEFAULTACTION : |
- if (!active_acc_obj->DoDefaultAction(child_id)) |
+ case WebAccessibility::FUNCTION_DODEFAULTACTION: { |
+ if (!active_acc_obj.performDefaultAction()) |
return false; |
break; |
- case WebAccessibility::FUNCTION_HITTEST : |
- out_acc_obj = active_acc_obj->HitTest(in_params.input_long1, |
- in_params.input_long2); |
- if (!out_acc_obj.get()) |
+ } |
+ 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 false; |
break; |
- case WebAccessibility::FUNCTION_LOCATION : |
- if (!active_acc_obj->Location(&out_params->output_long1, |
- &out_params->output_long2, |
- &out_params->output_long3, |
- &out_params->output_long4, |
- child_id)) { |
- return false; |
- } |
+ } |
+ 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 : |
- out_acc_obj = active_acc_obj->Navigate( |
- static_cast<WebAccessibility::Direction>(in_params.input_long1), |
- child_id); |
- if (!out_acc_obj.get()) |
- return false; |
- break; |
- case WebAccessibility::FUNCTION_GETCHILD : |
- if (child_id == 0) { |
- // If child requested is self, stay with the same accessibility object. |
- out_params->object_id = in_params.object_id; |
- out_acc_obj = active_acc_obj.get(); |
- } else { |
- out_acc_obj = active_acc_obj->GetChild(child_id); |
+ } |
+ 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 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 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 false; |
} |
- |
- if (!out_acc_obj.get()) |
+ if (out_acc_obj.isNull()) |
return false; |
break; |
- case WebAccessibility::FUNCTION_CHILDCOUNT : |
- if (!active_acc_obj->ChildCount(&out_params->output_long1)) |
- return false; |
+ } |
+ case WebAccessibility::FUNCTION_GETCHILD: { |
+ out_params->object_id = in_params.object_id; |
+ out_acc_obj = active_acc_obj; |
break; |
- case WebAccessibility::FUNCTION_DEFAULTACTION : |
- if (!active_acc_obj->DefaultAction(child_id, &out_string)) |
- return false; |
+ } |
+ case WebAccessibility::FUNCTION_CHILDCOUNT: { |
+ out_params->output_long1 = active_acc_obj.childCount(); |
break; |
- case WebAccessibility::FUNCTION_DESCRIPTION : |
- if (!active_acc_obj->Description(child_id, &out_string)) |
+ } |
+ case WebAccessibility::FUNCTION_DEFAULTACTION: { |
+ out_string = active_acc_obj.actionVerb(); |
+ if (out_string.empty()) |
return false; |
break; |
- case WebAccessibility::FUNCTION_GETFOCUSEDCHILD : |
- out_acc_obj = active_acc_obj->GetFocusedChild(); |
- if (!out_acc_obj.get()) |
+ } |
+ case WebAccessibility::FUNCTION_DESCRIPTION: { |
+ out_string = active_acc_obj.accessibilityDescription(); |
+ if (out_string.empty()) |
return 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_HELPTEXT : |
- if (!active_acc_obj->HelpText(child_id, &out_string)) |
+ } |
+ case WebAccessibility::FUNCTION_GETFOCUSEDCHILD: { |
+ out_acc_obj = active_acc_obj.focusedChild(); |
+ if (out_acc_obj.isNull()) |
return false; |
break; |
- case WebAccessibility::FUNCTION_KEYBOARDSHORTCUT : |
- if (!active_acc_obj->KeyboardShortcut(child_id, &out_string)) |
+ } |
+ case WebAccessibility::FUNCTION_HELPTEXT: { |
+ out_string = active_acc_obj.helpText(); |
+ if (out_string.empty()) |
return false; |
break; |
- case WebAccessibility::FUNCTION_NAME : |
- if (!active_acc_obj->Name(child_id, &out_string)) |
+ } |
+ case WebAccessibility::FUNCTION_KEYBOARDSHORTCUT: { |
+ out_string = active_acc_obj.keyboardShortcut(); |
+ if (out_string.empty()) |
return false; |
break; |
- case WebAccessibility::FUNCTION_GETPARENT : |
- out_acc_obj = active_acc_obj->GetParent(); |
- if (!out_acc_obj.get()) |
+ } |
+ case WebAccessibility::FUNCTION_NAME: { |
+ out_string = active_acc_obj.title(); |
+ if (out_string.empty()) |
return false; |
break; |
- case WebAccessibility::FUNCTION_ROLE : |
- if (!active_acc_obj->Role(child_id, &out_params->output_long1)) |
+ } |
+ case WebAccessibility::FUNCTION_GETPARENT: { |
+ out_acc_obj = active_acc_obj.parentObject(); |
+ if (out_acc_obj.isNull()) |
return false; |
break; |
- case WebAccessibility::FUNCTION_STATE : |
- if (!active_acc_obj->State(child_id, &out_params->output_long1)) |
- return false; |
+ } |
+ case WebAccessibility::FUNCTION_ROLE: { |
+ out_params->output_long1 = ConvertRole(active_acc_obj.roleValue()); |
break; |
- case WebAccessibility::FUNCTION_VALUE : |
- if (!active_acc_obj->Value(child_id, &out_string)) |
+ } |
+ 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 false; |
break; |
+ } |
default: |
// Non-supported function id. |
return false; |
} |
// Output and hashmap assignments, as appropriate. |
- if (!out_string.isEmpty()) |
- out_params->output_string = StringToString16(out_string); |
+ if (!out_string.empty()) |
+ out_params->output_string = out_string; |
- if (out_acc_obj) { |
- AccObjToIntMap::iterator it = |
- acc_obj_to_int_map_.find(out_acc_obj->accessibilityObject()); |
- |
- if (it != acc_obj_to_int_map_.end()) { |
- // Data already present in map, return previously assigned id. |
- out_params->object_id = it->second; |
- out_params->output_long1 = -1; |
- } else { |
- // Insert new GlueAccessibilityObject in hashmaps. |
- int_to_glue_acc_obj_map_[acc_obj_id_] = out_acc_obj.get(); |
- acc_obj_to_int_map_[out_acc_obj->accessibilityObject()] = acc_obj_id_; |
- out_params->object_id = acc_obj_id_++; |
- out_params->output_long1 = -1; |
- } |
- } |
- // TODO(klink): Handle simple objects returned. |
- return true; |
-} |
- |
-bool WebAccessibilityManagerImpl::InitAccObjRoot(WebView* view) { |
- // Enable accessibility and retrieve Document. |
- WebCore::AXObjectCache::enableAccessibility(); |
- WebFrameImpl* main_frame_impl = |
- static_cast<WebFrameImpl*>(view->mainFrame()); |
- if (!main_frame_impl || !main_frame_impl->frame()) |
- return false; |
- |
- WebCore::Document* doc = main_frame_impl->frame()->document(); |
- |
- if (!doc || !doc->renderer()) |
- return false; |
- |
- if (!root_->acc_obj_root_) { |
- // Either we've never had a wrapper for this frame's top-level Document, |
- // the Document renderer was destroyed and its wrapper was detached, or |
- // the previous Document is in the page cache, and the current document |
- // needs to be wrapped. |
- root_->acc_obj_root_ = GlueAccessibilityObject::CreateInstance(doc-> |
- axObjectCache()->getOrCreate(doc->renderer())); |
- } |
- // Insert root in hashmaps. |
- int_to_glue_acc_obj_map_[acc_obj_id_] = root_->acc_obj_root_.get(); |
- acc_obj_to_int_map_[root_->acc_obj_root_->accessibilityObject()] = |
- acc_obj_id_++; |
- |
- return true; |
-} |
- |
-bool WebAccessibilityManagerImpl::ClearAccObjMap(int acc_obj_id, |
- bool clear_all) { |
- if (clear_all) { |
- // Clear maps and invalidate root. |
- int_to_glue_acc_obj_map_.clear(); |
- acc_obj_to_int_map_.clear(); |
- root_->acc_obj_root_ = 0; |
+ if (out_acc_obj.isNull()) |
return true; |
- } |
- IntToGlueAccObjMap::iterator it = int_to_glue_acc_obj_map_.find(acc_obj_id); |
+ int id = cache->addOrGetId(out_acc_obj); |
+ out_params->object_id = id; |
+ out_params->output_long1 = -1; |
- if (it == int_to_glue_acc_obj_map_.end()) { |
- // Element not found. |
- return false; |
- } |
- |
- if (it->second) { |
- // Erase element from reverse hashmap. |
- AccObjToIntMap::iterator it2 = |
- acc_obj_to_int_map_.find(it->second->accessibilityObject()); |
- |
- if (it2 != acc_obj_to_int_map_.end()) |
- acc_obj_to_int_map_.erase(it2); |
- } |
- int_to_glue_acc_obj_map_.erase(it); |
- |
- if (acc_obj_id == 1000) { |
- // Invalidate root. |
- root_->acc_obj_root_ = 0; |
- } |
+ // TODO(klink): Handle simple objects returned. |
return true; |
} |
-int WebAccessibilityManagerImpl::FocusAccObj( |
- const WebAccessibilityObject& object) { |
- if (object.isNull()) { |
- // Return with failure. |
- return -1; |
- } |
- |
- RefPtr<WebCore::AccessibilityObject> acc_obj = |
- WebAccessibilityObjectToAccessibilityObject(object); |
- |
- AccObjToIntMap::iterator it = acc_obj_to_int_map_.find(acc_obj.get()); |
- |
- if (it != acc_obj_to_int_map_.end()) |
- return it->second; |
- |
- // Insert new accessibility object in hashmaps and return its newly |
- // assigned accessibility object id. |
- int_to_glue_acc_obj_map_[acc_obj_id_] = |
- GlueAccessibilityObject::CreateInstance(acc_obj.get()); |
- acc_obj_to_int_map_[acc_obj.get()] = acc_obj_id_; |
- |
- return acc_obj_id_++; |
-} |
- |
} // namespace webkit_glue |