Skip to content

Commit

Permalink
fix: add various fixes to make IE & ShadyCSS working
Browse files Browse the repository at this point in the history
  • Loading branch information
Lodin committed Aug 31, 2019
1 parent 3c31a2b commit 1a462f8
Show file tree
Hide file tree
Showing 7 changed files with 119 additions and 70 deletions.
119 changes: 74 additions & 45 deletions adoptedStyleSheets.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
var polyfillLoaded = false;

// Polyfill-level reference to the iframe body
var frameBody, frameWindow;
var frameBody, frameCSSStyleSheet;

// Style elements that will be attached to the head
// that need to be moved to the iframe
Expand All @@ -28,11 +28,11 @@
document.body.appendChild(iframe);

frameBody = iframe.contentWindow.document.body;
frameWindow = iframe.contentWindow;
frameCSSStyleSheet = iframe.contentWindow.CSSStyleSheet;

// Since we get the sheet from iframe, we need to patch prototype of the
// CSSStyleSheet in iframe as well.
updatePrototype(frameWindow.CSSStyleSheet.prototype);
updatePrototype(iframe.contentWindow.CSSStyleSheet.prototype);

// Document body will be observed from the very start to catch all added
// custom elements
Expand Down Expand Up @@ -60,6 +60,13 @@

var OldCSSStyleSheet = CSSStyleSheet;

function instanceOfStyleSheet(instance) {
return (
instance instanceof OldCSSStyleSheet ||
instance instanceof frameCSSStyleSheet
);
}

