UNPKG

jsdom

Version:

A JavaScript implementation of many web standards

128 lines (106 loc) 3.76 kB
"use strict"; const StyleSheetImpl = require("./StyleSheet-impl.js").implementation; const CSSRuleList = require("../../../generated/idl/CSSRuleList.js"); const DOMException = require("../../../generated/idl/DOMException.js"); const CSSImportRule = require("../../../generated/idl/CSSImportRule.js"); const cssParser = require("./helpers/css-parser.js"); const { wrapperForImpl } = require("../../../generated/idl/utils.js"); class CSSStyleSheetImpl extends StyleSheetImpl { #constructed; #disallowModification; constructor(globalObject, args, privateData = {}) { super(globalObject, args, privateData); this.#constructed = "constructed" in privateData ? privateData.constructed : true; this.#disallowModification = false; this.ownerRule = privateData.ownerRule || null; this.cssRules = CSSRuleList.createImpl(globalObject); } // Legacy alias for cssRules get rules() { return this.cssRules; } insertRule(rule, index) { // TODO: check origin-clean flag this.#checkModificationAllowed(); const parsed = cssParser.parseRule(rule, this._globalObject); if (CSSImportRule.isImpl(parsed) && this.#constructed) { throw DOMException.create(this._globalObject, [ "Cannot insert @import rules into a constructed stylesheet.", "SyntaxError" ]); } return this.cssRules._insertParsed(parsed, index, null, this); } deleteRule(index) { // TODO: check origin-clean flag this.#checkModificationAllowed(); this.cssRules._remove(index); } // Legacy method addRule(selector, style, index) { const rule = `${selector} { ${style} }`; if (index === undefined) { index = this.cssRules._list.length; } this.insertRule(rule, index); return -1; } // Legacy method removeRule(index) { this.deleteRule(index); } replaceSync(text) { if (!this.#constructed) { throw DOMException.create(this._globalObject, [ "Cannot call replaceSync on non-constructed CSSStyleSheet.", "NotAllowedError" ]); } if (this.#disallowModification) { throw DOMException.create(this._globalObject, [ "The stylesheet is currently being modified.", "NotAllowedError" ]); } // Clear existing rules this.cssRules._clear(); // Parse and populate cssParser.parseIntoStyleSheet(text, this._globalObject, this); } replace(text) { if (!this.#constructed) { return Promise.reject(DOMException.create(this._globalObject, [ "Cannot call replace on non-constructed CSSStyleSheet.", "NotAllowedError" ])); } if (this.#disallowModification) { return Promise.reject(DOMException.create(this._globalObject, [ "The stylesheet is currently being modified.", "NotAllowedError" ])); } this.#disallowModification = true; // The spec says "in parallel" which allows off-main-thread parsing. We parse synchronously but // defer via a microtask so the disallow modification flag is observable between the call and the // promise settling. return new Promise(resolve => { queueMicrotask(() => { this.cssRules._clear(); cssParser.parseIntoStyleSheet(text, this._globalObject, this); this.#disallowModification = false; // webidl2js doesn't auto-wrap return values: https://github.com/jsdom/webidl2js/issues/257 resolve(wrapperForImpl(this)); }); }); } #checkModificationAllowed() { if (this.#disallowModification) { throw DOMException.create(this._globalObject, [ "The stylesheet is currently being modified.", "NotAllowedError" ]); } } } exports.implementation = CSSStyleSheetImpl;