diff --git a/stetho-js-rhino/src/main/java/com/facebook/stetho/rhino/JsRuntimeRepl.java b/stetho-js-rhino/src/main/java/com/facebook/stetho/rhino/JsRuntimeRepl.java index 963a7269..4a0632ba 100644 --- a/stetho-js-rhino/src/main/java/com/facebook/stetho/rhino/JsRuntimeRepl.java +++ b/stetho-js-rhino/src/main/java/com/facebook/stetho/rhino/JsRuntimeRepl.java @@ -36,6 +36,18 @@ class JsRuntimeRepl implements RuntimeRepl { return Context.jsToJava(result, Object.class); } + @Override + public boolean assignVariable(String varName, Object value) throws Throwable { + enterJsContext(); + try { + Object jsValue = Context.javaToJS(value, mJsScope); + ScriptableObject.putProperty(mJsScope, varName, jsValue); + return true; + } finally { + Context.exit(); + } + } + /** * Setups a proper javascript context so that it can run javascript code properly under android. * For android we need to disable bytecode generation since the android vms don't understand JVM bytecode. diff --git a/stetho/src/main/java/com/facebook/stetho/Stetho.java b/stetho/src/main/java/com/facebook/stetho/Stetho.java index 120bd993..25d3145c 100644 --- a/stetho/src/main/java/com/facebook/stetho/Stetho.java +++ b/stetho/src/main/java/com/facebook/stetho/Stetho.java @@ -132,11 +132,18 @@ public static InspectorModulesProvider defaultInspectorModulesProvider(final Con @Override public Iterable get() { ArrayList modules = new ArrayList(); - modules.add(new Console()); modules.add(new CSS()); modules.add(new Debugger()); + Runtime runtime = new Runtime(); + modules.add(runtime); if (Build.VERSION.SDK_INT >= AndroidDOMConstants.MIN_API_LEVEL) { - modules.add(new DOM(new AndroidDOMProviderFactory((Application)context.getApplicationContext()))); + DOM dom = new DOM( + new AndroidDOMProviderFactory( + (Application)context.getApplicationContext())); + modules.add(dom); + modules.add(new Console(dom, runtime)); + } else { + modules.add(new Console()); } modules.add(new DOMStorage(context)); modules.add(new HeapProfiler()); @@ -144,7 +151,6 @@ public Iterable get() { modules.add(new Network(context)); modules.add(new Page(context)); modules.add(new Profiler()); - modules.add(new Runtime()); modules.add(new Worker()); if (Build.VERSION.SDK_INT >= DatabaseConstants.MIN_API_LEVEL) { modules.add(new Database(context, new DefaultDatabaseFilesProvider(context))); diff --git a/stetho/src/main/java/com/facebook/stetho/inspector/console/RuntimeRepl.java b/stetho/src/main/java/com/facebook/stetho/inspector/console/RuntimeRepl.java index cc8f9115..cd358b47 100644 --- a/stetho/src/main/java/com/facebook/stetho/inspector/console/RuntimeRepl.java +++ b/stetho/src/main/java/com/facebook/stetho/inspector/console/RuntimeRepl.java @@ -11,4 +11,15 @@ public interface RuntimeRepl { Object evaluate(String expression) throws Throwable; + + /** + * Assign the variable in the evaluation scope such that it can be accessed by subsequent + * evaluations. The value object must be strong referenced by the implementor! + + * @param varName Variable name to assign + * @param value Value of the object + * @return True if assignment is supported; false otherwise + * @throws Throwable Thrown if there is an error evaluating the variable name + */ + boolean assignVariable(String varName, Object value) throws Throwable; } diff --git a/stetho/src/main/java/com/facebook/stetho/inspector/protocol/module/Console.java b/stetho/src/main/java/com/facebook/stetho/inspector/protocol/module/Console.java index 42d8bc18..a28cb0a4 100644 --- a/stetho/src/main/java/com/facebook/stetho/inspector/protocol/module/Console.java +++ b/stetho/src/main/java/com/facebook/stetho/inspector/protocol/module/Console.java @@ -12,16 +12,40 @@ import android.annotation.SuppressLint; import com.facebook.stetho.inspector.console.ConsolePeerManager; +import com.facebook.stetho.inspector.console.RuntimeReplFactory; +import com.facebook.stetho.inspector.helper.ObjectIdMapper; +import com.facebook.stetho.inspector.jsonrpc.JsonRpcException; import com.facebook.stetho.inspector.jsonrpc.JsonRpcPeer; +import com.facebook.stetho.inspector.jsonrpc.protocol.JsonRpcError; import com.facebook.stetho.inspector.protocol.ChromeDevtoolsDomain; import com.facebook.stetho.inspector.protocol.ChromeDevtoolsMethod; +import com.facebook.stetho.json.ObjectMapper; import com.facebook.stetho.json.annotation.JsonProperty; import com.facebook.stetho.json.annotation.JsonValue; import org.json.JSONObject; +import javax.annotation.Nullable; + public class Console implements ChromeDevtoolsDomain { + @Nullable + private final ObjectIdMapper mDomObjectIdMapper; + private final RuntimeReplFactory mRuntimeReplFactory; + + private final ObjectMapper mObjectMapper = new ObjectMapper(); + + /** + * @deprecated See {@link #Console(DOM, Runtime)} + */ + @Deprecated public Console() { + mDomObjectIdMapper = null; + mRuntimeReplFactory = null; + } + + public Console(DOM dom, Runtime runtime) { + mDomObjectIdMapper = dom.getObjectIdMapper(); + mRuntimeReplFactory = runtime.getReplFactory(); } @ChromeDevtoolsMethod @@ -34,6 +58,38 @@ public void disable(JsonRpcPeer peer, JSONObject params) { ConsolePeerManager.getOrCreateInstance().removePeer(peer); } + @ChromeDevtoolsMethod + public void addInspectedNode(JsonRpcPeer peer, JSONObject params) throws JsonRpcException { + if (mDomObjectIdMapper == null) { + throw new JsonRpcException( + new JsonRpcError( + JsonRpcError.ErrorCode.INTERNAL_ERROR, + "No DOM object mapper present", + null /* data */)); + } + + AddInspectedNodeRequest request = + mObjectMapper.convertValue(params, AddInspectedNodeRequest.class); + Object object = mDomObjectIdMapper.getObjectForId(request.nodeId); + if (object == null) { + throw new JsonRpcException( + new JsonRpcError( + JsonRpcError.ErrorCode.INVALID_PARAMS, + "No known nodeId=" + request.nodeId, + null /* data */)); + } + + try { + Runtime.addInspectedNode(peer, mRuntimeReplFactory, object); + } catch (Throwable t) { + throw new JsonRpcException( + new JsonRpcError( + JsonRpcError.ErrorCode.INTERNAL_ERROR, + t.toString(), + null /* data */)); + } + } + @SuppressLint({ "UsingDefaultJsonDeserializer", "EmptyJsonPropertyUse" }) public static class MessageAddedRequest { @JsonProperty(required = true) @@ -118,4 +174,9 @@ public CallFrame(String functionName, String url, int lineNumber, int columnNumb this.columnNumber = columnNumber; } } + + private static class AddInspectedNodeRequest { + @JsonProperty(required = true) + public int nodeId; + } } diff --git a/stetho/src/main/java/com/facebook/stetho/inspector/protocol/module/DOM.java b/stetho/src/main/java/com/facebook/stetho/inspector/protocol/module/DOM.java index aab46ac5..965a9241 100644 --- a/stetho/src/main/java/com/facebook/stetho/inspector/protocol/module/DOM.java +++ b/stetho/src/main/java/com/facebook/stetho/inspector/protocol/module/DOM.java @@ -87,6 +87,10 @@ public DOM(DOMProvider.Factory providerFactory) { mResultCounter = new AtomicInteger(0); } + ObjectIdMapper getObjectIdMapper() { + return mObjectIdMapper; + } + @ChromeDevtoolsMethod public void enable(JsonRpcPeer peer, JSONObject params) { mPeerManager.addPeer(peer); diff --git a/stetho/src/main/java/com/facebook/stetho/inspector/protocol/module/Runtime.java b/stetho/src/main/java/com/facebook/stetho/inspector/protocol/module/Runtime.java index 88d5bff5..b66881ea 100644 --- a/stetho/src/main/java/com/facebook/stetho/inspector/protocol/module/Runtime.java +++ b/stetho/src/main/java/com/facebook/stetho/inspector/protocol/module/Runtime.java @@ -57,10 +57,22 @@ public Runtime(RuntimeReplFactory replFactory) { mReplFactory = replFactory; } + RuntimeReplFactory getReplFactory() { + return mReplFactory; + } + public static int mapObject(JsonRpcPeer peer, Object object) { return getSession(peer).getObjects().putObject(object); } + public static void addInspectedNode( + JsonRpcPeer peer, + RuntimeReplFactory replFactory, + Object domObject) + throws Throwable { + getSession(peer).addInspectedNode(replFactory, domObject); + } + @Nonnull private static synchronized Session getSession(final JsonRpcPeer peer) { Session session = sSessions.get(peer); @@ -237,6 +249,17 @@ public EvaluateResponse evaluate(RuntimeReplFactory replFactory, JSONObject para } } + public void addInspectedNode(RuntimeReplFactory replFactory, Object domObject) + throws Throwable { + RuntimeRepl repl = getRepl(replFactory); + boolean success = repl.assignVariable("$0", domObject); + if (!success) { + LogUtil.d("Cannot assign $0 to " + + domObject.getClass().getSimpleName() + + "{" + System.identityHashCode(domObject) + "}"); + } + } + @Nonnull private synchronized RuntimeRepl getRepl(RuntimeReplFactory replFactory) { if (mRepl == null) { @@ -568,6 +591,11 @@ public RuntimeRepl newInstance() { public Object evaluate(String expression) throws Exception { return "Not supported"; } + + @Override + public boolean assignVariable(String varName, Object value) throws Throwable { + return false; + } }; } }