From 214402e41010002874c745366ab644487af19017 Mon Sep 17 00:00:00 2001 From: "Carsten \"Tomcat\" Book" Date: Tue, 21 Jul 2015 08:34:57 +0200 Subject: [PATCH] Backed out changeset 313ea7f814d3 (bug 1178847) --- dom/ipc/TabChild.cpp | 454 +++++++++++++++++++++++++- dom/ipc/TabChild.h | 25 +- layout/base/MobileViewportManager.cpp | 279 ---------------- layout/base/MobileViewportManager.h | 62 ---- layout/base/moz.build | 1 - layout/base/nsIPresShell.h | 11 +- layout/base/nsPresShell.cpp | 29 -- layout/base/nsPresShell.h | 10 +- 8 files changed, 472 insertions(+), 399 deletions(-) delete mode 100644 layout/base/MobileViewportManager.cpp delete mode 100644 layout/base/MobileViewportManager.h diff --git a/dom/ipc/TabChild.cpp b/dom/ipc/TabChild.cpp index dc5a19e6a135..c559d66c8768 100644 --- a/dom/ipc/TabChild.cpp +++ b/dom/ipc/TabChild.cpp @@ -165,7 +165,8 @@ NS_IMPL_ISUPPORTS(TabChild::DelayedFireContextMenuEvent, nsITimerCallback) TabChildBase::TabChildBase() - : mTabChildGlobal(nullptr) + : mContentDocumentIsDisplayed(false) + , mTabChildGlobal(nullptr) { mozilla::HoldJSObjects(this); } @@ -205,6 +206,280 @@ NS_INTERFACE_MAP_END NS_IMPL_CYCLE_COLLECTING_ADDREF(TabChildBase) NS_IMPL_CYCLE_COLLECTING_RELEASE(TabChildBase) +// For the root frame, Screen and ParentLayer pixels are interchangeable. +// nsViewportInfo stores zoom values as CSSToScreenScale (because it's a +// data structure specific to the root frame), while FrameMetrics and +// ZoomConstraints store zoom values as CSSToParentLayerScale (because they +// are not specific to the root frame). We define convenience functions for +// converting between the two. As the name suggests, they should only be used +// when dealing with the root frame! +CSSToScreenScale ConvertScaleForRoot(CSSToParentLayerScale aScale) +{ + return ViewTargetAs(aScale, PixelCastJustification::ScreenIsParentLayerForRoot); +} +CSSToParentLayerScale ConvertScaleForRoot(CSSToScreenScale aScale) +{ + return ViewTargetAs(aScale, PixelCastJustification::ScreenIsParentLayerForRoot); +} + +// Calculate the scale needed to fit the given viewport into the given display. +CSSToScreenScale CalculateIntrinsicScale(const ScreenIntSize& aDisplaySize, const CSSSize& aViewportSize) +{ + return MaxScaleRatio(ScreenSize(aDisplaySize), aViewportSize); +} + +void +TabChildBase::InitializeRootMetrics() +{ + // Calculate a really simple resolution that we probably won't + // be keeping, as well as putting the scroll offset back to + // the top-left of the page. + mLastRootMetrics.SetViewport(CSSRect(CSSPoint(), kDefaultViewportSize)); + mLastRootMetrics.SetCompositionBounds(ParentLayerRect( + ParentLayerPoint(), + ParentLayerSize( + ViewAs(GetInnerSize(), + PixelCastJustification::ScreenIsParentLayerForRoot)))); + mLastRootMetrics.SetZoom(CSSToParentLayerScale2D( + ConvertScaleForRoot(CalculateIntrinsicScale(GetInnerSize(), kDefaultViewportSize)))); + mLastRootMetrics.SetDevPixelsPerCSSPixel(WebWidget()->GetDefaultScale()); + // We use ParentLayerToLayerScale(1) below in order to turn the + // async zoom amount into the gecko zoom amount. + mLastRootMetrics.SetCumulativeResolution(mLastRootMetrics.GetZoom() / mLastRootMetrics.GetDevPixelsPerCSSPixel() * ParentLayerToLayerScale(1)); + // This is the root layer, so the cumulative resolution is the same + // as the resolution. + mLastRootMetrics.SetPresShellResolution(mLastRootMetrics.GetCumulativeResolution().ToScaleFactor().scale); + + nsCOMPtr shell = GetPresShell(); + if (shell && shell->GetRootScrollFrameAsScrollable()) { + // The session history code might restore a scroll position when navigating + // back or forward, and we don't want to clobber that. + nsPoint pos = shell->GetRootScrollFrameAsScrollable()->GetScrollPosition(); + mLastRootMetrics.SetScrollOffset(CSSPoint::FromAppUnits(pos)); + } else { + mLastRootMetrics.SetScrollOffset(CSSPoint(0, 0)); + } + + TABC_LOG("After InitializeRootMetrics, mLastRootMetrics is %s\n", + Stringify(mLastRootMetrics).c_str()); +} + +void +TabChildBase::SetCSSViewport(const CSSSize& aSize) +{ + mOldViewportSize = aSize; + TABC_LOG("Setting CSS viewport to %s\n", Stringify(aSize).c_str()); + + if (mContentDocumentIsDisplayed) { + if (nsCOMPtr shell = GetPresShell()) { + nsLayoutUtils::SetCSSViewport(shell, aSize); + } + } +} + +CSSSize +TabChildBase::GetPageSize(nsCOMPtr aDocument, const CSSSize& aViewport) +{ + nsCOMPtr htmlDOMElement = aDocument->GetHtmlElement(); + HTMLBodyElement* bodyDOMElement = aDocument->GetBodyElement(); + + if (!htmlDOMElement && !bodyDOMElement) { + // For non-HTML content (e.g. SVG), just assume page size == viewport size. + return aViewport; + } + + int32_t htmlWidth = 0, htmlHeight = 0; + if (htmlDOMElement) { + htmlWidth = htmlDOMElement->ScrollWidth(); + htmlHeight = htmlDOMElement->ScrollHeight(); + } + int32_t bodyWidth = 0, bodyHeight = 0; + if (bodyDOMElement) { + bodyWidth = bodyDOMElement->ScrollWidth(); + bodyHeight = bodyDOMElement->ScrollHeight(); + } + return CSSSize(std::max(htmlWidth, bodyWidth), + std::max(htmlHeight, bodyHeight)); +} + +bool +TabChildBase::HandlePossibleViewportChange(const ScreenIntSize& aOldScreenSize) +{ + PuppetWidget* widget = WebWidget(); + if (!widget || !widget->AsyncPanZoomEnabled()) { + return false; + } + + TABC_LOG("HandlePossibleViewportChange aOldScreenSize=%s mInnerSize=%s\n", + Stringify(aOldScreenSize).c_str(), Stringify(GetInnerSize()).c_str()); + + nsCOMPtr document(GetDocument()); + if (!document) { + return false; + } + + nsViewportInfo viewportInfo = nsContentUtils::GetViewportInfo(document, GetInnerSize()); + uint32_t presShellId = 0; + mozilla::layers::FrameMetrics::ViewID viewId = FrameMetrics::NULL_SCROLL_ID; + APZCCallbackHelper::GetOrCreateScrollIdentifiers( + document->GetDocumentElement(), &presShellId, &viewId); + + float screenW = GetInnerSize().width; + float screenH = GetInnerSize().height; + CSSSize viewport(viewportInfo.GetSize()); + + // We're not being displayed in any way; don't bother doing anything because + // that will just confuse future adjustments. + if (!screenW || !screenH) { + return false; + } + + TABC_LOG("HandlePossibleViewportChange mOldViewportSize=%s viewport=%s\n", + Stringify(mOldViewportSize).c_str(), Stringify(viewport).c_str()); + CSSSize oldBrowserSize = mOldViewportSize; + mLastRootMetrics.SetViewport(CSSRect( + mLastRootMetrics.GetViewport().TopLeft(), viewport)); + if (oldBrowserSize == CSSSize()) { + oldBrowserSize = kDefaultViewportSize; + } + SetCSSViewport(viewport); + + // If this page has not been painted yet, then this must be getting run + // because a meta-viewport element was added (via the DOMMetaAdded handler). + // in this case, we should not do anything that forces a reflow (see bug + // 759678) such as requesting the page size or sending a viewport update. this + // code will get run again in the before-first-paint handler and that point we + // will run though all of it. the reason we even bother executing up to this + // point on the DOMMetaAdded handler is so that scripts that use + // window.innerWidth before they are painted have a correct value (bug + // 771575). + if (!mContentDocumentIsDisplayed) { + return false; + } + + ScreenIntSize oldScreenSize = aOldScreenSize; + if (oldScreenSize == ScreenIntSize()) { + oldScreenSize = GetInnerSize(); + } + + FrameMetrics metrics(mLastRootMetrics); + metrics.SetViewport(CSSRect(CSSPoint(), viewport)); + + // Calculate the composition bounds based on the inner size, excluding the sizes + // of the scrollbars if they are not overlay scrollbars. + ScreenSize compositionSize(GetInnerSize()); + nsCOMPtr shell = GetPresShell(); + if (shell) { + nsMargin scrollbarsAppUnits = + nsLayoutUtils::ScrollbarAreaToExcludeFromCompositionBoundsFor(shell->GetRootScrollFrame()); + // Scrollbars are not subject to scaling, so CSS pixels = screen pixels for them. + ScreenMargin scrollbars = CSSMargin::FromAppUnits(scrollbarsAppUnits) + * CSSToScreenScale(1.0f); + compositionSize.width -= scrollbars.LeftRight(); + compositionSize.height -= scrollbars.TopBottom(); + } + + metrics.SetCompositionBounds(ParentLayerRect( + ParentLayerPoint(), + ParentLayerSize( + ViewAs(GetInnerSize(), + PixelCastJustification::ScreenIsParentLayerForRoot)))); + metrics.SetRootCompositionSize( + ScreenSize(compositionSize) * ScreenToLayoutDeviceScale(1.0f) / metrics.GetDevPixelsPerCSSPixel()); + + // This change to the zoom accounts for all types of changes I can conceive: + // 1. screen size changes, CSS viewport does not (pages with no meta viewport + // or a fixed size viewport) + // 2. screen size changes, CSS viewport also does (pages with a device-width + // viewport) + // 3. screen size remains constant, but CSS viewport changes (meta viewport + // tag is added or removed) + // 4. neither screen size nor CSS viewport changes + // + // In all of these cases, we maintain how much actual content is visible + // within the screen width. Note that "actual content" may be different with + // respect to CSS pixels because of the CSS viewport size changing. + CSSToScreenScale oldIntrinsicScale = CalculateIntrinsicScale(oldScreenSize, oldBrowserSize); + CSSToScreenScale newIntrinsicScale = CalculateIntrinsicScale(GetInnerSize(), viewport); + metrics.ZoomBy(newIntrinsicScale.scale / oldIntrinsicScale.scale); + + // Changing the zoom when we're not doing a first paint will get ignored + // by AsyncPanZoomController and causes a blurry flash. + bool isFirstPaint = true; + if (shell) { + isFirstPaint = shell->GetIsFirstPaint(); + } + if (isFirstPaint) { + // FIXME/bug 799585(?): GetViewportInfo() returns a defaultZoom of + // 0.0 to mean "did not calculate a zoom". In that case, we default + // it to the intrinsic scale. + if (viewportInfo.GetDefaultZoom().scale < 0.01f) { + viewportInfo.SetDefaultZoom(newIntrinsicScale); + } + + CSSToScreenScale defaultZoom = viewportInfo.GetDefaultZoom(); + MOZ_ASSERT(viewportInfo.GetMinZoom() <= defaultZoom && + defaultZoom <= viewportInfo.GetMaxZoom()); + metrics.SetZoom(CSSToParentLayerScale2D(ConvertScaleForRoot(defaultZoom))); + + metrics.SetPresShellId(presShellId); + metrics.SetScrollId(viewId); + } + + if (shell) { + if (nsPresContext* context = shell->GetPresContext()) { + metrics.SetDevPixelsPerCSSPixel(CSSToLayoutDeviceScale( + (float)nsPresContext::AppUnitsPerCSSPixel() / context->AppUnitsPerDevPixel())); + } + } + + metrics.SetCumulativeResolution(metrics.GetZoom() + / metrics.GetDevPixelsPerCSSPixel() + * ParentLayerToLayerScale(1)); + // This is the root layer, so the cumulative resolution is the same + // as the resolution. + metrics.SetPresShellResolution(metrics.GetCumulativeResolution().ToScaleFactor().scale); + if (shell) { + nsLayoutUtils::SetResolutionAndScaleTo(shell, metrics.GetPresShellResolution()); + nsLayoutUtils::SetScrollPositionClampingScrollPortSize(shell, + metrics.CalculateCompositedSizeInCssPixels()); + } + + // The call to GetPageSize forces a resize event to content, so we need to + // make sure that we have the right CSS viewport and + // scrollPositionClampingScrollPortSize set up before that happens. + + CSSSize pageSize = GetPageSize(document, viewport); + if (!pageSize.width) { + // Return early rather than divide by 0. + return false; + } + metrics.SetScrollableRect(CSSRect(CSSPoint(), pageSize)); + + // Calculate a display port _after_ having a scrollable rect because the + // display port is clamped to the scrollable rect. + metrics.SetDisplayPortMargins(APZCTreeManager::CalculatePendingDisplayPort( + // The page must have been refreshed in some way such as a new document or + // new CSS viewport, so we know that there's no velocity, acceleration, and + // we have no idea how long painting will take. + metrics, ParentLayerPoint(0.0f, 0.0f), 0.0)); + metrics.SetUseDisplayPortMargins(); + + // Force a repaint with these metrics. This, among other things, sets the + // displayport, so we start with async painting. + mLastRootMetrics = ProcessUpdateFrame(metrics); + + return true; +} + +already_AddRefed +TabChildBase::GetDOMWindowUtils() +{ + nsCOMPtr window = do_GetInterface(WebNavigation()); + nsCOMPtr utils = do_GetInterface(window); + return utils.forget(); +} + already_AddRefed TabChildBase::GetDocument() const { @@ -260,7 +535,7 @@ TabChildBase::UpdateFrameHandler(const FrameMetrics& aFrameMetrics) // Guard against stale updates (updates meant for a pres shell which // has since been torn down and destroyed). if (aFrameMetrics.GetPresShellId() == shell->GetPresShellId()) { - ProcessUpdateFrame(aFrameMetrics); + mLastRootMetrics = ProcessUpdateFrame(aFrameMetrics); return true; } } @@ -274,11 +549,11 @@ TabChildBase::UpdateFrameHandler(const FrameMetrics& aFrameMetrics) return true; } -void +FrameMetrics TabChildBase::ProcessUpdateFrame(const FrameMetrics& aFrameMetrics) { if (!mGlobal || !mTabChildGlobal) { - return; + return aFrameMetrics; } FrameMetrics newMetrics = aFrameMetrics; @@ -316,6 +591,7 @@ TabChildBase::ProcessUpdateFrame(const FrameMetrics& aFrameMetrics) data.AppendLiteral(" }"); DispatchMessageManagerMessage(NS_LITERAL_STRING("Viewport:Change"), data); + return newMetrics; } NS_IMETHODIMP @@ -633,6 +909,22 @@ TabChild::TabChild(nsIContentChild* aManager, } } +NS_IMETHODIMP +TabChild::HandleEvent(nsIDOMEvent* aEvent) +{ + nsAutoString eventType; + aEvent->GetType(eventType); + if (eventType.EqualsLiteral("DOMMetaAdded")) { + // This meta data may or may not have been a meta viewport tag. If it was, + // we should handle it immediately. + HandlePossibleViewportChange(GetInnerSize()); + } else if (eventType.EqualsLiteral("FullZoomChange")) { + HandlePossibleViewportChange(GetInnerSize()); + } + + return NS_OK; +} + NS_IMETHODIMP TabChild::Observe(nsISupports *aSubject, const char *aTopic, @@ -665,7 +957,19 @@ TabChild::Observe(nsISupports *aSubject, shell->SetIsFirstPaint(true); } - APZCCallbackHelper::InitializeRootDisplayport(shell); + mContentDocumentIsDisplayed = true; + + // In some cases before-first-paint gets called before + // RecvUpdateDimensions is called and therefore before we have an + // inner size value set. In such cases defer initializing the viewport + // until we we get an inner size. + if (HasValidInnerSize()) { + InitializeRootMetrics(); + if (shell) { + nsLayoutUtils::SetResolutionAndScaleTo(shell, mLastRootMetrics.GetPresShellResolution()); + } + HandlePossibleViewportChange(GetInnerSize()); + } } } } @@ -723,6 +1027,100 @@ TabChild::Observe(nsISupports *aSubject, return NS_OK; } +NS_IMETHODIMP +TabChild::OnStateChange(nsIWebProgress* aWebProgress, + nsIRequest* aRequest, + uint32_t aStateFlags, + nsresult aStatus) +{ + NS_NOTREACHED("not implemented in TabChild"); + return NS_OK; +} + +NS_IMETHODIMP +TabChild::OnProgressChange(nsIWebProgress* aWebProgress, + nsIRequest* aRequest, + int32_t aCurSelfProgress, + int32_t aMaxSelfProgress, + int32_t aCurTotalProgress, + int32_t aMaxTotalProgress) +{ + NS_NOTREACHED("not implemented in TabChild"); + return NS_OK; +} + +NS_IMETHODIMP +TabChild::OnLocationChange(nsIWebProgress* aWebProgress, + nsIRequest* aRequest, + nsIURI *aLocation, + uint32_t aFlags) +{ + if (!AsyncPanZoomEnabled()) { + return NS_OK; + } + + nsCOMPtr window; + aWebProgress->GetDOMWindow(getter_AddRefs(window)); + if (!window) { + return NS_OK; + } + + nsCOMPtr progressDoc; + window->GetDocument(getter_AddRefs(progressDoc)); + if (!progressDoc) { + return NS_OK; + } + + nsCOMPtr domDoc; + WebNavigation()->GetDocument(getter_AddRefs(domDoc)); + if (!domDoc || !SameCOMIdentity(domDoc, progressDoc)) { + return NS_OK; + } + + nsCOMPtr urifixup(do_GetService(NS_URIFIXUP_CONTRACTID)); + if (!urifixup) { + return NS_OK; + } + + nsCOMPtr exposableURI; + urifixup->CreateExposableURI(aLocation, getter_AddRefs(exposableURI)); + if (!exposableURI) { + return NS_OK; + } + + if (!(aFlags & nsIWebProgressListener::LOCATION_CHANGE_SAME_DOCUMENT)) { + mContentDocumentIsDisplayed = false; + } else if (mLastURI != nullptr) { + bool exposableEqualsLast, exposableEqualsNew; + exposableURI->Equals(mLastURI.get(), &exposableEqualsLast); + exposableURI->Equals(aLocation, &exposableEqualsNew); + if (exposableEqualsLast && !exposableEqualsNew) { + mContentDocumentIsDisplayed = false; + } + } + + return NS_OK; +} + +NS_IMETHODIMP +TabChild::OnStatusChange(nsIWebProgress* aWebProgress, + nsIRequest* aRequest, + nsresult aStatus, + const char16_t* aMessage) +{ + NS_NOTREACHED("not implemented in TabChild"); + return NS_OK; +} + +NS_IMETHODIMP +TabChild::OnSecurityChange(nsIWebProgress* aWebProgress, + nsIRequest* aRequest, + uint32_t aState) +{ + NS_NOTREACHED("not implemented in TabChild"); + return NS_OK; +} + bool TabChild::DoUpdateZoomConstraints(const uint32_t& aPresShellId, const ViewID& aViewId, @@ -803,6 +1201,10 @@ TabChild::Init() loadContext->SetRemoteTabs( mChromeFlags & nsIWebBrowserChrome::CHROME_REMOTE_WINDOW); + nsCOMPtr webProgress = do_GetInterface(docShell); + NS_ENSURE_TRUE(webProgress, NS_ERROR_FAILURE); + webProgress->AddProgressListener(this, nsIWebProgress::NOTIFY_LOCATION); + // Few lines before, baseWindow->Create() will end up creating a new // window root in nsGlobalWindow::SetDocShell. // Then this chrome event handler, will be inherited to inner windows. @@ -847,6 +1249,8 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(TabChild) NS_INTERFACE_MAP_ENTRY(nsIWebBrowserChromeFocus) NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor) NS_INTERFACE_MAP_ENTRY(nsIWindowProvider) + NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener) + NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener) NS_INTERFACE_MAP_ENTRY(nsITabChild) NS_INTERFACE_MAP_ENTRY(nsIObserver) NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) @@ -1247,6 +1651,12 @@ TabChild::ProvideWindowCommon(nsIDOMWindow* aOpener, return NS_OK; } +bool +TabChild::HasValidInnerSize() +{ + return mHasValidInnerSize; +} + void TabChild::DestroyWindow() { @@ -1706,25 +2116,40 @@ TabChild::RecvUpdateDimensions(const CSSRect& rect, const CSSSize& size, mUnscaledOuterRect = rect; mChromeDisp = chromeDisp; + bool initialSizing = !HasValidInnerSize() + && (size.width != 0 && size.height != 0); + mOrientation = orientation; + ScreenIntSize oldScreenSize = GetInnerSize(); SetUnscaledInnerSize(size); - if (!mHasValidInnerSize && size.width != 0 && size.height != 0) { + ScreenIntSize screenSize = GetInnerSize(); + bool sizeChanged = true; + if (initialSizing) { mHasValidInnerSize = true; + } else if (screenSize == oldScreenSize) { + sizeChanged = false; } - ScreenIntSize screenSize = GetInnerSize(); ScreenIntRect screenRect = GetOuterRect(); + mPuppetWidget->Resize(screenRect.x + chromeDisp.x, + screenRect.y + chromeDisp.y, + screenSize.width, screenSize.height, true); - // 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 baseWin = do_QueryInterface(WebNavigation()); baseWin->SetPositionAndSize(0, 0, screenSize.width, screenSize.height, true); - mPuppetWidget->Resize(screenRect.x + chromeDisp.x, - screenRect.y + chromeDisp.y, - screenSize.width, screenSize.height, true); + if (initialSizing && mContentDocumentIsDisplayed) { + // If this is the first time we're getting a valid inner size, and the + // before-first-paint event has already been handled, then we need to set + // up our default viewport here. See the corresponding call to + // InitializeRootMetrics in the before-first-paint handler. + InitializeRootMetrics(); + } + + if (sizeChanged) { + HandlePossibleViewportChange(oldScreenSize); + } return true; } @@ -2576,6 +3001,9 @@ TabChild::InitTabChildGlobal(FrameScriptLoading aScriptLoading) nsCOMPtr root = do_QueryInterface(chromeHandler); NS_ENSURE_TRUE(root, false); root->SetParentTarget(scope); + + chromeHandler->AddEventListener(NS_LITERAL_STRING("DOMMetaAdded"), this, false); + chromeHandler->AddEventListener(NS_LITERAL_STRING("FullZoomChange"), this, false); } if (aScriptLoading != DONT_LOAD_SCRIPTS && !mTriedBrowserInit) { diff --git a/dom/ipc/TabChild.h b/dom/ipc/TabChild.h index 30f4efd31e5e..033982431bb9 100644 --- a/dom/ipc/TabChild.h +++ b/dom/ipc/TabChild.h @@ -21,6 +21,7 @@ #include "nsIDocShell.h" #include "nsIInterfaceRequestorUtils.h" #include "nsFrameMessageManager.h" +#include "nsIWebProgressListener.h" #include "nsIPresShell.h" #include "nsIScriptObjectPrincipal.h" #include "nsWeakReference.h" @@ -182,6 +183,12 @@ public: virtual nsIWebNavigation* WebNavigation() const = 0; virtual PuppetWidget* WebWidget() = 0; nsIPrincipal* GetPrincipal() { return mPrincipal; } + // Recalculates the display state, including the CSS + // viewport. This should be called whenever we believe the + // viewport data on a document may have changed. If it didn't + // change, this function doesn't do anything. However, it should + // not be called all the time as it is fairly expensive. + bool HandlePossibleViewportChange(const ScreenIntSize& aOldScreenSize); virtual bool DoUpdateZoomConstraints(const uint32_t& aPresShellId, const mozilla::layers::FrameMetrics::ViewID& aViewId, const Maybe& aConstraints) = 0; @@ -190,12 +197,19 @@ public: protected: virtual ~TabChildBase(); + CSSSize GetPageSize(nsCOMPtr aDocument, const CSSSize& aViewport); + // Get the DOMWindowUtils for the top-level window in this tab. + already_AddRefed GetDOMWindowUtils(); // Get the Document for the top-level window in this tab. already_AddRefed GetDocument() const; // Get the pres-shell of the document for the top-level window in this tab. already_AddRefed GetPresShell() const; + // Wrapper for nsIDOMWindowUtils.setCSSViewport(). This updates some state + // variables local to this class before setting it. + void SetCSSViewport(const CSSSize& aSize); + // Wraps up a JSON object as a structured clone and sends it to the browser // chrome script. // @@ -204,12 +218,17 @@ protected: void DispatchMessageManagerMessage(const nsAString& aMessageName, const nsAString& aJSONData); - void ProcessUpdateFrame(const mozilla::layers::FrameMetrics& aFrameMetrics); + void InitializeRootMetrics(); + + mozilla::layers::FrameMetrics ProcessUpdateFrame(const mozilla::layers::FrameMetrics& aFrameMetrics); bool UpdateFrameHandler(const mozilla::layers::FrameMetrics& aFrameMetrics); protected: + CSSSize mOldViewportSize; + bool mContentDocumentIsDisplayed; nsRefPtr mTabChildGlobal; + mozilla::layers::FrameMetrics mLastRootMetrics; nsCOMPtr mWebBrowserChrome; }; @@ -220,6 +239,8 @@ class TabChild final : public TabChildBase, public nsIWebBrowserChromeFocus, public nsIInterfaceRequestor, public nsIWindowProvider, + public nsIDOMEventListener, + public nsIWebProgressListener, public nsSupportsWeakReference, public nsITabChild, public nsIObserver, @@ -267,6 +288,8 @@ public: NS_DECL_NSIWEBBROWSERCHROMEFOCUS NS_DECL_NSIINTERFACEREQUESTOR NS_DECL_NSIWINDOWPROVIDER + NS_DECL_NSIDOMEVENTLISTENER + NS_DECL_NSIWEBPROGRESSLISTENER NS_DECL_NSITABCHILD NS_DECL_NSIOBSERVER NS_DECL_NSITOOLTIPLISTENER diff --git a/layout/base/MobileViewportManager.cpp b/layout/base/MobileViewportManager.cpp deleted file mode 100644 index 89587ce7f2f3..000000000000 --- a/layout/base/MobileViewportManager.cpp +++ /dev/null @@ -1,279 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* 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 "MobileViewportManager.h" - -#include "LayersLogging.h" -#include "nsViewManager.h" -#include "nsViewportInfo.h" - -#define MVM_LOG(...) -// #define MVM_LOG(...) printf_stderr("MVM: " __VA_ARGS__) - -NS_IMPL_ISUPPORTS(MobileViewportManager, nsIDOMEventListener, nsIObserver) - -static const nsLiteralString DOM_META_ADDED = NS_LITERAL_STRING("DOMMetaAdded"); -static const nsLiteralString FULL_ZOOM_CHANGE = NS_LITERAL_STRING("FullZoomChange"); -static const nsLiteralCString BEFORE_FIRST_PAINT = NS_LITERAL_CSTRING("before-first-paint"); - -using namespace mozilla; -using namespace mozilla::layers; - -MobileViewportManager::MobileViewportManager(nsIPresShell* aPresShell, - nsIDocument* aDocument) - : mDocument(aDocument) - , mPresShell(aPresShell) - , mIsFirstPaint(false) -{ - MOZ_ASSERT(mPresShell); - MOZ_ASSERT(mDocument); - - MVM_LOG("%p: creating with presShell %p document %p\n", this, mPresShell, aDocument); - - if (nsCOMPtr window = mDocument->GetWindow()) { - mEventTarget = window->GetChromeEventHandler(); - } - if (mEventTarget) { - mEventTarget->AddEventListener(DOM_META_ADDED, this, false); - mEventTarget->AddEventListener(FULL_ZOOM_CHANGE, this, false); - } - - nsCOMPtr observerService = mozilla::services::GetObserverService(); - if (observerService) { - observerService->AddObserver(this, BEFORE_FIRST_PAINT.Data(), false); - } -} - -MobileViewportManager::~MobileViewportManager() -{ -} - -void -MobileViewportManager::Destroy() -{ - MVM_LOG("%p: destroying\n", this); - - if (mEventTarget) { - mEventTarget->RemoveEventListener(DOM_META_ADDED, this, false); - mEventTarget->RemoveEventListener(FULL_ZOOM_CHANGE, this, false); - mEventTarget = nullptr; - } - - nsCOMPtr observerService = mozilla::services::GetObserverService(); - if (observerService) { - observerService->RemoveObserver(this, BEFORE_FIRST_PAINT.Data()); - } - - mDocument = nullptr; - mPresShell = nullptr; -} - -void -MobileViewportManager::RequestReflow() -{ - MVM_LOG("%p: got a reflow request\n", this); - RefreshViewportSize(false); -} - -NS_IMETHODIMP -MobileViewportManager::HandleEvent(nsIDOMEvent* event) -{ - nsAutoString type; - event->GetType(type); - - if (type.Equals(DOM_META_ADDED)) { - MVM_LOG("%p: got a dom-meta-added event\n", this); - RefreshViewportSize(true); - } else if (type.Equals(FULL_ZOOM_CHANGE)) { - MVM_LOG("%p: got a full-zoom-change event\n", this); - RefreshViewportSize(false); - } - return NS_OK; -} - -NS_IMETHODIMP -MobileViewportManager::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData) -{ - if (SameCOMIdentity(aSubject, mDocument) && BEFORE_FIRST_PAINT.EqualsASCII(aTopic)) { - MVM_LOG("%p: got a before-first-paint event\n", this); - mIsFirstPaint = true; - RefreshViewportSize(false); - } - return NS_OK; -} - -CSSToScreenScale -MobileViewportManager::UpdateResolution(const nsViewportInfo& aViewportInfo, - const ScreenIntSize& aDisplaySize, - const CSSSize& aViewport, - const Maybe& aDisplayWidthChangeRatio) -{ - CSSToLayoutDeviceScale cssToDev((float)nsPresContext::AppUnitsPerCSSPixel() - / mPresShell->GetPresContext()->AppUnitsPerDevPixel()); - - if (mIsFirstPaint) { - CSSToScreenScale defaultZoom = aViewportInfo.GetDefaultZoom(); - MVM_LOG("%p: default zoom from viewport is %f\n", this, defaultZoom.scale); - // FIXME/bug 799585(?): GetViewportInfo() returns a default zoom of - // 0.0 to mean "did not calculate a zoom". In that case, we default - // it to the intrinsic scale. - if (defaultZoom.scale < 0.01f) { - defaultZoom = MaxScaleRatio(ScreenSize(aDisplaySize), aViewport); - MVM_LOG("%p: Intrinsic computed zoom is %f\n", this, defaultZoom.scale); - } - MOZ_ASSERT(aViewportInfo.GetMinZoom() <= defaultZoom && - defaultZoom <= aViewportInfo.GetMaxZoom()); - - CSSToParentLayerScale zoom = ViewTargetAs(defaultZoom, - PixelCastJustification::ScreenIsParentLayerForRoot); - - LayoutDeviceToLayerScale resolution = zoom / cssToDev * ParentLayerToLayerScale(1); - MVM_LOG("%p: setting resolution %f\n", this, resolution.scale); - nsLayoutUtils::SetResolutionAndScaleTo(mPresShell, resolution.scale); - - return defaultZoom; - } - - // If this is not a first paint, then in some cases we want to update the pre- - // existing resolution so as to maintain how much actual content is visible - // within the display width. Note that "actual content" may be different with - // respect to CSS pixels because of the CSS viewport size changing. - // - // aDisplayWidthChangeRatio is non-empty if: - // (a) The meta-viewport tag information changes, and so the CSS viewport - // might change as a result. In this case, we want to adjust the zoom to - // compensate. OR - // (b) The display size changed from a nonzero value to another nonzero value. - // This covers the case where e.g. the device was rotated, and again we - // want to adjust the zoom to compensate. - // Note in particular that aDisplayWidthChangeRatio will be None if all that - // happened was a change in the full-zoom. In this case, we still want to - // compute a new CSS viewport, but we don't want to update the resolution. - // - // Given the above, the algorithm below accounts for all types of changes I - // can conceive of: - // 1. screen size changes, CSS viewport does not (pages with no meta viewport - // or a fixed size viewport) - // 2. screen size changes, CSS viewport also does (pages with a device-width - // viewport) - // 3. screen size remains constant, but CSS viewport changes (meta viewport - // tag is added or removed) - // 4. neither screen size nor CSS viewport changes - LayoutDeviceToLayerScale res(nsLayoutUtils::GetResolution(mPresShell)); - if (aDisplayWidthChangeRatio) { - float cssViewportChangeRatio = (mMobileViewportSize.width == 0) - ? 1.0f : aViewport.width / mMobileViewportSize.width; - LayoutDeviceToLayerScale newRes(res.scale * aDisplayWidthChangeRatio.value() - / cssViewportChangeRatio); - MVM_LOG("%p: Old resolution was %f, changed by %f/%f to %f\n", this, res.scale, - aDisplayWidthChangeRatio.value(), cssViewportChangeRatio, newRes.scale); - nsLayoutUtils::SetResolutionAndScaleTo(mPresShell, newRes.scale); - res = newRes; - } - - return ViewTargetAs(cssToDev * res / ParentLayerToLayerScale(1), - PixelCastJustification::ScreenIsParentLayerForRoot); -} - -void -MobileViewportManager::UpdateSPCSPS(const ScreenIntSize& aDisplaySize, - const CSSToScreenScale& aZoom) -{ - ScreenSize compositionSize(aDisplaySize); - ScreenMargin scrollbars = - CSSMargin::FromAppUnits( - nsLayoutUtils::ScrollbarAreaToExcludeFromCompositionBoundsFor( - mPresShell->GetRootScrollFrame())) - * CSSToScreenScale(1.0f); // Scrollbars are not subject to scaling, so - // CSS pixels = layer pixels for them (modulo bug 1168487). - compositionSize.width -= scrollbars.LeftRight(); - compositionSize.height -= scrollbars.TopBottom(); - CSSSize compSize = compositionSize / aZoom; - MVM_LOG("%p: Setting SPCSPS %s\n", this, Stringify(compSize).c_str()); - nsLayoutUtils::SetScrollPositionClampingScrollPortSize(mPresShell, compSize); -} - -void -MobileViewportManager::UpdateDisplayPortMargins() -{ - if (nsIScrollableFrame* root = mPresShell->GetRootScrollFrameAsScrollable()) { - nsLayoutUtils::CalculateAndSetDisplayPortMargins(root, - nsLayoutUtils::RepaintMode::DoNotRepaint); - } -} - -void -MobileViewportManager::RefreshViewportSize(bool aForceAdjustResolution) -{ - // This function gets called by the various triggers that may result in a - // change of the CSS viewport. In some of these cases (e.g. the meta-viewport - // tag changes) we want to update the resolution and in others (e.g. the full - // zoom changing) we don't want to update the resolution. See the comment in - // UpdateResolution for some more detail on this. An important assumption we - // make here is that this RefreshViewportSize function will be called - // separately for each trigger that changes. For instance it should never get - // called such that both the full zoom and the meta-viewport tag have changed; - // instead it would get called twice - once after each trigger changes. This - // assumption is what allows the aForceAdjustResolution parameter to work as - // intended; if this assumption is violated then we will need to add extra - // complicated logic in UpdateResolution to ensure we only do the resolution - // update in the right scenarios. - - Maybe displayWidthChangeRatio; - LayoutDeviceIntSize newDisplaySize; - if (nsLayoutUtils::GetContentViewerSize(mPresShell->GetPresContext(), newDisplaySize)) { - // See the comment in UpdateResolution for why we're doing this. - if (mDisplaySize.width > 0) { - if (aForceAdjustResolution || mDisplaySize.width != newDisplaySize.width) { - displayWidthChangeRatio = Some((float)newDisplaySize.width / (float)mDisplaySize.width); - } - } else if (aForceAdjustResolution) { - displayWidthChangeRatio = Some(1.0f); - } - - MVM_LOG("%p: Display width change ratio is %f\n", this, displayWidthChangeRatio.valueOr(0.0f)); - mDisplaySize = newDisplaySize; - } - - MVM_LOG("%p: Computing CSS viewport using %d,%d\n", this, - mDisplaySize.width, mDisplaySize.height); - if (mDisplaySize.width == 0 || mDisplaySize.height == 0) { - // We can't do anything useful here, we should just bail out - return; - } - - ScreenIntSize displaySize = ViewAs( - mDisplaySize, PixelCastJustification::LayoutDeviceIsScreenForBounds); - nsViewportInfo viewportInfo = nsContentUtils::GetViewportInfo( - mDocument, displaySize); - - CSSSize viewport = viewportInfo.GetSize(); - MVM_LOG("%p: Computed CSS viewport %s\n", this, Stringify(viewport).c_str()); - - if (!mIsFirstPaint && mMobileViewportSize == viewport) { - // Nothing changed, so no need to do a reflow - return; - } - - // If it's the first-paint or the viewport changed, we need to update - // various APZ properties (the zoom and some things that might depend on it) - MVM_LOG("%p: Updating properties because %d || %d\n", this, - mIsFirstPaint, mMobileViewportSize != viewport); - - CSSToScreenScale zoom = UpdateResolution(viewportInfo, displaySize, viewport, - displayWidthChangeRatio); - MVM_LOG("%p: New zoom is %f\n", this, zoom.scale); - UpdateSPCSPS(displaySize, zoom); - UpdateDisplayPortMargins(); - - // Update internal state. - mIsFirstPaint = false; - mMobileViewportSize = viewport; - - // Kick off a reflow. - mPresShell->ResizeReflowIgnoreOverride( - nsPresContext::CSSPixelsToAppUnits(viewport.width), - nsPresContext::CSSPixelsToAppUnits(viewport.height)); -} diff --git a/layout/base/MobileViewportManager.h b/layout/base/MobileViewportManager.h deleted file mode 100644 index 26676c602a4f..000000000000 --- a/layout/base/MobileViewportManager.h +++ /dev/null @@ -1,62 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* 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 MobileViewportManager_h_ -#define MobileViewportManager_h_ - -#include "mozilla/Maybe.h" -#include "nsIDOMEventListener.h" -#include "nsIDocument.h" -#include "nsIObserver.h" -#include "Units.h" - -class nsIDOMEventTarget; -class nsIDocument; -class nsIPresShell; - -class MobileViewportManager final : public nsIDOMEventListener - , public nsIObserver -{ -public: - NS_DECL_ISUPPORTS - NS_DECL_NSIDOMEVENTLISTENER - NS_DECL_NSIOBSERVER - - MobileViewportManager(nsIPresShell* aPresShell, - nsIDocument* aDocument); - void Destroy(); - - /* Notify the MobileViewportManager that a reflow was requested in the - * presShell.*/ - void RequestReflow(); - -private: - ~MobileViewportManager(); - - /* Main helper method to update the CSS viewport and any other properties that - * need updating. */ - void RefreshViewportSize(bool aForceAdjustResolution); - - /* Updates the presShell resolution and returns the new zoom. */ - mozilla::CSSToScreenScale UpdateResolution(const nsViewportInfo& aViewportInfo, - const mozilla::ScreenIntSize& aDisplaySize, - const mozilla::CSSSize& aViewport, - const mozilla::Maybe& aDisplayWidthChangeRatio); - /* Updates the scroll-position-clamping scrollport size */ - void UpdateSPCSPS(const mozilla::ScreenIntSize& aDisplaySize, - const mozilla::CSSToScreenScale& aZoom); - /* Updates the displayport margins for the presShell's root scrollable frame */ - void UpdateDisplayPortMargins(); - - nsCOMPtr mDocument; - nsIPresShell* MOZ_NON_OWNING_REF mPresShell; // raw ref since the presShell owns this - nsCOMPtr mEventTarget; - bool mIsFirstPaint; - mozilla::LayoutDeviceIntSize mDisplaySize; - mozilla::CSSSize mMobileViewportSize; -}; - -#endif - diff --git a/layout/base/moz.build b/layout/base/moz.build index 7e8797f2e62d..ff5755fb31e8 100644 --- a/layout/base/moz.build +++ b/layout/base/moz.build @@ -112,7 +112,6 @@ UNIFIED_SOURCES += [ 'GeometryUtils.cpp', 'LayoutLogging.cpp', 'MaskLayerImageCache.cpp', - 'MobileViewportManager.cpp', 'nsBidi.cpp', 'nsBidiPresUtils.cpp', 'nsCaret.cpp', diff --git a/layout/base/nsIPresShell.h b/layout/base/nsIPresShell.h index 6580620bdb04..7040c5e08c7f 100644 --- a/layout/base/nsIPresShell.h +++ b/layout/base/nsIPresShell.h @@ -139,10 +139,10 @@ typedef struct CapturingContentInfo { mozilla::StaticRefPtr mContent; } CapturingContentInfo; -// 4f512d0b-c58c-4fc9-ae42-8aa6d992e7ae +// 7f0ae6b1-5fa1-4ba7-885e-a93e17d72cd2 #define NS_IPRESSHELL_IID \ -{ 0x4f512d0b, 0xc58c, 0x4fc9, \ - { 0xae, 0x42, 0x8a, 0xa6, 0xd9, 0x92, 0xe7, 0xae } } +{ 0x7f0ae6b1, 0x5fa1, 0x4ba7, \ + { 0x88, 0x5e, 0xa9, 0x3e, 0x17, 0xd7, 0x2c, 0xd2 } } // debug VerifyReflow flags #define VERIFY_REFLOW_ON 0x01 @@ -410,11 +410,6 @@ public: * ResizeReflow() calls are ignored after ResizeReflowOverride(). */ virtual nsresult ResizeReflowOverride(nscoord aWidth, nscoord aHeight) = 0; - /** - * Do the same thing as ResizeReflow but even if ResizeReflowOverride was - * called previously. - */ - virtual nsresult ResizeReflowIgnoreOverride(nscoord aWidth, nscoord aHeight) = 0; /** * Returns true if ResizeReflowOverride has been called. diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp index f8864178f68d..0c577eddc2b2 100644 --- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -984,9 +984,6 @@ PresShell::Init(nsIDocument* aDocument, if (mPresContext->IsRootContentDocument()) { mZoomConstraintsClient = new ZoomConstraintsClient(); mZoomConstraintsClient->Init(this, mDocument); - if (gfxPrefs::MetaViewportEnabled()) { - mMobileViewportManager = new MobileViewportManager(this, mDocument); - } } } @@ -1105,10 +1102,6 @@ PresShell::Destroy() mZoomConstraintsClient->Destroy(); mZoomConstraintsClient = nullptr; } - if (mMobileViewportManager) { - mMobileViewportManager->Destroy(); - mMobileViewportManager = nullptr; - } #ifdef ACCESSIBILITY if (mDocAccessible) { @@ -1759,19 +1752,6 @@ nsresult PresShell::ResizeReflowOverride(nscoord aWidth, nscoord aHeight) { mViewportOverridden = true; - - if (mMobileViewportManager) { - // Once the viewport is explicitly overridden, we don't need the - // MobileViewportManager any more (in this presShell at least). Destroying - // it simplifies things because then it can maintain an invariant that any - // time it gets a meta-viewport change (for example) it knows it must - // recompute the CSS viewport and do a reflow. If we didn't destroy it here - // then there would be times where a meta-viewport change would have no - // effect. - mMobileViewportManager->Destroy(); - mMobileViewportManager = nullptr; - } - return ResizeReflowIgnoreOverride(aWidth, aHeight); } @@ -1783,15 +1763,6 @@ PresShell::ResizeReflow(nscoord aWidth, nscoord aHeight) // didn't ask to ignore the override. Pretend it didn't happen. return NS_OK; } - - if (mMobileViewportManager) { - // If we have a mobile viewport manager, request a reflow from it. It can - // recompute the final CSS viewport and trigger a call to - // ResizeReflowIgnoreOverride if it changed. - mMobileViewportManager->RequestReflow(); - return NS_OK; - } - return ResizeReflowIgnoreOverride(aWidth, aHeight); } diff --git a/layout/base/nsPresShell.h b/layout/base/nsPresShell.h index 574ff9ea8f58..9bcb48f11923 100644 --- a/layout/base/nsPresShell.h +++ b/layout/base/nsPresShell.h @@ -36,7 +36,6 @@ #include "mozilla/Attributes.h" #include "mozilla/EventForwards.h" #include "mozilla/MemoryReporting.h" -#include "MobileViewportManager.h" #include "ZoomConstraintsClient.h" class nsRange; @@ -106,7 +105,6 @@ public: virtual nsresult Initialize(nscoord aWidth, nscoord aHeight) override; virtual nsresult ResizeReflow(nscoord aWidth, nscoord aHeight) override; virtual nsresult ResizeReflowOverride(nscoord aWidth, nscoord aHeight) override; - virtual nsresult ResizeReflowIgnoreOverride(nscoord aWidth, nscoord aHeight) override; virtual nsIPageSequenceFrame* GetPageSequenceFrame() const override; virtual nsCanvasFrame* GetCanvasFrame() const override; virtual nsIFrame* GetRealPrimaryFrameFor(nsIContent* aContent) const override; @@ -358,9 +356,7 @@ public: virtual nsresult SetIsActive(bool aIsActive) override; - virtual bool GetIsViewportOverridden() override { - return mViewportOverridden || (mMobileViewportManager != nullptr); - } + virtual bool GetIsViewportOverridden() override { return mViewportOverridden; } virtual bool IsLayoutFlushObserver() override { @@ -452,6 +448,9 @@ protected: // sets up. void ScheduleReflow(); + // Reflow regardless of whether the override bit has been set. + nsresult ResizeReflowIgnoreOverride(nscoord aWidth, nscoord aHeight); + // DoReflow returns whether the reflow finished without interruption bool DoReflow(nsIFrame* aFrame, bool aInterruptible); #ifdef DEBUG @@ -819,7 +818,6 @@ protected: TouchManager mTouchManager; nsRefPtr mZoomConstraintsClient; - nsRefPtr mMobileViewportManager; // TouchCaret nsRefPtr mTouchCaret;