merge mozilla-inbound to mozilla-central a=merge

This commit is contained in:
Carsten "Tomcat" Book 2015-10-22 11:45:11 +02:00
Родитель aa7bcebf8c a0863c908c
Коммит b26729a177
121 изменённых файлов: 1989 добавлений и 1088 удалений

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

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

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше