Line data Source code
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/HTMLEditor.h"
7 :
8 : #include "HTMLEditUtils.h"
9 : #include "TextEditUtils.h"
10 : #include "TypeInState.h"
11 : #include "mozilla/Assertions.h"
12 : #include "mozilla/EditorUtils.h"
13 : #include "mozilla/SelectionState.h"
14 : #include "mozilla/dom/Selection.h"
15 : #include "mozilla/dom/Element.h"
16 : #include "mozilla/mozalloc.h"
17 : #include "nsAString.h"
18 : #include "nsAttrName.h"
19 : #include "nsCOMPtr.h"
20 : #include "nsCaseTreatment.h"
21 : #include "nsComponentManagerUtils.h"
22 : #include "nsDebug.h"
23 : #include "nsError.h"
24 : #include "nsGkAtoms.h"
25 : #include "nsIAtom.h"
26 : #include "nsIContent.h"
27 : #include "nsIContentIterator.h"
28 : #include "nsIDOMElement.h"
29 : #include "nsIEditRules.h"
30 : #include "nsNameSpaceManager.h"
31 : #include "nsINode.h"
32 : #include "nsISupportsImpl.h"
33 : #include "nsLiteralString.h"
34 : #include "nsRange.h"
35 : #include "nsReadableUtils.h"
36 : #include "nsString.h"
37 : #include "nsStringFwd.h"
38 : #include "nsTArray.h"
39 : #include "nsUnicharUtils.h"
40 : #include "nscore.h"
41 :
42 : class nsISupports;
43 :
44 : namespace mozilla {
45 :
46 : using namespace dom;
47 :
48 : static bool
49 0 : IsEmptyTextNode(HTMLEditor* aThis, nsINode* aNode)
50 : {
51 0 : bool isEmptyTextNode = false;
52 0 : return EditorBase::IsTextNode(aNode) &&
53 0 : NS_SUCCEEDED(aThis->IsEmptyNode(aNode, &isEmptyTextNode)) &&
54 0 : isEmptyTextNode;
55 : }
56 :
57 : NS_IMETHODIMP
58 0 : HTMLEditor::AddDefaultProperty(nsIAtom* aProperty,
59 : const nsAString& aAttribute,
60 : const nsAString& aValue)
61 : {
62 0 : nsString outValue;
63 : int32_t index;
64 0 : nsString attr(aAttribute);
65 0 : if (TypeInState::FindPropInList(aProperty, attr, &outValue,
66 : mDefaultStyles, index)) {
67 0 : PropItem *item = mDefaultStyles[index];
68 0 : item->value = aValue;
69 : } else {
70 0 : nsString value(aValue);
71 0 : PropItem *propItem = new PropItem(aProperty, attr, value);
72 0 : mDefaultStyles.AppendElement(propItem);
73 : }
74 0 : return NS_OK;
75 : }
76 :
77 : NS_IMETHODIMP
78 0 : HTMLEditor::RemoveDefaultProperty(nsIAtom* aProperty,
79 : const nsAString& aAttribute,
80 : const nsAString& aValue)
81 : {
82 0 : nsString outValue;
83 : int32_t index;
84 0 : nsString attr(aAttribute);
85 0 : if (TypeInState::FindPropInList(aProperty, attr, &outValue,
86 : mDefaultStyles, index)) {
87 0 : delete mDefaultStyles[index];
88 0 : mDefaultStyles.RemoveElementAt(index);
89 : }
90 0 : return NS_OK;
91 : }
92 :
93 : NS_IMETHODIMP
94 0 : HTMLEditor::RemoveAllDefaultProperties()
95 : {
96 0 : size_t defcon = mDefaultStyles.Length();
97 0 : for (size_t j = 0; j < defcon; j++) {
98 0 : delete mDefaultStyles[j];
99 : }
100 0 : mDefaultStyles.Clear();
101 0 : return NS_OK;
102 : }
103 :
104 :
105 : NS_IMETHODIMP
106 0 : HTMLEditor::SetInlineProperty(nsIAtom* aProperty,
107 : const nsAString& aAttribute,
108 : const nsAString& aValue)
109 : {
110 0 : NS_ENSURE_TRUE(aProperty, NS_ERROR_NULL_POINTER);
111 0 : NS_ENSURE_TRUE(mRules, NS_ERROR_NOT_INITIALIZED);
112 0 : nsCOMPtr<nsIEditRules> rules(mRules);
113 0 : ForceCompositionEnd();
114 :
115 0 : RefPtr<Selection> selection = GetSelection();
116 0 : NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
117 :
118 0 : if (selection->Collapsed()) {
119 : // Manipulating text attributes on a collapsed selection only sets state
120 : // for the next text insertion
121 0 : mTypeInState->SetProp(aProperty, aAttribute, aValue);
122 0 : return NS_OK;
123 : }
124 :
125 0 : AutoEditBatch batchIt(this);
126 : AutoRules beginRulesSniffing(this, EditAction::insertElement,
127 0 : nsIEditor::eNext);
128 0 : AutoSelectionRestorer selectionRestorer(selection, this);
129 0 : AutoTransactionsConserveSelection dontSpazMySelection(this);
130 :
131 : bool cancel, handled;
132 0 : TextRulesInfo ruleInfo(EditAction::setTextProperty);
133 : // Protect the edit rules object from dying
134 0 : nsresult rv = rules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
135 0 : NS_ENSURE_SUCCESS(rv, rv);
136 0 : if (!cancel && !handled) {
137 : // Loop through the ranges in the selection
138 0 : AutoRangeArray arrayOfRanges(selection);
139 0 : for (auto& range : arrayOfRanges.mRanges) {
140 : // Adjust range to include any ancestors whose children are entirely
141 : // selected
142 0 : rv = PromoteInlineRange(*range);
143 0 : NS_ENSURE_SUCCESS(rv, rv);
144 :
145 : // Check for easy case: both range endpoints in same text node
146 0 : nsCOMPtr<nsINode> startNode = range->GetStartContainer();
147 0 : nsCOMPtr<nsINode> endNode = range->GetEndContainer();
148 0 : if (startNode && startNode == endNode && startNode->GetAsText()) {
149 0 : rv = SetInlinePropertyOnTextNode(*startNode->GetAsText(),
150 : range->StartOffset(),
151 : range->EndOffset(),
152 0 : *aProperty, &aAttribute, aValue);
153 0 : NS_ENSURE_SUCCESS(rv, rv);
154 0 : continue;
155 : }
156 :
157 : // Not the easy case. Range not contained in single text node. There
158 : // are up to three phases here. There are all the nodes reported by the
159 : // subtree iterator to be processed. And there are potentially a
160 : // starting textnode and an ending textnode which are only partially
161 : // contained by the range.
162 :
163 : // Let's handle the nodes reported by the iterator. These nodes are
164 : // entirely contained in the selection range. We build up a list of them
165 : // (since doing operations on the document during iteration would perturb
166 : // the iterator).
167 :
168 0 : OwningNonNull<nsIContentIterator> iter = NS_NewContentSubtreeIterator();
169 :
170 0 : nsTArray<OwningNonNull<nsIContent>> arrayOfNodes;
171 :
172 : // Iterate range and build up array
173 0 : rv = iter->Init(range);
174 : // Init returns an error if there are no nodes in range. This can easily
175 : // happen with the subtree iterator if the selection doesn't contain any
176 : // *whole* nodes.
177 0 : if (NS_SUCCEEDED(rv)) {
178 0 : for (; !iter->IsDone(); iter->Next()) {
179 0 : OwningNonNull<nsINode> node = *iter->GetCurrentNode();
180 :
181 0 : if (node->IsContent() && IsEditable(node)) {
182 0 : arrayOfNodes.AppendElement(*node->AsContent());
183 : }
184 : }
185 : }
186 : // First check the start parent of the range to see if it needs to be
187 : // separately handled (it does if it's a text node, due to how the
188 : // subtree iterator works - it will not have reported it).
189 0 : if (startNode && startNode->GetAsText() && IsEditable(startNode)) {
190 0 : rv = SetInlinePropertyOnTextNode(*startNode->GetAsText(),
191 : range->StartOffset(),
192 0 : startNode->Length(), *aProperty,
193 0 : &aAttribute, aValue);
194 0 : NS_ENSURE_SUCCESS(rv, rv);
195 : }
196 :
197 : // Then loop through the list, set the property on each node
198 0 : for (auto& node : arrayOfNodes) {
199 0 : rv = SetInlinePropertyOnNode(*node, *aProperty, &aAttribute, aValue);
200 0 : NS_ENSURE_SUCCESS(rv, rv);
201 : }
202 :
203 : // Last check the end parent of the range to see if it needs to be
204 : // separately handled (it does if it's a text node, due to how the
205 : // subtree iterator works - it will not have reported it).
206 0 : if (endNode && endNode->GetAsText() && IsEditable(endNode)) {
207 0 : rv = SetInlinePropertyOnTextNode(*endNode->GetAsText(), 0,
208 : range->EndOffset(), *aProperty,
209 0 : &aAttribute, aValue);
210 0 : NS_ENSURE_SUCCESS(rv, rv);
211 : }
212 : }
213 : }
214 0 : if (!cancel) {
215 : // Post-process
216 0 : return rules->DidDoAction(selection, &ruleInfo, rv);
217 : }
218 0 : return NS_OK;
219 : }
220 :
221 : // Helper function for SetInlinePropertyOn*: is aNode a simple old <b>, <font>,
222 : // <span style="">, etc. that we can reuse instead of creating a new one?
223 : bool
224 0 : HTMLEditor::IsSimpleModifiableNode(nsIContent* aContent,
225 : nsIAtom* aProperty,
226 : const nsAString* aAttribute,
227 : const nsAString* aValue)
228 : {
229 : // aContent can be null, in which case we'll return false in a few lines
230 0 : MOZ_ASSERT(aProperty);
231 0 : MOZ_ASSERT_IF(aAttribute, aValue);
232 :
233 0 : nsCOMPtr<dom::Element> element = do_QueryInterface(aContent);
234 0 : if (!element) {
235 0 : return false;
236 : }
237 :
238 : // First check for <b>, <i>, etc.
239 0 : if (element->IsHTMLElement(aProperty) && !element->GetAttrCount() &&
240 0 : (!aAttribute || aAttribute->IsEmpty())) {
241 0 : return true;
242 : }
243 :
244 : // Special cases for various equivalencies: <strong>, <em>, <s>
245 0 : if (!element->GetAttrCount() &&
246 0 : ((aProperty == nsGkAtoms::b &&
247 0 : element->IsHTMLElement(nsGkAtoms::strong)) ||
248 0 : (aProperty == nsGkAtoms::i &&
249 0 : element->IsHTMLElement(nsGkAtoms::em)) ||
250 0 : (aProperty == nsGkAtoms::strike &&
251 0 : element->IsHTMLElement(nsGkAtoms::s)))) {
252 0 : return true;
253 : }
254 :
255 : // Now look for things like <font>
256 0 : if (aAttribute && !aAttribute->IsEmpty()) {
257 0 : nsCOMPtr<nsIAtom> atom = NS_Atomize(*aAttribute);
258 0 : MOZ_ASSERT(atom);
259 :
260 0 : nsString attrValue;
261 0 : if (element->IsHTMLElement(aProperty) &&
262 0 : IsOnlyAttribute(element, *aAttribute) &&
263 0 : element->GetAttr(kNameSpaceID_None, atom, attrValue) &&
264 0 : attrValue.Equals(*aValue, nsCaseInsensitiveStringComparator())) {
265 : // This is not quite correct, because it excludes cases like
266 : // <font face=000> being the same as <font face=#000000>.
267 : // Property-specific handling is needed (bug 760211).
268 0 : return true;
269 : }
270 : }
271 :
272 : // No luck so far. Now we check for a <span> with a single style=""
273 : // attribute that sets only the style we're looking for, if this type of
274 : // style supports it
275 0 : if (!mCSSEditUtils->IsCSSEditableProperty(element, aProperty, aAttribute) ||
276 0 : !element->IsHTMLElement(nsGkAtoms::span) ||
277 0 : element->GetAttrCount() != 1 ||
278 0 : !element->HasAttr(kNameSpaceID_None, nsGkAtoms::style)) {
279 0 : return false;
280 : }
281 :
282 : // Some CSS styles are not so simple. For instance, underline is
283 : // "text-decoration: underline", which decomposes into four different text-*
284 : // properties. So for now, we just create a span, add the desired style, and
285 : // see if it matches.
286 0 : nsCOMPtr<Element> newSpan = CreateHTMLContent(nsGkAtoms::span);
287 0 : NS_ASSERTION(newSpan, "CreateHTMLContent failed");
288 0 : NS_ENSURE_TRUE(newSpan, false);
289 0 : mCSSEditUtils->SetCSSEquivalentToHTMLStyle(newSpan, aProperty,
290 : aAttribute, aValue,
291 0 : /*suppress transaction*/ true);
292 :
293 0 : return mCSSEditUtils->ElementsSameStyle(newSpan, element);
294 : }
295 :
296 : nsresult
297 0 : HTMLEditor::SetInlinePropertyOnTextNode(Text& aText,
298 : int32_t aStartOffset,
299 : int32_t aEndOffset,
300 : nsIAtom& aProperty,
301 : const nsAString* aAttribute,
302 : const nsAString& aValue)
303 : {
304 0 : if (!aText.GetParentNode() ||
305 0 : !CanContainTag(*aText.GetParentNode(), aProperty)) {
306 0 : return NS_OK;
307 : }
308 :
309 : // Don't need to do anything if no characters actually selected
310 0 : if (aStartOffset == aEndOffset) {
311 0 : return NS_OK;
312 : }
313 :
314 : // Don't need to do anything if property already set on node
315 0 : if (mCSSEditUtils->IsCSSEditableProperty(&aText, &aProperty, aAttribute)) {
316 : // The HTML styles defined by aProperty/aAttribute have a CSS equivalence
317 : // for node; let's check if it carries those CSS styles
318 0 : if (mCSSEditUtils->IsCSSEquivalentToHTMLInlineStyleSet(&aText, &aProperty,
319 : aAttribute, aValue, CSSEditUtils::eComputed)) {
320 0 : return NS_OK;
321 : }
322 0 : } else if (IsTextPropertySetByContent(&aText, &aProperty, aAttribute,
323 : &aValue)) {
324 0 : return NS_OK;
325 : }
326 :
327 : // Do we need to split the text node?
328 0 : ErrorResult rv;
329 0 : RefPtr<Text> text = &aText;
330 0 : if (uint32_t(aEndOffset) != aText.Length()) {
331 : // We need to split off back of text node
332 0 : text = SplitNode(aText, aEndOffset, rv)->GetAsText();
333 0 : NS_ENSURE_TRUE(!rv.Failed(), rv.StealNSResult());
334 : }
335 :
336 0 : if (aStartOffset) {
337 : // We need to split off front of text node
338 0 : SplitNode(*text, aStartOffset, rv);
339 0 : NS_ENSURE_TRUE(!rv.Failed(), rv.StealNSResult());
340 : }
341 :
342 0 : if (aAttribute) {
343 : // Look for siblings that are correct type of node
344 0 : nsIContent* sibling = GetPriorHTMLSibling(text);
345 0 : if (IsSimpleModifiableNode(sibling, &aProperty, aAttribute, &aValue)) {
346 : // Previous sib is already right kind of inline node; slide this over
347 0 : return MoveNode(text, sibling, -1);
348 : }
349 0 : sibling = GetNextHTMLSibling(text);
350 0 : if (IsSimpleModifiableNode(sibling, &aProperty, aAttribute, &aValue)) {
351 : // Following sib is already right kind of inline node; slide this over
352 0 : return MoveNode(text, sibling, 0);
353 : }
354 : }
355 :
356 : // Reparent the node inside inline node with appropriate {attribute,value}
357 0 : return SetInlinePropertyOnNode(*text, aProperty, aAttribute, aValue);
358 : }
359 :
360 : nsresult
361 0 : HTMLEditor::SetInlinePropertyOnNodeImpl(nsIContent& aNode,
362 : nsIAtom& aProperty,
363 : const nsAString* aAttribute,
364 : const nsAString& aValue)
365 : {
366 0 : nsCOMPtr<nsIAtom> attrAtom = aAttribute ? NS_Atomize(*aAttribute) : nullptr;
367 :
368 : // If this is an element that can't be contained in a span, we have to
369 : // recurse to its children.
370 0 : if (!TagCanContain(*nsGkAtoms::span, aNode)) {
371 0 : if (aNode.HasChildren()) {
372 0 : nsTArray<OwningNonNull<nsIContent>> arrayOfNodes;
373 :
374 : // Populate the list.
375 0 : for (nsCOMPtr<nsIContent> child = aNode.GetFirstChild();
376 : child;
377 0 : child = child->GetNextSibling()) {
378 0 : if (IsEditable(child) && !IsEmptyTextNode(this, child)) {
379 0 : arrayOfNodes.AppendElement(*child);
380 : }
381 : }
382 :
383 : // Then loop through the list, set the property on each node.
384 0 : for (auto& node : arrayOfNodes) {
385 0 : nsresult rv = SetInlinePropertyOnNode(node, aProperty, aAttribute,
386 0 : aValue);
387 0 : NS_ENSURE_SUCCESS(rv, rv);
388 : }
389 : }
390 0 : return NS_OK;
391 : }
392 :
393 : // First check if there's an adjacent sibling we can put our node into.
394 0 : nsCOMPtr<nsIContent> previousSibling = GetPriorHTMLSibling(&aNode);
395 0 : nsCOMPtr<nsIContent> nextSibling = GetNextHTMLSibling(&aNode);
396 0 : if (IsSimpleModifiableNode(previousSibling, &aProperty, aAttribute, &aValue)) {
397 0 : nsresult rv = MoveNode(&aNode, previousSibling, -1);
398 0 : NS_ENSURE_SUCCESS(rv, rv);
399 0 : if (IsSimpleModifiableNode(nextSibling, &aProperty, aAttribute, &aValue)) {
400 0 : rv = JoinNodes(*previousSibling, *nextSibling);
401 0 : NS_ENSURE_SUCCESS(rv, rv);
402 : }
403 0 : return NS_OK;
404 : }
405 0 : if (IsSimpleModifiableNode(nextSibling, &aProperty, aAttribute, &aValue)) {
406 0 : nsresult rv = MoveNode(&aNode, nextSibling, 0);
407 0 : NS_ENSURE_SUCCESS(rv, rv);
408 0 : return NS_OK;
409 : }
410 :
411 : // Don't need to do anything if property already set on node
412 0 : if (mCSSEditUtils->IsCSSEditableProperty(&aNode, &aProperty, aAttribute)) {
413 0 : if (mCSSEditUtils->IsCSSEquivalentToHTMLInlineStyleSet(
414 : &aNode, &aProperty, aAttribute, aValue, CSSEditUtils::eComputed)) {
415 0 : return NS_OK;
416 : }
417 0 : } else if (IsTextPropertySetByContent(&aNode, &aProperty,
418 : aAttribute, &aValue)) {
419 0 : return NS_OK;
420 : }
421 :
422 0 : bool useCSS = (IsCSSEnabled() &&
423 0 : mCSSEditUtils->IsCSSEditableProperty(&aNode, &aProperty,
424 0 : aAttribute)) ||
425 : // bgcolor is always done using CSS
426 0 : attrAtom == nsGkAtoms::bgcolor;
427 :
428 0 : if (useCSS) {
429 0 : nsCOMPtr<dom::Element> tmp;
430 : // We only add style="" to <span>s with no attributes (bug 746515). If we
431 : // don't have one, we need to make one.
432 0 : if (aNode.IsHTMLElement(nsGkAtoms::span) &&
433 0 : !aNode.AsElement()->GetAttrCount()) {
434 0 : tmp = aNode.AsElement();
435 : } else {
436 0 : tmp = InsertContainerAbove(&aNode, nsGkAtoms::span);
437 0 : NS_ENSURE_STATE(tmp);
438 : }
439 :
440 : // Add the CSS styles corresponding to the HTML style request
441 0 : mCSSEditUtils->SetCSSEquivalentToHTMLStyle(tmp,
442 : &aProperty, attrAtom,
443 0 : &aValue, false);
444 0 : return NS_OK;
445 : }
446 :
447 : // is it already the right kind of node, but with wrong attribute?
448 0 : if (aNode.IsHTMLElement(&aProperty)) {
449 : // Just set the attribute on it.
450 0 : nsCOMPtr<nsIDOMElement> elem = do_QueryInterface(&aNode);
451 0 : return SetAttribute(elem, *aAttribute, aValue);
452 : }
453 :
454 : // ok, chuck it in its very own container
455 0 : nsCOMPtr<Element> tmp = InsertContainerAbove(&aNode, &aProperty, attrAtom,
456 0 : &aValue);
457 0 : NS_ENSURE_STATE(tmp);
458 :
459 0 : return NS_OK;
460 : }
461 :
462 : nsresult
463 0 : HTMLEditor::SetInlinePropertyOnNode(nsIContent& aNode,
464 : nsIAtom& aProperty,
465 : const nsAString* aAttribute,
466 : const nsAString& aValue)
467 : {
468 0 : nsCOMPtr<nsIContent> previousSibling = aNode.GetPreviousSibling(),
469 0 : nextSibling = aNode.GetNextSibling();
470 0 : NS_ENSURE_STATE(aNode.GetParentNode());
471 0 : OwningNonNull<nsINode> parent = *aNode.GetParentNode();
472 :
473 0 : nsresult rv = RemoveStyleInside(aNode, &aProperty, aAttribute);
474 0 : NS_ENSURE_SUCCESS(rv, rv);
475 :
476 0 : if (aNode.GetParentNode()) {
477 : // The node is still where it was
478 : return SetInlinePropertyOnNodeImpl(aNode, aProperty,
479 0 : aAttribute, aValue);
480 : }
481 :
482 : // It's vanished. Use the old siblings for reference to construct a
483 : // list. But first, verify that the previous/next siblings are still
484 : // where we expect them; otherwise we have to give up.
485 0 : if ((previousSibling && previousSibling->GetParentNode() != parent) ||
486 0 : (nextSibling && nextSibling->GetParentNode() != parent)) {
487 0 : return NS_ERROR_UNEXPECTED;
488 : }
489 0 : nsTArray<OwningNonNull<nsIContent>> nodesToSet;
490 : nsCOMPtr<nsIContent> cur = previousSibling
491 0 : ? previousSibling->GetNextSibling() : parent->GetFirstChild();
492 0 : for (; cur && cur != nextSibling; cur = cur->GetNextSibling()) {
493 0 : if (IsEditable(cur)) {
494 0 : nodesToSet.AppendElement(*cur);
495 : }
496 : }
497 :
498 0 : for (auto& node : nodesToSet) {
499 0 : rv = SetInlinePropertyOnNodeImpl(node, aProperty, aAttribute, aValue);
500 0 : NS_ENSURE_SUCCESS(rv, rv);
501 : }
502 :
503 0 : return NS_OK;
504 : }
505 :
506 : nsresult
507 0 : HTMLEditor::SplitStyleAboveRange(nsRange* inRange,
508 : nsIAtom* aProperty,
509 : const nsAString* aAttribute)
510 : {
511 0 : NS_ENSURE_TRUE(inRange, NS_ERROR_NULL_POINTER);
512 :
513 0 : nsCOMPtr<nsINode> startNode = inRange->GetStartContainer();
514 0 : int32_t startOffset = inRange->StartOffset();
515 0 : nsCOMPtr<nsINode> endNode = inRange->GetEndContainer();
516 0 : int32_t endOffset = inRange->EndOffset();
517 :
518 0 : nsCOMPtr<nsINode> origStartNode = startNode;
519 :
520 : // split any matching style nodes above the start of range
521 : {
522 0 : AutoTrackDOMPoint tracker(mRangeUpdater, address_of(endNode), &endOffset);
523 : nsresult rv =
524 0 : SplitStyleAbovePoint(address_of(startNode), &startOffset, aProperty,
525 0 : aAttribute);
526 0 : NS_ENSURE_SUCCESS(rv, rv);
527 : }
528 :
529 : // second verse, same as the first...
530 : nsresult rv =
531 0 : SplitStyleAbovePoint(address_of(endNode), &endOffset, aProperty,
532 0 : aAttribute);
533 0 : NS_ENSURE_SUCCESS(rv, rv);
534 :
535 : // reset the range
536 0 : rv = inRange->SetStartAndEnd(startNode, startOffset, endNode, endOffset);
537 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
538 0 : return rv;
539 : }
540 0 : return NS_OK;
541 : }
542 :
543 : nsresult
544 0 : HTMLEditor::SplitStyleAbovePoint(nsCOMPtr<nsINode>* aNode,
545 : int32_t* aOffset,
546 : // null here means we split all properties
547 : nsIAtom* aProperty,
548 : const nsAString* aAttribute,
549 : nsIContent** aOutLeftNode,
550 : nsIContent** aOutRightNode)
551 : {
552 0 : NS_ENSURE_TRUE(aNode && *aNode && aOffset, NS_ERROR_NULL_POINTER);
553 0 : NS_ENSURE_TRUE((*aNode)->IsContent(), NS_OK);
554 :
555 : // Split any matching style nodes above the node/offset
556 0 : OwningNonNull<nsIContent> node = *(*aNode)->AsContent();
557 :
558 0 : bool useCSS = IsCSSEnabled();
559 :
560 : bool isSet;
561 0 : while (!IsBlockNode(node) && node->GetParent() &&
562 0 : IsEditable(node->GetParent())) {
563 0 : isSet = false;
564 0 : if (useCSS && mCSSEditUtils->IsCSSEditableProperty(node, aProperty,
565 0 : aAttribute)) {
566 : // The HTML style defined by aProperty/aAttribute has a CSS equivalence
567 : // in this implementation for the node; let's check if it carries those
568 : // CSS styles
569 0 : nsAutoString firstValue;
570 0 : isSet = mCSSEditUtils->IsCSSEquivalentToHTMLInlineStyleSet(
571 : node, aProperty, aAttribute, firstValue,
572 0 : CSSEditUtils::eSpecified);
573 : }
574 0 : if (// node is the correct inline prop
575 0 : (aProperty && node->IsHTMLElement(aProperty)) ||
576 : // node is href - test if really <a href=...
577 0 : (aProperty == nsGkAtoms::href && HTMLEditUtils::IsLink(node)) ||
578 : // or node is any prop, and we asked to split them all
579 0 : (!aProperty && NodeIsProperty(node)) ||
580 : // or the style is specified in the style attribute
581 : isSet) {
582 : // Found a style node we need to split
583 0 : int32_t offset = SplitNodeDeep(*node, *(*aNode)->AsContent(), *aOffset,
584 : EmptyContainers::yes, aOutLeftNode,
585 0 : aOutRightNode);
586 0 : NS_ENSURE_TRUE(offset != -1, NS_ERROR_FAILURE);
587 : // reset startNode/startOffset
588 0 : *aNode = node->GetParent();
589 0 : *aOffset = offset;
590 : }
591 0 : node = node->GetParent();
592 : }
593 :
594 0 : return NS_OK;
595 : }
596 :
597 : nsresult
598 0 : HTMLEditor::ClearStyle(nsCOMPtr<nsINode>* aNode,
599 : int32_t* aOffset,
600 : nsIAtom* aProperty,
601 : const nsAString* aAttribute)
602 : {
603 0 : nsCOMPtr<nsIContent> leftNode, rightNode;
604 0 : nsresult rv = SplitStyleAbovePoint(aNode, aOffset, aProperty,
605 0 : aAttribute, getter_AddRefs(leftNode),
606 0 : getter_AddRefs(rightNode));
607 0 : NS_ENSURE_SUCCESS(rv, rv);
608 :
609 0 : if (leftNode) {
610 : bool bIsEmptyNode;
611 0 : IsEmptyNode(leftNode, &bIsEmptyNode, false, true);
612 0 : if (bIsEmptyNode) {
613 : // delete leftNode if it became empty
614 0 : rv = DeleteNode(leftNode);
615 0 : NS_ENSURE_SUCCESS(rv, rv);
616 : }
617 : }
618 0 : if (rightNode) {
619 0 : nsCOMPtr<nsINode> secondSplitParent = GetLeftmostChild(rightNode);
620 : // don't try to split non-containers (br's, images, hr's, etc.)
621 0 : if (!secondSplitParent) {
622 0 : secondSplitParent = rightNode;
623 : }
624 0 : nsCOMPtr<Element> savedBR;
625 0 : if (!IsContainer(secondSplitParent)) {
626 0 : if (TextEditUtils::IsBreak(secondSplitParent)) {
627 0 : savedBR = do_QueryInterface(secondSplitParent);
628 0 : NS_ENSURE_STATE(savedBR);
629 : }
630 :
631 0 : secondSplitParent = secondSplitParent->GetParentNode();
632 : }
633 0 : *aOffset = 0;
634 0 : rv = SplitStyleAbovePoint(address_of(secondSplitParent),
635 : aOffset, aProperty, aAttribute,
636 0 : getter_AddRefs(leftNode),
637 0 : getter_AddRefs(rightNode));
638 0 : NS_ENSURE_SUCCESS(rv, rv);
639 :
640 0 : if (rightNode) {
641 : bool bIsEmptyNode;
642 0 : IsEmptyNode(rightNode, &bIsEmptyNode, false, true);
643 0 : if (bIsEmptyNode) {
644 : // delete rightNode if it became empty
645 0 : rv = DeleteNode(rightNode);
646 0 : NS_ENSURE_SUCCESS(rv, rv);
647 : }
648 : }
649 :
650 0 : if (!leftNode) {
651 0 : return NS_OK;
652 : }
653 :
654 : // should be impossible to not get a new leftnode here
655 0 : nsCOMPtr<nsINode> newSelParent = GetLeftmostChild(leftNode);
656 0 : if (!newSelParent) {
657 0 : newSelParent = leftNode;
658 : }
659 : // If rightNode starts with a br, suck it out of right node and into
660 : // leftNode. This is so we you don't revert back to the previous style
661 : // if you happen to click at the end of a line.
662 0 : if (savedBR) {
663 0 : rv = MoveNode(savedBR, newSelParent, 0);
664 0 : NS_ENSURE_SUCCESS(rv, rv);
665 : }
666 : // remove the style on this new hierarchy
667 0 : int32_t newSelOffset = 0;
668 : {
669 : // Track the point at the new hierarchy. This is so we can know where
670 : // to put the selection after we call RemoveStyleInside().
671 : // RemoveStyleInside() could remove any and all of those nodes, so I
672 : // have to use the range tracking system to find the right spot to put
673 : // selection.
674 : AutoTrackDOMPoint tracker(mRangeUpdater,
675 0 : address_of(newSelParent), &newSelOffset);
676 0 : rv = RemoveStyleInside(*leftNode, aProperty, aAttribute);
677 0 : NS_ENSURE_SUCCESS(rv, rv);
678 : }
679 : // reset our node offset values to the resulting new sel point
680 0 : *aNode = newSelParent;
681 0 : *aOffset = newSelOffset;
682 : }
683 :
684 0 : return NS_OK;
685 : }
686 :
687 : bool
688 0 : HTMLEditor::NodeIsProperty(nsINode& aNode)
689 : {
690 0 : return IsContainer(&aNode) && IsEditable(&aNode) && !IsBlockNode(&aNode) &&
691 0 : !aNode.IsHTMLElement(nsGkAtoms::a);
692 : }
693 :
694 : nsresult
695 0 : HTMLEditor::ApplyDefaultProperties()
696 : {
697 0 : size_t defcon = mDefaultStyles.Length();
698 0 : for (size_t j = 0; j < defcon; j++) {
699 0 : PropItem *propItem = mDefaultStyles[j];
700 0 : NS_ENSURE_TRUE(propItem, NS_ERROR_NULL_POINTER);
701 : nsresult rv =
702 0 : SetInlineProperty(propItem->tag, propItem->attr, propItem->value);
703 0 : NS_ENSURE_SUCCESS(rv, rv);
704 : }
705 0 : return NS_OK;
706 : }
707 :
708 : nsresult
709 0 : HTMLEditor::RemoveStyleInside(nsIContent& aNode,
710 : nsIAtom* aProperty,
711 : const nsAString* aAttribute,
712 : const bool aChildrenOnly /* = false */)
713 : {
714 0 : if (aNode.GetAsText()) {
715 0 : return NS_OK;
716 : }
717 :
718 : // first process the children
719 0 : RefPtr<nsIContent> child = aNode.GetFirstChild();
720 0 : while (child) {
721 : // cache next sibling since we might remove child
722 0 : nsCOMPtr<nsIContent> next = child->GetNextSibling();
723 0 : nsresult rv = RemoveStyleInside(*child, aProperty, aAttribute);
724 0 : NS_ENSURE_SUCCESS(rv, rv);
725 0 : child = next.forget();
726 : }
727 :
728 : // then process the node itself
729 0 : if (!aChildrenOnly &&
730 : // node is prop we asked for
731 0 : ((aProperty && aNode.NodeInfo()->NameAtom() == aProperty) ||
732 : // but check for link (<a href=...)
733 0 : (aProperty == nsGkAtoms::href && HTMLEditUtils::IsLink(&aNode)) ||
734 : // and for named anchors
735 0 : (aProperty == nsGkAtoms::name && HTMLEditUtils::IsNamedAnchor(&aNode)) ||
736 : // or node is any prop and we asked for that
737 0 : (!aProperty && NodeIsProperty(aNode)))) {
738 : // if we weren't passed an attribute, then we want to
739 : // remove any matching inlinestyles entirely
740 0 : if (!aAttribute || aAttribute->IsEmpty()) {
741 0 : bool hasStyleAttr = aNode.HasAttr(kNameSpaceID_None, nsGkAtoms::style);
742 0 : bool hasClassAttr = aNode.HasAttr(kNameSpaceID_None, nsGkAtoms::_class);
743 0 : if (aProperty && (hasStyleAttr || hasClassAttr)) {
744 : // aNode carries inline styles or a class attribute so we can't
745 : // just remove the element... We need to create above the element
746 : // a span that will carry those styles or class, then we can delete
747 : // the node.
748 : RefPtr<Element> spanNode =
749 0 : InsertContainerAbove(&aNode, nsGkAtoms::span);
750 0 : NS_ENSURE_STATE(spanNode);
751 : nsresult rv =
752 0 : CloneAttribute(nsGkAtoms::style, spanNode, aNode.AsElement());
753 0 : NS_ENSURE_SUCCESS(rv, rv);
754 : rv =
755 0 : CloneAttribute(nsGkAtoms::_class, spanNode, aNode.AsElement());
756 0 : NS_ENSURE_SUCCESS(rv, rv);
757 : }
758 0 : nsresult rv = RemoveContainer(&aNode);
759 0 : NS_ENSURE_SUCCESS(rv, rv);
760 : } else {
761 : // otherwise we just want to eliminate the attribute
762 0 : nsCOMPtr<nsIAtom> attribute = NS_Atomize(*aAttribute);
763 0 : if (aNode.HasAttr(kNameSpaceID_None, attribute)) {
764 : // if this matching attribute is the ONLY one on the node,
765 : // then remove the whole node. Otherwise just nix the attribute.
766 0 : if (IsOnlyAttribute(&aNode, *aAttribute)) {
767 0 : nsresult rv = RemoveContainer(&aNode);
768 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
769 0 : return rv;
770 : }
771 : } else {
772 0 : nsCOMPtr<nsIDOMElement> elem = do_QueryInterface(&aNode);
773 0 : NS_ENSURE_TRUE(elem, NS_ERROR_NULL_POINTER);
774 0 : nsresult rv = RemoveAttribute(elem, *aAttribute);
775 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
776 0 : return rv;
777 : }
778 : }
779 : }
780 : }
781 : }
782 :
783 0 : if (!aChildrenOnly &&
784 0 : mCSSEditUtils->IsCSSEditableProperty(&aNode, aProperty, aAttribute)) {
785 : // the HTML style defined by aProperty/aAttribute has a CSS equivalence in
786 : // this implementation for the node aNode; let's check if it carries those
787 : // css styles
788 0 : if (aNode.IsElement()) {
789 : nsCOMPtr<nsIAtom> attribute =
790 0 : aAttribute ? NS_Atomize(*aAttribute) : nullptr;
791 : bool hasAttribute =
792 0 : mCSSEditUtils->HaveCSSEquivalentStyles(
793 0 : aNode, aProperty, attribute, CSSEditUtils::eSpecified);
794 0 : if (hasAttribute) {
795 : // yes, tmp has the corresponding css declarations in its style
796 : // attribute
797 : // let's remove them
798 0 : mCSSEditUtils->RemoveCSSEquivalentToHTMLStyle(aNode.AsElement(),
799 : aProperty,
800 : attribute,
801 : nullptr,
802 0 : false);
803 : // remove the node if it is a span or font, if its style attribute is
804 : // empty or absent, and if it does not have a class nor an id
805 0 : RemoveElementIfNoStyleOrIdOrClass(*aNode.AsElement());
806 : }
807 : }
808 : }
809 :
810 : // Or node is big or small and we are setting font size
811 0 : if (aChildrenOnly) {
812 0 : return NS_OK;
813 : }
814 0 : if (aProperty == nsGkAtoms::font &&
815 0 : (aNode.IsHTMLElement(nsGkAtoms::big) ||
816 0 : aNode.IsHTMLElement(nsGkAtoms::small)) &&
817 0 : aAttribute && aAttribute->LowerCaseEqualsLiteral("size")) {
818 : // if we are setting font size, remove any nested bigs and smalls
819 0 : return RemoveContainer(&aNode);
820 : }
821 0 : return NS_OK;
822 : }
823 :
824 : bool
825 0 : HTMLEditor::IsOnlyAttribute(const nsIContent* aContent,
826 : const nsAString& aAttribute)
827 : {
828 0 : MOZ_ASSERT(aContent);
829 :
830 0 : uint32_t attrCount = aContent->GetAttrCount();
831 0 : for (uint32_t i = 0; i < attrCount; ++i) {
832 0 : const nsAttrName* name = aContent->GetAttrNameAt(i);
833 0 : if (!name->NamespaceEquals(kNameSpaceID_None)) {
834 0 : return false;
835 : }
836 :
837 0 : nsAutoString attrString;
838 0 : name->LocalName()->ToString(attrString);
839 : // if it's the attribute we know about, or a special _moz attribute,
840 : // keep looking
841 0 : if (!attrString.Equals(aAttribute, nsCaseInsensitiveStringComparator()) &&
842 0 : !StringBeginsWith(attrString, NS_LITERAL_STRING("_moz"))) {
843 0 : return false;
844 : }
845 : }
846 : // if we made it through all of them without finding a real attribute
847 : // other than aAttribute, then return true
848 0 : return true;
849 : }
850 :
851 : nsresult
852 0 : HTMLEditor::PromoteRangeIfStartsOrEndsInNamedAnchor(nsRange& aRange)
853 : {
854 : // We assume that <a> is not nested.
855 0 : nsCOMPtr<nsINode> startNode = aRange.GetStartContainer();
856 0 : int32_t startOffset = aRange.StartOffset();
857 0 : nsCOMPtr<nsINode> endNode = aRange.GetEndContainer();
858 0 : int32_t endOffset = aRange.EndOffset();
859 :
860 0 : nsCOMPtr<nsINode> parent = startNode;
861 :
862 0 : while (parent && !parent->IsHTMLElement(nsGkAtoms::body) &&
863 0 : !HTMLEditUtils::IsNamedAnchor(parent)) {
864 0 : parent = parent->GetParentNode();
865 : }
866 0 : NS_ENSURE_TRUE(parent, NS_ERROR_NULL_POINTER);
867 :
868 0 : if (HTMLEditUtils::IsNamedAnchor(parent)) {
869 0 : startNode = parent->GetParentNode();
870 0 : startOffset = startNode ? startNode->IndexOf(parent) : -1;
871 : }
872 :
873 0 : parent = endNode;
874 0 : while (parent && !parent->IsHTMLElement(nsGkAtoms::body) &&
875 0 : !HTMLEditUtils::IsNamedAnchor(parent)) {
876 0 : parent = parent->GetParentNode();
877 : }
878 0 : NS_ENSURE_TRUE(parent, NS_ERROR_NULL_POINTER);
879 :
880 0 : if (HTMLEditUtils::IsNamedAnchor(parent)) {
881 0 : endNode = parent->GetParentNode();
882 0 : endOffset = endNode ? endNode->IndexOf(parent) + 1 : 0;
883 : }
884 :
885 0 : nsresult rv = aRange.SetStartAndEnd(startNode, startOffset,
886 0 : endNode, endOffset);
887 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
888 0 : return rv;
889 : }
890 :
891 0 : return NS_OK;
892 : }
893 :
894 : nsresult
895 0 : HTMLEditor::PromoteInlineRange(nsRange& aRange)
896 : {
897 0 : nsCOMPtr<nsINode> startNode = aRange.GetStartContainer();
898 0 : int32_t startOffset = aRange.StartOffset();
899 0 : nsCOMPtr<nsINode> endNode = aRange.GetEndContainer();
900 0 : int32_t endOffset = aRange.EndOffset();
901 :
902 0 : while (startNode && !startNode->IsHTMLElement(nsGkAtoms::body) &&
903 0 : IsEditable(startNode) && IsAtFrontOfNode(*startNode, startOffset)) {
904 0 : nsCOMPtr<nsINode> parent = startNode->GetParentNode();
905 0 : NS_ENSURE_TRUE(parent, NS_ERROR_NULL_POINTER);
906 0 : startOffset = parent->IndexOf(startNode);
907 0 : startNode = parent;
908 : }
909 :
910 0 : while (endNode && !endNode->IsHTMLElement(nsGkAtoms::body) &&
911 0 : IsEditable(endNode) && IsAtEndOfNode(*endNode, endOffset)) {
912 0 : nsCOMPtr<nsINode> parent = endNode->GetParentNode();
913 0 : NS_ENSURE_TRUE(parent, NS_ERROR_NULL_POINTER);
914 : // We are AFTER this node
915 0 : endOffset = 1 + parent->IndexOf(endNode);
916 0 : endNode = parent;
917 : }
918 :
919 0 : nsresult rv = aRange.SetStartAndEnd(startNode, startOffset,
920 0 : endNode, endOffset);
921 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
922 0 : return rv;
923 : }
924 :
925 0 : return NS_OK;
926 : }
927 :
928 : bool
929 0 : HTMLEditor::IsAtFrontOfNode(nsINode& aNode,
930 : int32_t aOffset)
931 : {
932 0 : if (!aOffset) {
933 0 : return true;
934 : }
935 :
936 0 : if (IsTextNode(&aNode)) {
937 0 : return false;
938 : }
939 :
940 0 : nsCOMPtr<nsIContent> firstNode = GetFirstEditableChild(aNode);
941 0 : NS_ENSURE_TRUE(firstNode, true);
942 0 : if (aNode.IndexOf(firstNode) < aOffset) {
943 0 : return false;
944 : }
945 0 : return true;
946 : }
947 :
948 : bool
949 0 : HTMLEditor::IsAtEndOfNode(nsINode& aNode,
950 : int32_t aOffset)
951 : {
952 0 : if (aOffset == (int32_t)aNode.Length()) {
953 0 : return true;
954 : }
955 :
956 0 : if (IsTextNode(&aNode)) {
957 0 : return false;
958 : }
959 :
960 0 : nsCOMPtr<nsIContent> lastNode = GetLastEditableChild(aNode);
961 0 : NS_ENSURE_TRUE(lastNode, true);
962 0 : if (aNode.IndexOf(lastNode) < aOffset) {
963 0 : return true;
964 : }
965 0 : return false;
966 : }
967 :
968 :
969 : nsresult
970 0 : HTMLEditor::GetInlinePropertyBase(nsIAtom& aProperty,
971 : const nsAString* aAttribute,
972 : const nsAString* aValue,
973 : bool* aFirst,
974 : bool* aAny,
975 : bool* aAll,
976 : nsAString* outValue,
977 : bool aCheckDefaults)
978 : {
979 0 : *aAny = false;
980 0 : *aAll = true;
981 0 : *aFirst = false;
982 0 : bool first = true;
983 :
984 0 : RefPtr<Selection> selection = GetSelection();
985 0 : NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
986 :
987 0 : bool isCollapsed = selection->Collapsed();
988 0 : RefPtr<nsRange> range = selection->GetRangeAt(0);
989 : // XXX: Should be a while loop, to get each separate range
990 : // XXX: ERROR_HANDLING can currentItem be null?
991 0 : if (range) {
992 : // For each range, set a flag
993 0 : bool firstNodeInRange = true;
994 :
995 0 : if (isCollapsed) {
996 0 : nsCOMPtr<nsINode> collapsedNode = range->GetStartContainer();
997 0 : NS_ENSURE_TRUE(collapsedNode, NS_ERROR_FAILURE);
998 : bool isSet, theSetting;
999 0 : nsString tOutString;
1000 0 : if (aAttribute) {
1001 0 : nsString tString(*aAttribute);
1002 0 : mTypeInState->GetTypingState(isSet, theSetting, &aProperty, tString,
1003 0 : &tOutString);
1004 0 : if (outValue) {
1005 0 : outValue->Assign(tOutString);
1006 : }
1007 : } else {
1008 0 : mTypeInState->GetTypingState(isSet, theSetting, &aProperty);
1009 : }
1010 0 : if (isSet) {
1011 0 : *aFirst = *aAny = *aAll = theSetting;
1012 0 : return NS_OK;
1013 : }
1014 :
1015 0 : if (mCSSEditUtils->IsCSSEditableProperty(collapsedNode, &aProperty,
1016 : aAttribute)) {
1017 0 : if (aValue) {
1018 0 : tOutString.Assign(*aValue);
1019 : }
1020 0 : *aFirst = *aAny = *aAll =
1021 0 : mCSSEditUtils->IsCSSEquivalentToHTMLInlineStyleSet(collapsedNode,
1022 : &aProperty, aAttribute, tOutString, CSSEditUtils::eComputed);
1023 0 : if (outValue) {
1024 0 : outValue->Assign(tOutString);
1025 : }
1026 0 : return NS_OK;
1027 : }
1028 :
1029 0 : isSet = IsTextPropertySetByContent(collapsedNode, &aProperty,
1030 : aAttribute, aValue, outValue);
1031 0 : *aFirst = *aAny = *aAll = isSet;
1032 :
1033 0 : if (!isSet && aCheckDefaults) {
1034 : // Style not set, but if it is a default then it will appear if content
1035 : // is inserted, so we should report it as set (analogous to
1036 : // TypeInState).
1037 : int32_t index;
1038 0 : if (aAttribute && TypeInState::FindPropInList(&aProperty, *aAttribute,
1039 : outValue, mDefaultStyles,
1040 : index)) {
1041 0 : *aFirst = *aAny = *aAll = true;
1042 0 : if (outValue) {
1043 0 : outValue->Assign(mDefaultStyles[index]->value);
1044 : }
1045 : }
1046 : }
1047 0 : return NS_OK;
1048 : }
1049 :
1050 : // Non-collapsed selection
1051 0 : nsCOMPtr<nsIContentIterator> iter = NS_NewContentIterator();
1052 :
1053 0 : nsAutoString firstValue, theValue;
1054 :
1055 0 : nsCOMPtr<nsINode> endNode = range->GetEndContainer();
1056 0 : int32_t endOffset = range->EndOffset();
1057 :
1058 0 : for (iter->Init(range); !iter->IsDone(); iter->Next()) {
1059 0 : if (!iter->GetCurrentNode()->IsContent()) {
1060 0 : continue;
1061 : }
1062 0 : nsCOMPtr<nsIContent> content = iter->GetCurrentNode()->AsContent();
1063 :
1064 0 : if (content->IsHTMLElement(nsGkAtoms::body)) {
1065 0 : break;
1066 : }
1067 :
1068 : // just ignore any non-editable nodes
1069 0 : if (content->GetAsText() && (!IsEditable(content) ||
1070 0 : IsEmptyTextNode(this, content))) {
1071 0 : continue;
1072 : }
1073 0 : if (content->GetAsText()) {
1074 0 : if (!isCollapsed && first && firstNodeInRange) {
1075 0 : firstNodeInRange = false;
1076 0 : if (range->StartOffset() == (int32_t)content->Length()) {
1077 0 : continue;
1078 : }
1079 0 : } else if (content == endNode && !endOffset) {
1080 0 : continue;
1081 : }
1082 0 : } else if (content->IsElement()) {
1083 : // handle non-text leaf nodes here
1084 0 : continue;
1085 : }
1086 :
1087 0 : bool isSet = false;
1088 0 : if (first) {
1089 0 : if (mCSSEditUtils->IsCSSEditableProperty(content, &aProperty,
1090 0 : aAttribute)) {
1091 : // The HTML styles defined by aProperty/aAttribute have a CSS
1092 : // equivalence in this implementation for node; let's check if it
1093 : // carries those CSS styles
1094 0 : if (aValue) {
1095 0 : firstValue.Assign(*aValue);
1096 : }
1097 0 : isSet = mCSSEditUtils->IsCSSEquivalentToHTMLInlineStyleSet(content,
1098 0 : &aProperty, aAttribute, firstValue, CSSEditUtils::eComputed);
1099 : } else {
1100 : isSet = IsTextPropertySetByContent(content, &aProperty, aAttribute,
1101 0 : aValue, &firstValue);
1102 : }
1103 0 : *aFirst = isSet;
1104 0 : first = false;
1105 0 : if (outValue) {
1106 0 : *outValue = firstValue;
1107 : }
1108 : } else {
1109 0 : if (mCSSEditUtils->IsCSSEditableProperty(content, &aProperty,
1110 0 : aAttribute)) {
1111 : // The HTML styles defined by aProperty/aAttribute have a CSS
1112 : // equivalence in this implementation for node; let's check if it
1113 : // carries those CSS styles
1114 0 : if (aValue) {
1115 0 : theValue.Assign(*aValue);
1116 : }
1117 0 : isSet = mCSSEditUtils->IsCSSEquivalentToHTMLInlineStyleSet(content,
1118 0 : &aProperty, aAttribute, theValue, CSSEditUtils::eComputed);
1119 : } else {
1120 : isSet = IsTextPropertySetByContent(content, &aProperty, aAttribute,
1121 0 : aValue, &theValue);
1122 : }
1123 0 : if (firstValue != theValue) {
1124 0 : *aAll = false;
1125 : }
1126 : }
1127 :
1128 0 : if (isSet) {
1129 0 : *aAny = true;
1130 : } else {
1131 0 : *aAll = false;
1132 : }
1133 : }
1134 : }
1135 0 : if (!*aAny) {
1136 : // make sure that if none of the selection is set, we don't report all is
1137 : // set
1138 0 : *aAll = false;
1139 : }
1140 0 : return NS_OK;
1141 : }
1142 :
1143 : NS_IMETHODIMP
1144 0 : HTMLEditor::GetInlineProperty(nsIAtom* aProperty,
1145 : const nsAString& aAttribute,
1146 : const nsAString& aValue,
1147 : bool* aFirst,
1148 : bool* aAny,
1149 : bool* aAll)
1150 : {
1151 0 : NS_ENSURE_TRUE(aProperty && aFirst && aAny && aAll, NS_ERROR_NULL_POINTER);
1152 0 : const nsAString *att = nullptr;
1153 0 : if (!aAttribute.IsEmpty())
1154 0 : att = &aAttribute;
1155 0 : const nsAString *val = nullptr;
1156 0 : if (!aValue.IsEmpty())
1157 0 : val = &aValue;
1158 0 : return GetInlinePropertyBase(*aProperty, att, val, aFirst, aAny, aAll, nullptr);
1159 : }
1160 :
1161 : NS_IMETHODIMP
1162 0 : HTMLEditor::GetInlinePropertyWithAttrValue(nsIAtom* aProperty,
1163 : const nsAString& aAttribute,
1164 : const nsAString& aValue,
1165 : bool* aFirst,
1166 : bool* aAny,
1167 : bool* aAll,
1168 : nsAString& outValue)
1169 : {
1170 0 : NS_ENSURE_TRUE(aProperty && aFirst && aAny && aAll, NS_ERROR_NULL_POINTER);
1171 0 : const nsAString *att = nullptr;
1172 0 : if (!aAttribute.IsEmpty())
1173 0 : att = &aAttribute;
1174 0 : const nsAString *val = nullptr;
1175 0 : if (!aValue.IsEmpty())
1176 0 : val = &aValue;
1177 0 : return GetInlinePropertyBase(*aProperty, att, val, aFirst, aAny, aAll, &outValue);
1178 : }
1179 :
1180 : NS_IMETHODIMP
1181 0 : HTMLEditor::RemoveAllInlineProperties()
1182 : {
1183 0 : AutoEditBatch batchIt(this);
1184 : AutoRules beginRulesSniffing(this, EditAction::resetTextProperties,
1185 0 : nsIEditor::eNext);
1186 :
1187 0 : nsresult rv = RemoveInlinePropertyImpl(nullptr, nullptr);
1188 0 : NS_ENSURE_SUCCESS(rv, rv);
1189 0 : return ApplyDefaultProperties();
1190 : }
1191 :
1192 : NS_IMETHODIMP
1193 0 : HTMLEditor::RemoveInlineProperty(nsIAtom* aProperty,
1194 : const nsAString& aAttribute)
1195 : {
1196 0 : return RemoveInlinePropertyImpl(aProperty, &aAttribute);
1197 : }
1198 :
1199 : nsresult
1200 0 : HTMLEditor::RemoveInlinePropertyImpl(nsIAtom* aProperty,
1201 : const nsAString* aAttribute)
1202 : {
1203 0 : MOZ_ASSERT_IF(aProperty, aAttribute);
1204 0 : NS_ENSURE_TRUE(mRules, NS_ERROR_NOT_INITIALIZED);
1205 0 : ForceCompositionEnd();
1206 :
1207 0 : RefPtr<Selection> selection = GetSelection();
1208 0 : NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
1209 :
1210 0 : if (selection->Collapsed()) {
1211 : // Manipulating text attributes on a collapsed selection only sets state
1212 : // for the next text insertion
1213 :
1214 : // For links, aProperty uses "href", use "a" instead
1215 0 : if (aProperty == nsGkAtoms::href || aProperty == nsGkAtoms::name) {
1216 0 : aProperty = nsGkAtoms::a;
1217 : }
1218 :
1219 0 : if (aProperty) {
1220 0 : mTypeInState->ClearProp(aProperty, *aAttribute);
1221 : } else {
1222 0 : mTypeInState->ClearAllProps();
1223 : }
1224 0 : return NS_OK;
1225 : }
1226 :
1227 0 : AutoEditBatch batchIt(this);
1228 : AutoRules beginRulesSniffing(this, EditAction::removeTextProperty,
1229 0 : nsIEditor::eNext);
1230 0 : AutoSelectionRestorer selectionRestorer(selection, this);
1231 0 : AutoTransactionsConserveSelection dontSpazMySelection(this);
1232 :
1233 : bool cancel, handled;
1234 0 : TextRulesInfo ruleInfo(EditAction::removeTextProperty);
1235 : // Protect the edit rules object from dying
1236 0 : nsCOMPtr<nsIEditRules> rules(mRules);
1237 0 : nsresult rv = rules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
1238 0 : NS_ENSURE_SUCCESS(rv, rv);
1239 0 : if (!cancel && !handled) {
1240 : // Loop through the ranges in the selection
1241 : // Since ranges might be modified by SplitStyleAboveRange, we need hold
1242 : // current ranges
1243 0 : AutoRangeArray arrayOfRanges(selection);
1244 0 : for (auto& range : arrayOfRanges.mRanges) {
1245 0 : if (aProperty == nsGkAtoms::name) {
1246 : // Promote range if it starts or end in a named anchor and we want to
1247 : // remove named anchors
1248 0 : rv = PromoteRangeIfStartsOrEndsInNamedAnchor(*range);
1249 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1250 0 : return rv;
1251 : }
1252 : } else {
1253 : // Adjust range to include any ancestors whose children are entirely
1254 : // selected
1255 0 : rv = PromoteInlineRange(*range);
1256 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1257 0 : return rv;
1258 : }
1259 : }
1260 :
1261 : // Remove this style from ancestors of our range endpoints, splitting
1262 : // them as appropriate
1263 0 : rv = SplitStyleAboveRange(range, aProperty, aAttribute);
1264 0 : NS_ENSURE_SUCCESS(rv, rv);
1265 :
1266 : // Check for easy case: both range endpoints in same text node
1267 0 : nsCOMPtr<nsINode> startNode = range->GetStartContainer();
1268 0 : nsCOMPtr<nsINode> endNode = range->GetEndContainer();
1269 0 : if (startNode && startNode == endNode && startNode->GetAsText()) {
1270 : // We're done with this range!
1271 0 : if (IsCSSEnabled() &&
1272 0 : mCSSEditUtils->IsCSSEditableProperty(startNode, aProperty,
1273 : aAttribute)) {
1274 : // The HTML style defined by aProperty/aAttribute has a CSS
1275 : // equivalence in this implementation for startNode
1276 0 : if (mCSSEditUtils->IsCSSEquivalentToHTMLInlineStyleSet(startNode,
1277 0 : aProperty, aAttribute, EmptyString(),
1278 : CSSEditUtils::eComputed)) {
1279 : // startNode's computed style indicates the CSS equivalence to the
1280 : // HTML style to remove is applied; but we found no element in the
1281 : // ancestors of startNode carrying specified styles; assume it
1282 : // comes from a rule and try to insert a span "inverting" the style
1283 0 : if (mCSSEditUtils->IsCSSInvertible(*aProperty, aAttribute)) {
1284 0 : NS_NAMED_LITERAL_STRING(value, "-moz-editor-invert-value");
1285 0 : SetInlinePropertyOnTextNode(*startNode->GetAsText(),
1286 : range->StartOffset(),
1287 : range->EndOffset(), *aProperty,
1288 0 : aAttribute, value);
1289 : }
1290 : }
1291 : }
1292 : } else {
1293 : // Not the easy case. Range not contained in single text node.
1294 0 : nsCOMPtr<nsIContentIterator> iter = NS_NewContentSubtreeIterator();
1295 :
1296 0 : nsTArray<OwningNonNull<nsIContent>> arrayOfNodes;
1297 :
1298 : // Iterate range and build up array
1299 0 : for (iter->Init(range); !iter->IsDone(); iter->Next()) {
1300 0 : nsCOMPtr<nsINode> node = iter->GetCurrentNode();
1301 0 : NS_ENSURE_TRUE(node, NS_ERROR_FAILURE);
1302 :
1303 0 : if (IsEditable(node) && node->IsContent()) {
1304 0 : arrayOfNodes.AppendElement(*node->AsContent());
1305 : }
1306 : }
1307 :
1308 : // Loop through the list, remove the property on each node
1309 0 : for (auto& node : arrayOfNodes) {
1310 0 : rv = RemoveStyleInside(node, aProperty, aAttribute);
1311 0 : NS_ENSURE_SUCCESS(rv, rv);
1312 0 : if (IsCSSEnabled() &&
1313 0 : mCSSEditUtils->IsCSSEditableProperty(node, aProperty,
1314 0 : aAttribute) &&
1315 0 : mCSSEditUtils->IsCSSEquivalentToHTMLInlineStyleSet(node,
1316 0 : aProperty, aAttribute, EmptyString(),
1317 0 : CSSEditUtils::eComputed) &&
1318 : // startNode's computed style indicates the CSS equivalence to
1319 : // the HTML style to remove is applied; but we found no element
1320 : // in the ancestors of startNode carrying specified styles;
1321 : // assume it comes from a rule and let's try to insert a span
1322 : // "inverting" the style
1323 0 : mCSSEditUtils->IsCSSInvertible(*aProperty, aAttribute)) {
1324 0 : NS_NAMED_LITERAL_STRING(value, "-moz-editor-invert-value");
1325 0 : SetInlinePropertyOnNode(node, *aProperty, aAttribute, value);
1326 : }
1327 : }
1328 : }
1329 : }
1330 : }
1331 0 : if (!cancel) {
1332 : // Post-process
1333 0 : rv = rules->DidDoAction(selection, &ruleInfo, rv);
1334 0 : NS_ENSURE_SUCCESS(rv, rv);
1335 : }
1336 0 : return NS_OK;
1337 : }
1338 :
1339 : NS_IMETHODIMP
1340 0 : HTMLEditor::IncreaseFontSize()
1341 : {
1342 0 : return RelativeFontChange(FontSize::incr);
1343 : }
1344 :
1345 : NS_IMETHODIMP
1346 0 : HTMLEditor::DecreaseFontSize()
1347 : {
1348 0 : return RelativeFontChange(FontSize::decr);
1349 : }
1350 :
1351 : nsresult
1352 0 : HTMLEditor::RelativeFontChange(FontSize aDir)
1353 : {
1354 0 : ForceCompositionEnd();
1355 :
1356 : // Get the selection
1357 0 : RefPtr<Selection> selection = GetSelection();
1358 0 : NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
1359 : // If selection is collapsed, set typing state
1360 0 : if (selection->Collapsed()) {
1361 : nsIAtom& atom = aDir == FontSize::incr ? *nsGkAtoms::big :
1362 0 : *nsGkAtoms::small;
1363 :
1364 : // Let's see in what kind of element the selection is
1365 0 : NS_ENSURE_TRUE(selection->RangeCount() &&
1366 : selection->GetRangeAt(0)->GetStartContainer(), NS_OK);
1367 : OwningNonNull<nsINode> selectedNode =
1368 0 : *selection->GetRangeAt(0)->GetStartContainer();
1369 0 : if (IsTextNode(selectedNode)) {
1370 0 : NS_ENSURE_TRUE(selectedNode->GetParentNode(), NS_OK);
1371 0 : selectedNode = *selectedNode->GetParentNode();
1372 : }
1373 0 : if (!CanContainTag(selectedNode, atom)) {
1374 0 : return NS_OK;
1375 : }
1376 :
1377 : // Manipulating text attributes on a collapsed selection only sets state
1378 : // for the next text insertion
1379 0 : mTypeInState->SetProp(&atom, EmptyString(), EmptyString());
1380 0 : return NS_OK;
1381 : }
1382 :
1383 : // Wrap with txn batching, rules sniffing, and selection preservation code
1384 0 : AutoEditBatch batchIt(this);
1385 : AutoRules beginRulesSniffing(this, EditAction::setTextProperty,
1386 0 : nsIEditor::eNext);
1387 0 : AutoSelectionRestorer selectionRestorer(selection, this);
1388 0 : AutoTransactionsConserveSelection dontSpazMySelection(this);
1389 :
1390 : // Loop through the ranges in the selection
1391 0 : AutoRangeArray arrayOfRanges(selection);
1392 0 : for (auto& range : arrayOfRanges.mRanges) {
1393 : // Adjust range to include any ancestors with entirely selected children
1394 0 : nsresult rv = PromoteInlineRange(*range);
1395 0 : NS_ENSURE_SUCCESS(rv, rv);
1396 :
1397 : // Check for easy case: both range endpoints in same text node
1398 0 : nsCOMPtr<nsINode> startNode = range->GetStartContainer();
1399 0 : nsCOMPtr<nsINode> endNode = range->GetEndContainer();
1400 0 : if (startNode == endNode && IsTextNode(startNode)) {
1401 0 : rv = RelativeFontChangeOnTextNode(aDir, *startNode->GetAsText(),
1402 : range->StartOffset(),
1403 0 : range->EndOffset());
1404 0 : NS_ENSURE_SUCCESS(rv, rv);
1405 : } else {
1406 : // Not the easy case. Range not contained in single text node. There
1407 : // are up to three phases here. There are all the nodes reported by the
1408 : // subtree iterator to be processed. And there are potentially a
1409 : // starting textnode and an ending textnode which are only partially
1410 : // contained by the range.
1411 :
1412 : // Let's handle the nodes reported by the iterator. These nodes are
1413 : // entirely contained in the selection range. We build up a list of them
1414 : // (since doing operations on the document during iteration would perturb
1415 : // the iterator).
1416 :
1417 0 : OwningNonNull<nsIContentIterator> iter = NS_NewContentSubtreeIterator();
1418 :
1419 : // Iterate range and build up array
1420 0 : rv = iter->Init(range);
1421 0 : if (NS_SUCCEEDED(rv)) {
1422 0 : nsTArray<OwningNonNull<nsIContent>> arrayOfNodes;
1423 0 : for (; !iter->IsDone(); iter->Next()) {
1424 0 : NS_ENSURE_TRUE(iter->GetCurrentNode()->IsContent(), NS_ERROR_FAILURE);
1425 0 : OwningNonNull<nsIContent> node = *iter->GetCurrentNode()->AsContent();
1426 :
1427 0 : if (IsEditable(node)) {
1428 0 : arrayOfNodes.AppendElement(node);
1429 : }
1430 : }
1431 :
1432 : // Now that we have the list, do the font size change on each node
1433 0 : for (auto& node : arrayOfNodes) {
1434 0 : rv = RelativeFontChangeOnNode(aDir == FontSize::incr ? +1 : -1, node);
1435 0 : NS_ENSURE_SUCCESS(rv, rv);
1436 : }
1437 : }
1438 : // Now check the start and end parents of the range to see if they need
1439 : // to be separately handled (they do if they are text nodes, due to how
1440 : // the subtree iterator works - it will not have reported them).
1441 0 : if (IsTextNode(startNode) && IsEditable(startNode)) {
1442 0 : rv = RelativeFontChangeOnTextNode(aDir, *startNode->GetAsText(),
1443 : range->StartOffset(),
1444 0 : startNode->Length());
1445 0 : NS_ENSURE_SUCCESS(rv, rv);
1446 : }
1447 0 : if (IsTextNode(endNode) && IsEditable(endNode)) {
1448 0 : rv = RelativeFontChangeOnTextNode(aDir, *endNode->GetAsText(), 0,
1449 0 : range->EndOffset());
1450 0 : NS_ENSURE_SUCCESS(rv, rv);
1451 : }
1452 : }
1453 : }
1454 :
1455 0 : return NS_OK;
1456 : }
1457 :
1458 : nsresult
1459 0 : HTMLEditor::RelativeFontChangeOnTextNode(FontSize aDir,
1460 : Text& aTextNode,
1461 : int32_t aStartOffset,
1462 : int32_t aEndOffset)
1463 : {
1464 : // Don't need to do anything if no characters actually selected
1465 0 : if (aStartOffset == aEndOffset) {
1466 0 : return NS_OK;
1467 : }
1468 :
1469 0 : if (!aTextNode.GetParentNode() ||
1470 0 : !CanContainTag(*aTextNode.GetParentNode(), *nsGkAtoms::big)) {
1471 0 : return NS_OK;
1472 : }
1473 :
1474 0 : OwningNonNull<nsIContent> node = aTextNode;
1475 :
1476 : // Do we need to split the text node?
1477 :
1478 : // -1 is a magic value meaning to the end of node
1479 0 : if (aEndOffset == -1) {
1480 0 : aEndOffset = aTextNode.Length();
1481 : }
1482 :
1483 0 : ErrorResult rv;
1484 0 : if ((uint32_t)aEndOffset != aTextNode.Length()) {
1485 : // We need to split off back of text node
1486 0 : node = SplitNode(node, aEndOffset, rv);
1487 0 : NS_ENSURE_TRUE(!rv.Failed(), rv.StealNSResult());
1488 : }
1489 0 : if (aStartOffset) {
1490 : // We need to split off front of text node
1491 0 : SplitNode(node, aStartOffset, rv);
1492 0 : NS_ENSURE_TRUE(!rv.Failed(), rv.StealNSResult());
1493 : }
1494 :
1495 : // Look for siblings that are correct type of node
1496 0 : nsIAtom* nodeType = aDir == FontSize::incr ? nsGkAtoms::big
1497 0 : : nsGkAtoms::small;
1498 0 : nsCOMPtr<nsIContent> sibling = GetPriorHTMLSibling(node);
1499 0 : if (sibling && sibling->IsHTMLElement(nodeType)) {
1500 : // Previous sib is already right kind of inline node; slide this over
1501 0 : nsresult rv = MoveNode(node, sibling, -1);
1502 0 : NS_ENSURE_SUCCESS(rv, rv);
1503 0 : return NS_OK;
1504 : }
1505 0 : sibling = GetNextHTMLSibling(node);
1506 0 : if (sibling && sibling->IsHTMLElement(nodeType)) {
1507 : // Following sib is already right kind of inline node; slide this over
1508 0 : nsresult rv = MoveNode(node, sibling, 0);
1509 0 : NS_ENSURE_SUCCESS(rv, rv);
1510 0 : return NS_OK;
1511 : }
1512 :
1513 : // Else reparent the node inside font node with appropriate relative size
1514 0 : nsCOMPtr<Element> newElement = InsertContainerAbove(node, nodeType);
1515 0 : NS_ENSURE_STATE(newElement);
1516 :
1517 0 : return NS_OK;
1518 : }
1519 :
1520 : nsresult
1521 0 : HTMLEditor::RelativeFontChangeHelper(int32_t aSizeChange,
1522 : nsINode* aNode)
1523 : {
1524 0 : MOZ_ASSERT(aNode);
1525 :
1526 : /* This routine looks for all the font nodes in the tree rooted by aNode,
1527 : including aNode itself, looking for font nodes that have the size attr
1528 : set. Any such nodes need to have big or small put inside them, since
1529 : they override any big/small that are above them.
1530 : */
1531 :
1532 : // Can only change font size by + or - 1
1533 0 : if (aSizeChange != 1 && aSizeChange != -1) {
1534 0 : return NS_ERROR_ILLEGAL_VALUE;
1535 : }
1536 :
1537 : // If this is a font node with size, put big/small inside it.
1538 0 : if (aNode->IsHTMLElement(nsGkAtoms::font) &&
1539 0 : aNode->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::size)) {
1540 : // Cycle through children and adjust relative font size.
1541 0 : for (uint32_t i = aNode->GetChildCount(); i--; ) {
1542 0 : nsresult rv = RelativeFontChangeOnNode(aSizeChange, aNode->GetChildAt(i));
1543 0 : NS_ENSURE_SUCCESS(rv, rv);
1544 : }
1545 :
1546 : // RelativeFontChangeOnNode already calls us recursively,
1547 : // so we don't need to check our children again.
1548 0 : return NS_OK;
1549 : }
1550 :
1551 : // Otherwise cycle through the children.
1552 0 : for (uint32_t i = aNode->GetChildCount(); i--; ) {
1553 0 : nsresult rv = RelativeFontChangeHelper(aSizeChange, aNode->GetChildAt(i));
1554 0 : NS_ENSURE_SUCCESS(rv, rv);
1555 : }
1556 :
1557 0 : return NS_OK;
1558 : }
1559 :
1560 : nsresult
1561 0 : HTMLEditor::RelativeFontChangeOnNode(int32_t aSizeChange,
1562 : nsIContent* aNode)
1563 : {
1564 0 : MOZ_ASSERT(aNode);
1565 : // Can only change font size by + or - 1
1566 0 : if (aSizeChange != 1 && aSizeChange != -1) {
1567 0 : return NS_ERROR_ILLEGAL_VALUE;
1568 : }
1569 :
1570 : nsIAtom* atom;
1571 0 : if (aSizeChange == 1) {
1572 0 : atom = nsGkAtoms::big;
1573 : } else {
1574 0 : atom = nsGkAtoms::small;
1575 : }
1576 :
1577 : // Is it the opposite of what we want?
1578 0 : if ((aSizeChange == 1 && aNode->IsHTMLElement(nsGkAtoms::small)) ||
1579 0 : (aSizeChange == -1 && aNode->IsHTMLElement(nsGkAtoms::big))) {
1580 : // first populate any nested font tags that have the size attr set
1581 0 : nsresult rv = RelativeFontChangeHelper(aSizeChange, aNode);
1582 0 : NS_ENSURE_SUCCESS(rv, rv);
1583 : // in that case, just remove this node and pull up the children
1584 0 : return RemoveContainer(aNode);
1585 : }
1586 :
1587 : // can it be put inside a "big" or "small"?
1588 0 : if (TagCanContain(*atom, *aNode)) {
1589 : // first populate any nested font tags that have the size attr set
1590 0 : nsresult rv = RelativeFontChangeHelper(aSizeChange, aNode);
1591 0 : NS_ENSURE_SUCCESS(rv, rv);
1592 :
1593 : // ok, chuck it in.
1594 : // first look at siblings of aNode for matching bigs or smalls.
1595 : // if we find one, move aNode into it.
1596 0 : nsIContent* sibling = GetPriorHTMLSibling(aNode);
1597 0 : if (sibling && sibling->IsHTMLElement(atom)) {
1598 : // previous sib is already right kind of inline node; slide this over into it
1599 0 : return MoveNode(aNode, sibling, -1);
1600 : }
1601 :
1602 0 : sibling = GetNextHTMLSibling(aNode);
1603 0 : if (sibling && sibling->IsHTMLElement(atom)) {
1604 : // following sib is already right kind of inline node; slide this over into it
1605 0 : return MoveNode(aNode, sibling, 0);
1606 : }
1607 :
1608 : // else insert it above aNode
1609 0 : nsCOMPtr<Element> newElement = InsertContainerAbove(aNode, atom);
1610 0 : NS_ENSURE_STATE(newElement);
1611 :
1612 0 : return NS_OK;
1613 : }
1614 :
1615 : // none of the above? then cycle through the children.
1616 : // MOOSE: we should group the children together if possible
1617 : // into a single "big" or "small". For the moment they are
1618 : // each getting their own.
1619 0 : for (uint32_t i = aNode->GetChildCount(); i--; ) {
1620 0 : nsresult rv = RelativeFontChangeOnNode(aSizeChange, aNode->GetChildAt(i));
1621 0 : NS_ENSURE_SUCCESS(rv, rv);
1622 : }
1623 :
1624 0 : return NS_OK;
1625 : }
1626 :
1627 : NS_IMETHODIMP
1628 0 : HTMLEditor::GetFontFaceState(bool* aMixed,
1629 : nsAString& outFace)
1630 : {
1631 0 : NS_ENSURE_TRUE(aMixed, NS_ERROR_FAILURE);
1632 0 : *aMixed = true;
1633 0 : outFace.Truncate();
1634 :
1635 : bool first, any, all;
1636 :
1637 0 : NS_NAMED_LITERAL_STRING(attr, "face");
1638 : nsresult rv =
1639 0 : GetInlinePropertyBase(*nsGkAtoms::font, &attr.AsString(), nullptr, &first,
1640 0 : &any, &all, &outFace);
1641 0 : NS_ENSURE_SUCCESS(rv, rv);
1642 0 : if (any && !all) {
1643 0 : return NS_OK; // mixed
1644 : }
1645 0 : if (all) {
1646 0 : *aMixed = false;
1647 0 : return NS_OK;
1648 : }
1649 :
1650 : // if there is no font face, check for tt
1651 0 : rv = GetInlinePropertyBase(*nsGkAtoms::tt, nullptr, nullptr, &first, &any,
1652 0 : &all,nullptr);
1653 0 : NS_ENSURE_SUCCESS(rv, rv);
1654 0 : if (any && !all) {
1655 0 : return rv; // mixed
1656 : }
1657 0 : if (all) {
1658 0 : *aMixed = false;
1659 0 : outFace.AssignLiteral("tt");
1660 : }
1661 :
1662 0 : if (!any) {
1663 : // there was no font face attrs of any kind. We are in normal font.
1664 0 : outFace.Truncate();
1665 0 : *aMixed = false;
1666 : }
1667 0 : return NS_OK;
1668 : }
1669 :
1670 : NS_IMETHODIMP
1671 0 : HTMLEditor::GetFontColorState(bool* aMixed,
1672 : nsAString& aOutColor)
1673 : {
1674 0 : NS_ENSURE_TRUE(aMixed, NS_ERROR_NULL_POINTER);
1675 0 : *aMixed = true;
1676 0 : aOutColor.Truncate();
1677 :
1678 0 : NS_NAMED_LITERAL_STRING(colorStr, "color");
1679 : bool first, any, all;
1680 :
1681 : nsresult rv =
1682 0 : GetInlinePropertyBase(*nsGkAtoms::font, &colorStr.AsString(), nullptr,
1683 0 : &first, &any, &all, &aOutColor);
1684 0 : NS_ENSURE_SUCCESS(rv, rv);
1685 0 : if (any && !all) {
1686 0 : return NS_OK; // mixed
1687 : }
1688 0 : if (all) {
1689 0 : *aMixed = false;
1690 0 : return NS_OK;
1691 : }
1692 :
1693 0 : if (!any) {
1694 : // there was no font color attrs of any kind..
1695 0 : aOutColor.Truncate();
1696 0 : *aMixed = false;
1697 : }
1698 0 : return NS_OK;
1699 : }
1700 :
1701 : // the return value is true only if the instance of the HTML editor we created
1702 : // can handle CSS styles (for instance, Composer can, Messenger can't) and if
1703 : // the CSS preference is checked
1704 : nsresult
1705 0 : HTMLEditor::GetIsCSSEnabled(bool* aIsCSSEnabled)
1706 : {
1707 0 : *aIsCSSEnabled = IsCSSEnabled();
1708 0 : return NS_OK;
1709 : }
1710 :
1711 : static bool
1712 0 : HasNonEmptyAttribute(Element* aElement,
1713 : nsIAtom* aName)
1714 : {
1715 0 : MOZ_ASSERT(aElement);
1716 :
1717 0 : nsAutoString value;
1718 0 : return aElement->GetAttr(kNameSpaceID_None, aName, value) && !value.IsEmpty();
1719 : }
1720 :
1721 : bool
1722 0 : HTMLEditor::HasStyleOrIdOrClass(Element* aElement)
1723 : {
1724 0 : MOZ_ASSERT(aElement);
1725 :
1726 : // remove the node if its style attribute is empty or absent,
1727 : // and if it does not have a class nor an id
1728 0 : return HasNonEmptyAttribute(aElement, nsGkAtoms::style) ||
1729 0 : HasNonEmptyAttribute(aElement, nsGkAtoms::_class) ||
1730 0 : HasNonEmptyAttribute(aElement, nsGkAtoms::id);
1731 : }
1732 :
1733 : nsresult
1734 0 : HTMLEditor::RemoveElementIfNoStyleOrIdOrClass(Element& aElement)
1735 : {
1736 : // early way out if node is not the right kind of element
1737 0 : if ((!aElement.IsHTMLElement(nsGkAtoms::span) &&
1738 0 : !aElement.IsHTMLElement(nsGkAtoms::font)) ||
1739 0 : HasStyleOrIdOrClass(&aElement)) {
1740 0 : return NS_OK;
1741 : }
1742 :
1743 0 : return RemoveContainer(&aElement);
1744 : }
1745 :
1746 : } // namespace mozilla
|