-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix #418 Use of inline styles doesn't play nice with Content Security Policy #1976
Conversation
…an updating dynamic stylesheets * removing logic related to dynamic stylesheets as they aren't required and violate CSP sctrict style-src rule * removed UTs related to Dynamic stylesheets
src/gridstack.ts
Outdated
@@ -776,7 +775,7 @@ export class GridStack { | |||
} else { | |||
this.el.parentNode.removeChild(this.el); | |||
} | |||
this._removeStylesheet(); | |||
this._max = undefined; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no need, already undefined.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thank you for looking into this. Have a few comments and I will need to pull it locally later to test as well.
src/gridstack.ts
Outdated
/** @internal */ | ||
protected _styles: GridCSSStyleSheet; | ||
/** @internal max height of the gridstack*/ | ||
public _max: number; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
does it need to be public ? if so '_' isn't the right prefix, and rename to curRow or something (need to see how it's used)
src/gridstack.ts
Outdated
@@ -240,6 +235,8 @@ export class GridStack { | |||
protected _extraDragRow = 0; | |||
/** @internal true if nested grid should get column count from our width */ | |||
protected _autoColumn?: boolean; | |||
/** @internal calculates height for a given row */ | |||
private _getHeight: (row: number) => string; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
make this a proper TS method, not var you assign to function later.
src/gridstack.ts
Outdated
@@ -338,6 +335,8 @@ export class GridStack { | |||
|
|||
this._setStaticClass(); | |||
|
|||
this._getHeight = (rows: number) => (rows * (this.opts.cellHeight as number)) + this.opts.cellHeightUnit; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
see above
src/gridstack.ts
Outdated
this._styles._id = id; | ||
this._styles._max = 0; | ||
if (!this._max) { | ||
this._max = 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
doesn't prevent if (!this._max) {...} clause, so likely incorrect placement.
src/gridstack.ts
Outdated
} | ||
|
||
// apply layout styles on all widgets | ||
const widgets: HTMLElement[] = Utils.getElements(prefix); | ||
for (const widget of widgets) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Utils.getElements(prefix).forEach(w => Utils.updatePositionStyleOnWidget(w, this._getHeight));
src/utils.ts
Outdated
|
||
// list of fields we will skip during cloneDeep (nested objects, other internal) | ||
const skipFields = ['_isNested', 'el', 'grid', 'subGrid', 'engine']; | ||
import { GridStackElement, GridStackNode, GridStackOptions, numberOrString, GridStackPosition, GridStackWidget } from './types'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
indent all left so we can see actual changes...
* fixed indent in utils.ts * incorporated review comments
Hi @adumesny Thank you so much for such quick feedback. I have incorporated the review comments. I have a couple of questions. I have fixed the wrong indent that I committed. Regards, |
src/gridstack.ts
Outdated
@@ -240,6 +235,8 @@ export class GridStack { | |||
protected _extraDragRow = 0; | |||
/** @internal true if nested grid should get column count from our width */ | |||
protected _autoColumn?: boolean; | |||
/** @internal calculates height for a given row */ | |||
private readonly _getHeight = (rows: number) => (rows * (this.opts.cellHeight as number)) + this.opts.cellHeightUnit; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/** @internal returns the CSS height for a given row */
private _getCSSHeight(rows: number): string {
return (rows * (this.opts.cellHeight as number)) + this.opts.cellHeightUnit;
}
and move it to the function area.
src/gridstack.ts
Outdated
this._styles._id = id; | ||
this._styles._max = 0; | ||
if (!this._maxRowIndex) { | ||
this._maxRowIndex = 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
again this doesn't seem right... set to actual value instead ?
if (!this._styles) { | ||
let id = 'gridstack-style-' + (Math.random() * 100000).toFixed(); | ||
// insert style to parent (instead of 'head' by default) to support WebComponent | ||
let styleLocation = this.opts.styleInHead ? undefined : this.el.parentNode as HTMLElement; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
styleInHead
needs to be removed everywhere (doc and code) as it no longer applies
src/gridstack.ts
Outdated
|
||
// these are done once only | ||
Utils.addCSSRule(this._styles, prefix, `min-height: ${cellHeight}${cellHeightUnit}`); | ||
Utils.updateStyleOnElements(prefix, {"min-height": cellHeight+cellHeightUnit}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we use ' (single) instead of " in TS code.
// content margins | ||
let top: string = this.opts.marginTop + this.opts.marginUnit; | ||
let bottom: string = this.opts.marginBottom + this.opts.marginUnit; | ||
let right: string = this.opts.marginRight + this.opts.marginUnit; | ||
let left: string = this.opts.marginLeft + this.opts.marginUnit; | ||
let content = `${prefix} > .grid-stack-item-content`; | ||
let placeholder = `.${this.opts._styleSheetClass} > .grid-stack-placeholder > .placeholder-content`; | ||
Utils.addCSSRule(this._styles, content, `top: ${top}; right: ${right}; bottom: ${bottom}; left: ${left};`); | ||
Utils.addCSSRule(this._styles, placeholder, `top: ${top}; right: ${right}; bottom: ${bottom}; left: ${left};`); | ||
Utils.updateStyleOnElements(content, {top, right, bottom, left}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
every single new element will also need those attributes, so how do you re-use this code since it isn't a css style anymore ?
src/utils.ts
Outdated
@@ -44,7 +44,7 @@ export function obsoleteAttr(el: HTMLElement, oldName: string, newName: string, | |||
if (oldAttr !== null) { | |||
el.setAttribute(newName, oldAttr); | |||
console.warn('gridstack.js: attribute `' + oldName + '`=' + oldAttr + ' is deprecated on this object in ' + rev + ' and has been replaced with `' + | |||
newName + '`. It will be **completely** removed in v1.0'); | |||
newName + '`. It will be **completely** removed in v1.0'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
revert
src/gridstack.ts
Outdated
@@ -1305,6 +1280,7 @@ export class GridStack { | |||
if (n.y !== undefined && n.y !== null) { el.setAttribute('gs-y', String(n.y)); } | |||
if (n.w) { el.setAttribute('gs-w', String(n.w)); } | |||
if (n.h) { el.setAttribute('gs-h', String(n.h)); } | |||
Utils.updatePositionStyleOnWidget(el, this._getHeight); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would pass n as that is the source of truth, not the gs-*** attributes
src/utils.ts
Outdated
const minH= Utils.toNumber(el.getAttribute('gs-min-h')); | ||
const maxH = Utils.toNumber(el.getAttribute('gs-max-h')); | ||
const y = Utils.toNumber(el.getAttribute('gs-y')); | ||
el.style.height = getHeight(h); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would pass GridStackPosition instead.
and you are setting the height(s) and top only - what about left and widths ? seem one sided...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
also I would not set properties (like min/max) that are not set
src/utils.ts
Outdated
parent.insertBefore(style, parent.firstChild); | ||
/** update CSS style on elements with given selector */ | ||
static updateStyleOnElements(selector: string, styles: {[props: string]: string | string[] }): void { | ||
const elements = Utils.getElements(selector); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Utils.getElements(selector).forEach(el => Utils.addElementStyle(el, styles));
let el = document.querySelector('STYLE[gs-style-id=' + id + ']'); | ||
if (el && el.parentNode) el.remove(); | ||
/** update CSS style on element */ | ||
static addElementStyle(el: HTMLElement, styles: {[props: string]: string | string[] }): void { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (!(styles instanceof Object)) return; // not sure this even needed if (!styles) is enough
for (const key in styles) {
if (!styles.hasOwnProperty(key)) return;
const style = styles[key];
if (Array.isArray(style)) { // why do we need this ?
style.forEach(val => el.style[key] = val);
} else {
el.style[key] = style;
}
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
also your code will update all the elements styles when one of them changes, which will be very inefficient (prev code only did incremental stylesheet update as maxRow was reached).
without deleting gridstack.css (or at least any widget location/size info) and gridstack-extra.css you can't tell if you have handled all cases (I doubt it). Are you ONLY removing JS code that create inline styles, but still allowing static styles sheets (with dynamic class setting). I don't know enough about CSP to tell how far we would need to go...
this will need very different architecture.
I tried demo/two.html and your branch is pretty broken as you resize items around... |
Hi @adumesny CSP strict style rule prohibits the following:
That's why I had to change the implementation such that we set styles on each element. We can use the old architecture and still make it CSP compliant for all browsers except SAFARI. We can use CSSStyleSheet.replaceSync() instead of insertRule() to update styles on dynamic style sheet. We can also instead of appending this dynamic stylesheet can use We can use old architecture with adoptedStyleSheets and replaceSync is all the browsers and new architecture in case of SAFARI. Or ask applications to use precalculated stylesheets in case of safari similar to IE 8 Support. |
so we have a mix of css rules + classes for thing that don't change (eg column based layout) but attributes for height stuff...nthat would satisfy CSP but looks odd. Also we do NOT need to update all elements as we know when a node is dirty and needs to be updated (we already track that) so the call to generate styles need to taken out as it was done only of we added rows (or cellHeight/margin changed). Your logic isn't correct. also keep in m id you changed break some of the demos so there are issues there - just playing with two.html shows issues dragging/resizing around. https://gridstackjs.com/demo/two.html is fine while your version using demo/two.html is not |
Hi @adumesny I am aware of the demos breaking and have fixed the changes locally and will commit shortly. Also so we can use the architecture but update styles on elements only when they are dirty? |
Hi @adumesny I have fixed the code and incorporated the review you sent. The demo is also working perfectly. We are updating styles on dirty nodes only. Can you please review the architecture? If you feel it's risky I will look to accommodate the risky part. The proposed architecture is:
I have fixed the demo it was breaking before because I wasn't updating the position styling on widget content and resizable icon when they are dropped into the grid. But now it all should be fixed. Please let me know your concerns. Thanks for putting your time into this. Regards |
…sn't-play-nice-with-Content-Security-Policy
…nc to insert css rules * safari doesn't support contructable stylesheets and replaceSync yet so we are updating styles directly on elements to make gridstack csp compliant
…sn't-play-nice-with-Content-Security-Policy
this is likely not needed (and quite a risky change) given I would like to close it - we can always get back to it from 'won't fix' issue listed. |
fix #418
Description
Checklist
yarn test
)