/*
* Copyright (c) 2014-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
package com.facebook.stetho.inspector.protocol.module;
import android.content.Context;
import android.support.annotation.Nullable;
import com.facebook.stetho.common.ProcessUtil;
import com.facebook.stetho.inspector.domstorage.SharedPreferencesHelper;
import com.facebook.stetho.inspector.jsonrpc.JsonRpcPeer;
import com.facebook.stetho.inspector.jsonrpc.JsonRpcResult;
import com.facebook.stetho.inspector.protocol.ChromeDevtoolsDomain;
import com.facebook.stetho.inspector.protocol.ChromeDevtoolsMethod;
import com.facebook.stetho.inspector.screencast.ScreencastDispatcher;
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 java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
public class Page implements ChromeDevtoolsDomain {
private final Context mContext;
private final ObjectMapper mObjectMapper = new ObjectMapper();
@Nullable
private ScreencastDispatcher mScreencastDispatcher;
public Page(Context context) {
mContext = context;
}
@ChromeDevtoolsMethod
public void enable(JsonRpcPeer peer, JSONObject params) {
notifyExecutionContexts(peer);
sendWelcomeMessage(peer);
}
@ChromeDevtoolsMethod
public void disable(JsonRpcPeer peer, JSONObject params) {
}
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 */);
}
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 */);
}
// Dog science...
@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;
}
private static FrameResourceTree createSimpleFrameResourceTree(
String id,
String parentId,
String name,
String securityOrigin) {
Frame frame = new Frame();
frame.id = id;
frame.parentId = parentId;
frame.loaderId = "1";
frame.name = name;
frame.url = "";
frame.securityOrigin = securityOrigin;
frame.mimeType = "text/plain";
FrameResourceTree tree = new FrameResourceTree();
tree.frame = frame;
tree.resources = Collections.emptyList();
tree.childFrames = null;
return tree;
}
@ChromeDevtoolsMethod
public JsonRpcResult canScreencast(JsonRpcPeer peer, JSONObject params) {
return new SimpleBooleanResult(true);
}
@ChromeDevtoolsMethod
public JsonRpcResult hasTouchInputs(JsonRpcPeer peer, JSONObject params) {
return new SimpleBooleanResult(false);
}
@ChromeDevtoolsMethod
public void setDeviceMetricsOverride(JsonRpcPeer peer, JSONObject params) {
}
@ChromeDevtoolsMethod
public void clearDeviceOrientationOverride(JsonRpcPeer peer, JSONObject params) {
}
@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 void screencastFrameAck(JsonRpcPeer peer, JSONObject params) {
// Nothing to do here, just need to make sure Chrome doesn't get an error that this method
// isn't implemented
}
@ChromeDevtoolsMethod
public void clearGeolocationOverride(JsonRpcPeer peer, JSONObject params) {
}
@ChromeDevtoolsMethod
public void setTouchEmulationEnabled(JsonRpcPeer peer, JSONObject params) {
}
@ChromeDevtoolsMethod
public void setEmulatedMedia(JsonRpcPeer peer, JSONObject params) {
}
@ChromeDevtoolsMethod
public void setShowViewportSizeOnResize(JsonRpcPeer peer, JSONObject params) {
}
private static class GetResourceTreeParams implements JsonRpcResult {
@JsonProperty(required = true)
public FrameResourceTree frameTree;
}
private static class FrameResourceTree {
@JsonProperty(required = true)
public Frame frame;
@JsonProperty
public List<FrameResourceTree> childFrames;
@JsonProperty(required = true)
public List<Resource> resources;
}
private static class Frame {
@JsonProperty(required = true)
public String id;
@JsonProperty
public String parentId;
@JsonProperty(required = true)
public String loaderId;
@JsonProperty
public String name;
@JsonProperty(required = true)
public String url;
@JsonProperty(required = true)
public String securityOrigin;
@JsonProperty(required = true)
public String mimeType;
}
private static class Resource {
// Incomplete...
}
public enum ResourceType {
DOCUMENT("Document"),
STYLESHEET("Stylesheet"),
IMAGE("Image"),
FONT("Font"),
SCRIPT("Script"),
XHR("XHR"),
WEBSOCKET("WebSocket"),
OTHER("Other");
private final String mProtocolValue;
private ResourceType(String protocolValue) {
mProtocolValue = protocolValue;
}
@JsonValue
public String getProtocolValue() {
return mProtocolValue;
}
}
private static class ExecutionContextCreatedParams {
@JsonProperty(required = true)
public ExecutionContextDescription context;
}
private static class ExecutionContextDescription {
@JsonProperty(required = true)
public String frameId;
@JsonProperty(required = true)
public int id;
}
public static class ScreencastFrameEvent {
@JsonProperty(required = true)
public String data;
@JsonProperty(required = true)
public ScreencastFrameEventMetadata metadata;
}
public static class ScreencastFrameEventMetadata {
@JsonProperty(required = true)
public int pageScaleFactor;
@JsonProperty(required = true)
public int offsetTop;
@JsonProperty(required = true)
public int deviceWidth;
@JsonProperty(required = true)
public int deviceHeight;
@JsonProperty(required = true)
public int scrollOffsetX;
@JsonProperty(required = true)
public int scrollOffsetY;
}
public static class StartScreencastRequest {
@JsonProperty
public String format;
@JsonProperty
public int quality;
@JsonProperty
public int maxWidth;
@JsonProperty
public int maxHeight;
}
}