LCOV - code coverage report
Current view: top level - layout/base - MobileViewportManager.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 5 178 2.8 %
Date: 2017-07-14 16:53:18 Functions: 0 20 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* This Source Code Form is subject to the terms of the Mozilla Public
       3             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       4             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       5             : 
       6             : #include "MobileViewportManager.h"
       7             : 
       8             : #include "gfxPrefs.h"
       9             : #include "LayersLogging.h"
      10             : #include "mozilla/PresShell.h"
      11             : #include "nsIDOMEvent.h"
      12             : #include "nsIFrame.h"
      13             : #include "nsLayoutUtils.h"
      14             : #include "nsViewManager.h"
      15             : #include "nsViewportInfo.h"
      16             : #include "UnitTransforms.h"
      17             : #include "nsIDocument.h"
      18             : 
      19             : #define MVM_LOG(...)
      20             : // #define MVM_LOG(...) printf_stderr("MVM: " __VA_ARGS__)
      21             : 
      22           0 : NS_IMPL_ISUPPORTS(MobileViewportManager, nsIDOMEventListener, nsIObserver)
      23             : 
      24           3 : static const nsLiteralString DOM_META_ADDED = NS_LITERAL_STRING("DOMMetaAdded");
      25           3 : static const nsLiteralString DOM_META_CHANGED = NS_LITERAL_STRING("DOMMetaChanged");
      26           3 : static const nsLiteralString FULL_ZOOM_CHANGE = NS_LITERAL_STRING("FullZoomChange");
      27           3 : static const nsLiteralString LOAD = NS_LITERAL_STRING("load");
      28           3 : static const nsLiteralCString BEFORE_FIRST_PAINT = NS_LITERAL_CSTRING("before-first-paint");
      29             : 
      30             : using namespace mozilla;
      31             : using namespace mozilla::layers;
      32             : 
      33           0 : MobileViewportManager::MobileViewportManager(nsIPresShell* aPresShell,
      34           0 :                                              nsIDocument* aDocument)
      35             :   : mDocument(aDocument)
      36             :   , mPresShell(aPresShell)
      37             :   , mIsFirstPaint(false)
      38           0 :   , mPainted(false)
      39             : {
      40           0 :   MOZ_ASSERT(mPresShell);
      41           0 :   MOZ_ASSERT(mDocument);
      42             : 
      43             :   MVM_LOG("%p: creating with presShell %p document %p\n", this, mPresShell, aDocument);
      44             : 
      45           0 :   if (nsCOMPtr<nsPIDOMWindowOuter> window = mDocument->GetWindow()) {
      46           0 :     mEventTarget = window->GetChromeEventHandler();
      47             :   }
      48           0 :   if (mEventTarget) {
      49           0 :     mEventTarget->AddEventListener(DOM_META_ADDED, this, false);
      50           0 :     mEventTarget->AddEventListener(DOM_META_CHANGED, this, false);
      51           0 :     mEventTarget->AddEventListener(FULL_ZOOM_CHANGE, this, false);
      52           0 :     mEventTarget->AddEventListener(LOAD, this, true);
      53             :   }
      54             : 
      55           0 :   nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
      56           0 :   if (observerService) {
      57           0 :     observerService->AddObserver(this, BEFORE_FIRST_PAINT.Data(), false);
      58             :   }
      59           0 : }
      60             : 
      61           0 : MobileViewportManager::~MobileViewportManager()
      62             : {
      63           0 : }
      64             : 
      65             : void
      66           0 : MobileViewportManager::Destroy()
      67             : {
      68             :   MVM_LOG("%p: destroying\n", this);
      69             : 
      70           0 :   if (mEventTarget) {
      71           0 :     mEventTarget->RemoveEventListener(DOM_META_ADDED, this, false);
      72           0 :     mEventTarget->RemoveEventListener(DOM_META_CHANGED, this, false);
      73           0 :     mEventTarget->RemoveEventListener(FULL_ZOOM_CHANGE, this, false);
      74           0 :     mEventTarget->RemoveEventListener(LOAD, this, true);
      75           0 :     mEventTarget = nullptr;
      76             :   }
      77             : 
      78           0 :   nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
      79           0 :   if (observerService) {
      80           0 :     observerService->RemoveObserver(this, BEFORE_FIRST_PAINT.Data());
      81             :   }
      82             : 
      83           0 :   mDocument = nullptr;
      84           0 :   mPresShell = nullptr;
      85           0 : }
      86             : 
      87             : void
      88           0 : MobileViewportManager::SetRestoreResolution(float aResolution,
      89             :                                             LayoutDeviceIntSize aDisplaySize)
      90             : {
      91           0 :   SetRestoreResolution(aResolution);
      92             :   ScreenIntSize restoreDisplaySize = ViewAs<ScreenPixel>(aDisplaySize,
      93           0 :     PixelCastJustification::LayoutDeviceIsScreenForBounds);
      94           0 :   mRestoreDisplaySize = Some(restoreDisplaySize);
      95           0 : }
      96             : 
      97             : void
      98           0 : MobileViewportManager::SetRestoreResolution(float aResolution)
      99             : {
     100           0 :   mRestoreResolution = Some(aResolution);
     101           0 : }
     102             : 
     103             : void
     104           0 : MobileViewportManager::RequestReflow()
     105             : {
     106             :   MVM_LOG("%p: got a reflow request\n", this);
     107           0 :   RefreshViewportSize(false);
     108           0 : }
     109             : 
     110             : void
     111           0 : MobileViewportManager::ResolutionUpdated()
     112             : {
     113             :   MVM_LOG("%p: resolution updated\n", this);
     114           0 :   if (!mPainted) {
     115             :     // Save the value, so our default zoom calculation
     116             :     // can take it into account later on.
     117           0 :     SetRestoreResolution(mPresShell->GetResolution());
     118             :   }
     119           0 :   RefreshSPCSPS();
     120           0 : }
     121             : 
     122             : NS_IMETHODIMP
     123           0 : MobileViewportManager::HandleEvent(nsIDOMEvent* event)
     124             : {
     125           0 :   nsAutoString type;
     126           0 :   event->GetType(type);
     127             : 
     128           0 :   if (type.Equals(DOM_META_ADDED)) {
     129             :     MVM_LOG("%p: got a dom-meta-added event\n", this);
     130           0 :     RefreshViewportSize(mPainted);
     131           0 :   } else if (type.Equals(DOM_META_CHANGED)) {
     132             :     MVM_LOG("%p: got a dom-meta-changed event\n", this);
     133           0 :     RefreshViewportSize(mPainted);
     134           0 :   } else if (type.Equals(FULL_ZOOM_CHANGE)) {
     135             :     MVM_LOG("%p: got a full-zoom-change event\n", this);
     136           0 :     RefreshViewportSize(false);
     137           0 :   } else if (type.Equals(LOAD)) {
     138             :     MVM_LOG("%p: got a load event\n", this);
     139           0 :     if (!mPainted) {
     140             :       // Load event got fired before the before-first-paint message
     141           0 :       SetInitialViewport();
     142             :     }
     143             :   }
     144           0 :   return NS_OK;
     145             : }
     146             : 
     147             : NS_IMETHODIMP
     148           0 : MobileViewportManager::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData)
     149             : {
     150           0 :   if (SameCOMIdentity(aSubject, mDocument) && BEFORE_FIRST_PAINT.EqualsASCII(aTopic)) {
     151             :     MVM_LOG("%p: got a before-first-paint event\n", this);
     152           0 :     if (!mPainted) {
     153             :       // before-first-paint message arrived before load event
     154           0 :       SetInitialViewport();
     155             :     }
     156             :   }
     157           0 :   return NS_OK;
     158             : }
     159             : 
     160             : void
     161           0 : MobileViewportManager::SetInitialViewport()
     162             : {
     163             :   MVM_LOG("%p: setting initial viewport\n", this);
     164           0 :   mIsFirstPaint = true;
     165           0 :   mPainted = true;
     166           0 :   RefreshViewportSize(false);
     167           0 : }
     168             : 
     169             : CSSToScreenScale
     170           0 : MobileViewportManager::ClampZoom(const CSSToScreenScale& aZoom,
     171             :                                  const nsViewportInfo& aViewportInfo)
     172             : {
     173           0 :   CSSToScreenScale zoom = aZoom;
     174           0 :   if (zoom < aViewportInfo.GetMinZoom()) {
     175           0 :     zoom = aViewportInfo.GetMinZoom();
     176             :     MVM_LOG("%p: Clamped to %f\n", this, zoom.scale);
     177             :   }
     178           0 :   if (zoom > aViewportInfo.GetMaxZoom()) {
     179           0 :     zoom = aViewportInfo.GetMaxZoom();
     180             :     MVM_LOG("%p: Clamped to %f\n", this, zoom.scale);
     181             :   }
     182           0 :   return zoom;
     183             : }
     184             : 
     185             : LayoutDeviceToLayerScale
     186           0 : MobileViewportManager::ScaleResolutionWithDisplayWidth(const LayoutDeviceToLayerScale& aRes,
     187             :                                                        const float& aDisplayWidthChangeRatio,
     188             :                                                        const CSSSize& aNewViewport,
     189             :                                                        const CSSSize& aOldViewport)
     190             : {
     191           0 :   float cssViewportChangeRatio = (aOldViewport.width == 0)
     192           0 :      ? 1.0f : aNewViewport.width / aOldViewport.width;
     193           0 :   LayoutDeviceToLayerScale newRes(aRes.scale * aDisplayWidthChangeRatio
     194           0 :     / cssViewportChangeRatio);
     195             :   MVM_LOG("%p: Old resolution was %f, changed by %f/%f to %f\n", this, aRes.scale,
     196             :     aDisplayWidthChangeRatio, cssViewportChangeRatio, newRes.scale);
     197           0 :   return newRes;
     198             : }
     199             : 
     200             : CSSToScreenScale
     201           0 : MobileViewportManager::UpdateResolution(const nsViewportInfo& aViewportInfo,
     202             :                                         const ScreenIntSize& aDisplaySize,
     203             :                                         const CSSSize& aViewport,
     204             :                                         const Maybe<float>& aDisplayWidthChangeRatio)
     205             : {
     206             :   CSSToLayoutDeviceScale cssToDev =
     207           0 :       mPresShell->GetPresContext()->CSSToDevPixelScale();
     208           0 :   LayoutDeviceToLayerScale res(mPresShell->GetResolution());
     209             : 
     210           0 :   if (mIsFirstPaint) {
     211           0 :     CSSToScreenScale defaultZoom;
     212           0 :     if (mRestoreResolution) {
     213           0 :       LayoutDeviceToLayerScale restoreResolution(mRestoreResolution.value());
     214           0 :       if (mRestoreDisplaySize) {
     215           0 :         CSSSize prevViewport = mDocument->GetViewportInfo(mRestoreDisplaySize.value()).GetSize();
     216           0 :         float restoreDisplayWidthChangeRatio = (mRestoreDisplaySize.value().width > 0)
     217           0 :           ? (float)aDisplaySize.width / (float)mRestoreDisplaySize.value().width : 1.0f;
     218             : 
     219           0 :         restoreResolution =
     220           0 :           ScaleResolutionWithDisplayWidth(restoreResolution,
     221             :                                           restoreDisplayWidthChangeRatio,
     222             :                                           aViewport,
     223             :                                           prevViewport);
     224             :       }
     225           0 :       defaultZoom = CSSToScreenScale(restoreResolution.scale * cssToDev.scale);
     226             :       MVM_LOG("%p: restored zoom is %f\n", this, defaultZoom.scale);
     227           0 :       defaultZoom = ClampZoom(defaultZoom, aViewportInfo);
     228             :     } else {
     229           0 :       defaultZoom = aViewportInfo.GetDefaultZoom();
     230             :       MVM_LOG("%p: default zoom from viewport is %f\n", this, defaultZoom.scale);
     231           0 :       if (!aViewportInfo.IsDefaultZoomValid()) {
     232           0 :         defaultZoom = MaxScaleRatio(ScreenSize(aDisplaySize), aViewport);
     233             :         MVM_LOG("%p: Intrinsic computed zoom is %f\n", this, defaultZoom.scale);
     234           0 :         defaultZoom = ClampZoom(defaultZoom, aViewportInfo);
     235             :       }
     236             :     }
     237           0 :     MOZ_ASSERT(aViewportInfo.GetMinZoom() <= defaultZoom &&
     238             :       defaultZoom <= aViewportInfo.GetMaxZoom());
     239             : 
     240             :     CSSToParentLayerScale zoom = ViewTargetAs<ParentLayerPixel>(defaultZoom,
     241           0 :       PixelCastJustification::ScreenIsParentLayerForRoot);
     242             : 
     243           0 :     LayoutDeviceToLayerScale resolution = zoom / cssToDev * ParentLayerToLayerScale(1);
     244             :     MVM_LOG("%p: setting resolution %f\n", this, resolution.scale);
     245           0 :     mPresShell->SetResolutionAndScaleTo(resolution.scale);
     246             : 
     247           0 :     return defaultZoom;
     248             :   }
     249             : 
     250             :   // If this is not a first paint, then in some cases we want to update the pre-
     251             :   // existing resolution so as to maintain how much actual content is visible
     252             :   // within the display width. Note that "actual content" may be different with
     253             :   // respect to CSS pixels because of the CSS viewport size changing.
     254             :   //
     255             :   // aDisplayWidthChangeRatio is non-empty if:
     256             :   // (a) The meta-viewport tag information changes, and so the CSS viewport
     257             :   //     might change as a result. If this happens after the content has been
     258             :   //     painted, we want to adjust the zoom to compensate. OR
     259             :   // (b) The display size changed from a nonzero value to another nonzero value.
     260             :   //     This covers the case where e.g. the device was rotated, and again we
     261             :   //     want to adjust the zoom to compensate.
     262             :   // Note in particular that aDisplayWidthChangeRatio will be None if all that
     263             :   // happened was a change in the full-zoom. In this case, we still want to
     264             :   // compute a new CSS viewport, but we don't want to update the resolution.
     265             :   //
     266             :   // Given the above, the algorithm below accounts for all types of changes I
     267             :   // can conceive of:
     268             :   // 1. screen size changes, CSS viewport does not (pages with no meta viewport
     269             :   //    or a fixed size viewport)
     270             :   // 2. screen size changes, CSS viewport also does (pages with a device-width
     271             :   //    viewport)
     272             :   // 3. screen size remains constant, but CSS viewport changes (meta viewport
     273             :   //    tag is added or removed)
     274             :   // 4. neither screen size nor CSS viewport changes
     275           0 :   if (aDisplayWidthChangeRatio) {
     276           0 :     res = ScaleResolutionWithDisplayWidth(res, aDisplayWidthChangeRatio.value(),
     277             :       aViewport, mMobileViewportSize);
     278           0 :     mPresShell->SetResolutionAndScaleTo(res.scale);
     279             :   }
     280             : 
     281           0 :   return ViewTargetAs<ScreenPixel>(cssToDev * res / ParentLayerToLayerScale(1),
     282           0 :     PixelCastJustification::ScreenIsParentLayerForRoot);
     283             : }
     284             : 
     285             : void
     286           0 : MobileViewportManager::UpdateSPCSPS(const ScreenIntSize& aDisplaySize,
     287             :                                     const CSSToScreenScale& aZoom)
     288             : {
     289           0 :   ScreenSize compositionSize(aDisplaySize);
     290             :   ScreenMargin scrollbars =
     291           0 :     LayoutDeviceMargin::FromAppUnits(
     292           0 :       nsLayoutUtils::ScrollbarAreaToExcludeFromCompositionBoundsFor(
     293           0 :         mPresShell->GetRootScrollFrame()),
     294           0 :       mPresShell->GetPresContext()->AppUnitsPerDevPixel())
     295             :     // Scrollbars are not subject to resolution scaling, so LD pixels =
     296             :     // Screen pixels for them.
     297           0 :     * LayoutDeviceToScreenScale(1.0f);
     298             : 
     299           0 :   compositionSize.width -= scrollbars.LeftRight();
     300           0 :   compositionSize.height -= scrollbars.TopBottom();
     301           0 :   CSSSize compSize = compositionSize / aZoom;
     302             :   MVM_LOG("%p: Setting SPCSPS %s\n", this, Stringify(compSize).c_str());
     303           0 :   nsLayoutUtils::SetScrollPositionClampingScrollPortSize(mPresShell, compSize);
     304           0 : }
     305             : 
     306             : void
     307           0 : MobileViewportManager::UpdateDisplayPortMargins()
     308             : {
     309           0 :   if (nsIFrame* root = mPresShell->GetRootScrollFrame()) {
     310           0 :     bool hasDisplayPort = nsLayoutUtils::HasDisplayPort(root->GetContent());
     311           0 :     bool hasResolution = mPresShell->ScaleToResolution() &&
     312           0 :         mPresShell->GetResolution() != 1.0f;
     313           0 :     if (!hasDisplayPort && !hasResolution) {
     314             :       // We only want to update the displayport if there is one already, or
     315             :       // add one if there's a resolution on the document (see bug 1225508
     316             :       // comment 1).
     317           0 :       return;
     318             :     }
     319             :     nsRect displayportBase =
     320           0 :       nsRect(nsPoint(0, 0), nsLayoutUtils::CalculateCompositionSizeForFrame(root));
     321             :     // We only create MobileViewportManager for root content documents. If that ever changes
     322             :     // we'd need to limit the size of this displayport base rect because non-toplevel documents
     323             :     // have no limit on their size.
     324           0 :     MOZ_ASSERT(mPresShell->GetPresContext()->IsRootContentDocument());
     325           0 :     nsLayoutUtils::SetDisplayPortBaseIfNotSet(root->GetContent(), displayportBase);
     326           0 :     nsIScrollableFrame* scrollable = do_QueryFrame(root);
     327             :     nsLayoutUtils::CalculateAndSetDisplayPortMargins(scrollable,
     328           0 :       nsLayoutUtils::RepaintMode::DoNotRepaint);
     329             :   }
     330             : }
     331             : 
     332             : void
     333           0 : MobileViewportManager::RefreshSPCSPS()
     334             : {
     335             :   // This function is a subset of RefreshViewportSize, and only updates the
     336             :   // SPCSPS.
     337             : 
     338           0 :   if (!gfxPrefs::APZAllowZooming()) {
     339           0 :     return;
     340             :   }
     341             : 
     342             :   ScreenIntSize displaySize = ViewAs<ScreenPixel>(
     343           0 :     mDisplaySize, PixelCastJustification::LayoutDeviceIsScreenForBounds);
     344             : 
     345             :   CSSToLayoutDeviceScale cssToDev =
     346           0 :       mPresShell->GetPresContext()->CSSToDevPixelScale();
     347           0 :   LayoutDeviceToLayerScale res(mPresShell->GetResolution());
     348           0 :   CSSToScreenScale zoom = ViewTargetAs<ScreenPixel>(cssToDev * res / ParentLayerToLayerScale(1),
     349           0 :     PixelCastJustification::ScreenIsParentLayerForRoot);
     350             : 
     351           0 :   UpdateSPCSPS(displaySize, zoom);
     352             : }
     353             : 
     354             : void
     355           0 : MobileViewportManager::RefreshViewportSize(bool aForceAdjustResolution)
     356             : {
     357             :   // This function gets called by the various triggers that may result in a
     358             :   // change of the CSS viewport. In some of these cases (e.g. the meta-viewport
     359             :   // tag changes) we want to update the resolution and in others (e.g. the full
     360             :   // zoom changing) we don't want to update the resolution. See the comment in
     361             :   // UpdateResolution for some more detail on this. An important assumption we
     362             :   // make here is that this RefreshViewportSize function will be called
     363             :   // separately for each trigger that changes. For instance it should never get
     364             :   // called such that both the full zoom and the meta-viewport tag have changed;
     365             :   // instead it would get called twice - once after each trigger changes. This
     366             :   // assumption is what allows the aForceAdjustResolution parameter to work as
     367             :   // intended; if this assumption is violated then we will need to add extra
     368             :   // complicated logic in UpdateResolution to ensure we only do the resolution
     369             :   // update in the right scenarios.
     370             : 
     371           0 :   Maybe<float> displayWidthChangeRatio;
     372           0 :   LayoutDeviceIntSize newDisplaySize;
     373           0 :   if (nsLayoutUtils::GetContentViewerSize(mPresShell->GetPresContext(), newDisplaySize)) {
     374             :     // See the comment in UpdateResolution for why we're doing this.
     375           0 :     if (mDisplaySize.width > 0) {
     376           0 :       if (aForceAdjustResolution || mDisplaySize.width != newDisplaySize.width) {
     377           0 :         displayWidthChangeRatio = Some((float)newDisplaySize.width / (float)mDisplaySize.width);
     378             :       }
     379           0 :     } else if (aForceAdjustResolution) {
     380           0 :       displayWidthChangeRatio = Some(1.0f);
     381             :     }
     382             : 
     383             :     MVM_LOG("%p: Display width change ratio is %f\n", this, displayWidthChangeRatio.valueOr(0.0f));
     384           0 :     mDisplaySize = newDisplaySize;
     385             :   }
     386             : 
     387             :   MVM_LOG("%p: Computing CSS viewport using %d,%d\n", this,
     388             :     mDisplaySize.width, mDisplaySize.height);
     389           0 :   if (mDisplaySize.width == 0 || mDisplaySize.height == 0) {
     390             :     // We can't do anything useful here, we should just bail out
     391           0 :     return;
     392             :   }
     393             : 
     394             :   ScreenIntSize displaySize = ViewAs<ScreenPixel>(
     395           0 :     mDisplaySize, PixelCastJustification::LayoutDeviceIsScreenForBounds);
     396           0 :   nsViewportInfo viewportInfo = mDocument->GetViewportInfo(displaySize);
     397             : 
     398           0 :   CSSSize viewport = viewportInfo.GetSize();
     399             :   MVM_LOG("%p: Computed CSS viewport %s\n", this, Stringify(viewport).c_str());
     400             : 
     401           0 :   if (!mIsFirstPaint && mMobileViewportSize == viewport) {
     402             :     // Nothing changed, so no need to do a reflow
     403           0 :     return;
     404             :   }
     405             : 
     406             :   // If it's the first-paint or the viewport changed, we need to update
     407             :   // various APZ properties (the zoom and some things that might depend on it)
     408             :   MVM_LOG("%p: Updating properties because %d || %d\n", this,
     409             :     mIsFirstPaint, mMobileViewportSize != viewport);
     410             : 
     411           0 :   if (gfxPrefs::APZAllowZooming()) {
     412             :     CSSToScreenScale zoom = UpdateResolution(viewportInfo, displaySize, viewport,
     413           0 :       displayWidthChangeRatio);
     414             :     MVM_LOG("%p: New zoom is %f\n", this, zoom.scale);
     415           0 :     UpdateSPCSPS(displaySize, zoom);
     416             :   }
     417           0 :   if (gfxPlatform::AsyncPanZoomEnabled()) {
     418           0 :     UpdateDisplayPortMargins();
     419             :   }
     420             : 
     421           0 :   CSSSize oldSize = mMobileViewportSize;
     422             : 
     423             :   // Update internal state.
     424           0 :   mIsFirstPaint = false;
     425           0 :   mMobileViewportSize = viewport;
     426             : 
     427             :   // Kick off a reflow.
     428           0 :   mPresShell->ResizeReflowIgnoreOverride(
     429             :     nsPresContext::CSSPixelsToAppUnits(viewport.width),
     430             :     nsPresContext::CSSPixelsToAppUnits(viewport.height),
     431             :     nsPresContext::CSSPixelsToAppUnits(oldSize.width),
     432           0 :     nsPresContext::CSSPixelsToAppUnits(oldSize.height));
     433             : }

Generated by: LCOV version 1.13