mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 16:14:10 -08:00
Complete implementation of Google Chrome RDP backend. Add Google Chrome
extension
This commit is contained in:
parent
0cb0bd469b
commit
ba9f6ef718
|
@ -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);
|
||||||
|
}
|
|
@ -20,27 +20,22 @@ import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import javax.websocket.OnMessage;
|
import javax.websocket.*;
|
||||||
import javax.websocket.OnOpen;
|
|
||||||
import javax.websocket.Session;
|
|
||||||
import javax.websocket.server.ServerEndpoint;
|
|
||||||
import org.codehaus.jackson.JsonNode;
|
import org.codehaus.jackson.JsonNode;
|
||||||
import org.codehaus.jackson.JsonProcessingException;
|
|
||||||
import org.codehaus.jackson.map.ObjectMapper;
|
import org.codehaus.jackson.map.ObjectMapper;
|
||||||
import org.teavm.chromerpd.data.CallFrameDTO;
|
import org.teavm.chromerpd.data.CallFrameDTO;
|
||||||
import org.teavm.chromerpd.data.LocationDTO;
|
import org.teavm.chromerpd.data.LocationDTO;
|
||||||
import org.teavm.chromerpd.data.Message;
|
import org.teavm.chromerpd.data.Message;
|
||||||
import org.teavm.chromerpd.messages.ContinueToLocationCommand;
|
import org.teavm.chromerpd.data.Response;
|
||||||
import org.teavm.chromerpd.messages.ScriptParsedNotification;
|
import org.teavm.chromerpd.messages.*;
|
||||||
import org.teavm.chromerpd.messages.SuspendedNotification;
|
|
||||||
import org.teavm.debugging.*;
|
import org.teavm.debugging.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @author Alexey Andreev
|
* @author Alexey Andreev
|
||||||
*/
|
*/
|
||||||
@ServerEndpoint("/")
|
@ClientEndpoint
|
||||||
public class ChromeRDPDebugger implements JavaScriptDebugger {
|
public class ChromeRDPDebuggerEndpoint implements JavaScriptDebugger {
|
||||||
private List<JavaScriptDebuggerListener> listeners = new ArrayList<>();
|
private List<JavaScriptDebuggerListener> listeners = new ArrayList<>();
|
||||||
private Session session;
|
private Session session;
|
||||||
private RDPCallFrame[] callStack = new RDPCallFrame[0];
|
private RDPCallFrame[] callStack = new RDPCallFrame[0];
|
||||||
|
@ -48,25 +43,43 @@ public class ChromeRDPDebugger implements JavaScriptDebugger {
|
||||||
private Map<String, String> scriptIds = new HashMap<>();
|
private Map<String, String> scriptIds = new HashMap<>();
|
||||||
private boolean suspended;
|
private boolean suspended;
|
||||||
private ObjectMapper mapper = new ObjectMapper();
|
private ObjectMapper mapper = new ObjectMapper();
|
||||||
|
private Map<Integer, Deferred> deferredResponses = new HashMap<>();
|
||||||
|
private int messageIdGenerator;
|
||||||
|
boolean closed;
|
||||||
|
|
||||||
@OnOpen
|
@OnOpen
|
||||||
public void open(Session session) {
|
public void open(Session session) {
|
||||||
this.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
|
@OnMessage
|
||||||
public void receive(String messageText) throws IOException {
|
public void receive(String messageText) throws IOException {
|
||||||
Message message = mapper.reader(Message.class).readValue(messageText);
|
JsonNode jsonMessage = mapper.readTree(messageText);
|
||||||
switch (message.getMethod()) {
|
if (jsonMessage.has("result")) {
|
||||||
case "Debugger.paused":
|
Response response = mapper.reader(Response.class).readValue(jsonMessage);
|
||||||
firePaused(parseJson(SuspendedNotification.class, message.getParams()));
|
deferredResponses.remove(response.getId()).set(response.getResult());
|
||||||
break;
|
} else {
|
||||||
case "Debugger.resumed":
|
Message message = mapper.reader(Message.class).readValue(messageText);
|
||||||
fireResumed();
|
switch (message.getMethod()) {
|
||||||
break;
|
case "Debugger.paused":
|
||||||
case "Debugger.scriptParsed":
|
firePaused(parseJson(SuspendedNotification.class, message.getParams()));
|
||||||
scriptParsed(parseJson(ScriptParsedNotification.class, message.getParams()));
|
break;
|
||||||
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());
|
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() {
|
private void fireResumed() {
|
||||||
suspended = false;
|
suspended = false;
|
||||||
for (JavaScriptDebuggerListener listener : listeners) {
|
for (JavaScriptDebuggerListener listener : listeners) {
|
||||||
|
@ -123,6 +144,9 @@ public class ChromeRDPDebugger implements JavaScriptDebugger {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendMessage(Message message) {
|
private void sendMessage(Message message) {
|
||||||
|
if (closed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
String messageText = mapper.writer().writeValueAsString(message);
|
String messageText = mapper.writer().writeValueAsString(message);
|
||||||
session.getAsyncRemote().sendText(messageText);
|
session.getAsyncRemote().sendText(messageText);
|
||||||
|
@ -133,6 +157,9 @@ public class ChromeRDPDebugger implements JavaScriptDebugger {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void suspend() {
|
public void suspend() {
|
||||||
|
if (closed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
Message message = new Message();
|
Message message = new Message();
|
||||||
message.setMethod("Debugger.pause");
|
message.setMethod("Debugger.pause");
|
||||||
sendMessage(message);
|
sendMessage(message);
|
||||||
|
@ -140,6 +167,9 @@ public class ChromeRDPDebugger implements JavaScriptDebugger {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void resume() {
|
public void resume() {
|
||||||
|
if (closed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
Message message = new Message();
|
Message message = new Message();
|
||||||
message.setMethod("Debugger.resume");
|
message.setMethod("Debugger.resume");
|
||||||
sendMessage(message);
|
sendMessage(message);
|
||||||
|
@ -147,6 +177,9 @@ public class ChromeRDPDebugger implements JavaScriptDebugger {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void stepInto() {
|
public void stepInto() {
|
||||||
|
if (closed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
Message message = new Message();
|
Message message = new Message();
|
||||||
message.setMethod("Debugger.stepInto");
|
message.setMethod("Debugger.stepInto");
|
||||||
sendMessage(message);
|
sendMessage(message);
|
||||||
|
@ -154,6 +187,9 @@ public class ChromeRDPDebugger implements JavaScriptDebugger {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void stepOut() {
|
public void stepOut() {
|
||||||
|
if (closed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
Message message = new Message();
|
Message message = new Message();
|
||||||
message.setMethod("Debugger.stepOut");
|
message.setMethod("Debugger.stepOut");
|
||||||
sendMessage(message);
|
sendMessage(message);
|
||||||
|
@ -161,6 +197,9 @@ public class ChromeRDPDebugger implements JavaScriptDebugger {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void stepOver() {
|
public void stepOver() {
|
||||||
|
if (closed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
Message message = new Message();
|
Message message = new Message();
|
||||||
message.setMethod("Debugger.stepOver");
|
message.setMethod("Debugger.stepOver");
|
||||||
sendMessage(message);
|
sendMessage(message);
|
||||||
|
@ -171,11 +210,7 @@ public class ChromeRDPDebugger implements JavaScriptDebugger {
|
||||||
Message message = new Message();
|
Message message = new Message();
|
||||||
message.setMethod("Debugger.continueToLocation");
|
message.setMethod("Debugger.continueToLocation");
|
||||||
ContinueToLocationCommand params = new ContinueToLocationCommand();
|
ContinueToLocationCommand params = new ContinueToLocationCommand();
|
||||||
LocationDTO locationDTO = new LocationDTO();
|
params.setLocation(unmap(location));
|
||||||
locationDTO.setScriptId(scriptIds.get(location.getScript()));
|
|
||||||
locationDTO.setLineNumber(location.getLine());
|
|
||||||
locationDTO.setColumnNumber(location.getColumn());
|
|
||||||
params.setLocation(locationDTO);
|
|
||||||
message.setParams(mapper.valueToTree(params));
|
message.setParams(mapper.valueToTree(params));
|
||||||
sendMessage(message);
|
sendMessage(message);
|
||||||
}
|
}
|
||||||
|
@ -197,6 +232,30 @@ public class ChromeRDPDebugger implements JavaScriptDebugger {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JavaScriptBreakpoint createBreakpoint(JavaScriptLocation location) {
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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<JavaScriptDebugger> debugger = new AtomicReference<>();
|
||||||
|
private List<ChromeRDPServerListener> 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<String, Object> userProperties = new HashMap<>();
|
||||||
|
|
||||||
|
public RPDEndpointConfig() {
|
||||||
|
userProperties.put("rdp.container", new ChromeRDPContainer() {
|
||||||
|
@Override public void setDebugger(ChromeRDPDebuggerEndpoint debugger) {
|
||||||
|
ChromeRDPServer.this.setDebugger(debugger);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Class<? extends Decoder>> getDecoders() {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Class<? extends Encoder>> getEncoders() {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Object> getUserProperties() {
|
||||||
|
return userProperties;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Configurator getConfigurator() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<?> getEndpointClass() {
|
||||||
|
return ChromeRDPDebuggerEndpoint.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Extension> getExtensions() {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getPath() {
|
||||||
|
return "/";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -22,9 +22,18 @@ import org.codehaus.jackson.JsonNode;
|
||||||
* @author Alexey Andreev
|
* @author Alexey Andreev
|
||||||
*/
|
*/
|
||||||
public class Message {
|
public class Message {
|
||||||
|
private Integer id;
|
||||||
private String method;
|
private String method;
|
||||||
private JsonNode params;
|
private JsonNode params;
|
||||||
|
|
||||||
|
public Integer getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Integer id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
public String getMethod() {
|
public String getMethod() {
|
||||||
return method;
|
return method;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
BIN
teavm-chrome-rdp/src/main/js/chrome/ghost.png
Normal file
BIN
teavm-chrome-rdp/src/main/js/chrome/ghost.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 363 B |
69
teavm-chrome-rdp/src/main/js/chrome/main.js
Normal file
69
teavm-chrome-rdp/src/main/js/chrome/main.js
Normal file
|
@ -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();
|
||||||
|
}
|
||||||
|
});
|
19
teavm-chrome-rdp/src/main/js/chrome/manifest.json
Normal file
19
teavm-chrome-rdp/src/main/js/chrome/manifest.json
Normal file
|
@ -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
|
||||||
|
},
|
||||||
|
}
|
|
@ -24,45 +24,25 @@ import java.util.List;
|
||||||
public class Breakpoint {
|
public class Breakpoint {
|
||||||
private Debugger debugger;
|
private Debugger debugger;
|
||||||
private List<JavaScriptBreakpoint> jsBreakpoints;
|
private List<JavaScriptBreakpoint> jsBreakpoints;
|
||||||
private String fileName;
|
private SourceLocation location;
|
||||||
private int line;
|
|
||||||
private boolean enabled = true;
|
|
||||||
|
|
||||||
Breakpoint(Debugger debugger, List<JavaScriptBreakpoint> jsBreakpoints, String fileName, int line) {
|
Breakpoint(Debugger debugger, List<JavaScriptBreakpoint> jsBreakpoints, SourceLocation location) {
|
||||||
this.debugger = debugger;
|
this.debugger = debugger;
|
||||||
this.jsBreakpoints = jsBreakpoints;
|
this.jsBreakpoints = jsBreakpoints;
|
||||||
this.fileName = fileName;
|
this.location = location;
|
||||||
this.line = line;
|
|
||||||
for (JavaScriptBreakpoint jsBreakpoint : jsBreakpoints) {
|
for (JavaScriptBreakpoint jsBreakpoint : jsBreakpoints) {
|
||||||
debugger.breakpointMap.put(jsBreakpoint.getId(), this);
|
debugger.breakpointMap.put(jsBreakpoint, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isEnabled() {
|
public SourceLocation getLocation() {
|
||||||
return enabled;
|
return location;
|
||||||
}
|
|
||||||
|
|
||||||
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 void destroy() {
|
public void destroy() {
|
||||||
for (JavaScriptBreakpoint jsBreakpoint : jsBreakpoints) {
|
for (JavaScriptBreakpoint jsBreakpoint : jsBreakpoints) {
|
||||||
jsBreakpoint.destroy();
|
jsBreakpoint.destroy();
|
||||||
debugger.breakpointMap.remove(jsBreakpoint.getId());
|
debugger.breakpointMap.remove(jsBreakpoint);
|
||||||
}
|
}
|
||||||
jsBreakpoints.clear();
|
jsBreakpoints.clear();
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,7 @@ public class Debugger {
|
||||||
private Map<String, DebugInformation> debugInformationMap = new HashMap<>();
|
private Map<String, DebugInformation> debugInformationMap = new HashMap<>();
|
||||||
private Map<String, List<DebugInformation>> debugInformationFileMap = new HashMap<>();
|
private Map<String, List<DebugInformation>> debugInformationFileMap = new HashMap<>();
|
||||||
private Map<DebugInformation, String> scriptMap = new HashMap<>();
|
private Map<DebugInformation, String> scriptMap = new HashMap<>();
|
||||||
Map<String, Breakpoint> breakpointMap = new HashMap<>();
|
Map<JavaScriptBreakpoint, Breakpoint> breakpointMap = new HashMap<>();
|
||||||
private CallFrame[] callStack;
|
private CallFrame[] callStack;
|
||||||
|
|
||||||
public Debugger(JavaScriptDebugger javaScriptDebugger, DebugInformationProvider debugInformationProvider) {
|
public Debugger(JavaScriptDebugger javaScriptDebugger, DebugInformationProvider debugInformationProvider) {
|
||||||
|
@ -83,6 +83,10 @@ public class Debugger {
|
||||||
return list != null ? list : Collections.<DebugInformation>emptyList();
|
return list != null ? list : Collections.<DebugInformation>emptyList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void continueToLocation(SourceLocation location) {
|
||||||
|
continueToLocation(location.getFileName(), location.getLine());
|
||||||
|
}
|
||||||
|
|
||||||
public void continueToLocation(String fileName, int line) {
|
public void continueToLocation(String fileName, int line) {
|
||||||
if (!javaScriptDebugger.isSuspended()) {
|
if (!javaScriptDebugger.isSuspended()) {
|
||||||
return;
|
return;
|
||||||
|
@ -105,20 +109,24 @@ public class Debugger {
|
||||||
return javaScriptDebugger.isSuspended();
|
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<JavaScriptBreakpoint> jsBreakpoints = new ArrayList<>();
|
List<JavaScriptBreakpoint> jsBreakpoints = new ArrayList<>();
|
||||||
for (DebugInformation debugInformation : debugInformationBySource(fileName)) {
|
for (DebugInformation debugInformation : debugInformationBySource(location.getFileName())) {
|
||||||
Collection<GeneratedLocation> locations = debugInformation.getGeneratedLocations(fileName, line);
|
Collection<GeneratedLocation> locations = debugInformation.getGeneratedLocations(location);
|
||||||
for (GeneratedLocation location : locations) {
|
for (GeneratedLocation genLocation : locations) {
|
||||||
JavaScriptLocation jsLocation = new JavaScriptLocation(scriptMap.get(debugInformation),
|
JavaScriptLocation jsLocation = new JavaScriptLocation(scriptMap.get(debugInformation),
|
||||||
location.getLine(), location.getColumn());
|
genLocation.getLine(), genLocation.getColumn());
|
||||||
JavaScriptBreakpoint jsBreakpoint = javaScriptDebugger.createBreakpoint(jsLocation);
|
JavaScriptBreakpoint jsBreakpoint = javaScriptDebugger.createBreakpoint(jsLocation);
|
||||||
if (jsBreakpoint != null) {
|
if (jsBreakpoint != null) {
|
||||||
jsBreakpoints.add(jsBreakpoint);
|
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() {
|
public CallFrame[] getCallStack() {
|
||||||
|
|
|
@ -20,14 +20,6 @@ package org.teavm.debugging;
|
||||||
* @author Alexey Andreev
|
* @author Alexey Andreev
|
||||||
*/
|
*/
|
||||||
public interface JavaScriptBreakpoint {
|
public interface JavaScriptBreakpoint {
|
||||||
boolean isEnabled();
|
|
||||||
|
|
||||||
void setEnabled(boolean enabled);
|
|
||||||
|
|
||||||
String getId();
|
|
||||||
|
|
||||||
String getFileName();
|
|
||||||
|
|
||||||
JavaScriptLocation getLocation();
|
JavaScriptLocation getLocation();
|
||||||
|
|
||||||
void destroy();
|
void destroy();
|
||||||
|
|
Loading…
Reference in New Issue
Block a user