Commit 58bec564 authored by Edgar Chen's avatar Edgar Chen
Browse files

Bug 1823284 - Use `SizeModeChanged` notification to handle fullscreen change;...

Bug 1823284 - Use `SizeModeChanged` notification to handle fullscreen change; r=geckoview-reviewers,rkraesig,stransky,bradwerth,smaug,m_kato

Depends on D175213

Differential Revision: https://phabricator.services.mozilla.com/D175215
parent 65bc134f
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -36,3 +36,4 @@ support-files =
  file_fullscreen-bug-1798219.html
  file_fullscreen-bug-1798219-2.html
[browser_fullscreen-window-open-race.js]
[browser_fullscreen-sizemode.js]
+225 −0
Original line number Diff line number Diff line
/* Any copyright is dedicated to the Public Domain.
   http://creativecommons.org/publicdomain/zero/1.0/ */

"use strict";

const isMac = AppConstants.platform == "macosx";
const isWin = AppConstants.platform == "win";

async function waitForSizeMode(aWindow, aSizeMode) {
  await BrowserTestUtils.waitForEvent(aWindow, "sizemodechange", false, () => {
    return aWindow.windowState === aSizeMode;
  });
  const expectedHidden =
    aSizeMode == aWindow.STATE_MINIMIZED || aWindow.isFullyOccluded;
  if (aWindow.document.hidden != expectedHidden) {
    await BrowserTestUtils.waitForEvent(aWindow, "visibilitychange");
  }
  is(
    aWindow.document.hidden,
    expectedHidden,
    "Should be inactive if minimized or occluded"
  );
}

async function checkSizeModeAndFullscreenState(
  aWindow,
  aSizeMode,
  aFullscreen,
  aFullscreenEventShouldHaveFired,
  aStepFun
) {
  let promises = [];
  if (aWindow.windowState != aSizeMode) {
    promises.push(waitForSizeMode(aWindow, aSizeMode));
  }
  if (aFullscreenEventShouldHaveFired) {
    promises.push(
      BrowserTestUtils.waitForEvent(
        aWindow,
        aFullscreen ? "willenterfullscreen" : "willexitfullscreen"
      )
    );
    promises.push(BrowserTestUtils.waitForEvent(aWindow, "fullscreen"));
  }

  // Add listener for unexpected event.
  let unexpectedEventListener = aEvent => {
    ok(false, `should not receive ${aEvent.type} event`);
  };
  if (aFullscreenEventShouldHaveFired) {
    aWindow.addEventListener(
      aFullscreen ? "willexitfullscreen" : "willenterfullscreen",
      unexpectedEventListener
    );
  } else {
    aWindow.addEventListener("willenterfullscreen", unexpectedEventListener);
    aWindow.addEventListener("willexitfullscreen", unexpectedEventListener);
    aWindow.addEventListener("fullscreen", unexpectedEventListener);
  }

  let eventPromise = Promise.all(promises);
  aStepFun();
  await eventPromise;

  // Check SizeMode.
  is(
    aWindow.windowState,
    aSizeMode,
    "The new sizemode should have the expected value"
  );
  // Check Fullscreen state.
  is(
    aWindow.fullScreen,
    aFullscreen,
    `chrome window should ${aFullscreen ? "be" : "not be"} in fullscreen`
  );
  is(
    aWindow.document.documentElement.hasAttribute("inFullscreen"),
    aFullscreen,
    `chrome documentElement should ${
      aFullscreen ? "have" : "not have"
    } inFullscreen attribute`
  );

  // Remove listener for unexpected event.
  if (aFullscreenEventShouldHaveFired) {
    aWindow.removeEventListener(
      aFullscreen ? "willexitfullscreen" : "willenterfullscreen",
      unexpectedEventListener
    );
  } else {
    aWindow.removeEventListener("willenterfullscreen", unexpectedEventListener);
    aWindow.removeEventListener("willexitfullscreen", unexpectedEventListener);
    aWindow.removeEventListener("fullscreen", unexpectedEventListener);
  }
}

async function restoreWindowToNormal(aWindow) {
  while (aWindow.windowState != aWindow.STATE_NORMAL) {
    info(`Try to restore window with state ${aWindow.windowState} to normal`);
    let eventPromise = BrowserTestUtils.waitForEvent(aWindow, "sizemodechange");
    aWindow.restore();
    await eventPromise;
    info(`Window is now in state ${aWindow.windowState}`);
  }
}

add_task(async function test_fullscreen_restore() {
  let win = await BrowserTestUtils.openNewBrowserWindow();
  await restoreWindowToNormal(win);

  info("Enter fullscreen");
  await checkSizeModeAndFullscreenState(
    win,
    win.STATE_FULLSCREEN,
    true,
    true,
    () => {
      win.fullScreen = true;
    }
  );

  info("Restore window");
  await checkSizeModeAndFullscreenState(
    win,
    win.STATE_NORMAL,
    false,
    true,
    () => {
      win.restore();
    }
  );

  await BrowserTestUtils.closeWindow(win);
});

// This test only enable on Windows because:
// - Test gets intermittent timeout on macOS, see bug 1828848.
// - Restoring a fullscreen window on GTK doesn't return it to the previous
//   sizemode, see bug 1828837.
if (isWin) {
  add_task(async function test_maximize_fullscreen_restore() {
    let win = await BrowserTestUtils.openNewBrowserWindow();
    await restoreWindowToNormal(win);

    info("Maximize window");
    await checkSizeModeAndFullscreenState(
      win,
      win.STATE_MAXIMIZED,
      false,
      false,
      () => {
        win.maximize();
      }
    );

    info("Enter fullscreen");
    await checkSizeModeAndFullscreenState(
      win,
      win.STATE_FULLSCREEN,
      true,
      true,
      () => {
        win.fullScreen = true;
      }
    );

    info("Restore window");
    await checkSizeModeAndFullscreenState(
      win,
      win.STATE_MAXIMIZED,
      false,
      true,
      () => {
        win.restore();
      }
    );

    await BrowserTestUtils.closeWindow(win);
  });
}

