tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

TestMobileViewportManager.cpp (9024B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "gtest/gtest.h"
      8 #include "gmock/gmock.h"
      9 
     10 #include <functional>
     11 
     12 #include "MobileViewportManager.h"
     13 #include "mozilla/MVMContext.h"
     14 #include "mozilla/dom/Event.h"
     15 #include "mozilla/dom/InteractiveWidget.h"
     16 
     17 using namespace mozilla;
     18 
     19 class MockMVMContext : public MVMContext {
     20  using AutoSizeFlag = nsViewportInfo::AutoSizeFlag;
     21  using AutoScaleFlag = nsViewportInfo::AutoScaleFlag;
     22  using ZoomFlag = nsViewportInfo::ZoomFlag;
     23 
     24  // A "layout function" is a function that computes the content size
     25  // as a function of the ICB size.
     26  using LayoutFunction = std::function<CSSSize(CSSSize aICBSize)>;
     27 
     28 public:
     29  // MVMContext methods we don't care to implement.
     30  MOCK_METHOD3(AddEventListener,
     31               void(const nsAString& aType, nsIDOMEventListener* aListener,
     32                    bool aUseCapture));
     33  MOCK_METHOD3(RemoveEventListener,
     34               void(const nsAString& aType, nsIDOMEventListener* aListener,
     35                    bool aUseCapture));
     36  MOCK_METHOD3(AddObserver, void(nsIObserver* aObserver, const char* aTopic,
     37                                 bool aOwnsWeak));
     38  MOCK_METHOD2(RemoveObserver,
     39               void(nsIObserver* aObserver, const char* aTopic));
     40  MOCK_METHOD0(Destroy, void());
     41 
     42  MOCK_METHOD1(SetVisualViewportSize, void(const CSSSize& aSize));
     43  MOCK_METHOD0(PostVisualViewportResizeEventByDynamicToolbar, void());
     44  MOCK_METHOD0(UpdateDisplayPortMargins, void());
     45  MOCK_METHOD0(GetDynamicToolbarOffset, ScreenIntCoord());
     46  MOCK_CONST_METHOD0(GetInteractiveWidgetMode, dom::InteractiveWidget());
     47 
     48  void SetMVM(MobileViewportManager* aMVM) { mMVM = aMVM; }
     49 
     50  // MVMContext method implementations.
     51  nsViewportInfo GetViewportInfo(const ScreenIntSize& aDisplaySize) const {
     52    // This is a very basic approximation of what Document::GetViewportInfo()
     53    // does in the most common cases.
     54    // Ideally, we would invoke the algorithm in Document::GetViewportInfo()
     55    // itself, but that would require refactoring it a bit to remove
     56    // dependencies on the actual Document which we don't have available in
     57    // this test harness.
     58    CSSSize viewportSize = mDisplaySize / mDeviceScale;
     59    if (mAutoSizeFlag == AutoSizeFlag::FixedSize) {
     60      viewportSize = CSSSize(mFixedViewportWidth,
     61                             mFixedViewportWidth * (float(mDisplaySize.height) /
     62                                                    mDisplaySize.width));
     63    }
     64    return nsViewportInfo(mDefaultScale, mMinScale, mMaxScale, viewportSize,
     65                          mAutoSizeFlag, mAutoScaleFlag, mZoomFlag,
     66                          dom::ViewportFitType::Auto);
     67  }
     68  CSSToLayoutDeviceScale CSSToDevPixelScale() const { return mDeviceScale; }
     69  float GetResolution() const { return mResolution; }
     70  bool SubjectMatchesDocument(nsISupports* aSubject) const { return true; }
     71  Maybe<CSSRect> CalculateScrollableRectForRSF() const {
     72    return Some(CSSRect(CSSPoint(), mContentSize));
     73  }
     74  bool IsResolutionUpdatedByApz() const { return false; }
     75  LayoutDeviceMargin ScrollbarAreaToExcludeFromCompositionBounds() const {
     76    return LayoutDeviceMargin();
     77  }
     78  Maybe<LayoutDeviceIntSize> GetDocumentViewerSize() const {
     79    return Some(mDisplaySize);
     80  }
     81  bool AllowZoomingForDocument() const { return true; }
     82  bool IsInReaderMode() const { return false; }
     83  bool IsDocumentLoading() const { return false; }
     84  bool IsDocumentFullscreen() const { return false; }
     85 
     86  void SetResolutionAndScaleTo(float aResolution,
     87                               ResolutionChangeOrigin aOrigin) {
     88    mResolution = aResolution;
     89    mMVM->ResolutionUpdated(aOrigin);
     90  }
     91  void Reflow(const CSSSize& aNewSize) {
     92    mICBSize = aNewSize;
     93    mContentSize = mLayoutFunction(mICBSize);
     94  }
     95 
     96  // Allow test code to modify the input metrics.
     97  void SetMinScale(CSSToScreenScale aMinScale) { mMinScale = aMinScale; }
     98  void SetMaxScale(CSSToScreenScale aMaxScale) { mMaxScale = aMaxScale; }
     99  void SetInitialScale(CSSToScreenScale aInitialScale) {
    100    mDefaultScale = aInitialScale;
    101    mAutoScaleFlag = AutoScaleFlag::FixedScale;
    102  }
    103  void SetFixedViewportWidth(CSSCoord aWidth) {
    104    mFixedViewportWidth = aWidth;
    105    mAutoSizeFlag = AutoSizeFlag::FixedSize;
    106  }
    107  void SetDisplaySize(const LayoutDeviceIntSize& aNewDisplaySize) {
    108    mDisplaySize = aNewDisplaySize;
    109  }
    110  void SetLayoutFunction(const LayoutFunction& aLayoutFunction) {
    111    mLayoutFunction = aLayoutFunction;
    112  }
    113 
    114  // Allow test code to query the output metrics.
    115  CSSSize GetICBSize() const { return mICBSize; }
    116  CSSSize GetContentSize() const { return mContentSize; }
    117 
    118 private:
    119  // Input metrics, with some sensible defaults.
    120  LayoutDeviceIntSize mDisplaySize{300, 600};
    121  CSSToScreenScale mDefaultScale{1.0f};
    122  CSSToScreenScale mMinScale{0.25f};
    123  CSSToScreenScale mMaxScale{10.0f};
    124  CSSToLayoutDeviceScale mDeviceScale{1.0f};
    125  CSSCoord mFixedViewportWidth;
    126  AutoSizeFlag mAutoSizeFlag = AutoSizeFlag::AutoSize;
    127  AutoScaleFlag mAutoScaleFlag = AutoScaleFlag::AutoScale;
    128  ZoomFlag mZoomFlag = ZoomFlag::AllowZoom;
    129  // As a default layout function, just set the content size to the ICB size.
    130  LayoutFunction mLayoutFunction = [](CSSSize aICBSize) { return aICBSize; };
    131 
    132  // Output metrics.
    133  float mResolution = 1.0f;
    134  CSSSize mICBSize;
    135  CSSSize mContentSize;
    136 
    137  MobileViewportManager* mMVM = nullptr;
    138 };
    139 
    140 class MVMTester : public ::testing::Test {
    141 public:
    142  MVMTester()
    143      : mMVMContext(new MockMVMContext()),
    144        mMVM(new MobileViewportManager(
    145            mMVMContext,
    146            MobileViewportManager::ManagerType::VisualAndMetaViewport)) {
    147    mMVMContext->SetMVM(mMVM.get());
    148  }
    149 
    150  void Resize(const LayoutDeviceIntSize& aNewDisplaySize) {
    151    mMVMContext->SetDisplaySize(aNewDisplaySize);
    152    mMVM->RequestReflow(false);
    153  }
    154 
    155 protected:
    156  RefPtr<MockMVMContext> mMVMContext;
    157  RefPtr<MobileViewportManager> mMVM;
    158 };
    159 
    160 TEST_F(MVMTester, ZoomBoundsRespectedAfterRotation_Bug1536755) {
    161  // Set up initial conditions.
    162  mMVMContext->SetDisplaySize(LayoutDeviceIntSize(600, 300));
    163  mMVMContext->SetInitialScale(CSSToScreenScale(1.0f));
    164  mMVMContext->SetMinScale(CSSToScreenScale(1.0f));
    165  mMVMContext->SetMaxScale(CSSToScreenScale(1.0f));
    166  // Set a layout function that simulates a page which is twice
    167  // as tall as it is wide.
    168  mMVMContext->SetLayoutFunction([](CSSSize aICBSize) {
    169    return CSSSize(aICBSize.width, aICBSize.width * 2);
    170  });
    171 
    172  // Perform an initial viewport computation and reflow, and
    173  // sanity-check the results.
    174  mMVM->SetInitialViewport();
    175  EXPECT_EQ(CSSSize(600, 300), mMVMContext->GetICBSize());
    176  EXPECT_EQ(CSSSize(600, 1200), mMVMContext->GetContentSize());
    177  EXPECT_EQ(1.0f, mMVMContext->GetResolution());
    178 
    179  // Now rotate the screen, and check that the minimum and maximum
    180  // scales are still respected after the rotation.
    181  Resize(LayoutDeviceIntSize(300, 600));
    182  EXPECT_EQ(CSSSize(300, 600), mMVMContext->GetICBSize());
    183  EXPECT_EQ(CSSSize(300, 600), mMVMContext->GetContentSize());
    184  EXPECT_EQ(1.0f, mMVMContext->GetResolution());
    185 }
    186 
    187 TEST_F(MVMTester, LandscapeToPortraitRotation_Bug1523844) {
    188  // Set up initial conditions.
    189  mMVMContext->SetDisplaySize(LayoutDeviceIntSize(300, 600));
    190  // Set a layout function that simulates a page with a fixed
    191  // content size that's as wide as the screen in one orientation
    192  // (and wider in the other).
    193  mMVMContext->SetLayoutFunction(
    194      [](CSSSize aICBSize) { return CSSSize(600, 1200); });
    195 
    196  // Simulate a "DOMMetaAdded" event being fired before calling
    197  // SetInitialViewport(). This matches what typically happens
    198  // during real usage (the MVM receives the "DOMMetaAdded"
    199  // before the "load", and it's the "load" that calls
    200  // SetInitialViewport()), and is important to trigger this
    201  // bug, because it causes the MVM to be stuck with an
    202  // "mRestoreResolution" (prior to the fix).
    203  mMVM->HandleDOMMetaAdded();
    204 
    205  // Perform an initial viewport computation and reflow, and
    206  // sanity-check the results.
    207  mMVM->SetInitialViewport();
    208  EXPECT_EQ(CSSSize(300, 600), mMVMContext->GetICBSize());
    209  EXPECT_EQ(CSSSize(600, 1200), mMVMContext->GetContentSize());
    210  EXPECT_EQ(0.5f, mMVMContext->GetResolution());
    211 
    212  // Rotate to landscape.
    213  Resize(LayoutDeviceIntSize(600, 300));
    214  EXPECT_EQ(CSSSize(600, 300), mMVMContext->GetICBSize());
    215  EXPECT_EQ(CSSSize(600, 1200), mMVMContext->GetContentSize());
    216  EXPECT_EQ(1.0f, mMVMContext->GetResolution());
    217 
    218  // Rotate back to portrait and check that we have returned
    219  // to the portrait resolution.
    220  Resize(LayoutDeviceIntSize(300, 600));
    221  EXPECT_EQ(CSSSize(300, 600), mMVMContext->GetICBSize());
    222  EXPECT_EQ(CSSSize(600, 1200), mMVMContext->GetContentSize());
    223  EXPECT_EQ(0.5f, mMVMContext->GetResolution());
    224 }