1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is mozilla.org code.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Netscape Communications Corporation.
19 : * Portions created by the Initial Developer are Copyright (C) 1998
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : *
24 : * Alternatively, the contents of this file may be used under the terms of
25 : * either of the GNU General Public License Version 2 or later (the "GPL"),
26 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 : * in which case the provisions of the GPL or the LGPL are applicable instead
28 : * of those above. If you wish to allow use of your version of this file only
29 : * under the terms of either the GPL or the LGPL, and not to allow others to
30 : * use your version of this file under the terms of the MPL, indicate your
31 : * decision by deleting the provisions above and replace them with the notice
32 : * and other provisions required by the GPL or the LGPL. If you do not delete
33 : * the provisions above, a recipient may use your version of this file under
34 : * the terms of any one of the MPL, the GPL or the LGPL.
35 : *
36 : * ***** END LICENSE BLOCK ***** */
37 :
38 : #include "nsTextFragment.h"
39 : #include "nsWSRunObject.h"
40 : #include "nsIDOMNode.h"
41 : #include "nsHTMLEditor.h"
42 : #include "nsTextEditUtils.h"
43 : #include "nsIContent.h"
44 : #include "nsIDOMCharacterData.h"
45 : #include "nsCRT.h"
46 : #include "nsRange.h"
47 : #include "nsContentUtils.h"
48 :
49 : const PRUnichar nbsp = 160;
50 :
51 0 : static bool IsBlockNode(nsIDOMNode* node)
52 : {
53 0 : bool isBlock (false);
54 0 : nsHTMLEditor::NodeIsBlockStatic(node, &isBlock);
55 0 : return isBlock;
56 : }
57 :
58 : //- constructor / destructor -----------------------------------------------
59 0 : nsWSRunObject::nsWSRunObject(nsHTMLEditor *aEd, nsIDOMNode *aNode, PRInt32 aOffset) :
60 : mNode(aNode)
61 : ,mOffset(aOffset)
62 : ,mPRE(false)
63 : ,mStartNode()
64 : ,mStartOffset(0)
65 : ,mStartReason(0)
66 : ,mStartReasonNode()
67 : ,mEndNode()
68 : ,mEndOffset(0)
69 : ,mEndReason(0)
70 : ,mEndReasonNode()
71 : ,mFirstNBSPNode()
72 : ,mFirstNBSPOffset(0)
73 : ,mLastNBSPNode()
74 : ,mLastNBSPOffset(0)
75 : ,mNodeArray()
76 : ,mStartRun(nsnull)
77 : ,mEndRun(nsnull)
78 0 : ,mHTMLEditor(aEd)
79 : {
80 0 : GetWSNodes();
81 0 : GetRuns();
82 0 : }
83 :
84 0 : nsWSRunObject::~nsWSRunObject()
85 : {
86 0 : ClearRuns();
87 0 : }
88 :
89 :
90 :
91 : //--------------------------------------------------------------------------------------------
92 : // public static methods
93 : //--------------------------------------------------------------------------------------------
94 :
95 : nsresult
96 0 : nsWSRunObject::ScrubBlockBoundary(nsHTMLEditor *aHTMLEd,
97 : nsCOMPtr<nsIDOMNode> *aBlock,
98 : BlockBoundary aBoundary,
99 : PRInt32 *aOffset)
100 : {
101 0 : NS_ENSURE_TRUE(aBlock && aHTMLEd, NS_ERROR_NULL_POINTER);
102 0 : if ((aBoundary == kBlockStart) || (aBoundary == kBlockEnd))
103 0 : return ScrubBlockBoundaryInner(aHTMLEd, aBlock, aBoundary);
104 :
105 : // else we are scrubbing an outer boundary - just before or after
106 : // a block element.
107 0 : NS_ENSURE_TRUE(aOffset, NS_ERROR_NULL_POINTER);
108 0 : nsAutoTrackDOMPoint tracker(aHTMLEd->mRangeUpdater, aBlock, aOffset);
109 0 : nsWSRunObject theWSObj(aHTMLEd, *aBlock, *aOffset);
110 0 : return theWSObj.Scrub();
111 : }
112 :
113 : nsresult
114 0 : nsWSRunObject::PrepareToJoinBlocks(nsHTMLEditor *aHTMLEd,
115 : nsIDOMNode *aLeftParent,
116 : nsIDOMNode *aRightParent)
117 : {
118 0 : NS_ENSURE_TRUE(aLeftParent && aRightParent && aHTMLEd, NS_ERROR_NULL_POINTER);
119 : PRUint32 count;
120 0 : aHTMLEd->GetLengthOfDOMNode(aLeftParent, count);
121 0 : nsWSRunObject leftWSObj(aHTMLEd, aLeftParent, count);
122 0 : nsWSRunObject rightWSObj(aHTMLEd, aRightParent, 0);
123 :
124 0 : return leftWSObj.PrepareToDeleteRangePriv(&rightWSObj);
125 : }
126 :
127 : nsresult
128 0 : nsWSRunObject::PrepareToDeleteRange(nsHTMLEditor *aHTMLEd,
129 : nsCOMPtr<nsIDOMNode> *aStartNode,
130 : PRInt32 *aStartOffset,
131 : nsCOMPtr<nsIDOMNode> *aEndNode,
132 : PRInt32 *aEndOffset)
133 : {
134 0 : NS_ENSURE_TRUE(aStartNode && aEndNode && *aStartNode && *aEndNode && aStartOffset && aEndOffset && aHTMLEd, NS_ERROR_NULL_POINTER);
135 :
136 0 : nsAutoTrackDOMPoint trackerStart(aHTMLEd->mRangeUpdater, aStartNode, aStartOffset);
137 0 : nsAutoTrackDOMPoint trackerEnd(aHTMLEd->mRangeUpdater, aEndNode, aEndOffset);
138 :
139 0 : nsWSRunObject leftWSObj(aHTMLEd, *aStartNode, *aStartOffset);
140 0 : nsWSRunObject rightWSObj(aHTMLEd, *aEndNode, *aEndOffset);
141 :
142 0 : return leftWSObj.PrepareToDeleteRangePriv(&rightWSObj);
143 : }
144 :
145 : nsresult
146 0 : nsWSRunObject::PrepareToDeleteNode(nsHTMLEditor *aHTMLEd,
147 : nsIDOMNode *aNode)
148 : {
149 0 : NS_ENSURE_TRUE(aNode && aHTMLEd, NS_ERROR_NULL_POINTER);
150 0 : nsresult res = NS_OK;
151 :
152 0 : nsCOMPtr<nsIDOMNode> parent;
153 : PRInt32 offset;
154 0 : res = aHTMLEd->GetNodeLocation(aNode, address_of(parent), &offset);
155 0 : NS_ENSURE_SUCCESS(res, res);
156 :
157 0 : nsWSRunObject leftWSObj(aHTMLEd, parent, offset);
158 0 : nsWSRunObject rightWSObj(aHTMLEd, parent, offset+1);
159 :
160 0 : return leftWSObj.PrepareToDeleteRangePriv(&rightWSObj);
161 : }
162 :
163 : nsresult
164 0 : nsWSRunObject::PrepareToSplitAcrossBlocks(nsHTMLEditor *aHTMLEd,
165 : nsCOMPtr<nsIDOMNode> *aSplitNode,
166 : PRInt32 *aSplitOffset)
167 : {
168 0 : NS_ENSURE_TRUE(aSplitNode && aSplitOffset && *aSplitNode && aHTMLEd, NS_ERROR_NULL_POINTER);
169 :
170 0 : nsAutoTrackDOMPoint tracker(aHTMLEd->mRangeUpdater, aSplitNode, aSplitOffset);
171 :
172 0 : nsWSRunObject wsObj(aHTMLEd, *aSplitNode, *aSplitOffset);
173 :
174 0 : return wsObj.PrepareToSplitAcrossBlocksPriv();
175 : }
176 :
177 : //--------------------------------------------------------------------------------------------
178 : // public instance methods
179 : //--------------------------------------------------------------------------------------------
180 :
181 : nsresult
182 0 : nsWSRunObject::InsertBreak(nsCOMPtr<nsIDOMNode> *aInOutParent,
183 : PRInt32 *aInOutOffset,
184 : nsCOMPtr<nsIDOMNode> *outBRNode,
185 : nsIEditor::EDirection aSelect)
186 : {
187 : // MOOSE: for now, we always assume non-PRE formatting. Fix this later.
188 : // meanwhile, the pre case is handled in WillInsertText in nsHTMLEditRules.cpp
189 0 : NS_ENSURE_TRUE(aInOutParent && aInOutOffset && outBRNode, NS_ERROR_NULL_POINTER);
190 :
191 0 : nsresult res = NS_OK;
192 : WSFragment *beforeRun, *afterRun;
193 0 : res = FindRun(*aInOutParent, *aInOutOffset, &beforeRun, false);
194 0 : res = FindRun(*aInOutParent, *aInOutOffset, &afterRun, true);
195 :
196 : {
197 : // some scoping for nsAutoTrackDOMPoint. This will track our insertion point
198 : // while we tweak any surrounding whitespace
199 0 : nsAutoTrackDOMPoint tracker(mHTMLEditor->mRangeUpdater, aInOutParent, aInOutOffset);
200 :
201 : // handle any changes needed to ws run after inserted br
202 0 : if (!afterRun)
203 : {
204 : // don't need to do anything. just insert break. ws won't change.
205 : }
206 0 : else if (afterRun->mType & eTrailingWS)
207 : {
208 : // don't need to do anything. just insert break. ws won't change.
209 : }
210 0 : else if (afterRun->mType & eLeadingWS)
211 : {
212 : // delete the leading ws that is after insertion point. We don't
213 : // have to (it would still not be significant after br), but it's
214 : // just more aesthetically pleasing to.
215 : res = DeleteChars(*aInOutParent, *aInOutOffset, afterRun->mEndNode, afterRun->mEndOffset,
216 0 : eOutsideUserSelectAll);
217 0 : NS_ENSURE_SUCCESS(res, res);
218 : }
219 0 : else if (afterRun->mType == eNormalWS)
220 : {
221 : // need to determine if break at front of non-nbsp run. if so
222 : // convert run to nbsp.
223 0 : WSPoint thePoint;
224 0 : res = GetCharAfter(*aInOutParent, *aInOutOffset, &thePoint);
225 0 : if ( (NS_SUCCEEDED(res)) && thePoint.mTextNode && (nsCRT::IsAsciiSpace(thePoint.mChar)) )
226 : {
227 0 : WSPoint prevPoint;
228 0 : res = GetCharBefore(thePoint, &prevPoint);
229 0 : if ( (NS_FAILED(res)) || (prevPoint.mTextNode && !nsCRT::IsAsciiSpace(prevPoint.mChar)) )
230 : {
231 : // we are at start of non-nbsps. convert to a single nbsp.
232 0 : res = ConvertToNBSP(thePoint);
233 0 : NS_ENSURE_SUCCESS(res, res);
234 : }
235 : }
236 : }
237 :
238 : // handle any changes needed to ws run before inserted br
239 0 : if (!beforeRun)
240 : {
241 : // don't need to do anything. just insert break. ws won't change.
242 : }
243 0 : else if (beforeRun->mType & eLeadingWS)
244 : {
245 : // don't need to do anything. just insert break. ws won't change.
246 : }
247 0 : else if (beforeRun->mType & eTrailingWS)
248 : {
249 : // need to delete the trailing ws that is before insertion point, because it
250 : // would become significant after break inserted.
251 : res = DeleteChars(beforeRun->mStartNode, beforeRun->mStartOffset, *aInOutParent, *aInOutOffset,
252 0 : eOutsideUserSelectAll);
253 0 : NS_ENSURE_SUCCESS(res, res);
254 : }
255 0 : else if (beforeRun->mType == eNormalWS)
256 : {
257 : // try to change an nbsp to a space, if possible, just to prevent nbsp proliferation
258 0 : res = CheckTrailingNBSP(beforeRun, *aInOutParent, *aInOutOffset);
259 0 : NS_ENSURE_SUCCESS(res, res);
260 : }
261 : }
262 :
263 : // ready, aim, fire!
264 0 : return mHTMLEditor->CreateBRImpl(aInOutParent, aInOutOffset, outBRNode, aSelect);
265 : }
266 :
267 : nsresult
268 0 : nsWSRunObject::InsertText(const nsAString& aStringToInsert,
269 : nsCOMPtr<nsIDOMNode> *aInOutParent,
270 : PRInt32 *aInOutOffset,
271 : nsIDOMDocument *aDoc)
272 : {
273 : // MOOSE: for now, we always assume non-PRE formatting. Fix this later.
274 : // meanwhile, the pre case is handled in WillInsertText in nsHTMLEditRules.cpp
275 :
276 : // MOOSE: for now, just getting the ws logic straight. This implementation
277 : // is very slow. Will need to replace edit rules impl with a more efficient
278 : // text sink here that does the minimal amount of searching/replacing/copying
279 :
280 0 : NS_ENSURE_TRUE(aInOutParent && aInOutOffset && aDoc, NS_ERROR_NULL_POINTER);
281 :
282 0 : nsresult res = NS_OK;
283 0 : if (aStringToInsert.IsEmpty()) return res;
284 :
285 : // string copying sux.
286 0 : nsAutoString theString(aStringToInsert);
287 :
288 : WSFragment *beforeRun, *afterRun;
289 0 : res = FindRun(*aInOutParent, *aInOutOffset, &beforeRun, false);
290 0 : res = FindRun(*aInOutParent, *aInOutOffset, &afterRun, true);
291 :
292 : {
293 : // some scoping for nsAutoTrackDOMPoint. This will track our insertion point
294 : // while we tweak any surrounding whitespace
295 0 : nsAutoTrackDOMPoint tracker(mHTMLEditor->mRangeUpdater, aInOutParent, aInOutOffset);
296 :
297 : // handle any changes needed to ws run after inserted text
298 0 : if (!afterRun)
299 : {
300 : // don't need to do anything. just insert text. ws won't change.
301 : }
302 0 : else if (afterRun->mType & eTrailingWS)
303 : {
304 : // don't need to do anything. just insert text. ws won't change.
305 : }
306 0 : else if (afterRun->mType & eLeadingWS)
307 : {
308 : // delete the leading ws that is after insertion point, because it
309 : // would become significant after text inserted.
310 : res = DeleteChars(*aInOutParent, *aInOutOffset, afterRun->mEndNode, afterRun->mEndOffset,
311 0 : eOutsideUserSelectAll);
312 0 : NS_ENSURE_SUCCESS(res, res);
313 : }
314 0 : else if (afterRun->mType == eNormalWS)
315 : {
316 : // try to change an nbsp to a space, if possible, just to prevent nbsp proliferation
317 0 : res = CheckLeadingNBSP(afterRun, *aInOutParent, *aInOutOffset);
318 0 : NS_ENSURE_SUCCESS(res, res);
319 : }
320 :
321 : // handle any changes needed to ws run before inserted text
322 0 : if (!beforeRun)
323 : {
324 : // don't need to do anything. just insert text. ws won't change.
325 : }
326 0 : else if (beforeRun->mType & eLeadingWS)
327 : {
328 : // don't need to do anything. just insert text. ws won't change.
329 : }
330 0 : else if (beforeRun->mType & eTrailingWS)
331 : {
332 : // need to delete the trailing ws that is before insertion point, because it
333 : // would become significant after text inserted.
334 : res = DeleteChars(beforeRun->mStartNode, beforeRun->mStartOffset, *aInOutParent, *aInOutOffset,
335 0 : eOutsideUserSelectAll);
336 0 : NS_ENSURE_SUCCESS(res, res);
337 : }
338 0 : else if (beforeRun->mType == eNormalWS)
339 : {
340 : // try to change an nbsp to a space, if possible, just to prevent nbsp proliferation
341 0 : res = CheckTrailingNBSP(beforeRun, *aInOutParent, *aInOutOffset);
342 0 : NS_ENSURE_SUCCESS(res, res);
343 : }
344 : }
345 :
346 : // next up, tweak head and tail of string as needed.
347 : // first the head:
348 : // there are a variety of circumstances that would require us to convert a
349 : // leading ws char into an nbsp:
350 :
351 0 : if (nsCRT::IsAsciiSpace(theString[0]))
352 : {
353 : // we have a leading space
354 0 : if (beforeRun)
355 : {
356 0 : if (beforeRun->mType & eLeadingWS)
357 : {
358 0 : theString.SetCharAt(nbsp, 0);
359 : }
360 0 : else if (beforeRun->mType & eNormalWS)
361 : {
362 0 : WSPoint wspoint;
363 0 : res = GetCharBefore(*aInOutParent, *aInOutOffset, &wspoint);
364 0 : if (NS_SUCCEEDED(res) && wspoint.mTextNode && nsCRT::IsAsciiSpace(wspoint.mChar))
365 : {
366 0 : theString.SetCharAt(nbsp, 0);
367 : }
368 : }
369 : }
370 : else
371 : {
372 0 : if ((mStartReason & eBlock) || (mStartReason == eBreak))
373 : {
374 0 : theString.SetCharAt(nbsp, 0);
375 : }
376 : }
377 : }
378 :
379 : // then the tail
380 0 : PRUint32 lastCharIndex = theString.Length()-1;
381 :
382 0 : if (nsCRT::IsAsciiSpace(theString[lastCharIndex]))
383 : {
384 : // we have a leading space
385 0 : if (afterRun)
386 : {
387 0 : if (afterRun->mType & eTrailingWS)
388 : {
389 0 : theString.SetCharAt(nbsp, lastCharIndex);
390 : }
391 0 : else if (afterRun->mType & eNormalWS)
392 : {
393 0 : WSPoint wspoint;
394 0 : res = GetCharAfter(*aInOutParent, *aInOutOffset, &wspoint);
395 0 : if (NS_SUCCEEDED(res) && wspoint.mTextNode && nsCRT::IsAsciiSpace(wspoint.mChar))
396 : {
397 0 : theString.SetCharAt(nbsp, lastCharIndex);
398 : }
399 : }
400 : }
401 : else
402 : {
403 0 : if ((mEndReason & eBlock))
404 : {
405 0 : theString.SetCharAt(nbsp, lastCharIndex);
406 : }
407 : }
408 : }
409 :
410 : // next scan string for adjacent ws and convert to nbsp/space combos
411 : // MOOSE: don't need to convert tabs here since that is done by WillInsertText()
412 : // before we are called. Eventually, all that logic will be pushed down into
413 : // here and made more efficient.
414 : PRUint32 j;
415 0 : bool prevWS = false;
416 0 : for (j=0; j<=lastCharIndex; j++)
417 : {
418 0 : if (nsCRT::IsAsciiSpace(theString[j]))
419 : {
420 0 : if (prevWS)
421 : {
422 0 : theString.SetCharAt(nbsp, j-1); // j-1 can't be negative because prevWS starts out false
423 : }
424 : else
425 : {
426 0 : prevWS = true;
427 : }
428 : }
429 : else
430 : {
431 0 : prevWS = false;
432 : }
433 : }
434 :
435 : // ready, aim, fire!
436 0 : res = mHTMLEditor->InsertTextImpl(theString, aInOutParent, aInOutOffset, aDoc);
437 0 : return NS_OK;
438 : }
439 :
440 : nsresult
441 0 : nsWSRunObject::DeleteWSBackward()
442 : {
443 0 : nsresult res = NS_OK;
444 0 : WSPoint point;
445 0 : res = GetCharBefore(mNode, mOffset, &point);
446 0 : NS_ENSURE_SUCCESS(res, res);
447 0 : NS_ENSURE_TRUE(point.mTextNode, NS_OK); // nothing to delete
448 :
449 0 : if (mPRE) // easy case, preformatted ws
450 : {
451 0 : if (nsCRT::IsAsciiSpace(point.mChar) || (point.mChar == nbsp))
452 : {
453 0 : nsCOMPtr<nsIDOMNode> node(do_QueryInterface(point.mTextNode));
454 0 : PRInt32 startOffset = point.mOffset;
455 0 : PRInt32 endOffset = point.mOffset+1;
456 0 : return DeleteChars(node, startOffset, node, endOffset);
457 : }
458 : }
459 :
460 : // callers job to insure that previous char is really ws.
461 : // If it is normal ws, we need to delete the whole run
462 0 : if (nsCRT::IsAsciiSpace(point.mChar))
463 : {
464 0 : nsCOMPtr<nsIDOMNode> startNode, endNode, node(do_QueryInterface(point.mTextNode));
465 : PRInt32 startOffset, endOffset;
466 : res = GetAsciiWSBounds(eBoth, node, point.mOffset+1, address_of(startNode),
467 0 : &startOffset, address_of(endNode), &endOffset);
468 0 : NS_ENSURE_SUCCESS(res, res);
469 :
470 : // adjust surrounding ws
471 : res = nsWSRunObject::PrepareToDeleteRange(mHTMLEditor, address_of(startNode), &startOffset,
472 0 : address_of(endNode), &endOffset);
473 0 : NS_ENSURE_SUCCESS(res, res);
474 :
475 : // finally, delete that ws
476 0 : return DeleteChars(startNode, startOffset, endNode, endOffset);
477 : }
478 0 : else if (point.mChar == nbsp)
479 : {
480 0 : nsCOMPtr<nsIDOMNode> node(do_QueryInterface(point.mTextNode));
481 : // adjust surrounding ws
482 0 : PRInt32 startOffset = point.mOffset;
483 0 : PRInt32 endOffset = point.mOffset+1;
484 : res = nsWSRunObject::PrepareToDeleteRange(mHTMLEditor, address_of(node), &startOffset,
485 0 : address_of(node), &endOffset);
486 0 : NS_ENSURE_SUCCESS(res, res);
487 :
488 : // finally, delete that ws
489 0 : return DeleteChars(node, startOffset, node, endOffset);
490 :
491 : }
492 0 : return NS_OK;
493 : }
494 :
495 : nsresult
496 0 : nsWSRunObject::DeleteWSForward()
497 : {
498 0 : nsresult res = NS_OK;
499 0 : WSPoint point;
500 0 : res = GetCharAfter(mNode, mOffset, &point);
501 0 : NS_ENSURE_SUCCESS(res, res);
502 0 : NS_ENSURE_TRUE(point.mTextNode, NS_OK); // nothing to delete
503 :
504 0 : if (mPRE) // easy case, preformatted ws
505 : {
506 0 : if (nsCRT::IsAsciiSpace(point.mChar) || (point.mChar == nbsp))
507 : {
508 0 : nsCOMPtr<nsIDOMNode> node(do_QueryInterface(point.mTextNode));
509 0 : PRInt32 startOffset = point.mOffset;
510 0 : PRInt32 endOffset = point.mOffset+1;
511 0 : return DeleteChars(node, startOffset, node, endOffset);
512 : }
513 : }
514 :
515 : // callers job to insure that next char is really ws.
516 : // If it is normal ws, we need to delete the whole run
517 0 : if (nsCRT::IsAsciiSpace(point.mChar))
518 : {
519 0 : nsCOMPtr<nsIDOMNode> startNode, endNode, node(do_QueryInterface(point.mTextNode));
520 : PRInt32 startOffset, endOffset;
521 : res = GetAsciiWSBounds(eBoth, node, point.mOffset+1, address_of(startNode),
522 0 : &startOffset, address_of(endNode), &endOffset);
523 0 : NS_ENSURE_SUCCESS(res, res);
524 :
525 : // adjust surrounding ws
526 : res = nsWSRunObject::PrepareToDeleteRange(mHTMLEditor, address_of(startNode), &startOffset,
527 0 : address_of(endNode), &endOffset);
528 0 : NS_ENSURE_SUCCESS(res, res);
529 :
530 : // finally, delete that ws
531 0 : return DeleteChars(startNode, startOffset, endNode, endOffset);
532 : }
533 0 : else if (point.mChar == nbsp)
534 : {
535 0 : nsCOMPtr<nsIDOMNode> node(do_QueryInterface(point.mTextNode));
536 : // adjust surrounding ws
537 0 : PRInt32 startOffset = point.mOffset;
538 0 : PRInt32 endOffset = point.mOffset+1;
539 : res = nsWSRunObject::PrepareToDeleteRange(mHTMLEditor, address_of(node), &startOffset,
540 0 : address_of(node), &endOffset);
541 0 : NS_ENSURE_SUCCESS(res, res);
542 :
543 : // finally, delete that ws
544 0 : return DeleteChars(node, startOffset, node, endOffset);
545 :
546 : }
547 0 : return NS_OK;
548 : }
549 :
550 : nsresult
551 0 : nsWSRunObject::PriorVisibleNode(nsIDOMNode *aNode,
552 : PRInt32 aOffset,
553 : nsCOMPtr<nsIDOMNode> *outVisNode,
554 : PRInt32 *outVisOffset,
555 : PRInt16 *outType)
556 : {
557 : // Find first visible thing before the point. position outVisNode/outVisOffset
558 : // just _after_ that thing. If we don't find anything return start of ws.
559 0 : NS_ENSURE_TRUE(aNode && outVisNode && outVisOffset && outType, NS_ERROR_NULL_POINTER);
560 :
561 0 : *outType = eNone;
562 : WSFragment *run;
563 0 : FindRun(aNode, aOffset, &run, false);
564 :
565 : // is there a visible run there or earlier?
566 0 : while (run)
567 : {
568 0 : if (run->mType == eNormalWS)
569 : {
570 0 : WSPoint point;
571 0 : GetCharBefore(aNode, aOffset, &point);
572 0 : if (point.mTextNode)
573 : {
574 0 : *outVisNode = do_QueryInterface(point.mTextNode);
575 0 : *outVisOffset = point.mOffset+1;
576 0 : if (nsCRT::IsAsciiSpace(point.mChar) || (point.mChar==nbsp))
577 : {
578 0 : *outType = eNormalWS;
579 : }
580 0 : else if (!point.mChar)
581 : {
582 : // MOOSE: not possible?
583 0 : *outType = eNone;
584 : }
585 : else
586 : {
587 0 : *outType = eText;
588 : }
589 0 : return NS_OK;
590 : }
591 : // else if no text node then keep looking. We should eventually fall out of loop
592 : }
593 :
594 0 : run = run->mLeft;
595 : }
596 :
597 : // if we get here then nothing in ws data to find. return start reason
598 0 : *outVisNode = mStartReasonNode;
599 0 : *outVisOffset = mStartOffset; // this really isn't meaningful if mStartReasonNode!=mStartNode
600 0 : *outType = mStartReason;
601 0 : return NS_OK;
602 : }
603 :
604 :
605 : nsresult
606 0 : nsWSRunObject::NextVisibleNode (nsIDOMNode *aNode,
607 : PRInt32 aOffset,
608 : nsCOMPtr<nsIDOMNode> *outVisNode,
609 : PRInt32 *outVisOffset,
610 : PRInt16 *outType)
611 : {
612 : // Find first visible thing after the point. position outVisNode/outVisOffset
613 : // just _before_ that thing. If we don't find anything return end of ws.
614 0 : NS_ENSURE_TRUE(aNode && outVisNode && outVisOffset && outType, NS_ERROR_NULL_POINTER);
615 :
616 : WSFragment *run;
617 0 : FindRun(aNode, aOffset, &run, true);
618 :
619 : // is there a visible run there or later?
620 0 : while (run)
621 : {
622 0 : if (run->mType == eNormalWS)
623 : {
624 0 : WSPoint point;
625 0 : GetCharAfter(aNode, aOffset, &point);
626 0 : if (point.mTextNode)
627 : {
628 0 : *outVisNode = do_QueryInterface(point.mTextNode);
629 0 : *outVisOffset = point.mOffset;
630 0 : if (nsCRT::IsAsciiSpace(point.mChar) || (point.mChar==nbsp))
631 : {
632 0 : *outType = eNormalWS;
633 : }
634 0 : else if (!point.mChar)
635 : {
636 : // MOOSE: not possible?
637 0 : *outType = eNone;
638 : }
639 : else
640 : {
641 0 : *outType = eText;
642 : }
643 0 : return NS_OK;
644 : }
645 : // else if no text node then keep looking. We should eventually fall out of loop
646 : }
647 :
648 0 : run = run->mRight;
649 : }
650 :
651 : // if we get here then nothing in ws data to find. return end reason
652 0 : *outVisNode = mEndReasonNode;
653 0 : *outVisOffset = mEndOffset; // this really isn't meaningful if mEndReasonNode!=mEndNode
654 0 : *outType = mEndReason;
655 0 : return NS_OK;
656 : }
657 :
658 : nsresult
659 0 : nsWSRunObject::AdjustWhitespace()
660 : {
661 : // this routine examines a run of ws and tries to get rid of some unneeded nbsp's,
662 : // replacing them with regualr ascii space if possible. Keeping things simple
663 : // for now and just trying to fix up the trailing ws in the run.
664 0 : if (!mLastNBSPNode) {
665 : // nothing to do!
666 0 : return NS_OK;
667 : }
668 0 : nsresult res = NS_OK;
669 0 : WSFragment *curRun = mStartRun;
670 0 : while (curRun)
671 : {
672 : // look for normal ws run
673 0 : if (curRun->mType == eNormalWS)
674 : {
675 0 : res = CheckTrailingNBSPOfRun(curRun);
676 0 : break;
677 : }
678 0 : curRun = curRun->mRight;
679 : }
680 0 : return res;
681 : }
682 :
683 :
684 : //--------------------------------------------------------------------------------------------
685 : // protected methods
686 : //--------------------------------------------------------------------------------------------
687 :
688 : already_AddRefed<nsIDOMNode>
689 0 : nsWSRunObject::GetWSBoundingParent()
690 : {
691 0 : NS_ENSURE_TRUE(mNode, nsnull);
692 0 : nsCOMPtr<nsIDOMNode> wsBoundingParent = mNode;
693 0 : while (!IsBlockNode(wsBoundingParent))
694 : {
695 0 : nsCOMPtr<nsIDOMNode> parent;
696 0 : wsBoundingParent->GetParentNode(getter_AddRefs(parent));
697 0 : if (!parent || !mHTMLEditor->IsEditable(parent))
698 : break;
699 0 : wsBoundingParent.swap(parent);
700 : }
701 0 : return wsBoundingParent.forget();
702 : }
703 :
704 : nsresult
705 0 : nsWSRunObject::GetWSNodes()
706 : {
707 : // collect up an array of nodes that are contiguous with the insertion point
708 : // and which contain only whitespace. Stop if you reach non-ws text or a new
709 : // block boundary.
710 0 : nsresult res = NS_OK;
711 :
712 0 : DOMPoint start(mNode, mOffset), end(mNode, mOffset);
713 0 : nsCOMPtr<nsIDOMNode> wsBoundingParent = GetWSBoundingParent();
714 :
715 : // first look backwards to find preceding ws nodes
716 0 : if (mHTMLEditor->IsTextNode(mNode))
717 : {
718 0 : nsCOMPtr<nsIContent> textNode(do_QueryInterface(mNode));
719 0 : const nsTextFragment *textFrag = textNode->GetText();
720 :
721 0 : res = PrependNodeToList(mNode);
722 0 : NS_ENSURE_SUCCESS(res, res);
723 0 : if (mOffset)
724 : {
725 : PRInt32 pos;
726 0 : for (pos=mOffset-1; pos>=0; pos--)
727 : {
728 : // sanity bounds check the char position. bug 136165
729 0 : if (PRUint32(pos) >= textFrag->GetLength())
730 : {
731 0 : NS_NOTREACHED("looking beyond end of text fragment");
732 0 : continue;
733 : }
734 0 : PRUnichar theChar = textFrag->CharAt(pos);
735 0 : if (!nsCRT::IsAsciiSpace(theChar))
736 : {
737 0 : if (theChar != nbsp)
738 : {
739 0 : mStartNode = mNode;
740 0 : mStartOffset = pos+1;
741 0 : mStartReason = eText;
742 0 : mStartReasonNode = mNode;
743 0 : break;
744 : }
745 : // as we look backwards update our earliest found nbsp
746 0 : mFirstNBSPNode = mNode;
747 0 : mFirstNBSPOffset = pos;
748 : // also keep track of latest nbsp so far
749 0 : if (!mLastNBSPNode)
750 : {
751 0 : mLastNBSPNode = mNode;
752 0 : mLastNBSPOffset = pos;
753 : }
754 : }
755 0 : start.SetPoint(mNode,pos);
756 : }
757 : }
758 : }
759 :
760 0 : nsCOMPtr<nsIDOMNode> priorNode;
761 0 : while (!mStartNode)
762 : {
763 : // we haven't found the start of ws yet. Keep looking
764 0 : res = GetPreviousWSNode(start, wsBoundingParent, address_of(priorNode));
765 0 : NS_ENSURE_SUCCESS(res, res);
766 0 : if (priorNode)
767 : {
768 0 : if (IsBlockNode(priorNode))
769 : {
770 0 : start.GetPoint(mStartNode, mStartOffset);
771 0 : mStartReason = eOtherBlock;
772 0 : mStartReasonNode = priorNode;
773 : }
774 0 : else if (mHTMLEditor->IsTextNode(priorNode))
775 : {
776 0 : res = PrependNodeToList(priorNode);
777 0 : NS_ENSURE_SUCCESS(res, res);
778 0 : nsCOMPtr<nsIContent> textNode(do_QueryInterface(priorNode));
779 : const nsTextFragment *textFrag;
780 0 : if (!textNode || !(textFrag = textNode->GetText())) {
781 0 : return NS_ERROR_NULL_POINTER;
782 : }
783 0 : PRUint32 len = textNode->TextLength();
784 :
785 0 : if (len < 1)
786 : {
787 : // Zero length text node. Set start point to it
788 : // so we can get past it!
789 0 : start.SetPoint(priorNode,0);
790 : }
791 : else
792 : {
793 : PRInt32 pos;
794 0 : for (pos=len-1; pos>=0; pos--)
795 : {
796 : // sanity bounds check the char position. bug 136165
797 0 : if (PRUint32(pos) >= textFrag->GetLength())
798 : {
799 0 : NS_NOTREACHED("looking beyond end of text fragment");
800 0 : continue;
801 : }
802 0 : PRUnichar theChar = textFrag->CharAt(pos);
803 0 : if (!nsCRT::IsAsciiSpace(theChar))
804 : {
805 0 : if (theChar != nbsp)
806 : {
807 0 : mStartNode = priorNode;
808 0 : mStartOffset = pos+1;
809 0 : mStartReason = eText;
810 0 : mStartReasonNode = priorNode;
811 0 : break;
812 : }
813 : // as we look backwards update our earliest found nbsp
814 0 : mFirstNBSPNode = priorNode;
815 0 : mFirstNBSPOffset = pos;
816 : // also keep track of latest nbsp so far
817 0 : if (!mLastNBSPNode)
818 : {
819 0 : mLastNBSPNode = priorNode;
820 0 : mLastNBSPOffset = pos;
821 : }
822 : }
823 0 : start.SetPoint(priorNode,pos);
824 : }
825 : }
826 : }
827 : else
828 : {
829 : // it's a break or a special node, like <img>, that is not a block and not
830 : // a break but still serves as a terminator to ws runs.
831 0 : start.GetPoint(mStartNode, mStartOffset);
832 0 : if (nsTextEditUtils::IsBreak(priorNode))
833 0 : mStartReason = eBreak;
834 : else
835 0 : mStartReason = eSpecial;
836 0 : mStartReasonNode = priorNode;
837 : }
838 : }
839 : else
840 : {
841 : // no prior node means we exhausted wsBoundingParent
842 0 : start.GetPoint(mStartNode, mStartOffset);
843 0 : mStartReason = eThisBlock;
844 0 : mStartReasonNode = wsBoundingParent;
845 : }
846 : }
847 :
848 : // then look ahead to find following ws nodes
849 0 : if (mHTMLEditor->IsTextNode(mNode))
850 : {
851 : // don't need to put it on list. it already is from code above
852 0 : nsCOMPtr<nsIContent> textNode(do_QueryInterface(mNode));
853 0 : const nsTextFragment *textFrag = textNode->GetText();
854 :
855 0 : PRUint32 len = textNode->TextLength();
856 0 : if (PRUint16(mOffset)<len)
857 : {
858 : PRInt32 pos;
859 0 : for (pos=mOffset; PRUint32(pos)<len; pos++)
860 : {
861 : // sanity bounds check the char position. bug 136165
862 0 : if ((pos<0) || (PRUint32(pos)>=textFrag->GetLength()))
863 : {
864 0 : NS_NOTREACHED("looking beyond end of text fragment");
865 0 : continue;
866 : }
867 0 : PRUnichar theChar = textFrag->CharAt(pos);
868 0 : if (!nsCRT::IsAsciiSpace(theChar))
869 : {
870 0 : if (theChar != nbsp)
871 : {
872 0 : mEndNode = mNode;
873 0 : mEndOffset = pos;
874 0 : mEndReason = eText;
875 0 : mEndReasonNode = mNode;
876 0 : break;
877 : }
878 : // as we look forwards update our latest found nbsp
879 0 : mLastNBSPNode = mNode;
880 0 : mLastNBSPOffset = pos;
881 : // also keep track of earliest nbsp so far
882 0 : if (!mFirstNBSPNode)
883 : {
884 0 : mFirstNBSPNode = mNode;
885 0 : mFirstNBSPOffset = pos;
886 : }
887 : }
888 0 : end.SetPoint(mNode,pos+1);
889 : }
890 : }
891 : }
892 :
893 0 : nsCOMPtr<nsIDOMNode> nextNode;
894 0 : while (!mEndNode)
895 : {
896 : // we haven't found the end of ws yet. Keep looking
897 0 : res = GetNextWSNode(end, wsBoundingParent, address_of(nextNode));
898 0 : NS_ENSURE_SUCCESS(res, res);
899 0 : if (nextNode)
900 : {
901 0 : if (IsBlockNode(nextNode))
902 : {
903 : // we encountered a new block. therefore no more ws.
904 0 : end.GetPoint(mEndNode, mEndOffset);
905 0 : mEndReason = eOtherBlock;
906 0 : mEndReasonNode = nextNode;
907 : }
908 0 : else if (mHTMLEditor->IsTextNode(nextNode))
909 : {
910 0 : res = AppendNodeToList(nextNode);
911 0 : NS_ENSURE_SUCCESS(res, res);
912 0 : nsCOMPtr<nsIContent> textNode(do_QueryInterface(nextNode));
913 : const nsTextFragment *textFrag;
914 0 : if (!textNode || !(textFrag = textNode->GetText())) {
915 0 : return NS_ERROR_NULL_POINTER;
916 : }
917 0 : PRUint32 len = textNode->TextLength();
918 :
919 0 : if (len < 1)
920 : {
921 : // Zero length text node. Set end point to it
922 : // so we can get past it!
923 0 : end.SetPoint(nextNode,0);
924 : }
925 : else
926 : {
927 : PRInt32 pos;
928 0 : for (pos=0; PRUint32(pos)<len; pos++)
929 : {
930 : // sanity bounds check the char position. bug 136165
931 0 : if (PRUint32(pos) >= textFrag->GetLength())
932 : {
933 0 : NS_NOTREACHED("looking beyond end of text fragment");
934 0 : continue;
935 : }
936 0 : PRUnichar theChar = textFrag->CharAt(pos);
937 0 : if (!nsCRT::IsAsciiSpace(theChar))
938 : {
939 0 : if (theChar != nbsp)
940 : {
941 0 : mEndNode = nextNode;
942 0 : mEndOffset = pos;
943 0 : mEndReason = eText;
944 0 : mEndReasonNode = nextNode;
945 0 : break;
946 : }
947 : // as we look forwards update our latest found nbsp
948 0 : mLastNBSPNode = nextNode;
949 0 : mLastNBSPOffset = pos;
950 : // also keep track of earliest nbsp so far
951 0 : if (!mFirstNBSPNode)
952 : {
953 0 : mFirstNBSPNode = nextNode;
954 0 : mFirstNBSPOffset = pos;
955 : }
956 : }
957 0 : end.SetPoint(nextNode,pos+1);
958 : }
959 : }
960 : }
961 : else
962 : {
963 : // we encountered a break or a special node, like <img>,
964 : // that is not a block and not a break but still
965 : // serves as a terminator to ws runs.
966 0 : end.GetPoint(mEndNode, mEndOffset);
967 0 : if (nsTextEditUtils::IsBreak(nextNode))
968 0 : mEndReason = eBreak;
969 : else
970 0 : mEndReason = eSpecial;
971 0 : mEndReasonNode = nextNode;
972 : }
973 : }
974 : else
975 : {
976 : // no next node means we exhausted wsBoundingParent
977 0 : end.GetPoint(mEndNode, mEndOffset);
978 0 : mEndReason = eThisBlock;
979 0 : mEndReasonNode = wsBoundingParent;
980 : }
981 : }
982 :
983 0 : return NS_OK;
984 : }
985 :
986 : nsresult
987 0 : nsWSRunObject::GetRuns()
988 : {
989 0 : ClearRuns();
990 :
991 : // handle some easy cases first
992 0 : mHTMLEditor->IsPreformatted(mNode, &mPRE);
993 : // if it's preformatedd, or if we are surrounded by text or special, it's all one
994 : // big normal ws run
995 0 : if ( mPRE || (((mStartReason == eText) || (mStartReason == eSpecial)) &&
996 : ((mEndReason == eText) || (mEndReason == eSpecial) || (mEndReason == eBreak))) )
997 : {
998 0 : return MakeSingleWSRun(eNormalWS);
999 : }
1000 :
1001 : // if we are before or after a block (or after a break), and there are no nbsp's,
1002 : // then it's all non-rendering ws.
1003 0 : if ( !(mFirstNBSPNode || mLastNBSPNode) &&
1004 : ( (mStartReason & eBlock) || (mStartReason == eBreak) || (mEndReason & eBlock) ) )
1005 : {
1006 0 : PRInt16 wstype = eNone;
1007 0 : if ((mStartReason & eBlock) || (mStartReason == eBreak))
1008 0 : wstype = eLeadingWS;
1009 0 : if (mEndReason & eBlock)
1010 0 : wstype |= eTrailingWS;
1011 0 : return MakeSingleWSRun(wstype);
1012 : }
1013 :
1014 : // otherwise a little trickier. shucks.
1015 0 : mStartRun = new WSFragment();
1016 0 : NS_ENSURE_TRUE(mStartRun, NS_ERROR_NULL_POINTER);
1017 0 : mStartRun->mStartNode = mStartNode;
1018 0 : mStartRun->mStartOffset = mStartOffset;
1019 :
1020 0 : if ( (mStartReason & eBlock) || (mStartReason == eBreak) )
1021 : {
1022 : // set up mStartRun
1023 0 : mStartRun->mType = eLeadingWS;
1024 0 : mStartRun->mEndNode = mFirstNBSPNode;
1025 0 : mStartRun->mEndOffset = mFirstNBSPOffset;
1026 0 : mStartRun->mLeftType = mStartReason;
1027 0 : mStartRun->mRightType = eNormalWS;
1028 :
1029 : // set up next run
1030 0 : WSFragment *normalRun = new WSFragment();
1031 0 : NS_ENSURE_TRUE(normalRun, NS_ERROR_NULL_POINTER);
1032 0 : mStartRun->mRight = normalRun;
1033 0 : normalRun->mType = eNormalWS;
1034 0 : normalRun->mStartNode = mFirstNBSPNode;
1035 0 : normalRun->mStartOffset = mFirstNBSPOffset;
1036 0 : normalRun->mLeftType = eLeadingWS;
1037 0 : normalRun->mLeft = mStartRun;
1038 0 : if (mEndReason != eBlock)
1039 : {
1040 : // then no trailing ws. this normal run ends the overall ws run.
1041 0 : normalRun->mRightType = mEndReason;
1042 0 : normalRun->mEndNode = mEndNode;
1043 0 : normalRun->mEndOffset = mEndOffset;
1044 0 : mEndRun = normalRun;
1045 : }
1046 : else
1047 : {
1048 : // we might have trailing ws.
1049 : // it so happens that *if* there is an nbsp at end, {mEndNode,mEndOffset-1}
1050 : // will point to it, even though in general start/end points not
1051 : // guaranteed to be in text nodes.
1052 0 : if ((mLastNBSPNode == mEndNode) && (mLastNBSPOffset == (mEndOffset-1)))
1053 : {
1054 : // normal ws runs right up to adjacent block (nbsp next to block)
1055 0 : normalRun->mRightType = mEndReason;
1056 0 : normalRun->mEndNode = mEndNode;
1057 0 : normalRun->mEndOffset = mEndOffset;
1058 0 : mEndRun = normalRun;
1059 : }
1060 : else
1061 : {
1062 0 : normalRun->mEndNode = mLastNBSPNode;
1063 0 : normalRun->mEndOffset = mLastNBSPOffset+1;
1064 0 : normalRun->mRightType = eTrailingWS;
1065 :
1066 : // set up next run
1067 0 : WSFragment *lastRun = new WSFragment();
1068 0 : NS_ENSURE_TRUE(lastRun, NS_ERROR_NULL_POINTER);
1069 0 : lastRun->mType = eTrailingWS;
1070 0 : lastRun->mStartNode = mLastNBSPNode;
1071 0 : lastRun->mStartOffset = mLastNBSPOffset+1;
1072 0 : lastRun->mEndNode = mEndNode;
1073 0 : lastRun->mEndOffset = mEndOffset;
1074 0 : lastRun->mLeftType = eNormalWS;
1075 0 : lastRun->mLeft = normalRun;
1076 0 : lastRun->mRightType = mEndReason;
1077 0 : mEndRun = lastRun;
1078 0 : normalRun->mRight = lastRun;
1079 : }
1080 0 : }
1081 : }
1082 : else // mStartReason is not eBlock or eBreak
1083 : {
1084 : // set up mStartRun
1085 0 : mStartRun->mType = eNormalWS;
1086 0 : mStartRun->mEndNode = mLastNBSPNode;
1087 0 : mStartRun->mEndOffset = mLastNBSPOffset+1;
1088 0 : mStartRun->mLeftType = mStartReason;
1089 :
1090 : // we might have trailing ws.
1091 : // it so happens that *if* there is an nbsp at end, {mEndNode,mEndOffset-1}
1092 : // will point to it, even though in general start/end points not
1093 : // guaranteed to be in text nodes.
1094 0 : if ((mLastNBSPNode == mEndNode) && (mLastNBSPOffset == (mEndOffset-1)))
1095 : {
1096 0 : mStartRun->mRightType = mEndReason;
1097 0 : mStartRun->mEndNode = mEndNode;
1098 0 : mStartRun->mEndOffset = mEndOffset;
1099 0 : mEndRun = mStartRun;
1100 : }
1101 : else
1102 : {
1103 : // set up next run
1104 0 : WSFragment *lastRun = new WSFragment();
1105 0 : NS_ENSURE_TRUE(lastRun, NS_ERROR_NULL_POINTER);
1106 0 : lastRun->mType = eTrailingWS;
1107 0 : lastRun->mStartNode = mLastNBSPNode;
1108 0 : lastRun->mStartOffset = mLastNBSPOffset+1;
1109 0 : lastRun->mLeftType = eNormalWS;
1110 0 : lastRun->mLeft = mStartRun;
1111 0 : lastRun->mRightType = mEndReason;
1112 0 : mEndRun = lastRun;
1113 0 : mStartRun->mRight = lastRun;
1114 0 : mStartRun->mRightType = eTrailingWS;
1115 : }
1116 : }
1117 :
1118 0 : return NS_OK;
1119 : }
1120 :
1121 : void
1122 0 : nsWSRunObject::ClearRuns()
1123 : {
1124 : WSFragment *tmp, *run;
1125 0 : run = mStartRun;
1126 0 : while (run)
1127 : {
1128 0 : tmp = run->mRight;
1129 0 : delete run;
1130 0 : run = tmp;
1131 : }
1132 0 : mStartRun = 0;
1133 0 : mEndRun = 0;
1134 0 : }
1135 :
1136 : nsresult
1137 0 : nsWSRunObject::MakeSingleWSRun(PRInt16 aType)
1138 : {
1139 0 : mStartRun = new WSFragment();
1140 0 : NS_ENSURE_TRUE(mStartRun, NS_ERROR_NULL_POINTER);
1141 :
1142 0 : mStartRun->mStartNode = mStartNode;
1143 0 : mStartRun->mStartOffset = mStartOffset;
1144 0 : mStartRun->mType = aType;
1145 0 : mStartRun->mEndNode = mEndNode;
1146 0 : mStartRun->mEndOffset = mEndOffset;
1147 0 : mStartRun->mLeftType = mStartReason;
1148 0 : mStartRun->mRightType = mEndReason;
1149 :
1150 0 : mEndRun = mStartRun;
1151 :
1152 0 : return NS_OK;
1153 : }
1154 :
1155 : nsresult
1156 0 : nsWSRunObject::PrependNodeToList(nsIDOMNode *aNode)
1157 : {
1158 0 : NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER);
1159 0 : if (!mNodeArray.InsertObjectAt(aNode, 0))
1160 0 : return NS_ERROR_FAILURE;
1161 0 : return NS_OK;
1162 : }
1163 :
1164 : nsresult
1165 0 : nsWSRunObject::AppendNodeToList(nsIDOMNode *aNode)
1166 : {
1167 0 : NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER);
1168 0 : if (!mNodeArray.AppendObject(aNode))
1169 0 : return NS_ERROR_FAILURE;
1170 0 : return NS_OK;
1171 : }
1172 :
1173 : nsresult
1174 0 : nsWSRunObject::GetPreviousWSNode(nsIDOMNode *aStartNode,
1175 : nsIDOMNode *aBlockParent,
1176 : nsCOMPtr<nsIDOMNode> *aPriorNode)
1177 : {
1178 : // can't really recycle various getnext/prior routines because we
1179 : // have special needs here. Need to step into inline containers but
1180 : // not block containers.
1181 0 : NS_ENSURE_TRUE(aStartNode && aBlockParent && aPriorNode, NS_ERROR_NULL_POINTER);
1182 :
1183 0 : nsresult res = aStartNode->GetPreviousSibling(getter_AddRefs(*aPriorNode));
1184 0 : NS_ENSURE_SUCCESS(res, res);
1185 0 : nsCOMPtr<nsIDOMNode> temp, curNode = aStartNode;
1186 0 : while (!*aPriorNode)
1187 : {
1188 : // we have exhausted nodes in parent of aStartNode.
1189 0 : res = curNode->GetParentNode(getter_AddRefs(temp));
1190 0 : NS_ENSURE_SUCCESS(res, res);
1191 0 : NS_ENSURE_TRUE(temp, NS_ERROR_NULL_POINTER);
1192 0 : if (temp == aBlockParent)
1193 : {
1194 : // we have exhausted nodes in the block parent. The convention here is to return null.
1195 0 : *aPriorNode = nsnull;
1196 0 : return NS_OK;
1197 : }
1198 : // we have a parent: look for previous sibling
1199 0 : res = temp->GetPreviousSibling(getter_AddRefs(*aPriorNode));
1200 0 : NS_ENSURE_SUCCESS(res, res);
1201 0 : curNode = temp;
1202 : }
1203 : // we have a prior node. If it's a block, return it.
1204 0 : if (IsBlockNode(*aPriorNode))
1205 0 : return NS_OK;
1206 : // else if it's a container, get deep rightmost child
1207 0 : else if (mHTMLEditor->IsContainer(*aPriorNode))
1208 : {
1209 0 : temp = mHTMLEditor->GetRightmostChild(*aPriorNode);
1210 0 : if (temp)
1211 0 : *aPriorNode = temp;
1212 0 : return NS_OK;
1213 : }
1214 : // else return the node itself
1215 0 : return NS_OK;
1216 : }
1217 :
1218 : nsresult
1219 0 : nsWSRunObject::GetPreviousWSNode(DOMPoint aPoint,
1220 : nsIDOMNode *aBlockParent,
1221 : nsCOMPtr<nsIDOMNode> *aPriorNode)
1222 : {
1223 0 : nsCOMPtr<nsIDOMNode> node;
1224 : PRInt32 offset;
1225 0 : aPoint.GetPoint(node, offset);
1226 0 : return GetPreviousWSNode(node,offset,aBlockParent,aPriorNode);
1227 : }
1228 :
1229 : nsresult
1230 0 : nsWSRunObject::GetPreviousWSNode(nsIDOMNode *aStartNode,
1231 : PRInt16 aOffset,
1232 : nsIDOMNode *aBlockParent,
1233 : nsCOMPtr<nsIDOMNode> *aPriorNode)
1234 : {
1235 : // can't really recycle various getnext/prior routines because we
1236 : // have special needs here. Need to step into inline containers but
1237 : // not block containers.
1238 0 : NS_ENSURE_TRUE(aStartNode && aBlockParent && aPriorNode, NS_ERROR_NULL_POINTER);
1239 0 : *aPriorNode = 0;
1240 :
1241 0 : if (mHTMLEditor->IsTextNode(aStartNode))
1242 0 : return GetPreviousWSNode(aStartNode, aBlockParent, aPriorNode);
1243 0 : if (!mHTMLEditor->IsContainer(aStartNode))
1244 0 : return GetPreviousWSNode(aStartNode, aBlockParent, aPriorNode);
1245 :
1246 0 : if (!aOffset)
1247 : {
1248 0 : if (aStartNode==aBlockParent)
1249 : {
1250 : // we are at start of the block.
1251 0 : return NS_OK;
1252 : }
1253 :
1254 : // we are at start of non-block container
1255 0 : return GetPreviousWSNode(aStartNode, aBlockParent, aPriorNode);
1256 : }
1257 :
1258 0 : nsCOMPtr<nsIContent> startContent( do_QueryInterface(aStartNode) );
1259 0 : NS_ENSURE_STATE(startContent);
1260 0 : nsIContent *priorContent = startContent->GetChildAt(aOffset - 1);
1261 0 : NS_ENSURE_TRUE(priorContent, NS_ERROR_NULL_POINTER);
1262 0 : *aPriorNode = do_QueryInterface(priorContent);
1263 : // we have a prior node. If it's a block, return it.
1264 0 : if (IsBlockNode(*aPriorNode))
1265 0 : return NS_OK;
1266 : // else if it's a container, get deep rightmost child
1267 0 : else if (mHTMLEditor->IsContainer(*aPriorNode))
1268 : {
1269 0 : nsCOMPtr<nsIDOMNode> temp;
1270 0 : temp = mHTMLEditor->GetRightmostChild(*aPriorNode);
1271 0 : if (temp)
1272 0 : *aPriorNode = temp;
1273 0 : return NS_OK;
1274 : }
1275 : // else return the node itself
1276 0 : return NS_OK;
1277 : }
1278 :
1279 : nsresult
1280 0 : nsWSRunObject::GetNextWSNode(nsIDOMNode *aStartNode,
1281 : nsIDOMNode *aBlockParent,
1282 : nsCOMPtr<nsIDOMNode> *aNextNode)
1283 : {
1284 : // can't really recycle various getnext/prior routines because we
1285 : // have special needs here. Need to step into inline containers but
1286 : // not block containers.
1287 0 : NS_ENSURE_TRUE(aStartNode && aBlockParent && aNextNode, NS_ERROR_NULL_POINTER);
1288 :
1289 0 : *aNextNode = 0;
1290 0 : nsresult res = aStartNode->GetNextSibling(getter_AddRefs(*aNextNode));
1291 0 : NS_ENSURE_SUCCESS(res, res);
1292 0 : nsCOMPtr<nsIDOMNode> temp, curNode = aStartNode;
1293 0 : while (!*aNextNode)
1294 : {
1295 : // we have exhausted nodes in parent of aStartNode.
1296 0 : res = curNode->GetParentNode(getter_AddRefs(temp));
1297 0 : NS_ENSURE_SUCCESS(res, res);
1298 0 : NS_ENSURE_TRUE(temp, NS_ERROR_NULL_POINTER);
1299 0 : if (temp == aBlockParent)
1300 : {
1301 : // we have exhausted nodes in the block parent. The convention
1302 : // here is to return null.
1303 0 : *aNextNode = nsnull;
1304 0 : return NS_OK;
1305 : }
1306 : // we have a parent: look for next sibling
1307 0 : res = temp->GetNextSibling(getter_AddRefs(*aNextNode));
1308 0 : NS_ENSURE_SUCCESS(res, res);
1309 0 : curNode = temp;
1310 : }
1311 : // we have a next node. If it's a block, return it.
1312 0 : if (IsBlockNode(*aNextNode))
1313 0 : return NS_OK;
1314 : // else if it's a container, get deep leftmost child
1315 0 : else if (mHTMLEditor->IsContainer(*aNextNode))
1316 : {
1317 0 : temp = mHTMLEditor->GetLeftmostChild(*aNextNode);
1318 0 : if (temp)
1319 0 : *aNextNode = temp;
1320 0 : return NS_OK;
1321 : }
1322 : // else return the node itself
1323 0 : return NS_OK;
1324 : }
1325 :
1326 : nsresult
1327 0 : nsWSRunObject::GetNextWSNode(DOMPoint aPoint,
1328 : nsIDOMNode *aBlockParent,
1329 : nsCOMPtr<nsIDOMNode> *aNextNode)
1330 : {
1331 0 : nsCOMPtr<nsIDOMNode> node;
1332 : PRInt32 offset;
1333 0 : aPoint.GetPoint(node, offset);
1334 0 : return GetNextWSNode(node,offset,aBlockParent,aNextNode);
1335 : }
1336 :
1337 : nsresult
1338 0 : nsWSRunObject::GetNextWSNode(nsIDOMNode *aStartNode,
1339 : PRInt16 aOffset,
1340 : nsIDOMNode *aBlockParent,
1341 : nsCOMPtr<nsIDOMNode> *aNextNode)
1342 : {
1343 : // can't really recycle various getnext/prior routines because we have special needs
1344 : // here. Need to step into inline containers but not block containers.
1345 0 : NS_ENSURE_TRUE(aStartNode && aBlockParent && aNextNode, NS_ERROR_NULL_POINTER);
1346 0 : *aNextNode = 0;
1347 :
1348 0 : if (mHTMLEditor->IsTextNode(aStartNode))
1349 0 : return GetNextWSNode(aStartNode, aBlockParent, aNextNode);
1350 0 : if (!mHTMLEditor->IsContainer(aStartNode))
1351 0 : return GetNextWSNode(aStartNode, aBlockParent, aNextNode);
1352 :
1353 0 : nsCOMPtr<nsIContent> startContent( do_QueryInterface(aStartNode) );
1354 0 : NS_ENSURE_STATE(startContent);
1355 0 : nsIContent *nextContent = startContent->GetChildAt(aOffset);
1356 0 : if (!nextContent)
1357 : {
1358 0 : if (aStartNode==aBlockParent)
1359 : {
1360 : // we are at end of the block.
1361 0 : return NS_OK;
1362 : }
1363 :
1364 : // we are at end of non-block container
1365 0 : return GetNextWSNode(aStartNode, aBlockParent, aNextNode);
1366 : }
1367 :
1368 0 : *aNextNode = do_QueryInterface(nextContent);
1369 : // we have a next node. If it's a block, return it.
1370 0 : if (IsBlockNode(*aNextNode))
1371 0 : return NS_OK;
1372 : // else if it's a container, get deep leftmost child
1373 0 : else if (mHTMLEditor->IsContainer(*aNextNode))
1374 : {
1375 0 : nsCOMPtr<nsIDOMNode> temp;
1376 0 : temp = mHTMLEditor->GetLeftmostChild(*aNextNode);
1377 0 : if (temp)
1378 0 : *aNextNode = temp;
1379 0 : return NS_OK;
1380 : }
1381 : // else return the node itself
1382 0 : return NS_OK;
1383 : }
1384 :
1385 : nsresult
1386 0 : nsWSRunObject::PrepareToDeleteRangePriv(nsWSRunObject* aEndObject)
1387 : {
1388 : // this routine adjust whitespace before *this* and after aEndObject
1389 : // in preperation for the two areas to become adjacent after the
1390 : // intervening content is deleted. It's overly agressive right
1391 : // now. There might be a block boundary remaining between them after
1392 : // the deletion, in which case these adjstments are unneeded (though
1393 : // I don't think they can ever be harmful?)
1394 :
1395 0 : NS_ENSURE_TRUE(aEndObject, NS_ERROR_NULL_POINTER);
1396 0 : nsresult res = NS_OK;
1397 :
1398 : // get the runs before and after selection
1399 : WSFragment *beforeRun, *afterRun;
1400 0 : res = FindRun(mNode, mOffset, &beforeRun, false);
1401 0 : NS_ENSURE_SUCCESS(res, res);
1402 0 : res = aEndObject->FindRun(aEndObject->mNode, aEndObject->mOffset, &afterRun, true);
1403 0 : NS_ENSURE_SUCCESS(res, res);
1404 :
1405 : // trim after run of any leading ws
1406 0 : if (afterRun && (afterRun->mType & eLeadingWS))
1407 : {
1408 : res = aEndObject->DeleteChars(aEndObject->mNode, aEndObject->mOffset, afterRun->mEndNode, afterRun->mEndOffset,
1409 0 : eOutsideUserSelectAll);
1410 0 : NS_ENSURE_SUCCESS(res, res);
1411 : }
1412 : // adjust normal ws in afterRun if needed
1413 0 : if (afterRun && (afterRun->mType == eNormalWS) && !aEndObject->mPRE)
1414 : {
1415 0 : if ( (beforeRun && (beforeRun->mType & eLeadingWS)) ||
1416 0 : (!beforeRun && ((mStartReason & eBlock) || (mStartReason == eBreak))) )
1417 : {
1418 : // make sure leading char of following ws is an nbsp, so that it will show up
1419 0 : WSPoint point;
1420 0 : aEndObject->GetCharAfter(aEndObject->mNode, aEndObject->mOffset, &point);
1421 0 : if (point.mTextNode && nsCRT::IsAsciiSpace(point.mChar))
1422 : {
1423 0 : res = aEndObject->ConvertToNBSP(point, eOutsideUserSelectAll);
1424 0 : NS_ENSURE_SUCCESS(res, res);
1425 : }
1426 : }
1427 : }
1428 : // trim before run of any trailing ws
1429 0 : if (beforeRun && (beforeRun->mType & eTrailingWS))
1430 : {
1431 : res = DeleteChars(beforeRun->mStartNode, beforeRun->mStartOffset, mNode, mOffset,
1432 0 : eOutsideUserSelectAll);
1433 0 : NS_ENSURE_SUCCESS(res, res);
1434 : }
1435 0 : else if (beforeRun && (beforeRun->mType == eNormalWS) && !mPRE)
1436 : {
1437 0 : if ( (afterRun && (afterRun->mType & eTrailingWS)) ||
1438 : (afterRun && (afterRun->mType == eNormalWS)) ||
1439 0 : (!afterRun && ((aEndObject->mEndReason & eBlock))) )
1440 : {
1441 : // make sure trailing char of starting ws is an nbsp, so that it will show up
1442 0 : WSPoint point;
1443 0 : GetCharBefore(mNode, mOffset, &point);
1444 0 : if (point.mTextNode && nsCRT::IsAsciiSpace(point.mChar))
1445 : {
1446 0 : nsCOMPtr<nsIDOMNode> wsStartNode, wsEndNode;
1447 : PRInt32 wsStartOffset, wsEndOffset;
1448 : res = GetAsciiWSBounds(eBoth, mNode, mOffset,
1449 : address_of(wsStartNode), &wsStartOffset,
1450 0 : address_of(wsEndNode), &wsEndOffset);
1451 0 : NS_ENSURE_SUCCESS(res, res);
1452 0 : point.mTextNode = do_QueryInterface(wsStartNode);
1453 0 : if (!point.mTextNode->IsNodeOfType(nsINode::eDATA_NODE)) {
1454 : // Not sure if this is needed, but it'll maintain the same
1455 : // functionality
1456 0 : point.mTextNode = nsnull;
1457 : }
1458 0 : point.mOffset = wsStartOffset;
1459 0 : res = ConvertToNBSP(point, eOutsideUserSelectAll);
1460 0 : NS_ENSURE_SUCCESS(res, res);
1461 : }
1462 : }
1463 : }
1464 0 : return res;
1465 : }
1466 :
1467 : nsresult
1468 0 : nsWSRunObject::PrepareToSplitAcrossBlocksPriv()
1469 : {
1470 : // used to prepare ws to be split across two blocks. The main issue
1471 : // here is make sure normalWS doesn't end up becoming non-significant
1472 : // leading or trailing ws after the split.
1473 0 : nsresult res = NS_OK;
1474 :
1475 : // get the runs before and after selection
1476 : WSFragment *beforeRun, *afterRun;
1477 0 : res = FindRun(mNode, mOffset, &beforeRun, false);
1478 0 : NS_ENSURE_SUCCESS(res, res);
1479 0 : res = FindRun(mNode, mOffset, &afterRun, true);
1480 :
1481 : // adjust normal ws in afterRun if needed
1482 0 : if (afterRun && (afterRun->mType == eNormalWS))
1483 : {
1484 : // make sure leading char of following ws is an nbsp, so that it will show up
1485 0 : WSPoint point;
1486 0 : GetCharAfter(mNode, mOffset, &point);
1487 0 : if (point.mTextNode && nsCRT::IsAsciiSpace(point.mChar))
1488 : {
1489 0 : res = ConvertToNBSP(point);
1490 0 : NS_ENSURE_SUCCESS(res, res);
1491 : }
1492 : }
1493 :
1494 : // adjust normal ws in beforeRun if needed
1495 0 : if (beforeRun && (beforeRun->mType == eNormalWS))
1496 : {
1497 : // make sure trailing char of starting ws is an nbsp, so that it will show up
1498 0 : WSPoint point;
1499 0 : GetCharBefore(mNode, mOffset, &point);
1500 0 : if (point.mTextNode && nsCRT::IsAsciiSpace(point.mChar))
1501 : {
1502 0 : nsCOMPtr<nsIDOMNode> wsStartNode, wsEndNode;
1503 : PRInt32 wsStartOffset, wsEndOffset;
1504 : res = GetAsciiWSBounds(eBoth, mNode, mOffset,
1505 : address_of(wsStartNode), &wsStartOffset,
1506 0 : address_of(wsEndNode), &wsEndOffset);
1507 0 : NS_ENSURE_SUCCESS(res, res);
1508 0 : point.mTextNode = do_QueryInterface(wsStartNode);
1509 0 : if (!point.mTextNode->IsNodeOfType(nsINode::eDATA_NODE)) {
1510 : // Not sure if this is needed, but it'll maintain the same
1511 : // functionality
1512 0 : point.mTextNode = nsnull;
1513 : }
1514 0 : point.mOffset = wsStartOffset;
1515 0 : res = ConvertToNBSP(point);
1516 0 : NS_ENSURE_SUCCESS(res, res);
1517 : }
1518 : }
1519 0 : return res;
1520 : }
1521 :
1522 : nsresult
1523 0 : nsWSRunObject::DeleteChars(nsIDOMNode *aStartNode, PRInt32 aStartOffset,
1524 : nsIDOMNode *aEndNode, PRInt32 aEndOffset,
1525 : AreaRestriction aAR)
1526 : {
1527 : // MOOSE: this routine needs to be modified to preserve the integrity of the
1528 : // wsFragment info.
1529 0 : NS_ENSURE_TRUE(aStartNode && aEndNode, NS_ERROR_NULL_POINTER);
1530 :
1531 0 : if (aAR == eOutsideUserSelectAll)
1532 : {
1533 0 : nsCOMPtr<nsIDOMNode> san = mHTMLEditor->FindUserSelectAllNode(aStartNode);
1534 0 : if (san)
1535 0 : return NS_OK;
1536 :
1537 0 : if (aStartNode != aEndNode)
1538 : {
1539 0 : san = mHTMLEditor->FindUserSelectAllNode(aEndNode);
1540 0 : if (san)
1541 0 : return NS_OK;
1542 : }
1543 : }
1544 :
1545 0 : if ((aStartNode == aEndNode) && (aStartOffset == aEndOffset))
1546 0 : return NS_OK; // nothing to delete
1547 :
1548 0 : nsresult res = NS_OK;
1549 0 : PRInt32 idx = mNodeArray.IndexOf(aStartNode);
1550 0 : if (idx==-1) idx = 0; // if our strarting point wasn't one of our ws text nodes,
1551 : // then just go through them from the beginning.
1552 0 : nsCOMPtr<nsIDOMNode> node;
1553 0 : nsCOMPtr<nsIDOMCharacterData> textnode;
1554 0 : nsRefPtr<nsRange> range;
1555 :
1556 0 : if (aStartNode == aEndNode)
1557 : {
1558 0 : textnode = do_QueryInterface(aStartNode);
1559 0 : if (textnode)
1560 : {
1561 : return mHTMLEditor->DeleteText(textnode, (PRUint32)aStartOffset,
1562 0 : (PRUint32)(aEndOffset-aStartOffset));
1563 : }
1564 : }
1565 :
1566 0 : PRInt32 count = mNodeArray.Count();
1567 0 : while (idx < count)
1568 : {
1569 0 : node = mNodeArray[idx];
1570 0 : if (!node)
1571 0 : break; // we ran out of ws nodes; must have been deleting to end
1572 0 : if (node == aStartNode)
1573 : {
1574 0 : textnode = do_QueryInterface(node);
1575 : PRUint32 len;
1576 0 : textnode->GetLength(&len);
1577 0 : if (PRUint32(aStartOffset)<len)
1578 : {
1579 0 : res = mHTMLEditor->DeleteText(textnode, (PRUint32)aStartOffset, len-aStartOffset);
1580 0 : NS_ENSURE_SUCCESS(res, res);
1581 : }
1582 : }
1583 0 : else if (node == aEndNode)
1584 : {
1585 0 : if (aEndOffset)
1586 : {
1587 0 : textnode = do_QueryInterface(node);
1588 0 : res = mHTMLEditor->DeleteText(textnode, 0, (PRUint32)aEndOffset);
1589 0 : NS_ENSURE_SUCCESS(res, res);
1590 : }
1591 0 : break;
1592 : }
1593 : else
1594 : {
1595 0 : if (!range)
1596 : {
1597 0 : range = new nsRange();
1598 0 : res = range->SetStart(aStartNode, aStartOffset);
1599 0 : NS_ENSURE_SUCCESS(res, res);
1600 0 : res = range->SetEnd(aEndNode, aEndOffset);
1601 0 : NS_ENSURE_SUCCESS(res, res);
1602 : }
1603 : bool nodeBefore, nodeAfter;
1604 0 : nsCOMPtr<nsIContent> content (do_QueryInterface(node));
1605 0 : res = nsRange::CompareNodeToRange(content, range, &nodeBefore, &nodeAfter);
1606 0 : NS_ENSURE_SUCCESS(res, res);
1607 0 : if (nodeAfter)
1608 : {
1609 : break;
1610 : }
1611 0 : if (!nodeBefore)
1612 : {
1613 0 : res = mHTMLEditor->DeleteNode(node);
1614 0 : NS_ENSURE_SUCCESS(res, res);
1615 0 : mNodeArray.RemoveObject(node);
1616 0 : --count;
1617 0 : --idx;
1618 : }
1619 : }
1620 0 : idx++;
1621 : }
1622 0 : return res;
1623 : }
1624 :
1625 : nsresult
1626 0 : nsWSRunObject::GetCharAfter(nsIDOMNode *aNode, PRInt32 aOffset, WSPoint *outPoint)
1627 : {
1628 0 : NS_ENSURE_TRUE(aNode && outPoint, NS_ERROR_NULL_POINTER);
1629 :
1630 0 : PRInt32 idx = mNodeArray.IndexOf(aNode);
1631 0 : if (idx == -1)
1632 : {
1633 : // use range comparisons to get right ws node
1634 0 : return GetWSPointAfter(aNode, aOffset, outPoint);
1635 : }
1636 : else
1637 : {
1638 : // use wspoint version of GetCharAfter()
1639 0 : WSPoint point(aNode,aOffset,0);
1640 0 : return GetCharAfter(point, outPoint);
1641 : }
1642 :
1643 : return NS_ERROR_FAILURE;
1644 : }
1645 :
1646 : nsresult
1647 0 : nsWSRunObject::GetCharBefore(nsIDOMNode *aNode, PRInt32 aOffset, WSPoint *outPoint)
1648 : {
1649 0 : NS_ENSURE_TRUE(aNode && outPoint, NS_ERROR_NULL_POINTER);
1650 :
1651 0 : PRInt32 idx = mNodeArray.IndexOf(aNode);
1652 0 : if (idx == -1)
1653 : {
1654 : // use range comparisons to get right ws node
1655 0 : return GetWSPointBefore(aNode, aOffset, outPoint);
1656 : }
1657 : else
1658 : {
1659 : // use wspoint version of GetCharBefore()
1660 0 : WSPoint point(aNode,aOffset,0);
1661 0 : return GetCharBefore(point, outPoint);
1662 : }
1663 :
1664 : return NS_ERROR_FAILURE;
1665 : }
1666 :
1667 : nsresult
1668 0 : nsWSRunObject::GetCharAfter(WSPoint &aPoint, WSPoint *outPoint)
1669 : {
1670 0 : NS_ENSURE_TRUE(aPoint.mTextNode && outPoint, NS_ERROR_NULL_POINTER);
1671 :
1672 0 : outPoint->mTextNode = nsnull;
1673 0 : outPoint->mOffset = 0;
1674 0 : outPoint->mChar = 0;
1675 :
1676 0 : nsCOMPtr<nsIDOMNode> pointTextNode(do_QueryInterface(aPoint.mTextNode));
1677 0 : PRInt32 idx = mNodeArray.IndexOf(pointTextNode);
1678 0 : if (idx == -1) return NS_OK; // can't find point, but it's not an error
1679 0 : PRInt32 numNodes = mNodeArray.Count();
1680 :
1681 0 : if (PRUint16(aPoint.mOffset) < aPoint.mTextNode->TextLength())
1682 : {
1683 0 : *outPoint = aPoint;
1684 0 : outPoint->mChar = GetCharAt(aPoint.mTextNode, aPoint.mOffset);
1685 : }
1686 0 : else if (idx < (PRInt32)(numNodes-1))
1687 : {
1688 0 : nsIDOMNode* node = mNodeArray[idx+1];
1689 0 : NS_ENSURE_TRUE(node, NS_ERROR_FAILURE);
1690 0 : outPoint->mTextNode = do_QueryInterface(node);
1691 0 : if (!outPoint->mTextNode->IsNodeOfType(nsINode::eDATA_NODE)) {
1692 : // Not sure if this is needed, but it'll maintain the same
1693 : // functionality
1694 0 : outPoint->mTextNode = nsnull;
1695 : }
1696 0 : outPoint->mOffset = 0;
1697 0 : outPoint->mChar = GetCharAt(outPoint->mTextNode, 0);
1698 : }
1699 0 : return NS_OK;
1700 : }
1701 :
1702 : nsresult
1703 0 : nsWSRunObject::GetCharBefore(WSPoint &aPoint, WSPoint *outPoint)
1704 : {
1705 0 : NS_ENSURE_TRUE(aPoint.mTextNode && outPoint, NS_ERROR_NULL_POINTER);
1706 :
1707 0 : outPoint->mTextNode = nsnull;
1708 0 : outPoint->mOffset = 0;
1709 0 : outPoint->mChar = 0;
1710 :
1711 0 : nsCOMPtr<nsIDOMNode> pointTextNode(do_QueryInterface(aPoint.mTextNode));
1712 0 : PRInt32 idx = mNodeArray.IndexOf(pointTextNode);
1713 0 : if (idx == -1) return NS_OK; // can't find point, but it's not an error
1714 :
1715 0 : if (aPoint.mOffset != 0)
1716 : {
1717 0 : *outPoint = aPoint;
1718 0 : outPoint->mOffset--;
1719 0 : outPoint->mChar = GetCharAt(aPoint.mTextNode, aPoint.mOffset-1);
1720 : }
1721 0 : else if (idx)
1722 : {
1723 0 : nsIDOMNode* node = mNodeArray[idx-1];
1724 0 : NS_ENSURE_TRUE(node, NS_ERROR_FAILURE);
1725 0 : outPoint->mTextNode = do_QueryInterface(node);
1726 :
1727 0 : PRUint32 len = outPoint->mTextNode->TextLength();
1728 :
1729 0 : if (len)
1730 : {
1731 0 : outPoint->mOffset = len-1;
1732 0 : outPoint->mChar = GetCharAt(outPoint->mTextNode, len-1);
1733 : }
1734 : }
1735 0 : return NS_OK;
1736 : }
1737 :
1738 : nsresult
1739 0 : nsWSRunObject::ConvertToNBSP(WSPoint aPoint, AreaRestriction aAR)
1740 : {
1741 : // MOOSE: this routine needs to be modified to preserve the integrity of the
1742 : // wsFragment info.
1743 0 : NS_ENSURE_TRUE(aPoint.mTextNode, NS_ERROR_NULL_POINTER);
1744 :
1745 0 : if (aAR == eOutsideUserSelectAll)
1746 : {
1747 0 : nsCOMPtr<nsIDOMNode> domnode = do_QueryInterface(aPoint.mTextNode);
1748 0 : if (domnode)
1749 : {
1750 0 : nsCOMPtr<nsIDOMNode> san = mHTMLEditor->FindUserSelectAllNode(domnode);
1751 0 : if (san)
1752 0 : return NS_OK;
1753 : }
1754 : }
1755 :
1756 0 : nsCOMPtr<nsIDOMCharacterData> textNode(do_QueryInterface(aPoint.mTextNode));
1757 0 : NS_ENSURE_TRUE(textNode, NS_ERROR_NULL_POINTER);
1758 0 : nsCOMPtr<nsIDOMNode> node(do_QueryInterface(textNode));
1759 :
1760 : // first, insert an nbsp
1761 0 : nsAutoTxnsConserveSelection dontSpazMySelection(mHTMLEditor);
1762 0 : nsAutoString nbspStr(nbsp);
1763 0 : nsresult res = mHTMLEditor->InsertTextIntoTextNodeImpl(nbspStr, textNode, aPoint.mOffset, true);
1764 0 : NS_ENSURE_SUCCESS(res, res);
1765 :
1766 : // next, find range of ws it will replace
1767 0 : nsCOMPtr<nsIDOMNode> startNode, endNode;
1768 0 : PRInt32 startOffset=0, endOffset=0;
1769 :
1770 : res = GetAsciiWSBounds(eAfter, node, aPoint.mOffset+1, address_of(startNode),
1771 0 : &startOffset, address_of(endNode), &endOffset);
1772 0 : NS_ENSURE_SUCCESS(res, res);
1773 :
1774 : // finally, delete that replaced ws, if any
1775 0 : if (startNode)
1776 : {
1777 0 : res = DeleteChars(startNode, startOffset, endNode, endOffset);
1778 : }
1779 :
1780 0 : return res;
1781 : }
1782 :
1783 : nsresult
1784 0 : nsWSRunObject::GetAsciiWSBounds(PRInt16 aDir, nsIDOMNode *aNode, PRInt32 aOffset,
1785 : nsCOMPtr<nsIDOMNode> *outStartNode, PRInt32 *outStartOffset,
1786 : nsCOMPtr<nsIDOMNode> *outEndNode, PRInt32 *outEndOffset)
1787 : {
1788 0 : NS_ENSURE_TRUE(aNode && outStartNode && outEndNode, NS_ERROR_NULL_POINTER);
1789 :
1790 0 : nsCOMPtr<nsIDOMNode> startNode, endNode;
1791 0 : PRInt32 startOffset=0, endOffset=0;
1792 :
1793 0 : nsresult res = NS_OK;
1794 :
1795 0 : if (aDir & eAfter)
1796 : {
1797 0 : WSPoint point, tmp;
1798 0 : res = GetCharAfter(aNode, aOffset, &point);
1799 0 : if (NS_SUCCEEDED(res) && point.mTextNode)
1800 : { // we found a text node, at least
1801 0 : endNode = do_QueryInterface(point.mTextNode);
1802 0 : endOffset = point.mOffset;
1803 0 : startNode = endNode;
1804 0 : startOffset = endOffset;
1805 :
1806 : // scan ahead to end of ascii ws
1807 0 : while (nsCRT::IsAsciiSpace(point.mChar))
1808 : {
1809 0 : endNode = do_QueryInterface(point.mTextNode);
1810 0 : point.mOffset++; // endOffset is _after_ ws
1811 0 : endOffset = point.mOffset;
1812 0 : tmp = point;
1813 0 : res = GetCharAfter(tmp, &point);
1814 0 : if (NS_FAILED(res) || !point.mTextNode) break;
1815 : }
1816 : }
1817 : }
1818 :
1819 0 : if (aDir & eBefore)
1820 : {
1821 0 : WSPoint point, tmp;
1822 0 : res = GetCharBefore(aNode, aOffset, &point);
1823 0 : if (NS_SUCCEEDED(res) && point.mTextNode)
1824 : { // we found a text node, at least
1825 0 : startNode = do_QueryInterface(point.mTextNode);
1826 0 : startOffset = point.mOffset+1;
1827 0 : if (!endNode)
1828 : {
1829 0 : endNode = startNode;
1830 0 : endOffset = startOffset;
1831 : }
1832 :
1833 : // scan back to start of ascii ws
1834 0 : while (nsCRT::IsAsciiSpace(point.mChar))
1835 : {
1836 0 : startNode = do_QueryInterface(point.mTextNode);
1837 0 : startOffset = point.mOffset;
1838 0 : tmp = point;
1839 0 : res = GetCharBefore(tmp, &point);
1840 0 : if (NS_FAILED(res) || !point.mTextNode) break;
1841 : }
1842 : }
1843 : }
1844 :
1845 0 : *outStartNode = startNode;
1846 0 : *outStartOffset = startOffset;
1847 0 : *outEndNode = endNode;
1848 0 : *outEndOffset = endOffset;
1849 :
1850 0 : return NS_OK;
1851 : }
1852 :
1853 : nsresult
1854 0 : nsWSRunObject::FindRun(nsIDOMNode *aNode, PRInt32 aOffset, WSFragment **outRun, bool after)
1855 : {
1856 0 : *outRun = nsnull;
1857 : // given a dompoint, find the ws run that is before or after it, as caller needs
1858 0 : NS_ENSURE_TRUE(aNode && outRun, NS_ERROR_NULL_POINTER);
1859 :
1860 0 : nsresult res = NS_OK;
1861 0 : WSFragment *run = mStartRun;
1862 0 : while (run)
1863 : {
1864 : PRInt16 comp = nsContentUtils::ComparePoints(aNode, aOffset, run->mStartNode,
1865 0 : run->mStartOffset);
1866 0 : if (comp <= 0)
1867 : {
1868 0 : if (after)
1869 : {
1870 0 : *outRun = run;
1871 0 : return res;
1872 : }
1873 : else // before
1874 : {
1875 0 : *outRun = nsnull;
1876 0 : return res;
1877 : }
1878 : }
1879 : comp = nsContentUtils::ComparePoints(aNode, aOffset,
1880 0 : run->mEndNode, run->mEndOffset);
1881 0 : if (comp < 0)
1882 : {
1883 0 : *outRun = run;
1884 0 : return res;
1885 : }
1886 0 : else if (comp == 0)
1887 : {
1888 0 : if (after)
1889 : {
1890 0 : *outRun = run->mRight;
1891 0 : return res;
1892 : }
1893 : else // before
1894 : {
1895 0 : *outRun = run;
1896 0 : return res;
1897 : }
1898 : }
1899 0 : if (!run->mRight)
1900 : {
1901 0 : if (after)
1902 : {
1903 0 : *outRun = nsnull;
1904 0 : return res;
1905 : }
1906 : else // before
1907 : {
1908 0 : *outRun = run;
1909 0 : return res;
1910 : }
1911 : }
1912 0 : run = run->mRight;
1913 : }
1914 0 : return res;
1915 : }
1916 :
1917 : PRUnichar
1918 0 : nsWSRunObject::GetCharAt(nsIContent *aTextNode, PRInt32 aOffset)
1919 : {
1920 : // return 0 if we can't get a char, for whatever reason
1921 0 : NS_ENSURE_TRUE(aTextNode, 0);
1922 :
1923 0 : PRInt32 len = PRInt32(aTextNode->TextLength());
1924 0 : if (aOffset < 0 || aOffset >= len)
1925 0 : return 0;
1926 :
1927 0 : return aTextNode->GetText()->CharAt(aOffset);
1928 : }
1929 :
1930 : nsresult
1931 0 : nsWSRunObject::GetWSPointAfter(nsIDOMNode *aNode, PRInt32 aOffset, WSPoint *outPoint)
1932 : {
1933 : // Note: only to be called if aNode is not a ws node.
1934 :
1935 : // binary search on wsnodes
1936 : PRInt32 numNodes, firstNum, curNum, lastNum;
1937 0 : numNodes = mNodeArray.Count();
1938 :
1939 0 : NS_ENSURE_TRUE(numNodes, NS_OK); // do nothing if there are no nodes to search
1940 :
1941 0 : firstNum = 0;
1942 0 : curNum = numNodes/2;
1943 0 : lastNum = numNodes;
1944 0 : PRInt16 cmp=0;
1945 0 : nsCOMPtr<nsIDOMNode> curNode;
1946 :
1947 : // begin binary search
1948 : // we do this because we need to minimize calls to ComparePoints(),
1949 : // which is mongo expensive
1950 0 : while (curNum != lastNum)
1951 : {
1952 0 : curNode = mNodeArray[curNum];
1953 0 : cmp = nsContentUtils::ComparePoints(aNode, aOffset, curNode, 0);
1954 0 : if (cmp < 0)
1955 0 : lastNum = curNum;
1956 : else
1957 0 : firstNum = curNum + 1;
1958 0 : curNum = (lastNum - firstNum) / 2 + firstNum;
1959 0 : NS_ASSERTION(firstNum <= curNum && curNum <= lastNum, "Bad binary search");
1960 : }
1961 :
1962 : // When the binary search is complete, we always know that the current node
1963 : // is the same as the end node, which is always past our range. Therefore,
1964 : // we've found the node immediately after the point of interest.
1965 0 : if (curNum == mNodeArray.Count()) {
1966 : // they asked for past our range (it's after the last node). GetCharAfter
1967 : // will do the work for us when we pass it the last index of the last node.
1968 0 : nsCOMPtr<nsIContent> textNode(do_QueryInterface(mNodeArray[curNum-1]));
1969 0 : WSPoint point(textNode, textNode->TextLength(), 0);
1970 0 : return GetCharAfter(point, outPoint);
1971 : } else {
1972 : // The char after the point of interest is the first character of our range.
1973 0 : nsCOMPtr<nsIContent> textNode(do_QueryInterface(mNodeArray[curNum]));
1974 0 : WSPoint point(textNode, 0, 0);
1975 0 : return GetCharAfter(point, outPoint);
1976 : }
1977 : }
1978 :
1979 : nsresult
1980 0 : nsWSRunObject::GetWSPointBefore(nsIDOMNode *aNode, PRInt32 aOffset, WSPoint *outPoint)
1981 : {
1982 : // Note: only to be called if aNode is not a ws node.
1983 :
1984 : // binary search on wsnodes
1985 : PRInt32 numNodes, firstNum, curNum, lastNum;
1986 0 : numNodes = mNodeArray.Count();
1987 :
1988 0 : NS_ENSURE_TRUE(numNodes, NS_OK); // do nothing if there are no nodes to search
1989 :
1990 0 : firstNum = 0;
1991 0 : curNum = numNodes/2;
1992 0 : lastNum = numNodes;
1993 0 : PRInt16 cmp=0;
1994 0 : nsCOMPtr<nsIDOMNode> curNode;
1995 :
1996 : // begin binary search
1997 : // we do this because we need to minimize calls to ComparePoints(),
1998 : // which is mongo expensive
1999 0 : while (curNum != lastNum)
2000 : {
2001 0 : curNode = mNodeArray[curNum];
2002 0 : cmp = nsContentUtils::ComparePoints(aNode, aOffset, curNode, 0);
2003 0 : if (cmp < 0)
2004 0 : lastNum = curNum;
2005 : else
2006 0 : firstNum = curNum + 1;
2007 0 : curNum = (lastNum - firstNum) / 2 + firstNum;
2008 0 : NS_ASSERTION(firstNum <= curNum && curNum <= lastNum, "Bad binary search");
2009 : }
2010 :
2011 : // When the binary search is complete, we always know that the current node
2012 : // is the same as the end node, which is always past our range. Therefore,
2013 : // we've found the node immediately after the point of interest.
2014 0 : if (curNum == mNodeArray.Count()) {
2015 : // get the point before the end of the last node, we can pass the length
2016 : // of the node into GetCharBefore, and it will return the last character.
2017 0 : nsCOMPtr<nsIContent> textNode(do_QueryInterface(mNodeArray[curNum - 1]));
2018 0 : WSPoint point(textNode, textNode->TextLength(), 0);
2019 0 : return GetCharBefore(point, outPoint);
2020 : } else {
2021 : // we can just ask the current node for the point immediately before it,
2022 : // it will handle moving to the previous node (if any) and returning the
2023 : // appropriate character
2024 0 : nsCOMPtr<nsIContent> textNode(do_QueryInterface(mNodeArray[curNum]));
2025 0 : WSPoint point(textNode, 0, 0);
2026 0 : return GetCharBefore(point, outPoint);
2027 : }
2028 : }
2029 :
2030 : nsresult
2031 0 : nsWSRunObject::CheckTrailingNBSPOfRun(WSFragment *aRun)
2032 : {
2033 : // try to change an nbsp to a space, if possible, just to prevent nbsp proliferation.
2034 : // examine what is before and after the trailing nbsp, if any.
2035 0 : NS_ENSURE_TRUE(aRun, NS_ERROR_NULL_POINTER);
2036 0 : WSPoint thePoint;
2037 0 : bool leftCheck = false;
2038 0 : bool spaceNBSP = false;
2039 0 : bool rightCheck = false;
2040 :
2041 : // confirm run is normalWS
2042 0 : if (aRun->mType != eNormalWS) return NS_ERROR_FAILURE;
2043 :
2044 : // first check for trailing nbsp
2045 0 : nsresult res = GetCharBefore(aRun->mEndNode, aRun->mEndOffset, &thePoint);
2046 0 : if (NS_SUCCEEDED(res) && thePoint.mTextNode && thePoint.mChar == nbsp)
2047 : {
2048 : // now check that what is to the left of it is compatible with replacing nbsp with space
2049 0 : WSPoint prevPoint;
2050 0 : res = GetCharBefore(thePoint, &prevPoint);
2051 0 : if (NS_SUCCEEDED(res) && prevPoint.mTextNode)
2052 : {
2053 0 : if (!nsCRT::IsAsciiSpace(prevPoint.mChar)) leftCheck = true;
2054 0 : else spaceNBSP = true;
2055 : }
2056 0 : else if (aRun->mLeftType == eText) leftCheck = true;
2057 0 : else if (aRun->mLeftType == eSpecial) leftCheck = true;
2058 0 : if (leftCheck || spaceNBSP)
2059 : {
2060 : // now check that what is to the right of it is compatible with replacing nbsp with space
2061 0 : if (aRun->mRightType == eText) rightCheck = true;
2062 0 : if (aRun->mRightType == eSpecial) rightCheck = true;
2063 0 : if (aRun->mRightType == eBreak) rightCheck = true;
2064 0 : if ((aRun->mRightType & eBlock) &&
2065 0 : IsBlockNode(nsCOMPtr<nsIDOMNode>(GetWSBoundingParent())))
2066 : {
2067 : // we are at a block boundary. Insert a <br>. Why? Well, first note that
2068 : // the br will have no visible effect since it is up against a block boundary.
2069 : // |foo<br><p>bar| renders like |foo<p>bar| and similarly
2070 : // |<p>foo<br></p>bar| renders like |<p>foo</p>bar|. What this <br> addition
2071 : // gets us is the ability to convert a trailing nbsp to a space. Consider:
2072 : // |<body>foo. '</body>|, where ' represents selection. User types space attempting
2073 : // to put 2 spaces after the end of their sentence. We used to do this as:
2074 : // |<body>foo.  </body>| This caused problems with soft wrapping: the nbsp
2075 : // would wrap to the next line, which looked attrocious. If you try to do:
2076 : // |<body>foo.  </body>| instead, the trailing space is invisible because it
2077 : // is against a block boundary. If you do: |<body>foo.  </body>| then
2078 : // you get an even uglier soft wrapping problem, where foo is on one line until
2079 : // you type the final space, and then "foo " jumps down to the next line. Ugh.
2080 : // The best way I can find out of this is to throw in a harmless <br>
2081 : // here, which allows us to do: |<body>foo.  <br></body>|, which doesn't
2082 : // cause foo to jump lines, doesn't cause spaces to show up at the beginning of
2083 : // soft wrapped lines, and lets the user see 2 spaces when they type 2 spaces.
2084 :
2085 0 : nsCOMPtr<nsIDOMNode> brNode;
2086 0 : res = mHTMLEditor->CreateBR(aRun->mEndNode, aRun->mEndOffset, address_of(brNode));
2087 0 : NS_ENSURE_SUCCESS(res, res);
2088 :
2089 : // refresh thePoint, prevPoint
2090 0 : res = GetCharBefore(aRun->mEndNode, aRun->mEndOffset, &thePoint);
2091 0 : NS_ENSURE_SUCCESS(res, res);
2092 0 : res = GetCharBefore(thePoint, &prevPoint);
2093 0 : NS_ENSURE_SUCCESS(res, res);
2094 0 : rightCheck = true;
2095 : }
2096 : }
2097 0 : if (leftCheck && rightCheck)
2098 : {
2099 : // now replace nbsp with space
2100 : // first, insert a space
2101 0 : nsCOMPtr<nsIDOMCharacterData> textNode(do_QueryInterface(thePoint.mTextNode));
2102 0 : NS_ENSURE_TRUE(textNode, NS_ERROR_NULL_POINTER);
2103 0 : nsAutoTxnsConserveSelection dontSpazMySelection(mHTMLEditor);
2104 0 : nsAutoString spaceStr(PRUnichar(32));
2105 0 : res = mHTMLEditor->InsertTextIntoTextNodeImpl(spaceStr, textNode, thePoint.mOffset, true);
2106 0 : NS_ENSURE_SUCCESS(res, res);
2107 :
2108 : // finally, delete that nbsp
2109 0 : nsCOMPtr<nsIDOMNode> delNode(do_QueryInterface(thePoint.mTextNode));
2110 0 : res = DeleteChars(delNode, thePoint.mOffset+1, delNode, thePoint.mOffset+2);
2111 0 : NS_ENSURE_SUCCESS(res, res);
2112 : }
2113 0 : else if (!mPRE && spaceNBSP && rightCheck) // don't mess with this preformatted for now.
2114 : {
2115 : // we have a run of ascii whitespace (which will render as one space)
2116 : // followed by an nbsp (which is at the end of the whitespace run). Let's
2117 : // switch their order. This will insure that if someone types two spaces
2118 : // after a sentence, and the editor softwraps at this point, the spaces wont
2119 : // be split across lines, which looks ugly and is bad for the moose.
2120 :
2121 0 : nsCOMPtr<nsIDOMNode> startNode, endNode, thenode(do_QueryInterface(prevPoint.mTextNode));
2122 : PRInt32 startOffset, endOffset;
2123 : res = GetAsciiWSBounds(eBoth, thenode, prevPoint.mOffset+1, address_of(startNode),
2124 0 : &startOffset, address_of(endNode), &endOffset);
2125 0 : NS_ENSURE_SUCCESS(res, res);
2126 :
2127 : // delete that nbsp
2128 0 : nsCOMPtr<nsIDOMNode> delNode(do_QueryInterface(thePoint.mTextNode));
2129 0 : res = DeleteChars(delNode, thePoint.mOffset, delNode, thePoint.mOffset+1);
2130 0 : NS_ENSURE_SUCCESS(res, res);
2131 :
2132 : // finally, insert that nbsp before the ascii ws run
2133 0 : nsAutoTxnsConserveSelection dontSpazMySelection(mHTMLEditor);
2134 0 : nsAutoString nbspStr(nbsp);
2135 0 : nsCOMPtr<nsIDOMCharacterData> textNode(do_QueryInterface(startNode));
2136 0 : res = mHTMLEditor->InsertTextIntoTextNodeImpl(nbspStr, textNode, startOffset, true);
2137 0 : NS_ENSURE_SUCCESS(res, res);
2138 : }
2139 : }
2140 0 : return NS_OK;
2141 : }
2142 :
2143 : nsresult
2144 0 : nsWSRunObject::CheckTrailingNBSP(WSFragment *aRun, nsIDOMNode *aNode, PRInt32 aOffset)
2145 : {
2146 : // try to change an nbsp to a space, if possible, just to prevent nbsp proliferation.
2147 : // this routine is called when we about to make this point in the ws abut an inserted break
2148 : // or text, so we don't have to worry about what is after it. What is after it now will
2149 : // end up after the inserted object.
2150 0 : NS_ENSURE_TRUE(aRun && aNode, NS_ERROR_NULL_POINTER);
2151 0 : WSPoint thePoint;
2152 0 : bool canConvert = false;
2153 0 : nsresult res = GetCharBefore(aNode, aOffset, &thePoint);
2154 0 : if (NS_SUCCEEDED(res) && thePoint.mTextNode && thePoint.mChar == nbsp)
2155 : {
2156 0 : WSPoint prevPoint;
2157 0 : res = GetCharBefore(thePoint, &prevPoint);
2158 0 : if (NS_SUCCEEDED(res) && prevPoint.mTextNode)
2159 : {
2160 0 : if (!nsCRT::IsAsciiSpace(prevPoint.mChar)) canConvert = true;
2161 : }
2162 0 : else if (aRun->mLeftType == eText) canConvert = true;
2163 0 : else if (aRun->mLeftType == eSpecial) canConvert = true;
2164 : }
2165 0 : if (canConvert)
2166 : {
2167 : // first, insert a space
2168 0 : nsCOMPtr<nsIDOMCharacterData> textNode(do_QueryInterface(thePoint.mTextNode));
2169 0 : NS_ENSURE_TRUE(textNode, NS_ERROR_NULL_POINTER);
2170 0 : nsAutoTxnsConserveSelection dontSpazMySelection(mHTMLEditor);
2171 0 : nsAutoString spaceStr(PRUnichar(32));
2172 0 : res = mHTMLEditor->InsertTextIntoTextNodeImpl(spaceStr, textNode, thePoint.mOffset, true);
2173 0 : NS_ENSURE_SUCCESS(res, res);
2174 :
2175 : // finally, delete that nbsp
2176 0 : nsCOMPtr<nsIDOMNode> delNode(do_QueryInterface(thePoint.mTextNode));
2177 0 : res = DeleteChars(delNode, thePoint.mOffset+1, delNode, thePoint.mOffset+2);
2178 0 : NS_ENSURE_SUCCESS(res, res);
2179 : }
2180 0 : return NS_OK;
2181 : }
2182 :
2183 : nsresult
2184 0 : nsWSRunObject::CheckLeadingNBSP(WSFragment *aRun, nsIDOMNode *aNode, PRInt32 aOffset)
2185 : {
2186 : // try to change an nbsp to a space, if possible, just to prevent nbsp proliferation
2187 : // this routine is called when we about to make this point in the ws abut an inserted
2188 : // text, so we don't have to worry about what is before it. What is before it now will
2189 : // end up before the inserted text.
2190 0 : WSPoint thePoint;
2191 0 : bool canConvert = false;
2192 0 : nsresult res = GetCharAfter(aNode, aOffset, &thePoint);
2193 0 : if (NS_SUCCEEDED(res) && thePoint.mChar == nbsp)
2194 : {
2195 0 : WSPoint nextPoint, tmp=thePoint;
2196 0 : tmp.mOffset++; // we want to be after thePoint
2197 0 : res = GetCharAfter(tmp, &nextPoint);
2198 0 : if (NS_SUCCEEDED(res) && nextPoint.mTextNode)
2199 : {
2200 0 : if (!nsCRT::IsAsciiSpace(nextPoint.mChar)) canConvert = true;
2201 : }
2202 0 : else if (aRun->mRightType == eText) canConvert = true;
2203 0 : else if (aRun->mRightType == eSpecial) canConvert = true;
2204 0 : else if (aRun->mRightType == eBreak) canConvert = true;
2205 : }
2206 0 : if (canConvert)
2207 : {
2208 : // first, insert a space
2209 0 : nsCOMPtr<nsIDOMCharacterData> textNode(do_QueryInterface(thePoint.mTextNode));
2210 0 : NS_ENSURE_TRUE(textNode, NS_ERROR_NULL_POINTER);
2211 0 : nsAutoTxnsConserveSelection dontSpazMySelection(mHTMLEditor);
2212 0 : nsAutoString spaceStr(PRUnichar(32));
2213 0 : res = mHTMLEditor->InsertTextIntoTextNodeImpl(spaceStr, textNode, thePoint.mOffset, true);
2214 0 : NS_ENSURE_SUCCESS(res, res);
2215 :
2216 : // finally, delete that nbsp
2217 0 : nsCOMPtr<nsIDOMNode> delNode(do_QueryInterface(thePoint.mTextNode));
2218 0 : res = DeleteChars(delNode, thePoint.mOffset+1, delNode, thePoint.mOffset+2);
2219 0 : NS_ENSURE_SUCCESS(res, res);
2220 : }
2221 0 : return NS_OK;
2222 : }
2223 :
2224 :
2225 : nsresult
2226 0 : nsWSRunObject::ScrubBlockBoundaryInner(nsHTMLEditor *aHTMLEd,
2227 : nsCOMPtr<nsIDOMNode> *aBlock,
2228 : BlockBoundary aBoundary)
2229 : {
2230 0 : NS_ENSURE_TRUE(aBlock && aHTMLEd, NS_ERROR_NULL_POINTER);
2231 0 : PRInt32 offset=0;
2232 0 : if (aBoundary == kBlockEnd)
2233 : {
2234 : PRUint32 uOffset;
2235 0 : aHTMLEd->GetLengthOfDOMNode(*aBlock, uOffset);
2236 0 : offset = uOffset;
2237 : }
2238 0 : nsWSRunObject theWSObj(aHTMLEd, *aBlock, offset);
2239 0 : return theWSObj.Scrub();
2240 : }
2241 :
2242 :
2243 : nsresult
2244 0 : nsWSRunObject::Scrub()
2245 : {
2246 0 : WSFragment *run = mStartRun;
2247 0 : while (run)
2248 : {
2249 0 : if (run->mType & (eLeadingWS|eTrailingWS) )
2250 : {
2251 0 : nsresult res = DeleteChars(run->mStartNode, run->mStartOffset, run->mEndNode, run->mEndOffset);
2252 0 : NS_ENSURE_SUCCESS(res, res);
2253 : }
2254 0 : run = run->mRight;
2255 : }
2256 0 : return NS_OK;
2257 : }
2258 :
|