CLOSED TREE: TraceMonkey merge head. (a=blockers)
[mozilla-central.git] / editor / libeditor / html / nsHTMLCSSUtils.cpp
blob884a00d46909ed26b8a261f3001af7ab56a2736f
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 NS_ENSURE_SUCCESS(result, 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 NS_ENSURE_TRUE(content, 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 NS_ENSURE_TRUE(aElement, NS_ERROR_NULL_POINTER);
524 *aTxn = new ChangeCSSInlineStyleTxn();
525 NS_ENSURE_TRUE(*aTxn, NS_ERROR_OUT_OF_MEMORY);
526 NS_ADDREF(*aTxn);
527 return (*aTxn)->Init(mHTMLEditor, aElement, aAttribute, aValue, aRemoveProperty);
530 nsresult
531 nsHTMLCSSUtils::GetSpecifiedProperty(nsIDOMNode *aNode, nsIAtom *aProperty,
532 nsAString & aValue)
534 return GetCSSInlinePropertyBase(aNode, aProperty, aValue, nsnull, SPECIFIED_STYLE_TYPE);
537 nsresult
538 nsHTMLCSSUtils::GetComputedProperty(nsIDOMNode *aNode, nsIAtom *aProperty,
539 nsAString & aValue)
541 nsCOMPtr<nsIDOMViewCSS> viewCSS = nsnull;
542 nsresult res = GetDefaultViewCSS(aNode, getter_AddRefs(viewCSS));
543 NS_ENSURE_SUCCESS(res, res);
545 return GetCSSInlinePropertyBase(aNode, aProperty, aValue, viewCSS, COMPUTED_STYLE_TYPE);
548 nsresult
549 nsHTMLCSSUtils::GetCSSInlinePropertyBase(nsIDOMNode *aNode, nsIAtom *aProperty,
550 nsAString &aValue,
551 nsIDOMViewCSS *aViewCSS,
552 PRUint8 aStyleType)
554 aValue.Truncate();
555 NS_ENSURE_TRUE(aProperty, NS_ERROR_NULL_POINTER);
557 nsCOMPtr<nsIDOMElement>element;
558 nsresult res = GetElementContainerOrSelf(aNode, getter_AddRefs(element));
559 NS_ENSURE_SUCCESS(res, res);
561 switch (aStyleType) {
562 case COMPUTED_STYLE_TYPE:
563 if (element && aViewCSS) {
564 nsAutoString value, propString;
565 nsCOMPtr<nsIDOMCSSStyleDeclaration> cssDecl;
566 aProperty->ToString(propString);
567 // Get the all the computed css styles attached to the element node
568 res = aViewCSS->GetComputedStyle(element, EmptyString(), getter_AddRefs(cssDecl));
569 if (NS_FAILED(res) || !cssDecl) return res;
570 // from these declarations, get the one we want and that one only
571 res = cssDecl->GetPropertyValue(propString, value);
572 NS_ENSURE_SUCCESS(res, res);
573 aValue.Assign(value);
575 break;
576 case SPECIFIED_STYLE_TYPE:
577 if (element) {
578 nsCOMPtr<nsIDOMCSSStyleDeclaration> cssDecl;
579 PRUint32 length;
580 res = GetInlineStyles(element, getter_AddRefs(cssDecl), &length);
581 if (NS_FAILED(res) || !cssDecl) return res;
582 nsAutoString value, propString;
583 aProperty->ToString(propString);
584 res = cssDecl->GetPropertyValue(propString, value);
585 NS_ENSURE_SUCCESS(res, res);
586 aValue.Assign(value);
588 break;
590 return NS_OK;
593 nsresult
594 nsHTMLCSSUtils::GetDefaultViewCSS(nsIDOMNode *aNode, nsIDOMViewCSS **aViewCSS)
596 nsCOMPtr<nsIDOMElement>element;
597 nsresult res = GetElementContainerOrSelf(aNode, getter_AddRefs(element));
598 NS_ENSURE_SUCCESS(res, res);
600 // if we have an element node
601 if (element) {
602 // find the owner document
603 nsCOMPtr<nsIDOMDocument> doc;
604 nsCOMPtr<nsIDOMNode> node = do_QueryInterface(element);
605 res = node->GetOwnerDocument(getter_AddRefs(doc));
606 NS_ENSURE_SUCCESS(res, res);
607 if (doc) {
608 nsCOMPtr<nsIDOMDocumentView> documentView = do_QueryInterface(doc);
609 nsCOMPtr<nsIDOMAbstractView> abstractView;
610 // from the document, get the abtractView
611 res = documentView->GetDefaultView(getter_AddRefs(abstractView));
612 NS_ENSURE_SUCCESS(res, res);
613 if (abstractView) {
614 // from the abstractView, get the CSS view
615 CallQueryInterface(abstractView, aViewCSS);
616 return NS_OK;
620 *aViewCSS = nsnull;
621 return NS_OK;
624 nsresult
625 NS_NewHTMLCSSUtils(nsHTMLCSSUtils** aInstancePtrResult)
627 nsHTMLCSSUtils * rules = new nsHTMLCSSUtils();
628 if (rules) {
629 *aInstancePtrResult = rules;
630 return NS_OK;
633 *aInstancePtrResult = nsnull;
634 return NS_ERROR_OUT_OF_MEMORY;
637 // remove the CSS style "aProperty : aPropertyValue" and possibly remove the whole node
638 // if it is a span and if its only attribute is _moz_dirty
639 nsresult
640 nsHTMLCSSUtils::RemoveCSSInlineStyle(nsIDOMNode *aNode, nsIAtom *aProperty, const nsAString & aPropertyValue)
642 nsCOMPtr<nsIDOMElement> elem = do_QueryInterface(aNode);
644 // remove the property from the style attribute
645 nsresult res = RemoveCSSProperty(elem, aProperty, aPropertyValue, PR_FALSE);
646 NS_ENSURE_SUCCESS(res, res);
648 if (nsEditor::NodeIsType(aNode, nsEditProperty::span)) {
649 nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
650 PRUint32 attrCount = content->GetAttrCount();
652 if (0 == attrCount) {
653 // no more attributes on this span, let's remove the element
654 res = mHTMLEditor->RemoveContainer(aNode);
655 NS_ENSURE_SUCCESS(res, res);
657 else if (1 == attrCount) {
658 // incredible hack in case the only remaining attribute is a _moz_dirty...
659 if (content->GetAttrNameAt(0)->Equals(nsEditProperty::mozdirty)) {
660 res = mHTMLEditor->RemoveContainer(aNode);
661 NS_ENSURE_SUCCESS(res, res);
665 return NS_OK;
668 // Answers true is the property can be removed by setting a "none" CSS value
669 // on a node
670 PRBool
671 nsHTMLCSSUtils::IsCSSInvertable(nsIAtom *aProperty, const nsAString *aAttribute)
673 return PRBool(nsEditProperty::b == aProperty);
676 // Get the default browser background color if we need it for GetCSSBackgroundColorState
677 nsresult
678 nsHTMLCSSUtils::GetDefaultBackgroundColor(nsAString & aColor)
680 nsresult result;
681 nsCOMPtr<nsIPrefBranch> prefBranch =
682 do_GetService(NS_PREFSERVICE_CONTRACTID, &result);
683 NS_ENSURE_SUCCESS(result, result);
684 aColor.AssignLiteral("#ffffff");
685 nsXPIDLCString returnColor;
686 if (prefBranch) {
687 PRBool useCustomColors;
688 result = prefBranch->GetBoolPref("editor.use_custom_colors", &useCustomColors);
689 NS_ENSURE_SUCCESS(result, result);
690 if (useCustomColors) {
691 result = prefBranch->GetCharPref("editor.background_color",
692 getter_Copies(returnColor));
693 NS_ENSURE_SUCCESS(result, result);
695 else {
696 PRBool useSystemColors;
697 result = prefBranch->GetBoolPref("browser.display.use_system_colors", &useSystemColors);
698 NS_ENSURE_SUCCESS(result, result);
699 if (!useSystemColors) {
700 result = prefBranch->GetCharPref("browser.display.background_color",
701 getter_Copies(returnColor));
702 NS_ENSURE_SUCCESS(result, result);
706 if (returnColor) {
707 CopyASCIItoUTF16(returnColor, aColor);
709 return NS_OK;
712 // Get the default length unit used for CSS Indent/Outdent
713 nsresult
714 nsHTMLCSSUtils::GetDefaultLengthUnit(nsAString & aLengthUnit)
716 nsresult result;
717 nsCOMPtr<nsIPrefBranch> prefBranch =
718 do_GetService(NS_PREFSERVICE_CONTRACTID, &result);
719 NS_ENSURE_SUCCESS(result, result);
720 aLengthUnit.AssignLiteral("px");
721 if (NS_SUCCEEDED(result) && prefBranch) {
722 nsXPIDLCString returnLengthUnit;
723 result = prefBranch->GetCharPref("editor.css.default_length_unit",
724 getter_Copies(returnLengthUnit));
725 NS_ENSURE_SUCCESS(result, result);
726 if (returnLengthUnit) {
727 CopyASCIItoUTF16(returnLengthUnit, aLengthUnit);
730 return NS_OK;
733 // Unfortunately, CSSStyleDeclaration::GetPropertyCSSValue is not yet implemented...
734 // We need then a way to determine the number part and the unit from aString, aString
735 // being the result of a GetPropertyValue query...
736 void
737 nsHTMLCSSUtils::ParseLength(const nsAString & aString, float * aValue, nsIAtom ** aUnit)
739 nsAString::const_iterator iter;
740 aString.BeginReading(iter);
742 float a = 10.0f , b = 1.0f, value = 0;
743 PRInt8 sign = 1;
744 PRInt32 i = 0, j = aString.Length();
745 PRUnichar c;
746 PRBool floatingPointFound = PR_FALSE;
747 c = *iter;
748 if (PRUnichar('-') == c) { sign = -1; iter++; i++; }
749 else if (PRUnichar('+') == c) { iter++; i++; }
750 while (i < j) {
751 c = *iter;
752 if ((PRUnichar('0') == c) ||
753 (PRUnichar('1') == c) ||
754 (PRUnichar('2') == c) ||
755 (PRUnichar('3') == c) ||
756 (PRUnichar('4') == c) ||
757 (PRUnichar('5') == c) ||
758 (PRUnichar('6') == c) ||
759 (PRUnichar('7') == c) ||
760 (PRUnichar('8') == c) ||
761 (PRUnichar('9') == c)) {
762 value = (value * a) + (b * (c - PRUnichar('0')));
763 b = b / 10 * a;
765 else if (!floatingPointFound && (PRUnichar('.') == c)) {
766 floatingPointFound = PR_TRUE;
767 a = 1.0f; b = 0.1f;
769 else break;
770 iter++;
771 i++;
773 *aValue = value * sign;
774 *aUnit = NS_NewAtom(StringTail(aString, j-i));
777 void
778 nsHTMLCSSUtils::GetCSSPropertyAtom(nsCSSEditableProperty aProperty, nsIAtom ** aAtom)
780 *aAtom = nsnull;
781 switch (aProperty) {
782 case eCSSEditableProperty_background_color:
783 *aAtom = nsEditProperty::cssBackgroundColor;
784 break;
785 case eCSSEditableProperty_background_image:
786 *aAtom = nsEditProperty::cssBackgroundImage;
787 break;
788 case eCSSEditableProperty_border:
789 *aAtom = nsEditProperty::cssBorder;
790 break;
791 case eCSSEditableProperty_caption_side:
792 *aAtom = nsEditProperty::cssCaptionSide;
793 break;
794 case eCSSEditableProperty_color:
795 *aAtom = nsEditProperty::cssColor;
796 break;
797 case eCSSEditableProperty_float:
798 *aAtom = nsEditProperty::cssFloat;
799 break;
800 case eCSSEditableProperty_font_family:
801 *aAtom = nsEditProperty::cssFontFamily;
802 break;
803 case eCSSEditableProperty_font_size:
804 *aAtom = nsEditProperty::cssFontSize;
805 break;
806 case eCSSEditableProperty_font_style:
807 *aAtom = nsEditProperty::cssFontStyle;
808 break;
809 case eCSSEditableProperty_font_weight:
810 *aAtom = nsEditProperty::cssFontWeight;
811 break;
812 case eCSSEditableProperty_height:
813 *aAtom = nsEditProperty::cssHeight;
814 break;
815 case eCSSEditableProperty_list_style_type:
816 *aAtom = nsEditProperty::cssListStyleType;
817 break;
818 case eCSSEditableProperty_margin_left:
819 *aAtom = nsEditProperty::cssMarginLeft;
820 break;
821 case eCSSEditableProperty_margin_right:
822 *aAtom = nsEditProperty::cssMarginRight;
823 break;
824 case eCSSEditableProperty_text_align:
825 *aAtom = nsEditProperty::cssTextAlign;
826 break;
827 case eCSSEditableProperty_text_decoration:
828 *aAtom = nsEditProperty::cssTextDecoration;
829 break;
830 case eCSSEditableProperty_vertical_align:
831 *aAtom = nsEditProperty::cssVerticalAlign;
832 break;
833 case eCSSEditableProperty_whitespace:
834 *aAtom = nsEditProperty::cssWhitespace;
835 break;
836 case eCSSEditableProperty_width:
837 *aAtom = nsEditProperty::cssWidth;
838 break;
839 case eCSSEditableProperty_NONE:
840 // intentionally empty
841 break;
845 // Populate aProperty and aValueArray with the CSS declarations equivalent to the
846 // value aValue according to the equivalence table aEquivTable
847 void
848 nsHTMLCSSUtils::BuildCSSDeclarations(nsTArray<nsIAtom*> & aPropertyArray,
849 nsTArray<nsString> & aValueArray,
850 const CSSEquivTable * aEquivTable,
851 const nsAString * aValue,
852 PRBool aGetOrRemoveRequest)
854 // clear arrays
855 aPropertyArray.Clear();
856 aValueArray.Clear();
858 // if we have an input value, let's use it
859 nsAutoString value, lowerCasedValue;
860 if (aValue) {
861 value.Assign(*aValue);
862 lowerCasedValue.Assign(*aValue);
863 ToLowerCase(lowerCasedValue);
866 PRInt8 index = 0;
867 nsCSSEditableProperty cssProperty = aEquivTable[index].cssProperty;
868 while (cssProperty) {
869 if (!aGetOrRemoveRequest|| aEquivTable[index].gettable) {
870 nsAutoString cssValue, cssPropertyString;
871 nsIAtom * cssPropertyAtom;
872 // find the equivalent css value for the index-th property in
873 // the equivalence table
874 (*aEquivTable[index].processValueFunctor) ((!aGetOrRemoveRequest || aEquivTable[index].caseSensitiveValue) ? &value : &lowerCasedValue,
875 cssValue,
876 aEquivTable[index].defaultValue,
877 aEquivTable[index].prependValue,
878 aEquivTable[index].appendValue);
879 GetCSSPropertyAtom(cssProperty, &cssPropertyAtom);
880 aPropertyArray.AppendElement(cssPropertyAtom);
881 aValueArray.AppendElement(cssValue);
883 index++;
884 cssProperty = aEquivTable[index].cssProperty;
888 // Populate cssPropertyArray and cssValueArray with the declarations equivalent
889 // to aHTMLProperty/aAttribute/aValue for the node aNode
890 void
891 nsHTMLCSSUtils::GenerateCSSDeclarationsFromHTMLStyle(nsIDOMNode * aNode,
892 nsIAtom *aHTMLProperty,
893 const nsAString * aAttribute,
894 const nsAString * aValue,
895 nsTArray<nsIAtom*> & cssPropertyArray,
896 nsTArray<nsString> & cssValueArray,
897 PRBool aGetOrRemoveRequest)
899 nsCOMPtr<nsIDOMNode> node = aNode;
900 if (mHTMLEditor->IsTextNode(aNode)) {
901 aNode->GetParentNode(getter_AddRefs(node));
903 if (!node) return;
905 nsIAtom *tagName = nsEditor::GetTag(node);
907 if (nsEditProperty::b == aHTMLProperty) {
908 BuildCSSDeclarations(cssPropertyArray, cssValueArray, boldEquivTable, aValue, aGetOrRemoveRequest);
910 else if (nsEditProperty::i == aHTMLProperty) {
911 BuildCSSDeclarations(cssPropertyArray, cssValueArray, italicEquivTable, aValue, aGetOrRemoveRequest);
913 else if (nsEditProperty::u == aHTMLProperty) {
914 BuildCSSDeclarations(cssPropertyArray, cssValueArray, underlineEquivTable, aValue, aGetOrRemoveRequest);
916 else if (nsEditProperty::strike == aHTMLProperty) {
917 BuildCSSDeclarations(cssPropertyArray, cssValueArray, strikeEquivTable, aValue, aGetOrRemoveRequest);
919 else if (nsEditProperty::tt == aHTMLProperty) {
920 BuildCSSDeclarations(cssPropertyArray, cssValueArray, ttEquivTable, aValue, aGetOrRemoveRequest);
922 else if (aAttribute) {
923 if (nsEditProperty::font == aHTMLProperty &&
924 aAttribute->EqualsLiteral("color")) {
925 BuildCSSDeclarations(cssPropertyArray, cssValueArray, fontColorEquivTable, aValue, aGetOrRemoveRequest);
927 else if (nsEditProperty::font == aHTMLProperty &&
928 aAttribute->EqualsLiteral("face")) {
929 BuildCSSDeclarations(cssPropertyArray, cssValueArray, fontFaceEquivTable, aValue, aGetOrRemoveRequest);
931 else if (aAttribute->EqualsLiteral("bgcolor")) {
932 BuildCSSDeclarations(cssPropertyArray, cssValueArray, bgcolorEquivTable, aValue, aGetOrRemoveRequest);
934 else if (aAttribute->EqualsLiteral("background")) {
935 BuildCSSDeclarations(cssPropertyArray, cssValueArray, backgroundImageEquivTable, aValue, aGetOrRemoveRequest);
937 else if (aAttribute->EqualsLiteral("text")) {
938 BuildCSSDeclarations(cssPropertyArray, cssValueArray, textColorEquivTable, aValue, aGetOrRemoveRequest);
940 else if (aAttribute->EqualsLiteral("border")) {
941 BuildCSSDeclarations(cssPropertyArray, cssValueArray, borderEquivTable, aValue, aGetOrRemoveRequest);
943 else if (aAttribute->EqualsLiteral("align")) {
944 if (nsEditProperty::table == tagName) {
945 BuildCSSDeclarations(cssPropertyArray, cssValueArray, tableAlignEquivTable, aValue, aGetOrRemoveRequest);
947 else if (nsEditProperty::hr == tagName) {
948 BuildCSSDeclarations(cssPropertyArray, cssValueArray, hrAlignEquivTable, aValue, aGetOrRemoveRequest);
950 else if (nsEditProperty::legend == tagName ||
951 nsEditProperty::caption == tagName) {
952 BuildCSSDeclarations(cssPropertyArray, cssValueArray, captionAlignEquivTable, aValue, aGetOrRemoveRequest);
954 else {
955 BuildCSSDeclarations(cssPropertyArray, cssValueArray, textAlignEquivTable, aValue, aGetOrRemoveRequest);
958 else if (aAttribute->EqualsLiteral("valign")) {
959 BuildCSSDeclarations(cssPropertyArray, cssValueArray, verticalAlignEquivTable, aValue, aGetOrRemoveRequest);
961 else if (aAttribute->EqualsLiteral("nowrap")) {
962 BuildCSSDeclarations(cssPropertyArray, cssValueArray, nowrapEquivTable, aValue, aGetOrRemoveRequest);
964 else if (aAttribute->EqualsLiteral("width")) {
965 BuildCSSDeclarations(cssPropertyArray, cssValueArray, widthEquivTable, aValue, aGetOrRemoveRequest);
967 else if (aAttribute->EqualsLiteral("height") ||
968 (nsEditProperty::hr == tagName && aAttribute->EqualsLiteral("size"))) {
969 BuildCSSDeclarations(cssPropertyArray, cssValueArray, heightEquivTable, aValue, aGetOrRemoveRequest);
971 else if (aAttribute->EqualsLiteral("type") &&
972 (nsEditProperty::ol == tagName
973 || nsEditProperty::ul == tagName
974 || nsEditProperty::li == tagName)) {
975 BuildCSSDeclarations(cssPropertyArray, cssValueArray, listStyleTypeEquivTable, aValue, aGetOrRemoveRequest);
980 // Add to aNode the CSS inline style equivalent to HTMLProperty/aAttribute/aValue for the node,
981 // and return in aCount the number of CSS properties set by the call
982 nsresult
983 nsHTMLCSSUtils::SetCSSEquivalentToHTMLStyle(nsIDOMNode * aNode,
984 nsIAtom *aHTMLProperty,
985 const nsAString *aAttribute,
986 const nsAString *aValue,
987 PRInt32 * aCount,
988 PRBool aSuppressTransaction)
990 nsCOMPtr<nsIDOMElement> theElement = do_QueryInterface(aNode);
991 nsresult res = NS_OK;
992 *aCount = 0;
993 if (theElement && IsCSSEditableProperty(aNode, aHTMLProperty, aAttribute)) {
994 // we can apply the styles only if the node is an element and if we have
995 // an equivalence for the requested HTML style in this implementation
997 // Find the CSS equivalence to the HTML style
998 nsTArray<nsIAtom*> cssPropertyArray;
999 nsTArray<nsString> cssValueArray;
1000 GenerateCSSDeclarationsFromHTMLStyle(aNode, aHTMLProperty, aAttribute, aValue,
1001 cssPropertyArray, cssValueArray, PR_FALSE);
1003 // set the individual CSS inline styles
1004 *aCount = cssPropertyArray.Length();
1005 PRInt32 index;
1006 for (index = 0; index < *aCount; index++) {
1007 nsCOMPtr<nsIDOMElement> theElement = do_QueryInterface(aNode);
1008 res = SetCSSProperty(theElement, cssPropertyArray[index],
1009 cssValueArray[index], aSuppressTransaction);
1010 NS_ENSURE_SUCCESS(res, res);
1013 return NS_OK;
1016 // Remove from aNode the CSS inline style equivalent to HTMLProperty/aAttribute/aValue for the node
1017 nsresult
1018 nsHTMLCSSUtils::RemoveCSSEquivalentToHTMLStyle(nsIDOMNode * aNode,
1019 nsIAtom *aHTMLProperty,
1020 const nsAString *aAttribute,
1021 const nsAString *aValue,
1022 PRBool aSuppressTransaction)
1024 nsCOMPtr<nsIDOMElement> theElement = do_QueryInterface(aNode);
1025 nsresult res = NS_OK;
1026 PRInt32 count = 0;
1027 if (theElement && IsCSSEditableProperty(aNode, aHTMLProperty, aAttribute)) {
1028 // we can apply the styles only if the node is an element and if we have
1029 // an equivalence for the requested HTML style in this implementation
1031 // Find the CSS equivalence to the HTML style
1032 nsTArray<nsIAtom*> cssPropertyArray;
1033 nsTArray<nsString> cssValueArray;
1034 GenerateCSSDeclarationsFromHTMLStyle(aNode, aHTMLProperty, aAttribute, aValue,
1035 cssPropertyArray, cssValueArray, PR_TRUE);
1037 // remove the individual CSS inline styles
1038 count = cssPropertyArray.Length();
1039 PRInt32 index;
1040 for (index = 0; index < count; index++) {
1041 res = RemoveCSSProperty(theElement,
1042 cssPropertyArray[index],
1043 cssValueArray[index],
1044 aSuppressTransaction);
1045 NS_ENSURE_SUCCESS(res, res);
1048 return NS_OK;
1051 // aReturn is true if the element aElement carries an ID or a class.
1052 nsresult
1053 nsHTMLCSSUtils::HasClassOrID(nsIDOMElement * aElement, PRBool & aReturn)
1055 nsAutoString classVal, idVal;
1056 PRBool isClassSet, isIdSet;
1057 aReturn = PR_FALSE;
1059 nsresult res = mHTMLEditor->GetAttributeValue(aElement, NS_LITERAL_STRING("class"), classVal, &isClassSet);
1060 NS_ENSURE_SUCCESS(res, res);
1061 res = mHTMLEditor->GetAttributeValue(aElement, NS_LITERAL_STRING("id"), idVal, &isIdSet);
1062 NS_ENSURE_SUCCESS(res, res);
1064 // we need to make sure that if the element has an id or a class attribute,
1065 // the attribute is not the empty string
1066 aReturn = ((isClassSet && !classVal.IsEmpty()) ||
1067 (isIdSet && !idVal.IsEmpty()));
1068 return NS_OK;
1071 // returns in aValueString the list of values for the CSS equivalences to
1072 // the HTML style aHTMLProperty/aAttribute/aValueString for the node aNode;
1073 // the value of aStyleType controls the styles we retrieve : specified or
1074 // computed.
1075 nsresult
1076 nsHTMLCSSUtils::GetCSSEquivalentToHTMLInlineStyleSet(nsIDOMNode * aNode,
1077 nsIAtom *aHTMLProperty,
1078 const nsAString *aAttribute,
1079 nsAString & aValueString,
1080 PRUint8 aStyleType)
1082 aValueString.Truncate();
1083 nsCOMPtr<nsIDOMElement> theElement;
1084 nsresult res = GetElementContainerOrSelf(aNode, getter_AddRefs(theElement));
1085 NS_ENSURE_SUCCESS(res, res);
1087 if (theElement && IsCSSEditableProperty(theElement, aHTMLProperty, aAttribute)) {
1088 // Yes, the requested HTML style has a CSS equivalence in this implementation
1089 // Retrieve the default ViewCSS if we are asked for computed styles
1090 nsCOMPtr<nsIDOMViewCSS> viewCSS = nsnull;
1091 if (COMPUTED_STYLE_TYPE == aStyleType) {
1092 res = GetDefaultViewCSS(theElement, getter_AddRefs(viewCSS));
1093 NS_ENSURE_SUCCESS(res, res);
1095 nsTArray<nsIAtom*> cssPropertyArray;
1096 nsTArray<nsString> cssValueArray;
1097 // get the CSS equivalence with last param PR_TRUE indicating we want only the
1098 // "gettable" properties
1099 GenerateCSSDeclarationsFromHTMLStyle(theElement, aHTMLProperty, aAttribute, nsnull,
1100 cssPropertyArray, cssValueArray, PR_TRUE);
1101 PRInt32 count = cssPropertyArray.Length();
1102 PRInt32 index;
1103 for (index = 0; index < count; index++) {
1104 nsAutoString valueString;
1105 // retrieve the specified/computed value of the property
1106 res = GetCSSInlinePropertyBase(theElement, cssPropertyArray[index],
1107 valueString, viewCSS, aStyleType);
1108 NS_ENSURE_SUCCESS(res, res);
1109 // append the value to aValueString (possibly with a leading whitespace)
1110 if (index) aValueString.Append(PRUnichar(' '));
1111 aValueString.Append(valueString);
1114 return NS_OK;
1117 // Does the node aNode (or his parent if it is not an element node) carries
1118 // the CSS equivalent styles to the HTML style aHTMLProperty/aAttribute/
1119 // aValueString for this node ?
1120 // The value of aStyleType controls the styles we retrieve : specified or
1121 // computed. The return value aIsSet is true is the CSS styles are set.
1122 nsresult
1123 nsHTMLCSSUtils::IsCSSEquivalentToHTMLInlineStyleSet(nsIDOMNode * aNode,
1124 nsIAtom *aHTMLProperty,
1125 const nsAString * aHTMLAttribute,
1126 PRBool & aIsSet,
1127 nsAString & valueString,
1128 PRUint8 aStyleType)
1130 NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER);
1132 nsAutoString htmlValueString(valueString);
1133 aIsSet = PR_FALSE;
1134 nsCOMPtr<nsIDOMNode> node = aNode;
1135 NS_NAMED_LITERAL_STRING(boldStr, "bold");
1136 do {
1137 valueString.Assign(htmlValueString);
1138 // get the value of the CSS equivalent styles
1139 nsresult res = GetCSSEquivalentToHTMLInlineStyleSet(node, aHTMLProperty, aHTMLAttribute,
1140 valueString, aStyleType);
1141 NS_ENSURE_SUCCESS(res, res);
1143 // early way out if we can
1144 if (valueString.IsEmpty()) return NS_OK;
1146 if (nsEditProperty::b == aHTMLProperty) {
1147 if (valueString.Equals(boldStr)) {
1148 aIsSet = PR_TRUE;
1150 else if (valueString.EqualsLiteral("normal")) {
1151 aIsSet = PR_FALSE;
1153 else if (valueString.EqualsLiteral("bolder")) {
1154 aIsSet = PR_TRUE;
1155 valueString.Assign(boldStr);
1157 else {
1158 PRInt32 weight = 0;
1159 PRInt32 errorCode;
1160 nsAutoString value(valueString);
1161 weight = value.ToInteger(&errorCode, 10);
1162 if (400 < weight) {
1163 aIsSet = PR_TRUE;
1164 valueString.Assign(boldStr);
1166 else {
1167 aIsSet = PR_FALSE;
1168 valueString.AssignLiteral("normal");
1173 else if (nsEditProperty::i == aHTMLProperty) {
1174 if (valueString.EqualsLiteral("italic") ||
1175 valueString.EqualsLiteral("oblique")) {
1176 aIsSet= PR_TRUE;
1180 else if (nsEditProperty::u == aHTMLProperty) {
1181 nsAutoString val;
1182 val.AssignLiteral("underline");
1183 aIsSet = PRBool(ChangeCSSInlineStyleTxn::ValueIncludes(valueString, val, PR_FALSE));
1186 else if (nsEditProperty::strike == aHTMLProperty) {
1187 nsAutoString val;
1188 val.AssignLiteral("line-through");
1189 aIsSet = PRBool(ChangeCSSInlineStyleTxn::ValueIncludes(valueString, val, PR_FALSE));
1192 else if (aHTMLAttribute &&
1193 ( (nsEditProperty::font == aHTMLProperty &&
1194 aHTMLAttribute->EqualsLiteral("color")) ||
1195 aHTMLAttribute->EqualsLiteral("bgcolor"))) {
1196 if (htmlValueString.IsEmpty())
1197 aIsSet = PR_TRUE;
1198 else {
1199 nscolor rgba;
1200 nsAutoString subStr;
1201 htmlValueString.Right(subStr, htmlValueString.Length()-1);
1202 if (NS_ColorNameToRGB(htmlValueString, &rgba) ||
1203 NS_HexToRGB(subStr, &rgba)) {
1204 nsAutoString htmlColor, tmpStr;
1205 htmlColor.AppendLiteral("rgb(");
1207 NS_NAMED_LITERAL_STRING(comma, ", ");
1209 tmpStr.AppendInt(NS_GET_R(rgba), 10);
1210 htmlColor.Append(tmpStr + comma);
1212 tmpStr.Truncate();
1213 tmpStr.AppendInt(NS_GET_G(rgba), 10);
1214 htmlColor.Append(tmpStr + comma);
1216 tmpStr.Truncate();
1217 tmpStr.AppendInt(NS_GET_B(rgba), 10);
1218 htmlColor.Append(tmpStr);
1220 htmlColor.Append(PRUnichar(')'));
1221 aIsSet = htmlColor.Equals(valueString,
1222 nsCaseInsensitiveStringComparator());
1224 else
1225 aIsSet = htmlValueString.Equals(valueString,
1226 nsCaseInsensitiveStringComparator());
1230 else if (nsEditProperty::tt == aHTMLProperty) {
1231 aIsSet = StringBeginsWith(valueString, NS_LITERAL_STRING("monospace"));
1234 else if ((nsEditProperty::font == aHTMLProperty) && aHTMLAttribute
1235 && aHTMLAttribute->EqualsLiteral("face")) {
1236 if (!htmlValueString.IsEmpty()) {
1237 const PRUnichar commaSpace[] = { PRUnichar(','), PRUnichar(' '), 0 };
1238 const PRUnichar comma[] = { PRUnichar(','), 0 };
1239 htmlValueString.ReplaceSubstring(commaSpace, comma);
1240 nsAutoString valueStringNorm(valueString);
1241 valueStringNorm.ReplaceSubstring(commaSpace, comma);
1242 aIsSet = htmlValueString.Equals(valueStringNorm,
1243 nsCaseInsensitiveStringComparator());
1245 else {
1246 // ignore this, it's TT or our default
1247 nsAutoString valueStringLower;
1248 ToLowerCase(valueString, valueStringLower);
1249 aIsSet = !valueStringLower.EqualsLiteral("monospace") &&
1250 !valueStringLower.EqualsLiteral("serif");
1252 return NS_OK;
1254 else if (aHTMLAttribute
1255 && aHTMLAttribute->EqualsLiteral("align")) {
1256 aIsSet = PR_TRUE;
1258 else {
1259 aIsSet = PR_FALSE;
1260 return NS_OK;
1263 if (!htmlValueString.IsEmpty()) {
1264 if (htmlValueString.Equals(valueString,
1265 nsCaseInsensitiveStringComparator())) {
1266 aIsSet = PR_TRUE;
1270 if (nsEditProperty::u == aHTMLProperty || nsEditProperty::strike == aHTMLProperty) {
1271 // unfortunately, the value of the text-decoration property is not inherited.
1272 // that means that we have to look at ancestors of node to see if they are underlined
1273 nsCOMPtr<nsIDOMNode> tmp;
1274 res = node->GetParentNode(getter_AddRefs(tmp));
1275 NS_ENSURE_SUCCESS(res, res);
1276 nsCOMPtr<nsIDOMElement> element = do_QueryInterface(tmp);
1277 node = element; // set to null if it's not a dom element
1279 } while ((nsEditProperty::u == aHTMLProperty || nsEditProperty::strike == aHTMLProperty) &&
1280 !aIsSet && node);
1281 return NS_OK;
1284 nsresult
1285 nsHTMLCSSUtils::SetCSSEnabled(PRBool aIsCSSPrefChecked)
1287 mIsCSSPrefChecked = aIsCSSPrefChecked;
1288 return NS_OK;
1291 PRBool
1292 nsHTMLCSSUtils::IsCSSPrefChecked()
1294 return mIsCSSPrefChecked ;
1297 // ElementsSameStyle compares two elements and checks if they have the same
1298 // specified CSS declarations in the STYLE attribute
1299 // The answer is always negative if at least one of them carries an ID or a class
1300 PRBool
1301 nsHTMLCSSUtils::ElementsSameStyle(nsIDOMNode *aFirstNode, nsIDOMNode *aSecondNode)
1303 nsresult res;
1304 nsCOMPtr<nsIDOMElement> firstElement = do_QueryInterface(aFirstNode);
1305 nsCOMPtr<nsIDOMElement> secondElement = do_QueryInterface(aSecondNode);
1307 NS_ASSERTION((firstElement && secondElement), "Non element nodes passed to ElementsSameStyle.");
1309 nsAutoString firstID, secondID;
1310 PRBool isFirstIDSet, isSecondIDSet;
1311 res = mHTMLEditor->GetAttributeValue(firstElement, NS_LITERAL_STRING("id"), firstID, &isFirstIDSet);
1312 res = mHTMLEditor->GetAttributeValue(secondElement, NS_LITERAL_STRING("id"), secondID, &isSecondIDSet);
1313 if (isFirstIDSet || isSecondIDSet) {
1314 // at least one of the spans carries an ID ; suspect a CSS rule applies to it and
1315 // refuse to merge the nodes
1316 return PR_FALSE;
1319 nsAutoString firstClass, secondClass;
1320 PRBool isFirstClassSet, isSecondClassSet;
1321 res = mHTMLEditor->GetAttributeValue(firstElement, NS_LITERAL_STRING("class"), firstClass, &isFirstClassSet);
1322 res = mHTMLEditor->GetAttributeValue(secondElement, NS_LITERAL_STRING("class"), secondClass, &isSecondClassSet);
1323 if (isFirstClassSet && isSecondClassSet) {
1324 // both spans carry a class, let's compare them
1325 if (!firstClass.Equals(secondClass)) {
1326 // WARNING : technically, the comparison just above is questionable :
1327 // from a pure HTML/CSS point of view class="a b" is NOT the same than
1328 // class="b a" because a CSS rule could test the exact value of the class
1329 // attribute to be "a b" for instance ; from a user's point of view, a
1330 // wysiwyg editor should probably NOT make any difference. CSS people
1331 // need to discuss this issue before any modification.
1332 return PR_FALSE;
1335 else if (isFirstClassSet || isSecondClassSet) {
1336 // one span only carries a class, early way out
1337 return PR_FALSE;
1340 nsCOMPtr<nsIDOMCSSStyleDeclaration> firstCSSDecl, secondCSSDecl;
1341 PRUint32 firstLength, secondLength;
1342 res = GetInlineStyles(firstElement, getter_AddRefs(firstCSSDecl), &firstLength);
1343 if (NS_FAILED(res) || !firstCSSDecl) return PR_FALSE;
1344 res = GetInlineStyles(secondElement, getter_AddRefs(secondCSSDecl), &secondLength);
1345 if (NS_FAILED(res) || !secondCSSDecl) return PR_FALSE;
1347 if (firstLength != secondLength) {
1348 // early way out if we can
1349 return PR_FALSE;
1351 else if (0 == firstLength) {
1352 // no inline style !
1353 return PR_TRUE;
1356 PRUint32 i;
1357 nsAutoString propertyNameString;
1358 nsAutoString firstValue, secondValue;
1359 for (i=0; i<firstLength; i++) {
1360 firstCSSDecl->Item(i, propertyNameString);
1361 firstCSSDecl->GetPropertyValue(propertyNameString, firstValue);
1362 secondCSSDecl->GetPropertyValue(propertyNameString, secondValue);
1363 if (!firstValue.Equals(secondValue)) {
1364 return PR_FALSE;
1367 for (i=0; i<secondLength; i++) {
1368 secondCSSDecl->Item(i, propertyNameString);
1369 secondCSSDecl->GetPropertyValue(propertyNameString, secondValue);
1370 firstCSSDecl->GetPropertyValue(propertyNameString, firstValue);
1371 if (!firstValue.Equals(secondValue)) {
1372 return PR_FALSE;
1376 return PR_TRUE;
1379 nsresult
1380 nsHTMLCSSUtils::GetInlineStyles(nsIDOMElement *aElement,
1381 nsIDOMCSSStyleDeclaration **aCssDecl,
1382 PRUint32 *aLength)
1384 NS_ENSURE_TRUE(aElement && aLength, NS_ERROR_NULL_POINTER);
1385 *aLength = 0;
1386 nsCOMPtr<nsIDOMElementCSSInlineStyle> inlineStyles = do_QueryInterface(aElement);
1387 NS_ENSURE_TRUE(inlineStyles, NS_ERROR_NULL_POINTER);
1388 nsresult res = inlineStyles->GetStyle(aCssDecl);
1389 if (NS_FAILED(res) || !aCssDecl) return NS_ERROR_NULL_POINTER;
1390 (*aCssDecl)->GetLength(aLength);
1391 return NS_OK;
1394 nsresult
1395 nsHTMLCSSUtils::GetElementContainerOrSelf(nsIDOMNode * aNode, nsIDOMElement ** aElement)
1397 NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER);
1399 nsCOMPtr<nsIDOMNode> node=aNode, parentNode;
1400 PRUint16 type;
1401 nsresult res;
1402 res = node->GetNodeType(&type);
1403 NS_ENSURE_SUCCESS(res, res);
1405 if (nsIDOMNode::DOCUMENT_NODE == type) {
1406 return NS_ERROR_NULL_POINTER;
1409 // loop until we find an element
1410 while (node && nsIDOMNode::ELEMENT_NODE != type) {
1411 parentNode = node;
1412 res = parentNode->GetParentNode(getter_AddRefs(node));
1413 NS_ENSURE_SUCCESS(res, res);
1414 if (node) {
1415 res = node->GetNodeType(&type);
1416 NS_ENSURE_SUCCESS(res, res);
1419 NS_ASSERTION(node, "we reached a null node ancestor !");
1420 NS_ENSURE_TRUE(node, NS_ERROR_NULL_POINTER);
1421 nsCOMPtr<nsIDOMElement> element = do_QueryInterface(node);
1422 (*aElement) = element;
1423 NS_IF_ADDREF(*aElement);
1424 return NS_OK;
1427 nsresult
1428 nsHTMLCSSUtils::SetCSSProperty(nsIDOMElement * aElement,
1429 const nsAString & aProperty,
1430 const nsAString & aValue)
1432 nsCOMPtr<nsIDOMCSSStyleDeclaration> cssDecl;
1433 PRUint32 length;
1434 nsresult res = GetInlineStyles(aElement, getter_AddRefs(cssDecl), &length);
1435 if (NS_FAILED(res) || !cssDecl) return res;
1437 return cssDecl->SetProperty(aProperty,
1438 aValue,
1439 EmptyString());
1442 nsresult
1443 nsHTMLCSSUtils::SetCSSPropertyPixels(nsIDOMElement * aElement,
1444 const nsAString & aProperty,
1445 PRInt32 aIntValue)
1447 nsAutoString s;
1448 s.AppendInt(aIntValue);
1449 return SetCSSProperty(aElement, aProperty, s + NS_LITERAL_STRING("px"));
1452 nsresult
1453 nsHTMLCSSUtils::RemoveCSSProperty(nsIDOMElement * aElement,
1454 const nsAString & aProperty)
1456 nsCOMPtr<nsIDOMCSSStyleDeclaration> cssDecl;
1457 PRUint32 length;
1458 nsresult res = GetInlineStyles(aElement, getter_AddRefs(cssDecl), &length);
1459 if (NS_FAILED(res) || !cssDecl) return res;
1461 nsAutoString returnString;
1462 return cssDecl->RemoveProperty(aProperty, returnString);