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 +}