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

Commit f547b0d

Browse files
committed
fixup! feat($compile): add support for arbitrary property and event bindings
1 parent 3e94bd1 commit f547b0d

File tree

2 files changed

+133
-8
lines changed

2 files changed

+133
-8
lines changed

src/ng/compile.js

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1620,7 +1620,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
16201620
*
16211621
* Copy of https://github.com/angular/angular/blob/6.0.6/packages/compiler/src/schema/dom_security_schema.ts#L31-L58
16221622
* Changing:
1623-
* - SecurityContext.* => $sce.*
1623+
* - SecurityContext.* => SCE_CONTEXTS/$sce.*
16241624
* - STYLE => CSS
16251625
* - various URL => MEDIA_URL
16261626
*/
@@ -3488,13 +3488,17 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
34883488
directives.push({
34893489
priority: 100,
34903490
compile: function ngPropCompileFn(_, attr) {
3491-
var fn = $parse(attr[attrName]);
3492-
return {
3493-
pre: function ngPropPreLinkFn(scope, $element) {
3494-
scope.$watch(fn, function propertyWatchActionFn(value) {
3495-
$element.prop(propName, sanitizer(value));
3496-
});
3497-
}
3491+
var ngPropGetter = $parse(attr[attrName]);
3492+
var ngPropWatch = $parse(attr[attrName], function sceValueOf(val) {
3493+
// Unwrap the value to compare the actual inner safe value, not the wrapper object.
3494+
return $sce.valueOf(val);
3495+
});
3496+
3497+
return function ngPropPreLinkFn(scope, $element) {
3498+
scope.$watch(ngPropWatch, function propertyWatchActionFn() {
3499+
var value = ngPropGetter(scope);
3500+
$element.prop(propName, sanitizer(value));
3501+
});
34983502
};
34993503
}
35003504
});

test/ng/compileSpec.js

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12719,6 +12719,127 @@ describe('$compile', function() {
1271912719
$rootScope.$digest();
1272012720
}));
1272112721
});
12722+
12723+
describe('*[innerHTML]', function() {
12724+
describe('SCE disabled', function() {
12725+
beforeEach(function() {
12726+
module(function($sceProvider) { $sceProvider.enabled(false); });
12727+
});
12728+
12729+
it('should set html', inject(function($rootScope, $compile) {
12730+
element = $compile('<div ng-prop-inner_h_t_m_l="html"></div>')($rootScope);
12731+
$rootScope.html = '<div onclick="">hello</div>';
12732+
$rootScope.$digest();
12733+
expect(lowercase(element.html())).toEqual('<div onclick="">hello</div>');
12734+
}));
12735+
12736+
it('should update html', inject(function($rootScope, $compile, $sce) {
12737+
element = $compile('<div ng-prop-inner_h_t_m_l="html"></div>')($rootScope);
12738+
$rootScope.html = 'hello';
12739+
$rootScope.$digest();
12740+
expect(lowercase(element.html())).toEqual('hello');
12741+
$rootScope.html = 'goodbye';
12742+
$rootScope.$digest();
12743+
expect(lowercase(element.html())).toEqual('goodbye');
12744+
}));
12745+
12746+
it('should one-time bind if the expression starts with two colons', inject(function($rootScope, $compile) {
12747+
element = $compile('<div ng-prop-inner_h_t_m_l="::html"></div>')($rootScope);
12748+
$rootScope.html = '<div onclick="">hello</div>';
12749+
expect($rootScope.$$watchers.length).toEqual(1);
12750+
$rootScope.$digest();
12751+
expect(element.text()).toEqual('hello');
12752+
expect($rootScope.$$watchers.length).toEqual(0);
12753+
$rootScope.html = '<div onclick="">hello</div>';
12754+
$rootScope.$digest();
12755+
expect(element.text()).toEqual('hello');
12756+
}));
12757+
});
12758+
12759+
12760+
describe('SCE enabled', function() {
12761+
it('should NOT set html for untrusted values', inject(function($rootScope, $compile) {
12762+
element = $compile('<div ng-prop-inner_h_t_m_l="html"></div>')($rootScope);
12763+
$rootScope.html = '<div onclick="">hello</div>';
12764+
expect(function() { $rootScope.$digest(); }).toThrow();
12765+
}));
12766+
12767+
it('should NOT set html for wrongly typed values', inject(function($rootScope, $compile, $sce) {
12768+
element = $compile('<div ng-prop-inner_h_t_m_l="html"></div>')($rootScope);
12769+
$rootScope.html = $sce.trustAsCss('<div onclick="">hello</div>');
12770+
expect(function() { $rootScope.$digest(); }).toThrow();
12771+
}));
12772+
12773+
it('should set html for trusted values', inject(function($rootScope, $compile, $sce) {
12774+
element = $compile('<div ng-prop-inner_h_t_m_l="html"></div>')($rootScope);
12775+
$rootScope.html = $sce.trustAsHtml('<div onclick="">hello</div>');
12776+
$rootScope.$digest();
12777+
expect(lowercase(element.html())).toEqual('<div onclick="">hello</div>');
12778+
}));
12779+
12780+
it('should update html', inject(function($rootScope, $compile, $sce) {
12781+
element = $compile('<div ng-prop-inner_h_t_m_l="html"></div>')($rootScope);
12782+
$rootScope.html = $sce.trustAsHtml('hello');
12783+
$rootScope.$digest();
12784+
expect(lowercase(element.html())).toEqual('hello');
12785+
$rootScope.html = $sce.trustAsHtml('goodbye');
12786+
$rootScope.$digest();
12787+
expect(lowercase(element.html())).toEqual('goodbye');
12788+
}));
12789+
12790+
it('should not cause infinite recursion for trustAsHtml object watches',
12791+
inject(function($rootScope, $compile, $sce) {
12792+
// Ref: https://github.com/angular/angular.js/issues/3932
12793+
// If the binding is a function that creates a new value on every call via trustAs, we'll
12794+
// trigger an infinite digest if we don't take care of it.
12795+
element = $compile('<div ng-prop-inner_h_t_m_l="getHtml()"></div>')($rootScope);
12796+
$rootScope.getHtml = function() {
12797+
return $sce.trustAsHtml('<div onclick="">hello</div>');
12798+
};
12799+
$rootScope.$digest();
12800+
expect(lowercase(element.html())).toEqual('<div onclick="">hello</div>');
12801+
}));
12802+
12803+
it('should handle custom $sce objects', function() {
12804+
function MySafeHtml(val) { this.val = val; }
12805+
12806+
module(function($provide) {
12807+
$provide.decorator('$sce', function($delegate) {
12808+
$delegate.trustAsHtml = function(html) { return new MySafeHtml(html); };
12809+
$delegate.getTrusted = function(type, mySafeHtml) { return mySafeHtml.val; };
12810+
$delegate.valueOf = function(v) { return v instanceof MySafeHtml ? v.val : v; };
12811+
return $delegate;
12812+
});
12813+
});
12814+
12815+
inject(function($rootScope, $compile, $sce) {
12816+
// Ref: https://github.com/angular/angular.js/issues/14526
12817+
// Previous code used toString for change detection, which fails for custom objects
12818+
// that don't override toString.
12819+
element = $compile('<div ng-prop-inner_h_t_m_l="getHtml()"></div>')($rootScope);
12820+
var html = 'hello';
12821+
$rootScope.getHtml = function() { return $sce.trustAsHtml(html); };
12822+
$rootScope.$digest();
12823+
expect(lowercase(element.html())).toEqual('hello');
12824+
html = 'goodbye';
12825+
$rootScope.$digest();
12826+
expect(lowercase(element.html())).toEqual('goodbye');
12827+
});
12828+
});
12829+
12830+
describe('when $sanitize is available', function() {
12831+
beforeEach(function() { module('ngSanitize'); });
12832+
12833+
it('should sanitize untrusted html', inject(function($rootScope, $compile) {
12834+
element = $compile('<div ng-prop-inner_h_t_m_l="html"></div>')($rootScope);
12835+
$rootScope.html = '<div onclick="">hello</div>';
12836+
$rootScope.$digest();
12837+
expect(lowercase(element.html())).toEqual('<div>hello</div>');
12838+
}));
12839+
});
12840+
});
12841+
12842+
})
1272212843
});
1272312844

1272412845
describe('addPropertySecurityContext', function() {

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy