Implement the AtkTableCell interface

Bug: 934815
Change-Id: I6d3ab551580818142b7ad7c289b442f6aa841343
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1492111
Commit-Queue: Joanmarie Diggs <joanmarie.diggs@gmail.com>
Reviewed-by: Dominic Mazzoni <dmazzoni@chromium.org>
Cr-Commit-Position: refs/heads/master@{#640819}
diff --git a/content/browser/accessibility/accessibility_tree_formatter_auralinux.cc b/content/browser/accessibility/accessibility_tree_formatter_auralinux.cc
index fc09678c..5a662f8 100644
--- a/content/browser/accessibility/accessibility_tree_formatter_auralinux.cc
+++ b/content/browser/accessibility/accessibility_tree_formatter_auralinux.cc
@@ -337,6 +337,14 @@
       WriteAttribute(true, table_property, &line);
   }
 
+  const base::ListValue* cell_info;
+  node.GetList("cell", &cell_info);
+  for (auto it = cell_info->begin(); it != cell_info->end(); ++it) {
+    std::string cell_property;
+    if (it->GetAsString(&cell_property))
+      WriteAttribute(true, cell_property, &line);
+  }
+
   return line;
 }
 
diff --git a/content/test/data/accessibility/html/caption-expected-auralinux.txt b/content/test/data/accessibility/html/caption-expected-auralinux.txt
index 970e6c0..0ac5603 100644
--- a/content/test/data/accessibility/html/caption-expected-auralinux.txt
+++ b/content/test/data/accessibility/html/caption-expected-auralinux.txt
@@ -3,17 +3,17 @@
 ++++[caption]
 ++++++[text] name='Browser and Engine'
 ++++[table row]
-++++++[column header] name='Browser'
+++++++[column header] name='Browser' (row=0, col=0, row_span=1, col_span=1 n_row_headers=0, n_col_headers=0)
 ++++++++[text] name='Browser'
-++++++[column header] name='Engine'
+++++++[column header] name='Engine' (row=0, col=1, row_span=1, col_span=1 n_row_headers=0, n_col_headers=0)
 ++++++++[text] name='Engine'
 ++++[table row]
-++++++[table cell] name='Chrome'
+++++++[table cell] name='Chrome' (row=1, col=0, row_span=1, col_span=1 n_row_headers=0, n_col_headers=1)
 ++++++++[text] name='Chrome'
-++++++[table cell] name='Blink'
+++++++[table cell] name='Blink' (row=1, col=1, row_span=1, col_span=1 n_row_headers=0, n_col_headers=1)
 ++++++++[text] name='Blink'
 ++++[table row]
-++++++[table cell] name='Safari'
+++++++[table cell] name='Safari' (row=2, col=0, row_span=1, col_span=1 n_row_headers=0, n_col_headers=1)
 ++++++++[text] name='Safari'
-++++++[table cell] name='WebKit'
+++++++[table cell] name='WebKit' (row=2, col=1, row_span=1, col_span=1 n_row_headers=0, n_col_headers=1)
 ++++++++[text] name='WebKit'
diff --git a/content/test/data/accessibility/html/col-expected-auralinux.txt b/content/test/data/accessibility/html/col-expected-auralinux.txt
index 293ae464..6863cda8 100644
--- a/content/test/data/accessibility/html/col-expected-auralinux.txt
+++ b/content/test/data/accessibility/html/col-expected-auralinux.txt
@@ -1,12 +1,12 @@
 [document web]
 ++[table] cols=2 headers=('Browser', 'Rendering Engine'); rows=2 headers=(NONE); caption=false; spans=(all: 1x1)
 ++++[table row]
-++++++[column header] name='Browser'
+++++++[column header] name='Browser' (row=0, col=0, row_span=1, col_span=1 n_row_headers=0, n_col_headers=0)
 ++++++++[text] name='Browser'
-++++++[column header] name='Rendering Engine'
+++++++[column header] name='Rendering Engine' (row=0, col=1, row_span=1, col_span=1 n_row_headers=0, n_col_headers=0)
 ++++++++[text] name='Rendering Engine'
 ++++[table row]
-++++++[table cell] name='Chrome'
+++++++[table cell] name='Chrome' (row=1, col=0, row_span=1, col_span=1 n_row_headers=0, n_col_headers=1)
 ++++++++[text] name='Chrome'
