Bumping manifests a=b2g-bump
[gecko.git] / editor / libeditor / nsHTMLCSSUtils.cpp
blob94055770525497f9ddfb39628f1a64ad350bc0b7
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"
7 #include "EditTxn.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"
16 #include "nsCOMPtr.h"
17 #include "nsColor.h"
18 #include "nsComputedDOMStyle.h"
19 #include "nsDebug.h"
20 #include "nsDependentSubstring.h"
21 #include "nsError.h"
22 #include "nsGkAtoms.h"
23 #include "nsHTMLCSSUtils.h"
24 #include "nsHTMLEditor.h"
25 #include "nsIAtom.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"
34 #include "nsINode.h"
35 #include "nsISupportsImpl.h"
36 #include "nsISupportsUtils.h"
37 #include "nsLiteralString.h"
38 #include "nsPIDOMWindow.h"
39 #include "nsReadableUtils.h"
40 #include "nsString.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;
49 static
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");
57 else {
58 aOutputString.AssignLiteral("bold");
62 static
63 void ProcessDefaultValue(const nsAString * aInputString, nsAString & aOutputString,
64 const char * aDefaultValueString,
65 const char * aPrependString, const char* aAppendString)
67 CopyASCIItoUTF16(aDefaultValueString, aOutputString);
70 static
71 void ProcessSameValue(const nsAString * aInputString, nsAString & aOutputString,
72 const char * aDefaultValueString,
73 const char * aPrependString, const char* aAppendString)
75 if (aInputString) {
76 aOutputString.Assign(*aInputString);
78 else
79 aOutputString.Truncate();
82 static
83 void ProcessExtendedValue(const nsAString * aInputString, nsAString & aOutputString,
84 const char * aDefaultValueString,
85 const char * aPrependString, const char* aAppendString)
87 aOutputString.Truncate();
88 if (aInputString) {
89 if (aPrependString) {
90 AppendASCIItoUTF16(aPrependString, aOutputString);
92 aOutputString.Append(*aInputString);
93 if (aAppendString) {
94 AppendASCIItoUTF16(aAppendString, aOutputString);
99 static
100 void ProcessLengthValue(const nsAString * aInputString, nsAString & aOutputString,
101 const char * aDefaultValueString,
102 const char * aPrependString, const char* aAppendString)
104 aOutputString.Truncate();
105 if (aInputString) {
106 aOutputString.Append(*aInputString);
107 if (-1 == aOutputString.FindChar(char16_t('%'))) {
108 aOutputString.AppendLiteral("px");
113 static
114 void ProcessListStyleTypeValue(const nsAString * aInputString, nsAString & aOutputString,
115 const char * aDefaultValueString,
116 const char * aPrependString, const char* aAppendString)
118 aOutputString.Truncate();
119 if (aInputString) {
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);
143 static
144 void ProcessMarginLeftValue(const nsAString * aInputString, nsAString & aOutputString,
145 const char * aDefaultValueString,
146 const char * aPrependString, const char* aAppendString)
148 aOutputString.Truncate();
149 if (aInputString) {
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");
158 else {
159 aOutputString.AppendLiteral("0px");
164 static
165 void ProcessMarginRightValue(const nsAString * aInputString, nsAString & aOutputString,
166 const char * aDefaultValueString,
167 const char * aPrependString, const char* aAppendString)
169 aOutputString.Truncate();
170 if (aInputString) {
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");
179 else {
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
302 bool
303 nsHTMLCSSUtils::IsCSSEditableProperty(nsIDOMNode* aNode,
304 nsIAtom* aProperty,
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);
314 bool
315 nsHTMLCSSUtils::IsCSSEditableProperty(nsIContent* aNode,
316 nsIAtom* aProperty,
317 const nsAString* aAttribute)
319 MOZ_ASSERT(aNode);
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")))) {
340 return true;
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)) {
363 return true;
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)) {
375 return true;
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"))) {
383 return true;
386 // attribute BGCOLOR on other elements
387 if (aAttribute && aAttribute->EqualsLiteral("bgcolor")) {
388 return true;
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"))) {
396 return true;
399 // attributes HEIGHT and WIDTH on TABLE
400 if (aAttribute && nsGkAtoms::table == tagName &&
401 (aAttribute->EqualsLiteral("height")
402 || aAttribute->EqualsLiteral("width"))) {
403 return true;
406 // attributes SIZE and WIDTH on HR
407 if (aAttribute && nsGkAtoms::hr == tagName &&
408 (aAttribute->EqualsLiteral("size")
409 || aAttribute->EqualsLiteral("width"))) {
410 return true;
413 // attribute TYPE on OL UL LI
414 if (aAttribute &&
415 (nsGkAtoms::ol == tagName || nsGkAtoms::ul == tagName ||
416 nsGkAtoms::li == tagName) && aAttribute->EqualsLiteral("type")) {
417 return true;
420 if (aAttribute && nsGkAtoms::img == tagName &&
421 (aAttribute->EqualsLiteral("border")
422 || aAttribute->EqualsLiteral("width")
423 || aAttribute->EqualsLiteral("height"))) {
424 return true;
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)) {
439 return true;
442 return false;
445 // The lowest level above the transaction; adds the CSS declaration
446 // "aProperty : aValue" to the inline styles carried by aElement
447 nsresult
448 nsHTMLCSSUtils::SetCSSProperty(Element& aElement, nsIAtom& aProperty,
449 const nsAString& aValue, bool aSuppressTxn)
451 nsRefPtr<ChangeStyleTxn> txn =
452 CreateCSSPropertyTxn(aElement, aProperty, aValue, ChangeStyleTxn::eSet);
453 if (aSuppressTxn) {
454 return txn->DoTransaction();
456 return mHTMLEditor->DoTransaction(txn);
459 nsresult
460 nsHTMLCSSUtils::SetCSSPropertyPixels(Element& aElement, nsIAtom& aProperty,
461 int32_t aIntValue)
463 nsAutoString s;
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
472 nsresult
473 nsHTMLCSSUtils::RemoveCSSProperty(Element& aElement, nsIAtom& aProperty,
474 const nsAString& aValue, bool aSuppressTxn)
476 nsRefPtr<ChangeStyleTxn> txn =
477 CreateCSSPropertyTxn(aElement, aProperty, aValue, ChangeStyleTxn::eRemove);
478 if (aSuppressTxn) {
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);
491 return txn.forget();
494 nsresult
495 nsHTMLCSSUtils::GetSpecifiedProperty(nsIDOMNode *aNode, nsIAtom *aProperty,
496 nsAString & aValue)
498 return GetCSSInlinePropertyBase(aNode, aProperty, aValue, eSpecified);
501 nsresult
502 nsHTMLCSSUtils::GetComputedProperty(nsIDOMNode *aNode, nsIAtom *aProperty,
503 nsAString & aValue)
505 return GetCSSInlinePropertyBase(aNode, aProperty, aValue, eComputed);
508 nsresult
509 nsHTMLCSSUtils::GetCSSInlinePropertyBase(nsIDOMNode* aNode, nsIAtom* aProperty,
510 nsAString& aValue,
511 StyleType aStyleType)
513 nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
514 return GetCSSInlinePropertyBase(node, aProperty, aValue, aStyleType);
517 nsresult
518 nsHTMLCSSUtils::GetCSSInlinePropertyBase(nsINode* aNode, nsIAtom* aProperty,
519 nsAString& aValue,
520 StyleType aStyleType)
522 MOZ_ASSERT(aNode && aProperty);
523 aValue.Truncate();
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)));
537 return NS_OK;
540 MOZ_ASSERT(aStyleType == eSpecified);
541 nsRefPtr<css::StyleRule> rule = element->GetInlineStyleRule();
542 if (!rule) {
543 return NS_OK;
545 nsCSSProperty prop =
546 nsCSSProps::LookupProperty(nsDependentAtomString(aProperty),
547 nsCSSProps::eEnabledForAllContent);
548 MOZ_ASSERT(prop != eCSSProperty_UNKNOWN);
549 rule->GetDeclaration()->GetValue(prop, aValue);
551 return NS_OK;
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
580 nsresult
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)) {
592 return NS_OK;
595 return mHTMLEditor->RemoveContainer(element);
598 // Answers true is the property can be removed by setting a "none" CSS value
599 // on a node
600 bool
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
607 void
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?
613 if (NS_FAILED(rv)) {
614 NS_WARNING("failed to get editor.background_color");
615 aColor.AssignLiteral("#ffffff"); // Default to white
617 return;
620 if (Preferences::GetBool("browser.display.use_system_colors", false)) {
621 return;
624 nsresult rv =
625 Preferences::GetString("browser.display.background_color", &aColor);
626 // XXX Why don't you validate the pref value?
627 if (NS_FAILED(rv)) {
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
634 void
635 nsHTMLCSSUtils::GetDefaultLengthUnit(nsAString & aLengthUnit)
637 nsresult rv =
638 Preferences::GetString("editor.css.default_length_unit", &aLengthUnit);
639 // XXX Why don't you validate the pref value?
640 if (NS_FAILED(rv)) {
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...
648 void
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;
655 int8_t sign = 1;
656 int32_t i = 0, j = aString.Length();
657 char16_t c;
658 bool floatingPointFound = false;
659 c = *iter;
660 if (char16_t('-') == c) { sign = -1; iter++; i++; }
661 else if (char16_t('+') == c) { iter++; i++; }
662 while (i < j) {
663 c = *iter;
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')));
675 b = b / 10 * a;
677 else if (!floatingPointFound && (char16_t('.') == c)) {
678 floatingPointFound = true;
679 a = 1.0f; b = 0.1f;
681 else break;
682 iter++;
683 i++;
685 *aValue = value * sign;
686 *aUnit = NS_NewAtom(StringTail(aString, j-i)).take();
689 void
690 nsHTMLCSSUtils::GetCSSPropertyAtom(nsCSSEditableProperty aProperty, nsIAtom ** aAtom)
692 *aAtom = nullptr;
693 switch (aProperty) {
694 case eCSSEditableProperty_background_color:
695 *aAtom = nsGkAtoms::backgroundColor;
696 break;
697 case eCSSEditableProperty_background_image:
698 *aAtom = nsGkAtoms::background_image;
699 break;
700 case eCSSEditableProperty_border:
701 *aAtom = nsGkAtoms::border;
702 break;
703 case eCSSEditableProperty_caption_side:
704 *aAtom = nsGkAtoms::caption_side;
705 break;
706 case eCSSEditableProperty_color:
707 *aAtom = nsGkAtoms::color;
708 break;
709 case eCSSEditableProperty_float:
710 *aAtom = nsGkAtoms::_float;
711 break;
712 case eCSSEditableProperty_font_family:
713 *aAtom = nsGkAtoms::font_family;
714 break;
715 case eCSSEditableProperty_font_size:
716 *aAtom = nsGkAtoms::font_size;
717 break;
718 case eCSSEditableProperty_font_style:
719 *aAtom = nsGkAtoms::font_style;
720 break;
721 case eCSSEditableProperty_font_weight:
722 *aAtom = nsGkAtoms::fontWeight;
723 break;
724 case eCSSEditableProperty_height:
725 *aAtom = nsGkAtoms::height;
726 break;
727 case eCSSEditableProperty_list_style_type:
728 *aAtom = nsGkAtoms::list_style_type;
729 break;
730 case eCSSEditableProperty_margin_left:
731 *aAtom = nsGkAtoms::marginLeft;
732 break;
733 case eCSSEditableProperty_margin_right:
734 *aAtom = nsGkAtoms::marginRight;
735 break;
736 case eCSSEditableProperty_text_align:
737 *aAtom = nsGkAtoms::textAlign;
738 break;
739 case eCSSEditableProperty_text_decoration:
740 *aAtom = nsGkAtoms::text_decoration;
741 break;
742 case eCSSEditableProperty_vertical_align:
743 *aAtom = nsGkAtoms::vertical_align;
744 break;
745 case eCSSEditableProperty_whitespace:
746 *aAtom = nsGkAtoms::white_space;
747 break;
748 case eCSSEditableProperty_width:
749 *aAtom = nsGkAtoms::width;
750 break;
751 case eCSSEditableProperty_NONE:
752 // intentionally empty
753 break;
757 // Populate aProperty and aValueArray with the CSS declarations equivalent to the
758 // value aValue according to the equivalence table aEquivTable
759 void
760 nsHTMLCSSUtils::BuildCSSDeclarations(nsTArray<nsIAtom*> & aPropertyArray,
761 nsTArray<nsString> & aValueArray,
762 const CSSEquivTable * aEquivTable,
763 const nsAString * aValue,
764 bool aGetOrRemoveRequest)
766 // clear arrays
767 aPropertyArray.Clear();
768 aValueArray.Clear();
770 // if we have an input value, let's use it
771 nsAutoString value, lowerCasedValue;
772 if (aValue) {
773 value.Assign(*aValue);
774 lowerCasedValue.Assign(*aValue);
775 ToLowerCase(lowerCasedValue);
778 int8_t index = 0;
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,
787 cssValue,
788 aEquivTable[index].defaultValue,
789 aEquivTable[index].prependValue,
790 aEquivTable[index].appendValue);
791 GetCSSPropertyAtom(cssProperty, &cssPropertyAtom);
792 aPropertyArray.AppendElement(cssPropertyAtom);
793 aValueArray.AppendElement(cssValue);
795 index++;
796 cssProperty = aEquivTable[index].cssProperty;
800 // Populate cssPropertyArray and cssValueArray with the declarations equivalent
801 // to aHTMLProperty/aAttribute/aValue for the node aNode
802 void
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;
848 } else {
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;
868 if (equivTable) {
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.
877 int32_t
878 nsHTMLCSSUtils::SetCSSEquivalentToHTMLStyle(dom::Element* aElement,
879 nsIAtom* aProperty,
880 const nsAString* aAttribute,
881 const nsAString* aValue,
882 bool aSuppressTransaction)
884 MOZ_ASSERT(aElement && aProperty);
885 MOZ_ASSERT_IF(aAttribute, aValue);
886 int32_t count;
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,
893 aValue, &count,
894 aSuppressTransaction);
895 NS_ASSERTION(NS_SUCCEEDED(res), "SetCSSEquivalentToHTMLStyle failed");
896 NS_ENSURE_SUCCESS(res, count);
897 return count;
900 nsresult
901 nsHTMLCSSUtils::SetCSSEquivalentToHTMLStyle(nsIDOMNode * aNode,
902 nsIAtom *aHTMLProperty,
903 const nsAString *aAttribute,
904 const nsAString *aValue,
905 int32_t * aCount,
906 bool aSuppressTransaction)
908 nsCOMPtr<dom::Element> element = do_QueryInterface(aNode);
909 *aCount = 0;
910 if (!element || !IsCSSEditableProperty(element, aHTMLProperty, aAttribute)) {
911 return NS_OK;
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,
922 false);
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);
931 return NS_OK;
934 // Remove from aNode the CSS inline style equivalent to HTMLProperty/aAttribute/aValue for the node
935 nsresult
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);
949 nsresult
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)) {
959 return NS_OK;
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,
970 true);
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);
981 return NS_OK;
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
987 // computed.
988 nsresult
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)) {
1000 return NS_OK;
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)
1018 if (index) {
1019 aValueString.Append(char16_t(' '));
1021 aValueString.Append(valueString);
1023 return NS_OK;
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.
1033 bool
1034 nsHTMLCSSUtils::IsCSSEquivalentToHTMLInlineStyleSet(nsIContent* aContent,
1035 nsIAtom* aProperty,
1036 const nsAString* aAttribute,
1037 const nsAString& aValue,
1038 StyleType aStyleType)
1040 MOZ_ASSERT(aContent && aProperty);
1041 bool isSet;
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);
1048 return isSet;
1051 nsresult
1052 nsHTMLCSSUtils::IsCSSEquivalentToHTMLInlineStyleSet(nsIDOMNode *aNode,
1053 nsIAtom *aHTMLProperty,
1054 const nsAString *aHTMLAttribute,
1055 bool& aIsSet,
1056 nsAString& valueString,
1057 StyleType aStyleType)
1059 NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER);
1061 nsAutoString htmlValueString(valueString);
1062 aIsSet = false;
1063 nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
1064 do {
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()) {
1073 return NS_OK;
1076 if (nsGkAtoms::b == aHTMLProperty) {
1077 if (valueString.EqualsLiteral("bold")) {
1078 aIsSet = true;
1079 } else if (valueString.EqualsLiteral("normal")) {
1080 aIsSet = false;
1081 } else if (valueString.EqualsLiteral("bolder")) {
1082 aIsSet = true;
1083 valueString.AssignLiteral("bold");
1084 } else {
1085 int32_t weight = 0;
1086 nsresult errorCode;
1087 nsAutoString value(valueString);
1088 weight = value.ToInteger(&errorCode);
1089 if (400 < weight) {
1090 aIsSet = true;
1091 valueString.AssignLiteral("bold");
1092 } else {
1093 aIsSet = false;
1094 valueString.AssignLiteral("normal");
1097 } else if (nsGkAtoms::i == aHTMLProperty) {
1098 if (valueString.EqualsLiteral("italic") ||
1099 valueString.EqualsLiteral("oblique")) {
1100 aIsSet = true;
1102 } else if (nsGkAtoms::u == aHTMLProperty) {
1103 nsAutoString val;
1104 val.AssignLiteral("underline");
1105 aIsSet = ChangeStyleTxn::ValueIncludes(valueString, val);
1106 } else if (nsGkAtoms::strike == aHTMLProperty) {
1107 nsAutoString val;
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()) {
1115 aIsSet = true;
1116 } else {
1117 nscolor rgba;
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");
1130 } else {
1131 htmlColor.AppendLiteral("rgb(");
1133 NS_NAMED_LITERAL_STRING(comma, ", ");
1135 tmpStr.AppendInt(NS_GET_R(rgba), 10);
1136 htmlColor.Append(tmpStr + comma);
1138 tmpStr.Truncate();
1139 tmpStr.AppendInt(NS_GET_G(rgba), 10);
1140 htmlColor.Append(tmpStr + comma);
1142 tmpStr.Truncate();
1143 tmpStr.AppendInt(NS_GET_B(rgba), 10);
1144 htmlColor.Append(tmpStr);
1146 htmlColor.Append(char16_t(')'));
1149 aIsSet = htmlColor.Equals(valueString,
1150 nsCaseInsensitiveStringComparator());
1151 } else {
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());
1168 } else {
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");
1175 return NS_OK;
1176 } else if (aHTMLAttribute && aHTMLAttribute->EqualsLiteral("align")) {
1177 aIsSet = true;
1178 } else {
1179 aIsSet = false;
1180 return NS_OK;
1183 if (!htmlValueString.IsEmpty() &&
1184 htmlValueString.Equals(valueString,
1185 nsCaseInsensitiveStringComparator())) {
1186 aIsSet = true;
1189 if (htmlValueString.EqualsLiteral("-moz-editor-invert-value")) {
1190 aIsSet = !aIsSet;
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);
1200 return NS_OK;
1203 void
1204 nsHTMLCSSUtils::SetCSSEnabled(bool aIsCSSPrefChecked)
1206 mIsCSSPrefChecked = aIsCSSPrefChecked;
1209 bool
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
1218 bool
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);
1231 bool
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
1242 return false;
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.
1257 return false;
1259 } else if (isFirstClassSet || isSecondClassSet) {
1260 // one span only carries a class, early way out
1261 return false;
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) {
1268 return false;
1270 rv = GetInlineStyles(aSecondElement, getter_AddRefs(secondCSSDecl), &secondLength);
1271 if (NS_FAILED(rv) || !secondCSSDecl) {
1272 return false;
1275 if (firstLength != secondLength) {
1276 // early way out if we can
1277 return false;
1280 if (!firstLength) {
1281 // no inline style !
1282 return true;
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)) {
1292 return false;
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)) {
1300 return false;
1304 return true;
1307 nsresult
1308 nsHTMLCSSUtils::GetInlineStyles(dom::Element* aElement,
1309 nsIDOMCSSStyleDeclaration** aCssDecl,
1310 uint32_t* aLength)
1312 return GetInlineStyles(static_cast<nsISupports*>(aElement), aCssDecl, aLength);
1315 nsresult
1316 nsHTMLCSSUtils::GetInlineStyles(nsIDOMElement* aElement,
1317 nsIDOMCSSStyleDeclaration** aCssDecl,
1318 uint32_t* aLength)
1320 return GetInlineStyles(static_cast<nsISupports*>(aElement), aCssDecl, aLength);
1323 nsresult
1324 nsHTMLCSSUtils::GetInlineStyles(nsISupports *aElement,
1325 nsIDOMCSSStyleDeclaration **aCssDecl,
1326 uint32_t *aLength)
1328 NS_ENSURE_TRUE(aElement && aLength, NS_ERROR_NULL_POINTER);
1329 *aLength = 0;
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);
1338 return NS_OK;
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();
1351 dom::Element*
1352 nsHTMLCSSUtils::GetElementContainerOrSelf(nsINode* aNode)
1354 MOZ_ASSERT(aNode);
1355 if (nsIDOMNode::DOCUMENT_NODE == aNode->NodeType()) {
1356 return nullptr;
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();
1369 nsresult
1370 nsHTMLCSSUtils::SetCSSProperty(nsIDOMElement * aElement,
1371 const nsAString & aProperty,
1372 const nsAString & aValue)
1374 nsCOMPtr<nsIDOMCSSStyleDeclaration> cssDecl;
1375 uint32_t length;
1376 nsresult res = GetInlineStyles(aElement, getter_AddRefs(cssDecl), &length);
1377 if (NS_FAILED(res) || !cssDecl) return res;
1379 return cssDecl->SetProperty(aProperty,
1380 aValue,
1381 EmptyString());
1384 nsresult
1385 nsHTMLCSSUtils::SetCSSPropertyPixels(nsIDOMElement * aElement,
1386 const nsAString & aProperty,
1387 int32_t aIntValue)
1389 nsAutoString s;
1390 s.AppendInt(aIntValue);
1391 return SetCSSProperty(aElement, aProperty, s + NS_LITERAL_STRING("px"));