Wysiwyg editing is hard #2, let's apply a larger font size
By glazou on Tuesday 31 August 2010, 12:35 - Mozilla - Permalink
Let's say the user wants to use a larger font size on the LAST paragraph in the following example:
<style>
#section p { font-size: larger ! important}
.warning { color: red; border: thin solid red }
</style>
<div id="section">
<p>This is NOT a warning</p>
<p class="warning">This is a warning</p>
</div>
We're in a Wysiwyg environment, the user got the document from an unkown source, places the caret in the paragraph or selects the whole paragraph, click on some UI thingy meaning "I want a larger font" and he/she expects a larger font size for the paragraph, whatever has the editing tool to do for that... The user has no knowledge of the underlying technology - in our case CSS - and he/she's the perfect target for Wysiwyg editors. Let's suppose the editing tool manipulates stylesheets and hates HTML attributes and inline styles, and that's also our goal.
First thought and first
problem, we cannot apply font-size:
larger to the paragraph since
it's already applied... But, wait, how do you know it's already
applied? Just looking at the computed value of font-size, you can't
since that will reply only an absolute value like 18px! So to determine
if and how you
can tweak a given property on a given element, you first need to browse
all the CSS rules applying to your element. In Gecko, you can use the inIDOMUtils
DOM Inspector interface and namely its getCSSStyleRules()
method. Then for each rule applying to the element, you need to check
if the property is applied to the element through that rule and if it
is, compute the specificity of the selector and preserve the rule, the
specificity of the rule and the priority (think !important) of the
declaration in an array. When that's done for all rules, sort the array
according to the CSS Cascade rules... Yep, that's right, you read it
well, you need to recascade yourself the whole thing because the CSS
Object Model has no getCascadedValue()
method for the time being. Oh and of course, nothing either to compute
the specificity of a given selector...
Once you have the cascaded the whole thing, you have the rule applying the current font-size to the element, its specificity and the priority of the declaration. Of course, there is a trivial case where no rule applies to the element for that property and your final data set is empty.
Then the editing tool has no choice, it must create a rule with a specificity greater than or equal to the specificity of the rule we found and caring about the priority of the declaration...
- If the data set above is
empty or if the rule's specificity is equal to or less than the
specificity of a class selector, AND IF the user says through some UI
dialog we can safely apply the change to all
<p class="warning">, then we can create a.warning { font-size: ...px }or.warning { font-size: ...px !important }rule at the end of our stylesheet where ... is a font-size value greater than the computed value of font-size on the element. Hey, yeah, you have to compute that value yourself again
We won't deal with the p.warningpossibility because it's just impossible to show an average user a dialog asking for permission to change the styles applied to all paragraph of class warning and not other elements... - Else if the rule's
specificity is equal to or less than the specificity of an #ID
selector, we can safely insert a
#myNewId { font-size: ...px }or#myNewId { font-size: ...px ! important }at the end of our stylesheet but please note the editing tool has to query the user for an ID or select a random - and unreadable - one by itself. Of course, the font-size computation issue detailed just above still applies here. - Else if the rule's
specificity is equal to or less than the specificity of an #ID selector
and a type element selector, we can safely insert a
p#myNewId { font-size: ...px }orp#myNewId { font-size: ...px ! important }at the end of our stylesheet. And the font-size computation and the ID UI query issues still apply here. - Else we have the most
complex case: we need to prompt the user for an ID or find a random one
and derive a rule from the selector of the rule we found above adding
an ID simple selector to the last chain of simple selectors in the
selector. In other terms in our case here, create
#sectionfromp#myNewId { font-size: larger ! important}#section p { font-size: larger ! important}.
Woof. Of course, the CSS Object
Model's minimalistic interfaces don't help a lot here and the editing
tool's author will end up writing a lot of code that is already
implemented in the rendering engine, but not exposed. Welcome to my
world 

Comments
CSS sucks. How MS Word does it?
MS Word sucks too. As far as I know, it uses XML, so it's basically the same thing.
@manuell and Tiago: No, I cannot agree with you here; given the complexity of application of CSS styles to a given element, I think MS Word does an _excellent_ and even _remarkable_ job at it. It hides entirely the complexity of the underlyinh technos and zillions of people are able to use styles in Word. Granted, it's not perfect, and MS Word certainly limits itself to a given profile of selectors (more probably only classes and IDs) but sill, it's amazing that such a mass market software is xml+css based...
Try to rethink your approach and I belief it will make the problem disappear.
Example:
#1 Client edits a corporate site and he wants to make last paragraph "larger". Maybe he even want to apply the style "Corporate Policy" which is in fact "font-size: larger; border: 2px solid silver;".
#2 Client edits his hobby site and he wants to make the last paragraph crazier and standing out: "font-size: larger; color: pink; text-transform: uppercase;"
In the ideal Wor(l)d of WYSIWYG editors the first client should have a toolbar control saying "Apply Corporate Policy Style" and a second client using different design should have completely different set of style controls and one of them should be "Apply My Crazy Style"...
This is what clients should use. Have simple choices predefined by designers based on the current design and needs... which means ... and finally you realize that you don't need to allow editing of CSS attributes but you simply need powerful style definition/management which results in simple adding/removing of class names...
oh, it's a painful world, where all the needed functions are just there, but one can't reach them
i wonder could ctypes make things any better?
Why not make the Gecko interface public instead of rewriting it in your app?