editor/libeditor/html/nsHTMLEditorStyle.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     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 #include "TypeInState.h"
     6 #include "mozilla/Assertions.h"
     7 #include "mozilla/dom/Selection.h"
     8 #include "mozilla/dom/Element.h"
     9 #include "mozilla/mozalloc.h"
    10 #include "nsAString.h"
    11 #include "nsAttrName.h"
    12 #include "nsAutoPtr.h"
    13 #include "nsCOMArray.h"
    14 #include "nsCOMPtr.h"
    15 #include "nsCaseTreatment.h"
    16 #include "nsComponentManagerUtils.h"
    17 #include "nsDebug.h"
    18 #include "nsEditProperty.h"
    19 #include "nsEditRules.h"
    20 #include "nsEditor.h"
    21 #include "nsEditorUtils.h"
    22 #include "nsError.h"
    23 #include "nsGkAtoms.h"
    24 #include "nsHTMLCSSUtils.h"
    25 #include "nsHTMLEditUtils.h"
    26 #include "nsHTMLEditor.h"
    27 #include "nsIAtom.h"
    28 #include "nsIContent.h"
    29 #include "nsIContentIterator.h"
    30 #include "nsIDOMCharacterData.h"
    31 #include "nsIDOMElement.h"
    32 #include "nsIDOMNode.h"
    33 #include "nsIDOMRange.h"
    34 #include "nsIEditor.h"
    35 #include "nsIEditorIMESupport.h"
    36 #include "nsNameSpaceManager.h"
    37 #include "nsINode.h"
    38 #include "nsISelection.h"
    39 #include "nsISelectionPrivate.h"
    40 #include "nsISupportsImpl.h"
    41 #include "nsLiteralString.h"
    42 #include "nsReadableUtils.h"
    43 #include "nsSelectionState.h"
    44 #include "nsString.h"
    45 #include "nsStringFwd.h"
    46 #include "nsTArray.h"
    47 #include "nsTextEditRules.h"
    48 #include "nsTextEditUtils.h"
    49 #include "nsUnicharUtils.h"
    50 #include "nscore.h"
    52 class nsISupports;
    54 using namespace mozilla;
    55 using namespace mozilla::dom;
    57 static bool
    58 IsEmptyTextNode(nsHTMLEditor* aThis, nsINode* aNode)
    59 {
    60   bool isEmptyTextNode = false;
    61   return nsEditor::IsTextNode(aNode) &&
    62          NS_SUCCEEDED(aThis->IsEmptyNode(aNode, &isEmptyTextNode)) &&
    63          isEmptyTextNode;
    64 }
    66 NS_IMETHODIMP nsHTMLEditor::AddDefaultProperty(nsIAtom *aProperty, 
    67                                             const nsAString & aAttribute, 
    68                                             const nsAString & aValue)
    69 {
    70   nsString outValue;
    71   int32_t index;
    72   nsString attr(aAttribute);
    73   if (TypeInState::FindPropInList(aProperty, attr, &outValue, mDefaultStyles, index))
    74   {
    75     PropItem *item = mDefaultStyles[index];
    76     item->value = aValue;
    77   }
    78   else
    79   {
    80     nsString value(aValue);
    81     PropItem *propItem = new PropItem(aProperty, attr, value);
    82     mDefaultStyles.AppendElement(propItem);
    83   }
    84   return NS_OK;
    85 }
    87 NS_IMETHODIMP nsHTMLEditor::RemoveDefaultProperty(nsIAtom *aProperty, 
    88                                    const nsAString & aAttribute, 
    89                                    const nsAString & aValue)
    90 {
    91   nsString outValue;
    92   int32_t index;
    93   nsString attr(aAttribute);
    94   if (TypeInState::FindPropInList(aProperty, attr, &outValue, mDefaultStyles, index))
    95   {
    96     delete mDefaultStyles[index];
    97     mDefaultStyles.RemoveElementAt(index);
    98   }
    99   return NS_OK;
   100 }
   102 NS_IMETHODIMP nsHTMLEditor::RemoveAllDefaultProperties()
   103 {
   104   uint32_t j, defcon = mDefaultStyles.Length();
   105   for (j=0; j<defcon; j++)
   106   {
   107     delete mDefaultStyles[j];
   108   }
   109   mDefaultStyles.Clear();
   110   return NS_OK;
   111 }
   114 NS_IMETHODIMP
   115 nsHTMLEditor::SetInlineProperty(nsIAtom *aProperty,
   116                                 const nsAString& aAttribute,
   117                                 const nsAString& aValue)
   118 {
   119   if (!aProperty) {
   120     return NS_ERROR_NULL_POINTER;
   121   }
   122   if (!mRules) {
   123     return NS_ERROR_NOT_INITIALIZED;
   124   }
   125   ForceCompositionEnd();
   127   nsRefPtr<Selection> selection = GetSelection();
   128   NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
   130   if (selection->Collapsed()) {
   131     // manipulating text attributes on a collapsed selection only sets state
   132     // for the next text insertion
   133     mTypeInState->SetProp(aProperty, aAttribute, aValue);
   134     return NS_OK;
   135   }
   137   nsAutoEditBatch batchIt(this);
   138   nsAutoRules beginRulesSniffing(this, EditAction::insertElement, nsIEditor::eNext);
   139   nsAutoSelectionReset selectionResetter(selection, this);
   140   nsAutoTxnsConserveSelection dontSpazMySelection(this);
   142   bool cancel, handled;
   143   nsTextRulesInfo ruleInfo(EditAction::setTextProperty);
   144   // Protect the edit rules object from dying
   145   nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules);
   146   nsresult res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
   147   NS_ENSURE_SUCCESS(res, res);
   148   if (!cancel && !handled) {
   149     // loop thru the ranges in the selection
   150     uint32_t rangeCount = selection->GetRangeCount();
   151     for (uint32_t rangeIdx = 0; rangeIdx < rangeCount; ++rangeIdx) {
   152       nsRefPtr<nsRange> range = selection->GetRangeAt(rangeIdx);
   154       // adjust range to include any ancestors whose children are entirely
   155       // selected
   156       res = PromoteInlineRange(range);
   157       NS_ENSURE_SUCCESS(res, res);
   159       // check for easy case: both range endpoints in same text node
   160       nsCOMPtr<nsIDOMNode> startNode, endNode;
   161       res = range->GetStartContainer(getter_AddRefs(startNode));
   162       NS_ENSURE_SUCCESS(res, res);
   163       res = range->GetEndContainer(getter_AddRefs(endNode));
   164       NS_ENSURE_SUCCESS(res, res);
   165       if (startNode == endNode && IsTextNode(startNode)) {
   166         int32_t startOffset, endOffset;
   167         range->GetStartOffset(&startOffset);
   168         range->GetEndOffset(&endOffset);
   169         nsCOMPtr<nsIDOMCharacterData> nodeAsText = do_QueryInterface(startNode);
   170         res = SetInlinePropertyOnTextNode(nodeAsText, startOffset, endOffset,
   171                                           aProperty, &aAttribute, &aValue);
   172         NS_ENSURE_SUCCESS(res, res);
   173         continue;
   174       }
   176       // Not the easy case.  Range not contained in single text node.  There
   177       // are up to three phases here.  There are all the nodes reported by the
   178       // subtree iterator to be processed.  And there are potentially a
   179       // starting textnode and an ending textnode which are only partially
   180       // contained by the range.
   182       // Let's handle the nodes reported by the iterator.  These nodes are
   183       // entirely contained in the selection range.  We build up a list of them
   184       // (since doing operations on the document during iteration would perturb
   185       // the iterator).
   187       nsCOMPtr<nsIContentIterator> iter =
   188         do_CreateInstance("@mozilla.org/content/subtree-content-iterator;1", &res);
   189       NS_ENSURE_SUCCESS(res, res);
   190       NS_ENSURE_TRUE(iter, NS_ERROR_FAILURE);
   192       nsCOMArray<nsIDOMNode> arrayOfNodes;
   194       // iterate range and build up array
   195       res = iter->Init(range);
   196       // Init returns an error if there are no nodes in range.  This can easily
   197       // happen with the subtree iterator if the selection doesn't contain any
   198       // *whole* nodes.
   199       if (NS_SUCCEEDED(res)) {
   200         nsCOMPtr<nsIDOMNode> node;
   201         for (; !iter->IsDone(); iter->Next()) {
   202           node = do_QueryInterface(iter->GetCurrentNode());
   203           NS_ENSURE_TRUE(node, NS_ERROR_FAILURE);
   205           if (IsEditable(node)) {
   206             arrayOfNodes.AppendObject(node);
   207           }
   208         }
   209       }
   210       // first check the start parent of the range to see if it needs to
   211       // be separately handled (it does if it's a text node, due to how the
   212       // subtree iterator works - it will not have reported it).
   213       if (IsTextNode(startNode) && IsEditable(startNode)) {
   214         nsCOMPtr<nsIDOMCharacterData> nodeAsText = do_QueryInterface(startNode);
   215         int32_t startOffset;
   216         uint32_t textLen;
   217         range->GetStartOffset(&startOffset);
   218         nodeAsText->GetLength(&textLen);
   219         res = SetInlinePropertyOnTextNode(nodeAsText, startOffset, textLen,
   220                                           aProperty, &aAttribute, &aValue);
   221         NS_ENSURE_SUCCESS(res, res);
   222       }
   224       // then loop through the list, set the property on each node
   225       int32_t listCount = arrayOfNodes.Count();
   226       int32_t j;
   227       for (j = 0; j < listCount; j++) {
   228         res = SetInlinePropertyOnNode(arrayOfNodes[j], aProperty,
   229                                       &aAttribute, &aValue);
   230         NS_ENSURE_SUCCESS(res, res);
   231       }
   233       // last check the end parent of the range to see if it needs to
   234       // be separately handled (it does if it's a text node, due to how the
   235       // subtree iterator works - it will not have reported it).
   236       if (IsTextNode(endNode) && IsEditable(endNode)) {
   237         nsCOMPtr<nsIDOMCharacterData> nodeAsText = do_QueryInterface(endNode);
   238         int32_t endOffset;
   239         range->GetEndOffset(&endOffset);
   240         res = SetInlinePropertyOnTextNode(nodeAsText, 0, endOffset,
   241                                           aProperty, &aAttribute, &aValue);
   242         NS_ENSURE_SUCCESS(res, res);
   243       }
   244     }
   245   }
   246   if (!cancel) {
   247     // post-process
   248     return mRules->DidDoAction(selection, &ruleInfo, res);
   249   }
   250   return NS_OK;
   251 }
   255 // Helper function for SetInlinePropertyOn*: is aNode a simple old <b>, <font>,
   256 // <span style="">, etc. that we can reuse instead of creating a new one?
   257 bool
   258 nsHTMLEditor::IsSimpleModifiableNode(nsIContent* aContent,
   259                                      nsIAtom* aProperty,
   260                                      const nsAString* aAttribute,
   261                                      const nsAString* aValue)
   262 {
   263   // aContent can be null, in which case we'll return false in a few lines
   264   MOZ_ASSERT(aProperty);
   265   MOZ_ASSERT_IF(aAttribute, aValue);
   267   nsCOMPtr<dom::Element> element = do_QueryInterface(aContent);
   268   if (!element) {
   269     return false;
   270   }
   272   // First check for <b>, <i>, etc.
   273   if (element->IsHTML(aProperty) && !element->GetAttrCount() &&
   274       (!aAttribute || aAttribute->IsEmpty())) {
   275     return true;
   276   }
   278   // Special cases for various equivalencies: <strong>, <em>, <s>
   279   if (!element->GetAttrCount() &&
   280       ((aProperty == nsGkAtoms::b && element->IsHTML(nsGkAtoms::strong)) ||
   281        (aProperty == nsGkAtoms::i && element->IsHTML(nsGkAtoms::em)) ||
   282        (aProperty == nsGkAtoms::strike && element->IsHTML(nsGkAtoms::s)))) {
   283     return true;
   284   }
   286   // Now look for things like <font>
   287   if (aAttribute && !aAttribute->IsEmpty()) {
   288     nsCOMPtr<nsIAtom> atom = do_GetAtom(*aAttribute);
   289     MOZ_ASSERT(atom);
   291     nsString attrValue;
   292     if (element->IsHTML(aProperty) && IsOnlyAttribute(element, *aAttribute) &&
   293         element->GetAttr(kNameSpaceID_None, atom, attrValue) &&
   294         attrValue.Equals(*aValue, nsCaseInsensitiveStringComparator())) {
   295       // This is not quite correct, because it excludes cases like
   296       // <font face=000> being the same as <font face=#000000>.
   297       // Property-specific handling is needed (bug 760211).
   298       return true;
   299     }
   300   }
   302   // No luck so far.  Now we check for a <span> with a single style=""
   303   // attribute that sets only the style we're looking for, if this type of
   304   // style supports it
   305   if (!mHTMLCSSUtils->IsCSSEditableProperty(element, aProperty, aAttribute) ||
   306       !element->IsHTML(nsGkAtoms::span) || element->GetAttrCount() != 1 ||
   307       !element->HasAttr(kNameSpaceID_None, nsGkAtoms::style)) {
   308     return false;
   309   }
   311   // Some CSS styles are not so simple.  For instance, underline is
   312   // "text-decoration: underline", which decomposes into four different text-*
   313   // properties.  So for now, we just create a span, add the desired style, and
   314   // see if it matches.
   315   nsCOMPtr<dom::Element> newSpan;
   316   nsresult res = CreateHTMLContent(NS_LITERAL_STRING("span"),
   317                                    getter_AddRefs(newSpan));
   318   NS_ASSERTION(NS_SUCCEEDED(res), "CreateHTMLContent failed");
   319   NS_ENSURE_SUCCESS(res, false);
   320   mHTMLCSSUtils->SetCSSEquivalentToHTMLStyle(newSpan, aProperty,
   321                                              aAttribute, aValue,
   322                                              /*suppress transaction*/ true);
   324   return mHTMLCSSUtils->ElementsSameStyle(newSpan, element);
   325 }
   328 nsresult
   329 nsHTMLEditor::SetInlinePropertyOnTextNode( nsIDOMCharacterData *aTextNode, 
   330                                             int32_t aStartOffset,
   331                                             int32_t aEndOffset,
   332                                             nsIAtom *aProperty, 
   333                                             const nsAString *aAttribute,
   334                                             const nsAString *aValue)
   335 {
   336   MOZ_ASSERT(aValue);
   337   NS_ENSURE_TRUE(aTextNode, NS_ERROR_NULL_POINTER);
   338   nsCOMPtr<nsIDOMNode> parent;
   339   nsresult res = aTextNode->GetParentNode(getter_AddRefs(parent));
   340   NS_ENSURE_SUCCESS(res, res);
   342   if (!CanContainTag(parent, aProperty)) {
   343     return NS_OK;
   344   }
   346   // don't need to do anything if no characters actually selected
   347   if (aStartOffset == aEndOffset) return NS_OK;
   349   nsCOMPtr<nsIDOMNode> node = aTextNode;
   351   // don't need to do anything if property already set on node
   352   bool bHasProp;
   353   if (mHTMLCSSUtils->IsCSSEditableProperty(node, aProperty, aAttribute)) {
   354     // the HTML styles defined by aProperty/aAttribute has a CSS equivalence
   355     // in this implementation for node; let's check if it carries those css styles
   356     nsAutoString value(*aValue);
   357     mHTMLCSSUtils->IsCSSEquivalentToHTMLInlineStyleSet(node, aProperty, aAttribute,
   358                                                        bHasProp, value,
   359                                                        nsHTMLCSSUtils::eComputed);
   360   } else {
   361     IsTextPropertySetByContent(node, aProperty, aAttribute, aValue, bHasProp);
   362   }
   364   if (bHasProp) return NS_OK;
   366   // do we need to split the text node?
   367   uint32_t textLen;
   368   aTextNode->GetLength(&textLen);
   370   if (uint32_t(aEndOffset) != textLen) {
   371     // we need to split off back of text node
   372     nsCOMPtr<nsIDOMNode> tmp;
   373     res = SplitNode(node, aEndOffset, getter_AddRefs(tmp));
   374     NS_ENSURE_SUCCESS(res, res);
   375     node = tmp;  // remember left node
   376   }
   378   if (aStartOffset) {
   379     // we need to split off front of text node
   380     nsCOMPtr<nsIDOMNode> tmp;
   381     res = SplitNode(node, aStartOffset, getter_AddRefs(tmp));
   382     NS_ENSURE_SUCCESS(res, res);
   383   }
   385   nsCOMPtr<nsIContent> content = do_QueryInterface(node);
   386   NS_ENSURE_STATE(content);
   388   if (aAttribute) {
   389     // look for siblings that are correct type of node
   390     nsIContent* sibling = GetPriorHTMLSibling(content);
   391     if (IsSimpleModifiableNode(sibling, aProperty, aAttribute, aValue)) {
   392       // previous sib is already right kind of inline node; slide this over into it
   393       return MoveNode(node, sibling->AsDOMNode(), -1);
   394     }
   395     sibling = GetNextHTMLSibling(content);
   396     if (IsSimpleModifiableNode(sibling, aProperty, aAttribute, aValue)) {
   397       // following sib is already right kind of inline node; slide this over into it
   398       return MoveNode(node, sibling->AsDOMNode(), 0);
   399     }
   400   }
   402   // reparent the node inside inline node with appropriate {attribute,value}
   403   return SetInlinePropertyOnNode(node, aProperty, aAttribute, aValue);
   404 }
   407 nsresult
   408 nsHTMLEditor::SetInlinePropertyOnNodeImpl(nsIContent* aNode,
   409                                           nsIAtom* aProperty,
   410                                           const nsAString* aAttribute,
   411                                           const nsAString* aValue)
   412 {
   413   MOZ_ASSERT(aNode && aProperty);
   414   MOZ_ASSERT(aValue);
   416   // If this is an element that can't be contained in a span, we have to
   417   // recurse to its children.
   418   if (!TagCanContain(nsGkAtoms::span, aNode->AsDOMNode())) {
   419     if (aNode->HasChildren()) {
   420       nsCOMArray<nsIContent> arrayOfNodes;
   422       // Populate the list.
   423       for (nsIContent* child = aNode->GetFirstChild();
   424            child;
   425            child = child->GetNextSibling()) {
   426         if (IsEditable(child) && !IsEmptyTextNode(this, child)) {
   427           arrayOfNodes.AppendObject(child);
   428         }
   429       }
   431       // Then loop through the list, set the property on each node.
   432       int32_t listCount = arrayOfNodes.Count();
   433       for (int32_t j = 0; j < listCount; ++j) {
   434         nsresult rv = SetInlinePropertyOnNode(arrayOfNodes[j], aProperty,
   435                                               aAttribute, aValue);
   436         NS_ENSURE_SUCCESS(rv, rv);
   437       }
   438     }
   439     return NS_OK;
   440   }
   442   // First check if there's an adjacent sibling we can put our node into.
   443   nsresult res;
   444   nsCOMPtr<nsIContent> previousSibling = GetPriorHTMLSibling(aNode);
   445   nsCOMPtr<nsIContent> nextSibling = GetNextHTMLSibling(aNode);
   446   if (IsSimpleModifiableNode(previousSibling, aProperty, aAttribute, aValue)) {
   447     res = MoveNode(aNode, previousSibling, -1);
   448     NS_ENSURE_SUCCESS(res, res);
   449     if (IsSimpleModifiableNode(nextSibling, aProperty, aAttribute, aValue)) {
   450       res = JoinNodes(previousSibling, nextSibling);
   451       NS_ENSURE_SUCCESS(res, res);
   452     }
   453     return NS_OK;
   454   }
   455   if (IsSimpleModifiableNode(nextSibling, aProperty, aAttribute, aValue)) {
   456     res = MoveNode(aNode, nextSibling, 0);
   457     NS_ENSURE_SUCCESS(res, res);
   458     return NS_OK;
   459   }
   461   // don't need to do anything if property already set on node
   462   if (mHTMLCSSUtils->IsCSSEditableProperty(aNode, aProperty, aAttribute)) {
   463     if (mHTMLCSSUtils->IsCSSEquivalentToHTMLInlineStyleSet(
   464           aNode, aProperty, aAttribute, *aValue, nsHTMLCSSUtils::eComputed)) {
   465       return NS_OK;
   466     }
   467   } else if (IsTextPropertySetByContent(aNode, aProperty,
   468                                         aAttribute, aValue)) {
   469     return NS_OK;
   470   }
   472   bool useCSS = (IsCSSEnabled() &&
   473                  mHTMLCSSUtils->IsCSSEditableProperty(aNode, aProperty, aAttribute)) ||
   474                 // bgcolor is always done using CSS
   475                 aAttribute->EqualsLiteral("bgcolor");
   477   if (useCSS) {
   478     nsCOMPtr<dom::Element> tmp;
   479     // We only add style="" to <span>s with no attributes (bug 746515).  If we
   480     // don't have one, we need to make one.
   481     if (aNode->IsElement() && aNode->AsElement()->IsHTML(nsGkAtoms::span) &&
   482         !aNode->AsElement()->GetAttrCount()) {
   483       tmp = aNode->AsElement();
   484     } else {
   485       res = InsertContainerAbove(aNode, getter_AddRefs(tmp),
   486                                  NS_LITERAL_STRING("span"),
   487                                  nullptr, nullptr);
   488       NS_ENSURE_SUCCESS(res, res);
   489     }
   491     // Add the CSS styles corresponding to the HTML style request
   492     int32_t count;
   493     res = mHTMLCSSUtils->SetCSSEquivalentToHTMLStyle(tmp->AsDOMNode(),
   494                                                      aProperty, aAttribute,
   495                                                      aValue, &count, false);
   496     NS_ENSURE_SUCCESS(res, res);
   497     return NS_OK;
   498   }
   500   // is it already the right kind of node, but with wrong attribute?
   501   if (aNode->Tag() == aProperty) {
   502     // Just set the attribute on it.
   503     nsCOMPtr<nsIDOMElement> elem = do_QueryInterface(aNode);
   504     return SetAttribute(elem, *aAttribute, *aValue);
   505   }
   507   // ok, chuck it in its very own container
   508   nsAutoString tag;
   509   aProperty->ToString(tag);
   510   ToLowerCase(tag);
   511   nsCOMPtr<nsIDOMNode> tmp;
   512   return InsertContainerAbove(aNode->AsDOMNode(), address_of(tmp), tag,
   513                               aAttribute, aValue);
   514 }
   517 nsresult
   518 nsHTMLEditor::SetInlinePropertyOnNode(nsIDOMNode *aNode,
   519                                       nsIAtom *aProperty,
   520                                       const nsAString *aAttribute,
   521                                       const nsAString *aValue)
   522 {
   523   // Before setting the property, we remove it if it's already set.
   524   // RemoveStyleInside might remove the node we're looking at or some of its
   525   // descendants, however, in which case we want to set the property on
   526   // whatever wound up in its place.  We have to save the original siblings and
   527   // parent to figure this out.
   528   NS_ENSURE_TRUE(aNode && aProperty, NS_ERROR_NULL_POINTER);
   530   nsCOMPtr<nsIContent> node = do_QueryInterface(aNode);
   531   NS_ENSURE_STATE(node);
   533   return SetInlinePropertyOnNode(node, aProperty, aAttribute, aValue);
   534 }
   536 nsresult
   537 nsHTMLEditor::SetInlinePropertyOnNode(nsIContent* aNode,
   538                                       nsIAtom* aProperty,
   539                                       const nsAString* aAttribute,
   540                                       const nsAString* aValue)
   541 {
   542   MOZ_ASSERT(aNode);
   543   MOZ_ASSERT(aProperty);
   545   nsCOMPtr<nsIContent> previousSibling = aNode->GetPreviousSibling(),
   546                        nextSibling = aNode->GetNextSibling();
   547   nsCOMPtr<nsINode> parent = aNode->GetParentNode();
   548   NS_ENSURE_STATE(parent);
   550   nsresult res = RemoveStyleInside(aNode->AsDOMNode(), aProperty, aAttribute);
   551   NS_ENSURE_SUCCESS(res, res);
   553   if (aNode->GetParentNode()) {
   554     // The node is still where it was
   555     return SetInlinePropertyOnNodeImpl(aNode, aProperty,
   556                                        aAttribute, aValue);
   557   }
   559   // It's vanished.  Use the old siblings for reference to construct a
   560   // list.  But first, verify that the previous/next siblings are still
   561   // where we expect them; otherwise we have to give up.
   562   if ((previousSibling && previousSibling->GetParentNode() != parent) ||
   563       (nextSibling && nextSibling->GetParentNode() != parent)) {
   564     return NS_ERROR_UNEXPECTED;
   565   }
   566   nsCOMArray<nsIContent> nodesToSet;
   567   nsCOMPtr<nsIContent> cur = previousSibling
   568     ? previousSibling->GetNextSibling() : parent->GetFirstChild();
   569   while (cur && cur != nextSibling) {
   570     if (IsEditable(cur)) {
   571       nodesToSet.AppendObject(cur);
   572     }
   573     cur = cur->GetNextSibling();
   574   }
   576   int32_t nodesToSetCount = nodesToSet.Count();
   577   for (int32_t k = 0; k < nodesToSetCount; k++) {
   578     res = SetInlinePropertyOnNodeImpl(nodesToSet[k], aProperty,
   579                                       aAttribute, aValue);
   580     NS_ENSURE_SUCCESS(res, res);
   581   }
   583   return NS_OK;
   584 }
   587 nsresult nsHTMLEditor::SplitStyleAboveRange(nsIDOMRange *inRange, 
   588                                             nsIAtom *aProperty, 
   589                                             const nsAString *aAttribute)
   590 {
   591   NS_ENSURE_TRUE(inRange, NS_ERROR_NULL_POINTER);
   592   nsresult res;
   593   nsCOMPtr<nsIDOMNode> startNode, endNode, origStartNode;
   594   int32_t startOffset, endOffset;
   596   res = inRange->GetStartContainer(getter_AddRefs(startNode));
   597   NS_ENSURE_SUCCESS(res, res);
   598   res = inRange->GetStartOffset(&startOffset);
   599   NS_ENSURE_SUCCESS(res, res);
   600   res = inRange->GetEndContainer(getter_AddRefs(endNode));
   601   NS_ENSURE_SUCCESS(res, res);
   602   res = inRange->GetEndOffset(&endOffset);
   603   NS_ENSURE_SUCCESS(res, res);
   605   origStartNode = startNode;
   607   // split any matching style nodes above the start of range
   608   {
   609     nsAutoTrackDOMPoint tracker(mRangeUpdater, address_of(endNode), &endOffset);
   610     res = SplitStyleAbovePoint(address_of(startNode), &startOffset, aProperty, aAttribute);
   611     NS_ENSURE_SUCCESS(res, res);
   612   }
   614   // second verse, same as the first...
   615   res = SplitStyleAbovePoint(address_of(endNode), &endOffset, aProperty, aAttribute);
   616   NS_ENSURE_SUCCESS(res, res);
   618   // reset the range
   619   res = inRange->SetStart(startNode, startOffset);
   620   NS_ENSURE_SUCCESS(res, res);
   621   res = inRange->SetEnd(endNode, endOffset);
   622   return res;
   623 }
   625 nsresult nsHTMLEditor::SplitStyleAbovePoint(nsCOMPtr<nsIDOMNode> *aNode,
   626                                            int32_t *aOffset,
   627                                            nsIAtom *aProperty,          // null here means we split all properties
   628                                            const nsAString *aAttribute,
   629                                            nsCOMPtr<nsIDOMNode> *outLeftNode,
   630                                            nsCOMPtr<nsIDOMNode> *outRightNode)
   631 {
   632   NS_ENSURE_TRUE(aNode && *aNode && aOffset, NS_ERROR_NULL_POINTER);
   633   if (outLeftNode)  *outLeftNode  = nullptr;
   634   if (outRightNode) *outRightNode = nullptr;
   635   // split any matching style nodes above the node/offset
   636   nsCOMPtr<nsIDOMNode> parent, tmp = *aNode;
   637   int32_t offset;
   639   bool useCSS = IsCSSEnabled();
   641   bool isSet;
   642   while (tmp && !IsBlockNode(tmp))
   643   {
   644     isSet = false;
   645     if (useCSS && mHTMLCSSUtils->IsCSSEditableProperty(tmp, aProperty, aAttribute)) {
   646       // the HTML style defined by aProperty/aAttribute has a CSS equivalence
   647       // in this implementation for the node tmp; let's check if it carries those css styles
   648       nsAutoString firstValue;
   649       mHTMLCSSUtils->IsCSSEquivalentToHTMLInlineStyleSet(tmp, aProperty,
   650         aAttribute, isSet, firstValue, nsHTMLCSSUtils::eSpecified);
   651     }
   652     if ( (aProperty && NodeIsType(tmp, aProperty)) ||   // node is the correct inline prop
   653          (aProperty == nsEditProperty::href && nsHTMLEditUtils::IsLink(tmp)) ||
   654                                                         // node is href - test if really <a href=...
   655          (!aProperty && NodeIsProperty(tmp)) ||         // or node is any prop, and we asked to split them all
   656          isSet)                                         // or the style is specified in the style attribute
   657     {
   658       // found a style node we need to split
   659       nsresult rv = SplitNodeDeep(tmp, *aNode, *aOffset, &offset, false,
   660                                   outLeftNode, outRightNode);
   661       NS_ENSURE_SUCCESS(rv, rv);
   662       // reset startNode/startOffset
   663       tmp->GetParentNode(getter_AddRefs(*aNode));
   664       *aOffset = offset;
   665     }
   666     tmp->GetParentNode(getter_AddRefs(parent));
   667     tmp = parent;
   668   }
   669   return NS_OK;
   670 }
   672 nsresult
   673 nsHTMLEditor::ClearStyle(nsCOMPtr<nsIDOMNode>* aNode, int32_t* aOffset,
   674                          nsIAtom* aProperty, const nsAString* aAttribute)
   675 {
   676   nsCOMPtr<nsIDOMNode> leftNode, rightNode, tmp;
   677   nsresult res = SplitStyleAbovePoint(aNode, aOffset, aProperty, aAttribute,
   678                                       address_of(leftNode),
   679                                       address_of(rightNode));
   680   NS_ENSURE_SUCCESS(res, res);
   681   if (leftNode) {
   682     bool bIsEmptyNode;
   683     IsEmptyNode(leftNode, &bIsEmptyNode, false, true);
   684     if (bIsEmptyNode) {
   685       // delete leftNode if it became empty
   686       res = DeleteNode(leftNode);
   687       NS_ENSURE_SUCCESS(res, res);
   688     }
   689   }
   690   if (rightNode) {
   691     nsCOMPtr<nsIDOMNode> secondSplitParent = GetLeftmostChild(rightNode);
   692     // don't try to split non-containers (br's, images, hr's, etc)
   693     if (!secondSplitParent) {
   694       secondSplitParent = rightNode;
   695     }
   696     nsCOMPtr<nsIDOMNode> savedBR;
   697     if (!IsContainer(secondSplitParent)) {
   698       if (nsTextEditUtils::IsBreak(secondSplitParent)) {
   699         savedBR = secondSplitParent;
   700       }
   702       secondSplitParent->GetParentNode(getter_AddRefs(tmp));
   703       secondSplitParent = tmp;
   704     }
   705     *aOffset = 0;
   706     res = SplitStyleAbovePoint(address_of(secondSplitParent),
   707                                aOffset, aProperty, aAttribute,
   708                                address_of(leftNode), address_of(rightNode));
   709     NS_ENSURE_SUCCESS(res, res);
   710     // should be impossible to not get a new leftnode here
   711     NS_ENSURE_TRUE(leftNode, NS_ERROR_FAILURE);
   712     nsCOMPtr<nsIDOMNode> newSelParent = GetLeftmostChild(leftNode);
   713     if (!newSelParent) {
   714       newSelParent = leftNode;
   715     }
   716     // If rightNode starts with a br, suck it out of right node and into
   717     // leftNode.  This is so we you don't revert back to the previous style
   718     // if you happen to click at the end of a line.
   719     if (savedBR) {
   720       res = MoveNode(savedBR, newSelParent, 0);
   721       NS_ENSURE_SUCCESS(res, res);
   722     }
   723     bool bIsEmptyNode;
   724     IsEmptyNode(rightNode, &bIsEmptyNode, false, true);
   725     if (bIsEmptyNode) {
   726       // delete rightNode if it became empty
   727       res = DeleteNode(rightNode);
   728       NS_ENSURE_SUCCESS(res, res);
   729     }
   730     // remove the style on this new hierarchy
   731     int32_t newSelOffset = 0;
   732     {
   733       // Track the point at the new hierarchy.  This is so we can know where
   734       // to put the selection after we call RemoveStyleInside().
   735       // RemoveStyleInside() could remove any and all of those nodes, so I
   736       // have to use the range tracking system to find the right spot to put
   737       // selection.
   738       nsAutoTrackDOMPoint tracker(mRangeUpdater,
   739                                   address_of(newSelParent), &newSelOffset);
   740       res = RemoveStyleInside(leftNode, aProperty, aAttribute);
   741       NS_ENSURE_SUCCESS(res, res);
   742     }
   743     // reset our node offset values to the resulting new sel point
   744     *aNode = newSelParent;
   745     *aOffset = newSelOffset;
   746   }
   748   return NS_OK;
   749 }
   751 bool nsHTMLEditor::NodeIsProperty(nsIDOMNode *aNode)
   752 {
   753   NS_ENSURE_TRUE(aNode, false);
   754   if (!IsContainer(aNode))  return false;
   755   if (!IsEditable(aNode))   return false;
   756   if (IsBlockNode(aNode))   return false;
   757   if (NodeIsType(aNode, nsEditProperty::a)) return false;
   758   return true;
   759 }
   761 nsresult nsHTMLEditor::ApplyDefaultProperties()
   762 {
   763   nsresult res = NS_OK;
   764   uint32_t j, defcon = mDefaultStyles.Length();
   765   for (j=0; j<defcon; j++)
   766   {
   767     PropItem *propItem = mDefaultStyles[j];
   768     NS_ENSURE_TRUE(propItem, NS_ERROR_NULL_POINTER);
   769     res = SetInlineProperty(propItem->tag, propItem->attr, propItem->value);
   770     NS_ENSURE_SUCCESS(res, res);
   771   }
   772   return res;
   773 }
   775 nsresult nsHTMLEditor::RemoveStyleInside(nsIDOMNode *aNode, 
   776                                          // null here means remove all properties
   777                                          nsIAtom *aProperty,
   778                                          const nsAString *aAttribute,
   779                                          const bool aChildrenOnly)
   780 {
   781   NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER);
   782   if (IsTextNode(aNode)) {
   783     return NS_OK;
   784   }
   785   nsresult res;
   787   // first process the children
   788   nsCOMPtr<nsIDOMNode> child, tmp;
   789   aNode->GetFirstChild(getter_AddRefs(child));
   790   while (child) {
   791     // cache next sibling since we might remove child
   792     child->GetNextSibling(getter_AddRefs(tmp));
   793     res = RemoveStyleInside(child, aProperty, aAttribute);
   794     NS_ENSURE_SUCCESS(res, res);
   795     child = tmp;
   796   }
   798   // then process the node itself
   799   if (!aChildrenOnly &&
   800     (
   801       // node is prop we asked for
   802       (aProperty && NodeIsType(aNode, aProperty)) ||
   803       // but check for link (<a href=...)
   804       (aProperty == nsEditProperty::href && nsHTMLEditUtils::IsLink(aNode)) ||
   805       // and for named anchors
   806       (aProperty == nsEditProperty::name && nsHTMLEditUtils::IsNamedAnchor(aNode)) ||
   807       // or node is any prop and we asked for that
   808       (!aProperty && NodeIsProperty(aNode))
   809     )
   810   ) {
   811     // if we weren't passed an attribute, then we want to 
   812     // remove any matching inlinestyles entirely
   813     if (!aAttribute || aAttribute->IsEmpty()) {
   814       NS_NAMED_LITERAL_STRING(styleAttr, "style");
   815       NS_NAMED_LITERAL_STRING(classAttr, "class");
   816       bool hasStyleAttr = HasAttr(aNode, &styleAttr);
   817       bool hasClassAttr = HasAttr(aNode, &classAttr);
   818       if (aProperty && (hasStyleAttr || hasClassAttr)) {
   819         // aNode carries inline styles or a class attribute so we can't
   820         // just remove the element... We need to create above the element
   821         // a span that will carry those styles or class, then we can delete
   822         // the node.
   823         nsCOMPtr<nsIDOMNode> spanNode;
   824         res = InsertContainerAbove(aNode, address_of(spanNode),
   825                                    NS_LITERAL_STRING("span"));
   826         NS_ENSURE_SUCCESS(res, res);
   827         res = CloneAttribute(styleAttr, spanNode, aNode);
   828         NS_ENSURE_SUCCESS(res, res);
   829         res = CloneAttribute(classAttr, spanNode, aNode);
   830         NS_ENSURE_SUCCESS(res, res);
   831       }
   832       res = RemoveContainer(aNode);
   833       NS_ENSURE_SUCCESS(res, res);
   834     } else {
   835       // otherwise we just want to eliminate the attribute
   836       if (HasAttr(aNode, aAttribute)) {
   837         // if this matching attribute is the ONLY one on the node,
   838         // then remove the whole node.  Otherwise just nix the attribute.
   839         if (IsOnlyAttribute(aNode, aAttribute)) {
   840           res = RemoveContainer(aNode);
   841         } else {
   842           nsCOMPtr<nsIDOMElement> elem = do_QueryInterface(aNode);
   843           NS_ENSURE_TRUE(elem, NS_ERROR_NULL_POINTER);
   844           res = RemoveAttribute(elem, *aAttribute);
   845         }
   846         NS_ENSURE_SUCCESS(res, res);
   847       }
   848     }
   849   }
   851   if (!aChildrenOnly &&
   852       mHTMLCSSUtils->IsCSSEditableProperty(aNode, aProperty, aAttribute)) {
   853     // the HTML style defined by aProperty/aAttribute has a CSS equivalence in
   854     // this implementation for the node aNode; let's check if it carries those
   855     // css styles
   856     nsAutoString propertyValue;
   857     bool isSet;
   858     mHTMLCSSUtils->IsCSSEquivalentToHTMLInlineStyleSet(aNode, aProperty,
   859       aAttribute, isSet, propertyValue, nsHTMLCSSUtils::eSpecified);
   860     if (isSet) {
   861       // yes, tmp has the corresponding css declarations in its style attribute
   862       // let's remove them
   863       mHTMLCSSUtils->RemoveCSSEquivalentToHTMLStyle(aNode,
   864                                                     aProperty,
   865                                                     aAttribute,
   866                                                     &propertyValue,
   867                                                     false);
   868       // remove the node if it is a span or font, if its style attribute is
   869       // empty or absent, and if it does not have a class nor an id
   870       RemoveElementIfNoStyleOrIdOrClass(aNode);
   871     }
   872   }
   874   if (!aChildrenOnly &&
   875     (
   876       (aProperty == nsEditProperty::font) &&    // or node is big or small and we are setting font size
   877       (nsHTMLEditUtils::IsBig(aNode) || nsHTMLEditUtils::IsSmall(aNode)) &&
   878       (aAttribute && aAttribute->LowerCaseEqualsLiteral("size"))
   879     )
   880   ) {
   881     return RemoveContainer(aNode);  // if we are setting font size, remove any nested bigs and smalls
   882   }
   883   return NS_OK;
   884 }
   886 bool nsHTMLEditor::IsOnlyAttribute(nsIDOMNode *aNode, 
   887                                      const nsAString *aAttribute)
   888 {
   889   NS_ENSURE_TRUE(aNode && aAttribute, false);  // ooops
   891   nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
   892   NS_ENSURE_TRUE(content, false);  // ooops
   894   return IsOnlyAttribute(content, *aAttribute);
   895 }
   897 bool
   898 nsHTMLEditor::IsOnlyAttribute(const nsIContent* aContent,
   899                               const nsAString& aAttribute)
   900 {
   901   MOZ_ASSERT(aContent);
   903   uint32_t attrCount = aContent->GetAttrCount();
   904   for (uint32_t i = 0; i < attrCount; ++i) {
   905     const nsAttrName* name = aContent->GetAttrNameAt(i);
   906     if (!name->NamespaceEquals(kNameSpaceID_None)) {
   907       return false;
   908     }
   910     nsAutoString attrString;
   911     name->LocalName()->ToString(attrString);
   912     // if it's the attribute we know about, or a special _moz attribute,
   913     // keep looking
   914     if (!attrString.Equals(aAttribute, nsCaseInsensitiveStringComparator()) &&
   915         !StringBeginsWith(attrString, NS_LITERAL_STRING("_moz"))) {
   916       return false;
   917     }
   918   }
   919   // if we made it through all of them without finding a real attribute
   920   // other than aAttribute, then return true
   921   return true;
   922 }
   924 bool nsHTMLEditor::HasAttr(nsIDOMNode* aNode,
   925                            const nsAString* aAttribute)
   926 {
   927   NS_ENSURE_TRUE(aNode, false);
   928   if (!aAttribute || aAttribute->IsEmpty()) {
   929     // everybody has the 'null' attribute
   930     return true;
   931   }
   933   // get element
   934   nsCOMPtr<dom::Element> element = do_QueryInterface(aNode);
   935   NS_ENSURE_TRUE(element, false);
   937   nsCOMPtr<nsIAtom> atom = do_GetAtom(*aAttribute);
   938   NS_ENSURE_TRUE(atom, false);
   940   return element->HasAttr(kNameSpaceID_None, atom);
   941 }
   944 nsresult nsHTMLEditor::PromoteRangeIfStartsOrEndsInNamedAnchor(nsIDOMRange *inRange)
   945 {
   946   NS_ENSURE_TRUE(inRange, NS_ERROR_NULL_POINTER);
   947   nsresult res;
   948   nsCOMPtr<nsIDOMNode> startNode, endNode, parent, tmp;
   949   int32_t startOffset, endOffset, tmpOffset;
   951   res = inRange->GetStartContainer(getter_AddRefs(startNode));
   952   NS_ENSURE_SUCCESS(res, res);
   953   res = inRange->GetStartOffset(&startOffset);
   954   NS_ENSURE_SUCCESS(res, res);
   955   res = inRange->GetEndContainer(getter_AddRefs(endNode));
   956   NS_ENSURE_SUCCESS(res, res);
   957   res = inRange->GetEndOffset(&endOffset);
   958   NS_ENSURE_SUCCESS(res, res);
   960   tmp = startNode;
   961   while ( tmp && 
   962           !nsTextEditUtils::IsBody(tmp) &&
   963           !nsHTMLEditUtils::IsNamedAnchor(tmp))
   964   {
   965     parent = GetNodeLocation(tmp, &tmpOffset);
   966     tmp = parent;
   967   }
   968   NS_ENSURE_TRUE(tmp, NS_ERROR_NULL_POINTER);
   969   if (nsHTMLEditUtils::IsNamedAnchor(tmp))
   970   {
   971     parent = GetNodeLocation(tmp, &tmpOffset);
   972     startNode = parent;
   973     startOffset = tmpOffset;
   974   }
   976   tmp = endNode;
   977   while ( tmp && 
   978           !nsTextEditUtils::IsBody(tmp) &&
   979           !nsHTMLEditUtils::IsNamedAnchor(tmp))
   980   {
   981     parent = GetNodeLocation(tmp, &tmpOffset);
   982     tmp = parent;
   983   }
   984   NS_ENSURE_TRUE(tmp, NS_ERROR_NULL_POINTER);
   985   if (nsHTMLEditUtils::IsNamedAnchor(tmp))
   986   {
   987     parent = GetNodeLocation(tmp, &tmpOffset);
   988     endNode = parent;
   989     endOffset = tmpOffset + 1;
   990   }
   992   res = inRange->SetStart(startNode, startOffset);
   993   NS_ENSURE_SUCCESS(res, res);
   994   res = inRange->SetEnd(endNode, endOffset);
   995   return res;
   996 }
   998 nsresult nsHTMLEditor::PromoteInlineRange(nsIDOMRange *inRange)
   999 {
  1000   NS_ENSURE_TRUE(inRange, NS_ERROR_NULL_POINTER);
  1001   nsresult res;
  1002   nsCOMPtr<nsIDOMNode> startNode, endNode, parent;
  1003   int32_t startOffset, endOffset;
  1005   res = inRange->GetStartContainer(getter_AddRefs(startNode));
  1006   NS_ENSURE_SUCCESS(res, res);
  1007   res = inRange->GetStartOffset(&startOffset);
  1008   NS_ENSURE_SUCCESS(res, res);
  1009   res = inRange->GetEndContainer(getter_AddRefs(endNode));
  1010   NS_ENSURE_SUCCESS(res, res);
  1011   res = inRange->GetEndOffset(&endOffset);
  1012   NS_ENSURE_SUCCESS(res, res);
  1014   while ( startNode && 
  1015           !nsTextEditUtils::IsBody(startNode) && 
  1016           IsEditable(startNode) &&
  1017           IsAtFrontOfNode(startNode, startOffset) )
  1019     parent = GetNodeLocation(startNode, &startOffset);
  1020     startNode = parent;
  1022   NS_ENSURE_TRUE(startNode, NS_ERROR_NULL_POINTER);
  1024   while ( endNode && 
  1025           !nsTextEditUtils::IsBody(endNode) && 
  1026           IsEditable(endNode) &&
  1027           IsAtEndOfNode(endNode, endOffset) )
  1029     parent = GetNodeLocation(endNode, &endOffset);
  1030     endNode = parent;
  1031     endOffset++;  // we are AFTER this node
  1033   NS_ENSURE_TRUE(endNode, NS_ERROR_NULL_POINTER);
  1035   res = inRange->SetStart(startNode, startOffset);
  1036   NS_ENSURE_SUCCESS(res, res);
  1037   res = inRange->SetEnd(endNode, endOffset);
  1038   return res;
  1041 bool nsHTMLEditor::IsAtFrontOfNode(nsIDOMNode *aNode, int32_t aOffset)
  1043   NS_ENSURE_TRUE(aNode, false);  // oops
  1044   if (!aOffset) {
  1045     return true;
  1048   if (IsTextNode(aNode))
  1050     return false;
  1052   else
  1054     nsCOMPtr<nsIDOMNode> firstNode;
  1055     GetFirstEditableChild(aNode, address_of(firstNode));
  1056     NS_ENSURE_TRUE(firstNode, true); 
  1057     int32_t offset = GetChildOffset(firstNode, aNode);
  1058     if (offset < aOffset) return false;
  1059     return true;
  1063 bool nsHTMLEditor::IsAtEndOfNode(nsIDOMNode *aNode, int32_t aOffset)
  1065   NS_ENSURE_TRUE(aNode, false);  // oops
  1066   uint32_t len;
  1067   GetLengthOfDOMNode(aNode, len);
  1068   if (aOffset == (int32_t)len) return true;
  1070   if (IsTextNode(aNode))
  1072     return false;
  1074   else
  1076     nsCOMPtr<nsIDOMNode> lastNode;
  1077     GetLastEditableChild(aNode, address_of(lastNode));
  1078     NS_ENSURE_TRUE(lastNode, true); 
  1079     int32_t offset = GetChildOffset(lastNode, aNode);
  1080     if (offset < aOffset) return true;
  1081     return false;
  1086 nsresult
  1087 nsHTMLEditor::GetInlinePropertyBase(nsIAtom *aProperty, 
  1088                                     const nsAString *aAttribute,
  1089                                     const nsAString *aValue,
  1090                                     bool *aFirst, 
  1091                                     bool *aAny, 
  1092                                     bool *aAll,
  1093                                     nsAString *outValue,
  1094                                     bool aCheckDefaults)
  1096   NS_ENSURE_TRUE(aProperty, NS_ERROR_NULL_POINTER);
  1098   nsresult result;
  1099   *aAny = false;
  1100   *aAll = true;
  1101   *aFirst = false;
  1102   bool first = true;
  1104   nsCOMPtr<nsISelection> selection;
  1105   result = GetSelection(getter_AddRefs(selection));
  1106   NS_ENSURE_SUCCESS(result, result);
  1107   NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
  1108   Selection* sel = static_cast<Selection*>(selection.get());
  1110   bool isCollapsed = selection->Collapsed();
  1111   nsCOMPtr<nsIDOMNode> collapsedNode;
  1112   nsRefPtr<nsRange> range = sel->GetRangeAt(0);
  1113   // XXX: should be a while loop, to get each separate range
  1114   // XXX: ERROR_HANDLING can currentItem be null?
  1115   if (range) {
  1116     bool firstNodeInRange = true; // for each range, set a flag 
  1118     if (isCollapsed) {
  1119       range->GetStartContainer(getter_AddRefs(collapsedNode));
  1120       NS_ENSURE_TRUE(collapsedNode, NS_ERROR_FAILURE);
  1121       bool isSet, theSetting;
  1122       nsString tOutString;
  1123       if (aAttribute) {
  1124         nsString tString(*aAttribute);
  1125         mTypeInState->GetTypingState(isSet, theSetting, aProperty, tString,
  1126                                      &tOutString);
  1127         if (outValue) {
  1128           outValue->Assign(tOutString);
  1130       } else {
  1131         mTypeInState->GetTypingState(isSet, theSetting, aProperty);
  1133       if (isSet) {
  1134         *aFirst = *aAny = *aAll = theSetting;
  1135         return NS_OK;
  1138       if (mHTMLCSSUtils->IsCSSEditableProperty(collapsedNode, aProperty, aAttribute)) {
  1139         mHTMLCSSUtils->IsCSSEquivalentToHTMLInlineStyleSet(
  1140           collapsedNode, aProperty, aAttribute, isSet, tOutString,
  1141           nsHTMLCSSUtils::eComputed);
  1142         if (outValue) {
  1143           outValue->Assign(tOutString);
  1145         *aFirst = *aAny = *aAll = isSet;
  1146         return NS_OK;
  1149       IsTextPropertySetByContent(collapsedNode, aProperty, aAttribute, aValue,
  1150                                  isSet, outValue);
  1151       *aFirst = *aAny = *aAll = isSet;
  1153       if (!isSet && aCheckDefaults) {
  1154         // style not set, but if it is a default then it will appear if
  1155         // content is inserted, so we should report it as set (analogous to
  1156         // TypeInState).
  1157         int32_t index;
  1158         if (aAttribute && TypeInState::FindPropInList(aProperty, *aAttribute,
  1159                                                       outValue, mDefaultStyles,
  1160                                                       index)) {
  1161           *aFirst = *aAny = *aAll = true;
  1162           if (outValue) {
  1163             outValue->Assign(mDefaultStyles[index]->value);
  1167       return NS_OK;
  1170     // non-collapsed selection
  1171     nsCOMPtr<nsIContentIterator> iter =
  1172             do_CreateInstance("@mozilla.org/content/post-content-iterator;1");
  1173     NS_ENSURE_TRUE(iter, NS_ERROR_NULL_POINTER);
  1175     nsAutoString firstValue, theValue;
  1177     nsCOMPtr<nsIDOMNode> endNode;
  1178     int32_t endOffset;
  1179     result = range->GetEndContainer(getter_AddRefs(endNode));
  1180     NS_ENSURE_SUCCESS(result, result);
  1181     result = range->GetEndOffset(&endOffset);
  1182     NS_ENSURE_SUCCESS(result, result);
  1184     for (iter->Init(range); !iter->IsDone(); iter->Next()) {
  1185       if (!iter->GetCurrentNode()->IsContent()) {
  1186         continue;
  1188       nsCOMPtr<nsIContent> content = iter->GetCurrentNode()->AsContent();
  1189       nsCOMPtr<nsIDOMNode> node = content->AsDOMNode();
  1191       if (nsTextEditUtils::IsBody(node)) {
  1192         break;
  1195       nsCOMPtr<nsIDOMCharacterData> text;
  1196       text = do_QueryInterface(content);
  1198       // just ignore any non-editable nodes
  1199       if (text && (!IsEditable(text) || IsEmptyTextNode(this, content))) {
  1200         continue;
  1202       if (text) {
  1203         if (!isCollapsed && first && firstNodeInRange) {
  1204           firstNodeInRange = false;
  1205           int32_t startOffset;
  1206           range->GetStartOffset(&startOffset);
  1207           uint32_t count;
  1208           text->GetLength(&count);
  1209           if (startOffset == (int32_t)count) {
  1210             continue;
  1212         } else if (node == endNode && !endOffset) {
  1213           continue;
  1215       } else if (content->IsElement()) {
  1216         // handle non-text leaf nodes here
  1217         continue;
  1220       bool isSet = false;
  1221       if (first) {
  1222         if (mHTMLCSSUtils->IsCSSEditableProperty(node, aProperty, aAttribute)){
  1223           // the HTML styles defined by aProperty/aAttribute has a CSS
  1224           // equivalence in this implementation for node; let's check if it
  1225           // carries those css styles
  1226           if (aValue) {
  1227             firstValue.Assign(*aValue);
  1229           mHTMLCSSUtils->IsCSSEquivalentToHTMLInlineStyleSet(node, aProperty,
  1230             aAttribute, isSet, firstValue, nsHTMLCSSUtils::eComputed);
  1231         } else {
  1232           IsTextPropertySetByContent(node, aProperty, aAttribute, aValue, isSet,
  1233                                      &firstValue);
  1235         *aFirst = isSet;
  1236         first = false;
  1237         if (outValue) {
  1238           *outValue = firstValue;
  1240       } else {
  1241         if (mHTMLCSSUtils->IsCSSEditableProperty(node, aProperty, aAttribute)){
  1242           // the HTML styles defined by aProperty/aAttribute has a CSS equivalence
  1243           // in this implementation for node; let's check if it carries those css styles
  1244           if (aValue) {
  1245             theValue.Assign(*aValue);
  1247           mHTMLCSSUtils->IsCSSEquivalentToHTMLInlineStyleSet(node, aProperty,
  1248             aAttribute, isSet, theValue, nsHTMLCSSUtils::eComputed);
  1249         } else {
  1250           IsTextPropertySetByContent(node, aProperty, aAttribute, aValue, isSet,
  1251                                      &theValue);
  1253         if (firstValue != theValue) {
  1254           *aAll = false;
  1258       if (isSet) {
  1259         *aAny = true;
  1260       } else {
  1261         *aAll = false;
  1265   if (!*aAny) {
  1266     // make sure that if none of the selection is set, we don't report all is
  1267     // set
  1268     *aAll = false;
  1270   return result;
  1274 NS_IMETHODIMP nsHTMLEditor::GetInlineProperty(nsIAtom *aProperty, 
  1275                                               const nsAString &aAttribute, 
  1276                                               const nsAString &aValue,
  1277                                               bool *aFirst, 
  1278                                               bool *aAny, 
  1279                                               bool *aAll)
  1281   NS_ENSURE_TRUE(aProperty && aFirst && aAny && aAll, NS_ERROR_NULL_POINTER);
  1282   const nsAString *att = nullptr;
  1283   if (!aAttribute.IsEmpty())
  1284     att = &aAttribute;
  1285   const nsAString *val = nullptr;
  1286   if (!aValue.IsEmpty())
  1287     val = &aValue;
  1288   return GetInlinePropertyBase( aProperty, att, val, aFirst, aAny, aAll, nullptr);
  1292 NS_IMETHODIMP nsHTMLEditor::GetInlinePropertyWithAttrValue(nsIAtom *aProperty, 
  1293                                               const nsAString &aAttribute, 
  1294                                               const nsAString &aValue,
  1295                                               bool *aFirst, 
  1296                                               bool *aAny, 
  1297                                               bool *aAll,
  1298                                               nsAString &outValue)
  1300   NS_ENSURE_TRUE(aProperty && aFirst && aAny && aAll, NS_ERROR_NULL_POINTER);
  1301   const nsAString *att = nullptr;
  1302   if (!aAttribute.IsEmpty())
  1303     att = &aAttribute;
  1304   const nsAString *val = nullptr;
  1305   if (!aValue.IsEmpty())
  1306     val = &aValue;
  1307   return GetInlinePropertyBase( aProperty, att, val, aFirst, aAny, aAll, &outValue);
  1311 NS_IMETHODIMP nsHTMLEditor::RemoveAllInlineProperties()
  1313   nsAutoEditBatch batchIt(this);
  1314   nsAutoRules beginRulesSniffing(this, EditAction::resetTextProperties, nsIEditor::eNext);
  1316   nsresult res = RemoveInlinePropertyImpl(nullptr, nullptr);
  1317   NS_ENSURE_SUCCESS(res, res);
  1318   return ApplyDefaultProperties();
  1321 NS_IMETHODIMP nsHTMLEditor::RemoveInlineProperty(nsIAtom *aProperty, const nsAString &aAttribute)
  1323   return RemoveInlinePropertyImpl(aProperty, &aAttribute);
  1326 nsresult nsHTMLEditor::RemoveInlinePropertyImpl(nsIAtom *aProperty, const nsAString *aAttribute)
  1328   MOZ_ASSERT_IF(aProperty, aAttribute);
  1329   NS_ENSURE_TRUE(mRules, NS_ERROR_NOT_INITIALIZED);
  1330   ForceCompositionEnd();
  1332   nsresult res;
  1333   nsRefPtr<Selection> selection = GetSelection();
  1334   NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
  1336   bool useCSS = IsCSSEnabled();
  1337   if (selection->Collapsed()) {
  1338     // manipulating text attributes on a collapsed selection only sets state for the next text insertion
  1340     // For links, aProperty uses "href", use "a" instead
  1341     if (aProperty == nsEditProperty::href ||
  1342         aProperty == nsEditProperty::name)
  1343       aProperty = nsEditProperty::a;
  1345     if (aProperty) {
  1346       mTypeInState->ClearProp(aProperty, *aAttribute);
  1347     } else {
  1348       mTypeInState->ClearAllProps();
  1350     return NS_OK;
  1353   nsAutoEditBatch batchIt(this);
  1354   nsAutoRules beginRulesSniffing(this, EditAction::removeTextProperty, nsIEditor::eNext);
  1355   nsAutoSelectionReset selectionResetter(selection, this);
  1356   nsAutoTxnsConserveSelection dontSpazMySelection(this);
  1358   bool cancel, handled;
  1359   nsTextRulesInfo ruleInfo(EditAction::removeTextProperty);
  1360   // Protect the edit rules object from dying
  1361   nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules);
  1362   res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
  1363   NS_ENSURE_SUCCESS(res, res);
  1364   if (!cancel && !handled)
  1366     // loop thru the ranges in the selection
  1367     uint32_t rangeCount = selection->GetRangeCount();
  1368     for (uint32_t rangeIdx = 0; rangeIdx < rangeCount; ++rangeIdx) {
  1369       nsRefPtr<nsRange> range = selection->GetRangeAt(rangeIdx);
  1370       if (aProperty == nsEditProperty::name)
  1372         // promote range if it starts or end in a named anchor and we
  1373         // want to remove named anchors
  1374         res = PromoteRangeIfStartsOrEndsInNamedAnchor(range);
  1376       else {
  1377         // adjust range to include any ancestors who's children are entirely selected
  1378         res = PromoteInlineRange(range);
  1380       NS_ENSURE_SUCCESS(res, res);
  1382       // remove this style from ancestors of our range endpoints, 
  1383       // splitting them as appropriate
  1384       res = SplitStyleAboveRange(range, aProperty, aAttribute);
  1385       NS_ENSURE_SUCCESS(res, res);
  1387       // check for easy case: both range endpoints in same text node
  1388       nsCOMPtr<nsIDOMNode> startNode, endNode;
  1389       res = range->GetStartContainer(getter_AddRefs(startNode));
  1390       NS_ENSURE_SUCCESS(res, res);
  1391       res = range->GetEndContainer(getter_AddRefs(endNode));
  1392       NS_ENSURE_SUCCESS(res, res);
  1393       if ((startNode == endNode) && IsTextNode(startNode))
  1395         // we're done with this range!
  1396         if (useCSS && mHTMLCSSUtils->IsCSSEditableProperty(startNode, aProperty, aAttribute)) {
  1397           // the HTML style defined by aProperty/aAttribute has a CSS equivalence
  1398           // in this implementation for startNode
  1399           nsAutoString cssValue;
  1400           bool isSet = false;
  1401           mHTMLCSSUtils->IsCSSEquivalentToHTMLInlineStyleSet(startNode,
  1402             aProperty, aAttribute, isSet , cssValue,
  1403             nsHTMLCSSUtils::eComputed);
  1404           if (isSet) {
  1405             // startNode's computed style indicates the CSS equivalence to the HTML style to
  1406             // remove is applied; but we found no element in the ancestors of startNode
  1407             // carrying specified styles; assume it comes from a rule and let's try to
  1408             // insert a span "inverting" the style
  1409             nsAutoString value; value.AssignLiteral("-moz-editor-invert-value");
  1410             int32_t startOffset, endOffset;
  1411             range->GetStartOffset(&startOffset);
  1412             range->GetEndOffset(&endOffset);
  1413             nsCOMPtr<nsIDOMCharacterData> nodeAsText = do_QueryInterface(startNode);
  1414             if (mHTMLCSSUtils->IsCSSInvertable(aProperty, aAttribute)) {
  1415               SetInlinePropertyOnTextNode(nodeAsText, startOffset, endOffset, aProperty, aAttribute, &value);
  1420       else
  1422         // not the easy case.  range not contained in single text node. 
  1423         nsCOMPtr<nsIContentIterator> iter =
  1424           do_CreateInstance("@mozilla.org/content/subtree-content-iterator;1", &res);
  1425         NS_ENSURE_SUCCESS(res, res);
  1426         NS_ENSURE_TRUE(iter, NS_ERROR_FAILURE);
  1428         nsCOMArray<nsIDOMNode> arrayOfNodes;
  1429         nsCOMPtr<nsIDOMNode> node;
  1431         // iterate range and build up array
  1432         iter->Init(range);
  1433         while (!iter->IsDone())
  1435           node = do_QueryInterface(iter->GetCurrentNode());
  1436           NS_ENSURE_TRUE(node, NS_ERROR_FAILURE);
  1438           if (IsEditable(node))
  1440             arrayOfNodes.AppendObject(node);
  1443           iter->Next();
  1446         // loop through the list, remove the property on each node
  1447         int32_t listCount = arrayOfNodes.Count();
  1448         int32_t j;
  1449         for (j = 0; j < listCount; j++)
  1451           node = arrayOfNodes[j];
  1452           res = RemoveStyleInside(node, aProperty, aAttribute);
  1453           NS_ENSURE_SUCCESS(res, res);
  1454           if (useCSS && mHTMLCSSUtils->IsCSSEditableProperty(node, aProperty, aAttribute)) {
  1455             // the HTML style defined by aProperty/aAttribute has a CSS equivalence
  1456             // in this implementation for node
  1457             nsAutoString cssValue;
  1458             bool isSet = false;
  1459             mHTMLCSSUtils->IsCSSEquivalentToHTMLInlineStyleSet(node, aProperty,
  1460               aAttribute, isSet , cssValue, nsHTMLCSSUtils::eComputed);
  1461             if (isSet) {
  1462               // startNode's computed style indicates the CSS equivalence to the HTML style to
  1463               // remove is applied; but we found no element in the ancestors of startNode
  1464               // carrying specified styles; assume it comes from a rule and let's try to
  1465               // insert a span "inverting" the style
  1466               if (mHTMLCSSUtils->IsCSSInvertable(aProperty, aAttribute)) {
  1467                 nsAutoString value; value.AssignLiteral("-moz-editor-invert-value");
  1468                 SetInlinePropertyOnNode(node, aProperty, aAttribute, &value);
  1473         arrayOfNodes.Clear();
  1477   if (!cancel)
  1479     // post-process 
  1480     res = mRules->DidDoAction(selection, &ruleInfo, res);
  1482   return res;
  1485 NS_IMETHODIMP nsHTMLEditor::IncreaseFontSize()
  1487   return RelativeFontChange(1);
  1490 NS_IMETHODIMP nsHTMLEditor::DecreaseFontSize()
  1492   return RelativeFontChange(-1);
  1495 nsresult
  1496 nsHTMLEditor::RelativeFontChange( int32_t aSizeChange)
  1498   // Can only change font size by + or - 1
  1499   if ( !( (aSizeChange==1) || (aSizeChange==-1) ) )
  1500     return NS_ERROR_ILLEGAL_VALUE;
  1502   ForceCompositionEnd();
  1504   // Get the selection 
  1505   nsRefPtr<Selection> selection = GetSelection();
  1506   NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
  1507   // Is the selection collapsed?
  1508   // if it's collapsed set typing state
  1509   if (selection->Collapsed()) {
  1510     nsCOMPtr<nsIAtom> atom;
  1511     if (aSizeChange==1) atom = nsEditProperty::big;
  1512     else                atom = nsEditProperty::small;
  1514     // Let's see in what kind of element the selection is
  1515     int32_t offset;
  1516     nsCOMPtr<nsIDOMNode> selectedNode;
  1517     GetStartNodeAndOffset(selection, getter_AddRefs(selectedNode), &offset);
  1518     NS_ENSURE_TRUE(selectedNode, NS_OK);
  1519     if (IsTextNode(selectedNode)) {
  1520       nsCOMPtr<nsIDOMNode> parent;
  1521       nsresult res = selectedNode->GetParentNode(getter_AddRefs(parent));
  1522       NS_ENSURE_SUCCESS(res, res);
  1523       selectedNode = parent;
  1525     if (!CanContainTag(selectedNode, atom)) {
  1526       return NS_OK;
  1529     // manipulating text attributes on a collapsed selection only sets state for the next text insertion
  1530     mTypeInState->SetProp(atom, EmptyString(), EmptyString());
  1531     return NS_OK;
  1534   // wrap with txn batching, rules sniffing, and selection preservation code
  1535   nsAutoEditBatch batchIt(this);
  1536   nsAutoRules beginRulesSniffing(this, EditAction::setTextProperty, nsIEditor::eNext);
  1537   nsAutoSelectionReset selectionResetter(selection, this);
  1538   nsAutoTxnsConserveSelection dontSpazMySelection(this);
  1540   // loop thru the ranges in the selection
  1541   uint32_t rangeCount = selection->GetRangeCount();
  1542   for (uint32_t rangeIdx = 0; rangeIdx < rangeCount; ++rangeIdx) {
  1543     nsRefPtr<nsRange> range = selection->GetRangeAt(rangeIdx);
  1545     // adjust range to include any ancestors who's children are entirely selected
  1546     nsresult res = PromoteInlineRange(range);
  1547     NS_ENSURE_SUCCESS(res, res);
  1549     // check for easy case: both range endpoints in same text node
  1550     nsCOMPtr<nsIDOMNode> startNode, endNode;
  1551     res = range->GetStartContainer(getter_AddRefs(startNode));
  1552     NS_ENSURE_SUCCESS(res, res);
  1553     res = range->GetEndContainer(getter_AddRefs(endNode));
  1554     NS_ENSURE_SUCCESS(res, res);
  1555     if ((startNode == endNode) && IsTextNode(startNode))
  1557       int32_t startOffset, endOffset;
  1558       range->GetStartOffset(&startOffset);
  1559       range->GetEndOffset(&endOffset);
  1560       nsCOMPtr<nsIDOMCharacterData> nodeAsText = do_QueryInterface(startNode);
  1561       res = RelativeFontChangeOnTextNode(aSizeChange, nodeAsText, startOffset, endOffset);
  1562       NS_ENSURE_SUCCESS(res, res);
  1564     else
  1566       // not the easy case.  range not contained in single text node. 
  1567       // there are up to three phases here.  There are all the nodes
  1568       // reported by the subtree iterator to be processed.  And there
  1569       // are potentially a starting textnode and an ending textnode
  1570       // which are only partially contained by the range.
  1572       // lets handle the nodes reported by the iterator.  These nodes
  1573       // are entirely contained in the selection range.  We build up
  1574       // a list of them (since doing operations on the document during
  1575       // iteration would perturb the iterator).
  1577       nsCOMPtr<nsIContentIterator> iter =
  1578         do_CreateInstance("@mozilla.org/content/subtree-content-iterator;1", &res);
  1579       NS_ENSURE_SUCCESS(res, res);
  1580       NS_ENSURE_TRUE(iter, NS_ERROR_FAILURE);
  1582       // iterate range and build up array
  1583       res = iter->Init(range);
  1584       if (NS_SUCCEEDED(res)) {
  1585         nsCOMArray<nsIContent> arrayOfNodes;
  1586         while (!iter->IsDone()) {
  1587           NS_ENSURE_TRUE(iter->GetCurrentNode()->IsContent(), NS_ERROR_FAILURE);
  1588           nsCOMPtr<nsIContent> node = iter->GetCurrentNode()->AsContent();
  1590           if (IsEditable(node)) {
  1591             arrayOfNodes.AppendObject(node);
  1594           iter->Next();
  1597         // now that we have the list, do the font size change on each node
  1598         int32_t listCount = arrayOfNodes.Count();
  1599         for (int32_t j = 0; j < listCount; ++j) {
  1600           nsIContent* node = arrayOfNodes[j];
  1601           res = RelativeFontChangeOnNode(aSizeChange, node);
  1602           NS_ENSURE_SUCCESS(res, res);
  1604         arrayOfNodes.Clear();
  1606       // now check the start and end parents of the range to see if they need to 
  1607       // be separately handled (they do if they are text nodes, due to how the
  1608       // subtree iterator works - it will not have reported them).
  1609       if (IsTextNode(startNode) && IsEditable(startNode))
  1611         nsCOMPtr<nsIDOMCharacterData> nodeAsText = do_QueryInterface(startNode);
  1612         int32_t startOffset;
  1613         uint32_t textLen;
  1614         range->GetStartOffset(&startOffset);
  1615         nodeAsText->GetLength(&textLen);
  1616         res = RelativeFontChangeOnTextNode(aSizeChange, nodeAsText, startOffset, textLen);
  1617         NS_ENSURE_SUCCESS(res, res);
  1619       if (IsTextNode(endNode) && IsEditable(endNode))
  1621         nsCOMPtr<nsIDOMCharacterData> nodeAsText = do_QueryInterface(endNode);
  1622         int32_t endOffset;
  1623         range->GetEndOffset(&endOffset);
  1624         res = RelativeFontChangeOnTextNode(aSizeChange, nodeAsText, 0, endOffset);
  1625         NS_ENSURE_SUCCESS(res, res);
  1630   return NS_OK;  
  1633 nsresult
  1634 nsHTMLEditor::RelativeFontChangeOnTextNode( int32_t aSizeChange, 
  1635                                             nsIDOMCharacterData *aTextNode, 
  1636                                             int32_t aStartOffset,
  1637                                             int32_t aEndOffset)
  1639   // Can only change font size by + or - 1
  1640   if ( !( (aSizeChange==1) || (aSizeChange==-1) ) )
  1641     return NS_ERROR_ILLEGAL_VALUE;
  1642   NS_ENSURE_TRUE(aTextNode, NS_ERROR_NULL_POINTER);
  1644   // don't need to do anything if no characters actually selected
  1645   if (aStartOffset == aEndOffset) return NS_OK;
  1647   nsresult res = NS_OK;
  1648   nsCOMPtr<nsIDOMNode> parent;
  1649   res = aTextNode->GetParentNode(getter_AddRefs(parent));
  1650   NS_ENSURE_SUCCESS(res, res);
  1651   if (!CanContainTag(parent, nsGkAtoms::big)) {
  1652     return NS_OK;
  1655   nsCOMPtr<nsIDOMNode> tmp, node = do_QueryInterface(aTextNode);
  1657   // do we need to split the text node?
  1658   uint32_t textLen;
  1659   aTextNode->GetLength(&textLen);
  1661   // -1 is a magic value meaning to the end of node
  1662   if (aEndOffset == -1) aEndOffset = textLen;
  1664   if ( (uint32_t)aEndOffset != textLen )
  1666     // we need to split off back of text node
  1667     res = SplitNode(node, aEndOffset, getter_AddRefs(tmp));
  1668     NS_ENSURE_SUCCESS(res, res);
  1669     node = tmp;  // remember left node
  1671   if ( aStartOffset )
  1673     // we need to split off front of text node
  1674     res = SplitNode(node, aStartOffset, getter_AddRefs(tmp));
  1675     NS_ENSURE_SUCCESS(res, res);
  1678   NS_NAMED_LITERAL_STRING(bigSize, "big");
  1679   NS_NAMED_LITERAL_STRING(smallSize, "small");
  1680   const nsAString& nodeType = (aSizeChange==1) ? static_cast<const nsAString&>(bigSize) : static_cast<const nsAString&>(smallSize);
  1681   // look for siblings that are correct type of node
  1682   nsCOMPtr<nsIDOMNode> sibling;
  1683   GetPriorHTMLSibling(node, address_of(sibling));
  1684   if (sibling && NodeIsType(sibling, (aSizeChange==1) ? nsEditProperty::big : nsEditProperty::small))
  1686     // previous sib is already right kind of inline node; slide this over into it
  1687     res = MoveNode(node, sibling, -1);
  1688     return res;
  1690   sibling = nullptr;
  1691   GetNextHTMLSibling(node, address_of(sibling));
  1692   if (sibling && NodeIsType(sibling, (aSizeChange==1) ? nsEditProperty::big : nsEditProperty::small))
  1694     // following sib is already right kind of inline node; slide this over into it
  1695     res = MoveNode(node, sibling, 0);
  1696     return res;
  1699   // else reparent the node inside font node with appropriate relative size
  1700   res = InsertContainerAbove(node, address_of(tmp), nodeType);
  1701   return res;
  1705 nsresult
  1706 nsHTMLEditor::RelativeFontChangeHelper(int32_t aSizeChange, nsINode* aNode)
  1708   MOZ_ASSERT(aNode);
  1710   /*  This routine looks for all the font nodes in the tree rooted by aNode,
  1711       including aNode itself, looking for font nodes that have the size attr
  1712       set.  Any such nodes need to have big or small put inside them, since
  1713       they override any big/small that are above them.
  1714   */
  1716   // Can only change font size by + or - 1
  1717   if (aSizeChange != 1 && aSizeChange != -1) {
  1718     return NS_ERROR_ILLEGAL_VALUE;
  1721   // If this is a font node with size, put big/small inside it.
  1722   if (aNode->IsElement() && aNode->AsElement()->IsHTML(nsGkAtoms::font) &&
  1723       aNode->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::size)) {
  1724     // Cycle through children and adjust relative font size.
  1725     for (uint32_t i = aNode->GetChildCount(); i--; ) {
  1726       nsresult rv = RelativeFontChangeOnNode(aSizeChange, aNode->GetChildAt(i));
  1727       NS_ENSURE_SUCCESS(rv, rv);
  1730     // RelativeFontChangeOnNode already calls us recursively,
  1731     // so we don't need to check our children again.
  1732     return NS_OK;
  1735   // Otherwise cycle through the children.
  1736   for (uint32_t i = aNode->GetChildCount(); i--; ) {
  1737     nsresult rv = RelativeFontChangeHelper(aSizeChange, aNode->GetChildAt(i));
  1738     NS_ENSURE_SUCCESS(rv, rv);
  1741   return NS_OK;
  1745 nsresult
  1746 nsHTMLEditor::RelativeFontChangeOnNode(int32_t aSizeChange, nsINode* aNode)
  1748   MOZ_ASSERT(aNode);
  1749   // Can only change font size by + or - 1
  1750   if (aSizeChange != 1 && aSizeChange != -1) {
  1751     return NS_ERROR_ILLEGAL_VALUE;
  1754   nsIAtom* atom;
  1755   if (aSizeChange == 1) {
  1756     atom = nsGkAtoms::big;
  1757   } else {
  1758     atom = nsGkAtoms::small;
  1761   // Is it the opposite of what we want?
  1762   if (aNode->IsElement() &&
  1763       ((aSizeChange == 1 && aNode->AsElement()->IsHTML(nsGkAtoms::small)) ||
  1764        (aSizeChange == -1 && aNode->AsElement()->IsHTML(nsGkAtoms::big)))) {
  1765     // first populate any nested font tags that have the size attr set
  1766     nsresult rv = RelativeFontChangeHelper(aSizeChange, aNode);
  1767     NS_ENSURE_SUCCESS(rv, rv);
  1768     // in that case, just remove this node and pull up the children
  1769     return RemoveContainer(aNode);
  1772   // can it be put inside a "big" or "small"?
  1773   if (TagCanContain(atom, aNode->AsDOMNode())) {
  1774     // first populate any nested font tags that have the size attr set
  1775     nsresult rv = RelativeFontChangeHelper(aSizeChange, aNode);
  1776     NS_ENSURE_SUCCESS(rv, rv);
  1778     // ok, chuck it in.
  1779     // first look at siblings of aNode for matching bigs or smalls.
  1780     // if we find one, move aNode into it.
  1781     nsIContent* sibling = GetPriorHTMLSibling(aNode);
  1782     if (sibling && sibling->IsHTML(atom)) {
  1783       // previous sib is already right kind of inline node; slide this over into it
  1784       return MoveNode(aNode->AsDOMNode(), sibling->AsDOMNode(), -1);
  1787     sibling = GetNextHTMLSibling(aNode);
  1788     if (sibling && sibling->IsHTML(atom)) {
  1789       // following sib is already right kind of inline node; slide this over into it
  1790       return MoveNode(aNode->AsDOMNode(), sibling->AsDOMNode(), 0);
  1793     // else insert it above aNode
  1794     nsCOMPtr<nsIDOMNode> tmp;
  1795     return InsertContainerAbove(aNode->AsDOMNode(), address_of(tmp),
  1796                                 nsAtomString(atom));
  1799   // none of the above?  then cycle through the children.
  1800   // MOOSE: we should group the children together if possible
  1801   // into a single "big" or "small".  For the moment they are
  1802   // each getting their own.  
  1803   for (uint32_t i = aNode->GetChildCount(); i--; ) {
  1804     nsresult rv = RelativeFontChangeOnNode(aSizeChange, aNode->GetChildAt(i));
  1805     NS_ENSURE_SUCCESS(rv, rv);
  1808   return NS_OK;
  1811 NS_IMETHODIMP 
  1812 nsHTMLEditor::GetFontFaceState(bool *aMixed, nsAString &outFace)
  1814   NS_ENSURE_TRUE(aMixed, NS_ERROR_FAILURE);
  1815   *aMixed = true;
  1816   outFace.Truncate();
  1818   nsresult res;
  1819   bool first, any, all;
  1821   NS_NAMED_LITERAL_STRING(attr, "face");
  1822   res = GetInlinePropertyBase(nsEditProperty::font, &attr, nullptr, &first, &any, &all, &outFace);
  1823   NS_ENSURE_SUCCESS(res, res);
  1824   if (any && !all) return res; // mixed
  1825   if (all)
  1827     *aMixed = false;
  1828     return res;
  1831   // if there is no font face, check for tt
  1832   res = GetInlinePropertyBase(nsEditProperty::tt, nullptr, nullptr, &first, &any, &all,nullptr);
  1833   NS_ENSURE_SUCCESS(res, res);
  1834   if (any && !all) return res; // mixed
  1835   if (all)
  1837     *aMixed = false;
  1838     nsEditProperty::tt->ToString(outFace);
  1841   if (!any)
  1843     // there was no font face attrs of any kind.  We are in normal font.
  1844     outFace.Truncate();
  1845     *aMixed = false;
  1847   return res;
  1850 NS_IMETHODIMP 
  1851 nsHTMLEditor::GetFontColorState(bool *aMixed, nsAString &aOutColor)
  1853   NS_ENSURE_TRUE(aMixed, NS_ERROR_NULL_POINTER);
  1854   *aMixed = true;
  1855   aOutColor.Truncate();
  1857   nsresult res;
  1858   NS_NAMED_LITERAL_STRING(colorStr, "color");
  1859   bool first, any, all;
  1861   res = GetInlinePropertyBase(nsEditProperty::font, &colorStr, nullptr, &first, &any, &all, &aOutColor);
  1862   NS_ENSURE_SUCCESS(res, res);
  1863   if (any && !all) return res; // mixed
  1864   if (all)
  1866     *aMixed = false;
  1867     return res;
  1870   if (!any)
  1872     // there was no font color attrs of any kind..
  1873     aOutColor.Truncate();
  1874     *aMixed = false;
  1876   return res;
  1879 // the return value is true only if the instance of the HTML editor we created
  1880 // can handle CSS styles (for instance, Composer can, Messenger can't) and if
  1881 // the CSS preference is checked
  1882 nsresult
  1883 nsHTMLEditor::GetIsCSSEnabled(bool *aIsCSSEnabled)
  1885   *aIsCSSEnabled = IsCSSEnabled();
  1886   return NS_OK;
  1889 static bool
  1890 HasNonEmptyAttribute(dom::Element* aElement, nsIAtom* aName)
  1892   MOZ_ASSERT(aElement);
  1894   nsAutoString value;
  1895   return aElement->GetAttr(kNameSpaceID_None, aName, value) && !value.IsEmpty();
  1898 bool
  1899 nsHTMLEditor::HasStyleOrIdOrClass(dom::Element* aElement)
  1901   MOZ_ASSERT(aElement);
  1903   // remove the node if its style attribute is empty or absent,
  1904   // and if it does not have a class nor an id
  1905   return HasNonEmptyAttribute(aElement, nsGkAtoms::style) ||
  1906          HasNonEmptyAttribute(aElement, nsGkAtoms::_class) ||
  1907          HasNonEmptyAttribute(aElement, nsGkAtoms::id);
  1910 nsresult
  1911 nsHTMLEditor::RemoveElementIfNoStyleOrIdOrClass(nsIDOMNode* aElement)
  1913   nsCOMPtr<dom::Element> element = do_QueryInterface(aElement);
  1914   NS_ENSURE_TRUE(element, NS_ERROR_NULL_POINTER);
  1916   // early way out if node is not the right kind of element
  1917   if ((!element->IsHTML(nsGkAtoms::span) &&
  1918        !element->IsHTML(nsGkAtoms::font)) ||
  1919       HasStyleOrIdOrClass(element)) {
  1920     return NS_OK;
  1923   return RemoveContainer(element);

mercurial