1// Copyright 2013 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5package org.chromium.android_webview.test; 6 7import android.graphics.Rect; 8import android.test.InstrumentationTestCase; 9import android.test.suitebuilder.annotation.SmallTest; 10import android.widget.OverScroller; 11 12import org.chromium.android_webview.AwScrollOffsetManager; 13import org.chromium.base.test.util.Feature; 14 15public class AwScrollOffsetManagerTest extends InstrumentationTestCase { 16 private static class TestScrollOffsetManagerDelegate implements AwScrollOffsetManager.Delegate { 17 private int mOverScrollDeltaX; 18 private int mOverScrollDeltaY; 19 private int mOverScrollCallCount; 20 private int mScrollX; 21 private int mScrollY; 22 private int mNativeScrollX; 23 private int mNativeScrollY; 24 private int mInvalidateCount; 25 26 public int getOverScrollDeltaX() { 27 return mOverScrollDeltaX; 28 } 29 30 public int getOverScrollDeltaY() { 31 return mOverScrollDeltaY; 32 } 33 34 public int getOverScrollCallCount() { 35 return mOverScrollCallCount; 36 } 37 38 public int getScrollX() { 39 return mScrollX; 40 } 41 42 public int getScrollY() { 43 return mScrollY; 44 } 45 46 public int getNativeScrollX() { 47 return mNativeScrollX; 48 } 49 50 public int getNativeScrollY() { 51 return mNativeScrollY; 52 } 53 54 public int getInvalidateCount() { 55 return mInvalidateCount; 56 } 57 58 @Override 59 public void overScrollContainerViewBy(int deltaX, int deltaY, int scrollX, int scrollY, 60 int scrollRangeX, int scrollRangeY, boolean isTouchEvent) { 61 mOverScrollDeltaX = deltaX; 62 mOverScrollDeltaY = deltaY; 63 mOverScrollCallCount += 1; 64 } 65 66 @Override 67 public void scrollContainerViewTo(int x, int y) { 68 mScrollX = x; 69 mScrollY = y; 70 } 71 72 @Override 73 public void scrollNativeTo(int x, int y) { 74 mNativeScrollX = x; 75 mNativeScrollY = y; 76 } 77 78 @Override 79 public int getContainerViewScrollX() { 80 return mScrollX; 81 } 82 83 @Override 84 public int getContainerViewScrollY() { 85 return mScrollY; 86 } 87 88 @Override 89 public void invalidate() { 90 mInvalidateCount += 1; 91 } 92 } 93 94 private void simulateScrolling(AwScrollOffsetManager offsetManager, 95 TestScrollOffsetManagerDelegate delegate, int scrollX, int scrollY) { 96 // Scrolling is a two-phase action. First we ask the manager to scroll 97 int callCount = delegate.getOverScrollCallCount(); 98 offsetManager.scrollContainerViewTo(scrollX, scrollY); 99 // The manager then asks the delegate to overscroll the view. 100 assertEquals(callCount + 1, delegate.getOverScrollCallCount()); 101 assertEquals(scrollX, delegate.getOverScrollDeltaX() + delegate.getScrollX()); 102 assertEquals(scrollY, delegate.getOverScrollDeltaY() + delegate.getScrollY()); 103 // As a response to that the menager expects the view to call back with the new scroll. 104 offsetManager.onContainerViewOverScrolled(scrollX, scrollY, false, false); 105 } 106 107 private void simlateOverScrollPropagation(AwScrollOffsetManager offsetManager, 108 TestScrollOffsetManagerDelegate delegate) { 109 assertTrue(delegate.getOverScrollCallCount() > 0); 110 111 offsetManager.onContainerViewOverScrolled( 112 delegate.getOverScrollDeltaX() + delegate.getScrollX(), 113 delegate.getOverScrollDeltaY() + delegate.getScrollY(), false, false); 114 } 115 116 @SmallTest 117 @Feature({"AndroidWebView"}) 118 public void testWhenContentSizeMatchesView() { 119 TestScrollOffsetManagerDelegate delegate = new TestScrollOffsetManagerDelegate(); 120 OverScroller scroller = new OverScroller(getInstrumentation().getContext()); 121 AwScrollOffsetManager offsetManager = new AwScrollOffsetManager(delegate, scroller); 122 123 final int width = 132; 124 final int height = 212; 125 final int scrollX = 11; 126 final int scrollY = 13; 127 128 offsetManager.setMaxScrollOffset(0, 0); 129 offsetManager.setContainerViewSize(width, height); 130 131 assertEquals(width, offsetManager.computeHorizontalScrollRange()); 132 assertEquals(height, offsetManager.computeVerticalScrollRange()); 133 134 // Since the view size and contents size are equal no scrolling should be possible. 135 assertEquals(0, offsetManager.computeMaximumHorizontalScrollOffset()); 136 assertEquals(0, offsetManager.computeMaximumVerticalScrollOffset()); 137 138 // Scrolling should generate overscroll but not update the scroll offset. 139 simulateScrolling(offsetManager, delegate, scrollX, scrollY); 140 assertEquals(scrollX, delegate.getOverScrollDeltaX()); 141 assertEquals(scrollY, delegate.getOverScrollDeltaY()); 142 assertEquals(0, delegate.getScrollX()); 143 assertEquals(0, delegate.getScrollY()); 144 assertEquals(0, delegate.getNativeScrollX()); 145 assertEquals(0, delegate.getNativeScrollY()); 146 147 // Scrolling to 0,0 should result in no deltas. 148 simulateScrolling(offsetManager, delegate, 0, 0); 149 assertEquals(0, delegate.getOverScrollDeltaX()); 150 assertEquals(0, delegate.getOverScrollDeltaY()); 151 152 // Negative scrolling should result in negative deltas but no scroll offset update. 153 simulateScrolling(offsetManager, delegate, -scrollX, -scrollY); 154 assertEquals(-scrollX, delegate.getOverScrollDeltaX()); 155 assertEquals(-scrollY, delegate.getOverScrollDeltaY()); 156 assertEquals(0, delegate.getScrollX()); 157 assertEquals(0, delegate.getScrollY()); 158 assertEquals(0, delegate.getNativeScrollX()); 159 assertEquals(0, delegate.getNativeScrollY()); 160 } 161 162 private static final int VIEW_WIDTH = 211; 163 private static final int VIEW_HEIGHT = 312; 164 private static final int MAX_HORIZONTAL_OFFSET = 757; 165 private static final int MAX_VERTICAL_OFFSET = 127; 166 private static final int CONTENT_WIDTH = VIEW_WIDTH + MAX_HORIZONTAL_OFFSET; 167 private static final int CONTENT_HEIGHT = VIEW_HEIGHT + MAX_VERTICAL_OFFSET; 168 169 @SmallTest 170 @Feature({"AndroidWebView"}) 171 public void testScrollRangeAndMaxOffset() { 172 TestScrollOffsetManagerDelegate delegate = new TestScrollOffsetManagerDelegate(); 173 OverScroller scroller = new OverScroller(getInstrumentation().getContext()); 174 AwScrollOffsetManager offsetManager = new AwScrollOffsetManager(delegate, scroller); 175 176 offsetManager.setMaxScrollOffset(MAX_HORIZONTAL_OFFSET, MAX_VERTICAL_OFFSET); 177 offsetManager.setContainerViewSize(VIEW_WIDTH, VIEW_HEIGHT); 178 179 assertEquals(CONTENT_WIDTH, offsetManager.computeHorizontalScrollRange()); 180 assertEquals(CONTENT_HEIGHT, offsetManager.computeVerticalScrollRange()); 181 182 assertEquals(MAX_HORIZONTAL_OFFSET, offsetManager.computeMaximumHorizontalScrollOffset()); 183 assertEquals(MAX_VERTICAL_OFFSET, offsetManager.computeMaximumVerticalScrollOffset()); 184 185 // Scrolling beyond the maximum should be clamped. 186 final int scrollX = MAX_HORIZONTAL_OFFSET + 10; 187 final int scrollY = MAX_VERTICAL_OFFSET + 11; 188 189 simulateScrolling(offsetManager, delegate, scrollX, scrollY); 190 assertEquals(scrollX, delegate.getOverScrollDeltaX()); 191 assertEquals(scrollY, delegate.getOverScrollDeltaY()); 192 assertEquals(MAX_HORIZONTAL_OFFSET, delegate.getScrollX()); 193 assertEquals(MAX_VERTICAL_OFFSET, delegate.getScrollY()); 194 assertEquals(MAX_HORIZONTAL_OFFSET, delegate.getNativeScrollX()); 195 assertEquals(MAX_VERTICAL_OFFSET, delegate.getNativeScrollY()); 196 197 // Scrolling to negative coordinates should be clamped back to 0,0. 198 simulateScrolling(offsetManager, delegate, -scrollX, -scrollY); 199 assertEquals(0, delegate.getScrollX()); 200 assertEquals(0, delegate.getScrollY()); 201 assertEquals(0, delegate.getNativeScrollX()); 202 assertEquals(0, delegate.getNativeScrollY()); 203 204 // The onScrollChanged method is callable by third party code and should also be clamped 205 offsetManager.onContainerViewScrollChanged(scrollX, scrollY); 206 assertEquals(MAX_HORIZONTAL_OFFSET, delegate.getNativeScrollX()); 207 assertEquals(MAX_VERTICAL_OFFSET, delegate.getNativeScrollY()); 208 209 offsetManager.onContainerViewScrollChanged(-scrollX, -scrollY); 210 assertEquals(0, delegate.getNativeScrollX()); 211 assertEquals(0, delegate.getNativeScrollY()); 212 } 213 214 @SmallTest 215 @Feature({"AndroidWebView"}) 216 public void testDelegateCanOverrideScroll() { 217 final int overrideScrollX = 10; 218 final int overrideScrollY = 10; 219 220 TestScrollOffsetManagerDelegate delegate = new TestScrollOffsetManagerDelegate() { 221 @Override 222 public int getContainerViewScrollX() { 223 return overrideScrollX; 224 } 225 226 @Override 227 public int getContainerViewScrollY() { 228 return overrideScrollY; 229 } 230 }; 231 OverScroller scroller = new OverScroller(getInstrumentation().getContext()); 232 AwScrollOffsetManager offsetManager = new AwScrollOffsetManager(delegate, scroller); 233 234 offsetManager.setMaxScrollOffset(MAX_HORIZONTAL_OFFSET, MAX_VERTICAL_OFFSET); 235 offsetManager.setContainerViewSize(VIEW_WIDTH, VIEW_HEIGHT); 236 237 offsetManager.onContainerViewOverScrolled(0, 0, false, false); 238 assertEquals(overrideScrollX, delegate.getNativeScrollX()); 239 assertEquals(overrideScrollY, delegate.getNativeScrollY()); 240 } 241 242 @SmallTest 243 @Feature({"AndroidWebView"}) 244 public void testDelegateOverridenScrollsDontExceedBounds() { 245 final int overrideScrollX = MAX_HORIZONTAL_OFFSET + 10; 246 final int overrideScrollY = MAX_VERTICAL_OFFSET + 20; 247 TestScrollOffsetManagerDelegate delegate = new TestScrollOffsetManagerDelegate() { 248 @Override 249 public int getContainerViewScrollX() { 250 return overrideScrollX; 251 } 252 253 @Override 254 public int getContainerViewScrollY() { 255 return overrideScrollY; 256 } 257 }; 258 OverScroller scroller = new OverScroller(getInstrumentation().getContext()); 259 AwScrollOffsetManager offsetManager = new AwScrollOffsetManager(delegate, scroller); 260 261 offsetManager.setMaxScrollOffset(MAX_HORIZONTAL_OFFSET, MAX_VERTICAL_OFFSET); 262 offsetManager.setContainerViewSize(VIEW_WIDTH, VIEW_HEIGHT); 263 264 offsetManager.onContainerViewOverScrolled(0, 0, false, false); 265 assertEquals(MAX_HORIZONTAL_OFFSET, delegate.getNativeScrollX()); 266 assertEquals(MAX_VERTICAL_OFFSET, delegate.getNativeScrollY()); 267 } 268 269 @SmallTest 270 @Feature({"AndroidWebView"}) 271 public void testScrollContainerViewTo() { 272 TestScrollOffsetManagerDelegate delegate = new TestScrollOffsetManagerDelegate(); 273 OverScroller scroller = new OverScroller(getInstrumentation().getContext()); 274 AwScrollOffsetManager offsetManager = new AwScrollOffsetManager(delegate, scroller); 275 276 final int scrollX = 31; 277 final int scrollY = 41; 278 279 offsetManager.setMaxScrollOffset(MAX_HORIZONTAL_OFFSET, MAX_VERTICAL_OFFSET); 280 offsetManager.setContainerViewSize(VIEW_WIDTH, VIEW_HEIGHT); 281 282 assertEquals(0, delegate.getOverScrollDeltaX()); 283 assertEquals(0, delegate.getOverScrollDeltaY()); 284 int callCount = delegate.getOverScrollCallCount(); 285 286 offsetManager.scrollContainerViewTo(scrollX, scrollY); 287 assertEquals(callCount + 1, delegate.getOverScrollCallCount()); 288 assertEquals(scrollX, delegate.getOverScrollDeltaX()); 289 assertEquals(scrollY, delegate.getOverScrollDeltaY()); 290 } 291 292 @SmallTest 293 @Feature({"AndroidWebView"}) 294 public void testOnContainerViewOverScrolled() { 295 TestScrollOffsetManagerDelegate delegate = new TestScrollOffsetManagerDelegate(); 296 OverScroller scroller = new OverScroller(getInstrumentation().getContext()); 297 AwScrollOffsetManager offsetManager = new AwScrollOffsetManager(delegate, scroller); 298 299 final int scrollX = 31; 300 final int scrollY = 41; 301 302 offsetManager.setMaxScrollOffset(MAX_HORIZONTAL_OFFSET, MAX_VERTICAL_OFFSET); 303 offsetManager.setContainerViewSize(VIEW_WIDTH, VIEW_HEIGHT); 304 305 assertEquals(0, delegate.getScrollX()); 306 assertEquals(0, delegate.getScrollY()); 307 assertEquals(0, delegate.getNativeScrollX()); 308 assertEquals(0, delegate.getNativeScrollY()); 309 310 offsetManager.onContainerViewOverScrolled(scrollX, scrollY, false, false); 311 assertEquals(scrollX, delegate.getScrollX()); 312 assertEquals(scrollY, delegate.getScrollY()); 313 assertEquals(scrollX, delegate.getNativeScrollX()); 314 assertEquals(scrollY, delegate.getNativeScrollY()); 315 } 316 317 @SmallTest 318 @Feature({"AndroidWebView"}) 319 public void testDefersScrollUntilTouchEnd() { 320 TestScrollOffsetManagerDelegate delegate = new TestScrollOffsetManagerDelegate(); 321 OverScroller scroller = new OverScroller(getInstrumentation().getContext()); 322 AwScrollOffsetManager offsetManager = new AwScrollOffsetManager(delegate, scroller); 323 324 final int scrollX = 31; 325 final int scrollY = 41; 326 327 offsetManager.setMaxScrollOffset(MAX_HORIZONTAL_OFFSET, MAX_VERTICAL_OFFSET); 328 offsetManager.setContainerViewSize(VIEW_WIDTH, VIEW_HEIGHT); 329 330 offsetManager.setProcessingTouchEvent(true); 331 offsetManager.onContainerViewOverScrolled(scrollX, scrollY, false, false); 332 assertEquals(scrollX, delegate.getScrollX()); 333 assertEquals(scrollY, delegate.getScrollY()); 334 assertEquals(0, delegate.getNativeScrollX()); 335 assertEquals(0, delegate.getNativeScrollY()); 336 337 offsetManager.setProcessingTouchEvent(false); 338 assertEquals(scrollX, delegate.getScrollX()); 339 assertEquals(scrollY, delegate.getScrollY()); 340 assertEquals(scrollX, delegate.getNativeScrollX()); 341 assertEquals(scrollY, delegate.getNativeScrollY()); 342 } 343 344 @SmallTest 345 @Feature({"AndroidWebView"}) 346 public void testFlingScroll() { 347 TestScrollOffsetManagerDelegate delegate = new TestScrollOffsetManagerDelegate(); 348 OverScroller scroller = new OverScroller(getInstrumentation().getContext()); 349 AwScrollOffsetManager offsetManager = new AwScrollOffsetManager(delegate, scroller); 350 351 offsetManager.flingScroll(0, 101); 352 assertTrue(!scroller.isFinished()); 353 assertTrue(delegate.getInvalidateCount() == 1); 354 assertEquals(101, (int) scroller.getCurrVelocity()); 355 356 offsetManager.flingScroll(111, 0); 357 assertTrue(!scroller.isFinished()); 358 assertTrue(delegate.getInvalidateCount() == 2); 359 assertEquals(111, (int) scroller.getCurrVelocity()); 360 } 361 362 @SmallTest 363 @Feature({"AndroidWebView"}) 364 public void testRequestChildRectangleOnScreenDontScrollIfAlreadyThere() { 365 TestScrollOffsetManagerDelegate delegate = new TestScrollOffsetManagerDelegate(); 366 OverScroller scroller = new OverScroller(getInstrumentation().getContext()); 367 AwScrollOffsetManager offsetManager = new AwScrollOffsetManager(delegate, scroller); 368 369 offsetManager.setMaxScrollOffset(MAX_HORIZONTAL_OFFSET, MAX_VERTICAL_OFFSET); 370 offsetManager.setContainerViewSize(VIEW_WIDTH, VIEW_HEIGHT); 371 372 offsetManager.requestChildRectangleOnScreen(0, 0, 373 new Rect(0, 0, VIEW_WIDTH / 4, VIEW_HEIGHT / 4), true); 374 assertEquals(0, delegate.getOverScrollDeltaX()); 375 assertEquals(0, delegate.getOverScrollDeltaY()); 376 assertEquals(0, delegate.getScrollX()); 377 assertEquals(0, delegate.getScrollY()); 378 379 offsetManager.requestChildRectangleOnScreen(3 * VIEW_WIDTH / 4, 3 * VIEW_HEIGHT / 4, 380 new Rect(0, 0, VIEW_WIDTH / 4, VIEW_HEIGHT / 4), true); 381 assertEquals(0, delegate.getOverScrollDeltaX()); 382 assertEquals(0, delegate.getOverScrollDeltaY()); 383 assertEquals(0, delegate.getScrollX()); 384 assertEquals(0, delegate.getScrollY()); 385 } 386 387 @SmallTest 388 @Feature({"AndroidWebView"}) 389 public void testRequestChildRectangleOnScreenScrollToBottom() { 390 TestScrollOffsetManagerDelegate delegate = new TestScrollOffsetManagerDelegate(); 391 OverScroller scroller = new OverScroller(getInstrumentation().getContext()); 392 AwScrollOffsetManager offsetManager = new AwScrollOffsetManager(delegate, scroller); 393 394 final int rectWidth = 2; 395 final int rectHeight = 3; 396 397 offsetManager.setMaxScrollOffset(MAX_HORIZONTAL_OFFSET, MAX_VERTICAL_OFFSET); 398 offsetManager.setContainerViewSize(VIEW_WIDTH, VIEW_HEIGHT); 399 400 offsetManager.requestChildRectangleOnScreen(CONTENT_WIDTH - rectWidth, 401 CONTENT_HEIGHT - rectHeight, new Rect(0, 0, rectWidth, rectHeight), true); 402 simlateOverScrollPropagation(offsetManager, delegate); 403 assertEquals(MAX_HORIZONTAL_OFFSET, delegate.getOverScrollDeltaX()); 404 assertEquals(CONTENT_HEIGHT - rectHeight - VIEW_HEIGHT / 3, delegate.getOverScrollDeltaY()); 405 assertEquals(MAX_HORIZONTAL_OFFSET, delegate.getScrollX()); 406 assertEquals(MAX_VERTICAL_OFFSET, delegate.getScrollY()); 407 } 408 409 @SmallTest 410 @Feature({"AndroidWebView"}) 411 public void testRequestChildRectangleOnScreenScrollToBottomLargeRect() { 412 TestScrollOffsetManagerDelegate delegate = new TestScrollOffsetManagerDelegate(); 413 OverScroller scroller = new OverScroller(getInstrumentation().getContext()); 414 AwScrollOffsetManager offsetManager = new AwScrollOffsetManager(delegate, scroller); 415 416 final int rectWidth = VIEW_WIDTH; 417 final int rectHeight = VIEW_HEIGHT; 418 419 offsetManager.setMaxScrollOffset(MAX_HORIZONTAL_OFFSET, MAX_VERTICAL_OFFSET); 420 offsetManager.setContainerViewSize(VIEW_WIDTH, VIEW_HEIGHT); 421 422 offsetManager.requestChildRectangleOnScreen(CONTENT_WIDTH - rectWidth, 423 CONTENT_HEIGHT - rectHeight, new Rect(0, 0, rectWidth, rectHeight), true); 424 simlateOverScrollPropagation(offsetManager, delegate); 425 assertEquals(MAX_HORIZONTAL_OFFSET, delegate.getOverScrollDeltaX()); 426 assertEquals(MAX_VERTICAL_OFFSET, delegate.getOverScrollDeltaY()); 427 assertEquals(MAX_HORIZONTAL_OFFSET, delegate.getScrollX()); 428 assertEquals(MAX_VERTICAL_OFFSET, delegate.getScrollY()); 429 } 430 431 @SmallTest 432 @Feature({"AndroidWebView"}) 433 public void testRequestChildRectangleOnScreenScrollToTop() { 434 TestScrollOffsetManagerDelegate delegate = new TestScrollOffsetManagerDelegate(); 435 OverScroller scroller = new OverScroller(getInstrumentation().getContext()); 436 AwScrollOffsetManager offsetManager = new AwScrollOffsetManager(delegate, scroller); 437 438 final int rectWidth = 2; 439 final int rectHeight = 3; 440 441 offsetManager.setMaxScrollOffset(MAX_HORIZONTAL_OFFSET, MAX_VERTICAL_OFFSET); 442 offsetManager.setContainerViewSize(VIEW_WIDTH, VIEW_HEIGHT); 443 simulateScrolling(offsetManager, delegate, 444 CONTENT_WIDTH - VIEW_WIDTH, CONTENT_HEIGHT - VIEW_HEIGHT); 445 446 offsetManager.requestChildRectangleOnScreen(0, 0, 447 new Rect(0, 0, rectWidth, rectHeight), true); 448 simlateOverScrollPropagation(offsetManager, delegate); 449 assertEquals(-CONTENT_WIDTH + VIEW_WIDTH, delegate.getOverScrollDeltaX()); 450 assertEquals(-CONTENT_HEIGHT + VIEW_HEIGHT, delegate.getOverScrollDeltaY()); 451 assertEquals(0, delegate.getScrollX()); 452 assertEquals(0, delegate.getScrollX()); 453 } 454} 455