merge autoland to mozilla-central a=merge

This commit is contained in:
Carsten "Tomcat" Book 2016-09-08 11:59:31 +02:00
Родитель 38f7eb71e0 2487c1c0d0
Коммит 7dde8b7620
53 изменённых файлов: 1512 добавлений и 602 удалений

Просмотреть файл

@ -3842,6 +3842,9 @@
spinnerDisplayed: function () {
this.assert(!this.spinnerTab);
TelemetryStopwatch.start("FX_TAB_SWITCH_SPINNER_VISIBLE_MS", window);
// We have a second, similar probe for capturing recordings of
// when the spinner is displayed for very long periods.
TelemetryStopwatch.start("FX_TAB_SWITCH_SPINNER_VISIBLE_LONG_MS", window);
this.addMarker("AsyncTabSwitch:SpinnerShown");
},
@ -3850,6 +3853,7 @@
this.log("DEBUG: spinner time = " +
TelemetryStopwatch.timeElapsed("FX_TAB_SWITCH_SPINNER_VISIBLE_MS", window));
TelemetryStopwatch.finish("FX_TAB_SWITCH_SPINNER_VISIBLE_MS", window);
TelemetryStopwatch.finish("FX_TAB_SWITCH_SPINNER_VISIBLE_LONG_MS", window);
this.addMarker("AsyncTabSwitch:SpinnerHidden");
// we do not get a onPaint after displaying the spinner
this.maybeFinishTabSwitch();

Просмотреть файл

