2
2
"use strict" ;
3
3
4
4
import Future = require( "fibers/future" ) ;
5
+ import net = require( "net" ) ;
5
6
import ref = require( "ref" ) ;
6
7
import os = require( "os" ) ;
7
8
import path = require( "path" ) ;
@@ -18,6 +19,7 @@ var CoreTypes = iosCore.CoreTypes;
18
19
19
20
export class IOSDevice implements Mobile . IIOSDevice {
20
21
private static IMAGE_ALREADY_MOUNTED_ERROR_CODE = 3892314230 ;
22
+ private static INTERFACE_USB = 1 ;
21
23
22
24
private identifier : string = null ;
23
25
private voidPtr = ref . refType ( ref . types . void ) ;
@@ -299,6 +301,10 @@ export class IOSDevice implements Mobile.IIOSDevice {
299
301
} ) . future < void > ( ) ( ) ;
300
302
}
301
303
304
+ private getInterfaceType ( ) : number {
305
+ return this . $mobileDevice . deviceGetInterfaceType ( this . devicePointer ) ;
306
+ }
307
+
302
308
public startService ( serviceName : string ) : number {
303
309
var func = ( ) => {
304
310
var socket = ref . alloc ( "int" ) ;
@@ -318,11 +324,16 @@ export class IOSDevice implements Mobile.IIOSDevice {
318
324
} ) . future < void > ( ) ( ) ;
319
325
}
320
326
321
- public debug ( packageFile : string , packageName : string ) : IFuture < void > {
322
- return ( ( ) => {
323
- this . $errors . fail ( { formatStr :"this will come in a future version" , suppressCommandHelp : true } ) ;
324
- } ) . future < void > ( ) ( ) ;
325
- }
327
+ public debug ( packageFile : string , packageName : string ) : IFuture < void > {
328
+ return ( ( ) => {
329
+ var debugging : IOSDeviceDebugging = this . $injector . resolve ( IOSDeviceDebugging , {
330
+ packageFile : packageFile ,
331
+ packageName : packageName ,
332
+ $iOSDevice : this
333
+ } ) ;
334
+ debugging . debug ( ) ;
335
+ } ) . future < void > ( ) ( ) ;
336
+ }
326
337
327
338
public sync ( localToDevicePaths : Mobile . ILocalToDevicePathData [ ] , appIdentifier : Mobile . IAppIdentifier , liveSyncUrl : string , options : Mobile . ISyncOptions = { } ) : IFuture < void > {
328
339
return ( ( ) => {
@@ -340,7 +351,6 @@ export class IOSDevice implements Mobile.IIOSDevice {
340
351
var notificationProxyClient = this . $injector . resolve ( iOSProxyServices . NotificationProxyClient , { device : this } ) ;
341
352
notificationProxyClient . postNotification ( "com.telerik.app.refreshWebView" ) ;
342
353
notificationProxyClient . closeSocket ( ) ;
343
-
344
354
}
345
355
346
356
this . $logger . out ( "Successfully synced device with identifier '%s'" , this . getIdentifier ( ) ) ;
@@ -381,5 +391,169 @@ export class IOSDevice implements Mobile.IIOSDevice {
381
391
this . $logger . info ( "Successfully run application %s on device with ID %s" , applicationId , this . getIdentifier ( ) ) ;
382
392
} ) . future < void > ( ) ( ) ;
383
393
}
394
+
395
+ public connectToPort ( port : number ) : net . NodeSocket {
396
+ var interfaceType = this . getInterfaceType ( ) ;
397
+ if ( interfaceType === IOSDevice . INTERFACE_USB ) {
398
+ var connectionId = this . $mobileDevice . deviceGetConnectionId ( this . devicePointer ) ;
399
+ var socketRef = ref . alloc ( CoreTypes . intType ) ;
400
+
401
+ this . $mobileDevice . uSBMuxConnectByPort ( connectionId , this . htons ( port ) , socketRef ) ;
402
+ var socketValue = socketRef . deref ( ) ;
403
+
404
+ return new net . Socket ( { fd : socketValue } ) ;
405
+ }
406
+
407
+ return null ;
408
+ }
409
+
410
+ /**
411
+ * Converts a little endian 16 bit int number to 16 bit int big endian number.
412
+ */
413
+ private htons ( port : number ) : number {
414
+ var result = ( port & 0xff00 ) >> 8 | ( port & 0x00ff ) << 8 ;
415
+ return result ;
416
+ }
417
+ }
418
+ $injector . register ( "iOSDevice" , IOSDevice ) ;
419
+
420
+ export class IOSDeviceDebugging {
421
+ private notificationProxyClient : iOSProxyServices . NotificationProxyClient ;
422
+
423
+ constructor (
424
+ private packageFile : string ,
425
+ private packageName : string ,
426
+ private $iOSDevice : IOSDevice ,
427
+
428
+ private $injector : IInjector ,
429
+
430
+ private $logger : ILogger ,
431
+ private $errors : IErrors ,
432
+
433
+ private $npm : INodePackageManager ,
434
+ private $childProcess : IChildProcess ) {
435
+
436
+ this . notificationProxyClient = this . $injector . resolve ( iOSProxyServices . NotificationProxyClient , { device : this . $iOSDevice } ) ;
437
+ }
438
+
439
+ public debug ( ) : void {
440
+ if ( options [ "get-port" ] ) {
441
+ this . $errors . fail ( { formatStr : "this feature is not supported in the selected platform" , suppressCommandHelp : true } ) ;
442
+ } else if ( options [ "start" ] ) {
443
+ this . attachDebugger ( ) ;
444
+ } else if ( options [ "stop" ] ) {
445
+ this . $errors . fail ( { formatStr : "this feature is not supported in the selected platform" , suppressCommandHelp : true } ) ;
446
+ } else if ( options [ "debug-brk" ] ) {
447
+ this . startAppWithDebugger ( ) ;
448
+ } else {
449
+ this . $logger . info ( "Should specify exactly one option: debug-brk, start" ) ;
450
+ }
451
+ }
452
+
453
+ public attachDebugger ( ) : void {
454
+ this . attachAtRuntime ( ) ;
455
+ }
456
+
457
+ public startAppWithDebugger ( ) : void {
458
+ this . $iOSDevice . deploy ( this . packageFile , this . packageName ) . wait ( ) ;
459
+ this . attachAtStartup ( ) ;
460
+ this . $iOSDevice . runApplication ( this . packageName ) . wait ( ) ;
461
+ }
462
+
463
+ private attachAtStartup ( ) {
464
+ this . startTheFrontEnd ( ) ;
465
+
466
+ var appLaunchMessage = this . packageName + ":NativeScript.Debug.AppLaunching" ;
467
+ this . notificationProxyClient . addObserver ( appLaunchMessage , ( ) => {
468
+ this . $logger . info ( "Got AppLaunching" ) ;
469
+ this . startTheBackend ( "WaitForDebugger" ) ;
470
+ } ) ;
471
+ }
472
+
473
+ private attachAtRuntime ( ) : void {
474
+ this . startTheFrontEnd ( ) ;
475
+ this . startTheBackend ( "AttachRequest" ) ;
476
+ }
477
+
478
+ private startTheFrontEnd ( ) : void {
479
+ this . openSafariFrontEnd ( ) ;
480
+ this . printHowToTerminate ( ) ;
481
+ }
482
+
483
+ private startTheBackend ( message : string ) {
484
+ this . attachWhenReady ( ) ;
485
+ this . postDebugNotification ( message ) ;
486
+ }
487
+
488
+ private openSafariFrontEnd ( ) : void {
489
+ var tnsIosPackage = this . $npm . install ( "tns-ios" ) . wait ( ) ;
490
+ var safariPath = path . join ( tnsIosPackage , "WebInspectorUI/Safari/Main.html" ) ;
491
+ this . $childProcess . exec ( "open -a Safari " + safariPath ) . wait ( ) ;
492
+ }
493
+
494
+ private attachWhenReady ( ) : void {
495
+
496
+ var identifier = this . $iOSDevice . getIdentifier ( ) ;
497
+ this . $logger . info ( "Device Identifier: " + identifier ) ;
498
+
499
+ var appReadyForAttachMessage = this . packageName + ":NativeScript.Debug.ReadyForAttach" ;
500
+ this . notificationProxyClient . addObserver ( appReadyForAttachMessage , ( ) => {
501
+ this . $logger . info ( "Got ReadyForAttach" ) ;
502
+
503
+ // NOTE: We will try to provide command line options to select ports, at least on the localhost.
504
+ var devicePort = 8080 ;
505
+ var localPort = 8080 ;
506
+
507
+ var localServer = net . createServer ( ( localSocket ) => {
508
+ this . $logger . info ( "Front-end client connected to localhost " + localPort ) ;
509
+
510
+ var deviceSocket : any ;
511
+
512
+ var buff = new Array ( ) ;
513
+ localSocket . on ( 'data' , ( data : NodeBuffer ) => {
514
+ if ( deviceSocket ) {
515
+ deviceSocket . write ( data ) ;
516
+ } else {
517
+ buff . push ( data ) ;
518
+ }
519
+ } ) ;
520
+ localSocket . on ( 'end' , ( ) => {
521
+ this . $logger . info ( 'Localhost client disconnected!' ) ;
522
+ process . exit ( 0 ) ;
523
+ } ) ;
524
+
525
+ deviceSocket = this . $iOSDevice . connectToPort ( devicePort ) ;
526
+ this . $logger . info ( "Connected localhost " + localPort + " to device " + devicePort ) ;
527
+
528
+ buff . forEach ( ( data ) => {
529
+ deviceSocket . write ( data ) ;
530
+ } ) ;
531
+
532
+ deviceSocket . on ( 'data' , ( data : NodeBuffer ) => {
533
+ localSocket . write ( data ) ;
534
+ } ) ;
535
+
536
+ deviceSocket . on ( 'end' , ( ) => {
537
+ this . $logger . info ( "Device socket closed!" ) ;
538
+ process . exit ( 0 ) ;
539
+ } ) ;
540
+
541
+ } ) ;
542
+
543
+ localServer . listen ( localPort , ( ) => {
544
+ this . $logger . info ( "Opened localhost " + localPort ) ;
545
+ } ) ;
546
+ } ) ;
547
+ }
548
+
549
+ private printHowToTerminate ( ) {
550
+ this . $logger . info ( "\nSetting up debugger proxy...\n\nPress Ctrl + C to terminate!\n" ) ;
551
+ }
552
+
553
+ private postDebugNotification ( notification : string ) : void {
554
+ var attachRequestMessage = this . packageName + ":NativeScript.Debug.AttachRequest" ;
555
+ this . notificationProxyClient . postNotification ( attachRequestMessage ) ;
556
+ this . $logger . info ( "Send " + notification ) ;
557
+ }
384
558
}
385
- $injector . register ( "iOSDevice " , IOSDevice ) ;
559
+ $injector . register ( "iOSDeviceDebugging " , IOSDeviceDebugging ) ;
0 commit comments