diff --git a/NON-STANDARD-APIS.md b/NON-STANDARD-APIS.md index 4e4dbe9a7..8cca25bdd 100644 --- a/NON-STANDARD-APIS.md +++ b/NON-STANDARD-APIS.md @@ -170,7 +170,7 @@ In electron, we patched the following APIs with `zone.js` ## Usage. -add/update following line into `polyfill.ts`. +add following line into `polyfill.ts` after loading zone-mix. ``` //import 'zone.js/dist/zone'; // originally added by angular-cli, comment it out @@ -194,7 +194,6 @@ user need to patch `io` themselves just like following code. ``` - please reference the sample repo [zone-socketio](https://github.com/JiaLiPassion/zone-socketio) about detail usage. @@ -217,3 +216,14 @@ Zone['__zone_symbol__jsonp']({ failedFuncName: 'jsonpFailedCallback' }); ``` +* ResizeObserver + +Currently only `Chrome 64` native support this feature. +you can add the following line into `polyfill.ts` after loading `zone.js`. + +``` +import 'zone.js/dist/zone'; +import 'zone.js/dist/zone-patch-resize-observer'; +``` + +there is a sample repo [zone-resize-observer](https://github.com/JiaLiPassion/zone-resize-observer) here diff --git a/gulpfile.js b/gulpfile.js index cae2fc587..b3f63b29a 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -228,6 +228,14 @@ gulp.task('build/zone-patch-promise-testing.min.js', ['compile-esm'], function(c return generateScript('./lib/testing/promise-testing.ts', 'zone-patch-promise-test.min.js', true, cb); }); +gulp.task('build/zone-patch-resize-observer.js', ['compile-esm'], function(cb) { + return generateScript('./lib/browser/webapis-resize-observer.ts', 'zone-patch-resize-observer.js', false, cb); +}); + +gulp.task('build/zone-patch-resize-observer.min.js', ['compile-esm'], function(cb) { + return generateScript('./lib/browser/webapis-resize-observer.ts', 'zone-patch-resize-observer.min.js', true, cb); +}); + gulp.task('build/bluebird.js', ['compile-esm'], function(cb) { return generateScript('./lib/extra/bluebird.ts', 'zone-bluebird.js', false, cb); }); @@ -354,6 +362,8 @@ gulp.task('build', [ 'build/zone-patch-socket-io.min.js', 'build/zone-patch-promise-testing.js', 'build/zone-patch-promise-testing.min.js', + 'build/zone-patch-resize-observer.js', + 'build/zone-patch-resize-observer.min.js', 'build/zone-mix.js', 'build/bluebird.js', 'build/bluebird.min.js', diff --git a/lib/browser/webapis-resize-observer.ts b/lib/browser/webapis-resize-observer.ts new file mode 100644 index 000000000..cfaa70f0b --- /dev/null +++ b/lib/browser/webapis-resize-observer.ts @@ -0,0 +1,88 @@ +/** + * @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 + */ +Zone.__load_patch('ResizeObserver', (global: any, Zone: any, api: _ZonePrivate) => { + const ResizeObserver = global['ResizeObserver']; + if (!ResizeObserver) { + return; + } + + const resizeObserverSymbol = api.symbol('ResizeObserver'); + + api.patchMethod(global, 'ResizeObserver', (delegate: Function) => (self: any, args: any[]) => { + const callback = args.length > 0 ? args[0] : null; + if (callback) { + args[0] = function(entries: any, observer: any) { + const zones: {[zoneName: string]: any} = {}; + const currZone = Zone.current; + for (let entry of entries) { + let zone = entry.target[resizeObserverSymbol]; + if (!zone) { + zone = currZone; + } + let zoneEntriesInfo = zones[zone.name]; + if (!zoneEntriesInfo) { + zones[zone.name] = zoneEntriesInfo = {entries: [], zone: zone}; + } + zoneEntriesInfo.entries.push(entry); + } + + Object.keys(zones).forEach(zoneName => { + const zoneEntriesInfo = zones[zoneName]; + if (zoneEntriesInfo.zone !== Zone.current) { + zoneEntriesInfo.zone.run( + callback, this, [zoneEntriesInfo.entries, observer], 'ResizeObserver'); + } else { + callback.call(this, zoneEntriesInfo.entries, observer); + } + }); + }; + } + return args.length > 0 ? new ResizeObserver(args[0]) : new ResizeObserver(); + }); + + api.patchMethod(ResizeObserver.prototype, 'observe', (delegate: Function) => (self: any, args: any[]) => { + const target = args.length > 0 ? args[0] : null; + if (!target) { + return delegate.apply(self, args); + } + let targets = self[resizeObserverSymbol]; + if (!targets) { + targets = self[resizeObserverSymbol] = []; + } + targets.push(target); + target[resizeObserverSymbol] = Zone.current; + return delegate.apply(self, args); + }); + + api.patchMethod(ResizeObserver.prototype, 'unobserve', (delegate: Function) => (self: any, args: any[]) => { + const target = args.length > 0 ? args[0] : null; + if (!target) { + return delegate.apply(self, args); + } + let targets = self[resizeObserverSymbol]; + if (targets) { + for (let i = 0; i < targets.length; i ++) { + if (targets[i] === target) { + targets.splice(i, 1); + break; + } + } + } + target[resizeObserverSymbol] = undefined; + return delegate.apply(self, args); + }); + + api.patchMethod(ResizeObserver.prototype, 'disconnect', (delegate: Function) => (self: any, args: any[]) => { + const targets = self[resizeObserverSymbol]; + if (targets) { + targets.forEach((target: any) => {target[resizeObserverSymbol] = undefined;}); + self[resizeObserverSymbol] = undefined; + } + return delegate.apply(self, args); + }); +}); diff --git a/test/browser-zone-setup.ts b/test/browser-zone-setup.ts index 2e6974bb2..476ffdb0d 100644 --- a/test/browser-zone-setup.ts +++ b/test/browser-zone-setup.ts @@ -20,3 +20,4 @@ import '../lib/zone-spec/task-tracking'; import '../lib/zone-spec/wtf'; import '../lib/extra/cordova'; import '../lib/testing/promise-testing'; +import '../lib/browser/webapis-resize-observer'; diff --git a/test/browser/browser.spec.ts b/test/browser/browser.spec.ts index 82845f2ae..6f312eafe 100644 --- a/test/browser/browser.spec.ts +++ b/test/browser/browser.spec.ts @@ -2470,6 +2470,62 @@ describe('Zone', function() { }); })); + describe('ResizeObserver', ifEnvSupports('ResizeObserver', () => { + it('ResizeObserver callback should be in zone', (done) => { + const ResizeObserver = (window as any)['ResizeObserver']; + const div = document.createElement('div'); + const zone = Zone.current.fork({ + name: 'observer' + }); + const observer = new ResizeObserver((entries: any, ob: any) => { + expect(Zone.current.name).toEqual(zone.name); + + expect(entries.length).toBe(1); + expect(entries[0].target).toBe(div); + done(); + }); + + zone.run(() => { + observer.observe(div); + }); + + document.body.appendChild(div); + }); + + it('ResizeObserver callback should be able to in different zones which when they were observed', (done) => { + const ResizeObserver = (window as any)['ResizeObserver']; + const div1 = document.createElement('div'); + const div2 = document.createElement('div'); + const zone = Zone.current.fork({ + name: 'observer' + }); + let count = 0; + const observer = new ResizeObserver((entries: any, ob: any) => { + entries.forEach((entry: any) => { + if (entry.target === div1) { + expect(Zone.current.name).toEqual(zone.name); + } else { + expect(Zone.current.name).toEqual(''); + } + }); + count ++; + if (count === 2) { + done(); + } + }); + + zone.run(() => { + observer.observe(div1); + }); + Zone.root.run(() => { + observer.observe(div2); + }); + + document.body.appendChild(div1); + document.body.appendChild(div2); + }); + })); + xdescribe('getUserMedia', () => { it('navigator.mediaDevices.getUserMedia should in zone', ifEnvSupportsWithDone( @@ -2516,6 +2572,7 @@ describe('Zone', function() { }); }); })); + }); }); });