Skip to content
Permalink
Browse files
Display can go to sleep when camera is on
https://bugs.webkit.org/show_bug.cgi?id=245726
rdar://100423979

Reviewed by Eric Carlson.

In case a document is capturing or screen, it is good to keep the display on so that the preview stays visible.
To do so, the document creates a SleepDisabler when capturing.

Covered by updated API tests.

* Source/WebCore/dom/Document.cpp:
(WebCore::Document::mediaStreamCaptureStateChanged):
(WebCore::Document::canvasChanged):
(WebCore::Document::updateSleepDisablerIfNeeded):
* Source/WebCore/dom/Document.h:
(WebCore::Document::hasSleepDisabler const):
* Source/WebCore/testing/Internals.cpp:
(WebCore::Internals::hasSleepDisabler const):
* Source/WebCore/testing/Internals.h:
* Source/WebCore/testing/Internals.idl:
* Tools/TestWebKitAPI/Tests/WebKit/GetUserMedia.mm:
* Tools/TestWebKitAPI/Tests/WebKit/SleepDisabler.mm:
(TEST_F):
* Tools/TestWebKitAPI/Tests/WebKit/getDisplayMedia.html:
* Tools/TestWebKitAPI/Tests/WebKitCocoa/GetDisplayMediaWindowAndScreen.mm:
(TestWebKitAPI::TEST):
* Tools/TestWebKitAPI/Tests/WebKitLegacy/ios/video-with-audio.html:

Canonical link: https://commits.webkit.org/255636@main
  • Loading branch information
youennf committed Oct 17, 2022
1 parent c97fbc8 commit 79a3c4d8d291353d877baa33fd41bb4c104e2abc
Show file tree
Hide file tree
Showing 9 changed files with 89 additions and 0 deletions.
@@ -229,6 +229,7 @@
#include "ServiceWorkerProvider.h"
#include "Settings.h"
#include "ShadowRoot.h"
#include "SleepDisabler.h"
#include "SocketProvider.h"
#include "SpeechRecognition.h"
#include "StorageEvent.h"
@@ -8427,6 +8428,8 @@ void Document::stopMediaCapture(MediaProducerMediaCaptureKind kind)

void Document::mediaStreamCaptureStateChanged()
{
updateSleepDisablerIfNeeded();

if (!MediaProducer::isCapturing(m_mediaState))
return;

@@ -9171,6 +9174,17 @@ void Document::canvasChanged(CanvasBase& canvasBase, const std::optional<FloatRe
}
}

void Document::updateSleepDisablerIfNeeded()
{
MediaProducerMediaStateFlags activeVideoCaptureMask { MediaProducerMediaState::HasActiveVideoCaptureDevice, MediaProducerMediaState::HasActiveScreenCaptureDevice, MediaProducerMediaState::HasActiveWindowCaptureDevice };
if (m_mediaState & activeVideoCaptureMask) {
if (!m_sleepDisabler)
m_sleepDisabler = makeUnique<SleepDisabler>("com.apple.WebCore: Document doing camera, screen or window capture"_s, PAL::SleepDisabler::Type::Display);
return;
}
m_sleepDisabler = nullptr;
}

void Document::canvasDestroyed(CanvasBase& canvasBase)
{
if (!is<HTMLCanvasElement>(canvasBase))
@@ -222,6 +222,7 @@ class SelectorQuery;
class SelectorQueryCache;
class SerializedScriptValue;
class Settings;
class SleepDisabler;
class SpeechRecognition;
class StorageConnection;
class StringCallback;
@@ -1713,6 +1714,8 @@ class Document
ReportingScope& reportingScope() const { return m_reportingScope.get(); }
WEBCORE_EXPORT String endpointURIForToken(const String&) const final;

bool hasSleepDisabler() const { return !!m_sleepDisabler; }

protected:
enum ConstructionFlags { Synthesized = 1, NonRenderedPlaceholder = 1 << 1 };
WEBCORE_EXPORT Document(Frame*, const Settings&, const URL&, DocumentClasses = { }, unsigned constructionFlags = 0, ScriptExecutionContextIdentifier = { });
@@ -1844,6 +1847,8 @@ class Document
void sendReportToEndpoints(const URL& baseURL, const Vector<String>& endpointURIs, const Vector<String>& endpointTokens, Ref<FormData>&& report, ViolationReportType) final;
String httpUserAgent() const final;

void updateSleepDisablerIfNeeded();

const Ref<const Settings> m_settings;

UniqueRef<Quirks> m_quirks;
@@ -2317,6 +2322,8 @@ class Document

Ref<ReportingScope> m_reportingScope;
std::unique_ptr<WakeLockManager> m_wakeLockManager;

std::unique_ptr<SleepDisabler> m_sleepDisabler;
};

Element* eventTargetElementForDocument(Document*);
@@ -6938,4 +6938,11 @@ void Internals::avoidIOSurfaceSizeCheckInWebProcess(HTMLCanvasElement& element)
HTMLCanvasElement::setMaxPixelMemoryForTesting(UINT_MAX);
element.setAvoidIOSurfaceSizeCheckInWebProcessForTesting();
}

bool Internals::hasSleepDisabler() const
{
auto* document = contextDocument();
return document ? document->hasSleepDisabler() : false;
}

} // namespace WebCore
@@ -1350,6 +1350,7 @@ class Internals final : public RefCounted<Internals>, private ContextDestruction
#endif

void avoidIOSurfaceSizeCheckInWebProcess(HTMLCanvasElement&);
bool hasSleepDisabler() const;

private:
explicit Internals(Document&);
@@ -1173,4 +1173,6 @@ typedef (FetchRequest or FetchResponse) FetchObject;
[Conditional=ARKIT_INLINE_PREVIEW_MAC] Promise<sequence<DOMString>> modelInlinePreviewUUIDs();
[Conditional=ARKIT_INLINE_PREVIEW_MAC] DOMString modelInlinePreviewUUIDForModelElement(HTMLModelElement modelElement);
undefined avoidIOSurfaceSizeCheckInWebProcess(HTMLCanvasElement element);

boolean hasSleepDisabler();
};
@@ -848,6 +848,17 @@ function doTest()
});
}

function hasSleepDisabler()
{
return window.internals ? internals.hasSleepDisabler() : false;
}

function stop()
{
if (localVideo.srcObject)
localVideo.srcObject.getVideoTracks()[0].stop();
window.webkit.messageHandlers.gum.postMessage("PASS");
}
</script>
</body></html>
)DOCDOCDOC"_s;
@@ -886,13 +897,23 @@ function doTest()
[webView stringByEvaluatingJavaScript:@"capture()"];
TestWebKitAPI::Util::run(&done);

bool hasSleepDisabler = [webView stringByEvaluatingJavaScript:@"hasSleepDisabler()"].boolValue;
EXPECT_TRUE(hasSleepDisabler);

done = false;
[hostWindow deminiaturize:hostWindow];
TestWebKitAPI::Util::run(&done);

done = false;
[webView stringByEvaluatingJavaScript:@"doTest()"];
TestWebKitAPI::Util::run(&done);

done = false;
[webView stringByEvaluatingJavaScript:@"stop()"];
TestWebKitAPI::Util::run(&done);

hasSleepDisabler = [webView stringByEvaluatingJavaScript:@"hasSleepDisabler()"].boolValue;
EXPECT_FALSE(hasSleepDisabler);
}

static constexpr auto getUserMediaFocusText = R"DOCDOCDOC(
@@ -49,6 +49,11 @@
{
return stream !== null;
}

function hasSleepDisabler()
{
return window.internals ? internals.hasSleepDisabler() : false;
}
</script>
<head>

@@ -150,9 +150,16 @@ - (BOOL)waitForDisplayCaptureSurfaces:(WKDisplayCaptureSurfaces)expectedState
EXPECT_TRUE([webView _displayCaptureState] == WKDisplayCaptureStateNone);
EXPECT_TRUE([webView _displayCaptureSurfaces] == WKDisplayCaptureSurfaceNone);

auto hasSleepDisabler = [m_webView stringByEvaluatingJavaScript:@"hasSleepDisabler()"].booleanValue;
EXPECT_TRUE(hasSleepDisabler);

// Check "Allow Screen"
[webView stringByEvaluatingJavaScript:@"stop()"];
[delegate resetWasPrompted];

hasSleepDisabler = [m_webView stringByEvaluatingJavaScript:@"hasSleepDisabler()"].booleanValue;
EXPECT_FALSE(hasSleepDisabler);

[webView _setIndexOfGetDisplayMediaDeviceSelectedForTesting:@0];
[delegate setGetDisplayMediaDecision:WKDisplayCapturePermissionDecisionScreenPrompt];
[webView stringByEvaluatingJavaScript:@"promptForCapture({ video : true })"];
@@ -37,10 +37,35 @@
go();
}

function playAudio() {
window.webkit.messageHandlers.testHandler.postMessage('playAudio');
const audio = document.getElementsByTagName('audio')[0];
audio.play().then(playing).catch(notPlaying);
}

let canvasTimer;
function startCanvasUpdate()
{
stopCanvasUpdate();
canvasTimer = setInterval(() => {
window.webkit.messageHandlers.testHandler.postMessage('canvasClearFill');
myCanvas.getContext('2d').clearRect(0, 0, 50, 50);
myCanvas.getContext('2d').fillRect(0, 0, 100, 100);
}, 50);
}

function stopCanvasUpdate()
{
if (canvasTimer)
clearInterval(canvasTimer);
}

document.addEventListener('pageshow', go);
</script>
</head>
<body onload="go()">
<video src="video-with-audio.mp4" webkit-playsinline></video>
<audio src="video-with-audio.mp4" webkit-playsinline></video>
<canvas id="myCanvas" width="200" height="200"></canvas>
</body>
</html>

0 comments on commit 79a3c4d

Please sign in to comment.