Bug 485270 - embed and object HTML tags should be given an accessible role of embedded object, r=marcoz, davidb
authorAlexander Surkov <surkov.alexander@gmail.com>
Thu, 15 Oct 2009 11:53:08 +0800
changeset 33872 c42a859b67185d3f099cb96e86cc5e4fe16aca06
parent 33871 cb01e7897aadeb266bd844e353c59e0445ff368d
child 33873 6ce66d91db3dcbf9dc7f70c09b377413ff4b3483
push id1
push userroot
push dateTue, 26 Apr 2011 22:38:44 +0000
treeherdermozilla-beta@bfdb6e623a36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmarcoz, davidb
bugs485270
milestone1.9.3a1pre
Bug 485270 - embed and object HTML tags should be given an accessible role of embedded object, r=marcoz, davidb
accessible/public/nsIAccessibleRole.idl
accessible/src/atk/nsRoleMap.h
accessible/src/base/nsAccessibilityService.cpp
accessible/src/base/nsAccessibilityService.h
accessible/src/mac/nsRoleMap.h
accessible/src/msaa/nsHTMLWin32ObjectAccessible.cpp
accessible/src/msaa/nsHTMLWin32ObjectAccessible.h
accessible/src/msaa/nsRoleMap.h
accessible/tests/mochitest/Makefile.in
accessible/tests/mochitest/role.js
accessible/tests/mochitest/states.js
accessible/tests/mochitest/test_elm_plugin.html
--- a/accessible/public/nsIAccessibleRole.idl
+++ b/accessible/public/nsIAccessibleRole.idl
@@ -39,17 +39,17 @@
 #include "nsISupports.idl"
 
 /**
  * Defines cross platform (Gecko) roles.
  *
  * @note - When adding a new role, be sure to also add it to nsRoleMap.h for
  *         each platform.
  */
-[scriptable, uuid(6793ca5c-c7cb-41db-9fb9-c16c0525f962)]
+[scriptable, uuid(f134da65-39a8-4330-843c-5bd42780b34c)]
 interface nsIAccessibleRole : nsISupports
 {
   /**
    * Used when accessible hans't strong defined role.
    */
   const unsigned long ROLE_NOTHING = 0;
 
   /**
@@ -772,14 +772,19 @@ interface nsIAccessibleRole : nsISupport
   /**
    * Represents a cell within a grid. It is used for role="gridcell". Unlike
    * ROLE_CELL, it allows the calculation of the accessible name from subtree.
    * Also, see ROLE_TABLE.
    */
   const unsigned long ROLE_GRID_CELL = 121;
 
   /**
+   * Represents an embedded object. It is used for html:object or html:embed.
+   */
+  const unsigned long ROLE_EMBEDDED_OBJECT = 122;
+
+  /**
    * It's not role actually. This constant is important to help ensure
    * nsRoleMap's are synchronized.
    */
-  const unsigned long ROLE_LAST_ENTRY = 122;
+  const unsigned long ROLE_LAST_ENTRY = 123;
 };
 
--- a/accessible/src/atk/nsRoleMap.h
+++ b/accessible/src/atk/nsRoleMap.h
@@ -163,11 +163,12 @@ static const PRUint32 atkRoleMap[] = {
     ATK_ROLE_MENU,                // nsIAccessibleRole::ROLE_COMBOBOX_LIST        114
     ATK_ROLE_MENU_ITEM,           // nsIAccessibleRole::ROLE_COMBOBOX_OPTION      115
     ATK_ROLE_IMAGE,               // nsIAccessibleRole::ROLE_IMAGE_MAP            116
     ATK_ROLE_LIST_ITEM,           // nsIAccessibleRole::ROLE_OPTION               117
     ATK_ROLE_LIST_ITEM,           // nsIAccessibleRole::ROLE_RICH_OPTION          118
     ATK_ROLE_LIST,                // nsIAccessibleRole::ROLE_LISTBOX              119
     ATK_ROLE_UNKNOWN,             // nsIAccessibleRole::ROLE_FLAT_EQUATION        120
     ATK_ROLE_TABLE_CELL,          // nsIAccessibleRole::ROLE_GRID_CELL            121
+    ATK_ROLE_PANEL,               // nsIAccessibleRole::ROLE_EMBEDDED_OBJECT      122
     kROLE_ATK_LAST_ENTRY          // nsIAccessibleRole::ROLE_LAST_ENTRY
 };
 
