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