Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

Commit a6339d3

Browse files
randing89lgalfaso
authored andcommitted
fix($compile): exception when using "watch" as isolated scope binding variable in Firefox
Fix on all binding modes: '=', '@' and '&' as well as optional cases Throw exception when user define 'hasOwnProperty' in binding. Closes #11627
1 parent 351fe4b commit a6339d3

File tree

2 files changed

+162
-3
lines changed

2 files changed

+162
-3
lines changed

src/ng/compile.js

+11-3
Original file line numberDiff line numberDiff line change
@@ -2551,9 +2551,19 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
25512551
lastValue,
25522552
parentGet, parentSet, compare;
25532553

2554+
if (!hasOwnProperty.call(attrs, attrName)) {
2555+
// In the case of user defined a binding with the same name as a method in Object.prototype but didn't set
2556+
// the corresponding attribute. We need to make sure subsequent code won't access to the prototype function
2557+
attrs[attrName] = undefined;
2558+
}
2559+
25542560
switch (mode) {
25552561

25562562
case '@':
2563+
if (!attrs[attrName] && !optional) {
2564+
destination[scopeName] = undefined;
2565+
}
2566+
25572567
attrs.$observe(attrName, function(value) {
25582568
destination[scopeName] = value;
25592569
});
@@ -2570,6 +2580,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
25702580
return;
25712581
}
25722582
parentGet = $parse(attrs[attrName]);
2583+
25732584
if (parentGet.literal) {
25742585
compare = equals;
25752586
} else {
@@ -2608,9 +2619,6 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
26082619
break;
26092620

26102621
case '&':
2611-
// Don't assign Object.prototype method to scope
2612-
if (!attrs.hasOwnProperty(attrName) && optional) break;
2613-
26142622
parentGet = $parse(attrs[attrName]);
26152623

26162624
// Don't assign noop to destination if expression is not valid

test/ng/compileSpec.js

+151
Original file line numberDiff line numberDiff line change
@@ -2231,6 +2231,45 @@ describe('$compile', function() {
22312231
}}
22322232
};
22332233
});
2234+
directive('prototypeMethodNameAsScopeVarA', function() {
2235+
return {
2236+
scope: {
2237+
'constructor': '=?',
2238+
'valueOf': '='
2239+
},
2240+
restrict: 'AE',
2241+
template: '<span></span>'
2242+
};
2243+
});
2244+
directive('prototypeMethodNameAsScopeVarB', function() {
2245+
return {
2246+
scope: {
2247+
'constructor': '@?',
2248+
'valueOf': '@'
2249+
},
2250+
restrict: 'AE',
2251+
template: '<span></span>'
2252+
};
2253+
});
2254+
directive('prototypeMethodNameAsScopeVarC', function() {
2255+
return {
2256+
scope: {
2257+
'constructor': '&?',
2258+
'valueOf': '&'
2259+
},
2260+
restrict: 'AE',
2261+
template: '<span></span>'
2262+
};
2263+
});
2264+
directive('watchAsScopeVar', function() {
2265+
return {
2266+
scope: {
2267+
'watch': '='
2268+
},
2269+
restrict: 'AE',
2270+
template: '<span></span>'
2271+
};
2272+
});
22342273
}));
22352274

22362275

