mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-17 22:32:51 +00:00
merge mozilla-inbound to mozilla-central a=merge
This commit is contained in:
commit
15d6f2b06d
@ -83,6 +83,32 @@ namespace {
|
||||
// Animation interface:
|
||||
//
|
||||
// ---------------------------------------------------------------------------
|
||||
/* static */ already_AddRefed<Animation>
|
||||
Animation::Constructor(const GlobalObject& aGlobal,
|
||||
KeyframeEffectReadOnly* aEffect,
|
||||
AnimationTimeline* aTimeline,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
|
||||
RefPtr<Animation> animation = new Animation(global);
|
||||
|
||||
if (!aTimeline) {
|
||||
// Bug 1096776: We do not support null timeline yet.
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
if (!aEffect) {
|
||||
// Bug 1049975: We do not support null effect yet.
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
animation->SetTimeline(aTimeline);
|
||||
animation->SetEffect(aEffect);
|
||||
|
||||
return animation.forget();
|
||||
}
|
||||
|
||||
void
|
||||
Animation::SetId(const nsAString& aId)
|
||||
{
|
||||
|
@ -90,6 +90,11 @@ public:
|
||||
};
|
||||
|
||||
// Animation interface methods
|
||||
static already_AddRefed<Animation>
|
||||
Constructor(const GlobalObject& aGlobal,
|
||||
KeyframeEffectReadOnly* aEffect,
|
||||
AnimationTimeline* aTimeline,
|
||||
ErrorResult& aRv);
|
||||
void GetId(nsAString& aResult) const { aResult = mId; }
|
||||
void SetId(const nsAString& aId);
|
||||
KeyframeEffectReadOnly* GetEffect() const { return mEffect; }
|
||||
|
@ -70,12 +70,13 @@ EffectSet::GetEffectSet(const nsIFrame* aFrame)
|
||||
return nullptr;
|
||||
}
|
||||
} else {
|
||||
if (!content->MayHaveAnimations()) {
|
||||
return nullptr;
|
||||
}
|
||||
propName = nsGkAtoms::animationEffectsProperty;
|
||||
}
|
||||
|
||||
if (!content->MayHaveAnimations()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return static_cast<EffectSet*>(content->GetProperty(propName));
|
||||
}
|
||||
|
||||
@ -101,9 +102,7 @@ EffectSet::GetOrCreateEffectSet(dom::Element* aElement,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (aPseudoType == nsCSSPseudoElements::ePseudo_NotPseudoElement) {
|
||||
aElement->SetMayHaveAnimations();
|
||||
}
|
||||
aElement->SetMayHaveAnimations();
|
||||
|
||||
return effectSet;
|
||||
}
|
||||
|
@ -63,6 +63,7 @@ PendingAnimationTracker::TriggerPendingAnimationsOnNextTick(const TimeStamp&
|
||||
// itself on the next tick where it has a timeline.
|
||||
if (!timeline) {
|
||||
iter.Remove();
|
||||
continue;
|
||||
}
|
||||
|
||||
// When the timeline's refresh driver is under test control, its values
|
||||
|
@ -1352,10 +1352,11 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(FragmentOrElement)
|
||||
for (uint32_t i = 0; props[i]; ++i) {
|
||||
tmp->DeleteProperty(*props[i]);
|
||||
}
|
||||
// Bug 1226091: Call MayHaveAnimations() first
|
||||
nsIAtom** effectProps = EffectSet::GetEffectSetPropertyAtoms();
|
||||
for (uint32_t i = 0; effectProps[i]; ++i) {
|
||||
tmp->DeleteProperty(effectProps[i]);
|
||||
if (tmp->MayHaveAnimations()) {
|
||||
nsIAtom** effectProps = EffectSet::GetEffectSetPropertyAtoms();
|
||||
for (uint32_t i = 0; effectProps[i]; ++i) {
|
||||
tmp->DeleteProperty(effectProps[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1899,6 +1900,14 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(FragmentOrElement)
|
||||
|
||||
tmp->OwnerDoc()->BindingManager()->Traverse(tmp, cb);
|
||||
|
||||
// Check that whenever we have effect properties, MayHaveAnimations is set.
|
||||
#ifdef DEBUG
|
||||
nsIAtom** effectProps = EffectSet::GetEffectSetPropertyAtoms();
|
||||
for (uint32_t i = 0; effectProps[i]; ++i) {
|
||||
MOZ_ASSERT_IF(tmp->GetProperty(effectProps[i]), tmp->MayHaveAnimations());
|
||||
}
|
||||
#endif
|
||||
|
||||
if (tmp->HasProperties()) {
|
||||
if (tmp->IsHTMLElement() || tmp->IsSVGElement()) {
|
||||
nsIAtom*** props = Element::HTMLSVGPropertiesToTraverseAndUnlink();
|
||||
@ -1907,13 +1916,14 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(FragmentOrElement)
|
||||
static_cast<nsISupports*>(tmp->GetProperty(*props[i]));
|
||||
cb.NoteXPCOMChild(property);
|
||||
}
|
||||
// Bug 1226091: Check MayHaveAnimations() first
|
||||
nsIAtom** effectProps = EffectSet::GetEffectSetPropertyAtoms();
|
||||
for (uint32_t i = 0; effectProps[i]; ++i) {
|
||||
EffectSet* effectSet =
|
||||
static_cast<EffectSet*>(tmp->GetProperty(effectProps[i]));
|
||||
if (effectSet) {
|
||||
effectSet->Traverse(cb);
|
||||
if (tmp->MayHaveAnimations()) {
|
||||
nsIAtom** effectProps = EffectSet::GetEffectSetPropertyAtoms();
|
||||
for (uint32_t i = 0; effectProps[i]; ++i) {
|
||||
EffectSet* effectSet =
|
||||
static_cast<EffectSet*>(tmp->GetProperty(effectProps[i]));
|
||||
if (effectSet) {
|
||||
effectSet->Traverse(cb);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ CaptureStreamTestHelper.prototype = {
|
||||
blackTransparent: { data: [0, 0, 0, 0], name: "blackTransparent" },
|
||||
green: { data: [0, 255, 0, 255], name: "green" },
|
||||
red: { data: [255, 0, 0, 255], name: "red" },
|
||||
blue: { data: [0, 0, 255, 255], name: "blue"},
|
||||
grey: { data: [128, 128, 128, 255], name: "grey" },
|
||||
|
||||
/* Default element size for createAndAppendElement() */
|
||||
|
@ -231,11 +231,11 @@ Request::Constructor(const GlobalObject& aGlobal,
|
||||
RefPtr<Request> inputReq = &aInput.GetAsRequest();
|
||||
nsCOMPtr<nsIInputStream> body;
|
||||
inputReq->GetBody(getter_AddRefs(body));
|
||||
if (inputReq->BodyUsed()) {
|
||||
aRv.ThrowTypeError<MSG_FETCH_BODY_CONSUMED_ERROR>();
|
||||
return nullptr;
|
||||
}
|
||||
if (body) {
|
||||
if (inputReq->BodyUsed()) {
|
||||
aRv.ThrowTypeError<MSG_FETCH_BODY_CONSUMED_ERROR>();
|
||||
return nullptr;
|
||||
}
|
||||
temporaryBody = body;
|
||||
}
|
||||
|
||||
|
@ -1925,8 +1925,7 @@ MediaManager::GetUserMedia(nsPIDOMWindow* aWindow,
|
||||
nsPIDOMWindow *outer = aWindow->GetOuterWindow();
|
||||
vc.mBrowserWindow.Construct(outer->WindowID());
|
||||
}
|
||||
// | Fall through
|
||||
// V
|
||||
MOZ_FALLTHROUGH;
|
||||
case dom::MediaSourceEnum::Screen:
|
||||
case dom::MediaSourceEnum::Application:
|
||||
case dom::MediaSourceEnum::Window:
|
||||
|
@ -252,7 +252,7 @@ already_AddRefed<SharedThreadPool> GetMediaThreadPool(MediaThreadType aType)
|
||||
name = "MediaPDecoder";
|
||||
break;
|
||||
default:
|
||||
MOZ_ASSERT(false);
|
||||
MOZ_FALLTHROUGH_ASSERT("Unexpected MediaThreadType");
|
||||
case MediaThreadType::PLAYBACK:
|
||||
name = "MediaPlayback";
|
||||
break;
|
||||
|
@ -90,10 +90,10 @@ MediaCodecProxy::MediaCodecProxy(sp<ALooper> aLooper,
|
||||
: mCodecLooper(aLooper)
|
||||
, mCodecMime(aMime)
|
||||
, mCodecEncoder(aEncoder)
|
||||
, mMediaCodecLock("MediaCodecProxy::mMediaCodecLock")
|
||||
, mPendingRequestMediaResource(false)
|
||||
, mPromiseMonitor("MediaCodecProxy::mPromiseMonitor")
|
||||
{
|
||||
MOZ_ASSERT(mCodecLooper != nullptr, "ALooper should not be nullptr.");
|
||||
mCodecPromise.SetMonitor(&mPromiseMonitor);
|
||||
}
|
||||
|
||||
MediaCodecProxy::~MediaCodecProxy()
|
||||
@ -134,6 +134,7 @@ MediaCodecProxy::AsyncAllocateVideoMediaCodec()
|
||||
mResourceClient->SetListener(this);
|
||||
mResourceClient->Acquire();
|
||||
|
||||
mozilla::MonitorAutoLock lock(mPromiseMonitor);
|
||||
RefPtr<CodecPromise> p = mCodecPromise.Ensure(__func__);
|
||||
return p.forget();
|
||||
}
|
||||
@ -141,23 +142,16 @@ MediaCodecProxy::AsyncAllocateVideoMediaCodec()
|
||||
void
|
||||
MediaCodecProxy::ReleaseMediaCodec()
|
||||
{
|
||||
mCodecPromise.RejectIfExists(true, __func__);
|
||||
releaseCodec();
|
||||
|
||||
if (!mResourceClient) {
|
||||
return;
|
||||
}
|
||||
|
||||
mozilla::MonitorAutoLock mon(mMediaCodecLock);
|
||||
if (mPendingRequestMediaResource) {
|
||||
mPendingRequestMediaResource = false;
|
||||
mon.NotifyAll();
|
||||
}
|
||||
|
||||
// At first, release mResourceClient's resource to prevent a conflict with
|
||||
// mResourceClient's callback.
|
||||
if (mResourceClient) {
|
||||
mResourceClient->ReleaseResource();
|
||||
mResourceClient = nullptr;
|
||||
}
|
||||
|
||||
mozilla::MonitorAutoLock lock(mPromiseMonitor);
|
||||
mCodecPromise.RejectIfExists(true, __func__);
|
||||
releaseCodec();
|
||||
}
|
||||
|
||||
bool
|
||||
@ -473,18 +467,12 @@ void
|
||||
MediaCodecProxy::ResourceReserved()
|
||||
{
|
||||
MCP_LOG("resourceReserved");
|
||||
mozilla::MonitorAutoLock lock(mPromiseMonitor);
|
||||
// Create MediaCodec
|
||||
if (!allocateCodec()) {
|
||||
ReleaseMediaCodec();
|
||||
mCodecPromise.RejectIfExists(true, __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
// Notify initialization waiting.
|
||||
mozilla::MonitorAutoLock mon(mMediaCodecLock);
|
||||
mPendingRequestMediaResource = false;
|
||||
mon.NotifyAll();
|
||||
|
||||
mCodecPromise.ResolveIfExists(true, __func__);
|
||||
}
|
||||
|
||||
@ -492,7 +480,7 @@ MediaCodecProxy::ResourceReserved()
|
||||
void
|
||||
MediaCodecProxy::ResourceReserveFailed()
|
||||
{
|
||||
ReleaseMediaCodec();
|
||||
mozilla::MonitorAutoLock lock(mPromiseMonitor);
|
||||
mCodecPromise.RejectIfExists(true, __func__);
|
||||
}
|
||||
|
||||
|
@ -163,6 +163,8 @@ private:
|
||||
bool mCodecEncoder;
|
||||
|
||||
mozilla::MozPromiseHolder<CodecPromise> mCodecPromise;
|
||||
// When mPromiseMonitor is held, mResourceClient's functions should not be called.
|
||||
mozilla::Monitor mPromiseMonitor;
|
||||
|
||||
// Media Resource Management
|
||||
RefPtr<mozilla::MediaSystemResourceClient> mResourceClient;
|
||||
@ -174,9 +176,6 @@ private:
|
||||
//MediaCodec buffers to hold input/output data.
|
||||
Vector<sp<ABuffer> > mInputBuffers;
|
||||
Vector<sp<ABuffer> > mOutputBuffers;
|
||||
|
||||
mozilla::Monitor mMediaCodecLock;
|
||||
bool mPendingRequestMediaResource;
|
||||
};
|
||||
|
||||
} // namespace android
|
||||
|
@ -64,6 +64,13 @@ OMXCodecProxy::OMXCodecProxy(
|
||||
|
||||
OMXCodecProxy::~OMXCodecProxy()
|
||||
{
|
||||
// At first, release mResourceClient's resource to prevent a conflict with
|
||||
// mResourceClient's callback.
|
||||
if (mResourceClient) {
|
||||
mResourceClient->ReleaseResource();
|
||||
mResourceClient = nullptr;
|
||||
}
|
||||
|
||||
mState = ResourceState::END;
|
||||
mCodecPromise.RejectIfExists(true, __func__);
|
||||
|
||||
@ -78,11 +85,6 @@ OMXCodecProxy::~OMXCodecProxy()
|
||||
// Complete all pending Binder ipc transactions
|
||||
IPCThreadState::self()->flushCommands();
|
||||
|
||||
if (mResourceClient) {
|
||||
mResourceClient->ReleaseResource();
|
||||
mResourceClient = nullptr;
|
||||
}
|
||||
|
||||
mSource.clear();
|
||||
free(mComponentName);
|
||||
mComponentName = nullptr;
|
||||
|
@ -112,6 +112,8 @@ tags=capturestream
|
||||
skip-if = toolkit == 'gonk' || buildapp == 'mulet' || android_version == '18' # b2g(Bug 960442, video support for WebRTC is disabled on b2g), android(Bug 1189784, timeouts on 4.3 emulator)
|
||||
[test_peerConnection_captureStream_canvas_2d.html]
|
||||
skip-if = toolkit == 'gonk' || buildapp == 'mulet' || (android_version == '18' && debug) # b2g(Bug 960442, video support for WebRTC is disabled on b2g), android(Bug 1189784, timeouts on 4.3 emulator)
|
||||
[test_peerConnection_multiple_captureStream_canvas_2d.html]
|
||||
skip-if = toolkit == 'gonk' || buildapp == 'mulet' || (android_version == '18' && debug) # b2g(Bug 960442, video support for WebRTC is disabled on b2g), android(Bug 1189784, timeouts on 4.3 emulator)
|
||||
[test_peerConnection_captureStream_canvas_webgl.html]
|
||||
skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
|
||||
# [test_peerConnection_certificates.html] # bug 1180968
|
||||
@ -206,10 +208,13 @@ skip-if = toolkit == 'gonk' || android_version == '18'
|
||||
[test_peerConnection_addDataChannel.html]
|
||||
skip-if = toolkit == 'gonk' # B2G emulator seems to be so slow that DTLS cannot establish properly
|
||||
[test_peerConnection_addDataChannelNoBundle.html]
|
||||
# b2g(Bug 960442, video support for WebRTC is disabled on b2g), android(Bug 1189784, timeouts on 4.3 emulator)
|
||||
skip-if = toolkit == 'gonk' || (android_version == '18' && debug) # b2g(emulator seems to be so slow that DTLS cannot establish properly), android(bug 1240256, intermittent ICE failures starting w/bug 1232082, possibly from timeout)
|
||||
|
||||
# b2g(Bug 960442, video support for WebRTC is disabled on b2g), android(Bug 1189784, timeouts on 4.3 emulator)
|
||||
|
||||
[test_peerConnection_verifyAudioAfterRenegotiation.html]
|
||||
skip-if = toolkit == 'gonk' || (android_version == '18' && debug) # B2G emulator is too slow to finish a renegotiation test in under 5 minutes, android(Bug 1189784, timeouts on 4.3 emulator)
|
||||
[test_peerConnection_verifyVideoAfterRenegotiation.html]
|
||||
# B2G emulator is too slow to finish a renegotiation test in under 5 minutes, Bug 1180000 for Linux debug e10s, android(Bug 1189784, timeouts on 4.3 emulator)
|
||||
skip-if = toolkit == 'gonk' || android_version == '18'
|
||||
[test_peerConnection_webAudio.html]
|
||||
tags = webaudio webrtc
|
||||
skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g (Bug 1059867)
|
||||
|
@ -0,0 +1,100 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<script type="application/javascript" src="pc.js"></script>
|
||||
<script type="application/javascript" src="/tests/dom/canvas/test/captureStream_common.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script type="application/javascript">
|
||||
createHTML({
|
||||
bug: "1166832",
|
||||
title: "Canvas(2D)::Multiple CaptureStream as video-only input to peerconnection",
|
||||
visible: true
|
||||
});
|
||||
|
||||
/**
|
||||
* Test to verify using multiple capture streams concurrently.
|
||||
*/
|
||||
runNetworkTest(() => {
|
||||
var test = new PeerConnectionTest();
|
||||
var h = new CaptureStreamTestHelper2D(50, 50);
|
||||
|
||||
var vremote1;
|
||||
var stream1;
|
||||
var canvas1 = h.createAndAppendElement('canvas', 'source_canvas1');
|
||||
|
||||
var vremote2;
|
||||
var stream2;
|
||||
var canvas2 = h.createAndAppendElement('canvas', 'source_canvas2');
|
||||
|
||||
test.setMediaConstraints([{video: true}, {video: true}], []);
|
||||
test.chain.replace("PC_LOCAL_GUM", [
|
||||
function DRAW_INITIAL_LOCAL1_GREEN(test) {
|
||||
h.drawColor(canvas1, h.green);
|
||||
},
|
||||
function DRAW_INITIAL_LOCAL2_BLUE(test) {
|
||||
h.drawColor(canvas2, h.blue);
|
||||
},
|
||||
function PC_LOCAL_CANVAS_CAPTURESTREAM(test) {
|
||||
stream1 = canvas1.captureStream(0); // fps = 0 to capture single frame
|
||||
test.pcLocal.attachMedia(stream1, 'video', 'local');
|
||||
stream2 = canvas2.captureStream(0); // fps = 0 to capture single frame
|
||||
test.pcLocal.attachMedia(stream2, 'video', 'local');
|
||||
}
|
||||
]);
|
||||
|
||||
test.chain.append([
|
||||
function CHECK_REMOTE_VIDEO() {
|
||||
var testremote = document.getElementById('pcRemote_remote3_video');
|
||||
ok(!testremote, "Should not have remote3 video element for pcRemote");
|
||||
vremote1 = document.getElementById('pcRemote_remote1_video');
|
||||
vremote2 = document.getElementById('pcRemote_remote2_video');
|
||||
|
||||
// since we don't know which remote video is created first, we don't know
|
||||
// which should be blue or green, but this will make sure that one is
|
||||
// green and one is blue
|
||||
return Promise.race([
|
||||
Promise.all([
|
||||
h.waitForPixelColor(vremote1, h.green, 128,
|
||||
"pcRemote's remote1 should become green"),
|
||||
h.waitForPixelColor(vremote2, h.blue, 128,
|
||||
"pcRemote's remote2 should become blue")
|
||||
]),
|
||||
Promise.all([
|
||||
h.waitForPixelColor(vremote2, h.green, 128,
|
||||
"pcRemote's remote2 should become green"),
|
||||
h.waitForPixelColor(vremote1, h.blue, 128,
|
||||
"pcRemote's remote1 should become blue")
|
||||
])
|
||||
]);
|
||||
},
|
||||
function DRAW_LOCAL1_RED() {
|
||||
// After requesting a frame it will be captured at the time of next render.
|
||||
// Next render will happen at next stable state, at the earliest,
|
||||
// i.e., this order of `requestFrame(); draw();` should work.
|
||||
stream1.requestFrame();
|
||||
h.drawColor(canvas1, h.red);
|
||||
},
|
||||
function DRAW_LOCAL2_RED() {
|
||||
// After requesting a frame it will be captured at the time of next render.
|
||||
// Next render will happen at next stable state, at the earliest,
|
||||
// i.e., this order of `requestFrame(); draw();` should work.
|
||||
stream2.requestFrame();
|
||||
h.drawColor(canvas2, h.red);
|
||||
},
|
||||
function WAIT_FOR_REMOTE1_RED() {
|
||||
return h.waitForPixelColor(vremote1, h.red, 128,
|
||||
"pcRemote's remote1 should become red");
|
||||
},
|
||||
function WAIT_FOR_REMOTE2_RED() {
|
||||
return h.waitForPixelColor(vremote2, h.red, 128,
|
||||
"pcRemote's remote2 should become red");
|
||||
}
|
||||
]);
|
||||
test.run();
|
||||
});
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,123 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<script type="application/javascript" src="pc.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script type="application/javascript">
|
||||
createHTML({
|
||||
bug: "1166832",
|
||||
title: "Renegotiation: verify audio after renegotiation"
|
||||
});
|
||||
|
||||
var test;
|
||||
runNetworkTest(function (options) {
|
||||
test = new PeerConnectionTest(options);
|
||||
|
||||
var checkAudio = (analyser, fun) => {
|
||||
analyser.enableDebugCanvas();
|
||||
return analyser.waitForAnalysisSuccess(fun)
|
||||
.then(() => analyser.disableDebugCanvas());
|
||||
};
|
||||
var checkAudioEnabled = (analyser, freq) =>
|
||||
checkAudio(analyser, array => array[freq] > 200);
|
||||
var checkAudioDisabled = (analyser, freq) =>
|
||||
checkAudio(analyser, array => array[freq] < 50);
|
||||
|
||||
var ac = new AudioContext();
|
||||
var local1Analyser;
|
||||
var remote1Analyser;
|
||||
|
||||
test.chain.append([
|
||||
function CHECK_ASSUMPTIONS() {
|
||||
is(test.pcLocal.mediaElements.length, 1,
|
||||
"pcLocal should only have one media element");
|
||||
is(test.pcRemote.mediaElements.length, 1,
|
||||
"pcRemote should only have one media element");
|
||||
is(test.pcLocal.streams.length, 1,
|
||||
"pcLocal should only have one stream (the local one)");
|
||||
is(test.pcRemote.streams.length, 1,
|
||||
"pcRemote should only have one stream (the remote one)");
|
||||
},
|
||||
function CHECK_AUDIO() {
|
||||
local1Analyser = new AudioStreamAnalyser(ac, test.pcLocal.streams[0]);
|
||||
remote1Analyser = new AudioStreamAnalyser(ac, test.pcRemote.streams[0]);
|
||||
|
||||
freq1k = local1Analyser.binIndexForFrequency(1000);
|
||||
|
||||
return Promise.resolve()
|
||||
.then(() => info("Checking local audio enabled"))
|
||||
.then(() => checkAudioEnabled(local1Analyser, freq1k))
|
||||
.then(() => info("Checking remote audio enabled"))
|
||||
.then(() => checkAudioEnabled(remote1Analyser, freq1k))
|
||||
|
||||
.then(() => test.pcLocal.streams[0].getAudioTracks()[0].enabled = false)
|
||||
|
||||
.then(() => info("Checking local audio disabled"))
|
||||
.then(() => checkAudioDisabled(local1Analyser, freq1k))
|
||||
.then(() => info("Checking remote audio disabled"))
|
||||
.then(() => checkAudioDisabled(remote1Analyser, freq1k))
|
||||
}
|
||||
]);
|
||||
|
||||
addRenegotiation(test.chain,
|
||||
[
|
||||
function PC_LOCAL_ADD_SECOND_STREAM(test) {
|
||||
test.setMediaConstraints([{audio: true}],
|
||||
[]);
|
||||
return test.pcLocal.getAllUserMedia([{audio: true}]);
|
||||
},
|
||||
]
|
||||
);
|
||||
|
||||
test.chain.append([
|
||||
function CHECK_ASSUMPTIONS2() {
|
||||
is(test.pcLocal.mediaElements.length, 2,
|
||||
"pcLocal should have two media elements");
|
||||
is(test.pcRemote.mediaElements.length, 2,
|
||||
"pcRemote should have two media elements");
|
||||
is(test.pcLocal.streams.length, 2,
|
||||
"pcLocal should have two streams");
|
||||
is(test.pcRemote.streams.length, 2,
|
||||
"pcRemote should have two streams");
|
||||
},
|
||||
function RE_CHECK_AUDIO() {
|
||||
local2Analyser = new AudioStreamAnalyser(ac, test.pcLocal.streams[1]);
|
||||
remote2Analyser = new AudioStreamAnalyser(ac, test.pcRemote.streams[1]);
|
||||
|
||||
freq1k = local2Analyser.binIndexForFrequency(1000);
|
||||
|
||||
return Promise.resolve()
|
||||
.then(() => info("Checking local audio disabled"))
|
||||
.then(() => checkAudioDisabled(local1Analyser, freq1k))
|
||||
.then(() => info("Checking remote audio disabled"))
|
||||
.then(() => checkAudioDisabled(remote1Analyser, freq1k))
|
||||
|
||||
.then(() => info("Checking local2 audio enabled"))
|
||||
.then(() => checkAudioEnabled(local2Analyser, freq1k))
|
||||
.then(() => info("Checking remote2 audio enabled"))
|
||||
.then(() => checkAudioEnabled(remote2Analyser, freq1k))
|
||||
|
||||
.then(() => test.pcLocal.streams[1].getAudioTracks()[0].enabled = false)
|
||||
.then(() => test.pcLocal.streams[0].getAudioTracks()[0].enabled = true)
|
||||
|
||||
.then(() => info("Checking local2 audio disabled"))
|
||||
.then(() => checkAudioDisabled(local2Analyser, freq1k))
|
||||
.then(() => info("Checking remote2 audio disabled"))
|
||||
.then(() => checkAudioDisabled(remote2Analyser, freq1k))
|
||||
|
||||
.then(() => info("Checking local audio enabled"))
|
||||
.then(() => checkAudioEnabled(local1Analyser, freq1k))
|
||||
.then(() => info("Checking remote audio enabled"))
|
||||
.then(() => checkAudioEnabled(remote1Analyser, freq1k))
|
||||
}
|
||||
]);
|
||||
|
||||
test.setMediaConstraints([{audio: true}], []);
|
||||
test.run();
|
||||
});
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,100 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<script type="application/javascript" src="pc.js"></script>
|
||||
<script type="application/javascript" src="/tests/dom/canvas/test/captureStream_common.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script type="application/javascript">
|
||||
createHTML({
|
||||
bug: "1166832",
|
||||
title: "Renegotiation: verify video after renegotiation"
|
||||
});
|
||||
|
||||
runNetworkTest(() => {
|
||||
var test = new PeerConnectionTest();
|
||||
|
||||
var h1 = new CaptureStreamTestHelper2D(50, 50);
|
||||
var canvas1 = h1.createAndAppendElement('canvas', 'source_canvas1');
|
||||
var stream1;
|
||||
var vremote1;
|
||||
|
||||
var h2 = new CaptureStreamTestHelper2D(50, 50);
|
||||
var canvas2;
|
||||
var stream2;
|
||||
var vremote2;
|
||||
|
||||
test.setMediaConstraints([{video: true}], []);
|
||||
test.chain.replace("PC_LOCAL_GUM", [
|
||||
function DRAW_INITIAL_LOCAL_GREEN(test) {
|
||||
h1.drawColor(canvas1, h1.green);
|
||||
},
|
||||
function PC_LOCAL_CANVAS_CAPTURESTREAM(test) {
|
||||
stream1 = canvas1.captureStream(0);
|
||||
test.pcLocal.attachMedia(stream1, 'video', 'local');
|
||||
}
|
||||
]);
|
||||
|
||||
test.chain.append([
|
||||
function FIND_REMOTE_VIDEO() {
|
||||
vremote1 = document.getElementById('pcRemote_remote1_video');
|
||||
ok(!!vremote1, "Should have remote video element for pcRemote");
|
||||
},
|
||||
function WAIT_FOR_REMOTE_GREEN() {
|
||||
return h1.waitForPixelColor(vremote1, h1.green, 128,
|
||||
"pcRemote's remote should become green");
|
||||
},
|
||||
function DRAW_LOCAL_RED() {
|
||||
// After requesting a frame it will be captured at the time of next render.
|
||||
// Next render will happen at next stable state, at the earliest,
|
||||
// i.e., this order of `requestFrame(); draw();` should work.
|
||||
stream1.requestFrame();
|
||||
h1.drawColor(canvas1, h1.red);
|
||||
},
|
||||
function WAIT_FOR_REMOTE_RED() {
|
||||
return h1.waitForPixelColor(vremote1, h1.red, 128,
|
||||
"pcRemote's remote should become red");
|
||||
}
|
||||
]);
|
||||
|
||||
addRenegotiation(test.chain,
|
||||
[
|
||||
function PC_LOCAL_ADD_SECOND_STREAM(test) {
|
||||
canvas2 = h2.createAndAppendElement('canvas', 'source_canvas2');
|
||||
h2.drawColor(canvas2, h2.blue);
|
||||
stream2 = canvas2.captureStream(0);
|
||||
|
||||
// can't use test.pcLocal.getAllUserMedia([{video: true}]);
|
||||
// because it doesn't let us substitute the capture stream
|
||||
return test.pcLocal.attachMedia(stream2, 'video', 'local');
|
||||
}
|
||||
]
|
||||
);
|
||||
|
||||
test.chain.append([
|
||||
function FIND_REMOTE2_VIDEO() {
|
||||
vremote2 = document.getElementById('pcRemote_remote2_video');
|
||||
ok(!!vremote2, "Should have remote2 video element for pcRemote");
|
||||
},
|
||||
function WAIT_FOR_REMOTE2_BLUE() {
|
||||
return h2.waitForPixelColor(vremote2, h2.blue, 128,
|
||||
"pcRemote's remote2 should become blue");
|
||||
},
|
||||
function DRAW_NEW_LOCAL_GREEN(test) {
|
||||
stream1.requestFrame();
|
||||
h1.drawColor(canvas1, h1.green);
|
||||
},
|
||||
function WAIT_FOR_REMOTE1_GREEN() {
|
||||
return h1.waitForPixelColor(vremote1, h1.green, 128,
|
||||
"pcRemote's remote1 should become green");
|
||||
}
|
||||
]);
|
||||
|
||||
test.run();
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -569,7 +569,7 @@ public:
|
||||
aPrevious->mCurve, aPrevious->mCurveLength,
|
||||
aPrevious->mDuration, aTime);
|
||||
case AudioTimelineEvent::SetTarget:
|
||||
MOZ_ASSERT(false, "unreached");
|
||||
MOZ_FALLTHROUGH_ASSERT("AudioTimelineEvent::SetTarget");
|
||||
case AudioTimelineEvent::SetValue:
|
||||
case AudioTimelineEvent::Cancel:
|
||||
case AudioTimelineEvent::Stream:
|
||||
@ -617,7 +617,7 @@ public:
|
||||
aPrevious->mCurve, aPrevious->mCurveLength,
|
||||
aPrevious->mDuration, aTime);
|
||||
case AudioTimelineEvent::SetTarget:
|
||||
MOZ_ASSERT(false, "unreached");
|
||||
MOZ_FALLTHROUGH_ASSERT("AudioTimelineEvent::SetTarget");
|
||||
case AudioTimelineEvent::SetValue:
|
||||
case AudioTimelineEvent::Cancel:
|
||||
case AudioTimelineEvent::Stream:
|
||||
|
@ -570,7 +570,7 @@ WebAudioDecodeJob::OnFailure(ErrorCode aErrorCode)
|
||||
const char* errorMessage;
|
||||
switch (aErrorCode) {
|
||||
case NoError:
|
||||
MOZ_ASSERT(false, "Who passed NoError to OnFailure?");
|
||||
MOZ_FALLTHROUGH_ASSERT("Who passed NoError to OnFailure?");
|
||||
// Fall through to get some sort of a sane error message if this actually
|
||||
// happens at runtime.
|
||||
case UnknownError:
|
||||
|
@ -133,7 +133,7 @@ void WebMBufferedParser::Append(const unsigned char* aBuffer, uint32_t aLength,
|
||||
case EBML_ID:
|
||||
mLastInitStartOffset = mCurrentOffset + (p - aBuffer) -
|
||||
(mElement.mID.mLength + mElement.mSize.mLength);
|
||||
/* FALLTHROUGH */
|
||||
MOZ_FALLTHROUGH;
|
||||
default:
|
||||
mSkipBytes = mElement.mSize.mValue;
|
||||
mState = SKIP_DATA;
|
||||
|
@ -187,7 +187,7 @@ ParseClockValue(RangedPtr<const char16_t>& aIter,
|
||||
!ParseColon(iter, aEnd)) {
|
||||
return false;
|
||||
}
|
||||
// intentional fall through
|
||||
MOZ_FALLTHROUGH;
|
||||
case PARTIAL_CLOCK_VALUE:
|
||||
if (!ParseSecondsOrMinutes(iter, aEnd, minutes) ||
|
||||
!ParseColon(iter, aEnd) ||
|
||||
|
@ -352,7 +352,7 @@ ConvertPathSegmentData(SVGPathDataAndInfo::const_iterator& aStart,
|
||||
aResult[5] = aStart[5];
|
||||
aResult[6] = aStart[6];
|
||||
AdjustSegmentForRelativeness(adjustmentType, aResult + 5, aState);
|
||||
// fall through
|
||||
MOZ_FALLTHROUGH;
|
||||
case PATHSEG_CURVETO_QUADRATIC_ABS:
|
||||
case PATHSEG_CURVETO_QUADRATIC_REL:
|
||||
case PATHSEG_CURVETO_CUBIC_SMOOTH_ABS:
|
||||
@ -360,7 +360,7 @@ ConvertPathSegmentData(SVGPathDataAndInfo::const_iterator& aStart,
|
||||
aResult[3] = aStart[3];
|
||||
aResult[4] = aStart[4];
|
||||
AdjustSegmentForRelativeness(adjustmentType, aResult + 3, aState);
|
||||
// fall through
|
||||
MOZ_FALLTHROUGH;
|
||||
case PATHSEG_MOVETO_ABS:
|
||||
case PATHSEG_MOVETO_REL:
|
||||
case PATHSEG_LINETO_ABS:
|
||||
|
@ -143,7 +143,7 @@ SVGTransformListParser::ParseTranslate()
|
||||
switch (count) {
|
||||
case 1:
|
||||
t[1] = 0.f;
|
||||
// fall-through
|
||||
MOZ_FALLTHROUGH;
|
||||
case 2:
|
||||
{
|
||||
nsSVGTransform* transform = mTransforms.AppendElement(fallible);
|
||||
@ -171,7 +171,7 @@ SVGTransformListParser::ParseScale()
|
||||
switch (count) {
|
||||
case 1:
|
||||
s[1] = s[0];
|
||||
// fall-through
|
||||
MOZ_FALLTHROUGH;
|
||||
case 2:
|
||||
{
|
||||
nsSVGTransform* transform = mTransforms.AppendElement(fallible);
|
||||
@ -200,7 +200,7 @@ SVGTransformListParser::ParseRotate()
|
||||
switch (count) {
|
||||
case 1:
|
||||
r[1] = r[2] = 0.f;
|
||||
// fall-through
|
||||
MOZ_FALLTHROUGH;
|
||||
case 3:
|
||||
{
|
||||
nsSVGTransform* transform = mTransforms.AppendElement(fallible);
|
||||
|
@ -124,6 +124,19 @@ function testBug1109574() {
|
||||
var r3 = new Request(r1);
|
||||
}
|
||||
|
||||
// Bug 1184550 - Request constructor should always throw if used flag is set,
|
||||
// even if body is null
|
||||
function testBug1184550() {
|
||||
var req = new Request("", { method: 'post', body: "Test" });
|
||||
fetch(req);
|
||||
ok(req.bodyUsed, "Request body should be used immediately after fetch()");
|
||||
return fetch(req).then(function(resp) {
|
||||
ok(false, "Second fetch with same request should fail.");
|
||||
}).catch(function(err) {
|
||||
is(err.name, 'TypeError', "Second fetch with same request should fail.");
|
||||
});
|
||||
}
|
||||
|
||||
function testHeaderGuard() {
|
||||
var headers = {
|
||||
"Cookie": "Custom cookie",
|
||||
@ -500,6 +513,7 @@ function runTest() {
|
||||
testUrlMalformed();
|
||||
testMethod();
|
||||
testBug1109574();
|
||||
testBug1184550();
|
||||
testHeaderGuard();
|
||||
testModeCorsPreflightEnumValue();
|
||||
testBug1154268();
|
||||
|
@ -12,7 +12,9 @@
|
||||
|
||||
enum AnimationPlayState { "idle", "pending", "running", "paused", "finished" };
|
||||
|
||||
[Func="nsDocument::IsWebAnimationsEnabled"]
|
||||
[Func="nsDocument::IsWebAnimationsEnabled",
|
||||
Constructor (optional KeyframeEffectReadOnly? effect = null,
|
||||
optional AnimationTimeline? timeline = null)]
|
||||
interface Animation : EventTarget {
|
||||
attribute DOMString id;
|
||||
// Bug 1049975: Make 'effect' writeable
|
||||
|
@ -76,8 +76,7 @@ LayerManager::GetRootScrollableLayerId()
|
||||
return FrameMetrics::NULL_SCROLL_ID;
|
||||
}
|
||||
|
||||
nsTArray<LayerMetricsWrapper> queue;
|
||||
queue.AppendElement(LayerMetricsWrapper(mRoot));
|
||||
nsTArray<LayerMetricsWrapper> queue = { LayerMetricsWrapper(mRoot) };
|
||||
while (queue.Length()) {
|
||||
LayerMetricsWrapper layer = queue[0];
|
||||
queue.RemoveElementAt(0);
|
||||
@ -110,8 +109,7 @@ LayerManager::GetRootScrollableLayers(nsTArray<Layer*>& aArray)
|
||||
return;
|
||||
}
|
||||
|
||||
nsTArray<Layer*> queue;
|
||||
queue.AppendElement(mRoot);
|
||||
nsTArray<Layer*> queue = { mRoot };
|
||||
while (queue.Length()) {
|
||||
Layer* layer = queue[0];
|
||||
queue.RemoveElementAt(0);
|
||||
@ -134,8 +132,7 @@ LayerManager::GetScrollableLayers(nsTArray<Layer*>& aArray)
|
||||
return;
|
||||
}
|
||||
|
||||
nsTArray<Layer*> queue;
|
||||
queue.AppendElement(mRoot);
|
||||
nsTArray<Layer*> queue = { mRoot };
|
||||
while (!queue.IsEmpty()) {
|
||||
Layer* layer = queue.LastElement();
|
||||
queue.RemoveElementAt(queue.Length() - 1);
|
||||
|
@ -890,9 +890,8 @@ TEST_F(APZCPinchGestureDetectorTester, Pinch_UseGestureDetector_NoTouchAction) {
|
||||
|
||||
TEST_F(APZCPinchGestureDetectorTester, Pinch_UseGestureDetector_TouchActionNone) {
|
||||
SCOPED_GFX_PREF(TouchActionEnabled, bool, true);
|
||||
nsTArray<uint32_t> behaviors;
|
||||
behaviors.AppendElement(mozilla::layers::AllowedTouchBehavior::NONE);
|
||||
behaviors.AppendElement(mozilla::layers::AllowedTouchBehavior::NONE);
|
||||
nsTArray<uint32_t> behaviors = { mozilla::layers::AllowedTouchBehavior::NONE,
|
||||
mozilla::layers::AllowedTouchBehavior::NONE };
|
||||
DoPinchTest(false, &behaviors);
|
||||
}
|
||||
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "gfx2DGlue.h"
|
||||
#include "gfxUtils.h"
|
||||
#include "mozilla/gfx/2D.h"
|
||||
#include "GeckoProfiler.h"
|
||||
|
||||
using namespace mozilla::gfx;
|
||||
|
||||
@ -227,6 +228,7 @@ TextureSourceD3D9::DataToTexture(DeviceManagerD3D9* aDeviceManager,
|
||||
_D3DFORMAT aFormat,
|
||||
uint32_t aBPP)
|
||||
{
|
||||
PROFILER_LABEL_FUNC(js::ProfileEntry::Category::GRAPHICS);
|
||||
RefPtr<IDirect3DSurface9> surface;
|
||||
D3DLOCKED_RECT lockedRect;
|
||||
RefPtr<IDirect3DTexture9> texture = InitTextures(aDeviceManager, aSize, aFormat,
|
||||
@ -322,6 +324,7 @@ DataTextureSourceD3D9::Update(gfx::DataSourceSurface* aSurface,
|
||||
nsIntRegion* aDestRegion,
|
||||
gfx::IntPoint* aSrcOffset)
|
||||
{
|
||||
PROFILER_LABEL_FUNC(js::ProfileEntry::Category::GRAPHICS);
|
||||
// Right now we only support full surface update. If aDestRegion is provided,
|
||||
// It will be ignored. Incremental update with a source offset is only used
|
||||
// on Mac so it is not clear that we ever will need to support it for D3D.
|
||||
@ -636,6 +639,7 @@ DXGID3D9TextureData::Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
|
||||
TextureFlags aFlags,
|
||||
IDirect3DDevice9* aDevice)
|
||||
{
|
||||
PROFILER_LABEL_FUNC(js::ProfileEntry::Category::GRAPHICS);
|
||||
MOZ_ASSERT(aFormat == gfx::SurfaceFormat::B8G8R8X8);
|
||||
if (aFormat != gfx::SurfaceFormat::B8G8R8X8) {
|
||||
return nullptr;
|
||||
@ -707,6 +711,7 @@ bool
|
||||
DataTextureSourceD3D9::UpdateFromTexture(IDirect3DTexture9* aTexture,
|
||||
const nsIntRegion* aRegion)
|
||||
{
|
||||
PROFILER_LABEL_FUNC(js::ProfileEntry::Category::GRAPHICS);
|
||||
MOZ_ASSERT(aTexture);
|
||||
|
||||
D3DSURFACE_DESC desc;
|
||||
|
@ -105,9 +105,8 @@ CommonAnimationManager::GetAnimationCollection(dom::Element *aElement,
|
||||
AnimationCollection::PropertyDtor(aElement, propName, collection, nullptr);
|
||||
return nullptr;
|
||||
}
|
||||
if (aPseudoType == nsCSSPseudoElements::ePseudo_NotPseudoElement) {
|
||||
aElement->SetMayHaveAnimations();
|
||||
}
|
||||
|
||||
aElement->SetMayHaveAnimations();
|
||||
|
||||
AddElementCollection(collection);
|
||||
}
|
||||
@ -124,9 +123,7 @@ CommonAnimationManager::GetAnimationCollection(const nsIFrame* aFrame)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (pseudoElement->second() ==
|
||||
nsCSSPseudoElements::ePseudo_NotPseudoElement &&
|
||||
!pseudoElement->first()->MayHaveAnimations()) {
|
||||
if (!pseudoElement->first()->MayHaveAnimations()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -112,7 +112,7 @@ MP4Metadata::~MP4Metadata()
|
||||
|
||||
#ifdef MOZ_RUST_MP4PARSE
|
||||
// Helper to test the rust parser on a data source.
|
||||
static bool try_rust(const UniquePtr<mp4parse_state, FreeMP4ParseState>& aRustState, RefPtr<Stream> aSource, int32_t* aCount)
|
||||
static bool try_rust(const UniquePtr<mp4parse_state, FreeMP4ParseState>& aRustState, RefPtr<Stream> aSource)
|
||||
{
|
||||
static LazyLogModule sLog("MP4Metadata");
|
||||
int64_t length;
|
||||
@ -129,9 +129,7 @@ static bool try_rust(const UniquePtr<mp4parse_state, FreeMP4ParseState>& aRustSt
|
||||
MOZ_LOG(sLog, LogLevel::Warning, ("Error copying mp4 data"));
|
||||
return false;
|
||||
}
|
||||
*aCount = mp4parse_read(aRustState.get(), buffer.data(), bytes_read);
|
||||
MOZ_LOG(sLog, LogLevel::Info, ("rust parser found %d tracks", int(*aCount)));
|
||||
return true;
|
||||
return mp4parse_read(aRustState.get(), buffer.data(), bytes_read);
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -139,12 +137,18 @@ uint32_t
|
||||
MP4Metadata::GetNumberTracks(mozilla::TrackInfo::TrackType aType) const
|
||||
{
|
||||
#ifdef MOZ_RUST_MP4PARSE
|
||||
static LazyLogModule sLog("MP4Metadata");
|
||||
// Try in rust first.
|
||||
mRustState.reset(mp4parse_new());
|
||||
int32_t rust_tracks = 0;
|
||||
bool rust_mp4parse_success = try_rust(mRustState, mSource, &rust_tracks);
|
||||
int32_t rust_mp4parse_success = try_rust(mRustState, mSource);
|
||||
Telemetry::Accumulate(Telemetry::MEDIA_RUST_MP4PARSE_SUCCESS,
|
||||
rust_mp4parse_success);
|
||||
rust_mp4parse_success == 0);
|
||||
if (rust_mp4parse_success < 0) {
|
||||
Telemetry::Accumulate(Telemetry::MEDIA_RUST_MP4PARSE_ERROR_CODE,
|
||||
-rust_mp4parse_success);
|
||||
}
|
||||
uint32_t rust_tracks = mp4parse_get_track_count(mRustState.get());
|
||||
MOZ_LOG(sLog, LogLevel::Info, ("rust parser found %u tracks", rust_tracks));
|
||||
#endif
|
||||
size_t tracks = mPrivate->mMetadataExtractor->countTracks();
|
||||
uint32_t total = 0;
|
||||
@ -176,7 +180,7 @@ MP4Metadata::GetNumberTracks(mozilla::TrackInfo::TrackType aType) const
|
||||
uint32_t rust_total = 0;
|
||||
const char* rust_track_type = nullptr;
|
||||
if (rust_mp4parse_success && rust_tracks > 0) {
|
||||
for (int32_t i = 0; i < rust_tracks; ++i) {
|
||||
for (uint32_t i = 0; i < rust_tracks; ++i) {
|
||||
mp4parse_track_info track_info;
|
||||
int32_t r = mp4parse_get_track_info(mRustState.get(), i, &track_info);
|
||||
switch (aType) {
|
||||
@ -197,7 +201,6 @@ MP4Metadata::GetNumberTracks(mozilla::TrackInfo::TrackType aType) const
|
||||
}
|
||||
}
|
||||
}
|
||||
static LazyLogModule sLog("MP4Metadata");
|
||||
MOZ_LOG(sLog, LogLevel::Info, ("%s tracks found: stagefright=%u rust=%u",
|
||||
rust_track_type, total, rust_total));
|
||||
switch (aType) {
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,8 +1,8 @@
|
||||
diff --git a/media/libstagefright/binding/byteorder/mod.rs b/media/libstagefright/binding/byteorder/mod.rs
|
||||
index 59ba692..9d2d1d5 100644
|
||||
index 7eea1e3..8a108cf 100644
|
||||
--- a/media/libstagefright/binding/byteorder/mod.rs
|
||||
+++ b/media/libstagefright/binding/byteorder/mod.rs
|
||||
@@ -36,16 +36,16 @@ assert_eq!(wtr, vec![5, 2, 0, 3]);
|
||||
@@ -36,7 +36,6 @@ assert_eq!(wtr, vec![5, 2, 0, 3]);
|
||||
```
|
||||
*/
|
||||
|
||||
@ -10,12 +10,14 @@ index 59ba692..9d2d1d5 100644
|
||||
#![doc(html_root_url = "http://burntsushi.net/rustdoc/byteorder")]
|
||||
|
||||
#![deny(missing_docs)]
|
||||
@@ -45,10 +44,11 @@ use std::mem::transmute;
|
||||
use std::ptr::copy_nonoverlapping;
|
||||
|
||||
use std::mem::transmute;
|
||||
|
||||
#[cfg(not(feature = "no-std"))]
|
||||
-pub use new::{ReadBytesExt, WriteBytesExt, Error, Result};
|
||||
+pub use byteorder::new::{ReadBytesExt, WriteBytesExt, Error, Result};
|
||||
|
||||
#[cfg(not(feature = "no-std"))]
|
||||
-mod new;
|
||||
+// Re-export new so gecko can build us as a mod intead of a crate.
|
||||
+pub mod new;
|
||||
@ -23,7 +25,7 @@ index 59ba692..9d2d1d5 100644
|
||||
#[inline]
|
||||
fn extend_sign(val: u64, nbytes: usize) -> i64 {
|
||||
diff --git a/media/libstagefright/binding/byteorder/new.rs b/media/libstagefright/binding/byteorder/new.rs
|
||||
index bbef0cd..a2e5393 100644
|
||||
index 54ee6a7..4efcbc3 100644
|
||||
--- a/media/libstagefright/binding/byteorder/new.rs
|
||||
+++ b/media/libstagefright/binding/byteorder/new.rs
|
||||
@@ -3,7 +3,7 @@ use std::fmt;
|
||||
|
@ -41,18 +41,48 @@ assert_eq!(wtr, vec![5, 2, 0, 3]);
|
||||
#![deny(missing_docs)]
|
||||
|
||||
use std::mem::transmute;
|
||||
use std::ptr::copy_nonoverlapping;
|
||||
|
||||
#[cfg(not(feature = "no-std"))]
|
||||
pub use byteorder::new::{ReadBytesExt, WriteBytesExt, Error, Result};
|
||||
|
||||
#[cfg(not(feature = "no-std"))]
|
||||
// Re-export new so gecko can build us as a mod intead of a crate.
|
||||
pub mod new;
|
||||
|
||||
#[inline]
|
||||
fn extend_sign(val: u64, nbytes: usize) -> i64 {
|
||||
let shift = (8 - nbytes) * 8;
|
||||
let shift = (8 - nbytes) * 8;
|
||||
(val << shift) as i64 >> shift
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn unextend_sign(val: i64, nbytes: usize) -> u64 {
|
||||
let shift = (8 - nbytes) * 8;
|
||||
(val << shift) as u64 >> shift
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn pack_size(n: u64) -> usize {
|
||||
if n < 1 << 8 {
|
||||
1
|
||||
} else if n < 1 << 16 {
|
||||
2
|
||||
} else if n < 1 << 24 {
|
||||
3
|
||||
} else if n < 1 << 32 {
|
||||
4
|
||||
} else if n < 1 << 40 {
|
||||
5
|
||||
} else if n < 1 << 48 {
|
||||
6
|
||||
} else if n < 1 << 56 {
|
||||
7
|
||||
} else {
|
||||
8
|
||||
}
|
||||
}
|
||||
|
||||
/// ByteOrder describes types that can serialize integers as bytes.
|
||||
///
|
||||
/// Note that `Self` does not appear anywhere in this trait's definition!
|
||||
@ -120,6 +150,12 @@ pub trait ByteOrder {
|
||||
/// Panics when `buf.len() < 8`.
|
||||
fn write_u64(buf: &mut [u8], n: u64);
|
||||
|
||||
/// Writes an unsigned integer `n` to `buf` using only `nbytes`.
|
||||
///
|
||||
/// If `n` is not representable in `nbytes`, or if `nbytes` is `> 8`, then
|
||||
/// this method panics.
|
||||
fn write_uint(buf: &mut [u8], n: u64, nbytes: usize);
|
||||
|
||||
/// Reads a signed 16 bit integer from `buf`.
|
||||
///
|
||||
/// Panics when `buf.len() < 2`.
|
||||
@ -193,6 +229,15 @@ pub trait ByteOrder {
|
||||
Self::write_u64(buf, n as u64)
|
||||
}
|
||||
|
||||
/// Writes a signed integer `n` to `buf` using only `nbytes`.
|
||||
///
|
||||
/// If `n` is not representable in `nbytes`, or if `nbytes` is `> 8`, then
|
||||
/// this method panics.
|
||||
#[inline]
|
||||
fn write_int(buf: &mut [u8], n: i64, nbytes: usize) {
|
||||
Self::write_uint(buf, unextend_sign(n, nbytes), nbytes)
|
||||
}
|
||||
|
||||
/// Writes a IEEE754 single-precision (4 bytes) floating point number.
|
||||
///
|
||||
/// Panics when `buf.len() < 4`.
|
||||
@ -238,41 +283,16 @@ pub type NativeEndian = BigEndian;
|
||||
|
||||
macro_rules! read_num_bytes {
|
||||
($ty:ty, $size:expr, $src:expr, $which:ident) => ({
|
||||
assert!($src.len() >= $size); // critical for memory safety!
|
||||
assert!($size <= $src.len());
|
||||
unsafe {
|
||||
(*($src.as_ptr() as *const $ty)).$which()
|
||||
}
|
||||
});
|
||||
($ty:ty, $size:expr, le $bytes:expr, $src:expr, $which:ident) => ({
|
||||
use std::ptr::copy_nonoverlapping;
|
||||
|
||||
assert!($bytes > 0 && $bytes < 9 && $bytes <= $src.len());
|
||||
let mut out = [0u8; $size];
|
||||
let ptr_out = out.as_mut_ptr();
|
||||
unsafe {
|
||||
copy_nonoverlapping($src.as_ptr(), ptr_out, $bytes);
|
||||
(*(ptr_out as *const $ty)).$which()
|
||||
}
|
||||
});
|
||||
($ty:ty, $size:expr, be $bytes:expr, $src:expr, $which:ident) => ({
|
||||
use std::ptr::copy_nonoverlapping;
|
||||
|
||||
assert!($bytes > 0 && $bytes < 9 && $bytes <= $src.len());
|
||||
let mut out = [0u8; $size];
|
||||
let ptr_out = out.as_mut_ptr();
|
||||
unsafe {
|
||||
copy_nonoverlapping($src.as_ptr(),
|
||||
ptr_out.offset((8 - $bytes) as isize), $bytes);
|
||||
(*(ptr_out as *const $ty)).$which()
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
macro_rules! write_num_bytes {
|
||||
($ty:ty, $size:expr, $n:expr, $dst:expr, $which:ident) => ({
|
||||
use std::ptr::copy_nonoverlapping;
|
||||
|
||||
assert!($dst.len() >= $size); // critical for memory safety!
|
||||
assert!($size <= $dst.len());
|
||||
unsafe {
|
||||
// N.B. https://github.com/rust-lang/rust/issues/22776
|
||||
let bytes = transmute::<_, [u8; $size]>($n.$which());
|
||||
@ -299,7 +319,14 @@ impl ByteOrder for BigEndian {
|
||||
|
||||
#[inline]
|
||||
fn read_uint(buf: &[u8], nbytes: usize) -> u64 {
|
||||
read_num_bytes!(u64, 8, be nbytes, buf, to_be)
|
||||
assert!(1 <= nbytes && nbytes <= 8 && nbytes <= buf.len());
|
||||
let mut out = [0u8; 8];
|
||||
let ptr_out = out.as_mut_ptr();
|
||||
unsafe {
|
||||
copy_nonoverlapping(
|
||||
buf.as_ptr(), ptr_out.offset((8 - nbytes) as isize), nbytes);
|
||||
(*(ptr_out as *const u64)).to_be()
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@ -316,6 +343,19 @@ impl ByteOrder for BigEndian {
|
||||
fn write_u64(buf: &mut [u8], n: u64) {
|
||||
write_num_bytes!(u64, 8, n, buf, to_be);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_uint(buf: &mut [u8], n: u64, nbytes: usize) {
|
||||
assert!(pack_size(n) <= nbytes && nbytes <= 8);
|
||||
assert!(nbytes <= buf.len());
|
||||
unsafe {
|
||||
let bytes: [u8; 8] = transmute(n.to_be());
|
||||
copy_nonoverlapping(
|
||||
bytes.as_ptr().offset((8 - nbytes) as isize),
|
||||
buf.as_mut_ptr(),
|
||||
nbytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ByteOrder for LittleEndian {
|
||||
@ -336,7 +376,13 @@ impl ByteOrder for LittleEndian {
|
||||
|
||||
#[inline]
|
||||
fn read_uint(buf: &[u8], nbytes: usize) -> u64 {
|
||||
read_num_bytes!(u64, 8, le nbytes, buf, to_le)
|
||||
assert!(1 <= nbytes && nbytes <= 8 && nbytes <= buf.len());
|
||||
let mut out = [0u8; 8];
|
||||
let ptr_out = out.as_mut_ptr();
|
||||
unsafe {
|
||||
copy_nonoverlapping(buf.as_ptr(), ptr_out, nbytes);
|
||||
(*(ptr_out as *const u64)).to_le()
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@ -353,6 +399,16 @@ impl ByteOrder for LittleEndian {
|
||||
fn write_u64(buf: &mut [u8], n: u64) {
|
||||
write_num_bytes!(u64, 8, n, buf, to_le);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_uint(buf: &mut [u8], n: u64, nbytes: usize) {
|
||||
assert!(pack_size(n as u64) <= nbytes && nbytes <= 8);
|
||||
assert!(nbytes <= buf.len());
|
||||
unsafe {
|
||||
let bytes: [u8; 8] = transmute(n.to_le());
|
||||
copy_nonoverlapping(bytes.as_ptr(), buf.as_mut_ptr(), nbytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@ -386,8 +442,8 @@ mod test {
|
||||
let max = ($max - 1) >> (8 * (8 - $bytes));
|
||||
fn prop(n: $ty_int) -> bool {
|
||||
let mut buf = [0; 8];
|
||||
BigEndian::$write(&mut buf, n);
|
||||
n == BigEndian::$read(&mut buf[8 - $bytes..], $bytes)
|
||||
BigEndian::$write(&mut buf, n, $bytes);
|
||||
n == BigEndian::$read(&mut buf[..$bytes], $bytes)
|
||||
}
|
||||
qc_sized(prop as fn($ty_int) -> bool, max);
|
||||
}
|
||||
@ -397,7 +453,7 @@ mod test {
|
||||
let max = ($max - 1) >> (8 * (8 - $bytes));
|
||||
fn prop(n: $ty_int) -> bool {
|
||||
let mut buf = [0; 8];
|
||||
LittleEndian::$write(&mut buf, n);
|
||||
LittleEndian::$write(&mut buf, n, $bytes);
|
||||
n == LittleEndian::$read(&mut buf[..$bytes], $bytes)
|
||||
}
|
||||
qc_sized(prop as fn($ty_int) -> bool, max);
|
||||
@ -408,7 +464,7 @@ mod test {
|
||||
let max = ($max - 1) >> (8 * (8 - $bytes));
|
||||
fn prop(n: $ty_int) -> bool {
|
||||
let mut buf = [0; 8];
|
||||
NativeEndian::$write(&mut buf, n);
|
||||
NativeEndian::$write(&mut buf, n, $bytes);
|
||||
n == NativeEndian::$read(&mut buf[..$bytes], $bytes)
|
||||
}
|
||||
qc_sized(prop as fn($ty_int) -> bool, max);
|
||||
@ -467,23 +523,23 @@ mod test {
|
||||
qc_byte_order!(prop_f32, f32, ::std::u64::MAX as u64, read_f32, write_f32);
|
||||
qc_byte_order!(prop_f64, f64, ::std::i64::MAX as u64, read_f64, write_f64);
|
||||
|
||||
qc_byte_order!(prop_uint_1, u64, super::U64_MAX, 1, read_uint, write_u64);
|
||||
qc_byte_order!(prop_uint_2, u64, super::U64_MAX, 2, read_uint, write_u64);
|
||||
qc_byte_order!(prop_uint_3, u64, super::U64_MAX, 3, read_uint, write_u64);
|
||||
qc_byte_order!(prop_uint_4, u64, super::U64_MAX, 4, read_uint, write_u64);
|
||||
qc_byte_order!(prop_uint_5, u64, super::U64_MAX, 5, read_uint, write_u64);
|
||||
qc_byte_order!(prop_uint_6, u64, super::U64_MAX, 6, read_uint, write_u64);
|
||||
qc_byte_order!(prop_uint_7, u64, super::U64_MAX, 7, read_uint, write_u64);
|
||||
qc_byte_order!(prop_uint_8, u64, super::U64_MAX, 8, read_uint, write_u64);
|
||||
qc_byte_order!(prop_uint_1, u64, super::U64_MAX, 1, read_uint, write_uint);
|
||||
qc_byte_order!(prop_uint_2, u64, super::U64_MAX, 2, read_uint, write_uint);
|
||||
qc_byte_order!(prop_uint_3, u64, super::U64_MAX, 3, read_uint, write_uint);
|
||||
qc_byte_order!(prop_uint_4, u64, super::U64_MAX, 4, read_uint, write_uint);
|
||||
qc_byte_order!(prop_uint_5, u64, super::U64_MAX, 5, read_uint, write_uint);
|
||||
qc_byte_order!(prop_uint_6, u64, super::U64_MAX, 6, read_uint, write_uint);
|
||||
qc_byte_order!(prop_uint_7, u64, super::U64_MAX, 7, read_uint, write_uint);
|
||||
qc_byte_order!(prop_uint_8, u64, super::U64_MAX, 8, read_uint, write_uint);
|
||||
|
||||
qc_byte_order!(prop_int_1, i64, super::I64_MAX, 1, read_int, write_i64);
|
||||
qc_byte_order!(prop_int_2, i64, super::I64_MAX, 2, read_int, write_i64);
|
||||
qc_byte_order!(prop_int_3, i64, super::I64_MAX, 3, read_int, write_i64);
|
||||
qc_byte_order!(prop_int_4, i64, super::I64_MAX, 4, read_int, write_i64);
|
||||
qc_byte_order!(prop_int_5, i64, super::I64_MAX, 5, read_int, write_i64);
|
||||
qc_byte_order!(prop_int_6, i64, super::I64_MAX, 6, read_int, write_i64);
|
||||
qc_byte_order!(prop_int_7, i64, super::I64_MAX, 7, read_int, write_i64);
|
||||
qc_byte_order!(prop_int_8, i64, super::I64_MAX, 8, read_int, write_i64);
|
||||
qc_byte_order!(prop_int_1, i64, super::I64_MAX, 1, read_int, write_int);
|
||||
qc_byte_order!(prop_int_2, i64, super::I64_MAX, 2, read_int, write_int);
|
||||
qc_byte_order!(prop_int_3, i64, super::I64_MAX, 3, read_int, write_int);
|
||||
qc_byte_order!(prop_int_4, i64, super::I64_MAX, 4, read_int, write_int);
|
||||
qc_byte_order!(prop_int_5, i64, super::I64_MAX, 5, read_int, write_int);
|
||||
qc_byte_order!(prop_int_6, i64, super::I64_MAX, 6, read_int, write_int);
|
||||
qc_byte_order!(prop_int_7, i64, super::I64_MAX, 7, read_int, write_int);
|
||||
qc_byte_order!(prop_int_8, i64, super::I64_MAX, 8, read_int, write_int);
|
||||
|
||||
macro_rules! qc_bytes_ext {
|
||||
($name:ident, $ty_int:ident, $max:expr,
|
||||
|
@ -295,6 +295,36 @@ pub trait WriteBytesExt: io::Write {
|
||||
write_all(self, &buf)
|
||||
}
|
||||
|
||||
/// Writes an unsigned n-bytes integer to the underlying writer.
|
||||
///
|
||||
/// If the given integer is not representable in the given number of bytes,
|
||||
/// this method panics. If `nbytes > 8`, this method panics.
|
||||
#[inline]
|
||||
fn write_uint<T: ByteOrder>(
|
||||
&mut self,
|
||||
n: u64,
|
||||
nbytes: usize,
|
||||
) -> Result<()> {
|
||||
let mut buf = [0; 8];
|
||||
T::write_uint(&mut buf, n, nbytes);
|
||||
write_all(self, &buf[0..nbytes])
|
||||
}
|
||||
|
||||
/// Writes a signed n-bytes integer to the underlying writer.
|
||||
///
|
||||
/// If the given integer is not representable in the given number of bytes,
|
||||
/// this method panics. If `nbytes > 8`, this method panics.
|
||||
#[inline]
|
||||
fn write_int<T: ByteOrder>(
|
||||
&mut self,
|
||||
n: i64,
|
||||
nbytes: usize,
|
||||
) -> Result<()> {
|
||||
let mut buf = [0; 8];
|
||||
T::write_int(&mut buf, n, nbytes);
|
||||
write_all(self, &buf[0..nbytes])
|
||||
}
|
||||
|
||||
/// Writes a IEEE754 single-precision (4 bytes) floating point number to
|
||||
/// the underlying writer.
|
||||
#[inline]
|
||||
|
@ -34,16 +34,30 @@ use media_time_to_ms;
|
||||
use track_time_to_ms;
|
||||
use SampleEntry;
|
||||
|
||||
// These constants *must* match those in include/mp4parse.h.
|
||||
|
||||
/// Map Error to int32 return codes.
|
||||
const MP4PARSE_OK: i32 = 0;
|
||||
const MP4PARSE_ERROR_BADARG: i32 = -1;
|
||||
const MP4PARSE_ERROR_INVALID: i32 = -2;
|
||||
const MP4PARSE_ERROR_UNSUPPORTED: i32 = -3;
|
||||
const MP4PARSE_ERROR_EOF: i32 = -4;
|
||||
const MP4PARSE_ASSERT: i32 = -5;
|
||||
const MP4PARSE_ERROR_IO: i32 = -6;
|
||||
|
||||
/// Map TrackType to uint32 constants.
|
||||
const TRACK_TYPE_H264: u32 = 0;
|
||||
const TRACK_TYPE_AAC: u32 = 1;
|
||||
const TRACK_TYPE_AAC: u32 = 1;
|
||||
|
||||
// These structs *must* match those declared in include/mp4parse.h.
|
||||
|
||||
#[repr(C)]
|
||||
pub struct TrackInfo {
|
||||
track_type: u32,
|
||||
track_id: u32,
|
||||
duration: u64,
|
||||
media_time: i64, // wants to be u64? understand how elst adjustment works
|
||||
// TODO(kinetik): include crypto guff
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
@ -51,8 +65,11 @@ pub struct TrackAudioInfo {
|
||||
channels: u16,
|
||||
bit_depth: u16,
|
||||
sample_rate: u32,
|
||||
// profile: i32,
|
||||
// extended_profile: i32, // check types
|
||||
// TODO(kinetik):
|
||||
// int32_t profile;
|
||||
// int32_t extended_profile; // check types
|
||||
// extra_data
|
||||
// codec_specific_config
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
@ -61,8 +78,13 @@ pub struct TrackVideoInfo {
|
||||
display_height: u32,
|
||||
image_width: u16,
|
||||
image_height: u16,
|
||||
// TODO(kinetik):
|
||||
// extra_data
|
||||
// codec_specific_config
|
||||
}
|
||||
|
||||
// C API wrapper functions.
|
||||
|
||||
/// Allocate an opaque rust-side parser context.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn mp4parse_new() -> *mut MediaContext {
|
||||
@ -78,7 +100,7 @@ pub unsafe extern "C" fn mp4parse_free(context: *mut MediaContext) {
|
||||
}
|
||||
|
||||
/// Feed a buffer through `read_mp4()` with the given rust-side
|
||||
/// parser context, returning the number of detected tracks.
|
||||
/// parser context, returning success or an error code.
|
||||
///
|
||||
/// This is safe to call with NULL arguments but will crash
|
||||
/// if given invalid pointers, as is usual for C.
|
||||
@ -86,7 +108,7 @@ pub unsafe extern "C" fn mp4parse_free(context: *mut MediaContext) {
|
||||
pub unsafe extern "C" fn mp4parse_read(context: *mut MediaContext, buffer: *const u8, size: usize) -> i32 {
|
||||
// Validate arguments from C.
|
||||
if context.is_null() || buffer.is_null() || size < 8 {
|
||||
return -1;
|
||||
return MP4PARSE_ERROR_BADARG;
|
||||
}
|
||||
|
||||
let mut context: &mut MediaContext = &mut *context;
|
||||
@ -96,24 +118,38 @@ pub unsafe extern "C" fn mp4parse_read(context: *mut MediaContext, buffer: *cons
|
||||
let mut c = Cursor::new(b);
|
||||
|
||||
// Parse in a subthread to catch any panics.
|
||||
let task = std::thread::spawn(move || {
|
||||
match read_mp4(&mut c, &mut context) {
|
||||
Ok(_) => {},
|
||||
Err(Error::UnexpectedEOF) => {},
|
||||
Err(e) => { panic!(e); },
|
||||
}
|
||||
// Make sure the track count fits in an i32 so we can use
|
||||
// negative values for failure.
|
||||
assert!(context.tracks.len() < i32::max_value() as usize);
|
||||
context.tracks.len() as i32
|
||||
});
|
||||
task.join().unwrap_or(-1)
|
||||
let task = std::thread::spawn(move || read_mp4(&mut c, &mut context));
|
||||
// The task's JoinHandle will return an error result if the
|
||||
// thread panicked, and will wrap the closure's return'd
|
||||
// result in an Ok(..) otherwise, meaning we could see
|
||||
// Ok(Err(Error::..)) here. So map thread failures back
|
||||
// to an mp4parse::Error before converting to a C return value.
|
||||
match task.join().or(Err(Error::AssertCaught)) {
|
||||
Ok(_) => MP4PARSE_OK,
|
||||
Err(Error::InvalidData) => MP4PARSE_ERROR_INVALID,
|
||||
Err(Error::Unsupported) => MP4PARSE_ERROR_UNSUPPORTED,
|
||||
Err(Error::UnexpectedEOF) => MP4PARSE_ERROR_EOF,
|
||||
Err(Error::AssertCaught) => MP4PARSE_ASSERT,
|
||||
Err(Error::Io(_)) => MP4PARSE_ERROR_IO,
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the number of tracks parsed by previous `read_mp4()` calls.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn mp4parse_get_track_count(context: *const MediaContext) -> u32 {
|
||||
// Validate argument from C.
|
||||
assert!(!context.is_null());
|
||||
let context = &*context;
|
||||
|
||||
// Make sure the track count fits in a u32.
|
||||
assert!(context.tracks.len() < u32::max_value() as usize);
|
||||
context.tracks.len() as u32
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn mp4parse_get_track_info(context: *mut MediaContext, track: i32, info: *mut TrackInfo) -> i32 {
|
||||
if context.is_null() || track < 0 || info.is_null() {
|
||||
return -1;
|
||||
pub unsafe extern "C" fn mp4parse_get_track_info(context: *mut MediaContext, track: u32, info: *mut TrackInfo) -> i32 {
|
||||
if context.is_null() || info.is_null() {
|
||||
return MP4PARSE_ERROR_BADARG;
|
||||
}
|
||||
|
||||
let context: &mut MediaContext = &mut *context;
|
||||
@ -121,13 +157,13 @@ pub unsafe extern "C" fn mp4parse_get_track_info(context: *mut MediaContext, tra
|
||||
let info: &mut TrackInfo = &mut *info;
|
||||
|
||||
if track_index >= context.tracks.len() {
|
||||
return -1;
|
||||
return MP4PARSE_ERROR_BADARG;
|
||||
}
|
||||
|
||||
info.track_type = match context.tracks[track_index].track_type {
|
||||
TrackType::Video => TRACK_TYPE_H264,
|
||||
TrackType::Audio => TRACK_TYPE_AAC,
|
||||
TrackType::Unknown => return -1,
|
||||
TrackType::Unknown => return MP4PARSE_ERROR_UNSUPPORTED,
|
||||
};
|
||||
|
||||
// Maybe context & track should just have a single simple is_valid() instead?
|
||||
@ -135,8 +171,8 @@ pub unsafe extern "C" fn mp4parse_get_track_info(context: *mut MediaContext, tra
|
||||
context.tracks[track_index].timescale.is_none() ||
|
||||
context.tracks[track_index].duration.is_none() ||
|
||||
context.tracks[track_index].track_id.is_none() {
|
||||
return -1;
|
||||
}
|
||||
return MP4PARSE_ERROR_INVALID;
|
||||
}
|
||||
|
||||
std::thread::spawn(move || {
|
||||
let track = &context.tracks[track_index];
|
||||
@ -152,98 +188,102 @@ pub unsafe extern "C" fn mp4parse_get_track_info(context: *mut MediaContext, tra
|
||||
};
|
||||
info.duration = track_time_to_ms(track.duration.unwrap(), track.timescale.unwrap());
|
||||
info.track_id = track.track_id.unwrap();
|
||||
0
|
||||
}).join().unwrap_or(-1)
|
||||
MP4PARSE_OK
|
||||
}).join().unwrap_or(MP4PARSE_ERROR_INVALID)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn mp4parse_get_track_audio_info(context: *mut MediaContext, track: i32, info: *mut TrackAudioInfo) -> i32 {
|
||||
if context.is_null() || track < 0 || info.is_null() {
|
||||
return -1;
|
||||
pub unsafe extern "C" fn mp4parse_get_track_audio_info(context: *mut MediaContext, track: u32, info: *mut TrackAudioInfo) -> i32 {
|
||||
if context.is_null() || info.is_null() {
|
||||
return MP4PARSE_ERROR_BADARG;
|
||||
}
|
||||
|
||||
let context: &mut MediaContext = &mut *context;
|
||||
|
||||
if track as usize >= context.tracks.len() {
|
||||
return -1;
|
||||
return MP4PARSE_ERROR_BADARG;
|
||||
}
|
||||
|
||||
let track = &context.tracks[track as usize];
|
||||
|
||||
match track.track_type {
|
||||
TrackType::Audio => {},
|
||||
_ => return -1,
|
||||
TrackType::Audio => {}
|
||||
_ => return MP4PARSE_ERROR_INVALID,
|
||||
};
|
||||
|
||||
let audio = match track.data {
|
||||
Some(ref data) => data,
|
||||
None => return -1,
|
||||
None => return MP4PARSE_ERROR_INVALID,
|
||||
};
|
||||
|
||||
let audio = match audio {
|
||||
&SampleEntry::Audio(ref x) => x,
|
||||
_ => return -1,
|
||||
let audio = match *audio {
|
||||
SampleEntry::Audio(ref x) => x,
|
||||
_ => return MP4PARSE_ERROR_INVALID,
|
||||
};
|
||||
|
||||
(*info).channels = audio.channelcount;
|
||||
(*info).bit_depth = audio.samplesize;
|
||||
(*info).sample_rate = audio.samplerate >> 16; // 16.16 fixed point
|
||||
|
||||
0
|
||||
MP4PARSE_OK
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn mp4parse_get_track_video_info(context: *mut MediaContext, track: i32, info: *mut TrackVideoInfo) -> i32 {
|
||||
if context.is_null() || track < 0 || info.is_null() {
|
||||
return -1;
|
||||
pub unsafe extern "C" fn mp4parse_get_track_video_info(context: *mut MediaContext, track: u32, info: *mut TrackVideoInfo) -> i32 {
|
||||
if context.is_null() || info.is_null() {
|
||||
return MP4PARSE_ERROR_BADARG;
|
||||
}
|
||||
|
||||
let context: &mut MediaContext = &mut *context;
|
||||
|
||||
if track as usize >= context.tracks.len() {
|
||||
return -1;
|
||||
return MP4PARSE_ERROR_BADARG;
|
||||
}
|
||||
|
||||
let track = &context.tracks[track as usize];
|
||||
|
||||
match track.track_type {
|
||||
TrackType::Video => {},
|
||||
_ => return -1,
|
||||
TrackType::Video => {}
|
||||
_ => return MP4PARSE_ERROR_INVALID,
|
||||
};
|
||||
|
||||
let video = match track.data {
|
||||
Some(ref data) => data,
|
||||
None => return -1,
|
||||
None => return MP4PARSE_ERROR_INVALID,
|
||||
};
|
||||
|
||||
let video = match video {
|
||||
&SampleEntry::Video(ref x) => x,
|
||||
_ => return -1,
|
||||
let video = match *video {
|
||||
SampleEntry::Video(ref x) => x,
|
||||
_ => return MP4PARSE_ERROR_INVALID,
|
||||
};
|
||||
|
||||
if let Some(ref tkhd) = track.tkhd {
|
||||
(*info).display_width = tkhd.width >> 16; // 16.16 fixed point
|
||||
(*info).display_height = tkhd.height >> 16; // 16.16 fixed point
|
||||
} else {
|
||||
return -1
|
||||
return MP4PARSE_ERROR_INVALID;
|
||||
}
|
||||
(*info).image_width = video.width;
|
||||
(*info).image_width = video.height;
|
||||
|
||||
0
|
||||
MP4PARSE_OK
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn new_context() {
|
||||
let context = mp4parse_new();
|
||||
assert!(!context.is_null());
|
||||
unsafe { mp4parse_free(context); }
|
||||
unsafe {
|
||||
mp4parse_free(context);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "assertion failed")]
|
||||
fn free_null_context() {
|
||||
unsafe { mp4parse_free(std::ptr::null_mut()); }
|
||||
unsafe {
|
||||
mp4parse_free(std::ptr::null_mut());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -257,16 +297,21 @@ fn arg_validation() {
|
||||
let buffer = vec![0u8; 8];
|
||||
|
||||
unsafe {
|
||||
assert_eq!(-1, mp4parse_read(null_context, null_buffer, 0));
|
||||
assert_eq!(-1, mp4parse_read(context, null_buffer, 0));
|
||||
assert_eq!(MP4PARSE_ERROR_BADARG,
|
||||
mp4parse_read(null_context, null_buffer, 0));
|
||||
assert_eq!(MP4PARSE_ERROR_BADARG,
|
||||
mp4parse_read(context, null_buffer, 0));
|
||||
}
|
||||
|
||||
for size in 0..buffer.len() {
|
||||
println!("testing buffer length {}", size);
|
||||
unsafe {
|
||||
assert_eq!(-1, mp4parse_read(context, buffer.as_ptr(), size));
|
||||
assert_eq!(MP4PARSE_ERROR_BADARG,
|
||||
mp4parse_read(context, buffer.as_ptr(), size));
|
||||
}
|
||||
}
|
||||
|
||||
unsafe { mp4parse_free(context); }
|
||||
unsafe {
|
||||
mp4parse_free(context);
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,14 @@ extern "C" {
|
||||
|
||||
struct mp4parse_state;
|
||||
|
||||
#define MP4PARSE_OK 0
|
||||
#define MP4PARSE_ERROR_BADARG -1 // Argument validation failure
|
||||
#define MP4PARSE_ERROR_INVALID -2 // Error::InvalidData
|
||||
#define MP4PARSE_ERROR_UNSUPPORTED -3 // Error::Unsupported
|
||||
#define MP4PARSE_ERROR_EOF -4 // Error::UnexpectedEOF
|
||||
#define MP4PARSE_ASSERT -5 // Error::AssertCaught
|
||||
#define MP4PARSE_ERROR_IO -6 // Error::Io(_)
|
||||
|
||||
#define MP4PARSE_TRACK_TYPE_H264 0 // "video/avc"
|
||||
#define MP4PARSE_TRACK_TYPE_AAC 1 // "audio/mp4a-latm"
|
||||
|
||||
@ -18,12 +26,6 @@ struct mp4parse_track_audio_info {
|
||||
uint16_t channels;
|
||||
uint16_t bit_depth;
|
||||
uint32_t sample_rate;
|
||||
//int32_t profile;
|
||||
//int32_t extended_profile; // check types
|
||||
|
||||
// TODO(kinetik):
|
||||
// extra_data
|
||||
// codec_specific_config
|
||||
};
|
||||
|
||||
struct mp4parse_track_video_info {
|
||||
@ -31,10 +33,6 @@ struct mp4parse_track_video_info {
|
||||
uint32_t display_height;
|
||||
uint16_t image_width;
|
||||
uint16_t image_height;
|
||||
|
||||
// TODO(kinetik):
|
||||
// extra_data
|
||||
// codec_specific_config
|
||||
};
|
||||
|
||||
struct mp4parse_track_info {
|
||||
@ -42,7 +40,6 @@ struct mp4parse_track_info {
|
||||
uint32_t track_id;
|
||||
uint64_t duration;
|
||||
int64_t media_time;
|
||||
// TODO(kinetik): crypto guff
|
||||
};
|
||||
|
||||
struct mp4parse_state* mp4parse_new(void);
|
||||
@ -50,11 +47,13 @@ void mp4parse_free(struct mp4parse_state* state);
|
||||
|
||||
int32_t mp4parse_read(struct mp4parse_state* state, uint8_t *buffer, size_t size);
|
||||
|
||||
int32_t mp4parse_get_track_info(struct mp4parse_state* state, int32_t track, struct mp4parse_track_info* track_info);
|
||||
uint32_t mp4parse_get_track_count(struct mp4parse_state* state);
|
||||
|
||||
int32_t mp4parse_get_track_audio_info(struct mp4parse_state* state, int32_t track, struct mp4parse_track_audio_info* track_info);
|
||||
int32_t mp4parse_get_track_info(struct mp4parse_state* state, uint32_t track, struct mp4parse_track_info* track_info);
|
||||
|
||||
int32_t mp4parse_get_track_video_info(struct mp4parse_state* state, int32_t track, struct mp4parse_track_video_info* track_info);
|
||||
int32_t mp4parse_get_track_audio_info(struct mp4parse_state* state, uint32_t track, struct mp4parse_track_audio_info* track_info);
|
||||
|
||||
int32_t mp4parse_get_track_video_info(struct mp4parse_state* state, uint32_t track, struct mp4parse_track_video_info* track_info);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
# Script to update mp4parse-rust sources to latest upstream
|
||||
|
||||
# Default version.
|
||||
VER=v0.1.4
|
||||
VER=v0.1.6
|
||||
|
||||
# Accept version or commit from the command line.
|
||||
if test -n "$1"; then
|
||||
@ -23,7 +23,7 @@ cp _upstream/mp4parse/include/mp4parse.h include/
|
||||
|
||||
git clone https://github.com/BurntSushi/byteorder _upstream/byteorder
|
||||
pushd _upstream/byteorder
|
||||
git checkout 0.3.13
|
||||
git checkout 0.4.2
|
||||
popd
|
||||
cp _upstream/byteorder/src/lib.rs byteorder/mod.rs
|
||||
cp _upstream/byteorder/src/new.rs byteorder/new.rs
|
||||
|
@ -22,27 +22,27 @@ TEST(rust, MP4MetadataEmpty)
|
||||
ASSERT_NE(context, nullptr);
|
||||
|
||||
rv = mp4parse_read(nullptr, nullptr, 0);
|
||||
EXPECT_EQ(rv, -1);
|
||||
EXPECT_EQ(rv, MP4PARSE_ERROR_BADARG);
|
||||
rv = mp4parse_read(context, nullptr, 0);
|
||||
EXPECT_EQ(rv, -1);
|
||||
EXPECT_EQ(rv, MP4PARSE_ERROR_BADARG);
|
||||
|
||||
size_t len = 4097;
|
||||
rv = mp4parse_read(nullptr, nullptr, len);
|
||||
EXPECT_EQ(rv, -1);
|
||||
EXPECT_EQ(rv, MP4PARSE_ERROR_BADARG);
|
||||
rv = mp4parse_read(context, nullptr, len);
|
||||
EXPECT_EQ(rv, -1);
|
||||
EXPECT_EQ(rv, MP4PARSE_ERROR_BADARG);
|
||||
|
||||
std::vector<uint8_t> buf;
|
||||
rv = mp4parse_read(nullptr, buf.data(), buf.size());
|
||||
EXPECT_EQ(rv, -1);
|
||||
EXPECT_EQ(rv, MP4PARSE_ERROR_BADARG);
|
||||
rv = mp4parse_read(context, buf.data(), buf.size());
|
||||
EXPECT_EQ(rv, -1);
|
||||
EXPECT_EQ(rv, MP4PARSE_ERROR_BADARG);
|
||||
|
||||
buf.reserve(len);
|
||||
rv = mp4parse_read(nullptr, buf.data(), buf.size());
|
||||
EXPECT_EQ(rv, -1);
|
||||
EXPECT_EQ(rv, MP4PARSE_ERROR_BADARG);
|
||||
rv = mp4parse_read(context, buf.data(), buf.size());
|
||||
EXPECT_EQ(rv, -1);
|
||||
EXPECT_EQ(rv, MP4PARSE_ERROR_BADARG);
|
||||
|
||||
mp4parse_free(context);
|
||||
}
|
||||
@ -62,7 +62,10 @@ TEST(rust, MP4Metadata)
|
||||
ASSERT_NE(context, nullptr);
|
||||
|
||||
int32_t rv = mp4parse_read(context, buf.data(), buf.size());
|
||||
EXPECT_EQ(rv, 2);
|
||||
EXPECT_EQ(rv, MP4PARSE_OK);
|
||||
|
||||
uint32_t tracks = mp4parse_get_track_count(context);
|
||||
EXPECT_EQ(tracks, 2U);
|
||||
|
||||
mp4parse_free(context);
|
||||
}
|
||||
|
@ -1144,6 +1144,8 @@ WebrtcVideoConduit::SelectSendResolution(unsigned short width,
|
||||
bool changed = false;
|
||||
if (mSendingWidth != width || mSendingHeight != height)
|
||||
{
|
||||
CSFLogDebug(logTag, "%s: resolution changing to %ux%u (from %ux%u)",
|
||||
__FUNCTION__, width, height, mSendingWidth, mSendingHeight);
|
||||
// This will avoid us continually retrying this operation if it fails.
|
||||
// If the resolution changes, we'll try again. In the meantime, we'll
|
||||
// keep using the old size in the encoder.
|
||||
@ -1155,6 +1157,8 @@ WebrtcVideoConduit::SelectSendResolution(unsigned short width,
|
||||
// uses mSendingWidth/Height
|
||||
unsigned int framerate = SelectSendFrameRate(mSendingFramerate);
|
||||
if (mSendingFramerate != framerate) {
|
||||
CSFLogDebug(logTag, "%s: framerate changing to %u (from %u)",
|
||||
__FUNCTION__, framerate, mSendingFramerate);
|
||||
mSendingFramerate = framerate;
|
||||
changed = true;
|
||||
}
|
||||
@ -1221,6 +1225,10 @@ WebrtcVideoConduit::ReconfigureSendCodec(unsigned short width,
|
||||
CSFLogError(logTag, "%s: GetSendCodec failed, err %d", __FUNCTION__, err);
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
CSFLogDebug(logTag,
|
||||
"%s: Requesting resolution change to %ux%u (from %ux%u)",
|
||||
__FUNCTION__, width, height, vie_codec.width, vie_codec.height);
|
||||
// Likely spurious unless there was some error, but rarely checked
|
||||
if (vie_codec.width != width || vie_codec.height != height ||
|
||||
vie_codec.maxFramerate != mSendingFramerate)
|
||||
@ -1399,6 +1407,8 @@ WebrtcVideoConduit::SendVideoFrame(webrtc::I420VideoFrame& frame)
|
||||
return kMediaConduitNoError;
|
||||
}
|
||||
if (frame.width() != mLastWidth || frame.height() != mLastHeight) {
|
||||
CSFLogDebug(logTag, "%s: call SelectSendResolution with %ux%u",
|
||||
__FUNCTION__, frame.width(), frame.height());
|
||||
if (SelectSendResolution(frame.width(), frame.height(), &frame)) {
|
||||
// SelectSendResolution took ownership of the data in i420_frame.
|
||||
// Submit the frame after reconfig is done
|
||||
|
@ -1,308 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=2 ts=8 et tw=80 : */
|
||||
/* 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/. */
|
||||
|
||||
#include "ClosingService.h"
|
||||
#include "nsIOService.h"
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
#include "ipc/Nuwa.h"
|
||||
#endif
|
||||
|
||||
class ClosingLayerSecret
|
||||
{
|
||||
public:
|
||||
explicit ClosingLayerSecret(mozilla::net::ClosingService *aClosingService)
|
||||
: mClosingService(aClosingService)
|
||||
{
|
||||
}
|
||||
|
||||
~ClosingLayerSecret()
|
||||
{
|
||||
mClosingService = nullptr;
|
||||
}
|
||||
|
||||
RefPtr<mozilla::net::ClosingService> mClosingService;
|
||||
};
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
static PRIOMethods sTcpUdpPRCloseLayerMethods;
|
||||
static PRIOMethods *sTcpUdpPRCloseLayerMethodsPtr = nullptr;
|
||||
static PRDescIdentity sTcpUdpPRCloseLayerId;
|
||||
|
||||
static PRStatus
|
||||
TcpUdpPRCloseLayerClose(PRFileDesc *aFd)
|
||||
{
|
||||
if (!aFd) {
|
||||
return PR_FAILURE;
|
||||
}
|
||||
|
||||
PRFileDesc* layer = PR_PopIOLayer(aFd, PR_TOP_IO_LAYER);
|
||||
MOZ_RELEASE_ASSERT(layer &&
|
||||
layer->identity == sTcpUdpPRCloseLayerId,
|
||||
"Closing Layer not on top of stack");
|
||||
|
||||
ClosingLayerSecret *closingLayerSecret =
|
||||
reinterpret_cast<ClosingLayerSecret *>(layer->secret);
|
||||
|
||||
PRStatus status = PR_SUCCESS;
|
||||
|
||||
if (aFd) {
|
||||
// If this is called during shutdown do not call ..method->close(fd) and
|
||||
// let it leak.
|
||||
if (gIOService->IsNetTearingDown()) {
|
||||
// If the ClosingService layer is the first layer above PR_NSPR_IO_LAYER
|
||||
// we are not going to leak anything, but the PR_Close will not be called.
|
||||
PR_Free(aFd);
|
||||
} else if (closingLayerSecret->mClosingService) {
|
||||
closingLayerSecret->mClosingService->PostRequest(aFd);
|
||||
} else {
|
||||
// Socket is created before closing service has been started or there was
|
||||
// a problem with starting it.
|
||||
PR_Close(aFd);
|
||||
}
|
||||
}
|
||||
|
||||
layer->secret = nullptr;
|
||||
layer->dtor(layer);
|
||||
delete closingLayerSecret;
|
||||
return status;
|
||||
}
|
||||
|
||||
ClosingService* ClosingService::sInstance = nullptr;
|
||||
|
||||
ClosingService::ClosingService()
|
||||
: mShutdown(false)
|
||||
, mMonitor("ClosingService.mMonitor")
|
||||
{
|
||||
MOZ_ASSERT(!sInstance,
|
||||
"multiple ClosingService instances!");
|
||||
}
|
||||
|
||||
// static
|
||||
void
|
||||
ClosingService::Start()
|
||||
{
|
||||
if (!sTcpUdpPRCloseLayerMethodsPtr) {
|
||||
sTcpUdpPRCloseLayerId = PR_GetUniqueIdentity("TCP and UDP PRClose layer");
|
||||
PR_ASSERT(PR_INVALID_IO_LAYER != sTcpUdpPRCloseLayerId);
|
||||
|
||||
sTcpUdpPRCloseLayerMethods = *PR_GetDefaultIOMethods();
|
||||
sTcpUdpPRCloseLayerMethods.close = TcpUdpPRCloseLayerClose;
|
||||
sTcpUdpPRCloseLayerMethodsPtr = &sTcpUdpPRCloseLayerMethods;
|
||||
}
|
||||
|
||||
if (!sInstance) {
|
||||
ClosingService* service = new ClosingService();
|
||||
if (NS_SUCCEEDED(service->StartInternal())) {
|
||||
NS_ADDREF(service);
|
||||
sInstance = service;
|
||||
} else {
|
||||
delete service;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
ClosingService::StartInternal()
|
||||
{
|
||||
mThread = PR_CreateThread(PR_USER_THREAD, ThreadFunc, this,
|
||||
PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
|
||||
PR_JOINABLE_THREAD, 32 * 1024);
|
||||
if (!mThread) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// static
|
||||
nsresult
|
||||
ClosingService::AttachIOLayer(PRFileDesc *aFd)
|
||||
{
|
||||
// We are going to remove ClosingService soon.
|
||||
// This change is going to turn it off, so ClosingService is not used.
|
||||
// Bug 1238010.
|
||||
return NS_OK;
|
||||
|
||||
if (!sTcpUdpPRCloseLayerMethodsPtr) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
PRFileDesc * layer;
|
||||
PRStatus status;
|
||||
|
||||
layer = PR_CreateIOLayerStub(sTcpUdpPRCloseLayerId,
|
||||
sTcpUdpPRCloseLayerMethodsPtr);
|
||||
|
||||
if (!layer) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
ClosingLayerSecret *secret = new ClosingLayerSecret(sInstance);
|
||||
layer->secret = reinterpret_cast<PRFilePrivate *>(secret);
|
||||
|
||||
status = PR_PushIOLayer(aFd, PR_NSPR_IO_LAYER, layer);
|
||||
|
||||
if (status == PR_FAILURE) {
|
||||
delete secret;
|
||||
PR_DELETE(layer);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
ClosingService::PostRequest(PRFileDesc *aFd)
|
||||
{
|
||||
mozilla::MonitorAutoLock mon(mMonitor);
|
||||
|
||||
// Check if shutdown is called.
|
||||
if (mShutdown) {
|
||||
// Let the socket leak. We are in shutdown and some PRClose can take a long
|
||||
// time. To prevent shutdown crash (bug 1152046) do not accept sockets any
|
||||
// more.
|
||||
// If the ClosingService layer is the first layer above PR_NSPR_IO_LAYER
|
||||
// we are not going to leak anything, but PR_Close will not be called.
|
||||
PR_Free(aFd);
|
||||
return;
|
||||
}
|
||||
|
||||
mQueue.AppendElement(aFd);
|
||||
if (mQueue.Length() == 1) {
|
||||
mon.Notify();
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
void
|
||||
ClosingService::Shutdown()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (sInstance) {
|
||||
sInstance->ShutdownInternal();
|
||||
NS_RELEASE(sInstance);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ClosingService::ShutdownInternal()
|
||||
{
|
||||
{
|
||||
mozilla::MonitorAutoLock mon(mMonitor);
|
||||
if (mShutdown) {
|
||||
// This should not happen.
|
||||
return;
|
||||
}
|
||||
|
||||
mShutdown = true;
|
||||
// If it is waiting on the empty queue, wake it up.
|
||||
if (mQueue.Length() == 0) {
|
||||
mon.Notify();
|
||||
}
|
||||
}
|
||||
|
||||
if (mThread) {
|
||||
PR_JoinThread(mThread);
|
||||
mThread = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ClosingService::ThreadFunc()
|
||||
{
|
||||
PR_SetCurrentThreadName("Closing Service");
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
if (IsNuwaProcess()) {
|
||||
NuwaMarkCurrentThread(nullptr, nullptr);
|
||||
}
|
||||
#endif
|
||||
|
||||
for (;;) {
|
||||
PRFileDesc *fd;
|
||||
{
|
||||
mozilla::MonitorAutoLock mon(mMonitor);
|
||||
while (!mShutdown && (mQueue.Length() == 0)) {
|
||||
mon.Wait();
|
||||
}
|
||||
|
||||
if (mShutdown) {
|
||||
// If we are in shutdown leak the rest of the sockets.
|
||||
for (uint32_t i = 0; i < mQueue.Length(); i++) {
|
||||
fd = mQueue[i];
|
||||
// If the ClosingService layer is the first layer above
|
||||
// PR_NSPR_IO_LAYER we are not going to leak anything, but PR_Close
|
||||
// will not be called.
|
||||
PR_Free(fd);
|
||||
}
|
||||
mQueue.Clear();
|
||||
return;
|
||||
}
|
||||
|
||||
fd = mQueue[0];
|
||||
mQueue.RemoveElementAt(0);
|
||||
}
|
||||
// Leave lock before closing socket. It can block for a long time and in
|
||||
// case we accidentally attach this layer twice this would cause deadlock.
|
||||
|
||||
bool tcp = (PR_GetDescType(PR_GetIdentitiesLayer(fd, PR_NSPR_IO_LAYER)) ==
|
||||
PR_DESC_SOCKET_TCP);
|
||||
|
||||
PRIntervalTime closeStarted = PR_IntervalNow();
|
||||
fd->methods->close(fd);
|
||||
|
||||
// Post telemetry.
|
||||
if (tcp) {
|
||||
SendPRCloseTelemetry(closeStarted,
|
||||
Telemetry::PRCLOSE_TCP_BLOCKING_TIME_NORMAL,
|
||||
Telemetry::PRCLOSE_TCP_BLOCKING_TIME_SHUTDOWN,
|
||||
Telemetry::PRCLOSE_TCP_BLOCKING_TIME_CONNECTIVITY_CHANGE,
|
||||
Telemetry::PRCLOSE_TCP_BLOCKING_TIME_LINK_CHANGE,
|
||||
Telemetry::PRCLOSE_TCP_BLOCKING_TIME_OFFLINE);
|
||||
} else {
|
||||
SendPRCloseTelemetry(closeStarted,
|
||||
Telemetry::PRCLOSE_UDP_BLOCKING_TIME_NORMAL,
|
||||
Telemetry::PRCLOSE_UDP_BLOCKING_TIME_SHUTDOWN,
|
||||
Telemetry::PRCLOSE_UDP_BLOCKING_TIME_CONNECTIVITY_CHANGE,
|
||||
Telemetry::PRCLOSE_UDP_BLOCKING_TIME_LINK_CHANGE,
|
||||
Telemetry::PRCLOSE_UDP_BLOCKING_TIME_OFFLINE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ClosingService::SendPRCloseTelemetry(PRIntervalTime aStart,
|
||||
mozilla::Telemetry::ID aIDNormal,
|
||||
mozilla::Telemetry::ID aIDShutdown,
|
||||
mozilla::Telemetry::ID aIDConnectivityChange,
|
||||
mozilla::Telemetry::ID aIDLinkChange,
|
||||
mozilla::Telemetry::ID aIDOffline)
|
||||
{
|
||||
PRIntervalTime now = PR_IntervalNow();
|
||||
if (gIOService->IsNetTearingDown()) {
|
||||
Telemetry::Accumulate(aIDShutdown,
|
||||
PR_IntervalToMilliseconds(now - aStart));
|
||||
|
||||
} else if (PR_IntervalToSeconds(now - gIOService->LastConnectivityChange())
|
||||
< 60) {
|
||||
Telemetry::Accumulate(aIDConnectivityChange,
|
||||
PR_IntervalToMilliseconds(now - aStart));
|
||||
} else if (PR_IntervalToSeconds(now - gIOService->LastNetworkLinkChange())
|
||||
< 60) {
|
||||
Telemetry::Accumulate(aIDLinkChange,
|
||||
PR_IntervalToMilliseconds(now - aStart));
|
||||
|
||||
} else if (PR_IntervalToSeconds(now - gIOService->LastOfflineStateChange())
|
||||
< 60) {
|
||||
Telemetry::Accumulate(aIDOffline,
|
||||
PR_IntervalToMilliseconds(now - aStart));
|
||||
} else {
|
||||
Telemetry::Accumulate(aIDNormal,
|
||||
PR_IntervalToMilliseconds(now - aStart));
|
||||
}
|
||||
}
|
||||
|
||||
} //namwspacw mozilla
|
||||
} //namespace net
|
@ -1,71 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=2 ts=8 et tw=80 : */
|
||||
/* 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/. */
|
||||
|
||||
#ifndef ClosingService_h__
|
||||
#define ClosingService_h__
|
||||
|
||||
#include "nsTArray.h"
|
||||
#include "nspr.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
#include "mozilla/Monitor.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// ClosingService
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// A helper class carrying call to PR_Close on FD to a separate thread -
|
||||
// closingThread. This may be a workaround for shutdown blocks that are caused
|
||||
// by serial calls to close on UDP and TCP sockets.
|
||||
// This service is started by nsIOService and also the class adds itself as an
|
||||
// observer to "xpcom-shutdown-threads" notification where we join the thread
|
||||
// and remove reference.
|
||||
// During worktime of the thread the class is also self-referenced,
|
||||
// since observer service might throw the reference away sooner than the thread
|
||||
// is actually done.
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
class ClosingService final
|
||||
{
|
||||
public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ClosingService)
|
||||
|
||||
ClosingService();
|
||||
|
||||
// Attaching this layer on tcp or udp sockets PRClose will be send to the
|
||||
// closingThread.
|
||||
static nsresult AttachIOLayer(PRFileDesc *aFd);
|
||||
static void Start();
|
||||
static void Shutdown();
|
||||
void PostRequest(PRFileDesc *aFd);
|
||||
|
||||
private:
|
||||
~ClosingService() {}
|
||||
nsresult StartInternal();
|
||||
void ShutdownInternal();
|
||||
void ThreadFunc();
|
||||
static void ThreadFunc(void *aClosure)
|
||||
{ static_cast<ClosingService*>(aClosure)->ThreadFunc(); }
|
||||
|
||||
void SendPRCloseTelemetry(PRIntervalTime aStart,
|
||||
mozilla::Telemetry::ID aIDNormal,
|
||||
mozilla::Telemetry::ID aIDShutdown,
|
||||
mozilla::Telemetry::ID aIDConnectivityChange,
|
||||
mozilla::Telemetry::ID aIDLinkChange,
|
||||
mozilla::Telemetry::ID aIDOffline);
|
||||
|
||||
static ClosingService* sInstance;
|
||||
Atomic<bool> mShutdown;
|
||||
nsTArray<PRFileDesc *> mQueue;
|
||||
mozilla::Monitor mMonitor;
|
||||
PRThread *mThread;
|
||||
};
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // ClosingService_h_
|
@ -193,7 +193,6 @@ UNIFIED_SOURCES += [
|
||||
'CaptivePortalService.cpp',
|
||||
'ChannelDiverterChild.cpp',
|
||||
'ChannelDiverterParent.cpp',
|
||||
'ClosingService.cpp',
|
||||
'Dashboard.cpp',
|
||||
'EventTokenBucket.cpp',
|
||||
'LoadContextInfo.cpp',
|
||||
|
@ -49,7 +49,6 @@
|
||||
#include "mozilla/Telemetry.h"
|
||||
#include "mozilla/net/DNS.h"
|
||||
#include "CaptivePortalService.h"
|
||||
#include "ClosingService.h"
|
||||
#include "ReferrerPolicy.h"
|
||||
#include "nsContentSecurityManager.h"
|
||||
#include "nsHttpHandler.h"
|
||||
@ -65,7 +64,6 @@
|
||||
|
||||
using namespace mozilla;
|
||||
using mozilla::net::IsNeckoChild;
|
||||
using mozilla::net::ClosingService;
|
||||
using mozilla::net::CaptivePortalService;
|
||||
using mozilla::net::gHttpHandler;
|
||||
|
||||
@ -260,10 +258,6 @@ nsIOService::Init()
|
||||
|
||||
InitializeNetworkLinkService();
|
||||
|
||||
// Start the closing service. Actual PR_Close() will be carried out on
|
||||
// a separate "closing" thread. Start the closing servicee here since this
|
||||
// point is executed only once per session.
|
||||
ClosingService::Start();
|
||||
SetOffline(false);
|
||||
|
||||
return NS_OK;
|
||||
@ -1077,9 +1071,6 @@ nsIOService::SetOffline(bool offline)
|
||||
DebugOnly<nsresult> rv = mSocketTransportService->Shutdown();
|
||||
NS_ASSERTION(NS_SUCCEEDED(rv), "socket transport service shutdown failed");
|
||||
}
|
||||
if (mShutdown) {
|
||||
ClosingService::Shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
mSettingOffline = false;
|
||||
|
@ -138,7 +138,7 @@ private:
|
||||
|
||||
private:
|
||||
bool mOffline;
|
||||
bool mOfflineForProfileChange;
|
||||
mozilla::Atomic<bool, mozilla::Relaxed> mOfflineForProfileChange;
|
||||
bool mManageLinkStatus;
|
||||
bool mConnectivity;
|
||||
// If true, the connectivity state will be mirrored by IOService.offline
|
||||
@ -150,7 +150,7 @@ private:
|
||||
bool mSettingOffline;
|
||||
bool mSetOfflineValue;
|
||||
|
||||
bool mShutdown;
|
||||
mozilla::Atomic<bool, mozilla::Relaxed> mShutdown;
|
||||
|
||||
nsCOMPtr<nsPISocketTransportService> mSocketTransportService;
|
||||
nsCOMPtr<nsPIDNSService> mDNSService;
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/UniquePtrExtensions.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
|
||||
#include "nsIIncrementalDownload.h"
|
||||
#include "nsIRequestObserver.h"
|
||||
@ -150,7 +151,7 @@ private:
|
||||
nsCOMPtr<nsIFile> mDest;
|
||||
nsCOMPtr<nsIChannel> mChannel;
|
||||
nsCOMPtr<nsITimer> mTimer;
|
||||
UniquePtr<char[]> mChunk;
|
||||
mozilla::UniquePtr<char[]> mChunk;
|
||||
int32_t mChunkLen;
|
||||
int32_t mChunkSize;
|
||||
int32_t mInterval;
|
||||
@ -712,7 +713,7 @@ nsIncrementalDownload::OnStartRequest(nsIRequest *request,
|
||||
if (diff < int64_t(mChunkSize))
|
||||
mChunkSize = uint32_t(diff);
|
||||
|
||||
mChunk = MakeUniqueFallible<char[]>(mChunkSize);
|
||||
mChunk = mozilla::MakeUniqueFallible<char[]>(mChunkSize);
|
||||
if (!mChunk)
|
||||
rv = NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
|
@ -16,7 +16,6 @@
|
||||
#include "nsProxyInfo.h"
|
||||
#include "nsNetCID.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "ClosingService.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "plstr.h"
|
||||
@ -1325,9 +1324,6 @@ nsSocketTransport::InitiateSocket()
|
||||
// Attach network activity monitor
|
||||
mozilla::net::NetworkActivityMonitor::AttachIOLayer(fd);
|
||||
|
||||
// Attach closing service.
|
||||
ClosingService::AttachIOLayer(fd);
|
||||
|
||||
PRStatus status;
|
||||
|
||||
// Make the socket non-blocking...
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "nsError.h"
|
||||
#include "nsNetCID.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsIOService.h"
|
||||
#include "prnetdb.h"
|
||||
#include "prio.h"
|
||||
#include "nsNetAddr.h"
|
||||
@ -29,7 +30,6 @@
|
||||
#include "nsIDNSRecord.h"
|
||||
#include "nsIDNSService.h"
|
||||
#include "nsICancelable.h"
|
||||
#include "ClosingService.h"
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
#include "NetStatistics.h"
|
||||
@ -332,6 +332,10 @@ nsUDPSocket::TryAttach()
|
||||
if (!gSocketTransportService)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
if (gIOService->IsNetTearingDown()) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
//
|
||||
// find out if it is going to be ok to attach another socket to the STS.
|
||||
// if not then we have to wait for the STS to tell us that it is ok.
|
||||
@ -581,6 +585,10 @@ nsUDPSocket::InitWithAddress(const NetAddr *aAddr, nsIPrincipal *aPrincipal,
|
||||
{
|
||||
NS_ENSURE_TRUE(mFD == nullptr, NS_ERROR_ALREADY_INITIALIZED);
|
||||
|
||||
if (gIOService->IsNetTearingDown()) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
bool addressReuse = (aOptionalArgc == 1) ? aAddressReuse : true;
|
||||
|
||||
//
|
||||
@ -657,7 +665,6 @@ nsUDPSocket::InitWithAddress(const NetAddr *aAddr, nsIPrincipal *aPrincipal,
|
||||
|
||||
// create proxy via NetworkActivityMonitor
|
||||
NetworkActivityMonitor::AttachIOLayer(mFD);
|
||||
ClosingService::AttachIOLayer(mFD);
|
||||
|
||||
// wait until AsyncListen is called before polling the socket for
|
||||
// client connections.
|
||||
|
@ -36,7 +36,6 @@
|
||||
#include "prerr.h"
|
||||
#include "prerror.h"
|
||||
#include "NetworkActivityMonitor.h"
|
||||
#include "ClosingService.h"
|
||||
|
||||
using namespace mozilla::net;
|
||||
|
||||
@ -145,9 +144,6 @@ void ARTPConnection::MakePortPair(
|
||||
NetworkActivityMonitor::AttachIOLayer(*rtpSocket);
|
||||
NetworkActivityMonitor::AttachIOLayer(*rtcpSocket);
|
||||
|
||||
ClosingService::AttachIOLayer(*rtpSocket);
|
||||
ClosingService::AttachIOLayer(*rtcpSocket);
|
||||
|
||||
// Reduce the chance of using duplicate port numbers.
|
||||
srand(time(NULL));
|
||||
// rand() * 1000 may overflow int type, use long long.
|
||||
|
@ -34,7 +34,6 @@
|
||||
#include "prnetdb.h"
|
||||
#include "prerr.h"
|
||||
#include "NetworkActivityMonitor.h"
|
||||
#include "ClosingService.h"
|
||||
|
||||
using namespace mozilla::net;
|
||||
|
||||
@ -110,7 +109,6 @@ void ARTPSession::MakeUDPSocket(PRFileDesc **s, unsigned port) {
|
||||
}
|
||||
|
||||
NetworkActivityMonitor::AttachIOLayer(*s);
|
||||
ClosingService::AttachIOLayer(*s);
|
||||
|
||||
PRNetAddr addr;
|
||||
addr.inet.family = PR_AF_INET;
|
||||
|
@ -31,7 +31,6 @@
|
||||
#include <media/stagefright/MetaData.h>
|
||||
#include <utils/ByteOrder.h>
|
||||
|
||||
#include "ClosingService.h"
|
||||
#include "NetworkActivityMonitor.h"
|
||||
|
||||
using namespace mozilla::net;
|
||||
@ -65,7 +64,6 @@ ARTPWriter::ARTPWriter(int fd)
|
||||
}
|
||||
|
||||
NetworkActivityMonitor::AttachIOLayer(mSocket);
|
||||
ClosingService::AttachIOLayer(mSocket);
|
||||
|
||||
mRTPAddr.inet.family = PR_AF_INET;
|
||||
|
||||
|
@ -34,7 +34,6 @@
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsString.h"
|
||||
#include "nsNetCID.h"
|
||||
#include "ClosingService.h"
|
||||
#include "nsIServiceManager.h"
|
||||
#include "nsICryptoHash.h"
|
||||
|
||||
@ -284,7 +283,6 @@ void ARTSPConnection::onConnect(const sp<AMessage> &msg) {
|
||||
}
|
||||
|
||||
NetworkActivityMonitor::AttachIOLayer(mSocket);
|
||||
ClosingService::AttachIOLayer(mSocket);
|
||||
|
||||
MakeSocketBlocking(mSocket, false);
|
||||
|
||||
|
@ -27,7 +27,6 @@
|
||||
#include "prnetdb.h"
|
||||
#include "prerr.h"
|
||||
#include "NetworkActivityMonitor.h"
|
||||
#include "ClosingService.h"
|
||||
|
||||
using namespace mozilla::net;
|
||||
|
||||
@ -45,7 +44,6 @@ UDPPusher::UDPPusher(const char *filename, unsigned port)
|
||||
}
|
||||
|
||||
NetworkActivityMonitor::AttachIOLayer(mSocket);
|
||||
ClosingService::AttachIOLayer(mSocket);
|
||||
|
||||
PRNetAddr addr;
|
||||
addr.inet.family = PR_AF_INET;
|
||||
|
@ -149,7 +149,6 @@ void nsNotifyAddrListener::checkLink(void)
|
||||
void nsNotifyAddrListener::OnNetlinkMessage(int aNetlinkSocket)
|
||||
{
|
||||
struct nlmsghdr *nlh;
|
||||
struct rtmsg *route_entry;
|
||||
|
||||
// The buffer size below, (4095) was chosen partly based on testing and
|
||||
// partly on existing sample source code using this size. It needs to be
|
||||
@ -158,7 +157,6 @@ void nsNotifyAddrListener::OnNetlinkMessage(int aNetlinkSocket)
|
||||
struct rtattr *attr;
|
||||
int attr_len;
|
||||
const struct ifaddrmsg* newifam;
|
||||
bool link_local;
|
||||
|
||||
// inspired by check_pf.c.
|
||||
nsAutoPtr<char> addr;
|
||||
@ -181,131 +179,79 @@ void nsNotifyAddrListener::OnNetlinkMessage(int aNetlinkSocket)
|
||||
break;
|
||||
}
|
||||
|
||||
switch(nlh->nlmsg_type) {
|
||||
case RTM_DELROUTE:
|
||||
LOG(("nsNotifyAddrListener::OnNetlinkMessage deleted route"));
|
||||
case RTM_NEWROUTE:
|
||||
LOG(("nsNotifyAddrListener::OnNetlinkMessage new/deleted route"));
|
||||
// Get the route data
|
||||
route_entry = static_cast<struct rtmsg *>(NLMSG_DATA(nlh));
|
||||
LOG(("nsNotifyAddrListener::OnNetlinkMessage: new/deleted address\n"));
|
||||
newifam = reinterpret_cast<struct ifaddrmsg*>(NLMSG_DATA(nlh));
|
||||
|
||||
// We are just intrested in main routing table
|
||||
if (route_entry->rtm_table != RT_TABLE_MAIN)
|
||||
continue;
|
||||
|
||||
if ((route_entry->rtm_family != AF_INET) &&
|
||||
(route_entry->rtm_family != AF_INET6)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
attr = (struct rtattr *) RTM_RTA(route_entry);
|
||||
attr_len = RTM_PAYLOAD(nlh);
|
||||
link_local = false;
|
||||
|
||||
/* Loop through all attributes */
|
||||
for ( ; RTA_OK(attr, attr_len); attr = RTA_NEXT(attr, attr_len)) {
|
||||
if (attr->rta_type == RTA_GATEWAY) {
|
||||
if (route_entry->rtm_family == AF_INET6) {
|
||||
unsigned char *g = (unsigned char *)
|
||||
RTA_DATA(attr);
|
||||
if ((g[0] == 0xFE) && ((g[1] & 0xc0) == 0x80)) {
|
||||
link_local = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!link_local) {
|
||||
LOG(("OnNetlinkMessage: route update\n"));
|
||||
networkChange = true;
|
||||
} else {
|
||||
LOG(("OnNetlinkMessage: ignored link-local route update\n"));
|
||||
}
|
||||
break;
|
||||
|
||||
case RTM_DELADDR:
|
||||
LOG(("nsNotifyAddrListener::OnNetlinkMessage deleted address"));
|
||||
case RTM_NEWADDR:
|
||||
LOG(("nsNotifyAddrListener::OnNetlinkMessage: new/deleted address"
|
||||
"\n"));
|
||||
newifam = reinterpret_cast<struct ifaddrmsg*>(NLMSG_DATA(nlh));
|
||||
|
||||
if ((newifam->ifa_family != AF_INET) &&
|
||||
(newifam->ifa_family != AF_INET6)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
attr = IFA_RTA (newifam);
|
||||
attr_len = IFA_PAYLOAD (nlh);
|
||||
for (;attr_len && RTA_OK (attr, attr_len);
|
||||
attr = RTA_NEXT (attr, attr_len)) {
|
||||
if (attr->rta_type == IFA_ADDRESS) {
|
||||
if (newifam->ifa_family == AF_INET) {
|
||||
struct in_addr* in = (struct in_addr*)RTA_DATA(attr);
|
||||
addr = (char*)malloc(INET_ADDRSTRLEN);
|
||||
inet_ntop(AF_INET, in, addr.get(), INET_ADDRSTRLEN);
|
||||
} else {
|
||||
struct in6_addr* in = (struct in6_addr*)RTA_DATA(attr);
|
||||
addr = (char*)malloc(INET6_ADDRSTRLEN);
|
||||
inet_ntop(AF_INET6, in, addr.get(), INET6_ADDRSTRLEN);
|
||||
}
|
||||
} else if (attr->rta_type == IFA_LOCAL) {
|
||||
if (newifam->ifa_family == AF_INET) {
|
||||
struct in_addr* in = (struct in_addr*)RTA_DATA(attr);
|
||||
localaddr = (char*)malloc(INET_ADDRSTRLEN);
|
||||
inet_ntop(AF_INET, in, localaddr.get(), INET_ADDRSTRLEN);
|
||||
} else {
|
||||
struct in6_addr* in = (struct in6_addr*)RTA_DATA(attr);
|
||||
localaddr = (char*)malloc(INET6_ADDRSTRLEN);
|
||||
inet_ntop(AF_INET6, in, localaddr.get(), INET6_ADDRSTRLEN);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (localaddr) {
|
||||
addr = localaddr;
|
||||
}
|
||||
if (!addr) {
|
||||
continue;
|
||||
}
|
||||
if (nlh->nlmsg_type == RTM_NEWADDR) {
|
||||
LOG(("nsNotifyAddrListener::OnNetlinkMessage: a new address "
|
||||
"- %s.", addr.get()));
|
||||
struct ifaddrmsg* ifam;
|
||||
nsCString addrStr;
|
||||
addrStr.Assign(addr);
|
||||
if (mAddressInfo.Get(addrStr, &ifam)) {
|
||||
LOG(("nsNotifyAddrListener::OnNetlinkMessage: the address "
|
||||
"already known."));
|
||||
if (memcmp(ifam, newifam, sizeof(struct ifaddrmsg))) {
|
||||
LOG(("nsNotifyAddrListener::OnNetlinkMessage: but "
|
||||
"the address info has been changed."));
|
||||
networkChange = true;
|
||||
memcpy(ifam, newifam, sizeof(struct ifaddrmsg));
|
||||
}
|
||||
} else {
|
||||
networkChange = true;
|
||||
ifam = (struct ifaddrmsg*)malloc(sizeof(struct ifaddrmsg));
|
||||
memcpy(ifam, newifam, sizeof(struct ifaddrmsg));
|
||||
mAddressInfo.Put(addrStr,ifam);
|
||||
}
|
||||
} else {
|
||||
LOG(("nsNotifyAddrListener::OnNetlinkMessage: an address "
|
||||
"has been deleted - %s.", addr.get()));
|
||||
networkChange = true;
|
||||
nsCString addrStr;
|
||||
addrStr.Assign(addr);
|
||||
mAddressInfo.Remove(addrStr);
|
||||
}
|
||||
|
||||
// clean it up.
|
||||
localaddr = nullptr;
|
||||
addr = nullptr;
|
||||
break;
|
||||
|
||||
default:
|
||||
if ((newifam->ifa_family != AF_INET) &&
|
||||
(newifam->ifa_family != AF_INET6)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
attr = IFA_RTA (newifam);
|
||||
attr_len = IFA_PAYLOAD (nlh);
|
||||
for (;attr_len && RTA_OK (attr, attr_len);
|
||||
attr = RTA_NEXT (attr, attr_len)) {
|
||||
if (attr->rta_type == IFA_ADDRESS) {
|
||||
if (newifam->ifa_family == AF_INET) {
|
||||
struct in_addr* in = (struct in_addr*)RTA_DATA(attr);
|
||||
addr = (char*)malloc(INET_ADDRSTRLEN);
|
||||
inet_ntop(AF_INET, in, addr.get(), INET_ADDRSTRLEN);
|
||||
} else {
|
||||
struct in6_addr* in = (struct in6_addr*)RTA_DATA(attr);
|
||||
addr = (char*)malloc(INET6_ADDRSTRLEN);
|
||||
inet_ntop(AF_INET6, in, addr.get(), INET6_ADDRSTRLEN);
|
||||
}
|
||||
} else if (attr->rta_type == IFA_LOCAL) {
|
||||
if (newifam->ifa_family == AF_INET) {
|
||||
struct in_addr* in = (struct in_addr*)RTA_DATA(attr);
|
||||
localaddr = (char*)malloc(INET_ADDRSTRLEN);
|
||||
inet_ntop(AF_INET, in, localaddr.get(), INET_ADDRSTRLEN);
|
||||
} else {
|
||||
struct in6_addr* in = (struct in6_addr*)RTA_DATA(attr);
|
||||
localaddr = (char*)malloc(INET6_ADDRSTRLEN);
|
||||
inet_ntop(AF_INET6, in, localaddr.get(), INET6_ADDRSTRLEN);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (localaddr) {
|
||||
addr = localaddr;
|
||||
}
|
||||
if (!addr) {
|
||||
continue;
|
||||
}
|
||||
if (nlh->nlmsg_type == RTM_NEWADDR) {
|
||||
LOG(("nsNotifyAddrListener::OnNetlinkMessage: a new address "
|
||||
"- %s.", addr.get()));
|
||||
struct ifaddrmsg* ifam;
|
||||
nsCString addrStr;
|
||||
addrStr.Assign(addr);
|
||||
if (mAddressInfo.Get(addrStr, &ifam)) {
|
||||
LOG(("nsNotifyAddrListener::OnNetlinkMessage: the address "
|
||||
"already known."));
|
||||
if (memcmp(ifam, newifam, sizeof(struct ifaddrmsg))) {
|
||||
LOG(("nsNotifyAddrListener::OnNetlinkMessage: but "
|
||||
"the address info has been changed."));
|
||||
networkChange = true;
|
||||
memcpy(ifam, newifam, sizeof(struct ifaddrmsg));
|
||||
}
|
||||
} else {
|
||||
networkChange = true;
|
||||
ifam = (struct ifaddrmsg*)malloc(sizeof(struct ifaddrmsg));
|
||||
memcpy(ifam, newifam, sizeof(struct ifaddrmsg));
|
||||
mAddressInfo.Put(addrStr,ifam);
|
||||
}
|
||||
} else {
|
||||
LOG(("nsNotifyAddrListener::OnNetlinkMessage: an address "
|
||||
"has been deleted - %s.", addr.get()));
|
||||
networkChange = true;
|
||||
nsCString addrStr;
|
||||
addrStr.Assign(addr);
|
||||
mAddressInfo.Remove(addrStr);
|
||||
}
|
||||
|
||||
// clean it up.
|
||||
localaddr = nullptr;
|
||||
addr = nullptr;
|
||||
}
|
||||
|
||||
if (networkChange && mAllowChangedEvent) {
|
||||
@ -329,8 +275,7 @@ nsNotifyAddrListener::Run()
|
||||
memset(&addr, 0, sizeof(addr)); // clear addr
|
||||
|
||||
addr.nl_family = AF_NETLINK;
|
||||
addr.nl_groups = RTMGRP_IPV4_ROUTE | RTMGRP_IPV4_IFADDR |
|
||||
RTMGRP_IPV6_IFADDR | RTMGRP_IPV6_ROUTE;
|
||||
addr.nl_groups = RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR;
|
||||
|
||||
if (bind(netlinkSocket, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
|
||||
// failure!
|
||||
|
335
testing/marionette/accessibility.js
Normal file
335
testing/marionette/accessibility.js
Normal file
@ -0,0 +1,335 @@
|
||||
/* 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/. */
|
||||
|
||||
/* global Accessibility, Components, Log, ElementNotAccessibleError,
|
||||
XPCOMUtils */
|
||||
|
||||
var {classes: Cc, interfaces: Ci, utils: Cu} = Components;
|
||||
|
||||
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
|
||||
Cu.import('resource://gre/modules/Log.jsm');
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, 'setInterval',
|
||||
'resource://gre/modules/Timer.jsm');
|
||||
XPCOMUtils.defineLazyModuleGetter(this, 'clearInterval',
|
||||
'resource://gre/modules/Timer.jsm');
|
||||
XPCOMUtils.defineLazyModuleGetter(this, 'ElementNotAccessibleError',
|
||||
'chrome://marionette/content/error.js');
|
||||
|
||||
this.EXPORTED_SYMBOLS = ['Accessibility'];
|
||||
|
||||
/**
|
||||
* Accessible states used to check element's state from the accessiblity API
|
||||
* perspective.
|
||||
*/
|
||||
const states = {
|
||||
unavailable: Ci.nsIAccessibleStates.STATE_UNAVAILABLE,
|
||||
focusable: Ci.nsIAccessibleStates.STATE_FOCUSABLE,
|
||||
selectable: Ci.nsIAccessibleStates.STATE_SELECTABLE,
|
||||
selected: Ci.nsIAccessibleStates.STATE_SELECTED
|
||||
};
|
||||
|
||||
var logger = Log.repository.getLogger('Marionette');
|
||||
|
||||
/**
|
||||
* Component responsible for interacting with platform accessibility API. Its
|
||||
* methods serve as wrappers for testing content and chrome accessibility as
|
||||
* well as accessibility of user interactions.
|
||||
*
|
||||
* @param {Function} getCapabilies
|
||||
* Session capabilities getter.
|
||||
*/
|
||||
this.Accessibility = function Accessibility(getCapabilies = () => {}) {
|
||||
// A flag indicating whether the accessibility issue should be logged or cause
|
||||
// an exception. Default: log to stdout.
|
||||
Object.defineProperty(this, 'strict', {
|
||||
configurable: true,
|
||||
get: function() {
|
||||
let capabilies = getCapabilies();
|
||||
return !!capabilies.raisesAccessibilityExceptions;
|
||||
}
|
||||
});
|
||||
// An interface for in-process accessibility clients
|
||||
// Note: we access it lazily to not enable accessibility when it is not needed
|
||||
Object.defineProperty(this, 'retrieval', {
|
||||
configurable: true,
|
||||
get: function() {
|
||||
delete this.retrieval;
|
||||
this.retrieval = Cc[
|
||||
'@mozilla.org/accessibleRetrieval;1'].getService(
|
||||
Ci.nsIAccessibleRetrieval);
|
||||
return this.retrieval;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
Accessibility.prototype = {
|
||||
|
||||
/**
|
||||
* Number of attempts to get an accessible object for an element. We attempt
|
||||
* more than once because accessible tree can be out of sync with the DOM tree
|
||||
* for a short period of time.
|
||||
* @type {Number}
|
||||
*/
|
||||
GET_ACCESSIBLE_ATTEMPTS: 100,
|
||||
|
||||
/**
|
||||
* An interval between attempts to retrieve an accessible object for an
|
||||
* element.
|
||||
* @type {Number} ms
|
||||
*/
|
||||
GET_ACCESSIBLE_ATTEMPT_INTERVAL: 10,
|
||||
|
||||
/**
|
||||
* Accessible object roles that support some action
|
||||
* @type Object
|
||||
*/
|
||||
ACTIONABLE_ROLES: new Set([
|
||||
'pushbutton',
|
||||
'checkbutton',
|
||||
'combobox',
|
||||
'key',
|
||||
'link',
|
||||
'menuitem',
|
||||
'check menu item',
|
||||
'radio menu item',
|
||||
'option',
|
||||
'listbox option',
|
||||
'listbox rich option',
|
||||
'check rich option',
|
||||
'combobox option',
|
||||
'radiobutton',
|
||||
'rowheader',
|
||||
'switch',
|
||||
'slider',
|
||||
'spinbutton',
|
||||
'pagetab',
|
||||
'entry',
|
||||
'outlineitem'
|
||||
]),
|
||||
|
||||
/**
|
||||
* Get an accessible object for a DOM element
|
||||
* @param nsIDOMElement element
|
||||
* @param Boolean mustHaveAccessible a flag indicating that the element must
|
||||
* have an accessible object
|
||||
* @return nsIAccessible object for the element
|
||||
*/
|
||||
getAccessibleObject(element, mustHaveAccessible = false) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let acc = this.retrieval.getAccessibleFor(element);
|
||||
|
||||
if (acc || !mustHaveAccessible) {
|
||||
// If accessible object is found, return it. If it is not required,
|
||||
// also resolve.
|
||||
resolve(acc);
|
||||
} else {
|
||||
// If we require an accessible object, we need to poll for it because
|
||||
// accessible tree might be out of sync with DOM tree for a short time.
|
||||
let attempts = this.GET_ACCESSIBLE_ATTEMPTS;
|
||||
let intervalId = setInterval(() => {
|
||||
let acc = this.retrieval.getAccessibleFor(element);
|
||||
if (acc || --attempts <= 0) {
|
||||
clearInterval(intervalId);
|
||||
if (acc) { resolve(acc); }
|
||||
else { reject(); }
|
||||
}
|
||||
}, this.GET_ACCESSIBLE_ATTEMPT_INTERVAL);
|
||||
}
|
||||
}).catch(() => this.error(
|
||||
'Element does not have an accessible object', element));
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if the accessible has a role that supports some action
|
||||
* @param nsIAccessible object
|
||||
* @return Boolean an indicator of role being actionable
|
||||
*/
|
||||
isActionableRole(accessible) {
|
||||
return this.ACTIONABLE_ROLES.has(
|
||||
this.retrieval.getStringRole(accessible.role));
|
||||
},
|
||||
|
||||
/**
|
||||
* Determine if an accessible has at least one action that it supports
|
||||
* @param nsIAccessible object
|
||||
* @return Boolean an indicator of supporting at least one accessible action
|
||||
*/
|
||||
hasActionCount(accessible) {
|
||||
return accessible.actionCount > 0;
|
||||
},
|
||||
|
||||
/**
|
||||
* Determine if an accessible has a valid name
|
||||
* @param nsIAccessible object
|
||||
* @return Boolean an indicator that the element has a non empty valid name
|
||||
*/
|
||||
hasValidName(accessible) {
|
||||
return accessible.name && accessible.name.trim();
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if an accessible has a set hidden attribute
|
||||
* @param nsIAccessible object
|
||||
* @return Boolean an indicator that the element has a hidden accessible
|
||||
* attribute set to true
|
||||
*/
|
||||
hasHiddenAttribute(accessible) {
|
||||
let hidden = false;
|
||||
try {
|
||||
hidden = accessible.attributes.getStringProperty('hidden');
|
||||
} finally {
|
||||
// If the property is missing, exception will be thrown.
|
||||
return hidden && hidden === 'true';
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Verify if an accessible has a given state
|
||||
* @param nsIAccessible object
|
||||
* @param Number stateToMatch the state to match
|
||||
* @return Boolean accessible has a state
|
||||
*/
|
||||
matchState(accessible, stateToMatch) {
|
||||
let state = {};
|
||||
accessible.getState(state, {});
|
||||
return !!(state.value & stateToMatch);
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if an accessible is hidden from the user of the accessibility API
|
||||
* @param nsIAccessible object
|
||||
* @return Boolean an indicator that the element is hidden from the user
|
||||
*/
|
||||
isHidden(accessible) {
|
||||
while (accessible) {
|
||||
if (this.hasHiddenAttribute(accessible)) {
|
||||
return true;
|
||||
}
|
||||
accessible = accessible.parent;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Send an error message or log the error message in the log
|
||||
* @param String message
|
||||
* @param DOMElement element that caused an error
|
||||
*/
|
||||
error(message, element) {
|
||||
if (!message) {
|
||||
return;
|
||||
}
|
||||
if (element) {
|
||||
let {id, tagName, className} = element;
|
||||
message += `: id: ${id}, tagName: ${tagName}, className: ${className}`;
|
||||
}
|
||||
if (this.strict) {
|
||||
throw new ElementNotAccessibleError(message);
|
||||
}
|
||||
logger.error(message);
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if the element's visible state corresponds to its accessibility API
|
||||
* visibility
|
||||
* @param nsIAccessible object
|
||||
* @param WebElement corresponding to nsIAccessible object
|
||||
* @param Boolean visible element's visibility state
|
||||
*/
|
||||
checkVisible(accessible, element, visible) {
|
||||
if (!accessible) {
|
||||
return;
|
||||
}
|
||||
let hiddenAccessibility = this.isHidden(accessible);
|
||||
let message;
|
||||
if (visible && hiddenAccessibility) {
|
||||
message = 'Element is not currently visible via the accessibility API ' +
|
||||
'and may not be manipulated by it';
|
||||
} else if (!visible && !hiddenAccessibility) {
|
||||
message = 'Element is currently only visible via the accessibility API ' +
|
||||
'and can be manipulated by it';
|
||||
}
|
||||
this.error(message, element);
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if the element's unavailable accessibility state matches the enabled
|
||||
* state
|
||||
* @param nsIAccessible object
|
||||
* @param WebElement corresponding to nsIAccessible object
|
||||
* @param Boolean enabled element's enabled state
|
||||
* @param Object container frame and optional ShadowDOM
|
||||
*/
|
||||
checkEnabled(accessible, element, enabled, container) {
|
||||
if (!accessible) {
|
||||
return;
|
||||
}
|
||||
let disabledAccessibility = this.matchState(accessible, states.unavailable);
|
||||
let explorable = container.frame.document.defaultView.getComputedStyle(
|
||||
element).getPropertyValue('pointer-events') !== 'none';
|
||||
let message;
|
||||
|
||||
if (!explorable && !disabledAccessibility) {
|
||||
message = 'Element is enabled but is not explorable via the ' +
|
||||
'accessibility API';
|
||||
} else if (enabled && disabledAccessibility) {
|
||||
message = 'Element is enabled but disabled via the accessibility API';
|
||||
} else if (!enabled && !disabledAccessibility) {
|
||||
message = 'Element is disabled but enabled via the accessibility API';
|
||||
}
|
||||
this.error(message, element);
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if it is possible to activate an element with the accessibility API
|
||||
* @param nsIAccessible object
|
||||
* @param WebElement corresponding to nsIAccessible object
|
||||
*/
|
||||
checkActionable(accessible, element) {
|
||||
if (!accessible) {
|
||||
return;
|
||||
}
|
||||
let message;
|
||||
if (!this.hasActionCount(accessible)) {
|
||||
message = 'Element does not support any accessible actions';
|
||||
} else if (!this.isActionableRole(accessible)) {
|
||||
message = 'Element does not have a correct accessibility role ' +
|
||||
'and may not be manipulated via the accessibility API';
|
||||
} else if (!this.hasValidName(accessible)) {
|
||||
message = 'Element is missing an accessible name';
|
||||
} else if (!this.matchState(accessible, states.focusable)) {
|
||||
message = 'Element is not focusable via the accessibility API';
|
||||
}
|
||||
this.error(message, element);
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if element's selected state corresponds to its accessibility API
|
||||
* selected state.
|
||||
* @param nsIAccessible object
|
||||
* @param WebElement corresponding to nsIAccessible object
|
||||
* @param Boolean selected element's selected state
|
||||
*/
|
||||
checkSelected(accessible, element, selected) {
|
||||
if (!accessible) {
|
||||
return;
|
||||
}
|
||||
if (!this.matchState(accessible, states.selectable)) {
|
||||
// Element is not selectable via the accessibility API
|
||||
return;
|
||||
}
|
||||
|
||||
let selectedAccessibility = this.matchState(accessible, states.selected);
|
||||
let message;
|
||||
if (selected && !selectedAccessibility) {
|
||||
message =
|
||||
'Element is selected but not selected via the accessibility API';
|
||||
} else if (!selected && selectedAccessibility) {
|
||||
message =
|
||||
'Element is not selected but selected via the accessibility API';
|
||||
}
|
||||
this.error(message, element);
|
||||
}
|
||||
};
|
@ -23,6 +23,7 @@ XPCOMUtils.defineLazyServiceGetter(
|
||||
this, "cookieManager", "@mozilla.org/cookiemanager;1", "nsICookieManager2");
|
||||
|
||||
Cu.import("chrome://marionette/content/actions.js");
|
||||
Cu.import("chrome://marionette/content/interactions.js");
|
||||
Cu.import("chrome://marionette/content/elements.js");
|
||||
Cu.import("chrome://marionette/content/error.js");
|
||||
Cu.import("chrome://marionette/content/modal.js");
|
||||
@ -164,6 +165,8 @@ this.GeckoDriver = function(appName, device, stopSignal, emulator) {
|
||||
"version": Services.appinfo.version,
|
||||
};
|
||||
|
||||
this.interactions = new Interactions(utils, () => this.sessionCapabilities);
|
||||
|
||||
this.mm = globalMessageManager;
|
||||
this.listener = proxy.toListener(() => this.mm, this.sendAsync.bind(this));
|
||||
|
||||
@ -1981,10 +1984,9 @@ GeckoDriver.prototype.clickElement = function(cmd, resp) {
|
||||
|
||||
switch (this.context) {
|
||||
case Context.CHROME:
|
||||
// click atom fails, fall back to click() action
|
||||
let win = this.getCurrentWindow();
|
||||
let el = this.curBrowser.elementManager.getKnownElement(id, { frame: win });
|
||||
el.click();
|
||||
yield this.interactions.clickElement({ frame: win },
|
||||
this.curBrowser.elementManager, id)
|
||||
break;
|
||||
|
||||
case Context.CONTENT:
|
||||
@ -2082,8 +2084,8 @@ GeckoDriver.prototype.isElementDisplayed = function(cmd, resp) {
|
||||
switch (this.context) {
|
||||
case Context.CHROME:
|
||||
let win = this.getCurrentWindow();
|
||||
let el = this.curBrowser.elementManager.getKnownElement(id, {frame: win});
|
||||
resp.body.value = utils.isElementDisplayed(el);
|
||||
resp.body.value = yield this.interactions.isElementDisplayed(
|
||||
{frame: win}, this.curBrowser.elementManager, id);
|
||||
break;
|
||||
|
||||
case Context.CONTENT:
|
||||
@ -2130,8 +2132,8 @@ GeckoDriver.prototype.isElementEnabled = function(cmd, resp) {
|
||||
case Context.CHROME:
|
||||
// Selenium atom doesn't quite work here
|
||||
let win = this.getCurrentWindow();
|
||||
let el = this.curBrowser.elementManager.getKnownElement(id, {frame: win});
|
||||
resp.body.value = !(!!el.disabled);
|
||||
resp.body.value = yield this.interactions.isElementEnabled(
|
||||
{frame: win}, this.curBrowser.elementManager, id);
|
||||
break;
|
||||
|
||||
case Context.CONTENT:
|
||||
@ -2153,14 +2155,8 @@ GeckoDriver.prototype.isElementSelected = function(cmd, resp) {
|
||||
case Context.CHROME:
|
||||
// Selenium atom doesn't quite work here
|
||||
let win = this.getCurrentWindow();
|
||||
let el = this.curBrowser.elementManager.getKnownElement(id, { frame: win });
|
||||
if (typeof el.checked != "undefined") {
|
||||
resp.body.value = !!el.checked;
|
||||
} else if (typeof el.selected != "undefined") {
|
||||
resp.body.value = !!el.selected;
|
||||
} else {
|
||||
resp.body.value = true;
|
||||
}
|
||||
resp.body.value = yield this.interactions.isElementSelected(
|
||||
{ frame: win }, this.curBrowser.elementManager, id);
|
||||
break;
|
||||
|
||||
case Context.CONTENT:
|
||||
@ -2209,15 +2205,8 @@ GeckoDriver.prototype.sendKeysToElement = function(cmd, resp) {
|
||||
switch (this.context) {
|
||||
case Context.CHROME:
|
||||
let win = this.getCurrentWindow();
|
||||
let el = this.curBrowser.elementManager.getKnownElement(id, { frame: win });
|
||||
utils.sendKeysToElement(
|
||||
win,
|
||||
el,
|
||||
value,
|
||||
() => {},
|
||||
e => { throw e; },
|
||||
cmd.id,
|
||||
true /* ignore visibility check */);
|
||||
yield this.interactions.sendKeysToElement(
|
||||
{ frame: win }, this.curBrowser.elementManager, id, value, true);
|
||||
break;
|
||||
|
||||
case Context.CONTENT:
|
||||
@ -2806,9 +2795,6 @@ GeckoDriver.prototype.sendKeysToDialog = function(cmd, resp) {
|
||||
win,
|
||||
loginTextbox,
|
||||
cmd.parameters.value,
|
||||
() => {},
|
||||
e => { throw e; },
|
||||
this.command_id,
|
||||
true /* ignore visibility check */);
|
||||
};
|
||||
|
||||
|
@ -5,12 +5,6 @@
|
||||
let {classes: Cc, interfaces: Ci, utils: Cu} = Components;
|
||||
|
||||
Cu.import("chrome://marionette/content/error.js");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, 'setInterval',
|
||||
'resource://gre/modules/Timer.jsm');
|
||||
XPCOMUtils.defineLazyModuleGetter(this, 'clearInterval',
|
||||
'resource://gre/modules/Timer.jsm');
|
||||
|
||||
/**
|
||||
* The ElementManager manages DOM element references and
|
||||
@ -30,7 +24,6 @@ XPCOMUtils.defineLazyModuleGetter(this, 'clearInterval',
|
||||
*/
|
||||
|
||||
this.EXPORTED_SYMBOLS = [
|
||||
"Accessibility",
|
||||
"elements",
|
||||
"ElementManager",
|
||||
"CLASS_NAME",
|
||||
@ -47,8 +40,8 @@ this.EXPORTED_SYMBOLS = [
|
||||
|
||||
const DOCUMENT_POSITION_DISCONNECTED = 1;
|
||||
|
||||
const uuidGen = Components.classes["@mozilla.org/uuid-generator;1"]
|
||||
.getService(Components.interfaces.nsIUUIDGenerator);
|
||||
const uuidGen = Cc["@mozilla.org/uuid-generator;1"]
|
||||
.getService(Ci.nsIUUIDGenerator);
|
||||
|
||||
this.CLASS_NAME = "class name";
|
||||
this.SELECTOR = "css selector";
|
||||
@ -61,192 +54,6 @@ this.XPATH = "xpath";
|
||||
this.ANON= "anon";
|
||||
this.ANON_ATTRIBUTE = "anon attribute";
|
||||
|
||||
this.Accessibility = function Accessibility() {
|
||||
// A flag indicating whether the accessibility issue should be logged or cause
|
||||
// an exception. Default: log to stdout.
|
||||
this.strict = false;
|
||||
// An interface for in-process accessibility clients
|
||||
// Note: we access it lazily to not enable accessibility when it is not needed
|
||||
Object.defineProperty(this, 'accessibleRetrieval', {
|
||||
configurable: true,
|
||||
get: function() {
|
||||
delete this.accessibleRetrieval;
|
||||
this.accessibleRetrieval = Components.classes[
|
||||
'@mozilla.org/accessibleRetrieval;1'].getService(
|
||||
Components.interfaces.nsIAccessibleRetrieval);
|
||||
return this.accessibleRetrieval;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
Accessibility.prototype = {
|
||||
|
||||
/**
|
||||
* Number of attempts to get an accessible object for an element. We attempt
|
||||
* more than once because accessible tree can be out of sync with the DOM tree
|
||||
* for a short period of time.
|
||||
* @type {Number}
|
||||
*/
|
||||
GET_ACCESSIBLE_ATTEMPTS: 100,
|
||||
|
||||
/**
|
||||
* An interval between attempts to retrieve an accessible object for an
|
||||
* element.
|
||||
* @type {Number} ms
|
||||
*/
|
||||
GET_ACCESSIBLE_ATTEMPT_INTERVAL: 10,
|
||||
|
||||
/**
|
||||
* Accessible object roles that support some action
|
||||
* @type Object
|
||||
*/
|
||||
actionableRoles: new Set([
|
||||
'pushbutton',
|
||||
'checkbutton',
|
||||
'combobox',
|
||||
'key',
|
||||
'link',
|
||||
'menuitem',
|
||||
'check menu item',
|
||||
'radio menu item',
|
||||
'option',
|
||||
'listbox option',
|
||||
'listbox rich option',
|
||||
'check rich option',
|
||||
'combobox option',
|
||||
'radiobutton',
|
||||
'rowheader',
|
||||
'switch',
|
||||
'slider',
|
||||
'spinbutton',
|
||||
'pagetab',
|
||||
'entry',
|
||||
'outlineitem'
|
||||
]),
|
||||
|
||||
/**
|
||||
* Get an accessible object for a DOM element
|
||||
* @param nsIDOMElement element
|
||||
* @param Boolean mustHaveAccessible a flag indicating that the element must
|
||||
* have an accessible object
|
||||
* @return nsIAccessible object for the element
|
||||
*/
|
||||
getAccessibleObject(element, mustHaveAccessible = false) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let acc = this.accessibleRetrieval.getAccessibleFor(element);
|
||||
|
||||
if (acc || !mustHaveAccessible) {
|
||||
// If accessible object is found, return it. If it is not required,
|
||||
// also resolve.
|
||||
resolve(acc);
|
||||
} else {
|
||||
// If we require an accessible object, we need to poll for it because
|
||||
// accessible tree might be out of sync with DOM tree for a short time.
|
||||
let attempts = this.GET_ACCESSIBLE_ATTEMPTS;
|
||||
let intervalId = setInterval(() => {
|
||||
let acc = this.accessibleRetrieval.getAccessibleFor(element);
|
||||
if (acc || --attempts <= 0) {
|
||||
clearInterval(intervalId);
|
||||
if (acc) { resolve(acc); }
|
||||
else { reject(); }
|
||||
}
|
||||
}, this.GET_ACCESSIBLE_ATTEMPT_INTERVAL);
|
||||
}
|
||||
}).catch(() => this.handleErrorMessage(
|
||||
'Element does not have an accessible object', element));
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if the accessible has a role that supports some action
|
||||
* @param nsIAccessible object
|
||||
* @return Boolean an indicator of role being actionable
|
||||
*/
|
||||
isActionableRole(accessible) {
|
||||
return this.actionableRoles.has(
|
||||
this.accessibleRetrieval.getStringRole(accessible.role));
|
||||
},
|
||||
|
||||
/**
|
||||
* Determine if an accessible has at least one action that it supports
|
||||
* @param nsIAccessible object
|
||||
* @return Boolean an indicator of supporting at least one accessible action
|
||||
*/
|
||||
hasActionCount(accessible) {
|
||||
return accessible.actionCount > 0;
|
||||
},
|
||||
|
||||
/**
|
||||
* Determine if an accessible has a valid name
|
||||
* @param nsIAccessible object
|
||||
* @return Boolean an indicator that the element has a non empty valid name
|
||||
*/
|
||||
hasValidName(accessible) {
|
||||
return accessible.name && accessible.name.trim();
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if an accessible has a set hidden attribute
|
||||
* @param nsIAccessible object
|
||||
* @return Boolean an indicator that the element has a hidden accessible
|
||||
* attribute set to true
|
||||
*/
|
||||
hasHiddenAttribute(accessible) {
|
||||
let hidden;
|
||||
try {
|
||||
hidden = accessible.attributes.getStringProperty('hidden');
|
||||
} finally {
|
||||
// If the property is missing, exception will be thrown.
|
||||
return hidden && hidden === 'true';
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Verify if an accessible has a given state
|
||||
* @param nsIAccessible object
|
||||
* @param String stateName name of the state to match
|
||||
* @return Boolean accessible has a state
|
||||
*/
|
||||
matchState(accessible, stateName) {
|
||||
let stateToMatch = Components.interfaces.nsIAccessibleStates[stateName];
|
||||
let state = {};
|
||||
accessible.getState(state, {});
|
||||
return !!(state.value & stateToMatch);
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if an accessible is hidden from the user of the accessibility API
|
||||
* @param nsIAccessible object
|
||||
* @return Boolean an indicator that the element is hidden from the user
|
||||
*/
|
||||
isHidden(accessible) {
|
||||
while (accessible) {
|
||||
if (this.hasHiddenAttribute(accessible)) {
|
||||
return true;
|
||||
}
|
||||
accessible = accessible.parent;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Send an error message or log the error message in the log
|
||||
* @param String message
|
||||
* @param DOMElement element that caused an error
|
||||
*/
|
||||
handleErrorMessage(message, element) {
|
||||
if (!message) {
|
||||
return;
|
||||
}
|
||||
if (element) {
|
||||
message += ` -> id: ${element.id}, tagName: ${element.tagName}, className: ${element.className}\n`;
|
||||
}
|
||||
if (this.strict) {
|
||||
throw new ElementNotAccessibleError(message);
|
||||
}
|
||||
dump(Date.now() + " Marionette: " + message);
|
||||
}
|
||||
};
|
||||
|
||||
this.ElementManager = function ElementManager(notSupported) {
|
||||
this.seenItems = {};
|
||||
this.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
||||
@ -291,7 +98,7 @@ ElementManager.prototype = {
|
||||
}
|
||||
}
|
||||
let id = elements.generateUUID();
|
||||
this.seenItems[id] = Components.utils.getWeakReference(element);
|
||||
this.seenItems[id] = Cu.getWeakReference(element);
|
||||
return id;
|
||||
},
|
||||
|
||||
@ -562,7 +369,7 @@ ElementManager.prototype = {
|
||||
on_success, on_error,
|
||||
command_id),
|
||||
100,
|
||||
Components.interfaces.nsITimer.TYPE_ONE_SHOT);
|
||||
Ci.nsITimer.TYPE_ONE_SHOT);
|
||||
}
|
||||
} else {
|
||||
if (isArrayLike) {
|
||||
@ -598,7 +405,7 @@ ElementManager.prototype = {
|
||||
*/
|
||||
findByXPath: function EM_findByXPath(root, value, node) {
|
||||
return root.evaluate(value, node, null,
|
||||
Components.interfaces.nsIDOMXPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
|
||||
Ci.nsIDOMXPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
|
||||
},
|
||||
|
||||
/**
|
||||
@ -616,7 +423,7 @@ ElementManager.prototype = {
|
||||
*/
|
||||
findByXPathAll: function EM_findByXPathAll(root, value, node) {
|
||||
let values = root.evaluate(value, node, null,
|
||||
Components.interfaces.nsIDOMXPathResult.ORDERED_NODE_ITERATOR_TYPE, null);
|
||||
Ci.nsIDOMXPathResult.ORDERED_NODE_ITERATOR_TYPE, null);
|
||||
let elements = [];
|
||||
let element = values.iterateNext();
|
||||
while (element) {
|
||||
|
317
testing/marionette/interactions.js
Normal file
317
testing/marionette/interactions.js
Normal file
@ -0,0 +1,317 @@
|
||||
/* 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/. */
|
||||
|
||||
/* global Components, Accessibility, ElementNotVisibleError,
|
||||
InvalidElementStateError, Interactions */
|
||||
|
||||
var {utils: Cu} = Components;
|
||||
|
||||
this.EXPORTED_SYMBOLS = ['Interactions'];
|
||||
|
||||
Cu.import('chrome://marionette/content/accessibility.js');
|
||||
Cu.import('chrome://marionette/content/error.js');
|
||||
|
||||
/**
|
||||
* XUL elements that support disabled attribtue.
|
||||
*/
|
||||
const DISABLED_ATTRIBUTE_SUPPORTED_XUL = new Set([
|
||||
'ARROWSCROLLBOX',
|
||||
'BUTTON',
|
||||
'CHECKBOX',
|
||||
'COLORPICKER',
|
||||
'COMMAND',
|
||||
'DATEPICKER',
|
||||
'DESCRIPTION',
|
||||
'KEY',
|
||||
'KEYSET',
|
||||
'LABEL',
|
||||
'LISTBOX',
|
||||
'LISTCELL',
|
||||
'LISTHEAD',
|
||||
'LISTHEADER',
|
||||
'LISTITEM',
|
||||
'MENU',
|
||||
'MENUITEM',
|
||||
'MENULIST',
|
||||
'MENUSEPARATOR',
|
||||
'PREFERENCE',
|
||||
'RADIO',
|
||||
'RADIOGROUP',
|
||||
'RICHLISTBOX',
|
||||
'RICHLISTITEM',
|
||||
'SCALE',
|
||||
'TAB',
|
||||
'TABS',
|
||||
'TEXTBOX',
|
||||
'TIMEPICKER',
|
||||
'TOOLBARBUTTON',
|
||||
'TREE'
|
||||
]);
|
||||
|
||||
/**
|
||||
* XUL elements that support checked property.
|
||||
*/
|
||||
const CHECKED_PROPERTY_SUPPORTED_XUL = new Set([
|
||||
'BUTTON',
|
||||
'CHECKBOX',
|
||||
'LISTITEM',
|
||||
'TOOLBARBUTTON'
|
||||
]);
|
||||
|
||||
/**
|
||||
* XUL elements that support selected property.
|
||||
*/
|
||||
const SELECTED_PROPERTY_SUPPORTED_XUL = new Set([
|
||||
'LISTITEM',
|
||||
'MENU',
|
||||
'MENUITEM',
|
||||
'MENUSEPARATOR',
|
||||
'RADIO',
|
||||
'RICHLISTITEM',
|
||||
'TAB'
|
||||
]);
|
||||
|
||||
/**
|
||||
* This function generates a pair of coordinates relative to the viewport given
|
||||
* a target element and coordinates relative to that element's top-left corner.
|
||||
* @param 'x', and 'y' are the relative to the target.
|
||||
* If they are not specified, then the center of the target is used.
|
||||
*/
|
||||
function coordinates(target, x, y) {
|
||||
let box = target.getBoundingClientRect();
|
||||
if (typeof x === 'undefined') {
|
||||
x = box.width / 2;
|
||||
}
|
||||
if (typeof y === 'undefined') {
|
||||
y = box.height / 2;
|
||||
}
|
||||
return {
|
||||
x: box.left + x,
|
||||
y: box.top + y
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* A collection of interactions available in marionette.
|
||||
* @type {Object}
|
||||
*/
|
||||
this.Interactions = function(utils, getCapabilies) {
|
||||
this.utils = utils;
|
||||
this.accessibility = new Accessibility(getCapabilies);
|
||||
};
|
||||
|
||||
Interactions.prototype = {
|
||||
/**
|
||||
* Send click event to element.
|
||||
*
|
||||
* @param nsIDOMWindow, ShadowRoot container
|
||||
* The window and an optional shadow root that contains the element
|
||||
*
|
||||
* @param ElementManager elementManager
|
||||
*
|
||||
* @param String id
|
||||
* The DOM reference ID
|
||||
*/
|
||||
clickElement(container, elementManager, id) {
|
||||
let el = elementManager.getKnownElement(id, container);
|
||||
let visible = this.checkVisible(container, el);
|
||||
if (!visible) {
|
||||
throw new ElementNotVisibleError('Element is not visible');
|
||||
}
|
||||
return this.accessibility.getAccessibleObject(el, true).then(acc => {
|
||||
this.accessibility.checkVisible(acc, el, visible);
|
||||
if (this.utils.isElementEnabled(el)) {
|
||||
this.accessibility.checkEnabled(acc, el, true, container);
|
||||
this.accessibility.checkActionable(acc, el);
|
||||
if (this.isXULElement(el)) {
|
||||
el.click();
|
||||
} else {
|
||||
let rects = el.getClientRects();
|
||||
this.utils.synthesizeMouseAtPoint(rects[0].left + rects[0].width/2,
|
||||
rects[0].top + rects[0].height/2,
|
||||
{}, el.ownerDocument.defaultView);
|
||||
}
|
||||
} else {
|
||||
throw new InvalidElementStateError('Element is not enabled');
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Send keys to element
|
||||
*
|
||||
* @param nsIDOMWindow, ShadowRoot container
|
||||
* The window and an optional shadow root that contains the element
|
||||
*
|
||||
* @param ElementManager elementManager
|
||||
*
|
||||
* @param String id
|
||||
* The DOM reference ID
|
||||
*
|
||||
* @param String?Array value
|
||||
* Value to send to an element
|
||||
*
|
||||
* @param Boolean ignoreVisibility
|
||||
* A flag to check element visibility
|
||||
*/
|
||||
sendKeysToElement(container, elementManager, id, value, ignoreVisibility) {
|
||||
let el = elementManager.getKnownElement(id, container);
|
||||
return this.accessibility.getAccessibleObject(el, true).then(acc => {
|
||||
this.accessibility.checkActionable(acc, el);
|
||||
this.utils.sendKeysToElement(
|
||||
container.frame, el, value, ignoreVisibility);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Determine the element displayedness of the given web element.
|
||||
*
|
||||
* @param nsIDOMWindow, ShadowRoot container
|
||||
* The window and an optional shadow root that contains the element
|
||||
*
|
||||
* @param ElementManager elementManager
|
||||
*
|
||||
* @param {WebElement} id
|
||||
* Reference to web element.
|
||||
*
|
||||
* Also performs additional accessibility checks if enabled by session
|
||||
* capability.
|
||||
*/
|
||||
isElementDisplayed(container, elementManager, id) {
|
||||
let el = elementManager.getKnownElement(id, container);
|
||||
let displayed = this.utils.isElementDisplayed(el);
|
||||
return this.accessibility.getAccessibleObject(el).then(acc => {
|
||||
this.accessibility.checkVisible(acc, el, displayed);
|
||||
return displayed;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if element is enabled.
|
||||
*
|
||||
* @param nsIDOMWindow, ShadowRoot container
|
||||
* The window and an optional shadow root that contains the element
|
||||
*
|
||||
* @param ElementManager elementManager
|
||||
*
|
||||
* @param {WebElement} id
|
||||
* Reference to web element.
|
||||
*
|
||||
* @return {boolean}
|
||||
* True if enabled, false otherwise.
|
||||
*/
|
||||
isElementEnabled(container, elementManager, id) {
|
||||
let el = elementManager.getKnownElement(id, container);
|
||||
let enabled = true;
|
||||
if (this.isXULElement(el)) {
|
||||
// Check if XUL element supports disabled attribute
|
||||
if (DISABLED_ATTRIBUTE_SUPPORTED_XUL.has(el.tagName.toUpperCase())) {
|
||||
let disabled = this.utils.getElementAttribute(el, 'disabled');
|
||||
if (disabled && disabled === 'true') {
|
||||
enabled = false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
enabled = this.utils.isElementEnabled(el);
|
||||
}
|
||||
return this.accessibility.getAccessibleObject(el).then(acc => {
|
||||
this.accessibility.checkEnabled(acc, el, enabled, container);
|
||||
return enabled;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Determines if the referenced element is selected or not.
|
||||
*
|
||||
* This operation only makes sense on input elements of the Checkbox-
|
||||
* and Radio Button states, or option elements.
|
||||
*
|
||||
* @param nsIDOMWindow, ShadowRoot container
|
||||
* The window and an optional shadow root that contains the element
|
||||
*
|
||||
* @param ElementManager elementManager
|
||||
*
|
||||
* @param {WebElement} id
|
||||
* Reference to web element.
|
||||
*/
|
||||
isElementSelected(container, elementManager, id) {
|
||||
let el = elementManager.getKnownElement(id, container);
|
||||
let selected = true;
|
||||
if (this.isXULElement(el)) {
|
||||
let tagName = el.tagName.toUpperCase();
|
||||
if (CHECKED_PROPERTY_SUPPORTED_XUL.has(tagName)) {
|
||||
selected = el.checked;
|
||||
}
|
||||
if (SELECTED_PROPERTY_SUPPORTED_XUL.has(tagName)) {
|
||||
selected = el.selected;
|
||||
}
|
||||
} else {
|
||||
selected = this.utils.isElementSelected(el);
|
||||
}
|
||||
return this.accessibility.getAccessibleObject(el).then(acc => {
|
||||
this.accessibility.checkSelected(acc, el, selected);
|
||||
return selected;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* This function throws the visibility of the element error if the element is
|
||||
* not displayed or the given coordinates are not within the viewport.
|
||||
*
|
||||
* @param 'x', and 'y' are the coordinates relative to the target.
|
||||
* If they are not specified, then the center of the target is used.
|
||||
*/
|
||||
checkVisible(container, el, x, y) {
|
||||
// Bug 1094246 - Webdriver's isShown doesn't work with content xul
|
||||
if (!this.isXULElement(el)) {
|
||||
//check if the element is visible
|
||||
let visible = this.utils.isElementDisplayed(el);
|
||||
if (!visible) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (el.tagName.toLowerCase() === 'body') {
|
||||
return true;
|
||||
}
|
||||
if (!this.elementInViewport(container, el, x, y)) {
|
||||
//check if scroll function exist. If so, call it.
|
||||
if (el.scrollIntoView) {
|
||||
el.scrollIntoView(false);
|
||||
if (!this.elementInViewport(container, el)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
isXULElement(el) {
|
||||
return this.utils.getElementAttribute(el, 'namespaceURI').indexOf(
|
||||
'there.is.only.xul') >= 0;
|
||||
},
|
||||
|
||||
/**
|
||||
* This function returns true if the given coordinates are in the viewport.
|
||||
* @param 'x', and 'y' are the coordinates relative to the target.
|
||||
* If they are not specified, then the center of the target is used.
|
||||
*/
|
||||
elementInViewport(container, el, x, y) {
|
||||
let c = coordinates(el, x, y);
|
||||
let win = container.frame;
|
||||
let viewPort = {
|
||||
top: win.pageYOffset,
|
||||
left: win.pageXOffset,
|
||||
bottom: win.pageYOffset + win.innerHeight,
|
||||
right: win.pageXOffset + win.innerWidth
|
||||
};
|
||||
return (viewPort.left <= c.x + win.pageXOffset &&
|
||||
c.x + win.pageXOffset <= viewPort.right &&
|
||||
viewPort.top <= c.y + win.pageYOffset &&
|
||||
c.y + win.pageYOffset <= viewPort.bottom);
|
||||
}
|
||||
};
|
@ -7,6 +7,8 @@ marionette.jar:
|
||||
content/server.js (server.js)
|
||||
content/driver.js (driver.js)
|
||||
content/actions.js (actions.js)
|
||||
content/interactions.js (interactions.js)
|
||||
content/accessibility.js (accessibility.js)
|
||||
content/listener.js (listener.js)
|
||||
content/elements.js (elements.js)
|
||||
content/sendkeys.js (sendkeys.js)
|
||||
|
@ -18,6 +18,7 @@ Cu.import("chrome://marionette/content/cookies.js");
|
||||
Cu.import("chrome://marionette/content/elements.js");
|
||||
Cu.import("chrome://marionette/content/error.js");
|
||||
Cu.import("chrome://marionette/content/proxy.js");
|
||||
Cu.import("chrome://marionette/content/interactions.js");
|
||||
|
||||
Cu.import("resource://gre/modules/FileUtils.jsm");
|
||||
Cu.import("resource://gre/modules/NetUtil.jsm");
|
||||
@ -43,7 +44,11 @@ var curContainer = { frame: content, shadowRoot: null };
|
||||
var isRemoteBrowser = () => curContainer.frame.contentWindow !== null;
|
||||
var previousContainer = null;
|
||||
var elementManager = new ElementManager([]);
|
||||
var accessibility = new Accessibility();
|
||||
|
||||
// Holds session capabilities.
|
||||
var capabilities = {};
|
||||
var interactions = new Interactions(utils, () => capabilities);
|
||||
|
||||
var actions = new ActionChain(utils, checkForInterrupted);
|
||||
var importedScripts = null;
|
||||
|
||||
@ -108,9 +113,8 @@ function registerSelf() {
|
||||
|
||||
if (register[0]) {
|
||||
let {id, remotenessChange} = register[0][0];
|
||||
let {B2G, raisesAccessibilityExceptions} = register[0][2];
|
||||
isB2G = B2G;
|
||||
accessibility.strict = raisesAccessibilityExceptions;
|
||||
capabilities = register[0][2];
|
||||
isB2G = capabilities.B2G;
|
||||
listenerId = id;
|
||||
if (typeof id != "undefined") {
|
||||
// check if we're the main process
|
||||
@ -299,8 +303,8 @@ function waitForReady() {
|
||||
* current environment, and resets all values
|
||||
*/
|
||||
function newSession(msg) {
|
||||
isB2G = msg.json.B2G;
|
||||
accessibility.strict = msg.json.raisesAccessibilityExceptions;
|
||||
capabilities = msg.json;
|
||||
isB2G = capabilities.B2G;
|
||||
resetValues();
|
||||
if (isB2G) {
|
||||
readyStateTimer.initWithCallback(waitForReady, 100, Ci.nsITimer.TYPE_ONE_SHOT);
|
||||
@ -938,9 +942,9 @@ function singleTap(id, corx, cory) {
|
||||
if (!visible) {
|
||||
throw new ElementNotVisibleError("Element is not currently visible and may not be manipulated");
|
||||
}
|
||||
return accessibility.getAccessibleObject(el, true).then(acc => {
|
||||
checkVisibleAccessibility(acc, el, visible);
|
||||
checkActionableAccessibility(acc, el);
|
||||
return interactions.accessibility.getAccessibleObject(el, true).then(acc => {
|
||||
interactions.accessibility.checkVisible(acc, el, visible);
|
||||
interactions.accessibility.checkActionable(acc, el);
|
||||
if (!curContainer.frame.document.createTouch) {
|
||||
actions.mouseEventsOnly = true;
|
||||
}
|
||||
@ -955,108 +959,6 @@ function singleTap(id, corx, cory) {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the element's unavailable accessibility state matches the enabled
|
||||
* state
|
||||
* @param nsIAccessible object
|
||||
* @param WebElement corresponding to nsIAccessible object
|
||||
* @param Boolean enabled element's enabled state
|
||||
*/
|
||||
function checkEnabledAccessibility(accesible, element, enabled) {
|
||||
if (!accesible) {
|
||||
return;
|
||||
}
|
||||
let disabledAccessibility = accessibility.matchState(
|
||||
accesible, 'STATE_UNAVAILABLE');
|
||||
let explorable = curContainer.frame.document.defaultView.getComputedStyle(
|
||||
element, null).getPropertyValue('pointer-events') !== 'none';
|
||||
let message;
|
||||
|
||||
if (!explorable && !disabledAccessibility) {
|
||||
message = 'Element is enabled but is not explorable via the ' +
|
||||
'accessibility API';
|
||||
} else if (enabled && disabledAccessibility) {
|
||||
message = 'Element is enabled but disabled via the accessibility API';
|
||||
} else if (!enabled && !disabledAccessibility) {
|
||||
message = 'Element is disabled but enabled via the accessibility API';
|
||||
}
|
||||
accessibility.handleErrorMessage(message, element);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the element's visible state corresponds to its accessibility API
|
||||
* visibility
|
||||
* @param nsIAccessible object
|
||||
* @param WebElement corresponding to nsIAccessible object
|
||||
* @param Boolean visible element's visibility state
|
||||
*/
|
||||
function checkVisibleAccessibility(accesible, element, visible) {
|
||||
if (!accesible) {
|
||||
return;
|
||||
}
|
||||
let hiddenAccessibility = accessibility.isHidden(accesible);
|
||||
let message;
|
||||
if (visible && hiddenAccessibility) {
|
||||
message = 'Element is not currently visible via the accessibility API ' +
|
||||
'and may not be manipulated by it';
|
||||
} else if (!visible && !hiddenAccessibility) {
|
||||
message = 'Element is currently only visible via the accessibility API ' +
|
||||
'and can be manipulated by it';
|
||||
}
|
||||
accessibility.handleErrorMessage(message, element);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if it is possible to activate an element with the accessibility API
|
||||
* @param nsIAccessible object
|
||||
* @param WebElement corresponding to nsIAccessible object
|
||||
*/
|
||||
function checkActionableAccessibility(accesible, element) {
|
||||
if (!accesible) {
|
||||
return;
|
||||
}
|
||||
let message;
|
||||
if (!accessibility.hasActionCount(accesible)) {
|
||||
message = 'Element does not support any accessible actions';
|
||||
} else if (!accessibility.isActionableRole(accesible)) {
|
||||
message = 'Element does not have a correct accessibility role ' +
|
||||
'and may not be manipulated via the accessibility API';
|
||||
} else if (!accessibility.hasValidName(accesible)) {
|
||||
message = 'Element is missing an accesible name';
|
||||
} else if (!accessibility.matchState(accesible, 'STATE_FOCUSABLE')) {
|
||||
message = 'Element is not focusable via the accessibility API';
|
||||
}
|
||||
accessibility.handleErrorMessage(message, element);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if element's selected state corresponds to its accessibility API
|
||||
* selected state.
|
||||
* @param nsIAccessible object
|
||||
* @param WebElement corresponding to nsIAccessible object
|
||||
* @param Boolean selected element's selected state
|
||||
*/
|
||||
function checkSelectedAccessibility(accessible, element, selected) {
|
||||
if (!accessible) {
|
||||
return;
|
||||
}
|
||||
if (!accessibility.matchState(accessible, 'STATE_SELECTABLE')) {
|
||||
// Element is not selectable via the accessibility API
|
||||
return;
|
||||
}
|
||||
|
||||
let selectedAccessibility = accessibility.matchState(
|
||||
accessible, 'STATE_SELECTED');
|
||||
let message;
|
||||
if (selected && !selectedAccessibility) {
|
||||
message = 'Element is selected but not selected via the accessibility API';
|
||||
} else if (!selected && selectedAccessibility) {
|
||||
message = 'Element is not selected but selected via the accessibility API';
|
||||
}
|
||||
accessibility.handleErrorMessage(message, element);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Function to create a touch based on the element
|
||||
* corx and cory are relative to the viewport, id is the touchId
|
||||
@ -1460,24 +1362,7 @@ function getActiveElement() {
|
||||
* Reference to the web element to click.
|
||||
*/
|
||||
function clickElement(id) {
|
||||
let el = elementManager.getKnownElement(id, curContainer);
|
||||
let visible = checkVisible(el);
|
||||
if (!visible) {
|
||||
throw new ElementNotVisibleError("Element is not visible");
|
||||
}
|
||||
return accessibility.getAccessibleObject(el, true).then(acc => {
|
||||
checkVisibleAccessibility(acc, el, visible);
|
||||
if (utils.isElementEnabled(el)) {
|
||||
checkEnabledAccessibility(acc, el, true);
|
||||
checkActionableAccessibility(acc, el);
|
||||
let rects = el.getClientRects();
|
||||
utils.synthesizeMouseAtPoint(rects[0].left + rects[0].width/2,
|
||||
rects[0].top + rects[0].height/2,
|
||||
{}, el.ownerDocument.defaultView);
|
||||
} else {
|
||||
throw new InvalidElementStateError("Element is not Enabled");
|
||||
}
|
||||
});
|
||||
return interactions.clickElement(curContainer, elementManager, id);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1531,12 +1416,7 @@ function getElementTagName(id) {
|
||||
* capability.
|
||||
*/
|
||||
function isElementDisplayed(id) {
|
||||
let el = elementManager.getKnownElement(id, curContainer);
|
||||
let displayed = utils.isElementDisplayed(el);
|
||||
return accessibility.getAccessibleObject(el).then(acc => {
|
||||
checkVisibleAccessibility(acc, el, displayed);
|
||||
return displayed;
|
||||
});
|
||||
return interactions.isElementDisplayed(curContainer, elementManager, id);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1587,12 +1467,7 @@ function getElementRect(id) {
|
||||
* True if enabled, false otherwise.
|
||||
*/
|
||||
function isElementEnabled(id) {
|
||||
let el = elementManager.getKnownElement(id, curContainer);
|
||||
let enabled = utils.isElementEnabled(el);
|
||||
return accessibility.getAccessibleObject(el).then(acc => {
|
||||
checkEnabledAccessibility(acc, el, enabled);
|
||||
return enabled;
|
||||
});
|
||||
return interactions.isElementEnabled(curContainer, elementManager, id);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1602,12 +1477,7 @@ function isElementEnabled(id) {
|
||||
* and Radio Button states, or option elements.
|
||||
*/
|
||||
function isElementSelected(id) {
|
||||
let el = elementManager.getKnownElement(id, curContainer);
|
||||
let selected = utils.isElementSelected(el);
|
||||
return accessibility.getAccessibleObject(el).then(acc => {
|
||||
checkSelectedAccessibility(acc, el, selected);
|
||||
return selected;
|
||||
});
|
||||
return interactions.isElementSelected(curContainer, elementManager, id);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1616,28 +1486,22 @@ function isElementSelected(id) {
|
||||
function sendKeysToElement(msg) {
|
||||
let command_id = msg.json.command_id;
|
||||
let val = msg.json.value;
|
||||
let el;
|
||||
let id = msg.json.id;
|
||||
let el = elementManager.getKnownElement(id, curContainer);
|
||||
|
||||
return Promise.resolve(elementManager.getKnownElement(msg.json.id, curContainer))
|
||||
.then(knownEl => {
|
||||
el = knownEl;
|
||||
// Element should be actionable from the accessibility standpoint to be able
|
||||
// to send keys to it.
|
||||
return accessibility.getAccessibleObject(el, true)
|
||||
}).then(acc => {
|
||||
checkActionableAccessibility(acc, el);
|
||||
if (el.type == "file") {
|
||||
let p = val.join("");
|
||||
if (el.type == "file") {
|
||||
let p = val.join("");
|
||||
fileInputElement = el;
|
||||
// In e10s, we can only construct File objects in the parent process,
|
||||
// so pass the filename to driver.js, which in turn passes them back
|
||||
// to this frame script in receiveFiles.
|
||||
sendSyncMessage("Marionette:getFiles",
|
||||
{value: p, command_id: command_id});
|
||||
} else {
|
||||
utils.sendKeysToElement(curContainer.frame, el, val, sendOk, sendError, command_id);
|
||||
}
|
||||
}).catch(e => sendError(e, command_id));
|
||||
} else {
|
||||
interactions.sendKeysToElement(curContainer, elementManager, id, val)
|
||||
.then(() => sendOk(command_id))
|
||||
.catch(e => sendError(e, command_id));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -144,7 +144,7 @@ function focusElement(el) {
|
||||
el.focus();
|
||||
}
|
||||
|
||||
function sendKeysToElement(document, element, keysToSend, successCallback, errorCallback, command_id, ignoreVisibility) {
|
||||
function sendKeysToElement(document, element, keysToSend, ignoreVisibility) {
|
||||
if (ignoreVisibility || checkVisible(element)) {
|
||||
focusElement(element);
|
||||
|
||||
@ -159,9 +159,7 @@ function sendKeysToElement(document, element, keysToSend, successCallback, error
|
||||
var c = value.charAt(i);
|
||||
sendSingleKey(c, modifiers, document);
|
||||
}
|
||||
|
||||
successCallback(command_id);
|
||||
} else {
|
||||
errorCallback(new ElementNotVisibleError("Element is not visible"), command_id);
|
||||
throw new ElementNotVisibleError("Element is not visible");
|
||||
}
|
||||
};
|
||||
|
@ -26579,6 +26579,10 @@
|
||||
"path": "vibration/silent-ignore.html",
|
||||
"url": "/vibration/silent-ignore.html"
|
||||
},
|
||||
{
|
||||
"path": "web-animations/animation/constructor.html",
|
||||
"url": "/web-animations/animation/constructor.html"
|
||||
},
|
||||
{
|
||||
"path": "web-animations/animation-node/animation-node-after.html",
|
||||
"url": "/web-animations/animation-node/animation-node-after.html"
|
||||
|
@ -0,0 +1,13 @@
|
||||
[constructor.html]
|
||||
type: testharness
|
||||
[Animation can be constructed with null effect and null timeline]
|
||||
expected: FAIL
|
||||
bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1049975
|
||||
|
||||
[Animation can be constructed with null effect and non-null timeline]
|
||||
expected: FAIL
|
||||
bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1049975
|
||||
|
||||
[Animation can be constructed with non-null effect and null timeline]
|
||||
expected: FAIL
|
||||
bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1096776
|
@ -0,0 +1,60 @@
|
||||
<!DOCTYPE html>
|
||||
<meta charset=utf-8>
|
||||
<title>Animation constructor tests</title>
|
||||
<link rel="help" href="http://w3c.github.io/web-animations/#dom-animation-animation">
|
||||
<link rel="author" title="Hiroyuki Ikezoe" href="mailto:hiikezoe@mozilla-japan.org">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../testcommon.js"></script>
|
||||
<link rel="stylesheet" href="/resources/testharness.css">
|
||||
<body>
|
||||
<div id="log"></div>
|
||||
<div id="target"></div>
|
||||
<script>
|
||||
"use strict";
|
||||
|
||||
var gTarget = document.getElementById("target");
|
||||
var gEffect = new KeyframeEffectReadOnly(gTarget, { opacity: [0, 1] });
|
||||
|
||||
var gTestArguments = [
|
||||
{
|
||||
effect: null,
|
||||
timeline: null,
|
||||
description: "with null effect and null timeline"
|
||||
},
|
||||
{
|
||||
effect: null,
|
||||
timeline: document.timeline,
|
||||
description: "with null effect and non-null timeline"
|
||||
},
|
||||
{
|
||||
effect: gEffect,
|
||||
timeline: null,
|
||||
description: "with non-null effect and null timeline"
|
||||
},
|
||||
{
|
||||
effect: gEffect,
|
||||
timeline: document.timeline,
|
||||
description: "with non-null effect and non-null timeline"
|
||||
},
|
||||
];
|
||||
|
||||
gTestArguments.forEach(function(args) {
|
||||
test(function(t) {
|
||||
var animation = new Animation(args.effect, args.timeline);
|
||||
|
||||
assert_not_equals(animation, null,
|
||||
"An animation sohuld be created");
|
||||
assert_equals(animation.effect, args.effect,
|
||||
"Animation returns the same effect passed to " +
|
||||
"the Constructor");
|
||||
assert_equals(animation.timeline, args.timeline,
|
||||
"Animation returns the same timeline passed to " +
|
||||
"the Constructor");
|
||||
assert_equals(animation.playState, "idle",
|
||||
"Animation.playState should be initially 'idle'");
|
||||
}, "Animation can be constructed " + args.description);
|
||||
});
|
||||
|
||||
</script>
|
||||
</body>
|
@ -6032,11 +6032,22 @@
|
||||
"description": "The time (in milliseconds) that it took a 'reconfigure thread' request to go round trip."
|
||||
},
|
||||
"MEDIA_RUST_MP4PARSE_SUCCESS": {
|
||||
"alert_emails": ["giles@mozilla.com", "kinetik@flim.org"],
|
||||
"expires_in_version": "50",
|
||||
"kind": "boolean",
|
||||
"bug_numbers": [1220885],
|
||||
"description": "(Bug 1220885) Whether the rust mp4 demuxer successfully parsed a stream segment.",
|
||||
"cpp_guard": "MOZ_RUST_MP4PARSE"
|
||||
},
|
||||
"MEDIA_RUST_MP4PARSE_ERROR_CODE": {
|
||||
"alert_emails": ["giles@mozilla.com", "kinetik@flim.org"],
|
||||
"expires_in_version": "50",
|
||||
"kind": "enumerated",
|
||||
"n_values": 32,
|
||||
"bug_numbers": [1238420],
|
||||
"description": "The error code reported when an MP4 parse attempt has failed.",
|
||||
"cpp_guard": "MOZ_RUST_MP4PARSE"
|
||||
},
|
||||
"MEDIA_RUST_MP4PARSE_TRACK_MATCH_AUDIO": {
|
||||
"alert_emails": ["giles@mozilla.com", "kinetik@flim.org"],
|
||||
"expires_in_version": "50",
|
||||
|
@ -64,7 +64,7 @@ PluginWidgetProxy::Create(nsIWidget* aParent,
|
||||
|
||||
BaseCreate(aParent, aInitData);
|
||||
|
||||
mBounds = aRect.ToUnknownRect();
|
||||
mBounds = aRect;
|
||||
mEnabled = true;
|
||||
mVisible = true;
|
||||
|
||||
|
@ -30,11 +30,11 @@ using namespace mozilla::layers;
|
||||
using namespace mozilla::widget;
|
||||
|
||||
static void
|
||||
InvalidateRegion(nsIWidget* aWidget, const nsIntRegion& aRegion)
|
||||
InvalidateRegion(nsIWidget* aWidget, const LayoutDeviceIntRegion& aRegion)
|
||||
{
|
||||
nsIntRegionRectIterator it(aRegion);
|
||||
while(const nsIntRect* r = it.Next()) {
|
||||
aWidget->Invalidate(LayoutDeviceIntRect::FromUnknownRect(*r));
|
||||
LayoutDeviceIntRegion::RectIterator it(aRegion);
|
||||
while(const LayoutDeviceIntRect* r = it.Next()) {
|
||||
aWidget->Invalidate(*r);
|
||||
}
|
||||
}
|
||||
|
||||
@ -107,7 +107,7 @@ PuppetWidget::Create(nsIWidget* aParent,
|
||||
|
||||
BaseCreate(nullptr, aInitData);
|
||||
|
||||
mBounds = aRect.ToUnknownRect();
|
||||
mBounds = aRect;
|
||||
mEnabled = true;
|
||||
mVisible = true;
|
||||
|
||||
@ -197,7 +197,7 @@ PuppetWidget::Show(bool aState)
|
||||
|
||||
if (!wasVisible && mVisible) {
|
||||
Resize(mBounds.width, mBounds.height, false);
|
||||
Invalidate(LayoutDeviceIntRect::FromUnknownRect(mBounds));
|
||||
Invalidate(mBounds);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
@ -208,8 +208,9 @@ PuppetWidget::Resize(double aWidth,
|
||||
double aHeight,
|
||||
bool aRepaint)
|
||||
{
|
||||
nsIntRect oldBounds = mBounds;
|
||||
mBounds.SizeTo(nsIntSize(NSToIntRound(aWidth), NSToIntRound(aHeight)));
|
||||
LayoutDeviceIntRect oldBounds = mBounds;
|
||||
mBounds.SizeTo(LayoutDeviceIntSize(NSToIntRound(aWidth),
|
||||
NSToIntRound(aHeight)));
|
||||
|
||||
if (mChild) {
|
||||
return mChild->Resize(aWidth, aHeight, aRepaint);
|
||||
@ -218,8 +219,8 @@ PuppetWidget::Resize(double aWidth,
|
||||
// XXX: roc says that |aRepaint| dictates whether or not to
|
||||
// invalidate the expanded area
|
||||
if (oldBounds.Size() < mBounds.Size() && aRepaint) {
|
||||
nsIntRegion dirty(mBounds);
|
||||
dirty.Sub(dirty, oldBounds);
|
||||
LayoutDeviceIntRegion dirty(mBounds);
|
||||
dirty.Sub(dirty, oldBounds);
|
||||
InvalidateRegion(this, dirty);
|
||||
}
|
||||
|
||||
@ -1240,7 +1241,7 @@ PuppetWidget::GetWindowPosition()
|
||||
NS_METHOD
|
||||
PuppetWidget::GetScreenBounds(LayoutDeviceIntRect& aRect) {
|
||||
aRect.MoveTo(WidgetToScreenOffset());
|
||||
aRect.SizeTo(LayoutDeviceIntSize::FromUnknownSize(mBounds.Size()));
|
||||
aRect.SizeTo(mBounds.Size());
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -1075,7 +1075,7 @@ nsWindow::Create(nsIWidget* aParent,
|
||||
}
|
||||
}
|
||||
|
||||
mBounds = aRect.ToUnknownRect();
|
||||
mBounds = aRect;
|
||||
|
||||
// for toplevel windows, bounds are fixed to full screen size
|
||||
if (!parent) {
|
||||
|
@ -535,11 +535,6 @@ public:
|
||||
CGFloat DevPixelsToCocoaPoints(int32_t aPixels) const {
|
||||
return nsCocoaUtils::DevPixelsToCocoaPoints(aPixels, BackingScaleFactor());
|
||||
}
|
||||
// XXX: all calls to this function should eventually be replaced with calls
|
||||
// to DevPixelsToCocoaPoints().
|
||||
NSRect UntypedDevPixelsToCocoaPoints(const nsIntRect& aRect) const {
|
||||
return nsCocoaUtils::UntypedDevPixelsToCocoaPoints(aRect, BackingScaleFactor());
|
||||
}
|
||||
NSRect DevPixelsToCocoaPoints(const LayoutDeviceIntRect& aRect) const {
|
||||
return nsCocoaUtils::DevPixelsToCocoaPoints(aRect, BackingScaleFactor());
|
||||
}
|
||||
|
@ -496,7 +496,7 @@ nsresult nsChildView::Create(nsIWidget* aParent,
|
||||
gChildViewMethodsSwizzled = true;
|
||||
}
|
||||
|
||||
mBounds = aRect.ToUnknownRect();
|
||||
mBounds = aRect;
|
||||
|
||||
// Ensure that the toolkit is created.
|
||||
nsToolkit::GetToolkit();
|
||||
@ -522,8 +522,7 @@ nsresult nsChildView::Create(nsIWidget* aParent,
|
||||
// create our parallel NSView and hook it up to our parent. Recall
|
||||
// that NS_NATIVE_WIDGET is the NSView.
|
||||
CGFloat scaleFactor = nsCocoaUtils::GetBackingScaleFactor(mParentView);
|
||||
NSRect r = nsCocoaUtils::DevPixelsToCocoaPoints(
|
||||
LayoutDeviceIntRect::FromUnknownRect(mBounds), scaleFactor);
|
||||
NSRect r = nsCocoaUtils::DevPixelsToCocoaPoints(mBounds, scaleFactor);
|
||||
mView = [(NSView<mozView>*)CreateCocoaView(r) retain];
|
||||
if (!mView) {
|
||||
return NS_ERROR_FAILURE;
|
||||
@ -929,9 +928,7 @@ NS_IMETHODIMP nsChildView::SetCursor(imgIContainer* aCursor,
|
||||
// Get this component dimension
|
||||
NS_IMETHODIMP nsChildView::GetBounds(LayoutDeviceIntRect& aRect)
|
||||
{
|
||||
aRect = !mView
|
||||
? LayoutDeviceIntRect::FromUnknownRect(mBounds)
|
||||
: CocoaPointsToDevPixels([mView frame]);
|
||||
aRect = !mView ? mBounds : CocoaPointsToDevPixels([mView frame]);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -1023,7 +1020,7 @@ NS_IMETHODIMP nsChildView::Move(double aX, double aY)
|
||||
mBounds.y = y;
|
||||
|
||||
ManipulateViewWithoutNeedingDisplay(mView, ^{
|
||||
[mView setFrame:UntypedDevPixelsToCocoaPoints(mBounds)];
|
||||
[mView setFrame:DevPixelsToCocoaPoints(mBounds)];
|
||||
});
|
||||
|
||||
NotifyRollupGeometryChange();
|
||||
@ -1048,7 +1045,7 @@ NS_IMETHODIMP nsChildView::Resize(double aWidth, double aHeight, bool aRepaint)
|
||||
mBounds.height = height;
|
||||
|
||||
ManipulateViewWithoutNeedingDisplay(mView, ^{
|
||||
[mView setFrame:UntypedDevPixelsToCocoaPoints(mBounds)];
|
||||
[mView setFrame:DevPixelsToCocoaPoints(mBounds)];
|
||||
});
|
||||
|
||||
if (mVisible && aRepaint)
|
||||
@ -1087,7 +1084,7 @@ NS_IMETHODIMP nsChildView::Resize(double aX, double aY,
|
||||
}
|
||||
|
||||
ManipulateViewWithoutNeedingDisplay(mView, ^{
|
||||
[mView setFrame:UntypedDevPixelsToCocoaPoints(mBounds)];
|
||||
[mView setFrame:DevPixelsToCocoaPoints(mBounds)];
|
||||
});
|
||||
|
||||
if (mVisible && aRepaint)
|
||||
@ -2650,8 +2647,7 @@ nsChildView::StartRemoteDrawingInRegion(LayoutDeviceIntRegion& aInvalidRegion)
|
||||
}
|
||||
|
||||
LayoutDeviceIntRegion dirtyRegion(aInvalidRegion);
|
||||
LayoutDeviceIntSize renderSize =
|
||||
LayoutDeviceIntSize::FromUnknownSize(mBounds.Size());
|
||||
LayoutDeviceIntSize renderSize = mBounds.Size();
|
||||
|
||||
if (!mBasicCompositorImage) {
|
||||
mBasicCompositorImage = new RectTextureImage(mGLPresenter->gl());
|
||||
@ -2662,7 +2658,7 @@ nsChildView::StartRemoteDrawingInRegion(LayoutDeviceIntRegion& aInvalidRegion)
|
||||
|
||||
if (!drawTarget) {
|
||||
// Composite unchanged textures.
|
||||
DoRemoteComposition(LayoutDeviceIntRect::FromUnknownRect(mBounds));
|
||||
DoRemoteComposition(mBounds);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@ -2675,7 +2671,7 @@ void
|
||||
nsChildView::EndRemoteDrawing()
|
||||
{
|
||||
mBasicCompositorImage->EndUpdate(true);
|
||||
DoRemoteComposition(LayoutDeviceIntRect::FromUnknownRect(mBounds));
|
||||
DoRemoteComposition(mBounds);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -184,17 +184,6 @@ public:
|
||||
(CGFloat)aPt.y / aBackingScale);
|
||||
}
|
||||
|
||||
// XXX: all calls to this function should eventually be replaced with calls
|
||||
// to DevPixelsToCocoaPoints().
|
||||
static NSRect
|
||||
UntypedDevPixelsToCocoaPoints(const nsIntRect& aRect, CGFloat aBackingScale)
|
||||
{
|
||||
return NSMakeRect((CGFloat)aRect.x / aBackingScale,
|
||||
(CGFloat)aRect.y / aBackingScale,
|
||||
(CGFloat)aRect.width / aBackingScale,
|
||||
(CGFloat)aRect.height / aBackingScale);
|
||||
}
|
||||
|
||||
static NSRect
|
||||
DevPixelsToCocoaPoints(const LayoutDeviceIntRect& aRect,
|
||||
CGFloat aBackingScale)
|
||||
|
@ -1621,8 +1621,8 @@ nsCocoaWindow::UpdateBounds()
|
||||
if (mWindow) {
|
||||
frame = [mWindow frame];
|
||||
}
|
||||
mBounds = nsCocoaUtils::CocoaRectToGeckoRectDevPix(
|
||||
frame, BackingScaleFactor()).ToUnknownRect();
|
||||
mBounds =
|
||||
nsCocoaUtils::CocoaRectToGeckoRectDevPix(frame, BackingScaleFactor());
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsCocoaWindow::GetScreenBounds(LayoutDeviceIntRect &aRect)
|
||||
@ -1631,10 +1631,10 @@ NS_IMETHODIMP nsCocoaWindow::GetScreenBounds(LayoutDeviceIntRect &aRect)
|
||||
|
||||
#ifdef DEBUG
|
||||
LayoutDeviceIntRect r = nsCocoaUtils::CocoaRectToGeckoRectDevPix([mWindow frame], BackingScaleFactor());
|
||||
NS_ASSERTION(mWindow && mBounds == r.ToUnknownRect(), "mBounds out of sync!");
|
||||
NS_ASSERTION(mWindow && mBounds == r, "mBounds out of sync!");
|
||||
#endif
|
||||
|
||||
aRect = LayoutDeviceIntRect::FromUnknownRect(mBounds);
|
||||
aRect = mBounds;
|
||||
return NS_OK;
|
||||
|
||||
NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
|
||||
@ -2025,8 +2025,7 @@ LayoutDeviceIntPoint nsCocoaWindow::GetClientOffset()
|
||||
LayoutDeviceIntRect clientRect;
|
||||
GetClientBounds(clientRect);
|
||||
|
||||
return clientRect.TopLeft() -
|
||||
LayoutDeviceIntPoint::FromUnknownPoint(mBounds.TopLeft());
|
||||
return clientRect.TopLeft() - mBounds.TopLeft();
|
||||
|
||||
NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(LayoutDeviceIntPoint(0, 0));
|
||||
}
|
||||
|
@ -344,13 +344,13 @@ nsWindow::Create(nsIWidget* aParent,
|
||||
|
||||
mScreen = static_cast<nsScreenGonk*>(screen.get());
|
||||
|
||||
mBounds = aRect.ToUnknownRect();
|
||||
mBounds = aRect;
|
||||
|
||||
mParent = (nsWindow *)aParent;
|
||||
mVisible = false;
|
||||
|
||||
if (!aParent) {
|
||||
mBounds = mScreen->GetRect().ToUnknownRect();
|
||||
mBounds = mScreen->GetRect();
|
||||
}
|
||||
|
||||
mComposer2D = HwcComposer2D::GetInstance();
|
||||
@ -448,14 +448,14 @@ nsWindow::Resize(double aX,
|
||||
double aHeight,
|
||||
bool aRepaint)
|
||||
{
|
||||
mBounds = nsIntRect(NSToIntRound(aX), NSToIntRound(aY),
|
||||
NSToIntRound(aWidth), NSToIntRound(aHeight));
|
||||
mBounds = LayoutDeviceIntRect(NSToIntRound(aX), NSToIntRound(aY),
|
||||
NSToIntRound(aWidth), NSToIntRound(aHeight));
|
||||
if (mWidgetListener) {
|
||||
mWidgetListener->WindowResized(this, mBounds.width, mBounds.height);
|
||||
}
|
||||
|
||||
if (aRepaint) {
|
||||
Invalidate(LayoutDeviceIntRect::FromUnknownRect(mBounds));
|
||||
Invalidate(mBounds);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
@ -726,7 +726,7 @@ nsWindow::BringToTop()
|
||||
mWidgetListener->WindowActivated();
|
||||
}
|
||||
|
||||
Invalidate(LayoutDeviceIntRect::FromUnknownRect(mBounds));
|
||||
Invalidate(mBounds);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -1497,7 +1497,7 @@ nsWindow::GetScreenBounds(LayoutDeviceIntRect& aRect)
|
||||
// bounds (bug 581863). gdk_window_get_frame_extents would give the
|
||||
// frame bounds, but mBounds.Size() is returned here for consistency
|
||||
// with Resize.
|
||||
aRect.SizeTo(LayoutDeviceIntSize::FromUnknownSize(mBounds.Size()));
|
||||
aRect.SizeTo(mBounds.Size());
|
||||
LOG(("GetScreenBounds %d,%d | %dx%d\n",
|
||||
aRect.x, aRect.y, aRect.width, aRect.height));
|
||||
return NS_OK;
|
||||
@ -2419,7 +2419,7 @@ nsWindow::OnConfigureEvent(GtkWidget *aWidget, GdkEventConfigure *aEvent)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
mBounds.MoveTo(screenBounds.TopLeft().ToUnknownPoint());
|
||||
mBounds.MoveTo(screenBounds.TopLeft());
|
||||
|
||||
// XXX mozilla will invalidate the entire window after this move
|
||||
// complete. wtf?
|
||||
@ -2450,7 +2450,7 @@ nsWindow::OnSizeAllocate(GtkAllocation *aAllocation)
|
||||
(void *)this, aAllocation->x, aAllocation->y,
|
||||
aAllocation->width, aAllocation->height));
|
||||
|
||||
nsIntSize size = GdkRectToDevicePixels(*aAllocation).Size();
|
||||
LayoutDeviceIntSize size = GdkRectToDevicePixels(*aAllocation).Size();
|
||||
|
||||
if (mBounds.Size() == size)
|
||||
return;
|
||||
@ -3542,7 +3542,7 @@ nsWindow::Create(nsIWidget* aParent,
|
||||
CommonCreate(aParent, listenForResizes);
|
||||
|
||||
// save our bounds
|
||||
mBounds = aRect.ToUnknownRect();
|
||||
mBounds = aRect;
|
||||
ConstrainSize(&mBounds.width, &mBounds.height);
|
||||
|
||||
// figure out our parent window
|
||||
@ -4035,7 +4035,7 @@ nsWindow::NativeResize()
|
||||
}
|
||||
|
||||
GdkRectangle size = DevicePixelsToGdkSizeRoundUp(mBounds.Size());
|
||||
|
||||
|
||||
LOG(("nsWindow::NativeResize [%p] %d %d\n", (void *)this,
|
||||
size.width, size.height));
|
||||
|
||||
@ -4301,14 +4301,12 @@ nsWindow::ConfigureChildren(const nsTArray<Configuration>& aConfigurations)
|
||||
nsWindow* w = static_cast<nsWindow*>(configuration.mChild.get());
|
||||
NS_ASSERTION(w->GetParent() == this,
|
||||
"Configured widget is not a child");
|
||||
LayoutDeviceIntRect wBounds =
|
||||
LayoutDeviceIntRect::FromUnknownRect(w->mBounds);
|
||||
w->SetWindowClipRegion(configuration.mClipRegion, true);
|
||||
if (wBounds.Size() != configuration.mBounds.Size()) {
|
||||
if (w->mBounds.Size() != configuration.mBounds.Size()) {
|
||||
w->Resize(configuration.mBounds.x, configuration.mBounds.y,
|
||||
configuration.mBounds.width, configuration.mBounds.height,
|
||||
true);
|
||||
} else if (wBounds.TopLeft() != configuration.mBounds.TopLeft()) {
|
||||
} else if (w->mBounds.TopLeft() != configuration.mBounds.TopLeft()) {
|
||||
w->Move(configuration.mBounds.x, configuration.mBounds.y);
|
||||
}
|
||||
w->SetWindowClipRegion(configuration.mClipRegion, false);
|
||||
@ -6702,7 +6700,7 @@ nsWindow::DevicePixelsToGdkCoordRoundDown(int pixels) {
|
||||
}
|
||||
|
||||
GdkPoint
|
||||
nsWindow::DevicePixelsToGdkPointRoundDown(nsIntPoint point) {
|
||||
nsWindow::DevicePixelsToGdkPointRoundDown(LayoutDeviceIntPoint point) {
|
||||
gint scale = GdkScaleFactor();
|
||||
return { point.x / scale, point.y / scale };
|
||||
}
|
||||
@ -6718,7 +6716,7 @@ nsWindow::DevicePixelsToGdkRectRoundOut(LayoutDeviceIntRect rect) {
|
||||
}
|
||||
|
||||
GdkRectangle
|
||||
nsWindow::DevicePixelsToGdkSizeRoundUp(nsIntSize pixelSize) {
|
||||
nsWindow::DevicePixelsToGdkSizeRoundUp(LayoutDeviceIntSize pixelSize) {
|
||||
gint scale = GdkScaleFactor();
|
||||
gint width = (pixelSize.width + scale - 1) / scale;
|
||||
gint height = (pixelSize.height + scale - 1) / scale;
|
||||
@ -6744,13 +6742,13 @@ nsWindow::GdkPointToDevicePixels(GdkPoint point) {
|
||||
point.y * scale);
|
||||
}
|
||||
|
||||
nsIntRect
|
||||
LayoutDeviceIntRect
|
||||
nsWindow::GdkRectToDevicePixels(GdkRectangle rect) {
|
||||
gint scale = GdkScaleFactor();
|
||||
return nsIntRect(rect.x * scale,
|
||||
rect.y * scale,
|
||||
rect.width * scale,
|
||||
rect.height * scale);
|
||||
return LayoutDeviceIntRect(rect.x * scale,
|
||||
rect.y * scale,
|
||||
rect.width * scale,
|
||||
rect.height * scale);
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
@ -340,14 +340,14 @@ public:
|
||||
// To GDK
|
||||
gint DevicePixelsToGdkCoordRoundUp(int pixels);
|
||||
gint DevicePixelsToGdkCoordRoundDown(int pixels);
|
||||
GdkPoint DevicePixelsToGdkPointRoundDown(nsIntPoint point);
|
||||
GdkRectangle DevicePixelsToGdkSizeRoundUp(nsIntSize pixelSize);
|
||||
GdkPoint DevicePixelsToGdkPointRoundDown(LayoutDeviceIntPoint point);
|
||||
GdkRectangle DevicePixelsToGdkSizeRoundUp(LayoutDeviceIntSize pixelSize);
|
||||
|
||||
// From GDK
|
||||
int GdkCoordToDevicePixels(gint coord);
|
||||
LayoutDeviceIntPoint GdkPointToDevicePixels(GdkPoint point);
|
||||
LayoutDeviceIntPoint GdkEventCoordsToDevicePixels(gdouble x, gdouble y);
|
||||
nsIntRect GdkRectToDevicePixels(GdkRectangle rect);
|
||||
LayoutDeviceIntRect GdkRectToDevicePixels(GdkRectangle rect);
|
||||
|
||||
protected:
|
||||
virtual ~nsWindow();
|
||||
|
@ -1443,7 +1443,7 @@ NS_METHOD nsBaseWidget::GetClientBounds(LayoutDeviceIntRect &aRect)
|
||||
**/
|
||||
NS_METHOD nsBaseWidget::GetBounds(LayoutDeviceIntRect &aRect)
|
||||
{
|
||||
aRect = LayoutDeviceIntRect::FromUnknownRect(mBounds);
|
||||
aRect = mBounds;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -515,7 +515,7 @@ protected:
|
||||
RefPtr<TextEventDispatcher> mTextEventDispatcher;
|
||||
nsCursor mCursor;
|
||||
nsBorderStyle mBorderStyle;
|
||||
nsIntRect mBounds;
|
||||
LayoutDeviceIntRect mBounds;
|
||||
LayoutDeviceIntRect* mOriginalBounds;
|
||||
// When this pointer is null, the widget is not clipped
|
||||
mozilla::UniquePtr<LayoutDeviceIntRect[]> mClipRects;
|
||||
|
@ -151,7 +151,7 @@ nsWindow::Create(nsIWidget* aParent,
|
||||
mParent = (nsWindow *)aParent;
|
||||
|
||||
// save our bounds
|
||||
mBounds = aRect.ToUnknownRect();
|
||||
mBounds = aRect;
|
||||
|
||||
// find native parent
|
||||
MozQWidget *parent = nullptr;
|
||||
@ -466,8 +466,7 @@ nsWindow::Resize(double aWidth, double aHeight, bool aRepaint)
|
||||
// synthesize a resize event if this isn't a toplevel
|
||||
if (mIsTopLevel || mListenForResizes) {
|
||||
nsEventStatus status;
|
||||
DispatchResizeEvent(LayoutDeviceIntRect::FromUnknownRect(mBounds),
|
||||
status);
|
||||
DispatchResizeEvent(mBounds, status);
|
||||
}
|
||||
|
||||
NotifyRollupGeometryChange();
|
||||
@ -530,8 +529,7 @@ nsWindow::Resize(double aX, double aY, double aWidth, double aHeight,
|
||||
if (mIsTopLevel || mListenForResizes) {
|
||||
// synthesize a resize event
|
||||
nsEventStatus status;
|
||||
DispatchResizeEvent(LayoutDeviceIntRect::FromUnknownRect(mBounds),
|
||||
status);
|
||||
DispatchResizeEvent(mBounds, status);
|
||||
}
|
||||
|
||||
if (aRepaint) {
|
||||
@ -603,13 +601,11 @@ nsWindow::ConfigureChildren(const nsTArray<nsIWidget::Configuration>& aConfigura
|
||||
NS_ASSERTION(w->GetParent() == this,
|
||||
"Configured widget is not a child");
|
||||
|
||||
LayoutDeviceIntRect wBounds =
|
||||
LayoutDeviceIntRect::FromUnknownRect(w->mBounds);
|
||||
if (wBounds.Size() != configuration.mBounds.Size()) {
|
||||
if (w->mBounds.Size() != configuration.mBounds.Size()) {
|
||||
w->Resize(configuration.mBounds.x, configuration.mBounds.y,
|
||||
configuration.mBounds.width, configuration.mBounds.height,
|
||||
true);
|
||||
} else if (wBounds.TopLeft() != configuration.mBounds.TopLeft()) {
|
||||
} else if (w->mBounds.TopLeft() != configuration.mBounds.TopLeft()) {
|
||||
w->Move(configuration.mBounds.x, configuration.mBounds.y);
|
||||
}
|
||||
}
|
||||
@ -1503,8 +1499,7 @@ void find_first_visible_parent(QWindow* aItem, QWindow*& aVisibleItem)
|
||||
NS_IMETHODIMP
|
||||
nsWindow::GetScreenBounds(LayoutDeviceIntRect& aRect)
|
||||
{
|
||||
aRect = LayoutDeviceIntRect(LayoutDeviceIntPoint(0, 0),
|
||||
LayoutDeviceIntSize::FromUnknownSize(mBounds.Size()));
|
||||
aRect = LayoutDeviceIntRect(LayoutDeviceIntPoint(0, 0), mBounds.Size());
|
||||
if (mIsTopLevel) {
|
||||
QPoint pos = mWidget->position();
|
||||
aRect.MoveTo(pos.x(), pos.y());
|
||||
|
@ -45,7 +45,7 @@ public:
|
||||
|
||||
NS_DECL_NSISCREENMANAGER
|
||||
|
||||
static nsIntRect GetBounds();
|
||||
static LayoutDeviceIntRect GetBounds();
|
||||
|
||||
private:
|
||||
virtual ~UIKitScreenManager () {}
|
||||
|
@ -9,7 +9,7 @@
|
||||
#include "nsScreenManager.h"
|
||||
#include "nsAppShell.h"
|
||||
|
||||
static nsIntRect gScreenBounds;
|
||||
static LayoutDeviceIntRect gScreenBounds;
|
||||
static bool gScreenBoundsSet = false;
|
||||
|
||||
UIKitScreen::UIKitScreen(UIScreen* aScreen)
|
||||
@ -83,7 +83,7 @@ UIKitScreenManager::UIKitScreenManager()
|
||||
{
|
||||
}
|
||||
|
||||
nsIntRect
|
||||
LayoutDeviceIntRect
|
||||
UIKitScreenManager::GetBounds()
|
||||
{
|
||||
if (!gScreenBoundsSet) {
|
||||
|
@ -51,7 +51,7 @@ UIKitPointsToDevPixels(CGPoint aPoint, CGFloat aBackingScale)
|
||||
}
|
||||
|
||||
static CGRect
|
||||
DevPixelsToUIKitPoints(const nsIntRect& aRect, CGFloat aBackingScale)
|
||||
DevPixelsToUIKitPoints(const LayoutDeviceIntRect& aRect, CGFloat aBackingScale)
|
||||
{
|
||||
return CGRectMake((CGFloat)aRect.x / aBackingScale,
|
||||
(CGFloat)aRect.y / aBackingScale,
|
||||
@ -493,7 +493,7 @@ nsWindow::Create(nsIWidget* aParent,
|
||||
mBounds.height = cgRect.size.height;
|
||||
}
|
||||
} else {
|
||||
mBounds = aRect.ToUnknownRect();
|
||||
mBounds = aRect;
|
||||
}
|
||||
|
||||
ALOG("nsWindow[%p]::Create bounds: %d %d %d %d", (void*)this,
|
||||
|
@ -4099,6 +4099,7 @@ TSFTextStore::RecordCompositionStartAction(ITfCompositionView* aComposition,
|
||||
}
|
||||
|
||||
lockedContent.StartComposition(aComposition, *action, aPreserveSelection);
|
||||
action->mData = mComposition.mString;
|
||||
|
||||
MOZ_LOG(sTextStoreLog, LogLevel::Info,
|
||||
("TSF: 0x%p TSFTextStore::RecordCompositionStartAction() succeeded: "
|
||||
@ -4137,6 +4138,33 @@ TSFTextStore::RecordCompositionEndAction()
|
||||
}
|
||||
lockedContent.EndComposition(*action);
|
||||
|
||||
// If this composition was restart but the composition doesn't modify
|
||||
// anything, we should remove the pending composition for preventing to
|
||||
// dispatch redundant composition events.
|
||||
for (size_t i = mPendingActions.Length(), j = 1; i > 0; --i, ++j) {
|
||||
PendingAction& pendingAction = mPendingActions[i - 1];
|
||||
if (pendingAction.mType == PendingAction::COMPOSITION_START) {
|
||||
if (pendingAction.mData != action->mData) {
|
||||
break;
|
||||
}
|
||||
// When only setting selection is necessary, we should append it.
|
||||
if (pendingAction.mAdjustSelection) {
|
||||
PendingAction* setSelection = mPendingActions.AppendElement();
|
||||
setSelection->mType = PendingAction::SET_SELECTION;
|
||||
setSelection->mSelectionStart = pendingAction.mSelectionStart;
|
||||
setSelection->mSelectionLength = pendingAction.mSelectionLength;
|
||||
setSelection->mSelectionReversed = false;
|
||||
}
|
||||
// Remove the redundant pending composition.
|
||||
mPendingActions.RemoveElementsAt(i - 1, j);
|
||||
MOZ_LOG(sTextStoreLog, LogLevel::Info,
|
||||
("TSF: 0x%p TSFTextStore::RecordCompositionEndAction(), "
|
||||
"succeeded, but the composition was canceled due to redundant",
|
||||
this));
|
||||
return S_OK;
|
||||
}
|
||||
}
|
||||
|
||||
MOZ_LOG(sTextStoreLog, LogLevel::Info,
|
||||
("TSF: 0x%p TSFTextStore::RecordCompositionEndAction(), succeeded",
|
||||
this));
|
||||
|
@ -518,7 +518,7 @@ protected:
|
||||
// For compositionstart and selectionset
|
||||
LONG mSelectionStart;
|
||||
LONG mSelectionLength;
|
||||
// For compositionupdate and compositionend
|
||||
// For compositionstart, compositionupdate and compositionend
|
||||
nsString mData;
|
||||
// For compositionupdate
|
||||
RefPtr<TextRangeArray> mRanges;
|
||||
|
@ -189,6 +189,7 @@
|
||||
|
||||
#include "mozilla/layers/APZCTreeManager.h"
|
||||
#include "mozilla/layers/InputAPZContext.h"
|
||||
#include "ClientLayerManager.h"
|
||||
#include "InputData.h"
|
||||
|
||||
#include "mozilla/Telemetry.h"
|
||||
@ -547,7 +548,7 @@ nsWindow::Create(nsIWidget* aParent,
|
||||
nullptr : aParent;
|
||||
|
||||
mIsTopWidgetWindow = (nullptr == baseParent);
|
||||
mBounds = aRect.ToUnknownRect();
|
||||
mBounds = aRect;
|
||||
|
||||
// Ensure that the toolkit is created.
|
||||
nsToolkit::GetToolkit();
|
||||
@ -1410,6 +1411,20 @@ nsWindow::SetSizeConstraints(const SizeConstraints& aConstraints)
|
||||
c.mMinSize.width = std::max(int32_t(::GetSystemMetrics(SM_CXMINTRACK)), c.mMinSize.width);
|
||||
c.mMinSize.height = std::max(int32_t(::GetSystemMetrics(SM_CYMINTRACK)), c.mMinSize.height);
|
||||
}
|
||||
ClientLayerManager *clientLayerManager =
|
||||
(GetLayerManager()->GetBackendType() == LayersBackend::LAYERS_CLIENT)
|
||||
? static_cast<ClientLayerManager*>(GetLayerManager())
|
||||
: nullptr;
|
||||
|
||||
if (clientLayerManager) {
|
||||
int32_t maxSize = clientLayerManager->GetMaxTextureSize();
|
||||
// We can't make ThebesLayers bigger than this anyway.. no point it letting
|
||||
// a window grow bigger as we won't be able to draw content there in
|
||||
// general.
|
||||
c.mMaxSize.width = std::min(c.mMaxSize.width, maxSize);
|
||||
c.mMaxSize.height = std::min(c.mMaxSize.height, maxSize);
|
||||
}
|
||||
|
||||
mSizeConstraintsScale = GetDefaultScale().scale;
|
||||
|
||||
nsBaseWidget::SetSizeConstraints(c);
|
||||
@ -2015,7 +2030,7 @@ NS_METHOD nsWindow::GetBounds(LayoutDeviceIntRect& aRect)
|
||||
aRect.x = r.left;
|
||||
aRect.y = r.top;
|
||||
} else {
|
||||
aRect = LayoutDeviceIntRect::FromUnknownRect(mBounds);
|
||||
aRect = mBounds;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
@ -2051,7 +2066,7 @@ NS_METHOD nsWindow::GetScreenBounds(LayoutDeviceIntRect& aRect)
|
||||
aRect.x = r.left;
|
||||
aRect.y = r.top;
|
||||
} else {
|
||||
aRect = LayoutDeviceIntRect::FromUnknownRect(mBounds);
|
||||
aRect = mBounds;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -585,7 +585,7 @@ protected:
|
||||
HDC mPaintDC; // only set during painting
|
||||
HDC mCompositeDC; // only set during StartRemoteDrawing
|
||||
|
||||
nsIntRect mLastPaintBounds;
|
||||
LayoutDeviceIntRect mLastPaintBounds;
|
||||
|
||||
// Used for displayport suppression during window resize
|
||||
enum ResizeState {
|
||||
|
@ -274,33 +274,6 @@ PLDHashTable::Hash2(PLDHashNumber aHash,
|
||||
// uses the high order bits of mKeyHash, so this least-significant reservation
|
||||
// should not hurt the hash function's effectiveness much.
|
||||
|
||||
/* static */ MOZ_ALWAYS_INLINE bool
|
||||
PLDHashTable::EntryIsFree(PLDHashEntryHdr* aEntry)
|
||||
{
|
||||
return aEntry->mKeyHash == 0;
|
||||
}
|
||||
/* static */ MOZ_ALWAYS_INLINE bool
|
||||
PLDHashTable::EntryIsRemoved(PLDHashEntryHdr* aEntry)
|
||||
{
|
||||
return aEntry->mKeyHash == 1;
|
||||
}
|
||||
/* static */ MOZ_ALWAYS_INLINE bool
|
||||
PLDHashTable::EntryIsLive(PLDHashEntryHdr* aEntry)
|
||||
{
|
||||
return aEntry->mKeyHash >= 2;
|
||||
}
|
||||
|
||||
/* static */ MOZ_ALWAYS_INLINE void
|
||||
PLDHashTable::MarkEntryFree(PLDHashEntryHdr* aEntry)
|
||||
{
|
||||
aEntry->mKeyHash = 0;
|
||||
}
|
||||
/* static */ MOZ_ALWAYS_INLINE void
|
||||
PLDHashTable::MarkEntryRemoved(PLDHashEntryHdr* aEntry)
|
||||
{
|
||||
aEntry->mKeyHash = 1;
|
||||
}
|
||||
|
||||
// Match an entry's mKeyHash against an unstored one computed from a key.
|
||||
/* static */ bool
|
||||
PLDHashTable::MatchEntryKeyhash(PLDHashEntryHdr* aEntry, PLDHashNumber aKeyHash)
|
||||
@ -783,12 +756,6 @@ PLDHashTable::Iterator::~Iterator()
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
PLDHashTable::Iterator::Done() const
|
||||
{
|
||||
return mNexts == mNextsLimit;
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE bool
|
||||
PLDHashTable::Iterator::IsOnNonLiveEntry() const
|
||||
{
|
||||
@ -805,16 +772,6 @@ PLDHashTable::Iterator::MoveToNextEntry()
|
||||
}
|
||||
}
|
||||
|
||||
PLDHashEntryHdr*
|
||||
PLDHashTable::Iterator::Get() const
|
||||
{
|
||||
MOZ_ASSERT(!Done());
|
||||
|
||||
PLDHashEntryHdr* entry = reinterpret_cast<PLDHashEntryHdr*>(mCurrent);
|
||||
MOZ_ASSERT(EntryIsLive(entry));
|
||||
return entry;
|
||||
}
|
||||
|
||||
void
|
||||
PLDHashTable::Iterator::Next()
|
||||
{
|
||||
|
@ -450,9 +450,21 @@ public:
|
||||
Iterator(Iterator&& aOther);
|
||||
~Iterator();
|
||||
|
||||
bool Done() const; // Have we finished?
|
||||
PLDHashEntryHdr* Get() const; // Get the current entry.
|
||||
void Next(); // Advance to the next entry.
|
||||
// Have we finished?
|
||||
bool Done() const { return mNexts == mNextsLimit; }
|
||||
|
||||
// Get the current entry.
|
||||
PLDHashEntryHdr* Get() const
|
||||
{
|
||||
MOZ_ASSERT(!Done());
|
||||
|
||||
PLDHashEntryHdr* entry = reinterpret_cast<PLDHashEntryHdr*>(mCurrent);
|
||||
MOZ_ASSERT(EntryIsLive(entry));
|
||||
return entry;
|
||||
}
|
||||
|
||||
// Advance to the next entry.
|
||||
void Next();
|
||||
|
||||
// Remove the current entry. Must only be called once per entry, and Get()
|
||||
// must not be called on that entry afterwards.
|
||||
@ -498,11 +510,27 @@ private:
|
||||
|
||||
static const PLDHashNumber kCollisionFlag = 1;
|
||||
|
||||
static bool EntryIsFree(PLDHashEntryHdr* aEntry);
|
||||
static bool EntryIsRemoved(PLDHashEntryHdr* aEntry);
|
||||
static bool EntryIsLive(PLDHashEntryHdr* aEntry);
|
||||
static void MarkEntryFree(PLDHashEntryHdr* aEntry);
|
||||
static void MarkEntryRemoved(PLDHashEntryHdr* aEntry);
|
||||
static bool EntryIsFree(PLDHashEntryHdr* aEntry)
|
||||
{
|
||||
return aEntry->mKeyHash == 0;
|
||||
}
|
||||
static bool EntryIsRemoved(PLDHashEntryHdr* aEntry)
|
||||
{
|
||||
return aEntry->mKeyHash == 1;
|
||||
}
|
||||
static bool EntryIsLive(PLDHashEntryHdr* aEntry)
|
||||
{
|
||||
return aEntry->mKeyHash >= 2;
|
||||
}
|
||||
|
||||
static void MarkEntryFree(PLDHashEntryHdr* aEntry)
|
||||
{
|
||||
aEntry->mKeyHash = 0;
|
||||
}
|
||||
static void MarkEntryRemoved(PLDHashEntryHdr* aEntry)
|
||||
{
|
||||
aEntry->mKeyHash = 1;
|
||||
}
|
||||
|
||||
PLDHashNumber Hash1(PLDHashNumber aHash0);
|
||||
void Hash2(PLDHashNumber aHash, uint32_t& aHash2Out, uint32_t& aSizeMaskOut);
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/BinarySearch.h"
|
||||
#include "mozilla/fallible.h"
|
||||
#include "mozilla/InitializerList.h"
|
||||
#include "mozilla/MathAlgorithms.h"
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
#include "mozilla/Move.h"
|
||||
@ -867,6 +868,7 @@ public:
|
||||
// |const nsTArray_Impl<E, OtherAlloc>&|.
|
||||
explicit nsTArray_Impl(const self_type& aOther) { AppendElements(aOther); }
|
||||
|
||||
explicit nsTArray_Impl(std::initializer_list<E> aIL) { AppendElements(aIL.begin(), aIL.size()); }
|
||||
// Allow converting to a const array with a different kind of allocator,
|
||||
// Since the allocator doesn't matter for const arrays
|
||||
template<typename Allocator>
|
||||
@ -2101,6 +2103,7 @@ public:
|
||||
explicit nsTArray(size_type aCapacity) : base_type(aCapacity) {}
|
||||
explicit nsTArray(const nsTArray& aOther) : base_type(aOther) {}
|
||||
MOZ_IMPLICIT nsTArray(nsTArray&& aOther) : base_type(mozilla::Move(aOther)) {}
|
||||
MOZ_IMPLICIT nsTArray(std::initializer_list<E> aIL) : base_type(aIL) {}
|
||||
|
||||
template<class Allocator>
|
||||
explicit nsTArray(const nsTArray_Impl<E, Allocator>& aOther)
|
||||
|
Loading…
x
Reference in New Issue
Block a user