| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136 |
- /**
- * @license
- * Copyright Google Inc. 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);
- }
- }
|