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