Coverage Report

Created: 2018-09-25 14:53

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