mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-13 21:35:39 +00:00
Merge inbound to m-c. a=merge
CLOSED TREE
This commit is contained in:
commit
6a8fa342ed
@ -361,14 +361,51 @@ nsImageLoadingContent::GetImageBlockingStatus(int16_t* aStatus)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
static void
|
||||
ReplayImageStatus(imgIRequest* aRequest, imgINotificationObserver* aObserver)
|
||||
{
|
||||
if (!aRequest) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t status = 0;
|
||||
nsresult rv = aRequest->GetImageStatus(&status);
|
||||
if (NS_FAILED(rv)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (status & imgIRequest::STATUS_SIZE_AVAILABLE) {
|
||||
aObserver->Notify(aRequest, imgINotificationObserver::SIZE_AVAILABLE, nullptr);
|
||||
}
|
||||
if (status & imgIRequest::STATUS_FRAME_COMPLETE) {
|
||||
aObserver->Notify(aRequest, imgINotificationObserver::FRAME_COMPLETE, nullptr);
|
||||
}
|
||||
if (status & imgIRequest::STATUS_HAS_TRANSPARENCY) {
|
||||
aObserver->Notify(aRequest, imgINotificationObserver::HAS_TRANSPARENCY, nullptr);
|
||||
}
|
||||
if (status & imgIRequest::STATUS_IS_ANIMATED) {
|
||||
aObserver->Notify(aRequest, imgINotificationObserver::IS_ANIMATED, nullptr);
|
||||
}
|
||||
if (status & imgIRequest::STATUS_DECODE_COMPLETE) {
|
||||
aObserver->Notify(aRequest, imgINotificationObserver::DECODE_COMPLETE, nullptr);
|
||||
}
|
||||
if (status & imgIRequest::STATUS_LOAD_COMPLETE) {
|
||||
aObserver->Notify(aRequest, imgINotificationObserver::LOAD_COMPLETE, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsImageLoadingContent::AddObserver(imgINotificationObserver* aObserver)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aObserver);
|
||||
|
||||
if (!mObserverList.mObserver) {
|
||||
mObserverList.mObserver = aObserver;
|
||||
// Don't touch the linking of the list!
|
||||
mObserverList.mObserver = aObserver;
|
||||
|
||||
ReplayImageStatus(mCurrentRequest, aObserver);
|
||||
ReplayImageStatus(mPendingRequest, aObserver);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -384,6 +421,9 @@ nsImageLoadingContent::AddObserver(imgINotificationObserver* aObserver)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
ReplayImageStatus(mCurrentRequest, aObserver);
|
||||
ReplayImageStatus(mPendingRequest, aObserver);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -107,6 +107,23 @@ AppendToString(std::stringstream& aStream, const nsIntRect& r,
|
||||
aStream << sfx;
|
||||
}
|
||||
|
||||
void
|
||||
AppendToString(std::stringstream& aStream, const nsRegion& r,
|
||||
const char* pfx, const char* sfx)
|
||||
{
|
||||
aStream << pfx;
|
||||
|
||||
nsRegionRectIterator it(r);
|
||||
aStream << "< ";
|
||||
while (const nsRect* sr = it.Next()) {
|
||||
AppendToString(aStream, *sr);
|
||||
aStream << "; ";
|
||||
}
|
||||
aStream << ">";
|
||||
|
||||
aStream << sfx;
|
||||
}
|
||||
|
||||
void
|
||||
AppendToString(std::stringstream& aStream, const nsIntRegion& r,
|
||||
const char* pfx, const char* sfx)
|
||||
|
@ -13,7 +13,7 @@
|
||||
#include "mozilla/layers/CompositorTypes.h" // for TextureFlags
|
||||
#include "nsAString.h"
|
||||
#include "nsPrintfCString.h" // for nsPrintfCString
|
||||
#include "nsRegion.h" // for nsIntRegion
|
||||
#include "nsRegion.h" // for nsRegion, nsIntRegion
|
||||
#include "nscore.h" // for nsACString, etc
|
||||
|
||||
struct gfxRGBA;
|
||||
@ -93,6 +93,10 @@ AppendToString(std::stringstream& aStream, const mozilla::gfx::IntRectTyped<T>&
|
||||
aStream << sfx;
|
||||
}
|
||||
|
||||
void
|
||||
AppendToString(std::stringstream& aStream, const nsRegion& r,
|
||||
const char* pfx="", const char* sfx="");
|
||||
|
||||
void
|
||||
AppendToString(std::stringstream& aStream, const nsIntRegion& r,
|
||||
const char* pfx="", const char* sfx="");
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include "AsyncPanZoomController.h"
|
||||
#include "gfxPrefs.h"
|
||||
#include "InputBlockState.h"
|
||||
#include "LayersLogging.h"
|
||||
#include "OverscrollHandoffState.h"
|
||||
|
||||
#define INPQ_LOG(...)
|
||||
|
@ -207,13 +207,11 @@ CompositableParentManager::ReceiveCompositableUpdate(const CompositableOperation
|
||||
MOZ_ASSERT(tex.get());
|
||||
compositable->UseTextureHost(tex);
|
||||
|
||||
if (IsAsync()) {
|
||||
if (IsAsync() && compositable->GetLayer()) {
|
||||
ScheduleComposition(op);
|
||||
// Async layer updates don't trigger invalidation, manually tell the layer
|
||||
// that its content have changed.
|
||||
if (compositable->GetLayer()) {
|
||||
compositable->GetLayer()->SetInvalidRectToVisibleRegion();
|
||||
}
|
||||
compositable->GetLayer()->SetInvalidRectToVisibleRegion();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -828,6 +828,18 @@ nsGIFDecoder2::WriteInternal(const char* aBuffer, uint32_t aCount,
|
||||
if (mGIFStruct.disposal_method == 4) {
|
||||
mGIFStruct.disposal_method = 3;
|
||||
}
|
||||
|
||||
{
|
||||
int32_t method =
|
||||
FrameBlender::FrameDisposalMethod(mGIFStruct.disposal_method);
|
||||
if (method == FrameBlender::kDisposeClearAll ||
|
||||
method == FrameBlender::kDisposeClear) {
|
||||
// We may have to display the background under this image during
|
||||
// animation playback, so we regard it as transparent.
|
||||
PostHasTransparency();
|
||||
}
|
||||
}
|
||||
|
||||
mGIFStruct.delay_time = GETINT16(q + 1) * 10;
|
||||
GETN(1, gif_consume_block);
|
||||
break;
|
||||
|
@ -59,6 +59,9 @@ nsIconDecoder::WriteInternal(const char* aBuffer, uint32_t aCount,
|
||||
|
||||
// Post our size to the superclass
|
||||
PostSize(mWidth, mHeight);
|
||||
|
||||
PostHasTransparency();
|
||||
|
||||
if (HasError()) {
|
||||
// Setting the size led to an error.
|
||||
mState = iconStateFinished;
|
||||
|
@ -155,9 +155,17 @@ void nsPNGDecoder::CreateFrame(png_uint_32 x_offset, png_uint_32 y_offset,
|
||||
// infrastructure. Just use it as long as it matches up.
|
||||
nsIntRect neededRect(x_offset, y_offset, width, height);
|
||||
nsRefPtr<imgFrame> currentFrame = GetCurrentFrame();
|
||||
if (mNumFrames != 0 || !currentFrame->GetRect().IsEqualEdges(neededRect)) {
|
||||
if (!currentFrame->GetRect().IsEqualEdges(neededRect)) {
|
||||
if (mNumFrames == 0) {
|
||||
// We need padding on the first frame, which means that we don't draw into
|
||||
// part of the image at all. Report that as transparency.
|
||||
PostHasTransparency();
|
||||
}
|
||||
|
||||
NeedNewFrame(mNumFrames, x_offset, y_offset, width, height, format);
|
||||
} else if (mNumFrames == 0) {
|
||||
} else if (mNumFrames != 0) {
|
||||
NeedNewFrame(mNumFrames, x_offset, y_offset, width, height, format);
|
||||
} else {
|
||||
// Our preallocated frame matches up, with the possible exception of alpha.
|
||||
if (format == gfx::SurfaceFormat::B8G8R8X8) {
|
||||
currentFrame->SetHasNoAlpha();
|
||||
@ -176,6 +184,12 @@ void nsPNGDecoder::CreateFrame(png_uint_32 x_offset, png_uint_32 y_offset,
|
||||
#ifdef PNG_APNG_SUPPORTED
|
||||
if (png_get_valid(mPNG, mInfo, PNG_INFO_acTL)) {
|
||||
mAnimInfo = AnimFrameInfo(mPNG, mInfo);
|
||||
|
||||
if (mAnimInfo.mDispose == FrameBlender::kDisposeClear) {
|
||||
// We may have to display the background under this image during
|
||||
// animation playback, so we regard it as transparent.
|
||||
PostHasTransparency();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
@ -69,7 +69,7 @@ native nsIntSizeByVal(nsIntSize);
|
||||
*
|
||||
* Internally, imgIContainer also manages animation of images.
|
||||
*/
|
||||
[scriptable, builtinclass, uuid(f8bb7671-5f36-490b-b828-3f4c6ad38665)]
|
||||
[scriptable, builtinclass, uuid(14ea6fa5-183e-4409-ac88-110bd2e05292)]
|
||||
interface imgIContainer : nsISupports
|
||||
{
|
||||
/**
|
||||
@ -215,12 +215,9 @@ interface imgIContainer : nsISupports
|
||||
in uint32_t aFlags);
|
||||
|
||||
/**
|
||||
* Whether the given frame is opaque; that is, needs the background painted
|
||||
* behind it.
|
||||
*
|
||||
* @param aWhichFrame Frame specifier of the FRAME_* variety.
|
||||
* Whether this image is opaque (i.e., needs a background painted behind it).
|
||||
*/
|
||||
[notxpcom] boolean frameIsOpaque(in uint32_t aWhichFrame);
|
||||
[notxpcom] boolean isOpaque();
|
||||
|
||||
/**
|
||||
* Attempts to create an ImageContainer (and Image) containing the current
|
||||
|
@ -19,7 +19,7 @@ interface nsIPrincipal;
|
||||
* @version 0.1
|
||||
* @see imagelib2
|
||||
*/
|
||||
[scriptable, builtinclass, uuid(dc61f0ea-4139-4c2a-ae69-cec82d33e089)]
|
||||
[scriptable, builtinclass, uuid(83a7708b-5c35-409f-bab3-7fc08be6a264)]
|
||||
interface imgIRequest : nsIRequest
|
||||
{
|
||||
/**
|
||||
@ -54,6 +54,10 @@ interface imgIRequest : nsIRequest
|
||||
* completely decoded.
|
||||
*
|
||||
* STATUS_DECODE_COMPLETE: The whole image has been decoded.
|
||||
*
|
||||
* STATUS_IS_ANIMATED: The image is animated.
|
||||
*
|
||||
* STATUS_HAS_TRANSPARENCY: The image is partially or completely transparent.
|
||||
*/
|
||||
//@{
|
||||
const long STATUS_NONE = 0x0;
|
||||
@ -63,6 +67,8 @@ interface imgIRequest : nsIRequest
|
||||
const long STATUS_DECODE_STARTED = 0x8;
|
||||
const long STATUS_FRAME_COMPLETE = 0x10;
|
||||
const long STATUS_DECODE_COMPLETE = 0x20;
|
||||
const long STATUS_IS_ANIMATED = 0x40;
|
||||
const long STATUS_HAS_TRANSPARENCY = 0x80;
|
||||
//@}
|
||||
|
||||
/**
|
||||
|
@ -215,6 +215,15 @@ protected:
|
||||
Orientation aOrientation = Orientation());
|
||||
|
||||
// Called by decoders if they determine that the image has transparency.
|
||||
//
|
||||
// This should be fired as early as possible to allow observers to do things
|
||||
// that affect content, so it's necessarily pessimistic - if there's a
|
||||
// possibility that the image has transparency, for example because its header
|
||||
// specifies that it has an alpha channel, we fire PostHasTransparency
|
||||
// immediately. PostFrameStop's aFrameAlpha argument, on the other hand, is
|
||||
// only used internally to ImageLib. Because PostFrameStop isn't delivered
|
||||
// until the entire frame has been decoded, decoders may take into account the
|
||||
// actual contents of the frame and give a more accurate result.
|
||||
void PostHasTransparency();
|
||||
|
||||
// Called by decoders when they begin a frame. Informs the image, sends
|
||||
|
@ -228,7 +228,7 @@ DynamicImage::GetFrame(uint32_t aWhichFrame,
|
||||
}
|
||||
|
||||
NS_IMETHODIMP_(bool)
|
||||
DynamicImage::FrameIsOpaque(uint32_t aWhichFrame)
|
||||
DynamicImage::IsOpaque()
|
||||
{
|
||||
// XXX(seth): For performance reasons it'd be better to return true here, but
|
||||
// I'm not sure how we can guarantee it for an arbitrary gfxDrawable.
|
||||
|
@ -50,12 +50,6 @@ FrozenImage::GetFrame(uint32_t aWhichFrame,
|
||||
return InnerImage()->GetFrame(FRAME_FIRST, aFlags);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP_(bool)
|
||||
FrozenImage::FrameIsOpaque(uint32_t aWhichFrame)
|
||||
{
|
||||
return InnerImage()->FrameIsOpaque(FRAME_FIRST);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
FrozenImage::GetImageContainer(layers::LayerManager* aManager,
|
||||
layers::ImageContainer** _retval)
|
||||
|
@ -38,7 +38,6 @@ public:
|
||||
NS_IMETHOD GetAnimated(bool* aAnimated) MOZ_OVERRIDE;
|
||||
NS_IMETHOD_(TemporaryRef<SourceSurface>)
|
||||
GetFrame(uint32_t aWhichFrame, uint32_t aFlags) MOZ_OVERRIDE;
|
||||
NS_IMETHOD_(bool) FrameIsOpaque(uint32_t aWhichFrame) MOZ_OVERRIDE;
|
||||
NS_IMETHOD GetImageContainer(layers::LayerManager* aManager,
|
||||
layers::ImageContainer** _retval) MOZ_OVERRIDE;
|
||||
NS_IMETHOD Draw(gfxContext* aContext,
|
||||
|
@ -209,9 +209,9 @@ ImageWrapper::GetFrame(uint32_t aWhichFrame,
|
||||
}
|
||||
|
||||
NS_IMETHODIMP_(bool)
|
||||
ImageWrapper::FrameIsOpaque(uint32_t aWhichFrame)
|
||||
ImageWrapper::IsOpaque()
|
||||
{
|
||||
return mInnerImage->FrameIsOpaque(aWhichFrame);
|
||||
return mInnerImage->IsOpaque();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
@ -112,7 +112,7 @@ OrientedImage::GetFrame(uint32_t aWhichFrame,
|
||||
|
||||
// Determine an appropriate format for the surface.
|
||||
gfx::SurfaceFormat surfaceFormat;
|
||||
if (InnerImage()->FrameIsOpaque(aWhichFrame)) {
|
||||
if (InnerImage()->IsOpaque()) {
|
||||
surfaceFormat = gfx::SurfaceFormat::B8G8R8X8;
|
||||
} else {
|
||||
surfaceFormat = gfx::SurfaceFormat::B8G8R8A8;
|
||||
|
@ -79,7 +79,6 @@ CheckProgressConsistency(Progress aProgress)
|
||||
MOZ_ASSERT(aProgress & FLAG_SIZE_AVAILABLE);
|
||||
}
|
||||
if (aProgress & FLAG_HAS_TRANSPARENCY) {
|
||||
MOZ_ASSERT(aProgress & FLAG_DECODE_STARTED);
|
||||
MOZ_ASSERT(aProgress & FLAG_SIZE_AVAILABLE);
|
||||
}
|
||||
if (aProgress & FLAG_IS_MULTIPART) {
|
||||
@ -153,6 +152,12 @@ ProgressTracker::GetImageStatus() const
|
||||
if (mProgress & FLAG_LOAD_COMPLETE) {
|
||||
status |= imgIRequest::STATUS_LOAD_COMPLETE;
|
||||
}
|
||||
if (mProgress & FLAG_IS_ANIMATED) {
|
||||
status |= imgIRequest::STATUS_IS_ANIMATED;
|
||||
}
|
||||
if (mProgress & FLAG_HAS_TRANSPARENCY) {
|
||||
status |= imgIRequest::STATUS_HAS_TRANSPARENCY;
|
||||
}
|
||||
if (mProgress & FLAG_HAS_ERROR) {
|
||||
status |= imgIRequest::STATUS_ERROR;
|
||||
}
|
||||
|
@ -34,8 +34,8 @@ enum {
|
||||
FLAG_LOAD_COMPLETE = 1u << 4, // STATUS_LOAD_COMPLETE
|
||||
FLAG_ONLOAD_BLOCKED = 1u << 5,
|
||||
FLAG_ONLOAD_UNBLOCKED = 1u << 6,
|
||||
FLAG_IS_ANIMATED = 1u << 7,
|
||||
FLAG_HAS_TRANSPARENCY = 1u << 8,
|
||||
FLAG_IS_ANIMATED = 1u << 7, // STATUS_IS_ANIMATED
|
||||
FLAG_HAS_TRANSPARENCY = 1u << 8, // STATUS_HAS_TRANSPARENCY
|
||||
FLAG_IS_MULTIPART = 1u << 9,
|
||||
FLAG_LAST_PART_COMPLETE = 1u << 10,
|
||||
FLAG_HAS_ERROR = 1u << 11 // STATUS_ERROR
|
||||
@ -101,6 +101,9 @@ public:
|
||||
// Get the current image status (as in imgIRequest).
|
||||
uint32_t GetImageStatus() const;
|
||||
|
||||
// Get the current Progress.
|
||||
Progress GetProgress() const { return mProgress; }
|
||||
|
||||
// Schedule an asynchronous "replaying" of all the notifications that would
|
||||
// have to happen to put us in the current state.
|
||||
// We will also take note of any notifications that happen between the time
|
||||
|
@ -654,33 +654,22 @@ RasterImage::GetFirstFrameRect()
|
||||
return nsIntRect(nsIntPoint(0,0), mSize);
|
||||
}
|
||||
|
||||
//******************************************************************************
|
||||
/* [notxpcom] boolean frameIsOpaque(in uint32_t aWhichFrame); */
|
||||
NS_IMETHODIMP_(bool)
|
||||
RasterImage::FrameIsOpaque(uint32_t aWhichFrame)
|
||||
RasterImage::IsOpaque()
|
||||
{
|
||||
if (aWhichFrame > FRAME_MAX_VALUE) {
|
||||
NS_WARNING("aWhichFrame outside valid range!");
|
||||
if (mError) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mError)
|
||||
Progress progress = mProgressTracker->GetProgress();
|
||||
|
||||
// If we haven't yet finished decoding, the safe answer is "not opaque".
|
||||
if (!(progress & FLAG_DECODE_COMPLETE)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// See if we can get an image frame.
|
||||
nsRefPtr<imgFrame> frame =
|
||||
LookupFrameNoDecode(GetRequestedFrameIndex(aWhichFrame));
|
||||
|
||||
// If we don't get a frame, the safe answer is "not opaque".
|
||||
if (!frame)
|
||||
return false;
|
||||
|
||||
// Other, the frame is transparent if either:
|
||||
// 1. It needs a background.
|
||||
// 2. Its size doesn't cover our entire area.
|
||||
nsIntRect framerect = frame->GetRect();
|
||||
return !frame->GetNeedsBackground() &&
|
||||
framerect.IsEqualInterior(nsIntRect(0, 0, mSize.width, mSize.height));
|
||||
// Other, we're opaque if FLAG_HAS_TRANSPARENCY is not set.
|
||||
return !(progress & FLAG_HAS_TRANSPARENCY);
|
||||
}
|
||||
|
||||
nsIntRect
|
||||
@ -767,7 +756,7 @@ RasterImage::CopyFrame(uint32_t aWhichFrame,
|
||||
if (mError)
|
||||
return nullptr;
|
||||
|
||||
if (!ApplyDecodeFlags(aFlags, aWhichFrame))
|
||||
if (!ApplyDecodeFlags(aFlags))
|
||||
return nullptr;
|
||||
|
||||
// Get the frame. If it's not there, it's probably the caller's fault for
|
||||
@ -844,7 +833,7 @@ RasterImage::GetFrameInternal(uint32_t aWhichFrame,
|
||||
if (mError)
|
||||
return nullptr;
|
||||
|
||||
if (!ApplyDecodeFlags(aFlags, aWhichFrame))
|
||||
if (!ApplyDecodeFlags(aFlags))
|
||||
return nullptr;
|
||||
|
||||
// Get the frame. If it's not there, it's probably the caller's fault for
|
||||
@ -1120,7 +1109,7 @@ RasterImage::InternalAddFrame(uint32_t framenum,
|
||||
}
|
||||
|
||||
bool
|
||||
RasterImage::ApplyDecodeFlags(uint32_t aNewFlags, uint32_t aWhichFrame)
|
||||
RasterImage::ApplyDecodeFlags(uint32_t aNewFlags)
|
||||
{
|
||||
if (mFrameDecodeFlags == (aNewFlags & DECODE_FLAGS_MASK))
|
||||
return true; // Not asking very much of us here.
|
||||
@ -1133,7 +1122,7 @@ RasterImage::ApplyDecodeFlags(uint32_t aNewFlags, uint32_t aWhichFrame)
|
||||
(mFrameDecodeFlags & DECODE_FLAGS_MASK) & ~FLAG_DECODE_NO_PREMULTIPLY_ALPHA;
|
||||
uint32_t newNonAlphaFlags =
|
||||
(aNewFlags & DECODE_FLAGS_MASK) & ~FLAG_DECODE_NO_PREMULTIPLY_ALPHA;
|
||||
if (currentNonAlphaFlags == newNonAlphaFlags && FrameIsOpaque(aWhichFrame)) {
|
||||
if (currentNonAlphaFlags == newNonAlphaFlags && IsOpaque()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -2537,8 +2526,7 @@ RasterImage::Draw(gfxContext* aContext,
|
||||
// is opaque.
|
||||
bool haveDefaultFlags = (mFrameDecodeFlags == DECODE_FLAGS_DEFAULT);
|
||||
bool haveSafeAlphaFlags =
|
||||
(mFrameDecodeFlags == FLAG_DECODE_NO_PREMULTIPLY_ALPHA) &&
|
||||
FrameIsOpaque(FRAME_CURRENT);
|
||||
(mFrameDecodeFlags == FLAG_DECODE_NO_PREMULTIPLY_ALPHA) && IsOpaque();
|
||||
|
||||
if (!(haveDefaultFlags || haveSafeAlphaFlags)) {
|
||||
if (!CanForciblyDiscardAndRedecode())
|
||||
|
@ -338,7 +338,7 @@ private:
|
||||
|
||||
nsresult DoImageDataComplete();
|
||||
|
||||
bool ApplyDecodeFlags(uint32_t aNewFlags, uint32_t aWhichFrame);
|
||||
bool ApplyDecodeFlags(uint32_t aNewFlags);
|
||||
|
||||
already_AddRefed<layers::Image> GetCurrentImage();
|
||||
void UpdateImageContainer();
|
||||
|
@ -687,15 +687,9 @@ VectorImage::GetFirstFrameDelay()
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
//******************************************************************************
|
||||
/* [notxpcom] boolean frameIsOpaque(in uint32_t aWhichFrame); */
|
||||
NS_IMETHODIMP_(bool)
|
||||
VectorImage::FrameIsOpaque(uint32_t aWhichFrame)
|
||||
VectorImage::IsOpaque()
|
||||
{
|
||||
if (aWhichFrame > FRAME_MAX_VALUE)
|
||||
NS_WARNING("aWhichFrame outside valid range!");
|
||||
|
||||
return false; // In general, SVG content is not opaque.
|
||||
}
|
||||
|
||||
|
@ -584,12 +584,6 @@ SurfaceFormat imgFrame::GetFormat() const
|
||||
return mFormat;
|
||||
}
|
||||
|
||||
bool imgFrame::GetNeedsBackground() const
|
||||
{
|
||||
// We need a background painted if we have alpha or we're incomplete.
|
||||
return (mFormat == SurfaceFormat::B8G8R8A8 || !ImageComplete());
|
||||
}
|
||||
|
||||
uint32_t imgFrame::GetImageBytesPerRow() const
|
||||
{
|
||||
if (mVBuf)
|
||||
|
@ -88,7 +88,6 @@ public:
|
||||
bool NeedsPadding() const { return mOffset != nsIntPoint(0, 0); }
|
||||
int32_t GetStride() const;
|
||||
SurfaceFormat GetFormat() const;
|
||||
bool GetNeedsBackground() const;
|
||||
uint32_t GetImageBytesPerRow() const;
|
||||
uint32_t GetImageDataLength() const;
|
||||
bool GetIsPaletted() const;
|
||||
|
@ -80,7 +80,7 @@ function AnimationTest(pollFreq, timeout, referenceElementId, imageElementId,
|
||||
{
|
||||
// We want to test the cold loading behavior, so clear cache in case an
|
||||
// earlier test got our image in there already.
|
||||
clearImageCache();
|
||||
clearAllImageCaches();
|
||||
|
||||
this.wereFailures = false;
|
||||
this.pollFreq = pollFreq;
|
||||
|
@ -4,6 +4,16 @@
|
||||
// Note that this is use by tests elsewhere in the source tree. When in doubt,
|
||||
// check mxr before removing or changing functionality.
|
||||
|
||||
// Helper function to clear both the content and chrome image caches
|
||||
function clearAllImageCaches()
|
||||
{
|
||||
var tools = SpecialPowers.Cc["@mozilla.org/image/tools;1"]
|
||||
.getService(SpecialPowers.Ci.imgITools);
|
||||
var imageCache = tools.getImgCacheForDocument(window.document);
|
||||
imageCache.clearCache(true); // true=chrome
|
||||
imageCache.clearCache(false); // false=content
|
||||
}
|
||||
|
||||
// Helper function to clear the image cache of content images
|
||||
function clearImageCache()
|
||||
{
|
||||
|
@ -32,7 +32,7 @@ const gImg = document.getElementsByTagName("img")[0];
|
||||
|
||||
var gMyDecoderObserver; // value will be set in main()
|
||||
var gReferenceSnapshot; // value will be set in takeReferenceSnapshot()
|
||||
var gOnStopFrameCounter = 0;
|
||||
var gPollCounter = 0;
|
||||
var gIsTestFinished = false;
|
||||
|
||||
|
||||
@ -54,29 +54,29 @@ function takeReferenceSnapshot() {
|
||||
"reference div should disappear when it becomes display:none");
|
||||
}
|
||||
|
||||
function myOnStopFrame() {
|
||||
gOnStopFrameCounter++;
|
||||
ok(true, "myOnStopFrame called");
|
||||
function myPoll() {
|
||||
gPollCounter++;
|
||||
ok(true, "myPoll called");
|
||||
let currentSnapshot = snapshotWindow(window, false);
|
||||
if (compareSnapshots(currentSnapshot, gReferenceSnapshot, true)[0]) {
|
||||
// SUCCESS!
|
||||
ok(true, "Animated image looks correct, " +
|
||||
"at call #" + gOnStopFrameCounter + " to onStopFrame");
|
||||
"at call #" + gPollCounter + " to myPoll");
|
||||
cleanUpAndFinish();
|
||||
}
|
||||
else
|
||||
setTimeout(myOnStopFrame, 1);
|
||||
setTimeout(myPoll, 1);
|
||||
}
|
||||
|
||||
function failTest() {
|
||||
ok(false, "timing out after " + FAILURE_TIMEOUT + "ms. " +
|
||||
"Animated image still doesn't look correct, " +
|
||||
"after call #" + gOnStopFrameCounter + " to onStopFrame");
|
||||
"after call #" + gPollCounter + " to myPoll");
|
||||
cleanUpAndFinish();
|
||||
}
|
||||
|
||||
function cleanUpAndFinish() {
|
||||
// On the off chance that failTest and myOnStopFrame are triggered
|
||||
// On the off chance that failTest and myPoll are triggered
|
||||
// back-to-back, use a flag to prevent multiple calls to SimpleTest.finish.
|
||||
if (gIsTestFinished) {
|
||||
return;
|
||||
@ -90,11 +90,11 @@ function main() {
|
||||
|
||||
// We want to test the cold loading behavior, so clear cache in case an
|
||||
// earlier test got our image in there already.
|
||||
clearImageCache();
|
||||
clearAllImageCaches();
|
||||
|
||||
setTimeout(myOnStopFrame, 1);
|
||||
setTimeout(myPoll, 1);
|
||||
|
||||
// kick off image-loading! myOnStopFrame handles the rest.
|
||||
// kick off image-loading! myPoll handles the rest.
|
||||
gImg.setAttribute("src", "lime-anim-100x100.svg");
|
||||
|
||||
// In case something goes wrong, fail earlier than mochitest timeout,
|
||||
|
@ -32,9 +32,8 @@ const gImg = document.getElementsByTagName("img")[0];
|
||||
|
||||
var gMyDecoderObserver; // value will be set in main()
|
||||
var gReferenceSnapshot; // value will be set in takeReferenceSnapshot()
|
||||
var gOnStopFrameCounter = 0;
|
||||
var gOnFrameUpdateCounter = 0;
|
||||
var gIsTestFinished = false;
|
||||
var gTimer = null;
|
||||
|
||||
|
||||
function takeReferenceSnapshot() {
|
||||
@ -55,30 +54,33 @@ function takeReferenceSnapshot() {
|
||||
"reference div should disappear when it becomes display:none");
|
||||
}
|
||||
|
||||
function myOnStopFrame(aRequest) {
|
||||
gOnStopFrameCounter++;
|
||||
ok(true, "myOnStopFrame called");
|
||||
function myOnFrameUpdate(aRequest) {
|
||||
if (gIsTestFinished) {
|
||||
return;
|
||||
}
|
||||
gOnFrameUpdateCounter++;
|
||||
ok(true, "myOnFrameUpdate called");
|
||||
let currentSnapshot = snapshotWindow(window, false);
|
||||
if (compareSnapshots(currentSnapshot, gReferenceSnapshot, true)[0]) {
|
||||
// SUCCESS!
|
||||
ok(true, "Animated image looks correct, " +
|
||||
"at call #" + gOnStopFrameCounter + " to onStopFrame");
|
||||
"at call #" + gOnFrameUpdateCounter + " to myOnFrameUpdate");
|
||||
cleanUpAndFinish();
|
||||
}
|
||||
if (!gTimer)
|
||||
gTimer = setTimeout(function() { gTimer = null; myOnStopFrame(0, 0); }, 1000);
|
||||
}
|
||||
|
||||
function failTest() {
|
||||
if (gIsTestFinished) {
|
||||
return;
|
||||
}
|
||||
ok(false, "timing out after " + FAILURE_TIMEOUT + "ms. " +
|
||||
"Animated image still doesn't look correct, " +
|
||||
"after call #" + gOnStopFrameCounter + " to onStopFrame");
|
||||
"after call #" + gOnFrameUpdateCounter + " to myOnFrameUpdate");
|
||||
cleanUpAndFinish();
|
||||
}
|
||||
|
||||
function cleanUpAndFinish() {
|
||||
clearTimeout(gTimer);
|
||||
// On the off chance that failTest and myOnStopFrame are triggered
|
||||
// On the off chance that failTest and myOnFrameUpdate are triggered
|
||||
// back-to-back, use a flag to prevent multiple calls to SimpleTest.finish.
|
||||
if (gIsTestFinished) {
|
||||
return;
|
||||
@ -94,7 +96,7 @@ function main() {
|
||||
|
||||
// Create, customize & attach decoder observer
|
||||
observer = new ImageDecoderObserverStub();
|
||||
observer.frameComplete = myOnStopFrame;
|
||||
observer.frameUpdate = myOnFrameUpdate;
|
||||
gMyDecoderObserver =
|
||||
Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools)
|
||||
.createScriptedObserver(observer);
|
||||
@ -103,9 +105,9 @@ function main() {
|
||||
|
||||
// We want to test the cold loading behavior, so clear cache in case an
|
||||
// earlier test got our image in there already.
|
||||
clearImageCache();
|
||||
clearAllImageCaches();
|
||||
|
||||
// kick off image-loading! myOnStopFrame handles the rest.
|
||||
// kick off image-loading! myOnFrameUpdate handles the rest.
|
||||
gImg.setAttribute("src", "lime-anim-100x100-2.svg");
|
||||
|
||||
// In case something goes wrong, fail earlier than mochitest timeout,
|
||||
|
@ -27,6 +27,8 @@ const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const gContent = document.getElementById("content");
|
||||
|
||||
var gCanvas;
|
||||
var gCanvasCtx;
|
||||
var gImg;
|
||||
var gMyDecoderObserver;
|
||||
var gIsTestFinished = false;
|
||||
@ -99,6 +101,11 @@ function onLoad() {
|
||||
return;
|
||||
}
|
||||
ok(true, "Should successfully load " + gImg.src);
|
||||
|
||||
// Force decoding of the image.
|
||||
SimpleTest.executeSoon(function() {
|
||||
gCanvasCtx.drawImage(gImg, 0, 0);
|
||||
});
|
||||
}
|
||||
|
||||
function failTest() {
|
||||
@ -119,6 +126,8 @@ function cleanUpAndFinish() {
|
||||
|
||||
function main() {
|
||||
gFiles = testFiles();
|
||||
gCanvas = document.createElement('canvas');
|
||||
gCanvasCtx = gCanvas.getContext('2d');
|
||||
gImg = new Image();
|
||||
gImg.onload = onLoad;
|
||||
gImg.onerror = onError;
|
||||
@ -135,7 +144,7 @@ function main() {
|
||||
|
||||
// We want to test the cold loading behavior, so clear cache in case an
|
||||
// earlier test got our image in there already.
|
||||
clearImageCache();
|
||||
clearAllImageCaches();
|
||||
|
||||
// Load the first image.
|
||||
loadNext();
|
||||
|
@ -109,7 +109,7 @@ function main() {
|
||||
|
||||
// We want to test the cold loading behavior, so clear cache in case an
|
||||
// earlier test got our image in there already.
|
||||
clearImageCache();
|
||||
clearAllImageCaches();
|
||||
|
||||
// kick off image-loading! myOnStopFrame handles the rest.
|
||||
gImg.setAttribute("src", gFiles.next());
|
||||
|
@ -109,7 +109,7 @@ function main() {
|
||||
|
||||
// We want to test the cold loading behavior, so clear cache in case an
|
||||
// earlier test got our image in there already.
|
||||
clearImageCache();
|
||||
clearAllImageCaches();
|
||||
|
||||
// kick off image-loading! myOnStopFrame handles the rest.
|
||||
gImg.setAttribute("src", gFiles.next());
|
||||
|
@ -106,7 +106,7 @@ function main() {
|
||||
|
||||
// We want to test the cold loading behavior, so clear cache in case an
|
||||
// earlier test got our image in there already.
|
||||
clearImageCache();
|
||||
clearAllImageCaches();
|
||||
|
||||
// These are two copies of the same image; hence, they have the same frame rate.
|
||||
gImg1.src = "animated1.gif";
|
||||
|
@ -36,7 +36,7 @@ Message::Message()
|
||||
#ifdef MOZ_TASK_TRACER
|
||||
header()->source_event_id = 0;
|
||||
header()->parent_task_id = 0;
|
||||
header()->source_event_type = SourceEventType::UNKNOWN;
|
||||
header()->source_event_type = SourceEventType::Unknown;
|
||||
#endif
|
||||
InitLoggingVariables();
|
||||
}
|
||||
@ -61,7 +61,7 @@ Message::Message(int32_t routing_id, msgid_t type, PriorityValue priority,
|
||||
#ifdef MOZ_TASK_TRACER
|
||||
header()->source_event_id = 0;
|
||||
header()->parent_task_id = 0;
|
||||
header()->source_event_type = SourceEventType::UNKNOWN;
|
||||
header()->source_event_type = SourceEventType::Unknown;
|
||||
#endif
|
||||
InitLoggingVariables(name);
|
||||
}
|
||||
|
@ -79,7 +79,7 @@ AsmJSFrameIterator::settle()
|
||||
fp_ = nullptr;
|
||||
MOZ_ASSERT(done());
|
||||
break;
|
||||
case AsmJSModule::CodeRange::IonFFI:
|
||||
case AsmJSModule::CodeRange::JitFFI:
|
||||
case AsmJSModule::CodeRange::SlowFFI:
|
||||
case AsmJSModule::CodeRange::Interrupt:
|
||||
case AsmJSModule::CodeRange::Inline:
|
||||
@ -458,7 +458,7 @@ AsmJSProfilingFrameIterator::initFromFP(const AsmJSActivation &activation)
|
||||
callerFP_ = CallerFPFromFP(fp);
|
||||
AssertMatchesCallSite(*module_, codeRange, callerPC_, callerFP_, fp);
|
||||
break;
|
||||
case AsmJSModule::CodeRange::IonFFI:
|
||||
case AsmJSModule::CodeRange::JitFFI:
|
||||
case AsmJSModule::CodeRange::SlowFFI:
|
||||
case AsmJSModule::CodeRange::Interrupt:
|
||||
case AsmJSModule::CodeRange::Inline:
|
||||
@ -513,7 +513,7 @@ AsmJSProfilingFrameIterator::AsmJSProfilingFrameIterator(const AsmJSActivation &
|
||||
const AsmJSModule::CodeRange *codeRange = module_->lookupCodeRange(state.pc);
|
||||
switch (codeRange->kind()) {
|
||||
case AsmJSModule::CodeRange::Function:
|
||||
case AsmJSModule::CodeRange::IonFFI:
|
||||
case AsmJSModule::CodeRange::JitFFI:
|
||||
case AsmJSModule::CodeRange::SlowFFI:
|
||||
case AsmJSModule::CodeRange::Interrupt:
|
||||
case AsmJSModule::CodeRange::Thunk: {
|
||||
@ -615,7 +615,7 @@ AsmJSProfilingFrameIterator::operator++()
|
||||
callerPC_ = nullptr;
|
||||
break;
|
||||
case AsmJSModule::CodeRange::Function:
|
||||
case AsmJSModule::CodeRange::IonFFI:
|
||||
case AsmJSModule::CodeRange::JitFFI:
|
||||
case AsmJSModule::CodeRange::SlowFFI:
|
||||
case AsmJSModule::CodeRange::Interrupt:
|
||||
case AsmJSModule::CodeRange::Inline:
|
||||
@ -672,15 +672,15 @@ AsmJSProfilingFrameIterator::label() const
|
||||
//
|
||||
// NB: these labels are regexp-matched by
|
||||
// browser/devtools/profiler/cleopatra/js/parserWorker.js.
|
||||
const char *ionFFIDescription = "fast FFI trampoline (in asm.js)";
|
||||
const char *jitFFIDescription = "fast FFI trampoline (in asm.js)";
|
||||
const char *slowFFIDescription = "slow FFI trampoline (in asm.js)";
|
||||
const char *interruptDescription = "interrupt due to out-of-bounds or long execution (in asm.js)";
|
||||
|
||||
switch (AsmJSExit::ExtractReasonKind(exitReason_)) {
|
||||
case AsmJSExit::Reason_None:
|
||||
break;
|
||||
case AsmJSExit::Reason_IonFFI:
|
||||
return ionFFIDescription;
|
||||
case AsmJSExit::Reason_JitFFI:
|
||||
return jitFFIDescription;
|
||||
case AsmJSExit::Reason_SlowFFI:
|
||||
return slowFFIDescription;
|
||||
case AsmJSExit::Reason_Interrupt:
|
||||
@ -693,7 +693,7 @@ AsmJSProfilingFrameIterator::label() const
|
||||
switch (codeRange->kind()) {
|
||||
case AsmJSModule::CodeRange::Function: return codeRange->functionProfilingLabel(*module_);
|
||||
case AsmJSModule::CodeRange::Entry: return "entry trampoline (in asm.js)";
|
||||
case AsmJSModule::CodeRange::IonFFI: return ionFFIDescription;
|
||||
case AsmJSModule::CodeRange::JitFFI: return jitFFIDescription;
|
||||
case AsmJSModule::CodeRange::SlowFFI: return slowFFIDescription;
|
||||
case AsmJSModule::CodeRange::Interrupt: return interruptDescription;
|
||||
case AsmJSModule::CodeRange::Inline: return "inline stub (in asm.js)";
|
||||
|
@ -69,7 +69,7 @@ namespace AsmJSExit
|
||||
// handler).
|
||||
enum ReasonKind {
|
||||
Reason_None,
|
||||
Reason_IonFFI,
|
||||
Reason_JitFFI,
|
||||
Reason_SlowFFI,
|
||||
Reason_Interrupt,
|
||||
Reason_Builtin
|
||||
@ -106,7 +106,7 @@ namespace AsmJSExit
|
||||
typedef uint32_t Reason;
|
||||
|
||||
static const uint32_t None = Reason_None;
|
||||
static const uint32_t IonFFI = Reason_IonFFI;
|
||||
static const uint32_t JitFFI = Reason_JitFFI;
|
||||
static const uint32_t SlowFFI = Reason_SlowFFI;
|
||||
static const uint32_t Interrupt = Reason_Interrupt;
|
||||
static inline Reason Builtin(BuiltinKind builtin) {
|
||||
|
@ -105,11 +105,11 @@ AsmJSModule::~AsmJSModule()
|
||||
if (code_) {
|
||||
for (unsigned i = 0; i < numExits(); i++) {
|
||||
AsmJSModule::ExitDatum &exitDatum = exitIndexToGlobalDatum(i);
|
||||
if (!exitDatum.ionScript)
|
||||
if (!exitDatum.baselineScript)
|
||||
continue;
|
||||
|
||||
jit::DependentAsmJSModuleExit exit(this, i);
|
||||
exitDatum.ionScript->removeDependentAsmJSModule(exit);
|
||||
exitDatum.baselineScript->removeDependentAsmJSModule(exit);
|
||||
}
|
||||
|
||||
DeallocateExecutableMemory(code_, pod.totalBytes_, AsmJSPageSize);
|
||||
@ -501,23 +501,31 @@ CoerceInPlace_ToNumber(MutableHandleValue val)
|
||||
}
|
||||
|
||||
static bool
|
||||
TryEnablingIon(JSContext *cx, AsmJSModule &module, HandleFunction fun, uint32_t exitIndex,
|
||||
TryEnablingJit(JSContext *cx, AsmJSModule &module, HandleFunction fun, uint32_t exitIndex,
|
||||
int32_t argc, Value *argv)
|
||||
{
|
||||
if (!fun->hasScript())
|
||||
return true;
|
||||
|
||||
// Test if the function is Ion compiled
|
||||
// Test if the function is JIT compiled.
|
||||
JSScript *script = fun->nonLazyScript();
|
||||
if (!script->hasIonScript())
|
||||
if (!script->hasBaselineScript()) {
|
||||
MOZ_ASSERT(!script->hasIonScript());
|
||||
return true;
|
||||
}
|
||||
|
||||
// Currently we can't rectify arguments. Therefore disabling if argc is too low.
|
||||
if (fun->nargs() > size_t(argc))
|
||||
return true;
|
||||
|
||||
// Normally the types should correspond, since we just ran with those types,
|
||||
// but there are reports this is asserting. Therefore doing it as a check, instead of DEBUG only.
|
||||
// Ensure the argument types are included in the argument TypeSets stored in
|
||||
// the TypeScript. This is necessary for Ion, because the FFI exit will
|
||||
// use the skip-arg-checks entry point.
|
||||
//
|
||||
// Note that the TypeScript is never discarded while the script has a
|
||||
// BaselineScript, so if those checks hold now they must hold at least until
|
||||
// the BaselineScript is discarded and when that happens the FFI exit is
|
||||
// patched back.
|
||||
if (!types::TypeScript::ThisTypes(script)->hasType(types::Type::UndefinedType()))
|
||||
return true;
|
||||
for (uint32_t i = 0; i < fun->nargs(); i++) {
|
||||
@ -533,11 +541,11 @@ TryEnablingIon(JSContext *cx, AsmJSModule &module, HandleFunction fun, uint32_t
|
||||
if (module.exitIsOptimized(exitIndex))
|
||||
return true;
|
||||
|
||||
IonScript *ionScript = script->ionScript();
|
||||
if (!ionScript->addDependentAsmJSModule(cx, DependentAsmJSModuleExit(&module, exitIndex)))
|
||||
BaselineScript *baselineScript = script->baselineScript();
|
||||
if (!baselineScript->addDependentAsmJSModule(cx, DependentAsmJSModuleExit(&module, exitIndex)))
|
||||
return false;
|
||||
|
||||
module.optimizeExit(exitIndex, ionScript);
|
||||
module.optimizeExit(exitIndex, baselineScript);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -553,7 +561,7 @@ InvokeFromAsmJS(AsmJSActivation *activation, int32_t exitIndex, int32_t argc, Va
|
||||
if (!Invoke(cx, UndefinedValue(), fval, argc, argv, rval))
|
||||
return false;
|
||||
|
||||
return TryEnablingIon(cx, module, fun, exitIndex, argc, argv);
|
||||
return TryEnablingJit(cx, module, fun, exitIndex, argc, argv);
|
||||
}
|
||||
|
||||
// Use an int32_t return type instead of bool since bool does not have a
|
||||
@ -748,7 +756,7 @@ AsmJSModule::staticallyLink(ExclusiveContext *cx)
|
||||
AsmJSModule::ExitDatum &exitDatum = exitIndexToGlobalDatum(i);
|
||||
exitDatum.exit = interpExitTrampoline(exits_[i]);
|
||||
exitDatum.fun = nullptr;
|
||||
exitDatum.ionScript = nullptr;
|
||||
exitDatum.baselineScript = nullptr;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(isStaticallyLinked());
|
||||
@ -904,7 +912,7 @@ AsmJSModule::detachHeap(JSContext *cx)
|
||||
// Even if this->active(), to reach here, the activation must have called
|
||||
// out via an FFI stub. FFI stubs check if heapDatum() is null on reentry
|
||||
// and throw an exception if so.
|
||||
MOZ_ASSERT_IF(active(), activation()->exitReason() == AsmJSExit::Reason_IonFFI ||
|
||||
MOZ_ASSERT_IF(active(), activation()->exitReason() == AsmJSExit::Reason_JitFFI ||
|
||||
activation()->exitReason() == AsmJSExit::Reason_SlowFFI);
|
||||
|
||||
restoreHeapToInitialState(maybeHeap_);
|
||||
@ -1338,7 +1346,7 @@ AsmJSModule::CodeRange::CodeRange(Kind kind, uint32_t begin, uint32_t profilingR
|
||||
|
||||
MOZ_ASSERT(begin_ < profilingReturn_);
|
||||
MOZ_ASSERT(profilingReturn_ < end_);
|
||||
MOZ_ASSERT(u.kind_ == IonFFI || u.kind_ == SlowFFI || u.kind_ == Interrupt);
|
||||
MOZ_ASSERT(u.kind_ == JitFFI || u.kind_ == SlowFFI || u.kind_ == Interrupt);
|
||||
}
|
||||
|
||||
AsmJSModule::CodeRange::CodeRange(AsmJSExit::BuiltinKind builtin, uint32_t begin,
|
||||
|
@ -371,7 +371,7 @@ class AsmJSModule
|
||||
unsigned ffiIndex_;
|
||||
unsigned globalDataOffset_;
|
||||
unsigned interpCodeOffset_;
|
||||
unsigned ionCodeOffset_;
|
||||
unsigned jitCodeOffset_;
|
||||
|
||||
friend class AsmJSModule;
|
||||
|
||||
@ -379,7 +379,7 @@ class AsmJSModule
|
||||
Exit() {}
|
||||
Exit(unsigned ffiIndex, unsigned globalDataOffset)
|
||||
: ffiIndex_(ffiIndex), globalDataOffset_(globalDataOffset),
|
||||
interpCodeOffset_(0), ionCodeOffset_(0)
|
||||
interpCodeOffset_(0), jitCodeOffset_(0)
|
||||
{}
|
||||
unsigned ffiIndex() const {
|
||||
return ffiIndex_;
|
||||
@ -391,13 +391,13 @@ class AsmJSModule
|
||||
MOZ_ASSERT(!interpCodeOffset_);
|
||||
interpCodeOffset_ = off;
|
||||
}
|
||||
void initIonOffset(unsigned off) {
|
||||
MOZ_ASSERT(!ionCodeOffset_);
|
||||
ionCodeOffset_ = off;
|
||||
void initJitOffset(unsigned off) {
|
||||
MOZ_ASSERT(!jitCodeOffset_);
|
||||
jitCodeOffset_ = off;
|
||||
}
|
||||
void updateOffsets(jit::MacroAssembler &masm) {
|
||||
interpCodeOffset_ = masm.actualOffset(interpCodeOffset_);
|
||||
ionCodeOffset_ = masm.actualOffset(ionCodeOffset_);
|
||||
jitCodeOffset_ = masm.actualOffset(jitCodeOffset_);
|
||||
}
|
||||
|
||||
size_t serializedSize() const;
|
||||
@ -419,7 +419,7 @@ class AsmJSModule
|
||||
struct ExitDatum
|
||||
{
|
||||
uint8_t *exit;
|
||||
jit::IonScript *ionScript;
|
||||
jit::BaselineScript *baselineScript;
|
||||
HeapPtrFunction fun;
|
||||
};
|
||||
|
||||
@ -558,7 +558,7 @@ class AsmJSModule
|
||||
void setDeltas(uint32_t entry, uint32_t profilingJump, uint32_t profilingEpilogue);
|
||||
|
||||
public:
|
||||
enum Kind { Function, Entry, IonFFI, SlowFFI, Interrupt, Thunk, Inline };
|
||||
enum Kind { Function, Entry, JitFFI, SlowFFI, Interrupt, Thunk, Inline };
|
||||
|
||||
CodeRange() {}
|
||||
CodeRange(uint32_t nameIndex, uint32_t lineNumber, const AsmJSFunctionLabels &l);
|
||||
@ -570,7 +570,7 @@ class AsmJSModule
|
||||
Kind kind() const { return Kind(u.kind_); }
|
||||
bool isFunction() const { return kind() == Function; }
|
||||
bool isEntry() const { return kind() == Entry; }
|
||||
bool isFFI() const { return kind() == IonFFI || kind() == SlowFFI; }
|
||||
bool isFFI() const { return kind() == JitFFI || kind() == SlowFFI; }
|
||||
bool isInterrupt() const { return kind() == Interrupt; }
|
||||
bool isThunk() const { return kind() == Thunk; }
|
||||
|
||||
@ -1325,10 +1325,10 @@ class AsmJSModule
|
||||
MOZ_ASSERT(exit.interpCodeOffset_);
|
||||
return code_ + exit.interpCodeOffset_;
|
||||
}
|
||||
uint8_t *ionExitTrampoline(const Exit &exit) const {
|
||||
uint8_t *jitExitTrampoline(const Exit &exit) const {
|
||||
MOZ_ASSERT(isFinished());
|
||||
MOZ_ASSERT(exit.ionCodeOffset_);
|
||||
return code_ + exit.ionCodeOffset_;
|
||||
MOZ_ASSERT(exit.jitCodeOffset_);
|
||||
return code_ + exit.jitCodeOffset_;
|
||||
}
|
||||
public:
|
||||
|
||||
@ -1463,17 +1463,17 @@ class AsmJSModule
|
||||
ExitDatum &exitDatum = exitIndexToGlobalDatum(exitIndex);
|
||||
return exitDatum.exit != interpExitTrampoline(exit(exitIndex));
|
||||
}
|
||||
void optimizeExit(unsigned exitIndex, jit::IonScript *ionScript) const {
|
||||
void optimizeExit(unsigned exitIndex, jit::BaselineScript *baselineScript) const {
|
||||
MOZ_ASSERT(!exitIsOptimized(exitIndex));
|
||||
ExitDatum &exitDatum = exitIndexToGlobalDatum(exitIndex);
|
||||
exitDatum.exit = ionExitTrampoline(exit(exitIndex));
|
||||
exitDatum.ionScript = ionScript;
|
||||
exitDatum.exit = jitExitTrampoline(exit(exitIndex));
|
||||
exitDatum.baselineScript = baselineScript;
|
||||
}
|
||||
void detachIonCompilation(size_t exitIndex) const {
|
||||
void detachJitCompilation(size_t exitIndex) const {
|
||||
MOZ_ASSERT(isFinished());
|
||||
ExitDatum &exitDatum = exitIndexToGlobalDatum(exitIndex);
|
||||
exitDatum.exit = interpExitTrampoline(exit(exitIndex));
|
||||
exitDatum.ionScript = nullptr;
|
||||
exitDatum.baselineScript = nullptr;
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
|
@ -1870,13 +1870,13 @@ class MOZ_STACK_CLASS ModuleCompiler
|
||||
uint32_t end = masm_.currentOffset();
|
||||
return module_->addCodeRange(AsmJSModule::CodeRange::SlowFFI, beg, pret, end);
|
||||
}
|
||||
bool finishGeneratingIonExit(unsigned exitIndex, Label *begin, Label *profilingReturn) {
|
||||
bool finishGeneratingJitExit(unsigned exitIndex, Label *begin, Label *profilingReturn) {
|
||||
MOZ_ASSERT(finishedFunctionBodies_);
|
||||
uint32_t beg = begin->offset();
|
||||
module_->exit(exitIndex).initIonOffset(beg);
|
||||
module_->exit(exitIndex).initJitOffset(beg);
|
||||
uint32_t pret = profilingReturn->offset();
|
||||
uint32_t end = masm_.currentOffset();
|
||||
return module_->addCodeRange(AsmJSModule::CodeRange::IonFFI, beg, pret, end);
|
||||
return module_->addCodeRange(AsmJSModule::CodeRange::JitFFI, beg, pret, end);
|
||||
}
|
||||
bool finishGeneratingInterrupt(Label *begin, Label *profilingReturn) {
|
||||
MOZ_ASSERT(finishedFunctionBodies_);
|
||||
@ -8453,7 +8453,7 @@ GenerateFFIIonExit(ModuleCompiler &m, const ModuleCompiler::ExitDescriptor &exit
|
||||
unsigned framePushed = Max(ionFrameSize, coerceFrameSize);
|
||||
|
||||
Label begin;
|
||||
GenerateAsmJSExitPrologue(masm, framePushed, AsmJSExit::IonFFI, &begin);
|
||||
GenerateAsmJSExitPrologue(masm, framePushed, AsmJSExit::JitFFI, &begin);
|
||||
|
||||
// 1. Descriptor
|
||||
size_t argOffset = offsetToIonArgs;
|
||||
@ -8501,7 +8501,7 @@ GenerateFFIIonExit(ModuleCompiler &m, const ModuleCompiler::ExitDescriptor &exit
|
||||
argOffset += exit.sig().args().length() * sizeof(Value);
|
||||
MOZ_ASSERT(argOffset == offsetToIonArgs + ionArgBytes);
|
||||
|
||||
// 6. Ion will clobber all registers, even non-volatiles. GlobalReg and
|
||||
// 6. Jit code will clobber all registers, even non-volatiles. GlobalReg and
|
||||
// HeapReg are removed from the general register set for asm.js code, so
|
||||
// these will not have been saved by the caller like all other registers,
|
||||
// so they must be explicitly preserved. Only save GlobalReg since
|
||||
@ -8553,7 +8553,7 @@ GenerateFFIIonExit(ModuleCompiler &m, const ModuleCompiler::ExitDescriptor &exit
|
||||
|
||||
// 2. Call
|
||||
AssertStackAlignment(masm, AsmJSStackAlignment);
|
||||
masm.callIonFromAsmJS(callee);
|
||||
masm.callJitFromAsmJS(callee);
|
||||
AssertStackAlignment(masm, AsmJSStackAlignment);
|
||||
|
||||
{
|
||||
@ -8625,7 +8625,7 @@ GenerateFFIIonExit(ModuleCompiler &m, const ModuleCompiler::ExitDescriptor &exit
|
||||
GenerateCheckForHeapDetachment(m, ABIArgGenerator::NonReturn_VolatileReg0);
|
||||
|
||||
Label profilingReturn;
|
||||
GenerateAsmJSExitEpilogue(masm, framePushed, AsmJSExit::IonFFI, &profilingReturn);
|
||||
GenerateAsmJSExitEpilogue(masm, framePushed, AsmJSExit::JitFFI, &profilingReturn);
|
||||
|
||||
if (oolConvert.used()) {
|
||||
masm.bind(&oolConvert);
|
||||
@ -8669,7 +8669,7 @@ GenerateFFIIonExit(ModuleCompiler &m, const ModuleCompiler::ExitDescriptor &exit
|
||||
|
||||
MOZ_ASSERT(masm.framePushed() == 0);
|
||||
|
||||
return m.finishGeneratingIonExit(exitIndex, &begin, &profilingReturn) && !masm.oom();
|
||||
return m.finishGeneratingJitExit(exitIndex, &begin, &profilingReturn) && !masm.oom();
|
||||
}
|
||||
|
||||
// See "asm.js FFI calls" comment above.
|
||||
|
@ -383,6 +383,14 @@ class LifoAlloc
|
||||
return n;
|
||||
}
|
||||
|
||||
// Get the total size of the arena chunks (including unused space).
|
||||
size_t computedSizeOfExcludingThis() const {
|
||||
size_t n = 0;
|
||||
for (BumpChunk *chunk = first; chunk; chunk = chunk->next())
|
||||
n += chunk->computedSizeOfIncludingThis();
|
||||
return n;
|
||||
}
|
||||
|
||||
// Like sizeOfExcludingThis(), but includes the size of the LifoAlloc itself.
|
||||
size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
|
||||
return mallocSizeOf(this) + sizeOfExcludingThis(mallocSizeOf);
|
||||
|
@ -147,7 +147,6 @@ struct MovingTracer : JSTracer {
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
} /* namespace gc */
|
||||
} /* namespace js */
|
||||
|
||||
|
@ -599,13 +599,14 @@ class GCRuntime
|
||||
void decommitAllWithoutUnlocking(const AutoLockGC &lock);
|
||||
void decommitArenas(AutoLockGC &lock);
|
||||
void expireChunksAndArenas(bool shouldShrink, AutoLockGC &lock);
|
||||
void sweepBackgroundThings();
|
||||
void queueZonesForBackgroundSweep(js::gc::ZoneList& zones);
|
||||
void sweepBackgroundThings(js::gc::ZoneList &zones, ThreadType threadType);
|
||||
void assertBackgroundSweepingFinished();
|
||||
bool shouldCompact();
|
||||
bool compactPhase(bool lastGC);
|
||||
#ifdef JSGC_COMPACTING
|
||||
void sweepTypesAfterCompacting(Zone *zone);
|
||||
void sweepZoneAfterCompacting(Zone *zone);
|
||||
void compactPhase(bool lastGC);
|
||||
ArenaHeader *relocateArenas();
|
||||
void updateAllCellPointersParallel(ArenasToUpdate &source);
|
||||
void updateAllCellPointersSerial(MovingTracer *trc, ArenasToUpdate &source);
|
||||
@ -758,9 +759,8 @@ class GCRuntime
|
||||
/* Whether any black->gray edges were found during marking. */
|
||||
bool foundBlackGrayEdges;
|
||||
|
||||
/* List head of zones to be swept in the background. */
|
||||
JS::Zone *sweepingZones;
|
||||
|
||||
/* Singly linekd list of zones to be swept in the background. */
|
||||
js::gc::ZoneList backgroundSweepZones;
|
||||
/*
|
||||
* Free LIFO blocks are transferred to this allocator before being freed on
|
||||
* the background GC thread.
|
||||
|
@ -19,6 +19,8 @@
|
||||
using namespace js;
|
||||
using namespace js::gc;
|
||||
|
||||
Zone * const Zone::NotOnList = reinterpret_cast<Zone *>(1);
|
||||
|
||||
JS::Zone::Zone(JSRuntime *rt)
|
||||
: JS::shadow::Zone(rt, &rt->gc.marker),
|
||||
allocator(this),
|
||||
@ -37,7 +39,8 @@ JS::Zone::Zone(JSRuntime *rt)
|
||||
gcState_(NoGC),
|
||||
gcScheduled_(false),
|
||||
gcPreserveCode_(false),
|
||||
jitUsingBarriers_(false)
|
||||
jitUsingBarriers_(false),
|
||||
listNext_(NotOnList)
|
||||
{
|
||||
/* Ensure that there are no vtables to mess us up here. */
|
||||
MOZ_ASSERT(reinterpret_cast<JS::shadow::Zone *>(this) ==
|
||||
@ -265,3 +268,95 @@ js::ZonesIter::atAtomsZone(JSRuntime *rt)
|
||||
{
|
||||
return rt->isAtomsZone(*it);
|
||||
}
|
||||
|
||||
bool Zone::isOnList()
|
||||
{
|
||||
return listNext_ != NotOnList;
|
||||
}
|
||||
|
||||
ZoneList::ZoneList()
|
||||
: head(nullptr), tail(nullptr)
|
||||
{}
|
||||
|
||||
ZoneList::ZoneList(Zone *zone)
|
||||
: head(zone), tail(zone)
|
||||
{
|
||||
MOZ_ASSERT(!zone->isOnList());
|
||||
zone->listNext_ = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
ZoneList::check() const
|
||||
{
|
||||
#ifdef DEBUG
|
||||
MOZ_ASSERT((head == nullptr) == (tail == nullptr));
|
||||
if (head) {
|
||||
Zone *zone = head;
|
||||
while (zone != tail) {
|
||||
zone = zone->listNext_;
|
||||
MOZ_ASSERT(zone);
|
||||
}
|
||||
MOZ_ASSERT(!zone->listNext_);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool ZoneList::isEmpty() const
|
||||
{
|
||||
return head == nullptr;
|
||||
}
|
||||
|
||||
Zone *
|
||||
ZoneList::front() const
|
||||
{
|
||||
MOZ_ASSERT(!isEmpty());
|
||||
return head;
|
||||
}
|
||||
|
||||
void
|
||||
ZoneList::append(Zone *zone)
|
||||
{
|
||||
ZoneList singleZone(zone);
|
||||
append(singleZone);
|
||||
}
|
||||
|
||||
void
|
||||
ZoneList::append(ZoneList &other)
|
||||
{
|
||||
check();
|
||||
other.check();
|
||||
MOZ_ASSERT(tail != other.tail);
|
||||
|
||||
if (tail)
|
||||
tail->listNext_ = other.head;
|
||||
else
|
||||
head = other.head;
|
||||
tail = other.tail;
|
||||
}
|
||||
|
||||
Zone *
|
||||
ZoneList::removeFront()
|
||||
{
|
||||
MOZ_ASSERT(!isEmpty());
|
||||
check();
|
||||
|
||||
Zone *front = head;
|
||||
head = head->listNext_;
|
||||
if (!head)
|
||||
tail = nullptr;
|
||||
|
||||
front->listNext_ = Zone::NotOnList;
|
||||
return front;
|
||||
}
|
||||
|
||||
void
|
||||
ZoneList::transferFrom(ZoneList& other)
|
||||
{
|
||||
MOZ_ASSERT(isEmpty());
|
||||
other.check();
|
||||
|
||||
head = other.head;
|
||||
tail = other.tail;
|
||||
other.head = nullptr;
|
||||
other.tail = nullptr;
|
||||
}
|
||||
|
@ -250,6 +250,10 @@ struct Zone : public JS::shadow::Zone,
|
||||
|
||||
js::jit::JitZone *createJitZone(JSContext *cx);
|
||||
|
||||
bool isQueuedForBackgroundSweep() {
|
||||
return isOnList();
|
||||
}
|
||||
|
||||
public:
|
||||
js::Allocator allocator;
|
||||
|
||||
@ -314,6 +318,12 @@ struct Zone : public JS::shadow::Zone,
|
||||
bool gcPreserveCode_;
|
||||
bool jitUsingBarriers_;
|
||||
|
||||
// Allow zones to be linked into a list
|
||||
friend class js::gc::ZoneList;
|
||||
static Zone * const NotOnList;
|
||||
Zone *listNext_;
|
||||
bool isOnList();
|
||||
|
||||
friend bool js::CurrentThreadCanAccessZone(Zone *zone);
|
||||
friend class js::gc::GCRuntime;
|
||||
};
|
||||
|
@ -39,33 +39,33 @@ enum MemoryBarrierBits {
|
||||
MembarAllbits = 31,
|
||||
};
|
||||
|
||||
inline MemoryBarrierBits
|
||||
static inline MOZ_CONSTEXPR MemoryBarrierBits
|
||||
operator|(MemoryBarrierBits a, MemoryBarrierBits b)
|
||||
{
|
||||
return MemoryBarrierBits(int(a) | int(b));
|
||||
}
|
||||
|
||||
inline MemoryBarrierBits
|
||||
static inline MOZ_CONSTEXPR MemoryBarrierBits
|
||||
operator&(MemoryBarrierBits a, MemoryBarrierBits b)
|
||||
{
|
||||
return MemoryBarrierBits(int(a) & int(b));
|
||||
}
|
||||
|
||||
inline MemoryBarrierBits
|
||||
static inline MOZ_CONSTEXPR MemoryBarrierBits
|
||||
operator~(MemoryBarrierBits a)
|
||||
{
|
||||
return MemoryBarrierBits(~int(a));
|
||||
}
|
||||
|
||||
// Standard barrier bits for a full barrier.
|
||||
static const MemoryBarrierBits MembarFull = MembarLoadLoad|MembarLoadStore|MembarStoreLoad|MembarStoreStore;
|
||||
static MOZ_CONSTEXPR_VAR MemoryBarrierBits MembarFull = MembarLoadLoad|MembarLoadStore|MembarStoreLoad|MembarStoreStore;
|
||||
|
||||
// Standard sets of barrier bits for atomic loads and stores.
|
||||
// See http://gee.cs.oswego.edu/dl/jmm/cookbook.html for more.
|
||||
static const MemoryBarrierBits MembarBeforeLoad = MembarNobits;
|
||||
static const MemoryBarrierBits MembarAfterLoad = MembarLoadLoad|MembarLoadStore;
|
||||
static const MemoryBarrierBits MembarBeforeStore = MembarStoreStore;
|
||||
static const MemoryBarrierBits MembarAfterStore = MembarStoreLoad;
|
||||
static MOZ_CONSTEXPR_VAR MemoryBarrierBits MembarBeforeLoad = MembarNobits;
|
||||
static MOZ_CONSTEXPR_VAR MemoryBarrierBits MembarAfterLoad = MembarLoadLoad|MembarLoadStore;
|
||||
static MOZ_CONSTEXPR_VAR MemoryBarrierBits MembarBeforeStore = MembarStoreStore;
|
||||
static MOZ_CONSTEXPR_VAR MemoryBarrierBits MembarAfterStore = MembarStoreLoad;
|
||||
|
||||
} // namespace jit
|
||||
} // namespace js
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
|
||||
#include "asmjs/AsmJSModule.h"
|
||||
#include "jit/BaselineCompiler.h"
|
||||
#include "jit/BaselineIC.h"
|
||||
#include "jit/CompileInfo.h"
|
||||
@ -45,6 +46,7 @@ BaselineScript::BaselineScript(uint32_t prologueOffset, uint32_t epilogueOffset,
|
||||
: method_(nullptr),
|
||||
templateScope_(nullptr),
|
||||
fallbackStubSpace_(),
|
||||
dependentAsmJSModules_(nullptr),
|
||||
prologueOffset_(prologueOffset),
|
||||
epilogueOffset_(epilogueOffset),
|
||||
#ifdef DEBUG
|
||||
@ -440,9 +442,54 @@ BaselineScript::Destroy(FreeOp *fop, BaselineScript *script)
|
||||
MOZ_ASSERT(fop->runtime()->gc.nursery.isEmpty());
|
||||
#endif
|
||||
|
||||
script->unlinkDependentAsmJSModules(fop);
|
||||
|
||||
fop->delete_(script);
|
||||
}
|
||||
|
||||
void
|
||||
BaselineScript::unlinkDependentAsmJSModules(FreeOp *fop)
|
||||
{
|
||||
// Remove any links from AsmJSModules that contain optimized FFI calls into
|
||||
// this BaselineScript.
|
||||
if (dependentAsmJSModules_) {
|
||||
for (size_t i = 0; i < dependentAsmJSModules_->length(); i++) {
|
||||
DependentAsmJSModuleExit exit = (*dependentAsmJSModules_)[i];
|
||||
exit.module->detachJitCompilation(exit.exitIndex);
|
||||
}
|
||||
|
||||
fop->delete_(dependentAsmJSModules_);
|
||||
dependentAsmJSModules_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
BaselineScript::addDependentAsmJSModule(JSContext *cx, DependentAsmJSModuleExit exit)
|
||||
{
|
||||
if (!dependentAsmJSModules_) {
|
||||
dependentAsmJSModules_ = cx->new_<Vector<DependentAsmJSModuleExit> >(cx);
|
||||
if (!dependentAsmJSModules_)
|
||||
return false;
|
||||
}
|
||||
return dependentAsmJSModules_->append(exit);
|
||||
}
|
||||
|
||||
void
|
||||
BaselineScript::removeDependentAsmJSModule(DependentAsmJSModuleExit exit)
|
||||
{
|
||||
if (!dependentAsmJSModules_)
|
||||
return;
|
||||
|
||||
for (size_t i = 0; i < dependentAsmJSModules_->length(); i++) {
|
||||
if ((*dependentAsmJSModules_)[i].module == exit.module &&
|
||||
(*dependentAsmJSModules_)[i].exitIndex == exit.exitIndex)
|
||||
{
|
||||
dependentAsmJSModules_->erase(dependentAsmJSModules_->begin() + i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ICEntry &
|
||||
BaselineScript::icEntry(size_t index)
|
||||
{
|
||||
|
@ -93,6 +93,19 @@ struct PCMappingIndexEntry
|
||||
uint32_t bufferOffset;
|
||||
};
|
||||
|
||||
// Describes a single AsmJSModule which jumps (via an FFI exit with the given
|
||||
// index) directly to a BaselineScript or IonScript.
|
||||
struct DependentAsmJSModuleExit
|
||||
{
|
||||
const AsmJSModule *module;
|
||||
size_t exitIndex;
|
||||
|
||||
DependentAsmJSModuleExit(const AsmJSModule *module, size_t exitIndex)
|
||||
: module(module),
|
||||
exitIndex(exitIndex)
|
||||
{ }
|
||||
};
|
||||
|
||||
struct BaselineScript
|
||||
{
|
||||
public:
|
||||
@ -114,6 +127,10 @@ struct BaselineScript
|
||||
// Allocated space for fallback stubs.
|
||||
FallbackICStubSpace fallbackStubSpace_;
|
||||
|
||||
// If non-null, the list of AsmJSModules that contain an optimized call
|
||||
// directly to this script.
|
||||
Vector<DependentAsmJSModuleExit> *dependentAsmJSModules_;
|
||||
|
||||
// Native code offset right before the scope chain is initialized.
|
||||
uint32_t prologueOffset_;
|
||||
|
||||
@ -347,6 +364,10 @@ struct BaselineScript
|
||||
jsbytecode *pcForNativeAddress(JSScript *script, uint8_t *nativeAddress);
|
||||
jsbytecode *pcForNativeOffset(JSScript *script, uint32_t nativeOffset);
|
||||
|
||||
bool addDependentAsmJSModule(JSContext *cx, DependentAsmJSModuleExit exit);
|
||||
void unlinkDependentAsmJSModules(FreeOp *fop);
|
||||
void removeDependentAsmJSModule(DependentAsmJSModuleExit exit);
|
||||
|
||||
private:
|
||||
jsbytecode *pcForNativeOffset(JSScript *script, uint32_t nativeOffset, bool isReturn);
|
||||
|
||||
|
@ -1217,8 +1217,8 @@ PrepareAndExecuteRegExp(JSContext *cx, MacroAssembler &masm, Register regexp, Re
|
||||
}
|
||||
|
||||
static void
|
||||
CopyStringChars(MacroAssembler &masm, Register to, Register from, Register len, Register scratch,
|
||||
size_t fromWidth, size_t toWidth);
|
||||
CopyStringChars(MacroAssembler &masm, Register to, Register from, Register len,
|
||||
Register byteOpScratch, size_t fromWidth, size_t toWidth);
|
||||
|
||||
static void
|
||||
CreateDependentString(MacroAssembler &masm, const JSAtomState &names,
|
||||
@ -5921,8 +5921,8 @@ CodeGenerator::visitConcatPar(LConcatPar *lir)
|
||||
}
|
||||
|
||||
static void
|
||||
CopyStringChars(MacroAssembler &masm, Register to, Register from, Register len, Register scratch,
|
||||
size_t fromWidth, size_t toWidth)
|
||||
CopyStringChars(MacroAssembler &masm, Register to, Register from, Register len,
|
||||
Register byteOpScratch, size_t fromWidth, size_t toWidth)
|
||||
{
|
||||
// Copy |len| char16_t code units from |from| to |to|. Assumes len > 0
|
||||
// (checked below in debug builds), and when done |to| must point to the
|
||||
@ -5942,13 +5942,13 @@ CopyStringChars(MacroAssembler &masm, Register to, Register from, Register len,
|
||||
Label start;
|
||||
masm.bind(&start);
|
||||
if (fromWidth == 2)
|
||||
masm.load16ZeroExtend(Address(from, 0), scratch);
|
||||
masm.load16ZeroExtend(Address(from, 0), byteOpScratch);
|
||||
else
|
||||
masm.load8ZeroExtend(Address(from, 0), scratch);
|
||||
masm.load8ZeroExtend(Address(from, 0), byteOpScratch);
|
||||
if (toWidth == 2)
|
||||
masm.store16(scratch, Address(to, 0));
|
||||
masm.store16(byteOpScratch, Address(to, 0));
|
||||
else
|
||||
masm.store8(scratch, Address(to, 0));
|
||||
masm.store8(byteOpScratch, Address(to, 0));
|
||||
masm.addPtr(Imm32(fromWidth), from);
|
||||
masm.addPtr(Imm32(toWidth), to);
|
||||
masm.branchSub32(Assembler::NonZero, Imm32(1), len, &start);
|
||||
@ -6065,8 +6065,12 @@ bool CodeGenerator::visitSubstr(LSubstr *lir)
|
||||
Register length = ToRegister(lir->length());
|
||||
Register output = ToRegister(lir->output());
|
||||
Register temp = ToRegister(lir->temp());
|
||||
Register temp2 = ToRegister(lir->temp2());
|
||||
Register temp3 = ToRegister(lir->temp3());
|
||||
|
||||
// On x86 there are not enough registers. In that case reuse the string
|
||||
// register as temporary.
|
||||
Register temp2 = lir->temp2()->isBogusTemp() ? string : ToRegister(lir->temp2());
|
||||
|
||||
Address stringFlags(string, JSString::offsetOfFlags());
|
||||
|
||||
Label isLatin1, notInline, nonZero, isInlinedLatin1;
|
||||
@ -6110,18 +6114,24 @@ bool CodeGenerator::visitSubstr(LSubstr *lir)
|
||||
masm.store32(Imm32(JSString::INIT_FAT_INLINE_FLAGS),
|
||||
Address(output, JSString::offsetOfFlags()));
|
||||
masm.computeEffectiveAddress(stringStorage, temp);
|
||||
if (temp2 == string)
|
||||
masm.push(string);
|
||||
BaseIndex chars(temp, begin, ScaleFromElemWidth(sizeof(char16_t)));
|
||||
masm.computeEffectiveAddress(chars, temp2);
|
||||
masm.computeEffectiveAddress(outputStorage, temp);
|
||||
CopyStringChars(masm, temp, temp2, length, temp3, sizeof(char16_t), sizeof(char16_t));
|
||||
masm.load32(Address(output, JSString::offsetOfLength()), length);
|
||||
masm.store16(Imm32(0), Address(temp, 0));
|
||||
if (temp2 == string)
|
||||
masm.pop(string);
|
||||
masm.jump(done);
|
||||
}
|
||||
masm.bind(&isInlinedLatin1);
|
||||
{
|
||||
masm.store32(Imm32(JSString::INIT_FAT_INLINE_FLAGS | JSString::LATIN1_CHARS_BIT),
|
||||
Address(output, JSString::offsetOfFlags()));
|
||||
if (temp2 == string)
|
||||
masm.push(string);
|
||||
masm.computeEffectiveAddress(stringStorage, temp2);
|
||||
static_assert(sizeof(char) == 1, "begin index shouldn't need scaling");
|
||||
masm.addPtr(begin, temp2);
|
||||
@ -6129,6 +6139,8 @@ bool CodeGenerator::visitSubstr(LSubstr *lir)
|
||||
CopyStringChars(masm, temp, temp2, length, temp3, sizeof(char), sizeof(char));
|
||||
masm.load32(Address(output, JSString::offsetOfLength()), length);
|
||||
masm.store8(Imm32(0), Address(temp, 0));
|
||||
if (temp2 == string)
|
||||
masm.pop(string);
|
||||
masm.jump(done);
|
||||
}
|
||||
|
||||
|
@ -12,7 +12,6 @@
|
||||
#include "jscompartment.h"
|
||||
#include "jsprf.h"
|
||||
|
||||
#include "asmjs/AsmJSModule.h"
|
||||
#include "gc/Marking.h"
|
||||
#include "jit/AliasAnalysis.h"
|
||||
#include "jit/BacktrackingAllocator.h"
|
||||
@ -844,7 +843,6 @@ IonScript::IonScript()
|
||||
parallelAge_(0),
|
||||
recompileInfo_(),
|
||||
osrPcMismatchCounter_(0),
|
||||
dependentAsmJSModules(nullptr),
|
||||
pendingBuilder_(nullptr)
|
||||
{
|
||||
}
|
||||
@ -1222,32 +1220,9 @@ IonScript::destroyCaches()
|
||||
getCacheFromIndex(i).destroy();
|
||||
}
|
||||
|
||||
bool
|
||||
IonScript::addDependentAsmJSModule(JSContext *cx, DependentAsmJSModuleExit exit)
|
||||
{
|
||||
if (!dependentAsmJSModules) {
|
||||
dependentAsmJSModules = cx->new_<Vector<DependentAsmJSModuleExit> >(cx);
|
||||
if (!dependentAsmJSModules)
|
||||
return false;
|
||||
}
|
||||
return dependentAsmJSModules->append(exit);
|
||||
}
|
||||
|
||||
void
|
||||
IonScript::unlinkFromRuntime(FreeOp *fop)
|
||||
{
|
||||
// Remove any links from AsmJSModules that contain optimized FFI calls into
|
||||
// this IonScript.
|
||||
if (dependentAsmJSModules) {
|
||||
for (size_t i = 0; i < dependentAsmJSModules->length(); i++) {
|
||||
DependentAsmJSModuleExit exit = dependentAsmJSModules->begin()[i];
|
||||
exit.module->detachIonCompilation(exit.exitIndex);
|
||||
}
|
||||
|
||||
fop->delete_(dependentAsmJSModules);
|
||||
dependentAsmJSModules = nullptr;
|
||||
}
|
||||
|
||||
// The writes to the executable buffer below may clobber backedge jumps, so
|
||||
// make sure that those backedges are unlinked from the runtime and not
|
||||
// reclobbered with garbage if an interrupt is requested.
|
||||
|
@ -160,19 +160,6 @@ class IonCache;
|
||||
struct PatchableBackedgeInfo;
|
||||
struct CacheLocation;
|
||||
|
||||
// Describes a single AsmJSModule which jumps (via an FFI exit with the given
|
||||
// index) directly into an IonScript.
|
||||
struct DependentAsmJSModuleExit
|
||||
{
|
||||
const AsmJSModule *module;
|
||||
size_t exitIndex;
|
||||
|
||||
DependentAsmJSModuleExit(const AsmJSModule *module, size_t exitIndex)
|
||||
: module(module),
|
||||
exitIndex(exitIndex)
|
||||
{ }
|
||||
};
|
||||
|
||||
// An IonScript attaches Ion-generated information to a JSScript.
|
||||
struct IonScript
|
||||
{
|
||||
@ -298,10 +285,6 @@ struct IonScript
|
||||
// a LOOPENTRY pc other than osrPc_.
|
||||
uint32_t osrPcMismatchCounter_;
|
||||
|
||||
// If non-null, the list of AsmJSModules
|
||||
// that contain an optimized call directly into this IonScript.
|
||||
Vector<DependentAsmJSModuleExit> *dependentAsmJSModules;
|
||||
|
||||
IonBuilder *pendingBuilder_;
|
||||
|
||||
private:
|
||||
@ -352,19 +335,6 @@ struct IonScript
|
||||
PatchableBackedge *backedgeList() {
|
||||
return (PatchableBackedge *) &bottomBuffer()[backedgeList_];
|
||||
}
|
||||
bool addDependentAsmJSModule(JSContext *cx, DependentAsmJSModuleExit exit);
|
||||
void removeDependentAsmJSModule(DependentAsmJSModuleExit exit) {
|
||||
if (!dependentAsmJSModules)
|
||||
return;
|
||||
for (size_t i = 0; i < dependentAsmJSModules->length(); i++) {
|
||||
if (dependentAsmJSModules->begin()[i].module == exit.module &&
|
||||
dependentAsmJSModules->begin()[i].exitIndex == exit.exitIndex)
|
||||
{
|
||||
dependentAsmJSModules->erase(dependentAsmJSModules->begin() + i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void trace(JSTracer *trc);
|
||||
|
@ -2181,21 +2181,6 @@ LIRGenerator::visitStringReplace(MStringReplace *ins)
|
||||
return defineReturn(lir, ins) && assignSafepoint(lir, ins);
|
||||
}
|
||||
|
||||
bool
|
||||
LIRGenerator::visitSubstr(MSubstr *ins)
|
||||
{
|
||||
// The last temporary need to be a register that can handle 8bit moves, but
|
||||
// there is no way to signal that to register allocator, except to give a
|
||||
// fixed temporary that is able to do this.
|
||||
LSubstr *lir = new (alloc()) LSubstr(useRegister(ins->string()),
|
||||
useRegister(ins->begin()),
|
||||
useRegister(ins->length()),
|
||||
temp(),
|
||||
temp(),
|
||||
tempFixed(CallTempReg1));
|
||||
return define(lir, ins) && assignSafepoint(lir, ins);
|
||||
}
|
||||
|
||||
bool
|
||||
LIRGenerator::visitLambda(MLambda *ins)
|
||||
{
|
||||
|
@ -146,7 +146,6 @@ class LIRGenerator : public LIRGeneratorSpecific
|
||||
bool visitCharCodeAt(MCharCodeAt *ins);
|
||||
bool visitFromCharCode(MFromCharCode *ins);
|
||||
bool visitStringSplit(MStringSplit *ins);
|
||||
bool visitSubstr(MSubstr *ins);
|
||||
bool visitStart(MStart *start);
|
||||
bool visitOsrEntry(MOsrEntry *entry);
|
||||
bool visitNop(MNop *nop);
|
||||
|
@ -55,6 +55,12 @@ LIRGeneratorARM::useByteOpRegisterOrNonDoubleConstant(MDefinition *mir)
|
||||
return useRegisterOrNonDoubleConstant(mir);
|
||||
}
|
||||
|
||||
LDefinition
|
||||
LIRGeneratorARM::tempByteOpRegister()
|
||||
{
|
||||
return temp();
|
||||
}
|
||||
|
||||
bool
|
||||
LIRGeneratorARM::lowerConstantDouble(double d, MInstruction *mir)
|
||||
{
|
||||
@ -674,3 +680,15 @@ LIRGeneratorARM::visitAsmJSAtomicBinopHeap(MAsmJSAtomicBinopHeap *ins)
|
||||
|
||||
return define(lir, ins);
|
||||
}
|
||||
|
||||
bool
|
||||
LIRGeneratorARM::visitSubstr(MSubstr *ins)
|
||||
{
|
||||
LSubstr *lir = new (alloc()) LSubstr(useRegister(ins->string()),
|
||||
useRegister(ins->begin()),
|
||||
useRegister(ins->length()),
|
||||
temp(),
|
||||
temp(),
|
||||
tempByteOpRegister());
|
||||
return define(lir, ins) && assignSafepoint(lir, ins);
|
||||
}
|
||||
|
@ -30,6 +30,7 @@ class LIRGeneratorARM : public LIRGeneratorShared
|
||||
// stores and loads; on ARM all registers are okay.
|
||||
LAllocation useByteOpRegister(MDefinition *mir);
|
||||
LAllocation useByteOpRegisterOrNonDoubleConstant(MDefinition *mir);
|
||||
LDefinition tempByteOpRegister();
|
||||
|
||||
inline LDefinition tempToUnbox() {
|
||||
return LDefinition::BogusTemp();
|
||||
@ -111,6 +112,7 @@ class LIRGeneratorARM : public LIRGeneratorShared
|
||||
bool visitSimdValueX4(MSimdValueX4 *ins);
|
||||
bool visitCompareExchangeTypedArrayElement(MCompareExchangeTypedArrayElement *ins);
|
||||
bool visitAtomicTypedArrayElementBinop(MAtomicTypedArrayElementBinop *ins);
|
||||
bool visitSubstr(MSubstr *ins);
|
||||
};
|
||||
|
||||
typedef LIRGeneratorARM LIRGeneratorSpecific;
|
||||
|
@ -1861,11 +1861,11 @@ MacroAssemblerARMCompat::callIon(Register callee)
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerARMCompat::callIonFromAsmJS(Register callee)
|
||||
MacroAssemblerARMCompat::callJitFromAsmJS(Register callee)
|
||||
{
|
||||
ma_callIonNoPush(callee);
|
||||
|
||||
// The Ion ABI has the callee pop the return address off the stack.
|
||||
// The JIT ABI has the callee pop the return address off the stack.
|
||||
// The asm.js caller assumes that the call leaves sp unchanged, so bump
|
||||
// the stack.
|
||||
subPtr(Imm32(sizeof(void*)), sp);
|
||||
|
@ -1293,7 +1293,7 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM
|
||||
// Makes an Ion call using the only two methods that it is sane for
|
||||
// independent code to make a call.
|
||||
void callIon(Register callee);
|
||||
void callIonFromAsmJS(Register callee);
|
||||
void callJitFromAsmJS(Register callee);
|
||||
|
||||
void reserveStack(uint32_t amount);
|
||||
void freeStack(uint32_t amount);
|
||||
|
@ -57,6 +57,12 @@ LIRGeneratorMIPS::useByteOpRegisterOrNonDoubleConstant(MDefinition *mir)
|
||||
return useRegisterOrNonDoubleConstant(mir);
|
||||
}
|
||||
|
||||
LDefinition
|
||||
LIRGeneratorMIPS::tempByteOpRegister()
|
||||
{
|
||||
return temp();
|
||||
}
|
||||
|
||||
bool
|
||||
LIRGeneratorMIPS::lowerConstantDouble(double d, MInstruction *mir)
|
||||
{
|
||||
@ -525,6 +531,18 @@ LIRGeneratorMIPS::lowerTruncateFToInt32(MTruncateToInt32 *ins)
|
||||
return define(new(alloc()) LTruncateFToInt32(useRegister(opd), LDefinition::BogusTemp()), ins);
|
||||
}
|
||||
|
||||
bool
|
||||
LIRGeneratorMIPS::visitSubstr(MSubstr *ins)
|
||||
{
|
||||
LSubstr *lir = new (alloc()) LSubstr(useRegister(ins->string()),
|
||||
useRegister(ins->begin()),
|
||||
useRegister(ins->length()),
|
||||
temp(),
|
||||
temp(),
|
||||
tempByteOpRegister());
|
||||
return define(lir, ins) && assignSafepoint(lir, ins);
|
||||
}
|
||||
|
||||
bool
|
||||
LIRGeneratorMIPS::visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic *ins)
|
||||
{
|
||||
|
@ -30,6 +30,7 @@ class LIRGeneratorMIPS : public LIRGeneratorShared
|
||||
// stores and loads; on MIPS all registers are okay.
|
||||
LAllocation useByteOpRegister(MDefinition *mir);
|
||||
LAllocation useByteOpRegisterOrNonDoubleConstant(MDefinition *mir);
|
||||
LDefinition tempByteOpRegister();
|
||||
|
||||
inline LDefinition tempToUnbox() {
|
||||
return LDefinition::BogusTemp();
|
||||
@ -109,6 +110,7 @@ class LIRGeneratorMIPS : public LIRGeneratorShared
|
||||
bool visitSimdValueX4(MSimdValueX4 *ins);
|
||||
bool visitCompareExchangeTypedArrayElement(MCompareExchangeTypedArrayElement *ins);
|
||||
bool visitAtomicTypedArrayElementBinop(MAtomicTypedArrayElementBinop *ins);
|
||||
bool visitSubstr(MSubstr *ins);
|
||||
};
|
||||
|
||||
typedef LIRGeneratorMIPS LIRGeneratorSpecific;
|
||||
|
@ -1549,11 +1549,11 @@ MacroAssemblerMIPSCompat::callIon(Register callee)
|
||||
}
|
||||
}
|
||||
void
|
||||
MacroAssemblerMIPSCompat::callIonFromAsmJS(Register callee)
|
||||
MacroAssemblerMIPSCompat::callJitFromAsmJS(Register callee)
|
||||
{
|
||||
ma_callIonNoPush(callee);
|
||||
|
||||
// The Ion ABI has the callee pop the return address off the stack.
|
||||
// The JIT ABI has the callee pop the return address off the stack.
|
||||
// The asm.js caller assumes that the call leaves sp unchanged, so bump
|
||||
// the stack.
|
||||
subPtr(Imm32(sizeof(void*)), StackPointer);
|
||||
|
@ -1140,7 +1140,7 @@ public:
|
||||
// Makes an Ion call using the only two methods that it is sane for
|
||||
// indep code to make a call
|
||||
void callIon(Register callee);
|
||||
void callIonFromAsmJS(Register callee);
|
||||
void callJitFromAsmJS(Register callee);
|
||||
|
||||
void reserveStack(uint32_t amount);
|
||||
void freeStack(uint32_t amount);
|
||||
|
@ -29,6 +29,7 @@ class LIRGeneratorNone : public LIRGeneratorShared
|
||||
|
||||
LAllocation useByteOpRegister(MDefinition *) { MOZ_CRASH(); }
|
||||
LAllocation useByteOpRegisterOrNonDoubleConstant(MDefinition *) { MOZ_CRASH(); }
|
||||
LDefinition tempByteOpRegister() { MOZ_CRASH(); }
|
||||
LDefinition tempToUnbox() { MOZ_CRASH(); }
|
||||
bool needTempForPostBarrier() { MOZ_CRASH(); }
|
||||
LDefinition tempForDispatchCache(MIRType v = MIRType_None) { MOZ_CRASH(); }
|
||||
|
@ -191,7 +191,7 @@ class MacroAssemblerNone : public Assembler
|
||||
void callWithExitFrame(JitCode *, Register) { MOZ_CRASH(); }
|
||||
|
||||
void callIon(Register callee) { MOZ_CRASH(); }
|
||||
void callIonFromAsmJS(Register callee) { MOZ_CRASH(); }
|
||||
void callJitFromAsmJS(Register callee) { MOZ_CRASH(); }
|
||||
|
||||
void nop() { MOZ_CRASH(); }
|
||||
void breakpoint() { MOZ_CRASH(); }
|
||||
|
@ -1205,7 +1205,7 @@ class MacroAssemblerX86Shared : public Assembler
|
||||
void callIon(Register callee) {
|
||||
call(callee);
|
||||
}
|
||||
void callIonFromAsmJS(Register callee) {
|
||||
void callJitFromAsmJS(Register callee) {
|
||||
call(callee);
|
||||
}
|
||||
void call(AsmJSImmPtr target) {
|
||||
|
@ -49,6 +49,12 @@ LIRGeneratorX64::useByteOpRegisterOrNonDoubleConstant(MDefinition *mir)
|
||||
return useRegisterOrNonDoubleConstant(mir);
|
||||
}
|
||||
|
||||
LDefinition
|
||||
LIRGeneratorX64::tempByteOpRegister()
|
||||
{
|
||||
return temp();
|
||||
}
|
||||
|
||||
LDefinition
|
||||
LIRGeneratorX64::tempToUnbox()
|
||||
{
|
||||
@ -190,6 +196,18 @@ LIRGeneratorX64::visitAsmJSLoadFuncPtr(MAsmJSLoadFuncPtr *ins)
|
||||
return define(new(alloc()) LAsmJSLoadFuncPtr(useRegister(ins->index()), temp()), ins);
|
||||
}
|
||||
|
||||
bool
|
||||
LIRGeneratorX64::visitSubstr(MSubstr *ins)
|
||||
{
|
||||
LSubstr *lir = new (alloc()) LSubstr(useRegister(ins->string()),
|
||||
useRegister(ins->begin()),
|
||||
useRegister(ins->length()),
|
||||
temp(),
|
||||
temp(),
|
||||
tempByteOpRegister());
|
||||
return define(lir, ins) && assignSafepoint(lir, ins);
|
||||
}
|
||||
|
||||
bool
|
||||
LIRGeneratorX64::visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic *ins)
|
||||
{
|
||||
|
@ -32,6 +32,7 @@ class LIRGeneratorX64 : public LIRGeneratorX86Shared
|
||||
// stores and loads; on x64 all registers are okay.
|
||||
LAllocation useByteOpRegister(MDefinition *mir);
|
||||
LAllocation useByteOpRegisterOrNonDoubleConstant(MDefinition *mir);
|
||||
LDefinition tempByteOpRegister();
|
||||
|
||||
LDefinition tempToUnbox();
|
||||
|
||||
@ -53,6 +54,7 @@ class LIRGeneratorX64 : public LIRGeneratorX86Shared
|
||||
bool visitAsmJSStoreHeap(MAsmJSStoreHeap *ins);
|
||||
bool visitAsmJSLoadFuncPtr(MAsmJSLoadFuncPtr *ins);
|
||||
bool visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic *ins);
|
||||
bool visitSubstr(MSubstr *ins);
|
||||
|
||||
static bool allowInlineForkJoinGetSlice() {
|
||||
return true;
|
||||
|
@ -76,6 +76,12 @@ LIRGeneratorX86::useByteOpRegisterOrNonDoubleConstant(MDefinition *mir)
|
||||
return useFixed(mir, eax);
|
||||
}
|
||||
|
||||
LDefinition
|
||||
LIRGeneratorX86::tempByteOpRegister()
|
||||
{
|
||||
return tempFixed(eax);
|
||||
}
|
||||
|
||||
bool
|
||||
LIRGeneratorX86::visitBox(MBox *box)
|
||||
{
|
||||
@ -309,3 +315,18 @@ LIRGeneratorX86::visitAsmJSLoadFuncPtr(MAsmJSLoadFuncPtr *ins)
|
||||
{
|
||||
return define(new(alloc()) LAsmJSLoadFuncPtr(useRegisterAtStart(ins->index())), ins);
|
||||
}
|
||||
|
||||
bool
|
||||
LIRGeneratorX86::visitSubstr(MSubstr *ins)
|
||||
{
|
||||
// Due to lack of registers on x86, we reuse the string register as
|
||||
// temporary. As a result we only need two temporary registers and take a
|
||||
// bugos temporary as fifth argument.
|
||||
LSubstr *lir = new (alloc()) LSubstr(useRegister(ins->string()),
|
||||
useRegister(ins->begin()),
|
||||
useRegister(ins->length()),
|
||||
temp(),
|
||||
LDefinition::BogusTemp(),
|
||||
tempByteOpRegister());
|
||||
return define(lir, ins) && assignSafepoint(lir, ins);
|
||||
}
|
||||
|
@ -33,6 +33,7 @@ class LIRGeneratorX86 : public LIRGeneratorX86Shared
|
||||
// give us one of {al,bl,cl,dl}. For now, just useFixed(al).
|
||||
LAllocation useByteOpRegister(MDefinition *mir);
|
||||
LAllocation useByteOpRegisterOrNonDoubleConstant(MDefinition *mir);
|
||||
LDefinition tempByteOpRegister();
|
||||
|
||||
inline LDefinition tempToUnbox() {
|
||||
return LDefinition::BogusTemp();
|
||||
@ -55,6 +56,7 @@ class LIRGeneratorX86 : public LIRGeneratorX86Shared
|
||||
bool visitAsmJSStoreHeap(MAsmJSStoreHeap *ins);
|
||||
bool visitAsmJSLoadFuncPtr(MAsmJSLoadFuncPtr *ins);
|
||||
bool visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic *ins);
|
||||
bool visitSubstr(MSubstr *ins);
|
||||
bool lowerPhi(MPhi *phi);
|
||||
|
||||
static bool allowTypedElementHoleCheck() {
|
||||
|
209
js/src/jsgc.cpp
209
js/src/jsgc.cpp
@ -560,7 +560,7 @@ FinalizeTypedArenas(FreeOp *fop,
|
||||
{
|
||||
// When operating in the foreground, take the lock at the top.
|
||||
Maybe<AutoLockGC> maybeLock;
|
||||
if (!fop->runtime()->gc.isBackgroundSweeping())
|
||||
if (!fop->onBackgroundThread())
|
||||
maybeLock.emplace(fop->runtime());
|
||||
|
||||
/*
|
||||
@ -580,9 +580,9 @@ FinalizeTypedArenas(FreeOp *fop,
|
||||
|
||||
if (nmarked) {
|
||||
dest.insertAt(aheader, nfree);
|
||||
} else if (keepArenas) {
|
||||
} else if (keepArenas == ArenaLists::KEEP_ARENAS) {
|
||||
aheader->chunk()->recycleArena(aheader, dest, thingKind, thingsPerArena);
|
||||
} else if (fop->runtime()->gc.isBackgroundSweeping()) {
|
||||
} else if (fop->onBackgroundThread()) {
|
||||
// When background sweeping, take the lock around each release so
|
||||
// that we do not block the foreground for extended periods.
|
||||
AutoLockGC lock(fop->runtime());
|
||||
@ -1159,7 +1159,6 @@ GCRuntime::GCRuntime(JSRuntime *rt) :
|
||||
lastMarkSlice(false),
|
||||
sweepOnBackgroundThread(false),
|
||||
foundBlackGrayEdges(false),
|
||||
sweepingZones(nullptr),
|
||||
freeLifoAlloc(JSRuntime::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
|
||||
zoneGroupIndex(0),
|
||||
zoneGroups(nullptr),
|
||||
@ -2869,7 +2868,6 @@ inline void
|
||||
ArenaLists::queueForBackgroundSweep(FreeOp *fop, AllocKind thingKind)
|
||||
{
|
||||
MOZ_ASSERT(IsBackgroundFinalized(thingKind));
|
||||
MOZ_ASSERT(!fop->runtime()->gc.isBackgroundSweeping());
|
||||
|
||||
ArenaList *al = &arenaLists[thingKind];
|
||||
if (al->isEmpty()) {
|
||||
@ -2885,10 +2883,11 @@ ArenaLists::queueForBackgroundSweep(FreeOp *fop, AllocKind thingKind)
|
||||
}
|
||||
|
||||
/*static*/ void
|
||||
ArenaLists::backgroundFinalize(FreeOp *fop, ArenaHeader *listHead)
|
||||
ArenaLists::backgroundFinalize(FreeOp *fop, ArenaHeader *listHead, ArenaHeader **empty)
|
||||
{
|
||||
MOZ_ASSERT(listHead);
|
||||
MOZ_ASSERT(!InParallelSection());
|
||||
MOZ_ASSERT(empty);
|
||||
|
||||
AllocKind thingKind = listHead->getAllocKind();
|
||||
Zone *zone = listHead->zone;
|
||||
@ -2897,9 +2896,11 @@ ArenaLists::backgroundFinalize(FreeOp *fop, ArenaHeader *listHead)
|
||||
SortedArenaList finalizedSorted(thingsPerArena);
|
||||
|
||||
SliceBudget budget;
|
||||
FinalizeArenas(fop, &listHead, finalizedSorted, thingKind, budget, RELEASE_ARENAS);
|
||||
FinalizeArenas(fop, &listHead, finalizedSorted, thingKind, budget, KEEP_ARENAS);
|
||||
MOZ_ASSERT(!listHead);
|
||||
|
||||
finalizedSorted.extractEmpty(empty);
|
||||
|
||||
// When arenas are queued for background finalization, all arenas are moved
|
||||
// to arenaListsToSweep[], leaving the arenaLists[] empty. However, new
|
||||
// arenas may be allocated before background finalization finishes; now that
|
||||
@ -3444,38 +3445,41 @@ GCRuntime::expireChunksAndArenas(bool shouldShrink, AutoLockGC &lock)
|
||||
}
|
||||
|
||||
void
|
||||
GCRuntime::sweepBackgroundThings()
|
||||
GCRuntime::sweepBackgroundThings(ZoneList &zones, ThreadType threadType)
|
||||
{
|
||||
/*
|
||||
* We must finalize in the correct order, see comments in
|
||||
* finalizeObjects.
|
||||
*/
|
||||
FreeOp fop(rt);
|
||||
for (unsigned phase = 0 ; phase < ArrayLength(BackgroundFinalizePhases) ; ++phase) {
|
||||
for (Zone *zone = sweepingZones; zone; zone = zone->gcNextGraphNode) {
|
||||
// We must finalize thing kinds in the order specified by BackgroundFinalizePhases.
|
||||
FreeOp fop(rt, threadType);
|
||||
while (!zones.isEmpty()) {
|
||||
Zone *zone = zones.front();
|
||||
ArenaHeader *emptyArenas = nullptr;
|
||||
for (unsigned phase = 0 ; phase < ArrayLength(BackgroundFinalizePhases) ; ++phase) {
|
||||
for (unsigned index = 0 ; index < BackgroundFinalizePhases[phase].length ; ++index) {
|
||||
AllocKind kind = BackgroundFinalizePhases[phase].kinds[index];
|
||||
ArenaHeader *arenas = zone->allocator.arenas.arenaListsToSweep[kind];
|
||||
if (arenas)
|
||||
ArenaLists::backgroundFinalize(&fop, arenas);
|
||||
ArenaLists::backgroundFinalize(&fop, arenas, &emptyArenas);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sweepingZones = nullptr;
|
||||
AutoLockGC lock(rt);
|
||||
ReleaseArenaList(rt, emptyArenas, lock);
|
||||
zones.removeFront();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GCRuntime::assertBackgroundSweepingFinished()
|
||||
{
|
||||
#ifdef DEBUG
|
||||
MOZ_ASSERT(!sweepingZones);
|
||||
MOZ_ASSERT(backgroundSweepZones.isEmpty());
|
||||
for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
|
||||
MOZ_ASSERT(!zone->isOnList());
|
||||
for (unsigned i = 0; i < FINALIZE_LIMIT; ++i) {
|
||||
MOZ_ASSERT(!zone->allocator.arenas.arenaListsToSweep[i]);
|
||||
MOZ_ASSERT(zone->allocator.arenas.doneBackgroundFinalize(AllocKind(i)));
|
||||
}
|
||||
}
|
||||
MOZ_ASSERT(freeLifoAlloc.computedSizeOfExcludingThis() == 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -3622,22 +3626,43 @@ BackgroundAllocTask::run()
|
||||
}
|
||||
|
||||
void
|
||||
GCHelperState::startBackgroundSweep()
|
||||
GCRuntime::queueZonesForBackgroundSweep(ZoneList &zones)
|
||||
{
|
||||
AutoLockHelperThreadState helperLock;
|
||||
AutoLockGC lock(rt);
|
||||
backgroundSweepZones.append(zones);
|
||||
helperState.maybeStartBackgroundSweep(lock);
|
||||
}
|
||||
|
||||
void
|
||||
GCRuntime::freeUnusedLifoBlocksAfterSweeping(LifoAlloc *lifo)
|
||||
{
|
||||
MOZ_ASSERT(isHeapBusy());
|
||||
AutoLockGC lock(rt);
|
||||
freeLifoAlloc.transferUnusedFrom(lifo);
|
||||
}
|
||||
|
||||
void
|
||||
GCRuntime::freeAllLifoBlocksAfterSweeping(LifoAlloc *lifo)
|
||||
{
|
||||
MOZ_ASSERT(isHeapBusy());
|
||||
AutoLockGC lock(rt);
|
||||
freeLifoAlloc.transferFrom(lifo);
|
||||
}
|
||||
|
||||
void
|
||||
GCHelperState::maybeStartBackgroundSweep(const AutoLockGC &lock)
|
||||
{
|
||||
MOZ_ASSERT(CanUseExtraThreads());
|
||||
|
||||
AutoLockHelperThreadState helperLock;
|
||||
AutoLockGC lock(rt);
|
||||
MOZ_ASSERT(state() == IDLE);
|
||||
MOZ_ASSERT(!sweepFlag);
|
||||
sweepFlag = true;
|
||||
shrinkFlag = false;
|
||||
startBackgroundThread(SWEEPING);
|
||||
if (state() == IDLE)
|
||||
startBackgroundThread(SWEEPING);
|
||||
}
|
||||
|
||||
/* Must be called with the GC lock taken. */
|
||||
void
|
||||
GCHelperState::startBackgroundShrink()
|
||||
GCHelperState::startBackgroundShrink(const AutoLockGC &lock)
|
||||
{
|
||||
MOZ_ASSERT(CanUseExtraThreads());
|
||||
switch (state()) {
|
||||
@ -3667,13 +3692,16 @@ GCHelperState::waitBackgroundSweepEnd()
|
||||
void
|
||||
GCHelperState::doSweep(AutoLockGC &lock)
|
||||
{
|
||||
if (sweepFlag) {
|
||||
while (sweepFlag) {
|
||||
sweepFlag = false;
|
||||
ZoneList zones;
|
||||
zones.transferFrom(rt->gc.backgroundSweepZones);
|
||||
LifoAlloc freeLifoAlloc(JSRuntime::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE);
|
||||
freeLifoAlloc.transferFrom(&rt->gc.freeLifoAlloc);
|
||||
AutoUnlockGC unlock(lock);
|
||||
|
||||
rt->gc.sweepBackgroundThings();
|
||||
|
||||
rt->gc.freeLifoAlloc.freeAll();
|
||||
rt->gc.sweepBackgroundThings(zones, BackgroundThread);
|
||||
freeLifoAlloc.freeAll();
|
||||
}
|
||||
|
||||
bool shrinking = shrinkFlag;
|
||||
@ -3762,6 +3790,8 @@ Zone::sweepCompartments(FreeOp *fop, bool keepAtleastOne, bool lastGC)
|
||||
void
|
||||
GCRuntime::sweepZones(FreeOp *fop, bool lastGC)
|
||||
{
|
||||
AutoLockGC lock(rt); // Avoid race with background sweeping.
|
||||
|
||||
JSZoneCallback callback = rt->destroyZoneCallback;
|
||||
|
||||
/* Skip the atomsCompartment zone. */
|
||||
@ -3775,10 +3805,13 @@ GCRuntime::sweepZones(FreeOp *fop, bool lastGC)
|
||||
Zone *zone = *read++;
|
||||
|
||||
if (zone->wasGCStarted()) {
|
||||
if ((zone->allocator.arenas.arenaListsAreEmpty() && !zone->hasMarkedCompartments()) ||
|
||||
lastGC)
|
||||
if ((!zone->isQueuedForBackgroundSweep() &&
|
||||
zone->allocator.arenas.arenaListsAreEmpty() &&
|
||||
!zone->hasMarkedCompartments()) || lastGC)
|
||||
{
|
||||
zone->allocator.arenas.checkEmptyFreeLists();
|
||||
AutoUnlockGC unlock(lock);
|
||||
|
||||
if (callback)
|
||||
callback(zone);
|
||||
zone->sweepCompartments(fop, false, lastGC);
|
||||
@ -3793,27 +3826,12 @@ GCRuntime::sweepZones(FreeOp *fop, bool lastGC)
|
||||
zones.resize(write - zones.begin());
|
||||
}
|
||||
|
||||
void
|
||||
GCRuntime::freeUnusedLifoBlocksAfterSweeping(LifoAlloc *lifo)
|
||||
{
|
||||
MOZ_ASSERT(isHeapBusy());
|
||||
freeLifoAlloc.transferUnusedFrom(lifo);
|
||||
}
|
||||
|
||||
void
|
||||
GCRuntime::freeAllLifoBlocksAfterSweeping(LifoAlloc *lifo)
|
||||
{
|
||||
MOZ_ASSERT(isHeapBusy());
|
||||
freeLifoAlloc.transferFrom(lifo);
|
||||
}
|
||||
|
||||
void
|
||||
GCRuntime::purgeRuntime()
|
||||
{
|
||||
for (GCCompartmentsIter comp(rt); !comp.done(); comp.next())
|
||||
comp->purge();
|
||||
|
||||
|
||||
freeUnusedLifoBlocksAfterSweeping(&rt->tempLifoAlloc);
|
||||
|
||||
rt->interpreterStack().purge(rt);
|
||||
@ -4264,6 +4282,8 @@ js::gc::MarkingValidator::nonIncrementalMark()
|
||||
JSRuntime *runtime = gc->rt;
|
||||
GCMarker *gcmarker = &gc->marker;
|
||||
|
||||
gc->waitBackgroundSweepEnd();
|
||||
|
||||
/* Save existing mark bits. */
|
||||
for (auto chunk = gc->allNonEmptyChunks(); !chunk.done(); chunk.next()) {
|
||||
ChunkBitmap *bitmap = &chunk->bitmap;
|
||||
@ -4372,6 +4392,8 @@ js::gc::MarkingValidator::validate()
|
||||
if (!initialized)
|
||||
return;
|
||||
|
||||
gc->waitBackgroundSweepEnd();
|
||||
|
||||
for (auto chunk = gc->allNonEmptyChunks(); !chunk.done(); chunk.next()) {
|
||||
BitmapMap::Ptr ptr = map.lookup(chunk);
|
||||
if (!ptr)
|
||||
@ -5169,12 +5191,20 @@ GCRuntime::beginSweepingZoneGroup()
|
||||
void
|
||||
GCRuntime::endSweepingZoneGroup()
|
||||
{
|
||||
/* Update the GC state for zones we have swept and unlink the list. */
|
||||
/* Update the GC state for zones we have swept. */
|
||||
for (GCZoneGroupIter zone(rt); !zone.done(); zone.next()) {
|
||||
MOZ_ASSERT(zone->isGCSweeping());
|
||||
zone->setGCState(Zone::Finished);
|
||||
}
|
||||
|
||||
/* Start background thread to sweep zones if required. */
|
||||
if (sweepOnBackgroundThread) {
|
||||
ZoneList zones;
|
||||
for (GCZoneGroupIter zone(rt); !zone.done(); zone.next())
|
||||
zones.append(zone);
|
||||
queueZonesForBackgroundSweep(zones);
|
||||
}
|
||||
|
||||
/* Reset the list of arenas marked as being allocated during sweep phase. */
|
||||
while (ArenaHeader *arena = arenasAllocatedDuringSweep) {
|
||||
arenasAllocatedDuringSweep = arena->getNextAllocDuringSweep();
|
||||
@ -5206,7 +5236,7 @@ GCRuntime::beginSweepPhase(bool lastGC)
|
||||
gcstats::AutoPhase ap(stats, gcstats::PHASE_SWEEP);
|
||||
|
||||
sweepOnBackgroundThread =
|
||||
!lastGC && !TraceEnabled() && CanUseExtraThreads() && !shouldCompact();
|
||||
!lastGC && !TraceEnabled() && CanUseExtraThreads();
|
||||
|
||||
releaseObservedTypes = shouldReleaseObservedTypes();
|
||||
|
||||
@ -5483,18 +5513,14 @@ GCRuntime::endSweepPhase(bool lastGC)
|
||||
grayBitsValid = true;
|
||||
}
|
||||
|
||||
/* Set up list of zones for sweeping of background things. */
|
||||
MOZ_ASSERT(!sweepingZones);
|
||||
for (GCZonesIter zone(rt); !zone.done(); zone.next()) {
|
||||
zone->gcNextGraphNode = sweepingZones;
|
||||
sweepingZones = zone;
|
||||
}
|
||||
|
||||
/* If not sweeping on background thread then we must do it here. */
|
||||
if (!sweepOnBackgroundThread) {
|
||||
gcstats::AutoPhase ap(stats, gcstats::PHASE_DESTROY);
|
||||
|
||||
sweepBackgroundThings();
|
||||
ZoneList zones;
|
||||
for (GCZonesIter zone(rt); !zone.done(); zone.next())
|
||||
zones.append(zone);
|
||||
sweepBackgroundThings(zones, MainThread);
|
||||
|
||||
/*
|
||||
* Destroy arenas after we finished the sweeping so finalizers can
|
||||
@ -5534,20 +5560,28 @@ GCRuntime::endSweepPhase(bool lastGC)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (sweepOnBackgroundThread)
|
||||
helperState.startBackgroundSweep();
|
||||
}
|
||||
|
||||
#ifdef JSGC_COMPACTING
|
||||
void
|
||||
bool
|
||||
GCRuntime::compactPhase(bool lastGC)
|
||||
{
|
||||
MOZ_ASSERT(rt->gc.nursery.isEmpty());
|
||||
MOZ_ASSERT(!sweepOnBackgroundThread);
|
||||
|
||||
#ifndef JSGC_COMPACTING
|
||||
MOZ_CRASH();
|
||||
#else
|
||||
gcstats::AutoPhase ap(stats, gcstats::PHASE_COMPACT);
|
||||
|
||||
if (isIncremental) {
|
||||
// Poll for end of background sweeping
|
||||
AutoLockGC lock(rt);
|
||||
if (isBackgroundSweeping())
|
||||
return false;
|
||||
} else {
|
||||
waitBackgroundSweepEnd();
|
||||
}
|
||||
|
||||
MOZ_ASSERT(rt->gc.nursery.isEmpty());
|
||||
assertBackgroundSweepingFinished();
|
||||
|
||||
ArenaHeader *relocatedList = relocateArenas();
|
||||
updatePointersToRelocatedCells();
|
||||
|
||||
@ -5583,8 +5617,10 @@ GCRuntime::compactPhase(bool lastGC)
|
||||
zone->allocator.arenas.checkEmptyFreeLists();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // JSGC_COMPACTING
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
GCRuntime::finishCollection()
|
||||
@ -5710,6 +5746,8 @@ GCRuntime::resetIncrementalGC(const char *reason)
|
||||
rt->setNeedsIncrementalBarrier(false);
|
||||
AssertNeedsBarrierFlagsConsistent(rt);
|
||||
|
||||
freeLifoAlloc.freeAll();
|
||||
|
||||
incrementalState = NO_INCREMENTAL;
|
||||
|
||||
MOZ_ASSERT(!marker.shouldCheckCompartments());
|
||||
@ -5735,6 +5773,11 @@ GCRuntime::resetIncrementalGC(const char *reason)
|
||||
break;
|
||||
}
|
||||
|
||||
#ifdef JSGC_COMPACTING
|
||||
case COMPACT:
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
MOZ_CRASH("Invalid incremental GC state");
|
||||
}
|
||||
@ -5927,24 +5970,31 @@ GCRuntime::incrementalCollectSlice(SliceBudget &budget, JS::gcreason::Reason rea
|
||||
/* fall through */
|
||||
}
|
||||
|
||||
case SWEEP: {
|
||||
bool finished = sweepPhase(budget);
|
||||
if (!finished)
|
||||
break;
|
||||
case SWEEP:
|
||||
{
|
||||
bool finished = sweepPhase(budget);
|
||||
if (!finished)
|
||||
break;
|
||||
}
|
||||
|
||||
endSweepPhase(lastGC);
|
||||
|
||||
#ifdef JSGC_COMPACTING
|
||||
incrementalState = COMPACT;
|
||||
|
||||
/* Yield before compacting since it is not incremental. */
|
||||
if (shouldCompact() && isIncremental)
|
||||
break;
|
||||
|
||||
case COMPACT:
|
||||
if (shouldCompact()) {
|
||||
incrementalState = COMPACT;
|
||||
compactPhase(lastGC);
|
||||
bool finished = compactPhase(lastGC);
|
||||
if (!finished)
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
finishCollection();
|
||||
incrementalState = NO_INCREMENTAL;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
MOZ_ASSERT(false);
|
||||
@ -6078,9 +6128,8 @@ GCRuntime::gcCycle(bool incremental, SliceBudget &budget, JSGCInvocationKind gck
|
||||
{
|
||||
gcstats::AutoPhase ap(stats, gcstats::PHASE_WAIT_BACKGROUND_THREAD);
|
||||
|
||||
// As we are about to purge caches and clear the mark bits, wait for
|
||||
// background finalization to finish. It cannot run between slices
|
||||
// so we only need to wait on the first slice.
|
||||
// As we are about to clear the mark bits, wait for background
|
||||
// finalization to finish. We only need to wait on the first slice.
|
||||
if (incrementalState == NO_INCREMENTAL)
|
||||
waitBackgroundSweepEnd();
|
||||
|
||||
@ -6389,7 +6438,7 @@ GCRuntime::shrinkBuffers()
|
||||
MOZ_ASSERT(!rt->isHeapBusy());
|
||||
|
||||
if (CanUseExtraThreads())
|
||||
helperState.startBackgroundShrink();
|
||||
helperState.startBackgroundShrink(lock);
|
||||
else
|
||||
expireChunksAndArenas(true, lock);
|
||||
}
|
||||
|
@ -39,6 +39,12 @@ enum HeapState {
|
||||
MinorCollecting // doing a GC of the minor heap (nursery)
|
||||
};
|
||||
|
||||
enum ThreadType
|
||||
{
|
||||
MainThread,
|
||||
BackgroundThread
|
||||
};
|
||||
|
||||
namespace jit {
|
||||
class JitCode;
|
||||
}
|
||||
@ -52,9 +58,7 @@ enum State {
|
||||
MARK_ROOTS,
|
||||
MARK,
|
||||
SWEEP,
|
||||
#ifdef JSGC_COMPACTING
|
||||
COMPACT
|
||||
#endif
|
||||
};
|
||||
|
||||
/* Return a printable string for the given kind, for diagnostic purposes. */
|
||||
@ -845,7 +849,7 @@ class ArenaLists
|
||||
|
||||
bool foregroundFinalize(FreeOp *fop, AllocKind thingKind, SliceBudget &sliceBudget,
|
||||
SortedArenaList &sweepList);
|
||||
static void backgroundFinalize(FreeOp *fop, ArenaHeader *listHead);
|
||||
static void backgroundFinalize(FreeOp *fop, ArenaHeader *listHead, ArenaHeader **empty);
|
||||
|
||||
void wipeDuringParallelExecution(JSRuntime *rt);
|
||||
|
||||
@ -1040,11 +1044,8 @@ class GCHelperState
|
||||
|
||||
void work();
|
||||
|
||||
/* Must be called with the GC lock taken. */
|
||||
void startBackgroundSweep();
|
||||
|
||||
/* Must be called with the GC lock taken. */
|
||||
void startBackgroundShrink();
|
||||
void maybeStartBackgroundSweep(const AutoLockGC &lock);
|
||||
void startBackgroundShrink(const AutoLockGC &lock);
|
||||
|
||||
/* Must be called without the GC lock taken. */
|
||||
void waitBackgroundSweepEnd();
|
||||
@ -1457,6 +1458,34 @@ class AutoEnterOOMUnsafeRegion {};
|
||||
bool
|
||||
IsInsideGGCNursery(const gc::Cell *cell);
|
||||
|
||||
// A singly linked list of zones.
|
||||
class ZoneList
|
||||
{
|
||||
static Zone * const End;
|
||||
|
||||
Zone *head;
|
||||
Zone *tail;
|
||||
|
||||
public:
|
||||
ZoneList();
|
||||
explicit ZoneList(Zone *singleZone);
|
||||
|
||||
bool isEmpty() const;
|
||||
Zone *front() const;
|
||||
|
||||
void append(Zone *zone);
|
||||
void append(ZoneList& list);
|
||||
Zone *removeFront();
|
||||
|
||||
void transferFrom(ZoneList &other);
|
||||
|
||||
private:
|
||||
void check() const;
|
||||
|
||||
ZoneList(const ZoneList &other) MOZ_DELETE;
|
||||
ZoneList &operator=(const ZoneList &other) MOZ_DELETE;
|
||||
};
|
||||
|
||||
} /* namespace gc */
|
||||
|
||||
#ifdef DEBUG
|
||||
|
@ -364,14 +364,15 @@ class NewObjectCache
|
||||
class FreeOp : public JSFreeOp
|
||||
{
|
||||
Vector<void *, 0, SystemAllocPolicy> freeLaterList;
|
||||
ThreadType threadType;
|
||||
|
||||
public:
|
||||
static FreeOp *get(JSFreeOp *fop) {
|
||||
return static_cast<FreeOp *>(fop);
|
||||
}
|
||||
|
||||
explicit FreeOp(JSRuntime *rt)
|
||||
: JSFreeOp(rt)
|
||||
explicit FreeOp(JSRuntime *rt, ThreadType thread = MainThread)
|
||||
: JSFreeOp(rt), threadType(thread)
|
||||
{}
|
||||
|
||||
~FreeOp() {
|
||||
@ -379,6 +380,10 @@ class FreeOp : public JSFreeOp
|
||||
free_(freeLaterList[i]);
|
||||
}
|
||||
|
||||
bool onBackgroundThread() {
|
||||
return threadType == BackgroundThread;
|
||||
}
|
||||
|
||||
inline void free_(void *p);
|
||||
inline void freeLater(void *p);
|
||||
|
||||
|
@ -1949,11 +1949,13 @@ nsDisplaySolidColor::Paint(nsDisplayListBuilder* aBuilder,
|
||||
|
||||
#ifdef MOZ_DUMP_PAINTING
|
||||
void
|
||||
nsDisplaySolidColor::WriteDebugInfo(nsACString& aTo)
|
||||
nsDisplaySolidColor::WriteDebugInfo(std::stringstream& aStream)
|
||||
{
|
||||
aTo += nsPrintfCString(" (rgba %d,%d,%d,%d)",
|
||||
NS_GET_R(mColor), NS_GET_G(mColor),
|
||||
NS_GET_B(mColor), NS_GET_A(mColor));
|
||||
aStream << " (rgba "
|
||||
<< (int)NS_GET_R(mColor) << ","
|
||||
<< (int)NS_GET_G(mColor) << ","
|
||||
<< (int)NS_GET_B(mColor) << ","
|
||||
<< (int)NS_GET_A(mColor) << ")";
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -2663,9 +2665,9 @@ nsDisplayThemedBackground::~nsDisplayThemedBackground()
|
||||
|
||||
#ifdef MOZ_DUMP_PAINTING
|
||||
void
|
||||
nsDisplayThemedBackground::WriteDebugInfo(nsACString& aTo)
|
||||
nsDisplayThemedBackground::WriteDebugInfo(std::stringstream& aStream)
|
||||
{
|
||||
aTo += nsPrintfCString(" (themed, appearance:%d)", mAppearance);
|
||||
aStream << " (themed, appearance:" << (int)mAppearance << ")";
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -2868,11 +2870,10 @@ nsDisplayBackgroundColor::HitTest(nsDisplayListBuilder* aBuilder,
|
||||
|
||||
#ifdef MOZ_DUMP_PAINTING
|
||||
void
|
||||
nsDisplayBackgroundColor::WriteDebugInfo(nsACString& aTo)
|
||||
nsDisplayBackgroundColor::WriteDebugInfo(std::stringstream& aStream)
|
||||
{
|
||||
aTo += nsPrintfCString(" (rgba %f,%f,%f,%f)",
|
||||
mColor.r, mColor.g,
|
||||
mColor.b, mColor.a);
|
||||
aStream << " (rgba " << mColor.r << "," << mColor.g << ","
|
||||
<< mColor.b << "," << mColor.a << ")";
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -2987,6 +2988,22 @@ nsDisplayLayerEventRegions::AddInactiveScrollPort(const nsRect& aRect)
|
||||
mDispatchToContentHitRegion.Or(mDispatchToContentHitRegion, aRect);
|
||||
}
|
||||
|
||||
#ifdef MOZ_DUMP_PAINTING
|
||||
void
|
||||
nsDisplayLayerEventRegions::WriteDebugInfo(std::stringstream& aStream)
|
||||
{
|
||||
if (!mHitRegion.IsEmpty()) {
|
||||
AppendToString(aStream, mHitRegion, " (hitRegion ", ")");
|
||||
}
|
||||
if (!mMaybeHitRegion.IsEmpty()) {
|
||||
AppendToString(aStream, mMaybeHitRegion, " (maybeHitRegion ", ")");
|
||||
}
|
||||
if (!mDispatchToContentHitRegion.IsEmpty()) {
|
||||
AppendToString(aStream, mDispatchToContentHitRegion, " (dispatchToContentRegion ", ")");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
nsDisplayCaret::nsDisplayCaret(nsDisplayListBuilder* aBuilder,
|
||||
nsIFrame* aCaretFrame)
|
||||
: nsDisplayItem(aBuilder, aCaretFrame)
|
||||
@ -3678,9 +3695,9 @@ bool nsDisplayOpacity::TryMerge(nsDisplayListBuilder* aBuilder, nsDisplayItem* a
|
||||
|
||||
#ifdef MOZ_DUMP_PAINTING
|
||||
void
|
||||
nsDisplayOpacity::WriteDebugInfo(nsACString& aTo)
|
||||
nsDisplayOpacity::WriteDebugInfo(std::stringstream& aStream)
|
||||
{
|
||||
aTo += nsPrintfCString(" (opacity %f)", mOpacity);
|
||||
aStream << " (opacity " << mOpacity << ")";
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -4443,10 +4460,10 @@ nsDisplayScrollLayer::GetScrollLayerCount()
|
||||
|
||||
#ifdef MOZ_DUMP_PAINTING
|
||||
void
|
||||
nsDisplayScrollLayer::WriteDebugInfo(nsACString& aTo)
|
||||
nsDisplayScrollLayer::WriteDebugInfo(std::stringstream& aStream)
|
||||
{
|
||||
aTo += nsPrintfCString(" (scrollframe %p scrolledframe %p)",
|
||||
mScrollFrame, mScrolledFrame);
|
||||
aStream << " (scrollframe " << mScrollFrame
|
||||
<< " scrolledFrame " << mScrolledFrame << ")";
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -5631,11 +5648,9 @@ bool nsDisplayTransform::UntransformVisibleRect(nsDisplayListBuilder* aBuilder,
|
||||
|
||||
#ifdef MOZ_DUMP_PAINTING
|
||||
void
|
||||
nsDisplayTransform::WriteDebugInfo(nsACString& aTo)
|
||||
nsDisplayTransform::WriteDebugInfo(std::stringstream& aStream)
|
||||
{
|
||||
std::stringstream ss;
|
||||
AppendToString(ss, GetTransform());
|
||||
aTo += ss.str().c_str();
|
||||
AppendToString(aStream, GetTransform());
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -1345,7 +1345,7 @@ public:
|
||||
*/
|
||||
virtual const char* Name() = 0;
|
||||
|
||||
virtual void WriteDebugInfo(nsACString& aTo) {}
|
||||
virtual void WriteDebugInfo(std::stringstream& aStream) {}
|
||||
#endif
|
||||
|
||||
nsDisplayItem* GetAbove() { return mAbove; }
|
||||
@ -2165,7 +2165,7 @@ public:
|
||||
}
|
||||
|
||||
#ifdef MOZ_DUMP_PAINTING
|
||||
virtual void WriteDebugInfo(nsACString& aTo) MOZ_OVERRIDE;
|
||||
virtual void WriteDebugInfo(std::stringstream& aStream) MOZ_OVERRIDE;
|
||||
#endif
|
||||
|
||||
NS_DISPLAY_DECL_NAME("SolidColor", TYPE_SOLID_COLOR)
|
||||
@ -2326,7 +2326,7 @@ public:
|
||||
nsRegion* aInvalidRegion) MOZ_OVERRIDE;
|
||||
|
||||
#ifdef MOZ_DUMP_PAINTING
|
||||
virtual void WriteDebugInfo(nsACString& aTo) MOZ_OVERRIDE;
|
||||
virtual void WriteDebugInfo(std::stringstream& aStream) MOZ_OVERRIDE;
|
||||
#endif
|
||||
protected:
|
||||
nsRect GetBoundsInternal();
|
||||
@ -2389,7 +2389,7 @@ public:
|
||||
|
||||
NS_DISPLAY_DECL_NAME("BackgroundColor", TYPE_BACKGROUND_COLOR)
|
||||
#ifdef MOZ_DUMP_PAINTING
|
||||
virtual void WriteDebugInfo(nsACString& aTo) MOZ_OVERRIDE;
|
||||
virtual void WriteDebugInfo(std::stringstream& aStream) MOZ_OVERRIDE;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
@ -2629,6 +2629,10 @@ public:
|
||||
const nsRegion& MaybeHitRegion() { return mMaybeHitRegion; }
|
||||
const nsRegion& DispatchToContentHitRegion() { return mDispatchToContentHitRegion; }
|
||||
|
||||
#ifdef MOZ_DUMP_PAINTING
|
||||
virtual void WriteDebugInfo(std::stringstream& aStream) MOZ_OVERRIDE;
|
||||
#endif
|
||||
|
||||
private:
|
||||
// Relative to aFrame's reference frame.
|
||||
// These are the points that are definitely in the hit region.
|
||||
@ -2834,7 +2838,7 @@ public:
|
||||
bool NeedsActiveLayer(nsDisplayListBuilder* aBuilder);
|
||||
NS_DISPLAY_DECL_NAME("Opacity", TYPE_OPACITY)
|
||||
#ifdef MOZ_DUMP_PAINTING
|
||||
virtual void WriteDebugInfo(nsACString& aTo) MOZ_OVERRIDE;
|
||||
virtual void WriteDebugInfo(std::stringstream& aStream) MOZ_OVERRIDE;
|
||||
#endif
|
||||
|
||||
bool CanUseAsyncAnimations(nsDisplayListBuilder* aBuilder) MOZ_OVERRIDE;
|
||||
@ -3132,7 +3136,7 @@ public:
|
||||
virtual nsIFrame* GetScrolledFrame() { return mScrolledFrame; }
|
||||
|
||||
#ifdef MOZ_DUMP_PAINTING
|
||||
virtual void WriteDebugInfo(nsACString& aTo) MOZ_OVERRIDE;
|
||||
virtual void WriteDebugInfo(std::stringstream& aStream) MOZ_OVERRIDE;
|
||||
#endif
|
||||
|
||||
bool IsDisplayPortOpaque() { return mDisplayPortContentsOpaque; }
|
||||
@ -3543,7 +3547,7 @@ public:
|
||||
bool ShouldPrerender(nsDisplayListBuilder* aBuilder);
|
||||
|
||||
#ifdef MOZ_DUMP_PAINTING
|
||||
virtual void WriteDebugInfo(nsACString& aTo) MOZ_OVERRIDE;
|
||||
virtual void WriteDebugInfo(std::stringstream& aStream) MOZ_OVERRIDE;
|
||||
#endif
|
||||
|
||||
private:
|
||||
|
@ -191,9 +191,7 @@ PrintDisplayItemTo(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem,
|
||||
}
|
||||
|
||||
// Display item specific debug info
|
||||
nsCString itemStr;
|
||||
aItem->WriteDebugInfo(itemStr);
|
||||
aStream << itemStr.get();
|
||||
aItem->WriteDebugInfo(aStream);
|
||||
|
||||
if (aDumpHtml && aItem->Painted()) {
|
||||
aStream << "</a>";
|
||||
|
@ -286,11 +286,13 @@ nsDisplayCanvasBackgroundColor::Paint(nsDisplayListBuilder* aBuilder,
|
||||
|
||||
#ifdef MOZ_DUMP_PAINTING
|
||||
void
|
||||
nsDisplayCanvasBackgroundColor::WriteDebugInfo(nsACString& aTo)
|
||||
nsDisplayCanvasBackgroundColor::WriteDebugInfo(std::stringstream& aStream)
|
||||
{
|
||||
aTo += nsPrintfCString(" (rgba %d,%d,%d,%d)",
|
||||
NS_GET_R(mColor), NS_GET_G(mColor),
|
||||
NS_GET_B(mColor), NS_GET_A(mColor));
|
||||
aStream << " (rgba "
|
||||
<< (int)NS_GET_R(mColor) << ","
|
||||
<< (int)NS_GET_G(mColor) << ","
|
||||
<< (int)NS_GET_B(mColor) << ","
|
||||
<< (int)NS_GET_A(mColor) << ")";
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -214,7 +214,7 @@ public:
|
||||
|
||||
NS_DISPLAY_DECL_NAME("CanvasBackgroundColor", TYPE_CANVAS_BACKGROUND_COLOR)
|
||||
#ifdef MOZ_DUMP_PAINTING
|
||||
virtual void WriteDebugInfo(nsACString& aTo) MOZ_OVERRIDE;
|
||||
virtual void WriteDebugInfo(std::stringstream& aStream) MOZ_OVERRIDE;
|
||||
#endif
|
||||
|
||||
private:
|
||||
|
@ -1453,9 +1453,7 @@ nsDisplayImage::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
|
||||
bool* aSnap)
|
||||
{
|
||||
*aSnap = true;
|
||||
bool animated;
|
||||
if (mImage && mImage->GetAnimated(&animated) == NS_OK && !animated &&
|
||||
mImage->FrameIsOpaque(imgIContainer::FRAME_CURRENT)) {
|
||||
if (mImage && mImage->IsOpaque()) {
|
||||
// OK, the entire region painted by the image is opaque. But what is that
|
||||
// region? It's the image's "dest rect" (the rect where a full copy of
|
||||
// the image is mapped), clipped to the container's content box (which is
|
||||
|
@ -2054,8 +2054,8 @@ nsStyleImage::IsOpaque() const
|
||||
mImage->GetImage(getter_AddRefs(imageContainer));
|
||||
NS_ABORT_IF_FALSE(imageContainer, "IsComplete() said image container is ready");
|
||||
|
||||
// Check if the crop region of the current image frame is opaque.
|
||||
if (imageContainer->FrameIsOpaque(imgIContainer::FRAME_CURRENT)) {
|
||||
// Check if the crop region of the image is opaque.
|
||||
if (imageContainer->IsOpaque()) {
|
||||
if (!mCropRect)
|
||||
return true;
|
||||
|
||||
|
@ -723,7 +723,7 @@ int ParseFTPList(const char *line, struct list_state *state,
|
||||
* "07-21-00 01:19PM 52275 Name Plate.jpg"
|
||||
* "07-14-00 01:38PM 2250540 Valentineoffprank-HiRes.jpg"
|
||||
*/
|
||||
if ((numtoks >= 4) && toklen[0] == 8 && toklen[1] == 7 &&
|
||||
if ((numtoks >= 4) && (toklen[0] == 8 || toklen[0] == 10) && toklen[1] == 7 &&
|
||||
(*tokens[2] == '<' || isdigit(*tokens[2])) )
|
||||
{
|
||||
p = tokens[0];
|
||||
|
@ -1007,6 +1007,89 @@ static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
|
||||
"VQQDExVRdW9WYWRpcyBSb290IENBIDIgRzM=",
|
||||
"RFc0JFuBiZs18s64KztbpybwdSg=",
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
// CN=COMODO RSA Certification Authority,O=COMODO CA Limited,L=Salford,ST=Greater Manchester,C=GB
|
||||
"1.3.6.1.4.1.6449.1.2.1.5.1",
|
||||
"COMODORSACertificationAuthority",
|
||||
SEC_OID_UNKNOWN,
|
||||
{ 0x52, 0xF0, 0xE1, 0xC4, 0xE5, 0x8E, 0xC6, 0x29, 0x29, 0x1B, 0x60,
|
||||
0x31, 0x7F, 0x07, 0x46, 0x71, 0xB8, 0x5D, 0x7E, 0xA8, 0x0D, 0x5B,
|
||||
0x07, 0x27, 0x34, 0x63, 0x53, 0x4B, 0x32, 0xB4, 0x02, 0x34 },
|
||||
"MIGFMQswCQYDVQQGEwJHQjEbMBkGA1UECBMSR3JlYXRlciBNYW5jaGVzdGVyMRAw"
|
||||
"DgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFDT01PRE8gQ0EgTGltaXRlZDErMCkG"
|
||||
"A1UEAxMiQ09NT0RPIFJTQSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eQ==",
|
||||
"TKr5yttjb+Af907YWwOGnQ==",
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
// CN=USERTrust RSA Certification Authority,O=The USERTRUST Network,L=Jersey City,ST=New Jersey,C=US
|
||||
"1.3.6.1.4.1.6449.1.2.1.5.1",
|
||||
"USERTrustRSACertificationAuthority",
|
||||
SEC_OID_UNKNOWN,
|
||||
{ 0xE7, 0x93, 0xC9, 0xB0, 0x2F, 0xD8, 0xAA, 0x13, 0xE2, 0x1C, 0x31,
|
||||
0x22, 0x8A, 0xCC, 0xB0, 0x81, 0x19, 0x64, 0x3B, 0x74, 0x9C, 0x89,
|
||||
0x89, 0x64, 0xB1, 0x74, 0x6D, 0x46, 0xC3, 0xD4, 0xCB, 0xD2 },
|
||||
"MIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMKTmV3IEplcnNleTEUMBIGA1UEBxML"
|
||||
"SmVyc2V5IENpdHkxHjAcBgNVBAoTFVRoZSBVU0VSVFJVU1QgTmV0d29yazEuMCwG"
|
||||
"A1UEAxMlVVNFUlRydXN0IFJTQSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eQ==",
|
||||
"Af1tMPyjylGoG7xkDjUDLQ==",
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
// CN=USERTrust ECC Certification Authority,O=The USERTRUST Network,L=Jersey City,ST=New Jersey,C=US
|
||||
"1.3.6.1.4.1.6449.1.2.1.5.1",
|
||||
"USERTrust ECC Certification Authority",
|
||||
SEC_OID_UNKNOWN,
|
||||
{ 0x4F, 0xF4, 0x60, 0xD5, 0x4B, 0x9C, 0x86, 0xDA, 0xBF, 0xBC, 0xFC,
|
||||
0x57, 0x12, 0xE0, 0x40, 0x0D, 0x2B, 0xED, 0x3F, 0xBC, 0x4D, 0x4F,
|
||||
0xBD, 0xAA, 0x86, 0xE0, 0x6A, 0xDC, 0xD2, 0xA9, 0xAD, 0x7A },
|
||||
"MIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMKTmV3IEplcnNleTEUMBIGA1UEBxML"
|
||||
"SmVyc2V5IENpdHkxHjAcBgNVBAoTFVRoZSBVU0VSVFJVU1QgTmV0d29yazEuMCwG"
|
||||
"A1UEAxMlVVNFUlRydXN0IEVDQyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eQ==",
|
||||
"XIuZxVqUxdJxVt7NiYDMJg==",
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
// CN=GlobalSign,O=GlobalSign,OU=GlobalSign ECC Root CA - R4
|
||||
"1.3.6.1.4.1.4146.1.1",
|
||||
"GlobalSign ECC Root CA - R4",
|
||||
SEC_OID_UNKNOWN,
|
||||
{ 0xBE, 0xC9, 0x49, 0x11, 0xC2, 0x95, 0x56, 0x76, 0xDB, 0x6C, 0x0A,
|
||||
0x55, 0x09, 0x86, 0xD7, 0x6E, 0x3B, 0xA0, 0x05, 0x66, 0x7C, 0x44,
|
||||
0x2C, 0x97, 0x62, 0xB4, 0xFB, 0xB7, 0x73, 0xDE, 0x22, 0x8C },
|
||||
"MFAxJDAiBgNVBAsTG0dsb2JhbFNpZ24gRUNDIFJvb3QgQ0EgLSBSNDETMBEGA1UE"
|
||||
"ChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbg==",
|
||||
"KjikHJYKBN5CsiilC+g0mAI=",
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
// CN=GlobalSign,O=GlobalSign,OU=GlobalSign ECC Root CA - R5
|
||||
"1.3.6.1.4.1.4146.1.1",
|
||||
"GlobalSign ECC Root CA - R5",
|
||||
SEC_OID_UNKNOWN,
|
||||
{ 0x17, 0x9F, 0xBC, 0x14, 0x8A, 0x3D, 0xD0, 0x0F, 0xD2, 0x4E, 0xA1,
|
||||
0x34, 0x58, 0xCC, 0x43, 0xBF, 0xA7, 0xF5, 0x9C, 0x81, 0x82, 0xD7,
|
||||
0x83, 0xA5, 0x13, 0xF6, 0xEB, 0xEC, 0x10, 0x0C, 0x89, 0x24 },
|
||||
"MFAxJDAiBgNVBAsTG0dsb2JhbFNpZ24gRUNDIFJvb3QgQ0EgLSBSNTETMBEGA1UE"
|
||||
"ChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbg==",
|
||||
"YFlJ4CYuu1X5CneKcflK2Gw=",
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
// CN=Entrust.net Certification Authority (2048),OU=(c) 1999 Entrust.net Limited,OU=www.entrust.net/CPS_2048 incorp. by ref. (limits liab.),O=Entrust.net
|
||||
"2.16.840.1.114028.10.1.2",
|
||||
"EntrustCA2048",
|
||||
SEC_OID_UNKNOWN,
|
||||
{ 0x6D, 0xC4, 0x71, 0x72, 0xE0, 0x1C, 0xBC, 0xB0, 0xBF, 0x62, 0x58,
|
||||
0x0D, 0x89, 0x5F, 0xE2, 0xB8, 0xAC, 0x9A, 0xD4, 0xF8, 0x73, 0x80,
|
||||
0x1E, 0x0C, 0x10, 0xB9, 0xC8, 0x37, 0xD2, 0x1E, 0xB1, 0x77 },
|
||||
"MIG0MRQwEgYDVQQKEwtFbnRydXN0Lm5ldDFAMD4GA1UECxQ3d3d3LmVudHJ1c3Qu"
|
||||
"bmV0L0NQU18yMDQ4IGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTElMCMG"
|
||||
"A1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEGA1UEAxMqRW50"
|
||||
"cnVzdC5uZXQgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgKDIwNDgp",
|
||||
"OGPe+A==",
|
||||
nullptr
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -13,7 +13,7 @@ do_get_profile(); // must be called before getting nsIX509CertDB
|
||||
const certdb = Cc["@mozilla.org/security/x509certdb;1"]
|
||||
.getService(Ci.nsIX509CertDB);
|
||||
|
||||
const SERVER_PORT = 8080;
|
||||
const SERVER_PORT = 8888;
|
||||
|
||||
function start_ocsp_responder(expectedCertNames, expectedPaths,
|
||||
expectedMethods) {
|
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -22,7 +22,7 @@ def generate_certs():
|
||||
[noise_file, pwd_file] = CertUtils.init_nss_db(srcdir)
|
||||
generate_ca_cert(srcdir, srcdir, noise_file, 'ca')
|
||||
generate_child_cert(srcdir, srcdir, noise_file, 'int', 'ca', False, '')
|
||||
ocsp_url = "http://www.example.com:8080/"
|
||||
ocsp_url = "http://www.example.com:8888/"
|
||||
generate_child_cert(srcdir, srcdir, noise_file, "a", 'int', True, ocsp_url)
|
||||
generate_child_cert(srcdir, srcdir, noise_file, "b", 'int', True, ocsp_url)
|
||||
|
||||
|
Binary file not shown.
Binary file not shown.
@ -91,12 +91,8 @@ run-sequentially = hardcoded ports
|
||||
skip-if = os == "android"
|
||||
[test_ocsp_url.js]
|
||||
run-sequentially = hardcoded ports
|
||||
# Bug 1009158: this test times out on Android
|
||||
skip-if = os == "android"
|
||||
[test_ocsp_fetch_method.js]
|
||||
run-sequentially = hardcoded ports
|
||||
# Bug 1009158: this test times out on Android
|
||||
skip-if = os == "android"
|
||||
[test_ocsp_no_hsts_upgrade.js]
|
||||
run-sequentially = hardcoded ports
|
||||
# Bug 1009158: this test times out on Android
|
||||
@ -105,9 +101,8 @@ skip-if = os == "android"
|
||||
[test_keysize.js]
|
||||
[test_keysize_ev.js]
|
||||
run-sequentially = hardcoded ports
|
||||
# Bug 1009158: this test times out on Android
|
||||
# Bug 1008316: B2G doesn't have EV enabled
|
||||
skip-if = os == "android" || buildapp == "b2g"
|
||||
skip-if = buildapp == "b2g"
|
||||
[test_cert_chains.js]
|
||||
run-sequentially = hardcoded ports
|
||||
# Bug 1009158: this test times out on Android
|
||||
|
46
testing/marionette/client/docs/advanced/actions.rst
Normal file
46
testing/marionette/client/docs/advanced/actions.rst
Normal file
@ -0,0 +1,46 @@
|
||||
Actions
|
||||
=======
|
||||
|
||||
.. py:currentmodule:: marionette
|
||||
|
||||
Action Sequences
|
||||
----------------
|
||||
|
||||
:class:`Actions` are designed as a way to simulate user input as closely as possible
|
||||
on a touch device like a smart phone. A common operation is to tap the screen
|
||||
and drag your finger to another part of the screen and lift it off.
|
||||
|
||||
This can be simulated using an Action::
|
||||
|
||||
from marionette import Actions
|
||||
|
||||
start_element = marionette.find_element('id', 'start')
|
||||
end_element = marionette.find_element('id', 'end')
|
||||
|
||||
action = Actions(marionette)
|
||||
action.press(start_element).wait(1).move(end_element).release()
|
||||
action.perform()
|
||||
|
||||
This will simulate pressing an element, waiting for one second, moving the
|
||||
finger over to another element and then lifting the finger off the screen. The
|
||||
wait is optional in this case, but can be useful for simulating delays typical
|
||||
to a users behaviour.
|
||||
|
||||
Multi-Action Sequences
|
||||
----------------------
|
||||
|
||||
Sometimes it may be necessary to simulate multiple actions at the same time.
|
||||
For example a user may be dragging one finger while tapping another. This is
|
||||
where :class:`MultiActions` come in. MultiActions are simply a way of combining
|
||||
two or more actions together and performing them all at the same time::
|
||||
|
||||
action1 = Actions(marionette)
|
||||
action1.press(start_element).move(end_element).release()
|
||||
|
||||
action2 = Actions(marionette)
|
||||
action2.press(another_element).wait(1).release()
|
||||
|
||||
multi = MultiActions(marionette)
|
||||
multi.add(action1)
|
||||
multi.add(action2)
|
||||
multi.perform()
|
54
testing/marionette/client/docs/advanced/debug.rst
Normal file
54
testing/marionette/client/docs/advanced/debug.rst
Normal file
@ -0,0 +1,54 @@
|
||||
Debugging
|
||||
=========
|
||||
|
||||
.. py:currentmodule:: marionette
|
||||
|
||||
Sometimes when working with Marionette you'll run into unexpected behaviour and
|
||||
need to do some debugging. This page outlines some of the Marionette methods
|
||||
that can be useful to you.
|
||||
|
||||
Please note that the best tools for debugging are the `ones that ship with
|
||||
Gecko`_. This page doesn't describe how to use those with Marionette. Also see
|
||||
a related topic about `using the debugger with Marionette`_ on MDN.
|
||||
|
||||
.. _ones that ship with Gecko: https://developer.mozilla.org/en-US/docs/Tools
|
||||
.. _using the debugger with Marionette: https://developer.mozilla.org/en-US/docs/Marionette/Debugging
|
||||
|
||||
|
||||
Storing Logs on the Server
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
By calling `~Marionette.log` it is possible to store a message on the server.
|
||||
Logs can later be retrieved using `~Marionette.get_logs`. For example::
|
||||
|
||||
try:
|
||||
marionette.log("Sending a click event") # logged at INFO level
|
||||
elem.click()
|
||||
except:
|
||||
marionette.log("Something went wrong!", "ERROR")
|
||||
|
||||
print(marionette.get_logs())
|
||||
|
||||
Disclaimer: Example for illustrative purposes only, don't actually hide
|
||||
tracebacks like that!
|
||||
|
||||
|
||||
Seeing What's on the Page
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Sometimes it's difficult to tell what is actually on the page that is being
|
||||
manipulated. Either because it happens too fast, the window isn't big enough or
|
||||
you are manipulating a remote server! There are two methods that can help you
|
||||
out. The first is `~Marionette.screenshot`::
|
||||
|
||||
marionette.screenshot() # takes screenshot of entire frame
|
||||
elem = marionette.find_element(By.ID, 'some-div')
|
||||
marionette.screenshot(elem) # takes a screenshot of only the given element
|
||||
|
||||
Sometimes you just want to see the DOM layout. You can do this with the
|
||||
`~Marionette.page_source` property. Note that the page source depends on the
|
||||
context you are in::
|
||||
|
||||
print(marionette.page_source)
|
||||
marionette.set_context('chrome')
|
||||
print(marionette.page_source)
|
126
testing/marionette/client/docs/advanced/findelement.rst
Normal file
126
testing/marionette/client/docs/advanced/findelement.rst
Normal file
@ -0,0 +1,126 @@
|
||||
Finding Elements
|
||||
================
|
||||
.. py:currentmodule:: marionette
|
||||
|
||||
One of the most common and yet often most difficult tasks in Marionette is
|
||||
finding a DOM element on a webpage or in the chrome UI. Marionette provides
|
||||
several different search strategies to use when finding elements. All search
|
||||
strategies work with both :func:`~Marionette.find_element` and
|
||||
:func:`~Marionette.find_elements`, though some strategies are not implemented
|
||||
in chrome scope.
|
||||
|
||||
In the event that more than one element is matched by the query,
|
||||
:func:`~Marionette.find_element` will only return the first element found. In
|
||||
the event that no elements are matched by the query,
|
||||
:func:`~Marionette.find_element` will raise `NoSuchElementException` while
|
||||
:func:`~Marionette.find_elements` will return an empty list.
|
||||
|
||||
Search Strategies
|
||||
-----------------
|
||||
|
||||
Search strategies are defined in the :class:`By` class::
|
||||
|
||||
from marionette import By
|
||||
print(By.ID)
|
||||
|
||||
The strategies are:
|
||||
|
||||
* `id` - The easiest way to find an element is to refer to its id directly::
|
||||
|
||||
container = client.find_element(By.ID, 'container')
|
||||
|
||||
* `class name` - To find elements belonging to a certain class, use `class name`::
|
||||
|
||||
buttons = client.find_elements(By.CLASS_NAME, 'button')
|
||||
|
||||
* `css selector` - It's also possible to find elements using a `css selector`_::
|
||||
|
||||
container_buttons = client.find_elements(By.CSS_SELECTOR, '#container .buttons')
|
||||
|
||||
* `name` - Find elements by their name attribute (not implemented in chrome
|
||||
scope)::
|
||||
|
||||
form = client.find_element(By.NAME, 'signup')
|
||||
|
||||
* `tag name` - To find all the elements with a given tag, use `tag name`::
|
||||
|
||||
paragraphs = client.find_elements(By.TAG_NAME, 'p')
|
||||
|
||||
* `link text` - A convenience strategy for finding link elements by their
|
||||
innerHTML (not implemented in chrome scope)::
|
||||
|
||||
link = client.find_element(By.LINK_TEXT, 'Click me!')
|
||||
|
||||
* `partial link text` - Same as `link text` except substrings of the innerHTML
|
||||
are matched (not implemented in chrome scope)::
|
||||
|
||||
link = client.find_element(By.PARTIAL_LINK_TEXT, 'Clic')
|
||||
|
||||
* `xpath` - Find elements using an xpath_ query::
|
||||
|
||||
elem = client.find_element(By.XPATH, './/*[@id="foobar"')
|
||||
|
||||
.. _css selector: https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Getting_Started/Selectors
|
||||
.. _xpath: https://developer.mozilla.org/en-US/docs/Web/XPath
|
||||
|
||||
|
||||
|
||||
Chaining Searches
|
||||
-----------------
|
||||
|
||||
In addition to the methods on the Marionette object, HTMLElement objects also
|
||||
provide :func:`~HTMLElement.find_element` and :func:`~HTMLElement.find_elements`
|
||||
methods. The difference is that only child nodes of the element will be searched.
|
||||
Consider the following html snippet::
|
||||
|
||||
<div id="content">
|
||||
<span id="main"></span>
|
||||
</div>
|
||||
<div id="footer"></div>
|
||||
|
||||
Doing the following will work::
|
||||
|
||||
client.find_element(By.ID, 'container').find_element(By.ID, 'main')
|
||||
|
||||
But this will raise a `NoSuchElementException`::
|
||||
|
||||
client.find_element(By.ID, 'container').find_element(By.ID, 'footer')
|
||||
|
||||
|
||||
Finding Anonymous Nodes
|
||||
-----------------------
|
||||
|
||||
When working in chrome scope, for example manipulating the Firefox user
|
||||
interface, you may run into something called an anonymous node.
|
||||
|
||||
Firefox uses a markup language called XUL_ for its interface. XUL is similar
|
||||
to HTML in that it has a DOM and tags that render controls on the display. One
|
||||
ability of XUL is to create re-useable widgets that are made up out of several
|
||||
smaller XUL elements. These widgets can be bound to the DOM using something
|
||||
called the `XML binding language (XBL)`_.
|
||||
|
||||
The end result is that the DOM sees the widget as a single entity. It doesn't
|
||||
know anything about how that widget is made up. All of the smaller XUL elements
|
||||
that make up the widget are called `anonymous content`_. It is not possible to
|
||||
query such elements using traditional DOM methods like `getElementById`.
|
||||
|
||||
Marionette provides two special strategies used for finding anonymous content.
|
||||
Unlike normal elements, anonymous nodes can only be seen by their parent. So
|
||||
it's necessary to first find the parent element and then search for the
|
||||
anonymous children from there.
|
||||
|
||||
* `anon` - Finds all anonymous children of the element, there is no search term
|
||||
so `None` must be passed in::
|
||||
|
||||
anon_children = client.find_element('id', 'parent').find_elements('anon', None)
|
||||
|
||||
* `anon attribute` - Find an anonymous child based on an attribute. An
|
||||
unofficial convention is for anonymous nodes to have an
|
||||
`anonid` attribute::
|
||||
|
||||
anon_child = client.find_element('id', 'parent').find_element('anon attribute', {'anonid': 'container'})
|
||||
|
||||
|
||||
.. _XUL: https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XUL
|
||||
.. _XML binding language (XBL): https://developer.mozilla.org/en-US/docs/XBL
|
||||
.. _anonymous content: https://developer.mozilla.org/en-US/docs/XBL/XBL_1.0_Reference/Anonymous_Content
|
13
testing/marionette/client/docs/advanced/landing.rst
Normal file
13
testing/marionette/client/docs/advanced/landing.rst
Normal file
@ -0,0 +1,13 @@
|
||||
Advanced Topics
|
||||
===============
|
||||
|
||||
Here are a collection of articles explaining some of the more complicated
|
||||
aspects of Marionette.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
findelement
|
||||
stale
|
||||
actions
|
||||
debug
|
71
testing/marionette/client/docs/advanced/stale.rst
Normal file
71
testing/marionette/client/docs/advanced/stale.rst
Normal file
@ -0,0 +1,71 @@
|
||||
Dealing with Stale Elements
|
||||
===========================
|
||||
.. py:currentmodule:: marionette
|
||||
|
||||
Marionette does not keep a live representation of the DOM saved. All it can do
|
||||
is send commands to the Marionette server which queries the DOM on the client's
|
||||
behalf. References to elements are also not passed from server to client. A
|
||||
unique id is generated for each element that gets referenced and a mapping of
|
||||
id to element object is stored on the server. When commands such as
|
||||
:func:`~HTMLElement.click()` are run, the client sends the element's id along
|
||||
with the command. The server looks up the proper DOM element in its reference
|
||||
table and executes the command on it.
|
||||
|
||||
In practice this means that the DOM can change state and Marionette will never
|
||||
know until it sends another query. For example, look at the following HTML::
|
||||
|
||||
<head>
|
||||
<script type=text/javascript>
|
||||
function addDiv() {
|
||||
var div = document.createElement("div");
|
||||
document.getElementById("container").appendChild(div);
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="container">
|
||||
</div>
|
||||
<input id="button" type=button onclick="addDiv();">
|
||||
</body>
|
||||
|
||||
Care needs to be taken as the DOM is being modified after the page has loaded.
|
||||
The following code has a race condition::
|
||||
|
||||
button = client.find_element('id', 'button')
|
||||
button.click()
|
||||
assert len(client.find_elements('css selector', '#container div')) > 0
|
||||
|
||||
|
||||
Explicit Waiting and Expected Conditions
|
||||
----------------------------------------
|
||||
|
||||
To avoid the above scenario, manual synchronisation is needed. Waits are used
|
||||
to pause program execution until a given condition is true. This is a useful
|
||||
technique to employ when documents load new content or change after
|
||||
``Document.readyState``'s value changes to "complete".
|
||||
|
||||
The :class:`Wait` helper class provided by Marionette avoids some of the
|
||||
caveats of ``time.sleep(n)``. It will return immediately once the provided
|
||||
condition evaluates to true.
|
||||
|
||||
To avoid the race condition in the above example, one could do::
|
||||
|
||||
button = client.find_element('id', 'button')
|
||||
button.click()
|
||||
|
||||
def find_divs():
|
||||
return client.find_elements('css selector', '#container div')
|
||||
|
||||
divs = Wait(client).until(find_divs)
|
||||
assert len(divs) > 0
|
||||
|
||||
This avoids the race condition. Because finding elements is a common condition
|
||||
to wait for, it is built in to Marionette. Instead of the above, you could
|
||||
write::
|
||||
|
||||
button = client.find_element('id', 'button')
|
||||
button.click()
|
||||
assert len(Wait(client).until(expected.elements_present('css selector', '#container div'))) > 0
|
||||
|
||||
For a full list of built-in conditions, see :mod:`~marionette.expected`.
|
185
testing/marionette/client/docs/basics.rst
Normal file
185
testing/marionette/client/docs/basics.rst
Normal file
@ -0,0 +1,185 @@
|
||||
.. py:currentmodule:: marionette
|
||||
|
||||
Marionette Python Client
|
||||
========================
|
||||
|
||||
The Marionette python client library allows you to remotely control a
|
||||
Gecko-based browser or device which is running a Marionette_
|
||||
server. This includes desktop Firefox and FirefoxOS (support for
|
||||
Firefox for Android is planned, but not yet fully implemented).
|
||||
|
||||
The Marionette server is built directly into Gecko and can be started by
|
||||
passing in a command line option to Gecko, or by using a Marionette-enabled
|
||||
build. The server listens for connections from various clients. Clients can
|
||||
then control Gecko by sending commands to the server.
|
||||
|
||||
This is the official python client for Marionette. There also exists a
|
||||
`NodeJS client`_ maintained by the Firefox OS automation team.
|
||||
|
||||
.. _Marionette: https://developer.mozilla.org/en-US/docs/Marionette
|
||||
.. _NodeJS client: https://github.com/mozilla-b2g/marionette-js-client
|
||||
|
||||
Getting the Client
|
||||
------------------
|
||||
|
||||
The python client is officially supported. To install it, first make sure you
|
||||
have `pip installed`_ then run:
|
||||
|
||||
.. parsed-literal::
|
||||
pip install marionette_client
|
||||
|
||||
It's highly recommended to use virtualenv_ when installing Marionette to avoid
|
||||
package conflicts and other general nastiness.
|
||||
|
||||
You should now be ready to start using Marionette. The best way to learn is to
|
||||
play around with it. Start a `Marionette-enabled instance of Firefox`_, fire up
|
||||
a python shell and follow along with the
|
||||
:doc:`interactive tutorial <interactive>`!
|
||||
|
||||
.. _pip installed: https://pip.pypa.io/en/latest/installing.html
|
||||
.. _virtualenv: http://virtualenv.readthedocs.org/en/latest/
|
||||
.. _Marionette-enabled instance of Firefox: https://developer.mozilla.org/en-US/docs/Mozilla/QA/Marionette/Builds
|
||||
|
||||
Using the Client for Testing
|
||||
----------------------------
|
||||
|
||||
Please visit the `Marionette Tests`_ section on MDN for information regarding
|
||||
testing with Marionette.
|
||||
|
||||
.. _Marionette Tests: https://developer.mozilla.org/en/Marionette/Tests
|
||||
|
||||
Session Management
|
||||
------------------
|
||||
A session is a single instance of a Marionette client connected to a Marionette
|
||||
server. Before you can start executing commands, you need to start a session
|
||||
with :func:`start_session() <Marionette.start_session>`:
|
||||
|
||||
.. parsed-literal::
|
||||
client = Marionette('localhost', port=2828)
|
||||
client.start_session()
|
||||
|
||||
This returns a session id and an object listing the capabilities of the
|
||||
Marionette server. For example, a server running on a Firefox OS device will
|
||||
have the ability to rotate the window, while a server running from Firefox
|
||||
won't. It's also possible to access the capabilities using the
|
||||
:attr:`~Marionette.session_capabilities` attribute. After finishing with a
|
||||
session, you can delete it with :func:`~Marionette.delete_session()`. Note that
|
||||
this will also happen automatically when the Marionette object is garbage
|
||||
collected.
|
||||
|
||||
Context Management
|
||||
------------------
|
||||
Commands can only be executed in a single window, frame and scope at a time. In
|
||||
order to run commands elsewhere, it's necessary to explicitly switch to the
|
||||
appropriate context.
|
||||
|
||||
Use :func:`~Marionette.switch_to_window` to execute commands in the context of a
|
||||
new window:
|
||||
|
||||
.. parsed-literal::
|
||||
original_window = client.current_window_handle
|
||||
for handle in client.window_handles:
|
||||
if handle != original_window:
|
||||
client.switch_to_window(handle)
|
||||
print("Switched to window with '{}' loaded.".format(client.get_url()))
|
||||
client.switch_to_window(original_window)
|
||||
|
||||
Similarly, use :func:`~Marionette.switch_to_frame` to execute commands in the
|
||||
context of a new frame (e.g an <iframe> element):
|
||||
|
||||
.. parsed-literal::
|
||||
iframe = client.find_element(By.TAG_NAME, 'iframe')
|
||||
client.switch_to_frame(iframe)
|
||||
assert iframe == client.get_active_frame()
|
||||
|
||||
Finally Marionette can switch between `chrome` and `content` scope. Chrome is a
|
||||
privileged scope where you can access things like the Firefox UI itself or the
|
||||
system app in Firefox OS. Content scope is where things like webpages or normal
|
||||
Firefox OS apps live. You can switch between `chrome` and `content` using the
|
||||
:func:`~Marionette.set_context` and :func:`~Marionette.using_context` functions:
|
||||
|
||||
.. parsed-literal::
|
||||
client.set_context(client.CONTEXT_CONTENT)
|
||||
# content scope
|
||||
with client.using_context(client.CONTEXT_CHROME):
|
||||
#chrome scope
|
||||
... do stuff ...
|
||||
# content scope restored
|
||||
|
||||
|
||||
Navigation
|
||||
----------
|
||||
|
||||
Use :func:`~Marionette.navigate` to open a new website. It's also possible to
|
||||
move through the back/forward cache using :func:`~Marionette.go_forward` and
|
||||
:func:`~Marionette.go_back` respectively. To retrieve the currently
|
||||
open website, use :func:`~Marionette.get_url`:
|
||||
|
||||
.. parsed-literal::
|
||||
url = 'http://mozilla.org'
|
||||
client.navigate(url)
|
||||
client.go_back()
|
||||
client.go_forward()
|
||||
assert client.get_url() == url
|
||||
|
||||
|
||||
DOM Elements
|
||||
------------
|
||||
|
||||
In order to inspect or manipulate actual DOM elements, they must first be found
|
||||
using the :func:`~Marionette.find_element` or :func:`~Marionette.find_elements`
|
||||
methods:
|
||||
|
||||
.. parsed-literal::
|
||||
from marionette import HTMLElement
|
||||
element = client.find_element(By.ID, 'my-id')
|
||||
assert type(element) == HTMLElement
|
||||
elements = client.find_elements(By.TAG_NAME, 'a')
|
||||
assert type(elements) == list
|
||||
|
||||
For a full list of valid search strategies, see :doc:`advanced/findelement`.
|
||||
|
||||
Now that an element has been found, it's possible to manipulate it:
|
||||
|
||||
.. parsed-literal::
|
||||
element.click()
|
||||
element.send_keys('hello!')
|
||||
print(element.get_attribute('style'))
|
||||
|
||||
For the full list of possible commands, see the :class:`HTMLElement`
|
||||
reference.
|
||||
|
||||
Be warned that a reference to an element object can become stale if it was
|
||||
modified or removed from the document. See :doc:`advanced/stale` for tips
|
||||
on working around this limitation.
|
||||
|
||||
Script Execution
|
||||
----------------
|
||||
|
||||
Sometimes Marionette's provided APIs just aren't enough and it is necessary to
|
||||
run arbitrary javascript. This is accomplished with the
|
||||
:func:`~Marionette.execute_script` and :func:`~Marionette.execute_async_script`
|
||||
functions. They accomplish what their names suggest, the former executes some
|
||||
synchronous JavaScript, while the latter provides a callback mechanism for
|
||||
running asynchronous JavaScript:
|
||||
|
||||
.. parsed-literal::
|
||||
result = client.execute_script("return arguments[0] + arguments[1];",
|
||||
script_args=[2, 3])
|
||||
assert result == 5
|
||||
|
||||
The async method works the same way, except it won't return until a special
|
||||
`marionetteScriptFinished()` function is called:
|
||||
|
||||
.. parsed-literal::
|
||||
result = client.execute_async_script("""
|
||||
setTimeout(function() {
|
||||
marionetteScriptFinished("all done");
|
||||
}, arguments[0]);
|
||||
""", script_args=[1000])
|
||||
assert result == "all done"
|
||||
|
||||
Beware that running asynchronous scripts can potentially hang the program
|
||||
indefinitely if they are not written properly. It is generally a good idea to
|
||||
set a script timeout using :func:`~Marionette.set_script_timeout` and handling
|
||||
`ScriptTimeoutException`.
|
@ -95,8 +95,20 @@ pygments_style = 'sphinx'
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
|
||||
html_theme = 'default'
|
||||
|
||||
on_rtd = os.environ.get('READTHEDOCS', None) == 'True'
|
||||
|
||||
if not on_rtd:
|
||||
try:
|
||||
import sphinx_rtd_theme
|
||||
html_theme = 'sphinx_rtd_theme'
|
||||
html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
# documentation.
|
||||
|
@ -1,220 +1,16 @@
|
||||
.. Marionette Python Client documentation master file, created by
|
||||
sphinx-quickstart on Tue Aug 6 13:54:46 2013.
|
||||
You can adapt this file completely to your liking, but it should at least
|
||||
contain the root `toctree` directive.
|
||||
|
||||
Marionette Python Client
|
||||
========================
|
||||
|
||||
The Marionette python client library allows you to remotely control a
|
||||
Gecko-based browser or device which is running a Marionette_
|
||||
server. This includes desktop Firefox and FirefoxOS (support for
|
||||
Firefox for Android is planned, but not yet fully implemented).
|
||||
|
||||
.. _Marionette: https://developer.mozilla.org/en-US/docs/Marionette
|
||||
|
||||
Getting Started
|
||||
---------------
|
||||
|
||||
Getting the Client
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
|
||||
We officially support a python client. The latest supported version of
|
||||
the client is available on pypi_, so you can download it via pip.
|
||||
This client should be used within a virtual environment to ensure that
|
||||
your environment is pristine:
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
virtualenv venv
|
||||
source venv/bin/activate
|
||||
pip install marionette_client
|
||||
|
||||
.. _pypi: https://pypi.python.org/pypi/marionette_client/
|
||||
|
||||
Using the Client Interactively
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Once you installed the client and have Marionette running, you can fire
|
||||
up your favourite interactive python environment and start playing with
|
||||
Marionette. Let's use a typical python shell:
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
python
|
||||
|
||||
First, import Marionette:
|
||||
|
||||
.. parsed-literal::
|
||||
from marionette import Marionette
|
||||
|
||||
Now create the client for this session. Assuming you're using the default
|
||||
port on a Marionette instance running locally:
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
client = Marionette(host='localhost', port=2828)
|
||||
client.start_session()
|
||||
|
||||
This will return some id representing your session id. Now that you've
|
||||
established a connection, let's start doing interesting things:
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
client.execute_script("alert('o hai there!');")
|
||||
|
||||
You should now see this alert pop up! How exciting! Okay, let's do
|
||||
something practical. Close the dialog and try this:
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
client.navigate("http://www.mozilla.org")
|
||||
|
||||
Now you're at mozilla.org! You can even verify it using the following:
|
||||
|
||||
.. parsed-literal::
|
||||
client.get_url()
|
||||
|
||||
You can even find an element and click on it. Let's say you want to get
|
||||
the first link:
|
||||
|
||||
.. parsed-literal::
|
||||
first_link = client.find_element("tag name", "a")
|
||||
|
||||
first_link now holds a reference to the first link on the page. You can click it:
|
||||
|
||||
.. parsed-literal::
|
||||
first_link.click()
|
||||
|
||||
Using the Client for Testing
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Please visit, our `Marionette Tests`_ section for information regarding testing.
|
||||
|
||||
.. _Marionette Tests: https://developer.mozilla.org/en/Marionette/Tests
|
||||
|
||||
.. automodule:: marionette
|
||||
|
||||
Marionette Objects
|
||||
------------------
|
||||
.. autoclass:: Marionette
|
||||
|
||||
Session Management
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
.. automethod:: Marionette.start_session
|
||||
.. automethod:: Marionette.delete_session
|
||||
.. autoattribute:: Marionette.session_capabilities
|
||||
.. automethod:: Marionette.get_cookie
|
||||
.. automethod:: Marionette.get_cookies
|
||||
.. automethod:: Marionette.add_cookie
|
||||
.. automethod:: Marionette.delete_all_cookies
|
||||
|
||||
Context Management
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
.. autoattribute:: Marionette.current_window_handle
|
||||
.. autoattribute:: Marionette.window_handles
|
||||
.. automethod:: Marionette.set_context
|
||||
.. automethod:: Marionette.switch_to_frame
|
||||
.. automethod:: Marionette.switch_to_window
|
||||
.. automethod:: Marionette.get_active_frame
|
||||
.. automethod:: Marionette.close
|
||||
|
||||
Navigation Methods
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
.. autoattribute:: Marionette.title
|
||||
.. automethod:: Marionette.navigate
|
||||
.. automethod:: Marionette.get_url
|
||||
.. automethod:: Marionette.go_back
|
||||
.. automethod:: Marionette.go_forward
|
||||
.. automethod:: Marionette.refresh
|
||||
.. automethod:: Marionette.absolute_url
|
||||
.. automethod:: Marionette.get_window_type
|
||||
|
||||
DOM Element Methods
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
.. automethod:: Marionette.set_search_timeout
|
||||
.. automethod:: Marionette.find_element
|
||||
.. automethod:: Marionette.find_elements
|
||||
|
||||
Script Execution
|
||||
^^^^^^^^^^^^^^^^
|
||||
.. automethod:: Marionette.execute_script
|
||||
.. automethod:: Marionette.execute_async_script
|
||||
.. automethod:: Marionette.set_script_timeout
|
||||
|
||||
Debugging
|
||||
^^^^^^^^^
|
||||
.. autoattribute:: Marionette.page_source
|
||||
.. automethod:: Marionette.log
|
||||
.. automethod:: Marionette.get_logs
|
||||
.. automethod:: Marionette.screenshot
|
||||
|
||||
Querying and Modifying Document Content
|
||||
---------------------------------------
|
||||
.. autoclass:: HTMLElement
|
||||
:members:
|
||||
|
||||
.. autoclass:: DateTimeValue
|
||||
:members:
|
||||
|
||||
Action Objects
|
||||
--------------
|
||||
|
||||
Action Sequences
|
||||
^^^^^^^^^^^^^^^^
|
||||
.. autoclass:: Actions
|
||||
:members:
|
||||
|
||||
Multi-action Sequences
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
.. autoclass:: MultiActions
|
||||
:members:
|
||||
|
||||
Explicit Waiting and Expected Conditions
|
||||
----------------------------------------
|
||||
|
||||
Waits are used to pause program execution
|
||||
until a given condition is true.
|
||||
This is a useful technique to employ
|
||||
when documents load new content or change
|
||||
after ``Document.readyState``'s value changes to "complete".
|
||||
|
||||
Because Marionette returns control to the user
|
||||
when the document is completely loaded,
|
||||
any subsequent interaction with elements
|
||||
are subject to manual synchronisation.
|
||||
The reason for this is that Marionette
|
||||
does not keep a direct representation of the DOM,
|
||||
but instead exposes a way for the user to
|
||||
query the browser's DOM state.
|
||||
|
||||
The `Wait` helper class provided by Marionette
|
||||
avoids some of the caveats of ``time.sleep(n)``,
|
||||
which sets the condition to an exact time period to wait.
|
||||
It will return immediately
|
||||
once the provided condition evaluates to true.
|
||||
|
||||
In addition to writing your own custom conditions
|
||||
you can combine `Wait`
|
||||
with a number of ready-made expected conditions
|
||||
that are listed below.
|
||||
|
||||
Waits
|
||||
^^^^^
|
||||
.. autoclass:: marionette.wait.Wait
|
||||
:members:
|
||||
:special-members:
|
||||
.. autoattribute marionette.wait.DEFAULT_TIMEOUT
|
||||
.. autoattribute marionette.wait.DEFAULT_INTERVAL
|
||||
|
||||
Expected Conditions
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
.. automodule:: marionette.expected
|
||||
:members:
|
||||
.. include:: basics.rst
|
||||
|
||||
Indices and tables
|
||||
==================
|
||||
------------------
|
||||
|
||||
* :ref:`genindex`
|
||||
* :ref:`modindex`
|
||||
* :ref:`search`
|
||||
|
||||
.. toctree::
|
||||
:hidden:
|
||||
|
||||
Getting Started <basics>
|
||||
Interactive Tutorial <interactive>
|
||||
advanced/landing
|
||||
reference
|
||||
|
55
testing/marionette/client/docs/interactive.rst
Normal file
55
testing/marionette/client/docs/interactive.rst
Normal file
@ -0,0 +1,55 @@
|
||||
Using the Client Interactively
|
||||
==============================
|
||||
|
||||
Once you installed the client and have Marionette running, you can fire
|
||||
up your favourite interactive python environment and start playing with
|
||||
Marionette. Let's use a typical python shell:
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
python
|
||||
|
||||
First, import Marionette:
|
||||
|
||||
.. parsed-literal::
|
||||
from marionette import Marionette
|
||||
|
||||
Now create the client for this session. Assuming you're using the default
|
||||
port on a Marionette instance running locally:
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
client = Marionette(host='localhost', port=2828)
|
||||
client.start_session()
|
||||
|
||||
This will return some id representing your session id. Now that you've
|
||||
established a connection, let's start doing interesting things:
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
client.execute_script("alert('o hai there!');")
|
||||
|
||||
You should now see this alert pop up! How exciting! Okay, let's do
|
||||
something practical. Close the dialog and try this:
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
client.navigate("http://www.mozilla.org")
|
||||
|
||||
Now you're at mozilla.org! You can even verify it using the following:
|
||||
|
||||
.. parsed-literal::
|
||||
client.get_url()
|
||||
|
||||
You can even find an element and click on it. Let's say you want to get
|
||||
the first link:
|
||||
|
||||
.. parsed-literal::
|
||||
from marionette import By
|
||||
first_link = client.find_element(By.TAG_NAME, "a")
|
||||
|
||||
first_link now holds a reference to the first link on the page. You can click it:
|
||||
|
||||
.. parsed-literal::
|
||||
first_link.click()
|
||||
|
43
testing/marionette/client/docs/reference.rst
Normal file
43
testing/marionette/client/docs/reference.rst
Normal file
@ -0,0 +1,43 @@
|
||||
=============
|
||||
API Reference
|
||||
=============
|
||||
.. py:currentmodule:: marionette
|
||||
|
||||
Marionette
|
||||
----------
|
||||
.. autoclass:: Marionette
|
||||
:members:
|
||||
|
||||
HTMLElement
|
||||
-----------
|
||||
.. autoclass:: HTMLElement
|
||||
:members:
|
||||
|
||||
DateTimeValue
|
||||
-------------
|
||||
.. autoclass:: DateTimeValue
|
||||
:members:
|
||||
|
||||
Actions
|
||||
-------
|
||||
.. autoclass:: Actions
|
||||
:members:
|
||||
|
||||
MultiActions
|
||||
------------
|
||||
.. autoclass:: MultiActions
|
||||
:members:
|
||||
|
||||
Wait
|
||||
----
|
||||
|
||||
.. autoclass:: Wait
|
||||
:members:
|
||||
:special-members:
|
||||
.. autoattribute marionette.wait.DEFAULT_TIMEOUT
|
||||
.. autoattribute marionette.wait.DEFAULT_INTERVAL
|
||||
|
||||
Built-in Conditions
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
.. automodule:: marionette.expected
|
||||
:members:
|
@ -125,7 +125,7 @@ class Wait(object):
|
||||
except self.exceptions as e:
|
||||
last_exc = sys.exc_info()
|
||||
|
||||
if isinstance(rv, bool) and not rv:
|
||||
if not rv:
|
||||
self.clock.sleep(self.interval)
|
||||
continue
|
||||
|
||||
|
@ -16,6 +16,10 @@
|
||||
#include "nsSetDllDirectory.h"
|
||||
#endif
|
||||
|
||||
#if defined(MOZ_METRO) || defined(__GNUC__)
|
||||
#define XRE_DONT_SUPPORT_XPSP2
|
||||
#endif
|
||||
|
||||
#ifndef XRE_DONT_SUPPORT_XPSP2
|
||||
#include "WindowsCrtPatch.h"
|
||||
#endif
|
||||
@ -80,7 +84,7 @@ FreeAllocStrings(int argc, char **argv)
|
||||
|
||||
int wmain(int argc, WCHAR **argv)
|
||||
{
|
||||
#if !defined(XRE_DONT_SUPPORT_XPSP2) && !defined(MOZ_METRO)
|
||||
#if !defined(XRE_DONT_SUPPORT_XPSP2)
|
||||
WindowsCrtPatch::Init();
|
||||
#endif
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user