diff --git a/src/internals/html_impl.ts b/src/internals/html_impl.ts
index 92286742..e786c2ea 100644
--- a/src/internals/html_impl.ts
+++ b/src/internals/html_impl.ts
@@ -5,46 +5,43 @@
import '../environment/dev';
+/* g3_import_pure from './pure' */
import {ensureTokenIsValid, secretToken} from './secrets';
import {getTrustedTypes, getTrustedTypesPolicy} from './trusted_types';
/**
- * Runtime implementation of `TrustedHTML` in browsers that don't support it.
+ * String that is safe to use in HTML contexts in DOM APIs and HTML documents.
+ * See
+ * https://github.com/google/safevalues/blob/main/src/README.md#trustedresourceurl
*/
-class HtmlImpl {
- readonly privateDoNotAccessOrElseWrappedHtml: string;
+export abstract class SafeHtml {
+ // tslint:disable-next-line:no-unused-variable
+ // @ts-ignore
+ private readonly brand!: never; // To prevent structural typing.
+}
+
+/** Implementation for `SafeHtml` */
+class HtmlImpl extends SafeHtml {
+ readonly privateDoNotAccessOrElseWrappedHtml: TrustedHTML|string;
- constructor(html: string, token: object) {
- ensureTokenIsValid(token);
+ constructor(html: TrustedHTML|string, token: object) {
+ super();
+ if (process.env.NODE_ENV !== 'production') {
+ ensureTokenIsValid(token);
+ }
this.privateDoNotAccessOrElseWrappedHtml = html;
}
- toString(): string {
+ override toString(): string {
return this.privateDoNotAccessOrElseWrappedHtml.toString();
}
}
-function createTrustedHtmlOrPolyfill(
- html: string, trusted?: TrustedHTML): SafeHtml {
- return (trusted ?? new HtmlImpl(html, secretToken)) as SafeHtml;
+function createHtmlInstance(html: string, trusted?: TrustedHTML): SafeHtml {
+ return new HtmlImpl(trusted ?? html, secretToken);
}
-const GlobalTrustedHTML =
- (typeof window !== undefined) ? window.TrustedHTML : undefined;
-
-/**
- * String that is safe to use in HTML contexts in DOM APIs and HTML
- documents.
- */
-export type SafeHtml = TrustedHTML;
-
-/**
- * Also exports the constructor so that instanceof checks work.
- */
-export const SafeHtml =
- (GlobalTrustedHTML ?? HtmlImpl) as unknown as TrustedHTML;
-
/**
* Builds a new `SafeHtml` from the given string, without enforcing safety
* guarantees. It may cause side effects by creating a Trusted Types policy.
@@ -54,7 +51,7 @@ export const SafeHtml =
export function createHtmlInternal(html: string): SafeHtml {
/** @noinline */
const noinlineHtml = html;
- return createTrustedHtmlOrPolyfill(
+ return createHtmlInstance(
noinlineHtml, getTrustedTypesPolicy()?.createHTML(noinlineHtml));
}
@@ -64,13 +61,13 @@ export function createHtmlInternal(html: string): SafeHtml {
*/
export const EMPTY_HTML: SafeHtml =
/* #__PURE__ */ (
- () => createTrustedHtmlOrPolyfill('', getTrustedTypes()?.emptyHTML))();
+ () => createHtmlInstance('', getTrustedTypes()?.emptyHTML))();
/**
* Checks if the given value is a `SafeHtml` instance.
*/
export function isHtml(value: unknown): value is SafeHtml {
- return getTrustedTypes()?.isHTML(value) || value instanceof HtmlImpl;
+ return value instanceof HtmlImpl;
}
/**
@@ -78,12 +75,19 @@ export function isHtml(value: unknown): value is SafeHtml {
* has the correct type.
*
* Returns a native `TrustedHTML` or a string if Trusted Types are disabled.
+ *
+ * The strange return type is to ensure the value can be used at sinks without a
+ * cast despite the TypeScript DOM lib not supporting Trusted Types.
+ * (https://github.com/microsoft/TypeScript/issues/30024)
+ *
+ * Note that while the return type is compatible with `string`, you shouldn't
+ * use any string functions on the result as that will fail in browsers
+ * supporting Trusted Types.
*/
-export function unwrapHtml(value: SafeHtml): TrustedHTML|string {
- if (getTrustedTypes()?.isHTML(value)) {
- return value;
- } else if (value instanceof HtmlImpl) {
- return value.privateDoNotAccessOrElseWrappedHtml;
+export function unwrapHtml(value: SafeHtml): TrustedHTML&string {
+ if (value instanceof HtmlImpl) {
+ const unwrapped = value.privateDoNotAccessOrElseWrappedHtml;
+ return unwrapped as TrustedHTML & string;
} else {
let message = '';
if (process.env.NODE_ENV !== 'production') {
diff --git a/src/internals/resource_url_impl.ts b/src/internals/resource_url_impl.ts
index c7df56f5..38d9574b 100644
--- a/src/internals/resource_url_impl.ts
+++ b/src/internals/resource_url_impl.ts
@@ -6,42 +6,40 @@
import '../environment/dev';
import {ensureTokenIsValid, secretToken} from './secrets';
-import {getTrustedTypes, getTrustedTypesPolicy} from './trusted_types';
+import {getTrustedTypesPolicy} from './trusted_types';
/**
- * Runtime implementation of `TrustedScriptURL` in browsers that don't support
- * it.
+ * String that is safe to use in all URL contexts in DOM APIs and HTML
+ * documents; even as a reference to resources that may load in the current
+ * origin (e.g. scripts and stylesheets).
+
+ * See
+ https://github.com/google/safevalues/blob/main/src/README.md#trustedresourceurl
*/
-class ResourceUrlImpl {
- readonly privateDoNotAccessOrElseWrappedResourceUrl: string;
+export abstract class TrustedResourceUrl {
+ // tslint:disable-next-line:no-unused-variable
+ // @ts-ignore
+ private readonly brand!: never; // To prevent structural typing.
+}
- constructor(url: string, token: object) {
- ensureTokenIsValid(token);
+/** Implementation for `TrustedResourceUrl` */
+class ResourceUrlImpl extends TrustedResourceUrl {
+ readonly privateDoNotAccessOrElseWrappedResourceUrl: TrustedScriptURL|string;
+
+ constructor(url: TrustedScriptURL|string, token: object) {
+ super();
+ if (process.env.NODE_ENV !== 'production') {
+ ensureTokenIsValid(token);
+ }
this.privateDoNotAccessOrElseWrappedResourceUrl = url;
}
- toString(): string {
+ override toString(): string {
return this.privateDoNotAccessOrElseWrappedResourceUrl.toString();
}
}
-const GlobalTrustedScriptURL =
- (typeof window !== undefined) ? window.TrustedScriptURL : undefined;
-
-/**
- * String that is safe to use in all URL contexts in DOM APIs and HTML
- * documents; even as a reference to resources that may load in the current
- * origin (e.g. scripts and stylesheets).
- */
-export type TrustedResourceUrl = TrustedScriptURL;
-
-/**
- * Also exports the constructor so that instanceof checks work.
- */
-export const TrustedResourceUrl =
- (GlobalTrustedScriptURL ?? ResourceUrlImpl) as unknown as TrustedScriptURL;
-
/**
* Builds a new `TrustedResourceUrl` from the given string, without
* enforcing safety guarantees. It may cause side effects by creating a Trusted
@@ -53,16 +51,14 @@ export function createResourceUrlInternal(url: string): TrustedResourceUrl {
const noinlineUrl = url;
const trustedScriptURL =
getTrustedTypesPolicy()?.createScriptURL(noinlineUrl);
- return (trustedScriptURL ?? new ResourceUrlImpl(noinlineUrl, secretToken)) as
- TrustedResourceUrl;
+ return new ResourceUrlImpl(trustedScriptURL ?? noinlineUrl, secretToken);
}
/**
* Checks if the given value is a `TrustedResourceUrl` instance.
*/
export function isResourceUrl(value: unknown): value is TrustedResourceUrl {
- return getTrustedTypes()?.isScriptURL(value) ||
- value instanceof ResourceUrlImpl;
+ return value instanceof ResourceUrlImpl;
}
/**
@@ -71,13 +67,20 @@ export function isResourceUrl(value: unknown): value is TrustedResourceUrl {
*
* Returns a native `TrustedScriptURL` or a string if Trusted Types are
* disabled.
+ *
+ * The strange return type is to ensure the value can be used at sinks without a
+ * cast despite the TypeScript DOM lib not supporting Trusted Types.
+ * (https://github.com/microsoft/TypeScript/issues/30024)
+ *
+ * Note that while the return type is compatible with `string`, you shouldn't
+ * use any string functions on the result as that will fail in browsers
+ * supporting Trusted Types.
*/
-export function unwrapResourceUrl(value: TrustedResourceUrl): TrustedScriptURL|
+export function unwrapResourceUrl(value: TrustedResourceUrl): TrustedScriptURL&
string {
- if (getTrustedTypes()?.isScriptURL(value)) {
- return value;
- } else if (value instanceof ResourceUrlImpl) {
- return value.privateDoNotAccessOrElseWrappedResourceUrl;
+ if (value instanceof ResourceUrlImpl) {
+ const unwrapped = value.privateDoNotAccessOrElseWrappedResourceUrl;
+ return unwrapped as TrustedScriptURL & string;
} else {
let message = '';
if (process.env.NODE_ENV !== 'production') {
diff --git a/src/internals/script_impl.ts b/src/internals/script_impl.ts
index b8b87ea6..d0bcbfcc 100644
--- a/src/internals/script_impl.ts
+++ b/src/internals/script_impl.ts
@@ -5,47 +5,46 @@
import '../environment/dev';
+/* g3_import_pure from './pure' */
import {ensureTokenIsValid, secretToken} from './secrets';
import {getTrustedTypes, getTrustedTypesPolicy} from './trusted_types';
+
/**
- * Runtime implementation of `TrustedScript` in browswers that don't support it.
+ * JavaScript code that is safe to evaluate and use as the content of an HTML
* script element.
+ *
+ * See https://github.com/google/safevalues/blob/main/src/README.md#safescript
*/
-class ScriptImpl {
- readonly privateDoNotAccessOrElseWrappedScript: string;
+export abstract class SafeScript {
+ // tslint:disable-next-line:no-unused-variable
+ // @ts-ignore
+ private readonly brand!: never; // To prevent structural typing.
+}
+
+/** Implementation for `SafeScript` */
+class ScriptImpl extends SafeScript {
+ readonly privateDoNotAccessOrElseWrappedScript: TrustedScript|string;
- constructor(script: string, token: object) {
- ensureTokenIsValid(token);
+ constructor(script: TrustedScript|string, token: object) {
+ super();
+ if (process.env.NODE_ENV !== 'production') {
+ ensureTokenIsValid(token);
+ }
this.privateDoNotAccessOrElseWrappedScript = script;
}
- toString(): string {
+ override toString(): string {
return this.privateDoNotAccessOrElseWrappedScript.toString();
}
}
-function createTrustedScriptOrPolyfill(
+function createScriptInstance(
script: string, trusted?: TrustedScript): SafeScript {
- return (trusted ?? new ScriptImpl(script, secretToken)) as SafeScript;
+ return new ScriptImpl(trusted ?? script, secretToken);
}
-const GlobalTrustedScript =
- (typeof window !== undefined) ? window.TrustedScript : undefined;
-
-/**
- * JavaScript code that is safe to evaluate and use as the content of an HTML
- * script element.
- */
-export type SafeScript = TrustedScript;
-
-/**
- * Also exports the constructor so that instanceof checks work.
- */
-export const SafeScript =
- (GlobalTrustedScript ?? ScriptImpl) as unknown as TrustedScript;
-
/**
* Builds a new `SafeScript` from the given string, without enforcing
* safety guarantees. It may cause side effects by creating a Trusted Types
@@ -55,7 +54,7 @@ export const SafeScript =
export function createScriptInternal(script: string): SafeScript {
/** @noinline */
const noinlineScript = script;
- return createTrustedScriptOrPolyfill(
+ return createScriptInstance(
noinlineScript, getTrustedTypesPolicy()?.createScript(noinlineScript));
}
@@ -65,14 +64,13 @@ export function createScriptInternal(script: string): SafeScript {
*/
export const EMPTY_SCRIPT: SafeScript =
/* #__PURE__ */ (
- () => createTrustedScriptOrPolyfill(
- '', getTrustedTypes()?.emptyScript))();
+ () => createScriptInstance('', getTrustedTypes()?.emptyScript))();
/**
* Checks if the given value is a `SafeScript` instance.
*/
export function isScript(value: unknown): value is SafeScript {
- return getTrustedTypes()?.isScript(value) || value instanceof ScriptImpl;
+ return value instanceof ScriptImpl;
}
/**
@@ -80,12 +78,19 @@ export function isScript(value: unknown): value is SafeScript {
* has the correct type.
*
* Returns a native `TrustedScript` or a string if Trusted Types are disabled.
+ *
+ * The strange return type is to ensure the value can be used at sinks without a
+ * cast despite the TypeScript DOM lib not supporting Trusted Types.
+ * (https://github.com/microsoft/TypeScript/issues/30024)
+ *
+ * Note that while the return type is compatible with `string`, you shouldn't
+ * use any string functions on the result as that will fail in browsers
+ * supporting Trusted Types.
*/
-export function unwrapScript(value: SafeScript): TrustedScript|string {
- if (getTrustedTypes()?.isScript(value)) {
- return value;
- } else if (value instanceof ScriptImpl) {
- return value.privateDoNotAccessOrElseWrappedScript;
+export function unwrapScript(value: SafeScript): TrustedScript&string {
+ if (value instanceof ScriptImpl) {
+ const unwrapped = value.privateDoNotAccessOrElseWrappedScript;
+ return unwrapped as TrustedScript & string;
} else {
let message = '';
if (process.env.NODE_ENV !== 'production') {