PageRenderTime 34ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/platform/external/webkit/WebCore/accessibility/gtk/AccessibilityObjectWrapperAtk.cpp

https://github.com/aharish/totoro-gb-opensource-update2
C++ | 1786 lines | 1368 code | 276 blank | 142 comment | 227 complexity | 90bd1925842b4b0d19183d39805e95a1 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, LGPL-2.0, AGPL-3.0, BSD-3-Clause
  1. /*
  2. * Copyright (C) 2008 Nuanti Ltd.
  3. * Copyright (C) 2009 Igalia S.L.
  4. * Copyright (C) 2009 Jan Alonzo
  5. *
  6. * Portions from Mozilla a11y, copyright as follows:
  7. *
  8. * The Original Code is mozilla.org code.
  9. *
  10. * The Initial Developer of the Original Code is
  11. * Sun Microsystems, Inc.
  12. * Portions created by the Initial Developer are Copyright (C) 2002
  13. * the Initial Developer. All Rights Reserved.
  14. *
  15. * This library is free software; you can redistribute it and/or
  16. * modify it under the terms of the GNU Library General Public
  17. * License as published by the Free Software Foundation; either
  18. * version 2 of the License, or (at your option) any later version.
  19. *
  20. * This library is distributed in the hope that it will be useful,
  21. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  22. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  23. * Library General Public License for more details.
  24. *
  25. * You should have received a copy of the GNU Library General Public License
  26. * along with this library; see the file COPYING.LIB. If not, write to
  27. * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  28. * Boston, MA 02110-1301, USA.
  29. */
  30. #include "config.h"
  31. #include "AccessibilityObjectWrapperAtk.h"
  32. #if HAVE(ACCESSIBILITY)
  33. #include "AXObjectCache.h"
  34. #include "AccessibilityListBox.h"
  35. #include "AccessibilityListBoxOption.h"
  36. #include "AccessibilityRenderObject.h"
  37. #include "AccessibilityTable.h"
  38. #include "AccessibilityTableCell.h"
  39. #include "AccessibilityTableColumn.h"
  40. #include "AccessibilityTableRow.h"
  41. #include "AtomicString.h"
  42. #include "CString.h"
  43. #include "Document.h"
  44. #include "DocumentType.h"
  45. #include "Editor.h"
  46. #include "Frame.h"
  47. #include "FrameView.h"
  48. #include "HostWindow.h"
  49. #include "HTMLNames.h"
  50. #include "HTMLTableCaptionElement.h"
  51. #include "HTMLTableElement.h"
  52. #include "InlineTextBox.h"
  53. #include "IntRect.h"
  54. #include "NotImplemented.h"
  55. #include "RenderText.h"
  56. #include "TextEncoding.h"
  57. #include <atk/atk.h>
  58. #include <glib.h>
  59. #include <glib/gprintf.h>
  60. #include <libgail-util/gail-util.h>
  61. #include <pango/pango.h>
  62. using namespace WebCore;
  63. static AccessibilityObject* fallbackObject()
  64. {
  65. static AXObjectCache* fallbackCache = new AXObjectCache;
  66. static AccessibilityObject* object = 0;
  67. if (!object) {
  68. // FIXME: using fallbackCache->getOrCreate(ListBoxOptionRole) is a hack
  69. object = fallbackCache->getOrCreate(ListBoxOptionRole);
  70. object->ref();
  71. }
  72. return object;
  73. }
  74. // Used to provide const char* returns.
  75. static const char* returnString(const String& str)
  76. {
  77. static CString returnedString;
  78. returnedString = str.utf8();
  79. return returnedString.data();
  80. }
  81. static AccessibilityObject* core(WebKitAccessible* accessible)
  82. {
  83. if (!accessible)
  84. return 0;
  85. return accessible->m_object;
  86. }
  87. static AccessibilityObject* core(AtkObject* object)
  88. {
  89. if (!WEBKIT_IS_ACCESSIBLE(object))
  90. return 0;
  91. return core(WEBKIT_ACCESSIBLE(object));
  92. }
  93. static AccessibilityObject* core(AtkAction* action)
  94. {
  95. return core(ATK_OBJECT(action));
  96. }
  97. static AccessibilityObject* core(AtkSelection* selection)
  98. {
  99. return core(ATK_OBJECT(selection));
  100. }
  101. static AccessibilityObject* core(AtkText* text)
  102. {
  103. return core(ATK_OBJECT(text));
  104. }
  105. static AccessibilityObject* core(AtkEditableText* text)
  106. {
  107. return core(ATK_OBJECT(text));
  108. }
  109. static AccessibilityObject* core(AtkComponent* component)
  110. {
  111. return core(ATK_OBJECT(component));
  112. }
  113. static AccessibilityObject* core(AtkImage* image)
  114. {
  115. return core(ATK_OBJECT(image));
  116. }
  117. static AccessibilityObject* core(AtkTable* table)
  118. {
  119. return core(ATK_OBJECT(table));
  120. }
  121. static AccessibilityObject* core(AtkDocument* document)
  122. {
  123. return core(ATK_OBJECT(document));
  124. }
  125. static const gchar* nameFromChildren(AccessibilityObject* object)
  126. {
  127. if (!object)
  128. return 0;
  129. AccessibilityRenderObject::AccessibilityChildrenVector children = object->children();
  130. // Currently, object->stringValue() should be an empty String. This might not be the case down the road.
  131. String name = object->stringValue();
  132. for (unsigned i = 0; i < children.size(); ++i)
  133. name += children.at(i).get()->stringValue();
  134. return returnString(name);
  135. }
  136. static const gchar* webkit_accessible_get_name(AtkObject* object)
  137. {
  138. AccessibilityObject* coreObject = core(object);
  139. if (!coreObject->isAccessibilityRenderObject())
  140. return returnString(coreObject->stringValue());
  141. AccessibilityRenderObject* renderObject = static_cast<AccessibilityRenderObject*>(coreObject);
  142. if (coreObject->isControl()) {
  143. AccessibilityObject* label = renderObject->correspondingLabelForControlElement();
  144. if (label)
  145. return returnString(nameFromChildren(label));
  146. }
  147. if (renderObject->isImage() || renderObject->isInputImage()) {
  148. Node* node = renderObject->renderer()->node();
  149. if (node && node->isHTMLElement()) {
  150. // Get the attribute rather than altText String so as not to fall back on title.
  151. String alt = static_cast<HTMLElement*>(node)->getAttribute(HTMLNames::altAttr);
  152. if (!alt.isEmpty())
  153. return returnString(alt);
  154. }
  155. }
  156. return returnString(coreObject->stringValue());
  157. }
  158. static const gchar* webkit_accessible_get_description(AtkObject* object)
  159. {
  160. AccessibilityObject* coreObject = core(object);
  161. Node* node = 0;
  162. if (coreObject->isAccessibilityRenderObject())
  163. node = static_cast<AccessibilityRenderObject*>(coreObject)->renderer()->node();
  164. if (!node || !node->isHTMLElement() || coreObject->ariaRoleAttribute() != UnknownRole)
  165. return returnString(coreObject->accessibilityDescription());
  166. // atk_table_get_summary returns an AtkObject. We have no summary object, so expose summary here.
  167. if (coreObject->roleValue() == TableRole) {
  168. String summary = static_cast<HTMLTableElement*>(node)->summary();
  169. if (!summary.isEmpty())
  170. return returnString(summary);
  171. }
  172. // The title attribute should be reliably available as the object's descripton.
  173. // We do not want to fall back on other attributes in its absence. See bug 25524.
  174. String title = static_cast<HTMLElement*>(node)->title();
  175. if (!title.isEmpty())
  176. return returnString(title);
  177. return returnString(coreObject->accessibilityDescription());
  178. }
  179. static void setAtkRelationSetFromCoreObject(AccessibilityObject* coreObject, AtkRelationSet* relationSet)
  180. {
  181. AccessibilityRenderObject* accObject = static_cast<AccessibilityRenderObject*>(coreObject);
  182. if (accObject->isControl()) {
  183. AccessibilityObject* label = accObject->correspondingLabelForControlElement();
  184. if (label)
  185. atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_LABELLED_BY, label->wrapper());
  186. } else {
  187. AccessibilityObject* control = accObject->correspondingControlForLabelElement();
  188. if (control)
  189. atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_LABEL_FOR, control->wrapper());
  190. }
  191. }
  192. static gpointer webkit_accessible_parent_class = 0;
  193. static AtkObject* atkParentOfWebView(AtkObject* object)
  194. {
  195. AccessibilityObject* coreParent = core(object)->parentObjectUnignored();
  196. // The top level web view claims to not have a parent. This makes it
  197. // impossible for assistive technologies to ascend the accessible
  198. // hierarchy all the way to the application. (Bug 30489)
  199. if (!coreParent && core(object)->isWebArea()) {
  200. HostWindow* hostWindow = core(object)->document()->view()->hostWindow();
  201. if (hostWindow) {
  202. PlatformPageClient webView = hostWindow->platformPageClient();
  203. if (webView) {
  204. GtkWidget* webViewParent = gtk_widget_get_parent(webView);
  205. if (webViewParent)
  206. return gtk_widget_get_accessible(webViewParent);
  207. }
  208. }
  209. }
  210. if (!coreParent)
  211. return 0;
  212. return coreParent->wrapper();
  213. }
  214. static AtkObject* webkit_accessible_get_parent(AtkObject* object)
  215. {
  216. AccessibilityObject* coreParent = core(object)->parentObjectUnignored();
  217. if (!coreParent && core(object)->isWebArea())
  218. return atkParentOfWebView(object);
  219. if (!coreParent)
  220. return 0;
  221. return coreParent->wrapper();
  222. }
  223. static gint webkit_accessible_get_n_children(AtkObject* object)
  224. {
  225. return core(object)->children().size();
  226. }
  227. static AtkObject* webkit_accessible_ref_child(AtkObject* object, gint index)
  228. {
  229. AccessibilityObject* coreObject = core(object);
  230. AccessibilityObject::AccessibilityChildrenVector children = coreObject->children();
  231. if (index < 0 || static_cast<unsigned>(index) >= children.size())
  232. return 0;
  233. AccessibilityObject* coreChild = children.at(index).get();
  234. if (!coreChild)
  235. return 0;
  236. AtkObject* child = coreChild->wrapper();
  237. atk_object_set_parent(child, object);
  238. g_object_ref(child);
  239. return child;
  240. }
  241. static gint webkit_accessible_get_index_in_parent(AtkObject* object)
  242. {
  243. AccessibilityObject* coreObject = core(object);
  244. AccessibilityObject* parent = coreObject->parentObjectUnignored();
  245. if (!parent && core(object)->isWebArea()) {
  246. AtkObject* atkParent = atkParentOfWebView(object);
  247. if (!atkParent)
  248. return -1;
  249. unsigned count = atk_object_get_n_accessible_children(atkParent);
  250. for (unsigned i = 0; i < count; ++i) {
  251. AtkObject* child = atk_object_ref_accessible_child(atkParent, i);
  252. bool childIsObject = child == object;
  253. g_object_unref(child);
  254. if (childIsObject)
  255. return i;
  256. }
  257. }
  258. AccessibilityObject::AccessibilityChildrenVector children = parent->children();
  259. unsigned count = children.size();
  260. for (unsigned i = 0; i < count; ++i) {
  261. if (children[i] == coreObject)
  262. return i;
  263. }
  264. return -1;
  265. }
  266. static AtkAttributeSet* addAttributeToSet(AtkAttributeSet* attributeSet, const char* name, const char* value)
  267. {
  268. AtkAttribute* attribute = static_cast<AtkAttribute*>(g_malloc(sizeof(AtkAttribute)));
  269. attribute->name = g_strdup(name);
  270. attribute->value = g_strdup(value);
  271. attributeSet = g_slist_prepend(attributeSet, attribute);
  272. return attributeSet;
  273. }
  274. static AtkAttributeSet* webkit_accessible_get_attributes(AtkObject* object)
  275. {
  276. AtkAttributeSet* attributeSet = 0;
  277. int headingLevel = core(object)->headingLevel();
  278. if (headingLevel) {
  279. String value = String::number(headingLevel);
  280. attributeSet = addAttributeToSet(attributeSet, "level", value.utf8().data());
  281. }
  282. return attributeSet;
  283. }
  284. static AtkRole atkRole(AccessibilityRole role)
  285. {
  286. switch (role) {
  287. case UnknownRole:
  288. return ATK_ROLE_UNKNOWN;
  289. case ButtonRole:
  290. return ATK_ROLE_PUSH_BUTTON;
  291. case RadioButtonRole:
  292. return ATK_ROLE_RADIO_BUTTON;
  293. case CheckBoxRole:
  294. return ATK_ROLE_CHECK_BOX;
  295. case SliderRole:
  296. return ATK_ROLE_SLIDER;
  297. case TabGroupRole:
  298. return ATK_ROLE_PAGE_TAB_LIST;
  299. case TextFieldRole:
  300. case TextAreaRole:
  301. return ATK_ROLE_ENTRY;
  302. case StaticTextRole:
  303. return ATK_ROLE_TEXT;
  304. case OutlineRole:
  305. return ATK_ROLE_TREE;
  306. case MenuBarRole:
  307. return ATK_ROLE_MENU_BAR;
  308. case MenuRole:
  309. return ATK_ROLE_MENU;
  310. case MenuItemRole:
  311. return ATK_ROLE_MENU_ITEM;
  312. case ColumnRole:
  313. //return ATK_ROLE_TABLE_COLUMN_HEADER; // Is this right?
  314. return ATK_ROLE_UNKNOWN; // Matches Mozilla
  315. case RowRole:
  316. //return ATK_ROLE_TABLE_ROW_HEADER; // Is this right?
  317. return ATK_ROLE_LIST_ITEM; // Matches Mozilla
  318. case ToolbarRole:
  319. return ATK_ROLE_TOOL_BAR;
  320. case BusyIndicatorRole:
  321. return ATK_ROLE_PROGRESS_BAR; // Is this right?
  322. case ProgressIndicatorRole:
  323. //return ATK_ROLE_SPIN_BUTTON; // Some confusion about this role in AccessibilityRenderObject.cpp
  324. return ATK_ROLE_PROGRESS_BAR;
  325. case WindowRole:
  326. return ATK_ROLE_WINDOW;
  327. case ComboBoxRole:
  328. return ATK_ROLE_COMBO_BOX;
  329. case SplitGroupRole:
  330. return ATK_ROLE_SPLIT_PANE;
  331. case SplitterRole:
  332. return ATK_ROLE_SEPARATOR;
  333. case ColorWellRole:
  334. return ATK_ROLE_COLOR_CHOOSER;
  335. case ListRole:
  336. return ATK_ROLE_LIST;
  337. case ScrollBarRole:
  338. return ATK_ROLE_SCROLL_BAR;
  339. case GridRole: // Is this right?
  340. case TableRole:
  341. return ATK_ROLE_TABLE;
  342. case ApplicationRole:
  343. return ATK_ROLE_APPLICATION;
  344. case GroupRole:
  345. case RadioGroupRole:
  346. return ATK_ROLE_PANEL;
  347. case CellRole:
  348. return ATK_ROLE_TABLE_CELL;
  349. case LinkRole:
  350. case WebCoreLinkRole:
  351. case ImageMapLinkRole:
  352. return ATK_ROLE_LINK;
  353. case ImageMapRole:
  354. case ImageRole:
  355. return ATK_ROLE_IMAGE;
  356. case ListMarkerRole:
  357. return ATK_ROLE_TEXT;
  358. case WebAreaRole:
  359. //return ATK_ROLE_HTML_CONTAINER; // Is this right?
  360. return ATK_ROLE_DOCUMENT_FRAME;
  361. case HeadingRole:
  362. return ATK_ROLE_HEADING;
  363. case ListBoxRole:
  364. return ATK_ROLE_LIST;
  365. case ListBoxOptionRole:
  366. return ATK_ROLE_LIST_ITEM;
  367. default:
  368. return ATK_ROLE_UNKNOWN;
  369. }
  370. }
  371. static AtkRole webkit_accessible_get_role(AtkObject* object)
  372. {
  373. AccessibilityObject* axObject = core(object);
  374. if (!axObject)
  375. return ATK_ROLE_UNKNOWN;
  376. // WebCore does not seem to have a role for list items
  377. if (axObject->isGroup()) {
  378. AccessibilityObject* parent = axObject->parentObjectUnignored();
  379. if (parent && parent->isList())
  380. return ATK_ROLE_LIST_ITEM;
  381. }
  382. // WebCore does not know about paragraph role, label role, or section role
  383. if (axObject->isAccessibilityRenderObject()) {
  384. Node* node = static_cast<AccessibilityRenderObject*>(axObject)->renderer()->node();
  385. if (node) {
  386. if (node->hasTagName(HTMLNames::pTag))
  387. return ATK_ROLE_PARAGRAPH;
  388. if (node->hasTagName(HTMLNames::labelTag))
  389. return ATK_ROLE_LABEL;
  390. if (node->hasTagName(HTMLNames::divTag))
  391. return ATK_ROLE_SECTION;
  392. }
  393. }
  394. // Note: Why doesn't WebCore have a password field for this
  395. if (axObject->isPasswordField())
  396. return ATK_ROLE_PASSWORD_TEXT;
  397. return atkRole(axObject->roleValue());
  398. }
  399. static void setAtkStateSetFromCoreObject(AccessibilityObject* coreObject, AtkStateSet* stateSet)
  400. {
  401. AccessibilityObject* parent = coreObject->parentObject();
  402. bool isListBoxOption = parent && parent->isListBox();
  403. // Please keep the state list in alphabetical order
  404. if (coreObject->isChecked())
  405. atk_state_set_add_state(stateSet, ATK_STATE_CHECKED);
  406. // FIXME: isReadOnly does not seem to do the right thing for
  407. // controls, so check explicitly for them. In addition, because
  408. // isReadOnly is false for listBoxOptions, we need to add one
  409. // more check so that we do not present them as being "editable".
  410. if ((!coreObject->isReadOnly() ||
  411. (coreObject->isControl() && coreObject->canSetValueAttribute())) &&
  412. !isListBoxOption)
  413. atk_state_set_add_state(stateSet, ATK_STATE_EDITABLE);
  414. // FIXME: Put both ENABLED and SENSITIVE together here for now
  415. if (coreObject->isEnabled()) {
  416. atk_state_set_add_state(stateSet, ATK_STATE_ENABLED);
  417. atk_state_set_add_state(stateSet, ATK_STATE_SENSITIVE);
  418. }
  419. if (coreObject->canSetFocusAttribute())
  420. atk_state_set_add_state(stateSet, ATK_STATE_FOCUSABLE);
  421. if (coreObject->isFocused())
  422. atk_state_set_add_state(stateSet, ATK_STATE_FOCUSED);
  423. // TODO: ATK_STATE_HORIZONTAL
  424. if (coreObject->isIndeterminate())
  425. atk_state_set_add_state(stateSet, ATK_STATE_INDETERMINATE);
  426. if (coreObject->isMultiSelectable())
  427. atk_state_set_add_state(stateSet, ATK_STATE_MULTISELECTABLE);
  428. // TODO: ATK_STATE_OPAQUE
  429. if (coreObject->isPressed())
  430. atk_state_set_add_state(stateSet, ATK_STATE_PRESSED);
  431. // TODO: ATK_STATE_SELECTABLE_TEXT
  432. if (coreObject->canSetSelectedAttribute()) {
  433. atk_state_set_add_state(stateSet, ATK_STATE_SELECTABLE);
  434. // Items in focusable lists in Gtk have both STATE_SELECT{ABLE,ED}
  435. // and STATE_FOCUS{ABLE,ED}. We'll fake the latter based on the
  436. // former.
  437. if (isListBoxOption)
  438. atk_state_set_add_state(stateSet, ATK_STATE_FOCUSABLE);
  439. }
  440. if (coreObject->isSelected()) {
  441. atk_state_set_add_state(stateSet, ATK_STATE_SELECTED);
  442. // Items in focusable lists in Gtk have both STATE_SELECT{ABLE,ED}
  443. // and STATE_FOCUS{ABLE,ED}. We'll fake the latter based on the
  444. // former.
  445. if (isListBoxOption)
  446. atk_state_set_add_state(stateSet, ATK_STATE_FOCUSED);
  447. }
  448. // FIXME: Group both SHOWING and VISIBLE here for now
  449. // Not sure how to handle this in WebKit, see bug
  450. // http://bugzilla.gnome.org/show_bug.cgi?id=509650 for other
  451. // issues with SHOWING vs VISIBLE within GTK+
  452. if (!coreObject->isOffScreen()) {
  453. atk_state_set_add_state(stateSet, ATK_STATE_SHOWING);
  454. atk_state_set_add_state(stateSet, ATK_STATE_VISIBLE);
  455. }
  456. // Mutually exclusive, so we group these two
  457. if (coreObject->roleValue() == TextFieldRole)
  458. atk_state_set_add_state(stateSet, ATK_STATE_SINGLE_LINE);
  459. else if (coreObject->roleValue() == TextAreaRole)
  460. atk_state_set_add_state(stateSet, ATK_STATE_MULTI_LINE);
  461. // TODO: ATK_STATE_SENSITIVE
  462. // TODO: ATK_STATE_VERTICAL
  463. if (coreObject->isVisited())
  464. atk_state_set_add_state(stateSet, ATK_STATE_VISITED);
  465. }
  466. static AtkStateSet* webkit_accessible_ref_state_set(AtkObject* object)
  467. {
  468. AtkStateSet* stateSet = ATK_OBJECT_CLASS(webkit_accessible_parent_class)->ref_state_set(object);
  469. AccessibilityObject* coreObject = core(object);
  470. if (coreObject == fallbackObject()) {
  471. atk_state_set_add_state(stateSet, ATK_STATE_DEFUNCT);
  472. return stateSet;
  473. }
  474. setAtkStateSetFromCoreObject(coreObject, stateSet);
  475. return stateSet;
  476. }
  477. static AtkRelationSet* webkit_accessible_ref_relation_set(AtkObject* object)
  478. {
  479. AtkRelationSet* relationSet = ATK_OBJECT_CLASS(webkit_accessible_parent_class)->ref_relation_set(object);
  480. AccessibilityObject* coreObject = core(object);
  481. setAtkRelationSetFromCoreObject(coreObject, relationSet);
  482. return relationSet;
  483. }
  484. static void webkit_accessible_init(AtkObject* object, gpointer data)
  485. {
  486. if (ATK_OBJECT_CLASS(webkit_accessible_parent_class)->initialize)
  487. ATK_OBJECT_CLASS(webkit_accessible_parent_class)->initialize(object, data);
  488. WEBKIT_ACCESSIBLE(object)->m_object = reinterpret_cast<AccessibilityObject*>(data);
  489. }
  490. static void webkit_accessible_finalize(GObject* object)
  491. {
  492. // This is a good time to clear the return buffer.
  493. returnString(String());
  494. G_OBJECT_CLASS(webkit_accessible_parent_class)->finalize(object);
  495. }
  496. static void webkit_accessible_class_init(AtkObjectClass* klass)
  497. {
  498. GObjectClass* gobjectClass = G_OBJECT_CLASS(klass);
  499. webkit_accessible_parent_class = g_type_class_peek_parent(klass);
  500. gobjectClass->finalize = webkit_accessible_finalize;
  501. klass->initialize = webkit_accessible_init;
  502. klass->get_name = webkit_accessible_get_name;
  503. klass->get_description = webkit_accessible_get_description;
  504. klass->get_parent = webkit_accessible_get_parent;
  505. klass->get_n_children = webkit_accessible_get_n_children;
  506. klass->ref_child = webkit_accessible_ref_child;
  507. klass->get_role = webkit_accessible_get_role;
  508. klass->ref_state_set = webkit_accessible_ref_state_set;
  509. klass->get_index_in_parent = webkit_accessible_get_index_in_parent;
  510. klass->get_attributes = webkit_accessible_get_attributes;
  511. klass->ref_relation_set = webkit_accessible_ref_relation_set;
  512. }
  513. GType
  514. webkit_accessible_get_type(void)
  515. {
  516. static volatile gsize type_volatile = 0;
  517. if (g_once_init_enter(&type_volatile)) {
  518. static const GTypeInfo tinfo = {
  519. sizeof(WebKitAccessibleClass),
  520. (GBaseInitFunc) 0,
  521. (GBaseFinalizeFunc) 0,
  522. (GClassInitFunc) webkit_accessible_class_init,
  523. (GClassFinalizeFunc) 0,
  524. 0, /* class data */
  525. sizeof(WebKitAccessible), /* instance size */
  526. 0, /* nb preallocs */
  527. (GInstanceInitFunc) 0,
  528. 0 /* value table */
  529. };
  530. GType type = g_type_register_static(ATK_TYPE_OBJECT,
  531. "WebKitAccessible", &tinfo, GTypeFlags(0));
  532. g_once_init_leave(&type_volatile, type);
  533. }
  534. return type_volatile;
  535. }
  536. static gboolean webkit_accessible_action_do_action(AtkAction* action, gint i)
  537. {
  538. g_return_val_if_fail(i == 0, FALSE);
  539. return core(action)->performDefaultAction();
  540. }
  541. static gint webkit_accessible_action_get_n_actions(AtkAction* action)
  542. {
  543. return 1;
  544. }
  545. static const gchar* webkit_accessible_action_get_description(AtkAction* action, gint i)
  546. {
  547. g_return_val_if_fail(i == 0, 0);
  548. // TODO: Need a way to provide/localize action descriptions.
  549. notImplemented();
  550. return "";
  551. }
  552. static const gchar* webkit_accessible_action_get_keybinding(AtkAction* action, gint i)
  553. {
  554. g_return_val_if_fail(i == 0, 0);
  555. // FIXME: Construct a proper keybinding string.
  556. return returnString(core(action)->accessKey().string());
  557. }
  558. static const gchar* webkit_accessible_action_get_name(AtkAction* action, gint i)
  559. {
  560. g_return_val_if_fail(i == 0, 0);
  561. return returnString(core(action)->actionVerb());
  562. }
  563. static void atk_action_interface_init(AtkActionIface* iface)
  564. {
  565. iface->do_action = webkit_accessible_action_do_action;
  566. iface->get_n_actions = webkit_accessible_action_get_n_actions;
  567. iface->get_description = webkit_accessible_action_get_description;
  568. iface->get_keybinding = webkit_accessible_action_get_keybinding;
  569. iface->get_name = webkit_accessible_action_get_name;
  570. }
  571. // Selection (for controls)
  572. static AccessibilityObject* optionFromList(AtkSelection* selection, gint i)
  573. {
  574. AccessibilityObject* coreSelection = core(selection);
  575. if (!coreSelection || i < 0)
  576. return 0;
  577. AccessibilityRenderObject::AccessibilityChildrenVector options = core(selection)->children();
  578. if (i < static_cast<gint>(options.size()))
  579. return options.at(i).get();
  580. return 0;
  581. }
  582. static AccessibilityObject* optionFromSelection(AtkSelection* selection, gint i)
  583. {
  584. // i is the ith selection as opposed to the ith child.
  585. AccessibilityObject* coreSelection = core(selection);
  586. if (!coreSelection || i < 0)
  587. return 0;
  588. AccessibilityRenderObject::AccessibilityChildrenVector selectedItems;
  589. if (coreSelection->isListBox())
  590. static_cast<AccessibilityListBox*>(coreSelection)->selectedChildren(selectedItems);
  591. // TODO: Combo boxes
  592. if (i < static_cast<gint>(selectedItems.size()))
  593. return selectedItems.at(i).get();
  594. return 0;
  595. }
  596. static gboolean webkit_accessible_selection_add_selection(AtkSelection* selection, gint i)
  597. {
  598. AccessibilityObject* option = optionFromList(selection, i);
  599. if (option && core(selection)->isListBox()) {
  600. AccessibilityListBoxOption* listBoxOption = static_cast<AccessibilityListBoxOption*>(option);
  601. listBoxOption->setSelected(true);
  602. return listBoxOption->isSelected();
  603. }
  604. return false;
  605. }
  606. static gboolean webkit_accessible_selection_clear_selection(AtkSelection* selection)
  607. {
  608. AccessibilityObject* coreSelection = core(selection);
  609. if (!coreSelection)
  610. return false;
  611. AccessibilityRenderObject::AccessibilityChildrenVector selectedItems;
  612. if (coreSelection->isListBox()) {
  613. // Set the list of selected items to an empty list; then verify that it worked.
  614. AccessibilityListBox* listBox = static_cast<AccessibilityListBox*>(coreSelection);
  615. listBox->setSelectedChildren(selectedItems);
  616. listBox->selectedChildren(selectedItems);
  617. return selectedItems.size() == 0;
  618. }
  619. return false;
  620. }
  621. static AtkObject* webkit_accessible_selection_ref_selection(AtkSelection* selection, gint i)
  622. {
  623. AccessibilityObject* option = optionFromSelection(selection, i);
  624. if (option) {
  625. AtkObject* child = option->wrapper();
  626. g_object_ref(child);
  627. return child;
  628. }
  629. return 0;
  630. }
  631. static gint webkit_accessible_selection_get_selection_count(AtkSelection* selection)
  632. {
  633. AccessibilityObject* coreSelection = core(selection);
  634. if (coreSelection && coreSelection->isListBox()) {
  635. AccessibilityRenderObject::AccessibilityChildrenVector selectedItems;
  636. static_cast<AccessibilityListBox*>(coreSelection)->selectedChildren(selectedItems);
  637. return static_cast<gint>(selectedItems.size());
  638. }
  639. return 0;
  640. }
  641. static gboolean webkit_accessible_selection_is_child_selected(AtkSelection* selection, gint i)
  642. {
  643. AccessibilityObject* option = optionFromList(selection, i);
  644. if (option && core(selection)->isListBox())
  645. return static_cast<AccessibilityListBoxOption*>(option)->isSelected();
  646. return false;
  647. }
  648. static gboolean webkit_accessible_selection_remove_selection(AtkSelection* selection, gint i)
  649. {
  650. // TODO: This is only getting called if i == 0. What is preventing the rest?
  651. AccessibilityObject* option = optionFromSelection(selection, i);
  652. if (option && core(selection)->isListBox()) {
  653. AccessibilityListBoxOption* listBoxOption = static_cast<AccessibilityListBoxOption*>(option);
  654. listBoxOption->setSelected(false);
  655. return !listBoxOption->isSelected();
  656. }
  657. return false;
  658. }
  659. static gboolean webkit_accessible_selection_select_all_selection(AtkSelection* selection)
  660. {
  661. AccessibilityObject* coreSelection = core(selection);
  662. if (!coreSelection || !coreSelection->isMultiSelectable())
  663. return false;
  664. AccessibilityRenderObject::AccessibilityChildrenVector children = coreSelection->children();
  665. if (coreSelection->isListBox()) {
  666. AccessibilityListBox* listBox = static_cast<AccessibilityListBox*>(coreSelection);
  667. listBox->setSelectedChildren(children);
  668. AccessibilityRenderObject::AccessibilityChildrenVector selectedItems;
  669. listBox->selectedChildren(selectedItems);
  670. return selectedItems.size() == children.size();
  671. }
  672. return false;
  673. }
  674. static void atk_selection_interface_init(AtkSelectionIface* iface)
  675. {
  676. iface->add_selection = webkit_accessible_selection_add_selection;
  677. iface->clear_selection = webkit_accessible_selection_clear_selection;
  678. iface->ref_selection = webkit_accessible_selection_ref_selection;
  679. iface->get_selection_count = webkit_accessible_selection_get_selection_count;
  680. iface->is_child_selected = webkit_accessible_selection_is_child_selected;
  681. iface->remove_selection = webkit_accessible_selection_remove_selection;
  682. iface->select_all_selection = webkit_accessible_selection_select_all_selection;
  683. }
  684. // Text
  685. static gchar* utf8Substr(const gchar* string, gint start, gint end)
  686. {
  687. ASSERT(string);
  688. glong strLen = g_utf8_strlen(string, -1);
  689. if (start > strLen || end > strLen)
  690. return 0;
  691. gchar* startPtr = g_utf8_offset_to_pointer(string, start);
  692. gsize lenInBytes = g_utf8_offset_to_pointer(string, end) - startPtr + 1;
  693. gchar* output = static_cast<gchar*>(g_malloc0(lenInBytes + 1));
  694. return g_utf8_strncpy(output, startPtr, end - start + 1);
  695. }
  696. // This function is not completely general, is it's tied to the
  697. // internals of WebCore's text presentation.
  698. static gchar* convertUniCharToUTF8(const UChar* characters, gint length, int from, int to)
  699. {
  700. CString stringUTF8 = UTF8Encoding().encode(characters, length, QuestionMarksForUnencodables);
  701. gchar* utf8String = utf8Substr(stringUTF8.data(), from, to);
  702. if (!g_utf8_validate(utf8String, -1, 0)) {
  703. g_free(utf8String);
  704. return 0;
  705. }
  706. gsize len = strlen(utf8String);
  707. GString* ret = g_string_new_len(0, len);
  708. gchar* ptr = utf8String;
  709. // WebCore introduces line breaks in the text that do not reflect
  710. // the layout you see on the screen, replace them with spaces
  711. while (len > 0) {
  712. gint index, start;
  713. pango_find_paragraph_boundary(ptr, len, &index, &start);
  714. g_string_append_len(ret, ptr, index);
  715. if (index == start)
  716. break;
  717. g_string_append_c(ret, ' ');
  718. ptr += start;
  719. len -= start;
  720. }
  721. g_free(utf8String);
  722. return g_string_free(ret, FALSE);
  723. }
  724. gchar* textForObject(AccessibilityRenderObject* accObject)
  725. {
  726. GString* str = g_string_new(0);
  727. // For text controls, we can get the text line by line.
  728. if (accObject->isTextControl()) {
  729. unsigned textLength = accObject->textLength();
  730. int lineNumber = 0;
  731. PlainTextRange range = accObject->doAXRangeForLine(lineNumber);
  732. while (range.length) {
  733. // When a line of text wraps in a text area, the final space is removed.
  734. if (range.start + range.length < textLength)
  735. range.length -= 1;
  736. String lineText = accObject->doAXStringForRange(range);
  737. g_string_append(str, lineText.utf8().data());
  738. g_string_append(str, "\n");
  739. range = accObject->doAXRangeForLine(++lineNumber);
  740. }
  741. } else if (accObject->renderer()) {
  742. // For RenderBlocks, piece together the text from the RenderText objects they contain.
  743. for (RenderObject* obj = accObject->renderer()->firstChild(); obj; obj = obj->nextSibling()) {
  744. if (obj->isBR()) {
  745. g_string_append(str, "\n");
  746. continue;
  747. }
  748. RenderText* renderText;
  749. if (obj->isText())
  750. renderText = toRenderText(obj);
  751. else if (obj->firstChild() && obj->firstChild()->isText()) {
  752. // Handle RenderInlines (and any other similiar RenderObjects).
  753. renderText = toRenderText(obj->firstChild());
  754. } else
  755. continue;
  756. InlineTextBox* box = renderText->firstTextBox();
  757. while (box) {
  758. gchar* text = convertUniCharToUTF8(renderText->characters(), renderText->textLength(), box->start(), box->end());
  759. g_string_append(str, text);
  760. // Newline chars in the source result in separate text boxes, so check
  761. // before adding a newline in the layout. See bug 25415 comment #78.
  762. // If the next sibling is a BR, we'll add the newline when we examine that child.
  763. if (!box->nextOnLineExists() && (!obj->nextSibling() || !obj->nextSibling()->isBR()))
  764. g_string_append(str, "\n");
  765. box = box->nextTextBox();
  766. }
  767. }
  768. }
  769. return g_string_free(str, FALSE);
  770. }
  771. static gchar* webkit_accessible_text_get_text(AtkText* text, gint startOffset, gint endOffset)
  772. {
  773. AccessibilityObject* coreObject = core(text);
  774. String ret;
  775. unsigned start = startOffset;
  776. if (endOffset == -1) {
  777. endOffset = coreObject->stringValue().length();
  778. if (!endOffset)
  779. endOffset = coreObject->textUnderElement().length();
  780. }
  781. int length = endOffset - startOffset;
  782. if (coreObject->isTextControl())
  783. ret = coreObject->doAXStringForRange(PlainTextRange(start, length));
  784. else
  785. ret = coreObject->textUnderElement().substring(start, length);
  786. if (!ret.length()) {
  787. // This can happen at least with anonymous RenderBlocks (e.g. body text amongst paragraphs)
  788. ret = String(textForObject(static_cast<AccessibilityRenderObject*>(coreObject)));
  789. if (!endOffset)
  790. endOffset = ret.length();
  791. ret = ret.substring(start, endOffset - startOffset);
  792. }
  793. return g_strdup(ret.utf8().data());
  794. }
  795. static GailTextUtil* getGailTextUtilForAtk(AtkText* textObject)
  796. {
  797. gpointer data = g_object_get_data(G_OBJECT(textObject), "webkit-accessible-gail-text-util");
  798. if (data)
  799. return static_cast<GailTextUtil*>(data);
  800. GailTextUtil* gailTextUtil = gail_text_util_new();
  801. gail_text_util_text_setup(gailTextUtil, webkit_accessible_text_get_text(textObject, 0, -1));
  802. g_object_set_data_full(G_OBJECT(textObject), "webkit-accessible-gail-text-util", gailTextUtil, g_object_unref);
  803. return gailTextUtil;
  804. }
  805. static PangoLayout* getPangoLayoutForAtk(AtkText* textObject)
  806. {
  807. AccessibilityObject* coreObject = core(textObject);
  808. HostWindow* hostWindow = coreObject->document()->view()->hostWindow();
  809. if (!hostWindow)
  810. return 0;
  811. PlatformPageClient webView = hostWindow->platformPageClient();
  812. if (!webView)
  813. return 0;
  814. AccessibilityRenderObject* accObject = static_cast<AccessibilityRenderObject*>(coreObject);
  815. if (!accObject)
  816. return 0;
  817. // Create a string with the layout as it appears on the screen
  818. PangoLayout* layout = gtk_widget_create_pango_layout(static_cast<GtkWidget*>(webView), textForObject(accObject));
  819. g_object_set_data_full(G_OBJECT(textObject), "webkit-accessible-pango-layout", layout, g_object_unref);
  820. return layout;
  821. }
  822. static gchar* webkit_accessible_text_get_text_after_offset(AtkText* text, gint offset, AtkTextBoundary boundaryType, gint* startOffset, gint* endOffset)
  823. {
  824. return gail_text_util_get_text(getGailTextUtilForAtk(text), getPangoLayoutForAtk(text), GAIL_AFTER_OFFSET, boundaryType, offset, startOffset, endOffset);
  825. }
  826. static gchar* webkit_accessible_text_get_text_at_offset(AtkText* text, gint offset, AtkTextBoundary boundaryType, gint* startOffset, gint* endOffset)
  827. {
  828. return gail_text_util_get_text(getGailTextUtilForAtk(text), getPangoLayoutForAtk(text), GAIL_AT_OFFSET, boundaryType, offset, startOffset, endOffset);
  829. }
  830. static gchar* webkit_accessible_text_get_text_before_offset(AtkText* text, gint offset, AtkTextBoundary boundaryType, gint* startOffset, gint* endOffset)
  831. {
  832. return gail_text_util_get_text(getGailTextUtilForAtk(text), getPangoLayoutForAtk(text), GAIL_BEFORE_OFFSET, boundaryType, offset, startOffset, endOffset);
  833. }
  834. static gunichar webkit_accessible_text_get_character_at_offset(AtkText* text, gint offset)
  835. {
  836. notImplemented();
  837. return 0;
  838. }
  839. static gint webkit_accessible_text_get_caret_offset(AtkText* text)
  840. {
  841. // coreObject is the unignored object whose offset the caller is requesting.
  842. // focusedObject is the object with the caret. It is likely ignored -- unless it's a link.
  843. AccessibilityObject* coreObject = core(text);
  844. RenderObject* focusedNode = coreObject->selection().end().node()->renderer();
  845. AccessibilityObject* focusedObject = coreObject->document()->axObjectCache()->getOrCreate(focusedNode);
  846. int offset;
  847. // Don't ignore links if the offset is being requested for a link.
  848. objectAndOffsetUnignored(focusedObject, offset, !coreObject->isLink());
  849. // TODO: Verify this for RTL text.
  850. return offset;
  851. }
  852. static AtkAttributeSet* webkit_accessible_text_get_run_attributes(AtkText* text, gint offset, gint* start_offset, gint* end_offset)
  853. {
  854. notImplemented();
  855. return 0;
  856. }
  857. static AtkAttributeSet* webkit_accessible_text_get_default_attributes(AtkText* text)
  858. {
  859. notImplemented();
  860. return 0;
  861. }
  862. static void webkit_accessible_text_get_character_extents(AtkText* text, gint offset, gint* x, gint* y, gint* width, gint* height, AtkCoordType coords)
  863. {
  864. IntRect extents = core(text)->doAXBoundsForRange(PlainTextRange(offset, 1));
  865. // FIXME: Use the AtkCoordType
  866. // Requires WebCore::ScrollView::contentsToScreen() to be implemented
  867. #if 0
  868. switch(coords) {
  869. case ATK_XY_SCREEN:
  870. extents = core(text)->document()->view()->contentsToScreen(extents);
  871. break;
  872. case ATK_XY_WINDOW:
  873. // No-op
  874. break;
  875. }
  876. #endif
  877. *x = extents.x();
  878. *y = extents.y();
  879. *width = extents.width();
  880. *height = extents.height();
  881. }
  882. static gint webkit_accessible_text_get_character_count(AtkText* text)
  883. {
  884. AccessibilityObject* coreObject = core(text);
  885. if (coreObject->isTextControl())
  886. return coreObject->textLength();
  887. else
  888. return coreObject->textUnderElement().length();
  889. }
  890. static gint webkit_accessible_text_get_offset_at_point(AtkText* text, gint x, gint y, AtkCoordType coords)
  891. {
  892. // FIXME: Use the AtkCoordType
  893. // TODO: Is it correct to ignore range.length?
  894. IntPoint pos(x, y);
  895. PlainTextRange range = core(text)->doAXRangeForPosition(pos);
  896. return range.start;
  897. }
  898. static bool selectionBelongsToObject(AccessibilityObject* coreObject, VisibleSelection& selection)
  899. {
  900. if (!coreObject->isAccessibilityRenderObject())
  901. return false;
  902. Node* node = static_cast<AccessibilityRenderObject*>(coreObject)->renderer()->node();
  903. return node == selection.base().containerNode();
  904. }
  905. static gint webkit_accessible_text_get_n_selections(AtkText* text)
  906. {
  907. AccessibilityObject* coreObject = core(text);
  908. VisibleSelection selection = coreObject->selection();
  909. // We don't support multiple selections for now, so there's only
  910. // two possibilities
  911. // Also, we don't want to do anything if the selection does not
  912. // belong to the currently selected object. We have to check since
  913. // there's no way to get the selection for a given object, only
  914. // the global one (the API is a bit confusing)
  915. return !selectionBelongsToObject(coreObject, selection) || selection.isNone() ? 0 : 1;
  916. }
  917. static gchar* webkit_accessible_text_get_selection(AtkText* text, gint selection_num, gint* start_offset, gint* end_offset)
  918. {
  919. AccessibilityObject* coreObject = core(text);
  920. VisibleSelection selection = coreObject->selection();
  921. // WebCore does not support multiple selection, so anything but 0 does not make sense for now.
  922. // Also, we don't want to do anything if the selection does not
  923. // belong to the currently selected object. We have to check since
  924. // there's no way to get the selection for a given object, only
  925. // the global one (the API is a bit confusing)
  926. if (selection_num != 0 || !selectionBelongsToObject(coreObject, selection)) {
  927. *start_offset = *end_offset = 0;
  928. return 0;
  929. }
  930. *start_offset = selection.start().offsetInContainerNode();
  931. *end_offset = selection.end().offsetInContainerNode();
  932. return webkit_accessible_text_get_text(text, *start_offset, *end_offset);
  933. }
  934. static gboolean webkit_accessible_text_add_selection(AtkText* text, gint start_offset, gint end_offset)
  935. {
  936. notImplemented();
  937. return FALSE;
  938. }
  939. static gboolean webkit_accessible_text_remove_selection(AtkText* text, gint selection_num)
  940. {
  941. notImplemented();
  942. return FALSE;
  943. }
  944. static gboolean webkit_accessible_text_set_selection(AtkText* text, gint selection_num, gint start_offset, gint end_offset)
  945. {
  946. notImplemented();
  947. return FALSE;
  948. }
  949. static gboolean webkit_accessible_text_set_caret_offset(AtkText* text, gint offset)
  950. {
  951. AccessibilityObject* coreObject = core(text);
  952. // FIXME: We need to reimplement visiblePositionRangeForRange here
  953. // because the actual function checks the offset is within the
  954. // boundaries of text().length(), but text() only works for text
  955. // controls...
  956. VisiblePosition startPosition = coreObject->visiblePositionForIndex(offset);
  957. startPosition.setAffinity(DOWNSTREAM);
  958. VisiblePosition endPosition = coreObject->visiblePositionForIndex(offset);
  959. VisiblePositionRange range = VisiblePositionRange(startPosition, endPosition);
  960. coreObject->setSelectedVisiblePositionRange(range);
  961. return TRUE;
  962. }
  963. static void atk_text_interface_init(AtkTextIface* iface)
  964. {
  965. iface->get_text = webkit_accessible_text_get_text;
  966. iface->get_text_after_offset = webkit_accessible_text_get_text_after_offset;
  967. iface->get_text_at_offset = webkit_accessible_text_get_text_at_offset;
  968. iface->get_character_at_offset = webkit_accessible_text_get_character_at_offset;
  969. iface->get_text_before_offset = webkit_accessible_text_get_text_before_offset;
  970. iface->get_caret_offset = webkit_accessible_text_get_caret_offset;
  971. iface->get_run_attributes = webkit_accessible_text_get_run_attributes;
  972. iface->get_default_attributes = webkit_accessible_text_get_default_attributes;
  973. iface->get_character_extents = webkit_accessible_text_get_character_extents;
  974. iface->get_character_count = webkit_accessible_text_get_character_count;
  975. iface->get_offset_at_point = webkit_accessible_text_get_offset_at_point;
  976. iface->get_n_selections = webkit_accessible_text_get_n_selections;
  977. iface->get_selection = webkit_accessible_text_get_selection;
  978. // set methods
  979. iface->add_selection = webkit_accessible_text_add_selection;
  980. iface->remove_selection = webkit_accessible_text_remove_selection;
  981. iface->set_selection = webkit_accessible_text_set_selection;
  982. iface->set_caret_offset = webkit_accessible_text_set_caret_offset;
  983. }
  984. // EditableText
  985. static gboolean webkit_accessible_editable_text_set_run_attributes(AtkEditableText* text, AtkAttributeSet* attrib_set, gint start_offset, gint end_offset)
  986. {
  987. notImplemented();
  988. return FALSE;
  989. }
  990. static void webkit_accessible_editable_text_set_text_contents(AtkEditableText* text, const gchar* string)
  991. {
  992. // FIXME: string nullcheck?
  993. core(text)->setValue(String::fromUTF8(string));
  994. }
  995. static void webkit_accessible_editable_text_insert_text(AtkEditableText* text, const gchar* string, gint length, gint* position)
  996. {
  997. // FIXME: string nullcheck?
  998. AccessibilityObject* coreObject = core(text);
  999. // FIXME: Not implemented in WebCore
  1000. //coreObject->setSelectedTextRange(PlainTextRange(*position, 0));
  1001. //coreObject->setSelectedText(String::fromUTF8(string));
  1002. if (!coreObject->document() || !coreObject->document()->frame())
  1003. return;
  1004. coreObject->setSelectedVisiblePositionRange(coreObject->visiblePositionRangeForRange(PlainTextRange(*position, 0)));
  1005. coreObject->setFocused(true);
  1006. // FIXME: We should set position to the actual inserted text length, which may be less than that requested.
  1007. if (coreObject->document()->frame()->editor()->insertTextWithoutSendingTextEvent(String::fromUTF8(string), false, 0))
  1008. *position += length;
  1009. }
  1010. static void webkit_accessible_editable_text_copy_text(AtkEditableText* text, gint start_pos, gint end_pos)
  1011. {
  1012. notImplemented();
  1013. }
  1014. static void webkit_accessible_editable_text_cut_text(AtkEditableText* text, gint start_pos, gint end_pos)
  1015. {
  1016. notImplemented();
  1017. }
  1018. static void webkit_accessible_editable_text_delete_text(AtkEditableText* text, gint start_pos, gint end_pos)
  1019. {
  1020. AccessibilityObject* coreObject = core(text);
  1021. // FIXME: Not implemented in WebCore
  1022. //coreObject->setSelectedTextRange(PlainTextRange(start_pos, end_pos - start_pos));
  1023. //coreObject->setSelectedText(String());
  1024. if (!coreObject->document() || !coreObject->document()->frame())
  1025. return;
  1026. coreObject->setSelectedVisiblePositionRange(coreObject->visiblePositionRangeForRange(PlainTextRange(start_pos, end_pos - start_pos)));
  1027. coreObject->setFocused(true);
  1028. coreObject->document()->frame()->editor()->performDelete();
  1029. }
  1030. static void webkit_accessible_editable_text_paste_text(AtkEditableText* text, gint position)
  1031. {
  1032. notImplemented();
  1033. }
  1034. static void atk_editable_text_interface_init(AtkEditableTextIface* iface)
  1035. {
  1036. iface->set_run_attributes = webkit_accessible_editable_text_set_run_attributes;
  1037. iface->set_text_contents = webkit_accessible_editable_text_set_text_contents;
  1038. iface->insert_text = webkit_accessible_editable_text_insert_text;
  1039. iface->copy_text = webkit_accessible_editable_text_copy_text;
  1040. iface->cut_text = webkit_accessible_editable_text_cut_text;
  1041. iface->delete_text = webkit_accessible_editable_text_delete_text;
  1042. iface->paste_text = webkit_accessible_editable_text_paste_text;
  1043. }
  1044. static void contentsToAtk(AccessibilityObject* coreObject, AtkCoordType coordType, IntRect rect, gint* x, gint* y, gint* width = 0, gint* height = 0)
  1045. {
  1046. FrameView* frameView = coreObject->documentFrameView();
  1047. if (frameView) {
  1048. switch (coordType) {
  1049. case ATK_XY_WINDOW:
  1050. rect = frameView->contentsToWindow(rect);
  1051. break;
  1052. case ATK_XY_SCREEN:
  1053. rect = frameView->contentsToScreen(rect);
  1054. break;
  1055. }
  1056. }
  1057. if (x)
  1058. *x = rect.x();
  1059. if (y)
  1060. *y = rect.y();
  1061. if (width)
  1062. *width = rect.width();
  1063. if (height)
  1064. *height = rect.height();
  1065. }
  1066. static IntPoint atkToContents(AccessibilityObject* coreObject, AtkCoordType coordType, gint x, gint y)
  1067. {
  1068. IntPoint pos(x, y);
  1069. FrameView* frameView = coreObject->documentFrameView();
  1070. if (frameView) {
  1071. switch (coordType) {
  1072. case ATK_XY_SCREEN:
  1073. return frameView->screenToContents(pos);
  1074. case ATK_XY_WINDOW:
  1075. return frameView->windowToContents(pos);
  1076. }
  1077. }
  1078. return pos;
  1079. }
  1080. static AtkObject* webkit_accessible_component_ref_accessible_at_point(AtkComponent* component, gint x, gint y, AtkCoordType coordType)
  1081. {
  1082. IntPoint pos = atkToContents(core(component), coordType, x, y);
  1083. AccessibilityObject* target = core(component)->doAccessibilityHitTest(pos);
  1084. if (!target)
  1085. return 0;
  1086. g_object_ref(target->wrapper());
  1087. return target->wrapper();
  1088. }
  1089. static void webkit_accessible_component_get_extents(AtkComponent* component, gint* x, gint* y, gint* width, gint* height, AtkCoordType coordType)
  1090. {
  1091. IntRect rect = core(component)->elementRect();
  1092. contentsToAtk(core(component), coordType, rect, x, y, width, height);
  1093. }
  1094. static gboolean webkit_accessible_component_grab_focus(AtkComponent* component)
  1095. {
  1096. core(component)->setFocused(true);
  1097. return core(component)->isFocused();
  1098. }
  1099. static void atk_component_interface_init(AtkComponentIface* iface)
  1100. {
  1101. iface->ref_accessible_at_point = webkit_accessible_component_ref_accessible_at_point;
  1102. iface->get_extents = webkit_accessible_component_get_extents;
  1103. iface->grab_focus = webkit_accessible_component_grab_focus;
  1104. }
  1105. // Image
  1106. static void webkit_accessible_image_get_image_position(AtkImage* image, gint* x, gint* y, AtkCoordType coordType)
  1107. {
  1108. IntRect rect = core(image)->elementRect();
  1109. contentsToAtk(core(image), coordType, rect, x, y);
  1110. }
  1111. static const gchar* webkit_accessible_image_get_image_description(AtkImage* image)
  1112. {
  1113. return returnString(core(image)->accessibilityDescription());
  1114. }
  1115. static void webkit_accessible_image_get_image_size(AtkImage* image, gint* width, gint* height)
  1116. {
  1117. IntSize size = core(image)->size();
  1118. if (width)
  1119. *width = size.width();
  1120. if (height)
  1121. *height = size.height();
  1122. }
  1123. static void atk_image_interface_init(AtkImageIface* iface)
  1124. {
  1125. iface->get_image_position = webkit_accessible_image_get_image_position;
  1126. iface->get_image_description = webkit_accessible_image_get_image_description;
  1127. iface->get_image_size = webkit_accessible_image_get_image_size;
  1128. }
  1129. // Table
  1130. static AccessibilityTableCell* cell(AtkTable* table, guint row, guint column)
  1131. {
  1132. AccessibilityObject* accTable = core(table);
  1133. if (accTable->isAccessibilityRenderObject())
  1134. return static_cast<AccessibilityTable*>(accTable)->cellForColumnAndRow(column, row);
  1135. return 0;
  1136. }
  1137. static gint cellIndex(AccessibilityTableCell* axCell, AccessibilityTable* axTable)
  1138. {
  1139. // Calculate the cell's index as if we had a traditional Gtk+ table in
  1140. // which cells are all direct children of the table, arranged row-first.
  1141. AccessibilityObject::AccessibilityChildrenVector allCells;
  1142. axTable->cells(allCells);
  1143. AccessibilityObject::AccessibilityChildrenVector::iterator position;
  1144. position = std::find(allCells.begin(), allCells.end(), axCell);
  1145. if (position == allCells.end())
  1146. return -1;
  1147. return position - allCells.begin();
  1148. }
  1149. static AccessibilityTableCell* cellAtIndex(AtkTable* table, gint index)
  1150. {
  1151. AccessibilityObject* accTable = core(table);
  1152. if (accTable->isAccessibilityRenderObject()) {
  1153. AccessibilityObject::AccessibilityChildrenVector allCells;
  1154. static_cast<AccessibilityTable*>(accTable)->cells(allCells);
  1155. if (0 <= index && static_cast<unsigned>(index) < allCells.size()) {
  1156. AccessibilityObject* accCell = allCells.at(index).get();
  1157. return static_cast<AccessibilityTableCell*>(accCell);
  1158. }
  1159. }
  1160. return 0;
  1161. }
  1162. static AtkObject* webkit_accessible_table_ref_at(AtkTable* table, gint row, gint column)
  1163. {
  1164. AccessibilityTableCell* axCell = cell(table, row, column);
  1165. if (!axCell)
  1166. return 0;
  1167. return axCell->wrapper();
  1168. }
  1169. static gint webkit_accessible_table_get_index_at(AtkTable* table, gint row, gint column)
  1170. {
  1171. AccessibilityTableCell* axCell = cell(table, row, column);
  1172. AccessibilityTable* axTable = static_cast<AccessibilityTable*>(core(table));
  1173. return cellIndex(axCell, axTable);
  1174. }
  1175. static gint webkit_accessible_table_get_column_at_index(AtkTable* table, gint index)
  1176. {
  1177. AccessibilityTableCell* axCell = cellAtIndex(table, index);
  1178. if (axCell){
  1179. pair<int, int> columnRange;
  1180. axCell->columnIndexRange(columnRange);
  1181. return columnRange.first;
  1182. }
  1183. return -1;
  1184. }
  1185. static gint webkit_accessible_table_get_row_at_index(AtkTable* table, gint index)
  1186. {
  1187. AccessibilityTableCell* axCell = cellAtIndex(table, index);
  1188. if (axCell){
  1189. pair<int, int> rowRange;
  1190. axCell->rowIndexRange(rowRange);
  1191. return rowRange.first;
  1192. }
  1193. return -1;
  1194. }
  1195. static gint webkit_accessible_table_get_n_columns(AtkTable* table)
  1196. {
  1197. AccessibilityObject* accTable = core(table);
  1198. if (accTable->isAccessibilityRenderObject())
  1199. return static_cast<AccessibilityTable*>(accTable)->columnCount();
  1200. return 0;
  1201. }
  1202. static gint webkit_accessible_table_get_n_rows(AtkTable* table)
  1203. {
  1204. AccessibilityObject* accTable = core(table);
  1205. if (accTable->isAccessibilityRenderObject())
  1206. return static_cast<AccessibilityTable*>(accTable)->rowCount();
  1207. return 0;
  1208. }
  1209. static gint webkit_accessible_table_get_column_extent_at(AtkTable* table, gint row, gint column)
  1210. {
  1211. AccessibilityTableCell* axCell = cell(table, row, column);
  1212. if (axCell) {
  1213. pair<int, int> columnRange;
  1214. axCell->columnIndexRange(columnRange);
  1215. return columnRange.second;
  1216. }
  1217. return 0;
  1218. }
  1219. static gint webkit_accessible_table_get_row_extent_at(AtkTable* table, gint row, gint column)
  1220. {
  1221. AccessibilityTableCell* axCell = cell(table, row, column);
  1222. if (axCell) {
  1223. pair<int, int> rowRange;
  1224. axCell->rowIndexRange(rowRange);
  1225. return rowRange.second;
  1226. }
  1227. return 0;
  1228. }
  1229. static AtkObject* webkit_accessible_table_get_column_header(AtkTable* table, gint column)
  1230. {
  1231. // FIXME: This needs to be implemented.
  1232. notImplemented();
  1233. return 0;
  1234. }
  1235. static AtkObject* webkit_accessible_table_get_row_header(AtkTable* table, gint row)
  1236. {
  1237. AccessibilityObject* accTable = core(table);
  1238. if (accTable->isAccessibilityRenderObject()) {
  1239. AccessibilityObject::AccessibilityChildrenVector allRowHeaders;
  1240. static_cast<AccessibilityTable*>(accTable)->rowHeaders(allRowHeaders);
  1241. unsigned rowCount = allRowHeaders.size();
  1242. for (unsigned k = 0; k < rowCount; ++k) {
  1243. AccessibilityObject* rowObject = allRowHeaders[k]->parentObject();
  1244. if (static_cast<AccessibilityTableRow*>(rowObject)->rowIndex() == row)
  1245. return allRowHeaders[k]->wrapper();
  1246. }
  1247. }
  1248. return 0;
  1249. }
  1250. static AtkObject* webkit_accessible_table_get_caption(AtkTable* table)
  1251. {
  1252. AccessibilityObject* accTable = core(table);
  1253. if (accTable->isAccessibilityRenderObject()) {
  1254. Node* node = static_cast<AccessibilityRenderObject*>(accTable)->renderer()->node();
  1255. if (node && node->hasTagName(HTMLNames::tableTag)) {
  1256. HTMLTableCaptionElement* caption = static_cast<HTMLTableElement*>(node)->caption();
  1257. if (caption)
  1258. return AccessibilityObject::firstAccessibleObjectFromNode(caption->renderer()->node())->wrapper();
  1259. }
  1260. }
  1261. return 0;
  1262. }
  1263. static const gchar* webkit_accessible_table_get_column_description(AtkTable* table, gint column)
  1264. {
  1265. AtkObject* columnHeader = atk_table_get_column_header(table, column);
  1266. if (columnHeader)
  1267. return returnString(nameFromChildren(core(columnHeader)));
  1268. return 0;
  1269. }
  1270. static const gchar* webkit_accessible_table_get_row_description(AtkTable* table, gint row)
  1271. {
  1272. AtkObject* rowHeader = atk_table_get_row_header(table, row);
  1273. if (rowHeader)
  1274. return returnString(nameFromChildren(core(rowHeader)));
  1275. return 0;
  1276. }
  1277. static void atk_table_interface_init(AtkTableIface* iface)
  1278. {
  1279. iface->ref_at = webkit_accessible_table_ref_at;
  1280. iface->get_index_at = webkit_accessible_table_get_index_at;
  1281. iface->get_column_at_index = webkit_accessible_table_get_column_at_index;
  1282. iface->get_row_at_index = webkit_accessible_table_get_row_at_index;
  1283. iface->get_n_columns = webkit_accessible_table_get_n_columns;
  1284. iface->get_n_rows = webkit_accessible_table_get_n_rows;
  1285. iface->get_column_extent_at = webkit_accessible_table_get_column_extent_at;
  1286. iface->get_row_extent_at = webkit_accessible_table_get_row_extent_at;
  1287. iface->get_column_header = webkit_accessible_table_get_column_header;
  1288. iface->get_row_header = webkit_accessible_table_get_row_header;
  1289. iface->get_caption = webkit_accessible_table_get_caption;
  1290. iface->get_column_description = webkit_accessible_table_get_column_description;
  1291. iface->get_row_description = webkit_accessible_table_get_row_description;
  1292. }
  1293. static const gchar* documentAttributeValue(AtkDocument* document, const gchar* attribute)
  1294. {
  1295. Document* coreDocument = core(document)->document();
  1296. if (!coreDocument)
  1297. return 0;
  1298. String value = String();
  1299. if (!g_ascii_strcasecmp(attribute, "DocType") && coreDocument->doctype())
  1300. value = coreDocument->doctype()->name();
  1301. else if (!g_ascii_strcasecmp(attribute, "Encoding"))
  1302. value = coreDocument->charset();
  1303. else if (!g_ascii_strcasecmp(attribute, "URI"))
  1304. value = coreDocument->documentURI();
  1305. if (!value.isEmpty())
  1306. return returnString(value);
  1307. return 0;
  1308. }
  1309. static const gchar* webkit_accessible_document_get_attribute_value(AtkDocument* document, const gchar* attribute)
  1310. {
  1311. return documentAttributeValue(document, attribute);
  1312. }
  1313. static AtkAttributeSet* webkit_accessible_document_get_attributes(AtkDocument* document)
  1314. {
  1315. AtkAttributeSet* attributeSet = 0;
  1316. const gchar* attributes [] = {"DocType", "Encoding", "URI"};
  1317. for (unsigned i = 0; i < G_N_ELEMENTS(attributes); i++) {
  1318. const gchar* value = documentAttributeValue(document, attributes[i]);
  1319. if (value)
  1320. attributeSet = addAttributeToSet(attributeSet, attributes[i], value);
  1321. }
  1322. return attributeSet;
  1323. }
  1324. static const gchar* webkit_accessible_document_get_locale(AtkDocument* document)
  1325. {
  1326. // TODO: Should we fall back on lang xml:lang when the following comes up empty?
  1327. String language = static_cast<AccessibilityRenderObject*>(core(document))->language();
  1328. if (!language.isEmpty())
  1329. return returnString(language);
  1330. return 0;
  1331. }
  1332. static void atk_document_interface_init(AtkDocumentIface* iface)
  1333. {
  1334. iface->get_document_attribute_value = webkit_accessible_document_get_attribute_value;
  1335. iface->get_document_attributes = webkit_accessible_document_get_attributes;
  1336. iface->get_document_locale = webkit_accessible_document_get_locale;
  1337. }
  1338. static const GInterfaceInfo AtkInterfacesInitFunctions[] = {
  1339. {(GInterfaceInitFunc)atk_action_interface_init,
  1340. (GInterfaceFinalizeFunc) 0, 0},
  1341. {(GInterfaceInitFunc)atk_selection_interface_init,
  1342. (GInterfaceFinalizeFunc) 0, 0},
  1343. {(GInterfaceInitFunc)atk_editable_text_interface_init,
  1344. (GInterfaceFinalizeFunc) 0, 0},
  1345. {(GInterfaceInitFunc)atk_text_interface_init,
  1346. (GInterfaceFinalizeFunc) 0, 0},
  1347. {(GInterfaceInitFunc)atk_component_interface_init,
  1348. (GInterfaceFinalizeFunc) 0, 0},
  1349. {(GInterfaceInitFunc)atk_image_interface_init,
  1350. (GInterfaceFinalizeFunc) 0, 0},
  1351. {(GInterfaceInitFunc)atk_table_interface_init,
  1352. (GInterfaceFinalizeFunc) 0, 0},
  1353. {(GInterfaceInitFunc)atk_document_interface_init,
  1354. (GInterfaceFinalizeFunc) 0, 0}
  1355. };
  1356. enum WAIType {
  1357. WAI_ACTION,
  1358. WAI_SELECTION,
  1359. WAI_EDITABLE_TEXT,
  1360. WAI_TEXT,
  1361. WAI_COMPONENT,
  1362. WAI_IMAGE,
  1363. WAI_TABLE,
  1364. WAI_DOCUMENT
  1365. };
  1366. static GType GetAtkInterfaceTypeFromWAIType(WAIType type)
  1367. {
  1368. switch (type) {
  1369. case WAI_ACTION:
  1370. return ATK_TYPE_ACTION;
  1371. case WAI_SELECTION:
  1372. return ATK_TYPE_SELECTION;
  1373. case WAI_EDITABLE_TEXT:
  1374. return ATK_TYPE_EDITABLE_TEXT;
  1375. case WAI_TEXT:
  1376. return ATK_TYPE_TEXT;
  1377. case WAI_COMPONENT:
  1378. return ATK_TYPE_COMPONENT;
  1379. case WAI_IMAGE:
  1380. return ATK_TYPE_IMAGE;
  1381. case WAI_TABLE:
  1382. return ATK_TYPE_TABLE;
  1383. case WAI_DOCUMENT:
  1384. return ATK_TYPE_DOCUMENT;
  1385. }
  1386. return G_TYPE_INVALID;
  1387. }
  1388. static guint16 getInterfaceMaskFromObject(AccessibilityObject* coreObject)
  1389. {
  1390. guint16 interfaceMask = 0;
  1391. // Component interface is always supported
  1392. interfaceMask |= 1 << WAI_COMPONENT;
  1393. // Action
  1394. if (!coreObject->actionVerb().isEmpty())
  1395. interfaceMask |= 1 << WAI_ACTION;
  1396. // Selection
  1397. if (coreObject->isListBox())
  1398. interfaceMask |= 1 << WAI_SELECTION;
  1399. // Text & Editable Text
  1400. AccessibilityRole role = coreObject->roleValue();
  1401. if (role == StaticTextRole)
  1402. interfaceMask |= 1 << WAI_TEXT;
  1403. else if (coreObject->isAccessibilityRenderObject())
  1404. if (coreObject->isTextControl()) {
  1405. interfaceMask |= 1 << WAI_TEXT;
  1406. if (!coreObject->isReadOnly())
  1407. interfaceMask |= 1 << WAI_EDITABLE_TEXT;
  1408. } else if (static_cast<AccessibilityRenderObject*>(coreObject)->renderer()->childrenInline())
  1409. interfaceMask |= 1 << WAI_TEXT;
  1410. // Image
  1411. if (coreObject->isImage())
  1412. interfaceMask |= 1 << WAI_IMAGE;
  1413. // Table
  1414. if (role == TableRole)
  1415. interfaceMask |= 1 << WAI_TABLE;
  1416. // Document
  1417. if (role == WebAreaRole)
  1418. interfaceMask |= 1 << WAI_DOCUMENT;
  1419. return interfaceMask;
  1420. }
  1421. static const char* getUniqueAccessibilityTypeName(guint16 interfaceMask)
  1422. {
  1423. #define WAI_TYPE_NAME_LEN (30) /* Enough for prefix + 5 hex characters (max) */
  1424. static char name[WAI_TYPE_NAME_LEN + 1];
  1425. g_sprintf(name, "WAIType%x", interfaceMask);
  1426. name[WAI_TYPE_NAME_LEN] = '\0';
  1427. return name;
  1428. }
  1429. static GType getAccessibilityTypeFromObject(AccessibilityObject* coreObject)
  1430. {
  1431. static const GTypeInfo typeInfo = {
  1432. sizeof(WebKitAccessibleClass),
  1433. (GBaseInitFunc) 0,
  1434. (GBaseFinalizeFunc) 0,
  1435. (GClassInitFunc) 0,
  1436. (GClassFinalizeFunc) 0,
  1437. 0, /* class data */
  1438. sizeof(WebKitAccessible), /* instance size */
  1439. 0, /* nb preallocs */
  1440. (GInstanceInitFunc) 0,
  1441. 0 /* value table */
  1442. };
  1443. guint16 interfaceMask = getInterfaceMaskFromObject(coreObject);
  1444. const char* atkTypeName = getUniqueAccessibilityTypeName(interfaceMask);
  1445. GType type = g_type_from_name(atkTypeName);
  1446. if (type)
  1447. return type;
  1448. type = g_type_register_static(WEBKIT_TYPE_ACCESSIBLE,
  1449. atkTypeName,
  1450. &typeInfo, GTypeFlags(0));
  1451. for (guint i = 0; i < G_N_ELEMENTS(AtkInterfacesInitFunctions); i++) {
  1452. if (interfaceMask & (1 << i))
  1453. g_type_add_interface_static(type,
  1454. GetAtkInterfaceTypeFromWAIType(static_cast<WAIType>(i)),
  1455. &AtkInterfacesInitFunctions[i]);
  1456. }
  1457. return type;
  1458. }
  1459. WebKitAccessible* webkit_accessible_new(AccessibilityObject* coreObject)
  1460. {
  1461. GType type = getAccessibilityTypeFromObject(coreObject);
  1462. AtkObject* object = static_cast<AtkObject*>(g_object_new(type, 0));
  1463. atk_object_initialize(object, coreObject);
  1464. return WEBKIT_ACCESSIBLE(object);
  1465. }
  1466. AccessibilityObject* webkit_accessible_get_accessibility_object(WebKitAccessible* accessible)
  1467. {
  1468. return accessible->m_object;
  1469. }
  1470. void webkit_accessible_detach(WebKitAccessible* accessible)
  1471. {
  1472. ASSERT(accessible->m_object);
  1473. // We replace the WebCore AccessibilityObject with a fallback object that
  1474. // provides default implementations to avoid repetitive null-checking after
  1475. // detachment.
  1476. accessible->m_object = fallbackObject();
  1477. }
  1478. AtkObject* webkit_accessible_get_focused_element(WebKitAccessible* accessible)
  1479. {
  1480. if (!accessible->m_object)
  1481. return 0;
  1482. RefPtr<AccessibilityObject> focusedObj = accessible->m_object->focusedUIElement();
  1483. if (!focusedObj)
  1484. return 0;
  1485. return focusedObj->wrapper();
  1486. }
  1487. AccessibilityObject* objectAndOffsetUnignored(AccessibilityObject* coreObject, int& offset, bool ignoreLinks)
  1488. {
  1489. Node* endNode = static_cast<AccessibilityRenderObject*>(coreObject)->renderer()->node();
  1490. int endOffset = coreObject->selection().end().computeOffsetInContainerNode();
  1491. // Indication that something bogus has transpired.
  1492. offset = -1;
  1493. AccessibilityObject* realObject = coreObject;
  1494. if (realObject->accessibilityIsIgnored())
  1495. realObject = realObject->parentObjectUnignored();
  1496. if (ignoreLinks && realObject->isLink())
  1497. realObject = realObject->parentObjectUnignored();
  1498. Node* node = static_cast<AccessibilityRenderObject*>(realObject)->renderer()->node();
  1499. if (node) {
  1500. RefPtr<Range> range = rangeOfContents(node);
  1501. if (range->ownerDocument() == node->document()) {
  1502. ExceptionCode ec = 0;
  1503. range->setEndBefore(endNode, ec);
  1504. if (range->boundaryPointsValid())
  1505. offset = range->text().length() + endOffset;
  1506. }
  1507. }
  1508. return realObject;
  1509. }
  1510. #endif // HAVE(ACCESSIBILITY)