Bug 522181 - add an ability to inspect nsIAccessibleTableCell interface, r=sdwilsh, sr=neil
--- 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">