@@ -2472,6 +2511,118 @@ describe('$compile', function() {
24722511
expect(element.isolateScope()).not.toBe($rootScope);
24732512
})
24742513
);
2514+
2515+
it('should handle "=" bindings with same method names in Object.prototype correctly when not present', inject(
2516+
function($rootScope, $compile) {
2517+
var func = function() {
2518+
element = $compile(
2519+
'<div prototype-method-name-as-scope-var-a></div>'
2520+
)($rootScope);
2521+
};
2522+
2523+
expect(func).not.toThrow();
2524+
expect(element.find('span').scope()).toBe(element.isolateScope());
2525+
expect(element.isolateScope()).not.toBe($rootScope);
2526+
expect(element.isolateScope()['constructor']).toBe($rootScope.constructor);
2527+
expect(element.isolateScope()['valueOf']).toBeUndefined();
2528+
})
2529+
);
2530+
2531+
it('should handle "=" bindings with same method names in Object.prototype correctly when present', inject(
2532+
function($rootScope, $compile) {
2533+
$rootScope.constructor = 'constructor';
2534+
$rootScope.valueOf = 'valueOf';
2535+
var func = function() {
2536+
element = $compile(
2537+
'<div prototype-method-name-as-scope-var-a constructor="constructor" value-of="valueOf"></div>'
2538+
)($rootScope);
2539+
};
2540+
2541+
expect(func).not.toThrow();
2542+
expect(element.find('span').scope()).toBe(element.isolateScope());
2543+
expect(element.isolateScope()).not.toBe($rootScope);
2544+
expect(element.isolateScope()['constructor']).toBe('constructor');
2545+
expect(element.isolateScope()['valueOf']).toBe('valueOf');
2546+
})
2547+
);
2548+
2549+
it('should handle "@" bindings with same method names in Object.prototype correctly when not present', inject(
2550+
function($rootScope, $compile) {
2551+
var func = function() {
2552+
element = $compile('<div prototype-method-name-as-scope-var-b></div>')($rootScope);
2553+
};
2554+
2555+
expect(func).not.toThrow();
2556+
expect(element.find('span').scope()).toBe(element.isolateScope());
2557+
expect(element.isolateScope()).not.toBe($rootScope);
2558+
expect(element.isolateScope()['constructor']).toBe($rootScope.constructor);
2559+
expect(element.isolateScope()['valueOf']).toBeUndefined();
2560+
})
2561+
);
2562+
2563+
it('should handle "@" bindings with same method names in Object.prototype correctly when present', inject(
2564+
function($rootScope, $compile) {
2565+
var func = function() {
2566+
element = $compile(
2567+
'<div prototype-method-name-as-scope-var-b constructor="constructor" value-of="valueOf"></div>'
2568+
)($rootScope);
2569+
};
2570+
2571+
expect(func).not.toThrow();
2572+
expect(element.find('span').scope()).toBe(element.isolateScope());
2573+
expect(element.isolateScope()).not.toBe($rootScope);
2574+
expect(element.isolateScope()['constructor']).toBe('constructor');
2575+
expect(element.isolateScope()['valueOf']).toBe('valueOf');
2576+
})
2577+
);
2578+
2579+
it('should handle "&" bindings with same method names in Object.prototype correctly when not present', inject(
2580+
function($rootScope, $compile) {
2581+
var func = function() {
2582+
element = $compile('<div prototype-method-name-as-scope-var-c></div>')($rootScope);
2583+
};
2584+
2585+
expect(func).not.toThrow();
2586+
expect(element.find('span').scope()).toBe(element.isolateScope());
2587+
expect(element.isolateScope()).not.toBe($rootScope);
2588+
expect(element.isolateScope()['constructor']).toBe($rootScope.constructor);
2589+
expect(element.isolateScope()['valueOf']()).toBeUndefined();
2590+
})
2591+
);
2592+
2593+
it('should handle "&" bindings with same method names in Object.prototype correctly when present', inject(
2594+
function($rootScope, $compile) {
2595+
$rootScope.constructor = function() { return 'constructor'; };
2596+
$rootScope.valueOf = function() { return 'valueOf'; };
2597+
var func = function() {
2598+
element = $compile(
2599+
'<div prototype-method-name-as-scope-var-c constructor="constructor()" value-of="valueOf()"></div>'
2600+
)($rootScope);
2601+
};
2602+
2603+
expect(func).not.toThrow();
2604+
expect(element.find('span').scope()).toBe(element.isolateScope());
2605+
expect(element.isolateScope()).not.toBe($rootScope);
2606+
expect(element.isolateScope()['constructor']()).toBe('constructor');
2607+
expect(element.isolateScope()['valueOf']()).toBe('valueOf');
2608+
})
2609+
);
2610+
2611+
it('should not throw exception when using "watch" as binding in Firefox', inject(
2612+
function($rootScope, $compile) {
2613+
$rootScope.watch = 'watch';
2614+
var func = function() {
2615+
element = $compile(
2616+
'<div watch-as-scope-var watch="watch"></div>'
2617+
)($rootScope);
2618+
};
2619+
2620+
expect(func).not.toThrow();
2621+
expect(element.find('span').scope()).toBe(element.isolateScope());
2622+
expect(element.isolateScope()).not.toBe($rootScope);
2623+
expect(element.isolateScope()['watch']).toBe('watch');
2624+
})
2625+
);
24752626
});
24762627

24772628

0 commit comments

Comments
 (0)