зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1586144 - Expand the clipt rect for visual viewport scrollable display item. r=botond
Without this change, the junit test in this commit fail, the failure rendered result is the area of the position:fixed element covered by the dynamic toolbar before scrolling is rendered as blank. Depends on D50418 Differential Revision: https://phabricator.services.mozilla.com/D50419 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
f5f6f60da5
Коммит
4f001373b7
|
@ -2996,6 +2996,16 @@ class nsLayoutUtils {
|
|||
static bool FrameIsMostlyScrolledOutOfViewInCrossProcess(
|
||||
const nsIFrame* aFrame, nscoord aMargin);
|
||||
|
||||
/**
|
||||
* Expand the height of |aSize| to the size of `vh` units.
|
||||
*
|
||||
* With dynamic toolbar(s) the height for `vh` units is greater than the
|
||||
* ICB height, we need to expand it in some places.
|
||||
**/
|
||||
template <typename SizeType>
|
||||
static SizeType ExpandHeightForViewportUnits(nsPresContext* aPresContext,
|
||||
const SizeType& aSize);
|
||||
|
||||
private:
|
||||
/**
|
||||
* Helper function for LogTestDataForPaint().
|
||||
|
@ -3048,6 +3058,23 @@ template <typename PointType, typename RectType, typename CoordType>
|
|||
return false;
|
||||
}
|
||||
|
||||
template <typename SizeType>
|
||||
/* static */ SizeType nsLayoutUtils::ExpandHeightForViewportUnits(
|
||||
nsPresContext* aPresContext, const SizeType& aSize) {
|
||||
nsSize sizeForViewportUnits = aPresContext->GetSizeForViewportUnits();
|
||||
|
||||
// |aSize| might be the size expanded to the minimum-scale size whereas the
|
||||
// size for viewport units is not scaled so that we need to expand the |aSize|
|
||||
// height with the aspect ratio of the size for viewport units instead of just
|
||||
// expanding to the size for viewport units.
|
||||
float ratio = (float)sizeForViewportUnits.height / sizeForViewportUnits.width;
|
||||
|
||||
MOZ_ASSERT(aSize.height <=
|
||||
NSCoordSaturatingNonnegativeMultiply(aSize.width, ratio));
|
||||
return SizeType(aSize.width,
|
||||
NSCoordSaturatingNonnegativeMultiply(aSize.width, ratio));
|
||||
}
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
/**
|
||||
|
|
|
@ -371,6 +371,8 @@ class nsPresContext : public nsISupports,
|
|||
*/
|
||||
void SetVisibleArea(const nsRect& r);
|
||||
|
||||
nsSize GetSizeForViewportUnits() const { return mSizeForViewportUnits; }
|
||||
|
||||
/**
|
||||
* Set the maximum height of the dynamic toolbar in nscoord units.
|
||||
*/
|
||||
|
|
|
@ -3544,6 +3544,17 @@ void ScrollFrameHelper::BuildDisplayList(nsDisplayListBuilder* aBuilder,
|
|||
aBuilder->ShouldBuildAsyncZoomContainer() && isRootContent;
|
||||
|
||||
nsRect scrollPortClip = mScrollPort + aBuilder->ToReferenceFrame(mOuter);
|
||||
// Expand the clip rect to the size including the area covered by dynamic
|
||||
// toolbar in the case where the dynamic toolbar is being used since
|
||||
// position:fixed elements attached to this root scroller might be taller than
|
||||
// its scroll port (e.g 100vh). Even if the dynamic toolbar covers the taller
|
||||
// area, it doesn't mean the area is clipped by the toolbar because the
|
||||
// dynamic toolbar is laid out outside of our topmost window and it
|
||||
// transitions without changing our topmost window size.
|
||||
if (isRootContent && mOuter->PresContext()->HasDynamicToolbar()) {
|
||||
scrollPortClip.SizeTo(nsLayoutUtils::ExpandHeightForViewportUnits(
|
||||
mOuter->PresContext(), scrollPortClip.Size()));
|
||||
}
|
||||
nsRect clipRect = scrollPortClip;
|
||||
// Our override of GetBorderRadii ensures we never have a radius at
|
||||
// the corners where we have a scrollbar.
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
<!DOCTYPE html>
|
||||
<meta name="viewport" content="width=device-width, minimum-scale=0.5">
|
||||
<style>
|
||||
html {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
body {
|
||||
width: 200%;
|
||||
height: 2000px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#fixed-element {
|
||||
width: 100%;
|
||||
height: 200vh;
|
||||
position: fixed;
|
||||
top: 0px;
|
||||
background-color: green;
|
||||
}
|
||||
</style>
|
||||
<div id="fixed-element"></div>
|
|
@ -59,6 +59,7 @@ open class BaseSessionTest(noErrorCollector: Boolean = false) {
|
|||
const val SCROLL_TEST_PATH = "/assets/www/scroll.html"
|
||||
const val COLORS_HTML_PATH = "/assets/www/colors.html"
|
||||
const val FIXED_BOTTOM = "/assets/www/fixedbottom.html"
|
||||
const val FIXED_VH = "/assets/www/fixedvh.html"
|
||||
const val STORAGE_TITLE_HTML_PATH = "/assets/www/reflect_local_storage_into_title.html"
|
||||
const val HUNG_SCRIPT = "/assets/www/hungScript.html"
|
||||
const val PUSH_HTML_PATH = "/assets/www/push/push.html"
|
||||
|
|
|
@ -4,16 +4,22 @@
|
|||
|
||||
package org.mozilla.geckoview.test
|
||||
|
||||
import android.graphics.*
|
||||
import android.graphics.Bitmap
|
||||
import android.support.test.filters.MediumTest
|
||||
import android.support.test.runner.AndroidJUnit4
|
||||
import android.util.Base64
|
||||
import java.io.ByteArrayOutputStream
|
||||
import org.hamcrest.Matchers.*
|
||||
import org.junit.Assert.fail
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mozilla.geckoview.GeckoResult
|
||||
import org.mozilla.geckoview.test.rule.GeckoSessionTestRule.WithDisplay
|
||||
import org.hamcrest.Matchers.equalTo
|
||||
|
||||
private const val SCREEN_HEIGHT = 100
|
||||
private const val SCREEN_WIDTH = 200
|
||||
private const val SCREEN_WIDTH = 100
|
||||
private const val SCREEN_HEIGHT = 200
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@MediumTest
|
||||
|
@ -29,4 +35,74 @@ class DynamicToolbarTest : BaseSessionTest() {
|
|||
e.toString(), containsString("maximum height of the dynamic toolbar"))
|
||||
}
|
||||
}
|
||||
|
||||
private fun assertScreenshotResult(result: GeckoResult<Bitmap>, comparisonImage: Bitmap) {
|
||||
sessionRule.waitForResult(result).let {
|
||||
assertThat("Screenshot is not null",
|
||||
it, notNullValue())
|
||||
assertThat("Widths are the same", comparisonImage.width, equalTo(it.width))
|
||||
assertThat("Heights are the same", comparisonImage.height, equalTo(it.height))
|
||||
assertThat("Byte counts are the same", comparisonImage.byteCount, equalTo(it.byteCount))
|
||||
assertThat("Configs are the same", comparisonImage.config, equalTo(it.config))
|
||||
|
||||
if (!comparisonImage.sameAs(it)) {
|
||||
val outputForComparison = ByteArrayOutputStream()
|
||||
comparisonImage.compress(Bitmap.CompressFormat.PNG, 100, outputForComparison)
|
||||
|
||||
val outputForActual = ByteArrayOutputStream()
|
||||
it.compress(Bitmap.CompressFormat.PNG, 100, outputForActual)
|
||||
val actualString: String = Base64.encodeToString(outputForActual.toByteArray(), Base64.DEFAULT)
|
||||
val comparisonString: String = Base64.encodeToString(outputForComparison.toByteArray(), Base64.DEFAULT)
|
||||
|
||||
assertThat("Encoded strings are the same", comparisonString, equalTo(actualString))
|
||||
}
|
||||
|
||||
assertThat("Bytes are the same", comparisonImage.sameAs(it), equalTo(true))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a whole green Bitmap.
|
||||
* This Bitmap would be a reference image of tests in this file.
|
||||
*/
|
||||
private fun getComparisonScreenshot(width: Int, height: Int): Bitmap {
|
||||
val screenshotFile = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
|
||||
val canvas = Canvas(screenshotFile)
|
||||
val paint = Paint()
|
||||
paint.color = Color.rgb(0, 128, 0)
|
||||
canvas.drawRect(0f, 0f, width.toFloat(), height.toFloat(), paint)
|
||||
return screenshotFile
|
||||
}
|
||||
|
||||
// With the dynamic toolbar max height vh units values exceed
|
||||
// the top most window height. This is a test case that exceeded area
|
||||
// is rendered properly (on the compositor).
|
||||
@WithDisplay(height = SCREEN_HEIGHT, width = SCREEN_WIDTH)
|
||||
@Test
|
||||
fun positionFixedElementClipping() {
|
||||
sessionRule.display?.run { setDynamicToolbarMaxHeight(SCREEN_HEIGHT / 2) }
|
||||
|
||||
val reference = getComparisonScreenshot(SCREEN_WIDTH, SCREEN_HEIGHT)
|
||||
|
||||
// FIXED_VH is an HTML file which has a position:fixed element whose
|
||||
// style is "width: 100%; height: 200vh" and the document is scaled by
|
||||
// minimum-scale 0.5, so that the height of the element exceeds the
|
||||
// window height.
|
||||
mainSession.loadTestPath(BaseSessionTest.FIXED_VH)
|
||||
mainSession.waitForPageStop()
|
||||
|
||||
// Scroll down bit, if we correctly render the document, the position
|
||||
// fixed element still covers whole the document area.
|
||||
mainSession.evaluateJS("window.scrollTo({ top: 100, behevior: 'instant' })")
|
||||
|
||||
// Wait a while to make sure the scrolling result is composited on the compositor
|
||||
// since capturePixels() takes a snapshot directly from the compositor without
|
||||
// waiting for a corresponding MozAfterPaint on the main-thread so it's possible
|
||||
// to take a stale snapshot even if it's a result of syncronous scrolling.
|
||||
mainSession.evaluateJS("new Promise(resolve => window.setTimeout(resolve, 1000))")
|
||||
|
||||
sessionRule.display?.let {
|
||||
assertScreenshotResult(it.capturePixels(), reference)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче