editor/libeditor/html/nsWSRunObject.h

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/editor/libeditor/html/nsWSRunObject.h	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,414 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +#ifndef __wsrunobject_h__
    1.10 +#define __wsrunobject_h__
    1.11 +
    1.12 +#include "nsCOMArray.h"
    1.13 +#include "nsCOMPtr.h"
    1.14 +#include "nsIContent.h"
    1.15 +#include "nsIDOMNode.h"
    1.16 +#include "nsIEditor.h"
    1.17 +#include "nsINode.h"
    1.18 +#include "nscore.h"
    1.19 +
    1.20 +class nsHTMLEditor;
    1.21 +class nsIDOMDocument;
    1.22 +class nsIDOMNode;
    1.23 +struct DOMPoint;
    1.24 +
    1.25 +// class nsWSRunObject represents the entire whitespace situation
    1.26 +// around a given point.  It collects up a list of nodes that contain
    1.27 +// whitespace and categorizes in up to 3 different WSFragments (detailed
    1.28 +// below).  Each WSFragment is a collection of whitespace that is
    1.29 +// either all insignificant, or that is significant.  A WSFragment could
    1.30 +// consist of insignificant whitespace because it is after a block
    1.31 +// boundary or after a break.  Or it could be insignificant because it
    1.32 +// is before a block.  Or it could be significant because it is
    1.33 +// surrounded by text, or starts and ends with nbsps, etc.
    1.34 +
    1.35 +// Throughout I refer to LeadingWS, NormalWS, TrailingWS.  LeadingWS & TrailingWS
    1.36 +// are runs of ascii ws that are insignificant (do not render) because they
    1.37 +// are adjacent to block boundaries, or after a break.  NormalWS is ws that
    1.38 +// does cause soem rendering.  Note that not all the ws in a NormalWS run need
    1.39 +// render.  For example, two ascii spaces surrounded by text on both sides
    1.40 +// will only render as one space (in non-preformatted stlye html), yet both
    1.41 +// spaces count as NormalWS.  Together, they render as the one visible space.
    1.42 +
    1.43 +/**
    1.44 + * A type-safe bitfield indicating various types of whitespace or other things.
    1.45 + * Used as a member variable in nsWSRunObject and WSFragment.
    1.46 + *
    1.47 + * XXX: If this idea is useful in other places, we should generalize it using a
    1.48 + * template.
    1.49 + */
    1.50 +class WSType {
    1.51 +public:
    1.52 +  enum Enum {
    1.53 +    none       = 0,
    1.54 +    leadingWS  = 1,      // leading insignificant ws, ie, after block or br
    1.55 +    trailingWS = 1 << 1, // trailing insignificant ws, ie, before block
    1.56 +    normalWS   = 1 << 2, // normal significant ws, ie, after text, image, ...
    1.57 +    text       = 1 << 3, // indicates regular (non-ws) text
    1.58 +    special    = 1 << 4, // indicates an inline non-container, like image
    1.59 +    br         = 1 << 5, // indicates a br node
    1.60 +    otherBlock = 1 << 6, // indicates a block other than one ws run is in
    1.61 +    thisBlock  = 1 << 7, // indicates the block ws run is in
    1.62 +    block      = otherBlock | thisBlock // block found
    1.63 +  };
    1.64 +
    1.65 +  /**
    1.66 +   * Implicit constructor, because the enums are logically just WSTypes
    1.67 +   * themselves, and are only a separate type because there's no other obvious
    1.68 +   * way to name specific WSType values.
    1.69 +   */
    1.70 +  WSType(const Enum& aEnum = none) : mEnum(aEnum) {}
    1.71 +  // operator==, &, and | need to access mEnum
    1.72 +  friend bool operator==(const WSType& aLeft, const WSType& aRight);
    1.73 +  friend const WSType operator&(const WSType& aLeft, const WSType& aRight);
    1.74 +  friend const WSType operator|(const WSType& aLeft, const WSType& aRight);
    1.75 +  WSType& operator=(const WSType& aOther) {
    1.76 +    // This handles self-assignment fine
    1.77 +    mEnum = aOther.mEnum;
    1.78 +    return *this;
    1.79 +  }
    1.80 +  WSType& operator&=(const WSType& aOther) {
    1.81 +    mEnum &= aOther.mEnum;
    1.82 +    return *this;
    1.83 +  }
    1.84 +  WSType& operator|=(const WSType& aOther) {
    1.85 +    mEnum |= aOther.mEnum;
    1.86 +    return *this;
    1.87 +  }
    1.88 +private:
    1.89 +  uint16_t mEnum;
    1.90 +  void bool_conversion_helper() {}
    1.91 +public:
    1.92 +  // Allow boolean conversion with no numeric conversion
    1.93 +  typedef void (WSType::*bool_type)();
    1.94 +  operator bool_type() const
    1.95 +  {
    1.96 +    return mEnum ? &WSType::bool_conversion_helper : nullptr;
    1.97 +  }
    1.98 +};
    1.99 +
   1.100 +/**
   1.101 + * These are declared as global functions so "WSType::Enum == WSType" et al.
   1.102 + * will work using the implicit constructor.
   1.103 + */
   1.104 +inline bool operator==(const WSType& aLeft, const WSType& aRight)
   1.105 +{
   1.106 +  return aLeft.mEnum == aRight.mEnum;
   1.107 +}
   1.108 +inline bool operator!=(const WSType& aLeft, const WSType& aRight)
   1.109 +{
   1.110 +  return !(aLeft == aRight);
   1.111 +}
   1.112 +inline const WSType operator&(const WSType& aLeft, const WSType& aRight)
   1.113 +{
   1.114 +  WSType ret;
   1.115 +  ret.mEnum = aLeft.mEnum & aRight.mEnum;
   1.116 +  return ret;
   1.117 +}
   1.118 +inline const WSType operator|(const WSType& aLeft, const WSType& aRight)
   1.119 +{
   1.120 +  WSType ret;
   1.121 +  ret.mEnum = aLeft.mEnum | aRight.mEnum;
   1.122 +  return ret;
   1.123 +}
   1.124 +
   1.125 +/**
   1.126 + * Make sure that & and | of WSType::Enum creates a WSType instead of an int,
   1.127 + * because operators between WSType and int shouldn't work
   1.128 + */
   1.129 +inline const WSType operator&(const WSType::Enum& aLeft,
   1.130 +                              const WSType::Enum& aRight)
   1.131 +{
   1.132 +  return WSType(aLeft) & WSType(aRight);
   1.133 +}
   1.134 +inline const WSType operator|(const WSType::Enum& aLeft,
   1.135 +                              const WSType::Enum& aRight)
   1.136 +{
   1.137 +  return WSType(aLeft) | WSType(aRight);
   1.138 +}
   1.139 +
   1.140 +
   1.141 +class MOZ_STACK_CLASS nsWSRunObject
   1.142 +{
   1.143 +  public:
   1.144 +
   1.145 +    // public enums ---------------------------------------------------------
   1.146 +    enum BlockBoundary
   1.147 +    {
   1.148 +      kBeforeBlock,
   1.149 +      kBlockStart,
   1.150 +      kBlockEnd,
   1.151 +      kAfterBlock
   1.152 +    };
   1.153 +
   1.154 +    enum {eBefore = 1};
   1.155 +    enum {eAfter  = 1 << 1};
   1.156 +    enum {eBoth   = eBefore | eAfter};
   1.157 +
   1.158 +    // constructor / destructor -----------------------------------------------
   1.159 +    nsWSRunObject(nsHTMLEditor *aEd, nsIDOMNode *aNode, int32_t aOffset);
   1.160 +    ~nsWSRunObject();
   1.161 +    
   1.162 +    // public methods ---------------------------------------------------------
   1.163 +
   1.164 +    // ScrubBlockBoundary removes any non-visible whitespace at the specified
   1.165 +    // location relative to a block node.  
   1.166 +    static nsresult ScrubBlockBoundary(nsHTMLEditor *aHTMLEd, 
   1.167 +                                       nsCOMPtr<nsIDOMNode> *aBlock,
   1.168 +                                       BlockBoundary aBoundary,
   1.169 +                                       int32_t *aOffset = 0);
   1.170 +    
   1.171 +    // PrepareToJoinBlocks fixes up ws at the end of aLeftParent and the
   1.172 +    // beginning of aRightParent in preperation for them to be joined.
   1.173 +    // example of fixup: trailingws in aLeftParent needs to be removed.
   1.174 +    static nsresult PrepareToJoinBlocks(nsHTMLEditor *aEd, 
   1.175 +                                        nsIDOMNode *aLeftParent,
   1.176 +                                        nsIDOMNode *aRightParent);
   1.177 +
   1.178 +    // PrepareToDeleteRange fixes up ws before {aStartNode,aStartOffset}
   1.179 +    // and after {aEndNode,aEndOffset} in preperation for content
   1.180 +    // in that range to be deleted.  Note that the nodes and offsets
   1.181 +    // are adjusted in response to any dom changes we make while 
   1.182 +    // adjusting ws.
   1.183 +    // example of fixup: trailingws before {aStartNode,aStartOffset}
   1.184 +    //                   needs to be removed.
   1.185 +    static nsresult PrepareToDeleteRange(nsHTMLEditor *aHTMLEd, 
   1.186 +                                         nsCOMPtr<nsIDOMNode> *aStartNode,
   1.187 +                                         int32_t *aStartOffset, 
   1.188 +                                         nsCOMPtr<nsIDOMNode> *aEndNode,
   1.189 +                                         int32_t *aEndOffset);
   1.190 +
   1.191 +    // PrepareToDeleteNode fixes up ws before and after aNode in preperation 
   1.192 +    // for aNode to be deleted.
   1.193 +    // example of fixup: trailingws before aNode needs to be removed.
   1.194 +    static nsresult PrepareToDeleteNode(nsHTMLEditor *aHTMLEd, 
   1.195 +                                        nsIDOMNode *aNode);
   1.196 +
   1.197 +    // PrepareToSplitAcrossBlocks fixes up ws before and after 
   1.198 +    // {aSplitNode,aSplitOffset} in preperation for a block
   1.199 +    // parent to be split.  Note that the aSplitNode and aSplitOffset
   1.200 +    // are adjusted in response to any dom changes we make while 
   1.201 +    // adjusting ws.
   1.202 +    // example of fixup: normalws before {aSplitNode,aSplitOffset} 
   1.203 +    //                   needs to end with nbsp.
   1.204 +    static nsresult PrepareToSplitAcrossBlocks(nsHTMLEditor *aHTMLEd, 
   1.205 +                                               nsCOMPtr<nsIDOMNode> *aSplitNode, 
   1.206 +                                               int32_t *aSplitOffset);
   1.207 +
   1.208 +    // InsertBreak inserts a br node at {aInOutParent,aInOutOffset}
   1.209 +    // and makes any needed adjustments to ws around that point.
   1.210 +    // example of fixup: normalws after {aInOutParent,aInOutOffset}
   1.211 +    //                   needs to begin with nbsp.
   1.212 +    nsresult InsertBreak(nsCOMPtr<nsIDOMNode> *aInOutParent, 
   1.213 +                         int32_t *aInOutOffset, 
   1.214 +                         nsCOMPtr<nsIDOMNode> *outBRNode, 
   1.215 +                         nsIEditor::EDirection aSelect);
   1.216 +
   1.217 +    // InsertText inserts a string at {aInOutParent,aInOutOffset}
   1.218 +    // and makes any needed adjustments to ws around that point.
   1.219 +    // example of fixup: trailingws before {aInOutParent,aInOutOffset}
   1.220 +    //                   needs to be removed.
   1.221 +    nsresult InsertText(const nsAString& aStringToInsert, 
   1.222 +                        nsCOMPtr<nsIDOMNode> *aInOutNode, 
   1.223 +                        int32_t *aInOutOffset,
   1.224 +                        nsIDOMDocument *aDoc);
   1.225 +
   1.226 +    // DeleteWSBackward deletes a single visible piece of ws before
   1.227 +    // the ws point (the point to create the wsRunObject, passed to 
   1.228 +    // its constructor).  It makes any needed conversion to adjacent
   1.229 +    // ws to retain its significance.
   1.230 +    nsresult DeleteWSBackward();
   1.231 +
   1.232 +    // DeleteWSForward deletes a single visible piece of ws after
   1.233 +    // the ws point (the point to create the wsRunObject, passed to 
   1.234 +    // its constructor).  It makes any needed conversion to adjacent
   1.235 +    // ws to retain its significance.
   1.236 +    nsresult DeleteWSForward();
   1.237 +
   1.238 +    // PriorVisibleNode returns the first piece of visible thing
   1.239 +    // before {aNode,aOffset}.  If there is no visible ws qualifying
   1.240 +    // it returns what is before the ws run.  Note that 
   1.241 +    // {outVisNode,outVisOffset} is set to just AFTER the visible
   1.242 +    // object.
   1.243 +    void PriorVisibleNode(nsIDOMNode *aNode,
   1.244 +                          int32_t aOffset,
   1.245 +                          nsCOMPtr<nsIDOMNode> *outVisNode,
   1.246 +                          int32_t *outVisOffset,
   1.247 +                          WSType *outType);
   1.248 +
   1.249 +    // NextVisibleNode returns the first piece of visible thing
   1.250 +    // after {aNode,aOffset}.  If there is no visible ws qualifying
   1.251 +    // it returns what is after the ws run.  Note that 
   1.252 +    // {outVisNode,outVisOffset} is set to just BEFORE the visible
   1.253 +    // object.
   1.254 +    void NextVisibleNode(nsIDOMNode *aNode,
   1.255 +                         int32_t aOffset,
   1.256 +                         nsCOMPtr<nsIDOMNode> *outVisNode,
   1.257 +                         int32_t *outVisOffset,
   1.258 +                         WSType *outType);
   1.259 +    
   1.260 +    // AdjustWhitespace examines the ws object for nbsp's that can
   1.261 +    // be safely converted to regular ascii space and converts them.
   1.262 +    nsresult AdjustWhitespace();
   1.263 +
   1.264 +  protected:
   1.265 +    
   1.266 +    // WSFragment struct ---------------------------------------------------------
   1.267 +    // WSFragment represents a single run of ws (all leadingws, or all normalws,
   1.268 +    // or all trailingws, or all leading+trailingws).  Note that this single run may
   1.269 +    // still span multiple nodes.
   1.270 +    struct WSFragment
   1.271 +    {
   1.272 +      nsCOMPtr<nsIDOMNode> mStartNode;  // node where ws run starts
   1.273 +      nsCOMPtr<nsIDOMNode> mEndNode;    // node where ws run ends
   1.274 +      int32_t mStartOffset;             // offset where ws run starts
   1.275 +      int32_t mEndOffset;               // offset where ws run ends
   1.276 +      // type of ws, and what is to left and right of it
   1.277 +      WSType mType, mLeftType, mRightType;
   1.278 +      // other ws runs to left or right.  may be null.
   1.279 +      WSFragment *mLeft, *mRight;
   1.280 +
   1.281 +      WSFragment() : mStartNode(0), mEndNode(0),
   1.282 +                     mStartOffset(0), mEndOffset(0),
   1.283 +                     mType(), mLeftType(), mRightType(),
   1.284 +                     mLeft(0), mRight(0)
   1.285 +      {
   1.286 +      }
   1.287 +    };
   1.288 +    
   1.289 +    // WSPoint struct ------------------------------------------------------------
   1.290 +    // A WSPoint struct represents a unique location within the ws run.  It is 
   1.291 +    // always within a textnode that is one of the nodes stored in the list
   1.292 +    // in the wsRunObject.  For convenience, the character at that point is also 
   1.293 +    // stored in the struct.
   1.294 +    struct MOZ_STACK_CLASS WSPoint
   1.295 +    {
   1.296 +      nsCOMPtr<nsIContent> mTextNode;
   1.297 +      uint32_t mOffset;
   1.298 +      char16_t mChar;
   1.299 +
   1.300 +      WSPoint() : mTextNode(0),mOffset(0),mChar(0) {}
   1.301 +      WSPoint(nsIDOMNode *aNode, int32_t aOffset, char16_t aChar) : 
   1.302 +                     mTextNode(do_QueryInterface(aNode)),mOffset(aOffset),mChar(aChar)
   1.303 +      {
   1.304 +        if (!mTextNode->IsNodeOfType(nsINode::eDATA_NODE)) {
   1.305 +          // Not sure if this is needed, but it'll maintain the same
   1.306 +          // functionality
   1.307 +          mTextNode = nullptr;
   1.308 +        }
   1.309 +      }
   1.310 +      WSPoint(nsIContent *aTextNode, int32_t aOffset, char16_t aChar) : 
   1.311 +                     mTextNode(aTextNode),mOffset(aOffset),mChar(aChar) {}
   1.312 +    };    
   1.313 +
   1.314 +    enum AreaRestriction
   1.315 +    {
   1.316 +      eAnywhere, eOutsideUserSelectAll
   1.317 +    };    
   1.318 +    
   1.319 +    // protected methods ---------------------------------------------------------
   1.320 +    // tons of utility methods.  
   1.321 +
   1.322 +    /**
   1.323 +     * Return the node which we will handle white-space under. This is the
   1.324 +     * closest block within the DOM subtree we're editing, or if none is
   1.325 +     * found, the (inline) root of the editable subtree.
   1.326 +     */
   1.327 +    already_AddRefed<nsIDOMNode> GetWSBoundingParent();
   1.328 +
   1.329 +    nsresult GetWSNodes();
   1.330 +    void     GetRuns();
   1.331 +    void     ClearRuns();
   1.332 +    void     MakeSingleWSRun(WSType aType);
   1.333 +    nsresult PrependNodeToList(nsIDOMNode *aNode);
   1.334 +    nsresult AppendNodeToList(nsIDOMNode *aNode);
   1.335 +    nsresult GetPreviousWSNode(nsIDOMNode *aStartNode, 
   1.336 +                               nsIDOMNode *aBlockParent, 
   1.337 +                               nsCOMPtr<nsIDOMNode> *aPriorNode);
   1.338 +    nsresult GetPreviousWSNode(nsIDOMNode *aStartNode,
   1.339 +                               int32_t      aOffset,
   1.340 +                               nsIDOMNode  *aBlockParent, 
   1.341 +                               nsCOMPtr<nsIDOMNode> *aPriorNode);
   1.342 +    nsresult GetPreviousWSNode(::DOMPoint aPoint,
   1.343 +                               nsIDOMNode  *aBlockParent, 
   1.344 +                               nsCOMPtr<nsIDOMNode> *aPriorNode);
   1.345 +    nsresult GetNextWSNode(nsIDOMNode *aStartNode, 
   1.346 +                           nsIDOMNode *aBlockParent, 
   1.347 +                           nsCOMPtr<nsIDOMNode> *aNextNode);
   1.348 +    nsresult GetNextWSNode(nsIDOMNode *aStartNode,
   1.349 +                           int32_t     aOffset,
   1.350 +                           nsIDOMNode *aBlockParent, 
   1.351 +                           nsCOMPtr<nsIDOMNode> *aNextNode);
   1.352 +    nsresult GetNextWSNode(::DOMPoint aPoint,
   1.353 +                           nsIDOMNode  *aBlockParent, 
   1.354 +                           nsCOMPtr<nsIDOMNode> *aNextNode);
   1.355 +    nsresult PrepareToDeleteRangePriv(nsWSRunObject* aEndObject);
   1.356 +    nsresult PrepareToSplitAcrossBlocksPriv();
   1.357 +    nsresult DeleteChars(nsIDOMNode *aStartNode, int32_t aStartOffset, 
   1.358 +                         nsIDOMNode *aEndNode, int32_t aEndOffset,
   1.359 +                         AreaRestriction aAR = eAnywhere);
   1.360 +    WSPoint  GetCharAfter(nsIDOMNode *aNode, int32_t aOffset);
   1.361 +    WSPoint  GetCharBefore(nsIDOMNode *aNode, int32_t aOffset);
   1.362 +    WSPoint  GetCharAfter(const WSPoint &aPoint);
   1.363 +    WSPoint  GetCharBefore(const WSPoint &aPoint);
   1.364 +    nsresult ConvertToNBSP(WSPoint aPoint,
   1.365 +                           AreaRestriction aAR = eAnywhere);
   1.366 +    void     GetAsciiWSBounds(int16_t aDir, nsIDOMNode *aNode, int32_t aOffset,
   1.367 +                                nsCOMPtr<nsIDOMNode> *outStartNode, int32_t *outStartOffset,
   1.368 +                                nsCOMPtr<nsIDOMNode> *outEndNode, int32_t *outEndOffset);
   1.369 +    void     FindRun(nsIDOMNode *aNode, int32_t aOffset, WSFragment **outRun, bool after);
   1.370 +    char16_t GetCharAt(nsIContent *aTextNode, int32_t aOffset);
   1.371 +    WSPoint  GetWSPointAfter(nsIDOMNode *aNode, int32_t aOffset);
   1.372 +    WSPoint  GetWSPointBefore(nsIDOMNode *aNode, int32_t aOffset);
   1.373 +    nsresult CheckTrailingNBSPOfRun(WSFragment *aRun);
   1.374 +    nsresult CheckTrailingNBSP(WSFragment *aRun, nsIDOMNode *aNode, int32_t aOffset);
   1.375 +    nsresult CheckLeadingNBSP(WSFragment *aRun, nsIDOMNode *aNode, int32_t aOffset);
   1.376 +    
   1.377 +    static nsresult ScrubBlockBoundaryInner(nsHTMLEditor *aHTMLEd, 
   1.378 +                                       nsCOMPtr<nsIDOMNode> *aBlock,
   1.379 +                                       BlockBoundary aBoundary);
   1.380 +    nsresult Scrub();
   1.381 +    
   1.382 +    // member variables ---------------------------------------------------------
   1.383 +    
   1.384 +    nsCOMPtr<nsIDOMNode> mNode;           // the node passed to our constructor
   1.385 +    int32_t mOffset;                      // the offset passed to our contructor
   1.386 +    // together, the above represent the point at which we are building up ws info.
   1.387 +    
   1.388 +    bool    mPRE;                         // true if we are in preformatted whitespace context
   1.389 +    nsCOMPtr<nsIDOMNode> mStartNode;      // node/offset where ws starts
   1.390 +    int32_t mStartOffset;                 // ...
   1.391 +    WSType mStartReason;                  // reason why ws starts (eText, eOtherBlock, etc)
   1.392 +    nsCOMPtr<nsIDOMNode> mStartReasonNode;// the node that implicated by start reason
   1.393 +    
   1.394 +    nsCOMPtr<nsIDOMNode> mEndNode;        // node/offset where ws ends
   1.395 +    int32_t mEndOffset;                   // ...
   1.396 +    WSType mEndReason;                    // reason why ws ends (eText, eOtherBlock, etc)
   1.397 +    nsCOMPtr<nsIDOMNode> mEndReasonNode;  // the node that implicated by end reason
   1.398 +    
   1.399 +    nsCOMPtr<nsIDOMNode> mFirstNBSPNode;  // location of first nbsp in ws run, if any
   1.400 +    int32_t mFirstNBSPOffset;             // ...
   1.401 +    
   1.402 +    nsCOMPtr<nsIDOMNode> mLastNBSPNode;   // location of last nbsp in ws run, if any
   1.403 +    int32_t mLastNBSPOffset;              // ...
   1.404 +    
   1.405 +    nsCOMArray<nsIDOMNode> mNodeArray;//the list of nodes containing ws in this run
   1.406 +    
   1.407 +    WSFragment *mStartRun;                // the first WSFragment in the run
   1.408 +    WSFragment *mEndRun;                  // the last WSFragment in the run, may be same as first
   1.409 +    
   1.410 +    nsHTMLEditor *mHTMLEditor;            // non-owning.
   1.411 +    
   1.412 +    friend class nsHTMLEditRules;  // opening this class up for pillaging
   1.413 +    friend class nsHTMLEditor;     // opening this class up for more pillaging
   1.414 +};
   1.415 +
   1.416 +#endif
   1.417 +

mercurial