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 "CSSEditUtils.h"
8 #include "ChangeStyleTransaction.h"
9 #include "HTMLEditor.h"
10 #include "HTMLEditUtils.h"
12 #include "mozilla/Assertions.h"
13 #include "mozilla/DeclarationBlock.h"
14 #include "mozilla/mozalloc.h"
15 #include "mozilla/Preferences.h"
16 #include "mozilla/StaticPrefs_editor.h"
17 #include "mozilla/dom/Document.h"
18 #include "mozilla/dom/Element.h"
19 #include "nsAString.h"
21 #include "nsCSSProps.h"
23 #include "nsComputedDOMStyle.h"
25 #include "nsDependentSubstring.h"
27 #include "nsGkAtoms.h"
29 #include "nsIContent.h"
30 #include "nsICSSDeclaration.h"
32 #include "nsISupportsImpl.h"
33 #include "nsISupportsUtils.h"
34 #include "nsLiteralString.h"
35 #include "nsPIDOMWindow.h"
36 #include "nsReadableUtils.h"
38 #include "nsStringFwd.h"
39 #include "nsStringIterator.h"
40 #include "nsStyledElement.h"
41 #include "nsUnicharUtils.h"
47 static void ProcessBValue(const nsAString
* aInputString
,
48 nsAString
& aOutputString
,
49 const char* aDefaultValueString
,
50 const char* aPrependString
,
51 const char* aAppendString
) {
52 if (aInputString
&& aInputString
->EqualsLiteral("-moz-editor-invert-value")) {
53 aOutputString
.AssignLiteral("normal");
55 aOutputString
.AssignLiteral("bold");
59 static void ProcessDefaultValue(const nsAString
* aInputString
,
60 nsAString
& aOutputString
,
61 const char* aDefaultValueString
,
62 const char* aPrependString
,
63 const char* aAppendString
) {
64 CopyASCIItoUTF16(MakeStringSpan(aDefaultValueString
), aOutputString
);
67 static void ProcessSameValue(const nsAString
* aInputString
,
68 nsAString
& aOutputString
,
69 const char* aDefaultValueString
,
70 const char* aPrependString
,
71 const char* aAppendString
) {
73 aOutputString
.Assign(*aInputString
);
75 aOutputString
.Truncate();
78 static void ProcessExtendedValue(const nsAString
* aInputString
,
79 nsAString
& aOutputString
,
80 const char* aDefaultValueString
,
81 const char* aPrependString
,
82 const char* aAppendString
) {
83 aOutputString
.Truncate();
86 AppendASCIItoUTF16(MakeStringSpan(aPrependString
), aOutputString
);
88 aOutputString
.Append(*aInputString
);
90 AppendASCIItoUTF16(MakeStringSpan(aAppendString
), aOutputString
);
95 static void ProcessLengthValue(const nsAString
* aInputString
,
96 nsAString
& aOutputString
,
97 const char* aDefaultValueString
,
98 const char* aPrependString
,
99 const char* aAppendString
) {
100 aOutputString
.Truncate();
102 aOutputString
.Append(*aInputString
);
103 if (-1 == aOutputString
.FindChar(char16_t('%'))) {
104 aOutputString
.AppendLiteral("px");
109 static void ProcessListStyleTypeValue(const nsAString
* aInputString
,
110 nsAString
& aOutputString
,
111 const char* aDefaultValueString
,
112 const char* aPrependString
,
113 const char* aAppendString
) {
114 aOutputString
.Truncate();
116 if (aInputString
->EqualsLiteral("1")) {
117 aOutputString
.AppendLiteral("decimal");
118 } else if (aInputString
->EqualsLiteral("a")) {
119 aOutputString
.AppendLiteral("lower-alpha");
120 } else if (aInputString
->EqualsLiteral("A")) {
121 aOutputString
.AppendLiteral("upper-alpha");
122 } else if (aInputString
->EqualsLiteral("i")) {
123 aOutputString
.AppendLiteral("lower-roman");
124 } else if (aInputString
->EqualsLiteral("I")) {
125 aOutputString
.AppendLiteral("upper-roman");
126 } else if (aInputString
->EqualsLiteral("square") ||
127 aInputString
->EqualsLiteral("circle") ||
128 aInputString
->EqualsLiteral("disc")) {
129 aOutputString
.Append(*aInputString
);
134 static void ProcessMarginLeftValue(const nsAString
* aInputString
,
135 nsAString
& aOutputString
,
136 const char* aDefaultValueString
,
137 const char* aPrependString
,
138 const char* aAppendString
) {
139 aOutputString
.Truncate();
141 if (aInputString
->EqualsLiteral("center") ||
142 aInputString
->EqualsLiteral("-moz-center")) {
143 aOutputString
.AppendLiteral("auto");
144 } else if (aInputString
->EqualsLiteral("right") ||
145 aInputString
->EqualsLiteral("-moz-right")) {
146 aOutputString
.AppendLiteral("auto");
148 aOutputString
.AppendLiteral("0px");
153 static void ProcessMarginRightValue(const nsAString
* aInputString
,
154 nsAString
& aOutputString
,
155 const char* aDefaultValueString
,
156 const char* aPrependString
,
157 const char* aAppendString
) {
158 aOutputString
.Truncate();
160 if (aInputString
->EqualsLiteral("center") ||
161 aInputString
->EqualsLiteral("-moz-center")) {
162 aOutputString
.AppendLiteral("auto");
163 } else if (aInputString
->EqualsLiteral("left") ||
164 aInputString
->EqualsLiteral("-moz-left")) {
165 aOutputString
.AppendLiteral("auto");
167 aOutputString
.AppendLiteral("0px");
172 #define CSS_EQUIV_TABLE_NONE \
173 { CSSEditUtils::eCSSEditableProperty_NONE, 0 }
175 const CSSEditUtils::CSSEquivTable boldEquivTable
[] = {
176 {CSSEditUtils::eCSSEditableProperty_font_weight
, true, false, ProcessBValue
,
177 nullptr, nullptr, nullptr},
178 CSS_EQUIV_TABLE_NONE
};
180 const CSSEditUtils::CSSEquivTable italicEquivTable
[] = {
181 {CSSEditUtils::eCSSEditableProperty_font_style
, true, false,
182 ProcessDefaultValue
, "italic", nullptr, nullptr},
183 CSS_EQUIV_TABLE_NONE
};
185 const CSSEditUtils::CSSEquivTable underlineEquivTable
[] = {
186 {CSSEditUtils::eCSSEditableProperty_text_decoration
, true, false,
187 ProcessDefaultValue
, "underline", nullptr, nullptr},
188 CSS_EQUIV_TABLE_NONE
};
190 const CSSEditUtils::CSSEquivTable strikeEquivTable
[] = {
191 {CSSEditUtils::eCSSEditableProperty_text_decoration
, true, false,
192 ProcessDefaultValue
, "line-through", nullptr, nullptr},
193 CSS_EQUIV_TABLE_NONE
};
195 const CSSEditUtils::CSSEquivTable ttEquivTable
[] = {
196 {CSSEditUtils::eCSSEditableProperty_font_family
, true, false,
197 ProcessDefaultValue
, "monospace", nullptr, nullptr},
198 CSS_EQUIV_TABLE_NONE
};
200 const CSSEditUtils::CSSEquivTable fontColorEquivTable
[] = {
201 {CSSEditUtils::eCSSEditableProperty_color
, true, false, ProcessSameValue
,
202 nullptr, nullptr, nullptr},
203 CSS_EQUIV_TABLE_NONE
};
205 const CSSEditUtils::CSSEquivTable fontFaceEquivTable
[] = {
206 {CSSEditUtils::eCSSEditableProperty_font_family
, true, false,
207 ProcessSameValue
, nullptr, nullptr, nullptr},
208 CSS_EQUIV_TABLE_NONE
};
210 const CSSEditUtils::CSSEquivTable bgcolorEquivTable
[] = {
211 {CSSEditUtils::eCSSEditableProperty_background_color
, true, false,
212 ProcessSameValue
, nullptr, nullptr, nullptr},
213 CSS_EQUIV_TABLE_NONE
};
215 const CSSEditUtils::CSSEquivTable backgroundImageEquivTable
[] = {
216 {CSSEditUtils::eCSSEditableProperty_background_image
, true, true,
217 ProcessExtendedValue
, nullptr, "url(", ")"},
218 CSS_EQUIV_TABLE_NONE
};
220 const CSSEditUtils::CSSEquivTable textColorEquivTable
[] = {
221 {CSSEditUtils::eCSSEditableProperty_color
, true, false, ProcessSameValue
,
222 nullptr, nullptr, nullptr},
223 CSS_EQUIV_TABLE_NONE
};
225 const CSSEditUtils::CSSEquivTable borderEquivTable
[] = {
226 {CSSEditUtils::eCSSEditableProperty_border
, true, false,
227 ProcessExtendedValue
, nullptr, nullptr, "px solid"},
228 CSS_EQUIV_TABLE_NONE
};
230 const CSSEditUtils::CSSEquivTable textAlignEquivTable
[] = {
231 {CSSEditUtils::eCSSEditableProperty_text_align
, true, false,
232 ProcessSameValue
, nullptr, nullptr, nullptr},
233 CSS_EQUIV_TABLE_NONE
};
235 const CSSEditUtils::CSSEquivTable captionAlignEquivTable
[] = {
236 {CSSEditUtils::eCSSEditableProperty_caption_side
, true, false,
237 ProcessSameValue
, nullptr, nullptr, nullptr},
238 CSS_EQUIV_TABLE_NONE
};
240 const CSSEditUtils::CSSEquivTable verticalAlignEquivTable
[] = {
241 {CSSEditUtils::eCSSEditableProperty_vertical_align
, true, false,
242 ProcessSameValue
, nullptr, nullptr, nullptr},
243 CSS_EQUIV_TABLE_NONE
};
245 const CSSEditUtils::CSSEquivTable nowrapEquivTable
[] = {
246 {CSSEditUtils::eCSSEditableProperty_whitespace
, true, false,
247 ProcessDefaultValue
, "nowrap", nullptr, nullptr},
248 CSS_EQUIV_TABLE_NONE
};
250 const CSSEditUtils::CSSEquivTable widthEquivTable
[] = {
251 {CSSEditUtils::eCSSEditableProperty_width
, true, false, ProcessLengthValue
,
252 nullptr, nullptr, nullptr},
253 CSS_EQUIV_TABLE_NONE
};
255 const CSSEditUtils::CSSEquivTable heightEquivTable
[] = {
256 {CSSEditUtils::eCSSEditableProperty_height
, true, false, ProcessLengthValue
,
257 nullptr, nullptr, nullptr},
258 CSS_EQUIV_TABLE_NONE
};
260 const CSSEditUtils::CSSEquivTable listStyleTypeEquivTable
[] = {
261 {CSSEditUtils::eCSSEditableProperty_list_style_type
, true, true,
262 ProcessListStyleTypeValue
, nullptr, nullptr, nullptr},
263 CSS_EQUIV_TABLE_NONE
};
265 const CSSEditUtils::CSSEquivTable tableAlignEquivTable
[] = {
266 {CSSEditUtils::eCSSEditableProperty_text_align
, false, false,
267 ProcessDefaultValue
, "left", nullptr, nullptr},
268 {CSSEditUtils::eCSSEditableProperty_margin_left
, true, false,
269 ProcessMarginLeftValue
, nullptr, nullptr, nullptr},
270 {CSSEditUtils::eCSSEditableProperty_margin_right
, true, false,
271 ProcessMarginRightValue
, nullptr, nullptr, nullptr},
272 CSS_EQUIV_TABLE_NONE
};
274 const CSSEditUtils::CSSEquivTable hrAlignEquivTable
[] = {
275 {CSSEditUtils::eCSSEditableProperty_margin_left
, true, false,
276 ProcessMarginLeftValue
, nullptr, nullptr, nullptr},
277 {CSSEditUtils::eCSSEditableProperty_margin_right
, true, false,
278 ProcessMarginRightValue
, nullptr, nullptr, nullptr},
279 CSS_EQUIV_TABLE_NONE
};
281 #undef CSS_EQUIV_TABLE_NONE
283 CSSEditUtils::CSSEditUtils(HTMLEditor
* aHTMLEditor
)
284 : mHTMLEditor(aHTMLEditor
),
285 mIsCSSPrefChecked(StaticPrefs::editor_use_css()) {}
287 // Answers true if we have some CSS equivalence for the HTML style defined
288 // by aProperty and/or aAttribute for the node aNode
291 bool CSSEditUtils::IsCSSEditableProperty(nsINode
* aNode
, nsAtom
* aProperty
,
292 nsAtom
* aAttribute
) {
295 Element
* element
= aNode
->GetAsElementOrParentElement();
296 if (NS_WARN_IF(!element
)) {
300 // html inline styles B I TT U STRIKE and COLOR/FACE on FONT
301 if (nsGkAtoms::b
== aProperty
|| nsGkAtoms::i
== aProperty
||
302 nsGkAtoms::tt
== aProperty
|| nsGkAtoms::u
== aProperty
||
303 nsGkAtoms::strike
== aProperty
||
304 (nsGkAtoms::font
== aProperty
&& aAttribute
&&
305 (aAttribute
== nsGkAtoms::color
|| aAttribute
== nsGkAtoms::face
))) {
309 // ALIGN attribute on elements supporting it
310 if (aAttribute
== nsGkAtoms::align
&&
311 element
->IsAnyOfHTMLElements(
312 nsGkAtoms::div
, nsGkAtoms::p
, nsGkAtoms::h1
, nsGkAtoms::h2
,
313 nsGkAtoms::h3
, nsGkAtoms::h4
, nsGkAtoms::h5
, nsGkAtoms::h6
,
314 nsGkAtoms::td
, nsGkAtoms::th
, nsGkAtoms::table
, nsGkAtoms::hr
,
315 // For the above, why not use
316 // HTMLEditUtils::SupportsAlignAttr?
317 // It also checks for tbody, tfoot, thead.
318 // Let's add the following elements here even
319 // if "align" has a different meaning for them
320 nsGkAtoms::legend
, nsGkAtoms::caption
)) {
324 if (aAttribute
== nsGkAtoms::valign
&&
325 element
->IsAnyOfHTMLElements(
326 nsGkAtoms::col
, nsGkAtoms::colgroup
, nsGkAtoms::tbody
, nsGkAtoms::td
,
327 nsGkAtoms::th
, nsGkAtoms::tfoot
, nsGkAtoms::thead
, nsGkAtoms::tr
)) {
331 // attributes TEXT, BACKGROUND and BGCOLOR on BODY
332 if (element
->IsHTMLElement(nsGkAtoms::body
) &&
333 (aAttribute
== nsGkAtoms::text
|| aAttribute
== nsGkAtoms::background
||
334 aAttribute
== nsGkAtoms::bgcolor
)) {
338 // attribute BGCOLOR on other elements
339 if (aAttribute
== nsGkAtoms::bgcolor
) {
343 // attributes HEIGHT, WIDTH and NOWRAP on TD and TH
344 if (element
->IsAnyOfHTMLElements(nsGkAtoms::td
, nsGkAtoms::th
) &&
345 (aAttribute
== nsGkAtoms::height
|| aAttribute
== nsGkAtoms::width
||
346 aAttribute
== nsGkAtoms::nowrap
)) {
350 // attributes HEIGHT and WIDTH on TABLE
351 if (element
->IsHTMLElement(nsGkAtoms::table
) &&
352 (aAttribute
== nsGkAtoms::height
|| aAttribute
== nsGkAtoms::width
)) {
356 // attributes SIZE and WIDTH on HR
357 if (element
->IsHTMLElement(nsGkAtoms::hr
) &&
358 (aAttribute
== nsGkAtoms::size
|| aAttribute
== nsGkAtoms::width
)) {
362 // attribute TYPE on OL UL LI
363 if (element
->IsAnyOfHTMLElements(nsGkAtoms::ol
, nsGkAtoms::ul
,
365 aAttribute
== nsGkAtoms::type
) {
369 if (element
->IsHTMLElement(nsGkAtoms::img
) &&
370 (aAttribute
== nsGkAtoms::border
|| aAttribute
== nsGkAtoms::width
||
371 aAttribute
== nsGkAtoms::height
)) {
375 // other elements that we can align using CSS even if they
376 // can't carry the html ALIGN attribute
377 if (aAttribute
== nsGkAtoms::align
&&
378 element
->IsAnyOfHTMLElements(nsGkAtoms::ul
, nsGkAtoms::ol
, nsGkAtoms::dl
,
379 nsGkAtoms::li
, nsGkAtoms::dd
, nsGkAtoms::dt
,
380 nsGkAtoms::address
, nsGkAtoms::pre
)) {
387 // The lowest level above the transaction; adds the CSS declaration
388 // "aProperty : aValue" to the inline styles carried by aStyledElement
389 nsresult
CSSEditUtils::SetCSSPropertyInternal(nsStyledElement
& aStyledElement
,
391 const nsAString
& aValue
,
393 RefPtr
<ChangeStyleTransaction
> transaction
=
394 ChangeStyleTransaction::Create(aStyledElement
, aProperty
, aValue
);
396 nsresult rv
= transaction
->DoTransaction();
397 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv
),
398 "ChangeStyleTransaction::DoTransaction() failed");
401 if (NS_WARN_IF(!mHTMLEditor
)) {
402 return NS_ERROR_NOT_AVAILABLE
;
404 RefPtr
<HTMLEditor
> htmlEditor(mHTMLEditor
);
405 nsresult rv
= htmlEditor
->DoTransactionInternal(transaction
);
406 if (NS_WARN_IF(htmlEditor
->Destroyed())) {
407 return NS_ERROR_EDITOR_DESTROYED
;
409 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv
),
410 "EditorBase::DoTransactionInternal() failed");
414 nsresult
CSSEditUtils::SetCSSPropertyPixelsWithTransaction(
415 nsStyledElement
& aStyledElement
, nsAtom
& aProperty
, int32_t aIntValue
) {
417 s
.AppendInt(aIntValue
);
419 SetCSSPropertyWithTransaction(aStyledElement
, aProperty
, s
+ u
"px"_ns
);
420 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv
),
421 "CSSEditUtils::SetCSSPropertyWithTransaction() failed");
425 nsresult
CSSEditUtils::SetCSSPropertyPixelsWithoutTransaction(
426 nsStyledElement
& aStyledElement
, const nsAtom
& aProperty
,
428 nsCOMPtr
<nsICSSDeclaration
> cssDecl
= aStyledElement
.Style();
430 nsAutoCString propertyNameString
;
431 aProperty
.ToUTF8String(propertyNameString
);
434 s
.AppendInt(aIntValue
);
435 s
.AppendLiteral("px");
438 cssDecl
->SetProperty(propertyNameString
, s
, EmptyCString(), error
);
439 if (error
.Failed()) {
440 NS_WARNING("nsICSSDeclaration::SetProperty() failed");
441 return error
.StealNSResult();
447 // The lowest level above the transaction; removes the value aValue from the
448 // list of values specified for the CSS property aProperty, or totally remove
449 // the declaration if this property accepts only one value
450 nsresult
CSSEditUtils::RemoveCSSPropertyInternal(
451 nsStyledElement
& aStyledElement
, nsAtom
& aProperty
, const nsAString
& aValue
,
453 RefPtr
<ChangeStyleTransaction
> transaction
=
454 ChangeStyleTransaction::CreateToRemove(aStyledElement
, aProperty
, aValue
);
456 nsresult rv
= transaction
->DoTransaction();
457 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv
),
458 "ChangeStyleTransaction::DoTransaction() failed");
461 if (NS_WARN_IF(!mHTMLEditor
)) {
462 return NS_ERROR_NOT_AVAILABLE
;
464 RefPtr
<HTMLEditor
> htmlEditor(mHTMLEditor
);
465 nsresult rv
= htmlEditor
->DoTransactionInternal(transaction
);
466 if (NS_WARN_IF(htmlEditor
->Destroyed())) {
467 return NS_ERROR_EDITOR_DESTROYED
;
469 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv
),
470 "EditorBase::DoTransactionInternal() failed");
475 nsresult
CSSEditUtils::GetSpecifiedProperty(nsIContent
& aContent
,
476 nsAtom
& aCSSProperty
,
479 GetSpecifiedCSSInlinePropertyBase(aContent
, aCSSProperty
, aValue
);
480 NS_WARNING_ASSERTION(
482 "CSSEditUtils::GeSpecifiedCSSInlinePropertyBase() failed");
487 nsresult
CSSEditUtils::GetComputedProperty(nsIContent
& aContent
,
488 nsAtom
& aCSSProperty
,
491 GetComputedCSSInlinePropertyBase(aContent
, aCSSProperty
, aValue
);
492 NS_WARNING_ASSERTION(
494 "CSSEditUtils::GetComputedCSSInlinePropertyBase() failed");
499 nsresult
CSSEditUtils::GetComputedCSSInlinePropertyBase(nsIContent
& aContent
,
500 nsAtom
& aCSSProperty
,
504 RefPtr
<Element
> element
= aContent
.GetAsElementOrParentElement();
505 if (NS_WARN_IF(!element
)) {
506 return NS_ERROR_INVALID_ARG
;
509 // Get the all the computed css styles attached to the element node
510 RefPtr
<nsComputedDOMStyle
> computedDOMStyle
= GetComputedStyle(element
);
511 if (NS_WARN_IF(!computedDOMStyle
)) {
512 return NS_ERROR_INVALID_ARG
;
515 // from these declarations, get the one we want and that one only
517 // FIXME(bug 1606994): nsAtomCString copies, we should just keep around the
520 // FIXME: Maybe we can avoid copying aValue too, though it's no worse than
521 // what we used to do.
524 computedDOMStyle
->GetPropertyValue(nsAtomCString(&aCSSProperty
), value
));
525 CopyUTF8toUTF16(value
, aValue
);
530 nsresult
CSSEditUtils::GetSpecifiedCSSInlinePropertyBase(nsIContent
& aContent
,
531 nsAtom
& aCSSProperty
,
535 RefPtr
<Element
> element
= aContent
.GetAsElementOrParentElement();
536 if (NS_WARN_IF(!element
)) {
537 return NS_ERROR_INVALID_ARG
;
540 RefPtr
<DeclarationBlock
> decl
= element
->GetInlineStyleDeclaration();
545 // FIXME: Same comments as above.
546 nsCSSPropertyID prop
=
547 nsCSSProps::LookupProperty(nsAtomCString(&aCSSProperty
));
548 MOZ_ASSERT(prop
!= eCSSProperty_UNKNOWN
);
551 decl
->GetPropertyValueByID(prop
, value
);
552 CopyUTF8toUTF16(value
, aValue
);
557 already_AddRefed
<nsComputedDOMStyle
> CSSEditUtils::GetComputedStyle(
559 MOZ_ASSERT(aElement
);
561 Document
* document
= aElement
->GetComposedDoc();
562 if (NS_WARN_IF(!document
)) {
566 RefPtr
<nsComputedDOMStyle
> computedDOMStyle
= NS_NewComputedDOMStyle(
567 aElement
, u
""_ns
, document
, nsComputedDOMStyle::StyleType::All
,
569 return computedDOMStyle
.forget();
572 // remove the CSS style "aProperty : aPropertyValue" and possibly remove the
573 // whole node if it is a span and if its only attribute is _moz_dirty
574 nsresult
CSSEditUtils::RemoveCSSInlineStyleWithTransaction(
575 nsStyledElement
& aStyledElement
, nsAtom
* aProperty
,
576 const nsAString
& aPropertyValue
) {
577 // remove the property from the style attribute
578 nsresult rv
= RemoveCSSPropertyWithTransaction(aStyledElement
, *aProperty
,
581 NS_WARNING("CSSEditUtils::RemoveCSSPropertyWithTransaction() failed");
585 if (!aStyledElement
.IsHTMLElement(nsGkAtoms::span
) ||
586 HTMLEditor::HasAttributes(&aStyledElement
)) {
590 OwningNonNull
<HTMLEditor
> htmlEditor(*mHTMLEditor
);
591 const Result
<EditorDOMPoint
, nsresult
> unwrapStyledElementResult
=
592 htmlEditor
->RemoveContainerWithTransaction(aStyledElement
);
593 if (MOZ_UNLIKELY(unwrapStyledElementResult
.isErr())) {
594 NS_WARNING("HTMLEditor::RemoveContainerWithTransaction() failed");
595 return unwrapStyledElementResult
.inspectErr();
597 const EditorDOMPoint
& pointToPutCaret
= unwrapStyledElementResult
.inspect();
598 if (!htmlEditor
->AllowsTransactionsToChangeSelection() ||
599 !pointToPutCaret
.IsSet()) {
602 rv
= htmlEditor
->CollapseSelectionTo(pointToPutCaret
);
603 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv
),
604 "EditorBase::CollapseSelectionTo() failed");
608 // Answers true if the property can be removed by setting a "none" CSS value
612 bool CSSEditUtils::IsCSSInvertible(nsAtom
& aProperty
, nsAtom
* aAttribute
) {
613 return nsGkAtoms::b
== &aProperty
;
616 // Get the default browser background color if we need it for
617 // GetCSSBackgroundColorState
620 void CSSEditUtils::GetDefaultBackgroundColor(nsAString
& aColor
) {
621 if (MOZ_UNLIKELY(StaticPrefs::editor_use_custom_colors())) {
622 nsresult rv
= Preferences::GetString("editor.background_color", aColor
);
623 // XXX Why don't you validate the pref value?
625 NS_WARNING("failed to get editor.background_color");
626 aColor
.AssignLiteral("#ffffff"); // Default to white
631 if (Preferences::GetBool("browser.display.use_system_colors", false)) {
636 Preferences::GetString("browser.display.background_color", aColor
);
637 // XXX Why don't you validate the pref value?
639 NS_WARNING("failed to get browser.display.background_color");
640 aColor
.AssignLiteral("#ffffff"); // Default to white
644 // Get the default length unit used for CSS Indent/Outdent
647 void CSSEditUtils::GetDefaultLengthUnit(nsAString
& aLengthUnit
) {
648 // XXX Why don't you validate the pref value?
649 if (MOZ_UNLIKELY(NS_FAILED(Preferences::GetString(
650 "editor.css.default_length_unit", aLengthUnit
)))) {
651 aLengthUnit
.AssignLiteral("px");
656 void CSSEditUtils::ParseLength(const nsAString
& aString
, float* aValue
,
658 if (aString
.IsEmpty()) {
660 *aUnit
= NS_Atomize(aString
).take();
664 nsAString::const_iterator iter
;
665 aString
.BeginReading(iter
);
667 float a
= 10.0f
, b
= 1.0f
, value
= 0;
669 int32_t i
= 0, j
= aString
.Length();
671 bool floatingPointFound
= false;
673 if (char16_t('-') == c
) {
677 } else if (char16_t('+') == c
) {
683 if ((char16_t('0') == c
) || (char16_t('1') == c
) || (char16_t('2') == c
) ||
684 (char16_t('3') == c
) || (char16_t('4') == c
) || (char16_t('5') == c
) ||
685 (char16_t('6') == c
) || (char16_t('7') == c
) || (char16_t('8') == c
) ||
686 (char16_t('9') == c
)) {
687 value
= (value
* a
) + (b
* (c
- char16_t('0')));
689 } else if (!floatingPointFound
&& (char16_t('.') == c
)) {
690 floatingPointFound
= true;
698 *aValue
= value
* sign
;
699 *aUnit
= NS_Atomize(StringTail(aString
, j
- i
)).take();
703 nsStaticAtom
* CSSEditUtils::GetCSSPropertyAtom(
704 nsCSSEditableProperty aProperty
) {
706 case eCSSEditableProperty_background_color
:
707 return nsGkAtoms::backgroundColor
;
708 case eCSSEditableProperty_background_image
:
709 return nsGkAtoms::background_image
;
710 case eCSSEditableProperty_border
:
711 return nsGkAtoms::border
;
712 case eCSSEditableProperty_caption_side
:
713 return nsGkAtoms::caption_side
;
714 case eCSSEditableProperty_color
:
715 return nsGkAtoms::color
;
716 case eCSSEditableProperty_float
:
717 return nsGkAtoms::_float
;
718 case eCSSEditableProperty_font_family
:
719 return nsGkAtoms::font_family
;
720 case eCSSEditableProperty_font_size
:
721 return nsGkAtoms::font_size
;
722 case eCSSEditableProperty_font_style
:
723 return nsGkAtoms::font_style
;
724 case eCSSEditableProperty_font_weight
:
725 return nsGkAtoms::fontWeight
;
726 case eCSSEditableProperty_height
:
727 return nsGkAtoms::height
;
728 case eCSSEditableProperty_list_style_type
:
729 return nsGkAtoms::list_style_type
;
730 case eCSSEditableProperty_margin_left
:
731 return nsGkAtoms::marginLeft
;
732 case eCSSEditableProperty_margin_right
:
733 return nsGkAtoms::marginRight
;
734 case eCSSEditableProperty_text_align
:
735 return nsGkAtoms::textAlign
;
736 case eCSSEditableProperty_text_decoration
:
737 return nsGkAtoms::text_decoration
;
738 case eCSSEditableProperty_vertical_align
:
739 return nsGkAtoms::vertical_align
;
740 case eCSSEditableProperty_whitespace
:
741 return nsGkAtoms::white_space
;
742 case eCSSEditableProperty_width
:
743 return nsGkAtoms::width
;
744 case eCSSEditableProperty_NONE
:
745 // intentionally empty
748 MOZ_ASSERT_UNREACHABLE("Got unknown property");
752 // Populate aOutArrayOfCSSProperty and aOutArrayOfCSSValue with the CSS
753 // declarations equivalent to the value aValue according to the equivalence
757 void CSSEditUtils::BuildCSSDeclarations(
758 nsTArray
<nsStaticAtom
*>& aOutArrayOfCSSProperty
,
759 nsTArray
<nsString
>& aOutArrayOfCSSValue
, const CSSEquivTable
* aEquivTable
,
760 const nsAString
* aValue
, bool aGetOrRemoveRequest
) {
762 aOutArrayOfCSSProperty
.Clear();
763 aOutArrayOfCSSValue
.Clear();
765 // if we have an input value, let's use it
766 nsAutoString value
, lowerCasedValue
;
768 value
.Assign(*aValue
);
769 lowerCasedValue
.Assign(*aValue
);
770 ToLowerCase(lowerCasedValue
);
774 nsCSSEditableProperty cssProperty
= aEquivTable
[index
].cssProperty
;
775 while (cssProperty
) {
776 if (!aGetOrRemoveRequest
|| aEquivTable
[index
].gettable
) {
777 nsAutoString cssValue
, cssPropertyString
;
778 // find the equivalent css value for the index-th property in
779 // the equivalence table
780 (*aEquivTable
[index
].processValueFunctor
)(
781 (!aGetOrRemoveRequest
|| aEquivTable
[index
].caseSensitiveValue
)
784 cssValue
, aEquivTable
[index
].defaultValue
,
785 aEquivTable
[index
].prependValue
, aEquivTable
[index
].appendValue
);
786 aOutArrayOfCSSProperty
.AppendElement(GetCSSPropertyAtom(cssProperty
));
787 aOutArrayOfCSSValue
.AppendElement(cssValue
);
790 cssProperty
= aEquivTable
[index
].cssProperty
;
794 // Populate aOutArrayOfCSSProperty and aOutArrayOfCSSValue with the declarations
795 // equivalent to aHTMLProperty/aAttribute/aValue for the node aNode
798 void CSSEditUtils::GenerateCSSDeclarationsFromHTMLStyle(
799 Element
& aElement
, nsAtom
* aHTMLProperty
, nsAtom
* aAttribute
,
800 const nsAString
* aValue
, nsTArray
<nsStaticAtom
*>& aOutArrayOfCSSProperty
,
801 nsTArray
<nsString
>& aOutArrayOfCSSValue
, bool aGetOrRemoveRequest
) {
802 const CSSEditUtils::CSSEquivTable
* equivTable
= nullptr;
804 if (nsGkAtoms::b
== aHTMLProperty
) {
805 equivTable
= boldEquivTable
;
806 } else if (nsGkAtoms::i
== aHTMLProperty
) {
807 equivTable
= italicEquivTable
;
808 } else if (nsGkAtoms::u
== aHTMLProperty
) {
809 equivTable
= underlineEquivTable
;
810 } else if (nsGkAtoms::strike
== aHTMLProperty
) {
811 equivTable
= strikeEquivTable
;
812 } else if (nsGkAtoms::tt
== aHTMLProperty
) {
813 equivTable
= ttEquivTable
;
814 } else if (aAttribute
) {
815 if (nsGkAtoms::font
== aHTMLProperty
&& aAttribute
== nsGkAtoms::color
) {
816 equivTable
= fontColorEquivTable
;
817 } else if (nsGkAtoms::font
== aHTMLProperty
&&
818 aAttribute
== nsGkAtoms::face
) {
819 equivTable
= fontFaceEquivTable
;
820 } else if (aAttribute
== nsGkAtoms::bgcolor
) {
821 equivTable
= bgcolorEquivTable
;
822 } else if (aAttribute
== nsGkAtoms::background
) {
823 equivTable
= backgroundImageEquivTable
;
824 } else if (aAttribute
== nsGkAtoms::text
) {
825 equivTable
= textColorEquivTable
;
826 } else if (aAttribute
== nsGkAtoms::border
) {
827 equivTable
= borderEquivTable
;
828 } else if (aAttribute
== nsGkAtoms::align
) {
829 if (aElement
.IsHTMLElement(nsGkAtoms::table
)) {
830 equivTable
= tableAlignEquivTable
;
831 } else if (aElement
.IsHTMLElement(nsGkAtoms::hr
)) {
832 equivTable
= hrAlignEquivTable
;
833 } else if (aElement
.IsAnyOfHTMLElements(nsGkAtoms::legend
,
834 nsGkAtoms::caption
)) {
835 equivTable
= captionAlignEquivTable
;
837 equivTable
= textAlignEquivTable
;
839 } else if (aAttribute
== nsGkAtoms::valign
) {
840 equivTable
= verticalAlignEquivTable
;
841 } else if (aAttribute
== nsGkAtoms::nowrap
) {
842 equivTable
= nowrapEquivTable
;
843 } else if (aAttribute
== nsGkAtoms::width
) {
844 equivTable
= widthEquivTable
;
845 } else if (aAttribute
== nsGkAtoms::height
||
846 (aElement
.IsHTMLElement(nsGkAtoms::hr
) &&
847 aAttribute
== nsGkAtoms::size
)) {
848 equivTable
= heightEquivTable
;
849 } else if (aAttribute
== nsGkAtoms::type
&&
850 aElement
.IsAnyOfHTMLElements(nsGkAtoms::ol
, nsGkAtoms::ul
,
852 equivTable
= listStyleTypeEquivTable
;
856 BuildCSSDeclarations(aOutArrayOfCSSProperty
, aOutArrayOfCSSValue
,
857 equivTable
, aValue
, aGetOrRemoveRequest
);
861 // Add to aNode the CSS inline style equivalent to HTMLProperty/aAttribute/
862 // aValue for the node, and return in aCount the number of CSS properties set
863 // by the call. The Element version returns aCount instead.
864 Result
<int32_t, nsresult
> CSSEditUtils::SetCSSEquivalentToHTMLStyleInternal(
865 nsStyledElement
& aStyledElement
, nsAtom
* aHTMLProperty
, nsAtom
* aAttribute
,
866 const nsAString
* aValue
, bool aSuppressTransaction
) {
867 if (!IsCSSEditableProperty(&aStyledElement
, aHTMLProperty
, aAttribute
)) {
871 // we can apply the styles only if the node is an element and if we have
872 // an equivalence for the requested HTML style in this implementation
874 // Find the CSS equivalence to the HTML style
875 nsTArray
<nsStaticAtom
*> cssPropertyArray
;
876 nsTArray
<nsString
> cssValueArray
;
877 GenerateCSSDeclarationsFromHTMLStyle(aStyledElement
, aHTMLProperty
,
878 aAttribute
, aValue
, cssPropertyArray
,
879 cssValueArray
, false);
881 // set the individual CSS inline styles
882 const size_t count
= cssPropertyArray
.Length();
883 for (size_t index
= 0; index
< count
; index
++) {
884 nsresult rv
= SetCSSPropertyInternal(
885 aStyledElement
, MOZ_KnownLive(*cssPropertyArray
[index
]),
886 cssValueArray
[index
], aSuppressTransaction
);
888 NS_WARNING("CSSEditUtils::SetCSSPropertyInternal() failed");
895 // Remove from aNode the CSS inline style equivalent to
896 // HTMLProperty/aAttribute/aValue for the node
897 nsresult
CSSEditUtils::RemoveCSSEquivalentToHTMLStyleInternal(
898 nsStyledElement
& aStyledElement
, nsAtom
* aHTMLProperty
, nsAtom
* aAttribute
,
899 const nsAString
* aValue
, bool aSuppressTransaction
) {
900 if (!IsCSSEditableProperty(&aStyledElement
, aHTMLProperty
, aAttribute
)) {
904 // we can apply the styles only if the node is an element and if we have
905 // an equivalence for the requested HTML style in this implementation
907 // Find the CSS equivalence to the HTML style
908 nsTArray
<nsStaticAtom
*> cssPropertyArray
;
909 nsTArray
<nsString
> cssValueArray
;
910 GenerateCSSDeclarationsFromHTMLStyle(aStyledElement
, aHTMLProperty
,
911 aAttribute
, aValue
, cssPropertyArray
,
912 cssValueArray
, true);
914 // remove the individual CSS inline styles
915 const size_t count
= cssPropertyArray
.Length();
919 for (size_t index
= 0; index
< count
; index
++) {
920 nsresult rv
= RemoveCSSPropertyInternal(
921 aStyledElement
, MOZ_KnownLive(*cssPropertyArray
[index
]),
922 cssValueArray
[index
], aSuppressTransaction
);
924 NS_WARNING("CSSEditUtils::RemoveCSSPropertyWithoutTransaction() failed");
931 // returns in aValue the list of values for the CSS equivalences to
932 // the HTML style aHTMLProperty/aAttribute/aValue for the node aNode;
933 // the value of aStyleType controls the styles we retrieve : specified or
937 nsresult
CSSEditUtils::GetCSSEquivalentToHTMLInlineStyleSetInternal(
938 nsIContent
& aContent
, nsAtom
* aHTMLProperty
, nsAtom
* aAttribute
,
939 nsAString
& aValue
, StyleType aStyleType
) {
940 MOZ_ASSERT(aHTMLProperty
|| aAttribute
);
943 RefPtr
<Element
> theElement
= aContent
.GetAsElementOrParentElement();
944 if (NS_WARN_IF(!theElement
)) {
945 return NS_ERROR_INVALID_ARG
;
949 !IsCSSEditableProperty(theElement
, aHTMLProperty
, aAttribute
)) {
953 // Yes, the requested HTML style has a CSS equivalence in this implementation
954 nsTArray
<nsStaticAtom
*> cssPropertyArray
;
955 nsTArray
<nsString
> cssValueArray
;
956 // get the CSS equivalence with last param true indicating we want only the
957 // "gettable" properties
958 GenerateCSSDeclarationsFromHTMLStyle(*theElement
, aHTMLProperty
, aAttribute
,
959 nullptr, cssPropertyArray
, cssValueArray
,
961 int32_t count
= cssPropertyArray
.Length();
962 for (int32_t index
= 0; index
< count
; index
++) {
963 nsAutoString valueString
;
964 // retrieve the specified/computed value of the property
965 if (aStyleType
== StyleType::Computed
) {
966 nsresult rv
= GetComputedCSSInlinePropertyBase(
967 *theElement
, MOZ_KnownLive(*cssPropertyArray
[index
]), valueString
);
969 NS_WARNING("CSSEditUtils::GetComputedCSSInlinePropertyBase() failed");
973 nsresult rv
= GetSpecifiedCSSInlinePropertyBase(
974 *theElement
, *cssPropertyArray
[index
], valueString
);
976 NS_WARNING("CSSEditUtils::GetSpecifiedCSSInlinePropertyBase() failed");
980 // append the value to aValue (possibly with a leading white-space)
982 aValue
.Append(HTMLEditUtils::kSpace
);
984 aValue
.Append(valueString
);
989 // Does the node aContent (or its parent, if it's not an element node) have a
990 // CSS style equivalent to the HTML style
991 // aHTMLProperty/aAttribute/valueString? The value of aStyleType controls
992 // the styles we retrieve: specified or computed. The return value aIsSet is
993 // true if the CSS styles are set.
995 // The nsIContent variant returns aIsSet instead of using an out parameter, and
996 // does not modify aValue.
998 Result
<bool, nsresult
>
999 CSSEditUtils::IsCSSEquivalentToHTMLInlineStyleSetInternal(
1000 nsIContent
& aContent
, nsAtom
* aHTMLProperty
, nsAtom
* aAttribute
,
1001 nsAString
& aValue
, StyleType aStyleType
) {
1002 MOZ_ASSERT(aHTMLProperty
|| aAttribute
);
1004 nsAutoString
htmlValueString(aValue
);
1006 // FYI: Cannot use InclusiveAncestorsOfType here because
1007 // GetCSSEquivalentToHTMLInlineStyleSetInternal() may flush pending
1009 for (nsCOMPtr
<nsIContent
> content
= &aContent
; content
;
1010 content
= content
->GetParentElement()) {
1011 nsCOMPtr
<nsINode
> parentNode
= content
->GetParentNode();
1012 aValue
.Assign(htmlValueString
);
1013 // get the value of the CSS equivalent styles
1014 nsresult rv
= GetCSSEquivalentToHTMLInlineStyleSetInternal(
1015 *content
, aHTMLProperty
, aAttribute
, aValue
, aStyleType
);
1016 if (NS_WARN_IF(!mHTMLEditor
|| mHTMLEditor
->Destroyed())) {
1017 return Err(NS_ERROR_EDITOR_DESTROYED
);
1019 if (NS_FAILED(rv
)) {
1021 "CSSEditUtils::GetCSSEquivalentToHTMLInlineStyleSetInternal() "
1025 if (NS_WARN_IF(parentNode
!= content
->GetParentNode())) {
1026 return Err(NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE
);
1029 // early way out if we can
1030 if (aValue
.IsEmpty()) {
1034 if (nsGkAtoms::b
== aHTMLProperty
) {
1035 if (aValue
.EqualsLiteral("bold")) {
1037 } else if (aValue
.EqualsLiteral("normal")) {
1039 } else if (aValue
.EqualsLiteral("bolder")) {
1041 aValue
.AssignLiteral("bold");
1045 nsAutoString
value(aValue
);
1046 weight
= value
.ToInteger(&rvIgnored
);
1047 NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored
),
1048 "nsAString::ToInteger() failed, but ignored");
1051 aValue
.AssignLiteral("bold");
1054 aValue
.AssignLiteral("normal");
1057 } else if (nsGkAtoms::i
== aHTMLProperty
) {
1058 if (aValue
.EqualsLiteral("italic") || aValue
.EqualsLiteral("oblique")) {
1061 } else if (nsGkAtoms::u
== aHTMLProperty
) {
1062 isSet
= ChangeStyleTransaction::ValueIncludes(
1063 NS_ConvertUTF16toUTF8(aValue
), "underline"_ns
);
1064 } else if (nsGkAtoms::strike
== aHTMLProperty
) {
1065 isSet
= ChangeStyleTransaction::ValueIncludes(
1066 NS_ConvertUTF16toUTF8(aValue
), "line-through"_ns
);
1067 } else if ((nsGkAtoms::font
== aHTMLProperty
&&
1068 aAttribute
== nsGkAtoms::color
) ||
1069 aAttribute
== nsGkAtoms::bgcolor
) {
1070 if (htmlValueString
.IsEmpty()) {
1074 nsAutoString subStr
;
1075 htmlValueString
.Right(subStr
, htmlValueString
.Length() - 1);
1076 if (NS_ColorNameToRGB(htmlValueString
, &rgba
) ||
1077 NS_HexToRGBA(subStr
, nsHexColorType::NoAlpha
, &rgba
)) {
1078 nsAutoString htmlColor
, tmpStr
;
1080 if (NS_GET_A(rgba
) != 255) {
1081 // This should only be hit by the "transparent" keyword, which
1082 // currently serializes to "transparent" (not "rgba(0, 0, 0, 0)").
1083 MOZ_ASSERT(NS_GET_R(rgba
) == 0 && NS_GET_G(rgba
) == 0 &&
1084 NS_GET_B(rgba
) == 0 && NS_GET_A(rgba
) == 0);
1085 htmlColor
.AppendLiteral("transparent");
1087 htmlColor
.AppendLiteral("rgb(");
1089 constexpr auto comma
= u
", "_ns
;
1091 tmpStr
.AppendInt(NS_GET_R(rgba
), 10);
1092 htmlColor
.Append(tmpStr
+ comma
);
1095 tmpStr
.AppendInt(NS_GET_G(rgba
), 10);
1096 htmlColor
.Append(tmpStr
+ comma
);
1099 tmpStr
.AppendInt(NS_GET_B(rgba
), 10);
1100 htmlColor
.Append(tmpStr
);
1102 htmlColor
.Append(char16_t(')'));
1105 isSet
= htmlColor
.Equals(aValue
, nsCaseInsensitiveStringComparator
);
1108 htmlValueString
.Equals(aValue
, nsCaseInsensitiveStringComparator
);
1111 } else if (nsGkAtoms::tt
== aHTMLProperty
) {
1112 isSet
= StringBeginsWith(aValue
, u
"monospace"_ns
);
1113 } else if (nsGkAtoms::font
== aHTMLProperty
&& aAttribute
&&
1114 aAttribute
== nsGkAtoms::face
) {
1115 if (!htmlValueString
.IsEmpty()) {
1116 const char16_t commaSpace
[] = {char16_t(','), HTMLEditUtils::kSpace
, 0};
1117 const char16_t comma
[] = {char16_t(','), 0};
1118 htmlValueString
.ReplaceSubstring(commaSpace
, comma
);
1119 nsAutoString
valueStringNorm(aValue
);
1120 valueStringNorm
.ReplaceSubstring(commaSpace
, comma
);
1121 isSet
= htmlValueString
.Equals(valueStringNorm
,
1122 nsCaseInsensitiveStringComparator
);
1127 } else if (aAttribute
== nsGkAtoms::align
) {
1133 if (!htmlValueString
.IsEmpty() &&
1134 htmlValueString
.Equals(aValue
, nsCaseInsensitiveStringComparator
)) {
1138 if (htmlValueString
.EqualsLiteral("-moz-editor-invert-value")) {
1146 if (nsGkAtoms::u
!= aHTMLProperty
&& nsGkAtoms::strike
!= aHTMLProperty
) {
1150 // Unfortunately, the value of the text-decoration property is not
1151 // inherited. that means that we have to look at ancestors of node to see
1152 // if they are underlined.
1157 Result
<bool, nsresult
> CSSEditUtils::HaveCSSEquivalentStylesInternal(
1158 nsIContent
& aContent
, nsAtom
* aHTMLProperty
, nsAtom
* aAttribute
,
1159 StyleType aStyleType
) {
1160 MOZ_ASSERT(aHTMLProperty
|| aAttribute
);
1162 // FYI: Unfortunately, we cannot use InclusiveAncestorsOfType here
1163 // because GetCSSEquivalentToHTMLInlineStyleSetInternal() may flush
1164 // pending notifications.
1165 nsAutoString valueString
;
1166 for (nsCOMPtr
<nsIContent
> content
= &aContent
; content
;
1167 content
= content
->GetParentElement()) {
1168 nsCOMPtr
<nsINode
> parentNode
= content
->GetParentNode();
1169 // get the value of the CSS equivalent styles
1170 nsresult rv
= GetCSSEquivalentToHTMLInlineStyleSetInternal(
1171 *content
, aHTMLProperty
, aAttribute
, valueString
, aStyleType
);
1172 if (NS_WARN_IF(!mHTMLEditor
|| mHTMLEditor
->Destroyed())) {
1173 return Err(NS_ERROR_EDITOR_DESTROYED
);
1175 if (NS_FAILED(rv
)) {
1177 "CSSEditUtils::GetCSSEquivalentToHTMLInlineStyleSetInternal() "
1181 if (NS_WARN_IF(parentNode
!= content
->GetParentNode())) {
1182 return Err(NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE
);
1185 if (!valueString
.IsEmpty()) {
1189 if (nsGkAtoms::u
!= aHTMLProperty
&& nsGkAtoms::strike
!= aHTMLProperty
) {
1193 // 'nfortunately, the value of the text-decoration property is not
1195 // that means that we have to look at ancestors of node to see if they
1202 void CSSEditUtils::SetCSSEnabled(bool aIsCSSPrefChecked
) {
1203 mIsCSSPrefChecked
= aIsCSSPrefChecked
;
1206 bool CSSEditUtils::IsCSSPrefChecked() const { return mIsCSSPrefChecked
; }
1208 // ElementsSameStyle compares two elements and checks if they have the same
1209 // specified CSS declarations in the STYLE attribute
1210 // The answer is always negative if at least one of them carries an ID or a
1214 bool CSSEditUtils::DoStyledElementsHaveSameStyle(
1215 nsStyledElement
& aStyledElement
, nsStyledElement
& aOtherStyledElement
) {
1216 if (aStyledElement
.HasAttr(kNameSpaceID_None
, nsGkAtoms::id
) ||
1217 aOtherStyledElement
.HasAttr(kNameSpaceID_None
, nsGkAtoms::id
)) {
1218 // at least one of the spans carries an ID ; suspect a CSS rule applies to
1219 // it and refuse to merge the nodes
1223 nsAutoString firstClass
, otherClass
;
1224 bool isElementClassSet
=
1225 aStyledElement
.GetAttr(kNameSpaceID_None
, nsGkAtoms::_class
, firstClass
);
1226 bool isOtherElementClassSet
= aOtherStyledElement
.GetAttr(
1227 kNameSpaceID_None
, nsGkAtoms::_class
, otherClass
);
1228 if (isElementClassSet
&& isOtherElementClassSet
) {
1229 // both spans carry a class, let's compare them
1230 if (!firstClass
.Equals(otherClass
)) {
1231 // WARNING : technically, the comparison just above is questionable :
1232 // from a pure HTML/CSS point of view class="a b" is NOT the same than
1233 // class="b a" because a CSS rule could test the exact value of the class
1234 // attribute to be "a b" for instance ; from a user's point of view, a
1235 // wysiwyg editor should probably NOT make any difference. CSS people
1236 // need to discuss this issue before any modification.
1239 } else if (isElementClassSet
|| isOtherElementClassSet
) {
1240 // one span only carries a class, early way out
1244 // XXX If `GetPropertyValue()` won't run script, we can stop using
1246 nsCOMPtr
<nsICSSDeclaration
> firstCSSDecl
= aStyledElement
.Style();
1247 if (!firstCSSDecl
) {
1248 NS_WARNING("nsStyledElement::Style() failed");
1251 nsCOMPtr
<nsICSSDeclaration
> otherCSSDecl
= aOtherStyledElement
.Style();
1252 if (!otherCSSDecl
) {
1253 NS_WARNING("nsStyledElement::Style() failed");
1257 const uint32_t firstLength
= firstCSSDecl
->Length();
1258 const uint32_t otherLength
= otherCSSDecl
->Length();
1259 if (firstLength
!= otherLength
) {
1260 // early way out if we can
1265 // no inline style !
1269 for (uint32_t i
= 0; i
< firstLength
; i
++) {
1270 nsAutoCString firstValue
, otherValue
;
1271 nsAutoCString propertyNameString
;
1272 firstCSSDecl
->Item(i
, propertyNameString
);
1273 DebugOnly
<nsresult
> rvIgnored
=
1274 firstCSSDecl
->GetPropertyValue(propertyNameString
, firstValue
);
1275 NS_WARNING_ASSERTION(
1276 NS_SUCCEEDED(rvIgnored
),
1277 "nsICSSDeclaration::GetPropertyValue() failed, but ignored");
1278 rvIgnored
= otherCSSDecl
->GetPropertyValue(propertyNameString
, otherValue
);
1279 NS_WARNING_ASSERTION(
1280 NS_SUCCEEDED(rvIgnored
),
1281 "nsICSSDeclaration::GetPropertyValue() failed, but ignored");
1282 if (!firstValue
.Equals(otherValue
)) {
1286 for (uint32_t i
= 0; i
< otherLength
; i
++) {
1287 nsAutoCString firstValue
, otherValue
;
1288 nsAutoCString propertyNameString
;
1289 otherCSSDecl
->Item(i
, propertyNameString
);
1290 DebugOnly
<nsresult
> rvIgnored
=
1291 otherCSSDecl
->GetPropertyValue(propertyNameString
, otherValue
);
1292 NS_WARNING_ASSERTION(
1293 NS_SUCCEEDED(rvIgnored
),
1294 "nsICSSDeclaration::GetPropertyValue() failed, but ignored");
1295 rvIgnored
= firstCSSDecl
->GetPropertyValue(propertyNameString
, firstValue
);
1296 NS_WARNING_ASSERTION(
1297 NS_SUCCEEDED(rvIgnored
),
1298 "nsICSSDeclaration::GetPropertyValue() failed, but ignored");
1299 if (!firstValue
.Equals(otherValue
)) {
1307 } // namespace mozilla