Bug 522181 - add an ability to inspect nsIAccessibleTableCell interface, r=sdwilsh, sr=neil
authorAlexander Surkov <surkov.alexander@gmail.com>
Sat, 17 Oct 2009 11:11:22 +0800
changeset 749 b05c6d3b5b68f24d1eb1ded7c4950978613d7b42
parent 740 f1fc2297e2c50e4326c4d94e9c1cbaa2f05844d3
child 750 d919eb85ff3e8927a2bab6670b946ff1728152f6
push id87
push usersurkov.alexander@gmail.com
push dateSat, 17 Oct 2009 02:30:01 +0000
reviewerssdwilsh, neil
bugs522181
Bug 522181 - add an ability to inspect nsIAccessibleTableCell interface, r=sdwilsh, sr=neil
jar.mn
resources/Makefile.in
resources/content/viewers/accessibleProps/accessiblePropViewerMgr.js
resources/content/viewers/accessibleProps/accessibleProps.js
resources/content/viewers/accessibleProps/accessibleProps.xul
resources/locale/en-US/viewers/accessibleProps.dtd
--- a/jar.mn
+++ b/jar.mn
@@ -68,18 +68,19 @@ inspector.jar:
   content/inspector/prefs/pref-sidebar.js                             (resources/content/prefs/pref-sidebar.js)
   content/inspector/tests/allskin.xul                                 (resources/content/tests/allskin.xul)
   content/inspector/viewers/accessibleEvent/accessibleEvent.js       (resources/content/viewers/accessibleEvent/accessibleEvent.js)
   content/inspector/viewers/accessibleEvent/accessibleEvent.xul      (resources/content/viewers/accessibleEvent/accessibleEvent.xul)
   content/inspector/viewers/accessibleEvents/accessibleEvents.js      (resources/content/viewers/accessibleEvents/accessibleEvents.js)
   content/inspector/viewers/accessibleEvents/accessibleEvents.xul     (resources/content/viewers/accessibleEvents/accessibleEvents.xul)
   content/inspector/viewers/accessibleObject/accessibleObject.js      (resources/content/viewers/accessibleObject/accessibleObject.js)
   content/inspector/viewers/accessibleObject/accessibleObject.xul     (resources/content/viewers/accessibleObject/accessibleObject.xul)
-  content/inspector/viewers/accessibleProps/accessibleProps.js        (resources/content/viewers/accessibleProps/accessibleProps.js)
-  content/inspector/viewers/accessibleProps/accessibleProps.xul       (resources/content/viewers/accessibleProps/accessibleProps.xul)
+  content/inspector/viewers/accessibleProps/accessibleProps.js           (resources/content/viewers/accessibleProps/accessibleProps.js)
+  content/inspector/viewers/accessibleProps/accessiblePropViewerMrg.js   (resources/content/viewers/accessibleProps/accessiblePropViewerMgr.js)
+  content/inspector/viewers/accessibleProps/accessibleProps.xul          (resources/content/viewers/accessibleProps/accessibleProps.xul)
   content/inspector/viewers/accessibleRelations/accessibleRelations.js  (resources/content/viewers/accessibleRelations/accessibleRelations.js)
   content/inspector/viewers/accessibleRelations/accessibleRelations.xul (resources/content/viewers/accessibleRelations/accessibleRelations.xul)
   content/inspector/viewers/accessibleTree/accessibleTree.js          (resources/content/viewers/accessibleTree/accessibleTree.js)
   content/inspector/viewers/accessibleTree/accessibleTree.xul         (resources/content/viewers/accessibleTree/accessibleTree.xul)
   content/inspector/viewers/accessibleTree/evalJSDialog.js            (resources/content/viewers/accessibleTree/evalJSDialog.js)
   content/inspector/viewers/accessibleTree/evalJSDialog.xul           (resources/content/viewers/accessibleTree/evalJSDialog.xul)
   content/inspector/viewers/computedStyle/computedStyle.js            (resources/content/viewers/computedStyle/computedStyle.js)
   content/inspector/viewers/computedStyle/computedStyle.xul           (resources/content/viewers/computedStyle/computedStyle.xul)
