Closed Bug 438278 Opened 16 years ago Closed 6 years ago

Implement getMatchedCSSRules

Categories

(Core :: DOM: Core & HTML, enhancement)

enhancement
Not set
normal

Tracking

()

RESOLVED WONTFIX

People

(Reporter: bedney, Unassigned)

References

Details

(Whiteboard: [firebug-p3])

Attachments

(1 file, 1 obsolete file)

User-Agent:       Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_3; en-us) AppleWebKit/525.18 (KHTML, like Gecko) Version/3.1.1 Safari/525.20
Build Identifier: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9) Gecko/2008053008 Firefox/3.0

Webkit has a very nice enhancement to its DOM - getMatchedCSSRules(), which allows JavaScript-based retrieval of all CSS rules which are currently being applied to an element.

It is documented here: http://beta.devworld.apple.com/documentation/Cocoa/Reference/WebKit/Classes/DOMDocument_WebKitAdditions/DOMDocument_WebKitAdditions.pdf

Obviously the DOM Inspector, when installed, has an API for this functionality, but it'd be great to access it without requiring XPCOM privileges.

Thanks in advance.

Cheers,

- Bill

Reproducible: Always

Steps to Reproduce:
1.
2.
3.
Severity: normal → enhancement
Component: General → DOM: Mozilla Extensions
Product: Firefox → Core
QA Contact: general → general
Natch -

getComputedStyle() hands you back a single CSSStyleDeclaration programmed with the final cascaded, computed style for that element.

This method is different in that it hands you back a list of all of the rules currently applying to the element. It is very useful when introspecting on an Element to find out all of the rules in all of the various stylesheets that are applying to the Element.

Cheers,

- Bill 
David, would Mozilla take this extension?
David -

Should you decide to take this (:-) ), here's an example snippet of code from both Gecko and Webkit that show how I as a JavaScripter interact with both APIs (note here that I repackage the results of both calls into an Array - 'anElement' is the element in question):

Gecko way:

queryObj = Components.classes[
                        '@mozilla.org/inspector/dom-utils;1'].getService(
                                Components.interfaces.nsIDOMUtils);

            rules = queryObj.getCSSStyleRules(anElement);

            //  Repackage this into an array for convenience.
            ruleArray = [];
            for (i = 0; i < rules.Count(); i++)
            {
                ruleArray.push(rules.GetElementAt(i));
            };

Webkit way:

        //  Note here how we *must* supply the empty string for the 'pseudo
        //  element'
        //  (elemWin here is the Window object of the element in question).
        rules = elemWin.getMatchedCSSRules(anElement, '');

        //  Repackage this into an array for convenience.
        ruleArray = [];
        for (i = 0; i < rules.length; i++)
        {
            ruleArray.push(rules[i]);
        };

The Gecko way has 2 very distinct disadvantages:

1. The DOM Inspector extension must be installed
2. XPCOM privileges must be granted.


Thanks for looking into this!

Cheers,

- Bill
This sounds good to me.

I would expect a Web-accessible way only to give the rules that are in author style sheets, and not give pages access to user and UA style sheets like Inspector's DOMUtils's getCSSStyleRules does?  Is that what WebKit's implementation does?
Webkit has 2 versions of the function, one of them has 3 arguments, the last one being a bool specifying whether or not to return authorOnly rules. Is that fine or is there a problem in handing back ua-styles (i.e. should we force non-chrome callers to use the authorOnly mode)?

Here's the idl definitions:  http://developer.apple.com/documentation/AppleApplications/Reference/WebKitDOMRef/Document_idl/Classes/Document/CompositePage.html#//apple_ref/IDL/instm/Document/getMatchedCSSRules/CSSRuleList/(inElement,inDOMString)
Status: UNCONFIRMED → NEW
Ever confirmed: true
We'd need to give non-chrome callers only author rules.  (We could potentially consider doing otherwise if we had a way to hand back the user and UA rules in a way that the page couldn't change them; however, I'm still not sure whether I'd be ok with the information leak.)
David -

