Skip to content

SWTFocusCellManager does not check for disposed TableItem #3089

@marcushoepfner

Description

@marcushoepfner

Describe the bug
We are having our own framework which can render swt/jface user interfaces based on EMF models.
We are using databinding everywhere, also for table and tree viewers.
Additionally we interfere in table manipulation, like remove.
And I guess the following bug can only happen in due to our framework

The bug seems to only occur on MacOS, but not on every Mac machine. For example a colleague of mine get's the issue, while I cannot reproduce it on my machine.

What happens?
If a row from a tableviewer is removed, the table viewer is corrupted afterwards (refer to the first corrupted line):

Image

This is the error in the console:
org.eclipse.swt.SWTException: Widget is disposed at org.eclipse.swt.SWT.error(SWT.java:4950) at org.eclipse.swt.SWT.error(SWT.java:4865) at org.eclipse.swt.SWT.error(SWT.java:4836) at org.eclipse.swt.widgets.Widget.error(Widget.java:854) at org.eclipse.swt.widgets.Widget.checkWidget(Widget.java:633) at org.eclipse.swt.widgets.Widget.getData(Widget.java:920) at org.eclipse.jface.viewers.AbstractTableViewer.setSelectionToWidget(AbstractTableViewer.java:930) at org.eclipse.jface.viewers.StructuredViewer.setSelectionToWidget(StructuredViewer.java:1709) at org.eclipse.jface.viewers.StructuredViewer.preservingSelection(StructuredViewer.java:1402) at org.eclipse.jface.viewers.StructuredViewer.preservingSelection(StructuredViewer.java:1356) at org.eclipse.jface.viewers.AbstractTableViewer.remove(AbstractTableViewer.java:839) at org.eclipse.jface.viewers.TableViewer.remove(TableViewer.java:440) at org.eclipse.jface.viewers.AbstractTableViewer.remove(AbstractTableViewer.java:859) at org.eclipse.jface.internal.databinding.viewers.TableViewerUpdater.remove(TableViewerUpdater.java:43) at org.eclipse.jface.databinding.viewers.ObservableListContentProvider$Impl$2.handleRemove(ObservableListContentProvider.java:134) at org.eclipse.core.databinding.observable.list.ListDiff.accept(ListDiff.java:152) at org.eclipse.jface.databinding.viewers.ObservableListContentProvider$Impl.handleListChange(ObservableListContentProvider.java:126) at org.eclipse.core.databinding.observable.list.ListChangeEvent.dispatch(ListChangeEvent.java:75) at org.eclipse.core.databinding.observable.ChangeManager.fireEvent(ChangeManager.java:122) at org.eclipse.core.databinding.observable.list.ObservableList.fireListChange(ObservableList.java:84) at org.eclipse.core.databinding.observable.list.WritableList.remove(WritableList.java:174) at table.bug.DataboundTableViewerDialog.remove(DataboundTableViewerDialog.java:149) at org.eclipse.swt.events.SelectionListener$1.widgetSelected(SelectionListener.java:83) at org.eclipse.swt.widgets.TypedListener.handleEvent(TypedListener.java:290) at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:91) at org.eclipse.swt.widgets.Display.sendEvent(Display.java:4671) at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1657) at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1680) at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1665) at org.eclipse.swt.widgets.Widget.notifyListeners(Widget.java:1394) at org.eclipse.swt.widgets.Display.runDeferredEvents(Display.java:4438) at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:4014) at org.eclipse.jface.window.Window.runEventLoop(Window.java:819) at org.eclipse.jface.window.Window.open(Window.java:799) at table.bug.DataboundTableViewerDialog.main(DataboundTableViewerDialog.java:45)

The table item seems to be disposed.

What I found out:
The actual error is occurring here but the error could only be seen in the debugger, not in the error log or console:

