chromium/chrome/test/data/extensions/api_test/tab_capture/api_tests/api_tests.js

// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

var tabCapture = chrome.tabCapture;

var helloWorldPageUri = 'data:text/html;charset=UTF-8,' +
    encodeURIComponent('<html><body>Hello world!</body></html>');

function assertIsSameSetOfTabs(list_a, list_b, id_field_name) {
  chrome.test.assertEq(list_a.length, list_b.length);
  function tabIdSortFunction(a, b) {
    return (a[id_field_name] || 0) - (b[id_field_name] || 0);
  }
  list_a.sort(tabIdSortFunction);
  list_b.sort(tabIdSortFunction);
  for (var i = 0, end = list_a.length; i < end; ++i) {
    chrome.test.assertEq(list_a[i][id_field_name], list_b[i][id_field_name]);
  }
}

// Since all of these tests run in the same tab with limited teardown between
// tests, we have to be extra-careful to not pollute state between tests. Tests
// should be completely sure that capture state is as expected before exiting,
// otherwise the next test in the suite will fail. For context, see
// https://crbug.com/764464 for some of the motivation here.
const CaptureState = {
  kPending: 'pending',
  kActive: 'active',
  kStopped: 'stopped'
};
let g_state = CaptureState.kStopped;
let g_should_call_succeed = false;

tabCapture.onStatusChanged.addListener(function(info) {
  g_state = info.status;
  if (g_should_call_succeed) {
    chrome.test.succeed();
    g_should_call_succeed = false;
  }
});

// Since if capture is already stopped this method immediately calls succeed,
// tests should use it directly in place of a chrome.test.succeed call.
function succeedOnCaptureStopped() {
  if (g_state == CaptureState.kStopped) {
    chrome.test.succeed();
  } else {
    g_should_call_succeed = true;
  }
}

function assertIsValidStreamId(streamId) {
  chrome.test.assertTrue(typeof streamId == 'string');
  navigator.webkitGetUserMedia({
    audio: false,
    video: {
      mandatory: {
        chromeMediaSource: 'tab',
        chromeMediaSourceId: streamId
      }
    }
  }, function(stream) {
    chrome.test.assertTrue(!!stream);
    stream.getVideoTracks()[0].stop();
    succeedOnCaptureStopped();
  }, function(error) {
    chrome.test.fail(error);
  });
}

function assertGetUserMediaError(streamId) {
  chrome.test.assertTrue(typeof streamId == 'string');
  navigator.webkitGetUserMedia(
      {
        audio: false,
        video: {
          mandatory: {chromeMediaSource: 'tab', chromeMediaSourceId: streamId}
        }
      },
      function(stream) {
        chrome.test.assertTrue(!!stream);
        stream.getVideoTracks()[0].stop();
        chrome.test.fail('Should not get stream.');
      },
      function(error) {
        succeedOnCaptureStopped();
      });
}

var testsToRun = [
  function captureTabAndVerifyStateTransitions() {
    // Tab capture events in the order they happen.
    var tabCaptureEvents = [];

    var tabCaptureListener = function(info) {
      if (info.status == 'stopped') {
        chrome.test.assertEq('active', tabCaptureEvents.pop());
        chrome.test.assertEq('pending', tabCaptureEvents.pop());
        tabCapture.onStatusChanged.removeListener(tabCaptureListener);
        chrome.test.succeed();
        return;
      }
      tabCaptureEvents.push(info.status);
    };
    tabCapture.onStatusChanged.addListener(tabCaptureListener);

    tabCapture.capture({audio: true, video: true}, function(stream) {
      chrome.test.assertTrue(!!stream);
      stream.getVideoTracks()[0].stop();
      stream.getAudioTracks()[0].stop();
    });
  },

  function getCapturedTabs() {
    chrome.tabs.create({active: true}, function(secondTab) {
      // chrome.tabCapture.capture() will only capture the active tab.
      chrome.test.assertTrue(secondTab.active);

      function checkInfoForSecondTabHasStatus(infos, status) {
        for (var i = 0; i < infos.length; ++i) {
          if (infos[i].tabId == secondTab) {
            chrome.test.assertNe(null, status);
            chrome.test.assertEq(status, infos[i].status);
            chrome.test.assertEq(false, infos[i].fullscreen);
            return;
          }
        }
      }

      // Step 4: After the second tab is closed, check that getCapturedTabs()
      // returns no info at all about the second tab.
      chrome.tabs.onRemoved.addListener(function() {
        tabCapture.getCapturedTabs(function checkNoInfos(infos) {
          checkInfoForSecondTabHasStatus(infos, null);
          chrome.test.succeed();
        });
      });

      var activeStream = null;

      // Step 3: After the stream is stopped, check that getCapturedTabs()
      // returns 'stopped' capturing status for the second tab.
      var capturedTabsAfterStopCapture = function(infos) {
        checkInfoForSecondTabHasStatus(infos, 'stopped');
        chrome.tabs.remove(secondTab.id);
      };

      // Step 2: After the stream is started, check that getCapturedTabs()
      // returns 'active' capturing status for the second tab.
      var capturedTabsAfterStartCapture = function(infos) {
        checkInfoForSecondTabHasStatus(infos, 'active');
        activeStream.getVideoTracks()[0].stop();
        activeStream.getAudioTracks()[0].stop();
        tabCapture.getCapturedTabs(capturedTabsAfterStopCapture);
      };

      // Step 1: Start capturing the second tab (the currently active tab).
      tabCapture.capture({audio: true, video: true}, function(stream) {
        chrome.test.assertTrue(!!stream);
        activeStream = stream;
        tabCapture.getCapturedTabs(capturedTabsAfterStartCapture);
      });
    });
  },

  function captureSameTab() {
    var stream1 = null;

    var tabMediaRequestCallback2 = function(stream) {
      chrome.test.assertLastError(
          'Cannot capture a tab with an active stream.');
      chrome.test.assertTrue(!stream);
      stream1.getVideoTracks()[0].stop();
      stream1.getAudioTracks()[0].stop();
      succeedOnCaptureStopped();
    };

    tabCapture.capture({audio: true, video: true}, function(stream) {
      chrome.test.assertTrue(!!stream);
      stream1 = stream;
      tabCapture.capture({audio: true, video: true}, tabMediaRequestCallback2);
    });
  },

  function onlyVideo() {
    tabCapture.capture({video: true}, function(stream) {
      chrome.test.assertTrue(!!stream);
      stream.getVideoTracks()[0].stop();
      succeedOnCaptureStopped();
    });
  },

  function onlyAudio() {
    tabCapture.capture({audio: true}, function(stream) {
      chrome.test.assertTrue(!!stream);
      stream.getAudioTracks()[0].stop();
      succeedOnCaptureStopped();
    });
  },

  function noAudioOrVideoRequested() {
    // If not specified, video is not requested.
    tabCapture.capture({audio: false}, function(stream) {
      chrome.test.assertLastError(
          'Capture failed. No audio or video requested.');
      chrome.test.assertTrue(!stream);
      succeedOnCaptureStopped();
    });
  },

  function getMediaStreamIdWithCallerTab() {
    chrome.tabs.getCurrent(function(tab) {
      tabCapture.getMediaStreamId({consumerTabId: tab.id}, function(streamId) {
        assertIsValidStreamId(streamId);
      });
    });
  },

  function getMediaStreamIdWithTargetTab() {
    chrome.tabs.getCurrent(function(tab) {
      tabCapture.getMediaStreamId({targetTabId: tab.id}, function(streamId) {
        assertIsValidStreamId(streamId);
      });
    });
  },

  function getMediaStreamIdWithoutTabIds() {
    tabCapture.getMediaStreamId(function(streamId) {
      assertIsValidStreamId(streamId);
    });
  },

  // Test that if calling getMediaStreamId() with consumer tab specified and
  // then calling getUserMedia() on another tab will fail to get the stream.
  function getMediaStreamIdAndGetUserMediaOnDifferentTabs() {
    var currentTabId;
    var secondTabId;

    var listener = function(tabId, changeInfo, tab) {
      if (changeInfo.status == 'complete') {
        chrome.tabs.onUpdated.removeListener(listener);
        tabCapture.getMediaStreamId(
            {consumerTabId: secondTabId},
            function(streamId) {
              chrome.tabs.get(currentTabId, function(tab) {
                assertGetUserMediaError(streamId);
                chrome.tabs.remove(secondTabId);
              });
            });
      }
    };

    chrome.tabs.onUpdated.addListener(listener);
    chrome.tabs.getCurrent(function(tab) {
      currentTabId = tab.id;
      chrome.tabs.create({}, function(tab) {
        secondTabId = tab.id;
      });
    });
  },

  function getMediaStreamIdWithCallerTabWithoutSecuredUrl() {
    var listener = function(tabId, changeInfo, tab) {
      if (changeInfo.status == 'complete') {
        chrome.tabs.onUpdated.removeListener(listener);
        tabCapture.getMediaStreamId({consumerTabId: tabId}, function(streamId) {
          chrome.test.assertTrue(typeof streamId == 'undefined');
          chrome.test.assertLastError(
              'URL scheme for the specified tab is not secure.');
          chrome.test.succeed();
          chrome.tabs.remove(tab.id);
        });
      }
    };

    chrome.tabs.onUpdated.addListener(listener);
    chrome.tabs.create({url: 'http://example.com/fun.html'});
  },
];

chrome.test.runTests(testsToRun);