@ -1076,6 +1076,9 @@ MediaDecoderStateMachine::ExitState()
case DECODER_STATE_COMPLETED:
mSentPlaybackEndedEvent = false;
break;
case DECODER_STATE_SHUTDOWN:
MOZ_DIAGNOSTIC_ASSERT(false, "Shouldn't escape the SHUTDOWN state.");
break;
default:
break;
}
@ -1224,12 +1227,10 @@ MediaDecoderStateMachine::Shutdown()
{
MOZ_ASSERT(OnTaskQueue());
// Once we've entered the shutdown state here there's no going back.
// Change state before issuing shutdown request to threads so those
// threads can start exiting cleanly during the Shutdown call.
ScheduleStateMachine();
SetState(DECODER_STATE_SHUTDOWN);
mDelayedScheduler.Reset();
mBufferedUpdateRequest.DisconnectIfExists();
mQueuedSeek.RejectIfExists(__func__);
@ -1255,6 +1256,40 @@ MediaDecoderStateMachine::Shutdown()
mMediaSink->Shutdown();
// Prevent dangling pointers by disconnecting the listeners.
mAudioQueueListener.Disconnect();
mVideoQueueListener.Disconnect();
mMetadataManager.Disconnect();
// Disconnect canonicals and mirrors before shutting down our task queue.
mBuffered.DisconnectIfConnected();
mIsReaderSuspended.DisconnectIfConnected();
mEstimatedDuration.DisconnectIfConnected();
mExplicitDuration.DisconnectIfConnected();
mPlayState.DisconnectIfConnected();
mNextPlayState.DisconnectIfConnected();
mVolume.DisconnectIfConnected();
mLogicalPlaybackRate.DisconnectIfConnected();
mPreservesPitch.DisconnectIfConnected();
mSameOriginMedia.DisconnectIfConnected();
mMediaPrincipalHandle.DisconnectIfConnected();
mPlaybackBytesPerSecond.DisconnectIfConnected();
mPlaybackRateReliable.DisconnectIfConnected();
mDecoderPosition.DisconnectIfConnected();
mMediaSeekable.DisconnectIfConnected();
mMediaSeekableOnlyInBufferedRanges.DisconnectIfConnected();
mIsVisible.DisconnectIfConnected();
mDuration.DisconnectAll();
mIsShutdown.DisconnectAll();
mNextFrameStatus.DisconnectAll();
mCurrentPosition.DisconnectAll();
mPlaybackOffset.DisconnectAll();
mIsAudioDataAudible.DisconnectAll();
// Shut down the watch manager to stop further notifications.
mWatchManager.Shutdown();
DECODER_LOG("Shutdown started");
// Put a task in the decode queue to shutdown the reader.
@ -1963,16 +1998,8 @@ void
MediaDecoderStateMachine::DecodeError()
{
MOZ_ASSERT(OnTaskQueue());
if (IsShutdown()) {
// Already shutdown.
return;
}
MOZ_ASSERT(!IsShutdown());
DECODER_WARN("Decode error");
// Change state to SHUTDOWN so we have no more processing.
SetState(DECODER_STATE_SHUTDOWN);
// Notify the decode error and MediaDecoder will shut down MDSM.
mOnPlaybackEvent.Notify(MediaEventType::DecodeError);
}
@ -2222,45 +2249,6 @@ RefPtr<ShutdownPromise>
MediaDecoderStateMachine::FinishShutdown()
{
MOZ_ASSERT(OnTaskQueue());
// The reader's listeners hold references to the state machine,
// creating a cycle which keeps the state machine and its shared
// thread pools alive. So break it here.
// Prevent dangling pointers by disconnecting the listeners.
mAudioQueueListener.Disconnect();
mVideoQueueListener.Disconnect();
mMetadataManager.Disconnect();
// Disconnect canonicals and mirrors before shutting down our task queue.
mBuffered.DisconnectIfConnected();
mIsReaderSuspended.DisconnectIfConnected();
mEstimatedDuration.DisconnectIfConnected();
mExplicitDuration.DisconnectIfConnected();
mPlayState.DisconnectIfConnected();
mNextPlayState.DisconnectIfConnected();
mVolume.DisconnectIfConnected();
mLogicalPlaybackRate.DisconnectIfConnected();
mPreservesPitch.DisconnectIfConnected();
mSameOriginMedia.DisconnectIfConnected();
mMediaPrincipalHandle.DisconnectIfConnected();
mPlaybackBytesPerSecond.DisconnectIfConnected();
mPlaybackRateReliable.DisconnectIfConnected();
mDecoderPosition.DisconnectIfConnected();
mMediaSeekable.DisconnectIfConnected();
mMediaSeekableOnlyInBufferedRanges.DisconnectIfConnected();
mIsVisible.DisconnectIfConnected();
mDuration.DisconnectAll();
mIsShutdown.DisconnectAll();
mNextFrameStatus.DisconnectAll();
mCurrentPosition.DisconnectAll();
mPlaybackOffset.DisconnectAll();
mIsAudioDataAudible.DisconnectAll();
// Shut down the watch manager before shutting down our task queue.
mWatchManager.Shutdown();
MOZ_ASSERT(mState == DECODER_STATE_SHUTDOWN,
"How did we escape from the shutdown state?");
DECODER_LOG("Shutting down state machine task queue");
@ -2623,13 +2611,13 @@ MediaDecoderStateMachine::ScheduleStateMachineIn(int64_t aMicroseconds)
TimeStamp now = TimeStamp::Now();
TimeStamp target = now + TimeDuration::FromMicroseconds(aMicroseconds);
SAMPLE_LOG("Scheduling state machine for %lf ms from now", (target - now).ToMilliseconds());
RefPtr<MediaDecoderStateMachine> self = this;
mDelayedScheduler.Ensure(target, [self] () {
self->OnDelayedSchedule();
}, [self] () {
self->NotReached();
// It is OK to capture 'this' without causing UAF because the callback
// always happens before shutdown.
mDelayedScheduler.Ensure(target, [this] () {
mDelayedScheduler.CompleteRequest();
RunStateMachine();
}, [] () {
MOZ_DIAGNOSTIC_ASSERT(false);
});
}
@ -2673,7 +2661,8 @@ void MediaDecoderStateMachine::PreservesPitchChanged()
mMediaSink->SetPreservesPitch(mPreservesPitch);
}
bool MediaDecoderStateMachine::IsShutdown()
bool
MediaDecoderStateMachine::IsShutdown() const
{
MOZ_ASSERT(OnTaskQueue());
return mIsShutdown;

Просмотреть файл

@ -319,23 +319,13 @@ private:
// request is discarded.
void ScheduleStateMachineIn(int64_t aMicroseconds);
void OnDelayedSchedule()
{
MOZ_ASSERT(OnTaskQueue());
mDelayedScheduler.CompleteRequest();
ScheduleStateMachine();
}
void NotReached() { MOZ_DIAGNOSTIC_ASSERT(false); }
// Discard audio/video data that are already played by MSG.
void DiscardStreamData();
bool HaveEnoughDecodedAudio();
bool HaveEnoughDecodedVideo();
// Returns true if the state machine has shutdown or is in the process of
// shutting down. The decoder monitor must be held while calling this.
bool IsShutdown();
// True if shutdown process has begun.
bool IsShutdown() const;
// Returns true if we're currently playing. The decoder monitor must
// be held.

Просмотреть файл

@ -14,6 +14,7 @@
#include "mp4_demuxer/ResourceStream.h"
#include "mp4_demuxer/BufferStream.h"
#include "mp4_demuxer/Index.h"
#include "nsPrintfCString.h"
// Used for telemetry
#include "mozilla/Telemetry.h"
@ -27,6 +28,8 @@ mozilla::LogModule* GetDemuxerLog() {
return gMediaDemuxerLog;
}
#define LOG(arg, ...) MOZ_LOG(gMediaDemuxerLog, mozilla::LogLevel::Debug, ("MP4Demuxer(%p)::%s: " arg, this, __func__, ##__VA_ARGS__))
namespace mozilla {
class MP4TrackDemuxer : public MediaTrackDemuxer
@ -55,7 +58,7 @@ public:
private:
friend class MP4Demuxer;
void NotifyDataArrived();
void UpdateSamples(nsTArray<RefPtr<MediaRawData>>& aSamples);
already_AddRefed<MediaRawData> GetNextSample();
void EnsureUpToDateIndex();
void SetNextKeyFrameTime();
RefPtr<MP4Demuxer> mParent;
@ -68,6 +71,7 @@ private:
RefPtr<MediaRawData> mQueuedSample;
bool mNeedReIndex;
bool mNeedSPSForTelemetry;
bool mIsH264 = false;
};
@ -241,6 +245,7 @@ MP4TrackDemuxer::MP4TrackDemuxer(MP4Demuxer* aParent,
if (videoInfo &&
(mInfo->mMimeType.EqualsLiteral("video/mp4") ||
mInfo->mMimeType.EqualsLiteral("video/avc"))) {
mIsH264 = true;
RefPtr<MediaByteBuffer> extraData = videoInfo->mExtraData;
mNeedSPSForTelemetry = AccumulateSPSTelemetry(extraData);
mp4_demuxer::SPSData spsdata;
@ -289,15 +294,76 @@ MP4TrackDemuxer::Seek(media::TimeUnit aTime)
mIterator->Seek(seekTime);
// Check what time we actually seeked to.
mQueuedSample = mIterator->GetNext();
if (mQueuedSample) {
seekTime = mQueuedSample->mTime;
}
RefPtr<MediaRawData> sample;
do {
sample = GetNextSample();
if (!sample) {
return SeekPromise::CreateAndReject(DemuxerFailureReason::END_OF_STREAM, __func__);
}
if (!sample->Size()) {
// This sample can't be decoded, continue searching.
continue;
}
if (sample->mKeyframe) {
mQueuedSample = sample;
seekTime = mQueuedSample->mTime;
}
} while (!mQueuedSample);
SetNextKeyFrameTime();
return SeekPromise::CreateAndResolve(media::TimeUnit::FromMicroseconds(seekTime), __func__);
}
already_AddRefed<MediaRawData>
MP4TrackDemuxer::GetNextSample()
{
RefPtr<MediaRawData> sample = mIterator->GetNext();
if (!sample) {
return nullptr;
}
if (mInfo->GetAsVideoInfo()) {
sample->mExtraData = mInfo->GetAsVideoInfo()->mExtraData;
if (mIsH264) {
mp4_demuxer::H264::FrameType type =
mp4_demuxer::H264::GetFrameType(sample);
switch (type) {
case mp4_demuxer::H264::FrameType::I_FRAME: MOZ_FALLTHROUGH;
case mp4_demuxer::H264::FrameType::OTHER:
{
bool keyframe = type == mp4_demuxer::H264::FrameType::I_FRAME;
if (sample->mKeyframe != keyframe) {
NS_WARNING(nsPrintfCString("Frame incorrectly marked as %skeyframe @ pts:%lld dur:%u dts:%lld",
keyframe ? "" : "non-",
sample->mTime,
sample->mDuration,
sample->mTimecode).get());
sample->mKeyframe = keyframe;
}
break;
}
case mp4_demuxer::H264::FrameType::INVALID:
NS_WARNING(nsPrintfCString("Invalid H264 frame @ pts:%lld dur:%u dts:%lld",
sample->mTime,
sample->mDuration,
sample->mTimecode).get());
// We could reject the sample now, however demuxer errors are fatal.
// So we keep the invalid frame, relying on the H264 decoder to
// handle the error later.
// TODO: make demuxer errors non-fatal.
break;
}
}
}
if (sample->mCrypto.mValid) {
nsAutoPtr<MediaRawDataWriter> writer(sample->CreateWriter());
writer->mCrypto.mMode = mInfo->mCrypto.mMode;
writer->mCrypto.mIVSize = mInfo->mCrypto.mIVSize;
writer->mCrypto.mKeyId.AppendElements(mInfo->mCrypto.mKeyId);
}
return sample.forget();
}
RefPtr<MP4TrackDemuxer::SamplesPromise>
MP4TrackDemuxer::GetSamples(int32_t aNumSamples)
{
@ -308,12 +374,14 @@ MP4TrackDemuxer::GetSamples(int32_t aNumSamples)
}
if (mQueuedSample) {
MOZ_ASSERT(mQueuedSample->mKeyframe,
"mQueuedSample must be a keyframe");
samples->mSamples.AppendElement(mQueuedSample);
mQueuedSample = nullptr;
aNumSamples--;
}
RefPtr<MediaRawData> sample;
while (aNumSamples && (sample = mIterator->GetNext())) {
while (aNumSamples && (sample = GetNextSample())) {
if (!sample->Size()) {
continue;
}
@ -324,7 +392,19 @@ MP4TrackDemuxer::GetSamples(int32_t aNumSamples)
if (samples->mSamples.IsEmpty()) {
return SamplesPromise::CreateAndReject(DemuxerFailureReason::END_OF_STREAM, __func__);
} else {
UpdateSamples(samples->mSamples);
for (const auto& sample : samples->mSamples) {
// Collect telemetry from h264 Annex B SPS.
if (mNeedSPSForTelemetry && mp4_demuxer::AnnexB::HasSPS(sample)) {
RefPtr<MediaByteBuffer> extradata =
mp4_demuxer::AnnexB::ExtractExtraData(sample);
mNeedSPSForTelemetry = AccumulateSPSTelemetry(extradata);
}
}
if (mNextKeyframeTime.isNothing() ||
samples->mSamples.LastElement()->mTime >= mNextKeyframeTime.value().ToMicroseconds()) {
SetNextKeyFrameTime();
}
return SamplesPromise::CreateAndResolve(samples, __func__);
}
}
@ -349,33 +429,6 @@ MP4TrackDemuxer::Reset()
SetNextKeyFrameTime();
}
void
MP4TrackDemuxer::UpdateSamples(nsTArray<RefPtr<MediaRawData>>& aSamples)
{
for (size_t i = 0; i < aSamples.Length(); i++) {
MediaRawData* sample = aSamples[i];
// Collect telemetry from h264 Annex B SPS.
if (mNeedSPSForTelemetry && mp4_demuxer::AnnexB::HasSPS(sample)) {
RefPtr<MediaByteBuffer> extradata =
mp4_demuxer::AnnexB::ExtractExtraData(sample);
mNeedSPSForTelemetry = AccumulateSPSTelemetry(extradata);
}
if (sample->mCrypto.mValid) {
nsAutoPtr<MediaRawDataWriter> writer(sample->CreateWriter());
writer->mCrypto.mMode = mInfo->mCrypto.mMode;
writer->mCrypto.mIVSize = mInfo->mCrypto.mIVSize;
writer->mCrypto.mKeyId.AppendElements(mInfo->mCrypto.mKeyId);
}
if (mInfo->GetAsVideoInfo()) {
sample->mExtraData = mInfo->GetAsVideoInfo()->mExtraData;
}
}
if (mNextKeyframeTime.isNothing() ||
aSamples.LastElement()->mTime >= mNextKeyframeTime.value().ToMicroseconds()) {
SetNextKeyFrameTime();
}
}
nsresult
MP4TrackDemuxer::GetNextRandomAccessPoint(media::TimeUnit* aTime)
{
@ -397,7 +450,7 @@ MP4TrackDemuxer::SkipToNextRandomAccessPoint(media::TimeUnit aTimeThreshold)
uint32_t parsed = 0;
bool found = false;
RefPtr<MediaRawData> sample;
while (!found && (sample = mIterator->GetNext())) {
while (!found && (sample = GetNextSample())) {
parsed++;
if (sample->mKeyframe && sample->mTime >= aTimeThreshold.ToMicroseconds()) {
found = true;
@ -441,3 +494,5 @@ MP4TrackDemuxer::BreakCycles()
}
} // namespace mozilla
#undef LOG

Просмотреть файл

@ -109,7 +109,7 @@ public:
// WMF H.264 Video Decoder and Apple ATDecoder
// do not support YUV444 format.
// For consistency, all decoders should be checked.
if (!mp4_demuxer::H264::DecodeSPSFromExtraData(extraData, spsdata) ||
if (mp4_demuxer::H264::DecodeSPSFromExtraData(extraData, spsdata) &&
spsdata.chroma_format_idc == PDMFactory::kYUV444) {
return SupportChecker::Result::kVideoFormatNotSupported;
}
@ -123,10 +123,10 @@ public:
Check()
{
for (auto& checker : mCheckerList) {
auto result = checker();
if (result != SupportChecker::Result::kSupported) {
return result;
}
auto result = checker();
if (result != SupportChecker::Result::kSupported) {
return result;
}
}
return SupportChecker::Result::kSupported;
}

Просмотреть файл

@ -59,6 +59,14 @@ H264Converter::Input(MediaRawData* aSample)
}
if (mInitPromiseRequest.Exists()) {
if (mNeedKeyframe) {
if (!aSample->mKeyframe) {
// Frames dropped, we need a new one.
mCallback->InputExhausted();
return NS_OK;
}
mNeedKeyframe = false;
}
mMediaRawSamples.AppendElement(aSample);
return NS_OK;
}
@ -72,6 +80,7 @@ H264Converter::Input(MediaRawData* aSample)
if (rv == NS_ERROR_NOT_INITIALIZED) {
// We are missing the required SPS to create the decoder.
// Ignore for the time being, the MediaRawData will be dropped.
mCallback->InputExhausted();
return NS_OK;
}
} else {
@ -79,11 +88,18 @@ H264Converter::Input(MediaRawData* aSample)
}
NS_ENSURE_SUCCESS(rv, rv);
if (mNeedKeyframe && !aSample->mKeyframe) {
mCallback->InputExhausted();
return NS_OK;
}
if (!mNeedAVCC &&
!mp4_demuxer::AnnexB::ConvertSampleToAnnexB(aSample)) {
return NS_ERROR_FAILURE;
}
mNeedKeyframe = false;
aSample->mExtraData = mCurrentConfig.mExtraData;
return mDecoder->Input(aSample);
@ -92,6 +108,7 @@ H264Converter::Input(MediaRawData* aSample)
nsresult
H264Converter::Flush()
{
mNeedKeyframe = true;
if (mDecoder) {
return mDecoder->Flush();
}
@ -101,6 +118,7 @@ H264Converter::Flush()
nsresult
H264Converter::Drain()
{
mNeedKeyframe = true;
if (mDecoder) {
return mDecoder->Drain();
}
@ -137,6 +155,24 @@ H264Converter::CreateDecoder(DecoderDoctorDiagnostics* aDiagnostics)
return NS_ERROR_NOT_INITIALIZED;
}
UpdateConfigFromExtraData(mCurrentConfig.mExtraData);
mp4_demuxer::SPSData spsdata;
if (mp4_demuxer::H264::DecodeSPSFromExtraData(mCurrentConfig.mExtraData, spsdata)) {
// Do some format check here.
// WMF H.264 Video Decoder and Apple ATDecoder do not support YUV444 format.
if (spsdata.chroma_format_idc == 3 /*YUV444*/) {
mLastError = NS_ERROR_FAILURE;
if (aDiagnostics) {
aDiagnostics->SetVideoFormatNotSupport();
}
return NS_ERROR_FAILURE;
}
} else if (mNeedAVCC) {
// SPS was invalid.
mLastError = NS_ERROR_FAILURE;
return NS_ERROR_FAILURE;
}
if (!mNeedAVCC) {
// When using a decoder handling AnnexB, we get here only once from the
// constructor. We do want to get the dimensions extracted from the SPS.
@ -157,6 +193,9 @@ H264Converter::CreateDecoder(DecoderDoctorDiagnostics* aDiagnostics)
mLastError = NS_ERROR_FAILURE;
return NS_ERROR_FAILURE;
}
mNeedKeyframe = true;
return NS_OK;
}
@ -190,11 +229,22 @@ void
H264Converter::OnDecoderInitDone(const TrackType aTrackType)
{
mInitPromiseRequest.Complete();
bool gotInput = false;
for (uint32_t i = 0 ; i < mMediaRawSamples.Length(); i++) {
if (NS_FAILED(mDecoder->Input(mMediaRawSamples[i]))) {
const RefPtr<MediaRawData>& sample = mMediaRawSamples[i];
if (mNeedKeyframe) {
if (!sample->mKeyframe) {
continue;
}
mNeedKeyframe = false;
}
if (NS_FAILED(mDecoder->Input(sample))) {
mCallback->Error(MediaDataDecoderError::FATAL_ERROR);
}
}
if (!gotInput) {
mCallback->InputExhausted();
}
mMediaRawSamples.Clear();
}

Просмотреть файл

@ -66,6 +66,7 @@ private:
RefPtr<GMPCrashHelper> mGMPCrashHelper;
bool mNeedAVCC;
nsresult mLastError;
bool mNeedKeyframe = true;
};
} // namespace mozilla

Просмотреть файл

@ -463,7 +463,7 @@ PresentationService::HandleSessionRequest(nsIPresentationSessionRequest* aReques
info->SetControlChannel(ctrlChannel);
info->SetDevice(device);
return static_cast<PresentationPresentingInfo*>(
info.get())->NotifyResponderReady();
info.get())->DoReconnect();
}
// This is the case for a new session.

Просмотреть файл

@ -405,11 +405,16 @@ PresentationSessionInfo::ContinueTermination()
NS_IMETHODIMP
PresentationSessionInfo::NotifyTransportReady()
{
PRES_DEBUG("%s:id[%s], role[%d]\n", __func__,
NS_ConvertUTF16toUTF8(mSessionId).get(), mRole);
PRES_DEBUG("%s:id[%s], role[%d], state[%d]\n", __func__,
NS_ConvertUTF16toUTF8(mSessionId).get(), mRole, mState);
MOZ_ASSERT(NS_IsMainThread());
if (mState != nsIPresentationSessionListener::STATE_CONNECTING &&
mState != nsIPresentationSessionListener::STATE_CONNECTED) {
return NS_OK;
}
mIsTransportReady = true;
// Established RTCDataChannel implies responder is ready.
@ -484,11 +489,15 @@ PresentationSessionInfo::NotifyData(const nsACString& aData)
NS_IMETHODIMP
PresentationSessionInfo::OnSessionTransport(nsIPresentationSessionTransport* transport)
{
PRES_DEBUG("%s:id[%s], role[%d]\n", __func__,
NS_ConvertUTF16toUTF8(mSessionId).get(), mRole);
PRES_DEBUG("%s:id[%s], role[%d], state[%d]\n", __func__,
NS_ConvertUTF16toUTF8(mSessionId).get(), mRole, mState);
SetBuilder(nullptr);
if (mState != nsIPresentationSessionListener::STATE_CONNECTING) {
return NS_ERROR_FAILURE;
}
// The session transport is managed by content process
if (!transport) {
return NS_OK;
@ -1195,6 +1204,7 @@ nsresult
PresentationPresentingInfo::InitTransportAndSendAnswer()
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mState == nsIPresentationSessionListener::STATE_CONNECTING);
uint8_t type = 0;
nsresult rv = mRequesterDescription->GetType(&type);
@ -1308,8 +1318,8 @@ PresentationPresentingInfo::IsAccessible(base::ProcessId aProcessId)
nsresult
PresentationPresentingInfo::NotifyResponderReady()
{
PRES_DEBUG("%s:id[%s], role[%d]\n", __func__,
NS_ConvertUTF16toUTF8(mSessionId).get(), mRole);
PRES_DEBUG("%s:id[%s], role[%d], state[%d]\n", __func__,
NS_ConvertUTF16toUTF8(mSessionId).get(), mRole, mState);
if (mTimer) {
mTimer->Cancel();
@ -1344,6 +1354,19 @@ PresentationPresentingInfo::NotifyResponderFailure()
return ReplyError(NS_ERROR_DOM_OPERATION_ERR);
}
nsresult
PresentationPresentingInfo::DoReconnect()
{
PRES_DEBUG("%s:id[%s], role[%d]\n", __func__,
NS_ConvertUTF16toUTF8(mSessionId).get(), mRole);
MOZ_ASSERT(mState == nsIPresentationSessionListener::STATE_CLOSED);
SetStateWithReason(nsIPresentationSessionListener::STATE_CONNECTING, NS_OK);
return NotifyResponderReady();
}
// nsIPresentationControlChannelListener
NS_IMETHODIMP
PresentationPresentingInfo::OnOffer(nsIPresentationChannelDescription* aDescription)

Просмотреть файл

@ -254,6 +254,8 @@ public:
bool IsAccessible(base::ProcessId aProcessId) override;
nsresult DoReconnect();
private:
~PresentationPresentingInfo()
{

Просмотреть файл

@ -240,19 +240,19 @@ const mockedSessionTransport = {
return this._selfAddress;
},
buildTCPSenderTransport: function(transport, listener) {
sendAsyncMessage('data-transport-initialized');
this._listener = listener;
this._role = Ci.nsIPresentationService.ROLE_CONTROLLER;
this._listener.onSessionTransport(this);
this._listener = null;
sendAsyncMessage('data-transport-initialized');
setTimeout(()=>{
this._listener.onSessionTransport(this);
this._listener = null;
this.simulateTransportReady();
}, 0);
},
buildTCPReceiverTransport: function(description, listener) {
this._listener = listener;
this._role = Ci.nsIPresentationService.ROLE_CONTROLLER;
this._role = Ci.nsIPresentationService.ROLE_RECEIVER;
var addresses = description.QueryInterface(Ci.nsIPresentationChannelDescription).tcpAddress;
this._selfAddress = {
@ -269,7 +269,6 @@ const mockedSessionTransport = {
},
// in-process case
buildDataChannelTransport: function(role, window, listener) {
dump("PresentationSessionChromeScript: build data channel transport\n");
this._listener = listener;
this._role = role;

Просмотреть файл

@ -42,318 +42,437 @@ function setup() {
}
function testStartConnectionCancelPrompt() {
return new Promise(function(aResolve, aReject) {
gScript.addMessageListener('device-prompt', function devicePromptHandler() {
gScript.removeMessageListener('device-prompt', devicePromptHandler);
info("Device prompt is triggered.");
gScript.sendAsyncMessage('trigger-device-prompt-cancel', SpecialPowers.Cr.NS_ERROR_DOM_NOT_ALLOWED_ERR);
});
info('--- testStartConnectionCancelPrompt ---');
return Promise.all([
new Promise((resolve) => {
gScript.addMessageListener('device-prompt', function devicePromptHandler() {
gScript.removeMessageListener('device-prompt', devicePromptHandler);
info("Device prompt is triggered.");
gScript.sendAsyncMessage('trigger-device-prompt-cancel', SpecialPowers.Cr.NS_ERROR_DOM_NOT_ALLOWED_ERR);
resolve();
});
}),
request.start().then(
function(aConnection) {
ok(false, "|start| shouldn't succeed in this case.");
aReject();
},
function(aError) {
is(aError.name, "NotAllowedError", "NotAllowedError is expected when the prompt is canceled.");
aResolve();
}
);
});
),
]);
}
function testStartConnectionNoDevice() {
return new Promise(function(aResolve, aReject) {
gScript.addMessageListener('device-prompt', function devicePromptHandler() {
gScript.removeMessageListener('device-prompt', devicePromptHandler);
info("Device prompt is triggered.");
gScript.sendAsyncMessage('trigger-device-prompt-cancel', SpecialPowers.Cr.NS_ERROR_DOM_NOT_FOUND_ERR);
});
info('--- testStartConnectionNoDevice ---');
return Promise.all([
new Promise((resolve) => {
gScript.addMessageListener('device-prompt', function devicePromptHandler() {
gScript.removeMessageListener('device-prompt', devicePromptHandler);
info("Device prompt is triggered.");
gScript.sendAsyncMessage('trigger-device-prompt-cancel', SpecialPowers.Cr.NS_ERROR_DOM_NOT_FOUND_ERR);
resolve();
});
}),
request.start().then(
function(aConnection) {
ok(false, "|start| shouldn't succeed in this case.");
aReject();
},
function(aError) {
is(aError.name, "NotFoundError", "NotFoundError is expected when no available device.");
aResolve();
}
);
});
),
]);
}
function testStartConnectionUnexpectedControlChannelCloseBeforeDataTransportInit() {
return new Promise(function(aResolve, aReject) {
gScript.addMessageListener('device-prompt', function devicePromptHandler() {
gScript.removeMessageListener('device-prompt', devicePromptHandler);
info("Device prompt is triggered.");
gScript.sendAsyncMessage('trigger-device-prompt-select');
});
info('--- testStartConnectionUnexpectedControlChannelCloseBeforeDataTransportInit ---');
return Promise.all([
gScript.addMessageListener('control-channel-established', function controlChannelEstablishedHandler() {
gScript.removeMessageListener('control-channel-established', controlChannelEstablishedHandler);
info("A control channel is established.");
gScript.sendAsyncMessage('trigger-control-channel-open');
});
new Promise((resolve) => {
gScript.addMessageListener('device-prompt', function devicePromptHandler() {
gScript.removeMessageListener('device-prompt', devicePromptHandler);
info("Device prompt is triggered.");
gScript.sendAsyncMessage('trigger-device-prompt-select');
resolve();
});
}),
gScript.addMessageListener('control-channel-opened', function controlChannelOpenedHandler(aReason) {
gScript.removeMessageListener('control-channel-opened', controlChannelOpenedHandler);
info("The control channel is opened.");
});
new Promise((resolve) => {
gScript.addMessageListener('control-channel-established', function controlChannelEstablishedHandler() {
gScript.removeMessageListener('control-channel-established', controlChannelEstablishedHandler);
info("A control channel is established.");
gScript.sendAsyncMessage('trigger-control-channel-open');
resolve();
});
}),
gScript.addMessageListener('control-channel-closed', function controlChannelClosedHandler(aReason) {
gScript.removeMessageListener('control-channel-closed', controlChannelClosedHandler);
info("The control channel is closed. " + aReason);
});
new Promise((resolve) => {
gScript.addMessageListener('control-channel-opened', function controlChannelOpenedHandler() {
gScript.removeMessageListener('control-channel-opened', controlChannelOpenedHandler);
info("The control channel is opened.");
resolve();
});
}),
gScript.addMessageListener('offer-sent', function offerSentHandler(aIsValid) {
gScript.removeMessageListener('offer-sent', offerSentHandler);
ok(aIsValid, "A valid offer is sent out.");
gScript.sendAsyncMessage('trigger-control-channel-close', SpecialPowers.Cr.NS_ERROR_FAILURE);
});
new Promise((resolve) => {
gScript.addMessageListener('control-channel-closed', function controlChannelClosedHandler(aReason) {
gScript.removeMessageListener('control-channel-closed', controlChannelClosedHandler);
info("The control channel is closed. " + aReason);
is(aReason, SpecialPowers.Cr.NS_ERROR_FAILURE, "The control channel is closed with NS_ERROR_FAILURE");
resolve();
});
}),
new Promise((resolve) => {
gScript.addMessageListener('offer-sent', function offerSentHandler(aIsValid) {
gScript.removeMessageListener('offer-sent', offerSentHandler);
ok(aIsValid, "A valid offer is sent out.");
gScript.sendAsyncMessage('trigger-control-channel-close', SpecialPowers.Cr.NS_ERROR_FAILURE);
resolve();
});
}),
request.start().then(
function(aConnection) {
is(aConnection.state, "connecting", "The initial state should be connecting.");
aConnection.onclose = function() {
aConnection.onclose = null;
is(aConnection.state, "closed", "Connection should be closed.");
aResolve();
};
return new Promise((resolve) => {
aConnection.onclose = function() {
aConnection.onclose = null;
is(aConnection.state, "closed", "Connection should be closed.");
resolve();
};
});
},
function(aError) {
ok(false, "Error occurred when establishing a connection: " + aError);
teardown();
aReject();
}
);
});
),
]);
}
function testStartConnectionUnexpectedControlChannelCloseNoReasonBeforeDataTransportInit() {
return new Promise(function(aResolve, aReject) {
gScript.addMessageListener('device-prompt', function devicePromptHandler() {
gScript.removeMessageListener('device-prompt', devicePromptHandler);
info("Device prompt is triggered.");
gScript.sendAsyncMessage('trigger-device-prompt-select');
});
info('--- testStartConnectionUnexpectedControlChannelCloseNoReasonBeforeDataTransportInit ---');
return Promise.all([
gScript.addMessageListener('control-channel-established', function controlChannelEstablishedHandler() {
gScript.removeMessageListener('control-channel-established', controlChannelEstablishedHandler);
info("A control channel is established.");
gScript.sendAsyncMessage('trigger-control-channel-open');
});
new Promise((resolve) => {
gScript.addMessageListener('device-prompt', function devicePromptHandler() {
gScript.removeMessageListener('device-prompt', devicePromptHandler);
info("Device prompt is triggered.");
gScript.sendAsyncMessage('trigger-device-prompt-select');
resolve();
});
}),
gScript.addMessageListener('control-channel-opened', function controlChannelOpenedHandler(aReason) {
gScript.removeMessageListener('control-channel-opened', controlChannelOpenedHandler);
info("The control channel is opened.");
});
new Promise((resolve) => {
gScript.addMessageListener('control-channel-established', function controlChannelEstablishedHandler() {
gScript.removeMessageListener('control-channel-established', controlChannelEstablishedHandler);
info("A control channel is established.");
gScript.sendAsyncMessage('trigger-control-channel-open');
resolve();
});
}),
gScript.addMessageListener('control-channel-closed', function controlChannelClosedHandler(aReason) {
gScript.removeMessageListener('control-channel-closed', controlChannelClosedHandler);
info("The control channel is closed. " + aReason);
});
new Promise((resolve) => {
gScript.addMessageListener('control-channel-opened', function controlChannelOpenedHandler() {
gScript.removeMessageListener('control-channel-opened', controlChannelOpenedHandler);
info("The control channel is opened.");
resolve();
});
}),
gScript.addMessageListener('offer-sent', function offerSentHandler(aIsValid) {
gScript.removeMessageListener('offer-sent', offerSentHandler);
ok(aIsValid, "A valid offer is sent out.");
gScript.sendAsyncMessage('trigger-control-channel-close', SpecialPowers.Cr.NS_OK);
});
new Promise((resolve) => {
gScript.addMessageListener('control-channel-closed', function controlChannelClosedHandler(aReason) {
gScript.removeMessageListener('control-channel-closed', controlChannelClosedHandler);
info("The control channel is closed. " + aReason);
is(aReason, SpecialPowers.Cr.NS_OK, "The control channel is closed with NS_OK");
resolve();
});
}),
new Promise((resolve) => {
gScript.addMessageListener('offer-sent', function offerSentHandler(aIsValid) {
gScript.removeMessageListener('offer-sent', offerSentHandler);
ok(aIsValid, "A valid offer is sent out.");
gScript.sendAsyncMessage('trigger-control-channel-close', SpecialPowers.Cr.NS_OK);
resolve();
});
}),
request.start().then(
function(aConnection) {
is(aConnection.state, "connecting", "The initial state should be connecting.");
aConnection.onclose = function() {
aConnection.onclose = null;
is(aConnection.state, "closed", "Connection should be closed.");
aResolve();
};
return new Promise((resolve) => {
aConnection.onclose = function() {
aConnection.onclose = null;
is(aConnection.state, "closed", "Connection should be closed.");
resolve();
};
});
},
function(aError) {
ok(false, "Error occurred when establishing a connection: " + aError);
teardown();
aReject();
}
);
});
),
]);
}
function testStartConnectionUnexpectedControlChannelCloseBeforeDataTransportReady() {
return new Promise(function(aResolve, aReject) {
gScript.addMessageListener('device-prompt', function devicePromptHandler() {
gScript.removeMessageListener('device-prompt', devicePromptHandler);
info("Device prompt is triggered.");
gScript.sendAsyncMessage('trigger-device-prompt-select');
});
info('--- testStartConnectionUnexpectedControlChannelCloseBeforeDataTransportReady ---');
return Promise.all([
gScript.addMessageListener('control-channel-established', function controlChannelEstablishedHandler() {
gScript.removeMessageListener('control-channel-established', controlChannelEstablishedHandler);
info("A control channel is established.");
gScript.sendAsyncMessage('trigger-control-channel-open');
});
new Promise((resolve) => {
gScript.addMessageListener('device-prompt', function devicePromptHandler() {
gScript.removeMessageListener('device-prompt', devicePromptHandler);
info("Device prompt is triggered.");
gScript.sendAsyncMessage('trigger-device-prompt-select');
resolve();
});
}),
gScript.addMessageListener('control-channel-opened', function controlChannelOpenedHandler(aReason) {
gScript.removeMessageListener('control-channel-opened', controlChannelOpenedHandler);
info("The control channel is opened.");
});
new Promise((resolve) => {
gScript.addMessageListener('control-channel-established', function controlChannelEstablishedHandler() {
gScript.removeMessageListener('control-channel-established', controlChannelEstablishedHandler);
info("A control channel is established.");
gScript.sendAsyncMessage('trigger-control-channel-open');
resolve();
});
}),
gScript.addMessageListener('control-channel-closed', function controlChannelClosedHandler(aReason) {
gScript.removeMessageListener('control-channel-closed', controlChannelClosedHandler);
info("The control channel is closed. " + aReason);
});
new Promise((resolve) => {
gScript.addMessageListener('control-channel-opened', function controlChannelOpenedHandler() {
gScript.removeMessageListener('control-channel-opened', controlChannelOpenedHandler);
info("The control channel is opened.");
resolve();
});
}),
gScript.addMessageListener('offer-sent', function offerSentHandler(aIsValid) {
gScript.removeMessageListener('offer-sent', offerSentHandler);
ok(aIsValid, "A valid offer is sent out.");
gScript.sendAsyncMessage('trigger-incoming-transport');
});
new Promise((resolve) => {
gScript.addMessageListener('control-channel-closed', function controlChannelClosedHandler(aReason) {
gScript.removeMessageListener('control-channel-closed', controlChannelClosedHandler);
is(aReason, SpecialPowers.Cr.NS_ERROR_ABORT, "The control channel is closed with NS_ERROR_ABORT");
resolve();
});
}),
gScript.addMessageListener('data-transport-initialized', function dataTransportInitializedHandler() {
gScript.removeMessageListener('data-transport-initialized', dataTransportInitializedHandler);
info("Data transport channel is initialized.");
gScript.sendAsyncMessage('trigger-control-channel-close', SpecialPowers.Cr.NS_ERROR_ABORT);
});
new Promise((resolve) => {
gScript.addMessageListener('offer-sent', function offerSentHandler(aIsValid) {
gScript.removeMessageListener('offer-sent', offerSentHandler);
ok(aIsValid, "A valid offer is sent out.");
gScript.sendAsyncMessage('trigger-incoming-transport');
resolve();
});
}),
gScript.addMessageListener('data-transport-closed', function dataTransportClosedHandler(aReason) {
gScript.removeMessageListener('data-transport-closed', dataTransportClosedHandler);
info("The data transport is closed. " + aReason);
});
new Promise((resolve) => {
gScript.addMessageListener('data-transport-initialized', function dataTransportInitializedHandler() {
gScript.removeMessageListener('data-transport-initialized', dataTransportInitializedHandler);
info("Data transport channel is initialized.");
gScript.sendAsyncMessage('trigger-control-channel-close', SpecialPowers.Cr.NS_ERROR_ABORT);
resolve();
});
}),
new Promise((resolve) => {
gScript.addMessageListener('data-transport-closed', function dataTransportClosedHandler(aReason) {
gScript.removeMessageListener('data-transport-closed', dataTransportClosedHandler);
info("The data transport is closed. " + aReason);
resolve();
});
}),
request.start().then(
function(aConnection) {
is(aConnection.state, "connecting", "The initial state should be connecting.");
aConnection.onclose = function() {
aConnection.onclose = null;
is(aConnection.state, "closed", "Connection should be closed.");
aResolve();
};
return new Promise((resolve) => {
aConnection.onclose = function() {
aConnection.onclose = null;
is(aConnection.state, "closed", "Connection should be closed.");
resolve();
};
});
},
function(aError) {
ok(false, "Error occurred when establishing a connection: " + aError);
teardown();
aReject();
}
);
});
),
]);
}
function testStartConnectionUnexpectedControlChannelCloseNoReasonBeforeDataTransportReady() {
return new Promise(function(aResolve, aReject) {
gScript.addMessageListener('device-prompt', function devicePromptHandler() {
gScript.removeMessageListener('device-prompt', devicePromptHandler);
info("Device prompt is triggered.");
gScript.sendAsyncMessage('trigger-device-prompt-select');
});
info('--- testStartConnectionUnexpectedControlChannelCloseNoReasonBeforeDataTransportReady -- ');
return Promise.all([
gScript.addMessageListener('control-channel-established', function controlChannelEstablishedHandler() {
gScript.removeMessageListener('control-channel-established', controlChannelEstablishedHandler);
info("A control channel is established.");
gScript.sendAsyncMessage('trigger-control-channel-open');
});
new Promise((resolve) => {
gScript.addMessageListener('device-prompt', function devicePromptHandler() {
gScript.removeMessageListener('device-prompt', devicePromptHandler);
info("Device prompt is triggered.");
gScript.sendAsyncMessage('trigger-device-prompt-select');
resolve();
});
}),
gScript.addMessageListener('control-channel-opened', function controlChannelOpenedHandler(aReason) {
gScript.removeMessageListener('control-channel-opened', controlChannelOpenedHandler);
info("The control channel is opened.");
});
new Promise((resolve) => {
gScript.addMessageListener('control-channel-established', function controlChannelEstablishedHandler() {
gScript.removeMessageListener('control-channel-established', controlChannelEstablishedHandler);
info("A control channel is established.");
gScript.sendAsyncMessage('trigger-control-channel-open');
resolve();
});
}),
gScript.addMessageListener('control-channel-closed', function controlChannelClosedHandler(aReason) {
gScript.removeMessageListener('control-channel-closed', controlChannelClosedHandler);
info("The control channel is closed. " + aReason);
});
new Promise((resolve) => {
gScript.addMessageListener('control-channel-opened', function controlChannelOpenedHandler() {
gScript.removeMessageListener('control-channel-opened', controlChannelOpenedHandler);
info("The control channel is opened.");
resolve();
});
}),
gScript.addMessageListener('offer-sent', function offerSentHandler(aIsValid) {
gScript.removeMessageListener('offer-sent', offerSentHandler);
ok(aIsValid, "A valid offer is sent out.");
gScript.sendAsyncMessage('trigger-incoming-transport');
});
new Promise((resolve) => {
gScript.addMessageListener('control-channel-closed', function controlChannelClosedHandler(aReason) {
gScript.removeMessageListener('control-channel-closed', controlChannelClosedHandler);
info("The control channel is closed. " + aReason);
is(aReason, SpecialPowers.Cr.NS_OK, "The control channel is closed with NS_OK");
resolve();
});
}),
gScript.addMessageListener('data-transport-initialized', function dataTransportInitializedHandler() {
gScript.removeMessageListener('data-transport-initialized', dataTransportInitializedHandler);
info("Data transport channel is initialized.");
gScript.sendAsyncMessage('trigger-control-channel-close', SpecialPowers.Cr.NS_OK);
});
new Promise((resolve) => {
gScript.addMessageListener('offer-sent', function offerSentHandler(aIsValid) {
gScript.removeMessageListener('offer-sent', offerSentHandler);
ok(aIsValid, "A valid offer is sent out.");
gScript.sendAsyncMessage('trigger-incoming-transport');
resolve();
});
}),
gScript.addMessageListener('data-transport-closed', function dataTransportClosedHandler(aReason) {
gScript.removeMessageListener('data-transport-closed', dataTransportClosedHandler);
info("The data transport is closed. " + aReason);
});
new Promise((resolve) => {
gScript.addMessageListener('data-transport-initialized', function dataTransportInitializedHandler() {
gScript.removeMessageListener('data-transport-initialized', dataTransportInitializedHandler);
info("Data transport channel is initialized.");
gScript.sendAsyncMessage('trigger-control-channel-close', SpecialPowers.Cr.NS_OK);
resolve();
});
}),
new Promise((resolve) => {
gScript.addMessageListener('data-transport-closed', function dataTransportClosedHandler(aReason) {
gScript.removeMessageListener('data-transport-closed', dataTransportClosedHandler);
info("The data transport is closed. " + aReason);
resolve();
});
}),
request.start().then(
function(aConnection) {
is(aConnection.state, "connecting", "The initial state should be connecting.");
aConnection.onclose = function() {
aConnection.onclose = null;
is(aConnection.state, "closed", "Connection should be closed.");
aResolve();
};
return new Promise((resolve) => {
aConnection.onclose = function() {
aConnection.onclose = null;
is(aConnection.state, "closed", "Connection should be closed.");
resolve();
};
});
},
function(aError) {
ok(false, "Error occurred when establishing a connection: " + aError);
teardown();
aReject();
}
);
});
),
]);
}
function testStartConnectionUnexpectedDataTransportClose() {
return new Promise(function(aResolve, aReject) {
gScript.addMessageListener('device-prompt', function devicePromptHandler() {
gScript.removeMessageListener('device-prompt', devicePromptHandler);
info("Device prompt is triggered.");
gScript.sendAsyncMessage('trigger-device-prompt-select');
});
info('--- testStartConnectionUnexpectedDataTransportClose ---');
return Promise.all([
gScript.addMessageListener('control-channel-established', function controlChannelEstablishedHandler() {
gScript.removeMessageListener('control-channel-established', controlChannelEstablishedHandler);
info("A control channel is established.");
gScript.sendAsyncMessage('trigger-control-channel-open');
});
new Promise((resolve) => {
gScript.addMessageListener('device-prompt', function devicePromptHandler() {
gScript.removeMessageListener('device-prompt', devicePromptHandler);
info("Device prompt is triggered.");
gScript.sendAsyncMessage('trigger-device-prompt-select');
resolve();
});
}),
gScript.addMessageListener('control-channel-opened', function controlChannelOpenedHandler(aReason) {
gScript.removeMessageListener('control-channel-opened', controlChannelOpenedHandler);
info("The control channel is opened.");
});
new Promise((resolve) => {
gScript.addMessageListener('control-channel-established', function controlChannelEstablishedHandler() {
gScript.removeMessageListener('control-channel-established', controlChannelEstablishedHandler);
info("A control channel is established.");
gScript.sendAsyncMessage('trigger-control-channel-open');
resolve();
});
}),
gScript.addMessageListener('control-channel-closed', function controlChannelClosedHandler(aReason) {
gScript.removeMessageListener('control-channel-closed', controlChannelClosedHandler);
info("The control channel is closed. " + aReason);
});
new Promise((resolve) => {
gScript.addMessageListener('control-channel-opened', function controlChannelOpenedHandler() {
gScript.removeMessageListener('control-channel-opened', controlChannelOpenedHandler);
info("The control channel is opened.");
resolve();
});
}),
gScript.addMessageListener('offer-sent', function offerSentHandler(aIsValid) {
gScript.removeMessageListener('offer-sent', offerSentHandler);
ok(aIsValid, "A valid offer is sent out.");
gScript.sendAsyncMessage('trigger-incoming-transport');
});
new Promise((resolve) => {
gScript.addMessageListener('control-channel-closed', function controlChannelClosedHandler(aReason) {
gScript.removeMessageListener('control-channel-closed', controlChannelClosedHandler);
info("The control channel is closed. " + aReason);
resolve();
});
}),
gScript.addMessageListener('data-transport-initialized', function dataTransportInitializedHandler() {
gScript.removeMessageListener('data-transport-initialized', dataTransportInitializedHandler);
info("Data transport channel is initialized.");
gScript.sendAsyncMessage('trigger-data-transport-close', SpecialPowers.Cr.NS_ERROR_UNEXPECTED);
});
new Promise((resolve) => {
gScript.addMessageListener('offer-sent', function offerSentHandler(aIsValid) {
gScript.removeMessageListener('offer-sent', offerSentHandler);
ok(aIsValid, "A valid offer is sent out.");
info("recv offer-sent.");
gScript.sendAsyncMessage('trigger-incoming-transport');
resolve();
});
}),
gScript.addMessageListener('data-transport-closed', function dataTransportClosedHandler(aReason) {
gScript.removeMessageListener('data-transport-closed', dataTransportClosedHandler);
info("The data transport is closed. " + aReason);
});
new Promise((resolve) => {
gScript.addMessageListener('data-transport-initialized', function dataTransportInitializedHandler() {
gScript.removeMessageListener('data-transport-initialized', dataTransportInitializedHandler);
info("Data transport channel is initialized.");
gScript.sendAsyncMessage('trigger-data-transport-close', SpecialPowers.Cr.NS_ERROR_UNEXPECTED);
resolve();
});
}),
new Promise((resolve) => {
gScript.addMessageListener('data-transport-closed', function dataTransportClosedHandler(aReason) {
gScript.removeMessageListener('data-transport-closed', dataTransportClosedHandler);
info("The data transport is closed. " + aReason);
resolve();
});
}),
request.start().then(
function(aConnection) {
is(aConnection.state, "connecting", "The initial state should be connecting.");
aConnection.onclose = function() {
aConnection.onclose = null;
is(aConnection.state, "closed", "Connection should be closed.");
aResolve();
};
return new Promise((resolve) => {
aConnection.onclose = function() {
aConnection.onclose = null;
is(aConnection.state, "closed", "Connection should be closed.");
resolve();
};
});
},
function(aError) {
ok(false, "Error occurred when establishing a connection: " + aError);
teardown();
aReject();
}
);
});
),
]);
}
function teardown() {

Просмотреть файл

@ -488,4 +488,41 @@ H264::ComputeMaxRefFrames(const mozilla::MediaByteBuffer* aExtraData)
return maxRefFrames;
}
/* static */ H264::FrameType
H264::GetFrameType(const mozilla::MediaRawData* aSample)
{
if (!AnnexB::IsAVCC(aSample)) {
// We must have a valid AVCC frame with extradata.
return FrameType::INVALID;
}
MOZ_ASSERT(aSample->Data());
int nalLenSize = ((*aSample->mExtraData)[4] & 3) + 1;
ByteReader reader(aSample->Data(), aSample->Size());
while (reader.Remaining() >= nalLenSize) {
uint32_t nalLen;
switch (nalLenSize) {
case 1: nalLen = reader.ReadU8(); break;
case 2: nalLen = reader.ReadU16(); break;
case 3: nalLen = reader.ReadU24(); break;
case 4: nalLen = reader.ReadU32(); break;
}
if (!nalLen) {
continue;
}
const uint8_t* p = reader.Read(nalLen);
if (!p) {
return FrameType::INVALID;
}
if ((p[0] & 0x1f) == 5) {
// IDR NAL.
return FrameType::I_FRAME;
}
}
return FrameType::OTHER;
}
} // namespace mp4_demuxer

Просмотреть файл

@ -350,6 +350,17 @@ public:
// clamped to be in the range of [4, 16]; otherwise return 4.
static uint32_t ComputeMaxRefFrames(const mozilla::MediaByteBuffer* aExtraData);
enum class FrameType
{
I_FRAME,
OTHER,
INVALID,
};
// Returns the frame type. Returns I_FRAME if the sample is an IDR
// (Instantaneous Decoding Refresh) Picture.
static FrameType GetFrameType(const mozilla::MediaRawData* aSample);
private:
static void vui_parameters(BitReader& aBr, SPSData& aDest);
// Read HRD parameters, all data is ignored.

Просмотреть файл

@ -140,6 +140,7 @@ public class Tab {
mPluginViews = new ArrayList<View>();
mState = shouldShowProgress(url) ? STATE_LOADING : STATE_SUCCESS;
mLoadProgress = LOAD_PROGRESS_INIT;
mIconRequestBuilder = Icons.with(mAppContext).pageUrl(mUrl);
updateBookmark();
}
@ -452,13 +453,6 @@ public class Tab {
return;
}
if (mIconRequestBuilder == null) {
// For the first internal homepage we might want to load a favicon without ever receiving
// a location change event first. In this case we didn't start to build a request yet.
// Let's do that now.
mIconRequestBuilder = Icons.with(mAppContext).pageUrl(mUrl);
}
mRunningIconRequest = mIconRequestBuilder
.build()
.execute(new IconCallback() {

Просмотреть файл

@ -312,6 +312,7 @@
@BINPATH@/components/addonManager.js
@BINPATH@/components/amContentHandler.js
@BINPATH@/components/amInstallTrigger.js
@BINPATH@/components/amWebAPI.js
@BINPATH@/components/amWebInstallListener.js
@BINPATH@/components/nsBlocklistService.js
#ifndef RELEASE_BUILD

Просмотреть файл

@ -122,7 +122,6 @@ UNIFIED_SOURCES += [
'nsPKCS11Slot.cpp',
'nsPKCS12Blob.cpp',
'nsProtectedAuthThread.cpp',
'nsPSMBackgroundThread.cpp',
'nsRandomGenerator.cpp',
'nsSecureBrowserUIImpl.cpp',
'nsSecurityHeaderParser.cpp',

Просмотреть файл

@ -1,90 +0,0 @@
/* 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 "nsPSMBackgroundThread.h"
#include "nsThreadUtils.h"
using namespace mozilla;
void nsPSMBackgroundThread::nsThreadRunner(void *arg)
{
nsPSMBackgroundThread *self = static_cast<nsPSMBackgroundThread *>(arg);
PR_SetCurrentThreadName(self->mName.BeginReading());
self->Run();
}
nsPSMBackgroundThread::nsPSMBackgroundThread()
: mThreadHandle(nullptr),
mMutex("nsPSMBackgroundThread.mMutex"),
mCond(mMutex, "nsPSMBackgroundThread.mCond"),
mExitState(ePSMThreadRunning)
{
}
nsresult nsPSMBackgroundThread::startThread(const nsCSubstring & name)
{
mName = name;
mThreadHandle = PR_CreateThread(PR_USER_THREAD, nsThreadRunner, static_cast<void*>(this),
PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0);
NS_ASSERTION(mThreadHandle, "Could not create nsPSMBackgroundThread\n");
if (!mThreadHandle)
return NS_ERROR_OUT_OF_MEMORY;
return NS_OK;
}
nsPSMBackgroundThread::~nsPSMBackgroundThread()
{
}
bool
nsPSMBackgroundThread::exitRequested(const MutexAutoLock & /*proofOfLock*/) const
{
return exitRequestedNoLock();
}
nsresult
nsPSMBackgroundThread::postStoppedEventToMainThread(
MutexAutoLock const & /*proofOfLock*/)
{
NS_ASSERTION(PR_GetCurrentThread() == mThreadHandle,
"Background thread stopped from another thread");
mExitState = ePSMThreadStopped;
// requestExit is waiting for an event, so give it one.
return NS_DispatchToMainThread(new Runnable());
}
void nsPSMBackgroundThread::requestExit()
{
NS_ASSERTION(NS_IsMainThread(),
"nsPSMBackgroundThread::requestExit called off main thread.");
if (!mThreadHandle)
return;
{
MutexAutoLock threadLock(mMutex);
if (mExitState < ePSMThreadStopRequested) {
mExitState = ePSMThreadStopRequested;
mCond.NotifyAll();
}
}
nsCOMPtr<nsIThread> mainThread = do_GetCurrentThread();
for (;;) {
{
MutexAutoLock threadLock(mMutex);
if (mExitState == ePSMThreadStopped)
break;
}
NS_ProcessPendingEvents(mainThread, PR_MillisecondsToInterval(50));
}
PR_JoinThread(mThreadHandle);
mThreadHandle = nullptr;
}

Просмотреть файл

@ -1,56 +0,0 @@
/* 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 _NSPSMBACKGROUNDTHREAD_H_
#define _NSPSMBACKGROUNDTHREAD_H_
#include "nspr.h"
#include "nscore.h"
#include "mozilla/CondVar.h"
#include "mozilla/Mutex.h"
#include "nsNSSComponent.h"
class nsPSMBackgroundThread
{
protected:
static void nsThreadRunner(void *arg);
virtual void Run(void) = 0;
// used to join the thread
PRThread *mThreadHandle;
// Shared mutex used for condition variables,
// and to protect access to mExitState.
// Derived classes may use it to protect additional
// resources.
mozilla::Mutex mMutex;
// Used to signal the thread's Run loop when a job is added
// and/or exit is requested.
mozilla::CondVar mCond;
bool exitRequested(::mozilla::MutexAutoLock const & proofOfLock) const;
bool exitRequestedNoLock() const { return mExitState != ePSMThreadRunning; }
nsresult postStoppedEventToMainThread(::mozilla::MutexAutoLock const & proofOfLock);
private:
enum {
ePSMThreadRunning = 0,
ePSMThreadStopRequested = 1,
ePSMThreadStopped = 2
} mExitState;
// The thread's name.
nsCString mName;
public:
nsPSMBackgroundThread();
virtual ~nsPSMBackgroundThread();
nsresult startThread(const nsCSubstring & name);
void requestExit();
};
#endif

Просмотреть файл

@ -150,7 +150,7 @@
"Entrust Root Certification Authority - EC1",
"Entrust Root Certification Authority - G2",
"Entrust.net Premium 2048 Secure Server CA",
"Equifax Secure CA",
// "Equifax Secure Certificate Authority",
"GeoTrust Global CA",
"GeoTrust Global CA 2",
"GeoTrust Primary Certification Authority",

Просмотреть файл

@ -57,14 +57,31 @@ function downloadRoots() {
return roots;
}
function makeFormattedNickname(cert) {
if (cert.nickname.startsWith("Builtin Object Token:")) {
return `"${cert.nickname.substring("Builtin Object Token:".length)}"`;
}
// Otherwise, this isn't a built-in and we have to comment it out.
if (cert.commonName) {
return `// "${cert.commonName}"`;
}
if (cert.organizationalUnit) {
return `// "${cert.organizationalUnit}"`;
}
if (cert.organization) {
return `// "${cert.organization}"`;
}
throw new Error(`couldn't make nickname for ${cert.subjectName}`);
}
var roots = downloadRoots();
var rootNicknames = [];
for (var root of roots) {
rootNicknames.push(root.nickname.substring("Builtin Object Token:".length));
rootNicknames.push(makeFormattedNickname(root));
}
rootNicknames.sort(function(rootA, rootB) {
let rootALowercase = rootA.toLowerCase();
let rootBLowercase = rootB.toLowerCase();
let rootALowercase = rootA.toLowerCase().replace(/(^[^"]*")|"/g, "");
let rootBLowercase = rootB.toLowerCase().replace(/(^[^"]*")|"/g, "");
if (rootALowercase < rootBLowercase) {
return -1;
}
@ -82,7 +99,7 @@ for (var nickname of rootNicknames) {
dump(",\n");
}
first = false;
dump(" \"" + nickname + "\"");
dump(" " + nickname);
}
dump("\n");
dump(" ]\n");

Просмотреть файл

@ -20,7 +20,7 @@
this.EXPORTED_SYMBOLS = ["loadKinto"];
/*
* Version 4.0.3 - 8100433
* Version 4.0.4 - 03f82da
*/
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.loadKinto = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
@ -1805,15 +1805,7 @@ class Collection {
});
}).then(syncResultObject => {
syncResultObject.lastModified = changeObject.lastModified;
// Don't persist lastModified value if any conflict or error occured
if (!syncResultObject.ok) {
return syncResultObject;
}
// No conflict occured, persist collection's lastModified value
return this.db.saveLastModified(syncResultObject.lastModified).then(lastModified => {
this._lastModified = lastModified;
return syncResultObject;
});
return syncResultObject;
});
}
@ -2216,7 +2208,18 @@ class Collection {
// Avoid redownloading our own changes during the last pull.
const pullOpts = _extends({}, options, { exclude: result.published });
return this.pullChanges(client, result, pullOpts);
}).then(syncResultObject => {
// Don't persist lastModified value if any conflict or error occured
if (!syncResultObject.ok) {
return syncResultObject;
}
// No conflict occured, persist collection's lastModified value
return this.db.saveLastModified(syncResultObject.lastModified).then(lastModified => {
this._lastModified = lastModified;
return syncResultObject;
});
});
// Ensure API default remote is reverted if a custom one's been used
return (0, _utils.pFinally)(syncPromise, () => this.api.remote = previousRemote);
}

Просмотреть файл

@ -0,0 +1,201 @@
/* 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/. */
"use strict";
const Cu = Components.utils;
Cu.import("resource://services-sync/record.js");
Cu.import("resource://services-sync/util.js");
Cu.import("resource://services-sync/bookmark_utils.js");
Cu.import("resource://services-common/async.js");
Cu.import("resource://services-sync/main.js");
this.EXPORTED_SYMBOLS = ["CollectionValidator", "CollectionProblemData"];
class CollectionProblemData {
constructor() {
this.missingIDs = 0;
this.duplicates = [];
this.clientMissing = [];
this.serverMissing = [];
this.serverDeleted = [];
this.serverUnexpected = [];
this.differences = [];
}
/**
* Produce a list summarizing problems found. Each entry contains {name, count},
* where name is the field name for the problem, and count is the number of times
* the problem was encountered.
*
* Validation has failed if all counts are not 0.
*/
getSummary() {
return [
{ name: "clientMissing", count: this.clientMissing.length },
{ name: "serverMissing", count: this.serverMissing.length },
{ name: "serverDeleted", count: this.serverDeleted.length },
{ name: "serverUnexpected", count: this.serverUnexpected.length },
{ name: "differences", count: this.differences.length },
{ name: "missingIDs", count: this.missingIDs },
{ name: "duplicates", count: this.duplicates.length }
];
}
}
class CollectionValidator {
// Construct a generic collection validator. This is intended to be called by
// subclasses.
// - name: Name of the engine
// - idProp: Property that identifies a record. That is, if a client and server
// record have the same value for the idProp property, they should be
// compared against eachother.
// - props: Array of properties that should be compared
constructor(name, idProp, props) {
this.name = name;
this.props = props;
this.idProp = idProp;
}
// Should a custom ProblemData type be needed, return it here.
emptyProblemData() {
return new CollectionProblemData();
}
getServerItems(engine) {
let collection = engine._itemSource();
let collectionKey = engine.service.collectionKeys.keyForCollection(engine.name);
collection.full = true;
let items = [];
collection.recordHandler = function(item) {
item.decrypt(collectionKey);
items.push(item.cleartext);
};
collection.get();
return items;
}
// Should return a promise that resolves to an array of client items.
getClientItems() {
return Promise.reject("Must implement");
}
// Turn the client item into something that can be compared with the server item,
// and is also safe to mutate.
normalizeClientItem(item) {
return Cu.cloneInto(item, {});
}
// Turn the server item into something that can be easily compared with the client
// items.
normalizeServerItem(item) {
return item;
}
// Return whether or not a server item should be present on the client. Expected
// to be overridden.
clientUnderstands(item) {
return true;
}
// Return whether or not a client item should be present on the server. Expected
// to be overridden
syncedByClient(item) {
return true;
}
// Compare the server item and the client item, and return a list of property
// names that are different. Can be overridden if needed.
getDifferences(client, server) {
let differences = [];
for (let prop of this.props) {
let clientProp = client[prop];
let serverProp = server[prop];
if ((clientProp || "") !== (serverProp || "")) {
differences.push(prop);
}
}
return differences;
}
// Returns an object containing
// problemData: an instance of the class returned by emptyProblemData(),
// clientRecords: Normalized client records
// records: Normalized server records,
// deletedRecords: Array of ids that were marked as deleted by the server.
compareClientWithServer(clientItems, serverItems) {
clientItems = clientItems.map(item => this.normalizeClientItem(item));
serverItems = serverItems.map(item => this.normalizeServerItem(item));
let problems = this.emptyProblemData();
let seenServer = new Map();
let serverDeleted = new Set();
let allRecords = new Map();
for (let record of serverItems) {
let id = record[this.idProp];
if (!id) {
++problems.missingIDs;
continue;
}
if (record.deleted) {
serverDeleted.add(record);
} else {
let possibleDupe = seenServer.get(id);
if (possibleDupe) {
problems.duplicates.push(id);
} else {
seenServer.set(id, record);
allRecords.set(id, { server: record, client: null, });
}
record.understood = this.clientUnderstands(record);
}
}
let recordPairs = [];
let seenClient = new Map();
for (let record of clientItems) {
let id = record[this.idProp];
record.shouldSync = this.syncedByClient(record);
seenClient.set(id, record);
let combined = allRecords.get(id);
if (combined) {
combined.client = record;
} else {
allRecords.set(id, { client: record, server: null });
}
}
for (let [id, { server, client }] of allRecords) {
if (!client && !server) {
throw new Error("Impossible: no client or server record for " + id);
} else if (server && !client) {
if (server.understood) {
problems.clientMissing.push(id);
}
} else if (client && !server) {
if (client.shouldSync) {
problems.serverMissing.push(id);
}
} else {
if (!client.shouldSync) {
if (!problems.serverUnexpected.includes(id)) {
problems.serverUnexpected.push(id);
}
continue;
}
let differences = this.getDifferences(client, server);
if (differences && differences.length) {
problems.differences.push({ id, differences });
}
}
}
return {
problemData: problems,
clientRecords: clientItems,
records: serverItems,
deletedRecords: [...serverDeleted]
};
}
}

Просмотреть файл

@ -2,12 +2,13 @@
* 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/. */
this.EXPORTED_SYMBOLS = ['PasswordEngine', 'LoginRec'];
this.EXPORTED_SYMBOLS = ['PasswordEngine', 'LoginRec', 'PasswordValidator'];
var {classes: Cc, interfaces: Ci, utils: Cu} = Components;
Cu.import("resource://services-sync/record.js");
Cu.import("resource://services-sync/constants.js");
Cu.import("resource://services-sync/collection_validator.js");
Cu.import("resource://services-sync/engines.js");
Cu.import("resource://services-sync/util.js");
Cu.import("resource://services-common/async.js");
@ -325,3 +326,46 @@ PasswordTracker.prototype = {
}
},
};
class PasswordValidator extends CollectionValidator {
constructor() {
super("passwords", "id", [
"hostname",
"formSubmitURL",
"httpRealm",
"password",
"passwordField",
"username",
"usernameField",
]);
}
getClientItems() {
let logins = Services.logins.getAllLogins({});
let syncHosts = Utils.getSyncCredentialsHosts()
let result = logins.map(l => l.QueryInterface(Ci.nsILoginMetaInfo))
.filter(l => !syncHosts.has(l.hostname));
return Promise.resolve(result);
}
normalizeClientItem(item) {
return {
id: item.guid,
guid: item.guid,
hostname: item.hostname,
formSubmitURL: item.formSubmitURL,
httpRealm: item.httpRealm,
password: item.password,
passwordField: item.passwordField,
username: item.username,
usernameField: item.usernameField,
original: item,
}
}
normalizeServerItem(item) {
return Object.assign({ guid: item.id }, item);
}
}

