From ce5f54a48c502770996bf92e4ebcb863e410a854 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Go=C5=82e=CC=A8biowski?= Date: Thu, 17 Nov 2016 14:53:34 +0100 Subject: [PATCH 1/3] chore(*): cleanup msie handling; add support comments 1. The conditions checking the msie variable value have been simplified. There is e.g. no point to check if `msie <= 11` since there IE 12 won't ever exist. 2. Edge UA-sniffing has been added to tests (only!) where appropriate 3. Support comments for IE/Edge have been added. --- src/Angular.js | 1 + src/auto/injector.js | 3 ++- src/ng/compile.js | 1 + src/ng/directive/attrs.js | 5 +++-- src/ng/rootScope.js | 1 + src/ng/sce.js | 1 + src/ng/sniffer.js | 3 ++- src/ng/urlUtils.js | 1 + src/ngScenario/dsl.js | 7 +++++++ test/helpers/matchers.js | 5 +++++ test/ng/compileSpec.js | 10 ++++++++-- test/ng/directive/booleanAttrsSpec.js | 6 ++++-- test/ng/directive/inputSpec.js | 1 + test/ng/locationSpec.js | 16 ++++++++++------ test/ng/parseSpec.js | 3 ++- test/ng/rootScopeSpec.js | 7 ++++--- test/ng/snifferSpec.js | 3 ++- 17 files changed, 55 insertions(+), 19 deletions(-) diff --git a/src/Angular.js b/src/Angular.js index f97bffeac330..038c888ad160 100644 --- a/src/Angular.js +++ b/src/Angular.js @@ -169,6 +169,7 @@ var angularModule, uid = 0; +// Support: IE 9-11 only /** * documentMode is an IE-only property * http://msdn.microsoft.com/en-us/library/ie/cc196988(v=vs.85).aspx diff --git a/src/auto/injector.js b/src/auto/injector.js index 61eac820e035..38d38777ff34 100644 --- a/src/auto/injector.js +++ b/src/auto/injector.js @@ -849,8 +849,9 @@ function createInjector(modulesToLoad, strictDi) { } function isClass(func) { + // Support: IE 9-11 only // IE 9-11 do not support classes and IE9 leaks with the code below. - if (msie <= 11 || typeof func !== 'function') { + if (msie || typeof func !== 'function') { return false; } var result = func.$$ngIsClass; diff --git a/src/ng/compile.js b/src/ng/compile.js index afd0a803b819..160f9e8ccf6d 100644 --- a/src/ng/compile.js +++ b/src/ng/compile.js @@ -1949,6 +1949,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { for (var i = 0; i < nodeList.length; i++) { attrs = new Attributes(); + // Support: IE 11 only // Workaround for #11781 and #14924 if (msie === 11) { mergeConsecutiveTextNodes(nodeList, i, notLiveList); diff --git a/src/ng/directive/attrs.js b/src/ng/directive/attrs.js index adc398425fd9..56d237da74b6 100644 --- a/src/ng/directive/attrs.js +++ b/src/ng/directive/attrs.js @@ -426,10 +426,11 @@ forEach(['src', 'srcset', 'href'], function(attrName) { attr.$set(name, value); - // on IE, if "ng:src" directive declaration is used and "src" attribute doesn't exist + // Support: IE 9-11 only + // On IE, if "ng:src" directive declaration is used and "src" attribute doesn't exist // then calling element.setAttribute('src', 'foo') doesn't do anything, so we need // to set the property as well to achieve the desired effect. - // we use attr[attrName] value since $set can sanitize the url. + // We use attr[attrName] value since $set can sanitize the url. if (msie && propName) element.prop(propName, attr[name]); }); } diff --git a/src/ng/rootScope.js b/src/ng/rootScope.js index cd53ab4dc552..e3d332afefbd 100644 --- a/src/ng/rootScope.js +++ b/src/ng/rootScope.js @@ -105,6 +105,7 @@ function $RootScopeProvider() { function cleanUpScope($scope) { + // Support: IE 9 only if (msie === 9) { // There is a memory leak in IE9 if all child scopes are not disconnected // completely when a scope is destroyed. So this code will recurse up through diff --git a/src/ng/sce.js b/src/ng/sce.js index 0201656c6ea7..70b94ad150ce 100644 --- a/src/ng/sce.js +++ b/src/ng/sce.js @@ -748,6 +748,7 @@ function $SceProvider() { this.$get = ['$parse', '$sceDelegate', function( $parse, $sceDelegate) { + // Support: IE 9-11 only // Prereq: Ensure that we're not running in IE<11 quirks mode. In that mode, IE < 11 allow // the "expression(javascript expression)" syntax which is insecure. if (enabled && msie < 8) { diff --git a/src/ng/sniffer.js b/src/ng/sniffer.js index 217f3ba9d0a7..be6af00029ed 100644 --- a/src/ng/sniffer.js +++ b/src/ng/sniffer.js @@ -57,12 +57,13 @@ function $SnifferProvider() { // We are purposefully using `!(android < 4)` to cover the case when `android` is undefined history: !!(hasHistoryPushState && !(android < 4) && !boxee), hasEvent: function(event) { + // Support: IE 9-11 only // IE9 implements 'input' event it's so fubared that we rather pretend that it doesn't have // it. In particular the event is not fired when backspace or delete key are pressed or // when cut operation is performed. // IE10+ implements 'input' event but it erroneously fires under various situations, // e.g. when placeholder changes, or a form is focused. - if (event === 'input' && msie <= 11) return false; + if (event === 'input' && msie) return false; if (isUndefined(eventSupport[event])) { var divElm = document.createElement('div'); diff --git a/src/ng/urlUtils.js b/src/ng/urlUtils.js index 60a40b7caa87..d8231ef078f3 100644 --- a/src/ng/urlUtils.js +++ b/src/ng/urlUtils.js @@ -58,6 +58,7 @@ var originUrl = urlResolve(window.location.href); function urlResolve(url) { var href = url; + // Support: IE 9-11 only if (msie) { // Normalize before parse. Refer Implementation Notes on why this is // done in two steps on IE. diff --git a/src/ngScenario/dsl.js b/src/ngScenario/dsl.js index 142811b043d1..3849c4515aff 100644 --- a/src/ngScenario/dsl.js +++ b/src/ngScenario/dsl.js @@ -201,6 +201,13 @@ angular.scenario.dsl('binding', function() { */ angular.scenario.dsl('input', function() { var chain = {}; + + // Support: IE 9-11 only + // IE9 implements 'input' event it's so fubared that we rather pretend that it doesn't have + // it. In particular the event is not fired when backspace or delete key are pressed or + // when cut operation is performed. + // IE10+ implements 'input' event but it erroneously fires under various situations, + // e.g. when placeholder changes, or a form is focused. var supportInputEvent = 'oninput' in window.document.createElement('div') && !msie; chain.enter = function(value, event) { diff --git a/test/helpers/matchers.js b/test/helpers/matchers.js index 5a21ec52756d..3ccb10fb0529 100644 --- a/test/helpers/matchers.js +++ b/test/helpers/matchers.js @@ -357,8 +357,11 @@ beforeEach(function() { toBeMarkedAsSelected: function() { // Selected is special because the element property and attribute reflect each other's state. + + // Support: IE 9 only // IE9 will wrongly report hasAttribute('selected') === true when the property is // undefined or null, and the dev tools show that no attribute is set + return { compare: function(actual) { var errors = []; @@ -366,6 +369,7 @@ beforeEach(function() { errors.push('Expected option property "selected" to be truthy'); } + // Support: IE 9 only if (msie !== 9 && actual.hasAttribute('selected') === false) { errors.push('Expected option to have attribute "selected"'); } @@ -383,6 +387,7 @@ beforeEach(function() { errors.push('Expected option property "selected" to be falsy'); } + // Support: IE 9 only if (msie !== 9 && actual.hasAttribute('selected')) { errors.push('Expected option not to have attribute "selected"'); } diff --git a/test/ng/compileSpec.js b/test/ng/compileSpec.js index b537977d2779..fd36210235e9 100644 --- a/test/ng/compileSpec.js +++ b/test/ng/compileSpec.js @@ -1127,7 +1127,8 @@ describe('$compile', function() { expect(element).toHaveClass('class_2'); })); - if (!msie || msie > 11) { + // Support: IE 9-11 only + if (!msie) { // style interpolation not working on IE (including IE11). it('should handle interpolated css style from replacing directive', inject( function($compile, $rootScope) { @@ -10698,6 +10699,8 @@ describe('$compile', function() { expect(element.text()).toBe('102030'); expect(newWatcherCount).toBe(3); + // Support: IE 11 only + // See #11781 and #14924 if (msie === 11) { expect(element.find('ng-transclude').contents().length).toBe(1); } @@ -10719,9 +10722,10 @@ describe('$compile', function() { expect(element.attr('src')).toEqual('http://example.com/image2.png'); })); + // Support: IE 9 only // IE9 rejects the video / audio tag with "Error: Not implemented" and the source tag with // "Unable to get value of the property 'childNodes': object is null or undefined" - if (!msie || msie > 9) { + if (msie !== 9) { they('should NOT require trusted values for $prop src', ['video', 'audio'], function(tag) { inject(function($rootScope, $compile, $sce) { @@ -11154,6 +11158,8 @@ describe('$compile', function() { })); }); + // Support: IE 9-10 only + // IE <=11 don't support srcdoc if (!msie || msie >= 11) { describe('iframe[srcdoc]', function() { it('should NOT set iframe contents for untrusted values', inject(function($compile, $rootScope, $sce) { diff --git a/test/ng/directive/booleanAttrsSpec.js b/test/ng/directive/booleanAttrsSpec.js index ed74c6f99b21..8a655dc09cf7 100644 --- a/test/ng/directive/booleanAttrsSpec.js +++ b/test/ng/directive/booleanAttrsSpec.js @@ -182,6 +182,7 @@ describe('ngSrc', function() { })); + // Support: IE 9-11 only if (msie) { it('should update the element property as well as the attribute', inject( function($compile, $rootScope, $sce) { @@ -283,8 +284,9 @@ describe('ngHref', function() { expect(element.attr('href')).toEqual(undefined); })); - if (msie) { - // IE11/10/Edge fail when setting a href to a URL containing a % that isn't a valid escape sequence + // Support: IE 9-11 only, Edge 12-14+ + if (msie || /\bEdge\/[\d\.]+\b/) { + // IE/Edge fail when setting a href to a URL containing a % that isn't a valid escape sequence // See https://github.com/angular/angular.js/issues/13388 it('should throw error if ng-href contains a non-escaped percent symbol', inject(function($rootScope, $compile) { element = $compile('')($rootScope); diff --git a/test/ng/directive/inputSpec.js b/test/ng/directive/inputSpec.js index 2f329ead4ebf..f810ce6bb78c 100644 --- a/test/ng/directive/inputSpec.js +++ b/test/ng/directive/inputSpec.js @@ -135,6 +135,7 @@ describe('input', function() { describe('IE placeholder input events', function() { + // Support: IE 9-11 only //IE fires an input event whenever a placeholder visually changes, essentially treating it as a value //Events: // placeholder attribute change: *input* diff --git a/test/ng/locationSpec.js b/test/ng/locationSpec.js index c2715db0e599..923687f64693 100644 --- a/test/ng/locationSpec.js +++ b/test/ng/locationSpec.js @@ -39,8 +39,11 @@ describe('$location', function() { /* global urlParsingNode: true */ var urlParsingNodePlaceholder; - beforeEach(inject(function($sniffer) { - if (msie) return; + beforeEach(function() { + // Support: non-Windows browsers + // These tests expect a Windows environment which we can only guarantee + // on IE & Edge. + if (msie || /\bEdge\/[\d\.]+\b/) return; urlParsingNodePlaceholder = urlParsingNode; @@ -57,13 +60,14 @@ describe('$location', function() { search: '', setAttribute: angular.noop }; - })); + }); - afterEach(inject(function($sniffer) { - if (msie) return; + afterEach(function() { + // Support: non-Windows browsers + if (msie || /\bEdge\/[\d\.]+\b/) return; //reset urlParsingNode urlParsingNode = urlParsingNodePlaceholder; - })); + }); it('should not include the drive name in path() on WIN', function() { diff --git a/test/ng/parseSpec.js b/test/ng/parseSpec.js index 252640d7e99d..6be6421da8ce 100644 --- a/test/ng/parseSpec.js +++ b/test/ng/parseSpec.js @@ -2180,8 +2180,9 @@ describe('parser', function() { expect(scope.$eval('getter()()')).toBe(33); }); + // Support: IE 9 only // There is no "strict mode" in IE9 - if (!msie || msie > 9) { + if (msie !== 9) { it('should set no context to functions returned by other functions', function() { scope.getter = function() { return function() { expect(this).toBeUndefined(); }; }; scope.$eval('getter()()'); diff --git a/test/ng/rootScopeSpec.js b/test/ng/rootScopeSpec.js index 737fba9fb986..73660a99746d 100644 --- a/test/ng/rootScopeSpec.js +++ b/test/ng/rootScopeSpec.js @@ -13,9 +13,7 @@ describe('Scope', function() { it('should expose the constructor', inject(function($rootScope) { - if (msie < 11) return; - // eslint-disable-next-line no-proto - expect($rootScope.__proto__).toBe($rootScope.constructor.prototype); + expect(Object.getPrototypeOf($rootScope)).toBe($rootScope.constructor.prototype); })); @@ -125,6 +123,8 @@ describe('Scope', function() { function Listener() { expect(this).toBeUndefined(); } + // Support: IE 9 only + // IE 9 doesn't support strict mode so its `this` will always be defined. if (msie < 10) return; $rootScope.$watch(Getter, Listener); $rootScope.$digest(); @@ -1227,6 +1227,7 @@ describe('Scope', function() { })); + // Support: IE 9 only if (msie === 9) { // See issue https://github.com/angular/angular.js/issues/10706 it('should completely disconnect all child scopes on IE9', inject(function($rootScope) { diff --git a/test/ng/snifferSpec.js b/test/ng/snifferSpec.js index 9ab3a7315ebe..50f90ec842c7 100644 --- a/test/ng/snifferSpec.js +++ b/test/ng/snifferSpec.js @@ -153,11 +153,12 @@ describe('$sniffer', function() { it('should claim that IE9 doesn\'t have support for "oninput"', function() { + // Support: IE 9-11 only // IE9 implementation is fubared, so it's better to pretend that it doesn't have the support // IE10+ implementation is fubared when mixed with placeholders mockDivElement = {oninput: noop}; - expect($sniffer.hasEvent('input')).toBe(!(msie && msie <= 11)); + expect($sniffer.hasEvent('input')).toBe(!msie); }); }); From d297d8ee5d363d7c280ec6bf68d6372d32828848 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Go=C5=82e=CC=A8biowski?= Date: Thu, 17 Nov 2016 23:30:46 +0100 Subject: [PATCH 2/3] fixup! chore(*): cleanup msie handling; add support comments --- src/Angular.js | 1 + test/ng/compileSpec.js | 4 ++++ test/ng/directive/booleanAttrsSpec.js | 2 +- test/ng/locationSpec.js | 4 ++-- 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/Angular.js b/src/Angular.js index 038c888ad160..48cdb7b2c138 100644 --- a/src/Angular.js +++ b/src/Angular.js @@ -1280,6 +1280,7 @@ function fromJson(json) { var ALL_COLONS = /:/g; function timezoneToOffset(timezone, fallback) { + // Support: IE 9-11 only, Edge 13-14+ // IE/Edge do not "understand" colon (`:`) in timezone timezone = timezone.replace(ALL_COLONS, ''); var requestedTimezoneOffset = Date.parse('Jan 01, 1970 00:00:00 ' + timezone) / 60000; diff --git a/test/ng/compileSpec.js b/test/ng/compileSpec.js index fd36210235e9..e3a15b53ec6b 100644 --- a/test/ng/compileSpec.js +++ b/test/ng/compileSpec.js @@ -469,6 +469,10 @@ describe('$compile', function() { })); // NOTE: This test may be redundant. + // Support: Edge 14+ + // An `` element inside a `` element on MS Edge has no + // size, causing the included `` element to also have no size and thus fails an + // assertion (relying on the element having a non-zero size). if (!isEdge) { it('should handle custom svg containers that transclude to foreignObject' + ' that transclude to custom svg containers that transclude to custom elements', inject(function() { diff --git a/test/ng/directive/booleanAttrsSpec.js b/test/ng/directive/booleanAttrsSpec.js index 8a655dc09cf7..b146c1aae705 100644 --- a/test/ng/directive/booleanAttrsSpec.js +++ b/test/ng/directive/booleanAttrsSpec.js @@ -285,7 +285,7 @@ describe('ngHref', function() { })); // Support: IE 9-11 only, Edge 12-14+ - if (msie || /\bEdge\/[\d\.]+\b/) { + if (msie || /\bEdge\/[\d\.]+\b/.test(window.navigator.userAgent)) { // IE/Edge fail when setting a href to a URL containing a % that isn't a valid escape sequence // See https://github.com/angular/angular.js/issues/13388 it('should throw error if ng-href contains a non-escaped percent symbol', inject(function($rootScope, $compile) { diff --git a/test/ng/locationSpec.js b/test/ng/locationSpec.js index 923687f64693..4780decef95d 100644 --- a/test/ng/locationSpec.js +++ b/test/ng/locationSpec.js @@ -43,7 +43,7 @@ describe('$location', function() { // Support: non-Windows browsers // These tests expect a Windows environment which we can only guarantee // on IE & Edge. - if (msie || /\bEdge\/[\d\.]+\b/) return; + if (msie || /\bEdge\/[\d\.]+\b/.test(window.navigator.userAgent)) return; urlParsingNodePlaceholder = urlParsingNode; @@ -64,7 +64,7 @@ describe('$location', function() { afterEach(function() { // Support: non-Windows browsers - if (msie || /\bEdge\/[\d\.]+\b/) return; + if (msie || /\bEdge\/[\d\.]+\b/.test(window.navigator.userAgent)) return; //reset urlParsingNode urlParsingNode = urlParsingNodePlaceholder; }); From fa8eb15c3f2803435f5a58ed84bffb7fba6fd6d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Go=C5=82e=CC=A8biowski?= Date: Fri, 18 Nov 2016 15:00:12 +0100 Subject: [PATCH 3/3] fixup! chore(*): cleanup msie handling; add support comments --- test/ng/compileSpec.js | 4 ++-- test/ng/rootScopeSpec.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/ng/compileSpec.js b/test/ng/compileSpec.js index e3a15b53ec6b..91c5d02b9876 100644 --- a/test/ng/compileSpec.js +++ b/test/ng/compileSpec.js @@ -11163,8 +11163,8 @@ describe('$compile', function() { }); // Support: IE 9-10 only - // IE <=11 don't support srcdoc - if (!msie || msie >= 11) { + // IEs <11 don't support srcdoc + if (!msie || msie === 11) { describe('iframe[srcdoc]', function() { it('should NOT set iframe contents for untrusted values', inject(function($compile, $rootScope, $sce) { element = $compile('')($rootScope); diff --git a/test/ng/rootScopeSpec.js b/test/ng/rootScopeSpec.js index 73660a99746d..66a5eeeed3e7 100644 --- a/test/ng/rootScopeSpec.js +++ b/test/ng/rootScopeSpec.js @@ -125,7 +125,7 @@ describe('Scope', function() { } // Support: IE 9 only // IE 9 doesn't support strict mode so its `this` will always be defined. - if (msie < 10) return; + if (msie === 9) return; $rootScope.$watch(Getter, Listener); $rootScope.$digest(); }));