mirror of
https://github.com/roytam1/basilisk55.git
synced 2026-05-26 13:31:57 +00:00
cherry-picked mozilla upstream changes:
bug1437842, bug1452619, bug1453127, bug1447080, bug1426129, bug1454692, bug1458270, bug1452576, bug1459206, bug1459162, bug1451297, bug1462682, bug1450688, bug1456975, bug1442722, bug1465108, bug1459693
This commit is contained in:
@@ -1075,9 +1075,9 @@ StructuredCloneHolder::CustomReadHandler(JSContext* aCx,
|
||||
return ReadFormData(aCx, aReader, aIndex, this);
|
||||
}
|
||||
|
||||
if (aTag == SCTAG_DOM_IMAGEBITMAP) {
|
||||
MOZ_ASSERT(mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread ||
|
||||
mStructuredCloneScope == StructuredCloneScope::SameProcessDifferentThread);
|
||||
if (aTag == SCTAG_DOM_IMAGEBITMAP &&
|
||||
(mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread ||
|
||||
mStructuredCloneScope == StructuredCloneScope::SameProcessDifferentThread)) {
|
||||
|
||||
// Get the current global object.
|
||||
// This can be null.
|
||||
@@ -1087,7 +1087,9 @@ StructuredCloneHolder::CustomReadHandler(JSContext* aCx,
|
||||
parent, GetSurfaces(), aIndex);
|
||||
}
|
||||
|
||||
if (aTag == SCTAG_DOM_WASM) {
|
||||
if (aTag == SCTAG_DOM_WASM &&
|
||||
(mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread ||
|
||||
mStructuredCloneScope == StructuredCloneScope::SameProcessDifferentThread)) {
|
||||
return ReadWasmModule(aCx, aIndex, this);
|
||||
}
|
||||
|
||||
@@ -1202,9 +1204,9 @@ StructuredCloneHolder::CustomReadTransferHandler(JSContext* aCx,
|
||||
return true;
|
||||
}
|
||||
|
||||
if (aTag == SCTAG_DOM_CANVAS) {
|
||||
MOZ_ASSERT(mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread ||
|
||||
mStructuredCloneScope == StructuredCloneScope::SameProcessDifferentThread);
|
||||
if (aTag == SCTAG_DOM_CANVAS &&
|
||||
(mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread ||
|
||||
mStructuredCloneScope == StructuredCloneScope::SameProcessDifferentThread)) {
|
||||
MOZ_ASSERT(aContent);
|
||||
OffscreenCanvasCloneData* data =
|
||||
static_cast<OffscreenCanvasCloneData*>(aContent);
|
||||
@@ -1222,9 +1224,9 @@ StructuredCloneHolder::CustomReadTransferHandler(JSContext* aCx,
|
||||
return true;
|
||||
}
|
||||
|
||||
if (aTag == SCTAG_DOM_IMAGEBITMAP) {
|
||||
MOZ_ASSERT(mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread ||
|
||||
mStructuredCloneScope == StructuredCloneScope::SameProcessDifferentThread);
|
||||
if (aTag == SCTAG_DOM_IMAGEBITMAP &&
|
||||
(mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread ||
|
||||
mStructuredCloneScope == StructuredCloneScope::SameProcessDifferentThread)) {
|
||||
MOZ_ASSERT(aContent);
|
||||
ImageBitmapCloneData* data =
|
||||
static_cast<ImageBitmapCloneData*>(aContent);
|
||||
@@ -1328,9 +1330,9 @@ StructuredCloneHolder::CustomFreeTransferHandler(uint32_t aTag,
|
||||
return;
|
||||
}
|
||||
|
||||
if (aTag == SCTAG_DOM_CANVAS) {
|
||||
MOZ_ASSERT(mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread ||
|
||||
mStructuredCloneScope == StructuredCloneScope::SameProcessDifferentThread);
|
||||
if (aTag == SCTAG_DOM_CANVAS &&
|
||||
(mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread ||
|
||||
mStructuredCloneScope == StructuredCloneScope::SameProcessDifferentThread)) {
|
||||
MOZ_ASSERT(aContent);
|
||||
OffscreenCanvasCloneData* data =
|
||||
static_cast<OffscreenCanvasCloneData*>(aContent);
|
||||
@@ -1338,9 +1340,9 @@ StructuredCloneHolder::CustomFreeTransferHandler(uint32_t aTag,
|
||||
return;
|
||||
}
|
||||
|
||||
if (aTag == SCTAG_DOM_IMAGEBITMAP) {
|
||||
MOZ_ASSERT(mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread ||
|
||||
mStructuredCloneScope == StructuredCloneScope::SameProcessDifferentThread);
|
||||
if (aTag == SCTAG_DOM_IMAGEBITMAP &&
|
||||
(mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread ||
|
||||
mStructuredCloneScope == StructuredCloneScope::SameProcessDifferentThread)) {
|
||||
MOZ_ASSERT(aContent);
|
||||
ImageBitmapCloneData* data =
|
||||
static_cast<ImageBitmapCloneData*>(aContent);
|
||||
|
||||
@@ -1065,6 +1065,7 @@ nsFrameMessageManager::ReceiveMessage(nsISupports* aTarget,
|
||||
if (aRetVal) {
|
||||
ErrorResult rv;
|
||||
StructuredCloneData* data = aRetVal->AppendElement();
|
||||
data->InitScope(JS::StructuredCloneScope::DifferentProcess);
|
||||
data->Write(cx, rval, rv);
|
||||
if (NS_WARN_IF(rv.Failed())) {
|
||||
aRetVal->RemoveElementAt(aRetVal->Length() - 1);
|
||||
|
||||
@@ -137,7 +137,7 @@ nsStructuredCloneContainer::GetDataAsBase64(nsAString &aOut)
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
auto iter = Data().Iter();
|
||||
auto iter = Data().Start();
|
||||
size_t size = Data().Size();
|
||||
nsAutoCString binaryData;
|
||||
binaryData.SetLength(size);
|
||||
|
||||
@@ -1914,8 +1914,6 @@ CanvasRenderingContext2D::GetHeight() const
|
||||
NS_IMETHODIMP
|
||||
CanvasRenderingContext2D::SetDimensions(int32_t aWidth, int32_t aHeight)
|
||||
{
|
||||
ClearTarget();
|
||||
|
||||
// Zero sized surfaces can cause problems.
|
||||
mZero = false;
|
||||
if (aHeight == 0) {
|
||||
@@ -1926,14 +1924,14 @@ CanvasRenderingContext2D::SetDimensions(int32_t aWidth, int32_t aHeight)
|
||||
aWidth = 1;
|
||||
mZero = true;
|
||||
}
|
||||
mWidth = aWidth;
|
||||
mHeight = aHeight;
|
||||
|
||||
ClearTarget(aWidth, aHeight);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
CanvasRenderingContext2D::ClearTarget()
|
||||
CanvasRenderingContext2D::ClearTarget(int32_t aWidth, int32_t aHeight)
|
||||
{
|
||||
Reset();
|
||||
|
||||
@@ -1941,6 +1939,12 @@ CanvasRenderingContext2D::ClearTarget()
|
||||
|
||||
SetInitialState();
|
||||
|
||||
// Update dimensions only if new (strictly positive) values were passed.
|
||||
if (aWidth > 0 && aHeight > 0) {
|
||||
mWidth = aWidth;
|
||||
mHeight = aHeight;
|
||||
}
|
||||
|
||||
// For vertical writing-mode, unless text-orientation is sideways,
|
||||
// we'll modify the initial value of textBaseline to 'middle'.
|
||||
RefPtr<nsStyleContext> canvasStyle;
|
||||
|
||||
@@ -673,8 +673,11 @@ protected:
|
||||
|
||||
/**
|
||||
* Disposes an old target and prepares to lazily create a new target.
|
||||
*
|
||||
* Parameters are the new dimensions to be used, or if either is negative,
|
||||
* existing dimensions will be left unchanged.
|
||||
*/
|
||||
void ClearTarget();
|
||||
void ClearTarget(int32_t aWidth = -1, int32_t aHeight = -1);
|
||||
|
||||
/*
|
||||
* Returns the target to the buffer provider. i.e. this will queue a frame for
|
||||
|
||||
@@ -152,6 +152,7 @@ function test_changeDataWhileWorking() {
|
||||
|
||||
function test_setup() {
|
||||
SpecialPowers.pushPrefEnv({"set": [["dom.input.dirpicker", true],
|
||||
["dom.filesystem.pathcheck.disabled", true],
|
||||
["dom.webkitBlink.dirPicker.enabled", true]]}, next);
|
||||
}
|
||||
|
||||
|
||||
@@ -3597,7 +3597,8 @@ HTMLInputElement::Focus(ErrorResult& aError)
|
||||
nsNumberControlFrame* numberControlFrame =
|
||||
do_QueryFrame(GetPrimaryFrame());
|
||||
if (numberControlFrame) {
|
||||
HTMLInputElement* textControl = numberControlFrame->GetAnonTextControl();
|
||||
RefPtr<HTMLInputElement> textControl =
|
||||
numberControlFrame->GetAnonTextControl();
|
||||
if (textControl) {
|
||||
textControl->Focus(aError);
|
||||
return;
|
||||
|
||||
@@ -8410,12 +8410,12 @@ class ObjectStoreAddOrPutRequestOp::SCInputStream final
|
||||
: public nsIInputStream
|
||||
{
|
||||
const JSStructuredCloneData& mData;
|
||||
JSStructuredCloneData::IterImpl mIter;
|
||||
JSStructuredCloneData::Iterator mIter;
|
||||
|
||||
public:
|
||||
explicit SCInputStream(const JSStructuredCloneData& aData)
|
||||
: mData(aData)
|
||||
, mIter(aData.Iter())
|
||||
, mIter(aData.Start())
|
||||
{ }
|
||||
|
||||
private:
|
||||
@@ -19622,7 +19622,7 @@ UpgradeFileIdsFunction::OnFunctionCall(mozIStorageValueArray* aArguments,
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
StructuredCloneReadInfo cloneInfo;
|
||||
StructuredCloneReadInfo cloneInfo(JS::StructuredCloneScope::DifferentProcess);
|
||||
DatabaseOperationBase::GetStructuredCloneReadInfoFromValueArray(aArguments,
|
||||
1,
|
||||
0,
|
||||
@@ -19827,7 +19827,7 @@ DatabaseOperationBase::GetStructuredCloneReadInfoFromBlob(
|
||||
return NS_ERROR_FILE_CORRUPTED;
|
||||
}
|
||||
|
||||
if (!aInfo->mData.WriteBytes(uncompressedBuffer, uncompressed.Length())) {
|
||||
if (!aInfo->mData.AppendBytes(uncompressedBuffer, uncompressed.Length())) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
@@ -19913,7 +19913,7 @@ DatabaseOperationBase::GetStructuredCloneReadInfoFromExternalBlob(
|
||||
break;
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(!aInfo->mData.WriteBytes(buffer, numRead))) {
|
||||
if (NS_WARN_IF(!aInfo->mData.AppendBytes(buffer, numRead))) {
|
||||
rv = NS_ERROR_OUT_OF_MEMORY;
|
||||
break;
|
||||
}
|
||||
@@ -25280,7 +25280,7 @@ UpdateIndexDataValuesFunction::OnFunctionCall(mozIStorageValueArray* aValues,
|
||||
}
|
||||
#endif
|
||||
|
||||
StructuredCloneReadInfo cloneInfo;
|
||||
StructuredCloneReadInfo cloneInfo(JS::StructuredCloneScope::DifferentProcess);
|
||||
nsresult rv =
|
||||
GetStructuredCloneReadInfoFromValueArray(aValues,
|
||||
/* aDataIndex */ 3,
|
||||
@@ -26489,18 +26489,9 @@ ObjectStoreAddOrPutRequestOp::DoDatabaseWork(DatabaseConnection* aConnection)
|
||||
char keyPropBuffer[keyPropSize];
|
||||
LittleEndian::writeUint64(keyPropBuffer, keyPropValue);
|
||||
|
||||
auto iter = cloneData.Iter();
|
||||
DebugOnly<bool> result =
|
||||
iter.AdvanceAcrossSegments(cloneData, cloneInfo.offsetToKeyProp());
|
||||
MOZ_ASSERT(result);
|
||||
|
||||
for (char index : keyPropBuffer) {
|
||||
char* keyPropPointer = iter.Data();
|
||||
*keyPropPointer = index;
|
||||
|
||||
result = iter.AdvanceAcrossSegments(cloneData, 1);
|
||||
MOZ_ASSERT(result);
|
||||
}
|
||||
auto iter = cloneData.Start();
|
||||
MOZ_ALWAYS_TRUE(cloneData.Advance(iter, cloneInfo.offsetToKeyProp()));
|
||||
MOZ_ALWAYS_TRUE(cloneData.UpdateBytes(iter, keyPropBuffer, keyPropSize));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26526,7 +26517,7 @@ ObjectStoreAddOrPutRequestOp::DoDatabaseWork(DatabaseConnection* aConnection)
|
||||
} else {
|
||||
nsCString flatCloneData;
|
||||
flatCloneData.SetLength(cloneDataSize);
|
||||
auto iter = cloneData.Iter();
|
||||
auto iter = cloneData.Start();
|
||||
cloneData.ReadBytes(iter, flatCloneData.BeginWriting(), cloneDataSize);
|
||||
|
||||
// Compress the bytes before adding into the database.
|
||||
@@ -26783,7 +26774,7 @@ SCInputStream::ReadSegments(nsWriteSegmentFun aWriter,
|
||||
*_retval += count;
|
||||
aCount -= count;
|
||||
|
||||
mIter.Advance(mData, count);
|
||||
mData.Advance(mIter, count);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
@@ -27972,7 +27963,7 @@ CursorOpBase::PopulateResponseFromStatement(
|
||||
|
||||
switch (mCursor->mType) {
|
||||
case OpenCursorParams::TObjectStoreOpenCursorParams: {
|
||||
StructuredCloneReadInfo cloneInfo;
|
||||
StructuredCloneReadInfo cloneInfo(JS::StructuredCloneScope::DifferentProcess);
|
||||
rv = GetStructuredCloneReadInfoFromStatement(aStmt,
|
||||
2,
|
||||
1,
|
||||
@@ -28020,7 +28011,7 @@ CursorOpBase::PopulateResponseFromStatement(
|
||||
return rv;
|
||||
}
|
||||
|
||||
StructuredCloneReadInfo cloneInfo;
|
||||
StructuredCloneReadInfo cloneInfo(JS::StructuredCloneScope::DifferentProcess);
|
||||
rv = GetStructuredCloneReadInfoFromStatement(aStmt,
|
||||
4,
|
||||
3,
|
||||
|
||||
@@ -67,7 +67,7 @@ struct IDBObjectStore::StructuredCloneWriteInfo
|
||||
uint64_t mOffsetToKeyProp;
|
||||
|
||||
explicit StructuredCloneWriteInfo(IDBDatabase* aDatabase)
|
||||
: mCloneBuffer(JS::StructuredCloneScope::SameProcessSameThread, nullptr,
|
||||
: mCloneBuffer(JS::StructuredCloneScope::DifferentProcessForIndexedDB, nullptr,
|
||||
nullptr)
|
||||
, mDatabase(aDatabase)
|
||||
, mOffsetToKeyProp(0)
|
||||
@@ -1225,7 +1225,7 @@ IDBObjectStore::DeserializeValue(JSContext* aCx,
|
||||
// FIXME: Consider to use StructuredCloneHolder here and in other
|
||||
// deserializing methods.
|
||||
if (!JS_ReadStructuredClone(aCx, aCloneReadInfo.mData, JS_STRUCTURED_CLONE_VERSION,
|
||||
JS::StructuredCloneScope::SameProcessSameThread,
|
||||
JS::StructuredCloneScope::DifferentProcessForIndexedDB,
|
||||
aValue, &callbacks, &aCloneReadInfo)) {
|
||||
return false;
|
||||
}
|
||||
@@ -1258,7 +1258,7 @@ IDBObjectStore::DeserializeIndexValue(JSContext* aCx,
|
||||
};
|
||||
|
||||
if (!JS_ReadStructuredClone(aCx, aCloneReadInfo.mData, JS_STRUCTURED_CLONE_VERSION,
|
||||
JS::StructuredCloneScope::SameProcessSameThread,
|
||||
JS::StructuredCloneScope::DifferentProcessForIndexedDB,
|
||||
aValue, &callbacks, &aCloneReadInfo)) {
|
||||
return false;
|
||||
}
|
||||
@@ -1294,7 +1294,7 @@ IDBObjectStore::DeserializeUpgradeValue(JSContext* aCx,
|
||||
};
|
||||
|
||||
if (!JS_ReadStructuredClone(aCx, aCloneReadInfo.mData, JS_STRUCTURED_CLONE_VERSION,
|
||||
JS::StructuredCloneScope::SameProcessSameThread,
|
||||
JS::StructuredCloneScope::DifferentProcessForIndexedDB,
|
||||
aValue, &callbacks, &aCloneReadInfo)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -64,6 +64,10 @@ struct StructuredCloneReadInfo
|
||||
IDBDatabase* mDatabase;
|
||||
bool mHasPreprocessInfo;
|
||||
|
||||
// In IndexedDatabaseInlines.h
|
||||
inline explicit
|
||||
StructuredCloneReadInfo(JS::StructuredCloneScope aScope);
|
||||
|
||||
// In IndexedDatabaseInlines.h
|
||||
inline
|
||||
StructuredCloneReadInfo();
|
||||
|
||||
@@ -45,13 +45,20 @@ StructuredCloneFile::operator==(const StructuredCloneFile& aOther) const
|
||||
}
|
||||
|
||||
inline
|
||||
StructuredCloneReadInfo::StructuredCloneReadInfo()
|
||||
: mDatabase(nullptr)
|
||||
StructuredCloneReadInfo::StructuredCloneReadInfo(JS::StructuredCloneScope aScope)
|
||||
: mData(aScope)
|
||||
, mDatabase(nullptr)
|
||||
, mHasPreprocessInfo(false)
|
||||
{
|
||||
MOZ_COUNT_CTOR(StructuredCloneReadInfo);
|
||||
}
|
||||
|
||||
inline
|
||||
StructuredCloneReadInfo::StructuredCloneReadInfo()
|
||||
: StructuredCloneReadInfo(JS::StructuredCloneScope::DifferentProcessForIndexedDB)
|
||||
{
|
||||
}
|
||||
|
||||
inline
|
||||
StructuredCloneReadInfo::StructuredCloneReadInfo(
|
||||
StructuredCloneReadInfo&& aCloneReadInfo)
|
||||
|
||||
@@ -4616,6 +4616,14 @@ ContentParent::RecvGetFilesRequest(const nsID& aUUID,
|
||||
{
|
||||
MOZ_ASSERT(!mGetFilesPendingRequests.GetWeak(aUUID));
|
||||
|
||||
if (!mozilla::Preferences::GetBool("dom.filesystem.pathcheck.disabled", false)) {
|
||||
RefPtr<FileSystemSecurity> fss = FileSystemSecurity::Get();
|
||||
if (NS_WARN_IF(!fss ||
|
||||
!fss->ContentProcessHasAccessTo(ChildID(), aDirectoryPath))) {
|
||||
return IPC_FAIL_NO_REASON(this);
|
||||
}
|
||||
}
|
||||
|
||||
ErrorResult rv;
|
||||
RefPtr<GetFilesHelper> helper =
|
||||
GetFilesHelperParent::Create(aUUID, aDirectoryPath, aRecursiveFlag, this,
|
||||
|
||||
@@ -90,7 +90,7 @@ StructuredCloneData::Write(JSContext* aCx,
|
||||
return;
|
||||
}
|
||||
|
||||
JSStructuredCloneData data;
|
||||
JSStructuredCloneData data(mBuffer->scope());
|
||||
mBuffer->abandon();
|
||||
mBuffer->steal(&data);
|
||||
mBuffer = nullptr;
|
||||
@@ -197,10 +197,10 @@ BuildClonedMessageData(typename ParentManagerTraits<Flavor, ManagerFlavor>::Conc
|
||||
ClonedMessageData& aClonedData)
|
||||
{
|
||||
SerializedStructuredCloneBuffer& buffer = aClonedData.data();
|
||||
auto iter = aData.Data().Iter();
|
||||
auto iter = aData.Data().Start();
|
||||
size_t size = aData.Data().Size();
|
||||
bool success;
|
||||
buffer.data = aData.Data().Borrow<js::SystemAllocPolicy>(iter, size, &success);
|
||||
buffer.data = aData.Data().Borrow(iter, size, &success);
|
||||
if (NS_WARN_IF(!success)) {
|
||||
return false;
|
||||
}
|
||||
@@ -443,7 +443,7 @@ StructuredCloneData::ReadIPCParams(const IPC::Message* aMsg,
|
||||
PickleIterator* aIter)
|
||||
{
|
||||
MOZ_ASSERT(!mInitialized);
|
||||
JSStructuredCloneData data;
|
||||
JSStructuredCloneData data(JS::StructuredCloneScope::DifferentProcess);
|
||||
if (!ReadParam(aMsg, aIter, &data)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -52,8 +52,8 @@ public:
|
||||
static already_AddRefed<SharedJSAllocatedData>
|
||||
CreateFromExternalData(const char* aData, size_t aDataLength)
|
||||
{
|
||||
JSStructuredCloneData buf;
|
||||
buf.WriteBytes(aData, aDataLength);
|
||||
JSStructuredCloneData buf(JS::StructuredCloneScope::DifferentProcess);
|
||||
buf.AppendBytes(aData, aDataLength);
|
||||
RefPtr<SharedJSAllocatedData> sharedData =
|
||||
new SharedJSAllocatedData(Move(buf));
|
||||
return sharedData.forget();
|
||||
@@ -62,12 +62,8 @@ public:
|
||||
static already_AddRefed<SharedJSAllocatedData>
|
||||
CreateFromExternalData(const JSStructuredCloneData& aData)
|
||||
{
|
||||
JSStructuredCloneData buf;
|
||||
auto iter = aData.Iter();
|
||||
while (!iter.Done()) {
|
||||
buf.WriteBytes(iter.Data(), iter.RemainingInSegment());
|
||||
iter.Advance(aData, iter.RemainingInSegment());
|
||||
}
|
||||
JSStructuredCloneData buf(aData.scope());
|
||||
buf.Append(aData);
|
||||
RefPtr<SharedJSAllocatedData> sharedData =
|
||||
new SharedJSAllocatedData(Move(buf));
|
||||
return sharedData.forget();
|
||||
@@ -229,10 +225,9 @@ public:
|
||||
// StructuredCloneData instance is destroyed before aData is destroyed.
|
||||
bool UseExternalData(const JSStructuredCloneData& aData)
|
||||
{
|
||||
auto iter = aData.Iter();
|
||||
auto iter = aData.Start();
|
||||
bool success = false;
|
||||
mExternalData =
|
||||
aData.Borrow<js::SystemAllocPolicy>(iter, aData.Size(), &success);
|
||||
mExternalData = aData.Borrow(iter, aData.Size(), &success);
|
||||
mInitialized = true;
|
||||
return success;
|
||||
}
|
||||
@@ -261,6 +256,11 @@ public:
|
||||
return mSharedData ? mSharedData->Data() : mExternalData;
|
||||
}
|
||||
|
||||
void InitScope(JS::StructuredCloneScope aScope)
|
||||
{
|
||||
Data().initScope(aScope);
|
||||
}
|
||||
|
||||
size_t DataLength() const
|
||||
{
|
||||
return mSharedData ? mSharedData->DataLength() : mExternalData.Size();
|
||||
@@ -285,6 +285,7 @@ protected:
|
||||
: StructuredCloneHolder(StructuredCloneHolder::CloningSupported,
|
||||
aSupportsTransferring,
|
||||
StructuredCloneHolder::StructuredCloneScope::DifferentProcess)
|
||||
, mExternalData(StructuredCloneHolder::StructuredCloneScope::DifferentProcess)
|
||||
, mInitialized(false)
|
||||
{}
|
||||
|
||||
|
||||
@@ -168,11 +168,15 @@ MediaStreamTrack::Destroy()
|
||||
mPrincipalHandleListener->Forget();
|
||||
mPrincipalHandleListener = nullptr;
|
||||
}
|
||||
for (auto l : mTrackListeners) {
|
||||
RemoveListener(l);
|
||||
// Remove all listeners -- avoid iterating over the list we're removing from
|
||||
const nsTArray<RefPtr<MediaStreamTrackListener>> trackListeners(mTrackListeners);
|
||||
for (auto listener : trackListeners) {
|
||||
RemoveListener(listener);
|
||||
}
|
||||
for (auto l : mDirectTrackListeners) {
|
||||
RemoveDirectListener(l);
|
||||
// Do the same as above for direct listeners
|
||||
const nsTArray<RefPtr<DirectMediaStreamTrackListener>> directTrackListeners(mDirectTrackListeners);
|
||||
for (auto listener : directTrackListeners) {
|
||||
RemoveDirectListener(listener);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -34,7 +34,9 @@ CamerasSingleton::CamerasSingleton()
|
||||
: mCamerasMutex("CamerasSingleton::mCamerasMutex"),
|
||||
mCameras(nullptr),
|
||||
mCamerasChildThread(nullptr),
|
||||
mFakeDeviceChangeEventThread(nullptr) {
|
||||
mFakeDeviceChangeEventThread(nullptr),
|
||||
mInShutdown(false)
|
||||
{
|
||||
LOG(("CamerasSingleton: %p", this));
|
||||
}
|
||||
|
||||
@@ -284,6 +286,7 @@ CamerasChild::NumberOfCapabilities(CaptureEngine aCapEngine,
|
||||
LOG((__PRETTY_FUNCTION__));
|
||||
LOG(("NumberOfCapabilities for %s", deviceUniqueIdUTF8));
|
||||
nsCString unique_id(deviceUniqueIdUTF8);
|
||||
RefPtr<CamerasChild> deathGrip = this;
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
mozilla::NewNonOwningRunnableMethod<CaptureEngine, nsCString>
|
||||
(this, &CamerasChild::SendNumberOfCapabilities, aCapEngine, unique_id);
|
||||
@@ -320,6 +323,7 @@ int
|
||||
CamerasChild::EnsureInitialized(CaptureEngine aCapEngine)
|
||||
{
|
||||
LOG((__PRETTY_FUNCTION__));
|
||||
RefPtr<CamerasChild> deathGrip = this;
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
mozilla::NewNonOwningRunnableMethod<CaptureEngine>
|
||||
(this, &CamerasChild::SendEnsureInitialized, aCapEngine);
|
||||
@@ -335,6 +339,7 @@ CamerasChild::GetCaptureCapability(CaptureEngine aCapEngine,
|
||||
webrtc::VideoCaptureCapability& capability)
|
||||
{
|
||||
LOG(("GetCaptureCapability: %s %d", unique_idUTF8, capability_number));
|
||||
RefPtr<CamerasChild> deathGrip = this;
|
||||
nsCString unique_id(unique_idUTF8);
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
mozilla::NewNonOwningRunnableMethod<CaptureEngine, nsCString, unsigned int>
|
||||
@@ -373,6 +378,7 @@ CamerasChild::GetCaptureDevice(CaptureEngine aCapEngine,
|
||||
bool* scary)
|
||||
{
|
||||
LOG((__PRETTY_FUNCTION__));
|
||||
RefPtr<CamerasChild> deathGrip = this;
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
mozilla::NewNonOwningRunnableMethod<CaptureEngine, unsigned int>
|
||||
(this, &CamerasChild::SendGetCaptureDevice, aCapEngine, list_number);
|
||||
@@ -412,6 +418,7 @@ CamerasChild::AllocateCaptureDevice(CaptureEngine aCapEngine,
|
||||
const mozilla::ipc::PrincipalInfo& aPrincipalInfo)
|
||||
{
|
||||
LOG((__PRETTY_FUNCTION__));
|
||||
RefPtr<CamerasChild> deathGrip = this;
|
||||
nsCString unique_id(unique_idUTF8);
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
mozilla::NewNonOwningRunnableMethod<CaptureEngine, nsCString, const mozilla::ipc::PrincipalInfo&>
|
||||
@@ -442,6 +449,7 @@ CamerasChild::ReleaseCaptureDevice(CaptureEngine aCapEngine,
|
||||
const int capture_id)
|
||||
{
|
||||
LOG((__PRETTY_FUNCTION__));
|
||||
RefPtr<CamerasChild> deathGrip = this;
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
mozilla::NewNonOwningRunnableMethod<CaptureEngine, int>
|
||||
(this, &CamerasChild::SendReleaseCaptureDevice, aCapEngine, capture_id);
|
||||
@@ -489,6 +497,7 @@ CamerasChild::StartCapture(CaptureEngine aCapEngine,
|
||||
webrtcCaps.rawType,
|
||||
webrtcCaps.codecType,
|
||||
webrtcCaps.interlaced);
|
||||
RefPtr<CamerasChild> deathGrip = this;
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
mozilla::NewNonOwningRunnableMethod<CaptureEngine, int, VideoCaptureCapability>
|
||||
(this, &CamerasChild::SendStartCapture, aCapEngine, capture_id, capCap);
|
||||
@@ -500,6 +509,7 @@ int
|
||||
CamerasChild::StopCapture(CaptureEngine aCapEngine, const int capture_id)
|
||||
{
|
||||
LOG((__PRETTY_FUNCTION__));
|
||||
RefPtr<CamerasChild> deathGrip = this;
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
mozilla::NewNonOwningRunnableMethod<CaptureEngine, int>
|
||||
(this, &CamerasChild::SendStopCapture, aCapEngine, capture_id);
|
||||
@@ -565,6 +575,7 @@ CamerasChild::ShutdownParent()
|
||||
// Delete the parent actor.
|
||||
// CamerasChild (this) will remain alive and is only deleted by the
|
||||
// IPC layer when SendAllDone returns.
|
||||
RefPtr<CamerasChild> deathGrip = this;
|
||||
nsCOMPtr<nsIRunnable> deleteRunnable =
|
||||
mozilla::NewNonOwningRunnableMethod(this, &CamerasChild::SendAllDone);
|
||||
CamerasSingleton::Thread()->Dispatch(deleteRunnable, NS_DISPATCH_NORMAL);
|
||||
@@ -687,7 +698,7 @@ CamerasChild::~CamerasChild()
|
||||
{
|
||||
LOG(("~CamerasChild: %p", this));
|
||||
|
||||
{
|
||||
if (!CamerasSingleton::InShutdown()) {
|
||||
OffTheBooksMutexAutoLock lock(CamerasSingleton::Mutex());
|
||||
// In normal circumstances we've already shut down and the
|
||||
// following does nothing. But on fatal IPC errors we will
|
||||
|
||||
@@ -97,6 +97,14 @@ public:
|
||||
return gTheInstance.get()->mFakeDeviceChangeEventThread;
|
||||
}
|
||||
|
||||
static bool InShutdown() {
|
||||
return gTheInstance.get()->mInShutdown;
|
||||
}
|
||||
|
||||
static void StartShutdown() {
|
||||
gTheInstance.get()->mInShutdown = true;
|
||||
}
|
||||
|
||||
private:
|
||||
static Singleton<CamerasSingleton> gTheInstance;
|
||||
|
||||
@@ -114,6 +122,7 @@ private:
|
||||
CamerasChild* mCameras;
|
||||
nsCOMPtr<nsIThread> mCamerasChildThread;
|
||||
nsCOMPtr<nsIThread> mFakeDeviceChangeEventThread;
|
||||
Atomic<bool> mInShutdown;
|
||||
};
|
||||
|
||||
// Get a pointer to a CamerasChild object we can use to do IPC with.
|
||||
@@ -153,7 +162,7 @@ class CamerasChild final : public PCamerasChild
|
||||
public:
|
||||
// We are owned by the PBackground thread only. CamerasSingleton
|
||||
// takes a non-owning reference.
|
||||
NS_INLINE_DECL_REFCOUNTING(CamerasChild)
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CamerasChild)
|
||||
|
||||
// IPC messages recevied, received on the PBackground thread
|
||||
// these are the actual callbacks with data
|
||||
|
||||
@@ -1004,7 +1004,17 @@ nsXBLBinding::DoInitJSClass(JSContext *cx,
|
||||
NS_ENSURE_TRUE(xblScope, NS_ERROR_UNEXPECTED);
|
||||
|
||||
JS::Rooted<JSObject*> parent_proto(cx);
|
||||
if (!JS_GetPrototype(cx, obj, &parent_proto)) {
|
||||
{
|
||||
JS::RootedObject wrapped(cx, obj);
|
||||
JSAutoCompartment ac(cx, xblScope);
|
||||
if (!JS_WrapObject(cx, &wrapped)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
if (!JS_GetPrototype(cx, wrapped, &parent_proto)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
}
|
||||
if (!JS_WrapObject(cx, &parent_proto)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
|
||||
@@ -83,6 +83,41 @@ ComputeYCbCrBufferSize(const gfx::IntSize& aYSize, const gfx::IntSize& aCbCrSize
|
||||
return ComputeYCbCrBufferSize(aYSize, aYSize.width, aCbCrSize, aCbCrSize.width);
|
||||
}
|
||||
|
||||
uint32_t
|
||||
ComputeYCbCrBufferSize(const gfx::IntSize& aYSize, const gfx::IntSize& aCbCrSize,
|
||||
uint32_t aYOffset, uint32_t aCbOffset, uint32_t aCrOffset)
|
||||
{
|
||||
MOZ_ASSERT(aYSize.height >= 0 && aYSize.width >= 0);
|
||||
|
||||
int32_t yStride = aYSize.width;
|
||||
int32_t cbCrStride = aCbCrSize.width;
|
||||
if (aYSize.height < 0 || aYSize.width < 0 || aCbCrSize.height < 0 || aCbCrSize.width < 0 ||
|
||||
!gfx::Factory::AllowedSurfaceSize(IntSize(yStride, aYSize.height)) ||
|
||||
!gfx::Factory::AllowedSurfaceSize(IntSize(cbCrStride, aCbCrSize.height))) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t yLength = GetAlignedStride<4>(yStride, aYSize.height);
|
||||
uint32_t cbCrLength = GetAlignedStride<4>(cbCrStride, aCbCrSize.height);
|
||||
if (yLength == 0 || cbCrLength == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
CheckedInt<uint32_t> yEnd = aYOffset;
|
||||
yEnd += yLength;
|
||||
CheckedInt<uint32_t> cbEnd = aCbOffset;
|
||||
cbEnd += cbCrLength;
|
||||
CheckedInt<uint32_t> crEnd = aCrOffset;
|
||||
crEnd += cbCrLength;
|
||||
|
||||
if (!yEnd.isValid() || !cbEnd.isValid() || !crEnd.isValid() ||
|
||||
yEnd.value() > aCbOffset || cbEnd.value() > aCrOffset) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return crEnd.value();
|
||||
}
|
||||
|
||||
uint32_t
|
||||
ComputeYCbCrBufferSize(uint32_t aBufferSize)
|
||||
{
|
||||
|
||||
@@ -47,7 +47,11 @@ uint32_t ComputeYCbCrBufferSize(const gfx::IntSize& aYSize,
|
||||
int32_t aCbCrStride);
|
||||
uint32_t ComputeYCbCrBufferSize(const gfx::IntSize& aYSize,
|
||||
const gfx::IntSize& aCbCrSize);
|
||||
|
||||
uint32_t ComputeYCbCrBufferSize(const gfx::IntSize& aYSize,
|
||||
const gfx::IntSize& aCbCrSize,
|
||||
uint32_t aYOffset,
|
||||
uint32_t aCbOffset,
|
||||
uint32_t aCrOffset);
|
||||
uint32_t ComputeYCbCrBufferSize(uint32_t aBufferSize);
|
||||
|
||||
void ComputeYCbCrOffsets(int32_t yStride, int32_t yHeight,
|
||||
|
||||
@@ -259,7 +259,9 @@ CreateBackendIndependentTextureHost(const SurfaceDescriptor& aDesc,
|
||||
case BufferDescriptor::TYCbCrDescriptor: {
|
||||
const YCbCrDescriptor& ycbcr = desc.get_YCbCrDescriptor();
|
||||
reqSize =
|
||||
ImageDataSerializer::ComputeYCbCrBufferSize(ycbcr.ySize(), ycbcr.cbCrSize());
|
||||
ImageDataSerializer::ComputeYCbCrBufferSize(ycbcr.ySize(), ycbcr.cbCrSize(),
|
||||
ycbcr.yOffset(), ycbcr.cbOffset(),
|
||||
ycbcr.crOffset());
|
||||
break;
|
||||
}
|
||||
case BufferDescriptor::TRGBDescriptor: {
|
||||
@@ -272,7 +274,7 @@ CreateBackendIndependentTextureHost(const SurfaceDescriptor& aDesc,
|
||||
MOZ_CRASH("GFX: Bad descriptor");
|
||||
}
|
||||
|
||||
if (bufSize < reqSize) {
|
||||
if (reqSize == 0 || bufSize < reqSize) {
|
||||
NS_ERROR("A client process gave a shmem too small to fit for its descriptor!");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -43,7 +43,12 @@ uint8_t* SkMask::AllocImage(size_t size) {
|
||||
#ifdef TRACK_SKMASK_LIFETIME
|
||||
SkDebugf("SkMask::AllocImage %d\n", gCounter++);
|
||||
#endif
|
||||
return (uint8_t*)sk_malloc_throw(SkAlign4(size));
|
||||
size_t aligned_size = std::numeric_limits<size_t>::max();
|
||||
size_t adjustment = 3;
|
||||
if (size + adjustment > size) {
|
||||
aligned_size = (size + adjustment) & ~adjustment;
|
||||
}
|
||||
return static_cast<uint8_t*>(sk_malloc_throw(aligned_size));
|
||||
}
|
||||
|
||||
/** We explicitly use this allocator for SkBimap pixels, so that we can
|
||||
|
||||
@@ -591,16 +591,10 @@ static bool clip_to_limit(const SkRegion& orig, SkRegion* reduced) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Variants of SkScalarRoundToInt, identical to SkDScalarRoundToInt except when the input fraction
|
||||
* is 0.5. When SK_RASTERIZE_EVEN_ROUNDING is enabled, we must bias the result before rounding to
|
||||
* account for potential FDot6 rounding edge-cases.
|
||||
*/
|
||||
#ifdef SK_RASTERIZE_EVEN_ROUNDING
|
||||
static const double kRoundBias = 0.5 / SK_FDot6One;
|
||||
#else
|
||||
static const double kRoundBias = 0.0;
|
||||
#endif
|
||||
// Bias used for conservative rounding of float rects to int rects, to nudge the irects a little
|
||||
// larger, so we don't "think" a path's bounds are inside a clip, when (due to numeric drift in
|
||||
// the scan-converter) we might walk beyond the predicted limits.
|
||||
static const double kConservativeRoundBias = 0.5 + 1.5 / SK_FDot6One;
|
||||
|
||||
/**
|
||||
* Round the value down. This is used to round the top and left of a rectangle,
|
||||
@@ -608,8 +602,8 @@ static const double kRoundBias = 0.0;
|
||||
*/
|
||||
static inline int round_down_to_int(SkScalar x) {
|
||||
double xx = x;
|
||||
xx -= 0.5 + kRoundBias;
|
||||
return (int)ceil(xx);
|
||||
xx -= kConservativeRoundBias;
|
||||
return pin_double_to_int(ceil(xx));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -618,8 +612,8 @@ static inline int round_down_to_int(SkScalar x) {
|
||||
*/
|
||||
static inline int round_up_to_int(SkScalar x) {
|
||||
double xx = x;
|
||||
xx += 0.5 + kRoundBias;
|
||||
return (int)floor(xx);
|
||||
xx += kConservativeRoundBias;
|
||||
return pin_double_to_int(floor(xx));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "GrResourceProvider.h"
|
||||
#include "GrTypes.h"
|
||||
|
||||
#include "SkSafeMath.h"
|
||||
#include "SkTraceEvent.h"
|
||||
|
||||
#ifdef SK_DEBUG
|
||||
@@ -335,7 +336,7 @@ void* GrVertexBufferAllocPool::makeSpace(size_t vertexSize,
|
||||
SkASSERT(startVertex);
|
||||
|
||||
size_t offset = 0; // assign to suppress warning
|
||||
void* ptr = INHERITED::makeSpace(vertexSize * vertexCount,
|
||||
void* ptr = INHERITED::makeSpace(SkSafeMath::Mul(vertexSize, vertexCount),
|
||||
vertexSize,
|
||||
buffer,
|
||||
&offset);
|
||||
@@ -360,7 +361,7 @@ void* GrIndexBufferAllocPool::makeSpace(int indexCount,
|
||||
SkASSERT(startIndex);
|
||||
|
||||
size_t offset = 0; // assign to suppress warning
|
||||
void* ptr = INHERITED::makeSpace(indexCount * sizeof(uint16_t),
|
||||
void* ptr = INHERITED::makeSpace(SkSafeMath::Mul(indexCount, sizeof(uint16_t)),
|
||||
sizeof(uint16_t),
|
||||
buffer,
|
||||
&offset);
|
||||
|
||||
@@ -828,6 +828,13 @@ void AAHairlineBatch::onPrepareDraws(Target* target) const {
|
||||
|
||||
int lineCount = lines.count() / 2;
|
||||
int conicCount = conics.count() / 3;
|
||||
int quadAndConicCount = conicCount + quadCount;
|
||||
|
||||
static constexpr int kMaxLines = SK_MaxS32 / kLineSegNumVertices;
|
||||
static constexpr int kMaxQuadsAndConics = SK_MaxS32 / kQuadNumVertices;
|
||||
if (lineCount > kMaxLines || quadAndConicCount > kMaxQuadsAndConics) {
|
||||
return;
|
||||
}
|
||||
|
||||
// do lines first
|
||||
if (lineCount) {
|
||||
@@ -899,7 +906,7 @@ void AAHairlineBatch::onPrepareDraws(Target* target) const {
|
||||
ref_quads_index_buffer(target->resourceProvider()));
|
||||
|
||||
size_t vertexStride = sizeof(BezierVertex);
|
||||
int vertexCount = kQuadNumVertices * quadCount + kQuadNumVertices * conicCount;
|
||||
int vertexCount = kQuadNumVertices * quadAndConicCount;
|
||||
void *vertices = target->makeVertexSpace(vertexStride, vertexCount,
|
||||
&vertexBuffer, &firstVertex);
|
||||
|
||||
|
||||
@@ -688,7 +688,17 @@ nsMozIconURI::Deserialize(const URIParams& aParams)
|
||||
mContentType = params.contentType();
|
||||
mFileName = params.fileName();
|
||||
mStockIcon = params.stockIcon();
|
||||
|
||||
if (params.iconSize() < -1 ||
|
||||
params.iconSize() >= (int32_t) ArrayLength(kSizeStrings)) {
|
||||
return false;
|
||||
}
|
||||
mIconSize = params.iconSize();
|
||||
|
||||
if (params.iconState() < -1 ||
|
||||
params.iconState() >= (int32_t) ArrayLength(kStateStrings)) {
|
||||
return false;
|
||||
}
|
||||
mIconState = params.iconState();
|
||||
|
||||
return true;
|
||||
|
||||
+12
-11
@@ -63,15 +63,18 @@ struct null_t {
|
||||
|
||||
struct SerializedStructuredCloneBuffer final
|
||||
{
|
||||
SerializedStructuredCloneBuffer()
|
||||
: data(JS::StructuredCloneScope::Unassigned)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
SerializedStructuredCloneBuffer&
|
||||
operator=(const SerializedStructuredCloneBuffer& aOther)
|
||||
{
|
||||
data.Clear();
|
||||
auto iter = aOther.data.Iter();
|
||||
while (!iter.Done()) {
|
||||
data.WriteBytes(iter.Data(), iter.RemainingInSegment());
|
||||
iter.Advance(aOther.data, iter.RemainingInSegment());
|
||||
}
|
||||
data.initScope(aOther.data.scope());
|
||||
data.Append(aOther.data);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -728,11 +731,9 @@ struct ParamTraits<JSStructuredCloneData>
|
||||
{
|
||||
MOZ_ASSERT(!(aParam.Size() % sizeof(uint64_t)));
|
||||
WriteParam(aMsg, aParam.Size());
|
||||
auto iter = aParam.Iter();
|
||||
while (!iter.Done()) {
|
||||
aMsg->WriteBytes(iter.Data(), iter.RemainingInSegment(), sizeof(uint64_t));
|
||||
iter.Advance(aParam, iter.RemainingInSegment());
|
||||
}
|
||||
aParam.ForEachDataChunk([&](const char* aData, size_t aSize) {
|
||||
return aMsg->WriteBytes(aData, aSize, sizeof(uint64_t));
|
||||
});
|
||||
}
|
||||
|
||||
static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
|
||||
@@ -762,7 +763,7 @@ struct ParamTraits<JSStructuredCloneData>
|
||||
return false;
|
||||
}
|
||||
|
||||
*aResult = JSStructuredCloneData(Move(out));
|
||||
*aResult = JSStructuredCloneData(Move(out), JS::StructuredCloneScope::DifferentProcess);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
+178
-34
@@ -9,6 +9,7 @@
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/BufferList.h"
|
||||
#include "mozilla/Move.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
@@ -30,7 +31,35 @@ namespace JS {
|
||||
enum class StructuredCloneScope : uint32_t {
|
||||
SameProcessSameThread,
|
||||
SameProcessDifferentThread,
|
||||
DifferentProcess
|
||||
|
||||
/**
|
||||
* When writing, this means we're writing for an audience in a different
|
||||
* process. Produce serialized data that can be sent to other processes,
|
||||
* bitwise copied, or even stored as bytes in a database and read by later
|
||||
* versions of Firefox years from now. The HTML5 spec refers to this as
|
||||
* "ForStorage" as in StructuredSerializeForStorage, though we use
|
||||
* DifferentProcess for IPC as well as storage.
|
||||
*
|
||||
* Transferable objects are limited to ArrayBuffers, whose contents are
|
||||
* copied into the serialized data (rather than just writing a pointer).
|
||||
*/
|
||||
DifferentProcess,
|
||||
|
||||
/**
|
||||
* Handle a backwards-compatibility case with IndexedDB (bug 1434308): when
|
||||
* reading, this means to treat legacy SameProcessSameThread data as if it
|
||||
* were DifferentProcess.
|
||||
*
|
||||
* Do not use this for writing; use DifferentProcess instead.
|
||||
*/
|
||||
DifferentProcessForIndexedDB,
|
||||
|
||||
/**
|
||||
* Existing code wants to be able to create an uninitialized
|
||||
* JSStructuredCloneData without knowing the scope, then populate it with
|
||||
* data (at which point the scope *is* known.)
|
||||
*/
|
||||
Unassigned
|
||||
};
|
||||
|
||||
enum TransferableOwnership {
|
||||
@@ -90,6 +119,10 @@ class CloneDataPolicy
|
||||
|
||||
} /* namespace JS */
|
||||
|
||||
namespace js {
|
||||
template <typename T, typename AllocPolicy> struct BufferIterator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read structured data from the reader r. This hook is used to read a value
|
||||
* previously serialized by a call to the WriteStructuredCloneOp hook.
|
||||
@@ -211,50 +244,153 @@ namespace js
|
||||
};
|
||||
}
|
||||
|
||||
class MOZ_NON_MEMMOVABLE JS_PUBLIC_API(JSStructuredCloneData) :
|
||||
public mozilla::BufferList<js::SystemAllocPolicy>
|
||||
{
|
||||
typedef js::SystemAllocPolicy AllocPolicy;
|
||||
typedef mozilla::BufferList<js::SystemAllocPolicy> BufferList;
|
||||
/**
|
||||
* JSStructuredCloneData represents structured clone data together with the
|
||||
* information needed to read/write/transfer/free the records within it, in the
|
||||
* form of a set of callbacks.
|
||||
*/
|
||||
class MOZ_NON_MEMMOVABLE JS_PUBLIC_API(JSStructuredCloneData) {
|
||||
public:
|
||||
using BufferList = mozilla::BufferList<js::SystemAllocPolicy>;
|
||||
using Iterator = BufferList::IterImpl;
|
||||
|
||||
static const size_t kInitialSize = 0;
|
||||
static const size_t kInitialCapacity = 4096;
|
||||
private:
|
||||
static const size_t kStandardCapacity = 4096;
|
||||
|
||||
BufferList bufList_;
|
||||
|
||||
// The (address space, thread) scope within which this clone is valid. Note
|
||||
// that this must be either set during construction, or start out as
|
||||
// Unassigned and transition once to something else.
|
||||
JS::StructuredCloneScope scope_;
|
||||
|
||||
const JSStructuredCloneCallbacks* callbacks_;
|
||||
void* closure_;
|
||||
OwnTransferablePolicy ownTransferables_;
|
||||
js::SharedArrayRawBufferRefs refsHeld_;
|
||||
|
||||
void setOptionalCallbacks(const JSStructuredCloneCallbacks* callbacks,
|
||||
void* closure,
|
||||
OwnTransferablePolicy policy) {
|
||||
callbacks_ = callbacks;
|
||||
closure_ = closure;
|
||||
ownTransferables_ = policy;
|
||||
}
|
||||
|
||||
friend struct JSStructuredCloneWriter;
|
||||
friend class JS_PUBLIC_API(JSAutoStructuredCloneBuffer);
|
||||
template <typename T, typename AllocPolicy> friend struct js::BufferIterator;
|
||||
|
||||
public:
|
||||
explicit JSStructuredCloneData(AllocPolicy aAP = AllocPolicy())
|
||||
: BufferList(kInitialSize, kInitialCapacity, kStandardCapacity, aAP)
|
||||
public:
|
||||
// The constructor must be infallible but SystemAllocPolicy is not, so both
|
||||
// the initial size and initial capacity of the BufferList must be zero.
|
||||
explicit JSStructuredCloneData(JS::StructuredCloneScope aScope)
|
||||
: bufList_(0, 0, kStandardCapacity, js::SystemAllocPolicy())
|
||||
, scope_(aScope)
|
||||
, callbacks_(nullptr)
|
||||
, closure_(nullptr)
|
||||
, ownTransferables_(OwnTransferablePolicy::NoTransferables)
|
||||
{}
|
||||
|
||||
// Steal the raw data from a BufferList. In this case, we don't know the
|
||||
// scope and none of the callback info is assigned yet.
|
||||
JSStructuredCloneData(BufferList&& buffers, JS::StructuredCloneScope aScope)
|
||||
: bufList_(mozilla::Move(buffers))
|
||||
, scope_(aScope)
|
||||
, callbacks_(nullptr)
|
||||
, closure_(nullptr)
|
||||
, ownTransferables_(OwnTransferablePolicy::NoTransferables)
|
||||
{}
|
||||
MOZ_IMPLICIT JSStructuredCloneData(BufferList&& buffers)
|
||||
: BufferList(Move(buffers))
|
||||
, callbacks_(nullptr)
|
||||
, closure_(nullptr)
|
||||
, ownTransferables_(OwnTransferablePolicy::NoTransferables)
|
||||
: JSStructuredCloneData(mozilla::Move(buffers), JS::StructuredCloneScope::Unassigned)
|
||||
{}
|
||||
JSStructuredCloneData(JSStructuredCloneData&& other) = default;
|
||||
JSStructuredCloneData& operator=(JSStructuredCloneData&& other) = default;
|
||||
~JSStructuredCloneData();
|
||||
~JSStructuredCloneData() { discardTransferables(); }
|
||||
|
||||
using BufferList::BufferList;
|
||||
void setCallbacks(const JSStructuredCloneCallbacks* callbacks,
|
||||
void* closure,
|
||||
OwnTransferablePolicy policy)
|
||||
{
|
||||
callbacks_ = callbacks;
|
||||
closure_ = closure;
|
||||
ownTransferables_ = policy;
|
||||
}
|
||||
|
||||
JS::StructuredCloneScope scope() const { return scope_; }
|
||||
|
||||
void initScope(JS::StructuredCloneScope aScope) {
|
||||
MOZ_ASSERT(Size() == 0, "initScope() of nonempty JSStructuredCloneData");
|
||||
if (scope_ != JS::StructuredCloneScope::Unassigned)
|
||||
MOZ_ASSERT(scope_ == aScope, "Cannot change scope after it has been initialized");
|
||||
scope_ = aScope;
|
||||
}
|
||||
|
||||
size_t Size() const { return bufList_.Size(); }
|
||||
|
||||
const Iterator Start() const { return bufList_.Iter(); }
|
||||
|
||||
bool Advance(Iterator& iter, size_t distance) const {
|
||||
return iter.AdvanceAcrossSegments(bufList_, distance);
|
||||
}
|
||||
|
||||
bool ReadBytes(Iterator& iter, char* buffer, size_t size) const {
|
||||
return bufList_.ReadBytes(iter, buffer, size);
|
||||
}
|
||||
|
||||
// Append new data to the end of the buffer.
|
||||
bool AppendBytes(const char* data, size_t size) {
|
||||
MOZ_ASSERT(scope_ != JS::StructuredCloneScope::Unassigned);
|
||||
return bufList_.WriteBytes(data, size);
|
||||
}
|
||||
|
||||
// Update data stored within the existing buffer. There must be at least
|
||||
// 'size' bytes between the position of 'iter' and the end of the buffer.
|
||||
bool UpdateBytes(Iterator& iter, const char* data, size_t size) const {
|
||||
MOZ_ASSERT(scope_ != JS::StructuredCloneScope::Unassigned);
|
||||
while (size > 0) {
|
||||
size_t remaining = iter.RemainingInSegment();
|
||||
size_t nbytes = std::min(remaining, size);
|
||||
memcpy(iter.Data(), data, nbytes);
|
||||
data += nbytes;
|
||||
size -= nbytes;
|
||||
iter.Advance(bufList_, nbytes);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Clear() {
|
||||
discardTransferables();
|
||||
bufList_.Clear();
|
||||
}
|
||||
|
||||
// Return a new read-only JSStructuredCloneData that "borrows" the contents
|
||||
// of |this|. Its lifetime should not exceed the donor's. This is only
|
||||
// allowed for DifferentProcess clones, so finalization of the borrowing
|
||||
// clone will do nothing.
|
||||
JSStructuredCloneData Borrow(Iterator& iter, size_t size, bool* success) const
|
||||
{
|
||||
MOZ_ASSERT(scope_ == JS::StructuredCloneScope::DifferentProcess);
|
||||
return JSStructuredCloneData(bufList_.Borrow<js::SystemAllocPolicy>(iter, size, success),
|
||||
scope_);
|
||||
}
|
||||
|
||||
// Iterate over all contained data, one BufferList segment's worth at a
|
||||
// time, and invoke the given FunctionToApply with the data pointer and
|
||||
// size. The function should return a bool value, and this loop will exit
|
||||
// with false if the function ever returns false.
|
||||
template <typename FunctionToApply>
|
||||
bool ForEachDataChunk(FunctionToApply&& function) const {
|
||||
Iterator iter = bufList_.Iter();
|
||||
while (!iter.Done()) {
|
||||
if (!function(iter.Data(), iter.RemainingInSegment()))
|
||||
return false;
|
||||
iter.Advance(bufList_, iter.RemainingInSegment());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Append the entire contents of other's bufList_ to our own.
|
||||
bool Append(const JSStructuredCloneData& other) {
|
||||
MOZ_ASSERT(scope_ == other.scope_);
|
||||
return other.ForEachDataChunk([&](const char* data, size_t size) {
|
||||
return AppendBytes(data, size);
|
||||
});
|
||||
}
|
||||
|
||||
void discardTransferables();
|
||||
};
|
||||
|
||||
/** Note: if the *data contains transferable objects, it can be read only once. */
|
||||
@@ -278,18 +414,29 @@ JS_PUBLIC_API(bool)
|
||||
JS_StructuredClone(JSContext* cx, JS::HandleValue v, JS::MutableHandleValue vp,
|
||||
const JSStructuredCloneCallbacks* optionalCallbacks, void* closure);
|
||||
|
||||
/** RAII sugar for JS_WriteStructuredClone. */
|
||||
/**
|
||||
* The C-style API calls to read and write structured clones are fragile --
|
||||
* they rely on the caller to properly handle ownership of the clone data, and
|
||||
* the handling of the input data as well as the interpretation of the contents
|
||||
* of the clone buffer are dependent on the callbacks passed in. If you
|
||||
* serialize and deserialize with different callbacks, the results are
|
||||
* questionable.
|
||||
*
|
||||
* JSAutoStructuredCloneBuffer wraps things up in an RAII class for data
|
||||
* management, and uses the same callbacks for both writing and reading
|
||||
* (serializing and deserializing).
|
||||
*/
|
||||
class JS_PUBLIC_API(JSAutoStructuredCloneBuffer) {
|
||||
const JS::StructuredCloneScope scope_;
|
||||
JSStructuredCloneData data_;
|
||||
uint32_t version_;
|
||||
|
||||
public:
|
||||
JSAutoStructuredCloneBuffer(JS::StructuredCloneScope scope,
|
||||
JSAutoStructuredCloneBuffer(JS::StructuredCloneScope aScope,
|
||||
const JSStructuredCloneCallbacks* callbacks, void* closure)
|
||||
: scope_(scope), version_(JS_STRUCTURED_CLONE_VERSION)
|
||||
: scope_(aScope), data_(aScope), version_(JS_STRUCTURED_CLONE_VERSION)
|
||||
{
|
||||
data_.setOptionalCallbacks(callbacks, closure, OwnTransferablePolicy::NoTransferables);
|
||||
data_.setCallbacks(callbacks, closure, OwnTransferablePolicy::NoTransferables);
|
||||
}
|
||||
|
||||
JSAutoStructuredCloneBuffer(JSAutoStructuredCloneBuffer&& other);
|
||||
@@ -300,12 +447,9 @@ class JS_PUBLIC_API(JSAutoStructuredCloneBuffer) {
|
||||
JSStructuredCloneData& data() { return data_; }
|
||||
bool empty() const { return !data_.Size(); }
|
||||
|
||||
void clear(const JSStructuredCloneCallbacks* optionalCallbacks=nullptr, void* closure=nullptr);
|
||||
void clear();
|
||||
|
||||
/** Copy some memory. It will be automatically freed by the destructor. */
|
||||
bool copy(JSContext* cx, const JSStructuredCloneData& data,
|
||||
uint32_t version=JS_STRUCTURED_CLONE_VERSION,
|
||||
const JSStructuredCloneCallbacks* callbacks=nullptr, void* closure=nullptr);
|
||||
JS::StructuredCloneScope scope() const { return scope_; }
|
||||
|
||||
/**
|
||||
* Adopt some memory. It will be automatically freed by the destructor.
|
||||
|
||||
@@ -711,13 +711,14 @@ function CreateArrayIterator(obj, kind) {
|
||||
// http://www.ecma-international.org/ecma-262/6.0/index.html#sec-%arrayiteratorprototype%.next
|
||||
function ArrayIteratorNext() {
|
||||
// Step 1-3.
|
||||
if (!IsObject(this) || !IsArrayIterator(this)) {
|
||||
var obj;
|
||||
if (!IsObject(this) || (obj = GuardToArrayIterator(this)) === null) {
|
||||
return callFunction(CallArrayIteratorMethodIfWrapped, this,
|
||||
"ArrayIteratorNext");
|
||||
}
|
||||
|
||||
// Step 4.
|
||||
var a = UnsafeGetReservedSlot(this, ITERATOR_SLOT_TARGET);
|
||||
var a = UnsafeGetReservedSlot(obj, ITERATOR_SLOT_TARGET);
|
||||
var result = { value: undefined, done: false };
|
||||
|
||||
// Step 5.
|
||||
@@ -728,10 +729,10 @@ function ArrayIteratorNext() {
|
||||
|
||||
// Step 6.
|
||||
// The index might not be an integer, so we have to do a generic get here.
|
||||
var index = UnsafeGetReservedSlot(this, ITERATOR_SLOT_NEXT_INDEX);
|
||||
var index = UnsafeGetReservedSlot(obj, ITERATOR_SLOT_NEXT_INDEX);
|
||||
|
||||
// Step 7.
|
||||
var itemKind = UnsafeGetInt32FromReservedSlot(this, ITERATOR_SLOT_ITEM_KIND);
|
||||
var itemKind = UnsafeGetInt32FromReservedSlot(obj, ITERATOR_SLOT_ITEM_KIND);
|
||||
|
||||
// Step 8-9.
|
||||
var len;
|
||||
@@ -746,13 +747,13 @@ function ArrayIteratorNext() {
|
||||
|
||||
// Step 10.
|
||||
if (index >= len) {
|
||||
UnsafeSetReservedSlot(this, ITERATOR_SLOT_TARGET, null);
|
||||
UnsafeSetReservedSlot(obj, ITERATOR_SLOT_TARGET, null);
|
||||
result.done = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
// Step 11.
|
||||
UnsafeSetReservedSlot(this, ITERATOR_SLOT_NEXT_INDEX, index + 1);
|
||||
UnsafeSetReservedSlot(obj, ITERATOR_SLOT_NEXT_INDEX, index + 1);
|
||||
|
||||
// Step 16.
|
||||
if (itemKind === ITEM_KIND_VALUE) {
|
||||
|
||||
@@ -67,8 +67,8 @@ function MapIteratorNext() {
|
||||
var O = this;
|
||||
|
||||
// Steps 2-3.
|
||||
if (!IsObject(O) || !IsMapIterator(O))
|
||||
return callFunction(CallMapIteratorMethodIfWrapped, O, "MapIteratorNext");
|
||||
if (!IsObject(O) || (O = GuardToMapIterator(O)) === null)
|
||||
return callFunction(CallMapIteratorMethodIfWrapped, this, "MapIteratorNext");
|
||||
|
||||
// Steps 4-5 (implemented in _GetNextMapEntryForIterator).
|
||||
// Steps 8-9 (omitted).
|
||||
@@ -87,7 +87,7 @@ function MapIteratorNext() {
|
||||
// Steps 10.b-c (omitted).
|
||||
|
||||
// Step 6.
|
||||
var itemKind = UnsafeGetInt32FromReservedSlot(this, ITERATOR_SLOT_ITEM_KIND);
|
||||
var itemKind = UnsafeGetInt32FromReservedSlot(O, ITERATOR_SLOT_ITEM_KIND);
|
||||
|
||||
var result;
|
||||
if (itemKind === ITEM_KIND_KEY) {
|
||||
|
||||
@@ -69,8 +69,8 @@ function SetIteratorNext() {
|
||||
var O = this;
|
||||
|
||||
// Steps 2-3.
|
||||
if (!IsObject(O) || !IsSetIterator(O))
|
||||
return callFunction(CallSetIteratorMethodIfWrapped, O, "SetIteratorNext");
|
||||
if (!IsObject(O) || (O = GuardToSetIterator(O)) === null)
|
||||
return callFunction(CallSetIteratorMethodIfWrapped, this, "SetIteratorNext");
|
||||
|
||||
// Steps 4-5 (implemented in _GetNextSetEntryForIterator).
|
||||
// Steps 8-9 (omitted).
|
||||
@@ -88,7 +88,7 @@ function SetIteratorNext() {
|
||||
// Steps 10.b-c (omitted).
|
||||
|
||||
// Step 6.
|
||||
var itemKind = UnsafeGetInt32FromReservedSlot(this, ITERATOR_SLOT_ITEM_KIND);
|
||||
var itemKind = UnsafeGetInt32FromReservedSlot(O, ITERATOR_SLOT_ITEM_KIND);
|
||||
|
||||
var result;
|
||||
if (itemKind === ITEM_KIND_VALUE) {
|
||||
|
||||
@@ -536,16 +536,17 @@ function String_iterator() {
|
||||
}
|
||||
|
||||
function StringIteratorNext() {
|
||||
if (!IsObject(this) || !IsStringIterator(this)) {
|
||||
var obj;
|
||||
if (!IsObject(this) || (obj = GuardToStringIterator(this)) === null) {
|
||||
return callFunction(CallStringIteratorMethodIfWrapped, this,
|
||||
"StringIteratorNext");
|
||||
}
|
||||
|
||||
var S = UnsafeGetStringFromReservedSlot(this, ITERATOR_SLOT_TARGET);
|
||||
var S = UnsafeGetStringFromReservedSlot(obj, ITERATOR_SLOT_TARGET);
|
||||
// We know that JSString::MAX_LENGTH <= INT32_MAX (and assert this in
|
||||
// SelfHostring.cpp) so our current index can never be anything other than
|
||||
// an Int32Value.
|
||||
var index = UnsafeGetInt32FromReservedSlot(this, ITERATOR_SLOT_NEXT_INDEX);
|
||||
var index = UnsafeGetInt32FromReservedSlot(obj, ITERATOR_SLOT_NEXT_INDEX);
|
||||
var size = S.length;
|
||||
var result = { value: undefined, done: false };
|
||||
|
||||
@@ -563,7 +564,7 @@ function StringIteratorNext() {
|
||||
}
|
||||
}
|
||||
|
||||
UnsafeSetReservedSlot(this, ITERATOR_SLOT_NEXT_INDEX, index + charCount);
|
||||
UnsafeSetReservedSlot(obj, ITERATOR_SLOT_NEXT_INDEX, index + charCount);
|
||||
result.value = callFunction(String_substring, S, index, index + charCount);
|
||||
|
||||
return result;
|
||||
|
||||
@@ -2060,7 +2060,7 @@ class CloneBufferObject : public NativeObject {
|
||||
Rooted<CloneBufferObject*> obj(cx, Create(cx));
|
||||
if (!obj)
|
||||
return nullptr;
|
||||
auto data = js::MakeUnique<JSStructuredCloneData>();
|
||||
auto data = js::MakeUnique<JSStructuredCloneData>(buffer->scope());
|
||||
if (!data) {
|
||||
ReportOutOfMemory(cx);
|
||||
return nullptr;
|
||||
@@ -2113,8 +2113,11 @@ class CloneBufferObject : public NativeObject {
|
||||
return false;
|
||||
size_t nbytes = JS_GetStringLength(args[0].toString());
|
||||
MOZ_ASSERT(nbytes % sizeof(uint64_t) == 0);
|
||||
auto buf = js::MakeUnique<JSStructuredCloneData>(nbytes, nbytes, nbytes);
|
||||
js_memcpy(buf->Start(), str, nbytes);
|
||||
auto buf = js::MakeUnique<JSStructuredCloneData>(JS::StructuredCloneScope::DifferentProcess);
|
||||
if (!buf->AppendBytes(str, nbytes)) {
|
||||
ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
JS_free(cx, str);
|
||||
obj->setData(buf.release());
|
||||
|
||||
@@ -2158,7 +2161,7 @@ class CloneBufferObject : public NativeObject {
|
||||
ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
auto iter = obj->data()->Iter();
|
||||
auto iter = obj->data()->Start();
|
||||
obj->data()->ReadBytes(iter, buffer.get(), size);
|
||||
JSString* str = JS_NewStringCopyN(cx, buffer.get(), size);
|
||||
if (!str)
|
||||
@@ -2216,6 +2219,8 @@ ParseCloneScope(JSContext* cx, HandleString str)
|
||||
scope.emplace(JS::StructuredCloneScope::SameProcessDifferentThread);
|
||||
else if (strcmp(scopeStr.ptr(), "DifferentProcess") == 0)
|
||||
scope.emplace(JS::StructuredCloneScope::DifferentProcess);
|
||||
else if (strcmp(scopeStr.ptr(), "DifferentProcessForIndexedDB") == 0)
|
||||
scope.emplace(JS::StructuredCloneScope::DifferentProcessForIndexedDB);
|
||||
|
||||
return scope;
|
||||
}
|
||||
@@ -4346,19 +4351,22 @@ JS_FN_HELP("rejectPromise", RejectPromise, 2, 0,
|
||||
" clone buffer object. 'policy' may be an options hash. Valid keys:\n"
|
||||
" 'SharedArrayBuffer' - either 'allow' (the default) or 'deny'\n"
|
||||
" to specify whether SharedArrayBuffers may be serialized.\n"
|
||||
"\n"
|
||||
" 'scope' - SameProcessSameThread, SameProcessDifferentThread, or\n"
|
||||
" DifferentProcess. Determines how some values will be serialized.\n"
|
||||
" Clone buffers may only be deserialized with a compatible scope."),
|
||||
" 'scope' - SameProcessSameThread, SameProcessDifferentThread,\n"
|
||||
" DifferentProcess, or DifferentProcessForIndexedDB. Determines how some\n"
|
||||
" values will be serialized. Clone buffers may only be deserialized with a\n"
|
||||
" compatible scope. NOTE - For DifferentProcess/DifferentProcessForIndexedDB,\n"
|
||||
" must also set SharedArrayBuffer:'deny' if data contains any shared memory\n"
|
||||
" object."),
|
||||
|
||||
JS_FN_HELP("deserialize", Deserialize, 1, 0,
|
||||
"deserialize(clonebuffer[, opts])",
|
||||
" Deserialize data generated by serialize. 'opts' is an options hash with one\n"
|
||||
" recognized key 'scope', which limits the clone buffers that are considered\n"
|
||||
" valid. Allowed values: 'SameProcessSameThread', 'SameProcessDifferentThread',\n"
|
||||
" and 'DifferentProcess'. So for example, a DifferentProcess clone buffer\n"
|
||||
" may be deserialized in any scope, but a SameProcessSameThread clone buffer\n"
|
||||
" cannot be deserialized in a DifferentProcess scope."),
|
||||
" 'DifferentProcess', and 'DifferentProcessForIndexedDB'. So for example, a\n"
|
||||
" DifferentProcessForIndexedDB clone buffer may be deserialized in any scope, but\n"
|
||||
" a SameProcessSameThread clone buffer cannot be deserialized in a\n"
|
||||
" DifferentProcess scope."),
|
||||
|
||||
JS_FN_HELP("detachArrayBuffer", DetachArrayBuffer, 1, 0,
|
||||
"detachArrayBuffer(buffer)",
|
||||
|
||||
@@ -73,7 +73,7 @@ class ChunkPool
|
||||
|
||||
// Performs extra allocation off the main thread so that when memory is
|
||||
// required on the main thread it will already be available and waiting.
|
||||
class BackgroundAllocTask : public GCParallelTask
|
||||
class BackgroundAllocTask : public GCParallelTaskHelper<BackgroundAllocTask>
|
||||
{
|
||||
// Guarded by the GC lock.
|
||||
JSRuntime* runtime;
|
||||
@@ -85,12 +85,11 @@ class BackgroundAllocTask : public GCParallelTask
|
||||
BackgroundAllocTask(JSRuntime* rt, ChunkPool& pool);
|
||||
bool enabled() const { return enabled_; }
|
||||
|
||||
protected:
|
||||
void run() override;
|
||||
void run();
|
||||
};
|
||||
|
||||
// Search the provided Chunks for free arenas and decommit them.
|
||||
class BackgroundDecommitTask : public GCParallelTask
|
||||
// Search the provided Chunks for free arenas and recommit them.
|
||||
class BackgroundDecommitTask : public GCParallelTaskHelper<BackgroundDecommitTask>
|
||||
{
|
||||
public:
|
||||
using ChunkVector = mozilla::Vector<Chunk*>;
|
||||
@@ -98,8 +97,7 @@ class BackgroundDecommitTask : public GCParallelTask
|
||||
explicit BackgroundDecommitTask(JSRuntime *rt) : runtime(rt) {}
|
||||
void setChunksToScan(ChunkVector &chunks);
|
||||
|
||||
protected:
|
||||
void run() override;
|
||||
void run();
|
||||
|
||||
private:
|
||||
JSRuntime* runtime;
|
||||
@@ -1171,8 +1169,10 @@ class GCRuntime
|
||||
/*
|
||||
* Concurrent sweep infrastructure.
|
||||
*/
|
||||
void startTask(GCParallelTask& task, gcstats::Phase phase, AutoLockHelperThreadState& locked);
|
||||
void joinTask(GCParallelTask& task, gcstats::Phase phase, AutoLockHelperThreadState& locked);
|
||||
void startTask(GCParallelTask& task, gcstats::Phase phase,
|
||||
AutoLockHelperThreadState& locked);
|
||||
void joinTask(GCParallelTask& task, gcstats::Phase phase,
|
||||
AutoLockHelperThreadState& locked);
|
||||
|
||||
/*
|
||||
* List head of arenas allocated during the sweep phase.
|
||||
|
||||
@@ -44,19 +44,19 @@ using mozilla::TimeStamp;
|
||||
|
||||
static const uintptr_t CanaryMagicValue = 0xDEADB15D;
|
||||
|
||||
struct js::Nursery::FreeMallocedBuffersTask : public GCParallelTask
|
||||
struct js::Nursery::FreeMallocedBuffersTask : public GCParallelTaskHelper<FreeMallocedBuffersTask>
|
||||
{
|
||||
explicit FreeMallocedBuffersTask(FreeOp* fop) : fop_(fop) {}
|
||||
bool init() { return buffers_.init(); }
|
||||
void transferBuffersToFree(MallocedBuffersSet& buffersToFree,
|
||||
const AutoLockHelperThreadState& lock);
|
||||
~FreeMallocedBuffersTask() override { join(); }
|
||||
~FreeMallocedBuffersTask() { join(); }
|
||||
|
||||
void run();
|
||||
|
||||
private:
|
||||
FreeOp* fop_;
|
||||
MallocedBuffersSet buffers_;
|
||||
|
||||
virtual void run() override;
|
||||
};
|
||||
|
||||
struct js::Nursery::SweepAction
|
||||
|
||||
@@ -22,9 +22,6 @@
|
||||
using mozilla::Maybe;
|
||||
|
||||
namespace js {
|
||||
|
||||
class GCParallelTask;
|
||||
|
||||
namespace gcstats {
|
||||
|
||||
enum Phase : uint8_t {
|
||||
|
||||
@@ -11738,6 +11738,32 @@ CodeGenerator::visitHasClass(LHasClass* ins)
|
||||
masm.cmpPtrSet(Assembler::Equal, output, ImmPtr(ins->mir()->getClass()), output);
|
||||
}
|
||||
|
||||
void
|
||||
CodeGenerator::visitGuardToClass(LGuardToClass* ins)
|
||||
{
|
||||
Register lhs = ToRegister(ins->lhs());
|
||||
Register output = ToRegister(ins->output());
|
||||
Register temp = ToRegister(ins->temp());
|
||||
|
||||
Label notEqual;
|
||||
|
||||
masm.branchTestObjClass(Assembler::NotEqual, lhs, temp, ins->mir()->getClass(), ¬Equal);
|
||||
masm.mov(lhs, output);
|
||||
|
||||
if (ins->mir()->type() == MIRType::Object) {
|
||||
// Can't return null-return here, so bail
|
||||
bailoutFrom(¬Equal, ins->snapshot());
|
||||
} else {
|
||||
Label done;
|
||||
masm.jump(&done);
|
||||
|
||||
masm.bind(¬Equal);
|
||||
masm.mov(ImmPtr(0), output);
|
||||
|
||||
masm.bind(&done);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CodeGenerator::visitWasmParameter(LWasmParameter* lir)
|
||||
{
|
||||
|
||||
@@ -379,6 +379,7 @@ class CodeGenerator final : public CodeGeneratorSpecific
|
||||
void visitIsObject(LIsObject* lir);
|
||||
void visitIsObjectAndBranch(LIsObjectAndBranch* lir);
|
||||
void visitHasClass(LHasClass* lir);
|
||||
void visitGuardToClass(LGuardToClass* lir);
|
||||
void visitWasmParameter(LWasmParameter* lir);
|
||||
void visitWasmParameterI64(LWasmParameterI64* lir);
|
||||
void visitWasmReturn(LWasmReturn* ret);
|
||||
|
||||
@@ -117,13 +117,15 @@
|
||||
_(IntrinsicDefineDataProperty) \
|
||||
_(IntrinsicObjectHasPrototype) \
|
||||
\
|
||||
_(IntrinsicIsArrayIterator) \
|
||||
_(IntrinsicIsMapIterator) \
|
||||
_(IntrinsicIsSetIterator) \
|
||||
_(IntrinsicIsStringIterator) \
|
||||
_(IntrinsicGuardToArrayIterator) \
|
||||
_(IntrinsicGuardToMapIterator) \
|
||||
_(IntrinsicGuardToSetIterator) \
|
||||
_(IntrinsicGuardToStringIterator) \
|
||||
\
|
||||
_(IntrinsicGuardToMapObject) \
|
||||
_(IntrinsicGetNextMapEntryForIterator) \
|
||||
\
|
||||
_(IntrinsicGuardToSetObject) \
|
||||
_(IntrinsicGetNextSetEntryForIterator) \
|
||||
\
|
||||
_(IntrinsicArrayBufferByteLength) \
|
||||
|
||||
@@ -764,6 +764,7 @@ class IonBuilder
|
||||
const Class* clasp2 = nullptr,
|
||||
const Class* clasp3 = nullptr,
|
||||
const Class* clasp4 = nullptr);
|
||||
InliningStatus inlineGuardToClass(CallInfo& callInfo, const Class* clasp);
|
||||
InliningResult inlineIsConstructing(CallInfo& callInfo);
|
||||
InliningResult inlineSubstringKernel(CallInfo& callInfo);
|
||||
InliningResult inlineObjectHasPrototype(CallInfo& callInfo);
|
||||
|
||||
@@ -4188,6 +4188,16 @@ LIRGenerator::visitHasClass(MHasClass* ins)
|
||||
define(new(alloc()) LHasClass(useRegister(ins->object())), ins);
|
||||
}
|
||||
|
||||
void
|
||||
LIRGenerator::visitGuardToClass(MGuardToClass* ins)
|
||||
{
|
||||
MOZ_ASSERT(ins->object()->type() == MIRType::Object);
|
||||
MOZ_ASSERT(ins->type() == MIRType::ObjectOrNull|| ins->type() == MIRType::Object);
|
||||
LGuardToClass* lir = new(alloc()) LGuardToClass(useRegister(ins->object()), temp());
|
||||
assignSnapshot(lir, Bailout_TypeBarrierO);
|
||||
define(lir, ins);
|
||||
}
|
||||
|
||||
void
|
||||
LIRGenerator::visitWasmAddOffset(MWasmAddOffset* ins)
|
||||
{
|
||||
|
||||
@@ -289,6 +289,7 @@ class LIRGenerator : public LIRGeneratorSpecific
|
||||
void visitIsConstructor(MIsConstructor* ins);
|
||||
void visitIsObject(MIsObject* ins);
|
||||
void visitHasClass(MHasClass* ins);
|
||||
void visitGuardToClass(MGuardToClass* ins);
|
||||
void visitWasmAddOffset(MWasmAddOffset* ins);
|
||||
void visitWasmBoundsCheck(MWasmBoundsCheck* ins);
|
||||
void visitWasmLoadGlobalVar(MWasmLoadGlobalVar* ins);
|
||||
|
||||
@@ -280,24 +280,28 @@ IonBuilder::inlineNativeCall(CallInfo& callInfo, JSFunction* target)
|
||||
return inlineIsConstructing(callInfo);
|
||||
case InlinableNative::IntrinsicSubstringKernel:
|
||||
return inlineSubstringKernel(callInfo);
|
||||
case InlinableNative::IntrinsicIsArrayIterator:
|
||||
return inlineHasClass(callInfo, &ArrayIteratorObject::class_);
|
||||
case InlinableNative::IntrinsicIsMapIterator:
|
||||
return inlineHasClass(callInfo, &MapIteratorObject::class_);
|
||||
case InlinableNative::IntrinsicIsSetIterator:
|
||||
return inlineHasClass(callInfo, &SetIteratorObject::class_);
|
||||
case InlinableNative::IntrinsicIsStringIterator:
|
||||
return inlineHasClass(callInfo, &StringIteratorObject::class_);
|
||||
case InlinableNative::IntrinsicGuardToArrayIterator:
|
||||
return inlineGuardToClass(callInfo, &ArrayIteratorObject::class_);
|
||||
case InlinableNative::IntrinsicGuardToMapIterator:
|
||||
return inlineGuardToClass(callInfo, &MapIteratorObject::class_);
|
||||
case InlinableNative::IntrinsicGuardToSetIterator:
|
||||
return inlineGuardToClass(callInfo, &SetIteratorObject::class_);
|
||||
case InlinableNative::IntrinsicGuardToStringIterator:
|
||||
return inlineGuardToClass(callInfo, &StringIteratorObject::class_);
|
||||
case InlinableNative::IntrinsicDefineDataProperty:
|
||||
return inlineDefineDataProperty(callInfo);
|
||||
case InlinableNative::IntrinsicObjectHasPrototype:
|
||||
return inlineObjectHasPrototype(callInfo);
|
||||
|
||||
// Map intrinsics.
|
||||
case InlinableNative::IntrinsicGuardToMapObject:
|
||||
return inlineGuardToClass(callInfo, &MapObject::class_);
|
||||
case InlinableNative::IntrinsicGetNextMapEntryForIterator:
|
||||
return inlineGetNextEntryForIterator(callInfo, MGetNextEntryForIterator::Map);
|
||||
|
||||
// Set intrinsics.
|
||||
case InlinableNative::IntrinsicGuardToSetObject:
|
||||
return inlineGuardToClass(callInfo, &SetObject::class_);
|
||||
case InlinableNative::IntrinsicGetNextSetEntryForIterator:
|
||||
return inlineGetNextEntryForIterator(callInfo, MGetNextEntryForIterator::Set);
|
||||
|
||||
@@ -2191,6 +2195,37 @@ IonBuilder::inlineHasClass(CallInfo& callInfo,
|
||||
return InliningStatus_Inlined;
|
||||
}
|
||||
|
||||
IonBuilder::InliningStatus
|
||||
IonBuilder::inlineGuardToClass(CallInfo& callInfo, const Class* clasp)
|
||||
{
|
||||
MOZ_ASSERT(!callInfo.constructing());
|
||||
MOZ_ASSERT(callInfo.argc() == 1);
|
||||
|
||||
if (callInfo.getArg(0)->type() != MIRType::Object)
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
if (getInlineReturnType() != MIRType::ObjectOrNull &&
|
||||
getInlineReturnType() != MIRType::Object)
|
||||
{
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
TemporaryTypeSet* types = callInfo.getArg(0)->resultTypeSet();
|
||||
const Class* knownClass = types ? types->getKnownClass(constraints()) : nullptr;
|
||||
|
||||
if (knownClass && knownClass == clasp) {
|
||||
current->push(callInfo.getArg(0));
|
||||
} else {
|
||||
MGuardToClass* guardToClass = MGuardToClass::New(alloc(), callInfo.getArg(0),
|
||||
clasp, getInlineReturnType());
|
||||
current->add(guardToClass);
|
||||
current->push(guardToClass);
|
||||
}
|
||||
|
||||
callInfo.setImplicitlyUsedUnchecked();
|
||||
return InliningStatus_Inlined;
|
||||
}
|
||||
|
||||
IonBuilder::InliningResult
|
||||
IonBuilder::inlineGetNextEntryForIterator(CallInfo& callInfo, MGetNextEntryForIterator::Mode mode)
|
||||
{
|
||||
|
||||
@@ -13175,6 +13175,48 @@ class MHasClass
|
||||
}
|
||||
};
|
||||
|
||||
class MGuardToClass
|
||||
: public MUnaryInstruction,
|
||||
public SingleObjectPolicy::Data
|
||||
{
|
||||
const Class* class_;
|
||||
|
||||
MGuardToClass(MDefinition* object, const Class* clasp, MIRType resultType)
|
||||
: MUnaryInstruction(object)
|
||||
, class_(clasp)
|
||||
{
|
||||
MOZ_ASSERT(object->type() == MIRType::Object ||
|
||||
(object->type() == MIRType::Value && object->mightBeType(MIRType::Object)));
|
||||
MOZ_ASSERT(resultType == MIRType::Object || resultType == MIRType::ObjectOrNull);
|
||||
setResultType(resultType);
|
||||
setMovable();
|
||||
if (resultType == MIRType::Object) {
|
||||
// We will bail out if the class type is incorrect,
|
||||
// so we need to ensure we don't eliminate this instruction
|
||||
setGuard();
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
INSTRUCTION_HEADER(GuardToClass)
|
||||
TRIVIAL_NEW_WRAPPERS
|
||||
NAMED_OPERANDS((0, object))
|
||||
|
||||
const Class* getClass() const {
|
||||
return class_;
|
||||
}
|
||||
AliasSet getAliasSet() const override {
|
||||
return AliasSet::None();
|
||||
}
|
||||
bool congruentTo(const MDefinition* ins) const override {
|
||||
if (!ins->isGuardToClass())
|
||||
return false;
|
||||
if (getClass() != ins->toGuardToClass()->getClass())
|
||||
return false;
|
||||
return congruentIfOperandsEqual(ins);
|
||||
}
|
||||
};
|
||||
|
||||
class MCheckReturn
|
||||
: public MBinaryInstruction,
|
||||
public BoxInputsPolicy::Data
|
||||
|
||||
@@ -272,6 +272,7 @@ namespace jit {
|
||||
_(IsCallable) \
|
||||
_(IsObject) \
|
||||
_(HasClass) \
|
||||
_(GuardToClass) \
|
||||
_(CopySign) \
|
||||
_(Rotate) \
|
||||
_(NewDerivedTypedObject) \
|
||||
|
||||
@@ -7891,6 +7891,29 @@ class LHasClass : public LInstructionHelper<1, 1, 0>
|
||||
}
|
||||
};
|
||||
|
||||
class LGuardToClass : public LInstructionHelper<1, 1, 1>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(GuardToClass);
|
||||
explicit LGuardToClass(const LAllocation& lhs, const LDefinition& temp)
|
||||
{
|
||||
setOperand(0, lhs);
|
||||
setTemp(0, temp);
|
||||
}
|
||||
|
||||
const LAllocation* lhs() {
|
||||
return getOperand(0);
|
||||
}
|
||||
|
||||
const LDefinition* temp() {
|
||||
return getTemp(0);
|
||||
}
|
||||
|
||||
MGuardToClass* mir() const {
|
||||
return mir_->toGuardToClass();
|
||||
}
|
||||
};
|
||||
|
||||
template<size_t Defs, size_t Ops>
|
||||
class LWasmSelectBase : public LInstructionHelper<Defs, Ops, 0>
|
||||
{
|
||||
|
||||
@@ -387,6 +387,7 @@
|
||||
_(IsObject) \
|
||||
_(IsObjectAndBranch) \
|
||||
_(HasClass) \
|
||||
_(GuardToClass) \
|
||||
_(RecompileCheck) \
|
||||
_(MemoryBarrier) \
|
||||
_(AssertRangeI) \
|
||||
|
||||
+16
-13
@@ -2142,7 +2142,7 @@ ArenasToUpdate::getArenasToUpdate(AutoLockHelperThreadState& lock, unsigned maxL
|
||||
return { begin, last->next };
|
||||
}
|
||||
|
||||
struct UpdatePointersTask : public GCParallelTask
|
||||
struct UpdatePointersTask : public GCParallelTaskHelper<UpdatePointersTask>
|
||||
{
|
||||
// Maximum number of arenas to update in one block.
|
||||
#ifdef DEBUG
|
||||
@@ -2158,14 +2158,13 @@ struct UpdatePointersTask : public GCParallelTask
|
||||
arenas_.end = nullptr;
|
||||
}
|
||||
|
||||
~UpdatePointersTask() override { join(); }
|
||||
void run();
|
||||
|
||||
private:
|
||||
JSRuntime* rt_;
|
||||
ArenasToUpdate* source_;
|
||||
ArenaListSegment arenas_;
|
||||
|
||||
virtual void run() override;
|
||||
bool getArenasToUpdate();
|
||||
void updateArenas();
|
||||
};
|
||||
@@ -2262,7 +2261,7 @@ GCRuntime::updateCellPointers(MovingTracer* trc, Zone* zone, AllocKinds kinds, s
|
||||
for (size_t i = 0; i < bgTaskCount && !bgArenas.done(); i++) {
|
||||
bgTasks[i].emplace(rt, &bgArenas, lock);
|
||||
startTask(*bgTasks[i], gcstats::PHASE_COMPACT_UPDATE_CELLS, lock);
|
||||
tasksStarted = i;
|
||||
tasksStarted++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2273,6 +2272,8 @@ GCRuntime::updateCellPointers(MovingTracer* trc, Zone* zone, AllocKinds kinds, s
|
||||
|
||||
for (size_t i = 0; i < tasksStarted; i++)
|
||||
joinTask(*bgTasks[i], gcstats::PHASE_COMPACT_UPDATE_CELLS, lock);
|
||||
for (size_t i = tasksStarted; i < MaxCellUpdateBackgroundTasks; i++)
|
||||
MOZ_ASSERT(bgTasks[i].isNothing());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2971,7 +2972,6 @@ js::gc::BackgroundDecommitTask::run()
|
||||
AutoLockGC lock(runtime);
|
||||
|
||||
for (Chunk* chunk : toDecommit) {
|
||||
|
||||
// The arena list is not doubly-linked, so we have to work in the free
|
||||
// list order and not in the natural order.
|
||||
while (chunk->info.numArenasFreeCommitted) {
|
||||
@@ -4383,7 +4383,8 @@ GCRuntime::endMarkingZoneGroup()
|
||||
marker.setMarkColorBlack();
|
||||
}
|
||||
|
||||
class GCSweepTask : public GCParallelTask
|
||||
template <typename Derived>
|
||||
class GCSweepTask : public GCParallelTaskHelper<Derived>
|
||||
{
|
||||
GCSweepTask(const GCSweepTask&) = delete;
|
||||
|
||||
@@ -4393,13 +4394,13 @@ class GCSweepTask : public GCParallelTask
|
||||
public:
|
||||
explicit GCSweepTask(JSRuntime* rt) : runtime(rt) {}
|
||||
GCSweepTask(GCSweepTask&& other)
|
||||
: GCParallelTask(mozilla::Move(other)),
|
||||
: GCParallelTaskHelper<Derived>(mozilla::Move(other)),
|
||||
runtime(other.runtime)
|
||||
{}
|
||||
};
|
||||
|
||||
// Causes the given WeakCache to be swept when run.
|
||||
class SweepWeakCacheTask : public GCSweepTask
|
||||
class SweepWeakCacheTask : public GCSweepTask<SweepWeakCacheTask>
|
||||
{
|
||||
JS::WeakCache<void*>& cache;
|
||||
|
||||
@@ -4411,15 +4412,15 @@ class SweepWeakCacheTask : public GCSweepTask
|
||||
: GCSweepTask(mozilla::Move(other)), cache(other.cache)
|
||||
{}
|
||||
|
||||
void run() override {
|
||||
void run() {
|
||||
cache.sweep();
|
||||
}
|
||||
};
|
||||
|
||||
#define MAKE_GC_SWEEP_TASK(name) \
|
||||
class name : public GCSweepTask { \
|
||||
void run() override; \
|
||||
class name : public GCSweepTask<name> { \
|
||||
public: \
|
||||
void run(); \
|
||||
explicit name (JSRuntime* rt) : GCSweepTask(rt) {} \
|
||||
}
|
||||
MAKE_GC_SWEEP_TASK(SweepAtomsTask);
|
||||
@@ -4471,7 +4472,8 @@ SweepMiscTask::run()
|
||||
}
|
||||
|
||||
void
|
||||
GCRuntime::startTask(GCParallelTask& task, gcstats::Phase phase, AutoLockHelperThreadState& locked)
|
||||
GCRuntime::startTask(GCParallelTask& task, gcstats::Phase phase,
|
||||
AutoLockHelperThreadState& locked)
|
||||
{
|
||||
if (!task.startWithLockHeld(locked)) {
|
||||
AutoUnlockHelperThreadState unlock(locked);
|
||||
@@ -4481,7 +4483,8 @@ GCRuntime::startTask(GCParallelTask& task, gcstats::Phase phase, AutoLockHelperT
|
||||
}
|
||||
|
||||
void
|
||||
GCRuntime::joinTask(GCParallelTask& task, gcstats::Phase phase, AutoLockHelperThreadState& locked)
|
||||
GCRuntime::joinTask(GCParallelTask& task, gcstats::Phase phase,
|
||||
AutoLockHelperThreadState& locked)
|
||||
{
|
||||
gcstats::AutoPhase ap(stats, task, phase);
|
||||
task.joinWithLockHeld(locked);
|
||||
|
||||
+44
-7
@@ -12,6 +12,7 @@
|
||||
#include "mozilla/Atomics.h"
|
||||
#include "mozilla/EnumeratedArray.h"
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
#include "mozilla/Move.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
#include "mozilla/TypeTraits.h"
|
||||
|
||||
@@ -936,10 +937,19 @@ class GCHelperState
|
||||
};
|
||||
|
||||
// A generic task used to dispatch work to the helper thread system.
|
||||
// Users should derive from GCParallelTask add what data they need and
|
||||
// override |run|.
|
||||
// Users supply a function pointer to call.
|
||||
//
|
||||
// Note that we don't use virtual functions here because destructors can write
|
||||
// the vtable pointer on entry, which can causes races if synchronization
|
||||
// happens there.
|
||||
class GCParallelTask
|
||||
{
|
||||
public:
|
||||
using TaskFunc = void (*)(GCParallelTask*);
|
||||
|
||||
private:
|
||||
TaskFunc func_;
|
||||
|
||||
// The state of the parallel computation.
|
||||
enum TaskState {
|
||||
NotStarted,
|
||||
@@ -956,19 +966,24 @@ class GCParallelTask
|
||||
// A flag to signal a request for early completion of the off-thread task.
|
||||
mozilla::Atomic<bool> cancel_;
|
||||
|
||||
virtual void run() = 0;
|
||||
|
||||
public:
|
||||
GCParallelTask() : state(NotStarted), duration_(0) {}
|
||||
explicit GCParallelTask(TaskFunc func)
|
||||
: func_(func),
|
||||
state(NotStarted),
|
||||
duration_(0),
|
||||
cancel_(false)
|
||||
{}
|
||||
|
||||
GCParallelTask(GCParallelTask&& other)
|
||||
: state(other.state),
|
||||
: func_(other.func_),
|
||||
state(other.state),
|
||||
duration_(0),
|
||||
cancel_(false)
|
||||
{}
|
||||
|
||||
// Derived classes must override this to ensure that join() gets called
|
||||
// before members get destructed.
|
||||
virtual ~GCParallelTask();
|
||||
~GCParallelTask();
|
||||
|
||||
// Time spent in the most recent invocation of this task.
|
||||
mozilla::TimeDuration duration() const { return duration_; }
|
||||
@@ -997,12 +1012,34 @@ class GCParallelTask
|
||||
bool isRunningWithLockHeld(const AutoLockHelperThreadState& locked) const;
|
||||
bool isRunning() const;
|
||||
|
||||
void runTask() {
|
||||
func_(this);
|
||||
}
|
||||
|
||||
// This should be friended to HelperThread, but cannot be because it
|
||||
// would introduce several circular dependencies.
|
||||
public:
|
||||
void runFromHelperThread(AutoLockHelperThreadState& locked);
|
||||
};
|
||||
|
||||
// CRTP template to handle cast to derived type when calling run().
|
||||
template <typename Derived>
|
||||
class GCParallelTaskHelper : public GCParallelTask
|
||||
{
|
||||
public:
|
||||
GCParallelTaskHelper()
|
||||
: GCParallelTask(&runTaskTyped)
|
||||
{}
|
||||
GCParallelTaskHelper(GCParallelTaskHelper&& other)
|
||||
: GCParallelTask(mozilla::Move(other))
|
||||
{}
|
||||
|
||||
private:
|
||||
static void runTaskTyped(GCParallelTask* task) {
|
||||
static_cast<Derived*>(task)->run();
|
||||
}
|
||||
};
|
||||
|
||||
typedef void (*IterateChunkCallback)(JSRuntime* rt, void* data, gc::Chunk* chunk);
|
||||
typedef void (*IterateZoneCallback)(JSRuntime* rt, void* data, JS::Zone* zone);
|
||||
typedef void (*IterateArenaCallback)(JSRuntime* rt, void* data, gc::Arena* arena,
|
||||
|
||||
+2
-1
@@ -9,6 +9,7 @@
|
||||
|
||||
#include "mozilla/HashFunctions.h"
|
||||
#include "mozilla/PodOperations.h"
|
||||
#include "mozilla/TextUtils.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
@@ -95,7 +96,7 @@ struct JSSubString {
|
||||
#define JS7_UNOCT(c) (JS7_UNDEC(c))
|
||||
#define JS7_ISHEX(c) ((c) < 128 && isxdigit(c))
|
||||
#define JS7_UNHEX(c) (unsigned)(JS7_ISDEC(c) ? (c) - '0' : 10 + tolower(c) - 'a')
|
||||
#define JS7_ISLET(c) ((c) < 128 && isalpha(c))
|
||||
#define JS7_ISLET(c) (mozilla::IsAsciiAlpha(c))
|
||||
|
||||
extern size_t
|
||||
js_strlen(const char16_t* s);
|
||||
|
||||
@@ -25,6 +25,7 @@ check({get x() { throw new Error("fail"); }});
|
||||
// Mismatched scopes.
|
||||
for (let [write_scope, read_scope] of [['SameProcessSameThread', 'SameProcessDifferentThread'],
|
||||
['SameProcessSameThread', 'DifferentProcess'],
|
||||
['SameProcessDifferentThread', 'DifferentProcessForIndexedDB'],
|
||||
['SameProcessDifferentThread', 'DifferentProcess']])
|
||||
{
|
||||
var ab = new ArrayBuffer(12);
|
||||
|
||||
@@ -3,11 +3,15 @@
|
||||
// http://creativecommons.org/licenses/publicdomain/
|
||||
|
||||
function* buffer_options() {
|
||||
for (var scope of ["SameProcessSameThread", "SameProcessDifferentThread", "DifferentProcess"]) {
|
||||
for (var size of [0, 8, 16, 200, 1000, 4096, 8192, 65536]) {
|
||||
yield { scope, size };
|
||||
for (var scope of ["SameProcessSameThread",
|
||||
"SameProcessDifferentThread",
|
||||
"DifferentProcess",
|
||||
"DifferentProcessForIndexedDB"])
|
||||
{
|
||||
for (var size of [0, 8, 16, 200, 1000, 4096, 8192, 65536]) {
|
||||
yield { scope, size };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1137,7 +1137,7 @@ js::GCParallelTask::runFromMainThread(JSRuntime* rt)
|
||||
MOZ_ASSERT(state == NotStarted);
|
||||
MOZ_ASSERT(js::CurrentThreadCanAccessRuntime(rt));
|
||||
mozilla::TimeStamp timeStart = mozilla::TimeStamp::Now();
|
||||
run();
|
||||
runTask();
|
||||
duration_ = mozilla::TimeStamp::Now() - timeStart;
|
||||
}
|
||||
|
||||
@@ -1148,7 +1148,7 @@ js::GCParallelTask::runFromHelperThread(AutoLockHelperThreadState& locked)
|
||||
AutoUnlockHelperThreadState parallelSection(locked);
|
||||
gc::AutoSetThreadIsPerformingGC performingGC;
|
||||
mozilla::TimeStamp timeStart = mozilla::TimeStamp::Now();
|
||||
run();
|
||||
runTask();
|
||||
duration_ = mozilla::TimeStamp::Now() - timeStart;
|
||||
}
|
||||
|
||||
|
||||
+33
-12
@@ -189,6 +189,22 @@ intrinsic_IsInstanceOfBuiltin(JSContext* cx, unsigned argc, Value* vp)
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static bool
|
||||
intrinsic_GuardToBuiltin(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
MOZ_ASSERT(args.length() == 1);
|
||||
MOZ_ASSERT(args[0].isObject());
|
||||
|
||||
if (args[0].toObject().is<T>()) {
|
||||
args.rval().setObject(args[0].toObject());
|
||||
return true;
|
||||
}
|
||||
args.rval().setNull();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Self-hosting intrinsic returning the original constructor for a builtin
|
||||
* the name of which is the first and only argument.
|
||||
@@ -2322,18 +2338,18 @@ static const JSFunctionSpec intrinsic_functions[] = {
|
||||
|
||||
JS_FN("_SetCanonicalName", intrinsic_SetCanonicalName, 2,0),
|
||||
|
||||
JS_INLINABLE_FN("IsArrayIterator",
|
||||
intrinsic_IsInstanceOfBuiltin<ArrayIteratorObject>, 1,0,
|
||||
IntrinsicIsArrayIterator),
|
||||
JS_INLINABLE_FN("IsMapIterator",
|
||||
intrinsic_IsInstanceOfBuiltin<MapIteratorObject>, 1,0,
|
||||
IntrinsicIsMapIterator),
|
||||
JS_INLINABLE_FN("IsSetIterator",
|
||||
intrinsic_IsInstanceOfBuiltin<SetIteratorObject>, 1,0,
|
||||
IntrinsicIsSetIterator),
|
||||
JS_INLINABLE_FN("IsStringIterator",
|
||||
intrinsic_IsInstanceOfBuiltin<StringIteratorObject>, 1,0,
|
||||
IntrinsicIsStringIterator),
|
||||
JS_INLINABLE_FN("GuardToArrayIterator",
|
||||
intrinsic_GuardToBuiltin<ArrayIteratorObject>, 1,0,
|
||||
IntrinsicGuardToArrayIterator),
|
||||
JS_INLINABLE_FN("GuardToMapIterator",
|
||||
intrinsic_GuardToBuiltin<MapIteratorObject>, 1,0,
|
||||
IntrinsicGuardToMapIterator),
|
||||
JS_INLINABLE_FN("GuardToSetIterator",
|
||||
intrinsic_GuardToBuiltin<SetIteratorObject>, 1,0,
|
||||
IntrinsicGuardToSetIterator),
|
||||
JS_INLINABLE_FN("GuardToStringIterator",
|
||||
intrinsic_GuardToBuiltin<StringIteratorObject>, 1,0,
|
||||
IntrinsicGuardToStringIterator),
|
||||
|
||||
JS_FN("_CreateMapIterationResultPair", intrinsic_CreateMapIterationResultPair, 0, 0),
|
||||
JS_INLINABLE_FN("_GetNextMapEntryForIterator", intrinsic_GetNextMapEntryForIterator, 2,0,
|
||||
@@ -2436,7 +2452,12 @@ static const JSFunctionSpec intrinsic_functions[] = {
|
||||
JS_FN("CallStarGeneratorMethodIfWrapped",
|
||||
CallNonGenericSelfhostedMethod<Is<StarGeneratorObject>>, 2, 0),
|
||||
|
||||
JS_INLINABLE_FN("GuardToMapObject", intrinsic_GuardToBuiltin<MapObject>, 1, 0,
|
||||
IntrinsicGuardToMapObject),
|
||||
JS_FN("IsWeakSet", intrinsic_IsInstanceOfBuiltin<WeakSetObject>, 1,0),
|
||||
|
||||
JS_INLINABLE_FN("GuardToSetObject", intrinsic_GuardToBuiltin<SetObject>, 1, 0,
|
||||
IntrinsicGuardToSetObject),
|
||||
JS_FN("CallWeakSetMethodIfWrapped",
|
||||
CallNonGenericSelfhostedMethod<Is<WeakSetObject>>, 2, 0),
|
||||
|
||||
|
||||
+181
-217
@@ -160,16 +160,16 @@ template<typename T, typename AllocPolicy>
|
||||
struct BufferIterator {
|
||||
typedef mozilla::BufferList<AllocPolicy> BufferList;
|
||||
|
||||
explicit BufferIterator(BufferList& buffer)
|
||||
explicit BufferIterator(const BufferList& buffer)
|
||||
: mBuffer(buffer)
|
||||
, mIter(buffer.Iter())
|
||||
{
|
||||
JS_STATIC_ASSERT(8 % sizeof(T) == 0);
|
||||
}
|
||||
|
||||
BufferIterator(const BufferIterator& other)
|
||||
: mBuffer(other.mBuffer)
|
||||
, mIter(other.mIter)
|
||||
explicit BufferIterator(const JSStructuredCloneData& data)
|
||||
: mBuffer(data.bufList_)
|
||||
, mIter(data.Start())
|
||||
{
|
||||
}
|
||||
|
||||
@@ -228,7 +228,7 @@ struct BufferIterator {
|
||||
return mIter.HasRoomFor(sizeof(T));
|
||||
}
|
||||
|
||||
BufferList& mBuffer;
|
||||
const BufferList& mBuffer;
|
||||
typename BufferList::IterImpl mIter;
|
||||
};
|
||||
|
||||
@@ -295,13 +295,22 @@ SharedArrayRawBufferRefs::releaseAll()
|
||||
refs_.clear();
|
||||
}
|
||||
|
||||
// SCOutput provides an interface to write raw data -- eg uint64_ts, doubles,
|
||||
// arrays of bytes -- into a structured clone data output stream. It also knows
|
||||
// how to free any transferable data within that stream.
|
||||
//
|
||||
// Note that it contains a full JSStructuredCloneData object, which holds the
|
||||
// callbacks necessary to read/write/transfer/free the data. For the purpose of
|
||||
// this class, only the freeTransfer callback is relevant; the rest of the callbacks
|
||||
// are used by the higher-level JSStructuredCloneWriter interface.
|
||||
struct SCOutput {
|
||||
public:
|
||||
using Iter = BufferIterator<uint64_t, TempAllocPolicy>;
|
||||
using Iter = BufferIterator<uint64_t, SystemAllocPolicy>;
|
||||
|
||||
explicit SCOutput(JSContext* cx);
|
||||
SCOutput(JSContext* cx, JS::StructuredCloneScope scope);
|
||||
|
||||
JSContext* context() const { return cx; }
|
||||
JS::StructuredCloneScope scope() const { return buf.scope(); }
|
||||
|
||||
bool write(uint64_t u);
|
||||
bool writePair(uint32_t tag, uint32_t data);
|
||||
@@ -314,22 +323,25 @@ struct SCOutput {
|
||||
template <class T>
|
||||
bool writeArray(const T* p, size_t nbytes);
|
||||
|
||||
bool extractBuffer(JSStructuredCloneData* data);
|
||||
void discardTransferables(const JSStructuredCloneCallbacks* cb, void* cbClosure);
|
||||
void setCallbacks(const JSStructuredCloneCallbacks* callbacks,
|
||||
void* closure,
|
||||
OwnTransferablePolicy policy)
|
||||
{
|
||||
buf.setCallbacks(callbacks, closure, policy);
|
||||
}
|
||||
void extractBuffer(JSStructuredCloneData* data) { *data = Move(buf); }
|
||||
void discardTransferables();
|
||||
|
||||
uint64_t tell() const { return buf.Size(); }
|
||||
uint64_t count() const { return buf.Size() / sizeof(uint64_t); }
|
||||
Iter iter() {
|
||||
return BufferIterator<uint64_t, TempAllocPolicy>(buf);
|
||||
}
|
||||
Iter iter() { return Iter(buf); }
|
||||
|
||||
size_t offset(Iter dest) {
|
||||
return dest - iter();
|
||||
}
|
||||
|
||||
private:
|
||||
JSContext* cx;
|
||||
mozilla::BufferList<TempAllocPolicy> buf;
|
||||
JSStructuredCloneData buf;
|
||||
};
|
||||
|
||||
class SCInput {
|
||||
@@ -419,13 +431,6 @@ struct JSStructuredCloneReader {
|
||||
// be valid cross-process.)
|
||||
JS::StructuredCloneScope allowedScope;
|
||||
|
||||
// The scope the buffer was generated for (what sort of buffer it is.) The
|
||||
// scope is not just a permissions thing; it also affects the storage
|
||||
// format (eg a Transferred ArrayBuffer can be stored as a pointer for
|
||||
// SameProcessSameThread but must have its contents in the clone buffer for
|
||||
// DifferentProcess.)
|
||||
JS::StructuredCloneScope storedScope;
|
||||
|
||||
// Stack of objects with properties remaining to be read.
|
||||
AutoValueVector objs;
|
||||
|
||||
@@ -449,13 +454,15 @@ struct JSStructuredCloneWriter {
|
||||
const JSStructuredCloneCallbacks* cb,
|
||||
void* cbClosure,
|
||||
const Value& tVal)
|
||||
: out(cx), scope(scope), objs(out.context()),
|
||||
: out(cx, scope), objs(out.context()),
|
||||
counts(out.context()), entries(out.context()),
|
||||
memory(out.context()), callbacks(cb),
|
||||
closure(cbClosure), transferable(out.context(), tVal),
|
||||
memory(out.context()),
|
||||
transferable(out.context(), tVal),
|
||||
transferableObjects(out.context(), GCHashSet<JSObject*>(cx)),
|
||||
cloneDataPolicy(cloneDataPolicy)
|
||||
{}
|
||||
{
|
||||
out.setCallbacks(cb, cbClosure, OwnTransferablePolicy::NoTransferables);
|
||||
}
|
||||
|
||||
~JSStructuredCloneWriter();
|
||||
|
||||
@@ -471,20 +478,10 @@ struct JSStructuredCloneWriter {
|
||||
|
||||
SCOutput& output() { return out; }
|
||||
|
||||
bool extractBuffer(JSStructuredCloneData* data) {
|
||||
bool success = out.extractBuffer(data);
|
||||
if (success) {
|
||||
// Move the SharedArrayRawBuf references here, SCOutput::extractBuffer
|
||||
// moves the serialized data.
|
||||
data->refsHeld_.takeOwnership(Move(refsHeld));
|
||||
data->setOptionalCallbacks(callbacks, closure,
|
||||
OwnTransferablePolicy::OwnsTransferablesIfAny);
|
||||
}
|
||||
return success;
|
||||
void extractBuffer(JSStructuredCloneData* newData) {
|
||||
out.extractBuffer(newData);
|
||||
}
|
||||
|
||||
JS::StructuredCloneScope cloneScope() const { return scope; }
|
||||
|
||||
private:
|
||||
JSStructuredCloneWriter() = delete;
|
||||
JSStructuredCloneWriter(const JSStructuredCloneWriter&) = delete;
|
||||
@@ -515,9 +512,6 @@ struct JSStructuredCloneWriter {
|
||||
|
||||
SCOutput out;
|
||||
|
||||
// The (address space, thread) scope within which this clone is valid.
|
||||
JS::StructuredCloneScope scope;
|
||||
|
||||
// Vector of objects with properties remaining to be written.
|
||||
//
|
||||
// NB: These can span multiple compartments, so the compartment must be
|
||||
@@ -543,12 +537,6 @@ struct JSStructuredCloneWriter {
|
||||
SystemAllocPolicy>;
|
||||
Rooted<CloneMemory> memory;
|
||||
|
||||
// The user defined callbacks that will be used for cloning.
|
||||
const JSStructuredCloneCallbacks* callbacks;
|
||||
|
||||
// Any value passed to JS_WriteStructuredClone.
|
||||
void* closure;
|
||||
|
||||
// Set of transferable objects
|
||||
RootedValue transferable;
|
||||
Rooted<GCHashSet<JSObject*>> transferableObjects;
|
||||
@@ -615,7 +603,12 @@ WriteStructuredClone(JSContext* cx, HandleValue v, JSStructuredCloneData* bufp,
|
||||
const Value& transferable)
|
||||
{
|
||||
JSStructuredCloneWriter w(cx, scope, cloneDataPolicy, cb, cbClosure, transferable);
|
||||
return w.init() && w.write(v) && w.extractBuffer(bufp);
|
||||
if (!w.init())
|
||||
return false;
|
||||
if (!w.write(v))
|
||||
return false;
|
||||
w.extractBuffer(bufp);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
@@ -628,91 +621,15 @@ ReadStructuredClone(JSContext* cx, JSStructuredCloneData& data,
|
||||
return r.read(vp);
|
||||
}
|
||||
|
||||
// If the given buffer contains Transferables, free them. Note that custom
|
||||
// Transferables will use the JSStructuredCloneCallbacks::freeTransfer() to
|
||||
// delete their transferables.
|
||||
template<typename AllocPolicy>
|
||||
static void
|
||||
DiscardTransferables(mozilla::BufferList<AllocPolicy>& buffer,
|
||||
const JSStructuredCloneCallbacks* cb, void* cbClosure)
|
||||
{
|
||||
auto point = BufferIterator<uint64_t, AllocPolicy>(buffer);
|
||||
if (point.done())
|
||||
return; // Empty buffer
|
||||
|
||||
uint32_t tag, data;
|
||||
MOZ_RELEASE_ASSERT(point.canPeek());
|
||||
SCInput::getPair(point.peek(), &tag, &data);
|
||||
point.next();
|
||||
|
||||
if (tag == SCTAG_HEADER) {
|
||||
if (point.done())
|
||||
return;
|
||||
|
||||
MOZ_RELEASE_ASSERT(point.canPeek());
|
||||
SCInput::getPair(point.peek(), &tag, &data);
|
||||
point.next();
|
||||
}
|
||||
|
||||
if (tag != SCTAG_TRANSFER_MAP_HEADER)
|
||||
return;
|
||||
|
||||
if (TransferableMapHeader(data) == SCTAG_TM_TRANSFERRED)
|
||||
return;
|
||||
|
||||
// freeTransfer should not GC
|
||||
JS::AutoSuppressGCAnalysis nogc;
|
||||
|
||||
if (point.done())
|
||||
return;
|
||||
|
||||
uint64_t numTransferables = NativeEndian::swapFromLittleEndian(point.peek());
|
||||
point.next();
|
||||
while (numTransferables--) {
|
||||
if (!point.canPeek())
|
||||
return;
|
||||
|
||||
uint32_t ownership;
|
||||
SCInput::getPair(point.peek(), &tag, &ownership);
|
||||
point.next();
|
||||
MOZ_ASSERT(tag >= SCTAG_TRANSFER_MAP_PENDING_ENTRY);
|
||||
if (!point.canPeek())
|
||||
return;
|
||||
|
||||
void* content;
|
||||
SCInput::getPtr(point.peek(), &content);
|
||||
point.next();
|
||||
if (!point.canPeek())
|
||||
return;
|
||||
|
||||
uint64_t extraData = NativeEndian::swapFromLittleEndian(point.peek());
|
||||
point.next();
|
||||
|
||||
if (ownership < JS::SCTAG_TMO_FIRST_OWNED)
|
||||
continue;
|
||||
|
||||
if (ownership == JS::SCTAG_TMO_ALLOC_DATA) {
|
||||
js_free(content);
|
||||
} else if (ownership == JS::SCTAG_TMO_MAPPED_DATA) {
|
||||
JS_ReleaseMappedArrayBufferContents(content, extraData);
|
||||
} else if (cb && cb->freeTransfer) {
|
||||
cb->freeTransfer(tag, JS::TransferableOwnership(ownership), content, extraData, cbClosure);
|
||||
} else {
|
||||
MOZ_ASSERT(false, "unknown ownership");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
StructuredCloneHasTransferObjects(const JSStructuredCloneData& data)
|
||||
{
|
||||
auto iter = data.Iter();
|
||||
|
||||
if (data.Size() < sizeof(uint64_t))
|
||||
return false;
|
||||
|
||||
uint64_t u;
|
||||
data.ReadBytes(iter, reinterpret_cast<char*>(&u), sizeof(u));
|
||||
BufferIterator<uint64_t, SystemAllocPolicy> iter(data);
|
||||
MOZ_ALWAYS_TRUE(iter.readBytes(reinterpret_cast<char*>(&u), sizeof(u)));
|
||||
uint32_t tag = uint32_t(u >> 32);
|
||||
return (tag == SCTAG_TRANSFER_MAP_HEADER);
|
||||
}
|
||||
@@ -723,7 +640,7 @@ SCInput::SCInput(JSContext* cx, JSStructuredCloneData& data)
|
||||
: cx(cx), point(data)
|
||||
{
|
||||
|
||||
static_assert(JSStructuredCloneData::kSegmentAlignment % 8 == 0,
|
||||
static_assert(JSStructuredCloneData::BufferList::kSegmentAlignment % 8 == 0,
|
||||
"structured clone buffer reads should be aligned");
|
||||
MOZ_ASSERT(data.Size() % 8 == 0);
|
||||
}
|
||||
@@ -885,9 +802,8 @@ SCInput::readPtr(void** p)
|
||||
return true;
|
||||
}
|
||||
|
||||
SCOutput::SCOutput(JSContext* cx)
|
||||
: cx(cx)
|
||||
, buf(0, 0, 4096, cx)
|
||||
SCOutput::SCOutput(JSContext* cx, JS::StructuredCloneScope scope)
|
||||
: cx(cx), buf(scope)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -895,7 +811,11 @@ bool
|
||||
SCOutput::write(uint64_t u)
|
||||
{
|
||||
uint64_t v = NativeEndian::swapToLittleEndian(u);
|
||||
return buf.WriteBytes(reinterpret_cast<char*>(&v), sizeof(u));
|
||||
if (!buf.AppendBytes(reinterpret_cast<char*>(&v), sizeof(u))) {
|
||||
ReportOutOfMemory(context());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
@@ -956,7 +876,7 @@ SCOutput::writeArray(const T* p, size_t nelems)
|
||||
|
||||
for (size_t i = 0; i < nelems; i++) {
|
||||
T value = swapToLittleEndian(p[i]);
|
||||
if (!buf.WriteBytes(reinterpret_cast<char*>(&value), sizeof(value)))
|
||||
if (!buf.AppendBytes(reinterpret_cast<char*>(&value), sizeof(value)))
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -965,7 +885,7 @@ SCOutput::writeArray(const T* p, size_t nelems)
|
||||
size_t padbytes = sizeof(uint64_t) * nwords - sizeof(T) * nelems;
|
||||
char zero = 0;
|
||||
for (size_t i = 0; i < padbytes; i++) {
|
||||
if (!buf.WriteBytes(&zero, sizeof(zero)))
|
||||
if (!buf.AppendBytes(&zero, sizeof(zero)))
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1000,34 +920,101 @@ SCOutput::writePtr(const void* p)
|
||||
return write(reinterpret_cast<uint64_t>(p));
|
||||
}
|
||||
|
||||
bool
|
||||
SCOutput::extractBuffer(JSStructuredCloneData* data)
|
||||
{
|
||||
bool success;
|
||||
mozilla::BufferList<SystemAllocPolicy> out =
|
||||
buf.MoveFallible<SystemAllocPolicy>(&success);
|
||||
if (!success) {
|
||||
ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
*data = JSStructuredCloneData(Move(out));
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
SCOutput::discardTransferables(const JSStructuredCloneCallbacks* cb, void* cbClosure)
|
||||
SCOutput::discardTransferables()
|
||||
{
|
||||
DiscardTransferables(buf, cb, cbClosure);
|
||||
buf.discardTransferables();
|
||||
}
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
JSStructuredCloneData::~JSStructuredCloneData()
|
||||
|
||||
// If the buffer contains Transferables, free them. Note that custom
|
||||
// Transferables will use the JSStructuredCloneCallbacks::freeTransfer() to
|
||||
// delete their transferables.
|
||||
void
|
||||
JSStructuredCloneData::discardTransferables()
|
||||
{
|
||||
if (!Size())
|
||||
return;
|
||||
if (ownTransferables_ == OwnTransferablePolicy::OwnsTransferablesIfAny)
|
||||
DiscardTransferables(*this, callbacks_, closure_);
|
||||
|
||||
if (ownTransferables_ != OwnTransferablePolicy::OwnsTransferablesIfAny)
|
||||
return;
|
||||
|
||||
// DifferentProcess clones cannot contain pointers, so nothing needs to be
|
||||
// released.
|
||||
if (scope_ == JS::StructuredCloneScope::DifferentProcess)
|
||||
return;
|
||||
|
||||
FreeTransferStructuredCloneOp freeTransfer = nullptr;
|
||||
if (callbacks_)
|
||||
freeTransfer = callbacks_->freeTransfer;
|
||||
|
||||
auto point = BufferIterator<uint64_t, SystemAllocPolicy>(*this);
|
||||
if (point.done())
|
||||
return; // Empty buffer
|
||||
|
||||
uint32_t tag, data;
|
||||
MOZ_RELEASE_ASSERT(point.canPeek());
|
||||
SCInput::getPair(point.peek(), &tag, &data);
|
||||
point.next();
|
||||
|
||||
if (tag == SCTAG_HEADER) {
|
||||
if (point.done())
|
||||
return;
|
||||
|
||||
MOZ_RELEASE_ASSERT(point.canPeek());
|
||||
SCInput::getPair(point.peek(), &tag, &data);
|
||||
point.next();
|
||||
}
|
||||
|
||||
if (tag != SCTAG_TRANSFER_MAP_HEADER)
|
||||
return;
|
||||
|
||||
if (TransferableMapHeader(data) == SCTAG_TM_TRANSFERRED)
|
||||
return;
|
||||
|
||||
// freeTransfer should not GC
|
||||
JS::AutoSuppressGCAnalysis nogc;
|
||||
|
||||
if (point.done())
|
||||
return;
|
||||
|
||||
uint64_t numTransferables = NativeEndian::swapFromLittleEndian(point.peek());
|
||||
point.next();
|
||||
while (numTransferables--) {
|
||||
if (!point.canPeek())
|
||||
return;
|
||||
|
||||
uint32_t ownership;
|
||||
SCInput::getPair(point.peek(), &tag, &ownership);
|
||||
point.next();
|
||||
MOZ_ASSERT(tag >= SCTAG_TRANSFER_MAP_PENDING_ENTRY);
|
||||
if (!point.canPeek())
|
||||
return;
|
||||
|
||||
void* content;
|
||||
SCInput::getPtr(point.peek(), &content);
|
||||
point.next();
|
||||
if (!point.canPeek())
|
||||
return;
|
||||
|
||||
uint64_t extraData = NativeEndian::swapFromLittleEndian(point.peek());
|
||||
point.next();
|
||||
|
||||
if (ownership < JS::SCTAG_TMO_FIRST_OWNED)
|
||||
continue;
|
||||
|
||||
if (ownership == JS::SCTAG_TMO_ALLOC_DATA) {
|
||||
js_free(content);
|
||||
} else if (ownership == JS::SCTAG_TMO_MAPPED_DATA) {
|
||||
JS_ReleaseMappedArrayBufferContents(content, extraData);
|
||||
} else if (freeTransfer) {
|
||||
freeTransfer(tag, JS::TransferableOwnership(ownership), content, extraData, closure_);
|
||||
} else {
|
||||
MOZ_ASSERT(false, "unknown ownership");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
JS_STATIC_ASSERT(JSString::MAX_LENGTH < UINT32_MAX);
|
||||
@@ -1035,9 +1022,8 @@ JS_STATIC_ASSERT(JSString::MAX_LENGTH < UINT32_MAX);
|
||||
JSStructuredCloneWriter::~JSStructuredCloneWriter()
|
||||
{
|
||||
// Free any transferable data left lying around in the buffer
|
||||
if (out.count()) {
|
||||
out.discardTransferables(callbacks, closure);
|
||||
}
|
||||
if (out.count())
|
||||
out.discardTransferables();
|
||||
}
|
||||
|
||||
bool
|
||||
@@ -1106,7 +1092,7 @@ JSStructuredCloneWriter::parseTransferable()
|
||||
bool
|
||||
JSStructuredCloneWriter::reportDataCloneError(uint32_t errorId)
|
||||
{
|
||||
ReportDataCloneError(context(), callbacks, errorId);
|
||||
ReportDataCloneError(context(), out.buf.callbacks_, errorId);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1521,8 +1507,8 @@ JSStructuredCloneWriter::startWrite(HandleValue v)
|
||||
return traverseSavedFrame(obj);
|
||||
}
|
||||
|
||||
if (callbacks && callbacks->write)
|
||||
return callbacks->write(context(), this, obj, closure);
|
||||
if (out.buf.callbacks_ && out.buf.callbacks_->write)
|
||||
return out.buf.callbacks_->write(context(), this, obj, out.buf.closure_);
|
||||
/* else fall through */
|
||||
}
|
||||
|
||||
@@ -1532,7 +1518,7 @@ JSStructuredCloneWriter::startWrite(HandleValue v)
|
||||
bool
|
||||
JSStructuredCloneWriter::writeHeader()
|
||||
{
|
||||
return out.writePair(SCTAG_HEADER, (uint32_t)scope);
|
||||
return out.writePair(SCTAG_HEADER, (uint32_t)output().scope());
|
||||
}
|
||||
|
||||
bool
|
||||
@@ -1590,6 +1576,7 @@ JSStructuredCloneWriter::transferOwnership()
|
||||
|
||||
JSContext* cx = context();
|
||||
RootedObject obj(cx);
|
||||
JS::StructuredCloneScope scope = output().scope();
|
||||
for (auto tr = transferableObjects.all(); !tr.empty(); tr.popFront()) {
|
||||
obj = tr.front();
|
||||
|
||||
@@ -1622,7 +1609,9 @@ JSStructuredCloneWriter::transferOwnership()
|
||||
return false;
|
||||
}
|
||||
|
||||
if (scope == JS::StructuredCloneScope::DifferentProcess) {
|
||||
if (scope == JS::StructuredCloneScope::DifferentProcess ||
|
||||
scope == JS::StructuredCloneScope::DifferentProcessForIndexedDB)
|
||||
{
|
||||
// Write Transferred ArrayBuffers in DifferentProcess scope at
|
||||
// the end of the clone buffer, and store the offset within the
|
||||
// buffer to where the ArrayBuffer was written. Note that this
|
||||
@@ -1659,9 +1648,9 @@ JSStructuredCloneWriter::transferOwnership()
|
||||
extraData = nbytes;
|
||||
}
|
||||
} else {
|
||||
if (!callbacks || !callbacks->writeTransfer)
|
||||
if (!out.buf.callbacks_ || !out.buf.callbacks_->writeTransfer)
|
||||
return reportDataCloneError(JS_SCERR_TRANSFERABLE);
|
||||
if (!callbacks->writeTransfer(cx, obj, closure, &tag, &ownership, &content, &extraData))
|
||||
if (!out.buf.callbacks_->writeTransfer(cx, obj, out.buf.closure_, &tag, &ownership, &content, &extraData))
|
||||
return false;
|
||||
MOZ_ASSERT(tag > SCTAG_TRANSFER_MAP_PENDING_ENTRY);
|
||||
}
|
||||
@@ -2260,25 +2249,33 @@ JSStructuredCloneReader::readHeader()
|
||||
if (!in.getPair(&tag, &data))
|
||||
return in.reportTruncated();
|
||||
|
||||
if (tag != SCTAG_HEADER) {
|
||||
JS::StructuredCloneScope storedScope;
|
||||
if (tag == SCTAG_HEADER) {
|
||||
MOZ_ALWAYS_TRUE(in.readPair(&tag, &data));
|
||||
storedScope = JS::StructuredCloneScope(data);
|
||||
} else {
|
||||
// Old structured clone buffer. We must have read it from disk.
|
||||
storedScope = JS::StructuredCloneScope::DifferentProcess;
|
||||
return true;
|
||||
storedScope = JS::StructuredCloneScope::DifferentProcessForIndexedDB;
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_TRUE(in.readPair(&tag, &data));
|
||||
storedScope = JS::StructuredCloneScope(data);
|
||||
|
||||
if (data != uint32_t(JS::StructuredCloneScope::SameProcessSameThread) &&
|
||||
data != uint32_t(JS::StructuredCloneScope::SameProcessDifferentThread) &&
|
||||
data != uint32_t(JS::StructuredCloneScope::DifferentProcess))
|
||||
if (storedScope < JS::StructuredCloneScope::SameProcessSameThread ||
|
||||
storedScope > JS::StructuredCloneScope::DifferentProcessForIndexedDB)
|
||||
{
|
||||
JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr, JSMSG_SC_BAD_SERIALIZED_DATA,
|
||||
"invalid structured clone scope");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (allowedScope == JS::StructuredCloneScope::DifferentProcessForIndexedDB) {
|
||||
// Bug 1434308 and bug 1458320 - the scopes stored in old IndexedDB
|
||||
// clones are incorrect. Treat them as if they were DifferentProcess.
|
||||
allowedScope = JS::StructuredCloneScope::DifferentProcess;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (storedScope < allowedScope) {
|
||||
JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr, JSMSG_SC_BAD_SERIALIZED_DATA,
|
||||
JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr,
|
||||
JSMSG_SC_BAD_SERIALIZED_DATA,
|
||||
"incompatible structured clone scope");
|
||||
return false;
|
||||
}
|
||||
@@ -2322,10 +2319,14 @@ JSStructuredCloneReader::readTransferMap()
|
||||
return false;
|
||||
|
||||
if (tag == SCTAG_TRANSFER_MAP_ARRAY_BUFFER) {
|
||||
if (storedScope == JS::StructuredCloneScope::DifferentProcess) {
|
||||
if (allowedScope == JS::StructuredCloneScope::DifferentProcess ||
|
||||
allowedScope == JS::StructuredCloneScope::DifferentProcessForIndexedDB)
|
||||
{
|
||||
// Transferred ArrayBuffers in a DifferentProcess clone buffer
|
||||
// are treated as if they weren't Transferred at all.
|
||||
continue;
|
||||
// are treated as if they weren't Transferred at all. We should
|
||||
// only see SCTAG_TRANSFER_MAP_STORED_ARRAY_BUFFER.
|
||||
ReportDataCloneError(cx, callbacks, JS_SCERR_TRANSFERABLE);
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t nbytes = extraData;
|
||||
@@ -2659,7 +2660,7 @@ JS_StructuredClone(JSContext* cx, HandleValue value, MutableHandleValue vp,
|
||||
}
|
||||
|
||||
JSAutoStructuredCloneBuffer::JSAutoStructuredCloneBuffer(JSAutoStructuredCloneBuffer&& other)
|
||||
: scope_(other.scope_)
|
||||
: scope_(other.scope()), data_(other.scope())
|
||||
{
|
||||
data_.ownTransferables_ = other.data_.ownTransferables_;
|
||||
other.steal(&data_, &version_, &data_.callbacks_, &data_.closure_);
|
||||
@@ -2677,51 +2678,14 @@ JSAutoStructuredCloneBuffer::operator=(JSAutoStructuredCloneBuffer&& other)
|
||||
}
|
||||
|
||||
void
|
||||
JSAutoStructuredCloneBuffer::clear(const JSStructuredCloneCallbacks* optionalCallbacks,
|
||||
void* optionalClosure)
|
||||
JSAutoStructuredCloneBuffer::clear()
|
||||
{
|
||||
if (!data_.Size())
|
||||
return;
|
||||
|
||||
const JSStructuredCloneCallbacks* callbacks =
|
||||
optionalCallbacks ? optionalCallbacks : data_.callbacks_;
|
||||
void* closure = optionalClosure ? optionalClosure : data_.closure_;
|
||||
|
||||
if (data_.ownTransferables_ == OwnTransferablePolicy::OwnsTransferablesIfAny)
|
||||
DiscardTransferables(data_, callbacks, closure);
|
||||
data_.discardTransferables();
|
||||
data_.ownTransferables_ = OwnTransferablePolicy::NoTransferables;
|
||||
data_.refsHeld_.releaseAll();
|
||||
data_.Clear();
|
||||
version_ = 0;
|
||||
}
|
||||
|
||||
bool
|
||||
JSAutoStructuredCloneBuffer::copy(JSContext* cx, const JSStructuredCloneData& srcData,
|
||||
uint32_t version, const JSStructuredCloneCallbacks* callbacks,
|
||||
void* closure)
|
||||
{
|
||||
// transferable objects cannot be copied
|
||||
if (StructuredCloneHasTransferObjects(srcData))
|
||||
return false;
|
||||
|
||||
clear();
|
||||
|
||||
auto iter = srcData.Iter();
|
||||
while (!iter.Done()) {
|
||||
if (!data_.WriteBytes(iter.Data(), iter.RemainingInSegment()))
|
||||
return false;
|
||||
iter.Advance(srcData, iter.RemainingInSegment());
|
||||
}
|
||||
|
||||
version_ = version;
|
||||
|
||||
if (!data_.refsHeld_.acquireAll(cx, srcData.refsHeld_))
|
||||
return false;
|
||||
|
||||
data_.setOptionalCallbacks(callbacks, closure, OwnTransferablePolicy::NoTransferables);
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
JSAutoStructuredCloneBuffer::adopt(JSStructuredCloneData&& data, uint32_t version,
|
||||
const JSStructuredCloneCallbacks* callbacks,
|
||||
@@ -2730,7 +2694,7 @@ JSAutoStructuredCloneBuffer::adopt(JSStructuredCloneData&& data, uint32_t versio
|
||||
clear();
|
||||
data_ = Move(data);
|
||||
version_ = version;
|
||||
data_.setOptionalCallbacks(callbacks, closure, OwnTransferablePolicy::OwnsTransferablesIfAny);
|
||||
data_.setCallbacks(callbacks, closure, OwnTransferablePolicy::OwnsTransferablesIfAny);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -2747,7 +2711,7 @@ JSAutoStructuredCloneBuffer::steal(JSStructuredCloneData* data, uint32_t* versio
|
||||
*data = Move(data_);
|
||||
|
||||
version_ = 0;
|
||||
data_.setOptionalCallbacks(nullptr, nullptr, OwnTransferablePolicy::NoTransferables);
|
||||
data_.setCallbacks(nullptr, nullptr, OwnTransferablePolicy::NoTransferables);
|
||||
}
|
||||
|
||||
bool
|
||||
@@ -2861,5 +2825,5 @@ JS_ObjectNotWritten(JSStructuredCloneWriter* w, HandleObject obj)
|
||||
JS_PUBLIC_API(JS::StructuredCloneScope)
|
||||
JS_GetStructuredCloneScope(JSStructuredCloneWriter* w)
|
||||
{
|
||||
return w->cloneScope();
|
||||
return w->output().scope();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
/* -*- 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/. */
|
||||
|
||||
/* Character/text operations. */
|
||||
|
||||
#ifndef mozilla_TextUtils_h
|
||||
#define mozilla_TextUtils_h
|
||||
|
||||
#include "mozilla/TypeTraits.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<typename Char>
|
||||
class MakeUnsignedChar
|
||||
: public MakeUnsigned<Char>
|
||||
{};
|
||||
|
||||
template<>
|
||||
class MakeUnsignedChar<char16_t>
|
||||
{
|
||||
public:
|
||||
using Type = char16_t;
|
||||
};
|
||||
|
||||
template<>
|
||||
class MakeUnsignedChar<char32_t>
|
||||
{
|
||||
public:
|
||||
using Type = char32_t;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
* Returns true iff |aChar| matches [a-zA-Z].
|
||||
*
|
||||
* This function is basically what you thought isalpha was, except its behavior
|
||||
* doesn't depend on the user's current locale.
|
||||
*/
|
||||
template<typename Char>
|
||||
constexpr bool
|
||||
IsAsciiAlpha(Char aChar)
|
||||
{
|
||||
using UnsignedChar = typename detail::MakeUnsignedChar<Char>::Type;
|
||||
return ('a' <= static_cast<UnsignedChar>(aChar) &&
|
||||
static_cast<UnsignedChar>(aChar) <= 'z') ||
|
||||
('A' <= static_cast<UnsignedChar>(aChar) &&
|
||||
static_cast<UnsignedChar>(aChar) <= 'Z');
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* mozilla_TextUtils_h */
|
||||
@@ -87,6 +87,7 @@ EXPORTS.mozilla = [
|
||||
'StaticAnalysisFunctions.h',
|
||||
'TaggedAnonymousMemory.h',
|
||||
'TemplateLib.h',
|
||||
'TextUtils.h',
|
||||
'ThreadLocal.h',
|
||||
'ToString.h',
|
||||
'Tuple.h',
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
/* -*- 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/. */
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/TextUtils.h"
|
||||
|
||||
using mozilla::IsAsciiAlpha;
|
||||
|
||||
// char
|
||||
|
||||
static_assert(!IsAsciiAlpha('@'), "'@' isn't ASCII alpha");
|
||||
static_assert('@' == 0x40, "'@' has value 0x40");
|
||||
|
||||
static_assert('A' == 0x41, "'A' has value 0x41");
|
||||
static_assert(IsAsciiAlpha('A'), "'A' is ASCII alpha");
|
||||
static_assert(IsAsciiAlpha('B'), "'B' is ASCII alpha");
|
||||
static_assert(IsAsciiAlpha('M'), "'M' is ASCII alpha");
|
||||
static_assert(IsAsciiAlpha('Y'), "'Y' is ASCII alpha");
|
||||
static_assert(IsAsciiAlpha('Z'), "'Z' is ASCII alpha");
|
||||
|
||||
static_assert('Z' == 0x5A, "'Z' has value 0x5A");
|
||||
static_assert('[' == 0x5B, "'[' has value 0x5B");
|
||||
static_assert(!IsAsciiAlpha('['), "'[' isn't ASCII alpha");
|
||||
|
||||
static_assert(!IsAsciiAlpha('`'), "'`' isn't ASCII alpha");
|
||||
static_assert('`' == 0x60, "'`' has value 0x60");
|
||||
|
||||
static_assert('a' == 0x61, "'a' has value 0x61");
|
||||
static_assert(IsAsciiAlpha('a'), "'a' is ASCII alpha");
|
||||
static_assert(IsAsciiAlpha('b'), "'b' is ASCII alpha");
|
||||
static_assert(IsAsciiAlpha('m'), "'m' is ASCII alpha");
|
||||
static_assert(IsAsciiAlpha('y'), "'y' is ASCII alpha");
|
||||
static_assert(IsAsciiAlpha('z'), "'z' is ASCII alpha");
|
||||
|
||||
static_assert('z' == 0x7A, "'z' has value 0x7A");
|
||||
static_assert('{' == 0x7B, "'{' has value 0x7B");
|
||||
static_assert(!IsAsciiAlpha('{'), "'{' isn't ASCII alpha");
|
||||
|
||||
// char16_t
|
||||
|
||||
static_assert(!IsAsciiAlpha(u'@'), "u'@' isn't ASCII alpha");
|
||||
static_assert(u'@' == 0x40, "u'@' has value 0x40");
|
||||
|
||||
static_assert(u'A' == 0x41, "u'A' has value 0x41");
|
||||
static_assert(IsAsciiAlpha(u'A'), "u'A' is ASCII alpha");
|
||||
static_assert(IsAsciiAlpha(u'B'), "u'B' is ASCII alpha");
|
||||
static_assert(IsAsciiAlpha(u'M'), "u'M' is ASCII alpha");
|
||||
static_assert(IsAsciiAlpha(u'Y'), "u'Y' is ASCII alpha");
|
||||
static_assert(IsAsciiAlpha(u'Z'), "u'Z' is ASCII alpha");
|
||||
|
||||
static_assert(u'Z' == 0x5A, "u'Z' has value 0x5A");
|
||||
static_assert(u'[' == 0x5B, "u'[' has value 0x5B");
|
||||
static_assert(!IsAsciiAlpha(u'['), "u'[' isn't ASCII alpha");
|
||||
|
||||
static_assert(!IsAsciiAlpha(u'`'), "u'`' isn't ASCII alpha");
|
||||
static_assert(u'`' == 0x60, "u'`' has value 0x60");
|
||||
|
||||
static_assert(u'a' == 0x61, "u'a' has value 0x61");
|
||||
static_assert(IsAsciiAlpha(u'a'), "u'a' is ASCII alpha");
|
||||
static_assert(IsAsciiAlpha(u'b'), "u'b' is ASCII alpha");
|
||||
static_assert(IsAsciiAlpha(u'm'), "u'm' is ASCII alpha");
|
||||
static_assert(IsAsciiAlpha(u'y'), "u'y' is ASCII alpha");
|
||||
static_assert(IsAsciiAlpha(u'z'), "u'z' is ASCII alpha");
|
||||
|
||||
static_assert(u'z' == 0x7A, "u'z' has value 0x7A");
|
||||
static_assert(u'{' == 0x7B, "u'{' has value 0x7B");
|
||||
static_assert(!IsAsciiAlpha(u'{'), "u'{' isn't ASCII alpha");
|
||||
|
||||
// char32_t
|
||||
|
||||
static_assert(!IsAsciiAlpha(U'@'), "U'@' isn't ASCII alpha");
|
||||
static_assert(U'@' == 0x40, "U'@' has value 0x40");
|
||||
|
||||
static_assert(U'A' == 0x41, "U'A' has value 0x41");
|
||||
static_assert(IsAsciiAlpha(U'A'), "U'A' is ASCII alpha");
|
||||
static_assert(IsAsciiAlpha(U'B'), "U'B' is ASCII alpha");
|
||||
static_assert(IsAsciiAlpha(U'M'), "U'M' is ASCII alpha");
|
||||
static_assert(IsAsciiAlpha(U'Y'), "U'Y' is ASCII alpha");
|
||||
static_assert(IsAsciiAlpha(U'Z'), "U'Z' is ASCII alpha");
|
||||
|
||||
static_assert(U'Z' == 0x5A, "U'Z' has value 0x5A");
|
||||
static_assert(U'[' == 0x5B, "U'[' has value 0x5B");
|
||||
static_assert(!IsAsciiAlpha(U'['), "U'[' isn't ASCII alpha");
|
||||
|
||||
static_assert(!IsAsciiAlpha(U'`'), "U'`' isn't ASCII alpha");
|
||||
static_assert(U'`' == 0x60, "U'`' has value 0x60");
|
||||
|
||||
static_assert(U'a' == 0x61, "U'a' has value 0x61");
|
||||
static_assert(IsAsciiAlpha(U'a'), "U'a' is ASCII alpha");
|
||||
static_assert(IsAsciiAlpha(U'b'), "U'b' is ASCII alpha");
|
||||
static_assert(IsAsciiAlpha(U'm'), "U'm' is ASCII alpha");
|
||||
static_assert(IsAsciiAlpha(U'y'), "U'y' is ASCII alpha");
|
||||
static_assert(IsAsciiAlpha(U'z'), "U'z' is ASCII alpha");
|
||||
|
||||
static_assert(U'z' == 0x7A, "U'z' has value 0x7A");
|
||||
static_assert(U'{' == 0x7B, "U'{' has value 0x7B");
|
||||
static_assert(!IsAsciiAlpha(U'{'), "U'{' isn't ASCII alpha");
|
||||
|
||||
int
|
||||
main()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@@ -42,6 +42,7 @@ CppUnitTests([
|
||||
'TestSHA1',
|
||||
'TestSplayTree',
|
||||
'TestTemplateLib',
|
||||
'TestTextUtils',
|
||||
'TestTuple',
|
||||
'TestTypedEnum',
|
||||
'TestTypeTraits',
|
||||
|
||||
@@ -243,8 +243,7 @@ nsMIMEInfoWin::LoadUriInternal(nsIURI * aURL)
|
||||
SHELLEXECUTEINFOW sinfo;
|
||||
memset(&sinfo, 0, sizeof(sinfo));
|
||||
sinfo.cbSize = sizeof(sinfo);
|
||||
sinfo.fMask = SEE_MASK_FLAG_DDEWAIT |
|
||||
SEE_MASK_FLAG_NO_UI;
|
||||
sinfo.fMask = SEE_MASK_FLAG_DDEWAIT;
|
||||
sinfo.hwnd = nullptr;
|
||||
sinfo.lpVerb = (LPWSTR)&cmdVerb;
|
||||
sinfo.nShow = SW_SHOWNORMAL;
|
||||
|
||||
@@ -68,11 +68,15 @@ public:
|
||||
delete GfxInfoBase::mFeatureStatus;
|
||||
GfxInfoBase::mFeatureStatus = nullptr;
|
||||
|
||||
for (uint32_t i = 0; i < DeviceFamilyMax; i++)
|
||||
for (uint32_t i = 0; i < DeviceFamilyMax; i++) {
|
||||
delete GfxDriverInfo::mDeviceFamilies[i];
|
||||
GfxDriverInfo::mDeviceFamilies[i] = nullptr;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < DeviceVendorMax; i++)
|
||||
for (uint32_t i = 0; i < DeviceVendorMax; i++) {
|
||||
delete GfxDriverInfo::mDeviceVendors[i];
|
||||
GfxDriverInfo::mDeviceVendors[i] = nullptr;
|
||||
}
|
||||
|
||||
GfxInfoBase::mShutdownOccurred = true;
|
||||
|
||||
|
||||
@@ -268,8 +268,6 @@ GfxInfo::GetFeatureStatusImpl(int32_t aFeature,
|
||||
OperatingSystem* aOS /* = nullptr */)
|
||||
|
||||
{
|
||||
GetData();
|
||||
|
||||
NS_ENSURE_ARG_POINTER(aStatus);
|
||||
*aStatus = nsIGfxInfo::FEATURE_STATUS_UNKNOWN;
|
||||
aSuggestedDriverVersion.SetIsVoid(true);
|
||||
@@ -277,6 +275,12 @@ GfxInfo::GetFeatureStatusImpl(int32_t aFeature,
|
||||
if (aOS)
|
||||
*aOS = os;
|
||||
|
||||
if (mShutdownOccurred) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
GetData();
|
||||
|
||||
if (mGLMajorVersion == 1) {
|
||||
// We're on OpenGL 1. In most cases that indicates really old hardware.
|
||||
// We better block them, rather than rely on them to fail gracefully, because they don't!
|
||||
|
||||
@@ -376,6 +376,10 @@ GfxInfo::GetFeatureStatusImpl(int32_t aFeature,
|
||||
if (aOS)
|
||||
*aOS = os;
|
||||
|
||||
if (mShutdownOccurred) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// OpenGL layers are never blacklisted on Android.
|
||||
// This early return is so we avoid potentially slow
|
||||
// GLStrings initialization on startup when we initialize GL layers.
|
||||
|
||||
@@ -307,6 +307,10 @@ GfxInfo::GetFeatureStatusImpl(int32_t aFeature,
|
||||
if (aOS)
|
||||
*aOS = os;
|
||||
|
||||
if (mShutdownOccurred) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Don't evaluate special cases when we're evaluating the downloaded blocklist.
|
||||
if (!aDriverInfo.Length()) {
|
||||
if (aFeature == nsIGfxInfo::FEATURE_WEBGL_MSAA) {
|
||||
|
||||
@@ -175,6 +175,10 @@ GfxInfo::GetFeatureStatusImpl(int32_t aFeature,
|
||||
if (aOS)
|
||||
*aOS = OperatingSystem::Ios;
|
||||
|
||||
if (mShutdownOccurred) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// OpenGL layers are never blacklisted on iOS.
|
||||
// This early return is so we avoid potentially slow
|
||||
// GLStrings initialization on startup when we initialize GL layers.
|
||||
|
||||
@@ -1160,6 +1160,10 @@ GfxInfo::GetFeatureStatusImpl(int32_t aFeature,
|
||||
if (aOS)
|
||||
*aOS = os;
|
||||
|
||||
if (mShutdownOccurred) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Don't evaluate special cases if we're checking the downloaded blocklist.
|
||||
if (!aDriverInfo.Length()) {
|
||||
nsAutoString adapterVendorID;
|
||||
|
||||
Reference in New Issue
Block a user