@@ -1587,6 +1587,84 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
1587
1587
return cssClassDirectivesEnabledConfig ;
1588
1588
} ;
1589
1589
1590
+
1591
+ /**
1592
+ * The security context of DOM Properties.
1593
+ * @private
1594
+ */
1595
+ var PROP_CONTEXTS = Object . create ( null ) ;
1596
+
1597
+ /**
1598
+ * @ngdoc method
1599
+ * @name $compileProvider#addPropertyContext
1600
+ * @description
1601
+ *
1602
+ * Defines the security context for HTML properties bound by ng-prop-*
1603
+ *
1604
+ * @param {string } the context type
1605
+ * @param {string } the element name or '*' to match any element
1606
+ * @param {string } the property name
1607
+ */
1608
+ this . addPropertyContext = function ( ctx , elementName , propertyName ) {
1609
+ PROP_CONTEXTS [ ( elementName . toLowerCase ( ) + '|' + propertyName . toLowerCase ( ) ) ] = ctx ;
1610
+ } ;
1611
+
1612
+ /* Default property contexts.
1613
+ *
1614
+ * Copy of https://github.com/angular/angular/blob/6.0.6/packages/compiler/src/schema/dom_security_schema.ts#L31-L58
1615
+ * Changing:
1616
+ * - SecurityContext.* => $sce.*
1617
+ * - STYLE => CSS
1618
+ * - various URL => MEDIA_URL
1619
+ */
1620
+ ( function setupPropertyContexts ( ) {
1621
+ function registerContext ( ctx , values ) {
1622
+ forEach ( values , function ( v ) { PROP_CONTEXTS [ v . toLowerCase ( ) ] = ctx ; } ) ;
1623
+ }
1624
+
1625
+ registerContext ( SCE_CONTEXTS . HTML , [
1626
+ 'iframe|srcdoc' ,
1627
+ '*|innerHTML' ,
1628
+ '*|outerHTML' ,
1629
+ ] ) ;
1630
+ registerContext ( SCE_CONTEXTS . CSS , [ '*|style' ] ) ;
1631
+ registerContext ( SCE_CONTEXTS . URL , [
1632
+ '*|formAction' ,
1633
+ 'area|href' , 'area|ping' ,
1634
+ 'a|href' , 'a|ping' ,
1635
+ 'blockquote|cite' ,
1636
+ 'body|background' ,
1637
+ 'del|cite' ,
1638
+ 'form|action' ,
1639
+ 'input|src' ,
1640
+ 'ins|cite' ,
1641
+ 'q|cite' ,
1642
+ 'script|src' ,
1643
+ 'video|poster'
1644
+ ] ) ;
1645
+ registerContext ( SCE_CONTEXTS . MEDIA_URL , [
1646
+ 'audio|src' ,
1647
+ 'img|src' , 'img|srcset' ,
1648
+ 'source|src' , 'source|srcset' ,
1649
+ 'track|src' ,
1650
+ 'video|src'
1651
+ ] ) ;
1652
+ registerContext ( SCE_CONTEXTS . RESOURCE_URL , [
1653
+ 'applet|code' , 'applet|codebase' ,
1654
+ 'base|href' ,
1655
+ 'embed|src' ,
1656
+ 'frame|src' ,
1657
+ 'head|profile' ,
1658
+ 'html|manifest' ,
1659
+ 'iframe|src' ,
1660
+ 'link|href' ,
1661
+ 'media|src' ,
1662
+ 'object|codebase' , 'object|data' ,
1663
+ 'script|src' ,
1664
+ ] ) ;
1665
+ } ) ( ) ;
1666
+
1667
+
1590
1668
this . $get = [
1591
1669
'$injector' , '$interpolate' , '$exceptionHandler' , '$templateRequest' , '$parse' ,
1592
1670
'$controller' , '$rootScope' , '$sce' , '$animate' ,
@@ -1632,12 +1710,12 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
1632
1710
}
1633
1711
1634
1712
1635
- function sanitizeSrcset ( value ) {
1713
+ function sanitizeSrcset ( value , invokeType ) {
1636
1714
if ( ! value ) {
1637
1715
return value ;
1638
1716
}
1639
1717
if ( ! isString ( value ) ) {
1640
- throw $compileMinErr ( 'srcset' , 'Can\'t pass trusted values to `$set(\'srcset\', value) `: "{0}"' , value . toString ( ) ) ;
1718
+ throw $compileMinErr ( 'srcset' , 'Can\'t pass trusted values to `' + invokeType + ' `: "{0}"', value . toString ( ) ) ;
1641
1719
}
1642
1720
1643
1721
// Such values are a bit too complex to handle automatically inside $sce.
@@ -1820,7 +1898,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
1820
1898
1821
1899
// Sanitize img[srcset] + source[srcset] values.
1822
1900
if ( ( nodeName === 'img' || nodeName === 'source' ) && key === 'srcset' ) {
1823
- this [ key ] = value = sanitizeSrcset ( value ) ;
1901
+ this [ key ] = value = sanitizeSrcset ( value , '$set(\'srcset\', value)' ) ;
1824
1902
}
1825
1903
1826
1904
if ( writeAttr !== false ) {
@@ -1917,7 +1995,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
1917
1995
: function denormalizeTemplate ( template ) {
1918
1996
return template . replace ( / \{ \{ / g, startSymbol ) . replace ( / } } / g, endSymbol ) ;
1919
1997
} ,
1920
- NG_ATTR_BINDING = / ^ n g A t t r [ A - Z ] / ;
1998
+ NG_PREFIX_BINDING = / ^ n g ( A t t r | P r o p | O n ) ( [ A - Z ] . * ) $ / ;
1921
1999
var MULTI_ELEMENT_DIR_RE = / ^ ( .+ ) S t a r t $ / ;
1922
2000
1923
2001
compile . $$addBindingInfo = debugInfoEnabled ? function $$addBindingInfo ( $element , binding ) {
@@ -2253,43 +2331,58 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
2253
2331
directiveNormalize ( nodeName ) , 'E' , maxPriority , ignoreDirective ) ;
2254
2332
2255
2333
// iterate over the attributes
2256
- for ( var attr , name , nName , ngAttrName , value , isNgAttr , nAttrs = node . attributes ,
2334
+ for ( var attr , name , nName , ngAttrName , value , ngPrefixMatch , nAttrs = node . attributes ,
2257
2335
j = 0 , jj = nAttrs && nAttrs . length ; j < jj ; j ++ ) {
2258
2336
var attrStartName = false ;
2259
2337
var attrEndName = false ;
2260
2338
2339
+ var isNgAttr = false , isNgProp = false , isNgEvent = false ;
2340
+ var multiElementMatch ;
2341
+
2261
2342
attr = nAttrs [ j ] ;
2262
2343
name = attr . name ;
2263
2344
value = attr . value ;
2264
2345
2265
- // support ngAttr attribute binding
2346
+ // support ng-attr-*, ng-prop-* and ng-on-*
2266
2347
ngAttrName = directiveNormalize ( name ) ;
2267
- isNgAttr = NG_ATTR_BINDING . test ( ngAttrName ) ;
2268
- if ( isNgAttr ) {
2348
+ if ( ngPrefixMatch = ngAttrName . match ( NG_PREFIX_BINDING ) ) {
2349
+ isNgAttr = ngPrefixMatch [ 1 ] === "Attr" ;
2350
+ isNgProp = ngPrefixMatch [ 1 ] === "Prop" ;
2351
+ isNgEvent = ngPrefixMatch [ 1 ] === "On" ;
2352
+
2269
2353
name = name . replace ( PREFIX_REGEXP , '' )
2270
- . substr ( 8 ) . replace ( / _ ( .) / g, function ( match , letter ) {
2354
+ . substr ( 4 + ngPrefixMatch [ 1 ] . length ) . replace ( / _ ( .) / g, function ( match , letter ) {
2271
2355
return letter . toUpperCase ( ) ;
2272
2356
} ) ;
2273
- }
2274
2357
2275
- var multiElementMatch = ngAttrName . match ( MULTI_ELEMENT_DIR_RE ) ;
2276
- if ( multiElementMatch && directiveIsMultiElement ( multiElementMatch [ 1 ] ) ) {
2358
+ // support *-start / *-end multi element directives
2359
+ } else if ( ( multiElementMatch = ngAttrName . match ( MULTI_ELEMENT_DIR_RE ) ) && directiveIsMultiElement ( multiElementMatch [ 1 ] ) ) {
2277
2360
attrStartName = name ;
2278
2361
attrEndName = name . substr ( 0 , name . length - 5 ) + 'end' ;
2279
2362
name = name . substr ( 0 , name . length - 6 ) ;
2280
2363
}
2281
2364
2282
2365
nName = directiveNormalize ( name . toLowerCase ( ) ) ;
2283
2366
attrsMap [ nName ] = name ;
2284
- if ( isNgAttr || ! attrs . hasOwnProperty ( nName ) ) {
2285
- attrs [ nName ] = value ;
2286
- if ( getBooleanAttrName ( node , nName ) ) {
2287
- attrs [ nName ] = true ; // presence means true
2288
- }
2367
+
2368
+ if ( isNgProp ) {
2369
+ attrs [ ngAttrName ] = value ;
2370
+ addPropertyDirective ( node , directives , ngAttrName , name ) ;
2371
+ } else if ( isNgEvent ) {
2372
+ attrs [ ngAttrName ] = value ;
2373
+ addEventDirective ( directives , ngAttrName , name ) ;
2374
+ } else {
2375
+ if ( isNgAttr || ! attrs . hasOwnProperty ( nName ) ) {
2376
+ attrs [ nName ] = value ;
2377
+ if ( getBooleanAttrName ( node , nName ) ) {
2378
+ attrs [ nName ] = true ; // presence means true
2379
+ }
2380
+ }
2381
+
2382
+ addAttrInterpolateDirective ( node , directives , value , nName , isNgAttr ) ;
2383
+ addDirective ( directives , nName , 'A' , maxPriority , ignoreDirective , attrStartName ,
2384
+ attrEndName ) ;
2289
2385
}
2290
- addAttrInterpolateDirective ( node , directives , value , nName , isNgAttr ) ;
2291
- addDirective ( directives , nName , 'A' , maxPriority , ignoreDirective , attrStartName ,
2292
- attrEndName ) ;
2293
2386
}
2294
2387
2295
2388
if ( nodeName === 'input' && node . getAttribute ( 'type' ) === 'hidden' ) {
@@ -3325,7 +3418,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
3325
3418
}
3326
3419
3327
3420
3328
- function getTrustedContext ( node , attrNormalizedName ) {
3421
+ function getTrustedAttrContext ( node , attrNormalizedName ) {
3329
3422
if ( attrNormalizedName === 'srcdoc' ) {
3330
3423
return $sce . HTML ;
3331
3424
}
@@ -3358,9 +3451,54 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
3358
3451
}
3359
3452
}
3360
3453
3454
+ function getTrustedPropContext ( node , propNormalizedName ) {
3455
+ var tag = nodeName_ ( node ) ;
3456
+ var prop = propNormalizedName . toLowerCase ( ) ;
3457
+ return PROP_CONTEXTS [ tag + "|" + prop ] || PROP_CONTEXTS [ "*|" + prop ] ;
3458
+ }
3459
+
3460
+ function addPropertyDirective ( node , directives , attrName , propName ) {
3461
+ if ( EVENT_HANDLER_ATTR_REGEXP . test ( propName ) ) {
3462
+ throw $compileMinErr ( 'nodomevents' ,
3463
+ 'Property bindings for HTML DOM event properties are disallowed. Please use the ' +
3464
+ 'ng- versions (such as ng-click or ng-on-click instead of ng-prop-onclick) instead.' ) ;
3465
+ }
3466
+
3467
+ var nodeName = nodeName_ ( node ) ;
3468
+ var trustedContext = getTrustedPropContext ( node , propName ) ;
3469
+
3470
+ directives . push ( {
3471
+ priority : 100 ,
3472
+ compile : function ngPropCompileFn ( _ , attr ) {
3473
+ var fn = $parse ( attr [ attrName ] ) ;
3474
+ return {
3475
+ pre : function ngPropPreLinkFn ( scope , $element ) {
3476
+ scope . $watch ( fn , function propertyWatchActionFn ( value ) {
3477
+ if ( value ) {
3478
+ // Sanitize img[srcset] + source[srcset] values.
3479
+ if ( ( nodeName === 'img' || nodeName === 'source' ) && propName === 'srcset' ) {
3480
+ value = sanitizeSrcset ( $sce . valueOf ( value ) , 'ng-prop-srcset' ) ;
3481
+ } else if ( trustedContext ) {
3482
+ value = $sce . getTrusted ( trustedContext , value ) ;
3483
+ }
3484
+ }
3485
+
3486
+ $element . prop ( propName , value ) ;
3487
+ } ) ;
3488
+ }
3489
+ } ;
3490
+ }
3491
+ } ) ;
3492
+ }
3493
+
3494
+ function addEventDirective ( directives , attrName , eventName ) {
3495
+ directives . push (
3496
+ createEventDirective ( $parse , $rootScope , attrName , eventName , /*forceAsync=*/ false )
3497
+ ) ;
3498
+ }
3361
3499
3362
3500
function addAttrInterpolateDirective ( node , directives , value , name , isNgAttr ) {
3363
- var trustedContext = getTrustedContext ( node , name ) ;
3501
+ var trustedContext = getTrustedAttrContext ( node , name ) ;
3364
3502
var mustHaveExpression = ! isNgAttr ;
3365
3503
var allOrNothing = ALL_OR_NOTHING_ATTRS [ name ] || isNgAttr ;
3366
3504
@@ -3378,7 +3516,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
3378
3516
if ( EVENT_HANDLER_ATTR_REGEXP . test ( name ) ) {
3379
3517
throw $compileMinErr ( 'nodomevents' ,
3380
3518
'Interpolations for HTML DOM event attributes are disallowed. Please use the ' +
3381
- 'ng- versions (such as ng-click instead of onclick) instead.' ) ;
3519
+ 'ng- versions (such as ng-click or ng-on-click instead of onclick) instead.' ) ;
3382
3520
}
3383
3521
3384
3522
directives . push ( {
0 commit comments