| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2011 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.sdk.internal.wip; | |
| 6 | |
| 7 import java.util.ArrayList; | |
| 8 import java.util.Collection; | |
| 9 import java.util.Collections; | |
| 10 import java.util.HashSet; | |
| 11 import java.util.List; | |
| 12 import java.util.Set; | |
| 13 | |
| 14 import org.chromium.sdk.Breakpoint; | |
| 15 import org.chromium.sdk.BreakpointTypeExtension; | |
| 16 import org.chromium.sdk.IgnoreCountBreakpointExtension; | |
| 17 import org.chromium.sdk.JavascriptVm.BreakpointCallback; | |
| 18 import org.chromium.sdk.RelayOk; | |
| 19 import org.chromium.sdk.SyncCallback; | |
| 20 import org.chromium.sdk.internal.ScriptRegExpBreakpointTarget; | |
| 21 import org.chromium.sdk.internal.wip.protocol.input.WipCommandResponse.Success; | |
| 22 import org.chromium.sdk.internal.wip.protocol.input.debugger.LocationValue; | |
| 23 import org.chromium.sdk.internal.wip.protocol.input.debugger.SetBreakpointByUrlD
ata; | |
| 24 import org.chromium.sdk.internal.wip.protocol.input.debugger.SetBreakpointData; | |
| 25 import org.chromium.sdk.internal.wip.protocol.output.WipParamsWithResponse; | |
| 26 import org.chromium.sdk.internal.wip.protocol.output.debugger.LocationParam; | |
| 27 import org.chromium.sdk.internal.wip.protocol.output.debugger.RemoveBreakpointPa
rams; | |
| 28 import org.chromium.sdk.internal.wip.protocol.output.debugger.SetBreakpointByUrl
Params; | |
| 29 import org.chromium.sdk.internal.wip.protocol.output.debugger.SetBreakpointParam
s; | |
| 30 import org.chromium.sdk.util.GenericCallback; | |
| 31 import org.chromium.sdk.util.RelaySyncCallback; | |
| 32 | |
| 33 /** | |
| 34 * Wip-based breakpoint implementation. | |
| 35 * The implementation is based on volatile fields and expects client code to do
some | |
| 36 * synchronization (serialize calls to setters, {@link #flush} and {@link #clear
}). | |
| 37 */ | |
| 38 public class WipBreakpointImpl implements Breakpoint { | |
| 39 private final WipBreakpointManager breakpointManager; | |
| 40 | |
| 41 private final Target target; | |
| 42 | |
| 43 private final int lineNumber; | |
| 44 private final int columnNumber; | |
| 45 | |
| 46 private final int sdkId; | |
| 47 private volatile String protocolId = null; | |
| 48 | |
| 49 private volatile String condition; | |
| 50 private volatile boolean enabled; | |
| 51 private volatile boolean isDirty; | |
| 52 | |
| 53 // Access only from Dispatch thread. | |
| 54 private Set<ActualLocation> actualLocations = new HashSet<ActualLocation>(2); | |
| 55 | |
| 56 public WipBreakpointImpl(WipBreakpointManager breakpointManager, int sdkId, Ta
rget target, | |
| 57 int lineNumber, int columnNumber, String condition, boolean enabled) { | |
| 58 this.breakpointManager = breakpointManager; | |
| 59 this.sdkId = sdkId; | |
| 60 this.target = target; | |
| 61 this.lineNumber = lineNumber; | |
| 62 this.columnNumber = columnNumber; | |
| 63 this.condition = condition; | |
| 64 this.enabled = enabled; | |
| 65 | |
| 66 this.isDirty = false; | |
| 67 } | |
| 68 | |
| 69 @Override | |
| 70 public Target getTarget() { | |
| 71 return target; | |
| 72 } | |
| 73 | |
| 74 @Override | |
| 75 public long getId() { | |
| 76 return sdkId; | |
| 77 } | |
| 78 | |
| 79 public static final BreakpointTypeExtension TYPE_EXTENSION = new BreakpointTyp
eExtension() { | |
| 80 @Override | |
| 81 public FunctionSupport getFunctionSupport() { | |
| 82 return null; | |
| 83 } | |
| 84 @Override | |
| 85 public ScriptRegExpSupport getScriptRegExpSupport() { | |
| 86 return scriptRegExpSupport; | |
| 87 } | |
| 88 | |
| 89 private final ScriptRegExpSupport scriptRegExpSupport = new ScriptRegExpSupp
ort() { | |
| 90 @Override | |
| 91 public Target createTarget(String regExp) { | |
| 92 return new ScriptRegExpBreakpointTarget(regExp); | |
| 93 } | |
| 94 }; | |
| 95 }; | |
| 96 | |
| 97 @Override | |
| 98 public long getLineNumber() { | |
| 99 return lineNumber; | |
| 100 } | |
| 101 | |
| 102 @Override | |
| 103 public boolean isEnabled() { | |
| 104 return enabled; | |
| 105 } | |
| 106 | |
| 107 @Override | |
| 108 public void setEnabled(boolean enabled) { | |
| 109 if (enabled == this.enabled) { | |
| 110 return; | |
| 111 } | |
| 112 this.enabled = enabled; | |
| 113 isDirty = true; | |
| 114 } | |
| 115 | |
| 116 @Override | |
| 117 public String getCondition() { | |
| 118 return condition; | |
| 119 } | |
| 120 | |
| 121 @Override | |
| 122 public void setCondition(String condition) { | |
| 123 if (eq(this.condition, condition)) { | |
| 124 return; | |
| 125 } | |
| 126 this.condition = condition; | |
| 127 isDirty = true; | |
| 128 } | |
| 129 | |
| 130 @Override | |
| 131 public IgnoreCountBreakpointExtension getIgnoreCountBreakpointExtension() { | |
| 132 return getIgnoreCountBreakpointExtensionImpl(); | |
| 133 } | |
| 134 | |
| 135 public static IgnoreCountBreakpointExtension getIgnoreCountBreakpointExtension
Impl() { | |
| 136 // TODO(peter.rybin): implement when protocol supports. | |
| 137 return null; | |
| 138 } | |
| 139 | |
| 140 | |
| 141 void setRemoteData(String protocolId, Collection<ActualLocation> actualLocatio
ns) { | |
| 142 this.protocolId = protocolId; | |
| 143 this.actualLocations.clear(); | |
| 144 this.actualLocations.addAll(actualLocations); | |
| 145 this.breakpointManager.getDb().setIdMapping(this, protocolId); | |
| 146 } | |
| 147 | |
| 148 void addResolvedLocation(LocationValue locationValue) { | |
| 149 ActualLocation location = locationFromProtocol(locationValue); | |
| 150 actualLocations.add(location); | |
| 151 } | |
| 152 | |
| 153 Set<ActualLocation> getActualLocations() { | |
| 154 return actualLocations; | |
| 155 } | |
| 156 | |
| 157 void clearActualLocations() { | |
| 158 actualLocations.clear(); | |
| 159 } | |
| 160 | |
| 161 void deleteSelfFromDb() { | |
| 162 if (protocolId != null) { | |
| 163 breakpointManager.getDb().setIdMapping(this, null); | |
| 164 } | |
| 165 breakpointManager.getDb().removeBreakpoint(this); | |
| 166 } | |
| 167 | |
| 168 @Override | |
| 169 public RelayOk clear(final BreakpointCallback callback, SyncCallback syncCallb
ack) { | |
| 170 // TODO: make sure this is thread-safe. | |
| 171 if (protocolId == null) { | |
| 172 breakpointManager.getDb().removeBreakpoint(this); | |
| 173 callback.success(this); | |
| 174 return RelaySyncCallback.finish(syncCallback); | |
| 175 } | |
| 176 | |
| 177 RemoveBreakpointParams params = new RemoveBreakpointParams(protocolId); | |
| 178 | |
| 179 WipCommandCallback commandCallback; | |
| 180 if (callback == null) { | |
| 181 commandCallback = null; | |
| 182 } else { | |
| 183 commandCallback = new WipCommandCallback.Default() { | |
| 184 @Override protected void onSuccess(Success success) { | |
| 185 breakpointManager.getDb().setIdMapping(WipBreakpointImpl.this, null); | |
| 186 breakpointManager.getDb().removeBreakpoint(WipBreakpointImpl.this); | |
| 187 callback.success(WipBreakpointImpl.this); | |
| 188 } | |
| 189 @Override protected void onError(String message) { | |
| 190 callback.failure(message); | |
| 191 } | |
| 192 }; | |
| 193 } | |
| 194 | |
| 195 return breakpointManager.getCommandProcessor().send(params, commandCallback,
syncCallback); | |
| 196 } | |
| 197 | |
| 198 @Override | |
| 199 public RelayOk flush(final BreakpointCallback callback, final SyncCallback syn
cCallback) { | |
| 200 final RelaySyncCallback relay = new RelaySyncCallback(syncCallback); | |
| 201 | |
| 202 if (!isDirty) { | |
| 203 if (callback != null) { | |
| 204 callback.success(this); | |
| 205 } | |
| 206 return RelaySyncCallback.finish(syncCallback); | |
| 207 } | |
| 208 | |
| 209 isDirty = false; | |
| 210 | |
| 211 if (protocolId == null) { | |
| 212 // Breakpoint was disabled, it doesn't exist in VM, immediately start step
2. | |
| 213 return recreateBreakpointAsync(callback, relay); | |
| 214 } else { | |
| 215 // Call syncCallback if something goes wrong after we sent request. | |
| 216 final RelaySyncCallback.Guard guard = relay.newGuard(); | |
| 217 | |
| 218 WipCommandCallback removeCallback = new WipCommandCallback.Default() { | |
| 219 @Override | |
| 220 protected void onSuccess(Success success) { | |
| 221 setRemoteData(null, Collections.<ActualLocation>emptyList()); | |
| 222 RelayOk relayOk = recreateBreakpointAsync(callback, relay); | |
| 223 guard.discharge(relayOk); | |
| 224 } | |
| 225 | |
| 226 @Override | |
| 227 protected void onError(String message) { | |
| 228 throw new RuntimeException("Failed to remove breakpoint: " + message); | |
| 229 } | |
| 230 }; | |
| 231 | |
| 232 // Call syncCallback if something goes wrong. | |
| 233 return breakpointManager.getCommandProcessor().send(new RemoveBreakpointPa
rams(protocolId), | |
| 234 removeCallback, guard.asSyncCallback()); | |
| 235 } | |
| 236 } | |
| 237 | |
| 238 static class ActualLocation { | |
| 239 private final String sourceId; | |
| 240 private final long lineNumber; | |
| 241 private final Long columnNumber; | |
| 242 | |
| 243 ActualLocation(String sourceId, long lineNumber, Long columnNumber) { | |
| 244 this.sourceId = sourceId; | |
| 245 this.lineNumber = lineNumber; | |
| 246 this.columnNumber = columnNumber; | |
| 247 } | |
| 248 | |
| 249 @Override | |
| 250 public boolean equals(Object obj) { | |
| 251 ActualLocation other = (ActualLocation) obj; | |
| 252 return this.sourceId.equals(other.sourceId) && | |
| 253 this.lineNumber == other.lineNumber && | |
| 254 eq(this.columnNumber, other.columnNumber); | |
| 255 } | |
| 256 | |
| 257 @Override | |
| 258 public int hashCode() { | |
| 259 int column; | |
| 260 if (columnNumber == null) { | |
| 261 column = 0; | |
| 262 } else { | |
| 263 column = columnNumber.intValue(); | |
| 264 } | |
| 265 return sourceId.hashCode() + 31 * (int) lineNumber + column; | |
| 266 } | |
| 267 | |
| 268 @Override | |
| 269 public String toString() { | |
| 270 return "<sourceId=" + sourceId + ", line=" + lineNumber + ", column=" + co
lumnNumber + ">"; | |
| 271 } | |
| 272 } | |
| 273 | |
| 274 private RelayOk recreateBreakpointAsync(final BreakpointCallback flushCallback
, | |
| 275 RelaySyncCallback relay) { | |
| 276 | |
| 277 if (enabled) { | |
| 278 SetBreakpointCallback setCommandCallback = new SetBreakpointCallback() { | |
| 279 @Override | |
| 280 public void onSuccess(String protocolId, Collection<ActualLocation> actu
alLocations) { | |
| 281 setRemoteData(protocolId, actualLocations); | |
| 282 if (flushCallback != null) { | |
| 283 flushCallback.success(WipBreakpointImpl.this); | |
| 284 } | |
| 285 } | |
| 286 | |
| 287 @Override | |
| 288 public void onFailure(Exception exception) { | |
| 289 if (flushCallback != null) { | |
| 290 flushCallback.failure(exception.getMessage()); | |
| 291 } | |
| 292 } | |
| 293 }; | |
| 294 | |
| 295 RelaySyncCallback.Guard guard = relay.newGuard(); | |
| 296 | |
| 297 if (condition == null) { | |
| 298 condition = ""; | |
| 299 } | |
| 300 return sendSetBreakpointRequest(target, lineNumber, columnNumber, conditio
n, | |
| 301 setCommandCallback, guard.asSyncCallback(), | |
| 302 breakpointManager.getCommandProcessor()); | |
| 303 } else { | |
| 304 // Breakpoint is disabled, do not create it. | |
| 305 RelayOk relayOk; | |
| 306 try { | |
| 307 if (flushCallback != null) { | |
| 308 flushCallback.success(WipBreakpointImpl.this); | |
| 309 } | |
| 310 } finally { | |
| 311 relayOk = relay.finish(); | |
| 312 } | |
| 313 return relayOk; | |
| 314 } | |
| 315 } | |
| 316 | |
| 317 interface SetBreakpointCallback { | |
| 318 void onSuccess(String breakpointId, Collection<ActualLocation> actualLocatio
ns); | |
| 319 void onFailure(Exception cause); | |
| 320 } | |
| 321 | |
| 322 /** | |
| 323 * @param callback a generic callback that receives breakpoint protocol id | |
| 324 * @return | |
| 325 */ | |
| 326 static RelayOk sendSetBreakpointRequest(Target target, final int lineNumber, | |
| 327 int columnNumber, final String condition, | |
| 328 final SetBreakpointCallback callback, final SyncCallback syncCallback, | |
| 329 final WipCommandProcessor commandProcessor) { | |
| 330 | |
| 331 final Long columnNumberParam; | |
| 332 if (columnNumber == Breakpoint.EMPTY_VALUE) { | |
| 333 columnNumberParam = null; | |
| 334 } else { | |
| 335 columnNumberParam = Long.valueOf(columnNumber); | |
| 336 } | |
| 337 | |
| 338 return target.accept(new BreakpointTypeExtension.ScriptRegExpSupport.Visitor
<RelayOk>() { | |
| 339 @Override | |
| 340 public RelayOk visitScriptName(String scriptName) { | |
| 341 return sendRequest(scriptName, RequestHandler.FOR_URL); | |
| 342 } | |
| 343 | |
| 344 @Override | |
| 345 public RelayOk visitRegExp(String regExp) { | |
| 346 return sendRequest(regExp, RequestHandler.FOR_REGEXP); | |
| 347 } | |
| 348 | |
| 349 @Override | |
| 350 public RelayOk visitScriptId(Object scriptId) { | |
| 351 String scriptIdString = WipScriptManager.convertAlienSourceId(scriptId); | |
| 352 return sendRequest(scriptIdString, RequestHandler.FOR_ID); | |
| 353 } | |
| 354 | |
| 355 @Override | |
| 356 public RelayOk visitUnknown(Target target) { | |
| 357 throw new IllegalArgumentException(); | |
| 358 } | |
| 359 | |
| 360 private <T, DATA, PARAMS extends WipParamsWithResponse<DATA>> RelayOk send
Request( | |
| 361 T parameter, final RequestHandler<T, DATA, PARAMS> handler) { | |
| 362 PARAMS requestParams = | |
| 363 handler.createRequestParams(parameter, lineNumber, columnNumberParam
, condition); | |
| 364 | |
| 365 GenericCallback<DATA> wrappedCallback; | |
| 366 if (callback == null) { | |
| 367 wrappedCallback = null; | |
| 368 } else { | |
| 369 wrappedCallback = new GenericCallback<DATA>() { | |
| 370 @Override | |
| 371 public void success(DATA data) { | |
| 372 String breakpointId = handler.getBreakpointId(data); | |
| 373 Collection<LocationValue> locationValues = handler.getActualLocati
ons(data); | |
| 374 List<ActualLocation> locationList = | |
| 375 new ArrayList<ActualLocation>(locationValues.size()); | |
| 376 for (LocationValue value : locationValues) { | |
| 377 locationList.add(locationFromProtocol(value)); | |
| 378 } | |
| 379 callback.onSuccess(breakpointId, locationList); | |
| 380 } | |
| 381 | |
| 382 @Override | |
| 383 public void failure(Exception exception) { | |
| 384 callback.onFailure(exception); | |
| 385 } | |
| 386 }; | |
| 387 } | |
| 388 | |
| 389 return commandProcessor.send(requestParams, wrappedCallback, syncCallbac
k); | |
| 390 } | |
| 391 }); | |
| 392 } | |
| 393 | |
| 394 private static abstract class RequestHandler<T, | |
| 395 DATA, PARAMS extends WipParamsWithResponse<DATA>> { | |
| 396 | |
| 397 abstract PARAMS createRequestParams(T parameter, long lineNumber, Long colum
nNumberOpt, | |
| 398 String conditionOpt); | |
| 399 | |
| 400 abstract String getBreakpointId(DATA data); | |
| 401 | |
| 402 abstract Collection<LocationValue> getActualLocations(DATA data); | |
| 403 | |
| 404 static abstract class ForUrlOrRegExp | |
| 405 extends RequestHandler<String, SetBreakpointByUrlData, SetBreakpointByUr
lParams> { | |
| 406 @Override | |
| 407 String getBreakpointId(SetBreakpointByUrlData data) { | |
| 408 return data.breakpointId(); | |
| 409 } | |
| 410 | |
| 411 @Override | |
| 412 Collection<LocationValue> getActualLocations(SetBreakpointByUrlData data)
{ | |
| 413 return data.locations(); | |
| 414 } | |
| 415 } | |
| 416 | |
| 417 static final ForUrlOrRegExp FOR_URL = new ForUrlOrRegExp() { | |
| 418 @Override | |
| 419 SetBreakpointByUrlParams createRequestParams(String url, | |
| 420 long lineNumber, Long columnNumber, String condition) { | |
| 421 return new SetBreakpointByUrlParams(lineNumber, url, null, columnNumber,
condition); | |
| 422 } | |
| 423 }; | |
| 424 | |
| 425 static final ForUrlOrRegExp FOR_REGEXP = new ForUrlOrRegExp() { | |
| 426 @Override | |
| 427 SetBreakpointByUrlParams createRequestParams(String url, | |
| 428 long lineNumber, Long columnNumber, String condition) { | |
| 429 return new SetBreakpointByUrlParams(lineNumber, null, url, columnNumber,
condition); | |
| 430 } | |
| 431 }; | |
| 432 | |
| 433 static final RequestHandler<String, SetBreakpointData, SetBreakpointParams>
FOR_ID = | |
| 434 new RequestHandler<String, SetBreakpointData, SetBreakpointParams>() { | |
| 435 @Override | |
| 436 SetBreakpointParams createRequestParams(String sourceId, | |
| 437 long lineNumber, Long columnNumber, String condition) { | |
| 438 LocationParam locationParam = | |
| 439 new LocationParam(sourceId, lineNumber, columnNumber); | |
| 440 return new SetBreakpointParams(locationParam, condition); | |
| 441 } | |
| 442 | |
| 443 @Override | |
| 444 String getBreakpointId(SetBreakpointData data) { | |
| 445 return data.breakpointId(); | |
| 446 } | |
| 447 | |
| 448 @Override | |
| 449 Collection<LocationValue> getActualLocations(SetBreakpointData data) { | |
| 450 return Collections.singletonList(data.actualLocation()); | |
| 451 } | |
| 452 }; | |
| 453 } | |
| 454 | |
| 455 private static ActualLocation locationFromProtocol(LocationValue locationValue
) { | |
| 456 return new ActualLocation(locationValue.scriptId(), locationValue.lineNumber
(), | |
| 457 locationValue.columnNumber()); | |
| 458 } | |
| 459 | |
| 460 private static <T> boolean eq(T left, T right) { | |
| 461 return left == right || (left != null && left.equals(right)); | |
| 462 } | |
| 463 } | |
| OLD | NEW |