-++++++[table cell] name='Blink'
+++++++[table cell] name='Blink' (row=1, col=1, row_span=1, col_span=1 n_row_headers=0, n_col_headers=1)
 ++++++++[text] name='Blink'
diff --git a/content/test/data/accessibility/html/colgroup-expected-auralinux.txt b/content/test/data/accessibility/html/colgroup-expected-auralinux.txt
index a3b6397..93d4721 100644
--- a/content/test/data/accessibility/html/colgroup-expected-auralinux.txt
+++ b/content/test/data/accessibility/html/colgroup-expected-auralinux.txt
@@ -1,12 +1,12 @@
 [document web]
 ++[table] cols=2 headers=('Single', 'Pair'); rows=2 headers=(NONE); caption=false; spans=(all: 1x1)
 ++++[table row]
-++++++[column header] name='Single'
+++++++[column header] name='Single' (row=0, col=0, row_span=1, col_span=1 n_row_headers=0, n_col_headers=0)
 ++++++++[text] name='Single'
-++++++[column header] name='Pair'
+++++++[column header] name='Pair' (row=0, col=1, row_span=1, col_span=1 n_row_headers=0, n_col_headers=0)
 ++++++++[text] name='Pair'
 ++++[table row]
-++++++[table cell] name='A'
+++++++[table cell] name='A' (row=1, col=0, row_span=1, col_span=1 n_row_headers=0, n_col_headers=1)
 ++++++++[text] name='A'
-++++++[table cell] name='AA'
+++++++[table cell] name='AA' (row=1, col=1, row_span=1, col_span=1 n_row_headers=0, n_col_headers=1)
 ++++++++[text] name='AA'
diff --git a/content/test/data/accessibility/html/contenteditable-descendants-expected-auralinux.txt b/content/test/data/accessibility/html/contenteditable-descendants-expected-auralinux.txt
index a5a5d7b..fb2efbe 100644
--- a/content/test/data/accessibility/html/contenteditable-descendants-expected-auralinux.txt
+++ b/content/test/data/accessibility/html/contenteditable-descendants-expected-auralinux.txt
@@ -11,7 +11,7 @@
 ++++++[text] name='.' editable
 ++++[table] editable cols=1 headers=(NONE); rows=1 headers=(NONE); caption=false; spans=(all: 1x1)
 ++++++[table row] editable
-++++++++[table cell] name='Always expose editable tables as tables.' editable
+++++++++[table cell] name='Always expose editable tables as tables.' editable (row=0, col=0, row_span=1, col_span=1 n_row_headers=0, n_col_headers=0)
 ++++++++++[text] name='Always expose editable tables as tables.' editable
 ++++[list] editable
 ++++++[list item] editable
diff --git a/content/test/data/accessibility/html/contenteditable-descendants-with-selection-expected-auralinux.txt b/content/test/data/accessibility/html/contenteditable-descendants-with-selection-expected-auralinux.txt
index 125348a..2fa7f7a 100644
--- a/content/test/data/accessibility/html/contenteditable-descendants-with-selection-expected-auralinux.txt
+++ b/content/test/data/accessibility/html/contenteditable-descendants-with-selection-expected-auralinux.txt
@@ -11,7 +11,7 @@
 ++++++[text] name='.' editable
 ++++[table] editable cols=1 headers=(NONE); rows=1 headers=(NONE); caption=false; spans=(all: 1x1)
 ++++++[table row] editable
-++++++++[table cell] name='Always expose editable tables as tables.' editable
+++++++++[table cell] name='Always expose editable tables as tables.' editable (row=0, col=0, row_span=1, col_span=1 n_row_headers=0, n_col_headers=0)
 ++++++++++[text] name='Always expose editable tables as tables.' editable
 ++++[list] editable
 ++++++[list item] editable
diff --git a/content/test/data/accessibility/html/table-focusable-sections-expected-auralinux.txt b/content/test/data/accessibility/html/table-focusable-sections-expected-auralinux.txt
index 97848ca..f776f1d 100644
--- a/content/test/data/accessibility/html/table-focusable-sections-expected-auralinux.txt
+++ b/content/test/data/accessibility/html/table-focusable-sections-expected-auralinux.txt
@@ -2,24 +2,24 @@
 ++[table] cols=2 headers=('Sum', 'Subtraction'); rows=4 headers=(NONE); caption=false; spans=(all: 1x1)
 ++++[panel]
 ++++++[table row]
-++++++++[column header] name='Sum'
+++++++++[column header] name='Sum' (row=0, col=0, row_span=1, col_span=1 n_row_headers=0, n_col_headers=0)
 ++++++++++[text] name='Sum'
-++++++++[column header] name='Subtraction'
+++++++++[column header] name='Subtraction' (row=0, col=1, row_span=1, col_span=1 n_row_headers=0, n_col_headers=0)
 ++++++++++[text] name='Subtraction'
 ++++[panel]
 ++++++[table row]
-++++++++[table cell] name='10'
+++++++++[table cell] name='10' (row=1, col=0, row_span=1, col_span=1 n_row_headers=0, n_col_headers=1)
 ++++++++++[text] name='10'
-++++++++[table cell] name='7'
+++++++++[table cell] name='7' (row=1, col=1, row_span=1, col_span=1 n_row_headers=0, n_col_headers=1)
 ++++++++++[text] name='7'
 ++++++[table row]
-++++++++[table cell] name='2'
+++++++++[table cell] name='2' (row=2, col=0, row_span=1, col_span=1 n_row_headers=0, n_col_headers=1)
 ++++++++++[text] name='2'
-++++++++[table cell] name='4'
+++++++++[table cell] name='4' (row=2, col=1, row_span=1, col_span=1 n_row_headers=0, n_col_headers=1)
 ++++++++++[text] name='4'
 ++++[panel]
 ++++++[table row]
-++++++++[table cell] name='12'
+++++++++[table cell] name='12' (row=3, col=0, row_span=1, col_span=1 n_row_headers=0, n_col_headers=1)
 ++++++++++[text] name='12'
-++++++++[table cell] name='3'
+++++++++[table cell] name='3' (row=3, col=1, row_span=1, col_span=1 n_row_headers=0, n_col_headers=1)
 ++++++++++[text] name='3'
diff --git a/content/test/data/accessibility/html/table-simple-expected-auralinux.txt b/content/test/data/accessibility/html/table-simple-expected-auralinux.txt
index 0687a97..6f2a7de 100644
--- a/content/test/data/accessibility/html/table-simple-expected-auralinux.txt
+++ b/content/test/data/accessibility/html/table-simple-expected-auralinux.txt
@@ -1,17 +1,17 @@
 [document web] name='Table example'
 ++[table] cols=2 headers=('Pair', 'Single'); rows=3 headers=(NONE); caption=false; spans=(all: 1x1)
 ++++[table row]
-++++++[column header] name='Pair'
+++++++[column header] name='Pair' (row=0, col=0, row_span=1, col_span=1 n_row_headers=0, n_col_headers=0)
 ++++++++[text] name='Pair'
-++++++[column header] name='Single'
+++++++[column header] name='Single' (row=0, col=1, row_span=1, col_span=1 n_row_headers=0, n_col_headers=0)
 ++++++++[text] name='Single'
 ++++[table row]
-++++++[table cell] name='AB'
+++++++[table cell] name='AB' (row=1, col=0, row_span=1, col_span=1 n_row_headers=0, n_col_headers=1)
 ++++++++[text] name='AB'
-++++++[table cell] name='B'
+++++++[table cell] name='B' (row=1, col=1, row_span=1, col_span=1 n_row_headers=0, n_col_headers=1)
 ++++++++[text] name='B'
 ++++[table row]
-++++++[table cell] name='CD'
+++++++[table cell] name='CD' (row=2, col=0, row_span=1, col_span=1 n_row_headers=0, n_col_headers=1)
 ++++++++[text] name='CD'
-++++++[table cell] name='D'
+++++++[table cell] name='D' (row=2, col=1, row_span=1, col_span=1 n_row_headers=0, n_col_headers=1)
 ++++++++[text] name='D'
diff --git a/content/test/data/accessibility/html/table-spans-expected-auralinux.txt b/content/test/data/accessibility/html/table-spans-expected-auralinux.txt
index f0fa7c7..3380342 100644
--- a/content/test/data/accessibility/html/table-spans-expected-auralinux.txt
+++ b/content/test/data/accessibility/html/table-spans-expected-auralinux.txt
@@ -1,21 +1,21 @@
 [document web] name='Table example with rowspan and colspan'
 ++[table] cols=2 headers=(NONE); rows=2 headers=(NONE); caption=false; spans=(cell at 0,0: 2x1, cell at 1,0: 2x1)
 ++++[table row]
