/** * Creates an empty string constant. */ public static SanitizedContent emptyString(ContentKind kind) throws IOException { return new SanitizedContent("", kind); }
/** * Converts a Soy {@link SanitizedContent} of kind HTML into a {@link SafeHtml}. * * @throws IllegalStateException if this SanitizedContent's content kind is not {@link * ContentKind#HTML}. */ public SafeHtml toSafeHtml() { Preconditions.checkState( getContentKind() == ContentKind.HTML, "toSafeHtml() only valid for SanitizedContent of kind HTML, is: %s", getContentKind()); return UncheckedConversions.safeHtmlFromStringKnownToSatisfyTypeContract(getContent()); }
/** * Concatenate the contents of multiple {@link SanitizedContent} objects of kind HTML. * * @param contents The HTML content to combine. */ public static SanitizedContent concatHtml(SanitizedContent... contents) { for (SanitizedContent content : contents) { checkArgument(content.getContentKind() == ContentKind.HTML, "Can only concat HTML"); } StringBuilder combined = new StringBuilder(); Dir dir = Dir.NEUTRAL; // Empty string is neutral. for (SanitizedContent content : contents) { combined.append(content.getContent()); if (dir == Dir.NEUTRAL) { // neutral + x -> x dir = content.getContentDirection(); } else if (content.getContentDirection() == dir || content.getContentDirection() == Dir.NEUTRAL) { // x + x|neutral -> x, so leave dir unchanged. } else { // LTR|unknown + RTL|unknown -> unknown // RTL|unknown + LTR|unknown -> unknown dir = null; } } return SanitizedContent.create(combined.toString(), ContentKind.HTML, dir); }
/** * Converts a Soy {@link SanitizedContent} of kind CSS into a {@link SafeStyleSheetProto}. * * <p>To ensure correct behavior and usage, the SanitizedContent object should fulfill the * contract of SafeStyleSheet - the CSS content should represent the top-level content of a style * element within HTML. * * @throws IllegalStateException if this SanitizedContent's content kind is not {@link * ContentKind#CSS}. */ public SafeStyleSheetProto toSafeStyleSheetProto() { Preconditions.checkState( getContentKind() == ContentKind.CSS, "toSafeStyleSheetProto() only valid for SanitizedContent of kind CSS, is: %s", getContentKind()); return SafeStyleSheets.toProto(toSafeStyleSheet()); }
public static int bidiTextDir(SoyValue value, boolean isHtml) { Dir valueDir = null; boolean isHtmlForValueDirEstimation = false; if (value instanceof SanitizedContent) { SanitizedContent sanitizedContent = (SanitizedContent) value; valueDir = sanitizedContent.getContentDirection(); if (valueDir == null) { isHtmlForValueDirEstimation = sanitizedContent.getContentKind() == ContentKind.HTML; } } if (valueDir == null) { isHtmlForValueDirEstimation = isHtmlForValueDirEstimation || isHtml; valueDir = BidiUtils.estimateDirection(value.coerceToString(), isHtmlForValueDirEstimation); } return valueDir.ord; }
@Override public void close() throws IOException { if (!isInHtml()) { StringBuilder buffer = (StringBuilder) activeAppendable; if (buffer.length() > 0) { SanitizedContent content = cleanHtml(buffer.toString(), getSanitizedContentDirectionality(), optionalSafeTags); delegate .setSanitizedContentKind(content.getContentKind()) .setSanitizedContentDirectionality(content.getContentDirection()) .append(content.getContent()); buffer.setLength(0); } } } }
/** True iff the given value is sanitized content of the given kind. */ private static boolean isSanitizedContentOfKind( SoyValue value, SanitizedContent.ContentKind kind) { return value instanceof SanitizedContent && kind == ((SanitizedContent) value).getContentKind(); }
/** * Returns the string value. * * <p>In contexts where a string value is required, SanitizedContent is permitted. */ @Override public String stringValue() { return getContent(); }
/** Creates an empty string constant. */ public static SanitizedContent emptyString(ContentKind kind) { return SanitizedContent.create("", kind, Dir.NEUTRAL); // Empty string is neutral. }
@Override public boolean equals(@Nullable Object other) { // TODO(user): js uses reference equality, this uses content comparison if (other instanceof StringData) { // So that StringData and UnsanitizedString can be used interchangeably. Keep this in sync // with StringData#equals. return ((StringData) other).stringValue().equals(this.getContent()); } return other instanceof UnsanitizedString && this.getContentDirection() == ((SanitizedContent) other).getContentDirection() && this.getContent().equals(((UnsanitizedString) other).getContent()); }
@Override Object protoFromSoy(SoyValue field) { return ((SanitizedContent) field).toSafeHtmlProto(); } };
@Override Object protoFromSoy(SoyValue field) { return ((SanitizedContent) field).toSafeScriptProto(); } };
@Override Object protoFromSoy(SoyValue field) { return ((SanitizedContent) field).toSafeStyleProto(); } };
/** Creates a SanitizedContent that wraps the given thunk. */ public static SanitizedContent forThunk(RenderableThunk value, ContentKind kind) { return SanitizedContent.createLazy(value, kind, kind.getDefaultDir()); }
public static int bidiTextDir(SoyValue value, boolean isHtml) { Dir valueDir = null; boolean isHtmlForValueDirEstimation = false; if (value instanceof SanitizedContent) { SanitizedContent sanitizedContent = (SanitizedContent) value; valueDir = sanitizedContent.getContentDirection(); if (valueDir == null) { isHtmlForValueDirEstimation = sanitizedContent.getContentKind() == ContentKind.HTML; } } if (valueDir == null) { isHtmlForValueDirEstimation = isHtmlForValueDirEstimation || isHtml; valueDir = BidiUtils.estimateDirection(value.coerceToString(), isHtmlForValueDirEstimation); } return valueDir.ord; } }
@Override public void close() throws IOException { if (!isInHtml()) { StringBuilder buffer = (StringBuilder) activeAppendable; if (buffer.length() > 0) { SanitizedContent content = cleanHtml(buffer.toString(), getSanitizedContentDirectionality(), optionalSafeTags); delegate .setSanitizedContentKind(content.getContentKind()) .setSanitizedContentDirectionality(content.getContentDirection()) .append(content.getContent()); buffer.setLength(0); } } } }
/** True iff the given value is sanitized content of the given kind. */ private static boolean isSanitizedContentOfKind( SoyValue value, SanitizedContent.ContentKind kind) { return value instanceof SanitizedContent && kind == ((SanitizedContent) value).getContentKind(); }
/** * Returns the string value. * * <p>In contexts where a string value is required, SanitizedContent is permitted. */ @Override public String stringValue() { return getContent(); }
/** * Creates a SanitizedContent object of kind TEXT of a given direction (null if unknown). * * <p>This is useful when stubbing out a function that needs to create a SanitizedContent object. */ public static SanitizedContent unsanitizedText(String text, @Nullable Dir dir) { return SanitizedContent.create(text, ContentKind.TEXT, dir); }
public static String bidiSpanWrap(BidiGlobalDir dir, SoyValue value) { Dir valueDir = null; if (value instanceof SanitizedContent) { valueDir = ((SanitizedContent) value).getContentDirection(); } BidiFormatter bidiFormatter = BidiFormatter.getInstance(dir.toDir()); // We always treat the value as HTML, because span-wrapping is only useful when its output will // be treated as HTML (without escaping), and because |bidiSpanWrap is not itself specified to // do HTML escaping in Soy. (Both explicit and automatic HTML escaping, if any, is done before // calling |bidiSpanWrap because BidiSpanWrapDirective implements SanitizedContentOperator, // but this does not mean that the input has to be HTML SanitizedContent. In legacy usage, a // string that is not SanitizedContent is often printed in an autoescape="false" template or by // a print with a |noAutoescape, in which case our input is just SoyData.) If the output will be // treated as HTML, the input had better be safe HTML/HTML-escaped (even if it isn't HTML // SanitizedData), or we have an XSS opportunity and a much bigger problem than bidi garbling. String wrappedValue = bidiFormatter.spanWrap(valueDir, value.coerceToString(), /* isHtml= */ true); // Like other directives implementing SanitizedContentOperator, BidiSpanWrapDirective is called // after the escaping (if any) has already been done, and thus there is no need for it to // produce actual SanitizedContent. return wrappedValue; }