Skip to content

DevTools Network Agent implementation #725

Merged
petekanev merged 7 commits intomasterfrom
pete/network-agent
Mar 27, 2017
Merged

DevTools Network Agent implementation #725
petekanev merged 7 commits intomasterfrom
pete/network-agent

Conversation

@petekanev
Copy link
Copy Markdown
Contributor

@petekanev petekanev commented Mar 17, 2017

The Runtime will now expose __inspector object that contains methods for communicating directly with the DevTools frontend. JavaScript objects are constructed in compliance with the v8 inspector protocol and sent to the DevTools frontend when:

  • a request is about to be made to a url
  • a response is received from the server
  • a request is finished (normally right after a response is received)

Features:

  • making a request marks it on the Network tab Timeline, this allows you to select only those made in a certain period of the application's lifetime
  • Status codes and status text are displayed correctly for requests made from and to the device
  • requests show the headers they are sent with, as well as any data that is encoded in the request; requests also display headers from the response when they are completed
  • Image responses can be previewed; Text/documents can be expanded (when in a tree-like - xml, json format).

Current limitations:

  • for some reason the frontend doesn't always update the "timeline" with the time it took to complete the request. This directly affects the "Time" tab for the request, as it will show "Pending", though the response was received and the request was completed.

Associated issue: #715
Associated PRs:

Part of the #563 effort

Demo images (Click to enlarge):

Overview Request Response
network-agent-1 network-agent-2 network-agent-3

add generated Network backend/frontend dispatchers
…the app is built in debug or release

expose __inspector object to the global JS object that has callbacks for co mmunicating with the devtools inspector frontend

rename loaderId in Page agent implementation to be matching with the one used in the network agent - NSLoaderIndentifier
update project template code to pass an additional parameter to StaticConfig
@petekanev petekanev requested a review from Plamen5kov March 17, 2017 11:32
@petekanev petekanev self-assigned this Mar 17, 2017
@ns-bot
Copy link
Copy Markdown

ns-bot commented Mar 17, 2017

💚

@petekanev
Copy link
Copy Markdown
Contributor Author

petekanev commented Mar 22, 2017

PR can be tested internally with the Groceries project:

git clone https://github.com/Pip3r4o/network-agent
cd network-agent
git checkout master
tns plugin add tns-core-modules@internal-preview
tns platform add android --frameworkPath http://nsbuild01.telerik.com/build/job/tns-android-PR/1038/artifact/dist/tns-android-3.0.0-2017.3.17.1.tgz
tns debug android


public class Runtime {
private native void initNativeScript(int runtimeId, String filesPath, String nativeLibDir, boolean verboseLoggingEnabled, String packageName, Object[] v8Options, String callingDir);
private native void initNativeScript(int runtimeId, String filesPath, String nativeLibDir, boolean verboseLoggingEnabled, boolean isDebuggable, String packageName, Object[] v8Options, String callingDir);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

8 Parameters sound like refactoring and class

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Passing raw objects as parameters through JNI is much less cumbersome to deal with on the native (C++) side as it won't require precaching fieldIds for each (8 in total) of the properties.


v8::Local<v8::Object> argsObj = args[0]->ToObject();

if ((!argsObj->Has(context, ArgConverter::ConvertToV8String(isolate, "requestId")).FromMaybe(false) ||
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do you think it's ok these to be constants? we repeat them twice here

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think all of the strings that we check for should be constants, or just some of them?


v8::Local<v8::Object> argsObj = args[0]->ToObject();

if ((!argsObj->Has(context, ArgConverter::ConvertToV8String(isolate, "requestId")).FromMaybe(false) ||
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not use a for statement - we check for 4 different parameters in the same pattern

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The v8::Object has no iterator, and while we can go over all of its properties (https://github.com/NativeScript/android-runtime/blob/master/runtime/src/main/jni/include/v8.h#L3086 GetOwnPropertyNames), we will still have to validate each one.

"Not all parameters are present in the object argument in the call to ResponseReceived! Required params: 'requestId', `timestamp`, `type`, `response`");
}

auto requestId = argsObj->Get(context, ArgConverter::ConvertToV8String(isolate, "requestId")).ToLocalChecked()->ToString();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we extract the validation + extraction logic to a function which returns a struct/class?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can certainly move the validation and extraction in separate methods, but it won't be one universal method that can validate each and every callback, so I don't think there will be use of separate validation.


static void LoadingFinishedCallback(const v8::FunctionCallbackInfo<v8::Value>& args) {
try {
auto networkAgentInstance = V8NetworkAgentImpl::Instance;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is seen at least 3 times. Can we make a Guard method and extract this functionality there?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure making a Guard method will make the code more readable than it is right now. We will have to check whether the guard has passed, and code will look almost the same.

NetworkRequestData::NetworkRequestData()
: m_data(""),
m_hasTextContent(true) { }
NetworkRequestData::NetworkRequestData(std::string data, bool hasTextContent)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

strange identation

nsEx.ReThrowToV8();
}
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

strange identation for the 3 }}}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code is formatted according to the tool used - astyle. https://github.com/NativeScript/android-runtime/blob/master/tools/astyle.js#L11

}

void setHasTextContent(const bool hasTextContent) {
m_hasTextContent = hasTextContent;
Copy link
Copy Markdown
Contributor

@yyosifov yyosifov Mar 23, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have a convention to use this-> for instance members?

namespace v8_inspector {

namespace NetworkAgentState {
static const char networkEnabled[] = "networkEnabled";
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

identation is not ok here

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indentation is according to the formatting tool used - astyle. https://github.com/NativeScript/android-runtime/blob/master/tools/astyle.js#L11

namespace v8_inspector {

namespace NetworkAgentState {
static const char networkEnabled[] = "networkEnabled";
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

aren't constants upperCase-named by convention? You can give me a convention for the C++ code we right, so that I don't write dummy comments :)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

try {
v8Inspector = new AndroidJsV8Inspector(app, logger);
v8Inspector.start();
File debugBreakFile = new File("/data/local/tmp", app.getPackageName() + "-debugger-started");
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what's the reasoning here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops, must have committed this by mistake. Nice catch!

Copy link
Copy Markdown
Contributor

@Plamen5kov Plamen5kov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In modules implementation: https://github.com/NativeScript/NativeScript/blob/master/tns-core-modules/debugger/debugger.ts#L169
Please check that mimetype exists before doing operations on it.

@petekanev
Copy link
Copy Markdown
Contributor Author

@Plamen5kov addressed in NativeScript/NativeScript#3867

@ns-bot
Copy link
Copy Markdown

ns-bot commented Mar 24, 2017

💚

@ns-bot
Copy link
Copy Markdown

ns-bot commented Mar 24, 2017

💚

Copy link
Copy Markdown
Contributor

@Plamen5kov Plamen5kov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After running builds and a fix for the commented issue.


inspectorJSObject->Set(ArgConverter::ConvertToV8String(isolate, "responseReceived"), FunctionTemplate::New(isolate, NetworkDomainCallbackHandlers::ResponseReceivedCallback));
inspectorJSObject->Set(ArgConverter::ConvertToV8String(isolate, "requestWillBeSent"), FunctionTemplate::New(isolate, NetworkDomainCallbackHandlers::RequestWillBeSentCallback));
inspectorJSObject->Set(ArgConverter::ConvertToV8String(isolate, "dataForRequestId"), FunctionTemplate::New(isolate, NetworkDomainCallbackHandlers::DataForRequestId));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DataForRequestId -> DataForRequestIdCallback

static const char* FrameId = "NSFrameIdentifier";
static const char* LoaderId = "NSLoaderIdentifier";

static void ResponseReceivedCallback(const v8::FunctionCallbackInfo<v8::Value>& args) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extract all methods to a source file, and leave only the API in the header.
Please add comments on all methods, so their function is clear without reading the code. The name's just not verbose enough.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Callbacks have since been documented. I'll just extract them as static members of a new class now.

@@ -19,7 +20,8 @@ JsV8InspectorClient::JsV8InspectorClient(v8::Isolate* isolate)
session_(nullptr),
connection(nullptr),
context_(),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

initialize context with default value

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be wrong to initialize the persistent container with value different than the default (0).

@ns-bot
Copy link
Copy Markdown

ns-bot commented Mar 27, 2017

💚

@petekanev petekanev merged commit b753387 into master Mar 27, 2017
@petekanev petekanev deleted the pete/network-agent branch March 27, 2017 08:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants