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