@Override public void onPeerRegistered(JsonRpcPeer jsonRpcPeer) { for (File dir : dirs) { if (!dir.isDirectory() || !dir.canRead()) { continue; } for (File file : dir.listFiles()) { if (!file.isFile() || !file.canRead() || !namePattern.matcher(file.getName()) .matches()) { continue; } final DatabaseObject databaseParams = new DatabaseObject(); databaseParams.id = file.getAbsolutePath(); databaseParams.name = file.getName(); databaseParams.domain = packageName; databaseParams.version = "N/A"; final AddDatabaseEvent eventParams = new AddDatabaseEvent(); eventParams.database = databaseParams; jsonRpcPeer.invokeMethod("Database.addDatabase", eventParams, null); } } }
@ChromeDevtoolsMethod @SuppressWarnings("unused") public JsonRpcResult executeSQL(JsonRpcPeer peer, JSONObject params) { Timber.d("executeSQL: %s", String.valueOf(params)); ExecuteSQLResponse response = new ExecuteSQLResponse(); try { ExecuteSQLRequest request = mObjectMapper.convertValue(params, ExecuteSQLRequest.class); return mCouchbasePeerManager.executeSQL(request.databaseId, request.query); } catch (Exception e) { Timber.e(e.toString()); Error error = new Error(); error.code = 0; error.message = e.getMessage(); response.sqlError = error; return response; } }
private void sendWelcomeMessage(JsonRpcPeer peer) { Console.ConsoleMessage message = new Console.ConsoleMessage(); message.source = Console.MessageSource.JAVASCRIPT; message.level = Console.MessageLevel.LOG; message.text = // Note: not using Android resources so we can maintain .jar distribution for now. "_____/\\\\\\\\\\\\\\\\\\\\\\_______________________________________________/\\\\\\_______________________\n" + " ___/\\\\\\/////////\\\\\\____________________________________________\\/\\\\\\_______________________\n" + " __\\//\\\\\\______\\///______/\\\\\\_________________________/\\\\\\______\\/\\\\\\_______________________\n" + " ___\\////\\\\\\__________/\\\\\\\\\\\\\\\\\\\\\\_____/\\\\\\\\\\\\\\\\___/\\\\\\\\\\\\\\\\\\\\\\_\\/\\\\\\_____________/\\\\\\\\\\____\n" + " ______\\////\\\\\\______\\////\\\\\\////____/\\\\\\/////\\\\\\_\\////\\\\\\////__\\/\\\\\\\\\\\\\\\\\\\\____/\\\\\\///\\\\\\__\n" + " _________\\////\\\\\\______\\/\\\\\\_______/\\\\\\\\\\\\\\\\\\\\\\_____\\/\\\\\\______\\/\\\\\\/////\\\\\\__/\\\\\\__\\//\\\\\\_\n" + " __/\\\\\\______\\//\\\\\\_____\\/\\\\\\_/\\\\__\\//\\\\///////______\\/\\\\\\_/\\\\__\\/\\\\\\___\\/\\\\\\_\\//\\\\\\__/\\\\\\__\n" + " _\\///\\\\\\\\\\\\\\\\\\\\\\/______\\//\\\\\\\\\\____\\//\\\\\\\\\\\\\\\\\\\\____\\//\\\\\\\\\\___\\/\\\\\\___\\/\\\\\\__\\///\\\\\\\\\\/___\n" + " ___\\///////////_________\\/////______\\//////////______\\/////____\\///____\\///_____\\/////_____\n" + " Welcome to Stetho\n" + " Attached to " + ProcessUtil.getProcessName() + "\n"; Console.MessageAddedRequest messageAddedRequest = new Console.MessageAddedRequest(); messageAddedRequest.message = message; peer.invokeMethod("Console.messageAdded", messageAddedRequest, null /* callback */); }
@ChromeDevtoolsMethod public void highlightNode(JsonRpcPeer peer, JSONObject params) { final HighlightNodeRequest request = mObjectMapper.convertValue(params, HighlightNodeRequest.class); if (request.nodeId == null) { LogUtil.w("DOM.highlightNode was not given a nodeId; JS objectId is not supported"); return; } final RGBAColor contentColor = request.highlightConfig.contentColor; if (contentColor == null) { LogUtil.w("DOM.highlightNode was not given a color to highlight with"); return; } mDocument.postAndWait(new Runnable() { @Override public void run() { Object element = mDocument.getElementForNodeId(request.nodeId); if (element != null) { mDocument.highlightElement(element, contentColor.getColor()); } } }); }
@ChromeDevtoolsMethod public void setAttributesAsText(JsonRpcPeer peer, JSONObject params) { final SetAttributesAsTextRequest request = mObjectMapper.convertValue( params, SetAttributesAsTextRequest.class); mDocument.postAndWait(new Runnable() { @Override public void run() { Object element = mDocument.getElementForNodeId(request.nodeId); if (element != null) { mDocument.setAttributesAsText(element, request.text); } } }); }
@ChromeDevtoolsMethod public GetSearchResultsResponse getSearchResults(JsonRpcPeer peer, JSONObject params) { final GetSearchResultsRequest request = mObjectMapper.convertValue( params, GetSearchResultsRequest.class); if (request.searchId == null) { LogUtil.w("searchId may not be null"); return null; } final List<Integer> results = mSearchResults.get(request.searchId); if (results == null) { LogUtil.w("\"" + request.searchId + "\" is not a valid reference to a search result"); return null; } final List<Integer> resultsRange = results.subList(request.fromIndex, request.toIndex); final GetSearchResultsResponse response = new GetSearchResultsResponse(); response.nodeIds = resultsRange; return response; }
@ChromeDevtoolsMethod public JsonRpcResult getDatabaseTableNames(JsonRpcPeer peer, JSONObject params) throws JsonRpcException { GetDatabaseTableNamesRequest request = mObjectMapper.convertValue(params, GetDatabaseTableNamesRequest.class); String databaseId = request.databaseId; DatabaseDescriptorHolder holder = mPeerListener.getDatabaseDescriptorHolder(databaseId); try { GetDatabaseTableNamesResponse response = new GetDatabaseTableNamesResponse(); response.tableNames = holder.driver.getTableNames(holder.descriptor); return response; } catch (SQLiteException e) { throw new JsonRpcException( new JsonRpcError( JsonRpcError.ErrorCode.INVALID_REQUEST, e.toString(), null /* data */)); } }
@Override protected synchronized void onPeerAdded(JsonRpcPeer peer) { for (int i = 0, N = mDatabaseHolders.size(); i < N; i++) { int id = mDatabaseHolders.keyAt(i); DatabaseDescriptorHolder holder = mDatabaseHolders.valueAt(i); Database.DatabaseObject databaseParams = new Database.DatabaseObject(); databaseParams.id = String.valueOf(id); databaseParams.name = holder.descriptor.name(); databaseParams.domain = holder.driver.getContext().getPackageName(); databaseParams.version = "N/A"; Database.AddDatabaseEvent eventParams = new Database.AddDatabaseEvent(); eventParams.database = databaseParams; peer.invokeMethod("Database.addDatabase", eventParams, null /* callback */); } }
@ChromeDevtoolsMethod public JsonRpcResult getDOMStorageItems(JsonRpcPeer peer, JSONObject params) throws JSONException { StorageId storage = mObjectMapper.convertValue( params.getJSONObject("storageId"), StorageId.class); ArrayList<List<String>> entries = new ArrayList<List<String>>(); String prefTag = storage.securityOrigin; if (storage.isLocalStorage) { SharedPreferences prefs = mContext.getSharedPreferences(prefTag, Context.MODE_PRIVATE); for (Map.Entry<String, ?> prefsEntry : prefs.getAll().entrySet()) { ArrayList<String> entry = new ArrayList<String>(2); entry.add(prefsEntry.getKey()); entry.add(SharedPreferencesHelper.valueToString(prefsEntry.getValue())); entries.add(entry); } } GetDOMStorageItemsResult result = new GetDOMStorageItemsResult(); result.entries = entries; return result; }
@ChromeDevtoolsMethod public JsonRpcResult getDatabaseTableNames(JsonRpcPeer peer, JSONObject params) { final SharedRealm realm = getRealm( objectMapper.convertValue(params, GetDatabaseTableNamesRequest.class).databaseId); final int size = (int) realm.size(); final List<String> tableNames = new ArrayList<>(size); for (int i = 0; i < size; ++i) { tableNames.add(realm.getTableName(i)); } final GetDatabaseTableNamesResponse response = new GetDatabaseTableNamesResponse(); response.tableNames = tableNames; return response; }
@Override public void onPeerUnregistered(JsonRpcPeer jsonRpcPeer) { for (Map.Entry<String, SharedRealm> entry : realms.entrySet()) { entry.getValue().close(); } realms.clear(); }
private void bootstrapNewPeer(JsonRpcPeer peer) { List<File> potentialDatabaseFiles = realmFilesProvider.getDatabaseFiles(); Iterable<File> tidiedList = tidyDatabaseList(potentialDatabaseFiles); for (File database : tidiedList) { Database.DatabaseObject databaseParams = new Database.DatabaseObject(); databaseParams.id = database.getPath(); databaseParams.name = database.getName(); databaseParams.domain = packageName; databaseParams.version = "N/A"; Database.AddDatabaseEvent eventParams = new Database.AddDatabaseEvent(); eventParams.database = databaseParams; peer.invokeMethod("Database.addDatabase", eventParams, null /* callback */); } }
@ChromeDevtoolsMethod @SuppressWarnings("unused") public JsonRpcResult getDatabaseTableNames(JsonRpcPeer peer, JSONObject params) { GetDatabaseTableNamesRequest request = objectMapper.convertValue(params, GetDatabaseTableNamesRequest.class); GetDatabaseTableNamesResponse response = new GetDatabaseTableNamesResponse(); response.tableNames = realmPeerManager.getDatabaseTableNames(request.databaseId, withMetaTables); return response; }
private void setupPeer(JsonRpcPeer peer) { List<String> potentialDatabases = mManager.getAllDatabaseNames(); for (String database : potentialDatabases) { Timber.d("Datebase Name: %s", database); Database.DatabaseObject databaseParams = new Database.DatabaseObject(); databaseParams.id = database; databaseParams.name = database; databaseParams.domain = mPackageName; databaseParams.version = "N/A"; Database.AddDatabaseEvent eventParams = new Database.AddDatabaseEvent(); eventParams.database = databaseParams; peer.invokeMethod("Database.addDatabase", eventParams, null /* callback */); } }
@ChromeDevtoolsMethod @SuppressWarnings("unused") public JsonRpcResult getDatabaseTableNames(JsonRpcPeer peer, JSONObject params) { Timber.d("getAllDocumentIds: %s", String.valueOf(params)); GetDatabaseTableNamesRequest request = mObjectMapper.convertValue(params, GetDatabaseTableNamesRequest.class); GetDatabaseTableNamesResponse response = new GetDatabaseTableNamesResponse(); response.tableNames = mCouchbasePeerManager.getAllDocumentIds(request.databaseId); return response; }
private void notifyExecutionContexts(JsonRpcPeer peer) { ExecutionContextDescription context = new ExecutionContextDescription(); context.frameId = "1"; context.id = 1; ExecutionContextCreatedParams params = new ExecutionContextCreatedParams(); params.context = context; peer.invokeMethod("Runtime.executionContextCreated", params, null /* callback */); }
@ChromeDevtoolsMethod public JsonRpcResult getResourceTree(JsonRpcPeer peer, JSONObject params) { // The DOMStorage module expects one key/value store per "security origin" which has a 1:1 // relationship with resource tree frames. List<String> prefsTags = SharedPreferencesHelper.getSharedPreferenceTags(mContext); Iterator<String> prefsTagsIter = prefsTags.iterator(); FrameResourceTree tree = createSimpleFrameResourceTree( "1", null /* parentId */, "Stetho", prefsTagsIter.hasNext() ? prefsTagsIter.next() : ""); if (tree.childFrames == null) { tree.childFrames = new ArrayList<FrameResourceTree>(); } int nextChildFrameId = 1; while (prefsTagsIter.hasNext()) { String frameId = "1." + (nextChildFrameId++); String prefsTag = prefsTagsIter.next(); FrameResourceTree child = createSimpleFrameResourceTree( frameId, "1", "Child #" + frameId, prefsTag); tree.childFrames.add(child); } GetResourceTreeParams resultParams = new GetResourceTreeParams(); resultParams.frameTree = tree; return resultParams; }
@ChromeDevtoolsMethod public void startScreencast(final JsonRpcPeer peer, JSONObject params) { final StartScreencastRequest request = mObjectMapper.convertValue( params, StartScreencastRequest.class); if (mScreencastDispatcher == null) { mScreencastDispatcher = new ScreencastDispatcher(); mScreencastDispatcher.startScreencast(peer, request); } }
@ChromeDevtoolsMethod public void stopScreencast(JsonRpcPeer peer, JSONObject params) { if (mScreencastDispatcher != null) { mScreencastDispatcher.stopScreencast(); mScreencastDispatcher = null; } }
@ChromeDevtoolsMethod public JsonRpcResult getDocument(JsonRpcPeer peer, JSONObject params) { final GetDocumentResponse result = new GetDocumentResponse(); result.root = mDocument.postAndWait(new UncheckedCallable<Node>() { @Override public Node call() { Object element = mDocument.getRootElement(); return createNodeForElement(element, mDocument.getDocumentView(), null); } }); return result; }
@ChromeDevtoolsMethod public void hideHighlight(JsonRpcPeer peer, JSONObject params) { mDocument.postAndWait(new Runnable() { @Override public void run() { mDocument.hideHighlight(); } }); }
@ChromeDevtoolsMethod public ResolveNodeResponse resolveNode(JsonRpcPeer peer, JSONObject params) throws JsonRpcException { final ResolveNodeRequest request = mObjectMapper.convertValue(params, ResolveNodeRequest.class); final Object element = mDocument.postAndWait(new UncheckedCallable<Object>() { @Override public Object call() { return mDocument.getElementForNodeId(request.nodeId); } }); if (element == null) { throw new JsonRpcException( new JsonRpcError( JsonRpcError.ErrorCode.INVALID_PARAMS, "No known nodeId=" + request.nodeId, null /* data */)); } int mappedObjectId = Runtime.mapObject(peer, element); Runtime.RemoteObject remoteObject = new Runtime.RemoteObject(); remoteObject.type = Runtime.ObjectType.OBJECT; remoteObject.subtype = Runtime.ObjectSubType.NODE; remoteObject.className = element.getClass().getName(); remoteObject.value = null; // not a primitive remoteObject.description = null; // not sure what this does... remoteObject.objectId = String.valueOf(mappedObjectId); ResolveNodeResponse response = new ResolveNodeResponse(); response.object = remoteObject; return response; }
@ChromeDevtoolsMethod public void setInspectModeEnabled(JsonRpcPeer peer, JSONObject params) { final SetInspectModeEnabledRequest request = mObjectMapper.convertValue( params, SetInspectModeEnabledRequest.class); mDocument.postAndWait(new Runnable() { @Override public void run() { mDocument.setInspectModeEnabled(request.enabled); } }); }
@ChromeDevtoolsMethod public PerformSearchResponse performSearch(JsonRpcPeer peer, final JSONObject params) { final PerformSearchRequest request = mObjectMapper.convertValue( params, PerformSearchRequest.class); final ArrayListAccumulator<Integer> resultNodeIds = new ArrayListAccumulator<>(); mDocument.postAndWait(new Runnable() { @Override public void run() { mDocument.findMatchingElements(request.query, resultNodeIds); } }); // Each search action has a unique ID so that // it can be queried later. final String searchId = String.valueOf(mResultCounter.getAndIncrement()); mSearchResults.put(searchId, resultNodeIds); final PerformSearchResponse response = new PerformSearchResponse(); response.searchId = searchId; response.resultCount = resultNodeIds.size(); return response; }
@ChromeDevtoolsMethod public void discardSearchResults(JsonRpcPeer peer, JSONObject params) { final DiscardSearchResultsRequest request = mObjectMapper.convertValue( params, DiscardSearchResultsRequest.class); if (request.searchId != null) { mSearchResults.remove(request.searchId); } }
@ChromeDevtoolsMethod public void removeDOMStorageItem(JsonRpcPeer peer, JSONObject params) throws JSONException { StorageId storage = mObjectMapper.convertValue( params.getJSONObject("storageId"), StorageId.class); String key = params.getString("key"); if (storage.isLocalStorage) { SharedPreferences prefs = mContext.getSharedPreferences( storage.securityOrigin, Context.MODE_PRIVATE); prefs.edit().remove(key).apply(); } }
@ChromeDevtoolsMethod public JsonRpcResult getComputedStyleForNode(JsonRpcPeer peer, JSONObject params) { final GetComputedStyleForNodeRequest request = mObjectMapper.convertValue( params, GetComputedStyleForNodeRequest.class); final GetComputedStyleForNodeResult result = new GetComputedStyleForNodeResult(); result.computedStyle = new ArrayList<>(); mDocument.postAndWait(new Runnable() { @Override public void run() { Object element = mDocument.getElementForNodeId(request.nodeId); if (element == null) { LogUtil.e("Tried to get the style of an element that does not exist, using nodeid=" + request.nodeId); return; } mDocument.getElementComputedStyles( element, new ComputedStyleAccumulator() { @Override public void store(String name, String value) { final CSSComputedStyleProperty property = new CSSComputedStyleProperty(); property.name = name; property.value = value; result.computedStyle.add(property); } }); } }); return result; }
@Nonnull private static synchronized Session getSession(final JsonRpcPeer peer) { Session session = sSessions.get(peer); if (session == null) { session = new Session(); sSessions.put(peer, session); peer.registerDisconnectReceiver(new DisconnectReceiver() { @Override public void onDisconnect() { sSessions.remove(peer); } }); } return session; }
@ChromeDevtoolsMethod public CallFunctionOnResponse callFunctionOn(JsonRpcPeer peer, JSONObject params) throws JsonRpcException { CallFunctionOnRequest args = mObjectMapper.convertValue(params, CallFunctionOnRequest.class); Session session = getSession(peer); Object object = session.getObjectOrThrow(args.objectId); // The DevTools UI thinks it can run arbitrary JavaScript against us in order to figure out // the class structure of an object. That obviously won't fly, and there's no way to // translate without building a crude JavaScript parser so let's just go ahead and guess // what this function does by name. if (!args.functionDeclaration.startsWith("function protoList(")) { throw new JsonRpcException( new JsonRpcError( JsonRpcError.ErrorCode.INTERNAL_ERROR, "Expected protoList, got: " + args.functionDeclaration, null /* data */)); } // Since this is really a function call we have to create this fake object to hold the // "result" of the function. ObjectProtoContainer objectContainer = new ObjectProtoContainer(object); RemoteObject result = new RemoteObject(); result.type = ObjectType.OBJECT; result.subtype = ObjectSubType.NODE; result.className = object.getClass().getName(); result.description = getPropertyClassName(object); result.objectId = String.valueOf(session.getObjects().putObject(objectContainer)); CallFunctionOnResponse response = new CallFunctionOnResponse(); response.result = result; response.wasThrown = false; return response; }
/** * Register a new peer, adding them to an internal list of receivers. * * @param peer * @return True if this is a newly registered peer; false if it was already registered. */ public synchronized boolean addPeer(JsonRpcPeer peer) { if (mReceivingPeers.containsKey(peer)) { return false; } DisconnectReceiver disconnectReceiver = new UnregisterOnDisconnect(peer); peer.registerDisconnectReceiver(disconnectReceiver); mReceivingPeers.put(peer, disconnectReceiver); mReceivingPeersSnapshot = null; if (mListener != null) { mListener.onPeerRegistered(peer); } return true; }
/** * Unregister an existing peer. * * @param peer */ public synchronized void removePeer(JsonRpcPeer peer) { if (mReceivingPeers.remove(peer) != null) { mReceivingPeersSnapshot = null; if (mListener != null) { mListener.onPeerUnregistered(peer); } } }
private synchronized JsonRpcPeer[] getReceivingPeersSnapshot() { if (mReceivingPeersSnapshot == null) { mReceivingPeersSnapshot = mReceivingPeers.keySet().toArray( new JsonRpcPeer[mReceivingPeers.size()]); } return mReceivingPeersSnapshot; }
private void sendMessageToPeers(String method, Object params, @Nullable PendingRequestCallback callback) { JsonRpcPeer[] peers = getReceivingPeersSnapshot(); for (JsonRpcPeer peer : peers) { try { peer.invokeMethod(method, params, callback); } catch (NotYetConnectedException e) { LogRedirector.e(TAG, "Error delivering data to Chrome", e); } } }
@Override public final void onPeerRegistered(JsonRpcPeer peer) { if (mPeers.incrementAndGet() == 1) { onFirstPeerRegistered(); } onPeerAdded(peer); }
@Override public final void onPeerUnregistered(JsonRpcPeer peer) { if (mPeers.decrementAndGet() == 0) { onLastPeerUnregistered(); } onPeerRemoved(peer); }
public JSONObject invoke(JsonRpcPeer peer, @Nullable JSONObject params) throws InvocationTargetException, IllegalAccessException, JSONException, JsonRpcException { Object internalResult = mMethod.invoke(mInstance, peer, params); if (internalResult == null || internalResult instanceof EmptyResult) { return new JSONObject(); } else { JsonRpcResult convertableResult = (JsonRpcResult)internalResult; return mObjectMapper.convertValue(convertableResult, JSONObject.class); } }
/** * Determines if the method is a {@link ChromeDevtoolsMethod}, and validates accordingly * if it is. * * @throws IllegalArgumentException Thrown if it is a {@link ChromeDevtoolsMethod} but * it otherwise fails to satisfy requirements. */ private static boolean isDevtoolsMethod(Method method) throws IllegalArgumentException { if (!method.isAnnotationPresent(ChromeDevtoolsMethod.class)) { return false; } else { Class<?> args[] = method.getParameterTypes(); String methodName = method.getDeclaringClass().getSimpleName() + "." + method.getName(); Util.throwIfNot(args.length == 2, "%s: expected 2 args, got %s", methodName, args.length); Util.throwIfNot(args[0].equals(JsonRpcPeer.class), "%s: expected 1st arg of JsonRpcPeer, got %s", methodName, args[0].getName()); Util.throwIfNot(args[1].equals(JSONObject.class), "%s: expected 2nd arg of JSONObject, got %s", methodName, args[1].getName()); Class<?> returnType = method.getReturnType(); if (!returnType.equals(void.class)) { Util.throwIfNot(JsonRpcResult.class.isAssignableFrom(returnType), "%s: expected JsonRpcResult return type, got %s", methodName, returnType.getName()); } return true; } }
public void startScreencast(JsonRpcPeer peer, Page.StartScreencastRequest request) { LogUtil.d("Starting screencast"); mRequest = request; mHandlerThread = new HandlerThread("Screencast Thread"); mHandlerThread.start(); mPeer = peer; mIsRunning = true; mStream = new ByteArrayOutputStream(); mBackgroundHandler = new Handler(mHandlerThread.getLooper()); mMainHandler.postDelayed(mBitmapFetchRunnable, FRAME_DELAY); }
@Override public void onClose(SimpleSession session, int code, String reasonPhrase) { LogRedirector.d(TAG, "onClose: reason=" + code + " " + reasonPhrase); JsonRpcPeer peer = mPeers.remove(session); if (peer != null) { peer.invokeDisconnectReceivers(); } }
private void handleRemoteMessage(JsonRpcPeer peer, String message) throws IOException, MessageHandlingException, JSONException { // Parse as a generic JSONObject first since we don't know if this is a request or response. JSONObject messageNode = new JSONObject(message); if (messageNode.has("method")) { handleRemoteRequest(peer, messageNode); } else if (messageNode.has("result")) { handleRemoteResponse(peer, messageNode); } else { throw new MessageHandlingException("Improper JSON-RPC message: " + message); } }