Not sure I can make a strong case for accessibility to UA rules, given that 'breakage' of them is breakage of the UA itself.

However, I am building a JS tool that would allow page designers to see and manipulate rules on-the-fly. It'd be really great to have access to all rules, such that user rules could at least be displayed so that page authors know why, even thought they're editing a rule, it is not taking effect in the intended way.

In this mode, user rules wouldn't be editable or at least changes to them wouldn't persist.

Thoughts?

Thanks again!

Cheers,

- Bill
Tentatively taking but it will probably take me a while to come up with something, so feel free to steal it from me.
Assignee: nobody → highmind63
You probably want to do something roughly like:
 * add a new method to nsIDOMNSDocument (in the idl, and bump the IID)
 * add a simple implementation of nsIDOMCSSRuleList to hold a given list of
   rules (rather than our current implementations that proxy to some other object)
 * implement it in nsDocument.cpp by:
    + calling nsInspectorCSSUtils::GetStyleContextForContent, which gives you
      a style context for the content node
    + walking up the chain of rule nodes from that style context's rule node,
      and using the rule for those that are author-level and non-!important,
      and putting those in the resulting rule list in the appropriate order
@Bill: Does Webkit have any docs where I can read up some specs?

@David: Should the function have a moz prefix (ie mozGetMatchedCSSRules)?
Natch -

I haven't found any. Sorta stumbled upon the fact that Webkit could do this when I saw it and played with it a bit. I got excited about it and filed the bug here.

I did dig around in the Webkit source a bit (gosh, its been a long time since I did C...) and I found that this call on their 'DOMWindow' turns around and calls their 'CSSStyleSelector::styleRulesForElement' call. I'm sure you could poke around in there and see what they're up to:

http://trac.webkit.org/browser/trunk/WebCore/css/CSSStyleSelector.cpp

Not to speak for David, but I wouldn't assume that it would be 'mozGetMatchedCSSRules'. Those prefixes are normally reserved for the DOM equivalent of 'Mozilla only' CSS style properties when accessing through a '.style' object, like '<elemRef>.style.MozUserSelect = "text"' or some such. David, feel free to correct me here.

Thanks so much for taking on this bug, Natch!

Cheers,

- Bill
Note that if you implement per comment 10 you better make the rules readonly somehow.  If you want to allow them to be writable, you need to force eager cloning of the stylesheet inners when you create that array of rules.  Then trigger a style reresolve.  Then rebuild your array of rules.  Or something.  You might want to talk to John J Barton about how firebug handles that....
So, the thing that has been holding me up a bit here is that I can't seem to find a way to get the rule out of the nsStyleContext as an nsIDOMCSSRule. Basically, I'm not very familiar with the mechanics of nsStyleContext, so all I get is the nsRuleNode out of it, but I'm pretty much stuck there. Am I suppose to fill in the nsICSSRule myself from the data provided by the nsStyleContext?

