@@ -16,7 +16,7 @@ import {
16
16
SecurityContext ,
17
17
SkipSelf ,
18
18
} from '@angular/core' ;
19
- import { DomSanitizer , SafeResourceUrl } from '@angular/platform-browser' ;
19
+ import { DomSanitizer , SafeResourceUrl , SafeHtml } from '@angular/platform-browser' ;
20
20
import { forkJoin , Observable , of as observableOf , throwError as observableThrow } from 'rxjs' ;
21
21
import { catchError , finalize , map , share , tap } from 'rxjs/operators' ;
22
22
@@ -48,18 +48,41 @@ export function getMatIconNoHttpProviderError(): Error {
48
48
* @param url URL that was attempted to be sanitized.
49
49
* @docs -private
50
50
*/
51
- export function getMatIconFailedToSanitizeError ( url : SafeResourceUrl ) : Error {
51
+ export function getMatIconFailedToSanitizeUrlError ( url : SafeResourceUrl ) : Error {
52
52
return Error ( `The URL provided to MatIconRegistry was not trusted as a resource URL ` +
53
53
`via Angular's DomSanitizer. Attempted URL was "${ url } ".` ) ;
54
54
}
55
55
56
+ /**
57
+ * Returns an exception to be thrown when a HTML string couldn't be sanitized.
58
+ * @param literal HTML that was attempted to be sanitized.
59
+ * @docs -private
60
+ */
61
+ export function getMatIconFailedToSanitizeLiteralError ( literal : SafeHtml ) : Error {
62
+ return Error ( `The literal provided to MatIconRegistry was not trusted as safe HTML by ` +
63
+ `Angular's DomSanitizer. Attempted literal was "${ literal } ".` ) ;
64
+ }
65
+
66
+
56
67
/**
57
68
* Configuration for an icon, including the URL and possibly the cached SVG element.
58
69
* @docs -private
59
70
*/
60
71
class SvgIconConfig {
61
- svgElement : SVGElement | null = null ;
62
- constructor ( public url : SafeResourceUrl ) { }
72
+ url : SafeResourceUrl | null ;
73
+ svgElement : SVGElement | null ;
74
+
75
+ constructor ( url : SafeResourceUrl ) ;
76
+ constructor ( svgElement : SVGElement ) ;
77
+ constructor ( data : SafeResourceUrl | SVGElement ) {
78
+ // Note that we can't use `instanceof SVGElement` here,
79
+ // because it'll break during server-side rendering.
80
+ if ( ! ! ( data as any ) . nodeName ) {
81
+ this . svgElement = data as SVGElement ;
82
+ } else {
83
+ this . url = data as SafeResourceUrl ;
84
+ }
85
+ }
63
86
}
64
87
65
88
/**
@@ -116,16 +139,40 @@ export class MatIconRegistry {
116
139
return this . addSvgIconInNamespace ( '' , iconName , url ) ;
117
140
}
118
141
142
+ /**
143
+ * Registers an icon using an HTML string in the default namespace.
144
+ * @param iconName Name under which the icon should be registered.
145
+ * @param literal SVG source of the icon.
146
+ */
147
+ addSvgIconLiteral ( iconName : string , literal : SafeHtml ) : this {
148
+ return this . addSvgIconLiteralInNamespace ( '' , iconName , literal ) ;
149
+ }
150
+
119
151
/**
120
152
* Registers an icon by URL in the specified namespace.
121
153
* @param namespace Namespace in which the icon should be registered.
122
154
* @param iconName Name under which the icon should be registered.
123
155
* @param url
124
156
*/
125
157
addSvgIconInNamespace ( namespace : string , iconName : string , url : SafeResourceUrl ) : this {
126
- const key = iconKey ( namespace , iconName ) ;
127
- this . _svgIconConfigs . set ( key , new SvgIconConfig ( url ) ) ;
128
- return this ;
158
+ return this . _addSvgIconConfig ( namespace , iconName , new SvgIconConfig ( url ) ) ;
159
+ }
160
+
161
+ /**
162
+ * Registers an icon using an HTML string in the specified namespace.
163
+ * @param namespace Namespace in which the icon should be registered.
164
+ * @param iconName Name under which the icon should be registered.
165
+ * @param literal SVG source of the icon.
166
+ */
167
+ addSvgIconLiteralInNamespace ( namespace : string , iconName : string , literal : SafeHtml ) : this {
168
+ const sanitizedLiteral = this . _sanitizer . sanitize ( SecurityContext . HTML , literal ) ;
169
+
170
+ if ( ! sanitizedLiteral ) {
171
+ throw getMatIconFailedToSanitizeLiteralError ( literal ) ;
172
+ }
173
+
174
+ const svgElement = this . _createSvgElementForSingleIcon ( sanitizedLiteral ) ;
175
+ return this . _addSvgIconConfig ( namespace , iconName , new SvgIconConfig ( svgElement ) ) ;
129
176
}
130
177
131
178
/**
@@ -136,21 +183,37 @@ export class MatIconRegistry {
136
183
return this . addSvgIconSetInNamespace ( '' , url ) ;
137
184
}
138
185
186
+ /**
187
+ * Registers an icon set using an HTML string in the default namespace.
188
+ * @param literal SVG source of the icon set.
189
+ */
190
+ addSvgIconSetLiteral ( literal : SafeHtml ) : this {
191
+ return this . addSvgIconSetLiteralInNamespace ( '' , literal ) ;
192
+ }
193
+
139
194
/**
140
195
* Registers an icon set by URL in the specified namespace.
141
196
* @param namespace Namespace in which to register the icon set.
142
197
* @param url
143
198
*/
144
199
addSvgIconSetInNamespace ( namespace : string , url : SafeResourceUrl ) : this {
145
- const config = new SvgIconConfig ( url ) ;
146
- const configNamespace = this . _iconSetConfigs . get ( namespace ) ;
200
+ return this . _addSvgIconSetConfig ( namespace , new SvgIconConfig ( url ) ) ;
201
+ }
147
202
148
- if ( configNamespace ) {
149
- configNamespace . push ( config ) ;
150
- } else {
151
- this . _iconSetConfigs . set ( namespace , [ config ] ) ;
203
+ /**
204
+ * Registers an icon set using an HTML string in the specified namespace.
205
+ * @param namespace Namespace in which to register the icon set.
206
+ * @param literal SVG source of the icon set.
207
+ */
208
+ addSvgIconSetLiteralInNamespace ( namespace : string , literal : SafeHtml ) : this {
209
+ const sanitizedLiteral = this . _sanitizer . sanitize ( SecurityContext . HTML , literal ) ;
210
+
211
+ if ( ! sanitizedLiteral ) {
212
+ throw getMatIconFailedToSanitizeLiteralError ( literal ) ;
152
213
}
153
- return this ;
214
+
215
+ const svgElement = this . _svgElementFromString ( sanitizedLiteral ) ;
216
+ return this . _addSvgIconSetConfig ( namespace , new SvgIconConfig ( svgElement ) ) ;
154
217
}
155
218
156
219
/**
@@ -202,13 +265,13 @@ export class MatIconRegistry {
202
265
* @param safeUrl URL from which to fetch the SVG icon.
203
266
*/
204
267
getSvgIconFromUrl ( safeUrl : SafeResourceUrl ) : Observable < SVGElement > {
205
- let url = this . _sanitizer . sanitize ( SecurityContext . RESOURCE_URL , safeUrl ) ;
268
+ const url = this . _sanitizer . sanitize ( SecurityContext . RESOURCE_URL , safeUrl ) ;
206
269
207
270
if ( ! url ) {
208
- throw getMatIconFailedToSanitizeError ( safeUrl ) ;
271
+ throw getMatIconFailedToSanitizeUrlError ( safeUrl ) ;
209
272
}
210
273
211
- let cachedIcon = this . _cachedIconsByUrl . get ( url ) ;
274
+ const cachedIcon = this . _cachedIconsByUrl . get ( url ) ;
212
275
213
276
if ( cachedIcon ) {
214
277
return observableOf ( cloneSvg ( cachedIcon ) ) ;
@@ -461,15 +524,19 @@ export class MatIconRegistry {
461
524
* Returns an Observable which produces the string contents of the given URL. Results may be
462
525
* cached, so future calls with the same URL may not cause another HTTP request.
463
526
*/
464
- private _fetchUrl ( safeUrl : SafeResourceUrl ) : Observable < string > {
527
+ private _fetchUrl ( safeUrl : SafeResourceUrl | null ) : Observable < string > {
465
528
if ( ! this . _httpClient ) {
466
529
throw getMatIconNoHttpProviderError ( ) ;
467
530
}
468
531
532
+ if ( safeUrl == null ) {
533
+ throw Error ( `Cannot fetch icon from URL "${ safeUrl } ".` ) ;
534
+ }
535
+
469
536
const url = this . _sanitizer . sanitize ( SecurityContext . RESOURCE_URL , safeUrl ) ;
470
537
471
538
if ( ! url ) {
472
- throw getMatIconFailedToSanitizeError ( safeUrl ) ;
539
+ throw getMatIconFailedToSanitizeUrlError ( safeUrl ) ;
473
540
}
474
541
475
542
// Store in-progress fetches to avoid sending a duplicate request for a URL when there is
@@ -491,6 +558,34 @@ export class MatIconRegistry {
491
558
this . _inProgressUrlFetches . set ( url , req ) ;
492
559
return req ;
493
560
}
561
+
562
+ /**
563
+ * Registers an icon config by name in the specified namespace.
564
+ * @param namespace Namespace in which to register the icon config.
565
+ * @param iconName Name under which to register the config.
566
+ * @param config Config to be registered.
567
+ */
568
+ private _addSvgIconConfig ( namespace : string , iconName : string , config : SvgIconConfig ) : this {
569
+ this . _svgIconConfigs . set ( iconKey ( namespace , iconName ) , config ) ;
570
+ return this ;
571
+ }
572
+
573
+ /**
574
+ * Registers an icon set config in the specified namespace.
575
+ * @param namespace Namespace in which to register the icon config.
576
+ * @param config Config to be registered.
577
+ */
578
+ private _addSvgIconSetConfig ( namespace : string , config : SvgIconConfig ) : this {
579
+ const configNamespace = this . _iconSetConfigs . get ( namespace ) ;
580
+
581
+ if ( configNamespace ) {
582
+ configNamespace . push ( config ) ;
583
+ } else {
584
+ this . _iconSetConfigs . set ( namespace , [ config ] ) ;
585
+ }
586
+
587
+ return this ;
588
+ }
494
589
}
495
590
496
591
/** @docs -private */
0 commit comments