-++++++[table cell] name='AD'
+++++++[table cell] name='AD' (row=0, col=0, row_span=2, col_span=1 n_row_headers=0, n_col_headers=0)
 ++++++++[text] name='AD'
-++++++[table cell] name='BC'
+++++++[table cell] name='BC' (row=0, col=1, row_span=1, col_span=1 n_row_headers=0, n_col_headers=0)
 ++++++++[text] name='BC'
 ++++[table row]
-++++++[table cell] name='EF'
+++++++[table cell] name='EF' (row=1, col=1, row_span=1, col_span=1 n_row_headers=0, n_col_headers=0)
 ++++++++[text] name='EF'
 ++[table] cols=3 headers=(NONE); rows=2 headers=(NONE); caption=false; spans=(cell at 0,0: 2x1, cell at 0,1: 1x2, cell at 0,2: 1x2, cell at 1,0: 2x1)
 ++++[table row]
-++++++[table cell] name='AD'
+++++++[table cell] name='AD' (row=0, col=0, row_span=2, col_span=1 n_row_headers=0, n_col_headers=0)
 ++++++++[text] name='AD'
-++++++[table cell] name='BC'
+++++++[table cell] name='BC' (row=0, col=1, row_span=1, col_span=2 n_row_headers=0, n_col_headers=0)
 ++++++++[text] name='BC'
 ++++[table row]
-++++++[table cell] name='EF'
+++++++[table cell] name='EF' (row=1, col=1, row_span=1, col_span=1 n_row_headers=0, n_col_headers=0)
 ++++++++[text] name='EF'
-++++++[table cell] name='GH'
+++++++[table cell] name='GH' (row=1, col=2, row_span=1, col_span=1 n_row_headers=0, n_col_headers=0)
 ++++++++[text] name='GH'
diff --git a/content/test/data/accessibility/html/table-th-colheader-expected-auralinux.txt b/content/test/data/accessibility/html/table-th-colheader-expected-auralinux.txt
index 10721b8..15a3f20 100644
--- a/content/test/data/accessibility/html/table-th-colheader-expected-auralinux.txt
+++ b/content/test/data/accessibility/html/table-th-colheader-expected-auralinux.txt
@@ -1,12 +1,12 @@
 [document web]
 ++[table] cols=2 headers=('Firstname', 'Lastname'); rows=2 headers=(NONE); caption=false; spans=(all: 1x1)
 ++++[table row]
-++++++[column header] name='Firstname' table-cell-index:0
+++++++[column header] name='Firstname' table-cell-index:0 (row=0, col=0, row_span=1, col_span=1 n_row_headers=0, n_col_headers=0)
 ++++++++[text] name='Firstname'
-++++++[column header] name='Lastname' table-cell-index:1
+++++++[column header] name='Lastname' table-cell-index:1 (row=0, col=1, row_span=1, col_span=1 n_row_headers=0, n_col_headers=0)
 ++++++++[text] name='Lastname'
 ++++[table row]
-++++++[table cell] name='Jill' table-cell-index:2
+++++++[table cell] name='Jill' table-cell-index:2 (row=1, col=0, row_span=1, col_span=1 n_row_headers=0, n_col_headers=1)
 ++++++++[text] name='Jill'
-++++++[table cell] name='Smith' table-cell-index:3
+++++++[table cell] name='Smith' table-cell-index:3 (row=1, col=1, row_span=1, col_span=1 n_row_headers=0, n_col_headers=1)
 ++++++++[text] name='Smith'
diff --git a/content/test/data/accessibility/html/table-th-rowheader-expected-auralinux.txt b/content/test/data/accessibility/html/table-th-rowheader-expected-auralinux.txt
index db40158..ed81a2c 100644
--- a/content/test/data/accessibility/html/table-th-rowheader-expected-auralinux.txt
+++ b/content/test/data/accessibility/html/table-th-rowheader-expected-auralinux.txt
@@ -1,12 +1,12 @@
 [document web] name='Table example - th rowheader'
 ++[table] cols=2 headers=(NONE); rows=2 headers=('Firstname', 'Lastname'); caption=false; spans=(all: 1x1)
 ++++[table row]
