/** * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * @flow */ import { registrationNameDependencies, possibleRegistrationNames, } from '../events/EventRegistry'; import {canUseDOM} from 'shared/ExecutionEnvironment'; import hasOwnProperty from 'shared/hasOwnProperty'; import {checkHtmlStringCoercion} from 'shared/CheckStringCoercion'; import { getValueForAttribute, getValueForProperty, setValueForProperty, } from './DOMPropertyOperations'; import { initWrapperState as ReactDOMInputInitWrapperState, getHostProps as ReactDOMInputGetHostProps, postMountWrapper as ReactDOMInputPostMountWrapper, updateChecked as ReactDOMInputUpdateChecked, updateWrapper as ReactDOMInputUpdateWrapper, restoreControlledState as ReactDOMInputRestoreControlledState, } from './ReactDOMInput'; import { postMountWrapper as ReactDOMOptionPostMountWrapper, validateProps as ReactDOMOptionValidateProps, } from './ReactDOMOption'; import { initWrapperState as ReactDOMSelectInitWrapperState, getHostProps as ReactDOMSelectGetHostProps, postMountWrapper as ReactDOMSelectPostMountWrapper, restoreControlledState as ReactDOMSelectRestoreControlledState, postUpdateWrapper as ReactDOMSelectPostUpdateWrapper, } from './ReactDOMSelect'; import { initWrapperState as ReactDOMTextareaInitWrapperState, getHostProps as ReactDOMTextareaGetHostProps, postMountWrapper as ReactDOMTextareaPostMountWrapper, updateWrapper as ReactDOMTextareaUpdateWrapper, restoreControlledState as ReactDOMTextareaRestoreControlledState, } from './ReactDOMTextarea'; import {track} from './inputValueTracking'; import setInnerHTML from './setInnerHTML'; import setTextContent from './setTextContent'; import { createDangerousStringForStyles, setValueForStyles, validateShorthandPropertyCollisionInDev, } from './CSSPropertyOperations'; import {HTML_NAMESPACE, getIntrinsicNamespace} from '../shared/DOMNamespaces'; import { getPropertyInfo, shouldIgnoreAttribute, shouldRemoveAttribute, } from '../shared/DOMProperty'; import assertValidProps from '../shared/assertValidProps'; import {DOCUMENT_NODE} from '../shared/HTMLNodeType'; import isCustomComponent from '../shared/isCustomComponent'; import possibleStandardNames from '../shared/possibleStandardNames'; import {validateProperties as validateARIAProperties} from '../shared/ReactDOMInvalidARIAHook'; import {validateProperties as validateInputProperties} from '../shared/ReactDOMNullInputValuePropHook'; import {validateProperties as validateUnknownProperties} from '../shared/ReactDOMUnknownPropertyHook'; import { enableTrustedTypesIntegration, enableCustomElementPropertySupport, enableClientRenderFallbackOnTextMismatch, } from 'shared/ReactFeatureFlags'; import { mediaEventTypes, listenToNonDelegatedEvent, } from '../events/DOMPluginEventSystem'; let didWarnInvalidHydration = false; let didWarnScriptTags = false; const DANGEROUSLY_SET_INNER_HTML = 'dangerouslySetInnerHTML'; const SUPPRESS_CONTENT_EDITABLE_WARNING = 'suppressContentEditableWarning'; const SUPPRESS_HYDRATION_WARNING = 'suppressHydrationWarning'; const AUTOFOCUS = 'autoFocus'; const CHILDREN = 'children'; const STYLE = 'style'; const HTML = '__html'; let warnedUnknownTags; let validatePropertiesInDevelopment; let warnForPropDifference; let warnForExtraAttributes; let warnForInvalidEventListener; let canDiffStyleForHydrationWarning; let normalizeHTML; if (__DEV__) { warnedUnknownTags = { // There are working polyfills for . Let people use it. dialog: true, // Electron ships a custom tag to display external web content in // an isolated frame and process. // This tag is not present in non Electron environments such as JSDom which // is often used for testing purposes. // @see https://electronjs.org/docs/api/webview-tag webview: true, }; validatePropertiesInDevelopment = function(type, props) { validateARIAProperties(type, props); validateInputProperties(type, props); validateUnknownProperties(type, props, { registrationNameDependencies, possibleRegistrationNames, }); }; // IE 11 parses & normalizes the style attribute as opposed to other // browsers. It adds spaces and sorts the properties in some // non-alphabetical order. Handling that would require sorting CSS // properties in the client & server versions or applying // `expectedStyle` to a temporary DOM node to read its `style` attribute // normalized. Since it only affects IE, we're skipping style warnings // in that browser completely in favor of doing all that work. // See https://github.com/facebook/react/issues/11807 canDiffStyleForHydrationWarning = canUseDOM && !document.documentMode; warnForPropDifference = function( propName: string, serverValue: mixed, clientValue: mixed, ) { if (didWarnInvalidHydration) { return; } const normalizedClientValue = normalizeMarkupForTextOrAttribute( clientValue, ); const normalizedServerValue = normalizeMarkupForTextOrAttribute( serverValue, ); if (normalizedServerValue === normalizedClientValue) { return; } didWarnInvalidHydration = true; console.error( 'Prop `%s` did not match. Server: %s Client: %s', propName, JSON.stringify(normalizedServerValue), JSON.stringify(normalizedClientValue), ); }; warnForExtraAttributes = function(attributeNames: Set) { if (didWarnInvalidHydration) { return; } didWarnInvalidHydration = true; const names = []; attributeNames.forEach(function(name) { names.push(name); }); console.error('Extra attributes from the server: %s', names); }; warnForInvalidEventListener = function(registrationName, listener) { if (listener === false) { console.error( 'Expected `%s` listener to be a function, instead got `false`.\n\n' + 'If you used to conditionally omit it with %s={condition && value}, ' + 'pass %s={condition ? value : undefined} instead.', registrationName, registrationName, registrationName, ); } else { console.error( 'Expected `%s` listener to be a function, instead got a value of `%s` type.', registrationName, typeof listener, ); } }; // Parse the HTML and read it back to normalize the HTML string so that it // can be used for comparison. normalizeHTML = function(parent: Element, html: string) { // We could have created a separate document here to avoid // re-initializing custom elements if they exist. But this breaks // how