--- a/accessible/src/base/nsAccessibilityService.cpp
+++ b/accessible/src/base/nsAccessibilityService.cpp
@@ -758,25 +758,26 @@ nsAccessibilityService::CreateHTMLObject
   if (domDoc)
     return CreateOuterDocAccessible(node, aAccessible);
 
 #ifdef XP_WIN
   // 2) for plugins
   nsCOMPtr<nsIPluginInstance> pluginInstance ;
   aFrame->GetPluginInstance(*getter_AddRefs(pluginInstance));
   if (pluginInstance) {
+    // Note: pluginPort will be null if windowless.
     HWND pluginPort = nsnull;
     aFrame->GetPluginPort(&pluginPort);
-    if (pluginPort) {
-      *aAccessible = new nsHTMLWin32ObjectOwnerAccessible(node, weakShell, pluginPort);
-      if (*aAccessible) {
-        NS_ADDREF(*aAccessible);
-        return NS_OK;
-      }
-    }
+
+    *aAccessible =
+      new nsHTMLWin32ObjectOwnerAccessible(node, weakShell, pluginPort);
+    NS_ENSURE_TRUE(*aAccessible, NS_ERROR_OUT_OF_MEMORY);
+
+    NS_ADDREF(*aAccessible);
+    return NS_OK;
   }
 #endif
 
   // 3) for images and imagemaps, or anything else with a child frame
   // we have the object frame, get the image frame
   nsIFrame *frame = aFrame->GetFirstChild(nsnull);
   if (frame)
     return frame->GetAccessible(aAccessible);
--- a/accessible/src/base/nsAccessibilityService.h
+++ b/accessible/src/base/nsAccessibilityService.h
@@ -284,17 +284,18 @@ static const char kRoleNames[][20] = {
   "calendar",            //ROLE_CALENDAR
   "combobox list",       //ROLE_COMBOBOX_LIST
   "combobox option",     //ROLE_COMBOBOX_OPTION
   "image map",           //ROLE_IMAGE_MAP
   "listbox option",      //ROLE_OPTION
   "listbox rich option", //ROLE_RICH_OPTION
   "listbox",             //ROLE_LISTBOX
   "flat equation",       //ROLE_FLAT_EQUATION  
-  "gridcell"             //ROLE_GRID_CELL
+  "gridcell",            //ROLE_GRID_CELL
+  "embedded object"      //ROLE_EMBEDDED_OBJECT
 };
 
 /**
  * Map nsIAccessibleEvents constants to strings. Used by
  * nsIAccessibleRetrieval::getStringEventType() method.
  */
 static const char kEventTypeNames[][40] = {
   "unknown",                                 //
--- a/accessible/src/mac/nsRoleMap.h
+++ b/accessible/src/mac/nsRoleMap.h
@@ -159,10 +159,11 @@ static const NSString* AXRoles [] = {
   NSAccessibilityMenuRole,                      // ROLE_COMBOBOX_LIST
   NSAccessibilityMenuItemRole,                  // ROLE_COMBOBOX_OPTION
   NSAccessibilityImageRole,                     // ROLE_IMAGE_MAP
   NSAccessibilityRowRole,                       // ROLE_OPTION
   NSAccessibilityRowRole,                       // ROLE_RICH_OPTION
   NSAccessibilityListRole,                      // ROLE_LISTBOX
   NSAccessibilityUnknownRole,                   // ROLE_FLAT_EQUATION
   NSAccessibilityGroupRole,                     // ROLE_GRID_CELL
+  NSAccessibilityGroupRole                      // ROLE_EMBEDDED_OBJECT
   @"ROLE_LAST_ENTRY"                            // ROLE_LAST_ENTRY. bogus role that will never be shown (just marks the end of this array)!
 };
--- a/accessible/src/msaa/nsHTMLWin32ObjectAccessible.cpp
+++ b/accessible/src/msaa/nsHTMLWin32ObjectAccessible.cpp
@@ -34,62 +34,116 @@
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsHTMLWin32ObjectAccessible.h"
 #include "nsAccessibleWrap.h"
 
+////////////////////////////////////////////////////////////////////////////////
+// nsHTMLWin32ObjectOwnerAccessible
+////////////////////////////////////////////////////////////////////////////////
 
-nsHTMLWin32ObjectOwnerAccessible::nsHTMLWin32ObjectOwnerAccessible(nsIDOMNode* aNode, nsIWeakReference* aShell, void* aHwnd):
-nsAccessibleWrap(aNode, aShell)
+nsHTMLWin32ObjectOwnerAccessible::
+  nsHTMLWin32ObjectOwnerAccessible(nsIDOMNode* aNode, nsIWeakReference* aShell,
+                                   void* aHwnd) :
+  nsAccessibleWrap(aNode, aShell)
 {
   mHwnd = aHwnd;
 }
 
+////////////////////////////////////////////////////////////////////////////////
+// nsHTMLWin32ObjectOwnerAccessible: nsAccessNode implementation
+
 nsresult
 nsHTMLWin32ObjectOwnerAccessible::Shutdown()
 {
   nsAccessibleWrap::Shutdown();
   mNativeAccessible = nsnull;
   return NS_OK;
 }
 
-/** 
-  * Our only child is a nsHTMLWin32ObjectAccessible 
-  */
-NS_IMETHODIMP nsHTMLWin32ObjectOwnerAccessible::GetFirstChild(nsIAccessible **aFirstChild)
+////////////////////////////////////////////////////////////////////////////////
+// nsHTMLWin32ObjectOwnerAccessible: nsIAccessible implementation
+
+NS_IMETHODIMP
+nsHTMLWin32ObjectOwnerAccessible::GetFirstChild(nsIAccessible **aFirstChild)
 {
-  *aFirstChild = mNativeAccessible;
+  NS_ENSURE_ARG_POINTER(aFirstChild);
+  *aFirstChild = nsnull;
+
+  // Our only child is a nsHTMLWin32ObjectAccessible object.
   if (!mNativeAccessible) {
-    if (!mHwnd) {
+    if (!mHwnd)
       return NS_OK;
-    }
-    mNativeAccessible = new nsHTMLWin32ObjectAccessible(mHwnd) ;
+
+    mNativeAccessible = new nsHTMLWin32ObjectAccessible(mHwnd);
+    NS_ENSURE_TRUE(mNativeAccessible, NS_ERROR_OUT_OF_MEMORY);
+
     SetFirstChild(mNativeAccessible);
-    *aFirstChild = mNativeAccessible;
   }
+
+  *aFirstChild = mNativeAccessible;
   NS_IF_ADDREF(*aFirstChild);
+
   return NS_OK;
 }
 
-NS_IMETHODIMP nsHTMLWin32ObjectOwnerAccessible::GetLastChild(nsIAccessible **aLastChild)
+NS_IMETHODIMP
+nsHTMLWin32ObjectOwnerAccessible::GetLastChild(nsIAccessible **aLastChild)
 {
   return GetFirstChild(aLastChild);
 }
 
-NS_IMETHODIMP nsHTMLWin32ObjectOwnerAccessible::GetChildCount(PRInt32 *aChildCount)
+NS_IMETHODIMP
+nsHTMLWin32ObjectOwnerAccessible::GetChildCount(PRInt32 *aChildCount)
 {
+  NS_ENSURE_ARG_POINTER(aChildCount);
+
   nsCOMPtr<nsIAccessible> onlyChild;
   GetFirstChild(getter_AddRefs(onlyChild));
   *aChildCount = onlyChild ? 1 : 0;
   return NS_OK;
 }
 
+
+////////////////////////////////////////////////////////////////////////////////
+// nsHTMLWin32ObjectOwnerAccessible: nsAccessible implementation
+
+nsresult
+nsHTMLWin32ObjectOwnerAccessible::GetRoleInternal(PRUint32 *aRole)
+{
+  NS_ENSURE_ARG_POINTER(aRole);
+
+  *aRole = nsIAccessibleRole::ROLE_EMBEDDED_OBJECT;
+  return NS_OK;
+}
+
+nsresult
+nsHTMLWin32ObjectOwnerAccessible::GetStateInternal(PRUint32 *aState,
+                                                   PRUint32 *aExtraState)
+{
+  nsresult rv = nsAccessibleWrap::GetStateInternal(aState, aExtraState);
+  if (rv == NS_OK_DEFUNCT_OBJECT)
+    return rv;
+
+  // XXX: No HWND means this is windowless plugin which is not accessible in
+  // the meantime.
+  if (!mHwnd)
+    *aState = nsIAccessibleStates::STATE_UNAVAILABLE;
+
+  return rv;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+// nsHTMLWin32ObjectAccessible
+////////////////////////////////////////////////////////////////////////////////
+
 nsHTMLWin32ObjectAccessible::nsHTMLWin32ObjectAccessible(void* aHwnd):
 nsLeafAccessible(nsnull, nsnull)
 {
   mHwnd = aHwnd;
   if (mHwnd) {
     // The plugin is not windowless. In this situation we use 
     // use its inner child owned by the plugin so that we don't get
     // in an infinite loop, where the WM_GETOBJECT's get forwarded
--- a/accessible/src/msaa/nsHTMLWin32ObjectAccessible.h
+++ b/accessible/src/msaa/nsHTMLWin32ObjectAccessible.h
@@ -59,16 +59,20 @@ public:
   // nsIAccessible
   NS_IMETHOD GetFirstChild(nsIAccessible **aFirstChild);
   NS_IMETHOD GetLastChild(nsIAccessible **aLastChild);
   NS_IMETHOD GetChildCount(PRInt32 *aChildCount);  // Zero or one child
 
   // nsAccessNode
   virtual nsresult Shutdown();
 
+  // nsAccessible
+  virtual nsresult GetRoleInternal(PRUint32 *aRole);
+  virtual nsresult GetStateInternal(PRUint32 *aState, PRUint32 *aExtraState);
+
 protected:
   void* mHwnd;
   nsCOMPtr<nsIAccessible> mNativeAccessible;
 };
 
 /**
   * This class is used only internally, we never! send out an IAccessible linked
   *   back to this object. This class is used to represent a plugin object when
--- a/accessible/src/msaa/nsRoleMap.h
+++ b/accessible/src/msaa/nsRoleMap.h
@@ -435,12 +435,15 @@ static const WindowsRoleMapItem gWindows
   { ROLE_SYSTEM_LIST, ROLE_SYSTEM_LIST },
   
   // nsIAccessibleRole::ROLE_FLAT_EQUATION
   { ROLE_SYSTEM_EQUATION, ROLE_SYSTEM_EQUATION },
   
   // nsIAccessibleRole::ROLE_GRID_CELL
   { ROLE_SYSTEM_CELL, ROLE_SYSTEM_CELL },
 
+  // nsIAccessibleRole::ROLE_EMBEDDED_OBJECT
+  { USE_ROLE_STRING, IA2_ROLE_EMBEDDED_OBJECT },
+
   // nsIAccessibleRole::ROLE_LAST_ENTRY
   { ROLE_WINDOWS_LAST_ENTRY, ROLE_WINDOWS_LAST_ENTRY }
 };
 
--- a/accessible/tests/mochitest/Makefile.in
+++ b/accessible/tests/mochitest/Makefile.in
@@ -84,16 +84,17 @@ include $(topsrcdir)/config/rules.mk
 		test_bug420863.html \
 	$(warning   test_childAtPoint.html temporarily disabled) \
 	$(warning	test_childAtPoint.xul temporarily disabled) \
 		test_cssattrs.html \
 		test_descr.html \
 		test_elm_filectrl.html \
 		test_elm_listbox.xul \
 	$(warning   test_elm_media.html temporarily disabled) \
+		test_elm_plugin.html \
 		test_elm_tree.xul \
 		test_elm_txtcntnr.html \
 		test_events_caretmove.html \
 		test_events_coalescence.html \
 		test_events_doc.html \
 		test_events_draganddrop.html \
 		test_events_flush.html \
 		test_events_focus.html \
--- a/accessible/tests/mochitest/role.js
+++ b/accessible/tests/mochitest/role.js
@@ -7,16 +7,17 @@ const ROLE_APP_ROOT = nsIAccessibleRole.
 const ROLE_CAPTION = nsIAccessibleRole.ROLE_CAPTION;
 const ROLE_CELL = nsIAccessibleRole.ROLE_CELL;
 const ROLE_CHROME_WINDOW = nsIAccessibleRole.ROLE_CHROME_WINDOW;
 const ROLE_COMBOBOX = nsIAccessibleRole.ROLE_COMBOBOX;
 const ROLE_COMBOBOX_LIST = nsIAccessibleRole.ROLE_COMBOBOX_LIST;
 const ROLE_COMBOBOX_OPTION = nsIAccessibleRole.ROLE_COMBOBOX_OPTION;
 const ROLE_COLUMNHEADER = nsIAccessibleRole.ROLE_COLUMNHEADER;
 const ROLE_DOCUMENT = nsIAccessibleRole.ROLE_DOCUMENT;
+const ROLE_EMBEDDED_OBJECT = nsIAccessibleRole.ROLE_EMBEDDED_OBJECT;
 const ROLE_ENTRY = nsIAccessibleRole.ROLE_ENTRY;
 const ROLE_FLAT_EQUATION = nsIAccessibleRole.ROLE_FLAT_EQUATION;
 const ROLE_FORM = nsIAccessibleRole.ROLE_FORM;
 const ROLE_GRAPHIC = nsIAccessibleRole.ROLE_GRAPHIC;
 const ROLE_GRID_CELL = nsIAccessibleRole.ROLE_GRID_CELL;
 const ROLE_GROUPING = nsIAccessibleRole.ROLE_GROUPING;
 const ROLE_HEADING = nsIAccessibleRole.ROLE_HEADING;
 const ROLE_IMAGE_MAP = nsIAccessibleRole.ROLE_IMAGE_MAP;
--- a/accessible/tests/mochitest/states.js
+++ b/accessible/tests/mochitest/states.js
@@ -89,20 +89,22 @@ function testStates(aAccOrElmOrID, aStat
 
   // selected/selectable
   if (state & STATE_SELECTED) {
     isState(state & STATE_SELECTABLE, STATE_SELECTABLE, false,
             "Selected element should be selectable!");
   }
 
   // unavailable
-  if ((state & STATE_UNAVAILABLE)
-      && (getRole(aAccOrElmOrID) != ROLE_GROUPING))
-    isState(state & STATE_FOCUSABLE, STATE_FOCUSABLE, false,
-            "Disabled " + id + " must be focusable!");
+  if (state & STATE_UNAVAILABLE) {
+    var role = getRole(aAccOrElmOrID);
+    if (role != ROLE_GROUPING && role != ROLE_EMBEDDED_OBJECT)
+      isState(state & STATE_FOCUSABLE, STATE_FOCUSABLE, false,
+              "Disabled " + id + " must be focusable!");
+  }
 }
 
 /**
  * Tests an acessible and its sub tree for the passed in state bits.
  * Used to make sure that states are propagated to descendants, for example the
  * STATE_UNAVAILABLE from a container to its children.
  *
  * @param aAccOrElmOrID  The accessible, DOM element or ID to be tested.
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/test_elm_plugin.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>Plugin tests</title>
+  <link rel="stylesheet" type="text/css"
+        href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+  <script type="application/javascript"
+          src="chrome://mochikit/content/MochiKit/packed.js"></script>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+  <script type="application/javascript"
+          src="chrome://mochikit/content/a11y/accessible/common.js"></script>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/a11y/accessible/role.js"></script>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/a11y/accessible/states.js"></script>
+
+  <script type="application/javascript">
+
+    function doTest()
+    {
+      testRole("plugin", ROLE_EMBEDDED_OBJECT);
+      testStates("plugin", STATE_UNAVAILABLE);
+
+      SimpleTest.finish();
+    }
+
+    SimpleTest.waitForExplicitFinish();
+    addA11yLoadEvent(doTest);
+  </script>
+</head>
+<body>
+
+  <a target="_blank"
+     title="embed and object HTML tags should be given an accessible role of embedded object"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=485270">Mozilla Bug 485270</a>
+  <p id="display"></p>
+  <div id="content" style="display: none"></div>
+  <pre id="test">
+  </pre>
+
+  <embed id="plugin" type="application/x-test" width="300" height="300"></embed>
+</body>
+</html>