Bug 1645954 - Ensure we update both the content viewer and widget sizes before doing a reflow. r=tnikkel

The existing comment in BrowserChild::RecvUpdateDimensions may have been
accurate at some point in the past, but I'm seeing cases where setting
the content viewer size itself triggers a reflow. Since the widget size
hasn't been updated yet, the reflow uses some stale values and produces
incorrect outcomes. This patch ensures both the content viewer and widget
get their sizes updated first, and only then do we do the reflow.

Differential Revision: https://phabricator.services.mozilla.com/D79885
This commit is contained in:
Kartikaya Gupta 2020-06-20 11:25:22 +00:00
Родитель 8349c27e4a
Коммит e28e2f103c
5 изменённых файлов: 135 добавлений и 9 удалений

Просмотреть файл

@ -32,6 +32,7 @@
#include "ipc/nsGUIEventIPC.h"
#include "js/JSON.h"
#include "mozilla/AsyncEventDispatcher.h"
#include "mozilla/AutoResizeReflowSquasher.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/EventForwards.h"
#include "mozilla/EventListenerManager.h"
@ -1166,16 +1167,33 @@ mozilla::ipc::IPCResult BrowserChild::RecvUpdateDimensions(
ScreenIntSize screenSize = GetInnerSize();
ScreenIntRect screenRect = GetOuterRect();
// Set the size on the document viewer before we update the widget and
// trigger a reflow. Otherwise the MobileViewportManager reads the stale
// size from the content viewer when it computes a new CSS viewport.
nsCOMPtr<nsIBaseWindow> baseWin = do_QueryInterface(WebNavigation());
baseWin->SetPositionAndSize(0, 0, screenSize.width, screenSize.height,
nsIBaseWindow::eRepaint);
{
#ifdef MOZ_WIDGET_ANDROID
// For some reason on geckoview-junit tests we get multiple reflows on the
// top-level presShell, but with different sizes. This seems kinda wrong,
// but for now let's just leave it as-is and not squash them, since handling
// the multiple reflows in order might be important. Filed bug 1646261 to
// investigate this more closely.
#else
AutoResizeReflowSquasher squasher(GetTopLevelPresShell());
#endif
mPuppetWidget->Resize(screenRect.x + mClientOffset.x + mChromeOffset.x,
screenRect.y + mClientOffset.y + mChromeOffset.y,
screenSize.width, screenSize.height, true);
// Make sure to set the size on the document viewer and the widget before
// triggering a reflow. We do this by capturing the reflows and making
// that happen at the end of this scoped block, using the squasher.
// The MobileViewportManager needs the content viewer size to be updated
// before the reflow, otherwise it gets a stale size when it computes
// a new CSS viewport. Similarly, the widget size needs to be updated before
// the root scrollframe figures out which scrollbars are needed because
// it might read the composition size from the widget.
nsCOMPtr<nsIBaseWindow> baseWin = do_QueryInterface(WebNavigation());
baseWin->SetPositionAndSize(0, 0, screenSize.width, screenSize.height,
nsIBaseWindow::eRepaint);
mPuppetWidget->Resize(screenRect.x + mClientOffset.x + mChromeOffset.x,
screenRect.y + mClientOffset.y + mChromeOffset.y,
screenSize.width, screenSize.height, true);
}
RecvSafeAreaInsetsChanged(mPuppetWidget->GetSafeAreaInsets());

Просмотреть файл

@ -0,0 +1,60 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "AutoResizeReflowSquasher.h"
namespace mozilla {
StaticRefPtr<PresShell> AutoResizeReflowSquasher::sPresShell;
bool AutoResizeReflowSquasher::sHasCapture = false;
nscoord AutoResizeReflowSquasher::sWidth = 0;
nscoord AutoResizeReflowSquasher::sHeight = 0;
ResizeReflowOptions AutoResizeReflowSquasher::sOptions =
ResizeReflowOptions::NoOption;
AutoResizeReflowSquasher::AutoResizeReflowSquasher(PresShell* aShell) {
// Don't allow nested AutoResizeReflowSquashers. Note that aShell may
// be null, in which case this AutoResizeReflowSquasher should behave as
// though it was never created. In this scenario nested instances are
// allowed.
MOZ_ASSERT(!sPresShell);
sPresShell = aShell;
MOZ_ASSERT(!sHasCapture);
}
AutoResizeReflowSquasher::~AutoResizeReflowSquasher() {
RefPtr<PresShell> presShell = sPresShell;
sPresShell = nullptr;
if (sHasCapture) {
presShell->ResizeReflow(sWidth, sHeight, sOptions);
sHasCapture = false;
}
}
bool AutoResizeReflowSquasher::CaptureResizeReflow(
PresShell* aShell, nscoord aWidth, nscoord aHeight,
ResizeReflowOptions aOptions) {
if (!sPresShell) {
return false;
}
if (sPresShell.get() != aShell) {
return false;
}
if (!sHasCapture) {
sHasCapture = true;
sWidth = aWidth;
sHeight = aHeight;
sOptions = aOptions;
return true;
}
MOZ_ASSERT(sWidth == aWidth);
MOZ_ASSERT(sHeight == aHeight);
MOZ_ASSERT(sOptions == aOptions);
return true;
}
} // namespace mozilla

Просмотреть файл

@ -0,0 +1,40 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_AutoResizeReflowSquasher_h_
#define mozilla_AutoResizeReflowSquasher_h_
#include "mozilla/Attributes.h"
#include "mozilla/PresShellForwards.h"
namespace mozilla {
// Put one of these on the stack to "capture" any calls to aShell->ResizeReflow
// and have them execute when this class is destroyed.
// This effectively "squashes" together multiple calls to ResizeReflow and
// combines them into one call at the end of the scope that contains this
// RAII class. Note that only calls to ResizeReflow on a particular shell are
// captured, and they must all have the same arguments, otherwise an assertion
// will be thrown.
class MOZ_RAII AutoResizeReflowSquasher {
public:
explicit AutoResizeReflowSquasher(PresShell* aShell);
MOZ_CAN_RUN_SCRIPT ~AutoResizeReflowSquasher();
static bool CaptureResizeReflow(PresShell* aShell, nscoord aWidth,
nscoord aHeight,
ResizeReflowOptions aOptions);
private:
static StaticRefPtr<PresShell> sPresShell;
static bool sHasCapture;
static nscoord sWidth;
static nscoord sHeight;
static ResizeReflowOptions sOptions;
};
} // namespace mozilla
#endif // mozilla_AutoResizeReflowSquasher_h_

Просмотреть файл

@ -12,6 +12,7 @@
#include "mozilla/dom/FontFaceSet.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/Attributes.h"
#include "mozilla/AutoResizeReflowSquasher.h"
#include "mozilla/AutoRestore.h"
#include "mozilla/ContentIterator.h"
#include "mozilla/EventDispatcher.h"
@ -1939,6 +1940,11 @@ void PresShell::sPaintSuppressionCallback(nsITimer* aTimer, void* aPresShell) {
nsresult PresShell::ResizeReflow(nscoord aWidth, nscoord aHeight,
ResizeReflowOptions aOptions) {
if (AutoResizeReflowSquasher::CaptureResizeReflow(this, aWidth, aHeight,
aOptions)) {
return NS_OK;
}
if (mZoomConstraintsClient) {
// If we have a ZoomConstraintsClient and the available screen area
// changed, then we might need to disable double-tap-to-zoom, so notify

Просмотреть файл

@ -72,6 +72,7 @@ EXPORTS += [
EXPORTS.mozilla += [
'AccessibleCaretEventHub.h',
'ArenaObjectID.h',
'AutoResizeReflowSquasher.h',
'GeckoMVMContext.h',
'GeometryUtils.h',
'MediaEmulationData.h',
@ -99,6 +100,7 @@ UNIFIED_SOURCES += [
'AccessibleCaret.cpp',
'AccessibleCaretEventHub.cpp',
'AccessibleCaretManager.cpp',
'AutoResizeReflowSquasher.cpp',
'GeckoMVMContext.cpp',
'GeometryUtils.cpp',
'LayoutLogging.cpp',