How to Prevent CSS Interference in an Injected Piece of HTML

How do I prevent CSS interference in an injected piece of HTML?

You can't prevent that from happen. However, you can override the CSS rules. Give your main element a unique id (which really should be unique by obfustation, like "yourapplicationname_mainelement_name" or something), then override all possible styles that might give strange effects on your html.

Your plugin:

<div id="yourapplicationname_mainelement_name">
<p>My paragraph that must not be styled</p>
</div>

Your css:

#yourapplicationname_mainelement_name p {
display: block;
color: black;
background: white;
position: relative;
... and so on ...
}

As your css style rules are the most specific, given your id, they will override any settings present on the page where your html is injected.

Further... It might be hard to see what rules are the most important. You can use firebug or similar to understand which is overriding another. You'll have a hard time without it when developing your application.

Is there a way to sandbox an HTML block away from its page's CSS without using iframes?

Nope, this is (sadly) not possible without an iframe.

You would have to reset every existing CSS rule for that div like so:

div.sandbox
{
font-size: ....
font-family: ..........
margin: .........
padding: .........
line-height: .........
}

while difficult and never 100% reliable, it might be possible to achieve a usable result this way. You could look at one of the "reset stylesheets" like Eric Meyer's to get a list of important properties to reset; here is what claims to be a complete list of CSS 2.1 properties - excluding CSS 3 and vendor specific ones, which you would have to take into consideration as well.

Providers of 3rd party widgets often hard-code their "reset CSS" as inline CSS inside the HTML element to override any !important rules that threaten to override the sandbox class's rules.

How not to inherit styles in a chrome extension content script

I would go with the first choice--to fully specify the style of the elements you use. But this is a bit more involved than I thought.

First, you have to completely specify the container element. Then, for its descendants, you have to say that they should also use the default values or inherit from their parent (up to the container). Finally, you have to specify the look of every other element so that they're not all plain spans.

The relevant APIs are getComputedStyle and the CSSStyleSheet interface from DOM Level 2 Style. You can use all the values there except width and height, which should be auto by default. You also need to download a default stylesheet, such as the Webkit user agent stylesheet. Then you can call the following function to create a complete stylesheet that you can inject into the document.

Note that when you insert the stylesheet into the target document, you'll have to make the container selector as specific as possible because the webpage could conceivably give rules that have a higher specificity than your rules. For example, in <html id=a><head id=b><style>#a #b * {weird overrides}</style></head>, #a #b * has a higher specificity than #yourId div would. But I imagine that this is uncommon.

Note: for some reason, Chrome is giving me error "Failed to load resource" when I load the CSS, unless it is already in a <link> of the current document. So you should include html.css in the page that calls this function too.

