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.

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

mercurial