Skip to content

Instantly share code, notes, and snippets.

@aslushnikov
Last active August 29, 2015 14:22
Show Gist options
  • Select an option

  • Save aslushnikov/22f85e890d818957896c to your computer and use it in GitHub Desktop.

Select an option

Save aslushnikov/22f85e890d818957896c to your computer and use it in GitHub Desktop.
CSS subsystem
# 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