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
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.
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"
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"
53 #include "nsTextEditUtils.h"
54 #include "nsReadableUtils.h"
55 #include "nsUnicharUtils.h"
56 #include "nsHTMLCSSUtils.h"
58 #include "nsAttrName.h"
59 #include "nsAutoPtr.h"
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");
70 aOutputString
.AssignLiteral("bold");
75 void ProcessDefaultValue(const nsAString
* aInputString
, nsAString
& aOutputString
,
76 const char * aDefaultValueString
,
77 const char * aPrependString
, const char* aAppendString
)
79 CopyASCIItoUTF16(aDefaultValueString
, aOutputString
);
83 void ProcessSameValue(const nsAString
* aInputString
, nsAString
& aOutputString
,
84 const char * aDefaultValueString
,
85 const char * aPrependString
, const char* aAppendString
)
88 aOutputString
.Assign(*aInputString
);
91 aOutputString
.Truncate();
95 void ProcessExtendedValue(const nsAString
* aInputString
, nsAString
& aOutputString
,
96 const char * aDefaultValueString
,
97 const char * aPrependString
, const char* aAppendString
)
99 aOutputString
.Truncate();
101 if (aPrependString
) {
102 AppendASCIItoUTF16(aPrependString
, aOutputString
);
104 aOutputString
.Append(*aInputString
);
106 AppendASCIItoUTF16(aAppendString
, aOutputString
);
112 void ProcessLengthValue(const nsAString
* aInputString
, nsAString
& aOutputString
,
113 const char * aDefaultValueString
,
114 const char * aPrependString
, const char* aAppendString
)
116 aOutputString
.Truncate();
118 aOutputString
.Append(*aInputString
);
119 if (-1 == aOutputString
.FindChar(PRUnichar('%'))) {
120 aOutputString
.AppendLiteral("px");
126 void ProcessListStyleTypeValue(const nsAString
* aInputString
, nsAString
& aOutputString
,
127 const char * aDefaultValueString
,
128 const char * aPrependString
, const char* aAppendString
)
130 aOutputString
.Truncate();
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
);
156 void ProcessMarginLeftValue(const nsAString
* aInputString
, nsAString
& aOutputString
,
157 const char * aDefaultValueString
,
158 const char * aPrependString
, const char* aAppendString
)
160 aOutputString
.Truncate();
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");
171 aOutputString
.AppendLiteral("0px");
177 void ProcessMarginRightValue(const nsAString
* aInputString
, nsAString
& aOutputString
,
178 const char * aDefaultValueString
,
179 const char * aPrependString
, const char* aAppendString
)
181 aOutputString
.Truncate();
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");
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()
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
;
325 // Answers true if we have some CSS equivalence for the HTML style defined
326 // by aProperty and/or aAttribute for the node aNode
328 nsHTMLCSSUtils::IsCSSEditableProperty(nsIDOMNode
* aNode
,
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")))) {
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
)) {
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
)) {
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"))) {
401 // attribute BGCOLOR on other elements
402 if (aAttribute
&& aAttribute
->EqualsLiteral("bgcolor")) {
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"))) {
415 // attributes HEIGHT and WIDTH on TABLE
416 if (aAttribute
&& (nsEditProperty::table
== tagName
) &&
417 (aAttribute
->EqualsLiteral("height")
418 || aAttribute
->EqualsLiteral("width"))) {
422 // attributes SIZE and WIDTH on HR
423 if (aAttribute
&& (nsEditProperty::hr
== tagName
) &&
424 (aAttribute
->EqualsLiteral("size")
425 || aAttribute
->EqualsLiteral("width"))) {
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")) {
437 if (aAttribute
&& nsEditProperty::img
== tagName
&&
438 (aAttribute
->EqualsLiteral("border")
439 || aAttribute
->EqualsLiteral("width")
440 || aAttribute
->EqualsLiteral("height"))) {
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
)) {
462 // the lowest level above the transaction; adds the css declaration "aProperty : aValue" to
463 // the inline styles carried by aElement
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();
476 result
= mHTMLEditor
->DoTransaction(txn
);
483 nsHTMLCSSUtils::SetCSSPropertyPixels(nsIDOMElement
*aElement
,
486 PRBool aSuppressTransaction
)
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
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();
509 result
= mHTMLEditor
->DoTransaction(txn
);
516 nsHTMLCSSUtils::CreateCSSPropertyTxn(nsIDOMElement
*aElement
,
517 nsIAtom
* aAttribute
,
518 const nsAString
& aValue
,
519 ChangeCSSInlineStyleTxn
** aTxn
,
520 PRBool aRemoveProperty
)
523 return NS_ERROR_NULL_POINTER
;
525 *aTxn
= new ChangeCSSInlineStyleTxn();
527 return NS_ERROR_OUT_OF_MEMORY
;
529 return (*aTxn
)->Init(mHTMLEditor
, aElement
, aAttribute
, aValue
, aRemoveProperty
);
533 nsHTMLCSSUtils::GetSpecifiedProperty(nsIDOMNode
*aNode
, nsIAtom
*aProperty
,
536 return GetCSSInlinePropertyBase(aNode
, aProperty
, aValue
, nsnull
, SPECIFIED_STYLE_TYPE
);
540 nsHTMLCSSUtils::GetComputedProperty(nsIDOMNode
*aNode
, nsIAtom
*aProperty
,
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
);
551 nsHTMLCSSUtils::GetCSSInlinePropertyBase(nsIDOMNode
*aNode
, nsIAtom
*aProperty
,
553 nsIDOMViewCSS
*aViewCSS
,
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
);
578 case SPECIFIED_STYLE_TYPE
:
580 nsCOMPtr
<nsIDOMCSSStyleDeclaration
> cssDecl
;
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
);
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
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
;
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
;
616 // from the abstractView, get the CSS view
617 CallQueryInterface(abstractView
, aViewCSS
);
627 NS_NewHTMLCSSUtils(nsHTMLCSSUtils
** aInstancePtrResult
)
629 nsHTMLCSSUtils
* rules
= new nsHTMLCSSUtils();
631 *aInstancePtrResult
= rules
;
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
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
;
670 // Answers true is the property can be removed by setting a "none" CSS value
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
680 nsHTMLCSSUtils::GetDefaultBackgroundColor(nsAString
& aColor
)
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
;
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
;
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
;
709 CopyASCIItoUTF16(returnColor
, aColor
);
714 // Get the default length unit used for CSS Indent/Outdent
716 nsHTMLCSSUtils::GetDefaultLengthUnit(nsAString
& aLengthUnit
)
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
);
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...
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;
746 PRInt32 i
= 0, j
= aString
.Length();
748 PRBool floatingPointFound
= PR_FALSE
;
750 if (PRUnichar('-') == c
) { sign
= -1; iter
++; i
++; }
751 else if (PRUnichar('+') == c
) { iter
++; i
++; }
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')));
767 else if (!floatingPointFound
&& (PRUnichar('.') == c
)) {
768 floatingPointFound
= PR_TRUE
;
775 *aValue
= value
* sign
;
776 *aUnit
= NS_NewAtom(StringTail(aString
, j
-i
));
780 nsHTMLCSSUtils::GetCSSPropertyAtom(nsCSSEditableProperty aProperty
, nsIAtom
** aAtom
)
784 case eCSSEditableProperty_background_color
:
785 *aAtom
= nsEditProperty::cssBackgroundColor
;
787 case eCSSEditableProperty_background_image
:
788 *aAtom
= nsEditProperty::cssBackgroundImage
;
790 case eCSSEditableProperty_border
:
791 *aAtom
= nsEditProperty::cssBorder
;
793 case eCSSEditableProperty_caption_side
:
794 *aAtom
= nsEditProperty::cssCaptionSide
;
796 case eCSSEditableProperty_color
:
797 *aAtom
= nsEditProperty::cssColor
;
799 case eCSSEditableProperty_float
:
800 *aAtom
= nsEditProperty::cssFloat
;
802 case eCSSEditableProperty_font_family
:
803 *aAtom
= nsEditProperty::cssFontFamily
;
805 case eCSSEditableProperty_font_size
:
806 *aAtom
= nsEditProperty::cssFontSize
;
808 case eCSSEditableProperty_font_style
:
809 *aAtom
= nsEditProperty::cssFontStyle
;
811 case eCSSEditableProperty_font_weight
:
812 *aAtom
= nsEditProperty::cssFontWeight
;
814 case eCSSEditableProperty_height
:
815 *aAtom
= nsEditProperty::cssHeight
;
817 case eCSSEditableProperty_list_style_type
:
818 *aAtom
= nsEditProperty::cssListStyleType
;
820 case eCSSEditableProperty_margin_left
:
821 *aAtom
= nsEditProperty::cssMarginLeft
;
823 case eCSSEditableProperty_margin_right
:
824 *aAtom
= nsEditProperty::cssMarginRight
;
826 case eCSSEditableProperty_text_align
:
827 *aAtom
= nsEditProperty::cssTextAlign
;
829 case eCSSEditableProperty_text_decoration
:
830 *aAtom
= nsEditProperty::cssTextDecoration
;
832 case eCSSEditableProperty_vertical_align
:
833 *aAtom
= nsEditProperty::cssVerticalAlign
;
835 case eCSSEditableProperty_whitespace
:
836 *aAtom
= nsEditProperty::cssWhitespace
;
838 case eCSSEditableProperty_width
:
839 *aAtom
= nsEditProperty::cssWidth
;
841 case eCSSEditableProperty_NONE
:
842 // intentionally empty
847 // Populate aProperty and aValueArray with the CSS declarations equivalent to the
848 // value aValue according to the equivalence table aEquivTable
850 nsHTMLCSSUtils::BuildCSSDeclarations(nsTArray
<nsIAtom
*> & aPropertyArray
,
851 nsTArray
<nsString
> & aValueArray
,
852 const CSSEquivTable
* aEquivTable
,
853 const nsAString
* aValue
,
854 PRBool aGetOrRemoveRequest
)
857 aPropertyArray
.Clear();
860 // if we have an input value, let's use it
861 nsAutoString value
, lowerCasedValue
;
863 value
.Assign(*aValue
);
864 lowerCasedValue
.Assign(*aValue
);
865 ToLowerCase(lowerCasedValue
);
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
,
878 aEquivTable
[index
].defaultValue
,
879 aEquivTable
[index
].prependValue
,
880 aEquivTable
[index
].appendValue
);
881 GetCSSPropertyAtom(cssProperty
, &cssPropertyAtom
);
882 aPropertyArray
.AppendElement(cssPropertyAtom
);
883 aValueArray
.AppendElement(cssValue
);
886 cssProperty
= aEquivTable
[index
].cssProperty
;
890 // Populate cssPropertyArray and cssValueArray with the declarations equivalent
891 // to aHTMLProperty/aAttribute/aValue for the node aNode
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
));
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
);
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
985 nsHTMLCSSUtils::SetCSSEquivalentToHTMLStyle(nsIDOMNode
* aNode
,
986 nsIAtom
*aHTMLProperty
,
987 const nsAString
*aAttribute
,
988 const nsAString
*aValue
,
990 PRBool aSuppressTransaction
)
992 nsCOMPtr
<nsIDOMElement
> theElement
= do_QueryInterface(aNode
);
993 nsresult res
= NS_OK
;
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();
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
;
1018 // Remove from aNode the CSS inline style equivalent to HTMLProperty/aAttribute/aValue for the node
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
;
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();
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
;
1053 // aReturn is true if the element aElement carries an ID or a class.
1055 nsHTMLCSSUtils::HasClassOrID(nsIDOMElement
* aElement
, PRBool
& aReturn
)
1057 nsAutoString classVal
, idVal
;
1058 PRBool isClassSet
, isIdSet
;
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()));
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
1078 nsHTMLCSSUtils::GetCSSEquivalentToHTMLInlineStyleSet(nsIDOMNode
* aNode
,
1079 nsIAtom
*aHTMLProperty
,
1080 const nsAString
*aAttribute
,
1081 nsAString
& aValueString
,
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();
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
);
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.
1125 nsHTMLCSSUtils::IsCSSEquivalentToHTMLInlineStyleSet(nsIDOMNode
* aNode
,
1126 nsIAtom
*aHTMLProperty
,
1127 const nsAString
* aHTMLAttribute
,
1129 nsAString
& valueString
,
1132 NS_ENSURE_TRUE(aNode
, NS_ERROR_NULL_POINTER
);
1134 nsAutoString
htmlValueString(valueString
);
1136 nsCOMPtr
<nsIDOMNode
> node
= aNode
;
1137 NS_NAMED_LITERAL_STRING(boldStr
, "bold");
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
)) {
1152 else if (valueString
.EqualsLiteral("normal")) {
1155 else if (valueString
.EqualsLiteral("bolder")) {
1157 valueString
.Assign(boldStr
);
1162 nsAutoString
value(valueString
);
1163 weight
= value
.ToInteger(&errorCode
, 10);
1166 valueString
.Assign(boldStr
);
1170 valueString
.AssignLiteral("normal");
1175 else if (nsEditProperty::i
== aHTMLProperty
) {
1176 if (valueString
.EqualsLiteral("italic") ||
1177 valueString
.EqualsLiteral("oblique")) {
1182 else if (nsEditProperty::u
== aHTMLProperty
) {
1184 val
.AssignLiteral("underline");
1185 aIsSet
= PRBool(ChangeCSSInlineStyleTxn::ValueIncludes(valueString
, val
, PR_FALSE
));
1188 else if (nsEditProperty::strike
== aHTMLProperty
) {
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())
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
);
1215 tmpStr
.AppendInt(NS_GET_G(rgba
), 10);
1216 htmlColor
.Append(tmpStr
+ comma
);
1219 tmpStr
.AppendInt(NS_GET_B(rgba
), 10);
1220 htmlColor
.Append(tmpStr
);
1222 htmlColor
.Append(PRUnichar(')'));
1223 aIsSet
= htmlColor
.Equals(valueString
,
1224 nsCaseInsensitiveStringComparator());
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());
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");
1256 else if (aHTMLAttribute
1257 && aHTMLAttribute
->EqualsLiteral("align")) {
1265 if (!htmlValueString
.IsEmpty()) {
1266 if (htmlValueString
.Equals(valueString
,
1267 nsCaseInsensitiveStringComparator())) {
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
) &&
1287 nsHTMLCSSUtils::SetCSSEnabled(PRBool aIsCSSPrefChecked
)
1289 mIsCSSPrefChecked
= aIsCSSPrefChecked
;
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
1303 nsHTMLCSSUtils::ElementsSameStyle(nsIDOMNode
*aFirstNode
, nsIDOMNode
*aSecondNode
)
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
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.
1337 else if (isFirstClassSet
|| isSecondClassSet
) {
1338 // one span only carries a class, early way out
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
1353 else if (0 == firstLength
) {
1354 // no inline style !
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
)) {
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
)) {
1382 nsHTMLCSSUtils::GetInlineStyles(nsIDOMElement
*aElement
,
1383 nsIDOMCSSStyleDeclaration
**aCssDecl
,
1386 if (!aElement
|| !aLength
) return NS_ERROR_NULL_POINTER
;
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
);
1397 nsHTMLCSSUtils::GetElementContainerOrSelf(nsIDOMNode
* aNode
, nsIDOMElement
** aElement
)
1399 NS_ENSURE_TRUE(aNode
, NS_ERROR_NULL_POINTER
);
1401 nsCOMPtr
<nsIDOMNode
> node
=aNode
, parentNode
;
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
) {
1414 res
= parentNode
->GetParentNode(getter_AddRefs(node
));
1415 if (NS_FAILED(res
)) return res
;
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
);
1430 nsHTMLCSSUtils::SetCSSProperty(nsIDOMElement
* aElement
,
1431 const nsAString
& aProperty
,
1432 const nsAString
& aValue
)
1434 nsCOMPtr
<nsIDOMCSSStyleDeclaration
> cssDecl
;
1436 nsresult res
= GetInlineStyles(aElement
, getter_AddRefs(cssDecl
), &length
);
1437 if (NS_FAILED(res
) || !cssDecl
) return res
;
1439 return cssDecl
->SetProperty(aProperty
,
1445 nsHTMLCSSUtils::SetCSSPropertyPixels(nsIDOMElement
* aElement
,
1446 const nsAString
& aProperty
,
1450 s
.AppendInt(aIntValue
);
1451 return SetCSSProperty(aElement
, aProperty
, s
+ NS_LITERAL_STRING("px"));
1455 nsHTMLCSSUtils::RemoveCSSProperty(nsIDOMElement
* aElement
,
1456 const nsAString
& aProperty
)
1458 nsCOMPtr
<nsIDOMCSSStyleDeclaration
> cssDecl
;
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
);