WSRunObject.cpp 64 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927
  1. /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /* This Source Code Form is subject to the terms of the Mozilla Public
  3. * License, v. 2.0. If a copy of the MPL was not distributed with this
  4. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  5. #include "WSRunObject.h"
  6. #include "TextEditUtils.h"
  7. #include "mozilla/Assertions.h"
  8. #include "mozilla/Casting.h"
  9. #include "mozilla/EditorUtils.h"
  10. #include "mozilla/HTMLEditor.h"
  11. #include "mozilla/mozalloc.h"
  12. #include "mozilla/OwningNonNull.h"
  13. #include "mozilla/SelectionState.h"
  14. #include "nsAString.h"
  15. #include "nsCRT.h"
  16. #include "nsContentUtils.h"
  17. #include "nsDebug.h"
  18. #include "nsError.h"
  19. #include "nsIContent.h"
  20. #include "nsIDOMDocument.h"
  21. #include "nsIDOMNode.h"
  22. #include "nsISupportsImpl.h"
  23. #include "nsRange.h"
  24. #include "nsString.h"
  25. #include "nsTextFragment.h"
  26. namespace mozilla {
  27. using namespace dom;
  28. const char16_t nbsp = 160;
  29. WSRunObject::WSRunObject(HTMLEditor* aHTMLEditor,
  30. nsINode* aNode,
  31. int32_t aOffset)
  32. : mNode(aNode)
  33. , mOffset(aOffset)
  34. , mPRE(false)
  35. , mStartOffset(0)
  36. , mEndOffset(0)
  37. , mFirstNBSPOffset(0)
  38. , mLastNBSPOffset(0)
  39. , mStartRun(nullptr)
  40. , mEndRun(nullptr)
  41. , mHTMLEditor(aHTMLEditor)
  42. {
  43. GetWSNodes();
  44. GetRuns();
  45. }
  46. WSRunObject::WSRunObject(HTMLEditor* aHTMLEditor,
  47. nsIDOMNode* aNode,
  48. int32_t aOffset)
  49. : mNode(do_QueryInterface(aNode))
  50. , mOffset(aOffset)
  51. , mPRE(false)
  52. , mStartOffset(0)
  53. , mEndOffset(0)
  54. , mFirstNBSPOffset(0)
  55. , mLastNBSPOffset(0)
  56. , mStartRun(nullptr)
  57. , mEndRun(nullptr)
  58. , mHTMLEditor(aHTMLEditor)
  59. {
  60. GetWSNodes();
  61. GetRuns();
  62. }
  63. WSRunObject::~WSRunObject()
  64. {
  65. ClearRuns();
  66. }
  67. nsresult
  68. WSRunObject::ScrubBlockBoundary(HTMLEditor* aHTMLEditor,
  69. BlockBoundary aBoundary,
  70. nsINode* aBlock,
  71. int32_t aOffset)
  72. {
  73. NS_ENSURE_TRUE(aHTMLEditor && aBlock, NS_ERROR_NULL_POINTER);
  74. int32_t offset;
  75. if (aBoundary == kBlockStart) {
  76. offset = 0;
  77. } else if (aBoundary == kBlockEnd) {
  78. offset = aBlock->Length();
  79. } else {
  80. // Else we are scrubbing an outer boundary - just before or after a block
  81. // element.
  82. NS_ENSURE_STATE(aOffset >= 0);
  83. offset = aOffset;
  84. }
  85. WSRunObject theWSObj(aHTMLEditor, aBlock, offset);
  86. return theWSObj.Scrub();
  87. }
  88. nsresult
  89. WSRunObject::PrepareToJoinBlocks(HTMLEditor* aHTMLEditor,
  90. Element* aLeftBlock,
  91. Element* aRightBlock)
  92. {
  93. NS_ENSURE_TRUE(aLeftBlock && aRightBlock && aHTMLEditor,
  94. NS_ERROR_NULL_POINTER);
  95. WSRunObject leftWSObj(aHTMLEditor, aLeftBlock, aLeftBlock->Length());
  96. WSRunObject rightWSObj(aHTMLEditor, aRightBlock, 0);
  97. return leftWSObj.PrepareToDeleteRangePriv(&rightWSObj);
  98. }
  99. nsresult
  100. WSRunObject::PrepareToDeleteRange(HTMLEditor* aHTMLEditor,
  101. nsCOMPtr<nsINode>* aStartNode,
  102. int32_t* aStartOffset,
  103. nsCOMPtr<nsINode>* aEndNode,
  104. int32_t* aEndOffset)
  105. {
  106. NS_ENSURE_TRUE(aHTMLEditor && aStartNode && *aStartNode && aStartOffset &&
  107. aEndNode && *aEndNode && aEndOffset, NS_ERROR_NULL_POINTER);
  108. AutoTrackDOMPoint trackerStart(aHTMLEditor->mRangeUpdater,
  109. aStartNode, aStartOffset);
  110. AutoTrackDOMPoint trackerEnd(aHTMLEditor->mRangeUpdater,
  111. aEndNode, aEndOffset);
  112. WSRunObject leftWSObj(aHTMLEditor, *aStartNode, *aStartOffset);
  113. WSRunObject rightWSObj(aHTMLEditor, *aEndNode, *aEndOffset);
  114. return leftWSObj.PrepareToDeleteRangePriv(&rightWSObj);
  115. }
  116. nsresult
  117. WSRunObject::PrepareToDeleteNode(HTMLEditor* aHTMLEditor,
  118. nsIContent* aContent)
  119. {
  120. NS_ENSURE_TRUE(aContent && aHTMLEditor, NS_ERROR_NULL_POINTER);
  121. nsCOMPtr<nsINode> parent = aContent->GetParentNode();
  122. NS_ENSURE_STATE(parent);
  123. int32_t offset = parent->IndexOf(aContent);
  124. WSRunObject leftWSObj(aHTMLEditor, parent, offset);
  125. WSRunObject rightWSObj(aHTMLEditor, parent, offset + 1);
  126. return leftWSObj.PrepareToDeleteRangePriv(&rightWSObj);
  127. }
  128. nsresult
  129. WSRunObject::PrepareToSplitAcrossBlocks(HTMLEditor* aHTMLEditor,
  130. nsCOMPtr<nsINode>* aSplitNode,
  131. int32_t* aSplitOffset)
  132. {
  133. NS_ENSURE_TRUE(aHTMLEditor && aSplitNode && *aSplitNode && aSplitOffset,
  134. NS_ERROR_NULL_POINTER);
  135. AutoTrackDOMPoint tracker(aHTMLEditor->mRangeUpdater,
  136. aSplitNode, aSplitOffset);
  137. WSRunObject wsObj(aHTMLEditor, *aSplitNode, *aSplitOffset);
  138. return wsObj.PrepareToSplitAcrossBlocksPriv();
  139. }
  140. already_AddRefed<Element>
  141. WSRunObject::InsertBreak(nsCOMPtr<nsINode>* aInOutParent,
  142. int32_t* aInOutOffset,
  143. nsIEditor::EDirection aSelect)
  144. {
  145. // MOOSE: for now, we always assume non-PRE formatting. Fix this later.
  146. // meanwhile, the pre case is handled in WillInsertText in
  147. // HTMLEditRules.cpp
  148. NS_ENSURE_TRUE(aInOutParent && aInOutOffset, nullptr);
  149. WSFragment *beforeRun, *afterRun;
  150. FindRun(*aInOutParent, *aInOutOffset, &beforeRun, false);
  151. FindRun(*aInOutParent, *aInOutOffset, &afterRun, true);
  152. {
  153. // Some scoping for AutoTrackDOMPoint. This will track our insertion
  154. // point while we tweak any surrounding whitespace
  155. AutoTrackDOMPoint tracker(mHTMLEditor->mRangeUpdater, aInOutParent,
  156. aInOutOffset);
  157. // Handle any changes needed to ws run after inserted br
  158. if (!afterRun || (afterRun->mType & WSType::trailingWS)) {
  159. // Don't need to do anything. Just insert break. ws won't change.
  160. } else if (afterRun->mType & WSType::leadingWS) {
  161. // Delete the leading ws that is after insertion point. We don't
  162. // have to (it would still not be significant after br), but it's
  163. // just more aesthetically pleasing to.
  164. nsresult rv = DeleteChars(*aInOutParent, *aInOutOffset,
  165. afterRun->mEndNode, afterRun->mEndOffset,
  166. eOutsideUserSelectAll);
  167. NS_ENSURE_SUCCESS(rv, nullptr);
  168. } else if (afterRun->mType == WSType::normalWS) {
  169. // Need to determine if break at front of non-nbsp run. If so, convert
  170. // run to nbsp.
  171. WSPoint thePoint = GetCharAfter(*aInOutParent, *aInOutOffset);
  172. if (thePoint.mTextNode && nsCRT::IsAsciiSpace(thePoint.mChar)) {
  173. WSPoint prevPoint = GetCharBefore(thePoint);
  174. if (prevPoint.mTextNode && !nsCRT::IsAsciiSpace(prevPoint.mChar)) {
  175. // We are at start of non-nbsps. Convert to a single nbsp.
  176. nsresult rv = ConvertToNBSP(thePoint);
  177. NS_ENSURE_SUCCESS(rv, nullptr);
  178. }
  179. }
  180. }
  181. // Handle any changes needed to ws run before inserted br
  182. if (!beforeRun || (beforeRun->mType & WSType::leadingWS)) {
  183. // Don't need to do anything. Just insert break. ws won't change.
  184. } else if (beforeRun->mType & WSType::trailingWS) {
  185. // Need to delete the trailing ws that is before insertion point, because it
  186. // would become significant after break inserted.
  187. nsresult rv = DeleteChars(beforeRun->mStartNode, beforeRun->mStartOffset,
  188. *aInOutParent, *aInOutOffset,
  189. eOutsideUserSelectAll);
  190. NS_ENSURE_SUCCESS(rv, nullptr);
  191. } else if (beforeRun->mType == WSType::normalWS) {
  192. // Try to change an nbsp to a space, just to prevent nbsp proliferation
  193. nsresult rv = CheckTrailingNBSP(beforeRun, *aInOutParent, *aInOutOffset);
  194. NS_ENSURE_SUCCESS(rv, nullptr);
  195. }
  196. }
  197. // ready, aim, fire!
  198. return mHTMLEditor->CreateBRImpl(aInOutParent, aInOutOffset, aSelect);
  199. }
  200. nsresult
  201. WSRunObject::InsertText(const nsAString& aStringToInsert,
  202. nsCOMPtr<nsINode>* aInOutParent,
  203. int32_t* aInOutOffset,
  204. nsIDocument* aDoc)
  205. {
  206. // MOOSE: for now, we always assume non-PRE formatting. Fix this later.
  207. // meanwhile, the pre case is handled in WillInsertText in
  208. // HTMLEditRules.cpp
  209. // MOOSE: for now, just getting the ws logic straight. This implementation
  210. // is very slow. Will need to replace edit rules impl with a more efficient
  211. // text sink here that does the minimal amount of searching/replacing/copying
  212. NS_ENSURE_TRUE(aInOutParent && aInOutOffset && aDoc, NS_ERROR_NULL_POINTER);
  213. if (aStringToInsert.IsEmpty()) {
  214. return NS_OK;
  215. }
  216. nsAutoString theString(aStringToInsert);
  217. WSFragment *beforeRun, *afterRun;
  218. FindRun(*aInOutParent, *aInOutOffset, &beforeRun, false);
  219. FindRun(*aInOutParent, *aInOutOffset, &afterRun, true);
  220. {
  221. // Some scoping for AutoTrackDOMPoint. This will track our insertion
  222. // point while we tweak any surrounding whitespace
  223. AutoTrackDOMPoint tracker(mHTMLEditor->mRangeUpdater, aInOutParent,
  224. aInOutOffset);
  225. // Handle any changes needed to ws run after inserted text
  226. if (!afterRun || afterRun->mType & WSType::trailingWS) {
  227. // Don't need to do anything. Just insert text. ws won't change.
  228. } else if (afterRun->mType & WSType::leadingWS) {
  229. // Delete the leading ws that is after insertion point, because it
  230. // would become significant after text inserted.
  231. nsresult rv =
  232. DeleteChars(*aInOutParent, *aInOutOffset, afterRun->mEndNode,
  233. afterRun->mEndOffset, eOutsideUserSelectAll);
  234. NS_ENSURE_SUCCESS(rv, rv);
  235. } else if (afterRun->mType == WSType::normalWS) {
  236. // Try to change an nbsp to a space, if possible, just to prevent nbsp
  237. // proliferation
  238. nsresult rv = CheckLeadingNBSP(afterRun, *aInOutParent, *aInOutOffset);
  239. NS_ENSURE_SUCCESS(rv, rv);
  240. }
  241. // Handle any changes needed to ws run before inserted text
  242. if (!beforeRun || beforeRun->mType & WSType::leadingWS) {
  243. // Don't need to do anything. Just insert text. ws won't change.
  244. } else if (beforeRun->mType & WSType::trailingWS) {
  245. // Need to delete the trailing ws that is before insertion point, because
  246. // it would become significant after text inserted.
  247. nsresult rv =
  248. DeleteChars(beforeRun->mStartNode, beforeRun->mStartOffset,
  249. *aInOutParent, *aInOutOffset, eOutsideUserSelectAll);
  250. NS_ENSURE_SUCCESS(rv, rv);
  251. } else if (beforeRun->mType == WSType::normalWS) {
  252. // Try to change an nbsp to a space, if possible, just to prevent nbsp
  253. // proliferation
  254. nsresult rv = CheckTrailingNBSP(beforeRun, *aInOutParent, *aInOutOffset);
  255. NS_ENSURE_SUCCESS(rv, rv);
  256. }
  257. }
  258. // Next up, tweak head and tail of string as needed. First the head: there
  259. // are a variety of circumstances that would require us to convert a leading
  260. // ws char into an nbsp:
  261. if (nsCRT::IsAsciiSpace(theString[0])) {
  262. // We have a leading space
  263. if (beforeRun) {
  264. if (beforeRun->mType & WSType::leadingWS) {
  265. theString.SetCharAt(nbsp, 0);
  266. } else if (beforeRun->mType & WSType::normalWS) {
  267. WSPoint wspoint = GetCharBefore(*aInOutParent, *aInOutOffset);
  268. if (wspoint.mTextNode && nsCRT::IsAsciiSpace(wspoint.mChar)) {
  269. theString.SetCharAt(nbsp, 0);
  270. }
  271. }
  272. } else if (mStartReason & WSType::block || mStartReason == WSType::br) {
  273. theString.SetCharAt(nbsp, 0);
  274. }
  275. }
  276. // Then the tail
  277. uint32_t lastCharIndex = theString.Length() - 1;
  278. if (nsCRT::IsAsciiSpace(theString[lastCharIndex])) {
  279. // We have a leading space
  280. if (afterRun) {
  281. if (afterRun->mType & WSType::trailingWS) {
  282. theString.SetCharAt(nbsp, lastCharIndex);
  283. } else if (afterRun->mType & WSType::normalWS) {
  284. WSPoint wspoint = GetCharAfter(*aInOutParent, *aInOutOffset);
  285. if (wspoint.mTextNode && nsCRT::IsAsciiSpace(wspoint.mChar)) {
  286. theString.SetCharAt(nbsp, lastCharIndex);
  287. }
  288. }
  289. } else if (mEndReason & WSType::block) {
  290. theString.SetCharAt(nbsp, lastCharIndex);
  291. }
  292. }
  293. // Next, scan string for adjacent ws and convert to nbsp/space combos
  294. // MOOSE: don't need to convert tabs here since that is done by
  295. // WillInsertText() before we are called. Eventually, all that logic will be
  296. // pushed down into here and made more efficient.
  297. bool prevWS = false;
  298. for (uint32_t i = 0; i <= lastCharIndex; i++) {
  299. if (nsCRT::IsAsciiSpace(theString[i])) {
  300. if (prevWS) {
  301. // i - 1 can't be negative because prevWS starts out false
  302. theString.SetCharAt(nbsp, i - 1);
  303. } else {
  304. prevWS = true;
  305. }
  306. } else {
  307. prevWS = false;
  308. }
  309. }
  310. // Ready, aim, fire!
  311. mHTMLEditor->InsertTextImpl(theString, aInOutParent, aInOutOffset, aDoc);
  312. return NS_OK;
  313. }
  314. nsresult
  315. WSRunObject::DeleteWSBackward()
  316. {
  317. WSPoint point = GetCharBefore(mNode, mOffset);
  318. NS_ENSURE_TRUE(point.mTextNode, NS_OK); // nothing to delete
  319. // Easy case, preformatted ws.
  320. if (mPRE && (nsCRT::IsAsciiSpace(point.mChar) || point.mChar == nbsp)) {
  321. return DeleteChars(point.mTextNode, point.mOffset,
  322. point.mTextNode, point.mOffset + 1);
  323. }
  324. // Caller's job to ensure that previous char is really ws. If it is normal
  325. // ws, we need to delete the whole run.
  326. if (nsCRT::IsAsciiSpace(point.mChar)) {
  327. RefPtr<Text> startNodeText, endNodeText;
  328. int32_t startOffset, endOffset;
  329. GetAsciiWSBounds(eBoth, point.mTextNode, point.mOffset + 1,
  330. getter_AddRefs(startNodeText), &startOffset,
  331. getter_AddRefs(endNodeText), &endOffset);
  332. // adjust surrounding ws
  333. nsCOMPtr<nsINode> startNode = startNodeText.get();
  334. nsCOMPtr<nsINode> endNode = endNodeText.get();
  335. nsresult rv =
  336. WSRunObject::PrepareToDeleteRange(mHTMLEditor,
  337. address_of(startNode), &startOffset,
  338. address_of(endNode), &endOffset);
  339. NS_ENSURE_SUCCESS(rv, rv);
  340. // finally, delete that ws
  341. return DeleteChars(startNode, startOffset, endNode, endOffset);
  342. }
  343. if (point.mChar == nbsp) {
  344. nsCOMPtr<nsINode> node(point.mTextNode);
  345. // adjust surrounding ws
  346. int32_t startOffset = point.mOffset;
  347. int32_t endOffset = point.mOffset + 1;
  348. nsresult rv =
  349. WSRunObject::PrepareToDeleteRange(mHTMLEditor,
  350. address_of(node), &startOffset,
  351. address_of(node), &endOffset);
  352. NS_ENSURE_SUCCESS(rv, rv);
  353. // finally, delete that ws
  354. return DeleteChars(node, startOffset, node, endOffset);
  355. }
  356. return NS_OK;
  357. }
  358. nsresult
  359. WSRunObject::DeleteWSForward()
  360. {
  361. WSPoint point = GetCharAfter(mNode, mOffset);
  362. NS_ENSURE_TRUE(point.mTextNode, NS_OK); // nothing to delete
  363. // Easy case, preformatted ws.
  364. if (mPRE && (nsCRT::IsAsciiSpace(point.mChar) || point.mChar == nbsp)) {
  365. return DeleteChars(point.mTextNode, point.mOffset,
  366. point.mTextNode, point.mOffset + 1);
  367. }
  368. // Caller's job to ensure that next char is really ws. If it is normal ws,
  369. // we need to delete the whole run.
  370. if (nsCRT::IsAsciiSpace(point.mChar)) {
  371. RefPtr<Text> startNodeText, endNodeText;
  372. int32_t startOffset, endOffset;
  373. GetAsciiWSBounds(eBoth, point.mTextNode, point.mOffset + 1,
  374. getter_AddRefs(startNodeText), &startOffset,
  375. getter_AddRefs(endNodeText), &endOffset);
  376. // Adjust surrounding ws
  377. nsCOMPtr<nsINode> startNode(startNodeText), endNode(endNodeText);
  378. nsresult rv =
  379. WSRunObject::PrepareToDeleteRange(mHTMLEditor,
  380. address_of(startNode), &startOffset,
  381. address_of(endNode), &endOffset);
  382. NS_ENSURE_SUCCESS(rv, rv);
  383. // Finally, delete that ws
  384. return DeleteChars(startNode, startOffset, endNode, endOffset);
  385. }
  386. if (point.mChar == nbsp) {
  387. nsCOMPtr<nsINode> node(point.mTextNode);
  388. // Adjust surrounding ws
  389. int32_t startOffset = point.mOffset;
  390. int32_t endOffset = point.mOffset+1;
  391. nsresult rv =
  392. WSRunObject::PrepareToDeleteRange(mHTMLEditor,
  393. address_of(node), &startOffset,
  394. address_of(node), &endOffset);
  395. NS_ENSURE_SUCCESS(rv, rv);
  396. // Finally, delete that ws
  397. return DeleteChars(node, startOffset, node, endOffset);
  398. }
  399. return NS_OK;
  400. }
  401. void
  402. WSRunObject::PriorVisibleNode(nsINode* aNode,
  403. int32_t aOffset,
  404. nsCOMPtr<nsINode>* outVisNode,
  405. int32_t* outVisOffset,
  406. WSType* outType)
  407. {
  408. // Find first visible thing before the point. Position
  409. // outVisNode/outVisOffset just _after_ that thing. If we don't find
  410. // anything return start of ws.
  411. MOZ_ASSERT(aNode && outVisNode && outVisOffset && outType);
  412. WSFragment* run;
  413. FindRun(aNode, aOffset, &run, false);
  414. // Is there a visible run there or earlier?
  415. for (; run; run = run->mLeft) {
  416. if (run->mType == WSType::normalWS) {
  417. WSPoint point = GetCharBefore(aNode, aOffset);
  418. // When it's a non-empty text node, return it.
  419. if (point.mTextNode && point.mTextNode->Length()) {
  420. *outVisNode = point.mTextNode;
  421. *outVisOffset = point.mOffset + 1;
  422. if (nsCRT::IsAsciiSpace(point.mChar) || point.mChar == nbsp) {
  423. *outType = WSType::normalWS;
  424. } else {
  425. *outType = WSType::text;
  426. }
  427. return;
  428. }
  429. // If no text node, keep looking. We should eventually fall out of loop
  430. }
  431. }
  432. // If we get here, then nothing in ws data to find. Return start reason.
  433. *outVisNode = mStartReasonNode;
  434. // This really isn't meaningful if mStartReasonNode != mStartNode
  435. *outVisOffset = mStartOffset;
  436. *outType = mStartReason;
  437. }
  438. void
  439. WSRunObject::NextVisibleNode(nsINode* aNode,
  440. int32_t aOffset,
  441. nsCOMPtr<nsINode>* outVisNode,
  442. int32_t* outVisOffset,
  443. WSType* outType)
  444. {
  445. // Find first visible thing after the point. Position
  446. // outVisNode/outVisOffset just _before_ that thing. If we don't find
  447. // anything return end of ws.
  448. MOZ_ASSERT(aNode && outVisNode && outVisOffset && outType);
  449. WSFragment* run;
  450. FindRun(aNode, aOffset, &run, true);
  451. // Is there a visible run there or later?
  452. for (; run; run = run->mRight) {
  453. if (run->mType == WSType::normalWS) {
  454. WSPoint point = GetCharAfter(aNode, aOffset);
  455. // When it's a non-empty text node, return it.
  456. if (point.mTextNode && point.mTextNode->Length()) {
  457. *outVisNode = point.mTextNode;
  458. *outVisOffset = point.mOffset;
  459. if (nsCRT::IsAsciiSpace(point.mChar) || point.mChar == nbsp) {
  460. *outType = WSType::normalWS;
  461. } else {
  462. *outType = WSType::text;
  463. }
  464. return;
  465. }
  466. // If no text node, keep looking. We should eventually fall out of loop
  467. }
  468. }
  469. // If we get here, then nothing in ws data to find. Return end reason
  470. *outVisNode = mEndReasonNode;
  471. // This really isn't meaningful if mEndReasonNode != mEndNode
  472. *outVisOffset = mEndOffset;
  473. *outType = mEndReason;
  474. }
  475. nsresult
  476. WSRunObject::AdjustWhitespace()
  477. {
  478. // this routine examines a run of ws and tries to get rid of some unneeded nbsp's,
  479. // replacing them with regualr ascii space if possible. Keeping things simple
  480. // for now and just trying to fix up the trailing ws in the run.
  481. if (!mLastNBSPNode) {
  482. // nothing to do!
  483. return NS_OK;
  484. }
  485. WSFragment *curRun = mStartRun;
  486. while (curRun) {
  487. // look for normal ws run
  488. if (curRun->mType == WSType::normalWS) {
  489. nsresult rv = CheckTrailingNBSPOfRun(curRun);
  490. if (NS_FAILED(rv)) {
  491. return rv;
  492. }
  493. }
  494. curRun = curRun->mRight;
  495. }
  496. return NS_OK;
  497. }
  498. //--------------------------------------------------------------------------------------------
  499. // protected methods
  500. //--------------------------------------------------------------------------------------------
  501. nsINode*
  502. WSRunObject::GetWSBoundingParent()
  503. {
  504. NS_ENSURE_TRUE(mNode, nullptr);
  505. OwningNonNull<nsINode> wsBoundingParent = *mNode;
  506. while (!IsBlockNode(wsBoundingParent)) {
  507. nsCOMPtr<nsINode> parent = wsBoundingParent->GetParentNode();
  508. if (!parent || !mHTMLEditor->IsEditable(parent)) {
  509. break;
  510. }
  511. wsBoundingParent = parent;
  512. }
  513. return wsBoundingParent;
  514. }
  515. nsresult
  516. WSRunObject::GetWSNodes()
  517. {
  518. // collect up an array of nodes that are contiguous with the insertion point
  519. // and which contain only whitespace. Stop if you reach non-ws text or a new
  520. // block boundary.
  521. EditorDOMPoint start(mNode, mOffset), end(mNode, mOffset);
  522. nsCOMPtr<nsINode> wsBoundingParent = GetWSBoundingParent();
  523. // first look backwards to find preceding ws nodes
  524. if (RefPtr<Text> textNode = mNode->GetAsText()) {
  525. const nsTextFragment* textFrag = textNode->GetText();
  526. mNodeArray.InsertElementAt(0, textNode);
  527. if (mOffset) {
  528. for (int32_t pos = mOffset - 1; pos >= 0; pos--) {
  529. // sanity bounds check the char position. bug 136165
  530. if (uint32_t(pos) >= textFrag->GetLength()) {
  531. NS_NOTREACHED("looking beyond end of text fragment");
  532. continue;
  533. }
  534. char16_t theChar = textFrag->CharAt(pos);
  535. if (!nsCRT::IsAsciiSpace(theChar)) {
  536. if (theChar != nbsp) {
  537. mStartNode = textNode;
  538. mStartOffset = pos + 1;
  539. mStartReason = WSType::text;
  540. mStartReasonNode = textNode;
  541. break;
  542. }
  543. // as we look backwards update our earliest found nbsp
  544. mFirstNBSPNode = textNode;
  545. mFirstNBSPOffset = pos;
  546. // also keep track of latest nbsp so far
  547. if (!mLastNBSPNode) {
  548. mLastNBSPNode = textNode;
  549. mLastNBSPOffset = pos;
  550. }
  551. }
  552. start.node = textNode;
  553. start.offset = pos;
  554. }
  555. }
  556. }
  557. while (!mStartNode) {
  558. // we haven't found the start of ws yet. Keep looking
  559. nsCOMPtr<nsIContent> priorNode = GetPreviousWSNode(start, wsBoundingParent);
  560. if (priorNode) {
  561. if (IsBlockNode(priorNode)) {
  562. mStartNode = start.node;
  563. mStartOffset = start.offset;
  564. mStartReason = WSType::otherBlock;
  565. mStartReasonNode = priorNode;
  566. } else if (RefPtr<Text> textNode = priorNode->GetAsText()) {
  567. mNodeArray.InsertElementAt(0, textNode);
  568. const nsTextFragment *textFrag;
  569. if (!textNode || !(textFrag = textNode->GetText())) {
  570. return NS_ERROR_NULL_POINTER;
  571. }
  572. uint32_t len = textNode->TextLength();
  573. if (len < 1) {
  574. // Zero length text node. Set start point to it
  575. // so we can get past it!
  576. start.SetPoint(priorNode, 0);
  577. } else {
  578. for (int32_t pos = len - 1; pos >= 0; pos--) {
  579. // sanity bounds check the char position. bug 136165
  580. if (uint32_t(pos) >= textFrag->GetLength()) {
  581. NS_NOTREACHED("looking beyond end of text fragment");
  582. continue;
  583. }
  584. char16_t theChar = textFrag->CharAt(pos);
  585. if (!nsCRT::IsAsciiSpace(theChar)) {
  586. if (theChar != nbsp) {
  587. mStartNode = textNode;
  588. mStartOffset = pos + 1;
  589. mStartReason = WSType::text;
  590. mStartReasonNode = textNode;
  591. break;
  592. }
  593. // as we look backwards update our earliest found nbsp
  594. mFirstNBSPNode = textNode;
  595. mFirstNBSPOffset = pos;
  596. // also keep track of latest nbsp so far
  597. if (!mLastNBSPNode) {
  598. mLastNBSPNode = textNode;
  599. mLastNBSPOffset = pos;
  600. }
  601. }
  602. start.SetPoint(textNode, pos);
  603. }
  604. }
  605. } else {
  606. // it's a break or a special node, like <img>, that is not a block and not
  607. // a break but still serves as a terminator to ws runs.
  608. mStartNode = start.node;
  609. mStartOffset = start.offset;
  610. if (TextEditUtils::IsBreak(priorNode)) {
  611. mStartReason = WSType::br;
  612. } else {
  613. mStartReason = WSType::special;
  614. }
  615. mStartReasonNode = priorNode;
  616. }
  617. } else {
  618. // no prior node means we exhausted wsBoundingParent
  619. mStartNode = start.node;
  620. mStartOffset = start.offset;
  621. mStartReason = WSType::thisBlock;
  622. mStartReasonNode = wsBoundingParent;
  623. }
  624. }
  625. // then look ahead to find following ws nodes
  626. if (RefPtr<Text> textNode = mNode->GetAsText()) {
  627. // don't need to put it on list. it already is from code above
  628. const nsTextFragment *textFrag = textNode->GetText();
  629. uint32_t len = textNode->TextLength();
  630. if (uint16_t(mOffset)<len) {
  631. for (uint32_t pos = mOffset; pos < len; pos++) {
  632. // sanity bounds check the char position. bug 136165
  633. if (pos >= textFrag->GetLength()) {
  634. NS_NOTREACHED("looking beyond end of text fragment");
  635. continue;
  636. }
  637. char16_t theChar = textFrag->CharAt(pos);
  638. if (!nsCRT::IsAsciiSpace(theChar)) {
  639. if (theChar != nbsp) {
  640. mEndNode = textNode;
  641. mEndOffset = pos;
  642. mEndReason = WSType::text;
  643. mEndReasonNode = textNode;
  644. break;
  645. }
  646. // as we look forwards update our latest found nbsp
  647. mLastNBSPNode = textNode;
  648. mLastNBSPOffset = pos;
  649. // also keep track of earliest nbsp so far
  650. if (!mFirstNBSPNode) {
  651. mFirstNBSPNode = textNode;
  652. mFirstNBSPOffset = pos;
  653. }
  654. }
  655. end.SetPoint(textNode, pos + 1);
  656. }
  657. }
  658. }
  659. while (!mEndNode) {
  660. // we haven't found the end of ws yet. Keep looking
  661. nsCOMPtr<nsIContent> nextNode = GetNextWSNode(end, wsBoundingParent);
  662. if (nextNode) {
  663. if (IsBlockNode(nextNode)) {
  664. // we encountered a new block. therefore no more ws.
  665. mEndNode = end.node;
  666. mEndOffset = end.offset;
  667. mEndReason = WSType::otherBlock;
  668. mEndReasonNode = nextNode;
  669. } else if (RefPtr<Text> textNode = nextNode->GetAsText()) {
  670. mNodeArray.AppendElement(textNode);
  671. const nsTextFragment *textFrag;
  672. if (!textNode || !(textFrag = textNode->GetText())) {
  673. return NS_ERROR_NULL_POINTER;
  674. }
  675. uint32_t len = textNode->TextLength();
  676. if (len < 1) {
  677. // Zero length text node. Set end point to it
  678. // so we can get past it!
  679. end.SetPoint(textNode, 0);
  680. } else {
  681. for (uint32_t pos = 0; pos < len; pos++) {
  682. // sanity bounds check the char position. bug 136165
  683. if (pos >= textFrag->GetLength()) {
  684. NS_NOTREACHED("looking beyond end of text fragment");
  685. continue;
  686. }
  687. char16_t theChar = textFrag->CharAt(pos);
  688. if (!nsCRT::IsAsciiSpace(theChar)) {
  689. if (theChar != nbsp) {
  690. mEndNode = textNode;
  691. mEndOffset = pos;
  692. mEndReason = WSType::text;
  693. mEndReasonNode = textNode;
  694. break;
  695. }
  696. // as we look forwards update our latest found nbsp
  697. mLastNBSPNode = textNode;
  698. mLastNBSPOffset = pos;
  699. // also keep track of earliest nbsp so far
  700. if (!mFirstNBSPNode) {
  701. mFirstNBSPNode = textNode;
  702. mFirstNBSPOffset = pos;
  703. }
  704. }
  705. end.SetPoint(textNode, pos + 1);
  706. }
  707. }
  708. } else {
  709. // we encountered a break or a special node, like <img>,
  710. // that is not a block and not a break but still
  711. // serves as a terminator to ws runs.
  712. mEndNode = end.node;
  713. mEndOffset = end.offset;
  714. if (TextEditUtils::IsBreak(nextNode)) {
  715. mEndReason = WSType::br;
  716. } else {
  717. mEndReason = WSType::special;
  718. }
  719. mEndReasonNode = nextNode;
  720. }
  721. } else {
  722. // no next node means we exhausted wsBoundingParent
  723. mEndNode = end.node;
  724. mEndOffset = end.offset;
  725. mEndReason = WSType::thisBlock;
  726. mEndReasonNode = wsBoundingParent;
  727. }
  728. }
  729. return NS_OK;
  730. }
  731. void
  732. WSRunObject::GetRuns()
  733. {
  734. ClearRuns();
  735. // handle some easy cases first
  736. mHTMLEditor->IsPreformatted(GetAsDOMNode(mNode), &mPRE);
  737. // if it's preformatedd, or if we are surrounded by text or special, it's all one
  738. // big normal ws run
  739. if (mPRE ||
  740. ((mStartReason == WSType::text || mStartReason == WSType::special) &&
  741. (mEndReason == WSType::text || mEndReason == WSType::special ||
  742. mEndReason == WSType::br))) {
  743. MakeSingleWSRun(WSType::normalWS);
  744. return;
  745. }
  746. // if we are before or after a block (or after a break), and there are no nbsp's,
  747. // then it's all non-rendering ws.
  748. if (!mFirstNBSPNode && !mLastNBSPNode &&
  749. ((mStartReason & WSType::block) || mStartReason == WSType::br ||
  750. (mEndReason & WSType::block))) {
  751. WSType wstype;
  752. if ((mStartReason & WSType::block) || mStartReason == WSType::br) {
  753. wstype = WSType::leadingWS;
  754. }
  755. if (mEndReason & WSType::block) {
  756. wstype |= WSType::trailingWS;
  757. }
  758. MakeSingleWSRun(wstype);
  759. return;
  760. }
  761. // otherwise a little trickier. shucks.
  762. mStartRun = new WSFragment();
  763. mStartRun->mStartNode = mStartNode;
  764. mStartRun->mStartOffset = mStartOffset;
  765. if (mStartReason & WSType::block || mStartReason == WSType::br) {
  766. // set up mStartRun
  767. mStartRun->mType = WSType::leadingWS;
  768. mStartRun->mEndNode = mFirstNBSPNode;
  769. mStartRun->mEndOffset = mFirstNBSPOffset;
  770. mStartRun->mLeftType = mStartReason;
  771. mStartRun->mRightType = WSType::normalWS;
  772. // set up next run
  773. WSFragment *normalRun = new WSFragment();
  774. mStartRun->mRight = normalRun;
  775. normalRun->mType = WSType::normalWS;
  776. normalRun->mStartNode = mFirstNBSPNode;
  777. normalRun->mStartOffset = mFirstNBSPOffset;
  778. normalRun->mLeftType = WSType::leadingWS;
  779. normalRun->mLeft = mStartRun;
  780. if (mEndReason != WSType::block) {
  781. // then no trailing ws. this normal run ends the overall ws run.
  782. normalRun->mRightType = mEndReason;
  783. normalRun->mEndNode = mEndNode;
  784. normalRun->mEndOffset = mEndOffset;
  785. mEndRun = normalRun;
  786. } else {
  787. // we might have trailing ws.
  788. // it so happens that *if* there is an nbsp at end, {mEndNode,mEndOffset-1}
  789. // will point to it, even though in general start/end points not
  790. // guaranteed to be in text nodes.
  791. if (mLastNBSPNode == mEndNode && mLastNBSPOffset == mEndOffset - 1) {
  792. // normal ws runs right up to adjacent block (nbsp next to block)
  793. normalRun->mRightType = mEndReason;
  794. normalRun->mEndNode = mEndNode;
  795. normalRun->mEndOffset = mEndOffset;
  796. mEndRun = normalRun;
  797. } else {
  798. normalRun->mEndNode = mLastNBSPNode;
  799. normalRun->mEndOffset = mLastNBSPOffset+1;
  800. normalRun->mRightType = WSType::trailingWS;
  801. // set up next run
  802. WSFragment *lastRun = new WSFragment();
  803. lastRun->mType = WSType::trailingWS;
  804. lastRun->mStartNode = mLastNBSPNode;
  805. lastRun->mStartOffset = mLastNBSPOffset+1;
  806. lastRun->mEndNode = mEndNode;
  807. lastRun->mEndOffset = mEndOffset;
  808. lastRun->mLeftType = WSType::normalWS;
  809. lastRun->mLeft = normalRun;
  810. lastRun->mRightType = mEndReason;
  811. mEndRun = lastRun;
  812. normalRun->mRight = lastRun;
  813. }
  814. }
  815. } else {
  816. // mStartReason is not WSType::block or WSType::br; set up mStartRun
  817. mStartRun->mType = WSType::normalWS;
  818. mStartRun->mEndNode = mLastNBSPNode;
  819. mStartRun->mEndOffset = mLastNBSPOffset+1;
  820. mStartRun->mLeftType = mStartReason;
  821. // we might have trailing ws.
  822. // it so happens that *if* there is an nbsp at end, {mEndNode,mEndOffset-1}
  823. // will point to it, even though in general start/end points not
  824. // guaranteed to be in text nodes.
  825. if (mLastNBSPNode == mEndNode && mLastNBSPOffset == (mEndOffset - 1)) {
  826. mStartRun->mRightType = mEndReason;
  827. mStartRun->mEndNode = mEndNode;
  828. mStartRun->mEndOffset = mEndOffset;
  829. mEndRun = mStartRun;
  830. } else {
  831. // set up next run
  832. WSFragment *lastRun = new WSFragment();
  833. lastRun->mType = WSType::trailingWS;
  834. lastRun->mStartNode = mLastNBSPNode;
  835. lastRun->mStartOffset = mLastNBSPOffset+1;
  836. lastRun->mLeftType = WSType::normalWS;
  837. lastRun->mLeft = mStartRun;
  838. lastRun->mRightType = mEndReason;
  839. mEndRun = lastRun;
  840. mStartRun->mRight = lastRun;
  841. mStartRun->mRightType = WSType::trailingWS;
  842. }
  843. }
  844. }
  845. void
  846. WSRunObject::ClearRuns()
  847. {
  848. WSFragment *tmp, *run;
  849. run = mStartRun;
  850. while (run) {
  851. tmp = run->mRight;
  852. delete run;
  853. run = tmp;
  854. }
  855. mStartRun = 0;
  856. mEndRun = 0;
  857. }
  858. void
  859. WSRunObject::MakeSingleWSRun(WSType aType)
  860. {
  861. mStartRun = new WSFragment();
  862. mStartRun->mStartNode = mStartNode;
  863. mStartRun->mStartOffset = mStartOffset;
  864. mStartRun->mType = aType;
  865. mStartRun->mEndNode = mEndNode;
  866. mStartRun->mEndOffset = mEndOffset;
  867. mStartRun->mLeftType = mStartReason;
  868. mStartRun->mRightType = mEndReason;
  869. mEndRun = mStartRun;
  870. }
  871. nsIContent*
  872. WSRunObject::GetPreviousWSNodeInner(nsINode* aStartNode,
  873. nsINode* aBlockParent)
  874. {
  875. // Can't really recycle various getnext/prior routines because we have
  876. // special needs here. Need to step into inline containers but not block
  877. // containers.
  878. MOZ_ASSERT(aStartNode && aBlockParent);
  879. nsCOMPtr<nsIContent> priorNode = aStartNode->GetPreviousSibling();
  880. OwningNonNull<nsINode> curNode = *aStartNode;
  881. while (!priorNode) {
  882. // We have exhausted nodes in parent of aStartNode.
  883. nsCOMPtr<nsINode> curParent = curNode->GetParentNode();
  884. NS_ENSURE_TRUE(curParent, nullptr);
  885. if (curParent == aBlockParent) {
  886. // We have exhausted nodes in the block parent. The convention here is
  887. // to return null.
  888. return nullptr;
  889. }
  890. // We have a parent: look for previous sibling
  891. priorNode = curParent->GetPreviousSibling();
  892. curNode = curParent;
  893. }
  894. // We have a prior node. If it's a block, return it.
  895. if (IsBlockNode(priorNode)) {
  896. return priorNode;
  897. }
  898. if (mHTMLEditor->IsContainer(priorNode)) {
  899. // Else if it's a container, get deep rightmost child
  900. nsCOMPtr<nsIContent> child = mHTMLEditor->GetRightmostChild(priorNode);
  901. if (child) {
  902. return child;
  903. }
  904. }
  905. // Else return the node itself
  906. return priorNode;
  907. }
  908. nsIContent*
  909. WSRunObject::GetPreviousWSNode(EditorDOMPoint aPoint,
  910. nsINode* aBlockParent)
  911. {
  912. // Can't really recycle various getnext/prior routines because we
  913. // have special needs here. Need to step into inline containers but
  914. // not block containers.
  915. MOZ_ASSERT(aPoint.node && aBlockParent);
  916. if (aPoint.node->NodeType() == nsIDOMNode::TEXT_NODE) {
  917. return GetPreviousWSNodeInner(aPoint.node, aBlockParent);
  918. }
  919. if (!mHTMLEditor->IsContainer(aPoint.node)) {
  920. return GetPreviousWSNodeInner(aPoint.node, aBlockParent);
  921. }
  922. if (!aPoint.offset) {
  923. if (aPoint.node == aBlockParent) {
  924. // We are at start of the block.
  925. return nullptr;
  926. }
  927. // We are at start of non-block container
  928. return GetPreviousWSNodeInner(aPoint.node, aBlockParent);
  929. }
  930. nsCOMPtr<nsIContent> startContent = do_QueryInterface(aPoint.node);
  931. NS_ENSURE_TRUE(startContent, nullptr);
  932. nsCOMPtr<nsIContent> priorNode = startContent->GetChildAt(aPoint.offset - 1);
  933. NS_ENSURE_TRUE(priorNode, nullptr);
  934. // We have a prior node. If it's a block, return it.
  935. if (IsBlockNode(priorNode)) {
  936. return priorNode;
  937. }
  938. if (mHTMLEditor->IsContainer(priorNode)) {
  939. // Else if it's a container, get deep rightmost child
  940. nsCOMPtr<nsIContent> child = mHTMLEditor->GetRightmostChild(priorNode);
  941. if (child) {
  942. return child;
  943. }
  944. }
  945. // Else return the node itself
  946. return priorNode;
  947. }
  948. nsIContent*
  949. WSRunObject::GetNextWSNodeInner(nsINode* aStartNode,
  950. nsINode* aBlockParent)
  951. {
  952. // Can't really recycle various getnext/prior routines because we have
  953. // special needs here. Need to step into inline containers but not block
  954. // containers.
  955. MOZ_ASSERT(aStartNode && aBlockParent);
  956. nsCOMPtr<nsIContent> nextNode = aStartNode->GetNextSibling();
  957. nsCOMPtr<nsINode> curNode = aStartNode;
  958. while (!nextNode) {
  959. // We have exhausted nodes in parent of aStartNode.
  960. nsCOMPtr<nsINode> curParent = curNode->GetParentNode();
  961. NS_ENSURE_TRUE(curParent, nullptr);
  962. if (curParent == aBlockParent) {
  963. // We have exhausted nodes in the block parent. The convention here is
  964. // to return null.
  965. return nullptr;
  966. }
  967. // We have a parent: look for next sibling
  968. nextNode = curParent->GetNextSibling();
  969. curNode = curParent;
  970. }
  971. // We have a next node. If it's a block, return it.
  972. if (IsBlockNode(nextNode)) {
  973. return nextNode;
  974. }
  975. if (mHTMLEditor->IsContainer(nextNode)) {
  976. // Else if it's a container, get deep leftmost child
  977. nsCOMPtr<nsIContent> child = mHTMLEditor->GetLeftmostChild(nextNode);
  978. if (child) {
  979. return child;
  980. }
  981. }
  982. // Else return the node itself
  983. return nextNode;
  984. }
  985. nsIContent*
  986. WSRunObject::GetNextWSNode(EditorDOMPoint aPoint,
  987. nsINode* aBlockParent)
  988. {
  989. // Can't really recycle various getnext/prior routines because we have
  990. // special needs here. Need to step into inline containers but not block
  991. // containers.
  992. MOZ_ASSERT(aPoint.node && aBlockParent);
  993. if (aPoint.node->NodeType() == nsIDOMNode::TEXT_NODE) {
  994. return GetNextWSNodeInner(aPoint.node, aBlockParent);
  995. }
  996. if (!mHTMLEditor->IsContainer(aPoint.node)) {
  997. return GetNextWSNodeInner(aPoint.node, aBlockParent);
  998. }
  999. nsCOMPtr<nsIContent> startContent = do_QueryInterface(aPoint.node);
  1000. NS_ENSURE_TRUE(startContent, nullptr);
  1001. nsCOMPtr<nsIContent> nextNode = startContent->GetChildAt(aPoint.offset);
  1002. if (!nextNode) {
  1003. if (aPoint.node == aBlockParent) {
  1004. // We are at end of the block.
  1005. return nullptr;
  1006. }
  1007. // We are at end of non-block container
  1008. return GetNextWSNodeInner(aPoint.node, aBlockParent);
  1009. }
  1010. // We have a next node. If it's a block, return it.
  1011. if (IsBlockNode(nextNode)) {
  1012. return nextNode;
  1013. }
  1014. if (mHTMLEditor->IsContainer(nextNode)) {
  1015. // else if it's a container, get deep leftmost child
  1016. nsCOMPtr<nsIContent> child = mHTMLEditor->GetLeftmostChild(nextNode);
  1017. if (child) {
  1018. return child;
  1019. }
  1020. }
  1021. // Else return the node itself
  1022. return nextNode;
  1023. }
  1024. nsresult
  1025. WSRunObject::PrepareToDeleteRangePriv(WSRunObject* aEndObject)
  1026. {
  1027. // this routine adjust whitespace before *this* and after aEndObject
  1028. // in preperation for the two areas to become adjacent after the
  1029. // intervening content is deleted. It's overly agressive right
  1030. // now. There might be a block boundary remaining between them after
  1031. // the deletion, in which case these adjstments are unneeded (though
  1032. // I don't think they can ever be harmful?)
  1033. NS_ENSURE_TRUE(aEndObject, NS_ERROR_NULL_POINTER);
  1034. // get the runs before and after selection
  1035. WSFragment *beforeRun, *afterRun;
  1036. FindRun(mNode, mOffset, &beforeRun, false);
  1037. aEndObject->FindRun(aEndObject->mNode, aEndObject->mOffset, &afterRun, true);
  1038. // trim after run of any leading ws
  1039. if (afterRun && (afterRun->mType & WSType::leadingWS)) {
  1040. nsresult rv =
  1041. aEndObject->DeleteChars(aEndObject->mNode, aEndObject->mOffset,
  1042. afterRun->mEndNode, afterRun->mEndOffset,
  1043. eOutsideUserSelectAll);
  1044. NS_ENSURE_SUCCESS(rv, rv);
  1045. }
  1046. // adjust normal ws in afterRun if needed
  1047. if (afterRun && afterRun->mType == WSType::normalWS && !aEndObject->mPRE) {
  1048. if ((beforeRun && (beforeRun->mType & WSType::leadingWS)) ||
  1049. (!beforeRun && ((mStartReason & WSType::block) ||
  1050. mStartReason == WSType::br))) {
  1051. // make sure leading char of following ws is an nbsp, so that it will show up
  1052. WSPoint point = aEndObject->GetCharAfter(aEndObject->mNode,
  1053. aEndObject->mOffset);
  1054. if (point.mTextNode && nsCRT::IsAsciiSpace(point.mChar)) {
  1055. nsresult rv = aEndObject->ConvertToNBSP(point, eOutsideUserSelectAll);
  1056. NS_ENSURE_SUCCESS(rv, rv);
  1057. }
  1058. }
  1059. }
  1060. // trim before run of any trailing ws
  1061. if (beforeRun && (beforeRun->mType & WSType::trailingWS)) {
  1062. nsresult rv = DeleteChars(beforeRun->mStartNode, beforeRun->mStartOffset,
  1063. mNode, mOffset, eOutsideUserSelectAll);
  1064. NS_ENSURE_SUCCESS(rv, rv);
  1065. } else if (beforeRun && beforeRun->mType == WSType::normalWS && !mPRE) {
  1066. if ((afterRun && (afterRun->mType & WSType::trailingWS)) ||
  1067. (afterRun && afterRun->mType == WSType::normalWS) ||
  1068. (!afterRun && (aEndObject->mEndReason & WSType::block))) {
  1069. // make sure trailing char of starting ws is an nbsp, so that it will show up
  1070. WSPoint point = GetCharBefore(mNode, mOffset);
  1071. if (point.mTextNode && nsCRT::IsAsciiSpace(point.mChar)) {
  1072. RefPtr<Text> wsStartNode, wsEndNode;
  1073. int32_t wsStartOffset, wsEndOffset;
  1074. GetAsciiWSBounds(eBoth, mNode, mOffset,
  1075. getter_AddRefs(wsStartNode), &wsStartOffset,
  1076. getter_AddRefs(wsEndNode), &wsEndOffset);
  1077. point.mTextNode = wsStartNode;
  1078. point.mOffset = wsStartOffset;
  1079. nsresult rv = ConvertToNBSP(point, eOutsideUserSelectAll);
  1080. NS_ENSURE_SUCCESS(rv, rv);
  1081. }
  1082. }
  1083. }
  1084. return NS_OK;
  1085. }
  1086. nsresult
  1087. WSRunObject::PrepareToSplitAcrossBlocksPriv()
  1088. {
  1089. // used to prepare ws to be split across two blocks. The main issue
  1090. // here is make sure normalWS doesn't end up becoming non-significant
  1091. // leading or trailing ws after the split.
  1092. // get the runs before and after selection
  1093. WSFragment *beforeRun, *afterRun;
  1094. FindRun(mNode, mOffset, &beforeRun, false);
  1095. FindRun(mNode, mOffset, &afterRun, true);
  1096. // adjust normal ws in afterRun if needed
  1097. if (afterRun && afterRun->mType == WSType::normalWS) {
  1098. // make sure leading char of following ws is an nbsp, so that it will show up
  1099. WSPoint point = GetCharAfter(mNode, mOffset);
  1100. if (point.mTextNode && nsCRT::IsAsciiSpace(point.mChar)) {
  1101. nsresult rv = ConvertToNBSP(point);
  1102. NS_ENSURE_SUCCESS(rv, rv);
  1103. }
  1104. }
  1105. // adjust normal ws in beforeRun if needed
  1106. if (beforeRun && beforeRun->mType == WSType::normalWS) {
  1107. // make sure trailing char of starting ws is an nbsp, so that it will show up
  1108. WSPoint point = GetCharBefore(mNode, mOffset);
  1109. if (point.mTextNode && nsCRT::IsAsciiSpace(point.mChar)) {
  1110. RefPtr<Text> wsStartNode, wsEndNode;
  1111. int32_t wsStartOffset, wsEndOffset;
  1112. GetAsciiWSBounds(eBoth, mNode, mOffset,
  1113. getter_AddRefs(wsStartNode), &wsStartOffset,
  1114. getter_AddRefs(wsEndNode), &wsEndOffset);
  1115. point.mTextNode = wsStartNode;
  1116. point.mOffset = wsStartOffset;
  1117. nsresult rv = ConvertToNBSP(point);
  1118. NS_ENSURE_SUCCESS(rv, rv);
  1119. }
  1120. }
  1121. return NS_OK;
  1122. }
  1123. nsresult
  1124. WSRunObject::DeleteChars(nsINode* aStartNode,
  1125. int32_t aStartOffset,
  1126. nsINode* aEndNode,
  1127. int32_t aEndOffset,
  1128. AreaRestriction aAR)
  1129. {
  1130. // MOOSE: this routine needs to be modified to preserve the integrity of the
  1131. // wsFragment info.
  1132. NS_ENSURE_TRUE(aStartNode && aEndNode, NS_ERROR_NULL_POINTER);
  1133. if (aAR == eOutsideUserSelectAll) {
  1134. nsCOMPtr<nsIDOMNode> san =
  1135. mHTMLEditor->FindUserSelectAllNode(GetAsDOMNode(aStartNode));
  1136. if (san) {
  1137. return NS_OK;
  1138. }
  1139. if (aStartNode != aEndNode) {
  1140. san = mHTMLEditor->FindUserSelectAllNode(GetAsDOMNode(aEndNode));
  1141. if (san) {
  1142. return NS_OK;
  1143. }
  1144. }
  1145. }
  1146. if (aStartNode == aEndNode && aStartOffset == aEndOffset) {
  1147. // Nothing to delete
  1148. return NS_OK;
  1149. }
  1150. int32_t idx = mNodeArray.IndexOf(aStartNode);
  1151. if (idx == -1) {
  1152. // If our strarting point wasn't one of our ws text nodes, then just go
  1153. // through them from the beginning.
  1154. idx = 0;
  1155. }
  1156. if (aStartNode == aEndNode && aStartNode->GetAsText()) {
  1157. return mHTMLEditor->DeleteText(*aStartNode->GetAsText(),
  1158. static_cast<uint32_t>(aStartOffset),
  1159. static_cast<uint32_t>(aEndOffset - aStartOffset));
  1160. }
  1161. RefPtr<nsRange> range;
  1162. int32_t count = mNodeArray.Length();
  1163. for (; idx < count; idx++) {
  1164. RefPtr<Text> node = mNodeArray[idx];
  1165. if (!node) {
  1166. // We ran out of ws nodes; must have been deleting to end
  1167. return NS_OK;
  1168. }
  1169. if (node == aStartNode) {
  1170. uint32_t len = node->Length();
  1171. if (uint32_t(aStartOffset) < len) {
  1172. nsresult rv =
  1173. mHTMLEditor->DeleteText(*node, AssertedCast<uint32_t>(aStartOffset),
  1174. len - aStartOffset);
  1175. NS_ENSURE_SUCCESS(rv, rv);
  1176. }
  1177. } else if (node == aEndNode) {
  1178. if (aEndOffset) {
  1179. nsresult rv =
  1180. mHTMLEditor->DeleteText(*node, 0, AssertedCast<uint32_t>(aEndOffset));
  1181. NS_ENSURE_SUCCESS(rv, rv);
  1182. }
  1183. break;
  1184. } else {
  1185. if (!range) {
  1186. range = new nsRange(aStartNode);
  1187. nsresult rv =
  1188. range->SetStartAndEnd(aStartNode, aStartOffset, aEndNode, aEndOffset);
  1189. NS_ENSURE_SUCCESS(rv, rv);
  1190. }
  1191. bool nodeBefore, nodeAfter;
  1192. nsresult rv =
  1193. nsRange::CompareNodeToRange(node, range, &nodeBefore, &nodeAfter);
  1194. NS_ENSURE_SUCCESS(rv, rv);
  1195. if (nodeAfter) {
  1196. break;
  1197. }
  1198. if (!nodeBefore) {
  1199. rv = mHTMLEditor->DeleteNode(node);
  1200. NS_ENSURE_SUCCESS(rv, rv);
  1201. mNodeArray.RemoveElement(node);
  1202. --count;
  1203. --idx;
  1204. }
  1205. }
  1206. }
  1207. return NS_OK;
  1208. }
  1209. WSRunObject::WSPoint
  1210. WSRunObject::GetCharAfter(nsINode* aNode,
  1211. int32_t aOffset)
  1212. {
  1213. MOZ_ASSERT(aNode);
  1214. int32_t idx = mNodeArray.IndexOf(aNode);
  1215. if (idx == -1) {
  1216. // Use range comparisons to get right ws node
  1217. return GetWSPointAfter(aNode, aOffset);
  1218. }
  1219. // Use WSPoint version of GetCharAfter()
  1220. return GetCharAfter(WSPoint(mNodeArray[idx], aOffset, 0));
  1221. }
  1222. WSRunObject::WSPoint
  1223. WSRunObject::GetCharBefore(nsINode* aNode,
  1224. int32_t aOffset)
  1225. {
  1226. MOZ_ASSERT(aNode);
  1227. int32_t idx = mNodeArray.IndexOf(aNode);
  1228. if (idx == -1) {
  1229. // Use range comparisons to get right ws node
  1230. return GetWSPointBefore(aNode, aOffset);
  1231. }
  1232. // Use WSPoint version of GetCharBefore()
  1233. return GetCharBefore(WSPoint(mNodeArray[idx], aOffset, 0));
  1234. }
  1235. WSRunObject::WSPoint
  1236. WSRunObject::GetCharAfter(const WSPoint &aPoint)
  1237. {
  1238. MOZ_ASSERT(aPoint.mTextNode);
  1239. WSPoint outPoint;
  1240. outPoint.mTextNode = nullptr;
  1241. outPoint.mOffset = 0;
  1242. outPoint.mChar = 0;
  1243. int32_t idx = mNodeArray.IndexOf(aPoint.mTextNode);
  1244. if (idx == -1) {
  1245. // Can't find point, but it's not an error
  1246. return outPoint;
  1247. }
  1248. if (static_cast<uint16_t>(aPoint.mOffset) < aPoint.mTextNode->TextLength()) {
  1249. outPoint = aPoint;
  1250. outPoint.mChar = GetCharAt(aPoint.mTextNode, aPoint.mOffset);
  1251. return outPoint;
  1252. }
  1253. int32_t numNodes = mNodeArray.Length();
  1254. if (idx + 1 < numNodes) {
  1255. outPoint.mTextNode = mNodeArray[idx + 1];
  1256. MOZ_ASSERT(outPoint.mTextNode);
  1257. outPoint.mOffset = 0;
  1258. outPoint.mChar = GetCharAt(outPoint.mTextNode, 0);
  1259. }
  1260. return outPoint;
  1261. }
  1262. WSRunObject::WSPoint
  1263. WSRunObject::GetCharBefore(const WSPoint &aPoint)
  1264. {
  1265. MOZ_ASSERT(aPoint.mTextNode);
  1266. WSPoint outPoint;
  1267. outPoint.mTextNode = nullptr;
  1268. outPoint.mOffset = 0;
  1269. outPoint.mChar = 0;
  1270. int32_t idx = mNodeArray.IndexOf(aPoint.mTextNode);
  1271. if (idx == -1) {
  1272. // Can't find point, but it's not an error
  1273. return outPoint;
  1274. }
  1275. if (aPoint.mOffset) {
  1276. outPoint = aPoint;
  1277. outPoint.mOffset--;
  1278. outPoint.mChar = GetCharAt(aPoint.mTextNode, aPoint.mOffset - 1);
  1279. return outPoint;
  1280. }
  1281. if (idx) {
  1282. outPoint.mTextNode = mNodeArray[idx - 1];
  1283. uint32_t len = outPoint.mTextNode->TextLength();
  1284. if (len) {
  1285. outPoint.mOffset = len - 1;
  1286. outPoint.mChar = GetCharAt(outPoint.mTextNode, len - 1);
  1287. }
  1288. }
  1289. return outPoint;
  1290. }
  1291. nsresult
  1292. WSRunObject::ConvertToNBSP(WSPoint aPoint, AreaRestriction aAR)
  1293. {
  1294. // MOOSE: this routine needs to be modified to preserve the integrity of the
  1295. // wsFragment info.
  1296. NS_ENSURE_TRUE(aPoint.mTextNode, NS_ERROR_NULL_POINTER);
  1297. if (aAR == eOutsideUserSelectAll) {
  1298. nsCOMPtr<nsIDOMNode> san =
  1299. mHTMLEditor->FindUserSelectAllNode(GetAsDOMNode(aPoint.mTextNode));
  1300. if (san) {
  1301. return NS_OK;
  1302. }
  1303. }
  1304. // First, insert an nbsp
  1305. AutoTransactionsConserveSelection dontSpazMySelection(mHTMLEditor);
  1306. nsAutoString nbspStr(nbsp);
  1307. nsresult rv =
  1308. mHTMLEditor->InsertTextIntoTextNodeImpl(nbspStr, *aPoint.mTextNode,
  1309. aPoint.mOffset, true);
  1310. NS_ENSURE_SUCCESS(rv, rv);
  1311. // Next, find range of ws it will replace
  1312. RefPtr<Text> startNode, endNode;
  1313. int32_t startOffset = 0, endOffset = 0;
  1314. GetAsciiWSBounds(eAfter, aPoint.mTextNode, aPoint.mOffset + 1,
  1315. getter_AddRefs(startNode), &startOffset,
  1316. getter_AddRefs(endNode), &endOffset);
  1317. // Finally, delete that replaced ws, if any
  1318. if (startNode) {
  1319. rv = DeleteChars(startNode, startOffset, endNode, endOffset);
  1320. NS_ENSURE_SUCCESS(rv, rv);
  1321. }
  1322. return NS_OK;
  1323. }
  1324. void
  1325. WSRunObject::GetAsciiWSBounds(int16_t aDir,
  1326. nsINode* aNode,
  1327. int32_t aOffset,
  1328. Text** outStartNode,
  1329. int32_t* outStartOffset,
  1330. Text** outEndNode,
  1331. int32_t* outEndOffset)
  1332. {
  1333. MOZ_ASSERT(aNode && outStartNode && outStartOffset && outEndNode &&
  1334. outEndOffset);
  1335. RefPtr<Text> startNode, endNode;
  1336. int32_t startOffset = 0, endOffset = 0;
  1337. if (aDir & eAfter) {
  1338. WSPoint point = GetCharAfter(aNode, aOffset);
  1339. if (point.mTextNode) {
  1340. // We found a text node, at least
  1341. startNode = endNode = point.mTextNode;
  1342. startOffset = endOffset = point.mOffset;
  1343. // Scan ahead to end of ASCII ws
  1344. for (; nsCRT::IsAsciiSpace(point.mChar) && point.mTextNode;
  1345. point = GetCharAfter(point)) {
  1346. endNode = point.mTextNode;
  1347. // endOffset is _after_ ws
  1348. point.mOffset++;
  1349. endOffset = point.mOffset;
  1350. }
  1351. }
  1352. }
  1353. if (aDir & eBefore) {
  1354. WSPoint point = GetCharBefore(aNode, aOffset);
  1355. if (point.mTextNode) {
  1356. // We found a text node, at least
  1357. startNode = point.mTextNode;
  1358. startOffset = point.mOffset + 1;
  1359. if (!endNode) {
  1360. endNode = startNode;
  1361. endOffset = startOffset;
  1362. }
  1363. // Scan back to start of ASCII ws
  1364. for (; nsCRT::IsAsciiSpace(point.mChar) && point.mTextNode;
  1365. point = GetCharBefore(point)) {
  1366. startNode = point.mTextNode;
  1367. startOffset = point.mOffset;
  1368. }
  1369. }
  1370. }
  1371. startNode.forget(outStartNode);
  1372. *outStartOffset = startOffset;
  1373. endNode.forget(outEndNode);
  1374. *outEndOffset = endOffset;
  1375. }
  1376. /**
  1377. * Given a dompoint, find the ws run that is before or after it, as caller
  1378. * needs
  1379. */
  1380. void
  1381. WSRunObject::FindRun(nsINode* aNode,
  1382. int32_t aOffset,
  1383. WSFragment** outRun,
  1384. bool after)
  1385. {
  1386. MOZ_ASSERT(aNode && outRun);
  1387. *outRun = nullptr;
  1388. for (WSFragment* run = mStartRun; run; run = run->mRight) {
  1389. int32_t comp = run->mStartNode ? nsContentUtils::ComparePoints(aNode,
  1390. aOffset, run->mStartNode, run->mStartOffset) : -1;
  1391. if (comp <= 0) {
  1392. if (after) {
  1393. *outRun = run;
  1394. } else {
  1395. // before
  1396. *outRun = nullptr;
  1397. }
  1398. return;
  1399. }
  1400. comp = run->mEndNode ? nsContentUtils::ComparePoints(aNode, aOffset,
  1401. run->mEndNode, run->mEndOffset) : -1;
  1402. if (comp < 0) {
  1403. *outRun = run;
  1404. return;
  1405. } else if (!comp) {
  1406. if (after) {
  1407. *outRun = run->mRight;
  1408. } else {
  1409. // before
  1410. *outRun = run;
  1411. }
  1412. return;
  1413. }
  1414. if (!run->mRight) {
  1415. if (after) {
  1416. *outRun = nullptr;
  1417. } else {
  1418. // before
  1419. *outRun = run;
  1420. }
  1421. return;
  1422. }
  1423. }
  1424. }
  1425. char16_t
  1426. WSRunObject::GetCharAt(Text* aTextNode,
  1427. int32_t aOffset)
  1428. {
  1429. // return 0 if we can't get a char, for whatever reason
  1430. NS_ENSURE_TRUE(aTextNode, 0);
  1431. int32_t len = int32_t(aTextNode->TextLength());
  1432. if (aOffset < 0 || aOffset >= len) {
  1433. return 0;
  1434. }
  1435. return aTextNode->GetText()->CharAt(aOffset);
  1436. }
  1437. WSRunObject::WSPoint
  1438. WSRunObject::GetWSPointAfter(nsINode* aNode,
  1439. int32_t aOffset)
  1440. {
  1441. // Note: only to be called if aNode is not a ws node.
  1442. // Binary search on wsnodes
  1443. uint32_t numNodes = mNodeArray.Length();
  1444. if (!numNodes) {
  1445. // Do nothing if there are no nodes to search
  1446. WSPoint outPoint;
  1447. return outPoint;
  1448. }
  1449. uint32_t firstNum = 0, curNum = numNodes/2, lastNum = numNodes;
  1450. int16_t cmp = 0;
  1451. RefPtr<Text> curNode;
  1452. // Begin binary search. We do this because we need to minimize calls to
  1453. // ComparePoints(), which is expensive.
  1454. while (curNum != lastNum) {
  1455. curNode = mNodeArray[curNum];
  1456. cmp = nsContentUtils::ComparePoints(aNode, aOffset, curNode, 0);
  1457. if (cmp < 0) {
  1458. lastNum = curNum;
  1459. } else {
  1460. firstNum = curNum + 1;
  1461. }
  1462. curNum = (lastNum - firstNum)/2 + firstNum;
  1463. MOZ_ASSERT(firstNum <= curNum && curNum <= lastNum, "Bad binary search");
  1464. }
  1465. // When the binary search is complete, we always know that the current node
  1466. // is the same as the end node, which is always past our range. Therefore,
  1467. // we've found the node immediately after the point of interest.
  1468. if (curNum == mNodeArray.Length()) {
  1469. // hey asked for past our range (it's after the last node). GetCharAfter
  1470. // will do the work for us when we pass it the last index of the last node.
  1471. RefPtr<Text> textNode(mNodeArray[curNum - 1]);
  1472. WSPoint point(textNode, textNode->TextLength(), 0);
  1473. return GetCharAfter(point);
  1474. } else {
  1475. // The char after the point is the first character of our range.
  1476. RefPtr<Text> textNode(mNodeArray[curNum]);
  1477. WSPoint point(textNode, 0, 0);
  1478. return GetCharAfter(point);
  1479. }
  1480. }
  1481. WSRunObject::WSPoint
  1482. WSRunObject::GetWSPointBefore(nsINode* aNode,
  1483. int32_t aOffset)
  1484. {
  1485. // Note: only to be called if aNode is not a ws node.
  1486. // Binary search on wsnodes
  1487. uint32_t numNodes = mNodeArray.Length();
  1488. if (!numNodes) {
  1489. // Do nothing if there are no nodes to search
  1490. WSPoint outPoint;
  1491. return outPoint;
  1492. }
  1493. uint32_t firstNum = 0, curNum = numNodes/2, lastNum = numNodes;
  1494. int16_t cmp = 0;
  1495. RefPtr<Text> curNode;
  1496. // Begin binary search. We do this because we need to minimize calls to
  1497. // ComparePoints(), which is expensive.
  1498. while (curNum != lastNum) {
  1499. curNode = mNodeArray[curNum];
  1500. cmp = nsContentUtils::ComparePoints(aNode, aOffset, curNode, 0);
  1501. if (cmp < 0) {
  1502. lastNum = curNum;
  1503. } else {
  1504. firstNum = curNum + 1;
  1505. }
  1506. curNum = (lastNum - firstNum)/2 + firstNum;
  1507. MOZ_ASSERT(firstNum <= curNum && curNum <= lastNum, "Bad binary search");
  1508. }
  1509. // When the binary search is complete, we always know that the current node
  1510. // is the same as the end node, which is always past our range. Therefore,
  1511. // we've found the node immediately after the point of interest.
  1512. if (curNum == mNodeArray.Length()) {
  1513. // Get the point before the end of the last node, we can pass the length of
  1514. // the node into GetCharBefore, and it will return the last character.
  1515. RefPtr<Text> textNode(mNodeArray[curNum - 1]);
  1516. WSPoint point(textNode, textNode->TextLength(), 0);
  1517. return GetCharBefore(point);
  1518. } else {
  1519. // We can just ask the current node for the point immediately before it,
  1520. // it will handle moving to the previous node (if any) and returning the
  1521. // appropriate character
  1522. RefPtr<Text> textNode(mNodeArray[curNum]);
  1523. WSPoint point(textNode, 0, 0);
  1524. return GetCharBefore(point);
  1525. }
  1526. }
  1527. nsresult
  1528. WSRunObject::CheckTrailingNBSPOfRun(WSFragment *aRun)
  1529. {
  1530. // Try to change an nbsp to a space, if possible, just to prevent nbsp
  1531. // proliferation. Examine what is before and after the trailing nbsp, if
  1532. // any.
  1533. NS_ENSURE_TRUE(aRun, NS_ERROR_NULL_POINTER);
  1534. bool leftCheck = false;
  1535. bool spaceNBSP = false;
  1536. bool rightCheck = false;
  1537. // confirm run is normalWS
  1538. if (aRun->mType != WSType::normalWS) {
  1539. return NS_ERROR_FAILURE;
  1540. }
  1541. // first check for trailing nbsp
  1542. WSPoint thePoint = GetCharBefore(aRun->mEndNode, aRun->mEndOffset);
  1543. if (thePoint.mTextNode && thePoint.mChar == nbsp) {
  1544. // now check that what is to the left of it is compatible with replacing nbsp with space
  1545. WSPoint prevPoint = GetCharBefore(thePoint);
  1546. if (prevPoint.mTextNode) {
  1547. if (!nsCRT::IsAsciiSpace(prevPoint.mChar)) {
  1548. leftCheck = true;
  1549. } else {
  1550. spaceNBSP = true;
  1551. }
  1552. } else if (aRun->mLeftType == WSType::text ||
  1553. aRun->mLeftType == WSType::special) {
  1554. leftCheck = true;
  1555. }
  1556. if (leftCheck || spaceNBSP) {
  1557. // now check that what is to the right of it is compatible with replacing
  1558. // nbsp with space
  1559. if (aRun->mRightType == WSType::text ||
  1560. aRun->mRightType == WSType::special ||
  1561. aRun->mRightType == WSType::br) {
  1562. rightCheck = true;
  1563. }
  1564. if ((aRun->mRightType & WSType::block) &&
  1565. IsBlockNode(GetWSBoundingParent())) {
  1566. // We are at a block boundary. Insert a <br>. Why? Well, first note
  1567. // that the br will have no visible effect since it is up against a
  1568. // block boundary. |foo<br><p>bar| renders like |foo<p>bar| and
  1569. // similarly |<p>foo<br></p>bar| renders like |<p>foo</p>bar|. What
  1570. // this <br> addition gets us is the ability to convert a trailing nbsp
  1571. // to a space. Consider: |<body>foo. '</body>|, where ' represents
  1572. // selection. User types space attempting to put 2 spaces after the
  1573. // end of their sentence. We used to do this as: |<body>foo.
  1574. // &nbsp</body>| This caused problems with soft wrapping: the nbsp
  1575. // would wrap to the next line, which looked attrocious. If you try to
  1576. // do: |<body>foo.&nbsp </body>| instead, the trailing space is
  1577. // invisible because it is against a block boundary. If you do:
  1578. // |<body>foo.&nbsp&nbsp</body>| then you get an even uglier soft
  1579. // wrapping problem, where foo is on one line until you type the final
  1580. // space, and then "foo " jumps down to the next line. Ugh. The best
  1581. // way I can find out of this is to throw in a harmless <br> here,
  1582. // which allows us to do: |<body>foo.&nbsp <br></body>|, which doesn't
  1583. // cause foo to jump lines, doesn't cause spaces to show up at the
  1584. // beginning of soft wrapped lines, and lets the user see 2 spaces when
  1585. // they type 2 spaces.
  1586. nsCOMPtr<Element> brNode =
  1587. mHTMLEditor->CreateBR(aRun->mEndNode, aRun->mEndOffset);
  1588. NS_ENSURE_TRUE(brNode, NS_ERROR_FAILURE);
  1589. // Refresh thePoint, prevPoint
  1590. thePoint = GetCharBefore(aRun->mEndNode, aRun->mEndOffset);
  1591. prevPoint = GetCharBefore(thePoint);
  1592. rightCheck = true;
  1593. }
  1594. }
  1595. if (leftCheck && rightCheck) {
  1596. // Now replace nbsp with space. First, insert a space
  1597. AutoTransactionsConserveSelection dontSpazMySelection(mHTMLEditor);
  1598. nsAutoString spaceStr(char16_t(32));
  1599. nsresult rv =
  1600. mHTMLEditor->InsertTextIntoTextNodeImpl(spaceStr, *thePoint.mTextNode,
  1601. thePoint.mOffset, true);
  1602. NS_ENSURE_SUCCESS(rv, rv);
  1603. // Finally, delete that nbsp
  1604. rv = DeleteChars(thePoint.mTextNode, thePoint.mOffset + 1,
  1605. thePoint.mTextNode, thePoint.mOffset + 2);
  1606. NS_ENSURE_SUCCESS(rv, rv);
  1607. } else if (!mPRE && spaceNBSP && rightCheck) {
  1608. // Don't mess with this preformatted for now. We have a run of ASCII
  1609. // whitespace (which will render as one space) followed by an nbsp (which
  1610. // is at the end of the whitespace run). Let's switch their order. This
  1611. // will ensure that if someone types two spaces after a sentence, and the
  1612. // editor softwraps at this point, the spaces won't be split across lines,
  1613. // which looks ugly and is bad for the moose.
  1614. RefPtr<Text> startNode, endNode;
  1615. int32_t startOffset, endOffset;
  1616. GetAsciiWSBounds(eBoth, prevPoint.mTextNode, prevPoint.mOffset + 1,
  1617. getter_AddRefs(startNode), &startOffset,
  1618. getter_AddRefs(endNode), &endOffset);
  1619. // Delete that nbsp
  1620. nsresult rv = DeleteChars(thePoint.mTextNode, thePoint.mOffset,
  1621. thePoint.mTextNode, thePoint.mOffset + 1);
  1622. NS_ENSURE_SUCCESS(rv, rv);
  1623. // Finally, insert that nbsp before the ASCII ws run
  1624. AutoTransactionsConserveSelection dontSpazMySelection(mHTMLEditor);
  1625. nsAutoString nbspStr(nbsp);
  1626. rv = mHTMLEditor->InsertTextIntoTextNodeImpl(nbspStr, *startNode,
  1627. startOffset, true);
  1628. NS_ENSURE_SUCCESS(rv, rv);
  1629. }
  1630. }
  1631. return NS_OK;
  1632. }
  1633. nsresult
  1634. WSRunObject::CheckTrailingNBSP(WSFragment* aRun,
  1635. nsINode* aNode,
  1636. int32_t aOffset)
  1637. {
  1638. // Try to change an nbsp to a space, if possible, just to prevent nbsp
  1639. // proliferation. This routine is called when we are about to make this
  1640. // point in the ws abut an inserted break or text, so we don't have to worry
  1641. // about what is after it. What is after it now will end up after the
  1642. // inserted object.
  1643. NS_ENSURE_TRUE(aRun && aNode, NS_ERROR_NULL_POINTER);
  1644. bool canConvert = false;
  1645. WSPoint thePoint = GetCharBefore(aNode, aOffset);
  1646. if (thePoint.mTextNode && thePoint.mChar == nbsp) {
  1647. WSPoint prevPoint = GetCharBefore(thePoint);
  1648. if (prevPoint.mTextNode) {
  1649. if (!nsCRT::IsAsciiSpace(prevPoint.mChar)) {
  1650. canConvert = true;
  1651. }
  1652. } else if (aRun->mLeftType == WSType::text ||
  1653. aRun->mLeftType == WSType::special) {
  1654. canConvert = true;
  1655. }
  1656. }
  1657. if (canConvert) {
  1658. // First, insert a space
  1659. AutoTransactionsConserveSelection dontSpazMySelection(mHTMLEditor);
  1660. nsAutoString spaceStr(char16_t(32));
  1661. nsresult rv =
  1662. mHTMLEditor->InsertTextIntoTextNodeImpl(spaceStr, *thePoint.mTextNode,
  1663. thePoint.mOffset, true);
  1664. NS_ENSURE_SUCCESS(rv, rv);
  1665. // Finally, delete that nbsp
  1666. rv = DeleteChars(thePoint.mTextNode, thePoint.mOffset + 1,
  1667. thePoint.mTextNode, thePoint.mOffset + 2);
  1668. NS_ENSURE_SUCCESS(rv, rv);
  1669. }
  1670. return NS_OK;
  1671. }
  1672. nsresult
  1673. WSRunObject::CheckLeadingNBSP(WSFragment* aRun,
  1674. nsINode* aNode,
  1675. int32_t aOffset)
  1676. {
  1677. // Try to change an nbsp to a space, if possible, just to prevent nbsp
  1678. // proliferation This routine is called when we are about to make this point
  1679. // in the ws abut an inserted text, so we don't have to worry about what is
  1680. // before it. What is before it now will end up before the inserted text.
  1681. bool canConvert = false;
  1682. WSPoint thePoint = GetCharAfter(aNode, aOffset);
  1683. if (thePoint.mChar == nbsp) {
  1684. WSPoint tmp = thePoint;
  1685. // we want to be after thePoint
  1686. tmp.mOffset++;
  1687. WSPoint nextPoint = GetCharAfter(tmp);
  1688. if (nextPoint.mTextNode) {
  1689. if (!nsCRT::IsAsciiSpace(nextPoint.mChar)) {
  1690. canConvert = true;
  1691. }
  1692. } else if (aRun->mRightType == WSType::text ||
  1693. aRun->mRightType == WSType::special ||
  1694. aRun->mRightType == WSType::br) {
  1695. canConvert = true;
  1696. }
  1697. }
  1698. if (canConvert) {
  1699. // First, insert a space
  1700. AutoTransactionsConserveSelection dontSpazMySelection(mHTMLEditor);
  1701. nsAutoString spaceStr(char16_t(32));
  1702. nsresult rv =
  1703. mHTMLEditor->InsertTextIntoTextNodeImpl(spaceStr, *thePoint.mTextNode,
  1704. thePoint.mOffset, true);
  1705. NS_ENSURE_SUCCESS(rv, rv);
  1706. // Finally, delete that nbsp
  1707. rv = DeleteChars(thePoint.mTextNode, thePoint.mOffset + 1,
  1708. thePoint.mTextNode, thePoint.mOffset + 2);
  1709. NS_ENSURE_SUCCESS(rv, rv);
  1710. }
  1711. return NS_OK;
  1712. }
  1713. nsresult
  1714. WSRunObject::Scrub()
  1715. {
  1716. WSFragment *run = mStartRun;
  1717. while (run) {
  1718. if (run->mType & (WSType::leadingWS | WSType::trailingWS)) {
  1719. nsresult rv = DeleteChars(run->mStartNode, run->mStartOffset,
  1720. run->mEndNode, run->mEndOffset);
  1721. NS_ENSURE_SUCCESS(rv, rv);
  1722. }
  1723. run = run->mRight;
  1724. }
  1725. return NS_OK;
  1726. }
  1727. bool
  1728. WSRunObject::IsBlockNode(nsINode* aNode)
  1729. {
  1730. return aNode && aNode->IsElement() &&
  1731. HTMLEditor::NodeIsBlockStatic(aNode->AsElement());
  1732. }
  1733. } // namespace mozilla