Skip to content

Commit

Permalink
Merge pull request #317 from NativeScript/KristinaKoeva/InspectorComm…
Browse files Browse the repository at this point in the history
…unication

Inject InspectorFrontendHost in the Inspector frontend and communicate over raw sockets
  • Loading branch information
fealebenpae committed Sep 18, 2015
2 parents 148961e + c180115 commit ba6281c
Show file tree
Hide file tree
Showing 13 changed files with 386 additions and 48 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,6 @@ node_modules/

# CLion
.idea

# Inspector application
/src/debugging/inspector/Inspector/Frameworks
Binary file modified build/inspector/NativeScript Inspector.zip
Binary file not shown.

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions src/debugging/Inspector/Inspector/Inspector/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIconFile</key>
<string>osx-debugger.icns</string>
<key>CFBundleIdentifier</key>
<string>org.NativeScript.$(PRODUCT_NAME:rfc1034identifier)</string>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
Expand All @@ -28,7 +30,5 @@
<string>Copyright © Telerik. All rights reserved.</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
<key>CFBundleIconFile</key>
<string>osx-debugger.icns</string>
</dict>
</plist>
23 changes: 17 additions & 6 deletions src/debugging/Inspector/Inspector/Inspector/main.m
Original file line number Diff line number Diff line change
@@ -1,19 +1,30 @@
#import <Cocoa/Cocoa.h>