-++++++[row header] name='Firstname'
+++++++[row header] name='Firstname' (row=0, col=0, row_span=1, col_span=1 n_row_headers=0, n_col_headers=0)
 ++++++++[text] name='Firstname'
-++++++[table cell] name='Jill'
+++++++[table cell] name='Jill' (row=0, col=1, row_span=1, col_span=1 n_row_headers=1, n_col_headers=0)
 ++++++++[text] name='Jill'
 ++++[table row]
-++++++[row header] name='Lastname'
+++++++[row header] name='Lastname' (row=1, col=0, row_span=1, col_span=1 n_row_headers=0, n_col_headers=0)
 ++++++++[text] name='Lastname'
-++++++[table cell] name='Smith'
+++++++[table cell] name='Smith' (row=1, col=1, row_span=1, col_span=1 n_row_headers=1, n_col_headers=0)
 ++++++++[text] name='Smith'
diff --git a/content/test/data/accessibility/html/table-thead-tbody-tfoot-expected-auralinux.txt b/content/test/data/accessibility/html/table-thead-tbody-tfoot-expected-auralinux.txt
index 285701d..f71c4455 100644
--- a/content/test/data/accessibility/html/table-thead-tbody-tfoot-expected-auralinux.txt
+++ b/content/test/data/accessibility/html/table-thead-tbody-tfoot-expected-auralinux.txt
@@ -1,22 +1,22 @@
 [document web] name='Table example - thead, tbody, tfoot'
 ++[table] cols=2 headers=('Sum', 'Subtraction'); rows=4 headers=(NONE); caption=false; spans=(all: 1x1)
 ++++[table row]
-++++++[column header] name='Sum'
+++++++[column header] name='Sum' (row=0, col=0, row_span=1, col_span=1 n_row_headers=0, n_col_headers=0)
 ++++++++[text] name='Sum'
-++++++[column header] name='Subtraction'
+++++++[column header] name='Subtraction' (row=0, col=1, row_span=1, col_span=1 n_row_headers=0, n_col_headers=0)
 ++++++++[text] name='Subtraction'
 ++++[table row]
-++++++[table cell] name='10'
+++++++[table cell] name='10' (row=1, col=0, row_span=1, col_span=1 n_row_headers=0, n_col_headers=1)
 ++++++++[text] name='10'
-++++++[table cell] name='7'
+++++++[table cell] name='7' (row=1, col=1, row_span=1, col_span=1 n_row_headers=0, n_col_headers=1)
 ++++++++[text] name='7'
 ++++[table row]
-++++++[table cell] name='2'
+++++++[table cell] name='2' (row=2, col=0, row_span=1, col_span=1 n_row_headers=0, n_col_headers=1)
 ++++++++[text] name='2'
-++++++[table cell] name='4'
+++++++[table cell] name='4' (row=2, col=1, row_span=1, col_span=1 n_row_headers=0, n_col_headers=1)
 ++++++++[text] name='4'
 ++++[table row]
-++++++[table cell] name='12'
+++++++[table cell] name='12' (row=3, col=0, row_span=1, col_span=1 n_row_headers=0, n_col_headers=1)
 ++++++++[text] name='12'
-++++++[table cell] name='3'
+++++++[table cell] name='3' (row=3, col=1, row_span=1, col_span=1 n_row_headers=0, n_col_headers=1)
 ++++++++[text] name='3'
diff --git a/ui/accessibility/platform/ax_platform_node_auralinux.cc b/ui/accessibility/platform/ax_platform_node_auralinux.cc
index 0963a99..2a95a27 100644
--- a/ui/accessibility/platform/ax_platform_node_auralinux.cc
+++ b/ui/accessibility/platform/ax_platform_node_auralinux.cc
@@ -4,6 +4,7 @@
 
 #include "ui/accessibility/platform/ax_platform_node_auralinux.h"
 
+#include <dlfcn.h>
 #include <stdint.h>
 
 #include <algorithm>
@@ -56,6 +57,47 @@
 // null if if the AtkObject is destroyed.
 AtkObject* g_active_top_level_frame = nullptr;
 
