|
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 |
1 package org.chromium.sdk.internal.tools.v8; |
5 package org.chromium.sdk.internal.tools.v8; |
2 |
6 |
|
7 import java.util.Collections; |
3 import java.util.HashMap; |
8 import java.util.HashMap; |
|
9 import java.util.Iterator; |
|
10 import java.util.List; |
4 import java.util.Map; |
11 import java.util.Map; |
5 |
12 |
6 import org.chromium.sdk.Breakpoint; |
13 import org.chromium.sdk.Breakpoint; |
7 import org.chromium.sdk.BrowserTab; |
14 import org.chromium.sdk.JavascriptVm; |
|
15 import org.chromium.sdk.SyncCallback; |
8 import org.chromium.sdk.JavascriptVm.BreakpointCallback; |
16 import org.chromium.sdk.JavascriptVm.BreakpointCallback; |
|
17 import org.chromium.sdk.JavascriptVm.ListBreakpointsCallback; |
9 import org.chromium.sdk.internal.DebugSession; |
18 import org.chromium.sdk.internal.DebugSession; |
10 import org.chromium.sdk.internal.protocol.BreakpointBody; |
19 import org.chromium.sdk.internal.protocol.BreakpointBody; |
|
20 import org.chromium.sdk.internal.protocol.CommandResponseBody; |
|
21 import org.chromium.sdk.internal.protocol.ListBreakpointsBody; |
11 import org.chromium.sdk.internal.protocol.SuccessCommandResponse; |
22 import org.chromium.sdk.internal.protocol.SuccessCommandResponse; |
|
23 import org.chromium.sdk.internal.protocol.data.BreakpointInfo; |
12 import org.chromium.sdk.internal.protocolparser.JsonProtocolParseException; |
24 import org.chromium.sdk.internal.protocolparser.JsonProtocolParseException; |
13 import org.chromium.sdk.internal.tools.v8.request.DebuggerMessageFactory; |
25 import org.chromium.sdk.internal.tools.v8.request.DebuggerMessageFactory; |
|
26 import org.chromium.sdk.internal.tools.v8.request.ListBreakpointsMessage; |
14 |
27 |
15 public class BreakpointManager { |
28 public class BreakpointManager { |
16 /** |
29 /** |
17 * This map shall contain only breakpoints with valid IDs. |
30 * This map shall contain only breakpoints with valid IDs. |
18 */ |
31 */ |
19 private final Map<Long, Breakpoint> idToBreakpoint = new HashMap<Long, Breakpoint>(); |
32 private final Map<Long, BreakpointImpl> idToBreakpoint = new HashMap<Long, BreakpointImpl>(); |
20 |
33 |
21 private final DebugSession debugSession; |
34 private final DebugSession debugSession; |
22 |
35 |
23 public BreakpointManager(DebugSession debugSession) { |
36 public BreakpointManager(DebugSession debugSession) { |
24 this.debugSession = debugSession; |
37 this.debugSession = debugSession; |
25 } |
38 } |
26 |
39 |
27 public void setBreakpoint(final Breakpoint.Type type, String target, int line, int position, |
40 public void setBreakpoint(final Breakpoint.Type type, final String target, |
28 final boolean enabled, final String condition, final int ignoreCount, |
41 final int line, int position, final boolean enabled, final String condition, |
29 final BrowserTab.BreakpointCallback callback) { |
42 final int ignoreCount, final JavascriptVm.BreakpointCallback callback, |
|
43 SyncCallback syncCallback) { |
|
44 |
|
45 final String scriptName; |
|
46 final Long scriptId; |
|
47 Object targetObject; |
|
48 if (type == Breakpoint.Type.SCRIPT_ID) { |
|
49 scriptName = null; |
|
50 scriptId = Long.parseLong(target); |
|
51 targetObject = scriptId; |
|
52 } else if (type == Breakpoint.Type.SCRIPT_NAME) { |
|
53 scriptName = target; |
|
54 scriptId = null; |
|
55 targetObject = scriptName; |
|
56 } else { |
|
57 throw new IllegalArgumentException("Unsupported breakpoint type " + type); |
|
58 } |
|
59 |
30 debugSession.sendMessageAsync( |
60 debugSession.sendMessageAsync( |
31 DebuggerMessageFactory.setBreakpoint(type, target, toNullableInteger(line), |
61 DebuggerMessageFactory.setBreakpoint(type, targetObject, toNullableInteger(line), |
32 toNullableInteger(position), enabled, condition, |
62 toNullableInteger(position), enabled, condition, |
33 toNullableInteger(ignoreCount)), |
63 toNullableInteger(ignoreCount)), |
34 true, |
64 true, |
35 callback == null |
65 callback == null |
36 ? null |
66 ? null |
44 throw new RuntimeException(e); |
74 throw new RuntimeException(e); |
45 } |
75 } |
46 long id = body.getBreakpoint(); |
76 long id = body.getBreakpoint(); |
47 |
77 |
48 final BreakpointImpl breakpoint = |
78 final BreakpointImpl breakpoint = |
49 new BreakpointImpl(type, id, enabled, ignoreCount, |
79 new BreakpointImpl(type, id, scriptName, scriptId, line, enabled, ignoreCount, |
50 condition, BreakpointManager.this); |
80 condition, BreakpointManager.this); |
51 |
81 |
52 callback.success(breakpoint); |
82 callback.success(breakpoint); |
53 idToBreakpoint.put(breakpoint.getId(), breakpoint); |
83 idToBreakpoint.put(breakpoint.getId(), breakpoint); |
54 } |
84 } |
57 if (callback != null) { |
87 if (callback != null) { |
58 callback.failure(message); |
88 callback.failure(message); |
59 } |
89 } |
60 } |
90 } |
61 }, |
91 }, |
62 null); |
92 syncCallback); |
63 } |
93 } |
64 |
94 |
65 public Breakpoint getBreakpoint(Long id) { |
95 public Breakpoint getBreakpoint(Long id) { |
66 return idToBreakpoint.get(id); |
96 return idToBreakpoint.get(id); |
67 } |
97 } |
68 |
98 |
69 public void clearBreakpoint( |
99 public void clearBreakpoint( |
70 final BreakpointImpl breakpointImpl, final BreakpointCallback callback) { |
100 final BreakpointImpl breakpointImpl, final BreakpointCallback callback, |
|
101 SyncCallback syncCallback) { |
71 long id = breakpointImpl.getId(); |
102 long id = breakpointImpl.getId(); |
72 if (id == Breakpoint.INVALID_ID) { |
103 if (id == Breakpoint.INVALID_ID) { |
73 return; |
104 return; |
74 } |
105 } |
75 idToBreakpoint.remove(id); |
106 idToBreakpoint.remove(id); |
110 if (callback != null) { |
141 if (callback != null) { |
111 callback.failure(message); |
142 callback.failure(message); |
112 } |
143 } |
113 } |
144 } |
114 }, |
145 }, |
115 null); |
146 syncCallback); |
|
147 } |
|
148 |
|
149 /** |
|
150 * Reads a list of breakpoints from remote and updates local instances and the map. |
|
151 */ |
|
152 public void reloadBreakpoints(final ListBreakpointsCallback callback, SyncCallback syncCallback) { |
|
153 V8CommandCallbackBase v8Callback = new V8CommandCallbackBase() { |
|
154 @Override |
|
155 public void failure(String message) { |
|
156 callback.failure(new Exception(message)); |
|
157 } |
|
158 @Override |
|
159 public void success(SuccessCommandResponse successResponse) { |
|
160 CommandResponseBody body = successResponse.getBody(); |
|
161 ListBreakpointsBody listBreakpointsBody; |
|
162 try { |
|
163 listBreakpointsBody = body.asListBreakpointsBody(); |
|
164 } catch (JsonProtocolParseException e) { |
|
165 callback.failure(new Exception("Failed to read server response", e)); |
|
166 return; |
|
167 } |
|
168 List<BreakpointInfo> infos = listBreakpointsBody.breakpoints(); |
|
169 try { |
|
170 syncBreakpoints(infos); |
|
171 } catch (RuntimeException e) { |
|
172 callback.failure(new Exception("Failed to read server response", e)); |
|
173 return; |
|
174 } |
|
175 callback.success(Collections.unmodifiableCollection(idToBreakpoint.values())); |
|
176 } |
|
177 }; |
|
178 debugSession.sendMessageAsync(new ListBreakpointsMessage(), true, v8Callback, syncCallback); |
116 } |
179 } |
117 |
180 |
118 private static Integer toNullableInteger(int value) { |
181 private static Integer toNullableInteger(int value) { |
119 return value == Breakpoint.EMPTY_VALUE |
182 return value == Breakpoint.EMPTY_VALUE |
120 ? null |
183 ? null |
121 : value; |
184 : value; |
122 } |
185 } |
|
186 |
|
187 private void syncBreakpoints(List<BreakpointInfo> infoList) { |
|
188 Map<Long, BreakpointImpl> actualBreakpoints = new HashMap<Long, BreakpointImpl>(); |
|
189 // Wrap all loaded BreakpointInfo as BreakpointImpl, possibly reusing old instances. |
|
190 // Also check that all breakpoint id's in loaded list are unique. |
|
191 for (BreakpointInfo info : infoList) { |
|
192 if (info.type() == BreakpointInfo.Type.function) { |
|
193 // We does not support function type breakpoints and ignore them. |
|
194 continue; |
|
195 } |
|
196 BreakpointImpl breakpoint = idToBreakpoint.get(info.number()); |
|
197 if (breakpoint == null) { |
|
198 breakpoint = new BreakpointImpl(info, this); |
|
199 } else { |
|
200 breakpoint.updateFromRemote(info); |
|
201 } |
|
202 Object conflict = actualBreakpoints.put(info.number(), breakpoint); |
|
203 if (conflict != null) { |
|
204 throw new RuntimeException("Duplicated breakpoint number " + info.number()); |
|
205 } |
|
206 } |
|
207 |
|
208 // Remove all obsolete breakpoints from the map. |
|
209 for (Iterator<Long> it = idToBreakpoint.keySet().iterator(); it.hasNext(); ) { |
|
210 Long id = it.next(); |
|
211 if (!actualBreakpoints.containsKey(id)) { |
|
212 it.remove(); |
|
213 } |
|
214 } |
|
215 |
|
216 // Add breakpoints that are not in the main map yet. |
|
217 for (BreakpointImpl breakpoint : actualBreakpoints.values()) { |
|
218 if (!idToBreakpoint.containsKey(breakpoint.getId())) { |
|
219 idToBreakpoint.put(breakpoint.getId(), breakpoint); |
|
220 } |
|
221 } |
|
222 } |
123 } |
223 } |