LCOV - code coverage report
Current view: top level - editor/libeditor - HTMLStyleEditor.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 824 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 38 0.0 %
Legend: Lines: hit not hit

          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

Generated by: LCOV version 1.13