Path: blob/trunk/third_party/closure/goog/soy/data.js
2868 views
// Copyright 2012 The Closure Library Authors. All Rights Reserved.1//2// Licensed under the Apache License, Version 2.0 (the "License");3// you may not use this file except in compliance with the License.4// You may obtain a copy of the License at5//6// http://www.apache.org/licenses/LICENSE-2.07//8// Unless required by applicable law or agreed to in writing, software9// distributed under the License is distributed on an "AS-IS" BASIS,10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.11// See the License for the specific language governing permissions and12// limitations under the License.1314/**15* @fileoverview Soy data primitives.16*17* The goal is to encompass data types used by Soy, especially to mark content18* as known to be "safe".19*20* @author [email protected] (Garrett Boyer)21*/2223goog.provide('goog.soy.data.SanitizedContent');24goog.provide('goog.soy.data.SanitizedContentKind');25goog.provide('goog.soy.data.SanitizedCss');26goog.provide('goog.soy.data.SanitizedHtml');27goog.provide('goog.soy.data.SanitizedHtmlAttribute');28goog.provide('goog.soy.data.SanitizedJs');29goog.provide('goog.soy.data.SanitizedTrustedResourceUri');30goog.provide('goog.soy.data.SanitizedUri');31goog.provide('goog.soy.data.UnsanitizedText');3233goog.require('goog.Uri');34goog.require('goog.html.SafeHtml');35goog.require('goog.html.SafeScript');36goog.require('goog.html.SafeStyle');37goog.require('goog.html.SafeUrl');38goog.require('goog.html.TrustedResourceUrl');39goog.require('goog.html.uncheckedconversions');40goog.require('goog.i18n.bidi.Dir');41goog.require('goog.string.Const');424344/**45* A type of textual content.46*47* This is an enum of type Object so that these values are unforgeable.48*49* @enum {!Object}50*/51goog.soy.data.SanitizedContentKind = {5253/**54* A snippet of HTML that does not start or end inside a tag, comment, entity,55* or DOCTYPE; and that does not contain any executable code56* (JS, {@code <object>}s, etc.) from a different trust domain.57*/58HTML: goog.DEBUG ? {sanitizedContentKindHtml: true} : {},5960/**61* Executable Javascript code or expression, safe for insertion in a62* script-tag or event handler context, known to be free of any63* attacker-controlled scripts. This can either be side-effect-free64* Javascript (such as JSON) or Javascript that's entirely under Google's65* control.66*/67JS: goog.DEBUG ? {sanitizedContentJsChars: true} : {},6869/** A properly encoded portion of a URI. */70URI: goog.DEBUG ? {sanitizedContentUri: true} : {},7172/** A resource URI not under attacker control. */73TRUSTED_RESOURCE_URI:74goog.DEBUG ? {sanitizedContentTrustedResourceUri: true} : {},7576/**77* Repeated attribute names and values. For example,78* {@code dir="ltr" foo="bar" onclick="trustedFunction()" checked}.79*/80ATTRIBUTES: goog.DEBUG ? {sanitizedContentHtmlAttribute: true} : {},8182// TODO: Consider separating rules, declarations, and values into83// separate types, but for simplicity, we'll treat explicitly blessed84// SanitizedContent as allowed in all of these contexts.85/**86* A CSS3 declaration, property, value or group of semicolon separated87* declarations.88*/89CSS: goog.DEBUG ? {sanitizedContentCss: true} : {},9091/**92* Unsanitized plain-text content.93*94* This is effectively the "null" entry of this enum, and is sometimes used95* to explicitly mark content that should never be used unescaped. Since any96* string is safe to use as text, being of ContentKind.TEXT makes no97* guarantees about its safety in any other context such as HTML.98*/99TEXT: goog.DEBUG ? {sanitizedContentKindText: true} : {}100};101102103104/**105* A string-like object that carries a content-type and a content direction.106*107* IMPORTANT! Do not create these directly, nor instantiate the subclasses.108* Instead, use a trusted, centrally reviewed library as endorsed by your team109* to generate these objects. Otherwise, you risk accidentally creating110* SanitizedContent that is attacker-controlled and gets evaluated unescaped in111* templates.112*113* @constructor114*/115goog.soy.data.SanitizedContent = function() {116throw Error('Do not instantiate directly');117};118119120/**121* The context in which this content is safe from XSS attacks.122* @type {goog.soy.data.SanitizedContentKind}123*/124goog.soy.data.SanitizedContent.prototype.contentKind;125126127/**128* The content's direction; null if unknown and thus to be estimated when129* necessary.130* @type {?goog.i18n.bidi.Dir}131*/132goog.soy.data.SanitizedContent.prototype.contentDir = null;133134135/**136* The already-safe content.137* @protected {string}138*/139goog.soy.data.SanitizedContent.prototype.content;140141142/**143* Gets the already-safe content.144* @return {string}145*/146goog.soy.data.SanitizedContent.prototype.getContent = function() {147return this.content;148};149150151/** @override */152goog.soy.data.SanitizedContent.prototype.toString = function() {153return this.content;154};155156157/**158* Converts sanitized content of kind TEXT or HTML into SafeHtml. HTML content159* is converted without modification, while text content is HTML-escaped.160* @return {!goog.html.SafeHtml}161* @throws {Error} when the content kind is not TEXT or HTML.162*/163goog.soy.data.SanitizedContent.prototype.toSafeHtml = function() {164if (this.contentKind === goog.soy.data.SanitizedContentKind.TEXT) {165return goog.html.SafeHtml.htmlEscape(this.toString());166}167if (this.contentKind !== goog.soy.data.SanitizedContentKind.HTML) {168throw Error('Sanitized content was not of kind TEXT or HTML.');169}170return goog.html.uncheckedconversions171.safeHtmlFromStringKnownToSatisfyTypeContract(172goog.string.Const.from(173'Soy SanitizedContent of kind HTML produces ' +174'SafeHtml-contract-compliant value.'),175this.toString(), this.contentDir);176};177178179/**180* Converts sanitized content of kind URI into SafeUrl without modification.181* @return {!goog.html.SafeUrl}182* @throws {Error} when the content kind is not URI.183*/184goog.soy.data.SanitizedContent.prototype.toSafeUrl = function() {185if (this.contentKind !== goog.soy.data.SanitizedContentKind.URI) {186throw Error('Sanitized content was not of kind URI.');187}188return goog.html.uncheckedconversions189.safeUrlFromStringKnownToSatisfyTypeContract(190goog.string.Const.from(191'Soy SanitizedContent of kind URI produces ' +192'SafeHtml-contract-compliant value.'),193this.toString());194};195196197/**198* Unsanitized plain text string.199*200* While all strings are effectively safe to use as a plain text, there are no201* guarantees about safety in any other context such as HTML. This is202* sometimes used to mark that should never be used unescaped.203*204* @param {*} content Plain text with no guarantees.205* @param {?goog.i18n.bidi.Dir=} opt_contentDir The content direction; null if206* unknown and thus to be estimated when necessary. Default: null.207* @extends {goog.soy.data.SanitizedContent}208* @constructor209*/210goog.soy.data.UnsanitizedText = function(content, opt_contentDir) {211// Not calling the superclass constructor which just throws an exception.212213/** @override */214this.content = String(content);215this.contentDir = opt_contentDir != null ? opt_contentDir : null;216};217goog.inherits(goog.soy.data.UnsanitizedText, goog.soy.data.SanitizedContent);218219220/** @override */221goog.soy.data.UnsanitizedText.prototype.contentKind =222goog.soy.data.SanitizedContentKind.TEXT;223224225226/**227* Content of type {@link goog.soy.data.SanitizedContentKind.HTML}.228*229* The content is a string of HTML that can safely be embedded in a PCDATA230* context in your app. If you would be surprised to find that an HTML231* sanitizer produced {@code s} (e.g. it runs code or fetches bad URLs) and232* you wouldn't write a template that produces {@code s} on security or privacy233* grounds, then don't pass {@code s} here. The default content direction is234* unknown, i.e. to be estimated when necessary.235*236* @extends {goog.soy.data.SanitizedContent}237* @constructor238*/239goog.soy.data.SanitizedHtml = function() {240goog.soy.data.SanitizedHtml.base(this, 'constructor');241};242goog.inherits(goog.soy.data.SanitizedHtml, goog.soy.data.SanitizedContent);243244/** @override */245goog.soy.data.SanitizedHtml.prototype.contentKind =246goog.soy.data.SanitizedContentKind.HTML;247248/**249* Checks if the value could be used as the Soy type {html}.250* @param {*} value251* @return {boolean}252*/253goog.soy.data.SanitizedHtml.isCompatibleWith = function(value) {254return goog.isString(value) ||255value instanceof goog.soy.data.SanitizedHtml ||256value instanceof goog.soy.data.UnsanitizedText ||257value instanceof goog.html.SafeHtml;258};259260261262/**263* Content of type {@link goog.soy.data.SanitizedContentKind.JS}.264*265* The content is JavaScript source that when evaluated does not execute any266* attacker-controlled scripts. The content direction is LTR.267*268* @extends {goog.soy.data.SanitizedContent}269* @constructor270*/271goog.soy.data.SanitizedJs = function() {272goog.soy.data.SanitizedJs.base(this, 'constructor');273};274goog.inherits(goog.soy.data.SanitizedJs, goog.soy.data.SanitizedContent);275276/** @override */277goog.soy.data.SanitizedJs.prototype.contentKind =278goog.soy.data.SanitizedContentKind.JS;279280/** @override */281goog.soy.data.SanitizedJs.prototype.contentDir = goog.i18n.bidi.Dir.LTR;282283/**284* Checks if the value could be used as the Soy type {js}.285* @param {*} value286* @return {boolean}287*/288goog.soy.data.SanitizedJs.isCompatibleWith = function(value) {289return goog.isString(value) ||290value instanceof goog.soy.data.SanitizedJs ||291value instanceof goog.soy.data.UnsanitizedText ||292value instanceof goog.html.SafeScript;293};294295296297/**298* Content of type {@link goog.soy.data.SanitizedContentKind.URI}.299*300* The content is a URI chunk that the caller knows is safe to emit in a301* template. The content direction is LTR.302*303* @extends {goog.soy.data.SanitizedContent}304* @constructor305*/306goog.soy.data.SanitizedUri = function() {307goog.soy.data.SanitizedUri.base(this, 'constructor');308};309goog.inherits(goog.soy.data.SanitizedUri, goog.soy.data.SanitizedContent);310311/** @override */312goog.soy.data.SanitizedUri.prototype.contentKind =313goog.soy.data.SanitizedContentKind.URI;314315/** @override */316goog.soy.data.SanitizedUri.prototype.contentDir = goog.i18n.bidi.Dir.LTR;317318/**319* Checks if the value could be used as the Soy type {uri}.320* @param {*} value321* @return {boolean}322*/323goog.soy.data.SanitizedUri.isCompatibleWith = function(value) {324return goog.isString(value) ||325value instanceof goog.soy.data.SanitizedUri ||326value instanceof goog.soy.data.UnsanitizedText ||327value instanceof goog.html.SafeUrl ||328value instanceof goog.html.TrustedResourceUrl ||329value instanceof goog.Uri;330};331332333334/**335* Content of type336* {@link goog.soy.data.SanitizedContentKind.TRUSTED_RESOURCE_URI}.337*338* The content is a TrustedResourceUri chunk that is not under attacker control.339* The content direction is LTR.340*341* @extends {goog.soy.data.SanitizedContent}342* @constructor343*/344goog.soy.data.SanitizedTrustedResourceUri = function() {345goog.soy.data.SanitizedTrustedResourceUri.base(this, 'constructor');346};347goog.inherits(348goog.soy.data.SanitizedTrustedResourceUri, goog.soy.data.SanitizedContent);349350/** @override */351goog.soy.data.SanitizedTrustedResourceUri.prototype.contentKind =352goog.soy.data.SanitizedContentKind.TRUSTED_RESOURCE_URI;353354/** @override */355goog.soy.data.SanitizedTrustedResourceUri.prototype.contentDir =356goog.i18n.bidi.Dir.LTR;357358/**359* Checks if the value could be used as the Soy type {trusted_resource_uri}.360* @param {*} value361* @return {boolean}362*/363goog.soy.data.SanitizedTrustedResourceUri.isCompatibleWith = function(value) {364return goog.isString(value) ||365value instanceof goog.soy.data.SanitizedTrustedResourceUri ||366value instanceof goog.soy.data.UnsanitizedText ||367value instanceof goog.html.TrustedResourceUrl;368};369370371372/**373* Content of type {@link goog.soy.data.SanitizedContentKind.ATTRIBUTES}.374*375* The content should be safely embeddable within an open tag, such as a376* key="value" pair. The content direction is LTR.377*378* @extends {goog.soy.data.SanitizedContent}379* @constructor380*/381goog.soy.data.SanitizedHtmlAttribute = function() {382goog.soy.data.SanitizedHtmlAttribute.base(this, 'constructor');383};384goog.inherits(385goog.soy.data.SanitizedHtmlAttribute, goog.soy.data.SanitizedContent);386387/** @override */388goog.soy.data.SanitizedHtmlAttribute.prototype.contentKind =389goog.soy.data.SanitizedContentKind.ATTRIBUTES;390391/** @override */392goog.soy.data.SanitizedHtmlAttribute.prototype.contentDir =393goog.i18n.bidi.Dir.LTR;394395/**396* Checks if the value could be used as the Soy type {attribute}.397* @param {*} value398* @return {boolean}399*/400goog.soy.data.SanitizedHtmlAttribute.isCompatibleWith = function(value) {401return goog.isString(value) ||402value instanceof goog.soy.data.SanitizedHtmlAttribute ||403value instanceof goog.soy.data.UnsanitizedText;404};405406407408/**409* Content of type {@link goog.soy.data.SanitizedContentKind.CSS}.410*411* The content is non-attacker-exploitable CSS, such as {@code color:#c3d9ff}.412* The content direction is LTR.413*414* @extends {goog.soy.data.SanitizedContent}415* @constructor416*/417goog.soy.data.SanitizedCss = function() {418goog.soy.data.SanitizedCss.base(this, 'constructor');419};420goog.inherits(goog.soy.data.SanitizedCss, goog.soy.data.SanitizedContent);421422423/** @override */424goog.soy.data.SanitizedCss.prototype.contentKind =425goog.soy.data.SanitizedContentKind.CSS;426427428/** @override */429goog.soy.data.SanitizedCss.prototype.contentDir = goog.i18n.bidi.Dir.LTR;430431432/**433* Checks if the value could be used as the Soy type {css}.434* @param {*} value435* @return {boolean}436*/437goog.soy.data.SanitizedCss.isCompatibleWith = function(value) {438return goog.isString(value) ||439value instanceof goog.soy.data.SanitizedCss ||440value instanceof goog.soy.data.UnsanitizedText ||441value instanceof goog.html.SafeStyle;442};443444445