+// AtkTableCell was introduced in ATK 2.12. Ubuntu Trusty has ATK 2.10.
+// Compile-time checks are in place for ATK versions that are older than 2.12.
+// However, we also need runtime checks in case the version we are building
+// against is newer than the runtime version. To prevent a runtime error, we
+// check that we have a version of ATK that supports AtkTableCell. If we do,
+// we dynamically load the symbol; if we don't, the interface is absent from
+// the accessible object and its methods will not be exposed or callable.
+// The definitions below ensure we have no missing symbols. Note that in
+// environments where we have ATK > 2.12, the definitions of AtkTableCell and
+// AtkTableCellIface below are overridden by the runtime version.
+// TODO(accessibility) Remove these definitions, along with the use of
+// LoadTableCellMethods() when 2.12 is the minimum supported version.
+typedef struct _AtkTableCell AtkTableCell;
+typedef struct _AtkTableCellIface AtkTableCellIface;
+typedef GType (*cell_get_type_func)();
+typedef GPtrArray* (*get_column_header_cells_func)(AtkTableCell* cell);
+typedef GPtrArray* (*get_row_header_cells_func)(AtkTableCell* cell);
+typedef bool (*get_row_column_span_func)(AtkTableCell* cell,
+                                         gint* row,
+                                         gint* column,
+                                         gint* row_span,
+                                         gint* col_span);
+
+cell_get_type_func cell_get_type = nullptr;
+get_column_header_cells_func get_column_header_cells = nullptr;
+get_row_header_cells_func get_row_header_cells = nullptr;
+get_row_column_span_func get_row_column_span = nullptr;
+
+static bool LoadTableCellMethods() {
+  cell_get_type = reinterpret_cast<cell_get_type_func>(
+      dlsym(RTLD_DEFAULT, "atk_table_cell_get_type"));
+  get_column_header_cells = reinterpret_cast<get_column_header_cells_func>(
+      dlsym(RTLD_DEFAULT, "atk_table_cell_get_column_header_cells"));
+  get_row_header_cells = reinterpret_cast<get_row_header_cells_func>(
+      dlsym(RTLD_DEFAULT, "atk_table_cell_get_row_header_cells"));
+  get_row_column_span = reinterpret_cast<get_row_column_span_func>(
+      dlsym(RTLD_DEFAULT, "atk_table_cell_get_row_column_span"));
+
+  return cell_get_type;
+}
+
 static AtkObject* FindAtkObjectParentFrame(AtkObject* atk_object) {
   while (atk_object) {
     if (atk_object_get_role(atk_object) == ATK_ROLE_FRAME)
@@ -286,6 +328,10 @@
     "footnote",  // ATK_ROLE_FOOTNOTE = 122.
 };
 
+#if defined(ATK_CHECK_VERSION) && ATK_CHECK_VERSION(2, 12, 0)
+#define ATK_212
+#endif
+
 #if defined(ATK_CHECK_VERSION) && ATK_CHECK_VERSION(2, 16, 0)
 #define ATK_216
 #endif
@@ -1581,6 +1627,116 @@
     nullptr};
 
 //
