it('should escape string', function() { var escaped = escapeTextForBrowser('<script type=\'\' src=""></script>'); expect(escaped).not.toContain('<'); expect(escaped).not.toContain('>'); expect(escaped).not.toContain('\''); expect(escaped).not.toContain('/'); expect(escaped).not.toContain('\"'); escaped = escapeTextForBrowser('&'); expect(escaped).toBe('&'); });
_createOpenTagMarkup: function() { var props = this.props; var ret = this._tagOpen; for (var propKey in props) { if (!props.hasOwnProperty(propKey)) { continue; } var propValue = props[propKey]; if (propValue == null) { continue; } if (registrationNames[propKey]) { putListener(this._rootNodeID, propKey, propValue); } else { if (propKey === STYLE) { if (propValue) { propValue = props.style = merge(props.style); } propValue = CSSPropertyOperations.createMarkupForStyles(propValue); } var markup = DOMPropertyOperations.createMarkupForProperty(propKey, propValue); if (markup) { ret += ' ' + markup; } } } var escapedID = escapeTextForBrowser(this._rootNodeID); return ret + ' ' + ReactMount.ATTR_NAME + '="' + escapedID + '">'; },
mountComponent: function(rootID) { ReactComponent.Mixin.mountComponent.call(this, rootID); return ( '<span id="' + rootID + '">' + escapeTextForBrowser(this.props.text) + '</span>' ); },
var flattenChildrenImpl = function(res, children, nameSoFar) { var key, escapedKey; if (Array.isArray(children)) { for (var i = 0; i < children.length; i++) { var child = children[i]; key = child && (child._key || (child.props && child.props.key)); escapedKey = key ? escapeTextForBrowser(key) : ('' + i); flattenChildrenImpl( res, child, nameSoFar + ':' + escapedKey ); } } else { var type = typeof children; var isOnlyChild = nameSoFar === ''; var storageName = isOnlyChild ? ONLY_CHILD_NAME : nameSoFar; if (children === null || children === undefined || type === 'boolean') { res[storageName] = null; } else if (children.mountComponentIntoNode) { /* We found a component instance */ if (__DEV__) { throwIf(res.hasOwnProperty(storageName), DUPLICATE_KEY_ERROR); } res[storageName] = children; } else { if (type === 'object') { throwIf(children && children.nodeType === 1, INVALID_CHILD); for (key in children) { if (children.hasOwnProperty(key)) { escapedKey = escapeTextForBrowser(key); flattenChildrenImpl( res, children[key], nameSoFar + ':' + escapedKey ); } } } else if (type === 'string') { res[storageName] = new ReactTextComponent(children); } else if (type === 'number') { res[storageName] = new ReactTextComponent('' + children); } } } };
it('should escape object to string', function() { var escaped = escapeTextForBrowser({ toString: function() { return 'ponys'; } }); expect(escaped).toBe('ponys'); });
createMarkupForProperty: function(name, value) { if (DOMProperty.isStandardName[name]) { if (value == null || DOMProperty.hasBooleanValue[name] && !value) { return ''; } var attributeName = DOMProperty.getAttributeName[name]; return processAttributeNameAndPrefix(attributeName) + escapeTextForBrowser(value) + '"'; } else if (DOMProperty.isCustomAttribute(name)) { if (value == null) { return ''; } return processAttributeNameAndPrefix(name) + escapeTextForBrowser(value) + '"'; } else { return null; } },
createMarkupForProperty: function(name, value) { if (DOMProperty.isStandardName[name]) { if (shouldIgnoreValue(name, value)) { return ''; } var attributeName = DOMProperty.getAttributeName[name]; return processAttributeNameAndPrefix(attributeName) + escapeTextForBrowser(value) + '"'; } else if (DOMProperty.isCustomAttribute(name)) { if (value == null) { return ''; } return processAttributeNameAndPrefix(name) + escapeTextForBrowser(value) + '"'; } else if (__DEV__) { warnUnknownProperty(name); } return null; },
mountComponent: function(rootID, transaction, mountDepth) { ReactComponent.Mixin.mountComponent.call( this, rootID, transaction, mountDepth ); return ( '<span ' + DOMPropertyOperations.createMarkupForID(rootID) + '>' + escapeTextForBrowser(this.props.text) + '</span>' ); },
mountComponent: function(rootID, transaction, mountDepth) { ReactComponent.Mixin.mountComponent.call( this, rootID, transaction, mountDepth ); return ( '<span ' + ReactMount.ATTR_NAME + '="' + rootID + '">' + escapeTextForBrowser(this.props.text) + '</span>' ); },
mountComponent: function(rootID, transaction, context) { this._rootNodeID = rootID; var escapedText = escapeTextForBrowser(this._stringText); if (transaction.renderToStaticMarkup) { // Normally we'd wrap this in a `span` for the reasons stated above, but // since this is a situation where React won't take over (static pages), // we can simply return the text as it is. return escapedText; } return ( '<span ' + DOMPropertyOperations.createMarkupForID(rootID) + '>' + escapedText + '</span>' ); },
_createContentMarkup: function(transaction) { // Intentional use of != to avoid catching zero/false. var innerHTML = this.props.dangerouslySetInnerHTML; if (innerHTML != null) { if (innerHTML.__html != null) { return innerHTML.__html; } } else { var contentToUse = CONTENT_TYPES[typeof this.props.children] ? this.props.children : null; var childrenToUse = contentToUse != null ? null : this.props.children; if (contentToUse != null) { return escapeTextForBrowser(contentToUse); } else if (childrenToUse != null) { return this.mountMultiChild( flattenChildren(childrenToUse), transaction ); } } return ''; },
_createContentMarkup: function(transaction, context) { var prefix = ''; if (this._tag === 'listing' || this._tag === 'pre' || this._tag === 'textarea') { // Add an initial newline because browsers ignore the first newline in // a <listing>, <pre>, or <textarea> as an "authoring convenience" -- see // https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-inbody. prefix = '\n'; } var props = this._currentElement.props; // Intentional use of != to avoid catching zero/false. var innerHTML = props.dangerouslySetInnerHTML; if (innerHTML != null) { if (innerHTML.__html != null) { return prefix + innerHTML.__html; } } else { var contentToUse = CONTENT_TYPES[typeof props.children] ? props.children : null; var childrenToUse = contentToUse != null ? null : props.children; if (contentToUse != null) { return prefix + escapeTextForBrowser(contentToUse); } else if (childrenToUse != null) { var mountImages = this.mountChildren( childrenToUse, transaction, context ); return prefix + mountImages.join(''); } } return prefix; },
createMarkupForID: function(id) { return processAttributeNameAndPrefix(DOMProperty.ID_ATTRIBUTE_NAME) + escapeTextForBrowser(id) + '"'; },
var processAttributeNameAndPrefix = memoizeStringOnly(function(name) { return escapeTextForBrowser(name) + '="'; });
var processStyleName = memoizeStringOnly(function(styleName) { return escapeTextForBrowser(hyphenate(styleName)); });
function assertValidProps(e){e&&(invariant(null==e.children||null==e.dangerouslySetInnerHTML,"Can only set one of `children` or `props.dangerouslySetInnerHTML`."),__DEV__&&e.contentEditable&&null!=e.children&&console.warn("A component is `contentEditable` and contains `children` managed by React. It is now your responsibility to guarantee that none of those nodes are unexpectedly modified or duplicated. This is probably not intentional."),invariant(null==e.style||"object"==typeof e.style,"The `style` prop expects a mapping from style properties to values, not a string."))}function putListener(e,t,n,r){__DEV__&&("onScroll"!==t||isEventSupported("scroll",!0)||(monitorCodeUse("react_no_scroll_event"),console.warn("This browser doesn't support the `onScroll` event")));var o=ReactMount.findReactContainerForID(e);if(o){var i=o.nodeType===ELEMENT_NODE_TYPE?o.ownerDocument:o;listenTo(t,i)}r.getPutListenerQueue().enqueuePutListener(e,t,n)}function validateDangerousTag(e){hasOwnProperty.call(validatedTagCache,e)||(invariant(VALID_TAG_REGEX.test(e),"Invalid tag: %s",e),validatedTagCache[e]=!0)}function ReactDOMComponent(e){validateDangerousTag(e),this._tag=e,this.tagName=e.toUpperCase()}var CSSPropertyOperations=require("CSSPropertyOperations"),DOMProperty=require("DOMProperty"),DOMPropertyOperations=require("DOMPropertyOperations"),ReactBrowserComponentMixin=require("ReactBrowserComponentMixin"),ReactComponent=require("ReactComponent"),ReactBrowserEventEmitter=require("ReactBrowserEventEmitter"),ReactMount=require("ReactMount"),ReactMultiChild=require("ReactMultiChild"),ReactPerf=require("ReactPerf"),assign=require("Object.assign"),escapeTextForBrowser=require("escapeTextForBrowser"),invariant=require("invariant"),isEventSupported=require("isEventSupported"),keyOf=require("keyOf"),monitorCodeUse=require("monitorCodeUse"),deleteListener=ReactBrowserEventEmitter.deleteListener,listenTo=ReactBrowserEventEmitter.listenTo,registrationNameModules=ReactBrowserEventEmitter.registrationNameModules,CONTENT_TYPES={string:!0,number:!0},STYLE=keyOf({style:null}),ELEMENT_NODE_TYPE=1,omittedCloseTags={area:!0,base:!0,br:!0,col:!0,embed:!0,hr:!0,img:!0,input:!0,keygen:!0,link:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0},VALID_TAG_REGEX=/^[a-zA-Z][a-zA-Z:_\.\-\d]*$/,validatedTagCache={},hasOwnProperty={}.hasOwnProperty;ReactDOMComponent.displayName="ReactDOMComponent",ReactDOMComponent.Mixin={mountComponent:ReactPerf.measure("ReactDOMComponent","mountComponent",function(e,t,n){ReactComponent.Mixin.mountComponent.call(this,e,t,n),assertValidProps(this.props);var r=omittedCloseTags[this._tag]?"":"</"+this._tag+">";return this._createOpenTagMarkupAndPutListeners(t)+this._createContentMarkup(t)+r}),_createOpenTagMarkupAndPutListeners:function(e){var t=this.props,n="<"+this._tag;for(var r in t)if(t.hasOwnProperty(r)){var o=t[r];if(null!=o)if(registrationNameModules.hasOwnProperty(r))putListener(this._rootNodeID,r,o,e);else{r===STYLE&&(o&&(o=t.style=assign({},t.style)),o=CSSPropertyOperations.createMarkupForStyles(o));var i=DOMPropertyOperations.createMarkupForProperty(r,o);i&&(n+=" "+i)}}if(e.renderToStaticMarkup)return n+">";var a=DOMPropertyOperations.createMarkupForID(this._rootNodeID);return n+" "+a+">"},_createContentMarkup:function(e){var t=this.props.dangerouslySetInnerHTML;if(null!=t){if(null!=t.__html)return t.__html}else{var n=CONTENT_TYPES[typeof this.props.children]?this.props.children:null,r=null!=n?null:this.props.children;if(null!=n)return escapeTextForBrowser(n);if(null!=r){var o=this.mountChildren(r,e);return o.join("")}}return""},receiveComponent:function(e,t){(e!==this._currentElement||null==e._owner)&&ReactComponent.Mixin.receiveComponent.call(this,e,t)},updateComponent:ReactPerf.measure("ReactDOMComponent","updateComponent",function(e,t){assertValidProps(this._currentElement.props),ReactComponent.Mixin.updateComponent.call(this,e,t),this._updateDOMProperties(t.props,e),this._updateDOMChildren(t.props,e)}),_updateDOMProperties:function(e,t){var n,r,o,i=this.props;for(n in e)if(!i.hasOwnProperty(n)&&e.hasOwnProperty(n))if(n===STYLE){var a=e[n];for(r in a)a.hasOwnProperty(r)&&(o=o||{},o[r]="")}else registrationNameModules.hasOwnProperty(n)?deleteListener(this._rootNodeID,n):(DOMProperty.isStandardName[n]||DOMProperty.isCustomAttribute(n))&&ReactComponent.BackendIDOperations.deletePropertyByID(this._rootNodeID,n);for(n in i){var s=i[n],l=e[n];if(i.hasOwnProperty(n)&&s!==l)if(n===STYLE)if(s&&(s=i.style=assign({},s)),l){for(r in l)!l.hasOwnProperty(r)||s&&s.hasOwnProperty(r)||(o=o||{},o[r]="");for(r in s)s.hasOwnProperty(r)&&l[r]!==s[r]&&(o=o||{},o[r]=s[r])}else o=s;else registrationNameModules.hasOwnProperty(n)?putListener(this._rootNodeID,n,s,t):(DOMProperty.isStandardName[n]||DOMProperty.isCustomAttribute(n))&&ReactComponent.BackendIDOperations.updatePropertyByID(this._rootNodeID,n,s)}o&&ReactComponent.BackendIDOperations.updateStylesByID(this._rootNodeID,o)},_updateDOMChildren:function(e,t){var n=this.props,r=CONTENT_TYPES[typeof e.children]?e.children:null,o=CONTENT_TYPES[typeof n.children]?n.children:null,i=e.dangerouslySetInnerHTML&&e.dangerouslySetInnerHTML.__html,a=n.dangerouslySetInnerHTML&&n.dangerouslySetInnerHTML.__html,s=null!=r?null:e.children,l=null!=o?null:n.children,p=null!=r||null!=i,u=null!=o||null!=a;null!=s&&null==l?this.updateChildren(null,t):p&&!u&&this.updateTextContent(""),null!=o?r!==o&&this.updateTextContent(""+o):null!=a?i!==a&&ReactComponent.BackendIDOperations.updateInnerHTMLByID(this._rootNodeID,a):null!=l&&this.updateChildren(l,t)},unmountComponent:function(){this.unmountChildren(),ReactBrowserEventEmitter.deleteAllListeners(this._rootNodeID),ReactComponent.Mixin.unmountComponent.call(this)}},assign(ReactDOMComponent.prototype,ReactComponent.Mixin,ReactDOMComponent.Mixin,ReactMultiChild.Mixin,ReactBrowserComponentMixin),module.exports=ReactDOMComponent;
it('should escape boolean to string', function() { expect(escapeTextForBrowser(true)).toBe('true'); expect(escapeTextForBrowser(false)).toBe('false'); });
it('should escape number to string', function() { expect(escapeTextForBrowser(42)).toBe('42'); });