1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 #ifndef mozilla_CSSEditUtils_h
7 #define mozilla_CSSEditUtils_h
8 
9 #include "mozilla/ChangeStyleTransaction.h"  // for ChangeStyleTransaction
10 #include "nsCOMPtr.h"                        // for already_AddRefed
11 #include "nsStringFwd.h"
12 #include "nsTArray.h"  // for nsTArray
13 #include "nscore.h"    // for nsAString, nsresult, nullptr
14 
15 class nsComputedDOMStyle;
16 class nsAtom;
17 class nsIContent;
18 class nsICSSDeclaration;
19 class nsINode;
20 class nsStaticAtom;
21 class nsStyledElement;
22 
23 namespace mozilla {
24 
25 class HTMLEditor;
26 namespace dom {
27 class Element;
28 }  // namespace dom
29 
30 typedef void (*nsProcessValueFunc)(const nsAString* aInputString,
31                                    nsAString& aOutputString,
32                                    const char* aDefaultValueString,
33                                    const char* aPrependString,
34                                    const char* aAppendString);
35 
36 class CSSEditUtils final {
37  public:
38   explicit CSSEditUtils(HTMLEditor* aEditor);
39 
40   enum nsCSSEditableProperty {
41     eCSSEditableProperty_NONE = 0,
42     eCSSEditableProperty_background_color,
43     eCSSEditableProperty_background_image,
44     eCSSEditableProperty_border,
45     eCSSEditableProperty_caption_side,
46     eCSSEditableProperty_color,
47     eCSSEditableProperty_float,
48     eCSSEditableProperty_font_family,
49     eCSSEditableProperty_font_size,
50     eCSSEditableProperty_font_style,
51     eCSSEditableProperty_font_weight,
52     eCSSEditableProperty_height,
53     eCSSEditableProperty_list_style_type,
54     eCSSEditableProperty_margin_left,
55     eCSSEditableProperty_margin_right,
56     eCSSEditableProperty_text_align,
57     eCSSEditableProperty_text_decoration,
58     eCSSEditableProperty_vertical_align,
59     eCSSEditableProperty_whitespace,
60     eCSSEditableProperty_width
61   };
62 
63   // Nb: keep these fields in an order that minimizes padding.
64   struct CSSEquivTable {
65     nsCSSEditableProperty cssProperty;
66     bool gettable;
67     bool caseSensitiveValue;
68     nsProcessValueFunc processValueFunctor;
69     const char* defaultValue;
70     const char* prependValue;
71     const char* appendValue;
72   };
73 
74   /**
75    * Answers true if the given combination element_name/attribute_name
76    * has a CSS equivalence in this implementation.
77    *
78    * @param aNode          [IN] A DOM node.
79    * @param aProperty      [IN] An atom containing a HTML tag name.
80    * @param aAttribute     [IN] An atom containing a HTML
81    *                            attribute carried by the element above.
82    * @return               A boolean saying if the tag/attribute has a CSS
83    *                       equiv.
84    */
85   static bool IsCSSEditableProperty(nsINode* aNode, nsAtom* aProperty,
86                                     nsAtom* aAttribute);
87 
88   /**
89    * Adds/remove a CSS declaration to the STYLE attribute carried by a given
90    * element.
91    *
92    * @param aStyledElement [IN] A DOM styled element.
93    * @param aProperty      [IN] An atom containing the CSS property to set.
94    * @param aValue         [IN] A string containing the value of the CSS
95    *                            property.
96    */
97   [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
SetCSSPropertyWithTransaction(nsStyledElement & aStyledElement,nsAtom & aProperty,const nsAString & aValue)98   SetCSSPropertyWithTransaction(nsStyledElement& aStyledElement,
99                                 nsAtom& aProperty, const nsAString& aValue) {
100     return SetCSSPropertyInternal(aStyledElement, aProperty, aValue, false);
101   }
102   [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult SetCSSPropertyPixelsWithTransaction(
103       nsStyledElement& aStyledElement, nsAtom& aProperty, int32_t aIntValue);
104   [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
105   SetCSSPropertyPixelsWithoutTransaction(nsStyledElement& aStyledElement,
106                                          const nsAtom& aProperty,
107                                          int32_t aIntValue);
RemoveCSSPropertyWithTransaction(nsStyledElement & aStyledElement,nsAtom & aProperty,const nsAString & aPropertyValue)108   [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult RemoveCSSPropertyWithTransaction(
109       nsStyledElement& aStyledElement, nsAtom& aProperty,
110       const nsAString& aPropertyValue) {
111     return RemoveCSSPropertyInternal(aStyledElement, aProperty, aPropertyValue,
112                                      false);
113   }
114 
115   /**
116    * Gets the specified/computed style value of a CSS property for a given
117    * node (or its element ancestor if it is not an element).
118    *
119    * @param aContent       [IN] A DOM node.
120    * @param aProperty      [IN] An atom containing the CSS property to get.
121    * @param aPropertyValue [OUT] The retrieved value of the property.
122    */
123   static nsresult GetSpecifiedProperty(nsIContent& aContent,
124                                        nsAtom& aCSSProperty, nsAString& aValue);
125   MOZ_CAN_RUN_SCRIPT static nsresult GetComputedProperty(nsIContent& aContent,
126                                                          nsAtom& aCSSProperty,
127                                                          nsAString& aValue);
128 
129   /**
130    * Removes a CSS property from the specified declarations in STYLE attribute
131    * and removes the node if it is an useless span.
132    *
133    * @param aStyledElement  [IN] The styled element we want to remove a style
134    *                             from.
135    * @param aProperty       [IN] The CSS property atom to remove.
136    * @param aPropertyValue  [IN] The value of the property we have to remove
137    *                             if the property accepts more than one value.
138    */
139   [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult RemoveCSSInlineStyleWithTransaction(
140       nsStyledElement& aStyledElement, nsAtom* aProperty,
141       const nsAString& aPropertyValue);
142 
143   /**
144    * Answers true is the property can be removed by setting a "none" CSS value
145    * on a node.
146    *
147    * @param aProperty     [IN] An atom containing a CSS property.
148    * @param aAttribute    [IN] Pointer to an attribute name or null if this
149    *                           information is irrelevant.
150    * @return              A boolean saying if the property can be remove by
151    *                      setting a "none" value.
152    */
153   static bool IsCSSInvertible(nsAtom& aProperty, nsAtom* aAttribute);
154 
155   /**
156    * Get the default browser background color if we need it for
157    * GetCSSBackgroundColorState().
158    *
159    * @param aColor         [OUT] The default color as it is defined in prefs.
160    */
161   static void GetDefaultBackgroundColor(nsAString& aColor);
162 
163   /**
164    * Get the default length unit used for CSS Indent/Outdent.
165    *
166    * @param aLengthUnit    [OUT] The default length unit as it is defined in
167    *                             prefs.
168    */
169   static void GetDefaultLengthUnit(nsAString& aLengthUnit);
170 
171   /**
172    * Returns the list of values for the CSS equivalences to
173    * the passed HTML style for the passed node.
174    *
175    * @param aContent       [IN] A DOM node.
176    * @param aHTMLProperty  [IN] An atom containing an HTML property.
177    * @param aAttribute     [IN] An atom of attribute name or nullptr if
178    *                            irrelevant.
179    * @param aValueString   [OUT] The list of CSS values.
180    */
181   MOZ_CAN_RUN_SCRIPT static nsresult
GetComputedCSSEquivalentToHTMLInlineStyleSet(nsIContent & aContent,nsAtom * aHTMLProperty,nsAtom * aAttribute,nsAString & aValue)182   GetComputedCSSEquivalentToHTMLInlineStyleSet(nsIContent& aContent,
183                                                nsAtom* aHTMLProperty,
184                                                nsAtom* aAttribute,
185                                                nsAString& aValue) {
186     return GetCSSEquivalentToHTMLInlineStyleSetInternal(
187         aContent, aHTMLProperty, aAttribute, aValue, StyleType::Computed);
188   }
189 
190   /**
191    * Does the node aNode (or his parent if it is not an element node) carries
192    * the CSS equivalent styles to the HTML style for this node ?
193    *
194    * @param aContent       [IN] A DOM node.
195    * @param aHTMLProperty  [IN] An atom containing an HTML property.
196    * @param aAttribute     [IN] A pointer/atom to an attribute name or nullptr
197    *                            if irrelevant.
198    * @param aValueString   [IN/OUT] The attribute value (in) the list of CSS
199    *                                values (out).
200    * @return               A boolean being true if the css properties are
201    *                       not same as initial value.
202    */
IsComputedCSSEquivalentToHTMLInlineStyleSet(nsIContent & aContent,nsAtom * aHTMLProperty,nsAtom * aAttribute,nsAString & aValue)203   MOZ_CAN_RUN_SCRIPT static bool IsComputedCSSEquivalentToHTMLInlineStyleSet(
204       nsIContent& aContent, nsAtom* aHTMLProperty, nsAtom* aAttribute,
205       nsAString& aValue) {
206     MOZ_ASSERT(aHTMLProperty || aAttribute);
207     return IsCSSEquivalentToHTMLInlineStyleSetInternal(
208         aContent, aHTMLProperty, aAttribute, aValue, StyleType::Computed);
209   }
210   MOZ_CAN_RUN_SCRIPT_BOUNDARY static bool
IsSpecifiedCSSEquivalentToHTMLInlineStyleSet(nsIContent & aContent,nsAtom * aHTMLProperty,nsAtom * aAttribute,nsAString & aValue)211   IsSpecifiedCSSEquivalentToHTMLInlineStyleSet(nsIContent& aContent,
212                                                nsAtom* aHTMLProperty,
213                                                nsAtom* aAttribute,
214                                                nsAString& aValue) {
215     MOZ_ASSERT(aHTMLProperty || aAttribute);
216     return IsCSSEquivalentToHTMLInlineStyleSetInternal(
217         aContent, aHTMLProperty, aAttribute, aValue, StyleType::Specified);
218   }
219 
220   /**
221    * This is a kind of IsCSSEquivalentToHTMLInlineStyleSet.
222    * IsCSSEquivalentToHTMLInlineStyleSet returns whether the properties
223    * aren't same as initial value.  But this method returns whether the
224    * properties aren't set.
225    * If node is <span style="font-weight: normal"/>,
226    *  - Is(Computed|Specified)CSSEquivalentToHTMLInlineStyleSet returns false.
227    *  - Have(Computed|Specified)CSSEquivalentStyles returns true.
228    *
229    * @param aContent       [IN] A DOM node.
230    * @param aHTMLProperty  [IN] An atom containing an HTML property.
231    * @param aAttribute     [IN] An atom to an attribute name or nullptr
232    *                            if irrelevant.
233    * @return               A boolean being true if the css properties are
234    *                       not set.
235    */
HaveComputedCSSEquivalentStyles(nsIContent & aContent,nsAtom * aHTMLProperty,nsAtom * aAttribute)236   MOZ_CAN_RUN_SCRIPT static bool HaveComputedCSSEquivalentStyles(
237       nsIContent& aContent, nsAtom* aHTMLProperty, nsAtom* aAttribute) {
238     MOZ_ASSERT(aHTMLProperty || aAttribute);
239     return HaveCSSEquivalentStylesInternal(aContent, aHTMLProperty, aAttribute,
240                                            StyleType::Computed);
241   }
HaveSpecifiedCSSEquivalentStyles(nsIContent & aContent,nsAtom * aHTMLProperty,nsAtom * aAttribute)242   MOZ_CAN_RUN_SCRIPT_BOUNDARY static bool HaveSpecifiedCSSEquivalentStyles(
243       nsIContent& aContent, nsAtom* aHTMLProperty, nsAtom* aAttribute) {
244     MOZ_ASSERT(aHTMLProperty || aAttribute);
245     return HaveCSSEquivalentStylesInternal(aContent, aHTMLProperty, aAttribute,
246                                            StyleType::Specified);
247   }
248 
249   /**
250    * Adds to the node the CSS inline styles equivalent to the HTML style
251    * and return the number of CSS properties set by the call.
252    *
253    * @param aNode          [IN] A DOM node.
254    * @param aHTMLProperty  [IN] An atom containing an HTML property.
255    * @param aAttribute     [IN] An atom to an attribute name or nullptr
256    *                            if irrelevant.
257    * @param aValue         [IN] The attribute value.
258    *
259    * @return               The number of CSS properties set by the call.
260    */
261   [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<int32_t, nsresult>
SetCSSEquivalentToHTMLStyleWithTransaction(nsStyledElement & aStyledElement,nsAtom * aProperty,nsAtom * aAttribute,const nsAString * aValue)262   SetCSSEquivalentToHTMLStyleWithTransaction(nsStyledElement& aStyledElement,
263                                              nsAtom* aProperty,
264                                              nsAtom* aAttribute,
265                                              const nsAString* aValue) {
266     return SetCSSEquivalentToHTMLStyleInternal(aStyledElement, aProperty,
267                                                aAttribute, aValue, false);
268   }
269   [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<int32_t, nsresult>
SetCSSEquivalentToHTMLStyleWithoutTransaction(nsStyledElement & aStyledElement,nsAtom * aProperty,nsAtom * aAttribute,const nsAString * aValue)270   SetCSSEquivalentToHTMLStyleWithoutTransaction(nsStyledElement& aStyledElement,
271                                                 nsAtom* aProperty,
272                                                 nsAtom* aAttribute,
273                                                 const nsAString* aValue) {
274     return SetCSSEquivalentToHTMLStyleInternal(aStyledElement, aProperty,
275                                                aAttribute, aValue, true);
276   }
277 
278   /**
279    * Removes from the node the CSS inline styles equivalent to the HTML style.
280    *
281    * @param aStyledElement [IN] A DOM Element (must not be null).
282    * @param aHTMLProperty  [IN] An atom containing an HTML property.
283    * @param aAttribute     [IN] An atom to an attribute name or nullptr if
284    *                            irrelevant.
285    * @param aValue         [IN] The attribute value.
286    */
287   [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
RemoveCSSEquivalentToHTMLStyleWithTransaction(nsStyledElement & aStyledElement,nsAtom * aHTMLProperty,nsAtom * aAttribute,const nsAString * aValue)288   RemoveCSSEquivalentToHTMLStyleWithTransaction(nsStyledElement& aStyledElement,
289                                                 nsAtom* aHTMLProperty,
290                                                 nsAtom* aAttribute,
291                                                 const nsAString* aValue) {
292     return RemoveCSSEquivalentToHTMLStyleInternal(aStyledElement, aHTMLProperty,
293                                                   aAttribute, aValue, false);
294   }
295   [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
RemoveCSSEquivalentToHTMLStyleWithoutTransaction(nsStyledElement & aStyledElement,nsAtom * aHTMLProperty,nsAtom * aAttribute,const nsAString * aValue)296   RemoveCSSEquivalentToHTMLStyleWithoutTransaction(
297       nsStyledElement& aStyledElement, nsAtom* aHTMLProperty,
298       nsAtom* aAttribute, const nsAString* aValue) {
299     return RemoveCSSEquivalentToHTMLStyleInternal(aStyledElement, aHTMLProperty,
300                                                   aAttribute, aValue, true);
301   }
302 
303   /**
304    * Parses a "xxxx.xxxxxuuu" string where x is a digit and u an alpha char.
305    *
306    * @param aString        [IN] Input string to parse.
307    * @param aValue         [OUT] Numeric part.
308    * @param aUnit          [OUT] Unit part.
309    */
310   static void ParseLength(const nsAString& aString, float* aValue,
311                           nsAtom** aUnit);
312 
313   /**
314    * Sets the mIsCSSPrefChecked private member; used as callback from observer
315    *  when the CSS pref state is changed.
316    *
317    * @param aIsCSSPrefChecked [IN] The new boolean state for the pref.
318    */
319   void SetCSSEnabled(bool aIsCSSPrefChecked);
320 
321   /**
322    * Retrieves the mIsCSSPrefChecked private member, true if the CSS pref is
323    * checked, false if it is not.
324    *
325    * @return                 the boolean value of the CSS pref.
326    */
327   bool IsCSSPrefChecked() const;
328 
329   /**
330    * DoStyledElementsHaveSameStyle compares two elements and checks if they have
331    * the same specified CSS declarations in the STYLE attribute. The answer is
332    * always false if at least one of them carries an ID or a class.
333    *
334    * @param aStyledElement      [IN] A styled element.
335    * @param aOtherStyledElement [IN] The other styled element.
336    * @return                    true if the two elements are considered to
337    *                            have same styles.
338    */
339   static bool DoStyledElementsHaveSameStyle(
340       nsStyledElement& aStyledElement, nsStyledElement& aOtherStyledElement);
341 
342  public:
343   /**
344    * Gets the computed style for a given element.  Can return null.
345    */
346   static already_AddRefed<nsComputedDOMStyle> GetComputedStyle(
347       dom::Element* aElement);
348 
349  private:
350   enum class StyleType { Specified, Computed };
351 
352   /**
353    * Retrieves the CSS property atom from an enum.
354    *
355    * @param aProperty           The enum value for the property.
356    * @return                    The corresponding atom.
357    */
358   static nsStaticAtom* GetCSSPropertyAtom(nsCSSEditableProperty aProperty);
359 
360   /**
361    * Retrieves the CSS declarations equivalent to a HTML style value for
362    * a given equivalence table.
363    *
364    * @param aOutArrayOfCSSProperty  [OUT] The array of css properties.
365    * @param aOutArrayOfCSSValue     [OUT] The array of values for the CSS
366    *                                      properties above.
367    * @param aEquivTable             The equivalence table.
368    * @param aValue                  The HTML style value.
369    * @param aGetOrRemoveRequest     A boolean value being true if the call to
370    *                                the current method is made for
371    *                                Get*CSSEquivalentToHTMLInlineStyleSet()
372    *                                or
373    *                                RemoveCSSEquivalentToHTMLInlineStyleSet().
374    */
375   static void BuildCSSDeclarations(
376       nsTArray<nsStaticAtom*>& aOutArrayOfCSSProperty,
377       nsTArray<nsString>& aOutArrayOfCSSValue, const CSSEquivTable* aEquivTable,
378       const nsAString* aValue, bool aGetOrRemoveRequest);
379 
380   /**
381    * Retrieves the CSS declarations equivalent to the given HTML
382    * property/attribute/value for a given node.
383    *
384    * @param aElement                The DOM node.
385    * @param aHTMLProperty           An atom containing an HTML property.
386    * @param aAttribute              An atom to an attribute name or nullptr
387    *                                if irrelevant
388    * @param aValue                  The attribute value.
389    * @param aOutArrayOfCSSProperty  [OUT] The array of CSS properties.
390    * @param aOutArrayOfCSSValue     [OUT] The array of values for the CSS
391    *                                      properties above.
392    * @param aGetOrRemoveRequest     A boolean value being true if the call to
393    *                                the current method is made for
394    *                                Get*CSSEquivalentToHTMLInlineStyleSet() or
395    *                                RemoveCSSEquivalentToHTMLInlineStyleSet().
396    */
397   static void GenerateCSSDeclarationsFromHTMLStyle(
398       dom::Element& aElement, nsAtom* aHTMLProperty, nsAtom* aAttribute,
399       const nsAString* aValue, nsTArray<nsStaticAtom*>& aOutArrayOfCSSProperty,
400       nsTArray<nsString>& aOutArrayOfCSSValue, bool aGetOrRemoveRequest);
401 
402   /**
403    * Back-end for GetSpecifiedProperty and GetComputedProperty.
404    *
405    * @param aNode               [IN] A DOM node.
406    * @param aProperty           [IN] A CSS property.
407    * @param aValue              [OUT] The retrieved value for this property.
408    */
409   MOZ_CAN_RUN_SCRIPT static nsresult GetComputedCSSInlinePropertyBase(
410       nsIContent& aContent, nsAtom& aCSSProperty, nsAString& aValue);
411   static nsresult GetSpecifiedCSSInlinePropertyBase(nsIContent& aContent,
412                                                     nsAtom& aCSSProperty,
413                                                     nsAString& aValue);
414 
415   /**
416    * Those methods are wrapped with corresponding methods which do not have
417    * "Internal" in their names.  Don't use these methods directly even if
418    * you want to use one of them in this class.
419    * Note that these methods may run scrip only when StyleType is Computed.
420    */
421   MOZ_CAN_RUN_SCRIPT static nsresult
422   GetCSSEquivalentToHTMLInlineStyleSetInternal(nsIContent& aContent,
423                                                nsAtom* aHTMLProperty,
424                                                nsAtom* aAttribute,
425                                                nsAString& aValue,
426                                                StyleType aStyleType);
427   MOZ_CAN_RUN_SCRIPT static bool IsCSSEquivalentToHTMLInlineStyleSetInternal(
428       nsIContent& aContent, nsAtom* aHTMLProperty, nsAtom* aAttribute,
429       nsAString& aValue, StyleType aStyleType);
430   MOZ_CAN_RUN_SCRIPT static bool HaveCSSEquivalentStylesInternal(
431       nsIContent& aContent, nsAtom* aHTMLProperty, nsAtom* aAttribute,
432       StyleType aStyleType);
433 
434   [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult RemoveCSSPropertyInternal(
435       nsStyledElement& aStyledElement, nsAtom& aProperty,
436       const nsAString& aPropertyValue, bool aSuppressTxn = false);
437   [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
438   RemoveCSSEquivalentToHTMLStyleInternal(nsStyledElement& aStyledElement,
439                                          nsAtom* aHTMLProperty,
440                                          nsAtom* aAttribute,
441                                          const nsAString* aValue,
442                                          bool aSuppressTransaction);
443   [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
444   SetCSSPropertyInternal(nsStyledElement& aStyledElement, nsAtom& aProperty,
445                          const nsAString& aValue, bool aSuppressTxn = false);
446   [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<int32_t, nsresult>
447   SetCSSEquivalentToHTMLStyleInternal(nsStyledElement& aStyledElement,
448                                       nsAtom* aProperty, nsAtom* aAttribute,
449                                       const nsAString* aValue,
450                                       bool aSuppressTransaction);
451 
452  private:
453   HTMLEditor* mHTMLEditor;
454   bool mIsCSSPrefChecked;
455 };
456 
457 #define NS_EDITOR_INDENT_INCREMENT_IN 0.4134f
458 #define NS_EDITOR_INDENT_INCREMENT_CM 1.05f
459 #define NS_EDITOR_INDENT_INCREMENT_MM 10.5f
460 #define NS_EDITOR_INDENT_INCREMENT_PT 29.76f
461 #define NS_EDITOR_INDENT_INCREMENT_PC 2.48f
462 #define NS_EDITOR_INDENT_INCREMENT_EM 3
463 #define NS_EDITOR_INDENT_INCREMENT_EX 6
464 #define NS_EDITOR_INDENT_INCREMENT_PX 40
465 #define NS_EDITOR_INDENT_INCREMENT_PERCENT 4
466 
467 }  // namespace mozilla
468 
469 #endif  // #ifndef mozilla_CSSEditUtils_h
470