|
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 |
|
6 #ifndef __wsrunobject_h__ |
|
7 #define __wsrunobject_h__ |
|
8 |
|
9 #include "nsCOMArray.h" |
|
10 #include "nsCOMPtr.h" |
|
11 #include "nsIContent.h" |
|
12 #include "nsIDOMNode.h" |
|
13 #include "nsIEditor.h" |
|
14 #include "nsINode.h" |
|
15 #include "nscore.h" |
|
16 |
|
17 class nsHTMLEditor; |
|
18 class nsIDOMDocument; |
|
19 class nsIDOMNode; |
|
20 struct DOMPoint; |
|
21 |
|
22 // class nsWSRunObject represents the entire whitespace situation |
|
23 // around a given point. It collects up a list of nodes that contain |
|
24 // whitespace and categorizes in up to 3 different WSFragments (detailed |
|
25 // below). Each WSFragment is a collection of whitespace that is |
|
26 // either all insignificant, or that is significant. A WSFragment could |
|
27 // consist of insignificant whitespace because it is after a block |
|
28 // boundary or after a break. Or it could be insignificant because it |
|
29 // is before a block. Or it could be significant because it is |
|
30 // surrounded by text, or starts and ends with nbsps, etc. |
|
31 |
|
32 // Throughout I refer to LeadingWS, NormalWS, TrailingWS. LeadingWS & TrailingWS |
|
33 // are runs of ascii ws that are insignificant (do not render) because they |
|
34 // are adjacent to block boundaries, or after a break. NormalWS is ws that |
|
35 // does cause soem rendering. Note that not all the ws in a NormalWS run need |
|
36 // render. For example, two ascii spaces surrounded by text on both sides |
|
37 // will only render as one space (in non-preformatted stlye html), yet both |
|
38 // spaces count as NormalWS. Together, they render as the one visible space. |
|
39 |
|
40 /** |
|
41 * A type-safe bitfield indicating various types of whitespace or other things. |
|
42 * Used as a member variable in nsWSRunObject and WSFragment. |
|
43 * |
|
44 * XXX: If this idea is useful in other places, we should generalize it using a |
|
45 * template. |
|
46 */ |
|
47 class WSType { |
|
48 public: |
|
49 enum Enum { |
|
50 none = 0, |
|
51 leadingWS = 1, // leading insignificant ws, ie, after block or br |
|
52 trailingWS = 1 << 1, // trailing insignificant ws, ie, before block |
|
53 normalWS = 1 << 2, // normal significant ws, ie, after text, image, ... |
|
54 text = 1 << 3, // indicates regular (non-ws) text |
|
55 special = 1 << 4, // indicates an inline non-container, like image |
|
56 br = 1 << 5, // indicates a br node |
|
57 otherBlock = 1 << 6, // indicates a block other than one ws run is in |
|
58 thisBlock = 1 << 7, // indicates the block ws run is in |
|
59 block = otherBlock | thisBlock // block found |
|
60 }; |
|
61 |
|
62 /** |
|
63 * Implicit constructor, because the enums are logically just WSTypes |
|
64 * themselves, and are only a separate type because there's no other obvious |
|
65 * way to name specific WSType values. |
|
66 */ |
|
67 WSType(const Enum& aEnum = none) : mEnum(aEnum) {} |
|
68 // operator==, &, and | need to access mEnum |
|
69 friend bool operator==(const WSType& aLeft, const WSType& aRight); |
|
70 friend const WSType operator&(const WSType& aLeft, const WSType& aRight); |
|
71 friend const WSType operator|(const WSType& aLeft, const WSType& aRight); |
|
72 WSType& operator=(const WSType& aOther) { |
|
73 // This handles self-assignment fine |
|
74 mEnum = aOther.mEnum; |
|
75 return *this; |
|
76 } |
|
77 WSType& operator&=(const WSType& aOther) { |
|
78 mEnum &= aOther.mEnum; |
|
79 return *this; |
|
80 } |
|
81 WSType& operator|=(const WSType& aOther) { |
|
82 mEnum |= aOther.mEnum; |
|
83 return *this; |
|
84 } |
|
85 private: |
|
86 uint16_t mEnum; |
|
87 void bool_conversion_helper() {} |
|
88 public: |
|
89 // Allow boolean conversion with no numeric conversion |
|
90 typedef void (WSType::*bool_type)(); |
|
91 operator bool_type() const |
|
92 { |
|
93 return mEnum ? &WSType::bool_conversion_helper : nullptr; |
|
94 } |
|
95 }; |
|
96 |
|
97 /** |
|
98 * These are declared as global functions so "WSType::Enum == WSType" et al. |
|
99 * will work using the implicit constructor. |
|
100 */ |
|
101 inline bool operator==(const WSType& aLeft, const WSType& aRight) |
|
102 { |
|
103 return aLeft.mEnum == aRight.mEnum; |
|
104 } |
|
105 inline bool operator!=(const WSType& aLeft, const WSType& aRight) |
|
106 { |
|
107 return !(aLeft == aRight); |
|
108 } |
|
109 inline const WSType operator&(const WSType& aLeft, const WSType& aRight) |
|
110 { |
|
111 WSType ret; |
|
112 ret.mEnum = aLeft.mEnum & aRight.mEnum; |
|
113 return ret; |
|
114 } |
|
115 inline const WSType operator|(const WSType& aLeft, const WSType& aRight) |
|
116 { |
|
117 WSType ret; |
|
118 ret.mEnum = aLeft.mEnum | aRight.mEnum; |
|
119 return ret; |
|
120 } |
|
121 |
|
122 /** |
|
123 * Make sure that & and | of WSType::Enum creates a WSType instead of an int, |
|
124 * because operators between WSType and int shouldn't work |
|
125 */ |
|
126 inline const WSType operator&(const WSType::Enum& aLeft, |
|
127 const WSType::Enum& aRight) |
|
128 { |
|
129 return WSType(aLeft) & WSType(aRight); |
|
130 } |
|
131 inline const WSType operator|(const WSType::Enum& aLeft, |
|
132 const WSType::Enum& aRight) |
|
133 { |
|
134 return WSType(aLeft) | WSType(aRight); |
|
135 } |
|
136 |
|
137 |
|
138 class MOZ_STACK_CLASS nsWSRunObject |
|
139 { |
|
140 public: |
|
141 |
|
142 // public enums --------------------------------------------------------- |
|
143 enum BlockBoundary |
|
144 { |
|
145 kBeforeBlock, |
|
146 kBlockStart, |
|
147 kBlockEnd, |
|
148 kAfterBlock |
|
149 }; |
|
150 |
|
151 enum {eBefore = 1}; |
|
152 enum {eAfter = 1 << 1}; |
|
153 enum {eBoth = eBefore | eAfter}; |
|
154 |
|
155 // constructor / destructor ----------------------------------------------- |
|
156 nsWSRunObject(nsHTMLEditor *aEd, nsIDOMNode *aNode, int32_t aOffset); |
|
157 ~nsWSRunObject(); |
|
158 |
|
159 // public methods --------------------------------------------------------- |
|
160 |
|
161 // ScrubBlockBoundary removes any non-visible whitespace at the specified |
|
162 // location relative to a block node. |
|
163 static nsresult ScrubBlockBoundary(nsHTMLEditor *aHTMLEd, |
|
164 nsCOMPtr<nsIDOMNode> *aBlock, |
|
165 BlockBoundary aBoundary, |
|
166 int32_t *aOffset = 0); |
|
167 |
|
168 // PrepareToJoinBlocks fixes up ws at the end of aLeftParent and the |
|
169 // beginning of aRightParent in preperation for them to be joined. |
|
170 // example of fixup: trailingws in aLeftParent needs to be removed. |
|
171 static nsresult PrepareToJoinBlocks(nsHTMLEditor *aEd, |
|
172 nsIDOMNode *aLeftParent, |
|
173 nsIDOMNode *aRightParent); |
|
174 |
|
175 // PrepareToDeleteRange fixes up ws before {aStartNode,aStartOffset} |
|
176 // and after {aEndNode,aEndOffset} in preperation for content |
|
177 // in that range to be deleted. Note that the nodes and offsets |
|
178 // are adjusted in response to any dom changes we make while |
|
179 // adjusting ws. |
|
180 // example of fixup: trailingws before {aStartNode,aStartOffset} |
|
181 // needs to be removed. |
|
182 static nsresult PrepareToDeleteRange(nsHTMLEditor *aHTMLEd, |
|
183 nsCOMPtr<nsIDOMNode> *aStartNode, |
|
184 int32_t *aStartOffset, |
|
185 nsCOMPtr<nsIDOMNode> *aEndNode, |
|
186 int32_t *aEndOffset); |
|
187 |
|
188 // PrepareToDeleteNode fixes up ws before and after aNode in preperation |
|
189 // for aNode to be deleted. |
|
190 // example of fixup: trailingws before aNode needs to be removed. |
|
191 static nsresult PrepareToDeleteNode(nsHTMLEditor *aHTMLEd, |
|
192 nsIDOMNode *aNode); |
|
193 |
|
194 // PrepareToSplitAcrossBlocks fixes up ws before and after |
|
195 // {aSplitNode,aSplitOffset} in preperation for a block |
|
196 // parent to be split. Note that the aSplitNode and aSplitOffset |
|
197 // are adjusted in response to any dom changes we make while |
|
198 // adjusting ws. |
|
199 // example of fixup: normalws before {aSplitNode,aSplitOffset} |
|
200 // needs to end with nbsp. |
|
201 static nsresult PrepareToSplitAcrossBlocks(nsHTMLEditor *aHTMLEd, |
|
202 nsCOMPtr<nsIDOMNode> *aSplitNode, |
|
203 int32_t *aSplitOffset); |
|
204 |
|
205 // InsertBreak inserts a br node at {aInOutParent,aInOutOffset} |
|
206 // and makes any needed adjustments to ws around that point. |
|
207 // example of fixup: normalws after {aInOutParent,aInOutOffset} |
|
208 // needs to begin with nbsp. |
|
209 nsresult InsertBreak(nsCOMPtr<nsIDOMNode> *aInOutParent, |
|
210 int32_t *aInOutOffset, |
|
211 nsCOMPtr<nsIDOMNode> *outBRNode, |
|
212 nsIEditor::EDirection aSelect); |
|
213 |
|
214 // InsertText inserts a string at {aInOutParent,aInOutOffset} |
|
215 // and makes any needed adjustments to ws around that point. |
|
216 // example of fixup: trailingws before {aInOutParent,aInOutOffset} |
|
217 // needs to be removed. |
|
218 nsresult InsertText(const nsAString& aStringToInsert, |
|
219 nsCOMPtr<nsIDOMNode> *aInOutNode, |
|
220 int32_t *aInOutOffset, |
|
221 nsIDOMDocument *aDoc); |
|
222 |
|
223 // DeleteWSBackward deletes a single visible piece of ws before |
|
224 // the ws point (the point to create the wsRunObject, passed to |
|
225 // its constructor). It makes any needed conversion to adjacent |
|
226 // ws to retain its significance. |
|
227 nsresult DeleteWSBackward(); |
|
228 |
|
229 // DeleteWSForward deletes a single visible piece of ws after |
|
230 // the ws point (the point to create the wsRunObject, passed to |
|
231 // its constructor). It makes any needed conversion to adjacent |
|
232 // ws to retain its significance. |
|
233 nsresult DeleteWSForward(); |
|
234 |
|
235 // PriorVisibleNode returns the first piece of visible thing |
|
236 // before {aNode,aOffset}. If there is no visible ws qualifying |
|
237 // it returns what is before the ws run. Note that |
|
238 // {outVisNode,outVisOffset} is set to just AFTER the visible |
|
239 // object. |
|
240 void PriorVisibleNode(nsIDOMNode *aNode, |
|
241 int32_t aOffset, |
|
242 nsCOMPtr<nsIDOMNode> *outVisNode, |
|
243 int32_t *outVisOffset, |
|
244 WSType *outType); |
|
245 |
|
246 // NextVisibleNode returns the first piece of visible thing |
|
247 // after {aNode,aOffset}. If there is no visible ws qualifying |
|
248 // it returns what is after the ws run. Note that |
|
249 // {outVisNode,outVisOffset} is set to just BEFORE the visible |
|
250 // object. |
|
251 void NextVisibleNode(nsIDOMNode *aNode, |
|
252 int32_t aOffset, |
|
253 nsCOMPtr<nsIDOMNode> *outVisNode, |
|
254 int32_t *outVisOffset, |
|
255 WSType *outType); |
|
256 |
|
257 // AdjustWhitespace examines the ws object for nbsp's that can |
|
258 // be safely converted to regular ascii space and converts them. |
|
259 nsresult AdjustWhitespace(); |
|
260 |
|
261 protected: |
|
262 |
|
263 // WSFragment struct --------------------------------------------------------- |
|
264 // WSFragment represents a single run of ws (all leadingws, or all normalws, |
|
265 // or all trailingws, or all leading+trailingws). Note that this single run may |
|
266 // still span multiple nodes. |
|
267 struct WSFragment |
|
268 { |
|
269 nsCOMPtr<nsIDOMNode> mStartNode; // node where ws run starts |
|
270 nsCOMPtr<nsIDOMNode> mEndNode; // node where ws run ends |
|
271 int32_t mStartOffset; // offset where ws run starts |
|
272 int32_t mEndOffset; // offset where ws run ends |
|
273 // type of ws, and what is to left and right of it |
|
274 WSType mType, mLeftType, mRightType; |
|
275 // other ws runs to left or right. may be null. |
|
276 WSFragment *mLeft, *mRight; |
|
277 |
|
278 WSFragment() : mStartNode(0), mEndNode(0), |
|
279 mStartOffset(0), mEndOffset(0), |
|
280 mType(), mLeftType(), mRightType(), |
|
281 mLeft(0), mRight(0) |
|
282 { |
|
283 } |
|
284 }; |
|
285 |
|
286 // WSPoint struct ------------------------------------------------------------ |
|
287 // A WSPoint struct represents a unique location within the ws run. It is |
|
288 // always within a textnode that is one of the nodes stored in the list |
|
289 // in the wsRunObject. For convenience, the character at that point is also |
|
290 // stored in the struct. |
|
291 struct MOZ_STACK_CLASS WSPoint |
|
292 { |
|
293 nsCOMPtr<nsIContent> mTextNode; |
|
294 uint32_t mOffset; |
|
295 char16_t mChar; |
|
296 |
|
297 WSPoint() : mTextNode(0),mOffset(0),mChar(0) {} |
|
298 WSPoint(nsIDOMNode *aNode, int32_t aOffset, char16_t aChar) : |
|
299 mTextNode(do_QueryInterface(aNode)),mOffset(aOffset),mChar(aChar) |
|
300 { |
|
301 if (!mTextNode->IsNodeOfType(nsINode::eDATA_NODE)) { |
|
302 // Not sure if this is needed, but it'll maintain the same |
|
303 // functionality |
|
304 mTextNode = nullptr; |
|
305 } |
|
306 } |
|
307 WSPoint(nsIContent *aTextNode, int32_t aOffset, char16_t aChar) : |
|
308 mTextNode(aTextNode),mOffset(aOffset),mChar(aChar) {} |
|
309 }; |
|
310 |
|
311 enum AreaRestriction |
|
312 { |
|
313 eAnywhere, eOutsideUserSelectAll |
|
314 }; |
|
315 |
|
316 // protected methods --------------------------------------------------------- |
|
317 // tons of utility methods. |
|
318 |
|
319 /** |
|
320 * Return the node which we will handle white-space under. This is the |
|
321 * closest block within the DOM subtree we're editing, or if none is |
|
322 * found, the (inline) root of the editable subtree. |
|
323 */ |
|
324 already_AddRefed<nsIDOMNode> GetWSBoundingParent(); |
|
325 |
|
326 nsresult GetWSNodes(); |
|
327 void GetRuns(); |
|
328 void ClearRuns(); |
|
329 void MakeSingleWSRun(WSType aType); |
|
330 nsresult PrependNodeToList(nsIDOMNode *aNode); |
|
331 nsresult AppendNodeToList(nsIDOMNode *aNode); |
|
332 nsresult GetPreviousWSNode(nsIDOMNode *aStartNode, |
|
333 nsIDOMNode *aBlockParent, |
|
334 nsCOMPtr<nsIDOMNode> *aPriorNode); |
|
335 nsresult GetPreviousWSNode(nsIDOMNode *aStartNode, |
|
336 int32_t aOffset, |
|
337 nsIDOMNode *aBlockParent, |
|
338 nsCOMPtr<nsIDOMNode> *aPriorNode); |
|
339 nsresult GetPreviousWSNode(::DOMPoint aPoint, |
|
340 nsIDOMNode *aBlockParent, |
|
341 nsCOMPtr<nsIDOMNode> *aPriorNode); |
|
342 nsresult GetNextWSNode(nsIDOMNode *aStartNode, |
|
343 nsIDOMNode *aBlockParent, |
|
344 nsCOMPtr<nsIDOMNode> *aNextNode); |
|
345 nsresult GetNextWSNode(nsIDOMNode *aStartNode, |
|
346 int32_t aOffset, |
|
347 nsIDOMNode *aBlockParent, |
|
348 nsCOMPtr<nsIDOMNode> *aNextNode); |
|
349 nsresult GetNextWSNode(::DOMPoint aPoint, |
|
350 nsIDOMNode *aBlockParent, |
|
351 nsCOMPtr<nsIDOMNode> *aNextNode); |
|
352 nsresult PrepareToDeleteRangePriv(nsWSRunObject* aEndObject); |
|
353 nsresult PrepareToSplitAcrossBlocksPriv(); |
|
354 nsresult DeleteChars(nsIDOMNode *aStartNode, int32_t aStartOffset, |
|
355 nsIDOMNode *aEndNode, int32_t aEndOffset, |
|
356 AreaRestriction aAR = eAnywhere); |
|
357 WSPoint GetCharAfter(nsIDOMNode *aNode, int32_t aOffset); |
|
358 WSPoint GetCharBefore(nsIDOMNode *aNode, int32_t aOffset); |
|
359 WSPoint GetCharAfter(const WSPoint &aPoint); |
|
360 WSPoint GetCharBefore(const WSPoint &aPoint); |
|
361 nsresult ConvertToNBSP(WSPoint aPoint, |
|
362 AreaRestriction aAR = eAnywhere); |
|
363 void GetAsciiWSBounds(int16_t aDir, nsIDOMNode *aNode, int32_t aOffset, |
|
364 nsCOMPtr<nsIDOMNode> *outStartNode, int32_t *outStartOffset, |
|
365 nsCOMPtr<nsIDOMNode> *outEndNode, int32_t *outEndOffset); |
|
366 void FindRun(nsIDOMNode *aNode, int32_t aOffset, WSFragment **outRun, bool after); |
|
367 char16_t GetCharAt(nsIContent *aTextNode, int32_t aOffset); |
|
368 WSPoint GetWSPointAfter(nsIDOMNode *aNode, int32_t aOffset); |
|
369 WSPoint GetWSPointBefore(nsIDOMNode *aNode, int32_t aOffset); |
|
370 nsresult CheckTrailingNBSPOfRun(WSFragment *aRun); |
|
371 nsresult CheckTrailingNBSP(WSFragment *aRun, nsIDOMNode *aNode, int32_t aOffset); |
|
372 nsresult CheckLeadingNBSP(WSFragment *aRun, nsIDOMNode *aNode, int32_t aOffset); |
|
373 |
|
374 static nsresult ScrubBlockBoundaryInner(nsHTMLEditor *aHTMLEd, |
|
375 nsCOMPtr<nsIDOMNode> *aBlock, |
|
376 BlockBoundary aBoundary); |
|
377 nsresult Scrub(); |
|
378 |
|
379 // member variables --------------------------------------------------------- |
|
380 |
|
381 nsCOMPtr<nsIDOMNode> mNode; // the node passed to our constructor |
|
382 int32_t mOffset; // the offset passed to our contructor |
|
383 // together, the above represent the point at which we are building up ws info. |
|
384 |
|
385 bool mPRE; // true if we are in preformatted whitespace context |
|
386 nsCOMPtr<nsIDOMNode> mStartNode; // node/offset where ws starts |
|
387 int32_t mStartOffset; // ... |
|
388 WSType mStartReason; // reason why ws starts (eText, eOtherBlock, etc) |
|
389 nsCOMPtr<nsIDOMNode> mStartReasonNode;// the node that implicated by start reason |
|
390 |
|
391 nsCOMPtr<nsIDOMNode> mEndNode; // node/offset where ws ends |
|
392 int32_t mEndOffset; // ... |
|
393 WSType mEndReason; // reason why ws ends (eText, eOtherBlock, etc) |
|
394 nsCOMPtr<nsIDOMNode> mEndReasonNode; // the node that implicated by end reason |
|
395 |
|
396 nsCOMPtr<nsIDOMNode> mFirstNBSPNode; // location of first nbsp in ws run, if any |
|
397 int32_t mFirstNBSPOffset; // ... |
|
398 |
|
399 nsCOMPtr<nsIDOMNode> mLastNBSPNode; // location of last nbsp in ws run, if any |
|
400 int32_t mLastNBSPOffset; // ... |
|
401 |
|
402 nsCOMArray<nsIDOMNode> mNodeArray;//the list of nodes containing ws in this run |
|
403 |
|
404 WSFragment *mStartRun; // the first WSFragment in the run |
|
405 WSFragment *mEndRun; // the last WSFragment in the run, may be same as first |
|
406 |
|
407 nsHTMLEditor *mHTMLEditor; // non-owning. |
|
408 |
|
409 friend class nsHTMLEditRules; // opening this class up for pillaging |
|
410 friend class nsHTMLEditor; // opening this class up for more pillaging |
|
411 }; |
|
412 |
|
413 #endif |
|
414 |