mirror of
https://github.com/zhigang1992/react-native-web.git
synced 2026-03-27 01:34:17 +08:00
[add] support for CSP policy requiring 'nonce' on <style>
CSP policy may prevent writing to `<style>` unless a `nonce` attribute
is set. This change makes that possible by moving the modality-related
styles into the main style sheet, and allowing additional props to be
provided to the `<style>` element when rendering on the server. For
example:
```
const { element, getStyleElement } = AppRegistry.getApplication('App');
const html = renderToString(element);
const css = renderToStaticMarkup(getStyleElement({ nonce }));
```
This commit is contained in:
@@ -39,6 +39,14 @@ describe('AppRegistry', () => {
|
||||
expect(styleElement).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('"getStyleElement" adds props to <style>', () => {
|
||||
const nonce = '2Bz9RM/UHvBbmo3jK/PbYZ==';
|
||||
AppRegistry.registerComponent('App', () => RootComponent);
|
||||
const { getStyleElement } = AppRegistry.getApplication('App', {});
|
||||
const styleElement = getStyleElement({ nonce });
|
||||
expect(styleElement.props.nonce).toBe(nonce);
|
||||
});
|
||||
|
||||
test('"getStyleElement" produces styles that are a function of rendering "element"', () => {
|
||||
const getApplicationStyles = appName => {
|
||||
const { element, getStyleElement } = AppRegistry.getApplication(appName, {});
|
||||
|
||||
@@ -46,9 +46,11 @@ export function getApplication(
|
||||
</AppContainer>
|
||||
);
|
||||
// Don't escape CSS text
|
||||
const getStyleElement = () => {
|
||||
const getStyleElement = props => {
|
||||
const sheet = styleResolver.getStyleSheet();
|
||||
return <style dangerouslySetInnerHTML={{ __html: sheet.textContent }} id={sheet.id} />;
|
||||
return (
|
||||
<style {...props} dangerouslySetInnerHTML={{ __html: sheet.textContent }} id={sheet.id} />
|
||||
);
|
||||
};
|
||||
return { element, getStyleElement };
|
||||
}
|
||||
|
||||
@@ -69,10 +69,6 @@ export default class StyleSheetManager {
|
||||
return className;
|
||||
}
|
||||
|
||||
injectKeyframe(): string {
|
||||
// return identifier;
|
||||
}
|
||||
|
||||
_addToCache(className, prop, value) {
|
||||
const cache = this._cache;
|
||||
if (!cache.byProp[prop]) {
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
*/
|
||||
|
||||
import { canUseDOM } from 'fbjs/lib/ExecutionEnvironment';
|
||||
import modality from './modality';
|
||||
|
||||
export default class WebStyleSheet {
|
||||
_cssRules = [];
|
||||
@@ -29,6 +30,7 @@ export default class WebStyleSheet {
|
||||
}
|
||||
|
||||
if (domStyleElement) {
|
||||
modality(domStyleElement);
|
||||
// $FlowFixMe
|
||||
this._sheet = domStyleElement.sheet;
|
||||
this._textContent = domStyleElement.textContent;
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
import modality from '../../modules/modality';
|
||||
import StyleSheet from './StyleSheet';
|
||||
|
||||
// initialize focus-ring fix
|
||||
modality();
|
||||
|
||||
// allow component styles to be editable in React Dev Tools
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
const { canUseDOM } = require('fbjs/lib/ExecutionEnvironment');
|
||||
|
||||
@@ -18,12 +18,14 @@
|
||||
|
||||
import { canUseDOM } from 'fbjs/lib/ExecutionEnvironment';
|
||||
|
||||
const modality = () => {
|
||||
const rule = ':focus { outline: none; }';
|
||||
let ruleExists = false;
|
||||
|
||||
const modality = styleElement => {
|
||||
if (!canUseDOM) {
|
||||
return;
|
||||
}
|
||||
|
||||
let styleElement;
|
||||
let hadKeyboardEvent = false;
|
||||
let keyboardThrottleTimeoutID = 0;
|
||||
|
||||
@@ -55,21 +57,6 @@ const modality = () => {
|
||||
'[role=textbox]'
|
||||
].join(',');
|
||||
|
||||
/**
|
||||
* Disable the focus ring by default
|
||||
*/
|
||||
const initialize = () => {
|
||||
// check if the style sheet needs to be created
|
||||
const id = 'react-native-modality';
|
||||
styleElement = document.getElementById(id);
|
||||
if (!styleElement) {
|
||||
// removes focus styles by default
|
||||
const style = `<style id="${id}">:focus { outline: none; }</style>`;
|
||||
document.head.insertAdjacentHTML('afterbegin', style);
|
||||
styleElement = document.getElementById(id);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Computes whether the given element should automatically trigger the
|
||||
* `focus-ring`.
|
||||
@@ -83,20 +70,22 @@ const modality = () => {
|
||||
};
|
||||
|
||||
/**
|
||||
* Add the focus ring to the focused element
|
||||
* Add the focus ring style
|
||||
*/
|
||||
const addFocusRing = () => {
|
||||
if (styleElement) {
|
||||
styleElement.disabled = true;
|
||||
if (styleElement && ruleExists) {
|
||||
styleElement.sheet.deleteRule(0);
|
||||
ruleExists = false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove the focus ring
|
||||
* Remove the focus ring style
|
||||
*/
|
||||
const removeFocusRing = () => {
|
||||
if (styleElement) {
|
||||
styleElement.disabled = false;
|
||||
if (styleElement && !ruleExists) {
|
||||
styleElement.sheet.insertRule(rule, 0);
|
||||
ruleExists = true;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -136,7 +125,7 @@ const modality = () => {
|
||||
};
|
||||
|
||||
if (document.body && document.body.addEventListener) {
|
||||
initialize();
|
||||
removeFocusRing();
|
||||
document.body.addEventListener('keydown', handleKeyDown, true);
|
||||
document.body.addEventListener('focus', handleFocus, true);
|
||||
document.body.addEventListener('blur', handleBlur, true);
|
||||
@@ -31,10 +31,18 @@ const AppRegistryScreen = () => (
|
||||
/>
|
||||
|
||||
<DocItem
|
||||
description="Use this for server-side rendering to HTML. Returns a object of the given application's element, and a function to get styles once the element is rendered."
|
||||
description={
|
||||
<AppText>
|
||||
Use this for server-side rendering to HTML. Returns an object containing the given
|
||||
application's element and a function to get styles once the element is rendered.
|
||||
Additional props can be passed to the <Code>getStyleElement</Code> function, e.g., your
|
||||
CSP policy may require a <Code>nonce</Code> to be set on <Code>style</Code>
|
||||
elements.
|
||||
</AppText>
|
||||
}
|
||||
label="web"
|
||||
name="static getApplication"
|
||||
typeInfo="(appKey: string, appParameters: ?object) => { element: ReactElement; getStyleElement: () => ReactElement }"
|
||||
typeInfo="(appKey: string, appParameters: ?object) => { element: ReactElement; getStyleElement: (props) => ReactElement }"
|
||||
/>
|
||||
|
||||
<DocItem
|
||||
|
||||
Reference in New Issue
Block a user