[evolution-patches] [gal]#52935, evo-1.5 sometime crashes when click on the message list



Hi, 
   Attached is a patch which fixed bug #52935. This is a very critical
bug, which caused a lot of crash when having accessibility enabled.

   This bug can be reproduced by the following steps:
1. start evolution with a11y enabled
2. goto a mail folder with few messages
3. click on the message.
4. switch to another folder which contains a lot of messages, click to
the message list several times, then evolution will goto crash at most
times.

The bug is caused by the following reason:
the cell_data pointer (used to the retrieve the AtkObject of table
cell)  is initialize the first folder is accessed, when switched to
another folder, the cell_data pointer keeps the original value ( The
cell_data pointer's value should change, but it didn't. )  

Then if we access a message out of the first folder's range(in another
folder), the AtkObject which we get via cell_data pointer is invalid,
and a SIGSEGV signal is raised.

Since we can't figure out which messages have been changed when the 
message-list is changed(when deleting a message, inserting a new message, 
switching mail folder, etc.), so we have to clear all the AtkObjects stored 
in the cell_data, and reinit it the next time we access it. As the clearing 
operation will cause a noticeable latency when the current-folder contains 
a lot of mails, this is not a very good solution, but it can just work.

--
Regards,
Eric
Index: gal-a11y-e-table-item.c
===================================================================
RCS file: /cvs/gnome/gal/gal/a11y/e-table/gal-a11y-e-table-item.c,v
retrieving revision 1.9
diff -u -r1.9 gal-a11y-e-table-item.c
--- gal-a11y-e-table-item.c	2 Dec 2003 08:25:17 -0000	1.9
+++ gal-a11y-e-table-item.c	11 Apr 2004 08:32:21 -0000
@@ -13,6 +13,8 @@
 #include "gal-a11y-e-cell.h"
 #include "gal-a11y-util.h"
 #include <gal/e-table/e-table-subset.h>
+#include <gal/e-table/e-table.h>
+#include <gal/e-table/e-tree.h>
 
 #include <atk/atkobject.h>
 #include <atk/atktable.h>
@@ -40,6 +42,7 @@
 	int cursor_change_id;
 	ETableCol ** columns;
 	ESelectionModel *selection;
