editor/libeditor/html/nsHTMLEditorStyle.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/editor/libeditor/html/nsHTMLEditorStyle.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1924 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +#include "TypeInState.h"
     1.9 +#include "mozilla/Assertions.h"
    1.10 +#include "mozilla/dom/Selection.h"
    1.11 +#include "mozilla/dom/Element.h"
    1.12 +#include "mozilla/mozalloc.h"
    1.13 +#include "nsAString.h"
    1.14 +#include "nsAttrName.h"
    1.15 +#include "nsAutoPtr.h"
    1.16 +#include "nsCOMArray.h"
    1.17 +#include "nsCOMPtr.h"
    1.18 +#include "nsCaseTreatment.h"
    1.19 +#include "nsComponentManagerUtils.h"
    1.20 +#include "nsDebug.h"
    1.21 +#include "nsEditProperty.h"
    1.22 +#include "nsEditRules.h"
    1.23 +#include "nsEditor.h"
    1.24 +#include "nsEditorUtils.h"
    1.25 +#include "nsError.h"
    1.26 +#include "nsGkAtoms.h"
    1.27 +#include "nsHTMLCSSUtils.h"
    1.28 +#include "nsHTMLEditUtils.h"
    1.29 +#include "nsHTMLEditor.h"
    1.30 +#include "nsIAtom.h"
    1.31 +#include "nsIContent.h"
    1.32 +#include "nsIContentIterator.h"
    1.33 +#include "nsIDOMCharacterData.h"
    1.34 +#include "nsIDOMElement.h"
    1.35 +#include "nsIDOMNode.h"
    1.36 +#include "nsIDOMRange.h"
    1.37 +#include "nsIEditor.h"
    1.38 +#include "nsIEditorIMESupport.h"
    1.39 +#include "nsNameSpaceManager.h"
    1.40 +#include "nsINode.h"
    1.41 +#include "nsISelection.h"
    1.42 +#include "nsISelectionPrivate.h"
    1.43 +#include "nsISupportsImpl.h"
    1.44 +#include "nsLiteralString.h"
    1.45 +#include "nsReadableUtils.h"
    1.46 +#include "nsSelectionState.h"
    1.47 +#include "nsString.h"
    1.48 +#include "nsStringFwd.h"
    1.49 +#include "nsTArray.h"
    1.50 +#include "nsTextEditRules.h"
    1.51 +#include "nsTextEditUtils.h"
    1.52 +#include "nsUnicharUtils.h"
    1.53 +#include "nscore.h"
    1.54 +
    1.55 +class nsISupports;
    1.56 +
    1.57 +using namespace mozilla;
    1.58 +using namespace mozilla::dom;
    1.59 +
    1.60 +static bool
    1.61 +IsEmptyTextNode(nsHTMLEditor* aThis, nsINode* aNode)
    1.62 +{
    1.63 +  bool isEmptyTextNode = false;
    1.64 +  return nsEditor::IsTextNode(aNode) &&
    1.65 +         NS_SUCCEEDED(aThis->IsEmptyNode(aNode, &isEmptyTextNode)) &&
    1.66 +         isEmptyTextNode;
    1.67 +}
    1.68 +
    1.69 +NS_IMETHODIMP nsHTMLEditor::AddDefaultProperty(nsIAtom *aProperty, 
    1.70 +                                            const nsAString & aAttribute, 
    1.71 +                                            const nsAString & aValue)
    1.72 +{
    1.73 +  nsString outValue;
    1.74 +  int32_t index;
    1.75 +  nsString attr(aAttribute);
    1.76 +  if (TypeInState::FindPropInList(aProperty, attr, &outValue, mDefaultStyles, index))
    1.77 +  {
    1.78 +    PropItem *item = mDefaultStyles[index];
    1.79 +    item->value = aValue;
    1.80 +  }
    1.81 +  else
    1.82 +  {
    1.83 +    nsString value(aValue);
    1.84 +    PropItem *propItem = new PropItem(aProperty, attr, value);
    1.85 +    mDefaultStyles.AppendElement(propItem);
    1.86 +  }
    1.87 +  return NS_OK;
    1.88 +}
    1.89 +
    1.90 +NS_IMETHODIMP nsHTMLEditor::RemoveDefaultProperty(nsIAtom *aProperty, 
    1.91 +                                   const nsAString & aAttribute, 
    1.92 +                                   const nsAString & aValue)
    1.93 +{
    1.94 +  nsString outValue;
    1.95 +  int32_t index;
    1.96 +  nsString attr(aAttribute);
    1.97 +  if (TypeInState::FindPropInList(aProperty, attr, &outValue, mDefaultStyles, index))
    1.98 +  {
    1.99 +    delete mDefaultStyles[index];
   1.100 +    mDefaultStyles.RemoveElementAt(index);
   1.101 +  }
   1.102 +  return NS_OK;
   1.103 +}
   1.104 +
   1.105 +NS_IMETHODIMP nsHTMLEditor::RemoveAllDefaultProperties()
   1.106 +{
   1.107 +  uint32_t j, defcon = mDefaultStyles.Length();
   1.108 +  for (j=0; j<defcon; j++)
   1.109 +  {
   1.110 +    delete mDefaultStyles[j];
   1.111 +  }
   1.112 +  mDefaultStyles.Clear();
   1.113 +  return NS_OK;
   1.114 +}
   1.115 +
   1.116 +
   1.117 +NS_IMETHODIMP
   1.118 +nsHTMLEditor::SetInlineProperty(nsIAtom *aProperty,
   1.119 +                                const nsAString& aAttribute,
   1.120 +                                const nsAString& aValue)
   1.121 +{
   1.122 +  if (!aProperty) {
   1.123 +    return NS_ERROR_NULL_POINTER;
   1.124 +  }
   1.125 +  if (!mRules) {
   1.126 +    return NS_ERROR_NOT_INITIALIZED;
   1.127 +  }
   1.128 +  ForceCompositionEnd();
   1.129 +
   1.130 +  nsRefPtr<Selection> selection = GetSelection();
   1.131 +  NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
   1.132 +
   1.133 +  if (selection->Collapsed()) {
   1.134 +    // manipulating text attributes on a collapsed selection only sets state
   1.135 +    // for the next text insertion
   1.136 +    mTypeInState->SetProp(aProperty, aAttribute, aValue);
   1.137 +    return NS_OK;
   1.138 +  }
   1.139 +
   1.140 +  nsAutoEditBatch batchIt(this);
   1.141 +  nsAutoRules beginRulesSniffing(this, EditAction::insertElement, nsIEditor::eNext);
   1.142 +  nsAutoSelectionReset selectionResetter(selection, this);
   1.143 +  nsAutoTxnsConserveSelection dontSpazMySelection(this);
   1.144 +
   1.145 +  bool cancel, handled;
   1.146 +  nsTextRulesInfo ruleInfo(EditAction::setTextProperty);
   1.147 +  // Protect the edit rules object from dying
   1.148 +  nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules);
   1.149 +  nsresult res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
   1.150 +  NS_ENSURE_SUCCESS(res, res);
   1.151 +  if (!cancel && !handled) {
   1.152 +    // loop thru the ranges in the selection
   1.153 +    uint32_t rangeCount = selection->GetRangeCount();
   1.154 +    for (uint32_t rangeIdx = 0; rangeIdx < rangeCount; ++rangeIdx) {
   1.155 +      nsRefPtr<nsRange> range = selection->GetRangeAt(rangeIdx);
   1.156 +
   1.157 +      // adjust range to include any ancestors whose children are entirely
   1.158 +      // selected
   1.159 +      res = PromoteInlineRange(range);
   1.160 +      NS_ENSURE_SUCCESS(res, res);
   1.161 +
   1.162 +      // check for easy case: both range endpoints in same text node
   1.163 +      nsCOMPtr<nsIDOMNode> startNode, endNode;
   1.164 +      res = range->GetStartContainer(getter_AddRefs(startNode));
   1.165 +      NS_ENSURE_SUCCESS(res, res);
   1.166 +      res = range->GetEndContainer(getter_AddRefs(endNode));
   1.167 +      NS_ENSURE_SUCCESS(res, res);
   1.168 +      if (startNode == endNode && IsTextNode(startNode)) {
   1.169 +        int32_t startOffset, endOffset;
   1.170 +        range->GetStartOffset(&startOffset);
   1.171 +        range->GetEndOffset(&endOffset);
   1.172 +        nsCOMPtr<nsIDOMCharacterData> nodeAsText = do_QueryInterface(startNode);
   1.173 +        res = SetInlinePropertyOnTextNode(nodeAsText, startOffset, endOffset,
   1.174 +                                          aProperty, &aAttribute, &aValue);
   1.175 +        NS_ENSURE_SUCCESS(res, res);
   1.176 +        continue;
   1.177 +      }
   1.178 +
   1.179 +      // Not the easy case.  Range not contained in single text node.  There
   1.180 +      // are up to three phases here.  There are all the nodes reported by the
   1.181 +      // subtree iterator to be processed.  And there are potentially a
   1.182 +      // starting textnode and an ending textnode which are only partially
   1.183 +      // contained by the range.
   1.184 +
   1.185 +      // Let's handle the nodes reported by the iterator.  These nodes are
   1.186 +      // entirely contained in the selection range.  We build up a list of them
   1.187 +      // (since doing operations on the document during iteration would perturb
   1.188 +      // the iterator).
   1.189 +
   1.190 +      nsCOMPtr<nsIContentIterator> iter =
   1.191 +        do_CreateInstance("@mozilla.org/content/subtree-content-iterator;1", &res);
   1.192 +      NS_ENSURE_SUCCESS(res, res);
   1.193 +      NS_ENSURE_TRUE(iter, NS_ERROR_FAILURE);
   1.194 +
   1.195 +      nsCOMArray<nsIDOMNode> arrayOfNodes;
   1.196 +
   1.197 +      // iterate range and build up array
   1.198 +      res = iter->Init(range);
   1.199 +      // Init returns an error if there are no nodes in range.  This can easily
   1.200 +      // happen with the subtree iterator if the selection doesn't contain any
   1.201 +      // *whole* nodes.
   1.202 +      if (NS_SUCCEEDED(res)) {
   1.203 +        nsCOMPtr<nsIDOMNode> node;
   1.204 +        for (; !iter->IsDone(); iter->Next()) {
   1.205 +          node = do_QueryInterface(iter->GetCurrentNode());
   1.206 +          NS_ENSURE_TRUE(node, NS_ERROR_FAILURE);
   1.207 +
   1.208 +          if (IsEditable(node)) {
   1.209 +            arrayOfNodes.AppendObject(node);
   1.210 +          }
   1.211 +        }
   1.212 +      }
   1.213 +      // first check the start parent of the range to see if it needs to
   1.214 +      // be separately handled (it does if it's a text node, due to how the
   1.215 +      // subtree iterator works - it will not have reported it).
   1.216 +      if (IsTextNode(startNode) && IsEditable(startNode)) {
   1.217 +        nsCOMPtr<nsIDOMCharacterData> nodeAsText = do_QueryInterface(startNode);
   1.218 +        int32_t startOffset;
   1.219 +        uint32_t textLen;
   1.220 +        range->GetStartOffset(&startOffset);
   1.221 +        nodeAsText->GetLength(&textLen);
   1.222 +        res = SetInlinePropertyOnTextNode(nodeAsText, startOffset, textLen,
   1.223 +                                          aProperty, &aAttribute, &aValue);
   1.224 +        NS_ENSURE_SUCCESS(res, res);
   1.225 +      }
   1.226 +
   1.227 +      // then loop through the list, set the property on each node
   1.228 +      int32_t listCount = arrayOfNodes.Count();
   1.229 +      int32_t j;
   1.230 +      for (j = 0; j < listCount; j++) {
   1.231 +        res = SetInlinePropertyOnNode(arrayOfNodes[j], aProperty,
   1.232 +                                      &aAttribute, &aValue);
   1.233 +        NS_ENSURE_SUCCESS(res, res);
   1.234 +      }
   1.235 +
   1.236 +      // last check the end parent of the range to see if it needs to
   1.237 +      // be separately handled (it does if it's a text node, due to how the
   1.238 +      // subtree iterator works - it will not have reported it).
   1.239 +      if (IsTextNode(endNode) && IsEditable(endNode)) {
   1.240 +        nsCOMPtr<nsIDOMCharacterData> nodeAsText = do_QueryInterface(endNode);
   1.241 +        int32_t endOffset;
   1.242 +        range->GetEndOffset(&endOffset);
   1.243 +        res = SetInlinePropertyOnTextNode(nodeAsText, 0, endOffset,
   1.244 +                                          aProperty, &aAttribute, &aValue);
   1.245 +        NS_ENSURE_SUCCESS(res, res);
   1.246 +      }
   1.247 +    }
   1.248 +  }
   1.249 +  if (!cancel) {
   1.250 +    // post-process
   1.251 +    return mRules->DidDoAction(selection, &ruleInfo, res);
   1.252 +  }
   1.253 +  return NS_OK;
   1.254 +}
   1.255 +
   1.256 +
   1.257 +
   1.258 +// Helper function for SetInlinePropertyOn*: is aNode a simple old <b>, <font>,
   1.259 +// <span style="">, etc. that we can reuse instead of creating a new one?
   1.260 +bool
   1.261 +nsHTMLEditor::IsSimpleModifiableNode(nsIContent* aContent,
   1.262 +                                     nsIAtom* aProperty,
   1.263 +                                     const nsAString* aAttribute,
   1.264 +                                     const nsAString* aValue)
   1.265 +{
   1.266 +  // aContent can be null, in which case we'll return false in a few lines
   1.267 +  MOZ_ASSERT(aProperty);
   1.268 +  MOZ_ASSERT_IF(aAttribute, aValue);
   1.269 +
   1.270 +  nsCOMPtr<dom::Element> element = do_QueryInterface(aContent);
   1.271 +  if (!element) {
   1.272 +    return false;
   1.273 +  }
   1.274 +
   1.275 +  // First check for <b>, <i>, etc.
   1.276 +  if (element->IsHTML(aProperty) && !element->GetAttrCount() &&
   1.277 +      (!aAttribute || aAttribute->IsEmpty())) {
   1.278 +    return true;
   1.279 +  }
   1.280 +
   1.281 +  // Special cases for various equivalencies: <strong>, <em>, <s>
   1.282 +  if (!element->GetAttrCount() &&
   1.283 +      ((aProperty == nsGkAtoms::b && element->IsHTML(nsGkAtoms::strong)) ||
   1.284 +       (aProperty == nsGkAtoms::i && element->IsHTML(nsGkAtoms::em)) ||
   1.285 +       (aProperty == nsGkAtoms::strike && element->IsHTML(nsGkAtoms::s)))) {
   1.286 +    return true;
   1.287 +  }
   1.288 +
   1.289 +  // Now look for things like <font>
   1.290 +  if (aAttribute && !aAttribute->IsEmpty()) {
   1.291 +    nsCOMPtr<nsIAtom> atom = do_GetAtom(*aAttribute);
   1.292 +    MOZ_ASSERT(atom);
   1.293 +
   1.294 +    nsString attrValue;
   1.295 +    if (element->IsHTML(aProperty) && IsOnlyAttribute(element, *aAttribute) &&
   1.296 +        element->GetAttr(kNameSpaceID_None, atom, attrValue) &&
   1.297 +        attrValue.Equals(*aValue, nsCaseInsensitiveStringComparator())) {
   1.298 +      // This is not quite correct, because it excludes cases like
   1.299 +      // <font face=000> being the same as <font face=#000000>.
   1.300 +      // Property-specific handling is needed (bug 760211).
   1.301 +      return true;
   1.302 +    }
   1.303 +  }
   1.304 +
   1.305 +  // No luck so far.  Now we check for a <span> with a single style=""
   1.306 +  // attribute that sets only the style we're looking for, if this type of
   1.307 +  // style supports it
   1.308 +  if (!mHTMLCSSUtils->IsCSSEditableProperty(element, aProperty, aAttribute) ||
   1.309 +      !element->IsHTML(nsGkAtoms::span) || element->GetAttrCount() != 1 ||
   1.310 +      !element->HasAttr(kNameSpaceID_None, nsGkAtoms::style)) {
   1.311 +    return false;
   1.312 +  }
   1.313 +
   1.314 +  // Some CSS styles are not so simple.  For instance, underline is
   1.315 +  // "text-decoration: underline", which decomposes into four different text-*
   1.316 +  // properties.  So for now, we just create a span, add the desired style, and
   1.317 +  // see if it matches.
   1.318 +  nsCOMPtr<dom::Element> newSpan;
   1.319 +  nsresult res = CreateHTMLContent(NS_LITERAL_STRING("span"),
   1.320 +                                   getter_AddRefs(newSpan));
   1.321 +  NS_ASSERTION(NS_SUCCEEDED(res), "CreateHTMLContent failed");
   1.322 +  NS_ENSURE_SUCCESS(res, false);
   1.323 +  mHTMLCSSUtils->SetCSSEquivalentToHTMLStyle(newSpan, aProperty,
   1.324 +                                             aAttribute, aValue,
   1.325 +                                             /*suppress transaction*/ true);
   1.326 +
   1.327 +  return mHTMLCSSUtils->ElementsSameStyle(newSpan, element);
   1.328 +}
   1.329 +
   1.330 +
   1.331 +nsresult
   1.332 +nsHTMLEditor::SetInlinePropertyOnTextNode( nsIDOMCharacterData *aTextNode, 
   1.333 +                                            int32_t aStartOffset,
   1.334 +                                            int32_t aEndOffset,
   1.335 +                                            nsIAtom *aProperty, 
   1.336 +                                            const nsAString *aAttribute,
   1.337 +                                            const nsAString *aValue)
   1.338 +{
   1.339 +  MOZ_ASSERT(aValue);
   1.340 +  NS_ENSURE_TRUE(aTextNode, NS_ERROR_NULL_POINTER);
   1.341 +  nsCOMPtr<nsIDOMNode> parent;
   1.342 +  nsresult res = aTextNode->GetParentNode(getter_AddRefs(parent));
   1.343 +  NS_ENSURE_SUCCESS(res, res);
   1.344 +
   1.345 +  if (!CanContainTag(parent, aProperty)) {
   1.346 +    return NS_OK;
   1.347 +  }
   1.348 +  
   1.349 +  // don't need to do anything if no characters actually selected
   1.350 +  if (aStartOffset == aEndOffset) return NS_OK;
   1.351 +  
   1.352 +  nsCOMPtr<nsIDOMNode> node = aTextNode;
   1.353 +  
   1.354 +  // don't need to do anything if property already set on node
   1.355 +  bool bHasProp;
   1.356 +  if (mHTMLCSSUtils->IsCSSEditableProperty(node, aProperty, aAttribute)) {
   1.357 +    // the HTML styles defined by aProperty/aAttribute has a CSS equivalence
   1.358 +    // in this implementation for node; let's check if it carries those css styles
   1.359 +    nsAutoString value(*aValue);
   1.360 +    mHTMLCSSUtils->IsCSSEquivalentToHTMLInlineStyleSet(node, aProperty, aAttribute,
   1.361 +                                                       bHasProp, value,
   1.362 +                                                       nsHTMLCSSUtils::eComputed);
   1.363 +  } else {
   1.364 +    IsTextPropertySetByContent(node, aProperty, aAttribute, aValue, bHasProp);
   1.365 +  }
   1.366 +
   1.367 +  if (bHasProp) return NS_OK;
   1.368 +  
   1.369 +  // do we need to split the text node?
   1.370 +  uint32_t textLen;
   1.371 +  aTextNode->GetLength(&textLen);
   1.372 +
   1.373 +  if (uint32_t(aEndOffset) != textLen) {
   1.374 +    // we need to split off back of text node
   1.375 +    nsCOMPtr<nsIDOMNode> tmp;
   1.376 +    res = SplitNode(node, aEndOffset, getter_AddRefs(tmp));
   1.377 +    NS_ENSURE_SUCCESS(res, res);
   1.378 +    node = tmp;  // remember left node
   1.379 +  }
   1.380 +
   1.381 +  if (aStartOffset) {
   1.382 +    // we need to split off front of text node
   1.383 +    nsCOMPtr<nsIDOMNode> tmp;
   1.384 +    res = SplitNode(node, aStartOffset, getter_AddRefs(tmp));
   1.385 +    NS_ENSURE_SUCCESS(res, res);
   1.386 +  }
   1.387 +
   1.388 +  nsCOMPtr<nsIContent> content = do_QueryInterface(node);
   1.389 +  NS_ENSURE_STATE(content);
   1.390 +
   1.391 +  if (aAttribute) {
   1.392 +    // look for siblings that are correct type of node
   1.393 +    nsIContent* sibling = GetPriorHTMLSibling(content);
   1.394 +    if (IsSimpleModifiableNode(sibling, aProperty, aAttribute, aValue)) {
   1.395 +      // previous sib is already right kind of inline node; slide this over into it
   1.396 +      return MoveNode(node, sibling->AsDOMNode(), -1);
   1.397 +    }
   1.398 +    sibling = GetNextHTMLSibling(content);
   1.399 +    if (IsSimpleModifiableNode(sibling, aProperty, aAttribute, aValue)) {
   1.400 +      // following sib is already right kind of inline node; slide this over into it
   1.401 +      return MoveNode(node, sibling->AsDOMNode(), 0);
   1.402 +    }
   1.403 +  }
   1.404 +  
   1.405 +  // reparent the node inside inline node with appropriate {attribute,value}
   1.406 +  return SetInlinePropertyOnNode(node, aProperty, aAttribute, aValue);
   1.407 +}
   1.408 +
   1.409 +
   1.410 +nsresult
   1.411 +nsHTMLEditor::SetInlinePropertyOnNodeImpl(nsIContent* aNode,
   1.412 +                                          nsIAtom* aProperty,
   1.413 +                                          const nsAString* aAttribute,
   1.414 +                                          const nsAString* aValue)
   1.415 +{
   1.416 +  MOZ_ASSERT(aNode && aProperty);
   1.417 +  MOZ_ASSERT(aValue);
   1.418 +
   1.419 +  // If this is an element that can't be contained in a span, we have to
   1.420 +  // recurse to its children.
   1.421 +  if (!TagCanContain(nsGkAtoms::span, aNode->AsDOMNode())) {
   1.422 +    if (aNode->HasChildren()) {
   1.423 +      nsCOMArray<nsIContent> arrayOfNodes;
   1.424 +
   1.425 +      // Populate the list.
   1.426 +      for (nsIContent* child = aNode->GetFirstChild();
   1.427 +           child;
   1.428 +           child = child->GetNextSibling()) {
   1.429 +        if (IsEditable(child) && !IsEmptyTextNode(this, child)) {
   1.430 +          arrayOfNodes.AppendObject(child);
   1.431 +        }
   1.432 +      }
   1.433 +
   1.434 +      // Then loop through the list, set the property on each node.
   1.435 +      int32_t listCount = arrayOfNodes.Count();
   1.436 +      for (int32_t j = 0; j < listCount; ++j) {
   1.437 +        nsresult rv = SetInlinePropertyOnNode(arrayOfNodes[j], aProperty,
   1.438 +                                              aAttribute, aValue);
   1.439 +        NS_ENSURE_SUCCESS(rv, rv);
   1.440 +      }
   1.441 +    }
   1.442 +    return NS_OK;
   1.443 +  }
   1.444 +
   1.445 +  // First check if there's an adjacent sibling we can put our node into.
   1.446 +  nsresult res;
   1.447 +  nsCOMPtr<nsIContent> previousSibling = GetPriorHTMLSibling(aNode);
   1.448 +  nsCOMPtr<nsIContent> nextSibling = GetNextHTMLSibling(aNode);
   1.449 +  if (IsSimpleModifiableNode(previousSibling, aProperty, aAttribute, aValue)) {
   1.450 +    res = MoveNode(aNode, previousSibling, -1);
   1.451 +    NS_ENSURE_SUCCESS(res, res);
   1.452 +    if (IsSimpleModifiableNode(nextSibling, aProperty, aAttribute, aValue)) {
   1.453 +      res = JoinNodes(previousSibling, nextSibling);
   1.454 +      NS_ENSURE_SUCCESS(res, res);
   1.455 +    }
   1.456 +    return NS_OK;
   1.457 +  }
   1.458 +  if (IsSimpleModifiableNode(nextSibling, aProperty, aAttribute, aValue)) {
   1.459 +    res = MoveNode(aNode, nextSibling, 0);
   1.460 +    NS_ENSURE_SUCCESS(res, res);
   1.461 +    return NS_OK;
   1.462 +  }
   1.463 +
   1.464 +  // don't need to do anything if property already set on node
   1.465 +  if (mHTMLCSSUtils->IsCSSEditableProperty(aNode, aProperty, aAttribute)) {
   1.466 +    if (mHTMLCSSUtils->IsCSSEquivalentToHTMLInlineStyleSet(
   1.467 +          aNode, aProperty, aAttribute, *aValue, nsHTMLCSSUtils::eComputed)) {
   1.468 +      return NS_OK;
   1.469 +    }
   1.470 +  } else if (IsTextPropertySetByContent(aNode, aProperty,
   1.471 +                                        aAttribute, aValue)) {
   1.472 +    return NS_OK;
   1.473 +  }
   1.474 +
   1.475 +  bool useCSS = (IsCSSEnabled() &&
   1.476 +                 mHTMLCSSUtils->IsCSSEditableProperty(aNode, aProperty, aAttribute)) ||
   1.477 +                // bgcolor is always done using CSS
   1.478 +                aAttribute->EqualsLiteral("bgcolor");
   1.479 +
   1.480 +  if (useCSS) {
   1.481 +    nsCOMPtr<dom::Element> tmp;
   1.482 +    // We only add style="" to <span>s with no attributes (bug 746515).  If we
   1.483 +    // don't have one, we need to make one.
   1.484 +    if (aNode->IsElement() && aNode->AsElement()->IsHTML(nsGkAtoms::span) &&
   1.485 +        !aNode->AsElement()->GetAttrCount()) {
   1.486 +      tmp = aNode->AsElement();
   1.487 +    } else {
   1.488 +      res = InsertContainerAbove(aNode, getter_AddRefs(tmp),
   1.489 +                                 NS_LITERAL_STRING("span"),
   1.490 +                                 nullptr, nullptr);
   1.491 +      NS_ENSURE_SUCCESS(res, res);
   1.492 +    }
   1.493 +
   1.494 +    // Add the CSS styles corresponding to the HTML style request
   1.495 +    int32_t count;
   1.496 +    res = mHTMLCSSUtils->SetCSSEquivalentToHTMLStyle(tmp->AsDOMNode(),
   1.497 +                                                     aProperty, aAttribute,
   1.498 +                                                     aValue, &count, false);
   1.499 +    NS_ENSURE_SUCCESS(res, res);
   1.500 +    return NS_OK;
   1.501 +  }
   1.502 +
   1.503 +  // is it already the right kind of node, but with wrong attribute?
   1.504 +  if (aNode->Tag() == aProperty) {
   1.505 +    // Just set the attribute on it.
   1.506 +    nsCOMPtr<nsIDOMElement> elem = do_QueryInterface(aNode);
   1.507 +    return SetAttribute(elem, *aAttribute, *aValue);
   1.508 +  }
   1.509 +
   1.510 +  // ok, chuck it in its very own container
   1.511 +  nsAutoString tag;
   1.512 +  aProperty->ToString(tag);
   1.513 +  ToLowerCase(tag);
   1.514 +  nsCOMPtr<nsIDOMNode> tmp;
   1.515 +  return InsertContainerAbove(aNode->AsDOMNode(), address_of(tmp), tag,
   1.516 +                              aAttribute, aValue);
   1.517 +}
   1.518 +
   1.519 +
   1.520 +nsresult
   1.521 +nsHTMLEditor::SetInlinePropertyOnNode(nsIDOMNode *aNode,
   1.522 +                                      nsIAtom *aProperty,
   1.523 +                                      const nsAString *aAttribute,
   1.524 +                                      const nsAString *aValue)
   1.525 +{
   1.526 +  // Before setting the property, we remove it if it's already set.
   1.527 +  // RemoveStyleInside might remove the node we're looking at or some of its
   1.528 +  // descendants, however, in which case we want to set the property on
   1.529 +  // whatever wound up in its place.  We have to save the original siblings and
   1.530 +  // parent to figure this out.
   1.531 +  NS_ENSURE_TRUE(aNode && aProperty, NS_ERROR_NULL_POINTER);
   1.532 +
   1.533 +  nsCOMPtr<nsIContent> node = do_QueryInterface(aNode);
   1.534 +  NS_ENSURE_STATE(node);
   1.535 +
   1.536 +  return SetInlinePropertyOnNode(node, aProperty, aAttribute, aValue);
   1.537 +}
   1.538 +
   1.539 +nsresult
   1.540 +nsHTMLEditor::SetInlinePropertyOnNode(nsIContent* aNode,
   1.541 +                                      nsIAtom* aProperty,
   1.542 +                                      const nsAString* aAttribute,
   1.543 +                                      const nsAString* aValue)
   1.544 +{
   1.545 +  MOZ_ASSERT(aNode);
   1.546 +  MOZ_ASSERT(aProperty);
   1.547 +
   1.548 +  nsCOMPtr<nsIContent> previousSibling = aNode->GetPreviousSibling(),
   1.549 +                       nextSibling = aNode->GetNextSibling();
   1.550 +  nsCOMPtr<nsINode> parent = aNode->GetParentNode();
   1.551 +  NS_ENSURE_STATE(parent);
   1.552 +
   1.553 +  nsresult res = RemoveStyleInside(aNode->AsDOMNode(), aProperty, aAttribute);
   1.554 +  NS_ENSURE_SUCCESS(res, res);
   1.555 +
   1.556 +  if (aNode->GetParentNode()) {
   1.557 +    // The node is still where it was
   1.558 +    return SetInlinePropertyOnNodeImpl(aNode, aProperty,
   1.559 +                                       aAttribute, aValue);
   1.560 +  }
   1.561 +
   1.562 +  // It's vanished.  Use the old siblings for reference to construct a
   1.563 +  // list.  But first, verify that the previous/next siblings are still
   1.564 +  // where we expect them; otherwise we have to give up.
   1.565 +  if ((previousSibling && previousSibling->GetParentNode() != parent) ||
   1.566 +      (nextSibling && nextSibling->GetParentNode() != parent)) {
   1.567 +    return NS_ERROR_UNEXPECTED;
   1.568 +  }
   1.569 +  nsCOMArray<nsIContent> nodesToSet;
   1.570 +  nsCOMPtr<nsIContent> cur = previousSibling
   1.571 +    ? previousSibling->GetNextSibling() : parent->GetFirstChild();
   1.572 +  while (cur && cur != nextSibling) {
   1.573 +    if (IsEditable(cur)) {
   1.574 +      nodesToSet.AppendObject(cur);
   1.575 +    }
   1.576 +    cur = cur->GetNextSibling();
   1.577 +  }
   1.578 +
   1.579 +  int32_t nodesToSetCount = nodesToSet.Count();
   1.580 +  for (int32_t k = 0; k < nodesToSetCount; k++) {
   1.581 +    res = SetInlinePropertyOnNodeImpl(nodesToSet[k], aProperty,
   1.582 +                                      aAttribute, aValue);
   1.583 +    NS_ENSURE_SUCCESS(res, res);
   1.584 +  }
   1.585 +
   1.586 +  return NS_OK;
   1.587 +}
   1.588 +
   1.589 +
   1.590 +nsresult nsHTMLEditor::SplitStyleAboveRange(nsIDOMRange *inRange, 
   1.591 +                                            nsIAtom *aProperty, 
   1.592 +                                            const nsAString *aAttribute)
   1.593 +{
   1.594 +  NS_ENSURE_TRUE(inRange, NS_ERROR_NULL_POINTER);
   1.595 +  nsresult res;
   1.596 +  nsCOMPtr<nsIDOMNode> startNode, endNode, origStartNode;
   1.597 +  int32_t startOffset, endOffset;
   1.598 +
   1.599 +  res = inRange->GetStartContainer(getter_AddRefs(startNode));
   1.600 +  NS_ENSURE_SUCCESS(res, res);
   1.601 +  res = inRange->GetStartOffset(&startOffset);
   1.602 +  NS_ENSURE_SUCCESS(res, res);
   1.603 +  res = inRange->GetEndContainer(getter_AddRefs(endNode));
   1.604 +  NS_ENSURE_SUCCESS(res, res);
   1.605 +  res = inRange->GetEndOffset(&endOffset);
   1.606 +  NS_ENSURE_SUCCESS(res, res);
   1.607 +
   1.608 +  origStartNode = startNode;
   1.609 +
   1.610 +  // split any matching style nodes above the start of range
   1.611 +  {
   1.612 +    nsAutoTrackDOMPoint tracker(mRangeUpdater, address_of(endNode), &endOffset);
   1.613 +    res = SplitStyleAbovePoint(address_of(startNode), &startOffset, aProperty, aAttribute);
   1.614 +    NS_ENSURE_SUCCESS(res, res);
   1.615 +  }
   1.616 +
   1.617 +  // second verse, same as the first...
   1.618 +  res = SplitStyleAbovePoint(address_of(endNode), &endOffset, aProperty, aAttribute);
   1.619 +  NS_ENSURE_SUCCESS(res, res);
   1.620 +
   1.621 +  // reset the range
   1.622 +  res = inRange->SetStart(startNode, startOffset);
   1.623 +  NS_ENSURE_SUCCESS(res, res);
   1.624 +  res = inRange->SetEnd(endNode, endOffset);
   1.625 +  return res;
   1.626 +}
   1.627 +
   1.628 +nsresult nsHTMLEditor::SplitStyleAbovePoint(nsCOMPtr<nsIDOMNode> *aNode,
   1.629 +                                           int32_t *aOffset,
   1.630 +                                           nsIAtom *aProperty,          // null here means we split all properties
   1.631 +                                           const nsAString *aAttribute,
   1.632 +                                           nsCOMPtr<nsIDOMNode> *outLeftNode,
   1.633 +                                           nsCOMPtr<nsIDOMNode> *outRightNode)
   1.634 +{
   1.635 +  NS_ENSURE_TRUE(aNode && *aNode && aOffset, NS_ERROR_NULL_POINTER);
   1.636 +  if (outLeftNode)  *outLeftNode  = nullptr;
   1.637 +  if (outRightNode) *outRightNode = nullptr;
   1.638 +  // split any matching style nodes above the node/offset
   1.639 +  nsCOMPtr<nsIDOMNode> parent, tmp = *aNode;
   1.640 +  int32_t offset;
   1.641 +
   1.642 +  bool useCSS = IsCSSEnabled();
   1.643 +
   1.644 +  bool isSet;
   1.645 +  while (tmp && !IsBlockNode(tmp))
   1.646 +  {
   1.647 +    isSet = false;
   1.648 +    if (useCSS && mHTMLCSSUtils->IsCSSEditableProperty(tmp, aProperty, aAttribute)) {
   1.649 +      // the HTML style defined by aProperty/aAttribute has a CSS equivalence
   1.650 +      // in this implementation for the node tmp; let's check if it carries those css styles
   1.651 +      nsAutoString firstValue;
   1.652 +      mHTMLCSSUtils->IsCSSEquivalentToHTMLInlineStyleSet(tmp, aProperty,
   1.653 +        aAttribute, isSet, firstValue, nsHTMLCSSUtils::eSpecified);
   1.654 +    }
   1.655 +    if ( (aProperty && NodeIsType(tmp, aProperty)) ||   // node is the correct inline prop
   1.656 +         (aProperty == nsEditProperty::href && nsHTMLEditUtils::IsLink(tmp)) ||
   1.657 +                                                        // node is href - test if really <a href=...
   1.658 +         (!aProperty && NodeIsProperty(tmp)) ||         // or node is any prop, and we asked to split them all
   1.659 +         isSet)                                         // or the style is specified in the style attribute
   1.660 +    {
   1.661 +      // found a style node we need to split
   1.662 +      nsresult rv = SplitNodeDeep(tmp, *aNode, *aOffset, &offset, false,
   1.663 +                                  outLeftNode, outRightNode);
   1.664 +      NS_ENSURE_SUCCESS(rv, rv);
   1.665 +      // reset startNode/startOffset
   1.666 +      tmp->GetParentNode(getter_AddRefs(*aNode));
   1.667 +      *aOffset = offset;
   1.668 +    }
   1.669 +    tmp->GetParentNode(getter_AddRefs(parent));
   1.670 +    tmp = parent;
   1.671 +  }
   1.672 +  return NS_OK;
   1.673 +}
   1.674 +
   1.675 +nsresult
   1.676 +nsHTMLEditor::ClearStyle(nsCOMPtr<nsIDOMNode>* aNode, int32_t* aOffset,
   1.677 +                         nsIAtom* aProperty, const nsAString* aAttribute)
   1.678 +{
   1.679 +  nsCOMPtr<nsIDOMNode> leftNode, rightNode, tmp;
   1.680 +  nsresult res = SplitStyleAbovePoint(aNode, aOffset, aProperty, aAttribute,
   1.681 +                                      address_of(leftNode),
   1.682 +                                      address_of(rightNode));
   1.683 +  NS_ENSURE_SUCCESS(res, res);
   1.684 +  if (leftNode) {
   1.685 +    bool bIsEmptyNode;
   1.686 +    IsEmptyNode(leftNode, &bIsEmptyNode, false, true);
   1.687 +    if (bIsEmptyNode) {
   1.688 +      // delete leftNode if it became empty
   1.689 +      res = DeleteNode(leftNode);
   1.690 +      NS_ENSURE_SUCCESS(res, res);
   1.691 +    }
   1.692 +  }
   1.693 +  if (rightNode) {
   1.694 +    nsCOMPtr<nsIDOMNode> secondSplitParent = GetLeftmostChild(rightNode);
   1.695 +    // don't try to split non-containers (br's, images, hr's, etc)
   1.696 +    if (!secondSplitParent) {
   1.697 +      secondSplitParent = rightNode;
   1.698 +    }
   1.699 +    nsCOMPtr<nsIDOMNode> savedBR;
   1.700 +    if (!IsContainer(secondSplitParent)) {
   1.701 +      if (nsTextEditUtils::IsBreak(secondSplitParent)) {
   1.702 +        savedBR = secondSplitParent;
   1.703 +      }
   1.704 +
   1.705 +      secondSplitParent->GetParentNode(getter_AddRefs(tmp));
   1.706 +      secondSplitParent = tmp;
   1.707 +    }
   1.708 +    *aOffset = 0;
   1.709 +    res = SplitStyleAbovePoint(address_of(secondSplitParent),
   1.710 +                               aOffset, aProperty, aAttribute,
   1.711 +                               address_of(leftNode), address_of(rightNode));
   1.712 +    NS_ENSURE_SUCCESS(res, res);
   1.713 +    // should be impossible to not get a new leftnode here
   1.714 +    NS_ENSURE_TRUE(leftNode, NS_ERROR_FAILURE);
   1.715 +    nsCOMPtr<nsIDOMNode> newSelParent = GetLeftmostChild(leftNode);
   1.716 +    if (!newSelParent) {
   1.717 +      newSelParent = leftNode;
   1.718 +    }
   1.719 +    // If rightNode starts with a br, suck it out of right node and into
   1.720 +    // leftNode.  This is so we you don't revert back to the previous style
   1.721 +    // if you happen to click at the end of a line.
   1.722 +    if (savedBR) {
   1.723 +      res = MoveNode(savedBR, newSelParent, 0);
   1.724 +      NS_ENSURE_SUCCESS(res, res);
   1.725 +    }
   1.726 +    bool bIsEmptyNode;
   1.727 +    IsEmptyNode(rightNode, &bIsEmptyNode, false, true);
   1.728 +    if (bIsEmptyNode) {
   1.729 +      // delete rightNode if it became empty
   1.730 +      res = DeleteNode(rightNode);
   1.731 +      NS_ENSURE_SUCCESS(res, res);
   1.732 +    }
   1.733 +    // remove the style on this new hierarchy
   1.734 +    int32_t newSelOffset = 0;
   1.735 +    {
   1.736 +      // Track the point at the new hierarchy.  This is so we can know where
   1.737 +      // to put the selection after we call RemoveStyleInside().
   1.738 +      // RemoveStyleInside() could remove any and all of those nodes, so I
   1.739 +      // have to use the range tracking system to find the right spot to put
   1.740 +      // selection.
   1.741 +      nsAutoTrackDOMPoint tracker(mRangeUpdater,
   1.742 +                                  address_of(newSelParent), &newSelOffset);
   1.743 +      res = RemoveStyleInside(leftNode, aProperty, aAttribute);
   1.744 +      NS_ENSURE_SUCCESS(res, res);
   1.745 +    }
   1.746 +    // reset our node offset values to the resulting new sel point
   1.747 +    *aNode = newSelParent;
   1.748 +    *aOffset = newSelOffset;
   1.749 +  }
   1.750 +
   1.751 +  return NS_OK;
   1.752 +}
   1.753 +
   1.754 +bool nsHTMLEditor::NodeIsProperty(nsIDOMNode *aNode)
   1.755 +{
   1.756 +  NS_ENSURE_TRUE(aNode, false);
   1.757 +  if (!IsContainer(aNode))  return false;
   1.758 +  if (!IsEditable(aNode))   return false;
   1.759 +  if (IsBlockNode(aNode))   return false;
   1.760 +  if (NodeIsType(aNode, nsEditProperty::a)) return false;
   1.761 +  return true;
   1.762 +}
   1.763 +
   1.764 +nsresult nsHTMLEditor::ApplyDefaultProperties()
   1.765 +{
   1.766 +  nsresult res = NS_OK;
   1.767 +  uint32_t j, defcon = mDefaultStyles.Length();
   1.768 +  for (j=0; j<defcon; j++)
   1.769 +  {
   1.770 +    PropItem *propItem = mDefaultStyles[j];
   1.771 +    NS_ENSURE_TRUE(propItem, NS_ERROR_NULL_POINTER);
   1.772 +    res = SetInlineProperty(propItem->tag, propItem->attr, propItem->value);
   1.773 +    NS_ENSURE_SUCCESS(res, res);
   1.774 +  }
   1.775 +  return res;
   1.776 +}
   1.777 +
   1.778 +nsresult nsHTMLEditor::RemoveStyleInside(nsIDOMNode *aNode, 
   1.779 +                                         // null here means remove all properties
   1.780 +                                         nsIAtom *aProperty,
   1.781 +                                         const nsAString *aAttribute,
   1.782 +                                         const bool aChildrenOnly)
   1.783 +{
   1.784 +  NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER);
   1.785 +  if (IsTextNode(aNode)) {
   1.786 +    return NS_OK;
   1.787 +  }
   1.788 +  nsresult res;
   1.789 +
   1.790 +  // first process the children
   1.791 +  nsCOMPtr<nsIDOMNode> child, tmp;
   1.792 +  aNode->GetFirstChild(getter_AddRefs(child));
   1.793 +  while (child) {
   1.794 +    // cache next sibling since we might remove child
   1.795 +    child->GetNextSibling(getter_AddRefs(tmp));
   1.796 +    res = RemoveStyleInside(child, aProperty, aAttribute);
   1.797 +    NS_ENSURE_SUCCESS(res, res);
   1.798 +    child = tmp;
   1.799 +  }
   1.800 +
   1.801 +  // then process the node itself
   1.802 +  if (!aChildrenOnly &&
   1.803 +    (
   1.804 +      // node is prop we asked for
   1.805 +      (aProperty && NodeIsType(aNode, aProperty)) ||
   1.806 +      // but check for link (<a href=...)
   1.807 +      (aProperty == nsEditProperty::href && nsHTMLEditUtils::IsLink(aNode)) ||
   1.808 +      // and for named anchors
   1.809 +      (aProperty == nsEditProperty::name && nsHTMLEditUtils::IsNamedAnchor(aNode)) ||
   1.810 +      // or node is any prop and we asked for that
   1.811 +      (!aProperty && NodeIsProperty(aNode))
   1.812 +    )
   1.813 +  ) {
   1.814 +    // if we weren't passed an attribute, then we want to 
   1.815 +    // remove any matching inlinestyles entirely
   1.816 +    if (!aAttribute || aAttribute->IsEmpty()) {
   1.817 +      NS_NAMED_LITERAL_STRING(styleAttr, "style");
   1.818 +      NS_NAMED_LITERAL_STRING(classAttr, "class");
   1.819 +      bool hasStyleAttr = HasAttr(aNode, &styleAttr);
   1.820 +      bool hasClassAttr = HasAttr(aNode, &classAttr);
   1.821 +      if (aProperty && (hasStyleAttr || hasClassAttr)) {
   1.822 +        // aNode carries inline styles or a class attribute so we can't
   1.823 +        // just remove the element... We need to create above the element
   1.824 +        // a span that will carry those styles or class, then we can delete
   1.825 +        // the node.
   1.826 +        nsCOMPtr<nsIDOMNode> spanNode;
   1.827 +        res = InsertContainerAbove(aNode, address_of(spanNode),
   1.828 +                                   NS_LITERAL_STRING("span"));
   1.829 +        NS_ENSURE_SUCCESS(res, res);
   1.830 +        res = CloneAttribute(styleAttr, spanNode, aNode);
   1.831 +        NS_ENSURE_SUCCESS(res, res);
   1.832 +        res = CloneAttribute(classAttr, spanNode, aNode);
   1.833 +        NS_ENSURE_SUCCESS(res, res);
   1.834 +      }
   1.835 +      res = RemoveContainer(aNode);
   1.836 +      NS_ENSURE_SUCCESS(res, res);
   1.837 +    } else {
   1.838 +      // otherwise we just want to eliminate the attribute
   1.839 +      if (HasAttr(aNode, aAttribute)) {
   1.840 +        // if this matching attribute is the ONLY one on the node,
   1.841 +        // then remove the whole node.  Otherwise just nix the attribute.
   1.842 +        if (IsOnlyAttribute(aNode, aAttribute)) {
   1.843 +          res = RemoveContainer(aNode);
   1.844 +        } else {
   1.845 +          nsCOMPtr<nsIDOMElement> elem = do_QueryInterface(aNode);
   1.846 +          NS_ENSURE_TRUE(elem, NS_ERROR_NULL_POINTER);
   1.847 +          res = RemoveAttribute(elem, *aAttribute);
   1.848 +        }
   1.849 +        NS_ENSURE_SUCCESS(res, res);
   1.850 +      }
   1.851 +    }
   1.852 +  }
   1.853 +
   1.854 +  if (!aChildrenOnly &&
   1.855 +      mHTMLCSSUtils->IsCSSEditableProperty(aNode, aProperty, aAttribute)) {
   1.856 +    // the HTML style defined by aProperty/aAttribute has a CSS equivalence in
   1.857 +    // this implementation for the node aNode; let's check if it carries those
   1.858 +    // css styles
   1.859 +    nsAutoString propertyValue;
   1.860 +    bool isSet;
   1.861 +    mHTMLCSSUtils->IsCSSEquivalentToHTMLInlineStyleSet(aNode, aProperty,
   1.862 +      aAttribute, isSet, propertyValue, nsHTMLCSSUtils::eSpecified);
   1.863 +    if (isSet) {
   1.864 +      // yes, tmp has the corresponding css declarations in its style attribute
   1.865 +      // let's remove them
   1.866 +      mHTMLCSSUtils->RemoveCSSEquivalentToHTMLStyle(aNode,
   1.867 +                                                    aProperty,
   1.868 +                                                    aAttribute,
   1.869 +                                                    &propertyValue,
   1.870 +                                                    false);
   1.871 +      // remove the node if it is a span or font, if its style attribute is
   1.872 +      // empty or absent, and if it does not have a class nor an id
   1.873 +      RemoveElementIfNoStyleOrIdOrClass(aNode);
   1.874 +    }
   1.875 +  }
   1.876 +
   1.877 +  if (!aChildrenOnly &&
   1.878 +    (
   1.879 +      (aProperty == nsEditProperty::font) &&    // or node is big or small and we are setting font size
   1.880 +      (nsHTMLEditUtils::IsBig(aNode) || nsHTMLEditUtils::IsSmall(aNode)) &&
   1.881 +      (aAttribute && aAttribute->LowerCaseEqualsLiteral("size"))
   1.882 +    )
   1.883 +  ) {
   1.884 +    return RemoveContainer(aNode);  // if we are setting font size, remove any nested bigs and smalls
   1.885 +  }
   1.886 +  return NS_OK;
   1.887 +}
   1.888 +
   1.889 +bool nsHTMLEditor::IsOnlyAttribute(nsIDOMNode *aNode, 
   1.890 +                                     const nsAString *aAttribute)
   1.891 +{
   1.892 +  NS_ENSURE_TRUE(aNode && aAttribute, false);  // ooops
   1.893 +
   1.894 +  nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
   1.895 +  NS_ENSURE_TRUE(content, false);  // ooops
   1.896 +
   1.897 +  return IsOnlyAttribute(content, *aAttribute);
   1.898 +}
   1.899 +
   1.900 +bool
   1.901 +nsHTMLEditor::IsOnlyAttribute(const nsIContent* aContent,
   1.902 +                              const nsAString& aAttribute)
   1.903 +{
   1.904 +  MOZ_ASSERT(aContent);
   1.905 +
   1.906 +  uint32_t attrCount = aContent->GetAttrCount();
   1.907 +  for (uint32_t i = 0; i < attrCount; ++i) {
   1.908 +    const nsAttrName* name = aContent->GetAttrNameAt(i);
   1.909 +    if (!name->NamespaceEquals(kNameSpaceID_None)) {
   1.910 +      return false;
   1.911 +    }
   1.912 +
   1.913 +    nsAutoString attrString;
   1.914 +    name->LocalName()->ToString(attrString);
   1.915 +    // if it's the attribute we know about, or a special _moz attribute,
   1.916 +    // keep looking
   1.917 +    if (!attrString.Equals(aAttribute, nsCaseInsensitiveStringComparator()) &&
   1.918 +        !StringBeginsWith(attrString, NS_LITERAL_STRING("_moz"))) {
   1.919 +      return false;
   1.920 +    }
   1.921 +  }
   1.922 +  // if we made it through all of them without finding a real attribute
   1.923 +  // other than aAttribute, then return true
   1.924 +  return true;
   1.925 +}
   1.926 +
   1.927 +bool nsHTMLEditor::HasAttr(nsIDOMNode* aNode,
   1.928 +                           const nsAString* aAttribute)
   1.929 +{
   1.930 +  NS_ENSURE_TRUE(aNode, false);
   1.931 +  if (!aAttribute || aAttribute->IsEmpty()) {
   1.932 +    // everybody has the 'null' attribute
   1.933 +    return true;
   1.934 +  }
   1.935 +
   1.936 +  // get element
   1.937 +  nsCOMPtr<dom::Element> element = do_QueryInterface(aNode);
   1.938 +  NS_ENSURE_TRUE(element, false);
   1.939 +
   1.940 +  nsCOMPtr<nsIAtom> atom = do_GetAtom(*aAttribute);
   1.941 +  NS_ENSURE_TRUE(atom, false);
   1.942 +
   1.943 +  return element->HasAttr(kNameSpaceID_None, atom);
   1.944 +}
   1.945 +
   1.946 +
   1.947 +nsresult nsHTMLEditor::PromoteRangeIfStartsOrEndsInNamedAnchor(nsIDOMRange *inRange)
   1.948 +{
   1.949 +  NS_ENSURE_TRUE(inRange, NS_ERROR_NULL_POINTER);
   1.950 +  nsresult res;
   1.951 +  nsCOMPtr<nsIDOMNode> startNode, endNode, parent, tmp;
   1.952 +  int32_t startOffset, endOffset, tmpOffset;
   1.953 +  
   1.954 +  res = inRange->GetStartContainer(getter_AddRefs(startNode));
   1.955 +  NS_ENSURE_SUCCESS(res, res);
   1.956 +  res = inRange->GetStartOffset(&startOffset);
   1.957 +  NS_ENSURE_SUCCESS(res, res);
   1.958 +  res = inRange->GetEndContainer(getter_AddRefs(endNode));
   1.959 +  NS_ENSURE_SUCCESS(res, res);
   1.960 +  res = inRange->GetEndOffset(&endOffset);
   1.961 +  NS_ENSURE_SUCCESS(res, res);
   1.962 +
   1.963 +  tmp = startNode;
   1.964 +  while ( tmp && 
   1.965 +          !nsTextEditUtils::IsBody(tmp) &&
   1.966 +          !nsHTMLEditUtils::IsNamedAnchor(tmp))
   1.967 +  {
   1.968 +    parent = GetNodeLocation(tmp, &tmpOffset);
   1.969 +    tmp = parent;
   1.970 +  }
   1.971 +  NS_ENSURE_TRUE(tmp, NS_ERROR_NULL_POINTER);
   1.972 +  if (nsHTMLEditUtils::IsNamedAnchor(tmp))
   1.973 +  {
   1.974 +    parent = GetNodeLocation(tmp, &tmpOffset);
   1.975 +    startNode = parent;
   1.976 +    startOffset = tmpOffset;
   1.977 +  }
   1.978 +
   1.979 +  tmp = endNode;
   1.980 +  while ( tmp && 
   1.981 +          !nsTextEditUtils::IsBody(tmp) &&
   1.982 +          !nsHTMLEditUtils::IsNamedAnchor(tmp))
   1.983 +  {
   1.984 +    parent = GetNodeLocation(tmp, &tmpOffset);
   1.985 +    tmp = parent;
   1.986 +  }
   1.987 +  NS_ENSURE_TRUE(tmp, NS_ERROR_NULL_POINTER);
   1.988 +  if (nsHTMLEditUtils::IsNamedAnchor(tmp))
   1.989 +  {
   1.990 +    parent = GetNodeLocation(tmp, &tmpOffset);
   1.991 +    endNode = parent;
   1.992 +    endOffset = tmpOffset + 1;
   1.993 +  }
   1.994 +
   1.995 +  res = inRange->SetStart(startNode, startOffset);
   1.996 +  NS_ENSURE_SUCCESS(res, res);
   1.997 +  res = inRange->SetEnd(endNode, endOffset);
   1.998 +  return res;
   1.999 +}
  1.1000 +
  1.1001 +nsresult nsHTMLEditor::PromoteInlineRange(nsIDOMRange *inRange)
  1.1002 +{
  1.1003 +  NS_ENSURE_TRUE(inRange, NS_ERROR_NULL_POINTER);
  1.1004 +  nsresult res;
  1.1005 +  nsCOMPtr<nsIDOMNode> startNode, endNode, parent;
  1.1006 +  int32_t startOffset, endOffset;
  1.1007 +  
  1.1008 +  res = inRange->GetStartContainer(getter_AddRefs(startNode));
  1.1009 +  NS_ENSURE_SUCCESS(res, res);
  1.1010 +  res = inRange->GetStartOffset(&startOffset);
  1.1011 +  NS_ENSURE_SUCCESS(res, res);
  1.1012 +  res = inRange->GetEndContainer(getter_AddRefs(endNode));
  1.1013 +  NS_ENSURE_SUCCESS(res, res);
  1.1014 +  res = inRange->GetEndOffset(&endOffset);
  1.1015 +  NS_ENSURE_SUCCESS(res, res);
  1.1016 +  
  1.1017 +  while ( startNode && 
  1.1018 +          !nsTextEditUtils::IsBody(startNode) && 
  1.1019 +          IsEditable(startNode) &&
  1.1020 +          IsAtFrontOfNode(startNode, startOffset) )
  1.1021 +  {
  1.1022 +    parent = GetNodeLocation(startNode, &startOffset);
  1.1023 +    startNode = parent;
  1.1024 +  }
  1.1025 +  NS_ENSURE_TRUE(startNode, NS_ERROR_NULL_POINTER);
  1.1026 +  
  1.1027 +  while ( endNode && 
  1.1028 +          !nsTextEditUtils::IsBody(endNode) && 
  1.1029 +          IsEditable(endNode) &&
  1.1030 +          IsAtEndOfNode(endNode, endOffset) )
  1.1031 +  {
  1.1032 +    parent = GetNodeLocation(endNode, &endOffset);
  1.1033 +    endNode = parent;
  1.1034 +    endOffset++;  // we are AFTER this node
  1.1035 +  }
  1.1036 +  NS_ENSURE_TRUE(endNode, NS_ERROR_NULL_POINTER);
  1.1037 +  
  1.1038 +  res = inRange->SetStart(startNode, startOffset);
  1.1039 +  NS_ENSURE_SUCCESS(res, res);
  1.1040 +  res = inRange->SetEnd(endNode, endOffset);
  1.1041 +  return res;
  1.1042 +}
  1.1043 +
  1.1044 +bool nsHTMLEditor::IsAtFrontOfNode(nsIDOMNode *aNode, int32_t aOffset)
  1.1045 +{
  1.1046 +  NS_ENSURE_TRUE(aNode, false);  // oops
  1.1047 +  if (!aOffset) {
  1.1048 +    return true;
  1.1049 +  }
  1.1050 +
  1.1051 +  if (IsTextNode(aNode))
  1.1052 +  {
  1.1053 +    return false;
  1.1054 +  }
  1.1055 +  else
  1.1056 +  {
  1.1057 +    nsCOMPtr<nsIDOMNode> firstNode;
  1.1058 +    GetFirstEditableChild(aNode, address_of(firstNode));
  1.1059 +    NS_ENSURE_TRUE(firstNode, true); 
  1.1060 +    int32_t offset = GetChildOffset(firstNode, aNode);
  1.1061 +    if (offset < aOffset) return false;
  1.1062 +    return true;
  1.1063 +  }
  1.1064 +}
  1.1065 +
  1.1066 +bool nsHTMLEditor::IsAtEndOfNode(nsIDOMNode *aNode, int32_t aOffset)
  1.1067 +{
  1.1068 +  NS_ENSURE_TRUE(aNode, false);  // oops
  1.1069 +  uint32_t len;
  1.1070 +  GetLengthOfDOMNode(aNode, len);
  1.1071 +  if (aOffset == (int32_t)len) return true;
  1.1072 +  
  1.1073 +  if (IsTextNode(aNode))
  1.1074 +  {
  1.1075 +    return false;
  1.1076 +  }
  1.1077 +  else
  1.1078 +  {
  1.1079 +    nsCOMPtr<nsIDOMNode> lastNode;
  1.1080 +    GetLastEditableChild(aNode, address_of(lastNode));
  1.1081 +    NS_ENSURE_TRUE(lastNode, true); 
  1.1082 +    int32_t offset = GetChildOffset(lastNode, aNode);
  1.1083 +    if (offset < aOffset) return true;
  1.1084 +    return false;
  1.1085 +  }
  1.1086 +}
  1.1087 +
  1.1088 +
  1.1089 +nsresult
  1.1090 +nsHTMLEditor::GetInlinePropertyBase(nsIAtom *aProperty, 
  1.1091 +                                    const nsAString *aAttribute,
  1.1092 +                                    const nsAString *aValue,
  1.1093 +                                    bool *aFirst, 
  1.1094 +                                    bool *aAny, 
  1.1095 +                                    bool *aAll,
  1.1096 +                                    nsAString *outValue,
  1.1097 +                                    bool aCheckDefaults)
  1.1098 +{
  1.1099 +  NS_ENSURE_TRUE(aProperty, NS_ERROR_NULL_POINTER);
  1.1100 +
  1.1101 +  nsresult result;
  1.1102 +  *aAny = false;
  1.1103 +  *aAll = true;
  1.1104 +  *aFirst = false;
  1.1105 +  bool first = true;
  1.1106 +
  1.1107 +  nsCOMPtr<nsISelection> selection;
  1.1108 +  result = GetSelection(getter_AddRefs(selection));
  1.1109 +  NS_ENSURE_SUCCESS(result, result);
  1.1110 +  NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
  1.1111 +  Selection* sel = static_cast<Selection*>(selection.get());
  1.1112 +
  1.1113 +  bool isCollapsed = selection->Collapsed();
  1.1114 +  nsCOMPtr<nsIDOMNode> collapsedNode;
  1.1115 +  nsRefPtr<nsRange> range = sel->GetRangeAt(0);
  1.1116 +  // XXX: should be a while loop, to get each separate range
  1.1117 +  // XXX: ERROR_HANDLING can currentItem be null?
  1.1118 +  if (range) {
  1.1119 +    bool firstNodeInRange = true; // for each range, set a flag 
  1.1120 +
  1.1121 +    if (isCollapsed) {
  1.1122 +      range->GetStartContainer(getter_AddRefs(collapsedNode));
  1.1123 +      NS_ENSURE_TRUE(collapsedNode, NS_ERROR_FAILURE);
  1.1124 +      bool isSet, theSetting;
  1.1125 +      nsString tOutString;
  1.1126 +      if (aAttribute) {
  1.1127 +        nsString tString(*aAttribute);
  1.1128 +        mTypeInState->GetTypingState(isSet, theSetting, aProperty, tString,
  1.1129 +                                     &tOutString);
  1.1130 +        if (outValue) {
  1.1131 +          outValue->Assign(tOutString);
  1.1132 +        }
  1.1133 +      } else {
  1.1134 +        mTypeInState->GetTypingState(isSet, theSetting, aProperty);
  1.1135 +      }
  1.1136 +      if (isSet) {
  1.1137 +        *aFirst = *aAny = *aAll = theSetting;
  1.1138 +        return NS_OK;
  1.1139 +      }
  1.1140 +
  1.1141 +      if (mHTMLCSSUtils->IsCSSEditableProperty(collapsedNode, aProperty, aAttribute)) {
  1.1142 +        mHTMLCSSUtils->IsCSSEquivalentToHTMLInlineStyleSet(
  1.1143 +          collapsedNode, aProperty, aAttribute, isSet, tOutString,
  1.1144 +          nsHTMLCSSUtils::eComputed);
  1.1145 +        if (outValue) {
  1.1146 +          outValue->Assign(tOutString);
  1.1147 +        }
  1.1148 +        *aFirst = *aAny = *aAll = isSet;
  1.1149 +        return NS_OK;
  1.1150 +      }
  1.1151 +
  1.1152 +      IsTextPropertySetByContent(collapsedNode, aProperty, aAttribute, aValue,
  1.1153 +                                 isSet, outValue);
  1.1154 +      *aFirst = *aAny = *aAll = isSet;
  1.1155 +
  1.1156 +      if (!isSet && aCheckDefaults) {
  1.1157 +        // style not set, but if it is a default then it will appear if
  1.1158 +        // content is inserted, so we should report it as set (analogous to
  1.1159 +        // TypeInState).
  1.1160 +        int32_t index;
  1.1161 +        if (aAttribute && TypeInState::FindPropInList(aProperty, *aAttribute,
  1.1162 +                                                      outValue, mDefaultStyles,
  1.1163 +                                                      index)) {
  1.1164 +          *aFirst = *aAny = *aAll = true;
  1.1165 +          if (outValue) {
  1.1166 +            outValue->Assign(mDefaultStyles[index]->value);
  1.1167 +          }
  1.1168 +        }
  1.1169 +      }
  1.1170 +      return NS_OK;
  1.1171 +    }
  1.1172 +
  1.1173 +    // non-collapsed selection
  1.1174 +    nsCOMPtr<nsIContentIterator> iter =
  1.1175 +            do_CreateInstance("@mozilla.org/content/post-content-iterator;1");
  1.1176 +    NS_ENSURE_TRUE(iter, NS_ERROR_NULL_POINTER);
  1.1177 +
  1.1178 +    nsAutoString firstValue, theValue;
  1.1179 +
  1.1180 +    nsCOMPtr<nsIDOMNode> endNode;
  1.1181 +    int32_t endOffset;
  1.1182 +    result = range->GetEndContainer(getter_AddRefs(endNode));
  1.1183 +    NS_ENSURE_SUCCESS(result, result);
  1.1184 +    result = range->GetEndOffset(&endOffset);
  1.1185 +    NS_ENSURE_SUCCESS(result, result);
  1.1186 +
  1.1187 +    for (iter->Init(range); !iter->IsDone(); iter->Next()) {
  1.1188 +      if (!iter->GetCurrentNode()->IsContent()) {
  1.1189 +        continue;
  1.1190 +      }
  1.1191 +      nsCOMPtr<nsIContent> content = iter->GetCurrentNode()->AsContent();
  1.1192 +      nsCOMPtr<nsIDOMNode> node = content->AsDOMNode();
  1.1193 +
  1.1194 +      if (nsTextEditUtils::IsBody(node)) {
  1.1195 +        break;
  1.1196 +      }
  1.1197 +
  1.1198 +      nsCOMPtr<nsIDOMCharacterData> text;
  1.1199 +      text = do_QueryInterface(content);
  1.1200 +      
  1.1201 +      // just ignore any non-editable nodes
  1.1202 +      if (text && (!IsEditable(text) || IsEmptyTextNode(this, content))) {
  1.1203 +        continue;
  1.1204 +      }
  1.1205 +      if (text) {
  1.1206 +        if (!isCollapsed && first && firstNodeInRange) {
  1.1207 +          firstNodeInRange = false;
  1.1208 +          int32_t startOffset;
  1.1209 +          range->GetStartOffset(&startOffset);
  1.1210 +          uint32_t count;
  1.1211 +          text->GetLength(&count);
  1.1212 +          if (startOffset == (int32_t)count) {
  1.1213 +            continue;
  1.1214 +          }
  1.1215 +        } else if (node == endNode && !endOffset) {
  1.1216 +          continue;
  1.1217 +        }
  1.1218 +      } else if (content->IsElement()) {
  1.1219 +        // handle non-text leaf nodes here
  1.1220 +        continue;
  1.1221 +      }
  1.1222 +
  1.1223 +      bool isSet = false;
  1.1224 +      if (first) {
  1.1225 +        if (mHTMLCSSUtils->IsCSSEditableProperty(node, aProperty, aAttribute)){
  1.1226 +          // the HTML styles defined by aProperty/aAttribute has a CSS
  1.1227 +          // equivalence in this implementation for node; let's check if it
  1.1228 +          // carries those css styles
  1.1229 +          if (aValue) {
  1.1230 +            firstValue.Assign(*aValue);
  1.1231 +          }
  1.1232 +          mHTMLCSSUtils->IsCSSEquivalentToHTMLInlineStyleSet(node, aProperty,
  1.1233 +            aAttribute, isSet, firstValue, nsHTMLCSSUtils::eComputed);
  1.1234 +        } else {
  1.1235 +          IsTextPropertySetByContent(node, aProperty, aAttribute, aValue, isSet,
  1.1236 +                                     &firstValue);
  1.1237 +        }
  1.1238 +        *aFirst = isSet;
  1.1239 +        first = false;
  1.1240 +        if (outValue) {
  1.1241 +          *outValue = firstValue;
  1.1242 +        }
  1.1243 +      } else {
  1.1244 +        if (mHTMLCSSUtils->IsCSSEditableProperty(node, aProperty, aAttribute)){
  1.1245 +          // the HTML styles defined by aProperty/aAttribute has a CSS equivalence
  1.1246 +          // in this implementation for node; let's check if it carries those css styles
  1.1247 +          if (aValue) {
  1.1248 +            theValue.Assign(*aValue);
  1.1249 +          }
  1.1250 +          mHTMLCSSUtils->IsCSSEquivalentToHTMLInlineStyleSet(node, aProperty,
  1.1251 +            aAttribute, isSet, theValue, nsHTMLCSSUtils::eComputed);
  1.1252 +        } else {
  1.1253 +          IsTextPropertySetByContent(node, aProperty, aAttribute, aValue, isSet,
  1.1254 +                                     &theValue);
  1.1255 +        }
  1.1256 +        if (firstValue != theValue) {
  1.1257 +          *aAll = false;
  1.1258 +        }
  1.1259 +      }
  1.1260 +
  1.1261 +      if (isSet) {
  1.1262 +        *aAny = true;
  1.1263 +      } else {
  1.1264 +        *aAll = false;
  1.1265 +      }
  1.1266 +    }
  1.1267 +  }
  1.1268 +  if (!*aAny) {
  1.1269 +    // make sure that if none of the selection is set, we don't report all is
  1.1270 +    // set
  1.1271 +    *aAll = false;
  1.1272 +  }
  1.1273 +  return result;
  1.1274 +}
  1.1275 +
  1.1276 +
  1.1277 +NS_IMETHODIMP nsHTMLEditor::GetInlineProperty(nsIAtom *aProperty, 
  1.1278 +                                              const nsAString &aAttribute, 
  1.1279 +                                              const nsAString &aValue,
  1.1280 +                                              bool *aFirst, 
  1.1281 +                                              bool *aAny, 
  1.1282 +                                              bool *aAll)
  1.1283 +{
  1.1284 +  NS_ENSURE_TRUE(aProperty && aFirst && aAny && aAll, NS_ERROR_NULL_POINTER);
  1.1285 +  const nsAString *att = nullptr;
  1.1286 +  if (!aAttribute.IsEmpty())
  1.1287 +    att = &aAttribute;
  1.1288 +  const nsAString *val = nullptr;
  1.1289 +  if (!aValue.IsEmpty())
  1.1290 +    val = &aValue;
  1.1291 +  return GetInlinePropertyBase( aProperty, att, val, aFirst, aAny, aAll, nullptr);
  1.1292 +}
  1.1293 +
  1.1294 +
  1.1295 +NS_IMETHODIMP nsHTMLEditor::GetInlinePropertyWithAttrValue(nsIAtom *aProperty, 
  1.1296 +                                              const nsAString &aAttribute, 
  1.1297 +                                              const nsAString &aValue,
  1.1298 +                                              bool *aFirst, 
  1.1299 +                                              bool *aAny, 
  1.1300 +                                              bool *aAll,
  1.1301 +                                              nsAString &outValue)
  1.1302 +{
  1.1303 +  NS_ENSURE_TRUE(aProperty && aFirst && aAny && aAll, NS_ERROR_NULL_POINTER);
  1.1304 +  const nsAString *att = nullptr;
  1.1305 +  if (!aAttribute.IsEmpty())
  1.1306 +    att = &aAttribute;
  1.1307 +  const nsAString *val = nullptr;
  1.1308 +  if (!aValue.IsEmpty())
  1.1309 +    val = &aValue;
  1.1310 +  return GetInlinePropertyBase( aProperty, att, val, aFirst, aAny, aAll, &outValue);
  1.1311 +}
  1.1312 +
  1.1313 +
  1.1314 +NS_IMETHODIMP nsHTMLEditor::RemoveAllInlineProperties()
  1.1315 +{
  1.1316 +  nsAutoEditBatch batchIt(this);
  1.1317 +  nsAutoRules beginRulesSniffing(this, EditAction::resetTextProperties, nsIEditor::eNext);
  1.1318 +
  1.1319 +  nsresult res = RemoveInlinePropertyImpl(nullptr, nullptr);
  1.1320 +  NS_ENSURE_SUCCESS(res, res);
  1.1321 +  return ApplyDefaultProperties();
  1.1322 +}
  1.1323 +
  1.1324 +NS_IMETHODIMP nsHTMLEditor::RemoveInlineProperty(nsIAtom *aProperty, const nsAString &aAttribute)
  1.1325 +{
  1.1326 +  return RemoveInlinePropertyImpl(aProperty, &aAttribute);
  1.1327 +}
  1.1328 +
  1.1329 +nsresult nsHTMLEditor::RemoveInlinePropertyImpl(nsIAtom *aProperty, const nsAString *aAttribute)
  1.1330 +{
  1.1331 +  MOZ_ASSERT_IF(aProperty, aAttribute);
  1.1332 +  NS_ENSURE_TRUE(mRules, NS_ERROR_NOT_INITIALIZED);
  1.1333 +  ForceCompositionEnd();
  1.1334 +
  1.1335 +  nsresult res;
  1.1336 +  nsRefPtr<Selection> selection = GetSelection();
  1.1337 +  NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
  1.1338 +
  1.1339 +  bool useCSS = IsCSSEnabled();
  1.1340 +  if (selection->Collapsed()) {
  1.1341 +    // manipulating text attributes on a collapsed selection only sets state for the next text insertion
  1.1342 +
  1.1343 +    // For links, aProperty uses "href", use "a" instead
  1.1344 +    if (aProperty == nsEditProperty::href ||
  1.1345 +        aProperty == nsEditProperty::name)
  1.1346 +      aProperty = nsEditProperty::a;
  1.1347 +
  1.1348 +    if (aProperty) {
  1.1349 +      mTypeInState->ClearProp(aProperty, *aAttribute);
  1.1350 +    } else {
  1.1351 +      mTypeInState->ClearAllProps();
  1.1352 +    }
  1.1353 +    return NS_OK;
  1.1354 +  }
  1.1355 +
  1.1356 +  nsAutoEditBatch batchIt(this);
  1.1357 +  nsAutoRules beginRulesSniffing(this, EditAction::removeTextProperty, nsIEditor::eNext);
  1.1358 +  nsAutoSelectionReset selectionResetter(selection, this);
  1.1359 +  nsAutoTxnsConserveSelection dontSpazMySelection(this);
  1.1360 +  
  1.1361 +  bool cancel, handled;
  1.1362 +  nsTextRulesInfo ruleInfo(EditAction::removeTextProperty);
  1.1363 +  // Protect the edit rules object from dying
  1.1364 +  nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules);
  1.1365 +  res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
  1.1366 +  NS_ENSURE_SUCCESS(res, res);
  1.1367 +  if (!cancel && !handled)
  1.1368 +  {
  1.1369 +    // loop thru the ranges in the selection
  1.1370 +    uint32_t rangeCount = selection->GetRangeCount();
  1.1371 +    for (uint32_t rangeIdx = 0; rangeIdx < rangeCount; ++rangeIdx) {
  1.1372 +      nsRefPtr<nsRange> range = selection->GetRangeAt(rangeIdx);
  1.1373 +      if (aProperty == nsEditProperty::name)
  1.1374 +      {
  1.1375 +        // promote range if it starts or end in a named anchor and we
  1.1376 +        // want to remove named anchors
  1.1377 +        res = PromoteRangeIfStartsOrEndsInNamedAnchor(range);
  1.1378 +      }
  1.1379 +      else {
  1.1380 +        // adjust range to include any ancestors who's children are entirely selected
  1.1381 +        res = PromoteInlineRange(range);
  1.1382 +      }
  1.1383 +      NS_ENSURE_SUCCESS(res, res);
  1.1384 +
  1.1385 +      // remove this style from ancestors of our range endpoints, 
  1.1386 +      // splitting them as appropriate
  1.1387 +      res = SplitStyleAboveRange(range, aProperty, aAttribute);
  1.1388 +      NS_ENSURE_SUCCESS(res, res);
  1.1389 +
  1.1390 +      // check for easy case: both range endpoints in same text node
  1.1391 +      nsCOMPtr<nsIDOMNode> startNode, endNode;
  1.1392 +      res = range->GetStartContainer(getter_AddRefs(startNode));
  1.1393 +      NS_ENSURE_SUCCESS(res, res);
  1.1394 +      res = range->GetEndContainer(getter_AddRefs(endNode));
  1.1395 +      NS_ENSURE_SUCCESS(res, res);
  1.1396 +      if ((startNode == endNode) && IsTextNode(startNode))
  1.1397 +      {
  1.1398 +        // we're done with this range!
  1.1399 +        if (useCSS && mHTMLCSSUtils->IsCSSEditableProperty(startNode, aProperty, aAttribute)) {
  1.1400 +          // the HTML style defined by aProperty/aAttribute has a CSS equivalence
  1.1401 +          // in this implementation for startNode
  1.1402 +          nsAutoString cssValue;
  1.1403 +          bool isSet = false;
  1.1404 +          mHTMLCSSUtils->IsCSSEquivalentToHTMLInlineStyleSet(startNode,
  1.1405 +            aProperty, aAttribute, isSet , cssValue,
  1.1406 +            nsHTMLCSSUtils::eComputed);
  1.1407 +          if (isSet) {
  1.1408 +            // startNode's computed style indicates the CSS equivalence to the HTML style to
  1.1409 +            // remove is applied; but we found no element in the ancestors of startNode
  1.1410 +            // carrying specified styles; assume it comes from a rule and let's try to
  1.1411 +            // insert a span "inverting" the style
  1.1412 +            nsAutoString value; value.AssignLiteral("-moz-editor-invert-value");
  1.1413 +            int32_t startOffset, endOffset;
  1.1414 +            range->GetStartOffset(&startOffset);
  1.1415 +            range->GetEndOffset(&endOffset);
  1.1416 +            nsCOMPtr<nsIDOMCharacterData> nodeAsText = do_QueryInterface(startNode);
  1.1417 +            if (mHTMLCSSUtils->IsCSSInvertable(aProperty, aAttribute)) {
  1.1418 +              SetInlinePropertyOnTextNode(nodeAsText, startOffset, endOffset, aProperty, aAttribute, &value);
  1.1419 +            }
  1.1420 +          }
  1.1421 +        }
  1.1422 +      }
  1.1423 +      else
  1.1424 +      {
  1.1425 +        // not the easy case.  range not contained in single text node. 
  1.1426 +        nsCOMPtr<nsIContentIterator> iter =
  1.1427 +          do_CreateInstance("@mozilla.org/content/subtree-content-iterator;1", &res);
  1.1428 +        NS_ENSURE_SUCCESS(res, res);
  1.1429 +        NS_ENSURE_TRUE(iter, NS_ERROR_FAILURE);
  1.1430 +
  1.1431 +        nsCOMArray<nsIDOMNode> arrayOfNodes;
  1.1432 +        nsCOMPtr<nsIDOMNode> node;
  1.1433 +        
  1.1434 +        // iterate range and build up array
  1.1435 +        iter->Init(range);
  1.1436 +        while (!iter->IsDone())
  1.1437 +        {
  1.1438 +          node = do_QueryInterface(iter->GetCurrentNode());
  1.1439 +          NS_ENSURE_TRUE(node, NS_ERROR_FAILURE);
  1.1440 +
  1.1441 +          if (IsEditable(node))
  1.1442 +          { 
  1.1443 +            arrayOfNodes.AppendObject(node);
  1.1444 +          }
  1.1445 +
  1.1446 +          iter->Next();
  1.1447 +        }
  1.1448 +        
  1.1449 +        // loop through the list, remove the property on each node
  1.1450 +        int32_t listCount = arrayOfNodes.Count();
  1.1451 +        int32_t j;
  1.1452 +        for (j = 0; j < listCount; j++)
  1.1453 +        {
  1.1454 +          node = arrayOfNodes[j];
  1.1455 +          res = RemoveStyleInside(node, aProperty, aAttribute);
  1.1456 +          NS_ENSURE_SUCCESS(res, res);
  1.1457 +          if (useCSS && mHTMLCSSUtils->IsCSSEditableProperty(node, aProperty, aAttribute)) {
  1.1458 +            // the HTML style defined by aProperty/aAttribute has a CSS equivalence
  1.1459 +            // in this implementation for node
  1.1460 +            nsAutoString cssValue;
  1.1461 +            bool isSet = false;
  1.1462 +            mHTMLCSSUtils->IsCSSEquivalentToHTMLInlineStyleSet(node, aProperty,
  1.1463 +              aAttribute, isSet , cssValue, nsHTMLCSSUtils::eComputed);
  1.1464 +            if (isSet) {
  1.1465 +              // startNode's computed style indicates the CSS equivalence to the HTML style to
  1.1466 +              // remove is applied; but we found no element in the ancestors of startNode
  1.1467 +              // carrying specified styles; assume it comes from a rule and let's try to
  1.1468 +              // insert a span "inverting" the style
  1.1469 +              if (mHTMLCSSUtils->IsCSSInvertable(aProperty, aAttribute)) {
  1.1470 +                nsAutoString value; value.AssignLiteral("-moz-editor-invert-value");
  1.1471 +                SetInlinePropertyOnNode(node, aProperty, aAttribute, &value);
  1.1472 +              }
  1.1473 +            }
  1.1474 +          }
  1.1475 +        }
  1.1476 +        arrayOfNodes.Clear();
  1.1477 +      }
  1.1478 +    }
  1.1479 +  }
  1.1480 +  if (!cancel)
  1.1481 +  {
  1.1482 +    // post-process 
  1.1483 +    res = mRules->DidDoAction(selection, &ruleInfo, res);
  1.1484 +  }
  1.1485 +  return res;
  1.1486 +}
  1.1487 +
  1.1488 +NS_IMETHODIMP nsHTMLEditor::IncreaseFontSize()
  1.1489 +{
  1.1490 +  return RelativeFontChange(1);
  1.1491 +}
  1.1492 +
  1.1493 +NS_IMETHODIMP nsHTMLEditor::DecreaseFontSize()
  1.1494 +{
  1.1495 +  return RelativeFontChange(-1);
  1.1496 +}
  1.1497 +
  1.1498 +nsresult
  1.1499 +nsHTMLEditor::RelativeFontChange( int32_t aSizeChange)
  1.1500 +{
  1.1501 +  // Can only change font size by + or - 1
  1.1502 +  if ( !( (aSizeChange==1) || (aSizeChange==-1) ) )
  1.1503 +    return NS_ERROR_ILLEGAL_VALUE;
  1.1504 +  
  1.1505 +  ForceCompositionEnd();
  1.1506 +
  1.1507 +  // Get the selection 
  1.1508 +  nsRefPtr<Selection> selection = GetSelection();
  1.1509 +  NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
  1.1510 +  // Is the selection collapsed?
  1.1511 +  // if it's collapsed set typing state
  1.1512 +  if (selection->Collapsed()) {
  1.1513 +    nsCOMPtr<nsIAtom> atom;
  1.1514 +    if (aSizeChange==1) atom = nsEditProperty::big;
  1.1515 +    else                atom = nsEditProperty::small;
  1.1516 +
  1.1517 +    // Let's see in what kind of element the selection is
  1.1518 +    int32_t offset;
  1.1519 +    nsCOMPtr<nsIDOMNode> selectedNode;
  1.1520 +    GetStartNodeAndOffset(selection, getter_AddRefs(selectedNode), &offset);
  1.1521 +    NS_ENSURE_TRUE(selectedNode, NS_OK);
  1.1522 +    if (IsTextNode(selectedNode)) {
  1.1523 +      nsCOMPtr<nsIDOMNode> parent;
  1.1524 +      nsresult res = selectedNode->GetParentNode(getter_AddRefs(parent));
  1.1525 +      NS_ENSURE_SUCCESS(res, res);
  1.1526 +      selectedNode = parent;
  1.1527 +    }
  1.1528 +    if (!CanContainTag(selectedNode, atom)) {
  1.1529 +      return NS_OK;
  1.1530 +    }
  1.1531 +
  1.1532 +    // manipulating text attributes on a collapsed selection only sets state for the next text insertion
  1.1533 +    mTypeInState->SetProp(atom, EmptyString(), EmptyString());
  1.1534 +    return NS_OK;
  1.1535 +  }
  1.1536 +  
  1.1537 +  // wrap with txn batching, rules sniffing, and selection preservation code
  1.1538 +  nsAutoEditBatch batchIt(this);
  1.1539 +  nsAutoRules beginRulesSniffing(this, EditAction::setTextProperty, nsIEditor::eNext);
  1.1540 +  nsAutoSelectionReset selectionResetter(selection, this);
  1.1541 +  nsAutoTxnsConserveSelection dontSpazMySelection(this);
  1.1542 +
  1.1543 +  // loop thru the ranges in the selection
  1.1544 +  uint32_t rangeCount = selection->GetRangeCount();
  1.1545 +  for (uint32_t rangeIdx = 0; rangeIdx < rangeCount; ++rangeIdx) {
  1.1546 +    nsRefPtr<nsRange> range = selection->GetRangeAt(rangeIdx);
  1.1547 +
  1.1548 +    // adjust range to include any ancestors who's children are entirely selected
  1.1549 +    nsresult res = PromoteInlineRange(range);
  1.1550 +    NS_ENSURE_SUCCESS(res, res);
  1.1551 +    
  1.1552 +    // check for easy case: both range endpoints in same text node
  1.1553 +    nsCOMPtr<nsIDOMNode> startNode, endNode;
  1.1554 +    res = range->GetStartContainer(getter_AddRefs(startNode));
  1.1555 +    NS_ENSURE_SUCCESS(res, res);
  1.1556 +    res = range->GetEndContainer(getter_AddRefs(endNode));
  1.1557 +    NS_ENSURE_SUCCESS(res, res);
  1.1558 +    if ((startNode == endNode) && IsTextNode(startNode))
  1.1559 +    {
  1.1560 +      int32_t startOffset, endOffset;
  1.1561 +      range->GetStartOffset(&startOffset);
  1.1562 +      range->GetEndOffset(&endOffset);
  1.1563 +      nsCOMPtr<nsIDOMCharacterData> nodeAsText = do_QueryInterface(startNode);
  1.1564 +      res = RelativeFontChangeOnTextNode(aSizeChange, nodeAsText, startOffset, endOffset);
  1.1565 +      NS_ENSURE_SUCCESS(res, res);
  1.1566 +    }
  1.1567 +    else
  1.1568 +    {
  1.1569 +      // not the easy case.  range not contained in single text node. 
  1.1570 +      // there are up to three phases here.  There are all the nodes
  1.1571 +      // reported by the subtree iterator to be processed.  And there
  1.1572 +      // are potentially a starting textnode and an ending textnode
  1.1573 +      // which are only partially contained by the range.
  1.1574 +      
  1.1575 +      // lets handle the nodes reported by the iterator.  These nodes
  1.1576 +      // are entirely contained in the selection range.  We build up
  1.1577 +      // a list of them (since doing operations on the document during
  1.1578 +      // iteration would perturb the iterator).
  1.1579 +
  1.1580 +      nsCOMPtr<nsIContentIterator> iter =
  1.1581 +        do_CreateInstance("@mozilla.org/content/subtree-content-iterator;1", &res);
  1.1582 +      NS_ENSURE_SUCCESS(res, res);
  1.1583 +      NS_ENSURE_TRUE(iter, NS_ERROR_FAILURE);
  1.1584 +
  1.1585 +      // iterate range and build up array
  1.1586 +      res = iter->Init(range);
  1.1587 +      if (NS_SUCCEEDED(res)) {
  1.1588 +        nsCOMArray<nsIContent> arrayOfNodes;
  1.1589 +        while (!iter->IsDone()) {
  1.1590 +          NS_ENSURE_TRUE(iter->GetCurrentNode()->IsContent(), NS_ERROR_FAILURE);
  1.1591 +          nsCOMPtr<nsIContent> node = iter->GetCurrentNode()->AsContent();
  1.1592 +
  1.1593 +          if (IsEditable(node)) {
  1.1594 +            arrayOfNodes.AppendObject(node);
  1.1595 +          }
  1.1596 +
  1.1597 +          iter->Next();
  1.1598 +        }
  1.1599 +        
  1.1600 +        // now that we have the list, do the font size change on each node
  1.1601 +        int32_t listCount = arrayOfNodes.Count();
  1.1602 +        for (int32_t j = 0; j < listCount; ++j) {
  1.1603 +          nsIContent* node = arrayOfNodes[j];
  1.1604 +          res = RelativeFontChangeOnNode(aSizeChange, node);
  1.1605 +          NS_ENSURE_SUCCESS(res, res);
  1.1606 +        }
  1.1607 +        arrayOfNodes.Clear();
  1.1608 +      }
  1.1609 +      // now check the start and end parents of the range to see if they need to 
  1.1610 +      // be separately handled (they do if they are text nodes, due to how the
  1.1611 +      // subtree iterator works - it will not have reported them).
  1.1612 +      if (IsTextNode(startNode) && IsEditable(startNode))
  1.1613 +      {
  1.1614 +        nsCOMPtr<nsIDOMCharacterData> nodeAsText = do_QueryInterface(startNode);
  1.1615 +        int32_t startOffset;
  1.1616 +        uint32_t textLen;
  1.1617 +        range->GetStartOffset(&startOffset);
  1.1618 +        nodeAsText->GetLength(&textLen);
  1.1619 +        res = RelativeFontChangeOnTextNode(aSizeChange, nodeAsText, startOffset, textLen);
  1.1620 +        NS_ENSURE_SUCCESS(res, res);
  1.1621 +      }
  1.1622 +      if (IsTextNode(endNode) && IsEditable(endNode))
  1.1623 +      {
  1.1624 +        nsCOMPtr<nsIDOMCharacterData> nodeAsText = do_QueryInterface(endNode);
  1.1625 +        int32_t endOffset;
  1.1626 +        range->GetEndOffset(&endOffset);
  1.1627 +        res = RelativeFontChangeOnTextNode(aSizeChange, nodeAsText, 0, endOffset);
  1.1628 +        NS_ENSURE_SUCCESS(res, res);
  1.1629 +      }
  1.1630 +    }
  1.1631 +  }
  1.1632 +  
  1.1633 +  return NS_OK;  
  1.1634 +}
  1.1635 +
  1.1636 +nsresult
  1.1637 +nsHTMLEditor::RelativeFontChangeOnTextNode( int32_t aSizeChange, 
  1.1638 +                                            nsIDOMCharacterData *aTextNode, 
  1.1639 +                                            int32_t aStartOffset,
  1.1640 +                                            int32_t aEndOffset)
  1.1641 +{
  1.1642 +  // Can only change font size by + or - 1
  1.1643 +  if ( !( (aSizeChange==1) || (aSizeChange==-1) ) )
  1.1644 +    return NS_ERROR_ILLEGAL_VALUE;
  1.1645 +  NS_ENSURE_TRUE(aTextNode, NS_ERROR_NULL_POINTER);
  1.1646 +  
  1.1647 +  // don't need to do anything if no characters actually selected
  1.1648 +  if (aStartOffset == aEndOffset) return NS_OK;
  1.1649 +  
  1.1650 +  nsresult res = NS_OK;
  1.1651 +  nsCOMPtr<nsIDOMNode> parent;
  1.1652 +  res = aTextNode->GetParentNode(getter_AddRefs(parent));
  1.1653 +  NS_ENSURE_SUCCESS(res, res);
  1.1654 +  if (!CanContainTag(parent, nsGkAtoms::big)) {
  1.1655 +    return NS_OK;
  1.1656 +  }
  1.1657 +
  1.1658 +  nsCOMPtr<nsIDOMNode> tmp, node = do_QueryInterface(aTextNode);
  1.1659 +
  1.1660 +  // do we need to split the text node?
  1.1661 +  uint32_t textLen;
  1.1662 +  aTextNode->GetLength(&textLen);
  1.1663 +  
  1.1664 +  // -1 is a magic value meaning to the end of node
  1.1665 +  if (aEndOffset == -1) aEndOffset = textLen;
  1.1666 +  
  1.1667 +  if ( (uint32_t)aEndOffset != textLen )
  1.1668 +  {
  1.1669 +    // we need to split off back of text node
  1.1670 +    res = SplitNode(node, aEndOffset, getter_AddRefs(tmp));
  1.1671 +    NS_ENSURE_SUCCESS(res, res);
  1.1672 +    node = tmp;  // remember left node
  1.1673 +  }
  1.1674 +  if ( aStartOffset )
  1.1675 +  {
  1.1676 +    // we need to split off front of text node
  1.1677 +    res = SplitNode(node, aStartOffset, getter_AddRefs(tmp));
  1.1678 +    NS_ENSURE_SUCCESS(res, res);
  1.1679 +  }
  1.1680 +
  1.1681 +  NS_NAMED_LITERAL_STRING(bigSize, "big");
  1.1682 +  NS_NAMED_LITERAL_STRING(smallSize, "small");
  1.1683 +  const nsAString& nodeType = (aSizeChange==1) ? static_cast<const nsAString&>(bigSize) : static_cast<const nsAString&>(smallSize);
  1.1684 +  // look for siblings that are correct type of node
  1.1685 +  nsCOMPtr<nsIDOMNode> sibling;
  1.1686 +  GetPriorHTMLSibling(node, address_of(sibling));
  1.1687 +  if (sibling && NodeIsType(sibling, (aSizeChange==1) ? nsEditProperty::big : nsEditProperty::small))
  1.1688 +  {
  1.1689 +    // previous sib is already right kind of inline node; slide this over into it
  1.1690 +    res = MoveNode(node, sibling, -1);
  1.1691 +    return res;
  1.1692 +  }
  1.1693 +  sibling = nullptr;
  1.1694 +  GetNextHTMLSibling(node, address_of(sibling));
  1.1695 +  if (sibling && NodeIsType(sibling, (aSizeChange==1) ? nsEditProperty::big : nsEditProperty::small))
  1.1696 +  {
  1.1697 +    // following sib is already right kind of inline node; slide this over into it
  1.1698 +    res = MoveNode(node, sibling, 0);
  1.1699 +    return res;
  1.1700 +  }
  1.1701 +  
  1.1702 +  // else reparent the node inside font node with appropriate relative size
  1.1703 +  res = InsertContainerAbove(node, address_of(tmp), nodeType);
  1.1704 +  return res;
  1.1705 +}
  1.1706 +
  1.1707 +
  1.1708 +nsresult
  1.1709 +nsHTMLEditor::RelativeFontChangeHelper(int32_t aSizeChange, nsINode* aNode)
  1.1710 +{
  1.1711 +  MOZ_ASSERT(aNode);
  1.1712 +
  1.1713 +  /*  This routine looks for all the font nodes in the tree rooted by aNode,
  1.1714 +      including aNode itself, looking for font nodes that have the size attr
  1.1715 +      set.  Any such nodes need to have big or small put inside them, since
  1.1716 +      they override any big/small that are above them.
  1.1717 +  */
  1.1718 +  
  1.1719 +  // Can only change font size by + or - 1
  1.1720 +  if (aSizeChange != 1 && aSizeChange != -1) {
  1.1721 +    return NS_ERROR_ILLEGAL_VALUE;
  1.1722 +  }
  1.1723 +
  1.1724 +  // If this is a font node with size, put big/small inside it.
  1.1725 +  if (aNode->IsElement() && aNode->AsElement()->IsHTML(nsGkAtoms::font) &&
  1.1726 +      aNode->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::size)) {
  1.1727 +    // Cycle through children and adjust relative font size.
  1.1728 +    for (uint32_t i = aNode->GetChildCount(); i--; ) {
  1.1729 +      nsresult rv = RelativeFontChangeOnNode(aSizeChange, aNode->GetChildAt(i));
  1.1730 +      NS_ENSURE_SUCCESS(rv, rv);
  1.1731 +    }
  1.1732 +
  1.1733 +    // RelativeFontChangeOnNode already calls us recursively,
  1.1734 +    // so we don't need to check our children again.
  1.1735 +    return NS_OK;
  1.1736 +  }
  1.1737 +
  1.1738 +  // Otherwise cycle through the children.
  1.1739 +  for (uint32_t i = aNode->GetChildCount(); i--; ) {
  1.1740 +    nsresult rv = RelativeFontChangeHelper(aSizeChange, aNode->GetChildAt(i));
  1.1741 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1742 +  }
  1.1743 +
  1.1744 +  return NS_OK;
  1.1745 +}
  1.1746 +
  1.1747 +
  1.1748 +nsresult
  1.1749 +nsHTMLEditor::RelativeFontChangeOnNode(int32_t aSizeChange, nsINode* aNode)
  1.1750 +{
  1.1751 +  MOZ_ASSERT(aNode);
  1.1752 +  // Can only change font size by + or - 1
  1.1753 +  if (aSizeChange != 1 && aSizeChange != -1) {
  1.1754 +    return NS_ERROR_ILLEGAL_VALUE;
  1.1755 +  }
  1.1756 +
  1.1757 +  nsIAtom* atom;
  1.1758 +  if (aSizeChange == 1) {
  1.1759 +    atom = nsGkAtoms::big;
  1.1760 +  } else {
  1.1761 +    atom = nsGkAtoms::small;
  1.1762 +  }
  1.1763 +  
  1.1764 +  // Is it the opposite of what we want?
  1.1765 +  if (aNode->IsElement() &&
  1.1766 +      ((aSizeChange == 1 && aNode->AsElement()->IsHTML(nsGkAtoms::small)) ||
  1.1767 +       (aSizeChange == -1 && aNode->AsElement()->IsHTML(nsGkAtoms::big)))) {
  1.1768 +    // first populate any nested font tags that have the size attr set
  1.1769 +    nsresult rv = RelativeFontChangeHelper(aSizeChange, aNode);
  1.1770 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1771 +    // in that case, just remove this node and pull up the children
  1.1772 +    return RemoveContainer(aNode);
  1.1773 +  }
  1.1774 +
  1.1775 +  // can it be put inside a "big" or "small"?
  1.1776 +  if (TagCanContain(atom, aNode->AsDOMNode())) {
  1.1777 +    // first populate any nested font tags that have the size attr set
  1.1778 +    nsresult rv = RelativeFontChangeHelper(aSizeChange, aNode);
  1.1779 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1780 +
  1.1781 +    // ok, chuck it in.
  1.1782 +    // first look at siblings of aNode for matching bigs or smalls.
  1.1783 +    // if we find one, move aNode into it.
  1.1784 +    nsIContent* sibling = GetPriorHTMLSibling(aNode);
  1.1785 +    if (sibling && sibling->IsHTML(atom)) {
  1.1786 +      // previous sib is already right kind of inline node; slide this over into it
  1.1787 +      return MoveNode(aNode->AsDOMNode(), sibling->AsDOMNode(), -1);
  1.1788 +    }
  1.1789 +
  1.1790 +    sibling = GetNextHTMLSibling(aNode);
  1.1791 +    if (sibling && sibling->IsHTML(atom)) {
  1.1792 +      // following sib is already right kind of inline node; slide this over into it
  1.1793 +      return MoveNode(aNode->AsDOMNode(), sibling->AsDOMNode(), 0);
  1.1794 +    }
  1.1795 +
  1.1796 +    // else insert it above aNode
  1.1797 +    nsCOMPtr<nsIDOMNode> tmp;
  1.1798 +    return InsertContainerAbove(aNode->AsDOMNode(), address_of(tmp),
  1.1799 +                                nsAtomString(atom));
  1.1800 +  }
  1.1801 +
  1.1802 +  // none of the above?  then cycle through the children.
  1.1803 +  // MOOSE: we should group the children together if possible
  1.1804 +  // into a single "big" or "small".  For the moment they are
  1.1805 +  // each getting their own.  
  1.1806 +  for (uint32_t i = aNode->GetChildCount(); i--; ) {
  1.1807 +    nsresult rv = RelativeFontChangeOnNode(aSizeChange, aNode->GetChildAt(i));
  1.1808 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1809 +  }
  1.1810 +
  1.1811 +  return NS_OK;
  1.1812 +}
  1.1813 +
  1.1814 +NS_IMETHODIMP 
  1.1815 +nsHTMLEditor::GetFontFaceState(bool *aMixed, nsAString &outFace)
  1.1816 +{
  1.1817 +  NS_ENSURE_TRUE(aMixed, NS_ERROR_FAILURE);
  1.1818 +  *aMixed = true;
  1.1819 +  outFace.Truncate();
  1.1820 +
  1.1821 +  nsresult res;
  1.1822 +  bool first, any, all;
  1.1823 +  
  1.1824 +  NS_NAMED_LITERAL_STRING(attr, "face");
  1.1825 +  res = GetInlinePropertyBase(nsEditProperty::font, &attr, nullptr, &first, &any, &all, &outFace);
  1.1826 +  NS_ENSURE_SUCCESS(res, res);
  1.1827 +  if (any && !all) return res; // mixed
  1.1828 +  if (all)
  1.1829 +  {
  1.1830 +    *aMixed = false;
  1.1831 +    return res;
  1.1832 +  }
  1.1833 +  
  1.1834 +  // if there is no font face, check for tt
  1.1835 +  res = GetInlinePropertyBase(nsEditProperty::tt, nullptr, nullptr, &first, &any, &all,nullptr);
  1.1836 +  NS_ENSURE_SUCCESS(res, res);
  1.1837 +  if (any && !all) return res; // mixed
  1.1838 +  if (all)
  1.1839 +  {
  1.1840 +    *aMixed = false;
  1.1841 +    nsEditProperty::tt->ToString(outFace);
  1.1842 +  }
  1.1843 +  
  1.1844 +  if (!any)
  1.1845 +  {
  1.1846 +    // there was no font face attrs of any kind.  We are in normal font.
  1.1847 +    outFace.Truncate();
  1.1848 +    *aMixed = false;
  1.1849 +  }
  1.1850 +  return res;
  1.1851 +}
  1.1852 +
  1.1853 +NS_IMETHODIMP 
  1.1854 +nsHTMLEditor::GetFontColorState(bool *aMixed, nsAString &aOutColor)
  1.1855 +{
  1.1856 +  NS_ENSURE_TRUE(aMixed, NS_ERROR_NULL_POINTER);
  1.1857 +  *aMixed = true;
  1.1858 +  aOutColor.Truncate();
  1.1859 +  
  1.1860 +  nsresult res;
  1.1861 +  NS_NAMED_LITERAL_STRING(colorStr, "color");
  1.1862 +  bool first, any, all;
  1.1863 +  
  1.1864 +  res = GetInlinePropertyBase(nsEditProperty::font, &colorStr, nullptr, &first, &any, &all, &aOutColor);
  1.1865 +  NS_ENSURE_SUCCESS(res, res);
  1.1866 +  if (any && !all) return res; // mixed
  1.1867 +  if (all)
  1.1868 +  {
  1.1869 +    *aMixed = false;
  1.1870 +    return res;
  1.1871 +  }
  1.1872 +  
  1.1873 +  if (!any)
  1.1874 +  {
  1.1875 +    // there was no font color attrs of any kind..
  1.1876 +    aOutColor.Truncate();
  1.1877 +    *aMixed = false;
  1.1878 +  }
  1.1879 +  return res;
  1.1880 +}
  1.1881 +
  1.1882 +// the return value is true only if the instance of the HTML editor we created
  1.1883 +// can handle CSS styles (for instance, Composer can, Messenger can't) and if
  1.1884 +// the CSS preference is checked
  1.1885 +nsresult
  1.1886 +nsHTMLEditor::GetIsCSSEnabled(bool *aIsCSSEnabled)
  1.1887 +{
  1.1888 +  *aIsCSSEnabled = IsCSSEnabled();
  1.1889 +  return NS_OK;
  1.1890 +}
  1.1891 +
  1.1892 +static bool
  1.1893 +HasNonEmptyAttribute(dom::Element* aElement, nsIAtom* aName)
  1.1894 +{
  1.1895 +  MOZ_ASSERT(aElement);
  1.1896 +
  1.1897 +  nsAutoString value;
  1.1898 +  return aElement->GetAttr(kNameSpaceID_None, aName, value) && !value.IsEmpty();
  1.1899 +}
  1.1900 +
  1.1901 +bool
  1.1902 +nsHTMLEditor::HasStyleOrIdOrClass(dom::Element* aElement)
  1.1903 +{
  1.1904 +  MOZ_ASSERT(aElement);
  1.1905 +
  1.1906 +  // remove the node if its style attribute is empty or absent,
  1.1907 +  // and if it does not have a class nor an id
  1.1908 +  return HasNonEmptyAttribute(aElement, nsGkAtoms::style) ||
  1.1909 +         HasNonEmptyAttribute(aElement, nsGkAtoms::_class) ||
  1.1910 +         HasNonEmptyAttribute(aElement, nsGkAtoms::id);
  1.1911 +}
  1.1912 +
  1.1913 +nsresult
  1.1914 +nsHTMLEditor::RemoveElementIfNoStyleOrIdOrClass(nsIDOMNode* aElement)
  1.1915 +{
  1.1916 +  nsCOMPtr<dom::Element> element = do_QueryInterface(aElement);
  1.1917 +  NS_ENSURE_TRUE(element, NS_ERROR_NULL_POINTER);
  1.1918 +
  1.1919 +  // early way out if node is not the right kind of element
  1.1920 +  if ((!element->IsHTML(nsGkAtoms::span) &&
  1.1921 +       !element->IsHTML(nsGkAtoms::font)) ||
  1.1922 +      HasStyleOrIdOrClass(element)) {
  1.1923 +    return NS_OK;
  1.1924 +  }
  1.1925 +
  1.1926 +  return RemoveContainer(element);
  1.1927 +}

mercurial