Thread [main] (Suspended (breakpoint at line 854 in Widget))	
	TableItem(Widget).error(int) line: 854	
	36 collapsed frames	
		TableItem(Widget).checkWidget() line: 633	
		TableItem(Widget).getData() line: 920	
		TableViewerRow.getElement() line: 172	
		TableViewerRow(ViewerRow).getCell(int) line: 212	
		TableViewerFocusCellManager.getFocusCell() line: 107	
		SWTFocusCellManager$1.getName(AccessibleEvent) line: 160	
		Accessible.getTitleAttribute(int) line: 1919	
		Accessible.internal_accessibilityIsIgnored(int) line: 1417	
		Table(Composite).accessibilityIsIgnored(long, long) line: 194	
		Display.windowProc(long, long) line: 6177	
		OS.objc_msgSendSuper(objc_super, long, long, boolean) line: not available [native method]	
		Table(Widget).selectRowIndexes_byExtendingSelection(long, long, long, boolean) line: 1634	
		Table.selectRowIndexes_byExtendingSelection(long, long, long, boolean) line: 3323	
		Display.windowProc(long, long, long, long) line: 6757	
		OS.objc_msgSend(long, long, long, boolean) line: not available [native method]	
		SWTTableView(NSTableView).selectRowIndexes(NSIndexSet, boolean) line: 158	
		Table.select(int[], int, boolean) line: 2549	
		Table.fixSelection(int, boolean) line: 1321	
		Table.remove(int[]) line: 2340	
		TableViewer.doRemove(int[]) line: 295	
		TableViewer(AbstractTableViewer).internalRemove(Object[]) line: 784	
		TableViewer(AbstractTableViewer).lambda$1(Object[]) line: 839	
		Lambda.run() line: not available	
		TableViewer(StructuredViewer).preservingSelection(Runnable, boolean) line: 1395	
		TableViewer(StructuredViewer).preservingSelection(Runnable) line: 1356	
		TableViewer(AbstractTableViewer).remove(Object[]) line: 839	
		TableViewer.remove(Object[]) line: 440	
		TableViewer(AbstractTableViewer).remove(Object) line: 859	
		TableViewerUpdater<E>.remove(E, int) line: 43	
		ObservableListContentProvider$Impl$2.handleRemove(int, E) line: 134	
		Diffs$8(ListDiff<E>).accept(ListDiffVisitor<? super E>) line: 152	
		ObservableListContentProvider$Impl<E>.handleListChange(ListChangeEvent<? extends E>) line: 126	
		ListChangeEvent<E>.dispatch(IObservablesListener) line: 75	
		WritableList<E>(ChangeManager).fireEvent(ObservableEvent) line: 122	
		WritableList<E>(ObservableList<E>).fireListChange(ListDiff<E>) line: 84	
		WritableList<E>.remove(int) line: 174	
	DataboundTableViewerDialog.remove(SelectionEvent) line: 148	
	Lambda.accept(Object) line: not available	
	12 collapsed frames	
	DataboundTableViewerDialog.main(String...) line: 45```

The `Accessible` class calls `SWTFocusCellManager` to get a name, unfortunately on the disposed table item which causes an SWT.error. That's why `org.eclipse.swt.Table` class cannot remove the item from the `items` attribute in its `remove(int[] indices)` method.

<img width="813" height="698" alt="Image" src="https://github.com/user-attachments/assets/e10165f1-0048-425a-ac2f-28b68895bb07" />

During removal of a table object we call `Display.asyncExec` and I have the impression that on my colleagues machine the `Accessible` class reacts too early. Additionally some accessibility settings on her Mac might have influence as on my machine `SWTFocusCellManager` is not called during deletion.

I have a PDE plugin project attached. Simply run `DataboundTableViewerDialog` as main application. The databinding is done similar as in our framework.
The removal is done by deleting all entries from the model and afterwards re-add the whole list except the removed. During this call, we do some `Display.asyncExec`.

If you execute the example and the viewer looks like in the screenshot above and you get an error in the console, you are "lucky" and can reproduce the issue.
If not, it works like on my machine.


I know this is really complicated and may be hard to reproduce. But I wonder if SWTFocusCellManager should be able to handle disposed table items if it is called too early. 
This is exactly the workaround in our framework. We have overwritten `SWTFocusCellManager.getName` and only call super of the table item is not disposed.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions