ScrollBoxObject.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385
  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 "mozilla/dom/ScrollBoxObject.h"
  6. #include "mozilla/dom/ScrollBoxObjectBinding.h"
  7. #include "mozilla/dom/Element.h"
  8. #include "mozilla/dom/ToJSValue.h"
  9. #include "nsCOMPtr.h"
  10. #include "nsIPresShell.h"
  11. #include "nsIContent.h"
  12. #include "nsIDOMElement.h"
  13. #include "nsPresContext.h"
  14. #include "nsBox.h"
  15. #include "nsIScrollableFrame.h"
  16. namespace mozilla {
  17. namespace dom {
  18. ScrollBoxObject::ScrollBoxObject()
  19. {
  20. }
  21. ScrollBoxObject::~ScrollBoxObject()
  22. {
  23. }
  24. JSObject* ScrollBoxObject::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
  25. {
  26. return ScrollBoxObjectBinding::Wrap(aCx, this, aGivenProto);
  27. }
  28. nsIScrollableFrame* ScrollBoxObject::GetScrollFrame()
  29. {
  30. return do_QueryFrame(GetFrame(false));
  31. }
  32. void ScrollBoxObject::ScrollTo(int32_t x, int32_t y, ErrorResult& aRv)
  33. {
  34. nsIScrollableFrame* sf = GetScrollFrame();
  35. if (!sf) {
  36. aRv.Throw(NS_ERROR_FAILURE);
  37. return;
  38. }
  39. sf->ScrollToCSSPixels(CSSIntPoint(x, y));
  40. }
  41. void ScrollBoxObject::ScrollBy(int32_t dx, int32_t dy, ErrorResult& aRv)
  42. {
  43. CSSIntPoint pt;
  44. GetPosition(pt, aRv);
  45. if (aRv.Failed()) {
  46. return;
  47. }
  48. ScrollTo(pt.x + dx, pt.y + dy, aRv);
  49. }
  50. void ScrollBoxObject::ScrollByLine(int32_t dlines, ErrorResult& aRv)
  51. {
  52. nsIScrollableFrame* sf = GetScrollFrame();
  53. if (!sf) {
  54. aRv.Throw(NS_ERROR_FAILURE);
  55. return;
  56. }
  57. sf->ScrollBy(nsIntPoint(0, dlines), nsIScrollableFrame::LINES,
  58. nsIScrollableFrame::SMOOTH);
  59. }
  60. // XUL <scrollbox> elements have a single box child element.
  61. // Get a pointer to that box.
  62. // Note that now that the <scrollbox> is just a regular box
  63. // with 'overflow:hidden', the boxobject's frame is an nsXULScrollFrame,
  64. // the <scrollbox>'s box frame is the scrollframe's "scrolled frame", and
  65. // the <scrollbox>'s child box is a child of that.
  66. static nsIFrame* GetScrolledBox(BoxObject* aScrollBox) {
  67. nsIFrame* frame = aScrollBox->GetFrame(false);
  68. if (!frame) {
  69. return nullptr;
  70. }
  71. nsIScrollableFrame* scrollFrame = do_QueryFrame(frame);
  72. if (!scrollFrame) {
  73. NS_WARNING("ScrollBoxObject attached to something that's not a scroll frame!");
  74. return nullptr;
  75. }
  76. nsIFrame* scrolledFrame = scrollFrame->GetScrolledFrame();
  77. if (!scrolledFrame)
  78. return nullptr;
  79. return nsBox::GetChildXULBox(scrolledFrame);
  80. }
  81. void ScrollBoxObject::ScrollByIndex(int32_t dindexes, ErrorResult& aRv)
  82. {
  83. nsIScrollableFrame* sf = GetScrollFrame();
  84. if (!sf) {
  85. aRv.Throw(NS_ERROR_FAILURE);
  86. return;
  87. }
  88. nsIFrame* scrolledBox = GetScrolledBox(this);
  89. if (!scrolledBox) {
  90. aRv.Throw(NS_ERROR_FAILURE);
  91. return;
  92. }
  93. nsRect rect;
  94. // now get the scrolled boxes first child.
  95. nsIFrame* child = nsBox::GetChildXULBox(scrolledBox);
  96. bool horiz = scrolledBox->IsXULHorizontal();
  97. nsPoint cp = sf->GetScrollPosition();
  98. nscoord diff = 0;
  99. int32_t curIndex = 0;
  100. bool isLTR = scrolledBox->IsXULNormalDirection();
  101. int32_t frameWidth = 0;
  102. if (!isLTR && horiz) {
  103. GetWidth(&frameWidth);
  104. nsCOMPtr<nsIPresShell> shell = GetPresShell(false);
  105. if (!shell) {
  106. aRv.Throw(NS_ERROR_UNEXPECTED);
  107. return;
  108. }
  109. frameWidth = nsPresContext::CSSPixelsToAppUnits(frameWidth);
  110. }
  111. // first find out what index we are currently at
  112. while(child) {
  113. rect = child->GetRect();
  114. if (horiz) {
  115. // In the left-to-right case we break from the loop when the center of
  116. // the current child rect is greater than the scrolled position of
  117. // the left edge of the scrollbox
  118. // In the right-to-left case we break when the center of the current
  119. // child rect is less than the scrolled position of the right edge of
  120. // the scrollbox.
  121. diff = rect.x + rect.width/2; // use the center, to avoid rounding errors
  122. if ((isLTR && diff > cp.x) ||
  123. (!isLTR && diff < cp.x + frameWidth)) {
  124. break;
  125. }
  126. } else {
  127. diff = rect.y + rect.height/2;// use the center, to avoid rounding errors
  128. if (diff > cp.y) {
  129. break;
  130. }
  131. }
  132. child = nsBox::GetNextXULBox(child);
  133. curIndex++;
  134. }
  135. int32_t count = 0;
  136. if (dindexes == 0)
  137. return;
  138. if (dindexes > 0) {
  139. while(child) {
  140. child = nsBox::GetNextXULBox(child);
  141. if (child) {
  142. rect = child->GetRect();
  143. }
  144. count++;
  145. if (count >= dindexes) {
  146. break;
  147. }
  148. }
  149. } else if (dindexes < 0) {
  150. child = nsBox::GetChildXULBox(scrolledBox);
  151. while(child) {
  152. rect = child->GetRect();
  153. if (count >= curIndex + dindexes) {
  154. break;
  155. }
  156. count++;
  157. child = nsBox::GetNextXULBox(child);
  158. }
  159. }
  160. nscoord csspixel = nsPresContext::CSSPixelsToAppUnits(1);
  161. if (horiz) {
  162. // In the left-to-right case we scroll so that the left edge of the
  163. // selected child is scrolled to the left edge of the scrollbox.
  164. // In the right-to-left case we scroll so that the right edge of the
  165. // selected child is scrolled to the right edge of the scrollbox.
  166. nsPoint pt(isLTR ? rect.x : rect.x + rect.width - frameWidth,
  167. cp.y);
  168. // Use a destination range that ensures the left edge (or right edge,
  169. // for RTL) will indeed be visible. Also ensure that the top edge
  170. // is visible.
  171. nsRect range(pt.x, pt.y, csspixel, 0);
  172. if (isLTR) {
  173. range.x -= csspixel;
  174. }
  175. sf->ScrollTo(pt, nsIScrollableFrame::INSTANT, &range);
  176. } else {
  177. // Use a destination range that ensures the top edge will be visible.
  178. nsRect range(cp.x, rect.y - csspixel, 0, csspixel);
  179. sf->ScrollTo(nsPoint(cp.x, rect.y), nsIScrollableFrame::INSTANT, &range);
  180. }
  181. }
  182. void ScrollBoxObject::ScrollToLine(int32_t line, ErrorResult& aRv)
  183. {
  184. nsIScrollableFrame* sf = GetScrollFrame();
  185. if (!sf) {
  186. aRv.Throw(NS_ERROR_FAILURE);
  187. return;
  188. }
  189. nscoord y = sf->GetLineScrollAmount().height * line;
  190. nsRect range(0, y - nsPresContext::CSSPixelsToAppUnits(1),
  191. 0, nsPresContext::CSSPixelsToAppUnits(1));
  192. sf->ScrollTo(nsPoint(0, y), nsIScrollableFrame::INSTANT, &range);
  193. }
  194. void ScrollBoxObject::ScrollToElement(Element& child, ErrorResult& aRv)
  195. {
  196. nsCOMPtr<nsIPresShell> shell = GetPresShell(false);
  197. if (!shell) {
  198. aRv.Throw(NS_ERROR_UNEXPECTED);
  199. return;
  200. }
  201. shell->ScrollContentIntoView(&child,
  202. nsIPresShell::ScrollAxis(
  203. nsIPresShell::SCROLL_TOP,
  204. nsIPresShell::SCROLL_ALWAYS),
  205. nsIPresShell::ScrollAxis(
  206. nsIPresShell::SCROLL_LEFT,
  207. nsIPresShell::SCROLL_ALWAYS),
  208. nsIPresShell::SCROLL_FIRST_ANCESTOR_ONLY |
  209. nsIPresShell::SCROLL_OVERFLOW_HIDDEN);
  210. }
  211. void ScrollBoxObject::ScrollToIndex(int32_t index, ErrorResult& aRv)
  212. {
  213. aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
  214. }
  215. int32_t ScrollBoxObject::GetPositionX(ErrorResult& aRv)
  216. {
  217. CSSIntPoint pt;
  218. GetPosition(pt, aRv);
  219. return pt.x;
  220. }
  221. int32_t ScrollBoxObject::GetPositionY(ErrorResult& aRv)
  222. {
  223. CSSIntPoint pt;
  224. GetPosition(pt, aRv);
  225. return pt.y;
  226. }
  227. int32_t ScrollBoxObject::GetScrolledWidth(ErrorResult& aRv)
  228. {
  229. nsRect scrollRect;
  230. GetScrolledSize(scrollRect, aRv);
  231. return scrollRect.width;
  232. }
  233. int32_t ScrollBoxObject::GetScrolledHeight(ErrorResult& aRv)
  234. {
  235. nsRect scrollRect;
  236. GetScrolledSize(scrollRect, aRv);
  237. return scrollRect.height;
  238. }
  239. /* private helper */
  240. void ScrollBoxObject::GetPosition(CSSIntPoint& aPos, ErrorResult& aRv)
  241. {
  242. nsIScrollableFrame* sf = GetScrollFrame();
  243. if (!sf) {
  244. aRv.Throw(NS_ERROR_FAILURE);
  245. return;
  246. }
  247. aPos = sf->GetScrollPositionCSSPixels();
  248. }
  249. /* private helper */
  250. void ScrollBoxObject::GetScrolledSize(nsRect& aRect, ErrorResult& aRv)
  251. {
  252. nsIFrame* scrolledBox = GetScrolledBox(this);
  253. if (!scrolledBox) {
  254. aRv.Throw(NS_ERROR_FAILURE);
  255. return;
  256. }
  257. aRect = scrolledBox->GetRect();
  258. aRect.width = nsPresContext::AppUnitsToIntCSSPixels(aRect.width);
  259. aRect.height = nsPresContext::AppUnitsToIntCSSPixels(aRect.height);
  260. }
  261. void ScrollBoxObject::GetPosition(JSContext* cx,
  262. JS::Handle<JSObject*> x,
  263. JS::Handle<JSObject*> y,
  264. ErrorResult& aRv)
  265. {
  266. CSSIntPoint pt;
  267. GetPosition(pt, aRv);
  268. JS::Rooted<JS::Value> v(cx);
  269. if (!ToJSValue(cx, pt.x, &v) ||
  270. !JS_SetProperty(cx, x, "value", v)) {
  271. aRv.Throw(NS_ERROR_XPC_CANT_SET_OUT_VAL);
  272. return;
  273. }
  274. if (!ToJSValue(cx, pt.y, &v) ||
  275. !JS_SetProperty(cx, y, "value", v)) {
  276. aRv.Throw(NS_ERROR_XPC_CANT_SET_OUT_VAL);
  277. return;
  278. }
  279. }
  280. void ScrollBoxObject::GetScrolledSize(JSContext* cx,
  281. JS::Handle<JSObject*> width,
  282. JS::Handle<JSObject*> height,
  283. ErrorResult& aRv)
  284. {
  285. nsRect rect;
  286. GetScrolledSize(rect, aRv);
  287. JS::Rooted<JS::Value> v(cx);
  288. if (!ToJSValue(cx, rect.width, &v) ||
  289. !JS_SetProperty(cx, width, "value", v)) {
  290. aRv.Throw(NS_ERROR_XPC_CANT_SET_OUT_VAL);
  291. return;
  292. }
  293. if (!ToJSValue(cx, rect.height, &v) ||
  294. !JS_SetProperty(cx, height, "value", v)) {
  295. aRv.Throw(NS_ERROR_XPC_CANT_SET_OUT_VAL);
  296. return;
  297. }
  298. }
  299. void ScrollBoxObject::EnsureElementIsVisible(Element& child, ErrorResult& aRv)
  300. {
  301. nsCOMPtr<nsIPresShell> shell = GetPresShell(false);
  302. if (!shell) {
  303. aRv.Throw(NS_ERROR_UNEXPECTED);
  304. return;
  305. }
  306. shell->ScrollContentIntoView(&child,
  307. nsIPresShell::ScrollAxis(),
  308. nsIPresShell::ScrollAxis(),
  309. nsIPresShell::SCROLL_FIRST_ANCESTOR_ONLY |
  310. nsIPresShell::SCROLL_OVERFLOW_HIDDEN);
  311. }
  312. void ScrollBoxObject::EnsureIndexIsVisible(int32_t index, ErrorResult& aRv)
  313. {
  314. aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
  315. }
  316. void ScrollBoxObject::EnsureLineIsVisible(int32_t line, ErrorResult& aRv)
  317. {
  318. aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
  319. }
  320. } // namespace dom
  321. } // namespace mozilla
  322. // Creation Routine ///////////////////////////////////////////////////////////////////////
  323. using namespace mozilla::dom;
  324. nsresult
  325. NS_NewScrollBoxObject(nsIBoxObject** aResult)
  326. {
  327. NS_ADDREF(*aResult = new ScrollBoxObject());
  328. return NS_OK;
  329. }