--- a/resources/Makefile.in
+++ b/resources/Makefile.in
@@ -40,22 +40,17 @@
 DEPTH=../../..
 topsrcdir=@top_srcdir@
 srcdir=@srcdir@
 VPATH=@srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 ALL_LOCALES = \
-	de \
 	en-US \
-	fr\
-	pl \
-	ru \
-	sk \
 	$(NULL)
 
 include $(topsrcdir)/config/config.mk
 
 SUBMAKEFILES += locale/Makefile
 
 include $(topsrcdir)/config/rules.mk
 
new file mode 100644
--- /dev/null
+++ b/resources/content/viewers/accessibleProps/accessiblePropViewerMgr.js
@@ -0,0 +1,397 @@
+/* ***** 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 DOM Inspector.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * 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 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 ***** */
+
+////////////////////////////////////////////////////////////////////////////////
+//// Global
+
+const nsIAccessibleTableCell = Components.interfaces.nsIAccessibleTableCell;
+
+
+////////////////////////////////////////////////////////////////////////////////
+//// Accessible property viewer manager
+
+/**
+ * Used to show additional properties of the accessible in tabbox.
+ *
+ * @param aPaneElm
+ *        A pane element where the view is hosted.
+ */
+function accessiblePropViewerMgr(aPaneElm)
+{
+  /**
+   * Updates all property views for the given accessible.
+   *
+   * @param aAccessible
+   *        The given accessible
+   */
+  this.updateViews = function accessiblePropViewerMgr_updateViews(aAccessible)
+  {
+    for (var id in this.viewers)
+    {
+      var tab = document.getElementById("tab_" + id);
+      tab.hidden = !this.viewers[id].update(aAccessible);
+    }
+
+    this.tabboxElm.selectedIndex = this.getCurrentViewerIdx();
+  }
+
+  /**
+   * Clear the data of property views.
+   */
+  this.clearViews = function accessiblePropViewerMgr_clearViews()
+  {
+    for (var id in this.viewers)
+    {
+      this.viewers[id].clear();
+
+      var tab = document.getElementById("tab_" + id);
+      tab.hidden = true;
+    }
+  }
+
+  /**
+   * Process 'inspectInNewView' command for selected property view.
+   */
+  this.inspectInNewView = function accessiblePropViewerMgr_inspectInNewView()
+  {
+    var tab = this.tabboxElm.selectedTab;
+    var viewrid = tab.id.replace("tab_", "");
+    this.viewers[viewrid].inspectInNewView();
+  }
+
+  //////////////////////////////////////////////////////////////////////////////
+  //// private
+
+  this.handleEvent = function accessiblePropViewerMgr_handleEvent(aEvent)
+  {
+    this.setCurrentViewerIdx(this.tabboxElm.selectedIndex);
+  }
+
+  this.setCurrentViewerIdx = function accessiblePropViewerMgr_setCurrentViewerIdx(aIdx)
+  {
+    this.paneElm.accessiblePropsCurrentViewerIdx = aIdx;
+  }
+
+  this.getCurrentViewerIdx = function accessiblePropViewerMgr_getCurrentViewerIdx()
+  {
+    var idx = this.paneElm.accessiblePropsCurrentViewerIdx;
+
+    idx = idx ? idx : 0;
+    var tab = this.tabsElm.children[idx];
+    if (tab.hidden)
+      return 0;
+
+    return idx;
+  }
+
+  this.viewers = {
+    "attributes": new attributesViewer(),
+    "tablecell": new tableCellViewer()
+  };
+
+  this.tabboxElm = document.getElementById("tabviewers");
+  this.tabsElm = this.tabboxElm.tabs;
+  this.tabsElm.addEventListener("select", this, false);
+  this.paneElm = aPaneElm;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+//// Accessible property viewers
+
+/**
+ * Object attribute property view. Used to display accessible attributes.
+ */
+function attributesViewer()
+{
+  /**
+   * Updates the view for the given accessible.
+   *
+   * @param aAccessible
+   *        The given accessible
+   */
+  this.update = function attributesViewer_update(aAccessible)
+  {
+    var attrs = aAccessible.attributes;
+    if (attrs) {
+      var enumerate = attrs.enumerate();
+      while (enumerate.hasMoreElements())
+        this.addAttribute(enumerate.getNext());
+    }
+
+    return true;
+  }
+
+  /**
+   * Clear the view's data.
+   */
+  this.clear = function attributesViewer_clear()
+  {
+    var trAttrBody = document.getElementById("trAttrBody");
+    while (trAttrBody.hasChildNodes())
+      trAttrBody.removeChild(trAttrBody.lastChild)
+  }
+
+  /**
+   * Prepares 'inspectInNewView' command.
+   */
+  this.inspectInNewView = function attributesViewer_inspectInNewView()
+  {
+  }
+
+  //////////////////////////////////////////////////////////////////////////////
+  //// private
+
+  this.addAttribute = function attrbiutesViewer_addAttribute(aElement)
+  {
+    var prop = XPCU.QI(aElement, nsIPropertyElement);
+    
+    var trAttrBody = document.getElementById("trAttrBody");
+    
+    var ti = document.createElement("treeitem");
+    var tr = document.createElement("treerow");
+    
+    var tc = document.createElement("treecell");
+    tc.setAttribute("label", prop.key);
+    tr.appendChild(tc);
+    
+    tc = document.createElement("treecell");
+    tc.setAttribute("label", prop.value);
+    tr.appendChild(tc);
+    
+    ti.appendChild(tr);
+    
+    trAttrBody.appendChild(ti);
+  }
+}
+
+/**
+ * Table cell property view. Used to display table cell properties of the
+ * accessible implementing nsIAccessibleTableCell.
+ */
+function tableCellViewer()
+{
+  /**
+   * Updates the view for the given accessible.
+   *
+   * @param aAccessible
+   *        The given accessible
+   */
+  this.update = function tableCellViewer_update(aAccessible)
+  {
+    if (!(aAccessible instanceof nsIAccessibleTableCell))
+      return false;
+
+    // columnIndex
+    var columnIndex = aAccessible.columnIndex;
+    this.columnIndexElm.textContent = columnIndex;
+
+    // rowIndex
+    var rowIndex = aAccessible.rowIndex;
+    this.rowIndexElm.textContent = rowIndex;
+
+    // columnExtent
+    var columnExtent = aAccessible.columnExtent;
+    this.columnExtentElm.textContent = columnExtent;
+    
+    // rowIndex
+    var rowExtent = aAccessible.rowExtent;
+    this.rowExtentElm.textContent = rowExtent;
+
+    // isSelected
+    var isSelected = aAccessible.isSelected();
+    this.isSelectedElm.textContent = isSelected;
+
+    // table, columnHeaderCells, rowHeaderCells
+    this.addRelated(aAccessible);
+
+    return true;
+  }
+
+  /**
+   * Clear the view's data.
+   */
+  this.clear = function tableCellViewer_clear()
+  {
+    this.mTreeBox.view = null;
+
+    this.columnIndexElm.textContent = "";
+    this.rowIndexElm.textContent = "";
+    this.columnExtentElm.textContent = "";
+    this.rowExtentElm.textContent = "";
+    this.isSelectedElm.textContent = "";
+  }
+
+  /**
+   * Prepares 'inspectInNewView' command.
+   */
+  this.inspectInNewView = function tableCellViewer_inspectInNewView()
+  {
+    var idx = this.mTree.currentIndex;
+    if (idx >= 0) {
+      var node = this.mTreeView.getDOMNode(idx);
+      if (node)
+        inspectObject(node);
+    }
+  }
+
+  //////////////////////////////////////////////////////////////////////////////
+  //// private
+
+  this.addRelated = function tableCellViewer_addRelated(aAccessible)
+  {
+    this.mTreeView = new TableCellTreeView(aAccessible);
+    this.mTreeBox.view = this.mTreeView;
+  }
+
+  this.mTree = document.getElementById("tableCell:accObjects");
+  this.mTreeBox = this.mTree.treeBoxObject;
+
+  this.columnIndexElm = document.getElementById("tableCell:columnIndex");
+  this.rowIndexElm = document.getElementById("tableCell:rowIndex");
+  this.columnExtentElm = document.getElementById("tableCell:columnExtent");
+  this.rowExtentElm = document.getElementById("tableCell:rowExtent");
+  this.isSelectedElm = document.getElementById("tableCell:isSelected");
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+//// TableCellTreeView. nsITreeView
+
+function TableCellTreeView(aTableCell)
+{
+  this.tableCell = aTableCell;
+  this.mRowCount = this.getRowCount();
+}
+
+TableCellTreeView.prototype = new inBaseTreeView();
+
+TableCellTreeView.prototype.getRowCount =
+  function TableCellTreeView_rowCount()
+{
+  this.columnHeaderCells = this.tableCell.columnHeaderCells;
+  this.columnHeaderCellsLen = (this.columnHeaderCells ?
+                               this.columnHeaderCells.length : 0);
+
+  this.rowHeaderCells = this.tableCell.rowHeaderCells;
+  this.rowHeaderCellsLen = (this.rowHeaderCells ?
+                            this.rowHeaderCells.length : 0);
+
+  return 1 + this.columnHeaderCellsLen + this.rowHeaderCellsLen;
+}
+
+TableCellTreeView.prototype.getCellText =
+  function TableCellTreeView_getCellText(aRow, aCol)
+{
+  var accessible = this.getAccessible(aRow);
+  if (!accessible)
+    return "";
+
+  if (aCol.id == "tableCell:property") {
+    return this.getPropertyName(aRow);
+
+  } else if (aCol.id == "tableCell:role") {
+    return gAccService.getStringRole(accessible.role);
+
+  } else if (aCol.id == "tableCell:name") {
+    return accessible.name;
+
+  } else if (aCol.id == "tableCell:nodeName") {
+    var node = this.getDOMNode(aRow);
+    if (node)
+      return node.nodeName;
+  }
+
+  return "";
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//// TableCellTreeView. Utils
+
+/**
+ * Return an accessible for the given row index.
+ *
+ * @param aRow
+ *        Row index
+ */
+TableCellTreeView.prototype.getAccessible =
+  function TableCellTreeView_getAccessible(aRow)
+{
+  if (aRow == 0)
+    return this.tableCell.table;
+
+  if (aRow <= this.columnHeaderCellsLen)
+    return this.columnHeaderCells.queryElementAt(aRow - 1, nsIAccessible);
+
+  return this.rowHeaderCells.queryElementAt(aRow - 1 - this.columnHeaderCellsLen, nsIAccessible);
+}
+
+/**
+ * Retrun interface attribute name (property) used at the given row index.
+ *
+ * @param aRow
+ *        Row index
+ */
+TableCellTreeView.prototype.getPropertyName =
+  function TableCellTreeView_getPropertyName(aRow)
+{
+  if (aRow == 0)
+    return "table";
+  
+  if (aRow <= this.columnHeaderCellsLen)
+    return "column header cell";
+  
+  return "row header cell";
+}
+
+/**
+ * Return DOM node at the given row index.
+ *
+ * @param aRow
+ *        Row index
+ */
+TableCellTreeView.prototype.getDOMNode =
+  function TableCellTreeView_getDOMNode(aRow)
+{
+  var accessNode = XPCU.QI(this.getAccessible(aRow), nsIAccessNode);
+  if (!accessNode)
+    return null;
+
+  var DOMNode = accessNode.DOMNode;
+  DOMNode[" accessible "] = accessNode;
+  return DOMNode;
+}
--- a/resources/content/viewers/accessibleProps/accessibleProps.js
+++ b/resources/content/viewers/accessibleProps/accessibleProps.js
@@ -37,24 +37,26 @@
  * ***** END LICENSE BLOCK ***** */
  
  
 ///////////////////////////////////////////////////////////////////////////////
 //// Global Variables
 
 var viewer;
 var gBundle;
+var gAccService = null;
 
 ///////////////////////////////////////////////////////////////////////////////
 //// Global Constants
 
 const kAccessibleRetrievalCID = "@mozilla.org/accessibleRetrieval;1";
 
 const nsIAccessibleRetrieval = Components.interfaces.nsIAccessibleRetrieval;
 const nsIAccessible = Components.interfaces.nsIAccessible;
+const nsIAccessNode = Components.interfaces.nsIAccessNode;
 
 const nsIPropertyElement = Components.interfaces.nsIPropertyElement;
 
 ///////////////////////////////////////////////////////////////////////////////
 //// Initialization/Destruction
 
 window.addEventListener("load", AccessiblePropsViewer_initialize, false);
 
@@ -67,78 +69,91 @@ function AccessiblePropsViewer_initializ
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 //// class AccessiblePropsViewer
 function AccessiblePropsViewer()
 {
   this.mURL = window.location;
   this.mObsMan = new ObserverManager(this);
-  this.mAccService = XPCU.getService(kAccessibleRetrievalCID,
-                                     nsIAccessibleRetrieval);
+
+  gAccService = XPCU.getService(kAccessibleRetrievalCID,
+                                nsIAccessibleRetrieval);
 }
 
 AccessiblePropsViewer.prototype =
 {
   mSubject: null,
   mPane: null,
   mAccSubject: null,
   mAccService: null,
+  mPropViewerMgr: null,
 
   get uid() { return "accessibleProps" },
   get pane() { return this.mPane },
 
   get subject() { return this.mSubject },
   set subject(aObject)
   {
     this.mSubject = aObject;
     this.updateView();
     this.mObsMan.dispatchEvent("subjectChange", { subject: aObject });
   },
 
   initialize: function initialize(aPane)
   {
+    this.mPropViewerMgr = new accessiblePropViewerMgr(aPane);
+
     this.mPane = aPane;
     aPane.notifyViewerReady(this);
   },
 
   isCommandEnabled: function isCommandEnabled(aCommand)
   {
     return false;
   },
   
   getCommand: function getCommand(aCommand)
   {
     return null;
   },
 
   destroy: function destroy() {},
 
-  // event dispatching
+  /////////////////////////
+  //// event dispatching
 
   addObserver: function addObserver(aEvent, aObserver)
   {
     this.mObsMan.addObserver(aEvent, aObserver);
   },
   removeObserver: function removeObserver(aEvent, aObserver)
   {
     this.mObsMan.removeObserver(aEvent, aObserver);
   },
 
+  /////////////////////////
+  //// utils
+
+  cmdInspectInNewView: function cmdInspectInNewView()
+  {
+    this.mPropViewerMgr.inspectInNewView();
+  },
+
   // private
   updateView: function updateView()
   {
     this.clearView();
 
     try {
       this.mAccSubject = this.mSubject[" accessible "];
       if (this.mAccSubject)
         XPCU.QI(this.mAccSubject, nsIAccessible);
       else
-        this.mAccSubject = this.mAccService.getAccessibleFor(this.mSubject);
+        this.mAccSubject = gAccService.getAccessibleFor(this.mSubject);
     } catch(e) {
       dump("Failed to get accessible object for node.");
       return;
     }
 
     // accessible properties.
     var containers = document.getElementsByAttribute("prop", "*");
     for (var i = 0; i < containers.length; ++i) {
@@ -167,69 +182,34 @@ AccessiblePropsViewer.prototype =
       gBundle.getFormattedString("accBoundsX", [x.value]);
     document.getElementById("bounds-y").textContent =
       gBundle.getFormattedString("accBoundsY", [y.value]);
     document.getElementById("bounds-width").textContent =
       gBundle.getFormattedString("accBoundsWidth", [width.value]);
     document.getElementById("bounds-height").textContent =
       gBundle.getFormattedString("accBoundsHeight", [height.value]);
 
-    // accessible attributes
-    var attrs = this.mAccSubject.attributes;
-    if (attrs) {
-      var enumerate = attrs.enumerate();
-      while (enumerate.hasMoreElements())
-        this.addAccessibleAttribute(enumerate.getNext());
-    }
-  },
-
-  addAccessibleAttribute: function addAccessibleAttribute(aElement)
-  {
-    var prop = XPCU.QI(aElement, nsIPropertyElement);
-
-    var trAttrBody = document.getElementById("trAttrBody");
-
-    var ti = document.createElement("treeitem");
-    var tr = document.createElement("treerow");
-
-    var tc = document.createElement("treecell");
-    tc.setAttribute("label", prop.key);
-    tr.appendChild(tc);
-
-    tc = document.createElement("treecell");
-    tc.setAttribute("label", prop.value);
-    tr.appendChild(tc);
-
-    ti.appendChild(tr);
-
-    trAttrBody.appendChild(ti);
-  },
-
-  removeAccessibleAttributes: function removeAccessibleAttributes()
-  {
-    var trAttrBody = document.getElementById("trAttrBody");
-    while (trAttrBody.hasChildNodes())
-      trAttrBody.removeChild(trAttrBody.lastChild)
+    this.mPropViewerMgr.updateViews(this.mAccSubject);
   },
 
   clearView: function clearView()
   {
     var containers = document.getElementsByAttribute("prop", "*");
     for (var i = 0; i < containers.length; ++i)
       containers[i].textContent = "";
 
-    this.removeAccessibleAttributes();
+    this.mPropViewerMgr.clearViews();
   },
 
   get role()
   {
     // 'finalRole' is replaced by 'role' property in Gecko 1.9.2.
     var role = "finalRole" in this.mAccSubject ?
       this.mAccSubject.finalRole : this.mAccSubject.role;
-    return this.mAccService.getStringRole(role);
+    return gAccService.getStringRole(role);
   },
 
   get name()
   {
     return this.mAccSubject.name;
   },
 
   get description()
@@ -251,17 +231,18 @@ AccessiblePropsViewer.prototype =
     // nsIAccessible::getState.
     if ("getState" in this.mAccSubject)
       this.mAccSubject.getState(stateObj, extStateObj);
     else
       this.mAccSubject.getFinalState(stateObj, extStateObj);
 
     var list = [];
 
-    states = this.mAccService.getStringStates(stateObj.value, extStateObj.value);
+    var states = gAccService.getStringStates(stateObj.value,
+                                             extStateObj.value);
 
     for (var i = 0; i < states.length; i++)
       list.push(states.item(i));
     return list;
   },
 
   get actionNames()
   {
--- a/resources/content/viewers/accessibleProps/accessibleProps.xul
+++ b/resources/content/viewers/accessibleProps/accessibleProps.xul
@@ -47,23 +47,40 @@
 <?xml-stylesheet href="chrome://inspector/skin"?>
 
 <page id="winAccessibleProps"
       xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 
   <script type="application/javascript"
           src="chrome://inspector/content/jsutil/xpcom/XPCU.js"/>
   <script type="application/javascript"
+          src="chrome://inspector/content/hooks.js"/>
+  <script type="application/javascript"
           src="chrome://inspector/content/jsutil/events/ObserverManager.js"/>
   <script type="application/javascript"
+          src="chrome://inspector/content/jsutil/xul/inBaseTreeView.js"/>
+
+  <script type="application/javascript"
           src="chrome://inspector/content/viewers/accessibleProps/accessibleProps.js"/>
+  <script type="application/javascript"
+          src="chrome://inspector/content/viewers/accessibleProps/accessiblePropViewerMgr.js"/>
 
   <stringbundle id="accessiblePropsBundle"
                 src="chrome://inspector/locale/viewers/accessibleProps.properties"/>
 
+  <commandset id="cmdsJSObjectViewer">
+    <command id="cmdInspectInNewView"
+             oncommand="viewer.cmdInspectInNewView();"/>
+  </commandset>
+
+  <menupopup id="popupContext">
+    <menuitem label="&inspectNewWindow.label;"
+              observes="cmdInspectInNewView"/>
+  </menupopup>
+
   <grid>
     <columns>
       <column/>
       <column flex="1"/>
     </columns>
     <rows>
       <row>
         <description>&descRole.label;</description>
@@ -106,17 +123,88 @@
               <description id="bounds-height"/>
             </row>
           </rows>
         </grid>
       </row>
     </rows>
   </grid>
 
-  <tree flex="1">
-    <treecols>
-      <treecol label="attrKey" flex="1"/>
-      <treecol label="attrValue" flex="2"/>
-    </treecols>
-    <treechildren id="trAttrBody"/>
-  </tree>
+  <tabbox id="tabviewers" flex="1">
+    <tabs>
+      <tab id="tab_attributes" label="&tabAttrs.label;"/>
+      <tab id="tab_tablecell" label="&tabTableCell.label;"/>
+    </tabs>
+    <tabpanels flex="1">
+
+      <!-- Object attributes tab panel -->
+      <tree>
+        <treecols>
+          <treecol label="&colAttrName.label;" flex="1"/>
+          <treecol label="&colAttrValue.label;" flex="2"/>
+        </treecols>
+        <treechildren id="trAttrBody"/>
+      </tree>
+
+      <!-- Table cell properties tab panel -->
+      <vbox>
+        <grid>
+          <columns>
+            <column/>
+            <column flex="1"/>
+            <column/>
+            <column flex="1"/>
+          </columns>
+          <rows>
+            <row>
+              <description>&descColumnIdx.label;</description>
+              <description id="tableCell:columnIndex"/>
+
+              <description>&descRowIdx.label;</description>
+              <description id="tableCell:rowIndex"/>
+            </row>
+            <row>
+              <description>&descColumnExtent.label;</description>
+              <description id="tableCell:columnExtent"/>
+
+              <description>&descRowExtent.label;</description>
+              <description id="tableCell:rowExtent"/>
+            </row>
+            <row>
+              <description>&descIsSelected.label;</description>
+              <description id="tableCell:isSelected"/>
+            </row>
+          </rows>
+        </grid>
+
+        <tree id="tableCell:accObjects"
+              flex="1"
+              context="popupContext">
+
+          <treecols>
+            <treecol id="tableCell:property"
+                     primary="true"
+                     label="&colProp.label;"
+                     persist="width,hidden,ordinal"
+                     flex="1"/>
+            <splitter class="tree-splitter"/>
+            <treecol id="tableCell:role"
+                     label="&colRole.label;"
+                     persist="width,hidden,ordinal"
+                     flex="1"/>
+            <splitter class="tree-splitter"/>
+            <treecol id="tableCell:name"
+                     label="&colName.label;"
+                     persist="width,hidden,ordinal"
+                     flex="1"/>
+            <splitter class="tree-splitter"/>
+            <treecol id="tableCell:nodeName"
+                     label="&colNodeName.label;"
+                     persist="width,hidden,ordinal"
+                     flex="1"/>
+          </treecols>
+          <treechildren/>
+        </tree>
+      </vbox>
+    </tabpanels>
+  </tabbox>
 </page>
 
--- a/resources/locale/en-US/viewers/accessibleProps.dtd
+++ b/resources/locale/en-US/viewers/accessibleProps.dtd
@@ -38,9 +38,21 @@
    - ***** END LICENSE BLOCK ***** -->
 <!ENTITY descRole.label "Role:">
 <!ENTITY descName.label "Name:">
 <!ENTITY descDescription.label "Description:">
 <!ENTITY descValue.label "Value:">
 <!ENTITY descState.label "State:">
 <!ENTITY descActionName.label "Action Name:">
 <!ENTITY descBounds.label "Bounds:">
-
+<!ENTITY tabAttrs.label "Object attributes">
+<!ENTITY colAttrName.label "Name">
+<!ENTITY colAttrValue.label "Value">
+<!ENTITY tabTableCell.label "Table cell">
+<!ENTITY descColumnIdx.label "Column index:">
+<!ENTITY descRowIdx.label "Row index:">
+<!ENTITY descColumnExtent.label "Column extent:">
+<!ENTITY descRowExtent.label "Row extent:">
+<!ENTITY descIsSelected.label "Is selected:">
+<!ENTITY colProp.label "Property">
+<!ENTITY colRole.label "Role">
+<!ENTITY colName.label "Name">
+<!ENTITY colNodeName.label "Node name">