+// AtkTableCell interface (Requires at least ATK 2.12)
+//
+static void IdsToGPtrArray(AXPlatformNodeDelegate* delegate,
+                           std::vector<int32_t>& ids,
+                           GPtrArray* array) {
+  for (const auto& node_id : ids) {
+    if (AXPlatformNode* header = delegate->GetFromNodeID(node_id)) {
+      if (AtkObject* atk_header = header->GetNativeViewAccessible()) {
+        g_object_ref(atk_header);
+        g_ptr_array_add(array, atk_header);
+      }
+    }
+  }
+}
+
+#if defined(ATK_212)
+gint AXPlatformNodeAuraLinuxGetColumnSpan(AtkTableCell* cell) {
+  if (auto* obj = AtkObjectToAXPlatformNodeAuraLinux(ATK_OBJECT(cell)))
+    return obj->GetTableColumnSpan();
+
+  return 0;
+}
+
+GPtrArray* AXPlatformNodeAuraLinuxGetColumnHeaderCells(AtkTableCell* cell) {
+  GPtrArray* array = g_ptr_array_new();
+
+  // AtkTableCell is implemented on cells, row headers, and column headers.
+  // Calling GetColHeaderNodeIds() on a column header cell will include that
+  // column header, along with any other column headers in the column which
+  // may or may not describe the header cell in question. Therefore, just return
+  // headers for non-header cells.
+  if (atk_object_get_role(ATK_OBJECT(cell)) != ATK_ROLE_TABLE_CELL)
+    return array;
+
+  if (auto* obj = AtkObjectToAXPlatformNodeAuraLinux(ATK_OBJECT(cell))) {
+    if (auto* table = obj->GetTable()) {
+      std::vector<int32_t> ids =
+          table->GetDelegate()->GetColHeaderNodeIds(obj->GetTableColumn());
+      IdsToGPtrArray(table->GetDelegate(), ids, array);
+    }
+  }
+
+  return array;
+}
+
+gboolean AXPlatformNodeAuraLinuxGetCellPosition(AtkTableCell* cell,
+                                                gint* row,
+                                                gint* column) {
+  if (auto* obj = AtkObjectToAXPlatformNodeAuraLinux(ATK_OBJECT(cell))) {
+    *row = obj->GetTableRow();
+    *column = obj->GetTableColumn();
+    return true;
+  }
+
+  return false;
+}
+
+gint AXPlatformNodeAuraLinuxGetRowSpan(AtkTableCell* cell) {
+  if (auto* obj = AtkObjectToAXPlatformNodeAuraLinux(ATK_OBJECT(cell)))
+    return obj->GetTableRowSpan();
+
+  return 0;
+}
+
+GPtrArray* AXPlatformNodeAuraLinuxGetRowHeaderCells(AtkTableCell* cell) {
+  GPtrArray* array = g_ptr_array_new();
+
+  // AtkTableCell is implemented on cells, row headers, and column headers.
+  // Calling GetRowHeaderNodeIds() on a row header cell will include that
+  // row header, along with any other row headers in the row which may or
+  // may not describe the header cell in question. Therefore, just return
+  // headers for non-header cells.
+  if (atk_object_get_role(ATK_OBJECT(cell)) != ATK_ROLE_TABLE_CELL)
+    return array;
+
+  if (auto* obj = AtkObjectToAXPlatformNodeAuraLinux(ATK_OBJECT(cell))) {
+    if (auto* table = obj->GetTable()) {
+      std::vector<int32_t> ids =
+          table->GetDelegate()->GetRowHeaderNodeIds(obj->GetTableRow());
+      IdsToGPtrArray(table->GetDelegate(), ids, array);
+    }
+  }
+
+  return array;
+}
+
+AtkObject* AXPlatformNodeAuraLinuxGetTable(AtkTableCell* cell) {
+  if (auto* obj = AtkObjectToAXPlatformNodeAuraLinux(ATK_OBJECT(cell))) {
+    if (auto* table = obj->GetTable())
+      return table->GetNativeViewAccessible();
+  }
+
+  return nullptr;
+}
+
+static void AXTableCellInterfaceBaseInit(AtkTableCellIface* iface) {
+  iface->get_column_span = AXPlatformNodeAuraLinuxGetColumnSpan;
+  iface->get_column_header_cells = AXPlatformNodeAuraLinuxGetColumnHeaderCells;
+  iface->get_position = AXPlatformNodeAuraLinuxGetCellPosition;
+  iface->get_row_span = AXPlatformNodeAuraLinuxGetRowSpan;
+  iface->get_row_header_cells = AXPlatformNodeAuraLinuxGetRowHeaderCells;
+  iface->get_table = AXPlatformNodeAuraLinuxGetTable;
+}
+
+static const GInterfaceInfo TableCellInfo = {
+    reinterpret_cast<GInterfaceInitFunc>(AXTableCellInterfaceBaseInit), nullptr,
+    nullptr};
+#endif
+
+//
 // The rest of the AXPlatformNodeAtk code, not specific to one
 // of the Atk* interfaces.
 //
@@ -1708,6 +1864,15 @@
   if (role == ATK_ROLE_TABLE)
     interface_mask |= 1 << ATK_TABLE_INTERFACE;
 
+  // Because the TableCell Interface is only supported in ATK version 2.12 and
+  // later, GetAccessibilityGType has a runtime check to verify we have a recent
+  // enough version. If we don't, GetAccessibilityGType will exclude
+  // AtkTableCell from the supported interfaces and none of its methods or
+  // properties will be exposed to assistive technologies.
+  if (role == ATK_ROLE_TABLE_CELL || role == ATK_ROLE_COLUMN_HEADER ||
+      role == ATK_ROLE_ROW_HEADER)
+    interface_mask |= 1 << ATK_TABLE_CELL_INTERFACE;
+
   return interface_mask;
 }
 
