Bug 570258: Some more atom usage cleanup. r=jst
[mozilla-central.git] / editor / libeditor / html / nsHTMLCSSUtils.cpp
blobb93bb67e4c913c5bfecba27661574e8db5787653
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
15 * The Original Code is mozilla.org code.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
22 * Contributor(s):
23 * Original Author: Daniel Glazman <glazman@netscape.com>
25 * Alternatively, the contents of this file may be used under the terms of
26 * either of the GNU General Public License Version 2 or later (the "GPL"),
27 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the MPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the MPL, the GPL or the LGPL.
37 * ***** END LICENSE BLOCK ***** */
39 #include "nsHTMLEditor.h"
40 #include "nsCOMPtr.h"
41 #include "nsHTMLEditUtils.h"
42 #include "nsIPrefBranch.h"
43 #include "nsIPrefService.h"
44 #include "nsIServiceManager.h"
45 #include "nsEditProperty.h"
46 #include "ChangeCSSInlineStyleTxn.h"
47 #include "nsIDOMElement.h"
48 #include "nsIDOMElementCSSInlineStyle.h"
49 #include "nsIDOMDocument.h"
50 #include "nsIDOMDocumentView.h"
51 #include "nsIContent.h"
52 #include "nsIAtom.h"
53 #include "nsTextEditUtils.h"
54 #include "nsReadableUtils.h"
55 #include "nsUnicharUtils.h"
56 #include "nsHTMLCSSUtils.h"
57 #include "nsColor.h"
58 #include "nsAttrName.h"
59 #include "nsAutoPtr.h"
61 static
62 void ProcessBValue(const nsAString * aInputString, nsAString & aOutputString,
63 const char * aDefaultValueString,
64 const char * aPrependString, const char* aAppendString)
66 if (aInputString && aInputString->EqualsLiteral("-moz-editor-invert-value")) {
67 aOutputString.AssignLiteral("normal");
69 else {
70 aOutputString.AssignLiteral("bold");
74 static
75 void ProcessDefaultValue(const nsAString * aInputString, nsAString & aOutputString,
76 const char * aDefaultValueString,
77 const char * aPrependString, const char* aAppendString)
79 CopyASCIItoUTF16(aDefaultValueString, aOutputString);
82 static
83 void ProcessSameValue(const nsAString * aInputString, nsAString & aOutputString,
84 const char * aDefaultValueString,
85 const char * aPrependString, const char* aAppendString)
87 if (aInputString) {
88 aOutputString.Assign(*aInputString);
90 else
91 aOutputString.Truncate();
94 static
95 void ProcessExtendedValue(const nsAString * aInputString, nsAString & aOutputString,
96 const char * aDefaultValueString,
97 const char * aPrependString, const char* aAppendString)
99 aOutputString.Truncate();
100 if (aInputString) {
101 if (aPrependString) {
102 AppendASCIItoUTF16(aPrependString, aOutputString);
104 aOutputString.Append(*aInputString);
105 if (aAppendString) {
106 AppendASCIItoUTF16(aAppendString, aOutputString);
111 static
112 void ProcessLengthValue(const nsAString * aInputString, nsAString & aOutputString,
113 const char * aDefaultValueString,
114 const char * aPrependString, const char* aAppendString)
116 aOutputString.Truncate();
117 if (aInputString) {
118 aOutputString.Append(*aInputString);
119 if (-1 == aOutputString.FindChar(PRUnichar('%'))) {
120 aOutputString.AppendLiteral("px");
125 static
126 void ProcessListStyleTypeValue(const nsAString * aInputString, nsAString & aOutputString,
127 const char * aDefaultValueString,
128 const char * aPrependString, const char* aAppendString)
130 aOutputString.Truncate();
131 if (aInputString) {
132 if (aInputString->EqualsLiteral("1")) {
133 aOutputString.AppendLiteral("decimal");
135 else if (aInputString->EqualsLiteral("a")) {
136 aOutputString.AppendLiteral("lower-alpha");
138 else if (aInputString->EqualsLiteral("A")) {
139 aOutputString.AppendLiteral("upper-alpha");
141 else if (aInputString->EqualsLiteral("i")) {
142 aOutputString.AppendLiteral("lower-roman");
144 else if (aInputString->EqualsLiteral("I")) {
145 aOutputString.AppendLiteral("upper-roman");
147 else if (aInputString->EqualsLiteral("square")
148 || aInputString->EqualsLiteral("circle")
149 || aInputString->EqualsLiteral("disc")) {
150 aOutputString.Append(*aInputString);
155 static
156 void ProcessMarginLeftValue(const nsAString * aInputString, nsAString & aOutputString,
157 const char * aDefaultValueString,
158 const char * aPrependString, const char* aAppendString)
160 aOutputString.Truncate();
161 if (aInputString) {
162 if (aInputString->EqualsLiteral("center") ||
163 aInputString->EqualsLiteral("-moz-center")) {
164 aOutputString.AppendLiteral("auto");
166 else if (aInputString->EqualsLiteral("right") ||
167 aInputString->EqualsLiteral("-moz-right")) {
168 aOutputString.AppendLiteral("auto");
170 else {
171 aOutputString.AppendLiteral("0px");
176 static
177 void ProcessMarginRightValue(const nsAString * aInputString, nsAString & aOutputString,
178 const char * aDefaultValueString,
179 const char * aPrependString, const char* aAppendString)
181 aOutputString.Truncate();
182 if (aInputString) {
183 if (aInputString->EqualsLiteral("center") ||
184 aInputString->EqualsLiteral("-moz-center")) {
185 aOutputString.AppendLiteral("auto");
187 else if (aInputString->EqualsLiteral("left") ||
188 aInputString->EqualsLiteral("-moz-left")) {
189 aOutputString.AppendLiteral("auto");
191 else {
192 aOutputString.AppendLiteral("0px");
197 const nsHTMLCSSUtils::CSSEquivTable boldEquivTable[] = {
198 { nsHTMLCSSUtils::eCSSEditableProperty_font_weight, ProcessBValue, nsnull, nsnull, nsnull, PR_TRUE, PR_FALSE },
199 { nsHTMLCSSUtils::eCSSEditableProperty_NONE, 0 }
202 const nsHTMLCSSUtils::CSSEquivTable italicEquivTable[] = {
203 { nsHTMLCSSUtils::eCSSEditableProperty_font_style, ProcessDefaultValue, "italic", nsnull, nsnull, PR_TRUE, PR_FALSE },
204 { nsHTMLCSSUtils::eCSSEditableProperty_NONE, 0 }
207 const nsHTMLCSSUtils::CSSEquivTable underlineEquivTable[] = {
208 { nsHTMLCSSUtils::eCSSEditableProperty_text_decoration, ProcessDefaultValue, "underline", nsnull, nsnull, PR_TRUE, PR_FALSE },
209 { nsHTMLCSSUtils::eCSSEditableProperty_NONE, 0 }
212 const nsHTMLCSSUtils::CSSEquivTable strikeEquivTable[] = {
213 { nsHTMLCSSUtils::eCSSEditableProperty_text_decoration, ProcessDefaultValue, "line-through", nsnull, nsnull, PR_TRUE, PR_FALSE },
214 { nsHTMLCSSUtils::eCSSEditableProperty_NONE, 0 }
217 const nsHTMLCSSUtils::CSSEquivTable ttEquivTable[] = {
218 { nsHTMLCSSUtils::eCSSEditableProperty_font_family, ProcessDefaultValue, "monospace", nsnull, nsnull, PR_TRUE, PR_FALSE },
219 { nsHTMLCSSUtils::eCSSEditableProperty_NONE, 0 }
222 const nsHTMLCSSUtils::CSSEquivTable fontColorEquivTable[] = {
223 { nsHTMLCSSUtils::eCSSEditableProperty_color, ProcessSameValue, nsnull, nsnull, nsnull, PR_TRUE, PR_FALSE },
224 { nsHTMLCSSUtils::eCSSEditableProperty_NONE, 0 }
227 const nsHTMLCSSUtils::CSSEquivTable fontFaceEquivTable[] = {
228 { nsHTMLCSSUtils::eCSSEditableProperty_font_family, ProcessSameValue, nsnull, nsnull, nsnull, PR_TRUE, PR_FALSE },
229 { nsHTMLCSSUtils::eCSSEditableProperty_NONE, 0 }
232 const nsHTMLCSSUtils::CSSEquivTable bgcolorEquivTable[] = {
233 { nsHTMLCSSUtils::eCSSEditableProperty_background_color, ProcessSameValue, nsnull, nsnull, nsnull, PR_TRUE, PR_FALSE },
234 { nsHTMLCSSUtils::eCSSEditableProperty_NONE, 0 }
237 const nsHTMLCSSUtils::CSSEquivTable backgroundImageEquivTable[] = {
238 { nsHTMLCSSUtils::eCSSEditableProperty_background_image, ProcessExtendedValue, nsnull, "url(", ")", PR_TRUE, PR_TRUE },
239 { nsHTMLCSSUtils::eCSSEditableProperty_NONE, 0 }
242 const nsHTMLCSSUtils::CSSEquivTable textColorEquivTable[] = {
243 { nsHTMLCSSUtils::eCSSEditableProperty_color, ProcessSameValue, nsnull, nsnull, nsnull, PR_TRUE, PR_FALSE },
244 { nsHTMLCSSUtils::eCSSEditableProperty_NONE, 0 }
247 const nsHTMLCSSUtils::CSSEquivTable borderEquivTable[] = {
248 { nsHTMLCSSUtils::eCSSEditableProperty_border, ProcessExtendedValue, nsnull, nsnull, "px solid", PR_TRUE, PR_FALSE },
249 { nsHTMLCSSUtils::eCSSEditableProperty_NONE, 0 }
252 const nsHTMLCSSUtils::CSSEquivTable textAlignEquivTable[] = {
253 { nsHTMLCSSUtils::eCSSEditableProperty_text_align, ProcessSameValue, nsnull, nsnull, nsnull, PR_TRUE, PR_FALSE },
254 { nsHTMLCSSUtils::eCSSEditableProperty_NONE, 0 }
257 const nsHTMLCSSUtils::CSSEquivTable captionAlignEquivTable[] = {
258 { nsHTMLCSSUtils::eCSSEditableProperty_caption_side, ProcessSameValue, nsnull, nsnull, nsnull, PR_TRUE, PR_FALSE },
259 { nsHTMLCSSUtils::eCSSEditableProperty_NONE, 0 }
262 const nsHTMLCSSUtils::CSSEquivTable verticalAlignEquivTable[] = {
263 { nsHTMLCSSUtils::eCSSEditableProperty_vertical_align, ProcessSameValue, nsnull, nsnull, nsnull, PR_TRUE, PR_FALSE },
264 { nsHTMLCSSUtils::eCSSEditableProperty_NONE, 0 }
267 const nsHTMLCSSUtils::CSSEquivTable nowrapEquivTable[] = {
268 { nsHTMLCSSUtils::eCSSEditableProperty_whitespace, ProcessDefaultValue, "nowrap", nsnull, nsnull, PR_TRUE, PR_FALSE },
269 { nsHTMLCSSUtils::eCSSEditableProperty_NONE, 0 }
272 const nsHTMLCSSUtils::CSSEquivTable widthEquivTable[] = {
273 { nsHTMLCSSUtils::eCSSEditableProperty_width, ProcessLengthValue, nsnull, nsnull, nsnull, PR_TRUE, PR_FALSE },
274 { nsHTMLCSSUtils::eCSSEditableProperty_NONE, 0 }
277 const nsHTMLCSSUtils::CSSEquivTable heightEquivTable[] = {
278 { nsHTMLCSSUtils::eCSSEditableProperty_height, ProcessLengthValue, nsnull, nsnull, nsnull, PR_TRUE, PR_FALSE },
279 { nsHTMLCSSUtils::eCSSEditableProperty_NONE, 0 }
282 const nsHTMLCSSUtils::CSSEquivTable listStyleTypeEquivTable[] = {
283 { nsHTMLCSSUtils::eCSSEditableProperty_list_style_type, ProcessListStyleTypeValue, nsnull, nsnull, nsnull, PR_TRUE, PR_TRUE },
284 { nsHTMLCSSUtils::eCSSEditableProperty_NONE, 0 }
287 const nsHTMLCSSUtils::CSSEquivTable tableAlignEquivTable[] = {
288 { nsHTMLCSSUtils::eCSSEditableProperty_text_align, ProcessDefaultValue, "left", nsnull, nsnull, PR_FALSE, PR_FALSE },
289 { nsHTMLCSSUtils::eCSSEditableProperty_margin_left, ProcessMarginLeftValue, nsnull, nsnull, nsnull, PR_TRUE, PR_FALSE },
290 { nsHTMLCSSUtils::eCSSEditableProperty_margin_right, ProcessMarginRightValue, nsnull, nsnull, nsnull, PR_TRUE, PR_FALSE },
291 { nsHTMLCSSUtils::eCSSEditableProperty_NONE, 0 }
294 const nsHTMLCSSUtils::CSSEquivTable hrAlignEquivTable[] = {
295 { nsHTMLCSSUtils::eCSSEditableProperty_margin_left, ProcessMarginLeftValue, nsnull, nsnull, nsnull, PR_TRUE, PR_FALSE },
296 { nsHTMLCSSUtils::eCSSEditableProperty_margin_right, ProcessMarginRightValue, nsnull, nsnull, nsnull, PR_TRUE, PR_FALSE },
297 { nsHTMLCSSUtils::eCSSEditableProperty_NONE, 0 }
300 nsHTMLCSSUtils::nsHTMLCSSUtils()
301 : mIsCSSPrefChecked(PR_FALSE)
305 nsHTMLCSSUtils::~nsHTMLCSSUtils()
309 nsresult
310 nsHTMLCSSUtils::Init(nsHTMLEditor *aEditor)
312 nsresult result = NS_OK;
313 mHTMLEditor = static_cast<nsHTMLEditor*>(aEditor);
315 // let's retrieve the value of the "CSS editing" pref
316 nsCOMPtr<nsIPrefBranch> prefBranch =
317 do_GetService(NS_PREFSERVICE_CONTRACTID, &result);
318 if (NS_SUCCEEDED(result) && prefBranch) {
319 result = prefBranch->GetBoolPref("editor.use_css", &mIsCSSPrefChecked);
320 if (NS_FAILED(result)) return result;
322 return result;
325 // Answers true if we have some CSS equivalence for the HTML style defined
326 // by aProperty and/or aAttribute for the node aNode
327 PRBool
328 nsHTMLCSSUtils::IsCSSEditableProperty(nsIDOMNode * aNode,
329 nsIAtom * aProperty,
330 const nsAString * aAttribute)
332 NS_ASSERTION(aNode, "Shouldn't you pass aNode? - Bug 214025");
334 nsCOMPtr<nsIDOMNode> node = aNode;
335 // we need an element node here
336 if (mHTMLEditor->IsTextNode(aNode)) {
337 aNode->GetParentNode(getter_AddRefs(node));
339 nsCOMPtr<nsIContent> content = do_QueryInterface(node);
340 if (!content) return PR_FALSE;
342 nsIAtom *tagName = content->Tag();
343 // brade: should the above use nsEditor::GetTag(aNode)?
344 // brade: shouldn't some of the above go below the next block?
346 // html inline styles B I TT U STRIKE and COLOR/FACE on FONT
347 if (nsEditProperty::b == aProperty
348 || nsEditProperty::i == aProperty
349 || nsEditProperty::tt == aProperty
350 || nsEditProperty::u == aProperty
351 || nsEditProperty::strike == aProperty
352 || ((nsEditProperty::font == aProperty) && aAttribute &&
353 (aAttribute->EqualsLiteral("color") ||
354 aAttribute->EqualsLiteral("face")))) {
355 return PR_TRUE;
358 // ALIGN attribute on elements supporting it
359 if (aAttribute && (aAttribute->EqualsLiteral("align")) &&
360 (nsEditProperty::div == tagName
361 || nsEditProperty::p == tagName
362 || nsEditProperty::h1 == tagName
363 || nsEditProperty::h2 == tagName
364 || nsEditProperty::h3 == tagName
365 || nsEditProperty::h4 == tagName
366 || nsEditProperty::h5 == tagName
367 || nsEditProperty::h6 == tagName
368 || nsEditProperty::td == tagName
369 || nsEditProperty::th == tagName
370 || nsEditProperty::table == tagName
371 || nsEditProperty::hr == tagName
372 // brade: for the above, why not use nsHTMLEditUtils::SupportsAlignAttr
373 // brade: but it also checks for tbody, tfoot, thead
374 // Let's add the following elements here even if ALIGN has not
375 // the same meaning for them
376 || nsEditProperty::legend == tagName
377 || nsEditProperty::caption == tagName)) {
378 return PR_TRUE;
381 if (aAttribute && (aAttribute->EqualsLiteral("valign")) &&
382 (nsEditProperty::col == tagName
383 || nsEditProperty::colgroup == tagName
384 || nsEditProperty::tbody == tagName
385 || nsEditProperty::td == tagName
386 || nsEditProperty::th == tagName
387 || nsEditProperty::tfoot == tagName
388 || nsEditProperty::thead == tagName
389 || nsEditProperty::tr == tagName)) {
390 return PR_TRUE;
393 // attributes TEXT, BACKGROUND and BGCOLOR on BODY
394 if (aAttribute && (nsEditProperty::body == tagName) &&
395 (aAttribute->EqualsLiteral("text")
396 || aAttribute->EqualsLiteral("background")
397 || aAttribute->EqualsLiteral("bgcolor"))) {
398 return PR_TRUE;
401 // attribute BGCOLOR on other elements
402 if (aAttribute && aAttribute->EqualsLiteral("bgcolor")) {
403 return PR_TRUE;
406 // attributes HEIGHT, WIDTH and NOWRAP on TD and TH
407 if (aAttribute && ((nsEditProperty::td == tagName)
408 || (nsEditProperty::th == tagName)) &&
409 (aAttribute->EqualsLiteral("height")
410 || aAttribute->EqualsLiteral("width")
411 || aAttribute->EqualsLiteral("nowrap"))) {
412 return PR_TRUE;
415 // attributes HEIGHT and WIDTH on TABLE
416 if (aAttribute && (nsEditProperty::table == tagName) &&
417 (aAttribute->EqualsLiteral("height")
418 || aAttribute->EqualsLiteral("width"))) {
419 return PR_TRUE;
422 // attributes SIZE and WIDTH on HR
423 if (aAttribute && (nsEditProperty::hr == tagName) &&
424 (aAttribute->EqualsLiteral("size")
425 || aAttribute->EqualsLiteral("width"))) {
426 return PR_TRUE;
429 // attribute TYPE on OL UL LI
430 if (aAttribute && (nsEditProperty::ol == tagName
431 || nsEditProperty::ul == tagName
432 || nsEditProperty::li == tagName) &&
433 aAttribute->EqualsLiteral("type")) {
434 return PR_TRUE;
437 if (aAttribute && nsEditProperty::img == tagName &&
438 (aAttribute->EqualsLiteral("border")
439 || aAttribute->EqualsLiteral("width")
440 || aAttribute->EqualsLiteral("height"))) {
441 return PR_TRUE;
444 // other elements that we can align using CSS even if they
445 // can't carry the html ALIGN attribute
446 if (aAttribute && aAttribute->EqualsLiteral("align") &&
447 (nsEditProperty::ul == tagName
448 || nsEditProperty::ol == tagName
449 || nsEditProperty::dl == tagName
450 || nsEditProperty::li == tagName
451 || nsEditProperty::dd == tagName
452 || nsEditProperty::dt == tagName
453 || nsEditProperty::address == tagName
454 || nsEditProperty::pre == tagName
455 || nsEditProperty::ul == tagName)) {
456 return PR_TRUE;
459 return PR_FALSE;
462 // the lowest level above the transaction; adds the css declaration "aProperty : aValue" to
463 // the inline styles carried by aElement
464 nsresult
465 nsHTMLCSSUtils::SetCSSProperty(nsIDOMElement *aElement, nsIAtom * aProperty, const nsAString & aValue,
466 PRBool aSuppressTransaction)
468 nsRefPtr<ChangeCSSInlineStyleTxn> txn;
469 nsresult result = CreateCSSPropertyTxn(aElement, aProperty, aValue,
470 getter_AddRefs(txn), PR_FALSE);
471 if (NS_SUCCEEDED(result)) {
472 if (aSuppressTransaction) {
473 result = txn->DoTransaction();
475 else {
476 result = mHTMLEditor->DoTransaction(txn);
479 return result;
482 nsresult
483 nsHTMLCSSUtils::SetCSSPropertyPixels(nsIDOMElement *aElement,
484 nsIAtom *aProperty,
485 PRInt32 aIntValue,
486 PRBool aSuppressTransaction)
488 nsAutoString s;
489 s.AppendInt(aIntValue);
490 return SetCSSProperty(aElement, aProperty, s + NS_LITERAL_STRING("px"),
491 aSuppressTransaction);
494 // the lowest level above the transaction; removes the value aValue from the list of values
495 // specified for the CSS property aProperty, or totally remove the declaration if this
496 // property accepts only one value
497 nsresult
498 nsHTMLCSSUtils::RemoveCSSProperty(nsIDOMElement *aElement, nsIAtom * aProperty, const nsAString & aValue,
499 PRBool aSuppressTransaction)
501 nsRefPtr<ChangeCSSInlineStyleTxn> txn;
502 nsresult result = CreateCSSPropertyTxn(aElement, aProperty, aValue,
503 getter_AddRefs(txn), PR_TRUE);
504 if (NS_SUCCEEDED(result)) {
505 if (aSuppressTransaction) {
506 result = txn->DoTransaction();
508 else {
509 result = mHTMLEditor->DoTransaction(txn);
512 return result;
515 nsresult
516 nsHTMLCSSUtils::CreateCSSPropertyTxn(nsIDOMElement *aElement,
517 nsIAtom * aAttribute,
518 const nsAString& aValue,
519 ChangeCSSInlineStyleTxn ** aTxn,
520 PRBool aRemoveProperty)
522 if (!aElement)
523 return NS_ERROR_NULL_POINTER;
525 *aTxn = new ChangeCSSInlineStyleTxn();
526 if (!*aTxn)
527 return NS_ERROR_OUT_OF_MEMORY;
528 NS_ADDREF(*aTxn);
529 return (*aTxn)->Init(mHTMLEditor, aElement, aAttribute, aValue, aRemoveProperty);
532 nsresult
533 nsHTMLCSSUtils::GetSpecifiedProperty(nsIDOMNode *aNode, nsIAtom *aProperty,
534 nsAString & aValue)
536 return GetCSSInlinePropertyBase(aNode, aProperty, aValue, nsnull, SPECIFIED_STYLE_TYPE);
539 nsresult
540 nsHTMLCSSUtils::GetComputedProperty(nsIDOMNode *aNode, nsIAtom *aProperty,
541 nsAString & aValue)
543 nsCOMPtr<nsIDOMViewCSS> viewCSS = nsnull;
544 nsresult res = GetDefaultViewCSS(aNode, getter_AddRefs(viewCSS));
545 if (NS_FAILED(res)) return res;
547 return GetCSSInlinePropertyBase(aNode, aProperty, aValue, viewCSS, COMPUTED_STYLE_TYPE);
550 nsresult
551 nsHTMLCSSUtils::GetCSSInlinePropertyBase(nsIDOMNode *aNode, nsIAtom *aProperty,
552 nsAString &aValue,
553 nsIDOMViewCSS *aViewCSS,
554 PRUint8 aStyleType)
556 aValue.Truncate();
557 NS_ENSURE_TRUE(aProperty, NS_ERROR_NULL_POINTER);
559 nsCOMPtr<nsIDOMElement>element;
560 nsresult res = GetElementContainerOrSelf(aNode, getter_AddRefs(element));
561 if (NS_FAILED(res)) return res;
563 switch (aStyleType) {
564 case COMPUTED_STYLE_TYPE:
565 if (element && aViewCSS) {
566 nsAutoString value, propString;
567 nsCOMPtr<nsIDOMCSSStyleDeclaration> cssDecl;
568 aProperty->ToString(propString);
569 // Get the all the computed css styles attached to the element node
570 res = aViewCSS->GetComputedStyle(element, EmptyString(), getter_AddRefs(cssDecl));
571 if (NS_FAILED(res) || !cssDecl) return res;
572 // from these declarations, get the one we want and that one only
573 res = cssDecl->GetPropertyValue(propString, value);
574 if (NS_FAILED(res)) return res;
575 aValue.Assign(value);
577 break;
578 case SPECIFIED_STYLE_TYPE:
579 if (element) {
580 nsCOMPtr<nsIDOMCSSStyleDeclaration> cssDecl;
581 PRUint32 length;
582 res = GetInlineStyles(element, getter_AddRefs(cssDecl), &length);
583 if (NS_FAILED(res) || !cssDecl) return res;
584 nsAutoString value, propString;
585 aProperty->ToString(propString);
586 res = cssDecl->GetPropertyValue(propString, value);
587 if (NS_FAILED(res)) return res;
588 aValue.Assign(value);
590 break;
592 return NS_OK;
595 nsresult
596 nsHTMLCSSUtils::GetDefaultViewCSS(nsIDOMNode *aNode, nsIDOMViewCSS **aViewCSS)
598 nsCOMPtr<nsIDOMElement>element;
599 nsresult res = GetElementContainerOrSelf(aNode, getter_AddRefs(element));
600 if (NS_FAILED(res)) return res;
602 // if we have an element node
603 if (element) {
604 // find the owner document
605 nsCOMPtr<nsIDOMDocument> doc;
606 nsCOMPtr<nsIDOMNode> node = do_QueryInterface(element);
607 res = node->GetOwnerDocument(getter_AddRefs(doc));
608 if (NS_FAILED(res)) return res;
609 if (doc) {
610 nsCOMPtr<nsIDOMDocumentView> documentView = do_QueryInterface(doc);
611 nsCOMPtr<nsIDOMAbstractView> abstractView;
612 // from the document, get the abtractView
613 res = documentView->GetDefaultView(getter_AddRefs(abstractView));
614 if (NS_FAILED(res)) return res;
615 if (abstractView) {
616 // from the abstractView, get the CSS view
617 CallQueryInterface(abstractView, aViewCSS);
618 return NS_OK;
622 *aViewCSS = nsnull;
623 return NS_OK;
626 nsresult
627 NS_NewHTMLCSSUtils(nsHTMLCSSUtils** aInstancePtrResult)
629 nsHTMLCSSUtils * rules = new nsHTMLCSSUtils();
630 if (rules) {
631 *aInstancePtrResult = rules;
632 return NS_OK;
635 *aInstancePtrResult = nsnull;
636 return NS_ERROR_OUT_OF_MEMORY;
639 // remove the CSS style "aProperty : aPropertyValue" and possibly remove the whole node
640 // if it is a span and if its only attribute is _moz_dirty
641 nsresult
642 nsHTMLCSSUtils::RemoveCSSInlineStyle(nsIDOMNode *aNode, nsIAtom *aProperty, const nsAString & aPropertyValue)
644 nsCOMPtr<nsIDOMElement> elem = do_QueryInterface(aNode);
646 // remove the property from the style attribute
647 nsresult res = RemoveCSSProperty(elem, aProperty, aPropertyValue, PR_FALSE);
648 if (NS_FAILED(res)) return res;
650 if (nsEditor::NodeIsType(aNode, nsEditProperty::span)) {
651 nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
652 PRUint32 attrCount = content->GetAttrCount();
654 if (0 == attrCount) {
655 // no more attributes on this span, let's remove the element
656 res = mHTMLEditor->RemoveContainer(aNode);
657 if (NS_FAILED(res)) return res;
659 else if (1 == attrCount) {
660 // incredible hack in case the only remaining attribute is a _moz_dirty...
661 if (content->GetAttrNameAt(0)->Equals(nsEditProperty::mozdirty)) {
662 res = mHTMLEditor->RemoveContainer(aNode);
663 if (NS_FAILED(res)) return res;
667 return NS_OK;
670 // Answers true is the property can be removed by setting a "none" CSS value
671 // on a node
672 PRBool
673 nsHTMLCSSUtils::IsCSSInvertable(nsIAtom *aProperty, const nsAString *aAttribute)
675 return PRBool(nsEditProperty::b == aProperty);
678 // Get the default browser background color if we need it for GetCSSBackgroundColorState
679 nsresult
680 nsHTMLCSSUtils::GetDefaultBackgroundColor(nsAString & aColor)
682 nsresult result;
683 nsCOMPtr<nsIPrefBranch> prefBranch =
684 do_GetService(NS_PREFSERVICE_CONTRACTID, &result);
685 if (NS_FAILED(result)) return result;
686 aColor.AssignLiteral("#ffffff");
687 nsXPIDLCString returnColor;
688 if (prefBranch) {
689 PRBool useCustomColors;
690 result = prefBranch->GetBoolPref("editor.use_custom_colors", &useCustomColors);
691 if (NS_FAILED(result)) return result;
692 if (useCustomColors) {
693 result = prefBranch->GetCharPref("editor.background_color",
694 getter_Copies(returnColor));
695 if (NS_FAILED(result)) return result;
697 else {
698 PRBool useSystemColors;
699 result = prefBranch->GetBoolPref("browser.display.use_system_colors", &useSystemColors);
700 if (NS_FAILED(result)) return result;
701 if (!useSystemColors) {
702 result = prefBranch->GetCharPref("browser.display.background_color",
703 getter_Copies(returnColor));
704 if (NS_FAILED(result)) return result;
708 if (returnColor) {
709 CopyASCIItoUTF16(returnColor, aColor);
711 return NS_OK;
714 // Get the default length unit used for CSS Indent/Outdent
715 nsresult
716 nsHTMLCSSUtils::GetDefaultLengthUnit(nsAString & aLengthUnit)
718 nsresult result;
719 nsCOMPtr<nsIPrefBranch> prefBranch =
720 do_GetService(NS_PREFSERVICE_CONTRACTID, &result);
721 if (NS_FAILED(result)) return result;
722 aLengthUnit.AssignLiteral("px");
723 if (NS_SUCCEEDED(result) && prefBranch) {
724 nsXPIDLCString returnLengthUnit;
725 result = prefBranch->GetCharPref("editor.css.default_length_unit",
726 getter_Copies(returnLengthUnit));
727 if (NS_FAILED(result)) return result;
728 if (returnLengthUnit) {
729 CopyASCIItoUTF16(returnLengthUnit, aLengthUnit);
732 return NS_OK;
735 // Unfortunately, CSSStyleDeclaration::GetPropertyCSSValue is not yet implemented...
736 // We need then a way to determine the number part and the unit from aString, aString
737 // being the result of a GetPropertyValue query...
738 void
739 nsHTMLCSSUtils::ParseLength(const nsAString & aString, float * aValue, nsIAtom ** aUnit)
741 nsAString::const_iterator iter;
742 aString.BeginReading(iter);
744 float a = 10.0f , b = 1.0f, value = 0;
745 PRInt8 sign = 1;
746 PRInt32 i = 0, j = aString.Length();
747 PRUnichar c;
748 PRBool floatingPointFound = PR_FALSE;
749 c = *iter;
750 if (PRUnichar('-') == c) { sign = -1; iter++; i++; }
751 else if (PRUnichar('+') == c) { iter++; i++; }
752 while (i < j) {
753 c = *iter;
754 if ((PRUnichar('0') == c) ||
755 (PRUnichar('1') == c) ||
756 (PRUnichar('2') == c) ||
757 (PRUnichar('3') == c) ||
758 (PRUnichar('4') == c) ||
759 (PRUnichar('5') == c) ||
760 (PRUnichar('6') == c) ||
761 (PRUnichar('7') == c) ||
762 (PRUnichar('8') == c) ||
763 (PRUnichar('9') == c)) {
764 value = (value * a) + (b * (c - PRUnichar('0')));
765 b = b / 10 * a;
767 else if (!floatingPointFound && (PRUnichar('.') == c)) {
768 floatingPointFound = PR_TRUE;
769 a = 1.0f; b = 0.1f;
771 else break;
772 iter++;
773 i++;
775 *aValue = value * sign;
776 *aUnit = NS_NewAtom(StringTail(aString, j-i));
779 void
780 nsHTMLCSSUtils::GetCSSPropertyAtom(nsCSSEditableProperty aProperty, nsIAtom ** aAtom)
782 *aAtom = nsnull;
783 switch (aProperty) {
784 case eCSSEditableProperty_background_color:
785 *aAtom = nsEditProperty::cssBackgroundColor;
786 break;
787 case eCSSEditableProperty_background_image:
788 *aAtom = nsEditProperty::cssBackgroundImage;
789 break;
790 case eCSSEditableProperty_border:
791 *aAtom = nsEditProperty::cssBorder;
792 break;
793 case eCSSEditableProperty_caption_side:
794 *aAtom = nsEditProperty::cssCaptionSide;
795 break;
796 case eCSSEditableProperty_color:
797 *aAtom = nsEditProperty::cssColor;
798 break;
799 case eCSSEditableProperty_float:
800 *aAtom = nsEditProperty::cssFloat;
801 break;
802 case eCSSEditableProperty_font_family:
803 *aAtom = nsEditProperty::cssFontFamily;
804 break;
805 case eCSSEditableProperty_font_size:
806 *aAtom = nsEditProperty::cssFontSize;
807 break;
808 case eCSSEditableProperty_font_style:
809 *aAtom = nsEditProperty::cssFontStyle;
810 break;
811 case eCSSEditableProperty_font_weight:
812 *aAtom = nsEditProperty::cssFontWeight;
813 break;
814 case eCSSEditableProperty_height:
815 *aAtom = nsEditProperty::cssHeight;
816 break;
817 case eCSSEditableProperty_list_style_type:
818 *aAtom = nsEditProperty::cssListStyleType;
819 break;
820 case eCSSEditableProperty_margin_left:
821 *aAtom = nsEditProperty::cssMarginLeft;
822 break;
823 case eCSSEditableProperty_margin_right:
824 *aAtom = nsEditProperty::cssMarginRight;
825 break;
826 case eCSSEditableProperty_text_align:
827 *aAtom = nsEditProperty::cssTextAlign;
828 break;
829 case eCSSEditableProperty_text_decoration:
830 *aAtom = nsEditProperty::cssTextDecoration;
831 break;
832 case eCSSEditableProperty_vertical_align:
833 *aAtom = nsEditProperty::cssVerticalAlign;
834 break;
835 case eCSSEditableProperty_whitespace:
836 *aAtom = nsEditProperty::cssWhitespace;
837 break;
838 case eCSSEditableProperty_width:
839 *aAtom = nsEditProperty::cssWidth;
840 break;
841 case eCSSEditableProperty_NONE:
842 // intentionally empty
843 break;
847 // Populate aProperty and aValueArray with the CSS declarations equivalent to the
848 // value aValue according to the equivalence table aEquivTable
849 void
850 nsHTMLCSSUtils::BuildCSSDeclarations(nsTArray<nsIAtom*> & aPropertyArray,
851 nsTArray<nsString> & aValueArray,
852 const CSSEquivTable * aEquivTable,
853 const nsAString * aValue,
854 PRBool aGetOrRemoveRequest)
856 // clear arrays
857 aPropertyArray.Clear();
858 aValueArray.Clear();
860 // if we have an input value, let's use it
861 nsAutoString value, lowerCasedValue;
862 if (aValue) {
863 value.Assign(*aValue);
864 lowerCasedValue.Assign(*aValue);
865 ToLowerCase(lowerCasedValue);
868 PRInt8 index = 0;
869 nsCSSEditableProperty cssProperty = aEquivTable[index].cssProperty;
870 while (cssProperty) {
871 if (!aGetOrRemoveRequest|| aEquivTable[index].gettable) {
872 nsAutoString cssValue, cssPropertyString;
873 nsIAtom * cssPropertyAtom;
874 // find the equivalent css value for the index-th property in
875 // the equivalence table
876 (*aEquivTable[index].processValueFunctor) ((!aGetOrRemoveRequest || aEquivTable[index].caseSensitiveValue) ? &value : &lowerCasedValue,
877 cssValue,
878 aEquivTable[index].defaultValue,
879 aEquivTable[index].prependValue,
880 aEquivTable[index].appendValue);
881 GetCSSPropertyAtom(cssProperty, &cssPropertyAtom);
882 aPropertyArray.AppendElement(cssPropertyAtom);
883 aValueArray.AppendElement(cssValue);
885 index++;
886 cssProperty = aEquivTable[index].cssProperty;
890 // Populate cssPropertyArray and cssValueArray with the declarations equivalent
891 // to aHTMLProperty/aAttribute/aValue for the node aNode
892 void
893 nsHTMLCSSUtils::GenerateCSSDeclarationsFromHTMLStyle(nsIDOMNode * aNode,
894 nsIAtom *aHTMLProperty,
895 const nsAString * aAttribute,
896 const nsAString * aValue,
897 nsTArray<nsIAtom*> & cssPropertyArray,
898 nsTArray<nsString> & cssValueArray,
899 PRBool aGetOrRemoveRequest)
901 nsCOMPtr<nsIDOMNode> node = aNode;
902 if (mHTMLEditor->IsTextNode(aNode)) {
903 aNode->GetParentNode(getter_AddRefs(node));
905 if (!node) return;
907 nsIAtom *tagName = nsEditor::GetTag(node);
909 if (nsEditProperty::b == aHTMLProperty) {
910 BuildCSSDeclarations(cssPropertyArray, cssValueArray, boldEquivTable, aValue, aGetOrRemoveRequest);
912 else if (nsEditProperty::i == aHTMLProperty) {
913 BuildCSSDeclarations(cssPropertyArray, cssValueArray, italicEquivTable, aValue, aGetOrRemoveRequest);
915 else if (nsEditProperty::u == aHTMLProperty) {
916 BuildCSSDeclarations(cssPropertyArray, cssValueArray, underlineEquivTable, aValue, aGetOrRemoveRequest);
918 else if (nsEditProperty::strike == aHTMLProperty) {
919 BuildCSSDeclarations(cssPropertyArray, cssValueArray, strikeEquivTable, aValue, aGetOrRemoveRequest);
921 else if (nsEditProperty::tt == aHTMLProperty) {
922 BuildCSSDeclarations(cssPropertyArray, cssValueArray, ttEquivTable, aValue, aGetOrRemoveRequest);
924 else if (aAttribute) {
925 if (nsEditProperty::font == aHTMLProperty &&
926 aAttribute->EqualsLiteral("color")) {
927 BuildCSSDeclarations(cssPropertyArray, cssValueArray, fontColorEquivTable, aValue, aGetOrRemoveRequest);
929 else if (nsEditProperty::font == aHTMLProperty &&
930 aAttribute->EqualsLiteral("face")) {
931 BuildCSSDeclarations(cssPropertyArray, cssValueArray, fontFaceEquivTable, aValue, aGetOrRemoveRequest);
933 else if (aAttribute->EqualsLiteral("bgcolor")) {
934 BuildCSSDeclarations(cssPropertyArray, cssValueArray, bgcolorEquivTable, aValue, aGetOrRemoveRequest);
936 else if (aAttribute->EqualsLiteral("background")) {
937 BuildCSSDeclarations(cssPropertyArray, cssValueArray, backgroundImageEquivTable, aValue, aGetOrRemoveRequest);
939 else if (aAttribute->EqualsLiteral("text")) {
940 BuildCSSDeclarations(cssPropertyArray, cssValueArray, textColorEquivTable, aValue, aGetOrRemoveRequest);
942 else if (aAttribute->EqualsLiteral("border")) {
943 BuildCSSDeclarations(cssPropertyArray, cssValueArray, borderEquivTable, aValue, aGetOrRemoveRequest);
945 else if (aAttribute->EqualsLiteral("align")) {
946 if (nsEditProperty::table == tagName) {
947 BuildCSSDeclarations(cssPropertyArray, cssValueArray, tableAlignEquivTable, aValue, aGetOrRemoveRequest);
949 else if (nsEditProperty::hr == tagName) {
950 BuildCSSDeclarations(cssPropertyArray, cssValueArray, hrAlignEquivTable, aValue, aGetOrRemoveRequest);
952 else if (nsEditProperty::legend == tagName ||
953 nsEditProperty::caption == tagName) {
954 BuildCSSDeclarations(cssPropertyArray, cssValueArray, captionAlignEquivTable, aValue, aGetOrRemoveRequest);
956 else {
957 BuildCSSDeclarations(cssPropertyArray, cssValueArray, textAlignEquivTable, aValue, aGetOrRemoveRequest);
960 else if (aAttribute->EqualsLiteral("valign")) {
961 BuildCSSDeclarations(cssPropertyArray, cssValueArray, verticalAlignEquivTable, aValue, aGetOrRemoveRequest);
963 else if (aAttribute->EqualsLiteral("nowrap")) {
964 BuildCSSDeclarations(cssPropertyArray, cssValueArray, nowrapEquivTable, aValue, aGetOrRemoveRequest);
966 else if (aAttribute->EqualsLiteral("width")) {
967 BuildCSSDeclarations(cssPropertyArray, cssValueArray, widthEquivTable, aValue, aGetOrRemoveRequest);
969 else if (aAttribute->EqualsLiteral("height") ||
970 (nsEditProperty::hr == tagName && aAttribute->EqualsLiteral("size"))) {
971 BuildCSSDeclarations(cssPropertyArray, cssValueArray, heightEquivTable, aValue, aGetOrRemoveRequest);
973 else if (aAttribute->EqualsLiteral("type") &&
974 (nsEditProperty::ol == tagName
975 || nsEditProperty::ul == tagName
976 || nsEditProperty::li == tagName)) {
977 BuildCSSDeclarations(cssPropertyArray, cssValueArray, listStyleTypeEquivTable, aValue, aGetOrRemoveRequest);
982 // Add to aNode the CSS inline style equivalent to HTMLProperty/aAttribute/aValue for the node,
983 // and return in aCount the number of CSS properties set by the call
984 nsresult
985 nsHTMLCSSUtils::SetCSSEquivalentToHTMLStyle(nsIDOMNode * aNode,
986 nsIAtom *aHTMLProperty,
987 const nsAString *aAttribute,
988 const nsAString *aValue,
989 PRInt32 * aCount,
990 PRBool aSuppressTransaction)
992 nsCOMPtr<nsIDOMElement> theElement = do_QueryInterface(aNode);
993 nsresult res = NS_OK;
994 *aCount = 0;
995 if (theElement && IsCSSEditableProperty(aNode, aHTMLProperty, aAttribute)) {
996 // we can apply the styles only if the node is an element and if we have
997 // an equivalence for the requested HTML style in this implementation
999 // Find the CSS equivalence to the HTML style
1000 nsTArray<nsIAtom*> cssPropertyArray;
1001 nsTArray<nsString> cssValueArray;
1002 GenerateCSSDeclarationsFromHTMLStyle(aNode, aHTMLProperty, aAttribute, aValue,
1003 cssPropertyArray, cssValueArray, PR_FALSE);
1005 // set the individual CSS inline styles
1006 *aCount = cssPropertyArray.Length();
1007 PRInt32 index;
1008 for (index = 0; index < *aCount; index++) {
1009 nsCOMPtr<nsIDOMElement> theElement = do_QueryInterface(aNode);
1010 res = SetCSSProperty(theElement, cssPropertyArray[index],
1011 cssValueArray[index], aSuppressTransaction);
1012 if (NS_FAILED(res)) return res;
1015 return NS_OK;
1018 // Remove from aNode the CSS inline style equivalent to HTMLProperty/aAttribute/aValue for the node
1019 nsresult
1020 nsHTMLCSSUtils::RemoveCSSEquivalentToHTMLStyle(nsIDOMNode * aNode,
1021 nsIAtom *aHTMLProperty,
1022 const nsAString *aAttribute,
1023 const nsAString *aValue,
1024 PRBool aSuppressTransaction)
1026 nsCOMPtr<nsIDOMElement> theElement = do_QueryInterface(aNode);
1027 nsresult res = NS_OK;
1028 PRInt32 count = 0;
1029 if (theElement && IsCSSEditableProperty(aNode, aHTMLProperty, aAttribute)) {
1030 // we can apply the styles only if the node is an element and if we have
1031 // an equivalence for the requested HTML style in this implementation
1033 // Find the CSS equivalence to the HTML style
1034 nsTArray<nsIAtom*> cssPropertyArray;
1035 nsTArray<nsString> cssValueArray;
1036 GenerateCSSDeclarationsFromHTMLStyle(aNode, aHTMLProperty, aAttribute, aValue,
1037 cssPropertyArray, cssValueArray, PR_TRUE);
1039 // remove the individual CSS inline styles
1040 count = cssPropertyArray.Length();
1041 PRInt32 index;
1042 for (index = 0; index < count; index++) {
1043 res = RemoveCSSProperty(theElement,
1044 cssPropertyArray[index],
1045 cssValueArray[index],
1046 aSuppressTransaction);
1047 if (NS_FAILED(res)) return res;
1050 return NS_OK;
1053 // aReturn is true if the element aElement carries an ID or a class.
1054 nsresult
1055 nsHTMLCSSUtils::HasClassOrID(nsIDOMElement * aElement, PRBool & aReturn)
1057 nsAutoString classVal, idVal;
1058 PRBool isClassSet, isIdSet;
1059 aReturn = PR_FALSE;
1061 nsresult res = mHTMLEditor->GetAttributeValue(aElement, NS_LITERAL_STRING("class"), classVal, &isClassSet);
1062 if (NS_FAILED(res)) return res;
1063 res = mHTMLEditor->GetAttributeValue(aElement, NS_LITERAL_STRING("id"), idVal, &isIdSet);
1064 if (NS_FAILED(res)) return res;
1066 // we need to make sure that if the element has an id or a class attribute,
1067 // the attribute is not the empty string
1068 aReturn = ((isClassSet && !classVal.IsEmpty()) ||
1069 (isIdSet && !idVal.IsEmpty()));
1070 return NS_OK;
1073 // returns in aValueString the list of values for the CSS equivalences to
1074 // the HTML style aHTMLProperty/aAttribute/aValueString for the node aNode;
1075 // the value of aStyleType controls the styles we retrieve : specified or
1076 // computed.
1077 nsresult
1078 nsHTMLCSSUtils::GetCSSEquivalentToHTMLInlineStyleSet(nsIDOMNode * aNode,
1079 nsIAtom *aHTMLProperty,
1080 const nsAString *aAttribute,
1081 nsAString & aValueString,
1082 PRUint8 aStyleType)
1084 aValueString.Truncate();
1085 nsCOMPtr<nsIDOMElement> theElement;
1086 nsresult res = GetElementContainerOrSelf(aNode, getter_AddRefs(theElement));
1087 if (NS_FAILED(res)) return res;
1089 if (theElement && IsCSSEditableProperty(theElement, aHTMLProperty, aAttribute)) {
1090 // Yes, the requested HTML style has a CSS equivalence in this implementation
1091 // Retrieve the default ViewCSS if we are asked for computed styles
1092 nsCOMPtr<nsIDOMViewCSS> viewCSS = nsnull;
1093 if (COMPUTED_STYLE_TYPE == aStyleType) {
1094 res = GetDefaultViewCSS(theElement, getter_AddRefs(viewCSS));
1095 if (NS_FAILED(res)) return res;
1097 nsTArray<nsIAtom*> cssPropertyArray;
1098 nsTArray<nsString> cssValueArray;
1099 // get the CSS equivalence with last param PR_TRUE indicating we want only the
1100 // "gettable" properties
1101 GenerateCSSDeclarationsFromHTMLStyle(theElement, aHTMLProperty, aAttribute, nsnull,
1102 cssPropertyArray, cssValueArray, PR_TRUE);
1103 PRInt32 count = cssPropertyArray.Length();
1104 PRInt32 index;
1105 for (index = 0; index < count; index++) {
1106 nsAutoString valueString;
1107 // retrieve the specified/computed value of the property
1108 res = GetCSSInlinePropertyBase(theElement, cssPropertyArray[index],
1109 valueString, viewCSS, aStyleType);
1110 if (NS_FAILED(res)) return res;
1111 // append the value to aValueString (possibly with a leading whitespace)
1112 if (index) aValueString.Append(PRUnichar(' '));
1113 aValueString.Append(valueString);
1116 return NS_OK;
1119 // Does the node aNode (or his parent if it is not an element node) carries
1120 // the CSS equivalent styles to the HTML style aHTMLProperty/aAttribute/
1121 // aValueString for this node ?
1122 // The value of aStyleType controls the styles we retrieve : specified or
1123 // computed. The return value aIsSet is true is the CSS styles are set.
1124 nsresult
1125 nsHTMLCSSUtils::IsCSSEquivalentToHTMLInlineStyleSet(nsIDOMNode * aNode,
1126 nsIAtom *aHTMLProperty,
1127 const nsAString * aHTMLAttribute,
1128 PRBool & aIsSet,
1129 nsAString & valueString,
1130 PRUint8 aStyleType)
1132 NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER);
1134 nsAutoString htmlValueString(valueString);
1135 aIsSet = PR_FALSE;
1136 nsCOMPtr<nsIDOMNode> node = aNode;
1137 NS_NAMED_LITERAL_STRING(boldStr, "bold");
1138 do {
1139 valueString.Assign(htmlValueString);
1140 // get the value of the CSS equivalent styles
1141 nsresult res = GetCSSEquivalentToHTMLInlineStyleSet(node, aHTMLProperty, aHTMLAttribute,
1142 valueString, aStyleType);
1143 if (NS_FAILED(res)) return res;
1145 // early way out if we can
1146 if (valueString.IsEmpty()) return NS_OK;
1148 if (nsEditProperty::b == aHTMLProperty) {
1149 if (valueString.Equals(boldStr)) {
1150 aIsSet = PR_TRUE;
1152 else if (valueString.EqualsLiteral("normal")) {
1153 aIsSet = PR_FALSE;
1155 else if (valueString.EqualsLiteral("bolder")) {
1156 aIsSet = PR_TRUE;
1157 valueString.Assign(boldStr);
1159 else {
1160 PRInt32 weight = 0;
1161 PRInt32 errorCode;
1162 nsAutoString value(valueString);
1163 weight = value.ToInteger(&errorCode, 10);
1164 if (400 < weight) {
1165 aIsSet = PR_TRUE;
1166 valueString.Assign(boldStr);
1168 else {
1169 aIsSet = PR_FALSE;
1170 valueString.AssignLiteral("normal");
1175 else if (nsEditProperty::i == aHTMLProperty) {
1176 if (valueString.EqualsLiteral("italic") ||
1177 valueString.EqualsLiteral("oblique")) {
1178 aIsSet= PR_TRUE;
1182 else if (nsEditProperty::u == aHTMLProperty) {
1183 nsAutoString val;
1184 val.AssignLiteral("underline");
1185 aIsSet = PRBool(ChangeCSSInlineStyleTxn::ValueIncludes(valueString, val, PR_FALSE));
1188 else if (nsEditProperty::strike == aHTMLProperty) {
1189 nsAutoString val;
1190 val.AssignLiteral("line-through");
1191 aIsSet = PRBool(ChangeCSSInlineStyleTxn::ValueIncludes(valueString, val, PR_FALSE));
1194 else if (aHTMLAttribute &&
1195 ( (nsEditProperty::font == aHTMLProperty &&
1196 aHTMLAttribute->EqualsLiteral("color")) ||
1197 aHTMLAttribute->EqualsLiteral("bgcolor"))) {
1198 if (htmlValueString.IsEmpty())
1199 aIsSet = PR_TRUE;
1200 else {
1201 nscolor rgba;
1202 nsAutoString subStr;
1203 htmlValueString.Right(subStr, htmlValueString.Length()-1);
1204 if (NS_ColorNameToRGB(htmlValueString, &rgba) ||
1205 NS_HexToRGB(subStr, &rgba)) {
1206 nsAutoString htmlColor, tmpStr;
1207 htmlColor.AppendLiteral("rgb(");
1209 NS_NAMED_LITERAL_STRING(comma, ", ");
1211 tmpStr.AppendInt(NS_GET_R(rgba), 10);
1212 htmlColor.Append(tmpStr + comma);
1214 tmpStr.Truncate();
1215 tmpStr.AppendInt(NS_GET_G(rgba), 10);
1216 htmlColor.Append(tmpStr + comma);
1218 tmpStr.Truncate();
1219 tmpStr.AppendInt(NS_GET_B(rgba), 10);
1220 htmlColor.Append(tmpStr);
1222 htmlColor.Append(PRUnichar(')'));
1223 aIsSet = htmlColor.Equals(valueString,
1224 nsCaseInsensitiveStringComparator());
1226 else
1227 aIsSet = htmlValueString.Equals(valueString,
1228 nsCaseInsensitiveStringComparator());
1232 else if (nsEditProperty::tt == aHTMLProperty) {
1233 aIsSet = StringBeginsWith(valueString, NS_LITERAL_STRING("monospace"));
1236 else if ((nsEditProperty::font == aHTMLProperty) && aHTMLAttribute
1237 && aHTMLAttribute->EqualsLiteral("face")) {
1238 if (!htmlValueString.IsEmpty()) {
1239 const PRUnichar commaSpace[] = { PRUnichar(','), PRUnichar(' '), 0 };
1240 const PRUnichar comma[] = { PRUnichar(','), 0 };
1241 htmlValueString.ReplaceSubstring(commaSpace, comma);
1242 nsAutoString valueStringNorm(valueString);
1243 valueStringNorm.ReplaceSubstring(commaSpace, comma);
1244 aIsSet = htmlValueString.Equals(valueStringNorm,
1245 nsCaseInsensitiveStringComparator());
1247 else {
1248 // ignore this, it's TT or our default
1249 nsAutoString valueStringLower;
1250 ToLowerCase(valueString, valueStringLower);
1251 aIsSet = !valueStringLower.EqualsLiteral("monospace") &&
1252 !valueStringLower.EqualsLiteral("serif");
1254 return NS_OK;
1256 else if (aHTMLAttribute
1257 && aHTMLAttribute->EqualsLiteral("align")) {
1258 aIsSet = PR_TRUE;
1260 else {
1261 aIsSet = PR_FALSE;
1262 return NS_OK;
1265 if (!htmlValueString.IsEmpty()) {
1266 if (htmlValueString.Equals(valueString,
1267 nsCaseInsensitiveStringComparator())) {
1268 aIsSet = PR_TRUE;
1272 if (nsEditProperty::u == aHTMLProperty || nsEditProperty::strike == aHTMLProperty) {
1273 // unfortunately, the value of the text-decoration property is not inherited.
1274 // that means that we have to look at ancestors of node to see if they are underlined
1275 nsCOMPtr<nsIDOMNode> tmp;
1276 res = node->GetParentNode(getter_AddRefs(tmp));
1277 if (NS_FAILED(res)) return res;
1278 nsCOMPtr<nsIDOMElement> element = do_QueryInterface(tmp);
1279 node = element; // set to null if it's not a dom element
1281 } while ((nsEditProperty::u == aHTMLProperty || nsEditProperty::strike == aHTMLProperty) &&
1282 !aIsSet && node);
1283 return NS_OK;
1286 nsresult
1287 nsHTMLCSSUtils::SetCSSEnabled(PRBool aIsCSSPrefChecked)
1289 mIsCSSPrefChecked = aIsCSSPrefChecked;
1290 return NS_OK;
1293 PRBool
1294 nsHTMLCSSUtils::IsCSSPrefChecked()
1296 return mIsCSSPrefChecked ;
1299 // ElementsSameStyle compares two elements and checks if they have the same
1300 // specified CSS declarations in the STYLE attribute
1301 // The answer is always negative if at least one of them carries an ID or a class
1302 PRBool
1303 nsHTMLCSSUtils::ElementsSameStyle(nsIDOMNode *aFirstNode, nsIDOMNode *aSecondNode)
1305 nsresult res;
1306 nsCOMPtr<nsIDOMElement> firstElement = do_QueryInterface(aFirstNode);
1307 nsCOMPtr<nsIDOMElement> secondElement = do_QueryInterface(aSecondNode);
1309 NS_ASSERTION((firstElement && secondElement), "Non element nodes passed to ElementsSameStyle.");
1311 nsAutoString firstID, secondID;
1312 PRBool isFirstIDSet, isSecondIDSet;
1313 res = mHTMLEditor->GetAttributeValue(firstElement, NS_LITERAL_STRING("id"), firstID, &isFirstIDSet);
1314 res = mHTMLEditor->GetAttributeValue(secondElement, NS_LITERAL_STRING("id"), secondID, &isSecondIDSet);
1315 if (isFirstIDSet || isSecondIDSet) {
1316 // at least one of the spans carries an ID ; suspect a CSS rule applies to it and
1317 // refuse to merge the nodes
1318 return PR_FALSE;
1321 nsAutoString firstClass, secondClass;
1322 PRBool isFirstClassSet, isSecondClassSet;
1323 res = mHTMLEditor->GetAttributeValue(firstElement, NS_LITERAL_STRING("class"), firstClass, &isFirstClassSet);
1324 res = mHTMLEditor->GetAttributeValue(secondElement, NS_LITERAL_STRING("class"), secondClass, &isSecondClassSet);
1325 if (isFirstClassSet && isSecondClassSet) {
1326 // both spans carry a class, let's compare them
1327 if (!firstClass.Equals(secondClass)) {
1328 // WARNING : technically, the comparison just above is questionable :
1329 // from a pure HTML/CSS point of view class="a b" is NOT the same than
1330 // class="b a" because a CSS rule could test the exact value of the class
1331 // attribute to be "a b" for instance ; from a user's point of view, a
1332 // wysiwyg editor should probably NOT make any difference. CSS people
1333 // need to discuss this issue before any modification.
1334 return PR_FALSE;
1337 else if (isFirstClassSet || isSecondClassSet) {
1338 // one span only carries a class, early way out
1339 return PR_FALSE;
1342 nsCOMPtr<nsIDOMCSSStyleDeclaration> firstCSSDecl, secondCSSDecl;
1343 PRUint32 firstLength, secondLength;
1344 res = GetInlineStyles(firstElement, getter_AddRefs(firstCSSDecl), &firstLength);
1345 if (NS_FAILED(res) || !firstCSSDecl) return PR_FALSE;
1346 res = GetInlineStyles(secondElement, getter_AddRefs(secondCSSDecl), &secondLength);
1347 if (NS_FAILED(res) || !secondCSSDecl) return PR_FALSE;
1349 if (firstLength != secondLength) {
1350 // early way out if we can
1351 return PR_FALSE;
1353 else if (0 == firstLength) {
1354 // no inline style !
1355 return PR_TRUE;
1358 PRUint32 i;
1359 nsAutoString propertyNameString;
1360 nsAutoString firstValue, secondValue;
1361 for (i=0; i<firstLength; i++) {
1362 firstCSSDecl->Item(i, propertyNameString);
1363 firstCSSDecl->GetPropertyValue(propertyNameString, firstValue);
1364 secondCSSDecl->GetPropertyValue(propertyNameString, secondValue);
1365 if (!firstValue.Equals(secondValue)) {
1366 return PR_FALSE;
1369 for (i=0; i<secondLength; i++) {
1370 secondCSSDecl->Item(i, propertyNameString);
1371 secondCSSDecl->GetPropertyValue(propertyNameString, secondValue);
1372 firstCSSDecl->GetPropertyValue(propertyNameString, firstValue);
1373 if (!firstValue.Equals(secondValue)) {
1374 return PR_FALSE;
1378 return PR_TRUE;
1381 nsresult
1382 nsHTMLCSSUtils::GetInlineStyles(nsIDOMElement *aElement,
1383 nsIDOMCSSStyleDeclaration **aCssDecl,
1384 PRUint32 *aLength)
1386 if (!aElement || !aLength) return NS_ERROR_NULL_POINTER;
1387 *aLength = 0;
1388 nsCOMPtr<nsIDOMElementCSSInlineStyle> inlineStyles = do_QueryInterface(aElement);
1389 if (!inlineStyles) return NS_ERROR_NULL_POINTER;
1390 nsresult res = inlineStyles->GetStyle(aCssDecl);
1391 if (NS_FAILED(res) || !aCssDecl) return NS_ERROR_NULL_POINTER;
1392 (*aCssDecl)->GetLength(aLength);
1393 return NS_OK;
1396 nsresult
1397 nsHTMLCSSUtils::GetElementContainerOrSelf(nsIDOMNode * aNode, nsIDOMElement ** aElement)
1399 NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER);
1401 nsCOMPtr<nsIDOMNode> node=aNode, parentNode;
1402 PRUint16 type;
1403 nsresult res;
1404 res = node->GetNodeType(&type);
1405 if (NS_FAILED(res)) return res;
1407 if (nsIDOMNode::DOCUMENT_NODE == type) {
1408 return NS_ERROR_NULL_POINTER;
1411 // loop until we find an element
1412 while (node && nsIDOMNode::ELEMENT_NODE != type) {
1413 parentNode = node;
1414 res = parentNode->GetParentNode(getter_AddRefs(node));
1415 if (NS_FAILED(res)) return res;
1416 if (node) {
1417 res = node->GetNodeType(&type);
1418 if (NS_FAILED(res)) return res;
1421 NS_ASSERTION(node, "we reached a null node ancestor !");
1422 NS_ENSURE_TRUE(node, NS_ERROR_NULL_POINTER);
1423 nsCOMPtr<nsIDOMElement> element = do_QueryInterface(node);
1424 (*aElement) = element;
1425 NS_IF_ADDREF(*aElement);
1426 return NS_OK;
1429 nsresult
1430 nsHTMLCSSUtils::SetCSSProperty(nsIDOMElement * aElement,
1431 const nsAString & aProperty,
1432 const nsAString & aValue)
1434 nsCOMPtr<nsIDOMCSSStyleDeclaration> cssDecl;
1435 PRUint32 length;
1436 nsresult res = GetInlineStyles(aElement, getter_AddRefs(cssDecl), &length);
1437 if (NS_FAILED(res) || !cssDecl) return res;
1439 return cssDecl->SetProperty(aProperty,
1440 aValue,
1441 EmptyString());
1444 nsresult
1445 nsHTMLCSSUtils::SetCSSPropertyPixels(nsIDOMElement * aElement,
1446 const nsAString & aProperty,
1447 PRInt32 aIntValue)
1449 nsAutoString s;
1450 s.AppendInt(aIntValue);
1451 return SetCSSProperty(aElement, aProperty, s + NS_LITERAL_STRING("px"));
1454 nsresult
1455 nsHTMLCSSUtils::RemoveCSSProperty(nsIDOMElement * aElement,
1456 const nsAString & aProperty)
1458 nsCOMPtr<nsIDOMCSSStyleDeclaration> cssDecl;
1459 PRUint32 length;
1460 nsresult res = GetInlineStyles(aElement, getter_AddRefs(cssDecl), &length);
1461 if (NS_FAILED(res) || !cssDecl) return res;
1463 nsAutoString returnString;
1464 return cssDecl->RemoveProperty(aProperty, returnString);