// Copyright (c) 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. package org.chromium.sdk.internal.wip; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import org.chromium.sdk.Breakpoint; import org.chromium.sdk.Breakpoint.Target; import org.chromium.sdk.JavascriptVm.BreakpointCallback; import org.chromium.sdk.RelayOk; import org.chromium.sdk.SyncCallback; import org.chromium.sdk.TextStreamPosition; import org.chromium.sdk.internal.wip.protocol.input.debugger.BreakpointResolvedEventData; import org.chromium.sdk.util.RelaySyncCallback; /** * A manager that works as factory for breakpoints. */ public class WipBreakpointManager { private final WipTabImpl tabImpl; private final AtomicInteger breakpointUniqueId = new AtomicInteger(0); private final Db db = new Db(); WipBreakpointManager(WipTabImpl tabImpl) { this.tabImpl = tabImpl; } RelayOk setBreakpoint(Breakpoint.Target target, final int line, final int column, final boolean enabled, String condition, final BreakpointCallback callback, SyncCallback syncCallback) { int sdkId = breakpointUniqueId.getAndAdd(1); final WipBreakpointImpl breakpointImpl = new WipBreakpointImpl(this, sdkId, target, line, column, condition, enabled); db.addBreakpoint(breakpointImpl); if (enabled) { if (condition == null) { condition = ""; } WipBreakpointImpl.SetBreakpointCallback wrappedCallback = new WipBreakpointImpl.SetBreakpointCallback() { @Override public void onSuccess(String protocolId, Collection<WipBreakpointImpl.ActualLocation> actualLocations) { breakpointImpl.setRemoteData(protocolId, actualLocations); if (callback != null) { callback.success(breakpointImpl); } } @Override public void onFailure(Exception exception) { if (callback != null) { callback.failure(exception.getMessage()); } } }; return WipBreakpointImpl.sendSetBreakpointRequest(target, line, column, condition, wrappedCallback, syncCallback, tabImpl.getCommandProcessor()); } else { callback.success(breakpointImpl); return RelaySyncCallback.finish(syncCallback); } } Db getDb() { return db; } void clearNonProvisionalBreakpoints() { db.visitAllBreakpoints(new Db.Visitor<Void>() { @Override public Void visitAllBreakpoints(Set<WipBreakpointImpl> breakpoints) { List<WipBreakpointImpl> deleteList = new ArrayList<WipBreakpointImpl>(); for (WipBreakpointImpl breakpoint : breakpoints) { if (breakpoint.getTarget().accept(IS_SCRIPT_ID_VISITOR)) { deleteList.add(breakpoint); } else { breakpoint.clearActualLocations(); } } for (WipBreakpointImpl breakpoint : deleteList) { breakpoint.deleteSelfFromDb(); } return null; } }); } private static final Breakpoint.Target.Visitor<Boolean> IS_SCRIPT_ID_VISITOR = new Breakpoint.Target.Visitor<Boolean>() { @Override public Boolean visitScriptName(String scriptName) { return Boolean.FALSE; } @Override public Boolean visitScriptId(Object scriptId) { return Boolean.TRUE; } @Override public Boolean visitUnknown(Target target) { return Boolean.FALSE; } }; WipCommandProcessor getCommandProcessor() { return tabImpl.getCommandProcessor(); } Collection<WipBreakpointImpl> getAllBreakpoints() { return db.visitAllBreakpoints(new Db.Visitor<Collection<WipBreakpointImpl>>() { @Override public Collection<WipBreakpointImpl> visitAllBreakpoints( Set<WipBreakpointImpl> breakpoints) { return new ArrayList<WipBreakpointImpl>(breakpoints); } }); } void breakpointReportedResolved(BreakpointResolvedEventData eventData) { String breakpointId = eventData.breakpointId(); WipBreakpointImpl breakpoint = db.getBreakpoint(breakpointId); if (breakpoint == null) { throw new RuntimeException("Failed to find breakpoint by id: " + breakpointId); } breakpoint.addResolvedLocation(eventData.location()); } // Accessed from Dispatch thread. public Collection<? extends Breakpoint> findRelatedBreakpoints( WipContextBuilder.WipDebugContextImpl.CallFrameImpl topFrame) { TextStreamPosition position = topFrame.getStatementStartPosition(); int line = position.getLine(); int column = position.getColumn(); String scriptId = topFrame.getSourceId(); final WipBreakpointImpl.ActualLocation location = new WipBreakpointImpl.ActualLocation(scriptId, line, Long.valueOf(column)); return db.visitAllBreakpoints(new Db.Visitor<List<Breakpoint>>() { @Override public List<Breakpoint> visitAllBreakpoints(Set<WipBreakpointImpl> breakpoints) { List<Breakpoint> result = new ArrayList<Breakpoint>(1); for (WipBreakpointImpl breakpoint : breakpoints) { if (breakpoint.getActualLocations().contains(location)) { result.add(breakpoint); } } return result; } }); } /** * Breakpoint data-base. Keeps track of all instances and their protocol-id -> instance mapping. * The name implies that it doesn't manage anything, only stores data. */ static class Db { // Accessed from any thread. private final Set<WipBreakpointImpl> breakpoints = new HashSet<WipBreakpointImpl>(); // Access from Dispatch thread only. private final Map<String, WipBreakpointImpl> idToBreakpoint = new HashMap<String, WipBreakpointImpl>(); void addBreakpoint(WipBreakpointImpl breakpoint) { synchronized (breakpoints) { breakpoints.add(breakpoint); } } void removeBreakpoint(WipBreakpointImpl breakpoint) { synchronized (breakpoints) { breakpoints.remove(breakpoint); } } void setIdMapping(WipBreakpointImpl breakpoint, String protocolId) { if (protocolId == null) { idToBreakpoint.remove(protocolId); } else { idToBreakpoint.put(protocolId, breakpoint); } } WipBreakpointImpl getBreakpoint(String breakpointId) { return idToBreakpoint.get(breakpointId); } /** * Gives a synchronized access to all breakpoints. */ <R> R visitAllBreakpoints(Visitor<R> visitor) { synchronized (breakpoints) { return visitor.visitAllBreakpoints(breakpoints); } } interface Visitor<R> { R visitAllBreakpoints(Set<WipBreakpointImpl> breakpoints); } } }