зеркало из https://github.com/mozilla/gecko-dev.git
merge mozilla-inbound to mozilla-central a=merge
This commit is contained in:
Коммит
b26729a177
2
CLOBBER
2
CLOBBER
|
@ -22,4 +22,4 @@
|
|||
# changes to stick? As of bug 928195, this shouldn't be necessary! Please
|
||||
# don't change CLOBBER for WebIDL changes any more.
|
||||
|
||||
Bug 1215696 - Update mp4parse to v0.1.1
|
||||
Bug 1217261 - Update mp4parse to v0.1.2
|
||||
|
|
|
@ -26,6 +26,7 @@ class ProxyAccessibleWrap : public AccessibleWrap
|
|||
virtual void Shutdown() override
|
||||
{
|
||||
mBits.proxy = nullptr;
|
||||
mStateFlags |= eIsDefunct;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -39,7 +40,11 @@ public:
|
|||
mBits.proxy = aProxy;
|
||||
}
|
||||
|
||||
virtual void Shutdown() override { mBits.proxy = nullptr; }
|
||||
virtual void Shutdown() override
|
||||
{
|
||||
mBits.proxy = nullptr;
|
||||
mStateFlags |= eIsDefunct;
|
||||
}
|
||||
};
|
||||
|
||||
class DocProxyAccessibleWrap : public HyperTextProxyAccessibleWrap
|
||||
|
|
|
@ -123,7 +123,7 @@ AccessibleWrap::QueryInterface(REFIID iid, void** ppv)
|
|||
return E_NOINTERFACE;
|
||||
|
||||
*ppv = static_cast<IEnumVARIANT*>(new ChildrenEnumVariant(this));
|
||||
} else if (IID_IServiceProvider == iid && !IsProxy())
|
||||
} else if (IID_IServiceProvider == iid)
|
||||
*ppv = new ServiceProvider(this);
|
||||
else if (IID_ISimpleDOMNode == iid && !IsProxy()) {
|
||||
if (IsDefunct() || (!HasOwnContent() && !IsDoc()))
|
||||
|
@ -1514,7 +1514,7 @@ AccessibleWrap::GetXPAccessibleFor(const VARIANT& aVarChild)
|
|||
#endif
|
||||
|
||||
// If it is a document then just return an accessible.
|
||||
if (IsDoc())
|
||||
if (child && IsDoc())
|
||||
return child;
|
||||
|
||||
// Otherwise check whether the accessible is a child (this path works for
|
||||
|
@ -1534,8 +1534,15 @@ AccessibleWrap::GetXPAccessibleFor(const VARIANT& aVarChild)
|
|||
if (IsProxy()) {
|
||||
DocAccessibleParent* proxyDoc = Proxy()->Document();
|
||||
AccessibleWrap* wrapper = GetProxiedAccessibleInSubtree(proxyDoc, id);
|
||||
if (!wrapper)
|
||||
return nullptr;
|
||||
|
||||
MOZ_ASSERT(wrapper->IsProxy());
|
||||
|
||||
if (proxyDoc == this->Proxy()) {
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
ProxyAccessible* parent = wrapper->Proxy();
|
||||
while (parent && parent != proxyDoc) {
|
||||
if (parent == this->Proxy()) {
|
||||
|
@ -1570,6 +1577,16 @@ AccessibleWrap::GetXPAccessibleFor(const VARIANT& aVarChild)
|
|||
continue;
|
||||
}
|
||||
|
||||
if (doc == this) {
|
||||
AccessibleWrap* proxyWrapper =
|
||||
GetProxiedAccessibleInSubtree(remoteDocs->ElementAt(i), id);
|
||||
if (proxyWrapper) {
|
||||
return proxyWrapper;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
Accessible* parent = outerDoc;
|
||||
while (parent && parent != doc) {
|
||||
if (parent == this) {
|
||||
|
|
|
@ -126,20 +126,21 @@ var gPrivacyPane = {
|
|||
* The list of preferences which affect the initial history mode settings.
|
||||
* If the auto start private browsing mode pref is active, the initial
|
||||
* history mode would be set to "Don't remember anything".
|
||||
* If all of these preferences have their default values, and the auto-start
|
||||
* If ALL of these preferences are set to the values that correspond
|
||||
* to keeping some part of history, and the auto-start
|
||||
* private browsing mode is not active, the initial history mode would be
|
||||
* set to "Remember everything".
|
||||
* Otherwise, the initial history mode would be set to "Custom".
|
||||
*
|
||||
* Extensions adding their own preferences can append their IDs to this array if needed.
|
||||
* Extensions adding their own preferences can set values here if needed.
|
||||
*/
|
||||
prefsForDefault: [
|
||||
"places.history.enabled",
|
||||
"browser.formfill.enable",
|
||||
"network.cookie.cookieBehavior",
|
||||
"network.cookie.lifetimePolicy",
|
||||
"privacy.sanitize.sanitizeOnShutdown"
|
||||
],
|
||||
prefsForKeepingHistory: {
|
||||
"places.history.enabled": true, // History is enabled
|
||||
"browser.formfill.enable": true, // Form information is saved
|
||||
"network.cookie.cookieBehavior": 0, // All cookies are enabled
|
||||
"network.cookie.lifetimePolicy": 0, // Cookies use supplied lifetime
|
||||
"privacy.sanitize.sanitizeOnShutdown": false, // Private date is NOT cleared on shutdown
|
||||
},
|
||||
|
||||
/**
|
||||
* The list of control IDs which are dependent on the auto-start private
|
||||
|
@ -158,16 +159,15 @@ var gPrivacyPane = {
|
|||
],
|
||||
|
||||
/**
|
||||
* Check whether all the preferences values are set to their default values
|
||||
* Check whether preferences values are set to keep history
|
||||
*
|
||||
* @param aPrefs an array of pref names to check for
|
||||
* @returns boolean true if all of the prefs are set to their default values,
|
||||
* @returns boolean true if all of the prefs are set to keep history,
|
||||
* false otherwise
|
||||
*/
|
||||
_checkDefaultValues: function(aPrefs) {
|
||||
for (let i = 0; i < aPrefs.length; ++i) {
|
||||
let pref = document.getElementById(aPrefs[i]);
|
||||
if (pref.value != pref.defaultValue)
|
||||
_checkHistoryValues: function(aPrefs) {
|
||||
for (let pref of Object.keys(aPrefs)) {
|
||||
if (document.getElementById(pref).value != aPrefs[pref])
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
@ -181,7 +181,7 @@ var gPrivacyPane = {
|
|||
let mode;
|
||||
let getVal = aPref => document.getElementById(aPref).value;
|
||||
|
||||
if (this._checkDefaultValues(this.prefsForDefault)) {
|
||||
if (this._checkHistoryValues(this.prefsForKeepingHistory)) {
|
||||
if (getVal("browser.privatebrowsing.autostart"))
|
||||
mode = "dontremember";
|
||||
else
|
||||
|
|
|
@ -68,6 +68,7 @@ CAIRO_VERSION=1.10
|
|||
PANGO_VERSION=1.22.0
|
||||
GTK2_VERSION=2.18.0
|
||||
GTK3_VERSION=3.4.0
|
||||
GDK_VERSION_MIN_REQUIRED=GDK_VERSION_3_4
|
||||
WINDRES_VERSION=2.14.90
|
||||
W32API_VERSION=3.14
|
||||
GNOMEUI_VERSION=2.2.0
|
||||
|
@ -4247,6 +4248,8 @@ if test "$COMPILE_ENVIRONMENT"; then
|
|||
dnl of a make reference because of how TK_LIBS is mangled in toolkit/library/moz.build
|
||||
dnl for GTK+3 builds.
|
||||
TK_LIBS=$MOZ_GTK3_LIBS
|
||||
AC_DEFINE_UNQUOTED(GDK_VERSION_MIN_REQUIRED,$GDK_VERSION_MIN_REQUIRED)
|
||||
AC_DEFINE_UNQUOTED(GDK_VERSION_MAX_ALLOWED,$GDK_VERSION_MIN_REQUIRED)
|
||||
GLIB_VERSION_MAX_ALLOWED=GLIB_VERSION_2_32
|
||||
fi
|
||||
if test "$MOZ_ENABLE_GTK2"; then
|
||||
|
|
|
@ -5491,6 +5491,7 @@ nsDocShell::LoadPage(nsISupports* aPageDescriptor, uint32_t aDisplayType)
|
|||
return rv;
|
||||
}
|
||||
shEntry->SetURI(newUri);
|
||||
shEntry->SetOriginalURI(nullptr);
|
||||
}
|
||||
|
||||
rv = LoadHistoryEntry(shEntry, LOAD_HISTORY);
|
||||
|
|
|
@ -105,7 +105,7 @@ Throw(JSContext* aCx, nsresult aRv, const nsACString& aMessage)
|
|||
nsCOMPtr<nsIException> existingException = runtime->GetPendingException();
|
||||
if (existingException) {
|
||||
nsresult nr;
|
||||
if (NS_SUCCEEDED(existingException->GetResult(&nr)) &&
|
||||
if (NS_SUCCEEDED(existingException->GetResult(&nr)) &&
|
||||
aRv == nr) {
|
||||
// Reuse the existing exception.
|
||||
|
||||
|
@ -387,7 +387,8 @@ static void
|
|||
GetValueIfNotCached(JSContext* aCx, JSObject* aStack,
|
||||
JS::SavedFrameResult (*aPropGetter)(JSContext*,
|
||||
JS::Handle<JSObject*>,
|
||||
GetterOutParamType),
|
||||
GetterOutParamType,
|
||||
JS::SavedFrameSelfHosted),
|
||||
bool aIsCached, bool* aCanCache, bool* aUseCachedValue,
|
||||
ReturnType aValue)
|
||||
{
|
||||
|
@ -405,7 +406,7 @@ GetValueIfNotCached(JSContext* aCx, JSObject* aStack,
|
|||
*aUseCachedValue = false;
|
||||
JS::ExposeObjectToActiveJS(stack);
|
||||
|
||||
aPropGetter(aCx, stack, aValue);
|
||||
aPropGetter(aCx, stack, aValue, JS::SavedFrameSelfHosted::Exclude);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP JSStackFrame::GetFilename(nsAString& aFilename)
|
||||
|
|
|
@ -210,7 +210,10 @@ Response::Constructor(const GlobalObject& aGlobal,
|
|||
|
||||
if (!contentType.IsVoid() &&
|
||||
!internalResponse->Headers()->Has(NS_LITERAL_CSTRING("Content-Type"), aRv)) {
|
||||
internalResponse->Headers()->Append(NS_LITERAL_CSTRING("Content-Type"), contentType, aRv);
|
||||
// Ignore Append() failing here.
|
||||
ErrorResult error;
|
||||
internalResponse->Headers()->Append(NS_LITERAL_CSTRING("Content-Type"), contentType, error);
|
||||
error.SuppressException();
|
||||
}
|
||||
|
||||
if (aRv.Failed()) {
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
clip-path: url(#clipPath);
|
||||
filter: opacity(0%);
|
||||
will-change: transform;
|
||||
perspective: 10px;
|
||||
transform: scale(0);
|
||||
}
|
||||
/* The following styles are copied from ua.css to ensure that
|
||||
|
|
|
@ -153,6 +153,14 @@ public:
|
|||
virtual bool IsWaitForDataSupported() { return false; }
|
||||
virtual RefPtr<WaitForDataPromise> WaitForData(MediaData::Type aType) { MOZ_CRASH(); }
|
||||
|
||||
// By default, the reader return the decoded data. Some readers support
|
||||
// retuning demuxed data.
|
||||
virtual bool IsDemuxOnlySupported() const { return false; }
|
||||
|
||||
// Configure the reader to return demuxed or decoded data
|
||||
// upon calls to Request{Audio,Video}Data.
|
||||
virtual void SetDemuxOnly(bool /*aDemuxedOnly*/) {}
|
||||
|
||||
virtual bool HasAudio() = 0;
|
||||
virtual bool HasVideo() = 0;
|
||||
|
||||
|
|
|
@ -354,6 +354,8 @@ private:
|
|||
// be held.
|
||||
bool IsPlaying() const;
|
||||
|
||||
// TODO: Those callback function may receive demuxed-only data.
|
||||
// Need to figure out a suitable API name for this case.
|
||||
void OnAudioDecoded(MediaData* aAudioSample);
|
||||
void OnVideoDecoded(MediaData* aVideoSample);
|
||||
void OnNotDecoded(MediaData::Type aType, MediaDecoderReader::NotDecodedReason aReason);
|
||||
|
|
|
@ -74,6 +74,7 @@ MediaFormatReader::MediaFormatReader(AbstractMediaDecoder* aDecoder,
|
|||
, mIsEncrypted(false)
|
||||
, mTrackDemuxersMayBlock(false)
|
||||
, mHardwareAccelerationDisabled(false)
|
||||
, mDemuxOnly(false)
|
||||
, mVideoFrameContainer(aVideoFrameContainer)
|
||||
{
|
||||
MOZ_ASSERT(aDemuxer);
|
||||
|
@ -570,7 +571,7 @@ MediaFormatReader::ShouldSkip(bool aSkipToNextKeyframe, media::TimeUnit aTimeThr
|
|||
|
||||
RefPtr<MediaDecoderReader::VideoDataPromise>
|
||||
MediaFormatReader::RequestVideoData(bool aSkipToNextKeyframe,
|
||||
int64_t aTimeThreshold)
|
||||
int64_t aTimeThreshold)
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
MOZ_DIAGNOSTIC_ASSERT(mSeekPromise.IsEmpty(), "No sample requests allowed while seeking");
|
||||
|
@ -901,8 +902,21 @@ MediaFormatReader::RequestDemuxSamples(TrackType aTrack)
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
bool
|
||||
MediaFormatReader::DecodeDemuxedSamples(TrackType aTrack,
|
||||
MediaRawData* aSample)
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
auto& decoder = GetDecoderData(aTrack);
|
||||
if (NS_FAILED(decoder.mDecoder->Input(aSample))) {
|
||||
LOG("Unable to pass frame to decoder");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
MediaFormatReader::HandleDemuxedSamples(TrackType aTrack,
|
||||
AbstractMediaDecoder::AutoNotifyDecoded& aA)
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
|
@ -1005,12 +1019,20 @@ MediaFormatReader::DecodeDemuxedSamples(TrackType aTrack,
|
|||
if (aTrack == TrackInfo::kVideoTrack) {
|
||||
aA.mParsed++;
|
||||
}
|
||||
if (NS_FAILED(decoder.mDecoder->Input(sample))) {
|
||||
LOG("Unable to pass frame to decoder");
|
||||
|
||||
if (mDemuxOnly) {
|
||||
ReturnOutput(sample, aTrack);
|
||||
} else if (!DecodeDemuxedSamples(aTrack, sample)) {
|
||||
NotifyError(aTrack);
|
||||
return;
|
||||
}
|
||||
|
||||
decoder.mQueuedSamples.RemoveElementAt(0);
|
||||
if (mDemuxOnly) {
|
||||
// If demuxed-only case, ReturnOutput will resolve with one demuxed data.
|
||||
// Then we should stop doing the iteration.
|
||||
return;
|
||||
}
|
||||
samplesPending = true;
|
||||
}
|
||||
|
||||
|
@ -1150,8 +1172,8 @@ MediaFormatReader::Update(TrackType aTrack)
|
|||
|
||||
// Demux samples if we don't have some.
|
||||
RequestDemuxSamples(aTrack);
|
||||
// Decode all pending demuxed samples.
|
||||
DecodeDemuxedSamples(aTrack, a);
|
||||
|
||||
HandleDemuxedSamples(aTrack, a);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -1166,19 +1188,21 @@ MediaFormatReader::ReturnOutput(MediaData* aData, TrackType aTrack)
|
|||
}
|
||||
|
||||
if (aTrack == TrackInfo::kAudioTrack) {
|
||||
AudioData* audioData = static_cast<AudioData*>(aData);
|
||||
if (aData->mType != MediaData::RAW_DATA) {
|
||||
AudioData* audioData = static_cast<AudioData*>(aData);
|
||||
|
||||
if (audioData->mChannels != mInfo.mAudio.mChannels ||
|
||||
audioData->mRate != mInfo.mAudio.mRate) {
|
||||
LOG("change of audio format (rate:%d->%d). "
|
||||
"This is an unsupported configuration",
|
||||
mInfo.mAudio.mRate, audioData->mRate);
|
||||
mInfo.mAudio.mRate = audioData->mRate;
|
||||
mInfo.mAudio.mChannels = audioData->mChannels;
|
||||
if (audioData->mChannels != mInfo.mAudio.mChannels ||
|
||||
audioData->mRate != mInfo.mAudio.mRate) {
|
||||
LOG("change of audio format (rate:%d->%d). "
|
||||
"This is an unsupported configuration",
|
||||
mInfo.mAudio.mRate, audioData->mRate);
|
||||
mInfo.mAudio.mRate = audioData->mRate;
|
||||
mInfo.mAudio.mChannels = audioData->mChannels;
|
||||
}
|
||||
}
|
||||
mAudio.mPromise.Resolve(audioData, __func__);
|
||||
mAudio.mPromise.Resolve(aData, __func__);
|
||||
} else if (aTrack == TrackInfo::kVideoTrack) {
|
||||
mVideo.mPromise.Resolve(static_cast<VideoData*>(aData), __func__);
|
||||
mVideo.mPromise.Resolve(aData, __func__);
|
||||
}
|
||||
LOG("Resolved data promise for %s", TrackTypeToStr(aTrack));
|
||||
}
|
||||
|
|
|
@ -89,6 +89,20 @@ public:
|
|||
bool IsWaitForDataSupported() override { return true; }
|
||||
RefPtr<WaitForDataPromise> WaitForData(MediaData::Type aType) override;
|
||||
|
||||
// MediaFormatReader supports demuxed-only mode.
|
||||
bool IsDemuxOnlySupported() const override { return true; }
|
||||
|
||||
void SetDemuxOnly(bool aDemuxedOnly) override
|
||||
{
|
||||
if (OnTaskQueue()) {
|
||||
mDemuxOnly = aDemuxedOnly;
|
||||
return;
|
||||
}
|
||||
nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethodWithArg<bool>(
|
||||
this, &MediaDecoderReader::SetDemuxOnly, aDemuxedOnly);
|
||||
OwnerThread()->Dispatch(r.forget());
|
||||
}
|
||||
|
||||
bool UseBufferingHeuristics() override
|
||||
{
|
||||
return mTrackDemuxersMayBlock;
|
||||
|
@ -123,9 +137,12 @@ private:
|
|||
bool UpdateReceivedNewData(TrackType aTrack);
|
||||
// Called when new samples need to be demuxed.
|
||||
void RequestDemuxSamples(TrackType aTrack);
|
||||
// Decode any pending already demuxed samples.
|
||||
void DecodeDemuxedSamples(TrackType aTrack,
|
||||
// Handle demuxed samples by the input behavior.
|
||||
void HandleDemuxedSamples(TrackType aTrack,
|
||||
AbstractMediaDecoder::AutoNotifyDecoded& aA);
|
||||
// Decode any pending already demuxed samples.
|
||||
bool DecodeDemuxedSamples(TrackType aTrack,
|
||||
MediaRawData* aSample);
|
||||
// Drain the current decoder.
|
||||
void DrainDecoder(TrackType aTrack);
|
||||
void NotifyNewOutput(TrackType aTrack, MediaData* aSample);
|
||||
|
@ -407,6 +424,9 @@ private:
|
|||
|
||||
bool mHardwareAccelerationDisabled;
|
||||
|
||||
// Set the demuxed-only flag.
|
||||
Atomic<bool> mDemuxOnly;
|
||||
|
||||
// Seeking objects.
|
||||
bool IsSeeking() const { return mPendingSeekTime.isSome(); }
|
||||
void AttemptSeek();
|
||||
|
|
|
@ -451,7 +451,7 @@ RTCPeerConnection.prototype = {
|
|||
"RTCPeerConnection does not currently support multiple certificates",
|
||||
"NotSupportedError");
|
||||
}
|
||||
let cert = certificates.find(c => c.expires.getTime() > Date.now());
|
||||
let cert = certificates.find(c => c.expires > Date.now());
|
||||
if (!cert) {
|
||||
throw new this._win.DOMException(
|
||||
"Unable to create RTCPeerConnection with an expired certificate",
|
||||
|
|
|
@ -29,6 +29,15 @@ public:
|
|||
virtual void PlaybackEnded() override {}
|
||||
virtual void SeekStarted() override {}
|
||||
virtual void SeekCompleted() override {}
|
||||
virtual void DownloadProgressed() override {}
|
||||
virtual void UpdateReadyState() override {}
|
||||
virtual void FirstFrameLoaded() override {}
|
||||
#ifdef MOZ_EME
|
||||
virtual void DispatchEncrypted(const nsTArray<uint8_t>& aInitData,
|
||||
const nsAString& aInitDataType) override {}
|
||||
#endif // MOZ_EME
|
||||
virtual bool IsActive() override { return true; }
|
||||
virtual bool IsHidden() override { return false; }
|
||||
virtual void DownloadSuspended() override {}
|
||||
virtual void DownloadResumed(bool aForceNetworkLoading) override {}
|
||||
virtual void NotifySuspendedByCache(bool aIsSuspended) override {}
|
||||
|
|
|
@ -11,7 +11,8 @@ namespace mozilla
|
|||
{
|
||||
|
||||
MockMediaResource::MockMediaResource(const char* aFileName)
|
||||
: mFileName(aFileName)
|
||||
: mFileHandle(nullptr)
|
||||
, mFileName(aFileName)
|
||||
, mContentType(NS_LITERAL_CSTRING("video/mp4"))
|
||||
{
|
||||
}
|
||||
|
|
|
@ -0,0 +1,245 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "mozilla/dom/HTMLMediaElement.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/TaskQueue.h"
|
||||
#include "ImageContainer.h"
|
||||
#include "Layers.h"
|
||||
#include "MediaData.h"
|
||||
#include "MediaFormatReader.h"
|
||||
#include "MP4Decoder.h"
|
||||
#include "MockMediaDecoderOwner.h"
|
||||
#include "MockMediaResource.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
|
||||
class MockMP4Decoder : public MP4Decoder
|
||||
{
|
||||
public:
|
||||
MockMP4Decoder()
|
||||
: MP4Decoder(new MockMediaDecoderOwner())
|
||||
{}
|
||||
|
||||
// Avoid the assertion.
|
||||
AbstractCanonical<media::NullableTimeUnit>* CanonicalDurationOrNull() override
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
class MediaFormatReaderBinding
|
||||
{
|
||||
public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaFormatReaderBinding);
|
||||
RefPtr<MockMP4Decoder> mDecoder;
|
||||
RefPtr<MockMediaResource> mResource;
|
||||
RefPtr<MediaDecoderReader> mReader;
|
||||
RefPtr<TaskQueue> mTaskQueue;
|
||||
explicit MediaFormatReaderBinding(const char* aFileName = "gizmo.mp4")
|
||||
: mDecoder(new MockMP4Decoder())
|
||||
, mResource(new MockMediaResource(aFileName))
|
||||
, mReader(new MediaFormatReader(mDecoder,
|
||||
new MP4Demuxer(mResource),
|
||||
new VideoFrameContainer(
|
||||
(HTMLMediaElement*)(0x1),
|
||||
layers::LayerManager::CreateImageContainer(
|
||||
layers::ImageContainer::ASYNCHRONOUS))
|
||||
))
|
||||
, mTaskQueue(new TaskQueue(GetMediaThreadPool(MediaThreadType::PLAYBACK)))
|
||||
{
|
||||
}
|
||||
|
||||
bool Init() {
|
||||
// Init Resource.
|
||||
nsresult rv = mResource->Open(nullptr);
|
||||
if (NS_FAILED(rv)) {
|
||||
return false;
|
||||
}
|
||||
mDecoder->SetResource(mResource);
|
||||
// Init Reader.
|
||||
rv = mReader->Init();
|
||||
if (NS_FAILED(rv)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void OnMetadataReadAudio(MetadataHolder* aMetadata)
|
||||
{
|
||||
EXPECT_TRUE(aMetadata);
|
||||
mReader->RequestAudioData()
|
||||
->Then(mReader->OwnerThread(), __func__, this,
|
||||
&MediaFormatReaderBinding::OnAudioRawDataDemuxed,
|
||||
&MediaFormatReaderBinding::OnNotDemuxed);
|
||||
}
|
||||
|
||||
void OnMetadataReadVideo(MetadataHolder* aMetadata)
|
||||
{
|
||||
EXPECT_TRUE(aMetadata);
|
||||
mReader->RequestVideoData(true, 0)
|
||||
->Then(mReader->OwnerThread(), __func__, this,
|
||||
&MediaFormatReaderBinding::OnVideoRawDataDemuxed,
|
||||
&MediaFormatReaderBinding::OnNotDemuxed);
|
||||
}
|
||||
|
||||
void OnMetadataNotRead(ReadMetadataFailureReason aReason) {
|
||||
EXPECT_TRUE(false);
|
||||
ReaderShutdown();
|
||||
}
|
||||
|
||||
void OnAudioRawDataDemuxed(MediaData* aAudioSample)
|
||||
{
|
||||
EXPECT_TRUE(aAudioSample);
|
||||
EXPECT_EQ(MediaData::RAW_DATA, aAudioSample->mType);
|
||||
ReaderShutdown();
|
||||
}
|
||||
|
||||
void OnVideoRawDataDemuxed(MediaData* aVideoSample)
|
||||
{
|
||||
EXPECT_TRUE(aVideoSample);
|
||||
EXPECT_EQ(MediaData::RAW_DATA, aVideoSample->mType);
|
||||
ReaderShutdown();
|
||||
}
|
||||
|
||||
void OnNotDemuxed(MediaDecoderReader::NotDecodedReason aReason)
|
||||
{
|
||||
EXPECT_TRUE(false);
|
||||
ReaderShutdown();
|
||||
}
|
||||
|
||||
void ReaderShutdown()
|
||||
{
|
||||
RefPtr<MediaFormatReaderBinding> self = this;
|
||||
mReader->Shutdown()
|
||||
->Then(mTaskQueue, __func__,
|
||||
[self]() {
|
||||
self->mTaskQueue->BeginShutdown();
|
||||
},
|
||||
[self]() {
|
||||
EXPECT_TRUE(false);
|
||||
self->mTaskQueue->BeginShutdown();
|
||||
}); //Then
|
||||
}
|
||||
template<class Function>
|
||||
void RunTestAndWait(Function&& aFunction)
|
||||
{
|
||||
RefPtr<nsRunnable> r = NS_NewRunnableFunction(Forward<Function>(aFunction));
|
||||
mTaskQueue->Dispatch(r.forget());
|
||||
mTaskQueue->AwaitShutdownAndIdle();
|
||||
}
|
||||
private:
|
||||
~MediaFormatReaderBinding()
|
||||
{}
|
||||
};
|
||||
|
||||
|
||||
template <typename T>
|
||||
T GetPref(const char* aPrefKey);
|
||||
|
||||
template <>
|
||||
bool GetPref<bool>(const char* aPrefKey)
|
||||
{
|
||||
return Preferences::GetBool(aPrefKey);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void SetPref(const char* a, T value);
|
||||
|
||||
template <>
|
||||
void SetPref<bool>(const char* aPrefKey, bool aValue)
|
||||
{
|
||||
unused << Preferences::SetBool(aPrefKey, aValue);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
class PreferencesRAII
|
||||
{
|
||||
public:
|
||||
explicit PreferencesRAII(const char* aPrefKey, T aValue)
|
||||
: mPrefKey(aPrefKey)
|
||||
{
|
||||
mDefaultPref = GetPref<T>(aPrefKey);
|
||||
SetPref(aPrefKey, aValue);
|
||||
}
|
||||
~PreferencesRAII()
|
||||
{
|
||||
SetPref(mPrefKey, mDefaultPref);
|
||||
}
|
||||
private:
|
||||
T mDefaultPref;
|
||||
const char* mPrefKey;
|
||||
};
|
||||
|
||||
TEST(MediaFormatReader, RequestAudioRawData)
|
||||
{
|
||||
PreferencesRAII<bool> pref =
|
||||
PreferencesRAII<bool>("media.use-blank-decoder",
|
||||
true);
|
||||
RefPtr<MediaFormatReaderBinding> b = new MediaFormatReaderBinding();
|
||||
if (!b->Init())
|
||||
{
|
||||
EXPECT_TRUE(false);
|
||||
// Stop the test since initialization failed.
|
||||
return;
|
||||
}
|
||||
if (!b->mReader->IsDemuxOnlySupported())
|
||||
{
|
||||
EXPECT_TRUE(false);
|
||||
// Stop the test since the reader cannot support demuxed-only demand.
|
||||
return;
|
||||
}
|
||||
// Switch to demuxed-only mode.
|
||||
b->mReader->SetDemuxOnly(true);
|
||||
// To ensure the MediaDecoderReader::InitializationTask and
|
||||
// MediaDecoderReader::SetDemuxOnly can be done.
|
||||
NS_ProcessNextEvent();
|
||||
auto testCase = [b]() {
|
||||
InvokeAsync(b->mReader->OwnerThread(),
|
||||
b->mReader.get(),
|
||||
__func__,
|
||||
&MediaDecoderReader::AsyncReadMetadata)
|
||||
->Then(b->mReader->OwnerThread(), __func__, b.get(),
|
||||
&MediaFormatReaderBinding::OnMetadataReadAudio,
|
||||
&MediaFormatReaderBinding::OnMetadataNotRead);
|
||||
};
|
||||
b->RunTestAndWait(testCase);
|
||||
}
|
||||
TEST(MediaFormatReader, RequestVideoRawData)
|
||||
{
|
||||
PreferencesRAII<bool> pref =
|
||||
PreferencesRAII<bool>("media.use-blank-decoder",
|
||||
true);
|
||||
RefPtr<MediaFormatReaderBinding> b = new MediaFormatReaderBinding();
|
||||
if (!b->Init())
|
||||
{
|
||||
EXPECT_TRUE(false);
|
||||
// Stop the test since initialization failed.
|
||||
return;
|
||||
}
|
||||
if (!b->mReader->IsDemuxOnlySupported())
|
||||
{
|
||||
EXPECT_TRUE(false);
|
||||
// Stop the test since the reader cannot support demuxed-only demand.
|
||||
return;
|
||||
}
|
||||
// Switch to demuxed-only mode.
|
||||
b->mReader->SetDemuxOnly(true);
|
||||
// To ensure the MediaDecoderReader::InitializationTask and
|
||||
// MediaDecoderReader::SetDemuxOnly can be done.
|
||||
NS_ProcessNextEvent();
|
||||
auto testCase = [b]() {
|
||||
InvokeAsync(b->mReader->OwnerThread(),
|
||||
b->mReader.get(),
|
||||
__func__,
|
||||
&MediaDecoderReader::AsyncReadMetadata)
|
||||
->Then(b->mReader->OwnerThread(), __func__, b.get(),
|
||||
&MediaFormatReaderBinding::OnMetadataReadVideo,
|
||||
&MediaFormatReaderBinding::OnMetadataNotRead);
|
||||
};
|
||||
b->RunTestAndWait(testCase);
|
||||
}
|
|
@ -12,6 +12,7 @@ UNIFIED_SOURCES += [
|
|||
'TestGMPUtils.cpp',
|
||||
'TestIntervalSet.cpp',
|
||||
'TestMediaEventSource.cpp',
|
||||
'TestMediaFormatReader.cpp',
|
||||
'TestMozPromise.cpp',
|
||||
'TestMP3Demuxer.cpp',
|
||||
'TestMP4Demuxer.cpp',
|
||||
|
|
|
@ -107,15 +107,15 @@
|
|||
expires: 1 // smallest possible expiration window
|
||||
}))
|
||||
.then(cert => {
|
||||
ok(cert.expires instanceof Date, 'cert has expiration time');
|
||||
info('Expires at ' + cert.expires);
|
||||
ok(!isNaN(cert.expires), 'cert has expiration time');
|
||||
info('Expires at ' + new Date(cert.expires));
|
||||
expiredCert = cert;
|
||||
})
|
||||
|
||||
.then(() => checkBadParameters())
|
||||
|
||||
.then(() => {
|
||||
var delay = expiredCert.expires.getTime() - Date.now();
|
||||
var delay = expiredCert.expires - Date.now();
|
||||
// Hopefully this delay is never needed.
|
||||
if (delay > 0) {
|
||||
return new Promise(r => setTimeout(r, delay));
|
||||
|
|
|
@ -18,10 +18,8 @@
|
|||
#include "mozilla/ErrorResult.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "mozilla/dom/Date.h"
|
||||
#include "mozilla/dom/CryptoKey.h"
|
||||
#include "mtransport/dtlsidentity.h"
|
||||
#include "js/Date.h"
|
||||
#include "js/StructuredClone.h"
|
||||
#include "js/TypeDecls.h"
|
||||
|
||||
|
@ -55,9 +53,9 @@ public:
|
|||
|
||||
// WebIDL expires attribute. Note: JS dates are milliseconds since epoch;
|
||||
// NSPR PRTime is in microseconds since the same epoch.
|
||||
JS::ClippedTime Expires() const
|
||||
uint64_t Expires() const
|
||||
{
|
||||
return JS::TimeClip(mExpires / PR_USEC_PER_MSEC);
|
||||
return mExpires / PR_USEC_PER_MSEC;
|
||||
}
|
||||
|
||||
// Accessors for use by PeerConnectionImpl.
|
||||
|
|
|
@ -616,11 +616,6 @@ MulticastDNSDeviceProvider::OnServiceFound(nsIDNSServiceInfo* aServiceInfo)
|
|||
|
||||
LOG_I("OnServiceFound: %s", serviceName.get());
|
||||
|
||||
if (mRegisteredName == serviceName) {
|
||||
LOG_I("ignore self");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (mMulticastDNS) {
|
||||
if (NS_WARN_IF(NS_FAILED(rv = mMulticastDNS->ResolveService(
|
||||
aServiceInfo, mWrappedListener)))) {
|
||||
|
@ -706,8 +701,11 @@ MulticastDNSDeviceProvider::OnServiceRegistered(nsIDNSServiceInfo* aServiceInfo)
|
|||
LOG_I("OnServiceRegistered (%s)", name.get());
|
||||
mRegisteredName = name;
|
||||
|
||||
if (NS_WARN_IF(NS_FAILED(rv = mPresentationServer->SetId(name)))) {
|
||||
return rv;
|
||||
if (mMulticastDNS) {
|
||||
if (NS_WARN_IF(NS_FAILED(rv = mMulticastDNS->ResolveService(
|
||||
aServiceInfo, mWrappedListener)))) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
|
@ -776,6 +774,16 @@ MulticastDNSDeviceProvider::OnServiceResolved(nsIDNSServiceInfo* aServiceInfo)
|
|||
return rv;
|
||||
}
|
||||
|
||||
if (mRegisteredName == serviceName) {
|
||||
LOG_I("ignore self");
|
||||
|
||||
if (NS_WARN_IF(NS_FAILED(rv = mPresentationServer->SetId(host)))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsAutoCString address;
|
||||
if (NS_WARN_IF(NS_FAILED(rv = aServiceInfo->GetAddress(address)))) {
|
||||
return rv;
|
||||
|
|
|
@ -634,6 +634,74 @@ function noAddDevice() {
|
|||
run_next_test();
|
||||
}
|
||||
|
||||
function ignoreSelfDevice() {
|
||||
Services.prefs.setBoolPref(PREF_DISCOVERY, false);
|
||||
Services.prefs.setBoolPref(PREF_DISCOVERABLE, true);
|
||||
|
||||
let mockDevice = createDevice("device.local",
|
||||
12345,
|
||||
"service.name",
|
||||
"_mozilla_papi._tcp");
|
||||
let mockSDObj = {
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIDNSServiceDiscovery]),
|
||||
startDiscovery: function(serviceType, listener) {
|
||||
listener.onDiscoveryStarted(serviceType);
|
||||
listener.onServiceFound(createDevice("",
|
||||
0,
|
||||
mockDevice.serviceName,
|
||||
mockDevice.serviceType));
|
||||
return {
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsICancelable]),
|
||||
cancel: function() {}
|
||||
};
|
||||
},
|
||||
registerService: function(serviceInfo, listener) {
|
||||
listener.onServiceRegistered(createDevice("",
|
||||
0,
|
||||
mockDevice.serviceName,
|
||||
mockDevice.serviceType));
|
||||
return {
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsICancelable]),
|
||||
cancel: function() {}
|
||||
};
|
||||
},
|
||||
resolveService: function(serviceInfo, listener) {
|
||||
Assert.equal(serviceInfo.serviceName, mockDevice.serviceName);
|
||||
Assert.equal(serviceInfo.serviceType, mockDevice.serviceType);
|
||||
listener.onServiceResolved(createDevice(mockDevice.host,
|
||||
mockDevice.port,
|
||||
mockDevice.serviceName,
|
||||
mockDevice.serviceType));
|
||||
}
|
||||
};
|
||||
|
||||
let mockServerObj = {
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsITCPPresentationServer]),
|
||||
init: function() {},
|
||||
sessionRequest: function() {},
|
||||
close: function() {},
|
||||
id: '',
|
||||
port: 0,
|
||||
listener: null,
|
||||
};
|
||||
|
||||
let contractHookSD = new ContractHook(SD_CONTRACT_ID, mockSDObj);
|
||||
let contractHookServer = new ContractHook(SERVER_CONTRACT_ID, mockServerObj);
|
||||
let provider = Cc[PROVIDER_CONTRACT_ID].createInstance(Ci.nsIPresentationDeviceProvider);
|
||||
let listener = new TestPresentationDeviceListener();
|
||||
|
||||
// Register service
|
||||
provider.listener = listener;
|
||||
Assert.equal(mockServerObj.id, mockDevice.host);
|
||||
|
||||
// Start discovery
|
||||
Services.prefs.setBoolPref(PREF_DISCOVERY, true);
|
||||
Assert.equal(listener.count(), 0);
|
||||
|
||||
provider.listener = null;
|
||||
|
||||
run_next_test();
|
||||
}
|
||||
function addDeviceDynamically() {
|
||||
Services.prefs.setBoolPref(PREF_DISCOVERY, false);
|
||||
|
||||
|
@ -954,6 +1022,7 @@ function run_test() {
|
|||
add_test(handleOnSessionRequest);
|
||||
add_test(handleOnSessionRequestFromUnknownDevice);
|
||||
add_test(noAddDevice);
|
||||
add_test(ignoreSelfDevice);
|
||||
add_test(addDeviceDynamically);
|
||||
add_test(updateDevice);
|
||||
add_test(diffDiscovery);
|
||||
|
|
|
@ -8,5 +8,5 @@
|
|||
|
||||
[Pref="media.peerconnection.enabled"]
|
||||
interface RTCCertificate {
|
||||
readonly attribute Date expires;
|
||||
readonly attribute DOMTimeStamp expires;
|
||||
};
|
||||
|
|
|
@ -916,6 +916,23 @@ XULDocument::AttributeWillChange(nsIDocument* aDocument,
|
|||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
ShouldPersistAttribute(Element* aElement, nsIAtom* aAttribute)
|
||||
{
|
||||
if (aElement->IsXULElement(nsGkAtoms::window)) {
|
||||
// The following attributes of xul:window should be handled in
|
||||
// nsXULWindow::SavePersistentAttributes instead of here.
|
||||
if (aAttribute == nsGkAtoms::screenX ||
|
||||
aAttribute == nsGkAtoms::screenY ||
|
||||
aAttribute == nsGkAtoms::width ||
|
||||
aAttribute == nsGkAtoms::height ||
|
||||
aAttribute == nsGkAtoms::sizemode) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
XULDocument::AttributeChanged(nsIDocument* aDocument,
|
||||
Element* aElement, int32_t aNameSpaceID,
|
||||
|
@ -995,14 +1012,14 @@ XULDocument::AttributeChanged(nsIDocument* aDocument,
|
|||
// XXX Namespace handling broken :-(
|
||||
nsAutoString persist;
|
||||
aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::persist, persist);
|
||||
if (!persist.IsEmpty()) {
|
||||
// Persistence of attributes of xul:window is handled in nsXULWindow.
|
||||
if (ShouldPersistAttribute(aElement, aAttribute) && !persist.IsEmpty() &&
|
||||
// XXXldb This should check that it's a token, not just a substring.
|
||||
if (persist.Find(nsDependentAtomString(aAttribute)) >= 0) {
|
||||
nsContentUtils::AddScriptRunner(NS_NewRunnableMethodWithArgs
|
||||
<nsIContent*, int32_t, nsIAtom*>
|
||||
(this, &XULDocument::DoPersist, aElement, kNameSpaceID_None,
|
||||
aAttribute));
|
||||
}
|
||||
persist.Find(nsDependentAtomString(aAttribute)) >= 0) {
|
||||
nsContentUtils::AddScriptRunner(NS_NewRunnableMethodWithArgs
|
||||
<nsIContent*, int32_t, nsIAtom*>
|
||||
(this, &XULDocument::DoPersist, aElement, kNameSpaceID_None,
|
||||
aAttribute));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -273,53 +273,32 @@ NS_IMETHODIMP mozHunspell::SetPersonalDictionary(mozIPersonalDictionary * aPerso
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
struct AppendNewStruct
|
||||
{
|
||||
char16_t **dics;
|
||||
uint32_t count;
|
||||
bool failed;
|
||||
};
|
||||
|
||||
static PLDHashOperator
|
||||
AppendNewString(const nsAString& aString, nsIFile* aFile, void* aClosure)
|
||||
{
|
||||
AppendNewStruct *ans = (AppendNewStruct*) aClosure;
|
||||
ans->dics[ans->count] = ToNewUnicode(aString);
|
||||
if (!ans->dics[ans->count]) {
|
||||
ans->failed = true;
|
||||
return PL_DHASH_STOP;
|
||||
}
|
||||
|
||||
++ans->count;
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP mozHunspell::GetDictionaryList(char16_t ***aDictionaries,
|
||||
uint32_t *aCount)
|
||||
{
|
||||
if (!aDictionaries || !aCount)
|
||||
return NS_ERROR_NULL_POINTER;
|
||||
|
||||
AppendNewStruct ans = {
|
||||
(char16_t**) moz_xmalloc(sizeof(char16_t*) * mDictionaries.Count()),
|
||||
0,
|
||||
false
|
||||
};
|
||||
uint32_t count = 0;
|
||||
char16_t** dicts =
|
||||
(char16_t**) moz_xmalloc(sizeof(char16_t*) * mDictionaries.Count());
|
||||
|
||||
// This pointer is used during enumeration
|
||||
mDictionaries.EnumerateRead(AppendNewString, &ans);
|
||||
|
||||
if (ans.failed) {
|
||||
while (ans.count) {
|
||||
--ans.count;
|
||||
free(ans.dics[ans.count]);
|
||||
for (auto iter = mDictionaries.Iter(); !iter.Done(); iter.Next()) {
|
||||
dicts[count] = ToNewUnicode(iter.Key());
|
||||
if (!dicts[count]) {
|
||||
while (count) {
|
||||
--count;
|
||||
free(dicts[count]);
|
||||
}
|
||||
free(dicts);
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
free(ans.dics);
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
++count;
|
||||
}
|
||||
|
||||
*aDictionaries = ans.dics;
|
||||
*aCount = ans.count;
|
||||
*aDictionaries = dicts;
|
||||
*aCount = count;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
|
@ -212,6 +212,8 @@ cairo-mask-extends-bug.patch: bug 918671, sometimes when building a mask we woul
|
|||
|
||||
ft-no-subpixel-if-surface-disables.patch: bug 929451, don't use subpixel aa for ft fonts on surfaces that don't support it
|
||||
|
||||
win32-printing-axis-swap.patch: bug 1205854, workaround for Windows printer drivers that can't handle swapped X and Y axes
|
||||
|
||||
==== pixman patches ====
|
||||
|
||||
pixman-android-cpu-detect.patch: Add CPU detection support for Android, where we can't reliably access /proc/self/auxv.
|
||||
|
|
|
@ -878,13 +878,15 @@ _cairo_matrix_is_pixel_exact (const cairo_matrix_t *matrix)
|
|||
see doc/tutorial/src/singular.c.
|
||||
*/
|
||||
|
||||
/* determine the length of the major axis of a circle of the given radius
|
||||
after applying the transformation matrix. */
|
||||
double
|
||||
_cairo_matrix_transformed_circle_major_axis (const cairo_matrix_t *matrix,
|
||||
double radius)
|
||||
/* determine the length of the major and minor axes of a circle of the given
|
||||
radius after applying the transformation matrix. */
|
||||
void
|
||||
_cairo_matrix_transformed_circle_axes (const cairo_matrix_t *matrix,
|
||||
double radius,
|
||||
double *major,
|
||||
double *minor)
|
||||
{
|
||||
double a, b, c, d, f, g, h, i, j;
|
||||
double a, b, c, d, f, g, h, i, j, k;
|
||||
|
||||
_cairo_matrix_get_affine (matrix,
|
||||
&a, &b,
|
||||
|
@ -893,17 +895,29 @@ _cairo_matrix_transformed_circle_major_axis (const cairo_matrix_t *matrix,
|
|||
|
||||
i = a*a + b*b;
|
||||
j = c*c + d*d;
|
||||
k = a*c + b*d;
|
||||
|
||||
f = 0.5 * (i + j);
|
||||
g = 0.5 * (i - j);
|
||||
h = a*c + b*d;
|
||||
h = hypot (g, k);
|
||||
|
||||
return radius * sqrt (f + hypot (g, h));
|
||||
if (major)
|
||||
*major = radius * sqrt (f + h);
|
||||
if (minor)
|
||||
*minor = radius * sqrt (f - h);
|
||||
}
|
||||
|
||||
/*
|
||||
* we don't need the minor axis length, which is
|
||||
* double min = radius * sqrt (f - sqrt (g*g+h*h));
|
||||
*/
|
||||
/* determine the length of the major axis of a circle of the given radius
|
||||
after applying the transformation matrix. */
|
||||
double
|
||||
_cairo_matrix_transformed_circle_major_axis (const cairo_matrix_t *matrix,
|
||||
double radius)
|
||||
{
|
||||
double major;
|
||||
|
||||
_cairo_matrix_transformed_circle_axes (matrix, radius, &major, NULL);
|
||||
|
||||
return major;
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -615,6 +615,7 @@ _cairo_win32_printing_surface_paint_image_pattern (cairo_win32_surface_t *surf
|
|||
cairo_image_info_t mime_info;
|
||||
cairo_bool_t use_mime;
|
||||
DWORD mime_type;
|
||||
cairo_bool_t axis_swap;
|
||||
|
||||
/* If we can't use StretchDIBits with this surface, we can't do anything
|
||||
* here.
|
||||
|
@ -663,29 +664,55 @@ _cairo_win32_printing_surface_paint_image_pattern (cairo_win32_surface_t *surf
|
|||
|
||||
use_mime = (status == CAIRO_STATUS_SUCCESS);
|
||||
|
||||
if (!use_mime && image->format != CAIRO_FORMAT_RGB24) {
|
||||
m = pattern->base.matrix;
|
||||
status = cairo_matrix_invert (&m);
|
||||
/* _cairo_pattern_set_matrix guarantees invertibility */
|
||||
assert (status == CAIRO_STATUS_SUCCESS);
|
||||
cairo_matrix_multiply (&m, &m, &surface->ctm);
|
||||
cairo_matrix_multiply (&m, &m, &surface->gdi_ctm);
|
||||
/* Check if the matrix swaps the X and Y axes by checking if the diagonal
|
||||
* is effectively zero. This can happen, for example, if it was composed
|
||||
* with a rotation such as a landscape transform. Some printing devices
|
||||
* don't support such transforms in StretchDIBits.
|
||||
*/
|
||||
axis_swap = fabs (m.xx*image->width) < 1 && fabs (m.yy*image->height) < 1;
|
||||
|
||||
if (!use_mime && (image->format != CAIRO_FORMAT_RGB24 || axis_swap)) {
|
||||
cairo_surface_t *opaque_surface;
|
||||
cairo_surface_pattern_t image_pattern;
|
||||
cairo_solid_pattern_t background_pattern;
|
||||
int width = image->width, height = image->height;
|
||||
|
||||
if (axis_swap) {
|
||||
width = image->height;
|
||||
height = image->width;
|
||||
}
|
||||
opaque_surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24,
|
||||
image->width,
|
||||
image->height);
|
||||
width,
|
||||
height);
|
||||
if (opaque_surface->status) {
|
||||
status = opaque_surface->status;
|
||||
goto CLEANUP_OPAQUE_IMAGE;
|
||||
}
|
||||
|
||||
_cairo_pattern_init_solid (&background_pattern,
|
||||
background_color);
|
||||
status = _cairo_surface_paint (opaque_surface,
|
||||
CAIRO_OPERATOR_SOURCE,
|
||||
&background_pattern.base,
|
||||
NULL);
|
||||
if (status)
|
||||
goto CLEANUP_OPAQUE_IMAGE;
|
||||
if (image->format != CAIRO_FORMAT_RGB24) {
|
||||
_cairo_pattern_init_solid (&background_pattern,
|
||||
background_color);
|
||||
status = _cairo_surface_paint (opaque_surface,
|
||||
CAIRO_OPERATOR_SOURCE,
|
||||
&background_pattern.base,
|
||||
NULL);
|
||||
if (status)
|
||||
goto CLEANUP_OPAQUE_IMAGE;
|
||||
}
|
||||
|
||||
_cairo_pattern_init_for_surface (&image_pattern, &image->base);
|
||||
if (axis_swap) {
|
||||
/* swap the X and Y axes to undo the axis swap in the matrix */
|
||||
cairo_matrix_t swap_xy = { 0, 1, 1, 0, 0, 0 };
|
||||
cairo_pattern_set_matrix (&image_pattern.base, &swap_xy);
|
||||
cairo_matrix_multiply (&m, &swap_xy, &m);
|
||||
}
|
||||
status = _cairo_surface_paint (opaque_surface,
|
||||
CAIRO_OPERATOR_OVER,
|
||||
&image_pattern.base,
|
||||
|
@ -711,13 +738,6 @@ _cairo_win32_printing_surface_paint_image_pattern (cairo_win32_surface_t *surf
|
|||
bi.bmiHeader.biClrUsed = 0;
|
||||
bi.bmiHeader.biClrImportant = 0;
|
||||
|
||||
m = pattern->base.matrix;
|
||||
status = cairo_matrix_invert (&m);
|
||||
/* _cairo_pattern_set_matrix guarantees invertibility */
|
||||
assert (status == CAIRO_STATUS_SUCCESS);
|
||||
|
||||
cairo_matrix_multiply (&m, &m, &surface->gdi_ctm);
|
||||
cairo_matrix_multiply(&m, &m, &surface->ctm);
|
||||
SaveDC (surface->dc);
|
||||
_cairo_matrix_to_win32_xform (&m, &xform);
|
||||
|
||||
|
@ -1265,6 +1285,7 @@ _cairo_win32_printing_surface_stroke (void *abstract_surface,
|
|||
cairo_matrix_t mat;
|
||||
double scale;
|
||||
double scaled_width;
|
||||
double major, minor;
|
||||
|
||||
status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
|
||||
if (status)
|
||||
|
@ -1355,12 +1376,30 @@ _cairo_win32_printing_surface_stroke (void *abstract_surface,
|
|||
*/
|
||||
SaveDC (surface->dc);
|
||||
|
||||
_cairo_matrix_to_win32_xform (&mat, &xform);
|
||||
xform.eDx = 0.0f;
|
||||
xform.eDy = 0.0f;
|
||||
/* Some printers don't handle transformed strokes. Avoid the transform
|
||||
* if not required for the pen shape. Use the SVD here to find the major
|
||||
* and minor scales then check if they differ by more than 1 device unit.
|
||||
* If the difference is smaller, then just treat the scaling as uniform.
|
||||
* This check ignores rotations as the pen shape is symmetric before
|
||||
* transformation.
|
||||
*/
|
||||
_cairo_matrix_transformed_circle_axes (&mat, scale, &major, &minor);
|
||||
if (fabs (major - minor) > 1) {
|
||||
/* Check if the matrix swaps the X and Y axes such that the diagonal
|
||||
* is nearly zero. This was observed to cause problems with XPS export.
|
||||
*/
|
||||
if (fabs (mat.xx) < 1e-6 && fabs (mat.yy) < 1e-6) {
|
||||
/* swap the X and Y axes to undo the axis swap in the matrix */
|
||||
cairo_matrix_t swap_xy = { 0, 1, 1, 0, 0, 0 };
|
||||
cairo_matrix_multiply (&mat, &swap_xy, &mat);
|
||||
}
|
||||
_cairo_matrix_to_win32_xform (&mat, &xform);
|
||||
xform.eDx = 0.0f;
|
||||
xform.eDy = 0.0f;
|
||||
|
||||
if (!ModifyWorldTransform (surface->dc, &xform, MWT_LEFTMULTIPLY))
|
||||
return _cairo_win32_print_gdi_error ("_win32_surface_stroke:SetWorldTransform");
|
||||
if (!ModifyWorldTransform (surface->dc, &xform, MWT_LEFTMULTIPLY))
|
||||
return _cairo_win32_print_gdi_error ("_win32_surface_stroke:SetWorldTransform");
|
||||
}
|
||||
|
||||
if (source->type == CAIRO_PATTERN_TYPE_SOLID) {
|
||||
StrokePath (surface->dc);
|
||||
|
|
|
@ -2120,6 +2120,12 @@ _cairo_matrix_has_unity_scale (const cairo_matrix_t *matrix);
|
|||
cairo_private cairo_bool_t
|
||||
_cairo_matrix_is_pixel_exact (const cairo_matrix_t *matrix) cairo_pure;
|
||||
|
||||
cairo_private void
|
||||
_cairo_matrix_transformed_circle_axes (const cairo_matrix_t *matrix,
|
||||
double radius,
|
||||
double *major,
|
||||
double *minor);
|
||||
|
||||
cairo_private double
|
||||
_cairo_matrix_transformed_circle_major_axis (const cairo_matrix_t *matrix,
|
||||
double radius) cairo_pure;
|
||||
|
|
|
@ -0,0 +1,292 @@
|
|||
# HG changeset patch
|
||||
# User Lee Salzman <lsalzman@mozilla.com>
|
||||
# Date 1445463645 14400
|
||||
# Wed Oct 21 17:40:45 2015 -0400
|
||||
# Node ID 9e84563cbd73c5b0993dfd018ca25b660b667e94
|
||||
# Parent 2d3fd51c4182c253a2f102655e8e9e466032853f
|
||||
workaround for Windows printer drivers that can't handle swapped X and Y axes
|
||||
|
||||
diff --git a/gfx/cairo/cairo/src/cairo-matrix.c b/gfx/cairo/cairo/src/cairo-matrix.c
|
||||
--- a/gfx/cairo/cairo/src/cairo-matrix.c
|
||||
+++ b/gfx/cairo/cairo/src/cairo-matrix.c
|
||||
@@ -873,42 +873,56 @@ cairo_bool_t
|
||||
(Note that the minor axis length is at the minimum of the above solution,
|
||||
which is just sqrt ( f - sqrt(g² + h²) ) given the symmetry of (D)).
|
||||
|
||||
|
||||
For another derivation of the same result, using Singular Value Decomposition,
|
||||
see doc/tutorial/src/singular.c.
|
||||
*/
|
||||
|
||||
-/* determine the length of the major axis of a circle of the given radius
|
||||
- after applying the transformation matrix. */
|
||||
-double
|
||||
-_cairo_matrix_transformed_circle_major_axis (const cairo_matrix_t *matrix,
|
||||
- double radius)
|
||||
+/* determine the length of the major and minor axes of a circle of the given
|
||||
+ radius after applying the transformation matrix. */
|
||||
+void
|
||||
+_cairo_matrix_transformed_circle_axes (const cairo_matrix_t *matrix,
|
||||
+ double radius,
|
||||
+ double *major,
|
||||
+ double *minor)
|
||||
{
|
||||
- double a, b, c, d, f, g, h, i, j;
|
||||
+ double a, b, c, d, f, g, h, i, j, k;
|
||||
|
||||
_cairo_matrix_get_affine (matrix,
|
||||
&a, &b,
|
||||
&c, &d,
|
||||
NULL, NULL);
|
||||
|
||||
i = a*a + b*b;
|
||||
j = c*c + d*d;
|
||||
+ k = a*c + b*d;
|
||||
|
||||
f = 0.5 * (i + j);
|
||||
g = 0.5 * (i - j);
|
||||
- h = a*c + b*d;
|
||||
+ h = hypot (g, k);
|
||||
|
||||
- return radius * sqrt (f + hypot (g, h));
|
||||
+ if (major)
|
||||
+ *major = radius * sqrt (f + h);
|
||||
+ if (minor)
|
||||
+ *minor = radius * sqrt (f - h);
|
||||
+}
|
||||
|
||||
- /*
|
||||
- * we don't need the minor axis length, which is
|
||||
- * double min = radius * sqrt (f - sqrt (g*g+h*h));
|
||||
- */
|
||||
+/* determine the length of the major axis of a circle of the given radius
|
||||
+ after applying the transformation matrix. */
|
||||
+double
|
||||
+_cairo_matrix_transformed_circle_major_axis (const cairo_matrix_t *matrix,
|
||||
+ double radius)
|
||||
+{
|
||||
+ double major;
|
||||
+
|
||||
+ _cairo_matrix_transformed_circle_axes (matrix, radius, &major, NULL);
|
||||
+
|
||||
+ return major;
|
||||
}
|
||||
|
||||
void
|
||||
_cairo_matrix_to_pixman_matrix (const cairo_matrix_t *matrix,
|
||||
pixman_transform_t *pixman_transform,
|
||||
double xc,
|
||||
double yc)
|
||||
{
|
||||
diff --git a/gfx/cairo/cairo/src/cairo-win32-printing-surface.c b/gfx/cairo/cairo/src/cairo-win32-printing-surface.c
|
||||
--- a/gfx/cairo/cairo/src/cairo-win32-printing-surface.c
|
||||
+++ b/gfx/cairo/cairo/src/cairo-win32-printing-surface.c
|
||||
@@ -610,16 +610,17 @@ static cairo_status_t
|
||||
int x_tile, y_tile, left, right, top, bottom;
|
||||
RECT clip;
|
||||
const cairo_color_t *background_color;
|
||||
const unsigned char *mime_data;
|
||||
unsigned long mime_size;
|
||||
cairo_image_info_t mime_info;
|
||||
cairo_bool_t use_mime;
|
||||
DWORD mime_type;
|
||||
+ cairo_bool_t axis_swap;
|
||||
|
||||
/* If we can't use StretchDIBits with this surface, we can't do anything
|
||||
* here.
|
||||
*/
|
||||
if (!(surface->flags & CAIRO_WIN32_SURFACE_CAN_STRETCHDIB))
|
||||
return CAIRO_INT_STATUS_UNSUPPORTED;
|
||||
|
||||
if (surface->content == CAIRO_CONTENT_COLOR_ALPHA)
|
||||
@@ -658,39 +659,65 @@ static cairo_status_t
|
||||
&mime_size,
|
||||
&mime_info);
|
||||
}
|
||||
if (_cairo_status_is_error (status))
|
||||
return status;
|
||||
|
||||
use_mime = (status == CAIRO_STATUS_SUCCESS);
|
||||
|
||||
- if (!use_mime && image->format != CAIRO_FORMAT_RGB24) {
|
||||
+ m = pattern->base.matrix;
|
||||
+ status = cairo_matrix_invert (&m);
|
||||
+ /* _cairo_pattern_set_matrix guarantees invertibility */
|
||||
+ assert (status == CAIRO_STATUS_SUCCESS);
|
||||
+ cairo_matrix_multiply (&m, &m, &surface->ctm);
|
||||
+ cairo_matrix_multiply (&m, &m, &surface->gdi_ctm);
|
||||
+ /* Check if the matrix swaps the X and Y axes by checking if the diagonal
|
||||
+ * is effectively zero. This can happen, for example, if it was composed
|
||||
+ * with a rotation such as a landscape transform. Some printing devices
|
||||
+ * don't support such transforms in StretchDIBits.
|
||||
+ */
|
||||
+ axis_swap = fabs (m.xx*image->width) < 1 && fabs (m.yy*image->height) < 1;
|
||||
+
|
||||
+ if (!use_mime && (image->format != CAIRO_FORMAT_RGB24 || axis_swap)) {
|
||||
cairo_surface_t *opaque_surface;
|
||||
cairo_surface_pattern_t image_pattern;
|
||||
cairo_solid_pattern_t background_pattern;
|
||||
+ int width = image->width, height = image->height;
|
||||
|
||||
+ if (axis_swap) {
|
||||
+ width = image->height;
|
||||
+ height = image->width;
|
||||
+ }
|
||||
opaque_surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24,
|
||||
- image->width,
|
||||
- image->height);
|
||||
+ width,
|
||||
+ height);
|
||||
if (opaque_surface->status) {
|
||||
status = opaque_surface->status;
|
||||
goto CLEANUP_OPAQUE_IMAGE;
|
||||
}
|
||||
|
||||
- _cairo_pattern_init_solid (&background_pattern,
|
||||
- background_color);
|
||||
- status = _cairo_surface_paint (opaque_surface,
|
||||
- CAIRO_OPERATOR_SOURCE,
|
||||
- &background_pattern.base,
|
||||
- NULL);
|
||||
- if (status)
|
||||
- goto CLEANUP_OPAQUE_IMAGE;
|
||||
+ if (image->format != CAIRO_FORMAT_RGB24) {
|
||||
+ _cairo_pattern_init_solid (&background_pattern,
|
||||
+ background_color);
|
||||
+ status = _cairo_surface_paint (opaque_surface,
|
||||
+ CAIRO_OPERATOR_SOURCE,
|
||||
+ &background_pattern.base,
|
||||
+ NULL);
|
||||
+ if (status)
|
||||
+ goto CLEANUP_OPAQUE_IMAGE;
|
||||
+ }
|
||||
|
||||
_cairo_pattern_init_for_surface (&image_pattern, &image->base);
|
||||
+ if (axis_swap) {
|
||||
+ /* swap the X and Y axes to undo the axis swap in the matrix */
|
||||
+ cairo_matrix_t swap_xy = { 0, 1, 1, 0, 0, 0 };
|
||||
+ cairo_pattern_set_matrix (&image_pattern.base, &swap_xy);
|
||||
+ cairo_matrix_multiply (&m, &swap_xy, &m);
|
||||
+ }
|
||||
status = _cairo_surface_paint (opaque_surface,
|
||||
CAIRO_OPERATOR_OVER,
|
||||
&image_pattern.base,
|
||||
NULL);
|
||||
_cairo_pattern_fini (&image_pattern.base);
|
||||
if (status)
|
||||
goto CLEANUP_OPAQUE_IMAGE;
|
||||
|
||||
@@ -706,23 +733,16 @@ static cairo_status_t
|
||||
bi.bmiHeader.biXPelsPerMeter = PELS_72DPI;
|
||||
bi.bmiHeader.biYPelsPerMeter = PELS_72DPI;
|
||||
bi.bmiHeader.biPlanes = 1;
|
||||
bi.bmiHeader.biBitCount = 32;
|
||||
bi.bmiHeader.biCompression = use_mime ? mime_type : BI_RGB;
|
||||
bi.bmiHeader.biClrUsed = 0;
|
||||
bi.bmiHeader.biClrImportant = 0;
|
||||
|
||||
- m = pattern->base.matrix;
|
||||
- status = cairo_matrix_invert (&m);
|
||||
- /* _cairo_pattern_set_matrix guarantees invertibility */
|
||||
- assert (status == CAIRO_STATUS_SUCCESS);
|
||||
-
|
||||
- cairo_matrix_multiply (&m, &m, &surface->gdi_ctm);
|
||||
- cairo_matrix_multiply(&m, &m, &surface->ctm);
|
||||
SaveDC (surface->dc);
|
||||
_cairo_matrix_to_win32_xform (&m, &xform);
|
||||
|
||||
if (! SetWorldTransform (surface->dc, &xform)) {
|
||||
status = _cairo_win32_print_gdi_error ("_cairo_win32_printing_surface_paint_image_pattern");
|
||||
goto CLEANUP_OPAQUE_IMAGE;
|
||||
}
|
||||
|
||||
@@ -1260,16 +1280,17 @@ static cairo_int_status_t
|
||||
DWORD pen_width;
|
||||
DWORD *dash_array;
|
||||
HGDIOBJ obj;
|
||||
unsigned int i;
|
||||
cairo_solid_pattern_t clear;
|
||||
cairo_matrix_t mat;
|
||||
double scale;
|
||||
double scaled_width;
|
||||
+ double major, minor;
|
||||
|
||||
status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
if (op == CAIRO_OPERATOR_CLEAR) {
|
||||
_cairo_win32_printing_surface_init_clear_color (surface, &clear);
|
||||
source = (cairo_pattern_t*) &clear;
|
||||
@@ -1350,22 +1371,40 @@ static cairo_int_status_t
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
/*
|
||||
* Switch to user space to set line parameters
|
||||
*/
|
||||
SaveDC (surface->dc);
|
||||
|
||||
- _cairo_matrix_to_win32_xform (&mat, &xform);
|
||||
- xform.eDx = 0.0f;
|
||||
- xform.eDy = 0.0f;
|
||||
+ /* Some printers don't handle transformed strokes. Avoid the transform
|
||||
+ * if not required for the pen shape. Use the SVD here to find the major
|
||||
+ * and minor scales then check if they differ by more than 1 device unit.
|
||||
+ * If the difference is smaller, then just treat the scaling as uniform.
|
||||
+ * This check ignores rotations as the pen shape is symmetric before
|
||||
+ * transformation.
|
||||
+ */
|
||||
+ _cairo_matrix_transformed_circle_axes (&mat, scale, &major, &minor);
|
||||
+ if (fabs (major - minor) > 1) {
|
||||
+ /* Check if the matrix swaps the X and Y axes such that the diagonal
|
||||
+ * is nearly zero. This was observed to cause problems with XPS export.
|
||||
+ */
|
||||
+ if (fabs (mat.xx) < 1e-6 && fabs (mat.yy) < 1e-6) {
|
||||
+ /* swap the X and Y axes to undo the axis swap in the matrix */
|
||||
+ cairo_matrix_t swap_xy = { 0, 1, 1, 0, 0, 0 };
|
||||
+ cairo_matrix_multiply (&mat, &swap_xy, &mat);
|
||||
+ }
|
||||
+ _cairo_matrix_to_win32_xform (&mat, &xform);
|
||||
+ xform.eDx = 0.0f;
|
||||
+ xform.eDy = 0.0f;
|
||||
|
||||
- if (!ModifyWorldTransform (surface->dc, &xform, MWT_LEFTMULTIPLY))
|
||||
- return _cairo_win32_print_gdi_error ("_win32_surface_stroke:SetWorldTransform");
|
||||
+ if (!ModifyWorldTransform (surface->dc, &xform, MWT_LEFTMULTIPLY))
|
||||
+ return _cairo_win32_print_gdi_error ("_win32_surface_stroke:SetWorldTransform");
|
||||
+ }
|
||||
|
||||
if (source->type == CAIRO_PATTERN_TYPE_SOLID) {
|
||||
StrokePath (surface->dc);
|
||||
} else {
|
||||
if (!WidenPath (surface->dc))
|
||||
return _cairo_win32_print_gdi_error ("_win32_surface_stroke:WidenPath");
|
||||
if (!SelectClipPath (surface->dc, RGN_AND))
|
||||
return _cairo_win32_print_gdi_error ("_win32_surface_stroke:SelectClipPath");
|
||||
diff --git a/gfx/cairo/cairo/src/cairoint.h b/gfx/cairo/cairo/src/cairoint.h
|
||||
--- a/gfx/cairo/cairo/src/cairoint.h
|
||||
+++ b/gfx/cairo/cairo/src/cairoint.h
|
||||
@@ -2115,16 +2115,22 @@ cairo_private cairo_bool_t
|
||||
int *itx, int *ity);
|
||||
|
||||
cairo_private cairo_bool_t
|
||||
_cairo_matrix_has_unity_scale (const cairo_matrix_t *matrix);
|
||||
|
||||
cairo_private cairo_bool_t
|
||||
_cairo_matrix_is_pixel_exact (const cairo_matrix_t *matrix) cairo_pure;
|
||||
|
||||
+cairo_private void
|
||||
+_cairo_matrix_transformed_circle_axes (const cairo_matrix_t *matrix,
|
||||
+ double radius,
|
||||
+ double *major,
|
||||
+ double *minor);
|
||||
+
|
||||
cairo_private double
|
||||
_cairo_matrix_transformed_circle_major_axis (const cairo_matrix_t *matrix,
|
||||
double radius) cairo_pure;
|
||||
|
||||
cairo_private void
|
||||
_cairo_matrix_to_pixman_matrix (const cairo_matrix_t *matrix,
|
||||
pixman_transform_t *pixman_transform,
|
||||
double xc,
|
|
@ -134,13 +134,19 @@ AsyncCompositionManager::ResolveRefLayers(CompositorParent* aCompositor,
|
|||
*aHasRemoteContent = false;
|
||||
}
|
||||
|
||||
// If valid *aResolvePlugins indicates if we need to update plugin geometry
|
||||
// when we walk the tree.
|
||||
bool willResolvePlugins = (aResolvePlugins && *aResolvePlugins);
|
||||
if (!mLayerManager->GetRoot()) {
|
||||
// Updated the return value since this result controls completing composition.
|
||||
if (aResolvePlugins) {
|
||||
*aResolvePlugins = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
mReadyForCompose = true;
|
||||
bool hasRemoteContent = false;
|
||||
bool willResolvePlugins = (aResolvePlugins && *aResolvePlugins);
|
||||
bool didResolvePlugins = false;
|
||||
WalkTheTree<Resolve>(mLayerManager->GetRoot(),
|
||||
mReadyForCompose,
|
||||
|
|
|
@ -2137,7 +2137,13 @@ CompositorParent::UpdatePluginWindowState(uint64_t aId)
|
|||
lts.mPluginData);
|
||||
lts.mUpdatedPluginDataAvailable = false;
|
||||
PLUGINS_LOG("[%" PRIu64 "] updated", aId);
|
||||
} else {
|
||||
PLUGINS_LOG("[%" PRIu64 "] no visibility data", aId);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
PLUGINS_LOG("[%" PRIu64 "] no content root", aId);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -424,7 +424,8 @@ gfxASurface::CheckSurfaceSize(const IntSize& sz, int32_t limit)
|
|||
int32_t
|
||||
gfxASurface::FormatStrideForWidth(gfxImageFormat format, int32_t width)
|
||||
{
|
||||
return cairo_format_stride_for_width((cairo_format_t)(int)format, (int)width);
|
||||
cairo_format_t cformat = gfxImageFormatToCairoFormat(format);
|
||||
return cairo_format_stride_for_width(cformat, (int)width);
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
@ -467,7 +468,6 @@ gfxASurface::ContentFromFormat(gfxImageFormat format)
|
|||
case gfxImageFormat::RGB16_565:
|
||||
return gfxContentType::COLOR;
|
||||
case gfxImageFormat::A8:
|
||||
case gfxImageFormat::A1:
|
||||
return gfxContentType::ALPHA;
|
||||
|
||||
case gfxImageFormat::Unknown:
|
||||
|
@ -674,8 +674,6 @@ gfxASurface::BytesPerPixel(gfxImageFormat aImageFormat)
|
|||
return 2;
|
||||
case gfxImageFormat::A8:
|
||||
return 1;
|
||||
case gfxImageFormat::A1:
|
||||
return 1; // Close enough
|
||||
case gfxImageFormat::Unknown:
|
||||
default:
|
||||
NS_NOTREACHED("Not really sure what you want me to say here");
|
||||
|
|
|
@ -77,8 +77,8 @@ public:
|
|||
mozilla::gfx::IntSize size(shmInfo->width, shmInfo->height);
|
||||
if (!gfxASurface::CheckSurfaceSize(size))
|
||||
return nullptr;
|
||||
|
||||
gfxImageFormat format = (gfxImageFormat)shmInfo->format;
|
||||
|
||||
gfxImageFormat format = shmInfo->format;
|
||||
long stride = gfxImageSurface::ComputeStride(size, format);
|
||||
|
||||
RefPtr<Sub> s =
|
||||
|
|
|
@ -32,7 +32,7 @@ gfxImageSurface::InitFromSurface(cairo_surface_t *csurf)
|
|||
mSize.width = cairo_image_surface_get_width(csurf);
|
||||
mSize.height = cairo_image_surface_get_height(csurf);
|
||||
mData = cairo_image_surface_get_data(csurf);
|
||||
mFormat = (gfxImageFormat) cairo_image_surface_get_format(csurf);
|
||||
mFormat = gfxCairoFormatToImageFormat(cairo_image_surface_get_format(csurf));
|
||||
mOwnsData = false;
|
||||
mStride = cairo_image_surface_get_stride(csurf);
|
||||
|
||||
|
@ -66,9 +66,10 @@ gfxImageSurface::InitWithData(unsigned char *aData, const IntSize& aSize,
|
|||
if (!CheckSurfaceSize(aSize))
|
||||
MakeInvalid();
|
||||
|
||||
cairo_format_t cformat = gfxImageFormatToCairoFormat(mFormat);
|
||||
cairo_surface_t *surface =
|
||||
cairo_image_surface_create_for_data((unsigned char*)mData,
|
||||
(cairo_format_t)(int)mFormat,
|
||||
cformat,
|
||||
mSize.width,
|
||||
mSize.height,
|
||||
mStride);
|
||||
|
@ -135,9 +136,10 @@ gfxImageSurface::AllocateAndInit(long aStride, int32_t aMinimalAllocation,
|
|||
|
||||
mOwnsData = true;
|
||||
|
||||
cairo_format_t cformat = gfxImageFormatToCairoFormat(mFormat);
|
||||
cairo_surface_t *surface =
|
||||
cairo_image_surface_create_for_data((unsigned char*)mData,
|
||||
(cairo_format_t)(int)mFormat,
|
||||
cformat,
|
||||
mSize.width,
|
||||
mSize.height,
|
||||
mStride);
|
||||
|
@ -162,7 +164,7 @@ gfxImageSurface::gfxImageSurface(cairo_surface_t *csurf)
|
|||
mSize.width = cairo_image_surface_get_width(csurf);
|
||||
mSize.height = cairo_image_surface_get_height(csurf);
|
||||
mData = cairo_image_surface_get_data(csurf);
|
||||
mFormat = (gfxImageFormat) cairo_image_surface_get_format(csurf);
|
||||
mFormat = gfxCairoFormatToImageFormat(cairo_image_surface_get_format(csurf));
|
||||
mOwnsData = false;
|
||||
mStride = cairo_image_surface_get_stride(csurf);
|
||||
|
||||
|
@ -188,9 +190,7 @@ gfxImageSurface::ComputeStride(const IntSize& aSize, gfxImageFormat aFormat)
|
|||
stride = aSize.width * 2;
|
||||
else if (aFormat == gfxImageFormat::A8)
|
||||
stride = aSize.width;
|
||||
else if (aFormat == gfxImageFormat::A1) {
|
||||
stride = (aSize.width + 7) / 8;
|
||||
} else {
|
||||
else {
|
||||
NS_WARNING("Unknown format specified to gfxImageSurface!");
|
||||
stride = aSize.width * 4;
|
||||
}
|
||||
|
|
|
@ -21,9 +21,9 @@ gfxQPainterSurface::gfxQPainterSurface(QPainter *painter)
|
|||
|
||||
gfxQPainterSurface::gfxQPainterSurface(const mozilla::gfx::IntSize& size, gfxImageFormat format)
|
||||
{
|
||||
cairo_surface_t *csurf = cairo_qt_surface_create_with_qimage ((cairo_format_t) format,
|
||||
size.width,
|
||||
size.height);
|
||||
cairo_format_t cformat = gfxImageFormatToCairoFormat(format);
|
||||
cairo_surface_t *csurf =
|
||||
cairo_qt_surface_create_with_qimage(cformat, size.width, size.height);
|
||||
mPainter = cairo_qt_surface_get_qpainter (csurf);
|
||||
|
||||
Init (csurf);
|
||||
|
|
|
@ -26,8 +26,8 @@ gfxQuartzSurface::gfxQuartzSurface(const gfxSize& desiredSize, gfxImageFormat fo
|
|||
unsigned int width = static_cast<unsigned int>(mSize.width);
|
||||
unsigned int height = static_cast<unsigned int>(mSize.height);
|
||||
|
||||
cairo_surface_t *surf = cairo_quartz_surface_create
|
||||
((cairo_format_t) format, width, height);
|
||||
cairo_format_t cformat = gfxImageFormatToCairoFormat(format);
|
||||
cairo_surface_t *surf = cairo_quartz_surface_create(cformat, width, height);
|
||||
|
||||
mCGContext = cairo_quartz_surface_get_cg_context (surf);
|
||||
|
||||
|
@ -109,8 +109,9 @@ gfxQuartzSurface::gfxQuartzSurface(unsigned char *data,
|
|||
unsigned int width = static_cast<unsigned int>(mSize.width);
|
||||
unsigned int height = static_cast<unsigned int>(mSize.height);
|
||||
|
||||
cairo_format_t cformat = gfxImageFormatToCairoFormat(format);
|
||||
cairo_surface_t *surf = cairo_quartz_surface_create_for_data
|
||||
(data, (cairo_format_t) format, width, height, stride);
|
||||
(data, cformat, width, height, stride);
|
||||
|
||||
mCGContext = cairo_quartz_surface_get_cg_context (surf);
|
||||
|
||||
|
@ -131,8 +132,9 @@ gfxQuartzSurface::gfxQuartzSurface(unsigned char *data,
|
|||
if (!CheckSurfaceSize(aSize))
|
||||
MakeInvalid();
|
||||
|
||||
cairo_format_t cformat = gfxImageFormatToCairoFormat(format);
|
||||
cairo_surface_t *surf = cairo_quartz_surface_create_for_data
|
||||
(data, (cairo_format_t) format, aSize.width, aSize.height, stride);
|
||||
(data, cformat, aSize.width, aSize.height, stride);
|
||||
|
||||
mCGContext = cairo_quartz_surface_get_cg_context (surf);
|
||||
|
||||
|
|
|
@ -52,11 +52,22 @@ enum class gfxImageFormat {
|
|||
ARGB32, ///< ARGB data in native endianness, using premultiplied alpha
|
||||
RGB24, ///< xRGB data in native endianness
|
||||
A8, ///< Only an alpha channel
|
||||
A1, ///< Packed transparency information (one byte refers to 8 pixels)
|
||||
RGB16_565, ///< RGB_565 data in native endianness
|
||||
Unknown
|
||||
};
|
||||
|
||||
// XXX: temporary
|
||||
// This works because the gfxImageFormat enum is defined so as to match the
|
||||
// _cairo_format enum.
|
||||
#define gfxCairoFormatToImageFormat(aFormat) \
|
||||
((gfxImageFormat)aFormat)
|
||||
|
||||
// XXX: temporary
|
||||
// This works because the gfxImageFormat enum is defined so as to match the
|
||||
// _cairo_format enum.
|
||||
#define gfxImageFormatToCairoFormat(aFormat) \
|
||||
((cairo_format_t)aFormat)
|
||||
|
||||
enum class gfxSurfaceType {
|
||||
Image,
|
||||
PDF,
|
||||
|
|
|
@ -59,8 +59,9 @@ gfxWindowsSurface::gfxWindowsSurface(const mozilla::gfx::IntSize& realSize, gfxI
|
|||
if (!CheckSurfaceSize(size))
|
||||
MakeInvalid(size);
|
||||
|
||||
cairo_surface_t *surf = cairo_win32_surface_create_with_dib((cairo_format_t)(int)imageFormat,
|
||||
size.width, size.height);
|
||||
cairo_format_t cformat = gfxImageFormatToCairoFormat(imageFormat);
|
||||
cairo_surface_t *surf =
|
||||
cairo_win32_surface_create_with_dib(cformat, size.width, size.height);
|
||||
|
||||
Init(surf);
|
||||
|
||||
|
@ -79,8 +80,10 @@ gfxWindowsSurface::gfxWindowsSurface(HDC dc, const mozilla::gfx::IntSize& realSi
|
|||
if (!CheckSurfaceSize(size))
|
||||
MakeInvalid(size);
|
||||
|
||||
cairo_surface_t *surf = cairo_win32_surface_create_with_ddb(dc, (cairo_format_t)(int)imageFormat,
|
||||
size.width, size.height);
|
||||
cairo_format_t cformat = gfxImageFormatToCairoFormat(imageFormat);
|
||||
cairo_surface_t *surf =
|
||||
cairo_win32_surface_create_with_ddb(dc, cformat,
|
||||
size.width, size.height);
|
||||
|
||||
Init(surf);
|
||||
|
||||
|
@ -138,9 +141,11 @@ gfxWindowsSurface::CreateSimilarSurface(gfxContentType aContent,
|
|||
// a surface that's the result of create_similar(gfxContentType::COLOR_ALPHA)
|
||||
// (e.g. a backbuffer for the window) --- that new surface *would*
|
||||
// have a DIB.
|
||||
surface =
|
||||
cairo_win32_surface_create_with_dib((cairo_format_t)(int)gfxPlatform::GetPlatform()->OptimalFormatForContent(aContent),
|
||||
aSize.width, aSize.height);
|
||||
gfxImageFormat gformat =
|
||||
gfxPlatform::GetPlatform()->OptimalFormatForContent(aContent);
|
||||
cairo_format_t cformat = gfxImageFormatToCairoFormat(gformat);
|
||||
surface = cairo_win32_surface_create_with_dib(cformat, aSize.width,
|
||||
aSize.height);
|
||||
} else {
|
||||
surface =
|
||||
cairo_surface_create_similar(mSurface, (cairo_content_t)(int)aContent,
|
||||
|
|
|
@ -352,7 +352,7 @@ CreateTempXlibSurface (cairo_surface_t* cairoTarget,
|
|||
} else if (cairoTargetType == CAIRO_SURFACE_TYPE_IMAGE || drawTarget) {
|
||||
gfxImageFormat imageFormat =
|
||||
drawTarget ? SurfaceFormatToImageFormat(drawTarget->GetFormat()) :
|
||||
(gfxImageFormat)cairo_image_surface_get_format(cairoTarget);
|
||||
gfxCairoFormatToImageFormat(cairo_image_surface_get_format(cairoTarget));
|
||||
target_visual = gfxXlibSurface::FindVisual(screen, imageFormat);
|
||||
Display *dpy = DisplayOfScreen(screen);
|
||||
if (target_visual) {
|
||||
|
|
|
@ -523,7 +523,6 @@ gfxXlibSurface::FindVisual(Screen *screen, gfxImageFormat format)
|
|||
blue_mask = 0x1f;
|
||||
break;
|
||||
case gfxImageFormat::A8:
|
||||
case gfxImageFormat::A1:
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -567,8 +566,6 @@ gfxXlibSurface::FindRenderFormat(Display *dpy, gfxImageFormat format)
|
|||
}
|
||||
case gfxImageFormat::A8:
|
||||
return XRenderFindStandardFormat (dpy, PictStandardA8);
|
||||
case gfxImageFormat::A1:
|
||||
return XRenderFindStandardFormat (dpy, PictStandardA1);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
/* -*- Mode: C++; tab-width: 40; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* -*- 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 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/. */
|
||||
|
@ -54,46 +55,21 @@ WakeLockInfoFromLockCount(const nsAString& aTopic, const LockCount& aLockCount)
|
|||
return info;
|
||||
}
|
||||
|
||||
PLDHashOperator
|
||||
CountWakeLocks(const uint64_t& aKey, LockCount aCount, void* aUserArg)
|
||||
static void
|
||||
CountWakeLocks(ProcessLockTable* aTable, LockCount* aTotalCount)
|
||||
{
|
||||
MOZ_ASSERT(aUserArg);
|
||||
for (auto iter = aTable->Iter(); !iter.Done(); iter.Next()) {
|
||||
const uint64_t& key = iter.Key();
|
||||
LockCount count = iter.UserData();
|
||||
|
||||
LockCount* totalCount = static_cast<LockCount*>(aUserArg);
|
||||
totalCount->numLocks += aCount.numLocks;
|
||||
totalCount->numHidden += aCount.numHidden;
|
||||
aTotalCount->numLocks += count.numLocks;
|
||||
aTotalCount->numHidden += count.numHidden;
|
||||
|
||||
// This is linear in the number of processes, but that should be small.
|
||||
if (!totalCount->processes.Contains(aKey)) {
|
||||
totalCount->processes.AppendElement(aKey);
|
||||
}
|
||||
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
static PLDHashOperator
|
||||
RemoveChildFromList(const nsAString& aKey, nsAutoPtr<ProcessLockTable>& aTable,
|
||||
void* aUserArg)
|
||||
{
|
||||
MOZ_ASSERT(aUserArg);
|
||||
|
||||
PLDHashOperator op = PL_DHASH_NEXT;
|
||||
uint64_t childID = *static_cast<uint64_t*>(aUserArg);
|
||||
if (aTable->Get(childID, nullptr)) {
|
||||
aTable->Remove(childID);
|
||||
|
||||
LockCount totalCount;
|
||||
aTable->EnumerateRead(CountWakeLocks, &totalCount);
|
||||
if (!totalCount.numLocks) {
|
||||
op = PL_DHASH_REMOVE;
|
||||
}
|
||||
|
||||
if (sActiveListeners) {
|
||||
NotifyWakeLockChange(WakeLockInfoFromLockCount(aKey, totalCount));
|
||||
// This is linear in the number of processes, but that should be small.
|
||||
if (!aTotalCount->processes.Contains(key)) {
|
||||
aTotalCount->processes.AppendElement(key);
|
||||
}
|
||||
}
|
||||
|
||||
return op;
|
||||
}
|
||||
|
||||
class ClearHashtableOnShutdown final : public nsIObserver {
|
||||
|
@ -144,7 +120,25 @@ CleanupOnContentShutdown::Observe(nsISupports* aSubject, const char* aTopic, con
|
|||
nsresult rv = props->GetPropertyAsUint64(NS_LITERAL_STRING("childID"),
|
||||
&childID);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
sLockTable->Enumerate(RemoveChildFromList, &childID);
|
||||
for (auto iter = sLockTable->Iter(); !iter.Done(); iter.Next()) {
|
||||
nsAutoPtr<ProcessLockTable>& table = iter.Data();
|
||||
|
||||
if (table->Get(childID, nullptr)) {
|
||||
table->Remove(childID);
|
||||
|
||||
LockCount totalCount;
|
||||
CountWakeLocks(table, &totalCount);
|
||||
|
||||
if (sActiveListeners) {
|
||||
NotifyWakeLockChange(WakeLockInfoFromLockCount(iter.Key(),
|
||||
totalCount));
|
||||
}
|
||||
|
||||
if (totalCount.numLocks == 0) {
|
||||
iter.Remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
NS_WARNING("ipc:content-shutdown message without childID property");
|
||||
}
|
||||
|
@ -222,7 +216,7 @@ ModifyWakeLock(const nsAString& aTopic,
|
|||
sLockTable->Put(aTopic, table);
|
||||
} else {
|
||||
table->Get(aProcessID, &processCount);
|
||||
table->EnumerateRead(CountWakeLocks, &totalCount);
|
||||
CountWakeLocks(table, &totalCount);
|
||||
}
|
||||
|
||||
MOZ_ASSERT(processCount.numLocks >= processCount.numHidden);
|
||||
|
@ -279,7 +273,7 @@ GetWakeLockInfo(const nsAString& aTopic, WakeLockInformation* aWakeLockInfo)
|
|||
return;
|
||||
}
|
||||
LockCount totalCount;
|
||||
table->EnumerateRead(CountWakeLocks, &totalCount);
|
||||
CountWakeLocks(table, &totalCount);
|
||||
*aWakeLockInfo = WakeLockInfoFromLockCount(aTopic, totalCount);
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ function WeakSet_add(value) {
|
|||
// Steps 1-3.
|
||||
var S = this;
|
||||
if (!IsObject(S) || !IsWeakSet(S))
|
||||
ThrowTypeError(JSMSG_INCOMPATIBLE_PROTO, "WeakSet", "add", typeof S);
|
||||
return callFunction(CallWeakSetMethodIfWrapped, this, value, "WeakSet_add");
|
||||
|
||||
// Step 4.,6.
|
||||
let entries = UnsafeGetReservedSlot(this, WEAKSET_MAP_SLOT);
|
||||
|
@ -30,7 +30,7 @@ function WeakSet_clear() {
|
|||
// Step 1-3.
|
||||
var S = this;
|
||||
if (!IsObject(S) || !IsWeakSet(S))
|
||||
ThrowTypeError(JSMSG_INCOMPATIBLE_PROTO, "WeakSet", "clear", typeof S);
|
||||
return callFunction(CallWeakSetMethodIfWrapped, this, "WeakSet_clear");
|
||||
|
||||
// Step 4.
|
||||
let entries = UnsafeGetReservedSlot(this, WEAKSET_MAP_SLOT);
|
||||
|
@ -49,7 +49,7 @@ function WeakSet_delete(value) {
|
|||
// Steps 1-3.
|
||||
var S = this;
|
||||
if (!IsObject(S) || !IsWeakSet(S))
|
||||
ThrowTypeError(JSMSG_INCOMPATIBLE_PROTO, "WeakSet", "delete", typeof S);
|
||||
return callFunction(CallWeakSetMethodIfWrapped, this, value, "WeakSet_delete");
|
||||
|
||||
// Step 4.,6.
|
||||
let entries = UnsafeGetReservedSlot(this, WEAKSET_MAP_SLOT);
|
||||
|
@ -69,7 +69,7 @@ function WeakSet_has(value) {
|
|||
// Steps 1-3.
|
||||
var S = this;
|
||||
if (!IsObject(S) || !IsWeakSet(S))
|
||||
ThrowTypeError(JSMSG_INCOMPATIBLE_PROTO, "WeakSet", "has", typeof S);
|
||||
return callFunction(CallWeakSetMethodIfWrapped, this, value, "WeakSet_has");
|
||||
|
||||
// Step 4-5.
|
||||
let entries = UnsafeGetReservedSlot(this, WEAKSET_MAP_SLOT);
|
||||
|
|
|
@ -821,8 +821,8 @@ struct ReadBarrieredHasher
|
|||
typedef T Lookup;
|
||||
|
||||
static HashNumber hash(Lookup obj) { return DefaultHasher<T>::hash(obj); }
|
||||
static bool match(const Key& k, Lookup l) { return k.get() == l; }
|
||||
static void rekey(Key& k, const Key& newKey) { k.set(newKey); }
|
||||
static bool match(const Key& k, Lookup l) { return k.unbarrieredGet() == l; }
|
||||
static void rekey(Key& k, const Key& newKey) { k.set(newKey.unbarrieredGet()); }
|
||||
};
|
||||
|
||||
/* Specialized hashing policy for ReadBarriereds. */
|
||||
|
@ -894,15 +894,12 @@ typedef ImmutableTenuredPtr<JS::Symbol*> ImmutableSymbolPtr;
|
|||
|
||||
typedef ReadBarriered<DebugScopeObject*> ReadBarrieredDebugScopeObject;
|
||||
typedef ReadBarriered<GlobalObject*> ReadBarrieredGlobalObject;
|
||||
typedef ReadBarriered<JSFunction*> ReadBarrieredFunction;
|
||||
typedef ReadBarriered<JSObject*> ReadBarrieredObject;
|
||||
typedef ReadBarriered<JSScript*> ReadBarrieredScript;
|
||||
typedef ReadBarriered<ScriptSourceObject*> ReadBarrieredScriptSourceObject;
|
||||
typedef ReadBarriered<Shape*> ReadBarrieredShape;
|
||||
typedef ReadBarriered<UnownedBaseShape*> ReadBarrieredUnownedBaseShape;
|
||||
typedef ReadBarriered<jit::JitCode*> ReadBarrieredJitCode;
|
||||
typedef ReadBarriered<ObjectGroup*> ReadBarrieredObjectGroup;
|
||||
typedef ReadBarriered<JSAtom*> ReadBarrieredAtom;
|
||||
typedef ReadBarriered<JS::Symbol*> ReadBarrieredSymbol;
|
||||
|
||||
typedef ReadBarriered<Value> ReadBarrieredValue;
|
||||
|
|
|
@ -664,6 +664,7 @@ class GCRuntime
|
|||
// Internal public interface
|
||||
js::gc::State state() const { return incrementalState; }
|
||||
bool isHeapCompacting() const { return state() == COMPACT; }
|
||||
bool isForegroundSweeping() const { return state() == SWEEP; }
|
||||
bool isBackgroundSweeping() { return helperState.isBackgroundSweeping(); }
|
||||
void waitBackgroundSweepEnd() { helperState.waitBackgroundSweepEnd(); }
|
||||
void waitBackgroundSweepOrAllocEnd() {
|
||||
|
|
|
@ -1457,8 +1457,13 @@ TenuredCell::readBarrier(TenuredCell* thing)
|
|||
MOZ_ASSERT(!isNullLike(thing));
|
||||
if (thing->shadowRuntimeFromAnyThread()->isHeapBusy())
|
||||
return;
|
||||
MOZ_ASSERT_IF(CurrentThreadCanAccessRuntime(thing->runtimeFromAnyThread()),
|
||||
!thing->shadowRuntimeFromAnyThread()->isHeapCollecting());
|
||||
|
||||
JS::shadow::Zone* shadowZone = thing->shadowZoneFromAnyThread();
|
||||
MOZ_ASSERT_IF(!CurrentThreadCanAccessRuntime(thing->runtimeFromAnyThread()),
|
||||
!shadowZone->needsIncrementalBarrier());
|
||||
|
||||
if (shadowZone->needsIncrementalBarrier()) {
|
||||
MOZ_ASSERT(!RuntimeFromMainThreadIsHeapMajorCollecting(shadowZone));
|
||||
Cell* tmp = thing;
|
||||
|
|
|
@ -2446,11 +2446,11 @@ void
|
|||
TypeSet::MarkTypeUnbarriered(JSTracer* trc, TypeSet::Type* v, const char* name)
|
||||
{
|
||||
if (v->isSingletonUnchecked()) {
|
||||
JSObject* obj = v->singleton();
|
||||
JSObject* obj = v->singletonNoBarrier();
|
||||
DispatchToTracer(trc, &obj, name);
|
||||
*v = TypeSet::ObjectType(obj);
|
||||
} else if (v->isGroupUnchecked()) {
|
||||
ObjectGroup* group = v->group();
|
||||
ObjectGroup* group = v->groupNoBarrier();
|
||||
DispatchToTracer(trc, &group, name);
|
||||
*v = TypeSet::ObjectType(group);
|
||||
}
|
||||
|
|
|
@ -147,7 +147,7 @@ Zone::logPromotionsToTenured()
|
|||
continue;
|
||||
|
||||
for (auto range = awaitingTenureLogging.all(); !range.empty(); range.popFront()) {
|
||||
if ((*dbgp)->isDebuggee(range.front()->compartment()))
|
||||
if ((*dbgp)->isDebuggeeUnbarriered(range.front()->compartment()))
|
||||
(*dbgp)->logTenurePromotion(rt, *range.front(), now);
|
||||
}
|
||||
}
|
||||
|
@ -311,7 +311,7 @@ Zone::notifyObservingDebuggers()
|
|||
{
|
||||
for (CompartmentsInZoneIter comps(this); !comps.done(); comps.next()) {
|
||||
JSRuntime* rt = runtimeFromAnyThread();
|
||||
RootedGlobalObject global(rt, comps->maybeGlobal());
|
||||
RootedGlobalObject global(rt, comps->unsafeUnbarrieredMaybeGlobal());
|
||||
if (!global)
|
||||
continue;
|
||||
|
||||
|
|
|
@ -40,3 +40,11 @@ ws.add(value2);
|
|||
value2 = null;
|
||||
gc();
|
||||
ws.clear();
|
||||
|
||||
// Clearing a cross-compartment WeakSet with a live value
|
||||
ws = new (newGlobal().WeakSet);
|
||||
value = {};
|
||||
WeakSet.prototype.add.call(ws, value);
|
||||
assertEq(WeakSet.prototype.has.call(ws, value), true);
|
||||
WeakSet.prototype.clear.call(ws);
|
||||
assertEq(WeakSet.prototype.has.call(ws, value), false);
|
||||
|
|
|
@ -29,3 +29,11 @@ assertEq(ws.has(value), false);
|
|||
|
||||
// Delete primitive
|
||||
assertEq(ws.delete(15), false);
|
||||
|
||||
// Delete with cross-compartment WeakSet
|
||||
ws = new (newGlobal().WeakSet);
|
||||
WeakSet.prototype.add.call(ws, value);
|
||||
assertEq(WeakSet.prototype.has.call(ws, value), true);
|
||||
assertEq(WeakSet.prototype.delete.call(ws, value), true);
|
||||
assertEq(WeakSet.prototype.has.call(ws, value), false);
|
||||
assertEq(WeakSet.prototype.delete.call(ws, value), false);
|
||||
|
|
|
@ -111,3 +111,94 @@ BEGIN_TEST(testSavedStacks_RangeBasedForLoops)
|
|||
return true;
|
||||
}
|
||||
END_TEST(testSavedStacks_RangeBasedForLoops)
|
||||
|
||||
BEGIN_TEST(testSavedStacks_selfHostedFrames)
|
||||
{
|
||||
CHECK(js::DefineTestingFunctions(cx, global, false, false));
|
||||
|
||||
JS::RootedValue val(cx);
|
||||
// 0 1 2 3
|
||||
// 0123456789012345678901234567890123456789
|
||||
CHECK(evaluate("(function one() { \n" // 1
|
||||
" try { \n" // 2
|
||||
" [1].map(function two() { \n" // 3
|
||||
" throw saveStack(); \n" // 4
|
||||
" }); \n" // 5
|
||||
" } catch (stack) { \n" // 6
|
||||
" return stack; \n" // 7
|
||||
" } \n" // 8
|
||||
"}()) \n", // 9
|
||||
"filename.js",
|
||||
1,
|
||||
&val));
|
||||
|
||||
CHECK(val.isObject());
|
||||
JS::RootedObject obj(cx, &val.toObject());
|
||||
|
||||
CHECK(obj->is<js::SavedFrame>());
|
||||
JS::Rooted<js::SavedFrame*> savedFrame(cx, &obj->as<js::SavedFrame>());
|
||||
|
||||
JS::Rooted<js::SavedFrame*> selfHostedFrame(cx, savedFrame->getParent());
|
||||
CHECK(selfHostedFrame->isSelfHosted());
|
||||
|
||||
// Source
|
||||
JS::RootedString str(cx);
|
||||
JS::SavedFrameResult result = JS::GetSavedFrameSource(cx, selfHostedFrame, &str,
|
||||
JS::SavedFrameSelfHosted::Exclude);
|
||||
CHECK(result == JS::SavedFrameResult::Ok);
|
||||
JSLinearString* lin = str->ensureLinear(cx);
|
||||
CHECK(lin);
|
||||
CHECK(js::StringEqualsAscii(lin, "filename.js"));
|
||||
|
||||
// Source, including self-hosted frames
|
||||
result = JS::GetSavedFrameSource(cx, selfHostedFrame, &str, JS::SavedFrameSelfHosted::Include);
|
||||
CHECK(result == JS::SavedFrameResult::Ok);
|
||||
lin = str->ensureLinear(cx);
|
||||
CHECK(lin);
|
||||
CHECK(js::StringEqualsAscii(lin, "self-hosted"));
|
||||
|
||||
// Line
|
||||
uint32_t line = 123;
|
||||
result = JS::GetSavedFrameLine(cx, selfHostedFrame, &line, JS::SavedFrameSelfHosted::Exclude);
|
||||
CHECK(result == JS::SavedFrameResult::Ok);
|
||||
CHECK_EQUAL(line, 3U);
|
||||
|
||||
// Column
|
||||
uint32_t column = 123;
|
||||
result = JS::GetSavedFrameColumn(cx, selfHostedFrame, &column,
|
||||
JS::SavedFrameSelfHosted::Exclude);
|
||||
CHECK(result == JS::SavedFrameResult::Ok);
|
||||
CHECK_EQUAL(column, 5U);
|
||||
|
||||
// Function display name
|
||||
result = JS::GetSavedFrameFunctionDisplayName(cx, selfHostedFrame, &str,
|
||||
JS::SavedFrameSelfHosted::Exclude);
|
||||
CHECK(result == JS::SavedFrameResult::Ok);
|
||||
lin = str->ensureLinear(cx);
|
||||
CHECK(lin);
|
||||
CHECK(js::StringEqualsAscii(lin, "one"));
|
||||
|
||||
// Parent
|
||||
JS::RootedObject parent(cx);
|
||||
result = JS::GetSavedFrameParent(cx, savedFrame, &parent, JS::SavedFrameSelfHosted::Exclude);
|
||||
CHECK(result == JS::SavedFrameResult::Ok);
|
||||
// JS::GetSavedFrameParent does this super funky and potentially unexpected
|
||||
// thing where it doesn't return the next subsumed parent but any next
|
||||
// parent. This so that callers can still get the "asyncParent" property
|
||||
// which is only on the first frame of the async parent stack and that frame
|
||||
// might not be subsumed by the caller. It is expected that callers will
|
||||
// still interact with the frame through the JSAPI accessors, so this should
|
||||
// be safe and should not leak privileged info to unprivileged
|
||||
// callers. However, because of that, we don't test that the parent we get
|
||||
// here is the selfHostedFrame's parent (because, as just explained, it
|
||||
// isn't) and instead check that asking for the source property gives us the
|
||||
// expected value.
|
||||
result = JS::GetSavedFrameSource(cx, parent, &str, JS::SavedFrameSelfHosted::Exclude);
|
||||
CHECK(result == JS::SavedFrameResult::Ok);
|
||||
lin = str->ensureLinear(cx);
|
||||
CHECK(lin);
|
||||
CHECK(js::StringEqualsAscii(lin, "filename.js"));
|
||||
|
||||
return true;
|
||||
}
|
||||
END_TEST(testSavedStacks_selfHostedFrames)
|
||||
|
|
|
@ -5550,6 +5550,10 @@ CaptureCurrentStack(JSContext* cx, MutableHandleObject stackp, unsigned maxFrame
|
|||
* same compartment as the cx, and the various out parameters are _NOT_
|
||||
* guaranteed to be in the same compartment as cx.
|
||||
*
|
||||
* You may consider or skip over self-hosted frames by passing
|
||||
* `SavedFrameSelfHosted::Include` or `SavedFrameSelfHosted::Exclude`
|
||||
* respectively.
|
||||
*
|
||||
* Additionally, it may be the case that there is no such SavedFrame object
|
||||
* whose captured frame's principals are subsumed by the caller's compartment's
|
||||
* principals! If the `HandleObject savedFrame` argument is null, or the
|
||||
|
@ -5565,24 +5569,32 @@ enum class SavedFrameResult {
|
|||
AccessDenied
|
||||
};
|
||||
|
||||
enum class SavedFrameSelfHosted {
|
||||
Include,
|
||||
Exclude
|
||||
};
|
||||
|
||||
/**
|
||||
* Given a SavedFrame JSObject, get its source property. Defaults to the empty
|
||||
* string.
|
||||
*/
|
||||
extern JS_PUBLIC_API(SavedFrameResult)
|
||||
GetSavedFrameSource(JSContext* cx, HandleObject savedFrame, MutableHandleString sourcep);
|
||||
GetSavedFrameSource(JSContext* cx, HandleObject savedFrame, MutableHandleString sourcep,
|
||||
SavedFrameSelfHosted selfHosted = SavedFrameSelfHosted::Include);
|
||||
|
||||
/**
|
||||
* Given a SavedFrame JSObject, get its line property. Defaults to 0.
|
||||
*/
|
||||
extern JS_PUBLIC_API(SavedFrameResult)
|
||||
GetSavedFrameLine(JSContext* cx, HandleObject savedFrame, uint32_t* linep);
|
||||
GetSavedFrameLine(JSContext* cx, HandleObject savedFrame, uint32_t* linep,
|
||||
SavedFrameSelfHosted selfHosted = SavedFrameSelfHosted::Include);
|
||||
|
||||
/**
|
||||
* Given a SavedFrame JSObject, get its column property. Defaults to 0.
|
||||
*/
|
||||
extern JS_PUBLIC_API(SavedFrameResult)
|
||||
GetSavedFrameColumn(JSContext* cx, HandleObject savedFrame, uint32_t* columnp);
|
||||
GetSavedFrameColumn(JSContext* cx, HandleObject savedFrame, uint32_t* columnp,
|
||||
SavedFrameSelfHosted selfHosted = SavedFrameSelfHosted::Include);
|
||||
|
||||
/**
|
||||
* Given a SavedFrame JSObject, get its functionDisplayName string, or nullptr
|
||||
|
@ -5590,13 +5602,15 @@ GetSavedFrameColumn(JSContext* cx, HandleObject savedFrame, uint32_t* columnp);
|
|||
* function. Defaults to nullptr.
|
||||
*/
|
||||
extern JS_PUBLIC_API(SavedFrameResult)
|
||||
GetSavedFrameFunctionDisplayName(JSContext* cx, HandleObject savedFrame, MutableHandleString namep);
|
||||
GetSavedFrameFunctionDisplayName(JSContext* cx, HandleObject savedFrame, MutableHandleString namep,
|
||||
SavedFrameSelfHosted selfHosted = SavedFrameSelfHosted::Include);
|
||||
|
||||
/**
|
||||
* Given a SavedFrame JSObject, get its asyncCause string. Defaults to nullptr.
|
||||
*/
|
||||
extern JS_PUBLIC_API(SavedFrameResult)
|
||||
GetSavedFrameAsyncCause(JSContext* cx, HandleObject savedFrame, MutableHandleString asyncCausep);
|
||||
GetSavedFrameAsyncCause(JSContext* cx, HandleObject savedFrame, MutableHandleString asyncCausep,
|
||||
SavedFrameSelfHosted selfHosted = SavedFrameSelfHosted::Include);
|
||||
|
||||
/**
|
||||
* Given a SavedFrame JSObject, get its asyncParent SavedFrame object or nullptr
|
||||
|
@ -5604,7 +5618,8 @@ GetSavedFrameAsyncCause(JSContext* cx, HandleObject savedFrame, MutableHandleStr
|
|||
* guaranteed to be in the cx's compartment. Defaults to nullptr.
|
||||
*/
|
||||
extern JS_PUBLIC_API(SavedFrameResult)
|
||||
GetSavedFrameAsyncParent(JSContext* cx, HandleObject savedFrame, MutableHandleObject asyncParentp);
|
||||
GetSavedFrameAsyncParent(JSContext* cx, HandleObject savedFrame, MutableHandleObject asyncParentp,
|
||||
SavedFrameSelfHosted selfHosted = SavedFrameSelfHosted::Include);
|
||||
|
||||
/**
|
||||
* Given a SavedFrame JSObject, get its parent SavedFrame object or nullptr if
|
||||
|
@ -5612,7 +5627,8 @@ GetSavedFrameAsyncParent(JSContext* cx, HandleObject savedFrame, MutableHandleOb
|
|||
* guaranteed to be in the cx's compartment. Defaults to nullptr.
|
||||
*/
|
||||
extern JS_PUBLIC_API(SavedFrameResult)
|
||||
GetSavedFrameParent(JSContext* cx, HandleObject savedFrame, MutableHandleObject parentp);
|
||||
GetSavedFrameParent(JSContext* cx, HandleObject savedFrame, MutableHandleObject parentp,
|
||||
SavedFrameSelfHosted selfHosted = SavedFrameSelfHosted::Include);
|
||||
|
||||
/**
|
||||
* Given a SavedFrame JSObject stack, stringify it in the same format as
|
||||
|
|
|
@ -199,11 +199,9 @@ js::MarkAtoms(JSTracer* trc)
|
|||
if (!entry.isPinned())
|
||||
continue;
|
||||
|
||||
JSAtom* atom = entry.asPtr();
|
||||
bool tagged = entry.isPinned();
|
||||
JSAtom* atom = entry.asPtrUnbarriered();
|
||||
TraceRoot(trc, &atom, "interned_atom");
|
||||
if (entry.asPtr() != atom)
|
||||
e.rekeyFront(AtomHasher::Lookup(atom), AtomStateEntry(atom, tagged));
|
||||
MOZ_ASSERT(entry.asPtrUnbarriered() == atom);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -252,7 +250,7 @@ JSRuntime::sweepAtoms()
|
|||
|
||||
for (AtomSet::Enum e(*atoms_); !e.empty(); e.popFront()) {
|
||||
AtomStateEntry entry = e.front();
|
||||
JSAtom* atom = entry.asPtr();
|
||||
JSAtom* atom = entry.asPtrUnbarriered();
|
||||
bool isDying = IsAboutToBeFinalizedUnbarriered(&atom);
|
||||
|
||||
/* Pinned or interned key cannot be finalized. */
|
||||
|
|
|
@ -75,6 +75,7 @@ class AtomStateEntry
|
|||
}
|
||||
|
||||
JSAtom* asPtr() const;
|
||||
JSAtom* asPtrUnbarriered() const;
|
||||
};
|
||||
|
||||
struct AtomHasher
|
||||
|
|
|
@ -26,6 +26,13 @@ js::AtomStateEntry::asPtr() const
|
|||
return atom;
|
||||
}
|
||||
|
||||
inline JSAtom*
|
||||
js::AtomStateEntry::asPtrUnbarriered() const
|
||||
{
|
||||
MOZ_ASSERT(bits != 0);
|
||||
return reinterpret_cast<JSAtom*>(bits & NO_TAG_MASK);
|
||||
}
|
||||
|
||||
namespace js {
|
||||
|
||||
inline jsid
|
||||
|
|
|
@ -264,7 +264,8 @@ JSCompartment::checkWrapperMapAfterMovingGC()
|
|||
CrossCompartmentKey key = e.front().key();
|
||||
CheckGCThingAfterMovingGC(key.debugger);
|
||||
CheckGCThingAfterMovingGC(key.wrapped);
|
||||
CheckGCThingAfterMovingGC(static_cast<Cell*>(e.front().value().get().toGCThing()));
|
||||
CheckGCThingAfterMovingGC(
|
||||
static_cast<Cell*>(e.front().value().unbarrieredGet().toGCThing()));
|
||||
|
||||
WrapperMap::Ptr ptr = crossCompartmentWrappers.lookup(key);
|
||||
MOZ_RELEASE_ASSERT(ptr.found() && &*ptr == &e.front());
|
||||
|
@ -545,7 +546,7 @@ JSCompartment::traceOutgoingCrossCompartmentWrappers(JSTracer* trc)
|
|||
MOZ_ASSERT(!zone()->isCollecting() || trc->runtime()->gc.isHeapCompacting());
|
||||
|
||||
for (WrapperMap::Enum e(crossCompartmentWrappers); !e.empty(); e.popFront()) {
|
||||
Value v = e.front().value();
|
||||
Value v = e.front().value().unbarrieredGet();
|
||||
if (e.front().key().kind == CrossCompartmentKey::ObjectWrapper) {
|
||||
ProxyObject* wrapper = &v.toObject().as<ProxyObject>();
|
||||
|
||||
|
@ -672,9 +673,9 @@ JSCompartment::sweepSavedStacks()
|
|||
void
|
||||
JSCompartment::sweepGlobalObject(FreeOp* fop)
|
||||
{
|
||||
if (global_.unbarrieredGet() && IsAboutToBeFinalized(&global_)) {
|
||||
if (global_ && IsAboutToBeFinalized(&global_)) {
|
||||
if (isDebuggee())
|
||||
Debugger::detachAllDebuggersFromGlobal(fop, global_);
|
||||
Debugger::detachAllDebuggersFromGlobal(fop, global_.unbarrieredGet());
|
||||
global_.set(nullptr);
|
||||
}
|
||||
}
|
||||
|
@ -1011,7 +1012,10 @@ JSCompartment::updateDebuggerObservesFlag(unsigned flag)
|
|||
flag == DebuggerObservesCoverage ||
|
||||
flag == DebuggerObservesAsmJS);
|
||||
|
||||
const GlobalObject::DebuggerVector* v = maybeGlobal()->getDebuggers();
|
||||
GlobalObject* global = zone()->runtimeFromMainThread()->gc.isForegroundSweeping()
|
||||
? unsafeUnbarrieredMaybeGlobal()
|
||||
: maybeGlobal();
|
||||
const GlobalObject::DebuggerVector* v = global->getDebuggers();
|
||||
for (Debugger * const* p = v->begin(); p != v->end(); p++) {
|
||||
Debugger* dbg = *p;
|
||||
if (flag == DebuggerObservesAllExecution ? dbg->observesAllExecution() :
|
||||
|
|
|
@ -2848,7 +2848,7 @@ GetNearestEnclosingWithScopeObjectForFunction(JSFunction* fun);
|
|||
* The savedFrame and cx do not need to be in the same compartment.
|
||||
*/
|
||||
extern JS_FRIEND_API(JSObject*)
|
||||
GetFirstSubsumedSavedFrame(JSContext* cx, JS::HandleObject savedFrame);
|
||||
GetFirstSubsumedSavedFrame(JSContext* cx, JS::HandleObject savedFrame, JS::SavedFrameSelfHosted selfHosted);
|
||||
|
||||
extern JS_FRIEND_API(bool)
|
||||
ReportIsNotFunction(JSContext* cx, JS::HandleValue v);
|
||||
|
|
|
@ -5226,7 +5226,7 @@ GCRuntime::beginSweepPhase(bool destroyingRuntime)
|
|||
MOZ_ASSERT(!c->gcIncomingGrayPointers);
|
||||
for (JSCompartment::WrapperEnum e(c); !e.empty(); e.popFront()) {
|
||||
if (e.front().key().kind != CrossCompartmentKey::StringWrapper)
|
||||
AssertNotOnGrayList(&e.front().value().get().toObject());
|
||||
AssertNotOnGrayList(&e.front().value().unbarrieredGet().toObject());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -1277,7 +1277,7 @@ template <typename T>
|
|||
inline void
|
||||
CheckGCThingAfterMovingGC(const ReadBarriered<T*>& t)
|
||||
{
|
||||
CheckGCThingAfterMovingGC(t.get());
|
||||
CheckGCThingAfterMovingGC(t.unbarrieredGet());
|
||||
}
|
||||
|
||||
struct CheckValueAfterMovingGCFunctor : public VoidDefaultAdaptor<Value> {
|
||||
|
|
|
@ -165,6 +165,9 @@ struct Runtime
|
|||
{}
|
||||
|
||||
bool isHeapBusy() const { return heapState_ != JS::HeapState::Idle; }
|
||||
bool isHeapMajorCollecting() const { return heapState_ == JS::HeapState::MajorCollecting; }
|
||||
bool isHeapMinorCollecting() const { return heapState_ == JS::HeapState::MinorCollecting; }
|
||||
bool isHeapCollecting() const { return isHeapMinorCollecting() || isHeapMajorCollecting(); }
|
||||
|
||||
js::gc::StoreBuffer* gcStoreBufferPtr() { return gcStoreBufferPtr_; }
|
||||
|
||||
|
|
|
@ -1702,10 +1702,10 @@ Debugger::slowPathOnIonCompilation(JSContext* cx, Handle<ScriptVector> scripts,
|
|||
}
|
||||
|
||||
bool
|
||||
Debugger::isDebuggee(const JSCompartment* compartment) const
|
||||
Debugger::isDebuggeeUnbarriered(const JSCompartment* compartment) const
|
||||
{
|
||||
MOZ_ASSERT(compartment);
|
||||
return compartment->isDebuggee() && debuggees.has(compartment->maybeGlobal());
|
||||
return compartment->isDebuggee() && debuggees.has(compartment->unsafeUnbarrieredMaybeGlobal());
|
||||
}
|
||||
|
||||
Debugger::TenurePromotionsLogEntry::TenurePromotionsLogEntry(JSRuntime* rt, JSObject& obj, double when)
|
||||
|
@ -2471,7 +2471,7 @@ Debugger::markAllIteratively(GCMarker* trc)
|
|||
JSRuntime* rt = trc->runtime();
|
||||
for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) {
|
||||
if (c->isDebuggee()) {
|
||||
GlobalObject* global = c->maybeGlobal();
|
||||
GlobalObject* global = c->unsafeUnbarrieredMaybeGlobal();
|
||||
if (!IsMarkedUnbarriered(&global))
|
||||
continue;
|
||||
|
||||
|
@ -2538,10 +2538,10 @@ Debugger::markAll(JSTracer* trc)
|
|||
for (Debugger* dbg : rt->debuggerList) {
|
||||
WeakGlobalObjectSet& debuggees = dbg->debuggees;
|
||||
for (WeakGlobalObjectSet::Enum e(debuggees); !e.empty(); e.popFront()) {
|
||||
GlobalObject* global = e.front();
|
||||
GlobalObject* global = e.front().unbarrieredGet();
|
||||
TraceManuallyBarrieredEdge(trc, &global, "Global Object");
|
||||
if (global != e.front())
|
||||
e.rekeyFront(ReadBarrieredGlobalObject(global));
|
||||
if (global != e.front().unbarrieredGet())
|
||||
e.rekeyFront(global, ReadBarrieredGlobalObject(global));
|
||||
}
|
||||
|
||||
HeapPtrNativeObject& dbgobj = dbg->toJSObjectRef();
|
||||
|
@ -2615,7 +2615,7 @@ Debugger::sweepAll(FreeOp* fop)
|
|||
* objects, this must be done before finalize time.
|
||||
*/
|
||||
for (WeakGlobalObjectSet::Enum e(dbg->debuggees); !e.empty(); e.popFront())
|
||||
dbg->removeDebuggeeGlobal(fop, e.front(), &e);
|
||||
dbg->removeDebuggeeGlobal(fop, e.front().unbarrieredGet(), &e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3455,7 +3455,7 @@ Debugger::recomputeDebuggeeZoneSet()
|
|||
AutoEnterOOMUnsafeRegion oomUnsafe;
|
||||
debuggeeZones.clear();
|
||||
for (auto range = debuggees.all(); !range.empty(); range.popFront()) {
|
||||
if (!debuggeeZones.put(range.front()->zone()))
|
||||
if (!debuggeeZones.put(range.front().unbarrieredGet()->zone()))
|
||||
oomUnsafe.crash("Debugger::removeDebuggeeGlobal");
|
||||
}
|
||||
}
|
||||
|
@ -3484,7 +3484,7 @@ Debugger::removeDebuggeeGlobal(FreeOp* fop, GlobalObject* global,
|
|||
*/
|
||||
MOZ_ASSERT(debuggees.has(global));
|
||||
MOZ_ASSERT(debuggeeZones.has(global->zone()));
|
||||
MOZ_ASSERT_IF(debugEnum, debugEnum->front() == global);
|
||||
MOZ_ASSERT_IF(debugEnum, debugEnum->front().unbarrieredGet() == global);
|
||||
|
||||
/*
|
||||
* FIXME Debugger::slowPathOnLeaveFrame needs to kill all Debugger.Frame
|
||||
|
@ -4103,7 +4103,7 @@ class MOZ_STACK_CLASS Debugger::ObjectQuery
|
|||
* node.
|
||||
*/
|
||||
JSCompartment* comp = referent.compartment();
|
||||
if (comp && !dbg->isDebuggee(comp)) {
|
||||
if (comp && !dbg->isDebuggeeUnbarriered(comp)) {
|
||||
traversal.abandonReferent();
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -259,7 +259,7 @@ class Debugger : private mozilla::LinkedListElement<Debugger>
|
|||
|
||||
// Return true if the given compartment is a debuggee of this debugger,
|
||||
// false otherwise.
|
||||
bool isDebuggee(const JSCompartment* compartment) const;
|
||||
bool isDebuggeeUnbarriered(const JSCompartment* compartment) const;
|
||||
|
||||
// Return true if this Debugger observed a debuggee that participated in the
|
||||
// GC identified by the given GC number. Return false otherwise.
|
||||
|
|
|
@ -400,8 +400,8 @@ struct ObjectGroupCompartment::NewEntry
|
|||
}
|
||||
|
||||
static inline bool match(const NewEntry& key, const Lookup& lookup) {
|
||||
return key.group->proto() == lookup.matchProto &&
|
||||
(!lookup.clasp || key.group->clasp() == lookup.clasp) &&
|
||||
return key.group.unbarrieredGet()->proto() == lookup.matchProto &&
|
||||
(!lookup.clasp || key.group.unbarrieredGet()->clasp() == lookup.clasp) &&
|
||||
key.associated == lookup.associated;
|
||||
}
|
||||
|
||||
|
@ -1809,11 +1809,11 @@ ObjectGroupCompartment::fixupNewTableAfterMovingGC(NewTable* table)
|
|||
for (NewTable::Enum e(*table); !e.empty(); e.popFront()) {
|
||||
NewEntry entry = e.front();
|
||||
bool needRekey = false;
|
||||
if (IsForwarded(entry.group.get())) {
|
||||
entry.group.set(Forwarded(entry.group.get()));
|
||||
if (IsForwarded(entry.group.unbarrieredGet())) {
|
||||
entry.group.set(Forwarded(entry.group.unbarrieredGet()));
|
||||
needRekey = true;
|
||||
}
|
||||
TaggedProto proto = entry.group->proto();
|
||||
TaggedProto proto = entry.group.unbarrieredGet()->proto();
|
||||
if (proto.isObject() && IsForwarded(proto.toObject())) {
|
||||
proto = TaggedProto(Forwarded(proto.toObject()));
|
||||
needRekey = true;
|
||||
|
@ -1823,7 +1823,7 @@ ObjectGroupCompartment::fixupNewTableAfterMovingGC(NewTable* table)
|
|||
needRekey = true;
|
||||
}
|
||||
if (needRekey) {
|
||||
const Class* clasp = entry.group->clasp();
|
||||
const Class* clasp = entry.group.unbarrieredGet()->clasp();
|
||||
if (entry.associated && entry.associated->is<JSFunction>())
|
||||
clasp = nullptr;
|
||||
NewEntry::Lookup lookup(clasp, proto, entry.associated);
|
||||
|
@ -1847,13 +1847,13 @@ ObjectGroupCompartment::checkNewTableAfterMovingGC(NewTable* table)
|
|||
|
||||
for (NewTable::Enum e(*table); !e.empty(); e.popFront()) {
|
||||
NewEntry entry = e.front();
|
||||
CheckGCThingAfterMovingGC(entry.group.get());
|
||||
TaggedProto proto = entry.group->proto();
|
||||
CheckGCThingAfterMovingGC(entry.group.unbarrieredGet());
|
||||
TaggedProto proto = entry.group.unbarrieredGet()->proto();
|
||||
if (proto.isObject())
|
||||
CheckGCThingAfterMovingGC(proto.toObject());
|
||||
CheckGCThingAfterMovingGC(entry.associated);
|
||||
|
||||
const Class* clasp = entry.group->clasp();
|
||||
const Class* clasp = entry.group.unbarrieredGet()->clasp();
|
||||
if (entry.associated && entry.associated->is<JSFunction>())
|
||||
clasp = nullptr;
|
||||
|
||||
|
|
|
@ -1035,10 +1035,6 @@ struct JSRuntime : public JS::shadow::Runtime,
|
|||
/* Garbage collector state has been sucessfully initialized. */
|
||||
bool gcInitialized;
|
||||
|
||||
bool isHeapMajorCollecting() const { return heapState_ == JS::HeapState::MajorCollecting; }
|
||||
bool isHeapMinorCollecting() const { return heapState_ == JS::HeapState::MinorCollecting; }
|
||||
bool isHeapCollecting() const { return isHeapMinorCollecting() || isHeapMajorCollecting(); }
|
||||
|
||||
int gcZeal() { return gc.zeal(); }
|
||||
|
||||
void lockGC() {
|
||||
|
|
|
@ -536,28 +536,37 @@ SavedFrameSubsumedByCaller(JSContext* cx, HandleSavedFrame frame)
|
|||
// skipped frames had the |asyncCause| property set, otherwise it is explicitly
|
||||
// set to false.
|
||||
static SavedFrame*
|
||||
GetFirstSubsumedFrame(JSContext* cx, HandleSavedFrame frame, bool& skippedAsync)
|
||||
GetFirstSubsumedFrame(JSContext* cx, HandleSavedFrame frame, JS::SavedFrameSelfHosted selfHosted,
|
||||
bool& skippedAsync)
|
||||
{
|
||||
skippedAsync = false;
|
||||
|
||||
RootedSavedFrame rootedFrame(cx, frame);
|
||||
while (rootedFrame && !SavedFrameSubsumedByCaller(cx, rootedFrame)) {
|
||||
while (rootedFrame) {
|
||||
if ((selfHosted == JS::SavedFrameSelfHosted::Include || !rootedFrame->isSelfHosted()) &&
|
||||
SavedFrameSubsumedByCaller(cx, rootedFrame))
|
||||
{
|
||||
return rootedFrame;
|
||||
}
|
||||
|
||||
if (rootedFrame->getAsyncCause())
|
||||
skippedAsync = true;
|
||||
|
||||
rootedFrame = rootedFrame->getParent();
|
||||
}
|
||||
|
||||
return rootedFrame;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
JS_FRIEND_API(JSObject*)
|
||||
GetFirstSubsumedSavedFrame(JSContext* cx, HandleObject savedFrame)
|
||||
GetFirstSubsumedSavedFrame(JSContext* cx, HandleObject savedFrame,
|
||||
JS::SavedFrameSelfHosted selfHosted)
|
||||
{
|
||||
if (!savedFrame)
|
||||
return nullptr;
|
||||
bool skippedAsync;
|
||||
RootedSavedFrame frame(cx, &savedFrame->as<SavedFrame>());
|
||||
return GetFirstSubsumedFrame(cx, frame, skippedAsync);
|
||||
return GetFirstSubsumedFrame(cx, frame, selfHosted, skippedAsync);
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
|
@ -659,23 +668,28 @@ public:
|
|||
} // namespace
|
||||
|
||||
static inline js::SavedFrame*
|
||||
UnwrapSavedFrame(JSContext* cx, HandleObject obj, bool& skippedAsync)
|
||||
UnwrapSavedFrame(JSContext* cx, HandleObject obj, SavedFrameSelfHosted selfHosted,
|
||||
bool& skippedAsync)
|
||||
{
|
||||
if (!obj)
|
||||
return nullptr;
|
||||
|
||||
RootedObject savedFrameObj(cx, CheckedUnwrap(obj));
|
||||
MOZ_ASSERT(savedFrameObj);
|
||||
if (!savedFrameObj)
|
||||
return nullptr;
|
||||
|
||||
MOZ_ASSERT(js::SavedFrame::isSavedFrameAndNotProto(*savedFrameObj));
|
||||
js::RootedSavedFrame frame(cx, &savedFrameObj->as<js::SavedFrame>());
|
||||
return GetFirstSubsumedFrame(cx, frame, skippedAsync);
|
||||
return GetFirstSubsumedFrame(cx, frame, selfHosted, skippedAsync);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(SavedFrameResult)
|
||||
GetSavedFrameSource(JSContext* cx, HandleObject savedFrame, MutableHandleString sourcep)
|
||||
GetSavedFrameSource(JSContext* cx, HandleObject savedFrame, MutableHandleString sourcep,
|
||||
SavedFrameSelfHosted selfHosted /* = SavedFrameSelfHosted::Include */)
|
||||
{
|
||||
AutoMaybeEnterFrameCompartment ac(cx, savedFrame);
|
||||
bool skippedAsync;
|
||||
js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame, skippedAsync));
|
||||
js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame, selfHosted, skippedAsync));
|
||||
if (!frame) {
|
||||
sourcep.set(cx->runtime()->emptyString);
|
||||
return SavedFrameResult::AccessDenied;
|
||||
|
@ -685,12 +699,13 @@ GetSavedFrameSource(JSContext* cx, HandleObject savedFrame, MutableHandleString
|
|||
}
|
||||
|
||||
JS_PUBLIC_API(SavedFrameResult)
|
||||
GetSavedFrameLine(JSContext* cx, HandleObject savedFrame, uint32_t* linep)
|
||||
GetSavedFrameLine(JSContext* cx, HandleObject savedFrame, uint32_t* linep,
|
||||
SavedFrameSelfHosted selfHosted /* = SavedFrameSelfHosted::Include */)
|
||||
{
|
||||
MOZ_ASSERT(linep);
|
||||
AutoMaybeEnterFrameCompartment ac(cx, savedFrame);
|
||||
bool skippedAsync;
|
||||
js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame, skippedAsync));
|
||||
js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame, selfHosted, skippedAsync));
|
||||
if (!frame) {
|
||||
*linep = 0;
|
||||
return SavedFrameResult::AccessDenied;
|
||||
|
@ -700,12 +715,13 @@ GetSavedFrameLine(JSContext* cx, HandleObject savedFrame, uint32_t* linep)
|
|||
}
|
||||
|
||||
JS_PUBLIC_API(SavedFrameResult)
|
||||
GetSavedFrameColumn(JSContext* cx, HandleObject savedFrame, uint32_t* columnp)
|
||||
GetSavedFrameColumn(JSContext* cx, HandleObject savedFrame, uint32_t* columnp,
|
||||
SavedFrameSelfHosted selfHosted /* = SavedFrameSelfHosted::Include */)
|
||||
{
|
||||
MOZ_ASSERT(columnp);
|
||||
AutoMaybeEnterFrameCompartment ac(cx, savedFrame);
|
||||
bool skippedAsync;
|
||||
js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame, skippedAsync));
|
||||
js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame, selfHosted, skippedAsync));
|
||||
if (!frame) {
|
||||
*columnp = 0;
|
||||
return SavedFrameResult::AccessDenied;
|
||||
|
@ -715,11 +731,12 @@ GetSavedFrameColumn(JSContext* cx, HandleObject savedFrame, uint32_t* columnp)
|
|||
}
|
||||
|
||||
JS_PUBLIC_API(SavedFrameResult)
|
||||
GetSavedFrameFunctionDisplayName(JSContext* cx, HandleObject savedFrame, MutableHandleString namep)
|
||||
GetSavedFrameFunctionDisplayName(JSContext* cx, HandleObject savedFrame, MutableHandleString namep,
|
||||
SavedFrameSelfHosted selfHosted /* = SavedFrameSelfHosted::Include */)
|
||||
{
|
||||
AutoMaybeEnterFrameCompartment ac(cx, savedFrame);
|
||||
bool skippedAsync;
|
||||
js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame, skippedAsync));
|
||||
js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame, selfHosted, skippedAsync));
|
||||
if (!frame) {
|
||||
namep.set(nullptr);
|
||||
return SavedFrameResult::AccessDenied;
|
||||
|
@ -729,11 +746,12 @@ GetSavedFrameFunctionDisplayName(JSContext* cx, HandleObject savedFrame, Mutable
|
|||
}
|
||||
|
||||
JS_PUBLIC_API(SavedFrameResult)
|
||||
GetSavedFrameAsyncCause(JSContext* cx, HandleObject savedFrame, MutableHandleString asyncCausep)
|
||||
GetSavedFrameAsyncCause(JSContext* cx, HandleObject savedFrame, MutableHandleString asyncCausep,
|
||||
SavedFrameSelfHosted selfHosted /* = SavedFrameSelfHosted::Include */)
|
||||
{
|
||||
AutoMaybeEnterFrameCompartment ac(cx, savedFrame);
|
||||
bool skippedAsync;
|
||||
js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame, skippedAsync));
|
||||
js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame, selfHosted, skippedAsync));
|
||||
if (!frame) {
|
||||
asyncCausep.set(nullptr);
|
||||
return SavedFrameResult::AccessDenied;
|
||||
|
@ -745,11 +763,12 @@ GetSavedFrameAsyncCause(JSContext* cx, HandleObject savedFrame, MutableHandleStr
|
|||
}
|
||||
|
||||
JS_PUBLIC_API(SavedFrameResult)
|
||||
GetSavedFrameAsyncParent(JSContext* cx, HandleObject savedFrame, MutableHandleObject asyncParentp)
|
||||
GetSavedFrameAsyncParent(JSContext* cx, HandleObject savedFrame, MutableHandleObject asyncParentp,
|
||||
SavedFrameSelfHosted selfHosted /* = SavedFrameSelfHosted::Include */)
|
||||
{
|
||||
AutoMaybeEnterFrameCompartment ac(cx, savedFrame);
|
||||
bool skippedAsync;
|
||||
js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame, skippedAsync));
|
||||
js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame, selfHosted, skippedAsync));
|
||||
if (!frame) {
|
||||
asyncParentp.set(nullptr);
|
||||
return SavedFrameResult::AccessDenied;
|
||||
|
@ -759,7 +778,8 @@ GetSavedFrameAsyncParent(JSContext* cx, HandleObject savedFrame, MutableHandleOb
|
|||
// The current value of |skippedAsync| is not interesting, because we are
|
||||
// interested in whether we would cross any async parents to get from here
|
||||
// to the first subsumed parent frame instead.
|
||||
js::RootedSavedFrame subsumedParent(cx, GetFirstSubsumedFrame(cx, parent, skippedAsync));
|
||||
js::RootedSavedFrame subsumedParent(cx, GetFirstSubsumedFrame(cx, parent, selfHosted,
|
||||
skippedAsync));
|
||||
|
||||
// Even if |parent| is not subsumed, we still want to return a pointer to it
|
||||
// rather than |subsumedParent| so it can pick up any |asyncCause| from the
|
||||
|
@ -772,11 +792,12 @@ GetSavedFrameAsyncParent(JSContext* cx, HandleObject savedFrame, MutableHandleOb
|
|||
}
|
||||
|
||||
JS_PUBLIC_API(SavedFrameResult)
|
||||
GetSavedFrameParent(JSContext* cx, HandleObject savedFrame, MutableHandleObject parentp)
|
||||
GetSavedFrameParent(JSContext* cx, HandleObject savedFrame, MutableHandleObject parentp,
|
||||
SavedFrameSelfHosted selfHosted /* = SavedFrameSelfHosted::Include */)
|
||||
{
|
||||
AutoMaybeEnterFrameCompartment ac(cx, savedFrame);
|
||||
bool skippedAsync;
|
||||
js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame, skippedAsync));
|
||||
js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame, selfHosted, skippedAsync));
|
||||
if (!frame) {
|
||||
parentp.set(nullptr);
|
||||
return SavedFrameResult::AccessDenied;
|
||||
|
@ -786,7 +807,8 @@ GetSavedFrameParent(JSContext* cx, HandleObject savedFrame, MutableHandleObject
|
|||
// The current value of |skippedAsync| is not interesting, because we are
|
||||
// interested in whether we would cross any async parents to get from here
|
||||
// to the first subsumed parent frame instead.
|
||||
js::RootedSavedFrame subsumedParent(cx, GetFirstSubsumedFrame(cx, parent, skippedAsync));
|
||||
js::RootedSavedFrame subsumedParent(cx, GetFirstSubsumedFrame(cx, parent, selfHosted,
|
||||
skippedAsync));
|
||||
|
||||
// Even if |parent| is not subsumed, we still want to return a pointer to it
|
||||
// rather than |subsumedParent| so it can pick up any |asyncCause| from the
|
||||
|
@ -810,7 +832,8 @@ BuildStackString(JSContext* cx, HandleObject stack, MutableHandleString stringp,
|
|||
{
|
||||
AutoMaybeEnterFrameCompartment ac(cx, stack);
|
||||
bool skippedAsync;
|
||||
js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, stack, skippedAsync));
|
||||
js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, stack, SavedFrameSelfHosted::Exclude,
|
||||
skippedAsync));
|
||||
if (!frame) {
|
||||
stringp.set(cx->runtime()->emptyString);
|
||||
return true;
|
||||
|
@ -819,30 +842,29 @@ BuildStackString(JSContext* cx, HandleObject stack, MutableHandleString stringp,
|
|||
js::RootedSavedFrame parent(cx);
|
||||
do {
|
||||
MOZ_ASSERT(SavedFrameSubsumedByCaller(cx, frame));
|
||||
MOZ_ASSERT(!frame->isSelfHosted());
|
||||
|
||||
if (!frame->isSelfHosted()) {
|
||||
RootedString asyncCause(cx, frame->getAsyncCause());
|
||||
if (!asyncCause && skippedAsync)
|
||||
asyncCause.set(cx->names().Async);
|
||||
RootedString asyncCause(cx, frame->getAsyncCause());
|
||||
if (!asyncCause && skippedAsync)
|
||||
asyncCause.set(cx->names().Async);
|
||||
|
||||
js::RootedAtom name(cx, frame->getFunctionDisplayName());
|
||||
if ((indent && !sb.appendN(' ', indent))
|
||||
|| (asyncCause && (!sb.append(asyncCause) || !sb.append('*')))
|
||||
|| (name && !sb.append(name))
|
||||
|| !sb.append('@')
|
||||
|| !sb.append(frame->getSource())
|
||||
|| !sb.append(':')
|
||||
|| !NumberValueToStringBuffer(cx, NumberValue(frame->getLine()), sb)
|
||||
|| !sb.append(':')
|
||||
|| !NumberValueToStringBuffer(cx, NumberValue(frame->getColumn()), sb)
|
||||
|| !sb.append('\n'))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
js::RootedAtom name(cx, frame->getFunctionDisplayName());
|
||||
if ((indent && !sb.appendN(' ', indent))
|
||||
|| (asyncCause && (!sb.append(asyncCause) || !sb.append('*')))
|
||||
|| (name && !sb.append(name))
|
||||
|| !sb.append('@')
|
||||
|| !sb.append(frame->getSource())
|
||||
|| !sb.append(':')
|
||||
|| !NumberValueToStringBuffer(cx, NumberValue(frame->getLine()), sb)
|
||||
|| !sb.append(':')
|
||||
|| !NumberValueToStringBuffer(cx, NumberValue(frame->getColumn()), sb)
|
||||
|| !sb.append('\n'))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
parent = frame->getParent();
|
||||
frame = js::GetFirstSubsumedFrame(cx, parent, skippedAsync);
|
||||
frame = js::GetFirstSubsumedFrame(cx, parent, SavedFrameSelfHosted::Exclude, skippedAsync);
|
||||
} while (frame);
|
||||
}
|
||||
|
||||
|
|
|
@ -2264,7 +2264,7 @@ DebugScopes::sweep(JSRuntime* rt)
|
|||
* Thus, we must explicitly remove the entries from both liveScopes
|
||||
* and missingScopes here.
|
||||
*/
|
||||
liveScopes.remove(&e.front().value()->scope());
|
||||
liveScopes.remove(&e.front().value().unbarrieredGet()->scope());
|
||||
e.removeFront();
|
||||
} else {
|
||||
MissingScopeKey key = e.front().key();
|
||||
|
|
|
@ -1662,6 +1662,8 @@ static const JSFunctionSpec intrinsic_functions[] = {
|
|||
CallNonGenericSelfhostedMethod<Is<StarGeneratorObject>>, 2, 0),
|
||||
|
||||
JS_FN("IsWeakSet", intrinsic_IsWeakSet, 1,0),
|
||||
JS_FN("CallWeakSetMethodIfWrapped",
|
||||
CallNonGenericSelfhostedMethod<Is<WeakSetObject>>, 2, 0),
|
||||
|
||||
// See builtin/TypedObject.h for descriptors of the typedobj functions.
|
||||
JS_FN("NewOpaqueTypedObject", js::NewOpaqueTypedObject, 1, 0),
|
||||
|
|
|
@ -1205,9 +1205,10 @@ StackBaseShape::hash(const Lookup& lookup)
|
|||
}
|
||||
|
||||
/* static */ inline bool
|
||||
StackBaseShape::match(UnownedBaseShape* key, const Lookup& lookup)
|
||||
StackBaseShape::match(ReadBarriered<UnownedBaseShape*> key, const Lookup& lookup)
|
||||
{
|
||||
return key->flags == lookup.flags && key->clasp_ == lookup.clasp;
|
||||
return key.unbarrieredGet()->flags == lookup.flags &&
|
||||
key.unbarrieredGet()->clasp_ == lookup.clasp;
|
||||
}
|
||||
|
||||
inline
|
||||
|
@ -1451,7 +1452,7 @@ JSCompartment::checkInitialShapesTableAfterMovingGC()
|
|||
for (InitialShapeSet::Enum e(initialShapes); !e.empty(); e.popFront()) {
|
||||
InitialShapeEntry entry = e.front();
|
||||
TaggedProto proto = entry.proto;
|
||||
Shape* shape = entry.shape.get();
|
||||
Shape* shape = entry.shape.unbarrieredGet();
|
||||
|
||||
if (proto.isObject())
|
||||
CheckGCThingAfterMovingGC(proto.toObject());
|
||||
|
@ -1631,8 +1632,8 @@ JSCompartment::fixupInitialShapeTable()
|
|||
for (InitialShapeSet::Enum e(initialShapes); !e.empty(); e.popFront()) {
|
||||
InitialShapeEntry entry = e.front();
|
||||
bool needRekey = false;
|
||||
if (IsForwarded(entry.shape.get())) {
|
||||
entry.shape.set(Forwarded(entry.shape.get()));
|
||||
if (IsForwarded(entry.shape.unbarrieredGet())) {
|
||||
entry.shape.set(Forwarded(entry.shape.unbarrieredGet()));
|
||||
needRekey = true;
|
||||
}
|
||||
if (entry.proto.isObject() && IsForwarded(entry.proto.toObject())) {
|
||||
|
@ -1640,10 +1641,10 @@ JSCompartment::fixupInitialShapeTable()
|
|||
needRekey = true;
|
||||
}
|
||||
if (needRekey) {
|
||||
InitialShapeEntry::Lookup relookup(entry.shape->getObjectClass(),
|
||||
InitialShapeEntry::Lookup relookup(entry.shape.unbarrieredGet()->getObjectClass(),
|
||||
entry.proto,
|
||||
entry.shape->numFixedSlots(),
|
||||
entry.shape->getObjectFlags());
|
||||
entry.shape.unbarrieredGet()->numFixedSlots(),
|
||||
entry.shape.unbarrieredGet()->getObjectFlags());
|
||||
e.rekeyFront(relookup, entry);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -481,7 +481,7 @@ BaseShape::baseUnowned()
|
|||
}
|
||||
|
||||
/* Entries for the per-compartment baseShapes set of unowned base shapes. */
|
||||
struct StackBaseShape : public DefaultHasher<ReadBarrieredUnownedBaseShape>
|
||||
struct StackBaseShape : public DefaultHasher<ReadBarriered<UnownedBaseShape*>>
|
||||
{
|
||||
uint32_t flags;
|
||||
const Class* clasp;
|
||||
|
@ -513,10 +513,10 @@ struct StackBaseShape : public DefaultHasher<ReadBarrieredUnownedBaseShape>
|
|||
};
|
||||
|
||||
static inline HashNumber hash(const Lookup& lookup);
|
||||
static inline bool match(UnownedBaseShape* key, const Lookup& lookup);
|
||||
static inline bool match(ReadBarriered<UnownedBaseShape*> key, const Lookup& lookup);
|
||||
};
|
||||
|
||||
typedef HashSet<ReadBarrieredUnownedBaseShape,
|
||||
typedef HashSet<ReadBarriered<UnownedBaseShape*>,
|
||||
StackBaseShape,
|
||||
SystemAllocPolicy> BaseShapeSet;
|
||||
|
||||
|
|
|
@ -109,11 +109,11 @@ void
|
|||
SymbolRegistry::sweep()
|
||||
{
|
||||
for (Enum e(*this); !e.empty(); e.popFront()) {
|
||||
Symbol* sym = e.front();
|
||||
if (IsAboutToBeFinalizedUnbarriered(&sym))
|
||||
mozilla::DebugOnly<Symbol*> sym = e.front().unbarrieredGet();
|
||||
if (IsAboutToBeFinalized(&e.mutableFront()))
|
||||
e.removeFront();
|
||||
else
|
||||
MOZ_ASSERT(sym == e.front());
|
||||
MOZ_ASSERT(sym == e.front().unbarrieredGet());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3381,7 +3381,7 @@ PreliminaryObjectArray::sweep()
|
|||
// the compartment's global is dead, we don't do anything as the
|
||||
// group's Class is not going to change in that case.
|
||||
JSObject* obj = *ptr;
|
||||
GlobalObject* global = obj->compartment()->maybeGlobal();
|
||||
GlobalObject* global = obj->compartment()->unsafeUnbarrieredMaybeGlobal();
|
||||
if (global && !obj->isSingleton()) {
|
||||
JSObject* objectProto = GetBuiltinPrototypePure(global, JSProto_Object);
|
||||
obj->setGroup(objectProto->groupRaw());
|
||||
|
@ -4074,7 +4074,8 @@ ConstraintTypeSet::sweep(Zone* zone, AutoClearTypeInferenceStateOnOOM& oom)
|
|||
objectCount = 0;
|
||||
break;
|
||||
}
|
||||
} else if (key->isGroup() && key->group()->unknownPropertiesDontCheckGeneration()) {
|
||||
} else if (key->isGroup() &&
|
||||
key->groupNoBarrier()->unknownPropertiesDontCheckGeneration()) {
|
||||
// Object sets containing objects with unknown properties might
|
||||
// not be complete. Mark the type set as unknown, which it will
|
||||
// be treated as during Ion compilation.
|
||||
|
@ -4098,7 +4099,7 @@ ConstraintTypeSet::sweep(Zone* zone, AutoClearTypeInferenceStateOnOOM& oom)
|
|||
} else {
|
||||
// As above, mark type sets containing objects with unknown
|
||||
// properties as unknown.
|
||||
if (key->isGroup() && key->group()->unknownPropertiesDontCheckGeneration())
|
||||
if (key->isGroup() && key->groupNoBarrier()->unknownPropertiesDontCheckGeneration())
|
||||
flags |= TYPE_FLAG_ANYOBJECT;
|
||||
objectSet = nullptr;
|
||||
setBaseObjectCount(0);
|
||||
|
|
|
@ -7705,26 +7705,12 @@ nsRuleNode::ComputePositionData(void* aStartStruct,
|
|||
// block-axis properties, we turn them into unset if we find them in
|
||||
// that case.
|
||||
|
||||
bool vertical;
|
||||
switch (aContext->StyleVisibility()->mWritingMode) {
|
||||
default:
|
||||
MOZ_ASSERT(false, "unexpected writing-mode value");
|
||||
// fall through
|
||||
case NS_STYLE_WRITING_MODE_HORIZONTAL_TB:
|
||||
vertical = false;
|
||||
break;
|
||||
case NS_STYLE_WRITING_MODE_VERTICAL_RL:
|
||||
case NS_STYLE_WRITING_MODE_VERTICAL_LR:
|
||||
case NS_STYLE_WRITING_MODE_SIDEWAYS_RL:
|
||||
case NS_STYLE_WRITING_MODE_SIDEWAYS_LR:
|
||||
vertical = true;
|
||||
break;
|
||||
}
|
||||
uint8_t wm = WritingMode(aContext).GetBits();
|
||||
WritingMode wm(aContext);
|
||||
bool vertical = wm.IsVertical();
|
||||
|
||||
const nsCSSValue* width = aRuleData->ValueForWidth();
|
||||
if (width->GetUnit() == eCSSUnit_Enumerated) {
|
||||
conditions.SetWritingModeDependency(wm);
|
||||
conditions.SetWritingModeDependency(wm.GetBits());
|
||||
}
|
||||
SetCoord(width->GetUnit() == eCSSUnit_Enumerated && vertical ?
|
||||
nsCSSValue(eCSSUnit_Unset) : *width,
|
||||
|
@ -7735,7 +7721,7 @@ nsRuleNode::ComputePositionData(void* aStartStruct,
|
|||
|
||||
const nsCSSValue* minWidth = aRuleData->ValueForMinWidth();
|
||||
if (minWidth->GetUnit() == eCSSUnit_Enumerated) {
|
||||
conditions.SetWritingModeDependency(wm);
|
||||
conditions.SetWritingModeDependency(wm.GetBits());
|
||||
}
|
||||
SetCoord(minWidth->GetUnit() == eCSSUnit_Enumerated && vertical ?
|
||||
nsCSSValue(eCSSUnit_Unset) : *minWidth,
|
||||
|
@ -7746,7 +7732,7 @@ nsRuleNode::ComputePositionData(void* aStartStruct,
|
|||
|
||||
const nsCSSValue* maxWidth = aRuleData->ValueForMaxWidth();
|
||||
if (maxWidth->GetUnit() == eCSSUnit_Enumerated) {
|
||||
conditions.SetWritingModeDependency(wm);
|
||||
conditions.SetWritingModeDependency(wm.GetBits());
|
||||
}
|
||||
SetCoord(maxWidth->GetUnit() == eCSSUnit_Enumerated && vertical ?
|
||||
nsCSSValue(eCSSUnit_Unset) : *maxWidth,
|
||||
|
@ -7757,7 +7743,7 @@ nsRuleNode::ComputePositionData(void* aStartStruct,
|
|||
|
||||
const nsCSSValue* height = aRuleData->ValueForHeight();
|
||||
if (height->GetUnit() == eCSSUnit_Enumerated) {
|
||||
conditions.SetWritingModeDependency(wm);
|
||||
conditions.SetWritingModeDependency(wm.GetBits());
|
||||
}
|
||||
SetCoord(height->GetUnit() == eCSSUnit_Enumerated && !vertical ?
|
||||
nsCSSValue(eCSSUnit_Unset) : *height,
|
||||
|
@ -7768,7 +7754,7 @@ nsRuleNode::ComputePositionData(void* aStartStruct,
|
|||
|
||||
const nsCSSValue* minHeight = aRuleData->ValueForMinHeight();
|
||||
if (minHeight->GetUnit() == eCSSUnit_Enumerated) {
|
||||
conditions.SetWritingModeDependency(wm);
|
||||
conditions.SetWritingModeDependency(wm.GetBits());
|
||||
}
|
||||
SetCoord(minHeight->GetUnit() == eCSSUnit_Enumerated && !vertical ?
|
||||
nsCSSValue(eCSSUnit_Unset) : *minHeight,
|
||||
|
@ -7779,7 +7765,7 @@ nsRuleNode::ComputePositionData(void* aStartStruct,
|
|||
|
||||
const nsCSSValue* maxHeight = aRuleData->ValueForMaxHeight();
|
||||
if (maxHeight->GetUnit() == eCSSUnit_Enumerated) {
|
||||
conditions.SetWritingModeDependency(wm);
|
||||
conditions.SetWritingModeDependency(wm.GetBits());
|
||||
}
|
||||
SetCoord(maxHeight->GetUnit() == eCSSUnit_Enumerated && !vertical ?
|
||||
nsCSSValue(eCSSUnit_Unset) : *maxHeight,
|
||||
|
|
|
@ -4,10 +4,27 @@
|
|||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
use std::fmt;
|
||||
|
||||
/// Expose C api wrapper.
|
||||
pub mod capi;
|
||||
// FIXME: We can 'pub use capi::*' in rustc 1.5 and later.
|
||||
pub use capi::{mp4parse_new, mp4parse_free, mp4parse_read};
|
||||
|
||||
#[derive(Clone, Copy, Eq, PartialEq)]
|
||||
pub struct FourCC(pub u32);
|
||||
|
||||
impl fmt::Debug for FourCC {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "'{}'", fourcc_to_string(*self))
|
||||
}
|
||||
}
|
||||
|
||||
/// Basic ISO box structure.
|
||||
#[derive(Debug)]
|
||||
pub struct BoxHeader {
|
||||
/// Four character box type
|
||||
pub name: u32,
|
||||
pub name: FourCC,
|
||||
/// Size of the box in bytes
|
||||
pub size: u64,
|
||||
/// Offset to the start of the contained data (or header size).
|
||||
|
@ -15,17 +32,19 @@ pub struct BoxHeader {
|
|||
}
|
||||
|
||||
/// File type box 'ftyp'.
|
||||
#[derive(Debug)]
|
||||
pub struct FileTypeBox {
|
||||
name: u32,
|
||||
name: FourCC,
|
||||
size: u64,
|
||||
major_brand: u32,
|
||||
major_brand: FourCC,
|
||||
minor_version: u32,
|
||||
compatible_brands: Vec<u32>,
|
||||
compatible_brands: Vec<FourCC>,
|
||||
}
|
||||
|
||||
/// Movie header box 'mvhd'.
|
||||
#[derive(Debug)]
|
||||
pub struct MovieHeaderBox {
|
||||
pub name: u32,
|
||||
pub name: FourCC,
|
||||
pub size: u64,
|
||||
pub timescale: u32,
|
||||
pub duration: u64,
|
||||
|
@ -33,8 +52,9 @@ pub struct MovieHeaderBox {
|
|||
}
|
||||
|
||||
/// Track header box 'tkhd'
|
||||
#[derive(Debug)]
|
||||
pub struct TrackHeaderBox {
|
||||
pub name: u32,
|
||||
pub name: FourCC,
|
||||
pub size: u64,
|
||||
pub track_id: u32,
|
||||
pub enabled: bool,
|
||||
|
@ -44,12 +64,14 @@ pub struct TrackHeaderBox {
|
|||
}
|
||||
|
||||
/// Edit list box 'elst'
|
||||
#[derive(Debug)]
|
||||
pub struct EditListBox {
|
||||
name: u32,
|
||||
name: FourCC,
|
||||
size: u64,
|
||||
edits: Vec<Edit>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Edit {
|
||||
segment_duration: u64,
|
||||
media_time: i64,
|
||||
|
@ -58,34 +80,39 @@ pub struct Edit {
|
|||
}
|
||||
|
||||
/// Media header box 'mdhd'
|
||||
#[derive(Debug)]
|
||||
pub struct MediaHeaderBox {
|
||||
name: u32,
|
||||
name: FourCC,
|
||||
size: u64,
|
||||
timescale: u32,
|
||||
duration: u64,
|
||||
}
|
||||
|
||||
// Chunk offset box 'stco' or 'co64'
|
||||
#[derive(Debug)]
|
||||
pub struct ChunkOffsetBox {
|
||||
name: u32,
|
||||
name: FourCC,
|
||||
size: u64,
|
||||
offsets: Vec<u64>,
|
||||
}
|
||||
|
||||
// Sync sample box 'stss'
|
||||
#[derive(Debug)]
|
||||
pub struct SyncSampleBox {
|
||||
name: u32,
|
||||
name: FourCC,
|
||||
size: u64,
|
||||
samples: Vec<u32>,
|
||||
}
|
||||
|
||||
// Sample to chunk box 'stsc'
|
||||
#[derive(Debug)]
|
||||
pub struct SampleToChunkBox {
|
||||
name: u32,
|
||||
name: FourCC,
|
||||
size: u64,
|
||||
samples: Vec<SampleToChunk>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct SampleToChunk {
|
||||
first_chunk: u32,
|
||||
samples_per_chunk: u32,
|
||||
|
@ -93,40 +120,46 @@ pub struct SampleToChunk {
|
|||
}
|
||||
|
||||
// Sample size box 'stsz'
|
||||
#[derive(Debug)]
|
||||
pub struct SampleSizeBox {
|
||||
name: u32,
|
||||
name: FourCC,
|
||||
size: u64,
|
||||
sample_size: u32,
|
||||
sample_sizes: Vec<u32>,
|
||||
}
|
||||
|
||||
// Time to sample box 'stts'
|
||||
#[derive(Debug)]
|
||||
pub struct TimeToSampleBox {
|
||||
name: u32,
|
||||
name: FourCC,
|
||||
size: u64,
|
||||
samples: Vec<Sample>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Sample {
|
||||
sample_count: u32,
|
||||
sample_delta: u32,
|
||||
}
|
||||
|
||||
// Handler reference box 'hdlr'
|
||||
#[derive(Debug)]
|
||||
pub struct HandlerBox {
|
||||
name: u32,
|
||||
name: FourCC,
|
||||
size: u64,
|
||||
handler_type: u32,
|
||||
handler_type: FourCC,
|
||||
}
|
||||
|
||||
// Sample description box 'stsd'
|
||||
#[derive(Debug)]
|
||||
pub struct SampleDescriptionBox {
|
||||
name: u32,
|
||||
name: FourCC,
|
||||
size: u64,
|
||||
descriptions: Vec<SampleEntry>,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug)]
|
||||
enum SampleEntry {
|
||||
Audio {
|
||||
data_reference_index: u16,
|
||||
|
@ -144,26 +177,39 @@ enum SampleEntry {
|
|||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug)]
|
||||
pub struct AVCDecoderConfigurationRecord {
|
||||
data: Vec<u8>,
|
||||
}
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug)]
|
||||
pub struct ES_Descriptor {
|
||||
data: Vec<u8>,
|
||||
}
|
||||
|
||||
/// Internal data structures.
|
||||
#[derive(Debug)]
|
||||
pub struct MediaContext {
|
||||
pub tracks: Vec<Track>,
|
||||
tracks: Vec<Track>,
|
||||
}
|
||||
|
||||
impl MediaContext {
|
||||
pub fn new() -> MediaContext {
|
||||
MediaContext {
|
||||
tracks: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum TrackType {
|
||||
Video,
|
||||
Audio
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Track {
|
||||
track_type: TrackType,
|
||||
}
|
||||
|
@ -177,7 +223,7 @@ use std::cmp;
|
|||
/// Parse a box out of a data buffer.
|
||||
pub fn read_box_header<T: ReadBytesExt>(src: &mut T) -> byteorder::Result<BoxHeader> {
|
||||
let size32 = try!(be_u32(src));
|
||||
let name = try!(be_u32(src));
|
||||
let name = FourCC(try!(be_u32(src)));
|
||||
let size = match size32 {
|
||||
0 => panic!("unknown box size not implemented"),
|
||||
1 => {
|
||||
|
@ -232,7 +278,7 @@ fn limit<'a, T: Read>(f: &'a mut T, h: &BoxHeader) -> Take<&'a mut T> {
|
|||
/// Helper to construct a Cursor over the contents of a box.
|
||||
fn recurse<T: Read>(f: &mut T, h: &BoxHeader, context: &mut MediaContext) -> byteorder::Result<()> {
|
||||
use std::error::Error;
|
||||
println!("{} -- recursing", h);
|
||||
println!("{:?} -- recursing", h);
|
||||
// FIXME: I couldn't figure out how to do this without copying.
|
||||
// We use Seek on the Read we return in skip_box_content, but
|
||||
// that trait isn't implemented for a Take like our limit()
|
||||
|
@ -254,14 +300,14 @@ fn recurse<T: Read>(f: &mut T, h: &BoxHeader, context: &mut MediaContext) -> byt
|
|||
break;
|
||||
},
|
||||
Err(byteorder::Error::Io(e)) => {
|
||||
println!("I/O Error '{:?}' reading box: {}",
|
||||
println!("I/O Error '{:?}' reading box: {:?}",
|
||||
e.kind(), e.description());
|
||||
return Err(byteorder::Error::Io(e));
|
||||
},
|
||||
}
|
||||
}
|
||||
assert!(content.position() == h.size - h.offset);
|
||||
println!("{} -- end", h);
|
||||
println!("{:?} -- end", h);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -273,53 +319,53 @@ pub fn read_box<T: BufRead>(f: &mut T, context: &mut MediaContext) -> byteorder:
|
|||
match &fourcc_to_string(h.name)[..] {
|
||||
"ftyp" => {
|
||||
let ftyp = try!(read_ftyp(&mut content, &h));
|
||||
println!("{}", ftyp);
|
||||
println!("{:?}", ftyp);
|
||||
},
|
||||
"moov" => try!(recurse(&mut content, &h, context)),
|
||||
"mvhd" => {
|
||||
let mvhd = try!(read_mvhd(&mut content, &h));
|
||||
println!(" {}", mvhd);
|
||||
println!(" {:?}", mvhd);
|
||||
},
|
||||
"trak" => try!(recurse(&mut content, &h, context)),
|
||||
"tkhd" => {
|
||||
let tkhd = try!(read_tkhd(&mut content, &h));
|
||||
println!(" {}", tkhd);
|
||||
println!(" {:?}", tkhd);
|
||||
},
|
||||
"edts" => try!(recurse(&mut content, &h, context)),
|
||||
"elst" => {
|
||||
let elst = try!(read_elst(&mut content, &h));
|
||||
println!(" {}", elst);
|
||||
println!(" {:?}", elst);
|
||||
},
|
||||
"mdia" => try!(recurse(&mut content, &h, context)),
|
||||
"mdhd" => {
|
||||
let mdhd = try!(read_mdhd(&mut content, &h));
|
||||
println!(" {}", mdhd);
|
||||
println!(" {:?}", mdhd);
|
||||
},
|
||||
"minf" => try!(recurse(&mut content, &h, context)),
|
||||
"stbl" => try!(recurse(&mut content, &h, context)),
|
||||
"stco" => {
|
||||
let stco = try!(read_stco(&mut content, &h));
|
||||
println!(" {}", stco);
|
||||
println!(" {:?}", stco);
|
||||
},
|
||||
"co64" => {
|
||||
let co64 = try!(read_co64(&mut content, &h));
|
||||
println!(" {}", co64);
|
||||
println!(" {:?}", co64);
|
||||
},
|
||||
"stss" => {
|
||||
let stss = try!(read_stss(&mut content, &h));
|
||||
println!(" {}", stss);
|
||||
println!(" {:?}", stss);
|
||||
},
|
||||
"stsc" => {
|
||||
let stsc = try!(read_stsc(&mut content, &h));
|
||||
println!(" {}", stsc);
|
||||
println!(" {:?}", stsc);
|
||||
},
|
||||
"stsz" => {
|
||||
let stsz = try!(read_stsz(&mut content, &h));
|
||||
println!(" {}", stsz);
|
||||
println!(" {:?}", stsz);
|
||||
},
|
||||
"stts" => {
|
||||
let stts = try!(read_stts(&mut content, &h));
|
||||
println!(" {}", stts);
|
||||
println!(" {:?}", stts);
|
||||
},
|
||||
"hdlr" => {
|
||||
let hdlr = try!(read_hdlr(&mut content, &h));
|
||||
|
@ -334,69 +380,33 @@ pub fn read_box<T: BufRead>(f: &mut T, context: &mut MediaContext) -> byteorder:
|
|||
context.tracks.push(Track { track_type: track_type }),
|
||||
None => println!("unknown track type!"),
|
||||
};
|
||||
println!(" {:?}", hdlr);
|
||||
},
|
||||
"stsd" => {
|
||||
let track = &context.tracks[context.tracks.len() - 1];
|
||||
let stsd = try!(read_stsd(&mut content, &h, &track));
|
||||
println!(" {}", stsd);
|
||||
println!(" {:?}", stsd);
|
||||
},
|
||||
_ => {
|
||||
// Skip the contents of unknown chunks.
|
||||
println!("{} (skipped)", h);
|
||||
println!("{:?} (skipped)", h);
|
||||
try!(skip_box_content(&mut content, &h));
|
||||
},
|
||||
};
|
||||
assert!(content.limit() == 0);
|
||||
println!("read_box context: {}", context);
|
||||
println!("read_box context: {:?}", context);
|
||||
Ok(()) // and_then needs a Result to return.
|
||||
})
|
||||
}
|
||||
|
||||
/// Entry point for C language callers.
|
||||
/// Take a buffer and call read_box() on it,
|
||||
/// returning the number of detected tracks.
|
||||
#[no_mangle]
|
||||
pub extern fn read_box_from_buffer(buffer: *const u8, size: usize) -> i32 {
|
||||
use std::slice;
|
||||
use std::thread;
|
||||
use std::i32;
|
||||
|
||||
// Validate arguments from C.
|
||||
if buffer.is_null() || size < 8 {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Wrap the buffer we've been give in a slice.
|
||||
let b = unsafe { slice::from_raw_parts(buffer, size) };
|
||||
let mut c = Cursor::new(b);
|
||||
|
||||
// Parse in a subthread.
|
||||
let task = thread::spawn(move || {
|
||||
let mut context = MediaContext { tracks: Vec::new() };
|
||||
loop {
|
||||
match read_box(&mut c, &mut context) {
|
||||
Ok(_) => {},
|
||||
Err(byteorder::Error::UnexpectedEOF) => { break },
|
||||
Err(e) => { panic!(e) },
|
||||
}
|
||||
}
|
||||
// Make sure the track count fits in an i32 so we can use
|
||||
// negative values for failure.
|
||||
assert!(context.tracks.len() < i32::MAX as usize);
|
||||
context.tracks.len() as i32
|
||||
});
|
||||
// Catch any panics.
|
||||
task.join().unwrap_or(-1)
|
||||
}
|
||||
|
||||
/// Parse an ftyp box.
|
||||
pub fn read_ftyp<T: ReadBytesExt>(src: &mut T, head: &BoxHeader) -> byteorder::Result<FileTypeBox> {
|
||||
let major = try!(be_u32(src));
|
||||
let major = FourCC(try!(be_u32(src)));
|
||||
let minor = try!(be_u32(src));
|
||||
let brand_count = (head.size - 8 - 8) / 4;
|
||||
let mut brands = Vec::new();
|
||||
for _ in 0..brand_count {
|
||||
brands.push(try!(be_u32(src)));
|
||||
brands.push(FourCC(try!(be_u32(src))));
|
||||
}
|
||||
Ok(FileTypeBox{
|
||||
name: head.name,
|
||||
|
@ -655,7 +665,7 @@ pub fn read_hdlr<T: ReadBytesExt + BufRead>(src: &mut T, head: &BoxHeader) -> by
|
|||
// Skip uninteresting fields.
|
||||
try!(skip(src, 4));
|
||||
|
||||
let handler_type = try!(be_u32(src));
|
||||
let handler_type = FourCC(try!(be_u32(src)));
|
||||
|
||||
// Skip uninteresting fields.
|
||||
try!(skip(src, 12));
|
||||
|
@ -774,14 +784,14 @@ pub fn read_stsd<T: ReadBytesExt + BufRead>(src: &mut T, head: &BoxHeader, track
|
|||
}
|
||||
|
||||
/// Convert the iso box type or other 4-character value to a string.
|
||||
fn fourcc_to_string(name: u32) -> String {
|
||||
fn fourcc_to_string(name: FourCC) -> String {
|
||||
let u32_to_vec = |u| {
|
||||
vec!((u >> 24 & 0xffu32) as u8,
|
||||
(u >> 16 & 0xffu32) as u8,
|
||||
(u >> 8 & 0xffu32) as u8,
|
||||
(u & 0xffu32) as u8)
|
||||
};
|
||||
let name_bytes = u32_to_vec(name);
|
||||
let name_bytes = u32_to_vec(name.0);
|
||||
String::from_utf8_lossy(&name_bytes).into_owned()
|
||||
}
|
||||
|
||||
|
@ -828,146 +838,6 @@ fn be_u64<T: ReadBytesExt>(src: &mut T) -> byteorder::Result<u64> {
|
|||
src.read_u64::<BigEndian>()
|
||||
}
|
||||
|
||||
use std::fmt;
|
||||
impl fmt::Display for BoxHeader {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "'{}' {} bytes", fourcc_to_string(self.name), self.size)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for FileTypeBox {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let name = fourcc_to_string(self.name);
|
||||
let brand = fourcc_to_string(self.major_brand);
|
||||
let mut compat = String::from("compatible with");
|
||||
for brand in &self.compatible_brands {
|
||||
let brand_string = fourcc_to_string(*brand);
|
||||
compat.push(' ');
|
||||
compat.push_str(&brand_string);
|
||||
}
|
||||
write!(f, "'{}' {} bytes '{}' v{}\n {}",
|
||||
name, self.size, brand, self.minor_version, compat)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for MovieHeaderBox {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let name = fourcc_to_string(self.name);
|
||||
write!(f, "'{}' {} bytes duration {}s", name, self.size,
|
||||
(self.duration as f64)/(self.timescale as f64))
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for MediaContext {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "Found {} tracks.", self.tracks.len())
|
||||
}
|
||||
}
|
||||
|
||||
use std::u16;
|
||||
impl fmt::Display for TrackHeaderBox {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let name = fourcc_to_string(self.name);
|
||||
// Dimensions are 16.16 fixed-point.
|
||||
let base = u16::MAX as f64 + 1.0;
|
||||
let width = (self.width as f64) / base;
|
||||
let height = (self.height as f64) / base;
|
||||
let disabled = if self.enabled { "" } else { " (disabled)" };
|
||||
write!(f, "'{}' {} bytes duration {} id {} {}x{}{}",
|
||||
name, self.size, self.duration, self.track_id,
|
||||
width, height, disabled)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for EditListBox {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let mut entries = String::new();
|
||||
for entry in &self.edits {
|
||||
entries.push_str(&format!("\n duration {} time {} rate {}/{}",
|
||||
entry.segment_duration, entry.media_time,
|
||||
entry.media_rate_integer, entry.media_rate_fraction));
|
||||
}
|
||||
write!(f, "'{}' {} bytes {}", fourcc_to_string(self.name), self.size, entries)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for MediaHeaderBox {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "'{}' {} bytes timescale {} duration {}",
|
||||
fourcc_to_string(self.name), self.size, self.timescale, self.duration)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ChunkOffsetBox {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let mut entries = String::new();
|
||||
for entry in &self.offsets {
|
||||
entries.push_str(&format!("\n offset {}", entry));
|
||||
}
|
||||
write!(f, "'{}' {} bytes {}",
|
||||
fourcc_to_string(self.name), self.size, entries)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for SyncSampleBox {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let mut entries = String::new();
|
||||
for entry in &self.samples {
|
||||
entries.push_str(&format!("\n sample {}", entry));
|
||||
}
|
||||
write!(f, "'{}' {} bytes {}",
|
||||
fourcc_to_string(self.name), self.size, entries)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for SampleToChunkBox {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let mut entries = String::new();
|
||||
for entry in &self.samples {
|
||||
entries.push_str(&format!("\n sample chunk {} {} {}",
|
||||
entry.first_chunk, entry.samples_per_chunk, entry.sample_description_index));
|
||||
}
|
||||
write!(f, "'{}' {} bytes {}",
|
||||
fourcc_to_string(self.name), self.size, entries)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for SampleSizeBox {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let mut entries = String::new();
|
||||
for entry in &self.sample_sizes {
|
||||
entries.push_str(&format!("\n sample size {}", entry));
|
||||
}
|
||||
write!(f, "'{}' {} bytes sample size {} {}",
|
||||
fourcc_to_string(self.name), self.size, self.sample_size, entries)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for TimeToSampleBox {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let mut entries = String::new();
|
||||
for entry in &self.samples {
|
||||
entries.push_str(&format!("\n sample count {} delta {}", entry.sample_count, entry.sample_delta));
|
||||
}
|
||||
write!(f, "'{}' {} bytes sample {}",
|
||||
fourcc_to_string(self.name), self.size, entries)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for HandlerBox {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "'{}' {} bytes handler_type '{}'",
|
||||
fourcc_to_string(self.name), self.size, fourcc_to_string(self.handler_type))
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for SampleDescriptionBox {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "'{}' {} bytes descriptions {}",
|
||||
fourcc_to_string(self.name), self.size, self.descriptions.len())
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_read_box_header() {
|
||||
use std::io::Cursor;
|
||||
|
@ -976,9 +846,9 @@ fn test_read_box_header() {
|
|||
write!(&mut test, "test").unwrap(); // box type
|
||||
let mut stream = Cursor::new(test);
|
||||
let parsed = read_box_header(&mut stream).unwrap();
|
||||
assert_eq!(parsed.name, 1952805748);
|
||||
assert_eq!(parsed.name, FourCC(1952805748));
|
||||
assert_eq!(parsed.size, 8);
|
||||
println!("box {}", parsed);
|
||||
println!("box {:?}", parsed);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -990,9 +860,9 @@ fn test_read_box_header_long() {
|
|||
// Skip generating box content.
|
||||
let mut stream = Cursor::new(test);
|
||||
let parsed = read_box_header(&mut stream).unwrap();
|
||||
assert_eq!(parsed.name, 1819242087);
|
||||
assert_eq!(parsed.name, FourCC(1819242087));
|
||||
assert_eq!(parsed.size, 4096);
|
||||
println!("box {}", parsed);
|
||||
println!("box {:?}", parsed);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -1010,14 +880,14 @@ fn test_read_ftyp() {
|
|||
let mut stream = Cursor::new(test);
|
||||
let header = read_box_header(&mut stream).unwrap();
|
||||
let parsed = read_ftyp(&mut stream, &header).unwrap();
|
||||
assert_eq!(parsed.name, 1718909296);
|
||||
assert_eq!(parsed.name, FourCC(1718909296));
|
||||
assert_eq!(parsed.size, 24);
|
||||
assert_eq!(parsed.major_brand, 1836069938);
|
||||
assert_eq!(parsed.major_brand, FourCC(1836069938));
|
||||
assert_eq!(parsed.minor_version, 0);
|
||||
assert_eq!(parsed.compatible_brands.len(), 2);
|
||||
assert_eq!(parsed.compatible_brands[0], 1769172845);
|
||||
assert_eq!(parsed.compatible_brands[0], FourCC(1769172845));
|
||||
assert_eq!(fourcc_to_string(parsed.compatible_brands[1]), "mp42");
|
||||
println!("box {}", parsed);
|
||||
println!("box {:?}", parsed);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -1037,14 +907,14 @@ fn test_read_elst_v0() {
|
|||
let mut stream = Cursor::new(test);
|
||||
let header = read_box_header(&mut stream).unwrap();
|
||||
let parsed = read_elst(&mut stream, &header).unwrap();
|
||||
assert_eq!(parsed.name, 1701606260);
|
||||
assert_eq!(parsed.name, FourCC(1701606260));
|
||||
assert_eq!(parsed.size, 28);
|
||||
assert_eq!(parsed.edits.len(), 1);
|
||||
assert_eq!(parsed.edits[0].segment_duration, 16909060);
|
||||
assert_eq!(parsed.edits[0].media_time, 84281096);
|
||||
assert_eq!(parsed.edits[0].media_rate_integer, 2314);
|
||||
assert_eq!(parsed.edits[0].media_rate_fraction, 2828);
|
||||
println!("box {}", parsed);
|
||||
println!("box {:?}", parsed);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -1068,14 +938,14 @@ fn test_read_elst_v1() {
|
|||
let mut stream = Cursor::new(test);
|
||||
let header = read_box_header(&mut stream).unwrap();
|
||||
let parsed = read_elst(&mut stream, &header).unwrap();
|
||||
assert_eq!(parsed.name, 1701606260);
|
||||
assert_eq!(parsed.name, FourCC(1701606260));
|
||||
assert_eq!(parsed.size, 56);
|
||||
assert_eq!(parsed.edits.len(), 2);
|
||||
assert_eq!(parsed.edits[1].segment_duration, 72623859723010820);
|
||||
assert_eq!(parsed.edits[1].media_time, 361984551075317512);
|
||||
assert_eq!(parsed.edits[1].media_rate_integer, 2314);
|
||||
assert_eq!(parsed.edits[1].media_rate_fraction, 2828);
|
||||
println!("box {}", parsed);
|
||||
println!("box {:?}", parsed);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -1095,11 +965,11 @@ fn test_read_mdhd_v0() {
|
|||
let mut stream = Cursor::new(test);
|
||||
let header = read_box_header(&mut stream).unwrap();
|
||||
let parsed = read_mdhd(&mut stream, &header).unwrap();
|
||||
assert_eq!(parsed.name, 1835296868);
|
||||
assert_eq!(parsed.name, FourCC(1835296868));
|
||||
assert_eq!(parsed.size, 32);
|
||||
assert_eq!(parsed.timescale, 16909060);
|
||||
assert_eq!(parsed.duration, 84281096);
|
||||
println!("box {}", parsed);
|
||||
println!("box {:?}", parsed);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -1119,9 +989,9 @@ fn test_read_mdhd_v1() {
|
|||
let mut stream = Cursor::new(test);
|
||||
let header = read_box_header(&mut stream).unwrap();
|
||||
let parsed = read_mdhd(&mut stream, &header).unwrap();
|
||||
assert_eq!(parsed.name, 1835296868);
|
||||
assert_eq!(parsed.name, FourCC(1835296868));
|
||||
assert_eq!(parsed.size, 44);
|
||||
assert_eq!(parsed.timescale, 16909060);
|
||||
assert_eq!(parsed.duration, 361984551075317512);
|
||||
println!("box {}", parsed);
|
||||
println!("box {:?}", parsed);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,103 @@
|
|||
// C API for mp4parse module.
|
||||
// Parses ISO Base Media Format aka video/mp4 streams.
|
||||
|
||||
// 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 https://mozilla.org/MPL/2.0/.
|
||||
|
||||
use std;
|
||||
use std::io::Cursor;
|
||||
use byteorder;
|
||||
|
||||
// Symbols we need from our rust api.
|
||||
use MediaContext;
|
||||
use read_box;
|
||||
|
||||
/// Allocate an opaque rust-side parser context.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn mp4parse_new() -> *mut MediaContext {
|
||||
let context = Box::new(MediaContext::new());
|
||||
unsafe {
|
||||
// transmute is unsafe, but context is always valid.
|
||||
std::mem::transmute(context)
|
||||
}
|
||||
}
|
||||
|
||||
/// Free a rust-side parser context.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn mp4parse_free(context: *mut MediaContext) {
|
||||
assert!(!context.is_null());
|
||||
let _: Box<MediaContext> = std::mem::transmute(context);
|
||||
}
|
||||
|
||||
/// Feed a buffer through read_box(), returning the number of detected tracks.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn mp4parse_read(context: *mut MediaContext, buffer: *const u8, size: usize) -> i32 {
|
||||
// Validate arguments from C.
|
||||
if context.is_null() {
|
||||
return -1;
|
||||
}
|
||||
if buffer.is_null() || size < 8 {
|
||||
return -1;
|
||||
}
|
||||
|
||||
let mut context: &mut MediaContext = &mut *context;
|
||||
|
||||
// Wrap the buffer we've been give in a slice.
|
||||
let b = std::slice::from_raw_parts(buffer, size);
|
||||
let mut c = Cursor::new(b);
|
||||
|
||||
// Parse in a subthread to catch any panics.
|
||||
let task = std::thread::spawn(move || {
|
||||
loop {
|
||||
match read_box(&mut c, &mut context) {
|
||||
Ok(_) => {},
|
||||
Err(byteorder::Error::UnexpectedEOF) => { break },
|
||||
Err(e) => { panic!(e); },
|
||||
}
|
||||
}
|
||||
// Make sure the track count fits in an i32 so we can use
|
||||
// negative values for failure.
|
||||
assert!(context.tracks.len() < std::i32::MAX as usize);
|
||||
context.tracks.len() as i32
|
||||
});
|
||||
task.join().unwrap_or(-1)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn new_context() {
|
||||
let context = mp4parse_new();
|
||||
assert!(!context.is_null());
|
||||
unsafe { mp4parse_free(context); }
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "assertion failed")]
|
||||
fn free_null_context() {
|
||||
unsafe { mp4parse_free(std::ptr::null_mut()); }
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn arg_validation() {
|
||||
let null_buffer = std::ptr::null();
|
||||
let null_context = std::ptr::null_mut();
|
||||
|
||||
let context = mp4parse_new();
|
||||
assert!(!context.is_null());
|
||||
|
||||
let buffer = vec![0u8; 8];
|
||||
|
||||
unsafe {
|
||||
assert_eq!(-1, mp4parse_read(null_context, null_buffer, 0));
|
||||
assert_eq!(-1, mp4parse_read(context, null_buffer, 0));
|
||||
}
|
||||
|
||||
for size in (0..buffer.len()) {
|
||||
println!("testing buffer length {}", size);
|
||||
unsafe {
|
||||
assert_eq!(-1, mp4parse_read(context, buffer.as_ptr(), size));
|
||||
}
|
||||
}
|
||||
|
||||
unsafe { mp4parse_free(context); }
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
// 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 https://mozilla.org/MPL/2.0/.
|
||||
|
||||
#ifndef _MP4PARSE_RUST_H
|
||||
#define _MP4PARSE_RUST_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct mp4parse_state;
|
||||
|
||||
struct mp4parse_state* mp4parse_new(void);
|
||||
void mp4parse_free(struct mp4parse_state* state);
|
||||
|
||||
int32_t mp4parse_read(struct mp4parse_state* state, uint8_t *buffer, size_t size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -5,33 +5,46 @@
|
|||
|
||||
#include "gtest/gtest.h"
|
||||
#include "mp4_demuxer/MP4Metadata.h"
|
||||
#include "mp4parse.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <vector>
|
||||
|
||||
extern "C" int32_t read_box_from_buffer(uint8_t *buffer, size_t size);
|
||||
|
||||
using namespace mp4_demuxer;
|
||||
using namespace mozilla;
|
||||
|
||||
TEST(rust, MP4MetadataEmpty)
|
||||
{
|
||||
int32_t rv;
|
||||
rv = read_box_from_buffer(nullptr, 0);
|
||||
|
||||
mp4parse_state* context = mp4parse_new();
|
||||
ASSERT_NE(context, nullptr);
|
||||
|
||||
rv = mp4parse_read(nullptr, nullptr, 0);
|
||||
EXPECT_EQ(rv, -1);
|
||||
rv = mp4parse_read(context, nullptr, 0);
|
||||
EXPECT_EQ(rv, -1);
|
||||
|
||||
size_t len = 4097;
|
||||
rv = read_box_from_buffer(nullptr, len);
|
||||
rv = mp4parse_read(nullptr, nullptr, len);
|
||||
EXPECT_EQ(rv, -1);
|
||||
rv = mp4parse_read(context, nullptr, len);
|
||||
EXPECT_EQ(rv, -1);
|
||||
|
||||
std::vector<uint8_t> buf;
|
||||
rv = read_box_from_buffer(buf.data(), buf.size());
|
||||
rv = mp4parse_read(nullptr, buf.data(), buf.size());
|
||||
EXPECT_EQ(rv, -1);
|
||||
rv = mp4parse_read(context, buf.data(), buf.size());
|
||||
EXPECT_EQ(rv, -1);
|
||||
|
||||
buf.reserve(len);
|
||||
rv = read_box_from_buffer(buf.data(), buf.size());
|
||||
rv = mp4parse_read(nullptr, buf.data(), buf.size());
|
||||
EXPECT_EQ(rv, -1);
|
||||
rv = mp4parse_read(context, buf.data(), buf.size());
|
||||
EXPECT_EQ(rv, -1);
|
||||
|
||||
mp4parse_free(context);
|
||||
}
|
||||
|
||||
TEST(rust, MP4Metadata)
|
||||
|
@ -45,6 +58,11 @@ TEST(rust, MP4Metadata)
|
|||
buf.resize(read);
|
||||
fclose(f);
|
||||
|
||||
int32_t rv = read_box_from_buffer(buf.data(), buf.size());
|
||||
mp4parse_state* context = mp4parse_new();
|
||||
ASSERT_NE(context, nullptr);
|
||||
|
||||
int32_t rv = mp4parse_read(context, buf.data(), buf.size());
|
||||
EXPECT_EQ(rv, 2);
|
||||
|
||||
mp4parse_free(context);
|
||||
}
|
||||
|
|
|
@ -23,6 +23,9 @@ if CONFIG['MOZ_RUST']:
|
|||
TEST_HARNESS_FILES.gtest += [
|
||||
'../../../dom/media/test/street.mp4',
|
||||
]
|
||||
LOCAL_INCLUDES += [
|
||||
'../binding/include',
|
||||
]
|
||||
|
||||
FINAL_LIBRARY = 'xul-gtest'
|
||||
|
||||
|
|
|
@ -4813,6 +4813,8 @@ pref("urlclassifier.trackingWhitelistTable", "test-trackwhite-simple,mozstd-trac
|
|||
pref("browser.safebrowsing.provider.mozilla.lists", "mozstd-track-digest256,mozstd-trackwhite-digest256,mozfull-track-digest256");
|
||||
pref("browser.safebrowsing.provider.mozilla.updateURL", "https://shavar.services.mozilla.com/downloads?client=SAFEBROWSING_ID&appver=%VERSION%&pver=2.2");
|
||||
pref("browser.safebrowsing.provider.mozilla.gethashURL", "https://shavar.services.mozilla.com/gethash?client=SAFEBROWSING_ID&appver=%VERSION%&pver=2.2");
|
||||
// Set to a date in the past to force immediate download in new profiles.
|
||||
pref("browser.safebrowsing.provider.mozilla.nextupdatetime", "1");
|
||||
// Block lists for tracking protection. The name values will be used as the keys
|
||||
// to lookup the localized name in preferences.properties.
|
||||
pref("browser.safebrowsing.provider.mozilla.lists.mozstd.name", "mozstdName");
|
||||
|
|
|
@ -83,6 +83,7 @@ nsHttpConnection::nsHttpConnection()
|
|||
, mTransactionCaps(0)
|
||||
, mResponseTimeoutEnabled(false)
|
||||
, mTCPKeepaliveConfig(kTCPKeepaliveDisabled)
|
||||
, mForceSendPending(false)
|
||||
{
|
||||
LOG(("Creating nsHttpConnection @%p\n", this));
|
||||
|
||||
|
@ -113,6 +114,10 @@ nsHttpConnection::~nsHttpConnection()
|
|||
Telemetry::HTTP_KBREAD_PER_CONN,
|
||||
totalKBRead);
|
||||
}
|
||||
if (mForceSendTimer) {
|
||||
mForceSendTimer->Cancel();
|
||||
mForceSendTimer = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
@ -569,6 +574,10 @@ nsHttpConnection::Close(nsresult reason)
|
|||
mTCPKeepaliveTransitionTimer->Cancel();
|
||||
mTCPKeepaliveTransitionTimer = nullptr;
|
||||
}
|
||||
if (mForceSendTimer) {
|
||||
mForceSendTimer->Cancel();
|
||||
mForceSendTimer = nullptr;
|
||||
}
|
||||
|
||||
if (NS_FAILED(reason)) {
|
||||
if (mIdleMonitoring)
|
||||
|
@ -1340,10 +1349,10 @@ nsHttpConnection::ResumeRecv()
|
|||
}
|
||||
|
||||
|
||||
class nsHttpConnectionForceIO : public nsRunnable
|
||||
class HttpConnectionForceIO : public nsRunnable
|
||||
{
|
||||
public:
|
||||
nsHttpConnectionForceIO(nsHttpConnection *aConn, bool doRecv)
|
||||
HttpConnectionForceIO(nsHttpConnection *aConn, bool doRecv)
|
||||
: mConn(aConn)
|
||||
, mDoRecv(doRecv)
|
||||
{}
|
||||
|
@ -1357,8 +1366,12 @@ public:
|
|||
return NS_OK;
|
||||
return mConn->OnInputStreamReady(mConn->mSocketIn);
|
||||
}
|
||||
if (!mConn->mSocketOut)
|
||||
|
||||
MOZ_ASSERT(mConn->mForceSendPending);
|
||||
mConn->mForceSendPending = false;
|
||||
if (!mConn->mSocketOut) {
|
||||
return NS_OK;
|
||||
}
|
||||
return mConn->OnOutputStreamReady(mConn->mSocketOut);
|
||||
}
|
||||
private:
|
||||
|
@ -1366,6 +1379,37 @@ private:
|
|||
bool mDoRecv;
|
||||
};
|
||||
|
||||
void
|
||||
nsHttpConnection::ForceSendIO(nsITimer *aTimer, void *aClosure)
|
||||
{
|
||||
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
|
||||
nsHttpConnection *self = static_cast<nsHttpConnection *>(aClosure);
|
||||
MOZ_ASSERT(aTimer == self->mForceSendTimer);
|
||||
self->mForceSendTimer = nullptr;
|
||||
NS_DispatchToCurrentThread(new HttpConnectionForceIO(self, false));
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHttpConnection::MaybeForceSendIO()
|
||||
{
|
||||
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
|
||||
// due to bug 1213084 sometimes real I/O events do not get serviced when
|
||||
// NSPR derived I/O events are ready and this can cause a deadlock with
|
||||
// https over https proxying. Normally we would expect the write callback to
|
||||
// be invoked before this timer goes off, but set it at the old windows
|
||||
// tick interval (kForceDelay) as a backup for those circumstances.
|
||||
static const uint32_t kForceDelay = 17; //ms
|
||||
|
||||
if (mForceSendPending) {
|
||||
return NS_OK;
|
||||
}
|
||||
MOZ_ASSERT(!mForceSendTimer);
|
||||
mForceSendPending = true;
|
||||
mForceSendTimer = do_CreateInstance("@mozilla.org/timer;1");
|
||||
return mForceSendTimer->InitWithFuncCallback(
|
||||
nsHttpConnection::ForceSendIO, this, kForceDelay, nsITimer::TYPE_ONE_SHOT);
|
||||
}
|
||||
|
||||
// trigger an asynchronous read
|
||||
nsresult
|
||||
nsHttpConnection::ForceRecv()
|
||||
|
@ -1373,7 +1417,7 @@ nsHttpConnection::ForceRecv()
|
|||
LOG(("nsHttpConnection::ForceRecv [this=%p]\n", this));
|
||||
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
|
||||
|
||||
return NS_DispatchToCurrentThread(new nsHttpConnectionForceIO(this, true));
|
||||
return NS_DispatchToCurrentThread(new HttpConnectionForceIO(this, true));
|
||||
}
|
||||
|
||||
// trigger an asynchronous write
|
||||
|
@ -1386,8 +1430,7 @@ nsHttpConnection::ForceSend()
|
|||
if (mTLSFilter) {
|
||||
return mTLSFilter->NudgeTunnel(this);
|
||||
}
|
||||
|
||||
return NS_DispatchToCurrentThread(new nsHttpConnectionForceIO(this, false));
|
||||
return MaybeForceSendIO();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -2052,7 +2095,6 @@ nsHttpConnection::OnOutputStreamReady(nsIAsyncOutputStream *out)
|
|||
{
|
||||
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
|
||||
MOZ_ASSERT(out == mSocketOut, "unexpected socket");
|
||||
|
||||
// if the transaction was dropped...
|
||||
if (!mTransaction) {
|
||||
LOG((" no transaction; ignoring event\n"));
|
||||
|
|
|
@ -142,7 +142,7 @@ public:
|
|||
int64_t MaxBytesRead() {return mMaxBytesRead;}
|
||||
uint8_t GetLastHttpResponseVersion() { return mLastHttpResponseVersion; }
|
||||
|
||||
friend class nsHttpConnectionForceIO;
|
||||
friend class HttpConnectionForceIO;
|
||||
nsresult ForceSend();
|
||||
nsresult ForceRecv();
|
||||
|
||||
|
@ -349,6 +349,13 @@ private:
|
|||
// Flag to indicate connection is in inital keepalive period (fast detect).
|
||||
uint32_t mTCPKeepaliveConfig;
|
||||
nsCOMPtr<nsITimer> mTCPKeepaliveTransitionTimer;
|
||||
|
||||
private:
|
||||
// For ForceSend()
|
||||
static void ForceSendIO(nsITimer *aTimer, void *aClosure);
|
||||
nsresult MaybeForceSendIO();
|
||||
bool mForceSendPending;
|
||||
nsCOMPtr<nsITimer> mForceSendTimer;
|
||||
};
|
||||
|
||||
} // namespace net
|
||||
|
|
|
@ -103,8 +103,6 @@ function storeCertOverride(port, cert) {
|
|||
}
|
||||
|
||||
function startClient(port, expectedResult, options = {}) {
|
||||
let SSL_ERROR_BASE = Ci.nsINSSErrorsService.NSS_SSL_ERROR_BASE;
|
||||
let SSL_ERROR_BAD_CERT_ALERT = SSL_ERROR_BASE + 17;
|
||||
let transport =
|
||||
socketTransportService.createTransport(["ssl"], 1, "127.0.0.1", port, null);
|
||||
if (options.isPrivate) {
|
||||
|
@ -113,8 +111,7 @@ function startClient(port, expectedResult, options = {}) {
|
|||
let input;
|
||||
let output;
|
||||
|
||||
let inputDeferred = Promise.defer();
|
||||
let outputDeferred = Promise.defer();
|
||||
let deferred = Promise.defer();
|
||||
|
||||
let handler = {
|
||||
|
||||
|
@ -125,46 +122,48 @@ function startClient(port, expectedResult, options = {}) {
|
|||
},
|
||||
|
||||
onInputStreamReady: function(input) {
|
||||
let errorCode = Cr.NS_OK;
|
||||
try {
|
||||
let data = NetUtil.readInputStreamToString(input, input.available());
|
||||
equal(data, "HELLO", "Echoed data received");
|
||||
input.close();
|
||||
output.close();
|
||||
deferred.resolve();
|
||||
} catch (e) {
|
||||
errorCode = e.result;
|
||||
}
|
||||
try {
|
||||
equal(errorCode, expectedResult,
|
||||
"Actual and expected connection result should match");
|
||||
inputDeferred.resolve();
|
||||
} catch (e) {
|
||||
inputDeferred.reject(e);
|
||||
deferred.reject(e);
|
||||
}
|
||||
},
|
||||
|
||||
onOutputStreamReady: function(output) {
|
||||
try {
|
||||
output.write("HELLO", 5);
|
||||
try {
|
||||
output.write("HELLO", 5);
|
||||
} catch (e) {
|
||||
if (e.result == Cr.NS_BASE_STREAM_WOULD_BLOCK) {
|
||||
output.asyncWait(handler, 0, 0, Services.tm.currentThread);
|
||||
return;
|
||||
}
|
||||
equal(e.result, expectedResult,
|
||||
"Actual and expected connection result should match");
|
||||
output.close();
|
||||
deferred.resolve();
|
||||
return;
|
||||
}
|
||||
equal(Cr.NS_OK, expectedResult, "Connection should succeed");
|
||||
do_print("Output to server written");
|
||||
outputDeferred.resolve();
|
||||
input = transport.openInputStream(0, 0, 0);
|
||||
input.asyncWait(handler, 0, 0, Services.tm.currentThread);
|
||||
} catch (e) {
|
||||
let errorCode = -1 * (e.result & 0xFFFF);
|
||||
if (errorCode == SSL_ERROR_BAD_CERT_ALERT) {
|
||||
do_print("Server doesn't like client cert");
|
||||
}
|
||||
outputDeferred.reject(e);
|
||||
deferred.reject(e);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
transport.setEventSink(handler, Services.tm.currentThread);
|
||||
output = transport.openOutputStream(0, 0, 0);
|
||||
output = transport.openOutputStream(Ci.nsITransport.OPEN_UNBUFFERED, 0, 0);
|
||||
output.QueryInterface(Ci.nsIAsyncOutputStream);
|
||||
|
||||
return Promise.all([inputDeferred.promise, outputDeferred.promise]);
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function run_test() {
|
||||
|
|
|
@ -152,4 +152,3 @@ skip-if = toolkit == 'android' || buildapp == 'b2g'
|
|||
|
||||
[test_weak_crypto.js]
|
||||
firefox-appdir = browser
|
||||
skip-if = toolkit == 'android'
|
||||
|
|
|
@ -6,23 +6,6 @@ import psutil
|
|||
here = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
|
||||
def check_for_process(processName):
|
||||
"""
|
||||
Use to determine if process of the given name is still running.
|
||||
|
||||
Returns:
|
||||
detected -- True if process is detected to exist, False otherwise
|
||||
output -- if process exists, stdout of the process, [] otherwise
|
||||
"""
|
||||
name = os.path.basename(processName)
|
||||
process = [p.pid for p in psutil.process_iter()
|
||||
if p.name() == name]
|
||||
|
||||
if process:
|
||||
return True, process
|
||||
return False, []
|
||||
|
||||
|
||||
class ProcTest(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
|
@ -30,24 +13,24 @@ class ProcTest(unittest.TestCase):
|
|||
cls.proclaunch = os.path.join(here, "proclaunch.py")
|
||||
cls.python = sys.executable
|
||||
|
||||
def determine_status(self,
|
||||
detected=False,
|
||||
output='',
|
||||
returncode=0,
|
||||
didtimeout=False,
|
||||
isalive=False,
|
||||
expectedfail=()):
|
||||
def determine_status(self, proc, isalive=False, expectedfail=()):
|
||||
"""
|
||||
Use to determine if the situation has failed.
|
||||
Parameters:
|
||||
detected -- value from check_for_process to determine if the process is detected
|
||||
output -- string of data from detected process, can be ''
|
||||
returncode -- return code from process, defaults to 0
|
||||
didtimeout -- True if process timed out, defaults to False
|
||||
proc -- the processhandler instance
|
||||
isalive -- Use True to indicate we pass if the process exists; however, by default
|
||||
the test will pass if the process does not exist (isalive == False)
|
||||
expectedfail -- Defaults to [], used to indicate a list of fields that are expected to fail
|
||||
"""
|
||||
returncode = proc.proc.returncode
|
||||
didtimeout = proc.didTimeout
|
||||
detected = psutil.pid_exists(proc.pid)
|
||||
output = ''
|
||||
# ProcessHandler has output when store_output is set to True in the constructor
|
||||
# (this is the default)
|
||||
if getattr(proc, 'output'):
|
||||
output = proc.output
|
||||
|
||||
if 'returncode' in expectedfail:
|
||||
self.assertTrue(returncode, "Detected an unexpected return code of: %s" % returncode)
|
||||
elif isalive:
|
||||
|
|
|
@ -8,6 +8,7 @@ import os
|
|||
import subprocess
|
||||
import sys
|
||||
import unittest
|
||||
import proctest
|
||||
from mozprocess import processhandler
|
||||
|
||||
here = os.path.dirname(os.path.abspath(__file__))
|
||||
|
@ -49,45 +50,8 @@ def make_proclaunch(aDir):
|
|||
raise AssertionError("proclaunch executable '%s' does not exist (sys.platform=%s)" % (exepath, sys.platform))
|
||||
return exepath
|
||||
|
||||
def check_for_process(processName):
|
||||
"""
|
||||
Use to determine if process of the given name is still running.
|
||||
|
||||
Returns:
|
||||
detected -- True if process is detected to exist, False otherwise
|
||||
output -- if process exists, stdout of the process, '' otherwise
|
||||
"""
|
||||
# TODO: replace with
|
||||
# https://github.com/mozilla/mozbase/blob/master/mozprocess/mozprocess/pid.py
|
||||
# which should be augmented from talos
|
||||
# see https://bugzilla.mozilla.org/show_bug.cgi?id=705864
|
||||
output = ''
|
||||
if sys.platform == "win32":
|
||||
# On windows we use tasklist
|
||||
p1 = subprocess.Popen(["tasklist"], stdout=subprocess.PIPE)
|
||||
output = p1.communicate()[0]
|
||||
detected = False
|
||||
for line in output.splitlines():
|
||||
if processName in line:
|
||||
detected = True
|
||||
break
|
||||
else:
|
||||
p1 = subprocess.Popen(["ps", "-ef"], stdout=subprocess.PIPE)
|
||||
p2 = subprocess.Popen(["grep", processName], stdin=p1.stdout, stdout=subprocess.PIPE)
|
||||
p1.stdout.close()
|
||||
output = p2.communicate()[0]
|
||||
detected = False
|
||||
for line in output.splitlines():
|
||||
if "grep %s" % processName in line:
|
||||
continue
|
||||
elif processName in line and not 'defunct' in line:
|
||||
detected = True
|
||||
break
|
||||
|
||||
return detected, output
|
||||
|
||||
|
||||
class ProcTest(unittest.TestCase):
|
||||
class ProcTest(proctest.ProcTest):
|
||||
|
||||
# whether to remove created files on exit
|
||||
cleanup = os.environ.get('CLEANUP', 'true').lower() in ('1', 'true')
|
||||
|
@ -128,11 +92,7 @@ class ProcTest(unittest.TestCase):
|
|||
p.run()
|
||||
p.wait()
|
||||
|
||||
detected, output = check_for_process(self.proclaunch)
|
||||
self.determine_status(detected,
|
||||
output,
|
||||
p.proc.returncode,
|
||||
p.didTimeout)
|
||||
self.determine_status(p)
|
||||
|
||||
def test_commandline_no_args(self):
|
||||
"""Command line is reported correctly when no arguments are specified"""
|
||||
|
@ -185,11 +145,7 @@ class ProcTest(unittest.TestCase):
|
|||
p.run()
|
||||
p.wait()
|
||||
|
||||
detected, output = check_for_process(self.proclaunch)
|
||||
self.determine_status(detected,
|
||||
output,
|
||||
p.proc.returncode,
|
||||
p.didTimeout)
|
||||
self.determine_status(p)
|
||||
|
||||
def test_process_timeout(self):
|
||||
""" Process is started, runs but we time out waiting on it
|
||||
|
@ -200,13 +156,7 @@ class ProcTest(unittest.TestCase):
|
|||
p.run(timeout=10)
|
||||
p.wait()
|
||||
|
||||
detected, output = check_for_process(self.proclaunch)
|
||||
self.determine_status(detected,
|
||||
output,
|
||||
p.proc.returncode,
|
||||
p.didTimeout,
|
||||
False,
|
||||
['returncode', 'didtimeout'])
|
||||
self.determine_status(p, False, ['returncode', 'didtimeout'])
|
||||
|
||||
def test_process_timeout_no_kill(self):
|
||||
""" Process is started, runs but we time out waiting on it
|
||||
|
@ -224,13 +174,7 @@ class ProcTest(unittest.TestCase):
|
|||
p.wait()
|
||||
self.assertTrue(p.didTimeout)
|
||||
|
||||
detected, output = check_for_process(self.proclaunch)
|
||||
self.determine_status(detected,
|
||||
output,
|
||||
p.proc.returncode,
|
||||
p.didTimeout,
|
||||
False,
|
||||
['returncode', 'didtimeout'])
|
||||
self.determine_status(p, False, ['returncode', 'didtimeout'])
|
||||
|
||||
def test_process_waittimeout(self):
|
||||
"""
|
||||
|
@ -244,13 +188,7 @@ class ProcTest(unittest.TestCase):
|
|||
p.run()
|
||||
p.wait(timeout=5)
|
||||
|
||||
detected, output = check_for_process(self.proclaunch)
|
||||
self.determine_status(detected,
|
||||
output,
|
||||
p.proc.returncode,
|
||||
p.didTimeout,
|
||||
True,
|
||||
())
|
||||
self.determine_status(p, True, ())
|
||||
|
||||
def test_process_waitnotimeout(self):
|
||||
""" Process is started, runs to completion before our wait times out
|
||||
|
@ -261,11 +199,7 @@ class ProcTest(unittest.TestCase):
|
|||
p.run(timeout=30)
|
||||
p.wait()
|
||||
|
||||
detected, output = check_for_process(self.proclaunch)
|
||||
self.determine_status(detected,
|
||||
output,
|
||||
p.proc.returncode,
|
||||
p.didTimeout)
|
||||
self.determine_status(p)
|
||||
|
||||
def test_process_kill(self):
|
||||
"""Process is started, we kill it"""
|
||||
|
@ -275,11 +209,7 @@ class ProcTest(unittest.TestCase):
|
|||
p.run()
|
||||
p.kill()
|
||||
|
||||
detected, output = check_for_process(self.proclaunch)
|
||||
self.determine_status(detected,
|
||||
output,
|
||||
p.proc.returncode,
|
||||
p.didTimeout)
|
||||
self.determine_status(p)
|
||||
|
||||
def test_process_output_twice(self):
|
||||
"""
|
||||
|
@ -293,46 +223,8 @@ class ProcTest(unittest.TestCase):
|
|||
p.processOutput(timeout=5)
|
||||
p.wait()
|
||||
|
||||
detected, output = check_for_process(self.proclaunch)
|
||||
self.determine_status(detected,
|
||||
output,
|
||||
p.proc.returncode,
|
||||
p.didTimeout,
|
||||
False,
|
||||
())
|
||||
self.determine_status(p, False, ())
|
||||
|
||||
def determine_status(self,
|
||||
detected=False,
|
||||
output='',
|
||||
returncode=0,
|
||||
didtimeout=False,
|
||||
isalive=False,
|
||||
expectedfail=()):
|
||||
"""
|
||||
Use to determine if the situation has failed.
|
||||
Parameters:
|
||||
detected -- value from check_for_process to determine if the process is detected
|
||||
output -- string of data from detected process, can be ''
|
||||
returncode -- return code from process, defaults to 0
|
||||
didtimeout -- True if process timed out, defaults to False
|
||||
isalive -- Use True to indicate we pass if the process exists; however, by default
|
||||
the test will pass if the process does not exist (isalive == False)
|
||||
expectedfail -- Defaults to [], used to indicate a list of fields that are expected to fail
|
||||
"""
|
||||
if 'returncode' in expectedfail:
|
||||
self.assertTrue(returncode, "Detected an unexpected return code of: %s" % returncode)
|
||||
elif not isalive:
|
||||
self.assertTrue(returncode == 0, "Detected non-zero return code of: %d" % returncode)
|
||||
|
||||
if 'didtimeout' in expectedfail:
|
||||
self.assertTrue(didtimeout, "Detected that process didn't time out")
|
||||
else:
|
||||
self.assertTrue(not didtimeout, "Detected that process timed out")
|
||||
|
||||
if isalive:
|
||||
self.assertTrue(detected, "Detected process is not running, process output: %s" % output)
|
||||
else:
|
||||
self.assertTrue(not detected, "Detected process is still running, process output: %s" % output)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
|
|
@ -26,12 +26,7 @@ class ProcTestKill(proctest.ProcTest):
|
|||
p.run()
|
||||
p.kill()
|
||||
|
||||
detected, output = proctest.check_for_process(self.proclaunch)
|
||||
self.determine_status(detected,
|
||||
output,
|
||||
p.proc.returncode,
|
||||
p.didTimeout,
|
||||
expectedfail=('returncode',))
|
||||
self.determine_status(p, expectedfail=('returncode',))
|
||||
|
||||
def test_process_kill_deep(self):
|
||||
"""Process is started, we kill it, we use a deep process tree"""
|
||||
|
@ -41,12 +36,7 @@ class ProcTestKill(proctest.ProcTest):
|
|||
p.run()
|
||||
p.kill()
|
||||
|
||||
detected, output = proctest.check_for_process(self.proclaunch)
|
||||
self.determine_status(detected,
|
||||
output,
|
||||
p.proc.returncode,
|
||||
p.didTimeout,
|
||||
expectedfail=('returncode',))
|
||||
self.determine_status(p, expectedfail=('returncode',))
|
||||
|
||||
def test_process_kill_deep_wait(self):
|
||||
"""Process is started, we use a deep process tree, we let it spawn
|
||||
|
@ -59,12 +49,7 @@ class ProcTestKill(proctest.ProcTest):
|
|||
time.sleep(3)
|
||||
p.kill()
|
||||
|
||||
detected, output = proctest.check_for_process(self.proclaunch)
|
||||
self.determine_status(detected,
|
||||
output,
|
||||
p.proc.returncode,
|
||||
p.didTimeout,
|
||||
expectedfail=('returncode',))
|
||||
self.determine_status(p, expectedfail=('returncode',))
|
||||
|
||||
def test_process_kill_broad(self):
|
||||
"""Process is started, we kill it, we use a broad process tree"""
|
||||
|
@ -74,12 +59,7 @@ class ProcTestKill(proctest.ProcTest):
|
|||
p.run()
|
||||
p.kill()
|
||||
|
||||
detected, output = proctest.check_for_process(self.proclaunch)
|
||||
self.determine_status(detected,
|
||||
output,
|
||||
p.proc.returncode,
|
||||
p.didTimeout,
|
||||
expectedfail=('returncode',))
|
||||
self.determine_status(p, expectedfail=('returncode',))
|
||||
|
||||
@unittest.skipUnless(processhandler.isPosix, "posix only")
|
||||
def test_process_kill_with_sigterm(self):
|
||||
|
|
|
@ -25,12 +25,7 @@ class ProcTestKill(proctest.ProcTest):
|
|||
time.sleep(3)
|
||||
p.kill()
|
||||
|
||||
detected, output = proctest.check_for_process(self.proclaunch)
|
||||
self.determine_status(detected,
|
||||
output,
|
||||
p.proc.returncode,
|
||||
p.didTimeout,
|
||||
expectedfail=('returncode',))
|
||||
self.determine_status(p, expectedfail=('returncode',))
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
|
|
@ -22,13 +22,7 @@ class ProcTestMisc(proctest.ProcTest):
|
|||
p.processOutput(timeout=5)
|
||||
p.wait()
|
||||
|
||||
detected, output = proctest.check_for_process(self.proclaunch)
|
||||
self.determine_status(detected,
|
||||
output,
|
||||
p.proc.returncode,
|
||||
p.didTimeout,
|
||||
False,
|
||||
())
|
||||
self.determine_status(p, False, ())
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
|
|
@ -22,13 +22,7 @@ class ProcTestOutput(proctest.ProcTest):
|
|||
p.processOutput(timeout=5)
|
||||
p.wait()
|
||||
|
||||
detected, output = proctest.check_for_process("procnonewline.py")
|
||||
self.determine_status(detected,
|
||||
output,
|
||||
p.proc.returncode,
|
||||
p.didTimeout,
|
||||
False,
|
||||
())
|
||||
self.determine_status(p, False, ())
|
||||
|
||||
def test_stream_process_output(self):
|
||||
"""
|
||||
|
@ -56,13 +50,7 @@ class ProcTestOutput(proctest.ProcTest):
|
|||
self.assertFalse(buf.closed)
|
||||
buf.close()
|
||||
|
||||
detected, output = proctest.check_for_process("proccountfive.py")
|
||||
self.determine_status(detected,
|
||||
output,
|
||||
p.proc.returncode,
|
||||
p.didTimeout,
|
||||
False,
|
||||
())
|
||||
self.determine_status(p, False, ())
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
|
|
@ -34,12 +34,7 @@ class ProcTestPoll(proctest.ProcTest):
|
|||
|
||||
self.assertEqual(returncode, None)
|
||||
|
||||
detected, output = proctest.check_for_process(self.proclaunch)
|
||||
self.determine_status(detected,
|
||||
output,
|
||||
returncode,
|
||||
p.didTimeout,
|
||||
True)
|
||||
self.determine_status(p, True)
|
||||
p.kill()
|
||||
|
||||
def test_poll_after_kill(self):
|
||||
|
@ -55,11 +50,7 @@ class ProcTestPoll(proctest.ProcTest):
|
|||
self.assertLess(returncode, 0)
|
||||
self.assertEqual(returncode, p.poll())
|
||||
|
||||
detected, output = proctest.check_for_process(self.proclaunch)
|
||||
self.determine_status(detected,
|
||||
output,
|
||||
returncode,
|
||||
p.didTimeout)
|
||||
self.determine_status(p)
|
||||
|
||||
def test_poll_after_kill_no_process_group(self):
|
||||
"""Process (no group) is killed, and poll() is called"""
|
||||
|
@ -76,11 +67,7 @@ class ProcTestPoll(proctest.ProcTest):
|
|||
self.assertLess(returncode, 0)
|
||||
self.assertEqual(returncode, p.poll())
|
||||
|
||||
detected, output = proctest.check_for_process(self.proclaunch)
|
||||
self.determine_status(detected,
|
||||
output,
|
||||
returncode,
|
||||
p.didTimeout)
|
||||
self.determine_status(p)
|
||||
|
||||
def test_poll_after_double_kill(self):
|
||||
"""Process is killed twice, and poll() is called"""
|
||||
|
@ -96,11 +83,7 @@ class ProcTestPoll(proctest.ProcTest):
|
|||
self.assertLess(returncode, 0)
|
||||
self.assertEqual(returncode, p.poll())
|
||||
|
||||
detected, output = proctest.check_for_process(self.proclaunch)
|
||||
self.determine_status(detected,
|
||||
output,
|
||||
returncode,
|
||||
p.didTimeout)
|
||||
self.determine_status(p)
|
||||
|
||||
def test_poll_after_external_kill(self):
|
||||
"""Process is killed externally, and poll() is called"""
|
||||
|
@ -116,11 +99,7 @@ class ProcTestPoll(proctest.ProcTest):
|
|||
self.assertEqual(returncode, -signal.SIGTERM)
|
||||
self.assertEqual(returncode, p.poll())
|
||||
|
||||
detected, output = proctest.check_for_process(self.proclaunch)
|
||||
self.determine_status(detected,
|
||||
output,
|
||||
returncode,
|
||||
p.didTimeout)
|
||||
self.determine_status(p)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
@ -19,11 +19,7 @@ class ProcTestWait(proctest.ProcTest):
|
|||
p.run()
|
||||
p.wait()
|
||||
|
||||
detected, output = proctest.check_for_process(self.proclaunch)
|
||||
self.determine_status(detected,
|
||||
output,
|
||||
p.proc.returncode,
|
||||
p.didTimeout)
|
||||
self.determine_status(p)
|
||||
|
||||
def test_wait(self):
|
||||
"""Process is started runs to completion while we wait indefinitely"""
|
||||
|
@ -34,11 +30,7 @@ class ProcTestWait(proctest.ProcTest):
|
|||
p.run()
|
||||
p.wait()
|
||||
|
||||
detected, output = proctest.check_for_process(self.proclaunch)
|
||||
self.determine_status(detected,
|
||||
output,
|
||||
p.proc.returncode,
|
||||
p.didTimeout)
|
||||
self.determine_status(p)
|
||||
|
||||
|
||||
def test_timeout(self):
|
||||
|
@ -50,18 +42,11 @@ class ProcTestWait(proctest.ProcTest):
|
|||
p.run(timeout=10)
|
||||
p.wait()
|
||||
|
||||
detected, output = proctest.check_for_process(self.proclaunch)
|
||||
|
||||
if mozinfo.isUnix:
|
||||
# process was killed, so returncode should be negative
|
||||
self.assertLess(p.proc.returncode, 0)
|
||||
|
||||
self.determine_status(detected,
|
||||
output,
|
||||
p.proc.returncode,
|
||||
p.didTimeout,
|
||||
False,
|
||||
['returncode', 'didtimeout'])
|
||||
self.determine_status(p, False, ['returncode', 'didtimeout'])
|
||||
|
||||
def test_waittimeout(self):
|
||||
"""
|
||||
|
@ -75,13 +60,7 @@ class ProcTestWait(proctest.ProcTest):
|
|||
p.run()
|
||||
p.wait(timeout=5)
|
||||
|
||||
detected, output = proctest.check_for_process(self.proclaunch)
|
||||
self.determine_status(detected,
|
||||
output,
|
||||
p.proc.returncode,
|
||||
p.didTimeout,
|
||||
True,
|
||||
())
|
||||
self.determine_status(p, True, ())
|
||||
|
||||
def test_waitnotimeout(self):
|
||||
""" Process is started, runs to completion before our wait times out
|
||||
|
@ -92,11 +71,7 @@ class ProcTestWait(proctest.ProcTest):
|
|||
p.run(timeout=30)
|
||||
p.wait()
|
||||
|
||||
detected, output = proctest.check_for_process(self.proclaunch)
|
||||
self.determine_status(detected,
|
||||
output,
|
||||
p.proc.returncode,
|
||||
p.didTimeout)
|
||||
self.determine_status(p)
|
||||
|
||||
def test_wait_twice_after_kill(self):
|
||||
"""Bug 968718: Process is started and stopped. wait() twice afterward."""
|
||||
|
@ -108,11 +83,7 @@ class ProcTestWait(proctest.ProcTest):
|
|||
returncode1 = p.wait()
|
||||
returncode2 = p.wait()
|
||||
|
||||
detected, output = proctest.check_for_process(self.proclaunch)
|
||||
self.determine_status(detected,
|
||||
output,
|
||||
returncode2,
|
||||
p.didTimeout)
|
||||
self.determine_status(p)
|
||||
|
||||
self.assertLess(returncode2, 0,
|
||||
'Negative returncode expected, got "%s"' % returncode2)
|
||||
|
|
|
@ -1396,12 +1396,17 @@ or run without that action (ie: --no-{action})"
|
|||
repo = self._query_repo()
|
||||
revision = self.query_revision()
|
||||
pushinfo = self.vcs_query_pushinfo(repo, revision)
|
||||
pushdate = time.strftime('%Y%m%d%H%M%S', time.gmtime(pushinfo.pushdate))
|
||||
|
||||
index = self.config.get('taskcluster_index', 'index.garbage.staging')
|
||||
fmt = {
|
||||
'index': index,
|
||||
'project': self.buildbot_config['properties']['branch'],
|
||||
'head_rev': revision,
|
||||
'pushdate': pushdate,
|
||||
'year': pushdate[0:4],
|
||||
'month': pushdate[4:6],
|
||||
'day': pushdate[6:8],
|
||||
'build_product': self.config['stage_product'],
|
||||
'build_name': self.query_build_name(),
|
||||
'build_type': self.query_build_type(),
|
||||
|
|
|
@ -11,6 +11,7 @@ This script manages Desktop repacks for nightly builds.
|
|||
import os
|
||||
import re
|
||||
import sys
|
||||
import time
|
||||
import shlex
|
||||
import logging
|
||||
|
||||
|
@ -965,6 +966,7 @@ class DesktopSingleLocale(LocalesMixin, ReleaseMixin, MockMixin, BuildbotMixin,
|
|||
if not repo:
|
||||
self.fatal("Unable to determine repository for querying the push info.")
|
||||
pushinfo = self.vcs_query_pushinfo(repo, revision, vcs='hgtool')
|
||||
pushdate = time.strftime('%Y%m%d%H%M%S', time.gmtime(pushinfo.pushdate))
|
||||
|
||||
routes_json = os.path.join(self.query_abs_dirs()['abs_mozilla_dir'],
|
||||
'testing/taskcluster/routes.json')
|
||||
|
@ -980,6 +982,10 @@ class DesktopSingleLocale(LocalesMixin, ReleaseMixin, MockMixin, BuildbotMixin,
|
|||
'index': self.config.get('taskcluster_index', 'index.garbage.staging'),
|
||||
'project': branch,
|
||||
'head_rev': revision,
|
||||
'pushdate': pushdate,
|
||||
'year': pushdate[0:4],
|
||||
'month': pushdate[4:6],
|
||||
'day': pushdate[6:8],
|
||||
'build_product': self.config['stage_product'],
|
||||
'build_name': self.query_build_name(),
|
||||
'build_type': self.query_build_type(),
|
||||
|
|
|
@ -15,6 +15,7 @@ import os
|
|||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
import shlex
|
||||
|
||||
try:
|
||||
|
@ -486,6 +487,7 @@ class MobileSingleLocale(MockMixin, LocalesMixin, ReleaseMixin,
|
|||
revision = self.query_revision()
|
||||
repo = self.query_l10n_repo()
|
||||
pushinfo = self.vcs_query_pushinfo(repo, revision, vcs='hgtool')
|
||||
pushdate = time.strftime('%Y%m%d%H%M%S', time.gmtime(pushinfo.pushdate))
|
||||
routes_json = os.path.join(self.query_abs_dirs()['abs_mozilla_dir'],
|
||||
'testing/taskcluster/routes.json')
|
||||
with open(routes_json) as f:
|
||||
|
@ -506,6 +508,10 @@ class MobileSingleLocale(MockMixin, LocalesMixin, ReleaseMixin,
|
|||
'index': self.config.get('taskcluster_index', 'index.garbage.staging'),
|
||||
'project': branch,
|
||||
'head_rev': revision,
|
||||
'pushdate': pushdate,
|
||||
'year': pushdate[0:4],
|
||||
'month': pushdate[4:6],
|
||||
'day': pushdate[6:8],
|
||||
'build_product': self.config['stage_product'],
|
||||
'build_name': self.query_build_name(),
|
||||
'build_type': self.query_build_type(),
|
||||
|
|
|
@ -297,14 +297,6 @@ def get_counters(config):
|
|||
def get_active_tests(config):
|
||||
activeTests = config.pop('activeTests').strip().split(':')
|
||||
|
||||
# temporary hack for now until we have e10s running on all tests
|
||||
if config['e10s'] and not config['develop']:
|
||||
for testname in ('sessionrestore',
|
||||
'sessionrestore_no_auto_restore'):
|
||||
if testname in activeTests:
|
||||
print "%s is unsupported on e10s, removing from list of " \
|
||||
"tests to run" % testname
|
||||
activeTests.remove(testname)
|
||||
# ensure tests are available
|
||||
availableTests = test.test_dict()
|
||||
if not set(activeTests).issubset(availableTests):
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80 filetype=javascript: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cu = Components.utils;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Services",
|
||||
"resource://gre/modules/Services.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "setTimeout",
|
||||
"resource://gre/modules/Timer.jsm");
|
||||
|
||||
// Observer Service topics.
|
||||
const STARTUP_TOPIC = "profile-after-change";
|
||||
const RESTORED_TOPIC = "sessionstore-windows-restored";
|
||||
|
||||
// Process Message Manager topics.
|
||||
const MSG_REQUEST = "session-restore-test?duration";
|
||||
const MSG_PROVIDE = "session-restore-test:duration";
|
||||
|
||||
function nsSessionRestoreTalosTest() {}
|
||||
|
||||
nsSessionRestoreTalosTest.prototype = {
|
||||
classID: Components.ID("{716346e5-0c45-4aa2-b601-da36f3c74bd8}"),
|
||||
|
||||
_xpcom_factory: XPCOMUtils.generateSingletonFactory(nsSessionRestoreTalosTest),
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//// nsISupports
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//// nsIObserver
|
||||
|
||||
observe: function DS_observe(aSubject, aTopic, aData) {
|
||||
switch (aTopic) {
|
||||
case STARTUP_TOPIC:
|
||||
this.init();
|
||||
break;
|
||||
case RESTORED_TOPIC:
|
||||
this.onRestored();
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Unknown topic ${aTopic}`);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Perform initialization on profile-after-change.
|
||||
*/
|
||||
init: function() {
|
||||
Services.obs.addObserver(this, RESTORED_TOPIC, false);
|
||||
},
|
||||
|
||||
/**
|
||||
* Session Restore is complete, hurray.
|
||||
*/
|
||||
onRestored: function() {
|
||||
setTimeout(function() {
|
||||
// `sessionRestored` actually becomes available only on the next tick.
|
||||
let startup_info = Services.startup.getStartupInfo();
|
||||
let duration = startup_info.sessionRestored - startup_info.sessionRestoreInit;
|
||||
|
||||
// Broadcast startup duration information immediately, in case the talos
|
||||
// page is already loaded.
|
||||
Services.ppmm.broadcastAsyncMessage(MSG_PROVIDE, {duration});
|
||||
|
||||
// Now, in case the talos page isn't loaded yet, prepare to respond if it
|
||||
// requestions the duration information.
|
||||
Services.ppmm.addMessageListener(MSG_REQUEST, function listener() {
|
||||
Services.ppmm.removeMessageListener(MSG_REQUEST, listener);
|
||||
Services.ppmm.broadcastAsyncMessage(MSG_PROVIDE, {duration});
|
||||
});
|
||||
}, 0);
|
||||
},
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// Module
|
||||
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([nsSessionRestoreTalosTest]);
|
|
@ -0,0 +1,8 @@
|
|||
# Register a component to be informed of startup. This component can then register
|
||||
# itself to watch sessionstore-windows-restored. Once it has observed
|
||||
# sessionstore-windows-restored, it will open the webpage with the harness.
|
||||
component {716346e5-0c45-4aa2-b601-da36f3c74bd8} SessionRestoreTalosTest.js
|
||||
contract @mozilla.org/talos/session-restore-test;1 {716346e5-0c45-4aa2-b601-da36f3c74bd8}
|
||||
category profile-after-change nsSessionRestoreTalosTest @mozilla.org/talos/session-restore-test;1
|
||||
|
||||
content session-restore-test content/
|
|
@ -0,0 +1,20 @@
|
|||
<?xml version="1.0"?><RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:em="http://www.mozilla.org/2004/em-rdf#"><Description about="urn:mozilla:install-manifest">
|
||||
|
||||
<!-- Required Items -->
|
||||
<em:id>session-restore-test@mozilla.org</em:id>
|
||||
<em:name>Session Restore Startup Performance Test</em:name>
|
||||
<em:version>1.2.0</em:version>
|
||||
|
||||
<em:targetApplication>
|
||||
<Description>
|
||||
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
|
||||
<em:minVersion>1.5</em:minVersion>
|
||||
<em:maxVersion>99.0.*</em:maxVersion>
|
||||
</Description>
|
||||
</em:targetApplication>
|
||||
|
||||
<!-- Optional Items -->
|
||||
<em:creator>David Rajchenbach-Teller</em:creator>
|
||||
<em:description>Bug 936630, bug 1098357. This add-on broadcasts the duration of session restore.</em:description>
|
||||
<em:homepageURL>https://bugzilla.mozilla.org/show_bug.cgi?id=1098357</em:homepageURL>
|
||||
</Description></RDF>
|
|
@ -2,53 +2,41 @@
|
|||
|
||||
var Services = Components.utils.import("resource://gre/modules/Services.jsm", {}).Services;
|
||||
|
||||
/**
|
||||
* Display the result, send it to the harness and quit.
|
||||
*/
|
||||
function finish() {
|
||||
Profiler.pause("This test measures the time between sessionRestoreInit and sessionRestored, ignore everything around that");
|
||||
Profiler.initFromURLQueryParams(location.search);
|
||||
Profiler.finishStartupProfiling();
|
||||
// Process Message Manager topics.
|
||||
const MSG_REQUEST = "session-restore-test?duration";
|
||||
const MSG_PROVIDE = "session-restore-test:duration";
|
||||
|
||||
setTimeout(function () {
|
||||
var startup_info = Services.startup.getStartupInfo();
|
||||
Services.cpmm.addMessageListener(MSG_PROVIDE,
|
||||
/**
|
||||
* Display the result, send it to the harness and quit.
|
||||
*/
|
||||
function finish(msg) {
|
||||
console.log(`main.js: received data on ${MSG_PROVIDE}`, msg);
|
||||
Services.cpmm.removeMessageListener(MSG_PROVIDE, finish);
|
||||
var duration = msg.data.duration;
|
||||
|
||||
var duration = startup_info.sessionRestored - startup_info.sessionRestoreInit;
|
||||
Profiler.pause("This test measures the time between sessionRestoreInit and sessionRestored, ignore everything around that");
|
||||
Profiler.initFromURLQueryParams(location.search);
|
||||
Profiler.finishStartupProfiling();
|
||||
|
||||
// Show result on screen. Nice but not really necessary.
|
||||
document.getElementById("sessionRestoreInit-to-sessionRestored").textContent = duration + "ms";
|
||||
setTimeout(function () {
|
||||
// Show result on screen. Nice but not really necessary.
|
||||
document.getElementById("sessionRestoreInit-to-sessionRestored").textContent = duration + "ms";
|
||||
|
||||
// Report data to Talos, if possible
|
||||
dumpLog("__start_report" +
|
||||
duration +
|
||||
"__end_report\n\n");
|
||||
// Report data to Talos, if possible
|
||||
dumpLog("__start_report" +
|
||||
duration +
|
||||
"__end_report\n\n");
|
||||
|
||||
// Next one is required by the test harness but not used
|
||||
dumpLog("__startTimestamp" +
|
||||
Date.now() +
|
||||
"__endTimestamp\n\n");
|
||||
// Next one is required by the test harness but not used
|
||||
dumpLog("__startTimestamp" +
|
||||
Date.now() +
|
||||
"__endTimestamp\n\n");
|
||||
|
||||
goQuitApplication();
|
||||
}, 0);
|
||||
}
|
||||
goQuitApplication();
|
||||
}, 0);
|
||||
});
|
||||
|
||||
function main() {
|
||||
// Collect (and display) data
|
||||
var startup_info = Services.startup.getStartupInfo();
|
||||
|
||||
// The script may be triggered before or after sessionRestored
|
||||
// and sessionRestoreInit are available. If both are available,
|
||||
// we are done.
|
||||
if (typeof startup_info.sessionRestored != "undefined"
|
||||
&& typeof startup_info.sessionRestoreInit != "undefined") {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise, we need to wait until *after* sesionstore-windows-restored,
|
||||
// which is the event that sets sessionRestored - since sessionRestoreInit
|
||||
// is set before sessionRestored, we are certain that both are now set.
|
||||
Services.obs.addObserver(finish, "sessionstore-windows-restored", false);
|
||||
}
|
||||
|
||||
main();
|
||||
// In case the add-on has broadcasted the message before we were loaded,
|
||||
// request a second broadcast.
|
||||
Services.cpmm.sendAsyncMessage(MSG_REQUEST, {});
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
{"profile-after-change":true,"final-ui-startup":true,"sessionstore-windows-restored":true,"quit-application-granted":true,"quit-application":true,"sessionstore-final-state-write-complete":true,"profile-change-net-teardown":true,"profile-change-teardown":true,"profile-before-change":true}
|
|
@ -148,6 +148,7 @@ class sessionrestore(TsBase):
|
|||
2. Launch Firefox.
|
||||
3. Measure the delta between firstPaint and sessionRestored.
|
||||
"""
|
||||
extensions = '${talos}/startup_test/sessionrestore/addon'
|
||||
cycles = 10
|
||||
timeout = 1000000
|
||||
sps_profile_startup = True
|
||||
|
@ -155,7 +156,7 @@ class sessionrestore(TsBase):
|
|||
profile_path = '${talos}/startup_test/sessionrestore/profile'
|
||||
url = 'startup_test/sessionrestore/index.html'
|
||||
shutdown = False
|
||||
reinstall = ['sessionstore.js']
|
||||
reinstall = ['sessionstore.js', 'sessionCheckpoints.json']
|
||||
# Restore the session
|
||||
preferences = {'browser.startup.page': 3}
|
||||
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче