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 mozilla_TypeInState_h
7 #define mozilla_TypeInState_h
8 
9 #include "mozilla/EditorDOMPoint.h"
10 #include "mozilla/EventForwards.h"
11 #include "mozilla/UniquePtr.h"
12 #include "nsCOMPtr.h"
13 #include "nsCycleCollectionParticipant.h"
14 #include "nsGkAtoms.h"
15 #include "nsISupportsImpl.h"
16 #include "nsString.h"
17 #include "nsTArray.h"
18 #include "nscore.h"
19 
20 // Workaround for windows headers
21 #ifdef SetProp
22 #  undef SetProp
23 #endif
24 
25 class nsAtom;
26 class nsINode;
27 
28 namespace mozilla {
29 class HTMLEditor;
30 namespace dom {
31 class MouseEvent;
32 class Selection;
33 }  // namespace dom
34 
35 enum class SpecifiedStyle : uint8_t { Preserve, Discard };
36 
37 struct PropItem {
38   nsAtom* tag;
39   nsAtom* attr;
40   nsString value;
41   // Whether the class and style attribute should be perserved or discarded.
42   SpecifiedStyle specifiedStyle;
43 
PropItemPropItem44   PropItem()
45       : tag(nullptr), attr(nullptr), specifiedStyle(SpecifiedStyle::Preserve) {
46     MOZ_COUNT_CTOR(PropItem);
47   }
PropItemPropItem48   PropItem(nsAtom* aTag, nsAtom* aAttr, const nsAString& aValue)
49       : tag(aTag),
50         attr(aAttr != nsGkAtoms::_empty ? aAttr : nullptr),
51         value(aValue),
52         specifiedStyle(SpecifiedStyle::Preserve) {
53     MOZ_COUNT_CTOR(PropItem);
54   }
55   MOZ_COUNTED_DTOR(PropItem)
56 };
57 
58 class StyleCache final {
59  public:
60   StyleCache() = delete;
StyleCache(nsStaticAtom * aTag,nsStaticAtom * aAttribute,const nsAString & aValue)61   StyleCache(nsStaticAtom* aTag, nsStaticAtom* aAttribute,
62              const nsAString& aValue)
63       : mTag(aTag), mAttribute(aAttribute), mValue(aValue) {
64     MOZ_ASSERT(mTag);
65   }
66 
Tag()67   nsStaticAtom* Tag() const { return mTag; }
GetAttribute()68   nsStaticAtom* GetAttribute() const { return mAttribute; }
Value()69   const nsString& Value() const { return mValue; }
70 
71  private:
72   nsStaticAtom* mTag;
73   nsStaticAtom* mAttribute;
74   nsString mValue;
75 };
76 
77 class MOZ_STACK_CLASS AutoStyleCacheArray final
78     : public AutoTArray<StyleCache, 21> {
79  public:
IndexOf(const nsStaticAtom * aTag,const nsStaticAtom * aAttribute)80   index_type IndexOf(const nsStaticAtom* aTag,
81                      const nsStaticAtom* aAttribute) const {
82     for (index_type index = 0; index < Length(); ++index) {
83       const StyleCache& styleCache = ElementAt(index);
84       if (styleCache.Tag() == aTag && styleCache.GetAttribute() == aAttribute) {
85         return index;
86       }
87     }
88     return NoIndex;
89   }
90 };
91 
92 class TypeInState final {
93  public:
94   NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(TypeInState)
95   NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(TypeInState)
96 
97   TypeInState();
98   void Reset();
99 
100   nsresult UpdateSelState(dom::Selection& aSelection);
101 
102   /**
103    * PreHandleMouseEvent() is called when `HTMLEditorEventListener` receives
104    * "mousedown" and "mouseup" events.  Note that `aMouseDownOrUpEvent` may not
105    * be acceptable event for the `HTMLEditor`, but this is called even in
106    * the case because the event may cause a following `OnSelectionChange()`
107    * call.
108    */
109   void PreHandleMouseEvent(const dom::MouseEvent& aMouseDownOrUpEvent);
110 
111   void PreHandleSelectionChangeCommand(Command aCommand);
112   void PostHandleSelectionChangeCommand(const HTMLEditor& aHTMLEditor,
113                                         Command aCommand);
114 
115   void OnSelectionChange(const HTMLEditor& aHTMLEditor, int16_t aReason);
116 
117   void SetProp(nsAtom* aProp, nsAtom* aAttr, const nsAString& aValue);
118 
119   void ClearAllProps();
120   void ClearProp(nsAtom* aProp, nsAtom* aAttr);
121   void ClearLinkPropAndDiscardItsSpecifiedStyle();
122 
123   /**
124    * TakeClearProperty() hands back next property item on the clear list.
125    * Caller assumes ownership of PropItem and must delete it.
126    */
127   UniquePtr<PropItem> TakeClearProperty();
128 
129   /**
130    * TakeSetProperty() hands back next property item on the set list.
131    * Caller assumes ownership of PropItem and must delete it.
132    */
133   UniquePtr<PropItem> TakeSetProperty();
134 
135   /**
136    * TakeRelativeFontSize() hands back relative font value, which is then
137    * cleared out.
138    */
139   int32_t TakeRelativeFontSize();
140 
141   void GetTypingState(bool& isSet, bool& theSetting, nsAtom* aProp,
142                       nsAtom* aAttr = nullptr, nsString* outValue = nullptr);
143 
144   static bool FindPropInList(nsAtom* aProp, nsAtom* aAttr, nsAString* outValue,
145                              const nsTArray<PropItem*>& aList,
146                              int32_t& outIndex);
147 
148  protected:
149   virtual ~TypeInState();
150 
151   void RemovePropFromSetList(nsAtom* aProp, nsAtom* aAttr);
152   void RemovePropFromClearedList(nsAtom* aProp, nsAtom* aAttr);
153   bool IsPropSet(nsAtom* aProp, nsAtom* aAttr, nsAString* outValue);
154   bool IsPropSet(nsAtom* aProp, nsAtom* aAttr, nsAString* outValue,
155                  int32_t& outIndex);
156   bool IsPropCleared(nsAtom* aProp, nsAtom* aAttr);
157   bool IsPropCleared(nsAtom* aProp, nsAtom* aAttr, int32_t& outIndex);
158 
IsLinkStyleSet()159   bool IsLinkStyleSet() const {
160     int32_t unusedIndex = -1;
161     return FindPropInList(nsGkAtoms::a, nullptr, nullptr, mSetArray,
162                           unusedIndex);
163   }
IsExplicitlyLinkStyleCleared()164   bool IsExplicitlyLinkStyleCleared() const {
165     int32_t unusedIndex = -1;
166     return FindPropInList(nsGkAtoms::a, nullptr, nullptr, mClearedArray,
167                           unusedIndex);
168   }
IsOnlyLinkStyleCleared()169   bool IsOnlyLinkStyleCleared() const {
170     return mClearedArray.Length() == 1 && IsExplicitlyLinkStyleCleared();
171   }
AreAllStylesCleared()172   bool AreAllStylesCleared() const {
173     int32_t unusedIndex = -1;
174     return FindPropInList(nullptr, nullptr, nullptr, mClearedArray,
175                           unusedIndex);
176   }
AreSomeStylesSet()177   bool AreSomeStylesSet() const { return !mSetArray.IsEmpty(); }
AreSomeStylesCleared()178   bool AreSomeStylesCleared() const { return !mClearedArray.IsEmpty(); }
179 
180   nsTArray<PropItem*> mSetArray;
181   nsTArray<PropItem*> mClearedArray;
182   EditorDOMPoint mLastSelectionPoint;
183   int32_t mRelativeFontSize;
184   Command mLastSelectionCommand;
185   bool mMouseDownFiredInLinkElement;
186   bool mMouseUpFiredInLinkElement;
187 };
188 
189 }  // namespace mozilla
190 
191 #endif  // #ifndef mozilla_TypeInState_h
192