Update the MediaCapabilities dictionaries to follow the spec more closely.

This partially backs out bug 1471165 now that we don't enforce a default value
for dictionary-typed members of dictionaries.

Differential Revision: https://phabricator.services.mozilla.com/D6876

bugzilla-url: https://bugzilla.mozilla.org/show_bug.cgi?id=1493798
gecko-commit: a4c0087797d6752c7f18563c8d2ead7e87eb2e9c
gecko-integration-branch: autoland
gecko-reviewers: jya
diff --git a/media-capabilities/decodingInfo.html b/media-capabilities/decodingInfo.html
index f3fca41..982bc17 100644
--- a/media-capabilities/decodingInfo.html
+++ b/media-capabilities/decodingInfo.html
@@ -291,7 +291,8 @@
 
 async_test(t => {
   var validTypes = [ 'file', 'media-source' ];
-  var invalidTypes = [ undefined, null, '', 'foobar', 'mse', 'MediaSource' ];
+  var invalidTypes = [ undefined, null, '', 'foobar', 'mse', 'MediaSource',
+                       'record', 'transmission' ];
 
   var validPromises = [];
   var invalidCaught = 0;
diff --git a/media-capabilities/encodingInfo.html b/media-capabilities/encodingInfo.html
new file mode 100644
index 0000000..5c0e318
--- /dev/null
+++ b/media-capabilities/encodingInfo.html
@@ -0,0 +1,328 @@
+<!DOCTYPE html>
+<title>MediaCapabilities.decodingInfo()</title>
+<script src=/resources/testharness.js></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+
+// Minimal VideoConfiguration that will be allowed per spec. All optional
+// properties are missing.
+var minimalVideoConfiguration = {
+  contentType: 'video/webm; codecs="vp09.00.10.08"',
+  width: 800,
+  height: 600,
+  bitrate: 3000,
+  framerate: 24,
+};
+
+// Minimal AudioConfiguration that will be allowed per spec. All optional
+// properties are missing.
+var minimalAudioConfiguration = {
+  contentType: 'audio/webm; codecs="opus"',
+};
+
+promise_test(t => {
+  return promise_rejects(t, new TypeError(), navigator.mediaCapabilities.encodingInfo());
+}, "Test that encodingInfo rejects if it doesn't get a configuration");
+
+promise_test(t => {
+  return promise_rejects(t, new TypeError(), navigator.mediaCapabilities.encodingInfo({}));
+}, "Test that encodingInfo rejects if the MediaConfiguration isn't valid");
+
+promise_test(t => {
+  return promise_rejects(t, new TypeError(), navigator.mediaCapabilities.encodingInfo({
+    video: minimalVideoConfiguration,
+    audio: minimalAudioConfiguration,
+  }));
+}, "Test that encodingInfo rejects if the MediaConfiguration does not have a type");
+
+promise_test(t => {
+  return promise_rejects(t, new TypeError(), navigator.mediaCapabilities.encodingInfo({
+    type: 'record',
+  }));
+}, "Test that encodingInfo rejects if the configuration doesn't have an audio or video field");
+
+promise_test(t => {
+  return promise_rejects(t, new TypeError(), navigator.mediaCapabilities.encodingInfo({
+    type: 'record',
+    video: {
+      contentType: 'video/webm; codecs="vp09.00.10.08"',
+      width: 800,
+      height: 600,
+      bitrate: 3000,
+      framerate: -1,
+    },
+  }));
+}, "Test that encodingInfo rejects if the video configuration has a negative framerate");
+
+promise_test(t => {
+  return promise_rejects(t, new TypeError(), navigator.mediaCapabilities.encodingInfo({
+    type: 'record',
+    video: {
+      contentType: 'video/webm; codecs="vp09.00.10.08"',
+      width: 800,
+      height: 600,
+      bitrate: 3000,
+      framerate: 0,
+    },
+  }));
+}, "Test that encodingInfo rejects if the video configuration has a framerate set to 0");
+
+promise_test(t => {
+  return promise_rejects(t, new TypeError(), navigator.mediaCapabilities.encodingInfo({
+    type: 'record',
+    video: {
+      contentType: 'video/webm; codecs="vp09.00.10.08"',
+      width: 800,
+      height: 600,
+      bitrate: 3000,
+      framerate: Infinity,
+    },
+  }));
+}, "Test that encodingInfo rejects if the video configuration has a framerate set to Infinity");
+
+promise_test(t => {
+  return promise_rejects(t, new TypeError(), navigator.mediaCapabilities.encodingInfo({
+    type: 'record',
+    video: {
+      contentType: 'fgeoa',
+      width: 800,
+      height: 600,
+      bitrate: 3000,
+      framerate: 24,
+    },
+  }));
+}, "Test that encodingInfo rejects if the video configuration contentType doesn't parse");
+
+promise_test(t => {
+  return promise_rejects(t, new TypeError(), navigator.mediaCapabilities.encodingInfo({
+    type: 'record',
+    video: {
+      contentType: 'audio/fgeoa',
+      width: 800,
+      height: 600,
+      bitrate: 3000,
+      framerate: 24,
+    },
+  }));
+}, "Test that encodingInfo rejects if the video configuration contentType isn't of type video");
+
+promise_test(t => {
+  return promise_rejects(t, new TypeError(), navigator.mediaCapabilities.encodingInfo({
+    type: 'record',
+    video: {
+      contentType: 'video/webm; codecs="vp09.00.10.08"; foo="bar"',
+      width: 800,
+      height: 600,
+      bitrate: 3000,
+      framerate: 24,
+    },
+  }));
+}, "Test that encodingInfo rejects if the video configuration contentType has more than one parameter");
+
+promise_test(t => {
+  return promise_rejects(t, new TypeError(), navigator.mediaCapabilities.encodingInfo({
+    type: 'record',
+    video: {
+      contentType: 'video/webm; foo="bar"',
+      width: 800,
+      height: 600,
+      bitrate: 3000,
+      framerate: 24,
+    },
+  }));
+}, "Test that encodingInfo rejects if the video configuration contentType has one parameter that isn't codecs");
+
+promise_test(t => {
+  return navigator.mediaCapabilities.encodingInfo({
+    type: 'record',
+    video: {
+      contentType: 'video/webm; codecs="vp09.00.10.08"',
+      width: 800,
+      height: 600,
+      bitrate: 3000,
+      framerate: '24000/1001',
+    }
+  });
+}, "Test that encodingInfo() accepts framerate in the form of x/y");
+
+promise_test(t => {
+  return promise_rejects(t, new TypeError(), navigator.mediaCapabilities.encodingInfo({
+    type: 'record',
+    video: {
+      contentType: 'video/webm; codecs="vp09.00.10.08"',
+      width: 800,
+      height: 600,
+      bitrate: 3000,
+      framerate: '24000/0',
+    }
+  }));
+}, "Test that encodingInfo() rejects framerate in the form of x/0");
+
+promise_test(t => {
+  return promise_rejects(t, new TypeError(), navigator.mediaCapabilities.encodingInfo({
+    type: 'record',
+    video: {
+      contentType: 'video/webm; codecs="vp09.00.10.08"',
+      width: 800,
+      height: 600,
+      bitrate: 3000,
+      framerate: '0/10001',
+    }
+  }));
+}, "Test that encodingInfo() rejects framerate in the form of 0/y");
+
+promise_test(t => {
+  return promise_rejects(t, new TypeError(), navigator.mediaCapabilities.encodingInfo({
+    type: 'record',
+    video: {
+      contentType: 'video/webm; codecs="vp09.00.10.08"',
+      width: 800,
+      height: 600,
+      bitrate: 3000,
+      framerate: '-24000/10001',
+    }
+  }));
+}, "Test that encodingInfo() rejects framerate in the form of -x/y");
+
+promise_test(t => {
+  return promise_rejects(t, new TypeError(), navigator.mediaCapabilities.encodingInfo({
+    type: 'record',
+    video: {
+      contentType: 'video/webm; codecs="vp09.00.10.08"',
+      width: 800,
+      height: 600,
+      bitrate: 3000,
+      framerate: '24000/-10001',
+    }
+  }));
+}, "Test that encodingInfo() rejects framerate in the form of x/-y");
+
+promise_test(t => {
+  return promise_rejects(t, new TypeError(), navigator.mediaCapabilities.encodingInfo({
+    type: 'record',
+    video: {
+      contentType: 'video/webm; codecs="vp09.00.10.08"',
+      width: 800,
+      height: 600,
+      bitrate: 3000,
+      framerate: '24000/',
+    }
+  }));
+}, "Test that encodingInfo() rejects framerate in the form of x/");
+
+promise_test(t => {
+  return navigator.mediaCapabilities.encodingInfo({
+    type: 'record',
+    video: {
+      contentType: 'video/webm; codecs="vp09.00.10.08"',
+      width: 800,
+      height: 600,
+      bitrate: 3000,
+      framerate: '24000/1e4',
+    }
+  });
+}, "Test that encodingInfo() accepts framerate with 'e'");
+
+promise_test(t => {
+  return navigator.mediaCapabilities.encodingInfo({
+    type: 'record',
+    video: {
+      contentType: 'video/webm; codecs="vp09.00.10.08"',
+      width: 800,
+      height: 600,
+      bitrate: 3000,
+      framerate: '24/1.0001',
+    }
+  });
+}, "Test that encodingInfo() accepts framerate as fraction with decimals");
+
+promise_test(t => {
+  return promise_rejects(t, new TypeError(), navigator.mediaCapabilities.encodingInfo({
+    type: 'record',
+    video: {
+      contentType: 'video/webm; codecs="vp09.00.10.08"',
+      width: 800,
+      height: 600,
+      bitrate: 3000,
+      framerate: '1/3x',
+    }
+  }));
+}, "Test that encodingInfo() rejects framerate with trailing unallowed characters");
+
+promise_test(t => {
+  return promise_rejects(t, new TypeError(), navigator.mediaCapabilities.encodingInfo({
+    type: 'record',
+    audio: { contentType: 'fgeoa' },
+  }));
+}, "Test that encodingInfo rejects if the audio configuration contenType doesn't parse");
+
+promise_test(t => {
+  return promise_rejects(t, new TypeError(), navigator.mediaCapabilities.encodingInfo({
+    type: 'record',
+    audio: { contentType: 'video/fgeoa' },
+  }));
+}, "Test that encodingInfo rejects if the audio configuration contentType isn't of type audio");
+
+promise_test(t => {
+  return promise_rejects(t, new TypeError(), navigator.mediaCapabilities.encodingInfo({
+    type: 'record',
+    audio: { contentType: 'audio/webm; codecs="opus"; foo="bar"' },
+  }));
+}, "Test that encodingInfo rejects if the audio configuration contentType has more than one parameters");
+
+promise_test(t => {
+  return promise_rejects(t, new TypeError(), navigator.mediaCapabilities.encodingInfo({
+    type: 'record',
+    audio: { contentType: 'audio/webm; foo="bar"' },
+  }));
+}, "Test that encodingInfo rejects if the audio configuration contentType has one parameter that isn't codecs");
+
+promise_test(t => {
+  return navigator.mediaCapabilities.encodingInfo({
+    type: 'record',
+    video: minimalVideoConfiguration,
+    audio: minimalAudioConfiguration,
+  }).then(ability => {
+    assert_idl_attribute(ability, 'supported');
+    assert_idl_attribute(ability, 'smooth');
+    assert_idl_attribute(ability, 'powerEfficient');
+  });
+}, "Test that encodingInfo returns a valid MediaCapabilitiesInfo objects");
+
+async_test(t => {
+  var validTypes = [ 'record', 'transmission' ];
+  var invalidTypes = [ undefined, null, '', 'foobar', 'mse', 'MediaSource',
+                       'file', 'media-source', ];
+
+  var validPromises = [];
+  var invalidCaught = 0;
+
+  validTypes.forEach(type => {
+    validPromises.push(navigator.mediaCapabilities.encodingInfo({
+      type: type,
+      video: minimalVideoConfiguration,
+      audio: minimalAudioConfiguration,
+    }));
+  });
+
+  // validTypes are tested via Promise.all(validPromises) because if one of the
+  // promises fail, Promise.all() will reject. This mechanism can't be used for
+  // invalid types which will be tested individually and increment invalidCaught
+  // when rejected until the amount of rejection matches the expectation.
+  Promise.all(validPromises).then(t.step_func(() => {
+    for (var i = 0; i < invalidTypes.length; ++i) {
+      navigator.mediaCapabilities.encodingInfo({
+        type: invalidTypes[i],
+        video: minimalVideoConfiguration,
+        audio: minimalAudioConfiguration,
+      }).then(t.unreached_func(), t.step_func(e => {
+        assert_equals(e.name, 'TypeError');
+        ++invalidCaught;
+        if (invalidCaught == invalidTypes.length)
+          t.done();
+      }));
+    }
+  }), t.unreached_func('Promise.all should not reject for valid types'));
+}, "Test that encodingInfo rejects if the MediaConfiguration does not have a valid type");
+
+</script>