1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/layout/mathml/nsMathMLmactionFrame.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,343 @@ 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 +#include "nsMathMLmactionFrame.h" 1.10 +#include "nsCOMPtr.h" 1.11 +#include "nsPresContext.h" 1.12 +#include "nsNameSpaceManager.h" 1.13 +#include "prprf.h" // For PR_snprintf() 1.14 +#include "nsIDocShell.h" 1.15 +#include "nsIDocShellTreeOwner.h" 1.16 +#include "nsIWebBrowserChrome.h" 1.17 +#include "nsIInterfaceRequestorUtils.h" 1.18 +#include "nsTextFragment.h" 1.19 +#include "nsIDOMEvent.h" 1.20 +#include "mozilla/gfx/2D.h" 1.21 + 1.22 +// 1.23 +// <maction> -- bind actions to a subexpression - implementation 1.24 +// 1.25 + 1.26 +enum nsMactionActionTypes { 1.27 + NS_MATHML_ACTION_TYPE_CLASS_ERROR = 0x10, 1.28 + NS_MATHML_ACTION_TYPE_CLASS_USE_SELECTION = 0x20, 1.29 + NS_MATHML_ACTION_TYPE_CLASS_IGNORE_SELECTION = 0x40, 1.30 + NS_MATHML_ACTION_TYPE_CLASS_BITMASK = 0xF0, 1.31 + 1.32 + NS_MATHML_ACTION_TYPE_NONE = NS_MATHML_ACTION_TYPE_CLASS_ERROR|0x01, 1.33 + 1.34 + NS_MATHML_ACTION_TYPE_TOGGLE = NS_MATHML_ACTION_TYPE_CLASS_USE_SELECTION|0x01, 1.35 + NS_MATHML_ACTION_TYPE_UNKNOWN = NS_MATHML_ACTION_TYPE_CLASS_USE_SELECTION|0x02, 1.36 + 1.37 + NS_MATHML_ACTION_TYPE_STATUSLINE = NS_MATHML_ACTION_TYPE_CLASS_IGNORE_SELECTION|0x01, 1.38 + NS_MATHML_ACTION_TYPE_TOOLTIP = NS_MATHML_ACTION_TYPE_CLASS_IGNORE_SELECTION|0x02 1.39 +}; 1.40 + 1.41 + 1.42 +// helper function to parse actiontype attribute 1.43 +static int32_t 1.44 +GetActionType(nsIContent* aContent) 1.45 +{ 1.46 + nsAutoString value; 1.47 + 1.48 + if (aContent) { 1.49 + if (!aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::actiontype_, value)) 1.50 + return NS_MATHML_ACTION_TYPE_NONE; 1.51 + } 1.52 + 1.53 + if (value.EqualsLiteral("toggle")) 1.54 + return NS_MATHML_ACTION_TYPE_TOGGLE; 1.55 + if (value.EqualsLiteral("statusline")) 1.56 + return NS_MATHML_ACTION_TYPE_STATUSLINE; 1.57 + if (value.EqualsLiteral("tooltip")) 1.58 + return NS_MATHML_ACTION_TYPE_TOOLTIP; 1.59 + 1.60 + return NS_MATHML_ACTION_TYPE_UNKNOWN; 1.61 +} 1.62 + 1.63 +nsIFrame* 1.64 +NS_NewMathMLmactionFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) 1.65 +{ 1.66 + return new (aPresShell) nsMathMLmactionFrame(aContext); 1.67 +} 1.68 + 1.69 +NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmactionFrame) 1.70 + 1.71 +nsMathMLmactionFrame::~nsMathMLmactionFrame() 1.72 +{ 1.73 + // unregister us as a mouse event listener ... 1.74 + // printf("maction:%p unregistering as mouse event listener ...\n", this); 1.75 + if (mListener) { 1.76 + mContent->RemoveSystemEventListener(NS_LITERAL_STRING("click"), mListener, 1.77 + false); 1.78 + mContent->RemoveSystemEventListener(NS_LITERAL_STRING("mouseover"), mListener, 1.79 + false); 1.80 + mContent->RemoveSystemEventListener(NS_LITERAL_STRING("mouseout"), mListener, 1.81 + false); 1.82 + } 1.83 +} 1.84 + 1.85 +void 1.86 +nsMathMLmactionFrame::Init(nsIContent* aContent, 1.87 + nsIFrame* aParent, 1.88 + nsIFrame* aPrevInFlow) 1.89 +{ 1.90 + // Init our local attributes 1.91 + 1.92 + mChildCount = -1; // these will be updated in GetSelectedFrame() 1.93 + mActionType = GetActionType(aContent); 1.94 + 1.95 + // Let the base class do the rest 1.96 + return nsMathMLSelectedFrame::Init(aContent, aParent, aPrevInFlow); 1.97 +} 1.98 + 1.99 +nsresult 1.100 +nsMathMLmactionFrame::ChildListChanged(int32_t aModType) 1.101 +{ 1.102 + // update cached values 1.103 + mChildCount = -1; 1.104 + mSelectedFrame = nullptr; 1.105 + 1.106 + return nsMathMLSelectedFrame::ChildListChanged(aModType); 1.107 +} 1.108 + 1.109 +// return the frame whose number is given by the attribute selection="number" 1.110 +nsIFrame* 1.111 +nsMathMLmactionFrame::GetSelectedFrame() 1.112 +{ 1.113 + nsAutoString value; 1.114 + int32_t selection; 1.115 + 1.116 + if ((mActionType & NS_MATHML_ACTION_TYPE_CLASS_BITMASK) == 1.117 + NS_MATHML_ACTION_TYPE_CLASS_ERROR) { 1.118 + mSelection = -1; 1.119 + mInvalidMarkup = true; 1.120 + mSelectedFrame = nullptr; 1.121 + return mSelectedFrame; 1.122 + } 1.123 + 1.124 + // Selection is not applied to tooltip and statusline. 1.125 + // Thereby return the first child. 1.126 + if ((mActionType & NS_MATHML_ACTION_TYPE_CLASS_BITMASK) == 1.127 + NS_MATHML_ACTION_TYPE_CLASS_IGNORE_SELECTION) { 1.128 + // We don't touch mChildCount here. It's incorrect to assign it 1, 1.129 + // and it's inefficient to count the children. It's fine to leave 1.130 + // it be equal -1 because it's not used with other actiontypes. 1.131 + mSelection = 1; 1.132 + mInvalidMarkup = false; 1.133 + mSelectedFrame = mFrames.FirstChild(); 1.134 + return mSelectedFrame; 1.135 + } 1.136 + 1.137 + mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::selection_, value); 1.138 + if (!value.IsEmpty()) { 1.139 + nsresult errorCode; 1.140 + selection = value.ToInteger(&errorCode); 1.141 + if (NS_FAILED(errorCode)) 1.142 + selection = 1; 1.143 + } 1.144 + else selection = 1; // default is first frame 1.145 + 1.146 + if (-1 != mChildCount) { // we have been in this function before... 1.147 + // cater for invalid user-supplied selection 1.148 + if (selection > mChildCount || selection < 1) 1.149 + selection = -1; 1.150 + // quick return if it is identical with our cache 1.151 + if (selection == mSelection) 1.152 + return mSelectedFrame; 1.153 + } 1.154 + 1.155 + // get the selected child and cache new values... 1.156 + int32_t count = 0; 1.157 + nsIFrame* childFrame = mFrames.FirstChild(); 1.158 + while (childFrame) { 1.159 + if (!mSelectedFrame) 1.160 + mSelectedFrame = childFrame; // default is first child 1.161 + if (++count == selection) 1.162 + mSelectedFrame = childFrame; 1.163 + 1.164 + childFrame = childFrame->GetNextSibling(); 1.165 + } 1.166 + // cater for invalid user-supplied selection 1.167 + if (selection > count || selection < 1) 1.168 + selection = -1; 1.169 + 1.170 + mChildCount = count; 1.171 + mSelection = selection; 1.172 + mInvalidMarkup = (mSelection == -1); 1.173 + TransmitAutomaticData(); 1.174 + 1.175 + return mSelectedFrame; 1.176 +} 1.177 + 1.178 +nsresult 1.179 +nsMathMLmactionFrame::SetInitialChildList(ChildListID aListID, 1.180 + nsFrameList& aChildList) 1.181 +{ 1.182 + nsresult rv = nsMathMLSelectedFrame::SetInitialChildList(aListID, aChildList); 1.183 + 1.184 + if (!mSelectedFrame) { 1.185 + mActionType = NS_MATHML_ACTION_TYPE_NONE; 1.186 + } 1.187 + else { 1.188 + // create mouse event listener and register it 1.189 + mListener = new nsMathMLmactionFrame::MouseListener(this); 1.190 + // printf("maction:%p registering as mouse event listener ...\n", this); 1.191 + mContent->AddSystemEventListener(NS_LITERAL_STRING("click"), mListener, 1.192 + false, false); 1.193 + mContent->AddSystemEventListener(NS_LITERAL_STRING("mouseover"), mListener, 1.194 + false, false); 1.195 + mContent->AddSystemEventListener(NS_LITERAL_STRING("mouseout"), mListener, 1.196 + false, false); 1.197 + } 1.198 + return rv; 1.199 +} 1.200 + 1.201 +nsresult 1.202 +nsMathMLmactionFrame::AttributeChanged(int32_t aNameSpaceID, 1.203 + nsIAtom* aAttribute, 1.204 + int32_t aModType) 1.205 +{ 1.206 + bool needsReflow = false; 1.207 + 1.208 + if (aAttribute == nsGkAtoms::actiontype_) { 1.209 + // updating mActionType ... 1.210 + int32_t oldActionType = mActionType; 1.211 + mActionType = GetActionType(mContent); 1.212 + 1.213 + // Initiate a reflow when actiontype classes are different. 1.214 + if ((oldActionType & NS_MATHML_ACTION_TYPE_CLASS_BITMASK) != 1.215 + (mActionType & NS_MATHML_ACTION_TYPE_CLASS_BITMASK)) { 1.216 + needsReflow = true; 1.217 + } 1.218 + } else if (aAttribute == nsGkAtoms::selection_) { 1.219 + if ((mActionType & NS_MATHML_ACTION_TYPE_CLASS_BITMASK) == 1.220 + NS_MATHML_ACTION_TYPE_CLASS_USE_SELECTION) { 1.221 + needsReflow = true; 1.222 + } 1.223 + } else { 1.224 + // let the base class handle other attribute changes 1.225 + return 1.226 + nsMathMLContainerFrame::AttributeChanged(aNameSpaceID, 1.227 + aAttribute, aModType); 1.228 + } 1.229 + 1.230 + if (needsReflow) { 1.231 + PresContext()->PresShell()-> 1.232 + FrameNeedsReflow(this, nsIPresShell::eTreeChange, NS_FRAME_IS_DIRTY); 1.233 + } 1.234 + 1.235 + return NS_OK; 1.236 +} 1.237 + 1.238 +// ################################################################ 1.239 +// Event handlers 1.240 +// ################################################################ 1.241 + 1.242 +NS_IMPL_ISUPPORTS(nsMathMLmactionFrame::MouseListener, 1.243 + nsIDOMEventListener) 1.244 + 1.245 + 1.246 +// helper to show a msg on the status bar 1.247 +// curled from nsObjectFrame.cpp ... 1.248 +void 1.249 +ShowStatus(nsPresContext* aPresContext, nsString& aStatusMsg) 1.250 +{ 1.251 + nsCOMPtr<nsIDocShellTreeItem> docShellItem(aPresContext->GetDocShell()); 1.252 + if (docShellItem) { 1.253 + nsCOMPtr<nsIDocShellTreeOwner> treeOwner; 1.254 + docShellItem->GetTreeOwner(getter_AddRefs(treeOwner)); 1.255 + if (treeOwner) { 1.256 + nsCOMPtr<nsIWebBrowserChrome> browserChrome(do_GetInterface(treeOwner)); 1.257 + if (browserChrome) { 1.258 + browserChrome->SetStatus(nsIWebBrowserChrome::STATUS_LINK, aStatusMsg.get()); 1.259 + } 1.260 + } 1.261 + } 1.262 +} 1.263 + 1.264 +NS_IMETHODIMP 1.265 +nsMathMLmactionFrame::MouseListener::HandleEvent(nsIDOMEvent* aEvent) 1.266 +{ 1.267 + nsAutoString eventType; 1.268 + aEvent->GetType(eventType); 1.269 + if (eventType.EqualsLiteral("mouseover")) { 1.270 + mOwner->MouseOver(); 1.271 + } 1.272 + else if (eventType.EqualsLiteral("click")) { 1.273 + mOwner->MouseClick(); 1.274 + } 1.275 + else if (eventType.EqualsLiteral("mouseout")) { 1.276 + mOwner->MouseOut(); 1.277 + } 1.278 + else { 1.279 + NS_ABORT(); 1.280 + } 1.281 + 1.282 + return NS_OK; 1.283 +} 1.284 + 1.285 +void 1.286 +nsMathMLmactionFrame::MouseOver() 1.287 +{ 1.288 + // see if we should display a status message 1.289 + if (NS_MATHML_ACTION_TYPE_STATUSLINE == mActionType) { 1.290 + // retrieve content from a second child if it exists 1.291 + nsIFrame* childFrame = mFrames.FrameAt(1); 1.292 + if (!childFrame) return; 1.293 + 1.294 + nsIContent* content = childFrame->GetContent(); 1.295 + if (!content) return; 1.296 + 1.297 + // check whether the content is mtext or not 1.298 + if (content->GetNameSpaceID() == kNameSpaceID_MathML && 1.299 + content->Tag() == nsGkAtoms::mtext_) { 1.300 + // get the text to be displayed 1.301 + content = content->GetFirstChild(); 1.302 + if (!content) return; 1.303 + 1.304 + const nsTextFragment* textFrg = content->GetText(); 1.305 + if (!textFrg) return; 1.306 + 1.307 + nsAutoString text; 1.308 + textFrg->AppendTo(text); 1.309 + // collapse whitespaces as listed in REC, section 3.2.6.1 1.310 + text.CompressWhitespace(); 1.311 + ShowStatus(PresContext(), text); 1.312 + } 1.313 + } 1.314 +} 1.315 + 1.316 +void 1.317 +nsMathMLmactionFrame::MouseOut() 1.318 +{ 1.319 + // see if we should remove the status message 1.320 + if (NS_MATHML_ACTION_TYPE_STATUSLINE == mActionType) { 1.321 + nsAutoString value; 1.322 + value.SetLength(0); 1.323 + ShowStatus(PresContext(), value); 1.324 + } 1.325 +} 1.326 + 1.327 +void 1.328 +nsMathMLmactionFrame::MouseClick() 1.329 +{ 1.330 + if (NS_MATHML_ACTION_TYPE_TOGGLE == mActionType) { 1.331 + if (mChildCount > 1) { 1.332 + int32_t selection = (mSelection == mChildCount)? 1 : mSelection + 1; 1.333 + nsAutoString value; 1.334 + char cbuf[10]; 1.335 + PR_snprintf(cbuf, sizeof(cbuf), "%d", selection); 1.336 + value.AssignASCII(cbuf); 1.337 + bool notify = false; // don't yet notify the document 1.338 + mContent->SetAttr(kNameSpaceID_None, nsGkAtoms::selection_, value, notify); 1.339 + 1.340 + // Now trigger a content-changed reflow... 1.341 + PresContext()->PresShell()-> 1.342 + FrameNeedsReflow(mSelectedFrame, nsIPresShell::eTreeChange, 1.343 + NS_FRAME_IS_DIRTY); 1.344 + } 1.345 + } 1.346 +}