org.chromium.debug.core/src/org/chromium/debug/core/model/DebugTargetImpl.java
changeset 2 e4420d2515f1
child 52 f577ea64429e
equal deleted inserted replaced
1:ef76fc2ac88c 2:e4420d2515f1
       
     1 // Copyright (c) 2009 The Chromium Authors. All rights reserved.
       
     2 // Use of this source code is governed by a BSD-style license that can be
       
     3 // found in the LICENSE file.
       
     4 
       
     5 package org.chromium.debug.core.model;
       
     6 
       
     7 import java.util.Collection;
       
     8 
       
     9 import org.chromium.debug.core.ChromiumDebugPlugin;
       
    10 import org.chromium.debug.core.util.ChromiumDebugPluginUtil;
       
    11 import org.chromium.sdk.Breakpoint;
       
    12 import org.chromium.sdk.CallFrame;
       
    13 import org.chromium.sdk.DebugContext;
       
    14 import org.chromium.sdk.DebugEventListener;
       
    15 import org.chromium.sdk.ExceptionData;
       
    16 import org.chromium.sdk.JavascriptVm;
       
    17 import org.chromium.sdk.Script;
       
    18 import org.chromium.sdk.DebugContext.State;
       
    19 import org.chromium.sdk.DebugContext.StepAction;
       
    20 import org.chromium.sdk.JavascriptVm.BreakpointCallback;
       
    21 import org.chromium.sdk.JavascriptVm.ScriptsCallback;
       
    22 import org.eclipse.core.resources.IFile;
       
    23 import org.eclipse.core.resources.IMarkerDelta;
       
    24 import org.eclipse.core.resources.IProject;
       
    25 import org.eclipse.core.runtime.CoreException;
       
    26 import org.eclipse.core.runtime.IProgressMonitor;
       
    27 import org.eclipse.core.runtime.IStatus;
       
    28 import org.eclipse.core.runtime.Status;
       
    29 import org.eclipse.core.runtime.jobs.Job;
       
    30 import org.eclipse.debug.core.DebugEvent;
       
    31 import org.eclipse.debug.core.DebugException;
       
    32 import org.eclipse.debug.core.DebugPlugin;
       
    33 import org.eclipse.debug.core.IBreakpointManager;
       
    34 import org.eclipse.debug.core.ILaunch;
       
    35 import org.eclipse.debug.core.ILaunchListener;
       
    36 import org.eclipse.debug.core.model.IBreakpoint;
       
    37 import org.eclipse.debug.core.model.IMemoryBlock;
       
    38 import org.eclipse.debug.core.model.IProcess;
       
    39 import org.eclipse.debug.core.model.ISourceLocator;
       
    40 import org.eclipse.debug.core.model.IStackFrame;
       
    41 import org.eclipse.debug.core.model.IThread;
       
    42 
       
    43 /**
       
    44  * An IDebugTarget implementation for remote JavaScript debugging.
       
    45  * Can debug any target that supports the ChromeDevTools protocol.
       
    46  */
       
    47 public class DebugTargetImpl extends DebugElementImpl implements IChromiumDebugTarget {
       
    48 
       
    49   private static final IThread[] EMPTY_THREADS = new IThread[0];
       
    50 
       
    51   private static final long OPERATION_TIMEOUT_MS = 15000L;
       
    52 
       
    53   private final ILaunch launch;
       
    54 
       
    55   private final JavascriptThread[] threads;
       
    56 
       
    57   private JavascriptVmEmbedder vmEmbedder = STUB_VM_EMBEDDER;
       
    58 
       
    59   private ResourceManager resourceManager;
       
    60 
       
    61   private BreakpointRegistry breakpointRegistry;
       
    62 
       
    63   private IProject debugProject = null;
       
    64 
       
    65   private DebugContext debugContext;
       
    66 
       
    67   private boolean isSuspended = false;
       
    68 
       
    69   private boolean isDisconnected = false;
       
    70 
       
    71 
       
    72   public DebugTargetImpl(ILaunch launch) {
       
    73     super(null);
       
    74     this.launch = launch;
       
    75     this.threads = new JavascriptThread[] { new JavascriptThread(this) };
       
    76   }
       
    77 
       
    78 
       
    79   /**
       
    80    * Loads browser tabs, consults the {@code selector} which of the tabs to
       
    81    * attach to, and if any has been selected, requests an attachment to the tab.
       
    82    *
       
    83    * @param projectNameBase to create for the browser scripts
       
    84    * @param remoteServer embedding application we are connected with
       
    85    * @param attachCallback to invoke on successful attachment
       
    86    * @param monitor to report the progress to
       
    87    * @return whether the target has attached to a tab
       
    88    * @throws CoreException
       
    89    */
       
    90   public boolean attach(String projectNameBase,
       
    91       JavascriptVmEmbedder.ConnectionToRemote remoteServer, DestructingGuard destructingGuard,
       
    92       Runnable attachCallback, IProgressMonitor monitor) throws CoreException {
       
    93     monitor.beginTask("", 2); //$NON-NLS-1$
       
    94     JavascriptVmEmbedder.VmConnector connector = remoteServer.selectVm();
       
    95     if (connector == null) {
       
    96       return false;
       
    97     }
       
    98     monitor.worked(1);
       
    99     return performAttach(projectNameBase, connector, destructingGuard, attachCallback);
       
   100   }
       
   101 
       
   102   private boolean performAttach(String projectNameBase, JavascriptVmEmbedder.VmConnector connector,
       
   103       DestructingGuard destructingGuard, Runnable attachCallback) throws CoreException {
       
   104     final JavascriptVmEmbedder embedder = connector.attach(embedderListener, debugEventListener);
       
   105 
       
   106     Destructable embedderDestructor = new Destructable() {
       
   107       public void destruct() {
       
   108         embedder.getJavascriptVm().detach();
       
   109       }
       
   110     };
       
   111 
       
   112     destructingGuard.addValue(embedderDestructor);
       
   113 
       
   114     vmEmbedder = embedder;
       
   115 
       
   116     // We might want to add some url-specific suffix here
       
   117     String projectName = projectNameBase;
       
   118     // We'd like to know when launch is removed to remove our project.
       
   119     DebugPlugin.getDefault().getLaunchManager().addLaunchListener(launchListener);
       
   120     this.debugProject = ChromiumDebugPluginUtil.createEmptyProject(projectName);
       
   121     this.breakpointRegistry = new BreakpointRegistry();
       
   122     this.resourceManager = new ResourceManager(debugProject, breakpointRegistry);
       
   123     onAttach(projectName, attachCallback);
       
   124     return true;
       
   125   }
       
   126 
       
   127   private void onAttach(String projectName, Runnable attachCallback) {
       
   128     DebugPlugin.getDefault().getBreakpointManager().addBreakpointListener(this);
       
   129     reloadScriptsAndPossiblyResume(attachCallback);
       
   130   }
       
   131 
       
   132   private void reloadScriptsAndPossiblyResume(final Runnable attachCallback) {
       
   133     reloadScripts(true, new Runnable() {
       
   134       public void run() {
       
   135         try {
       
   136           if (attachCallback != null) {
       
   137             attachCallback.run();
       
   138           }
       
   139         } finally {
       
   140           fireCreationEvent();
       
   141         }
       
   142         Job job = new Job("Update debugger state") {
       
   143           @Override
       
   144           protected IStatus run(IProgressMonitor monitor) {
       
   145             debugEventListener.resumedByDefault();
       
   146             return Status.OK_STATUS;
       
   147           }
       
   148         };
       
   149         job.schedule();
       
   150       }
       
   151     });
       
   152   }
       
   153 
       
   154   private void reloadScripts(boolean isSync, final Runnable runnable) {
       
   155     Runnable command = new Runnable() {
       
   156       public void run() {
       
   157         vmEmbedder.getJavascriptVm().getScripts(new ScriptsCallback() {
       
   158           public void failure(String errorMessage) {
       
   159             ChromiumDebugPlugin.logError(errorMessage);
       
   160           }
       
   161 
       
   162           public void success(Collection<Script> scripts) {
       
   163             if (!vmEmbedder.getJavascriptVm().isAttached()) {
       
   164               return;
       
   165             }
       
   166             for (Script script : scripts) {
       
   167               getResourceManager().addScript(script);
       
   168             }
       
   169             if (runnable != null) {
       
   170               runnable.run();
       
   171             }
       
   172           }
       
   173 
       
   174         });
       
   175       }
       
   176     };
       
   177     if (isSync) {
       
   178       command.run();
       
   179       return;
       
   180     }
       
   181     Thread t = new Thread(command);
       
   182     t.setDaemon(true);
       
   183     t.start();
       
   184     try {
       
   185       t.join(OPERATION_TIMEOUT_MS);
       
   186     } catch (InterruptedException e) {
       
   187       ChromiumDebugPlugin.log(e);
       
   188     }
       
   189   }
       
   190 
       
   191   public String getName() throws DebugException {
       
   192     if (vmEmbedder == null) {
       
   193       return ""; //$NON-NLS-1$
       
   194     }
       
   195     return vmEmbedder.getTargetName();
       
   196   }
       
   197 
       
   198   public IProcess getProcess() {
       
   199     return null;
       
   200   }
       
   201 
       
   202   public JavascriptVmEmbedder getJavascriptEmbedder() {
       
   203     return vmEmbedder;
       
   204   }
       
   205 
       
   206   public IThread[] getThreads() throws DebugException {
       
   207     return isDisconnected()
       
   208         ? EMPTY_THREADS
       
   209         : threads;
       
   210   }
       
   211 
       
   212   public boolean hasThreads() throws DebugException {
       
   213     return getThreads().length > 0;
       
   214   }
       
   215 
       
   216   public boolean supportsBreakpoint(IBreakpoint breakpoint) {
       
   217     return ChromiumDebugPlugin.DEBUG_MODEL_ID.equals(breakpoint.getModelIdentifier()) &&
       
   218         !isDisconnected();
       
   219   }
       
   220 
       
   221   @Override
       
   222   public DebugTargetImpl getDebugTarget() {
       
   223     return this;
       
   224   }
       
   225 
       
   226   @Override
       
   227   public ILaunch getLaunch() {
       
   228     return launch;
       
   229   }
       
   230 
       
   231   @Override
       
   232   public String getModelIdentifier() {
       
   233     return ChromiumDebugPlugin.DEBUG_MODEL_ID;
       
   234   }
       
   235 
       
   236   public boolean canTerminate() {
       
   237     return !isTerminated();
       
   238   }
       
   239 
       
   240   public boolean isTerminated() {
       
   241     return isDisconnected();
       
   242   }
       
   243 
       
   244   public void terminate() throws DebugException {
       
   245     disconnect();
       
   246   }
       
   247 
       
   248   public boolean canResume() {
       
   249     return !isDisconnected() && isSuspended();
       
   250   }
       
   251 
       
   252   public synchronized boolean isSuspended() {
       
   253     return isSuspended;
       
   254   }
       
   255 
       
   256   private synchronized void setSuspended(boolean isSuspended) {
       
   257     this.isSuspended = isSuspended;
       
   258   }
       
   259 
       
   260   public void suspended(int detail) {
       
   261     setSuspended(true);
       
   262     getThread().reset();
       
   263     fireSuspendEvent(detail);
       
   264   }
       
   265 
       
   266   public void resume() throws DebugException {
       
   267     debugContext.continueVm(StepAction.CONTINUE, 1, null);
       
   268     // Let's pretend Chromium does respond to the "continue" request immediately
       
   269     resumed(DebugEvent.CLIENT_REQUEST);
       
   270   }
       
   271 
       
   272   public void resumed(int detail) {
       
   273     fireResumeEvent(detail);
       
   274   }
       
   275 
       
   276   public boolean canSuspend() {
       
   277     return !isDisconnected() && !isSuspended();
       
   278   }
       
   279 
       
   280   public void suspend() throws DebugException {
       
   281     vmEmbedder.getJavascriptVm().suspend(null);
       
   282   }
       
   283 
       
   284   public boolean canDisconnect() {
       
   285     return !isDisconnected();
       
   286   }
       
   287 
       
   288   public void disconnect() throws DebugException {
       
   289     if (!canDisconnect()) {
       
   290       return;
       
   291     }
       
   292     removeAllBreakpoints();
       
   293     if (!vmEmbedder.getJavascriptVm().detach()) {
       
   294       ChromiumDebugPlugin.logWarning(Messages.DebugTargetImpl_BadResultWhileDisconnecting);
       
   295     }
       
   296     // This is a duplicated call to disconnected().
       
   297     // The primary one comes from V8DebuggerToolHandler#onDebuggerDetached
       
   298     // but we want to make sure the target becomes disconnected even if
       
   299     // there is a browser failure and it does not respond.
       
   300     debugEventListener.disconnected();
       
   301   }
       
   302 
       
   303   public synchronized boolean isDisconnected() {
       
   304     return isDisconnected;
       
   305   }
       
   306 
       
   307   public IMemoryBlock getMemoryBlock(long startAddress, long length) throws DebugException {
       
   308     return null;
       
   309   }
       
   310 
       
   311   public boolean supportsStorageRetrieval() {
       
   312     return false;
       
   313   }
       
   314 
       
   315   public IProject getDebugProject() {
       
   316     return debugProject;
       
   317   }
       
   318 
       
   319   /**
       
   320    * Fires a debug event
       
   321    *
       
   322    * @param event to be fired
       
   323    */
       
   324   public void fireEvent(DebugEvent event) {
       
   325     DebugPlugin debugPlugin = DebugPlugin.getDefault();
       
   326     if (debugPlugin != null) {
       
   327       debugPlugin.fireDebugEventSet(new DebugEvent[] { event });
       
   328     }
       
   329   }
       
   330 
       
   331   public void fireEventForThread(int kind, int detail) {
       
   332     try {
       
   333       IThread[] threads = getThreads();
       
   334       if (threads.length > 0) {
       
   335         fireEvent(new DebugEvent(threads[0], kind, detail));
       
   336       }
       
   337     } catch (DebugException e) {
       
   338       // Actually, this is not thrown in our getThreads()
       
   339       return;
       
   340     }
       
   341   }
       
   342 
       
   343   public void fireCreationEvent() {
       
   344     setDisconnected(false);
       
   345     fireEventForThread(DebugEvent.CREATE, DebugEvent.UNSPECIFIED);
       
   346   }
       
   347 
       
   348   public synchronized void setDisconnected(boolean disconnected) {
       
   349     isDisconnected = disconnected;
       
   350   }
       
   351 
       
   352   public void fireResumeEvent(int detail) {
       
   353     setSuspended(false);
       
   354     fireEventForThread(DebugEvent.RESUME, detail);
       
   355     fireEvent(new DebugEvent(this, DebugEvent.RESUME, detail));
       
   356   }
       
   357 
       
   358   public void fireSuspendEvent(int detail) {
       
   359     setSuspended(true);
       
   360     fireEventForThread(DebugEvent.SUSPEND, detail);
       
   361     fireEvent(new DebugEvent(this, DebugEvent.SUSPEND, detail));
       
   362   }
       
   363 
       
   364   public void fireTerminateEvent() {
       
   365     // TODO(peter.rybin): from Alexander Pavlov: I think you need to fire a terminate event after
       
   366     // this line, for consolePseudoProcess if one is not null.
       
   367     fireEventForThread(DebugEvent.TERMINATE, DebugEvent.UNSPECIFIED);
       
   368     fireEvent(new DebugEvent(this, DebugEvent.TERMINATE, DebugEvent.UNSPECIFIED));
       
   369     fireEvent(new DebugEvent(getLaunch(), DebugEvent.TERMINATE, DebugEvent.UNSPECIFIED));
       
   370   }
       
   371 
       
   372   public void breakpointAdded(IBreakpoint breakpoint) {
       
   373     if (!supportsBreakpoint(breakpoint)) {
       
   374       return;
       
   375     }
       
   376     try {
       
   377       if (breakpoint.isEnabled()) {
       
   378         // Class cast is ensured by the supportsBreakpoint implementation
       
   379         final ChromiumLineBreakpoint lineBreakpoint = (ChromiumLineBreakpoint) breakpoint;
       
   380         IFile file = (IFile) breakpoint.getMarker().getResource();
       
   381         if (getResourceManager().isAddingFile(file)) {
       
   382           return; // restoring breakpoints in progress
       
   383         }
       
   384         final Script script = getResourceManager().getScript(file);
       
   385         if (script == null) {
       
   386           // Might be a script from a different debug target
       
   387           return;
       
   388         }
       
   389         final int line = (lineBreakpoint.getLineNumber() - 1) + script.getStartLine();
       
   390         BreakpointCallback callback = new BreakpointCallback() {
       
   391           public void success(Breakpoint breakpoint) {
       
   392             lineBreakpoint.setBreakpoint(breakpoint);
       
   393             breakpointRegistry.add(script, line, breakpoint);
       
   394           }
       
   395 
       
   396           public void failure(String errorMessage) {
       
   397             ChromiumDebugPlugin.logError(errorMessage);
       
   398           }
       
   399         };
       
   400         // ILineBreakpoint lines are 1-based while V8 lines are 0-based
       
   401         JavascriptVm javascriptVm = vmEmbedder.getJavascriptVm();
       
   402         if (script.getName() != null) {
       
   403           javascriptVm.setBreakpoint(Breakpoint.Type.SCRIPT_NAME,
       
   404               script.getName(),
       
   405               line,
       
   406               Breakpoint.EMPTY_VALUE,
       
   407               breakpoint.isEnabled(),
       
   408               lineBreakpoint.getCondition(),
       
   409               lineBreakpoint.getIgnoreCount(),
       
   410               callback);
       
   411         } else {
       
   412           javascriptVm.setBreakpoint(Breakpoint.Type.SCRIPT_ID,
       
   413               String.valueOf(script.getId()),
       
   414               line,
       
   415               Breakpoint.EMPTY_VALUE,
       
   416               breakpoint.isEnabled(),
       
   417               lineBreakpoint.getCondition(),
       
   418               lineBreakpoint.getIgnoreCount(),
       
   419               callback);
       
   420         }
       
   421       }
       
   422     } catch (CoreException e) {
       
   423       ChromiumDebugPlugin.log(e);
       
   424     }
       
   425   }
       
   426 
       
   427   public void breakpointChanged(IBreakpoint breakpoint, IMarkerDelta delta) {
       
   428     if (!supportsBreakpoint(breakpoint)) {
       
   429       return;
       
   430     }
       
   431     // Class cast is ensured by the supportsBreakpoint implementation
       
   432     ((ChromiumLineBreakpoint) breakpoint).changed();
       
   433   }
       
   434 
       
   435   public void breakpointRemoved(IBreakpoint breakpoint, IMarkerDelta delta) {
       
   436     if (!supportsBreakpoint(breakpoint)) {
       
   437       return;
       
   438     }
       
   439     try {
       
   440       if (breakpoint.isEnabled()) {
       
   441         // Class cast is ensured by the supportsBreakpoint implementation
       
   442         ChromiumLineBreakpoint lineBreakpoint = (ChromiumLineBreakpoint) breakpoint;
       
   443         lineBreakpoint.clear();
       
   444         breakpointRegistry.remove(
       
   445             getResourceManager().getScript((IFile) breakpoint.getMarker().getResource()),
       
   446             lineBreakpoint.getLineNumber() - 1,
       
   447             lineBreakpoint.getBrowserBreakpoint());
       
   448       }
       
   449     } catch (CoreException e) {
       
   450       ChromiumDebugPlugin.log(e);
       
   451     }
       
   452   }
       
   453 
       
   454   @SuppressWarnings("unchecked")
       
   455   @Override
       
   456   public Object getAdapter(Class adapter) {
       
   457     if (ILaunch.class.equals(adapter)) {
       
   458       return this.launch;
       
   459     }
       
   460     return super.getAdapter(adapter);
       
   461   }
       
   462 
       
   463   public IResourceManager getResourceManager() {
       
   464     return resourceManager;
       
   465   }
       
   466 
       
   467   public JavascriptThread getThread() {
       
   468     return isDisconnected()
       
   469         ? null
       
   470         : threads[0];
       
   471   }
       
   472 
       
   473   private static void breakpointsHit(Collection<? extends Breakpoint> breakpointsHit) {
       
   474     if (breakpointsHit.isEmpty()) {
       
   475       return;
       
   476     }
       
   477     IBreakpoint[] breakpoints =
       
   478         DebugPlugin.getDefault().getBreakpointManager().getBreakpoints(
       
   479             ChromiumDebugPlugin.DEBUG_MODEL_ID);
       
   480     for (IBreakpoint breakpoint : breakpoints) {
       
   481       ChromiumLineBreakpoint jsBreakpoint = (ChromiumLineBreakpoint) breakpoint;
       
   482       if (breakpointsHit.contains(jsBreakpoint.getBrowserBreakpoint())) {
       
   483         jsBreakpoint.setIgnoreCount(-1); // reset ignore count as we've hit it
       
   484       }
       
   485     }
       
   486   }
       
   487 
       
   488   private static String trim(String text, int maxLength) {
       
   489     if (text == null || text.length() <= maxLength) {
       
   490       return text;
       
   491     }
       
   492     return text.substring(0, maxLength - 3) + "..."; //$NON-NLS-1$
       
   493   }
       
   494 
       
   495   public DebugContext getDebugContext() {
       
   496     return debugContext;
       
   497   }
       
   498 
       
   499   public ISourceLocator getSourceLocator() {
       
   500     return sourceLocator;
       
   501   }
       
   502 
       
   503   private void removeAllBreakpoints() {
       
   504     IBreakpointManager breakpointManager = DebugPlugin.getDefault().getBreakpointManager();
       
   505     IBreakpoint[] breakpoints =
       
   506         breakpointManager.getBreakpoints(ChromiumDebugPlugin.DEBUG_MODEL_ID);
       
   507     for (IBreakpoint bp : breakpoints) {
       
   508       ChromiumLineBreakpoint clb = (ChromiumLineBreakpoint) bp;
       
   509       if (clb.getBrowserBreakpoint() != null &&
       
   510           clb.getBrowserBreakpoint().getId() != Breakpoint.INVALID_ID) {
       
   511         clb.getBrowserBreakpoint().clear(null);
       
   512       }
       
   513     }
       
   514     try {
       
   515       breakpointManager.removeBreakpoints(breakpoints, true);
       
   516     } catch (CoreException e) {
       
   517       ChromiumDebugPlugin.log(e);
       
   518     }
       
   519   }
       
   520 
       
   521   private final DebugEventListenerImpl debugEventListener = new DebugEventListenerImpl();
       
   522 
       
   523   class DebugEventListenerImpl implements DebugEventListener {
       
   524     // Synchronizes calls from ReaderThread of Connection and one call from some worker thread
       
   525     private final Object suspendResumeMonitor = new Object();
       
   526     private boolean alreadyResumedOrSuspended = false;
       
   527 
       
   528     public void disconnected() {
       
   529       if (!isDisconnected()) {
       
   530         setDisconnected(true);
       
   531         DebugPlugin.getDefault().getBreakpointManager().removeBreakpointListener(
       
   532             DebugTargetImpl.this);
       
   533         fireTerminateEvent();
       
   534       }
       
   535     }
       
   536 
       
   537     public void resumedByDefault() {
       
   538       synchronized (suspendResumeMonitor) {
       
   539         if (!alreadyResumedOrSuspended) {
       
   540           resumed();
       
   541         }
       
   542       }
       
   543     }
       
   544 
       
   545     public void resumed() {
       
   546       synchronized (suspendResumeMonitor) {
       
   547         DebugTargetImpl.this.resumed(DebugEvent.CLIENT_REQUEST);
       
   548         alreadyResumedOrSuspended = true;
       
   549       }
       
   550     }
       
   551 
       
   552     public void scriptLoaded(Script newScript) {
       
   553       getResourceManager().addScript(newScript);
       
   554     }
       
   555 
       
   556     public void suspended(DebugContext context) {
       
   557       synchronized (suspendResumeMonitor) {
       
   558         DebugTargetImpl.this.debugContext = context;
       
   559         breakpointsHit(context.getBreakpointsHit());
       
   560         int suspendedDetail;
       
   561         if (context.getState() == State.EXCEPTION) {
       
   562           logExceptionFromContext(context);
       
   563           suspendedDetail = DebugEvent.BREAKPOINT;
       
   564         } else {
       
   565           if (context.getBreakpointsHit().isEmpty()) {
       
   566             suspendedDetail = DebugEvent.STEP_END;
       
   567           } else {
       
   568             suspendedDetail = DebugEvent.BREAKPOINT;
       
   569           }
       
   570         }
       
   571         DebugTargetImpl.this.suspended(suspendedDetail);
       
   572 
       
   573         alreadyResumedOrSuspended = true;
       
   574       }
       
   575     }
       
   576   }
       
   577 
       
   578   private void logExceptionFromContext(DebugContext context) {
       
   579     ExceptionData exceptionData = context.getExceptionData();
       
   580     CallFrame topFrame = context.getCallFrames().get(0);
       
   581     Script script = topFrame.getScript();
       
   582     ChromiumDebugPlugin.logError(
       
   583         Messages.DebugTargetImpl_LogExceptionFormat,
       
   584         exceptionData.isUncaught()
       
   585             ? Messages.DebugTargetImpl_Uncaught
       
   586             : Messages.DebugTargetImpl_Caught,
       
   587         exceptionData.getExceptionMessage(),
       
   588         script != null ? script.getName() : "<unknown>", //$NON-NLS-1$
       
   589         topFrame.getLineNumber(),
       
   590         trim(exceptionData.getSourceText(), 80));
       
   591   }
       
   592 
       
   593   private final JavascriptVmEmbedder.Listener embedderListener =
       
   594       new JavascriptVmEmbedder.Listener() {
       
   595     public void reset() {
       
   596       getResourceManager().clear();
       
   597       fireEvent(new DebugEvent(this, DebugEvent.CHANGE, DebugEvent.STATE));
       
   598     }
       
   599     public void closed() {
       
   600       debugEventListener.disconnected();
       
   601     }
       
   602   };
       
   603 
       
   604   private final ILaunchListener launchListener = new ILaunchListener() {
       
   605     public void launchAdded(ILaunch launch) {
       
   606     }
       
   607     public void launchChanged(ILaunch launch) {
       
   608     }
       
   609     // TODO(peter.rybin): maybe have one instance of listener for all targets?
       
   610     public void launchRemoved(ILaunch launch) {
       
   611       if (launch != DebugTargetImpl.this.launch) {
       
   612         return;
       
   613       }
       
   614       DebugPlugin.getDefault().getLaunchManager().removeLaunchListener(this);
       
   615       if (debugProject != null) {
       
   616         ChromiumDebugPluginUtil.deleteVirtualProjectAsync(debugProject);
       
   617       }
       
   618     }
       
   619   };
       
   620 
       
   621   private final static JavascriptVmEmbedder STUB_VM_EMBEDDER = new JavascriptVmEmbedder() {
       
   622     public JavascriptVm getJavascriptVm() {
       
   623       //TODO(peter.rybin): decide and redo this exception
       
   624       throw new UnsupportedOperationException();
       
   625     }
       
   626 
       
   627     public String getTargetName() {
       
   628       //TODO(peter.rybin): decide and redo this exception
       
   629       throw new UnsupportedOperationException();
       
   630     }
       
   631 
       
   632     public String getThreadName() {
       
   633       //TODO(peter.rybin): decide and redo this exception
       
   634       throw new UnsupportedOperationException();
       
   635     }
       
   636   };
       
   637 
       
   638   /**
       
   639    * This very simple source locator works because we provide our own source files.
       
   640    * We'll have to try harder, once we link with resource js files.
       
   641    */
       
   642   private final ISourceLocator sourceLocator = new ISourceLocator() {
       
   643     public Object getSourceElement(IStackFrame stackFrame) {
       
   644       if (stackFrame instanceof StackFrame == false) {
       
   645         return null;
       
   646       }
       
   647       StackFrame jsStackFrame = (StackFrame) stackFrame;
       
   648 
       
   649       Script script = jsStackFrame.getCallFrame().getScript();
       
   650       if (script == null) {
       
   651         return null;
       
   652       }
       
   653 
       
   654       return resourceManager.getResource(script);
       
   655     }
       
   656   };
       
   657 }