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 NS_ENSURE_SUCCESS(result
, 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 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")))) {
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
)
522 NS_ENSURE_TRUE(aElement
, NS_ERROR_NULL_POINTER
);
524 *aTxn
= new ChangeCSSInlineStyleTxn();
525 NS_ENSURE_TRUE(*aTxn
, NS_ERROR_OUT_OF_MEMORY
);
527 return (*aTxn
)->Init(mHTMLEditor
, aElement
, aAttribute
, aValue
, aRemoveProperty
);
531 nsHTMLCSSUtils::GetSpecifiedProperty(nsIDOMNode
*aNode
, nsIAtom
*aProperty
,
534 return GetCSSInlinePropertyBase(aNode
, aProperty
, aValue
, nsnull
, SPECIFIED_STYLE_TYPE
);
538 nsHTMLCSSUtils::GetComputedProperty(nsIDOMNode
*aNode
, nsIAtom
*aProperty
,
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
);
549 nsHTMLCSSUtils::GetCSSInlinePropertyBase(nsIDOMNode
*aNode
, nsIAtom
*aProperty
,
551 nsIDOMViewCSS
*aViewCSS
,
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
);
576 case SPECIFIED_STYLE_TYPE
:
578 nsCOMPtr
<nsIDOMCSSStyleDeclaration
> cssDecl
;
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
);
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
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
);
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
);
614 // from the abstractView, get the CSS view
615 CallQueryInterface(abstractView
, aViewCSS
);
625 NS_NewHTMLCSSUtils(nsHTMLCSSUtils
** aInstancePtrResult
)
627 nsHTMLCSSUtils
* rules
= new nsHTMLCSSUtils();
629 *aInstancePtrResult
= rules
;
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
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
);
668 // Answers true is the property can be removed by setting a "none" CSS value
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
678 nsHTMLCSSUtils::GetDefaultBackgroundColor(nsAString
& aColor
)
681 nsCOMPtr
<nsIPrefBranch
> prefBranch
=
682 do_GetService(NS_PREFSERVICE_CONTRACTID
, &result
);
683 NS_ENSURE_SUCCESS(result
, result
);
684 aColor
.AssignLiteral("#ffffff");
685 nsXPIDLCString returnColor
;
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
);
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
);
707 CopyASCIItoUTF16(returnColor
, aColor
);
712 // Get the default length unit used for CSS Indent/Outdent
714 nsHTMLCSSUtils::GetDefaultLengthUnit(nsAString
& aLengthUnit
)
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
);
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...
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;
744 PRInt32 i
= 0, j
= aString
.Length();
746 PRBool floatingPointFound
= PR_FALSE
;
748 if (PRUnichar('-') == c
) { sign
= -1; iter
++; i
++; }
749 else if (PRUnichar('+') == c
) { iter
++; i
++; }
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')));
765 else if (!floatingPointFound
&& (PRUnichar('.') == c
)) {
766 floatingPointFound
= PR_TRUE
;
773 *aValue
= value
* sign
;
774 *aUnit
= NS_NewAtom(StringTail(aString
, j
-i
));
778 nsHTMLCSSUtils::GetCSSPropertyAtom(nsCSSEditableProperty aProperty
, nsIAtom
** aAtom
)
782 case eCSSEditableProperty_background_color
:
783 *aAtom
= nsEditProperty::cssBackgroundColor
;
785 case eCSSEditableProperty_background_image
:
786 *aAtom
= nsEditProperty::cssBackgroundImage
;
788 case eCSSEditableProperty_border
:
789 *aAtom
= nsEditProperty::cssBorder
;
791 case eCSSEditableProperty_caption_side
:
792 *aAtom
= nsEditProperty::cssCaptionSide
;
794 case eCSSEditableProperty_color
:
795 *aAtom
= nsEditProperty::cssColor
;
797 case eCSSEditableProperty_float
:
798 *aAtom
= nsEditProperty::cssFloat
;
800 case eCSSEditableProperty_font_family
:
801 *aAtom
= nsEditProperty::cssFontFamily
;
803 case eCSSEditableProperty_font_size
:
804 *aAtom
= nsEditProperty::cssFontSize
;
806 case eCSSEditableProperty_font_style
:
807 *aAtom
= nsEditProperty::cssFontStyle
;
809 case eCSSEditableProperty_font_weight
:
810 *aAtom
= nsEditProperty::cssFontWeight
;
812 case eCSSEditableProperty_height
:
813 *aAtom
= nsEditProperty::cssHeight
;
815 case eCSSEditableProperty_list_style_type
:
816 *aAtom
= nsEditProperty::cssListStyleType
;
818 case eCSSEditableProperty_margin_left
:
819 *aAtom
= nsEditProperty::cssMarginLeft
;
821 case eCSSEditableProperty_margin_right
:
822 *aAtom
= nsEditProperty::cssMarginRight
;
824 case eCSSEditableProperty_text_align
:
825 *aAtom
= nsEditProperty::cssTextAlign
;
827 case eCSSEditableProperty_text_decoration
:
828 *aAtom
= nsEditProperty::cssTextDecoration
;
830 case eCSSEditableProperty_vertical_align
:
831 *aAtom
= nsEditProperty::cssVerticalAlign
;
833 case eCSSEditableProperty_whitespace
:
834 *aAtom
= nsEditProperty::cssWhitespace
;
836 case eCSSEditableProperty_width
:
837 *aAtom
= nsEditProperty::cssWidth
;
839 case eCSSEditableProperty_NONE
:
840 // intentionally empty
845 // Populate aProperty and aValueArray with the CSS declarations equivalent to the
846 // value aValue according to the equivalence table aEquivTable
848 nsHTMLCSSUtils::BuildCSSDeclarations(nsTArray
<nsIAtom
*> & aPropertyArray
,
849 nsTArray
<nsString
> & aValueArray
,
850 const CSSEquivTable
* aEquivTable
,
851 const nsAString
* aValue
,
852 PRBool aGetOrRemoveRequest
)
855 aPropertyArray
.Clear();
858 // if we have an input value, let's use it
859 nsAutoString value
, lowerCasedValue
;
861 value
.Assign(*aValue
);
862 lowerCasedValue
.Assign(*aValue
);
863 ToLowerCase(lowerCasedValue
);
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
,
876 aEquivTable
[index
].defaultValue
,
877 aEquivTable
[index
].prependValue
,
878 aEquivTable
[index
].appendValue
);
879 GetCSSPropertyAtom(cssProperty
, &cssPropertyAtom
);
880 aPropertyArray
.AppendElement(cssPropertyAtom
);
881 aValueArray
.AppendElement(cssValue
);
884 cssProperty
= aEquivTable
[index
].cssProperty
;
888 // Populate cssPropertyArray and cssValueArray with the declarations equivalent
889 // to aHTMLProperty/aAttribute/aValue for the node aNode
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
));
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
);
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
983 nsHTMLCSSUtils::SetCSSEquivalentToHTMLStyle(nsIDOMNode
* aNode
,
984 nsIAtom
*aHTMLProperty
,
985 const nsAString
*aAttribute
,
986 const nsAString
*aValue
,
988 PRBool aSuppressTransaction
)
990 nsCOMPtr
<nsIDOMElement
> theElement
= do_QueryInterface(aNode
);
991 nsresult res
= NS_OK
;
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();
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
);
1016 // Remove from aNode the CSS inline style equivalent to HTMLProperty/aAttribute/aValue for the node
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
;
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();
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
);
1051 // aReturn is true if the element aElement carries an ID or a class.
1053 nsHTMLCSSUtils::HasClassOrID(nsIDOMElement
* aElement
, PRBool
& aReturn
)
1055 nsAutoString classVal
, idVal
;
1056 PRBool isClassSet
, isIdSet
;
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()));
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
1076 nsHTMLCSSUtils::GetCSSEquivalentToHTMLInlineStyleSet(nsIDOMNode
* aNode
,
1077 nsIAtom
*aHTMLProperty
,
1078 const nsAString
*aAttribute
,
1079 nsAString
& aValueString
,
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();
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
);
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.
1123 nsHTMLCSSUtils::IsCSSEquivalentToHTMLInlineStyleSet(nsIDOMNode
* aNode
,
1124 nsIAtom
*aHTMLProperty
,
1125 const nsAString
* aHTMLAttribute
,
1127 nsAString
& valueString
,
1130 NS_ENSURE_TRUE(aNode
, NS_ERROR_NULL_POINTER
);
1132 nsAutoString
htmlValueString(valueString
);
1134 nsCOMPtr
<nsIDOMNode
> node
= aNode
;
1135 NS_NAMED_LITERAL_STRING(boldStr
, "bold");
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
)) {
1150 else if (valueString
.EqualsLiteral("normal")) {
1153 else if (valueString
.EqualsLiteral("bolder")) {
1155 valueString
.Assign(boldStr
);
1160 nsAutoString
value(valueString
);
1161 weight
= value
.ToInteger(&errorCode
, 10);
1164 valueString
.Assign(boldStr
);
1168 valueString
.AssignLiteral("normal");
1173 else if (nsEditProperty::i
== aHTMLProperty
) {
1174 if (valueString
.EqualsLiteral("italic") ||
1175 valueString
.EqualsLiteral("oblique")) {
1180 else if (nsEditProperty::u
== aHTMLProperty
) {
1182 val
.AssignLiteral("underline");
1183 aIsSet
= PRBool(ChangeCSSInlineStyleTxn::ValueIncludes(valueString
, val
, PR_FALSE
));
1186 else if (nsEditProperty::strike
== aHTMLProperty
) {
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())
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
);
1213 tmpStr
.AppendInt(NS_GET_G(rgba
), 10);
1214 htmlColor
.Append(tmpStr
+ comma
);
1217 tmpStr
.AppendInt(NS_GET_B(rgba
), 10);
1218 htmlColor
.Append(tmpStr
);
1220 htmlColor
.Append(PRUnichar(')'));
1221 aIsSet
= htmlColor
.Equals(valueString
,
1222 nsCaseInsensitiveStringComparator());
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());
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");
1254 else if (aHTMLAttribute
1255 && aHTMLAttribute
->EqualsLiteral("align")) {
1263 if (!htmlValueString
.IsEmpty()) {
1264 if (htmlValueString
.Equals(valueString
,
1265 nsCaseInsensitiveStringComparator())) {
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
) &&
1285 nsHTMLCSSUtils::SetCSSEnabled(PRBool aIsCSSPrefChecked
)
1287 mIsCSSPrefChecked
= aIsCSSPrefChecked
;
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
1301 nsHTMLCSSUtils::ElementsSameStyle(nsIDOMNode
*aFirstNode
, nsIDOMNode
*aSecondNode
)
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
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.
1335 else if (isFirstClassSet
|| isSecondClassSet
) {
1336 // one span only carries a class, early way out
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
1351 else if (0 == firstLength
) {
1352 // no inline style !
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
)) {
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
)) {
1380 nsHTMLCSSUtils::GetInlineStyles(nsIDOMElement
*aElement
,
1381 nsIDOMCSSStyleDeclaration
**aCssDecl
,
1384 NS_ENSURE_TRUE(aElement
&& aLength
, NS_ERROR_NULL_POINTER
);
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
);
1395 nsHTMLCSSUtils::GetElementContainerOrSelf(nsIDOMNode
* aNode
, nsIDOMElement
** aElement
)
1397 NS_ENSURE_TRUE(aNode
, NS_ERROR_NULL_POINTER
);
1399 nsCOMPtr
<nsIDOMNode
> node
=aNode
, parentNode
;
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
) {
1412 res
= parentNode
->GetParentNode(getter_AddRefs(node
));
1413 NS_ENSURE_SUCCESS(res
, res
);
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
);
1428 nsHTMLCSSUtils::SetCSSProperty(nsIDOMElement
* aElement
,
1429 const nsAString
& aProperty
,
1430 const nsAString
& aValue
)
1432 nsCOMPtr
<nsIDOMCSSStyleDeclaration
> cssDecl
;
1434 nsresult res
= GetInlineStyles(aElement
, getter_AddRefs(cssDecl
), &length
);
1435 if (NS_FAILED(res
) || !cssDecl
) return res
;
1437 return cssDecl
->SetProperty(aProperty
,
1443 nsHTMLCSSUtils::SetCSSPropertyPixels(nsIDOMElement
* aElement
,
1444 const nsAString
& aProperty
,
1448 s
.AppendInt(aIntValue
);
1449 return SetCSSProperty(aElement
, aProperty
, s
+ NS_LITERAL_STRING("px"));
1453 nsHTMLCSSUtils::RemoveCSSProperty(nsIDOMElement
* aElement
,
1454 const nsAString
& aProperty
)
1456 nsCOMPtr
<nsIDOMCSSStyleDeclaration
> cssDecl
;
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
);