mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-12 12:55:46 +00:00
merge mozilla-inbound to mozilla-central a=merge
This commit is contained in:
commit
6eece0b459
@ -67,6 +67,7 @@ included_inclnames_to_ignore = set([
|
||||
'js-config.h', # generated in $OBJDIR
|
||||
'pratom.h', # NSPR
|
||||
'prcvar.h', # NSPR
|
||||
'prerror.h', # NSPR
|
||||
'prinit.h', # NSPR
|
||||
'prlink.h', # NSPR
|
||||
'prlock.h', # NSPR
|
||||
|
@ -9130,6 +9130,9 @@ if test -z "$MOZ_NATIVE_JEMALLOC" -a "$MOZ_MEMORY" && test -n "$MOZ_JEMALLOC3" -
|
||||
# Force disable DSS support in jemalloc.
|
||||
ac_configure_args="$ac_configure_args ac_cv_func_sbrk=false"
|
||||
|
||||
# Make Linux builds munmap freed chunks instead of recycling them.
|
||||
ac_configure_args="$ac_configure_args --enable-munmap"
|
||||
|
||||
if ! test -e memory/jemalloc; then
|
||||
mkdir -p memory/jemalloc
|
||||
fi
|
||||
|
@ -31,7 +31,7 @@ public:
|
||||
|
||||
// As required
|
||||
nsIGlobalObject* GetParentObject() const;
|
||||
virtual JSObject* WrapObject(JSContext* aCx);
|
||||
virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
|
||||
|
||||
// setter and getter
|
||||
void Register(RTCIdentityProvider& aIdp);
|
||||
|
@ -110,8 +110,8 @@ MediaSourceReader::RequestAudioData()
|
||||
{
|
||||
nsRefPtr<AudioDataPromise> p = mAudioPromise.Ensure(__func__);
|
||||
MSE_DEBUGV("");
|
||||
if (!GetAudioReader()) {
|
||||
MSE_DEBUG("called with no audio reader");
|
||||
if (!mAudioTrack) {
|
||||
MSE_DEBUG("called with no audio track");
|
||||
mAudioPromise.Reject(DECODE_ERROR, __func__);
|
||||
return p;
|
||||
}
|
||||
@ -130,12 +130,13 @@ MediaSourceReader::RequestAudioData()
|
||||
&MediaSourceReader::CompleteAudioSeekAndDoRequest,
|
||||
&MediaSourceReader::CompleteAudioSeekAndRejectPromise));
|
||||
break;
|
||||
case SOURCE_ERROR:
|
||||
case SOURCE_NONE:
|
||||
if (mLastAudioTime) {
|
||||
CheckForWaitOrEndOfStream(MediaData::AUDIO_DATA, mLastAudioTime);
|
||||
break;
|
||||
}
|
||||
// Fallback to using current reader
|
||||
// Fallback to using first reader
|
||||
mAudioSourceDecoder = mAudioTrack->Decoders()[0];
|
||||
default:
|
||||
DoAudioRequest();
|
||||
break;
|
||||
@ -253,8 +254,8 @@ MediaSourceReader::RequestVideoData(bool aSkipToNextKeyframe, int64_t aTimeThres
|
||||
nsRefPtr<VideoDataPromise> p = mVideoPromise.Ensure(__func__);
|
||||
MSE_DEBUGV("RequestVideoData(%d, %lld)",
|
||||
aSkipToNextKeyframe, aTimeThreshold);
|
||||
if (!GetVideoReader()) {
|
||||
MSE_DEBUG("called with no video reader");
|
||||
if (!mVideoTrack) {
|
||||
MSE_DEBUG("called with no video track");
|
||||
mVideoPromise.Reject(DECODE_ERROR, __func__);
|
||||
return p;
|
||||
}
|
||||
@ -278,12 +279,13 @@ MediaSourceReader::RequestVideoData(bool aSkipToNextKeyframe, int64_t aTimeThres
|
||||
&MediaSourceReader::CompleteVideoSeekAndDoRequest,
|
||||
&MediaSourceReader::CompleteVideoSeekAndRejectPromise));
|
||||
break;
|
||||
case SOURCE_ERROR:
|
||||
case SOURCE_NONE:
|
||||
if (mLastVideoTime) {
|
||||
CheckForWaitOrEndOfStream(MediaData::VIDEO_DATA, mLastVideoTime);
|
||||
break;
|
||||
}
|
||||
// Fallback to using current reader.
|
||||
// Fallback to using first reader.
|
||||
mVideoSourceDecoder = mVideoTrack->Decoders()[0];
|
||||
default:
|
||||
DoVideoRequest();
|
||||
break;
|
||||
@ -482,7 +484,7 @@ MediaSourceReader::SwitchAudioSource(int64_t* aTarget)
|
||||
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
||||
// XXX: Can't handle adding an audio track after ReadMetadata.
|
||||
if (!mAudioTrack) {
|
||||
return SOURCE_ERROR;
|
||||
return SOURCE_NONE;
|
||||
}
|
||||
|
||||
// We first search without the tolerance and then search with it, so that, in
|
||||
@ -495,25 +497,31 @@ MediaSourceReader::SwitchAudioSource(int64_t* aTarget)
|
||||
newDecoder = SelectDecoder(*aTarget, EOS_FUZZ_US, mAudioTrack->Decoders());
|
||||
usedFuzz = true;
|
||||
}
|
||||
if (newDecoder && newDecoder != mAudioSourceDecoder) {
|
||||
if (GetAudioReader() && mAudioSourceDecoder != newDecoder) {
|
||||
GetAudioReader()->SetIdle();
|
||||
mAudioSourceDecoder = newDecoder;
|
||||
if (usedFuzz) {
|
||||
// A decoder buffered range is continuous. We would have failed the exact
|
||||
// search but succeeded the fuzzy one if our target was shortly before
|
||||
// start time.
|
||||
nsRefPtr<dom::TimeRanges> ranges = new dom::TimeRanges();
|
||||
newDecoder->GetBuffered(ranges);
|
||||
int64_t startTime = ranges->GetStartTime() * USECS_PER_S;
|
||||
if (*aTarget < startTime) {
|
||||
*aTarget = startTime;
|
||||
}
|
||||
}
|
||||
MSE_DEBUGV("switched decoder to %p (fuzz:%d)",
|
||||
mAudioSourceDecoder.get(), usedFuzz);
|
||||
return SOURCE_NEW;
|
||||
}
|
||||
return newDecoder ? SOURCE_EXISTING : SOURCE_ERROR;
|
||||
if (!newDecoder) {
|
||||
mAudioSourceDecoder = nullptr;
|
||||
return SOURCE_NONE;
|
||||
}
|
||||
if (newDecoder == mAudioSourceDecoder) {
|
||||
return SOURCE_EXISTING;
|
||||
}
|
||||
mAudioSourceDecoder = newDecoder;
|
||||
if (usedFuzz) {
|
||||
// A decoder buffered range is continuous. We would have failed the exact
|
||||
// search but succeeded the fuzzy one if our target was shortly before
|
||||
// start time.
|
||||
nsRefPtr<dom::TimeRanges> ranges = new dom::TimeRanges();
|
||||
newDecoder->GetBuffered(ranges);
|
||||
int64_t startTime = ranges->GetStartTime() * USECS_PER_S;
|
||||
if (*aTarget < startTime) {
|
||||
*aTarget = startTime;
|
||||
}
|
||||
}
|
||||
MSE_DEBUGV("switched decoder to %p (fuzz:%d)",
|
||||
mAudioSourceDecoder.get(), usedFuzz);
|
||||
return SOURCE_NEW;
|
||||
}
|
||||
|
||||
MediaSourceReader::SwitchSourceResult
|
||||
@ -522,7 +530,7 @@ MediaSourceReader::SwitchVideoSource(int64_t* aTarget)
|
||||
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
||||
// XXX: Can't handle adding a video track after ReadMetadata.
|
||||
if (!mVideoTrack) {
|
||||
return SOURCE_ERROR;
|
||||
return SOURCE_NONE;
|
||||
}
|
||||
|
||||
// We first search without the tolerance and then search with it, so that, in
|
||||
@ -535,25 +543,31 @@ MediaSourceReader::SwitchVideoSource(int64_t* aTarget)
|
||||
newDecoder = SelectDecoder(*aTarget, EOS_FUZZ_US, mVideoTrack->Decoders());
|
||||
usedFuzz = true;
|
||||
}
|
||||
if (newDecoder && newDecoder != mVideoSourceDecoder) {
|
||||
if (GetVideoReader() && mVideoSourceDecoder != newDecoder) {
|
||||
GetVideoReader()->SetIdle();
|
||||
mVideoSourceDecoder = newDecoder;
|
||||
if (usedFuzz) {
|
||||
// A decoder buffered range is continuous. We would have failed the exact
|
||||
// search but succeeded the fuzzy one if our target was shortly before
|
||||
// start time.
|
||||
nsRefPtr<dom::TimeRanges> ranges = new dom::TimeRanges();
|
||||
newDecoder->GetBuffered(ranges);
|
||||
int64_t startTime = ranges->GetStartTime() * USECS_PER_S;
|
||||
if (*aTarget < startTime) {
|
||||
*aTarget = startTime;
|
||||
}
|
||||
}
|
||||
MSE_DEBUGV("switched decoder to %p (fuzz:%d)",
|
||||
mVideoSourceDecoder.get(), usedFuzz);
|
||||
return SOURCE_NEW;
|
||||
}
|
||||
return newDecoder ? SOURCE_EXISTING : SOURCE_ERROR;
|
||||
if (!newDecoder) {
|
||||
mVideoSourceDecoder = nullptr;
|
||||
return SOURCE_NONE;
|
||||
}
|
||||
if (newDecoder == mVideoSourceDecoder) {
|
||||
return SOURCE_EXISTING;
|
||||
}
|
||||
mVideoSourceDecoder = newDecoder;
|
||||
if (usedFuzz) {
|
||||
// A decoder buffered range is continuous. We would have failed the exact
|
||||
// search but succeeded the fuzzy one if our target was shortly before
|
||||
// start time.
|
||||
nsRefPtr<dom::TimeRanges> ranges = new dom::TimeRanges();
|
||||
newDecoder->GetBuffered(ranges);
|
||||
int64_t startTime = ranges->GetStartTime() * USECS_PER_S;
|
||||
if (*aTarget < startTime) {
|
||||
*aTarget = startTime;
|
||||
}
|
||||
}
|
||||
MSE_DEBUGV("switched decoder to %p (fuzz:%d)",
|
||||
mVideoSourceDecoder.get(), usedFuzz);
|
||||
return SOURCE_NEW;
|
||||
}
|
||||
|
||||
bool
|
||||
@ -796,7 +810,12 @@ MediaSourceReader::OnVideoSeekFailed(nsresult aResult)
|
||||
void
|
||||
MediaSourceReader::DoAudioSeek()
|
||||
{
|
||||
SwitchAudioSource(&mPendingSeekTime);
|
||||
if (SwitchAudioSource(&mPendingSeekTime) == SOURCE_NONE) {
|
||||
// Data we need got evicted since the last time we checked for data
|
||||
// availability. Abort current seek attempt.
|
||||
mWaitingForSeekData = true;
|
||||
return;
|
||||
}
|
||||
mAudioSeekRequest.Begin(GetAudioReader()->Seek(GetReaderAudioTime(mPendingSeekTime), 0)
|
||||
->RefableThen(GetTaskQueue(), __func__, this,
|
||||
&MediaSourceReader::OnAudioSeekCompleted,
|
||||
@ -829,7 +848,12 @@ MediaSourceReader::AttemptSeek()
|
||||
// Seek methods since it can deadlock.
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
||||
if (!mWaitingForSeekData || !TrackBuffersContainTime(mPendingSeekTime)) {
|
||||
if (!mWaitingForSeekData) {
|
||||
return;
|
||||
}
|
||||
if (!TrackBuffersContainTime(mPendingSeekTime)) {
|
||||
mVideoSourceDecoder = nullptr;
|
||||
mAudioSourceDecoder = nullptr;
|
||||
return;
|
||||
}
|
||||
mWaitingForSeekData = false;
|
||||
@ -856,7 +880,12 @@ MediaSourceReader::AttemptSeek()
|
||||
void
|
||||
MediaSourceReader::DoVideoSeek()
|
||||
{
|
||||
SwitchVideoSource(&mPendingSeekTime);
|
||||
if (SwitchVideoSource(&mPendingSeekTime) == SOURCE_NONE) {
|
||||
// Data we need got evicted since the last time we checked for data
|
||||
// availability. Abort current seek attempt.
|
||||
mWaitingForSeekData = true;
|
||||
return;
|
||||
}
|
||||
mVideoSeekRequest.Begin(GetVideoReader()->Seek(GetReaderVideoTime(mPendingSeekTime), 0)
|
||||
->RefableThen(GetTaskQueue(), __func__, this,
|
||||
&MediaSourceReader::OnVideoSeekCompleted,
|
||||
@ -1085,7 +1114,7 @@ bool
|
||||
MediaSourceReader::IsActiveReader(MediaDecoderReader* aReader)
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
||||
return aReader == GetVideoReader() || aReader == GetAudioReader();
|
||||
return aReader && (aReader == GetVideoReader() || aReader == GetAudioReader());
|
||||
}
|
||||
|
||||
MediaDecoderReader*
|
||||
|
@ -160,7 +160,7 @@ private:
|
||||
// Search can be made using a fuzz factor. Should an approximated value be
|
||||
// found instead, aTarget will be updated to the actual target found.
|
||||
enum SwitchSourceResult {
|
||||
SOURCE_ERROR = -1,
|
||||
SOURCE_NONE = -1,
|
||||
SOURCE_EXISTING = 0,
|
||||
SOURCE_NEW = 1,
|
||||
};
|
||||
|
@ -323,7 +323,7 @@ TrackBuffer::EvictData(double aPlaybackTime,
|
||||
nsRefPtr<dom::TimeRanges> buffered = new dom::TimeRanges();
|
||||
decoders[i]->GetBuffered(buffered);
|
||||
|
||||
MSE_DEBUG("decoder=%u/%u threshold=%u toEvict=%lld",
|
||||
MSE_DEBUG("Step1. decoder=%u/%u threshold=%u toEvict=%lld",
|
||||
i, decoders.Length(), aThreshold, toEvict);
|
||||
|
||||
// To ensure we don't evict data past the current playback position
|
||||
@ -331,20 +331,34 @@ TrackBuffer::EvictData(double aPlaybackTime,
|
||||
// that point.
|
||||
if (aPlaybackTime > MSE_EVICT_THRESHOLD_TIME) {
|
||||
double time = aPlaybackTime - MSE_EVICT_THRESHOLD_TIME;
|
||||
int64_t playbackOffset = decoders[i]->ConvertToByteOffset(time);
|
||||
MSE_DEBUG("evicting some bufferedEnd=%f"
|
||||
"aPlaybackTime=%f time=%f, playbackOffset=%lld size=%lld",
|
||||
buffered->GetEndTime(), aPlaybackTime, time,
|
||||
playbackOffset, decoders[i]->GetResource()->GetSize());
|
||||
if (playbackOffset > 0) {
|
||||
toEvict -= decoders[i]->GetResource()->EvictData(playbackOffset,
|
||||
playbackOffset);
|
||||
bool isActive = decoders[i] == mCurrentDecoder ||
|
||||
mParentDecoder->IsActiveReader(decoders[i]->GetReader());
|
||||
if (!isActive && buffered->GetEndTime() < time) {
|
||||
// The entire decoder is contained before our current playback time.
|
||||
// It can be fully evicted.
|
||||
MSE_DEBUG("evicting all bufferedEnd=%f "
|
||||
"aPlaybackTime=%f time=%f, size=%lld",
|
||||
buffered->GetEndTime(), aPlaybackTime, time,
|
||||
decoders[i]->GetResource()->GetSize());
|
||||
toEvict -= decoders[i]->GetResource()->EvictAll();
|
||||
} else {
|
||||
int64_t playbackOffset = decoders[i]->ConvertToByteOffset(time);
|
||||
MSE_DEBUG("evicting some bufferedEnd=%f "
|
||||
"aPlaybackTime=%f time=%f, playbackOffset=%lld size=%lld",
|
||||
buffered->GetEndTime(), aPlaybackTime, time,
|
||||
playbackOffset, decoders[i]->GetResource()->GetSize());
|
||||
if (playbackOffset > 0) {
|
||||
toEvict -= decoders[i]->GetResource()->EvictData(playbackOffset,
|
||||
playbackOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Evict all data from decoders we've likely already read from.
|
||||
for (uint32_t i = 0; i < decoders.Length() && toEvict > 0; ++i) {
|
||||
MSE_DEBUG("Step2. decoder=%u/%u threshold=%u toEvict=%lld",
|
||||
i, decoders.Length(), aThreshold, toEvict);
|
||||
if (mParentDecoder->IsActiveReader(decoders[i]->GetReader())) {
|
||||
break;
|
||||
}
|
||||
@ -393,6 +407,8 @@ TrackBuffer::EvictData(double aPlaybackTime,
|
||||
decoders.Sort(DecoderSorter());
|
||||
|
||||
for (int32_t i = int32_t(decoders.Length()) - 1; i >= 0 && toEvict > 0; --i) {
|
||||
MSE_DEBUG("Step3. decoder=%u/%u threshold=%u toEvict=%lld",
|
||||
i, decoders.Length(), aThreshold, toEvict);
|
||||
if (decoders[i] == playingDecoder || decoders[i] == nextPlayingDecoder ||
|
||||
decoders[i] == mCurrentDecoder) {
|
||||
continue;
|
||||
@ -464,6 +480,9 @@ TrackBuffer::HasOnlyIncompleteMedia()
|
||||
}
|
||||
nsRefPtr<dom::TimeRanges> buffered = new dom::TimeRanges();
|
||||
mCurrentDecoder->GetBuffered(buffered);
|
||||
MSE_DEBUG("mCurrentDecoder.size=%lld, start=%f end=%f",
|
||||
mCurrentDecoder->GetResource()->GetSize(),
|
||||
buffered->GetStartTime(), buffered->GetEndTime());
|
||||
return mCurrentDecoder->GetResource()->GetSize() && !buffered->Length();
|
||||
}
|
||||
|
||||
@ -614,6 +633,10 @@ TrackBuffer::InitializeDecoder(SourceBufferDecoder* aDecoder)
|
||||
MSE_DEBUG("was shut down while reading metadata. Aborting initialization.");
|
||||
return;
|
||||
}
|
||||
if (mCurrentDecoder != aDecoder) {
|
||||
MSE_DEBUG("append was cancelled. Aborting initialization.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (NS_SUCCEEDED(rv) && reader->IsWaitingOnCDMResource()) {
|
||||
mWaitingDecoders.AppendElement(aDecoder);
|
||||
@ -963,31 +986,52 @@ TrackBuffer::RangeRemoval(int64_t aStart, int64_t aEnd)
|
||||
// Nothing to remove.
|
||||
return false;
|
||||
}
|
||||
if (aEnd < bufferedEnd) {
|
||||
// TODO. We only handle trimming.
|
||||
|
||||
if (aStart > bufferedStart && aEnd < bufferedEnd) {
|
||||
// TODO. We only handle trimming and removal from the start.
|
||||
NS_WARNING("RangeRemoval unsupported arguments. "
|
||||
"Can only handle trimming");
|
||||
"Can only handle trimming (trim left or trim right");
|
||||
return false;
|
||||
}
|
||||
|
||||
nsTArray<SourceBufferDecoder*> decoders;
|
||||
decoders.AppendElements(mInitializedDecoders);
|
||||
|
||||
// Only trimming existing buffers.
|
||||
for (size_t i = 0; i < decoders.Length(); ++i) {
|
||||
decoders[i]->Trim(aStart);
|
||||
if (aStart <= buffered->GetStartTime()) {
|
||||
// We've completely emptied it, can clear the data.
|
||||
int64_t size = decoders[i]->GetResource()->GetSize();
|
||||
decoders[i]->GetResource()->EvictData(size, size);
|
||||
if (decoders[i] == mCurrentDecoder ||
|
||||
mParentDecoder->IsActiveReader(decoders[i]->GetReader())) {
|
||||
continue;
|
||||
if (aStart <= bufferedStart && aEnd < bufferedEnd) {
|
||||
// Evict data from beginning.
|
||||
for (size_t i = 0; i < decoders.Length(); ++i) {
|
||||
nsRefPtr<dom::TimeRanges> buffered = new dom::TimeRanges();
|
||||
decoders[i]->GetBuffered(buffered);
|
||||
if (buffered->GetEndTime() < aEnd) {
|
||||
// Can be fully removed.
|
||||
MSE_DEBUG("remove all bufferedEnd=%f time=%f, size=%lld",
|
||||
buffered->GetEndTime(), time,
|
||||
decoders[i]->GetResource()->GetSize());
|
||||
decoders[i]->GetResource()->EvictAll();
|
||||
} else {
|
||||
int64_t offset = decoders[i]->ConvertToByteOffset(aEnd);
|
||||
MSE_DEBUG("removing some bufferedEnd=%f offset=%lld size=%lld",
|
||||
buffered->GetEndTime(), offset,
|
||||
decoders[i]->GetResource()->GetSize());
|
||||
if (offset > 0) {
|
||||
decoders[i]->GetResource()->EvictData(offset, offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Only trimming existing buffers.
|
||||
for (size_t i = 0; i < decoders.Length(); ++i) {
|
||||
if (aStart <= buffered->GetStartTime()) {
|
||||
// It will be entirely emptied, can clear all data.
|
||||
decoders[i]->GetResource()->EvictAll();
|
||||
} else {
|
||||
decoders[i]->Trim(aStart);
|
||||
}
|
||||
MSE_DEBUG("remove empty decoders=%d", i);
|
||||
RemoveDecoder(decoders[i]);
|
||||
}
|
||||
}
|
||||
|
||||
RemoveEmptyDecoders(decoders);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -19,8 +19,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=519928
|
||||
|
||||
var iframe = document.getElementById("load-frame");
|
||||
|
||||
function enableJS() allowJS(true, iframe);
|
||||
function disableJS() allowJS(false, iframe);
|
||||
function enableJS() { allowJS(true, iframe); }
|
||||
function disableJS() { allowJS(false, iframe); }
|
||||
function allowJS(allow, frame) {
|
||||
SpecialPowers.wrap(frame.contentWindow)
|
||||
.QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor)
|
||||
@ -28,6 +28,7 @@ function allowJS(allow, frame) {
|
||||
.QueryInterface(SpecialPowers.Ci.nsIDocShell)
|
||||
.allowJavascript = allow;
|
||||
}
|
||||
|
||||
function expectJSAllowed(allowed, testCondition, callback) {
|
||||
window.ICanRunMyJS = false;
|
||||
var self_ = window;
|
||||
@ -49,8 +50,8 @@ function expectJSAllowed(allowed, testCondition, callback) {
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addLoadEvent(function() {
|
||||
var enterDesignMode = function() document.designMode = "on";
|
||||
var leaveDesignMode = function() document.designMode = "off";
|
||||
var enterDesignMode = function() { document.designMode = "on"; };
|
||||
var leaveDesignMode = function() { document.designMode = "off"; };
|
||||
expectJSAllowed(false, disableJS, function() {
|
||||
expectJSAllowed(true, enableJS, function() {
|
||||
expectJSAllowed(true, enterDesignMode, function() {
|
||||
@ -59,8 +60,8 @@ addLoadEvent(function() {
|
||||
expectJSAllowed(false, enterDesignMode, function() {
|
||||
expectJSAllowed(false, leaveDesignMode, function() {
|
||||
expectJSAllowed(true, enableJS, function() {
|
||||
enterDesignMode = function() iframe.contentDocument.designMode = "on";
|
||||
leaveDesignMode = function() iframe.contentDocument.designMode = "off";
|
||||
enterDesignMode = function() { iframe.contentDocument.designMode = "on"; };
|
||||
leaveDesignMode = function() { iframe.contentDocument.designMode = "off"; };
|
||||
expectJSAllowed(false, disableJS, function() {
|
||||
expectJSAllowed(true, enableJS, function() {
|
||||
expectJSAllowed(true, enterDesignMode, function() {
|
||||
@ -120,4 +121,3 @@ function testDocumentDisabledJS() {
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
@ -45,7 +45,7 @@ function copyCF_HTML(cfhtml, success, failure) {
|
||||
|
||||
var flavors = [CF_HTML];
|
||||
if (!cb.hasDataMatchingFlavors(flavors, flavors.length, cb.kGlobalClipboard)) {
|
||||
setTimeout(function() copyCF_HTML_worker(success, failure), 100);
|
||||
setTimeout(function() { copyCF_HTML_worker(success, failure); }, 100);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -59,7 +59,7 @@ function copyCF_HTML(cfhtml, success, failure) {
|
||||
trans.getTransferData(CF_HTML, data, {});
|
||||
data = SpecialPowers.wrap(data).value.QueryInterface(Ci.nsISupportsCString).data;
|
||||
} catch (e) {
|
||||
setTimeout(function() copyCF_HTML_worker(success, failure), 100);
|
||||
setTimeout(function() { copyCF_HTML_worker(success, failure); }, 100);
|
||||
return;
|
||||
}
|
||||
success();
|
||||
|
@ -119,175 +119,175 @@ var tests = [
|
||||
isIFrame: true,
|
||||
payload: dataPayload,
|
||||
iframeCount: 0,
|
||||
rootElement: function() document.getElementById("a").contentDocument.documentElement
|
||||
rootElement() { return document.getElementById("a").contentDocument.documentElement; },
|
||||
},
|
||||
{
|
||||
id: "b",
|
||||
isIFrame: true,
|
||||
payload: jsPayload,
|
||||
iframeCount: 0,
|
||||
rootElement: function() document.getElementById("b").contentDocument.documentElement
|
||||
rootElement() { return document.getElementById("b").contentDocument.documentElement; },
|
||||
},
|
||||
{
|
||||
id: "c",
|
||||
isIFrame: true,
|
||||
payload: httpPayload,
|
||||
iframeCount: 0,
|
||||
rootElement: function() document.getElementById("c").contentDocument.documentElement
|
||||
rootElement() { return document.getElementById("c").contentDocument.documentElement; },
|
||||
},
|
||||
{
|
||||
id: "g",
|
||||
isIFrame: true,
|
||||
payload: scriptPayload,
|
||||
rootElement: function() document.getElementById("g").contentDocument.documentElement,
|
||||
rootElement() { return document.getElementById("g").contentDocument.documentElement; },
|
||||
iframeCount: 0
|
||||
},
|
||||
{
|
||||
id: "h",
|
||||
isIFrame: true,
|
||||
payload: scriptExternalPayload,
|
||||
rootElement: function() document.getElementById("h").contentDocument.documentElement,
|
||||
rootElement() { return document.getElementById("h").contentDocument.documentElement; },
|
||||
iframeCount: 0
|
||||
},
|
||||
{
|
||||
id: "d",
|
||||
payload: dataPayload,
|
||||
iframeCount: 0,
|
||||
rootElement: function() document.getElementById("d")
|
||||
rootElement() { return document.getElementById("d"); },
|
||||
},
|
||||
{
|
||||
id: "e",
|
||||
payload: jsPayload,
|
||||
iframeCount: 0,
|
||||
rootElement: function() document.getElementById("e")
|
||||
rootElement() { return document.getElementById("e"); },
|
||||
},
|
||||
{
|
||||
id: "f",
|
||||
payload: httpPayload,
|
||||
iframeCount: 0,
|
||||
rootElement: function() document.getElementById("f")
|
||||
rootElement() { return document.getElementById("f"); },
|
||||
},
|
||||
{
|
||||
id: "i",
|
||||
payload: scriptPayload,
|
||||
rootElement: function() document.getElementById("i"),
|
||||
rootElement() { return document.getElementById("i"); },
|
||||
iframeCount: 0
|
||||
},
|
||||
{
|
||||
id: "j",
|
||||
payload: scriptExternalPayload,
|
||||
rootElement: function() document.getElementById("j"),
|
||||
rootElement() { return document.getElementById("j"); },
|
||||
iframeCount: 0
|
||||
},
|
||||
{
|
||||
id: "k",
|
||||
isIFrame: true,
|
||||
payload: validStyle1Payload,
|
||||
rootElement: function() document.getElementById("k").contentDocument.documentElement,
|
||||
checkResult: function(html) isnot(html.indexOf("style"), -1, "Should have retained style")
|
||||
rootElement() { return document.getElementById("k").contentDocument.documentElement; },
|
||||
checkResult(html) { isnot(html.indexOf("style"), -1, "Should have retained style"); },
|
||||
},
|
||||
{
|
||||
id: "l",
|
||||
payload: validStyle1Payload,
|
||||
rootElement: function() document.getElementById("l"),
|
||||
checkResult: function(html) isnot(html.indexOf("style"), -1, "Should have retained style")
|
||||
rootElement() { return document.getElementById("l"); },
|
||||
checkResult(html) { isnot(html.indexOf("style"), -1, "Should have retained style"); },
|
||||
},
|
||||
{
|
||||
id: "m",
|
||||
isIFrame: true,
|
||||
payload: validStyle2Payload,
|
||||
rootElement: function() document.getElementById("m").contentDocument.documentElement,
|
||||
checkResult: function(html) isnot(html.indexOf("style"), -1, "Should have retained style")
|
||||
rootElement() { return document.getElementById("m").contentDocument.documentElement; },
|
||||
checkResult(html) { isnot(html.indexOf("style"), -1, "Should have retained style"); },
|
||||
},
|
||||
{
|
||||
id: "n",
|
||||
payload: validStyle2Payload,
|
||||
rootElement: function() document.getElementById("n"),
|
||||
checkResult: function(html) isnot(html.indexOf("style"), -1, "Should have retained style")
|
||||
rootElement() { return document.getElementById("n"); },
|
||||
checkResult(html) { isnot(html.indexOf("style"), -1, "Should have retained style"); },
|
||||
},
|
||||
{
|
||||
id: "o",
|
||||
isIFrame: true,
|
||||
payload: invalidStyle1Payload,
|
||||
rootElement: function() document.getElementById("o").contentDocument.documentElement,
|
||||
checkResult: function(html) is(html.indexOf("binding"), -1, "Should not have retained the binding style")
|
||||
rootElement() { return document.getElementById("o").contentDocument.documentElement; },
|
||||
checkResult(html) { is(html.indexOf("binding"), -1, "Should not have retained the binding style"); },
|
||||
},
|
||||
{
|
||||
id: "p",
|
||||
payload: invalidStyle1Payload,
|
||||
rootElement: function() document.getElementById("p"),
|
||||
checkResult: function(html) is(html.indexOf("binding"), -1, "Should not have retained the binding style")
|
||||
rootElement() { return document.getElementById("p"); },
|
||||
checkResult(html) { is(html.indexOf("binding"), -1, "Should not have retained the binding style"); },
|
||||
},
|
||||
{
|
||||
id: "q",
|
||||
isIFrame: true,
|
||||
payload: invalidStyle2Payload,
|
||||
rootElement: function() document.getElementById("q").contentDocument.documentElement,
|
||||
checkResult: function(html) is(html.indexOf("binding"), -1, "Should not have retained the binding style")
|
||||
rootElement() { return document.getElementById("q").contentDocument.documentElement; },
|
||||
checkResult(html) { is(html.indexOf("binding"), -1, "Should not have retained the binding style"); },
|
||||
},
|
||||
{
|
||||
id: "r",
|
||||
payload: invalidStyle2Payload,
|
||||
rootElement: function() document.getElementById("r"),
|
||||
checkResult: function(html) is(html.indexOf("binding"), -1, "Should not have retained the binding style")
|
||||
rootElement() { return document.getElementById("r"); },
|
||||
checkResult(html) { is(html.indexOf("binding"), -1, "Should not have retained the binding style"); },
|
||||
},
|
||||
{
|
||||
id: "s",
|
||||
isIFrame: true,
|
||||
payload: invalidStyle1Payload,
|
||||
rootElement: function() document.getElementById("s").contentDocument.documentElement,
|
||||
checkResult: function(html) is(html.indexOf("xxx"), -1, "Should not have retained the import style")
|
||||
rootElement() { return document.getElementById("s").contentDocument.documentElement; },
|
||||
checkResult(html) { is(html.indexOf("xxx"), -1, "Should not have retained the import style"); },
|
||||
},
|
||||
{
|
||||
id: "t",
|
||||
payload: invalidStyle1Payload,
|
||||
rootElement: function() document.getElementById("t"),
|
||||
checkResult: function(html) is(html.indexOf("xxx"), -1, "Should not have retained the import style")
|
||||
rootElement() { return document.getElementById("t"); },
|
||||
checkResult(html) { is(html.indexOf("xxx"), -1, "Should not have retained the import style"); },
|
||||
},
|
||||
{
|
||||
id: "u",
|
||||
isIFrame: true,
|
||||
payload: invalidStyle2Payload,
|
||||
rootElement: function() document.getElementById("u").contentDocument.documentElement,
|
||||
checkResult: function(html) is(html.indexOf("xxx"), -1, "Should not have retained the import style")
|
||||
rootElement() { return document.getElementById("u").contentDocument.documentElement; },
|
||||
checkResult(html) { is(html.indexOf("xxx"), -1, "Should not have retained the import style"); },
|
||||
},
|
||||
{
|
||||
id: "v",
|
||||
payload: invalidStyle2Payload,
|
||||
rootElement: function() document.getElementById("v"),
|
||||
checkResult: function(html) is(html.indexOf("xxx"), -1, "Should not have retained the import style")
|
||||
rootElement() { return document.getElementById("v"); },
|
||||
checkResult(html) { is(html.indexOf("xxx"), -1, "Should not have retained the import style"); },
|
||||
},
|
||||
{
|
||||
id: "w",
|
||||
isIFrame: true,
|
||||
payload: validStyle3Payload,
|
||||
rootElement: function() document.getElementById("w").contentDocument.documentElement,
|
||||
checkResult: function(html) isnot(html.indexOf("xxx"), -1, "Should have retained the font-face style")
|
||||
rootElement() { return document.getElementById("w").contentDocument.documentElement; },
|
||||
checkResult(html) { isnot(html.indexOf("xxx"), -1, "Should have retained the font-face style"); },
|
||||
},
|
||||
{
|
||||
id: "x",
|
||||
payload: validStyle3Payload,
|
||||
rootElement: function() document.getElementById("x"),
|
||||
checkResult: function(html) isnot(html.indexOf("xxx"), -1, "Should have retained the font-face style")
|
||||
rootElement() { return document.getElementById("x"); },
|
||||
checkResult(html) { isnot(html.indexOf("xxx"), -1, "Should have retained the font-face style"); },
|
||||
},
|
||||
{
|
||||
id: "y",
|
||||
isIFrame: true,
|
||||
payload: invalidStyle5Payload,
|
||||
rootElement: function() document.getElementById("y").contentDocument.documentElement,
|
||||
checkResult: function(html) isnot(html.indexOf("xxx"), -1, "Should not have retained the font-face style")
|
||||
rootElement() { return document.getElementById("y").contentDocument.documentElement; },
|
||||
checkResult(html) { isnot(html.indexOf("xxx"), -1, "Should not have retained the font-face style"); },
|
||||
},
|
||||
{
|
||||
id: "z",
|
||||
payload: invalidStyle5Payload,
|
||||
rootElement: function() document.getElementById("z"),
|
||||
checkResult: function(html) isnot(html.indexOf("xxx"), -1, "Should not have retained the font-face style")
|
||||
rootElement() { return document.getElementById("z"); },
|
||||
checkResult(html) { isnot(html.indexOf("xxx"), -1, "Should not have retained the font-face style"); },
|
||||
},
|
||||
{
|
||||
id: "aa",
|
||||
isIFrame: true,
|
||||
payload: nestedStylePayload,
|
||||
rootElement: function() document.getElementById("aa").contentDocument.documentElement,
|
||||
rootElement() { return document.getElementById("aa").contentDocument.documentElement; },
|
||||
checkResult: function(html, text) {
|
||||
is(html.indexOf("binding-1"), -1, "Should not have retained the binding-1 style");
|
||||
isnot(text.indexOf("#bar2"), -1, "Should have retained binding-2 as text content");
|
||||
@ -297,7 +297,7 @@ var tests = [
|
||||
{
|
||||
id: "bb",
|
||||
payload: nestedStylePayload,
|
||||
rootElement: function() document.getElementById("bb"),
|
||||
rootElement() { return document.getElementById("bb"); },
|
||||
checkResult: function(html, text) {
|
||||
is(html.indexOf("binding-1"), -1, "Should not have retained the binding-1 style");
|
||||
isnot(text.indexOf("#bar2"), -1, "Should have retained binding-2 as text content");
|
||||
@ -308,73 +308,73 @@ var tests = [
|
||||
id: "cc",
|
||||
isIFrame: true,
|
||||
payload: validStyle4Payload,
|
||||
rootElement: function() document.getElementById("cc").contentDocument.documentElement,
|
||||
checkResult: function(html) isnot(html.indexOf("xxx"), -1, "Should have retained the namespace style")
|
||||
rootElement() { return document.getElementById("cc").contentDocument.documentElement; },
|
||||
checkResult(html) { isnot(html.indexOf("xxx"), -1, "Should have retained the namespace style"); },
|
||||
},
|
||||
{
|
||||
id: "dd",
|
||||
payload: validStyle4Payload,
|
||||
rootElement: function() document.getElementById("dd"),
|
||||
checkResult: function(html) isnot(html.indexOf("xxx"), -1, "Should have retained the namespace style")
|
||||
rootElement() { return document.getElementById("dd"); },
|
||||
checkResult(html) { isnot(html.indexOf("xxx"), -1, "Should have retained the namespace style"); },
|
||||
},
|
||||
{
|
||||
id: "ee",
|
||||
isIFrame: true,
|
||||
payload: invalidStyle6Payload,
|
||||
rootElement: function() document.getElementById("ee").contentDocument.documentElement,
|
||||
checkResult: function(html) isnot(html.indexOf("xxx"), -1, "Should not have retained the namespace style")
|
||||
rootElement() { return document.getElementById("ee").contentDocument.documentElement; },
|
||||
checkResult(html) { isnot(html.indexOf("xxx"), -1, "Should not have retained the namespace style"); },
|
||||
},
|
||||
{
|
||||
id: "ff",
|
||||
payload: invalidStyle6Payload,
|
||||
rootElement: function() document.getElementById("ff"),
|
||||
checkResult: function(html) isnot(html.indexOf("xxx"), -1, "Should not have retained the namespace style")
|
||||
rootElement() { return document.getElementById("ff"); },
|
||||
checkResult(html) { isnot(html.indexOf("xxx"), -1, "Should not have retained the namespace style"); },
|
||||
},
|
||||
{
|
||||
id: "gg",
|
||||
isIFrame: true,
|
||||
payload: invalidStyle6Payload,
|
||||
rootElement: function() document.getElementById("gg").contentDocument.documentElement,
|
||||
checkResult: function(html) isnot(html.indexOf("bar"), -1, "Should have retained the src attribute for the image")
|
||||
rootElement() { return document.getElementById("gg").contentDocument.documentElement; },
|
||||
checkResult(html) { isnot(html.indexOf("bar"), -1, "Should have retained the src attribute for the image"); },
|
||||
},
|
||||
{
|
||||
id: "hh",
|
||||
payload: invalidStyle6Payload,
|
||||
rootElement: function() document.getElementById("hh"),
|
||||
checkResult: function(html) isnot(html.indexOf("bar"), -1, "Should have retained the src attribute for the image")
|
||||
rootElement() { return document.getElementById("hh"); },
|
||||
checkResult(html) { isnot(html.indexOf("bar"), -1, "Should have retained the src attribute for the image"); },
|
||||
},
|
||||
{
|
||||
id: "ii",
|
||||
isIFrame: true,
|
||||
payload: invalidStyle6Payload,
|
||||
rootElement: function() document.getElementById("ii").contentDocument.documentElement,
|
||||
checkResult: function(html) isnot(html.indexOf("bar"), -1, "Should have retained the src attribute for the image")
|
||||
rootElement() { return document.getElementById("ii").contentDocument.documentElement; },
|
||||
checkResult(html) { isnot(html.indexOf("bar"), -1, "Should have retained the src attribute for the image"); },
|
||||
},
|
||||
{
|
||||
id: "jj",
|
||||
payload: invalidStyle6Payload,
|
||||
rootElement: function() document.getElementById("jj"),
|
||||
checkResult: function(html) isnot(html.indexOf("bar"), -1, "Should have retained the src attribute for the image")
|
||||
rootElement() { return document.getElementById("jj"); },
|
||||
checkResult(html) { isnot(html.indexOf("bar"), -1, "Should have retained the src attribute for the image"); },
|
||||
},
|
||||
{
|
||||
id: "kk",
|
||||
isIFrame: true,
|
||||
payload: invalidStyle6Payload,
|
||||
rootElement: function() document.getElementById("kk").contentDocument.documentElement,
|
||||
checkResult: function(html) isnot(html.indexOf("bar"), -1, "Should have retained the src attribute for the image")
|
||||
rootElement() { return document.getElementById("kk").contentDocument.documentElement; },
|
||||
checkResult(html) { isnot(html.indexOf("bar"), -1, "Should have retained the src attribute for the image"); },
|
||||
},
|
||||
{
|
||||
id: "ll",
|
||||
payload: invalidStyle6Payload,
|
||||
rootElement: function() document.getElementById("ll"),
|
||||
checkResult: function(html) isnot(html.indexOf("bar"), -1, "Should have retained the src attribute for the image")
|
||||
rootElement() { return document.getElementById("ll"); },
|
||||
checkResult(html) { isnot(html.indexOf("bar"), -1, "Should have retained the src attribute for the image"); },
|
||||
},
|
||||
{
|
||||
id: "mm",
|
||||
isIFrame: true,
|
||||
indirectPaste: true,
|
||||
payload: invalidStyle7Payload,
|
||||
rootElement: function() document.getElementById("mm").contentDocument.documentElement,
|
||||
rootElement() { return document.getElementById("mm").contentDocument.documentElement; },
|
||||
checkResult: function(html) {
|
||||
is(html.indexOf("xxx"), -1, "Should not have retained the title text");
|
||||
isnot(html.indexOf("foo"), -1, "Should have retained the body text");
|
||||
@ -384,7 +384,7 @@ var tests = [
|
||||
id: "nn",
|
||||
indirectPaste: true,
|
||||
payload: invalidStyle7Payload,
|
||||
rootElement: function() document.getElementById("nn"),
|
||||
rootElement() { return document.getElementById("nn"); },
|
||||
checkResult: function(html) {
|
||||
is(html.indexOf("xxx"), -1, "Should not have retained the title text");
|
||||
isnot(html.indexOf("foo"), -1, "Should have retained the body text");
|
||||
@ -394,143 +394,143 @@ var tests = [
|
||||
id: "oo",
|
||||
isIFrame: true,
|
||||
payload: validDataFooPayload,
|
||||
rootElement: function() document.getElementById("oo").contentDocument.documentElement,
|
||||
checkResult: function(html) isnot(html.indexOf("bar"), -1, "Should have retained the data-bar attribute")
|
||||
rootElement() { return document.getElementById("oo").contentDocument.documentElement; },
|
||||
checkResult(html) { isnot(html.indexOf("bar"), -1, "Should have retained the data-bar attribute"); },
|
||||
},
|
||||
{
|
||||
id: "pp",
|
||||
payload: validDataFooPayload,
|
||||
rootElement: function() document.getElementById("pp"),
|
||||
checkResult: function(html) isnot(html.indexOf("bar"), -1, "Should have retained the data-bar attribute")
|
||||
rootElement() { return document.getElementById("pp"); },
|
||||
checkResult(html) { isnot(html.indexOf("bar"), -1, "Should have retained the data-bar attribute"); },
|
||||
},
|
||||
{
|
||||
id: "qq",
|
||||
isIFrame: true,
|
||||
payload: validDataFoo2Payload,
|
||||
rootElement: function() document.getElementById("qq").contentDocument.documentElement,
|
||||
checkResult: function(html) isnot(html.indexOf("bar"), -1, "Should have retained the _bar attribute")
|
||||
rootElement() { return document.getElementById("qq").contentDocument.documentElement; },
|
||||
checkResult(html) { isnot(html.indexOf("bar"), -1, "Should have retained the _bar attribute"); },
|
||||
},
|
||||
{
|
||||
id: "rr",
|
||||
payload: validDataFoo2Payload,
|
||||
rootElement: function() document.getElementById("rr"),
|
||||
checkResult: function(html) isnot(html.indexOf("bar"), -1, "Should have retained the _bar attribute")
|
||||
rootElement() { return document.getElementById("rr"); },
|
||||
checkResult(html) { isnot(html.indexOf("bar"), -1, "Should have retained the _bar attribute"); },
|
||||
},
|
||||
{
|
||||
id: "ss",
|
||||
isIFrame: true,
|
||||
payload: invalidStyle8Payload,
|
||||
rootElement: function() document.getElementById("ss").contentDocument.documentElement,
|
||||
checkResult: function(html) is(html.indexOf("@-moz-document"), -1, "Should not have retained the @-moz-document rule")
|
||||
rootElement() { return document.getElementById("ss").contentDocument.documentElement; },
|
||||
checkResult(html) { is(html.indexOf("@-moz-document"), -1, "Should not have retained the @-moz-document rule"); },
|
||||
},
|
||||
{
|
||||
id: "tt",
|
||||
payload: invalidStyle8Payload,
|
||||
rootElement: function() document.getElementById("tt"),
|
||||
checkResult: function(html) is(html.indexOf("@-moz-document"), -1, "Should not have retained the @-moz-document rule")
|
||||
rootElement() { return document.getElementById("tt"); },
|
||||
checkResult(html) { is(html.indexOf("@-moz-document"), -1, "Should not have retained the @-moz-document rule"); },
|
||||
},
|
||||
{
|
||||
id: "uu",
|
||||
isIFrame: true,
|
||||
payload: invalidStyle9Payload,
|
||||
rootElement: function() document.getElementById("uu").contentDocument.documentElement,
|
||||
checkResult: function(html) is(html.indexOf("@-moz-keyframes"), -1, "Should not have retained the @-moz-keyframes rule")
|
||||
rootElement() { return document.getElementById("uu").contentDocument.documentElement; },
|
||||
checkResult(html) { is(html.indexOf("@-moz-keyframes"), -1, "Should not have retained the @-moz-keyframes rule"); },
|
||||
},
|
||||
{
|
||||
id: "vv",
|
||||
payload: invalidStyle9Payload,
|
||||
rootElement: function() document.getElementById("vv"),
|
||||
checkResult: function(html) is(html.indexOf("@-moz-keyframes"), -1, "Should not have retained the @-moz-keyframes rule")
|
||||
rootElement() { return document.getElementById("vv"); },
|
||||
checkResult(html) { is(html.indexOf("@-moz-keyframes"), -1, "Should not have retained the @-moz-keyframes rule"); },
|
||||
},
|
||||
{
|
||||
id: "sss",
|
||||
payload: svgPayload,
|
||||
rootElement: function() document.getElementById("sss"),
|
||||
checkResult: function(html) isnot(html.indexOf("svgtitle"), -1, "Should have retained SVG title")
|
||||
rootElement() { return document.getElementById("sss"); },
|
||||
checkResult(html) { isnot(html.indexOf("svgtitle"), -1, "Should have retained SVG title"); },
|
||||
},
|
||||
{
|
||||
id: "ssss",
|
||||
isIFrame: true,
|
||||
payload: svgPayload,
|
||||
rootElement: function() document.getElementById("ssss").contentDocument.documentElement,
|
||||
checkResult: function(html) isnot(html.indexOf("svgtitle"), -1, "Should have retained SVG title")
|
||||
rootElement() { return document.getElementById("ssss").contentDocument.documentElement; },
|
||||
checkResult(html) { isnot(html.indexOf("svgtitle"), -1, "Should have retained SVG title"); },
|
||||
},
|
||||
{
|
||||
id: "ttt",
|
||||
payload: svg2Payload,
|
||||
rootElement: function() document.getElementById("ttt"),
|
||||
checkResult: function(html) is(html.indexOf("bogussvg"), -1, "Should have dropped bogussvg element")
|
||||
rootElement() { return document.getElementById("ttt"); },
|
||||
checkResult(html) { is(html.indexOf("bogussvg"), -1, "Should have dropped bogussvg element"); },
|
||||
},
|
||||
{
|
||||
id: "tttt",
|
||||
isIFrame: true,
|
||||
payload: svg2Payload,
|
||||
rootElement: function() document.getElementById("tttt").contentDocument.documentElement,
|
||||
checkResult: function(html) is(html.indexOf("bogussvg"), -1, "Should have dropped bogussvg element")
|
||||
rootElement() { return document.getElementById("tttt").contentDocument.documentElement; },
|
||||
checkResult(html) { is(html.indexOf("bogussvg"), -1, "Should have dropped bogussvg element"); },
|
||||
},
|
||||
{
|
||||
id: "uuu",
|
||||
payload: mathPayload,
|
||||
rootElement: function() document.getElementById("uuu"),
|
||||
checkResult: function(html) is(html.indexOf("bogusmath"), -1, "Should have dropped bogusmath element")
|
||||
rootElement() { return document.getElementById("uuu"); },
|
||||
checkResult(html) { is(html.indexOf("bogusmath"), -1, "Should have dropped bogusmath element"); },
|
||||
},
|
||||
{
|
||||
id: "uuuu",
|
||||
isIFrame: true,
|
||||
payload: mathPayload,
|
||||
rootElement: function() document.getElementById("uuuu").contentDocument.documentElement,
|
||||
checkResult: function(html) is(html.indexOf("bogusmath"), -1, "Should have dropped bogusmath element")
|
||||
rootElement() { return document.getElementById("uuuu").contentDocument.documentElement; },
|
||||
checkResult(html) { is(html.indexOf("bogusmath"), -1, "Should have dropped bogusmath element"); },
|
||||
},
|
||||
{
|
||||
id: "vvv",
|
||||
payload: math2Payload,
|
||||
rootElement: function() document.getElementById("vvv"),
|
||||
checkResult: function(html) is(html.indexOf("yyy.css"), -1, "Should have dropped MathML style element")
|
||||
rootElement() { return document.getElementById("vvv"); },
|
||||
checkResult(html) { is(html.indexOf("yyy.css"), -1, "Should have dropped MathML style element"); },
|
||||
},
|
||||
{
|
||||
id: "vvvv",
|
||||
isIFrame: true,
|
||||
payload: math2Payload,
|
||||
rootElement: function() document.getElementById("vvvv").contentDocument.documentElement,
|
||||
checkResult: function(html) is(html.indexOf("yyy.css"), -1, "Should have dropped MathML style element")
|
||||
rootElement() { return document.getElementById("vvvv").contentDocument.documentElement; },
|
||||
checkResult(html) { is(html.indexOf("yyy.css"), -1, "Should have dropped MathML style element"); },
|
||||
},
|
||||
{
|
||||
id: "www",
|
||||
payload: math3Payload,
|
||||
rootElement: function() document.getElementById("www"),
|
||||
checkResult: function(html) isnot(html.indexOf("<mi"), -1, "Should not have dropped MathML mi element")
|
||||
rootElement() { return document.getElementById("www"); },
|
||||
checkResult(html) { isnot(html.indexOf("<mi"), -1, "Should not have dropped MathML mi element"); },
|
||||
},
|
||||
{
|
||||
id: "wwww",
|
||||
isIFrame: true,
|
||||
payload: math3Payload,
|
||||
rootElement: function() document.getElementById("wwww").contentDocument.documentElement,
|
||||
checkResult: function(html) isnot(html.indexOf("<mi"), -1, "Should not have dropped MathML mi element")
|
||||
rootElement() { return document.getElementById("wwww").contentDocument.documentElement; },
|
||||
checkResult(html) { isnot(html.indexOf("<mi"), -1, "Should not have dropped MathML mi element"); },
|
||||
},
|
||||
{
|
||||
id: "xxx",
|
||||
payload: videoPayload,
|
||||
rootElement: function() document.getElementById("xxx"),
|
||||
checkResult: function(html) isnot(html.indexOf("controls="), -1, "Should have added the controls attribute")
|
||||
rootElement() { return document.getElementById("xxx"); },
|
||||
checkResult(html) { isnot(html.indexOf("controls="), -1, "Should have added the controls attribute"); },
|
||||
},
|
||||
{
|
||||
id: "xxxx",
|
||||
isIFrame: true,
|
||||
payload: videoPayload,
|
||||
rootElement: function() document.getElementById("xxxx").contentDocument.documentElement,
|
||||
checkResult: function(html) isnot(html.indexOf("controls="), -1, "Should have added the controls attribute")
|
||||
rootElement() { return document.getElementById("xxxx").contentDocument.documentElement; },
|
||||
checkResult(html) { isnot(html.indexOf("controls="), -1, "Should have added the controls attribute"); },
|
||||
},
|
||||
{
|
||||
id: "yyy",
|
||||
payload: microdataPayload,
|
||||
rootElement: function() document.getElementById("yyy"),
|
||||
rootElement() { return document.getElementById("yyy"); },
|
||||
checkResult: function(html) { is(html.indexOf("name"), -1, "Should have dropped name."); is(html.indexOf("rel"), -1, "Should have dropped rel."); isnot(html.indexOf("itemprop"), -1, "Should not have dropped itemprop."); }
|
||||
},
|
||||
{
|
||||
id: "yyyy",
|
||||
isIFrame: true,
|
||||
payload: microdataPayload,
|
||||
rootElement: function() document.getElementById("yyyy").contentDocument.documentElement,
|
||||
rootElement() { return document.getElementById("yyyy").contentDocument.documentElement; },
|
||||
checkResult: function(html) { is(html.indexOf("name"), -1, "Should have dropped name."); is(html.indexOf("rel"), -1, "Should have dropped rel."); isnot(html.indexOf("itemprop"), -1, "Should not have dropped itemprop."); }
|
||||
}
|
||||
];
|
||||
|
@ -34,7 +34,7 @@ function testTab(prefix, callback) {
|
||||
function() {
|
||||
dst.focus();
|
||||
var inputReceived = false;
|
||||
dst.addEventListener("input", function() inputReceived = true, false);
|
||||
dst.addEventListener("input", function() { inputReceived = true; }, false);
|
||||
synthesizeKey("v", {accelKey: true});
|
||||
ok(inputReceived, "An input event should be raised");
|
||||
is(dst.value, prefix + src.value, "The value should be pasted verbatim");
|
||||
|
@ -23,7 +23,7 @@ SimpleTest.waitForExplicitFinish();
|
||||
addLoadEvent(function() {
|
||||
var i = document.querySelector("input");
|
||||
var inputCount = 0;
|
||||
i.addEventListener("input", function() inputCount++, false);
|
||||
i.addEventListener("input", function() { inputCount++; }, false);
|
||||
|
||||
// test cut
|
||||
i.focus();
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "Rect.h"
|
||||
#include "ScaledFontMac.h"
|
||||
#include "Tools.h"
|
||||
#include "PathHelpers.h"
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include "MacIOSurface.h"
|
||||
@ -990,13 +991,162 @@ DrawTargetCG::FillRect(const Rect &aRect,
|
||||
CGContextRestoreGState(mCg);
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetCG::StrokeLine(const Point &p1, const Point &p2, const Pattern &aPattern, const StrokeOptions &aStrokeOptions, const DrawOptions &aDrawOptions)
|
||||
static Float
|
||||
DashPeriodLength(const StrokeOptions& aStrokeOptions)
|
||||
{
|
||||
if (!std::isfinite(p1.x) ||
|
||||
!std::isfinite(p1.y) ||
|
||||
!std::isfinite(p2.x) ||
|
||||
!std::isfinite(p2.y)) {
|
||||
Float length = 0;
|
||||
for (size_t i = 0; i < aStrokeOptions.mDashLength; i++) {
|
||||
length += aStrokeOptions.mDashPattern[i];
|
||||
}
|
||||
if (aStrokeOptions.mDashLength & 1) {
|
||||
// "If an odd number of values is provided, then the list of values is
|
||||
// repeated to yield an even number of values."
|
||||
// Double the length.
|
||||
length += length;
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
inline Float
|
||||
RoundDownToMultiple(Float aValue, Float aFactor)
|
||||
{
|
||||
return floorf(aValue / aFactor) * aFactor;
|
||||
}
|
||||
|
||||
static Rect
|
||||
UserSpaceStrokeClip(const Rect &aDeviceClip,
|
||||
const Matrix &aTransform,
|
||||
const StrokeOptions &aStrokeOptions)
|
||||
{
|
||||
Matrix inverse = aTransform;
|
||||
if (!inverse.Invert()) {
|
||||
return Rect();
|
||||
}
|
||||
Rect deviceClip = aDeviceClip;
|
||||
deviceClip.Inflate(MaxStrokeExtents(aStrokeOptions, aTransform));
|
||||
return inverse.TransformBounds(deviceClip);
|
||||
}
|
||||
|
||||
static Rect
|
||||
ShrinkClippedStrokedRect(const Rect &aStrokedRect, const Rect &aDeviceClip,
|
||||
const Matrix &aTransform,
|
||||
const StrokeOptions &aStrokeOptions)
|
||||
{
|
||||
Rect userSpaceStrokeClip =
|
||||
UserSpaceStrokeClip(aDeviceClip, aTransform, aStrokeOptions);
|
||||
|
||||
Rect intersection = aStrokedRect.Intersect(userSpaceStrokeClip);
|
||||
Float dashPeriodLength = DashPeriodLength(aStrokeOptions);
|
||||
if (intersection.IsEmpty() || dashPeriodLength == 0.0f) {
|
||||
return intersection;
|
||||
}
|
||||
|
||||
// Reduce the rectangle side lengths in multiples of the dash period length
|
||||
// so that the visible dashes stay in the same place.
|
||||
Margin insetBy = aStrokedRect - intersection;
|
||||
insetBy.top = RoundDownToMultiple(insetBy.top, dashPeriodLength);
|
||||
insetBy.right = RoundDownToMultiple(insetBy.right, dashPeriodLength);
|
||||
insetBy.bottom = RoundDownToMultiple(insetBy.bottom, dashPeriodLength);
|
||||
insetBy.left = RoundDownToMultiple(insetBy.left, dashPeriodLength);
|
||||
|
||||
Rect shrunkRect = aStrokedRect;
|
||||
shrunkRect.Deflate(insetBy);
|
||||
return shrunkRect;
|
||||
}
|
||||
|
||||
// Liang-Barsky
|
||||
// This algorithm was chosen for its code brevity, with the hope that its
|
||||
// performance is good enough.
|
||||
// Sets aStart and aEnd to floats between 0 and the line length, or returns
|
||||
// false if the line is completely outside the rect.
|
||||
static bool
|
||||
IntersectLineWithRect(const Point& aP1, const Point& aP2, const Rect& aClip,
|
||||
Float* aStart, Float* aEnd)
|
||||
{
|
||||
Float t0 = 0.0f;
|
||||
Float t1 = 1.0f;
|
||||
Point vector = aP2 - aP1;
|
||||
for (uint32_t edge = 0; edge < 4; edge++) {
|
||||
Float p, q;
|
||||
switch (edge) {
|
||||
case 0: p = -vector.x; q = aP1.x - aClip.x; break;
|
||||
case 1: p = vector.x; q = aClip.XMost() - aP1.x; break;
|
||||
case 2: p = -vector.y; q = aP1.y - aClip.y; break;
|
||||
case 3: p = vector.y; q = aClip.YMost() - aP1.y; break;
|
||||
}
|
||||
|
||||
if (p == 0.0f) {
|
||||
// Line is parallel to the edge.
|
||||
if (q < 0.0f) {
|
||||
return false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
Float r = q / p;
|
||||
if (p < 0) {
|
||||
t0 = std::max(t0, r);
|
||||
} else {
|
||||
t1 = std::min(t1, r);
|
||||
}
|
||||
|
||||
if (t0 > t1) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Float length = vector.Length();
|
||||
*aStart = t0 * length;
|
||||
*aEnd = t1 * length;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Adjusts aP1 and aP2 to a shrunk line, or returns false if the line is
|
||||
// completely outside the clip.
|
||||
static bool
|
||||
ShrinkClippedStrokedLine(Point &aP1, Point& aP2, const Rect &aDeviceClip,
|
||||
const Matrix &aTransform,
|
||||
const StrokeOptions &aStrokeOptions)
|
||||
{
|
||||
Rect userSpaceStrokeClip =
|
||||
UserSpaceStrokeClip(aDeviceClip, aTransform, aStrokeOptions);
|
||||
|
||||
Point vector = aP2 - aP1;
|
||||
Float length = vector.Length();
|
||||
|
||||
if (length == 0.0f) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Float start = 0;
|
||||
Float end = length;
|
||||
if (!IntersectLineWithRect(aP1, aP2, userSpaceStrokeClip, &start, &end)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Float dashPeriodLength = DashPeriodLength(aStrokeOptions);
|
||||
if (dashPeriodLength > 0.0f) {
|
||||
// Shift the line points by multiples of dashPeriodLength so that the
|
||||
// dashes stay in the same place.
|
||||
start = RoundDownToMultiple(start, dashPeriodLength);
|
||||
end = length - RoundDownToMultiple(length - end, dashPeriodLength);
|
||||
}
|
||||
|
||||
Point startPoint = aP1;
|
||||
aP1 = Point(startPoint.x + start * vector.x / length,
|
||||
startPoint.y + start * vector.y / length);
|
||||
aP2 = Point(startPoint.x + end * vector.x / length,
|
||||
startPoint.y + end * vector.y / length);
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetCG::StrokeLine(const Point &aP1, const Point &aP2, const Pattern &aPattern, const StrokeOptions &aStrokeOptions, const DrawOptions &aDrawOptions)
|
||||
{
|
||||
if (!std::isfinite(aP1.x) ||
|
||||
!std::isfinite(aP1.y) ||
|
||||
!std::isfinite(aP2.x) ||
|
||||
!std::isfinite(aP2.y)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1004,6 +1154,14 @@ DrawTargetCG::StrokeLine(const Point &p1, const Point &p2, const Pattern &aPatte
|
||||
return;
|
||||
}
|
||||
|
||||
Point p1 = aP1;
|
||||
Point p2 = aP2;
|
||||
|
||||
Rect deviceClip(0, 0, mSize.width, mSize.height);
|
||||
if (!ShrinkClippedStrokedLine(p1, p2, deviceClip, mTransform, aStrokeOptions)) {
|
||||
return;
|
||||
}
|
||||
|
||||
MarkChanged();
|
||||
|
||||
CGContextSaveGState(mCg);
|
||||
@ -1078,6 +1236,20 @@ DrawTargetCG::StrokeRect(const Rect &aRect,
|
||||
if (MOZ2D_ERROR_IF(!cg)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Stroking large rectangles with dashes is expensive with CG (fixed
|
||||
// overhead based on the number of dashes, regardless of whether the dashes
|
||||
// are visible), so we try to reduce the size of the stroked rectangle as
|
||||
// much as possible before passing it on to CG.
|
||||
Rect rect = aRect;
|
||||
if (!rect.IsEmpty()) {
|
||||
Rect deviceClip(0, 0, mSize.width, mSize.height);
|
||||
rect = ShrinkClippedStrokedRect(rect, deviceClip, mTransform, aStrokeOptions);
|
||||
if (rect.IsEmpty()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
CGContextSetAlpha(mCg, aDrawOptions.mAlpha);
|
||||
CGContextSetBlendMode(mCg, ToBlendMode(aDrawOptions.mCompositionOp));
|
||||
|
||||
|
@ -3,11 +3,9 @@
|
||||
* 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/. */
|
||||
|
||||
#define _USE_MATH_DEFINES
|
||||
#include <cmath>
|
||||
|
||||
#include "DrawTargetTiled.h"
|
||||
#include "Logging.h"
|
||||
#include "PathHelpers.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
@ -107,8 +105,6 @@ TILED_COMMAND(Flush)
|
||||
TILED_COMMAND4(DrawFilter, FilterNode*, const Rect&, const Point&, const DrawOptions&)
|
||||
TILED_COMMAND1(ClearRect, const Rect&)
|
||||
TILED_COMMAND4(MaskSurface, const Pattern&, SourceSurface*, Point, const DrawOptions&)
|
||||
TILED_COMMAND4(StrokeRect, const Rect&, const Pattern&, const StrokeOptions&, const DrawOptions&)
|
||||
TILED_COMMAND5(StrokeLine, const Point&, const Point&, const Pattern&, const StrokeOptions&, const DrawOptions&)
|
||||
TILED_COMMAND5(FillGlyphs, ScaledFont*, const GlyphBuffer&, const Pattern&, const DrawOptions&, const GlyphRenderingOptions*)
|
||||
TILED_COMMAND3(Mask, const Pattern&, const Pattern&, const DrawOptions&)
|
||||
|
||||
@ -232,40 +228,12 @@ DrawTargetTiled::FillRect(const Rect& aRect, const Pattern& aPattern, const Draw
|
||||
}
|
||||
}
|
||||
|
||||
// The logic for this comes from _cairo_stroke_style_max_distance_from_path
|
||||
static Rect
|
||||
PathExtentsToMaxStrokeExtents(const StrokeOptions &aStrokeOptions,
|
||||
const Rect &aRect,
|
||||
const Matrix &aTransform)
|
||||
{
|
||||
double styleExpansionFactor = 0.5f;
|
||||
|
||||
if (aStrokeOptions.mLineCap == CapStyle::SQUARE) {
|
||||
styleExpansionFactor = M_SQRT1_2;
|
||||
}
|
||||
|
||||
if (aStrokeOptions.mLineJoin == JoinStyle::MITER &&
|
||||
styleExpansionFactor < M_SQRT2 * aStrokeOptions.mMiterLimit) {
|
||||
styleExpansionFactor = M_SQRT2 * aStrokeOptions.mMiterLimit;
|
||||
}
|
||||
|
||||
styleExpansionFactor *= aStrokeOptions.mLineWidth;
|
||||
|
||||
double dx = styleExpansionFactor * hypot(aTransform._11, aTransform._21);
|
||||
double dy = styleExpansionFactor * hypot(aTransform._22, aTransform._12);
|
||||
|
||||
Rect result = aRect;
|
||||
result.Inflate(dx, dy);
|
||||
return result;
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetTiled::Stroke(const Path* aPath, const Pattern& aPattern, const StrokeOptions& aStrokeOptions, const DrawOptions& aDrawOptions)
|
||||
{
|
||||
// Approximate the stroke extents, since Path::GetStrokeExtents can be slow
|
||||
Rect deviceRect = PathExtentsToMaxStrokeExtents(aStrokeOptions,
|
||||
aPath->GetBounds(mTransform),
|
||||
mTransform);
|
||||
Rect deviceRect = aPath->GetBounds(mTransform);
|
||||
deviceRect.Inflate(MaxStrokeExtents(aStrokeOptions, mTransform));
|
||||
for (size_t i = 0; i < mTiles.size(); i++) {
|
||||
if (!mTiles[i].mClippedOut &&
|
||||
deviceRect.Intersects(Rect(mTiles[i].mTileOrigin.x,
|
||||
@ -277,6 +245,51 @@ DrawTargetTiled::Stroke(const Path* aPath, const Pattern& aPattern, const Stroke
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetTiled::StrokeRect(const Rect& aRect, const Pattern& aPattern, const StrokeOptions &aStrokeOptions, const DrawOptions& aDrawOptions)
|
||||
{
|
||||
Rect deviceRect = mTransform.TransformBounds(aRect);
|
||||
Margin strokeMargin = MaxStrokeExtents(aStrokeOptions, mTransform);
|
||||
Rect outerRect = deviceRect;
|
||||
outerRect.Inflate(strokeMargin);
|
||||
Rect innerRect;
|
||||
if (mTransform.IsRectilinear()) {
|
||||
// If rects are mapped to rects, we can compute the inner rect
|
||||
// of the stroked rect.
|
||||
innerRect = deviceRect;
|
||||
innerRect.Deflate(strokeMargin);
|
||||
}
|
||||
for (size_t i = 0; i < mTiles.size(); i++) {
|
||||
if (mTiles[i].mClippedOut) {
|
||||
continue;
|
||||
}
|
||||
Rect tileRect(mTiles[i].mTileOrigin.x,
|
||||
mTiles[i].mTileOrigin.y,
|
||||
mTiles[i].mDrawTarget->GetSize().width,
|
||||
mTiles[i].mDrawTarget->GetSize().height);
|
||||
if (outerRect.Intersects(tileRect) && !innerRect.Contains(tileRect)) {
|
||||
mTiles[i].mDrawTarget->StrokeRect(aRect, aPattern, aStrokeOptions, aDrawOptions);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetTiled::StrokeLine(const Point& aStart, const Point& aEnd, const Pattern& aPattern, const StrokeOptions &aStrokeOptions, const DrawOptions& aDrawOptions)
|
||||
{
|
||||
Rect lineBounds = Rect(aStart, Size()).UnionEdges(Rect(aEnd, Size()));
|
||||
Rect deviceRect = mTransform.TransformBounds(lineBounds);
|
||||
deviceRect.Inflate(MaxStrokeExtents(aStrokeOptions, mTransform));
|
||||
for (size_t i = 0; i < mTiles.size(); i++) {
|
||||
if (!mTiles[i].mClippedOut &&
|
||||
deviceRect.Intersects(Rect(mTiles[i].mTileOrigin.x,
|
||||
mTiles[i].mTileOrigin.y,
|
||||
mTiles[i].mDrawTarget->GetSize().width,
|
||||
mTiles[i].mDrawTarget->GetSize().height))) {
|
||||
mTiles[i].mDrawTarget->StrokeLine(aStart, aEnd, aPattern, aStrokeOptions, aDrawOptions);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetTiled::Fill(const Path* aPath, const Pattern& aPattern, const DrawOptions& aDrawOptions)
|
||||
{
|
||||
|
@ -3,6 +3,9 @@
|
||||
* 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/. */
|
||||
|
||||
#define _USE_MATH_DEFINES
|
||||
#include <cmath>
|
||||
|
||||
#include "PathHelpers.h"
|
||||
|
||||
namespace mozilla {
|
||||
@ -238,6 +241,29 @@ StrokeSnappedEdgesOfRect(const Rect& aRect, DrawTarget& aDrawTarget,
|
||||
aDrawTarget.StrokeLine(p1, p2, aColor, aStrokeOptions);
|
||||
}
|
||||
|
||||
// The logic for this comes from _cairo_stroke_style_max_distance_from_path
|
||||
Margin
|
||||
MaxStrokeExtents(const StrokeOptions& aStrokeOptions,
|
||||
const Matrix& aTransform)
|
||||
{
|
||||
double styleExpansionFactor = 0.5f;
|
||||
|
||||
if (aStrokeOptions.mLineCap == CapStyle::SQUARE) {
|
||||
styleExpansionFactor = M_SQRT1_2;
|
||||
}
|
||||
|
||||
if (aStrokeOptions.mLineJoin == JoinStyle::MITER &&
|
||||
styleExpansionFactor < M_SQRT2 * aStrokeOptions.mMiterLimit) {
|
||||
styleExpansionFactor = M_SQRT2 * aStrokeOptions.mMiterLimit;
|
||||
}
|
||||
|
||||
styleExpansionFactor *= aStrokeOptions.mLineWidth;
|
||||
|
||||
double dx = styleExpansionFactor * hypot(aTransform._11, aTransform._21);
|
||||
double dy = styleExpansionFactor * hypot(aTransform._22, aTransform._12);
|
||||
return Margin(dy, dx, dy, dx);
|
||||
}
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace mozilla
|
||||
|
||||
|
@ -283,6 +283,16 @@ GFX2D_API void StrokeSnappedEdgesOfRect(const Rect& aRect,
|
||||
const ColorPattern& aColor,
|
||||
const StrokeOptions& aStrokeOptions);
|
||||
|
||||
/**
|
||||
* Return the margin, in device space, by which a stroke can extend beyond the
|
||||
* rendered shape.
|
||||
* @param aStrokeOptions The stroke options that the stroke is drawn with.
|
||||
* @param aTransform The user space to device space transform.
|
||||
* @return The stroke margin.
|
||||
*/
|
||||
GFX2D_API Margin MaxStrokeExtents(const StrokeOptions& aStrokeOptions,
|
||||
const Matrix& aTransform);
|
||||
|
||||
extern UserDataKey sDisablePixelSnapping;
|
||||
|
||||
/**
|
||||
|
@ -109,6 +109,7 @@ UNIFIED_SOURCES += [
|
||||
'DrawTargetCapture.cpp',
|
||||
'DrawTargetDual.cpp',
|
||||
'DrawTargetRecording.cpp',
|
||||
'DrawTargetTiled.cpp',
|
||||
'Factory.cpp',
|
||||
'FilterNodeSoftware.cpp',
|
||||
'FilterProcessing.cpp',
|
||||
@ -117,7 +118,6 @@ UNIFIED_SOURCES += [
|
||||
'Matrix.cpp',
|
||||
'Path.cpp',
|
||||
'PathCairo.cpp',
|
||||
'PathHelpers.cpp',
|
||||
'PathRecording.cpp',
|
||||
'RecordedEvent.cpp',
|
||||
'Scale.cpp',
|
||||
@ -128,7 +128,7 @@ UNIFIED_SOURCES += [
|
||||
]
|
||||
|
||||
SOURCES += [
|
||||
'DrawTargetTiled.cpp',
|
||||
'PathHelpers.cpp', # Uses _USE_MATH_DEFINES
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
|
||||
|
@ -117,9 +117,6 @@ DEFINES['GL_APICALL'] = ""
|
||||
DEFINES['GL_GLEXT_PROTOTYPES'] = ""
|
||||
DEFINES['EGLAPI'] = ""
|
||||
|
||||
# ANGLE uses the STL, so we can't use our derpy STL wrappers.
|
||||
DISABLE_STL_WRAPPING = True
|
||||
|
||||
|
||||
# Only build libEGL/libGLESv2 on Windows
|
||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
|
||||
|
@ -277,21 +277,6 @@ struct ParamTraits<mozilla::layers::TextureFlags>
|
||||
mozilla::layers::TextureFlags::ALL_BITS>
|
||||
{};
|
||||
|
||||
template <>
|
||||
struct ParamTraits<mozilla::layers::TextureIdentifier>
|
||||
: public ContiguousEnumSerializer<
|
||||
mozilla::layers::TextureIdentifier,
|
||||
mozilla::layers::TextureIdentifier::Front,
|
||||
mozilla::layers::TextureIdentifier::HighBound>
|
||||
{};
|
||||
|
||||
template <>
|
||||
struct ParamTraits<mozilla::layers::DeprecatedTextureHostFlags>
|
||||
: public BitFlagsEnumSerializer<
|
||||
mozilla::layers::DeprecatedTextureHostFlags,
|
||||
mozilla::layers::DeprecatedTextureHostFlags::ALL_BITS>
|
||||
{};
|
||||
|
||||
template <>
|
||||
struct ParamTraits<mozilla::layers::DiagnosticTypes>
|
||||
: public BitFlagsEnumSerializer<
|
||||
@ -834,14 +819,12 @@ struct ParamTraits<mozilla::layers::TextureInfo>
|
||||
static void Write(Message* aMsg, const paramType& aParam)
|
||||
{
|
||||
WriteParam(aMsg, aParam.mCompositableType);
|
||||
WriteParam(aMsg, aParam.mDeprecatedTextureHostFlags);
|
||||
WriteParam(aMsg, aParam.mTextureFlags);
|
||||
}
|
||||
|
||||
static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
|
||||
{
|
||||
return ReadParam(aMsg, aIter, &aResult->mCompositableType) &&
|
||||
ReadParam(aMsg, aIter, &aResult->mDeprecatedTextureHostFlags) &&
|
||||
ReadParam(aMsg, aIter, &aResult->mTextureFlags);
|
||||
}
|
||||
};
|
||||
|
@ -133,8 +133,6 @@ enum class EffectTypes : uint8_t {
|
||||
*/
|
||||
enum class CompositableType : uint8_t {
|
||||
UNKNOWN,
|
||||
CONTENT_INC, // painted layer interface, only sends incremental
|
||||
// updates to a texture on the compositor side.
|
||||
CONTENT_TILED, // tiled painted layer
|
||||
IMAGE, // image with single buffering
|
||||
IMAGE_OVERLAY, // image without buffer
|
||||
@ -144,19 +142,6 @@ enum class CompositableType : uint8_t {
|
||||
COUNT
|
||||
};
|
||||
|
||||
/**
|
||||
* How the texture host is used for composition,
|
||||
* XXX - Only used by ContentClientIncremental
|
||||
*/
|
||||
enum class DeprecatedTextureHostFlags : uint8_t {
|
||||
DEFAULT = 0, // The default texture host for the given SurfaceDescriptor
|
||||
TILED = 1 << 0, // A texture host that supports tiling
|
||||
COPY_PREVIOUS = 1 << 1, // Texture contents should be initialized
|
||||
// from the previous texture.
|
||||
ALL_BITS = (1 << 2) - 1
|
||||
};
|
||||
MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(DeprecatedTextureHostFlags)
|
||||
|
||||
#ifdef XP_WIN
|
||||
typedef void* SyncHandle;
|
||||
#else
|
||||
@ -194,20 +179,6 @@ struct TextureFactoryIdentifier
|
||||
{}
|
||||
};
|
||||
|
||||
/**
|
||||
* Identify a texture to a compositable. Many textures can have the same id, but
|
||||
* the id is unique for any texture owned by a particular compositable.
|
||||
* XXX - We don't really need this, it will be removed along with the incremental
|
||||
* ContentClient/Host.
|
||||
*/
|
||||
enum class TextureIdentifier : uint8_t {
|
||||
Front = 1,
|
||||
Back = 2,
|
||||
OnWhiteFront = 3,
|
||||
OnWhiteBack = 4,
|
||||
HighBound
|
||||
};
|
||||
|
||||
/**
|
||||
* Information required by the compositor from the content-side for creating or
|
||||
* using compositables and textures.
|
||||
@ -218,27 +189,22 @@ enum class TextureIdentifier : uint8_t {
|
||||
struct TextureInfo
|
||||
{
|
||||
CompositableType mCompositableType;
|
||||
// XXX - only used by ContentClientIncremental
|
||||
DeprecatedTextureHostFlags mDeprecatedTextureHostFlags;
|
||||
TextureFlags mTextureFlags;
|
||||
|
||||
TextureInfo()
|
||||
: mCompositableType(CompositableType::UNKNOWN)
|
||||
, mDeprecatedTextureHostFlags(DeprecatedTextureHostFlags::DEFAULT)
|
||||
, mTextureFlags(TextureFlags::NO_FLAGS)
|
||||
{}
|
||||
|
||||
explicit TextureInfo(CompositableType aType,
|
||||
TextureFlags aTextureFlags = TextureFlags::DEFAULT)
|
||||
: mCompositableType(aType)
|
||||
, mDeprecatedTextureHostFlags(DeprecatedTextureHostFlags::DEFAULT)
|
||||
, mTextureFlags(aTextureFlags)
|
||||
{}
|
||||
|
||||
bool operator==(const TextureInfo& aOther) const
|
||||
{
|
||||
return mCompositableType == aOther.mCompositableType &&
|
||||
mDeprecatedTextureHostFlags == aOther.mDeprecatedTextureHostFlags &&
|
||||
mTextureFlags == aOther.mTextureFlags;
|
||||
}
|
||||
};
|
||||
|
@ -272,7 +272,6 @@ public:
|
||||
|
||||
struct DrawIterator {
|
||||
friend class RotatedContentBuffer;
|
||||
friend class ContentClientIncremental;
|
||||
DrawIterator()
|
||||
: mCount(0)
|
||||
{}
|
||||
|
@ -48,8 +48,6 @@ public:
|
||||
nsIntRegion* aDestRegion = nullptr,
|
||||
gfx::IntPoint* aSrcOffset = nullptr) MOZ_OVERRIDE
|
||||
{
|
||||
// XXX - For this to work with IncrementalContentHost we will need to support
|
||||
// the aDestRegion and aSrcOffset parameters properly;
|
||||
mSurface = aSurface;
|
||||
return true;
|
||||
}
|
||||
|
@ -587,17 +587,6 @@ ClientLayerManager::ForwardTransaction(bool aScheduleComposite)
|
||||
|
||||
break;
|
||||
}
|
||||
case EditReply::TOpTextureSwap: {
|
||||
MOZ_LAYERS_LOG(("[LayersForwarder] TextureSwap"));
|
||||
|
||||
const OpTextureSwap& ots = reply.get_OpTextureSwap();
|
||||
|
||||
CompositableClient* compositable =
|
||||
CompositableClient::FromIPDLActor(ots.compositableChild());
|
||||
MOZ_ASSERT(compositable);
|
||||
compositable->SetDescriptorFromReply(ots.textureId(), ots.image());
|
||||
break;
|
||||
}
|
||||
case EditReply::TReturnReleaseFence: {
|
||||
const ReturnReleaseFence& rep = reply.get_ReturnReleaseFence();
|
||||
FenceHandle fence = rep.fence();
|
||||
|
@ -143,12 +143,6 @@ public:
|
||||
TextureFlags aTextureFlags,
|
||||
TextureAllocationFlags aAllocFlags = ALLOC_DEFAULT);
|
||||
|
||||
virtual void SetDescriptorFromReply(TextureIdentifier aTextureId,
|
||||
const SurfaceDescriptor& aDescriptor)
|
||||
{
|
||||
MOZ_CRASH("If you want to call this, you should have implemented it");
|
||||
}
|
||||
|
||||
/**
|
||||
* Establishes the connection with compositor side through IPDL
|
||||
*/
|
||||
|
@ -93,11 +93,6 @@ ContentClient::CreateContentClient(CompositableForwarder* aForwarder)
|
||||
if (useDoubleBuffering || PR_GetEnv("MOZ_FORCE_DOUBLE_BUFFERING")) {
|
||||
return new ContentClientDoubleBuffered(aForwarder);
|
||||
}
|
||||
#ifdef XP_MACOSX
|
||||
if (backend == LayersBackend::LAYERS_OPENGL) {
|
||||
return new ContentClientIncremental(aForwarder);
|
||||
}
|
||||
#endif
|
||||
return new ContentClientSingleBuffered(aForwarder);
|
||||
}
|
||||
|
||||
@ -673,362 +668,5 @@ ContentClientSingleBuffered::FinalizeFrame(const nsIntRegion& aRegionToDraw)
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
WrapRotationAxis(int32_t* aRotationPoint, int32_t aSize)
|
||||
{
|
||||
if (*aRotationPoint < 0) {
|
||||
*aRotationPoint += aSize;
|
||||
} else if (*aRotationPoint >= aSize) {
|
||||
*aRotationPoint -= aSize;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
FillSurface(DrawTarget* aDT, const nsIntRegion& aRegion,
|
||||
const nsIntPoint& aOffset, const gfxRGBA& aColor)
|
||||
{
|
||||
nsIntRegionRectIterator iter(aRegion);
|
||||
const nsIntRect* r;
|
||||
while ((r = iter.Next()) != nullptr) {
|
||||
aDT->FillRect(Rect(r->x - aOffset.x, r->y - aOffset.y,
|
||||
r->width, r->height),
|
||||
ColorPattern(ToColor(aColor)));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ContentClientIncremental::NotifyBufferCreated(ContentType aType, TextureFlags aFlags)
|
||||
{
|
||||
mTextureInfo.mTextureFlags = aFlags;
|
||||
mContentType = aType;
|
||||
|
||||
mForwarder->CreatedIncrementalBuffer(this,
|
||||
mTextureInfo,
|
||||
mBufferRect);
|
||||
|
||||
}
|
||||
|
||||
RotatedContentBuffer::PaintState
|
||||
ContentClientIncremental::BeginPaintBuffer(PaintedLayer* aLayer,
|
||||
uint32_t aFlags)
|
||||
{
|
||||
mTextureInfo.mDeprecatedTextureHostFlags = DeprecatedTextureHostFlags::DEFAULT;
|
||||
PaintState result;
|
||||
// We need to disable rotation if we're going to be resampled when
|
||||
// drawing, because we might sample across the rotation boundary.
|
||||
bool canHaveRotation = !(aFlags & RotatedContentBuffer::PAINT_WILL_RESAMPLE);
|
||||
|
||||
nsIntRegion validRegion = aLayer->GetValidRegion();
|
||||
|
||||
bool canUseOpaqueSurface = aLayer->CanUseOpaqueSurface();
|
||||
ContentType contentType =
|
||||
canUseOpaqueSurface ? gfxContentType::COLOR :
|
||||
gfxContentType::COLOR_ALPHA;
|
||||
|
||||
SurfaceMode mode;
|
||||
nsIntRegion neededRegion;
|
||||
bool canReuseBuffer;
|
||||
nsIntRect destBufferRect;
|
||||
|
||||
while (true) {
|
||||
mode = aLayer->GetSurfaceMode();
|
||||
neededRegion = aLayer->GetVisibleRegion();
|
||||
// If we're going to resample, we need a buffer that's in clamp mode.
|
||||
canReuseBuffer = neededRegion.GetBounds().Size() <= mBufferRect.Size() &&
|
||||
mHasBuffer && !(aFlags & RotatedContentBuffer::PAINT_WILL_RESAMPLE);
|
||||
|
||||
if (canReuseBuffer) {
|
||||
if (mBufferRect.Contains(neededRegion.GetBounds())) {
|
||||
// We don't need to adjust mBufferRect.
|
||||
destBufferRect = mBufferRect;
|
||||
} else {
|
||||
// The buffer's big enough but doesn't contain everything that's
|
||||
// going to be visible. We'll move it.
|
||||
destBufferRect = nsIntRect(neededRegion.GetBounds().TopLeft(), mBufferRect.Size());
|
||||
}
|
||||
} else {
|
||||
destBufferRect = neededRegion.GetBounds();
|
||||
}
|
||||
|
||||
if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
|
||||
if (!gfxPrefs::ComponentAlphaEnabled() ||
|
||||
!aLayer->GetParent() ||
|
||||
!aLayer->GetParent()->SupportsComponentAlphaChildren()) {
|
||||
mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA;
|
||||
} else {
|
||||
contentType = gfxContentType::COLOR;
|
||||
}
|
||||
}
|
||||
|
||||
if ((aFlags & RotatedContentBuffer::PAINT_WILL_RESAMPLE) &&
|
||||
(!neededRegion.GetBounds().IsEqualInterior(destBufferRect) ||
|
||||
neededRegion.GetNumRects() > 1)) {
|
||||
// The area we add to neededRegion might not be painted opaquely
|
||||
if (mode == SurfaceMode::SURFACE_OPAQUE) {
|
||||
contentType = gfxContentType::COLOR_ALPHA;
|
||||
mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA;
|
||||
}
|
||||
// For component alpha layers, we leave contentType as gfxContentType::COLOR.
|
||||
|
||||
// We need to validate the entire buffer, to make sure that only valid
|
||||
// pixels are sampled
|
||||
neededRegion = destBufferRect;
|
||||
}
|
||||
|
||||
if (mHasBuffer &&
|
||||
(mContentType != contentType ||
|
||||
(mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) != mHasBufferOnWhite)) {
|
||||
#ifdef MOZ_DUMP_PAINTING
|
||||
if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
|
||||
if (mContentType != contentType) {
|
||||
printf_stderr("Layer's content type has changed\n");
|
||||
}
|
||||
if ((mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) != mHasBufferOnWhite) {
|
||||
printf_stderr("Layer's component alpha status has changed\n");
|
||||
}
|
||||
printf_stderr("Invalidating entire layer %p: no buffer, or content type or component alpha changed\n", aLayer);
|
||||
}
|
||||
#endif
|
||||
// We're effectively clearing the valid region, so we need to draw
|
||||
// the entire needed region now.
|
||||
result.mRegionToInvalidate = aLayer->GetValidRegion();
|
||||
validRegion.SetEmpty();
|
||||
mHasBuffer = false;
|
||||
mHasBufferOnWhite = false;
|
||||
mBufferRect.SetRect(0, 0, 0, 0);
|
||||
mBufferRotation.MoveTo(0, 0);
|
||||
// Restart decision process with the cleared buffer. We can only go
|
||||
// around the loop one more iteration, since mTexImage is null now.
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
result.mRegionToDraw.Sub(neededRegion, validRegion);
|
||||
if (result.mRegionToDraw.IsEmpty())
|
||||
return result;
|
||||
|
||||
if (destBufferRect.width > mForwarder->GetMaxTextureSize() ||
|
||||
destBufferRect.height > mForwarder->GetMaxTextureSize()) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// BlitTextureImage depends on the FBO texture target being
|
||||
// TEXTURE_2D. This isn't the case on some older X1600-era Radeons.
|
||||
if (!mForwarder->SupportsTextureBlitting() ||
|
||||
!mForwarder->SupportsPartialUploads()) {
|
||||
result.mRegionToDraw = neededRegion;
|
||||
validRegion.SetEmpty();
|
||||
mHasBuffer = false;
|
||||
mHasBufferOnWhite = false;
|
||||
mBufferRect.SetRect(0, 0, 0, 0);
|
||||
mBufferRotation.MoveTo(0, 0);
|
||||
canReuseBuffer = false;
|
||||
}
|
||||
|
||||
nsIntRect drawBounds = result.mRegionToDraw.GetBounds();
|
||||
bool createdBuffer = false;
|
||||
|
||||
TextureFlags bufferFlags = TextureFlags::NO_FLAGS;
|
||||
if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
|
||||
bufferFlags |= TextureFlags::COMPONENT_ALPHA;
|
||||
}
|
||||
if (canReuseBuffer) {
|
||||
nsIntRect keepArea;
|
||||
if (keepArea.IntersectRect(destBufferRect, mBufferRect)) {
|
||||
// Set mBufferRotation so that the pixels currently in mBuffer
|
||||
// will still be rendered in the right place when mBufferRect
|
||||
// changes to destBufferRect.
|
||||
nsIntPoint newRotation = mBufferRotation +
|
||||
(destBufferRect.TopLeft() - mBufferRect.TopLeft());
|
||||
WrapRotationAxis(&newRotation.x, mBufferRect.width);
|
||||
WrapRotationAxis(&newRotation.y, mBufferRect.height);
|
||||
NS_ASSERTION(nsIntRect(nsIntPoint(0,0), mBufferRect.Size()).Contains(newRotation),
|
||||
"newRotation out of bounds");
|
||||
int32_t xBoundary = destBufferRect.XMost() - newRotation.x;
|
||||
int32_t yBoundary = destBufferRect.YMost() - newRotation.y;
|
||||
if ((drawBounds.x < xBoundary && xBoundary < drawBounds.XMost()) ||
|
||||
(drawBounds.y < yBoundary && yBoundary < drawBounds.YMost()) ||
|
||||
(newRotation != nsIntPoint(0,0) && !canHaveRotation)) {
|
||||
// The stuff we need to redraw will wrap around an edge of the
|
||||
// buffer, so we will need to do a self-copy
|
||||
// If mBufferRotation == nsIntPoint(0,0) we could do a real
|
||||
// self-copy but we're not going to do that in GL yet.
|
||||
// We can't do a real self-copy because the buffer is rotated.
|
||||
// So allocate a new buffer for the destination.
|
||||
destBufferRect = neededRegion.GetBounds();
|
||||
createdBuffer = true;
|
||||
} else {
|
||||
mBufferRect = destBufferRect;
|
||||
mBufferRotation = newRotation;
|
||||
}
|
||||
} else {
|
||||
// No pixels are going to be kept. The whole visible region
|
||||
// will be redrawn, so we don't need to copy anything, so we don't
|
||||
// set destBuffer.
|
||||
mBufferRect = destBufferRect;
|
||||
mBufferRotation = nsIntPoint(0,0);
|
||||
}
|
||||
} else {
|
||||
// The buffer's not big enough, so allocate a new one
|
||||
createdBuffer = true;
|
||||
}
|
||||
NS_ASSERTION(!(aFlags & RotatedContentBuffer::PAINT_WILL_RESAMPLE) ||
|
||||
destBufferRect == neededRegion.GetBounds(),
|
||||
"If we're resampling, we need to validate the entire buffer");
|
||||
|
||||
if (!createdBuffer && !mHasBuffer) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (createdBuffer) {
|
||||
if (mHasBuffer &&
|
||||
(mode != SurfaceMode::SURFACE_COMPONENT_ALPHA || mHasBufferOnWhite)) {
|
||||
mTextureInfo.mDeprecatedTextureHostFlags = DeprecatedTextureHostFlags::COPY_PREVIOUS;
|
||||
}
|
||||
|
||||
mHasBuffer = true;
|
||||
if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
|
||||
mHasBufferOnWhite = true;
|
||||
}
|
||||
mBufferRect = destBufferRect;
|
||||
mBufferRotation = nsIntPoint(0,0);
|
||||
NotifyBufferCreated(contentType, bufferFlags);
|
||||
}
|
||||
|
||||
NS_ASSERTION(canHaveRotation || mBufferRotation == nsIntPoint(0,0),
|
||||
"Rotation disabled, but we have nonzero rotation?");
|
||||
|
||||
nsIntRegion invalidate;
|
||||
invalidate.Sub(aLayer->GetValidRegion(), destBufferRect);
|
||||
result.mRegionToInvalidate.Or(result.mRegionToInvalidate, invalidate);
|
||||
|
||||
// If we do partial updates, we have to clip drawing to the regionToDraw.
|
||||
// If we don't clip, background images will be fillrect'd to the region correctly,
|
||||
// while text or lines will paint outside of the regionToDraw. This becomes apparent
|
||||
// with concave regions. Right now the scrollbars invalidate a narrow strip of the bar
|
||||
// although they never cover it. This leads to two draw rects, the narow strip and the actually
|
||||
// newly exposed area. It would be wise to fix this glitch in any way to have simpler
|
||||
// clip and draw regions.
|
||||
result.mClip = DrawRegionClip::DRAW;
|
||||
result.mMode = mode;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
DrawTarget*
|
||||
ContentClientIncremental::BorrowDrawTargetForPainting(PaintState& aPaintState,
|
||||
RotatedContentBuffer::DrawIterator* aIter)
|
||||
{
|
||||
if (aPaintState.mMode == SurfaceMode::SURFACE_NONE) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (aIter) {
|
||||
if (aIter->mCount++ > 0) {
|
||||
return nullptr;
|
||||
}
|
||||
aIter->mDrawRegion = aPaintState.mRegionToDraw;
|
||||
}
|
||||
|
||||
DrawTarget* result = nullptr;
|
||||
|
||||
nsIntRect drawBounds = aPaintState.mRegionToDraw.GetBounds();
|
||||
MOZ_ASSERT(!mLoanedDrawTarget);
|
||||
|
||||
// BeginUpdate is allowed to modify the given region,
|
||||
// if it wants more to be repainted than we request.
|
||||
if (aPaintState.mMode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
|
||||
nsIntRegion drawRegionCopy = aPaintState.mRegionToDraw;
|
||||
RefPtr<DrawTarget> onBlack = GetUpdateSurface(BUFFER_BLACK, drawRegionCopy);
|
||||
RefPtr<DrawTarget> onWhite = GetUpdateSurface(BUFFER_WHITE, aPaintState.mRegionToDraw);
|
||||
if (onBlack && onWhite) {
|
||||
NS_ASSERTION(aPaintState.mRegionToDraw == drawRegionCopy,
|
||||
"BeginUpdate should always modify the draw region in the same way!");
|
||||
FillSurface(onBlack, aPaintState.mRegionToDraw, nsIntPoint(drawBounds.x, drawBounds.y), gfxRGBA(0.0, 0.0, 0.0, 1.0));
|
||||
FillSurface(onWhite, aPaintState.mRegionToDraw, nsIntPoint(drawBounds.x, drawBounds.y), gfxRGBA(1.0, 1.0, 1.0, 1.0));
|
||||
mLoanedDrawTarget = Factory::CreateDualDrawTarget(onBlack, onWhite);
|
||||
} else {
|
||||
mLoanedDrawTarget = nullptr;
|
||||
}
|
||||
} else {
|
||||
mLoanedDrawTarget = GetUpdateSurface(BUFFER_BLACK, aPaintState.mRegionToDraw);
|
||||
}
|
||||
if (!mLoanedDrawTarget) {
|
||||
NS_WARNING("unable to get context for update");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
result = mLoanedDrawTarget;
|
||||
mLoanedTransform = mLoanedDrawTarget->GetTransform();
|
||||
result->SetTransform(Matrix(mLoanedTransform).
|
||||
PreTranslate(-drawBounds.x, -drawBounds.y));
|
||||
|
||||
if (mContentType == gfxContentType::COLOR_ALPHA) {
|
||||
gfxUtils::ClipToRegion(result, aPaintState.mRegionToDraw);
|
||||
nsIntRect bounds = aPaintState.mRegionToDraw.GetBounds();
|
||||
result->ClearRect(Rect(bounds.x, bounds.y, bounds.width, bounds.height));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void
|
||||
ContentClientIncremental::Updated(const nsIntRegion& aRegionToDraw,
|
||||
const nsIntRegion& aVisibleRegion,
|
||||
bool aDidSelfCopy)
|
||||
{
|
||||
if (IsSurfaceDescriptorValid(mUpdateDescriptor)) {
|
||||
mForwarder->UpdateTextureIncremental(this,
|
||||
TextureIdentifier::Front,
|
||||
mUpdateDescriptor,
|
||||
aRegionToDraw,
|
||||
mBufferRect,
|
||||
mBufferRotation);
|
||||
mUpdateDescriptor = SurfaceDescriptor();
|
||||
}
|
||||
if (IsSurfaceDescriptorValid(mUpdateDescriptorOnWhite)) {
|
||||
mForwarder->UpdateTextureIncremental(this,
|
||||
TextureIdentifier::OnWhiteFront,
|
||||
mUpdateDescriptorOnWhite,
|
||||
aRegionToDraw,
|
||||
mBufferRect,
|
||||
mBufferRotation);
|
||||
mUpdateDescriptorOnWhite = SurfaceDescriptor();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
TemporaryRef<DrawTarget>
|
||||
ContentClientIncremental::GetUpdateSurface(BufferType aType,
|
||||
const nsIntRegion& aUpdateRegion)
|
||||
{
|
||||
nsIntRect rgnSize = aUpdateRegion.GetBounds();
|
||||
if (!mBufferRect.Contains(rgnSize)) {
|
||||
NS_ERROR("update outside of image");
|
||||
return nullptr;
|
||||
}
|
||||
SurfaceDescriptor desc;
|
||||
if (!mForwarder->AllocSurfaceDescriptor(rgnSize.Size().ToIntSize(),
|
||||
mContentType,
|
||||
&desc)) {
|
||||
NS_WARNING("creating SurfaceDescriptor failed!");
|
||||
Clear();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (aType == BUFFER_BLACK) {
|
||||
MOZ_ASSERT(!IsSurfaceDescriptorValid(mUpdateDescriptor));
|
||||
mUpdateDescriptor = desc;
|
||||
} else {
|
||||
MOZ_ASSERT(!IsSurfaceDescriptorValid(mUpdateDescriptorOnWhite));
|
||||
MOZ_ASSERT(aType == BUFFER_WHITE);
|
||||
mUpdateDescriptorOnWhite = desc;
|
||||
}
|
||||
|
||||
return GetDrawTargetForDescriptor(desc, gfx::BackendType::COREGRAPHICS);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -397,89 +397,6 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* A single buffered ContentClient that creates temporary buffers which are
|
||||
* used to update the host-side texture. The ownership of the buffers is
|
||||
* passed to the host side during the transaction, and we need to create
|
||||
* new ones each frame.
|
||||
*/
|
||||
class ContentClientIncremental : public ContentClientRemote
|
||||
, public BorrowDrawTarget
|
||||
{
|
||||
public:
|
||||
explicit ContentClientIncremental(CompositableForwarder* aFwd)
|
||||
: ContentClientRemote(aFwd)
|
||||
, mContentType(gfxContentType::COLOR_ALPHA)
|
||||
, mHasBuffer(false)
|
||||
, mHasBufferOnWhite(false)
|
||||
{
|
||||
mTextureInfo.mCompositableType = CompositableType::CONTENT_INC;
|
||||
}
|
||||
|
||||
typedef RotatedContentBuffer::PaintState PaintState;
|
||||
typedef RotatedContentBuffer::ContentType ContentType;
|
||||
|
||||
virtual TextureInfo GetTextureInfo() const MOZ_OVERRIDE
|
||||
{
|
||||
return mTextureInfo;
|
||||
}
|
||||
|
||||
virtual void Clear() MOZ_OVERRIDE
|
||||
{
|
||||
mBufferRect.SetEmpty();
|
||||
mHasBuffer = false;
|
||||
mHasBufferOnWhite = false;
|
||||
}
|
||||
|
||||
virtual PaintState BeginPaintBuffer(PaintedLayer* aLayer,
|
||||
uint32_t aFlags) MOZ_OVERRIDE;
|
||||
virtual gfx::DrawTarget* BorrowDrawTargetForPainting(PaintState& aPaintState,
|
||||
RotatedContentBuffer::DrawIterator* aIter = nullptr) MOZ_OVERRIDE;
|
||||
virtual void ReturnDrawTargetToBuffer(gfx::DrawTarget*& aReturned) MOZ_OVERRIDE
|
||||
{
|
||||
BorrowDrawTarget::ReturnDrawTarget(aReturned);
|
||||
}
|
||||
|
||||
virtual void Updated(const nsIntRegion& aRegionToDraw,
|
||||
const nsIntRegion& aVisibleRegion,
|
||||
bool aDidSelfCopy) MOZ_OVERRIDE;
|
||||
|
||||
virtual void EndPaint(nsTArray<ReadbackProcessor::Update>* aReadbackUpdates = nullptr) MOZ_OVERRIDE
|
||||
{
|
||||
if (IsSurfaceDescriptorValid(mUpdateDescriptor)) {
|
||||
mForwarder->DestroySharedSurface(&mUpdateDescriptor);
|
||||
}
|
||||
if (IsSurfaceDescriptorValid(mUpdateDescriptorOnWhite)) {
|
||||
mForwarder->DestroySharedSurface(&mUpdateDescriptorOnWhite);
|
||||
}
|
||||
ContentClientRemote::EndPaint(aReadbackUpdates);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
enum BufferType{
|
||||
BUFFER_BLACK,
|
||||
BUFFER_WHITE
|
||||
};
|
||||
|
||||
void NotifyBufferCreated(ContentType aType, TextureFlags aFlags);
|
||||
|
||||
TemporaryRef<gfx::DrawTarget> GetUpdateSurface(BufferType aType,
|
||||
const nsIntRegion& aUpdateRegion);
|
||||
|
||||
TextureInfo mTextureInfo;
|
||||
nsIntRect mBufferRect;
|
||||
nsIntPoint mBufferRotation;
|
||||
|
||||
SurfaceDescriptor mUpdateDescriptor;
|
||||
SurfaceDescriptor mUpdateDescriptorOnWhite;
|
||||
|
||||
ContentType mContentType;
|
||||
|
||||
bool mHasBuffer;
|
||||
bool mHasBufferOnWhite;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -181,9 +181,6 @@ CompositableHost::Create(const TextureInfo& aTextureInfo)
|
||||
case CompositableType::IMAGE_BRIDGE:
|
||||
NS_ERROR("Cannot create an image bridge compositable this way");
|
||||
break;
|
||||
case CompositableType::CONTENT_INC:
|
||||
result = new ContentHostIncremental(aTextureInfo);
|
||||
break;
|
||||
case CompositableType::CONTENT_TILED:
|
||||
result = new TiledContentHost(aTextureInfo);
|
||||
break;
|
||||
|
@ -101,45 +101,6 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the content host using a surface that only contains the updated
|
||||
* region.
|
||||
*
|
||||
* Takes ownership of aSurface, and is responsible for freeing it.
|
||||
*
|
||||
* @param aTextureId Texture to update.
|
||||
* @param aSurface Surface containing the update area. Its contents are relative
|
||||
* to aUpdated.TopLeft()
|
||||
* @param aUpdated Area of the content host to update.
|
||||
* @param aBufferRect New area covered by the content host.
|
||||
* @param aBufferRotation New buffer rotation.
|
||||
*/
|
||||
virtual void UpdateIncremental(TextureIdentifier aTextureId,
|
||||
SurfaceDescriptor& aSurface,
|
||||
const nsIntRegion& aUpdated,
|
||||
const nsIntRect& aBufferRect,
|
||||
const nsIntPoint& aBufferRotation)
|
||||
{
|
||||
MOZ_ASSERT(false, "should be implemented or not used");
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that a suitable texture host exists in this compsitable.
|
||||
*
|
||||
* Only used with ContentHostIncremental.
|
||||
*
|
||||
* No SurfaceDescriptor or TextureIdentifier is provider as we
|
||||
* don't have a single surface for the texture contents, and we
|
||||
* need to allocate our own one to be updated later.
|
||||
*/
|
||||
virtual bool CreatedIncrementalTexture(ISurfaceAllocator* aAllocator,
|
||||
const TextureInfo& aTextureInfo,
|
||||
const nsIntRect& aBufferRect)
|
||||
{
|
||||
NS_ERROR("should be implemented or not used");
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the front buffer.
|
||||
*/
|
||||
|
@ -388,429 +388,6 @@ ContentHostDoubleBuffered::UpdateThebes(const ThebesBufferData& aData,
|
||||
return true;
|
||||
}
|
||||
|
||||
ContentHostIncremental::ContentHostIncremental(const TextureInfo& aTextureInfo)
|
||||
: ContentHostBase(aTextureInfo)
|
||||
, mDeAllocator(nullptr)
|
||||
, mLocked(false)
|
||||
{
|
||||
}
|
||||
|
||||
ContentHostIncremental::~ContentHostIncremental()
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
ContentHostIncremental::CreatedIncrementalTexture(ISurfaceAllocator* aAllocator,
|
||||
const TextureInfo& aTextureInfo,
|
||||
const nsIntRect& aBufferRect)
|
||||
{
|
||||
mUpdateList.AppendElement(new TextureCreationRequest(aTextureInfo,
|
||||
aBufferRect));
|
||||
mDeAllocator = aAllocator;
|
||||
FlushUpdateQueue();
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
ContentHostIncremental::UpdateIncremental(TextureIdentifier aTextureId,
|
||||
SurfaceDescriptor& aSurface,
|
||||
const nsIntRegion& aUpdated,
|
||||
const nsIntRect& aBufferRect,
|
||||
const nsIntPoint& aBufferRotation)
|
||||
{
|
||||
mUpdateList.AppendElement(new TextureUpdateRequest(mDeAllocator,
|
||||
aTextureId,
|
||||
aSurface,
|
||||
aUpdated,
|
||||
aBufferRect,
|
||||
aBufferRotation));
|
||||
FlushUpdateQueue();
|
||||
}
|
||||
|
||||
void
|
||||
ContentHostIncremental::Composite(EffectChain& aEffectChain,
|
||||
float aOpacity,
|
||||
const gfx::Matrix4x4& aTransform,
|
||||
const Filter& aFilter,
|
||||
const Rect& aClipRect,
|
||||
const nsIntRegion* aVisibleRegion)
|
||||
{
|
||||
NS_ASSERTION(aVisibleRegion, "Requires a visible region");
|
||||
|
||||
AutoLockCompositableHost lock(this);
|
||||
if (lock.Failed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mSource) {
|
||||
return;
|
||||
}
|
||||
|
||||
RefPtr<TexturedEffect> effect = CreateTexturedEffect(mSource.get(),
|
||||
mSourceOnWhite.get(),
|
||||
aFilter, true);
|
||||
if (!effect) {
|
||||
return;
|
||||
}
|
||||
|
||||
aEffectChain.mPrimaryEffect = effect;
|
||||
|
||||
nsIntRegion tmpRegion;
|
||||
const nsIntRegion* renderRegion;
|
||||
if (PaintWillResample()) {
|
||||
// If we're resampling, then the texture image will contain exactly the
|
||||
// entire visible region's bounds, and we should draw it all in one quad
|
||||
// to avoid unexpected aliasing.
|
||||
tmpRegion = aVisibleRegion->GetBounds();
|
||||
renderRegion = &tmpRegion;
|
||||
} else {
|
||||
renderRegion = aVisibleRegion;
|
||||
}
|
||||
|
||||
nsIntRegion region(*renderRegion);
|
||||
nsIntPoint origin = GetOriginOffset();
|
||||
// translate into TexImage space, buffer origin might not be at texture (0,0)
|
||||
region.MoveBy(-origin);
|
||||
|
||||
// Figure out the intersecting draw region
|
||||
gfx::IntSize texSize = mSource->GetSize();
|
||||
nsIntRect textureRect = nsIntRect(0, 0, texSize.width, texSize.height);
|
||||
textureRect.MoveBy(region.GetBounds().TopLeft());
|
||||
nsIntRegion subregion;
|
||||
subregion.And(region, textureRect);
|
||||
if (subregion.IsEmpty()) {
|
||||
// Region is empty, nothing to draw
|
||||
return;
|
||||
}
|
||||
|
||||
nsIntRegion screenRects;
|
||||
nsIntRegion regionRects;
|
||||
|
||||
// Collect texture/screen coordinates for drawing
|
||||
nsIntRegionRectIterator iter(subregion);
|
||||
while (const nsIntRect* iterRect = iter.Next()) {
|
||||
nsIntRect regionRect = *iterRect;
|
||||
nsIntRect screenRect = regionRect;
|
||||
screenRect.MoveBy(origin);
|
||||
|
||||
screenRects.Or(screenRects, screenRect);
|
||||
regionRects.Or(regionRects, regionRect);
|
||||
}
|
||||
|
||||
BigImageIterator* bigImgIter = mSource->AsBigImageIterator();
|
||||
BigImageIterator* iterOnWhite = nullptr;
|
||||
if (bigImgIter) {
|
||||
bigImgIter->BeginBigImageIteration();
|
||||
}
|
||||
|
||||
if (mSourceOnWhite) {
|
||||
iterOnWhite = mSourceOnWhite->AsBigImageIterator();
|
||||
MOZ_ASSERT(!bigImgIter || bigImgIter->GetTileCount() == iterOnWhite->GetTileCount(),
|
||||
"Tile count mismatch on component alpha texture");
|
||||
if (iterOnWhite) {
|
||||
iterOnWhite->BeginBigImageIteration();
|
||||
}
|
||||
}
|
||||
|
||||
bool usingTiles = (bigImgIter && bigImgIter->GetTileCount() > 1);
|
||||
do {
|
||||
if (iterOnWhite) {
|
||||
MOZ_ASSERT(iterOnWhite->GetTileRect() == bigImgIter->GetTileRect(),
|
||||
"component alpha textures should be the same size.");
|
||||
}
|
||||
|
||||
nsIntRect texRect = bigImgIter ? bigImgIter->GetTileRect()
|
||||
: nsIntRect(0, 0,
|
||||
texSize.width,
|
||||
texSize.height);
|
||||
|
||||
// Draw texture. If we're using tiles, we do repeating manually, as texture
|
||||
// repeat would cause each individual tile to repeat instead of the
|
||||
// compound texture as a whole. This involves drawing at most 4 sections,
|
||||
// 2 for each axis that has texture repeat.
|
||||
for (int y = 0; y < (usingTiles ? 2 : 1); y++) {
|
||||
for (int x = 0; x < (usingTiles ? 2 : 1); x++) {
|
||||
nsIntRect currentTileRect(texRect);
|
||||
currentTileRect.MoveBy(x * texSize.width, y * texSize.height);
|
||||
|
||||
nsIntRegionRectIterator screenIter(screenRects);
|
||||
nsIntRegionRectIterator regionIter(regionRects);
|
||||
|
||||
const nsIntRect* screenRect;
|
||||
const nsIntRect* regionRect;
|
||||
while ((screenRect = screenIter.Next()) &&
|
||||
(regionRect = regionIter.Next())) {
|
||||
nsIntRect tileScreenRect(*screenRect);
|
||||
nsIntRect tileRegionRect(*regionRect);
|
||||
|
||||
// When we're using tiles, find the intersection between the tile
|
||||
// rect and this region rect. Tiling is then handled by the
|
||||
// outer for-loops and modifying the tile rect.
|
||||
if (usingTiles) {
|
||||
tileScreenRect.MoveBy(-origin);
|
||||
tileScreenRect = tileScreenRect.Intersect(currentTileRect);
|
||||
tileScreenRect.MoveBy(origin);
|
||||
|
||||
if (tileScreenRect.IsEmpty())
|
||||
continue;
|
||||
|
||||
tileRegionRect = regionRect->Intersect(currentTileRect);
|
||||
tileRegionRect.MoveBy(-currentTileRect.TopLeft());
|
||||
}
|
||||
gfx::Rect rect(tileScreenRect.x, tileScreenRect.y,
|
||||
tileScreenRect.width, tileScreenRect.height);
|
||||
|
||||
effect->mTextureCoords = Rect(Float(tileRegionRect.x) / texRect.width,
|
||||
Float(tileRegionRect.y) / texRect.height,
|
||||
Float(tileRegionRect.width) / texRect.width,
|
||||
Float(tileRegionRect.height) / texRect.height);
|
||||
GetCompositor()->DrawQuad(rect, aClipRect, aEffectChain, aOpacity, aTransform);
|
||||
if (usingTiles) {
|
||||
DiagnosticFlags diagnostics = DiagnosticFlags::CONTENT | DiagnosticFlags::BIGIMAGE;
|
||||
if (iterOnWhite) {
|
||||
diagnostics |= DiagnosticFlags::COMPONENT_ALPHA;
|
||||
}
|
||||
GetCompositor()->DrawDiagnostics(diagnostics, rect, aClipRect,
|
||||
aTransform, mFlashCounter);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (iterOnWhite) {
|
||||
iterOnWhite->NextTile();
|
||||
}
|
||||
} while (usingTiles && bigImgIter->NextTile());
|
||||
|
||||
if (bigImgIter) {
|
||||
bigImgIter->EndBigImageIteration();
|
||||
}
|
||||
if (iterOnWhite) {
|
||||
iterOnWhite->EndBigImageIteration();
|
||||
}
|
||||
|
||||
DiagnosticFlags diagnostics = DiagnosticFlags::CONTENT;
|
||||
if (iterOnWhite) {
|
||||
diagnostics |= DiagnosticFlags::COMPONENT_ALPHA;
|
||||
}
|
||||
GetCompositor()->DrawDiagnostics(diagnostics, nsIntRegion(mBufferRect), aClipRect,
|
||||
aTransform, mFlashCounter);
|
||||
}
|
||||
|
||||
void
|
||||
ContentHostIncremental::FlushUpdateQueue()
|
||||
{
|
||||
// If we're not compositing for some reason (the window being minimized
|
||||
// is one example), then we never process these updates and it can consume
|
||||
// huge amounts of memory. Instead we forcibly process the updates (during the
|
||||
// transaction) if the list gets too long.
|
||||
static const uint32_t kMaxUpdateCount = 6;
|
||||
if (mUpdateList.Length() >= kMaxUpdateCount) {
|
||||
ProcessTextureUpdates();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ContentHostIncremental::ProcessTextureUpdates()
|
||||
{
|
||||
for (uint32_t i = 0; i < mUpdateList.Length(); i++) {
|
||||
mUpdateList[i]->Execute(this);
|
||||
}
|
||||
mUpdateList.Clear();
|
||||
}
|
||||
|
||||
void
|
||||
ContentHostIncremental::TextureCreationRequest::Execute(ContentHostIncremental* aHost)
|
||||
{
|
||||
Compositor* compositor = aHost->GetCompositor();
|
||||
MOZ_ASSERT(compositor);
|
||||
|
||||
RefPtr<DataTextureSource> temp =
|
||||
compositor->CreateDataTextureSource(mTextureInfo.mTextureFlags);
|
||||
MOZ_ASSERT(temp->AsSourceOGL() &&
|
||||
temp->AsSourceOGL()->AsTextureImageTextureSource());
|
||||
RefPtr<TextureImageTextureSourceOGL> newSource =
|
||||
temp->AsSourceOGL()->AsTextureImageTextureSource();
|
||||
|
||||
RefPtr<TextureImageTextureSourceOGL> newSourceOnWhite;
|
||||
if (mTextureInfo.mTextureFlags & TextureFlags::COMPONENT_ALPHA) {
|
||||
temp =
|
||||
compositor->CreateDataTextureSource(mTextureInfo.mTextureFlags);
|
||||
MOZ_ASSERT(temp->AsSourceOGL() &&
|
||||
temp->AsSourceOGL()->AsTextureImageTextureSource());
|
||||
newSourceOnWhite = temp->AsSourceOGL()->AsTextureImageTextureSource();
|
||||
}
|
||||
|
||||
if (mTextureInfo.mDeprecatedTextureHostFlags & DeprecatedTextureHostFlags::COPY_PREVIOUS) {
|
||||
MOZ_ASSERT(aHost->mSource);
|
||||
MOZ_ASSERT(aHost->mSource->IsValid());
|
||||
nsIntRect bufferRect = aHost->mBufferRect;
|
||||
nsIntPoint bufferRotation = aHost->mBufferRotation;
|
||||
nsIntRect overlap;
|
||||
|
||||
// The buffer looks like:
|
||||
// ______
|
||||
// |1 |2 | Where the center point is offset by mBufferRotation from the top-left corner.
|
||||
// |___|__|
|
||||
// |3 |4 |
|
||||
// |___|__|
|
||||
//
|
||||
// This is drawn to the screen as:
|
||||
// ______
|
||||
// |4 |3 | Where the center point is { width - mBufferRotation.x, height - mBufferRotation.y } from
|
||||
// |___|__| from the top left corner - rotationPoint.
|
||||
// |2 |1 |
|
||||
// |___|__|
|
||||
//
|
||||
|
||||
// The basic idea below is to take all quadrant rectangles from the src and transform them into rectangles
|
||||
// in the destination. Unfortunately, it seems it is overly complex and could perhaps be simplified.
|
||||
|
||||
nsIntRect srcBufferSpaceBottomRight(bufferRotation.x, bufferRotation.y, bufferRect.width - bufferRotation.x, bufferRect.height - bufferRotation.y);
|
||||
nsIntRect srcBufferSpaceTopRight(bufferRotation.x, 0, bufferRect.width - bufferRotation.x, bufferRotation.y);
|
||||
nsIntRect srcBufferSpaceTopLeft(0, 0, bufferRotation.x, bufferRotation.y);
|
||||
nsIntRect srcBufferSpaceBottomLeft(0, bufferRotation.y, bufferRotation.x, bufferRect.height - bufferRotation.y);
|
||||
|
||||
overlap.IntersectRect(bufferRect, mBufferRect);
|
||||
|
||||
nsIntRect srcRect(overlap), dstRect(overlap);
|
||||
srcRect.MoveBy(- bufferRect.TopLeft() + bufferRotation);
|
||||
|
||||
nsIntRect srcRectDrawTopRight(srcRect);
|
||||
nsIntRect srcRectDrawTopLeft(srcRect);
|
||||
nsIntRect srcRectDrawBottomLeft(srcRect);
|
||||
// transform into the different quadrants
|
||||
srcRectDrawTopRight .MoveBy(-nsIntPoint(0, bufferRect.height));
|
||||
srcRectDrawTopLeft .MoveBy(-nsIntPoint(bufferRect.width, bufferRect.height));
|
||||
srcRectDrawBottomLeft.MoveBy(-nsIntPoint(bufferRect.width, 0));
|
||||
|
||||
// Intersect with the quadrant
|
||||
srcRect = srcRect .Intersect(srcBufferSpaceBottomRight);
|
||||
srcRectDrawTopRight = srcRectDrawTopRight .Intersect(srcBufferSpaceTopRight);
|
||||
srcRectDrawTopLeft = srcRectDrawTopLeft .Intersect(srcBufferSpaceTopLeft);
|
||||
srcRectDrawBottomLeft = srcRectDrawBottomLeft.Intersect(srcBufferSpaceBottomLeft);
|
||||
|
||||
dstRect = srcRect;
|
||||
nsIntRect dstRectDrawTopRight(srcRectDrawTopRight);
|
||||
nsIntRect dstRectDrawTopLeft(srcRectDrawTopLeft);
|
||||
nsIntRect dstRectDrawBottomLeft(srcRectDrawBottomLeft);
|
||||
|
||||
// transform back to src buffer space
|
||||
dstRect .MoveBy(-bufferRotation);
|
||||
dstRectDrawTopRight .MoveBy(-bufferRotation + nsIntPoint(0, bufferRect.height));
|
||||
dstRectDrawTopLeft .MoveBy(-bufferRotation + nsIntPoint(bufferRect.width, bufferRect.height));
|
||||
dstRectDrawBottomLeft.MoveBy(-bufferRotation + nsIntPoint(bufferRect.width, 0));
|
||||
|
||||
// transform back to draw coordinates
|
||||
dstRect .MoveBy(bufferRect.TopLeft());
|
||||
dstRectDrawTopRight .MoveBy(bufferRect.TopLeft());
|
||||
dstRectDrawTopLeft .MoveBy(bufferRect.TopLeft());
|
||||
dstRectDrawBottomLeft.MoveBy(bufferRect.TopLeft());
|
||||
|
||||
// transform to destBuffer space
|
||||
dstRect .MoveBy(-mBufferRect.TopLeft());
|
||||
dstRectDrawTopRight .MoveBy(-mBufferRect.TopLeft());
|
||||
dstRectDrawTopLeft .MoveBy(-mBufferRect.TopLeft());
|
||||
dstRectDrawBottomLeft.MoveBy(-mBufferRect.TopLeft());
|
||||
|
||||
newSource->EnsureBuffer(mBufferRect.Size(),
|
||||
ContentForFormat(aHost->mSource->GetFormat()));
|
||||
|
||||
aHost->mSource->CopyTo(srcRect, newSource, dstRect);
|
||||
if (bufferRotation != nsIntPoint(0, 0)) {
|
||||
// Draw the remaining quadrants. We call BlitTextureImage 3 extra
|
||||
// times instead of doing a single draw call because supporting that
|
||||
// with a tiled source is quite tricky.
|
||||
|
||||
if (!srcRectDrawTopRight.IsEmpty())
|
||||
aHost->mSource->CopyTo(srcRectDrawTopRight,
|
||||
newSource, dstRectDrawTopRight);
|
||||
if (!srcRectDrawTopLeft.IsEmpty())
|
||||
aHost->mSource->CopyTo(srcRectDrawTopLeft,
|
||||
newSource, dstRectDrawTopLeft);
|
||||
if (!srcRectDrawBottomLeft.IsEmpty())
|
||||
aHost->mSource->CopyTo(srcRectDrawBottomLeft,
|
||||
newSource, dstRectDrawBottomLeft);
|
||||
}
|
||||
|
||||
if (newSourceOnWhite) {
|
||||
newSourceOnWhite->EnsureBuffer(mBufferRect.Size(),
|
||||
ContentForFormat(aHost->mSourceOnWhite->GetFormat()));
|
||||
aHost->mSourceOnWhite->CopyTo(srcRect, newSourceOnWhite, dstRect);
|
||||
if (bufferRotation != nsIntPoint(0, 0)) {
|
||||
// draw the remaining quadrants
|
||||
if (!srcRectDrawTopRight.IsEmpty())
|
||||
aHost->mSourceOnWhite->CopyTo(srcRectDrawTopRight,
|
||||
newSourceOnWhite, dstRectDrawTopRight);
|
||||
if (!srcRectDrawTopLeft.IsEmpty())
|
||||
aHost->mSourceOnWhite->CopyTo(srcRectDrawTopLeft,
|
||||
newSourceOnWhite, dstRectDrawTopLeft);
|
||||
if (!srcRectDrawBottomLeft.IsEmpty())
|
||||
aHost->mSourceOnWhite->CopyTo(srcRectDrawBottomLeft,
|
||||
newSourceOnWhite, dstRectDrawBottomLeft);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
aHost->mSource = newSource;
|
||||
aHost->mSourceOnWhite = newSourceOnWhite;
|
||||
|
||||
aHost->mBufferRect = mBufferRect;
|
||||
aHost->mBufferRotation = nsIntPoint();
|
||||
}
|
||||
|
||||
nsIntRect
|
||||
ContentHostIncremental::TextureUpdateRequest::GetQuadrantRectangle(XSide aXSide,
|
||||
YSide aYSide) const
|
||||
{
|
||||
// quadrantTranslation is the amount we translate the top-left
|
||||
// of the quadrant by to get coordinates relative to the layer
|
||||
nsIntPoint quadrantTranslation = -mBufferRotation;
|
||||
quadrantTranslation.x += aXSide == LEFT ? mBufferRect.width : 0;
|
||||
quadrantTranslation.y += aYSide == TOP ? mBufferRect.height : 0;
|
||||
return mBufferRect + quadrantTranslation;
|
||||
}
|
||||
|
||||
void
|
||||
ContentHostIncremental::TextureUpdateRequest::Execute(ContentHostIncremental* aHost)
|
||||
{
|
||||
nsIntRect drawBounds = mUpdated.GetBounds();
|
||||
|
||||
aHost->mBufferRect = mBufferRect;
|
||||
aHost->mBufferRotation = mBufferRotation;
|
||||
|
||||
// Figure out which quadrant to draw in
|
||||
int32_t xBoundary = mBufferRect.XMost() - mBufferRotation.x;
|
||||
int32_t yBoundary = mBufferRect.YMost() - mBufferRotation.y;
|
||||
XSide sideX = drawBounds.XMost() <= xBoundary ? RIGHT : LEFT;
|
||||
YSide sideY = drawBounds.YMost() <= yBoundary ? BOTTOM : TOP;
|
||||
nsIntRect quadrantRect = GetQuadrantRectangle(sideX, sideY);
|
||||
NS_ASSERTION(quadrantRect.Contains(drawBounds), "Messed up quadrants");
|
||||
|
||||
mUpdated.MoveBy(-nsIntPoint(quadrantRect.x, quadrantRect.y));
|
||||
|
||||
IntPoint offset = ToIntPoint(-mUpdated.GetBounds().TopLeft());
|
||||
|
||||
RefPtr<DataSourceSurface> surf = GetSurfaceForDescriptor(mDescriptor);
|
||||
|
||||
if (mTextureId == TextureIdentifier::Front) {
|
||||
aHost->mSource->Update(surf, &mUpdated, &offset);
|
||||
} else {
|
||||
aHost->mSourceOnWhite->Update(surf, &mUpdated, &offset);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ContentHostIncremental::PrintInfo(std::stringstream& aStream, const char* aPrefix)
|
||||
{
|
||||
aStream << aPrefix;
|
||||
aStream << nsPrintfCString("ContentHostIncremental (0x%p)", this).get();
|
||||
|
||||
if (PaintWillResample()) {
|
||||
aStream << " [paint-will-resample]";
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ContentHostTexture::PrintInfo(std::stringstream& aStream, const char* aPrefix)
|
||||
{
|
||||
@ -869,15 +446,6 @@ ContentHostTexture::GenEffect(const gfx::Filter& aFilter)
|
||||
aFilter, true);
|
||||
}
|
||||
|
||||
TemporaryRef<TexturedEffect>
|
||||
ContentHostIncremental::GenEffect(const gfx::Filter& aFilter)
|
||||
{
|
||||
if (!mSource) {
|
||||
return nullptr;
|
||||
}
|
||||
return CreateTexturedEffect(mSource, mSourceOnWhite, aFilter, true);
|
||||
}
|
||||
|
||||
TemporaryRef<gfx::DataSourceSurface>
|
||||
ContentHostTexture::GetAsSurface()
|
||||
{
|
||||
|
@ -223,160 +223,6 @@ public:
|
||||
nsIntRegion* aUpdatedRegionBack);
|
||||
};
|
||||
|
||||
/**
|
||||
* Maintains a host-side only texture, and gets provided with
|
||||
* surfaces that only cover the changed pixels during an update.
|
||||
*
|
||||
* Takes ownership of the passed in update surfaces, and must
|
||||
* free them once texture upload is complete.
|
||||
*
|
||||
* Delays texture uploads until the next composite to
|
||||
* avoid blocking the main thread.
|
||||
*/
|
||||
class ContentHostIncremental : public ContentHostBase
|
||||
{
|
||||
public:
|
||||
explicit ContentHostIncremental(const TextureInfo& aTextureInfo);
|
||||
~ContentHostIncremental();
|
||||
|
||||
virtual CompositableType GetType() MOZ_OVERRIDE { return CompositableType::CONTENT_INC; }
|
||||
|
||||
virtual LayerRenderState GetRenderState() MOZ_OVERRIDE { return LayerRenderState(); }
|
||||
|
||||
virtual bool CreatedIncrementalTexture(ISurfaceAllocator* aAllocator,
|
||||
const TextureInfo& aTextureInfo,
|
||||
const nsIntRect& aBufferRect) MOZ_OVERRIDE;
|
||||
|
||||
virtual void UpdateIncremental(TextureIdentifier aTextureId,
|
||||
SurfaceDescriptor& aSurface,
|
||||
const nsIntRegion& aUpdated,
|
||||
const nsIntRect& aBufferRect,
|
||||
const nsIntPoint& aBufferRotation) MOZ_OVERRIDE;
|
||||
|
||||
virtual bool UpdateThebes(const ThebesBufferData& aData,
|
||||
const nsIntRegion& aUpdated,
|
||||
const nsIntRegion& aOldValidRegionBack,
|
||||
nsIntRegion* aUpdatedRegionBack) MOZ_OVERRIDE
|
||||
{
|
||||
NS_ERROR("Shouldn't call this");
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual void Composite(EffectChain& aEffectChain,
|
||||
float aOpacity,
|
||||
const gfx::Matrix4x4& aTransform,
|
||||
const gfx::Filter& aFilter,
|
||||
const gfx::Rect& aClipRect,
|
||||
const nsIntRegion* aVisibleRegion = nullptr) MOZ_OVERRIDE;
|
||||
|
||||
virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix) MOZ_OVERRIDE;
|
||||
|
||||
virtual bool Lock() MOZ_OVERRIDE {
|
||||
MOZ_ASSERT(!mLocked);
|
||||
ProcessTextureUpdates();
|
||||
mLocked = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void Unlock() MOZ_OVERRIDE {
|
||||
MOZ_ASSERT(mLocked);
|
||||
mLocked = false;
|
||||
}
|
||||
|
||||
virtual TemporaryRef<TexturedEffect>
|
||||
GenEffect(const gfx::Filter& aFilter) MOZ_OVERRIDE;
|
||||
|
||||
private:
|
||||
|
||||
void FlushUpdateQueue();
|
||||
void ProcessTextureUpdates();
|
||||
|
||||
class Request
|
||||
{
|
||||
public:
|
||||
Request()
|
||||
{
|
||||
MOZ_COUNT_CTOR(ContentHostIncremental::Request);
|
||||
}
|
||||
|
||||
virtual ~Request()
|
||||
{
|
||||
MOZ_COUNT_DTOR(ContentHostIncremental::Request);
|
||||
}
|
||||
|
||||
virtual void Execute(ContentHostIncremental *aHost) = 0;
|
||||
};
|
||||
|
||||
class TextureCreationRequest : public Request
|
||||
{
|
||||
public:
|
||||
TextureCreationRequest(const TextureInfo& aTextureInfo,
|
||||
const nsIntRect& aBufferRect)
|
||||
: mTextureInfo(aTextureInfo)
|
||||
, mBufferRect(aBufferRect)
|
||||
{}
|
||||
|
||||
virtual void Execute(ContentHostIncremental *aHost);
|
||||
|
||||
private:
|
||||
TextureInfo mTextureInfo;
|
||||
nsIntRect mBufferRect;
|
||||
};
|
||||
|
||||
class TextureUpdateRequest : public Request
|
||||
{
|
||||
public:
|
||||
TextureUpdateRequest(ISurfaceAllocator* aDeAllocator,
|
||||
TextureIdentifier aTextureId,
|
||||
SurfaceDescriptor& aDescriptor,
|
||||
const nsIntRegion& aUpdated,
|
||||
const nsIntRect& aBufferRect,
|
||||
const nsIntPoint& aBufferRotation)
|
||||
: mDeAllocator(aDeAllocator)
|
||||
, mTextureId(aTextureId)
|
||||
, mDescriptor(aDescriptor)
|
||||
, mUpdated(aUpdated)
|
||||
, mBufferRect(aBufferRect)
|
||||
, mBufferRotation(aBufferRotation)
|
||||
{}
|
||||
|
||||
~TextureUpdateRequest()
|
||||
{
|
||||
//TODO: Recycle these?
|
||||
mDeAllocator->DestroySharedSurface(&mDescriptor);
|
||||
}
|
||||
|
||||
virtual void Execute(ContentHostIncremental *aHost);
|
||||
|
||||
private:
|
||||
enum XSide {
|
||||
LEFT, RIGHT
|
||||
};
|
||||
enum YSide {
|
||||
TOP, BOTTOM
|
||||
};
|
||||
|
||||
nsIntRect GetQuadrantRectangle(XSide aXSide, YSide aYSide) const;
|
||||
|
||||
RefPtr<ISurfaceAllocator> mDeAllocator;
|
||||
TextureIdentifier mTextureId;
|
||||
SurfaceDescriptor mDescriptor;
|
||||
nsIntRegion mUpdated;
|
||||
nsIntRect mBufferRect;
|
||||
nsIntPoint mBufferRotation;
|
||||
};
|
||||
|
||||
nsTArray<UniquePtr<Request> > mUpdateList;
|
||||
|
||||
// Specific to OGL to avoid exposing methods on TextureSource that only
|
||||
// have one implementation.
|
||||
RefPtr<TextureImageTextureSourceOGL> mSource;
|
||||
RefPtr<TextureImageTextureSourceOGL> mSourceOnWhite;
|
||||
|
||||
RefPtr<ISurfaceAllocator> mDeAllocator;
|
||||
bool mLocked;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -53,7 +53,6 @@ bool
|
||||
PaintedLayerComposite::SetCompositableHost(CompositableHost* aHost)
|
||||
{
|
||||
switch (aHost->GetType()) {
|
||||
case CompositableType::CONTENT_INC:
|
||||
case CompositableType::CONTENT_TILED:
|
||||
case CompositableType::CONTENT_SINGLE:
|
||||
case CompositableType::CONTENT_DOUBLE:
|
||||
|
@ -55,14 +55,6 @@ public:
|
||||
*/
|
||||
virtual void Connect(CompositableClient* aCompositable) = 0;
|
||||
|
||||
/**
|
||||
* Notify the CompositableHost that it should create host-side-only
|
||||
* texture(s), that we will update incrementally using UpdateTextureIncremental.
|
||||
*/
|
||||
virtual void CreatedIncrementalBuffer(CompositableClient* aCompositable,
|
||||
const TextureInfo& aTextureInfo,
|
||||
const nsIntRect& aBufferRect) = 0;
|
||||
|
||||
/**
|
||||
* Tell the CompositableHost on the compositor side what TiledLayerBuffer to
|
||||
* use for the next composition.
|
||||
@ -83,23 +75,6 @@ public:
|
||||
const ThebesBufferData& aThebesBufferData,
|
||||
const nsIntRegion& aUpdatedRegion) = 0;
|
||||
|
||||
/**
|
||||
* Notify the compositor to update aTextureId using aDescriptor, and take
|
||||
* ownership of aDescriptor.
|
||||
*
|
||||
* aDescriptor only contains the pixels for aUpdatedRegion, and is relative
|
||||
* to aUpdatedRegion.TopLeft().
|
||||
*
|
||||
* aBufferRect/aBufferRotation define the new valid region contained
|
||||
* within the texture after the update has been applied.
|
||||
*/
|
||||
virtual void UpdateTextureIncremental(CompositableClient* aCompositable,
|
||||
TextureIdentifier aTextureId,
|
||||
SurfaceDescriptor& aDescriptor,
|
||||
const nsIntRegion& aUpdatedRegion,
|
||||
const nsIntRect& aBufferRect,
|
||||
const nsIntPoint& aBufferRotation) = 0;
|
||||
|
||||
/**
|
||||
* Communicate the picture rect of a YUV image in aLayer to the compositor
|
||||
*/
|
||||
|
@ -73,20 +73,6 @@ CompositableParentManager::ReceiveCompositableUpdate(const CompositableOperation
|
||||
EditReplyVector& replyv)
|
||||
{
|
||||
switch (aEdit.type()) {
|
||||
case CompositableOperation::TOpCreatedIncrementalTexture: {
|
||||
MOZ_LAYERS_LOG(("[ParentSide] Created texture"));
|
||||
const OpCreatedIncrementalTexture& op = aEdit.get_OpCreatedIncrementalTexture();
|
||||
CompositableHost* compositable = AsCompositable(op);
|
||||
|
||||
bool success =
|
||||
compositable->CreatedIncrementalTexture(this,
|
||||
op.textureInfo(),
|
||||
op.bufferRect());
|
||||
if (!success) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CompositableOperation::TOpPaintTextureRegion: {
|
||||
MOZ_LAYERS_LOG(("[ParentSide] Paint PaintedLayer"));
|
||||
|
||||
@ -116,22 +102,6 @@ CompositableParentManager::ReceiveCompositableUpdate(const CompositableOperation
|
||||
RenderTraceInvalidateEnd(thebes, "FF00FF");
|
||||
break;
|
||||
}
|
||||
case CompositableOperation::TOpPaintTextureIncremental: {
|
||||
MOZ_LAYERS_LOG(("[ParentSide] Paint PaintedLayer"));
|
||||
|
||||
const OpPaintTextureIncremental& op = aEdit.get_OpPaintTextureIncremental();
|
||||
|
||||
CompositableHost* compositable = AsCompositable(op);
|
||||
|
||||
SurfaceDescriptor desc = op.image();
|
||||
|
||||
compositable->UpdateIncremental(op.textureId(),
|
||||
desc,
|
||||
op.updatedRegion(),
|
||||
op.bufferRect(),
|
||||
op.bufferRotation());
|
||||
break;
|
||||
}
|
||||
case CompositableOperation::TOpUpdatePictureRect: {
|
||||
const OpUpdatePictureRect& op = aEdit.get_OpUpdatePictureRect();
|
||||
CompositableHost* compositable = AsCompositable(op);
|
||||
|
@ -541,17 +541,6 @@ ImageBridgeChild::EndTransaction()
|
||||
for (nsTArray<EditReply>::size_type i = 0; i < replies.Length(); ++i) {
|
||||
const EditReply& reply = replies[i];
|
||||
switch (reply.type()) {
|
||||
case EditReply::TOpTextureSwap: {
|
||||
const OpTextureSwap& ots = reply.get_OpTextureSwap();
|
||||
|
||||
CompositableClient* compositable =
|
||||
CompositableClient::FromIPDLActor(ots.compositableChild());
|
||||
|
||||
MOZ_ASSERT(compositable);
|
||||
|
||||
compositable->SetDescriptorFromReply(ots.textureId(), ots.image());
|
||||
break;
|
||||
}
|
||||
case EditReply::TReturnReleaseFence: {
|
||||
const ReturnReleaseFence& rep = reply.get_ReturnReleaseFence();
|
||||
FenceHandle fence = rep.fence();
|
||||
|
@ -13,7 +13,7 @@
|
||||
#include "mozilla/ipc/SharedMemory.h" // for SharedMemory, etc
|
||||
#include "mozilla/layers/AsyncTransactionTracker.h" // for AsyncTransactionTrackerHolder
|
||||
#include "mozilla/layers/CompositableForwarder.h"
|
||||
#include "mozilla/layers/CompositorTypes.h" // for TextureIdentifier, etc
|
||||
#include "mozilla/layers/CompositorTypes.h"
|
||||
#include "mozilla/layers/PImageBridgeChild.h"
|
||||
#include "nsDebug.h" // for NS_RUNTIMEABORT
|
||||
#include "nsRegion.h" // for nsIntRegion
|
||||
@ -248,16 +248,6 @@ public:
|
||||
NS_RUNTIMEABORT("should not be called");
|
||||
}
|
||||
|
||||
virtual void UpdateTextureIncremental(CompositableClient* aCompositable,
|
||||
TextureIdentifier aTextureId,
|
||||
SurfaceDescriptor& aDescriptor,
|
||||
const nsIntRegion& aUpdatedRegion,
|
||||
const nsIntRect& aBufferRect,
|
||||
const nsIntPoint& aBufferRotation) MOZ_OVERRIDE
|
||||
{
|
||||
NS_RUNTIMEABORT("should not be called");
|
||||
}
|
||||
|
||||
/**
|
||||
* Communicate the picture rect of a YUV image in aLayer to the compositor
|
||||
*/
|
||||
@ -265,12 +255,6 @@ public:
|
||||
const nsIntRect& aRect) MOZ_OVERRIDE;
|
||||
|
||||
|
||||
virtual void CreatedIncrementalBuffer(CompositableClient* aCompositable,
|
||||
const TextureInfo& aTextureInfo,
|
||||
const nsIntRect& aBufferRect) MOZ_OVERRIDE
|
||||
{
|
||||
NS_RUNTIMEABORT("should not be called");
|
||||
}
|
||||
virtual void UpdateTextureRegion(CompositableClient* aCompositable,
|
||||
const ThebesBufferData& aThebesBufferData,
|
||||
const nsIntRegion& aUpdatedRegion) MOZ_OVERRIDE {
|
||||
|
@ -41,7 +41,6 @@ using struct mozilla::layers::FrameMetrics from "FrameMetrics.h";
|
||||
using mozilla::layers::FrameMetrics::ViewID from "FrameMetrics.h";
|
||||
using struct mozilla::layers::FenceHandle from "mozilla/layers/FenceUtils.h";
|
||||
using struct mozilla::layers::FenceHandleFromChild from "mozilla/layers/FenceUtils.h";
|
||||
using mozilla::layers::TextureIdentifier from "mozilla/layers/CompositorTypes.h";
|
||||
using std::string from "string";
|
||||
|
||||
namespace mozilla {
|
||||
@ -339,27 +338,12 @@ struct OpUseOverlaySource {
|
||||
OverlaySource overlay;
|
||||
};
|
||||
|
||||
struct OpCreatedIncrementalTexture {
|
||||
PCompositable compositable;
|
||||
TextureInfo textureInfo;
|
||||
nsIntRect bufferRect;
|
||||
};
|
||||
|
||||
struct OpPaintTextureRegion {
|
||||
PCompositable compositable;
|
||||
ThebesBufferData bufferData;
|
||||
nsIntRegion updatedRegion;
|
||||
};
|
||||
|
||||
struct OpPaintTextureIncremental {
|
||||
PCompositable compositable;
|
||||
TextureIdentifier textureId;
|
||||
SurfaceDescriptor image;
|
||||
nsIntRegion updatedRegion;
|
||||
nsIntRect bufferRect;
|
||||
nsIntPoint bufferRotation;
|
||||
};
|
||||
|
||||
struct OpUpdatePictureRect {
|
||||
PCompositable compositable;
|
||||
nsIntRect picture;
|
||||
@ -438,10 +422,7 @@ struct OpReplyDeliverFence {
|
||||
union CompositableOperation {
|
||||
OpUpdatePictureRect;
|
||||
|
||||
OpCreatedIncrementalTexture;
|
||||
|
||||
OpPaintTextureRegion;
|
||||
OpPaintTextureIncremental;
|
||||
|
||||
OpUseTiledLayerBuffer;
|
||||
|
||||
@ -487,12 +468,6 @@ struct OpContentBufferSwap {
|
||||
nsIntRegion frontUpdatedRegion;
|
||||
};
|
||||
|
||||
struct OpTextureSwap {
|
||||
PCompositable compositable;
|
||||
TextureIdentifier textureId;
|
||||
SurfaceDescriptor image;
|
||||
};
|
||||
|
||||
struct ReturnReleaseFence {
|
||||
PCompositable compositable;
|
||||
PTexture texture;
|
||||
@ -503,7 +478,6 @@ struct ReturnReleaseFence {
|
||||
// only to be used for buffer swapping.
|
||||
union EditReply {
|
||||
OpContentBufferSwap;
|
||||
OpTextureSwap;
|
||||
|
||||
ReturnReleaseFence;
|
||||
};
|
||||
|
@ -352,26 +352,6 @@ ShadowLayerForwarder::UpdateTextureRegion(CompositableClient* aCompositable,
|
||||
aUpdatedRegion));
|
||||
}
|
||||
|
||||
void
|
||||
ShadowLayerForwarder::UpdateTextureIncremental(CompositableClient* aCompositable,
|
||||
TextureIdentifier aTextureId,
|
||||
SurfaceDescriptor& aDescriptor,
|
||||
const nsIntRegion& aUpdatedRegion,
|
||||
const nsIntRect& aBufferRect,
|
||||
const nsIntPoint& aBufferRotation)
|
||||
{
|
||||
CheckSurfaceDescriptor(&aDescriptor);
|
||||
MOZ_ASSERT(aCompositable);
|
||||
MOZ_ASSERT(aCompositable->GetIPDLActor());
|
||||
mTxn->AddNoSwapPaint(OpPaintTextureIncremental(nullptr, aCompositable->GetIPDLActor(),
|
||||
aTextureId,
|
||||
aDescriptor,
|
||||
aUpdatedRegion,
|
||||
aBufferRect,
|
||||
aBufferRotation));
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ShadowLayerForwarder::UpdatePictureRect(CompositableClient* aCompositable,
|
||||
const nsIntRect& aRect)
|
||||
@ -815,16 +795,6 @@ ShadowLayerForwarder::Connect(CompositableClient* aCompositable)
|
||||
aCompositable->InitIPDLActor(actor);
|
||||
}
|
||||
|
||||
void
|
||||
ShadowLayerForwarder::CreatedIncrementalBuffer(CompositableClient* aCompositable,
|
||||
const TextureInfo& aTextureInfo,
|
||||
const nsIntRect& aBufferRect)
|
||||
{
|
||||
MOZ_ASSERT(aCompositable);
|
||||
mTxn->AddNoSwapPaint(OpCreatedIncrementalTexture(nullptr, aCompositable->GetIPDLActor(),
|
||||
aTextureInfo, aBufferRect));
|
||||
}
|
||||
|
||||
void ShadowLayerForwarder::Attach(CompositableClient* aCompositable,
|
||||
ShadowableLayer* aLayer)
|
||||
{
|
||||
|
@ -133,7 +133,6 @@ class Transaction;
|
||||
|
||||
class ShadowLayerForwarder : public CompositableForwarder
|
||||
{
|
||||
friend class ContentClientIncremental;
|
||||
friend class ClientLayerManager;
|
||||
|
||||
public:
|
||||
@ -148,10 +147,6 @@ public:
|
||||
virtual PTextureChild* CreateTexture(const SurfaceDescriptor& aSharedData,
|
||||
TextureFlags aFlags) MOZ_OVERRIDE;
|
||||
|
||||
virtual void CreatedIncrementalBuffer(CompositableClient* aCompositable,
|
||||
const TextureInfo& aTextureInfo,
|
||||
const nsIntRect& aBufferRect) MOZ_OVERRIDE;
|
||||
|
||||
/**
|
||||
* Adds an edit in the layers transaction in order to attach
|
||||
* the corresponding compositable and layer on the compositor side.
|
||||
@ -259,13 +254,6 @@ public:
|
||||
const ThebesBufferData& aThebesBufferData,
|
||||
const nsIntRegion& aUpdatedRegion) MOZ_OVERRIDE;
|
||||
|
||||
virtual void UpdateTextureIncremental(CompositableClient* aCompositable,
|
||||
TextureIdentifier aTextureId,
|
||||
SurfaceDescriptor& aDescriptor,
|
||||
const nsIntRegion& aUpdatedRegion,
|
||||
const nsIntRect& aBufferRect,
|
||||
const nsIntPoint& aBufferRotation) MOZ_OVERRIDE;
|
||||
|
||||
/**
|
||||
* Communicate the picture rect of an image to the compositor
|
||||
*/
|
||||
|
@ -279,7 +279,8 @@ struct ForEachTrackedOptimizationAttemptOp
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
ForEachTrackedOptimizationAttempt(JSRuntime *rt, void *addr,
|
||||
ForEachTrackedOptimizationAttemptOp &op);
|
||||
ForEachTrackedOptimizationAttemptOp &op,
|
||||
JSScript **scriptOut, jsbytecode **pcOut);
|
||||
|
||||
struct ForEachTrackedOptimizationTypeInfoOp
|
||||
{
|
||||
@ -293,15 +294,28 @@ struct ForEachTrackedOptimizationTypeInfoOp
|
||||
// function.
|
||||
// - "alloc site" for object types tied to an allocation site.
|
||||
// - "prototype" for object types tied neither to a constructor nor
|
||||
// to an allocation site.
|
||||
// to an allocation site, but to a prototype.
|
||||
// - "singleton" for object types which only has a single value.
|
||||
// - "function" for object types referring to scripted functions.
|
||||
// - "native" for object types referring to native functions.
|
||||
//
|
||||
// The name parameter is the string representation of the type. If the
|
||||
// type is keyed by "constructor", or if the type itself refers to a
|
||||
// scripted function, the name is the function's displayAtom.
|
||||
// scripted function, the name is the function's displayAtom. If the type
|
||||
// is keyed by "native", this is nullptr.
|
||||
//
|
||||
// If the type is keyed by "constructor", "alloc site", or if the type
|
||||
// itself refers to a scripted function, the location and lineno
|
||||
// parameters will be respectively non-nullptr and non-0.
|
||||
// The location parameter is the filename if the type is keyed by
|
||||
// "constructor", "alloc site", or if the type itself refers to a scripted
|
||||
// function. If the type is keyed by "native", it is the offset of the
|
||||
// native function, suitable for use with addr2line on Linux or atos on OS
|
||||
// X. Otherwise it is nullptr.
|
||||
//
|
||||
// The lineno parameter is the line number if the type is keyed by
|
||||
// "constructor", "alloc site", or if the type itself refers to a scripted
|
||||
// function. Otherwise it is UINT32_MAX.
|
||||
//
|
||||
// The location parameter is the only one that may need escaping if being
|
||||
// quoted.
|
||||
virtual void readType(const char *keyedBy, const char *name,
|
||||
const char *location, unsigned lineno) = 0;
|
||||
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
#include "ctypes/Library.h"
|
||||
|
||||
#include "prerror.h"
|
||||
#include "prlink.h"
|
||||
|
||||
#include "ctypes/CTypes.h"
|
||||
@ -146,12 +147,17 @@ Library::Create(JSContext* cx, jsval path_, const JSCTypesCallbacks* callbacks)
|
||||
PRLibrary* library = PR_LoadLibraryWithFlags(libSpec, 0);
|
||||
|
||||
if (!library) {
|
||||
char *error = (char*) JS_malloc(cx, PR_GetErrorTextLength() + 1);
|
||||
if (error)
|
||||
PR_GetErrorText(error);
|
||||
|
||||
#ifdef XP_WIN
|
||||
JS_ReportError(cx, "couldn't open library %hs", pathChars);
|
||||
JS_ReportError(cx, "couldn't open library %hs: %s", pathChars, error);
|
||||
#else
|
||||
JS_ReportError(cx, "couldn't open library %s", pathBytes);
|
||||
JS_ReportError(cx, "couldn't open library %s: %s", pathBytes, error);
|
||||
JS_free(cx, pathBytes);
|
||||
#endif
|
||||
JS_free(cx, error);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -81,6 +81,8 @@ var ignoreCallees = {
|
||||
"PLDHashTableOps.hashKey" : true,
|
||||
"z_stream_s.zfree" : true,
|
||||
"GrGLInterface.fCallback" : true,
|
||||
"std::strstreambuf._M_alloc_fun" : true,
|
||||
"std::strstreambuf._M_free_fun" : true,
|
||||
};
|
||||
|
||||
function fieldCallCannotGC(csu, fullfield)
|
||||
@ -137,6 +139,10 @@ var ignoreFunctions = {
|
||||
"JSObject* js::GetWeakmapKeyDelegate(JSObject*)" : true, // FIXME: mark with AutoSuppressGCAnalysis instead
|
||||
"uint8 NS_IsMainThread()" : true,
|
||||
|
||||
// Has an indirect call under it by the name "__f", which seemed too
|
||||
// generic to ignore by itself.
|
||||
"void* std::_Locale_impl::~_Locale_impl(int32)" : true,
|
||||
|
||||
// FIXME!
|
||||
"NS_LogInit": true,
|
||||
"NS_LogTerm": true,
|
||||
|
@ -921,7 +921,7 @@ class GCRuntime
|
||||
void protectRelocatedArenas(ArenaHeader *relocatedList);
|
||||
void unprotectRelocatedArenas(ArenaHeader *relocatedList);
|
||||
#endif
|
||||
void finishCollection();
|
||||
void finishCollection(JS::gcreason::Reason reason);
|
||||
|
||||
void computeNonIncrementalMarkingForValidation();
|
||||
void validateIncrementalMarking();
|
||||
|
@ -20,19 +20,28 @@
|
||||
namespace js {
|
||||
namespace jit {
|
||||
|
||||
|
||||
static inline JitcodeRegionEntry
|
||||
RegionAtAddr(const JitcodeGlobalEntry::IonEntry &entry, void *ptr,
|
||||
uint32_t *ptrOffset)
|
||||
{
|
||||
MOZ_ASSERT(entry.containsPointer(ptr));
|
||||
*ptrOffset = reinterpret_cast<uint8_t *>(ptr) -
|
||||
reinterpret_cast<uint8_t *>(entry.nativeStartAddr());
|
||||
|
||||
uint32_t regionIdx = entry.regionTable()->findRegionEntry(*ptrOffset);
|
||||
MOZ_ASSERT(regionIdx < entry.regionTable()->numRegions());
|
||||
|
||||
return entry.regionTable()->regionEntry(regionIdx);
|
||||
}
|
||||
|
||||
bool
|
||||
JitcodeGlobalEntry::IonEntry::callStackAtAddr(JSRuntime *rt, void *ptr,
|
||||
BytecodeLocationVector &results,
|
||||
uint32_t *depth) const
|
||||
{
|
||||
MOZ_ASSERT(containsPointer(ptr));
|
||||
uint32_t ptrOffset = reinterpret_cast<uint8_t *>(ptr) -
|
||||
reinterpret_cast<uint8_t *>(nativeStartAddr());
|
||||
|
||||
uint32_t regionIdx = regionTable()->findRegionEntry(ptrOffset);
|
||||
MOZ_ASSERT(regionIdx < regionTable()->numRegions());
|
||||
|
||||
JitcodeRegionEntry region = regionTable()->regionEntry(regionIdx);
|
||||
uint32_t ptrOffset;
|
||||
JitcodeRegionEntry region = RegionAtAddr(*this, ptr, &ptrOffset);
|
||||
*depth = region.scriptDepth();
|
||||
|
||||
JitcodeRegionEntry::ScriptPcIterator locationIter = region.scriptPcIterator();
|
||||
@ -61,15 +70,10 @@ JitcodeGlobalEntry::IonEntry::callStackAtAddr(JSRuntime *rt, void *ptr,
|
||||
const char **results,
|
||||
uint32_t maxResults) const
|
||||
{
|
||||
MOZ_ASSERT(containsPointer(ptr));
|
||||
MOZ_ASSERT(maxResults >= 1);
|
||||
uint32_t ptrOffset = reinterpret_cast<uint8_t *>(ptr) -
|
||||
reinterpret_cast<uint8_t *>(nativeStartAddr());
|
||||
|
||||
uint32_t regionIdx = regionTable()->findRegionEntry(ptrOffset);
|
||||
MOZ_ASSERT(regionIdx < regionTable()->numRegions());
|
||||
|
||||
JitcodeRegionEntry region = regionTable()->regionEntry(regionIdx);
|
||||
uint32_t ptrOffset;
|
||||
JitcodeRegionEntry region = RegionAtAddr(*this, ptr, &ptrOffset);
|
||||
|
||||
JitcodeRegionEntry::ScriptPcIterator locationIter = region.scriptPcIterator();
|
||||
MOZ_ASSERT(locationIter.hasMore());
|
||||
@ -88,6 +92,23 @@ JitcodeGlobalEntry::IonEntry::callStackAtAddr(JSRuntime *rt, void *ptr,
|
||||
return count;
|
||||
}
|
||||
|
||||
void
|
||||
JitcodeGlobalEntry::IonEntry::youngestFrameLocationAtAddr(JSRuntime *rt, void *ptr,
|
||||
JSScript **script, jsbytecode **pc) const
|
||||
{
|
||||
uint32_t ptrOffset;
|
||||
JitcodeRegionEntry region = RegionAtAddr(*this, ptr, &ptrOffset);
|
||||
|
||||
JitcodeRegionEntry::ScriptPcIterator locationIter = region.scriptPcIterator();
|
||||
MOZ_ASSERT(locationIter.hasMore());
|
||||
uint32_t scriptIdx, pcOffset;
|
||||
locationIter.readNext(&scriptIdx, &pcOffset);
|
||||
pcOffset = region.findPcOffset(ptrOffset, pcOffset);
|
||||
|
||||
*script = getScript(scriptIdx);
|
||||
*pc = (*script)->offsetToPC(pcOffset);
|
||||
}
|
||||
|
||||
void
|
||||
JitcodeGlobalEntry::IonEntry::destroy()
|
||||
{
|
||||
@ -155,6 +176,16 @@ JitcodeGlobalEntry::BaselineEntry::callStackAtAddr(JSRuntime *rt, void *ptr,
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
JitcodeGlobalEntry::BaselineEntry::youngestFrameLocationAtAddr(JSRuntime *rt, void *ptr,
|
||||
JSScript **script,
|
||||
jsbytecode **pc) const
|
||||
{
|
||||
uint8_t *addr = reinterpret_cast<uint8_t*>(ptr);
|
||||
*script = script_;
|
||||
*pc = script_->baselineScript()->approximatePcForNativeAddress(script_, addr);
|
||||
}
|
||||
|
||||
void
|
||||
JitcodeGlobalEntry::BaselineEntry::destroy()
|
||||
{
|
||||
@ -164,19 +195,25 @@ JitcodeGlobalEntry::BaselineEntry::destroy()
|
||||
str_ = nullptr;
|
||||
}
|
||||
|
||||
static inline void
|
||||
RejoinEntry(JSRuntime *rt, const JitcodeGlobalEntry::IonCacheEntry &cache,
|
||||
void *ptr, JitcodeGlobalEntry *entry)
|
||||
{
|
||||
MOZ_ASSERT(cache.containsPointer(ptr));
|
||||
|
||||
// There must exist an entry for the rejoin addr if this entry exists.
|
||||
JitRuntime *jitrt = rt->jitRuntime();
|
||||
jitrt->getJitcodeGlobalTable()->lookupInfallible(cache.rejoinAddr(), entry, rt);
|
||||
MOZ_ASSERT(entry->isIon());
|
||||
}
|
||||
|
||||
bool
|
||||
JitcodeGlobalEntry::IonCacheEntry::callStackAtAddr(JSRuntime *rt, void *ptr,
|
||||
BytecodeLocationVector &results,
|
||||
uint32_t *depth) const
|
||||
{
|
||||
MOZ_ASSERT(containsPointer(ptr));
|
||||
|
||||
// There must exist an entry for the rejoin addr if this entry exists.
|
||||
JitRuntime *jitrt = rt->jitRuntime();
|
||||
JitcodeGlobalEntry entry;
|
||||
jitrt->getJitcodeGlobalTable()->lookupInfallible(rejoinAddr(), &entry, rt);
|
||||
MOZ_ASSERT(entry.isIon());
|
||||
|
||||
RejoinEntry(rt, *this, ptr, &entry);
|
||||
return entry.callStackAtAddr(rt, rejoinAddr(), results, depth);
|
||||
}
|
||||
|
||||
@ -185,17 +222,21 @@ JitcodeGlobalEntry::IonCacheEntry::callStackAtAddr(JSRuntime *rt, void *ptr,
|
||||
const char **results,
|
||||
uint32_t maxResults) const
|
||||
{
|
||||
MOZ_ASSERT(containsPointer(ptr));
|
||||
|
||||
// There must exist an entry for the rejoin addr if this entry exists.
|
||||
JitRuntime *jitrt = rt->jitRuntime();
|
||||
JitcodeGlobalEntry entry;
|
||||
jitrt->getJitcodeGlobalTable()->lookupInfallible(rejoinAddr(), &entry, rt);
|
||||
MOZ_ASSERT(entry.isIon());
|
||||
|
||||
RejoinEntry(rt, *this, ptr, &entry);
|
||||
return entry.callStackAtAddr(rt, rejoinAddr(), results, maxResults);
|
||||
}
|
||||
|
||||
void
|
||||
JitcodeGlobalEntry::IonCacheEntry::youngestFrameLocationAtAddr(JSRuntime *rt, void *ptr,
|
||||
JSScript **script,
|
||||
jsbytecode **pc) const
|
||||
{
|
||||
JitcodeGlobalEntry entry;
|
||||
RejoinEntry(rt, *this, ptr, &entry);
|
||||
return entry.youngestFrameLocationAtAddr(rt, ptr, script, pc);
|
||||
}
|
||||
|
||||
|
||||
static int ComparePointers(const void *a, const void *b) {
|
||||
const uint8_t *a_ptr = reinterpret_cast<const uint8_t *>(a);
|
||||
|
@ -210,10 +210,23 @@ class JitcodeGlobalEntry
|
||||
uint32_t callStackAtAddr(JSRuntime *rt, void *ptr, const char **results,
|
||||
uint32_t maxResults) const;
|
||||
|
||||
void youngestFrameLocationAtAddr(JSRuntime *rt, void *ptr,
|
||||
JSScript **script, jsbytecode **pc) const;
|
||||
|
||||
bool hasTrackedOptimizations() const {
|
||||
return !!optsRegionTable_;
|
||||
}
|
||||
|
||||
const IonTrackedOptimizationsRegionTable *trackedOptimizationsRegionTable() const {
|
||||
MOZ_ASSERT(hasTrackedOptimizations());
|
||||
return optsRegionTable_;
|
||||
}
|
||||
|
||||
uint8_t numOptimizationAttempts() const {
|
||||
MOZ_ASSERT(hasTrackedOptimizations());
|
||||
return optsAttemptsTable_->numEntries();
|
||||
}
|
||||
|
||||
IonTrackedOptimizationsAttempts trackedOptimizationAttempts(uint8_t index) {
|
||||
MOZ_ASSERT(hasTrackedOptimizations());
|
||||
return optsAttemptsTable_->entry(index);
|
||||
@ -278,6 +291,9 @@ class JitcodeGlobalEntry
|
||||
|
||||
uint32_t callStackAtAddr(JSRuntime *rt, void *ptr, const char **results,
|
||||
uint32_t maxResults) const;
|
||||
|
||||
void youngestFrameLocationAtAddr(JSRuntime *rt, void *ptr,
|
||||
JSScript **script, jsbytecode **pc) const;
|
||||
};
|
||||
|
||||
struct IonCacheEntry : public BaseEntry
|
||||
@ -302,6 +318,9 @@ class JitcodeGlobalEntry
|
||||
|
||||
uint32_t callStackAtAddr(JSRuntime *rt, void *ptr, const char **results,
|
||||
uint32_t maxResults) const;
|
||||
|
||||
void youngestFrameLocationAtAddr(JSRuntime *rt, void *ptr,
|
||||
JSScript **script, jsbytecode **pc) const;
|
||||
};
|
||||
|
||||
// Dummy entries are created for jitcode generated when profiling is not turned on,
|
||||
@ -326,6 +345,13 @@ class JitcodeGlobalEntry
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void youngestFrameLocationAtAddr(JSRuntime *rt, void *ptr,
|
||||
JSScript **script, jsbytecode **pc) const
|
||||
{
|
||||
*script = nullptr;
|
||||
*pc = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
// QueryEntry is never stored in the table, just used for queries
|
||||
@ -551,6 +577,23 @@ class JitcodeGlobalEntry
|
||||
return false;
|
||||
}
|
||||
|
||||
void youngestFrameLocationAtAddr(JSRuntime *rt, void *ptr,
|
||||
JSScript **script, jsbytecode **pc) const
|
||||
{
|
||||
switch (kind()) {
|
||||
case Ion:
|
||||
return ionEntry().youngestFrameLocationAtAddr(rt, ptr, script, pc);
|
||||
case Baseline:
|
||||
return baselineEntry().youngestFrameLocationAtAddr(rt, ptr, script, pc);
|
||||
case IonCache:
|
||||
return ionCacheEntry().youngestFrameLocationAtAddr(rt, ptr, script, pc);
|
||||
case Dummy:
|
||||
return dummyEntry().youngestFrameLocationAtAddr(rt, ptr, script, pc);
|
||||
default:
|
||||
MOZ_CRASH("Invalid JitcodeGlobalEntry kind.");
|
||||
}
|
||||
}
|
||||
|
||||
// Figure out the number of the (JSScript *, jsbytecode *) pairs that are active
|
||||
// at this location.
|
||||
uint32_t lookupInlineCallDepth(void *ptr);
|
||||
|
@ -1123,11 +1123,13 @@ IonBuilder::trackInlineSuccessUnchecked(InliningStatus status)
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
JS::ForEachTrackedOptimizationAttempt(JSRuntime *rt, void *addr,
|
||||
ForEachTrackedOptimizationAttemptOp &op)
|
||||
ForEachTrackedOptimizationAttemptOp &op,
|
||||
JSScript **scriptOut, jsbytecode **pcOut)
|
||||
{
|
||||
JitcodeGlobalTable *table = rt->jitRuntime()->getJitcodeGlobalTable();
|
||||
JitcodeGlobalEntry entry;
|
||||
table->lookupInfallible(addr, &entry, rt);
|
||||
entry.youngestFrameLocationAtAddr(rt, addr, scriptOut, pcOut);
|
||||
Maybe<uint8_t> index = entry.trackedOptimizationIndexAtAddr(addr);
|
||||
entry.trackedOptimizationAttempts(index.value()).forEach(op);
|
||||
}
|
||||
@ -1143,11 +1145,11 @@ InterpretedFunctionFilenameAndLineNumber(JSFunction *fun, const char **filename,
|
||||
source = fun->lazyScript()->maybeForwardedScriptSource();
|
||||
*lineno = fun->lazyScript()->lineno();
|
||||
}
|
||||
*filename = source->introducerFilename();
|
||||
*filename = source->filename();
|
||||
}
|
||||
|
||||
static JSFunction *
|
||||
InterpretedFunctionFromTrackedType(const IonTrackedTypeWithAddendum &tracked)
|
||||
FunctionFromTrackedType(const IonTrackedTypeWithAddendum &tracked)
|
||||
{
|
||||
if (tracked.hasConstructor())
|
||||
return tracked.constructor;
|
||||
@ -1162,55 +1164,78 @@ InterpretedFunctionFromTrackedType(const IonTrackedTypeWithAddendum &tracked)
|
||||
return ty.group()->maybeInterpretedFunction();
|
||||
}
|
||||
|
||||
// This adapter is needed as the internal API can deal with engine-internal
|
||||
// data structures directly, while the public API cannot.
|
||||
class ForEachTypeInfoAdapter : public IonTrackedOptimizationsTypeInfo::ForEachOp
|
||||
void
|
||||
IonTrackedOptimizationsTypeInfo::ForEachOpAdapter::readType(const IonTrackedTypeWithAddendum &tracked)
|
||||
{
|
||||
ForEachTrackedOptimizationTypeInfoOp &op_;
|
||||
TypeSet::Type ty = tracked.type;
|
||||
|
||||
public:
|
||||
explicit ForEachTypeInfoAdapter(ForEachTrackedOptimizationTypeInfoOp &op)
|
||||
: op_(op)
|
||||
{ }
|
||||
|
||||
void readType(const IonTrackedTypeWithAddendum &tracked) MOZ_OVERRIDE {
|
||||
TypeSet::Type ty = tracked.type;
|
||||
|
||||
if (ty.isPrimitive() || ty.isUnknown() || ty.isAnyObject()) {
|
||||
op_.readType("primitive", TypeSet::NonObjectTypeString(ty), nullptr, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
char buf[512];
|
||||
const uint32_t bufsize = mozilla::ArrayLength(buf);
|
||||
|
||||
if (JSFunction *fun = InterpretedFunctionFromTrackedType(tracked)) {
|
||||
PutEscapedString(buf, bufsize, fun->displayAtom(), 0);
|
||||
const char *filename;
|
||||
unsigned lineno;
|
||||
InterpretedFunctionFilenameAndLineNumber(fun, &filename, &lineno);
|
||||
op_.readType(tracked.constructor ? "constructor" : "function", buf, filename, lineno);
|
||||
return;
|
||||
}
|
||||
|
||||
const char *className = ty.objectKey()->clasp()->name;
|
||||
JS_snprintf(buf, bufsize, "[object %s]", className);
|
||||
|
||||
if (tracked.hasAllocationSite()) {
|
||||
JSScript *script = tracked.script;
|
||||
op_.readType("alloc site", buf,
|
||||
script->maybeForwardedScriptSource()->introducerFilename(),
|
||||
PCToLineNumber(script, script->offsetToPC(tracked.offset)));
|
||||
return;
|
||||
}
|
||||
|
||||
op_.readType("prototype", buf, nullptr, 0);
|
||||
if (ty.isPrimitive() || ty.isUnknown() || ty.isAnyObject()) {
|
||||
op_.readType("primitive", TypeSet::NonObjectTypeString(ty), nullptr, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
void operator()(JS::TrackedTypeSite site, MIRType mirType) MOZ_OVERRIDE {
|
||||
op_(site, StringFromMIRType(mirType));
|
||||
char buf[512];
|
||||
const uint32_t bufsize = mozilla::ArrayLength(buf);
|
||||
|
||||
if (JSFunction *fun = FunctionFromTrackedType(tracked)) {
|
||||
if (fun->isNative()) {
|
||||
//
|
||||
// Print out the absolute address of the function pointer.
|
||||
//
|
||||
// Note that this address is not usable without knowing the
|
||||
// starting address at which our shared library is loaded. Shared
|
||||
// library information is exposed by the profiler. If this address
|
||||
// needs to be symbolicated manually (e.g., when it is gotten via
|
||||
// debug spewing of all optimization information), it needs to be
|
||||
// converted to an offset from the beginning of the shared library
|
||||
// for use with utilities like `addr2line` on Linux and `atos` on
|
||||
// OS X. Converting to an offset may be done via dladdr():
|
||||
//
|
||||
// void *addr = JS_FUNC_TO_DATA_PTR(void *, fun->native());
|
||||
// uintptr_t offset;
|
||||
// Dl_info info;
|
||||
// if (dladdr(addr, &info) != 0)
|
||||
// offset = uintptr_t(addr) - uintptr_t(info.dli_fbase);
|
||||
//
|
||||
uintptr_t addr = JS_FUNC_TO_DATA_PTR(uintptr_t, fun->native());
|
||||
JS_snprintf(buf, bufsize, "%llx", addr);
|
||||
op_.readType("native", nullptr, buf, UINT32_MAX);
|
||||
return;
|
||||
}
|
||||
|
||||
PutEscapedString(buf, bufsize, fun->displayAtom(), 0);
|
||||
const char *filename;
|
||||
unsigned lineno;
|
||||
InterpretedFunctionFilenameAndLineNumber(fun, &filename, &lineno);
|
||||
op_.readType(tracked.constructor ? "constructor" : "function", buf, filename, lineno);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
const char *className = ty.objectKey()->clasp()->name;
|
||||
JS_snprintf(buf, bufsize, "[object %s]", className);
|
||||
|
||||
if (tracked.hasAllocationSite()) {
|
||||
JSScript *script = tracked.script;
|
||||
op_.readType("alloc site", buf,
|
||||
script->maybeForwardedScriptSource()->filename(),
|
||||
PCToLineNumber(script, script->offsetToPC(tracked.offset)));
|
||||
return;
|
||||
}
|
||||
|
||||
if (ty.isGroup()) {
|
||||
op_.readType("prototype", buf, nullptr, UINT32_MAX);
|
||||
return;
|
||||
}
|
||||
|
||||
op_.readType("singleton", buf, nullptr, UINT32_MAX);
|
||||
}
|
||||
|
||||
void
|
||||
IonTrackedOptimizationsTypeInfo::ForEachOpAdapter::operator()(JS::TrackedTypeSite site,
|
||||
MIRType mirType)
|
||||
{
|
||||
op_(site, StringFromMIRType(mirType));
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
JS::ForEachTrackedOptimizationTypeInfo(JSRuntime *rt, void *addr,
|
||||
@ -1219,7 +1244,7 @@ JS::ForEachTrackedOptimizationTypeInfo(JSRuntime *rt, void *addr,
|
||||
JitcodeGlobalTable *table = rt->jitRuntime()->getJitcodeGlobalTable();
|
||||
JitcodeGlobalEntry entry;
|
||||
table->lookupInfallible(addr, &entry, rt);
|
||||
ForEachTypeInfoAdapter adapter(op);
|
||||
IonTrackedOptimizationsTypeInfo::ForEachOpAdapter adapter(op);
|
||||
Maybe<uint8_t> index = entry.trackedOptimizationIndexAtAddr(addr);
|
||||
entry.trackedOptimizationTypeInfo(index.value()).forEach(adapter, entry.allTrackedTypes());
|
||||
}
|
||||
|
@ -484,12 +484,27 @@ class IonTrackedOptimizationsTypeInfo
|
||||
// JS::ForEachTrackedOptimizaitonTypeInfoOp cannot be used directly. The
|
||||
// internal API needs to deal with engine-internal data structures (e.g.,
|
||||
// TypeSet::Type) directly.
|
||||
//
|
||||
// An adapter is provided below.
|
||||
struct ForEachOp
|
||||
{
|
||||
virtual void readType(const IonTrackedTypeWithAddendum &tracked) = 0;
|
||||
virtual void operator()(JS::TrackedTypeSite site, MIRType mirType) = 0;
|
||||
};
|
||||
|
||||
class ForEachOpAdapter : public ForEachOp
|
||||
{
|
||||
JS::ForEachTrackedOptimizationTypeInfoOp &op_;
|
||||
|
||||
public:
|
||||
explicit ForEachOpAdapter(JS::ForEachTrackedOptimizationTypeInfoOp &op)
|
||||
: op_(op)
|
||||
{ }
|
||||
|
||||
void readType(const IonTrackedTypeWithAddendum &tracked) MOZ_OVERRIDE;
|
||||
void operator()(JS::TrackedTypeSite site, MIRType mirType) MOZ_OVERRIDE;
|
||||
};
|
||||
|
||||
void forEach(ForEachOp &op, const IonTrackedTypeVector *allTypes);
|
||||
};
|
||||
|
||||
|
@ -5546,7 +5546,7 @@ GCRuntime::compactPhase(bool lastGC, JS::gcreason::Reason reason)
|
||||
}
|
||||
|
||||
void
|
||||
GCRuntime::finishCollection()
|
||||
GCRuntime::finishCollection(JS::gcreason::Reason reason)
|
||||
{
|
||||
MOZ_ASSERT(marker.isDrained());
|
||||
marker.stop();
|
||||
@ -5566,6 +5566,13 @@ GCRuntime::finishCollection()
|
||||
}
|
||||
|
||||
lastGCTime = currentTime;
|
||||
|
||||
// If this is an OOM GC reason, wait on the background sweeping thread
|
||||
// before returning to ensure that we free as much as possible.
|
||||
if (reason == JS::gcreason::LAST_DITCH || reason == JS::gcreason::MEM_PRESSURE) {
|
||||
gcstats::AutoPhase ap(stats, gcstats::PHASE_WAIT_BACKGROUND_THREAD);
|
||||
rt->gc.waitBackgroundSweepOrAllocEnd();
|
||||
}
|
||||
}
|
||||
|
||||
/* Start a new heap session. */
|
||||
@ -5933,7 +5940,7 @@ GCRuntime::incrementalCollectSlice(SliceBudget &budget, JS::gcreason::Reason rea
|
||||
if (isCompacting && compactPhase(lastGC, reason) == NotFinished)
|
||||
break;
|
||||
|
||||
finishCollection();
|
||||
finishCollection(reason);
|
||||
|
||||
incrementalState = NO_INCREMENTAL;
|
||||
break;
|
||||
|
@ -60,9 +60,12 @@
|
||||
#include "frontend/Parser.h"
|
||||
#include "jit/arm/Simulator-arm.h"
|
||||
#include "jit/Ion.h"
|
||||
#include "jit/JitcodeMap.h"
|
||||
#include "jit/OptimizationTracking.h"
|
||||
#include "js/Debug.h"
|
||||
#include "js/GCAPI.h"
|
||||
#include "js/StructuredClone.h"
|
||||
#include "js/TrackedOptimizationInfo.h"
|
||||
#include "perf/jsperf.h"
|
||||
#include "shell/jsheaptools.h"
|
||||
#include "shell/jsoptparse.h"
|
||||
@ -4340,6 +4343,173 @@ SetSharedArrayBuffer(JSContext *cx, unsigned argc, Value *vp)
|
||||
return true;
|
||||
}
|
||||
|
||||
class SprintOptimizationTypeInfoOp : public ForEachTrackedOptimizationTypeInfoOp
|
||||
{
|
||||
Sprinter *sp;
|
||||
bool startedTypes_;
|
||||
|
||||
public:
|
||||
explicit SprintOptimizationTypeInfoOp(Sprinter *sp)
|
||||
: sp(sp),
|
||||
startedTypes_(false)
|
||||
{ }
|
||||
|
||||
void readType(const char *keyedBy, const char *name,
|
||||
const char *location, unsigned lineno) MOZ_OVERRIDE
|
||||
{
|
||||
if (!startedTypes_) {
|
||||
startedTypes_ = true;
|
||||
Sprint(sp, "{\"typeset\": [");
|
||||
}
|
||||
Sprint(sp, "{\"keyedBy\":\"%s\"", keyedBy);
|
||||
if (name)
|
||||
Sprint(sp, ",\"name\":\"%s\"", name);
|
||||
if (location) {
|
||||
char buf[512];
|
||||
PutEscapedString(buf, mozilla::ArrayLength(buf), location, strlen(location), '"');
|
||||
Sprint(sp, ",\"location\":%s", buf);
|
||||
}
|
||||
if (lineno != UINT32_MAX)
|
||||
Sprint(sp, ",\"line\":%u", lineno);
|
||||
Sprint(sp, "},");
|
||||
}
|
||||
|
||||
void operator()(TrackedTypeSite site, const char *mirType) MOZ_OVERRIDE {
|
||||
if (startedTypes_) {
|
||||
// Clear trailing ,
|
||||
if ((*sp)[sp->getOffset() - 1] == ',')
|
||||
(*sp)[sp->getOffset() - 1] = ' ';
|
||||
Sprint(sp, "],");
|
||||
startedTypes_ = false;
|
||||
} else {
|
||||
Sprint(sp, "{");
|
||||
}
|
||||
|
||||
Sprint(sp, "\"site\":\"%s\",\"mirType\":\"%s\"},",
|
||||
TrackedTypeSiteString(site), mirType);
|
||||
}
|
||||
};
|
||||
|
||||
class SprintOptimizationAttemptsOp : public ForEachTrackedOptimizationAttemptOp
|
||||
{
|
||||
Sprinter *sp;
|
||||
|
||||
public:
|
||||
explicit SprintOptimizationAttemptsOp(Sprinter *sp)
|
||||
: sp(sp)
|
||||
{ }
|
||||
|
||||
void operator()(TrackedStrategy strategy, TrackedOutcome outcome) MOZ_OVERRIDE {
|
||||
Sprint(sp, "{\"strategy\":\"%s\",\"outcome\":\"%s\"},",
|
||||
TrackedStrategyString(strategy), TrackedOutcomeString(outcome));
|
||||
}
|
||||
};
|
||||
|
||||
static bool
|
||||
ReflectTrackedOptimizations(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
RootedObject callee(cx, &args.callee());
|
||||
JSRuntime *rt = cx->runtime();
|
||||
|
||||
if (!rt->hasJitRuntime() || !rt->jitRuntime()->isOptimizationTrackingEnabled(rt)) {
|
||||
JS_ReportError(cx, "Optimization tracking is off.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (args.length() != 1) {
|
||||
ReportUsageError(cx, callee, "Wrong number of arguments");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!args[0].isObject() || !args[0].toObject().is<JSFunction>()) {
|
||||
ReportUsageError(cx, callee, "Argument must be a function");
|
||||
return false;
|
||||
}
|
||||
|
||||
RootedFunction fun(cx, &args[0].toObject().as<JSFunction>());
|
||||
if (!fun->hasScript() || !fun->nonLazyScript()->hasIonScript()) {
|
||||
args.rval().setNull();
|
||||
return true;
|
||||
}
|
||||
|
||||
jit::JitcodeGlobalTable *table = rt->jitRuntime()->getJitcodeGlobalTable();
|
||||
jit::JitcodeGlobalEntry entry;
|
||||
jit::IonScript *ion = fun->nonLazyScript()->ionScript();
|
||||
table->lookupInfallible(ion->method()->raw(), &entry, rt);
|
||||
|
||||
if (!entry.hasTrackedOptimizations()) {
|
||||
JSObject *obj = JS_NewPlainObject(cx);
|
||||
if (!obj)
|
||||
return false;
|
||||
args.rval().setObject(*obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
Sprinter sp(cx);
|
||||
if (!sp.init())
|
||||
return false;
|
||||
|
||||
const jit::IonTrackedOptimizationsRegionTable *regions =
|
||||
entry.ionEntry().trackedOptimizationsRegionTable();
|
||||
|
||||
Sprint(&sp, "{\"regions\": [");
|
||||
for (uint32_t i = 0; i < regions->numEntries(); i++) {
|
||||
jit::IonTrackedOptimizationsRegion region = regions->entry(i);
|
||||
jit::IonTrackedOptimizationsRegion::RangeIterator iter = region.ranges();
|
||||
while (iter.more()) {
|
||||
uint32_t startOffset, endOffset;
|
||||
uint8_t index;
|
||||
iter.readNext(&startOffset, &endOffset, &index);
|
||||
JSScript *script;
|
||||
jsbytecode *pc;
|
||||
// Use endOffset, as startOffset may be associated with a
|
||||
// previous, adjacent region ending exactly at startOffset. That
|
||||
// is, suppose we have two regions [0, startOffset], [startOffset,
|
||||
// endOffset]. Since we are not querying a return address, we want
|
||||
// the second region and not the first.
|
||||
uint8_t *addr = ion->method()->raw() + endOffset;
|
||||
entry.youngestFrameLocationAtAddr(rt, addr, &script, &pc);
|
||||
Sprint(&sp, "{\"location\":\"%s:%u\",\"offset\":%u,\"index\":%u}%s",
|
||||
script->filename(), script->lineno(), script->pcToOffset(pc), index,
|
||||
iter.more() ? "," : "");
|
||||
}
|
||||
}
|
||||
Sprint(&sp, "],");
|
||||
|
||||
Sprint(&sp, "\"opts\": [");
|
||||
for (uint8_t i = 0; i < entry.ionEntry().numOptimizationAttempts(); i++) {
|
||||
Sprint(&sp, "%s{\"typeinfo\":[", i == 0 ? "" : ",");
|
||||
SprintOptimizationTypeInfoOp top(&sp);
|
||||
jit::IonTrackedOptimizationsTypeInfo::ForEachOpAdapter adapter(top);
|
||||
entry.trackedOptimizationTypeInfo(i).forEach(adapter, entry.allTrackedTypes());
|
||||
// Clear the trailing ,
|
||||
if (sp[sp.getOffset() - 1] == ',')
|
||||
sp[sp.getOffset() - 1] = ' ';
|
||||
Sprint(&sp, "],\"attempts\":[");
|
||||
SprintOptimizationAttemptsOp aop(&sp);
|
||||
entry.trackedOptimizationAttempts(i).forEach(aop);
|
||||
// Clear the trailing ,
|
||||
if (sp[sp.getOffset() - 1] == ',')
|
||||
sp[sp.getOffset() - 1] = ' ';
|
||||
Sprint(&sp, "]}");
|
||||
}
|
||||
Sprint(&sp, "]}");
|
||||
|
||||
if (sp.hadOutOfMemory())
|
||||
return false;
|
||||
|
||||
RootedString str(cx, JS_NewStringCopyZ(cx, sp.string()));
|
||||
if (!str)
|
||||
return false;
|
||||
RootedValue jsonVal(cx);
|
||||
if (!JS_ParseJSON(cx, str, &jsonVal))
|
||||
return false;
|
||||
|
||||
args.rval().set(jsonVal);
|
||||
return true;
|
||||
}
|
||||
|
||||
static const JSFunctionSpecWithHelp shell_functions[] = {
|
||||
JS_FN_HELP("version", Version, 0, 0,
|
||||
"version([number])",
|
||||
@ -4784,6 +4954,12 @@ static const JSFunctionSpecWithHelp fuzzing_unsafe_functions[] = {
|
||||
" Note: This is not fuzzing safe because it can be used to construct\n"
|
||||
" deeply nested wrapper chains that cannot exist in the wild."),
|
||||
|
||||
JS_FN_HELP("trackedOpts", ReflectTrackedOptimizations, 1, 0,
|
||||
"trackedOpts(fun)",
|
||||
" Returns an object describing the tracked optimizations of |fun|, if\n"
|
||||
" any. If |fun| is not a scripted function or has not been compiled by\n"
|
||||
" Ion, null is returned."),
|
||||
|
||||
JS_FS_HELP_END
|
||||
};
|
||||
|
||||
|
@ -67,8 +67,14 @@ NewObjectCache::newObjectFromHit(JSContext *cx, EntryIndex entryIndex, js::gc::I
|
||||
return obj;
|
||||
}
|
||||
|
||||
// Trigger an identical allocation to the one that notified us of OOM
|
||||
// so that we trigger the right kind of GC automatically.
|
||||
// Trigger an identical allocation to the one that notified us of OOM so
|
||||
// that we trigger the right kind of GC automatically; note that even
|
||||
// though we are passing CanGC to AllocateObjectForCacheHit it will never
|
||||
// allow GC during the allocation itself. The reason is that this would
|
||||
// clobber our cache and make us unable to initialize from it. Instead we
|
||||
// do an independent non-allocation GC, then return nullptr so that we'll
|
||||
// take the slow allocation path. The callee is responsible for ensuring
|
||||
// that the index it uses to fill the cache is still correct after this GC.
|
||||
mozilla::DebugOnly<JSObject *> obj2 =
|
||||
js::gc::AllocateObjectForCacheHit<CanGC>(cx, entry->kind, heap, group->clasp());
|
||||
MOZ_ASSERT(!obj2);
|
||||
|
@ -5864,6 +5864,20 @@ ComputeSnappedImageDrawingParameters(gfxContext* aCtx,
|
||||
|
||||
gfxRect anchoredDestRect(anchorPoint, scaledDest);
|
||||
gfxRect anchoredImageRect(imageSpaceAnchorPoint, imageSize);
|
||||
|
||||
// Calculate anchoredDestRect with snapped fill rect when the devPixelFill rect
|
||||
// corresponds to just a single tile in that direction
|
||||
if (fill.Width() != devPixelFill.Width() &&
|
||||
devPixelDest.x == devPixelFill.x &&
|
||||
devPixelDest.XMost() == devPixelFill.XMost()) {
|
||||
anchoredDestRect.width = fill.width;
|
||||
}
|
||||
if (fill.Height() != devPixelFill.Height() &&
|
||||
devPixelDest.y == devPixelFill.y &&
|
||||
devPixelDest.YMost() == devPixelFill.YMost()) {
|
||||
anchoredDestRect.height = fill.height;
|
||||
}
|
||||
|
||||
transform = TransformBetweenRects(anchoredImageRect, anchoredDestRect);
|
||||
invTransform = TransformBetweenRects(anchoredDestRect, anchoredImageRect);
|
||||
}
|
||||
|
2
layout/generic/crashtests/1134667.html
Normal file
2
layout/generic/crashtests/1134667.html
Normal file
@ -0,0 +1,2 @@
|
||||
x
|
||||
<ruby><x>
|
@ -570,3 +570,4 @@ load 1039454-1.html
|
||||
load 1042489.html
|
||||
load 1054010-1.html
|
||||
load 1058954-1.html
|
||||
load 1134667.html
|
||||
|
@ -1019,8 +1019,6 @@ nsLineLayout::ReflowFrame(nsIFrame* aFrame,
|
||||
} else {
|
||||
if (nsGkAtoms::letterFrame==frameType) {
|
||||
pfd->mIsLetterFrame = true;
|
||||
} else if (nsGkAtoms::rubyFrame == frameType) {
|
||||
SyncAnnotationBounds(pfd);
|
||||
}
|
||||
if (pfd->mSpan) {
|
||||
isEmpty = !pfd->mSpan->mHasNonemptyContent && pfd->mFrame->IsSelfEmpty();
|
||||
@ -1127,6 +1125,7 @@ nsLineLayout::ReflowFrame(nsIFrame* aFrame,
|
||||
}
|
||||
if (nsGkAtoms::rubyFrame == frameType) {
|
||||
mHasRuby = true;
|
||||
SyncAnnotationBounds(pfd);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2797,17 +2796,15 @@ nsLineLayout::AdvanceAnnotationInlineBounds(PerFrameData* aPFD,
|
||||
/**
|
||||
* This function applies the changes of icoord and isize caused by
|
||||
* justification to annotations of the given frame.
|
||||
* aPFD must be one of the frames in aContainingSpan.
|
||||
*/
|
||||
void
|
||||
nsLineLayout::ApplyLineJustificationToAnnotations(PerFrameData* aPFD,
|
||||
PerSpanData* aContainingSpan,
|
||||
nscoord aDeltaICoord,
|
||||
nscoord aDeltaISize)
|
||||
{
|
||||
PerFrameData* pfd = aPFD->mNextAnnotation;
|
||||
nscoord containerWidth = ContainerWidthForSpan(aContainingSpan);
|
||||
while (pfd) {
|
||||
nscoord containerWidth = pfd->mFrame->GetParent()->GetRect().Width();
|
||||
AdvanceAnnotationInlineBounds(pfd, containerWidth,
|
||||
aDeltaICoord, aDeltaISize);
|
||||
|
||||
@ -2876,8 +2873,7 @@ nsLineLayout::ApplyFrameJustification(PerSpanData* aPSD,
|
||||
|
||||
// The gaps added to the end of the frame should also be
|
||||
// excluded from the isize added to the annotation.
|
||||
ApplyLineJustificationToAnnotations(pfd, aPSD,
|
||||
deltaICoord, dw - gapsAtEnd);
|
||||
ApplyLineJustificationToAnnotations(pfd, deltaICoord, dw - gapsAtEnd);
|
||||
deltaICoord += dw;
|
||||
pfd->mFrame->SetRect(lineWM, pfd->mBounds, ContainerWidthForSpan(aPSD));
|
||||
}
|
||||
|
@ -686,7 +686,6 @@ protected:
|
||||
nscoord aDeltaISize);
|
||||
|
||||
void ApplyLineJustificationToAnnotations(PerFrameData* aPFD,
|
||||
PerSpanData* aContainingSpan,
|
||||
nscoord aDeltaICoord,
|
||||
nscoord aDeltaISize);
|
||||
|
||||
|
@ -161,7 +161,7 @@ nsRubyTextContainerFrame::Reflow(nsPresContext* aPresContext,
|
||||
LogicalPoint pos = child->GetLogicalPosition(lineWM, containerWidth);
|
||||
pos.B(lineWM) += deltaBCoord;
|
||||
// Relative positioning hasn't happened yet.
|
||||
// So MovePositionBy should be used here.
|
||||
// So MovePositionBy should not be used here.
|
||||
child->SetPosition(lineWM, pos, containerWidth);
|
||||
nsContainerFrame::PlaceFrameView(child);
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
# For more pagination tests, see layout/reftests/w3c-css/submitted/css21/pagination/
|
||||
# and layout/reftests/w3c-css/submitted/multicol3/
|
||||
# Pagination tests
|
||||
asserts(3) == abspos-breaking-000.xhtml abspos-breaking-000.ref.xhtml # bug 1067755
|
||||
# asserts(3) == abspos-breaking-000.xhtml abspos-breaking-000.ref.xhtml # bug 1067755, 1135556
|
||||
== abspos-breaking-001.xhtml abspos-breaking-000.ref.xhtml
|
||||
== abspos-breaking-002.xhtml abspos-breaking-000.ref.xhtml
|
||||
== abspos-breaking-003.html abspos-breaking-003-ref.html
|
||||
|
@ -32,4 +32,4 @@ skip-if(B2G) fuzzy-if(cocoaWidget,1,5000) == 745025-1.html 745025-1-ref.html # r
|
||||
random-if(B2G&&browserIsRemote) == 960822.html 960822-ref.html # reftest-print doesn't work on B2G (scrollbar difference only)
|
||||
== 966419-1.html 966419-1-ref.html
|
||||
== 966419-2.html 966419-2-ref.html
|
||||
skip-if(B2G) asserts(3) HTTP(..) == 1108104.html 1108104-ref.html # bug 1067755
|
||||
# skip-if(B2G) asserts(3) HTTP(..) == 1108104.html 1108104-ref.html # bug 1067755, 1135556
|
||||
|
@ -17,7 +17,7 @@ pref(gfx.font_rendering.opentype_svg.enabled,true) fuzzy-if(gtk2Widget,1,2268
|
||||
pref(gfx.font_rendering.opentype_svg.enabled,true) == svg-glyph-paintnone.svg svg-glyph-paintnone-ref.svg
|
||||
pref(gfx.font_rendering.opentype_svg.enabled,true) == svg-glyph-cachedopacity.svg svg-glyph-cachedopacity-ref.svg
|
||||
pref(gfx.font_rendering.opentype_svg.enabled,true) fuzzy-if(cocoaWidget,255,100) == svg-glyph-objectvalue.svg svg-glyph-objectvalue-ref.svg
|
||||
pref(gfx.font_rendering.opentype_svg.enabled,true) fails == svg-glyph-mask.svg svg-glyph-mask-ref.svg # bug 872483
|
||||
#pref(gfx.font_rendering.opentype_svg.enabled,true) fails == svg-glyph-mask.svg svg-glyph-mask-ref.svg # bug 872483, 1135329
|
||||
pref(gfx.font_rendering.opentype_svg.enabled,true) == svg-glyph-paint-server.svg svg-glyph-paint-server-ref.svg
|
||||
pref(gfx.font_rendering.opentype_svg.enabled,true) == svg-glyph-transform.svg svg-glyph-transform-ref.svg
|
||||
pref(gfx.font_rendering.opentype_svg.enabled,true) == svg-glyph-extents.html svg-glyph-extents-ref.html
|
||||
|
@ -83,7 +83,7 @@ class Context(KeyedDefaultDict):
|
||||
# a list to be a problem.
|
||||
self._all_paths = []
|
||||
self.config = config
|
||||
self.executed_time = 0
|
||||
self.execution_time = 0
|
||||
KeyedDefaultDict.__init__(self, self._factory)
|
||||
|
||||
def push_source(self, path):
|
||||
|
@ -719,10 +719,9 @@ class BuildReader(object):
|
||||
each sandbox evaluation. Its return value is ignored.
|
||||
"""
|
||||
|
||||
def __init__(self, config, sandbox_post_eval_cb=None):
|
||||
def __init__(self, config):
|
||||
self.config = config
|
||||
|
||||
self._sandbox_post_eval_cb = sandbox_post_eval_cb
|
||||
self._log = logging.getLogger(__name__)
|
||||
self._read_files = set()
|
||||
self._execution_stack = []
|
||||
@ -967,9 +966,6 @@ class BuildReader(object):
|
||||
sandbox.exec_file(path)
|
||||
context.execution_time = time.time() - time_start
|
||||
|
||||
if self._sandbox_post_eval_cb:
|
||||
self._sandbox_post_eval_cb(context)
|
||||
|
||||
# We first collect directories populated in variables.
|
||||
dir_vars = ['DIRS']
|
||||
|
||||
@ -981,7 +977,7 @@ class BuildReader(object):
|
||||
curdir = mozpath.dirname(path)
|
||||
|
||||
gyp_contexts = []
|
||||
for target_dir in context['GYP_DIRS']:
|
||||
for target_dir in context.get('GYP_DIRS', []):
|
||||
gyp_dir = context['GYP_DIRS'][target_dir]
|
||||
for v in ('input', 'variables'):
|
||||
if not getattr(gyp_dir, v):
|
||||
@ -1011,9 +1007,6 @@ class BuildReader(object):
|
||||
gyp_contexts.append(gyp_context)
|
||||
|
||||
for gyp_context in gyp_contexts:
|
||||
if self._sandbox_post_eval_cb:
|
||||
self._sandbox_post_eval_cb(gyp_context)
|
||||
|
||||
context['DIRS'].append(mozpath.relpath(gyp_context.objdir, context.objdir))
|
||||
|
||||
yield context
|
||||
|
@ -237,21 +237,6 @@ class TestBuildReader(unittest.TestCase):
|
||||
self.assertEqual([context['XPIDL_MODULE'] for context in contexts],
|
||||
['foobar', 'foobar', 'baz', 'foobar'])
|
||||
|
||||
def test_process_eval_callback(self):
|
||||
def strip_dirs(context):
|
||||
context['DIRS'][:] = []
|
||||
count[0] += 1
|
||||
|
||||
reader = self.reader('traversal-simple',
|
||||
sandbox_post_eval_cb=strip_dirs)
|
||||
|
||||
count = [0]
|
||||
|
||||
contexts = list(reader.read_topsrcdir())
|
||||
|
||||
self.assertEqual(len(contexts), 1)
|
||||
self.assertEqual(len(count), 1)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
@ -243,6 +243,13 @@ AppTrustDomain::IsChainValid(const DERArray& certChain, Time time)
|
||||
return Success;
|
||||
}
|
||||
|
||||
Result
|
||||
AppTrustDomain::CheckSignatureDigestAlgorithm(DigestAlgorithm)
|
||||
{
|
||||
// TODO: We should restrict signatures to SHA-256 or better.
|
||||
return Success;
|
||||
}
|
||||
|
||||
Result
|
||||
AppTrustDomain::CheckRSAPublicKeyModulusSizeInBits(
|
||||
EndEntityOrCA /*endEntityOrCA*/, unsigned int modulusSizeInBits)
|
||||
@ -257,6 +264,7 @@ Result
|
||||
AppTrustDomain::VerifyRSAPKCS1SignedDigest(const SignedDigest& signedDigest,
|
||||
Input subjectPublicKeyInfo)
|
||||
{
|
||||
// TODO: We should restrict signatures to SHA-256 or better.
|
||||
return VerifyRSAPKCS1SignedDigestNSS(signedDigest, subjectPublicKeyInfo,
|
||||
mPinArg);
|
||||
}
|
||||
|
@ -38,6 +38,8 @@ public:
|
||||
/*optional*/ const mozilla::pkix::Input* aiaExtension) MOZ_OVERRIDE;
|
||||
virtual Result IsChainValid(const mozilla::pkix::DERArray& certChain,
|
||||
mozilla::pkix::Time time) MOZ_OVERRIDE;
|
||||
virtual Result CheckSignatureDigestAlgorithm(
|
||||
mozilla::pkix::DigestAlgorithm digestAlg) MOZ_OVERRIDE;
|
||||
virtual Result CheckRSAPublicKeyModulusSizeInBits(
|
||||
mozilla::pkix::EndEntityOrCA endEntityOrCA,
|
||||
unsigned int modulusSizeInBits) MOZ_OVERRIDE;
|
||||
|
@ -712,6 +712,12 @@ NSSCertDBTrustDomain::IsChainValid(const DERArray& certArray, Time time)
|
||||
return Success;
|
||||
}
|
||||
|
||||
Result
|
||||
NSSCertDBTrustDomain::CheckSignatureDigestAlgorithm(DigestAlgorithm)
|
||||
{
|
||||
return Success;
|
||||
}
|
||||
|
||||
Result
|
||||
NSSCertDBTrustDomain::CheckRSAPublicKeyModulusSizeInBits(
|
||||
EndEntityOrCA /*endEntityOrCA*/, unsigned int modulusSizeInBits)
|
||||
|
@ -70,6 +70,9 @@ public:
|
||||
/*out*/ mozilla::pkix::TrustLevel& trustLevel)
|
||||
MOZ_OVERRIDE;
|
||||
|
||||
virtual Result CheckSignatureDigestAlgorithm(
|
||||
mozilla::pkix::DigestAlgorithm digestAlg) MOZ_OVERRIDE;
|
||||
|
||||
virtual Result CheckRSAPublicKeyModulusSizeInBits(
|
||||
mozilla::pkix::EndEntityOrCA endEntityOrCA,
|
||||
unsigned int modulusSizeInBits) MOZ_OVERRIDE;
|
||||
|
@ -317,3 +317,4 @@ MOZILLA_PKIX_ERROR_INADEQUATE_KEY_SIZE=The server presented a certificate with a
|
||||
MOZILLA_PKIX_ERROR_V1_CERT_USED_AS_CA=An X.509 version 1 certificate that is not a trust anchor was used to issue the server's certificate. X.509 version 1 certificates are deprecated and should not be used to sign other certificates.
|
||||
MOZILLA_PKIX_ERROR_NOT_YET_VALID_CERTIFICATE=The server presented a certificate that is not yet valid.
|
||||
MOZILLA_PKIX_ERROR_NOT_YET_VALID_ISSUER_CERTIFICATE=A certificate that is not yet valid was used to issue the server's certificate.
|
||||
MOZILLA_PKIX_ERROR_SIGNATURE_ALGORITHM_MISMATCH=The signature algorithm in the signature field of the certificate does not match the algorithm in its signatureAlgorithm field.
|
||||
|
@ -179,6 +179,8 @@ static const unsigned int FATAL_ERROR_FLAG = 0x800;
|
||||
MOZILLA_PKIX_ERROR_NOT_YET_VALID_ISSUER_CERTIFICATE) \
|
||||
MOZILLA_PKIX_MAP(ERROR_UNSUPPORTED_EC_POINT_FORM, 47, \
|
||||
SEC_ERROR_UNSUPPORTED_EC_POINT_FORM) \
|
||||
MOZILLA_PKIX_MAP(ERROR_SIGNATURE_ALGORITHM_MISMATCH, 48, \
|
||||
MOZILLA_PKIX_ERROR_SIGNATURE_ALGORITHM_MISMATCH) \
|
||||
MOZILLA_PKIX_MAP(FATAL_ERROR_INVALID_ARGS, FATAL_ERROR_FLAG | 1, \
|
||||
SEC_ERROR_INVALID_ARGS) \
|
||||
MOZILLA_PKIX_MAP(FATAL_ERROR_INVALID_STATE, FATAL_ERROR_FLAG | 2, \
|
||||
|
@ -81,6 +81,7 @@ enum ErrorCode
|
||||
MOZILLA_PKIX_ERROR_NO_RFC822NAME_MATCH = ERROR_BASE + 4,
|
||||
MOZILLA_PKIX_ERROR_NOT_YET_VALID_CERTIFICATE = ERROR_BASE + 5,
|
||||
MOZILLA_PKIX_ERROR_NOT_YET_VALID_ISSUER_CERTIFICATE = ERROR_BASE + 6,
|
||||
MOZILLA_PKIX_ERROR_SIGNATURE_ALGORITHM_MISMATCH = ERROR_BASE + 7,
|
||||
};
|
||||
|
||||
void RegisterErrorTable();
|
||||
|
@ -271,6 +271,13 @@ public:
|
||||
/*optional*/ const Input* stapledOCSPresponse,
|
||||
/*optional*/ const Input* aiaExtension) = 0;
|
||||
|
||||
// Check that the given digest algorithm is acceptable for use in signatures.
|
||||
//
|
||||
// Return Success if the algorithm is acceptable,
|
||||
// Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED if the algorithm is not
|
||||
// acceptable, or another error code if another error occurred.
|
||||
virtual Result CheckSignatureDigestAlgorithm(DigestAlgorithm digestAlg) = 0;
|
||||
|
||||
// Check that the RSA public key size is acceptable.
|
||||
//
|
||||
// Return Success if the key size is acceptable,
|
||||
|
@ -79,9 +79,6 @@ BackCert::Init()
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
}
|
||||
// XXX: Ignored. What are we supposed to check? This seems totally redundant
|
||||
// with Certificate.signatureAlgorithm. Is it important to check that they
|
||||
// are consistent with each other? It doesn't seem to matter!
|
||||
rv = der::ExpectTagAndGetValue(tbsCertificate, der::SEQUENCE, signature);
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
|
@ -29,6 +29,99 @@
|
||||
|
||||
namespace mozilla { namespace pkix {
|
||||
|
||||
// 4.1.1.2 signatureAlgorithm
|
||||
// 4.1.2.3 signature
|
||||
|
||||
Result
|
||||
CheckSignatureAlgorithm(TrustDomain& trustDomain,
|
||||
EndEntityOrCA endEntityOrCA,
|
||||
const der::SignedDataWithSignature& signedData,
|
||||
Input signatureValue)
|
||||
{
|
||||
// 4.1.1.2. signatureAlgorithm
|
||||
der::PublicKeyAlgorithm publicKeyAlg;
|
||||
DigestAlgorithm digestAlg;
|
||||
Reader signatureAlgorithmReader(signedData.algorithm);
|
||||
Result rv = der::SignatureAlgorithmIdentifierValue(signatureAlgorithmReader,
|
||||
publicKeyAlg, digestAlg);
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
}
|
||||
rv = der::End(signatureAlgorithmReader);
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
// 4.1.2.3. Signature
|
||||
der::PublicKeyAlgorithm signedPublicKeyAlg;
|
||||
DigestAlgorithm signedDigestAlg;
|
||||
Reader signedSignatureAlgorithmReader(signatureValue);
|
||||
rv = der::SignatureAlgorithmIdentifierValue(signedSignatureAlgorithmReader,
|
||||
signedPublicKeyAlg,
|
||||
signedDigestAlg);
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
}
|
||||
rv = der::End(signedSignatureAlgorithmReader);
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
// "This field MUST contain the same algorithm identifier as the
|
||||
// signatureAlgorithm field in the sequence Certificate." However, it may
|
||||
// be encoded differently. In particular, one of the fields may have a NULL
|
||||
// parameter while the other one may omit the parameter field altogether, and
|
||||
// these are considered equivalent. Some certificates generation software
|
||||
// actually generates certificates like that, so we compare the parsed values
|
||||
// instead of comparing the encoded values byte-for-byte.
|
||||
//
|
||||
// Along the same lines, we accept two different OIDs for RSA-with-SHA1, and
|
||||
// we consider those OIDs to be equivalent here.
|
||||
if (publicKeyAlg != signedPublicKeyAlg || digestAlg != signedDigestAlg) {
|
||||
return Result::ERROR_SIGNATURE_ALGORITHM_MISMATCH;
|
||||
}
|
||||
|
||||
// During the time of the deprecation of SHA-1 and the deprecation of RSA
|
||||
// keys of less than 2048 bits, we will encounter many certs signed using
|
||||
// SHA-1 and/or too-small RSA keys. With this in mind, we ask the trust
|
||||
// domain early on if it knows it will reject the signature purely based on
|
||||
// the digest algorithm and/or the RSA key size (if an RSA signature). This
|
||||
// is a good optimization because it completely avoids calling
|
||||
// trustDomain.FindIssuers (which may be slow) for such rejected certs, and
|
||||
// more generally it short-circuits any path building with them (which, of
|
||||
// course, is even slower).
|
||||
|
||||
rv = trustDomain.CheckSignatureDigestAlgorithm(digestAlg);
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
switch (publicKeyAlg) {
|
||||
case der::PublicKeyAlgorithm::RSA_PKCS1:
|
||||
{
|
||||
// The RSA computation may give a result that requires fewer bytes to
|
||||
// encode than the public key (since it is modular arithmetic). However,
|
||||
// the last step of generating a PKCS#1.5 signature is the I2OSP
|
||||
// procedure, which pads any such shorter result with zeros so that it
|
||||
// is exactly the same length as the public key.
|
||||
unsigned int signatureSizeInBits = signedData.signature.GetLength() * 8u;
|
||||
return trustDomain.CheckRSAPublicKeyModulusSizeInBits(
|
||||
endEntityOrCA, signatureSizeInBits);
|
||||
}
|
||||
|
||||
case der::PublicKeyAlgorithm::ECDSA:
|
||||
// In theory, we could implement a similar early-pruning optimization for
|
||||
// ECDSA curves. However, since there has been no similar deprecation for
|
||||
// for any curve that we support, the chances of us encountering a curve
|
||||
// during path building is too low to be worth bothering with.
|
||||
break;
|
||||
|
||||
MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM
|
||||
}
|
||||
|
||||
return Success;
|
||||
}
|
||||
|
||||
// 4.1.2.5 Validity
|
||||
|
||||
Result
|
||||
@ -735,21 +828,46 @@ CheckIssuerIndependentProperties(TrustDomain& trustDomain,
|
||||
|
||||
const EndEntityOrCA endEntityOrCA = cert.endEntityOrCA;
|
||||
|
||||
// Check the cert's trust first, because we want to minimize the amount of
|
||||
// processing we do on a distrusted cert, in case it is trying to exploit
|
||||
// some bug in our processing.
|
||||
rv = trustDomain.GetCertTrust(endEntityOrCA, requiredPolicy, cert.GetDER(),
|
||||
trustLevel);
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
}
|
||||
if (trustLevel == TrustLevel::ActivelyDistrusted) {
|
||||
return Result::ERROR_UNTRUSTED_CERT;
|
||||
}
|
||||
if (trustLevel != TrustLevel::TrustAnchor &&
|
||||
trustLevel != TrustLevel::InheritsTrust) {
|
||||
// The TrustDomain returned a trust level that we weren't expecting.
|
||||
return Result::FATAL_ERROR_INVALID_STATE;
|
||||
|
||||
if (trustLevel == TrustLevel::TrustAnchor &&
|
||||
endEntityOrCA == EndEntityOrCA::MustBeEndEntity &&
|
||||
requiredEKUIfPresent == KeyPurposeId::id_kp_OCSPSigning) {
|
||||
// OCSP signer certificates can never be trust anchors, especially
|
||||
// since we don't support designated OCSP responders. All of the checks
|
||||
// below that are dependent on trustLevel rely on this overriding of the
|
||||
// trust level for OCSP signers.
|
||||
trustLevel = TrustLevel::InheritsTrust;
|
||||
}
|
||||
|
||||
// Check the SPKI first, because it is one of the most selective properties
|
||||
switch (trustLevel) {
|
||||
case TrustLevel::InheritsTrust:
|
||||
rv = CheckSignatureAlgorithm(trustDomain, endEntityOrCA,
|
||||
cert.GetSignedData(), cert.GetSignature());
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
}
|
||||
break;
|
||||
|
||||
case TrustLevel::TrustAnchor:
|
||||
// We don't even bother checking signatureAlgorithm or signature for
|
||||
// syntactic validity for trust anchors, because we don't use those
|
||||
// fields for anything, and because the trust anchor might be signed
|
||||
// with a signature algorithm we don't actually support.
|
||||
break;
|
||||
|
||||
case TrustLevel::ActivelyDistrusted:
|
||||
return Result::ERROR_UNTRUSTED_CERT;
|
||||
}
|
||||
|
||||
// Check the SPKI early, because it is one of the most selective properties
|
||||
// of the certificate due to SHA-1 deprecation and the deprecation of
|
||||
// certificates with keys weaker than RSA 2048.
|
||||
Reader spki(cert.GetSubjectPublicKeyInfo());
|
||||
|
@ -111,6 +111,13 @@ SignatureAlgorithmIdentifierValue(Reader& input,
|
||||
/*out*/ PublicKeyAlgorithm& publicKeyAlgorithm,
|
||||
/*out*/ DigestAlgorithm& digestAlgorithm)
|
||||
{
|
||||
// RFC 5758 Section 3.2 (ECDSA with SHA-2), and RFC 3279 Section 2.2.3
|
||||
// (ECDSA with SHA-1) say that parameters must be omitted.
|
||||
//
|
||||
// RFC 4055 Section 5 and RFC 3279 Section 2.2.1 both say that parameters for
|
||||
// RSA must be encoded as NULL; we relax that requirement by allowing the
|
||||
// NULL to be omitted, to match all the other signature algorithms we support
|
||||
// and for compatibility.
|
||||
Reader algorithmID;
|
||||
Result rv = AlgorithmIdentifierValue(input, algorithmID);
|
||||
if (rv != Success) {
|
||||
@ -166,15 +173,6 @@ SignatureAlgorithmIdentifierValue(Reader& input,
|
||||
0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x01
|
||||
};
|
||||
|
||||
// RFC 5758 Section 3.1 (DSA with SHA-2), RFC 3279 Section 2.2.2 (DSA with
|
||||
// SHA-1), RFC 5758 Section 3.2 (ECDSA with SHA-2), and RFC 3279
|
||||
// Section 2.2.3 (ECDSA with SHA-1) all say that parameters must be omitted.
|
||||
//
|
||||
// RFC 4055 Section 5 and RFC 3279 Section 2.2.1 both say that parameters for
|
||||
// RSA must be encoded as NULL; we relax that requirement by allowing the
|
||||
// NULL to be omitted, to match all the other signature algorithms we support
|
||||
// and for compatibility.
|
||||
|
||||
// Matching is attempted based on a rough estimate of the commonality of the
|
||||
// algorithm, to minimize the number of MatchRest calls.
|
||||
if (algorithmID.MatchRest(sha256WithRSAEncryption)) {
|
||||
|
@ -194,6 +194,9 @@ RegisterErrorTable()
|
||||
{ "MOZILLA_PKIX_ERROR_NOT_YET_VALID_ISSUER_CERTIFICATE",
|
||||
"A certificate that is not yet valid was used to issue the server's "
|
||||
"certificate." },
|
||||
{ "MOZILLA_PKIX_ERROR_SIGNATURE_ALGORITHM_MISMATCH",
|
||||
"The signature algorithm in the signature field of the certificate does "
|
||||
"not match the algorithm in its signatureAlgorithm field." },
|
||||
};
|
||||
// Note that these error strings are not localizable.
|
||||
// When these strings change, update the localization information too.
|
||||
|
@ -53,16 +53,18 @@ public:
|
||||
Result Init();
|
||||
|
||||
const Input GetDER() const { return der; }
|
||||
der::Version GetVersion() const { return version; }
|
||||
const der::SignedDataWithSignature& GetSignedData() const {
|
||||
return signedData;
|
||||
}
|
||||
|
||||
der::Version GetVersion() const { return version; }
|
||||
const Input GetSerialNumber() const { return serialNumber; }
|
||||
const Input GetSignature() const { return signature; }
|
||||
const Input GetIssuer() const { return issuer; }
|
||||
// XXX: "validity" is a horrible name for the structure that holds
|
||||
// notBefore & notAfter, but that is the name used in RFC 5280 and we use the
|
||||
// RFC 5280 names for everything.
|
||||
const Input GetValidity() const { return validity; }
|
||||
const Input GetSerialNumber() const { return serialNumber; }
|
||||
const Input GetSubject() const { return subject; }
|
||||
const Input GetSubjectPublicKeyInfo() const
|
||||
{
|
||||
|
@ -9,6 +9,7 @@ SOURCES += [
|
||||
'pkixcert_extension_tests.cpp',
|
||||
'pkixcert_signature_algorithm_tests.cpp',
|
||||
'pkixcheck_CheckKeyUsage_tests.cpp',
|
||||
'pkixcheck_CheckSignatureAlgorithm_tests.cpp',
|
||||
'pkixcheck_CheckValidity_tests.cpp',
|
||||
|
||||
# The naming conventions are described in ./README.txt.
|
||||
|
@ -36,9 +36,7 @@
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
#include "pkix/pkix.h"
|
||||
#include "pkixgtest.h"
|
||||
#include "pkixtestutil.h"
|
||||
|
||||
using namespace mozilla::pkix;
|
||||
using namespace mozilla::pkix::test;
|
||||
@ -80,7 +78,7 @@ CreateCert(const char* issuerCN, // null means "empty name"
|
||||
return certDER;
|
||||
}
|
||||
|
||||
class TestTrustDomain final : public TrustDomain
|
||||
class TestTrustDomain final : public DefaultCryptoTrustDomain
|
||||
{
|
||||
public:
|
||||
// The "cert chain tail" is a longish chain of certificates that is used by
|
||||
@ -153,36 +151,6 @@ private:
|
||||
return Success;
|
||||
}
|
||||
|
||||
Result DigestBuf(Input input, DigestAlgorithm digestAlg,
|
||||
/*out*/ uint8_t* digestBuf, size_t digestLen) override
|
||||
{
|
||||
return TestDigestBuf(input, digestAlg, digestBuf, digestLen);
|
||||
}
|
||||
|
||||
Result CheckRSAPublicKeyModulusSizeInBits(EndEntityOrCA, unsigned int)
|
||||
override
|
||||
{
|
||||
return Success;
|
||||
}
|
||||
|
||||
Result VerifyRSAPKCS1SignedDigest(const SignedDigest& signedDigest,
|
||||
Input subjectPublicKeyInfo) override
|
||||
{
|
||||
return TestVerifyRSAPKCS1SignedDigest(signedDigest, subjectPublicKeyInfo);
|
||||
}
|
||||
|
||||
Result CheckECDSACurveIsAcceptable(EndEntityOrCA, NamedCurve) override
|
||||
{
|
||||
return Success;
|
||||
}
|
||||
|
||||
Result VerifyECDSASignedDigest(const SignedDigest& signedDigest,
|
||||
Input subjectPublicKeyInfo) override
|
||||
{
|
||||
return TestVerifyECDSASignedDigest(signedDigest, subjectPublicKeyInfo);
|
||||
}
|
||||
|
||||
|
||||
std::map<ByteString, ByteString> subjectDERToCertDER;
|
||||
ByteString leafCACertDER;
|
||||
ByteString rootCACertDER;
|
||||
@ -276,7 +244,7 @@ TEST_F(pkixbuild, BeyondMaxAcceptableCertChainLength)
|
||||
// is treated as a trust anchor and is assumed to have issued all certificates
|
||||
// (i.e. FindIssuer always attempts to build the next step in the chain with
|
||||
// it).
|
||||
class ExpiredCertTrustDomain final : public TrustDomain
|
||||
class ExpiredCertTrustDomain final : public DefaultCryptoTrustDomain
|
||||
{
|
||||
public:
|
||||
explicit ExpiredCertTrustDomain(ByteString rootDER)
|
||||
@ -315,48 +283,11 @@ public:
|
||||
return checker.Check(rootCert, nullptr, keepGoing);
|
||||
}
|
||||
|
||||
Result CheckRevocation(EndEntityOrCA, const CertID&, Time,
|
||||
/*optional*/ const Input*,
|
||||
/*optional*/ const Input*) override
|
||||
{
|
||||
ADD_FAILURE();
|
||||
return Result::FATAL_ERROR_LIBRARY_FAILURE;
|
||||
}
|
||||
|
||||
Result IsChainValid(const DERArray&, Time) override
|
||||
{
|
||||
return Success;
|
||||
}
|
||||
|
||||
Result DigestBuf(Input input, DigestAlgorithm digestAlg,
|
||||
/*out*/ uint8_t* digestBuf, size_t digestLen) override
|
||||
{
|
||||
return TestDigestBuf(input, digestAlg, digestBuf, digestLen);
|
||||
}
|
||||
|
||||
Result CheckRSAPublicKeyModulusSizeInBits(EndEntityOrCA, unsigned int)
|
||||
override
|
||||
{
|
||||
return Success;
|
||||
}
|
||||
|
||||
Result VerifyRSAPKCS1SignedDigest(const SignedDigest& signedDigest,
|
||||
Input subjectPublicKeyInfo) override
|
||||
{
|
||||
return TestVerifyRSAPKCS1SignedDigest(signedDigest, subjectPublicKeyInfo);
|
||||
}
|
||||
|
||||
Result CheckECDSACurveIsAcceptable(EndEntityOrCA, NamedCurve) override
|
||||
{
|
||||
return Success;
|
||||
}
|
||||
|
||||
Result VerifyECDSASignedDigest(const SignedDigest& signedDigest,
|
||||
Input subjectPublicKeyInfo) override
|
||||
{
|
||||
return TestVerifyECDSASignedDigest(signedDigest, subjectPublicKeyInfo);
|
||||
}
|
||||
|
||||
private:
|
||||
ByteString rootDER;
|
||||
};
|
||||
@ -394,7 +325,7 @@ TEST_F(pkixbuild, NoRevocationCheckingForExpiredCert)
|
||||
nullptr));
|
||||
}
|
||||
|
||||
class DSSTrustDomain final : public TrustDomain
|
||||
class DSSTrustDomain final : public EverythingFailsByDefaultTrustDomain
|
||||
{
|
||||
public:
|
||||
Result GetCertTrust(EndEntityOrCA, const CertPolicyId&,
|
||||
@ -403,56 +334,6 @@ public:
|
||||
trustLevel = TrustLevel::TrustAnchor;
|
||||
return Success;
|
||||
}
|
||||
|
||||
Result FindIssuer(Input, IssuerChecker&, Time) override
|
||||
{
|
||||
ADD_FAILURE();
|
||||
return Result::FATAL_ERROR_LIBRARY_FAILURE;
|
||||
}
|
||||
|
||||
Result CheckRevocation(EndEntityOrCA, const CertID&, Time,
|
||||
/*optional*/ const Input*,
|
||||
/*optional*/ const Input*) override
|
||||
{
|
||||
ADD_FAILURE();
|
||||
return Result::FATAL_ERROR_LIBRARY_FAILURE;
|
||||
}
|
||||
|
||||
Result IsChainValid(const DERArray&, Time) override
|
||||
{
|
||||
return Success;
|
||||
}
|
||||
|
||||
Result DigestBuf(Input, DigestAlgorithm, /*out*/uint8_t*, size_t) override
|
||||
{
|
||||
ADD_FAILURE();
|
||||
return Result::FATAL_ERROR_LIBRARY_FAILURE;
|
||||
}
|
||||
|
||||
Result CheckRSAPublicKeyModulusSizeInBits(EndEntityOrCA, unsigned int)
|
||||
override
|
||||
{
|
||||
ADD_FAILURE();
|
||||
return Result::FATAL_ERROR_LIBRARY_FAILURE;
|
||||
}
|
||||
|
||||
Result VerifyRSAPKCS1SignedDigest(const SignedDigest&, Input) override
|
||||
{
|
||||
ADD_FAILURE();
|
||||
return Result::FATAL_ERROR_LIBRARY_FAILURE;
|
||||
}
|
||||
|
||||
Result CheckECDSACurveIsAcceptable(EndEntityOrCA, NamedCurve) override
|
||||
{
|
||||
ADD_FAILURE();
|
||||
return Result::FATAL_ERROR_LIBRARY_FAILURE;
|
||||
}
|
||||
|
||||
Result VerifyECDSASignedDigest(const SignedDigest&, Input) override
|
||||
{
|
||||
ADD_FAILURE();
|
||||
return Result::FATAL_ERROR_LIBRARY_FAILURE;
|
||||
}
|
||||
};
|
||||
|
||||
class pkixbuild_DSS : public ::testing::Test { };
|
||||
@ -492,7 +373,7 @@ TEST_F(pkixbuild_DSS, DSSEndEntityKeyNotAccepted)
|
||||
nullptr/*stapledOCSPResponse*/));
|
||||
}
|
||||
|
||||
class IssuerNameCheckTrustDomain final : public TrustDomain
|
||||
class IssuerNameCheckTrustDomain final : public DefaultCryptoTrustDomain
|
||||
{
|
||||
public:
|
||||
IssuerNameCheckTrustDomain(const ByteString& issuer, bool expectedKeepGoing)
|
||||
@ -534,35 +415,6 @@ public:
|
||||
return Success;
|
||||
}
|
||||
|
||||
Result DigestBuf(Input input, DigestAlgorithm digestAlg,
|
||||
/*out*/ uint8_t* digestBuf, size_t digestLen) override
|
||||
{
|
||||
return TestDigestBuf(input, digestAlg, digestBuf, digestLen);
|
||||
}
|
||||
|
||||
Result CheckRSAPublicKeyModulusSizeInBits(EndEntityOrCA, unsigned int)
|
||||
override
|
||||
{
|
||||
return Success;
|
||||
}
|
||||
|
||||
Result VerifyRSAPKCS1SignedDigest(const SignedDigest& signedDigest,
|
||||
Input subjectPublicKeyInfo) override
|
||||
{
|
||||
return TestVerifyRSAPKCS1SignedDigest(signedDigest, subjectPublicKeyInfo);
|
||||
}
|
||||
|
||||
Result CheckECDSACurveIsAcceptable(EndEntityOrCA, NamedCurve) override
|
||||
{
|
||||
return Success;
|
||||
}
|
||||
|
||||
Result VerifyECDSASignedDigest(const SignedDigest& signedDigest,
|
||||
Input subjectPublicKeyInfo) override
|
||||
{
|
||||
return TestVerifyECDSASignedDigest(signedDigest, subjectPublicKeyInfo);
|
||||
}
|
||||
|
||||
private:
|
||||
const ByteString issuer;
|
||||
const bool expectedKeepGoing;
|
||||
|
@ -22,10 +22,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "pkix/pkix.h"
|
||||
#include "pkixder.h"
|
||||
#include "pkixgtest.h"
|
||||
#include "pkixtestutil.h"
|
||||
|
||||
using namespace mozilla::pkix;
|
||||
using namespace mozilla::pkix::test;
|
||||
@ -60,7 +58,7 @@ CreateCertWithOneExtension(const char* subjectStr, const ByteString& extension)
|
||||
return CreateCertWithExtensions(subjectStr, extensions);
|
||||
}
|
||||
|
||||
class TrustEverythingTrustDomain final : public TrustDomain
|
||||
class TrustEverythingTrustDomain final : public DefaultCryptoTrustDomain
|
||||
{
|
||||
private:
|
||||
Result GetCertTrust(EndEntityOrCA, const CertPolicyId&, Input,
|
||||
@ -70,13 +68,6 @@ private:
|
||||
return Success;
|
||||
}
|
||||
|
||||
Result FindIssuer(Input /*encodedIssuerName*/, IssuerChecker& /*checker*/,
|
||||
Time /*time*/) override
|
||||
{
|
||||
ADD_FAILURE();
|
||||
return Result::FATAL_ERROR_LIBRARY_FAILURE;
|
||||
}
|
||||
|
||||
Result CheckRevocation(EndEntityOrCA, const CertID&, Time,
|
||||
/*optional*/ const Input*, /*optional*/ const Input*)
|
||||
override
|
||||
@ -88,36 +79,6 @@ private:
|
||||
{
|
||||
return Success;
|
||||
}
|
||||
|
||||
Result DigestBuf(Input input, DigestAlgorithm digestAlg,
|
||||
/*out*/ uint8_t* digestBuf, size_t digestLen) override
|
||||
{
|
||||
return TestDigestBuf(input, digestAlg, digestBuf, digestLen);
|
||||
}
|
||||
|
||||
Result CheckRSAPublicKeyModulusSizeInBits(EndEntityOrCA, unsigned int)
|
||||
override
|
||||
{
|
||||
return Success;
|
||||
}
|
||||
|
||||
Result VerifyRSAPKCS1SignedDigest(const SignedDigest& signedDigest,
|
||||
Input subjectPublicKeyInfo) override
|
||||
{
|
||||
return TestVerifyRSAPKCS1SignedDigest(signedDigest, subjectPublicKeyInfo);
|
||||
}
|
||||
|
||||
Result CheckECDSACurveIsAcceptable(EndEntityOrCA, NamedCurve) override
|
||||
{
|
||||
return Success;
|
||||
}
|
||||
|
||||
Result VerifyECDSASignedDigest(const SignedDigest& signedDigest,
|
||||
Input subjectPublicKeyInfo) override
|
||||
{
|
||||
return TestVerifyECDSASignedDigest(signedDigest, subjectPublicKeyInfo);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// python DottedOIDToCode.py --tlv unknownExtensionOID 1.3.6.1.4.1.13769.666.666.666.1.500.9.3
|
||||
|
@ -3,9 +3,7 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
#include "pkix/pkix.h"
|
||||
#include "pkixgtest.h"
|
||||
#include "pkixtestutil.h"
|
||||
|
||||
using namespace mozilla::pkix;
|
||||
using namespace mozilla::pkix::test;
|
||||
@ -45,7 +43,7 @@ CreateCert(const char* issuerCN,
|
||||
return certDER;
|
||||
}
|
||||
|
||||
class AlgorithmTestsTrustDomain final : public TrustDomain
|
||||
class AlgorithmTestsTrustDomain final : public DefaultCryptoTrustDomain
|
||||
{
|
||||
public:
|
||||
AlgorithmTestsTrustDomain(const ByteString& rootDER,
|
||||
@ -103,35 +101,6 @@ private:
|
||||
return Success;
|
||||
}
|
||||
|
||||
Result DigestBuf(Input input, DigestAlgorithm digestAlg,
|
||||
/*out*/ uint8_t* digestBuf, size_t digestLen) override
|
||||
{
|
||||
return TestDigestBuf(input, digestAlg, digestBuf, digestLen);
|
||||
}
|
||||
|
||||
Result CheckRSAPublicKeyModulusSizeInBits(EndEntityOrCA, unsigned int)
|
||||
override
|
||||
{
|
||||
return Success;
|
||||
}
|
||||
|
||||
Result VerifyRSAPKCS1SignedDigest(const SignedDigest& signedDigest,
|
||||
Input subjectPublicKeyInfo) override
|
||||
{
|
||||
return TestVerifyRSAPKCS1SignedDigest(signedDigest, subjectPublicKeyInfo);
|
||||
}
|
||||
|
||||
Result CheckECDSACurveIsAcceptable(EndEntityOrCA, NamedCurve) override
|
||||
{
|
||||
return Success;
|
||||
}
|
||||
|
||||
Result VerifyECDSASignedDigest(const SignedDigest& signedDigest,
|
||||
Input subjectPublicKeyInfo) override
|
||||
{
|
||||
return TestVerifyECDSASignedDigest(signedDigest, subjectPublicKeyInfo);
|
||||
}
|
||||
|
||||
ByteString rootDER;
|
||||
ByteString rootSubjectDER;
|
||||
ByteString intDER;
|
||||
|
@ -23,8 +23,6 @@
|
||||
*/
|
||||
|
||||
#include "pkixgtest.h"
|
||||
#include "pkix/pkixtypes.h"
|
||||
#include "pkixtestutil.h"
|
||||
|
||||
using namespace mozilla::pkix;
|
||||
using namespace mozilla::pkix::test;
|
||||
|
@ -0,0 +1,358 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This code is made available to you under your choice of the following sets
|
||||
* of licensing terms:
|
||||
*/
|
||||
/* 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/.
|
||||
*/
|
||||
/* Copyright 2015 Mozilla Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "pkixder.h"
|
||||
#include "pkixgtest.h"
|
||||
|
||||
using namespace mozilla::pkix;
|
||||
using namespace mozilla::pkix::test;
|
||||
|
||||
namespace mozilla { namespace pkix {
|
||||
|
||||
extern Result CheckSignatureAlgorithm(
|
||||
TrustDomain& trustDomain, EndEntityOrCA endEntityOrCA,
|
||||
const der::SignedDataWithSignature& signedData,
|
||||
Input signatureValue);
|
||||
|
||||
} } // namespace mozilla::pkix
|
||||
|
||||
struct CheckSignatureAlgorithmTestParams
|
||||
{
|
||||
ByteString signatureAlgorithmValue;
|
||||
ByteString signatureValue;
|
||||
unsigned int signatureLengthInBytes;
|
||||
Result expectedResult;
|
||||
};
|
||||
|
||||
#define BS(s) ByteString(s, MOZILLA_PKIX_ARRAY_LENGTH(s))
|
||||
|
||||
// python DottedOIDToCode.py --tlv sha256WithRSAEncryption 1.2.840.113549.1.1.11
|
||||
static const uint8_t tlv_sha256WithRSAEncryption[] = {
|
||||
0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b
|
||||
};
|
||||
|
||||
// Same as tlv_sha256WithRSAEncryption, except one without the "0x0b" and with
|
||||
// the DER length decreased accordingly.
|
||||
static const uint8_t tlv_sha256WithRSAEncryption_truncated[] = {
|
||||
0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01
|
||||
};
|
||||
|
||||
// python DottedOIDToCode.py --tlv sha-1WithRSAEncryption 1.2.840.113549.1.1.5
|
||||
static const uint8_t tlv_sha_1WithRSAEncryption[] = {
|
||||
0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05
|
||||
};
|
||||
|
||||
// python DottedOIDToCode.py --tlv sha1WithRSASignature 1.3.14.3.2.29
|
||||
static const uint8_t tlv_sha1WithRSASignature[] = {
|
||||
0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1d
|
||||
};
|
||||
|
||||
// python DottedOIDToCode.py --tlv md5WithRSAEncryption 1.2.840.113549.1.1.4
|
||||
static const uint8_t tlv_md5WithRSAEncryption[] = {
|
||||
0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x04
|
||||
};
|
||||
|
||||
static const CheckSignatureAlgorithmTestParams
|
||||
CHECKSIGNATUREALGORITHM_TEST_PARAMS[] =
|
||||
{
|
||||
{ // Both algorithm IDs are empty
|
||||
ByteString(),
|
||||
ByteString(),
|
||||
2048 / 8,
|
||||
Result::ERROR_BAD_DER,
|
||||
},
|
||||
{ // signatureAlgorithm is empty, signature is supported.
|
||||
ByteString(),
|
||||
BS(tlv_sha256WithRSAEncryption),
|
||||
2048 / 8,
|
||||
Result::ERROR_BAD_DER,
|
||||
},
|
||||
{ // signatureAlgorithm is supported, signature is empty.
|
||||
BS(tlv_sha256WithRSAEncryption),
|
||||
ByteString(),
|
||||
2048 / 8,
|
||||
Result::ERROR_BAD_DER,
|
||||
},
|
||||
{ // Algorithms match, both are supported.
|
||||
BS(tlv_sha256WithRSAEncryption),
|
||||
BS(tlv_sha256WithRSAEncryption),
|
||||
2048 / 8,
|
||||
Success
|
||||
},
|
||||
{ // Algorithms do not match because signatureAlgorithm is truncated.
|
||||
BS(tlv_sha256WithRSAEncryption_truncated),
|
||||
BS(tlv_sha256WithRSAEncryption),
|
||||
2048 / 8,
|
||||
Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED
|
||||
},
|
||||
{ // Algorithms do not match because signature is truncated.
|
||||
BS(tlv_sha256WithRSAEncryption),
|
||||
BS(tlv_sha256WithRSAEncryption_truncated),
|
||||
2048 / 8,
|
||||
Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED
|
||||
},
|
||||
{ // Algorithms do not match, both are supported.
|
||||
BS(tlv_sha_1WithRSAEncryption),
|
||||
BS(tlv_sha256WithRSAEncryption),
|
||||
2048 / 8,
|
||||
Result::ERROR_SIGNATURE_ALGORITHM_MISMATCH,
|
||||
},
|
||||
{ // Algorithms do not match, both are supported.
|
||||
BS(tlv_sha256WithRSAEncryption),
|
||||
BS(tlv_sha_1WithRSAEncryption),
|
||||
2048 / 8,
|
||||
Result::ERROR_SIGNATURE_ALGORITHM_MISMATCH,
|
||||
},
|
||||
{ // Algorithms match, both are unsupported.
|
||||
BS(tlv_md5WithRSAEncryption),
|
||||
BS(tlv_md5WithRSAEncryption),
|
||||
2048 / 8,
|
||||
Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED
|
||||
},
|
||||
{ // signatureAlgorithm is unsupported, signature is supported.
|
||||
BS(tlv_md5WithRSAEncryption),
|
||||
BS(tlv_sha256WithRSAEncryption),
|
||||
2048 / 8,
|
||||
Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED
|
||||
},
|
||||
{ // signatureAlgorithm is supported, signature is unsupported.
|
||||
BS(tlv_sha256WithRSAEncryption),
|
||||
BS(tlv_md5WithRSAEncryption),
|
||||
2048 / 8,
|
||||
Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED
|
||||
},
|
||||
{ // Both have the optional NULL parameter.
|
||||
BS(tlv_sha256WithRSAEncryption) + TLV(der::NULLTag, ByteString()),
|
||||
BS(tlv_sha256WithRSAEncryption) + TLV(der::NULLTag, ByteString()),
|
||||
2048 / 8,
|
||||
Success
|
||||
},
|
||||
{ // signatureAlgorithm has the optional NULL parameter, signature doesn't.
|
||||
BS(tlv_sha256WithRSAEncryption) + TLV(der::NULLTag, ByteString()),
|
||||
BS(tlv_sha256WithRSAEncryption),
|
||||
2048 / 8,
|
||||
Success
|
||||
},
|
||||
{ // signatureAlgorithm does not have the optional NULL parameter, signature
|
||||
// does.
|
||||
BS(tlv_sha256WithRSAEncryption),
|
||||
BS(tlv_sha256WithRSAEncryption) + TLV(der::NULLTag, ByteString()),
|
||||
2048 / 8,
|
||||
Success
|
||||
},
|
||||
{ // The different OIDs for RSA-with-SHA1 we support are semantically
|
||||
// equivalent.
|
||||
BS(tlv_sha1WithRSASignature),
|
||||
BS(tlv_sha_1WithRSAEncryption),
|
||||
2048 / 8,
|
||||
Success,
|
||||
},
|
||||
{ // The different OIDs for RSA-with-SHA1 we support are semantically
|
||||
// equivalent (opposite order).
|
||||
BS(tlv_sha_1WithRSAEncryption),
|
||||
BS(tlv_sha1WithRSASignature),
|
||||
2048 / 8,
|
||||
Success,
|
||||
},
|
||||
{ // Algorithms match, both are supported, key size is not a multile of 128
|
||||
// bits. This test verifies that we're not wrongly rounding up the
|
||||
// signature size like we did in the original patch for bug 1131767.
|
||||
BS(tlv_sha256WithRSAEncryption),
|
||||
BS(tlv_sha256WithRSAEncryption),
|
||||
(2048 / 8) - 1,
|
||||
Success
|
||||
},
|
||||
};
|
||||
|
||||
class pkixcheck_CheckSignatureAlgorithm
|
||||
: public ::testing::Test
|
||||
, public ::testing::WithParamInterface<CheckSignatureAlgorithmTestParams>
|
||||
{
|
||||
};
|
||||
|
||||
class pkixcheck_CheckSignatureAlgorithm_TrustDomain final
|
||||
: public EverythingFailsByDefaultTrustDomain
|
||||
{
|
||||
public:
|
||||
explicit pkixcheck_CheckSignatureAlgorithm_TrustDomain(
|
||||
unsigned int publicKeySizeInBits)
|
||||
: publicKeySizeInBits(publicKeySizeInBits)
|
||||
, checkedDigestAlgorithm(false)
|
||||
, checkedModulusSizeInBits(false)
|
||||
{
|
||||
}
|
||||
|
||||
Result CheckSignatureDigestAlgorithm(DigestAlgorithm) override
|
||||
{
|
||||
checkedDigestAlgorithm = true;
|
||||
return Success;
|
||||
}
|
||||
|
||||
Result CheckRSAPublicKeyModulusSizeInBits(EndEntityOrCA endEntityOrCA,
|
||||
unsigned int modulusSizeInBits)
|
||||
override
|
||||
{
|
||||
EXPECT_EQ(EndEntityOrCA::MustBeEndEntity, endEntityOrCA);
|
||||
EXPECT_EQ(publicKeySizeInBits, modulusSizeInBits);
|
||||
checkedModulusSizeInBits = true;
|
||||
return Success;
|
||||
}
|
||||
|
||||
const unsigned int publicKeySizeInBits;
|
||||
bool checkedDigestAlgorithm;
|
||||
bool checkedModulusSizeInBits;
|
||||
};
|
||||
|
||||
TEST_P(pkixcheck_CheckSignatureAlgorithm, CheckSignatureAlgorithm)
|
||||
{
|
||||
const CheckSignatureAlgorithmTestParams& params(GetParam());
|
||||
|
||||
Input signatureValueInput;
|
||||
ASSERT_EQ(Success,
|
||||
signatureValueInput.Init(params.signatureValue.data(),
|
||||
params.signatureValue.length()));
|
||||
|
||||
pkixcheck_CheckSignatureAlgorithm_TrustDomain
|
||||
trustDomain(params.signatureLengthInBytes * 8);
|
||||
|
||||
der::SignedDataWithSignature signedData;
|
||||
ASSERT_EQ(Success,
|
||||
signedData.algorithm.Init(params.signatureAlgorithmValue.data(),
|
||||
params.signatureAlgorithmValue.length()));
|
||||
|
||||
ByteString dummySignature(params.signatureLengthInBytes, 0xDE);
|
||||
ASSERT_EQ(Success,
|
||||
signedData.signature.Init(dummySignature.data(),
|
||||
dummySignature.length()));
|
||||
|
||||
ASSERT_EQ(params.expectedResult,
|
||||
CheckSignatureAlgorithm(trustDomain, EndEntityOrCA::MustBeEndEntity,
|
||||
signedData, signatureValueInput));
|
||||
ASSERT_EQ(params.expectedResult == Success,
|
||||
trustDomain.checkedDigestAlgorithm);
|
||||
ASSERT_EQ(params.expectedResult == Success,
|
||||
trustDomain.checkedModulusSizeInBits);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
pkixcheck_CheckSignatureAlgorithm, pkixcheck_CheckSignatureAlgorithm,
|
||||
testing::ValuesIn(CHECKSIGNATUREALGORITHM_TEST_PARAMS));
|
||||
|
||||
class pkixcheck_CheckSignatureAlgorithm_BuildCertChain_TrustDomain
|
||||
: public DefaultCryptoTrustDomain
|
||||
{
|
||||
public:
|
||||
explicit pkixcheck_CheckSignatureAlgorithm_BuildCertChain_TrustDomain(
|
||||
const ByteString& issuer)
|
||||
: issuer(issuer)
|
||||
{
|
||||
}
|
||||
|
||||
Result GetCertTrust(EndEntityOrCA, const CertPolicyId&,
|
||||
Input cert, /*out*/ TrustLevel& trustLevel) override
|
||||
{
|
||||
trustLevel = InputEqualsByteString(cert, issuer)
|
||||
? TrustLevel::TrustAnchor
|
||||
: TrustLevel::InheritsTrust;
|
||||
return Success;
|
||||
}
|
||||
|
||||
Result FindIssuer(Input, IssuerChecker& checker, Time) override
|
||||
{
|
||||
EXPECT_FALSE(ENCODING_FAILED(issuer));
|
||||
|
||||
Input issuerInput;
|
||||
EXPECT_EQ(Success, issuerInput.Init(issuer.data(), issuer.length()));
|
||||
|
||||
bool keepGoing;
|
||||
EXPECT_EQ(Success, checker.Check(issuerInput, nullptr, keepGoing));
|
||||
EXPECT_FALSE(keepGoing);
|
||||
|
||||
return Success;
|
||||
}
|
||||
|
||||
Result CheckRevocation(EndEntityOrCA, const CertID&, Time,
|
||||
/*optional*/ const Input*,
|
||||
/*optional*/ const Input*) override
|
||||
{
|
||||
return Success;
|
||||
}
|
||||
|
||||
Result IsChainValid(const DERArray&, Time) override
|
||||
{
|
||||
return Success;
|
||||
}
|
||||
|
||||
ByteString issuer;
|
||||
};
|
||||
|
||||
// Test that CheckSignatureAlgorithm actually gets called at some point when
|
||||
// BuildCertChain is called.
|
||||
TEST_F(pkixcheck_CheckSignatureAlgorithm, BuildCertChain)
|
||||
{
|
||||
ScopedTestKeyPair keyPair(CloneReusedKeyPair());
|
||||
ASSERT_TRUE(keyPair);
|
||||
|
||||
ByteString issuerExtensions[2];
|
||||
issuerExtensions[0] = CreateEncodedBasicConstraints(true, nullptr,
|
||||
Critical::No);
|
||||
ASSERT_FALSE(ENCODING_FAILED(issuerExtensions[0]));
|
||||
|
||||
ByteString issuer(CreateEncodedCertificate(3,
|
||||
sha256WithRSAEncryption,
|
||||
CreateEncodedSerialNumber(1),
|
||||
CNToDERName("issuer"),
|
||||
oneDayBeforeNow, oneDayAfterNow,
|
||||
CNToDERName("issuer"),
|
||||
*keyPair,
|
||||
issuerExtensions,
|
||||
*keyPair,
|
||||
sha256WithRSAEncryption));
|
||||
ASSERT_FALSE(ENCODING_FAILED(issuer));
|
||||
|
||||
ByteString subject(CreateEncodedCertificate(3,
|
||||
TLV(der::SEQUENCE,
|
||||
BS(tlv_sha_1WithRSAEncryption)),
|
||||
CreateEncodedSerialNumber(2),
|
||||
CNToDERName("issuer"),
|
||||
oneDayBeforeNow, oneDayAfterNow,
|
||||
CNToDERName("subject"),
|
||||
*keyPair,
|
||||
nullptr,
|
||||
*keyPair,
|
||||
sha256WithRSAEncryption));
|
||||
ASSERT_FALSE(ENCODING_FAILED(subject));
|
||||
|
||||
Input subjectInput;
|
||||
ASSERT_EQ(Success, subjectInput.Init(subject.data(), subject.length()));
|
||||
pkixcheck_CheckSignatureAlgorithm_BuildCertChain_TrustDomain
|
||||
trustDomain(issuer);
|
||||
Result rv = BuildCertChain(trustDomain, subjectInput, Now(),
|
||||
EndEntityOrCA::MustBeEndEntity,
|
||||
KeyUsage::noParticularKeyUsageRequired,
|
||||
KeyPurposeId::anyExtendedKeyUsage,
|
||||
CertPolicyId::anyPolicy,
|
||||
nullptr);
|
||||
ASSERT_EQ(Result::ERROR_SIGNATURE_ALGORITHM_MISMATCH, rv);
|
||||
}
|
@ -23,8 +23,6 @@
|
||||
*/
|
||||
|
||||
#include "pkixgtest.h"
|
||||
#include "pkix/pkixtypes.h"
|
||||
#include "pkixtestutil.h"
|
||||
|
||||
using namespace mozilla::pkix;
|
||||
using namespace mozilla::pkix::test;
|
||||
|
@ -23,12 +23,11 @@
|
||||
*/
|
||||
|
||||
#include <limits>
|
||||
#include <stdint.h>
|
||||
#include <vector>
|
||||
#include "pkixgtest.h"
|
||||
|
||||
#include "pkixder.h"
|
||||
#include "pkixtestutil.h"
|
||||
#include "stdint.h"
|
||||
#include "pkixgtest.h"
|
||||
|
||||
using namespace mozilla::pkix;
|
||||
using namespace mozilla::pkix::der;
|
||||
|
@ -56,7 +56,8 @@
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
#include "pkix/Result.h"
|
||||
#include "pkix/pkix.h"
|
||||
#include "pkixtestutil.h"
|
||||
|
||||
// PrintTo must be in the same namespace as the type we're overloading it for.
|
||||
namespace mozilla { namespace pkix {
|
||||
@ -82,6 +83,122 @@ extern const std::time_t now;
|
||||
extern const std::time_t oneDayBeforeNow;
|
||||
extern const std::time_t oneDayAfterNow;
|
||||
|
||||
|
||||
class EverythingFailsByDefaultTrustDomain : public TrustDomain
|
||||
{
|
||||
public:
|
||||
Result GetCertTrust(EndEntityOrCA, const CertPolicyId&,
|
||||
Input, /*out*/ TrustLevel&) override
|
||||
{
|
||||
ADD_FAILURE();
|
||||
return NotReached("GetCertTrust should not be called",
|
||||
Result::FATAL_ERROR_LIBRARY_FAILURE);
|
||||
}
|
||||
|
||||
Result FindIssuer(Input, IssuerChecker&, Time) override
|
||||
{
|
||||
ADD_FAILURE();
|
||||
return NotReached("FindIssuer should not be called",
|
||||
Result::FATAL_ERROR_LIBRARY_FAILURE);
|
||||
}
|
||||
|
||||
Result CheckRevocation(EndEntityOrCA, const CertID&, Time,
|
||||
/*optional*/ const Input*,
|
||||
/*optional*/ const Input*) override
|
||||
{
|
||||
ADD_FAILURE();
|
||||
return NotReached("CheckRevocation should not be called",
|
||||
Result::FATAL_ERROR_LIBRARY_FAILURE);
|
||||
}
|
||||
|
||||
Result IsChainValid(const DERArray&, Time) override
|
||||
{
|
||||
ADD_FAILURE();
|
||||
return NotReached("IsChainValid should not be called",
|
||||
Result::FATAL_ERROR_LIBRARY_FAILURE);
|
||||
}
|
||||
|
||||
Result DigestBuf(Input, DigestAlgorithm, /*out*/ uint8_t*, size_t) override
|
||||
{
|
||||
ADD_FAILURE();
|
||||
return NotReached("DigestBuf should not be called",
|
||||
Result::FATAL_ERROR_LIBRARY_FAILURE);
|
||||
}
|
||||
|
||||
Result CheckSignatureDigestAlgorithm(DigestAlgorithm) override
|
||||
{
|
||||
ADD_FAILURE();
|
||||
return NotReached("CheckSignatureDigestAlgorithm should not be called",
|
||||
Result::FATAL_ERROR_LIBRARY_FAILURE);
|
||||
}
|
||||
|
||||
Result CheckECDSACurveIsAcceptable(EndEntityOrCA, NamedCurve) override
|
||||
{
|
||||
ADD_FAILURE();
|
||||
return NotReached("CheckECDSACurveIsAcceptable should not be called",
|
||||
Result::FATAL_ERROR_LIBRARY_FAILURE);
|
||||
}
|
||||
|
||||
Result VerifyECDSASignedDigest(const SignedDigest&, Input) override
|
||||
{
|
||||
ADD_FAILURE();
|
||||
return NotReached("VerifyECDSASignedDigest should not be called",
|
||||
Result::FATAL_ERROR_LIBRARY_FAILURE);
|
||||
}
|
||||
|
||||
Result CheckRSAPublicKeyModulusSizeInBits(EndEntityOrCA, unsigned int)
|
||||
override
|
||||
{
|
||||
ADD_FAILURE();
|
||||
return NotReached("CheckRSAPublicKeyModulusSizeInBits should not be called",
|
||||
Result::FATAL_ERROR_LIBRARY_FAILURE);
|
||||
}
|
||||
|
||||
Result VerifyRSAPKCS1SignedDigest(const SignedDigest&, Input) override
|
||||
{
|
||||
ADD_FAILURE();
|
||||
return NotReached("VerifyRSAPKCS1SignedDigest should not be called",
|
||||
Result::FATAL_ERROR_LIBRARY_FAILURE);
|
||||
}
|
||||
};
|
||||
|
||||
class DefaultCryptoTrustDomain : public EverythingFailsByDefaultTrustDomain
|
||||
{
|
||||
Result DigestBuf(Input item, DigestAlgorithm digestAlg,
|
||||
/*out*/ uint8_t* digestBuf, size_t digestBufLen) override
|
||||
{
|
||||
return TestDigestBuf(item, digestAlg, digestBuf, digestBufLen);
|
||||
}
|
||||
|
||||
Result CheckSignatureDigestAlgorithm(DigestAlgorithm) override
|
||||
{
|
||||
return Success;
|
||||
}
|
||||
|
||||
Result CheckECDSACurveIsAcceptable(EndEntityOrCA, NamedCurve) override
|
||||
{
|
||||
return Success;
|
||||
}
|
||||
|
||||
Result VerifyECDSASignedDigest(const SignedDigest& signedDigest,
|
||||
Input subjectPublicKeyInfo) override
|
||||
{
|
||||
return TestVerifyECDSASignedDigest(signedDigest, subjectPublicKeyInfo);
|
||||
}
|
||||
|
||||
Result CheckRSAPublicKeyModulusSizeInBits(EndEntityOrCA, unsigned int)
|
||||
override
|
||||
{
|
||||
return Success;
|
||||
}
|
||||
|
||||
Result VerifyRSAPKCS1SignedDigest(const SignedDigest& signedDigest,
|
||||
Input subjectPublicKeyInfo) override
|
||||
{
|
||||
return TestVerifyRSAPKCS1SignedDigest(signedDigest, subjectPublicKeyInfo);
|
||||
}
|
||||
};
|
||||
|
||||
} } } // namespace mozilla::pkix::test
|
||||
|
||||
#endif // mozilla_pkix_pkixgtest_h
|
||||
|
@ -21,11 +21,9 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
#include "pkix/pkix.h"
|
||||
#include "pkixcheck.h"
|
||||
#include "pkixder.h"
|
||||
#include "pkixgtest.h"
|
||||
#include "pkixtestutil.h"
|
||||
#include "pkixutil.h"
|
||||
|
||||
namespace mozilla { namespace pkix {
|
||||
|
@ -23,42 +23,15 @@
|
||||
*/
|
||||
|
||||
#include "pkixgtest.h"
|
||||
#include "pkix/pkix.h"
|
||||
#include "pkixder.h"
|
||||
#include "pkixtestutil.h"
|
||||
|
||||
using namespace mozilla::pkix;
|
||||
using namespace mozilla::pkix::test;
|
||||
|
||||
class CreateEncodedOCSPRequestTrustDomain final : public TrustDomain
|
||||
class CreateEncodedOCSPRequestTrustDomain final
|
||||
: public EverythingFailsByDefaultTrustDomain
|
||||
{
|
||||
private:
|
||||
Result GetCertTrust(EndEntityOrCA, const CertPolicyId&, Input,
|
||||
/*out*/ TrustLevel&) override
|
||||
{
|
||||
ADD_FAILURE();
|
||||
return Result::FATAL_ERROR_LIBRARY_FAILURE;
|
||||
}
|
||||
|
||||
Result FindIssuer(Input, IssuerChecker&, Time) override
|
||||
{
|
||||
ADD_FAILURE();
|
||||
return Result::FATAL_ERROR_LIBRARY_FAILURE;
|
||||
}
|
||||
|
||||
Result CheckRevocation(EndEntityOrCA, const CertID&, Time, const Input*,
|
||||
const Input*) override
|
||||
{
|
||||
ADD_FAILURE();
|
||||
return Result::FATAL_ERROR_LIBRARY_FAILURE;
|
||||
}
|
||||
|
||||
Result IsChainValid(const DERArray&, Time) override
|
||||
{
|
||||
ADD_FAILURE();
|
||||
return Result::FATAL_ERROR_LIBRARY_FAILURE;
|
||||
}
|
||||
|
||||
Result DigestBuf(Input item, DigestAlgorithm digestAlg,
|
||||
/*out*/ uint8_t *digestBuf, size_t digestBufLen)
|
||||
override
|
||||
@ -67,28 +40,9 @@ private:
|
||||
}
|
||||
|
||||
Result CheckRSAPublicKeyModulusSizeInBits(EndEntityOrCA, unsigned int)
|
||||
final override
|
||||
override
|
||||
{
|
||||
ADD_FAILURE();
|
||||
return Result::FATAL_ERROR_LIBRARY_FAILURE;
|
||||
}
|
||||
|
||||
Result VerifyRSAPKCS1SignedDigest(const SignedDigest&, Input) override
|
||||
{
|
||||
ADD_FAILURE();
|
||||
return Result::FATAL_ERROR_LIBRARY_FAILURE;
|
||||
}
|
||||
|
||||
Result CheckECDSACurveIsAcceptable(EndEntityOrCA, NamedCurve) final override
|
||||
{
|
||||
ADD_FAILURE();
|
||||
return Result::FATAL_ERROR_LIBRARY_FAILURE;
|
||||
}
|
||||
|
||||
Result VerifyECDSASignedDigest(const SignedDigest&, Input) override
|
||||
{
|
||||
ADD_FAILURE();
|
||||
return Result::FATAL_ERROR_LIBRARY_FAILURE;
|
||||
return Success;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -22,21 +22,18 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "pkix/pkix.h"
|
||||
#include "pkixgtest.h"
|
||||
#include "pkixtestutil.h"
|
||||
|
||||
using namespace mozilla::pkix;
|
||||
using namespace mozilla::pkix::test;
|
||||
|
||||
const uint16_t END_ENTITY_MAX_LIFETIME_IN_DAYS = 10;
|
||||
|
||||
class OCSPTestTrustDomain : public TrustDomain
|
||||
// Note that CheckRevocation is never called for OCSP signing certificates.
|
||||
class OCSPTestTrustDomain : public DefaultCryptoTrustDomain
|
||||
{
|
||||
public:
|
||||
OCSPTestTrustDomain()
|
||||
{
|
||||
}
|
||||
OCSPTestTrustDomain() { }
|
||||
|
||||
Result GetCertTrust(EndEntityOrCA endEntityOrCA, const CertPolicyId&,
|
||||
Input, /*out*/ TrustLevel& trustLevel)
|
||||
@ -46,62 +43,6 @@ public:
|
||||
trustLevel = TrustLevel::InheritsTrust;
|
||||
return Success;
|
||||
}
|
||||
|
||||
Result FindIssuer(Input, IssuerChecker&, Time) final override
|
||||
{
|
||||
ADD_FAILURE();
|
||||
return Result::FATAL_ERROR_LIBRARY_FAILURE;
|
||||
}
|
||||
|
||||
Result CheckRevocation(EndEntityOrCA, const CertID&, Time,
|
||||
/*optional*/ const Input*, /*optional*/ const Input*)
|
||||
final override
|
||||
{
|
||||
// TODO: I guess mozilla::pkix should support revocation of designated
|
||||
// OCSP responder eventually, but we don't now, so this function should
|
||||
// never get called.
|
||||
ADD_FAILURE();
|
||||
return Result::FATAL_ERROR_LIBRARY_FAILURE;
|
||||
}
|
||||
|
||||
Result IsChainValid(const DERArray&, Time) final override
|
||||
{
|
||||
ADD_FAILURE();
|
||||
return Result::FATAL_ERROR_LIBRARY_FAILURE;
|
||||
}
|
||||
|
||||
Result DigestBuf(Input item, DigestAlgorithm digestAlg,
|
||||
/*out*/ uint8_t* digestBuf, size_t digestBufLen)
|
||||
final override
|
||||
{
|
||||
return TestDigestBuf(item, digestAlg, digestBuf, digestBufLen);
|
||||
}
|
||||
|
||||
Result CheckRSAPublicKeyModulusSizeInBits(EndEntityOrCA, unsigned int)
|
||||
final override
|
||||
{
|
||||
return Success;
|
||||
}
|
||||
|
||||
Result VerifyRSAPKCS1SignedDigest(const SignedDigest& signedDigest,
|
||||
Input subjectPublicKeyInfo) override
|
||||
{
|
||||
return TestVerifyRSAPKCS1SignedDigest(signedDigest, subjectPublicKeyInfo);
|
||||
}
|
||||
|
||||
Result CheckECDSACurveIsAcceptable(EndEntityOrCA, NamedCurve) final override
|
||||
{
|
||||
return Success;
|
||||
}
|
||||
|
||||
Result VerifyECDSASignedDigest(const SignedDigest& signedDigest,
|
||||
Input subjectPublicKeyInfo) override
|
||||
{
|
||||
return TestVerifyECDSASignedDigest(signedDigest, subjectPublicKeyInfo);
|
||||
}
|
||||
|
||||
OCSPTestTrustDomain(const OCSPTestTrustDomain&) = delete;
|
||||
void operator=(const OCSPTestTrustDomain&) = delete;
|
||||
};
|
||||
|
||||
namespace {
|
||||
|
@ -28,6 +28,7 @@ catch (e) {
|
||||
// under the "accessibility.*" branch.
|
||||
const PREFS_WHITELIST = [
|
||||
"accessibility.",
|
||||
"apz.",
|
||||
"browser.cache.",
|
||||
"browser.display.",
|
||||
"browser.download.folderList",
|
||||
|
@ -1446,8 +1446,9 @@ function readStringFromInputStream(inputStream) {
|
||||
sis.init(inputStream);
|
||||
var text = sis.read(sis.available());
|
||||
sis.close();
|
||||
if (text[text.length - 1] == "\n")
|
||||
if (text && text[text.length - 1] == "\n") {
|
||||
text = text.slice(0, -1);
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
|
@ -77,9 +77,8 @@ function runTest() {
|
||||
removeDirRecursive(addonPrepDir);
|
||||
}
|
||||
catch (e) {
|
||||
dump("Unable to remove directory\n" +
|
||||
"path: " + addonPrepDir.path + "\n" +
|
||||
"Exception: " + e + "\n");
|
||||
logTestInfo("Unable to remove directory. Path: " + addonPrepDir.path +
|
||||
", Exception: " + e);
|
||||
}
|
||||
|
||||
resetAddons(finishTest);
|
||||
|
@ -167,6 +167,8 @@ const TEST_ADDONS = [ "appdisabled_1", "appdisabled_2",
|
||||
"updateversion_1", "updateversion_2",
|
||||
"userdisabled_1", "userdisabled_2", "hotfix" ];
|
||||
|
||||
const LOG_FUNCTION = info;
|
||||
|
||||
var gURLData = URL_HOST + "/" + REL_PATH_DATA + "/";
|
||||
|
||||
var gTestTimeout = 240000; // 4 minutes
|
||||
@ -660,10 +662,11 @@ function waitForRemoteContentLoaded(aEvent) {
|
||||
// expected or isn't the event's originalTarget.
|
||||
if (gRemoteContentState != gTest.expectedRemoteContentState ||
|
||||
aEvent.originalTarget != gRemoteContent) {
|
||||
debugDump("returning early\n" +
|
||||
"gRemoteContentState: " + gRemoteContentState + "\n" +
|
||||
debugDump("returning early. " +
|
||||
"gRemoteContentState: " +
|
||||
gRemoteContentState + ", " +
|
||||
"expectedRemoteContentState: " +
|
||||
gTest.expectedRemoteContentState + "\n" +
|
||||
gTest.expectedRemoteContentState + ", " +
|
||||
"aEvent.originalTarget.nodeName: " +
|
||||
aEvent.originalTarget.nodeName);
|
||||
return true;
|
||||
@ -955,9 +958,8 @@ function resetFiles() {
|
||||
removeDirRecursive(updatedDir);
|
||||
}
|
||||
catch (e) {
|
||||
dump("Unable to remove directory\n" +
|
||||
"path: " + updatedDir.path + "\n" +
|
||||
"Exception: " + e + "\n");
|
||||
logTestInfo("Unable to remove directory. Path: " + updatedDir.path +
|
||||
", Exception: " + e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -430,8 +430,8 @@ function removeUpdateDirsAndFiles() {
|
||||
if (file.exists())
|
||||
file.remove(false);
|
||||
} catch (e) {
|
||||
dump("Unable to remove file\nPath: " + file.path +
|
||||
"\nException: " + e + "\n");
|
||||
logTestInfo("Unable to remove file. Path: " + file.path +
|
||||
", Exception: " + e);
|
||||
}
|
||||
|
||||
file = getUpdatesXMLFile(false);
|
||||
@ -439,8 +439,8 @@ function removeUpdateDirsAndFiles() {
|
||||
if (file.exists())
|
||||
file.remove(false);
|
||||
} catch (e) {
|
||||
dump("Unable to remove file\nPath: " + file.path +
|
||||
"\nException: " + e + "\n");
|
||||
logTestInfo("Unable to remove file. Path: " + file.path +
|
||||
", Exception: " + e);
|
||||
}
|
||||
|
||||
// This fails sporadically on Mac OS X so wrap it in a try catch
|
||||
@ -448,8 +448,8 @@ function removeUpdateDirsAndFiles() {
|
||||
try {
|
||||
cleanUpdatesDir(updatesDir);
|
||||
} catch (e) {
|
||||
dump("Unable to remove files / directories from directory\nPath: " +
|
||||
updatesDir.path + "\nException: " + e + "\n");
|
||||
logTestInfo("Unable to remove files / directories from directory. Path: " +
|
||||
updatesDir.path + ", Exception: " + e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -483,8 +483,8 @@ function cleanUpdatesDir(aDir) {
|
||||
try {
|
||||
entry.remove(true);
|
||||
} catch (e) {
|
||||
dump("cleanUpdatesDir: unable to remove directory\nPath: " +
|
||||
entry.path + "\nException: " + e + "\n");
|
||||
logTestInfo("cleanUpdatesDir: unable to remove directory. Path: " +
|
||||
entry.path + ", Exception: " + e);
|
||||
throw(e);
|
||||
}
|
||||
}
|
||||
@ -493,8 +493,8 @@ function cleanUpdatesDir(aDir) {
|
||||
try {
|
||||
entry.remove(false);
|
||||
} catch (e) {
|
||||
dump("cleanUpdatesDir: unable to remove file\nPath: " + entry.path +
|
||||
"\nException: " + e + "\n");
|
||||
logTestInfo("cleanUpdatesDir: unable to remove file. Path: " +
|
||||
entry.path + ", Exception: " + e);
|
||||
throw(e);
|
||||
}
|
||||
}
|
||||
@ -614,8 +614,9 @@ function logTestInfo(aText, aCaller) {
|
||||
(mm < 10 ? "0" + mm : mm) + ":" +
|
||||
(ss < 10 ? "0" + ss : ss) + ":" +
|
||||
(ms < 10 ? "00" + ms : ms < 100 ? "0" + ms : ms);
|
||||
dump(time + " | TEST-INFO | " + caller.filename + " | [" + caller.name +
|
||||
" : " + caller.lineNumber + "] " + aText + "\n");
|
||||
let msg = time + " | TEST-INFO | " + caller.filename + " | [" + caller.name +
|
||||
" : " + caller.lineNumber + "] " + aText;
|
||||
LOG_FUNCTION(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -61,74 +61,84 @@ function run_test() {
|
||||
doTestFinish();
|
||||
}
|
||||
|
||||
if (IS_WIN) {
|
||||
/**
|
||||
* Determines a unique mutex name for the installation.
|
||||
*
|
||||
* @return Global mutex path.
|
||||
*/
|
||||
function getPerInstallationMutexName() {
|
||||
let hasher = AUS_Cc["@mozilla.org/security/hash;1"].
|
||||
createInstance(AUS_Ci.nsICryptoHash);
|
||||
hasher.init(hasher.SHA1);
|
||||
|
||||
let exeFile = Services.dirsvc.get(XRE_EXECUTABLE_FILE, AUS_Ci.nsILocalFile);
|
||||
|
||||
let converter = AUS_Cc["@mozilla.org/intl/scriptableunicodeconverter"].
|
||||
createInstance(AUS_Ci.nsIScriptableUnicodeConverter);
|
||||
converter.charset = "UTF-8";
|
||||
let data = converter.convertToByteArray(exeFile.path.toLowerCase());
|
||||
|
||||
hasher.update(data, data.length);
|
||||
return "Global\\MozillaUpdateMutex-" + hasher.finish(true);
|
||||
/**
|
||||
* Determines a unique mutex name for the installation.
|
||||
*
|
||||
* @return Global mutex path.
|
||||
*/
|
||||
function getPerInstallationMutexName() {
|
||||
if (!IS_WIN) {
|
||||
do_throw("Windows only function called by a different platform!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes a Win32 handle.
|
||||
*
|
||||
* @param aHandle
|
||||
* The handle to close.
|
||||
*/
|
||||
function closeHandle(aHandle) {
|
||||
let lib = ctypes.open("kernel32.dll");
|
||||
let CloseHandle = lib.declare("CloseHandle",
|
||||
ctypes.winapi_abi,
|
||||
ctypes.int32_t, /* success */
|
||||
ctypes.void_t.ptr); /* handle */
|
||||
CloseHandle(aHandle);
|
||||
lib.close();
|
||||
}
|
||||
let hasher = AUS_Cc["@mozilla.org/security/hash;1"].
|
||||
createInstance(AUS_Ci.nsICryptoHash);
|
||||
hasher.init(hasher.SHA1);
|
||||
|
||||
/**
|
||||
* Creates a mutex.
|
||||
*
|
||||
* @param aName
|
||||
* The name for the mutex.
|
||||
* @return The Win32 handle to the mutex.
|
||||
*/
|
||||
function createMutex(aName) {
|
||||
const INITIAL_OWN = 1;
|
||||
const ERROR_ALREADY_EXISTS = 0xB7;
|
||||
let lib = ctypes.open("kernel32.dll");
|
||||
let CreateMutexW = lib.declare("CreateMutexW",
|
||||
ctypes.winapi_abi,
|
||||
ctypes.void_t.ptr, /* return handle */
|
||||
ctypes.void_t.ptr, /* security attributes */
|
||||
ctypes.int32_t, /* initial owner */
|
||||
ctypes.char16_t.ptr); /* name */
|
||||
let exeFile = Services.dirsvc.get(XRE_EXECUTABLE_FILE, AUS_Ci.nsILocalFile);
|
||||
|
||||
let handle = CreateMutexW(null, INITIAL_OWN, aName);
|
||||
lib.close();
|
||||
let alreadyExists = ctypes.winLastError == ERROR_ALREADY_EXISTS;
|
||||
if (handle && !handle.isNull() && alreadyExists) {
|
||||
closeHandle(handle);
|
||||
handle = null;
|
||||
}
|
||||
let converter = AUS_Cc["@mozilla.org/intl/scriptableunicodeconverter"].
|
||||
createInstance(AUS_Ci.nsIScriptableUnicodeConverter);
|
||||
converter.charset = "UTF-8";
|
||||
let data = converter.convertToByteArray(exeFile.path.toLowerCase());
|
||||
|
||||
if (handle && handle.isNull()) {
|
||||
handle = null;
|
||||
}
|
||||
|
||||
return handle;
|
||||
}
|
||||
hasher.update(data, data.length);
|
||||
return "Global\\MozillaUpdateMutex-" + hasher.finish(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes a Win32 handle.
|
||||
*
|
||||
* @param aHandle
|
||||
* The handle to close.
|
||||
*/
|
||||
function closeHandle(aHandle) {
|
||||
if (!IS_WIN) {
|
||||
do_throw("Windows only function called by a different platform!");
|
||||
}
|
||||
|
||||
let lib = ctypes.open("kernel32.dll");
|
||||
let CloseHandle = lib.declare("CloseHandle",
|
||||
ctypes.winapi_abi,
|
||||
ctypes.int32_t, /* success */
|
||||
ctypes.void_t.ptr); /* handle */
|
||||
CloseHandle(aHandle);
|
||||
lib.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a mutex.
|
||||
*
|
||||
* @param aName
|
||||
* The name for the mutex.
|
||||
* @return The Win32 handle to the mutex.
|
||||
*/
|
||||
function createMutex(aName) {
|
||||
if (!IS_WIN) {
|
||||
do_throw("Windows only function called by a different platform!");
|
||||
}
|
||||
|
||||
const INITIAL_OWN = 1;
|
||||
const ERROR_ALREADY_EXISTS = 0xB7;
|
||||
let lib = ctypes.open("kernel32.dll");
|
||||
let CreateMutexW = lib.declare("CreateMutexW",
|
||||
ctypes.winapi_abi,
|
||||
ctypes.void_t.ptr, /* return handle */
|
||||
ctypes.void_t.ptr, /* security attributes */
|
||||
ctypes.int32_t, /* initial owner */
|
||||
ctypes.char16_t.ptr); /* name */
|
||||
|
||||
let handle = CreateMutexW(null, INITIAL_OWN, aName);
|
||||
lib.close();
|
||||
let alreadyExists = ctypes.winLastError == ERROR_ALREADY_EXISTS;
|
||||
if (handle && !handle.isNull() && alreadyExists) {
|
||||
closeHandle(handle);
|
||||
handle = null;
|
||||
}
|
||||
|
||||
if (handle && handle.isNull()) {
|
||||
handle = null;
|
||||
}
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ function run_test() {
|
||||
do_check_eq(gUpdateManager.activeUpdate, null);
|
||||
// Verify that the active-update.xml file has had the update from the old
|
||||
// channel removed.
|
||||
file = getUpdatesXMLFile(true);
|
||||
let file = getUpdatesXMLFile(true);
|
||||
logTestInfo("verifying contents of " + FILE_UPDATE_ACTIVE);
|
||||
do_check_eq(readFile(file), getLocalUpdatesXMLString(""));
|
||||
|
||||
|
@ -5,7 +5,10 @@
|
||||
|
||||
const KEY_UPDATE_ARCHIVE_DIR = "UpdArchD"
|
||||
|
||||
let gActiveUpdate = null;
|
||||
let gActiveUpdate;
|
||||
let gDirService;
|
||||
let gDirProvider;
|
||||
let gOldProviders;
|
||||
|
||||
function FakeDirProvider() {}
|
||||
FakeDirProvider.prototype = {
|
||||
|
@ -152,7 +152,7 @@ IncrementalDownload.prototype = {
|
||||
tm.mainThread.dispatch(function() {
|
||||
this._observer = observer.QueryInterface(AUS_Ci.nsIRequestObserver);
|
||||
this._ctxt = ctxt;
|
||||
this._observer.onStartRequest(this, this.ctxt);
|
||||
this._observer.onStartRequest(this, this._ctxt);
|
||||
let mar = getTestDirFile(FILE_SIMPLE_MAR);
|
||||
mar.copyTo(this._destination.parent, this._destination.leafName);
|
||||
var status = AUS_Cr.NS_OK
|
||||
|
@ -2,6 +2,8 @@
|
||||
* 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 INSTALL_LOCALE = "@AB_CD@";
|
||||
const MOZ_APP_NAME = "@MOZ_APP_NAME@";
|
||||
const BIN_SUFFIX = "@BIN_SUFFIX@";
|
||||
@ -140,6 +142,8 @@ const PIPE_TO_NULL = ">nul";
|
||||
const PIPE_TO_NULL = "> /dev/null 2>&1";
|
||||
#endif
|
||||
|
||||
const LOG_FUNCTION = do_print;
|
||||
|
||||
// This default value will be overridden when using the http server.
|
||||
var gURLData = URL_HOST + "/";
|
||||
|
||||
@ -1417,41 +1421,46 @@ function getMockUpdRootD() {
|
||||
}
|
||||
#endif
|
||||
|
||||
if (IS_WIN) {
|
||||
const kLockFileName = "updated.update_in_progress.lock";
|
||||
/**
|
||||
* Helper function for locking a directory on Windows.
|
||||
*
|
||||
* @param aDir
|
||||
* The nsIFile for the directory to lock.
|
||||
*/
|
||||
function lockDirectory(aDir) {
|
||||
var file = aDir.clone();
|
||||
file.append(kLockFileName);
|
||||
file.create(file.NORMAL_FILE_TYPE, 0o444);
|
||||
file.QueryInterface(AUS_Ci.nsILocalFileWin);
|
||||
file.fileAttributesWin |= file.WFA_READONLY;
|
||||
file.fileAttributesWin &= ~file.WFA_READWRITE;
|
||||
logTestInfo("testing the successful creation of the lock file");
|
||||
do_check_true(file.exists());
|
||||
do_check_false(file.isWritable());
|
||||
const kLockFileName = "updated.update_in_progress.lock";
|
||||
/**
|
||||
* Helper function for locking a directory on Windows.
|
||||
*
|
||||
* @param aDir
|
||||
* The nsIFile for the directory to lock.
|
||||
*/
|
||||
function lockDirectory(aDir) {
|
||||
if (!IS_WIN) {
|
||||
do_throw("Windows only function called by a different platform!");
|
||||
}
|
||||
/**
|
||||
* Helper function for unlocking a directory on Windows.
|
||||
*
|
||||
* @param aDir
|
||||
* The nsIFile for the directory to unlock.
|
||||
*/
|
||||
function unlockDirectory(aDir) {
|
||||
var file = aDir.clone();
|
||||
file.append(kLockFileName);
|
||||
file.QueryInterface(AUS_Ci.nsILocalFileWin);
|
||||
file.fileAttributesWin |= file.WFA_READWRITE;
|
||||
file.fileAttributesWin &= ~file.WFA_READONLY;
|
||||
logTestInfo("removing and testing the successful removal of the lock file");
|
||||
file.remove(false);
|
||||
do_check_false(file.exists());
|
||||
|
||||
let file = aDir.clone();
|
||||
file.append(kLockFileName);
|
||||
file.create(file.NORMAL_FILE_TYPE, 0o444);
|
||||
file.QueryInterface(AUS_Ci.nsILocalFileWin);
|
||||
file.fileAttributesWin |= file.WFA_READONLY;
|
||||
file.fileAttributesWin &= ~file.WFA_READWRITE;
|
||||
logTestInfo("testing the successful creation of the lock file");
|
||||
do_check_true(file.exists());
|
||||
do_check_false(file.isWritable());
|
||||
}
|
||||
/**
|
||||
* Helper function for unlocking a directory on Windows.
|
||||
*
|
||||
* @param aDir
|
||||
* The nsIFile for the directory to unlock.
|
||||
*/
|
||||
function unlockDirectory(aDir) {
|
||||
if (!IS_WIN) {
|
||||
do_throw("Windows only function called by a different platform!");
|
||||
}
|
||||
let file = aDir.clone();
|
||||
file.append(kLockFileName);
|
||||
file.QueryInterface(AUS_Ci.nsILocalFileWin);
|
||||
file.fileAttributesWin |= file.WFA_READWRITE;
|
||||
file.fileAttributesWin &= ~file.WFA_READONLY;
|
||||
logTestInfo("removing and testing the successful removal of the lock file");
|
||||
file.remove(false);
|
||||
do_check_false(file.exists());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -3229,7 +3238,7 @@ function start_httpserver() {
|
||||
|
||||
if (!dir.isDirectory()) {
|
||||
do_throw("A file instead of a directory was specified for HttpServer " +
|
||||
"registerDirectory! Path: " + dir.path + "\n");
|
||||
"registerDirectory! Path: " + dir.path);
|
||||
}
|
||||
|
||||
AUS_Cu.import("resource://testing-common/httpd.js");
|
||||
@ -3639,7 +3648,7 @@ function setEnvironment() {
|
||||
env.set("XPCOM_DEBUG_BREAK", "warn");
|
||||
|
||||
if (gStageUpdate) {
|
||||
logTestInfo("setting the MOZ_UPDATE_STAGING environment variable to 1\n");
|
||||
logTestInfo("setting the MOZ_UPDATE_STAGING environment variable to 1");
|
||||
env.set("MOZ_UPDATE_STAGING", "1");
|
||||
}
|
||||
|
||||
@ -3705,7 +3714,7 @@ function resetEnvironment() {
|
||||
}
|
||||
|
||||
if (gStageUpdate) {
|
||||
logTestInfo("removing the MOZ_UPDATE_STAGING environment variable\n");
|
||||
logTestInfo("removing the MOZ_UPDATE_STAGING environment variable");
|
||||
env.set("MOZ_UPDATE_STAGING", "");
|
||||
}
|
||||
|
||||
|
@ -14,8 +14,6 @@ function run_test() {
|
||||
// The mock XMLHttpRequest is MUCH faster
|
||||
overrideXHR(callHandleEvent);
|
||||
standardInit();
|
||||
// The HTTP server is only used for the mar file downloads which is slow
|
||||
start_httpserver();
|
||||
|
||||
let registrar = Components.manager.QueryInterface(AUS_Ci.nsIComponentRegistrar);
|
||||
registrar.registerFactory(Components.ID("{1dfeb90a-2193-45d5-9cb8-864928b2af55}"),
|
||||
|
@ -5,6 +5,8 @@
|
||||
|
||||
/* General Update Timer Manager Tests */
|
||||
|
||||
'use strict';
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cm = Components.manager;
|
||||
@ -103,8 +105,8 @@ const TESTS = [ {
|
||||
lastUpdateTime : 0
|
||||
} ];
|
||||
|
||||
var gUTM;
|
||||
var gNextFunc;
|
||||
let gUTM;
|
||||
let gNextFunc;
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "gPref",
|
||||
"@mozilla.org/preferences-service;1",
|
||||
@ -127,7 +129,7 @@ function run_test() {
|
||||
gPref.setBoolPref(PREF_APP_UPDATE_LOG_ALL, true);
|
||||
|
||||
// Remove existing update timers to prevent them from being notified
|
||||
var entries = gCatMan.enumerateCategory(CATEGORY_UPDATE_TIMER);
|
||||
let entries = gCatMan.enumerateCategory(CATEGORY_UPDATE_TIMER);
|
||||
while (entries.hasMoreElements()) {
|
||||
let entry = entries.getNext().QueryInterface(Ci.nsISupportsCString).data;
|
||||
gCatMan.deleteCategoryEntry(CATEGORY_UPDATE_TIMER, entry, false);
|
||||
@ -165,7 +167,7 @@ function run_test1thru7() {
|
||||
TESTS[1].defaultInterval].join(","), false, true);
|
||||
|
||||
// has a last update time of now - 43200 which is half of its interval
|
||||
var lastUpdateTime = Math.round(Date.now() / 1000) - 43200;
|
||||
let lastUpdateTime = Math.round(Date.now() / 1000) - 43200;
|
||||
gPref.setIntPref(PREF_BRANCH_LAST_UPDATE_TIME + TESTS[2].timerID, lastUpdateTime);
|
||||
gCompReg.registerFactory(TESTS[2].classID, TESTS[2].desc,
|
||||
TESTS[2].contractID, gTest3Factory);
|
||||
@ -201,7 +203,7 @@ function run_test1thru7() {
|
||||
TESTS[5].defaultInterval].join(","), false, true);
|
||||
|
||||
// has a next update time 24 hours from now
|
||||
var nextUpdateTime = Math.round(Date.now() / 1000) + 86400;
|
||||
let nextUpdateTime = Math.round(Date.now() / 1000) + 86400;
|
||||
gPref.setIntPref(PREF_BRANCH_LAST_UPDATE_TIME + TESTS[6].timerID, nextUpdateTime);
|
||||
gCompReg.registerFactory(TESTS[6].classID, TESTS[6].desc,
|
||||
TESTS[6].contractID, gTest7Factory);
|
||||
@ -212,40 +214,41 @@ function run_test1thru7() {
|
||||
}
|
||||
|
||||
function finished_test1thru7() {
|
||||
if (TESTS[4].notified && TESTS[5].notified && TESTS[6].notified)
|
||||
if (TESTS[4].notified && TESTS[5].notified && TESTS[6].notified) {
|
||||
do_timeout(0, gNextFunc);
|
||||
}
|
||||
}
|
||||
|
||||
function check_test1thru7() {
|
||||
dump("Testing: a category registered timer didn't fire due to an invalid " +
|
||||
"default interval\n");
|
||||
do_print("Testing: a category registered timer didn't fire due to an " +
|
||||
"invalid default interval");
|
||||
do_check_false(TESTS[0].notified);
|
||||
|
||||
dump("Testing: a category registered timer didn't fire due to not " +
|
||||
"implementing nsITimerCallback\n");
|
||||
do_print("Testing: a category registered timer didn't fire due to not " +
|
||||
"implementing nsITimerCallback");
|
||||
do_check_false(TESTS[1].notified);
|
||||
|
||||
dump("Testing: a category registered timer didn't fire due to the next " +
|
||||
"update time being in the future\n");
|
||||
do_print("Testing: a category registered timer didn't fire due to the next " +
|
||||
"update time being in the future");
|
||||
do_check_false(TESTS[2].notified);
|
||||
|
||||
dump("Testing: a category registered timer didn't fire due to not " +
|
||||
"having a notify method\n");
|
||||
do_print("Testing: a category registered timer didn't fire due to not " +
|
||||
"having a notify method");
|
||||
do_check_false(TESTS[3].notified);
|
||||
|
||||
dump("Testing: a category registered timer has fired\n");
|
||||
do_print("Testing: a category registered timer has fired");
|
||||
do_check_true(TESTS[4].notified);
|
||||
|
||||
dump("Testing: a category registered timer fired that has an interval " +
|
||||
"preference that overrides a default that wouldn't have fired yet\n");
|
||||
do_print("Testing: a category registered timer fired that has an interval " +
|
||||
"preference that overrides a default that wouldn't have fired yet");
|
||||
do_check_true(TESTS[5].notified);
|
||||
|
||||
dump("Testing: a category registered timer has fired due to the next " +
|
||||
"update time being reset due to a future last update time\n");
|
||||
do_print("Testing: a category registered timer has fired due to the next " +
|
||||
"update time being reset due to a future last update time");
|
||||
do_check_true(TESTS[6].notified);
|
||||
|
||||
dump("Testing: two category registered timers last update time has " +
|
||||
"user values\n");
|
||||
do_print("Testing: two category registered timers last update time has " +
|
||||
"user values");
|
||||
do_check_true(gPref.prefHasUserValue(PREF_BRANCH_LAST_UPDATE_TIME +
|
||||
TESTS[4].timerID));
|
||||
do_check_true(gPref.prefHasUserValue(PREF_BRANCH_LAST_UPDATE_TIME +
|
||||
@ -256,23 +259,22 @@ function check_test1thru7() {
|
||||
gCatMan.deleteCategoryEntry(CATEGORY_UPDATE_TIMER, TESTS[1].desc, true);
|
||||
gCatMan.deleteCategoryEntry(CATEGORY_UPDATE_TIMER, TESTS[2].desc, true);
|
||||
gCatMan.deleteCategoryEntry(CATEGORY_UPDATE_TIMER, TESTS[3].desc, true);
|
||||
var count = 0;
|
||||
var entries = gCatMan.enumerateCategory(CATEGORY_UPDATE_TIMER);
|
||||
let count = 0;
|
||||
let entries = gCatMan.enumerateCategory(CATEGORY_UPDATE_TIMER);
|
||||
while (entries.hasMoreElements()) {
|
||||
let entry = entries.getNext().QueryInterface(Ci.nsISupportsCString).data;
|
||||
gCatMan.deleteCategoryEntry(CATEGORY_UPDATE_TIMER, entry, false);
|
||||
count++;
|
||||
}
|
||||
dump("Testing: no " + CATEGORY_UPDATE_TIMER + " categories are still " +
|
||||
"registered\n");
|
||||
do_print("Testing: no " + CATEGORY_UPDATE_TIMER + " categories are still " +
|
||||
"registered");
|
||||
do_check_eq(count, 0);
|
||||
|
||||
do_timeout(0, run_test8);
|
||||
}
|
||||
|
||||
function run_test8() {
|
||||
gNextFunc = check_test8;
|
||||
for (var i = 0; i < 2; i++) {
|
||||
for (let i = 0; i < 2; i++) {
|
||||
gPref.setIntPref(PREF_BRANCH_LAST_UPDATE_TIME + TESTS[7 + i].timerID, 1);
|
||||
gCompReg.registerFactory(TESTS[7 + i].classID, TESTS[7 + i].desc,
|
||||
TESTS[7 + i].contractID, eval("gTest" + (8 + i) + "Factory"));
|
||||
@ -281,15 +283,16 @@ function run_test8() {
|
||||
}
|
||||
}
|
||||
|
||||
function check_test8() {
|
||||
var self = arguments.callee;
|
||||
self.timesCalled = (self.timesCalled || 0) + 1;
|
||||
if (self.timesCalled < 2)
|
||||
function check_test8(aTestTimerCallback) {
|
||||
aTestTimerCallback.timesCalled = (aTestTimerCallback.timesCalled || 0) + 1;
|
||||
if (aTestTimerCallback.timesCalled < 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
dump("Testing: two registerTimer registered timers have fired\n");
|
||||
for (var i = 0; i < 2; i++)
|
||||
do_print("Testing: two registerTimer registered timers have fired");
|
||||
for (let i = 0; i < 2; i++) {
|
||||
do_check_true(TESTS[7 + i].notified);
|
||||
}
|
||||
|
||||
// Check that 'staggering' has happened: even though the two events wanted to fire at
|
||||
// the same time, we waited a full MAIN_TIMER_INTERVAL between them.
|
||||
@ -297,71 +300,76 @@ function check_test8() {
|
||||
do_check_true(Math.abs(TESTS[7].notifyTime - TESTS[8].notifyTime) >=
|
||||
MAIN_TIMER_INTERVAL * 0.5);
|
||||
|
||||
dump("Testing: two registerTimer registered timers last update time have " +
|
||||
"been updated\n");
|
||||
for (var i = 0; i < 2; i++)
|
||||
do_print("Testing: two registerTimer registered timers last update time have " +
|
||||
"been updated");
|
||||
for (let i = 0; i < 2; i++) {
|
||||
do_check_neq(gPref.getIntPref(PREF_BRANCH_LAST_UPDATE_TIME + TESTS[7 + i].timerID), 1);
|
||||
}
|
||||
end_test();
|
||||
}
|
||||
|
||||
var gTest1TimerCallback = {
|
||||
const gTest1TimerCallback = {
|
||||
notify: function T1CB_notify(aTimer) {
|
||||
do_throw("gTest1TimerCallback notify method should not have been called");
|
||||
},
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsITimerCallback])
|
||||
};
|
||||
|
||||
var gTest1Factory = {
|
||||
const gTest1Factory = {
|
||||
createInstance: function (outer, iid) {
|
||||
if (outer == null)
|
||||
if (outer == null) {
|
||||
return gTest1TimerCallback.QueryInterface(iid);
|
||||
}
|
||||
throw Cr.NS_ERROR_NO_AGGREGATION;
|
||||
}
|
||||
};
|
||||
|
||||
var gTest2TimerCallback = {
|
||||
const gTest2TimerCallback = {
|
||||
notify: function T2CB_notify(aTimer) {
|
||||
do_throw("gTest2TimerCallback notify method should not have been called");
|
||||
},
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsITimer])
|
||||
};
|
||||
|
||||
var gTest2Factory = {
|
||||
const gTest2Factory = {
|
||||
createInstance: function (outer, iid) {
|
||||
if (outer == null)
|
||||
if (outer == null) {
|
||||
return gTest2TimerCallback.QueryInterface(iid);
|
||||
}
|
||||
throw Cr.NS_ERROR_NO_AGGREGATION;
|
||||
}
|
||||
};
|
||||
|
||||
var gTest3TimerCallback = {
|
||||
const gTest3TimerCallback = {
|
||||
notify: function T3CB_notify(aTimer) {
|
||||
do_throw("gTest3TimerCallback notify method should not have been called");
|
||||
},
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsITimerCallback])
|
||||
};
|
||||
|
||||
var gTest3Factory = {
|
||||
const gTest3Factory = {
|
||||
createInstance: function (outer, iid) {
|
||||
if (outer == null)
|
||||
if (outer == null) {
|
||||
return gTest3TimerCallback.QueryInterface(iid);
|
||||
}
|
||||
throw Cr.NS_ERROR_NO_AGGREGATION;
|
||||
}
|
||||
};
|
||||
|
||||
var gTest4TimerCallback = {
|
||||
const gTest4TimerCallback = {
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsITimerCallback])
|
||||
};
|
||||
|
||||
var gTest4Factory = {
|
||||
const gTest4Factory = {
|
||||
createInstance: function (outer, iid) {
|
||||
if (outer == null)
|
||||
if (outer == null) {
|
||||
return gTest4TimerCallback.QueryInterface(iid);
|
||||
}
|
||||
throw Cr.NS_ERROR_NO_AGGREGATION;
|
||||
}
|
||||
};
|
||||
|
||||
var gTest5TimerCallback = {
|
||||
const gTest5TimerCallback = {
|
||||
notify: function T5CB_notify(aTimer) {
|
||||
gCatMan.deleteCategoryEntry(CATEGORY_UPDATE_TIMER, TESTS[4].desc, true);
|
||||
TESTS[4].notified = true;
|
||||
@ -370,15 +378,16 @@ var gTest5TimerCallback = {
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsITimerCallback])
|
||||
};
|
||||
|
||||
var gTest5Factory = {
|
||||
const gTest5Factory = {
|
||||
createInstance: function (outer, iid) {
|
||||
if (outer == null)
|
||||
if (outer == null) {
|
||||
return gTest5TimerCallback.QueryInterface(iid);
|
||||
}
|
||||
throw Cr.NS_ERROR_NO_AGGREGATION;
|
||||
}
|
||||
};
|
||||
|
||||
var gTest6TimerCallback = {
|
||||
const gTest6TimerCallback = {
|
||||
notify: function T6CB_notify(aTimer) {
|
||||
gCatMan.deleteCategoryEntry(CATEGORY_UPDATE_TIMER, TESTS[5].desc, true);
|
||||
TESTS[5].notified = true;
|
||||
@ -387,15 +396,16 @@ var gTest6TimerCallback = {
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsITimerCallback])
|
||||
};
|
||||
|
||||
var gTest6Factory = {
|
||||
const gTest6Factory = {
|
||||
createInstance: function (outer, iid) {
|
||||
if (outer == null)
|
||||
if (outer == null) {
|
||||
return gTest6TimerCallback.QueryInterface(iid);
|
||||
}
|
||||
throw Cr.NS_ERROR_NO_AGGREGATION;
|
||||
}
|
||||
};
|
||||
|
||||
var gTest7TimerCallback = {
|
||||
const gTest7TimerCallback = {
|
||||
notify: function T7CB_notify(aTimer) {
|
||||
gCatMan.deleteCategoryEntry(CATEGORY_UPDATE_TIMER, TESTS[6].desc, true);
|
||||
TESTS[6].notified = true;
|
||||
@ -404,44 +414,51 @@ var gTest7TimerCallback = {
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsITimerCallback])
|
||||
};
|
||||
|
||||
var gTest7Factory = {
|
||||
const gTest7Factory = {
|
||||
createInstance: function (outer, iid) {
|
||||
if (outer == null)
|
||||
if (outer == null) {
|
||||
return gTest7TimerCallback.QueryInterface(iid);
|
||||
}
|
||||
throw Cr.NS_ERROR_NO_AGGREGATION;
|
||||
}
|
||||
};
|
||||
|
||||
var gTest8TimerCallback = {
|
||||
const gTest8TimerCallback = {
|
||||
notify: function T8CB_notify(aTimer) {
|
||||
TESTS[7].notified = true;
|
||||
TESTS[7].notifyTime = Date.now();
|
||||
do_timeout(0, check_test8);
|
||||
do_timeout(0, function() {
|
||||
check_test8(gTest8TimerCallback);
|
||||
});
|
||||
},
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsITimerCallback])
|
||||
};
|
||||
|
||||
var gTest8Factory = {
|
||||
const gTest8Factory = {
|
||||
createInstance: function (outer, iid) {
|
||||
if (outer == null)
|
||||
if (outer == null) {
|
||||
return gTest8TimerCallback.QueryInterface(iid);
|
||||
}
|
||||
throw Cr.NS_ERROR_NO_AGGREGATION;
|
||||
}
|
||||
};
|
||||
|
||||
var gTest9TimerCallback = {
|
||||
const gTest9TimerCallback = {
|
||||
notify: function T9CB_notify(aTimer) {
|
||||
TESTS[8].notified = true;
|
||||
TESTS[8].notifyTime = Date.now();
|
||||
do_timeout(0, check_test8);
|
||||
do_timeout(0, function() {
|
||||
check_test8(gTest9TimerCallback);
|
||||
});
|
||||
},
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsITimerCallback])
|
||||
};
|
||||
|
||||
var gTest9Factory = {
|
||||
const gTest9Factory = {
|
||||
createInstance: function (outer, iid) {
|
||||
if (outer == null)
|
||||
if (outer == null) {
|
||||
return gTest9TimerCallback.QueryInterface(iid);
|
||||
}
|
||||
throw Cr.NS_ERROR_NO_AGGREGATION;
|
||||
}
|
||||
};
|
||||
|
@ -82,7 +82,8 @@ let runTest = Task.async(function*() {
|
||||
|
||||
let request = navigator.mozApps.installPackage(app.manifestURL);
|
||||
|
||||
let (deferred = Promise.defer()) {
|
||||
{
|
||||
let deferred = Promise.defer();
|
||||
request.onerror = function() {
|
||||
deferred.reject(this.error.name);
|
||||
};
|
||||
@ -93,13 +94,14 @@ let runTest = Task.async(function*() {
|
||||
let appObject = request.result;
|
||||
ok(appObject, "app is non-null");
|
||||
|
||||
let (deferred = Promise.defer()) {
|
||||
{
|
||||
let deferred = Promise.defer();
|
||||
appObject.ondownloaderror = function() {
|
||||
deferred.reject(appObject.downloadError.name);
|
||||
};
|
||||
appObject.ondownloadapplied = deferred.resolve;
|
||||
yield deferred.promise;
|
||||
};
|
||||
}
|
||||
|
||||
while (!WebappOSUtils.isLaunchable(app)) {
|
||||
yield wait(1000);
|
||||
|
@ -80,8 +80,10 @@ let runTest = Task.async(function*() {
|
||||
|
||||
confirmNextPopup();
|
||||
|
||||
let (request = navigator.mozApps.installPackage(app.manifestURL)) {
|
||||
let (deferred = Promise.defer()) {
|
||||
{
|
||||
let request = navigator.mozApps.installPackage(app.manifestURL);
|
||||
{
|
||||
let deferred = Promise.defer();
|
||||
request.onerror = function() {
|
||||
deferred.reject(this.error.name);
|
||||
};
|
||||
@ -92,14 +94,15 @@ let runTest = Task.async(function*() {
|
||||
let appObject = request.result;
|
||||
ok(appObject, "app is non-null");
|
||||
|
||||
let (deferred = Promise.defer()) {
|
||||
{
|
||||
let deferred = Promise.defer();
|
||||
appObject.ondownloaderror = function() {
|
||||
deferred.reject(appObject.downloadError.name);
|
||||
};
|
||||
appObject.ondownloadapplied = deferred.resolve;
|
||||
yield deferred.promise;
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
while (!WebappOSUtils.isLaunchable(app)) {
|
||||
yield wait(1000);
|
||||
@ -112,8 +115,10 @@ let runTest = Task.async(function*() {
|
||||
|
||||
confirmNextPopup();
|
||||
|
||||
let (request = navigator.mozApps.installPackage(app.manifestURL)) {
|
||||
let (deferred = Promise.defer()) {
|
||||
{
|
||||
let request = navigator.mozApps.installPackage(app.manifestURL);
|
||||
{
|
||||
let deferred = Promise.defer();
|
||||
request.onerror = function() {
|
||||
deferred.reject(this.error.name);
|
||||
};
|
||||
@ -124,14 +129,15 @@ let runTest = Task.async(function*() {
|
||||
let appObject = request.result;
|
||||
ok(appObject, "app is non-null");
|
||||
|
||||
let (deferred = Promise.defer()) {
|
||||
{
|
||||
let deferred = Promise.defer();
|
||||
appObject.ondownloaderror = function() {
|
||||
deferred.reject(appObject.downloadError.name);
|
||||
};
|
||||
appObject.ondownloadapplied = deferred.resolve;
|
||||
yield deferred.promise;
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
while (!WebappOSUtils.isLaunchable(app)) {
|
||||
yield wait(1000);
|
||||
|
@ -62,7 +62,8 @@ let runTest = Task.async(function*() {
|
||||
|
||||
let request = navigator.mozApps.install(app.manifestURL);
|
||||
|
||||
let (deferred = Promise.defer()) {
|
||||
{
|
||||
let deferred = Promise.defer();
|
||||
request.onerror = function() {
|
||||
deferred.reject(this.error.name);
|
||||
};
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user