Merge inbound to mozilla-central. a=merge

This commit is contained in:
Oana Pop Rus 2019-05-29 00:48:04 +03:00
Родитель f5a9cfc509 f8bc88a3eb
Коммит bb39524dc6
54 изменённых файлов: 889 добавлений и 603 удалений

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

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