Bug 552944 - No relationship between tabs and associated property page in new tabbrowser construct, r=enn, davidb, marcoz, sr=neil
authorAlexander Surkov <surkov.alexander@gmail.com>
Fri, 14 May 2010 18:55:00 +0900
changeset 42315 897f37baa800ed588a2bc09cfd5528a13731b7ff
parent 42314 840fcbf52747568f9011ed58e923acab6b3a5212
child 42316 8eb86ffb2325baf6f1c2aaefa7e159251642eb78
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)
reviewersenn, davidb, marcoz, neil
bugs552944
milestone1.9.3a5pre
first release with
nightly linux32
897f37baa800 / 3.7a5pre / 20100514030102 / files
nightly linux64
897f37baa800 / 3.7a5pre / 20100514030639 / files
nightly mac
897f37baa800 / 3.7a5pre / 20100514030623 / files
nightly win32
897f37baa800 / 3.7a5pre / 20100514040039 / files
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
Bug 552944 - No relationship between tabs and associated property page in new tabbrowser construct, r=enn, davidb, marcoz, sr=neil
accessible/public/nsIAccessibleProvider.idl
accessible/src/base/nsAccessibilityService.cpp
accessible/src/xul/nsXULTabAccessible.cpp
accessible/src/xul/nsXULTabAccessible.h
accessible/tests/mochitest/Makefile.in
accessible/tests/mochitest/relations/Makefile.in
accessible/tests/mochitest/relations/test_general.html
accessible/tests/mochitest/relations/test_general.xul
accessible/tests/mochitest/relations/test_tabbrowser.xul
accessible/tests/mochitest/relations/test_tree.xul
accessible/tests/mochitest/test_relations.html
accessible/tests/mochitest/test_relations.xul
accessible/tests/mochitest/test_relations_tree.xul
accessible/tests/mochitest/tree/test_tabbox.xul
accessible/tests/mochitest/tree/test_tabbrowser.xul
dom/interfaces/xul/Makefile.in
dom/interfaces/xul/nsIDOMXULRelatedElement.idl
toolkit/content/widgets/tabbox.xml
--- a/accessible/public/nsIAccessibleProvider.idl
+++ b/accessible/public/nsIAccessibleProvider.idl
@@ -40,17 +40,17 @@
 
 #include "nsISupports.idl"
 
 /**
  * nsIAccessibleProvider interface is used to link element and accessible
    object. For that XBL binding of element should implement the interface.
  */
 
-[scriptable, uuid(89fb8270-4f25-11df-9879-0800200c9a66)]
+[scriptable, uuid(ac0639d5-f95b-4e2b-970c-9eab281fb6a5)]
 interface nsIAccessibleProvider : nsISupports
 {
   /**
    * Constants set of common use.
    */
 
   /** Do not create an accessible for this object
    * This is useful if an ancestor binding already implements nsIAccessibleProvider,
@@ -87,25 +87,22 @@ interface nsIAccessibleProvider : nsISup
   const long XULMenuSeparator = 0x00001010;
   const long XULPane    = 0x00001011;
   const long XULProgressMeter = 0x00001012;
   const long XULScale = 0x00001013;
   const long XULStatusBar = 0x00001014;
   const long XULRadioButton = 0x00001015;
   const long XULRadioGroup = 0x00001016;
 
-  /** The single tab in a dialog or tabbrowser/editor interface */
+  /** Used for XUL tab element */
   const long XULTab = 0x00001017;
-
-  /** A combination of a tabs object and a tabpanels object */
-  const long XULTabBox = 0x00001018;
-
-  /** The collection of tab objects, usable in the TabBox and independent of it
-   as well */
-  const long XULTabs = 0x00001019;
+  /** Used for XUL tabs element, a container for tab elements */
+  const long XULTabs = 0x00001018;
+  /** Used for XUL tabpanels container element */
+  const long XULTabpanels = 0x00001019;
 
   const long XULText             = 0x0000101A;
   const long XULTextBox          = 0x0000101B;
   const long XULThumb            = 0x0000101C;
   const long XULTree             = 0x0000101D;
   const long XULTreeColumns      = 0x0000101E;
   const long XULTreeColumnItem   = 0x0000101F;
   const long XULToolbar          = 0x00001020;
--- a/accessible/src/base/nsAccessibilityService.cpp
+++ b/accessible/src/base/nsAccessibilityService.cpp
@@ -1911,22 +1911,22 @@ nsAccessibilityService::CreateAccessible
       accessible = new nsXULRadioButtonAccessible(aNode, aWeakShell);
       break;
     case nsIAccessibleProvider::XULRadioGroup:
       accessible = new nsXULRadioGroupAccessible(aNode, aWeakShell);
       break;
     case nsIAccessibleProvider::XULTab:
       accessible = new nsXULTabAccessible(aNode, aWeakShell);
       break;
-    case nsIAccessibleProvider::XULTabBox:
-      accessible = new nsXULTabBoxAccessible(aNode, aWeakShell);
-      break;
     case nsIAccessibleProvider::XULTabs:
       accessible = new nsXULTabsAccessible(aNode, aWeakShell);
       break;
+    case nsIAccessibleProvider::XULTabpanels:
+      accessible = new nsXULTabpanelsAccessible(aNode, aWeakShell);
+      break;
     case nsIAccessibleProvider::XULText:
       accessible = new nsXULTextAccessible(aNode, aWeakShell);
       break;
     case nsIAccessibleProvider::XULTextBox:
       accessible = new nsXULTextFieldAccessible(aNode, aWeakShell);
       break;
     case nsIAccessibleProvider::XULThumb:
       accessible = new nsXULThumbAccessible(aNode, aWeakShell);
--- a/accessible/src/xul/nsXULTabAccessible.cpp
+++ b/accessible/src/xul/nsXULTabAccessible.cpp
@@ -42,16 +42,17 @@
 #include "nsRelUtils.h"
 
 // NOTE: alphabetically ordered
 #include "nsIDocument.h"
 #include "nsIFrame.h"
 #include "nsIDOMDocument.h"
 #include "nsIDOMXULSelectCntrlEl.h"
 #include "nsIDOMXULSelectCntrlItemEl.h"
+#include "nsIDOMXULRelatedElement.h"
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsXULTabAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
 nsXULTabAccessible::
   nsXULTabAccessible(nsIDOMNode* aNode, nsIWeakReference* aShell) :
   nsAccessibleWrap(aNode, aShell)
@@ -144,141 +145,102 @@ nsXULTabAccessible::GetRelationByType(PR
   nsresult rv = nsAccessibleWrap::GetRelationByType(aRelationType,
                                                     aRelation);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (aRelationType != nsIAccessibleRelation::RELATION_LABEL_FOR)
     return NS_OK;
 
   // Expose 'LABEL_FOR' relation on tab accessible for tabpanel accessible.
-  // XXX: It makes sense to require the interface from xul:tab to get linked
-  // tabpanel element.
   nsCOMPtr<nsIContent> content(do_QueryInterface(mDOMNode));
-
-  // Check whether tab and tabpanel are related by 'linkedPanel' attribute on
-  // xul:tab element.
-  rv = nsRelUtils::AddTargetFromIDRefAttr(aRelationType, aRelation, content,
-                                          nsAccessibilityAtoms::linkedPanel,
-                                          PR_TRUE);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  if (rv != NS_OK_NO_RELATION_TARGET)
+  nsCOMPtr<nsIDOMXULRelatedElement> tabsElm =
+    do_QueryInterface(content->GetParent());
+  if (!tabsElm)
     return NS_OK;
 
-  // If there is no 'linkedPanel' attribute on xul:tab element then we
-  // assume tab and tabpanels are related 1 to 1. We follow algorithm from
-  // the setter 'selectedIndex' of tabbox.xml#tabs binding.
-
-  nsAccessible* tabsAcc = GetParent();
-  NS_ENSURE_TRUE(nsAccUtils::Role(tabsAcc) == nsIAccessibleRole::ROLE_PAGETABLIST,
-                 NS_ERROR_FAILURE);
-
-  PRInt32 tabIndex = -1;
-
-  PRInt32 childCount = tabsAcc->GetChildCount();
-  for (PRInt32 childIdx = 0; childIdx < childCount; childIdx++) {
-    nsAccessible* childAcc = tabsAcc->GetChildAt(childIdx);
-    if (nsAccUtils::Role(childAcc) == nsIAccessibleRole::ROLE_PAGETAB)
-      tabIndex++;
+  nsCOMPtr<nsIDOMNode> tabpanelNode;
+  tabsElm->GetRelatedElement(mDOMNode, getter_AddRefs(tabpanelNode));
+  if (!tabpanelNode)
+    return NS_OK;
 
-    if (childAcc == this)
-      break;
-  }
-
-  nsAccessible* tabBoxAcc = tabsAcc->GetParent();
-  NS_ENSURE_TRUE(nsAccUtils::Role(tabBoxAcc) == nsIAccessibleRole::ROLE_PANE,
-                 NS_ERROR_FAILURE);
-
-  childCount = tabBoxAcc->GetChildCount();
-  for (PRInt32 childIdx = 0; childIdx < childCount; childIdx++) {
-    nsAccessible* childAcc = tabBoxAcc->GetChildAt(childIdx);
-    if (nsAccUtils::Role(childAcc) == nsIAccessibleRole::ROLE_PROPERTYPAGE) {
-      if (tabIndex == 0)
-        return nsRelUtils::AddTarget(aRelationType, aRelation, childAcc);
-
-      tabIndex--;
-    }
-  }
-
-  return NS_OK;
+  nsCOMPtr<nsIContent> tabpanelContent(do_QueryInterface(tabpanelNode));
+  return nsRelUtils::AddTargetFromContent(aRelationType, aRelation,
+                                          tabpanelContent);
 }
 
 void
 nsXULTabAccessible::GetPositionAndSizeInternal(PRInt32 *aPosInSet,
                                                PRInt32 *aSetSize)
 {
   nsAccUtils::GetPositionAndSizeForXULSelectControlItem(mDOMNode, aPosInSet,
                                                         aSetSize);
 }
 
 
 ////////////////////////////////////////////////////////////////////////////////
-// nsXULTabBoxAccessible
+// nsXULTabsAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
-/**
-  * XUL TabBox
-  *  to facilitate naming of the tabPanels object we will give this the name
-  *   of the selected tab in the tabs object.
-  */
-
-/** Constructor */
-nsXULTabBoxAccessible::nsXULTabBoxAccessible(nsIDOMNode* aNode, nsIWeakReference* aShell):
-nsAccessibleWrap(aNode, aShell)
-{ 
+nsXULTabsAccessible::
+  nsXULTabsAccessible(nsIDOMNode *aNode, nsIWeakReference *aShell) :
+  nsXULSelectableAccessible(aNode, aShell)
+{
 }
 
-/** We are a window*/
-nsresult
-nsXULTabBoxAccessible::GetRoleInternal(PRUint32 *aRole)
-{
-  *aRole = nsIAccessibleRole::ROLE_PANE;
-  return NS_OK;
-}
-
-/**
-  * XUL Tabs - the s really stands for strip. this is a collection of tab objects
-  */
-
-/** Constructor */
-nsXULTabsAccessible::nsXULTabsAccessible(nsIDOMNode* aNode, nsIWeakReference* aShell):
-nsXULSelectableAccessible(aNode, aShell)
-{ 
-}
-
-/** We are a Page Tab List */
 nsresult
 nsXULTabsAccessible::GetRoleInternal(PRUint32 *aRole)
 {
   *aRole = nsIAccessibleRole::ROLE_PAGETABLIST;
   return NS_OK;
 }
 
-/** no actions */
-NS_IMETHODIMP nsXULTabsAccessible::GetNumActions(PRUint8 *_retval)
+NS_IMETHODIMP
+nsXULTabsAccessible::GetNumActions(PRUint8 *aCount)
 {
-  *_retval = 0;
+  NS_ENSURE_ARG_POINTER(aCount);
+  *aCount = 0;
+
   return NS_OK;
 }
 
 /** no value */
 NS_IMETHODIMP nsXULTabsAccessible::GetValue(nsAString& _retval)
 {
   return NS_OK;
 }
 
 nsresult
 nsXULTabsAccessible::GetNameInternal(nsAString& aName)
 {
   // no name
   return NS_OK;
 }
 
+
+////////////////////////////////////////////////////////////////////////////////
+// nsXULTabpanelsAccessible
+////////////////////////////////////////////////////////////////////////////////
+
+nsXULTabpanelsAccessible::
+  nsXULTabpanelsAccessible(nsIDOMNode *aNode, nsIWeakReference *aShell) :
+  nsAccessibleWrap(aNode, aShell)
+{
+}
+
+nsresult
+nsXULTabpanelsAccessible::GetRoleInternal(PRUint32 *aRole)
+{
+  *aRole = nsIAccessibleRole::ROLE_PANE;
+  return NS_OK;
+}
+
+
 ////////////////////////////////////////////////////////////////////////////////
 // nsXULTabpanelAccessible
+////////////////////////////////////////////////////////////////////////////////
 
 nsXULTabpanelAccessible::
   nsXULTabpanelAccessible(nsIDOMNode* aNode, nsIWeakReference* aShell):
   nsAccessibleWrap(aNode, aShell)
 {
 }
 
 nsresult
@@ -294,76 +256,23 @@ nsXULTabpanelAccessible::GetRelationByTy
 {
   nsresult rv = nsAccessibleWrap::GetRelationByType(aRelationType, aRelation);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (aRelationType != nsIAccessibleRelation::RELATION_LABELLED_BY)
     return NS_OK;
 
   // Expose 'LABELLED_BY' relation on tabpanel accessible for tab accessible.
-  nsCOMPtr<nsIAccessible> tabBoxAcc;
-  GetParent(getter_AddRefs(tabBoxAcc));
-  NS_ENSURE_TRUE(nsAccUtils::Role(tabBoxAcc) == nsIAccessibleRole::ROLE_PANE,
-                 NS_ERROR_FAILURE);
-
-  PRInt32 tabpanelIndex = -1;
-  nsCOMPtr<nsIAccessible> tabsAcc;
-
-  PRBool isTabpanelFound = PR_FALSE;
-  nsCOMPtr<nsIAccessible> childAcc;
-  tabBoxAcc->GetFirstChild(getter_AddRefs(childAcc));
-  while (childAcc && (!tabsAcc || !isTabpanelFound)) {
-    if (nsAccUtils::Role(childAcc) == nsIAccessibleRole::ROLE_PAGETABLIST)
-      tabsAcc = childAcc;
-
-    if (!isTabpanelFound &&
-        nsAccUtils::Role(childAcc) == nsIAccessibleRole::ROLE_PROPERTYPAGE)
-      tabpanelIndex++;
-
-    if (childAcc == this)
-      isTabpanelFound = PR_TRUE;
-
-    nsCOMPtr<nsIAccessible> acc;
-    childAcc->GetNextSibling(getter_AddRefs(acc));
-    childAcc.swap(acc);
-  }
-
-  if (!tabsAcc || tabpanelIndex == -1)
+  nsCOMPtr<nsIContent> content(do_QueryInterface(mDOMNode));
+  nsCOMPtr<nsIDOMXULRelatedElement> tabpanelsElm =
+    do_QueryInterface(content->GetParent());
+  if (!tabpanelsElm)
     return NS_OK;
 
-  nsCOMPtr<nsIContent> content(do_QueryInterface(mDOMNode));
-  nsIAtom *atomID = content->GetID();
-
-  nsCOMPtr<nsIAccessible> foundTabAcc;
-  tabsAcc->GetFirstChild(getter_AddRefs(childAcc));
-  while (childAcc) {
-    if (nsAccUtils::Role(childAcc) == nsIAccessibleRole::ROLE_PAGETAB) {
-      if (atomID) {
-        nsCOMPtr<nsIAccessNode> tabAccNode(do_QueryInterface(childAcc));
-        nsCOMPtr<nsIDOMNode> tabNode;
-        tabAccNode->GetDOMNode(getter_AddRefs(tabNode));
-        nsCOMPtr<nsIContent> tabContent(do_QueryInterface(tabNode));
-        NS_ENSURE_TRUE(tabContent, NS_ERROR_FAILURE);
+  nsCOMPtr<nsIDOMNode> tabNode;
+  tabpanelsElm->GetRelatedElement(mDOMNode, getter_AddRefs(tabNode));
+  if (!tabNode)
+    return NS_OK;
 
-        if (tabContent->AttrValueIs(kNameSpaceID_None,
-                                    nsAccessibilityAtoms::linkedPanel, atomID,
-                                    eCaseMatters)) {
-          return nsRelUtils::AddTarget(aRelationType, aRelation, childAcc);
-        }
-      }
-
-      if (tabpanelIndex == 0) {
-        foundTabAcc = childAcc;
-        if (!atomID)
-          break;
-      }
-
-      tabpanelIndex--;
-    }
-
-    nsCOMPtr<nsIAccessible> acc;
-    childAcc->GetNextSibling(getter_AddRefs(acc));
-    childAcc.swap(acc);
-  }
-
-  return nsRelUtils::AddTarget(aRelationType, aRelation, foundTabAcc);
+  nsCOMPtr<nsIContent> tabContent(do_QueryInterface(tabNode));
+  return nsRelUtils::AddTargetFromContent(aRelationType, aRelation,
+                                          tabContent);
 }
-
--- a/accessible/src/xul/nsXULTabAccessible.h
+++ b/accessible/src/xul/nsXULTabAccessible.h
@@ -39,17 +39,17 @@
 #ifndef _nsXULTabAccessible_H_
 #define _nsXULTabAccessible_H_
 
 // NOTE: alphabetically ordered
 #include "nsBaseWidgetAccessible.h"
 #include "nsXULMenuAccessible.h"
 
 /**
- * An individual tab, xul:tab element
+ * An individual tab, xul:tab element.
  */
 class nsXULTabAccessible : public nsAccessibleWrap
 {
 public:
   enum { eAction_Switch = 0 };
 
   nsXULTabAccessible(nsIDOMNode* aNode, nsIWeakReference* aShell);
 
@@ -62,52 +62,57 @@ public:
 
   // nsAccessible
   virtual void GetPositionAndSizeInternal(PRInt32 *aPosInSet,
                                           PRInt32 *aSetSize);
   virtual nsresult GetRoleInternal(PRUint32 *aRole);
   virtual nsresult GetStateInternal(PRUint32 *aState, PRUint32 *aExtraState);
 };
 
-/** 
-  * Contains a tabs object and a tabPanels object. A complete
-  *    entity with relationships between tabs and content to
-  *    be displayed in the tabpanels object
-  */
-class nsXULTabBoxAccessible : public nsAccessibleWrap
-{
-public:
-  nsXULTabBoxAccessible(nsIDOMNode* aNode, nsIWeakReference* aShell);
-
-  // nsAccessible
-  virtual nsresult GetRoleInternal(PRUint32 *aRole);
-};
 
 /**
- * A container of tab obejcts, xul:tabs element.
+ * A container of tab objects, xul:tabs element.
  */
 class nsXULTabsAccessible : public nsXULSelectableAccessible
 {
 public:
   nsXULTabsAccessible(nsIDOMNode* aNode, nsIWeakReference* aShell);
 
   // nsIAccessible
   NS_IMETHOD GetNumActions(PRUint8 *_retval);
   NS_IMETHOD GetValue(nsAString& _retval);
 
   // nsAccessible
   virtual nsresult GetNameInternal(nsAString& aName);
   virtual nsresult GetRoleInternal(PRUint32 *aRole);
 };
 
+
+/** 
+ * A container of tab panels, xul:tabpanels element.
+ */
+class nsXULTabpanelsAccessible : public nsAccessibleWrap
+{
+public:
+  nsXULTabpanelsAccessible(nsIDOMNode *aNode, nsIWeakReference *aShell);
+
+  // nsAccessible
+  virtual nsresult GetRoleInternal(PRUint32 *aRole);
+};
+
+
 /**
  * A tabpanel object, child elements of xul:tabpanels element. Note,the object
  * is created from nsAccessibilityService::GetAccessibleForDeckChildren()
  * method and we do not use nsIAccessibleProvider interface here because
  * all children of xul:tabpanels element acts as xul:tabpanel element.
+ *
+ * XXX: we need to move the class logic into generic class since
+ * for example we do not create instance of this class for XUL textbox used as
+ * a tabpanel.
  */
 class nsXULTabpanelAccessible : public nsAccessibleWrap
 {
 public:
   nsXULTabpanelAccessible(nsIDOMNode *aNode, nsIWeakReference *aShell);
 
   // nsIAccessible
   NS_IMETHOD GetRelationByType(PRUint32 aRelationType,
--- a/accessible/tests/mochitest/Makefile.in
+++ b/accessible/tests/mochitest/Makefile.in
@@ -37,17 +37,17 @@
 # ***** END LICENSE BLOCK *****
 
 DEPTH		= ../../..
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 relativesrcdir  = accessible
 
-DIRS	= actions attributes events selectable states tree
+DIRS	= actions attributes events relations selectable states tree
 
 include $(DEPTH)/config/autoconf.mk
 include $(topsrcdir)/config/rules.mk
 
 _TEST_FILES =\
 		formimage.png \
 		letters.gif \
 		moz.png \
@@ -99,19 +99,16 @@ include $(topsrcdir)/config/rules.mk
 		test_nsIAccessible_focus.html \
 		test_nsIAccessibleDocument.html \
 		test_nsIAccessibleHyperLink.html \
 		test_nsIAccessibleHyperLink.xul \
 		test_nsIAccessibleHyperText.html \
 		test_nsIAccessibleImage.html \
 		test_nsIAccessNode_utils.html \
 		test_nsOuterDocAccessible.html \
-		test_relations.html \
-		test_relations.xul \
-		test_relations_tree.xul \
 		test_role_nsHyperTextAcc.html \
 		test_table_1.html \
 		test_table_4.html \
 		test_table_headers.html \
 		test_table_headers_ariagrid.html \
 		test_table_headers_listbox.xul \
 		test_table_headers_tree.xul \
 		test_table_indexes.html \
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/relations/Makefile.in
@@ -0,0 +1,56 @@
+#
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is mozilla.org code.
+#
+# The Initial Developer of the Original Code is
+# Mozilla Corporation.
+# Portions created by the Initial Developer are Copyright (C) 2010
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#   Alexander Surkov <surkov.alexander@gmail.com> (original author)
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either of the GNU General Public License Version 2 or later (the "GPL"),
+# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# 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 *****
+
+DEPTH		= ../../../..
+topsrcdir	= @top_srcdir@
+srcdir		= @srcdir@
+VPATH		= @srcdir@
+relativesrcdir  = accessible/relations
+
+include $(DEPTH)/config/autoconf.mk
+include $(topsrcdir)/config/rules.mk
+
+_TEST_FILES =\
+		test_general.html \
+		test_general.xul \
+		test_tabbrowser.xul \
+		test_tree.xul \
+		$(NULL)
+
+libs:: $(_TEST_FILES)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/a11y/$(relativesrcdir)
rename from accessible/tests/mochitest/test_relations.html
rename to accessible/tests/mochitest/relations/test_general.html
rename from accessible/tests/mochitest/test_relations.xul
rename to accessible/tests/mochitest/relations/test_general.xul
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/relations/test_tabbrowser.xul
@@ -0,0 +1,121 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+                 type="text/css"?>
+<?xml-stylesheet href="chrome://browser/content/browser.css"
+                 type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+        title="Accessible XUL tabbrowser relation tests">
+
+  <script type="application/javascript" 
+          src="chrome://mochikit/content/MochiKit/packed.js" />
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+  <script type="application/javascript"
+          src="chrome://mochikit/content/a11y/accessible/common.js" />
+  <script type="application/javascript"
+          src="chrome://mochikit/content/a11y/accessible/role.js" />
+  <script type="application/javascript"
+          src="chrome://mochikit/content/a11y/accessible/relations.js" />
+
+  <script type="application/javascript">
+  <![CDATA[
+    ////////////////////////////////////////////////////////////////////////////
+    // Test
+
+    const Ci = Components.interfaces;
+
+    // Hack to make xul:tabbrowser work.
+    var handleDroppedLink  = null;
+    Components.utils.import("resource://gre/modules/Services.jsm");
+    var XULBrowserWindow = {
+      isBusy: false,
+      setOverLink: function (link, b) {
+      }
+    };
+    var gFindBar = {
+      hidden: true
+    };
+
+    function doTest()
+    {
+      var tabBrowser = document.getElementById("tabbrowser");
+
+      var progressListener =
+      {
+        onStateChange: function onStateChange(aWebProgress,
+                                              aRequest,
+                                              aStateFlags,
+                                              aStatus)
+       {
+        if (aStateFlags & Ci.nsIWebProgressListener.STATE_STOP)
+          testRelations();
+       }
+      };
+
+      tabBrowser.addProgressListener(progressListener,
+                                     Ci.nsIWebProgress.NOTIFY_STATE_WINDOW);
+
+      tabBrowser.loadTabs(["about:", "about:mozilla"], false, true);
+    }
+
+    function testRelations()
+    {
+      //////////////////////////////////////////////////////////////////////////
+      // 'labelled by'/'label for' relations for xul:tab and xul:tabpanel
+
+      var tabs = getNode("tabbrowser").tabContainer.childNodes;
+      var panels = getNode("tabbrowser").mTabBox.tabpanels.childNodes;
+
+      testRelation(panels[0], RELATION_LABELLED_BY, tabs[0]);
+      testRelation(tabs[0], RELATION_LABEL_FOR, panels[0]);
+      testRelation(panels[1], RELATION_LABELLED_BY, tabs[1]);
+      testRelation(tabs[1], RELATION_LABEL_FOR, panels[1]);
+
+      SimpleTest.finish()
+    }
+
+    SimpleTest.waitForExplicitFinish();
+    addA11yLoadEvent(doTest);
+  ]]>
+  </script>
+
+  <vbox flex="1" style="overflow: auto;">
+    <body xmlns="http://www.w3.org/1999/xhtml">
+      <a target="_blank"
+         href="https://bugzilla.mozilla.org/show_bug.cgi?id=552944"
+         title="No relationship between tabs and associated property page in new tabbrowser construct">
+        Mozilla Bug 552944
+      </a><br/>
+      <p id="display"></p>
+      <div id="content" style="display: none">
+      </div>
+      <pre id="test">
+      </pre>
+    </body>
+
+    <!-- Hack to make xul:tabbrowser work -->
+    <menubar>
+      <menu label="menu">
+        <menupopup>
+          <menuitem label="close window hook" id="menu_closeWindow"/>
+          <menuitem label="close hook" id="menu_close"/>
+        </menupopup>
+      </menu>
+    </menubar>
+
+    <tabs id="tabbrowser-tabs" class="tabbrowser-tabs"
+          tabbrowser="tabbrowser"
+          setfocus="false">
+      <tab class="tabbrowser-tab" selected="true"/>
+    </tabs>
+    <tabbrowser id="tabbrowser"
+                type="content-primary"
+                tabcontainer="tabbrowser-tabs"
+                flex="1"/>
+  </vbox>
+
+</window>
+
rename from accessible/tests/mochitest/test_relations_tree.xul
rename to accessible/tests/mochitest/relations/test_tree.xul
--- a/accessible/tests/mochitest/tree/test_tabbox.xul
+++ b/accessible/tests/mochitest/tree/test_tabbox.xul
@@ -22,72 +22,79 @@
     // Test
 
     function doTest()
     {
       //////////////////////////////////////////////////////////////////////////
       // tabbox
 
       var accTree = {
-        role: ROLE_PANE,
+        role: ROLE_PAGETABLIST,
         children: [
           {
-            role: ROLE_PAGETABLIST,
-            children: [
-              {
-                role: ROLE_PAGETAB,
-                children: []
-              },
-              {
-                role: ROLE_PAGETAB,
-                children: []
-              }
-            ]
+            role: ROLE_PAGETAB,
+            children: []
           },
           {
+            role: ROLE_PAGETAB,
+            children: []
+          }
+        ]
+      };
+      testAccessibleTree("tabs", accTree);
+
+      accTree = {
+        role: ROLE_PANE,
+        children: [
+          {
             role: ROLE_PROPERTYPAGE,
             children: []
           },
           {
             role: ROLE_PROPERTYPAGE,
             children: []
           }
         ]
       };
-      testAccessibleTree("tabbox", accTree);
+      testAccessibleTree("tabpanels", accTree);
 
       SimpleTest.finish()
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTest);
   ]]>
   </script>
 
   <hbox flex="1" style="overflow: auto;">
     <body xmlns="http://www.w3.org/1999/xhtml">
       <a target="_blank"
          href="https://bugzilla.mozilla.org/show_bug.cgi?id=540389"
          title=" WARNING: Bad accessible tree!: [tabbrowser tab] ">
         Mozilla Bug 540389
       </a><br/>
+      <a target="_blank"
+         href="https://bugzilla.mozilla.org/show_bug.cgi?id=552944"
+         title="No relationship between tabs and associated property page in new tabbrowser construct">
+        Mozilla Bug 552944
+      </a><br/>
       <p id="display"></p>
       <div id="content" style="display: none">
       </div>
       <pre id="test">
       </pre>
     </body>
 
     <vbox flex="1">
-      <tabbox id="tabbox">
-        <tabs>
+      <tabbox>
+        <tabs id="tabs">
           <tab label="tab1"/>
           <tab label="tab2"/>
         </tabs>
-        <tabpanels>
+        <tabpanels id="tabpanels">
           <tabpanel/>
           <tabpanel/>
         </tabpanels>
       </tabbox>
     </vbox>
   </hbox>
 
 </window>
--- a/accessible/tests/mochitest/tree/test_tabbrowser.xul
+++ b/accessible/tests/mochitest/tree/test_tabbrowser.xul
@@ -86,45 +86,53 @@
               }
             ]
           },
           {
             role: ROLE_PUSHBUTTON
           }
         ]
       };