Просмотреть файл

@ -22,6 +22,7 @@ EXTRA_JS_MODULES['services-sync'] += [
'modules/bookmark_utils.js',
'modules/bookmark_validator.js',
'modules/browserid_identity.js',
'modules/collection_validator.js',
'modules/engines.js',
'modules/FxaMigrator.jsm',
'modules/identity.js',

Просмотреть файл

@ -0,0 +1,158 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
Components.utils.import("resource://services-sync/engines/passwords.js");
function getDummyServerAndClient() {
return {
server: [
{
id: "11111",
guid: "11111",
hostname: "https://www.11111.com",
formSubmitURL: "https://www.11111.com/login",
password: "qwerty123",
passwordField: "pass",
username: "foobar",
usernameField: "user",
httpRealm: null,
},
{
id: "22222",
guid: "22222",
hostname: "https://www.22222.org",
formSubmitURL: "https://www.22222.org/login",
password: "hunter2",
passwordField: "passwd",
username: "baz12345",
usernameField: "user",
httpRealm: null,
},
{
id: "33333",
guid: "33333",
hostname: "https://www.33333.com",
formSubmitURL: "https://www.33333.com/login",
password: "p4ssw0rd",
passwordField: "passwad",
username: "quux",
usernameField: "user",
httpRealm: null,
},
],
client: [
{
id: "11111",
guid: "11111",
hostname: "https://www.11111.com",
formSubmitURL: "https://www.11111.com/login",
password: "qwerty123",
passwordField: "pass",
username: "foobar",
usernameField: "user",
httpRealm: null,
},
{
id: "22222",
guid: "22222",
hostname: "https://www.22222.org",
formSubmitURL: "https://www.22222.org/login",
password: "hunter2",
passwordField: "passwd",
username: "baz12345",
usernameField: "user",
httpRealm: null,
},
{
id: "33333",
guid: "33333",
hostname: "https://www.33333.com",
formSubmitURL: "https://www.33333.com/login",
password: "p4ssw0rd",
passwordField: "passwad",
username: "quux",
usernameField: "user",
httpRealm: null,
}
]
};
}
add_test(function test_valid() {
let { server, client } = getDummyServerAndClient();
let validator = new PasswordValidator();
let { problemData, clientRecords, records, deletedRecords } =
validator.compareClientWithServer(client, server);
equal(clientRecords.length, 3);
equal(records.length, 3)
equal(deletedRecords.length, 0);
deepEqual(problemData, validator.emptyProblemData());
run_next_test();
});
add_test(function test_missing() {
let validator = new PasswordValidator();
{
let { server, client } = getDummyServerAndClient();
client.pop();
let { problemData, clientRecords, records, deletedRecords } =
validator.compareClientWithServer(client, server);
equal(clientRecords.length, 2);
equal(records.length, 3)
equal(deletedRecords.length, 0);
let expected = validator.emptyProblemData();
expected.clientMissing.push("33333");
deepEqual(problemData, expected);
}
{
let { server, client } = getDummyServerAndClient();
server.pop();
let { problemData, clientRecords, records, deletedRecords } =
validator.compareClientWithServer(client, server);
equal(clientRecords.length, 3);
equal(records.length, 2)
equal(deletedRecords.length, 0);
let expected = validator.emptyProblemData();
expected.serverMissing.push("33333");
deepEqual(problemData, expected);
}
run_next_test();
});
add_test(function test_deleted() {
let { server, client } = getDummyServerAndClient();
let deletionRecord = { id: "444444", guid: "444444", deleted: true };
server.push(deletionRecord);
let validator = new PasswordValidator();
let { problemData, clientRecords, records, deletedRecords } =
validator.compareClientWithServer(client, server);
equal(clientRecords.length, 3);
equal(records.length, 4);
deepEqual(deletedRecords, [deletionRecord]);
let expected = validator.emptyProblemData();
deepEqual(problemData, expected);
run_next_test();
});
function run_test() {
run_next_test();
}

Просмотреть файл

@ -170,6 +170,7 @@ skip-if = debug
skip-if = debug
[test_places_guid_downgrade.js]
[test_password_store.js]
[test_password_validator.js]
[test_password_tracker.js]
# Too many intermittent "ASSERTION: thread pool wasn't shutdown: '!mPool'" (bug 804479)
skip-if = debug

Просмотреть файл

@ -24,6 +24,7 @@ Cu.import("resource://services-sync/constants.js");
Cu.import("resource://services-sync/main.js");
Cu.import("resource://services-sync/util.js");
Cu.import("resource://services-sync/bookmark_validator.js");
Cu.import("resource://services-sync/engines/passwords.js");
// TPS modules
Cu.import("resource://tps/logger.jsm");
@ -112,6 +113,7 @@ var TPS = {
_usSinceEpoch: 0,
_requestedQuit: false,
shouldValidateBookmarks: false,
shouldValidatePasswords: false,
_init: function TPS__init() {
// Check if Firefox Accounts is enabled
@ -416,6 +418,7 @@ var TPS = {
},
HandlePasswords: function (passwords, action) {
this.shouldValidatePasswords = true;
try {
for (let password of passwords) {
let password_id = -1;
@ -656,14 +659,47 @@ var TPS = {
Logger.logInfo("Bookmark validation finished");
},
ValidatePasswords() {
let serverRecordDumpStr;
try {
Logger.logInfo("About to perform password validation");
let pwEngine = Weave.Service.engineManager.get("passwords");
let validator = new PasswordValidator();
let serverRecords = validator.getServerItems(pwEngine);
let clientRecords = Async.promiseSpinningly(validator.getClientItems());
serverRecordDumpStr = JSON.stringify(serverRecords);
let { problemData } = validator.compareClientWithServer(clientRecords, serverRecords);
for (let { name, count } of problemData.getSummary()) {
if (count) {
Logger.logInfo(`Validation problem: "${name}": ${JSON.stringify(problemData[name])}`);
}
Logger.AssertEqual(count, 0, `Password validation error of type ${name}`);
}
} catch (e) {
// Dump the client records (should always be doable)
DumpPasswords();
// Dump the server records if gotten them already.
if (serverRecordDumpStr) {
Logger.logInfo("Server password records:\n" + serverRecordDumpStr + "\n");
}
this.DumpError("Password validation failed", e);
}
Logger.logInfo("Password validation finished");
},
RunNextTestAction: function() {
try {
if (this._currentAction >=
this._phaselist[this._currentPhase].length) {
// Run necessary validations and then finish up
if (this.shouldValidateBookmarks) {
// Run bookmark validation and then finish up
this.ValidateBookmarks();
}
if (this.shouldValidatePasswords) {
this.ValidatePasswords();
}
// we're all done
Logger.logInfo("test phase " + this._currentPhase + ": " +
(this._errors ? "FAIL" : "PASS"));
@ -1100,6 +1136,9 @@ var Passwords = {
},
verifyNot: function Passwords__verifyNot(passwords) {
this.HandlePasswords(passwords, ACTION_VERIFY_NOT);
},
skipValidation() {
TPS.shouldValidatePasswords = false;
}
};

Просмотреть файл

@ -25,6 +25,7 @@ task:
scopes:
- 'docker-worker:cache:level-{{level}}-hg-shared'
- 'docker-worker:cache:level-{{level}}-checkouts'
- 'secrets:get:project/taskcluster/gecko/hgfingerprint'
payload:
# Thirty minutes should be enough for lint checks
@ -39,6 +40,9 @@ task:
GECKO_HEAD_REPOSITORY: '{{head_repository}}'
GECKO_HEAD_REV: '{{head_rev}}'
features:
taskclusterProxy: true
extra:
build_product: '{{build_product}}'
build_name: {{build_name}}

Просмотреть файл

@ -19,11 +19,16 @@ import argparse
import datetime
import errno
import grp
import json
import os
import pwd
import re
import subprocess
import sys
import urllib2
FINGERPRINT_URL = 'http://taskcluster/secrets/v1/secret/project/taskcluster/gecko/hgfingerprint'
def print_line(prefix, m):
@ -88,8 +93,28 @@ def vcs_checkout(args):
print('revision is not specified for checkout')
sys.exit(1)
# Obtain certificate fingerprints.
try:
print_line(b'vcs', 'fetching hg.mozilla.org fingerprint from %s\n' %
FINGERPRINT_URL)
res = urllib2.urlopen(FINGERPRINT_URL, timeout=10)
secret = res.read()
except urllib2.URLError as e:
print('error retrieving hg fingerprint: %s' % e)
sys.exit(1)
try:
secret = json.loads(secret, encoding='utf-8')
except ValueError:
print('invalid JSON in hg fingerprint secret')
sys.exit(1)
hgmo_fingerprint = secret['secret']['fingerprints'].encode('ascii')
res = run_and_prefix_output(b'vcs', [
b'/usr/bin/hg', b'robustcheckout',
b'/usr/bin/hg',
b'--config', b'hostsecurity.hg.mozilla.org:fingerprints=%s' % hgmo_fingerprint,
b'robustcheckout',
b'--sharebase', b'/home/worker/hg-shared',
b'--purge',
b'--upstream', base_repo,

Просмотреть файл

@ -0,0 +1,14 @@
[mediasource-sequencemode-append-buffer.html]
type: testharness
prefs: [media.mediasource.enabled:true]
[Test sequence AppendMode appendBuffer(first media segment)]
expected:
if (os == "win") and (version == "5.1.2600"): FAIL
[Test sequence AppendMode appendBuffer(second media segment)]
expected:
if (os == "win") and (version == "5.1.2600"): FAIL
[Test sequence AppendMode appendBuffer(second media segment, then first media segment)]
expected: FAIL

Просмотреть файл

@ -261,8 +261,8 @@
removeAppendedDataTests(function(test, mediaSource, sourceBuffer, duration, subType, segmentInfo)
{
var expectations = {
webm: ("{ [3.187, " + duration + ") }"),
mp4: ("{ [3.187, " + duration + ") }"),
webm: ("{ [3.315, " + duration + ") }"),
mp4: ("{ [3.298, " + duration + ") }"),
};
// Note: Range doesn't start exactly at the end of the remove range because there isn't
@ -274,8 +274,8 @@
{
var start = Math.max(segmentInfo.media[0].timev, segmentInfo.media[0].timea).toFixed(3);
var expectations = {
webm: ("{ [" + start + ", 1.012) [3.187, " + duration + ") }"),
mp4: ("{ [" + start + ", 0.996) [3.187, " + duration + ") }"),
webm: ("{ [" + start + ", 1.005) [3.315, " + duration + ") }"),
mp4: ("{ [" + start + ", 0.997) [3.298, " + duration + ") }"),
};
// Note: The first resulting range ends slightly after start because the removal algorithm only removes
@ -288,7 +288,7 @@
{
var start = Math.max(segmentInfo.media[0].timev, segmentInfo.media[0].timea).toFixed(3);
var expectations = {
webm: "{ [" + start + ", 1.029) }",
webm: "{ [" + start + ", 1.013) }",
mp4: "{ [" + start + ", 1.022) }",
};

Просмотреть файл

@ -60,7 +60,7 @@
// Prior to EOS, the buffered range end time may not have fully reached the next media
// segment's timecode (adjusted by any timestampOffset). It should not exceed it though.
// Therefore, an exact assertBufferedEquals() will not work here.
assert_equals(sourceBuffer.buffered.length, 1, "sourceBuffer.buffered has 1 range before EOS");
assert_greater_than(sourceBuffer.buffered.length, 0, "sourceBuffer.buffered has at least 1 range before EOS");
assert_equals(threeDecimalPlaces(sourceBuffer.buffered.start(0)),
threeDecimalPlaces(expectedBufferedRangeStartTime),
"sourceBuffer.buffered range begins where expected before EOS");

Просмотреть файл

@ -1,63 +1,38 @@
(function(window) {
var SEGMENT_INFO_LIST = [
{
url: 'mp4/test.mp4',
type: 'video/mp4; codecs="mp4a.40.2,avc1.4d400d"',
duration: 6.0756,
init: { offset: 0, size: 1197 },
duration: 6.549,
init: { offset: 0, size: 1413 },
media: [
{ offset: 1241, size: 17845, timev: 0.033200, timea: 0, endtimev: 0.531200, endtimea: 0.510839 },
{ offset: 19130, size: 5551, timev: 0.464800, timea: 0.510839, endtimev: 0.796800, endtimea: 0.812698 },
{ offset: 24725, size: 10944, timev: 0.796800, timea: 0.812698, endtimev: 0.929600, endtimea: 0.905578 },
{ offset: 35713, size: 7131, timev: 0.863200, timea: 0.905578, endtimev: 1.195200, endtimea: 1.184217 },
{ offset: 42888, size: 2513, timev: 1.128800, timea: 1.184217, endtimev: 1.328000, endtimea: 1.300317 },
{ offset: 45457, size: 3022, timev: 1.261600, timea: 1.300317, endtimev: 1.460800, endtimea: 1.509297 },
{ offset: 48479, size: 815, timev: 1.494000, timea: 1.509297, endtimev: 1.527200, endtimea: 1.532517 },
{ offset: 49338, size: 2818, timev: 1.460800, timea: 1.532517, endtimev: 1.626800, endtimea: 1.648616 },
{ offset: 52200, size: 11581, timev: 1.626800, timea: 1.648616, endtimev: 1.792800, endtimea: 1.764716 },
{ offset: 63825, size: 3003, timev: 1.726400, timea: 1.764716, endtimev: 1.925600, endtimea: 1.973696 },
{ offset: 66872, size: 6390, timev: 1.925600, timea: 1.973696, endtimev: 2.191200, endtimea: 2.159455 },
{ offset: 73306, size: 3740, timev: 2.124800, timea: 2.159455, endtimev: 2.390400, endtimea: 2.368435 },
{ offset: 77102, size: 11779, timev: 2.324000, timea: 2.368435, endtimev: 2.523200, endtimea: 2.577414 },
{ offset: 88881, size: 851, timev: 2.556400, timea: 2.577414, endtimev: 2.589600, endtimea: 2.600634 },
{ offset: 89776, size: 4236, timev: 2.523200, timea: 2.600634, endtimev: 2.788800, endtimea: 2.832834 },
{ offset: 94056, size: 9538, timev: 2.788800, timea: 2.832834, endtimev: 3.187200, endtimea: 3.204353 },
{ offset: 103638, size: 13295, timev: 3.187200, timea: 3.204353, endtimev: 3.452800, endtimea: 3.436553 },
{ offset: 116977, size: 309, timev: 3.386400, timea: 3.436553, endtimev: 3.419600, endtimea: 3.506213 },
{ offset: 117330, size: 5806, timev: 3.452800, timea: 3.506213, endtimev: 3.784800, endtimea: 3.831292 },
{ offset: 123180, size: 4392, timev: 3.784800, timea: 3.831292, endtimev: 4.017200, endtimea: 4.040272 },
{ offset: 127616, size: 15408, timev: 4.017200, timea: 4.040272, endtimev: 4.249600, endtimea: 4.295691 },
{ offset: 143068, size: 9899, timev: 4.249600, timea: 4.295691, endtimev: 4.814000, endtimea: 4.829750 },
{ offset: 153011, size: 11562, timev: 4.814000, timea: 4.829750, endtimev: 4.980000, endtimea: 5.015510 },
{ offset: 164617, size: 7398, timev: 4.980000, timea: 5.015510, endtimev: 5.245600, endtimea: 5.294149 },
{ offset: 172059, size: 5698, timev: 5.245600, timea: 5.294149, endtimev: 5.577600, endtimea: 5.549569 },
{ offset: 177801, size: 11682, timev: 5.511200, timea: 5.549569, endtimev: 5.710400, endtimea: 5.758548 },
{ offset: 189527, size: 3023, timev: 5.710400, timea: 5.758548, endtimev: 5.909600, endtimea: 5.897868 },
{ offset: 192594, size: 5726, timev: 5.843200, timea: 5.897868, endtimev: 6.075600, endtimea: 6.037188 },
{ offset: 1413, size: 24034, timev: 0.095000, timea: 0, endtimev: 0.896666, endtimea: 0.882358 },
{ offset: 25447, size: 21757, timev: 0.896666, timea: 0.882358, endtimev: 1.696666, endtimea: 1.671836 },
{ offset: 47204, size: 23591, timev: 1.696666, timea: 1.671836, endtimev: 2.498333, endtimea: 2.461315 },
{ offset: 70795, size: 22614, timev: 2.498333, timea: 2.461315, endtimev: 3.298333, endtimea: 3.297233 },
{ offset: 93409, size: 18353, timev: 3.298333, timea: 3.297233, endtimev: 4.100000, endtimea: 4.086712},
{ offset: 111762, size: 23935, timev: 4.100000, timea: 4.086712, endtimev: 4.900000, endtimea: 4.876190 },
{ offset: 135697, size: 21911, timev: 4.900000, timea: 4.876190, endtimev: 5.701666, endtimea: 5.665668 },
{ offset: 157608, size: 23776, timev: 5.701666, timea: 5.665668, endtimev: 6.501666, endtimea: 6.501587 },
{ offset: 181384, size: 5843, timev: 6.501666, timea: 6.501587, endtimev: 6.501666, endtimea: 6.501678 },
]
},
{
url: 'webm/test.webm',
type: 'video/webm; codecs="vp8, vorbis"',
duration: 6.042,
init: { offset: 0, size: 4357 },
duration: 6.552,
init: { offset: 0, size: 4116 },
media: [
{ offset: 4357, size: 11830, timev: 0, timea: 0, endtimev: 0.398000, endtimea: 0.384000 },
{ offset: 16187, size: 12588, timev: 0.398000, timea: 0.385000, endtimev: 0.798000, endtimea: 0.779000 },
{ offset: 28775, size: 14588, timev: 0.797000, timea: 0.779000, endtimev: 1.195000, endtimea: 1.174000 },
{ offset: 43363, size: 13023, timev: 1.195000, timea: 1.174000, endtimev: 1.593000, endtimea: 1.592000 },
{ offset: 56386, size: 13127, timev: 1.594000, timea: 1.592000, endtimev: 1.992000, endtimea: 1.988000 },
{ offset: 69513, size: 14456, timev: 1.992000, timea: 1.987000, endtimev: 2.390000, endtimea: 2.381000 },
{ offset: 83969, size: 13458, timev: 2.390000, timea: 2.381000, endtimev: 2.790000, endtimea: 2.776000 },
{ offset: 97427, size: 14566, timev: 2.789000, timea: 2.776000, endtimev: 3.187000, endtimea: 3.171000 },
{ offset: 111993, size: 13201, timev: 3.187000, timea: 3.171000, endtimev: 3.585000, endtimea: 3.565000 },
{ offset: 125194, size: 14061, timev: 3.586000, timea: 3.566000, endtimev: 3.984000, endtimea: 3.960000 },
{ offset: 139255, size: 15353, timev: 3.984000, timea: 3.960000, endtimev: 4.382000, endtimea: 4.378000 },
{ offset: 154608, size: 13618, timev: 4.382000, timea: 4.378000, endtimev: 4.782000, endtimea: 4.773000 },
{ offset: 168226, size: 15094, timev: 4.781000, timea: 4.773000, endtimev: 5.179000, endtimea: 5.169000 },
{ offset: 183320, size: 13069, timev: 5.179000, timea: 5.168000, endtimev: 5.577000, endtimea: 5.562000 },
{ offset: 196389, size: 13788, timev: 5.578000, timea: 5.563000, endtimev: 5.976000, endtimea: 5.957000 },
{ offset: 210177, size: 9009, timev: 5.976000, timea: 5.957000, endtimev: 6.042000, endtimea: 6.050000 },
{ offset: 4116, size: 26583, timev: 0.112000, timea: 0, endtimev: 0.913000, endtimea: 0.912000 },
{ offset: 30699, size: 20555, timev: 0.913000, timea: 0.912000, endtimev: 1.714000, endtimea: 1.701000 },
{ offset: 51254, size: 22668, timev: 1.714000, timea: 1.701000, endtimev: 2.515000, endtimea: 2.514000 },
{ offset: 73922, size: 21943, timev: 2.515000, timea: 2.514000, endtimev: 3.315000, endtimea: 3.303000 },
{ offset: 95865, size: 23015, timev: 3.315000, timea: 3.303000, endtimev: 4.116000, endtimea: 4.093000},
{ offset: 118880, size: 20406, timev: 4.116000, timea: 4.093000, endtimev: 4.917000, endtimea: 4.906000 },
{ offset: 139286, size: 21537, timev: 4.917000, timea: 4.906000, endtimev: 5.718000, endtimea: 5.695000 },
{ offset: 160823, size: 24027, timev: 5.718000, timea: 5.695000, endtimev: 6.519000, endtimea: 6.508000 },
{ offset: 184850, size: 5955, timev: 6.519000, timea: 6.508000, endtimev: 6.577000, endtimea: 6.577000},
],
}
];

Двоичный файл не отображается.

Двоичный файл не отображается.

Просмотреть файл

@ -269,22 +269,22 @@ var ExtensionTestUtils = {
addonManagerStarted: false,
startAddonManager() {
if (this.addonManagerStarted) {
return;
}
this.addonManagerStarted = true;
let appInfo = {};
Cu.import("resource://testing-common/AppInfo.jsm", appInfo);
appInfo.updateAppInfo({
mockAppInfo() {
const {updateAppInfo} = Cu.import("resource://testing-common/AppInfo.jsm", {});
updateAppInfo({
ID: "xpcshell@tests.mozilla.org",
name: "XPCShell",
version: "48",
platformVersion: "48",
});
},
startAddonManager() {
if (this.addonManagerStarted) {
return;
}
this.addonManagerStarted = true;
this.mockAppInfo();
let manager = Cc["@mozilla.org/addons/integration;1"].getService(Ci.nsIObserver)
.QueryInterface(Ci.nsITimerCallback);

Просмотреть файл

@ -2,8 +2,24 @@
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
XPCOMUtils.defineLazyGetter(this, "strBundle", function() {
const stringSvc = Cc["@mozilla.org/intl/stringbundle;1"].getService(Ci.nsIStringBundleService);
return stringSvc.createBundle("chrome://global/locale/extensions.properties");
});
XPCOMUtils.defineLazyModuleGetter(this, "AddonManager",
"resource://gre/modules/AddonManager.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "promptService",
"@mozilla.org/embedcomp/prompt-service;1",
"nsIPromptService");
function _(key, ...args) {
if (args.length) {
return strBundle.formatStringFromName(key, args, args.length);
}
return strBundle.GetStringFromName(key);
}
function installType(addon) {
if (addon.temporarilyInstalled) {
@ -52,11 +68,42 @@ extensions.registerSchemaAPI("management", "addon_parent", context => {
}
resolve(extInfo);
} catch (e) {
reject(e);
} catch (err) {
reject(err);
}
}));
},
uninstallSelf: function(options) {
return new Promise((resolve, reject) => {
if (options && options.showConfirmDialog) {
let message = _("uninstall.confirmation.message", extension.name);
if (options.dialogMessage) {
message = `${options.dialogMessage}\n${message}`;
}
let title = _("uninstall.confirmation.title", extension.name);
let buttonFlags = promptService.BUTTON_POS_0 * promptService.BUTTON_TITLE_IS_STRING +
promptService.BUTTON_POS_1 * promptService.BUTTON_TITLE_IS_STRING;
let button0Title = _("uninstall.confirmation.button-0.label");
let button1Title = _("uninstall.confirmation.button-1.label");
let response = promptService.confirmEx(null, title, message, buttonFlags, button0Title, button1Title, null, null, {value: 0});
if (response == 1) {
return reject({message: "User cancelled uninstall of extension"});
}
}
AddonManager.getAddonByID(extension.id, addon => {
let canUninstall = Boolean(addon.permissions & AddonManager.PERM_CAN_UNINSTALL);
if (!canUninstall) {
return reject({message: "The add-on cannot be uninstalled"});
}
try {
addon.uninstall();
} catch (err) {
return reject(err);
}
});
});
},
},
};
});

Просмотреть файл

@ -76,6 +76,12 @@ extensions.registerSchemaAPI("runtime", "addon_parent", context => {
return context.lastError;
},
getBrowserInfo: function() {
const {name, vendor, version, appBuildID} = Services.appinfo;
const info = {name, vendor, version, buildID: appBuildID};
return Promise.resolve(info);
},
getPlatformInfo: function() {
return Promise.resolve(ExtensionUtils.PlatformInfo);
},

Просмотреть файл

@ -217,7 +217,6 @@
{
"name": "uninstallSelf",
"type": "function",
"unsupported": true,
"description": "Uninstalls the calling extension. Note: This function can be used without requesting the 'management' permission in the manifest.",
"async": "callback",
"parameters": [
@ -230,6 +229,11 @@
"type": "boolean",
"optional": true,
"description": "Whether or not a confirm-uninstall dialog should prompt the user. Defaults to false."
},
"dialogMessage": {
"type": "string",
"optional": true,
"description": "The message to display to a user when being asked to confirm removal of the extension."
}
}
},

Просмотреть файл

@ -89,6 +89,29 @@
}
}
},
{
"id": "BrowserInfo",
"type": "object",
"description": "An object containing information about the current browser.",
"properties": {
"name": {
"type": "string",
"description": "The name of the browser, for example 'Firefox'."
},
"vendor": {
"type": "string",
"description": "The name of the browser vendor, for example 'Mozilla'."
},
"version": {
"type": "string",
"description": "The browser's version, for example '42.0.0' or '0.8.1pre'."
},
"buildID": {
"type": "string",
"description": "The browser's build ID/date, for example '20160101'."
}
}
},
{
"id": "RequestUpdateCheckStatus",
"type": "string",
@ -367,6 +390,25 @@
}
]
},
{
"name": "getBrowserInfo",
"type": "function",
"description": "Returns information about the current browser.",
"async": "callback",
"parameters": [
{
"type": "function",
"name": "callback",
"description": "Called with results",
"parameters": [
{
"name": "browserInfo",
"$ref": "BrowserInfo"
}
]
}
]
},
{
"name": "getPlatformInfo",
"type": "function",

Просмотреть файл

@ -0,0 +1,137 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
Cu.import("resource://gre/modules/AddonManager.jsm");
Cu.import("resource://testing-common/AddonTestUtils.jsm");
Cu.import("resource://testing-common/MockRegistrar.jsm");
const {promiseAddonByID} = AddonTestUtils;
const id = "uninstall_self_test@tests.mozilla.com";
const manifest = {
applications: {
gecko: {
id,
},
},
name: "test extension name",
version: "1.0",
};
const waitForUninstalled = new Promise(resolve => {
const listener = {
onUninstalled: (addon) => {
equal(addon.id, id, "The expected add-on has been uninstalled");
AddonManager.getAddonByID(addon.id, checkedAddon => {
equal(checkedAddon, null, "Add-on no longer exists");
AddonManager.removeAddonListener(listener);
resolve();
});
},
};
AddonManager.addAddonListener(listener);
});
let promptService = {
_response: null,
QueryInterface: XPCOMUtils.generateQI([Ci.nsIPromptService]),
confirmEx: function(...args) {
this._confirmExArgs = args;
return this._response;
},
};
add_task(function* setup() {
let fakePromptService = MockRegistrar.register("@mozilla.org/embedcomp/prompt-service;1", promptService);
do_register_cleanup(() => {
MockRegistrar.unregister(fakePromptService);
});
yield ExtensionTestUtils.startAddonManager();
});
add_task(function* test_management_uninstall_no_prompt() {
function background() {
browser.test.onMessage.addListener(msg => {
browser.management.uninstallSelf();
});
}
let extension = ExtensionTestUtils.loadExtension({
manifest,
background,
useAddonManager: "temporary",
});
yield extension.startup();
let addon = yield promiseAddonByID(id);
notEqual(addon, null, "Add-on is installed");
extension.sendMessage("uninstall");
yield waitForUninstalled;
yield extension.markUnloaded();
Services.obs.notifyObservers(extension.extension.file, "flush-cache-entry", null);
});
add_task(function* test_management_uninstall_prompt_uninstall() {
promptService._response = 0;
function background() {
browser.test.onMessage.addListener(msg => {
browser.management.uninstallSelf({showConfirmDialog: true});
});
}
let extension = ExtensionTestUtils.loadExtension({
manifest,
background,
useAddonManager: "temporary",
});
yield extension.startup();
let addon = yield promiseAddonByID(id);
notEqual(addon, null, "Add-on is installed");
extension.sendMessage("uninstall");
yield waitForUninstalled;
yield extension.markUnloaded();
// Test localization strings
equal(promptService._confirmExArgs[1], `Uninstall ${manifest.name}`);
equal(promptService._confirmExArgs[2],
`The extension “${manifest.name}” is requesting to be uninstalled. What would you like to do?`);
equal(promptService._confirmExArgs[4], "Uninstall");
equal(promptService._confirmExArgs[5], "Keep Installed");
Services.obs.notifyObservers(extension.extension.file, "flush-cache-entry", null);
});
add_task(function* test_management_uninstall_prompt_keep() {
promptService._response = 1;
function background() {
browser.test.onMessage.addListener(msg => {
browser.management.uninstallSelf({showConfirmDialog: true}).then(() => {
browser.test.fail("uninstallSelf rejects when user declines uninstall");
}, error => {
browser.test.assertEq("User cancelled uninstall of extension",
error.message,
"Expected rejection when user declines uninstall");
browser.test.sendMessage("uninstall-rejected");
});
});
}
let extension = ExtensionTestUtils.loadExtension({
manifest,
background,
useAddonManager: "temporary",
});
yield extension.startup();
let addon = yield promiseAddonByID(id);
notEqual(addon, null, "Add-on is installed");
extension.sendMessage("uninstall");
yield extension.awaitMessage("uninstall-rejected");
addon = yield promiseAddonByID(id);
notEqual(addon, null, "Add-on remains installed");
yield extension.unload();
Services.obs.notifyObservers(extension.extension.file, "flush-cache-entry", null);
});

Просмотреть файл

@ -0,0 +1,26 @@
/* 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/. */
"use strict";
add_task(function* setup() {
ExtensionTestUtils.mockAppInfo();
});
add_task(function* test_getBrowserInfo() {
function background() {
browser.runtime.getBrowserInfo().then(info => {
browser.test.assertEq(info.name, "XPCShell", "name is valid");
browser.test.assertEq(info.vendor, "Mozilla", "vendor is Mozilla");
browser.test.assertEq(info.version, "48", "version is correct");
browser.test.assertEq(info.buildID, "20160315", "buildID is correct");
browser.test.notifyPass("runtime.getBrowserInfo");
});
}
const extension = ExtensionTestUtils.loadExtension({background});
yield extension.startup();
yield extension.awaitFinish("runtime.getBrowserInfo");
yield extension.unload();
});

Просмотреть файл

@ -38,11 +38,13 @@ skip-if = release_build
[test_ext_json_parser.js]
[test_ext_localStorage.js]
[test_ext_management.js]
[test_ext_management_uninstall_self.js]
[test_ext_manifest_content_security_policy.js]
[test_ext_manifest_incognito.js]
[test_ext_manifest_minimum_chrome_version.js]
[test_ext_onmessage_removelistener.js]
[test_ext_runtime_connect_no_receiver.js]
[test_ext_runtime_getBrowserInfo.js]
[test_ext_runtime_getPlatformInfo.js]
[test_ext_runtime_sendMessage.js]
[test_ext_runtime_sendMessage_errors.js]

Просмотреть файл

@ -4493,6 +4493,16 @@
"n_buckets": 20,
"description": "Firefox: If the spinner interstitial displays during tab switching, records the time in ms the graphic is visible"
},
"FX_TAB_SWITCH_SPINNER_VISIBLE_LONG_MS": {
"expires_in_version": "54",
"kind": "exponential",
"low": 1000,
"high": 64000,
"n_buckets": 7,
"bug_numbers": [1301104],
"alert_emails": ["mconley@mozilla.com"],
"description": "Firefox: If the spinner interstitial displays during tab switching, records the time in ms the graphic is visible. This probe is similar to FX_TAB_SWITCH_SPINNER_VISIBLE_MS, but is for truly degenerate cases."
},
"FX_TAB_CLICK_MS": {
"expires_in_version": "default",
"kind": "exponential",

Просмотреть файл

@ -401,7 +401,7 @@ PROT_ListManager.prototype.makeUpdateRequest_ = function(updateUrl, tableData) {
// "saving states to HashStore".
let statePrefName = "browser.safebrowsing.provider.google4.state." + listName;
let stateBase64 = this.prefs_.getPref(statePrefName, "");
stateArray.push(stateBase64 ? atob(stateBase64) : "");
stateArray.push(stateBase64);
});
let urlUtils = Cc["@mozilla.org/url-classifier/utils;1"]

Просмотреть файл

@ -54,12 +54,12 @@ interface nsIUrlClassifierUtils : nsISupports
* Make update request for given lists and their states.
*
* @param aListNames An array of list name represented in string.
* @param aState An array of states (in string) for each list.
* @param aState An array of states (encoded in base64 format) for each list.
* @param aCount The array length of aList and aState.
*
* @returns A string to store request. Not null-terminated.
*/
ACString makeUpdateRequestV4([array, size_is(aCount)] in string aListNames,
[array, size_is(aCount)] in string aStates,
[array, size_is(aCount)] in string aStatesBase64,
in uint32_t aCount);
};

Просмотреть файл

@ -100,7 +100,7 @@ typedef FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints Constraints;
static void
InitListUpdateRequest(ThreatType aThreatType,
const char* aState,
const char* aStateBase64,
ListUpdateRequest* aListUpdateRequest)
{
aListUpdateRequest->set_threat_type(aThreatType);
@ -114,8 +114,12 @@ InitListUpdateRequest(ThreatType aThreatType,
aListUpdateRequest->set_allocated_constraints(contraints);
// Only set non-empty state.
if (aState[0] != '\0') {
aListUpdateRequest->set_state(aState);
if (aStateBase64[0] != '\0') {
nsCString stateBinary;
nsresult rv = Base64Decode(nsCString(aStateBase64), stateBinary);
if (NS_SUCCEEDED(rv)) {
aListUpdateRequest->set_state(stateBinary.get());
}
}
}
@ -267,7 +271,7 @@ nsUrlClassifierUtils::GetProtocolVersion(const nsACString& aProvider,
NS_IMETHODIMP
nsUrlClassifierUtils::MakeUpdateRequestV4(const char** aListNames,
const char** aStates,
const char** aStatesBase64,
uint32_t aCount,
nsACString &aRequest)
{
@ -284,7 +288,7 @@ nsUrlClassifierUtils::MakeUpdateRequestV4(const char** aListNames,
continue; // Unknown list name.
}
auto lur = r.mutable_list_update_requests()->Add();
InitListUpdateRequest(static_cast<ThreatType>(threatType), aStates[i], lur);
InitListUpdateRequest(static_cast<ThreatType>(threatType), aStatesBase64[i], lur);
}
// Then serialize.

Просмотреть файл

@ -154,7 +154,7 @@ add_test(function test_partialUpdateV4() {
// test_update_all_tables, this update request should send
// a partial update to the server.
let requestV4 = gUrlUtils.makeUpdateRequestV4([TEST_TABLE_DATA_V4.tableName],
[NEW_CLIENT_STATE],
[btoa(NEW_CLIENT_STATE)],
1);
gExpectedQueryV4 = "&$req=" + btoa(requestV4);
@ -311,24 +311,12 @@ function readFileToString(aFilename) {
return buf;
}
function buildUpdateRequestV4InBase64() {
let request = urlUtils.makeUpdateRequestV4([TEST_TABLE_DATA_V4.tableName],
[""],
1);
return btoa(request);
}
function waitUntilStateSavedToPref(expectedState, callback) {
const STATE_PREF_NAME_PREFIX = 'browser.safebrowsing.provider.google4.state.';
let stateBase64 = '';
try {
// The reason we get pref from 'googpub-phish-proto' instead of
// 'test-phish-proto' is 'googpub-phish-proto' would be returned
// while we look up the list name from SOCIAL_ENGINEERING_PUBLIC.
// See nsUrlClassifierUtils::THREAT_TYPE_CONV_TABLE.
stateBase64 =
prefBranch.getCharPref(STATE_PREF_NAME_PREFIX + 'test-phish-proto');
} catch (e) {}

Просмотреть файл

@ -136,11 +136,15 @@ def read_output(*args):
class HGRepoInfo:
def __init__(self, path):
self.path = path
rev = read_output('hg', '-R', path,
'parent', '--template={node|short}')
# Look for the default hg path. If SRVSRV_ROOT is set, we
rev = os.environ.get('MOZ_SOURCE_CHANGESET')
if not rev:
rev = read_output('hg', '-R', path,
'parent', '--template={node|short}')
# Look for the default hg path. If MOZ_SOURCE_REPO is set, we
# don't bother asking hg.
hg_root = os.environ.get("SRCSRV_ROOT")
hg_root = os.environ.get('MOZ_SOURCE_REPO')
if hg_root:
root = hg_root
else:
@ -158,7 +162,7 @@ class HGRepoInfo:
if cleanroot is None:
print >> sys.stderr, textwrap.dedent("""\
Could not determine repo info for %s. This is either not a clone of the web-based
repository, or you have not specified SRCSRV_ROOT, or the clone is corrupt.""") % path
repository, or you have not specified MOZ_SOURCE_REPO, or the clone is corrupt.""") % path
sys.exit(1)
self.rev = rev
self.root = root
@ -205,7 +209,7 @@ class GitRepoInfo:
if cleanroot is None:
print >> sys.stderr, textwrap.dedent("""\
Could not determine repo info for %s (%s). This is either not a clone of a web-based
repository, or you have not specified SRCSRV_ROOT, or the clone is corrupt.""") % (path, root)
repository, or you have not specified MOZ_SOURCE_REPO, or the clone is corrupt.""") % (path, root)
sys.exit(1)
self.rev = rev
self.cleanroot = cleanroot
@ -614,7 +618,7 @@ class Dumper:
# tries to get the vcs root from the .mozconfig first - if it's not set
# the tinderbox vcs path will be assigned further down
vcs_root = os.environ.get("SRCSRV_ROOT")
vcs_root = os.environ.get('MOZ_SOURCE_REPO')
for arch_num, arch in enumerate(self.archs):
self.files_record[files] = 0 # record that we submitted jobs for this tuple of files
self.SubmitJob(files[-1], 'ProcessFilesWork', args=(files, arch_num, arch, vcs_root, after, after_arg), callback=self.ProcessFilesFinished)

Просмотреть файл

@ -59,6 +59,13 @@ class HelperMixin(object):
symbolstore.srcdirRepoInfo = {}
symbolstore.vcsFileInfoCache = {}
# Remove environment variables that can influence tests.
for e in ('MOZ_SOURCE_CHANGESET', 'MOZ_SOURCE_REPO'):
try:
del os.environ[e]
except KeyError:
pass
def tearDown(self):
shutil.rmtree(self.test_dir)
symbolstore.srcdirRepoInfo = {}
@ -264,6 +271,16 @@ class TestGetVCSFilename(HelperMixin, unittest.TestCase):
self.assertEqual("hg:example.com/other:bar.c:0987ffff",
symbolstore.GetVCSFilename(filename2, [srcdir1, srcdir2])[0])
def testVCSFilenameEnv(self):
# repo URL and changeset read from environment variables if defined.
os.environ['MOZ_SOURCE_REPO'] = 'https://somewhere.com/repo'
os.environ['MOZ_SOURCE_CHANGESET'] = 'abcdef0123456'
os.mkdir(os.path.join(self.test_dir, '.hg'))
filename = os.path.join(self.test_dir, 'foo.c')
self.assertEqual('hg:somewhere.com/repo:foo.c:abcdef0123456',
symbolstore.GetVCSFilename(filename, [self.test_dir])[0])
class TestRepoManifest(HelperMixin, unittest.TestCase):
def testRepoManifest(self):
manifest = os.path.join(self.test_dir, "sources.xml")

Просмотреть файл

@ -18,3 +18,12 @@ csp.error.missing-source = %1$S must include the source %2$S
#LOCALIZATION NOTE (csp.error.illegal-host-wildcard) %2$S a protocol name, such as "http", which appears as "http:", as it would in a URL.
csp.error.illegal-host-wildcard = %2$S: wildcard sources in %1$S directives must include at least one non-generic sub-domain (e.g., *.example.com rather than *.com)
#LOCALIZATION NOTE (uninstall.confirmation.title) %S is the name of the extension which is about to be uninstalled.
uninstall.confirmation.title = Uninstall %S
#LOCALIZATION NOTE (uninstall.confirmation.message) %S is the name of the extension which is about to be uninstalled.
uninstall.confirmation.message = The extension “%S” is requesting to be uninstalled. What would you like to do?
uninstall.confirmation.button-0.label = Uninstall
uninstall.confirmation.button-1.label = Keep Installed

Просмотреть файл

@ -41,7 +41,6 @@ const kModalStyle = `
.findbar-modalHighlight-outline {
position: absolute;
background: #ffc535;
border-radius: 3px;
box-shadow: 0 2px 0 0 rgba(0,0,0,.1);
color: #000;
display: -moz-box;
@ -115,6 +114,11 @@ const kModalStyle = `
.findbar-modalHighlight-outlineMask[brighttext] > .findbar-modalHighlight-rect {
background: #000;
}
.findbar-modalHighlight-outline,
.findbar-modalHighlight-rect {
border-radius: 3px;
}`;
function mockAnonymousContentNode(domNode) {