Note: nsInspectorCSSUtils was removed so I'm using the equivalent function from nsComputedDOMStyle.
Whiteboard: [firebug-p3]
Bug 536379 will implement the cloning thing you need here, at least for document stylesheets.
Depends on: 536379
Natch, are you still working on this?
(In reply to comment #11)
> @David: Should the function have a moz prefix (ie mozGetMatchedCSSRules)?

Given that WebKit is shipping it without a prefix, no.  (But we should test that our implementation is doing basically the same thing.)
(In reply to comment #17)
> Natch, are you still working on this?

I started a while back, but have been very busy with personal stuff since. Unfortunately I won't be able to actively work on this in the immediate future, I'm not sure when I'll be able to finish this. I'll unassign for now, if in the future I get back around to it, I'll reassign.
Assignee: highmind63 → nobody
Natch -

Thanks for trying - I appreciate it.

Anyone else interested? I could *really* use this.

Thanks in advance!

Cheers,

- Bill
Did you have work-in-progress that you could attach here so somebody else could continue?
(In reply to comment #4)

> The Gecko way has 2 very distinct disadvantages:
> 
> 1. The DOM Inspector extension must be installed

This part of the reason to implement is no longer correct. Quite a long time ago this began to work:
   domUtils = CCSV("@mozilla.org/inspector/dom-utils;1", "inIDOMUtils");

> 2. XPCOM privileges must be granted.

The use case in comment #8 it for a development tool. An extension can use the existing facility without privilege issues. 



> 
> 
> Thanks for looking into this!
> 
> Cheers,
> 
> - Bill
John -

Glad to see that DOM Inspector must no longer be installed, but that's the lesser of the 2 problems here for me.

I'm building a completely cloud-based IDE wherein I do not ask the user for any special privilege whatsoever. Right now, I can accomplish this on Webkit-based browsers without asking for any special permissions by using getMatchedCSSRules(). Without this call on Mozilla, I cannot accomplish the same end effect without packaging this as an extension, which I have no intention of doing as this is intended to be a 'cross-browser' solution.

Adding this call would bring Mozilla up to parity with Safari and Chrome and allow me to recommend Mozilla-based browsers with the same level of enthusiasm.

Cheers,

- Bill
Attached patch patch wip (obsolete) — Splinter Review
Ok, here's the patch I was working on (sorry about the lateness, haven't had any time to clean it up). It currently fails with a security failure (which I'm guessing might be coming from the |.get()| call at the end, I think it calls QI which creates the wrapper, could be totally wrong).

Left to do:

1) Handle mutations to the styles.
2) Handle UA style sheets.
3) Probably some code cleanup.
4) TESTS!!

That doesn't sound like a lot, but it probably is...

Good luck to whomever takes this up next.
Attached patch patch wipSplinter Review
Sorry for the spam, forgot to hg add the new files...
Attachment #427250 - Attachment is obsolete: true
The security error is due to lack of classinfo on nsDOMCSSRuleList.

The links in comment 6 and comment 0 are both dead, so I can't tell whether this is compatible with what Webkit implements (e.g. whether they return live or non-live lists, what item 1 from comment 24 means, whether the handling of !important or rules that match a node more than once is the same, etc).
(In reply to comment #26)
> The security error is due to lack of classinfo on nsDOMCSSRuleList.

Indeed! Completely forgot about the classinfo stuff (although I swear this worked without a security error a couple of months ago). In any event, I won't be able to do much with this for the foreseeable future...
I can probably pick this up and drive it in if someone can point me to docs on what webkit actually implements.
Boris -

Thanks for looking at this. Sorry that the docs are so thin (uhh... nonexistent... ;-) ) here. The only thing I can tell from the code link above is that it looks like, on Webkit, only access to the author rules are provided to the JS API (which I was unaware of when I asked for this feature). Still, the API has a lot of usefulness to me.

Therefore I'd be willing to post a set of questions to webkit-dev mailing list and gather the data for you from the fine folks over there if you would care to post those questions here.

Thanks again!
Hmm.  So looking through their current code, what's there is pretty different from that first landing....

They expose the method on Window, looks like, not Document (though they seem to have idl for both).  Their code will happily return null if there are no matching rules instead of returning an empty list; I don't think we should duplicate that.

I checked what they do if you try to get the non-author rules and then modify them.  Safari crashes very nicely if you do that.  Filed https://bugs.webkit.org/show_bug.cgi?id=35014

The list they return is non-live.

The rules in their list are sorted in least to most important order, ignoring !important issues (since those are on the property level, not the rule level).

Looks like if a rule matches a node more than once it'll be in the list more than once, with the locations determined by the selectors that matched.  That's the easy thing for us to do as well.

