/platform/external/webkit/WebCore/accessibility/AXObjectCache.cpp

https://github.com/aharish/totoro-gb-opensource-update2 · C++ · 524 lines · 380 code · 93 blank · 51 comment · 101 complexity · 5cbec6ea68474d8d3eb97664ad1813c6 MD5 · raw file

  1. /*
  2. * Copyright (C) 2008, 2009, 2010 Apple Inc. All rights reserved.
  3. *
  4. * Redistribution and use in source and binary forms, with or without
  5. * modification, are permitted provided that the following conditions
  6. * are met:
  7. *
  8. * 1. Redistributions of source code must retain the above copyright
  9. * notice, this list of conditions and the following disclaimer.
  10. * 2. Redistributions in binary form must reproduce the above copyright
  11. * notice, this list of conditions and the following disclaimer in the
  12. * documentation and/or other materials provided with the distribution.
  13. * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
  14. * its contributors may be used to endorse or promote products derived
  15. * from this software without specific prior written permission.
  16. *
  17. * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
  18. * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  19. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  20. * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
  21. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  22. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  23. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  24. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  25. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  26. * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  27. */
  28. #include "config.h"
  29. #include "AXObjectCache.h"
  30. #include "AccessibilityARIAGrid.h"
  31. #include "AccessibilityARIAGridCell.h"
  32. #include "AccessibilityARIAGridRow.h"
  33. #include "AccessibilityImageMapLink.h"
  34. #include "AccessibilityList.h"
  35. #include "AccessibilityListBox.h"
  36. #include "AccessibilityListBoxOption.h"
  37. #include "AccessibilityMediaControls.h"
  38. #include "AccessibilityMenuList.h"
  39. #include "AccessibilityMenuListPopup.h"
  40. #include "AccessibilityMenuListOption.h"
  41. #include "AccessibilityRenderObject.h"
  42. #include "AccessibilityScrollbar.h"
  43. #include "AccessibilitySlider.h"
  44. #include "AccessibilityTable.h"
  45. #include "AccessibilityTableCell.h"
  46. #include "AccessibilityTableColumn.h"
  47. #include "AccessibilityTableHeaderContainer.h"
  48. #include "AccessibilityTableRow.h"
  49. #include "FocusController.h"
  50. #include "Frame.h"
  51. #include "HTMLAreaElement.h"
  52. #include "HTMLImageElement.h"
  53. #include "HTMLNames.h"
  54. #if ENABLE(VIDEO)
  55. #include "MediaControlElements.h"
  56. #endif
  57. #include "InputElement.h"
  58. #include "Page.h"
  59. #include "RenderObject.h"
  60. #include "RenderView.h"
  61. #include <wtf/PassRefPtr.h>
  62. namespace WebCore {
  63. using namespace HTMLNames;
  64. bool AXObjectCache::gAccessibilityEnabled = false;
  65. bool AXObjectCache::gAccessibilityEnhancedUserInterfaceEnabled = false;
  66. AXObjectCache::AXObjectCache()
  67. : m_notificationPostTimer(this, &AXObjectCache::notificationPostTimerFired)
  68. {
  69. }
  70. AXObjectCache::~AXObjectCache()
  71. {
  72. HashMap<AXID, RefPtr<AccessibilityObject> >::iterator end = m_objects.end();
  73. for (HashMap<AXID, RefPtr<AccessibilityObject> >::iterator it = m_objects.begin(); it != end; ++it) {
  74. AccessibilityObject* obj = (*it).second.get();
  75. detachWrapper(obj);
  76. obj->detach();
  77. removeAXID(obj);
  78. }
  79. }
  80. AccessibilityObject* AXObjectCache::focusedImageMapUIElement(HTMLAreaElement* areaElement)
  81. {
  82. // Find the corresponding accessibility object for the HTMLAreaElement. This should be
  83. // in the list of children for its corresponding image.
  84. if (!areaElement)
  85. return 0;
  86. HTMLImageElement* imageElement = areaElement->imageElement();
  87. if (!imageElement)
  88. return 0;
  89. AccessibilityObject* axRenderImage = areaElement->document()->axObjectCache()->getOrCreate(imageElement->renderer());
  90. if (!axRenderImage)
  91. return 0;
  92. AccessibilityObject::AccessibilityChildrenVector imageChildren = axRenderImage->children();
  93. unsigned count = imageChildren.size();
  94. for (unsigned k = 0; k < count; ++k) {
  95. AccessibilityObject* child = imageChildren[k].get();
  96. if (!child->isImageMapLink())
  97. continue;
  98. if (static_cast<AccessibilityImageMapLink*>(child)->areaElement() == areaElement)
  99. return child;
  100. }
  101. return 0;
  102. }
  103. AccessibilityObject* AXObjectCache::focusedUIElementForPage(const Page* page)
  104. {
  105. // get the focused node in the page
  106. Document* focusedDocument = page->focusController()->focusedOrMainFrame()->document();
  107. Node* focusedNode = focusedDocument->focusedNode();
  108. if (!focusedNode)
  109. focusedNode = focusedDocument;
  110. if (focusedNode->hasTagName(areaTag))
  111. return focusedImageMapUIElement(static_cast<HTMLAreaElement*>(focusedNode));
  112. RenderObject* focusedNodeRenderer = focusedNode->renderer();
  113. if (!focusedNodeRenderer)
  114. return 0;
  115. AccessibilityObject* obj = focusedNodeRenderer->document()->axObjectCache()->getOrCreate(focusedNodeRenderer);
  116. if (obj->shouldFocusActiveDescendant()) {
  117. if (AccessibilityObject* descendant = obj->activeDescendant())
  118. obj = descendant;
  119. }
  120. // the HTML element, for example, is focusable but has an AX object that is ignored
  121. if (obj->accessibilityIsIgnored())
  122. obj = obj->parentObjectUnignored();
  123. return obj;
  124. }
  125. AccessibilityObject* AXObjectCache::get(RenderObject* renderer)
  126. {
  127. if (!renderer)
  128. return 0;
  129. AccessibilityObject* obj = 0;
  130. AXID axID = m_renderObjectMapping.get(renderer);
  131. ASSERT(!HashTraits<AXID>::isDeletedValue(axID));
  132. if (axID)
  133. obj = m_objects.get(axID).get();
  134. return obj;
  135. }
  136. bool AXObjectCache::nodeIsAriaType(Node* node, String role)
  137. {
  138. if (!node || !node->isElementNode())
  139. return false;
  140. return equalIgnoringCase(static_cast<Element*>(node)->getAttribute(roleAttr), role);
  141. }
  142. AccessibilityObject* AXObjectCache::getOrCreate(RenderObject* renderer)
  143. {
  144. if (!renderer)
  145. return 0;
  146. AccessibilityObject* obj = get(renderer);
  147. if (!obj) {
  148. Node* node = renderer->node();
  149. RefPtr<AccessibilityObject> newObj = 0;
  150. if (renderer->isListBox())
  151. newObj = AccessibilityListBox::create(renderer);
  152. else if (renderer->isMenuList())
  153. newObj = AccessibilityMenuList::create(renderer);
  154. // If the node is aria role="list" or the aria role is empty and its a ul/ol/dl type (it shouldn't be a list if aria says otherwise).
  155. else if (node && ((nodeIsAriaType(node, "list") || nodeIsAriaType(node, "directory"))
  156. || (nodeIsAriaType(node, nullAtom) && (node->hasTagName(ulTag) || node->hasTagName(olTag) || node->hasTagName(dlTag)))))
  157. newObj = AccessibilityList::create(renderer);
  158. // aria tables
  159. else if (nodeIsAriaType(node, "grid") || nodeIsAriaType(node, "treegrid"))
  160. newObj = AccessibilityARIAGrid::create(renderer);
  161. else if (nodeIsAriaType(node, "row"))
  162. newObj = AccessibilityARIAGridRow::create(renderer);
  163. else if (nodeIsAriaType(node, "gridcell") || nodeIsAriaType(node, "columnheader") || nodeIsAriaType(node, "rowheader"))
  164. newObj = AccessibilityARIAGridCell::create(renderer);
  165. // standard tables
  166. else if (renderer->isTable())
  167. newObj = AccessibilityTable::create(renderer);
  168. else if (renderer->isTableRow())
  169. newObj = AccessibilityTableRow::create(renderer);
  170. else if (renderer->isTableCell())
  171. newObj = AccessibilityTableCell::create(renderer);
  172. #if ENABLE(VIDEO)
  173. // media controls
  174. else if (renderer->node() && renderer->node()->isMediaControlElement())
  175. newObj = AccessibilityMediaControl::create(renderer);
  176. #endif
  177. // input type=range
  178. else if (renderer->isSlider())
  179. newObj = AccessibilitySlider::create(renderer);
  180. else
  181. newObj = AccessibilityRenderObject::create(renderer);
  182. obj = newObj.get();
  183. getAXID(obj);
  184. m_renderObjectMapping.set(renderer, obj->axObjectID());
  185. m_objects.set(obj->axObjectID(), obj);
  186. attachWrapper(obj);
  187. }
  188. return obj;
  189. }
  190. AccessibilityObject* AXObjectCache::getOrCreate(AccessibilityRole role)
  191. {
  192. RefPtr<AccessibilityObject> obj = 0;
  193. // will be filled in...
  194. switch (role) {
  195. case ListBoxOptionRole:
  196. obj = AccessibilityListBoxOption::create();
  197. break;
  198. case ImageMapLinkRole:
  199. obj = AccessibilityImageMapLink::create();
  200. break;
  201. case ColumnRole:
  202. obj = AccessibilityTableColumn::create();
  203. break;
  204. case TableHeaderContainerRole:
  205. obj = AccessibilityTableHeaderContainer::create();
  206. break;
  207. case SliderThumbRole:
  208. obj = AccessibilitySliderThumb::create();
  209. break;
  210. case MenuListPopupRole:
  211. obj = AccessibilityMenuListPopup::create();
  212. break;
  213. case MenuListOptionRole:
  214. obj = AccessibilityMenuListOption::create();
  215. break;
  216. case ScrollBarRole:
  217. obj = AccessibilityScrollbar::create();
  218. break;
  219. default:
  220. obj = 0;
  221. }
  222. if (obj)
  223. getAXID(obj.get());
  224. else
  225. return 0;
  226. m_objects.set(obj->axObjectID(), obj);
  227. attachWrapper(obj.get());
  228. return obj.get();
  229. }
  230. void AXObjectCache::remove(AXID axID)
  231. {
  232. if (!axID)
  233. return;
  234. // first fetch object to operate some cleanup functions on it
  235. AccessibilityObject* obj = m_objects.get(axID).get();
  236. if (!obj)
  237. return;
  238. detachWrapper(obj);
  239. obj->detach();
  240. removeAXID(obj);
  241. // finally remove the object
  242. if (!m_objects.take(axID))
  243. return;
  244. ASSERT(m_objects.size() >= m_idsInUse.size());
  245. }
  246. void AXObjectCache::remove(RenderObject* renderer)
  247. {
  248. if (!renderer)
  249. return;
  250. AXID axID = m_renderObjectMapping.get(renderer);
  251. remove(axID);
  252. m_renderObjectMapping.remove(renderer);
  253. }
  254. #if !PLATFORM(WIN)
  255. AXID AXObjectCache::platformGenerateAXID() const
  256. {
  257. static AXID lastUsedID = 0;
  258. // Generate a new ID.
  259. AXID objID = lastUsedID;
  260. do {
  261. ++objID;
  262. } while (!objID || HashTraits<AXID>::isDeletedValue(objID) || m_idsInUse.contains(objID));
  263. lastUsedID = objID;
  264. return objID;
  265. }
  266. #endif
  267. AXID AXObjectCache::getAXID(AccessibilityObject* obj)
  268. {
  269. // check for already-assigned ID
  270. AXID objID = obj->axObjectID();
  271. if (objID) {
  272. ASSERT(m_idsInUse.contains(objID));
  273. return objID;
  274. }
  275. objID = platformGenerateAXID();
  276. m_idsInUse.add(objID);
  277. obj->setAXObjectID(objID);
  278. return objID;
  279. }
  280. void AXObjectCache::removeAXID(AccessibilityObject* object)
  281. {
  282. if (!object)
  283. return;
  284. AXID objID = object->axObjectID();
  285. if (!objID)
  286. return;
  287. ASSERT(!HashTraits<AXID>::isDeletedValue(objID));
  288. ASSERT(m_idsInUse.contains(objID));
  289. object->setAXObjectID(0);
  290. m_idsInUse.remove(objID);
  291. }
  292. #if HAVE(ACCESSIBILITY)
  293. void AXObjectCache::contentChanged(RenderObject* renderer)
  294. {
  295. AccessibilityObject* object = getOrCreate(renderer);
  296. if (object)
  297. object->contentChanged();
  298. }
  299. #endif
  300. void AXObjectCache::childrenChanged(RenderObject* renderer)
  301. {
  302. if (!renderer)
  303. return;
  304. AXID axID = m_renderObjectMapping.get(renderer);
  305. if (!axID)
  306. return;
  307. AccessibilityObject* obj = m_objects.get(axID).get();
  308. if (obj)
  309. obj->childrenChanged();
  310. }
  311. void AXObjectCache::notificationPostTimerFired(Timer<AXObjectCache>*)
  312. {
  313. m_notificationPostTimer.stop();
  314. unsigned i = 0, count = m_notificationsToPost.size();
  315. for (i = 0; i < count; ++i) {
  316. AccessibilityObject* obj = m_notificationsToPost[i].first.get();
  317. #ifndef NDEBUG
  318. // Make sure none of the render views are in the process of being layed out.
  319. // Notifications should only be sent after the renderer has finished
  320. if (obj->isAccessibilityRenderObject()) {
  321. AccessibilityRenderObject* renderObj = static_cast<AccessibilityRenderObject*>(obj);
  322. RenderObject* renderer = renderObj->renderer();
  323. if (renderer && renderer->view())
  324. ASSERT(!renderer->view()->layoutState());
  325. }
  326. #endif
  327. postPlatformNotification(obj, m_notificationsToPost[i].second);
  328. }
  329. m_notificationsToPost.clear();
  330. }
  331. #if HAVE(ACCESSIBILITY)
  332. void AXObjectCache::postNotification(RenderObject* renderer, AXNotification notification, bool postToElement, PostType postType)
  333. {
  334. // Notifications for text input objects are sent to that object.
  335. // All others are sent to the top WebArea.
  336. if (!renderer)
  337. return;
  338. // Get an accessibility object that already exists. One should not be created here
  339. // because a render update may be in progress and creating an AX object can re-trigger a layout
  340. RefPtr<AccessibilityObject> object = get(renderer);
  341. while (!object && renderer) {
  342. renderer = renderer->parent();
  343. object = get(renderer);
  344. }
  345. if (!renderer)
  346. return;
  347. postNotification(object.get(), renderer->document(), notification, postToElement, postType);
  348. }
  349. void AXObjectCache::postNotification(AccessibilityObject* object, Document* document, AXNotification notification, bool postToElement, PostType postType)
  350. {
  351. if (object && !postToElement)
  352. object = object->observableObject();
  353. if (!object && document)
  354. object = get(document->renderer());
  355. if (!object)
  356. return;
  357. if (postType == PostAsynchronously) {
  358. m_notificationsToPost.append(make_pair(object, notification));
  359. if (!m_notificationPostTimer.isActive())
  360. m_notificationPostTimer.startOneShot(0);
  361. } else
  362. postPlatformNotification(object, notification);
  363. }
  364. void AXObjectCache::selectedChildrenChanged(RenderObject* renderer)
  365. {
  366. postNotification(renderer, AXSelectedChildrenChanged, true);
  367. }
  368. #endif
  369. #if HAVE(ACCESSIBILITY)
  370. void AXObjectCache::handleActiveDescendantChanged(RenderObject* renderer)
  371. {
  372. if (!renderer)
  373. return;
  374. AccessibilityObject* obj = getOrCreate(renderer);
  375. if (obj)
  376. obj->handleActiveDescendantChanged();
  377. }
  378. void AXObjectCache::handleAriaRoleChanged(RenderObject* renderer)
  379. {
  380. if (!renderer)
  381. return;
  382. AccessibilityObject* obj = getOrCreate(renderer);
  383. if (obj && obj->isAccessibilityRenderObject())
  384. static_cast<AccessibilityRenderObject*>(obj)->updateAccessibilityRole();
  385. }
  386. #endif
  387. VisiblePosition AXObjectCache::visiblePositionForTextMarkerData(TextMarkerData& textMarkerData)
  388. {
  389. VisiblePosition visiblePos = VisiblePosition(textMarkerData.node, textMarkerData.offset, textMarkerData.affinity);
  390. Position deepPos = visiblePos.deepEquivalent();
  391. if (deepPos.isNull())
  392. return VisiblePosition();
  393. RenderObject* renderer = deepPos.node()->renderer();
  394. if (!renderer)
  395. return VisiblePosition();
  396. AXObjectCache* cache = renderer->document()->axObjectCache();
  397. if (!cache->isIDinUse(textMarkerData.axID))
  398. return VisiblePosition();
  399. if (deepPos.node() != textMarkerData.node || deepPos.deprecatedEditingOffset() != textMarkerData.offset)
  400. return VisiblePosition();
  401. return visiblePos;
  402. }
  403. void AXObjectCache::textMarkerDataForVisiblePosition(TextMarkerData& textMarkerData, const VisiblePosition& visiblePos)
  404. {
  405. // This memory must be bzero'd so instances of TextMarkerData can be tested for byte-equivalence.
  406. // This also allows callers to check for failure by looking at textMarkerData upon return.
  407. memset(&textMarkerData, 0, sizeof(TextMarkerData));
  408. if (visiblePos.isNull())
  409. return;
  410. Position deepPos = visiblePos.deepEquivalent();
  411. Node* domNode = deepPos.node();
  412. ASSERT(domNode);
  413. if (!domNode)
  414. return;
  415. if (domNode->isHTMLElement()) {
  416. InputElement* inputElement = toInputElement(static_cast<Element*>(domNode));
  417. if (inputElement && inputElement->isPasswordField())
  418. return;
  419. }
  420. // locate the renderer, which must exist for a visible dom node
  421. RenderObject* renderer = domNode->renderer();
  422. ASSERT(renderer);
  423. // find or create an accessibility object for this renderer
  424. AXObjectCache* cache = renderer->document()->axObjectCache();
  425. RefPtr<AccessibilityObject> obj = cache->getOrCreate(renderer);
  426. textMarkerData.axID = obj.get()->axObjectID();
  427. textMarkerData.node = domNode;
  428. textMarkerData.offset = deepPos.deprecatedEditingOffset();
  429. textMarkerData.affinity = visiblePos.affinity();
  430. }
  431. } // namespace WebCore