1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/editor/libeditor/html/nsWSRunObject.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,2155 @@ 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 + 1.9 +#include "mozilla/Assertions.h" 1.10 +#include "mozilla/mozalloc.h" 1.11 +#include "nsAString.h" 1.12 +#include "nsAutoPtr.h" 1.13 +#include "nsCRT.h" 1.14 +#include "nsContentUtils.h" 1.15 +#include "nsDebug.h" 1.16 +#include "nsEditorUtils.h" 1.17 +#include "nsError.h" 1.18 +#include "nsHTMLEditor.h" 1.19 +#include "nsIContent.h" 1.20 +#include "nsIDOMCharacterData.h" 1.21 +#include "nsIDOMNode.h" 1.22 +#include "nsIDOMRange.h" 1.23 +#include "nsISupportsImpl.h" 1.24 +#include "nsRange.h" 1.25 +#include "nsSelectionState.h" 1.26 +#include "nsString.h" 1.27 +#include "nsTextEditUtils.h" 1.28 +#include "nsTextFragment.h" 1.29 +#include "nsWSRunObject.h" 1.30 + 1.31 +const char16_t nbsp = 160; 1.32 + 1.33 +static bool IsBlockNode(nsIDOMNode* node) 1.34 +{ 1.35 + bool isBlock (false); 1.36 + nsHTMLEditor::NodeIsBlockStatic(node, &isBlock); 1.37 + return isBlock; 1.38 +} 1.39 + 1.40 +//- constructor / destructor ----------------------------------------------- 1.41 +nsWSRunObject::nsWSRunObject(nsHTMLEditor *aEd, nsIDOMNode *aNode, int32_t aOffset) : 1.42 +mNode(aNode) 1.43 +,mOffset(aOffset) 1.44 +,mPRE(false) 1.45 +,mStartNode() 1.46 +,mStartOffset(0) 1.47 +,mStartReason() 1.48 +,mStartReasonNode() 1.49 +,mEndNode() 1.50 +,mEndOffset(0) 1.51 +,mEndReason() 1.52 +,mEndReasonNode() 1.53 +,mFirstNBSPNode() 1.54 +,mFirstNBSPOffset(0) 1.55 +,mLastNBSPNode() 1.56 +,mLastNBSPOffset(0) 1.57 +,mNodeArray() 1.58 +,mStartRun(nullptr) 1.59 +,mEndRun(nullptr) 1.60 +,mHTMLEditor(aEd) 1.61 +{ 1.62 + GetWSNodes(); 1.63 + GetRuns(); 1.64 +} 1.65 + 1.66 +nsWSRunObject::~nsWSRunObject() 1.67 +{ 1.68 + ClearRuns(); 1.69 +} 1.70 + 1.71 + 1.72 + 1.73 +//-------------------------------------------------------------------------------------------- 1.74 +// public static methods 1.75 +//-------------------------------------------------------------------------------------------- 1.76 + 1.77 +nsresult 1.78 +nsWSRunObject::ScrubBlockBoundary(nsHTMLEditor *aHTMLEd, 1.79 + nsCOMPtr<nsIDOMNode> *aBlock, 1.80 + BlockBoundary aBoundary, 1.81 + int32_t *aOffset) 1.82 +{ 1.83 + NS_ENSURE_TRUE(aBlock && aHTMLEd, NS_ERROR_NULL_POINTER); 1.84 + if ((aBoundary == kBlockStart) || (aBoundary == kBlockEnd)) 1.85 + return ScrubBlockBoundaryInner(aHTMLEd, aBlock, aBoundary); 1.86 + 1.87 + // else we are scrubbing an outer boundary - just before or after 1.88 + // a block element. 1.89 + NS_ENSURE_TRUE(aOffset, NS_ERROR_NULL_POINTER); 1.90 + nsAutoTrackDOMPoint tracker(aHTMLEd->mRangeUpdater, aBlock, aOffset); 1.91 + nsWSRunObject theWSObj(aHTMLEd, *aBlock, *aOffset); 1.92 + return theWSObj.Scrub(); 1.93 +} 1.94 + 1.95 +nsresult 1.96 +nsWSRunObject::PrepareToJoinBlocks(nsHTMLEditor *aHTMLEd, 1.97 + nsIDOMNode *aLeftParent, 1.98 + nsIDOMNode *aRightParent) 1.99 +{ 1.100 + NS_ENSURE_TRUE(aLeftParent && aRightParent && aHTMLEd, NS_ERROR_NULL_POINTER); 1.101 + uint32_t count; 1.102 + aHTMLEd->GetLengthOfDOMNode(aLeftParent, count); 1.103 + nsWSRunObject leftWSObj(aHTMLEd, aLeftParent, count); 1.104 + nsWSRunObject rightWSObj(aHTMLEd, aRightParent, 0); 1.105 + 1.106 + return leftWSObj.PrepareToDeleteRangePriv(&rightWSObj); 1.107 +} 1.108 + 1.109 +nsresult 1.110 +nsWSRunObject::PrepareToDeleteRange(nsHTMLEditor *aHTMLEd, 1.111 + nsCOMPtr<nsIDOMNode> *aStartNode, 1.112 + int32_t *aStartOffset, 1.113 + nsCOMPtr<nsIDOMNode> *aEndNode, 1.114 + int32_t *aEndOffset) 1.115 +{ 1.116 + NS_ENSURE_TRUE(aStartNode && aEndNode && *aStartNode && *aEndNode && aStartOffset && aEndOffset && aHTMLEd, NS_ERROR_NULL_POINTER); 1.117 + 1.118 + nsAutoTrackDOMPoint trackerStart(aHTMLEd->mRangeUpdater, aStartNode, aStartOffset); 1.119 + nsAutoTrackDOMPoint trackerEnd(aHTMLEd->mRangeUpdater, aEndNode, aEndOffset); 1.120 + 1.121 + nsWSRunObject leftWSObj(aHTMLEd, *aStartNode, *aStartOffset); 1.122 + nsWSRunObject rightWSObj(aHTMLEd, *aEndNode, *aEndOffset); 1.123 + 1.124 + return leftWSObj.PrepareToDeleteRangePriv(&rightWSObj); 1.125 +} 1.126 + 1.127 +nsresult 1.128 +nsWSRunObject::PrepareToDeleteNode(nsHTMLEditor *aHTMLEd, 1.129 + nsIDOMNode *aNode) 1.130 +{ 1.131 + NS_ENSURE_TRUE(aNode && aHTMLEd, NS_ERROR_NULL_POINTER); 1.132 + 1.133 + int32_t offset; 1.134 + nsCOMPtr<nsIDOMNode> parent = aHTMLEd->GetNodeLocation(aNode, &offset); 1.135 + 1.136 + nsWSRunObject leftWSObj(aHTMLEd, parent, offset); 1.137 + nsWSRunObject rightWSObj(aHTMLEd, parent, offset+1); 1.138 + 1.139 + return leftWSObj.PrepareToDeleteRangePriv(&rightWSObj); 1.140 +} 1.141 + 1.142 +nsresult 1.143 +nsWSRunObject::PrepareToSplitAcrossBlocks(nsHTMLEditor *aHTMLEd, 1.144 + nsCOMPtr<nsIDOMNode> *aSplitNode, 1.145 + int32_t *aSplitOffset) 1.146 +{ 1.147 + NS_ENSURE_TRUE(aSplitNode && aSplitOffset && *aSplitNode && aHTMLEd, NS_ERROR_NULL_POINTER); 1.148 + 1.149 + nsAutoTrackDOMPoint tracker(aHTMLEd->mRangeUpdater, aSplitNode, aSplitOffset); 1.150 + 1.151 + nsWSRunObject wsObj(aHTMLEd, *aSplitNode, *aSplitOffset); 1.152 + 1.153 + return wsObj.PrepareToSplitAcrossBlocksPriv(); 1.154 +} 1.155 + 1.156 +//-------------------------------------------------------------------------------------------- 1.157 +// public instance methods 1.158 +//-------------------------------------------------------------------------------------------- 1.159 + 1.160 +nsresult 1.161 +nsWSRunObject::InsertBreak(nsCOMPtr<nsIDOMNode> *aInOutParent, 1.162 + int32_t *aInOutOffset, 1.163 + nsCOMPtr<nsIDOMNode> *outBRNode, 1.164 + nsIEditor::EDirection aSelect) 1.165 +{ 1.166 + // MOOSE: for now, we always assume non-PRE formatting. Fix this later. 1.167 + // meanwhile, the pre case is handled in WillInsertText in nsHTMLEditRules.cpp 1.168 + NS_ENSURE_TRUE(aInOutParent && aInOutOffset && outBRNode, NS_ERROR_NULL_POINTER); 1.169 + 1.170 + nsresult res = NS_OK; 1.171 + WSFragment *beforeRun, *afterRun; 1.172 + FindRun(*aInOutParent, *aInOutOffset, &beforeRun, false); 1.173 + FindRun(*aInOutParent, *aInOutOffset, &afterRun, true); 1.174 + 1.175 + { 1.176 + // some scoping for nsAutoTrackDOMPoint. This will track our insertion point 1.177 + // while we tweak any surrounding whitespace 1.178 + nsAutoTrackDOMPoint tracker(mHTMLEditor->mRangeUpdater, aInOutParent, aInOutOffset); 1.179 + 1.180 + // handle any changes needed to ws run after inserted br 1.181 + if (!afterRun) { 1.182 + // don't need to do anything. just insert break. ws won't change. 1.183 + } else if (afterRun->mType & WSType::trailingWS) { 1.184 + // don't need to do anything. just insert break. ws won't change. 1.185 + } else if (afterRun->mType & WSType::leadingWS) { 1.186 + // delete the leading ws that is after insertion point. We don't 1.187 + // have to (it would still not be significant after br), but it's 1.188 + // just more aesthetically pleasing to. 1.189 + res = DeleteChars(*aInOutParent, *aInOutOffset, afterRun->mEndNode, afterRun->mEndOffset, 1.190 + eOutsideUserSelectAll); 1.191 + NS_ENSURE_SUCCESS(res, res); 1.192 + } else if (afterRun->mType == WSType::normalWS) { 1.193 + // need to determine if break at front of non-nbsp run. if so 1.194 + // convert run to nbsp. 1.195 + WSPoint thePoint = GetCharAfter(*aInOutParent, *aInOutOffset); 1.196 + if (thePoint.mTextNode && nsCRT::IsAsciiSpace(thePoint.mChar)) { 1.197 + WSPoint prevPoint = GetCharBefore(thePoint); 1.198 + if (prevPoint.mTextNode && !nsCRT::IsAsciiSpace(prevPoint.mChar)) { 1.199 + // we are at start of non-nbsps. convert to a single nbsp. 1.200 + res = ConvertToNBSP(thePoint); 1.201 + NS_ENSURE_SUCCESS(res, res); 1.202 + } 1.203 + } 1.204 + } 1.205 + 1.206 + // handle any changes needed to ws run before inserted br 1.207 + if (!beforeRun) { 1.208 + // don't need to do anything. just insert break. ws won't change. 1.209 + } else if (beforeRun->mType & WSType::leadingWS) { 1.210 + // don't need to do anything. just insert break. ws won't change. 1.211 + } else if (beforeRun->mType & WSType::trailingWS) { 1.212 + // need to delete the trailing ws that is before insertion point, because it 1.213 + // would become significant after break inserted. 1.214 + res = DeleteChars(beforeRun->mStartNode, beforeRun->mStartOffset, *aInOutParent, *aInOutOffset, 1.215 + eOutsideUserSelectAll); 1.216 + NS_ENSURE_SUCCESS(res, res); 1.217 + } else if (beforeRun->mType == WSType::normalWS) { 1.218 + // try to change an nbsp to a space, if possible, just to prevent nbsp proliferation 1.219 + res = CheckTrailingNBSP(beforeRun, *aInOutParent, *aInOutOffset); 1.220 + NS_ENSURE_SUCCESS(res, res); 1.221 + } 1.222 + } 1.223 + 1.224 + // ready, aim, fire! 1.225 + return mHTMLEditor->CreateBRImpl(aInOutParent, aInOutOffset, outBRNode, aSelect); 1.226 +} 1.227 + 1.228 +nsresult 1.229 +nsWSRunObject::InsertText(const nsAString& aStringToInsert, 1.230 + nsCOMPtr<nsIDOMNode> *aInOutParent, 1.231 + int32_t *aInOutOffset, 1.232 + nsIDOMDocument *aDoc) 1.233 +{ 1.234 + // MOOSE: for now, we always assume non-PRE formatting. Fix this later. 1.235 + // meanwhile, the pre case is handled in WillInsertText in nsHTMLEditRules.cpp 1.236 + 1.237 + // MOOSE: for now, just getting the ws logic straight. This implementation 1.238 + // is very slow. Will need to replace edit rules impl with a more efficient 1.239 + // text sink here that does the minimal amount of searching/replacing/copying 1.240 + 1.241 + NS_ENSURE_TRUE(aInOutParent && aInOutOffset && aDoc, NS_ERROR_NULL_POINTER); 1.242 + 1.243 + nsresult res = NS_OK; 1.244 + if (aStringToInsert.IsEmpty()) return res; 1.245 + 1.246 + // string copying sux. 1.247 + nsAutoString theString(aStringToInsert); 1.248 + 1.249 + WSFragment *beforeRun, *afterRun; 1.250 + FindRun(*aInOutParent, *aInOutOffset, &beforeRun, false); 1.251 + FindRun(*aInOutParent, *aInOutOffset, &afterRun, true); 1.252 + 1.253 + { 1.254 + // some scoping for nsAutoTrackDOMPoint. This will track our insertion point 1.255 + // while we tweak any surrounding whitespace 1.256 + nsAutoTrackDOMPoint tracker(mHTMLEditor->mRangeUpdater, aInOutParent, aInOutOffset); 1.257 + 1.258 + // handle any changes needed to ws run after inserted text 1.259 + if (!afterRun) { 1.260 + // don't need to do anything. just insert text. ws won't change. 1.261 + } else if (afterRun->mType & WSType::trailingWS) { 1.262 + // don't need to do anything. just insert text. ws won't change. 1.263 + } else if (afterRun->mType & WSType::leadingWS) { 1.264 + // delete the leading ws that is after insertion point, because it 1.265 + // would become significant after text inserted. 1.266 + res = DeleteChars(*aInOutParent, *aInOutOffset, afterRun->mEndNode, afterRun->mEndOffset, 1.267 + eOutsideUserSelectAll); 1.268 + NS_ENSURE_SUCCESS(res, res); 1.269 + } else if (afterRun->mType == WSType::normalWS) { 1.270 + // try to change an nbsp to a space, if possible, just to prevent nbsp proliferation 1.271 + res = CheckLeadingNBSP(afterRun, *aInOutParent, *aInOutOffset); 1.272 + NS_ENSURE_SUCCESS(res, res); 1.273 + } 1.274 + 1.275 + // handle any changes needed to ws run before inserted text 1.276 + if (!beforeRun) { 1.277 + // don't need to do anything. just insert text. ws won't change. 1.278 + } else if (beforeRun->mType & WSType::leadingWS) { 1.279 + // don't need to do anything. just insert text. ws won't change. 1.280 + } else if (beforeRun->mType & WSType::trailingWS) { 1.281 + // need to delete the trailing ws that is before insertion point, because it 1.282 + // would become significant after text inserted. 1.283 + res = DeleteChars(beforeRun->mStartNode, beforeRun->mStartOffset, *aInOutParent, *aInOutOffset, 1.284 + eOutsideUserSelectAll); 1.285 + NS_ENSURE_SUCCESS(res, res); 1.286 + } else if (beforeRun->mType == WSType::normalWS) { 1.287 + // try to change an nbsp to a space, if possible, just to prevent nbsp proliferation 1.288 + res = CheckTrailingNBSP(beforeRun, *aInOutParent, *aInOutOffset); 1.289 + NS_ENSURE_SUCCESS(res, res); 1.290 + } 1.291 + } 1.292 + 1.293 + // next up, tweak head and tail of string as needed. 1.294 + // first the head: 1.295 + // there are a variety of circumstances that would require us to convert a 1.296 + // leading ws char into an nbsp: 1.297 + 1.298 + if (nsCRT::IsAsciiSpace(theString[0])) 1.299 + { 1.300 + // we have a leading space 1.301 + if (beforeRun) { 1.302 + if (beforeRun->mType & WSType::leadingWS) { 1.303 + theString.SetCharAt(nbsp, 0); 1.304 + } else if (beforeRun->mType & WSType::normalWS) { 1.305 + WSPoint wspoint = GetCharBefore(*aInOutParent, *aInOutOffset); 1.306 + if (wspoint.mTextNode && nsCRT::IsAsciiSpace(wspoint.mChar)) { 1.307 + theString.SetCharAt(nbsp, 0); 1.308 + } 1.309 + } 1.310 + } else { 1.311 + if (mStartReason & WSType::block || mStartReason == WSType::br) { 1.312 + theString.SetCharAt(nbsp, 0); 1.313 + } 1.314 + } 1.315 + } 1.316 + 1.317 + // then the tail 1.318 + uint32_t lastCharIndex = theString.Length()-1; 1.319 + 1.320 + if (nsCRT::IsAsciiSpace(theString[lastCharIndex])) 1.321 + { 1.322 + // we have a leading space 1.323 + if (afterRun) 1.324 + { 1.325 + if (afterRun->mType & WSType::trailingWS) { 1.326 + theString.SetCharAt(nbsp, lastCharIndex); 1.327 + } else if (afterRun->mType & WSType::normalWS) { 1.328 + WSPoint wspoint = GetCharAfter(*aInOutParent, *aInOutOffset); 1.329 + if (wspoint.mTextNode && nsCRT::IsAsciiSpace(wspoint.mChar)) { 1.330 + theString.SetCharAt(nbsp, lastCharIndex); 1.331 + } 1.332 + } 1.333 + } 1.334 + else 1.335 + { 1.336 + if (mEndReason & WSType::block) { 1.337 + theString.SetCharAt(nbsp, lastCharIndex); 1.338 + } 1.339 + } 1.340 + } 1.341 + 1.342 + // next scan string for adjacent ws and convert to nbsp/space combos 1.343 + // MOOSE: don't need to convert tabs here since that is done by WillInsertText() 1.344 + // before we are called. Eventually, all that logic will be pushed down into 1.345 + // here and made more efficient. 1.346 + uint32_t j; 1.347 + bool prevWS = false; 1.348 + for (j=0; j<=lastCharIndex; j++) 1.349 + { 1.350 + if (nsCRT::IsAsciiSpace(theString[j])) 1.351 + { 1.352 + if (prevWS) 1.353 + { 1.354 + theString.SetCharAt(nbsp, j-1); // j-1 can't be negative because prevWS starts out false 1.355 + } 1.356 + else 1.357 + { 1.358 + prevWS = true; 1.359 + } 1.360 + } 1.361 + else 1.362 + { 1.363 + prevWS = false; 1.364 + } 1.365 + } 1.366 + 1.367 + // ready, aim, fire! 1.368 + res = mHTMLEditor->InsertTextImpl(theString, aInOutParent, aInOutOffset, aDoc); 1.369 + return NS_OK; 1.370 +} 1.371 + 1.372 +nsresult 1.373 +nsWSRunObject::DeleteWSBackward() 1.374 +{ 1.375 + nsresult res = NS_OK; 1.376 + WSPoint point = GetCharBefore(mNode, mOffset); 1.377 + NS_ENSURE_TRUE(point.mTextNode, NS_OK); // nothing to delete 1.378 + 1.379 + if (mPRE) // easy case, preformatted ws 1.380 + { 1.381 + if (nsCRT::IsAsciiSpace(point.mChar) || (point.mChar == nbsp)) 1.382 + { 1.383 + nsCOMPtr<nsIDOMNode> node(do_QueryInterface(point.mTextNode)); 1.384 + int32_t startOffset = point.mOffset; 1.385 + int32_t endOffset = point.mOffset+1; 1.386 + return DeleteChars(node, startOffset, node, endOffset); 1.387 + } 1.388 + } 1.389 + 1.390 + // callers job to insure that previous char is really ws. 1.391 + // If it is normal ws, we need to delete the whole run 1.392 + if (nsCRT::IsAsciiSpace(point.mChar)) 1.393 + { 1.394 + nsCOMPtr<nsIDOMNode> startNode, endNode, node(do_QueryInterface(point.mTextNode)); 1.395 + int32_t startOffset, endOffset; 1.396 + GetAsciiWSBounds(eBoth, node, point.mOffset+1, address_of(startNode), 1.397 + &startOffset, address_of(endNode), &endOffset); 1.398 + 1.399 + // adjust surrounding ws 1.400 + res = nsWSRunObject::PrepareToDeleteRange(mHTMLEditor, address_of(startNode), &startOffset, 1.401 + address_of(endNode), &endOffset); 1.402 + NS_ENSURE_SUCCESS(res, res); 1.403 + 1.404 + // finally, delete that ws 1.405 + return DeleteChars(startNode, startOffset, endNode, endOffset); 1.406 + } 1.407 + else if (point.mChar == nbsp) 1.408 + { 1.409 + nsCOMPtr<nsIDOMNode> node(do_QueryInterface(point.mTextNode)); 1.410 + // adjust surrounding ws 1.411 + int32_t startOffset = point.mOffset; 1.412 + int32_t endOffset = point.mOffset+1; 1.413 + res = nsWSRunObject::PrepareToDeleteRange(mHTMLEditor, address_of(node), &startOffset, 1.414 + address_of(node), &endOffset); 1.415 + NS_ENSURE_SUCCESS(res, res); 1.416 + 1.417 + // finally, delete that ws 1.418 + return DeleteChars(node, startOffset, node, endOffset); 1.419 + 1.420 + } 1.421 + return NS_OK; 1.422 +} 1.423 + 1.424 +nsresult 1.425 +nsWSRunObject::DeleteWSForward() 1.426 +{ 1.427 + nsresult res = NS_OK; 1.428 + WSPoint point = GetCharAfter(mNode, mOffset); 1.429 + NS_ENSURE_TRUE(point.mTextNode, NS_OK); // nothing to delete 1.430 + 1.431 + if (mPRE) // easy case, preformatted ws 1.432 + { 1.433 + if (nsCRT::IsAsciiSpace(point.mChar) || (point.mChar == nbsp)) 1.434 + { 1.435 + nsCOMPtr<nsIDOMNode> node(do_QueryInterface(point.mTextNode)); 1.436 + int32_t startOffset = point.mOffset; 1.437 + int32_t endOffset = point.mOffset+1; 1.438 + return DeleteChars(node, startOffset, node, endOffset); 1.439 + } 1.440 + } 1.441 + 1.442 + // callers job to insure that next char is really ws. 1.443 + // If it is normal ws, we need to delete the whole run 1.444 + if (nsCRT::IsAsciiSpace(point.mChar)) 1.445 + { 1.446 + nsCOMPtr<nsIDOMNode> startNode, endNode, node(do_QueryInterface(point.mTextNode)); 1.447 + int32_t startOffset, endOffset; 1.448 + GetAsciiWSBounds(eBoth, node, point.mOffset+1, address_of(startNode), 1.449 + &startOffset, address_of(endNode), &endOffset); 1.450 + 1.451 + // adjust surrounding ws 1.452 + res = nsWSRunObject::PrepareToDeleteRange(mHTMLEditor, address_of(startNode), &startOffset, 1.453 + address_of(endNode), &endOffset); 1.454 + NS_ENSURE_SUCCESS(res, res); 1.455 + 1.456 + // finally, delete that ws 1.457 + return DeleteChars(startNode, startOffset, endNode, endOffset); 1.458 + } 1.459 + else if (point.mChar == nbsp) 1.460 + { 1.461 + nsCOMPtr<nsIDOMNode> node(do_QueryInterface(point.mTextNode)); 1.462 + // adjust surrounding ws 1.463 + int32_t startOffset = point.mOffset; 1.464 + int32_t endOffset = point.mOffset+1; 1.465 + res = nsWSRunObject::PrepareToDeleteRange(mHTMLEditor, address_of(node), &startOffset, 1.466 + address_of(node), &endOffset); 1.467 + NS_ENSURE_SUCCESS(res, res); 1.468 + 1.469 + // finally, delete that ws 1.470 + return DeleteChars(node, startOffset, node, endOffset); 1.471 + 1.472 + } 1.473 + return NS_OK; 1.474 +} 1.475 + 1.476 +void 1.477 +nsWSRunObject::PriorVisibleNode(nsIDOMNode *aNode, 1.478 + int32_t aOffset, 1.479 + nsCOMPtr<nsIDOMNode> *outVisNode, 1.480 + int32_t *outVisOffset, 1.481 + WSType *outType) 1.482 +{ 1.483 + // Find first visible thing before the point. position outVisNode/outVisOffset 1.484 + // just _after_ that thing. If we don't find anything return start of ws. 1.485 + MOZ_ASSERT(aNode && outVisNode && outVisOffset && outType); 1.486 + 1.487 + *outType = WSType::none; 1.488 + WSFragment *run; 1.489 + FindRun(aNode, aOffset, &run, false); 1.490 + 1.491 + // is there a visible run there or earlier? 1.492 + while (run) 1.493 + { 1.494 + if (run->mType == WSType::normalWS) { 1.495 + WSPoint point = GetCharBefore(aNode, aOffset); 1.496 + if (point.mTextNode) 1.497 + { 1.498 + *outVisNode = do_QueryInterface(point.mTextNode); 1.499 + *outVisOffset = point.mOffset+1; 1.500 + if (nsCRT::IsAsciiSpace(point.mChar) || (point.mChar==nbsp)) 1.501 + { 1.502 + *outType = WSType::normalWS; 1.503 + } 1.504 + else if (!point.mChar) 1.505 + { 1.506 + // MOOSE: not possible? 1.507 + *outType = WSType::none; 1.508 + } 1.509 + else 1.510 + { 1.511 + *outType = WSType::text; 1.512 + } 1.513 + return; 1.514 + } 1.515 + // else if no text node then keep looking. We should eventually fall out of loop 1.516 + } 1.517 + 1.518 + run = run->mLeft; 1.519 + } 1.520 + 1.521 + // if we get here then nothing in ws data to find. return start reason 1.522 + *outVisNode = mStartReasonNode; 1.523 + *outVisOffset = mStartOffset; // this really isn't meaningful if mStartReasonNode!=mStartNode 1.524 + *outType = mStartReason; 1.525 +} 1.526 + 1.527 + 1.528 +void 1.529 +nsWSRunObject::NextVisibleNode (nsIDOMNode *aNode, 1.530 + int32_t aOffset, 1.531 + nsCOMPtr<nsIDOMNode> *outVisNode, 1.532 + int32_t *outVisOffset, 1.533 + WSType *outType) 1.534 +{ 1.535 + // Find first visible thing after the point. position outVisNode/outVisOffset 1.536 + // just _before_ that thing. If we don't find anything return end of ws. 1.537 + MOZ_ASSERT(aNode && outVisNode && outVisOffset && outType); 1.538 + 1.539 + WSFragment *run; 1.540 + FindRun(aNode, aOffset, &run, true); 1.541 + 1.542 + // is there a visible run there or later? 1.543 + while (run) 1.544 + { 1.545 + if (run->mType == WSType::normalWS) { 1.546 + WSPoint point = GetCharAfter(aNode, aOffset); 1.547 + if (point.mTextNode) 1.548 + { 1.549 + *outVisNode = do_QueryInterface(point.mTextNode); 1.550 + *outVisOffset = point.mOffset; 1.551 + if (nsCRT::IsAsciiSpace(point.mChar) || (point.mChar==nbsp)) 1.552 + { 1.553 + *outType = WSType::normalWS; 1.554 + } 1.555 + else if (!point.mChar) 1.556 + { 1.557 + // MOOSE: not possible? 1.558 + *outType = WSType::none; 1.559 + } 1.560 + else 1.561 + { 1.562 + *outType = WSType::text; 1.563 + } 1.564 + return; 1.565 + } 1.566 + // else if no text node then keep looking. We should eventually fall out of loop 1.567 + } 1.568 + 1.569 + run = run->mRight; 1.570 + } 1.571 + 1.572 + // if we get here then nothing in ws data to find. return end reason 1.573 + *outVisNode = mEndReasonNode; 1.574 + *outVisOffset = mEndOffset; // this really isn't meaningful if mEndReasonNode!=mEndNode 1.575 + *outType = mEndReason; 1.576 +} 1.577 + 1.578 +nsresult 1.579 +nsWSRunObject::AdjustWhitespace() 1.580 +{ 1.581 + // this routine examines a run of ws and tries to get rid of some unneeded nbsp's, 1.582 + // replacing them with regualr ascii space if possible. Keeping things simple 1.583 + // for now and just trying to fix up the trailing ws in the run. 1.584 + if (!mLastNBSPNode) { 1.585 + // nothing to do! 1.586 + return NS_OK; 1.587 + } 1.588 + nsresult res = NS_OK; 1.589 + WSFragment *curRun = mStartRun; 1.590 + while (curRun) 1.591 + { 1.592 + // look for normal ws run 1.593 + if (curRun->mType == WSType::normalWS) { 1.594 + res = CheckTrailingNBSPOfRun(curRun); 1.595 + break; 1.596 + } 1.597 + curRun = curRun->mRight; 1.598 + } 1.599 + return res; 1.600 +} 1.601 + 1.602 + 1.603 +//-------------------------------------------------------------------------------------------- 1.604 +// protected methods 1.605 +//-------------------------------------------------------------------------------------------- 1.606 + 1.607 +already_AddRefed<nsIDOMNode> 1.608 +nsWSRunObject::GetWSBoundingParent() 1.609 +{ 1.610 + NS_ENSURE_TRUE(mNode, nullptr); 1.611 + nsCOMPtr<nsIDOMNode> wsBoundingParent = mNode; 1.612 + while (!IsBlockNode(wsBoundingParent)) 1.613 + { 1.614 + nsCOMPtr<nsIDOMNode> parent; 1.615 + wsBoundingParent->GetParentNode(getter_AddRefs(parent)); 1.616 + if (!parent || !mHTMLEditor->IsEditable(parent)) 1.617 + break; 1.618 + wsBoundingParent.swap(parent); 1.619 + } 1.620 + return wsBoundingParent.forget(); 1.621 +} 1.622 + 1.623 +nsresult 1.624 +nsWSRunObject::GetWSNodes() 1.625 +{ 1.626 + // collect up an array of nodes that are contiguous with the insertion point 1.627 + // and which contain only whitespace. Stop if you reach non-ws text or a new 1.628 + // block boundary. 1.629 + nsresult res = NS_OK; 1.630 + 1.631 + DOMPoint start(mNode, mOffset), end(mNode, mOffset); 1.632 + nsCOMPtr<nsIDOMNode> wsBoundingParent = GetWSBoundingParent(); 1.633 + 1.634 + // first look backwards to find preceding ws nodes 1.635 + if (mHTMLEditor->IsTextNode(mNode)) 1.636 + { 1.637 + nsCOMPtr<nsIContent> textNode(do_QueryInterface(mNode)); 1.638 + const nsTextFragment *textFrag = textNode->GetText(); 1.639 + 1.640 + res = PrependNodeToList(mNode); 1.641 + NS_ENSURE_SUCCESS(res, res); 1.642 + if (mOffset) 1.643 + { 1.644 + int32_t pos; 1.645 + for (pos=mOffset-1; pos>=0; pos--) 1.646 + { 1.647 + // sanity bounds check the char position. bug 136165 1.648 + if (uint32_t(pos) >= textFrag->GetLength()) 1.649 + { 1.650 + NS_NOTREACHED("looking beyond end of text fragment"); 1.651 + continue; 1.652 + } 1.653 + char16_t theChar = textFrag->CharAt(pos); 1.654 + if (!nsCRT::IsAsciiSpace(theChar)) 1.655 + { 1.656 + if (theChar != nbsp) 1.657 + { 1.658 + mStartNode = mNode; 1.659 + mStartOffset = pos+1; 1.660 + mStartReason = WSType::text; 1.661 + mStartReasonNode = mNode; 1.662 + break; 1.663 + } 1.664 + // as we look backwards update our earliest found nbsp 1.665 + mFirstNBSPNode = mNode; 1.666 + mFirstNBSPOffset = pos; 1.667 + // also keep track of latest nbsp so far 1.668 + if (!mLastNBSPNode) 1.669 + { 1.670 + mLastNBSPNode = mNode; 1.671 + mLastNBSPOffset = pos; 1.672 + } 1.673 + } 1.674 + start.SetPoint(mNode,pos); 1.675 + } 1.676 + } 1.677 + } 1.678 + 1.679 + nsCOMPtr<nsIDOMNode> priorNode; 1.680 + while (!mStartNode) 1.681 + { 1.682 + // we haven't found the start of ws yet. Keep looking 1.683 + res = GetPreviousWSNode(start, wsBoundingParent, address_of(priorNode)); 1.684 + NS_ENSURE_SUCCESS(res, res); 1.685 + if (priorNode) 1.686 + { 1.687 + if (IsBlockNode(priorNode)) 1.688 + { 1.689 + start.GetPoint(mStartNode, mStartOffset); 1.690 + mStartReason = WSType::otherBlock; 1.691 + mStartReasonNode = priorNode; 1.692 + } 1.693 + else if (mHTMLEditor->IsTextNode(priorNode)) 1.694 + { 1.695 + res = PrependNodeToList(priorNode); 1.696 + NS_ENSURE_SUCCESS(res, res); 1.697 + nsCOMPtr<nsIContent> textNode(do_QueryInterface(priorNode)); 1.698 + const nsTextFragment *textFrag; 1.699 + if (!textNode || !(textFrag = textNode->GetText())) { 1.700 + return NS_ERROR_NULL_POINTER; 1.701 + } 1.702 + uint32_t len = textNode->TextLength(); 1.703 + 1.704 + if (len < 1) 1.705 + { 1.706 + // Zero length text node. Set start point to it 1.707 + // so we can get past it! 1.708 + start.SetPoint(priorNode,0); 1.709 + } 1.710 + else 1.711 + { 1.712 + int32_t pos; 1.713 + for (pos=len-1; pos>=0; pos--) 1.714 + { 1.715 + // sanity bounds check the char position. bug 136165 1.716 + if (uint32_t(pos) >= textFrag->GetLength()) 1.717 + { 1.718 + NS_NOTREACHED("looking beyond end of text fragment"); 1.719 + continue; 1.720 + } 1.721 + char16_t theChar = textFrag->CharAt(pos); 1.722 + if (!nsCRT::IsAsciiSpace(theChar)) 1.723 + { 1.724 + if (theChar != nbsp) 1.725 + { 1.726 + mStartNode = priorNode; 1.727 + mStartOffset = pos+1; 1.728 + mStartReason = WSType::text; 1.729 + mStartReasonNode = priorNode; 1.730 + break; 1.731 + } 1.732 + // as we look backwards update our earliest found nbsp 1.733 + mFirstNBSPNode = priorNode; 1.734 + mFirstNBSPOffset = pos; 1.735 + // also keep track of latest nbsp so far 1.736 + if (!mLastNBSPNode) 1.737 + { 1.738 + mLastNBSPNode = priorNode; 1.739 + mLastNBSPOffset = pos; 1.740 + } 1.741 + } 1.742 + start.SetPoint(priorNode,pos); 1.743 + } 1.744 + } 1.745 + } 1.746 + else 1.747 + { 1.748 + // it's a break or a special node, like <img>, that is not a block and not 1.749 + // a break but still serves as a terminator to ws runs. 1.750 + start.GetPoint(mStartNode, mStartOffset); 1.751 + if (nsTextEditUtils::IsBreak(priorNode)) 1.752 + mStartReason = WSType::br; 1.753 + else 1.754 + mStartReason = WSType::special; 1.755 + mStartReasonNode = priorNode; 1.756 + } 1.757 + } 1.758 + else 1.759 + { 1.760 + // no prior node means we exhausted wsBoundingParent 1.761 + start.GetPoint(mStartNode, mStartOffset); 1.762 + mStartReason = WSType::thisBlock; 1.763 + mStartReasonNode = wsBoundingParent; 1.764 + } 1.765 + } 1.766 + 1.767 + // then look ahead to find following ws nodes 1.768 + if (mHTMLEditor->IsTextNode(mNode)) 1.769 + { 1.770 + // don't need to put it on list. it already is from code above 1.771 + nsCOMPtr<nsIContent> textNode(do_QueryInterface(mNode)); 1.772 + const nsTextFragment *textFrag = textNode->GetText(); 1.773 + 1.774 + uint32_t len = textNode->TextLength(); 1.775 + if (uint16_t(mOffset)<len) 1.776 + { 1.777 + int32_t pos; 1.778 + for (pos=mOffset; uint32_t(pos)<len; pos++) 1.779 + { 1.780 + // sanity bounds check the char position. bug 136165 1.781 + if ((pos<0) || (uint32_t(pos)>=textFrag->GetLength())) 1.782 + { 1.783 + NS_NOTREACHED("looking beyond end of text fragment"); 1.784 + continue; 1.785 + } 1.786 + char16_t theChar = textFrag->CharAt(pos); 1.787 + if (!nsCRT::IsAsciiSpace(theChar)) 1.788 + { 1.789 + if (theChar != nbsp) 1.790 + { 1.791 + mEndNode = mNode; 1.792 + mEndOffset = pos; 1.793 + mEndReason = WSType::text; 1.794 + mEndReasonNode = mNode; 1.795 + break; 1.796 + } 1.797 + // as we look forwards update our latest found nbsp 1.798 + mLastNBSPNode = mNode; 1.799 + mLastNBSPOffset = pos; 1.800 + // also keep track of earliest nbsp so far 1.801 + if (!mFirstNBSPNode) 1.802 + { 1.803 + mFirstNBSPNode = mNode; 1.804 + mFirstNBSPOffset = pos; 1.805 + } 1.806 + } 1.807 + end.SetPoint(mNode,pos+1); 1.808 + } 1.809 + } 1.810 + } 1.811 + 1.812 + nsCOMPtr<nsIDOMNode> nextNode; 1.813 + while (!mEndNode) 1.814 + { 1.815 + // we haven't found the end of ws yet. Keep looking 1.816 + res = GetNextWSNode(end, wsBoundingParent, address_of(nextNode)); 1.817 + NS_ENSURE_SUCCESS(res, res); 1.818 + if (nextNode) 1.819 + { 1.820 + if (IsBlockNode(nextNode)) 1.821 + { 1.822 + // we encountered a new block. therefore no more ws. 1.823 + end.GetPoint(mEndNode, mEndOffset); 1.824 + mEndReason = WSType::otherBlock; 1.825 + mEndReasonNode = nextNode; 1.826 + } 1.827 + else if (mHTMLEditor->IsTextNode(nextNode)) 1.828 + { 1.829 + res = AppendNodeToList(nextNode); 1.830 + NS_ENSURE_SUCCESS(res, res); 1.831 + nsCOMPtr<nsIContent> textNode(do_QueryInterface(nextNode)); 1.832 + const nsTextFragment *textFrag; 1.833 + if (!textNode || !(textFrag = textNode->GetText())) { 1.834 + return NS_ERROR_NULL_POINTER; 1.835 + } 1.836 + uint32_t len = textNode->TextLength(); 1.837 + 1.838 + if (len < 1) 1.839 + { 1.840 + // Zero length text node. Set end point to it 1.841 + // so we can get past it! 1.842 + end.SetPoint(nextNode,0); 1.843 + } 1.844 + else 1.845 + { 1.846 + int32_t pos; 1.847 + for (pos=0; uint32_t(pos)<len; pos++) 1.848 + { 1.849 + // sanity bounds check the char position. bug 136165 1.850 + if (uint32_t(pos) >= textFrag->GetLength()) 1.851 + { 1.852 + NS_NOTREACHED("looking beyond end of text fragment"); 1.853 + continue; 1.854 + } 1.855 + char16_t theChar = textFrag->CharAt(pos); 1.856 + if (!nsCRT::IsAsciiSpace(theChar)) 1.857 + { 1.858 + if (theChar != nbsp) 1.859 + { 1.860 + mEndNode = nextNode; 1.861 + mEndOffset = pos; 1.862 + mEndReason = WSType::text; 1.863 + mEndReasonNode = nextNode; 1.864 + break; 1.865 + } 1.866 + // as we look forwards update our latest found nbsp 1.867 + mLastNBSPNode = nextNode; 1.868 + mLastNBSPOffset = pos; 1.869 + // also keep track of earliest nbsp so far 1.870 + if (!mFirstNBSPNode) 1.871 + { 1.872 + mFirstNBSPNode = nextNode; 1.873 + mFirstNBSPOffset = pos; 1.874 + } 1.875 + } 1.876 + end.SetPoint(nextNode,pos+1); 1.877 + } 1.878 + } 1.879 + } 1.880 + else 1.881 + { 1.882 + // we encountered a break or a special node, like <img>, 1.883 + // that is not a block and not a break but still 1.884 + // serves as a terminator to ws runs. 1.885 + end.GetPoint(mEndNode, mEndOffset); 1.886 + if (nsTextEditUtils::IsBreak(nextNode)) 1.887 + mEndReason = WSType::br; 1.888 + else 1.889 + mEndReason = WSType::special; 1.890 + mEndReasonNode = nextNode; 1.891 + } 1.892 + } 1.893 + else 1.894 + { 1.895 + // no next node means we exhausted wsBoundingParent 1.896 + end.GetPoint(mEndNode, mEndOffset); 1.897 + mEndReason = WSType::thisBlock; 1.898 + mEndReasonNode = wsBoundingParent; 1.899 + } 1.900 + } 1.901 + 1.902 + return NS_OK; 1.903 +} 1.904 + 1.905 +void 1.906 +nsWSRunObject::GetRuns() 1.907 +{ 1.908 + ClearRuns(); 1.909 + 1.910 + // handle some easy cases first 1.911 + mHTMLEditor->IsPreformatted(mNode, &mPRE); 1.912 + // if it's preformatedd, or if we are surrounded by text or special, it's all one 1.913 + // big normal ws run 1.914 + if (mPRE || 1.915 + ((mStartReason == WSType::text || mStartReason == WSType::special) && 1.916 + (mEndReason == WSType::text || mEndReason == WSType::special || 1.917 + mEndReason == WSType::br))) { 1.918 + MakeSingleWSRun(WSType::normalWS); 1.919 + return; 1.920 + } 1.921 + 1.922 + // if we are before or after a block (or after a break), and there are no nbsp's, 1.923 + // then it's all non-rendering ws. 1.924 + if (!mFirstNBSPNode && !mLastNBSPNode && 1.925 + ((mStartReason & WSType::block) || mStartReason == WSType::br || 1.926 + (mEndReason & WSType::block))) { 1.927 + WSType wstype; 1.928 + if ((mStartReason & WSType::block) || mStartReason == WSType::br) { 1.929 + wstype = WSType::leadingWS; 1.930 + } 1.931 + if (mEndReason & WSType::block) { 1.932 + wstype |= WSType::trailingWS; 1.933 + } 1.934 + MakeSingleWSRun(wstype); 1.935 + return; 1.936 + } 1.937 + 1.938 + // otherwise a little trickier. shucks. 1.939 + mStartRun = new WSFragment(); 1.940 + mStartRun->mStartNode = mStartNode; 1.941 + mStartRun->mStartOffset = mStartOffset; 1.942 + 1.943 + if (mStartReason & WSType::block || mStartReason == WSType::br) { 1.944 + // set up mStartRun 1.945 + mStartRun->mType = WSType::leadingWS; 1.946 + mStartRun->mEndNode = mFirstNBSPNode; 1.947 + mStartRun->mEndOffset = mFirstNBSPOffset; 1.948 + mStartRun->mLeftType = mStartReason; 1.949 + mStartRun->mRightType = WSType::normalWS; 1.950 + 1.951 + // set up next run 1.952 + WSFragment *normalRun = new WSFragment(); 1.953 + mStartRun->mRight = normalRun; 1.954 + normalRun->mType = WSType::normalWS; 1.955 + normalRun->mStartNode = mFirstNBSPNode; 1.956 + normalRun->mStartOffset = mFirstNBSPOffset; 1.957 + normalRun->mLeftType = WSType::leadingWS; 1.958 + normalRun->mLeft = mStartRun; 1.959 + if (mEndReason != WSType::block) { 1.960 + // then no trailing ws. this normal run ends the overall ws run. 1.961 + normalRun->mRightType = mEndReason; 1.962 + normalRun->mEndNode = mEndNode; 1.963 + normalRun->mEndOffset = mEndOffset; 1.964 + mEndRun = normalRun; 1.965 + } 1.966 + else 1.967 + { 1.968 + // we might have trailing ws. 1.969 + // it so happens that *if* there is an nbsp at end, {mEndNode,mEndOffset-1} 1.970 + // will point to it, even though in general start/end points not 1.971 + // guaranteed to be in text nodes. 1.972 + if ((mLastNBSPNode == mEndNode) && (mLastNBSPOffset == (mEndOffset-1))) 1.973 + { 1.974 + // normal ws runs right up to adjacent block (nbsp next to block) 1.975 + normalRun->mRightType = mEndReason; 1.976 + normalRun->mEndNode = mEndNode; 1.977 + normalRun->mEndOffset = mEndOffset; 1.978 + mEndRun = normalRun; 1.979 + } 1.980 + else 1.981 + { 1.982 + normalRun->mEndNode = mLastNBSPNode; 1.983 + normalRun->mEndOffset = mLastNBSPOffset+1; 1.984 + normalRun->mRightType = WSType::trailingWS; 1.985 + 1.986 + // set up next run 1.987 + WSFragment *lastRun = new WSFragment(); 1.988 + lastRun->mType = WSType::trailingWS; 1.989 + lastRun->mStartNode = mLastNBSPNode; 1.990 + lastRun->mStartOffset = mLastNBSPOffset+1; 1.991 + lastRun->mEndNode = mEndNode; 1.992 + lastRun->mEndOffset = mEndOffset; 1.993 + lastRun->mLeftType = WSType::normalWS; 1.994 + lastRun->mLeft = normalRun; 1.995 + lastRun->mRightType = mEndReason; 1.996 + mEndRun = lastRun; 1.997 + normalRun->mRight = lastRun; 1.998 + } 1.999 + } 1.1000 + } else { 1.1001 + // mStartReason is not WSType::block or WSType::br; set up mStartRun 1.1002 + mStartRun->mType = WSType::normalWS; 1.1003 + mStartRun->mEndNode = mLastNBSPNode; 1.1004 + mStartRun->mEndOffset = mLastNBSPOffset+1; 1.1005 + mStartRun->mLeftType = mStartReason; 1.1006 + 1.1007 + // we might have trailing ws. 1.1008 + // it so happens that *if* there is an nbsp at end, {mEndNode,mEndOffset-1} 1.1009 + // will point to it, even though in general start/end points not 1.1010 + // guaranteed to be in text nodes. 1.1011 + if ((mLastNBSPNode == mEndNode) && (mLastNBSPOffset == (mEndOffset-1))) 1.1012 + { 1.1013 + mStartRun->mRightType = mEndReason; 1.1014 + mStartRun->mEndNode = mEndNode; 1.1015 + mStartRun->mEndOffset = mEndOffset; 1.1016 + mEndRun = mStartRun; 1.1017 + } 1.1018 + else 1.1019 + { 1.1020 + // set up next run 1.1021 + WSFragment *lastRun = new WSFragment(); 1.1022 + lastRun->mType = WSType::trailingWS; 1.1023 + lastRun->mStartNode = mLastNBSPNode; 1.1024 + lastRun->mStartOffset = mLastNBSPOffset+1; 1.1025 + lastRun->mLeftType = WSType::normalWS; 1.1026 + lastRun->mLeft = mStartRun; 1.1027 + lastRun->mRightType = mEndReason; 1.1028 + mEndRun = lastRun; 1.1029 + mStartRun->mRight = lastRun; 1.1030 + mStartRun->mRightType = WSType::trailingWS; 1.1031 + } 1.1032 + } 1.1033 +} 1.1034 + 1.1035 +void 1.1036 +nsWSRunObject::ClearRuns() 1.1037 +{ 1.1038 + WSFragment *tmp, *run; 1.1039 + run = mStartRun; 1.1040 + while (run) 1.1041 + { 1.1042 + tmp = run->mRight; 1.1043 + delete run; 1.1044 + run = tmp; 1.1045 + } 1.1046 + mStartRun = 0; 1.1047 + mEndRun = 0; 1.1048 +} 1.1049 + 1.1050 +void 1.1051 +nsWSRunObject::MakeSingleWSRun(WSType aType) 1.1052 +{ 1.1053 + mStartRun = new WSFragment(); 1.1054 + 1.1055 + mStartRun->mStartNode = mStartNode; 1.1056 + mStartRun->mStartOffset = mStartOffset; 1.1057 + mStartRun->mType = aType; 1.1058 + mStartRun->mEndNode = mEndNode; 1.1059 + mStartRun->mEndOffset = mEndOffset; 1.1060 + mStartRun->mLeftType = mStartReason; 1.1061 + mStartRun->mRightType = mEndReason; 1.1062 + 1.1063 + mEndRun = mStartRun; 1.1064 +} 1.1065 + 1.1066 +nsresult 1.1067 +nsWSRunObject::PrependNodeToList(nsIDOMNode *aNode) 1.1068 +{ 1.1069 + NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER); 1.1070 + if (!mNodeArray.InsertObjectAt(aNode, 0)) 1.1071 + return NS_ERROR_FAILURE; 1.1072 + return NS_OK; 1.1073 +} 1.1074 + 1.1075 +nsresult 1.1076 +nsWSRunObject::AppendNodeToList(nsIDOMNode *aNode) 1.1077 +{ 1.1078 + NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER); 1.1079 + if (!mNodeArray.AppendObject(aNode)) 1.1080 + return NS_ERROR_FAILURE; 1.1081 + return NS_OK; 1.1082 +} 1.1083 + 1.1084 +nsresult 1.1085 +nsWSRunObject::GetPreviousWSNode(nsIDOMNode *aStartNode, 1.1086 + nsIDOMNode *aBlockParent, 1.1087 + nsCOMPtr<nsIDOMNode> *aPriorNode) 1.1088 +{ 1.1089 + // can't really recycle various getnext/prior routines because we 1.1090 + // have special needs here. Need to step into inline containers but 1.1091 + // not block containers. 1.1092 + NS_ENSURE_TRUE(aStartNode && aBlockParent && aPriorNode, NS_ERROR_NULL_POINTER); 1.1093 + 1.1094 + nsresult res = aStartNode->GetPreviousSibling(getter_AddRefs(*aPriorNode)); 1.1095 + NS_ENSURE_SUCCESS(res, res); 1.1096 + nsCOMPtr<nsIDOMNode> temp, curNode = aStartNode; 1.1097 + while (!*aPriorNode) 1.1098 + { 1.1099 + // we have exhausted nodes in parent of aStartNode. 1.1100 + res = curNode->GetParentNode(getter_AddRefs(temp)); 1.1101 + NS_ENSURE_SUCCESS(res, res); 1.1102 + NS_ENSURE_TRUE(temp, NS_ERROR_NULL_POINTER); 1.1103 + if (temp == aBlockParent) 1.1104 + { 1.1105 + // we have exhausted nodes in the block parent. The convention here is to return null. 1.1106 + *aPriorNode = nullptr; 1.1107 + return NS_OK; 1.1108 + } 1.1109 + // we have a parent: look for previous sibling 1.1110 + res = temp->GetPreviousSibling(getter_AddRefs(*aPriorNode)); 1.1111 + NS_ENSURE_SUCCESS(res, res); 1.1112 + curNode = temp; 1.1113 + } 1.1114 + // we have a prior node. If it's a block, return it. 1.1115 + if (IsBlockNode(*aPriorNode)) 1.1116 + return NS_OK; 1.1117 + // else if it's a container, get deep rightmost child 1.1118 + else if (mHTMLEditor->IsContainer(*aPriorNode)) 1.1119 + { 1.1120 + temp = mHTMLEditor->GetRightmostChild(*aPriorNode); 1.1121 + if (temp) 1.1122 + *aPriorNode = temp; 1.1123 + return NS_OK; 1.1124 + } 1.1125 + // else return the node itself 1.1126 + return NS_OK; 1.1127 +} 1.1128 + 1.1129 +nsresult 1.1130 +nsWSRunObject::GetPreviousWSNode(DOMPoint aPoint, 1.1131 + nsIDOMNode *aBlockParent, 1.1132 + nsCOMPtr<nsIDOMNode> *aPriorNode) 1.1133 +{ 1.1134 + nsCOMPtr<nsIDOMNode> node; 1.1135 + int32_t offset; 1.1136 + aPoint.GetPoint(node, offset); 1.1137 + return GetPreviousWSNode(node,offset,aBlockParent,aPriorNode); 1.1138 +} 1.1139 + 1.1140 +nsresult 1.1141 +nsWSRunObject::GetPreviousWSNode(nsIDOMNode *aStartNode, 1.1142 + int32_t aOffset, 1.1143 + nsIDOMNode *aBlockParent, 1.1144 + nsCOMPtr<nsIDOMNode> *aPriorNode) 1.1145 +{ 1.1146 + // can't really recycle various getnext/prior routines because we 1.1147 + // have special needs here. Need to step into inline containers but 1.1148 + // not block containers. 1.1149 + NS_ENSURE_TRUE(aStartNode && aBlockParent && aPriorNode, NS_ERROR_NULL_POINTER); 1.1150 + *aPriorNode = 0; 1.1151 + 1.1152 + if (mHTMLEditor->IsTextNode(aStartNode)) 1.1153 + return GetPreviousWSNode(aStartNode, aBlockParent, aPriorNode); 1.1154 + if (!mHTMLEditor->IsContainer(aStartNode)) 1.1155 + return GetPreviousWSNode(aStartNode, aBlockParent, aPriorNode); 1.1156 + 1.1157 + if (!aOffset) 1.1158 + { 1.1159 + if (aStartNode==aBlockParent) 1.1160 + { 1.1161 + // we are at start of the block. 1.1162 + return NS_OK; 1.1163 + } 1.1164 + 1.1165 + // we are at start of non-block container 1.1166 + return GetPreviousWSNode(aStartNode, aBlockParent, aPriorNode); 1.1167 + } 1.1168 + 1.1169 + nsCOMPtr<nsIContent> startContent( do_QueryInterface(aStartNode) ); 1.1170 + NS_ENSURE_STATE(startContent); 1.1171 + nsIContent *priorContent = startContent->GetChildAt(aOffset - 1); 1.1172 + NS_ENSURE_TRUE(priorContent, NS_ERROR_NULL_POINTER); 1.1173 + *aPriorNode = do_QueryInterface(priorContent); 1.1174 + // we have a prior node. If it's a block, return it. 1.1175 + if (IsBlockNode(*aPriorNode)) 1.1176 + return NS_OK; 1.1177 + // else if it's a container, get deep rightmost child 1.1178 + else if (mHTMLEditor->IsContainer(*aPriorNode)) 1.1179 + { 1.1180 + nsCOMPtr<nsIDOMNode> temp; 1.1181 + temp = mHTMLEditor->GetRightmostChild(*aPriorNode); 1.1182 + if (temp) 1.1183 + *aPriorNode = temp; 1.1184 + return NS_OK; 1.1185 + } 1.1186 + // else return the node itself 1.1187 + return NS_OK; 1.1188 +} 1.1189 + 1.1190 +nsresult 1.1191 +nsWSRunObject::GetNextWSNode(nsIDOMNode *aStartNode, 1.1192 + nsIDOMNode *aBlockParent, 1.1193 + nsCOMPtr<nsIDOMNode> *aNextNode) 1.1194 +{ 1.1195 + // can't really recycle various getnext/prior routines because we 1.1196 + // have special needs here. Need to step into inline containers but 1.1197 + // not block containers. 1.1198 + NS_ENSURE_TRUE(aStartNode && aBlockParent && aNextNode, NS_ERROR_NULL_POINTER); 1.1199 + 1.1200 + *aNextNode = 0; 1.1201 + nsresult res = aStartNode->GetNextSibling(getter_AddRefs(*aNextNode)); 1.1202 + NS_ENSURE_SUCCESS(res, res); 1.1203 + nsCOMPtr<nsIDOMNode> temp, curNode = aStartNode; 1.1204 + while (!*aNextNode) 1.1205 + { 1.1206 + // we have exhausted nodes in parent of aStartNode. 1.1207 + res = curNode->GetParentNode(getter_AddRefs(temp)); 1.1208 + NS_ENSURE_SUCCESS(res, res); 1.1209 + NS_ENSURE_TRUE(temp, NS_ERROR_NULL_POINTER); 1.1210 + if (temp == aBlockParent) 1.1211 + { 1.1212 + // we have exhausted nodes in the block parent. The convention 1.1213 + // here is to return null. 1.1214 + *aNextNode = nullptr; 1.1215 + return NS_OK; 1.1216 + } 1.1217 + // we have a parent: look for next sibling 1.1218 + res = temp->GetNextSibling(getter_AddRefs(*aNextNode)); 1.1219 + NS_ENSURE_SUCCESS(res, res); 1.1220 + curNode = temp; 1.1221 + } 1.1222 + // we have a next node. If it's a block, return it. 1.1223 + if (IsBlockNode(*aNextNode)) 1.1224 + return NS_OK; 1.1225 + // else if it's a container, get deep leftmost child 1.1226 + else if (mHTMLEditor->IsContainer(*aNextNode)) 1.1227 + { 1.1228 + temp = mHTMLEditor->GetLeftmostChild(*aNextNode); 1.1229 + if (temp) 1.1230 + *aNextNode = temp; 1.1231 + return NS_OK; 1.1232 + } 1.1233 + // else return the node itself 1.1234 + return NS_OK; 1.1235 +} 1.1236 + 1.1237 +nsresult 1.1238 +nsWSRunObject::GetNextWSNode(DOMPoint aPoint, 1.1239 + nsIDOMNode *aBlockParent, 1.1240 + nsCOMPtr<nsIDOMNode> *aNextNode) 1.1241 +{ 1.1242 + nsCOMPtr<nsIDOMNode> node; 1.1243 + int32_t offset; 1.1244 + aPoint.GetPoint(node, offset); 1.1245 + return GetNextWSNode(node,offset,aBlockParent,aNextNode); 1.1246 +} 1.1247 + 1.1248 +nsresult 1.1249 +nsWSRunObject::GetNextWSNode(nsIDOMNode *aStartNode, 1.1250 + int32_t aOffset, 1.1251 + nsIDOMNode *aBlockParent, 1.1252 + nsCOMPtr<nsIDOMNode> *aNextNode) 1.1253 +{ 1.1254 + // can't really recycle various getnext/prior routines because we have special needs 1.1255 + // here. Need to step into inline containers but not block containers. 1.1256 + NS_ENSURE_TRUE(aStartNode && aBlockParent && aNextNode, NS_ERROR_NULL_POINTER); 1.1257 + *aNextNode = 0; 1.1258 + 1.1259 + if (mHTMLEditor->IsTextNode(aStartNode)) 1.1260 + return GetNextWSNode(aStartNode, aBlockParent, aNextNode); 1.1261 + if (!mHTMLEditor->IsContainer(aStartNode)) 1.1262 + return GetNextWSNode(aStartNode, aBlockParent, aNextNode); 1.1263 + 1.1264 + nsCOMPtr<nsIContent> startContent( do_QueryInterface(aStartNode) ); 1.1265 + NS_ENSURE_STATE(startContent); 1.1266 + nsIContent *nextContent = startContent->GetChildAt(aOffset); 1.1267 + if (!nextContent) 1.1268 + { 1.1269 + if (aStartNode==aBlockParent) 1.1270 + { 1.1271 + // we are at end of the block. 1.1272 + return NS_OK; 1.1273 + } 1.1274 + 1.1275 + // we are at end of non-block container 1.1276 + return GetNextWSNode(aStartNode, aBlockParent, aNextNode); 1.1277 + } 1.1278 + 1.1279 + *aNextNode = do_QueryInterface(nextContent); 1.1280 + // we have a next node. If it's a block, return it. 1.1281 + if (IsBlockNode(*aNextNode)) 1.1282 + return NS_OK; 1.1283 + // else if it's a container, get deep leftmost child 1.1284 + else if (mHTMLEditor->IsContainer(*aNextNode)) 1.1285 + { 1.1286 + nsCOMPtr<nsIDOMNode> temp; 1.1287 + temp = mHTMLEditor->GetLeftmostChild(*aNextNode); 1.1288 + if (temp) 1.1289 + *aNextNode = temp; 1.1290 + return NS_OK; 1.1291 + } 1.1292 + // else return the node itself 1.1293 + return NS_OK; 1.1294 +} 1.1295 + 1.1296 +nsresult 1.1297 +nsWSRunObject::PrepareToDeleteRangePriv(nsWSRunObject* aEndObject) 1.1298 +{ 1.1299 + // this routine adjust whitespace before *this* and after aEndObject 1.1300 + // in preperation for the two areas to become adjacent after the 1.1301 + // intervening content is deleted. It's overly agressive right 1.1302 + // now. There might be a block boundary remaining between them after 1.1303 + // the deletion, in which case these adjstments are unneeded (though 1.1304 + // I don't think they can ever be harmful?) 1.1305 + 1.1306 + NS_ENSURE_TRUE(aEndObject, NS_ERROR_NULL_POINTER); 1.1307 + nsresult res = NS_OK; 1.1308 + 1.1309 + // get the runs before and after selection 1.1310 + WSFragment *beforeRun, *afterRun; 1.1311 + FindRun(mNode, mOffset, &beforeRun, false); 1.1312 + aEndObject->FindRun(aEndObject->mNode, aEndObject->mOffset, &afterRun, true); 1.1313 + 1.1314 + // trim after run of any leading ws 1.1315 + if (afterRun && (afterRun->mType & WSType::leadingWS)) { 1.1316 + res = aEndObject->DeleteChars(aEndObject->mNode, aEndObject->mOffset, afterRun->mEndNode, afterRun->mEndOffset, 1.1317 + eOutsideUserSelectAll); 1.1318 + NS_ENSURE_SUCCESS(res, res); 1.1319 + } 1.1320 + // adjust normal ws in afterRun if needed 1.1321 + if (afterRun && afterRun->mType == WSType::normalWS && !aEndObject->mPRE) { 1.1322 + if ((beforeRun && (beforeRun->mType & WSType::leadingWS)) || 1.1323 + (!beforeRun && ((mStartReason & WSType::block) || 1.1324 + mStartReason == WSType::br))) { 1.1325 + // make sure leading char of following ws is an nbsp, so that it will show up 1.1326 + WSPoint point = aEndObject->GetCharAfter(aEndObject->mNode, 1.1327 + aEndObject->mOffset); 1.1328 + if (point.mTextNode && nsCRT::IsAsciiSpace(point.mChar)) 1.1329 + { 1.1330 + res = aEndObject->ConvertToNBSP(point, eOutsideUserSelectAll); 1.1331 + NS_ENSURE_SUCCESS(res, res); 1.1332 + } 1.1333 + } 1.1334 + } 1.1335 + // trim before run of any trailing ws 1.1336 + if (beforeRun && (beforeRun->mType & WSType::trailingWS)) { 1.1337 + res = DeleteChars(beforeRun->mStartNode, beforeRun->mStartOffset, mNode, mOffset, 1.1338 + eOutsideUserSelectAll); 1.1339 + NS_ENSURE_SUCCESS(res, res); 1.1340 + } else if (beforeRun && beforeRun->mType == WSType::normalWS && !mPRE) { 1.1341 + if ((afterRun && (afterRun->mType & WSType::trailingWS)) || 1.1342 + (afterRun && afterRun->mType == WSType::normalWS) || 1.1343 + (!afterRun && (aEndObject->mEndReason & WSType::block))) { 1.1344 + // make sure trailing char of starting ws is an nbsp, so that it will show up 1.1345 + WSPoint point = GetCharBefore(mNode, mOffset); 1.1346 + if (point.mTextNode && nsCRT::IsAsciiSpace(point.mChar)) 1.1347 + { 1.1348 + nsCOMPtr<nsIDOMNode> wsStartNode, wsEndNode; 1.1349 + int32_t wsStartOffset, wsEndOffset; 1.1350 + GetAsciiWSBounds(eBoth, mNode, mOffset, address_of(wsStartNode), 1.1351 + &wsStartOffset, address_of(wsEndNode), &wsEndOffset); 1.1352 + point.mTextNode = do_QueryInterface(wsStartNode); 1.1353 + if (!point.mTextNode->IsNodeOfType(nsINode::eDATA_NODE)) { 1.1354 + // Not sure if this is needed, but it'll maintain the same 1.1355 + // functionality 1.1356 + point.mTextNode = nullptr; 1.1357 + } 1.1358 + point.mOffset = wsStartOffset; 1.1359 + res = ConvertToNBSP(point, eOutsideUserSelectAll); 1.1360 + NS_ENSURE_SUCCESS(res, res); 1.1361 + } 1.1362 + } 1.1363 + } 1.1364 + return res; 1.1365 +} 1.1366 + 1.1367 +nsresult 1.1368 +nsWSRunObject::PrepareToSplitAcrossBlocksPriv() 1.1369 +{ 1.1370 + // used to prepare ws to be split across two blocks. The main issue 1.1371 + // here is make sure normalWS doesn't end up becoming non-significant 1.1372 + // leading or trailing ws after the split. 1.1373 + nsresult res = NS_OK; 1.1374 + 1.1375 + // get the runs before and after selection 1.1376 + WSFragment *beforeRun, *afterRun; 1.1377 + FindRun(mNode, mOffset, &beforeRun, false); 1.1378 + FindRun(mNode, mOffset, &afterRun, true); 1.1379 + 1.1380 + // adjust normal ws in afterRun if needed 1.1381 + if (afterRun && afterRun->mType == WSType::normalWS) { 1.1382 + // make sure leading char of following ws is an nbsp, so that it will show up 1.1383 + WSPoint point = GetCharAfter(mNode, mOffset); 1.1384 + if (point.mTextNode && nsCRT::IsAsciiSpace(point.mChar)) 1.1385 + { 1.1386 + res = ConvertToNBSP(point); 1.1387 + NS_ENSURE_SUCCESS(res, res); 1.1388 + } 1.1389 + } 1.1390 + 1.1391 + // adjust normal ws in beforeRun if needed 1.1392 + if (beforeRun && beforeRun->mType == WSType::normalWS) { 1.1393 + // make sure trailing char of starting ws is an nbsp, so that it will show up 1.1394 + WSPoint point = GetCharBefore(mNode, mOffset); 1.1395 + if (point.mTextNode && nsCRT::IsAsciiSpace(point.mChar)) 1.1396 + { 1.1397 + nsCOMPtr<nsIDOMNode> wsStartNode, wsEndNode; 1.1398 + int32_t wsStartOffset, wsEndOffset; 1.1399 + GetAsciiWSBounds(eBoth, mNode, mOffset, address_of(wsStartNode), 1.1400 + &wsStartOffset, address_of(wsEndNode), &wsEndOffset); 1.1401 + point.mTextNode = do_QueryInterface(wsStartNode); 1.1402 + if (!point.mTextNode->IsNodeOfType(nsINode::eDATA_NODE)) { 1.1403 + // Not sure if this is needed, but it'll maintain the same 1.1404 + // functionality 1.1405 + point.mTextNode = nullptr; 1.1406 + } 1.1407 + point.mOffset = wsStartOffset; 1.1408 + res = ConvertToNBSP(point); 1.1409 + NS_ENSURE_SUCCESS(res, res); 1.1410 + } 1.1411 + } 1.1412 + return res; 1.1413 +} 1.1414 + 1.1415 +nsresult 1.1416 +nsWSRunObject::DeleteChars(nsIDOMNode *aStartNode, int32_t aStartOffset, 1.1417 + nsIDOMNode *aEndNode, int32_t aEndOffset, 1.1418 + AreaRestriction aAR) 1.1419 +{ 1.1420 + // MOOSE: this routine needs to be modified to preserve the integrity of the 1.1421 + // wsFragment info. 1.1422 + NS_ENSURE_TRUE(aStartNode && aEndNode, NS_ERROR_NULL_POINTER); 1.1423 + 1.1424 + if (aAR == eOutsideUserSelectAll) 1.1425 + { 1.1426 + nsCOMPtr<nsIDOMNode> san = mHTMLEditor->FindUserSelectAllNode(aStartNode); 1.1427 + if (san) 1.1428 + return NS_OK; 1.1429 + 1.1430 + if (aStartNode != aEndNode) 1.1431 + { 1.1432 + san = mHTMLEditor->FindUserSelectAllNode(aEndNode); 1.1433 + if (san) 1.1434 + return NS_OK; 1.1435 + } 1.1436 + } 1.1437 + 1.1438 + if ((aStartNode == aEndNode) && (aStartOffset == aEndOffset)) 1.1439 + return NS_OK; // nothing to delete 1.1440 + 1.1441 + nsresult res = NS_OK; 1.1442 + int32_t idx = mNodeArray.IndexOf(aStartNode); 1.1443 + if (idx==-1) idx = 0; // if our strarting point wasn't one of our ws text nodes, 1.1444 + // then just go through them from the beginning. 1.1445 + nsCOMPtr<nsIDOMNode> node; 1.1446 + nsCOMPtr<nsIDOMCharacterData> textnode; 1.1447 + nsRefPtr<nsRange> range; 1.1448 + 1.1449 + if (aStartNode == aEndNode) 1.1450 + { 1.1451 + textnode = do_QueryInterface(aStartNode); 1.1452 + if (textnode) 1.1453 + { 1.1454 + return mHTMLEditor->DeleteText(textnode, (uint32_t)aStartOffset, 1.1455 + (uint32_t)(aEndOffset-aStartOffset)); 1.1456 + } 1.1457 + } 1.1458 + 1.1459 + int32_t count = mNodeArray.Count(); 1.1460 + while (idx < count) 1.1461 + { 1.1462 + node = mNodeArray[idx]; 1.1463 + if (!node) 1.1464 + break; // we ran out of ws nodes; must have been deleting to end 1.1465 + if (node == aStartNode) 1.1466 + { 1.1467 + textnode = do_QueryInterface(node); 1.1468 + uint32_t len; 1.1469 + textnode->GetLength(&len); 1.1470 + if (uint32_t(aStartOffset)<len) 1.1471 + { 1.1472 + res = mHTMLEditor->DeleteText(textnode, (uint32_t)aStartOffset, len-aStartOffset); 1.1473 + NS_ENSURE_SUCCESS(res, res); 1.1474 + } 1.1475 + } 1.1476 + else if (node == aEndNode) 1.1477 + { 1.1478 + if (aEndOffset) 1.1479 + { 1.1480 + textnode = do_QueryInterface(node); 1.1481 + res = mHTMLEditor->DeleteText(textnode, 0, (uint32_t)aEndOffset); 1.1482 + NS_ENSURE_SUCCESS(res, res); 1.1483 + } 1.1484 + break; 1.1485 + } 1.1486 + else 1.1487 + { 1.1488 + if (!range) 1.1489 + { 1.1490 + nsCOMPtr<nsINode> startNode = do_QueryInterface(aStartNode); 1.1491 + NS_ENSURE_STATE(startNode); 1.1492 + range = new nsRange(startNode); 1.1493 + res = range->SetStart(startNode, aStartOffset); 1.1494 + NS_ENSURE_SUCCESS(res, res); 1.1495 + res = range->SetEnd(aEndNode, aEndOffset); 1.1496 + NS_ENSURE_SUCCESS(res, res); 1.1497 + } 1.1498 + bool nodeBefore, nodeAfter; 1.1499 + nsCOMPtr<nsIContent> content (do_QueryInterface(node)); 1.1500 + res = nsRange::CompareNodeToRange(content, range, &nodeBefore, &nodeAfter); 1.1501 + NS_ENSURE_SUCCESS(res, res); 1.1502 + if (nodeAfter) 1.1503 + { 1.1504 + break; 1.1505 + } 1.1506 + if (!nodeBefore) 1.1507 + { 1.1508 + res = mHTMLEditor->DeleteNode(node); 1.1509 + NS_ENSURE_SUCCESS(res, res); 1.1510 + mNodeArray.RemoveObject(node); 1.1511 + --count; 1.1512 + --idx; 1.1513 + } 1.1514 + } 1.1515 + idx++; 1.1516 + } 1.1517 + return res; 1.1518 +} 1.1519 + 1.1520 +nsWSRunObject::WSPoint 1.1521 +nsWSRunObject::GetCharAfter(nsIDOMNode *aNode, int32_t aOffset) 1.1522 +{ 1.1523 + MOZ_ASSERT(aNode); 1.1524 + 1.1525 + int32_t idx = mNodeArray.IndexOf(aNode); 1.1526 + if (idx == -1) 1.1527 + { 1.1528 + // use range comparisons to get right ws node 1.1529 + return GetWSPointAfter(aNode, aOffset); 1.1530 + } 1.1531 + else 1.1532 + { 1.1533 + // use wspoint version of GetCharAfter() 1.1534 + WSPoint point(aNode,aOffset,0); 1.1535 + return GetCharAfter(point); 1.1536 + } 1.1537 +} 1.1538 + 1.1539 +nsWSRunObject::WSPoint 1.1540 +nsWSRunObject::GetCharBefore(nsIDOMNode *aNode, int32_t aOffset) 1.1541 +{ 1.1542 + MOZ_ASSERT(aNode); 1.1543 + 1.1544 + int32_t idx = mNodeArray.IndexOf(aNode); 1.1545 + if (idx == -1) 1.1546 + { 1.1547 + // use range comparisons to get right ws node 1.1548 + return GetWSPointBefore(aNode, aOffset); 1.1549 + } 1.1550 + else 1.1551 + { 1.1552 + // use wspoint version of GetCharBefore() 1.1553 + WSPoint point(aNode,aOffset,0); 1.1554 + return GetCharBefore(point); 1.1555 + } 1.1556 +} 1.1557 + 1.1558 +nsWSRunObject::WSPoint 1.1559 +nsWSRunObject::GetCharAfter(const WSPoint &aPoint) 1.1560 +{ 1.1561 + MOZ_ASSERT(aPoint.mTextNode); 1.1562 + 1.1563 + WSPoint outPoint; 1.1564 + outPoint.mTextNode = nullptr; 1.1565 + outPoint.mOffset = 0; 1.1566 + outPoint.mChar = 0; 1.1567 + 1.1568 + nsCOMPtr<nsIDOMNode> pointTextNode(do_QueryInterface(aPoint.mTextNode)); 1.1569 + int32_t idx = mNodeArray.IndexOf(pointTextNode); 1.1570 + if (idx == -1) { 1.1571 + // can't find point, but it's not an error 1.1572 + return outPoint; 1.1573 + } 1.1574 + int32_t numNodes = mNodeArray.Count(); 1.1575 + 1.1576 + if (uint16_t(aPoint.mOffset) < aPoint.mTextNode->TextLength()) 1.1577 + { 1.1578 + outPoint = aPoint; 1.1579 + outPoint.mChar = GetCharAt(aPoint.mTextNode, aPoint.mOffset); 1.1580 + return outPoint; 1.1581 + } else if (idx + 1 < (int32_t)numNodes) { 1.1582 + nsIDOMNode* node = mNodeArray[idx+1]; 1.1583 + MOZ_ASSERT(node); 1.1584 + outPoint.mTextNode = do_QueryInterface(node); 1.1585 + if (!outPoint.mTextNode->IsNodeOfType(nsINode::eDATA_NODE)) { 1.1586 + // Not sure if this is needed, but it'll maintain the same 1.1587 + // functionality 1.1588 + outPoint.mTextNode = nullptr; 1.1589 + } 1.1590 + outPoint.mOffset = 0; 1.1591 + outPoint.mChar = GetCharAt(outPoint.mTextNode, 0); 1.1592 + } 1.1593 + return outPoint; 1.1594 +} 1.1595 + 1.1596 +nsWSRunObject::WSPoint 1.1597 +nsWSRunObject::GetCharBefore(const WSPoint &aPoint) 1.1598 +{ 1.1599 + MOZ_ASSERT(aPoint.mTextNode); 1.1600 + 1.1601 + WSPoint outPoint; 1.1602 + outPoint.mTextNode = nullptr; 1.1603 + outPoint.mOffset = 0; 1.1604 + outPoint.mChar = 0; 1.1605 + 1.1606 + nsCOMPtr<nsIDOMNode> pointTextNode(do_QueryInterface(aPoint.mTextNode)); 1.1607 + int32_t idx = mNodeArray.IndexOf(pointTextNode); 1.1608 + if (idx == -1) { 1.1609 + // can't find point, but it's not an error 1.1610 + return outPoint; 1.1611 + } 1.1612 + 1.1613 + if (aPoint.mOffset != 0) 1.1614 + { 1.1615 + outPoint = aPoint; 1.1616 + outPoint.mOffset--; 1.1617 + outPoint.mChar = GetCharAt(aPoint.mTextNode, aPoint.mOffset-1); 1.1618 + return outPoint; 1.1619 + } 1.1620 + else if (idx) 1.1621 + { 1.1622 + nsIDOMNode* node = mNodeArray[idx-1]; 1.1623 + MOZ_ASSERT(node); 1.1624 + outPoint.mTextNode = do_QueryInterface(node); 1.1625 + 1.1626 + uint32_t len = outPoint.mTextNode->TextLength(); 1.1627 + 1.1628 + if (len) 1.1629 + { 1.1630 + outPoint.mOffset = len-1; 1.1631 + outPoint.mChar = GetCharAt(outPoint.mTextNode, len-1); 1.1632 + } 1.1633 + } 1.1634 + return outPoint; 1.1635 +} 1.1636 + 1.1637 +nsresult 1.1638 +nsWSRunObject::ConvertToNBSP(WSPoint aPoint, AreaRestriction aAR) 1.1639 +{ 1.1640 + // MOOSE: this routine needs to be modified to preserve the integrity of the 1.1641 + // wsFragment info. 1.1642 + NS_ENSURE_TRUE(aPoint.mTextNode, NS_ERROR_NULL_POINTER); 1.1643 + 1.1644 + if (aAR == eOutsideUserSelectAll) 1.1645 + { 1.1646 + nsCOMPtr<nsIDOMNode> domnode = do_QueryInterface(aPoint.mTextNode); 1.1647 + if (domnode) 1.1648 + { 1.1649 + nsCOMPtr<nsIDOMNode> san = mHTMLEditor->FindUserSelectAllNode(domnode); 1.1650 + if (san) 1.1651 + return NS_OK; 1.1652 + } 1.1653 + } 1.1654 + 1.1655 + nsCOMPtr<nsIDOMCharacterData> textNode(do_QueryInterface(aPoint.mTextNode)); 1.1656 + NS_ENSURE_TRUE(textNode, NS_ERROR_NULL_POINTER); 1.1657 + nsCOMPtr<nsIDOMNode> node(do_QueryInterface(textNode)); 1.1658 + 1.1659 + // first, insert an nbsp 1.1660 + nsAutoTxnsConserveSelection dontSpazMySelection(mHTMLEditor); 1.1661 + nsAutoString nbspStr(nbsp); 1.1662 + nsresult res = mHTMLEditor->InsertTextIntoTextNodeImpl(nbspStr, textNode, aPoint.mOffset, true); 1.1663 + NS_ENSURE_SUCCESS(res, res); 1.1664 + 1.1665 + // next, find range of ws it will replace 1.1666 + nsCOMPtr<nsIDOMNode> startNode, endNode; 1.1667 + int32_t startOffset=0, endOffset=0; 1.1668 + 1.1669 + GetAsciiWSBounds(eAfter, node, aPoint.mOffset+1, address_of(startNode), 1.1670 + &startOffset, address_of(endNode), &endOffset); 1.1671 + 1.1672 + // finally, delete that replaced ws, if any 1.1673 + if (startNode) 1.1674 + { 1.1675 + res = DeleteChars(startNode, startOffset, endNode, endOffset); 1.1676 + } 1.1677 + 1.1678 + return res; 1.1679 +} 1.1680 + 1.1681 +void 1.1682 +nsWSRunObject::GetAsciiWSBounds(int16_t aDir, nsIDOMNode *aNode, int32_t aOffset, 1.1683 + nsCOMPtr<nsIDOMNode> *outStartNode, int32_t *outStartOffset, 1.1684 + nsCOMPtr<nsIDOMNode> *outEndNode, int32_t *outEndOffset) 1.1685 +{ 1.1686 + MOZ_ASSERT(aNode && outStartNode && outEndNode); 1.1687 + 1.1688 + nsCOMPtr<nsIDOMNode> startNode, endNode; 1.1689 + int32_t startOffset=0, endOffset=0; 1.1690 + 1.1691 + if (aDir & eAfter) 1.1692 + { 1.1693 + WSPoint point = GetCharAfter(aNode, aOffset); 1.1694 + if (point.mTextNode) { 1.1695 + // we found a text node, at least 1.1696 + endNode = do_QueryInterface(point.mTextNode); 1.1697 + endOffset = point.mOffset; 1.1698 + startNode = endNode; 1.1699 + startOffset = endOffset; 1.1700 + 1.1701 + // scan ahead to end of ascii ws 1.1702 + while (nsCRT::IsAsciiSpace(point.mChar)) 1.1703 + { 1.1704 + endNode = do_QueryInterface(point.mTextNode); 1.1705 + point.mOffset++; // endOffset is _after_ ws 1.1706 + endOffset = point.mOffset; 1.1707 + point = GetCharAfter(point); 1.1708 + if (!point.mTextNode) { 1.1709 + break; 1.1710 + } 1.1711 + } 1.1712 + } 1.1713 + } 1.1714 + 1.1715 + if (aDir & eBefore) 1.1716 + { 1.1717 + WSPoint point = GetCharBefore(aNode, aOffset); 1.1718 + if (point.mTextNode) { 1.1719 + // we found a text node, at least 1.1720 + startNode = do_QueryInterface(point.mTextNode); 1.1721 + startOffset = point.mOffset+1; 1.1722 + if (!endNode) 1.1723 + { 1.1724 + endNode = startNode; 1.1725 + endOffset = startOffset; 1.1726 + } 1.1727 + 1.1728 + // scan back to start of ascii ws 1.1729 + while (nsCRT::IsAsciiSpace(point.mChar)) 1.1730 + { 1.1731 + startNode = do_QueryInterface(point.mTextNode); 1.1732 + startOffset = point.mOffset; 1.1733 + point = GetCharBefore(point); 1.1734 + if (!point.mTextNode) { 1.1735 + break; 1.1736 + } 1.1737 + } 1.1738 + } 1.1739 + } 1.1740 + 1.1741 + *outStartNode = startNode; 1.1742 + *outStartOffset = startOffset; 1.1743 + *outEndNode = endNode; 1.1744 + *outEndOffset = endOffset; 1.1745 +} 1.1746 + 1.1747 +void 1.1748 +nsWSRunObject::FindRun(nsIDOMNode *aNode, int32_t aOffset, WSFragment **outRun, bool after) 1.1749 +{ 1.1750 + *outRun = nullptr; 1.1751 + // given a dompoint, find the ws run that is before or after it, as caller needs 1.1752 + MOZ_ASSERT(aNode && outRun); 1.1753 + 1.1754 + WSFragment *run = mStartRun; 1.1755 + while (run) 1.1756 + { 1.1757 + int16_t comp = nsContentUtils::ComparePoints(aNode, aOffset, run->mStartNode, 1.1758 + run->mStartOffset); 1.1759 + if (comp <= 0) 1.1760 + { 1.1761 + if (after) 1.1762 + { 1.1763 + *outRun = run; 1.1764 + } 1.1765 + else // before 1.1766 + { 1.1767 + *outRun = nullptr; 1.1768 + } 1.1769 + return; 1.1770 + } 1.1771 + comp = nsContentUtils::ComparePoints(aNode, aOffset, 1.1772 + run->mEndNode, run->mEndOffset); 1.1773 + if (comp < 0) 1.1774 + { 1.1775 + *outRun = run; 1.1776 + return; 1.1777 + } 1.1778 + else if (comp == 0) 1.1779 + { 1.1780 + if (after) 1.1781 + { 1.1782 + *outRun = run->mRight; 1.1783 + } 1.1784 + else // before 1.1785 + { 1.1786 + *outRun = run; 1.1787 + } 1.1788 + return; 1.1789 + } 1.1790 + if (!run->mRight) 1.1791 + { 1.1792 + if (after) 1.1793 + { 1.1794 + *outRun = nullptr; 1.1795 + } 1.1796 + else // before 1.1797 + { 1.1798 + *outRun = run; 1.1799 + } 1.1800 + return; 1.1801 + } 1.1802 + run = run->mRight; 1.1803 + } 1.1804 +} 1.1805 + 1.1806 +char16_t 1.1807 +nsWSRunObject::GetCharAt(nsIContent *aTextNode, int32_t aOffset) 1.1808 +{ 1.1809 + // return 0 if we can't get a char, for whatever reason 1.1810 + NS_ENSURE_TRUE(aTextNode, 0); 1.1811 + 1.1812 + int32_t len = int32_t(aTextNode->TextLength()); 1.1813 + if (aOffset < 0 || aOffset >= len) 1.1814 + return 0; 1.1815 + 1.1816 + return aTextNode->GetText()->CharAt(aOffset); 1.1817 +} 1.1818 + 1.1819 +nsWSRunObject::WSPoint 1.1820 +nsWSRunObject::GetWSPointAfter(nsIDOMNode *aNode, int32_t aOffset) 1.1821 +{ 1.1822 + // Note: only to be called if aNode is not a ws node. 1.1823 + 1.1824 + // binary search on wsnodes 1.1825 + int32_t numNodes, firstNum, curNum, lastNum; 1.1826 + numNodes = mNodeArray.Count(); 1.1827 + 1.1828 + if (!numNodes) { 1.1829 + // do nothing if there are no nodes to search 1.1830 + WSPoint outPoint; 1.1831 + return outPoint; 1.1832 + } 1.1833 + 1.1834 + firstNum = 0; 1.1835 + curNum = numNodes/2; 1.1836 + lastNum = numNodes; 1.1837 + int16_t cmp=0; 1.1838 + nsCOMPtr<nsIDOMNode> curNode; 1.1839 + 1.1840 + // begin binary search 1.1841 + // we do this because we need to minimize calls to ComparePoints(), 1.1842 + // which is mongo expensive 1.1843 + while (curNum != lastNum) 1.1844 + { 1.1845 + curNode = mNodeArray[curNum]; 1.1846 + cmp = nsContentUtils::ComparePoints(aNode, aOffset, curNode, 0); 1.1847 + if (cmp < 0) 1.1848 + lastNum = curNum; 1.1849 + else 1.1850 + firstNum = curNum + 1; 1.1851 + curNum = (lastNum - firstNum) / 2 + firstNum; 1.1852 + NS_ASSERTION(firstNum <= curNum && curNum <= lastNum, "Bad binary search"); 1.1853 + } 1.1854 + 1.1855 + // When the binary search is complete, we always know that the current node 1.1856 + // is the same as the end node, which is always past our range. Therefore, 1.1857 + // we've found the node immediately after the point of interest. 1.1858 + if (curNum == mNodeArray.Count()) { 1.1859 + // they asked for past our range (it's after the last node). GetCharAfter 1.1860 + // will do the work for us when we pass it the last index of the last node. 1.1861 + nsCOMPtr<nsIContent> textNode(do_QueryInterface(mNodeArray[curNum-1])); 1.1862 + WSPoint point(textNode, textNode->TextLength(), 0); 1.1863 + return GetCharAfter(point); 1.1864 + } else { 1.1865 + // The char after the point of interest is the first character of our range. 1.1866 + nsCOMPtr<nsIContent> textNode(do_QueryInterface(mNodeArray[curNum])); 1.1867 + WSPoint point(textNode, 0, 0); 1.1868 + return GetCharAfter(point); 1.1869 + } 1.1870 +} 1.1871 + 1.1872 +nsWSRunObject::WSPoint 1.1873 +nsWSRunObject::GetWSPointBefore(nsIDOMNode *aNode, int32_t aOffset) 1.1874 +{ 1.1875 + // Note: only to be called if aNode is not a ws node. 1.1876 + 1.1877 + // binary search on wsnodes 1.1878 + int32_t numNodes, firstNum, curNum, lastNum; 1.1879 + numNodes = mNodeArray.Count(); 1.1880 + 1.1881 + if (!numNodes) { 1.1882 + // do nothing if there are no nodes to search 1.1883 + WSPoint outPoint; 1.1884 + return outPoint; 1.1885 + } 1.1886 + 1.1887 + firstNum = 0; 1.1888 + curNum = numNodes/2; 1.1889 + lastNum = numNodes; 1.1890 + int16_t cmp=0; 1.1891 + nsCOMPtr<nsIDOMNode> curNode; 1.1892 + 1.1893 + // begin binary search 1.1894 + // we do this because we need to minimize calls to ComparePoints(), 1.1895 + // which is mongo expensive 1.1896 + while (curNum != lastNum) 1.1897 + { 1.1898 + curNode = mNodeArray[curNum]; 1.1899 + cmp = nsContentUtils::ComparePoints(aNode, aOffset, curNode, 0); 1.1900 + if (cmp < 0) 1.1901 + lastNum = curNum; 1.1902 + else 1.1903 + firstNum = curNum + 1; 1.1904 + curNum = (lastNum - firstNum) / 2 + firstNum; 1.1905 + NS_ASSERTION(firstNum <= curNum && curNum <= lastNum, "Bad binary search"); 1.1906 + } 1.1907 + 1.1908 + // When the binary search is complete, we always know that the current node 1.1909 + // is the same as the end node, which is always past our range. Therefore, 1.1910 + // we've found the node immediately after the point of interest. 1.1911 + if (curNum == mNodeArray.Count()) { 1.1912 + // get the point before the end of the last node, we can pass the length 1.1913 + // of the node into GetCharBefore, and it will return the last character. 1.1914 + nsCOMPtr<nsIContent> textNode(do_QueryInterface(mNodeArray[curNum - 1])); 1.1915 + WSPoint point(textNode, textNode->TextLength(), 0); 1.1916 + return GetCharBefore(point); 1.1917 + } else { 1.1918 + // we can just ask the current node for the point immediately before it, 1.1919 + // it will handle moving to the previous node (if any) and returning the 1.1920 + // appropriate character 1.1921 + nsCOMPtr<nsIContent> textNode(do_QueryInterface(mNodeArray[curNum])); 1.1922 + WSPoint point(textNode, 0, 0); 1.1923 + return GetCharBefore(point); 1.1924 + } 1.1925 +} 1.1926 + 1.1927 +nsresult 1.1928 +nsWSRunObject::CheckTrailingNBSPOfRun(WSFragment *aRun) 1.1929 +{ 1.1930 + // try to change an nbsp to a space, if possible, just to prevent nbsp proliferation. 1.1931 + // examine what is before and after the trailing nbsp, if any. 1.1932 + NS_ENSURE_TRUE(aRun, NS_ERROR_NULL_POINTER); 1.1933 + nsresult res; 1.1934 + bool leftCheck = false; 1.1935 + bool spaceNBSP = false; 1.1936 + bool rightCheck = false; 1.1937 + 1.1938 + // confirm run is normalWS 1.1939 + if (aRun->mType != WSType::normalWS) { 1.1940 + return NS_ERROR_FAILURE; 1.1941 + } 1.1942 + 1.1943 + // first check for trailing nbsp 1.1944 + WSPoint thePoint = GetCharBefore(aRun->mEndNode, aRun->mEndOffset); 1.1945 + if (thePoint.mTextNode && thePoint.mChar == nbsp) { 1.1946 + // now check that what is to the left of it is compatible with replacing nbsp with space 1.1947 + WSPoint prevPoint = GetCharBefore(thePoint); 1.1948 + if (prevPoint.mTextNode) { 1.1949 + if (!nsCRT::IsAsciiSpace(prevPoint.mChar)) leftCheck = true; 1.1950 + else spaceNBSP = true; 1.1951 + } else if (aRun->mLeftType == WSType::text) { 1.1952 + leftCheck = true; 1.1953 + } else if (aRun->mLeftType == WSType::special) { 1.1954 + leftCheck = true; 1.1955 + } 1.1956 + if (leftCheck || spaceNBSP) 1.1957 + { 1.1958 + // now check that what is to the right of it is compatible with replacing nbsp with space 1.1959 + if (aRun->mRightType == WSType::text) { 1.1960 + rightCheck = true; 1.1961 + } 1.1962 + if (aRun->mRightType == WSType::special) { 1.1963 + rightCheck = true; 1.1964 + } 1.1965 + if (aRun->mRightType == WSType::br) { 1.1966 + rightCheck = true; 1.1967 + } 1.1968 + if ((aRun->mRightType & WSType::block) && 1.1969 + IsBlockNode(nsCOMPtr<nsIDOMNode>(GetWSBoundingParent()))) { 1.1970 + // we are at a block boundary. Insert a <br>. Why? Well, first note that 1.1971 + // the br will have no visible effect since it is up against a block boundary. 1.1972 + // |foo<br><p>bar| renders like |foo<p>bar| and similarly 1.1973 + // |<p>foo<br></p>bar| renders like |<p>foo</p>bar|. What this <br> addition 1.1974 + // gets us is the ability to convert a trailing nbsp to a space. Consider: 1.1975 + // |<body>foo. '</body>|, where ' represents selection. User types space attempting 1.1976 + // to put 2 spaces after the end of their sentence. We used to do this as: 1.1977 + // |<body>foo.  </body>| This caused problems with soft wrapping: the nbsp 1.1978 + // would wrap to the next line, which looked attrocious. If you try to do: 1.1979 + // |<body>foo.  </body>| instead, the trailing space is invisible because it 1.1980 + // is against a block boundary. If you do: |<body>foo.  </body>| then 1.1981 + // you get an even uglier soft wrapping problem, where foo is on one line until 1.1982 + // you type the final space, and then "foo " jumps down to the next line. Ugh. 1.1983 + // The best way I can find out of this is to throw in a harmless <br> 1.1984 + // here, which allows us to do: |<body>foo.  <br></body>|, which doesn't 1.1985 + // cause foo to jump lines, doesn't cause spaces to show up at the beginning of 1.1986 + // soft wrapped lines, and lets the user see 2 spaces when they type 2 spaces. 1.1987 + 1.1988 + nsCOMPtr<nsIDOMNode> brNode; 1.1989 + res = mHTMLEditor->CreateBR(aRun->mEndNode, aRun->mEndOffset, address_of(brNode)); 1.1990 + NS_ENSURE_SUCCESS(res, res); 1.1991 + 1.1992 + // refresh thePoint, prevPoint 1.1993 + thePoint = GetCharBefore(aRun->mEndNode, aRun->mEndOffset); 1.1994 + prevPoint = GetCharBefore(thePoint); 1.1995 + rightCheck = true; 1.1996 + } 1.1997 + } 1.1998 + if (leftCheck && rightCheck) 1.1999 + { 1.2000 + // now replace nbsp with space 1.2001 + // first, insert a space 1.2002 + nsCOMPtr<nsIDOMCharacterData> textNode(do_QueryInterface(thePoint.mTextNode)); 1.2003 + NS_ENSURE_TRUE(textNode, NS_ERROR_NULL_POINTER); 1.2004 + nsAutoTxnsConserveSelection dontSpazMySelection(mHTMLEditor); 1.2005 + nsAutoString spaceStr(char16_t(32)); 1.2006 + res = mHTMLEditor->InsertTextIntoTextNodeImpl(spaceStr, textNode, thePoint.mOffset, true); 1.2007 + NS_ENSURE_SUCCESS(res, res); 1.2008 + 1.2009 + // finally, delete that nbsp 1.2010 + nsCOMPtr<nsIDOMNode> delNode(do_QueryInterface(thePoint.mTextNode)); 1.2011 + res = DeleteChars(delNode, thePoint.mOffset+1, delNode, thePoint.mOffset+2); 1.2012 + NS_ENSURE_SUCCESS(res, res); 1.2013 + } 1.2014 + else if (!mPRE && spaceNBSP && rightCheck) // don't mess with this preformatted for now. 1.2015 + { 1.2016 + // we have a run of ascii whitespace (which will render as one space) 1.2017 + // followed by an nbsp (which is at the end of the whitespace run). Let's 1.2018 + // switch their order. This will insure that if someone types two spaces 1.2019 + // after a sentence, and the editor softwraps at this point, the spaces wont 1.2020 + // be split across lines, which looks ugly and is bad for the moose. 1.2021 + 1.2022 + nsCOMPtr<nsIDOMNode> startNode, endNode, thenode(do_QueryInterface(prevPoint.mTextNode)); 1.2023 + int32_t startOffset, endOffset; 1.2024 + GetAsciiWSBounds(eBoth, thenode, prevPoint.mOffset+1, address_of(startNode), 1.2025 + &startOffset, address_of(endNode), &endOffset); 1.2026 + 1.2027 + // delete that nbsp 1.2028 + nsCOMPtr<nsIDOMNode> delNode(do_QueryInterface(thePoint.mTextNode)); 1.2029 + res = DeleteChars(delNode, thePoint.mOffset, delNode, thePoint.mOffset+1); 1.2030 + NS_ENSURE_SUCCESS(res, res); 1.2031 + 1.2032 + // finally, insert that nbsp before the ascii ws run 1.2033 + nsAutoTxnsConserveSelection dontSpazMySelection(mHTMLEditor); 1.2034 + nsAutoString nbspStr(nbsp); 1.2035 + nsCOMPtr<nsIDOMCharacterData> textNode(do_QueryInterface(startNode)); 1.2036 + res = mHTMLEditor->InsertTextIntoTextNodeImpl(nbspStr, textNode, startOffset, true); 1.2037 + NS_ENSURE_SUCCESS(res, res); 1.2038 + } 1.2039 + } 1.2040 + return NS_OK; 1.2041 +} 1.2042 + 1.2043 +nsresult 1.2044 +nsWSRunObject::CheckTrailingNBSP(WSFragment *aRun, nsIDOMNode *aNode, int32_t aOffset) 1.2045 +{ 1.2046 + // try to change an nbsp to a space, if possible, just to prevent nbsp proliferation. 1.2047 + // this routine is called when we about to make this point in the ws abut an inserted break 1.2048 + // or text, so we don't have to worry about what is after it. What is after it now will 1.2049 + // end up after the inserted object. 1.2050 + NS_ENSURE_TRUE(aRun && aNode, NS_ERROR_NULL_POINTER); 1.2051 + bool canConvert = false; 1.2052 + WSPoint thePoint = GetCharBefore(aNode, aOffset); 1.2053 + if (thePoint.mTextNode && thePoint.mChar == nbsp) { 1.2054 + WSPoint prevPoint = GetCharBefore(thePoint); 1.2055 + if (prevPoint.mTextNode) { 1.2056 + if (!nsCRT::IsAsciiSpace(prevPoint.mChar)) canConvert = true; 1.2057 + } else if (aRun->mLeftType == WSType::text) { 1.2058 + canConvert = true; 1.2059 + } else if (aRun->mLeftType == WSType::special) { 1.2060 + canConvert = true; 1.2061 + } 1.2062 + } 1.2063 + if (canConvert) 1.2064 + { 1.2065 + // first, insert a space 1.2066 + nsCOMPtr<nsIDOMCharacterData> textNode(do_QueryInterface(thePoint.mTextNode)); 1.2067 + NS_ENSURE_TRUE(textNode, NS_ERROR_NULL_POINTER); 1.2068 + nsAutoTxnsConserveSelection dontSpazMySelection(mHTMLEditor); 1.2069 + nsAutoString spaceStr(char16_t(32)); 1.2070 + nsresult res = mHTMLEditor->InsertTextIntoTextNodeImpl(spaceStr, textNode, 1.2071 + thePoint.mOffset, 1.2072 + true); 1.2073 + NS_ENSURE_SUCCESS(res, res); 1.2074 + 1.2075 + // finally, delete that nbsp 1.2076 + nsCOMPtr<nsIDOMNode> delNode(do_QueryInterface(thePoint.mTextNode)); 1.2077 + res = DeleteChars(delNode, thePoint.mOffset+1, delNode, thePoint.mOffset+2); 1.2078 + NS_ENSURE_SUCCESS(res, res); 1.2079 + } 1.2080 + return NS_OK; 1.2081 +} 1.2082 + 1.2083 +nsresult 1.2084 +nsWSRunObject::CheckLeadingNBSP(WSFragment *aRun, nsIDOMNode *aNode, int32_t aOffset) 1.2085 +{ 1.2086 + // try to change an nbsp to a space, if possible, just to prevent nbsp proliferation 1.2087 + // this routine is called when we about to make this point in the ws abut an inserted 1.2088 + // text, so we don't have to worry about what is before it. What is before it now will 1.2089 + // end up before the inserted text. 1.2090 + bool canConvert = false; 1.2091 + WSPoint thePoint = GetCharAfter(aNode, aOffset); 1.2092 + if (thePoint.mChar == nbsp) { 1.2093 + WSPoint tmp = thePoint; 1.2094 + tmp.mOffset++; // we want to be after thePoint 1.2095 + WSPoint nextPoint = GetCharAfter(tmp); 1.2096 + if (nextPoint.mTextNode) { 1.2097 + if (!nsCRT::IsAsciiSpace(nextPoint.mChar)) canConvert = true; 1.2098 + } else if (aRun->mRightType == WSType::text) { 1.2099 + canConvert = true; 1.2100 + } else if (aRun->mRightType == WSType::special) { 1.2101 + canConvert = true; 1.2102 + } else if (aRun->mRightType == WSType::br) { 1.2103 + canConvert = true; 1.2104 + } 1.2105 + } 1.2106 + if (canConvert) 1.2107 + { 1.2108 + // first, insert a space 1.2109 + nsCOMPtr<nsIDOMCharacterData> textNode(do_QueryInterface(thePoint.mTextNode)); 1.2110 + NS_ENSURE_TRUE(textNode, NS_ERROR_NULL_POINTER); 1.2111 + nsAutoTxnsConserveSelection dontSpazMySelection(mHTMLEditor); 1.2112 + nsAutoString spaceStr(char16_t(32)); 1.2113 + nsresult res = mHTMLEditor->InsertTextIntoTextNodeImpl(spaceStr, textNode, 1.2114 + thePoint.mOffset, 1.2115 + true); 1.2116 + NS_ENSURE_SUCCESS(res, res); 1.2117 + 1.2118 + // finally, delete that nbsp 1.2119 + nsCOMPtr<nsIDOMNode> delNode(do_QueryInterface(thePoint.mTextNode)); 1.2120 + res = DeleteChars(delNode, thePoint.mOffset+1, delNode, thePoint.mOffset+2); 1.2121 + NS_ENSURE_SUCCESS(res, res); 1.2122 + } 1.2123 + return NS_OK; 1.2124 +} 1.2125 + 1.2126 + 1.2127 +nsresult 1.2128 +nsWSRunObject::ScrubBlockBoundaryInner(nsHTMLEditor *aHTMLEd, 1.2129 + nsCOMPtr<nsIDOMNode> *aBlock, 1.2130 + BlockBoundary aBoundary) 1.2131 +{ 1.2132 + NS_ENSURE_TRUE(aBlock && aHTMLEd, NS_ERROR_NULL_POINTER); 1.2133 + int32_t offset=0; 1.2134 + if (aBoundary == kBlockEnd) 1.2135 + { 1.2136 + uint32_t uOffset; 1.2137 + aHTMLEd->GetLengthOfDOMNode(*aBlock, uOffset); 1.2138 + offset = uOffset; 1.2139 + } 1.2140 + nsWSRunObject theWSObj(aHTMLEd, *aBlock, offset); 1.2141 + return theWSObj.Scrub(); 1.2142 +} 1.2143 + 1.2144 + 1.2145 +nsresult 1.2146 +nsWSRunObject::Scrub() 1.2147 +{ 1.2148 + WSFragment *run = mStartRun; 1.2149 + while (run) 1.2150 + { 1.2151 + if (run->mType & (WSType::leadingWS | WSType::trailingWS)) { 1.2152 + nsresult res = DeleteChars(run->mStartNode, run->mStartOffset, run->mEndNode, run->mEndOffset); 1.2153 + NS_ENSURE_SUCCESS(res, res); 1.2154 + } 1.2155 + run = run->mRight; 1.2156 + } 1.2157 + return NS_OK; 1.2158 +}