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