+	GtkWidget *widget;
 };
 
 static gboolean gal_a11y_e_table_item_ref_selection (GalA11yETableItem *a11y,
@@ -294,7 +297,9 @@
 		ETableCol *ecol = e_table_header_get_column (item->header, column);
 		gpointer * cell_data;
 
-		cell_data = GET_PRIVATE (table)->cell_data;
+		cell_data = eti_reinit_data (table, item);
+		if (cell_data == NULL) 
+			return NULL;
 
 		if (cell_data[row*item->cols + column] == NULL) {
 			ret = gal_a11y_e_cell_registry_get_object (NULL,
@@ -309,7 +314,6 @@
 				g_object_weak_ref (G_OBJECT (ret),
 						   (GWeakNotify) cell_destroyed,
 						   ret);
-
 			else
 				ret = NULL;
 		} else {
@@ -675,6 +679,79 @@
 	g_signal_emit_by_name (table_item, "visible-data-changed");
 }
 
+/* 
+ * reinit the eti's private data
+ * make sure it is synchronized with the gal-e-table-item
+ */
+static gpointer *
+eti_reinit_data (AtkTable *table, ETableItem *item)
+{
+	gpointer * cell_data;
+
+	int oldsize, newsize;
+	cell_data = GET_PRIVATE (table)->cell_data;
+	if (GET_PRIVATE (table)->rows != item->rows 
+		|| GET_PRIVATE (table)->cols != item->cols ) { /* reinit cell_data */
+		oldsize = GET_PRIVATE (table)->cols * GET_PRIVATE (table)->rows;
+		newsize = item->cols*item->rows;
+		GET_PRIVATE (table)->cols = item->cols;
+		GET_PRIVATE (table)->rows = item->rows;
+
+		cell_data = g_realloc(cell_data, newsize*sizeof(gpointer));
+		if (newsize > oldsize)
+			memset(&cell_data[oldsize], 0, (newsize-oldsize)*sizeof(gpointer));
+		
+		GET_PRIVATE (table)->cell_data = cell_data;
+	}
+	return cell_data;
+}
+
+/* 
+ * clear all the AtkObjects stored in the cell_data
+ * doesn't free the cell_data or resize it.
+ */
+static void
+eti_clear_rows (ETableModel * model, AtkObject * table_item, int row, int count)
+{
+	gint i,j, n_rows, n_cols;
+	gpointer *cell_data;
+
+	g_return_if_fail (model && table_item);
+
+	cell_data = GET_PRIVATE (table_item)->cell_data;
+	g_return_if_fail (cell_data);
+
+	n_rows = GET_PRIVATE (table_item)->rows;
+	n_cols = GET_PRIVATE (table_item)->cols;
+
+	g_return_if_fail( row >= 0 && count > 0 && row+count <= n_rows);
+
+	/* DEFUNCT the deleted cells. */
+	for (i = row; i < row+count; i ++) {
+		for (j = 0; j < n_cols; j ++) {
+			if (cell_data[i*n_cols + j] != NULL) {
+				AtkObject * a11y;
+
+				a11y = ATK_OBJECT(cell_data[i*n_cols + j]);
+				gal_a11y_e_cell_add_state (GAL_A11Y_E_CELL(a11y), ATK_STATE_DEFUNCT, TRUE);
+				cell_data[i*n_cols + j] = NULL;
+			}
+		}
+	}
+
+	g_signal_emit_by_name (table_item, "row-deleted", row,
+			       count, NULL);
+
+	for (i = row; i < row+count; i ++) {
+		for (j = 0; j < n_cols; j ++) {
+			g_signal_emit_by_name (table_item,
+				"children_changed::remove",
+				( (i*n_cols) + j), NULL, NULL);
+		}
+        }
+	g_signal_emit_by_name (table_item, "visible-data-changed");
+}
+
 static void
 eti_rows_deleted (ETableModel * model, int row, int count, 
 		  AtkObject * table_item)
@@ -742,6 +819,22 @@
 	g_signal_emit_by_name (table_item, "visible-data-changed");
 }
 
+static void
+eti_tree_model_node_changed_cb (ETreeModel *model, ETreePath node, ETableItem *eti)
+{
+	AtkObject *atk_obj;
+	GalA11yETableItem *a11y;
+
+	g_return_if_fail (E_IS_TABLE_ITEM (eti));
+
+	atk_obj = atk_gobject_accessible_for_object (G_OBJECT (eti));
+	a11y = GAL_A11Y_E_TABLE_ITEM (atk_obj);
+
+	/* we can't figure out which rows are changed, so just clear all of them ... */
+	if  (GET_PRIVATE (a11y)->rows > 0)
+		eti_clear_rows (eti->table_model, atk_obj, 0, GET_PRIVATE (a11y)->rows);
+}
+
 enum {
         ETI_HEADER_UNCHANGED = 0,
         ETI_HEADER_REORDERED,
@@ -1035,8 +1128,10 @@
 			   int index_in_parent)
 {
 	GalA11yETableItem *a11y;
+	AtkObject *accessible;
 	int n;
 
+	g_return_val_if_fail (item && item->cols >= 0 && item->rows >= 0, NULL);
 	a11y = g_object_new (gal_a11y_e_table_item_get_type (), NULL);
 
 	atk_object_initialize (ATK_OBJECT (a11y), item);
@@ -1044,7 +1139,8 @@
 	GET_PRIVATE (a11y)->parent = parent;
 	GET_PRIVATE (a11y)->index_in_parent = index_in_parent;
 
-	g_return_val_if_fail (item->cols >= 0 && item->rows >= 0, NULL);
+	accessible  = ATK_OBJECT(a11y);
+	accessible->role = ATK_ROLE_TREE_TABLE; 
 	/* Initialize cell data. */
 	n = item->cols * item->rows;
 	GET_PRIVATE (a11y)->cols = item->cols;
@@ -1070,6 +1166,15 @@
 		if (item->selection)
 			gal_a11y_e_table_item_ref_selection (a11y,
 							     item->selection);
+
+		/* find the TableItem's parent: table or tree */
+		GET_PRIVATE (a11y)->widget = gtk_widget_get_parent (GTK_WIDGET (item->parent.canvas));
+		if (E_IS_TREE (GET_PRIVATE (a11y)->widget)) {
+			ETreeModel *model;
+			model = e_tree_get_model (E_TREE (GET_PRIVATE (a11y)->widget));
+			g_signal_connect (G_OBJECT(model), "node_changed",
+					G_CALLBACK (eti_tree_model_node_changed_cb), item);
+		} 
 	}
 	if (parent)
 		g_object_ref (parent);
@@ -1186,11 +1291,13 @@
 
 	g_signal_emit_by_name (a11y, "selection_changed");
 
-        cell = atk_table_ref_at (ATK_TABLE (a11y), row, col);
-        if (ATK_IS_OBJECT (cell))
-                g_signal_emit_by_name  (a11y,
-                                        "active-descendant-changed",
-                                        cell);
+	cell = atk_table_ref_at (ATK_TABLE (a11y), row, col);
+	if (cell != NULL) {
+        	if (ATK_IS_OBJECT (cell)) {
+			gal_a11y_e_cell_add_state(cell, ATK_STATE_FOCUSED, FALSE);
+			atk_focus_tracker_notify (cell);
+		}
+	}
 }
 
 /* atk selection */


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]