So it sounds to me like we should disallow access to the non-author rules from untrusted script, change things so that we're not actually resolving a style context but are just getting a list of rules (should be _much_ faster in display:none subtrees), and test carefully....
Oh, and probably need security checks on rules that are not same-origin with the caller.  It doesn't look like Webkit has those, and they're a must.
Excellent!

Let me know if there is anything I can do (besides cheering from the sidelines :-) ).

Thank you!
You got it!

Coming up.
Would be cool if this is brought up on www-style as well for non-Gecko/WebKit-based browsers, especially since the plan is to expose it to Web content.
I'm not planning to work on this (at least for web script) pending the outcome of the discussion in https://bugs.webkit.org/show_bug.cgi?id=35015 (where the latest comments have been suggestions to remove this feature from Webkit).
Boris -

I've been totally remiss in getting the tests done for this - so much other stuff got in the way. Sorry.

Thanks for the heads up re: the status of this feature in Webkit. It sure would be a shame if this feature went away in Webkit. Unfortunately, my login there doesn't give me access to the bug you mentioned - I assume there's a security problem. This effectively mutes me from stating my case as to the usefulness of this call. I guess I just don't see a security issue here, especially if either no access or read-only access were given to user-agent rules.

If you could please keep this bug up-to-date with the latest goings-on over there, I sure would appreciate it. Should that issue work itself out, ping this bug and I get to working on these tests - I promise :-).

Cheers,

- Bill
> I assume there's a security problem.

Yes.

> If you could please keep this bug up-to-date with the latest goings-on over
> there

