1- import { DeviceLiveSyncServiceBase } from "./device-livesync-service-base" ;
21import * as helpers from "../../common/helpers" ;
32import * as net from "net" ;
3+ import Future = require( "fibers/future" ) ;
44
55let currentPageReloadId = 0 ;
66
7- class IOSLiveSyncService extends DeviceLiveSyncServiceBase < Mobile . IiOSDevice > implements IDeviceLiveSyncService {
7+ class IOSLiveSyncService implements IDeviceLiveSyncService {
88 private static BACKEND_PORT = 18181 ;
99 private socket : net . Socket ;
10+ private device : Mobile . IiOSDevice ;
1011
1112 constructor ( _device : Mobile . IDevice ,
1213 private $iOSSocketRequestExecutor : IiOSSocketRequestExecutor ,
@@ -17,8 +18,9 @@ class IOSLiveSyncService extends DeviceLiveSyncServiceBase<Mobile.IiOSDevice> im
1718 private $options : IOptions ,
1819 private $iOSDebugService : IDebugService ,
1920 private $childProcess : IChildProcess ,
20- $liveSyncProvider : ILiveSyncProvider ) {
21- super ( _device , $liveSyncProvider ) ;
21+ private $fs : IFileSystem ,
22+ private $liveSyncProvider : ILiveSyncProvider ) {
23+ this . device = < Mobile . IiOSDevice > ( _device ) ;
2224 }
2325
2426 public get debugService ( ) : IDebugService {
@@ -31,76 +33,155 @@ class IOSLiveSyncService extends DeviceLiveSyncServiceBase<Mobile.IiOSDevice> im
3133 } ) . future < boolean > ( ) ( ) ;
3234 }
3335
36+ private setupSocketIfNeeded ( ) : IFuture < boolean > {
37+ return ( ( ) => {
38+ if ( this . socket ) {
39+ return true ;
40+ }
41+
42+ let enableDebuggerMessage = `{ "method":"Debugger.enable","id":${ ++ currentPageReloadId } }` ;
43+ if ( this . device . isEmulator ) {
44+ this . $iOSEmulatorServices . postDarwinNotification ( this . $iOSNotification . attachRequest ) . wait ( ) ;
45+ try {
46+ this . socket = helpers . connectEventuallyUntilTimeout ( ( ) => net . connect ( IOSLiveSyncService . BACKEND_PORT ) , 5000 ) . wait ( ) ;
47+ } catch ( e ) {
48+ this . $logger . warn ( e ) ;
49+
50+ return false ;
51+ }
52+ } else {
53+ let timeout = 9000 ;
54+ this . $iOSSocketRequestExecutor . executeAttachRequest ( this . device , timeout ) . wait ( ) ;
55+ this . socket = this . device . connectToPort ( IOSLiveSyncService . BACKEND_PORT ) ;
56+ }
57+
58+ this . attachEventHandlers ( ) ;
59+ this . sendMessage ( enableDebuggerMessage ) . wait ( ) ;
60+
61+ return true ;
62+ } ) . future < boolean > ( ) ( ) ;
63+ }
64+
3465 public removeFiles ( appIdentifier : string , localToDevicePaths : Mobile . ILocalToDevicePathData [ ] ) : IFuture < void > {
3566 return ( ( ) => {
3667 _ . each ( localToDevicePaths , localToDevicePathData => this . device . fileSystem . deleteFile ( localToDevicePathData . getDevicePath ( ) , appIdentifier ) ) ;
3768 } ) . future < void > ( ) ( ) ;
3869 }
3970
40- protected restartApplication ( deviceAppData : Mobile . IDeviceAppData ) : IFuture < void > {
71+ public refreshApplication ( deviceAppData : Mobile . IDeviceAppData , localToDevicePaths : Mobile . ILocalToDevicePathData [ ] , forceExecuteFullSync : boolean ) : IFuture < void > {
72+ return ( ( ) => {
73+ if ( forceExecuteFullSync ) {
74+ this . restartApplication ( deviceAppData ) . wait ( ) ;
75+ return ;
76+ }
77+ let scriptFiles = _ . filter ( localToDevicePaths , localToDevicePath => _ . endsWith ( localToDevicePath . getDevicePath ( ) , ".js" ) ) ;
78+ let otherFiles = _ . difference ( localToDevicePaths , scriptFiles ) ;
79+ let shouldRestart = _ . some ( otherFiles , ( localToDevicePath : Mobile . ILocalToDevicePathData ) => ! this . $liveSyncProvider . canExecuteFastSync ( localToDevicePath . getLocalPath ( ) , deviceAppData . platform ) ) ;
80+
81+ if ( shouldRestart ) {
82+ this . restartApplication ( deviceAppData ) . wait ( ) ;
83+
84+ return ;
85+ }
86+
87+ if ( ! this . $options . liveEdit && scriptFiles . length ) {
88+ this . restartApplication ( deviceAppData ) . wait ( ) ;
89+
90+ return ;
91+ }
92+
93+ if ( this . setupSocketIfNeeded ( ) . wait ( ) ) {
94+ this . liveEdit ( scriptFiles ) ;
95+ this . reloadPage ( deviceAppData , otherFiles ) . wait ( ) ;
96+ }
97+ } ) . future < void > ( ) ( ) ;
98+ }
99+
100+ private restartApplication ( deviceAppData : Mobile . IDeviceAppData ) : IFuture < void > {
41101 let projectData : IProjectData = this . $injector . resolve ( "projectData" ) ;
42102 return this . device . applicationManager . restartApplication ( deviceAppData . appIdentifier , projectData . projectName ) ;
43103 }
44104
45- protected reloadPage ( deviceAppData : Mobile . IDeviceAppData ) : IFuture < void > {
105+ private reloadPage ( deviceAppData : Mobile . IDeviceAppData , localToDevicePaths : Mobile . ILocalToDevicePathData [ ] ) : IFuture < void > {
46106 return ( ( ) => {
47- let timeout = 9000 ;
48- if ( this . device . isEmulator ) {
49- if ( ! this . socket ) {
50- helpers . connectEventually ( ( ) => net . connect ( IOSLiveSyncService . BACKEND_PORT ) , ( socket : net . Socket ) => {
51- this . socket = socket ;
52- this . attachEventHandlersIfNecessary ( ) ;
53- this . sendPageReloadMessage ( ) ;
54- } ) ;
55- } else {
56- this . sendPageReloadMessage ( ) ;
57- }
58- this . $iOSEmulatorServices . postDarwinNotification ( this . $iOSNotification . attachRequest ) . wait ( ) ;
59- } else {
60- if ( ! this . socket ) {
61- this . $iOSSocketRequestExecutor . executeAttachRequest ( this . device , timeout ) . wait ( ) ;
62- this . socket = this . device . connectToPort ( IOSLiveSyncService . BACKEND_PORT ) ;
63- this . attachEventHandlersIfNecessary ( ) ;
64- }
65- this . sendPageReloadMessage ( ) ;
107+ if ( localToDevicePaths . length ) {
108+ let message = JSON . stringify ( {
109+ method : "Page.reload" ,
110+ params : {
111+ ignoreCache : false
112+ } ,
113+ id : ++ currentPageReloadId
114+ } ) ;
115+
116+ this . sendMessage ( message ) . wait ( ) ;
66117 }
67118 } ) . future < void > ( ) ( ) ;
68119 }
69120
70- private attachEventHandlersIfNecessary ( ) : void {
71- if ( this . $options . watch ) {
72- this . attachProcessExitHandlers ( ) ;
73- this . attachSocketCloseEvent ( ) ;
74- }
121+ private liveEdit ( localToDevicePaths : Mobile . ILocalToDevicePathData [ ] ) {
122+ return ( ( ) => {
123+ _ . each ( localToDevicePaths , localToDevicePath => {
124+ let content = this . $fs . readText ( localToDevicePath . getLocalPath ( ) ) . wait ( ) ;
125+ let message = JSON . stringify ( {
126+ method : "Debugger.setScriptSource" ,
127+ params : {
128+ scriptUrl : localToDevicePath . getDevicePath ( ) ,
129+ scriptSource : content
130+ } ,
131+ id : ++ currentPageReloadId
132+ } ) ;
133+
134+ this . sendMessage ( message ) . wait ( ) ;
135+ } ) ;
136+ } ) . future < void > ( ) ( ) ;
75137 }
76138
77- private attachSocketCloseEvent ( ) : void {
139+ private attachEventHandlers ( ) : void {
140+ this . attachProcessExitHandlers ( ) ;
141+
78142 this . socket . on ( "close" , ( hadError : boolean ) => {
79143 this . $logger . trace ( `Socket closed, hadError is ${ hadError } .` ) ;
80144 this . socket = null ;
81145 } ) ;
82- }
83146
84- private sendPageReloadMessage ( ) : void {
85- try {
86- this . sendPageReloadMessageCore ( ) ;
87- this . socket . on ( "data" , ( data : NodeBuffer | string ) => {
88- this . $logger . trace ( `Socket sent data: ${ data . toString ( ) } ` ) ;
89- this . destroySocketIfNecessary ( ) ;
90- } ) ;
91- } catch ( err ) {
92- this . $logger . trace ( "Error while sending page reload:" , err ) ;
93- this . destroySocketIfNecessary ( ) ;
94- }
147+ this . socket . on ( "error" , ( error : any ) => {
148+ this . $logger . trace ( `Socket error received: ${ error } ` ) ;
149+ } ) ;
150+
151+ this . socket . on ( "data" , ( data : NodeBuffer | string ) => {
152+ this . $logger . trace ( `Socket sent data: ${ data . toString ( ) } ` ) ;
153+ } ) ;
95154 }
96155
97- private sendPageReloadMessageCore ( ) : void {
98- let message = `{ "method":"Page.reload","params":{"ignoreCache":false},"id":${ ++ currentPageReloadId } }` ;
99- let length = Buffer . byteLength ( message , "utf16le" ) ;
100- let payload = new Buffer ( length + 4 ) ;
101- payload . writeInt32BE ( length , 0 ) ;
102- payload . write ( message , 4 , length , "utf16le" ) ;
103- this . socket . write ( payload ) ;
156+ private sendMessage ( message : string ) : IFuture < void > {
157+ return ( ( ) => {
158+ let socketWriteFuture = new Future < void > ( ) ;
159+ try {
160+ let length = Buffer . byteLength ( message , "utf16le" ) ;
161+ let payload = new Buffer ( length + 4 ) ;
162+ payload . writeInt32BE ( length , 0 ) ;
163+ payload . write ( message , 4 , length , "utf16le" ) ;
164+
165+ this . socket . once ( "error" , ( error : Error ) => {
166+ if ( ! socketWriteFuture . isResolved ( ) ) {
167+ socketWriteFuture . throw ( error ) ;
168+ }
169+ } ) ;
170+
171+ this . socket . write ( payload , "utf16le" , ( ) => {
172+ this . socket . removeAllListeners ( "error" ) ;
173+
174+ if ( ! socketWriteFuture . isResolved ( ) ) {
175+ socketWriteFuture . return ( ) ;
176+ }
177+ } ) ;
178+
179+ socketWriteFuture . wait ( ) ;
180+ } catch ( error ) {
181+ this . $logger . trace ( "Error while sending message:" , error ) ;
182+ this . destroySocket ( ) ;
183+ }
184+ } ) . future < void > ( ) ( ) ;
104185 }
105186
106187 private attachProcessExitHandlers ( ) : void {
@@ -118,12 +199,6 @@ class IOSLiveSyncService extends DeviceLiveSyncServiceBase<Mobile.IiOSDevice> im
118199 } ) ;
119200 }
120201
121- private destroySocketIfNecessary ( ) : void {
122- if ( ! this . $options . watch ) {
123- this . destroySocket ( ) ;
124- }
125- }
126-
127202 private destroySocket ( ) : void {
128203 if ( this . socket ) {
129204 this . socket . destroy ( ) ;
0 commit comments