+      testAccessibleTree(getNode("tabbrowser").tabContainer, tabsAccTree);
+
       var tabboxAccTree = {
         role: ROLE_PANE,
         children: [
           {
             role: ROLE_PROPERTYPAGE
           },
           {
             role: ROLE_PROPERTYPAGE
           }
         ]
       };
-      testAccessibleTree(getNode("tabbrowser").tabContainer, tabsAccTree);
-      testAccessibleTree(getNode("tabbrowser").mTabBox, tabboxAccTree);
+
+      testAccessibleTree(getNode("tabbrowser").mTabBox.tabpanels,
+                         tabboxAccTree);
 
       SimpleTest.finish()
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTest);
   ]]>
   </script>
 
   <vbox flex="1" style="overflow: auto;">
     <body xmlns="http://www.w3.org/1999/xhtml">
       <a target="_blank"
          href="https://bugzilla.mozilla.org/show_bug.cgi?id=540389"
          title=" WARNING: Bad accessible tree!: [tabbrowser tab] ">
         Mozilla Bug 540389
       </a><br/>
+      <a target="_blank"
+         href="https://bugzilla.mozilla.org/show_bug.cgi?id=552944"
+         title="No relationship between tabs and associated property page in new tabbrowser construct">
+        Mozilla Bug 552944
+      </a><br/>
       <p id="display"></p>
       <div id="content" style="display: none">
       </div>
       <pre id="test">
       </pre>
     </body>
 
     <!-- Hack to make xul:tabbrowser work -->
--- a/dom/interfaces/xul/Makefile.in
+++ b/dom/interfaces/xul/Makefile.in
@@ -56,16 +56,17 @@ XPIDLSRCS =					\
 	nsIDOMXULDocument.idl			\
 	nsIDOMXULElement.idl			\
 	nsIDOMXULContainerElement.idl \
 	nsIDOMXULImageElement.idl               \
 	nsIDOMXULLabelElement.idl               \
 	nsIDOMXULLabeledControlEl.idl          \
 	nsIDOMXULMenuListElement.idl     \
 	nsIDOMXULPopupElement.idl          \
+	nsIDOMXULRelatedElement.idl \
 	nsIDOMXULSelectCntrlEl.idl       \
 	nsIDOMXULSelectCntrlItemEl.idl   \
 	nsIDOMXULMultSelectCntrlEl.idl \
 	nsIDOMXULTextboxElement.idl \
 	nsIDOMXULTreeElement.idl \
 	$(NULL)
 
 include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/dom/interfaces/xul/nsIDOMXULRelatedElement.idl
@@ -0,0 +1,51 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Alexander Surkov <surkov.alexander@gmail.com> (original author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * 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 "domstubs.idl"
+
+[scriptable, uuid(9fbac05a-fb27-470d-8e5f-028b2dc54ad0)]
+interface nsIDOMXULRelatedElement : nsISupports
+{
+  /**
+   * Retrun an element associated with the given element. It's implemented
+   * by container elements having relation between their items. For example,
+   * this interface is implemented by XUL tabs and XUL tabpanels elements
+   * and used to get XUL tab element by linked tab panel and vice versa.
+   */
+  nsIDOMNode getRelatedElement(in nsIDOMNode aElement);
+};
--- a/toolkit/content/widgets/tabbox.xml
+++ b/toolkit/content/widgets/tabbox.xml
@@ -8,25 +8,17 @@
   <binding id="tab-base">
     <resources>
       <stylesheet src="chrome://global/skin/tabbox.css"/>
     </resources>
   </binding>
 
   <binding id="tabbox"
            extends="chrome://global/content/bindings/tabbox.xml#tab-base">
-    <implementation implements="nsIDOMEventListener, nsIAccessibleProvider">
-      <property name="accessibleType" readonly="true">
-        <getter>
-          <![CDATA[
-            return Components.interfaces.nsIAccessibleProvider.XULTabBox;
-          ]]>
-        </getter>
-      </property>
-
+    <implementation implements="nsIDOMEventListener">
       <property name="handleCtrlTab">
         <setter>
         <![CDATA[
           this.setAttribute("handleCtrlTab", val);
           return val;
         ]]>
         </setter>
         <getter>
@@ -238,17 +230,17 @@
     </resources>
 
     <content>
       <xul:spacer class="tabs-left"/>
       <children/>
       <xul:spacer class="tabs-right" flex="1"/>
     </content>
     
-    <implementation implements="nsIDOMXULSelectControlElement, nsIAccessibleProvider">
+    <implementation implements="nsIDOMXULSelectControlElement, nsIDOMXULRelatedElement, nsIAccessibleProvider">
       <constructor>
       <![CDATA[
         // first and last tabs need to be able to have unique styles
         // and also need to select first tab on startup.
         if (this.firstChild)
           this.firstChild.setAttribute("first-tab", "true");
         if (this.lastChild)
           this.lastChild.setAttribute("last-tab", "true");
@@ -273,25 +265,75 @@
 
         var value = this.value;
         if (value)
           this.value = value;
         else
           this.selectedIndex = 0;
       ]]>
       </constructor>
-      
+
+      <!-- nsIAccessibleProvider -->
       <property name="accessibleType" readonly="true">
         <getter>
           <![CDATA[
             return Components.interfaces.nsIAccessibleProvider.XULTabs;
           ]]>
         </getter>
       </property>
 
+      <!-- nsIDOMXULRelatedElement -->
+      <method name="getRelatedElement">
+        <parameter name="aTabElm"/>
+        <body>
+        <![CDATA[
+          if (!aTabElm)
+            return null;
+
+          let tabboxElm = this.tabbox;
+          if (!tabboxElm)
+            return null;
+
+          let tabpanelsElm = tabboxElm.tabpanels;
+          if (!tabpanelsElm)
+            return null;
+
+          // Get linked tab panel by 'linkedpanel' attribute on the given tab
+          // element.
+          let linkedPanelElm = null;
+
+          let linkedPanelId = aTabElm.linkedPanel;
+          if (linkedPanelId) {
+            let ownerDoc = this.ownerDocument;
+
+            // XXX bug 565858: if XUL tab element is anonymous element then
+            // suppose linked tab panel is hosted within the same XBL binding
+            // and search it by ID attribute inside an anonymous content of
+            // the binding. This is not robust assumption since tab elements may
+            // live outside a tabbox element so that for example tab elements
+            // can be explicit content but tab panels can be anonymous.
+
+            let bindingParent = ownerDoc.getBindingParent(aTabElm);
+            if (bindingParent)
+              return ownerDoc.getAnonymousElementByAttribute(bindingParent,
+                                                             "id",
+                                                             linkedPanelId);
+
+            return ownerDoc.getElementById(linkedPanelId);
+          }
+
+          // otherwise linked tabpanel element has the same index as the given
+          // tab element.
+          let tabElmIdx = this.getIndexOfItem(aTabElm);
+          return tabpanelsElm.childNodes[tabElmIdx];
+        ]]>
+        </body>
+      </method>
+
+      <!-- nsIDOMXULSelectControlElement -->
       <property name="itemCount" readonly="true"
                 onget="return this.childNodes.length"/>
 
       <property name="value" onget="return this.getAttribute('value');">
         <setter>
           <![CDATA[
             this.setAttribute("value", val);
             var children = this.childNodes;
@@ -342,41 +384,23 @@
             Array.forEach(this.childNodes, function (aTab) {
               if (aTab.selected && aTab != tab)
                 aTab._selected = false;
             });
             tab._selected = true;
 
             this.setAttribute("value", tab.value);
 
-            if (this.tabbox) {
+            let linkedPanel = this.getRelatedElement(tab);
+            if (linkedPanel) {
               this.tabbox.setAttribute("selectedIndex", val);
-              var tabpanels = this.tabbox.tabpanels;
-              // This will cause an onselect event to fire for the tabpanel element.
-              if (tabpanels) {
-                // find an id
-                let linkedPanelId = tab.linkedPanel;
-                let linkedPanel = null;
-                if (linkedPanelId) {
-                  let ownerDoc = tab.ownerDocument;
-                  let bindingParent = ownerDoc.getBindingParent(tab);
-                  if (bindingParent) {
-                    linkedPanel =
-                      ownerDoc.getAnonymousElementByAttribute(bindingParent,
-                                                              "id",
-                                                              linkedPanelId);
-                  }
-                  else
-                    linkedPanel = ownerDoc.getElementById(linkedPanelId);
-                }
-                if (linkedPanel)
-                  tabpanels.selectedPanel = linkedPanel;
-                else
-                  tabpanels.selectedIndex = val;
-              }
+
+              // This will cause an onselect event to fire for the tabpanel
+              // element.
+              this.tabbox.tabpanels.selectedPanel = linkedPanel;
             }
 
             if (!alreadySelected) {
               // Fire an onselect event for the tabs element.
               var event = document.createEvent('Events');
               event.initEvent('select', true, true);
               this.dispatchEvent(event);
             }
@@ -550,17 +574,76 @@
       ]]>
       </handler>
     </handlers>
 #endif
   </binding>
 
   <binding id="tabpanels"
            extends="chrome://global/content/bindings/tabbox.xml#tab-base">
-    <implementation>
+    <implementation implements="nsIAccessibleProvider, nsIDOMXULRelatedElement">
+
+      <!-- nsIAccessibleProvider -->
+      <property name="accessibleType" readonly="true">
+        <getter>
+          <![CDATA[
+            return Components.interfaces.nsIAccessibleProvider.XULTabpanels;
+          ]]>
+        </getter>
+      </property>
+
+      <!-- nsIDOMXULRelatedElement -->
+      <method name="getRelatedElement">
+        <parameter name="aTabPanelElm"/>
+        <body>
+        <![CDATA[
+          if (!aTabPanelElm)
+            return null;
+
+          let tabboxElm = this.tabbox;
+          if (!tabboxElm)
+            return null;
+
+          let tabsElm = tabboxElm.tabs;
+          if (!tabsElm)
+            return null;
+
+          // Return tab element having 'linkedpanel' attribute equal to the id
+          // of the tab panel or the same index as the tab panel element.
+          let tabpanelIdx = Array.indexOf(this.childNodes, aTabPanelElm);
+          if (tabpanelIdx == -1)
+            return null;
+
+          let tabElms = tabsElm.childNodes;
+          let tabElmFromIndex = tabElms[tabpanelIdx];
+
+          let tabpanelId = aTabPanelElm.id;
+          if (tabpanelId) {
+            for (let idx = 0; idx < tabElms.length; idx++) {
+              var tabElm = tabElms[idx];
+              if (tabElm.linkedPanel == tabpanelId)
+                return tabElm;
+            }
+          }
+
+          return tabElmFromIndex;
+        ]]>
+        </body>
+      </method>
+
+      <!-- public -->
+      <field name="tabbox" readonly="true"><![CDATA[
+        var parent = this.parentNode;
+        while (parent) {
+          if (parent.localName == "tabbox")
+            break;
+          parent = parent.parentNode;
+        }
+        parent;
+      ]]></field>
 
       <field name="_selectedPanel">this.childNodes.item(this.selectedIndex)</field>
 
       <property name="selectedIndex">
         <getter>
         <![CDATA[
           var indexStr = this.getAttribute("selectedIndex");
           return indexStr ? parseInt(indexStr) : -1;