Will do.
(In reply to Boris Zbarsky (:bz) from comment #40)
> > I assume there's a security problem.
> 
> Yes.
> 
> > If you could please keep this bug up-to-date with the latest goings-on over
> > there
> 
> Will do.

I'm interested as well :)
> I'm interested as well :)

I just cced you on the webkit bug.
I've written a short Gist that polyfills this for Gecko:

https://gist.github.com/3033012

HTH (:
For some values of "polyfills", yes.  ;)  (Media handling is buggy, ordering looks buggy, no support for @import, @media, etc.)
It's just a POC implementation.
Anyhow:

* I don't see how media handling is buggy. Could you be more specific?
* Ordering? I'm not familiar with that aspect, what does it mean in this context?
* @import, @media: Yes, no support for now (just a POC), but if there's an interest it can be easily added.
> * I don't see how media handling is buggy. Could you be more specific?

It's just fundamentally broken in a world where browsers implement http://www.w3.org/TR/css3-mediaqueries/ (which they do).  Consider these two stylesheets:

   <style media="not print">
   <style media="all and (monochrome: 1)">

The former applies on screen but is not picked up by the script.  The latter only applies on devices which are monochrome and use one bit per pixel (so only two colors available for display; no grayscale).  Which means it basically never applies.  But it will be picked up by the script.

> * Ordering? I'm not familiar with that aspect, what does it mean in this context?

The use cases I know of for this want the rules in cascade order.
Cool, thanks!
I've implemented proper media matching and support for @import/@media rules.
Ordering according to cascade order is TBD.
This is basically there:
https://gist.github.com/3033012

Cheers,
~Y
Component: DOM: Mozilla Extensions → DOM
This bug was mentioned in a blink-dev thread to deprecate getMatchedCSSRules():
https://groups.google.com/a/chromium.org/d/msg/blink-dev/fd-QLCiLESQ/uqfzCsLja-MJ

Gecko developers, are you interested in standardizing and implementing this, or would you rather we try to remove it from Blink?
I think I'm somewhat interested, subject to the constraints discussed earlier in this bug (not exposing cross-origin rules, not exposing UA/user rules, etc).

Note, though, that the extension use cases that have actually asked for this do NOT want these restrictions.  What are the non-extension (web page) use cases?
I honestly don't know what the use cases for getMatchedCSSRules() on the general Web and can't tell from http://trac.webkit.org/changeset/11233 what the original use cases were.

I'm also not looking for reasons to keep it, as removing it seems like the faster way to reach interop...
Unless I'm missing another way of doing it, getMatchedCSSRules can allow Javascript to access style information for an element that is not currently displayed in the DOM. This can allow the developer to avoid repeating constants in CSS (SASS) and Javascript.

For example, here's a vertical progress bar:

    +--+
    |  |
    |  |
    +--+
    |##|
    |##|
    +--+

It's made up of two divs: the outer bar, and the inner fill. We construct it with the following function:

    function createVerticalProgressBar(fraction) {
        var bar = document.createElement("div");
        bar.className = "progress-vertbar";
        var fill = document.createElement("div");
        bar.appendChild(fill);
        fill.className = "progress-fill";
        var heightOfBar = // MAGIC!
        fill.style.height = Math.round(heightOfBar * fraction) + "px";
        return bar;
    }

The SASS file might look like this:

    $vertbar-height: 80px;
    $vertbar-width:  20px;
    
    .progress-vertbar {
    
        height: $vertbar-height;
        width: $vertbar-width;
        position: relative;
        // colors, etc. here
    
        .progress-fill {
            // height set by createVerticalProgressBar
            width: $vertbar-width;
            position: absolute;
            bottom: 0;
        }
    }

    .progress-fill {
        // colors, etc. here
    }

It seems like getMatchedCSSRules allows the implementation of the "MAGIC". Does the current API support a (better?) way?
I don't understand, if you have a progress-bar wouldn't you simply set the height to something between 0 and 100% depending on the current progress? Can you fill in the "MAGIC" bit using getMatchedCSSRules to clarify?
I can give the use-case that led me to use it and implement the Gecko polyfill. I was working on an app for creating emails via a WYSIWYG editor. Since styles needed to be inlined for the markup generation process I needed to get the matched rules from all existing style sheets and combine with anything set via JS directly on elements.
It was important I get the original values from the matched rules and not computed ones since they were generic and allowed reuse of the markup in other user agents.
Does the polyfill work just as well as WebKit and Blink's getMatchedCSSRules()? Would it make any difference to you if getMatchedCSSRules() were removed from Blink?
The project died in the middle, so the implementation might not be complete or buggy in edge cases, but I think it's basically functionally similar. Should Blink remove getMatchedCSSRules() this polyfill could also work there. I also say someone forked my gist and implemented support for Webkit and IE.
(In reply to Philip Jägenstedt from comment #53)
> I don't understand, if you have a progress-bar wouldn't you simply set the
> height to something between 0 and 100% depending on the current progress?
> Can you fill in the "MAGIC" bit using getMatchedCSSRules to clarify?

Good point; thanks for the suggestion. For some reason, I thought percent heights didn't play well with absolute positioning, but it looks fine to me.

This is not really a good example, then, of where getMatchedCSSRules overcomes a limitation. I could certainly come up with a hypothetical example that couldn't be overcome with percent styling (e.g., calculating a width based on a height). I admit that would be reaching, though, so I have no problems with removal.

For the record, this is what I had in mind for MAGIC:

    function styleOf(node, prop) {
        var styles = getMatchedCSSRules(node);
        var style = "";
        [].forEach.call(styles, function(s) {
            if (s.style[prop]) {
                // TODO: Handle !important
                style = s.style[prop];
            }
        });
        return style;
    }
    /* Then replace `var heightOfBar = // MAGIC!` with: */
    var heightOfBar = parseFloat(styleOf(bar, "height"));
Thanks Brandon. Let me know if you come across some existing code using getMatchedCSSRules() which can't easily be replaced by a polyfill or something even simpler as in the progress bar case.
Hi guys, I’m developing a polyfill for a CSS property called `object-fit` (https://github.com/anselmh/object-fit) which needs to parse through the CSS and makes use of `getMatchedCSSRules`. 

Unfortunately the polyfill for getMatchedCSSRules mentioned here is super-slow and is likely to crash any browser when parsing a bit larger CSS file.

Therefore I’d say it would make a difference if WebKit / Blink deprecate this and it would certainly help if Firefox would implement this.
(In reply to info from comment #59)
> Hi guys, I’m developing a polyfill for a CSS property called `object-fit`
> (https://github.com/anselmh/object-fit) which needs to parse through the CSS
> and makes use of `getMatchedCSSRules`.
> ...
> Therefore I’d say it would make a difference if WebKit / Blink deprecate
> this and it would certainly help if Firefox would implement this.

Implement what? 'getMatchedCSSRules' or 'object-fit'? Note that 'object-fit' is available in Firefox 36.[1][2]

[1] https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit
[2] Bug 1099450
(In reply to Sebastian Zartner [:sebo] from comment #60)
> (In reply to info from comment #59)

> Implement what? 'getMatchedCSSRules' or 'object-fit'? Note that 'object-fit'
> is available in Firefox 36.[1][2]
> 
> [1] https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit
> [2] Bug 1099450

Nah, I meant implementing 'getMatchedCSSRules' would be helpful for any CSS polyfills that need to parse correlated rules. I’m aware of the implementation of object-fit in v36.
https://wiki.css-houdini.org/ has a completely different direction for enabling polyfills so I don't think we should let that influence us too much here.
(In reply to Anne (:annevk) from comment #62)
> https://wiki.css-houdini.org/ has a completely different direction for
> enabling polyfills so I don't think we should let that influence us too much
> here.

Yeah, I’m totally aware that Tab is doing something to make polyfilling CSS better. Still, today we don’t have another option than using the getMatchedRules for some specific use cases and as far as I can see. I also played around with the existing codebase from Remy and Tab but they’re relatively heavy and not yet production-ready.

But anyway, I just added a use-case as people asked for some in this thread.
Looking at that use case, the getMatchedCSSRules is used for getMatchedStyle, which is actually trying to just find the specified values of the styles on an element, right?

That seems like a use case for having a way to get the specified styles (which is in fact something people have asked for a lot), with getMatchedCSSRules being used as a hacky workaround, afaict.
(In reply to Boris Zbarsky [:bz] from comment #64)
> Looking at that use case, the getMatchedCSSRules is used for
> getMatchedStyle, which is actually trying to just find the specified values
> of the styles on an element, right?
> 
> That seems like a use case for having a way to get the specified styles
> (which is in fact something people have asked for a lot), with
> getMatchedCSSRules being used as a hacky workaround, afaict.

Yep, exactly. From that point of view any other tool that provides me these styles is possible, too. It’s just that there isn’t any at the moment.
Neither getMatchedCSSRules nor getCascadedStyle would provide values or properties that we don't implement, so I don't see how they'd help with polyfills (which I think should have a better API that is more designed for extending support).
(In reply to David Baron [:dbaron] (UTC+13) (vacation, returning March 2) from comment #67)
> Neither getMatchedCSSRules nor getCascadedStyle would provide values or
> properties that we don't implement, so I don't see how they'd help with
> polyfills (which I think should have a better API that is more designed for
> extending support).

Of course. But this is not what I’m talking about. I’ve shared a use case which *is* a polyfill and *makes use of* getMatchedCSSRules to read them out and re-apply them to another element. This follows the principle of a polyfill: To emulate the native behavior of a not yet implemented though standardized functionality.
Chrome removed this since version 64, so this bug should be closed as WONTFIX
https://www.chromestatus.com/feature/4606972603138048
Good idea, especially since this hasn't been standardized in the ten years that passed since OP either.
Status: NEW → RESOLVED
Closed: 6 years ago
Resolution: --- → WONTFIX
Component: DOM → DOM: Core & HTML
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: