mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge m-i to m-c
This commit is contained in:
commit
482fc23a2c
@ -2678,30 +2678,24 @@ nsDocument::InitCSP(nsIChannel* aChannel)
|
||||
}
|
||||
|
||||
// While we are supporting both CSP 1.0 and the x- headers, the 1.0 headers
|
||||
// can coexist with x- headers. If both exist, they're both enforced, but
|
||||
// there's a warning posted in the web console that the x-headers are going
|
||||
// away.
|
||||
// take priority. If both are present, the x-* headers are ignored.
|
||||
|
||||
// ----- if there's a full-strength CSP header, apply it.
|
||||
if (!cspOldHeaderValue.IsEmpty()) {
|
||||
if (!cspHeaderValue.IsEmpty()) {
|
||||
rv = AppendCSPFromHeader(csp, cspHeaderValue, selfURI, false, true);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
} else if (!cspOldHeaderValue.IsEmpty()) {
|
||||
rv = AppendCSPFromHeader(csp, cspOldHeaderValue, selfURI, false, false);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
if (!cspHeaderValue.IsEmpty()) {
|
||||
rv = AppendCSPFromHeader(csp, cspHeaderValue, selfURI, false, true);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
// ----- if there's a report-only CSP header, apply it.
|
||||
if (!cspOldROHeaderValue.IsEmpty()) {
|
||||
rv = AppendCSPFromHeader(csp, cspOldROHeaderValue, selfURI, true, false);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
if (!cspROHeaderValue.IsEmpty()) {
|
||||
rv = AppendCSPFromHeader(csp, cspROHeaderValue, selfURI, true, true);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
} else if (!cspOldROHeaderValue.IsEmpty()) {
|
||||
rv = AppendCSPFromHeader(csp, cspOldROHeaderValue, selfURI, true, false);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
// ----- Enforce frame-ancestor policy on any applied policies
|
||||
|
@ -1,5 +1,6 @@
|
||||
<html>
|
||||
<body>
|
||||
<img src="http://example.org/nonexistent.jpg"></img>
|
||||
<img src="http://example.org/prefixed.jpg"></img>
|
||||
<img src="/unprefixed.jpg"></img>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -1,2 +1,2 @@
|
||||
X-Content-Security-Policy: default-src 'self' ; img-src 'self' http://example.org
|
||||
X-Content-Security-Policy: default-src 'none' ; img-src http://example.org
|
||||
Content-Security-Policy: default-src 'self'
|
||||
|
@ -2,6 +2,8 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Test for Correctly Handling Both Pre-1.0 and 1.0 Content Security Policy Headers</title>
|
||||
<!-- When both headers are present, we should ignore the pre-1.0 header and
|
||||
only recognize the 1.0 spec-compliant header. -->
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
@ -13,9 +15,12 @@
|
||||
<iframe style="width:200px;height:200px;" id='cspframe'></iframe>
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
var loadedImgURL = "http://example.org/nonexistent.jpg";
|
||||
var prefixedHeaderImgURL = "http://example.org/prefixed.jpg";
|
||||
var unprefixedHeaderImgURL = "http://mochi.test:8888/unprefixed.jpg";
|
||||
var testsRun = 0;
|
||||
var totalTests = 2;
|
||||
|
||||
// This is used to watch the blocked data bounce off CSP and allowed data
|
||||
// This is used to watch the blocked data bounce off CSP and allowed data
|
||||
// get sent out to the wire.
|
||||
function examiner() {
|
||||
SpecialPowers.addObserver(this, "csp-on-violate-policy", false);
|
||||
@ -28,25 +33,21 @@ examiner.prototype = {
|
||||
return;
|
||||
|
||||
if (topic === "http-on-modify-request") {
|
||||
// the load was allowed, this is a fail, the Content-Security Policy
|
||||
// should not allow the load
|
||||
var asciiSpec = SpecialPowers.getPrivilegedProps(SpecialPowers.do_QueryInterface(subject, "nsIHttpChannel"), "URI.asciiSpec");
|
||||
if (asciiSpec != loadedImgURL) return;
|
||||
|
||||
ok(false, "the Content-Security Policy header does not allow the load, the X-Content-Security header should be ignored");
|
||||
window.examiner.remove();
|
||||
SimpleTest.finish();
|
||||
if (asciiSpec == prefixedHeaderImgURL || asciiSpec == unprefixedHeaderImgURL) {
|
||||
is(asciiSpec, unprefixedHeaderImgURL, "Load was allowed - should be allowed by unprefixed header (blocked by prefixed)");
|
||||
testRan();
|
||||
}
|
||||
}
|
||||
|
||||
if (topic === "csp-on-violate-policy") {
|
||||
// the load was blocked, this is a pass, the Content-Security-Policy
|
||||
// header doesn't allow the load, but the X-Content-Security-Header does
|
||||
var asciiSpec = SpecialPowers.getPrivilegedProps(SpecialPowers.do_QueryInterface(subject, "nsIURI"), "asciiSpec");
|
||||
if (asciiSpec != loadedImgURL) return;
|
||||
|
||||
ok(true, "Load was blocked - the Content-Security-Policy header doesn't allow the load, the X-Content-Security-Header does but should have been ignored");
|
||||
window.examiner.remove();
|
||||
SimpleTest.finish();
|
||||
if (asciiSpec == prefixedHeaderImgURL || asciiSpec == unprefixedHeaderImgURL) {
|
||||
is(asciiSpec, prefixedHeaderImgURL, "Load was blocked - the Content-Security-Policy header doesn't allow the load, the X-Content-Security-Header does but should have been ignored");
|
||||
testRan();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -59,17 +60,21 @@ examiner.prototype = {
|
||||
}
|
||||
|
||||
window.examiner = new examiner();
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
// save this for last so that our listeners are registered.
|
||||
// ... this loads the testbed of good and bad requests.
|
||||
function testRan() {
|
||||
testsRun++;
|
||||
if (testsRun == totalTests) {
|
||||
window.examiner.remove();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
}
|
||||
|
||||
SpecialPowers.pushPrefEnv(
|
||||
{'set':[["security.csp.speccompliant", true]]},
|
||||
function() {
|
||||
// save this for last so that our listeners are registered.
|
||||
// ... this loads the testbed of good and bad requests.
|
||||
document.getElementById('cspframe').src = 'file_bothCSPheaders.html';
|
||||
{'set':[["security.csp.speccompliant", true]]},
|
||||
function loadTestRequests() {
|
||||
var cspframe = document.getElementById('cspframe');
|
||||
cspframe.src = 'file_bothCSPheaders.html';
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
@ -24,6 +24,10 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=843725
|
||||
* various key events while it is in various states.
|
||||
**/
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
// Turn off Spatial Navigation because it hijacks arrow keydown events:
|
||||
SpecialPowers.setBoolPref("snav.enabled", false);
|
||||
|
||||
SimpleTest.waitForFocus(function() {
|
||||
test();
|
||||
SimpleTest.finish();
|
||||
|
@ -26,6 +26,10 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=853525
|
||||
* ugly rounding errors.
|
||||
**/
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
// Turn off spatial navigation because it hijacks arrow keydown events:
|
||||
SpecialPowers.setBoolPref("snav.enabled", false);
|
||||
|
||||
SimpleTest.waitForFocus(function() {
|
||||
test();
|
||||
SimpleTest.finish();
|
||||
|
@ -22,6 +22,9 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=633058
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
// Turn off Spatial Navigation so that the 'keypress' event fires.
|
||||
SpecialPowers.setBoolPref('snav.enabled', false);
|
||||
|
||||
SimpleTest.waitForFocus(function() {
|
||||
var nbExpectedKeyPress = 8;
|
||||
var inputGotKeyPress = 0;
|
||||
|
@ -19,6 +19,11 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=674558
|
||||
|
||||
/** Test for Bug 674558 **/
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
// Turn off spatial navigation because it hijacks VK_RIGHT and VK_LEFT keydown
|
||||
// events.
|
||||
SpecialPowers.setBoolPref("snav.enabled", false);
|
||||
|
||||
SimpleTest.waitForFocus(function() {
|
||||
function textAreaCtor() {
|
||||
return document.createElement("textarea");
|
||||
|
@ -694,7 +694,9 @@ nsresult nsGeolocationService::Init()
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_WIDGET_COCOA
|
||||
mProvider = new CoreLocationLocationProvider();
|
||||
if (Preferences::GetBool("geo.provider.use_corelocation", false)) {
|
||||
mProvider = new CoreLocationLocationProvider();
|
||||
}
|
||||
#endif
|
||||
|
||||
// Override platform-specific providers with the default (network)
|
||||
|
@ -41,6 +41,10 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=265203
|
||||
|
||||
/** Test for Bug 265203 **/
|
||||
|
||||
// Turn off spatial navigation because it hijacks VK_RIGHT and VK_LEFT keydown
|
||||
// events
|
||||
SpecialPowers.setBoolPref("snav.enabled", false);
|
||||
|
||||
var gTestStarted = false;
|
||||
var expectedResult = [ null, 0, null ];
|
||||
var nextTest;
|
||||
|
@ -23,7 +23,13 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=795785
|
||||
|
||||
<script class="testbody" type="application/javascript">
|
||||
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
// Turn off spatial navigation because it hijacks arrow key events and VK_RETURN
|
||||
// events.
|
||||
SpecialPowers.setBoolPref("snav.enabled", false);
|
||||
|
||||
SimpleTest.waitForFocus(runTests);
|
||||
|
||||
var textarea = document.getElementById("textarea");
|
||||
|
@ -4647,11 +4647,12 @@ nsCSSFrameConstructor::ConstructFrameWithAnonymousChild(
|
||||
// Create the outer frame:
|
||||
nsIFrame* newFrame = aConstructor(mPresShell, styleContext);
|
||||
|
||||
nsIFrame* geometricParent =
|
||||
aState.GetGeometricParent(styleContext->StyleDisplay(),
|
||||
aParentFrame);
|
||||
|
||||
InitAndRestoreFrame(aState, content, geometricParent, newFrame);
|
||||
InitAndRestoreFrame(aState, content,
|
||||
aCandidateRootFrame ?
|
||||
aState.GetGeometricParent(styleContext->StyleDisplay(),
|
||||
aParentFrame) :
|
||||
aParentFrame,
|
||||
newFrame);
|
||||
|
||||
// Create the pseudo SC for the anonymous wrapper child as a child of the SC:
|
||||
nsRefPtr<nsStyleContext> scForAnon;
|
||||
@ -4666,7 +4667,8 @@ nsCSSFrameConstructor::ConstructFrameWithAnonymousChild(
|
||||
// Put the newly created frames into the right child list
|
||||
SetInitialSingleChild(newFrame, innerFrame);
|
||||
|
||||
aState.AddChild(newFrame, aFrameItems, content, styleContext, aParentFrame);
|
||||
aState.AddChild(newFrame, aFrameItems, content, styleContext, aParentFrame,
|
||||
aCandidateRootFrame, aCandidateRootFrame);
|
||||
|
||||
if (!mRootElementFrame && aCandidateRootFrame) {
|
||||
// The frame we're constructing will be the root element frame.
|
||||
|
@ -28,6 +28,10 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=345267
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
/** Test for Bug 345267 **/
|
||||
|
||||
// Turn off Spatial Navigation to stop if from hijacking "left" keypress event.
|
||||
SpecialPowers.setBoolPref('snav.enabled', false);
|
||||
|
||||
is($("d1").value, "abcde",
|
||||
"Displayed initial value should not be truncated by maxlength");
|
||||
is($("u1").value, "abcdef",
|
||||
|
@ -74,6 +74,9 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=365410
|
||||
|
||||
/** Test for Bug 365410 **/
|
||||
|
||||
// Turn off spatial nav so that it does not hijack the up and down events.
|
||||
SpecialPowers.setBoolPref("snav.enabled", false);
|
||||
|
||||
function pageUpDownTest(id,index) {
|
||||
var elm = document.getElementById(id);
|
||||
elm.focus();
|
||||
|
@ -37,6 +37,9 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=563642
|
||||
|
||||
/** Test for Bug 563642 **/
|
||||
|
||||
// Turn off Spatial Navigation because it hijacks down and up key events.
|
||||
SpecialPowers.setBoolPref("snav.enabled", false);
|
||||
|
||||
function pageUpDownTest(id,index) {
|
||||
var elm = document.getElementById(id);
|
||||
elm.focus();
|
||||
|
@ -11,6 +11,11 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=291082
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<script type="application/javascript">
|
||||
/** Test for Bug 291082 **/
|
||||
|
||||
|
||||
// Turn off Spatial Navigation because it hijacks arrow keydown events.
|
||||
SpecialPowers.setBoolPref("snav.enabled", false);
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
function preventDefault(event) {
|
||||
|
5
layout/svg/crashtests/919371-1.xhtml
Normal file
5
layout/svg/crashtests/919371-1.xhtml
Normal file
@ -0,0 +1,5 @@
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<svg xmlns="http://www.w3.org/2000/svg">
|
||||
<marker style="position: absolute;" />
|
||||
</svg>
|
||||
</html>
|
@ -176,3 +176,4 @@ load 895311-1.svg
|
||||
load 897342-1.svg
|
||||
load 898909-1.svg
|
||||
load 898951-1.svg
|
||||
load 919371-1.xhtml
|
||||
|
@ -119,7 +119,7 @@ struct cubeb_stream
|
||||
uint32_t buffer_frame_count;
|
||||
/* Resampler instance. If this is !NULL, resampling should happen. */
|
||||
SpeexResamplerState * resampler;
|
||||
/* Buffer to resample from, into the upmix buffer or the final buffer. */
|
||||
/* Buffer to resample from, into the mix buffer or the final buffer. */
|
||||
float * resampling_src_buffer;
|
||||
/* Pointer to the function used to refill the buffer, depending
|
||||
* on the respective samplerate of the stream and the mix. */
|
||||
@ -128,9 +128,10 @@ struct cubeb_stream
|
||||
uint32_t leftover_frame_count;
|
||||
uint32_t leftover_frame_size;
|
||||
float * leftover_frames_buffer;
|
||||
/* upmix buffer of size |buffer_frame_count * bytes_per_frame / 2|. */
|
||||
float * upmix_buffer;
|
||||
/* Number of bytes per frame. Prefer to use frames_to_bytes_before_upmix. */
|
||||
/* Buffer used to downmix or upmix to the number of channels the mixer has.
|
||||
* its size is |buffer_frame_count * bytes_per_frame * mixer_channels|. */
|
||||
float * mix_buffer;
|
||||
/* Number of bytes per frame. Prefer to use frames_to_bytes_before_mix. */
|
||||
uint8_t bytes_per_frame;
|
||||
/* True if the stream is draining. */
|
||||
bool draining;
|
||||
@ -139,7 +140,12 @@ struct cubeb_stream
|
||||
namespace {
|
||||
bool should_upmix(cubeb_stream * stream)
|
||||
{
|
||||
return stream->upmix_buffer;
|
||||
return stream->mix_params.channels > stream->stream_params.channels;
|
||||
}
|
||||
|
||||
bool should_downmix(cubeb_stream * stream)
|
||||
{
|
||||
return stream->mix_params.channels < stream->stream_params.channels;
|
||||
}
|
||||
|
||||
/* Upmix function, copies a mono channel in two interleaved
|
||||
@ -179,10 +185,23 @@ upmix(T * in, long inframes, T * out, int32_t in_channels, int32_t out_channels)
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void
|
||||
downmix_to_stereo(T * in, long inframes, T * out, int32_t in_channels)
|
||||
{
|
||||
/* We could use a downmix matrix here, applying mixing weight based on the
|
||||
* channel, but directsound and winmm simply drop the channels that cannot be
|
||||
* rendered by the hardware, so we do the same for consistency. */
|
||||
for (int32_t i = 0; i < inframes; i++) {
|
||||
out[i * 2] = in[i * in_channels];
|
||||
out[i * 2 + 1] = in[i * in_channels + 1];
|
||||
}
|
||||
}
|
||||
|
||||
/* This returns the size of a frame in the stream,
|
||||
* before the eventual upmix occurs. */
|
||||
static size_t
|
||||
frame_to_bytes_before_upmix(cubeb_stream * stm, size_t frames)
|
||||
frame_to_bytes_before_mix(cubeb_stream * stm, size_t frames)
|
||||
{
|
||||
size_t stream_frame_size = stm->stream_params.channels * sizeof(float);
|
||||
return stream_frame_size * frames;
|
||||
@ -202,7 +221,7 @@ refill_with_resampling(cubeb_stream * stm, float * data, long frames_needed)
|
||||
long frame_requested = before_resampling - stm->leftover_frame_count;
|
||||
|
||||
size_t leftover_bytes =
|
||||
frame_to_bytes_before_upmix(stm, stm->leftover_frame_count);
|
||||
frame_to_bytes_before_mix(stm, stm->leftover_frame_count);
|
||||
|
||||
/* Copy the previous leftover frames to the front of the buffer. */
|
||||
memcpy(stm->resampling_src_buffer, stm->leftover_frames_buffer, leftover_bytes);
|
||||
@ -218,11 +237,11 @@ refill_with_resampling(cubeb_stream * stm, float * data, long frames_needed)
|
||||
uint32_t in_frames = before_resampling;
|
||||
uint32_t out_frames = frames_needed;
|
||||
|
||||
/* if we need to upmix after resampling, resample into
|
||||
* the upmix buffer to avoid a copy */
|
||||
/* If we need to upmix after resampling, resample into the mix buffer to
|
||||
* avoid a copy. */
|
||||
float * resample_dest;
|
||||
if (should_upmix(stm)) {
|
||||
resample_dest = stm->upmix_buffer;
|
||||
if (should_upmix(stm) || should_downmix(stm)) {
|
||||
resample_dest = stm->mix_buffer;
|
||||
} else {
|
||||
resample_dest = data;
|
||||
}
|
||||
@ -236,11 +255,11 @@ refill_with_resampling(cubeb_stream * stm, float * data, long frames_needed)
|
||||
/* Copy the leftover frames to buffer for the next time. */
|
||||
stm->leftover_frame_count = before_resampling - in_frames;
|
||||
size_t unresampled_bytes =
|
||||
frame_to_bytes_before_upmix(stm, stm->leftover_frame_count);
|
||||
frame_to_bytes_before_mix(stm, stm->leftover_frame_count);
|
||||
|
||||
uint8_t * leftover_frames_start =
|
||||
reinterpret_cast<uint8_t *>(stm->resampling_src_buffer);
|
||||
leftover_frames_start += frame_to_bytes_before_upmix(stm, in_frames);
|
||||
leftover_frames_start += frame_to_bytes_before_mix(stm, in_frames);
|
||||
|
||||
assert(stm->leftover_frame_count <= stm->leftover_frame_size);
|
||||
memcpy(stm->leftover_frames_buffer, leftover_frames_start, unresampled_bytes);
|
||||
@ -252,17 +271,20 @@ refill_with_resampling(cubeb_stream * stm, float * data, long frames_needed)
|
||||
if (should_upmix(stm)) {
|
||||
upmix(resample_dest, out_frames, data,
|
||||
stm->stream_params.channels, stm->mix_params.channels);
|
||||
} else if (should_downmix(stm)) {
|
||||
downmix_to_stereo(resample_dest, out_frames, data,
|
||||
stm->stream_params.channels);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
refill(cubeb_stream * stm, float * data, long frames_needed)
|
||||
{
|
||||
/* If we need to upmix after resampling, get the data into
|
||||
* the upmix buffer to avoid a copy. */
|
||||
/* If we need to upmix/downmix, get the data into the mix buffer to avoid a
|
||||
* copy, then do the processing process. */
|
||||
float * dest;
|
||||
if (should_upmix(stm)) {
|
||||
dest = stm->upmix_buffer;
|
||||
if (should_upmix(stm) || should_downmix(stm)) {
|
||||
dest = stm->mix_buffer;
|
||||
} else {
|
||||
dest = data;
|
||||
}
|
||||
@ -277,6 +299,8 @@ refill(cubeb_stream * stm, float * data, long frames_needed)
|
||||
if (should_upmix(stm)) {
|
||||
upmix(dest, got, data,
|
||||
stm->stream_params.channels, stm->mix_params.channels);
|
||||
} else {
|
||||
downmix_to_stereo(dest, got, data, stm->stream_params.channels);
|
||||
}
|
||||
}
|
||||
|
||||
@ -514,13 +538,14 @@ void wasapi_stream_destroy(cubeb_stream * stm);
|
||||
static void
|
||||
handle_channel_layout(cubeb_stream * stm, WAVEFORMATEX ** mix_format, const cubeb_stream_params * stream_params)
|
||||
{
|
||||
/* Common case: the hardware supports stereo, and the stream is mono or
|
||||
* stereo. Easy. */
|
||||
if ((*mix_format)->nChannels == 2 &&
|
||||
stream_params->channels <= 2) {
|
||||
/* Common case: the hardware is stereo. Up-mixing and down-mixing will be
|
||||
* handled in the callback. */
|
||||
if ((*mix_format)->nChannels == 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Otherwise, the hardware supports more than two channels. */
|
||||
|
||||
/* The docs say that GetMixFormat is always of type WAVEFORMATEXTENSIBLE [1],
|
||||
* so the reinterpret_cast below should be safe. In practice, this is not
|
||||
* true, and we just want to bail out and let the rest of the code find a good
|
||||
@ -559,7 +584,7 @@ handle_channel_layout(cubeb_stream * stm, WAVEFORMATEX ** mix_format, const cub
|
||||
|
||||
if (hr == S_FALSE) {
|
||||
/* Not supported, but WASAPI gives us a suggestion. Use it, and handle the
|
||||
* eventual upmix ourselve */
|
||||
* eventual upmix/downmix ourselve */
|
||||
LOG("Using WASAPI suggested format: channels: %d", closest->nChannels);
|
||||
CoTaskMemFree(*mix_format);
|
||||
*mix_format = closest;
|
||||
@ -596,11 +621,6 @@ wasapi_stream_init(cubeb * context, cubeb_stream ** stream,
|
||||
return CUBEB_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
/* we don't support more that two channels for now. */
|
||||
if (stream_params.channels > 2) {
|
||||
return CUBEB_ERROR_INVALID_FORMAT;
|
||||
}
|
||||
|
||||
cubeb_stream * stm = (cubeb_stream *)calloc(1, sizeof(cubeb_stream));
|
||||
|
||||
assert(stm);
|
||||
@ -680,7 +700,7 @@ wasapi_stream_init(cubeb * context, cubeb_stream ** stream,
|
||||
* that is, the samples not consumed by the resampler that we will end up
|
||||
* using next time the render callback is called. */
|
||||
stm->leftover_frame_size = static_cast<uint32_t>(ceilf(1 / resampling_rate * 2) + 1);
|
||||
stm->leftover_frames_buffer = (float *)malloc(frame_to_bytes_before_upmix(stm, stm->leftover_frame_size));
|
||||
stm->leftover_frames_buffer = (float *)malloc(frame_to_bytes_before_mix(stm, stm->leftover_frame_size));
|
||||
|
||||
stm->refill_function = &refill_with_resampling;
|
||||
} else {
|
||||
@ -712,8 +732,8 @@ wasapi_stream_init(cubeb * context, cubeb_stream ** stream,
|
||||
|
||||
assert(stm->mix_params.channels >= 2);
|
||||
|
||||
if (stm->mix_params.channels != stm->stream_params.channels) {
|
||||
stm->upmix_buffer = (float *) malloc(frame_to_bytes_before_upmix(stm, stm->buffer_frame_count));
|
||||
if (should_upmix(stm) || should_downmix(stm)) {
|
||||
stm->mix_buffer = (float *) malloc(frame_to_bytes_before_mix(stm, stm->buffer_frame_count));
|
||||
}
|
||||
|
||||
/* If we are going to resample, we will end up needing a buffer
|
||||
@ -722,7 +742,7 @@ wasapi_stream_init(cubeb * context, cubeb_stream ** stream,
|
||||
* factor and the channel layout into account. */
|
||||
if (stm->resampler) {
|
||||
size_t frames_needed = static_cast<size_t>(frame_count_at_rate(stm->buffer_frame_count, resampling_rate));
|
||||
stm->resampling_src_buffer = (float *)malloc(frame_to_bytes_before_upmix(stm, frames_needed));
|
||||
stm->resampling_src_buffer = (float *)malloc(frame_to_bytes_before_mix(stm, frames_needed));
|
||||
}
|
||||
|
||||
hr = stm->client->SetEventHandle(stm->refill_event);
|
||||
@ -783,7 +803,7 @@ void wasapi_stream_destroy(cubeb_stream * stm)
|
||||
|
||||
free(stm->leftover_frames_buffer);
|
||||
free(stm->resampling_src_buffer);
|
||||
free(stm->upmix_buffer);
|
||||
free(stm->mix_buffer);
|
||||
free(stm);
|
||||
CoUninitialize();
|
||||
}
|
||||
|
@ -57,12 +57,28 @@ struct VideoCodecConfig
|
||||
int mType;
|
||||
std::string mName;
|
||||
uint32_t mRtcpFbTypes;
|
||||
unsigned int mMaxFrameSize;
|
||||
unsigned int mMaxFrameRate;
|
||||
|
||||
VideoCodecConfig(int type,
|
||||
std::string name,
|
||||
int rtcpFbTypes): mType(type),
|
||||
mName(name),
|
||||
mRtcpFbTypes(rtcpFbTypes)
|
||||
mRtcpFbTypes(rtcpFbTypes),
|
||||
mMaxFrameSize(0),
|
||||
mMaxFrameRate(0)
|
||||
{
|
||||
}
|
||||
|
||||
VideoCodecConfig(int type,
|
||||
std::string name,
|
||||
int rtcpFbTypes,
|
||||
unsigned int max_fs,
|
||||
unsigned int max_fr): mType(type),
|
||||
mName(name),
|
||||
mRtcpFbTypes(rtcpFbTypes),
|
||||
mMaxFrameSize(max_fs),
|
||||
mMaxFrameRate(max_fr)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -220,6 +220,18 @@ public:
|
||||
const std::vector<VideoCodecConfig* >& recvCodecConfigList) = 0;
|
||||
|
||||
|
||||
/**
|
||||
* These methods allow unit tests to double-check that the
|
||||
* max-fs and max-fr related settings are as expected.
|
||||
*/
|
||||
virtual unsigned short SendingWidth() = 0;
|
||||
|
||||
virtual unsigned short SendingHeight() = 0;
|
||||
|
||||
virtual unsigned int SendingMaxFs() = 0;
|
||||
|
||||
virtual unsigned int SendingMaxFr() = 0;
|
||||
|
||||
/**
|
||||
* These methods allow unit tests to double-check that the
|
||||
* rtcp-fb settings are as expected.
|
||||
|
@ -16,6 +16,9 @@
|
||||
#include "AndroidJNIWrapper.h"
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
#include <math.h>
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
static const char* logTag ="WebrtcVideoSessionConduit";
|
||||
@ -616,6 +619,71 @@ WebrtcVideoConduit::SelectSendResolution(unsigned short width,
|
||||
{
|
||||
// XXX This will do bandwidth-resolution adaptation as well - bug 877954
|
||||
|
||||
// Limit resolution to max-fs while keeping same aspect ratio as the
|
||||
// incoming image.
|
||||
if (mCurSendCodecConfig && mCurSendCodecConfig->mMaxFrameSize)
|
||||
{
|
||||
unsigned int cur_fs, max_width, max_height, mb_width, mb_height, mb_max;
|
||||
|
||||
mb_width = (width + 15) >> 4;
|
||||
mb_height = (height + 15) >> 4;
|
||||
|
||||
cur_fs = mb_width * mb_height;
|
||||
|
||||
// Limit resolution to max_fs, but don't scale up.
|
||||
if (cur_fs > mCurSendCodecConfig->mMaxFrameSize)
|
||||
{
|
||||
double scale_ratio;
|
||||
|
||||
scale_ratio = sqrt((double) mCurSendCodecConfig->mMaxFrameSize /
|
||||
(double) cur_fs);
|
||||
|
||||
mb_width = mb_width * scale_ratio;
|
||||
mb_height = mb_height * scale_ratio;
|
||||
|
||||
// Adjust mb_width and mb_height if they were truncated to zero.
|
||||
if (mb_width == 0) {
|
||||
mb_width = 1;
|
||||
mb_height = std::min(mb_height, mCurSendCodecConfig->mMaxFrameSize);
|
||||
}
|
||||
if (mb_height == 0) {
|
||||
mb_height = 1;
|
||||
mb_width = std::min(mb_width, mCurSendCodecConfig->mMaxFrameSize);
|
||||
}
|
||||
}
|
||||
|
||||
// Limit width/height seperately to limit effect of extreme aspect ratios.
|
||||
mb_max = (unsigned) sqrt(8 * (double) mCurSendCodecConfig->mMaxFrameSize);
|
||||
|
||||
max_width = 16 * std::min(mb_width, mb_max);
|
||||
max_height = 16 * std::min(mb_height, mb_max);
|
||||
|
||||
if (width * max_height > max_width * height)
|
||||
{
|
||||
if (width > max_width)
|
||||
{
|
||||
// Due to the value is truncated to integer here and forced to even
|
||||
// value later, adding 1 to improve accuracy.
|
||||
height = max_width * height / width + 1;
|
||||
width = max_width;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (height > max_height)
|
||||
{
|
||||
// Due to the value is truncated to integer here and forced to even
|
||||
// value later, adding 1 to improve accuracy.
|
||||
width = max_height * width / height + 1;
|
||||
height = max_height;
|
||||
}
|
||||
}
|
||||
|
||||
// Favor even multiples of pixels for width and height.
|
||||
width = std::max(width & ~1, 2);
|
||||
height = std::max(height & ~1, 2);
|
||||
}
|
||||
|
||||
// Adapt to getUserMedia resolution changes
|
||||
// check if we need to reconfigure the sending resolution
|
||||
if (mSendingWidth != width || mSendingHeight != height)
|
||||
@ -850,6 +918,10 @@ WebrtcVideoConduit::CodecConfigToWebRTCCodec(const VideoCodecConfig* codecInfo,
|
||||
{
|
||||
cinst.plType = codecInfo->mType;
|
||||
// leave width/height alone; they'll be overridden on the first frame
|
||||
if (codecInfo->mMaxFrameRate > 0)
|
||||
{
|
||||
cinst.maxFramerate = codecInfo->mMaxFrameRate;
|
||||
}
|
||||
cinst.minBitrate = 200;
|
||||
cinst.startBitrate = 300;
|
||||
cinst.maxBitrate = 2000;
|
||||
@ -892,8 +964,10 @@ WebrtcVideoConduit::CheckCodecsForMatch(const VideoCodecConfig* curCodecConfig,
|
||||
return false;
|
||||
}
|
||||
|
||||
if(curCodecConfig->mType == codecInfo->mType &&
|
||||
curCodecConfig->mName.compare(codecInfo->mName) == 0)
|
||||
if(curCodecConfig->mType == codecInfo->mType &&
|
||||
curCodecConfig->mName.compare(codecInfo->mName) == 0 &&
|
||||
curCodecConfig->mMaxFrameSize == codecInfo->mMaxFrameSize &&
|
||||
curCodecConfig->mMaxFrameRate == codecInfo->mMaxFrameRate)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@ -947,6 +1021,8 @@ WebrtcVideoConduit::DumpCodecDB() const
|
||||
{
|
||||
CSFLogDebug(logTag,"Payload Name: %s", mRecvCodecList[i]->mName.c_str());
|
||||
CSFLogDebug(logTag,"Payload Type: %d", mRecvCodecList[i]->mType);
|
||||
CSFLogDebug(logTag,"Payload Max Frame Size: %d", mRecvCodecList[i]->mMaxFrameSize);
|
||||
CSFLogDebug(logTag,"Payload Max Frame Rate: %d", mRecvCodecList[i]->mMaxFrameRate);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -152,6 +152,27 @@ public:
|
||||
|
||||
virtual int DeliverFrame(unsigned char*,int, uint32_t , int64_t);
|
||||
|
||||
unsigned short SendingWidth() {
|
||||
return mSendingWidth;
|
||||
}
|
||||
|
||||
unsigned short SendingHeight() {
|
||||
return mSendingHeight;
|
||||
}
|
||||
|
||||
unsigned int SendingMaxFs() {
|
||||
if(mCurSendCodecConfig) {
|
||||
return mCurSendCodecConfig->mMaxFrameSize;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned int SendingMaxFr() {
|
||||
if(mCurSendCodecConfig) {
|
||||
return mCurSendCodecConfig->mMaxFrameRate;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
WebrtcVideoConduit():
|
||||
mVideoEngine(nullptr),
|
||||
|
@ -24,6 +24,10 @@
|
||||
#include "cpr_stdlib.h"
|
||||
#include "cpr_string.h"
|
||||
#include "mozilla/SyncRunnable.h"
|
||||
#include "mozilla/Services.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "nsIPrefService.h"
|
||||
#include "nsIPrefBranch.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
@ -70,6 +74,7 @@ int VcmSIPCCBinding::gAudioCodecMask = 0;
|
||||
int VcmSIPCCBinding::gVideoCodecMask = 0;
|
||||
nsIThread *VcmSIPCCBinding::gMainThread = NULL;
|
||||
nsIEventTarget *VcmSIPCCBinding::gSTSThread = NULL;
|
||||
nsCOMPtr<nsIPrefBranch> VcmSIPCCBinding::gBranch = NULL;
|
||||
|
||||
static mozilla::RefPtr<TransportFlow> vcmCreateTransportFlow(
|
||||
sipcc::PeerConnectionImpl *pc,
|
||||
@ -102,6 +107,12 @@ VcmSIPCCBinding::VcmSIPCCBinding ()
|
||||
{
|
||||
delete gSelf;//delete is NULL safe, so I don't need to check if it's NULL
|
||||
gSelf = this;
|
||||
nsresult rv;
|
||||
|
||||
nsCOMPtr<nsIPrefService> prefs = do_GetService("@mozilla.org/preferences-service;1", &rv);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
gBranch = do_QueryInterface(prefs);
|
||||
}
|
||||
}
|
||||
|
||||
class VcmIceOpaque : public NrIceOpaque {
|
||||
@ -133,6 +144,8 @@ VcmSIPCCBinding::~VcmSIPCCBinding ()
|
||||
gSTSThread,
|
||||
WrapRunnable(this, &VcmSIPCCBinding::disconnect_all),
|
||||
true);
|
||||
|
||||
gBranch = NULL;
|
||||
}
|
||||
|
||||
void VcmSIPCCBinding::CandidateReady(NrIceMediaStream* stream,
|
||||
@ -235,6 +248,11 @@ void VcmSIPCCBinding::connectCandidateSignal(
|
||||
&VcmSIPCCBinding::CandidateReady);
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIPrefBranch> VcmSIPCCBinding::getPrefBranch()
|
||||
{
|
||||
return gBranch;
|
||||
}
|
||||
|
||||
/* static */
|
||||
AudioTermination * VcmSIPCCBinding::getAudioTermination()
|
||||
{
|
||||
@ -2249,7 +2267,9 @@ static int vcmTxStartICE_m(cc_mcapid_t mcap_id,
|
||||
config_raw = new mozilla::VideoCodecConfig(
|
||||
payload->remote_rtp_pt,
|
||||
ccsdpCodecName(payload->codec_type),
|
||||
payload->video.rtcp_fb_types);
|
||||
payload->video.rtcp_fb_types,
|
||||
payload->video.max_fs,
|
||||
payload->video.max_fr);
|
||||
|
||||
// Take possession of this pointer
|
||||
mozilla::ScopedDeletePtr<mozilla::VideoCodecConfig> config(config_raw);
|
||||
@ -3053,3 +3073,47 @@ int vcmDisableRtcpComponent(const char *peerconnection, int level) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
static short vcmGetVideoMaxFs_m(uint16_t codec,
|
||||
int32_t *max_fs) {
|
||||
nsCOMPtr<nsIPrefBranch> branch = VcmSIPCCBinding::getPrefBranch();
|
||||
if (branch && NS_SUCCEEDED(branch->GetIntPref("media.navigator.video.max_fs",
|
||||
max_fs))) {
|
||||
return 0;
|
||||
}
|
||||
return VCM_ERROR;
|
||||
}
|
||||
|
||||
short vcmGetVideoMaxFs(uint16_t codec,
|
||||
int32_t *max_fs) {
|
||||
short ret;
|
||||
|
||||
mozilla::SyncRunnable::DispatchToThread(VcmSIPCCBinding::getMainThread(),
|
||||
WrapRunnableNMRet(&vcmGetVideoMaxFs_m,
|
||||
codec,
|
||||
max_fs,
|
||||
&ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
static short vcmGetVideoMaxFr_m(uint16_t codec,
|
||||
int32_t *max_fr) {
|
||||
nsCOMPtr<nsIPrefBranch> branch = VcmSIPCCBinding::getPrefBranch();
|
||||
if (branch && NS_SUCCEEDED(branch->GetIntPref("media.navigator.video.max_fr",
|
||||
max_fr))) {
|
||||
return 0;
|
||||
}
|
||||
return VCM_ERROR;
|
||||
}
|
||||
|
||||
short vcmGetVideoMaxFr(uint16_t codec,
|
||||
int32_t *max_fr) {
|
||||
short ret;
|
||||
|
||||
mozilla::SyncRunnable::DispatchToThread(VcmSIPCCBinding::getMainThread(),
|
||||
WrapRunnableNMRet(&vcmGetVideoMaxFr_m,
|
||||
codec,
|
||||
max_fr,
|
||||
&ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -14,6 +14,7 @@ extern "C"
|
||||
|
||||
class nsIThread;
|
||||
class nsIEventTarget;
|
||||
class nsIPrefBranch;
|
||||
|
||||
namespace mozilla {
|
||||
class NrIceMediaStream;
|
||||
@ -69,6 +70,8 @@ namespace CSF
|
||||
|
||||
static void connectCandidateSignal(mozilla::NrIceMediaStream* stream);
|
||||
|
||||
static nsCOMPtr<nsIPrefBranch> getPrefBranch();
|
||||
|
||||
private:
|
||||
void CandidateReady(mozilla::NrIceMediaStream* stream,
|
||||
const std::string& candidate);
|
||||
@ -80,6 +83,7 @@ namespace CSF
|
||||
static int gVideoCodecMask;
|
||||
static nsIThread *gMainThread;
|
||||
static nsIEventTarget *gSTSThread;
|
||||
static nsCOMPtr<nsIPrefBranch> gBranch;
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -571,6 +571,28 @@ sip_config_local_supported_codecs_get (rtp_ptype aSupportedCodecs[],
|
||||
return count;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
config_get_video_max_fs(const rtp_ptype codec)
|
||||
{
|
||||
uint32_t max_fs;
|
||||
|
||||
if(vcmGetVideoMaxFs(codec, (int32_t *) &max_fs) == 0) {
|
||||
return max_fs;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
config_get_video_max_fr(const rtp_ptype codec)
|
||||
{
|
||||
uint32_t max_fr;
|
||||
|
||||
if(vcmGetVideoMaxFr(codec, (int32_t *) &max_fr) == 0) {
|
||||
return max_fr;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* sip_config_local_supported_codecs_get()
|
||||
*
|
||||
|
@ -292,5 +292,7 @@ int sip_minimum_config_check(void);
|
||||
void config_set_codec_table(int codec_mask);
|
||||
int sip_config_get_keepalive_expires();
|
||||
rtp_ptype sip_config_preferred_codec(void);
|
||||
uint32_t config_get_video_max_fs(const rtp_ptype codec);
|
||||
uint32_t config_get_video_max_fr(const rtp_ptype codec);
|
||||
|
||||
#endif /* PROT_CONFIGMGR_H_ */
|
||||
|
@ -1146,6 +1146,8 @@ gsmsdp_set_video_media_attributes (uint32_t media_type, void *cc_sdp_p, uint16_t
|
||||
{
|
||||
uint16_t a_inst;
|
||||
void *sdp_p = ((cc_sdp_t*)cc_sdp_p)->src_sdp;
|
||||
int max_fs = 0;
|
||||
int max_fr = 0;
|
||||
|
||||
switch (media_type) {
|
||||
case RTP_H263:
|
||||
@ -1182,6 +1184,31 @@ gsmsdp_set_video_media_attributes (uint32_t media_type, void *cc_sdp_p, uint16_t
|
||||
SIPSDP_ATTR_ENCNAME_VP8);
|
||||
(void) sdp_attr_set_rtpmap_clockrate(sdp_p, level, 0, a_inst,
|
||||
RTPMAP_VIDEO_CLOCKRATE);
|
||||
|
||||
max_fs = config_get_video_max_fs((rtp_ptype) media_type);
|
||||
max_fr = config_get_video_max_fr((rtp_ptype) media_type);
|
||||
|
||||
if (max_fs || max_fr) {
|
||||
if (sdp_add_new_attr(sdp_p, level, 0, SDP_ATTR_FMTP, &a_inst)
|
||||
!= SDP_SUCCESS) {
|
||||
GSM_ERR_MSG("Failed to add attribute");
|
||||
return;
|
||||
}
|
||||
|
||||
(void) sdp_attr_set_fmtp_payload_type(sdp_p, level, 0, a_inst,
|
||||
payload_number);
|
||||
|
||||
if (max_fs) {
|
||||
(void) sdp_attr_set_fmtp_max_fs(sdp_p, level, 0, a_inst,
|
||||
max_fs);
|
||||
}
|
||||
|
||||
if (max_fr) {
|
||||
(void) sdp_attr_set_fmtp_max_fr(sdp_p, level, 0, a_inst,
|
||||
max_fr);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
GSM_DEBUG("gsmsdp_set_video_media_attributes- populate attribs %d", payload_number );
|
||||
@ -3416,23 +3443,19 @@ gsmsdp_negotiate_codec (fsmdef_dcb_t *dcb_p, cc_sdp_t *sdp_p,
|
||||
|
||||
/* This should ultimately use RFC 6236 a=imageattr
|
||||
if present */
|
||||
switch (codec) {
|
||||
case RTP_VP8:
|
||||
payload_info->video.width = 640;
|
||||
payload_info->video.height = 480;
|
||||
break;
|
||||
case RTP_I420:
|
||||
payload_info->video.width = 176;
|
||||
payload_info->video.height = 144;
|
||||
break;
|
||||
default:
|
||||
GSM_DEBUG(DEB_L_C_F_PREFIX"codec=%d not setting "
|
||||
"codec parameters (not implemented)\n",
|
||||
DEB_L_C_F_PREFIX_ARGS(GSM, dcb_p->line,
|
||||
dcb_p->call_id, fname), codec);
|
||||
payload_info->video.width = -1;
|
||||
payload_info->video.height = -1;
|
||||
}
|
||||
|
||||
payload_info->video.width = 0;
|
||||
payload_info->video.height = 0;
|
||||
|
||||
/* Set maximum frame size */
|
||||
payload_info->video.max_fs = 0;
|
||||
sdp_attr_get_fmtp_max_fs(sdp_p->dest_sdp, level, 0, 1,
|
||||
&payload_info->video.max_fs);
|
||||
|
||||
/* Set maximum frame rate */
|
||||
payload_info->video.max_fr = 0;
|
||||
sdp_attr_get_fmtp_max_fr(sdp_p->dest_sdp, level, 0, 1,
|
||||
&payload_info->video.max_fr);
|
||||
} /* end video */
|
||||
|
||||
GSM_DEBUG(DEB_L_C_F_PREFIX"codec= %d",
|
||||
|
@ -405,6 +405,7 @@ typedef enum {
|
||||
SDP_USE_IN_BAND_FEC,
|
||||
SDP_MAX_CODED_AUDIO_BW,
|
||||
SDP_CBR,
|
||||
SDP_MAX_FR,
|
||||
SDP_MAX_FMTP_PARAM,
|
||||
SDP_FMTP_PARAM_UNKNOWN
|
||||
} sdp_fmtp_codec_param_e;
|
||||
@ -680,6 +681,7 @@ typedef struct sdp_fmtp {
|
||||
|
||||
u32 max_mbps;
|
||||
u32 max_fs;
|
||||
u32 max_fr;
|
||||
u32 max_cpb;
|
||||
u32 max_dpb;
|
||||
u32 max_br;
|
||||
@ -1518,6 +1520,12 @@ extern sdp_result_e sdp_attr_set_fmtp_max_fs (void *sdp_ptr,
|
||||
u16 inst_num,
|
||||
u32 max_fs);
|
||||
|
||||
extern sdp_result_e sdp_attr_set_fmtp_max_fr (void *sdp_ptr,
|
||||
u16 level,
|
||||
u8 cap_num,
|
||||
u16 inst_num,
|
||||
u32 max_fr);
|
||||
|
||||
extern sdp_result_e sdp_attr_set_fmtp_max_cpb (void *sdp_ptr,
|
||||
u16 level,
|
||||
u8 cap_num,
|
||||
@ -1694,6 +1702,8 @@ extern sdp_result_e sdp_attr_get_fmtp_max_mbps (void *sdp_ptr, u16 level,
|
||||
u8 cap_num, u16 inst_num, u32 *val);
|
||||
extern sdp_result_e sdp_attr_get_fmtp_max_fs (void *sdp_ptr, u16 level,
|
||||
u8 cap_num, u16 inst_num, u32 *val);
|
||||
extern sdp_result_e sdp_attr_get_fmtp_max_fr (void *sdp_ptr, u16 level,
|
||||
u8 cap_num, u16 inst_num, u32 *val);
|
||||
extern sdp_result_e sdp_attr_get_fmtp_max_cpb (void *sdp_ptr, u16 level,
|
||||
u8 cap_num, u16 inst_num, u32 *val);
|
||||
extern sdp_result_e sdp_attr_get_fmtp_max_dpb (void *sdp_ptr, u16 level,
|
||||
|
@ -1222,7 +1222,7 @@ sdp_result_e sdp_parse_attr_fmtp (sdp_t *sdp_p, sdp_attr_t *attr_p,
|
||||
if (result1 != SDP_SUCCESS) {
|
||||
fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), " \t", &result1);
|
||||
if (result1 != SDP_SUCCESS) {
|
||||
sdp_attr_fmtp_no_value(sdp_p, "max_fs");
|
||||
sdp_attr_fmtp_no_value(sdp_p, "max-fs");
|
||||
SDP_FREE(temp_ptr);
|
||||
return SDP_INVALID_PARAMETER;
|
||||
}
|
||||
@ -1234,7 +1234,7 @@ sdp_result_e sdp_parse_attr_fmtp (sdp_t *sdp_p, sdp_attr_t *attr_p,
|
||||
strtoul_result = strtoul(tok, &strtoul_end, 10);
|
||||
|
||||
if (errno || tok == strtoul_end || strtoul_result == 0 || strtoul_result > UINT_MAX) {
|
||||
sdp_attr_fmtp_invalid_value(sdp_p, "max_fs", tok);
|
||||
sdp_attr_fmtp_invalid_value(sdp_p, "max-fs", tok);
|
||||
SDP_FREE(temp_ptr);
|
||||
return SDP_INVALID_PARAMETER;
|
||||
}
|
||||
@ -1692,7 +1692,32 @@ sdp_result_e sdp_parse_attr_fmtp (sdp_t *sdp_p, sdp_attr_t *attr_p,
|
||||
fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
|
||||
fmtp_p->cbr = (u16) strtoul_result;
|
||||
codec_info_found = TRUE;
|
||||
|
||||
} else if (cpr_strncasecmp(tmp,sdp_fmtp_codec_param[49].name,
|
||||
sdp_fmtp_codec_param[49].strlen) == 0) {
|
||||
fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), "; \t",
|
||||
&result1);
|
||||
if (result1 != SDP_SUCCESS) {
|
||||
fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp),
|
||||
" \t", &result1);
|
||||
if (result1 != SDP_SUCCESS) {
|
||||
sdp_attr_fmtp_no_value(sdp_p, "max-fr");
|
||||
SDP_FREE(temp_ptr);
|
||||
return SDP_INVALID_PARAMETER;
|
||||
}
|
||||
}
|
||||
tok = tmp;
|
||||
tok++;
|
||||
errno = 0;
|
||||
strtoul_result = strtoul(tok, &strtoul_end, 10);
|
||||
if (errno || tok == strtoul_end || strtoul_result == 0 ||
|
||||
strtoul_result > UINT_MAX) {
|
||||
sdp_attr_fmtp_invalid_value(sdp_p, "max-fr", tok);
|
||||
SDP_FREE(temp_ptr);
|
||||
return SDP_INVALID_PARAMETER;
|
||||
}
|
||||
fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
|
||||
fmtp_p->max_fr = (u32) strtoul_result;
|
||||
codec_info_found = TRUE;
|
||||
} else if (fmtp_ptr != NULL && *fmtp_ptr == '\n') {
|
||||
temp=PL_strtok_r(tmp, ";", &strtok_state);
|
||||
if (temp) {
|
||||
@ -1999,6 +2024,8 @@ sdp_result_e sdp_build_attr_fmtp (sdp_t *sdp_p, sdp_attr_t *attr_p, flex_string
|
||||
|
||||
FMTP_BUILD_UNSIGNED(fmtp_p->max_fs > 0, "max-fs", fmtp_p->max_fs)
|
||||
|
||||
FMTP_BUILD_UNSIGNED(fmtp_p->max_fr > 0, "max-fr", fmtp_p->max_fr)
|
||||
|
||||
FMTP_BUILD_UNSIGNED(fmtp_p->max_cpb > 0, "max-cpb", fmtp_p->max_cpb)
|
||||
|
||||
FMTP_BUILD_UNSIGNED(fmtp_p->max_dpb > 0, "max-dpb", fmtp_p->max_dpb)
|
||||
|
@ -457,6 +457,7 @@ void sdp_copy_attr_fields (sdp_attr_t *src_attr_p, sdp_attr_t *dst_attr_p)
|
||||
|
||||
dst_attr_p->attr.fmtp.max_mbps = src_attr_p->attr.fmtp.max_mbps;
|
||||
dst_attr_p->attr.fmtp.max_fs = src_attr_p->attr.fmtp.max_fs;
|
||||
dst_attr_p->attr.fmtp.max_fr = src_attr_p->attr.fmtp.max_fr;
|
||||
dst_attr_p->attr.fmtp.max_cpb = src_attr_p->attr.fmtp.max_cpb;
|
||||
dst_attr_p->attr.fmtp.max_dpb = src_attr_p->attr.fmtp.max_dpb;
|
||||
dst_attr_p->attr.fmtp.max_br = src_attr_p->attr.fmtp.max_br;
|
||||
@ -862,6 +863,7 @@ sdp_result_e sdp_copy_attr (void *src_sdp_ptr, void *dst_sdp_ptr,
|
||||
|
||||
new_attr_p->attr.fmtp.max_mbps = src_attr_p->attr.fmtp.max_mbps;
|
||||
new_attr_p->attr.fmtp.max_fs = src_attr_p->attr.fmtp.max_fs;
|
||||
new_attr_p->attr.fmtp.max_fr = src_attr_p->attr.fmtp.max_fr;
|
||||
new_attr_p->attr.fmtp.max_cpb = src_attr_p->attr.fmtp.max_cpb;
|
||||
new_attr_p->attr.fmtp.max_dpb = src_attr_p->attr.fmtp.max_dpb;
|
||||
new_attr_p->attr.fmtp.max_br = src_attr_p->attr.fmtp.max_br;
|
||||
@ -6361,6 +6363,39 @@ sdp_result_e sdp_attr_set_fmtp_max_fs (void *sdp_ptr, u16 level,
|
||||
}
|
||||
}
|
||||
|
||||
sdp_result_e sdp_attr_set_fmtp_max_fr (void *sdp_ptr, u16 level,
|
||||
u8 cap_num, u16 inst_num,
|
||||
u32 max_fr)
|
||||
{
|
||||
sdp_t *sdp_p = (sdp_t *)sdp_ptr;
|
||||
sdp_attr_t *attr_p;
|
||||
sdp_fmtp_t *fmtp_p;
|
||||
|
||||
if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
|
||||
return (SDP_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP, inst_num);
|
||||
if (attr_p == NULL) {
|
||||
if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
|
||||
CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
|
||||
"not found.", sdp_p->debug_str, level, inst_num);
|
||||
}
|
||||
sdp_p->conf_p->num_invalid_param++;
|
||||
return (SDP_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
fmtp_p = &(attr_p->attr.fmtp);
|
||||
fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
|
||||
|
||||
if (max_fr > 0) {
|
||||
fmtp_p->max_fr = max_fr;
|
||||
return (SDP_SUCCESS);
|
||||
} else {
|
||||
return (SDP_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
sdp_result_e sdp_attr_set_fmtp_max_br (void *sdp_ptr, u16 level,
|
||||
u8 cap_num, u16 inst_num,
|
||||
u32 max_br)
|
||||
@ -8265,6 +8300,41 @@ sdp_result_e sdp_attr_get_fmtp_max_fs (void *sdp_ptr, u16 level,
|
||||
}
|
||||
}
|
||||
|
||||
/* Function: sdp_attr_get_fmtp_max_fr
|
||||
* Description: Gets the value of the fmtp attribute- max-fr parameter
|
||||
* Parameters: sdp_ptr The SDP handle returned by sdp_init_description.
|
||||
* level The level to check for the attribute.
|
||||
* cap_num The capability number associated with the
|
||||
* attribute if any. If none, should be zero.
|
||||
* inst_num The attribute instance number to check.
|
||||
* Returns: max-fr value.
|
||||
*/
|
||||
|
||||
sdp_result_e sdp_attr_get_fmtp_max_fr (void *sdp_ptr, u16 level,
|
||||
u8 cap_num, u16 inst_num, u32 *val)
|
||||
{
|
||||
sdp_t *sdp_p = (sdp_t *)sdp_ptr;
|
||||
sdp_attr_t *attr_p;
|
||||
|
||||
if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
|
||||
return (SDP_INVALID_SDP_PTR);
|
||||
}
|
||||
|
||||
attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP,
|
||||
inst_num);
|
||||
if (attr_p == NULL) {
|
||||
if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
|
||||
CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
|
||||
"not found.", sdp_p->debug_str, level, inst_num);
|
||||
}
|
||||
sdp_p->conf_p->num_invalid_param++;
|
||||
return (SDP_INVALID_PARAMETER);
|
||||
} else {
|
||||
*val = attr_p->attr.fmtp.max_fr;
|
||||
return (SDP_SUCCESS);
|
||||
}
|
||||
}
|
||||
|
||||
/* Function: sdp_attr_get_fmtp_max_cpb
|
||||
* Description: Gets the value of the fmtp attribute- max-cpb parameter for H.264 codec
|
||||
* Parameters: sdp_ptr The SDP handle returned by sdp_init_description.
|
||||
|
@ -400,7 +400,8 @@ const sdp_namearray_t sdp_fmtp_codec_param[SDP_MAX_FMTP_PARAM] =
|
||||
{"stereo", sizeof("stereo")}, /* 45 */
|
||||
{"useinbandfec", sizeof("useinbandfec")}, /* 46 */
|
||||
{"maxcodedaudiobandwidth", sizeof("maxcodedaudiobandwidth")}, /* 47 */
|
||||
{"cbr", sizeof("cbr")} /* 48 */
|
||||
{"cbr", sizeof("cbr")}, /* 48 */
|
||||
{"max-fr", sizeof("max-fr")} /* 49 */
|
||||
} ;
|
||||
|
||||
/* Note: These *must* be in the same order as the enum type. */
|
||||
|
@ -186,6 +186,8 @@ typedef struct
|
||||
int width;
|
||||
int height;
|
||||
uint32_t rtcp_fb_types;
|
||||
uint32_t max_fs; /* Max frame size */
|
||||
uint32_t max_fr; /* Max frame rate */
|
||||
} video;
|
||||
};
|
||||
|
||||
@ -1047,6 +1049,10 @@ int vcmOnSdpParseError(const char *peercconnection, const char *message);
|
||||
*/
|
||||
int vcmDisableRtcpComponent(const char *peerconnection, int level);
|
||||
|
||||
short vcmGetVideoMaxFs(uint16_t codec, int32_t *max_fs);
|
||||
|
||||
short vcmGetVideoMaxFr(uint16_t codec, int32_t *max_fs);
|
||||
|
||||
//Using C++ for gips. This is the end of extern "C" above.
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include <fstream>
|
||||
#include <unistd.h>
|
||||
#include <vector>
|
||||
#include <math.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
@ -709,6 +710,152 @@ class TransportConduitTest : public ::testing::Test
|
||||
|
||||
}
|
||||
|
||||
void DumpMaxFs(int orig_width, int orig_height, int max_fs,
|
||||
int new_width, int new_height)
|
||||
{
|
||||
cerr << "Applying max_fs=" << max_fs << " to input resolution " <<
|
||||
orig_width << "x" << orig_height << endl;
|
||||
cerr << "New resolution: " << new_width << "x" << new_height << endl;
|
||||
cerr << endl;
|
||||
}
|
||||
|
||||
// Calculate new resolution for sending video by applying max-fs constraint.
|
||||
void GetVideoResolutionWithMaxFs(int orig_width, int orig_height, int max_fs,
|
||||
int *new_width, int *new_height)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
// Get pointer to VideoSessionConduit.
|
||||
mVideoSession = mozilla::VideoSessionConduit::Create();
|
||||
if( !mVideoSession )
|
||||
ASSERT_NE(mVideoSession, (void*)NULL);
|
||||
|
||||
// Configure send codecs on the conduit.
|
||||
mozilla::VideoCodecConfig cinst1(120, "VP8", 0, max_fs, 0);
|
||||
|
||||
err = mVideoSession->ConfigureSendMediaCodec(&cinst1);
|
||||
ASSERT_EQ(mozilla::kMediaConduitNoError, err);
|
||||
|
||||
// Send one frame.
|
||||
MOZ_ASSERT(!(orig_width & 1));
|
||||
MOZ_ASSERT(!(orig_height & 1));
|
||||
int len = ((orig_width * orig_height) * 3 / 2);
|
||||
uint8_t* frame = (uint8_t*) PR_MALLOC(len);
|
||||
|
||||
memset(frame, COLOR, len);
|
||||
mVideoSession->SendVideoFrame((unsigned char*)frame,
|
||||
len,
|
||||
orig_width,
|
||||
orig_height,
|
||||
mozilla::kVideoI420,
|
||||
0);
|
||||
PR_Free(frame);
|
||||
|
||||
// Get the new resolution as adjusted by the max-fs constraint.
|
||||
*new_width = mVideoSession->SendingWidth();
|
||||
*new_height = mVideoSession->SendingHeight();
|
||||
}
|
||||
|
||||
void TestVideoConduitMaxFs()
|
||||
{
|
||||
int orig_width, orig_height, width, height, max_fs;
|
||||
|
||||
// No limitation.
|
||||
cerr << "Test no max-fs limition" << endl;
|
||||
orig_width = 640;
|
||||
orig_height = 480;
|
||||
max_fs = 0;
|
||||
GetVideoResolutionWithMaxFs(orig_width, orig_height, max_fs, &width, &height);
|
||||
DumpMaxFs(orig_width, orig_height, max_fs, width, height);
|
||||
ASSERT_EQ(width, 640);
|
||||
ASSERT_EQ(height, 480);
|
||||
|
||||
// VGA to QVGA.
|
||||
cerr << "Test resizing from VGA to QVGA" << endl;
|
||||
orig_width = 640;
|
||||
orig_height = 480;
|
||||
max_fs = 300;
|
||||
GetVideoResolutionWithMaxFs(orig_width, orig_height, max_fs, &width, &height);
|
||||
DumpMaxFs(orig_width, orig_height, max_fs, width, height);
|
||||
ASSERT_EQ(width, 320);
|
||||
ASSERT_EQ(height, 240);
|
||||
|
||||
// Extreme input resolution.
|
||||
cerr << "Test extreme input resolution" << endl;
|
||||
orig_width = 3072;
|
||||
orig_height = 100;
|
||||
max_fs = 300;
|
||||
GetVideoResolutionWithMaxFs(orig_width, orig_height, max_fs, &width, &height);
|
||||
DumpMaxFs(orig_width, orig_height, max_fs, width, height);
|
||||
ASSERT_EQ(width, 768);
|
||||
ASSERT_EQ(height, 26);
|
||||
|
||||
// Small max-fs.
|
||||
cerr << "Test small max-fs (case 1)" << endl;
|
||||
orig_width = 8;
|
||||
orig_height = 32;
|
||||
max_fs = 1;
|
||||
GetVideoResolutionWithMaxFs(orig_width, orig_height, max_fs, &width, &height);
|
||||
DumpMaxFs(orig_width, orig_height, max_fs, width, height);
|
||||
ASSERT_EQ(width, 4);
|
||||
ASSERT_EQ(height, 16);
|
||||
|
||||
// Small max-fs.
|
||||
cerr << "Test small max-fs (case 2)" << endl;
|
||||
orig_width = 4;
|
||||
orig_height = 50;
|
||||
max_fs = 1;
|
||||
GetVideoResolutionWithMaxFs(orig_width, orig_height, max_fs, &width, &height);
|
||||
DumpMaxFs(orig_width, orig_height, max_fs, width, height);
|
||||
ASSERT_EQ(width, 2);
|
||||
ASSERT_EQ(height, 16);
|
||||
|
||||
// Small max-fs.
|
||||
cerr << "Test small max-fs (case 3)" << endl;
|
||||
orig_width = 872;
|
||||
orig_height = 136;
|
||||
max_fs = 3;
|
||||
GetVideoResolutionWithMaxFs(orig_width, orig_height, max_fs, &width, &height);
|
||||
DumpMaxFs(orig_width, orig_height, max_fs, width, height);
|
||||
ASSERT_EQ(width, 48);
|
||||
ASSERT_EQ(height, 8);
|
||||
|
||||
// Small max-fs.
|
||||
cerr << "Test small max-fs (case 4)" << endl;
|
||||
orig_width = 160;
|
||||
orig_height = 8;
|
||||
max_fs = 5;
|
||||
GetVideoResolutionWithMaxFs(orig_width, orig_height, max_fs, &width, &height);
|
||||
DumpMaxFs(orig_width, orig_height, max_fs, width, height);
|
||||
ASSERT_EQ(width, 80);
|
||||
ASSERT_EQ(height, 4);
|
||||
|
||||
// Random values.
|
||||
for (int i = 0; i < 30; i++) {
|
||||
max_fs = rand() % 1000;
|
||||
orig_width = ((rand() % 2000) & ~1) + 2;
|
||||
orig_height = ((rand() % 2000) & ~1) + 2;
|
||||
// Potential crash on small resolution, see bug 919979.
|
||||
if (orig_width * orig_height <= 20) {
|
||||
cerr << "Temporarily skip resolution " << orig_width << "x" <<
|
||||
orig_height << endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
GetVideoResolutionWithMaxFs(orig_width, orig_height, max_fs,
|
||||
&width, &height);
|
||||
if (max_fs > 0 &&
|
||||
ceil(width / 16.) * ceil(height / 16.) > max_fs) {
|
||||
DumpMaxFs(orig_width, orig_height, max_fs, width, height);
|
||||
ADD_FAILURE();
|
||||
}
|
||||
if ((width & 1) || (height & 1)) {
|
||||
DumpMaxFs(orig_width, orig_height, max_fs, width, height);
|
||||
ADD_FAILURE();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
//Audio Conduit Test Objects
|
||||
mozilla::RefPtr<mozilla::AudioSessionConduit> mAudioSession;
|
||||
@ -744,6 +891,10 @@ TEST_F(TransportConduitTest, TestVideoConduitCodecAPI) {
|
||||
TestVideoConduitCodecAPI();
|
||||
}
|
||||
|
||||
TEST_F(TransportConduitTest, TestVideoConduitMaxFs) {
|
||||
TestVideoConduitMaxFs();
|
||||
}
|
||||
|
||||
} // end namespace
|
||||
|
||||
int main(int argc, char **argv)
|
||||
|
@ -190,6 +190,41 @@ class SdpTest : public ::testing::Test {
|
||||
return inst_num;
|
||||
}
|
||||
|
||||
u16 AddNewFmtpMaxFs(int level, u32 max_fs) {
|
||||
u16 inst_num = 0;
|
||||
EXPECT_EQ(sdp_add_new_attr(sdp_ptr_, level, 0, SDP_ATTR_FMTP,
|
||||
&inst_num), SDP_SUCCESS);
|
||||
EXPECT_EQ(sdp_attr_set_fmtp_payload_type(sdp_ptr_, level, 0, inst_num,
|
||||
120), SDP_SUCCESS);
|
||||
EXPECT_EQ(sdp_attr_set_fmtp_max_fs(sdp_ptr_, level, 0, inst_num, max_fs),
|
||||
SDP_SUCCESS);
|
||||
return inst_num;
|
||||
}
|
||||
|
||||
u16 AddNewFmtpMaxFr(int level, u32 max_fr) {
|
||||
u16 inst_num = 0;
|
||||
EXPECT_EQ(sdp_add_new_attr(sdp_ptr_, level, 0, SDP_ATTR_FMTP,
|
||||
&inst_num), SDP_SUCCESS);
|
||||
EXPECT_EQ(sdp_attr_set_fmtp_payload_type(sdp_ptr_, level, 0, inst_num,
|
||||
120), SDP_SUCCESS);
|
||||
EXPECT_EQ(sdp_attr_set_fmtp_max_fr(sdp_ptr_, level, 0, inst_num, max_fr),
|
||||
SDP_SUCCESS);
|
||||
return inst_num;
|
||||
}
|
||||
|
||||
u16 AddNewFmtpMaxFsFr(int level, u32 max_fs, u32 max_fr) {
|
||||
u16 inst_num = 0;
|
||||
EXPECT_EQ(sdp_add_new_attr(sdp_ptr_, level, 0, SDP_ATTR_FMTP,
|
||||
&inst_num), SDP_SUCCESS);
|
||||
EXPECT_EQ(sdp_attr_set_fmtp_payload_type(sdp_ptr_, level, 0, inst_num,
|
||||
120), SDP_SUCCESS);
|
||||
EXPECT_EQ(sdp_attr_set_fmtp_max_fs(sdp_ptr_, level, 0, inst_num, max_fs),
|
||||
SDP_SUCCESS);
|
||||
EXPECT_EQ(sdp_attr_set_fmtp_max_fr(sdp_ptr_, level, 0, inst_num, max_fr),
|
||||
SDP_SUCCESS);
|
||||
return inst_num;
|
||||
}
|
||||
|
||||
protected:
|
||||
int final_level_;
|
||||
void *config_p_;
|
||||
@ -689,6 +724,45 @@ TEST_F(SdpTest, parseRtcpFbAllPayloads) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(SdpTest, parseFmtpMaxFs) {
|
||||
u32 val = 0;
|
||||
ParseSdp(kVideoSdp + "a=fmtp:120 max-fs=300;max-fr=30\r\n");
|
||||
ASSERT_EQ(sdp_attr_get_fmtp_max_fs(sdp_ptr_, 1, 0, 1, &val), SDP_SUCCESS);
|
||||
ASSERT_EQ(val, 300);
|
||||
}
|
||||
|
||||
TEST_F(SdpTest, parseFmtpMaxFr) {
|
||||
u32 val = 0;
|
||||
ParseSdp(kVideoSdp + "a=fmtp:120 max-fs=300;max-fr=30\r\n");
|
||||
ASSERT_EQ(sdp_attr_get_fmtp_max_fr(sdp_ptr_, 1, 0, 1, &val), SDP_SUCCESS);
|
||||
ASSERT_EQ(val, 30);
|
||||
}
|
||||
|
||||
TEST_F(SdpTest, addFmtpMaxFs) {
|
||||
InitLocalSdp();
|
||||
int level = AddNewMedia(SDP_MEDIA_VIDEO);
|
||||
AddNewFmtpMaxFs(level, 300);
|
||||
std::string body = SerializeSdp();
|
||||
ASSERT_NE(body.find("a=fmtp:120 max-fs=300\r\n"), std::string::npos);
|
||||
}
|
||||
|
||||
TEST_F(SdpTest, addFmtpMaxFr) {
|
||||
InitLocalSdp();
|
||||
int level = AddNewMedia(SDP_MEDIA_VIDEO);
|
||||
AddNewFmtpMaxFr(level, 30);
|
||||
std::string body = SerializeSdp();
|
||||
ASSERT_NE(body.find("a=fmtp:120 max-fr=30\r\n"), std::string::npos);
|
||||
}
|
||||
|
||||
TEST_F(SdpTest, addFmtpMaxFsFr) {
|
||||
InitLocalSdp();
|
||||
int level = AddNewMedia(SDP_MEDIA_VIDEO);
|
||||
AddNewFmtpMaxFsFr(level, 300, 30);
|
||||
std::string body = SerializeSdp();
|
||||
ASSERT_NE(body.find("a=fmtp:120 max-fs=300;max-fr=30\r\n"),
|
||||
std::string::npos);
|
||||
}
|
||||
|
||||
} // End namespace test.
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
|
@ -26,6 +26,9 @@
|
||||
#include "PeerConnectionCtx.h"
|
||||
#include "runnable_utils.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "mozilla/Services.h"
|
||||
#include "nsIPrefService.h"
|
||||
#include "nsIPrefBranch.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsIIOService.h"
|
||||
#include "nsIDNSService.h"
|
||||
@ -114,6 +117,7 @@ enum sdpTestFlags
|
||||
SHOULD_REJECT_AUDIO = (1<<3),
|
||||
SHOULD_OMIT_AUDIO = (1<<4),
|
||||
DONT_CHECK_AUDIO = (1<<5),
|
||||
SHOULD_CHECK_AUDIO = (1<<6),
|
||||
|
||||
SHOULD_SEND_VIDEO = (1<<8),
|
||||
SHOULD_RECV_VIDEO = (1<<9),
|
||||
@ -121,6 +125,7 @@ enum sdpTestFlags
|
||||
SHOULD_REJECT_VIDEO = (1<<11),
|
||||
SHOULD_OMIT_VIDEO = (1<<12),
|
||||
DONT_CHECK_VIDEO = (1<<13),
|
||||
SHOULD_CHECK_VIDEO = (1<<14),
|
||||
|
||||
SHOULD_INCLUDE_DATA = (1 << 16),
|
||||
DONT_CHECK_DATA = (1 << 17),
|
||||
@ -128,14 +133,17 @@ enum sdpTestFlags
|
||||
SHOULD_SENDRECV_AUDIO = SHOULD_SEND_AUDIO | SHOULD_RECV_AUDIO,
|
||||
SHOULD_SENDRECV_VIDEO = SHOULD_SEND_VIDEO | SHOULD_RECV_VIDEO,
|
||||
SHOULD_SENDRECV_AV = SHOULD_SENDRECV_AUDIO | SHOULD_SENDRECV_VIDEO,
|
||||
SHOULD_CHECK_AV = SHOULD_CHECK_AUDIO | SHOULD_CHECK_VIDEO,
|
||||
|
||||
AUDIO_FLAGS = SHOULD_SEND_AUDIO | SHOULD_RECV_AUDIO
|
||||
| SHOULD_INACTIVE_AUDIO | SHOULD_REJECT_AUDIO
|
||||
| DONT_CHECK_AUDIO | SHOULD_OMIT_AUDIO,
|
||||
| DONT_CHECK_AUDIO | SHOULD_OMIT_AUDIO
|
||||
| SHOULD_CHECK_AUDIO,
|
||||
|
||||
VIDEO_FLAGS = SHOULD_SEND_VIDEO | SHOULD_RECV_VIDEO
|
||||
| SHOULD_INACTIVE_VIDEO | SHOULD_REJECT_VIDEO
|
||||
| DONT_CHECK_VIDEO | SHOULD_OMIT_VIDEO
|
||||
| SHOULD_CHECK_VIDEO
|
||||
};
|
||||
|
||||
enum offerAnswerFlags
|
||||
@ -1151,6 +1159,12 @@ private:
|
||||
case 0:
|
||||
ASSERT_EQ(sdp.find("a=rtpmap:109 opus/48000"), std::string::npos);
|
||||
break;
|
||||
case SHOULD_CHECK_AUDIO:
|
||||
ASSERT_NE(sdp.find("a=rtpmap:109 opus/48000"), std::string::npos);
|
||||
if (offer) {
|
||||
ASSERT_NE(sdp.find("a=rtpmap:0 PCMU/8000"), std::string::npos);
|
||||
}
|
||||
break;
|
||||
case SHOULD_SEND_AUDIO:
|
||||
ASSERT_NE(sdp.find("a=rtpmap:109 opus/48000"), std::string::npos);
|
||||
ASSERT_NE(sdp.find(" 0-15\r\na=sendonly"), std::string::npos);
|
||||
@ -1193,6 +1207,9 @@ private:
|
||||
case 0:
|
||||
ASSERT_EQ(sdp.find("a=rtpmap:120 VP8/90000"), std::string::npos);
|
||||
break;
|
||||
case SHOULD_CHECK_VIDEO:
|
||||
ASSERT_NE(sdp.find("a=rtpmap:120 VP8/90000"), std::string::npos);
|
||||
break;
|
||||
case SHOULD_SEND_VIDEO:
|
||||
ASSERT_NE(sdp.find("a=rtpmap:120 VP8/90000\r\na=sendonly"),
|
||||
std::string::npos);
|
||||
@ -1540,6 +1557,50 @@ public:
|
||||
kBogusSrflxAddress, kBogusSrflxPort);
|
||||
}
|
||||
|
||||
// Check max-fs and max-fr in SDP
|
||||
void CheckMaxFsFrSdp(const std::string sdp,
|
||||
int format,
|
||||
int max_fs,
|
||||
int max_fr) {
|
||||
ParsedSDP sdpWrapper(sdp);
|
||||
std::stringstream ss;
|
||||
ss << "a=fmtp:" << format;
|
||||
std::vector<std::string> lines = sdpWrapper.GetLines(ss.str());
|
||||
|
||||
// Both max-fs and max-fr not exist
|
||||
if (lines.empty()) {
|
||||
ASSERT_EQ(max_fs, 0);
|
||||
ASSERT_EQ(max_fr, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
// At most one instance allowed for each format
|
||||
ASSERT_EQ(lines.size(), 1U);
|
||||
|
||||
std::string line = lines.front();
|
||||
|
||||
// Make sure that max-fs doesn't exist
|
||||
if (max_fs == 0) {
|
||||
ASSERT_EQ(line.find("max-fs="), std::string::npos);
|
||||
}
|
||||
// Check max-fs value
|
||||
if (max_fs > 0) {
|
||||
std::stringstream ss;
|
||||
ss << "max-fs=" << max_fs;
|
||||
ASSERT_NE(line.find(ss.str()), std::string::npos);
|
||||
}
|
||||
// Make sure that max-fr doesn't exist
|
||||
if (max_fr == 0) {
|
||||
ASSERT_EQ(line.find("max-fr="), std::string::npos);
|
||||
}
|
||||
// Check max-fr value
|
||||
if (max_fr > 0) {
|
||||
std::stringstream ss;
|
||||
ss << "max-fr=" << max_fr;
|
||||
ASSERT_NE(line.find(ss.str()), std::string::npos);
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
bool init_;
|
||||
ScopedDeletePtr<SignalingAgent> a1_; // Canonically "caller"
|
||||
@ -1549,6 +1610,17 @@ public:
|
||||
uint16_t stun_port_;
|
||||
};
|
||||
|
||||
class FsFrPrefClearer {
|
||||
public:
|
||||
FsFrPrefClearer(nsCOMPtr<nsIPrefBranch> prefs): mPrefs(prefs) {}
|
||||
~FsFrPrefClearer() {
|
||||
mPrefs->ClearUserPref("media.navigator.video.max_fs");
|
||||
mPrefs->ClearUserPref("media.navigator.video.max_fr");
|
||||
}
|
||||
private:
|
||||
nsCOMPtr<nsIPrefBranch> mPrefs;
|
||||
};
|
||||
|
||||
TEST_F(SignalingTest, JustInit)
|
||||
{
|
||||
}
|
||||
@ -3328,6 +3400,167 @@ TEST_F(SignalingTest, hugeSdp)
|
||||
a2_->CreateAnswer(constraints, offer, OFFER_AV);
|
||||
}
|
||||
|
||||
// Test max_fs and max_fr prefs have proper impact on SDP offer
|
||||
TEST_F(SignalingTest, MaxFsFrInOffer)
|
||||
{
|
||||
EnsureInit();
|
||||
|
||||
sipcc::MediaConstraints constraints;
|
||||
|
||||
nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
|
||||
ASSERT_TRUE(prefs);
|
||||
FsFrPrefClearer prefClearer(prefs);
|
||||
|
||||
prefs->SetIntPref("media.navigator.video.max_fs", 300);
|
||||
prefs->SetIntPref("media.navigator.video.max_fr", 30);
|
||||
|
||||
a1_->CreateOffer(constraints, OFFER_AV, SHOULD_CHECK_AV);
|
||||
|
||||
// Verify that SDP contains correct max-fs and max-fr
|
||||
CheckMaxFsFrSdp(a1_->offer(), 120, 300, 30);
|
||||
}
|
||||
|
||||
// Test max_fs and max_fr prefs have proper impact on SDP answer
|
||||
TEST_F(SignalingTest, MaxFsFrInAnswer)
|
||||
{
|
||||
EnsureInit();
|
||||
|
||||
sipcc::MediaConstraints constraints;
|
||||
|
||||
nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
|
||||
ASSERT_TRUE(prefs);
|
||||
FsFrPrefClearer prefClearer(prefs);
|
||||
|
||||
// We don't want max_fs and max_fr prefs impact SDP at this moment
|
||||
prefs->SetIntPref("media.navigator.video.max_fs", 0);
|
||||
prefs->SetIntPref("media.navigator.video.max_fr", 0);
|
||||
|
||||
a1_->CreateOffer(constraints, OFFER_AV, SHOULD_CHECK_AV);
|
||||
|
||||
// SDP should not contain max-fs and max-fr here
|
||||
CheckMaxFsFrSdp(a1_->offer(), 120, 0, 0);
|
||||
|
||||
a2_->SetRemote(TestObserver::OFFER, a1_->offer());
|
||||
|
||||
prefs->SetIntPref("media.navigator.video.max_fs", 600);
|
||||
prefs->SetIntPref("media.navigator.video.max_fr", 60);
|
||||
|
||||
a2_->CreateAnswer(constraints, a1_->offer(), OFFER_AV | ANSWER_AV);
|
||||
|
||||
// Verify that SDP contains correct max-fs and max-fr
|
||||
CheckMaxFsFrSdp(a2_->answer(), 120, 600, 60);
|
||||
}
|
||||
|
||||
// Test SDP offer has proper impact on callee's codec configuration
|
||||
TEST_F(SignalingTest, MaxFsFrCalleeCodec)
|
||||
{
|
||||
EnsureInit();
|
||||
|
||||
sipcc::MediaConstraints constraints;
|
||||
|
||||
nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
|
||||
ASSERT_TRUE(prefs);
|
||||
FsFrPrefClearer prefClearer(prefs);
|
||||
|
||||
// We don't want max_fs and max_fr prefs impact SDP at this moment
|
||||
prefs->SetIntPref("media.navigator.video.max_fs", 0);
|
||||
prefs->SetIntPref("media.navigator.video.max_fr", 0);
|
||||
|
||||
a1_->CreateOffer(constraints, OFFER_AV, SHOULD_CHECK_AV);
|
||||
|
||||
ParsedSDP sdpWrapper(a1_->offer());
|
||||
|
||||
sdpWrapper.ReplaceLine("a=rtpmap:120",
|
||||
"a=rtpmap:120 VP8/90000\r\na=fmtp:120 max-fs=300;max-fr=30\r\n");
|
||||
|
||||
std::cout << "Modified SDP " << std::endl
|
||||
<< indent(sdpWrapper.getSdp()) << std::endl;
|
||||
|
||||
// Double confirm that SDP offer contains correct max-fs and max-fr
|
||||
CheckMaxFsFrSdp(sdpWrapper.getSdp(), 120, 300, 30);
|
||||
|
||||
a1_->SetLocal(TestObserver::OFFER, sdpWrapper.getSdp());
|
||||
a2_->SetRemote(TestObserver::OFFER, sdpWrapper.getSdp());
|
||||
|
||||
a2_->CreateAnswer(constraints, sdpWrapper.getSdp(), OFFER_AV | ANSWER_AV);
|
||||
|
||||
// SDP should not contain max-fs and max-fr here
|
||||
CheckMaxFsFrSdp(a2_->answer(), 120, 0, 0);
|
||||
|
||||
a2_->SetLocal(TestObserver::ANSWER, a2_->answer());
|
||||
a1_->SetRemote(TestObserver::ANSWER, a2_->answer());
|
||||
|
||||
ASSERT_TRUE_WAIT(a1_->IceCompleted() == true, kDefaultTimeout);
|
||||
ASSERT_TRUE_WAIT(a2_->IceCompleted() == true, kDefaultTimeout);
|
||||
|
||||
// Checking callee's video sending configuration does respect max-fs and
|
||||
// max-fr in SDP offer.
|
||||
mozilla::RefPtr<mozilla::MediaPipeline> pipeline =
|
||||
a2_->GetMediaPipeline(1, 0, 1);
|
||||
ASSERT_TRUE(pipeline);
|
||||
mozilla::MediaSessionConduit *conduit = pipeline->Conduit();
|
||||
ASSERT_TRUE(conduit);
|
||||
ASSERT_EQ(conduit->type(), mozilla::MediaSessionConduit::VIDEO);
|
||||
mozilla::VideoSessionConduit *video_conduit =
|
||||
static_cast<mozilla::VideoSessionConduit*>(conduit);
|
||||
|
||||
ASSERT_EQ(video_conduit->SendingMaxFs(), (unsigned short) 300);
|
||||
ASSERT_EQ(video_conduit->SendingMaxFr(), (unsigned short) 30);
|
||||
}
|
||||
|
||||
// Test SDP answer has proper impact on caller's codec configuration
|
||||
TEST_F(SignalingTest, MaxFsFrCallerCodec)
|
||||
{
|
||||
EnsureInit();
|
||||
|
||||
sipcc::MediaConstraints constraints;
|
||||
|
||||
nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
|
||||
ASSERT_TRUE(prefs);
|
||||
FsFrPrefClearer prefClearer(prefs);
|
||||
|
||||
// We don't want max_fs and max_fr prefs impact SDP at this moment
|
||||
prefs->SetIntPref("media.navigator.video.max_fs", 0);
|
||||
prefs->SetIntPref("media.navigator.video.max_fr", 0);
|
||||
|
||||
a1_->CreateOffer(constraints, OFFER_AV, SHOULD_CHECK_AV);
|
||||
a1_->SetLocal(TestObserver::OFFER, a1_->offer());
|
||||
a2_->SetRemote(TestObserver::OFFER, a1_->offer());
|
||||
|
||||
a2_->CreateAnswer(constraints, a1_->offer(), OFFER_AV | ANSWER_AV);
|
||||
|
||||
ParsedSDP sdpWrapper(a2_->answer());
|
||||
|
||||
sdpWrapper.ReplaceLine("a=rtpmap:120",
|
||||
"a=rtpmap:120 VP8/90000\r\na=fmtp:120 max-fs=600;max-fr=60\r\n");
|
||||
|
||||
std::cout << "Modified SDP " << std::endl
|
||||
<< indent(sdpWrapper.getSdp()) << std::endl;
|
||||
|
||||
// Double confirm that SDP answer contains correct max-fs and max-fr
|
||||
CheckMaxFsFrSdp(sdpWrapper.getSdp(), 120, 600, 60);
|
||||
|
||||
a2_->SetLocal(TestObserver::ANSWER, sdpWrapper.getSdp());
|
||||
a1_->SetRemote(TestObserver::ANSWER, sdpWrapper.getSdp());
|
||||
|
||||
ASSERT_TRUE_WAIT(a1_->IceCompleted() == true, kDefaultTimeout);
|
||||
ASSERT_TRUE_WAIT(a2_->IceCompleted() == true, kDefaultTimeout);
|
||||
|
||||
// Checking caller's video sending configuration does respect max-fs and
|
||||
// max-fr in SDP answer.
|
||||
mozilla::RefPtr<mozilla::MediaPipeline> pipeline =
|
||||
a1_->GetMediaPipeline(1, 0, 1);
|
||||
ASSERT_TRUE(pipeline);
|
||||
mozilla::MediaSessionConduit *conduit = pipeline->Conduit();
|
||||
ASSERT_TRUE(conduit);
|
||||
ASSERT_EQ(conduit->type(), mozilla::MediaSessionConduit::VIDEO);
|
||||
mozilla::VideoSessionConduit *video_conduit =
|
||||
static_cast<mozilla::VideoSessionConduit*>(conduit);
|
||||
|
||||
ASSERT_EQ(video_conduit->SendingMaxFs(), (unsigned short) 600);
|
||||
ASSERT_EQ(video_conduit->SendingMaxFr(), (unsigned short) 60);
|
||||
}
|
||||
|
||||
} // End namespace test.
|
||||
|
||||
bool is_color_terminal(const char *terminal) {
|
||||
|
@ -787,3 +787,5 @@ pref("general.useragent.override.youtube.com", "Android; Tablet;#Android; Mobile
|
||||
// When true, phone number linkification is enabled.
|
||||
pref("browser.ui.linkify.phone", false);
|
||||
|
||||
// Enables/disables Spatial Navigation
|
||||
pref("snav.enabled", true);
|
||||
|
@ -18,6 +18,7 @@ Cu.import("resource://gre/modules/JNI.jsm");
|
||||
Cu.import('resource://gre/modules/Payment.jsm');
|
||||
Cu.import("resource://gre/modules/PermissionPromptHelper.jsm");
|
||||
Cu.import("resource://gre/modules/ContactService.jsm");
|
||||
Cu.import("resource://gre/modules/SpatialNavigation.jsm");
|
||||
|
||||
#ifdef ACCESSIBILITY
|
||||
Cu.import("resource://gre/modules/accessibility/AccessFu.jsm");
|
||||
@ -4111,6 +4112,9 @@ var BrowserEventHandler = {
|
||||
BrowserApp.deck.addEventListener("touchstart", this, true);
|
||||
BrowserApp.deck.addEventListener("click", InputWidgetHelper, true);
|
||||
BrowserApp.deck.addEventListener("click", SelectHelper, true);
|
||||
|
||||
SpatialNavigation.init(BrowserApp.deck, null);
|
||||
|
||||
document.addEventListener("MozMagnifyGesture", this, true);
|
||||
|
||||
Services.prefs.addObserver("browser.zoom.reflowOnZoom", this, false);
|
||||
|
@ -230,6 +230,8 @@ pref("media.navigator.video.default_width",640);
|
||||
pref("media.navigator.video.default_height",480);
|
||||
pref("media.navigator.video.default_fps",30);
|
||||
pref("media.navigator.video.default_minfps",10);
|
||||
pref("media.navigator.video.max_fs", 0); // unrestricted
|
||||
pref("media.navigator.video.max_fr", 0); // unrestricted
|
||||
pref("media.peerconnection.enabled", true);
|
||||
pref("media.navigator.permission.disabled", false);
|
||||
pref("media.peerconnection.default_iceservers", "[{\"url\": \"stun:23.21.150.121\"}]");
|
||||
@ -4464,3 +4466,6 @@ pref("urlclassifier.malware_table", "goog-malware-shavar");
|
||||
pref("urlclassifier.phish_table", "goog-phish-shavar");
|
||||
pref("urlclassifier.download_block_table", "goog-badbinurl-shavar");
|
||||
pref("urlclassifier.download_allow_table", "goog-downloadwhite-digest256");
|
||||
|
||||
// Turn off Spatial navigation by default.
|
||||
pref("snav.enabled", false);
|
||||
|
505
toolkit/modules/SpatialNavigation.jsm
Normal file
505
toolkit/modules/SpatialNavigation.jsm
Normal file
@ -0,0 +1,505 @@
|
||||
// -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*-
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/**
|
||||
* Import this module through
|
||||
*
|
||||
* Components.utils.import("resource://gre/modules/SpatialNavigation.jsm");
|
||||
*
|
||||
* Usage: (Literal class)
|
||||
*
|
||||
* SpatialNavigation.init(browser_element, optional_callback);
|
||||
*
|
||||
* optional_callback will be called when a new element is focused.
|
||||
*
|
||||
* function optional_callback(element) {}
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["SpatialNavigation"];
|
||||
|
||||
var SpatialNavigation = {
|
||||
init: function(browser, callback) {
|
||||
browser.addEventListener("keydown", function (event) {
|
||||
_onInputKeyPress(event, callback);
|
||||
}, true);
|
||||
}
|
||||
};
|
||||
|
||||
// Private stuff
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cu = Components.utils;
|
||||
|
||||
Cu["import"]("resource://gre/modules/Services.jsm", this);
|
||||
|
||||
let eventListenerService = Cc["@mozilla.org/eventlistenerservice;1"]
|
||||
.getService(Ci.nsIEventListenerService);
|
||||
let focusManager = Cc["@mozilla.org/focus-manager;1"]
|
||||
.getService(Ci.nsIFocusManager);
|
||||
let windowMediator = Cc['@mozilla.org/appshell/window-mediator;1']
|
||||
.getService(Ci.nsIWindowMediator);
|
||||
|
||||
// Debug helpers:
|
||||
function dump(a) {
|
||||
Services.console.logStringMessage("SpatialNavigation: " + a);
|
||||
}
|
||||
|
||||
function dumpRect(desc, rect) {
|
||||
dump(desc + " " + Math.round(rect.left) + " " + Math.round(rect.top) + " " +
|
||||
Math.round(rect.right) + " " + Math.round(rect.bottom) + " width:" +
|
||||
Math.round(rect.width) + " height:" + Math.round(rect.height));
|
||||
}
|
||||
|
||||
function dumpNodeCoord(desc, node) {
|
||||
let rect = node.getBoundingClientRect();
|
||||
dump(desc + " " + node.tagName + " x:" + Math.round(rect.left + rect.width/2) +
|
||||
" y:" + Math.round(rect.top + rect.height / 2));
|
||||
}
|
||||
|
||||
// modifier values
|
||||
|
||||
const kAlt = "alt";
|
||||
const kShift = "shift";
|
||||
const kCtrl = "ctrl";
|
||||
const kNone = "none";
|
||||
|
||||
function _onInputKeyPress (event, callback) {
|
||||
//If Spatial Navigation isn't enabled, return.
|
||||
if (!PrefObserver['enabled']) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Use whatever key value is available (either keyCode or charCode).
|
||||
// It might be useful for addons or whoever wants to set different
|
||||
// key to be used here (e.g. "a", "F1", "arrowUp", ...).
|
||||
var key = event.which || event.keyCode;
|
||||
|
||||
if (key != PrefObserver['keyCodeDown'] &&
|
||||
key != PrefObserver['keyCodeRight'] &&
|
||||
key != PrefObserver['keyCodeUp'] &&
|
||||
key != PrefObserver['keyCodeLeft'] &&
|
||||
key != PrefObserver['keyCodeReturn']) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (key == PrefObserver['keyCodeReturn']) {
|
||||
// We report presses of the action button on a gamepad "A" as the return
|
||||
// key to the DOM. The behaviour of hitting the return key and clicking an
|
||||
// element is the same for some elements, but not all, so we handle the
|
||||
// ones we want (like the Select element) here:
|
||||
if (event.target instanceof Ci.nsIDOMHTMLSelectElement &&
|
||||
event.target.click) {
|
||||
event.target.click();
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
return;
|
||||
} else {
|
||||
// Leave the action key press to get reported to the DOM as a return
|
||||
// keypress.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If it is not using the modifiers it should, return.
|
||||
if (!event.altKey && PrefObserver['modifierAlt'] ||
|
||||
!event.shiftKey && PrefObserver['modifierShift'] ||
|
||||
!event.crtlKey && PrefObserver['modifierCtrl']) {
|
||||
return;
|
||||
}
|
||||
|
||||
let currentlyFocused = event.target;
|
||||
let currentlyFocusedWindow = currentlyFocused.ownerDocument.defaultView;
|
||||
let bestElementToFocus = null;
|
||||
|
||||
// If currentlyFocused is an nsIDOMHTMLBodyElement then the page has just been
|
||||
// loaded, and this is the first keypress in the page.
|
||||
if (currentlyFocused instanceof Ci.nsIDOMHTMLBodyElement) {
|
||||
focusManager.moveFocus(currentlyFocusedWindow, null, focusManager.MOVEFOCUS_FIRST, 0);
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
let windowUtils = currentlyFocusedWindow.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils);
|
||||
let cssPageRect = _getRootBounds(windowUtils);
|
||||
let searchRect = _getSearchRect(currentlyFocused, key, cssPageRect);
|
||||
|
||||
let nodes = {};
|
||||
nodes.length = 0;
|
||||
|
||||
let searchRectOverflows = false;
|
||||
|
||||
while (!bestElementToFocus && !searchRectOverflows) {
|
||||
switch (key) {
|
||||
case PrefObserver['keyCodeLeft']:
|
||||
case PrefObserver['keyCodeRight']: {
|
||||
if (searchRect.top < cssPageRect.top &&
|
||||
searchRect.bottom > cssPageRect.bottom) {
|
||||
searchRectOverflows = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PrefObserver['keyCodeUp']:
|
||||
case PrefObserver['keyCodeDown']: {
|
||||
if (searchRect.left < cssPageRect.left &&
|
||||
searchRect.right > cssPageRect.right) {
|
||||
searchRectOverflows = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
nodes = windowUtils.nodesFromRect(searchRect.left, searchRect.top,
|
||||
0, searchRect.width, searchRect.height, 0,
|
||||
true, false);
|
||||
// Make the search rectangle "wider": double it's size in the direction
|
||||
// that is not the keypress.
|
||||
switch (key) {
|
||||
case PrefObserver['keyCodeLeft']:
|
||||
case PrefObserver['keyCodeRight']: {
|
||||
searchRect.top = searchRect.top - (searchRect.height / 2);
|
||||
searchRect.bottom = searchRect.top + (searchRect.height * 2);
|
||||
searchRect.height = searchRect.height * 2;
|
||||
break;
|
||||
}
|
||||
case PrefObserver['keyCodeUp']:
|
||||
case PrefObserver['keyCodeDown']: {
|
||||
searchRect.left = searchRect.left - (searchRect.width / 2);
|
||||
searchRect.right = searchRect.left + (searchRect.width * 2);
|
||||
searchRect.width = searchRect.width * 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
bestElementToFocus = _getBestToFocus(nodes, key, currentlyFocused);
|
||||
}
|
||||
|
||||
|
||||
if (bestElementToFocus === null) {
|
||||
// Couldn't find an element to focus.
|
||||
return;
|
||||
}
|
||||
|
||||
focusManager.setFocus(bestElementToFocus, focusManager.FLAG_SHOWRING);
|
||||
|
||||
//if it is a text element, select all.
|
||||
if ((bestElementToFocus instanceof Ci.nsIDOMHTMLInputElement &&
|
||||
bestElementToFocus.mozIsTextField(false)) ||
|
||||
bestElementToFocus instanceof Ci.nsIDOMHTMLTextAreaElement) {
|
||||
bestElementToFocus.selectionStart = 0;
|
||||
bestElementToFocus.selectionEnd = bestElementToFocus.textLength;
|
||||
}
|
||||
|
||||
if (callback != undefined) {
|
||||
callback(bestElementToFocus);
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
||||
// Returns the bounds of the page relative to the viewport.
|
||||
function _getRootBounds(windowUtils) {
|
||||
let cssPageRect = windowUtils.getRootBounds();
|
||||
|
||||
let scrollX = {};
|
||||
let scrollY = {};
|
||||
windowUtils.getScrollXY(false, scrollX, scrollY);
|
||||
|
||||
let cssPageRectCopy = {};
|
||||
|
||||
cssPageRectCopy.right = cssPageRect.right - scrollX.value;
|
||||
cssPageRectCopy.left = cssPageRect.left - scrollX.value;
|
||||
cssPageRectCopy.top = cssPageRect.top - scrollY.value;
|
||||
cssPageRectCopy.bottom = cssPageRect.bottom - scrollY.value;
|
||||
cssPageRectCopy.width = cssPageRect.width;
|
||||
cssPageRectCopy.height = cssPageRect.height;
|
||||
|
||||
return cssPageRectCopy;
|
||||
}
|
||||
|
||||
// Returns the best node to focus from the list of nodes returned by the hit
|
||||
// test.
|
||||
function _getBestToFocus(nodes, key, currentlyFocused) {
|
||||
let best = null;
|
||||
let bestDist;
|
||||
let bestMid;
|
||||
let nodeMid;
|
||||
let currentlyFocusedMid = _getMidpoint(currentlyFocused);
|
||||
let currentlyFocusedRect = currentlyFocused.getBoundingClientRect();
|
||||
|
||||
for (let i = 0; i < nodes.length; i++) {
|
||||
// Reject the currentlyFocused, and all node types we can't focus
|
||||
if (!_canFocus(nodes[i]) || nodes[i] === currentlyFocused) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Reject all nodes that aren't "far enough" in the direction of the
|
||||
// keypress
|
||||
nodeMid = _getMidpoint(nodes[i]);
|
||||
switch (key) {
|
||||
case PrefObserver['keyCodeLeft']:
|
||||
if (nodeMid.x >= (currentlyFocusedMid.x - currentlyFocusedRect.width / 2)) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case PrefObserver['keyCodeRight']:
|
||||
if (nodeMid.x <= (currentlyFocusedMid.x + currentlyFocusedRect.width / 2)) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
|
||||
case PrefObserver['keyCodeUp']:
|
||||
if (nodeMid.y >= (currentlyFocusedMid.y - currentlyFocusedRect.height / 2)) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case PrefObserver['keyCodeDown']:
|
||||
if (nodeMid.y <= (currentlyFocusedMid.y + currentlyFocusedRect.height / 2)) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Initialize best to the first viable value:
|
||||
if (!best) {
|
||||
best = nodes[i];
|
||||
bestDist = _spatialDistance(best, currentlyFocused);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Of the remaining nodes, pick the one closest to the currently focused
|
||||
// node.
|
||||
let curDist = _spatialDistance(nodes[i], currentlyFocused);
|
||||
if (curDist > bestDist) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bestMid = _getMidpoint(best);
|
||||
switch (key) {
|
||||
case PrefObserver['keyCodeLeft']:
|
||||
if (nodeMid.x > bestMid.x) {
|
||||
best = nodes[i];
|
||||
bestDist = curDist;
|
||||
}
|
||||
break;
|
||||
case PrefObserver['keyCodeRight']:
|
||||
if (nodeMid.x < bestMid.x) {
|
||||
best = nodes[i];
|
||||
bestDist = curDist;
|
||||
}
|
||||
break;
|
||||
case PrefObserver['keyCodeUp']:
|
||||
if (nodeMid.y > bestMid.y) {
|
||||
best = nodes[i];
|
||||
bestDist = curDist;
|
||||
}
|
||||
break;
|
||||
case PrefObserver['keyCodeDown']:
|
||||
if (nodeMid.y < bestMid.y) {
|
||||
best = nodes[i];
|
||||
bestDist = curDist;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return best;
|
||||
}
|
||||
|
||||
// Returns the midpoint of a node.
|
||||
function _getMidpoint(node) {
|
||||
let mid = {};
|
||||
let box = node.getBoundingClientRect();
|
||||
mid.x = box.left + (box.width / 2);
|
||||
mid.y = box.top + (box.height / 2);
|
||||
|
||||
return mid;
|
||||
}
|
||||
|
||||
// Returns true if the node is a type that we want to focus, false otherwise.
|
||||
function _canFocus(node) {
|
||||
if (node instanceof Ci.nsIDOMHTMLLinkElement ||
|
||||
node instanceof Ci.nsIDOMHTMLAnchorElement) {
|
||||
return true;
|
||||
}
|
||||
if ((node instanceof Ci.nsIDOMHTMLButtonElement ||
|
||||
node instanceof Ci.nsIDOMHTMLInputElement ||
|
||||
node instanceof Ci.nsIDOMHTMLLinkElement ||
|
||||
node instanceof Ci.nsIDOMHTMLOptGroupElement ||
|
||||
node instanceof Ci.nsIDOMHTMLSelectElement ||
|
||||
node instanceof Ci.nsIDOMHTMLTextAreaElement) &&
|
||||
node.disabled === false) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Returns a rectangle that extends to the end of the screen in the direction that
|
||||
// the key is pressed.
|
||||
function _getSearchRect(currentlyFocused, key, cssPageRect) {
|
||||
let currentlyFocusedRect = currentlyFocused.getBoundingClientRect();
|
||||
|
||||
let newRect = {};
|
||||
newRect.left = currentlyFocusedRect.left;
|
||||
newRect.top = currentlyFocusedRect.top;
|
||||
newRect.right = currentlyFocusedRect.right;
|
||||
newRect.bottom = currentlyFocusedRect.bottom;
|
||||
newRect.width = currentlyFocusedRect.width;
|
||||
newRect.height = currentlyFocusedRect.height;
|
||||
|
||||
switch (key) {
|
||||
case PrefObserver['keyCodeLeft']:
|
||||
newRect.left = cssPageRect.left;
|
||||
newRect.width = newRect.right - newRect.left;
|
||||
break;
|
||||
|
||||
case PrefObserver['keyCodeRight']:
|
||||
newRect.right = cssPageRect.right;
|
||||
newRect.width = newRect.right - newRect.left;
|
||||
break;
|
||||
|
||||
case PrefObserver['keyCodeUp']:
|
||||
newRect.top = cssPageRect.top;
|
||||
newRect.height = newRect.bottom - newRect.top;
|
||||
break;
|
||||
|
||||
case PrefObserver['keyCodeDown']:
|
||||
newRect.bottom = cssPageRect.bottom;
|
||||
newRect.height = newRect.bottom - newRect.top;
|
||||
break;
|
||||
}
|
||||
return newRect;
|
||||
}
|
||||
|
||||
// Gets the distance between two points a and b.
|
||||
function _spatialDistance(a, b) {
|
||||
let mida = _getMidpoint(a);
|
||||
let midb = _getMidpoint(b);
|
||||
|
||||
return Math.round(Math.pow(mida.x - midb.x, 2) +
|
||||
Math.pow(mida.y - midb.y, 2));
|
||||
}
|
||||
|
||||
// Snav preference observer
|
||||
var PrefObserver = {
|
||||
register: function() {
|
||||
this.prefService = Cc["@mozilla.org/preferences-service;1"]
|
||||
.getService(Ci.nsIPrefService);
|
||||
|
||||
this._branch = this.prefService.getBranch("snav.");
|
||||
this._branch.QueryInterface(Ci.nsIPrefBranch2);
|
||||
this._branch.addObserver("", this, false);
|
||||
|
||||
// set current or default pref values
|
||||
this.observe(null, "nsPref:changed", "enabled");
|
||||
this.observe(null, "nsPref:changed", "xulContentEnabled");
|
||||
this.observe(null, "nsPref:changed", "keyCode.modifier");
|
||||
this.observe(null, "nsPref:changed", "keyCode.right");
|
||||
this.observe(null, "nsPref:changed", "keyCode.up");
|
||||
this.observe(null, "nsPref:changed", "keyCode.down");
|
||||
this.observe(null, "nsPref:changed", "keyCode.left");
|
||||
this.observe(null, "nsPref:changed", "keyCode.return");
|
||||
},
|
||||
|
||||
observe: function(aSubject, aTopic, aData) {
|
||||
if (aTopic != "nsPref:changed") {
|
||||
return;
|
||||
}
|
||||
|
||||
// aSubject is the nsIPrefBranch we're observing (after appropriate QI)
|
||||
// aData is the name of the pref that's been changed (relative to aSubject)
|
||||
switch (aData) {
|
||||
case "enabled":
|
||||
try {
|
||||
this.enabled = this._branch.getBoolPref("enabled");
|
||||
} catch(e) {
|
||||
this.enabled = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case "xulContentEnabled":
|
||||
try {
|
||||
this.xulContentEnabled = this._branch.getBoolPref("xulContentEnabled");
|
||||
} catch(e) {
|
||||
this.xulContentEnabled = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case "keyCode.modifier": {
|
||||
let keyCodeModifier;
|
||||
try {
|
||||
keyCodeModifier = this._branch.getCharPref("keyCode.modifier");
|
||||
|
||||
// resetting modifiers
|
||||
this.modifierAlt = false;
|
||||
this.modifierShift = false;
|
||||
this.modifierCtrl = false;
|
||||
|
||||
if (keyCodeModifier != this.kNone) {
|
||||
// we are using '+' as a separator in about:config.
|
||||
let mods = keyCodeModifier.split(/\++/);
|
||||
for (let i = 0; i < mods.length; i++) {
|
||||
let mod = mods[i].toLowerCase();
|
||||
if (mod === "")
|
||||
continue;
|
||||
else if (mod == kAlt)
|
||||
this.modifierAlt = true;
|
||||
else if (mod == kShift)
|
||||
this.modifierShift = true;
|
||||
else if (mod == kCtrl)
|
||||
this.modifierCtrl = true;
|
||||
else {
|
||||
keyCodeModifier = kNone;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch(e) { }
|
||||
break;
|
||||
}
|
||||
|
||||
case "keyCode.up":
|
||||
try {
|
||||
this.keyCodeUp = this._branch.getIntPref("keyCode.up");
|
||||
} catch(e) {
|
||||
this.keyCodeUp = Ci.nsIDOMKeyEvent.DOM_VK_UP;
|
||||
}
|
||||
break;
|
||||
case "keyCode.down":
|
||||
try {
|
||||
this.keyCodeDown = this._branch.getIntPref("keyCode.down");
|
||||
} catch(e) {
|
||||
this.keyCodeDown = Ci.nsIDOMKeyEvent.DOM_VK_DOWN;
|
||||
}
|
||||
break;
|
||||
case "keyCode.left":
|
||||
try {
|
||||
this.keyCodeLeft = this._branch.getIntPref("keyCode.left");
|
||||
} catch(e) {
|
||||
this.keyCodeLeft = Ci.nsIDOMKeyEvent.DOM_VK_LEFT;
|
||||
}
|
||||
break;
|
||||
case "keyCode.right":
|
||||
try {
|
||||
this.keyCodeRight = this._branch.getIntPref("keyCode.right");
|
||||
} catch(e) {
|
||||
this.keyCodeRight = Ci.nsIDOMKeyEvent.DOM_VK_RIGHT;
|
||||
}
|
||||
break;
|
||||
case "keyCode.return":
|
||||
try {
|
||||
this.keyCodeReturn = this._branch.getIntPref("keyCode.return");
|
||||
} catch(e) {
|
||||
this.keyCodeReturn = Ci.nsIDOMKeyEvent.DOM_VK_RETURN;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
PrefObserver.register();
|
@ -35,6 +35,7 @@ EXTRA_JS_MODULES += [
|
||||
'SelectContentHelper.jsm',
|
||||
'SelectParentHelper.jsm',
|
||||
'Sntp.jsm',
|
||||
'SpatialNavigation.jsm',
|
||||
'Sqlite.jsm',
|
||||
'Task.jsm',
|
||||
'TelemetryTimestamps.jsm',
|
||||
|
1
toolkit/modules/tests/mochitest/mochitest.ini
Normal file
1
toolkit/modules/tests/mochitest/mochitest.ini
Normal file
@ -0,0 +1 @@
|
||||
[test_spatial_navigation.html]
|
78
toolkit/modules/tests/mochitest/test_spatial_navigation.html
Normal file
78
toolkit/modules/tests/mochitest/test_spatial_navigation.html
Normal file
@ -0,0 +1,78 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=698437
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug 698437</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<script type="application/javascript">
|
||||
|
||||
/** Test for Bug 698437 **/
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
function Test() {
|
||||
if (!SpecialPowers.getBoolPref("snav.enabled")) {
|
||||
todo(false, "Enable spatial navigiation on this platform.");
|
||||
SimpleTest.finish();
|
||||
return;
|
||||
}
|
||||
|
||||
var center = document.getElementById("center");
|
||||
var right = document.getElementById("right");
|
||||
var left = document.getElementById("left");
|
||||
var top = document.getElementById("top");
|
||||
var bottom = document.getElementById("bottom");
|
||||
|
||||
console.log(top);
|
||||
console.log(bottom);
|
||||
console.log(center);
|
||||
console.log(left);
|
||||
console.log(right);
|
||||
|
||||
center.focus();
|
||||
is(center.id, document.activeElement.id, "How did we call focus on center and it did" +
|
||||
" not become the active element?");
|
||||
|
||||
synthesizeKey("VK_UP", { });
|
||||
is(top.id, document.activeElement.id,
|
||||
"Spatial navigation up key is not handled correctly.");
|
||||
|
||||
center.focus();
|
||||
synthesizeKey("VK_DOWN", { });
|
||||
is(bottom.id, document.activeElement.id,
|
||||
"Spatial navigation down key is not handled correctly.");
|
||||
|
||||
center.focus();
|
||||
synthesizeKey("VK_RIGHT", { });
|
||||
is(right.id, document.activeElement.id,
|
||||
"Spatial navigation right key is not handled correctly.");
|
||||
|
||||
center.focus();
|
||||
synthesizeKey("VK_LEFT", { });
|
||||
is(left.id, document.activeElement.id,
|
||||
"Spatial navigation left key is not handled correctly.");
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body onload="Test();">
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=698437">Mozilla Bug 698437</a>
|
||||
<p id="display"></p>
|
||||
<div id="content">
|
||||
<p> This is a <a id="top" href="#">really</a> long sentence </p>
|
||||
<p> <a id="left" href="#">This</a> is a
|
||||
<a id="center" href="#">really</a> long
|
||||
<a id="right" href="#">sentence</a> </p>
|
||||
<p> This is a <a id="bottom" href="#">really</a> long sentence </p>
|
||||
</div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -9,3 +9,5 @@ DIRS += ['browser']
|
||||
MODULE = 'test_toolkit_general'
|
||||
|
||||
XPCSHELL_TESTS_MANIFESTS += ['xpcshell/xpcshell.ini']
|
||||
|
||||
MOCHITEST_MANIFESTS += ['mochitest/mochitest.ini']
|
||||
|
@ -466,10 +466,12 @@ static void StartSignalHandler(int signal, siginfo_t* info, void* context) {
|
||||
char thread[256];
|
||||
|
||||
// TODO support selecting features from profiler.options
|
||||
const char* features[2] = {NULL, NULL};
|
||||
const char* features[3] = {NULL, NULL, NULL};
|
||||
uint32_t featureCount = 0;
|
||||
features[0] = "leaf";
|
||||
featureCount++;
|
||||
features[1] = "js";
|
||||
featureCount++;
|
||||
const char* threadFeature = "threads";
|
||||
|
||||
std::ifstream infile;
|
||||
|
@ -1438,6 +1438,9 @@ static unsigned int ConvertAndroidKeyCodeToDOMKeyCode(int androidKeyCode)
|
||||
case AKEYCODE_KANA: return NS_VK_KANA;
|
||||
case AKEYCODE_ASSIST: return NS_VK_HELP;
|
||||
|
||||
// the A key is the action key for gamepad devices.
|
||||
case AKEYCODE_BUTTON_A: return NS_VK_RETURN;
|
||||
|
||||
default:
|
||||
ALOG("ConvertAndroidKeyCodeToDOMKeyCode: "
|
||||
"No DOM keycode for Android keycode %d", androidKeyCode);
|
||||
|
Loading…
Reference in New Issue
Block a user