@@ -1756,6 +1921,11 @@
   if (interface_mask_ & (1 << ATK_TABLE_INTERFACE))
     g_type_add_interface_static(type, ATK_TYPE_TABLE, &TableInfo);
 
+  // Run-time check to ensure AtkTableCell is supported (requires ATK 2.12).
+  if (LoadTableCellMethods() &&
+      interface_mask_ & (1 << ATK_TABLE_CELL_INTERFACE))
+    g_type_add_interface_static(type, cell_get_type(), &TableCellInfo);
+
   return type;
 }
 
@@ -2526,8 +2696,6 @@
         base::StringPrintf("caption=%s;", caption ? "true" : "false"));
 
     // Summarize information about the cells from the table's perspective here.
-    // TODO(jdiggs): When AtkTableCell is implemented, we will use it to output
-    // specifics on a per-cell basis.
     std::vector<std::string> span_info;
     for (int r = 0; r < n_rows; r++) {
       for (int c = 0; c < n_cols; c++) {
@@ -2547,6 +2715,48 @@
   }
 
   dict->Set("table", std::move(table_properties));
+
+  // Properties obtained via AtkTableCell, if possible. If we do not have at
+  // least ATK 2.12, use the same logic in our AtkTableCell implementation so
+  // that tests can still be run.
+  auto cell_properties = std::make_unique<base::ListValue>();
+  if (role == ATK_ROLE_TABLE_CELL || role == ATK_ROLE_COLUMN_HEADER ||
+      role == ATK_ROLE_ROW_HEADER) {
+    int row, col, row_span, col_span;
+    GPtrArray* col_headers;
+    GPtrArray* row_headers;
+    if (cell_get_type) {
+      AtkTableCell* cell = G_TYPE_CHECK_INSTANCE_CAST(
+          (atk_object_), cell_get_type(), AtkTableCell);
+      col_headers = get_column_header_cells(cell);
+      row_headers = get_row_header_cells(cell);
+      get_row_column_span(cell, &row, &col, &row_span, &col_span);
+    } else {
+      auto* obj = AtkObjectToAXPlatformNodeAuraLinux(atk_object_);
+      row = obj->GetTableRow();
+      col = obj->GetTableColumn();
+      row_span = obj->GetTableRowSpan();
+      col_span = obj->GetTableColumnSpan();
+      col_headers = g_ptr_array_new();
+      row_headers = g_ptr_array_new();
+      if (role == ATK_ROLE_TABLE_CELL) {
+        auto* delegate = obj->GetTable()->GetDelegate();
+        std::vector<int32_t> col_header_ids =
+            delegate->GetColHeaderNodeIds(col);
+        std::vector<int32_t> row_header_ids =
+            delegate->GetRowHeaderNodeIds(row);
+        IdsToGPtrArray(delegate, col_header_ids, col_headers);
+        IdsToGPtrArray(delegate, row_header_ids, row_headers);
+      }
+    }
+    cell_properties->AppendString(
+        base::StringPrintf("(row=%i, col=%i, row_span=%i, col_span=%i", row,
+                           col, row_span, col_span));
+    cell_properties->AppendString(
+        base::StringPrintf("n_row_headers=%i, n_col_headers=%i)",
+                           row_headers->len, col_headers->len));
+  }
+  dict->Set("cell", std::move(cell_properties));
 }
 
 gfx::NativeViewAccessible AXPlatformNodeAuraLinux::GetNativeViewAccessible() {
diff --git a/ui/accessibility/platform/ax_platform_node_auralinux.h b/ui/accessibility/platform/ax_platform_node_auralinux.h
index ff805bb5..fb9125c 100644
--- a/ui/accessibility/platform/ax_platform_node_auralinux.h
+++ b/ui/accessibility/platform/ax_platform_node_auralinux.h
@@ -139,6 +139,7 @@
     ATK_IMAGE_INTERFACE,
     ATK_SELECTION_INTERFACE,
     ATK_TABLE_INTERFACE,
+    ATK_TABLE_CELL_INTERFACE,
     ATK_TEXT_INTERFACE,
     ATK_VALUE_INTERFACE,
     ATK_WINDOW_INTERFACE,