<!DOCTYPE html>
<html>
<head>
    <title>mediastream page muted</title>
    <script src="../../resources/js-test-pre.js"></script>
    <script>
        async function checkPageState(activeState, inactiveState) {
            await new Promise((resolve, reject) => {
                let retryCount = 0;
                let retryInterval = 100;
                let maximumRetryCount = 20 * 1000 / retryInterval;

                let test = () => {

                    if ( window.internals
                        && window.internals.pageMediaState().includes(activeState)
                        && !window.internals.pageMediaState().includes(inactiveState)) {

                        testPassed(`window.internals.pageMediaState().includes(${activeState}) became true`);
                        testPassed(`window.internals.pageMediaState().includes(${inactiveState}) became false`);

                        resolve()
                        return;
                    }

                    if (++retryCount > maximumRetryCount) {
                        testFailed(`Page muted state failed to change after ${maximumRetryCount / 1000 * retryInterval} seconds`);
                        resolve();
                        return;
                    }

                    setTimeout(test, retryInterval);
                }

                setTimeout(test, 0);
            });
        }

        async function waitForMutedChange(stream, event, mute, unmute) {
            await new Promise((resolve, reject) => {
                const interval = 200;
                let timeout = setTimeout(() => {
                    testFailed(`'${event}' event not fired in ${interval / 1000} seconds!`);
                    resolve();
                }, interval);

                let checkResult = (callback) => {
                    switch (callback()) {
                    case "resolve":
                        clearTimeout(timeout);
                        resolve();
                        break;
                    case "reject":
                        clearTimeout(timeout);
                        reject();
                        break;
                    case "continue":
                        break;
                    }
                }

                stream.getTracks().forEach(track => {
                    track.onmute = (evt) => { debug('EVENT: mute'); checkResult(mute); }
                    track.onunmute = (evt) => { debug('EVENT: unmute'); checkResult(unmute); }
                });
            });
        }

        function createScreenCaptureStream() {
            let resolveCallback, rejectCallback;
            let promise = new Promise((resolve, reject) => {
                resolveCallback = resolve;
                rejectCallback = reject;
            });
            debug("<br>*** Creating screen capture stream");
            window.internals.withUserGesture(async () => {
                try {
                    displayStream = await navigator.mediaDevices.getDisplayMedia({ video: true });
                    shouldBeType("displayStream", "Object");
                    shouldBe("displayStream.getTracks().length", "1");
                    shouldBe("displayStream.getVideoTracks().length", "1");
                    screenCaptureTrack = displayStream.getVideoTracks()[0];
                    shouldBeFalse("screenCaptureTrack.muted");
                    resolveCallback();
                } catch(e) {
                    rejectCallback(e);
                }
            });
            return promise;
        }

        async function createCameraMicrophoneStream() {
            debug("<br>*** Creating camera and microphone stream");
            cameraStream = await navigator.mediaDevices.getUserMedia({ audio: true, video: true });
            shouldBeType("cameraStream", "Object");
            shouldBe("cameraStream.getTracks().length", "2");
            microphoneCaptureTrack = cameraStream.getAudioTracks()[0];
            cameraCaptureTrack = cameraStream.getVideoTracks()[0];
            shouldBeFalse("microphoneCaptureTrack.muted");
            shouldBeFalse("cameraCaptureTrack.muted");
        }

        async function muteScreenCapture() {
            debug("<br>*** Muting screen capture");
            if (window.internals)
                window.internals.setPageMuted("screencapture");

            await waitForMutedChange(displayStream, "mute",
                () => { return "resolve"; },
                () => { testFailed("unexpected 'unmute' event"); return "reject"; }
            );

            shouldBeTrue("screenCaptureTrack.muted");
            shouldBeFalse("microphoneCaptureTrack.muted");
            shouldBeFalse("cameraCaptureTrack.muted");

            await checkPageState("HasMutedScreenCaptureDevice", "HasActiveScreenCaptureDevice");
            await checkPageState("HasActiveAudioCaptureDevice", "HasMutedAudioCaptureDevice");
            await checkPageState("HasActiveAudioCaptureDevice", "HasMutedAudioCaptureDevice");
        }

        async function muteCameraMicrophone() {
            debug("<br>*** Muting camera and microphone");
            if (window.internals)
                window.internals.setPageMuted("screencapture,capturedevices");

            count = 0;
            await waitForMutedChange(cameraStream, "mute",
                () => { return (++count == 2) ? "resolve" : "continue"; },
                () => { testFailed("unexpected 'unmute' event"); return "reject"; }
            );
            shouldBeTrue("screenCaptureTrack.muted");
            shouldBeTrue("microphoneCaptureTrack.muted");
            shouldBeTrue("cameraCaptureTrack.muted");

            await checkPageState("HasMutedScreenCaptureDevice", "HasActiveScreenCaptureDevice");
            await checkPageState("HasMutedAudioCaptureDevice", "HasActiveAudioCaptureDevice");
            await checkPageState("HasMutedVideoCaptureDevice", "HasActiveVideoCaptureDevice");
        }

        async function unmuteCameraMicrophone() {
            debug("<br>*** Unmuting camera and microphone");
            if (window.internals)
                internals.setPageMuted("screencapture");

            count = 0;
            await waitForMutedChange(cameraStream, "unmute",
                () => { testFailed("unexpected 'mute' event"); return "reject"; },
                () => { return (++count == 2) ? "resolve" : "continue"; }
            );
            shouldBeTrue("screenCaptureTrack.muted");
            shouldBeFalse("microphoneCaptureTrack.muted");
            shouldBeFalse("cameraCaptureTrack.muted");

            await checkPageState("HasMutedScreenCaptureDevice", "HasActiveScreenCaptureDevice");
            await checkPageState("HasActiveAudioCaptureDevice", "HasMutedAudioCaptureDevice");
            await checkPageState("HasActiveAudioCaptureDevice", "HasMutedAudioCaptureDevice");
        }

        async function unmuteScreenCapture() {
            debug("<br>*** Unmuting screen capture");
            if (window.internals)
                internals.setPageMuted("");

            await waitForMutedChange(displayStream, "unmute",
                () => { testFailed("unexpected 'mute' event"); return "reject"; },
                () => { return "resolve"; }
            );
            shouldBeFalse("screenCaptureTrack.muted");
            shouldBeFalse("microphoneCaptureTrack.muted");
            shouldBeFalse("cameraCaptureTrack.muted");

            await checkPageState("HasActiveScreenCaptureDevice", "HasMutedScreenCaptureDevice");
            await checkPageState("HasActiveAudioCaptureDevice", "HasMutedAudioCaptureDevice");
            await checkPageState("HasActiveAudioCaptureDevice", "HasMutedAudioCaptureDevice");
        }

        jsTestIsAsync = true;
        if (window.testRunner)
            testRunner.waitUntilDone();
        if (window.internals)
            window.internals.settings.setScreenCaptureEnabled(true);

        (async function() {
            try {
                await createScreenCaptureStream();
                await createCameraMicrophoneStream();
                await muteScreenCapture();
                await muteCameraMicrophone();
                await unmuteCameraMicrophone();
                await unmuteScreenCapture();
            } catch (exception) {
                failTest(exception.description);
            }

            finishJSTest();
        })()

    </script>
</head>
<body>
<script src="../../resources/js-test-post.js"></script>
</body>
</html>
