From ba9f6ef718974709bd62bf5b2d06af7cb89e6cb8 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Tue, 29 Jul 2014 13:26:21 +0400 Subject: [PATCH] Complete implementation of Google Chrome RDP backend. Add Google Chrome extension --- .../teavm/chromerpd/ChromeRDPContainer.java | 24 +++ ...er.java => ChromeRDPDebuggerEndpoint.java} | 113 +++++++++---- .../org/teavm/chromerpd/ChromeRDPServer.java | 150 ++++++++++++++++++ .../chromerpd/ChromeRDPServerListener.java | 24 +++ .../java/org/teavm/chromerpd/Deferred.java | 48 ++++++ .../org/teavm/chromerpd/RDPBreakpoint.java | 51 ++++++ .../org/teavm/chromerpd/data/Message.java | 9 ++ .../org/teavm/chromerpd/data/Response.java | 43 +++++ .../messages/RemoveBreakpointCommand.java | 32 ++++ .../messages/SetBreakpointCommand.java | 34 ++++ .../messages/SetBreakpointResponse.java | 43 +++++ teavm-chrome-rdp/src/main/js/chrome/ghost.png | Bin 0 -> 363 bytes teavm-chrome-rdp/src/main/js/chrome/main.js | 69 ++++++++ .../src/main/js/chrome/manifest.json | 19 +++ .../java/org/teavm/debugging/Breakpoint.java | 34 +--- .../java/org/teavm/debugging/Debugger.java | 22 ++- .../teavm/debugging/JavaScriptBreakpoint.java | 8 - 17 files changed, 654 insertions(+), 69 deletions(-) create mode 100644 teavm-chrome-rdp/src/main/java/org/teavm/chromerpd/ChromeRDPContainer.java rename teavm-chrome-rdp/src/main/java/org/teavm/chromerpd/{ChromeRDPDebugger.java => ChromeRDPDebuggerEndpoint.java} (61%) create mode 100644 teavm-chrome-rdp/src/main/java/org/teavm/chromerpd/ChromeRDPServer.java create mode 100644 teavm-chrome-rdp/src/main/java/org/teavm/chromerpd/ChromeRDPServerListener.java create mode 100644 teavm-chrome-rdp/src/main/java/org/teavm/chromerpd/Deferred.java create mode 100644 teavm-chrome-rdp/src/main/java/org/teavm/chromerpd/RDPBreakpoint.java create mode 100644 teavm-chrome-rdp/src/main/java/org/teavm/chromerpd/data/Response.java create mode 100644 teavm-chrome-rdp/src/main/java/org/teavm/chromerpd/messages/RemoveBreakpointCommand.java create mode 100644 teavm-chrome-rdp/src/main/java/org/teavm/chromerpd/messages/SetBreakpointCommand.java create mode 100644 teavm-chrome-rdp/src/main/java/org/teavm/chromerpd/messages/SetBreakpointResponse.java create mode 100644 teavm-chrome-rdp/src/main/js/chrome/ghost.png create mode 100644 teavm-chrome-rdp/src/main/js/chrome/main.js create mode 100644 teavm-chrome-rdp/src/main/js/chrome/manifest.json diff --git a/teavm-chrome-rdp/src/main/java/org/teavm/chromerpd/ChromeRDPContainer.java b/teavm-chrome-rdp/src/main/java/org/teavm/chromerpd/ChromeRDPContainer.java new file mode 100644 index 000000000..df09d9107 --- /dev/null +++ b/teavm-chrome-rdp/src/main/java/org/teavm/chromerpd/ChromeRDPContainer.java @@ -0,0 +1,24 @@ +/* + * Copyright 2014 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.chromerpd; + +/** + * + * @author Alexey Andreev + */ +public interface ChromeRDPContainer { + void setDebugger(ChromeRDPDebuggerEndpoint debugger); +} diff --git a/teavm-chrome-rdp/src/main/java/org/teavm/chromerpd/ChromeRDPDebugger.java b/teavm-chrome-rdp/src/main/java/org/teavm/chromerpd/ChromeRDPDebuggerEndpoint.java similarity index 61% rename from teavm-chrome-rdp/src/main/java/org/teavm/chromerpd/ChromeRDPDebugger.java rename to teavm-chrome-rdp/src/main/java/org/teavm/chromerpd/ChromeRDPDebuggerEndpoint.java index 4d9cb2590..665432607 100644 --- a/teavm-chrome-rdp/src/main/java/org/teavm/chromerpd/ChromeRDPDebugger.java +++ b/teavm-chrome-rdp/src/main/java/org/teavm/chromerpd/ChromeRDPDebuggerEndpoint.java @@ -20,27 +20,22 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -import javax.websocket.OnMessage; -import javax.websocket.OnOpen; -import javax.websocket.Session; -import javax.websocket.server.ServerEndpoint; +import javax.websocket.*; import org.codehaus.jackson.JsonNode; -import org.codehaus.jackson.JsonProcessingException; import org.codehaus.jackson.map.ObjectMapper; import org.teavm.chromerpd.data.CallFrameDTO; import org.teavm.chromerpd.data.LocationDTO; import org.teavm.chromerpd.data.Message; -import org.teavm.chromerpd.messages.ContinueToLocationCommand; -import org.teavm.chromerpd.messages.ScriptParsedNotification; -import org.teavm.chromerpd.messages.SuspendedNotification; +import org.teavm.chromerpd.data.Response; +import org.teavm.chromerpd.messages.*; import org.teavm.debugging.*; /** * * @author Alexey Andreev */ -@ServerEndpoint("/") -public class ChromeRDPDebugger implements JavaScriptDebugger { +@ClientEndpoint +public class ChromeRDPDebuggerEndpoint implements JavaScriptDebugger { private List listeners = new ArrayList<>(); private Session session; private RDPCallFrame[] callStack = new RDPCallFrame[0]; @@ -48,25 +43,43 @@ public class ChromeRDPDebugger implements JavaScriptDebugger { private Map scriptIds = new HashMap<>(); private boolean suspended; private ObjectMapper mapper = new ObjectMapper(); + private Map deferredResponses = new HashMap<>(); + private int messageIdGenerator; + boolean closed; @OnOpen public void open(Session session) { this.session = session; + Object container = session.getUserProperties().get("container"); + if (container instanceof ChromeRDPContainer) { + ((ChromeRDPContainer)container).setDebugger(this); + } + } + + @OnClose + public void close() { + closed = true; } @OnMessage public void receive(String messageText) throws IOException { - Message message = mapper.reader(Message.class).readValue(messageText); - switch (message.getMethod()) { - case "Debugger.paused": - firePaused(parseJson(SuspendedNotification.class, message.getParams())); - break; - case "Debugger.resumed": - fireResumed(); - break; - case "Debugger.scriptParsed": - scriptParsed(parseJson(ScriptParsedNotification.class, message.getParams())); - break; + JsonNode jsonMessage = mapper.readTree(messageText); + if (jsonMessage.has("result")) { + Response response = mapper.reader(Response.class).readValue(jsonMessage); + deferredResponses.remove(response.getId()).set(response.getResult()); + } else { + Message message = mapper.reader(Message.class).readValue(messageText); + switch (message.getMethod()) { + case "Debugger.paused": + firePaused(parseJson(SuspendedNotification.class, message.getParams())); + break; + case "Debugger.resumed": + fireResumed(); + break; + case "Debugger.scriptParsed": + scriptParsed(parseJson(ScriptParsedNotification.class, message.getParams())); + break; + } } } @@ -94,6 +107,14 @@ public class ChromeRDPDebugger implements JavaScriptDebugger { return new JavaScriptLocation(scripts.get(dto.getScriptId()), dto.getLineNumber(), dto.getColumnNumber()); } + LocationDTO unmap(JavaScriptLocation location) { + LocationDTO dto = new LocationDTO(); + dto.setScriptId(scriptIds.get(location.getScript())); + dto.setLineNumber(location.getLine()); + dto.setColumnNumber(location.getColumn()); + return dto; + } + private void fireResumed() { suspended = false; for (JavaScriptDebuggerListener listener : listeners) { @@ -123,6 +144,9 @@ public class ChromeRDPDebugger implements JavaScriptDebugger { } private void sendMessage(Message message) { + if (closed) { + return; + } try { String messageText = mapper.writer().writeValueAsString(message); session.getAsyncRemote().sendText(messageText); @@ -133,6 +157,9 @@ public class ChromeRDPDebugger implements JavaScriptDebugger { @Override public void suspend() { + if (closed) { + return; + } Message message = new Message(); message.setMethod("Debugger.pause"); sendMessage(message); @@ -140,6 +167,9 @@ public class ChromeRDPDebugger implements JavaScriptDebugger { @Override public void resume() { + if (closed) { + return; + } Message message = new Message(); message.setMethod("Debugger.resume"); sendMessage(message); @@ -147,6 +177,9 @@ public class ChromeRDPDebugger implements JavaScriptDebugger { @Override public void stepInto() { + if (closed) { + return; + } Message message = new Message(); message.setMethod("Debugger.stepInto"); sendMessage(message); @@ -154,6 +187,9 @@ public class ChromeRDPDebugger implements JavaScriptDebugger { @Override public void stepOut() { + if (closed) { + return; + } Message message = new Message(); message.setMethod("Debugger.stepOut"); sendMessage(message); @@ -161,6 +197,9 @@ public class ChromeRDPDebugger implements JavaScriptDebugger { @Override public void stepOver() { + if (closed) { + return; + } Message message = new Message(); message.setMethod("Debugger.stepOver"); sendMessage(message); @@ -171,11 +210,7 @@ public class ChromeRDPDebugger implements JavaScriptDebugger { Message message = new Message(); message.setMethod("Debugger.continueToLocation"); ContinueToLocationCommand params = new ContinueToLocationCommand(); - LocationDTO locationDTO = new LocationDTO(); - locationDTO.setScriptId(scriptIds.get(location.getScript())); - locationDTO.setLineNumber(location.getLine()); - locationDTO.setColumnNumber(location.getColumn()); - params.setLocation(locationDTO); + params.setLocation(unmap(location)); message.setParams(mapper.valueToTree(params)); sendMessage(message); } @@ -197,6 +232,30 @@ public class ChromeRDPDebugger implements JavaScriptDebugger { @Override public JavaScriptBreakpoint createBreakpoint(JavaScriptLocation location) { - return null; + Message message = new Message(); + message.setId(messageIdGenerator++); + message.setMethod("Debugger.setBreakpoint"); + SetBreakpointCommand params = new SetBreakpointCommand(); + params.setLocation(unmap(location)); + message.setParams(mapper.valueToTree(params)); + Deferred deferred = new Deferred(); + deferredResponses.put(message.getId(), deferred); + sendMessage(message); + try { + SetBreakpointResponse response = mapper.reader(SetBreakpointResponse.class) + .readValue((JsonNode)deferred.get()); + return new RDPBreakpoint(response.getBreakpointId(), this, map(response.getActualLocation())); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + void destroyBreakpoint(RDPBreakpoint breakpoint) { + Message message = new Message(); + message.setMethod("Debugger.removeBreakpoint"); + RemoveBreakpointCommand params = new RemoveBreakpointCommand(); + params.setBreakpointId(breakpoint.getChromeId()); + message.setParams(mapper.valueToTree(params)); + sendMessage(message); } } diff --git a/teavm-chrome-rdp/src/main/java/org/teavm/chromerpd/ChromeRDPServer.java b/teavm-chrome-rdp/src/main/java/org/teavm/chromerpd/ChromeRDPServer.java new file mode 100644 index 000000000..00b8ff649 --- /dev/null +++ b/teavm-chrome-rdp/src/main/java/org/teavm/chromerpd/ChromeRDPServer.java @@ -0,0 +1,150 @@ +/* + * Copyright 2014 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.chromerpd; + +import java.util.*; +import java.util.concurrent.atomic.AtomicReference; +import javax.websocket.Decoder; +import javax.websocket.Encoder; +import javax.websocket.Extension; +import javax.websocket.server.ServerContainer; +import javax.websocket.server.ServerEndpointConfig; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.websocket.jsr356.server.deploy.WebSocketServerContainerInitializer; +import org.teavm.debugging.JavaScriptDebugger; + +/** + * + * @author Alexey Andreev + */ +public class ChromeRDPServer { + private int port = 2357; + private Appendable output = System.err; + private AtomicReference debugger = new AtomicReference<>(); + private List listeners = new ArrayList<>(); + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + + public Appendable getOutput() { + return output; + } + + public void setOutput(Appendable output) { + this.output = output; + } + + public void addListener(ChromeRDPServerListener listener) { + listeners.add(listener); + } + + public void removeListener(ChromeRDPServerListener listener) { + listeners.remove(listener); + } + + public void start() { + Server server = new Server(); + ServerConnector connector = new ServerConnector(server); + connector.setPort(port); + server.addConnector(connector); + + ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); + context.setContextPath("/"); + server.setHandler(context); + + ServerContainer wscontainer = WebSocketServerContainerInitializer.configureContext(context); + + try { + wscontainer.addEndpoint(new RPDEndpointConfig()); + server.start(); + server.dump(output); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private class RPDEndpointConfig implements ServerEndpointConfig { + private Map userProperties = new HashMap<>(); + + public RPDEndpointConfig() { + userProperties.put("rdp.container", new ChromeRDPContainer() { + @Override public void setDebugger(ChromeRDPDebuggerEndpoint debugger) { + ChromeRDPServer.this.setDebugger(debugger); + } + }); + } + + @Override + public List> getDecoders() { + return Collections.emptyList(); + } + + @Override + public List> getEncoders() { + return Collections.emptyList(); + } + + @Override + public Map getUserProperties() { + return userProperties; + } + + @Override + public Configurator getConfigurator() { + return null; + } + + @Override + public Class getEndpointClass() { + return ChromeRDPDebuggerEndpoint.class; + } + + @Override + public List getExtensions() { + return Collections.emptyList(); + } + + @Override + public String getPath() { + return "/"; + } + + @Override + public List getSubprotocols() { + return Collections.emptyList(); + } + } + + public JavaScriptDebugger getDebugger() { + return debugger.get(); + } + + void setDebugger(JavaScriptDebugger debugger) { + if (!this.debugger.compareAndSet(null, debugger)) { + throw new IllegalStateException("Can't handle more than one connection"); + } + for (ChromeRDPServerListener listener : listeners) { + listener.connected(this); + } + } +} diff --git a/teavm-chrome-rdp/src/main/java/org/teavm/chromerpd/ChromeRDPServerListener.java b/teavm-chrome-rdp/src/main/java/org/teavm/chromerpd/ChromeRDPServerListener.java new file mode 100644 index 000000000..bd2822b6c --- /dev/null +++ b/teavm-chrome-rdp/src/main/java/org/teavm/chromerpd/ChromeRDPServerListener.java @@ -0,0 +1,24 @@ +/* + * Copyright 2014 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.chromerpd; + +/** + * + * @author Alexey Andreev + */ +public interface ChromeRDPServerListener { + void connected(ChromeRDPServer server); +} diff --git a/teavm-chrome-rdp/src/main/java/org/teavm/chromerpd/Deferred.java b/teavm-chrome-rdp/src/main/java/org/teavm/chromerpd/Deferred.java new file mode 100644 index 000000000..e5a5479f2 --- /dev/null +++ b/teavm-chrome-rdp/src/main/java/org/teavm/chromerpd/Deferred.java @@ -0,0 +1,48 @@ +/* + * Copyright 2014 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.chromerpd; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * + * @author Alexey Andreev + */ +public class Deferred { + private volatile Object calculatedValue; + private AtomicBoolean calculated = new AtomicBoolean(); + private CountDownLatch latch = new CountDownLatch(1); + + public Object get() { + while (latch.getCount() > 0) { + try { + latch.await(); + } catch (InterruptedException e) { + throw new RuntimeException("Interrupted while awaiting for value"); + } + } + return calculatedValue; + } + + public void set(Object value) { + if (!calculated.compareAndSet(false, true)) { + throw new IllegalStateException("Future already calculated"); + } + calculatedValue = value; + latch.countDown(); + } +} diff --git a/teavm-chrome-rdp/src/main/java/org/teavm/chromerpd/RDPBreakpoint.java b/teavm-chrome-rdp/src/main/java/org/teavm/chromerpd/RDPBreakpoint.java new file mode 100644 index 000000000..55927e262 --- /dev/null +++ b/teavm-chrome-rdp/src/main/java/org/teavm/chromerpd/RDPBreakpoint.java @@ -0,0 +1,51 @@ +/* + * Copyright 2014 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.chromerpd; + +import org.teavm.debugging.JavaScriptBreakpoint; +import org.teavm.debugging.JavaScriptLocation; + +/** + * + * @author Alexey Andreev + */ +public class RDPBreakpoint implements JavaScriptBreakpoint { + private String chromeId; + private ChromeRDPDebuggerEndpoint debugger; + private JavaScriptLocation location; + + public RDPBreakpoint(String chromeId, ChromeRDPDebuggerEndpoint debugger, JavaScriptLocation location) { + this.chromeId = chromeId; + this.debugger = debugger; + this.location = location; + } + + public String getChromeId() { + return chromeId; + } + + @Override + public JavaScriptLocation getLocation() { + return location; + } + + @Override + public void destroy() { + if (!debugger.closed) { + debugger.destroyBreakpoint(this); + } + } +} diff --git a/teavm-chrome-rdp/src/main/java/org/teavm/chromerpd/data/Message.java b/teavm-chrome-rdp/src/main/java/org/teavm/chromerpd/data/Message.java index ff8675522..22cd25a44 100644 --- a/teavm-chrome-rdp/src/main/java/org/teavm/chromerpd/data/Message.java +++ b/teavm-chrome-rdp/src/main/java/org/teavm/chromerpd/data/Message.java @@ -22,9 +22,18 @@ import org.codehaus.jackson.JsonNode; * @author Alexey Andreev */ public class Message { + private Integer id; private String method; private JsonNode params; + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + public String getMethod() { return method; } diff --git a/teavm-chrome-rdp/src/main/java/org/teavm/chromerpd/data/Response.java b/teavm-chrome-rdp/src/main/java/org/teavm/chromerpd/data/Response.java new file mode 100644 index 000000000..da7fa6046 --- /dev/null +++ b/teavm-chrome-rdp/src/main/java/org/teavm/chromerpd/data/Response.java @@ -0,0 +1,43 @@ +/* + * Copyright 2014 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.chromerpd.data; + +import org.codehaus.jackson.JsonNode; + +/** + * + * @author Alexey Andreev + */ +public class Response { + private int id; + private JsonNode result; + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public JsonNode getResult() { + return result; + } + + public void setResult(JsonNode result) { + this.result = result; + } +} diff --git a/teavm-chrome-rdp/src/main/java/org/teavm/chromerpd/messages/RemoveBreakpointCommand.java b/teavm-chrome-rdp/src/main/java/org/teavm/chromerpd/messages/RemoveBreakpointCommand.java new file mode 100644 index 000000000..14f7195fe --- /dev/null +++ b/teavm-chrome-rdp/src/main/java/org/teavm/chromerpd/messages/RemoveBreakpointCommand.java @@ -0,0 +1,32 @@ +/* + * Copyright 2014 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.chromerpd.messages; + +/** + * + * @author Alexey Andreev + */ +public class RemoveBreakpointCommand { + private String breakpointId; + + public String getBreakpointId() { + return breakpointId; + } + + public void setBreakpointId(String breakpointId) { + this.breakpointId = breakpointId; + } +} diff --git a/teavm-chrome-rdp/src/main/java/org/teavm/chromerpd/messages/SetBreakpointCommand.java b/teavm-chrome-rdp/src/main/java/org/teavm/chromerpd/messages/SetBreakpointCommand.java new file mode 100644 index 000000000..18e85e470 --- /dev/null +++ b/teavm-chrome-rdp/src/main/java/org/teavm/chromerpd/messages/SetBreakpointCommand.java @@ -0,0 +1,34 @@ +/* + * Copyright 2014 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.chromerpd.messages; + +import org.teavm.chromerpd.data.LocationDTO; + +/** + * + * @author Alexey Andreev + */ +public class SetBreakpointCommand { + private LocationDTO location; + + public LocationDTO getLocation() { + return location; + } + + public void setLocation(LocationDTO location) { + this.location = location; + } +} diff --git a/teavm-chrome-rdp/src/main/java/org/teavm/chromerpd/messages/SetBreakpointResponse.java b/teavm-chrome-rdp/src/main/java/org/teavm/chromerpd/messages/SetBreakpointResponse.java new file mode 100644 index 000000000..0850f5370 --- /dev/null +++ b/teavm-chrome-rdp/src/main/java/org/teavm/chromerpd/messages/SetBreakpointResponse.java @@ -0,0 +1,43 @@ +/* + * Copyright 2014 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.chromerpd.messages; + +import org.teavm.chromerpd.data.LocationDTO; + +/** + * + * @author Alexey Andreev + */ +public class SetBreakpointResponse { + private String breakpointId; + private LocationDTO actualLocation; + + public String getBreakpointId() { + return breakpointId; + } + + public void setBreakpointId(String breakpointId) { + this.breakpointId = breakpointId; + } + + public LocationDTO getActualLocation() { + return actualLocation; + } + + public void setActualLocation(LocationDTO actualLocation) { + this.actualLocation = actualLocation; + } +} diff --git a/teavm-chrome-rdp/src/main/js/chrome/ghost.png b/teavm-chrome-rdp/src/main/js/chrome/ghost.png new file mode 100644 index 0000000000000000000000000000000000000000..8d4c99342653f709185d42007240656978216679 GIT binary patch literal 363 zcmV-x0hIoUP)N1Fc5}Kt%@j!2=4R* z9>JAYaNipUo})Vt;w?Oa3)dh8nFa=8+Ad@P$pr8-NpaLG?4FXUCDVTtn6a=E04j6$lgKvVA`yX&V8s0^*Hwxbp?F4Q+ru ztMI-DJFpg_?5H{J@+w#`TODjc8*KPN*>#oUT?*8{pYun60Ra0MYV|m(P$d8W002ov JPDHLkV1hZykJbPH literal 0 HcmV?d00001 diff --git a/teavm-chrome-rdp/src/main/js/chrome/main.js b/teavm-chrome-rdp/src/main/js/chrome/main.js new file mode 100644 index 000000000..56f485086 --- /dev/null +++ b/teavm-chrome-rdp/src/main/js/chrome/main.js @@ -0,0 +1,69 @@ +debuggerAgentMap = {}; + +chrome.browserAction.onClicked.addListener(function(tab) { + new DebuggerAgent(tab).attach(); +}); +function DebuggerAgent(tab) { + this.connection = null; + this.tab = null; + this.debuggee = { tabId : tab.id }; + this.attachedToDebugger = false; + debuggerAgentMap[tab.id] = this; +} +DebuggerAgent.prototype.attach = function() { + chrome.debugger.attach(this.debuggee, "1.0", (function(callback) { + this.attachedToDebugger = true; + chrome.debugger.sendCommand(this.debuggee, "Debugger.enable", {}, callback); + }).bind(this, connectToServer.bind(this))); +}; +DebuggerAgent.prototype.connectToServer = function() { + this.connection = new WebSocket("ws://localhost:2357/"); + this.connection.onmessage = function(event) { + receiveMessage(this.debuggee, this.connection, JSON.parse(event.data)); + }.bind(this); + this.connection.onclose = function(event) { + if (this.connection != null) { + this.connection = null; + this.disconnect(); + } + }.bind(this); +}; +DebuggerAgent.prototype.receiveMessage = function(message) { + chrome.debugger.sendCommand(this.debuggee, message.method, message.params, function(response) { + if (message.id) { + var responseToServer = { id : message.id, result : response }; + this.connection.send(JSON.stringify(responseToServer)); + } + }.bind(this)); +}; +DebuggerAgent.prototype.disconnect = function() { + if (this.connection) { + var conn = this.connection; + this.connection = null; + conn.close(); + } + if (this.attachedToDebugger) { + chrome.debugger.detach(this.debuggee); + this.attachedToDebugger = false; + } + if (this.debuggee) { + delete debuggerAgentMap[this.debuggee.tabId]; + this.debuggee = null; + } +}; + +chrome.debugger.onEvent.addListener(function(source, method, params) { + var agent = debuggerAgentMap[source.tabId]; + if (!agent) { + return; + } + var message = { method : method, params : params }; + this.connection.send(JSON.stringify(message)); +}); +chrome.debugger.onDetach.addListener(function(source) { + var agent = debuggerAgentMap[source.tabId]; + if (agent) { + agent.attachedToDebugger = false; + agent.disconnect(); + } +}); \ No newline at end of file diff --git a/teavm-chrome-rdp/src/main/js/chrome/manifest.json b/teavm-chrome-rdp/src/main/js/chrome/manifest.json new file mode 100644 index 000000000..460c9c669 --- /dev/null +++ b/teavm-chrome-rdp/src/main/js/chrome/manifest.json @@ -0,0 +1,19 @@ +{ + "manifest_version": 2, + + "name": "TeaVM debugger agent", + "description": "TeaVM debugger agent, that sends RDP commands over WebSocket", + "version": "1.0", + + "permissions" : ["debugger", "activeTab", "tabs"], + + "browser_action" : { + "default_icon": "ghost.png", + "default_title ": "Connect to TeaVM debugger" + }, + + "background": { + "scripts": ["main.js"], + "persistent": false + }, +} \ No newline at end of file diff --git a/teavm-core/src/main/java/org/teavm/debugging/Breakpoint.java b/teavm-core/src/main/java/org/teavm/debugging/Breakpoint.java index 2be99d3d0..4bf0b9aef 100644 --- a/teavm-core/src/main/java/org/teavm/debugging/Breakpoint.java +++ b/teavm-core/src/main/java/org/teavm/debugging/Breakpoint.java @@ -24,45 +24,25 @@ import java.util.List; public class Breakpoint { private Debugger debugger; private List jsBreakpoints; - private String fileName; - private int line; - private boolean enabled = true; + private SourceLocation location; - Breakpoint(Debugger debugger, List jsBreakpoints, String fileName, int line) { + Breakpoint(Debugger debugger, List jsBreakpoints, SourceLocation location) { this.debugger = debugger; this.jsBreakpoints = jsBreakpoints; - this.fileName = fileName; - this.line = line; + this.location = location; for (JavaScriptBreakpoint jsBreakpoint : jsBreakpoints) { - debugger.breakpointMap.put(jsBreakpoint.getId(), this); + debugger.breakpointMap.put(jsBreakpoint, this); } } - public boolean isEnabled() { - return enabled; - } - - public void setEnabled(boolean enabled) { - if (this.enabled == enabled) { - return; - } - for (JavaScriptBreakpoint jsBreakpoint : jsBreakpoints) { - jsBreakpoint.setEnabled(enabled); - } - } - - public String getFileName() { - return fileName; - } - - public int getLine() { - return line; + public SourceLocation getLocation() { + return location; } public void destroy() { for (JavaScriptBreakpoint jsBreakpoint : jsBreakpoints) { jsBreakpoint.destroy(); - debugger.breakpointMap.remove(jsBreakpoint.getId()); + debugger.breakpointMap.remove(jsBreakpoint); } jsBreakpoints.clear(); } diff --git a/teavm-core/src/main/java/org/teavm/debugging/Debugger.java b/teavm-core/src/main/java/org/teavm/debugging/Debugger.java index 77ac0f960..8521b1881 100644 --- a/teavm-core/src/main/java/org/teavm/debugging/Debugger.java +++ b/teavm-core/src/main/java/org/teavm/debugging/Debugger.java @@ -32,7 +32,7 @@ public class Debugger { private Map debugInformationMap = new HashMap<>(); private Map> debugInformationFileMap = new HashMap<>(); private Map scriptMap = new HashMap<>(); - Map breakpointMap = new HashMap<>(); + Map breakpointMap = new HashMap<>(); private CallFrame[] callStack; public Debugger(JavaScriptDebugger javaScriptDebugger, DebugInformationProvider debugInformationProvider) { @@ -83,6 +83,10 @@ public class Debugger { return list != null ? list : Collections.emptyList(); } + public void continueToLocation(SourceLocation location) { + continueToLocation(location.getFileName(), location.getLine()); + } + public void continueToLocation(String fileName, int line) { if (!javaScriptDebugger.isSuspended()) { return; @@ -105,20 +109,24 @@ public class Debugger { return javaScriptDebugger.isSuspended(); } - public Breakpoint createBreakpoint(String fileName, int line) { + public Breakpoint createBreakpoint(String file, int line) { + return createBreakpoint(new SourceLocation(file, line)); + } + + public Breakpoint createBreakpoint(SourceLocation location) { List jsBreakpoints = new ArrayList<>(); - for (DebugInformation debugInformation : debugInformationBySource(fileName)) { - Collection locations = debugInformation.getGeneratedLocations(fileName, line); - for (GeneratedLocation location : locations) { + for (DebugInformation debugInformation : debugInformationBySource(location.getFileName())) { + Collection locations = debugInformation.getGeneratedLocations(location); + for (GeneratedLocation genLocation : locations) { JavaScriptLocation jsLocation = new JavaScriptLocation(scriptMap.get(debugInformation), - location.getLine(), location.getColumn()); + genLocation.getLine(), genLocation.getColumn()); JavaScriptBreakpoint jsBreakpoint = javaScriptDebugger.createBreakpoint(jsLocation); if (jsBreakpoint != null) { jsBreakpoints.add(jsBreakpoint); } } } - return !jsBreakpoints.isEmpty() ? new Breakpoint(this, jsBreakpoints, fileName, line) : null; + return !jsBreakpoints.isEmpty() ? new Breakpoint(this, jsBreakpoints, location) : null; } public CallFrame[] getCallStack() { diff --git a/teavm-core/src/main/java/org/teavm/debugging/JavaScriptBreakpoint.java b/teavm-core/src/main/java/org/teavm/debugging/JavaScriptBreakpoint.java index f6934836f..b2049fcc4 100644 --- a/teavm-core/src/main/java/org/teavm/debugging/JavaScriptBreakpoint.java +++ b/teavm-core/src/main/java/org/teavm/debugging/JavaScriptBreakpoint.java @@ -20,14 +20,6 @@ package org.teavm.debugging; * @author Alexey Andreev */ public interface JavaScriptBreakpoint { - boolean isEnabled(); - - void setEnabled(boolean enabled); - - String getId(); - - String getFileName(); - JavaScriptLocation getLocation(); void destroy();