Merge m-i to m-c

This commit is contained in:
Phil Ringnalda 2013-10-13 10:18:40 -07:00
commit 482fc23a2c
46 changed files with 1590 additions and 104 deletions

View File

@ -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

View File

@ -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>

View File

@ -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'

View File

@ -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>

View File

@ -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();

View File

@ -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();

View File

@ -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;

View File

@ -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");

View File

@ -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)

View File

@ -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;

View File

@ -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");

View File

@ -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.

View File

@ -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",

View File

@ -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();

View File

@ -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();

View File

@ -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) {

View 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>

View File

@ -176,3 +176,4 @@ load 895311-1.svg
load 897342-1.svg
load 898909-1.svg
load 898951-1.svg
load 919371-1.xhtml

View File

@ -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();
}

View File

@ -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)
{
}

View File

@ -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.

View File

@ -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);
}
}

View File

@ -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),

View File

@ -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;
}

View File

@ -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;
};
}

View File

@ -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()
*

View File

@ -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_ */

View File

@ -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",

View File

@ -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,

View File

@ -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)

View File

@ -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.

View File

@ -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. */

View File

@ -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
}

View File

@ -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)

View File

@ -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) {

View File

@ -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) {

View File

@ -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);

View File

@ -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);

View File

@ -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);

View 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();

View File

@ -35,6 +35,7 @@ EXTRA_JS_MODULES += [
'SelectContentHelper.jsm',
'SelectParentHelper.jsm',
'Sntp.jsm',
'SpatialNavigation.jsm',
'Sqlite.jsm',
'Task.jsm',
'TelemetryTimestamps.jsm',

View File

@ -0,0 +1 @@
[test_spatial_navigation.html]

View 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>

View File

@ -9,3 +9,5 @@ DIRS += ['browser']
MODULE = 'test_toolkit_general'
XPCSHELL_TESTS_MANIFESTS += ['xpcshell/xpcshell.ini']
MOCHITEST_MANIFESTS += ['mochitest/mochitest.ini']

View File

@ -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;

View File

@ -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);