-
-
Save aslushnikov/22f85e890d818957896c to your computer and use it in GitHub Desktop.
CSS subsystem
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # CSS Layer | |
| ## Basic concepts | |
| 1. CSSOM changes are not supported ATM. | |
| 2. `CSSModel.StyleSheetChanged` event happens only in case of DevTools successful changes/UndoEvents and has the following information: | |
| - `styleSheetId` | |
| - `editedRange` | |
| - `newText` | |
| 3. `CSSRule` / `CSSStyleDeclaration` / `CSSMedia` objects could be acquired as a result of `getMatchedStyles`, `getComputedStyle`, `getInlineStyle` or `getMediaQueries`. There is no other way to get these objects. | |
| 4. The `CSSMedia` objects are deduplicated. (why not dedup CSSRule?) | |
| 4. All `CSSRule` / `CSSStyleDeclaration` / `CSSMedia` / `CSSProperty` extend `CSSTextBasedModel`. | |
| ```javascript | |
| CSSTextBasedModel = function(styleSheetId, range) { | |
| this.styleSheetId = styleSheetId; | |
| this.range = range; | |
| } | |
| CSSTextBasedModel.Events = { | |
| RangeUpdated | |
| } | |
| CSSTextBasedModel.prototype = { | |
| /** | |
| * @return {boolean} | |
| */ | |
| rebaseAfterStyleSheetEdit: function(styleSheetId, oldRange, newRange) | |
| { | |
| if (NOT_APPLICABLE) | |
| return true; | |
| var result = TRY_TO_REBASE; | |
| if (result) | |
| FIRE(RangeUpdated); | |
| return result; | |
| } | |
| } | |
| ``` | |
| There is also abstract `CSSTextBasedModelCollection`: | |
| - listens `cssModel.StyleSheetChanged` and updates all its models | |
| - whenever at least one model fails to update, `FIRE` "detached" event | |
| - implements `iterable` for the sake of extending classes | |
| ```javascript | |
| /** | |
| * @param {!Array.<!CSSTextBasedModel>} models | |
| */ | |
| CSSTextBasedModelCollection = function(cssModel) | |
| { | |
| LISTEN(cssModel.StyleSheetChanged, this._onChange); | |
| } | |
| CSSTextBasedModelCollection.Events = { | |
| Detached | |
| } | |
| CSSTextBasedModelCollection.prototype = { | |
| _onChanged: function(styleSheetId, oldRange, newText) { | |
| var newRange = GET_RANGE(newText); | |
| var success = true; | |
| FOR_EACH_MODEL(mode) | |
| success = success && model.rebaseAfterStyleSheetEdit(styleSheetId, oldRange, newRange); | |
| if (!success) | |
| this.detach(); | |
| }, | |
| detach: function() { | |
| if (this.detached) | |
| return; | |
| this.detached = true; | |
| UNLISTEN(cssModel.StyleSheetChanged); | |
| FIRE(Detached); | |
| } | |
| } | |
| ``` | |
| ## Getting node styles | |
| ```javascript | |
| // CSSModel maintains weakmap <node, NodeStyle>. | |
| var nodeStyles = cssModel.stylesForNode(node); | |
| // Fetch matched styles. Computed/inline are similar. | |
| nodeStyles.getMatchedStyles().then(...) | |
| // Release nodeStyles when you no longer need them. | |
| nodeStyles.release(); | |
| ``` | |
| The `getMatchedStyles` returns a `Promise<CSSMatchedStyles>`, which: | |
| - extends `CSSTextBasedModelCollection` | |
| - correctly implements `iterable` | |
| ```javascript | |
| /** | |
| * @extends {CSSTextBasedModelCollection} | |
| */ | |
| CSSMatchedStyles = function(payload) { | |
| // Super constructor. | |
| CSSTextBasedModelCollection.call(this); | |
| this.initialize(payload); | |
| // Implement iteration across CSSRules to support rules rebaseline. | |
| this[Symbol.iterable] = ...; | |
| } | |
| ``` | |
| The `NodeStyles` is a **refcounted** object | |
| - It has "retain" and "release" methods (+1 / -1 to refcount property) | |
| - whenever the refcount drops to zero, all caches are resetted. | |
| ```javascript | |
| NodeStyles.prototype = { | |
| retain: function() { | |
| ++this._refcount; | |
| }, | |
| release: function() { | |
| --this._refcount; | |
| if (!this._refcount) | |
| this._resetMatchedCache(); | |
| }, | |
| getMatchedStyle: function() { | |
| // if we have an outdated value - clear caches. | |
| if (this._matchedStyle && this._matchedStyle.detached()) | |
| this._resetMatchedCache(); | |
| // Request a new value if we haven't yet. | |
| if (!this._cachedMatchedStylePromise) | |
| this._cachedMatchedStylePromise = FETCH_MATCHED_STYLE(); | |
| return this._cachedMatchedStylePromise; | |
| }, | |
| _resetMatchedCache: function() { | |
| if (this._matchedStyle) | |
| this._matchedStyle.detach(); | |
| delete this._matchedStyle; | |
| delete this._cachedMatchedStylePromise; | |
| } | |
| } | |
| ``` | |
| ## Getting all medias | |
| ```javascript | |
| // cssModel might cache medias if there's a need to. | |
| cssModel.getAllMedias().then(...) | |
| ``` | |
| The `getAllMedias` promise resolves to an array of `CSSMedia` objects - `Promise<CSSTextBasedModelList>` | |
| ```javascript | |
| /** | |
| * @extends {CSSTextBasedModelCollection} | |
| */ | |
| CSSTextBasedModelList = function(models) { | |
| // Super constructor. | |
| CSSTextBasedModelCollection.call(this); | |
| this._models = models; | |
| // Implement iteration across models to support rules rebaseline. | |
| this[Symbol.iterable] = ...; | |
| } | |
| ``` |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment