author | Haik Aftandilian <haftandilian@mozilla.com> |
Tue, 18 May 2021 04:54:25 +0000 | |
changeset 579867 | 8c381a3500a284dae1ca91120ecb74f1dd7c1254 |
parent 579866 | d70a37d3e1b975fe0275267e668c77e8972bf579 |
child 579868 | 99e716181ac9a7b325741188d1aad6f57d71ef88 |
push id | 143252 |
push user | haftandilian@mozilla.com |
push date | Tue, 18 May 2021 04:56:53 +0000 |
treeherder | autoland@8c381a3500a2 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | spohl |
bugs | 1708324 |
milestone | 90.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
new file mode 100644 --- /dev/null +++ b/dom/html/test/file_fullscreen-focus-inner.html @@ -0,0 +1,24 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Focus test - child window</title> + <script type="application/javascript" src="file_fullscreen-utils.js"></script> +</head> +<body> + +<script type="application/javascript"> + +function enterFullscreen() { + addFullscreenErrorContinuation(() => { opener.enteredFullscreen(false); }); + + addFullscreenChangeContinuation("enter", () => { + opener.enteredFullscreen(true); + }); + + document.body.requestFullscreen(); +} + +</script> +</pre> +</body> +</html>
new file mode 100644 --- /dev/null +++ b/dom/html/test/file_fullscreen-focus.html @@ -0,0 +1,67 @@ +<!DOCTYPE HTML> +<html> +<!-- + +Test that a fullscreen request fails if the window is not focused. + +Open window1, open window2, focus window2, and then attempt to fullscreen +window1 while it is not focused. The fullscreen attempt should be rejected +because the window is not focused. + +--> +<head> + <title>Test fullscreen request is blocked when window is not focused</title> + <script src="/tests/SimpleTest/EventUtils.js"></script> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="file_fullscreen-utils.js"></script> +</head> +<body> + +<script type="application/javascript"> + +function ok(condition, msg) { + opener.ok(condition, "[focus] " + msg); +} + +var window1, window2; + +function openWindow() { + var w = window.open("file_fullscreen-focus-inner.html", "", + "width=500,height=500"); + return w; +} + +function begin() { + window1 = openWindow(); + window1.focus(); + + SimpleTest.waitForFocus(function(){ + window2 = openWindow(); + window2.focus(); + + SimpleTest.waitForFocus(function(){ + // Now that window2 is focused, attempt to fullscreen window1. + // This should fail. + window1.enterFullscreen("one"); + }, window2); + + }, window1); +} + +async function enteredFullscreen(enteredSuccessfully) { + ok(!enteredSuccessfully, "window1 did not enter fullscreen"); + + if (enteredSuccessfully) { + await window1.document.exitFullscreen() + } + + window1.close(); + window2.close(); + opener.nextTest(); +} + +</script> +</pre> +<div id="full-screen-element"></div> +</body> +</html>
--- a/dom/html/test/mochitest.ini +++ b/dom/html/test/mochitest.ini @@ -446,16 +446,18 @@ support-files = file_fullscreen-backdrop.html file_fullscreen-denied-inner.html file_fullscreen-denied.html file_fullscreen-esc-exit-inner.html file_fullscreen-esc-exit.html file_fullscreen-event-order.html file_fullscreen-featurePolicy.html file_fullscreen-featurePolicy-inner.html + file_fullscreen-focus.html + file_fullscreen-focus-inner.html file_fullscreen-hidden.html file_fullscreen-lenient-setters.html file_fullscreen-multiple-inner.html file_fullscreen-multiple.html file_fullscreen-navigation.html file_fullscreen-nested.html file_fullscreen-prefixed.html file_fullscreen-rollback.html
--- a/dom/html/test/test_fullscreen-api.html +++ b/dom/html/test/test_fullscreen-api.html @@ -31,16 +31,17 @@ var gTestWindows = [ { test: "file_fullscreen-multiple.html", prefs: [["full-screen-api.exit-on.windowRaise", false], ["full-screen-api.exit-on.windowOpen", false]] }, { test: "file_fullscreen-rollback.html" }, { test: "file_fullscreen-esc-exit.html" }, { test: "file_fullscreen-denied.html" }, { test: "file_fullscreen-api.html" }, { test: "file_fullscreen-hidden.html" }, + { test: "file_fullscreen-focus.html" }, { test: "file_fullscreen-svg-element.html" }, { test: "file_fullscreen-navigation.html" }, { test: "file_fullscreen-scrollbar.html" }, { test: "file_fullscreen-selector.html" }, { test: "file_fullscreen-shadowdom.html" }, { test: "file_fullscreen-top-layer.html" }, { test: "file_fullscreen-backdrop.html" }, { test: "file_fullscreen-nested.html" },
--- a/widget/cocoa/nsCocoaWindow.h +++ b/widget/cocoa/nsCocoaWindow.h @@ -260,16 +260,17 @@ class nsCocoaWindow final : public nsBas virtual void SuppressAnimation(bool aSuppress) override; virtual void HideWindowChrome(bool aShouldHide) override; void WillEnterFullScreen(bool aFullScreen); void EnteredFullScreen(bool aFullScreen, bool aNativeMode = true); virtual bool PrepareForFullscreenTransition(nsISupports** aData) override; virtual void PerformFullscreenTransition(FullscreenTransitionStage aStage, uint16_t aDuration, nsISupports* aData, nsIRunnable* aCallback) override; + virtual void CleanupFullscreenTransition() override; nsresult MakeFullScreen(bool aFullScreen, nsIScreen* aTargetScreen = nullptr) final; nsresult MakeFullScreenWithNativeTransition(bool aFullScreen, nsIScreen* aTargetScreen = nullptr) final; NSAnimation* FullscreenTransitionAnimation() const { return mFullscreenTransitionAnimation; } void ReleaseFullscreenTransitionAnimation() { MOZ_ASSERT(mFullscreenTransitionAnimation, "Should only be called when there is animation"); [mFullscreenTransitionAnimation release]; mFullscreenTransitionAnimation = nil; @@ -404,16 +405,22 @@ class nsCocoaWindow final : public nsBas WindowAnimationType mAnimationType; bool mWindowMadeHere; // true if we created the window, false for embedding bool mSheetNeedsShow; // if this is a sheet, are we waiting to be shown? // this is used for sibling sheet contention only bool mInFullScreenMode; bool mInFullScreenTransition; // true from the request to enter/exit fullscreen // (MakeFullScreen() call) to EnteredFullScreen() + + // Ignore occlusion events caused by displaying the temporary fullscreen + // window during the fullscreen transition animation because only focused + // contexts are permitted to enter DOM fullscreen. + int mIgnoreOcclusionCount; + bool mModal; bool mFakeModal; // Whether we are currently using native fullscreen. It could be false because // we are in the DOM fullscreen where we do not use the native fullscreen. bool mInNativeFullScreenMode; bool mIsAnimationSuppressed;
--- a/widget/cocoa/nsCocoaWindow.mm +++ b/widget/cocoa/nsCocoaWindow.mm @@ -131,16 +131,17 @@ nsCocoaWindow::nsCocoaWindow() mFullscreenTransitionAnimation(nil), mShadowStyle(StyleWindowShadow::Default), mBackingScaleFactor(0.0), mAnimationType(nsIWidget::eGenericWindowAnimation), mWindowMadeHere(false), mSheetNeedsShow(false), mInFullScreenMode(false), mInFullScreenTransition(false), + mIgnoreOcclusionCount(0), mModal(false), mFakeModal(false), mInNativeFullScreenMode(false), mIsAnimationSuppressed(false), mInReportMoveEvent(false), mInResize(false), mWindowTransformIsIdentity(true), mAlwaysOnTop(false), @@ -1511,16 +1512,27 @@ NS_IMPL_ISUPPORTS0(FullscreenTransitionD static bool AlwaysUsesNativeFullScreen() { return Preferences::GetBool("full-screen-api.macos-native-full-screen", false); } /* virtual */ bool nsCocoaWindow::PrepareForFullscreenTransition(nsISupports** aData) { if (AlwaysUsesNativeFullScreen()) { return false; } + + // Our fullscreen transition creates a new window occluding this window. + // That triggers an occlusion event which can cause DOM fullscreen requests + // to fail due to the context not being focused at the time the focus check + // is performed in the child process. Until the transition is cleaned up in + // CleanupFullscreenTransition(), ignore occlusion events for this window. + // If this method is changed to return false, the transition will not be + // performed and mIgnoreOcclusionCount should not be incremented. + MOZ_ASSERT(mIgnoreOcclusionCount >= 0); + mIgnoreOcclusionCount++; + nsCOMPtr<nsIScreen> widgetScreen = GetWidgetScreen(); NSScreen* cocoaScreen = ScreenHelperCocoa::CocoaScreenForScreen(widgetScreen); NSWindow* win = [[NSWindow alloc] initWithContentRect:[cocoaScreen frame] styleMask:NSWindowStyleMaskBorderless backing:NSBackingStoreBuffered defer:YES]; [win setBackgroundColor:[NSColor blackColor]]; @@ -1530,16 +1542,21 @@ static bool AlwaysUsesNativeFullScreen() [win makeKeyAndOrderFront:nil]; auto data = new FullscreenTransitionData(win); *aData = data; NS_ADDREF(data); return true; } +/* virtual */ void nsCocoaWindow::CleanupFullscreenTransition() { + MOZ_ASSERT(mIgnoreOcclusionCount > 0); + mIgnoreOcclusionCount--; +} + /* virtual */ void nsCocoaWindow::PerformFullscreenTransition(FullscreenTransitionStage aStage, uint16_t aDuration, nsISupports* aData, nsIRunnable* aCallback) { auto data = static_cast<FullscreenTransitionData*>(aData); FullscreenTransitionDelegate* delegate = [[FullscreenTransitionDelegate alloc] init]; delegate->mWindow = this; // Storing already_AddRefed directly could cause static checking fail. @@ -2043,16 +2060,21 @@ void nsCocoaWindow::DispatchOcclusionEve bool newOcclusionState = !([mWindow occlusionState] & NSWindowOcclusionStateVisible); // Don't dispatch if the new occlustion state is the same as the current state. if (mIsFullyOccluded == newOcclusionState) { return; } + MOZ_ASSERT(mIgnoreOcclusionCount >= 0); + if (newOcclusionState && mIgnoreOcclusionCount > 0) { + return; + } + mIsFullyOccluded = newOcclusionState; if (mWidgetListener) { mWidgetListener->OcclusionStateChanged(mIsFullyOccluded); } } void nsCocoaWindow::ReportSizeEvent() { NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;