var cssStyleSheetMethods = [
'addImport',
'addPageRule',
Expand Down Expand Up @@ -146,13 +153,7 @@
// Allows instanceof checks with the window.CSSStyleSheet.
Object.defineProperty(ConstructStyleSheet, Symbol.hasInstance, {
configurable: true,
value: function(instance) {
return (
instance instanceof OldCSSStyleSheet ||
// Workaround for IE & Edge
instance instanceof frameWindow.CSSStyleSheet
);
}
value: instanceOfStyleSheet
});

ConstructStyleSheet.prototype.replace = function replace(contents) {
Expand All @@ -168,9 +169,8 @@
updateAdopters(self);
} else {
reject(
new DOMException(
"Failed to execute 'replace' on 'CSSStyleSheet': Can't call replace on non-constructed CSSStyleSheets.",
'NotAllowedError'
new Error(
"Failed to execute 'replace' on 'CSSStyleSheet': Can't call replace on non-constructed CSSStyleSheets."
)
);
}
Expand All @@ -179,9 +179,8 @@

ConstructStyleSheet.prototype.replaceSync = function replaceSync(contents) {
if (importPattern.test(contents)) {
throw new DOMException(
'@import rules are not allowed when creating stylesheet synchronously',
'NotAllowedError'
throw new Error(
'@import rules are not allowed when creating stylesheet synchronously'
);
}

Expand All @@ -194,9 +193,8 @@

return basicStyleElement.sheet;
} else {
throw new DOMException(
"Failed to execute 'replaceSync' on 'CSSStyleSheet': Can't call replaceSync on non-constructed CSSStyleSheets.",
'NotAllowedError'
throw new Error(
"Failed to execute 'replaceSync' on 'CSSStyleSheet': Can't call replaceSync on non-constructed CSSStyleSheets."
);
}
};
Expand Down Expand Up @@ -343,6 +341,30 @@
observerTool.observe();
}

function checkAndPrepare(sheets, location) {
var locationType = location.tagName ? 'Document' : 'ShadowRoot';

if (!Array.isArray(sheets)) {
throw new TypeError(
"Failed to set the 'adoptedStyleSheets' property on " +
locationType +
': Iterator getter is not callable.'
);
}

if (!sheets.every(instanceOfStyleSheet)) {
throw new TypeError(
"Failed to set the 'adoptedStyleSheets' property on " +
locationType +
": Failed to convert value to 'CSSStyleSheet'"
);
}

return sheets.filter(function(value, index) {
return sheets.indexOf(value) === index;
});
}

var adoptedStyleSheetAccessors = {
configurable: true,
get: function() {
Expand All @@ -355,45 +377,52 @@
// If `this` is the Document, the body element should be used as a
// location.
var location = this.body ? this.body : this;
var locationType = location.tagName ? 'Document' : 'ShadowRoot';
if (!Array.isArray(sheets)) {
throw new TypeError(
"Failed to set the 'adoptedStyleSheets' property on " +
locationType +
': Iterator getter is not callable.'
);
}

if (
!sheets.every(function(sheet) {
return sheet instanceof OldCSSStyleSheet;
})
) {
throw new TypeError(
"Failed to set the 'adoptedStyleSheets' property on " +
locationType +
": Failed to convert value to 'CSSStyleSheet'"
);
}

var uniqueSheets = sheets.filter(function(value, index) {
return sheets.indexOf(value) === index;
});
var uniqueSheets = checkAndPrepare(sheets, location);

var oldSheets = adoptedStyleSheetsRegistry.get(location) || [];
adoptedStyleSheetsRegistry.set(location, uniqueSheets);

// If the browser supports web components, it definitely supports
// isConnected. If not, we can just check if the document contains
// the current location.
var isConnected =
'isConnected' in location
? location.isConnected
: document.contains(location);

// Element can adopt style sheets only when it is connected
if (location.isConnected) {
if (isConnected) {
adoptStyleSheets(location);
// Remove all the sheets the received array does not include.
removeExcludedStyleSheets(location, oldSheets);
}
}
};

// Double check to make sure the browser supports ShadowRoot
if (typeof ShadowRoot !== 'undefined') {
// If the ShadyDOM is defined, the polyfill is loaded. Then, let's rely on
// it; otherwise, we check the existence of the ShadowRoot.
if ('ShadyCSS' in window && !window.ShadyCSS.nativeShadow) {
Object.defineProperty(ShadowRoot.prototype, 'adoptedStyleSheets', {
get: function() {
return adoptedStyleSheetsRegistry.get(this) || [];
},
set: function(sheets) {
var uniqueSheets = checkAndPrepare(sheets, location);

var cssToAdopt = uniqueSheets.map(function(sheet) {
return constructStyleSheetRegistry.get(
sheet
).basicStyleElement.innerHTML;
});

ShadyCSS.ScopingShim.prepareAdoptedCssText(
cssToAdopt,
this.host.localName
);
}
});
} else if (typeof ShadowRoot !== 'undefined') {
var oldAttachShadow = HTMLElement.prototype.attachShadow;

// Shadow root of each element should be observed to add styles to all
Expand Down
2 changes: 1 addition & 1 deletion karma.conf.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ module.exports = function(config) {
usePhantomJS: false,
preferHeadless: true,
postDetection(availableBrowsers) {
return availableBrowsers.filter(browser => browser !== 'IE');
return availableBrowsers.filter(browser => browser !== 'IE' && browser !== 'Edge');
},
},

Expand Down
17 changes: 17 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"@babel/core": "^7.5.5",
"@babel/plugin-transform-instanceof": "^7.5.5",
"@babel/preset-env": "^7.5.5",
"@babel/runtime": "^7.5.5",
"@open-wc/testing-helpers": "^1.2.1",
"@webcomponents/webcomponentsjs": "^2.2.10",
"babel-loader": "^8.0.6",
Expand Down
26 changes: 19 additions & 7 deletions test/polyfill.test.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import './polyfills';

import {fixtureCleanup} from '@open-wc/testing-helpers/src/fixtureWrapper';
import {defineCE} from '@open-wc/testing-helpers/src/helpers';
import {stringFixture as fixture} from '@open-wc/testing-helpers/src/stringFixture';
import '../adoptedStyleSheets.js';

var isPolyfill = new CSSStyleSheet().constructor !== CSSStyleSheet;
var hasShadyCSS = 'ShadyCSS' in window;

function ignore() {
if (!isPolyfill) {
if (!isPolyfill || hasShadyCSS) {
pending();
}
}
Expand All @@ -19,6 +21,10 @@ describe('Constructible Style Sheets polyfill', function() {
sheet = new CSSStyleSheet();
});

afterEach(function () {
fixtureCleanup();
});

describe('CSSStyleSheet object', function() {
var globalStyle;

Expand Down Expand Up @@ -69,11 +75,9 @@ describe('Constructible Style Sheets polyfill', function() {
return globalStyle.sheet
.replace('.only-test { color: blue; }')
.catch(function(error) {
expect(error instanceof DOMException).toBeTruthy();
expect(error.message).toBe(
"Failed to execute 'replace' on 'CSSStyleSheet': Can't call replace on non-constructed CSSStyleSheets."
);
expect(error.name).toBe('NotAllowedError');
});
});
});
Expand Down Expand Up @@ -103,7 +107,6 @@ describe('Constructible Style Sheets polyfill', function() {
try {
sheet.replaceSync('@import "test.css"');
} catch (error) {
expect(error instanceof DOMException).toBeTruthy();
expect(error.message).toContain(
'@import rules are not allowed when creating stylesheet synchronously'
);
Expand All @@ -114,11 +117,9 @@ describe('Constructible Style Sheets polyfill', function() {
try {
globalStyle.sheet.replaceSync('.only-test { color: blue; }');
} catch (error) {
expect(error instanceof DOMException).toBeTruthy();
expect(error.message).toBe(
"Failed to execute 'replaceSync' on 'CSSStyleSheet': Can't call replaceSync on non-constructed CSSStyleSheets."
);
expect(error.name).toBe('NotAllowedError');
}
});
});
Expand All @@ -131,6 +132,9 @@ describe('Constructible Style Sheets polyfill', function() {
function createCustomElement(sheets, html) {
html = html || '';

var template = document.createElement('template');
template.innerHTML = html + '<div class="test"></div>';

function CustomElement() {
var self = Reflect.construct(HTMLElement, [], CustomElement);

Expand All @@ -140,7 +144,15 @@ describe('Constructible Style Sheets polyfill', function() {
root.adoptedStyleSheets = sheets;
}

root.innerHTML = html + '<div class="test"></div>';
if ('ShadyCSS' in window) {
ShadyCSS.prepareTemplateStyles(template, self.localName);
}

root.appendChild(template.content.cloneNode(true));

if ('ShadyCSS' in window) {
ShadyCSS.styleElement(self);
}

return self;
}
Expand Down
15 changes: 2 additions & 13 deletions test/polyfills.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,3 @@
if (!('isConnected' in Node.prototype)) {
require('is-connected-node/implement')
}

if (typeof WeakMap === 'undefined') {
window.WeakMap = require('core-js-pure/es/weak-map');
}

if (typeof Map === 'undefined') {
window.Map = require('core-js-pure/es/map');
}

if (typeof Promise === 'undefined') {
window.Promise = require('core-js-pure/es/promise');
}
Expand All @@ -23,7 +11,8 @@ if (typeof Reflect === 'undefined') {
}

if (typeof customElements === 'undefined') {
require('@webcomponents/webcomponentsjs');
require('@babel/runtime/regenerator');
require('@webcomponents/webcomponentsjs/src/entrypoints/webcomponents-bundle-index');
}

if (!('assign' in Object)) {
Expand Down
9 changes: 5 additions & 4 deletions webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ const paths = {
nodeModules: resolve(cwd, 'node_modules'),
openWc: resolve(cwd, 'node_modules/@open-wc'),
polyfills: resolve(cwd, 'test/polyfills.js'),
test: resolve(cwd, 'test/polyfill.test.js')
test: resolve(cwd, 'test/polyfill.test.js'),
wc: resolve(cwd, '../polyfills/packages/webcomponentsjs')
};

module.exports = {
Expand Down Expand Up @@ -47,11 +48,11 @@ module.exports = {
}
}
],
include: [paths.test, paths.openWc, paths.polyfills]
include: [paths.test, paths.openWc, paths.polyfills, paths.wc]
}
].filter(Boolean)
},
performance: {
hints: false,
},
hints: false
}
};

0 comments on commit 1a462f8

Please sign in to comment.