| 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.io.IOException; | |
| 8 import java.util.Arrays; | |
| 9 import java.util.Collection; | |
| 10 import java.util.EnumMap; | |
| 11 import java.util.Map; | |
| 12 import java.util.logging.Level; | |
| 13 import java.util.logging.Logger; | |
| 14 | |
| 15 import org.chromium.sdk.Breakpoint; | |
| 16 import org.chromium.sdk.BreakpointTypeExtension; | |
| 17 import org.chromium.sdk.BrowserTab; | |
| 18 import org.chromium.sdk.CallbackSemaphore; | |
| 19 import org.chromium.sdk.FunctionScopeExtension; | |
| 20 import org.chromium.sdk.IgnoreCountBreakpointExtension; | |
| 21 import org.chromium.sdk.RelayOk; | |
| 22 import org.chromium.sdk.RestartFrameExtension; | |
| 23 import org.chromium.sdk.Script; | |
| 24 import org.chromium.sdk.SyncCallback; | |
| 25 import org.chromium.sdk.TabDebugEventListener; | |
| 26 import org.chromium.sdk.Version; | |
| 27 import org.chromium.sdk.internal.JsonUtil; | |
| 28 import org.chromium.sdk.internal.websocket.WsConnection; | |
| 29 import org.chromium.sdk.internal.wip.protocol.input.WipCommandResponse.Success; | |
| 30 import org.chromium.sdk.internal.wip.protocol.output.WipParams; | |
| 31 import org.chromium.sdk.internal.wip.protocol.output.debugger.PauseParams; | |
| 32 import org.chromium.sdk.internal.wip.protocol.output.debugger.SetBreakpointsActi
veParams; | |
| 33 import org.chromium.sdk.internal.wip.protocol.output.debugger.SetPauseOnExceptio
nsParams; | |
| 34 import org.chromium.sdk.util.GenericCallback; | |
| 35 import org.chromium.sdk.util.MethodIsBlockingException; | |
| 36 import org.chromium.sdk.util.RelaySyncCallback; | |
| 37 import org.chromium.sdk.util.SignalRelay; | |
| 38 import org.chromium.sdk.util.SignalRelay.AlreadySignalledException; | |
| 39 import org.chromium.sdk.wip.EvaluateToMappingExtension; | |
| 40 import org.chromium.sdk.wip.PermanentRemoteValueMapping; | |
| 41 import org.chromium.sdk.wip.WipBrowser; | |
| 42 import org.chromium.sdk.wip.WipBrowserTab; | |
| 43 import org.chromium.sdk.wip.WipJavascriptVm; | |
| 44 import org.json.simple.JSONObject; | |
| 45 import org.json.simple.parser.ParseException; | |
| 46 | |
| 47 /** | |
| 48 * {@link BrowserTab} implementation that attaches to remote tab via WebInspecto
r | |
| 49 * protocol (WIP). | |
| 50 */ | |
| 51 public class WipTabImpl implements WipBrowserTab, WipJavascriptVm { | |
| 52 private static final Logger LOGGER = Logger.getLogger(WipTabImpl.class.getName
()); | |
| 53 | |
| 54 private final WsConnection socket; | |
| 55 private final WipBrowserImpl browserImpl; | |
| 56 private final TabDebugEventListener tabListener; | |
| 57 private final WipCommandProcessor commandProcessor; | |
| 58 private final WipScriptManager scriptManager = new WipScriptManager(this); | |
| 59 private final WipBreakpointManager breakpointManager = new WipBreakpointManage
r(this); | |
| 60 private final WipContextBuilder contextBuilder = new WipContextBuilder(this); | |
| 61 private final WipFrameManager frameManager = new WipFrameManager(this); | |
| 62 | |
| 63 private final VmState vmState = new VmState(); | |
| 64 private final SignalRelay<Void> closeSignalRelay; | |
| 65 | |
| 66 private volatile String url; | |
| 67 | |
| 68 public WipTabImpl(WsConnection socket, WipBrowserImpl browserImpl, | |
| 69 TabDebugEventListener tabListener, String preliminaryUrl) throws IOExcepti
on { | |
| 70 this.socket = socket; | |
| 71 this.browserImpl = browserImpl; | |
| 72 this.tabListener = tabListener; | |
| 73 this.url = preliminaryUrl; | |
| 74 | |
| 75 this.closeSignalRelay = SignalRelay.create(new SignalRelay.Callback<Void>()
{ | |
| 76 @Override | |
| 77 public void onSignal(Void signal, Exception cause) { | |
| 78 WipTabImpl.this.tabListener.closed(); | |
| 79 WipTabImpl.this.tabListener.getDebugEventListener().disconnected(); | |
| 80 } | |
| 81 }); | |
| 82 | |
| 83 try { | |
| 84 closeSignalRelay.bind(socket.getCloser(), null, null); | |
| 85 } catch (AlreadySignalledException e) { | |
| 86 throw new IOException("Connection is closed", e); | |
| 87 } | |
| 88 | |
| 89 commandProcessor = new WipCommandProcessor(this, socket); | |
| 90 | |
| 91 WsConnection.Listener socketListener = new WsConnection.Listener() { | |
| 92 @Override | |
| 93 public void textMessageRecieved(String text) { | |
| 94 JSONObject json; | |
| 95 try { | |
| 96 json = JsonUtil.jsonObjectFromJson(text); | |
| 97 } catch (ParseException e) { | |
| 98 throw new RuntimeException(e); | |
| 99 } | |
| 100 commandProcessor.acceptResponse(json); | |
| 101 } | |
| 102 | |
| 103 @Override | |
| 104 public void errorMessage(Exception ex) { | |
| 105 LOGGER.log(Level.SEVERE, "WebSocket protocol error", ex); | |
| 106 } | |
| 107 | |
| 108 @Override | |
| 109 public void eofMessage() { | |
| 110 commandProcessor.processEos(); | |
| 111 } | |
| 112 }; | |
| 113 | |
| 114 socket.startListening(socketListener); | |
| 115 | |
| 116 init(); | |
| 117 } | |
| 118 | |
| 119 private void init() { | |
| 120 SyncCallback syncCallback = new SyncCallback() { | |
| 121 @Override | |
| 122 public void callbackDone(RuntimeException e) { | |
| 123 // This statement suits sync callback more rather than a regular callbac
k: | |
| 124 // it's safe enough and we prefer to execute it event if the command fai
led. | |
| 125 scriptManager.endPopulateScriptMode(); | |
| 126 } | |
| 127 }; | |
| 128 | |
| 129 commandProcessor.send( | |
| 130 new org.chromium.sdk.internal.wip.protocol.output.debugger.EnableParams(
), | |
| 131 null, syncCallback); | |
| 132 | |
| 133 commandProcessor.send( | |
| 134 new org.chromium.sdk.internal.wip.protocol.output.page.EnableParams(), | |
| 135 null, null); | |
| 136 | |
| 137 frameManager.readFrames(); | |
| 138 } | |
| 139 | |
| 140 void updateUrl(String url, boolean silent) { | |
| 141 this.url = url; | |
| 142 if (silent) { | |
| 143 return; | |
| 144 } | |
| 145 scriptManager.pageReloaded(); | |
| 146 breakpointManager.clearNonProvisionalBreakpoints(); | |
| 147 WipTabImpl.this.tabListener.navigated(this.url); | |
| 148 contextBuilder.getEvaluateHack().pageReloaded(); | |
| 149 } | |
| 150 | |
| 151 WipScriptManager getScriptManager() { | |
| 152 return scriptManager; | |
| 153 } | |
| 154 | |
| 155 WipBreakpointManager getBreakpointManager() { | |
| 156 return breakpointManager; | |
| 157 } | |
| 158 | |
| 159 @Override | |
| 160 public boolean detach() { | |
| 161 closeSignalRelay.sendSignal(null, null); | |
| 162 return true; | |
| 163 } | |
| 164 | |
| 165 @Override | |
| 166 public boolean isAttached() { | |
| 167 return !closeSignalRelay.isSignalled(); | |
| 168 } | |
| 169 | |
| 170 @Override | |
| 171 public PermanentRemoteValueMapping createPermanentValueMapping(String id) { | |
| 172 return new PermanentRemoteValueMappingImpl(this, id); | |
| 173 } | |
| 174 | |
| 175 @Override | |
| 176 public RelayOk enableBreakpoints(Boolean enabled, | |
| 177 GenericCallback<Boolean> callback, SyncCallback syncCallback) { | |
| 178 return updateVmVariable(enabled, VmState.BREAKPOINTS_ACTIVE, callback, syncC
allback); | |
| 179 } | |
| 180 | |
| 181 @Override | |
| 182 public RelayOk setBreakOnException(ExceptionCatchMode catchMode, | |
| 183 GenericCallback<ExceptionCatchMode> callback, SyncCallback syncCallback) { | |
| 184 | |
| 185 VmState.Variable<ExceptionCatchMode> variable = VmState.BREAK_ON_EXCEPTION; | |
| 186 return updateVmVariable(catchMode, variable, callback, syncCallback); | |
| 187 } | |
| 188 | |
| 189 /** | |
| 190 * Updates locally saved variables state and send request to remote. If user o
nly calls | |
| 191 * the method to learn the current value, request is sent anyway, to keep resp
onses in sequence. | |
| 192 * @return | |
| 193 */ | |
| 194 private <T> RelayOk updateVmVariable(T value, VmState.Variable<T> variable, | |
| 195 final GenericCallback<T> callback, SyncCallback syncCallback) { | |
| 196 synchronized (vmState) { | |
| 197 final T newValue; | |
| 198 if (value == null) { | |
| 199 newValue = variable.getValue(vmState); | |
| 200 } else { | |
| 201 variable.setValue(vmState, value); | |
| 202 newValue = value; | |
| 203 } | |
| 204 WipParams params = variable.createRequestParams(vmState); | |
| 205 WipCommandCallback wrappedCallback; | |
| 206 if (callback == null) { | |
| 207 wrappedCallback = null; | |
| 208 } else { | |
| 209 wrappedCallback = new WipCommandCallback.Default() { | |
| 210 @Override protected void onSuccess(Success success) { | |
| 211 callback.success(newValue); | |
| 212 } | |
| 213 @Override protected void onError(String message) { | |
| 214 callback.failure(new Exception(message)); | |
| 215 } | |
| 216 }; | |
| 217 } | |
| 218 return commandProcessor.send(params, wrappedCallback, syncCallback); | |
| 219 } | |
| 220 } | |
| 221 | |
| 222 @Override | |
| 223 public Version getVersion() { | |
| 224 // TODO(peter.rybin): support it. | |
| 225 return new Version(Arrays.asList(0, 0), " <Unknown V8 version>"); | |
| 226 } | |
| 227 | |
| 228 @Override | |
| 229 public BreakpointTypeExtension getBreakpointTypeExtension() { | |
| 230 return WipBreakpointImpl.TYPE_EXTENSION; | |
| 231 } | |
| 232 | |
| 233 @Override | |
| 234 public IgnoreCountBreakpointExtension getIgnoreCountBreakpointExtension() { | |
| 235 return WipBreakpointImpl.getIgnoreCountBreakpointExtensionImpl(); | |
| 236 } | |
| 237 | |
| 238 @Override | |
| 239 public EvaluateToMappingExtension getEvaluateWithDestinationMappingExtension()
{ | |
| 240 return WipEvaluateContextBase.EVALUATE_TO_MAPPING_EXTENSION; | |
| 241 } | |
| 242 | |
| 243 @Override | |
| 244 public FunctionScopeExtension getFunctionScopeExtension() { | |
| 245 return WipValueBuilder.FUNCTION_SCOPE_EXTENSION; | |
| 246 } | |
| 247 | |
| 248 @Override | |
| 249 public RestartFrameExtension getRestartFrameExtension() { | |
| 250 return null; | |
| 251 } | |
| 252 | |
| 253 @Override | |
| 254 public void getScripts(final ScriptsCallback callback) | |
| 255 throws MethodIsBlockingException { | |
| 256 | |
| 257 final CallbackSemaphore callbackSemaphore = new CallbackSemaphore(); | |
| 258 | |
| 259 GenericCallback<Collection<Script>> innerCallback; | |
| 260 if (callback == null) { | |
| 261 innerCallback = null; | |
| 262 } else { | |
| 263 innerCallback = new GenericCallback<Collection<Script>>() { | |
| 264 @Override public void success(Collection<Script> value) { | |
| 265 callback.success(value); | |
| 266 } | |
| 267 @Override public void failure(Exception exception) { | |
| 268 callback.failure(exception.getMessage()); | |
| 269 } | |
| 270 }; | |
| 271 } | |
| 272 | |
| 273 RelayOk relayOk = scriptManager.getScripts(innerCallback, callbackSemaphore)
; | |
| 274 | |
| 275 callbackSemaphore.acquireDefault(relayOk); | |
| 276 } | |
| 277 | |
| 278 @Override | |
| 279 public RelayOk setBreakpoint(Breakpoint.Target target, int line, int column, | |
| 280 boolean enabled, String condition, | |
| 281 BreakpointCallback callback, SyncCallback syncCallback) { | |
| 282 return breakpointManager.setBreakpoint(target, line, column, enabled, condit
ion, | |
| 283 callback, syncCallback); | |
| 284 } | |
| 285 | |
| 286 @Override | |
| 287 public void suspend(final SuspendCallback callback) { | |
| 288 PauseParams params = new PauseParams(); | |
| 289 WipCommandCallback wrappedCallback; | |
| 290 if (callback == null) { | |
| 291 wrappedCallback = null; | |
| 292 } else { | |
| 293 wrappedCallback = new WipCommandCallback.Default() { | |
| 294 @Override protected void onSuccess(Success success) { | |
| 295 callback.success(); | |
| 296 } | |
| 297 @Override protected void onError(String message) { | |
| 298 callback.failure(new Exception(message)); | |
| 299 } | |
| 300 }; | |
| 301 } | |
| 302 commandProcessor.send(params, wrappedCallback, null); | |
| 303 } | |
| 304 | |
| 305 @Override | |
| 306 public RelayOk listBreakpoints(ListBreakpointsCallback callback, | |
| 307 SyncCallback syncCallback) { | |
| 308 if (callback != null) { | |
| 309 callback.success(breakpointManager.getAllBreakpoints()); | |
| 310 } | |
| 311 return RelaySyncCallback.finish(syncCallback); | |
| 312 } | |
| 313 | |
| 314 @Override | |
| 315 public WipBrowser getBrowser() { | |
| 316 return browserImpl; | |
| 317 } | |
| 318 | |
| 319 @Override | |
| 320 public WipJavascriptVm getJavascriptVm() { | |
| 321 return this; | |
| 322 } | |
| 323 | |
| 324 @Override | |
| 325 public String getUrl() { | |
| 326 return url; | |
| 327 } | |
| 328 | |
| 329 public TabDebugEventListener getDebugListener() { | |
| 330 return this.tabListener; | |
| 331 } | |
| 332 | |
| 333 public WsConnection getWsSocket() { | |
| 334 return this.socket; | |
| 335 } | |
| 336 | |
| 337 WipContextBuilder getContextBuilder() { | |
| 338 return contextBuilder; | |
| 339 } | |
| 340 | |
| 341 WipCommandProcessor getCommandProcessor() { | |
| 342 return commandProcessor; | |
| 343 } | |
| 344 | |
| 345 WipFrameManager getFrameManager() { | |
| 346 return frameManager; | |
| 347 } | |
| 348 | |
| 349 TabDebugEventListener getTabListener() { | |
| 350 return tabListener; | |
| 351 } | |
| 352 | |
| 353 /** | |
| 354 * Saves currently set VM parameters. Default values must correspond to those | |
| 355 * of WebInspector protocol. | |
| 356 */ | |
| 357 private static class VmState { | |
| 358 // TODO: get protocol declare this default value explicitly. | |
| 359 private static final boolean DEFAULT_BREAKPOINTS_ACTIVE = true; | |
| 360 | |
| 361 // TODO: get protocol declare this default value explicitly. | |
| 362 private static final ExceptionCatchMode DEFAULT_CATCH_MODE = ExceptionCatchM
ode.NONE; | |
| 363 | |
| 364 boolean breakpointsActive = DEFAULT_BREAKPOINTS_ACTIVE; | |
| 365 | |
| 366 // TODO: do we know default value? | |
| 367 ExceptionCatchMode breakOnExceptionMode = DEFAULT_CATCH_MODE; | |
| 368 | |
| 369 static abstract class Variable<T> { | |
| 370 abstract T getValue(VmState vmState); | |
| 371 abstract void setValue(VmState vmState, T value); | |
| 372 abstract WipParams createRequestParams(VmState vmState); | |
| 373 } | |
| 374 | |
| 375 static final Variable<Boolean> BREAKPOINTS_ACTIVE = new Variable<Boolean>()
{ | |
| 376 @Override Boolean getValue(VmState vmState) { | |
| 377 return vmState.breakpointsActive; | |
| 378 } | |
| 379 @Override void setValue(VmState vmState, Boolean value) { | |
| 380 vmState.breakpointsActive = value; | |
| 381 } | |
| 382 @Override WipParams createRequestParams(VmState vmState) { | |
| 383 return new SetBreakpointsActiveParams(vmState.breakpointsActive); | |
| 384 } | |
| 385 }; | |
| 386 | |
| 387 static final Variable<ExceptionCatchMode> BREAK_ON_EXCEPTION = | |
| 388 new Variable<ExceptionCatchMode>() { | |
| 389 @Override ExceptionCatchMode getValue(VmState vmState) { | |
| 390 return vmState.breakOnExceptionMode; | |
| 391 } | |
| 392 @Override void setValue(VmState vmState, ExceptionCatchMode value) { | |
| 393 vmState.breakOnExceptionMode = value; | |
| 394 } | |
| 395 @Override WipParams createRequestParams(VmState vmState) { | |
| 396 return vmState.createPauseOnExceptionRequest(); | |
| 397 } | |
| 398 }; | |
| 399 | |
| 400 private SetPauseOnExceptionsParams createPauseOnExceptionRequest() { | |
| 401 SetPauseOnExceptionsParams.State state = SDK_TO_WIP_CATCH_MODE.get(breakOn
ExceptionMode); | |
| 402 return new SetPauseOnExceptionsParams(state); | |
| 403 } | |
| 404 | |
| 405 private static Map<ExceptionCatchMode, SetPauseOnExceptionsParams.State> SDK
_TO_WIP_CATCH_MODE; | |
| 406 static { | |
| 407 SDK_TO_WIP_CATCH_MODE = new EnumMap<ExceptionCatchMode, SetPauseOnExceptio
nsParams.State>( | |
| 408 ExceptionCatchMode.class); | |
| 409 | |
| 410 SDK_TO_WIP_CATCH_MODE.put(ExceptionCatchMode.ALL, SetPauseOnExceptionsPara
ms.State.ALL); | |
| 411 SDK_TO_WIP_CATCH_MODE.put(ExceptionCatchMode.UNCAUGHT, | |
| 412 SetPauseOnExceptionsParams.State.UNCAUGHT); | |
| 413 SDK_TO_WIP_CATCH_MODE.put(ExceptionCatchMode.NONE, SetPauseOnExceptionsPar
ams.State.NONE); | |
| 414 | |
| 415 assert SDK_TO_WIP_CATCH_MODE.size() == ExceptionCatchMode.values().length; | |
| 416 } | |
| 417 } | |
| 418 } | |
| OLD | NEW |