int main(int argc, const char * argv[]) {
int main(int argc, const char* argv[]) {
NSBundle* applicationBundle = [NSBundle bundleWithPath:[[NSBundle mainBundle] pathForAuxiliaryExecutable:@"NativeScript Inspector.app"]];
NSArray* runningApplications = [NSRunningApplication runningApplicationsWithBundleIdentifier:[applicationBundle bundleIdentifier]];


NSString* main_file_path = @(argv[1]);
NSString* project_name = @(argv[2]);
NSString* socket_path = @"";

if (argc >= 4) {
socket_path = @(argv[3]);
}

NSLog(@"Inspector socket path %@", socket_path);
if (runningApplications.count > 0) {
NSDictionary* innerArguments = @{ @"project_name": @(argv[2]) };
NSDictionary* innerArguments = @{ @"project_name" : project_name,
@"socket_path" : socket_path };

[[NSDistributedNotificationCenter defaultCenter] postNotificationName:@"org.NativeScriptInspector.ApplicationShouldHandleReopen" object:nil userInfo:innerArguments];
[runningApplications[0] activateWithOptions:NSApplicationActivateIgnoringOtherApps];
} else {
NSDictionary* configuration = @{
NSWorkspaceLaunchConfigurationArguments: @[ @(argv[1]), @(argv[2]) ],
NSWorkspaceLaunchConfigurationEnvironment: @{ @"DYLD_FRAMEWORK_PATH": [[NSBundle mainBundle] privateFrameworksPath] }
NSWorkspaceLaunchConfigurationArguments : @[ main_file_path, project_name, socket_path ],
NSWorkspaceLaunchConfigurationEnvironment : @{ @"DYLD_FRAMEWORK_PATH" : [[NSBundle mainBundle] privateFrameworksPath] }
};

[[NSWorkspace sharedWorkspace] launchApplicationAtURL:applicationBundle.bundleURL options:0 configuration:configuration error:nil];
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
#import "Communication.h"
#import <netinet/in.h>
#import <sys/types.h>
#import <sys/socket.h>
#import <sys/un.h>
#import <errno.h>
#import <stdlib.h>
#import <string.h>
#import <notify.h>

@implementation TNSCommunicationChannel {
InspectorErrorHandler errorHandler;
}

- (instancetype)initWithSocketPath:(NSString*)socketPath readHandler:(InspectorReadHandler)readHandler errorHandler:(InspectorErrorHandler)errorHandler {
self = [super init];
if (self) {
self->errorHandler = errorHandler;

__block dispatch_fd_t communicationSocket;
__block dispatch_io_t communicationIOChannel;

dispatch_queue_t global_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_queue_t main_queue = dispatch_get_main_queue();

void (^read)(void) = ^{
__block dispatch_io_handler_t ioHandler = ^(bool done, dispatch_data_t data, int error) {
if (!CheckError(error, self->errorHandler)) {
return;
}

const void* bytes = [(NSData*)data bytes];
if (!bytes) {
close(communicationSocket);
return;
}

uint32_t length = ntohl(*(uint32_t*)bytes);

dispatch_io_set_low_water(communicationIOChannel, length);
dispatch_io_read(communicationIOChannel, 0, length, global_queue,
^(bool done, dispatch_data_t data, int error) {
if (!CheckError(error, self->errorHandler)) {
return;
}

dispatch_async(main_queue, ^(void) {
readHandler(data);
});

dispatch_io_read(communicationIOChannel, 0, 4, global_queue, ioHandler);
});
};

dispatch_io_read(communicationIOChannel, 0, 4, global_queue, ioHandler);
};

BOOL (^setupConnection)
(const struct sockaddr*, socklen_t) = ^(const struct sockaddr* addr, socklen_t socketLength) {
communicationIOChannel = dispatch_io_create(DISPATCH_IO_STREAM, communicationSocket, global_queue,
^(int error) {
CheckError(error, self->errorHandler);
});

int result = connect(communicationSocket, addr, socketLength);
if (result) {
self->errorHandler([NSError errorWithDomain:@"Unable to connect" code:errno userInfo:nil]);

return NO;
}

read();

self.ioChannel = communicationIOChannel;
self.socket = communicationSocket;

return YES;
};

BOOL connected;

if (!socketPath || !socketPath.length) {
communicationSocket = socket(PF_INET, SOCK_STREAM, 0);

struct sockaddr_in addr = {
sizeof(addr), AF_INET, htons(18181), { INADDR_ANY }, { 0 }
};

connected = setupConnection((const struct sockaddr*)&addr, sizeof(addr));

} else {
communicationSocket = socket(AF_UNIX, SOCK_STREAM, 0);

struct sockaddr_un addr;
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;

strncpy(addr.sun_path, [socketPath UTF8String], sizeof(addr.sun_path) - 1);

connected = setupConnection((const struct sockaddr*)&addr, sizeof(addr));
}

if (!connected) {
return nil;
}
}

return self;
}

- (void)sendMessage:(uint32_t)length message:(void*)message {
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
void* buffer = malloc(length + sizeof(uint32_t));
*(uint32_t*)buffer = htonl(length);

memcpy(&buffer[sizeof(uint32_t)], message, length);

dispatch_data_t data = dispatch_data_create(buffer, length + sizeof(uint32_t), queue, ^{
free(buffer);
});

dispatch_io_write(self.ioChannel, 0, data, queue,
^(bool done, dispatch_data_t data, int error) {
CheckError(error, self->errorHandler);
});
}

- (void)dealloc {
if (self.ioChannel) {
dispatch_io_close(self.ioChannel, DISPATCH_IO_STOP);
}
}

@end
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#import <Foundation/Foundation.h>
#import "InspectorFrontendHostProtocol.h"
#import "Communication.h"
@import AppKit;

@interface InspectorFrontendHost : NSObject <InspectorFrontendHostProtocol> {
TNSCommunicationChannel* communication_channel;
}

- (NSString*)platform;
- (NSString*)localizedStringsURL;
- (NSString*)debuggableType;
- (void)connect:(NSString*)socketPath readHandler:(InspectorReadHandler)read_handler errorHandler:(InspectorErrorHandler)errorHandler;
- (void)disconnect;
- (void)loaded;
- (void)bringToFront;
- (void)sendMessageToBackend:(NSString*)message;
- (void)inspectedURLChanged;

@end
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#import <Foundation/Foundation.h>
#import "InspectorFrontendHost.h"

@implementation InspectorFrontendHost

- (void)bringToFront {
}

- (NSString*)platform {
return @"mac";
}

- (NSString*)localizedStringsURL {
return @"";
}

- (NSString*)debuggableType {
return @"web";
}

- (void)connect:(NSString*)socketPath readHandler:(InspectorReadHandler)read_handler errorHandler:(InspectorErrorHandler)errorHandler {
self->communication_channel = [[TNSCommunicationChannel alloc] initWithSocketPath:socketPath readHandler:read_handler errorHandler:errorHandler];
}

- (void)disconnect {
if (self->communication_channel) {
self->communication_channel = nil;
}
}

- (void)loaded {
}

- (void)inspectedURLChanged {
}

- (void)sendMessageToBackend:(NSString*)message {
if (self->communication_channel) {
uint32_t length = [message lengthOfBytesUsingEncoding:NSUTF16LittleEndianStringEncoding];

void* buffer = malloc(length);
[message getBytes:buffer maxLength:length usedLength:NULL encoding:NSUTF16LittleEndianStringEncoding options:0 range:NSMakeRange(0, message.length) remainingRange:NULL];

[self->communication_channel sendMessage:length message:buffer];
}
}

@end
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#import <Foundation/Foundation.h>
#import <JavaScriptCore/JSExport.h>

@protocol InspectorFrontendHostProtocol <JSExport>

- (NSString *)platform;
- (NSString *)localizedStringsURL;
- (NSString *)debuggableType;
- (void)loaded;
- (void)bringToFront;
- (void)sendMessageToBackend:(NSString *)message;
- (void)inspectedURLChanged;

@end
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
#import <Cocoa/Cocoa.h>
#import "InspectorFrontendHost.h"

@interface ViewController : NSViewController

@interface ViewController : NSViewController {
InspectorFrontendHost *frontendHost;
}

@end

Original file line number Diff line number Diff line change
@@ -1,41 +1,81 @@
#import "ViewController.h"
@import AppKit;
@import WebKit;
@import JavaScriptCore;
#import "InspectorFrontendHost.h"

@implementation ViewController

- (void)viewDidLoad {
[super viewDidLoad];

NSArray* arguments = [[NSProcessInfo processInfo] arguments];
WebView* webView = (WebView*)self.view;
webView.mainFrameURL = (NSString*)arguments[1];
}

- (void)awakeFromNib {
[super awakeFromNib];

[[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationShouldReopen:) name:@"org.NativeScriptInspector.ApplicationShouldHandleReopen" object:nil];

self->frontendHost = [[InspectorFrontendHost alloc] init];
[self setupWebView];

NSArray* arguments = [[NSProcessInfo processInfo] arguments];
NSString* socket_path = @"";

if (arguments.count >= 4) {
socket_path = arguments[3];
}

[self connect:socket_path];
}

- (void)applicationShouldReopen:(NSNotification*)notification {
[self->frontendHost disconnect];
self.view = [[WebView alloc] initWithFrame:self.view.frame];
[self setupWebView];

NSString* title = [notification.userInfo valueForKey:@"project_name"];
[self update: title];

[self update:title];

NSString* socket_path = [notification.userInfo valueForKey:@"socket_path"];
[self connect:socket_path];
}

- (void)setupWebView {
WebView* webView = (WebView*)self.view;
[webView reload:self];

JSContext* context = webView.mainFrame.javaScriptContext;

context[@"InspectorFrontendHost"] = self->frontendHost;
context[@"WebInspector"] = [JSValue valueWithNewObjectInContext:context];
context[@"WebInspector"][@"dontLocalizeUserInterface"] = @(true);

NSArray* arguments = [[NSProcessInfo processInfo] arguments];
webView.mainFrameURL = (NSString*)arguments[1];
}

- (void)viewWillAppear {
[super viewWillAppear];

NSArray* arguments = [[NSProcessInfo processInfo] arguments];

[self update:(NSString*)arguments[2]];
self.view.window.titlebarAppearsTransparent = YES;
}

- (void)update:(NSString*)title {
self.view.window.title = [NSString stringWithFormat:@"NativeScript Inspector - %@", title];
self.view.window.title = [NSString stringWithFormat:@"NativeScript Inspector - %@", title];
}

- (void)connect:(NSString*)socket_path {
InspectorReadHandler read_handler = ^(dispatch_data_t data) {
NSString* payload = [[NSString alloc] initWithData:(NSData*)data encoding:NSUTF16LittleEndianStringEncoding];

WebView* webView = (WebView*)self.view;
[webView.mainFrame.javaScriptContext[@"InspectorBackend"] invokeMethod:@"dispatch" withArguments:@[ payload ]];
};

InspectorErrorHandler error_handler = ^(NSError* error) {
[self->frontendHost disconnect];
[self.view presentError:error];
};

[self->frontendHost connect:socket_path readHandler:read_handler errorHandler:error_handler];
}

@end
Loading

0 comments on commit ba6281c

Please sign in to comment.