it('should escape string', function() {
    var escaped = escapeTextContentForBrowser('<script type=\'\' src=""></script>');
    expect(escaped).not.toContain('<');
    expect(escaped).not.toContain('>');
    expect(escaped).not.toContain('\'');
    expect(escaped).not.toContain('\"');

    escaped = escapeTextContentForBrowser('&');
    expect(escaped).toBe('&amp;');
  });
  it('should escape string', () => {
    var escaped = escapeTextContentForBrowser(
      '<script type=\'\' src=""></script>',
    );
    expect(escaped).not.toContain('<');
    expect(escaped).not.toContain('>');
    expect(escaped).not.toContain("'");
    expect(escaped).not.toContain('"');

    escaped = escapeTextContentForBrowser('&');
    expect(escaped).toBe('&amp;');
  });
Example #3
0
  mountComponent: function(rootID, transaction, context) {
    if (__DEV__) {
      if (context[validateDOMNesting.tagStackContextKey]) {
        validateDOMNesting(
          context[validateDOMNesting.tagStackContextKey],
          'span',
          null
        );
      }
    }

    this._rootNodeID = rootID;
    var escapedText = escapeTextContentForBrowser(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>'
    );
  },
Example #4
0
  mountComponent: function(
    transaction,
    hostParent,
    hostContainerInfo,
    context,
  ) {
    if (__DEV__) {
      var parentInfo;
      if (hostParent != null) {
        parentInfo = hostParent._ancestorInfo;
      } else if (hostContainerInfo != null) {
        parentInfo = hostContainerInfo._ancestorInfo;
      }
      if (parentInfo) {
        // parentInfo should always be present except for the top-level
        // component when server rendering
        validateDOMNesting(null, this._stringText, this, parentInfo);
      }
    }

    var domID = hostContainerInfo._idCounter++;
    var openingValue = ' react-text: ' + domID + ' ';
    var closingValue = ' /react-text ';
    this._domID = domID;
    this._hostParent = hostParent;
    if (transaction.useCreateElement) {
      var ownerDocument = hostContainerInfo._ownerDocument;
      var openingComment = ownerDocument.createComment(openingValue);
      var closingComment = ownerDocument.createComment(closingValue);
      var lazyTree = DOMLazyTree(ownerDocument.createDocumentFragment());
      DOMLazyTree.queueChild(lazyTree, DOMLazyTree(openingComment));
      if (this._stringText) {
        DOMLazyTree.queueChild(
          lazyTree,
          DOMLazyTree(ownerDocument.createTextNode(this._stringText)),
        );
      }
      DOMLazyTree.queueChild(lazyTree, DOMLazyTree(closingComment));
      ReactDOMComponentTree.precacheNode(this, openingComment);
      this._closingComment = closingComment;
      return lazyTree;
    } else {
      var escapedText = escapeTextContentForBrowser(this._stringText);

      if (transaction.renderToStaticMarkup) {
        // Normally we'd wrap this between comment nodes 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 '<!--' +
        openingValue +
        '-->' +
        escapedText +
        '<!--' +
        closingValue +
        '-->';
    }
  },
  it('should escape object to string', () => {
    var escaped = escapeTextContentForBrowser({
      toString: function() {
        return 'ponys';
      },
    });

    expect(escaped).toBe('ponys');
  });
Example #6
0
 render(child, context, parentNamespace) {
   if (typeof child === 'string' || typeof child === 'number') {
     var text = '' + child;
     if (text === '') {
       return '';
     }
     if (this.makeStaticMarkup) {
       return escapeTextContentForBrowser(text);
     }
     if (this.previousWasTextNode) {
       return '<!-- -->' + escapeTextContentForBrowser(text);
     }
     this.previousWasTextNode = true;
     return escapeTextContentForBrowser(text);
   } else {
     ({child, context} = resolve(child, context));
     if (child === null || child === false) {
       return '';
     } else {
       if (React.isValidElement(child)) {
         return this.renderDOM(child, context, parentNamespace);
       } else {
         var children = toArray(child);
         var frame = {
           domNamespace: parentNamespace,
           children,
           childIndex: 0,
           context: context,
           footer: '',
         };
         if (__DEV__) {
           frame.debugElementStack = [];
         }
         this.stack.push(frame);
         return '';
       }
     }
   }
 }
Example #7
0
function getNonChildrenInnerMarkup(props) {
  var innerHTML = props.dangerouslySetInnerHTML;
  if (innerHTML != null) {
    if (innerHTML.__html != null) {
      return innerHTML.__html;
    }
  } else {
    var content = props.children;
    if (typeof content === 'string' || typeof content === 'number') {
      return escapeTextContentForBrowser(content);
    }
  }
  return null;
}
Example #8
0
 render(child, context) {
   if (typeof child === 'string' || typeof child === 'number') {
     var text = '' + child;
     if (text === '') {
       return '';
     }
     if (this.makeStaticMarkup) {
       return escapeTextContentForBrowser(text);
     }
     if (this.previousWasTextNode) {
       return '<!-- -->' + escapeTextContentForBrowser(text);
     }
     this.previousWasTextNode = true;
     return escapeTextContentForBrowser(text);
   } else {
     ({child, context} = resolve(child, context));
     if (child === null || child === false) {
       return '';
     } else {
       return this.renderDOM(child, context);
     }
   }
 }
  mountComponent: function(
    transaction,
    nativeParent,
    nativeContainerInfo,
    context
  ) {
    if (__DEV__) {
      var parentInfo;
      if (nativeParent != null) {
        parentInfo = nativeParent._ancestorInfo;
      } else if (nativeContainerInfo != null) {
        parentInfo = nativeContainerInfo._ancestorInfo;
      }
      if (parentInfo) {
        // parentInfo should always be present except for the top-level
        // component when server rendering
        validateDOMNesting('span', this, parentInfo);
      }
    }

    var domID = nativeContainerInfo._idCounter++;
    this._domID = domID;
    this._nativeParent = nativeParent;
    if (transaction.useCreateElement) {
      var ownerDocument = nativeContainerInfo._ownerDocument;
      var el = ownerDocument.createElement('span');
      ReactDOMComponentTree.precacheNode(this, el);
      var lazyTree = DOMLazyTree(el);
      DOMLazyTree.queueText(lazyTree, this._stringText);
      return lazyTree;
    } else {
      var escapedText = escapeTextContentForBrowser(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(domID) + '>' +
          escapedText +
        '</span>'
      );
    }
  },
Example #10
0
  _createContentMarkup: function(transaction, props, context) {
    var ret = '';

    // Intentional use of != to avoid catching zero/false.
    var innerHTML = props.dangerouslySetInnerHTML;
    if (innerHTML != null) {
      if (innerHTML.__html != null) {
        ret = innerHTML.__html;
      }
    } else {
      var contentToUse = CONTENT_TYPES[typeof props.children]
        ? props.children
        : null;
      var childrenToUse = contentToUse != null ? null : props.children;
      if (contentToUse != null) {
        // TODO: Validate that text is allowed as a child of this node
        ret = escapeTextContentForBrowser(contentToUse);
        if (__DEV__) {
          setAndValidateContentChildDev.call(this, contentToUse);
        }
      } else if (childrenToUse != null) {
        var mountImages = this.mountChildren(
          childrenToUse,
          transaction,
          context,
        );
        ret = mountImages.join('');
      }
    }
    if (newlineEatingTags[this._tag] && ret.charAt(0) === '\n') {
      // text/html ignores the first character in these tags if it's a newline
      // Prefer to break application/xml over text/html (for now) by adding
      // a newline specifically to get eaten by the parser. (Alternately for
      // textareas, replacing "^\n" with "\r\n" doesn't get eaten, and the first
      // \r is normalized out by HTMLTextAreaElement#value.)
      // See: <http://www.w3.org/TR/html-polyglot/#newlines-in-textarea-and-pre>
      // See: <http://www.w3.org/TR/html5/syntax.html#element-restrictions>
      // See: <http://www.w3.org/TR/html5/syntax.html#newlines>
      // See: Parsing of "textarea" "listing" and "pre" elements
      //  from <http://www.w3.org/TR/html5/syntax.html#parsing-main-inbody>
      return '\n' + ret;
    } else {
      return ret;
    }
  },
  mountComponent: function(rootID, transaction, context) {
    if (__DEV__) {
      if (context[validateDOMNesting.ancestorInfoContextKey]) {
        validateDOMNesting(
          'span',
          null,
          context[validateDOMNesting.ancestorInfoContextKey]
        );
      }
    }

    this._rootNodeID = rootID;
    if (transaction.useCreateElement) {
      var ownerDocument = context[ReactMount.ownerDocumentContextKey];
      var el = ownerDocument.createElement('span');
      DOMPropertyOperations.setAttributeForID(el, rootID);
      // Populate node cache
      ReactMount.getID(el);
      setTextContent(el, this._stringText);
      return el;
    } else {
      var escapedText = escapeTextContentForBrowser(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, 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 + escapeTextContentForBrowser(contentToUse);
      } else if (childrenToUse != null) {
        var mountImages = this.mountChildren(
          childrenToUse,
          transaction,
          context
        );
        return prefix + mountImages.join('');
      }
    }
    return prefix;
  },
 it('should escape number to string', () => {
   expect(escapeTextContentForBrowser(42)).toBe('42');
 });
 it('should escape boolean to string', () => {
   expect(escapeTextContentForBrowser(true)).toBe('true');
   expect(escapeTextContentForBrowser(false)).toBe('false');
 });
Example #15
0
 setTextContent = function(node, text) {
   setInnerHTML(node, escapeTextContentForBrowser(text));
 };