forked from angular/angular
-
Notifications
You must be signed in to change notification settings - Fork 85
/
Copy pathproperty-descriptor-legacy.ts
136 lines (128 loc) · 4.63 KB
/
property-descriptor-legacy.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
/**
* @fileoverview
* @suppress {globalThis}
*/
import * as webSocketPatch from './websocket';
export function propertyDescriptorLegacyPatch(api: _ZonePrivate, _global: any) {
const {isNode, isMix} = api.getGlobalObjects()!;
if (isNode && !isMix) {
return;
}
if (!canPatchViaPropertyDescriptor(api, _global)) {
const supportsWebSocket = typeof WebSocket !== 'undefined';
// Safari, Android browsers (Jelly Bean)
patchViaCapturingAllTheEvents(api);
api.patchClass('XMLHttpRequest');
if (supportsWebSocket) {
webSocketPatch.apply(api, _global);
}
(Zone as any)[api.symbol('patchEvents')] = true;
}
}
function canPatchViaPropertyDescriptor(api: _ZonePrivate, _global: any) {
const {isBrowser, isMix} = api.getGlobalObjects()!;
if ((isBrowser || isMix) &&
!api.ObjectGetOwnPropertyDescriptor(HTMLElement.prototype, 'onclick') &&
typeof Element !== 'undefined') {
// WebKit https://bugs.webkit.org/show_bug.cgi?id=134364
// IDL interface attributes are not configurable
const desc = api.ObjectGetOwnPropertyDescriptor(Element.prototype, 'onclick');
if (desc && !desc.configurable) return false;
// try to use onclick to detect whether we can patch via propertyDescriptor
// because XMLHttpRequest is not available in service worker
if (desc) {
api.ObjectDefineProperty(Element.prototype, 'onclick', {
enumerable: true,
configurable: true,
get: function() {
return true;
}
});
const div = document.createElement('div');
const result = !!div.onclick;
api.ObjectDefineProperty(Element.prototype, 'onclick', desc);
return result;
}
}
const XMLHttpRequest = _global['XMLHttpRequest'];
if (!XMLHttpRequest) {
// XMLHttpRequest is not available in service worker
return false;
}
const ON_READY_STATE_CHANGE = 'onreadystatechange';
const XMLHttpRequestPrototype = XMLHttpRequest.prototype;
const xhrDesc =
api.ObjectGetOwnPropertyDescriptor(XMLHttpRequestPrototype, ON_READY_STATE_CHANGE);
// add enumerable and configurable here because in opera
// by default XMLHttpRequest.prototype.onreadystatechange is undefined
// without adding enumerable and configurable will cause onreadystatechange
// non-configurable
// and if XMLHttpRequest.prototype.onreadystatechange is undefined,
// we should set a real desc instead a fake one
if (xhrDesc) {
api.ObjectDefineProperty(XMLHttpRequestPrototype, ON_READY_STATE_CHANGE, {
enumerable: true,
configurable: true,
get: function() {
return true;
}
});
const req = new XMLHttpRequest();
const result = !!req.onreadystatechange;
// restore original desc
api.ObjectDefineProperty(XMLHttpRequestPrototype, ON_READY_STATE_CHANGE, xhrDesc || {});
return result;
} else {
const SYMBOL_FAKE_ONREADYSTATECHANGE = api.symbol('fake');
api.ObjectDefineProperty(XMLHttpRequestPrototype, ON_READY_STATE_CHANGE, {
enumerable: true,
configurable: true,
get: function() {
return this[SYMBOL_FAKE_ONREADYSTATECHANGE];
},
set: function(value) {
this[SYMBOL_FAKE_ONREADYSTATECHANGE] = value;
}
});
const req = new XMLHttpRequest();
const detectFunc = () => {};
req.onreadystatechange = detectFunc;
const result = (req as any)[SYMBOL_FAKE_ONREADYSTATECHANGE] === detectFunc;
req.onreadystatechange = null as any;
return result;
}
}
// Whenever any eventListener fires, we check the eventListener target and all parents
// for `onwhatever` properties and replace them with zone-bound functions
// - Chrome (for now)
function patchViaCapturingAllTheEvents(api: _ZonePrivate) {
const {eventNames} = api.getGlobalObjects()!;
const unboundKey = api.symbol('unbound');
for (let i = 0; i < eventNames.length; i++) {
const property = eventNames[i];
const onproperty = 'on' + property;
self.addEventListener(property, function(event) {
let elt: any = <Node>event.target, bound, source;
if (elt) {
source = elt.constructor['name'] + '.' + onproperty;
} else {
source = 'unknown.' + onproperty;
}
while (elt) {
if (elt[onproperty] && !elt[onproperty][unboundKey]) {
bound = api.wrapWithCurrentZone(elt[onproperty], source);
bound[unboundKey] = elt[onproperty];
elt[onproperty] = bound;
}
elt = elt.parentElement;
}
}, true);
}
}