/src/mozilla-central/layout/mathml/nsMathMLmactionFrame.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
3 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
4 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
5 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | |
7 | | #include "nsMathMLmactionFrame.h" |
8 | | #include "nsCOMPtr.h" |
9 | | #include "nsPresContext.h" |
10 | | #include "nsNameSpaceManager.h" |
11 | | #include "nsIDocShell.h" |
12 | | #include "nsIDocShellTreeOwner.h" |
13 | | #include "nsIWebBrowserChrome.h" |
14 | | #include "nsIInterfaceRequestorUtils.h" |
15 | | #include "nsTextFragment.h" |
16 | | #include "mozilla/gfx/2D.h" |
17 | | #include "mozilla/dom/Event.h" |
18 | | |
19 | | using mozilla::dom::Event; |
20 | | |
21 | | // |
22 | | // <maction> -- bind actions to a subexpression - implementation |
23 | | // |
24 | | |
25 | | enum nsMactionActionTypes { |
26 | | NS_MATHML_ACTION_TYPE_CLASS_ERROR = 0x10, |
27 | | NS_MATHML_ACTION_TYPE_CLASS_USE_SELECTION = 0x20, |
28 | | NS_MATHML_ACTION_TYPE_CLASS_IGNORE_SELECTION = 0x40, |
29 | | NS_MATHML_ACTION_TYPE_CLASS_BITMASK = 0xF0, |
30 | | |
31 | | NS_MATHML_ACTION_TYPE_NONE = NS_MATHML_ACTION_TYPE_CLASS_ERROR|0x01, |
32 | | |
33 | | NS_MATHML_ACTION_TYPE_TOGGLE = NS_MATHML_ACTION_TYPE_CLASS_USE_SELECTION|0x01, |
34 | | NS_MATHML_ACTION_TYPE_UNKNOWN = NS_MATHML_ACTION_TYPE_CLASS_USE_SELECTION|0x02, |
35 | | |
36 | | NS_MATHML_ACTION_TYPE_STATUSLINE = NS_MATHML_ACTION_TYPE_CLASS_IGNORE_SELECTION|0x01, |
37 | | NS_MATHML_ACTION_TYPE_TOOLTIP = NS_MATHML_ACTION_TYPE_CLASS_IGNORE_SELECTION|0x02 |
38 | | }; |
39 | | |
40 | | |
41 | | // helper function to parse actiontype attribute |
42 | | static int32_t |
43 | | GetActionType(nsIContent* aContent) |
44 | 0 | { |
45 | 0 | nsAutoString value; |
46 | 0 |
|
47 | 0 | if (aContent) { |
48 | 0 | if (!aContent->IsElement() || |
49 | 0 | !aContent->AsElement()->GetAttr(kNameSpaceID_None, |
50 | 0 | nsGkAtoms::actiontype_, |
51 | 0 | value)) |
52 | 0 | return NS_MATHML_ACTION_TYPE_NONE; |
53 | 0 | } |
54 | 0 | |
55 | 0 | if (value.EqualsLiteral("toggle")) |
56 | 0 | return NS_MATHML_ACTION_TYPE_TOGGLE; |
57 | 0 | if (value.EqualsLiteral("statusline")) |
58 | 0 | return NS_MATHML_ACTION_TYPE_STATUSLINE; |
59 | 0 | if (value.EqualsLiteral("tooltip")) |
60 | 0 | return NS_MATHML_ACTION_TYPE_TOOLTIP; |
61 | 0 | |
62 | 0 | return NS_MATHML_ACTION_TYPE_UNKNOWN; |
63 | 0 | } |
64 | | |
65 | | nsIFrame* |
66 | | NS_NewMathMLmactionFrame(nsIPresShell* aPresShell, ComputedStyle* aStyle) |
67 | 0 | { |
68 | 0 | return new (aPresShell) nsMathMLmactionFrame(aStyle); |
69 | 0 | } |
70 | | |
71 | | NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmactionFrame) |
72 | | |
73 | | nsMathMLmactionFrame::~nsMathMLmactionFrame() |
74 | 0 | { |
75 | 0 | // unregister us as a mouse event listener ... |
76 | 0 | // printf("maction:%p unregistering as mouse event listener ...\n", this); |
77 | 0 | if (mListener) { |
78 | 0 | mContent->RemoveSystemEventListener(NS_LITERAL_STRING("click"), mListener, |
79 | 0 | false); |
80 | 0 | mContent->RemoveSystemEventListener(NS_LITERAL_STRING("mouseover"), mListener, |
81 | 0 | false); |
82 | 0 | mContent->RemoveSystemEventListener(NS_LITERAL_STRING("mouseout"), mListener, |
83 | 0 | false); |
84 | 0 | } |
85 | 0 | } |
86 | | |
87 | | void |
88 | | nsMathMLmactionFrame::Init(nsIContent* aContent, |
89 | | nsContainerFrame* aParent, |
90 | | nsIFrame* aPrevInFlow) |
91 | 0 | { |
92 | 0 | // Init our local attributes |
93 | 0 |
|
94 | 0 | mChildCount = -1; // these will be updated in GetSelectedFrame() |
95 | 0 | mActionType = GetActionType(aContent); |
96 | 0 |
|
97 | 0 | // Let the base class do the rest |
98 | 0 | return nsMathMLSelectedFrame::Init(aContent, aParent, aPrevInFlow); |
99 | 0 | } |
100 | | |
101 | | nsresult |
102 | | nsMathMLmactionFrame::ChildListChanged(int32_t aModType) |
103 | 0 | { |
104 | 0 | // update cached values |
105 | 0 | mChildCount = -1; |
106 | 0 | mSelectedFrame = nullptr; |
107 | 0 |
|
108 | 0 | return nsMathMLSelectedFrame::ChildListChanged(aModType); |
109 | 0 | } |
110 | | |
111 | | // return the frame whose number is given by the attribute selection="number" |
112 | | nsIFrame* |
113 | | nsMathMLmactionFrame::GetSelectedFrame() |
114 | 0 | { |
115 | 0 | nsAutoString value; |
116 | 0 | int32_t selection; |
117 | 0 |
|
118 | 0 | if ((mActionType & NS_MATHML_ACTION_TYPE_CLASS_BITMASK) == |
119 | 0 | NS_MATHML_ACTION_TYPE_CLASS_ERROR) { |
120 | 0 | mSelection = -1; |
121 | 0 | mInvalidMarkup = true; |
122 | 0 | mSelectedFrame = nullptr; |
123 | 0 | return mSelectedFrame; |
124 | 0 | } |
125 | 0 | |
126 | 0 | // Selection is not applied to tooltip and statusline. |
127 | 0 | // Thereby return the first child. |
128 | 0 | if ((mActionType & NS_MATHML_ACTION_TYPE_CLASS_BITMASK) == |
129 | 0 | NS_MATHML_ACTION_TYPE_CLASS_IGNORE_SELECTION) { |
130 | 0 | // We don't touch mChildCount here. It's incorrect to assign it 1, |
131 | 0 | // and it's inefficient to count the children. It's fine to leave |
132 | 0 | // it be equal -1 because it's not used with other actiontypes. |
133 | 0 | mSelection = 1; |
134 | 0 | mInvalidMarkup = false; |
135 | 0 | mSelectedFrame = mFrames.FirstChild(); |
136 | 0 | return mSelectedFrame; |
137 | 0 | } |
138 | 0 | |
139 | 0 | mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::selection_, value); |
140 | 0 | if (!value.IsEmpty()) { |
141 | 0 | nsresult errorCode; |
142 | 0 | selection = value.ToInteger(&errorCode); |
143 | 0 | if (NS_FAILED(errorCode)) |
144 | 0 | selection = 1; |
145 | 0 | } |
146 | 0 | else selection = 1; // default is first frame |
147 | 0 |
|
148 | 0 | if (-1 != mChildCount) { // we have been in this function before... |
149 | 0 | // cater for invalid user-supplied selection |
150 | 0 | if (selection > mChildCount || selection < 1) |
151 | 0 | selection = -1; |
152 | 0 | // quick return if it is identical with our cache |
153 | 0 | if (selection == mSelection) |
154 | 0 | return mSelectedFrame; |
155 | 0 | } |
156 | 0 | |
157 | 0 | // get the selected child and cache new values... |
158 | 0 | int32_t count = 0; |
159 | 0 | nsIFrame* childFrame = mFrames.FirstChild(); |
160 | 0 | while (childFrame) { |
161 | 0 | if (!mSelectedFrame) |
162 | 0 | mSelectedFrame = childFrame; // default is first child |
163 | 0 | if (++count == selection) |
164 | 0 | mSelectedFrame = childFrame; |
165 | 0 |
|
166 | 0 | childFrame = childFrame->GetNextSibling(); |
167 | 0 | } |
168 | 0 | // cater for invalid user-supplied selection |
169 | 0 | if (selection > count || selection < 1) |
170 | 0 | selection = -1; |
171 | 0 |
|
172 | 0 | mChildCount = count; |
173 | 0 | mSelection = selection; |
174 | 0 | mInvalidMarkup = (mSelection == -1); |
175 | 0 | TransmitAutomaticData(); |
176 | 0 |
|
177 | 0 | return mSelectedFrame; |
178 | 0 | } |
179 | | |
180 | | void |
181 | | nsMathMLmactionFrame::SetInitialChildList(ChildListID aListID, |
182 | | nsFrameList& aChildList) |
183 | 0 | { |
184 | 0 | nsMathMLSelectedFrame::SetInitialChildList(aListID, aChildList); |
185 | 0 |
|
186 | 0 | if (!mSelectedFrame) { |
187 | 0 | mActionType = NS_MATHML_ACTION_TYPE_NONE; |
188 | 0 | } |
189 | 0 | else { |
190 | 0 | // create mouse event listener and register it |
191 | 0 | mListener = new nsMathMLmactionFrame::MouseListener(this); |
192 | 0 | // printf("maction:%p registering as mouse event listener ...\n", this); |
193 | 0 | mContent->AddSystemEventListener(NS_LITERAL_STRING("click"), mListener, |
194 | 0 | false, false); |
195 | 0 | mContent->AddSystemEventListener(NS_LITERAL_STRING("mouseover"), mListener, |
196 | 0 | false, false); |
197 | 0 | mContent->AddSystemEventListener(NS_LITERAL_STRING("mouseout"), mListener, |
198 | 0 | false, false); |
199 | 0 | } |
200 | 0 | } |
201 | | |
202 | | nsresult |
203 | | nsMathMLmactionFrame::AttributeChanged(int32_t aNameSpaceID, |
204 | | nsAtom* aAttribute, |
205 | | int32_t aModType) |
206 | 0 | { |
207 | 0 | bool needsReflow = false; |
208 | 0 |
|
209 | 0 | InvalidateFrame(); |
210 | 0 |
|
211 | 0 | if (aAttribute == nsGkAtoms::actiontype_) { |
212 | 0 | // updating mActionType ... |
213 | 0 | int32_t oldActionType = mActionType; |
214 | 0 | mActionType = GetActionType(mContent); |
215 | 0 |
|
216 | 0 | // Initiate a reflow when actiontype classes are different. |
217 | 0 | if ((oldActionType & NS_MATHML_ACTION_TYPE_CLASS_BITMASK) != |
218 | 0 | (mActionType & NS_MATHML_ACTION_TYPE_CLASS_BITMASK)) { |
219 | 0 | needsReflow = true; |
220 | 0 | } |
221 | 0 | } else if (aAttribute == nsGkAtoms::selection_) { |
222 | 0 | if ((mActionType & NS_MATHML_ACTION_TYPE_CLASS_BITMASK) == |
223 | 0 | NS_MATHML_ACTION_TYPE_CLASS_USE_SELECTION) { |
224 | 0 | needsReflow = true; |
225 | 0 | } |
226 | 0 | } else { |
227 | 0 | // let the base class handle other attribute changes |
228 | 0 | return |
229 | 0 | nsMathMLContainerFrame::AttributeChanged(aNameSpaceID, |
230 | 0 | aAttribute, aModType); |
231 | 0 | } |
232 | 0 | |
233 | 0 | if (needsReflow) { |
234 | 0 | PresShell()-> |
235 | 0 | FrameNeedsReflow(this, nsIPresShell::eTreeChange, NS_FRAME_IS_DIRTY); |
236 | 0 | } |
237 | 0 |
|
238 | 0 | return NS_OK; |
239 | 0 | } |
240 | | |
241 | | // ################################################################ |
242 | | // Event handlers |
243 | | // ################################################################ |
244 | | |
245 | | NS_IMPL_ISUPPORTS(nsMathMLmactionFrame::MouseListener, |
246 | | nsIDOMEventListener) |
247 | | |
248 | | |
249 | | // helper to show a msg on the status bar |
250 | | // curled from nsPluginFrame.cpp ... |
251 | | static void |
252 | | ShowStatus(nsPresContext* aPresContext, nsString& aStatusMsg) |
253 | 0 | { |
254 | 0 | nsCOMPtr<nsIDocShellTreeItem> docShellItem(aPresContext->GetDocShell()); |
255 | 0 | if (docShellItem) { |
256 | 0 | nsCOMPtr<nsIDocShellTreeOwner> treeOwner; |
257 | 0 | docShellItem->GetTreeOwner(getter_AddRefs(treeOwner)); |
258 | 0 | if (treeOwner) { |
259 | 0 | nsCOMPtr<nsIWebBrowserChrome> browserChrome(do_GetInterface(treeOwner)); |
260 | 0 | if (browserChrome) { |
261 | 0 | browserChrome->SetStatus(nsIWebBrowserChrome::STATUS_LINK, aStatusMsg.get()); |
262 | 0 | } |
263 | 0 | } |
264 | 0 | } |
265 | 0 | } |
266 | | |
267 | | NS_IMETHODIMP |
268 | | nsMathMLmactionFrame::MouseListener::HandleEvent(Event* aEvent) |
269 | 0 | { |
270 | 0 | nsAutoString eventType; |
271 | 0 | aEvent->GetType(eventType); |
272 | 0 | if (eventType.EqualsLiteral("mouseover")) { |
273 | 0 | mOwner->MouseOver(); |
274 | 0 | } |
275 | 0 | else if (eventType.EqualsLiteral("click")) { |
276 | 0 | mOwner->MouseClick(); |
277 | 0 | } |
278 | 0 | else if (eventType.EqualsLiteral("mouseout")) { |
279 | 0 | mOwner->MouseOut(); |
280 | 0 | } |
281 | 0 | else { |
282 | 0 | MOZ_ASSERT_UNREACHABLE("Unexpected eventType"); |
283 | 0 | } |
284 | 0 |
|
285 | 0 | return NS_OK; |
286 | 0 | } |
287 | | |
288 | | void |
289 | | nsMathMLmactionFrame::MouseOver() |
290 | 0 | { |
291 | 0 | // see if we should display a status message |
292 | 0 | if (NS_MATHML_ACTION_TYPE_STATUSLINE == mActionType) { |
293 | 0 | // retrieve content from a second child if it exists |
294 | 0 | nsIFrame* childFrame = mFrames.FrameAt(1); |
295 | 0 | if (!childFrame) return; |
296 | 0 | |
297 | 0 | nsIContent* content = childFrame->GetContent(); |
298 | 0 | if (!content) return; |
299 | 0 | |
300 | 0 | // check whether the content is mtext or not |
301 | 0 | if (content->IsMathMLElement(nsGkAtoms::mtext_)) { |
302 | 0 | // get the text to be displayed |
303 | 0 | content = content->GetFirstChild(); |
304 | 0 | if (!content) return; |
305 | 0 | |
306 | 0 | const nsTextFragment* textFrg = content->GetText(); |
307 | 0 | if (!textFrg) return; |
308 | 0 | |
309 | 0 | nsAutoString text; |
310 | 0 | textFrg->AppendTo(text); |
311 | 0 | // collapse whitespaces as listed in REC, section 3.2.6.1 |
312 | 0 | text.CompressWhitespace(); |
313 | 0 | ShowStatus(PresContext(), text); |
314 | 0 | } |
315 | 0 | } |
316 | 0 | } |
317 | | |
318 | | void |
319 | | nsMathMLmactionFrame::MouseOut() |
320 | 0 | { |
321 | 0 | // see if we should remove the status message |
322 | 0 | if (NS_MATHML_ACTION_TYPE_STATUSLINE == mActionType) { |
323 | 0 | nsAutoString value; |
324 | 0 | value.SetLength(0); |
325 | 0 | ShowStatus(PresContext(), value); |
326 | 0 | } |
327 | 0 | } |
328 | | |
329 | | void |
330 | | nsMathMLmactionFrame::MouseClick() |
331 | 0 | { |
332 | 0 | if (NS_MATHML_ACTION_TYPE_TOGGLE == mActionType) { |
333 | 0 | if (mChildCount > 1) { |
334 | 0 | int32_t selection = (mSelection == mChildCount)? 1 : mSelection + 1; |
335 | 0 | nsAutoString value; |
336 | 0 | value.AppendInt(selection); |
337 | 0 | bool notify = false; // don't yet notify the document |
338 | 0 | mContent->AsElement()->SetAttr(kNameSpaceID_None, nsGkAtoms::selection_, |
339 | 0 | value, notify); |
340 | 0 |
|
341 | 0 | // Now trigger a content-changed reflow... |
342 | 0 | PresShell()-> |
343 | 0 | FrameNeedsReflow(mSelectedFrame, nsIPresShell::eTreeChange, |
344 | 0 | NS_FRAME_IS_DIRTY); |
345 | 0 | } |
346 | 0 | } |
347 | 0 | } |