зеркало из https://github.com/mozilla/gecko-dev.git
Merge inbound to mozilla-central. a=merge
This commit is contained in:
Коммит
bb39524dc6
|
@ -2467,6 +2467,8 @@ void gfxPlatform::InitGPUProcessPrefs() {
|
|||
NS_LITERAL_CSTRING("FEATURE_FAILURE_LAYERSCOPE"));
|
||||
return;
|
||||
}
|
||||
|
||||
InitPlatformGPUProcessPrefs();
|
||||
}
|
||||
|
||||
void gfxPlatform::InitCompositorAccelerationPrefs() {
|
||||
|
|
|
@ -883,6 +883,7 @@ class gfxPlatform : public mozilla::layers::MemoryPressureListener {
|
|||
|
||||
void InitCompositorAccelerationPrefs();
|
||||
void InitGPUProcessPrefs();
|
||||
virtual void InitPlatformGPUProcessPrefs() {}
|
||||
void InitOMTPConfig();
|
||||
|
||||
static bool IsDXInterop2Blocked();
|
||||
|
|
|
@ -121,6 +121,17 @@ void gfxPlatformGtk::FlushContentDrawing() {
|
|||
}
|
||||
}
|
||||
|
||||
void gfxPlatformGtk::InitPlatformGPUProcessPrefs() {
|
||||
#ifdef MOZ_WAYLAND
|
||||
if (!GDK_IS_X11_DISPLAY(gdk_display_get_default())) {
|
||||
FeatureState& gpuProc = gfxConfig::GetFeature(Feature::GPU_PROCESS);
|
||||
gpuProc.ForceDisable(FeatureStatus::Blocked,
|
||||
"Wayland does not work in the GPU process",
|
||||
NS_LITERAL_CSTRING("FEATURE_FAILURE_WAYLAND"));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
already_AddRefed<gfxASurface> gfxPlatformGtk::CreateOffscreenSurface(
|
||||
const IntSize& aSize, gfxImageFormat aFormat) {
|
||||
if (!Factory::AllowedSurfaceSize(aSize)) {
|
||||
|
|
|
@ -106,6 +106,7 @@ class gfxPlatformGtk : public gfxPlatform {
|
|||
#endif
|
||||
|
||||
protected:
|
||||
void InitPlatformGPUProcessPrefs() override;
|
||||
bool CheckVariationFontSupport() override;
|
||||
|
||||
int8_t mMaxGenericSubstitutions;
|
||||
|
|
|
@ -44,7 +44,9 @@ class MOZ_STACK_CLASS AutoRecordDecoderTelemetry final {
|
|||
};
|
||||
|
||||
Decoder::Decoder(RasterImage* aImage)
|
||||
: mImageData(nullptr),
|
||||
: mInProfile(nullptr),
|
||||
mTransform(nullptr),
|
||||
mImageData(nullptr),
|
||||
mImageDataLength(0),
|
||||
mImage(aImage),
|
||||
mFrameRecycler(nullptr),
|
||||
|
@ -72,6 +74,14 @@ Decoder::~Decoder() {
|
|||
"Destroying Decoder without taking all its invalidations");
|
||||
mInitialized = false;
|
||||
|
||||
if (mInProfile) {
|
||||
// mTransform belongs to us only if mInProfile is non-null
|
||||
if (mTransform) {
|
||||
qcms_transform_release(mTransform);
|
||||
}
|
||||
qcms_profile_release(mInProfile);
|
||||
}
|
||||
|
||||
if (mImage && !NS_IsMainThread()) {
|
||||
// Dispatch mImage to main thread to prevent it from being destructed by the
|
||||
// decode thread.
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "SourceBuffer.h"
|
||||
#include "StreamingLexer.h"
|
||||
#include "SurfaceFlags.h"
|
||||
#include "qcms.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
|
@ -559,6 +560,12 @@ class Decoder {
|
|||
protected:
|
||||
Maybe<Downscaler> mDownscaler;
|
||||
|
||||
/// Color management profile from the ICCP chunk in the image.
|
||||
qcms_profile* mInProfile;
|
||||
|
||||
/// Color management transform to apply to image data.
|
||||
qcms_transform* mTransform;
|
||||
|
||||
uint8_t* mImageData; // Pointer to image data in BGRA/X
|
||||
uint32_t mImageDataLength;
|
||||
|
||||
|
|
|
@ -29,6 +29,68 @@
|
|||
namespace mozilla {
|
||||
namespace image {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// ColorManagementFilter
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template <typename Next>
|
||||
class ColorManagementFilter;
|
||||
|
||||
/**
|
||||
* A configuration struct for ColorManagementFilter.
|
||||
*/
|
||||
struct ColorManagementConfig {
|
||||
template <typename Next>
|
||||
using Filter = ColorManagementFilter<Next>;
|
||||
qcms_transform* mTransform;
|
||||
};
|
||||
|
||||
/**
|
||||
* ColorManagementFilter performs color transforms with qcms on rows written
|
||||
* to it.
|
||||
*
|
||||
* The 'Next' template parameter specifies the next filter in the chain.
|
||||
*/
|
||||
template <typename Next>
|
||||
class ColorManagementFilter final : public SurfaceFilter {
|
||||
public:
|
||||
ColorManagementFilter() : mTransform(nullptr) {}
|
||||
|
||||
template <typename... Rest>
|
||||
nsresult Configure(const ColorManagementConfig& aConfig,
|
||||
const Rest&... aRest) {
|
||||
nsresult rv = mNext.Configure(aRest...);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (!aConfig.mTransform) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
mTransform = aConfig.mTransform;
|
||||
ConfigureFilter(mNext.InputSize(), sizeof(uint32_t));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
Maybe<SurfaceInvalidRect> TakeInvalidRect() override {
|
||||
return mNext.TakeInvalidRect();
|
||||
}
|
||||
|
||||
protected:
|
||||
uint8_t* DoResetToFirstRow() override { return mNext.ResetToFirstRow(); }
|
||||
|
||||
uint8_t* DoAdvanceRow() override {
|
||||
uint8_t* rowPtr = mNext.CurrentRowPointer();
|
||||
qcms_transform_data(mTransform, rowPtr, rowPtr, mNext.InputSize().width);
|
||||
return mNext.AdvanceRow();
|
||||
}
|
||||
|
||||
Next mNext; /// The next SurfaceFilter in the chain.
|
||||
|
||||
qcms_transform* mTransform;
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// DeinterlacingFilter
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -89,7 +89,7 @@ class SurfacePipeFactory {
|
|||
Decoder* aDecoder, const nsIntSize& aInputSize,
|
||||
const nsIntSize& aOutputSize, const nsIntRect& aFrameRect,
|
||||
gfx::SurfaceFormat aFormat, const Maybe<AnimationParams>& aAnimParams,
|
||||
SurfacePipeFlags aFlags) {
|
||||
qcms_transform* aTransform, SurfacePipeFlags aFlags) {
|
||||
const bool deinterlace = bool(aFlags & SurfacePipeFlags::DEINTERLACE);
|
||||
const bool flipVertically =
|
||||
bool(aFlags & SurfacePipeFlags::FLIP_VERTICALLY);
|
||||
|
@ -99,6 +99,7 @@ class SurfacePipeFactory {
|
|||
const bool removeFrameRect = !aFrameRect.IsEqualEdges(
|
||||
nsIntRect(0, 0, aInputSize.width, aInputSize.height));
|
||||
const bool blendAnimation = aAnimParams.isSome();
|
||||
const bool colorManagement = aTransform != nullptr;
|
||||
|
||||
// Don't interpolate if we're sure we won't show this surface to the user
|
||||
// until it's completely decoded. The final pass of an ADAM7 image doesn't
|
||||
|
@ -124,63 +125,129 @@ class SurfacePipeFactory {
|
|||
RemoveFrameRectConfig removeFrameRectConfig{aFrameRect};
|
||||
BlendAnimationConfig blendAnimationConfig{aDecoder};
|
||||
DownscalingConfig downscalingConfig{aInputSize, aFormat};
|
||||
ColorManagementConfig colorManagementConfig{aTransform};
|
||||
SurfaceConfig surfaceConfig{aDecoder, aOutputSize, aFormat, flipVertically,
|
||||
aAnimParams};
|
||||
|
||||
Maybe<SurfacePipe> pipe;
|
||||
|
||||
if (downscale) {
|
||||
MOZ_ASSERT(!blendAnimation);
|
||||
if (removeFrameRect) {
|
||||
if (deinterlace) {
|
||||
pipe = MakePipe(deinterlacingConfig, removeFrameRectConfig,
|
||||
downscalingConfig, surfaceConfig);
|
||||
} else if (adam7Interpolate) {
|
||||
pipe = MakePipe(interpolatingConfig, removeFrameRectConfig,
|
||||
downscalingConfig, surfaceConfig);
|
||||
} else { // (deinterlace and adam7Interpolate are false)
|
||||
pipe =
|
||||
MakePipe(removeFrameRectConfig, downscalingConfig, surfaceConfig);
|
||||
if (colorManagement) {
|
||||
if (downscale) {
|
||||
MOZ_ASSERT(!blendAnimation);
|
||||
if (removeFrameRect) {
|
||||
if (deinterlace) {
|
||||
pipe = MakePipe(deinterlacingConfig, removeFrameRectConfig,
|
||||
downscalingConfig, colorManagementConfig,
|
||||
surfaceConfig);
|
||||
} else if (adam7Interpolate) {
|
||||
pipe = MakePipe(interpolatingConfig, removeFrameRectConfig,
|
||||
downscalingConfig, colorManagementConfig,
|
||||
surfaceConfig);
|
||||
} else { // (deinterlace and adam7Interpolate are false)
|
||||
pipe = MakePipe(removeFrameRectConfig, downscalingConfig,
|
||||
colorManagementConfig, surfaceConfig);
|
||||
}
|
||||
} else { // (removeFrameRect is false)
|
||||
if (deinterlace) {
|
||||
pipe = MakePipe(deinterlacingConfig, downscalingConfig,
|
||||
colorManagementConfig, surfaceConfig);
|
||||
} else if (adam7Interpolate) {
|
||||
pipe = MakePipe(interpolatingConfig, downscalingConfig,
|
||||
colorManagementConfig, surfaceConfig);
|
||||
} else { // (deinterlace and adam7Interpolate are false)
|
||||
pipe = MakePipe(downscalingConfig, colorManagementConfig,
|
||||
surfaceConfig);
|
||||
}
|
||||
}
|
||||
} else { // (removeFrameRect is false)
|
||||
if (deinterlace) {
|
||||
pipe =
|
||||
MakePipe(deinterlacingConfig, downscalingConfig, surfaceConfig);
|
||||
} else if (adam7Interpolate) {
|
||||
pipe =
|
||||
MakePipe(interpolatingConfig, downscalingConfig, surfaceConfig);
|
||||
} else { // (deinterlace and adam7Interpolate are false)
|
||||
pipe = MakePipe(downscalingConfig, surfaceConfig);
|
||||
} else { // (downscale is false)
|
||||
if (blendAnimation) {
|
||||
if (deinterlace) {
|
||||
pipe = MakePipe(deinterlacingConfig, colorManagementConfig,
|
||||
blendAnimationConfig, surfaceConfig);
|
||||
} else if (adam7Interpolate) {
|
||||
pipe = MakePipe(interpolatingConfig, colorManagementConfig,
|
||||
blendAnimationConfig, surfaceConfig);
|
||||
} else { // (deinterlace and adam7Interpolate are false)
|
||||
pipe = MakePipe(colorManagementConfig, blendAnimationConfig,
|
||||
surfaceConfig);
|
||||
}
|
||||
} else if (removeFrameRect) {
|
||||
if (deinterlace) {
|
||||
pipe = MakePipe(deinterlacingConfig, colorManagementConfig,
|
||||
removeFrameRectConfig, surfaceConfig);
|
||||
} else if (adam7Interpolate) {
|
||||
pipe = MakePipe(interpolatingConfig, colorManagementConfig,
|
||||
removeFrameRectConfig, surfaceConfig);
|
||||
} else { // (deinterlace and adam7Interpolate are false)
|
||||
pipe = MakePipe(colorManagementConfig, removeFrameRectConfig,
|
||||
surfaceConfig);
|
||||
}
|
||||
} else { // (blendAnimation and removeFrameRect is false)
|
||||
if (deinterlace) {
|
||||
pipe = MakePipe(deinterlacingConfig, colorManagementConfig,
|
||||
surfaceConfig);
|
||||
} else if (adam7Interpolate) {
|
||||
pipe = MakePipe(interpolatingConfig, colorManagementConfig,
|
||||
surfaceConfig);
|
||||
} else { // (deinterlace and adam7Interpolate are false)
|
||||
pipe = MakePipe(colorManagementConfig, surfaceConfig);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else { // (downscale is false)
|
||||
if (blendAnimation) {
|
||||
if (deinterlace) {
|
||||
pipe = MakePipe(deinterlacingConfig, blendAnimationConfig,
|
||||
surfaceConfig);
|
||||
} else if (adam7Interpolate) {
|
||||
pipe = MakePipe(interpolatingConfig, blendAnimationConfig,
|
||||
surfaceConfig);
|
||||
} else { // (deinterlace and adam7Interpolate are false)
|
||||
pipe = MakePipe(blendAnimationConfig, surfaceConfig);
|
||||
} else { // (colorManagement is false)
|
||||
if (downscale) {
|
||||
MOZ_ASSERT(!blendAnimation);
|
||||
if (removeFrameRect) {
|
||||
if (deinterlace) {
|
||||
pipe = MakePipe(deinterlacingConfig, removeFrameRectConfig,
|
||||
downscalingConfig, surfaceConfig);
|
||||
} else if (adam7Interpolate) {
|
||||
pipe = MakePipe(interpolatingConfig, removeFrameRectConfig,
|
||||
downscalingConfig, surfaceConfig);
|
||||
} else { // (deinterlace and adam7Interpolate are false)
|
||||
pipe = MakePipe(removeFrameRectConfig, downscalingConfig,
|
||||
surfaceConfig);
|
||||
}
|
||||
} else { // (removeFrameRect is false)
|
||||
if (deinterlace) {
|
||||
pipe =
|
||||
MakePipe(deinterlacingConfig, downscalingConfig, surfaceConfig);
|
||||
} else if (adam7Interpolate) {
|
||||
pipe =
|
||||
MakePipe(interpolatingConfig, downscalingConfig, surfaceConfig);
|
||||
} else { // (deinterlace and adam7Interpolate are false)
|
||||
pipe = MakePipe(downscalingConfig, surfaceConfig);
|
||||
}
|
||||
}
|
||||
} else if (removeFrameRect) {
|
||||
if (deinterlace) {
|
||||
pipe = MakePipe(deinterlacingConfig, removeFrameRectConfig,
|
||||
surfaceConfig);
|
||||
} else if (adam7Interpolate) {
|
||||
pipe = MakePipe(interpolatingConfig, removeFrameRectConfig,
|
||||
surfaceConfig);
|
||||
} else { // (deinterlace and adam7Interpolate are false)
|
||||
pipe = MakePipe(removeFrameRectConfig, surfaceConfig);
|
||||
}
|
||||
} else { // (blendAnimation and removeFrameRect is false)
|
||||
if (deinterlace) {
|
||||
pipe = MakePipe(deinterlacingConfig, surfaceConfig);
|
||||
} else if (adam7Interpolate) {
|
||||
pipe = MakePipe(interpolatingConfig, surfaceConfig);
|
||||
} else { // (deinterlace and adam7Interpolate are false)
|
||||
pipe = MakePipe(surfaceConfig);
|
||||
} else { // (downscale is false)
|
||||
if (blendAnimation) {
|
||||
if (deinterlace) {
|
||||
pipe = MakePipe(deinterlacingConfig, blendAnimationConfig,
|
||||
surfaceConfig);
|
||||
} else if (adam7Interpolate) {
|
||||
pipe = MakePipe(interpolatingConfig, blendAnimationConfig,
|
||||
surfaceConfig);
|
||||
} else { // (deinterlace and adam7Interpolate are false)
|
||||
pipe = MakePipe(blendAnimationConfig, surfaceConfig);
|
||||
}
|
||||
} else if (removeFrameRect) {
|
||||
if (deinterlace) {
|
||||
pipe = MakePipe(deinterlacingConfig, removeFrameRectConfig,
|
||||
surfaceConfig);
|
||||
} else if (adam7Interpolate) {
|
||||
pipe = MakePipe(interpolatingConfig, removeFrameRectConfig,
|
||||
surfaceConfig);
|
||||
} else { // (deinterlace and adam7Interpolate are false)
|
||||
pipe = MakePipe(removeFrameRectConfig, surfaceConfig);
|
||||
}
|
||||
} else { // (blendAnimation and removeFrameRect is false)
|
||||
if (deinterlace) {
|
||||
pipe = MakePipe(deinterlacingConfig, surfaceConfig);
|
||||
} else if (adam7Interpolate) {
|
||||
pipe = MakePipe(interpolatingConfig, surfaceConfig);
|
||||
} else { // (deinterlace and adam7Interpolate are false)
|
||||
pipe = MakePipe(surfaceConfig);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -193,7 +193,8 @@ nsresult nsGIFDecoder2::BeginImageFrame(const IntRect& aFrameRect,
|
|||
}
|
||||
|
||||
Maybe<SurfacePipe> pipe = SurfacePipeFactory::CreateSurfacePipe(
|
||||
this, Size(), OutputSize(), aFrameRect, format, animParams, pipeFlags);
|
||||
this, Size(), OutputSize(), aFrameRect, format, animParams, mTransform,
|
||||
pipeFlags);
|
||||
mCurrentFrameIndex = mGIFStruct.images_decoded;
|
||||
|
||||
if (!pipe) {
|
||||
|
@ -391,9 +392,10 @@ Tuple<int32_t, Maybe<WriteState>> nsGIFDecoder2::YieldPixels(
|
|||
|
||||
/// Expand the colormap from RGB to Packed ARGB as needed by Cairo.
|
||||
/// And apply any LCMS transformation.
|
||||
static void ConvertColormap(uint32_t* aColormap, uint32_t aColors) {
|
||||
void nsGIFDecoder2::ConvertColormap(uint32_t* aColormap, uint32_t aColors) {
|
||||
// Apply CMS transformation if enabled and available
|
||||
if (gfxPlatform::GetCMSMode() == eCMSMode_All) {
|
||||
if (!(GetSurfaceFlags() & SurfaceFlags::NO_COLORSPACE_CONVERSION) &&
|
||||
gfxPlatform::GetCMSMode() == eCMSMode_All) {
|
||||
qcms_transform* transform = gfxPlatform::GetCMSRGBTransform();
|
||||
if (transform) {
|
||||
qcms_transform_data(transform, aColormap, aColormap, aColors);
|
||||
|
|
|
@ -58,6 +58,9 @@ class nsGIFDecoder2 : public Decoder {
|
|||
/// Called when we finish decoding the entire image.
|
||||
void FlushImageData();
|
||||
|
||||
/// Convert color map to BGRA, applying any necessary CMS tranforms.
|
||||
void ConvertColormap(uint32_t* aColormap, uint32_t aColors);
|
||||
|
||||
/// Transforms a palette index into a pixel.
|
||||
template <typename PixelSize>
|
||||
PixelSize ColormapIndexToPixel(uint8_t aIndex);
|
||||
|
|
|
@ -68,7 +68,7 @@ LexerTransition<nsIconDecoder::State> nsIconDecoder::ReadHeader(
|
|||
MOZ_ASSERT(!mImageData, "Already have a buffer allocated?");
|
||||
Maybe<SurfacePipe> pipe = SurfacePipeFactory::CreateSurfacePipe(
|
||||
this, Size(), OutputSize(), FullFrame(), SurfaceFormat::B8G8R8A8,
|
||||
/* aAnimParams */ Nothing(), SurfacePipeFlags());
|
||||
/* aAnimParams */ Nothing(), mTransform, SurfacePipeFlags());
|
||||
if (!pipe) {
|
||||
return Transition::TerminateFailure();
|
||||
}
|
||||
|
|
|
@ -13,8 +13,7 @@
|
|||
#include "imgFrame.h"
|
||||
#include "Orientation.h"
|
||||
#include "EXIF.h"
|
||||
|
||||
#include "nsIInputStream.h"
|
||||
#include "SurfacePipeFactory.h"
|
||||
|
||||
#include "nspr.h"
|
||||
#include "nsCRT.h"
|
||||
|
@ -37,7 +36,8 @@ extern "C" {
|
|||
# define MOZ_JCS_EXT_NATIVE_ENDIAN_XRGB JCS_EXT_BGRX
|
||||
#endif
|
||||
|
||||
static void cmyk_convert_rgb(JSAMPROW row, JDIMENSION width);
|
||||
static void cmyk_convert_bgra(uint32_t* aInput, uint32_t* aOutput,
|
||||
int32_t aWidth);
|
||||
|
||||
using mozilla::gfx::SurfaceFormat;
|
||||
|
||||
|
@ -79,6 +79,7 @@ nsJPEGDecoder::nsJPEGDecoder(RasterImage* aImage,
|
|||
Transition::TerminateSuccess()),
|
||||
mProfile(nullptr),
|
||||
mProfileLength(0),
|
||||
mCMSLine(nullptr),
|
||||
mDecodeStyle(aDecodeStyle) {
|
||||
this->mErr.pub.error_exit = nullptr;
|
||||
this->mErr.pub.emit_message = nullptr;
|
||||
|
@ -108,9 +109,6 @@ nsJPEGDecoder::nsJPEGDecoder(RasterImage* aImage,
|
|||
mBackBuffer = nullptr;
|
||||
mBackBufferLen = mBackBufferSize = mBackBufferUnreadLen = 0;
|
||||
|
||||
mInProfile = nullptr;
|
||||
mTransform = nullptr;
|
||||
|
||||
mCMSMode = 0;
|
||||
|
||||
MOZ_LOG(sJPEGDecoderAccountingLog, LogLevel::Debug,
|
||||
|
@ -124,12 +122,8 @@ nsJPEGDecoder::~nsJPEGDecoder() {
|
|||
|
||||
free(mBackBuffer);
|
||||
mBackBuffer = nullptr;
|
||||
if (mTransform) {
|
||||
qcms_transform_release(mTransform);
|
||||
}
|
||||
if (mInProfile) {
|
||||
qcms_profile_release(mInProfile);
|
||||
}
|
||||
|
||||
delete[] mCMSLine;
|
||||
|
||||
MOZ_LOG(sJPEGDecoderAccountingLog, LogLevel::Debug,
|
||||
("nsJPEGDecoder::~nsJPEGDecoder: Destroying JPEG decoder %p", this));
|
||||
|
@ -263,74 +257,60 @@ LexerTransition<nsJPEGDecoder::State> nsJPEGDecoder::ReadJPEGData(
|
|||
}
|
||||
|
||||
// We're doing a full decode.
|
||||
if (mCMSMode != eCMSMode_Off &&
|
||||
(mInProfile = GetICCProfile(mInfo)) != nullptr) {
|
||||
uint32_t profileSpace = qcms_profile_get_color_space(mInProfile);
|
||||
bool mismatch = false;
|
||||
switch (mInfo.jpeg_color_space) {
|
||||
case JCS_GRAYSCALE:
|
||||
case JCS_RGB:
|
||||
case JCS_YCbCr:
|
||||
// By default, we will output directly to BGRA. If we need to apply
|
||||
// special color transforms, this may change.
|
||||
mInfo.out_color_space = MOZ_JCS_EXT_NATIVE_ENDIAN_XRGB;
|
||||
break;
|
||||
case JCS_CMYK:
|
||||
case JCS_YCCK:
|
||||
// libjpeg can convert from YCCK to CMYK, but not to XRGB.
|
||||
mInfo.out_color_space = JCS_CMYK;
|
||||
break;
|
||||
default:
|
||||
mState = JPEG_ERROR;
|
||||
MOZ_LOG(sJPEGDecoderAccountingLog, LogLevel::Debug,
|
||||
("} (unknown colorpsace (3))"));
|
||||
return Transition::TerminateFailure();
|
||||
}
|
||||
|
||||
if (mCMSMode != eCMSMode_Off) {
|
||||
if ((mInProfile = GetICCProfile(mInfo)) != nullptr &&
|
||||
gfxPlatform::GetCMSOutputProfile()) {
|
||||
uint32_t profileSpace = qcms_profile_get_color_space(mInProfile);
|
||||
|
||||
#ifdef DEBUG_tor
|
||||
fprintf(stderr, "JPEG profileSpace: 0x%08X\n", profileSpace);
|
||||
fprintf(stderr, "JPEG profileSpace: 0x%08X\n", profileSpace);
|
||||
#endif
|
||||
switch (mInfo.jpeg_color_space) {
|
||||
case JCS_GRAYSCALE:
|
||||
if (profileSpace == icSigRgbData) {
|
||||
mInfo.out_color_space = JCS_RGB;
|
||||
} else if (profileSpace != icSigGrayData) {
|
||||
mismatch = true;
|
||||
}
|
||||
break;
|
||||
case JCS_RGB:
|
||||
if (profileSpace != icSigRgbData) {
|
||||
mismatch = true;
|
||||
}
|
||||
break;
|
||||
case JCS_YCbCr:
|
||||
if (profileSpace == icSigRgbData) {
|
||||
mInfo.out_color_space = JCS_RGB;
|
||||
} else {
|
||||
// qcms doesn't support ycbcr
|
||||
mismatch = true;
|
||||
}
|
||||
break;
|
||||
case JCS_CMYK:
|
||||
case JCS_YCCK:
|
||||
// qcms doesn't support cmyk
|
||||
mismatch = true;
|
||||
break;
|
||||
default:
|
||||
mState = JPEG_ERROR;
|
||||
MOZ_LOG(sJPEGDecoderAccountingLog, LogLevel::Debug,
|
||||
("} (unknown colorpsace (1))"));
|
||||
return Transition::TerminateFailure();
|
||||
}
|
||||
|
||||
if (!mismatch) {
|
||||
qcms_data_type type;
|
||||
switch (mInfo.out_color_space) {
|
||||
case JCS_GRAYSCALE:
|
||||
type = QCMS_DATA_GRAY_8;
|
||||
break;
|
||||
case JCS_RGB:
|
||||
type = QCMS_DATA_RGB_8;
|
||||
break;
|
||||
default:
|
||||
mState = JPEG_ERROR;
|
||||
MOZ_LOG(sJPEGDecoderAccountingLog, LogLevel::Debug,
|
||||
("} (unknown colorpsace (2))"));
|
||||
return Transition::TerminateFailure();
|
||||
Maybe<qcms_data_type> type;
|
||||
if (profileSpace == icSigRgbData) {
|
||||
// We can always color manage RGB profiles since it happens at the
|
||||
// end of the pipeline.
|
||||
type.emplace(QCMS_DATA_BGRA_8);
|
||||
} else if (profileSpace == icSigGrayData &&
|
||||
mInfo.jpeg_color_space == JCS_GRAYSCALE) {
|
||||
// We can only color manage gray profiles if the original color
|
||||
// space is grayscale. This means we must downscale after color
|
||||
// management since the downscaler assumes BGRA.
|
||||
mInfo.out_color_space = JCS_GRAYSCALE;
|
||||
type.emplace(QCMS_DATA_GRAY_8);
|
||||
}
|
||||
#if 0
|
||||
// We don't currently support CMYK profiles. The following
|
||||
// code dealt with lcms types. Add something like this
|
||||
// back when we gain support for CMYK.
|
||||
|
||||
// Adobe Photoshop writes YCCK/CMYK files with inverted data
|
||||
if (mInfo.out_color_space == JCS_CMYK) {
|
||||
type |= FLAVOR_SH(mInfo.saw_Adobe_marker ? 1 : 0);
|
||||
}
|
||||
#if 0
|
||||
// We don't currently support CMYK profiles. The following
|
||||
// code dealt with lcms types. Add something like this
|
||||
// back when we gain support for CMYK.
|
||||
|
||||
// Adobe Photoshop writes YCCK/CMYK files with inverted data
|
||||
if (mInfo.out_color_space == JCS_CMYK) {
|
||||
type |= FLAVOR_SH(mInfo.saw_Adobe_marker ? 1 : 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (gfxPlatform::GetCMSOutputProfile()) {
|
||||
if (type) {
|
||||
// Calculate rendering intent.
|
||||
int intent = gfxPlatform::GetRenderingIntent();
|
||||
if (intent == -1) {
|
||||
|
@ -339,40 +319,24 @@ LexerTransition<nsJPEGDecoder::State> nsJPEGDecoder::ReadJPEGData(
|
|||
|
||||
// Create the color management transform.
|
||||
mTransform = qcms_transform_create(
|
||||
mInProfile, type, gfxPlatform::GetCMSOutputProfile(),
|
||||
QCMS_DATA_RGB_8, (qcms_intent)intent);
|
||||
mInProfile, *type, gfxPlatform::GetCMSOutputProfile(),
|
||||
QCMS_DATA_BGRA_8, (qcms_intent)intent);
|
||||
}
|
||||
} else {
|
||||
#ifdef DEBUG_tor
|
||||
fprintf(stderr, "ICM profile colorspace mismatch\n");
|
||||
#endif
|
||||
} else if (mCMSMode == eCMSMode_All) {
|
||||
mTransform = gfxPlatform::GetCMSBGRATransform();
|
||||
}
|
||||
}
|
||||
|
||||
if (!mTransform) {
|
||||
switch (mInfo.jpeg_color_space) {
|
||||
case JCS_GRAYSCALE:
|
||||
case JCS_RGB:
|
||||
case JCS_YCbCr:
|
||||
// if we're not color managing we can decode directly to
|
||||
// MOZ_JCS_EXT_NATIVE_ENDIAN_XRGB
|
||||
if (mCMSMode != eCMSMode_All) {
|
||||
mInfo.out_color_space = MOZ_JCS_EXT_NATIVE_ENDIAN_XRGB;
|
||||
mInfo.out_color_components = 4;
|
||||
} else {
|
||||
mInfo.out_color_space = JCS_RGB;
|
||||
}
|
||||
break;
|
||||
case JCS_CMYK:
|
||||
case JCS_YCCK:
|
||||
// libjpeg can convert from YCCK to CMYK, but not to RGB
|
||||
mInfo.out_color_space = JCS_CMYK;
|
||||
break;
|
||||
default:
|
||||
mState = JPEG_ERROR;
|
||||
MOZ_LOG(sJPEGDecoderAccountingLog, LogLevel::Debug,
|
||||
("} (unknown colorpsace (3))"));
|
||||
return Transition::TerminateFailure();
|
||||
// We don't want to use the pipe buffers directly because we don't want
|
||||
// any reads on non-BGRA formatted data.
|
||||
if (mInfo.out_color_space == JCS_GRAYSCALE ||
|
||||
mInfo.out_color_space == JCS_CMYK) {
|
||||
mCMSLine = new (std::nothrow) uint32_t[mInfo.image_width];
|
||||
if (!mCMSLine) {
|
||||
mState = JPEG_ERROR;
|
||||
MOZ_LOG(sJPEGDecoderAccountingLog, LogLevel::Debug,
|
||||
("} (could allocate buffer for color conversion)"));
|
||||
return Transition::TerminateFailure();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -384,25 +348,24 @@ LexerTransition<nsJPEGDecoder::State> nsJPEGDecoder::ReadJPEGData(
|
|||
/* Used to set up image size so arrays can be allocated */
|
||||
jpeg_calc_output_dimensions(&mInfo);
|
||||
|
||||
MOZ_ASSERT(!mImageData, "Already have a buffer allocated?");
|
||||
nsresult rv = AllocateFrame(OutputSize(), SurfaceFormat::B8G8R8X8);
|
||||
if (NS_FAILED(rv)) {
|
||||
// We handle the transform outside the pipeline if we are outputting in
|
||||
// grayscale, because the pipeline wants BGRA pixels, particularly the
|
||||
// downscaling filter, so we can't handle it after downscaling as would
|
||||
// be optimal.
|
||||
qcms_transform* pipeTransform =
|
||||
mInfo.out_color_space != JCS_GRAYSCALE ? mTransform : nullptr;
|
||||
|
||||
Maybe<SurfacePipe> pipe = SurfacePipeFactory::CreateSurfacePipe(
|
||||
this, Size(), OutputSize(), FullFrame(), SurfaceFormat::B8G8R8X8,
|
||||
Nothing(), pipeTransform, SurfacePipeFlags());
|
||||
if (!pipe) {
|
||||
mState = JPEG_ERROR;
|
||||
MOZ_LOG(sJPEGDecoderAccountingLog, LogLevel::Debug,
|
||||
("} (could not initialize image frame)"));
|
||||
("} (could not initialize surface pipe)"));
|
||||
return Transition::TerminateFailure();
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mImageData, "Should have a buffer now");
|
||||
|
||||
if (mDownscaler) {
|
||||
nsresult rv = mDownscaler->BeginFrame(Size(), Nothing(), mImageData,
|
||||
/* aHasAlpha = */ false);
|
||||
if (NS_FAILED(rv)) {
|
||||
mState = JPEG_ERROR;
|
||||
return Transition::TerminateFailure();
|
||||
}
|
||||
}
|
||||
mPipe = std::move(*pipe);
|
||||
|
||||
MOZ_LOG(sJPEGDecoderAccountingLog, LogLevel::Debug,
|
||||
(" JPEGDecoderAccounting: nsJPEGDecoder::"
|
||||
|
@ -448,20 +411,24 @@ LexerTransition<nsJPEGDecoder::State> nsJPEGDecoder::ReadJPEGData(
|
|||
"nsJPEGDecoder::Write -- "
|
||||
"JPEG_DECOMPRESS_SEQUENTIAL case");
|
||||
|
||||
bool suspend;
|
||||
OutputScanlines(&suspend);
|
||||
|
||||
if (suspend) {
|
||||
MOZ_LOG(sJPEGDecoderAccountingLog, LogLevel::Debug,
|
||||
("} (I/O suspension after OutputScanlines() - SEQUENTIAL)"));
|
||||
return Transition::ContinueUnbuffered(
|
||||
State::JPEG_DATA); // I/O suspension
|
||||
switch (OutputScanlines()) {
|
||||
case WriteState::NEED_MORE_DATA:
|
||||
MOZ_LOG(
|
||||
sJPEGDecoderAccountingLog, LogLevel::Debug,
|
||||
("} (I/O suspension after OutputScanlines() - SEQUENTIAL)"));
|
||||
return Transition::ContinueUnbuffered(
|
||||
State::JPEG_DATA); // I/O suspension
|
||||
case WriteState::FINISHED:
|
||||
NS_ASSERTION(mInfo.output_scanline == mInfo.output_height,
|
||||
"We didn't process all of the data!");
|
||||
mState = JPEG_DONE;
|
||||
break;
|
||||
case WriteState::FAILURE:
|
||||
mState = JPEG_ERROR;
|
||||
MOZ_LOG(sJPEGDecoderAccountingLog, LogLevel::Debug,
|
||||
("} (Error in pipeline from OutputScalines())"));
|
||||
return Transition::TerminateFailure();
|
||||
}
|
||||
|
||||
// If we've completed image output ...
|
||||
NS_ASSERTION(mInfo.output_scanline == mInfo.output_height,
|
||||
"We didn't process all of the data!");
|
||||
mState = JPEG_DONE;
|
||||
}
|
||||
MOZ_FALLTHROUGH; // to decompress progressive JPEG.
|
||||
}
|
||||
|
@ -476,7 +443,7 @@ LexerTransition<nsJPEGDecoder::State> nsJPEGDecoder::ReadJPEGData(
|
|||
status = jpeg_consume_input(&mInfo);
|
||||
} while ((status != JPEG_SUSPENDED) && (status != JPEG_REACHED_EOI));
|
||||
|
||||
for (;;) {
|
||||
while (mState != JPEG_DONE) {
|
||||
if (mInfo.output_scanline == 0) {
|
||||
int scan = mInfo.input_scan_number;
|
||||
|
||||
|
@ -500,43 +467,45 @@ LexerTransition<nsJPEGDecoder::State> nsJPEGDecoder::ReadJPEGData(
|
|||
mInfo.output_scanline = 0;
|
||||
}
|
||||
|
||||
bool suspend;
|
||||
OutputScanlines(&suspend);
|
||||
|
||||
if (suspend) {
|
||||
if (mInfo.output_scanline == 0) {
|
||||
// didn't manage to read any lines - flag so we don't call
|
||||
// jpeg_start_output() multiple times for the same scan
|
||||
mInfo.output_scanline = 0xffffff;
|
||||
}
|
||||
MOZ_LOG(
|
||||
sJPEGDecoderAccountingLog, LogLevel::Debug,
|
||||
("} (I/O suspension after OutputScanlines() - PROGRESSIVE)"));
|
||||
return Transition::ContinueUnbuffered(
|
||||
State::JPEG_DATA); // I/O suspension
|
||||
}
|
||||
|
||||
if (mInfo.output_scanline == mInfo.output_height) {
|
||||
if (!jpeg_finish_output(&mInfo)) {
|
||||
switch (OutputScanlines()) {
|
||||
case WriteState::NEED_MORE_DATA:
|
||||
if (mInfo.output_scanline == 0) {
|
||||
// didn't manage to read any lines - flag so we don't call
|
||||
// jpeg_start_output() multiple times for the same scan
|
||||
mInfo.output_scanline = 0xffffff;
|
||||
}
|
||||
MOZ_LOG(sJPEGDecoderAccountingLog, LogLevel::Debug,
|
||||
("} (I/O suspension after jpeg_finish_output() -"
|
||||
" PROGRESSIVE)"));
|
||||
("} (I/O suspension after OutputScanlines() - "
|
||||
"PROGRESSIVE)"));
|
||||
return Transition::ContinueUnbuffered(
|
||||
State::JPEG_DATA); // I/O suspension
|
||||
}
|
||||
case WriteState::FINISHED:
|
||||
NS_ASSERTION(mInfo.output_scanline == mInfo.output_height,
|
||||
"We didn't process all of the data!");
|
||||
|
||||
if (jpeg_input_complete(&mInfo) &&
|
||||
(mInfo.input_scan_number == mInfo.output_scan_number))
|
||||
if (!jpeg_finish_output(&mInfo)) {
|
||||
MOZ_LOG(sJPEGDecoderAccountingLog, LogLevel::Debug,
|
||||
("} (I/O suspension after jpeg_finish_output() -"
|
||||
" PROGRESSIVE)"));
|
||||
return Transition::ContinueUnbuffered(
|
||||
State::JPEG_DATA); // I/O suspension
|
||||
}
|
||||
|
||||
if (jpeg_input_complete(&mInfo) &&
|
||||
(mInfo.input_scan_number == mInfo.output_scan_number)) {
|
||||
mState = JPEG_DONE;
|
||||
} else {
|
||||
mInfo.output_scanline = 0;
|
||||
mPipe.ResetToFirstRow();
|
||||
}
|
||||
break;
|
||||
|
||||
mInfo.output_scanline = 0;
|
||||
if (mDownscaler) {
|
||||
mDownscaler->ResetForNextProgressivePass();
|
||||
}
|
||||
case WriteState::FAILURE:
|
||||
mState = JPEG_ERROR;
|
||||
MOZ_LOG(sJPEGDecoderAccountingLog, LogLevel::Debug,
|
||||
("} (Error in pipeline from OutputScalines())"));
|
||||
return Transition::TerminateFailure();
|
||||
}
|
||||
}
|
||||
|
||||
mState = JPEG_DONE;
|
||||
}
|
||||
MOZ_FALLTHROUGH; // to finish decompressing.
|
||||
}
|
||||
|
@ -583,7 +552,7 @@ LexerTransition<nsJPEGDecoder::State> nsJPEGDecoder::ReadJPEGData(
|
|||
|
||||
MOZ_ASSERT_UNREACHABLE("Escaped the JPEG decoder state machine");
|
||||
return Transition::TerminateFailure();
|
||||
}
|
||||
} // namespace image
|
||||
|
||||
LexerTransition<nsJPEGDecoder::State> nsJPEGDecoder::FinishedJPEGData() {
|
||||
// Since we set up an unbuffered read for SIZE_MAX bytes, if we actually read
|
||||
|
@ -618,120 +587,45 @@ void nsJPEGDecoder::NotifyDone() {
|
|||
PostDecodeDone();
|
||||
}
|
||||
|
||||
void nsJPEGDecoder::FinishRow(uint32_t aLastSourceRow) {
|
||||
if (mDownscaler) {
|
||||
mDownscaler->CommitRow();
|
||||
if (mDownscaler->HasInvalidation()) {
|
||||
DownscalerInvalidRect invalidRect = mDownscaler->TakeInvalidRect();
|
||||
PostInvalidation(invalidRect.mOriginalSizeRect,
|
||||
Some(invalidRect.mTargetSizeRect));
|
||||
MOZ_ASSERT(!mDownscaler->HasInvalidation());
|
||||
}
|
||||
} else if (aLastSourceRow != mInfo.output_scanline) {
|
||||
PostInvalidation(nsIntRect(0, aLastSourceRow, mInfo.output_width,
|
||||
mInfo.output_scanline - aLastSourceRow));
|
||||
}
|
||||
}
|
||||
|
||||
void nsJPEGDecoder::OutputScanlines(bool* suspend) {
|
||||
*suspend = false;
|
||||
|
||||
while ((mInfo.output_scanline < mInfo.output_height)) {
|
||||
const uint32_t top = mInfo.output_scanline;
|
||||
uint32_t* imageRow = nullptr;
|
||||
if (mDownscaler) {
|
||||
imageRow = reinterpret_cast<uint32_t*>(mDownscaler->RowBuffer());
|
||||
} else {
|
||||
imageRow = reinterpret_cast<uint32_t*>(mImageData) +
|
||||
(mInfo.output_scanline * mInfo.output_width);
|
||||
}
|
||||
|
||||
MOZ_ASSERT(imageRow, "Should have a row buffer here");
|
||||
|
||||
if (mInfo.out_color_space == MOZ_JCS_EXT_NATIVE_ENDIAN_XRGB) {
|
||||
// Special case: scanline will be directly converted into packed ARGB
|
||||
if (jpeg_read_scanlines(&mInfo, (JSAMPARRAY)&imageRow, 1) != 1) {
|
||||
*suspend = true; // suspend
|
||||
break;
|
||||
}
|
||||
FinishRow(top);
|
||||
continue; // all done for this row!
|
||||
}
|
||||
|
||||
JSAMPROW sampleRow = (JSAMPROW)imageRow;
|
||||
if (mInfo.output_components == 3) {
|
||||
// Put the pixels at end of row to enable in-place expansion
|
||||
sampleRow += mInfo.output_width;
|
||||
}
|
||||
|
||||
// Request one scanline. Returns 0 or 1 scanlines.
|
||||
if (jpeg_read_scanlines(&mInfo, &sampleRow, 1) != 1) {
|
||||
*suspend = true; // suspend
|
||||
break;
|
||||
}
|
||||
|
||||
if (mTransform) {
|
||||
JSAMPROW source = sampleRow;
|
||||
if (mInfo.out_color_space == JCS_GRAYSCALE) {
|
||||
// Convert from the 1byte grey pixels at begin of row
|
||||
// to the 3byte RGB byte pixels at 'end' of row
|
||||
sampleRow += mInfo.output_width;
|
||||
}
|
||||
qcms_transform_data(mTransform, source, sampleRow, mInfo.output_width);
|
||||
// Move 3byte RGB data to end of row
|
||||
if (mInfo.out_color_space == JCS_CMYK) {
|
||||
memmove(sampleRow + mInfo.output_width, sampleRow,
|
||||
3 * mInfo.output_width);
|
||||
sampleRow += mInfo.output_width;
|
||||
}
|
||||
} else {
|
||||
if (mInfo.out_color_space == JCS_CMYK) {
|
||||
// Convert from CMYK to RGB
|
||||
// We cannot convert directly to Cairo, as the CMSRGBTransform
|
||||
// may wants to do a RGB transform...
|
||||
// Would be better to have platform CMSenabled transformation
|
||||
// from CMYK to (A)RGB...
|
||||
cmyk_convert_rgb((JSAMPROW)imageRow, mInfo.output_width);
|
||||
sampleRow += mInfo.output_width;
|
||||
}
|
||||
if (mCMSMode == eCMSMode_All) {
|
||||
// No embedded ICC profile - treat as sRGB
|
||||
qcms_transform* transform = gfxPlatform::GetCMSRGBTransform();
|
||||
if (transform) {
|
||||
qcms_transform_data(transform, sampleRow, sampleRow,
|
||||
mInfo.output_width);
|
||||
WriteState nsJPEGDecoder::OutputScanlines() {
|
||||
auto result = mPipe.WritePixelBlocks<uint32_t>(
|
||||
[&](uint32_t* aPixelBlock, int32_t aBlockSize) {
|
||||
JSAMPROW sampleRow = (JSAMPROW)(mCMSLine ? mCMSLine : aPixelBlock);
|
||||
if (jpeg_read_scanlines(&mInfo, &sampleRow, 1) != 1) {
|
||||
return MakeTuple(/* aWritten */ 0, Some(WriteState::NEED_MORE_DATA));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// counter for while() loops below
|
||||
uint32_t idx = mInfo.output_width;
|
||||
switch (mInfo.out_color_space) {
|
||||
default:
|
||||
// Already outputted directly to aPixelBlock as BGRA.
|
||||
MOZ_ASSERT(!mCMSLine);
|
||||
break;
|
||||
case JCS_GRAYSCALE:
|
||||
// The transform here does both color management, and converts the
|
||||
// pixels from grayscale to BGRA. This is why we do it here, instead
|
||||
// of using ColorManagementFilter in the SurfacePipe, because the
|
||||
// other filters (e.g. DownscalingFilter) require BGRA pixels.
|
||||
MOZ_ASSERT(mCMSLine);
|
||||
qcms_transform_data(mTransform, mCMSLine, aPixelBlock,
|
||||
mInfo.output_width);
|
||||
break;
|
||||
case JCS_CMYK:
|
||||
// Convert from CMYK to BGRA
|
||||
MOZ_ASSERT(mCMSLine);
|
||||
cmyk_convert_bgra(mCMSLine, aPixelBlock, aBlockSize);
|
||||
break;
|
||||
}
|
||||
|
||||
// copy as bytes until source pointer is 32-bit-aligned
|
||||
for (; (NS_PTR_TO_UINT32(sampleRow) & 0x3) && idx; --idx) {
|
||||
*imageRow++ =
|
||||
gfxPackedPixel(0xFF, sampleRow[0], sampleRow[1], sampleRow[2]);
|
||||
sampleRow += 3;
|
||||
}
|
||||
return MakeTuple(aBlockSize, Maybe<WriteState>());
|
||||
});
|
||||
|
||||
// copy pixels in blocks of 4
|
||||
while (idx >= 4) {
|
||||
GFX_BLOCK_RGB_TO_FRGB(sampleRow, imageRow);
|
||||
idx -= 4;
|
||||
sampleRow += 12;
|
||||
imageRow += 4;
|
||||
}
|
||||
|
||||
// copy remaining pixel(s)
|
||||
while (idx--) {
|
||||
// 32-bit read of final pixel will exceed buffer, so read bytes
|
||||
*imageRow++ =
|
||||
gfxPackedPixel(0xFF, sampleRow[0], sampleRow[1], sampleRow[2]);
|
||||
sampleRow += 3;
|
||||
}
|
||||
|
||||
FinishRow(top);
|
||||
Maybe<SurfaceInvalidRect> invalidRect = mPipe.TakeInvalidRect();
|
||||
if (invalidRect) {
|
||||
PostInvalidation(invalidRect->mInputSpaceRect,
|
||||
Some(invalidRect->mOutputSpaceRect));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Override the standard error method in the IJG JPEG decoder code.
|
||||
|
@ -960,18 +854,16 @@ term_source(j_decompress_ptr jd) {
|
|||
///*************** Inverted CMYK -> RGB conversion *************************
|
||||
/// Input is (Inverted) CMYK stored as 4 bytes per pixel.
|
||||
/// Output is RGB stored as 3 bytes per pixel.
|
||||
/// @param row Points to row buffer containing the CMYK bytes for each pixel
|
||||
/// in the row.
|
||||
/// @param width Number of pixels in the row.
|
||||
static void cmyk_convert_rgb(JSAMPROW row, JDIMENSION width) {
|
||||
// Work from end to front to shrink from 4 bytes per pixel to 3
|
||||
JSAMPROW in = row + width * 4;
|
||||
JSAMPROW out = in;
|
||||
|
||||
for (uint32_t i = width; i > 0; i--) {
|
||||
in -= 4;
|
||||
out -= 3;
|
||||
/// @param aInput Points to row buffer containing the CMYK bytes for each pixel
|
||||
/// in the row.
|
||||
/// @param aOutput Points to row buffer to write BGRA to.
|
||||
/// @param aWidth Number of pixels in the row.
|
||||
static void cmyk_convert_bgra(uint32_t* aInput, uint32_t* aOutput,
|
||||
int32_t aWidth) {
|
||||
uint8_t* input = reinterpret_cast<uint8_t*>(aInput);
|
||||
uint8_t* output = reinterpret_cast<uint8_t*>(aOutput);
|
||||
|
||||
for (int32_t i = 0; i < aWidth; ++i) {
|
||||
// Source is 'Inverted CMYK', output is RGB.
|
||||
// See: http://www.easyrgb.com/math.php?MATH=M12#text12
|
||||
// Or: http://www.ilkeratalay.com/colorspacesfaq.php#rgb
|
||||
|
@ -991,12 +883,23 @@ static void cmyk_convert_rgb(JSAMPROW row, JDIMENSION width) {
|
|||
// B = 1 - Y => 1 - (1 - iY*iK) => iY*iK
|
||||
|
||||
// Convert from Inverted CMYK (0..255) to RGB (0..255)
|
||||
const uint32_t iC = in[0];
|
||||
const uint32_t iM = in[1];
|
||||
const uint32_t iY = in[2];
|
||||
const uint32_t iK = in[3];
|
||||
out[0] = iC * iK / 255; // Red
|
||||
out[1] = iM * iK / 255; // Green
|
||||
out[2] = iY * iK / 255; // Blue
|
||||
const uint32_t iC = input[0];
|
||||
const uint32_t iM = input[1];
|
||||
const uint32_t iY = input[2];
|
||||
const uint32_t iK = input[3];
|
||||
#if MOZ_BIG_ENDIAN
|
||||
output[0] = 0xFF; // Alpha
|
||||
output[1] = iC * iK / 255; // Red
|
||||
output[2] = iM * iK / 255; // Green
|
||||
output[3] = iY * iK / 255; // Blue
|
||||
#else
|
||||
output[0] = iY * iK / 255; // Blue
|
||||
output[1] = iM * iK / 255; // Green
|
||||
output[2] = iC * iK / 255; // Red
|
||||
output[3] = 0xFF; // Alpha
|
||||
#endif
|
||||
|
||||
input += 4;
|
||||
output += 4;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
#define mozilla_image_decoders_nsJPEGDecoder_h
|
||||
|
||||
#include "RasterImage.h"
|
||||
#include "SurfacePipe.h"
|
||||
|
||||
// On Windows systems, RasterImage.h brings in 'windows.h', which defines INT32.
|
||||
// But the jpeg decoder has its own definition of INT32. To avoid build issues,
|
||||
// we need to undefine the version from 'windows.h'.
|
||||
|
@ -15,10 +17,6 @@
|
|||
|
||||
#include "Decoder.h"
|
||||
|
||||
#include "nsIInputStream.h"
|
||||
#include "nsIPipe.h"
|
||||
#include "qcms.h"
|
||||
|
||||
extern "C" {
|
||||
#include "jpeglib.h"
|
||||
}
|
||||
|
@ -65,7 +63,7 @@ class nsJPEGDecoder : public Decoder {
|
|||
|
||||
protected:
|
||||
Orientation ReadOrientationFromEXIF();
|
||||
void OutputScanlines(bool* suspend);
|
||||
WriteState OutputScanlines();
|
||||
|
||||
private:
|
||||
friend class DecoderFactory;
|
||||
|
@ -100,14 +98,15 @@ class nsJPEGDecoder : public Decoder {
|
|||
JOCTET* mProfile;
|
||||
uint32_t mProfileLength;
|
||||
|
||||
qcms_profile* mInProfile;
|
||||
qcms_transform* mTransform;
|
||||
uint32_t* mCMSLine;
|
||||
|
||||
bool mReading;
|
||||
|
||||
const Decoder::DecodeStyle mDecodeStyle;
|
||||
|
||||
uint32_t mCMSMode;
|
||||
|
||||
SurfacePipe mPipe;
|
||||
};
|
||||
|
||||
} // namespace image
|
||||
|
|
|
@ -109,8 +109,6 @@ nsPNGDecoder::nsPNGDecoder(RasterImage* aImage)
|
|||
mInfo(nullptr),
|
||||
mCMSLine(nullptr),
|
||||
interlacebuf(nullptr),
|
||||
mInProfile(nullptr),
|
||||
mTransform(nullptr),
|
||||
mFormat(SurfaceFormat::UNKNOWN),
|
||||
mCMSMode(0),
|
||||
mChannels(0),
|
||||
|
@ -118,6 +116,7 @@ nsPNGDecoder::nsPNGDecoder(RasterImage* aImage)
|
|||
mFrameIsHidden(false),
|
||||
mDisablePremultipliedAlpha(false),
|
||||
mGotInfoCallback(false),
|
||||
mUsePipeTransform(false),
|
||||
mNumFrames(0) {}
|
||||
|
||||
nsPNGDecoder::~nsPNGDecoder() {
|
||||
|
@ -130,14 +129,6 @@ nsPNGDecoder::~nsPNGDecoder() {
|
|||
if (interlacebuf) {
|
||||
free(interlacebuf);
|
||||
}
|
||||
if (mInProfile) {
|
||||
qcms_profile_release(mInProfile);
|
||||
|
||||
// mTransform belongs to us only if mInProfile is non-null
|
||||
if (mTransform) {
|
||||
qcms_transform_release(mTransform);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nsPNGDecoder::TransparencyType nsPNGDecoder::GetTransparencyType(
|
||||
|
@ -222,9 +213,10 @@ nsresult nsPNGDecoder::CreateFrame(const FrameInfo& aFrameInfo) {
|
|||
pipeFlags |= SurfacePipeFlags::PROGRESSIVE_DISPLAY;
|
||||
}
|
||||
|
||||
qcms_transform* pipeTransform = mUsePipeTransform ? mTransform : nullptr;
|
||||
Maybe<SurfacePipe> pipe = SurfacePipeFactory::CreateSurfacePipe(
|
||||
this, Size(), OutputSize(), aFrameInfo.mFrameRect, mFormat, animParams,
|
||||
pipeFlags);
|
||||
pipeTransform, pipeFlags);
|
||||
|
||||
if (!pipe) {
|
||||
mPipe = SurfacePipe();
|
||||
|
@ -423,8 +415,7 @@ static void PNGDoGammaCorrection(png_structp png_ptr, png_infop info_ptr) {
|
|||
|
||||
// Adapted from http://www.littlecms.com/pngchrm.c example code
|
||||
static qcms_profile* PNGGetColorProfile(png_structp png_ptr, png_infop info_ptr,
|
||||
int color_type, qcms_data_type* inType,
|
||||
uint32_t* intent) {
|
||||
int color_type, uint32_t* intent) {
|
||||
qcms_profile* profile = nullptr;
|
||||
*intent = QCMS_INTENT_PERCEPTUAL; // Our default
|
||||
|
||||
|
@ -502,24 +493,6 @@ static qcms_profile* PNGGetColorProfile(png_structp png_ptr, png_infop info_ptr,
|
|||
}
|
||||
}
|
||||
|
||||
if (profile) {
|
||||
uint32_t profileSpace = qcms_profile_get_color_space(profile);
|
||||
if (profileSpace == icSigGrayData) {
|
||||
if (color_type & PNG_COLOR_MASK_ALPHA) {
|
||||
*inType = QCMS_DATA_GRAYA_8;
|
||||
} else {
|
||||
*inType = QCMS_DATA_GRAY_8;
|
||||
}
|
||||
} else {
|
||||
if (color_type & PNG_COLOR_MASK_ALPHA ||
|
||||
png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
|
||||
*inType = QCMS_DATA_RGBA_8;
|
||||
} else {
|
||||
*inType = QCMS_DATA_RGB_8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return profile;
|
||||
}
|
||||
|
||||
|
@ -597,25 +570,41 @@ void nsPNGDecoder::info_callback(png_structp png_ptr, png_infop info_ptr) {
|
|||
png_set_scale_16(png_ptr);
|
||||
}
|
||||
|
||||
qcms_data_type inType = QCMS_DATA_RGBA_8;
|
||||
// Let libpng expand interlaced images.
|
||||
const bool isInterlaced = interlace_type == PNG_INTERLACE_ADAM7;
|
||||
if (isInterlaced) {
|
||||
png_set_interlace_handling(png_ptr);
|
||||
}
|
||||
|
||||
uint32_t intent = -1;
|
||||
uint32_t pIntent;
|
||||
if (decoder->mCMSMode != eCMSMode_Off) {
|
||||
intent = gfxPlatform::GetRenderingIntent();
|
||||
decoder->mInProfile =
|
||||
PNGGetColorProfile(png_ptr, info_ptr, color_type, &inType, &pIntent);
|
||||
PNGGetColorProfile(png_ptr, info_ptr, color_type, &pIntent);
|
||||
// If we're not mandating an intent, use the one from the image.
|
||||
if (intent == uint32_t(-1)) {
|
||||
intent = pIntent;
|
||||
}
|
||||
}
|
||||
if (decoder->mInProfile && gfxPlatform::GetCMSOutputProfile()) {
|
||||
qcms_data_type inType;
|
||||
qcms_data_type outType;
|
||||
|
||||
if (color_type & PNG_COLOR_MASK_ALPHA || num_trans) {
|
||||
outType = QCMS_DATA_RGBA_8;
|
||||
uint32_t profileSpace = qcms_profile_get_color_space(decoder->mInProfile);
|
||||
decoder->mUsePipeTransform = profileSpace != icSigGrayData;
|
||||
if (decoder->mUsePipeTransform) {
|
||||
// If the transform happens with SurfacePipe, it will always be in BGRA.
|
||||
inType = QCMS_DATA_BGRA_8;
|
||||
outType = QCMS_DATA_BGRA_8;
|
||||
} else {
|
||||
outType = QCMS_DATA_RGB_8;
|
||||
if (color_type & PNG_COLOR_MASK_ALPHA) {
|
||||
inType = QCMS_DATA_GRAYA_8;
|
||||
outType = QCMS_DATA_RGBA_8;
|
||||
} else {
|
||||
inType = QCMS_DATA_GRAY_8;
|
||||
outType = QCMS_DATA_RGB_8;
|
||||
}
|
||||
}
|
||||
|
||||
decoder->mTransform = qcms_transform_create(
|
||||
|
@ -630,20 +619,11 @@ void nsPNGDecoder::info_callback(png_structp png_ptr, png_infop info_ptr) {
|
|||
}
|
||||
|
||||
if (decoder->mCMSMode == eCMSMode_All) {
|
||||
if (color_type & PNG_COLOR_MASK_ALPHA || num_trans) {
|
||||
decoder->mTransform = gfxPlatform::GetCMSRGBATransform();
|
||||
} else {
|
||||
decoder->mTransform = gfxPlatform::GetCMSRGBTransform();
|
||||
}
|
||||
decoder->mTransform = gfxPlatform::GetCMSBGRATransform();
|
||||
decoder->mUsePipeTransform = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Let libpng expand interlaced images.
|
||||
const bool isInterlaced = interlace_type == PNG_INTERLACE_ADAM7;
|
||||
if (isInterlaced) {
|
||||
png_set_interlace_handling(png_ptr);
|
||||
}
|
||||
|
||||
// now all of those things we set above are used to update various struct
|
||||
// members and whatnot, after which we can get channels, rowbytes, etc.
|
||||
png_read_update_info(png_ptr, info_ptr);
|
||||
|
@ -708,8 +688,8 @@ void nsPNGDecoder::info_callback(png_structp png_ptr, png_infop info_ptr) {
|
|||
}
|
||||
#endif
|
||||
|
||||
if (decoder->mTransform && (channels <= 2 || isInterlaced)) {
|
||||
uint32_t bpp[] = {0, 3, 4, 3, 4};
|
||||
if (decoder->mTransform && !decoder->mUsePipeTransform) {
|
||||
uint32_t bpp[] = {0, 3, 4};
|
||||
decoder->mCMSLine =
|
||||
static_cast<uint8_t*>(malloc(bpp[channels] * frameRect.Width()));
|
||||
if (!decoder->mCMSLine) {
|
||||
|
@ -850,21 +830,11 @@ void nsPNGDecoder::WriteRow(uint8_t* aRow) {
|
|||
uint32_t width = uint32_t(mFrameRect.Width());
|
||||
|
||||
// Apply color management to the row, if necessary, before writing it out.
|
||||
if (mTransform) {
|
||||
if (mCMSLine) {
|
||||
qcms_transform_data(mTransform, rowToWrite, mCMSLine, width);
|
||||
|
||||
// Copy alpha over.
|
||||
if (HasAlphaChannel()) {
|
||||
for (uint32_t i = 0; i < width; ++i) {
|
||||
mCMSLine[4 * i + 3] = rowToWrite[mChannels * i + mChannels - 1];
|
||||
}
|
||||
}
|
||||
|
||||
rowToWrite = mCMSLine;
|
||||
} else {
|
||||
qcms_transform_data(mTransform, rowToWrite, rowToWrite, width);
|
||||
}
|
||||
// This is only needed for grayscale images.
|
||||
if (mTransform && !mUsePipeTransform) {
|
||||
MOZ_ASSERT(mCMSLine);
|
||||
qcms_transform_data(mTransform, rowToWrite, mCMSLine, width);
|
||||
rowToWrite = mCMSLine;
|
||||
}
|
||||
|
||||
// Write this row to the SurfacePipe.
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
|
||||
#include "Decoder.h"
|
||||
#include "png.h"
|
||||
#include "qcms.h"
|
||||
#include "StreamingLexer.h"
|
||||
#include "SurfacePipe.h"
|
||||
|
||||
|
@ -92,8 +91,6 @@ class nsPNGDecoder : public Decoder {
|
|||
nsIntRect mFrameRect;
|
||||
uint8_t* mCMSLine;
|
||||
uint8_t* interlacebuf;
|
||||
qcms_profile* mInProfile;
|
||||
qcms_transform* mTransform;
|
||||
gfx::SurfaceFormat mFormat;
|
||||
|
||||
// whether CMS or premultiplied alpha are forced off
|
||||
|
@ -104,6 +101,7 @@ class nsPNGDecoder : public Decoder {
|
|||
bool mFrameIsHidden;
|
||||
bool mDisablePremultipliedAlpha;
|
||||
bool mGotInfoCallback;
|
||||
bool mUsePipeTransform;
|
||||
|
||||
struct AnimFrameInfo {
|
||||
AnimFrameInfo();
|
||||
|
|
|
@ -30,9 +30,7 @@ nsWebPDecoder::nsWebPDecoder(RasterImage* aImage)
|
|||
mLength(0),
|
||||
mIteratorComplete(false),
|
||||
mNeedDemuxer(true),
|
||||
mGotColorProfile(false),
|
||||
mInProfile(nullptr),
|
||||
mTransform(nullptr) {
|
||||
mGotColorProfile(false) {
|
||||
MOZ_LOG(sWebPLog, LogLevel::Debug,
|
||||
("[this=%p] nsWebPDecoder::nsWebPDecoder", this));
|
||||
}
|
||||
|
@ -44,13 +42,6 @@ nsWebPDecoder::~nsWebPDecoder() {
|
|||
WebPIDelete(mDecoder);
|
||||
WebPFreeDecBuffer(&mBuffer);
|
||||
}
|
||||
if (mInProfile) {
|
||||
// mTransform belongs to us only if mInProfile is non-null
|
||||
if (mTransform) {
|
||||
qcms_transform_release(mTransform);
|
||||
}
|
||||
qcms_profile_release(mInProfile);
|
||||
}
|
||||
}
|
||||
|
||||
LexerResult nsWebPDecoder::ReadData() {
|
||||
|
@ -237,7 +228,8 @@ nsresult nsWebPDecoder::CreateFrame(const nsIntRect& aFrameRect) {
|
|||
}
|
||||
|
||||
Maybe<SurfacePipe> pipe = SurfacePipeFactory::CreateSurfacePipe(
|
||||
this, Size(), OutputSize(), aFrameRect, mFormat, animParams, pipeFlags);
|
||||
this, Size(), OutputSize(), aFrameRect, mFormat, animParams, mTransform,
|
||||
pipeFlags);
|
||||
if (!pipe) {
|
||||
MOZ_LOG(sWebPLog, LogLevel::Error,
|
||||
("[this=%p] nsWebPDecoder::CreateFrame -- no pipe\n", this));
|
||||
|
@ -279,17 +271,17 @@ void nsWebPDecoder::ApplyColorProfile(const char* aProfile, size_t aLength) {
|
|||
}
|
||||
|
||||
auto mode = gfxPlatform::GetCMSMode();
|
||||
if (mode == eCMSMode_Off || (mode == eCMSMode_TaggedOnly && !aProfile)) {
|
||||
if (mode == eCMSMode_Off || (mode == eCMSMode_TaggedOnly && !aProfile) ||
|
||||
!gfxPlatform::GetCMSOutputProfile()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!aProfile || !gfxPlatform::GetCMSOutputProfile()) {
|
||||
if (!aProfile) {
|
||||
MOZ_LOG(sWebPLog, LogLevel::Debug,
|
||||
("[this=%p] nsWebPDecoder::ApplyColorProfile -- not tagged or no "
|
||||
"output "
|
||||
"profile , use sRGB transform\n",
|
||||
("[this=%p] nsWebPDecoder::ApplyColorProfile -- not tagged, use "
|
||||
"sRGB transform\n",
|
||||
this));
|
||||
mTransform = gfxPlatform::GetCMSRGBATransform();
|
||||
mTransform = gfxPlatform::GetCMSBGRATransform();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -303,10 +295,10 @@ void nsWebPDecoder::ApplyColorProfile(const char* aProfile, size_t aLength) {
|
|||
}
|
||||
|
||||
uint32_t profileSpace = qcms_profile_get_color_space(mInProfile);
|
||||
if (profileSpace == icSigGrayData) {
|
||||
if (profileSpace != icSigRgbData) {
|
||||
// WebP doesn't produce grayscale data, this must be corrupt.
|
||||
MOZ_LOG(sWebPLog, LogLevel::Error,
|
||||
("[this=%p] nsWebPDecoder::ApplyColorProfile -- ignoring grayscale "
|
||||
("[this=%p] nsWebPDecoder::ApplyColorProfile -- ignoring non-rgb "
|
||||
"color profile\n",
|
||||
this));
|
||||
return;
|
||||
|
@ -319,9 +311,9 @@ void nsWebPDecoder::ApplyColorProfile(const char* aProfile, size_t aLength) {
|
|||
}
|
||||
|
||||
// Create the color management transform.
|
||||
mTransform = qcms_transform_create(mInProfile, QCMS_DATA_RGBA_8,
|
||||
mTransform = qcms_transform_create(mInProfile, QCMS_DATA_BGRA_8,
|
||||
gfxPlatform::GetCMSOutputProfile(),
|
||||
QCMS_DATA_RGBA_8, (qcms_intent)intent);
|
||||
QCMS_DATA_BGRA_8, (qcms_intent)intent);
|
||||
MOZ_LOG(sWebPLog, LogLevel::Debug,
|
||||
("[this=%p] nsWebPDecoder::ApplyColorProfile -- use tagged "
|
||||
"transform\n",
|
||||
|
@ -471,9 +463,6 @@ LexerResult nsWebPDecoder::ReadSingle(const uint8_t* aData, size_t aLength,
|
|||
|
||||
for (int row = mLastRow; row < lastRow; row++) {
|
||||
uint8_t* src = rowStart + row * stride;
|
||||
if (mTransform) {
|
||||
qcms_transform_data(mTransform, src, src, width);
|
||||
}
|
||||
|
||||
WriteState result;
|
||||
if (mFormat == SurfaceFormat::B8G8R8A8) {
|
||||
|
|
|
@ -97,12 +97,6 @@ class nsWebPDecoder final : public Decoder {
|
|||
|
||||
/// True if we have setup the color profile for the image.
|
||||
bool mGotColorProfile;
|
||||
|
||||
/// Color management profile from the ICCP chunk in the image.
|
||||
qcms_profile* mInProfile;
|
||||
|
||||
/// Color management transform to apply to image data.
|
||||
qcms_transform* mTransform;
|
||||
};
|
||||
|
||||
} // namespace image
|
||||
|
|
|
@ -140,6 +140,8 @@ JSObject* GCRuntime::tryNewTenuredObject(JSContext* cx, AllocKind kind,
|
|||
if (obj) {
|
||||
if (nDynamicSlots) {
|
||||
static_cast<NativeObject*>(obj)->initSlots(slots);
|
||||
AddCellMemory(obj, nDynamicSlots * sizeof(HeapSlot),
|
||||
MemoryUse::ObjectSlots);
|
||||
}
|
||||
} else {
|
||||
js_free(slots);
|
||||
|
|
|
@ -93,7 +93,11 @@ enum class ZealMode {
|
|||
|
||||
#define JS_FOR_EACH_INTERNAL_MEMORY_USE(_) \
|
||||
_(ArrayBufferContents) \
|
||||
_(StringContents)
|
||||
_(StringContents) \
|
||||
_(ObjectElements) \
|
||||
_(ObjectSlots) \
|
||||
_(ScriptPrivateData) \
|
||||
_(LazyScriptData)
|
||||
|
||||
#define JS_FOR_EACH_MEMORY_USE(_) \
|
||||
JS_FOR_EACH_PUBLIC_MEMORY_USE(_) \
|
||||
|
|
|
@ -2968,14 +2968,15 @@ size_t js::TenuringTracer::moveSlotsToTenured(NativeObject* dst,
|
|||
return 0;
|
||||
}
|
||||
|
||||
Zone* zone = src->zone();
|
||||
size_t count = src->numDynamicSlots();
|
||||
|
||||
if (!nursery().isInside(src->slots_)) {
|
||||
AddCellMemory(dst, count * sizeof(HeapSlot), MemoryUse::ObjectSlots);
|
||||
nursery().removeMallocedBuffer(src->slots_);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Zone* zone = src->zone();
|
||||
size_t count = src->numDynamicSlots();
|
||||
|
||||
{
|
||||
AutoEnterOOMUnsafeRegion oomUnsafe;
|
||||
dst->slots_ = zone->pod_malloc<HeapSlot>(count);
|
||||
|
@ -2985,6 +2986,8 @@ size_t js::TenuringTracer::moveSlotsToTenured(NativeObject* dst,
|
|||
}
|
||||
}
|
||||
|
||||
AddCellMemory(dst, count * sizeof(HeapSlot), MemoryUse::ObjectSlots);
|
||||
|
||||
PodCopy(dst->slots_, src->slots_, count);
|
||||
nursery().setSlotsForwardingPointer(src->slots_, dst->slots_, count);
|
||||
return count * sizeof(HeapSlot);
|
||||
|
@ -2997,20 +3000,25 @@ size_t js::TenuringTracer::moveElementsToTenured(NativeObject* dst,
|
|||
return 0;
|
||||
}
|
||||
|
||||
Zone* zone = src->zone();
|
||||
|
||||
ObjectElements* srcHeader = src->getElementsHeader();
|
||||
size_t nslots = srcHeader->numAllocatedElements();
|
||||
|
||||
void* srcAllocatedHeader = src->getUnshiftedElementsHeader();
|
||||
|
||||
/* TODO Bug 874151: Prefer to put element data inline if we have space. */
|
||||
if (!nursery().isInside(srcAllocatedHeader)) {
|
||||
MOZ_ASSERT(src->elements_ == dst->elements_);
|
||||
nursery().removeMallocedBuffer(srcAllocatedHeader);
|
||||
|
||||
AddCellMemory(dst, nslots * sizeof(HeapSlot), MemoryUse::ObjectElements);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ObjectElements* srcHeader = src->getElementsHeader();
|
||||
|
||||
// Shifted elements are copied too.
|
||||
uint32_t numShifted = srcHeader->numShiftedElements();
|
||||
size_t nslots = srcHeader->numAllocatedElements();
|
||||
|
||||
/* Unlike other objects, Arrays can have fixed elements. */
|
||||
if (src->is<ArrayObject>() && nslots <= GetGCKindSlots(dstKind)) {
|
||||
|
@ -3025,7 +3033,6 @@ size_t js::TenuringTracer::moveElementsToTenured(NativeObject* dst,
|
|||
|
||||
MOZ_ASSERT(nslots >= 2);
|
||||
|
||||
Zone* zone = src->zone();
|
||||
ObjectElements* dstHeader;
|
||||
{
|
||||
AutoEnterOOMUnsafeRegion oomUnsafe;
|
||||
|
@ -3037,6 +3044,8 @@ size_t js::TenuringTracer::moveElementsToTenured(NativeObject* dst,
|
|||
}
|
||||
}
|
||||
|
||||
AddCellMemory(dst, nslots * sizeof(HeapSlot), MemoryUse::ObjectElements);
|
||||
|
||||
js_memcpy(dstHeader, srcAllocatedHeader, nslots * sizeof(HeapSlot));
|
||||
dst->elements_ = dstHeader->elements() + numShifted;
|
||||
nursery().setElementsForwardingPointer(srcHeader, dst->getElementsHeader(),
|
||||
|
|
|
@ -681,6 +681,11 @@ class MemoryTracker {
|
|||
untrackMemory(cell, nbytes, use);
|
||||
#endif
|
||||
}
|
||||
void swapMemory(Cell* a, Cell* b, MemoryUse use) {
|
||||
#ifdef DEBUG
|
||||
swapTrackedMemory(a, b, use);
|
||||
#endif
|
||||
}
|
||||
|
||||
size_t bytes() const { return bytes_; }
|
||||
|
||||
|
@ -692,9 +697,6 @@ class MemoryTracker {
|
|||
bytes_;
|
||||
|
||||
#ifdef DEBUG
|
||||
void trackMemory(Cell* cell, size_t nbytes, MemoryUse use);
|
||||
void untrackMemory(Cell* cell, size_t nbytes, MemoryUse use);
|
||||
|
||||
struct Key {
|
||||
Key(Cell* cell, MemoryUse use);
|
||||
Cell* cell() const;
|
||||
|
@ -718,9 +720,14 @@ class MemoryTracker {
|
|||
static void rekey(Key& k, const Key& newKey);
|
||||
};
|
||||
|
||||
Mutex mutex;
|
||||
|
||||
using Map = HashMap<Key, size_t, Hasher, SystemAllocPolicy>;
|
||||
|
||||
void trackMemory(Cell* cell, size_t nbytes, MemoryUse use);
|
||||
void untrackMemory(Cell* cell, size_t nbytes, MemoryUse use);
|
||||
void swapTrackedMemory(Cell* a, Cell* b, MemoryUse use);
|
||||
size_t getAndRemoveEntry(const Key& key, LockGuard<Mutex>& lock);
|
||||
|
||||
Mutex mutex;
|
||||
Map map;
|
||||
#endif
|
||||
};
|
||||
|
|
|
@ -595,6 +595,37 @@ void MemoryTracker::untrackMemory(Cell* cell, size_t nbytes, MemoryUse use) {
|
|||
map.remove(ptr);
|
||||
}
|
||||
|
||||
void MemoryTracker::swapTrackedMemory(Cell* a, Cell* b, MemoryUse use) {
|
||||
MOZ_ASSERT(a->isTenured());
|
||||
MOZ_ASSERT(b->isTenured());
|
||||
|
||||
Key ka{a, use};
|
||||
Key kb{b, use};
|
||||
|
||||
LockGuard<Mutex> lock(mutex);
|
||||
|
||||
size_t sa = getAndRemoveEntry(ka, lock);
|
||||
size_t sb = getAndRemoveEntry(kb, lock);
|
||||
|
||||
AutoEnterOOMUnsafeRegion oomUnsafe;
|
||||
|
||||
if ((sa && !map.put(kb, sa)) ||
|
||||
(sb && !map.put(ka, sb))) {
|
||||
oomUnsafe.crash("MemoryTracker::swapTrackedMemory");
|
||||
}
|
||||
}
|
||||
|
||||
size_t MemoryTracker::getAndRemoveEntry(const Key& key, LockGuard<Mutex>& lock) {
|
||||
auto ptr = map.lookup(key);
|
||||
if (!ptr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t size = ptr->value();
|
||||
map.remove(ptr);
|
||||
return size;
|
||||
}
|
||||
|
||||
void MemoryTracker::fixupAfterMovingGC() {
|
||||
// Update the table after we move GC things. We don't use MovableCellHasher
|
||||
// because that would create a difference between debug and release builds.
|
||||
|
|
|
@ -508,6 +508,9 @@ class Zone : public JS::shadow::Zone,
|
|||
void removeCellMemory(js::gc::Cell* cell, size_t nbytes, js::MemoryUse use) {
|
||||
gcMallocSize.removeMemory(cell, nbytes, use);
|
||||
}
|
||||
void swapCellMemory(js::gc::Cell* a, js::gc::Cell* b, js::MemoryUse use) {
|
||||
gcMallocSize.swapMemory(a, b, use);
|
||||
}
|
||||
|
||||
size_t totalBytes() const {
|
||||
return zoneSize.gcBytes() + gcMallocSize.bytes();
|
||||
|
|
|
@ -20,6 +20,35 @@
|
|||
#include "vm/ObjectOperations-inl.h" // js::MaybeHasInterestingSymbolProperty
|
||||
#include "vm/Realm-inl.h"
|
||||
|
||||
MOZ_ALWAYS_INLINE uint32_t js::NativeObject::numDynamicSlots() const {
|
||||
return dynamicSlotsCount(numFixedSlots(), slotSpan(), getClass());
|
||||
}
|
||||
|
||||
/* static */ MOZ_ALWAYS_INLINE uint32_t js::NativeObject::dynamicSlotsCount(
|
||||
uint32_t nfixed, uint32_t span, const Class* clasp) {
|
||||
if (span <= nfixed) {
|
||||
return 0;
|
||||
}
|
||||
span -= nfixed;
|
||||
|
||||
// Increase the slots to SLOT_CAPACITY_MIN to decrease the likelihood
|
||||
// the dynamic slots need to get increased again. ArrayObjects ignore
|
||||
// this because slots are uncommon in that case.
|
||||
if (clasp != &ArrayObject::class_ && span <= SLOT_CAPACITY_MIN) {
|
||||
return SLOT_CAPACITY_MIN;
|
||||
}
|
||||
|
||||
uint32_t slots = mozilla::RoundUpPow2(span);
|
||||
MOZ_ASSERT(slots >= span);
|
||||
return slots;
|
||||
}
|
||||
|
||||
/* static */ MOZ_ALWAYS_INLINE uint32_t
|
||||
js::NativeObject::dynamicSlotsCount(Shape* shape) {
|
||||
return dynamicSlotsCount(shape->numFixedSlots(), shape->slotSpan(),
|
||||
shape->getObjectClass());
|
||||
}
|
||||
|
||||
inline void JSObject::finalize(js::FreeOp* fop) {
|
||||
js::probes::FinalizeObject(this);
|
||||
|
||||
|
@ -45,21 +74,24 @@ inline void JSObject::finalize(js::FreeOp* fop) {
|
|||
}
|
||||
|
||||
if (nobj->hasDynamicSlots()) {
|
||||
fop->free_(nobj->slots_);
|
||||
size_t size = nobj->numDynamicSlots() * sizeof(js::HeapSlot);
|
||||
fop->free_(this, nobj->slots_, size, js::MemoryUse::ObjectSlots);
|
||||
}
|
||||
|
||||
if (nobj->hasDynamicElements()) {
|
||||
js::ObjectElements* elements = nobj->getElementsHeader();
|
||||
size_t size = elements->numAllocatedElements() * sizeof(js::HeapSlot);
|
||||
if (elements->isCopyOnWrite()) {
|
||||
if (elements->ownerObject() == this) {
|
||||
// Don't free the elements until object finalization finishes,
|
||||
// so that other objects can access these elements while they
|
||||
// are themselves finalized.
|
||||
MOZ_ASSERT(elements->numShiftedElements() == 0);
|
||||
fop->freeLater(elements);
|
||||
fop->freeLater(this, elements, size, js::MemoryUse::ObjectElements);
|
||||
}
|
||||
} else {
|
||||
fop->free_(nobj->getUnshiftedElementsHeader());
|
||||
fop->free_(this, nobj->getUnshiftedElementsHeader(), size,
|
||||
js::MemoryUse::ObjectElements);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1703,11 +1703,15 @@ template XDRResult js::XDRObjectLiteral(XDRState<XDR_DECODE>* xdr,
|
|||
|
||||
/* static */
|
||||
bool NativeObject::fillInAfterSwap(JSContext* cx, HandleNativeObject obj,
|
||||
HandleValueVector values, void* priv) {
|
||||
NativeObject* old, HandleValueVector values,
|
||||
void* priv) {
|
||||
// This object has just been swapped with some other object, and its shape
|
||||
// no longer reflects its allocated size. Correct this information and
|
||||
// fill the slots in with the specified values.
|
||||
MOZ_ASSERT(obj->slotSpan() == values.length());
|
||||
MOZ_ASSERT(!IsInsideNursery(obj));
|
||||
|
||||
size_t oldSlotCount = obj->numDynamicSlots();
|
||||
|
||||
// Make sure the shape's numFixedSlots() is correct.
|
||||
size_t nfixed =
|
||||
|
@ -1725,7 +1729,10 @@ bool NativeObject::fillInAfterSwap(JSContext* cx, HandleNativeObject obj,
|
|||
MOZ_ASSERT(!priv);
|
||||
}
|
||||
|
||||
Zone* zone = obj->zone();
|
||||
if (obj->slots_) {
|
||||
size_t size = oldSlotCount * sizeof(HeapSlot);
|
||||
zone->removeCellMemory(old, size, MemoryUse::ObjectSlots);
|
||||
js_free(obj->slots_);
|
||||
obj->slots_ = nullptr;
|
||||
}
|
||||
|
@ -1736,6 +1743,8 @@ bool NativeObject::fillInAfterSwap(JSContext* cx, HandleNativeObject obj,
|
|||
if (!obj->slots_) {
|
||||
return false;
|
||||
}
|
||||
size_t size = ndynamic * sizeof(HeapSlot);
|
||||
zone->addCellMemory(obj, size, MemoryUse::ObjectSlots);
|
||||
Debug_SetSlotRangeToCrashOnTouch(obj->slots_, ndynamic);
|
||||
}
|
||||
|
||||
|
@ -1866,9 +1875,17 @@ void JSObject::swap(JSContext* cx, HandleObject a, HandleObject b) {
|
|||
bool bIsProxyWithInlineValues =
|
||||
b->is<ProxyObject>() && b->as<ProxyObject>().usingInlineValueArray();
|
||||
|
||||
// Swap element associations.
|
||||
Zone* zone = a->zone();
|
||||
zone->swapCellMemory(a, b, MemoryUse::ObjectElements);
|
||||
|
||||
if (a->tenuredSizeOfThis() == b->tenuredSizeOfThis()) {
|
||||
// When both objects are the same size, just do a plain swap of their
|
||||
// contents.
|
||||
|
||||
// Swap slot associations.
|
||||
zone->swapCellMemory(a, b, MemoryUse::ObjectSlots);
|
||||
|
||||
size_t size = a->tenuredSizeOfThis();
|
||||
|
||||
char tmp[mozilla::tl::Max<sizeof(JSFunction),
|
||||
|
@ -1949,13 +1966,13 @@ void JSObject::swap(JSContext* cx, HandleObject a, HandleObject b) {
|
|||
b->fixDictionaryShapeAfterSwap();
|
||||
|
||||
if (na) {
|
||||
if (!NativeObject::fillInAfterSwap(cx, b.as<NativeObject>(), avals,
|
||||
if (!NativeObject::fillInAfterSwap(cx, b.as<NativeObject>(), na, avals,
|
||||
apriv)) {
|
||||
oomUnsafe.crash("fillInAfterSwap");
|
||||
}
|
||||
}
|
||||
if (nb) {
|
||||
if (!NativeObject::fillInAfterSwap(cx, a.as<NativeObject>(), bvals,
|
||||
if (!NativeObject::fillInAfterSwap(cx, a.as<NativeObject>(), nb, bvals,
|
||||
bpriv)) {
|
||||
oomUnsafe.crash("fillInAfterSwap");
|
||||
}
|
||||
|
@ -1985,7 +2002,6 @@ void JSObject::swap(JSContext* cx, HandleObject a, HandleObject b) {
|
|||
* Normally write barriers happen before the write. However, that's not
|
||||
* necessary here because nothing is being destroyed. We're just swapping.
|
||||
*/
|
||||
JS::Zone* zone = a->zone();
|
||||
if (zone->needsIncrementalBarrier()) {
|
||||
a->traceChildren(zone->barrierTracer());
|
||||
b->traceChildren(zone->barrierTracer());
|
||||
|
|
|
@ -3760,6 +3760,7 @@ bool JSScript::createPrivateScriptData(JSContext* cx, HandleScript script,
|
|||
uint32_t nscopenotes,
|
||||
uint32_t nresumeoffsets) {
|
||||
cx->check(script);
|
||||
MOZ_ASSERT(!script->data_);
|
||||
|
||||
uint32_t dataSize;
|
||||
|
||||
|
@ -3772,6 +3773,8 @@ bool JSScript::createPrivateScriptData(JSContext* cx, HandleScript script,
|
|||
|
||||
script->data_ = data;
|
||||
script->dataSize_ = dataSize;
|
||||
AddCellMemory(script, dataSize, MemoryUse::ScriptPrivateData);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -4068,8 +4071,9 @@ void JSScript::finalize(FreeOp* fop) {
|
|||
#endif
|
||||
|
||||
if (data_) {
|
||||
AlwaysPoison(data_, 0xdb, computedSizeOfData(), MemCheckKind::MakeNoAccess);
|
||||
fop->free_(data_);
|
||||
size_t size = computedSizeOfData();
|
||||
AlwaysPoison(data_, 0xdb, size, MemCheckKind::MakeNoAccess);
|
||||
fop->free_(this, data_, size, MemoryUse::ScriptPrivateData);
|
||||
}
|
||||
|
||||
freeScriptData();
|
||||
|
@ -4927,7 +4931,12 @@ void JSScript::traceChildren(JSTracer* trc) {
|
|||
}
|
||||
}
|
||||
|
||||
void LazyScript::finalize(FreeOp* fop) { fop->free_(lazyData_); }
|
||||
void LazyScript::finalize(FreeOp* fop) {
|
||||
if (lazyData_) {
|
||||
fop->free_(this, lazyData_, lazyData_->allocationSize(),
|
||||
MemoryUse::LazyScriptData);
|
||||
}
|
||||
}
|
||||
|
||||
size_t JSScript::calculateLiveFixed(jsbytecode* pc) {
|
||||
size_t nlivefixed = numAlwaysLiveFixedSlots();
|
||||
|
@ -5161,6 +5170,10 @@ bool JSScript::formalLivesInArgumentsObject(unsigned argSlot) {
|
|||
return size;
|
||||
}
|
||||
|
||||
inline size_t LazyScriptData::allocationSize() const {
|
||||
return AllocationSize(numClosedOverBindings_, numInnerFunctions_);
|
||||
}
|
||||
|
||||
// Placement-new elements of an array. This should optimize away for types with
|
||||
// trivial default initiation.
|
||||
template <typename T>
|
||||
|
@ -5256,6 +5269,10 @@ LazyScript::LazyScript(JSFunction* fun, ScriptSourceObject& sourceObject,
|
|||
MOZ_ASSERT(function_->compartment() == sourceObject_->compartment());
|
||||
MOZ_ASSERT(sourceStart <= sourceEnd);
|
||||
MOZ_ASSERT(toStringStart <= sourceStart);
|
||||
|
||||
if (data) {
|
||||
AddCellMemory(this, data->allocationSize(), MemoryUse::LazyScriptData);
|
||||
}
|
||||
}
|
||||
|
||||
void LazyScript::initScript(JSScript* script) {
|
||||
|
|
|
@ -2987,6 +2987,7 @@ class alignas(uintptr_t) LazyScriptData final {
|
|||
// Size to allocate
|
||||
static size_t AllocationSize(uint32_t numClosedOverBindings,
|
||||
uint32_t numInnerFunctions);
|
||||
size_t allocationSize() const;
|
||||
|
||||
// Translate an offset into a concrete pointer.
|
||||
template <typename T>
|
||||
|
|
|
@ -534,35 +534,6 @@ NativeObject::createWithTemplate(JSContext* cx, HandleObject templateObject) {
|
|||
return create(cx, kind, heap, shape, group);
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE uint32_t NativeObject::numDynamicSlots() const {
|
||||
return dynamicSlotsCount(numFixedSlots(), slotSpan(), getClass());
|
||||
}
|
||||
|
||||
/* static */ MOZ_ALWAYS_INLINE uint32_t NativeObject::dynamicSlotsCount(
|
||||
uint32_t nfixed, uint32_t span, const Class* clasp) {
|
||||
if (span <= nfixed) {
|
||||
return 0;
|
||||
}
|
||||
span -= nfixed;
|
||||
|
||||
// Increase the slots to SLOT_CAPACITY_MIN to decrease the likelihood
|
||||
// the dynamic slots need to get increased again. ArrayObjects ignore
|
||||
// this because slots are uncommon in that case.
|
||||
if (clasp != &ArrayObject::class_ && span <= SLOT_CAPACITY_MIN) {
|
||||
return SLOT_CAPACITY_MIN;
|
||||
}
|
||||
|
||||
uint32_t slots = mozilla::RoundUpPow2(span);
|
||||
MOZ_ASSERT(slots >= span);
|
||||
return slots;
|
||||
}
|
||||
|
||||
/* static */ MOZ_ALWAYS_INLINE uint32_t
|
||||
NativeObject::dynamicSlotsCount(Shape* shape) {
|
||||
return dynamicSlotsCount(shape->numFixedSlots(), shape->slotSpan(),
|
||||
shape->getObjectClass());
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE bool NativeObject::updateSlotsForSpan(JSContext* cx,
|
||||
size_t oldSpan,
|
||||
size_t newSpan) {
|
||||
|
|
|
@ -321,24 +321,6 @@ void NativeObject::setLastPropertyShrinkFixedSlots(Shape* shape) {
|
|||
setShape(shape);
|
||||
}
|
||||
|
||||
void NativeObject::setLastPropertyMakeNonNative(Shape* shape) {
|
||||
MOZ_ASSERT(!inDictionaryMode());
|
||||
MOZ_ASSERT(!shape->getObjectClass()->isNative());
|
||||
MOZ_ASSERT(shape->zone() == zone());
|
||||
MOZ_ASSERT(shape->slotSpan() == 0);
|
||||
MOZ_ASSERT(shape->numFixedSlots() == 0);
|
||||
|
||||
if (hasDynamicElements()) {
|
||||
js_free(getUnshiftedElementsHeader());
|
||||
}
|
||||
if (hasDynamicSlots()) {
|
||||
js_free(slots_);
|
||||
slots_ = nullptr;
|
||||
}
|
||||
|
||||
setShape(shape);
|
||||
}
|
||||
|
||||
bool NativeObject::setSlotSpan(JSContext* cx, uint32_t span) {
|
||||
MOZ_ASSERT(inDictionaryMode());
|
||||
|
||||
|
@ -375,6 +357,9 @@ bool NativeObject::growSlots(JSContext* cx, uint32_t oldCount,
|
|||
return false;
|
||||
}
|
||||
Debug_SetSlotRangeToCrashOnTouch(slots_, newCount);
|
||||
|
||||
AddCellMemory(this, newCount * sizeof(HeapSlot), MemoryUse::ObjectSlots);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -384,6 +369,9 @@ bool NativeObject::growSlots(JSContext* cx, uint32_t oldCount,
|
|||
return false; /* Leave slots at its old size. */
|
||||
}
|
||||
|
||||
RemoveCellMemory(this, oldCount * sizeof(HeapSlot), MemoryUse::ObjectSlots);
|
||||
AddCellMemory(this, newCount * sizeof(HeapSlot), MemoryUse::ObjectSlots);
|
||||
|
||||
slots_ = newslots;
|
||||
|
||||
Debug_SetSlotRangeToCrashOnTouch(slots_ + oldCount, newCount - oldCount);
|
||||
|
@ -443,6 +431,8 @@ void NativeObject::shrinkSlots(JSContext* cx, uint32_t oldCount,
|
|||
MOZ_ASSERT(newCount < oldCount);
|
||||
|
||||
if (newCount == 0) {
|
||||
RemoveCellMemory(this, numDynamicSlots() * sizeof(HeapSlot),
|
||||
MemoryUse::ObjectSlots);
|
||||
FreeSlots(cx, slots_);
|
||||
slots_ = nullptr;
|
||||
return;
|
||||
|
@ -457,6 +447,9 @@ void NativeObject::shrinkSlots(JSContext* cx, uint32_t oldCount,
|
|||
return; /* Leave slots at its old size. */
|
||||
}
|
||||
|
||||
RemoveCellMemory(this, oldCount * sizeof(HeapSlot), MemoryUse::ObjectSlots);
|
||||
AddCellMemory(this, newCount * sizeof(HeapSlot), MemoryUse::ObjectSlots);
|
||||
|
||||
slots_ = newslots;
|
||||
}
|
||||
|
||||
|
@ -905,10 +898,10 @@ bool NativeObject::growElements(JSContext* cx, uint32_t reqCapacity) {
|
|||
HeapSlot* oldHeaderSlots =
|
||||
reinterpret_cast<HeapSlot*>(getUnshiftedElementsHeader());
|
||||
HeapSlot* newHeaderSlots;
|
||||
uint32_t oldAllocated = 0;
|
||||
if (hasDynamicElements()) {
|
||||
MOZ_ASSERT(oldCapacity <= MAX_DENSE_ELEMENTS_COUNT);
|
||||
uint32_t oldAllocated =
|
||||
oldCapacity + ObjectElements::VALUES_PER_HEADER + numShifted;
|
||||
oldAllocated = oldCapacity + ObjectElements::VALUES_PER_HEADER + numShifted;
|
||||
|
||||
newHeaderSlots = ReallocateObjectBuffer<HeapSlot>(
|
||||
cx, this, oldHeaderSlots, oldAllocated, newAllocated);
|
||||
|
@ -924,12 +917,20 @@ bool NativeObject::growElements(JSContext* cx, uint32_t reqCapacity) {
|
|||
ObjectElements::VALUES_PER_HEADER + initlen + numShifted);
|
||||
}
|
||||
|
||||
if (oldAllocated) {
|
||||
RemoveCellMemory(this, oldAllocated * sizeof(HeapSlot),
|
||||
MemoryUse::ObjectElements);
|
||||
}
|
||||
|
||||
ObjectElements* newheader = reinterpret_cast<ObjectElements*>(newHeaderSlots);
|
||||
elements_ = newheader->elements() + numShifted;
|
||||
getElementsHeader()->capacity = newCapacity;
|
||||
|
||||
Debug_SetSlotRangeToCrashOnTouch(elements_ + initlen, newCapacity - initlen);
|
||||
|
||||
AddCellMemory(this, newAllocated * sizeof(HeapSlot),
|
||||
MemoryUse::ObjectElements);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -980,9 +981,52 @@ void NativeObject::shrinkElements(JSContext* cx, uint32_t reqCapacity) {
|
|||
return; // Leave elements at its old size.
|
||||
}
|
||||
|
||||
RemoveCellMemory(this, oldAllocated * sizeof(HeapSlot),
|
||||
MemoryUse::ObjectElements);
|
||||
|
||||
ObjectElements* newheader = reinterpret_cast<ObjectElements*>(newHeaderSlots);
|
||||
elements_ = newheader->elements() + numShifted;
|
||||
getElementsHeader()->capacity = newCapacity;
|
||||
|
||||
AddCellMemory(this, newAllocated * sizeof(HeapSlot),
|
||||
MemoryUse::ObjectElements);
|
||||
}
|
||||
|
||||
void NativeObject::shrinkCapacityToInitializedLength(JSContext* cx) {
|
||||
// When an array's length becomes non-writable, writes to indexes greater
|
||||
// greater than or equal to the length don't change the array. We handle this
|
||||
// with a check for non-writable length in most places. But in JIT code every
|
||||
// check counts -- so we piggyback the check on the already-required range
|
||||
// check for |index < capacity| by making capacity of arrays with non-writable
|
||||
// length never exceed the length. This mechanism is also used when an object
|
||||
// becomes non-extensible.
|
||||
|
||||
if (getElementsHeader()->numShiftedElements() > 0) {
|
||||
moveShiftedElements();
|
||||
}
|
||||
|
||||
ObjectElements* header = getElementsHeader();
|
||||
uint32_t len = header->initializedLength;
|
||||
MOZ_ASSERT(header->capacity >= len);
|
||||
if (header->capacity == len) {
|
||||
return;
|
||||
}
|
||||
|
||||
shrinkElements(cx, len);
|
||||
|
||||
header = getElementsHeader();
|
||||
uint32_t oldAllocated = header->numAllocatedElements();
|
||||
header->capacity = len;
|
||||
|
||||
// The size of the memory allocation hasn't changed but we lose the actual
|
||||
// capacity information. Make the associated size match the updated capacity.
|
||||
if (!hasFixedElements()) {
|
||||
uint32_t newAllocated = header->numAllocatedElements();
|
||||
RemoveCellMemory(this, oldAllocated * sizeof(HeapSlot),
|
||||
MemoryUse::ObjectElements);
|
||||
AddCellMemory(this, newAllocated * sizeof(HeapSlot),
|
||||
MemoryUse::ObjectElements);
|
||||
}
|
||||
}
|
||||
|
||||
/* static */
|
||||
|
@ -1022,6 +1066,9 @@ bool NativeObject::CopyElementsForWrite(JSContext* cx, NativeObject* obj) {
|
|||
Debug_SetSlotRangeToCrashOnTouch(obj->elements_ + initlen,
|
||||
newCapacity - initlen);
|
||||
|
||||
AddCellMemory(obj, newAllocated * sizeof(HeapSlot),
|
||||
MemoryUse::ObjectElements);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -538,11 +538,6 @@ class NativeObject : public JSObject {
|
|||
// the new properties.
|
||||
void setLastPropertyShrinkFixedSlots(Shape* shape);
|
||||
|
||||
// As for setLastProperty(), but changes the class associated with the
|
||||
// object to a non-native one. This leaves the object with a type and shape
|
||||
// that are (temporarily) inconsistent.
|
||||
void setLastPropertyMakeNonNative(Shape* shape);
|
||||
|
||||
// Newly-created TypedArrays that map a SharedArrayBuffer are
|
||||
// marked as shared by giving them an ObjectElements that has the
|
||||
// ObjectElements::SHARED_MEMORY flag set.
|
||||
|
@ -937,6 +932,7 @@ class NativeObject : public JSObject {
|
|||
|
||||
static MOZ_MUST_USE bool fillInAfterSwap(JSContext* cx,
|
||||
HandleNativeObject obj,
|
||||
NativeObject* old,
|
||||
HandleValueVector values,
|
||||
void* priv);
|
||||
|
||||
|
@ -1215,26 +1211,7 @@ class NativeObject : public JSObject {
|
|||
inline void elementsRangeWriteBarrierPost(uint32_t start, uint32_t count);
|
||||
|
||||
public:
|
||||
// When an array's length becomes non-writable, writes to indexes greater
|
||||
// greater than or equal to the length don't change the array. We handle
|
||||
// this with a check for non-writable length in most places. But in JIT code
|
||||
// every check counts -- so we piggyback the check on the already-required
|
||||
// range check for |index < capacity| by making capacity of arrays with
|
||||
// non-writable length never exceed the length. This mechanism is also used
|
||||
// when an object becomes non-extensible.
|
||||
void shrinkCapacityToInitializedLength(JSContext* cx) {
|
||||
if (getElementsHeader()->numShiftedElements() > 0) {
|
||||
moveShiftedElements();
|
||||
}
|
||||
|
||||
ObjectElements* header = getElementsHeader();
|
||||
uint32_t len = header->initializedLength;
|
||||
if (header->capacity > len) {
|
||||
shrinkElements(cx, len);
|
||||
header = getElementsHeader();
|
||||
header->capacity = len;
|
||||
}
|
||||
}
|
||||
void shrinkCapacityToInitializedLength(JSContext* cx);
|
||||
|
||||
private:
|
||||
void setDenseInitializedLengthInternal(uint32_t length) {
|
||||
|
|
|
@ -191,7 +191,7 @@ pref("xpinstall.signatures.required", true);
|
|||
// constants in AddonManager.jsm for values to use here, and Bug 1405528 for a rationale).
|
||||
pref("extensions.autoDisableScopes", 15);
|
||||
|
||||
pref("extensions.enabledScopes", 1);
|
||||
pref("extensions.enabledScopes", 5);
|
||||
pref("extensions.autoupdate.enabled", true);
|
||||
pref("extensions.autoupdate.interval", 86400);
|
||||
pref("extensions.update.enabled", true);
|
||||
|
|
|
@ -466,7 +466,7 @@ var BrowserApp = {
|
|||
|
||||
if (!ParentalControls.isAllowed(ParentalControls.INSTALL_EXTENSION)) {
|
||||
// Disable extension installs
|
||||
Services.prefs.setIntPref("extensions.enabledScopes", 1);
|
||||
Services.prefs.setIntPref("extensions.enabledScopes", 5);
|
||||
Services.prefs.setIntPref("extensions.autoDisableScopes", 1);
|
||||
Services.prefs.setBoolPref("xpinstall.enabled", false);
|
||||
} else if (ParentalControls.parentalControlsEnabled) {
|
||||
|
|
|
@ -428,28 +428,22 @@ class ExtensionData {
|
|||
}
|
||||
|
||||
let uri = this.rootURI.QueryInterface(Ci.nsIJARURI);
|
||||
let file = uri.JARFile.QueryInterface(Ci.nsIFileURL).file;
|
||||
|
||||
// Normalize the directory path.
|
||||
path = `${uri.JAREntry}/${path}`;
|
||||
path = path.replace(/\/\/+/g, "/").replace(/^\/|\/$/g, "") + "/";
|
||||
if (path === "/") {
|
||||
path = "";
|
||||
}
|
||||
|
||||
// Escape pattern metacharacters.
|
||||
let pattern = path.replace(/[[\]()?*~|$\\]/g, "\\$&") + "*";
|
||||
// Append the sub-directory path to the base JAR URI and normalize the
|
||||
// result.
|
||||
let entry = `${uri.JAREntry}/${path}/`.replace(/\/\/+/g, "/").replace(/^\//, "");
|
||||
uri = Services.io.newURI(`jar:${uri.JARFile.spec}!/${entry}`);
|
||||
|
||||
let results = [];
|
||||
for (let name of aomStartup.enumerateZipFile(file, pattern)) {
|
||||
if (!name.startsWith(path)) {
|
||||
for (let name of aomStartup.enumerateJARSubtree(uri)) {
|
||||
if (!name.startsWith(entry)) {
|
||||
throw new Error("Unexpected ZipReader entry");
|
||||
}
|
||||
|
||||
// The enumerator returns the full path of all entries.
|
||||
// Trim off the leading path, and filter out entries from
|
||||
// subdirectories.
|
||||
name = name.slice(path.length);
|
||||
name = name.slice(entry.length);
|
||||
if (name && !/\/./.test(name)) {
|
||||
results.push({
|
||||
name: name.replace("/", ""),
|
||||
|
|
|
@ -479,6 +479,7 @@ var snapshotFormatters = {
|
|||
"webgl2Extensions",
|
||||
["supportsHardwareH264", "hardware-h264"],
|
||||
["direct2DEnabled", "#Direct2D"],
|
||||
["windowProtocol", "graphics-window-protocol"],
|
||||
"usesTiling",
|
||||
"contentUsesTiling",
|
||||
"offMainThreadPaintEnabled",
|
||||
|
|
|
@ -468,6 +468,11 @@ IsGarbageCollecting:
|
|||
type: boolean
|
||||
ping: true
|
||||
|
||||
IsWayland:
|
||||
description: >
|
||||
If true then the Wayland windowing system was in use.
|
||||
type: boolean
|
||||
|
||||
JavaStackTrace:
|
||||
description: >
|
||||
Java stack trace, only present on Firefox for Android if we encounter an
|
||||
|
|
|
@ -82,6 +82,8 @@ graphics-gpu2-title = GPU #2
|
|||
graphics-decision-log-title = Decision Log
|
||||
graphics-crash-guards-title = Crash Guard Disabled Features
|
||||
graphics-workarounds-title = Workarounds
|
||||
# Windowing system in use on Linux (e.g. X11, Wayland).
|
||||
graphics-window-protocol = Window Protocol
|
||||
place-database-title = Places Database
|
||||
place-database-integrity = Integrity
|
||||
place-database-verify-integrity = Verify Integrity
|
||||
|
|
|
@ -467,6 +467,7 @@ var dataProviders = {
|
|||
OffMainThreadPaintEnabled: "offMainThreadPaintEnabled",
|
||||
OffMainThreadPaintWorkerCount: "offMainThreadPaintWorkerCount",
|
||||
TargetFrameRate: "targetFrameRate",
|
||||
windowProtocol: null,
|
||||
};
|
||||
|
||||
for (let prop in gfxInfoProps) {
|
||||
|
|
|
@ -463,6 +463,9 @@ const SNAPSHOT_SCHEMA = {
|
|||
targetFrameRate: {
|
||||
type: "number",
|
||||
},
|
||||
windowProtocol: {
|
||||
type: "string",
|
||||
},
|
||||
},
|
||||
},
|
||||
media: {
|
||||
|
|
|
@ -75,6 +75,44 @@ nsIFile* AddonManagerStartup::ProfileDir() {
|
|||
|
||||
NS_IMPL_ISUPPORTS(AddonManagerStartup, amIAddonManagerStartup, nsIObserver)
|
||||
|
||||
/*****************************************************************************
|
||||
* URI utils
|
||||
*****************************************************************************/
|
||||
|
||||
static nsresult ParseJARURI(nsIJARURI* uri, nsIURI** jarFile,
|
||||
nsCString& entry) {
|
||||
MOZ_TRY(uri->GetJARFile(jarFile));
|
||||
MOZ_TRY(uri->GetJAREntry(entry));
|
||||
|
||||
// The entry portion of a jar: URI is required to begin with a '/', but for
|
||||
// nested JAR URIs, the leading / of the outer entry is currently stripped.
|
||||
// This is a bug which should be fixed in the JAR URI code, but...
|
||||
if (entry.IsEmpty() || entry[0] != '/') {
|
||||
entry.Insert('/', 0);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
static nsresult ParseJARURI(nsIURI* uri, nsIURI** jarFile, nsCString& entry) {
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIJARURI> jarURI = do_QueryInterface(uri, &rv);
|
||||
MOZ_TRY(rv);
|
||||
|
||||
return ParseJARURI(jarURI, jarFile, entry);
|
||||
}
|
||||
|
||||
static Result<nsCOMPtr<nsIFile>, nsresult> GetFile(nsIURI* uri) {
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(uri, &rv);
|
||||
MOZ_TRY(rv);
|
||||
|
||||
nsCOMPtr<nsIFile> file;
|
||||
MOZ_TRY(fileURL->GetFile(getter_AddRefs(file)));
|
||||
MOZ_ASSERT(file);
|
||||
|
||||
return std::move(file);
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
* File utils
|
||||
*****************************************************************************/
|
||||
|
@ -210,19 +248,11 @@ static Result<FileLocation, nsresult> GetFileLocation(nsIURI* uri) {
|
|||
MOZ_TRY(fileURL->GetFile(getter_AddRefs(file)));
|
||||
location.Init(file);
|
||||
} else {
|
||||
nsCOMPtr<nsIJARURI> jarURI = do_QueryInterface(uri);
|
||||
NS_ENSURE_TRUE(jarURI, Err(NS_ERROR_INVALID_ARG));
|
||||
|
||||
nsCOMPtr<nsIURI> fileURI;
|
||||
MOZ_TRY(jarURI->GetJARFile(getter_AddRefs(fileURI)));
|
||||
|
||||
fileURL = do_QueryInterface(fileURI);
|
||||
NS_ENSURE_TRUE(fileURL, Err(NS_ERROR_INVALID_ARG));
|
||||
|
||||
MOZ_TRY(fileURL->GetFile(getter_AddRefs(file)));
|
||||
|
||||
nsCString entry;
|
||||
MOZ_TRY(jarURI->GetJAREntry(entry));
|
||||
MOZ_TRY(ParseJARURI(uri, getter_AddRefs(fileURI), entry));
|
||||
|
||||
MOZ_TRY_VAR(file, GetFile(fileURI));
|
||||
|
||||
location.Init(file, entry.get());
|
||||
}
|
||||
|
@ -562,24 +592,11 @@ nsresult AddonManagerStartup::DecodeBlob(JS::HandleValue value, JSContext* cx,
|
|||
;
|
||||
}
|
||||
|
||||
nsresult AddonManagerStartup::EnumerateZipFile(nsIFile* file,
|
||||
const nsACString& pattern,
|
||||
uint32_t* countOut,
|
||||
char16_t*** entriesOut) {
|
||||
NS_ENSURE_ARG_POINTER(file);
|
||||
NS_ENSURE_ARG_POINTER(countOut);
|
||||
NS_ENSURE_ARG_POINTER(entriesOut);
|
||||
|
||||
nsCOMPtr<nsIZipReaderCache> zipCache;
|
||||
MOZ_TRY_VAR(zipCache, GetJarCache());
|
||||
|
||||
nsCOMPtr<nsIZipReader> zip;
|
||||
MOZ_TRY(zipCache->GetZip(file, getter_AddRefs(zip)));
|
||||
|
||||
static nsresult EnumerateZip(nsIZipReader* zip, const nsACString& pattern,
|
||||
nsTArray<nsString>& results) {
|
||||
nsCOMPtr<nsIUTF8StringEnumerator> entries;
|
||||
MOZ_TRY(zip->FindEntries(pattern, getter_AddRefs(entries)));
|
||||
|
||||
nsTArray<nsString> results;
|
||||
bool hasMore;
|
||||
while (NS_SUCCEEDED(entries->HasMore(&hasMore)) && hasMore) {
|
||||
nsAutoCString name;
|
||||
|
@ -588,17 +605,62 @@ nsresult AddonManagerStartup::EnumerateZipFile(nsIFile* file,
|
|||
results.AppendElement(NS_ConvertUTF8toUTF16(name));
|
||||
}
|
||||
|
||||
auto strResults = MakeUnique<char16_t*[]>(results.Length());
|
||||
for (uint32_t i = 0; i < results.Length(); i++) {
|
||||
strResults[i] = ToNewUnicode(results[i]);
|
||||
}
|
||||
|
||||
*countOut = results.Length();
|
||||
*entriesOut = strResults.release();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult AddonManagerStartup::EnumerateJAR(nsIURI* uri,
|
||||
const nsACString& pattern,
|
||||
nsTArray<nsString>& results) {
|
||||
nsCOMPtr<nsIZipReaderCache> zipCache;
|
||||
MOZ_TRY_VAR(zipCache, GetJarCache());
|
||||
|
||||
nsCOMPtr<nsIZipReader> zip;
|
||||
nsCOMPtr<nsIFile> file;
|
||||
if (nsCOMPtr<nsIJARURI> jarURI = do_QueryInterface(uri)) {
|
||||
nsCOMPtr<nsIURI> fileURI;
|
||||
nsCString entry;
|
||||
MOZ_TRY(ParseJARURI(jarURI, getter_AddRefs(fileURI), entry));
|
||||
|
||||
MOZ_TRY_VAR(file, GetFile(fileURI));
|
||||
MOZ_TRY(zipCache->GetInnerZip(file, Substring(entry, 1),
|
||||
getter_AddRefs(zip)));
|
||||
} else {
|
||||
MOZ_TRY_VAR(file, GetFile(uri));
|
||||
MOZ_TRY(zipCache->GetZip(file, getter_AddRefs(zip)));
|
||||
}
|
||||
MOZ_ASSERT(zip);
|
||||
|
||||
return EnumerateZip(zip, pattern, results);
|
||||
}
|
||||
|
||||
nsresult AddonManagerStartup::EnumerateJARSubtree(nsIURI* uri,
|
||||
nsTArray<nsString>& results) {
|
||||
nsCOMPtr<nsIURI> fileURI;
|
||||
nsCString entry;
|
||||
MOZ_TRY(ParseJARURI(uri, getter_AddRefs(fileURI), entry));
|
||||
|
||||
// Mangle the path into a pattern to match all child entries by escaping any
|
||||
// existing pattern matching metacharacters it contains and appending "/*".
|
||||
NS_NAMED_LITERAL_CSTRING(metaChars, "[]()?*~|$\\");
|
||||
|
||||
nsCString pattern;
|
||||
pattern.SetCapacity(entry.Length());
|
||||
|
||||
// The first character of the entry name is "/", which we want to skip.
|
||||
for (auto chr : MakeSpan(Substring(entry, 1))) {
|
||||
if (metaChars.FindChar(chr) >= 0) {
|
||||
pattern.Append('\\');
|
||||
}
|
||||
pattern.Append(chr);
|
||||
}
|
||||
if (!pattern.IsEmpty() && !StringEndsWith(pattern, NS_LITERAL_CSTRING("/"))) {
|
||||
pattern.Append('/');
|
||||
}
|
||||
pattern.Append('*');
|
||||
|
||||
return EnumerateJAR(fileURI, pattern, results);
|
||||
}
|
||||
|
||||
nsresult AddonManagerStartup::InitializeURLPreloader() {
|
||||
MOZ_RELEASE_ASSERT(xpc::IsInAutomation());
|
||||
|
||||
|
|
|
@ -45,20 +45,32 @@ interface amIAddonManagerStartup : nsISupports
|
|||
jsval decodeBlob(in jsval value);
|
||||
|
||||
/**
|
||||
* Enumerates over all entries in the given zip file matching the given
|
||||
* pattern, and returns an array of their paths.
|
||||
* Enumerates over all entries in the JAR file at the given URI, and returns
|
||||
* an array of entry paths which match the given pattern. The URI may be
|
||||
* either a file: URL pointing directly to a zip file, or a jar: URI
|
||||
* pointing to a zip file nested within another zip file. Only one level of
|
||||
* nesting is supported.
|
||||
*
|
||||
* This should be used in preference to manually opening or retrieving a
|
||||
* ZipReader from the zip cache, since the former causes main thread IO and
|
||||
* the latter can lead to file locking issues due to unpredictable GC behavior
|
||||
* keeping the cached ZipReader alive after the cache is flushed.
|
||||
*
|
||||
* @param file The zip file to enumerate.
|
||||
* @param uri The URI of the zip file to enumerate.
|
||||
* @param pattern The pattern to match, as passed to nsIZipReader.findEntries.
|
||||
*/
|
||||
void enumerateZipFile(in nsIFile file, in AUTF8String pattern,
|
||||
[optional] out unsigned long count,
|
||||
[retval, array, size_is(count)] out wstring entries);
|
||||
Array<AString> enumerateJAR(in nsIURI uri, in AUTF8String pattern);
|
||||
|
||||
/**
|
||||
* Similar to |enumerateJAR| above, but accepts the URI of a directory
|
||||
* within a JAR file, and returns a list of all entries below it.
|
||||
*
|
||||
* The given URI must be a jar: URI, and its JAR file must point either to a
|
||||
* file: URI, or to a singly-nested JAR within another JAR file (i.e.,
|
||||
* "jar:file:///thing.jar!/" or "jar:jar:file:///thing.jar!/stuff.jar!/").
|
||||
* Multiple levels of nesting are not supported.
|
||||
*/
|
||||
Array<AString> enumerateJARSubtree(in nsIURI uri);
|
||||
|
||||
/**
|
||||
* Initializes the URL Preloader.
|
||||
|
|
|
@ -396,7 +396,7 @@ class AddonInternal {
|
|||
}
|
||||
|
||||
get hidden() {
|
||||
return this.location.hidden || (this._hidden && this.isPrivileged);
|
||||
return this.location.hidden || (this._hidden && this.isPrivileged) || false;
|
||||
}
|
||||
|
||||
set hidden(val) {
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
|
||||
[test_default_theme.html]
|
|
@ -0,0 +1,28 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for correct installation of default theme</title>
|
||||
<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script src="chrome://mochikit/content/tests/SimpleTest/ExtensionTestUtils.js"></script>
|
||||
<link rel="stylesheet" href="chrome://mochikit/contents/tests/SimpleTest/test.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<script>
|
||||
"use strict";
|
||||
|
||||
const {AddonManager} = ChromeUtils.import("resource://gre/modules/AddonManager.jsm");
|
||||
|
||||
add_task(async function() {
|
||||
let addon = await AddonManager.getAddonByID("default-theme@mozilla.org");
|
||||
|
||||
ok(addon != null, "Default theme exists");
|
||||
is(addon.type, "theme", "Add-on type is correct");
|
||||
is(addon.isActive, true, "Add-on is active");
|
||||
is(addon.hidden, false, "Add-on is not hidden");
|
||||
});
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -8,6 +8,7 @@ DIRS += ['browser']
|
|||
|
||||
BROWSER_CHROME_MANIFESTS += ['xpinstall/browser.ini']
|
||||
MOCHITEST_MANIFESTS += ['mochitest/mochitest.ini']
|
||||
MOCHITEST_CHROME_MANIFESTS += ['mochitest/chrome.ini']
|
||||
|
||||
XPCSHELL_TESTS_MANIFESTS += [
|
||||
'xpcshell/rs-blocklist/xpcshell.ini',
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
|
||||
#include "GfxInfoX11.h"
|
||||
|
||||
#include <gdk/gdkx.h>
|
||||
|
||||
#ifdef DEBUG
|
||||
bool fire_glxtest_process();
|
||||
#endif
|
||||
|
@ -40,6 +42,7 @@ nsresult GfxInfo::Init() {
|
|||
mHasTextureFromPixmap = false;
|
||||
mIsMesa = false;
|
||||
mIsAccelerated = true;
|
||||
mIsWayland = false;
|
||||
return GfxInfoBase::Init();
|
||||
}
|
||||
|
||||
|
@ -52,6 +55,8 @@ void GfxInfo::AddCrashReportAnnotations() {
|
|||
CrashReporter::Annotation::AdapterDriverVendor, mDriverVendor);
|
||||
CrashReporter::AnnotateCrashReport(
|
||||
CrashReporter::Annotation::AdapterDriverVersion, mDriverVersion);
|
||||
CrashReporter::AnnotateCrashReport(
|
||||
CrashReporter::Annotation::IsWayland, mIsWayland);
|
||||
}
|
||||
|
||||
void GfxInfo::GetData() {
|
||||
|
@ -286,6 +291,7 @@ void GfxInfo::GetData() {
|
|||
}
|
||||
|
||||
mAdapterDescription.Assign(glRenderer);
|
||||
mIsWayland = !GDK_IS_X11_DISPLAY(gdk_display_get_default());
|
||||
|
||||
AddCrashReportAnnotations();
|
||||
}
|
||||
|
@ -440,6 +446,17 @@ GfxInfo::GetCleartypeParameters(nsAString& aCleartypeParams) {
|
|||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
GfxInfo::GetWindowProtocol(nsAString& aWindowProtocol) {
|
||||
if (mIsWayland) {
|
||||
aWindowProtocol.AssignLiteral("wayland");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
aWindowProtocol.AssignLiteral("x11");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
GfxInfo::GetAdapterDescription(nsAString& aAdapterDescription) {
|
||||
GetData();
|
||||
|
|
|
@ -22,6 +22,7 @@ class GfxInfo final : public GfxInfoBase {
|
|||
NS_IMETHOD GetDWriteEnabled(bool* aDWriteEnabled) override;
|
||||
NS_IMETHOD GetDWriteVersion(nsAString& aDwriteVersion) override;
|
||||
NS_IMETHOD GetCleartypeParameters(nsAString& aCleartypeParams) override;
|
||||
NS_IMETHOD GetWindowProtocol(nsAString& aWindowProtocol) override;
|
||||
NS_IMETHOD GetAdapterDescription(nsAString& aAdapterDescription) override;
|
||||
NS_IMETHOD GetAdapterDriver(nsAString& aAdapterDriver) override;
|
||||
NS_IMETHOD GetAdapterVendorID(nsAString& aAdapterVendorID) override;
|
||||
|
@ -79,6 +80,7 @@ class GfxInfo final : public GfxInfoBase {
|
|||
unsigned int mGLMajorVersion, mGLMinorVersion;
|
||||
bool mIsMesa;
|
||||
bool mIsAccelerated;
|
||||
bool mIsWayland;
|
||||
|
||||
void AddCrashReportAnnotations();
|
||||
};
|
||||
|
|
|
@ -139,6 +139,11 @@ GfxInfo::GetCleartypeParameters(nsAString& aCleartypeParams) {
|
|||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
GfxInfo::GetWindowProtocol(nsAString& aWindowProtocol) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
void GfxInfo::EnsureInitialized() {
|
||||
if (mInitialized) return;
|
||||
|
||||
|
|
|
@ -31,6 +31,7 @@ class GfxInfo : public GfxInfoBase {
|
|||
NS_IMETHOD GetDWriteEnabled(bool* aDWriteEnabled) override;
|
||||
NS_IMETHOD GetDWriteVersion(nsAString& aDwriteVersion) override;
|
||||
NS_IMETHOD GetCleartypeParameters(nsAString& aCleartypeParams) override;
|
||||
NS_IMETHOD GetWindowProtocol(nsAString& aWindowProtocol) override;
|
||||
NS_IMETHOD GetAdapterDescription(nsAString& aAdapterDescription) override;
|
||||
NS_IMETHOD GetAdapterDriver(nsAString& aAdapterDriver) override;
|
||||
NS_IMETHOD GetAdapterVendorID(nsAString& aAdapterVendorID) override;
|
||||
|
|
|
@ -23,6 +23,7 @@ class GfxInfo : public GfxInfoBase {
|
|||
NS_IMETHOD GetD2DEnabled(bool* aD2DEnabled) override;
|
||||
NS_IMETHOD GetDWriteEnabled(bool* aDWriteEnabled) override;
|
||||
NS_IMETHOD GetDWriteVersion(nsAString& aDwriteVersion) override;
|
||||
NS_IMETHOD GetWindowProtocol(nsAString& aWindowProtocol) override;
|
||||
NS_IMETHOD GetCleartypeParameters(nsAString& aCleartypeParams) override;
|
||||
NS_IMETHOD GetAdapterDescription(nsAString& aAdapterDescription) override;
|
||||
NS_IMETHOD GetAdapterDriver(nsAString& aAdapterDriver) override;
|
||||
|
|
|
@ -118,6 +118,10 @@ GfxInfo::GetDWriteVersion(nsAString& aDwriteVersion) { return NS_ERROR_FAILURE;
|
|||
NS_IMETHODIMP
|
||||
GfxInfo::GetCleartypeParameters(nsAString& aCleartypeParams) { return NS_ERROR_FAILURE; }
|
||||
|
||||
/* readonly attribute DOMString windowProtocol; */
|
||||
NS_IMETHODIMP
|
||||
GfxInfo::GetWindowProtocol(nsAString& aWindowProtocol) { return NS_ERROR_FAILURE; }
|
||||
|
||||
/* readonly attribute DOMString adapterDescription; */
|
||||
NS_IMETHODIMP
|
||||
GfxInfo::GetAdapterDescription(nsAString& aAdapterDescription) {
|
||||
|
|
|
@ -250,10 +250,8 @@ EXPORTS.ipc = ['nsGUIEventIPC.h']
|
|||
|
||||
if CONFIG['MOZ_X11']:
|
||||
DIRS += ['x11']
|
||||
UNIFIED_SOURCES += [
|
||||
'GfxInfoX11.cpp'
|
||||
]
|
||||
SOURCES += [
|
||||
'GfxInfoX11.cpp',
|
||||
'nsShmImage.cpp',
|
||||
'WindowSurfaceX11SHM.cpp',
|
||||
]
|
||||
|
|
|
@ -19,6 +19,11 @@ interface nsIGfxInfo : nsISupports
|
|||
readonly attribute AString DWriteVersion;
|
||||
readonly attribute AString cleartypeParameters;
|
||||
|
||||
/*
|
||||
* These are non-Android linux-specific
|
||||
*/
|
||||
readonly attribute AString windowProtocol;
|
||||
|
||||
/*
|
||||
* These are valid across all platforms.
|
||||
*/
|
||||
|
|
|
@ -126,6 +126,11 @@ GfxInfo::GetCleartypeParameters(nsAString& aCleartypeParams) {
|
|||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
GfxInfo::GetWindowProtocol(nsAString& aWindowProtocol) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
static nsresult GetKeyValue(const WCHAR* keyLocation, const WCHAR* keyName,
|
||||
nsAString& destString, int type) {
|
||||
HKEY key;
|
||||
|
|
|
@ -25,6 +25,7 @@ class GfxInfo : public GfxInfoBase {
|
|||
NS_IMETHOD GetDWriteEnabled(bool* aDWriteEnabled) override;
|
||||
NS_IMETHOD GetDWriteVersion(nsAString& aDwriteVersion) override;
|
||||
NS_IMETHOD GetCleartypeParameters(nsAString& aCleartypeParams) override;
|
||||
NS_IMETHOD GetWindowProtocol(nsAString& aWindowProtocol) override;
|
||||
NS_IMETHOD GetAdapterDescription(nsAString& aAdapterDescription) override;
|
||||
NS_IMETHOD GetAdapterDriver(nsAString& aAdapterDriver) override;
|
||||
NS_IMETHOD GetAdapterVendorID(nsAString& aAdapterVendorID) override;
|
||||
|
|
Загрузка…
Ссылка в новой задаче