nsMathMLmactionFrame.cpp 10 KB

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