editor/libeditor/html/nsHTMLCSSUtils.cpp

changeset 2
7e26c7da4463
equal deleted inserted replaced
-1:000000000000 0:4c2571733d53
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6 #include "ChangeCSSInlineStyleTxn.h"
7 #include "EditTxn.h"
8 #include "mozilla/Assertions.h"
9 #include "mozilla/Preferences.h"
10 #include "mozilla/css/Declaration.h"
11 #include "mozilla/css/StyleRule.h"
12 #include "mozilla/dom/Element.h"
13 #include "mozilla/mozalloc.h"
14 #include "nsAString.h"
15 #include "nsAutoPtr.h"
16 #include "nsCOMPtr.h"
17 #include "nsColor.h"
18 #include "nsComputedDOMStyle.h"
19 #include "nsDebug.h"
20 #include "nsDependentSubstring.h"
21 #include "nsEditProperty.h"
22 #include "nsError.h"
23 #include "nsGkAtoms.h"
24 #include "nsHTMLCSSUtils.h"
25 #include "nsHTMLEditor.h"
26 #include "nsIAtom.h"
27 #include "nsIContent.h"
28 #include "nsIDOMCSSStyleDeclaration.h"
29 #include "nsIDOMElement.h"
30 #include "nsIDOMElementCSSInlineStyle.h"
31 #include "nsIDOMNode.h"
32 #include "nsIDOMWindow.h"
33 #include "nsIDocument.h"
34 #include "nsIEditor.h"
35 #include "nsINode.h"
36 #include "nsISupportsImpl.h"
37 #include "nsISupportsUtils.h"
38 #include "nsLiteralString.h"
39 #include "nsPIDOMWindow.h"
40 #include "nsReadableUtils.h"
41 #include "nsString.h"
42 #include "nsStringFwd.h"
43 #include "nsStringIterator.h"
44 #include "nsSubstringTuple.h"
45 #include "nsUnicharUtils.h"
46
47 using namespace mozilla;
48
49 static
50 void ProcessBValue(const nsAString * aInputString, nsAString & aOutputString,
51 const char * aDefaultValueString,
52 const char * aPrependString, const char* aAppendString)
53 {
54 if (aInputString && aInputString->EqualsLiteral("-moz-editor-invert-value")) {
55 aOutputString.AssignLiteral("normal");
56 }
57 else {
58 aOutputString.AssignLiteral("bold");
59 }
60 }
61
62 static
63 void ProcessDefaultValue(const nsAString * aInputString, nsAString & aOutputString,
64 const char * aDefaultValueString,
65 const char * aPrependString, const char* aAppendString)
66 {
67 CopyASCIItoUTF16(aDefaultValueString, aOutputString);
68 }
69
70 static
71 void ProcessSameValue(const nsAString * aInputString, nsAString & aOutputString,
72 const char * aDefaultValueString,
73 const char * aPrependString, const char* aAppendString)
74 {
75 if (aInputString) {
76 aOutputString.Assign(*aInputString);
77 }
78 else
79 aOutputString.Truncate();
80 }
81
82 static
83 void ProcessExtendedValue(const nsAString * aInputString, nsAString & aOutputString,
84 const char * aDefaultValueString,
85 const char * aPrependString, const char* aAppendString)
86 {
87 aOutputString.Truncate();
88 if (aInputString) {
89 if (aPrependString) {
90 AppendASCIItoUTF16(aPrependString, aOutputString);
91 }
92 aOutputString.Append(*aInputString);
93 if (aAppendString) {
94 AppendASCIItoUTF16(aAppendString, aOutputString);
95 }
96 }
97 }
98
99 static
100 void ProcessLengthValue(const nsAString * aInputString, nsAString & aOutputString,
101 const char * aDefaultValueString,
102 const char * aPrependString, const char* aAppendString)
103 {
104 aOutputString.Truncate();
105 if (aInputString) {
106 aOutputString.Append(*aInputString);
107 if (-1 == aOutputString.FindChar(char16_t('%'))) {
108 aOutputString.AppendLiteral("px");
109 }
110 }
111 }
112
113 static
114 void ProcessListStyleTypeValue(const nsAString * aInputString, nsAString & aOutputString,
115 const char * aDefaultValueString,
116 const char * aPrependString, const char* aAppendString)
117 {
118 aOutputString.Truncate();
119 if (aInputString) {
120 if (aInputString->EqualsLiteral("1")) {
121 aOutputString.AppendLiteral("decimal");
122 }
123 else if (aInputString->EqualsLiteral("a")) {
124 aOutputString.AppendLiteral("lower-alpha");
125 }
126 else if (aInputString->EqualsLiteral("A")) {
127 aOutputString.AppendLiteral("upper-alpha");
128 }
129 else if (aInputString->EqualsLiteral("i")) {
130 aOutputString.AppendLiteral("lower-roman");
131 }
132 else if (aInputString->EqualsLiteral("I")) {
133 aOutputString.AppendLiteral("upper-roman");
134 }
135 else if (aInputString->EqualsLiteral("square")
136 || aInputString->EqualsLiteral("circle")
137 || aInputString->EqualsLiteral("disc")) {
138 aOutputString.Append(*aInputString);
139 }
140 }
141 }
142
143 static
144 void ProcessMarginLeftValue(const nsAString * aInputString, nsAString & aOutputString,
145 const char * aDefaultValueString,
146 const char * aPrependString, const char* aAppendString)
147 {
148 aOutputString.Truncate();
149 if (aInputString) {
150 if (aInputString->EqualsLiteral("center") ||
151 aInputString->EqualsLiteral("-moz-center")) {
152 aOutputString.AppendLiteral("auto");
153 }
154 else if (aInputString->EqualsLiteral("right") ||
155 aInputString->EqualsLiteral("-moz-right")) {
156 aOutputString.AppendLiteral("auto");
157 }
158 else {
159 aOutputString.AppendLiteral("0px");
160 }
161 }
162 }
163
164 static
165 void ProcessMarginRightValue(const nsAString * aInputString, nsAString & aOutputString,
166 const char * aDefaultValueString,
167 const char * aPrependString, const char* aAppendString)
168 {
169 aOutputString.Truncate();
170 if (aInputString) {
171 if (aInputString->EqualsLiteral("center") ||
172 aInputString->EqualsLiteral("-moz-center")) {
173 aOutputString.AppendLiteral("auto");
174 }
175 else if (aInputString->EqualsLiteral("left") ||
176 aInputString->EqualsLiteral("-moz-left")) {
177 aOutputString.AppendLiteral("auto");
178 }
179 else {
180 aOutputString.AppendLiteral("0px");
181 }
182 }
183 }
184
185 const nsHTMLCSSUtils::CSSEquivTable boldEquivTable[] = {
186 { nsHTMLCSSUtils::eCSSEditableProperty_font_weight, ProcessBValue, nullptr, nullptr, nullptr, true, false },
187 { nsHTMLCSSUtils::eCSSEditableProperty_NONE, 0 }
188 };
189
190 const nsHTMLCSSUtils::CSSEquivTable italicEquivTable[] = {
191 { nsHTMLCSSUtils::eCSSEditableProperty_font_style, ProcessDefaultValue, "italic", nullptr, nullptr, true, false },
192 { nsHTMLCSSUtils::eCSSEditableProperty_NONE, 0 }
193 };
194
195 const nsHTMLCSSUtils::CSSEquivTable underlineEquivTable[] = {
196 { nsHTMLCSSUtils::eCSSEditableProperty_text_decoration, ProcessDefaultValue, "underline", nullptr, nullptr, true, false },
197 { nsHTMLCSSUtils::eCSSEditableProperty_NONE, 0 }
198 };
199
200 const nsHTMLCSSUtils::CSSEquivTable strikeEquivTable[] = {
201 { nsHTMLCSSUtils::eCSSEditableProperty_text_decoration, ProcessDefaultValue, "line-through", nullptr, nullptr, true, false },
202 { nsHTMLCSSUtils::eCSSEditableProperty_NONE, 0 }
203 };
204
205 const nsHTMLCSSUtils::CSSEquivTable ttEquivTable[] = {
206 { nsHTMLCSSUtils::eCSSEditableProperty_font_family, ProcessDefaultValue, "monospace", nullptr, nullptr, true, false },
207 { nsHTMLCSSUtils::eCSSEditableProperty_NONE, 0 }
208 };
209
210 const nsHTMLCSSUtils::CSSEquivTable fontColorEquivTable[] = {
211 { nsHTMLCSSUtils::eCSSEditableProperty_color, ProcessSameValue, nullptr, nullptr, nullptr, true, false },
212 { nsHTMLCSSUtils::eCSSEditableProperty_NONE, 0 }
213 };
214
215 const nsHTMLCSSUtils::CSSEquivTable fontFaceEquivTable[] = {
216 { nsHTMLCSSUtils::eCSSEditableProperty_font_family, ProcessSameValue, nullptr, nullptr, nullptr, true, false },
217 { nsHTMLCSSUtils::eCSSEditableProperty_NONE, 0 }
218 };
219
220 const nsHTMLCSSUtils::CSSEquivTable bgcolorEquivTable[] = {
221 { nsHTMLCSSUtils::eCSSEditableProperty_background_color, ProcessSameValue, nullptr, nullptr, nullptr, true, false },
222 { nsHTMLCSSUtils::eCSSEditableProperty_NONE, 0 }
223 };
224
225 const nsHTMLCSSUtils::CSSEquivTable backgroundImageEquivTable[] = {
226 { nsHTMLCSSUtils::eCSSEditableProperty_background_image, ProcessExtendedValue, nullptr, "url(", ")", true, true },
227 { nsHTMLCSSUtils::eCSSEditableProperty_NONE, 0 }
228 };
229
230 const nsHTMLCSSUtils::CSSEquivTable textColorEquivTable[] = {
231 { nsHTMLCSSUtils::eCSSEditableProperty_color, ProcessSameValue, nullptr, nullptr, nullptr, true, false },
232 { nsHTMLCSSUtils::eCSSEditableProperty_NONE, 0 }
233 };
234
235 const nsHTMLCSSUtils::CSSEquivTable borderEquivTable[] = {
236 { nsHTMLCSSUtils::eCSSEditableProperty_border, ProcessExtendedValue, nullptr, nullptr, "px solid", true, false },
237 { nsHTMLCSSUtils::eCSSEditableProperty_NONE, 0 }
238 };
239
240 const nsHTMLCSSUtils::CSSEquivTable textAlignEquivTable[] = {
241 { nsHTMLCSSUtils::eCSSEditableProperty_text_align, ProcessSameValue, nullptr, nullptr, nullptr, true, false },
242 { nsHTMLCSSUtils::eCSSEditableProperty_NONE, 0 }
243 };
244
245 const nsHTMLCSSUtils::CSSEquivTable captionAlignEquivTable[] = {
246 { nsHTMLCSSUtils::eCSSEditableProperty_caption_side, ProcessSameValue, nullptr, nullptr, nullptr, true, false },
247 { nsHTMLCSSUtils::eCSSEditableProperty_NONE, 0 }
248 };
249
250 const nsHTMLCSSUtils::CSSEquivTable verticalAlignEquivTable[] = {
251 { nsHTMLCSSUtils::eCSSEditableProperty_vertical_align, ProcessSameValue, nullptr, nullptr, nullptr, true, false },
252 { nsHTMLCSSUtils::eCSSEditableProperty_NONE, 0 }
253 };
254
255 const nsHTMLCSSUtils::CSSEquivTable nowrapEquivTable[] = {
256 { nsHTMLCSSUtils::eCSSEditableProperty_whitespace, ProcessDefaultValue, "nowrap", nullptr, nullptr, true, false },
257 { nsHTMLCSSUtils::eCSSEditableProperty_NONE, 0 }
258 };
259
260 const nsHTMLCSSUtils::CSSEquivTable widthEquivTable[] = {
261 { nsHTMLCSSUtils::eCSSEditableProperty_width, ProcessLengthValue, nullptr, nullptr, nullptr, true, false },
262 { nsHTMLCSSUtils::eCSSEditableProperty_NONE, 0 }
263 };
264
265 const nsHTMLCSSUtils::CSSEquivTable heightEquivTable[] = {
266 { nsHTMLCSSUtils::eCSSEditableProperty_height, ProcessLengthValue, nullptr, nullptr, nullptr, true, false },
267 { nsHTMLCSSUtils::eCSSEditableProperty_NONE, 0 }
268 };
269
270 const nsHTMLCSSUtils::CSSEquivTable listStyleTypeEquivTable[] = {
271 { nsHTMLCSSUtils::eCSSEditableProperty_list_style_type, ProcessListStyleTypeValue, nullptr, nullptr, nullptr, true, true },
272 { nsHTMLCSSUtils::eCSSEditableProperty_NONE, 0 }
273 };
274
275 const nsHTMLCSSUtils::CSSEquivTable tableAlignEquivTable[] = {
276 { nsHTMLCSSUtils::eCSSEditableProperty_text_align, ProcessDefaultValue, "left", nullptr, nullptr, false, false },
277 { nsHTMLCSSUtils::eCSSEditableProperty_margin_left, ProcessMarginLeftValue, nullptr, nullptr, nullptr, true, false },
278 { nsHTMLCSSUtils::eCSSEditableProperty_margin_right, ProcessMarginRightValue, nullptr, nullptr, nullptr, true, false },
279 { nsHTMLCSSUtils::eCSSEditableProperty_NONE, 0 }
280 };
281
282 const nsHTMLCSSUtils::CSSEquivTable hrAlignEquivTable[] = {
283 { nsHTMLCSSUtils::eCSSEditableProperty_margin_left, ProcessMarginLeftValue, nullptr, nullptr, nullptr, true, false },
284 { nsHTMLCSSUtils::eCSSEditableProperty_margin_right, ProcessMarginRightValue, nullptr, nullptr, nullptr, true, false },
285 { nsHTMLCSSUtils::eCSSEditableProperty_NONE, 0 }
286 };
287
288 nsHTMLCSSUtils::nsHTMLCSSUtils(nsHTMLEditor* aEditor)
289 : mHTMLEditor(aEditor)
290 , mIsCSSPrefChecked(true)
291 {
292 // let's retrieve the value of the "CSS editing" pref
293 mIsCSSPrefChecked = Preferences::GetBool("editor.use_css", mIsCSSPrefChecked);
294 }
295
296 nsHTMLCSSUtils::~nsHTMLCSSUtils()
297 {
298 }
299
300 // Answers true if we have some CSS equivalence for the HTML style defined
301 // by aProperty and/or aAttribute for the node aNode
302 bool
303 nsHTMLCSSUtils::IsCSSEditableProperty(nsIDOMNode* aNode,
304 nsIAtom* aProperty,
305 const nsAString* aAttribute)
306 {
307 NS_ASSERTION(aNode, "Shouldn't you pass aNode? - Bug 214025");
308
309 nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
310 NS_ENSURE_TRUE(content, false);
311 return IsCSSEditableProperty(content, aProperty, aAttribute);
312 }
313
314 bool
315 nsHTMLCSSUtils::IsCSSEditableProperty(nsIContent* aNode,
316 nsIAtom* aProperty,
317 const nsAString* aAttribute)
318 {
319 MOZ_ASSERT(aNode);
320
321 nsIContent* content = aNode;
322 // we need an element node here
323 if (content->NodeType() == nsIDOMNode::TEXT_NODE) {
324 content = content->GetParent();
325 NS_ENSURE_TRUE(content, false);
326 }
327
328 nsIAtom *tagName = content->Tag();
329 // brade: shouldn't some of the above go below the next block?
330
331 // html inline styles B I TT U STRIKE and COLOR/FACE on FONT
332 if (nsEditProperty::b == aProperty
333 || nsEditProperty::i == aProperty
334 || nsEditProperty::tt == aProperty
335 || nsEditProperty::u == aProperty
336 || nsEditProperty::strike == aProperty
337 || ((nsEditProperty::font == aProperty) && aAttribute &&
338 (aAttribute->EqualsLiteral("color") ||
339 aAttribute->EqualsLiteral("face")))) {
340 return true;
341 }
342
343 // ALIGN attribute on elements supporting it
344 if (aAttribute && (aAttribute->EqualsLiteral("align")) &&
345 (nsEditProperty::div == tagName
346 || nsEditProperty::p == tagName
347 || nsEditProperty::h1 == tagName
348 || nsEditProperty::h2 == tagName
349 || nsEditProperty::h3 == tagName
350 || nsEditProperty::h4 == tagName
351 || nsEditProperty::h5 == tagName
352 || nsEditProperty::h6 == tagName
353 || nsEditProperty::td == tagName
354 || nsEditProperty::th == tagName
355 || nsEditProperty::table == tagName
356 || nsEditProperty::hr == tagName
357 // brade: for the above, why not use nsHTMLEditUtils::SupportsAlignAttr
358 // brade: but it also checks for tbody, tfoot, thead
359 // Let's add the following elements here even if ALIGN has not
360 // the same meaning for them
361 || nsEditProperty::legend == tagName
362 || nsEditProperty::caption == tagName)) {
363 return true;
364 }
365
366 if (aAttribute && (aAttribute->EqualsLiteral("valign")) &&
367 (nsEditProperty::col == tagName
368 || nsEditProperty::colgroup == tagName
369 || nsEditProperty::tbody == tagName
370 || nsEditProperty::td == tagName
371 || nsEditProperty::th == tagName
372 || nsEditProperty::tfoot == tagName
373 || nsEditProperty::thead == tagName
374 || nsEditProperty::tr == tagName)) {
375 return true;
376 }
377
378 // attributes TEXT, BACKGROUND and BGCOLOR on BODY
379 if (aAttribute && (nsEditProperty::body == tagName) &&
380 (aAttribute->EqualsLiteral("text")
381 || aAttribute->EqualsLiteral("background")
382 || aAttribute->EqualsLiteral("bgcolor"))) {
383 return true;
384 }
385
386 // attribute BGCOLOR on other elements
387 if (aAttribute && aAttribute->EqualsLiteral("bgcolor")) {
388 return true;
389 }
390
391 // attributes HEIGHT, WIDTH and NOWRAP on TD and TH
392 if (aAttribute && ((nsEditProperty::td == tagName)
393 || (nsEditProperty::th == tagName)) &&
394 (aAttribute->EqualsLiteral("height")
395 || aAttribute->EqualsLiteral("width")
396 || aAttribute->EqualsLiteral("nowrap"))) {
397 return true;
398 }
399
400 // attributes HEIGHT and WIDTH on TABLE
401 if (aAttribute && (nsEditProperty::table == tagName) &&
402 (aAttribute->EqualsLiteral("height")
403 || aAttribute->EqualsLiteral("width"))) {
404 return true;
405 }
406
407 // attributes SIZE and WIDTH on HR
408 if (aAttribute && (nsEditProperty::hr == tagName) &&
409 (aAttribute->EqualsLiteral("size")
410 || aAttribute->EqualsLiteral("width"))) {
411 return true;
412 }
413
414 // attribute TYPE on OL UL LI
415 if (aAttribute && (nsEditProperty::ol == tagName
416 || nsEditProperty::ul == tagName
417 || nsEditProperty::li == tagName) &&
418 aAttribute->EqualsLiteral("type")) {
419 return true;
420 }
421
422 if (aAttribute && nsEditProperty::img == tagName &&
423 (aAttribute->EqualsLiteral("border")
424 || aAttribute->EqualsLiteral("width")
425 || aAttribute->EqualsLiteral("height"))) {
426 return true;
427 }
428
429 // other elements that we can align using CSS even if they
430 // can't carry the html ALIGN attribute
431 if (aAttribute && aAttribute->EqualsLiteral("align") &&
432 (nsEditProperty::ul == tagName
433 || nsEditProperty::ol == tagName
434 || nsEditProperty::dl == tagName
435 || nsEditProperty::li == tagName
436 || nsEditProperty::dd == tagName
437 || nsEditProperty::dt == tagName
438 || nsEditProperty::address == tagName
439 || nsEditProperty::pre == tagName
440 || nsEditProperty::ul == tagName)) {
441 return true;
442 }
443
444 return false;
445 }
446
447 // the lowest level above the transaction; adds the css declaration "aProperty : aValue" to
448 // the inline styles carried by aElement
449 nsresult
450 nsHTMLCSSUtils::SetCSSProperty(nsIDOMElement *aElement, nsIAtom * aProperty, const nsAString & aValue,
451 bool aSuppressTransaction)
452 {
453 nsRefPtr<ChangeCSSInlineStyleTxn> txn;
454 nsresult result = CreateCSSPropertyTxn(aElement, aProperty, aValue,
455 getter_AddRefs(txn), false);
456 if (NS_SUCCEEDED(result)) {
457 if (aSuppressTransaction) {
458 result = txn->DoTransaction();
459 }
460 else {
461 result = mHTMLEditor->DoTransaction(txn);
462 }
463 }
464 return result;
465 }
466
467 nsresult
468 nsHTMLCSSUtils::SetCSSPropertyPixels(nsIDOMElement *aElement,
469 nsIAtom *aProperty,
470 int32_t aIntValue,
471 bool aSuppressTransaction)
472 {
473 nsAutoString s;
474 s.AppendInt(aIntValue);
475 return SetCSSProperty(aElement, aProperty, s + NS_LITERAL_STRING("px"),
476 aSuppressTransaction);
477 }
478
479 // the lowest level above the transaction; removes the value aValue from the list of values
480 // specified for the CSS property aProperty, or totally remove the declaration if this
481 // property accepts only one value
482 nsresult
483 nsHTMLCSSUtils::RemoveCSSProperty(nsIDOMElement *aElement, nsIAtom * aProperty, const nsAString & aValue,
484 bool aSuppressTransaction)
485 {
486 nsRefPtr<ChangeCSSInlineStyleTxn> txn;
487 nsresult result = CreateCSSPropertyTxn(aElement, aProperty, aValue,
488 getter_AddRefs(txn), true);
489 if (NS_SUCCEEDED(result)) {
490 if (aSuppressTransaction) {
491 result = txn->DoTransaction();
492 }
493 else {
494 result = mHTMLEditor->DoTransaction(txn);
495 }
496 }
497 return result;
498 }
499
500 nsresult
501 nsHTMLCSSUtils::CreateCSSPropertyTxn(nsIDOMElement *aElement,
502 nsIAtom * aAttribute,
503 const nsAString& aValue,
504 ChangeCSSInlineStyleTxn ** aTxn,
505 bool aRemoveProperty)
506 {
507 NS_ENSURE_TRUE(aElement, NS_ERROR_NULL_POINTER);
508
509 *aTxn = new ChangeCSSInlineStyleTxn();
510 NS_ENSURE_TRUE(*aTxn, NS_ERROR_OUT_OF_MEMORY);
511 NS_ADDREF(*aTxn);
512 return (*aTxn)->Init(mHTMLEditor, aElement, aAttribute, aValue, aRemoveProperty);
513 }
514
515 nsresult
516 nsHTMLCSSUtils::GetSpecifiedProperty(nsIDOMNode *aNode, nsIAtom *aProperty,
517 nsAString & aValue)
518 {
519 return GetCSSInlinePropertyBase(aNode, aProperty, aValue, eSpecified);
520 }
521
522 nsresult
523 nsHTMLCSSUtils::GetComputedProperty(nsIDOMNode *aNode, nsIAtom *aProperty,
524 nsAString & aValue)
525 {
526 return GetCSSInlinePropertyBase(aNode, aProperty, aValue, eComputed);
527 }
528
529 nsresult
530 nsHTMLCSSUtils::GetCSSInlinePropertyBase(nsIDOMNode* aNode, nsIAtom* aProperty,
531 nsAString& aValue,
532 StyleType aStyleType)
533 {
534 nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
535 return GetCSSInlinePropertyBase(node, aProperty, aValue, aStyleType);
536 }
537
538 nsresult
539 nsHTMLCSSUtils::GetCSSInlinePropertyBase(nsINode* aNode, nsIAtom* aProperty,
540 nsAString& aValue,
541 StyleType aStyleType)
542 {
543 MOZ_ASSERT(aNode && aProperty);
544 aValue.Truncate();
545
546 nsCOMPtr<dom::Element> element = GetElementContainerOrSelf(aNode);
547 NS_ENSURE_TRUE(element, NS_ERROR_NULL_POINTER);
548
549 if (aStyleType == eComputed) {
550 // Get the all the computed css styles attached to the element node
551 nsRefPtr<nsComputedDOMStyle> cssDecl = GetComputedStyle(element);
552 NS_ENSURE_STATE(cssDecl);
553
554 // from these declarations, get the one we want and that one only
555 MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
556 cssDecl->GetPropertyValue(nsDependentAtomString(aProperty), aValue)));
557
558 return NS_OK;
559 }
560
561 MOZ_ASSERT(aStyleType == eSpecified);
562 nsRefPtr<css::StyleRule> rule = element->GetInlineStyleRule();
563 if (!rule) {
564 return NS_OK;
565 }
566 nsCSSProperty prop =
567 nsCSSProps::LookupProperty(nsDependentAtomString(aProperty),
568 nsCSSProps::eEnabledForAllContent);
569 MOZ_ASSERT(prop != eCSSProperty_UNKNOWN);
570 rule->GetDeclaration()->GetValue(prop, aValue);
571
572 return NS_OK;
573 }
574
575 already_AddRefed<nsComputedDOMStyle>
576 nsHTMLCSSUtils::GetComputedStyle(nsIDOMElement* aElement)
577 {
578 nsCOMPtr<dom::Element> element = do_QueryInterface(aElement);
579 return GetComputedStyle(element);
580 }
581
582 already_AddRefed<nsComputedDOMStyle>
583 nsHTMLCSSUtils::GetComputedStyle(dom::Element* aElement)
584 {
585 MOZ_ASSERT(aElement);
586
587 nsIDocument* doc = aElement->GetCurrentDoc();
588 NS_ENSURE_TRUE(doc, nullptr);
589
590 nsIPresShell* presShell = doc->GetShell();
591 NS_ENSURE_TRUE(presShell, nullptr);
592
593 nsRefPtr<nsComputedDOMStyle> style =
594 NS_NewComputedDOMStyle(aElement, EmptyString(), presShell);
595
596 return style.forget();
597 }
598
599 // remove the CSS style "aProperty : aPropertyValue" and possibly remove the whole node
600 // if it is a span and if its only attribute is _moz_dirty
601 nsresult
602 nsHTMLCSSUtils::RemoveCSSInlineStyle(nsIDOMNode *aNode, nsIAtom *aProperty, const nsAString & aPropertyValue)
603 {
604 nsCOMPtr<nsIDOMElement> elem = do_QueryInterface(aNode);
605
606 // remove the property from the style attribute
607 nsresult res = RemoveCSSProperty(elem, aProperty, aPropertyValue, false);
608 NS_ENSURE_SUCCESS(res, res);
609
610 nsCOMPtr<dom::Element> element = do_QueryInterface(aNode);
611 if (!element || !element->IsHTML(nsGkAtoms::span) ||
612 nsHTMLEditor::HasAttributes(element)) {
613 return NS_OK;
614 }
615
616 return mHTMLEditor->RemoveContainer(aNode);
617 }
618
619 // Answers true is the property can be removed by setting a "none" CSS value
620 // on a node
621 bool
622 nsHTMLCSSUtils::IsCSSInvertable(nsIAtom *aProperty, const nsAString *aAttribute)
623 {
624 return bool(nsEditProperty::b == aProperty);
625 }
626
627 // Get the default browser background color if we need it for GetCSSBackgroundColorState
628 void
629 nsHTMLCSSUtils::GetDefaultBackgroundColor(nsAString & aColor)
630 {
631 if (Preferences::GetBool("editor.use_custom_colors", false)) {
632 nsresult rv = Preferences::GetString("editor.background_color", &aColor);
633 // XXX Why don't you validate the pref value?
634 if (NS_FAILED(rv)) {
635 NS_WARNING("failed to get editor.background_color");
636 aColor.AssignLiteral("#ffffff"); // Default to white
637 }
638 return;
639 }
640
641 if (Preferences::GetBool("browser.display.use_system_colors", false)) {
642 return;
643 }
644
645 nsresult rv =
646 Preferences::GetString("browser.display.background_color", &aColor);
647 // XXX Why don't you validate the pref value?
648 if (NS_FAILED(rv)) {
649 NS_WARNING("failed to get browser.display.background_color");
650 aColor.AssignLiteral("#ffffff"); // Default to white
651 }
652 }
653
654 // Get the default length unit used for CSS Indent/Outdent
655 void
656 nsHTMLCSSUtils::GetDefaultLengthUnit(nsAString & aLengthUnit)
657 {
658 nsresult rv =
659 Preferences::GetString("editor.css.default_length_unit", &aLengthUnit);
660 // XXX Why don't you validate the pref value?
661 if (NS_FAILED(rv)) {
662 aLengthUnit.AssignLiteral("px");
663 }
664 }
665
666 // Unfortunately, CSSStyleDeclaration::GetPropertyCSSValue is not yet implemented...
667 // We need then a way to determine the number part and the unit from aString, aString
668 // being the result of a GetPropertyValue query...
669 void
670 nsHTMLCSSUtils::ParseLength(const nsAString & aString, float * aValue, nsIAtom ** aUnit)
671 {
672 nsAString::const_iterator iter;
673 aString.BeginReading(iter);
674
675 float a = 10.0f , b = 1.0f, value = 0;
676 int8_t sign = 1;
677 int32_t i = 0, j = aString.Length();
678 char16_t c;
679 bool floatingPointFound = false;
680 c = *iter;
681 if (char16_t('-') == c) { sign = -1; iter++; i++; }
682 else if (char16_t('+') == c) { iter++; i++; }
683 while (i < j) {
684 c = *iter;
685 if ((char16_t('0') == c) ||
686 (char16_t('1') == c) ||
687 (char16_t('2') == c) ||
688 (char16_t('3') == c) ||
689 (char16_t('4') == c) ||
690 (char16_t('5') == c) ||
691 (char16_t('6') == c) ||
692 (char16_t('7') == c) ||
693 (char16_t('8') == c) ||
694 (char16_t('9') == c)) {
695 value = (value * a) + (b * (c - char16_t('0')));
696 b = b / 10 * a;
697 }
698 else if (!floatingPointFound && (char16_t('.') == c)) {
699 floatingPointFound = true;
700 a = 1.0f; b = 0.1f;
701 }
702 else break;
703 iter++;
704 i++;
705 }
706 *aValue = value * sign;
707 *aUnit = NS_NewAtom(StringTail(aString, j-i)).take();
708 }
709
710 void
711 nsHTMLCSSUtils::GetCSSPropertyAtom(nsCSSEditableProperty aProperty, nsIAtom ** aAtom)
712 {
713 *aAtom = nullptr;
714 switch (aProperty) {
715 case eCSSEditableProperty_background_color:
716 *aAtom = nsEditProperty::cssBackgroundColor;
717 break;
718 case eCSSEditableProperty_background_image:
719 *aAtom = nsEditProperty::cssBackgroundImage;
720 break;
721 case eCSSEditableProperty_border:
722 *aAtom = nsEditProperty::cssBorder;
723 break;
724 case eCSSEditableProperty_caption_side:
725 *aAtom = nsEditProperty::cssCaptionSide;
726 break;
727 case eCSSEditableProperty_color:
728 *aAtom = nsEditProperty::cssColor;
729 break;
730 case eCSSEditableProperty_float:
731 *aAtom = nsEditProperty::cssFloat;
732 break;
733 case eCSSEditableProperty_font_family:
734 *aAtom = nsEditProperty::cssFontFamily;
735 break;
736 case eCSSEditableProperty_font_size:
737 *aAtom = nsEditProperty::cssFontSize;
738 break;
739 case eCSSEditableProperty_font_style:
740 *aAtom = nsEditProperty::cssFontStyle;
741 break;
742 case eCSSEditableProperty_font_weight:
743 *aAtom = nsEditProperty::cssFontWeight;
744 break;
745 case eCSSEditableProperty_height:
746 *aAtom = nsEditProperty::cssHeight;
747 break;
748 case eCSSEditableProperty_list_style_type:
749 *aAtom = nsEditProperty::cssListStyleType;
750 break;
751 case eCSSEditableProperty_margin_left:
752 *aAtom = nsEditProperty::cssMarginLeft;
753 break;
754 case eCSSEditableProperty_margin_right:
755 *aAtom = nsEditProperty::cssMarginRight;
756 break;
757 case eCSSEditableProperty_text_align:
758 *aAtom = nsEditProperty::cssTextAlign;
759 break;
760 case eCSSEditableProperty_text_decoration:
761 *aAtom = nsEditProperty::cssTextDecoration;
762 break;
763 case eCSSEditableProperty_vertical_align:
764 *aAtom = nsEditProperty::cssVerticalAlign;
765 break;
766 case eCSSEditableProperty_whitespace:
767 *aAtom = nsEditProperty::cssWhitespace;
768 break;
769 case eCSSEditableProperty_width:
770 *aAtom = nsEditProperty::cssWidth;
771 break;
772 case eCSSEditableProperty_NONE:
773 // intentionally empty
774 break;
775 }
776 }
777
778 // Populate aProperty and aValueArray with the CSS declarations equivalent to the
779 // value aValue according to the equivalence table aEquivTable
780 void
781 nsHTMLCSSUtils::BuildCSSDeclarations(nsTArray<nsIAtom*> & aPropertyArray,
782 nsTArray<nsString> & aValueArray,
783 const CSSEquivTable * aEquivTable,
784 const nsAString * aValue,
785 bool aGetOrRemoveRequest)
786 {
787 // clear arrays
788 aPropertyArray.Clear();
789 aValueArray.Clear();
790
791 // if we have an input value, let's use it
792 nsAutoString value, lowerCasedValue;
793 if (aValue) {
794 value.Assign(*aValue);
795 lowerCasedValue.Assign(*aValue);
796 ToLowerCase(lowerCasedValue);
797 }
798
799 int8_t index = 0;
800 nsCSSEditableProperty cssProperty = aEquivTable[index].cssProperty;
801 while (cssProperty) {
802 if (!aGetOrRemoveRequest|| aEquivTable[index].gettable) {
803 nsAutoString cssValue, cssPropertyString;
804 nsIAtom * cssPropertyAtom;
805 // find the equivalent css value for the index-th property in
806 // the equivalence table
807 (*aEquivTable[index].processValueFunctor) ((!aGetOrRemoveRequest || aEquivTable[index].caseSensitiveValue) ? &value : &lowerCasedValue,
808 cssValue,
809 aEquivTable[index].defaultValue,
810 aEquivTable[index].prependValue,
811 aEquivTable[index].appendValue);
812 GetCSSPropertyAtom(cssProperty, &cssPropertyAtom);
813 aPropertyArray.AppendElement(cssPropertyAtom);
814 aValueArray.AppendElement(cssValue);
815 }
816 index++;
817 cssProperty = aEquivTable[index].cssProperty;
818 }
819 }
820
821 // Populate cssPropertyArray and cssValueArray with the declarations equivalent
822 // to aHTMLProperty/aAttribute/aValue for the node aNode
823 void
824 nsHTMLCSSUtils::GenerateCSSDeclarationsFromHTMLStyle(dom::Element* aElement,
825 nsIAtom* aHTMLProperty,
826 const nsAString* aAttribute,
827 const nsAString* aValue,
828 nsTArray<nsIAtom*>& cssPropertyArray,
829 nsTArray<nsString>& cssValueArray,
830 bool aGetOrRemoveRequest)
831 {
832 MOZ_ASSERT(aElement);
833 nsIAtom* tagName = aElement->Tag();
834 const nsHTMLCSSUtils::CSSEquivTable* equivTable = nullptr;
835
836 if (nsEditProperty::b == aHTMLProperty) {
837 equivTable = boldEquivTable;
838 } else if (nsEditProperty::i == aHTMLProperty) {
839 equivTable = italicEquivTable;
840 } else if (nsEditProperty::u == aHTMLProperty) {
841 equivTable = underlineEquivTable;
842 } else if (nsEditProperty::strike == aHTMLProperty) {
843 equivTable = strikeEquivTable;
844 } else if (nsEditProperty::tt == aHTMLProperty) {
845 equivTable = ttEquivTable;
846 } else if (aAttribute) {
847 if (nsEditProperty::font == aHTMLProperty &&
848 aAttribute->EqualsLiteral("color")) {
849 equivTable = fontColorEquivTable;
850 } else if (nsEditProperty::font == aHTMLProperty &&
851 aAttribute->EqualsLiteral("face")) {
852 equivTable = fontFaceEquivTable;
853 } else if (aAttribute->EqualsLiteral("bgcolor")) {
854 equivTable = bgcolorEquivTable;
855 } else if (aAttribute->EqualsLiteral("background")) {
856 equivTable = backgroundImageEquivTable;
857 } else if (aAttribute->EqualsLiteral("text")) {
858 equivTable = textColorEquivTable;
859 } else if (aAttribute->EqualsLiteral("border")) {
860 equivTable = borderEquivTable;
861 } else if (aAttribute->EqualsLiteral("align")) {
862 if (nsEditProperty::table == tagName) {
863 equivTable = tableAlignEquivTable;
864 } else if (nsEditProperty::hr == tagName) {
865 equivTable = hrAlignEquivTable;
866 } else if (nsEditProperty::legend == tagName ||
867 nsEditProperty::caption == tagName) {
868 equivTable = captionAlignEquivTable;
869 } else {
870 equivTable = textAlignEquivTable;
871 }
872 } else if (aAttribute->EqualsLiteral("valign")) {
873 equivTable = verticalAlignEquivTable;
874 } else if (aAttribute->EqualsLiteral("nowrap")) {
875 equivTable = nowrapEquivTable;
876 } else if (aAttribute->EqualsLiteral("width")) {
877 equivTable = widthEquivTable;
878 } else if (aAttribute->EqualsLiteral("height") ||
879 (nsEditProperty::hr == tagName &&
880 aAttribute->EqualsLiteral("size"))) {
881 equivTable = heightEquivTable;
882 } else if (aAttribute->EqualsLiteral("type") &&
883 (nsEditProperty::ol == tagName
884 || nsEditProperty::ul == tagName
885 || nsEditProperty::li == tagName)) {
886 equivTable = listStyleTypeEquivTable;
887 }
888 }
889 if (equivTable) {
890 BuildCSSDeclarations(cssPropertyArray, cssValueArray, equivTable,
891 aValue, aGetOrRemoveRequest);
892 }
893 }
894
895 // Add to aNode the CSS inline style equivalent to HTMLProperty/aAttribute/
896 // aValue for the node, and return in aCount the number of CSS properties set
897 // by the call. The dom::Element version returns aCount instead.
898 int32_t
899 nsHTMLCSSUtils::SetCSSEquivalentToHTMLStyle(dom::Element* aElement,
900 nsIAtom* aProperty,
901 const nsAString* aAttribute,
902 const nsAString* aValue,
903 bool aSuppressTransaction)
904 {
905 MOZ_ASSERT(aElement && aProperty);
906 MOZ_ASSERT_IF(aAttribute, aValue);
907 int32_t count;
908 // This can only fail if SetCSSProperty fails, which should only happen if
909 // something is pretty badly wrong. In this case we assert so that hopefully
910 // someone will notice, but there's nothing more sensible to do than just
911 // return the count and carry on.
912 nsresult res = SetCSSEquivalentToHTMLStyle(aElement->AsDOMNode(),
913 aProperty, aAttribute,
914 aValue, &count,
915 aSuppressTransaction);
916 NS_ASSERTION(NS_SUCCEEDED(res), "SetCSSEquivalentToHTMLStyle failed");
917 NS_ENSURE_SUCCESS(res, count);
918 return count;
919 }
920
921 nsresult
922 nsHTMLCSSUtils::SetCSSEquivalentToHTMLStyle(nsIDOMNode * aNode,
923 nsIAtom *aHTMLProperty,
924 const nsAString *aAttribute,
925 const nsAString *aValue,
926 int32_t * aCount,
927 bool aSuppressTransaction)
928 {
929 nsCOMPtr<dom::Element> element = do_QueryInterface(aNode);
930 *aCount = 0;
931 if (!element || !IsCSSEditableProperty(element, aHTMLProperty, aAttribute)) {
932 return NS_OK;
933 }
934
935 // we can apply the styles only if the node is an element and if we have
936 // an equivalence for the requested HTML style in this implementation
937
938 // Find the CSS equivalence to the HTML style
939 nsTArray<nsIAtom*> cssPropertyArray;
940 nsTArray<nsString> cssValueArray;
941 GenerateCSSDeclarationsFromHTMLStyle(element, aHTMLProperty, aAttribute,
942 aValue, cssPropertyArray, cssValueArray,
943 false);
944
945 nsCOMPtr<nsIDOMElement> domElement = do_QueryInterface(element);
946 // set the individual CSS inline styles
947 *aCount = cssPropertyArray.Length();
948 for (int32_t index = 0; index < *aCount; index++) {
949 nsresult res = SetCSSProperty(domElement, cssPropertyArray[index],
950 cssValueArray[index], aSuppressTransaction);
951 NS_ENSURE_SUCCESS(res, res);
952 }
953 return NS_OK;
954 }
955
956 // Remove from aNode the CSS inline style equivalent to HTMLProperty/aAttribute/aValue for the node
957 nsresult
958 nsHTMLCSSUtils::RemoveCSSEquivalentToHTMLStyle(nsIDOMNode * aNode,
959 nsIAtom *aHTMLProperty,
960 const nsAString *aAttribute,
961 const nsAString *aValue,
962 bool aSuppressTransaction)
963 {
964 nsCOMPtr<dom::Element> element = do_QueryInterface(aNode);
965 NS_ENSURE_TRUE(element, NS_OK);
966
967 return RemoveCSSEquivalentToHTMLStyle(element, aHTMLProperty, aAttribute,
968 aValue, aSuppressTransaction);
969 }
970
971 nsresult
972 nsHTMLCSSUtils::RemoveCSSEquivalentToHTMLStyle(dom::Element* aElement,
973 nsIAtom* aHTMLProperty,
974 const nsAString* aAttribute,
975 const nsAString* aValue,
976 bool aSuppressTransaction)
977 {
978 MOZ_ASSERT(aElement);
979
980 if (!IsCSSEditableProperty(aElement, aHTMLProperty, aAttribute)) {
981 return NS_OK;
982 }
983
984 // we can apply the styles only if the node is an element and if we have
985 // an equivalence for the requested HTML style in this implementation
986
987 // Find the CSS equivalence to the HTML style
988 nsTArray<nsIAtom*> cssPropertyArray;
989 nsTArray<nsString> cssValueArray;
990 GenerateCSSDeclarationsFromHTMLStyle(aElement, aHTMLProperty, aAttribute,
991 aValue, cssPropertyArray, cssValueArray,
992 true);
993
994 nsCOMPtr<nsIDOMElement> domElement = do_QueryInterface(aElement);
995 // remove the individual CSS inline styles
996 int32_t count = cssPropertyArray.Length();
997 for (int32_t index = 0; index < count; index++) {
998 nsresult res = RemoveCSSProperty(domElement,
999 cssPropertyArray[index],
1000 cssValueArray[index],
1001 aSuppressTransaction);
1002 NS_ENSURE_SUCCESS(res, res);
1003 }
1004 return NS_OK;
1005 }
1006
1007 // returns in aValueString the list of values for the CSS equivalences to
1008 // the HTML style aHTMLProperty/aAttribute/aValueString for the node aNode;
1009 // the value of aStyleType controls the styles we retrieve : specified or
1010 // computed.
1011 nsresult
1012 nsHTMLCSSUtils::GetCSSEquivalentToHTMLInlineStyleSet(nsINode* aNode,
1013 nsIAtom *aHTMLProperty,
1014 const nsAString *aAttribute,
1015 nsAString & aValueString,
1016 StyleType aStyleType)
1017 {
1018 aValueString.Truncate();
1019 nsCOMPtr<dom::Element> theElement = GetElementContainerOrSelf(aNode);
1020 NS_ENSURE_TRUE(theElement, NS_ERROR_NULL_POINTER);
1021
1022 if (!theElement || !IsCSSEditableProperty(theElement, aHTMLProperty, aAttribute)) {
1023 return NS_OK;
1024 }
1025
1026 // Yes, the requested HTML style has a CSS equivalence in this implementation
1027 nsTArray<nsIAtom*> cssPropertyArray;
1028 nsTArray<nsString> cssValueArray;
1029 // get the CSS equivalence with last param true indicating we want only the
1030 // "gettable" properties
1031 GenerateCSSDeclarationsFromHTMLStyle(theElement, aHTMLProperty, aAttribute, nullptr,
1032 cssPropertyArray, cssValueArray, true);
1033 int32_t count = cssPropertyArray.Length();
1034 for (int32_t index = 0; index < count; index++) {
1035 nsAutoString valueString;
1036 // retrieve the specified/computed value of the property
1037 nsresult res = GetCSSInlinePropertyBase(theElement, cssPropertyArray[index],
1038 valueString, aStyleType);
1039 NS_ENSURE_SUCCESS(res, res);
1040 // append the value to aValueString (possibly with a leading whitespace)
1041 if (index) {
1042 aValueString.Append(char16_t(' '));
1043 }
1044 aValueString.Append(valueString);
1045 }
1046 return NS_OK;
1047 }
1048
1049 // Does the node aNode (or its parent, if it's not an element node) have a CSS
1050 // style equivalent to the HTML style aHTMLProperty/aHTMLAttribute/valueString?
1051 // The value of aStyleType controls the styles we retrieve: specified or
1052 // computed. The return value aIsSet is true if the CSS styles are set.
1053 //
1054 // The nsIContent variant returns aIsSet instead of using an out parameter, and
1055 // does not modify aValue.
1056 bool
1057 nsHTMLCSSUtils::IsCSSEquivalentToHTMLInlineStyleSet(nsIContent* aContent,
1058 nsIAtom* aProperty,
1059 const nsAString* aAttribute,
1060 const nsAString& aValue,
1061 StyleType aStyleType)
1062 {
1063 MOZ_ASSERT(aContent && aProperty);
1064 bool isSet;
1065 nsAutoString value(aValue);
1066 nsresult res = IsCSSEquivalentToHTMLInlineStyleSet(aContent->AsDOMNode(),
1067 aProperty, aAttribute,
1068 isSet, value, aStyleType);
1069 NS_ASSERTION(NS_SUCCEEDED(res), "IsCSSEquivalentToHTMLInlineStyleSet failed");
1070 NS_ENSURE_SUCCESS(res, false);
1071 return isSet;
1072 }
1073
1074 nsresult
1075 nsHTMLCSSUtils::IsCSSEquivalentToHTMLInlineStyleSet(nsIDOMNode *aNode,
1076 nsIAtom *aHTMLProperty,
1077 const nsAString *aHTMLAttribute,
1078 bool& aIsSet,
1079 nsAString& valueString,
1080 StyleType aStyleType)
1081 {
1082 NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER);
1083
1084 nsAutoString htmlValueString(valueString);
1085 aIsSet = false;
1086 nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
1087 do {
1088 valueString.Assign(htmlValueString);
1089 // get the value of the CSS equivalent styles
1090 nsresult res = GetCSSEquivalentToHTMLInlineStyleSet(node, aHTMLProperty, aHTMLAttribute,
1091 valueString, aStyleType);
1092 NS_ENSURE_SUCCESS(res, res);
1093
1094 // early way out if we can
1095 if (valueString.IsEmpty()) {
1096 return NS_OK;
1097 }
1098
1099 if (nsEditProperty::b == aHTMLProperty) {
1100 if (valueString.EqualsLiteral("bold")) {
1101 aIsSet = true;
1102 } else if (valueString.EqualsLiteral("normal")) {
1103 aIsSet = false;
1104 } else if (valueString.EqualsLiteral("bolder")) {
1105 aIsSet = true;
1106 valueString.AssignLiteral("bold");
1107 } else {
1108 int32_t weight = 0;
1109 nsresult errorCode;
1110 nsAutoString value(valueString);
1111 weight = value.ToInteger(&errorCode);
1112 if (400 < weight) {
1113 aIsSet = true;
1114 valueString.AssignLiteral("bold");
1115 } else {
1116 aIsSet = false;
1117 valueString.AssignLiteral("normal");
1118 }
1119 }
1120 } else if (nsEditProperty::i == aHTMLProperty) {
1121 if (valueString.EqualsLiteral("italic") ||
1122 valueString.EqualsLiteral("oblique")) {
1123 aIsSet = true;
1124 }
1125 } else if (nsEditProperty::u == aHTMLProperty) {
1126 nsAutoString val;
1127 val.AssignLiteral("underline");
1128 aIsSet = bool(ChangeCSSInlineStyleTxn::ValueIncludes(valueString, val, false));
1129 } else if (nsEditProperty::strike == aHTMLProperty) {
1130 nsAutoString val;
1131 val.AssignLiteral("line-through");
1132 aIsSet = bool(ChangeCSSInlineStyleTxn::ValueIncludes(valueString, val, false));
1133 } else if (aHTMLAttribute &&
1134 ((nsEditProperty::font == aHTMLProperty &&
1135 aHTMLAttribute->EqualsLiteral("color")) ||
1136 aHTMLAttribute->EqualsLiteral("bgcolor"))) {
1137 if (htmlValueString.IsEmpty()) {
1138 aIsSet = true;
1139 } else {
1140 nscolor rgba;
1141 nsAutoString subStr;
1142 htmlValueString.Right(subStr, htmlValueString.Length() - 1);
1143 if (NS_ColorNameToRGB(htmlValueString, &rgba) ||
1144 NS_HexToRGB(subStr, &rgba)) {
1145 nsAutoString htmlColor, tmpStr;
1146
1147 if (NS_GET_A(rgba) != 255) {
1148 // This should only be hit by the "transparent" keyword, which
1149 // currently serializes to "transparent" (not "rgba(0, 0, 0, 0)").
1150 MOZ_ASSERT(NS_GET_R(rgba) == 0 && NS_GET_G(rgba) == 0 &&
1151 NS_GET_B(rgba) == 0 && NS_GET_A(rgba) == 0);
1152 htmlColor.AppendLiteral("transparent");
1153 } else {
1154 htmlColor.AppendLiteral("rgb(");
1155
1156 NS_NAMED_LITERAL_STRING(comma, ", ");
1157
1158 tmpStr.AppendInt(NS_GET_R(rgba), 10);
1159 htmlColor.Append(tmpStr + comma);
1160
1161 tmpStr.Truncate();
1162 tmpStr.AppendInt(NS_GET_G(rgba), 10);
1163 htmlColor.Append(tmpStr + comma);
1164
1165 tmpStr.Truncate();
1166 tmpStr.AppendInt(NS_GET_B(rgba), 10);
1167 htmlColor.Append(tmpStr);
1168
1169 htmlColor.Append(char16_t(')'));
1170 }
1171
1172 aIsSet = htmlColor.Equals(valueString,
1173 nsCaseInsensitiveStringComparator());
1174 } else {
1175 aIsSet = htmlValueString.Equals(valueString,
1176 nsCaseInsensitiveStringComparator());
1177 }
1178 }
1179 } else if (nsEditProperty::tt == aHTMLProperty) {
1180 aIsSet = StringBeginsWith(valueString, NS_LITERAL_STRING("monospace"));
1181 } else if (nsEditProperty::font == aHTMLProperty && aHTMLAttribute &&
1182 aHTMLAttribute->EqualsLiteral("face")) {
1183 if (!htmlValueString.IsEmpty()) {
1184 const char16_t commaSpace[] = { char16_t(','), char16_t(' '), 0 };
1185 const char16_t comma[] = { char16_t(','), 0 };
1186 htmlValueString.ReplaceSubstring(commaSpace, comma);
1187 nsAutoString valueStringNorm(valueString);
1188 valueStringNorm.ReplaceSubstring(commaSpace, comma);
1189 aIsSet = htmlValueString.Equals(valueStringNorm,
1190 nsCaseInsensitiveStringComparator());
1191 } else {
1192 // ignore this, it's TT or our default
1193 nsAutoString valueStringLower;
1194 ToLowerCase(valueString, valueStringLower);
1195 aIsSet = !valueStringLower.EqualsLiteral("monospace") &&
1196 !valueStringLower.EqualsLiteral("serif");
1197 }
1198 return NS_OK;
1199 } else if (aHTMLAttribute && aHTMLAttribute->EqualsLiteral("align")) {
1200 aIsSet = true;
1201 } else {
1202 aIsSet = false;
1203 return NS_OK;
1204 }
1205
1206 if (!htmlValueString.IsEmpty() &&
1207 htmlValueString.Equals(valueString,
1208 nsCaseInsensitiveStringComparator())) {
1209 aIsSet = true;
1210 }
1211
1212 if (htmlValueString.EqualsLiteral("-moz-editor-invert-value")) {
1213 aIsSet = !aIsSet;
1214 }
1215
1216 if (nsEditProperty::u == aHTMLProperty || nsEditProperty::strike == aHTMLProperty) {
1217 // unfortunately, the value of the text-decoration property is not inherited.
1218 // that means that we have to look at ancestors of node to see if they are underlined
1219 node = node->GetParentElement(); // set to null if it's not a dom element
1220 }
1221 } while ((nsEditProperty::u == aHTMLProperty || nsEditProperty::strike == aHTMLProperty) &&
1222 !aIsSet && node);
1223 return NS_OK;
1224 }
1225
1226 void
1227 nsHTMLCSSUtils::SetCSSEnabled(bool aIsCSSPrefChecked)
1228 {
1229 mIsCSSPrefChecked = aIsCSSPrefChecked;
1230 }
1231
1232 bool
1233 nsHTMLCSSUtils::IsCSSPrefChecked()
1234 {
1235 return mIsCSSPrefChecked ;
1236 }
1237
1238 // ElementsSameStyle compares two elements and checks if they have the same
1239 // specified CSS declarations in the STYLE attribute
1240 // The answer is always negative if at least one of them carries an ID or a class
1241 bool
1242 nsHTMLCSSUtils::ElementsSameStyle(nsIDOMNode *aFirstNode, nsIDOMNode *aSecondNode)
1243 {
1244 nsCOMPtr<dom::Element> firstElement = do_QueryInterface(aFirstNode);
1245 nsCOMPtr<dom::Element> secondElement = do_QueryInterface(aSecondNode);
1246
1247 NS_ASSERTION((firstElement && secondElement), "Non element nodes passed to ElementsSameStyle.");
1248 NS_ENSURE_TRUE(firstElement, false);
1249 NS_ENSURE_TRUE(secondElement, false);
1250
1251 return ElementsSameStyle(firstElement, secondElement);
1252 }
1253
1254 bool
1255 nsHTMLCSSUtils::ElementsSameStyle(dom::Element* aFirstElement,
1256 dom::Element* aSecondElement)
1257 {
1258 MOZ_ASSERT(aFirstElement);
1259 MOZ_ASSERT(aSecondElement);
1260
1261 if (aFirstElement->HasAttr(kNameSpaceID_None, nsGkAtoms::id) ||
1262 aSecondElement->HasAttr(kNameSpaceID_None, nsGkAtoms::id)) {
1263 // at least one of the spans carries an ID ; suspect a CSS rule applies to it and
1264 // refuse to merge the nodes
1265 return false;
1266 }
1267
1268 nsAutoString firstClass, secondClass;
1269 bool isFirstClassSet = aFirstElement->GetAttr(kNameSpaceID_None, nsGkAtoms::_class, firstClass);
1270 bool isSecondClassSet = aSecondElement->GetAttr(kNameSpaceID_None, nsGkAtoms::_class, secondClass);
1271 if (isFirstClassSet && isSecondClassSet) {
1272 // both spans carry a class, let's compare them
1273 if (!firstClass.Equals(secondClass)) {
1274 // WARNING : technically, the comparison just above is questionable :
1275 // from a pure HTML/CSS point of view class="a b" is NOT the same than
1276 // class="b a" because a CSS rule could test the exact value of the class
1277 // attribute to be "a b" for instance ; from a user's point of view, a
1278 // wysiwyg editor should probably NOT make any difference. CSS people
1279 // need to discuss this issue before any modification.
1280 return false;
1281 }
1282 } else if (isFirstClassSet || isSecondClassSet) {
1283 // one span only carries a class, early way out
1284 return false;
1285 }
1286
1287 nsCOMPtr<nsIDOMCSSStyleDeclaration> firstCSSDecl, secondCSSDecl;
1288 uint32_t firstLength, secondLength;
1289 nsresult rv = GetInlineStyles(aFirstElement, getter_AddRefs(firstCSSDecl), &firstLength);
1290 if (NS_FAILED(rv) || !firstCSSDecl) {
1291 return false;
1292 }
1293 rv = GetInlineStyles(aSecondElement, getter_AddRefs(secondCSSDecl), &secondLength);
1294 if (NS_FAILED(rv) || !secondCSSDecl) {
1295 return false;
1296 }
1297
1298 if (firstLength != secondLength) {
1299 // early way out if we can
1300 return false;
1301 }
1302
1303 if (!firstLength) {
1304 // no inline style !
1305 return true;
1306 }
1307
1308 nsAutoString propertyNameString;
1309 nsAutoString firstValue, secondValue;
1310 for (uint32_t i = 0; i < firstLength; i++) {
1311 firstCSSDecl->Item(i, propertyNameString);
1312 firstCSSDecl->GetPropertyValue(propertyNameString, firstValue);
1313 secondCSSDecl->GetPropertyValue(propertyNameString, secondValue);
1314 if (!firstValue.Equals(secondValue)) {
1315 return false;
1316 }
1317 }
1318 for (uint32_t i = 0; i < secondLength; i++) {
1319 secondCSSDecl->Item(i, propertyNameString);
1320 secondCSSDecl->GetPropertyValue(propertyNameString, secondValue);
1321 firstCSSDecl->GetPropertyValue(propertyNameString, firstValue);
1322 if (!firstValue.Equals(secondValue)) {
1323 return false;
1324 }
1325 }
1326
1327 return true;
1328 }
1329
1330 nsresult
1331 nsHTMLCSSUtils::GetInlineStyles(dom::Element* aElement,
1332 nsIDOMCSSStyleDeclaration** aCssDecl,
1333 uint32_t* aLength)
1334 {
1335 return GetInlineStyles(static_cast<nsISupports*>(aElement), aCssDecl, aLength);
1336 }
1337
1338 nsresult
1339 nsHTMLCSSUtils::GetInlineStyles(nsIDOMElement* aElement,
1340 nsIDOMCSSStyleDeclaration** aCssDecl,
1341 uint32_t* aLength)
1342 {
1343 return GetInlineStyles(static_cast<nsISupports*>(aElement), aCssDecl, aLength);
1344 }
1345
1346 nsresult
1347 nsHTMLCSSUtils::GetInlineStyles(nsISupports *aElement,
1348 nsIDOMCSSStyleDeclaration **aCssDecl,
1349 uint32_t *aLength)
1350 {
1351 NS_ENSURE_TRUE(aElement && aLength, NS_ERROR_NULL_POINTER);
1352 *aLength = 0;
1353 nsCOMPtr<nsIDOMElementCSSInlineStyle> inlineStyles = do_QueryInterface(aElement);
1354 NS_ENSURE_TRUE(inlineStyles, NS_ERROR_NULL_POINTER);
1355
1356 nsresult res = inlineStyles->GetStyle(aCssDecl);
1357 NS_ENSURE_SUCCESS(res, NS_ERROR_NULL_POINTER);
1358 MOZ_ASSERT(*aCssDecl);
1359
1360 (*aCssDecl)->GetLength(aLength);
1361 return NS_OK;
1362 }
1363
1364 already_AddRefed<nsIDOMElement>
1365 nsHTMLCSSUtils::GetElementContainerOrSelf(nsIDOMNode* aNode)
1366 {
1367 nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
1368 NS_ENSURE_TRUE(node, nullptr);
1369 nsCOMPtr<nsIDOMElement> element =
1370 do_QueryInterface(GetElementContainerOrSelf(node));
1371 return element.forget();
1372 }
1373
1374 dom::Element*
1375 nsHTMLCSSUtils::GetElementContainerOrSelf(nsINode* aNode)
1376 {
1377 MOZ_ASSERT(aNode);
1378 if (nsIDOMNode::DOCUMENT_NODE == aNode->NodeType()) {
1379 return nullptr;
1380 }
1381
1382 nsINode* node = aNode;
1383 // Loop until we find an element.
1384 while (node && !node->IsElement()) {
1385 node = node->GetParentNode();
1386 }
1387
1388 NS_ENSURE_TRUE(node, nullptr);
1389 return node->AsElement();
1390 }
1391
1392 nsresult
1393 nsHTMLCSSUtils::SetCSSProperty(nsIDOMElement * aElement,
1394 const nsAString & aProperty,
1395 const nsAString & aValue)
1396 {
1397 nsCOMPtr<nsIDOMCSSStyleDeclaration> cssDecl;
1398 uint32_t length;
1399 nsresult res = GetInlineStyles(aElement, getter_AddRefs(cssDecl), &length);
1400 if (NS_FAILED(res) || !cssDecl) return res;
1401
1402 return cssDecl->SetProperty(aProperty,
1403 aValue,
1404 EmptyString());
1405 }
1406
1407 nsresult
1408 nsHTMLCSSUtils::SetCSSPropertyPixels(nsIDOMElement * aElement,
1409 const nsAString & aProperty,
1410 int32_t aIntValue)
1411 {
1412 nsAutoString s;
1413 s.AppendInt(aIntValue);
1414 return SetCSSProperty(aElement, aProperty, s + NS_LITERAL_STRING("px"));
1415 }

mercurial