Skip to content

Commit

Permalink
feat(android): support ui element module for devtools
Browse files Browse the repository at this point in the history
  • Loading branch information
MasonChanW authored and zoomchan-cxj committed Aug 12, 2021
1 parent 4e7d4ba commit 1567572
Show file tree
Hide file tree
Showing 16 changed files with 1,902 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import com.tencent.mtt.hippy.serialization.nio.reader.SafeDirectReader;
import com.tencent.mtt.hippy.serialization.nio.reader.SafeHeapReader;
import com.tencent.mtt.hippy.serialization.string.InternalizedStringTable;
import com.tencent.mtt.hippy.devsupport.inspector.Inspector;
import com.tencent.mtt.hippy.utils.UIThreadUtils;
import com.tencent.mtt.hippy.utils.UrlUtils;
import java.io.ByteArrayOutputStream;
Expand All @@ -37,6 +38,7 @@
import android.content.Context;
import android.content.res.AssetManager;
import android.text.TextUtils;

import com.tencent.mtt.hippy.common.HippyArray;
import com.tencent.mtt.hippy.devsupport.DebugWebSocketClient;
import com.tencent.mtt.hippy.devsupport.DevRemoteDebugProxy;
Expand Down Expand Up @@ -107,9 +109,9 @@ public void initJSBridge(String globalConfig, NativeCallback callback, final int
if (TextUtils.isEmpty(mDebugServerHost)) {
mDebugServerHost = "localhost:38989";
}

String clientId = mContext.getDevSupportManager().getDevInstanceUUID(); // 方便区分不同的 Hippy 调试页面
mDebugWebSocketClient.connect(
String.format(Locale.US, "ws://%s/debugger-proxy?role=android_client", mDebugServerHost),
String.format(Locale.US, "ws://%s/debugger-proxy?role=android_client&clientId=%s", mDebugServerHost, clientId),
new DebugWebSocketClient.JSDebuggerCallback() {
@SuppressWarnings("unused")
@Override
Expand Down Expand Up @@ -403,7 +405,11 @@ public void reportException(String message, String stackTrace) {
@Override
public void onReceiveData(String msg) {
if (this.mIsDevModule) {
callFunction("onWebsocketMsg", null, msg.getBytes(StandardCharsets.UTF_16LE));
boolean isInspectMsg = Inspector.getInstance(mContext)
.setWebSocketClient(mDebugWebSocketClient).dispatchReqFromFrontend(mContext, msg);
if (!isInspectMsg) {
callFunction("onWebsocketMsg", null, msg.getBytes(StandardCharsets.UTF_16LE));
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,14 @@

import com.tencent.mtt.hippy.HippyGlobalConfigs;
import com.tencent.mtt.hippy.HippyRootView;
import java.util.UUID;

@SuppressWarnings({"unused"})
public class DevSupportManager {

final DevServerInterface mDevImp;
final boolean mSupportDev;
private UUID mInstanceUUID = UUID.randomUUID();

public DevSupportManager(HippyGlobalConfigs configs, boolean enableDev, String serverHost,
String bundleName) {
Expand Down Expand Up @@ -52,5 +54,13 @@ public void handleException(Throwable throwable) {

public void loadRemoteResource(String url, DevServerCallBack serverCallBack) {
mDevImp.loadRemoteResource(url, serverCallBack);
}

public String getDevInstanceUUID() {
return mInstanceUUID.toString();
}

public boolean isSupportDev() {
return mSupportDev;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
package com.tencent.mtt.hippy.devsupport.inspector;

import android.text.TextUtils;
import com.tencent.mtt.hippy.HippyEngineContext;
import com.tencent.mtt.hippy.devsupport.DebugWebSocketClient;
import com.tencent.mtt.hippy.devsupport.inspector.domain.CSSDomain;
import com.tencent.mtt.hippy.devsupport.inspector.domain.DomDomain;
import com.tencent.mtt.hippy.devsupport.inspector.domain.InspectorDomain;
import com.tencent.mtt.hippy.devsupport.inspector.domain.PageDomain;
import com.tencent.mtt.hippy.devsupport.inspector.model.InspectEvent;
import com.tencent.mtt.hippy.dom.DomManager;
import com.tencent.mtt.hippy.dom.DomManager.BatchListener;
import com.tencent.mtt.hippy.utils.LogUtils;
import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.Map;
import org.json.JSONObject;

public class Inspector implements BatchListener {

private static final String TAG = "Inspector";

private static final String CHROME_SOCKET_CLOSED = "chrome_socket_closed";

private static Inspector sInspector;

private Map<String, InspectorDomain> mDomainMap = new HashMap<>();
private DebugWebSocketClient mDebugWebSocketClient;
private WeakReference<HippyEngineContext> mContextRef;
private boolean needBatchUpdateDom = true;

public static synchronized Inspector getInstance(HippyEngineContext context) {
if (sInspector == null) {
sInspector = new Inspector(context);
}
return sInspector;
}

private Inspector(HippyEngineContext context) {
DomDomain domDomain = new DomDomain(this);
CSSDomain cssDomain = new CSSDomain(this);
PageDomain pageDomain = new PageDomain(this);
mDomainMap.put(domDomain.getDomainName(), domDomain);
mDomainMap.put(cssDomain.getDomainName(), cssDomain);
mDomainMap.put(pageDomain.getDomainName(), pageDomain);
DomManager domManager = context.getDomManager();
if (domManager != null) {
domManager.setOnBatchListener(this);
}
}

public Inspector setWebSocketClient(DebugWebSocketClient client) {
mDebugWebSocketClient = client;
return this;
}

public boolean dispatchReqFromFrontend(HippyEngineContext context, String msg) {
if (TextUtils.isEmpty(msg)) {
LogUtils.e(TAG, "dispatchReqFromFrontend, msg null");
return false;
}

LogUtils.d(TAG, "dispatchReqFromFrontend, msg=" + msg);

if (CHROME_SOCKET_CLOSED.equals(msg)) {
onFrontendClosed(context);
return false;
}

try {
JSONObject msgObj = new JSONObject(msg);
String methodParam = msgObj.optString("method");
if (!TextUtils.isEmpty(methodParam)) {
String[] methodParamArray = methodParam.split("\\.");
if (methodParamArray.length > 1) {
String domain = methodParamArray[0];
if (!TextUtils.isEmpty(domain) && mDomainMap.containsKey(domain)) {
InspectorDomain inspectorDomain = mDomainMap.get(domain);
if (inspectorDomain != null) {
String method = methodParamArray[1];
int id = msgObj.optInt("id");
JSONObject paramsObj = msgObj.optJSONObject("params");
return inspectorDomain.handleRequestFromBackend(context, method, id, paramsObj);
}
}
}
}
} catch (Exception e) {
LogUtils.e(TAG, "dispatchReqFromFrontend, exception:", e);
}
return false;
}

private void onFrontendClosed(HippyEngineContext context) {
for (Map.Entry<String, InspectorDomain> entry : mDomainMap.entrySet()) {
entry.getValue().onFrontendClosed(context);
}
}

public void rspToFrontend(int id, JSONObject result) {
if (mDebugWebSocketClient == null) {
return;
}
try {
JSONObject resultObj = new JSONObject();
resultObj.put("id", id);
resultObj.put("result", result != null ? result : new JSONObject());
LogUtils.d(TAG, "rspToFrontend, msg=" + resultObj.toString());
mDebugWebSocketClient.sendMessage(resultObj.toString());
} catch (Exception e) {
LogUtils.e(TAG, "rspToFrontEnd, exception:", e);
}
}

public void sendEventToFrontend(InspectEvent event) {
String eventJson = event.toJson();
if (mDebugWebSocketClient == null || eventJson == null) {
return;
}

LogUtils.d(TAG, "sendEventToFrontend, eventJson=" + eventJson);
mDebugWebSocketClient.sendMessage(eventJson);
}

public void setNeedBatchUpdateDom(boolean needBatchUpdate) {
needBatchUpdateDom = needBatchUpdate;
}

@Override
public void onBatch(boolean isAnimation) {
if (needBatchUpdateDom && !isAnimation) {
DomDomain domDomain = (DomDomain) mDomainMap.get(DomDomain.DOM_DOMAIN_NAME);
domDomain.sendUpdateEvent();
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package com.tencent.mtt.hippy.devsupport.inspector.domain;

import com.tencent.mtt.hippy.HippyEngineContext;
import com.tencent.mtt.hippy.devsupport.inspector.Inspector;
import com.tencent.mtt.hippy.devsupport.inspector.model.CSSModel;
import org.json.JSONArray;
import org.json.JSONObject;

public class CSSDomain extends InspectorDomain {

private static final String TAG = "CSSDomain";

private static final String METHOD_GET_MATCHED_STYLES_FOR_NODE = "getMatchedStylesForNode";
private static final String METHOD_GET_COMPUTED_STYLE_FOR_NODE = "getComputedStyleForNode";
private static final String METHOD_GET_INLINE_STYLES_FOR_NODE = "getInlineStylesForNode";
private static final String METHOD_SET_STYLE_TEXTS = "setStyleTexts";

private CSSModel cssModel;

public CSSDomain(Inspector inspector) {
super(inspector);
cssModel = new CSSModel();
}

@Override
public String getDomainName() {
return "CSS";
}

@Override
public boolean handleRequest(HippyEngineContext context, String method, int id,
JSONObject paramsObj) {
switch (method) {
case METHOD_GET_MATCHED_STYLES_FOR_NODE:
handleGetMatchedStyles(context, id, paramsObj);
break;
case METHOD_GET_COMPUTED_STYLE_FOR_NODE:
handleGetComputedStyle(context, id, paramsObj);
break;
case METHOD_GET_INLINE_STYLES_FOR_NODE:
handleGetInlineStyles(context, id, paramsObj);
break;
case METHOD_SET_STYLE_TEXTS:
handleSetStyleTexts(context, id, paramsObj);
break;
default:
return false;
}
return true;
}

private void handleGetMatchedStyles(HippyEngineContext context, int id, JSONObject paramsObj) {
int nodeId = paramsObj.optInt("nodeId");
JSONObject matchedStyles = cssModel.getMatchedStyles(context, nodeId);
sendRspToFrontend(id, matchedStyles);
}

private void handleGetComputedStyle(HippyEngineContext context, int id, JSONObject paramsObj) {
int nodeId = paramsObj.optInt("nodeId");
JSONObject computedStyle = cssModel.getComputedStyle(context, nodeId);
sendRspToFrontend(id, computedStyle);
}

private void handleGetInlineStyles(HippyEngineContext context, int id, JSONObject paramsObj) {
int nodeId = paramsObj.optInt("nodeId");
JSONObject inlineStyles = cssModel.getInlineStyles(context, nodeId);
sendRspToFrontend(id, inlineStyles);
}


private void handleSetStyleTexts(HippyEngineContext context, int id, JSONObject paramsObj) {
JSONArray editArray = paramsObj.optJSONArray("edits");
JSONObject styleTexts = cssModel.setStyleTexts(context, editArray, this);
sendRspToFrontend(id, styleTexts);
}

public void setNeedBatchUpdateDom(boolean needBatchUpdate) {
if (mInspectorRef == null) {
return;
}
mInspectorRef.get().setNeedBatchUpdateDom(needBatchUpdate);
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package com.tencent.mtt.hippy.devsupport.inspector.domain;

import com.tencent.mtt.hippy.HippyEngineContext;
import com.tencent.mtt.hippy.devsupport.inspector.Inspector;
import com.tencent.mtt.hippy.devsupport.inspector.model.DomModel;
import com.tencent.mtt.hippy.devsupport.inspector.model.InspectEvent;
import org.json.JSONObject;

public class DomDomain extends InspectorDomain {

private static final String TAG = "DomDomain";
public static final String DOM_DOMAIN_NAME = "DOM";

private static final String METHOD_GET_DOCUMENT = "getDocument";
private static final String METHOD_GET_BOX_MODEL = "getBoxModel";
private static final String METHOD_GET_NODE_FOR_LOCATION = "getNodeForLocation";
private static final String METHOD_REMOVE_NODE = "removeNode";
private static final String METHOD_SET_INSPECT_NODE = "setInspectedNode";

private DomModel domModel;

public DomDomain(Inspector inspector) {
super(inspector);
domModel = new DomModel();
}

@Override
public String getDomainName() {
return DOM_DOMAIN_NAME;
}

@Override
public boolean handleRequest(HippyEngineContext context, String method, int id,
JSONObject paramsObj) {
switch (method) {
case METHOD_GET_DOCUMENT:
handleGetDocument(context, id);
break;
case METHOD_GET_BOX_MODEL:
handleGetBoxModel(context, id, paramsObj);
break;
case METHOD_GET_NODE_FOR_LOCATION:
handleGetNodeForLocation(context, id, paramsObj);
break;
case METHOD_SET_INSPECT_NODE:
handleSetInspectMode(context, id, paramsObj);
break;
default:
return false;
}
return true;
}

private void handleGetDocument(HippyEngineContext context, int id) {
JSONObject result = domModel.getDocument(context);
sendRspToFrontend(id, result);
}

private void handleGetBoxModel(HippyEngineContext context, int id, JSONObject paramsObj) {
JSONObject result = domModel.getBoxModel(context, paramsObj);
sendRspToFrontend(id, result);
}

private void handleGetNodeForLocation(HippyEngineContext context, int id, JSONObject paramsObj) {
JSONObject result = domModel.getNodeForLocation(context, paramsObj);
sendRspToFrontend(id, result);
}

private void handleSetInspectMode(HippyEngineContext context, int id, JSONObject paramsObj) {
JSONObject result = domModel.setInspectMode(context, paramsObj);
sendRspToFrontend(id, result);
}

public void sendUpdateEvent() {
InspectEvent updateEvent = new InspectEvent("DOM.documentUpdated", new JSONObject());
sendEventToFrontend(updateEvent);
}
}
Loading

0 comments on commit 1567572

Please sign in to comment.