// CSS 2.1 inherited prpoerties
var inheritedProperties = [
'azimuth', 'border-collapse', 'border-spacing', 'caption-side',
'color', 'cursor', 'direction', 'elevation', 'empty-cells',
'font-family', 'font-size', 'font-style', 'font-variant',
'font-weight', 'font', 'letter-spacing', 'line-height',
'list-style-image', 'list-style-position', 'list-style-type',
'list-style', 'orphans', 'pitch-range', 'pitch', 'quotes',
'richness', 'speak-header', 'speak-numeral', 'speak-punctuation',
'speak', 'speech-rate', 'stress', 'text-align', 'text-indent',
'text-transform', 'visibility', 'voice-family', 'volume',
'white-space', 'widows', 'word-spacing'];
// CSS Text Level 3 properties that inherit http://www.w3.org/TR/css3-text/
inheritedProperties.push(
'hanging-punctuation', 'line-break', 'punctuation-trim',
'text-align-last', 'text-autospace', 'text-decoration-skip',
'text-emphasis', 'text-emphasis-color', 'text-emphasis-position',
'text-emphasis-style', 'text-justify', 'text-outline',
'text-shadow', 'text-underline-position', 'text-wrap',
'white-space-collapsing', 'word-break', 'word-wrap');
/**
* Example usage:
var fullStylesheet = completeStylesheet('#container', 'html.css').map(
function(ruleInfo) {
return ruleInfo.selectorText + ' {' + ruleInfo.cssText + '}';
}).join('\n');
* @param {string} containerSelector The most specific selector you can think
* of for the container element; e.g. #container. It had better be more
* specific than any other selector that might affect the elements inside.
* @param {string=} defaultStylesheetLocation If specified, the location of the
* default stylesheet. Note that this script must be able to access that
* locatoin under same-origin policy.
* @return {Array.<{selectorText: string, cssText: string}>} rules
*/
var completeStylesheet = function(containerSelector,
defaultStylesheetLocation) {
var rules = [];
var iframe = document.createElement('iframe');
iframe.style.display = 'none';
document.body.appendChild(iframe); // initializes contentDocument
try {
var span = iframe.contentDocument.createElement('span');
iframe.contentDocument.body.appendChild(span);
/** @type {CSSStyleDeclaration} */
var basicStyle = iframe.contentDocument.defaultView.getComputedStyle(span);
var allPropertyValues = {};
Array.prototype.forEach.call(basicStyle, function(property) {
allPropertyValues[property] = basicStyle[property];
});
// Properties whose used value differs from computed value, and that
// don't have a default value of 0, should stay at 'auto'.
allPropertyValues['width'] = allPropertyValues['height'] = 'auto';
var declarations = [];
for (var property in allPropertyValues) {
var declaration = property + ': ' + allPropertyValues[property] + ';';
declarations.push(declaration);
}
// Initial values of all properties for the container element and
// its descendants
rules.push({selectorText: containerSelector + ', ' +
containerSelector + ' *',
cssText: declarations.join(' ')});

// For descendants, some of the properties should inherit instead
// (mostly dealing with text).
rules.push({selectorText: containerSelector + ' *',
cssText: inheritedProperties.map(
function(property) {
return property + ': inherit;'
}).join(' ')});

if (defaultStylesheetLocation) {
var link = iframe.contentDocument.createElement('link');
link.rel = 'stylesheet';
link.href = defaultStylesheetLocation;
iframe.contentDocument.head.appendChild(link);
/** @type {CSSStyleSheet} */
var sheet = link.sheet;
Array.prototype.forEach.call(
sheet.cssRules,
/** @param {CSSStyleRule} cssRule */
function(cssRule) {
rules.push({
selectorText: containerSelector + ' ' + cssRule.selectorText,
cssText: cssRule.style.cssText});
});
}
return rules;
} finally {
document.body.removeChild(iframe);
}
};

Is it okay to insert an iframe into the page's content?

As you've discovered, trying to sandbox your CSS from interference, when injecting into the DOM is very difficult without declaratively setting every known style attribute. It's a well documented problem and some solutions can be found in the following posts:

  • How do I prevent CSS interference in an injected piece of HTML?

  • Is there a way to "sandbox" an HTML block away from its page's CSS without using iframes?

There's nothing fundamentally wrong with dropping your UI it into an <iframe>. I'm not aware of any settings or extension that would block this behaviour.

If your framed UI needs to interact with the DOM on the parent page or a content script you can do so with the use of Window.Parent. Also you may need to consider Same-Origin Policy if your pulling in the UI from another domain.

How to avoid CSS conflicts in ReactJs

There are two different ways of doing this, by using:

  • CSS modules
  • CSS in JS

This short article compares the two. I prefer CSS modules.

How to use CSS modules?

Setup

  1. Let's say your CSS file is named styles.css to make it a module you will need to change it to styles.module.css
  2. Import the CSS file like so import styles from './styles.module.css' (you can use any other name instead of styles)

Naming

<div className={styles.box_one}>
some content
</div>

Important: make sure the class in your CSS file is named .box_one and not .box-one

My personal preference

I would import like so import s from './styles.module.css' using s instead of styles just to make the code cleaner.

My JSX would look like below

<div className={s.box_one}>
some content
</div>


Related Topics



Leave a reply



Submit