CSSToScreenScale
MobileViewportManager::ClampZoom(const CSSToScreenScale& aZoom,
                                 const nsViewportInfo& aViewportInfo)
{
  CSSToScreenScale zoom = aZoom;
  if (zoom < aViewportInfo.GetMinZoom()) {
    zoom = aViewportInfo.GetMinZoom();
    MVM_LOG("%p: Clamped to %f\n", this, zoom.scale);
  }
  if (zoom > aViewportInfo.GetMaxZoom()) {
    zoom = aViewportInfo.GetMaxZoom();
    MVM_LOG("%p: Clamped to %f\n", this, zoom.scale);
  }
  return zoom;
}
mozilla::layers::ZoomConstraints
ComputeZoomConstraintsFromViewportInfo(const nsViewportInfo& aViewportInfo)
{
  mozilla::layers::ZoomConstraints constraints;
  constraints.mAllowZoom = aViewportInfo.IsZoomAllowed();
  constraints.mAllowDoubleTapZoom = aViewportInfo.IsDoubleTapZoomAllowed();
  constraints.mMinZoom.scale = aViewportInfo.GetMinZoom().scale;
  constraints.mMaxZoom.scale = aViewportInfo.GetMaxZoom().scale;
  return constraints;
}
mozilla::layers::ZoomConstraints
ComputeZoomConstraintsFromViewportInfo(const nsViewportInfo& aViewportInfo)
{
  mozilla::layers::ZoomConstraints constraints;
  constraints.mAllowZoom = aViewportInfo.IsZoomAllowed() && gfxPrefs::APZAllowZooming();
  constraints.mAllowDoubleTapZoom = constraints.mAllowZoom;
  if (constraints.mAllowZoom) {
    constraints.mMinZoom.scale = aViewportInfo.GetMinZoom().scale;
    constraints.mMaxZoom.scale = aViewportInfo.GetMaxZoom().scale;
  } else {
    constraints.mMinZoom.scale = aViewportInfo.GetDefaultZoom().scale;
    constraints.mMaxZoom.scale = aViewportInfo.GetDefaultZoom().scale;
  }
  return constraints;
}
CSSToScreenScale
MobileViewportManager::UpdateResolution(const nsViewportInfo& aViewportInfo,
                                        const ScreenIntSize& aDisplaySize,
                                        const CSSSize& aViewport,
                                        const Maybe<float>& aDisplayWidthChangeRatio)
{
  CSSToLayoutDeviceScale cssToDev =
      mPresShell->GetPresContext()->CSSToDevPixelScale();
  LayoutDeviceToLayerScale res(mPresShell->GetResolution());

  if (mIsFirstPaint) {
    CSSToScreenScale defaultZoom;
    if (mRestoreResolution) {
      defaultZoom = CSSToScreenScale(mRestoreResolution.value() * cssToDev.scale);
      MVM_LOG("%p: restored zoom is %f\n", this, defaultZoom.scale);
      defaultZoom = ClampZoom(defaultZoom, aViewportInfo);
    } else {
      defaultZoom = aViewportInfo.GetDefaultZoom();
      MVM_LOG("%p: default zoom from viewport is %f\n", this, defaultZoom.scale);
      if (!aViewportInfo.IsDefaultZoomValid()) {
        defaultZoom = MaxScaleRatio(ScreenSize(aDisplaySize), aViewport);
        MVM_LOG("%p: Intrinsic computed zoom is %f\n", this, defaultZoom.scale);
        defaultZoom = ClampZoom(defaultZoom, aViewportInfo);
      }
    }
    MOZ_ASSERT(aViewportInfo.GetMinZoom() <= defaultZoom &&
      defaultZoom <= aViewportInfo.GetMaxZoom());

    CSSToParentLayerScale zoom = ViewTargetAs<ParentLayerPixel>(defaultZoom,
      PixelCastJustification::ScreenIsParentLayerForRoot);

    LayoutDeviceToLayerScale resolution = zoom / cssToDev * ParentLayerToLayerScale(1);
    MVM_LOG("%p: setting resolution %f\n", this, resolution.scale);
    mPresShell->SetResolutionAndScaleTo(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. If this happens after the content has been
  //     painted, 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
  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);
    mPresShell->SetResolutionAndScaleTo(newRes.scale);
    res = newRes;
  }

  return ViewTargetAs<ScreenPixel>(cssToDev * res / ParentLayerToLayerScale(1),
    PixelCastJustification::ScreenIsParentLayerForRoot);
}