Wed, 31 Dec 2014 06:55:50 +0100
Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2
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/. */
6 #ifndef __wsrunobject_h__
7 #define __wsrunobject_h__
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"
17 class nsHTMLEditor;
18 class nsIDOMDocument;
19 class nsIDOMNode;
20 struct DOMPoint;
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.
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.
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 };
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 };
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 }
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 }
138 class MOZ_STACK_CLASS nsWSRunObject
139 {
140 public:
142 // public enums ---------------------------------------------------------
143 enum BlockBoundary
144 {
145 kBeforeBlock,
146 kBlockStart,
147 kBlockEnd,
148 kAfterBlock
149 };
151 enum {eBefore = 1};
152 enum {eAfter = 1 << 1};
153 enum {eBoth = eBefore | eAfter};
155 // constructor / destructor -----------------------------------------------
156 nsWSRunObject(nsHTMLEditor *aEd, nsIDOMNode *aNode, int32_t aOffset);
157 ~nsWSRunObject();
159 // public methods ---------------------------------------------------------
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);
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);
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);
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);
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);
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);
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);
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();
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();
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);
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);
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();
261 protected:
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;
278 WSFragment() : mStartNode(0), mEndNode(0),
279 mStartOffset(0), mEndOffset(0),
280 mType(), mLeftType(), mRightType(),
281 mLeft(0), mRight(0)
282 {
283 }
284 };
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;
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 };
311 enum AreaRestriction
312 {
313 eAnywhere, eOutsideUserSelectAll
314 };
316 // protected methods ---------------------------------------------------------
317 // tons of utility methods.
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();
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);
374 static nsresult ScrubBlockBoundaryInner(nsHTMLEditor *aHTMLEd,
375 nsCOMPtr<nsIDOMNode> *aBlock,
376 BlockBoundary aBoundary);
377 nsresult Scrub();
379 // member variables ---------------------------------------------------------
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.
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
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
396 nsCOMPtr<nsIDOMNode> mFirstNBSPNode; // location of first nbsp in ws run, if any
397 int32_t mFirstNBSPOffset; // ...
399 nsCOMPtr<nsIDOMNode> mLastNBSPNode; // location of last nbsp in ws run, if any
400 int32_t mLastNBSPOffset; // ...
402 nsCOMArray<nsIDOMNode> mNodeArray;//the list of nodes containing ws in this run
404 WSFragment *mStartRun; // the first WSFragment in the run
405 WSFragment *mEndRun; // the last WSFragment in the run, may be same as first
407 nsHTMLEditor *mHTMLEditor; // non-owning.
409 friend class nsHTMLEditRules; // opening this class up for pillaging
410 friend class nsHTMLEditor; // opening this class up for more pillaging
411 };
413 #endif