1
+ /**
2
+ * @license
3
+ * Copyright Google Inc. All Rights Reserved.
4
+ *
5
+ * Use of this source code is governed by an MIT-style license that can be
6
+ * found in the LICENSE file at https://angular.io/license
7
+ */
8
+
9
+ import * as Rx from 'rxjs/Rx' ;
10
+
11
+ ( Zone as any ) . __load_patch ( 'rxjs' , ( global : any , Zone : ZoneType , api : any ) => {
12
+ const symbol : ( symbolString : string ) => string = ( Zone as any ) . __symbol__ ;
13
+ const subscribeSource = 'rxjs.subscribe' ;
14
+ const nextSource = 'rxjs.Subscriber.next' ;
15
+ const errorSource = 'rxjs.Subscriber.error' ;
16
+ const completeSource = 'rxjs.Subscriber.complete' ;
17
+ const unsubscribeSource = 'rxjs.Subscriber.unsubscribe' ;
18
+ const teardownSource = 'rxjs.Subscriber.teardownLogic' ;
19
+
20
+ const patchObservableInstance = function ( observable : any ) {
21
+ observable . _zone = Zone . current ;
22
+ // patch inner function this._subscribe to check
23
+ // SubscriptionZone is same with ConstuctorZone or not
24
+ if ( observable . _subscribe && typeof observable . _subscribe === 'function' &&
25
+ ! observable . _originalSubscribe ) {
26
+ observable . _originalSubscribe = observable . _subscribe ;
27
+ observable . _subscribe = _patchedSubscribe ;
28
+ }
29
+ } ;
30
+
31
+ const _patchedSubscribe = function ( ) {
32
+ const currentZone = Zone . current ;
33
+ const _zone = this . _zone ;
34
+
35
+ const args = Array . prototype . slice . call ( arguments ) ;
36
+ const subscriber = args . length > 0 ? args [ 0 ] : undefined ;
37
+ // also keep currentZone in Subscriber
38
+ // for later Subscriber.next/error/complete method
39
+ if ( subscriber && ! subscriber . _zone ) {
40
+ subscriber . _zone = currentZone ;
41
+ }
42
+ // _subscribe should run in ConstructorZone
43
+ // but for performance concern, we should check
44
+ // whether ConsturctorZone === Zone.current here
45
+ const tearDownLogic = _zone !== Zone . current ?
46
+ _zone . run ( this . _originalSubscribe , this , args , subscribeSource ) :
47
+ this . _originalSubscribe . apply ( this , args ) ;
48
+ if ( tearDownLogic && typeof tearDownLogic === 'function' ) {
49
+ const patchedTearDownLogic = function ( ) {
50
+ // tearDownLogic should also run in ConstructorZone
51
+ // but for performance concern, we should check
52
+ // whether ConsturctorZone === Zone.current here
53
+ if ( _zone && _zone !== Zone . current ) {
54
+ return _zone . run ( tearDownLogic , this , arguments , teardownSource ) ;
55
+ } else {
56
+ return tearDownLogic . apply ( this , arguments ) ;
57
+ }
58
+ } ;
59
+ return patchedTearDownLogic ;
60
+ }
61
+ return tearDownLogic ;
62
+ } ;
63
+
64
+ const patchObservable = function ( Rx : any , observableType : string ) {
65
+ const symbolObservable = symbol ( observableType ) ;
66
+
67
+ const Observable = Rx [ observableType ] ;
68
+ if ( ! Observable || Observable [ symbolObservable ] ) {
69
+ // the subclass of Observable not loaded or have been patched
70
+ return ;
71
+ }
72
+
73
+ // monkey-patch Observable to save the
74
+ // current zone as ConstructorZone
75
+ const patchedObservable : any = Rx [ observableType ] = function ( ) {
76
+ Observable . apply ( this , arguments ) ;
77
+ patchObservableInstance ( this ) ;
78
+ return this ;
79
+ } ;
80
+
81
+ patchedObservable . prototype = Observable . prototype ;
82
+ patchedObservable [ symbolObservable ] = Observable ;
83
+
84
+ Object . keys ( Observable ) . forEach ( key => {
85
+ patchedObservable [ key ] = Observable [ key ] ;
86
+ } ) ;
87
+
88
+ const ObservablePrototype : any = Observable . prototype ;
89
+ const symbolSubscribe = symbol ( 'subscribe' ) ;
90
+
91
+ if ( ! ObservablePrototype [ symbolSubscribe ] ) {
92
+ const subscribe = ObservablePrototype [ symbolSubscribe ] = ObservablePrototype . subscribe ;
93
+ // patch Observable.prototype.subscribe
94
+ // if SubscripitionZone is different with ConstructorZone
95
+ // we should run _subscribe in ConstructorZone and
96
+ // create sinke in SubscriptionZone,
97
+ // and tearDown should also run into ConstructorZone
98
+ Observable . prototype . subscribe = function ( ) {
99
+ const _zone = this . _zone ;
100
+ const currentZone = Zone . current ;
101
+
102
+ // if operator is involved, we should also
103
+ // patch the call method to save the Subscription zone
104
+ if ( this . operator && _zone && _zone !== currentZone ) {
105
+ const call = this . operator . call ;
106
+ this . operator . call = function ( ) {
107
+ const args = Array . prototype . slice . call ( arguments ) ;
108
+ const subscriber = args . length > 0 ? args [ 0 ] : undefined ;
109
+ if ( ! subscriber . _zone ) {
110
+ subscriber . _zone = currentZone ;
111
+ }
112
+ return _zone . run ( call , this , args , subscribeSource ) ;
113
+ } ;
114
+ }
115
+ const result = subscribe . apply ( this , arguments ) ;
116
+ // the result is the subscriber sink,
117
+ // we save the current Zone here
118
+ if ( ! result . _zone ) {
119
+ result . _zone = currentZone ;
120
+ }
121
+ return result ;
122
+ } ;
123
+ }
124
+
125
+ const symbolLift = symbol ( 'lift' ) ;
126
+ if ( ! ObservablePrototype [ symbolLift ] ) {
127
+ const lift = ObservablePrototype [ symbolLift ] = ObservablePrototype . lift ;
128
+
129
+ // patch lift method to save ConstructorZone of Observable
130
+ Observable . prototype . lift = function ( ) {
131
+ const observable = lift . apply ( this , arguments ) ;
132
+ patchObservableInstance ( observable ) ;
133
+
134
+ return observable ;
135
+ } ;
136
+ }
137
+
138
+ const symbolCreate = symbol ( 'create' ) ;
139
+ if ( ! patchedObservable [ symbolCreate ] ) {
140
+ const create = patchedObservable [ symbolCreate ] = Observable . create ;
141
+ // patch create method to save ConstructorZone of Observable
142
+ Rx . Observable . create = function ( ) {
143
+ const observable = create . apply ( this , arguments ) ;
144
+ patchObservableInstance ( observable ) ;
145
+
146
+ return observable ;
147
+ } ;
148
+ }
149
+ } ;
150
+
151
+ const patchSubscriber = function ( ) {
152
+ const Subscriber = Rx . Subscriber ;
153
+
154
+ const next = Subscriber . prototype . next ;
155
+ const error = Subscriber . prototype . error ;
156
+ const complete = Subscriber . prototype . complete ;
157
+ const unsubscribe = Subscriber . prototype . unsubscribe ;
158
+
159
+ // patch Subscriber.next to make sure it run
160
+ // into SubscriptionZone
161
+ Subscriber . prototype . next = function ( ) {
162
+ const currentZone = Zone . current ;
163
+ const subscriptionZone = this . _zone ;
164
+
165
+ // for performance concern, check Zone.current
166
+ // equal with this._zone(SubscriptionZone) or not
167
+ if ( subscriptionZone && subscriptionZone !== currentZone ) {
168
+ return subscriptionZone . run ( next , this , arguments , nextSource ) ;
169
+ } else {
170
+ return next . apply ( this , arguments ) ;
171
+ }
172
+ } ;
173
+
174
+ Subscriber . prototype . error = function ( ) {
175
+ const currentZone = Zone . current ;
176
+ const subscriptionZone = this . _zone ;
177
+
178
+ // for performance concern, check Zone.current
179
+ // equal with this._zone(SubscriptionZone) or not
180
+ if ( subscriptionZone && subscriptionZone !== currentZone ) {
181
+ return subscriptionZone . run ( error , this , arguments , errorSource ) ;
182
+ } else {
183
+ return error . apply ( this , arguments ) ;
184
+ }
185
+ } ;
186
+
187
+ Subscriber . prototype . complete = function ( ) {
188
+ const currentZone = Zone . current ;
189
+ const subscriptionZone = this . _zone ;
190
+
191
+ // for performance concern, check Zone.current
192
+ // equal with this._zone(SubscriptionZone) or not
193
+ if ( subscriptionZone && subscriptionZone !== currentZone ) {
194
+ return subscriptionZone . run ( complete , this , arguments , completeSource ) ;
195
+ } else {
196
+ return complete . apply ( this , arguments ) ;
197
+ }
198
+ } ;
199
+
200
+ Subscriber . prototype . unsubscribe = function ( ) {
201
+ const currentZone = Zone . current ;
202
+ const subscriptionZone = this . _zone ;
203
+
204
+ // for performance concern, check Zone.current
205
+ // equal with this._zone(SubscriptionZone) or not
206
+ if ( subscriptionZone && subscriptionZone !== currentZone ) {
207
+ return subscriptionZone . run ( unsubscribe , this , arguments , unsubscribeSource ) ;
208
+ } else {
209
+ return unsubscribe . apply ( this , arguments ) ;
210
+ }
211
+ } ;
212
+ } ;
213
+
214
+ const patchObservableFactoryCreator = function ( obj : any , factoryName : string ) {
215
+ const symbolFactory : string = symbol ( factoryName ) ;
216
+ if ( obj [ symbolFactory ] ) {
217
+ return ;
218
+ }
219
+ const factoryCreator : any = obj [ symbolFactory ] = obj [ factoryName ] ;
220
+ obj [ factoryName ] = function ( ) {
221
+ const factory : any = factoryCreator . apply ( this , arguments ) ;
222
+ return function ( ) {
223
+ const observable = factory . apply ( this , arguments ) ;
224
+ patchObservableInstance ( observable ) ;
225
+ return observable ;
226
+ } ;
227
+ } ;
228
+ } ;
229
+
230
+ patchObservable ( Rx , 'Observable' ) ;
231
+ patchSubscriber ( ) ;
232
+ patchObservableFactoryCreator ( Rx . Observable , 'bindCallback' ) ;
233
+ patchObservableFactoryCreator ( Rx . Observable , 'bindNodeCallback' ) ;
234
+ } ) ;
0 commit comments