// Restoring a minimized window on macOS doesn't return it to the previous
// sizemode, see bug 1828706.
if (!isMac) {
  add_task(async function test_fullscreen_minimize_restore() {
    let win = await BrowserTestUtils.openNewBrowserWindow();
    await restoreWindowToNormal(win);

    info("Enter fullscreen");
    await checkSizeModeAndFullscreenState(
      win,
      win.STATE_FULLSCREEN,
      true,
      true,
      () => {
        win.fullScreen = true;
      }
    );

    info("Minimize window");
    await checkSizeModeAndFullscreenState(
      win,
      win.STATE_MINIMIZED,
      true,
      false,
      () => {
        win.minimize();
      }
    );

    info("Restore window");
    await checkSizeModeAndFullscreenState(
      win,
      win.STATE_FULLSCREEN,
      true,
      false,
      () => {
        win.restore();
      }
    );

    await BrowserTestUtils.closeWindow(win);
  });
}
+1 −6
Original line number Diff line number Diff line
@@ -2426,19 +2426,14 @@ nsresult nsWindow::MakeFullScreen(bool aFullScreen) {
    return NS_ERROR_NOT_AVAILABLE;
  }

  nsIWidgetListener* listener = GetWidgetListener();
  if (listener) {
    listener->FullscreenWillChange(aFullScreen);
  }

  mIsFullScreen = aFullScreen;
  mAndroidView->mEventDispatcher->Dispatch(
      aFullScreen ? u"GeckoView:FullScreenEnter" : u"GeckoView:FullScreenExit");

  nsIWidgetListener* listener = GetWidgetListener();
  if (listener) {
    mSizeMode = mIsFullScreen ? nsSizeMode_Fullscreen : nsSizeMode_Normal;
    listener->SizeModeChanged(mSizeMode);
    listener->FullscreenChanged(mIsFullScreen);
  }
  return NS_OK;
}
+0 −20
Original line number Diff line number Diff line
@@ -1634,17 +1634,9 @@ void nsCocoaWindow::CocoaWindowWillEnterFullscreen(bool aFullscreen) {
  // happens.
  mUpdateFullscreenOnResize =
      Some(aFullscreen ? TransitionType::Fullscreen : TransitionType::Windowed);

  if (mWidgetListener) {
    mWidgetListener->FullscreenWillChange(aFullscreen);
  }
}

void nsCocoaWindow::CocoaWindowDidFailFullscreen(bool aAttemptedFullscreen) {
  if (mWidgetListener) {
    mWidgetListener->FullscreenWillChange(!aAttemptedFullscreen);
  }

  // If we already updated our fullscreen state due to a resize, we need to update it again.
  if (mUpdateFullscreenOnResize.isNothing()) {
    UpdateFullscreenState(!aAttemptedFullscreen, true);
@@ -1669,10 +1661,6 @@ void nsCocoaWindow::UpdateFullscreenState(bool aFullScreen, bool aNativeMode) {

  DispatchSizeModeEvent();

  if (mWidgetListener) {
    mWidgetListener->FullscreenChanged(aFullScreen);
  }

  // Notify the mainChildView with our new fullscreen state.
  nsChildView* mainChildView = static_cast<nsChildView*>([[mWindow mainChildView] widget]);
  if (mainChildView) {
@@ -1773,10 +1761,6 @@ void nsCocoaWindow::ProcessTransitions() {

      case TransitionType::EmulatedFullscreen: {
        if (!mInFullScreenMode) {
          // This can be done synchronously.
          if (mWidgetListener) {
            mWidgetListener->FullscreenWillChange(true);
          }
          NSDisableScreenUpdates();
          mSuppressSizeModeEvents = true;
          // The order here matters. When we exit full screen mode, we need to show the
@@ -1798,10 +1782,6 @@ void nsCocoaWindow::ProcessTransitions() {
            [mWindow toggleFullScreen:nil];
            continue;
          } else {
            // This can be done synchronously.
            if (mWidgetListener) {
              mWidgetListener->FullscreenWillChange(false);
            }
            NSDisableScreenUpdates();
            mSuppressSizeModeEvents = true;
            // The order here matters. When we exit full screen mode, we need to show the
+1 −9
Original line number Diff line number Diff line
@@ -5240,15 +5240,7 @@ void nsWindow::OnWindowStateEvent(GtkWidget* aWidget,
  LOG("\tTiled: %d\n", int(mIsTiled));

  if (mWidgetListener && mSizeMode != oldSizeMode) {
    if (mSizeMode == nsSizeMode_Fullscreen ||
        oldSizeMode == nsSizeMode_Fullscreen) {
      bool isFullscreen = mSizeMode == nsSizeMode_Fullscreen;
      mWidgetListener->FullscreenWillChange(isFullscreen);
    mWidgetListener->SizeModeChanged(mSizeMode);
      mWidgetListener->FullscreenChanged(isFullscreen);
    } else {
      mWidgetListener->SizeModeChanged(mSizeMode);
    }
  }

  if (mDrawInTitlebar && mTransparencyBitmapForTitlebar) {
Loading