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/. */
6 #include "ChangeStyleTxn.h"
8 #include "mozilla/Assertions.h"
9 #include "mozilla/Preferences.h"
10 #include "mozilla/css/Declaration.h"
11 #include "mozilla/css/StyleRule.h"
12 #include "mozilla/dom/Element.h"
13 #include "mozilla/mozalloc.h"
14 #include "nsAString.h"
15 #include "nsAutoPtr.h"
18 #include "nsComputedDOMStyle.h"
20 #include "nsDependentSubstring.h"
22 #include "nsGkAtoms.h"
23 #include "nsHTMLCSSUtils.h"
24 #include "nsHTMLEditor.h"
26 #include "nsIContent.h"
27 #include "nsIDOMCSSStyleDeclaration.h"
28 #include "nsIDOMElement.h"
29 #include "nsIDOMElementCSSInlineStyle.h"
30 #include "nsIDOMNode.h"
31 #include "nsIDOMWindow.h"
32 #include "nsIDocument.h"
33 #include "nsIEditor.h"
35 #include "nsISupportsImpl.h"
36 #include "nsISupportsUtils.h"
37 #include "nsLiteralString.h"
38 #include "nsPIDOMWindow.h"
39 #include "nsReadableUtils.h"
41 #include "nsStringFwd.h"
42 #include "nsStringIterator.h"
43 #include "nsSubstringTuple.h"
44 #include "nsUnicharUtils.h"
46 using namespace mozilla
;
47 using namespace mozilla::dom
;
50 void ProcessBValue(const nsAString
* aInputString
, nsAString
& aOutputString
,
51 const char * aDefaultValueString
,
52 const char * aPrependString
, const char* aAppendString
)
54 if (aInputString
&& aInputString
->EqualsLiteral("-moz-editor-invert-value")) {
55 aOutputString
.AssignLiteral("normal");
58 aOutputString
.AssignLiteral("bold");
63 void ProcessDefaultValue(const nsAString
* aInputString
, nsAString
& aOutputString
,
64 const char * aDefaultValueString
,
65 const char * aPrependString
, const char* aAppendString
)
67 CopyASCIItoUTF16(aDefaultValueString
, aOutputString
);
71 void ProcessSameValue(const nsAString
* aInputString
, nsAString
& aOutputString
,
72 const char * aDefaultValueString
,
73 const char * aPrependString
, const char* aAppendString
)
76 aOutputString
.Assign(*aInputString
);
79 aOutputString
.Truncate();
83 void ProcessExtendedValue(const nsAString
* aInputString
, nsAString
& aOutputString
,
84 const char * aDefaultValueString
,
85 const char * aPrependString
, const char* aAppendString
)
87 aOutputString
.Truncate();
90 AppendASCIItoUTF16(aPrependString
, aOutputString
);
92 aOutputString
.Append(*aInputString
);
94 AppendASCIItoUTF16(aAppendString
, aOutputString
);
100 void ProcessLengthValue(const nsAString
* aInputString
, nsAString
& aOutputString
,
101 const char * aDefaultValueString
,
102 const char * aPrependString
, const char* aAppendString
)
104 aOutputString
.Truncate();
106 aOutputString
.Append(*aInputString
);
107 if (-1 == aOutputString
.FindChar(char16_t('%'))) {
108 aOutputString
.AppendLiteral("px");
114 void ProcessListStyleTypeValue(const nsAString
* aInputString
, nsAString
& aOutputString
,
115 const char * aDefaultValueString
,
116 const char * aPrependString
, const char* aAppendString
)
118 aOutputString
.Truncate();
120 if (aInputString
->EqualsLiteral("1")) {
121 aOutputString
.AppendLiteral("decimal");
123 else if (aInputString
->EqualsLiteral("a")) {
124 aOutputString
.AppendLiteral("lower-alpha");
126 else if (aInputString
->EqualsLiteral("A")) {
127 aOutputString
.AppendLiteral("upper-alpha");
129 else if (aInputString
->EqualsLiteral("i")) {
130 aOutputString
.AppendLiteral("lower-roman");
132 else if (aInputString
->EqualsLiteral("I")) {
133 aOutputString
.AppendLiteral("upper-roman");
135 else if (aInputString
->EqualsLiteral("square")
136 || aInputString
->EqualsLiteral("circle")
137 || aInputString
->EqualsLiteral("disc")) {
138 aOutputString
.Append(*aInputString
);
144 void ProcessMarginLeftValue(const nsAString
* aInputString
, nsAString
& aOutputString
,
145 const char * aDefaultValueString
,
146 const char * aPrependString
, const char* aAppendString
)
148 aOutputString
.Truncate();
150 if (aInputString
->EqualsLiteral("center") ||
151 aInputString
->EqualsLiteral("-moz-center")) {
152 aOutputString
.AppendLiteral("auto");
154 else if (aInputString
->EqualsLiteral("right") ||
155 aInputString
->EqualsLiteral("-moz-right")) {
156 aOutputString
.AppendLiteral("auto");
159 aOutputString
.AppendLiteral("0px");
165 void ProcessMarginRightValue(const nsAString
* aInputString
, nsAString
& aOutputString
,
166 const char * aDefaultValueString
,
167 const char * aPrependString
, const char* aAppendString
)
169 aOutputString
.Truncate();
171 if (aInputString
->EqualsLiteral("center") ||
172 aInputString
->EqualsLiteral("-moz-center")) {
173 aOutputString
.AppendLiteral("auto");
175 else if (aInputString
->EqualsLiteral("left") ||
176 aInputString
->EqualsLiteral("-moz-left")) {
177 aOutputString
.AppendLiteral("auto");
180 aOutputString
.AppendLiteral("0px");
185 const nsHTMLCSSUtils::CSSEquivTable boldEquivTable
[] = {
186 { nsHTMLCSSUtils::eCSSEditableProperty_font_weight
, ProcessBValue
, nullptr, nullptr, nullptr, true, false },
187 { nsHTMLCSSUtils::eCSSEditableProperty_NONE
, 0 }
190 const nsHTMLCSSUtils::CSSEquivTable italicEquivTable
[] = {
191 { nsHTMLCSSUtils::eCSSEditableProperty_font_style
, ProcessDefaultValue
, "italic", nullptr, nullptr, true, false },
192 { nsHTMLCSSUtils::eCSSEditableProperty_NONE
, 0 }
195 const nsHTMLCSSUtils::CSSEquivTable underlineEquivTable
[] = {
196 { nsHTMLCSSUtils::eCSSEditableProperty_text_decoration
, ProcessDefaultValue
, "underline", nullptr, nullptr, true, false },
197 { nsHTMLCSSUtils::eCSSEditableProperty_NONE
, 0 }
200 const nsHTMLCSSUtils::CSSEquivTable strikeEquivTable
[] = {
201 { nsHTMLCSSUtils::eCSSEditableProperty_text_decoration
, ProcessDefaultValue
, "line-through", nullptr, nullptr, true, false },
202 { nsHTMLCSSUtils::eCSSEditableProperty_NONE
, 0 }
205 const nsHTMLCSSUtils::CSSEquivTable ttEquivTable
[] = {
206 { nsHTMLCSSUtils::eCSSEditableProperty_font_family
, ProcessDefaultValue
, "monospace", nullptr, nullptr, true, false },
207 { nsHTMLCSSUtils::eCSSEditableProperty_NONE
, 0 }
210 const nsHTMLCSSUtils::CSSEquivTable fontColorEquivTable
[] = {
211 { nsHTMLCSSUtils::eCSSEditableProperty_color
, ProcessSameValue
, nullptr, nullptr, nullptr, true, false },
212 { nsHTMLCSSUtils::eCSSEditableProperty_NONE
, 0 }
215 const nsHTMLCSSUtils::CSSEquivTable fontFaceEquivTable
[] = {
216 { nsHTMLCSSUtils::eCSSEditableProperty_font_family
, ProcessSameValue
, nullptr, nullptr, nullptr, true, false },
217 { nsHTMLCSSUtils::eCSSEditableProperty_NONE
, 0 }
220 const nsHTMLCSSUtils::CSSEquivTable bgcolorEquivTable
[] = {
221 { nsHTMLCSSUtils::eCSSEditableProperty_background_color
, ProcessSameValue
, nullptr, nullptr, nullptr, true, false },
222 { nsHTMLCSSUtils::eCSSEditableProperty_NONE
, 0 }
225 const nsHTMLCSSUtils::CSSEquivTable backgroundImageEquivTable
[] = {
226 { nsHTMLCSSUtils::eCSSEditableProperty_background_image
, ProcessExtendedValue
, nullptr, "url(", ")", true, true },
227 { nsHTMLCSSUtils::eCSSEditableProperty_NONE
, 0 }
230 const nsHTMLCSSUtils::CSSEquivTable textColorEquivTable
[] = {
231 { nsHTMLCSSUtils::eCSSEditableProperty_color
, ProcessSameValue
, nullptr, nullptr, nullptr, true, false },
232 { nsHTMLCSSUtils::eCSSEditableProperty_NONE
, 0 }
235 const nsHTMLCSSUtils::CSSEquivTable borderEquivTable
[] = {
236 { nsHTMLCSSUtils::eCSSEditableProperty_border
, ProcessExtendedValue
, nullptr, nullptr, "px solid", true, false },
237 { nsHTMLCSSUtils::eCSSEditableProperty_NONE
, 0 }
240 const nsHTMLCSSUtils::CSSEquivTable textAlignEquivTable
[] = {
241 { nsHTMLCSSUtils::eCSSEditableProperty_text_align
, ProcessSameValue
, nullptr, nullptr, nullptr, true, false },
242 { nsHTMLCSSUtils::eCSSEditableProperty_NONE
, 0 }
245 const nsHTMLCSSUtils::CSSEquivTable captionAlignEquivTable
[] = {
246 { nsHTMLCSSUtils::eCSSEditableProperty_caption_side
, ProcessSameValue
, nullptr, nullptr, nullptr, true, false },
247 { nsHTMLCSSUtils::eCSSEditableProperty_NONE
, 0 }
250 const nsHTMLCSSUtils::CSSEquivTable verticalAlignEquivTable
[] = {
251 { nsHTMLCSSUtils::eCSSEditableProperty_vertical_align
, ProcessSameValue
, nullptr, nullptr, nullptr, true, false },
252 { nsHTMLCSSUtils::eCSSEditableProperty_NONE
, 0 }
255 const nsHTMLCSSUtils::CSSEquivTable nowrapEquivTable
[] = {
256 { nsHTMLCSSUtils::eCSSEditableProperty_whitespace
, ProcessDefaultValue
, "nowrap", nullptr, nullptr, true, false },
257 { nsHTMLCSSUtils::eCSSEditableProperty_NONE
, 0 }
260 const nsHTMLCSSUtils::CSSEquivTable widthEquivTable
[] = {
261 { nsHTMLCSSUtils::eCSSEditableProperty_width
, ProcessLengthValue
, nullptr, nullptr, nullptr, true, false },
262 { nsHTMLCSSUtils::eCSSEditableProperty_NONE
, 0 }
265 const nsHTMLCSSUtils::CSSEquivTable heightEquivTable
[] = {
266 { nsHTMLCSSUtils::eCSSEditableProperty_height
, ProcessLengthValue
, nullptr, nullptr, nullptr, true, false },
267 { nsHTMLCSSUtils::eCSSEditableProperty_NONE
, 0 }
270 const nsHTMLCSSUtils::CSSEquivTable listStyleTypeEquivTable
[] = {
271 { nsHTMLCSSUtils::eCSSEditableProperty_list_style_type
, ProcessListStyleTypeValue
, nullptr, nullptr, nullptr, true, true },
272 { nsHTMLCSSUtils::eCSSEditableProperty_NONE
, 0 }
275 const nsHTMLCSSUtils::CSSEquivTable tableAlignEquivTable
[] = {
276 { nsHTMLCSSUtils::eCSSEditableProperty_text_align
, ProcessDefaultValue
, "left", nullptr, nullptr, false, false },
277 { nsHTMLCSSUtils::eCSSEditableProperty_margin_left
, ProcessMarginLeftValue
, nullptr, nullptr, nullptr, true, false },
278 { nsHTMLCSSUtils::eCSSEditableProperty_margin_right
, ProcessMarginRightValue
, nullptr, nullptr, nullptr, true, false },
279 { nsHTMLCSSUtils::eCSSEditableProperty_NONE
, 0 }
282 const nsHTMLCSSUtils::CSSEquivTable hrAlignEquivTable
[] = {
283 { nsHTMLCSSUtils::eCSSEditableProperty_margin_left
, ProcessMarginLeftValue
, nullptr, nullptr, nullptr, true, false },
284 { nsHTMLCSSUtils::eCSSEditableProperty_margin_right
, ProcessMarginRightValue
, nullptr, nullptr, nullptr, true, false },
285 { nsHTMLCSSUtils::eCSSEditableProperty_NONE
, 0 }
288 nsHTMLCSSUtils::nsHTMLCSSUtils(nsHTMLEditor
* aEditor
)
289 : mHTMLEditor(aEditor
)
290 , mIsCSSPrefChecked(true)
292 // let's retrieve the value of the "CSS editing" pref
293 mIsCSSPrefChecked
= Preferences::GetBool("editor.use_css", mIsCSSPrefChecked
);
296 nsHTMLCSSUtils::~nsHTMLCSSUtils()
300 // Answers true if we have some CSS equivalence for the HTML style defined
301 // by aProperty and/or aAttribute for the node aNode
303 nsHTMLCSSUtils::IsCSSEditableProperty(nsIDOMNode
* aNode
,
305 const nsAString
* aAttribute
)
307 NS_ASSERTION(aNode
, "Shouldn't you pass aNode? - Bug 214025");
309 nsCOMPtr
<nsIContent
> content
= do_QueryInterface(aNode
);
310 NS_ENSURE_TRUE(content
, false);
311 return IsCSSEditableProperty(content
, aProperty
, aAttribute
);
315 nsHTMLCSSUtils::IsCSSEditableProperty(nsIContent
* aNode
,
317 const nsAString
* aAttribute
)
321 nsIContent
* content
= aNode
;
322 // we need an element node here
323 if (content
->NodeType() == nsIDOMNode::TEXT_NODE
) {
324 content
= content
->GetParent();
325 NS_ENSURE_TRUE(content
, false);
328 nsIAtom
*tagName
= content
->Tag();
329 // brade: shouldn't some of the above go below the next block?
331 // html inline styles B I TT U STRIKE and COLOR/FACE on FONT
332 if (nsGkAtoms::b
== aProperty
||
333 nsGkAtoms::i
== aProperty
||
334 nsGkAtoms::tt
== aProperty
||
335 nsGkAtoms::u
== aProperty
||
336 nsGkAtoms::strike
== aProperty
||
337 (nsGkAtoms::font
== aProperty
&& aAttribute
&&
338 (aAttribute
->EqualsLiteral("color") ||
339 aAttribute
->EqualsLiteral("face")))) {
343 // ALIGN attribute on elements supporting it
344 if (aAttribute
&& (aAttribute
->EqualsLiteral("align")) &&
345 (nsGkAtoms::div
== tagName
||
346 nsGkAtoms::p
== tagName
||
347 nsGkAtoms::h1
== tagName
||
348 nsGkAtoms::h2
== tagName
||
349 nsGkAtoms::h3
== tagName
||
350 nsGkAtoms::h4
== tagName
||
351 nsGkAtoms::h5
== tagName
||
352 nsGkAtoms::h6
== tagName
||
353 nsGkAtoms::td
== tagName
||
354 nsGkAtoms::th
== tagName
||
355 nsGkAtoms::table
== tagName
||
356 nsGkAtoms::hr
== tagName
||
357 // brade: for the above, why not use nsHTMLEditUtils::SupportsAlignAttr
358 // brade: but it also checks for tbody, tfoot, thead
359 // Let's add the following elements here even if ALIGN has not
360 // the same meaning for them
361 nsGkAtoms::legend
== tagName
||
362 nsGkAtoms::caption
== tagName
)) {
366 if (aAttribute
&& (aAttribute
->EqualsLiteral("valign")) &&
367 (nsGkAtoms::col
== tagName
||
368 nsGkAtoms::colgroup
== tagName
||
369 nsGkAtoms::tbody
== tagName
||
370 nsGkAtoms::td
== tagName
||
371 nsGkAtoms::th
== tagName
||
372 nsGkAtoms::tfoot
== tagName
||
373 nsGkAtoms::thead
== tagName
||
374 nsGkAtoms::tr
== tagName
)) {
378 // attributes TEXT, BACKGROUND and BGCOLOR on BODY
379 if (aAttribute
&& nsGkAtoms::body
== tagName
&&
380 (aAttribute
->EqualsLiteral("text")
381 || aAttribute
->EqualsLiteral("background")
382 || aAttribute
->EqualsLiteral("bgcolor"))) {
386 // attribute BGCOLOR on other elements
387 if (aAttribute
&& aAttribute
->EqualsLiteral("bgcolor")) {
391 // attributes HEIGHT, WIDTH and NOWRAP on TD and TH
392 if (aAttribute
&& (nsGkAtoms::td
== tagName
|| nsGkAtoms::th
== tagName
) &&
393 (aAttribute
->EqualsLiteral("height")
394 || aAttribute
->EqualsLiteral("width")
395 || aAttribute
->EqualsLiteral("nowrap"))) {
399 // attributes HEIGHT and WIDTH on TABLE
400 if (aAttribute
&& nsGkAtoms::table
== tagName
&&
401 (aAttribute
->EqualsLiteral("height")
402 || aAttribute
->EqualsLiteral("width"))) {
406 // attributes SIZE and WIDTH on HR
407 if (aAttribute
&& nsGkAtoms::hr
== tagName
&&
408 (aAttribute
->EqualsLiteral("size")
409 || aAttribute
->EqualsLiteral("width"))) {
413 // attribute TYPE on OL UL LI
415 (nsGkAtoms::ol
== tagName
|| nsGkAtoms::ul
== tagName
||
416 nsGkAtoms::li
== tagName
) && aAttribute
->EqualsLiteral("type")) {
420 if (aAttribute
&& nsGkAtoms::img
== tagName
&&
421 (aAttribute
->EqualsLiteral("border")
422 || aAttribute
->EqualsLiteral("width")
423 || aAttribute
->EqualsLiteral("height"))) {
427 // other elements that we can align using CSS even if they
428 // can't carry the html ALIGN attribute
429 if (aAttribute
&& aAttribute
->EqualsLiteral("align") &&
430 (nsGkAtoms::ul
== tagName
||
431 nsGkAtoms::ol
== tagName
||
432 nsGkAtoms::dl
== tagName
||
433 nsGkAtoms::li
== tagName
||
434 nsGkAtoms::dd
== tagName
||
435 nsGkAtoms::dt
== tagName
||
436 nsGkAtoms::address
== tagName
||
437 nsGkAtoms::pre
== tagName
||
438 nsGkAtoms::ul
== tagName
)) {
445 // The lowest level above the transaction; adds the CSS declaration
446 // "aProperty : aValue" to the inline styles carried by aElement
448 nsHTMLCSSUtils::SetCSSProperty(Element
& aElement
, nsIAtom
& aProperty
,
449 const nsAString
& aValue
, bool aSuppressTxn
)
451 nsRefPtr
<ChangeStyleTxn
> txn
=
452 CreateCSSPropertyTxn(aElement
, aProperty
, aValue
, ChangeStyleTxn::eSet
);
454 return txn
->DoTransaction();
456 return mHTMLEditor
->DoTransaction(txn
);
460 nsHTMLCSSUtils::SetCSSPropertyPixels(Element
& aElement
, nsIAtom
& aProperty
,
464 s
.AppendInt(aIntValue
);
465 return SetCSSProperty(aElement
, aProperty
, s
+ NS_LITERAL_STRING("px"),
466 /* suppress txn */ false);
469 // The lowest level above the transaction; removes the value aValue from the
470 // list of values specified for the CSS property aProperty, or totally remove
471 // the declaration if this property accepts only one value
473 nsHTMLCSSUtils::RemoveCSSProperty(Element
& aElement
, nsIAtom
& aProperty
,
474 const nsAString
& aValue
, bool aSuppressTxn
)
476 nsRefPtr
<ChangeStyleTxn
> txn
=
477 CreateCSSPropertyTxn(aElement
, aProperty
, aValue
, ChangeStyleTxn::eRemove
);
479 return txn
->DoTransaction();
481 return mHTMLEditor
->DoTransaction(txn
);
484 already_AddRefed
<ChangeStyleTxn
>
485 nsHTMLCSSUtils::CreateCSSPropertyTxn(Element
& aElement
, nsIAtom
& aAttribute
,
486 const nsAString
& aValue
,
487 ChangeStyleTxn::EChangeType aChangeType
)
489 nsRefPtr
<ChangeStyleTxn
> txn
=
490 new ChangeStyleTxn(aElement
, aAttribute
, aValue
, aChangeType
);
495 nsHTMLCSSUtils::GetSpecifiedProperty(nsIDOMNode
*aNode
, nsIAtom
*aProperty
,
498 return GetCSSInlinePropertyBase(aNode
, aProperty
, aValue
, eSpecified
);
502 nsHTMLCSSUtils::GetComputedProperty(nsIDOMNode
*aNode
, nsIAtom
*aProperty
,
505 return GetCSSInlinePropertyBase(aNode
, aProperty
, aValue
, eComputed
);
509 nsHTMLCSSUtils::GetCSSInlinePropertyBase(nsIDOMNode
* aNode
, nsIAtom
* aProperty
,
511 StyleType aStyleType
)
513 nsCOMPtr
<nsINode
> node
= do_QueryInterface(aNode
);
514 return GetCSSInlinePropertyBase(node
, aProperty
, aValue
, aStyleType
);
518 nsHTMLCSSUtils::GetCSSInlinePropertyBase(nsINode
* aNode
, nsIAtom
* aProperty
,
520 StyleType aStyleType
)
522 MOZ_ASSERT(aNode
&& aProperty
);
525 nsCOMPtr
<dom::Element
> element
= GetElementContainerOrSelf(aNode
);
526 NS_ENSURE_TRUE(element
, NS_ERROR_NULL_POINTER
);
528 if (aStyleType
== eComputed
) {
529 // Get the all the computed css styles attached to the element node
530 nsRefPtr
<nsComputedDOMStyle
> cssDecl
= GetComputedStyle(element
);
531 NS_ENSURE_STATE(cssDecl
);
533 // from these declarations, get the one we want and that one only
534 MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
535 cssDecl
->GetPropertyValue(nsDependentAtomString(aProperty
), aValue
)));
540 MOZ_ASSERT(aStyleType
== eSpecified
);
541 nsRefPtr
<css::StyleRule
> rule
= element
->GetInlineStyleRule();
546 nsCSSProps::LookupProperty(nsDependentAtomString(aProperty
),
547 nsCSSProps::eEnabledForAllContent
);
548 MOZ_ASSERT(prop
!= eCSSProperty_UNKNOWN
);
549 rule
->GetDeclaration()->GetValue(prop
, aValue
);
554 already_AddRefed
<nsComputedDOMStyle
>
555 nsHTMLCSSUtils::GetComputedStyle(nsIDOMElement
* aElement
)
557 nsCOMPtr
<dom::Element
> element
= do_QueryInterface(aElement
);
558 return GetComputedStyle(element
);
561 already_AddRefed
<nsComputedDOMStyle
>
562 nsHTMLCSSUtils::GetComputedStyle(dom::Element
* aElement
)
564 MOZ_ASSERT(aElement
);
566 nsIDocument
* doc
= aElement
->GetCurrentDoc();
567 NS_ENSURE_TRUE(doc
, nullptr);
569 nsIPresShell
* presShell
= doc
->GetShell();
570 NS_ENSURE_TRUE(presShell
, nullptr);
572 nsRefPtr
<nsComputedDOMStyle
> style
=
573 NS_NewComputedDOMStyle(aElement
, EmptyString(), presShell
);
575 return style
.forget();
578 // remove the CSS style "aProperty : aPropertyValue" and possibly remove the whole node
579 // if it is a span and if its only attribute is _moz_dirty
581 nsHTMLCSSUtils::RemoveCSSInlineStyle(nsIDOMNode
*aNode
, nsIAtom
*aProperty
, const nsAString
& aPropertyValue
)
583 nsCOMPtr
<Element
> element
= do_QueryInterface(aNode
);
584 NS_ENSURE_STATE(element
);
586 // remove the property from the style attribute
587 nsresult res
= RemoveCSSProperty(*element
, *aProperty
, aPropertyValue
);
588 NS_ENSURE_SUCCESS(res
, res
);
590 if (!element
->IsHTML(nsGkAtoms::span
) ||
591 nsHTMLEditor::HasAttributes(element
)) {
595 return mHTMLEditor
->RemoveContainer(element
);
598 // Answers true is the property can be removed by setting a "none" CSS value
601 nsHTMLCSSUtils::IsCSSInvertable(nsIAtom
*aProperty
, const nsAString
*aAttribute
)
603 return nsGkAtoms::b
== aProperty
;
606 // Get the default browser background color if we need it for GetCSSBackgroundColorState
608 nsHTMLCSSUtils::GetDefaultBackgroundColor(nsAString
& aColor
)
610 if (Preferences::GetBool("editor.use_custom_colors", false)) {
611 nsresult rv
= Preferences::GetString("editor.background_color", &aColor
);
612 // XXX Why don't you validate the pref value?
614 NS_WARNING("failed to get editor.background_color");
615 aColor
.AssignLiteral("#ffffff"); // Default to white
620 if (Preferences::GetBool("browser.display.use_system_colors", false)) {
625 Preferences::GetString("browser.display.background_color", &aColor
);
626 // XXX Why don't you validate the pref value?
628 NS_WARNING("failed to get browser.display.background_color");
629 aColor
.AssignLiteral("#ffffff"); // Default to white
633 // Get the default length unit used for CSS Indent/Outdent
635 nsHTMLCSSUtils::GetDefaultLengthUnit(nsAString
& aLengthUnit
)
638 Preferences::GetString("editor.css.default_length_unit", &aLengthUnit
);
639 // XXX Why don't you validate the pref value?
641 aLengthUnit
.AssignLiteral("px");
645 // Unfortunately, CSSStyleDeclaration::GetPropertyCSSValue is not yet implemented...
646 // We need then a way to determine the number part and the unit from aString, aString
647 // being the result of a GetPropertyValue query...
649 nsHTMLCSSUtils::ParseLength(const nsAString
& aString
, float * aValue
, nsIAtom
** aUnit
)
651 nsAString::const_iterator iter
;
652 aString
.BeginReading(iter
);
654 float a
= 10.0f
, b
= 1.0f
, value
= 0;
656 int32_t i
= 0, j
= aString
.Length();
658 bool floatingPointFound
= false;
660 if (char16_t('-') == c
) { sign
= -1; iter
++; i
++; }
661 else if (char16_t('+') == c
) { iter
++; i
++; }
664 if ((char16_t('0') == c
) ||
665 (char16_t('1') == c
) ||
666 (char16_t('2') == c
) ||
667 (char16_t('3') == c
) ||
668 (char16_t('4') == c
) ||
669 (char16_t('5') == c
) ||
670 (char16_t('6') == c
) ||
671 (char16_t('7') == c
) ||
672 (char16_t('8') == c
) ||
673 (char16_t('9') == c
)) {
674 value
= (value
* a
) + (b
* (c
- char16_t('0')));
677 else if (!floatingPointFound
&& (char16_t('.') == c
)) {
678 floatingPointFound
= true;
685 *aValue
= value
* sign
;
686 *aUnit
= NS_NewAtom(StringTail(aString
, j
-i
)).take();
690 nsHTMLCSSUtils::GetCSSPropertyAtom(nsCSSEditableProperty aProperty
, nsIAtom
** aAtom
)
694 case eCSSEditableProperty_background_color
:
695 *aAtom
= nsGkAtoms::backgroundColor
;
697 case eCSSEditableProperty_background_image
:
698 *aAtom
= nsGkAtoms::background_image
;
700 case eCSSEditableProperty_border
:
701 *aAtom
= nsGkAtoms::border
;
703 case eCSSEditableProperty_caption_side
:
704 *aAtom
= nsGkAtoms::caption_side
;
706 case eCSSEditableProperty_color
:
707 *aAtom
= nsGkAtoms::color
;
709 case eCSSEditableProperty_float
:
710 *aAtom
= nsGkAtoms::_float
;
712 case eCSSEditableProperty_font_family
:
713 *aAtom
= nsGkAtoms::font_family
;
715 case eCSSEditableProperty_font_size
:
716 *aAtom
= nsGkAtoms::font_size
;
718 case eCSSEditableProperty_font_style
:
719 *aAtom
= nsGkAtoms::font_style
;
721 case eCSSEditableProperty_font_weight
:
722 *aAtom
= nsGkAtoms::fontWeight
;
724 case eCSSEditableProperty_height
:
725 *aAtom
= nsGkAtoms::height
;
727 case eCSSEditableProperty_list_style_type
:
728 *aAtom
= nsGkAtoms::list_style_type
;
730 case eCSSEditableProperty_margin_left
:
731 *aAtom
= nsGkAtoms::marginLeft
;
733 case eCSSEditableProperty_margin_right
:
734 *aAtom
= nsGkAtoms::marginRight
;
736 case eCSSEditableProperty_text_align
:
737 *aAtom
= nsGkAtoms::textAlign
;
739 case eCSSEditableProperty_text_decoration
:
740 *aAtom
= nsGkAtoms::text_decoration
;
742 case eCSSEditableProperty_vertical_align
:
743 *aAtom
= nsGkAtoms::vertical_align
;
745 case eCSSEditableProperty_whitespace
:
746 *aAtom
= nsGkAtoms::white_space
;
748 case eCSSEditableProperty_width
:
749 *aAtom
= nsGkAtoms::width
;
751 case eCSSEditableProperty_NONE
:
752 // intentionally empty
757 // Populate aProperty and aValueArray with the CSS declarations equivalent to the
758 // value aValue according to the equivalence table aEquivTable
760 nsHTMLCSSUtils::BuildCSSDeclarations(nsTArray
<nsIAtom
*> & aPropertyArray
,
761 nsTArray
<nsString
> & aValueArray
,
762 const CSSEquivTable
* aEquivTable
,
763 const nsAString
* aValue
,
764 bool aGetOrRemoveRequest
)
767 aPropertyArray
.Clear();
770 // if we have an input value, let's use it
771 nsAutoString value
, lowerCasedValue
;
773 value
.Assign(*aValue
);
774 lowerCasedValue
.Assign(*aValue
);
775 ToLowerCase(lowerCasedValue
);
779 nsCSSEditableProperty cssProperty
= aEquivTable
[index
].cssProperty
;
780 while (cssProperty
) {
781 if (!aGetOrRemoveRequest
|| aEquivTable
[index
].gettable
) {
782 nsAutoString cssValue
, cssPropertyString
;
783 nsIAtom
* cssPropertyAtom
;
784 // find the equivalent css value for the index-th property in
785 // the equivalence table
786 (*aEquivTable
[index
].processValueFunctor
) ((!aGetOrRemoveRequest
|| aEquivTable
[index
].caseSensitiveValue
) ? &value
: &lowerCasedValue
,
788 aEquivTable
[index
].defaultValue
,
789 aEquivTable
[index
].prependValue
,
790 aEquivTable
[index
].appendValue
);
791 GetCSSPropertyAtom(cssProperty
, &cssPropertyAtom
);
792 aPropertyArray
.AppendElement(cssPropertyAtom
);
793 aValueArray
.AppendElement(cssValue
);
796 cssProperty
= aEquivTable
[index
].cssProperty
;
800 // Populate cssPropertyArray and cssValueArray with the declarations equivalent
801 // to aHTMLProperty/aAttribute/aValue for the node aNode
803 nsHTMLCSSUtils::GenerateCSSDeclarationsFromHTMLStyle(dom::Element
* aElement
,
804 nsIAtom
* aHTMLProperty
,
805 const nsAString
* aAttribute
,
806 const nsAString
* aValue
,
807 nsTArray
<nsIAtom
*>& cssPropertyArray
,
808 nsTArray
<nsString
>& cssValueArray
,
809 bool aGetOrRemoveRequest
)
811 MOZ_ASSERT(aElement
);
812 nsIAtom
* tagName
= aElement
->Tag();
813 const nsHTMLCSSUtils::CSSEquivTable
* equivTable
= nullptr;
815 if (nsGkAtoms::b
== aHTMLProperty
) {
816 equivTable
= boldEquivTable
;
817 } else if (nsGkAtoms::i
== aHTMLProperty
) {
818 equivTable
= italicEquivTable
;
819 } else if (nsGkAtoms::u
== aHTMLProperty
) {
820 equivTable
= underlineEquivTable
;
821 } else if (nsGkAtoms::strike
== aHTMLProperty
) {
822 equivTable
= strikeEquivTable
;
823 } else if (nsGkAtoms::tt
== aHTMLProperty
) {
824 equivTable
= ttEquivTable
;
825 } else if (aAttribute
) {
826 if (nsGkAtoms::font
== aHTMLProperty
&&
827 aAttribute
->EqualsLiteral("color")) {
828 equivTable
= fontColorEquivTable
;
829 } else if (nsGkAtoms::font
== aHTMLProperty
&&
830 aAttribute
->EqualsLiteral("face")) {
831 equivTable
= fontFaceEquivTable
;
832 } else if (aAttribute
->EqualsLiteral("bgcolor")) {
833 equivTable
= bgcolorEquivTable
;
834 } else if (aAttribute
->EqualsLiteral("background")) {
835 equivTable
= backgroundImageEquivTable
;
836 } else if (aAttribute
->EqualsLiteral("text")) {
837 equivTable
= textColorEquivTable
;
838 } else if (aAttribute
->EqualsLiteral("border")) {
839 equivTable
= borderEquivTable
;
840 } else if (aAttribute
->EqualsLiteral("align")) {
841 if (nsGkAtoms::table
== tagName
) {
842 equivTable
= tableAlignEquivTable
;
843 } else if (nsGkAtoms::hr
== tagName
) {
844 equivTable
= hrAlignEquivTable
;
845 } else if (nsGkAtoms::legend
== tagName
||
846 nsGkAtoms::caption
== tagName
) {
847 equivTable
= captionAlignEquivTable
;
849 equivTable
= textAlignEquivTable
;
851 } else if (aAttribute
->EqualsLiteral("valign")) {
852 equivTable
= verticalAlignEquivTable
;
853 } else if (aAttribute
->EqualsLiteral("nowrap")) {
854 equivTable
= nowrapEquivTable
;
855 } else if (aAttribute
->EqualsLiteral("width")) {
856 equivTable
= widthEquivTable
;
857 } else if (aAttribute
->EqualsLiteral("height") ||
858 (nsGkAtoms::hr
== tagName
&&
859 aAttribute
->EqualsLiteral("size"))) {
860 equivTable
= heightEquivTable
;
861 } else if (aAttribute
->EqualsLiteral("type") &&
862 (nsGkAtoms::ol
== tagName
||
863 nsGkAtoms::ul
== tagName
||
864 nsGkAtoms::li
== tagName
)) {
865 equivTable
= listStyleTypeEquivTable
;
869 BuildCSSDeclarations(cssPropertyArray
, cssValueArray
, equivTable
,
870 aValue
, aGetOrRemoveRequest
);
874 // Add to aNode the CSS inline style equivalent to HTMLProperty/aAttribute/
875 // aValue for the node, and return in aCount the number of CSS properties set
876 // by the call. The dom::Element version returns aCount instead.
878 nsHTMLCSSUtils::SetCSSEquivalentToHTMLStyle(dom::Element
* aElement
,
880 const nsAString
* aAttribute
,
881 const nsAString
* aValue
,
882 bool aSuppressTransaction
)
884 MOZ_ASSERT(aElement
&& aProperty
);
885 MOZ_ASSERT_IF(aAttribute
, aValue
);
887 // This can only fail if SetCSSProperty fails, which should only happen if
888 // something is pretty badly wrong. In this case we assert so that hopefully
889 // someone will notice, but there's nothing more sensible to do than just
890 // return the count and carry on.
891 nsresult res
= SetCSSEquivalentToHTMLStyle(aElement
->AsDOMNode(),
892 aProperty
, aAttribute
,
894 aSuppressTransaction
);
895 NS_ASSERTION(NS_SUCCEEDED(res
), "SetCSSEquivalentToHTMLStyle failed");
896 NS_ENSURE_SUCCESS(res
, count
);
901 nsHTMLCSSUtils::SetCSSEquivalentToHTMLStyle(nsIDOMNode
* aNode
,
902 nsIAtom
*aHTMLProperty
,
903 const nsAString
*aAttribute
,
904 const nsAString
*aValue
,
906 bool aSuppressTransaction
)
908 nsCOMPtr
<dom::Element
> element
= do_QueryInterface(aNode
);
910 if (!element
|| !IsCSSEditableProperty(element
, aHTMLProperty
, aAttribute
)) {
914 // we can apply the styles only if the node is an element and if we have
915 // an equivalence for the requested HTML style in this implementation
917 // Find the CSS equivalence to the HTML style
918 nsTArray
<nsIAtom
*> cssPropertyArray
;
919 nsTArray
<nsString
> cssValueArray
;
920 GenerateCSSDeclarationsFromHTMLStyle(element
, aHTMLProperty
, aAttribute
,
921 aValue
, cssPropertyArray
, cssValueArray
,
924 // set the individual CSS inline styles
925 *aCount
= cssPropertyArray
.Length();
926 for (int32_t index
= 0; index
< *aCount
; index
++) {
927 nsresult res
= SetCSSProperty(*element
, *cssPropertyArray
[index
],
928 cssValueArray
[index
], aSuppressTransaction
);
929 NS_ENSURE_SUCCESS(res
, res
);
934 // Remove from aNode the CSS inline style equivalent to HTMLProperty/aAttribute/aValue for the node
936 nsHTMLCSSUtils::RemoveCSSEquivalentToHTMLStyle(nsIDOMNode
* aNode
,
937 nsIAtom
*aHTMLProperty
,
938 const nsAString
*aAttribute
,
939 const nsAString
*aValue
,
940 bool aSuppressTransaction
)
942 nsCOMPtr
<dom::Element
> element
= do_QueryInterface(aNode
);
943 NS_ENSURE_TRUE(element
, NS_OK
);
945 return RemoveCSSEquivalentToHTMLStyle(element
, aHTMLProperty
, aAttribute
,
946 aValue
, aSuppressTransaction
);
950 nsHTMLCSSUtils::RemoveCSSEquivalentToHTMLStyle(dom::Element
* aElement
,
951 nsIAtom
* aHTMLProperty
,
952 const nsAString
* aAttribute
,
953 const nsAString
* aValue
,
954 bool aSuppressTransaction
)
956 MOZ_ASSERT(aElement
);
958 if (!IsCSSEditableProperty(aElement
, aHTMLProperty
, aAttribute
)) {
962 // we can apply the styles only if the node is an element and if we have
963 // an equivalence for the requested HTML style in this implementation
965 // Find the CSS equivalence to the HTML style
966 nsTArray
<nsIAtom
*> cssPropertyArray
;
967 nsTArray
<nsString
> cssValueArray
;
968 GenerateCSSDeclarationsFromHTMLStyle(aElement
, aHTMLProperty
, aAttribute
,
969 aValue
, cssPropertyArray
, cssValueArray
,
972 // remove the individual CSS inline styles
973 int32_t count
= cssPropertyArray
.Length();
974 for (int32_t index
= 0; index
< count
; index
++) {
975 nsresult res
= RemoveCSSProperty(*aElement
,
976 *cssPropertyArray
[index
],
977 cssValueArray
[index
],
978 aSuppressTransaction
);
979 NS_ENSURE_SUCCESS(res
, res
);
984 // returns in aValueString the list of values for the CSS equivalences to
985 // the HTML style aHTMLProperty/aAttribute/aValueString for the node aNode;
986 // the value of aStyleType controls the styles we retrieve : specified or
989 nsHTMLCSSUtils::GetCSSEquivalentToHTMLInlineStyleSet(nsINode
* aNode
,
990 nsIAtom
*aHTMLProperty
,
991 const nsAString
*aAttribute
,
992 nsAString
& aValueString
,
993 StyleType aStyleType
)
995 aValueString
.Truncate();
996 nsCOMPtr
<dom::Element
> theElement
= GetElementContainerOrSelf(aNode
);
997 NS_ENSURE_TRUE(theElement
, NS_ERROR_NULL_POINTER
);
999 if (!theElement
|| !IsCSSEditableProperty(theElement
, aHTMLProperty
, aAttribute
)) {
1003 // Yes, the requested HTML style has a CSS equivalence in this implementation
1004 nsTArray
<nsIAtom
*> cssPropertyArray
;
1005 nsTArray
<nsString
> cssValueArray
;
1006 // get the CSS equivalence with last param true indicating we want only the
1007 // "gettable" properties
1008 GenerateCSSDeclarationsFromHTMLStyle(theElement
, aHTMLProperty
, aAttribute
, nullptr,
1009 cssPropertyArray
, cssValueArray
, true);
1010 int32_t count
= cssPropertyArray
.Length();
1011 for (int32_t index
= 0; index
< count
; index
++) {
1012 nsAutoString valueString
;
1013 // retrieve the specified/computed value of the property
1014 nsresult res
= GetCSSInlinePropertyBase(theElement
, cssPropertyArray
[index
],
1015 valueString
, aStyleType
);
1016 NS_ENSURE_SUCCESS(res
, res
);
1017 // append the value to aValueString (possibly with a leading whitespace)
1019 aValueString
.Append(char16_t(' '));
1021 aValueString
.Append(valueString
);
1026 // Does the node aNode (or its parent, if it's not an element node) have a CSS
1027 // style equivalent to the HTML style aHTMLProperty/aHTMLAttribute/valueString?
1028 // The value of aStyleType controls the styles we retrieve: specified or
1029 // computed. The return value aIsSet is true if the CSS styles are set.
1031 // The nsIContent variant returns aIsSet instead of using an out parameter, and
1032 // does not modify aValue.
1034 nsHTMLCSSUtils::IsCSSEquivalentToHTMLInlineStyleSet(nsIContent
* aContent
,
1036 const nsAString
* aAttribute
,
1037 const nsAString
& aValue
,
1038 StyleType aStyleType
)
1040 MOZ_ASSERT(aContent
&& aProperty
);
1042 nsAutoString
value(aValue
);
1043 nsresult res
= IsCSSEquivalentToHTMLInlineStyleSet(aContent
->AsDOMNode(),
1044 aProperty
, aAttribute
,
1045 isSet
, value
, aStyleType
);
1046 NS_ASSERTION(NS_SUCCEEDED(res
), "IsCSSEquivalentToHTMLInlineStyleSet failed");
1047 NS_ENSURE_SUCCESS(res
, false);
1052 nsHTMLCSSUtils::IsCSSEquivalentToHTMLInlineStyleSet(nsIDOMNode
*aNode
,
1053 nsIAtom
*aHTMLProperty
,
1054 const nsAString
*aHTMLAttribute
,
1056 nsAString
& valueString
,
1057 StyleType aStyleType
)
1059 NS_ENSURE_TRUE(aNode
, NS_ERROR_NULL_POINTER
);
1061 nsAutoString
htmlValueString(valueString
);
1063 nsCOMPtr
<nsINode
> node
= do_QueryInterface(aNode
);
1065 valueString
.Assign(htmlValueString
);
1066 // get the value of the CSS equivalent styles
1067 nsresult res
= GetCSSEquivalentToHTMLInlineStyleSet(node
, aHTMLProperty
, aHTMLAttribute
,
1068 valueString
, aStyleType
);
1069 NS_ENSURE_SUCCESS(res
, res
);
1071 // early way out if we can
1072 if (valueString
.IsEmpty()) {
1076 if (nsGkAtoms::b
== aHTMLProperty
) {
1077 if (valueString
.EqualsLiteral("bold")) {
1079 } else if (valueString
.EqualsLiteral("normal")) {
1081 } else if (valueString
.EqualsLiteral("bolder")) {
1083 valueString
.AssignLiteral("bold");
1087 nsAutoString
value(valueString
);
1088 weight
= value
.ToInteger(&errorCode
);
1091 valueString
.AssignLiteral("bold");
1094 valueString
.AssignLiteral("normal");
1097 } else if (nsGkAtoms::i
== aHTMLProperty
) {
1098 if (valueString
.EqualsLiteral("italic") ||
1099 valueString
.EqualsLiteral("oblique")) {
1102 } else if (nsGkAtoms::u
== aHTMLProperty
) {
1104 val
.AssignLiteral("underline");
1105 aIsSet
= ChangeStyleTxn::ValueIncludes(valueString
, val
);
1106 } else if (nsGkAtoms::strike
== aHTMLProperty
) {
1108 val
.AssignLiteral("line-through");
1109 aIsSet
= ChangeStyleTxn::ValueIncludes(valueString
, val
);
1110 } else if (aHTMLAttribute
&&
1111 ((nsGkAtoms::font
== aHTMLProperty
&&
1112 aHTMLAttribute
->EqualsLiteral("color")) ||
1113 aHTMLAttribute
->EqualsLiteral("bgcolor"))) {
1114 if (htmlValueString
.IsEmpty()) {
1118 nsAutoString subStr
;
1119 htmlValueString
.Right(subStr
, htmlValueString
.Length() - 1);
1120 if (NS_ColorNameToRGB(htmlValueString
, &rgba
) ||
1121 NS_HexToRGB(subStr
, &rgba
)) {
1122 nsAutoString htmlColor
, tmpStr
;
1124 if (NS_GET_A(rgba
) != 255) {
1125 // This should only be hit by the "transparent" keyword, which
1126 // currently serializes to "transparent" (not "rgba(0, 0, 0, 0)").
1127 MOZ_ASSERT(NS_GET_R(rgba
) == 0 && NS_GET_G(rgba
) == 0 &&
1128 NS_GET_B(rgba
) == 0 && NS_GET_A(rgba
) == 0);
1129 htmlColor
.AppendLiteral("transparent");
1131 htmlColor
.AppendLiteral("rgb(");
1133 NS_NAMED_LITERAL_STRING(comma
, ", ");
1135 tmpStr
.AppendInt(NS_GET_R(rgba
), 10);
1136 htmlColor
.Append(tmpStr
+ comma
);
1139 tmpStr
.AppendInt(NS_GET_G(rgba
), 10);
1140 htmlColor
.Append(tmpStr
+ comma
);
1143 tmpStr
.AppendInt(NS_GET_B(rgba
), 10);
1144 htmlColor
.Append(tmpStr
);
1146 htmlColor
.Append(char16_t(')'));
1149 aIsSet
= htmlColor
.Equals(valueString
,
1150 nsCaseInsensitiveStringComparator());
1152 aIsSet
= htmlValueString
.Equals(valueString
,
1153 nsCaseInsensitiveStringComparator());
1156 } else if (nsGkAtoms::tt
== aHTMLProperty
) {
1157 aIsSet
= StringBeginsWith(valueString
, NS_LITERAL_STRING("monospace"));
1158 } else if (nsGkAtoms::font
== aHTMLProperty
&& aHTMLAttribute
&&
1159 aHTMLAttribute
->EqualsLiteral("face")) {
1160 if (!htmlValueString
.IsEmpty()) {
1161 const char16_t commaSpace
[] = { char16_t(','), char16_t(' '), 0 };
1162 const char16_t comma
[] = { char16_t(','), 0 };
1163 htmlValueString
.ReplaceSubstring(commaSpace
, comma
);
1164 nsAutoString
valueStringNorm(valueString
);
1165 valueStringNorm
.ReplaceSubstring(commaSpace
, comma
);
1166 aIsSet
= htmlValueString
.Equals(valueStringNorm
,
1167 nsCaseInsensitiveStringComparator());
1169 // ignore this, it's TT or our default
1170 nsAutoString valueStringLower
;
1171 ToLowerCase(valueString
, valueStringLower
);
1172 aIsSet
= !valueStringLower
.EqualsLiteral("monospace") &&
1173 !valueStringLower
.EqualsLiteral("serif");
1176 } else if (aHTMLAttribute
&& aHTMLAttribute
->EqualsLiteral("align")) {
1183 if (!htmlValueString
.IsEmpty() &&
1184 htmlValueString
.Equals(valueString
,
1185 nsCaseInsensitiveStringComparator())) {
1189 if (htmlValueString
.EqualsLiteral("-moz-editor-invert-value")) {
1193 if (nsGkAtoms::u
== aHTMLProperty
|| nsGkAtoms::strike
== aHTMLProperty
) {
1194 // unfortunately, the value of the text-decoration property is not inherited.
1195 // that means that we have to look at ancestors of node to see if they are underlined
1196 node
= node
->GetParentElement(); // set to null if it's not a dom element
1198 } while ((nsGkAtoms::u
== aHTMLProperty
||
1199 nsGkAtoms::strike
== aHTMLProperty
) && !aIsSet
&& node
);
1204 nsHTMLCSSUtils::SetCSSEnabled(bool aIsCSSPrefChecked
)
1206 mIsCSSPrefChecked
= aIsCSSPrefChecked
;
1210 nsHTMLCSSUtils::IsCSSPrefChecked()
1212 return mIsCSSPrefChecked
;
1215 // ElementsSameStyle compares two elements and checks if they have the same
1216 // specified CSS declarations in the STYLE attribute
1217 // The answer is always negative if at least one of them carries an ID or a class
1219 nsHTMLCSSUtils::ElementsSameStyle(nsIDOMNode
*aFirstNode
, nsIDOMNode
*aSecondNode
)
1221 nsCOMPtr
<dom::Element
> firstElement
= do_QueryInterface(aFirstNode
);
1222 nsCOMPtr
<dom::Element
> secondElement
= do_QueryInterface(aSecondNode
);
1224 NS_ASSERTION((firstElement
&& secondElement
), "Non element nodes passed to ElementsSameStyle.");
1225 NS_ENSURE_TRUE(firstElement
, false);
1226 NS_ENSURE_TRUE(secondElement
, false);
1228 return ElementsSameStyle(firstElement
, secondElement
);
1232 nsHTMLCSSUtils::ElementsSameStyle(dom::Element
* aFirstElement
,
1233 dom::Element
* aSecondElement
)
1235 MOZ_ASSERT(aFirstElement
);
1236 MOZ_ASSERT(aSecondElement
);
1238 if (aFirstElement
->HasAttr(kNameSpaceID_None
, nsGkAtoms::id
) ||
1239 aSecondElement
->HasAttr(kNameSpaceID_None
, nsGkAtoms::id
)) {
1240 // at least one of the spans carries an ID ; suspect a CSS rule applies to it and
1241 // refuse to merge the nodes
1245 nsAutoString firstClass
, secondClass
;
1246 bool isFirstClassSet
= aFirstElement
->GetAttr(kNameSpaceID_None
, nsGkAtoms::_class
, firstClass
);
1247 bool isSecondClassSet
= aSecondElement
->GetAttr(kNameSpaceID_None
, nsGkAtoms::_class
, secondClass
);
1248 if (isFirstClassSet
&& isSecondClassSet
) {
1249 // both spans carry a class, let's compare them
1250 if (!firstClass
.Equals(secondClass
)) {
1251 // WARNING : technically, the comparison just above is questionable :
1252 // from a pure HTML/CSS point of view class="a b" is NOT the same than
1253 // class="b a" because a CSS rule could test the exact value of the class
1254 // attribute to be "a b" for instance ; from a user's point of view, a
1255 // wysiwyg editor should probably NOT make any difference. CSS people
1256 // need to discuss this issue before any modification.
1259 } else if (isFirstClassSet
|| isSecondClassSet
) {
1260 // one span only carries a class, early way out
1264 nsCOMPtr
<nsIDOMCSSStyleDeclaration
> firstCSSDecl
, secondCSSDecl
;
1265 uint32_t firstLength
, secondLength
;
1266 nsresult rv
= GetInlineStyles(aFirstElement
, getter_AddRefs(firstCSSDecl
), &firstLength
);
1267 if (NS_FAILED(rv
) || !firstCSSDecl
) {
1270 rv
= GetInlineStyles(aSecondElement
, getter_AddRefs(secondCSSDecl
), &secondLength
);
1271 if (NS_FAILED(rv
) || !secondCSSDecl
) {
1275 if (firstLength
!= secondLength
) {
1276 // early way out if we can
1281 // no inline style !
1285 nsAutoString propertyNameString
;
1286 nsAutoString firstValue
, secondValue
;
1287 for (uint32_t i
= 0; i
< firstLength
; i
++) {
1288 firstCSSDecl
->Item(i
, propertyNameString
);
1289 firstCSSDecl
->GetPropertyValue(propertyNameString
, firstValue
);
1290 secondCSSDecl
->GetPropertyValue(propertyNameString
, secondValue
);
1291 if (!firstValue
.Equals(secondValue
)) {
1295 for (uint32_t i
= 0; i
< secondLength
; i
++) {
1296 secondCSSDecl
->Item(i
, propertyNameString
);
1297 secondCSSDecl
->GetPropertyValue(propertyNameString
, secondValue
);
1298 firstCSSDecl
->GetPropertyValue(propertyNameString
, firstValue
);
1299 if (!firstValue
.Equals(secondValue
)) {
1308 nsHTMLCSSUtils::GetInlineStyles(dom::Element
* aElement
,
1309 nsIDOMCSSStyleDeclaration
** aCssDecl
,
1312 return GetInlineStyles(static_cast<nsISupports
*>(aElement
), aCssDecl
, aLength
);
1316 nsHTMLCSSUtils::GetInlineStyles(nsIDOMElement
* aElement
,
1317 nsIDOMCSSStyleDeclaration
** aCssDecl
,
1320 return GetInlineStyles(static_cast<nsISupports
*>(aElement
), aCssDecl
, aLength
);
1324 nsHTMLCSSUtils::GetInlineStyles(nsISupports
*aElement
,
1325 nsIDOMCSSStyleDeclaration
**aCssDecl
,
1328 NS_ENSURE_TRUE(aElement
&& aLength
, NS_ERROR_NULL_POINTER
);
1330 nsCOMPtr
<nsIDOMElementCSSInlineStyle
> inlineStyles
= do_QueryInterface(aElement
);
1331 NS_ENSURE_TRUE(inlineStyles
, NS_ERROR_NULL_POINTER
);
1333 nsresult res
= inlineStyles
->GetStyle(aCssDecl
);
1334 NS_ENSURE_SUCCESS(res
, NS_ERROR_NULL_POINTER
);
1335 MOZ_ASSERT(*aCssDecl
);
1337 (*aCssDecl
)->GetLength(aLength
);
1341 already_AddRefed
<nsIDOMElement
>
1342 nsHTMLCSSUtils::GetElementContainerOrSelf(nsIDOMNode
* aNode
)
1344 nsCOMPtr
<nsINode
> node
= do_QueryInterface(aNode
);
1345 NS_ENSURE_TRUE(node
, nullptr);
1346 nsCOMPtr
<nsIDOMElement
> element
=
1347 do_QueryInterface(GetElementContainerOrSelf(node
));
1348 return element
.forget();
1352 nsHTMLCSSUtils::GetElementContainerOrSelf(nsINode
* aNode
)
1355 if (nsIDOMNode::DOCUMENT_NODE
== aNode
->NodeType()) {
1359 nsINode
* node
= aNode
;
1360 // Loop until we find an element.
1361 while (node
&& !node
->IsElement()) {
1362 node
= node
->GetParentNode();
1365 NS_ENSURE_TRUE(node
, nullptr);
1366 return node
->AsElement();
1370 nsHTMLCSSUtils::SetCSSProperty(nsIDOMElement
* aElement
,
1371 const nsAString
& aProperty
,
1372 const nsAString
& aValue
)
1374 nsCOMPtr
<nsIDOMCSSStyleDeclaration
> cssDecl
;
1376 nsresult res
= GetInlineStyles(aElement
, getter_AddRefs(cssDecl
), &length
);
1377 if (NS_FAILED(res
) || !cssDecl
) return res
;
1379 return cssDecl
->SetProperty(aProperty
,
1385 nsHTMLCSSUtils::SetCSSPropertyPixels(nsIDOMElement
* aElement
,
1386 const nsAString
& aProperty
,
1390 s
.AppendInt(aIntValue
);
1391 return SetCSSProperty(aElement
, aProperty
, s
+ NS_LITERAL_STRING("px"));