Project: Eclipse_Platform_UI
Bug 283898 [DnD] EditorSiteDragAndDropServiceImpl should remove MergedDropTarget as listener when control gets disposed
Reported: 2009-07-17 17:08:44 | Status: verified fixed

Build ID: I20090611-1540 Steps To Reproduce: 1. Create a new Plug-in Project with the template "Plug-in with a multi-page editor" 2. Edit the file MultiPageEditor.java: - In method createPage1, replace the fontButton's SelectionAdapter's implementation of method widgetSelected: from: setFont(); to: removePage(0); createPage0(); - (optionally, change the fontButton's text to "Reload") - In method createPage0, force the page to index 0 by changing: from: int index = addPage(editor, getEditorInput()); setPageText(index, editor.getTitle()); to: addPage(0, editor, getEditorInput()); setPageText(0, editor.getTitle()); 3. Launch the project with Run As -> Eclipse Application 4. In the new workbench, create a new General Project 5. In the context menu for the new project, select New -> Other... -> Sample Wizards -> Multi-page Editor file 6. In the "new_file.mpe" tab, edit the text to create a huge document (ex: ~5MB) and Save it 7. In the "Properties" tab, click the Reload button (or Change Font... if it wasn't renamed) many times 8. The replaced editor in page 0 stays in memory and leaks heap (ex: ~10MB) every time the button is pressed More information: When the AbstractTextEditor method createPartControl is called, an instance of MergedDropTarget is added to the DragAndDropService (EditorSiteDragAndDropServiceImpl) tied to the site. The MergedDropTarget holds a reference to a DropTargetListener which is an inner class (DropTargetAdapter) of the AbstractTextEditor. When the AbstractTextEditor is disposed, it does not remove its MergedDropTarget from the DragAndDropService. Normally the DragAndDropService is disposed at the same time as the editor part is closed. But in the case of an AbstractTextEditor added to a MultiPageEditorSite of a MultiPageEditorPart, the AbstractTextEditor can be disposed through a call to removePage on the MultiPageEditorPart, while the DragAndDropService (EditorSiteDragAndDropServiceImpl) remains in memory since there is a single common instance of it tied to the parent MultiPageEditorPart. As a result, since the EditorSiteDragAndDropServiceImpl keeps in its addedListeners table the MergedDropTarget which contains a reference to the AbstractTextEditor's DropTargetAdapter, the removed AbstractTextEditor can not be garbage collected and remains in memory until the whole MultiPageEditorPart is disposed. The proposed solution is to call the method uninstallTextDragAndDrop from within AbstractTextEditor's dispose method. Note 1 (MultiPageEditorPart patch): To remove the MergedDropTarget from the DragAndDropService, the current implementation of AbstractTextEditor's uninstallTextDragAndDrop method needs a reference to the SourceViewer's TextWidget (of class StyledText). In the removePage method of MultiPageEditorPart, the pageControl is disposed before the editor. Consequently, when the AbstractTextEditor's dispose method is called, the SourceViewer's TextWidget has already been disposed and it can no longer be used to remove the MergedDropTarget. The proposed solution is to change the order in the removePage method so that the editor is disposed before the pageControl, which ensures that the TextWidget (StyledText) is still available at that moment. Note 2 (EditorSiteDragAndDropServiceImpl patch): When the MergedDropTarget is created, it creates an internal instance of the class DropTarget and stores it the "realDropTarget" member variable. This instance is also stored in the control's data (the instance of class StyledText) with the DROP_TARGET_KEY. The MergedDropTarget is then added to the listeners of the EditorSiteDragAndDropServiceImpl instance. But when the method removeMergedDropTarget is called with the StyledText control as a parameter, the "realDropTarget" instance of DropTarget is retrieved from the control with the DROP_TARGET_KEY. This DropTarget instance is then attempted to be removed from the listeners of EditorSiteDragAndDropServiceImpl, but since it is the owner MergedDropTarget instance which is stored in the listener table, this remove fails. Also, only the DropTarget instance is disposed. The proposed solution is to loop through all the MergedDropTarget instances in the listener table, and remove and dispose each of them for which the "realDropTarget" is equal to the StyledText control's DropTarget. The DropTarget is then also disposed.

Files changed in fix:
bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/EditorSiteDragAndDropServiceImpl.java