Merge mozilla-central to autoland. a=merge CLOSED TREE

This commit is contained in:
Noemi Erli 2018-04-19 12:58:43 +03:00
commit 7e6ef531b7
59 changed files with 1332 additions and 74722 deletions

View File

@ -10,6 +10,7 @@ const Services = require("Services");
const {HTMLTooltip} = require("devtools/client/shared/widgets/tooltip/HTMLTooltip");
const EventEmitter = require("devtools/shared/event-emitter");
const {PrefObserver} = require("devtools/client/shared/prefs");
const {colorUtils} = require("devtools/shared/css/color");
let itemIdCounter = 0;
/**
@ -461,8 +462,15 @@ AutocompletePopup.prototype = {
if (item.postLabel) {
let postDesc = this._document.createElementNS(HTML_NS, "span");
postDesc.textContent = item.postLabel;
postDesc.className = "autocomplete-postlabel";
postDesc.textContent = item.postLabel;
// Determines if the postlabel is a valid colour or other value
if (this._isValidColor(item.postLabel)) {
let colorSwatch = this._document.createElementNS(HTML_NS, "span");
colorSwatch.className = "autocomplete-swatch autocomplete-colorswatch";
colorSwatch.style.cssText = "background-color: " + item.postLabel;
postDesc.insertBefore(colorSwatch, postDesc.childNodes[0]);
}
listItem.appendChild(postDesc);
}
@ -603,6 +611,18 @@ AutocompletePopup.prototype = {
this._currentTheme = newValue;
},
/**
* Determines if the specified colour object is a valid colour, and if
* it is not a "special value"
*
* @return {Boolean}
* If the object represents a proper colour or not.
*/
_isValidColor: function(color) {
let colorObj = new colorUtils.CssColor(color);
return (colorObj.valid && (!colorObj.specialValue));
},
/**
* Used by tests.
*/

View File

@ -15,6 +15,8 @@ loadHelperScript("helper_inplace_editor.js");
// Also using a mocked list of CSS properties to avoid autocompletion when
// typing in "var"
// Used for representing the expectation of a visible color swatch
const COLORSWATCH = true;
// format :
// [
// what key to press,
@ -22,28 +24,40 @@ loadHelperScript("helper_inplace_editor.js");
// selected suggestion index (-1 if popup is hidden),
// number of suggestions in the popup (0 if popup is hidden),
// expected post label corresponding with the input box value,
// boolean representing if there should be a colour swatch visible,
// ]
const testData = [
["v", "v", -1, 0, null],
["a", "va", -1, 0, null],
["r", "var", -1, 0, null],
["(", "var()", -1, 0, null],
["-", "var(--abc)", 0, 4, "blue"],
["VK_BACK_SPACE", "var(-)", -1, 0, null],
["-", "var(--abc)", 0, 4, "blue"],
["VK_DOWN", "var(--def)", 1, 4, "red"],
["VK_DOWN", "var(--ghi)", 2, 4, "green"],
["VK_DOWN", "var(--jkl)", 3, 4, "yellow"],
["VK_DOWN", "var(--abc)", 0, 4, "blue"],
["VK_DOWN", "var(--def)", 1, 4, "red"],
["VK_LEFT", "var(--def)", -1, 0, null],
["v", "v", -1, 0, null, !COLORSWATCH],
["a", "va", -1, 0, null, !COLORSWATCH],
["r", "var", -1, 0, null, !COLORSWATCH],
["(", "var()", -1, 0, null, !COLORSWATCH],
["-", "var(--abc)", 0, 9, "inherit", !COLORSWATCH],
["VK_BACK_SPACE", "var(-)", -1, 0, null, !COLORSWATCH],
["-", "var(--abc)", 0, 9, "inherit", !COLORSWATCH],
["VK_DOWN", "var(--def)", 1, 9, "transparent", !COLORSWATCH],
["VK_DOWN", "var(--ghi)", 2, 9, "#00FF00", COLORSWATCH],
["VK_DOWN", "var(--jkl)", 3, 9, "rgb(255, 0, 0)", COLORSWATCH],
["VK_DOWN", "var(--mno)", 4, 9, "hsl(120, 60%, 70%)", COLORSWATCH],
["VK_DOWN", "var(--pqr)", 5, 9, "BlueViolet", COLORSWATCH],
["VK_DOWN", "var(--stu)", 6, 9, "15px", !COLORSWATCH],
["VK_DOWN", "var(--vwx)", 7, 9, "rgba(255, 0, 0, 0.4)", COLORSWATCH],
["VK_DOWN", "var(--yz)", 8, 9, "hsla(120, 60%, 70%, 0.3)", COLORSWATCH],
["VK_DOWN", "var(--abc)", 0, 9, "inherit", !COLORSWATCH],
["VK_DOWN", "var(--def)", 1, 9, "transparent", !COLORSWATCH],
["VK_DOWN", "var(--ghi)", 2, 9, "#00FF00", COLORSWATCH],
["VK_LEFT", "var(--ghi)", -1, 0, null, !COLORSWATCH],
];
const CSS_VARIABLES = [
["--abc", "blue"],
["--def", "red"],
["--ghi", "green"],
["--jkl", "yellow"]
["--abc", "inherit"],
["--def", "transparent"],
["--ghi", "#00FF00"],
["--jkl", "rgb(255, 0, 0)"],
["--mno", "hsl(120, 60%, 70%)"],
["--pqr", "BlueViolet"],
["--stu", "15px"],
["--vwx", "rgba(255, 0, 0, 0.4)"],
["--yz", "hsla(120, 60%, 70%, 0.3)"],
];
const mockGetCSSValuesForPropertyName = function(propertyName) {

View File

@ -11,6 +11,7 @@
const HTML_NS = "http://www.w3.org/1999/xhtml";
const { editableField } = require("devtools/client/shared/inplace-editor");
const {colorUtils} = require("devtools/shared/css/color");
/**
* Create an inplace editor linked to a span element and click on the span to
@ -73,10 +74,12 @@ function createSpan(doc) {
* - {Number} index, the index of the selected suggestion in the popup
* - {Number} total, the total number of suggestions in the popup
* - {String} postLabel, the expected post label for the selected suggestion
* - {Boolean} colorSwatch, if there is a swatch of color expected to be visible
* @param {InplaceEditor} editor
* The InplaceEditor instance being tested
*/
async function testCompletion([key, completion, index, total, postLabel], editor) {
async function testCompletion([key, completion, index, total,
postLabel, colorSwatch], editor) {
info("Pressing key " + key);
info("Expecting " + completion);
@ -112,6 +115,21 @@ async function testCompletion([key, completion, index, total, postLabel], editor
let selectedElement = editor.popup.elements.get(selectedItem);
ok(selectedElement.textContent.includes(postLabel),
"Selected popup element contains the expected post-label");
// Determines if there is a color swatch attached to the label
// and if the color swatch's background color matches the post label
let swatchSpan = selectedElement.getElementsByClassName(
"autocomplete-swatch autocomplete-colorswatch");
if (colorSwatch) {
ok(swatchSpan.length === 1, "Displayed the expected color swatch");
let color = new colorUtils.CssColor(swatchSpan[0].style.backgroundColor);
let swatchColor = color.rgba;
color.newColor(postLabel);
let postColor = color.rgba;
ok(swatchColor == postColor, "Color swatch matches postLabel value");
} else {
ok(swatchSpan.length === 0, "As expected no swatches were available");
}
}
if (total === 0) {

View File

@ -108,6 +108,35 @@ html|button, html|select {
text-align: end;
}
.devtools-autocomplete-listbox .autocomplete-swatch {
cursor: pointer;
border-radius: 50%;
width: 1em;
height: 1em;
vertical-align: middle;
/* align the swatch with its value */
margin-top: -1px;
margin-inline-end: 5px;
display: inline-block;
position: relative;
}
.devtools-autocomplete-listbox .autocomplete-colorswatch::before {
content: '';
background-color: #eee;
background-image: linear-gradient(45deg, #ccc 25%, transparent 25%, transparent 75%, #ccc 75%, #ccc),
linear-gradient(45deg, #ccc 25%, transparent 25%, transparent 75%, #ccc 75%, #ccc);
background-size: 12px 12px;
background-position: 0 0, 6px 6px;
position: absolute;
border-radius: 50%;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: -1;
}
/* Rest of the dark and light theme */
.devtools-autocomplete-popup,

View File

@ -173,15 +173,9 @@ StructuredCloneBlob::WriteStructuredClone(JSContext* aCx, JSStructuredCloneWrite
aHolder->BlobImpls().AppendElements(BlobImpls());
auto iter = data.Iter();
while (!iter.Done()) {
if (!JS_WriteBytes(aWriter, iter.Data(), iter.RemainingInSegment())) {
return false;
}
iter.Advance(data, iter.RemainingInSegment());
}
return true;
return data.ForEachDataChunk([&](const char* aData, size_t aSize) {
return JS_WriteBytes(aWriter, aData, aSize);
});
}
bool

View File

@ -4771,6 +4771,10 @@ nsIDocument::SetContainer(nsDocShell* aContainer)
}
EnumerateActivityObservers(NotifyActivityChanged, nullptr);
// IsTopLevelWindowInactive depends on the docshell, so
// update the cached value now that it's available.
UpdateDocumentStates(NS_DOCUMENT_STATE_WINDOW_INACTIVE);
if (!aContainer) {
return;
}

View File

@ -137,7 +137,7 @@ nsStructuredCloneContainer::GetDataAsBase64(nsAString &aOut)
return NS_ERROR_FAILURE;
}
auto iter = Data().Iter();
auto iter = Data().Start();
size_t size = Data().Size();
nsAutoCString binaryData;
binaryData.SetLength(size);

View File

@ -8314,12 +8314,12 @@ class ObjectStoreAddOrPutRequestOp::SCInputStream final
: public nsIInputStream
{
const JSStructuredCloneData& mData;
JSStructuredCloneData::IterImpl mIter;
JSStructuredCloneData::Iterator mIter;
public:
explicit SCInputStream(const JSStructuredCloneData& aData)
: mData(aData)
, mIter(aData.Iter())
, mIter(aData.Start())
{ }
private:
@ -19695,7 +19695,7 @@ DatabaseOperationBase::GetStructuredCloneReadInfoFromBlob(
return NS_ERROR_FILE_CORRUPTED;
}
if (!aInfo->mData.WriteBytes(uncompressedBuffer, uncompressed.Length())) {
if (!aInfo->mData.AppendBytes(uncompressedBuffer, uncompressed.Length())) {
return NS_ERROR_OUT_OF_MEMORY;
}
@ -19780,7 +19780,7 @@ DatabaseOperationBase::GetStructuredCloneReadInfoFromExternalBlob(
break;
}
if (NS_WARN_IF(!aInfo->mData.WriteBytes(buffer, numRead))) {
if (NS_WARN_IF(!aInfo->mData.AppendBytes(buffer, numRead))) {
rv = NS_ERROR_OUT_OF_MEMORY;
break;
}
@ -26181,18 +26181,9 @@ ObjectStoreAddOrPutRequestOp::DoDatabaseWork(DatabaseConnection* aConnection)
char keyPropBuffer[keyPropSize];
LittleEndian::writeUint64(keyPropBuffer, keyPropValue);
auto iter = cloneData.Iter();
DebugOnly<bool> result =
iter.AdvanceAcrossSegments(cloneData, cloneInfo.offsetToKeyProp());
MOZ_ASSERT(result);
for (char index : keyPropBuffer) {
char* keyPropPointer = iter.Data();
*keyPropPointer = index;
result = iter.AdvanceAcrossSegments(cloneData, 1);
MOZ_ASSERT(result);
}
auto iter = cloneData.Start();
MOZ_ALWAYS_TRUE(cloneData.Advance(iter, cloneInfo.offsetToKeyProp()));
MOZ_ALWAYS_TRUE(cloneData.UpdateBytes(iter, keyPropBuffer, keyPropSize));
}
}
@ -26218,7 +26209,7 @@ ObjectStoreAddOrPutRequestOp::DoDatabaseWork(DatabaseConnection* aConnection)
} else {
nsCString flatCloneData;
flatCloneData.SetLength(cloneDataSize);
auto iter = cloneData.Iter();
auto iter = cloneData.Start();
cloneData.ReadBytes(iter, flatCloneData.BeginWriting(), cloneDataSize);
// Compress the bytes before adding into the database.
@ -26478,7 +26469,7 @@ SCInputStream::ReadSegments(nsWriteSegmentFun aWriter,
*_retval += count;
aCount -= count;
mIter.Advance(mData, count);
mData.Advance(mIter, count);
}
return NS_OK;

View File

@ -148,7 +148,7 @@ BuildClonedMessageData(M* aManager, StructuredCloneData& aData,
ClonedMessageData& aClonedData)
{
SerializedStructuredCloneBuffer& buffer = aClonedData.data();
auto iter = aData.Data().Iter();
auto iter = aData.Data().Start();
size_t size = aData.Data().Size();
bool success;
buffer.data = aData.Data().Borrow<js::SystemAllocPolicy>(iter, size, &success);

View File

@ -55,7 +55,7 @@ public:
CreateFromExternalData(const char* aData, size_t aDataLength)
{
JSStructuredCloneData buf;
buf.WriteBytes(aData, aDataLength);
buf.AppendBytes(aData, aDataLength);
RefPtr<SharedJSAllocatedData> sharedData =
new SharedJSAllocatedData(Move(buf));
return sharedData.forget();
@ -65,11 +65,7 @@ public:
CreateFromExternalData(const JSStructuredCloneData& aData)
{
JSStructuredCloneData buf;
auto iter = aData.Iter();
while (!iter.Done()) {
buf.WriteBytes(iter.Data(), iter.RemainingInSegment());
iter.Advance(aData, iter.RemainingInSegment());
}
buf.Append(aData);
RefPtr<SharedJSAllocatedData> sharedData =
new SharedJSAllocatedData(Move(buf));
return sharedData.forget();
@ -238,7 +234,7 @@ public:
// StructuredCloneData instance is destroyed before aData is destroyed.
bool UseExternalData(const JSStructuredCloneData& aData)
{
auto iter = aData.Iter();
auto iter = aData.Start();
bool success = false;
mExternalData =
aData.Borrow<js::SystemAllocPolicy>(iter, aData.Size(), &success);

View File

@ -1777,7 +1777,7 @@ WorkerPrivate::Start()
// aCx is null when called from the finalizer
bool
WorkerPrivate::NotifyPrivate(WorkerStatus aStatus)
WorkerPrivate::Notify(WorkerStatus aStatus)
{
AssertIsOnParentThread();
@ -4345,7 +4345,7 @@ WorkerPrivate::AssertValidSyncLoop(nsIEventTarget* aSyncLoopTarget)
#endif
void
WorkerPrivate::PostMessageToParentInternal(
WorkerPrivate::PostMessageToParent(
JSContext* aCx,
JS::Handle<JS::Value> aMessage,
const Sequence<JSObject*>& aTransferable,

View File

@ -158,10 +158,7 @@ public:
// Called on the parent thread.
bool
Notify(WorkerStatus aStatus)
{
return NotifyPrivate(aStatus);
}
Notify(WorkerStatus aStatus);
bool
Cancel()
@ -178,8 +175,7 @@ public:
bool
Terminate()
{
AssertIsOnParentThread();
return TerminatePrivate();
return Notify(Terminating);
}
bool
@ -291,10 +287,7 @@ public:
PostMessageToParent(JSContext* aCx,
JS::Handle<JS::Value> aMessage,
const Sequence<JSObject*>& aTransferable,
ErrorResult& aRv)
{
PostMessageToParentInternal(aCx, aMessage, aTransferable, aRv);
}
ErrorResult& aRv);
void
PostMessageToParentMessagePort(JSContext* aCx,
@ -1219,15 +1212,6 @@ private:
~WorkerPrivate();
bool
NotifyPrivate(WorkerStatus aStatus);
bool
TerminatePrivate()
{
return NotifyPrivate(Terminating);
}
bool
MayContinueRunning()
{
@ -1279,12 +1263,6 @@ private:
void
WaitForWorkerEvents();
void
PostMessageToParentInternal(JSContext* aCx,
JS::Handle<JS::Value> aMessage,
const Sequence<JSObject*>& aTransferable,
ErrorResult& aRv);
// If the worker shutdown status is equal or greater then aFailStatus, this
// operation will fail and nullptr will be returned. See WorkerHolder.h for
// more information about the correct value to use.

View File

@ -78,8 +78,16 @@ static CTFontRef
CreateCTFontFromCGFontWithVariations(CGFontRef aCGFont, CGFloat aSize)
{
// Avoid calling potentially buggy variation APIs on pre-Sierra macOS
// versions (see bug 1331683)
if (!nsCocoaFeatures::OnSierraOrLater()) {
// versions (see bug 1331683).
//
// And on HighSierra, CTFontCreateWithGraphicsFont properly carries over
// variation settings from the CGFont to CTFont, so we don't need to do
// the extra work here -- and this seems to avoid Core Text crashiness
// seen in bug 1454094.
//
// So we only need to do this "the hard way" on Sierra; on other releases,
// just let the standard CTFont function do its thing.
if (!nsCocoaFeatures::OnSierraExactly()) {
return CTFontCreateWithGraphicsFont(aCGFont, aSize, nullptr, nullptr);
}

View File

@ -340,13 +340,23 @@ const cairo_font_face_backend_t _cairo_quartz_font_face_backend = {
static CTFontRef
CreateCTFontFromCGFontWithVariations(CGFontRef aCGFont, CGFloat aSize)
{
// Avoid calling potentially buggy variation APIs on pre-Sierra macOS
// versions (see bug 1331683)
// Declare helper provided by widget/cocoa/nsCocoaFeatures.mm
extern bool Gecko_OnSierraOrLater();
if (!Gecko_OnSierraOrLater()) {
extern bool Gecko_OnSierraExactly();
// Avoid calling potentially buggy variation APIs on pre-Sierra macOS
// versions (see bug 1331683).
//
// And on HighSierra, CTFontCreateWithGraphicsFont properly carries over
// variation settings from the CGFont to CTFont, so we don't need to do
// the extra work here -- and this seems to avoid Core Text crashiness
// seen in bug 1454094.
//
// So we only need to do this "the hard way" on Sierra; on other releases,
// just let the standard CTFont function do its thing.
if (!Gecko_OnSierraExactly()) {
return CTFontCreateWithGraphicsFont(aCGFont, aSize, NULL, NULL);
}
CFDictionaryRef vars = CGFontCopyVariations(aCGFont);
CTFontRef ctFont;
if (vars) {

View File

@ -95,7 +95,7 @@ struct BlobItemData
float mOpacity; // only used with nsDisplayOpacity items to detect change to opacity
IntRect mImageRect;
IntPoint mGroupOffset;
LayerIntPoint mGroupOffset;
BlobItemData(DIGroup* aGroup, nsDisplayItem *aItem)
: mGroup(aGroup)
@ -287,7 +287,7 @@ struct DIGroup
nsRect mGroupBounds;
int32_t mAppUnitsPerDevPixel;
gfx::Size mScale;
IntPoint mGroupOffset;
LayerIntRect mLayerBounds;
Maybe<wr::ImageKey> mKey;
DIGroup() : mAppUnitsPerDevPixel(0) {}
@ -316,9 +316,9 @@ struct DIGroup
}
static IntRect
ToDeviceSpace(nsRect aBounds, Matrix& aMatrix, int32_t aAppUnitsPerDevPixel, IntPoint aOffset)
ToDeviceSpace(nsRect aBounds, Matrix& aMatrix, int32_t aAppUnitsPerDevPixel, LayerIntPoint aOffset)
{
return RoundedOut(aMatrix.TransformBounds(ToRect(nsLayoutUtils::RectToGfxRect(aBounds, aAppUnitsPerDevPixel)))) - aOffset;
return RoundedOut(aMatrix.TransformBounds(ToRect(nsLayoutUtils::RectToGfxRect(aBounds, aAppUnitsPerDevPixel)))) - aOffset.ToUnknownPoint();
}
void ComputeGeometryChange(nsDisplayItem* aItem, BlobItemData* aData, Matrix& aMatrix, nsDisplayListBuilder* aBuilder)
@ -340,8 +340,7 @@ struct DIGroup
LayoutDeviceIntPoint offset = RoundedToInt(bounds.TopLeft());
GP("\n");
GP("CGC offset %d %d\n", offset.x, offset.y);
IntSize size = mGroupBounds.Size().ScaleToNearestPixels(mScale.width, mScale.height, appUnitsPerDevPixel);
//MOZ_RELEASE_ASSERT(mGroupOffset.x == offset.x && mGroupOffset.y == offset.y);
LayerIntSize size = mLayerBounds.Size();
IntRect imageRect(0, 0, size.width, size.height);
GP("imageSize: %d %d\n", size.width, size.height);
/*if (aItem->IsReused() && aData->mGeometry) {
@ -357,16 +356,16 @@ struct DIGroup
aData->mGeometry = Move(geometry);
nsRect bounds = combined.GetBounds();
IntRect transformedRect = ToDeviceSpace(combined.GetBounds(), aMatrix, appUnitsPerDevPixel, mGroupOffset);
IntRect transformedRect = ToDeviceSpace(combined.GetBounds(), aMatrix, appUnitsPerDevPixel, mLayerBounds.TopLeft());
aData->mRect = transformedRect.Intersect(imageRect);
GP("CGC %s %d %d %d %d\n", aItem->Name(), bounds.x, bounds.y, bounds.width, bounds.height);
GP("%d %d, %f %f\n", mGroupOffset.x, mGroupOffset.y, aMatrix._11, aMatrix._22);
GP("%d %d, %f %f\n", mLayerBounds.TopLeft().x, mLayerBounds.TopLeft().y, aMatrix._11, aMatrix._22);
GP("mRect %d %d %d %d\n", aData->mRect.x, aData->mRect.y, aData->mRect.width, aData->mRect.height);
InvalidateRect(aData->mRect);
aData->mInvalid = true;
} else if (/*aData->mIsInvalid || XXX: handle image load invalidation */ (aItem->IsInvalid(invalid) && invalid.IsEmpty())) {
MOZ_RELEASE_ASSERT(imageRect.IsEqualEdges(aData->mImageRect));
MOZ_RELEASE_ASSERT(mGroupOffset == aData->mGroupOffset);
MOZ_RELEASE_ASSERT(mLayerBounds.TopLeft() == aData->mGroupOffset);
UniquePtr<nsDisplayItemGeometry> geometry(aItem->AllocateGeometry(aBuilder));
/* Instead of doing this dance, let's just invalidate the old rect and the
* new rect.
@ -388,7 +387,7 @@ struct DIGroup
InvalidateRect(aData->mRect.Intersect(imageRect));
// We want to snap to outside pixels. When should we multiply by the matrix?
// XXX: TransformBounds is expensive. We should avoid doing it if we have no transform
IntRect transformedRect = ToDeviceSpace(combined.GetBounds(), aMatrix, appUnitsPerDevPixel, mGroupOffset);
IntRect transformedRect = ToDeviceSpace(combined.GetBounds(), aMatrix, appUnitsPerDevPixel, mLayerBounds.TopLeft());
aData->mRect = transformedRect.Intersect(imageRect);
InvalidateRect(aData->mRect);
GP("new rect: %d %d %d %d\n",
@ -399,7 +398,7 @@ struct DIGroup
aData->mInvalid = true;
} else {
MOZ_RELEASE_ASSERT(imageRect.IsEqualEdges(aData->mImageRect));
MOZ_RELEASE_ASSERT(mGroupOffset == aData->mGroupOffset);
MOZ_RELEASE_ASSERT(mLayerBounds.TopLeft() == aData->mGroupOffset);
GP("else invalidate: %s\n", aItem->Name());
aData->mGeometry->MoveBy(shift);
// this includes situations like reflow changing the position
@ -413,7 +412,7 @@ struct DIGroup
geometry ? geometry->ComputeInvalidationRegion() :
aData->mGeometry->ComputeInvalidationRegion(),
&combined);
IntRect transformedRect = ToDeviceSpace(combined.GetBounds(), aMatrix, appUnitsPerDevPixel, mGroupOffset);
IntRect transformedRect = ToDeviceSpace(combined.GetBounds(), aMatrix, appUnitsPerDevPixel, mLayerBounds.TopLeft());
IntRect invalidRect = transformedRect.Intersect(imageRect);
GP("combined not empty: mRect %d %d %d %d\n", invalidRect.x, invalidRect.y, invalidRect.width, invalidRect.height);
// invalidate the invalidated area.
@ -422,7 +421,7 @@ struct DIGroup
aData->mGeometry = Move(geometry);
combined = clip.ApplyNonRoundedIntersection(aData->mGeometry->ComputeInvalidationRegion());
transformedRect = ToDeviceSpace(combined.GetBounds(), aMatrix, appUnitsPerDevPixel, mGroupOffset);
transformedRect = ToDeviceSpace(combined.GetBounds(), aMatrix, appUnitsPerDevPixel, mLayerBounds.TopLeft());
aData->mRect = transformedRect.Intersect(imageRect);
aData->mInvalid = true;
@ -438,7 +437,7 @@ struct DIGroup
aData->mGeometry = Move(geometry);
}
combined = clip.ApplyNonRoundedIntersection(aData->mGeometry->ComputeInvalidationRegion());
IntRect transformedRect = ToDeviceSpace(combined.GetBounds(), aMatrix, appUnitsPerDevPixel, mGroupOffset);
IntRect transformedRect = ToDeviceSpace(combined.GetBounds(), aMatrix, appUnitsPerDevPixel, mLayerBounds.TopLeft());
InvalidateRect(aData->mRect.Intersect(imageRect));
aData->mRect = transformedRect.Intersect(imageRect);
InvalidateRect(aData->mRect);
@ -462,7 +461,7 @@ struct DIGroup
aData->mGeometry = Move(geometry);
}
combined = clip.ApplyNonRoundedIntersection(aData->mGeometry->ComputeInvalidationRegion());
IntRect transformedRect = ToDeviceSpace(combined.GetBounds(), aMatrix, appUnitsPerDevPixel, mGroupOffset);
IntRect transformedRect = ToDeviceSpace(combined.GetBounds(), aMatrix, appUnitsPerDevPixel, mLayerBounds.TopLeft());
InvalidateRect(aData->mRect.Intersect(imageRect));
aData->mRect = transformedRect.Intersect(imageRect);
InvalidateRect(aData->mRect);
@ -476,7 +475,7 @@ struct DIGroup
UpdateContainerLayerPropertiesAndDetectChange(aItem, aData)) {
combined = clip.ApplyNonRoundedIntersection(geometry->ComputeInvalidationRegion());
aData->mGeometry = Move(geometry);
IntRect transformedRect = ToDeviceSpace(combined.GetBounds(), aMatrix, appUnitsPerDevPixel, mGroupOffset);
IntRect transformedRect = ToDeviceSpace(combined.GetBounds(), aMatrix, appUnitsPerDevPixel, mLayerBounds.TopLeft());
InvalidateRect(aData->mRect.Intersect(imageRect));
aData->mRect = transformedRect.Intersect(imageRect);
InvalidateRect(aData->mRect);
@ -484,7 +483,7 @@ struct DIGroup
} else {
// XXX: this code can eventually be deleted/made debug only
combined = clip.ApplyNonRoundedIntersection(geometry->ComputeInvalidationRegion());
IntRect transformedRect = ToDeviceSpace(combined.GetBounds(), aMatrix, appUnitsPerDevPixel, mGroupOffset);
IntRect transformedRect = ToDeviceSpace(combined.GetBounds(), aMatrix, appUnitsPerDevPixel, mLayerBounds.TopLeft());
auto rect = transformedRect.Intersect(imageRect);
MOZ_RELEASE_ASSERT(rect.IsEqualEdges(aData->mRect));
GP("Layer NoChange: %s %d %d %d %d\n", aItem->Name(),
@ -494,7 +493,7 @@ struct DIGroup
// XXX: this code can eventually be deleted/made debug only
UniquePtr<nsDisplayItemGeometry> geometry(aItem->AllocateGeometry(aBuilder));
combined = clip.ApplyNonRoundedIntersection(geometry->ComputeInvalidationRegion());
IntRect transformedRect = ToDeviceSpace(combined.GetBounds(), aMatrix, appUnitsPerDevPixel, mGroupOffset);
IntRect transformedRect = ToDeviceSpace(combined.GetBounds(), aMatrix, appUnitsPerDevPixel, mLayerBounds.TopLeft());
auto rect = transformedRect.Intersect(imageRect);
MOZ_RELEASE_ASSERT(rect.IsEqualEdges(aData->mRect));
GP("NoChange: %s %d %d %d %d\n", aItem->Name(),
@ -504,7 +503,7 @@ struct DIGroup
}
aData->mClip = clip;
aData->mMatrix = aMatrix;
aData->mGroupOffset = mGroupOffset;
aData->mGroupOffset = mLayerBounds.TopLeft();
aData->mImageRect = imageRect;
GP("post mInvalidRect: %d %d %d %d\n", mInvalidRect.x, mInvalidRect.y, mInvalidRect.width, mInvalidRect.height);
}
@ -632,7 +631,7 @@ struct DIGroup
nsDisplayItem* aEndItem,
gfxContext* aContext,
gfx::DrawEventRecorderMemory* aRecorder) {
IntSize size = mGroupBounds.Size().ScaleToNearestPixels(mScale.width, mScale.height, aGrouper->mAppUnitsPerDevPixel);
LayerIntSize size = mLayerBounds.Size();
for (nsDisplayItem* item = aStartItem; item != aEndItem; item = item->GetAbove()) {
IntRect bounds = ItemBounds(item);
auto bottomRight = bounds.BottomRight();
@ -916,15 +915,17 @@ Grouper::ConstructGroups(WebRenderCommandBuilder* aCommandBuilder,
GP("Inner group size change\n");
groupData->mFollowingGroup.ClearItems();
if (groupData->mFollowingGroup.mKey) {
IntSize size = currentGroup->mGroupBounds.Size().ScaleToNearestPixels(currentGroup->mScale.width, currentGroup->mScale.height, mAppUnitsPerDevPixel);
groupData->mFollowingGroup.mInvalidRect = IntRect(IntPoint(0, 0), size);
LayerIntRect layerBounds = LayerIntRect::FromUnknownRect(currentGroup->mGroupBounds.ScaleToOutsidePixels(currentGroup->mScale.width,
currentGroup->mScale.height,
mAppUnitsPerDevPixel));
groupData->mFollowingGroup.mInvalidRect = IntRect(IntPoint(0, 0), layerBounds.Size().ToUnknownSize());
aCommandBuilder->mManager->AddImageKeyForDiscard(groupData->mFollowingGroup.mKey.value());
groupData->mFollowingGroup.mKey = Nothing();
}
}
groupData->mFollowingGroup.mGroupBounds = currentGroup->mGroupBounds;
groupData->mFollowingGroup.mAppUnitsPerDevPixel = currentGroup->mAppUnitsPerDevPixel;
groupData->mFollowingGroup.mGroupOffset = currentGroup->mGroupOffset;
groupData->mFollowingGroup.mLayerBounds = currentGroup->mLayerBounds;
groupData->mFollowingGroup.mScale = currentGroup->mScale;
currentGroup = &groupData->mFollowingGroup;
@ -1059,7 +1060,7 @@ WebRenderCommandBuilder::DoGroupingForDisplayList(nsDisplayList* aList,
group.mAppUnitsPerDevPixel = appUnitsPerDevPixel;
group.mGroupBounds = groupBounds;
group.mScale = scale;
group.mGroupOffset = group.mGroupBounds.TopLeft().ScaleToNearestPixels(scale.width, scale.height, g.mAppUnitsPerDevPixel);
group.mLayerBounds = LayerIntRect::FromUnknownRect(group.mGroupBounds.ScaleToOutsidePixels(scale.width, scale.height, group.mAppUnitsPerDevPixel));
group.mAnimatedGeometryRootOrigin = group.mGroupBounds.TopLeft();
g.ConstructGroups(this, aBuilder, aResources, &group, aList, aSc);
mScrollingHelper.EndList(aSc);

View File

@ -756,7 +756,7 @@ private:
// work with non-system fonts. As a result, create the strike specific CTFonts from the underlying
// CGFont.
#ifdef MOZ_SKIA
extern "C" bool Gecko_OnSierraOrLater();
extern "C" bool Gecko_OnSierraExactly();
#endif
static UniqueCFRef<CTFontRef> ctfont_create_exact_copy(CTFontRef baseFont, CGFloat textSize,
const CGAffineTransform* transform)
@ -774,8 +774,16 @@ static UniqueCFRef<CTFontRef> ctfont_create_exact_copy(CTFontRef baseFont, CGFlo
#ifdef MOZ_SKIA
// Avoid calling potentially buggy variation APIs on pre-Sierra macOS
// versions (see bug 1331683)
if (Gecko_OnSierraOrLater())
// versions (see bug 1331683).
//
// And on HighSierra, CTFontCreateWithGraphicsFont properly carries over
// variation settings from the CGFont to CTFont, so we don't need to do
// the extra work here -- and this seems to avoid Core Text crashiness
// seen in bug 1454094.
//
// So we only need to do this "the hard way" on Sierra; on other releases,
// just let the standard CTFont function do its thing.
if (Gecko_OnSierraExactly())
#endif
{
// Not UniqueCFRef<> because CGFontCopyVariations can return null!

View File

@ -424,8 +424,16 @@ gfxMacFont::CreateCTFontFromCGFontWithVariations(CGFontRef aCGFont,
CTFontDescriptorRef aFontDesc)
{
// Avoid calling potentially buggy variation APIs on pre-Sierra macOS
// versions (see bug 1331683)
if (!nsCocoaFeatures::OnSierraOrLater()) {
// versions (see bug 1331683).
//
// And on HighSierra, CTFontCreateWithGraphicsFont properly carries over
// variation settings from the CGFont to CTFont, so we don't need to do
// the extra work here -- and this seems to avoid Core Text crashiness
// seen in bug 1454094.
//
// So we only need to do this "the hard way" on Sierra; on other releases,
// just let the standard CTFont function do its thing.
if (!nsCocoaFeatures::OnSierraExactly()) {
return CTFontCreateWithGraphicsFont(aCGFont, aSize, nullptr, aFontDesc);
}

View File

@ -78,11 +78,7 @@ struct SerializedStructuredCloneBuffer final
operator=(const SerializedStructuredCloneBuffer& aOther)
{
data.Clear();
auto iter = aOther.data.Iter();
while (!iter.Done()) {
data.WriteBytes(iter.Data(), iter.RemainingInSegment());
iter.Advance(aOther.data, iter.RemainingInSegment());
}
data.Append(aOther.data);
return *this;
}
@ -837,11 +833,9 @@ struct ParamTraits<JSStructuredCloneData>
{
MOZ_ASSERT(!(aParam.Size() % sizeof(uint64_t)));
WriteParam(aMsg, aParam.Size());
auto iter = aParam.Iter();
while (!iter.Done()) {
aMsg->WriteBytes(iter.Data(), iter.RemainingInSegment(), sizeof(uint64_t));
iter.Advance(aParam, iter.RemainingInSegment());
}
aParam.ForEachDataChunk([&](const char* aData, size_t aSize) {
return aMsg->WriteBytes(aData, aSize, sizeof(uint64_t));
});
}
static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)

View File

@ -10,6 +10,7 @@
#include "mozilla/Attributes.h"
#include "mozilla/BufferList.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/Move.h"
#include <stdint.h>
@ -339,52 +340,138 @@ namespace js
private:
js::Vector<js::SharedArrayRawBuffer*, 0, js::SystemAllocPolicy> refs_;
};
template <typename T, typename AllocPolicy> struct BufferIterator;
}
class MOZ_NON_MEMMOVABLE JS_PUBLIC_API(JSStructuredCloneData) :
public mozilla::BufferList<js::SystemAllocPolicy>
{
typedef js::SystemAllocPolicy AllocPolicy;
typedef mozilla::BufferList<js::SystemAllocPolicy> BufferList;
/**
* JSStructuredCloneData represents structured clone data together with the
* information needed to read/write/transfer/free the records within it, in the
* form of a set of callbacks.
*/
class MOZ_NON_MEMMOVABLE JS_PUBLIC_API(JSStructuredCloneData) {
public:
using BufferList = mozilla::BufferList<js::SystemAllocPolicy>;
using Iterator = BufferList::IterImpl;
static const size_t kInitialSize = 0;
static const size_t kInitialCapacity = 4096;
private:
static const size_t kStandardCapacity = 4096;
BufferList bufList_;
const JSStructuredCloneCallbacks* callbacks_ = nullptr;
void* closure_ = nullptr;
OwnTransferablePolicy ownTransferables_ = OwnTransferablePolicy::NoTransferables;
js::SharedArrayRawBufferRefs refsHeld_;
void setOptionalCallbacks(const JSStructuredCloneCallbacks* callbacks,
void* closure,
OwnTransferablePolicy policy) {
callbacks_ = callbacks;
closure_ = closure;
ownTransferables_ = policy;
}
friend struct JSStructuredCloneWriter;
friend class JS_PUBLIC_API(JSAutoStructuredCloneBuffer);
template <typename T, typename AllocPolicy> friend struct js::BufferIterator;
public:
explicit JSStructuredCloneData(AllocPolicy aAP = AllocPolicy())
: BufferList(kInitialSize, kInitialCapacity, kStandardCapacity, aAP)
public:
// The constructor must be infallible but SystemAllocPolicy is not, so both
// the initial size and initial capacity of the BufferList must be zero.
explicit JSStructuredCloneData()
: bufList_(0, 0, kStandardCapacity, js::SystemAllocPolicy())
, callbacks_(nullptr)
, closure_(nullptr)
, ownTransferables_(OwnTransferablePolicy::NoTransferables)
{}
MOZ_IMPLICIT JSStructuredCloneData(BufferList&& buffers)
: BufferList(Move(buffers))
: bufList_(mozilla::Move(buffers))
, callbacks_(nullptr)
, closure_(nullptr)
, ownTransferables_(OwnTransferablePolicy::NoTransferables)
{}
JSStructuredCloneData(JSStructuredCloneData&& other) = default;
JSStructuredCloneData& operator=(JSStructuredCloneData&& other) = default;
~JSStructuredCloneData();
~JSStructuredCloneData() { discardTransferables(); }
using BufferList::BufferList;
void setCallbacks(const JSStructuredCloneCallbacks* callbacks,
void* closure,
OwnTransferablePolicy policy)
{
callbacks_ = callbacks;
closure_ = closure;
ownTransferables_ = policy;
}
bool Init(size_t initialCapacity = 0) { return bufList_.Init(0, initialCapacity); }
size_t Size() const { return bufList_.Size(); }
const Iterator Start() const { return bufList_.Iter(); }
bool Advance(Iterator& iter, size_t distance) const {
return iter.AdvanceAcrossSegments(bufList_, distance);
}
bool ReadBytes(Iterator& iter, char* buffer, size_t size) const {
return bufList_.ReadBytes(iter, buffer, size);
}
// Append new data to the end of the buffer.
bool AppendBytes(const char* data, size_t size) {
return bufList_.WriteBytes(data, size);
}
// Update data stored within the existing buffer. There must be at least
// 'size' bytes between the position of 'iter' and the end of the buffer.
bool UpdateBytes(Iterator& iter, const char* data, size_t size) const {
while (size > 0) {
size_t remaining = iter.RemainingInSegment();
size_t nbytes = std::min(remaining, size);
memcpy(iter.Data(), data, nbytes);
data += nbytes;
size -= nbytes;
iter.Advance(bufList_, nbytes);
}
return true;
}
char* AllocateBytes(size_t maxSize, size_t* size) {
return bufList_.AllocateBytes(maxSize, size);
}
void Clear() {
discardTransferables();
bufList_.Clear();
}
template<typename BorrowingAllocPolicy>
mozilla::BufferList<BorrowingAllocPolicy> Borrow(Iterator& iter, size_t size, bool* success,
BorrowingAllocPolicy ap = BorrowingAllocPolicy()) const
{
return bufList_.Borrow<BorrowingAllocPolicy>(iter, size, success);
}
// Iterate over all contained data, one BufferList segment's worth at a
// time, and invoke the given FunctionToApply with the data pointer and
// size. The function should return a bool value, and this loop will exit
// with false if the function ever returns false.
template <typename FunctionToApply>
bool ForEachDataChunk(FunctionToApply&& function) const {
Iterator iter = bufList_.Iter();
while (!iter.Done()) {
if (!function(iter.Data(), iter.RemainingInSegment()))
return false;
iter.Advance(bufList_, iter.RemainingInSegment());
}
return true;
}
// Append the entire contents of other's bufList_ to our own.
bool Append(const JSStructuredCloneData& other) {
return other.ForEachDataChunk([&](const char* data, size_t size) {
return AppendBytes(data, size);
});
}
size_t SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) {
return bufList_.SizeOfExcludingThis(mallocSizeOf);
}
void discardTransferables();
};
/**
@ -420,7 +507,18 @@ JS_PUBLIC_API(bool)
JS_StructuredClone(JSContext* cx, JS::HandleValue v, JS::MutableHandleValue vp,
const JSStructuredCloneCallbacks* optionalCallbacks, void* closure);
/** RAII sugar for JS_WriteStructuredClone. */
/**
* The C-style API calls to read and write structured clones are fragile --
* they rely on the caller to properly handle ownership of the clone data, and
* the handling of the input data as well as the interpretation of the contents
* of the clone buffer are dependent on the callbacks passed in. If you
* serialize and deserialize with different callbacks, the results are
* questionable.
*
* JSAutoStructuredCloneBuffer wraps things up in an RAII class for data
* management, and uses the same callbacks for both writing and reading
* (serializing and deserializing).
*/
class JS_PUBLIC_API(JSAutoStructuredCloneBuffer) {
const JS::StructuredCloneScope scope_;
JSStructuredCloneData data_;
@ -431,7 +529,7 @@ class JS_PUBLIC_API(JSAutoStructuredCloneBuffer) {
const JSStructuredCloneCallbacks* callbacks, void* closure)
: scope_(scope), version_(JS_STRUCTURED_CLONE_VERSION)
{
data_.setOptionalCallbacks(callbacks, closure, OwnTransferablePolicy::NoTransferables);
data_.setCallbacks(callbacks, closure, OwnTransferablePolicy::NoTransferables);
}
JSAutoStructuredCloneBuffer(JSAutoStructuredCloneBuffer&& other);
@ -442,12 +540,7 @@ class JS_PUBLIC_API(JSAutoStructuredCloneBuffer) {
JSStructuredCloneData& data() { return data_; }
bool empty() const { return !data_.Size(); }
void clear(const JSStructuredCloneCallbacks* optionalCallbacks=nullptr, void* closure=nullptr);
/** Copy some memory. It will be automatically freed by the destructor. */
bool copy(JSContext* cx, const JSStructuredCloneData& data,
uint32_t version=JS_STRUCTURED_CLONE_VERSION,
const JSStructuredCloneCallbacks* callbacks=nullptr, void* closure=nullptr);
void clear();
/**
* Adopt some memory. It will be automatically freed by the destructor.

View File

@ -28,6 +28,8 @@ name = "typedarray"
[[test]]
name = "stack_limit"
[[test]]
name = "value"
[[test]]
name = "vec_conversion"
[lib]

View File

@ -887,12 +887,10 @@ GetLengthOfJSStructuredCloneData(JSStructuredCloneData* data)
size_t len = 0;
auto iter = data->Iter();
while (!iter.Done()) {
size_t len_of_this_segment = iter.RemainingInSegment();
len += len_of_this_segment;
iter.Advance(*data, len_of_this_segment);
}
data->ForEachDataChunk([&](const char* bytes, size_t size) {
len += size;
return true;
});
return len;
}
@ -905,13 +903,11 @@ CopyJSStructuredCloneData(JSStructuredCloneData* src, uint8_t* dest)
size_t bytes_copied = 0;
auto iter = src->Iter();
while (!iter.Done()) {
size_t len_of_this_segment = iter.RemainingInSegment();
memcpy(dest + bytes_copied, iter.Data(), len_of_this_segment);
bytes_copied += len_of_this_segment;
iter.Advance(*src, len_of_this_segment);
}
src->ForEachDataChunk([&](const char* bytes, size_t size) {
memcpy(dest, bytes, size);
dest += size;
return true;
});
}
bool
@ -920,7 +916,7 @@ WriteBytesToJSStructuredCloneData(const uint8_t* src, size_t len, JSStructuredCl
assert(src != nullptr);
assert(dest != nullptr);
return dest->WriteBytes(reinterpret_cast<const char*>(src), len);
return dest->AppendBytes(reinterpret_cast<const char*>(src), len);
}
} // extern "C"

View File

@ -351,7 +351,7 @@ impl JS::Value {
#[cfg(target_pointer_width = "64")]
pub fn is_symbol(&self) -> bool {
unsafe {
self.asBits() == ValueShiftedTag::SYMBOL as u64
(self.asBits() >> JSVAL_TAG_SHIFT) == ValueTag::SYMBOL as u64
}
}

50
js/rust/tests/value.rs Normal file
View File

@ -0,0 +1,50 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#[macro_use]
extern crate js;
use js::jsapi::root::JS::CompartmentOptions;
use js::jsapi::root::JS_NewGlobalObject;
use js::jsapi::root::JS::OnNewGlobalHookOption;
use js::jsval::UndefinedValue;
use js::rust::{Runtime, SIMPLE_GLOBAL_CLASS};
use std::ptr;
#[test]
fn is_symbol() {
let rt = Runtime::new(false).unwrap();
let cx = rt.cx();
unsafe {
rooted!(in(cx) let global =
JS_NewGlobalObject(cx, &SIMPLE_GLOBAL_CLASS, ptr::null_mut(),
OnNewGlobalHookOption::FireOnNewGlobalHook,
&CompartmentOptions::default())
);
rooted!(in(cx) let mut rval = UndefinedValue());
assert!(rt.evaluate_script(global.handle(), "Symbol('test')",
"test", 1, rval.handle_mut()).is_ok());
assert!(rval.is_symbol());
}
}
#[test]
fn is_not_symbol() {
let rt = Runtime::new(false).unwrap();
let cx = rt.cx();
unsafe {
rooted!(in(cx) let global =
JS_NewGlobalObject(cx, &SIMPLE_GLOBAL_CLASS, ptr::null_mut(),
OnNewGlobalHookOption::FireOnNewGlobalHook,
&CompartmentOptions::default())
);
rooted!(in(cx) let mut rval = UndefinedValue());
assert!(rt.evaluate_script(global.handle(), "'not a symbol'",
"test", 1, rval.handle_mut()).is_ok());
assert!(!rval.is_symbol());
}
}

View File

@ -2833,13 +2833,13 @@ class CloneBufferObject : public NativeObject {
return false;
}
auto buf = js::MakeUnique<JSStructuredCloneData>(0, 0, nbytes);
if (!buf || !buf->Init(nbytes, nbytes)) {
auto buf = js::MakeUnique<JSStructuredCloneData>();
if (!buf || !buf->Init(nbytes)) {
ReportOutOfMemory(cx);
return false;
}
js_memcpy(buf->Start(), data, nbytes);
MOZ_ALWAYS_TRUE(buf->AppendBytes((const char*)data, nbytes));
obj->discard();
obj->setData(buf.release(), true);
@ -2893,7 +2893,7 @@ class CloneBufferObject : public NativeObject {
ReportOutOfMemory(cx);
return false;
}
auto iter = data->Iter();
auto iter = data->Start();
data->ReadBytes(iter, buffer.get(), size);
JSString* str = JS_NewStringCopyN(cx, buffer.get(), size);
if (!str)
@ -2923,7 +2923,7 @@ class CloneBufferObject : public NativeObject {
ReportOutOfMemory(cx);
return false;
}
auto iter = data->Iter();
auto iter = data->Start();
data->ReadBytes(iter, buffer.get(), size);
JSObject* arrayBuffer = JS_NewArrayBufferWithContents(cx, size, buffer.release());
if (!arrayBuffer)
@ -3098,9 +3098,7 @@ Deserialize(JSContext* cx, unsigned argc, Value* vp)
if (!JS_StructuredCloneHasTransferables(*obj->data(), &hasTransferable))
return false;
if (obj->isSynthetic() &&
(scope != JS::StructuredCloneScope::DifferentProcess || hasTransferable))
{
if (obj->isSynthetic() && scope != JS::StructuredCloneScope::DifferentProcess) {
JS_ReportErrorASCII(cx, "clone buffer data is synthetic but may contain pointers");
return false;
}

View File

@ -934,7 +934,7 @@ MaybeUnwrapArrayWrapper(JSObject* obj)
}
static MOZ_ALWAYS_INLINE JSString*
NewUCString(JSContext* cx, const AutoString& from)
NewUCString(JSContext* cx, const AutoStringChars&& from)
{
return JS_NewUCStringCopyN(cx, from.begin(), from.length());
}
@ -982,7 +982,7 @@ GetErrorMessage(void* userRef, const unsigned errorNumber)
static const char*
EncodeLatin1(JSContext* cx, AutoString& str, JSAutoByteString& bytes)
{
return bytes.encodeLatin1(cx, NewUCString(cx, str));
return bytes.encodeLatin1(cx, NewUCString(cx, str.finish()));
}
static const char*
@ -1014,12 +1014,12 @@ BuildCStyleTypeSource(JSContext* cx, JSObject* typeObj_, AutoString& source)
switch (CType::GetTypeCode(typeObj)) {
#define BUILD_SOURCE(name, fromType, ffiType) \
case TYPE_##name: \
AppendString(source, #name); \
AppendString(cx, source, #name); \
break;
CTYPES_FOR_EACH_TYPE(BUILD_SOURCE)
#undef BUILD_SOURCE
case TYPE_void_t:
AppendString(source, "void");
AppendString(cx, source, "void");
break;
case TYPE_pointer: {
unsigned ptrCount = 0;
@ -1041,8 +1041,8 @@ BuildCStyleTypeSource(JSContext* cx, JSObject* typeObj_, AutoString& source)
}
case TYPE_struct: {
RootedString name(cx, CType::GetName(cx, typeObj));
AppendString(source, "struct ");
AppendString(source, name);
AppendString(cx, source, "struct ");
AppendString(cx, source, name);
break;
}
case TYPE_function:
@ -1062,28 +1062,28 @@ BuildCStyleFunctionTypeSource(JSContext* cx, HandleObject typeObj,
FunctionInfo* fninfo = FunctionType::GetFunctionInfo(typeObj);
BuildCStyleTypeSource(cx, fninfo->mReturnType, source);
AppendString(source, " ");
AppendString(cx, source, " ");
if (nameStr) {
MOZ_ASSERT(ptrCount == 0);
AppendString(source, nameStr);
AppendString(cx, source, nameStr);
} else if (ptrCount) {
AppendString(source, "(");
AppendString(cx, source, "(");
AppendChars(source, '*', ptrCount);
AppendString(source, ")");
AppendString(cx, source, ")");
}
AppendString(source, "(");
AppendString(cx, source, "(");
if (fninfo->mArgTypes.length() > 0) {
for (size_t i = 0; i < fninfo->mArgTypes.length(); ++i) {
BuildCStyleTypeSource(cx, fninfo->mArgTypes[i], source);
if (i != fninfo->mArgTypes.length() - 1 || fninfo->mIsVariadic) {
AppendString(source, ", ");
AppendString(cx, source, ", ");
}
}
if (fninfo->mIsVariadic) {
AppendString(source, "...");
AppendString(cx, source, "...");
}
}
AppendString(source, ")");
AppendString(cx, source, ")");
}
static void
@ -1108,10 +1108,10 @@ BuildFunctionTypeSource(JSContext* cx, HandleObject funObj, AutoString& source)
RootedString funcStr(cx, JS_ValueToSource(cx, funVal));
if (!funcStr) {
JS_ClearPendingException(cx);
AppendString(source, "<<error converting function to string>>");
AppendString(cx, source, "<<error converting function to string>>");
return;
}
AppendString(source, funcStr);
AppendString(cx, source, funcStr);
}
enum class ConversionType {
@ -1131,22 +1131,22 @@ BuildConversionPosition(JSContext* cx, ConversionType convType,
case ConversionType::Argument: {
MOZ_ASSERT(funObj);
AppendString(source, " at argument ");
AppendString(cx, source, " at argument ");
AppendUInt(source, argIndex + 1);
AppendString(source, " of ");
AppendString(cx, source, " of ");
BuildFunctionTypeSource(cx, funObj, source);
break;
}
case ConversionType::Finalizer:
MOZ_ASSERT(funObj);
AppendString(source, " at argument 1 of ");
AppendString(cx, source, " at argument 1 of ");
BuildFunctionTypeSource(cx, funObj, source);
break;
case ConversionType::Return:
MOZ_ASSERT(funObj);
AppendString(source, " at the return value of ");
AppendString(cx, source, " at the return value of ");
BuildFunctionTypeSource(cx, funObj, source);
break;
default:
@ -1195,6 +1195,8 @@ ConvError(JSContext* cx, const char* expectedStr, HandleValue actual,
AutoString arrSource;
JSAutoByteString arrBytes;
BuildTypeSource(cx, arrObj, true, arrSource);
if (!arrSource)
return false;
const char* arrStr = EncodeLatin1(cx, arrSource, arrBytes);
if (!arrStr)
return false;
@ -1215,6 +1217,8 @@ ConvError(JSContext* cx, const char* expectedStr, HandleValue actual,
AutoString structSource;
JSAutoByteString structBytes;
BuildTypeSource(cx, arrObj, true, structSource);
if (!structSource)
return false;
const char* structStr = EncodeLatin1(cx, structSource, structBytes);
if (!structStr)
return false;
@ -1224,6 +1228,8 @@ ConvError(JSContext* cx, const char* expectedStr, HandleValue actual,
if (funObj) {
AutoString posSource;
BuildConversionPosition(cx, convType, funObj, argIndex, posSource);
if (!posSource)
return false;
posStr = EncodeLatin1(cx, posSource, posBytes);
if (!posStr)
return false;
@ -1252,6 +1258,8 @@ ConvError(JSContext* cx, const char* expectedStr, HandleValue actual,
AutoString funSource;
JSAutoByteString funBytes;
BuildFunctionTypeSource(cx, funObj, funSource);
if (!funSource)
return false;
const char* funStr = EncodeLatin1(cx, funSource, funBytes);
if (!funStr)
return false;
@ -1267,6 +1275,8 @@ ConvError(JSContext* cx, const char* expectedStr, HandleValue actual,
AutoString funSource;
JSAutoByteString funBytes;
BuildFunctionTypeSource(cx, funObj, funSource);
if (!funSource)
return false;
const char* funStr = EncodeLatin1(cx, funSource, funBytes);
if (!funStr)
return false;
@ -1281,6 +1291,8 @@ ConvError(JSContext* cx, const char* expectedStr, HandleValue actual,
AutoString funSource;
JSAutoByteString funBytes;
BuildFunctionTypeSource(cx, funObj, funSource);
if (!funSource)
return false;
const char* funStr = EncodeLatin1(cx, funSource, funBytes);
if (!funStr)
return false;
@ -1312,6 +1324,8 @@ ConvError(JSContext* cx, HandleObject expectedType, HandleValue actual,
AutoString expectedSource;
JSAutoByteString expectedBytes;
BuildTypeSource(cx, expectedType, true, expectedSource);
if (!expectedSource)
return false;
const char* expectedStr = EncodeLatin1(cx, expectedSource, expectedBytes);
if (!expectedStr)
return false;
@ -1366,6 +1380,8 @@ ArrayLengthMismatch(JSContext* cx, unsigned expectedLength, HandleObject arrObj,
AutoString arrSource;
JSAutoByteString arrBytes;
BuildTypeSource(cx, arrObj, true, arrSource);
if (!arrSource)
return false;
const char* arrStr = EncodeLatin1(cx, arrSource, arrBytes);
if (!arrStr)
return false;
@ -1396,6 +1412,8 @@ ArrayLengthOverflow(JSContext* cx, unsigned expectedLength, HandleObject arrObj,
AutoString arrSource;
JSAutoByteString arrBytes;
BuildTypeSource(cx, arrObj, true, arrSource);
if (!arrSource)
return false;
const char* arrStr = EncodeLatin1(cx, arrSource, arrBytes);
if (!arrStr)
return false;
@ -1461,6 +1479,8 @@ EmptyFinalizerError(JSContext* cx, ConversionType convType,
if (funObj) {
AutoString posSource;
BuildConversionPosition(cx, convType, funObj, argIndex, posSource);
if (!posSource)
return false;
posStr = EncodeLatin1(cx, posSource, posBytes);
if (!posStr)
return false;
@ -1490,6 +1510,8 @@ FieldCountMismatch(JSContext* cx,
AutoString structSource;
JSAutoByteString structBytes;
BuildTypeSource(cx, structObj, true, structSource);
if (!structSource)
return false;
const char* structStr = EncodeLatin1(cx, structSource, structBytes);
if (!structStr)
return false;
@ -1504,6 +1526,8 @@ FieldCountMismatch(JSContext* cx,
if (funObj) {
AutoString posSource;
BuildConversionPosition(cx, convType, funObj, argIndex, posSource);
if (!posSource)
return false;
posStr = EncodeLatin1(cx, posSource, posBytes);
if (!posStr)
return false;
@ -1633,6 +1657,8 @@ FinalizerSizeError(JSContext* cx, HandleObject funObj, HandleValue actual)
AutoString funSource;
JSAutoByteString funBytes;
BuildFunctionTypeSource(cx, funObj, funSource);
if (!funSource)
return false;
const char* funStr = EncodeLatin1(cx, funSource, funBytes);
if (!funStr)
return false;
@ -1656,6 +1682,8 @@ FunctionArgumentLengthMismatch(JSContext* cx,
} else {
BuildFunctionTypeSource(cx, typeObj, funSource);
}
if (!funSource)
return false;
const char* funStr = EncodeLatin1(cx, funSource, funBytes);
if (!funStr)
return false;
@ -1809,6 +1837,8 @@ NonPrimitiveError(JSContext* cx, HandleObject typeObj)
AutoString typeSource;
JSAutoByteString typeBytes;
BuildTypeSource(cx, typeObj, true, typeSource);
if (!typeSource)
return false;
const char* typeStr = EncodeLatin1(cx, typeSource, typeBytes);
if (!typeStr)
return false;
@ -1866,6 +1896,8 @@ PropNameNonStringError(JSContext* cx, HandleId id, HandleValue actual,
if (funObj) {
AutoString posSource;
BuildConversionPosition(cx, convType, funObj, argIndex, posSource);
if (!posSource)
return false;
posStr = EncodeLatin1(cx, posSource, posBytes);
if (!posStr)
return false;
@ -1918,8 +1950,9 @@ UndefinedSizeCastError(JSContext* cx, HandleObject targetTypeObj)
AutoString targetTypeSource;
JSAutoByteString targetTypeBytes;
BuildTypeSource(cx, targetTypeObj, true, targetTypeSource);
const char* targetTypeStr = EncodeLatin1(cx, targetTypeSource,
targetTypeBytes);
if (!targetTypeSource)
return false;
const char* targetTypeStr = EncodeLatin1(cx, targetTypeSource, targetTypeBytes);
if (!targetTypeStr)
return false;
@ -1936,16 +1969,18 @@ SizeMismatchCastError(JSContext* cx,
AutoString sourceTypeSource;
JSAutoByteString sourceTypeBytes;
BuildTypeSource(cx, sourceTypeObj, true, sourceTypeSource);
const char* sourceTypeStr = EncodeLatin1(cx, sourceTypeSource,
sourceTypeBytes);
if (!sourceTypeSource)
return false;
const char* sourceTypeStr = EncodeLatin1(cx, sourceTypeSource, sourceTypeBytes);
if (!sourceTypeStr)
return false;
AutoString targetTypeSource;
JSAutoByteString targetTypeBytes;
BuildTypeSource(cx, targetTypeObj, true, targetTypeSource);
const char* targetTypeStr = EncodeLatin1(cx, targetTypeSource,
targetTypeBytes);
if (!targetTypeSource)
return false;
const char* targetTypeStr = EncodeLatin1(cx, targetTypeSource, targetTypeBytes);
if (!targetTypeStr)
return false;
@ -3192,9 +3227,9 @@ jsvalToPtrExplicit(JSContext* cx, HandleValue val, uintptr_t* result)
return false;
}
template<class IntegerType, class CharType, size_t N, class AP>
template<class IntegerType, class CharType, size_t N>
void
IntegerToString(IntegerType i, int radix, mozilla::Vector<CharType, N, AP>& result)
IntegerToString(IntegerType i, int radix, StringBuilder<CharType, N>& result)
{
JS_STATIC_ASSERT(numeric_limits<IntegerType>::is_exact);
@ -3989,7 +4024,7 @@ BuildTypeName(JSContext* cx, JSObject* typeObj_)
switch (currentGrouping) {
case TYPE_pointer: {
// Pointer types go on the left.
PrependString(result, "*");
PrependString(cx, result, "*");
typeObj = PointerType::GetBaseType(typeObj);
prevGrouping = currentGrouping;
@ -3998,17 +4033,17 @@ BuildTypeName(JSContext* cx, JSObject* typeObj_)
case TYPE_array: {
if (prevGrouping == TYPE_pointer) {
// Outer type is pointer, inner type is array. Grouping is required.
PrependString(result, "(");
AppendString(result, ")");
PrependString(cx, result, "(");
AppendString(cx, result, ")");
}
// Array types go on the right.
AppendString(result, "[");
AppendString(cx, result, "[");
size_t length;
if (ArrayType::GetSafeLength(typeObj, &length))
IntegerToString(length, 10, result);
AppendString(result, "]");
AppendString(cx, result, "]");
typeObj = ArrayType::GetBaseType(typeObj);
prevGrouping = currentGrouping;
@ -4024,34 +4059,34 @@ BuildTypeName(JSContext* cx, JSObject* typeObj_)
// can't return functions.
ABICode abi = GetABICode(fninfo->mABI);
if (abi == ABI_STDCALL)
PrependString(result, "__stdcall");
PrependString(cx, result, "__stdcall");
else if (abi == ABI_THISCALL)
PrependString(result, "__thiscall");
PrependString(cx, result, "__thiscall");
else if (abi == ABI_WINAPI)
PrependString(result, "WINAPI");
PrependString(cx, result, "WINAPI");
// Function application binds more tightly than dereferencing, so
// wrap pointer types in parens. Functions can't return functions
// (only pointers to them), and arrays can't hold functions
// (similarly), so we don't need to address those cases.
if (prevGrouping == TYPE_pointer) {
PrependString(result, "(");
AppendString(result, ")");
PrependString(cx, result, "(");
AppendString(cx, result, ")");
}
// Argument list goes on the right.
AppendString(result, "(");
AppendString(cx, result, "(");
for (size_t i = 0; i < fninfo->mArgTypes.length(); ++i) {
RootedObject argType(cx, fninfo->mArgTypes[i]);
JSString* argName = CType::GetName(cx, argType);
AppendString(result, argName);
AppendString(cx, result, argName);
if (i != fninfo->mArgTypes.length() - 1 ||
fninfo->mIsVariadic)
AppendString(result, ", ");
AppendString(cx, result, ", ");
}
if (fninfo->mIsVariadic)
AppendString(result, "...");
AppendString(result, ")");
AppendString(cx, result, "...");
AppendString(cx, result, ")");
// Set 'typeObj' to the return type, and let the loop process it.
// 'prevGrouping' doesn't matter here, because functions cannot return
@ -4069,12 +4104,14 @@ BuildTypeName(JSContext* cx, JSObject* typeObj_)
// If prepending the base type name directly would splice two
// identifiers, insert a space.
if (IsAsciiAlpha(result[0]) || result[0] == '_')
PrependString(result, " ");
PrependString(cx, result, " ");
// Stick the base type and derived type parts together.
JSString* baseName = CType::GetName(cx, typeObj);
PrependString(result, baseName);
return NewUCString(cx, result);
PrependString(cx, result, baseName);
if (!result)
return nullptr;
return NewUCString(cx, result.finish());
}
// Given a CType 'typeObj', generate a string 'result' such that 'eval(result)'
@ -4082,7 +4119,7 @@ BuildTypeName(JSContext* cx, JSObject* typeObj_)
// StructType 't' is bound to an in-scope variable of name 't.name', and use
// that variable in place of generating a string to construct the type 't'.
// (This means the type comparison function CType::TypesEqual will return true
// when comparing the input and output of BuildTypeSource, since struct
// when comparing the input and output of AppendTypeSource, since struct
// equality is determined by strict JSObject pointer equality.)
static void
BuildTypeSource(JSContext* cx,
@ -4099,9 +4136,9 @@ BuildTypeSource(JSContext* cx,
CTYPES_FOR_EACH_TYPE(CASE_FOR_TYPE)
#undef CASE_FOR_TYPE
{
AppendString(result, "ctypes.");
AppendString(cx, result, "ctypes.");
JSString* nameStr = CType::GetName(cx, typeObj);
AppendString(result, nameStr);
AppendString(cx, result, nameStr);
break;
}
case TYPE_pointer: {
@ -4109,32 +4146,32 @@ BuildTypeSource(JSContext* cx,
// Specialcase ctypes.voidptr_t.
if (CType::GetTypeCode(baseType) == TYPE_void_t) {
AppendString(result, "ctypes.voidptr_t");
AppendString(cx, result, "ctypes.voidptr_t");
break;
}
// Recursively build the source string, and append '.ptr'.
BuildTypeSource(cx, baseType, makeShort, result);
AppendString(result, ".ptr");
AppendString(cx, result, ".ptr");
break;
}
case TYPE_function: {
FunctionInfo* fninfo = FunctionType::GetFunctionInfo(typeObj);
AppendString(result, "ctypes.FunctionType(");
AppendString(cx, result, "ctypes.FunctionType(");
switch (GetABICode(fninfo->mABI)) {
case ABI_DEFAULT:
AppendString(result, "ctypes.default_abi, ");
AppendString(cx, result, "ctypes.default_abi, ");
break;
case ABI_STDCALL:
AppendString(result, "ctypes.stdcall_abi, ");
AppendString(cx, result, "ctypes.stdcall_abi, ");
break;
case ABI_THISCALL:
AppendString(result, "ctypes.thiscall_abi, ");
AppendString(cx, result, "ctypes.thiscall_abi, ");
break;
case ABI_WINAPI:
AppendString(result, "ctypes.winapi_abi, ");
AppendString(cx, result, "ctypes.winapi_abi, ");
break;
case INVALID_ABI:
MOZ_CRASH("invalid abi");
@ -4145,19 +4182,19 @@ BuildTypeSource(JSContext* cx,
BuildTypeSource(cx, fninfo->mReturnType, true, result);
if (fninfo->mArgTypes.length() > 0) {
AppendString(result, ", [");
AppendString(cx, result, ", [");
for (size_t i = 0; i < fninfo->mArgTypes.length(); ++i) {
BuildTypeSource(cx, fninfo->mArgTypes[i], true, result);
if (i != fninfo->mArgTypes.length() - 1 ||
fninfo->mIsVariadic)
AppendString(result, ", ");
AppendString(cx, result, ", ");
}
if (fninfo->mIsVariadic)
AppendString(result, "\"...\"");
AppendString(result, "]");
AppendString(cx, result, "\"...\"");
AppendString(cx, result, "]");
}
AppendString(result, ")");
AppendString(cx, result, ")");
break;
}
case TYPE_array: {
@ -4166,13 +4203,13 @@ BuildTypeSource(JSContext* cx,
// is undefined.
JSObject* baseType = ArrayType::GetBaseType(typeObj);
BuildTypeSource(cx, baseType, makeShort, result);
AppendString(result, ".array(");
AppendString(cx, result, ".array(");
size_t length;
if (ArrayType::GetSafeLength(typeObj, &length))
IntegerToString(length, 10, result);
AppendString(result, ")");
AppendString(cx, result, ")");
break;
}
case TYPE_struct: {
@ -4181,22 +4218,22 @@ BuildTypeSource(JSContext* cx,
if (makeShort) {
// Shorten the type declaration by assuming that StructType 't' is bound
// to an in-scope variable of name 't.name'.
AppendString(result, name);
AppendString(cx, result, name);
break;
}
// Write the full struct declaration.
AppendString(result, "ctypes.StructType(\"");
AppendString(result, name);
AppendString(result, "\"");
AppendString(cx, result, "ctypes.StructType(\"");
AppendString(cx, result, name);
AppendString(cx, result, "\"");
// If it's an opaque struct, we're done.
if (!CType::IsSizeDefined(typeObj)) {
AppendString(result, ")");
AppendString(cx, result, ")");
break;
}
AppendString(result, ", [");
AppendString(cx, result, ", [");
const FieldInfoHash* fields = StructType::GetFieldInfo(typeObj);
size_t length = fields->count();
@ -4209,16 +4246,16 @@ BuildTypeSource(JSContext* cx,
for (size_t i = 0; i < length; ++i) {
const FieldInfoHash::Entry* entry = fieldsArray[i];
AppendString(result, "{ \"");
AppendString(result, entry->key());
AppendString(result, "\": ");
AppendString(cx, result, "{ \"");
AppendString(cx, result, entry->key());
AppendString(cx, result, "\": ");
BuildTypeSource(cx, entry->value().mType, true, result);
AppendString(result, " }");
AppendString(cx, result, " }");
if (i != length - 1)
AppendString(result, ", ");
AppendString(cx, result, ", ");
}
AppendString(result, "])");
AppendString(cx, result, "])");
break;
}
}
@ -4234,7 +4271,7 @@ BuildTypeSource(JSContext* cx,
// resulting string can ImplicitConvert successfully if passed to another data
// constructor. (This is important when called recursively, since fields of
// structs and arrays are converted with ImplicitConvert.)
static bool
static MOZ_MUST_USE bool
BuildDataSource(JSContext* cx,
HandleObject typeObj,
void* data,
@ -4245,9 +4282,9 @@ BuildDataSource(JSContext* cx,
switch (type) {
case TYPE_bool:
if (*static_cast<bool*>(data))
AppendString(result, "true");
AppendString(cx, result, "true");
else
AppendString(result, "false");
AppendString(cx, result, "false");
break;
#define INTEGRAL_CASE(name, type, ffiType) \
case TYPE_##name: \
@ -4260,12 +4297,12 @@ BuildDataSource(JSContext* cx,
case TYPE_##name: \
/* Serialize as a wrapped decimal integer. */ \
if (!numeric_limits<type>::is_signed) \
AppendString(result, "ctypes.UInt64(\""); \
AppendString(cx, result, "ctypes.UInt64(\""); \
else \
AppendString(result, "ctypes.Int64(\""); \
AppendString(cx, result, "ctypes.Int64(\""); \
\
IntegerToString(*static_cast<type*>(data), 10, result); \
AppendString(result, "\")"); \
AppendString(cx, result, "\")"); \
break;
CTYPES_FOR_EACH_WRAPPED_INT_TYPE(WRAPPED_INT_CASE)
#undef WRAPPED_INT_CASE
@ -4302,7 +4339,7 @@ BuildDataSource(JSContext* cx,
if (!src)
return false;
AppendString(result, src);
AppendString(cx, result, src);
break;
}
case TYPE_pointer:
@ -4311,17 +4348,17 @@ BuildDataSource(JSContext* cx,
// The result must be able to ImplicitConvert successfully.
// Wrap in a type constructor, then serialize for ExplicitConvert.
BuildTypeSource(cx, typeObj, true, result);
AppendString(result, "(");
AppendString(cx, result, "(");
}
// Serialize the pointer value as a wrapped hexadecimal integer.
uintptr_t ptr = *static_cast<uintptr_t*>(data);
AppendString(result, "ctypes.UInt64(\"0x");
AppendString(cx, result, "ctypes.UInt64(\"0x");
IntegerToString(ptr, 16, result);
AppendString(result, "\")");
AppendString(cx, result, "\")");
if (isImplicit)
AppendString(result, ")");
AppendString(cx, result, ")");
break;
}
@ -4329,7 +4366,7 @@ BuildDataSource(JSContext* cx,
// Serialize each element of the array recursively. Each element must
// be able to ImplicitConvert successfully.
RootedObject baseType(cx, ArrayType::GetBaseType(typeObj));
AppendString(result, "[");
AppendString(cx, result, "[");
size_t length = ArrayType::GetLength(typeObj);
size_t elementSize = CType::GetSize(baseType);
@ -4339,9 +4376,9 @@ BuildDataSource(JSContext* cx,
return false;
if (i + 1 < length)
AppendString(result, ", ");
AppendString(cx, result, ", ");
}
AppendString(result, "]");
AppendString(cx, result, "]");
break;
}
case TYPE_struct: {
@ -4349,7 +4386,7 @@ BuildDataSource(JSContext* cx,
// The result must be able to ImplicitConvert successfully.
// Serialize the data as an object with properties, rather than
// a sequence of arguments to the StructType constructor.
AppendString(result, "{");
AppendString(cx, result, "{");
}
// Serialize each field of the struct recursively. Each field must
@ -4367,9 +4404,9 @@ BuildDataSource(JSContext* cx,
const FieldInfoHash::Entry* entry = fieldsArray[i];
if (isImplicit) {
AppendString(result, "\"");
AppendString(result, entry->key());
AppendString(result, "\": ");
AppendString(cx, result, "\"");
AppendString(cx, result, entry->key());
AppendString(cx, result, "\": ");
}
char* fieldData = static_cast<char*>(data) + entry->value().mOffset;
@ -4378,11 +4415,11 @@ BuildDataSource(JSContext* cx,
return false;
if (i + 1 != length)
AppendString(result, ", ");
AppendString(cx, result, ", ");
}
if (isImplicit)
AppendString(result, "}");
AppendString(cx, result, "}");
break;
}
@ -4997,9 +5034,11 @@ CType::ToString(JSContext* cx, unsigned argc, Value* vp)
JSString* result;
if (CType::IsCType(obj)) {
AutoString type;
AppendString(type, "type ");
AppendString(type, GetName(cx, obj));
result = NewUCString(cx, type);
AppendString(cx, type, "type ");
AppendString(cx, type, GetName(cx, obj));
if (!type)
return false;
result = NewUCString(cx, type.finish());
}
else {
result = JS_NewStringCopyZ(cx, "[CType proto object]");
@ -5029,7 +5068,9 @@ CType::ToSource(JSContext* cx, unsigned argc, Value* vp)
if (CType::IsCType(obj)) {
AutoString source;
BuildTypeSource(cx, obj, false, source);
result = NewUCString(cx, source);
if (!source)
return false;
result = NewUCString(cx, source.finish());
} else {
result = JS_NewStringCopyZ(cx, "[CType proto object]");
}
@ -6805,7 +6846,8 @@ PrepareCIF(JSContext* cx,
}
void
FunctionType::BuildSymbolName(JSString* name,
FunctionType::BuildSymbolName(JSContext* cx,
JSString* name,
JSObject* typeObj,
AutoCString& result)
{
@ -6816,7 +6858,7 @@ FunctionType::BuildSymbolName(JSString* name,
case ABI_THISCALL:
case ABI_WINAPI:
// For cdecl or WINAPI functions, no mangling is necessary.
AppendString(result, name);
AppendString(cx, result, name);
break;
case ABI_STDCALL: {
@ -6825,9 +6867,9 @@ FunctionType::BuildSymbolName(JSString* name,
// _foo@40
// where 'foo' is the function name, and '40' is the aligned size of the
// arguments.
AppendString(result, "_");
AppendString(result, name);
AppendString(result, "@");
AppendString(cx, result, "_");
AppendString(cx, result, name);
AppendString(cx, result, "@");
// Compute the suffix by aligning each argument to sizeof(ffi_arg).
size_t size = 0;
@ -6840,7 +6882,7 @@ FunctionType::BuildSymbolName(JSString* name,
#elif defined(_WIN64)
// On Win64, stdcall is an alias to the default ABI for compatibility, so no
// mangling is done.
AppendString(result, name);
AppendString(cx, result, name);
#endif
break;
}
@ -8059,13 +8101,14 @@ CData::GetSourceString(JSContext* cx, HandleObject typeObj, void* data)
// bound to a variable in the current scope.)
AutoString source;
BuildTypeSource(cx, typeObj, true, source);
AppendString(source, "(");
AppendString(cx, source, "(");
if (!BuildDataSource(cx, typeObj, data, false, source))
source.handle(false);
AppendString(cx, source, ")");
if (!source)
return nullptr;
AppendString(source, ")");
return NewUCString(cx, source);
return NewUCString(cx, source.finish());
}
bool
@ -8143,13 +8186,13 @@ CDataFinalizer::Methods::ToSource(JSContext* cx, unsigned argc, Value* vp)
}
AutoString source;
AppendString(source, "ctypes.CDataFinalizer(");
AppendString(cx, source, "ctypes.CDataFinalizer(");
JSString* srcValue = CData::GetSourceString(cx, objType, p->cargs);
if (!srcValue) {
return false;
}
AppendString(source, srcValue);
AppendString(source, ", ");
AppendString(cx, source, srcValue);
AppendString(cx, source, ", ");
Value valCodePtrType = JS_GetReservedSlot(objThis,
SLOT_DATAFINALIZER_CODETYPE);
if (valCodePtrType.isPrimitive()) {
@ -8162,9 +8205,11 @@ CDataFinalizer::Methods::ToSource(JSContext* cx, unsigned argc, Value* vp)
return false;
}
AppendString(source, srcDispose);
AppendString(source, ")");
strMessage = NewUCString(cx, source);
AppendString(cx, source, srcDispose);
AppendString(cx, source, ")");
if (!source)
return false;
strMessage = NewUCString(cx, source.finish());
}
if (!strMessage) {
@ -8744,7 +8789,9 @@ Int64Base::ToString(JSContext* cx,
IntegerToString(static_cast<int64_t>(GetInt(obj)), radix, intString);
}
JSString* result = NewUCString(cx, intString);
if (!intString)
return false;
JSString* result = NewUCString(cx, intString.finish());
if (!result)
return false;
@ -8768,15 +8815,17 @@ Int64Base::ToSource(JSContext* cx,
// Return a decimal string suitable for constructing the number.
AutoString source;
if (isUnsigned) {
AppendString(source, "ctypes.UInt64(\"");
AppendString(cx, source, "ctypes.UInt64(\"");
IntegerToString(GetInt(obj), 10, source);
} else {
AppendString(source, "ctypes.Int64(\"");
AppendString(cx, source, "ctypes.Int64(\"");
IntegerToString(static_cast<int64_t>(GetInt(obj)), 10, source);
}
AppendString(source, "\")");
AppendString(cx, source, "\")");
if (!source)
return false;
JSString* result = NewUCString(cx, source);
JSString* result = NewUCString(cx, source.finish());
if (!result)
return false;

View File

@ -26,16 +26,95 @@ namespace ctypes {
** Utility classes
*******************************************************************************/
// String and AutoString classes, based on Vector.
typedef Vector<char16_t, 0, SystemAllocPolicy> String;
typedef Vector<char16_t, 64, SystemAllocPolicy> AutoString;
typedef Vector<char, 0, SystemAllocPolicy> CString;
typedef Vector<char, 64, SystemAllocPolicy> AutoCString;
// CTypes builds a number of strings. StringBuilder allows repeated appending
// with a single error check at the end. Only the Vector methods required for
// building the string are exposed.
template <class CharT, size_t N>
class StringBuilder {
Vector<CharT, N, SystemAllocPolicy> v;
// Have any (OOM) errors been encountered while constructing this string?
bool errored { false };
#ifdef DEBUG
// Have we finished building this string?
bool finished { false };
// Did we check for errors?
mutable bool checked { false };
#endif
public:
explicit operator bool() const {
#ifdef DEBUG
checked = true;
#endif
return !errored;
}
// Handle the result of modifying the string, by remembering the persistent
// errored status.
bool handle(bool result) {
MOZ_ASSERT(!finished);
if (!result)
errored = true;
return result;
}
bool resize(size_t n) {
return handle(v.resize(n));
}
CharT& operator[](size_t index) { return v[index]; }
const CharT& operator[](size_t index) const { return v[index]; }
size_t length() const { return v.length(); }
template<typename U> MOZ_MUST_USE bool append(U&& u) {
return handle(v.append(u));
}
template<typename U> MOZ_MUST_USE bool append(const U* begin, const U* end) {
return handle(v.append(begin, end));
}
template<typename U> MOZ_MUST_USE bool append(const U* begin, size_t len) {
return handle(v.append(begin, len));
}
CharT* begin() {
MOZ_ASSERT(!finished);
return v.begin();
}
// finish() produces the results of the string building, and is required as
// the last thing before the string contents are used. The StringBuilder must
// be checked for errors before calling this, however.
Vector<CharT, N, SystemAllocPolicy>&& finish() {
MOZ_ASSERT(!errored);
MOZ_ASSERT(!finished);
MOZ_ASSERT(checked);
#ifdef DEBUG
finished = true;
#endif
return mozilla::Move(v);
}
};
// Note that these strings do not have any inline storage, because we use move
// constructors to pass the data around and inline storage would necessitate
// copying.
typedef StringBuilder<char16_t, 0> AutoString;
typedef StringBuilder<char, 0> AutoCString;
typedef Vector<char16_t, 0, SystemAllocPolicy> AutoStringChars;
typedef Vector<char, 0, SystemAllocPolicy> AutoCStringChars;
// Convenience functions to append, insert, and compare Strings.
template <class T, size_t N, class AP, size_t ArrayLength>
template <class T, size_t N, size_t ArrayLength>
void
AppendString(mozilla::Vector<T, N, AP>& v, const char (&array)[ArrayLength])
AppendString(JSContext* cx, StringBuilder<T, N>& v, const char (&array)[ArrayLength])
{
// Don't include the trailing '\0'.
size_t alen = ArrayLength - 1;
@ -47,9 +126,9 @@ AppendString(mozilla::Vector<T, N, AP>& v, const char (&array)[ArrayLength])
v[i + vlen] = array[i];
}
template <class T, size_t N, class AP>
template <class T, size_t N>
void
AppendChars(mozilla::Vector<T, N, AP>& v, const char c, size_t count)
AppendChars(StringBuilder<T, N>& v, const char c, size_t count)
{
size_t vlen = v.length();
if (!v.resize(vlen + count))
@ -59,9 +138,9 @@ AppendChars(mozilla::Vector<T, N, AP>& v, const char c, size_t count)
v[i + vlen] = c;
}
template <class T, size_t N, class AP>
template <class T, size_t N>
void
AppendUInt(mozilla::Vector<T, N, AP>& v, unsigned n)
AppendUInt(StringBuilder<T, N>& v, unsigned n)
{
char array[16];
size_t alen = SprintfLiteral(array, "%u", n);
@ -75,18 +154,18 @@ AppendUInt(mozilla::Vector<T, N, AP>& v, unsigned n)
template <class T, size_t N, size_t M, class AP>
void
AppendString(mozilla::Vector<T, N, AP>& v, mozilla::Vector<T, M, AP>& w)
AppendString(JSContext* cx, StringBuilder<T, N>& v, mozilla::Vector<T, M, AP>& w)
{
if (!v.append(w.begin(), w.length()))
return;
}
template <size_t N, class AP>
template <size_t N>
void
AppendString(mozilla::Vector<char16_t, N, AP>& v, JSString* str)
AppendString(JSContext* cx, StringBuilder<char16_t, N>& v, JSString* str)
{
MOZ_ASSERT(str);
JSLinearString* linear = str->ensureLinear(nullptr);
JSLinearString* linear = str->ensureLinear(cx);
if (!linear)
return;
JS::AutoCheckCannotGC nogc;
@ -99,9 +178,9 @@ AppendString(mozilla::Vector<char16_t, N, AP>& v, JSString* str)
}
}
template <size_t N, class AP>
template <size_t N>
void
AppendString(mozilla::Vector<char, N, AP>& v, JSString* str)
AppendString(JSContext* cx, StringBuilder<char, N>& v, JSString* str)
{
MOZ_ASSERT(str);
size_t vlen = v.length();
@ -109,7 +188,7 @@ AppendString(mozilla::Vector<char, N, AP>& v, JSString* str)
if (!v.resize(vlen + alen))
return;
JSLinearString* linear = str->ensureLinear(nullptr);
JSLinearString* linear = str->ensureLinear(cx);
if (!linear)
return;
@ -125,9 +204,9 @@ AppendString(mozilla::Vector<char, N, AP>& v, JSString* str)
}
}
template <class T, size_t N, class AP, size_t ArrayLength>
template <class T, size_t N, size_t ArrayLength>
void
PrependString(mozilla::Vector<T, N, AP>& v, const char (&array)[ArrayLength])
PrependString(JSContext* cx, StringBuilder<T, N>& v, const char (&array)[ArrayLength])
{
// Don't include the trailing '\0'.
size_t alen = ArrayLength - 1;
@ -143,9 +222,9 @@ PrependString(mozilla::Vector<T, N, AP>& v, const char (&array)[ArrayLength])
v[i] = array[i];
}
template <size_t N, class AP>
template <size_t N>
void
PrependString(mozilla::Vector<char16_t, N, AP>& v, JSString* str)
PrependString(JSContext* cx, StringBuilder<char16_t, N>& v, JSString* str)
{
MOZ_ASSERT(str);
size_t vlen = v.length();
@ -153,7 +232,7 @@ PrependString(mozilla::Vector<char16_t, N, AP>& v, JSString* str)
if (!v.resize(vlen + alen))
return;
JSLinearString* linear = str->ensureLinear(nullptr);
JSLinearString* linear = str->ensureLinear(cx);
if (!linear)
return;
@ -502,7 +581,7 @@ namespace FunctionType {
JSObject* refObj, PRFuncPtr fnptr, JSObject* result);
FunctionInfo* GetFunctionInfo(JSObject* obj);
void BuildSymbolName(JSString* name, JSObject* typeObj,
void BuildSymbolName(JSContext* cx, JSString* name, JSObject* typeObj,
AutoCString& result);
} // namespace FunctionType

View File

@ -72,12 +72,14 @@ Library::Name(JSContext* cx, unsigned argc, Value* vp)
}
AutoString resultString;
AppendString(resultString, DLL_PREFIX);
AppendString(resultString, str);
AppendString(resultString, DLL_SUFFIX);
AppendString(cx, resultString, DLL_PREFIX);
AppendString(cx, resultString, str);
AppendString(cx, resultString, DLL_SUFFIX);
if (!resultString)
return false;
auto resultStr = resultString.finish();
JSString* result = JS_NewUCStringCopyN(cx, resultString.begin(),
resultString.length());
JSString* result = JS_NewUCStringCopyN(cx, resultStr.begin(), resultStr.length());
if (!result)
return false;
@ -339,11 +341,13 @@ Library::Declare(JSContext* cx, unsigned argc, Value* vp)
AutoCString symbol;
if (isFunction) {
// Build the symbol, with mangling if necessary.
FunctionType::BuildSymbolName(nameStr, fnObj, symbol);
AppendString(symbol, "\0");
FunctionType::BuildSymbolName(cx, nameStr, fnObj, symbol);
AppendString(cx, symbol, "\0");
if (!symbol)
return false;
// Look up the function symbol.
fnptr = PR_FindFunctionSymbol(library, symbol.begin());
fnptr = PR_FindFunctionSymbol(library, symbol.finish().begin());
if (!fnptr) {
JS_ReportErrorASCII(cx, "couldn't find function symbol in library");
return false;
@ -352,10 +356,12 @@ Library::Declare(JSContext* cx, unsigned argc, Value* vp)
} else {
// 'typeObj' is another data type. Look up the data symbol.
AppendString(symbol, nameStr);
AppendString(symbol, "\0");
AppendString(cx, symbol, nameStr);
AppendString(cx, symbol, "\0");
if (!symbol)
return false;
data = PR_FindSymbol(library, symbol.begin());
data = PR_FindSymbol(library, symbol.finish().begin());
if (!data) {
JS_ReportErrorASCII(cx, "couldn't find symbol in library");
return false;

View File

@ -39,15 +39,16 @@ testStructuredCloneReaderFuzz(const uint8_t* buf, size_t size) {
const size_t kSegmentAlignment = 8;
size_t buf_size = JS_ROUNDUP(size, kSegmentAlignment);
auto clonebuf = MakeUnique<JSStructuredCloneData>(0, 0, buf_size);
if (!clonebuf || !clonebuf->Init(buf_size, buf_size)) {
auto clonebuf = MakeUnique<JSStructuredCloneData>();
if (!clonebuf || !clonebuf->Init(buf_size)) {
ReportOutOfMemory(gCx);
return 0;
}
// Initialize with zeros, including padding, then copy buffer
memset(clonebuf->Start(), '\0', buf_size);
js_memcpy(clonebuf->Start(), buf, size);
// Copy buffer then pad with zeroes.
clonebuf->AppendBytes((const char*)buf, size);
char padding[kSegmentAlignment] = {0};
clonebuf->AppendBytes(padding, buf_size - size);
JS::StructuredCloneScope scope = JS::StructuredCloneScope::DifferentProcess;

View File

@ -1933,12 +1933,21 @@ IonBuilder::inlineStrFromCharCode(CallInfo& callInfo)
if (getInlineReturnType() != MIRType::String)
return InliningStatus_NotInlined;
if (callInfo.getArg(0)->type() != MIRType::Int32)
return InliningStatus_NotInlined;
MDefinition* codeUnit = callInfo.getArg(0);
if (codeUnit->type() != MIRType::Int32) {
// MTruncateToInt32 will always bail for objects and symbols, so don't
// try to inline String.fromCharCode() for these two value types.
if (codeUnit->mightBeType(MIRType::Object) || codeUnit->mightBeType(MIRType::Symbol))
return InliningStatus_NotInlined;
codeUnit = MTruncateToInt32::New(alloc(), codeUnit);
current->add(codeUnit->toInstruction());
}
callInfo.setImplicitlyUsedUnchecked();
MFromCharCode* string = MFromCharCode::New(alloc(), callInfo.getArg(0));
MFromCharCode* string = MFromCharCode::New(alloc(), codeUnit);
current->add(string);
current->push(string);
return InliningStatus_Inlined;

View File

@ -1577,6 +1577,8 @@ EqualStringsHelper(JSString* str1, JSString* str2)
MOZ_ASSERT(!str2->isAtom());
MOZ_ASSERT(str1->length() == str2->length());
// ensureLinear is intentionally called with a nullptr to avoid OOM
// reporting; if it fails, we will continue to the next stub.
JSLinearString* str2Linear = str2->ensureLinear(nullptr);
if (!str2Linear)
return false;

View File

@ -152,9 +152,12 @@ class MacroAssemblerMIPS : public MacroAssemblerMIPSShared
void ma_pop(FloatRegister f);
void ma_push(FloatRegister f);
void ma_cmp_set(Register dst, Register lhs, ImmPtr imm, Condition c) {
void ma_cmp_set(Register dst, Register lhs, ImmWord imm, Condition c) {
ma_cmp_set(dst, lhs, Imm32(uint32_t(imm.value)), c);
}
void ma_cmp_set(Register dst, Register lhs, ImmPtr imm, Condition c) {
ma_cmp_set(dst, lhs, ImmWord(uintptr_t(imm.value)), c);
}
void ma_cmp_set(Register dst, Register lhs, Address addr, Condition c) {
MOZ_ASSERT(lhs != ScratchRegister);
ma_lw(ScratchRegister, addr);

View File

@ -6079,13 +6079,6 @@ JS_PutEscapedString(JSContext* cx, char* buffer, size_t size, JSString* str, cha
return PutEscapedString(buffer, size, linearStr, quote);
}
JS_PUBLIC_API(bool)
JS_FileEscapedString(FILE* fp, JSString* str, char quote)
{
JSLinearString* linearStr = str->ensureLinear(nullptr);
return linearStr && FileEscapedString(fp, linearStr, quote);
}
JS_PUBLIC_API(JSString*)
JS_NewDependentString(JSContext* cx, HandleString str, size_t start, size_t length)
{

View File

@ -4717,9 +4717,6 @@ JS_StringEqualsAscii(JSContext* cx, JSString* str, const char* asciiBytes, bool*
extern JS_PUBLIC_API(size_t)
JS_PutEscapedString(JSContext* cx, char* buffer, size_t size, JSString* str, char quote);
extern JS_PUBLIC_API(bool)
JS_FileEscapedString(FILE* fp, JSString* str, char quote);
/*
* Extracting string characters and length.
*

View File

@ -240,6 +240,7 @@ JSString::equals(const char* s)
{
JSLinearString* linear = ensureLinear(nullptr);
if (!linear) {
// This is DEBUG-only code.
fprintf(stderr, "OOM in JSString::equals!\n");
return false;
}

View File

@ -159,16 +159,16 @@ template<typename T, typename AllocPolicy>
struct BufferIterator {
typedef mozilla::BufferList<AllocPolicy> BufferList;
explicit BufferIterator(BufferList& buffer)
explicit BufferIterator(const BufferList& buffer)
: mBuffer(buffer)
, mIter(buffer.Iter())
{
JS_STATIC_ASSERT(8 % sizeof(T) == 0);
}
BufferIterator(const BufferIterator& other)
: mBuffer(other.mBuffer)
, mIter(other.mIter)
explicit BufferIterator(const JSStructuredCloneData& data)
: mBuffer(data.bufList_)
, mIter(data.Start())
{
}
@ -227,7 +227,7 @@ struct BufferIterator {
return mIter.HasRoomFor(sizeof(T));
}
BufferList& mBuffer;
const BufferList& mBuffer;
typename BufferList::IterImpl mIter;
};
@ -294,9 +294,17 @@ SharedArrayRawBufferRefs::releaseAll()
refs_.clear();
}
// SCOutput provides an interface to write raw data -- eg uint64_ts, doubles,
// arrays of bytes -- into a structured clone data output stream. It also knows
// how to free any transferable data within that stream.
//
// Note that it contains a full JSStructuredCloneData object, which holds the
// callbacks necessary to read/write/transfer/free the data. For the purpose of
// this class, only the freeTransfer callback is relevant; the rest of the callbacks
// are used by the higher-level JSStructuredCloneWriter interface.
struct SCOutput {
public:
using Iter = BufferIterator<uint64_t, TempAllocPolicy>;
using Iter = BufferIterator<uint64_t, SystemAllocPolicy>;
explicit SCOutput(JSContext* cx);
@ -313,22 +321,25 @@ struct SCOutput {
template <class T>
MOZ_MUST_USE bool writeArray(const T* p, size_t nbytes);
MOZ_MUST_USE bool extractBuffer(JSStructuredCloneData* data);
void discardTransferables(const JSStructuredCloneCallbacks* cb, void* cbClosure);
void setCallbacks(const JSStructuredCloneCallbacks* callbacks,
void* closure,
OwnTransferablePolicy policy)
{
buf.setCallbacks(callbacks, closure, policy);
}
void extractBuffer(JSStructuredCloneData* data) { *data = Move(buf); }
void discardTransferables();
uint64_t tell() const { return buf.Size(); }
uint64_t count() const { return buf.Size() / sizeof(uint64_t); }
Iter iter() {
return BufferIterator<uint64_t, TempAllocPolicy>(buf);
}
Iter iter() { return Iter(buf); }
size_t offset(Iter dest) {
return dest - iter();
}
private:
JSContext* cx;
mozilla::BufferList<TempAllocPolicy> buf;
JSStructuredCloneData buf;
};
class SCInput {
@ -461,11 +472,13 @@ struct JSStructuredCloneWriter {
const Value& tVal)
: out(cx), scope(scope), objs(out.context()),
counts(out.context()), entries(out.context()),
memory(out.context()), callbacks(cb),
closure(cbClosure), transferable(out.context(), tVal),
memory(out.context()),
transferable(out.context(), tVal),
transferableObjects(out.context(), GCHashSet<JSObject*>(cx)),
cloneDataPolicy(cloneDataPolicy)
{}
{
out.setCallbacks(cb, cbClosure, OwnTransferablePolicy::NoTransferables);
}
~JSStructuredCloneWriter();
@ -481,16 +494,8 @@ struct JSStructuredCloneWriter {
SCOutput& output() { return out; }
bool extractBuffer(JSStructuredCloneData* data) {
bool success = out.extractBuffer(data);
if (success) {
// Move the SharedArrayRawBuf references here, SCOutput::extractBuffer
// moves the serialized data.
data->refsHeld_.takeOwnership(Move(refsHeld));
data->setOptionalCallbacks(callbacks, closure,
OwnTransferablePolicy::OwnsTransferablesIfAny);
}
return success;
void extractBuffer(JSStructuredCloneData* newData) {
out.extractBuffer(newData);
}
JS::StructuredCloneScope cloneScope() const { return scope; }
@ -554,21 +559,12 @@ struct JSStructuredCloneWriter {
SystemAllocPolicy>;
Rooted<CloneMemory> memory;
// The user defined callbacks that will be used for cloning.
const JSStructuredCloneCallbacks* callbacks;
// Any value passed to JS_WriteStructuredClone.
void* closure;
// Set of transferable objects
RootedValue transferable;
Rooted<GCHashSet<JSObject*>> transferableObjects;
const JS::CloneDataPolicy cloneDataPolicy;
// SharedArrayRawBuffers whose reference counts we have incremented.
SharedArrayRawBufferRefs refsHeld;
friend bool JS_WriteString(JSStructuredCloneWriter* w, HandleString str);
friend bool JS_WriteTypedArray(JSStructuredCloneWriter* w, HandleValue v);
friend bool JS_ObjectNotWritten(JSStructuredCloneWriter* w, HandleObject obj);
@ -626,7 +622,12 @@ WriteStructuredClone(JSContext* cx, HandleValue v, JSStructuredCloneData* bufp,
const Value& transferable)
{
JSStructuredCloneWriter w(cx, scope, cloneDataPolicy, cb, cbClosure, transferable);
return w.init() && w.write(v) && w.extractBuffer(bufp);
if (!w.init())
return false;
if (!w.write(v))
return false;
w.extractBuffer(bufp);
return true;
}
bool
@ -717,13 +718,12 @@ DiscardTransferables(mozilla::BufferList<AllocPolicy>& buffer,
static bool
StructuredCloneHasTransferObjects(const JSStructuredCloneData& data)
{
auto iter = data.Iter();
if (data.Size() < sizeof(uint64_t))
return false;
uint64_t u;
MOZ_ALWAYS_TRUE(data.ReadBytes(iter, reinterpret_cast<char*>(&u), sizeof(u)));
BufferIterator<uint64_t, SystemAllocPolicy> iter(data);
MOZ_ALWAYS_TRUE(iter.readBytes(reinterpret_cast<char*>(&u), sizeof(u)));
uint32_t tag = uint32_t(u >> 32);
return (tag == SCTAG_TRANSFER_MAP_HEADER);
}
@ -734,7 +734,7 @@ SCInput::SCInput(JSContext* cx, JSStructuredCloneData& data)
: cx(cx), point(data)
{
static_assert(JSStructuredCloneData::kSegmentAlignment % 8 == 0,
static_assert(JSStructuredCloneData::BufferList::kSegmentAlignment % 8 == 0,
"structured clone buffer reads should be aligned");
MOZ_ASSERT(data.Size() % 8 == 0);
}
@ -906,7 +906,6 @@ SCInput::readPtr(void** p)
SCOutput::SCOutput(JSContext* cx)
: cx(cx)
, buf(0, 0, 4096, cx)
{
}
@ -914,7 +913,11 @@ bool
SCOutput::write(uint64_t u)
{
uint64_t v = NativeEndian::swapToLittleEndian(u);
return buf.WriteBytes(reinterpret_cast<char*>(&v), sizeof(u));
if (!buf.AppendBytes(reinterpret_cast<char*>(&v), sizeof(u))) {
ReportOutOfMemory(context());
return false;
}
return true;
}
bool
@ -954,14 +957,14 @@ SCOutput::writeArray(const T* p, size_t nelems)
for (size_t i = 0; i < nelems; i++) {
T value = NativeEndian::swapToLittleEndian(p[i]);
if (!buf.WriteBytes(reinterpret_cast<char*>(&value), sizeof(value)))
if (!buf.AppendBytes(reinterpret_cast<char*>(&value), sizeof(value)))
return false;
}
// Zero-pad to 8 bytes boundary.
size_t padbytes = ComputePadding(nelems, sizeof(T));
char zeroes[sizeof(uint64_t)] = { 0 };
if (!buf.WriteBytes(zeroes, padbytes))
if (!buf.AppendBytes(zeroes, padbytes))
return false;
return true;
@ -974,13 +977,13 @@ SCOutput::writeArray<uint8_t>(const uint8_t* p, size_t nelems)
if (nelems == 0)
return true;
if (!buf.WriteBytes(reinterpret_cast<const char*>(p), nelems))
if (!buf.AppendBytes(reinterpret_cast<const char*>(p), nelems))
return false;
// zero-pad to 8 bytes boundary
size_t padbytes = ComputePadding(nelems, 1);
char zeroes[sizeof(uint64_t)] = { 0 };
if (!buf.WriteBytes(zeroes, padbytes))
if (!buf.AppendBytes(zeroes, padbytes))
return false;
return true;
@ -1014,34 +1017,22 @@ SCOutput::writePtr(const void* p)
return write(reinterpret_cast<uint64_t>(p));
}
bool
SCOutput::extractBuffer(JSStructuredCloneData* data)
{
bool success;
mozilla::BufferList<SystemAllocPolicy> out =
buf.MoveFallible<SystemAllocPolicy>(&success);
if (!success) {
ReportOutOfMemory(cx);
return false;
}
*data = JSStructuredCloneData(Move(out));
return true;
}
void
SCOutput::discardTransferables(const JSStructuredCloneCallbacks* cb, void* cbClosure)
SCOutput::discardTransferables()
{
DiscardTransferables(buf, cb, cbClosure);
buf.discardTransferables();
}
} // namespace js
JSStructuredCloneData::~JSStructuredCloneData()
void
JSStructuredCloneData::discardTransferables()
{
if (!Size())
return;
if (ownTransferables_ == OwnTransferablePolicy::OwnsTransferablesIfAny)
DiscardTransferables(*this, callbacks_, closure_);
DiscardTransferables(bufList_, callbacks_, closure_);
}
JS_STATIC_ASSERT(JSString::MAX_LENGTH < UINT32_MAX);
@ -1049,9 +1040,8 @@ JS_STATIC_ASSERT(JSString::MAX_LENGTH < UINT32_MAX);
JSStructuredCloneWriter::~JSStructuredCloneWriter()
{
// Free any transferable data left lying around in the buffer
if (out.count()) {
out.discardTransferables(callbacks, closure);
}
if (out.count())
out.discardTransferables();
}
bool
@ -1129,11 +1119,11 @@ JSStructuredCloneWriter::parseTransferable()
}
else {
if (!callbacks || !callbacks->canTransfer)
if (!out.buf.callbacks_ || !out.buf.callbacks_->canTransfer)
return reportDataCloneError(JS_SCERR_TRANSFERABLE);
JSAutoCompartment ac(cx, unwrappedObj);
if (!callbacks->canTransfer(cx, unwrappedObj, closure))
if (!out.buf.callbacks_->canTransfer(cx, unwrappedObj, out.buf.closure_))
return false;
}
@ -1152,7 +1142,7 @@ JSStructuredCloneWriter::parseTransferable()
bool
JSStructuredCloneWriter::reportDataCloneError(uint32_t errorId)
{
ReportDataCloneError(context(), callbacks, errorId);
ReportDataCloneError(context(), out.buf.callbacks_, errorId);
return false;
}
@ -1285,7 +1275,7 @@ JSStructuredCloneWriter::writeSharedArrayBuffer(HandleObject obj)
Rooted<SharedArrayBufferObject*> sharedArrayBuffer(context(), &CheckedUnwrap(obj)->as<SharedArrayBufferObject>());
SharedArrayRawBuffer* rawbuf = sharedArrayBuffer->rawBufferObject();
if (!refsHeld.acquire(context(), rawbuf))
if (!out.buf.refsHeld_.acquire(context(), rawbuf))
return false;
// We must serialize the length so that the buffer object arrives in the
@ -1611,8 +1601,8 @@ JSStructuredCloneWriter::startWrite(HandleValue v)
return traverseSavedFrame(obj);
}
if (callbacks && callbacks->write)
return callbacks->write(context(), this, obj, closure);
if (out.buf.callbacks_ && out.buf.callbacks_->write)
return out.buf.callbacks_->write(context(), this, obj, out.buf.closure_);
// else fall through
}
@ -1755,9 +1745,9 @@ JSStructuredCloneWriter::transferOwnership()
extraData = nbytes;
}
} else {
if (!callbacks || !callbacks->writeTransfer)
if (!out.buf.callbacks_ || !out.buf.callbacks_->writeTransfer)
return reportDataCloneError(JS_SCERR_TRANSFERABLE);
if (!callbacks->writeTransfer(cx, obj, closure, &tag, &ownership, &content, &extraData))
if (!out.buf.callbacks_->writeTransfer(cx, obj, out.buf.closure_, &tag, &ownership, &content, &extraData))
return false;
MOZ_ASSERT(tag > SCTAG_TRANSFER_MAP_PENDING_ENTRY);
}
@ -2831,51 +2821,15 @@ JSAutoStructuredCloneBuffer::operator=(JSAutoStructuredCloneBuffer&& other)
}
void
JSAutoStructuredCloneBuffer::clear(const JSStructuredCloneCallbacks* optionalCallbacks,
void* optionalClosure)
JSAutoStructuredCloneBuffer::clear()
{
if (!data_.Size())
return;
const JSStructuredCloneCallbacks* callbacks =
optionalCallbacks ? optionalCallbacks : data_.callbacks_;
void* closure = optionalClosure ? optionalClosure : data_.closure_;
if (data_.ownTransferables_ == OwnTransferablePolicy::OwnsTransferablesIfAny)
DiscardTransferables(data_, callbacks, closure);
data_.discardTransferables();
data_.ownTransferables_ = OwnTransferablePolicy::NoTransferables;
data_.refsHeld_.releaseAll();
data_.Clear();
version_ = 0;
}
bool
JSAutoStructuredCloneBuffer::copy(JSContext* cx, const JSStructuredCloneData& srcData,
uint32_t version, const JSStructuredCloneCallbacks* callbacks,
void* closure)
{
// transferable objects cannot be copied
if (StructuredCloneHasTransferObjects(srcData))
return false;
clear();
auto iter = srcData.Iter();
while (!iter.Done()) {
if (!data_.WriteBytes(iter.Data(), iter.RemainingInSegment()))
return false;
iter.Advance(srcData, iter.RemainingInSegment());
}
version_ = version;
if (!data_.refsHeld_.acquireAll(cx, srcData.refsHeld_))
return false;
data_.setOptionalCallbacks(callbacks, closure, OwnTransferablePolicy::NoTransferables);
return true;
}
void
JSAutoStructuredCloneBuffer::adopt(JSStructuredCloneData&& data, uint32_t version,
const JSStructuredCloneCallbacks* callbacks,
@ -2884,7 +2838,7 @@ JSAutoStructuredCloneBuffer::adopt(JSStructuredCloneData&& data, uint32_t versio
clear();
data_ = Move(data);
version_ = version;
data_.setOptionalCallbacks(callbacks, closure, OwnTransferablePolicy::OwnsTransferablesIfAny);
data_.setCallbacks(callbacks, closure, OwnTransferablePolicy::OwnsTransferablesIfAny);
}
void
@ -2901,7 +2855,7 @@ JSAutoStructuredCloneBuffer::steal(JSStructuredCloneData* data, uint32_t* versio
*data = Move(data_);
version_ = 0;
data_.setOptionalCallbacks(nullptr, nullptr, OwnTransferablePolicy::NoTransferables);
data_.setCallbacks(nullptr, nullptr, OwnTransferablePolicy::NoTransferables);
}
bool

View File

@ -160,7 +160,6 @@ using mozilla::dom::HTMLMediaElementBinding::HAVE_METADATA;
#define WEBKIT_PREFIXES_ENABLED_PREF_NAME "layout.css.prefixes.webkit"
#define TEXT_ALIGN_UNSAFE_ENABLED_PREF_NAME "layout.css.text-align-unsafe-value.enabled"
#define FLOAT_LOGICAL_VALUES_ENABLED_PREF_NAME "layout.css.float-logical-values.enabled"
#define INTERCHARACTER_RUBY_ENABLED_PREF_NAME "layout.css.ruby.intercharacter.enabled"
#define CONTENT_SELECT_ENABLED_PREF_NAME "dom.select_popup_in_content.enabled"
@ -315,61 +314,6 @@ TextAlignUnsafeEnabledPrefChangeCallback(const char* aPrefName, void* aClosure)
isTextAlignUnsafeEnabled ? eCSSKeyword_unsafe : eCSSKeyword_UNKNOWN;
}
// When the pref "layout.css.float-logical-values.enabled" changes, this
// function is called to let us update kFloatKTable & kClearKTable,
// to selectively disable or restore the entries for logical values
// (inline-start and inline-end) in those tables.
static void
FloatLogicalValuesEnabledPrefChangeCallback(const char* aPrefName,
void* aClosure)
{
NS_ASSERTION(strcmp(aPrefName, FLOAT_LOGICAL_VALUES_ENABLED_PREF_NAME) == 0,
"Did you misspell " FLOAT_LOGICAL_VALUES_ENABLED_PREF_NAME " ?");
static bool sIsInitialized;
static int32_t sIndexOfInlineStartInFloatTable;
static int32_t sIndexOfInlineEndInFloatTable;
static int32_t sIndexOfInlineStartInClearTable;
static int32_t sIndexOfInlineEndInClearTable;
bool isFloatLogicalValuesEnabled =
Preferences::GetBool(FLOAT_LOGICAL_VALUES_ENABLED_PREF_NAME, false);
if (!sIsInitialized) {
// First run: find the position of "inline-start" in kFloatKTable.
sIndexOfInlineStartInFloatTable =
nsCSSProps::FindIndexOfKeyword(eCSSKeyword_inline_start,
nsCSSProps::kFloatKTable);
// First run: find the position of "inline-end" in kFloatKTable.
sIndexOfInlineEndInFloatTable =
nsCSSProps::FindIndexOfKeyword(eCSSKeyword_inline_end,
nsCSSProps::kFloatKTable);
// First run: find the position of "inline-start" in kClearKTable.
sIndexOfInlineStartInClearTable =
nsCSSProps::FindIndexOfKeyword(eCSSKeyword_inline_start,
nsCSSProps::kClearKTable);
// First run: find the position of "inline-end" in kClearKTable.
sIndexOfInlineEndInClearTable =
nsCSSProps::FindIndexOfKeyword(eCSSKeyword_inline_end,
nsCSSProps::kClearKTable);
sIsInitialized = true;
}
// OK -- now, stomp on or restore the logical entries in the keyword tables,
// depending on whether the pref is enabled vs. disabled.
MOZ_ASSERT(sIndexOfInlineStartInFloatTable >= 0);
nsCSSProps::kFloatKTable[sIndexOfInlineStartInFloatTable].mKeyword =
isFloatLogicalValuesEnabled ? eCSSKeyword_inline_start : eCSSKeyword_UNKNOWN;
MOZ_ASSERT(sIndexOfInlineEndInFloatTable >= 0);
nsCSSProps::kFloatKTable[sIndexOfInlineEndInFloatTable].mKeyword =
isFloatLogicalValuesEnabled ? eCSSKeyword_inline_end : eCSSKeyword_UNKNOWN;
MOZ_ASSERT(sIndexOfInlineStartInClearTable >= 0);
nsCSSProps::kClearKTable[sIndexOfInlineStartInClearTable].mKeyword =
isFloatLogicalValuesEnabled ? eCSSKeyword_inline_start : eCSSKeyword_UNKNOWN;
MOZ_ASSERT(sIndexOfInlineEndInClearTable >= 0);
nsCSSProps::kClearKTable[sIndexOfInlineEndInClearTable].mKeyword =
isFloatLogicalValuesEnabled ? eCSSKeyword_inline_end : eCSSKeyword_UNKNOWN;
}
template<typename TestType>
static bool
HasMatchingAnimations(EffectSet* aEffects, TestType&& aTest)
@ -8217,8 +8161,6 @@ static const PrefCallbacks kPrefCallbacks[] = {
WebkitPrefixEnabledPrefChangeCallback },
{ TEXT_ALIGN_UNSAFE_ENABLED_PREF_NAME,
TextAlignUnsafeEnabledPrefChangeCallback },
{ FLOAT_LOGICAL_VALUES_ENABLED_PREF_NAME,
FloatLogicalValuesEnabledPrefChangeCallback },
};
/* static */

View File

@ -110,11 +110,9 @@ public:
*/
void AddSizeOfExcludingThis(nsWindowSizes& aWindowSizes) const;
#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
void Check() {
mPool.Check();
}
#endif
private:
void* Allocate(uint32_t aCode, size_t aSize);

View File

@ -2781,10 +2781,26 @@ ComputeClipForMaskItem(nsDisplayListBuilder* aBuilder, nsIFrame* aMaskedFrame,
return Nothing();
}
struct AutoCheckBuilder {
explicit AutoCheckBuilder(nsDisplayListBuilder* aBuilder)
: mBuilder(aBuilder)
{
aBuilder->Check();
}
~AutoCheckBuilder()
{
mBuilder->Check();
}
nsDisplayListBuilder* mBuilder;
};
void
nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder,
nsDisplayList* aList,
bool* aCreatedContainerItem) {
AutoCheckBuilder check(aBuilder);
if (GetStateBits() & NS_FRAME_TOO_DEEP_IN_FRAME_TREE)
return;
@ -3069,7 +3085,9 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder,
true);
MarkAbsoluteFramesForDisplayList(aBuilder);
aBuilder->Check();
BuildDisplayList(aBuilder, set);
aBuilder->Check();
// Blend modes are a real pain for retained display lists. We build a blend
// container item if the built list contains any blend mode items within
@ -3117,7 +3135,9 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder,
}
MarkAbsoluteFramesForDisplayList(aBuilder);
aBuilder->Check();
BuildDisplayList(aBuilder, set);
aBuilder->Check();
}
}
@ -3515,6 +3535,7 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder,
nsIFrame* aChild,
const nsDisplayListSet& aLists,
uint32_t aFlags) {
AutoCheckBuilder check(aBuilder);
// If painting is restricted to just the background of the top level frame,
// then we have nothing to do here.
if (aBuilder->IsBackgroundOnly())
@ -3577,7 +3598,9 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder,
child->MarkAbsoluteFramesForDisplayList(aBuilder);
aBuilder->AdjustWindowDraggingRegion(child);
aBuilder->Check();
child->BuildDisplayList(aBuilder, aLists);
aBuilder->Check();
aBuilder->DisplayCaret(child, aLists.Content());
#ifdef DEBUG
DisplayDebugBorders(aBuilder, child, aLists);
@ -3825,7 +3848,9 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder,
differentAGR);
aBuilder->AdjustWindowDraggingRegion(child);
aBuilder->Check();
child->BuildDisplayList(aBuilder, aLists);
aBuilder->Check();
aBuilder->DisplayCaret(child, aLists.Content());
#ifdef DEBUG
DisplayDebugBorders(aBuilder, child, aLists);
@ -3845,7 +3870,9 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder,
aBuilder->AdjustWindowDraggingRegion(child);
nsDisplayListBuilder::AutoContainerASRTracker contASRTracker(aBuilder);
aBuilder->Check();
child->BuildDisplayList(aBuilder, pseudoStack);
aBuilder->Check();
if (aBuilder->DisplayCaret(child, pseudoStack.Content())) {
canSkipWrapList = false;
}

View File

@ -711,9 +711,7 @@ public:
void RecomputeCurrentAnimatedGeometryRoot();
void Check() {
#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
mPool.Check();
#endif
}
/**

View File

@ -96,10 +96,10 @@ fuzzy-if(OSX==1010,28,7) == orthogonal-floats-1b.html orthogonal-floats-1-ref.ht
fuzzy-if(OSX==1010,103,802) fuzzy-if(winWidget,135,700) == orthogonal-floats-1c.html orthogonal-floats-1-ref.html
fuzzy-if(OSX==1010,103,802) fuzzy-if(winWidget,135,700) == orthogonal-floats-1d.html orthogonal-floats-1-ref.html
pref(layout.css.float-logical-values.enabled,true) == logical-float-side-1.html logical-float-side-1-ref.html
pref(layout.css.float-logical-values.enabled,true) == logical-float-side-2.html logical-float-side-2-ref.html
pref(layout.css.float-logical-values.enabled,true) == logical-float-side-3.html logical-float-side-3-ref.html
pref(layout.css.float-logical-values.enabled,true) == logical-float-side-4.html logical-float-side-4-ref.html
== logical-float-side-1.html logical-float-side-1-ref.html
== logical-float-side-2.html logical-float-side-2-ref.html
== logical-float-side-3.html logical-float-side-3-ref.html
== logical-float-side-4.html logical-float-side-4-ref.html
== float-in-rtl-slr-1a.html float-in-rtl-slr-1-ref.html
== float-in-rtl-slr-1b.html float-in-rtl-slr-1-ref.html

View File

@ -845,7 +845,7 @@ const KTableEntry nsCSSProps::kCaptionSideKTable[] = {
{ eCSSKeyword_UNKNOWN, -1 }
};
KTableEntry nsCSSProps::kClearKTable[] = {
const KTableEntry nsCSSProps::kClearKTable[] = {
{ eCSSKeyword_none, StyleClear::None },
{ eCSSKeyword_left, StyleClear::Left },
{ eCSSKeyword_right, StyleClear::Right },
@ -1277,7 +1277,7 @@ const KTableEntry nsCSSProps::kHyphensKTable[] = {
{ eCSSKeyword_UNKNOWN, -1 }
};
KTableEntry nsCSSProps::kFloatKTable[] = {
const KTableEntry nsCSSProps::kFloatKTable[] = {
{ eCSSKeyword_none, StyleFloat::None },
{ eCSSKeyword_left, StyleFloat::Left },
{ eCSSKeyword_right, StyleFloat::Right },

View File

@ -552,9 +552,7 @@ public:
static const KTableEntry kBoxShadowTypeKTable[];
static const KTableEntry kBoxSizingKTable[];
static const KTableEntry kCaptionSideKTable[];
// Not const because we modify its entries when the pref
// "layout.css.float-logical-values.enabled" changes:
static KTableEntry kClearKTable[];
static const KTableEntry kClearKTable[];
static const KTableEntry kColorKTable[];
static const KTableEntry kContentKTable[];
static const KTableEntry kControlCharacterVisibilityKTable[];
@ -583,9 +581,7 @@ public:
// ------------------------------------------------------------------
static const KTableEntry kFlexDirectionKTable[];
static const KTableEntry kFlexWrapKTable[];
// Not const because we modify its entries when the pref
// "layout.css.float-logical-values.enabled" changes:
static KTableEntry kFloatKTable[];
static const KTableEntry kFloatKTable[];
static const KTableEntry kFloatEdgeKTable[];
static const KTableEntry kFontDisplayKTable[];
static const KTableEntry kFontKTable[];

View File

@ -3320,7 +3320,7 @@ var gCSSProperties = {
inherited: false,
type: CSS_TYPE_LONGHAND,
initial_values: [ "none" ],
other_values: [ "left", "right", "both" ],
other_values: [ "left", "right", "both", "inline-start", "inline-end" ],
invalid_values: []
},
"clip": {
@ -3480,7 +3480,7 @@ var gCSSProperties = {
type: CSS_TYPE_LONGHAND,
applies_to_first_letter: true,
initial_values: [ "none" ],
other_values: [ "left", "right" ],
other_values: [ "left", "right", "inline-start", "inline-end" ],
invalid_values: []
},
"font": {
@ -8132,18 +8132,6 @@ if (IsCSSPropertyPrefEnabled("layout.css.text-align-unsafe-value.enabled")) {
gCSSProperties["text-align"].invalid_values.push("true left");
}
if (IsCSSPropertyPrefEnabled("layout.css.float-logical-values.enabled")) {
gCSSProperties["float"].other_values.push("inline-start");
gCSSProperties["float"].other_values.push("inline-end");
gCSSProperties["clear"].other_values.push("inline-start");
gCSSProperties["clear"].other_values.push("inline-end");
} else {
gCSSProperties["float"].invalid_values.push("inline-start");
gCSSProperties["float"].invalid_values.push("inline-end");
gCSSProperties["clear"].invalid_values.push("inline-start");
gCSSProperties["clear"].invalid_values.push("inline-end");
}
gCSSProperties["display"].other_values.push("flow-root");
// Copy aliased properties' fields from their alias targets.

View File

@ -2878,10 +2878,6 @@ pref("layout.css.text-align-unsafe-value.enabled", false);
// Is support for CSS text-justify property enabled?
pref("layout.css.text-justify.enabled", true);
// Is support for CSS "float: inline-{start,end}" and
// "clear: inline-{start,end}" enabled?
pref("layout.css.float-logical-values.enabled", true);
// Is support for the CSS4 image-orientation property enabled?
pref("layout.css.image-orientation.enabled", true);

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -11,8 +11,11 @@ use std::io::{BufRead, BufReader, Write};
use std::path::Path;
fn main() {
// https://github.com/rust-lang/cargo/issues/3544
let style_out_dir = env::var_os("DEP_FOR SOME REASON THE LINKS KEY IS REQUIRED \
TO PASS DATA AROUND BETWEEN BUILD SCRIPTS_OUT_DIR").unwrap();
let root_path = Path::new("../../../");
let bindings_file = root_path.join("components/style/gecko/generated/bindings.rs");
let bindings_file = Path::new(&style_out_dir).join("gecko/bindings.rs");
let glue_file = root_path.join("ports/geckolib/glue.rs");
println!("cargo:rerun-if-changed=build.rs");
@ -70,9 +73,6 @@ fn main() {
}
}
// https://github.com/rust-lang/cargo/issues/3544
let style_out_dir = env::var_os("DEP_FOR SOME REASON THE LINKS KEY IS REQUIRED \
TO PASS DATA AROUND BETWEEN BUILD SCRIPTS_OUT_DIR").unwrap();
File::create(out_dir.join("bindings.rs"))
.unwrap()
.write_all(format!("include!(concat!({:?}, \"/gecko/structs.rs\"));",

View File

@ -179,6 +179,15 @@ class MochitestRunner(MozbuildObject):
return runtestsremote.run_test_harness(parser, options)
def run_geckoview_junit_test(self, context, **kwargs):
host_ret = verify_host_bin()
if host_ret != 0:
return host_ret
import runjunit
options = Namespace(**kwargs)
return runjunit.run_test_harness(parser, options)
def run_robocop_test(self, context, tests, suite=None, **kwargs):
host_ret = verify_host_bin()
if host_ret != 0:
@ -241,6 +250,38 @@ def setup_argument_parser():
return parser
def setup_junit_argument_parser():
build_obj = MozbuildObject.from_environment(cwd=here)
build_path = os.path.join(build_obj.topobjdir, 'build')
if build_path not in sys.path:
sys.path.append(build_path)
mochitest_dir = os.path.join(build_obj.topobjdir, '_tests', 'testing', 'mochitest')
with warnings.catch_warnings():
warnings.simplefilter('ignore')
# runtests.py contains MochitestDesktop, required by runjunit
import imp
path = os.path.join(build_obj.topobjdir, mochitest_dir, 'runtests.py')
if not os.path.exists(path):
path = os.path.join(here, "runtests.py")
with open(path, 'r') as fh:
imp.load_module('mochitest', fh, path,
('.py', 'r', imp.PY_SOURCE))
import runjunit
from mozrunner.devices.android_device import verify_android_device
verify_android_device(build_obj, install=False, xre=True)
global parser
parser = runjunit.JunitArgumentParser()
return parser
# condition filters
def is_buildapp_in(*apps):
@ -441,6 +482,40 @@ class MachCommands(MachCommandBase):
return overall
@CommandProvider
class GeckoviewJunitCommands(MachCommandBase):
@Command('geckoview-junit', category='testing',
conditions=[conditions.is_android],
description='Run remote geckoview junit tests.',
parser=setup_junit_argument_parser)
def run_junit(self, **kwargs):
self._ensure_state_subdir_exists('.')
from mozrunner.devices.android_device import (grant_runtime_permissions,
get_adb_path,
verify_android_device)
# verify installation
app = kwargs.get('app')
device_serial = kwargs.get('deviceSerial')
verify_android_device(self, install=True, xre=False, app=app,
device_serial=device_serial)
grant_runtime_permissions(self, app, device_serial=device_serial)
if not kwargs.get('adbPath'):
kwargs['adbPath'] = get_adb_path(self)
if not kwargs.get('log'):
from mozlog.commandline import setup_logging
format_args = {'level': self._mach_context.settings['test']['level']}
default_format = self._mach_context.settings['test']['format']
kwargs['log'] = setup_logging('mach-mochitest', kwargs,
{default_format: sys.stdout}, format_args)
mochitest = self._spawn(MochitestRunner)
return mochitest.run_geckoview_junit_test(self._mach_context, **kwargs)
@CommandProvider
class RobocopCommands(MachCommandBase):

View File

@ -56,6 +56,7 @@ TEST_HARNESS_FILES.testing.mochitest += [
'pywebsocket_wrapper.py',
'redirect.html',
'rungeckoview.py',
'runjunit.py',
'runrobocop.py',
'runtests.py',
'runtestsremote.py',

View File

@ -147,9 +147,9 @@ class GeckoviewTestRunner:
expected = 'PASS'
(passed, message) = test()
if passed:
pass_count = pass_count + 1
pass_count += 1
else:
fail_count = fail_count + 1
fail_count += 1
status = 'PASS' if passed else 'FAIL'
self.log.test_end(self.test_name, status, expected, message)
@ -195,7 +195,7 @@ class GeckoviewTestRunner:
try:
shutil.rmtree(dump_dir)
except Exception:
self.log.warn("unable to remove directory: %s" % dump_dir)
self.log.warning("unable to remove directory: %s" % dump_dir)
return crashed
def cleanup(self):

View File

@ -0,0 +1,405 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
import argparse
import json
import os
import posixpath
import re
import shutil
import sys
import tempfile
import traceback
import mozcrash
import mozinfo
import mozlog
import moznetwork
from mozdevice import ADBAndroid
from mozprofile import Profile, Preferences, DEFAULT_PORTS
from runtests import MochitestDesktop, update_mozinfo
here = os.path.abspath(os.path.dirname(__file__))
try:
from mozbuild.base import (
MozbuildObject,
MachCommandConditions as conditions,
)
build_obj = MozbuildObject.from_environment(cwd=here)
except ImportError:
build_obj = None
conditions = None
class JUnitTestRunner(MochitestDesktop):
"""
A test harness to run geckoview junit tests on a remote device.
"""
def __init__(self, log, options):
self.log = log
verbose = False
if options.log_tbpl_level == 'debug' or options.log_mach_level == 'debug':
verbose = True
self.device = ADBAndroid(adb=options.adbPath or 'adb',
device=options.deviceSerial,
test_root=options.remoteTestRoot,
verbose=verbose)
self.options = options
self.log.debug("options=%s" % vars(options))
update_mozinfo()
self.remote_profile = posixpath.join(self.device.test_root, 'junit-profile')
self.server_init()
self.cleanup()
self.device.clear_logcat()
self.build_profile()
self.startServers(
self.options,
debuggerInfo=None,
ignoreSSLTunnelExts=True)
self.log.debug("Servers started")
def server_init(self):
"""
Additional initialization required to satisfy MochitestDesktop.startServers
"""
self._locations = None
self.server = None
self.wsserver = None
self.websocketProcessBridge = None
self.SERVER_STARTUP_TIMEOUT = 180 if mozinfo.info.get('debug') else 90
if self.options.remoteWebServer is None:
if os.name != "nt":
self.options.remoteWebServer = moznetwork.get_ip()
else:
raise Exception("--remote-webserver must be specified")
self.options.webServer = self.options.remoteWebServer
self.options.webSocketPort = '9988'
self.options.httpdPath = None
self.options.keep_open = False
self.options.pidFile = ""
self.options.subsuite = None
self.options.xrePath = None
if build_obj and 'MOZ_HOST_BIN' in os.environ:
self.options.xrePath = os.environ['MOZ_HOST_BIN']
if not self.options.utilityPath:
self.options.utilityPath = self.options.xrePath
if not self.options.xrePath:
self.options.xrePath = self.options.utilityPath
if build_obj:
self.options.certPath = os.path.join(build_obj.topsrcdir,
'build', 'pgo', 'certs')
def build_profile(self):
"""
Create a local profile with test prefs and proxy definitions and
push it to the remote device.
"""
preferences = [
os.path.join(
here,
'profile_data',
'prefs_general.js')]
prefs = {}
for path in preferences:
prefs.update(Preferences.read_prefs(path))
interpolation = {
"server": "%s:%s" %
(self.options.webServer, self.options.httpPort)}
prefs = json.loads(json.dumps(prefs) % interpolation)
for pref in prefs:
prefs[pref] = Preferences.cast(prefs[pref])
proxy = {'remote': self.options.webServer,
'http': self.options.httpPort,
'https': self.options.sslPort,
'ws': self.options.sslPort
}
self.profile = Profile(preferences=prefs, proxy=proxy)
self.options.profilePath = self.profile.profile
if self.fillCertificateDB(self.options):
self.log.error("Certificate integration failed")
self.device.mkdir(self.remote_profile, parents=True)
self.device.push(self.profile.profile, self.remote_profile)
self.log.debug("profile %s -> %s" %
(str(self.profile.profile), str(self.remote_profile)))
def cleanup(self):
try:
self.stopServers()
self.log.debug("Servers stopped")
self.device.stop_application(self.options.app)
self.device.rm(self.remote_profile, force=True, recursive=True)
if hasattr(self, 'profile'):
del self.profile
except Exception:
traceback.print_exc()
self.log.info("Caught and ignored an exception during cleanup")
def build_command_line(self, test_filters):
"""
Construct and return the 'am instrument' command line.
"""
cmd = "am instrument -w -r"
# profile location
cmd = cmd + " -e args '-profile %s'" % self.remote_profile
# multi-process
e10s = 'true' if self.options.e10s else 'false'
cmd = cmd + " -e use_multiprocess %s" % e10s
# test filters: limit run to specific test(s)
for f in test_filters:
# filter can be class-name or 'class-name#method-name' (single test)
cmd = cmd + " -e class %s" % f
# environment
env = {}
env["MOZ_CRASHREPORTER"] = "1"
env["MOZ_CRASHREPORTER_NO_REPORT"] = "1"
env["MOZ_CRASHREPORTER_SHUTDOWN"] = "1"
env["XPCOM_DEBUG_BREAK"] = "stack"
env["DISABLE_UNSAFE_CPOW_WARNINGS"] = "1"
env["MOZ_DISABLE_NONLOCAL_CONNECTIONS"] = "1"
env["MOZ_IN_AUTOMATION"] = "1"
env["R_LOG_VERBOSE"] = "1"
env["R_LOG_LEVEL"] = "6"
env["R_LOG_DESTINATION"] = "stderr"
for (env_count, (env_key, env_val)) in enumerate(env.iteritems()):
cmd = cmd + " -e env%d %s=%s" % (env_count, env_key, env_val)
# runner
cmd = cmd + " %s/%s" % (self.options.app, self.options.runner)
return cmd
def run_tests(self, test_filters=None):
"""
Run the tests.
"""
if not self.device.is_app_installed(self.options.app):
raise Exception("%s is not installed" %
self.options.app)
if self.device.process_exist(self.options.app):
raise Exception("%s already running before starting tests" %
self.options.app)
self.test_started = False
self.pass_count = 0
self.fail_count = 0
self.class_name = ""
self.test_name = ""
self.current_full_name = ""
def callback(line):
# Output callback: Parse the raw junit log messages, translating into
# treeherder-friendly test start/pass/fail messages.
self.log.process_output(self.options.app, str(line))
expected = 'PASS'
# Expect per-test info like: "INSTRUMENTATION_STATUS: class=something"
match = re.match(r'INSTRUMENTATION_STATUS:\s*class=(.*)', line)
if match:
self.class_name = match.group(1)
# Expect per-test info like: "INSTRUMENTATION_STATUS: test=something"
match = re.match(r'INSTRUMENTATION_STATUS:\s*test=(.*)', line)
if match:
self.test_name = match.group(1)
# Expect per-test info like: "INSTRUMENTATION_STATUS_CODE: 0|1|..."
match = re.match(r'INSTRUMENTATION_STATUS_CODE:\s*([+-]?\d+)', line)
if match:
status = match.group(1)
full_name = "%s.%s" % (self.class_name, self.test_name)
if full_name == self.current_full_name:
if status == '0':
message = ''
status = 'PASS'
self.pass_count += 1
else:
message = 'status %s' % status
status = 'FAIL'
self.fail_count += 1
self.log.test_end(full_name, status, expected, message)
self.test_started = False
else:
if self.test_started:
# next test started without reporting previous status
self.fail_count += 1
self.log.test_end(self.current_full_name, 'FAIL', expected,
"missing test completion status")
self.log.test_start(full_name)
self.test_started = True
self.current_full_name = full_name
# Ideally all test names should be reported to suite_start, but these test
# names are not known in advance.
self.log.suite_start(["geckoview-junit"])
try:
cmd = self.build_command_line(test_filters)
self.log.info("launching %s" % cmd)
self.device.shell(cmd, timeout=self.options.max_time, stdout_callback=callback)
self.log.info("Passed: %d" % self.pass_count)
self.log.info("Failed: %d" % self.fail_count)
finally:
self.log.suite_end()
if self.check_for_crashes():
self.fail_count = 1
return 1 if self.fail_count else 0
def check_for_crashes(self):
logcat = self.device.get_logcat()
if logcat:
if mozcrash.check_for_java_exception(logcat, self.current_full_name):
return True
symbols_path = self.options.symbolsPath
try:
dump_dir = tempfile.mkdtemp()
remote_dir = posixpath.join(self.remote_profile, 'minidumps')
if not self.device.is_dir(remote_dir):
# If crash reporting is enabled (MOZ_CRASHREPORTER=1), the
# minidumps directory is automatically created when the app
# (first) starts, so its lack of presence is a hint that
# something went wrong.
print "Automation Error: No crash directory (%s) found on remote device" % \
remote_dir
return True
self.device.pull(remote_dir, dump_dir)
crashed = mozcrash.log_crashes(self.log, dump_dir, symbols_path,
test=self.current_full_name)
finally:
try:
shutil.rmtree(dump_dir)
except Exception:
self.log.warning("unable to remove directory: %s" % dump_dir)
return crashed
class JunitArgumentParser(argparse.ArgumentParser):
"""
An argument parser for geckoview-junit.
"""
def __init__(self, **kwargs):
super(JunitArgumentParser, self).__init__(**kwargs)
self.add_argument("--appname",
action="store",
type=str,
dest="app",
default="org.mozilla.geckoview.test",
help="Test package name.")
self.add_argument("--adbpath",
action="store",
type=str,
dest="adbPath",
default=None,
help="Path to adb executable.")
self.add_argument("--deviceSerial",
action="store",
type=str,
dest="deviceSerial",
help="adb serial number of remote device.")
self.add_argument("--remoteTestRoot",
action="store",
type=str,
dest="remoteTestRoot",
help="Remote directory to use as test root "
"(eg. /mnt/sdcard/tests or /data/local/tests).")
self.add_argument("--disable-e10s",
action="store_false",
dest="e10s",
default=True,
help="Disable multiprocess mode in test app.")
self.add_argument("--max-time",
action="store",
type=str,
dest="max_time",
default="2400",
help="Max time in seconds to wait for tests (default 2400s).")
self.add_argument("--runner",
action="store",
type=str,
dest="runner",
default="android.support.test.runner.AndroidJUnitRunner",
help="Test runner name.")
self.add_argument("--symbols-path",
action="store",
type=str,
dest="symbolsPath",
default=None,
help="Path to directory containing breakpad symbols, "
"or the URL of a zip file containing symbols.")
self.add_argument("--utility-path",
action="store",
type=str,
dest="utilityPath",
default=None,
help="Path to directory containing host utility programs.")
# Additional options for server.
self.add_argument("--certificate-path",
action="store",
type=str,
dest="certPath",
default=None,
help="Path to directory containing certificate store."),
self.add_argument("--http-port",
action="store",
type=str,
dest="httpPort",
default=DEFAULT_PORTS['http'],
help="Port of the web server for http traffic.")
self.add_argument("--remote-webserver",
action="store",
type=str,
dest="remoteWebServer",
help="IP address of the webserver.")
self.add_argument("--ssl-port",
action="store",
type=str,
dest="sslPort",
default=DEFAULT_PORTS['https'],
help="Port of the web server for https traffic.")
# Remaining arguments are test filters.
self.add_argument("test_filters",
nargs="*",
help="Test filter(s): class and/or method names of test(s) to run.")
mozlog.commandline.add_logging_group(self)
def run_test_harness(parser, options):
if hasattr(options, 'log'):
log = options.log
else:
log = mozlog.commandline.setup_logging("runjunit", options,
{"tbpl": sys.stdout})
runner = JUnitTestRunner(log, options)
result = -1
try:
result = runner.run_tests(options.test_filters)
except KeyboardInterrupt:
log.info("runjunit.py | Received keyboard interrupt")
result = -1
except Exception:
traceback.print_exc()
log.error(
"runjunit.py | Received unexpected exception while running tests")
result = 1
finally:
runner.cleanup()
return result
def main(args=sys.argv[1:]):
parser = JunitArgumentParser()
options = parser.parse_args()
return run_test_harness(parser, options)
if __name__ == "__main__":
sys.exit(main())

View File

@ -622,12 +622,10 @@ AddonManagerStartup::EncodeBlob(JS::HandleValue value, JSContext* cx, JS::Mutabl
nsAutoCString scData;
auto& data = holder.Data();
auto iter = data.Iter();
while (!iter.Done()) {
scData.Append(nsDependentCSubstring(iter.Data(), iter.RemainingInSegment()));
iter.Advance(data, iter.RemainingInSegment());
}
holder.Data().ForEachDataChunk([&](const char* aData, size_t aSize) {
scData.Append(nsDependentCSubstring(aData, aSize));
return true;
});
nsCString lz4;
MOZ_TRY_VAR(lz4, EncodeLZ4(scData, STRUCTURED_CLONE_MAGIC));

View File

@ -7,16 +7,20 @@
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm", {});
const PREFS = {
"pluginFlashBlockingCheckbox": "plugins.flashBlock.enabled",
"pluginEnableProtectedModeCheckbox": "dom.ipc.plugins.flash.disable-protected-mode",
"pluginFlashBlockingCheckbox":
{ pref: "plugins.flashBlock.enabled", invert: false },
"pluginEnableProtectedModeCheckbox":
{ pref: "dom.ipc.plugins.flash.disable-protected-mode", invert: true },
};
function init() {
for (let id of Object.keys(PREFS)) {
let checkbox = document.getElementById(id);
checkbox.checked = Services.prefs.getBoolPref(PREFS[id]);
var prefVal = Services.prefs.getBoolPref(PREFS[id].pref);
checkbox.checked = PREFS[id].invert ? !prefVal : prefVal;
checkbox.addEventListener("command", () => {
Services.prefs.setBoolPref(PREFS[id], checkbox.checked);
Services.prefs.setBoolPref(PREFS[id].pref,
PREFS[id].invert ? !checkbox.checked : checkbox.checked);
});
}
}

View File

@ -22,6 +22,7 @@ public:
static bool OnElCapitanOrLater();
static bool OnSierraOrLater();
static bool OnHighSierraOrLater();
static bool OnSierraExactly();
static bool IsAtLeastVersion(int32_t aMajor, int32_t aMinor, int32_t aBugFix=0);
@ -41,9 +42,9 @@ private:
static int32_t mOSXVersion;
};
// C-callable helper for cairo-quartz-font.c
// C-callable helper for cairo-quartz-font.c and SkFontHost_mac.cpp
extern "C" {
bool Gecko_OnSierraOrLater();
bool Gecko_OnSierraExactly();
}
#endif // nsCocoaFeatures_h_

View File

@ -174,11 +174,18 @@ nsCocoaFeatures::OnHighSierraOrLater()
return (OSXVersion() >= MAC_OS_X_VERSION_10_13_HEX);
}
/* Version of OnSierraOrLater as a global function callable from C (cairo) */
bool
Gecko_OnSierraOrLater()
/* static */ bool
nsCocoaFeatures::OnSierraExactly()
{
return nsCocoaFeatures::OnSierraOrLater();
return (OSXVersion() >= MAC_OS_X_VERSION_10_12_HEX) &&
(OSXVersion() < MAC_OS_X_VERSION_10_13_HEX);
}
/* Version of OnSierraExactly as global function callable from cairo & skia */
bool
Gecko_OnSierraExactly()
{
return nsCocoaFeatures::OnSierraExactly();
}
/* static */ bool

View File

@ -221,8 +221,13 @@ bool nsWindow::OnPaint(HDC aDC, uint32_t aNestingLevel)
return true;
}
PAINTSTRUCT ps;
// Avoid starting the GPU process for the initial navigator:blank window.
if (mIsEarlyBlankWindow) {
// Call BeginPaint/EndPaint or Windows will keep sending us messages.
::BeginPaint(mWnd, &ps);
::EndPaint(mWnd, &ps);
return true;
}
@ -233,8 +238,6 @@ bool nsWindow::OnPaint(HDC aDC, uint32_t aNestingLevel)
}
mLastPaintBounds = mBounds;
PAINTSTRUCT ps;
#ifdef MOZ_XUL
if (!aDC && (eTransparencyTransparent == mTransparencyMode))
{

View File

@ -125,14 +125,12 @@ public:
}
#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
void Check()
{
for (auto arena = mHead.next; arena; arena = arena->next) {
arena->canary.Check();
if (mCurrent) {
mCurrent->canary.Check();
}
}
#endif
private:
struct ArenaHeader