diff options
Diffstat (limited to 'dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/viewmodel/breakpoints/GdbBreakpointVMProvider.java')
-rw-r--r-- | dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/viewmodel/breakpoints/GdbBreakpointVMProvider.java | 555 |
1 files changed, 281 insertions, 274 deletions
diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/viewmodel/breakpoints/GdbBreakpointVMProvider.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/viewmodel/breakpoints/GdbBreakpointVMProvider.java index 9dd12a1a9ba..0994819ade3 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/viewmodel/breakpoints/GdbBreakpointVMProvider.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/viewmodel/breakpoints/GdbBreakpointVMProvider.java @@ -7,7 +7,7 @@ * https://www.eclipse.org/legal/epl-2.0/ * * SPDX-License-Identifier: EPL-2.0 - * + * * Contributors: * Wind River Systems - initial API and implementation * Mikhail Khodjaiants (Mentor), Marc Khouzam (Ericsson) @@ -74,10 +74,10 @@ import org.eclipse.ui.PlatformUI; */ public class GdbBreakpointVMProvider extends BreakpointVMProvider { - final private DsfSession fSession; - - final private DsfServicesTracker fServicesTracker; - + final private DsfSession fSession; + + final private DsfServicesTracker fServicesTracker; + /** Indicator that we should use aggressive breakpoint filtering */ private boolean fUseAggressiveBpFilter = false; @@ -86,309 +86,316 @@ public class GdbBreakpointVMProvider extends BreakpointVMProvider { @Override public void propertyChange(final PropertyChangeEvent event) { if (IGdbDebugPreferenceConstants.PREF_AGGRESSIVE_BP_FILTER.equals(event.getProperty())) { - fUseAggressiveBpFilter = (Boolean)event.getNewValue(); + fUseAggressiveBpFilter = (Boolean) event.getNewValue(); // Set the property in the presentation context so it can be seen by the vmnode which // will refresh the view - getPresentationContext().setProperty(IGdbDebugPreferenceConstants.PREF_AGGRESSIVE_BP_FILTER, fUseAggressiveBpFilter); + getPresentationContext().setProperty(IGdbDebugPreferenceConstants.PREF_AGGRESSIVE_BP_FILTER, + fUseAggressiveBpFilter); } } }; - public GdbBreakpointVMProvider(AbstractVMAdapter adapter, IPresentationContext presentationContext, DsfSession session) { - super(adapter, presentationContext); - fSession = session; - fServicesTracker = new DsfServicesTracker(GdbUIPlugin.getBundleContext(), fSession.getId()); - - IPreferenceStore store = GdbUIPlugin.getDefault().getPreferenceStore(); - store.addPropertyChangeListener(fPropertyChangeListener); - fUseAggressiveBpFilter = store.getBoolean(IGdbDebugPreferenceConstants.PREF_AGGRESSIVE_BP_FILTER); + public GdbBreakpointVMProvider(AbstractVMAdapter adapter, IPresentationContext presentationContext, + DsfSession session) { + super(adapter, presentationContext); + fSession = session; + fServicesTracker = new DsfServicesTracker(GdbUIPlugin.getBundleContext(), fSession.getId()); + + IPreferenceStore store = GdbUIPlugin.getDefault().getPreferenceStore(); + store.addPropertyChangeListener(fPropertyChangeListener); + fUseAggressiveBpFilter = store.getBoolean(IGdbDebugPreferenceConstants.PREF_AGGRESSIVE_BP_FILTER); + } + + @Override + public void dispose() { + GdbUIPlugin.getDefault().getPreferenceStore().removePropertyChangeListener(fPropertyChangeListener); + fServicesTracker.dispose(); + super.dispose(); + } + + @Override + protected void calcFileteredBreakpoints(final DataRequestMonitor<IBreakpoint[]> rm) { + if (Boolean.TRUE.equals( + getPresentationContext().getProperty(IBreakpointUIConstants.PROP_BREAKPOINTS_FILTER_SELECTION))) { + if (fUseAggressiveBpFilter) { + // Aggressive filtering of breakpoints. Only return bps that are installed on the target. + ISelection debugContext = getDebugContext(); + if (debugContext instanceof IStructuredSelection) { + // Use a set to avoid duplicates + final Set<IBreakpoint> bps = new HashSet<IBreakpoint>(); + + int count = 0; + final ImmediateCountingRequestMonitor crm = new ImmediateCountingRequestMonitor(rm) { + @Override + protected void handleSuccess() { + rm.done(bps.toArray(new IBreakpoint[bps.size()])); + } + }; + + for (Object element : ((IStructuredSelection) debugContext).toList()) { + + IBreakpointsTargetDMContext bpContext = null; + IExecutionDMContext execContext = null; + if (element instanceof IDMVMContext) { + bpContext = DMContexts.getAncestorOfType(((IDMVMContext) element).getDMContext(), + IBreakpointsTargetDMContext.class); + execContext = DMContexts.getAncestorOfType(((IDMVMContext) element).getDMContext(), + IExecutionDMContext.class); + + if (bpContext != null && fSession.getId().equals(bpContext.getSessionId())) { + count++; + getInstalledBreakpoints(bpContext, execContext, + new DataRequestMonitor<Collection<IBreakpoint>>(getExecutor(), crm) { + @Override + protected void handleCompleted() { + if (isSuccess()) { + bps.addAll(getData()); + } + crm.done(); + } + }); + } + } + } + + crm.setDoneCount(count); + } else { + rm.done(new Status(IStatus.ERROR, GdbUIPlugin.PLUGIN_ID, IDsfStatusConstants.INVALID_HANDLE, + "Invalid debug selection", //$NON-NLS-1$ + null)); + } + } else { + // Original behavior of bp filtering. Return all bp of type ICBreakpoint + IBreakpoint[] allBreakpoints = DebugPlugin.getDefault().getBreakpointManager().getBreakpoints(); + List<IBreakpoint> filteredBPs = new ArrayList<IBreakpoint>(allBreakpoints.length); + for (IBreakpoint bp : allBreakpoints) { + if (bp instanceof ICBreakpoint && bp.getModelIdentifier().equals(CDebugCorePlugin.PLUGIN_ID)) { + filteredBPs.add(bp); + } + } + rm.done(filteredBPs.toArray(new IBreakpoint[filteredBPs.size()])); + } + } else { + super.calcFileteredBreakpoints(rm); + } } - - @Override - public void dispose() { - GdbUIPlugin.getDefault().getPreferenceStore().removePropertyChangeListener(fPropertyChangeListener); - fServicesTracker.dispose(); - super.dispose(); - } - - @Override - protected void calcFileteredBreakpoints( final DataRequestMonitor<IBreakpoint[]> rm ) { - if ( Boolean.TRUE.equals( getPresentationContext().getProperty( IBreakpointUIConstants.PROP_BREAKPOINTS_FILTER_SELECTION ) ) ) { - if ( fUseAggressiveBpFilter ) { - // Aggressive filtering of breakpoints. Only return bps that are installed on the target. - ISelection debugContext = getDebugContext(); - if ( debugContext instanceof IStructuredSelection ) { - // Use a set to avoid duplicates - final Set<IBreakpoint> bps = new HashSet<IBreakpoint>(); - - int count = 0; - final ImmediateCountingRequestMonitor crm = new ImmediateCountingRequestMonitor( rm ) { - @Override - protected void handleSuccess() { - rm.done( bps.toArray( new IBreakpoint[bps.size()] ) ); - } - }; - - for ( Object element : ( (IStructuredSelection)debugContext ).toList() ) { - - IBreakpointsTargetDMContext bpContext = null; - IExecutionDMContext execContext = null; - if ( element instanceof IDMVMContext ) { - bpContext = DMContexts.getAncestorOfType( ((IDMVMContext)element).getDMContext(), IBreakpointsTargetDMContext.class ); - execContext = DMContexts.getAncestorOfType( ((IDMVMContext)element).getDMContext(), IExecutionDMContext.class ); - - if ( bpContext != null && fSession.getId().equals( bpContext.getSessionId() ) ) { - count++; - getInstalledBreakpoints( bpContext, execContext, new DataRequestMonitor<Collection<IBreakpoint>>( getExecutor(), crm ) { - @Override - protected void handleCompleted() { - if ( isSuccess() ) { - bps.addAll( getData() ); - } - crm.done(); - } - } ); - } - } - } - - crm.setDoneCount(count); - } else { - rm.done( new Status( - IStatus.ERROR, - GdbUIPlugin.PLUGIN_ID, - IDsfStatusConstants.INVALID_HANDLE, - "Invalid debug selection", //$NON-NLS-1$ - null ) ); - } - } else { - // Original behavior of bp filtering. Return all bp of type ICBreakpoint - IBreakpoint[] allBreakpoints = DebugPlugin.getDefault().getBreakpointManager().getBreakpoints(); - List<IBreakpoint> filteredBPs = new ArrayList<IBreakpoint>( allBreakpoints.length ); - for (IBreakpoint bp : allBreakpoints) { - if ( bp instanceof ICBreakpoint && bp.getModelIdentifier().equals( CDebugCorePlugin.PLUGIN_ID ) ) { - filteredBPs.add( bp ); - } - } - rm.done( filteredBPs.toArray( new IBreakpoint[filteredBPs.size()]) ); - } + + @Override + protected IVMNode createBreakpointVMNode() { + return new GdbBreakpointVMNode(this); + } + + @Override + public void getBreakpointsForDebugContext(ISelection debugContext, final DataRequestMonitor<IBreakpoint[]> rm) { + IExecutionDMContext _execCtx = null; + if (debugContext instanceof IStructuredSelection) { + Object element = ((IStructuredSelection) debugContext).getFirstElement(); + if (element instanceof IDMVMContext) { + _execCtx = DMContexts.getAncestorOfType(((IDMVMContext) element).getDMContext(), + IExecutionDMContext.class); + } + } + + if (_execCtx == null || !fSession.getId().equals(_execCtx.getSessionId())) { + rm.setStatus(new Status(IStatus.ERROR, GdbUIPlugin.PLUGIN_ID, IDsfStatusConstants.INVALID_HANDLE, + "Debug context doesn't contain a thread", null)); //$NON-NLS-1$ + rm.done(); + return; + } + + final IExecutionDMContext execCtx = _execCtx; + + try { + fSession.getExecutor().execute(new DsfRunnable() { + @Override + public void run() { + IBreakpointsExtension bpService = fServicesTracker.getService(IBreakpointsExtension.class); + if (bpService == null) { + rm.setStatus(new Status(IStatus.ERROR, GdbUIPlugin.PLUGIN_ID, IDsfStatusConstants.INVALID_STATE, + "Breakpoints service not available", null)); //$NON-NLS-1$ + rm.done(); + return; + } + bpService.getExecutionContextBreakpoints(execCtx, + new DataRequestMonitor<IBreakpoints.IBreakpointDMContext[]>(fSession.getExecutor(), rm) { + @Override + protected void handleSuccess() { + MIBreakpointsManager bpManager = fServicesTracker + .getService(MIBreakpointsManager.class); + if (bpManager == null) { + rm.setStatus(new Status(IStatus.ERROR, GdbUIPlugin.PLUGIN_ID, + IDsfStatusConstants.INVALID_STATE, "Breakpoints service not available", //$NON-NLS-1$ + null)); + rm.done(); + return; + } + + IBreakpoint bp = null; + + if (getData().length > 0) { + bp = bpManager.findPlatformBreakpoint(getData()[0]); + } + + if (bp != null) { + rm.setData(new IBreakpoint[] { bp }); + } else { + rm.setData(new IBreakpoint[0]); + } + rm.done(); + } + }); + } + }); + } catch (RejectedExecutionException e) { + rm.setStatus(new Status(IStatus.ERROR, GdbUIPlugin.PLUGIN_ID, IDsfStatusConstants.INVALID_STATE, + "Request for monitor: '" + toString() + "' resulted in a rejected execution exception.", e)); //$NON-NLS-1$ //$NON-NLS-2$); + rm.done(); } - else { - super.calcFileteredBreakpoints( rm ); + } + + private ISelection getDebugContext() { + IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); + if (window != null) { + return DebugUITools.getDebugContextManager().getContextService(window).getActiveContext(); } - } - - @Override - protected IVMNode createBreakpointVMNode() { - return new GdbBreakpointVMNode(this); - } - - @Override - public void getBreakpointsForDebugContext(ISelection debugContext, final DataRequestMonitor<IBreakpoint[]> rm) { - IExecutionDMContext _execCtx = null; - if (debugContext instanceof IStructuredSelection) { - Object element = ((IStructuredSelection)debugContext).getFirstElement(); - if (element instanceof IDMVMContext) { - _execCtx = DMContexts.getAncestorOfType( ((IDMVMContext)element).getDMContext(), IExecutionDMContext.class); - } - } - - if (_execCtx == null || !fSession.getId().equals(_execCtx.getSessionId())) { - rm.setStatus(new Status(IStatus.ERROR, GdbUIPlugin.PLUGIN_ID, IDsfStatusConstants.INVALID_HANDLE, "Debug context doesn't contain a thread", null)); //$NON-NLS-1$ - rm.done(); - return; - } - - final IExecutionDMContext execCtx = _execCtx; - - try { - fSession.getExecutor().execute(new DsfRunnable() { - @Override - public void run() { - IBreakpointsExtension bpService = fServicesTracker.getService(IBreakpointsExtension.class); - if (bpService == null) { - rm.setStatus(new Status(IStatus.ERROR, GdbUIPlugin.PLUGIN_ID, IDsfStatusConstants.INVALID_STATE, "Breakpoints service not available", null)); //$NON-NLS-1$ - rm.done(); - return; - } - bpService.getExecutionContextBreakpoints( - execCtx, - new DataRequestMonitor<IBreakpoints.IBreakpointDMContext[]>(fSession.getExecutor(), rm) { - @Override - protected void handleSuccess() { - MIBreakpointsManager bpManager = fServicesTracker.getService(MIBreakpointsManager.class); - if (bpManager == null) { - rm.setStatus(new Status(IStatus.ERROR, GdbUIPlugin.PLUGIN_ID, IDsfStatusConstants.INVALID_STATE, "Breakpoints service not available", null)); //$NON-NLS-1$ - rm.done(); - return; - } - - IBreakpoint bp = null; - - - if (getData().length > 0) { - bp = bpManager.findPlatformBreakpoint(getData()[0]); - } - - if (bp != null) { - rm.setData(new IBreakpoint[] { bp }); - } else { - rm.setData(new IBreakpoint[0]); - } - rm.done(); - } - }); - } - }); - } catch (RejectedExecutionException e) { - rm.setStatus(new Status(IStatus.ERROR, GdbUIPlugin.PLUGIN_ID, IDsfStatusConstants.INVALID_STATE, "Request for monitor: '" + toString() + "' resulted in a rejected execution exception.", e)); //$NON-NLS-1$ //$NON-NLS-2$); - rm.done(); - } - } - - private ISelection getDebugContext() { - IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); - if ( window != null ) { - return DebugUITools.getDebugContextManager().getContextService( window ).getActiveContext(); - } - return StructuredSelection.EMPTY; - } - - private void getInstalledBreakpoints( final IBreakpointsTargetDMContext targetContext, - final IExecutionDMContext execContext, - final DataRequestMonitor<Collection<IBreakpoint>> rm ) { - + return StructuredSelection.EMPTY; + } + + private void getInstalledBreakpoints(final IBreakpointsTargetDMContext targetContext, + final IExecutionDMContext execContext, final DataRequestMonitor<Collection<IBreakpoint>> rm) { + try { - fSession.getExecutor().execute( new DsfRunnable() { + fSession.getExecutor().execute(new DsfRunnable() { @Override public void run() { - final IBreakpointsExtension bpService = fServicesTracker.getService( IBreakpointsExtension.class ); - if ( bpService == null ) { - rm.setStatus( new Status( - IStatus.ERROR, - GdbUIPlugin.PLUGIN_ID, IDsfStatusConstants.INVALID_STATE, + final IBreakpointsExtension bpService = fServicesTracker.getService(IBreakpointsExtension.class); + if (bpService == null) { + rm.setStatus(new Status(IStatus.ERROR, GdbUIPlugin.PLUGIN_ID, IDsfStatusConstants.INVALID_STATE, "Breakpoints service not available", //$NON-NLS-1$ - null ) ); + null)); rm.done(); return; } - bpService.getBreakpoints( targetContext, new DataRequestMonitor<IBreakpointDMContext[]>( fSession.getExecutor(), rm ) { - - @Override - protected void handleSuccess() { - final MIBreakpointsManager bpManager = fServicesTracker.getService( MIBreakpointsManager.class ); - if ( bpManager == null ) { - rm.setStatus( new Status( - IStatus.ERROR, - GdbUIPlugin.PLUGIN_ID, IDsfStatusConstants.INVALID_STATE, - "Breakpoint manager service not available", //$NON-NLS-1$ - null ) ); - rm.done(); - return; - } - - if ( getData().length > 0 ) { - final Set<IBreakpoint> bps = new HashSet<IBreakpoint>( getData().length ); - final CountingRequestMonitor crm = new CountingRequestMonitor( ImmediateExecutor.getInstance(), rm ) { - - @Override - protected void handleSuccess() { - rm.setData( bps ); + bpService.getBreakpoints(targetContext, + new DataRequestMonitor<IBreakpointDMContext[]>(fSession.getExecutor(), rm) { + + @Override + protected void handleSuccess() { + final MIBreakpointsManager bpManager = fServicesTracker + .getService(MIBreakpointsManager.class); + if (bpManager == null) { + rm.setStatus(new Status(IStatus.ERROR, GdbUIPlugin.PLUGIN_ID, + IDsfStatusConstants.INVALID_STATE, + "Breakpoint manager service not available", //$NON-NLS-1$ + null)); rm.done(); + return; } - }; - crm.setDoneCount( getData().length ); - - for ( final IBreakpointDMContext bpCtx : getData() ) { - bpService.getBreakpointDMData( - bpCtx, - new DataRequestMonitor<IBreakpointDMData>( ImmediateExecutor.getInstance(), crm ) { - - /* (non-Javadoc) - * @see org.eclipse.cdt.dsf.concurrent.RequestMonitor#handleSuccess() - */ + + if (getData().length > 0) { + final Set<IBreakpoint> bps = new HashSet<IBreakpoint>(getData().length); + final CountingRequestMonitor crm = new CountingRequestMonitor( + ImmediateExecutor.getInstance(), rm) { + @Override protected void handleSuccess() { - if ( getData() instanceof MIBreakpointDMData ) { - MIBreakpointDMData data = (MIBreakpointDMData)getData(); - if ( !data.isPending() ) { - bpBelongsToContext( execContext, data, new ImmediateDataRequestMonitor<Boolean>(crm) { - @Override - protected void handleSuccess() { - if (getData()) { - IBreakpoint bp = bpManager.findPlatformBreakpoint( bpCtx ); - if ( bp != null ) - bps.add( bp ); + rm.setData(bps); + rm.done(); + } + }; + crm.setDoneCount(getData().length); + + for (final IBreakpointDMContext bpCtx : getData()) { + bpService.getBreakpointDMData(bpCtx, + new DataRequestMonitor<IBreakpointDMData>( + ImmediateExecutor.getInstance(), crm) { + + /* (non-Javadoc) + * @see org.eclipse.cdt.dsf.concurrent.RequestMonitor#handleSuccess() + */ + @Override + protected void handleSuccess() { + if (getData() instanceof MIBreakpointDMData) { + MIBreakpointDMData data = (MIBreakpointDMData) getData(); + if (!data.isPending()) { + bpBelongsToContext(execContext, data, + new ImmediateDataRequestMonitor<Boolean>( + crm) { + @Override + protected void handleSuccess() { + if (getData()) { + IBreakpoint bp = bpManager + .findPlatformBreakpoint( + bpCtx); + if (bp != null) + bps.add(bp); + } + crm.done(); + } + }); + return; } - crm.done(); } - }); - return; - } - } - crm.done(); - } - } ); + crm.done(); + } + }); + } + } else { + rm.setData(new HashSet<IBreakpoint>()); + rm.done(); + } } - } - else { - rm.setData( new HashSet<IBreakpoint>() ); - rm.done(); - } - } - } ); + }); } - } ); - } - catch( RejectedExecutionException e ) { - rm.setStatus( new Status( - IStatus.ERROR, - GdbUIPlugin.PLUGIN_ID, - IDsfStatusConstants.INVALID_STATE, - "Request for monitor: '" + toString() + "' resulted in a rejected execution exception.", e ) ); //$NON-NLS-1$ //$NON-NLS-2$); + }); + } catch (RejectedExecutionException e) { + rm.setStatus(new Status(IStatus.ERROR, GdbUIPlugin.PLUGIN_ID, IDsfStatusConstants.INVALID_STATE, + "Request for monitor: '" + toString() + "' resulted in a rejected execution exception.", e)); //$NON-NLS-1$ //$NON-NLS-2$); rm.done(); } - } - - private void bpBelongsToContext( IExecutionDMContext execContext, final MIBreakpointDMData data, final DataRequestMonitor<Boolean> rm ) { - if ( execContext == null ) { - // No execution context so accept all breakpoints - rm.done( true ); - return; - } - - // First check if a thread is selected as the context. In that case, we only want to show breakpoints that - // are applicable to that thread. - IMIExecutionDMContext threadContext = DMContexts.getAncestorOfType( execContext, IMIExecutionDMContext.class ); - if ( threadContext != null ) { + } + + private void bpBelongsToContext(IExecutionDMContext execContext, final MIBreakpointDMData data, + final DataRequestMonitor<Boolean> rm) { + if (execContext == null) { + // No execution context so accept all breakpoints + rm.done(true); + return; + } + + // First check if a thread is selected as the context. In that case, we only want to show breakpoints that + // are applicable to that thread. + IMIExecutionDMContext threadContext = DMContexts.getAncestorOfType(execContext, IMIExecutionDMContext.class); + if (threadContext != null) { // A thread is selected. Now make sure this breakpoint is assigned to this thread. - if (data.getThreadId() != null && data.getThreadId().length() > 0) { - String bpThreadId = data.getThreadId().trim(); - // A threadId of 0 means all threads of this process. We therefore will have to check - // if this breakpoint applies to the process that is selected. But if the threadId is not 0 - // we simply make sure we have the right thread selected. - if (!bpThreadId.equals("0")) { //$NON-NLS-1$ - String ctxThreadId = threadContext.getThreadId(); - rm.done(ctxThreadId.equals(bpThreadId)); - return; - } - } - } + if (data.getThreadId() != null && data.getThreadId().length() > 0) { + String bpThreadId = data.getThreadId().trim(); + // A threadId of 0 means all threads of this process. We therefore will have to check + // if this breakpoint applies to the process that is selected. But if the threadId is not 0 + // we simply make sure we have the right thread selected. + if (!bpThreadId.equals("0")) { //$NON-NLS-1$ + String ctxThreadId = threadContext.getThreadId(); + rm.done(ctxThreadId.equals(bpThreadId)); + return; + } + } + } // If we get here it is that the breakpoint is not assigned to a single thread. // We therefore make sure the breakpoint is applicable to the selected process. - final IMIContainerDMContext containerContext = DMContexts.getAncestorOfType( execContext, IMIContainerDMContext.class ); - if ( containerContext != null ) { - if ( data.getGroupIds() != null ) { - List<String> groupIds = Arrays.asList( data.getGroupIds() ); - rm.done( groupIds.contains( containerContext.getGroupId() ) ); + final IMIContainerDMContext containerContext = DMContexts.getAncestorOfType(execContext, + IMIContainerDMContext.class); + if (containerContext != null) { + if (data.getGroupIds() != null) { + List<String> groupIds = Arrays.asList(data.getGroupIds()); + rm.done(groupIds.contains(containerContext.getGroupId())); } else { // This happens if we are debugging a single process, so all breakpoints apply. - rm.done( true ); + rm.done(true); } } else { // Accept breakpoint - rm.done( true ); + rm.done(true); } - } + } } |