зеркало из https://github.com/mozilla/gecko-dev.git
Merge inbound to central, a=merge
MozReview-Commit-ID: 5H1ZxSV0XuM
This commit is contained in:
Коммит
672c83ed65
|
@ -31,6 +31,7 @@ support-files =
|
|||
[browser_cubic-bezier-04.js]
|
||||
[browser_cubic-bezier-05.js]
|
||||
[browser_cubic-bezier-06.js]
|
||||
[browser_cubic-bezier-07.js]
|
||||
[browser_filter-editor-01.js]
|
||||
[browser_filter-editor-02.js]
|
||||
[browser_filter-editor-03.js]
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
// Tests that changing the cubic-bezier curve in the widget does change the dot animation
|
||||
// preview too.
|
||||
|
||||
const {CubicBezierWidget} = require("devtools/client/shared/widgets/CubicBezierWidget");
|
||||
const {PREDEFINED} = require("devtools/client/shared/widgets/CubicBezierPresets");
|
||||
|
||||
const TEST_URI = `data:text/html,<div id="cubic-bezier-container" />`;
|
||||
|
||||
add_task(function* () {
|
||||
let [host,, doc] = yield createHost("bottom", TEST_URI);
|
||||
|
||||
let container = doc.querySelector("#cubic-bezier-container");
|
||||
let w = new CubicBezierWidget(container, PREDEFINED.linear);
|
||||
|
||||
yield previewDotReactsToChanges(w, [0.6, -0.28, 0.74, 0.05]);
|
||||
yield previewDotReactsToChanges(w, [0.9, 0.03, 0.69, 0.22]);
|
||||
yield previewDotReactsToChanges(w, [0.68, -0.55, 0.27, 1.55]);
|
||||
yield previewDotReactsToChanges(w, PREDEFINED.ease, "ease");
|
||||
yield previewDotReactsToChanges(w, PREDEFINED["ease-in-out"], "ease-in-out");
|
||||
|
||||
w.destroy();
|
||||
host.destroy();
|
||||
});
|
||||
|
||||
function* previewDotReactsToChanges(widget, coords, expectedEasing) {
|
||||
let onUpdated = widget.once("updated");
|
||||
widget.coordinates = coords;
|
||||
yield onUpdated;
|
||||
|
||||
let animatedDot = widget.timingPreview.dot;
|
||||
let animations = animatedDot.getAnimations();
|
||||
|
||||
if (!expectedEasing) {
|
||||
expectedEasing =
|
||||
`cubic-bezier(${coords[0]}, ${coords[1]}, ${coords[2]}, ${coords[3]})`;
|
||||
}
|
||||
|
||||
is(animations.length, 1, "The dot is animated");
|
||||
|
||||
let goingToRight = animations[0].effect.getKeyframes()[2];
|
||||
is(goingToRight.easing, expectedEasing,
|
||||
`The easing when going to the right was set correctly to ${coords}`);
|
||||
|
||||
let goingToLeft = animations[0].effect.getKeyframes()[6];
|
||||
is(goingToLeft.easing, expectedEasing,
|
||||
`The easing when going to the left was set correctly to ${coords}`);
|
||||
}
|
|
@ -418,7 +418,7 @@ CubicBezierWidget.prototype = {
|
|||
this.bezierCanvas.plot();
|
||||
this.emit("updated", this.bezierCanvas.bezier);
|
||||
|
||||
this.timingPreview.preview(this.bezierCanvas.bezier + "");
|
||||
this.timingPreview.preview(this.bezierCanvas.bezier.toString());
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -721,7 +721,6 @@ CubicBezierPresetWidget.prototype = {
|
|||
*/
|
||||
function TimingFunctionPreviewWidget(parent) {
|
||||
this.previousValue = null;
|
||||
this.autoRestartAnimation = null;
|
||||
|
||||
this.parent = parent;
|
||||
this._initMarkup();
|
||||
|
@ -748,7 +747,7 @@ TimingFunctionPreviewWidget.prototype = {
|
|||
},
|
||||
|
||||
destroy: function () {
|
||||
clearTimeout(this.autoRestartAnimation);
|
||||
this.dot.getAnimations().forEach(anim => anim.cancel());
|
||||
this.parent.querySelector(".timing-function-preview").remove();
|
||||
this.parent = this.dot = null;
|
||||
},
|
||||
|
@ -765,35 +764,42 @@ TimingFunctionPreviewWidget.prototype = {
|
|||
return;
|
||||
}
|
||||
|
||||
clearTimeout(this.autoRestartAnimation);
|
||||
|
||||
if (parseTimingFunction(value)) {
|
||||
this.dot.style.animationTimingFunction = value;
|
||||
this.restartAnimation();
|
||||
this.restartAnimation(value);
|
||||
}
|
||||
|
||||
this.previousValue = value;
|
||||
},
|
||||
|
||||
/**
|
||||
* Re-start the preview animation from the beginning
|
||||
* Re-start the preview animation from the beginning.
|
||||
* @param {String} timingFunction The value for the timing-function.
|
||||
*/
|
||||
restartAnimation: function () {
|
||||
// Just toggling the class won't do it unless there's a sync reflow
|
||||
this.dot.animate([
|
||||
{ left: "-7px", offset: 0 },
|
||||
{ left: "143px", offset: 0.25 },
|
||||
{ left: "143px", offset: 0.5 },
|
||||
{ left: "-7px", offset: 0.75 },
|
||||
{ left: "-7px", offset: 1 }
|
||||
], {
|
||||
duration: (this.PREVIEW_DURATION * 2),
|
||||
fill: "forwards"
|
||||
});
|
||||
restartAnimation: function (timingFunction) {
|
||||
// Cancel the previous animation if there was any.
|
||||
this.dot.getAnimations().forEach(anim => anim.cancel());
|
||||
|
||||
// Restart it again after a while
|
||||
this.autoRestartAnimation = setTimeout(this.restartAnimation.bind(this),
|
||||
this.PREVIEW_DURATION * 2);
|
||||
// And start the new one.
|
||||
// The animation consists of a few keyframes that move the dot to the right of the
|
||||
// container, and then move it back to the left.
|
||||
// It also contains some pause where the dot is semi transparent, before it moves to
|
||||
// the right, and once again, before it comes back to the left.
|
||||
// The timing function passed to this function is applied to the keyframes that
|
||||
// actually move the dot. This way it can be previewed in both direction, instead of
|
||||
// being spread over the whole animation.
|
||||
this.dot.animate([
|
||||
{ left: "-7px", opacity: .5, offset: 0 },
|
||||
{ left: "-7px", opacity: .5, offset: .19 },
|
||||
{ left: "-7px", opacity: 1, offset: .2, easing: timingFunction },
|
||||
{ left: "143px", opacity: 1, offset: .5 },
|
||||
{ left: "143px", opacity: .5, offset: .51 },
|
||||
{ left: "143px", opacity: .5, offset: .7 },
|
||||
{ left: "143px", opacity: 1, offset: .71, easing: timingFunction },
|
||||
{ left: "-7px", opacity: 1, offset: 1 }
|
||||
], {
|
||||
duration: this.PREVIEW_DURATION * 2,
|
||||
iterations: Infinity
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -50,3 +50,4 @@ DEPRECATED_OPERATION(PrefixedFullscreenAPI)
|
|||
DEPRECATED_OPERATION(LenientSetter)
|
||||
DEPRECATED_OPERATION(FileLastModifiedDate)
|
||||
DEPRECATED_OPERATION(ImageBitmapRenderingContext_TransferImageBitmap)
|
||||
DEPRECATED_OPERATION(URLCreateObjectURL_MediaStream)
|
||||
|
|
|
@ -3618,8 +3618,15 @@ DeprecationWarning(JSContext* aCx, JSObject* aObject,
|
|||
return;
|
||||
}
|
||||
|
||||
DeprecationWarning(global, aOperation);
|
||||
}
|
||||
|
||||
void
|
||||
DeprecationWarning(const GlobalObject& aGlobal,
|
||||
nsIDocument::DeprecatedOperations aOperation)
|
||||
{
|
||||
if (NS_IsMainThread()) {
|
||||
nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(global.GetAsSupports());
|
||||
nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aGlobal.GetAsSupports());
|
||||
if (window && window->GetExtantDoc()) {
|
||||
window->GetExtantDoc()->WarnOnceAbout(aOperation);
|
||||
}
|
||||
|
@ -3627,7 +3634,7 @@ DeprecationWarning(JSContext* aCx, JSObject* aObject,
|
|||
return;
|
||||
}
|
||||
|
||||
WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx);
|
||||
WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aGlobal.Context());
|
||||
if (!workerPrivate) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -3195,6 +3195,10 @@ void
|
|||
DeprecationWarning(JSContext* aCx, JSObject* aObject,
|
||||
nsIDocument::DeprecatedOperations aOperation);
|
||||
|
||||
void
|
||||
DeprecationWarning(const GlobalObject& aGlobal,
|
||||
nsIDocument::DeprecatedOperations aOperation);
|
||||
|
||||
// A callback to perform funToString on an interface object
|
||||
JSString*
|
||||
InterfaceObjectToString(JSContext* aCx, JS::Handle<JSObject*> aObject,
|
||||
|
|
|
@ -59,7 +59,7 @@ skip-if = toolkit == 'android'
|
|||
[test_browserElement_inproc_AudioChannelSeeking.html]
|
||||
tags = audiochannel
|
||||
[test_browserElement_inproc_AudioPlayback.html]
|
||||
skip-if = toolkit == 'android' # bug 1332850
|
||||
skip-if = true # bug 1332850, bug 1332862
|
||||
[test_browserElement_inproc_AudioChannel.html]
|
||||
tags = audiochannel
|
||||
[test_browserElement_inproc_AudioChannel_nested.html]
|
||||
|
|
|
@ -0,0 +1,201 @@
|
|||
/* 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/. */
|
||||
|
||||
#ifndef BasicRenderingContext2D_h
|
||||
#define BasicRenderingContext2D_h
|
||||
|
||||
#include "mozilla/dom/CanvasRenderingContext2DBinding.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class HTMLImageElementOrHTMLCanvasElementOrHTMLVideoElementOrImageBitmap;
|
||||
typedef HTMLImageElementOrHTMLCanvasElementOrHTMLVideoElementOrImageBitmap
|
||||
CanvasImageSource;
|
||||
|
||||
/*
|
||||
* BasicRenderingContext2D
|
||||
*/
|
||||
class BasicRenderingContext2D
|
||||
{
|
||||
public:
|
||||
//
|
||||
// CanvasState
|
||||
//
|
||||
virtual void Save() = 0;
|
||||
virtual void Restore() = 0;
|
||||
|
||||
//
|
||||
// CanvasTransform
|
||||
//
|
||||
virtual void Scale(double aX, double aY, mozilla::ErrorResult& aError) = 0;
|
||||
virtual void Rotate(double aAngle, mozilla::ErrorResult& aError) = 0;
|
||||
virtual void Translate(double aX,
|
||||
double aY,
|
||||
mozilla::ErrorResult& aError) = 0;
|
||||
virtual void Transform(double aM11,
|
||||
double aM12,
|
||||
double aM21,
|
||||
double aM22,
|
||||
double aDx,
|
||||
double aDy,
|
||||
mozilla::ErrorResult& aError) = 0;
|
||||
virtual void SetTransform(double aM11,
|
||||
double aM12,
|
||||
double aM21,
|
||||
double aM22,
|
||||
double aDx,
|
||||
double aDy,
|
||||
mozilla::ErrorResult& aError) = 0;
|
||||
virtual void ResetTransform(mozilla::ErrorResult& aError) = 0;
|
||||
|
||||
//
|
||||
// CanvasCompositing
|
||||
//
|
||||
virtual double GlobalAlpha() = 0;
|
||||
virtual void SetGlobalAlpha(double aGlobalAlpha) = 0;
|
||||
virtual void GetGlobalCompositeOperation(nsAString& aOp,
|
||||
mozilla::ErrorResult& aError) = 0;
|
||||
virtual void SetGlobalCompositeOperation(const nsAString& aOp,
|
||||
mozilla::ErrorResult& aError) = 0;
|
||||
|
||||
//
|
||||
// CanvasImageSmoothing
|
||||
//
|
||||
virtual bool ImageSmoothingEnabled() = 0;
|
||||
virtual void SetImageSmoothingEnabled(bool aImageSmoothingEnabled) = 0;
|
||||
|
||||
//
|
||||
// CanvasFillStrokeStyles
|
||||
//
|
||||
virtual void GetStrokeStyle(
|
||||
OwningStringOrCanvasGradientOrCanvasPattern& aValue) = 0;
|
||||
virtual void SetStrokeStyle(
|
||||
const StringOrCanvasGradientOrCanvasPattern& aValue) = 0;
|
||||
virtual void GetFillStyle(
|
||||
OwningStringOrCanvasGradientOrCanvasPattern& aValue) = 0;
|
||||
virtual void SetFillStyle(
|
||||
const StringOrCanvasGradientOrCanvasPattern& aValue) = 0;
|
||||
virtual already_AddRefed<CanvasGradient> CreateLinearGradient(double aX0,
|
||||
double aY0,
|
||||
double aX1,
|
||||
double aY1) = 0;
|
||||
virtual already_AddRefed<CanvasGradient> CreateRadialGradient(
|
||||
double aX0,
|
||||
double aY0,
|
||||
double aR0,
|
||||
double aX1,
|
||||
double aY1,
|
||||
double aR1,
|
||||
ErrorResult& aError) = 0;
|
||||
virtual already_AddRefed<CanvasPattern> CreatePattern(
|
||||
const CanvasImageSource& aElement,
|
||||
const nsAString& aRepeat,
|
||||
ErrorResult& aError) = 0;
|
||||
//
|
||||
// CanvasShadowStyles
|
||||
//
|
||||
virtual double ShadowOffsetX() = 0;
|
||||
virtual void SetShadowOffsetX(double aShadowOffsetX) = 0;
|
||||
virtual double ShadowOffsetY() = 0;
|
||||
virtual void SetShadowOffsetY(double aShadowOffsetY) = 0;
|
||||
virtual double ShadowBlur() = 0;
|
||||
virtual void SetShadowBlur(double aShadowBlur) = 0;
|
||||
virtual void GetShadowColor(nsAString& aShadowColor) = 0;
|
||||
virtual void SetShadowColor(const nsAString& aShadowColor) = 0;
|
||||
|
||||
//
|
||||
// CanvasRect
|
||||
//
|
||||
virtual void ClearRect(double aX, double aY, double aW, double aH) = 0;
|
||||
virtual void FillRect(double aX, double aY, double aW, double aH) = 0;
|
||||
virtual void StrokeRect(double aX, double aY, double aW, double aH) = 0;
|
||||
|
||||
//
|
||||
// CanvasDrawImage
|
||||
//
|
||||
virtual void DrawImage(const CanvasImageSource& aImage,
|
||||
double aDx,
|
||||
double aDy,
|
||||
mozilla::ErrorResult& aError) = 0;
|
||||
virtual void DrawImage(const CanvasImageSource& aImage,
|
||||
double aDx,
|
||||
double aDy,
|
||||
double aDw,
|
||||
double aDh,
|
||||
mozilla::ErrorResult& aError) = 0;
|
||||
virtual void DrawImage(const CanvasImageSource& aImage,
|
||||
double aSx,
|
||||
double aSy,
|
||||
double aSw,
|
||||
double aSh,
|
||||
double aDx,
|
||||
double aDy,
|
||||
double aDw,
|
||||
double aDh,
|
||||
mozilla::ErrorResult& aError) = 0;
|
||||
|
||||
//
|
||||
// CanvasPathDrawingStyles
|
||||
//
|
||||
virtual double LineWidth() = 0;
|
||||
virtual void SetLineWidth(double aWidth) = 0;
|
||||
virtual void GetLineCap(nsAString& aLinecapStyle) = 0;
|
||||
virtual void SetLineCap(const nsAString& aLinecapStyle) = 0;
|
||||
virtual void GetLineJoin(nsAString& aLinejoinStyle,
|
||||
mozilla::ErrorResult& aError) = 0;
|
||||
virtual void SetLineJoin(const nsAString& aLinejoinStyle) = 0;
|
||||
virtual double MiterLimit() = 0;
|
||||
virtual void SetMiterLimit(double aMiter) = 0;
|
||||
virtual void SetLineDash(const Sequence<double>& aSegments,
|
||||
mozilla::ErrorResult& aRv) = 0;
|
||||
virtual void GetLineDash(nsTArray<double>& aSegments) const = 0;
|
||||
virtual void SetLineDashOffset(double aOffset) = 0;
|
||||
virtual double LineDashOffset() const = 0;
|
||||
|
||||
//
|
||||
// CanvasPath
|
||||
//
|
||||
virtual void ClosePath() = 0;
|
||||
virtual void MoveTo(double aX, double aY) = 0;
|
||||
virtual void LineTo(double aX, double aY) = 0;
|
||||
virtual void QuadraticCurveTo(double aCpx,
|
||||
double aCpy,
|
||||
double aX,
|
||||
double aY) = 0;
|
||||
virtual void BezierCurveTo(double aCp1x,
|
||||
double aCp1y,
|
||||
double aCp2x,
|
||||
double aCp2y,
|
||||
double aX,
|
||||
double aY) = 0;
|
||||
virtual void ArcTo(double aX1,
|
||||
double aY1,
|
||||
double aX2,
|
||||
double aY2,
|
||||
double aRadius,
|
||||
mozilla::ErrorResult& aError) = 0;
|
||||
virtual void Rect(double aX, double aY, double aW, double aH) = 0;
|
||||
virtual void Arc(double aX,
|
||||
double aY,
|
||||
double aRadius,
|
||||
double aStartAngle,
|
||||
double aEndAngle,
|
||||
bool aAnticlockwise,
|
||||
mozilla::ErrorResult& aError) = 0;
|
||||
virtual void Ellipse(double aX,
|
||||
double aY,
|
||||
double aRadiusX,
|
||||
double aRadiusY,
|
||||
double aRotation,
|
||||
double aStartAngle,
|
||||
double aEndAngle,
|
||||
bool aAnticlockwise,
|
||||
ErrorResult& aError) = 0;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* BasicRenderingContext2D_h */
|
|
@ -15,6 +15,7 @@
|
|||
#include "mozilla/dom/HTMLVideoElement.h"
|
||||
#include "gfxTextRun.h"
|
||||
#include "mozilla/ErrorResult.h"
|
||||
#include "mozilla/dom/BasicRenderingContext2D.h"
|
||||
#include "mozilla/dom/CanvasGradient.h"
|
||||
#include "mozilla/dom/CanvasRenderingContext2DBinding.h"
|
||||
#include "mozilla/dom/CanvasPattern.h"
|
||||
|
@ -61,7 +62,8 @@ class CanvasShutdownObserver;
|
|||
**/
|
||||
class CanvasRenderingContext2D final :
|
||||
public nsICanvasRenderingContextInternal,
|
||||
public nsWrapperCache
|
||||
public nsWrapperCache,
|
||||
public BasicRenderingContext2D
|
||||
{
|
||||
virtual ~CanvasRenderingContext2D();
|
||||
|
||||
|
@ -80,18 +82,18 @@ public:
|
|||
return mCanvasElement->GetOriginalCanvas();
|
||||
}
|
||||
|
||||
void Save();
|
||||
void Restore();
|
||||
void Scale(double aX, double aY, mozilla::ErrorResult& aError);
|
||||
void Rotate(double aAngle, mozilla::ErrorResult& aError);
|
||||
void Translate(double aX, double aY, mozilla::ErrorResult& aError);
|
||||
void Transform(double aM11, double aM12, double aM21, double aM22, double aDx,
|
||||
double aDy, mozilla::ErrorResult& aError);
|
||||
void SetTransform(double aM11, double aM12, double aM21, double aM22, double aDx,
|
||||
double aDy, mozilla::ErrorResult& aError);
|
||||
void ResetTransform(mozilla::ErrorResult& aError);
|
||||
void Save() override;
|
||||
void Restore() override;
|
||||
void Scale(double aX, double aY, mozilla::ErrorResult& aError) override;
|
||||
void Rotate(double aAngle, mozilla::ErrorResult& aError) override;
|
||||
void Translate(double aX, double aY, mozilla::ErrorResult& aError) override;
|
||||
void Transform(double aM11, double aM12, double aM21, double aM22,
|
||||
double aDx, double aDy, mozilla::ErrorResult& aError) override;
|
||||
void SetTransform(double aM11, double aM12, double aM21, double aM22,
|
||||
double aDx, double aDy, mozilla::ErrorResult& aError) override;
|
||||
void ResetTransform(mozilla::ErrorResult& aError) override;
|
||||
|
||||
double GlobalAlpha()
|
||||
double GlobalAlpha() override
|
||||
{
|
||||
return CurrentState().globalAlpha;
|
||||
}
|
||||
|
@ -99,79 +101,85 @@ public:
|
|||
// Useful for silencing cast warnings
|
||||
static mozilla::gfx::Float ToFloat(double aValue) { return mozilla::gfx::Float(aValue); }
|
||||
|
||||
void SetGlobalAlpha(double aGlobalAlpha)
|
||||
void SetGlobalAlpha(double aGlobalAlpha) override
|
||||
{
|
||||
if (aGlobalAlpha >= 0.0 && aGlobalAlpha <= 1.0) {
|
||||
CurrentState().globalAlpha = ToFloat(aGlobalAlpha);
|
||||
}
|
||||
}
|
||||
|
||||
void GetGlobalCompositeOperation(nsAString& aOp, mozilla::ErrorResult& aError);
|
||||
void GetGlobalCompositeOperation(nsAString& aOp,
|
||||
mozilla::ErrorResult& aError) override;
|
||||
void SetGlobalCompositeOperation(const nsAString& aOp,
|
||||
mozilla::ErrorResult& aError);
|
||||
mozilla::ErrorResult& aError) override;
|
||||
|
||||
void GetStrokeStyle(OwningStringOrCanvasGradientOrCanvasPattern& aValue)
|
||||
void
|
||||
GetStrokeStyle(OwningStringOrCanvasGradientOrCanvasPattern& aValue) override
|
||||
{
|
||||
GetStyleAsUnion(aValue, Style::STROKE);
|
||||
}
|
||||
|
||||
void SetStrokeStyle(const StringOrCanvasGradientOrCanvasPattern& aValue)
|
||||
void
|
||||
SetStrokeStyle(const StringOrCanvasGradientOrCanvasPattern& aValue) override
|
||||
{
|
||||
SetStyleFromUnion(aValue, Style::STROKE);
|
||||
}
|
||||
|
||||
void GetFillStyle(OwningStringOrCanvasGradientOrCanvasPattern& aValue)
|
||||
void
|
||||
GetFillStyle(OwningStringOrCanvasGradientOrCanvasPattern& aValue) override
|
||||
{
|
||||
GetStyleAsUnion(aValue, Style::FILL);
|
||||
}
|
||||
|
||||
void SetFillStyle(const StringOrCanvasGradientOrCanvasPattern& aValue)
|
||||
void
|
||||
SetFillStyle(const StringOrCanvasGradientOrCanvasPattern& aValue) override
|
||||
{
|
||||
SetStyleFromUnion(aValue, Style::FILL);
|
||||
}
|
||||
|
||||
already_AddRefed<CanvasGradient>
|
||||
CreateLinearGradient(double aX0, double aY0, double aX1, double aY1);
|
||||
CreateLinearGradient(double aX0, double aY0, double aX1, double aY1) override;
|
||||
already_AddRefed<CanvasGradient>
|
||||
CreateRadialGradient(double aX0, double aY0, double aR0, double aX1, double aY1,
|
||||
double aR1, ErrorResult& aError);
|
||||
CreateRadialGradient(double aX0, double aY0, double aR0,
|
||||
double aX1, double aY1, double aR1,
|
||||
ErrorResult& aError) override;
|
||||
already_AddRefed<CanvasPattern>
|
||||
CreatePattern(const CanvasImageSource& aElement,
|
||||
const nsAString& aRepeat, ErrorResult& aError);
|
||||
const nsAString& aRepeat, ErrorResult& aError) override;
|
||||
|
||||
double ShadowOffsetX()
|
||||
double ShadowOffsetX() override
|
||||
{
|
||||
return CurrentState().shadowOffset.x;
|
||||
}
|
||||
|
||||
void SetShadowOffsetX(double aShadowOffsetX)
|
||||
void SetShadowOffsetX(double aShadowOffsetX) override
|
||||
{
|
||||
CurrentState().shadowOffset.x = ToFloat(aShadowOffsetX);
|
||||
}
|
||||
|
||||
double ShadowOffsetY()
|
||||
double ShadowOffsetY() override
|
||||
{
|
||||
return CurrentState().shadowOffset.y;
|
||||
}
|
||||
|
||||
void SetShadowOffsetY(double aShadowOffsetY)
|
||||
void SetShadowOffsetY(double aShadowOffsetY) override
|
||||
{
|
||||
CurrentState().shadowOffset.y = ToFloat(aShadowOffsetY);
|
||||
}
|
||||
|
||||
double ShadowBlur()
|
||||
double ShadowBlur() override
|
||||
{
|
||||
return CurrentState().shadowBlur;
|
||||
}
|
||||
|
||||
void SetShadowBlur(double aShadowBlur)
|
||||
void SetShadowBlur(double aShadowBlur) override
|
||||
{
|
||||
if (aShadowBlur >= 0.0) {
|
||||
CurrentState().shadowBlur = ToFloat(aShadowBlur);
|
||||
}
|
||||
}
|
||||
|
||||
void GetShadowColor(nsAString& aShadowColor)
|
||||
void GetShadowColor(nsAString& aShadowColor) override
|
||||
{
|
||||
StyleColorToString(CurrentState().shadowColor, aShadowColor);
|
||||
}
|
||||
|
@ -181,11 +189,11 @@ public:
|
|||
aFilter = CurrentState().filterString;
|
||||
}
|
||||
|
||||
void SetShadowColor(const nsAString& aShadowColor);
|
||||
void SetShadowColor(const nsAString& aShadowColor) override;
|
||||
void SetFilter(const nsAString& aFilter, mozilla::ErrorResult& aError);
|
||||
void ClearRect(double aX, double aY, double aW, double aH);
|
||||
void FillRect(double aX, double aY, double aW, double aH);
|
||||
void StrokeRect(double aX, double aY, double aW, double aH);
|
||||
void ClearRect(double aX, double aY, double aW, double aH) override;
|
||||
void FillRect(double aX, double aY, double aW, double aH) override;
|
||||
void StrokeRect(double aX, double aY, double aW, double aH) override;
|
||||
void BeginPath();
|
||||
void Fill(const CanvasWindingRule& aWinding);
|
||||
void Fill(const CanvasPath& aPath, const CanvasWindingRule& aWinding);
|
||||
|
@ -212,22 +220,22 @@ public:
|
|||
void RemoveHitRegion(const nsAString& aId);
|
||||
void ClearHitRegions();
|
||||
|
||||
void DrawImage(const CanvasImageSource& aImage,
|
||||
double aDx, double aDy, mozilla::ErrorResult& aError)
|
||||
void DrawImage(const CanvasImageSource& aImage, double aDx, double aDy,
|
||||
mozilla::ErrorResult& aError) override
|
||||
{
|
||||
DrawImage(aImage, 0.0, 0.0, 0.0, 0.0, aDx, aDy, 0.0, 0.0, 0, aError);
|
||||
}
|
||||
|
||||
void DrawImage(const CanvasImageSource& aImage,
|
||||
double aDx, double aDy, double aDw, double aDh,
|
||||
mozilla::ErrorResult& aError)
|
||||
void DrawImage(const CanvasImageSource& aImage, double aDx, double aDy,
|
||||
double aDw, double aDh, mozilla::ErrorResult& aError) override
|
||||
{
|
||||
DrawImage(aImage, 0.0, 0.0, 0.0, 0.0, aDx, aDy, aDw, aDh, 2, aError);
|
||||
}
|
||||
|
||||
void DrawImage(const CanvasImageSource& aImage,
|
||||
double aSx, double aSy, double aSw, double aSh, double aDx,
|
||||
double aDy, double aDw, double aDh, mozilla::ErrorResult& aError)
|
||||
double aSx, double aSy, double aSw, double aSh,
|
||||
double aDx, double aDy, double aDw, double aDh,
|
||||
mozilla::ErrorResult& aError) override
|
||||
{
|
||||
DrawImage(aImage, aSx, aSy, aSw, aSh, aDx, aDy, aDw, aDh, 6, aError);
|
||||
}
|
||||
|
@ -248,28 +256,29 @@ public:
|
|||
double aDirtyWidth, double aDirtyHeight,
|
||||
mozilla::ErrorResult& aError);
|
||||
|
||||
double LineWidth()
|
||||
double LineWidth() override
|
||||
{
|
||||
return CurrentState().lineWidth;
|
||||
}
|
||||
|
||||
void SetLineWidth(double aWidth)
|
||||
void SetLineWidth(double aWidth) override
|
||||
{
|
||||
if (aWidth > 0.0) {
|
||||
CurrentState().lineWidth = ToFloat(aWidth);
|
||||
}
|
||||
}
|
||||
void GetLineCap(nsAString& aLinecapStyle);
|
||||
void SetLineCap(const nsAString& aLinecapStyle);
|
||||
void GetLineJoin(nsAString& aLinejoinStyle, mozilla::ErrorResult& aError);
|
||||
void SetLineJoin(const nsAString& aLinejoinStyle);
|
||||
void GetLineCap(nsAString& aLinecapStyle) override;
|
||||
void SetLineCap(const nsAString& aLinecapStyle) override;
|
||||
void GetLineJoin(nsAString& aLinejoinStyle,
|
||||
mozilla::ErrorResult& aError) override;
|
||||
void SetLineJoin(const nsAString& aLinejoinStyle) override;
|
||||
|
||||
double MiterLimit()
|
||||
double MiterLimit() override
|
||||
{
|
||||
return CurrentState().miterLimit;
|
||||
}
|
||||
|
||||
void SetMiterLimit(double aMiter)
|
||||
void SetMiterLimit(double aMiter) override
|
||||
{
|
||||
if (aMiter > 0.0) {
|
||||
CurrentState().miterLimit = ToFloat(aMiter);
|
||||
|
@ -287,7 +296,7 @@ public:
|
|||
void GetTextBaseline(nsAString& aTextBaseline);
|
||||
void SetTextBaseline(const nsAString& aTextBaseline);
|
||||
|
||||
void ClosePath()
|
||||
void ClosePath() override
|
||||
{
|
||||
EnsureWritablePath();
|
||||
|
||||
|
@ -298,7 +307,7 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
void MoveTo(double aX, double aY)
|
||||
void MoveTo(double aX, double aY) override
|
||||
{
|
||||
EnsureWritablePath();
|
||||
|
||||
|
@ -310,14 +319,14 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
void LineTo(double aX, double aY)
|
||||
void LineTo(double aX, double aY) override
|
||||
{
|
||||
EnsureWritablePath();
|
||||
|
||||
LineTo(mozilla::gfx::Point(ToFloat(aX), ToFloat(aY)));
|
||||
}
|
||||
|
||||
void QuadraticCurveTo(double aCpx, double aCpy, double aX, double aY)
|
||||
void QuadraticCurveTo(double aCpx, double aCpy, double aX, double aY) override
|
||||
{
|
||||
EnsureWritablePath();
|
||||
|
||||
|
@ -333,7 +342,8 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
void BezierCurveTo(double aCp1x, double aCp1y, double aCp2x, double aCp2y, double aX, double aY)
|
||||
void BezierCurveTo(double aCp1x, double aCp1y, double aCp2x, double aCp2y,
|
||||
double aX, double aY) override
|
||||
{
|
||||
EnsureWritablePath();
|
||||
|
||||
|
@ -342,14 +352,15 @@ public:
|
|||
mozilla::gfx::Point(ToFloat(aX), ToFloat(aY)));
|
||||
}
|
||||
|
||||
void ArcTo(double aX1, double aY1, double aX2, double aY2, double aRadius,
|
||||
mozilla::ErrorResult& aError);
|
||||
void Rect(double aX, double aY, double aW, double aH);
|
||||
void ArcTo(double aX1, double aY1, double aX2, double aY2,
|
||||
double aRadius, mozilla::ErrorResult& aError) override;
|
||||
void Rect(double aX, double aY, double aW, double aH) override;
|
||||
void Arc(double aX, double aY, double aRadius, double aStartAngle,
|
||||
double aEndAngle, bool aAnticlockwise, mozilla::ErrorResult& aError);
|
||||
double aEndAngle, bool aAnticlockwise,
|
||||
mozilla::ErrorResult& aError) override;
|
||||
void Ellipse(double aX, double aY, double aRadiusX, double aRadiusY,
|
||||
double aRotation, double aStartAngle, double aEndAngle,
|
||||
bool aAnticlockwise, ErrorResult& aError);
|
||||
bool aAnticlockwise, ErrorResult& aError) override;
|
||||
|
||||
void GetMozCurrentTransform(JSContext* aCx,
|
||||
JS::MutableHandle<JSObject*> aResult,
|
||||
|
@ -367,11 +378,11 @@ public:
|
|||
void SetFillRule(const nsAString& aFillRule);
|
||||
|
||||
void SetLineDash(const Sequence<double>& aSegments,
|
||||
mozilla::ErrorResult& aRv);
|
||||
void GetLineDash(nsTArray<double>& aSegments) const;
|
||||
mozilla::ErrorResult& aRv) override;
|
||||
void GetLineDash(nsTArray<double>& aSegments) const override;
|
||||
|
||||
void SetLineDashOffset(double aOffset);
|
||||
double LineDashOffset() const;
|
||||
void SetLineDashOffset(double aOffset) override;
|
||||
double LineDashOffset() const override;
|
||||
|
||||
void GetMozTextStyle(nsAString& aMozTextStyle)
|
||||
{
|
||||
|
@ -384,12 +395,12 @@ public:
|
|||
SetFont(aMozTextStyle, aError);
|
||||
}
|
||||
|
||||
bool ImageSmoothingEnabled()
|
||||
bool ImageSmoothingEnabled() override
|
||||
{
|
||||
return CurrentState().imageSmoothingEnabled;
|
||||
}
|
||||
|
||||
void SetImageSmoothingEnabled(bool aImageSmoothingEnabled)
|
||||
void SetImageSmoothingEnabled(bool aImageSmoothingEnabled) override
|
||||
{
|
||||
if (aImageSmoothingEnabled != CurrentState().imageSmoothingEnabled) {
|
||||
CurrentState().imageSmoothingEnabled = aImageSmoothingEnabled;
|
||||
|
|
|
@ -55,6 +55,7 @@ EXPORTS.mozilla.ipc += [
|
|||
]
|
||||
|
||||
EXPORTS.mozilla.dom += [
|
||||
'BasicRenderingContext2D.h',
|
||||
'CanvasGradient.h',
|
||||
'CanvasPath.h',
|
||||
'CanvasPattern.h',
|
||||
|
|
|
@ -6671,6 +6671,13 @@ class DatabaseFile final
|
|||
{
|
||||
friend class Database;
|
||||
|
||||
// mBlobImpl's ownership lifecycle:
|
||||
// - Initialized on the background thread at creation time. Then
|
||||
// responsibility is handed off to the connection thread.
|
||||
// - Checked and used by the connection thread to generate a stream to write
|
||||
// the blob to disk by an add/put operation.
|
||||
// - Cleared on the connection thread once the file has successfully been
|
||||
// written to disk.
|
||||
RefPtr<BlobImpl> mBlobImpl;
|
||||
RefPtr<FileInfo> mFileInfo;
|
||||
|
||||
|
@ -6685,16 +6692,6 @@ public:
|
|||
return mFileInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is there a blob available to provide an input stream? This may be called
|
||||
* on any thread under current lifecycle constraints.
|
||||
*/
|
||||
bool
|
||||
HasBlobImpl() const
|
||||
{
|
||||
return (bool)mBlobImpl;
|
||||
}
|
||||
|
||||
/**
|
||||
* If mBlobImpl is non-null (implying the contents of this file have not yet
|
||||
* been written to disk), then return an input stream that is guaranteed to
|
||||
|
@ -6734,10 +6731,17 @@ public:
|
|||
already_AddRefed<nsIInputStream>
|
||||
GetBlockingInputStream(ErrorResult &rv) const;
|
||||
|
||||
/**
|
||||
* To be called upon successful copying of the stream GetBlockingInputStream()
|
||||
* returned so that we won't try and redundantly write the file to disk in the
|
||||
* future. This is a separate step from GetBlockingInputStream() because
|
||||
* the write could fail due to quota errors that happen now but that might
|
||||
* not happen in a future attempt.
|
||||
*/
|
||||
void
|
||||
ClearInputStream()
|
||||
WriteSucceededClearBlobImpl()
|
||||
{
|
||||
AssertIsOnBackgroundThread();
|
||||
MOZ_ASSERT(!IsOnBackgroundThread());
|
||||
|
||||
mBlobImpl = nullptr;
|
||||
}
|
||||
|
@ -6773,8 +6777,8 @@ private:
|
|||
already_AddRefed<nsIInputStream>
|
||||
DatabaseFile::GetBlockingInputStream(ErrorResult &rv) const
|
||||
{
|
||||
// We should only be called from a database I/O thread, not the background
|
||||
// thread. The background thread should call HasBlobImpl().
|
||||
// We should only be called from our DB connection thread, not the background
|
||||
// thread.
|
||||
MOZ_ASSERT(!IsOnBackgroundThread());
|
||||
|
||||
if (!mBlobImpl) {
|
||||
|
@ -8320,8 +8324,6 @@ class ObjectStoreAddOrPutRequestOp final
|
|||
|
||||
FallibleTArray<StoredFileInfo> mStoredFileInfos;
|
||||
|
||||
RefPtr<FileManager> mFileManager;
|
||||
|
||||
Key mResponse;
|
||||
const nsCString mGroup;
|
||||
const nsCString mOrigin;
|
||||
|
@ -8361,11 +8363,9 @@ struct ObjectStoreAddOrPutRequestOp::StoredFileInfo final
|
|||
// still have a stream for us to write.
|
||||
nsCOMPtr<nsIInputStream> mInputStream;
|
||||
StructuredCloneFile::FileType mType;
|
||||
bool mCopiedSuccessfully;
|
||||
|
||||
StoredFileInfo()
|
||||
: mType(StructuredCloneFile::eBlob)
|
||||
, mCopiedSuccessfully(false)
|
||||
{
|
||||
AssertIsOnBackgroundThread();
|
||||
|
||||
|
@ -26155,10 +26155,6 @@ ObjectStoreAddOrPutRequestOp::Init(TransactionBase* aTransaction)
|
|||
return false;
|
||||
}
|
||||
|
||||
RefPtr<FileManager> fileManager =
|
||||
aTransaction->GetDatabase()->GetFileManager();
|
||||
MOZ_ASSERT(fileManager);
|
||||
|
||||
for (uint32_t index = 0; index < count; index++) {
|
||||
const FileAddInfo& fileAddInfo = fileAddInfos[index];
|
||||
|
||||
|
@ -26185,10 +26181,6 @@ ObjectStoreAddOrPutRequestOp::Init(TransactionBase* aTransaction)
|
|||
storedFileInfo->mFileInfo = storedFileInfo->mFileActor->GetFileInfo();
|
||||
MOZ_ASSERT(storedFileInfo->mFileInfo);
|
||||
|
||||
if (storedFileInfo->mFileActor->HasBlobImpl() && !mFileManager) {
|
||||
mFileManager = fileManager;
|
||||
}
|
||||
|
||||
storedFileInfo->mType = StructuredCloneFile::eBlob;
|
||||
break;
|
||||
}
|
||||
|
@ -26222,10 +26214,6 @@ ObjectStoreAddOrPutRequestOp::Init(TransactionBase* aTransaction)
|
|||
storedFileInfo->mFileInfo = storedFileInfo->mFileActor->GetFileInfo();
|
||||
MOZ_ASSERT(storedFileInfo->mFileInfo);
|
||||
|
||||
if (storedFileInfo->mFileActor->HasBlobImpl() && !mFileManager) {
|
||||
mFileManager = fileManager;
|
||||
}
|
||||
|
||||
storedFileInfo->mType = fileAddInfo.type();
|
||||
break;
|
||||
}
|
||||
|
@ -26240,12 +26228,11 @@ ObjectStoreAddOrPutRequestOp::Init(TransactionBase* aTransaction)
|
|||
StoredFileInfo* storedFileInfo = mStoredFileInfos.AppendElement(fallible);
|
||||
MOZ_ASSERT(storedFileInfo);
|
||||
|
||||
if (!mFileManager) {
|
||||
mFileManager = aTransaction->GetDatabase()->GetFileManager();
|
||||
MOZ_ASSERT(mFileManager);
|
||||
}
|
||||
RefPtr<FileManager> fileManager =
|
||||
aTransaction->GetDatabase()->GetFileManager();
|
||||
MOZ_ASSERT(fileManager);
|
||||
|
||||
storedFileInfo->mFileInfo = mFileManager->GetNewFileInfo();
|
||||
storedFileInfo->mFileInfo = fileManager->GetNewFileInfo();
|
||||
|
||||
storedFileInfo->mInputStream =
|
||||
new SCInputStream(mParams.cloneInfo().data().data);
|
||||
|
@ -26262,7 +26249,6 @@ ObjectStoreAddOrPutRequestOp::DoDatabaseWork(DatabaseConnection* aConnection)
|
|||
MOZ_ASSERT(aConnection);
|
||||
aConnection->AssertIsOnConnectionThread();
|
||||
MOZ_ASSERT(aConnection->GetStorageConnection());
|
||||
MOZ_ASSERT_IF(mFileManager, !mStoredFileInfos.IsEmpty());
|
||||
|
||||
PROFILER_LABEL("IndexedDB",
|
||||
"ObjectStoreAddOrPutRequestOp::DoDatabaseWork",
|
||||
|
@ -26439,19 +26425,10 @@ ObjectStoreAddOrPutRequestOp::DoDatabaseWork(DatabaseConnection* aConnection)
|
|||
}
|
||||
}
|
||||
|
||||
Maybe<FileHelper> fileHelper;
|
||||
|
||||
if (mFileManager) {
|
||||
fileHelper.emplace(mFileManager);
|
||||
|
||||
rv = fileHelper->Init();
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
IDB_REPORT_INTERNAL_ERR();
|
||||
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
||||
}
|
||||
}
|
||||
|
||||
if (!mStoredFileInfos.IsEmpty()) {
|
||||
// Moved outside the loop to allow it to be cached when demanded by the
|
||||
// first write. (We may have mStoredFileInfos without any required writes.)
|
||||
Maybe<FileHelper> fileHelper;
|
||||
nsAutoString fileIds;
|
||||
|
||||
for (uint32_t count = mStoredFileInfos.Length(), index = 0;
|
||||
|
@ -26465,8 +26442,7 @@ ObjectStoreAddOrPutRequestOp::DoDatabaseWork(DatabaseConnection* aConnection)
|
|||
// MUST be non-null.
|
||||
// - This is a reference to a Blob that may or may not have already been
|
||||
// written to disk. storedFileInfo.mFileActor MUST be non-null, but
|
||||
// its HasBlobImpl() may return false and so GetBlockingInputStream may
|
||||
// return null (so don't assert on them).
|
||||
// its GetBlockingInputStream may return null (so don't assert on them).
|
||||
// - It's a mutable file. No writing will be performed.
|
||||
MOZ_ASSERT(storedFileInfo.mInputStream || storedFileInfo.mFileActor ||
|
||||
storedFileInfo.mType == StructuredCloneFile::eMutableFile);
|
||||
|
@ -26485,6 +26461,19 @@ ObjectStoreAddOrPutRequestOp::DoDatabaseWork(DatabaseConnection* aConnection)
|
|||
}
|
||||
|
||||
if (inputStream) {
|
||||
if (fileHelper.isNothing()) {
|
||||
RefPtr<FileManager> fileManager =
|
||||
Transaction()->GetDatabase()->GetFileManager();
|
||||
MOZ_ASSERT(fileManager);
|
||||
|
||||
fileHelper.emplace(fileManager);
|
||||
rv = fileHelper->Init();
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
IDB_REPORT_INTERNAL_ERR();
|
||||
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
||||
}
|
||||
}
|
||||
|
||||
RefPtr<FileInfo>& fileInfo = storedFileInfo.mFileInfo;
|
||||
|
||||
nsCOMPtr<nsIFile> file = fileHelper->GetFile(fileInfo);
|
||||
|
@ -26521,7 +26510,9 @@ ObjectStoreAddOrPutRequestOp::DoDatabaseWork(DatabaseConnection* aConnection)
|
|||
return rv;
|
||||
}
|
||||
|
||||
storedFileInfo.mCopiedSuccessfully = true;
|
||||
if (storedFileInfo.mFileActor) {
|
||||
storedFileInfo.mFileActor->WriteSucceededClearBlobImpl();
|
||||
}
|
||||
}
|
||||
|
||||
if (index) {
|
||||
|
@ -26605,23 +26596,7 @@ ObjectStoreAddOrPutRequestOp::Cleanup()
|
|||
{
|
||||
AssertIsOnOwningThread();
|
||||
|
||||
if (!mStoredFileInfos.IsEmpty()) {
|
||||
for (uint32_t count = mStoredFileInfos.Length(), index = 0;
|
||||
index < count;
|
||||
index++) {
|
||||
StoredFileInfo& storedFileInfo = mStoredFileInfos[index];
|
||||
|
||||
MOZ_ASSERT_IF(storedFileInfo.mType == StructuredCloneFile::eMutableFile,
|
||||
!storedFileInfo.mCopiedSuccessfully);
|
||||
|
||||
RefPtr<DatabaseFile>& fileActor = storedFileInfo.mFileActor;
|
||||
if (fileActor && storedFileInfo.mCopiedSuccessfully) {
|
||||
fileActor->ClearInputStream();
|
||||
}
|
||||
}
|
||||
|
||||
mStoredFileInfos.Clear();
|
||||
}
|
||||
|
||||
NormalTransactionOp::Cleanup();
|
||||
}
|
||||
|
|
|
@ -321,3 +321,5 @@ LargeAllocationNonE10S=A Large-Allocation header was ignored due to the document
|
|||
GeolocationInsecureRequestIsForbidden=A Geolocation request can only be fulfilled in a secure context.
|
||||
# LOCALIZATION NOTE: Do not translate "Large-Allocation", as it is a literal header name.
|
||||
LargeAllocationNonWin32=This page would be loaded in a new process due to a Large-Allocation header, however Large-Allocation process creation is disabled on non-Win32 platforms.
|
||||
# LOCALIZATION NOTE: Do not translate URL.createObjectURL(MediaStream).
|
||||
URLCreateObjectURL_MediaStream=URL.createObjectURL(MediaStream) is deprecated and will be removed soon.
|
||||
|
|
|
@ -7,18 +7,6 @@
|
|||
#ifndef WMF_H_
|
||||
#define WMF_H_
|
||||
|
||||
#if WINVER < _WIN32_WINNT_WIN7
|
||||
#error \
|
||||
You must include WMF.h before including mozilla headers, \
|
||||
otherwise mozconfig.h will be included \
|
||||
and that sets WINVER to WinXP, \
|
||||
which makes Windows Media Foundation unavailable.
|
||||
#endif
|
||||
|
||||
#pragma push_macro("WINVER")
|
||||
#undef WINVER
|
||||
#define WINVER _WIN32_WINNT_WIN7
|
||||
|
||||
#include <windows.h>
|
||||
#include <mfapi.h>
|
||||
#include <mfidl.h>
|
||||
|
@ -35,7 +23,7 @@ which makes Windows Media Foundation unavailable.
|
|||
#include <codecapi.h>
|
||||
|
||||
// The Windows headers helpfully declare min and max macros, which don't
|
||||
// compile in the prescence of std::min and std::max and unified builds.
|
||||
// compile in the presence of std::min and std::max and unified builds.
|
||||
// So undef them here.
|
||||
#ifdef min
|
||||
#undef min
|
||||
|
@ -97,8 +85,4 @@ HRESULT MFCreateDXGISurfaceBuffer(REFIID riid,
|
|||
} // end namespace wmf
|
||||
} // end namespace mozilla
|
||||
|
||||
|
||||
|
||||
#pragma pop_macro("WINVER")
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1727,6 +1727,9 @@ URL::CreateObjectURL(const GlobalObject& aGlobal, DOMMediaStream& aStream,
|
|||
nsAString& aResult, ErrorResult& aRv)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
DeprecationWarning(aGlobal, nsIDocument::eURLCreateObjectURL_MediaStream);
|
||||
|
||||
URLMainThread::CreateObjectURL(aGlobal, aStream, aResult, aRv);
|
||||
}
|
||||
|
||||
|
|
|
@ -872,17 +872,9 @@ private:
|
|||
|
||||
WorkerPrivate* parentWorker = mWorkerPrivate->GetParent();
|
||||
|
||||
// Figure out which principal to use.
|
||||
nsIPrincipal* principal = mWorkerPrivate->GetPrincipal();
|
||||
nsCOMPtr<nsILoadGroup> loadGroup = mWorkerPrivate->GetLoadGroup();
|
||||
if (!principal) {
|
||||
NS_ASSERTION(parentWorker, "Must have a principal!");
|
||||
NS_ASSERTION(mIsMainScript, "Must have a principal for importScripts!");
|
||||
|
||||
principal = parentWorker->GetPrincipal();
|
||||
loadGroup = parentWorker->GetLoadGroup();
|
||||
}
|
||||
NS_ASSERTION(principal, "This should never be null here!");
|
||||
MOZ_DIAGNOSTIC_ASSERT(principal);
|
||||
MOZ_ASSERT(NS_LoadGroupMatchesPrincipal(loadGroup, principal));
|
||||
|
||||
// Figure out our base URI.
|
||||
|
@ -1137,61 +1129,7 @@ private:
|
|||
// Store the channel info if needed.
|
||||
mWorkerPrivate->InitChannelInfo(channel);
|
||||
|
||||
// Now to figure out which principal to give this worker.
|
||||
WorkerPrivate* parent = mWorkerPrivate->GetParent();
|
||||
|
||||
NS_ASSERTION(mWorkerPrivate->GetPrincipal() || parent,
|
||||
"Must have one of these!");
|
||||
|
||||
nsCOMPtr<nsIPrincipal> loadPrincipal = mWorkerPrivate->GetPrincipal() ?
|
||||
mWorkerPrivate->GetPrincipal() :
|
||||
parent->GetPrincipal();
|
||||
|
||||
nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
|
||||
NS_ASSERTION(ssm, "Should never be null!");
|
||||
|
||||
nsCOMPtr<nsIPrincipal> channelPrincipal;
|
||||
rv = ssm->GetChannelResultPrincipal(channel, getter_AddRefs(channelPrincipal));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsILoadGroup> channelLoadGroup;
|
||||
rv = channel->GetLoadGroup(getter_AddRefs(channelLoadGroup));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
MOZ_ASSERT(channelLoadGroup);
|
||||
|
||||
// If the load principal is the system principal then the channel
|
||||
// principal must also be the system principal (we do not allow chrome
|
||||
// code to create workers with non-chrome scripts, and if we ever decide
|
||||
// to change this we need to make sure we don't always set
|
||||
// mPrincipalIsSystem to true in WorkerPrivate::GetLoadInfo()). Otherwise
|
||||
// this channel principal must be same origin with the load principal (we
|
||||
// check again here in case redirects changed the location of the script).
|
||||
if (nsContentUtils::IsSystemPrincipal(loadPrincipal)) {
|
||||
if (!nsContentUtils::IsSystemPrincipal(channelPrincipal)) {
|
||||
// See if this is a resource URI. Since JSMs usually come from
|
||||
// resource:// URIs we're currently considering all URIs with the
|
||||
// URI_IS_UI_RESOURCE flag as valid for creating privileged workers.
|
||||
bool isResource;
|
||||
rv = NS_URIChainHasFlags(finalURI,
|
||||
nsIProtocolHandler::URI_IS_UI_RESOURCE,
|
||||
&isResource);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (isResource) {
|
||||
// Assign the system principal to the resource:// worker only if it
|
||||
// was loaded from code using the system principal.
|
||||
channelPrincipal = loadPrincipal;
|
||||
} else {
|
||||
return NS_ERROR_DOM_BAD_URI;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The principal can change, but it should still match the original
|
||||
// load group's appId and browser element flag.
|
||||
MOZ_ASSERT(NS_LoadGroupMatchesPrincipal(channelLoadGroup, channelPrincipal));
|
||||
|
||||
mWorkerPrivate->SetPrincipal(channelPrincipal, channelLoadGroup);
|
||||
MOZ_DIAGNOSTIC_ASSERT(mWorkerPrivate->FinalChannelPrincipalIsValid(channel));
|
||||
|
||||
// We did inherit CSP in bug 1223647. If we do not already have a CSP, we
|
||||
// should get it from the HTTP headers on the worker script.
|
||||
|
@ -1239,6 +1177,7 @@ private:
|
|||
}
|
||||
}
|
||||
}
|
||||
WorkerPrivate* parent = mWorkerPrivate->GetParent();
|
||||
if (parent) {
|
||||
// XHR Params Allowed
|
||||
mWorkerPrivate->SetXHRParamsAllowed(parent->XHRParamsAllowed());
|
||||
|
@ -1308,7 +1247,7 @@ private:
|
|||
MOZ_ASSERT(equal);
|
||||
|
||||
mWorkerPrivate->InitChannelInfo(aChannelInfo);
|
||||
mWorkerPrivate->SetPrincipal(responsePrincipal, loadGroup);
|
||||
mWorkerPrivate->SetPrincipalOnMainThread(responsePrincipal, loadGroup);
|
||||
}
|
||||
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
|
@ -1795,17 +1734,17 @@ CacheScriptLoader::OnStreamComplete(nsIStreamLoader* aLoader, nsISupports* aCont
|
|||
class ChannelGetterRunnable final : public WorkerMainThreadRunnable
|
||||
{
|
||||
const nsAString& mScriptURL;
|
||||
nsIChannel** mChannel;
|
||||
WorkerLoadInfo& mLoadInfo;
|
||||
nsresult mResult;
|
||||
|
||||
public:
|
||||
ChannelGetterRunnable(WorkerPrivate* aParentWorker,
|
||||
const nsAString& aScriptURL,
|
||||
nsIChannel** aChannel)
|
||||
WorkerLoadInfo& aLoadInfo)
|
||||
: WorkerMainThreadRunnable(aParentWorker,
|
||||
NS_LITERAL_CSTRING("ScriptLoader :: ChannelGetter"))
|
||||
, mScriptURL(aScriptURL)
|
||||
, mChannel(aChannel)
|
||||
, mLoadInfo(aLoadInfo)
|
||||
, mResult(NS_ERROR_FAILURE)
|
||||
{
|
||||
MOZ_ASSERT(aParentWorker);
|
||||
|
@ -1817,8 +1756,12 @@ public:
|
|||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
nsIPrincipal* principal = mWorkerPrivate->GetPrincipal();
|
||||
MOZ_ASSERT(principal);
|
||||
// Initialize the WorkerLoadInfo principal to our triggering principal
|
||||
// before doing anything else. Normally we do this in the WorkerPrivate
|
||||
// Constructor, but we can't do so off the main thread when creating
|
||||
// a nested worker. So do it here instead.
|
||||
mLoadInfo.mPrincipal = mWorkerPrivate->GetPrincipal();
|
||||
MOZ_ASSERT(mLoadInfo.mPrincipal);
|
||||
|
||||
// Figure out our base URI.
|
||||
nsCOMPtr<nsIURI> baseURI = mWorkerPrivate->GetBaseURI();
|
||||
|
@ -1827,22 +1770,25 @@ public:
|
|||
// May be null.
|
||||
nsCOMPtr<nsIDocument> parentDoc = mWorkerPrivate->GetDocument();
|
||||
|
||||
nsCOMPtr<nsILoadGroup> loadGroup = mWorkerPrivate->GetLoadGroup();
|
||||
mLoadInfo.mLoadGroup = mWorkerPrivate->GetLoadGroup();
|
||||
|
||||
nsCOMPtr<nsIChannel> channel;
|
||||
mResult =
|
||||
scriptloader::ChannelFromScriptURLMainThread(principal, baseURI,
|
||||
parentDoc, loadGroup,
|
||||
scriptloader::ChannelFromScriptURLMainThread(mLoadInfo.mPrincipal,
|
||||
baseURI, parentDoc,
|
||||
mLoadInfo.mLoadGroup,
|
||||
mScriptURL,
|
||||
// Nested workers are always dedicated.
|
||||
nsIContentPolicy::TYPE_INTERNAL_WORKER,
|
||||
// Nested workers use default uri encoding.
|
||||
true,
|
||||
getter_AddRefs(channel));
|
||||
if (NS_SUCCEEDED(mResult)) {
|
||||
channel.forget(mChannel);
|
||||
}
|
||||
NS_ENSURE_SUCCESS(mResult, true);
|
||||
|
||||
mResult = mLoadInfo.SetPrincipalFromChannel(channel);
|
||||
NS_ENSURE_SUCCESS(mResult, true);
|
||||
|
||||
mLoadInfo.mChannel = channel.forget();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -2178,12 +2124,12 @@ nsresult
|
|||
ChannelFromScriptURLWorkerThread(JSContext* aCx,
|
||||
WorkerPrivate* aParent,
|
||||
const nsAString& aScriptURL,
|
||||
nsIChannel** aChannel)
|
||||
WorkerLoadInfo& aLoadInfo)
|
||||
{
|
||||
aParent->AssertIsOnWorkerThread();
|
||||
|
||||
RefPtr<ChannelGetterRunnable> getter =
|
||||
new ChannelGetterRunnable(aParent, aScriptURL, aChannel);
|
||||
new ChannelGetterRunnable(aParent, aScriptURL, aLoadInfo);
|
||||
|
||||
ErrorResult rv;
|
||||
getter->Dispatch(Terminating, rv);
|
||||
|
|
|
@ -46,7 +46,7 @@ nsresult
|
|||
ChannelFromScriptURLWorkerThread(JSContext* aCx,
|
||||
WorkerPrivate* aParent,
|
||||
const nsAString& aScriptURL,
|
||||
nsIChannel** aChannel);
|
||||
WorkerLoadInfo& aLoadInfo);
|
||||
|
||||
void ReportLoadError(ErrorResult& aRv, nsresult aLoadResult,
|
||||
const nsAString& aScriptURL);
|
||||
|
|
|
@ -395,15 +395,7 @@ private:
|
|||
virtual bool
|
||||
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
|
||||
{
|
||||
nsCOMPtr<nsILoadGroup> loadGroupToCancel;
|
||||
mFinishedWorker->ForgetOverridenLoadGroup(loadGroupToCancel);
|
||||
|
||||
nsTArray<nsCOMPtr<nsISupports>> doomed;
|
||||
mFinishedWorker->ForgetMainThreadObjects(doomed);
|
||||
|
||||
RefPtr<MainThreadReleaseRunnable> runnable =
|
||||
new MainThreadReleaseRunnable(doomed, loadGroupToCancel);
|
||||
if (NS_FAILED(mWorkerPrivate->DispatchToMainThread(runnable.forget()))) {
|
||||
if (!mFinishedWorker->ProxyReleaseMainThreadObjects()) {
|
||||
NS_WARNING("Failed to dispatch, going to leak!");
|
||||
}
|
||||
|
||||
|
@ -447,15 +439,7 @@ private:
|
|||
|
||||
runtime->UnregisterWorker(mFinishedWorker);
|
||||
|
||||
nsCOMPtr<nsILoadGroup> loadGroupToCancel;
|
||||
mFinishedWorker->ForgetOverridenLoadGroup(loadGroupToCancel);
|
||||
|
||||
nsTArray<nsCOMPtr<nsISupports> > doomed;
|
||||
mFinishedWorker->ForgetMainThreadObjects(doomed);
|
||||
|
||||
RefPtr<MainThreadReleaseRunnable> runnable =
|
||||
new MainThreadReleaseRunnable(doomed, loadGroupToCancel);
|
||||
if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) {
|
||||
if (!mFinishedWorker->ProxyReleaseMainThreadObjects()) {
|
||||
NS_WARNING("Failed to dispatch, going to leak!");
|
||||
}
|
||||
|
||||
|
@ -1851,6 +1835,193 @@ WorkerLoadInfo::StealFrom(WorkerLoadInfo& aOther)
|
|||
mOriginAttributes = aOther.mOriginAttributes;
|
||||
}
|
||||
|
||||
void
|
||||
WorkerLoadInfo::SetPrincipalOnMainThread(nsIPrincipal* aPrincipal,
|
||||
nsILoadGroup* aLoadGroup)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(NS_LoadGroupMatchesPrincipal(aLoadGroup, aPrincipal));
|
||||
MOZ_ASSERT(!mPrincipalInfo);
|
||||
|
||||
mPrincipal = aPrincipal;
|
||||
mPrincipalIsSystem = nsContentUtils::IsSystemPrincipal(aPrincipal);
|
||||
|
||||
aPrincipal->GetCsp(getter_AddRefs(mCSP));
|
||||
|
||||
if (mCSP) {
|
||||
mCSP->GetAllowsEval(&mReportCSPViolations, &mEvalAllowed);
|
||||
// Set ReferrerPolicy
|
||||
bool hasReferrerPolicy = false;
|
||||
uint32_t rp = mozilla::net::RP_Unset;
|
||||
|
||||
nsresult rv = mCSP->GetReferrerPolicy(&rp, &hasReferrerPolicy);
|
||||
NS_ENSURE_SUCCESS_VOID(rv);
|
||||
|
||||
if (hasReferrerPolicy) {
|
||||
mReferrerPolicy = static_cast<net::ReferrerPolicy>(rp);
|
||||
}
|
||||
} else {
|
||||
mEvalAllowed = true;
|
||||
mReportCSPViolations = false;
|
||||
}
|
||||
|
||||
mLoadGroup = aLoadGroup;
|
||||
|
||||
mPrincipalInfo = new PrincipalInfo();
|
||||
mOriginAttributes = nsContentUtils::GetOriginAttributes(aLoadGroup);
|
||||
|
||||
MOZ_ALWAYS_SUCCEEDS(
|
||||
PrincipalToPrincipalInfo(aPrincipal, mPrincipalInfo));
|
||||
}
|
||||
|
||||
nsresult
|
||||
WorkerLoadInfo::GetPrincipalAndLoadGroupFromChannel(nsIChannel* aChannel,
|
||||
nsIPrincipal** aPrincipalOut,
|
||||
nsILoadGroup** aLoadGroupOut)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_DIAGNOSTIC_ASSERT(aChannel);
|
||||
MOZ_DIAGNOSTIC_ASSERT(aPrincipalOut);
|
||||
MOZ_DIAGNOSTIC_ASSERT(aLoadGroupOut);
|
||||
|
||||
// Initial triggering principal should be set
|
||||
MOZ_DIAGNOSTIC_ASSERT(mPrincipal);
|
||||
|
||||
nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
|
||||
MOZ_DIAGNOSTIC_ASSERT(ssm);
|
||||
|
||||
nsCOMPtr<nsIPrincipal> channelPrincipal;
|
||||
nsresult rv = ssm->GetChannelResultPrincipal(aChannel, getter_AddRefs(channelPrincipal));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsILoadGroup> channelLoadGroup;
|
||||
rv = aChannel->GetLoadGroup(getter_AddRefs(channelLoadGroup));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
MOZ_ASSERT(channelLoadGroup);
|
||||
|
||||
// If the load principal is the system principal then the channel
|
||||
// principal must also be the system principal (we do not allow chrome
|
||||
// code to create workers with non-chrome scripts, and if we ever decide
|
||||
// to change this we need to make sure we don't always set
|
||||
// mPrincipalIsSystem to true in WorkerPrivate::GetLoadInfo()). Otherwise
|
||||
// this channel principal must be same origin with the load principal (we
|
||||
// check again here in case redirects changed the location of the script).
|
||||
if (nsContentUtils::IsSystemPrincipal(mPrincipal)) {
|
||||
if (!nsContentUtils::IsSystemPrincipal(channelPrincipal)) {
|
||||
nsCOMPtr<nsIURI> finalURI;
|
||||
rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(finalURI));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// See if this is a resource URI. Since JSMs usually come from
|
||||
// resource:// URIs we're currently considering all URIs with the
|
||||
// URI_IS_UI_RESOURCE flag as valid for creating privileged workers.
|
||||
bool isResource;
|
||||
rv = NS_URIChainHasFlags(finalURI,
|
||||
nsIProtocolHandler::URI_IS_UI_RESOURCE,
|
||||
&isResource);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (isResource) {
|
||||
// Assign the system principal to the resource:// worker only if it
|
||||
// was loaded from code using the system principal.
|
||||
channelPrincipal = mPrincipal;
|
||||
} else {
|
||||
return NS_ERROR_DOM_BAD_URI;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The principal can change, but it should still match the original
|
||||
// load group's appId and browser element flag.
|
||||
MOZ_ASSERT(NS_LoadGroupMatchesPrincipal(channelLoadGroup, channelPrincipal));
|
||||
|
||||
channelPrincipal.forget(aPrincipalOut);
|
||||
channelLoadGroup.forget(aLoadGroupOut);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
WorkerLoadInfo::SetPrincipalFromChannel(nsIChannel* aChannel)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
nsCOMPtr<nsIPrincipal> principal;
|
||||
nsCOMPtr<nsILoadGroup> loadGroup;
|
||||
nsresult rv = GetPrincipalAndLoadGroupFromChannel(aChannel,
|
||||
getter_AddRefs(principal),
|
||||
getter_AddRefs(loadGroup));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
SetPrincipalOnMainThread(principal, loadGroup);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
#if defined(DEBUG) || !defined(RELEASE_OR_BETA)
|
||||
bool
|
||||
WorkerLoadInfo::FinalChannelPrincipalIsValid(nsIChannel* aChannel)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
nsCOMPtr<nsIPrincipal> principal;
|
||||
nsCOMPtr<nsILoadGroup> loadGroup;
|
||||
nsresult rv = GetPrincipalAndLoadGroupFromChannel(aChannel,
|
||||
getter_AddRefs(principal),
|
||||
getter_AddRefs(loadGroup));
|
||||
NS_ENSURE_SUCCESS(rv, false);
|
||||
|
||||
|
||||
// Verify that the channel is still a null principal. We don't care
|
||||
// if these are the exact same null principal object, though. From
|
||||
// the worker's perspective its the same effect.
|
||||
if (principal->GetIsNullPrincipal() && mPrincipal->GetIsNullPrincipal()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Otherwise we require exact equality. Redirects can happen, but they
|
||||
// are not allowed to change our principal.
|
||||
if (principal->Equals(mPrincipal)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
#endif // defined(DEBUG) || !defined(RELEASE_OR_BETA)
|
||||
|
||||
bool
|
||||
WorkerLoadInfo::ProxyReleaseMainThreadObjects(WorkerPrivate* aWorkerPrivate)
|
||||
{
|
||||
nsCOMPtr<nsILoadGroup> nullLoadGroup;
|
||||
return ProxyReleaseMainThreadObjects(aWorkerPrivate, nullLoadGroup);
|
||||
}
|
||||
|
||||
bool
|
||||
WorkerLoadInfo::ProxyReleaseMainThreadObjects(WorkerPrivate* aWorkerPrivate,
|
||||
nsCOMPtr<nsILoadGroup>& aLoadGroupToCancel)
|
||||
{
|
||||
|
||||
static const uint32_t kDoomedCount = 10;
|
||||
nsTArray<nsCOMPtr<nsISupports>> doomed(kDoomedCount);
|
||||
|
||||
SwapToISupportsArray(mWindow, doomed);
|
||||
SwapToISupportsArray(mScriptContext, doomed);
|
||||
SwapToISupportsArray(mBaseURI, doomed);
|
||||
SwapToISupportsArray(mResolvedScriptURI, doomed);
|
||||
SwapToISupportsArray(mPrincipal, doomed);
|
||||
SwapToISupportsArray(mChannel, doomed);
|
||||
SwapToISupportsArray(mCSP, doomed);
|
||||
SwapToISupportsArray(mLoadGroup, doomed);
|
||||
SwapToISupportsArray(mLoadFailedAsyncRunnable, doomed);
|
||||
SwapToISupportsArray(mInterfaceRequestor, doomed);
|
||||
// Before adding anything here update kDoomedCount above!
|
||||
|
||||
MOZ_ASSERT(doomed.Length() == kDoomedCount);
|
||||
|
||||
RefPtr<MainThreadReleaseRunnable> runnable =
|
||||
new MainThreadReleaseRunnable(doomed, aLoadGroupToCancel);
|
||||
return NS_SUCCEEDED(aWorkerPrivate->DispatchToMainThread(runnable.forget()));
|
||||
}
|
||||
|
||||
template <class Derived>
|
||||
class WorkerPrivateParent<Derived>::EventTarget final
|
||||
: public nsIEventTarget
|
||||
|
@ -3031,48 +3202,25 @@ WorkerPrivateParent<Derived>::ModifyBusyCount(bool aIncrease)
|
|||
}
|
||||
|
||||
template <class Derived>
|
||||
void
|
||||
WorkerPrivateParent<Derived>::ForgetOverridenLoadGroup(
|
||||
nsCOMPtr<nsILoadGroup>& aLoadGroupOut)
|
||||
{
|
||||
AssertIsOnParentThread();
|
||||
|
||||
// If we're not overriden, then do nothing here. Let the load group get
|
||||
// handled in ForgetMainThreadObjects().
|
||||
if (!mLoadInfo.mInterfaceRequestor) {
|
||||
return;
|
||||
}
|
||||
|
||||
mLoadInfo.mLoadGroup.swap(aLoadGroupOut);
|
||||
}
|
||||
|
||||
template <class Derived>
|
||||
void
|
||||
WorkerPrivateParent<Derived>::ForgetMainThreadObjects(
|
||||
nsTArray<nsCOMPtr<nsISupports> >& aDoomed)
|
||||
bool
|
||||
WorkerPrivateParent<Derived>::ProxyReleaseMainThreadObjects()
|
||||
{
|
||||
AssertIsOnParentThread();
|
||||
MOZ_ASSERT(!mMainThreadObjectsForgotten);
|
||||
|
||||
static const uint32_t kDoomedCount = 10;
|
||||
nsCOMPtr<nsILoadGroup> loadGroupToCancel;
|
||||
// If we're not overriden, then do nothing here. Let the load group get
|
||||
// handled in ForgetMainThreadObjects().
|
||||
if (mLoadInfo.mInterfaceRequestor) {
|
||||
mLoadInfo.mLoadGroup.swap(loadGroupToCancel);
|
||||
}
|
||||
|
||||
aDoomed.SetCapacity(kDoomedCount);
|
||||
|
||||
SwapToISupportsArray(mLoadInfo.mWindow, aDoomed);
|
||||
SwapToISupportsArray(mLoadInfo.mScriptContext, aDoomed);
|
||||
SwapToISupportsArray(mLoadInfo.mBaseURI, aDoomed);
|
||||
SwapToISupportsArray(mLoadInfo.mResolvedScriptURI, aDoomed);
|
||||
SwapToISupportsArray(mLoadInfo.mPrincipal, aDoomed);
|
||||
SwapToISupportsArray(mLoadInfo.mChannel, aDoomed);
|
||||
SwapToISupportsArray(mLoadInfo.mCSP, aDoomed);
|
||||
SwapToISupportsArray(mLoadInfo.mLoadGroup, aDoomed);
|
||||
SwapToISupportsArray(mLoadInfo.mLoadFailedAsyncRunnable, aDoomed);
|
||||
SwapToISupportsArray(mLoadInfo.mInterfaceRequestor, aDoomed);
|
||||
// Before adding anything here update kDoomedCount above!
|
||||
|
||||
MOZ_ASSERT(aDoomed.Length() == kDoomedCount);
|
||||
bool result = mLoadInfo.ProxyReleaseMainThreadObjects(ParentAsWorkerPrivate(),
|
||||
loadGroupToCancel);
|
||||
|
||||
mMainThreadObjectsForgotten = true;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template <class Derived>
|
||||
|
@ -3656,45 +3804,28 @@ WorkerPrivateParent<Derived>::SetBaseURI(nsIURI* aBaseURI)
|
|||
|
||||
template <class Derived>
|
||||
void
|
||||
WorkerPrivateParent<Derived>::SetPrincipal(nsIPrincipal* aPrincipal,
|
||||
WorkerPrivateParent<Derived>::SetPrincipalOnMainThread(nsIPrincipal* aPrincipal,
|
||||
nsILoadGroup* aLoadGroup)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(NS_LoadGroupMatchesPrincipal(aLoadGroup, aPrincipal));
|
||||
MOZ_ASSERT(!mLoadInfo.mPrincipalInfo);
|
||||
|
||||
mLoadInfo.mPrincipal = aPrincipal;
|
||||
mLoadInfo.mPrincipalIsSystem = nsContentUtils::IsSystemPrincipal(aPrincipal);
|
||||
|
||||
aPrincipal->GetCsp(getter_AddRefs(mLoadInfo.mCSP));
|
||||
|
||||
if (mLoadInfo.mCSP) {
|
||||
mLoadInfo.mCSP->GetAllowsEval(&mLoadInfo.mReportCSPViolations,
|
||||
&mLoadInfo.mEvalAllowed);
|
||||
// Set ReferrerPolicy
|
||||
bool hasReferrerPolicy = false;
|
||||
uint32_t rp = mozilla::net::RP_Unset;
|
||||
|
||||
nsresult rv = mLoadInfo.mCSP->GetReferrerPolicy(&rp, &hasReferrerPolicy);
|
||||
NS_ENSURE_SUCCESS_VOID(rv);
|
||||
|
||||
if (hasReferrerPolicy) {
|
||||
mLoadInfo.mReferrerPolicy = static_cast<net::ReferrerPolicy>(rp);
|
||||
}
|
||||
} else {
|
||||
mLoadInfo.mEvalAllowed = true;
|
||||
mLoadInfo.mReportCSPViolations = false;
|
||||
}
|
||||
|
||||
mLoadInfo.mLoadGroup = aLoadGroup;
|
||||
|
||||
mLoadInfo.mPrincipalInfo = new PrincipalInfo();
|
||||
mLoadInfo.mOriginAttributes = nsContentUtils::GetOriginAttributes(aLoadGroup);
|
||||
|
||||
MOZ_ALWAYS_SUCCEEDS(
|
||||
PrincipalToPrincipalInfo(aPrincipal, mLoadInfo.mPrincipalInfo));
|
||||
mLoadInfo.SetPrincipalOnMainThread(aPrincipal, aLoadGroup);
|
||||
}
|
||||
|
||||
template <class Derived>
|
||||
nsresult
|
||||
WorkerPrivateParent<Derived>::SetPrincipalFromChannel(nsIChannel* aChannel)
|
||||
{
|
||||
return mLoadInfo.SetPrincipalFromChannel(aChannel);
|
||||
}
|
||||
|
||||
#if defined(DEBUG) || !defined(RELEASE_OR_BETA)
|
||||
template <class Derived>
|
||||
bool
|
||||
WorkerPrivateParent<Derived>::FinalChannelPrincipalIsValid(nsIChannel* aChannel)
|
||||
{
|
||||
return mLoadInfo.FinalChannelPrincipalIsValid(aChannel);
|
||||
}
|
||||
#endif
|
||||
|
||||
template <class Derived>
|
||||
void
|
||||
WorkerPrivateParent<Derived>::UpdateOverridenLoadGroup(nsILoadGroup* aBaseLoadGroup)
|
||||
|
@ -4447,12 +4578,14 @@ WorkerPrivate::GetLoadInfo(JSContext* aCx, nsPIDOMWindowInner* aWindow,
|
|||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// StartAssignment() is used instead getter_AddRefs because, getter_AddRefs
|
||||
// does QI in debug build and, if this worker runs in a child process,
|
||||
// HttpChannelChild will crash because it's not thread-safe.
|
||||
// Passing a pointer to our stack loadInfo is safe here because this
|
||||
// method uses a sync runnable to get the channel from the main thread.
|
||||
rv = ChannelFromScriptURLWorkerThread(aCx, aParent, aScriptURL,
|
||||
loadInfo.mChannel.StartAssignment());
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
loadInfo);
|
||||
if (NS_FAILED(rv)) {
|
||||
MOZ_ALWAYS_TRUE(loadInfo.ProxyReleaseMainThreadObjects(aParent));
|
||||
return rv;
|
||||
}
|
||||
|
||||
// Now that we've spun the loop there's no guarantee that our parent is
|
||||
// still alive. We may have received control messages initiating shutdown.
|
||||
|
@ -4462,7 +4595,7 @@ WorkerPrivate::GetLoadInfo(JSContext* aCx, nsPIDOMWindowInner* aWindow,
|
|||
}
|
||||
|
||||
if (parentStatus > Running) {
|
||||
NS_ReleaseOnMainThread(loadInfo.mChannel.forget());
|
||||
MOZ_ALWAYS_TRUE(loadInfo.ProxyReleaseMainThreadObjects(aParent));
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
|
@ -4662,6 +4795,9 @@ WorkerPrivate::GetLoadInfo(JSContext* aCx, nsPIDOMWindowInner* aWindow,
|
|||
rv = NS_GetFinalChannelURI(loadInfo.mChannel,
|
||||
getter_AddRefs(loadInfo.mResolvedScriptURI));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = loadInfo.SetPrincipalFromChannel(loadInfo.mChannel);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
aLoadInfo->StealFrom(loadInfo);
|
||||
|
|
|
@ -353,11 +353,8 @@ public:
|
|||
bool
|
||||
ModifyBusyCount(bool aIncrease);
|
||||
|
||||
void
|
||||
ForgetOverridenLoadGroup(nsCOMPtr<nsILoadGroup>& aLoadGroupOut);
|
||||
|
||||
void
|
||||
ForgetMainThreadObjects(nsTArray<nsCOMPtr<nsISupports> >& aDoomed);
|
||||
bool
|
||||
ProxyReleaseMainThreadObjects();
|
||||
|
||||
void
|
||||
PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
|
||||
|
@ -611,7 +608,15 @@ public:
|
|||
}
|
||||
|
||||
void
|
||||
SetPrincipal(nsIPrincipal* aPrincipal, nsILoadGroup* aLoadGroup);
|
||||
SetPrincipalOnMainThread(nsIPrincipal* aPrincipal, nsILoadGroup* aLoadGroup);
|
||||
|
||||
nsresult
|
||||
SetPrincipalFromChannel(nsIChannel* aChannel);
|
||||
|
||||
#if defined(DEBUG) || !defined(RELEASE_OR_BETA)
|
||||
bool
|
||||
FinalChannelPrincipalIsValid(nsIChannel* aChannel);
|
||||
#endif
|
||||
|
||||
bool
|
||||
UsesSystemPrincipal() const
|
||||
|
|
|
@ -277,6 +277,29 @@ struct WorkerLoadInfo
|
|||
~WorkerLoadInfo();
|
||||
|
||||
void StealFrom(WorkerLoadInfo& aOther);
|
||||
|
||||
void
|
||||
SetPrincipalOnMainThread(nsIPrincipal* aPrincipal, nsILoadGroup* aLoadGroup);
|
||||
|
||||
nsresult
|
||||
GetPrincipalAndLoadGroupFromChannel(nsIChannel* aChannel,
|
||||
nsIPrincipal** aPrincipalOut,
|
||||
nsILoadGroup** aLoadGroupOut);
|
||||
|
||||
nsresult
|
||||
SetPrincipalFromChannel(nsIChannel* aChannel);
|
||||
|
||||
#if defined(DEBUG) || !defined(RELEASE_OR_BETA)
|
||||
bool
|
||||
FinalChannelPrincipalIsValid(nsIChannel* aChannel);
|
||||
#endif
|
||||
|
||||
bool
|
||||
ProxyReleaseMainThreadObjects(WorkerPrivate* aWorkerPrivate);
|
||||
|
||||
bool
|
||||
ProxyReleaseMainThreadObjects(WorkerPrivate* aWorkerPrivate,
|
||||
nsCOMPtr<nsILoadGroup>& aLoadGroupToCancel);
|
||||
};
|
||||
|
||||
// All of these are implemented in RuntimeService.cpp
|
||||
|
|
|
@ -1,7 +1 @@
|
|||
/**
|
||||
* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
onmessage = function(event) {
|
||||
throw "Shouldn't be able to read this!";
|
||||
}
|
||||
// doesn't matter what is in here if the URL is bad
|
||||
|
|
|
@ -21,23 +21,13 @@ onmessage = function(event) {
|
|||
}
|
||||
|
||||
// We shouldn't be able to make a ChromeWorker to a non-chrome URL.
|
||||
let worker = new ChromeWorker(mochitestURL);
|
||||
worker.onmessage = function(event) {
|
||||
throw event.data;
|
||||
};
|
||||
worker.onerror = function(event) {
|
||||
event.preventDefault();
|
||||
|
||||
// And we shouldn't be able to make a regular Worker to a non-chrome URL.
|
||||
worker = new Worker(mochitestURL);
|
||||
worker.onmessage = function(event) {
|
||||
throw event.data;
|
||||
};
|
||||
worker.onerror = function(event) {
|
||||
event.preventDefault();
|
||||
try{
|
||||
new ChromeWorker(mochitestURL);
|
||||
} catch(e) {
|
||||
if (e.name === 'SecurityError') {
|
||||
postMessage("Done");
|
||||
};
|
||||
worker.postMessage("Hi");
|
||||
};
|
||||
worker.postMessage("Hi");
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw('creating a chrome worker with a bad URL should throw a SecurityError');
|
||||
};
|
||||
|
|
|
@ -511,7 +511,7 @@ FrameAnimator::DoBlend(IntRect* aDirtyRect,
|
|||
// Create the Compositing Frame
|
||||
if (!mCompositingFrame) {
|
||||
RefPtr<imgFrame> newFrame = new imgFrame;
|
||||
nsresult rv = newFrame->InitForDecoder(mSize,
|
||||
nsresult rv = newFrame->InitForAnimator(mSize,
|
||||
SurfaceFormat::B8G8R8A8);
|
||||
if (NS_FAILED(rv)) {
|
||||
mCompositingFrame.reset();
|
||||
|
@ -652,7 +652,7 @@ FrameAnimator::DoBlend(IntRect* aDirtyRect,
|
|||
// overwrite.
|
||||
if (!mCompositingPrevFrame) {
|
||||
RefPtr<imgFrame> newFrame = new imgFrame;
|
||||
nsresult rv = newFrame->InitForDecoder(mSize,
|
||||
nsresult rv = newFrame->InitForAnimator(mSize,
|
||||
SurfaceFormat::B8G8R8A8);
|
||||
if (NS_FAILED(rv)) {
|
||||
mCompositingPrevFrame.reset();
|
||||
|
|
|
@ -605,7 +605,7 @@ RasterImage::GetImageContainer(LayerManager* aManager, uint32_t aFlags)
|
|||
}
|
||||
|
||||
// |image| holds a reference to a SourceSurface which in turn holds a lock on
|
||||
// the current frame's VolatileBuffer, ensuring that it doesn't get freed as
|
||||
// the current frame's data buffer, ensuring that it doesn't get freed as
|
||||
// long as the layer system keeps this ImageContainer alive.
|
||||
AutoTArray<ImageContainer::NonOwningImage, 1> imageList;
|
||||
imageList.AppendElement(ImageContainer::NonOwningImage(image, TimeStamp(),
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include "MainThreadUtils.h"
|
||||
#include "mozilla/CheckedInt.h"
|
||||
#include "mozilla/gfx/Tools.h"
|
||||
#include "mozilla/layers/SourceSurfaceVolatileData.h"
|
||||
#include "mozilla/Likely.h"
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
#include "nsMargin.h"
|
||||
|
@ -33,9 +34,9 @@ using namespace gfx;
|
|||
namespace image {
|
||||
|
||||
static void
|
||||
VolatileBufferRelease(void* vbuf)
|
||||
ScopedMapRelease(void* aMap)
|
||||
{
|
||||
delete static_cast<VolatileBufferPtr<unsigned char>*>(vbuf);
|
||||
delete static_cast<DataSourceSurface::ScopedMap*>(aMap);
|
||||
}
|
||||
|
||||
static int32_t
|
||||
|
@ -46,63 +47,62 @@ VolatileSurfaceStride(const IntSize& size, SurfaceFormat format)
|
|||
}
|
||||
|
||||
static already_AddRefed<DataSourceSurface>
|
||||
CreateLockedSurface(VolatileBuffer* vbuf,
|
||||
CreateLockedSurface(DataSourceSurface *aSurface,
|
||||
const IntSize& size,
|
||||
SurfaceFormat format)
|
||||
{
|
||||
VolatileBufferPtr<unsigned char>* vbufptr =
|
||||
new VolatileBufferPtr<unsigned char>(vbuf);
|
||||
MOZ_ASSERT(!vbufptr->WasBufferPurged(), "Expected image data!");
|
||||
|
||||
const int32_t stride = VolatileSurfaceStride(size, format);
|
||||
|
||||
// The VolatileBufferPtr is held by this DataSourceSurface.
|
||||
DataSourceSurface::ScopedMap* smap =
|
||||
new DataSourceSurface::ScopedMap(aSurface, DataSourceSurface::READ_WRITE);
|
||||
if (smap->IsMapped()) {
|
||||
// The ScopedMap is held by this DataSourceSurface.
|
||||
RefPtr<DataSourceSurface> surf =
|
||||
Factory::CreateWrappingDataSourceSurface(*vbufptr, stride, size, format,
|
||||
&VolatileBufferRelease,
|
||||
static_cast<void*>(vbufptr));
|
||||
if (!surf) {
|
||||
delete vbufptr;
|
||||
return nullptr;
|
||||
Factory::CreateWrappingDataSourceSurface(smap->GetData(),
|
||||
aSurface->Stride(),
|
||||
size,
|
||||
format,
|
||||
&ScopedMapRelease,
|
||||
static_cast<void*>(smap));
|
||||
if (surf) {
|
||||
return surf.forget();
|
||||
}
|
||||
}
|
||||
|
||||
return surf.forget();
|
||||
delete smap;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static already_AddRefed<VolatileBuffer>
|
||||
AllocateBufferForImage(const IntSize& size, SurfaceFormat format)
|
||||
static already_AddRefed<DataSourceSurface>
|
||||
AllocateBufferForImage(const IntSize& size,
|
||||
SurfaceFormat format,
|
||||
bool aIsAnimated = false)
|
||||
{
|
||||
int32_t stride = VolatileSurfaceStride(size, format);
|
||||
RefPtr<VolatileBuffer> buf = new VolatileBuffer();
|
||||
if (buf->Init(stride * size.height,
|
||||
size_t(1) << gfxAlphaRecovery::GoodAlignmentLog2())) {
|
||||
return buf.forget();
|
||||
RefPtr<SourceSurfaceVolatileData> newSurf = new SourceSurfaceVolatileData();
|
||||
if (newSurf->Init(size, stride, format)) {
|
||||
return newSurf.forget();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static bool
|
||||
ClearSurface(VolatileBuffer* aVBuf, const IntSize& aSize, SurfaceFormat aFormat)
|
||||
ClearSurface(DataSourceSurface* aSurface, const IntSize& aSize, SurfaceFormat aFormat)
|
||||
{
|
||||
VolatileBufferPtr<unsigned char> vbufptr(aVBuf);
|
||||
if (vbufptr.WasBufferPurged()) {
|
||||
NS_WARNING("VolatileBuffer was purged");
|
||||
return false;
|
||||
}
|
||||
int32_t stride = aSurface->Stride();
|
||||
uint8_t* data = aSurface->GetData();
|
||||
MOZ_ASSERT(data);
|
||||
|
||||
int32_t stride = VolatileSurfaceStride(aSize, aFormat);
|
||||
if (aFormat == SurfaceFormat::B8G8R8X8) {
|
||||
// Skia doesn't support RGBX surfaces, so ensure the alpha value is set
|
||||
// to opaque white. While it would be nice to only do this for Skia,
|
||||
// imgFrame can run off main thread and past shutdown where
|
||||
// we might not have gfxPlatform, so just memset everytime instead.
|
||||
memset(vbufptr, 0xFF, stride * aSize.height);
|
||||
} else if (aVBuf->OnHeap()) {
|
||||
memset(data, 0xFF, stride * aSize.height);
|
||||
} else if (aSurface->OnHeap()) {
|
||||
// We only need to memset it if the buffer was allocated on the heap.
|
||||
// Otherwise, it's allocated via mmap and refers to a zeroed page and will
|
||||
// be COW once it's written to.
|
||||
memset(vbufptr, 0, stride * aSize.height);
|
||||
memset(data, 0, stride * aSize.height);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -191,7 +191,8 @@ imgFrame::InitForDecoder(const nsIntSize& aImageSize,
|
|||
const nsIntRect& aRect,
|
||||
SurfaceFormat aFormat,
|
||||
uint8_t aPaletteDepth /* = 0 */,
|
||||
bool aNonPremult /* = false */)
|
||||
bool aNonPremult /* = false */,
|
||||
bool aIsAnimated /* = false */)
|
||||
{
|
||||
// Assert for properties that should be verified by decoders,
|
||||
// warn for properties related to bad content.
|
||||
|
@ -239,23 +240,22 @@ imgFrame::InitForDecoder(const nsIntSize& aImageSize,
|
|||
}
|
||||
NS_ENSURE_TRUE(mPalettedImageData, NS_ERROR_OUT_OF_MEMORY);
|
||||
} else {
|
||||
MOZ_ASSERT(!mImageSurface, "Called imgFrame::InitForDecoder() twice?");
|
||||
MOZ_ASSERT(!mLockedSurface, "Called imgFrame::InitForDecoder() twice?");
|
||||
|
||||
mVBuf = AllocateBufferForImage(mFrameRect.Size(), mFormat);
|
||||
if (!mVBuf) {
|
||||
mRawSurface = AllocateBufferForImage(mFrameRect.Size(), mFormat, aIsAnimated);
|
||||
if (!mRawSurface) {
|
||||
mAborted = true;
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
mImageSurface = CreateLockedSurface(mVBuf, mFrameRect.Size(), mFormat);
|
||||
|
||||
if (!mImageSurface) {
|
||||
NS_WARNING("Failed to create ImageSurface");
|
||||
mLockedSurface = CreateLockedSurface(mRawSurface, mFrameRect.Size(), mFormat);
|
||||
if (!mLockedSurface) {
|
||||
NS_WARNING("Failed to create LockedSurface");
|
||||
mAborted = true;
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
if (!ClearSurface(mVBuf, mFrameRect.Size(), mFormat)) {
|
||||
if (!ClearSurface(mRawSurface, mFrameRect.Size(), mFormat)) {
|
||||
NS_WARNING("Could not clear allocated buffer");
|
||||
mAborted = true;
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
@ -295,39 +295,31 @@ imgFrame::InitWithDrawable(gfxDrawable* aDrawable,
|
|||
if (canUseDataSurface) {
|
||||
// It's safe to use data surfaces for content on this platform, so we can
|
||||
// get away with using volatile buffers.
|
||||
MOZ_ASSERT(!mImageSurface, "Called imgFrame::InitWithDrawable() twice?");
|
||||
MOZ_ASSERT(!mLockedSurface, "Called imgFrame::InitWithDrawable() twice?");
|
||||
|
||||
mVBuf = AllocateBufferForImage(mFrameRect.Size(), mFormat);
|
||||
if (!mVBuf) {
|
||||
mRawSurface = AllocateBufferForImage(mFrameRect.Size(), mFormat);
|
||||
if (!mRawSurface) {
|
||||
mAborted = true;
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
int32_t stride = VolatileSurfaceStride(mFrameRect.Size(), mFormat);
|
||||
VolatileBufferPtr<uint8_t> ptr(mVBuf);
|
||||
if (!ptr) {
|
||||
mLockedSurface = CreateLockedSurface(mRawSurface, mFrameRect.Size(), mFormat);
|
||||
if (!mLockedSurface) {
|
||||
NS_WARNING("Failed to create LockedSurface");
|
||||
mAborted = true;
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
mImageSurface = CreateLockedSurface(mVBuf, mFrameRect.Size(), mFormat);
|
||||
|
||||
if (!mImageSurface) {
|
||||
NS_WARNING("Failed to create ImageSurface");
|
||||
mAborted = true;
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
if (!ClearSurface(mVBuf, mFrameRect.Size(), mFormat)) {
|
||||
if (!ClearSurface(mRawSurface, mFrameRect.Size(), mFormat)) {
|
||||
NS_WARNING("Could not clear allocated buffer");
|
||||
mAborted = true;
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
target = gfxPlatform::CreateDrawTargetForData(
|
||||
ptr,
|
||||
mLockedSurface->GetData(),
|
||||
mFrameRect.Size(),
|
||||
stride,
|
||||
mLockedSurface->Stride(),
|
||||
mFormat);
|
||||
} else {
|
||||
// We can't use data surfaces for content, so we'll create an offscreen
|
||||
|
@ -357,7 +349,7 @@ imgFrame::InitWithDrawable(gfxDrawable* aDrawable,
|
|||
ImageRegion::Create(ThebesRect(mFrameRect)),
|
||||
mFormat, aSamplingFilter, aImageFlags);
|
||||
|
||||
if (canUseDataSurface && !mImageSurface) {
|
||||
if (canUseDataSurface && !mLockedSurface) {
|
||||
NS_WARNING("Failed to create VolatileDataSourceSurface");
|
||||
mAborted = true;
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
@ -422,23 +414,23 @@ imgFrame::Optimize(DrawTarget* aTarget)
|
|||
}
|
||||
|
||||
mOptSurface = gfxPlatform::GetPlatform()
|
||||
->ScreenReferenceDrawTarget()->OptimizeSourceSurface(mImageSurface);
|
||||
if (mOptSurface == mImageSurface) {
|
||||
->ScreenReferenceDrawTarget()->OptimizeSourceSurface(mLockedSurface);
|
||||
if (mOptSurface == mLockedSurface) {
|
||||
mOptSurface = nullptr;
|
||||
}
|
||||
|
||||
if (mOptSurface) {
|
||||
// There's no reason to keep our volatile buffer around at all if we have an
|
||||
// There's no reason to keep our original surface around if we have an
|
||||
// optimized surface. Release our reference to it. This will leave
|
||||
// |mVBufPtr| and |mImageSurface| as the only things keeping it alive, so
|
||||
// it'll get freed below.
|
||||
mVBuf = nullptr;
|
||||
// |mLockedSurface| as the only thing keeping it alive, so it'll get freed
|
||||
// below.
|
||||
mRawSurface = nullptr;
|
||||
}
|
||||
|
||||
// Release all strong references to our volatile buffer's memory. This will
|
||||
// allow the operating system to free the memory if it needs to.
|
||||
mVBufPtr = nullptr;
|
||||
mImageSurface = nullptr;
|
||||
// Release all strong references to the surface's memory. If the underlying
|
||||
// surface is volatile, this will allow the operating system to free the
|
||||
// memory if it needs to.
|
||||
mLockedSurface = nullptr;
|
||||
mOptimizable = false;
|
||||
|
||||
return NS_OK;
|
||||
|
@ -536,7 +528,7 @@ bool imgFrame::Draw(gfxContext* aContext, const ImageRegion& aRegion,
|
|||
MonitorAutoLock lock(mMonitor);
|
||||
|
||||
// Possibly convert this image into a GPU texture, this may also cause our
|
||||
// mImageSurface to be released and the OS to release the underlying memory.
|
||||
// mLockedSurface to be released and the OS to release the underlying memory.
|
||||
Optimize(aContext->GetDrawTarget());
|
||||
|
||||
bool doPartialDecode = !AreAllPixelsWritten();
|
||||
|
@ -610,7 +602,7 @@ imgFrame::GetImageBytesPerRow() const
|
|||
{
|
||||
mMonitor.AssertCurrentThreadOwns();
|
||||
|
||||
if (mVBuf) {
|
||||
if (mRawSurface) {
|
||||
return mFrameRect.width * BytesPerPixel(mFormat);
|
||||
}
|
||||
|
||||
|
@ -640,17 +632,17 @@ imgFrame::GetImageDataInternal(uint8_t** aData, uint32_t* aLength) const
|
|||
mMonitor.AssertCurrentThreadOwns();
|
||||
MOZ_ASSERT(mLockCount > 0, "Image data should be locked");
|
||||
|
||||
if (mImageSurface) {
|
||||
*aData = mVBufPtr;
|
||||
if (mLockedSurface) {
|
||||
*aData = mLockedSurface->GetData();
|
||||
MOZ_ASSERT(*aData,
|
||||
"mImageSurface is non-null, but mVBufPtr is null in GetImageData");
|
||||
"mLockedSurface is non-null, but GetData is null in GetImageData");
|
||||
} else if (mPalettedImageData) {
|
||||
*aData = mPalettedImageData + PaletteDataLength();
|
||||
MOZ_ASSERT(*aData,
|
||||
"mPalettedImageData is non-null, but result is null in GetImageData");
|
||||
} else {
|
||||
MOZ_ASSERT(false,
|
||||
"Have neither mImageSurface nor mPalettedImageData in GetImageData");
|
||||
"Have neither mLockedSurface nor mPalettedImageData in GetImageData");
|
||||
*aData = nullptr;
|
||||
}
|
||||
|
||||
|
@ -712,9 +704,8 @@ imgFrame::LockImageData()
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
// If we're the first lock, but have an image surface, we're OK.
|
||||
if (mImageSurface) {
|
||||
mVBufPtr = mVBuf;
|
||||
// If we're the first lock, but have the locked surface, we're OK.
|
||||
if (mLockedSurface) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -783,21 +774,12 @@ imgFrame::GetSourceSurfaceInternal()
|
|||
}
|
||||
}
|
||||
|
||||
if (mImageSurface) {
|
||||
RefPtr<SourceSurface> surf(mImageSurface);
|
||||
if (mLockedSurface) {
|
||||
RefPtr<SourceSurface> surf(mLockedSurface);
|
||||
return surf.forget();
|
||||
}
|
||||
|
||||
if (!mVBuf) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
VolatileBufferPtr<char> buf(mVBuf);
|
||||
if (buf.WasBufferPurged()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return CreateLockedSurface(mVBuf, mFrameRect.Size(), mFormat);
|
||||
return CreateLockedSurface(mRawSurface, mFrameRect.Size(), mFormat);
|
||||
}
|
||||
|
||||
AnimationData
|
||||
|
@ -891,17 +873,16 @@ imgFrame::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
|
|||
if (mPalettedImageData) {
|
||||
aHeapSizeOut += aMallocSizeOf(mPalettedImageData);
|
||||
}
|
||||
if (mImageSurface) {
|
||||
aHeapSizeOut += aMallocSizeOf(mImageSurface);
|
||||
if (mLockedSurface) {
|
||||
aHeapSizeOut += aMallocSizeOf(mLockedSurface);
|
||||
}
|
||||
if (mOptSurface) {
|
||||
aHeapSizeOut += aMallocSizeOf(mOptSurface);
|
||||
}
|
||||
|
||||
if (mVBuf) {
|
||||
aHeapSizeOut += aMallocSizeOf(mVBuf);
|
||||
aHeapSizeOut += mVBuf->HeapSizeOfExcludingThis(aMallocSizeOf);
|
||||
aNonHeapSizeOut += mVBuf->NonHeapSizeOfExcludingThis();
|
||||
if (mRawSurface) {
|
||||
aHeapSizeOut += aMallocSizeOf(mRawSurface);
|
||||
mRawSurface->AddSizeOfExcludingThis(aMallocSizeOf, aHeapSizeOut,
|
||||
aNonHeapSizeOut);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -11,10 +11,10 @@
|
|||
#include "mozilla/MemoryReporting.h"
|
||||
#include "mozilla/Monitor.h"
|
||||
#include "mozilla/Move.h"
|
||||
#include "mozilla/VolatileBuffer.h"
|
||||
#include "gfxDrawable.h"
|
||||
#include "imgIContainer.h"
|
||||
#include "MainThreadUtils.h"
|
||||
#include "nsAutoPtr.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace image {
|
||||
|
@ -210,14 +210,14 @@ public:
|
|||
const nsIntRect& aRect,
|
||||
SurfaceFormat aFormat,
|
||||
uint8_t aPaletteDepth = 0,
|
||||
bool aNonPremult = false);
|
||||
bool aNonPremult = false,
|
||||
bool aIsAnimated = false);
|
||||
|
||||
nsresult InitForDecoder(const nsIntSize& aSize,
|
||||
SurfaceFormat aFormat,
|
||||
uint8_t aPaletteDepth = 0)
|
||||
nsresult InitForAnimator(const nsIntSize& aSize,
|
||||
SurfaceFormat aFormat)
|
||||
{
|
||||
return InitForDecoder(aSize, nsIntRect(0, 0, aSize.width, aSize.height),
|
||||
aFormat, aPaletteDepth);
|
||||
aFormat, 0, false, true);
|
||||
}
|
||||
|
||||
|
||||
|
@ -395,11 +395,25 @@ private: // data
|
|||
|
||||
mutable Monitor mMonitor;
|
||||
|
||||
RefPtr<DataSourceSurface> mImageSurface;
|
||||
RefPtr<SourceSurface> mOptSurface;
|
||||
/**
|
||||
* Surface which contains either a weak or a strong reference to its
|
||||
* underlying data buffer. If it is a weak reference, and there are no strong
|
||||
* references, the buffer may be released due to events such as low memory.
|
||||
*/
|
||||
RefPtr<DataSourceSurface> mRawSurface;
|
||||
|
||||
RefPtr<VolatileBuffer> mVBuf;
|
||||
VolatileBufferPtr<uint8_t> mVBufPtr;
|
||||
/**
|
||||
* Refers to the same data as mRawSurface, but when set, it guarantees that
|
||||
* we hold a strong reference to the underlying data buffer.
|
||||
*/
|
||||
RefPtr<DataSourceSurface> mLockedSurface;
|
||||
|
||||
/**
|
||||
* Optimized copy of mRawSurface for the DrawTarget that will render it. This
|
||||
* is unused if the DrawTarget is able to render DataSourceSurface buffers
|
||||
* directly.
|
||||
*/
|
||||
RefPtr<SourceSurface> mOptSurface;
|
||||
|
||||
nsIntRect mDecoded;
|
||||
|
||||
|
@ -450,17 +464,28 @@ private: // data
|
|||
*/
|
||||
class DrawableFrameRef final
|
||||
{
|
||||
typedef gfx::DataSourceSurface DataSourceSurface;
|
||||
|
||||
public:
|
||||
DrawableFrameRef() { }
|
||||
|
||||
explicit DrawableFrameRef(imgFrame* aFrame)
|
||||
: mFrame(aFrame)
|
||||
, mRef(aFrame->mVBuf)
|
||||
{
|
||||
if (mRef.WasBufferPurged()) {
|
||||
// Paletted images won't have a surface so there is no strong reference
|
||||
// to hold on to. Since Draw() and GetSourceSurface() calls will not work
|
||||
// in that case, we should be using RawAccessFrameRef exclusively instead.
|
||||
// See FrameAnimator::GetRawFrame for an example of this behaviour.
|
||||
if (aFrame->mRawSurface) {
|
||||
mRef = new DataSourceSurface::ScopedMap(aFrame->mRawSurface,
|
||||
DataSourceSurface::READ_WRITE);
|
||||
if (!mRef->IsMapped()) {
|
||||
mFrame = nullptr;
|
||||
mRef = nullptr;
|
||||
}
|
||||
} else {
|
||||
MOZ_ASSERT(aFrame->mOptSurface || aFrame->GetIsPaletted());
|
||||
}
|
||||
}
|
||||
|
||||
DrawableFrameRef(DrawableFrameRef&& aOther)
|
||||
|
@ -503,7 +528,7 @@ private:
|
|||
DrawableFrameRef(const DrawableFrameRef& aOther) = delete;
|
||||
|
||||
RefPtr<imgFrame> mFrame;
|
||||
VolatileBufferPtr<uint8_t> mRef;
|
||||
nsAutoPtr<DataSourceSurface::ScopedMap> mRef;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -15,6 +15,9 @@
|
|||
#ifndef ENABLE_INTL_API
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIUnicodeDecoder.h"
|
||||
#else
|
||||
#include "gtest/MozGtestFriend.h"
|
||||
#include "unicode/udat.h"
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
|
@ -47,6 +50,15 @@ private:
|
|||
static nsresult Initialize();
|
||||
|
||||
#ifdef ENABLE_INTL_API
|
||||
FRIEND_TEST(DateTimeFormat, FormatPRExplodedTime);
|
||||
|
||||
// performs a locale sensitive date formatting operation on the UDate parameter
|
||||
static nsresult FormatUDateTime(const nsDateFormatSelector aDateFormatSelector,
|
||||
const nsTimeFormatSelector aTimeFormatSelector,
|
||||
const UDate aUDateTime,
|
||||
const PRTimeParameters* aTimeParameters,
|
||||
nsAString& aStringOut);
|
||||
|
||||
static nsCString* mLocale;
|
||||
#else
|
||||
// performs a locale sensitive date formatting operation on the struct tm parameter
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
#include "nsCOMPtr.h"
|
||||
#include "nsIServiceManager.h"
|
||||
#include "nsILocaleService.h"
|
||||
#include "unicode/udat.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
|
@ -58,6 +57,27 @@ DateTimeFormat::FormatPRTime(const nsDateFormatSelector aDateFormatSelector,
|
|||
const nsTimeFormatSelector aTimeFormatSelector,
|
||||
const PRTime aPrTime,
|
||||
nsAString& aStringOut)
|
||||
{
|
||||
return FormatUDateTime(aDateFormatSelector, aTimeFormatSelector, (aPrTime / PR_USEC_PER_MSEC), nullptr, aStringOut);
|
||||
}
|
||||
|
||||
// performs a locale sensitive date formatting operation on the PRExplodedTime parameter
|
||||
/*static*/ nsresult
|
||||
DateTimeFormat::FormatPRExplodedTime(const nsDateFormatSelector aDateFormatSelector,
|
||||
const nsTimeFormatSelector aTimeFormatSelector,
|
||||
const PRExplodedTime* aExplodedTime,
|
||||
nsAString& aStringOut)
|
||||
{
|
||||
return FormatUDateTime(aDateFormatSelector, aTimeFormatSelector, (PR_ImplodeTime(aExplodedTime) / PR_USEC_PER_MSEC), &(aExplodedTime->tm_params), aStringOut);
|
||||
}
|
||||
|
||||
// performs a locale sensitive date formatting operation on the UDate parameter
|
||||
/*static*/ nsresult
|
||||
DateTimeFormat::FormatUDateTime(const nsDateFormatSelector aDateFormatSelector,
|
||||
const nsTimeFormatSelector aTimeFormatSelector,
|
||||
const UDate aUDateTime,
|
||||
const PRTimeParameters* aTimeParameters,
|
||||
nsAString& aStringOut)
|
||||
{
|
||||
#define DATETIME_FORMAT_INITIAL_LEN 127
|
||||
int32_t dateTimeLen = 0;
|
||||
|
@ -76,8 +96,6 @@ DateTimeFormat::FormatPRTime(const nsDateFormatSelector aDateFormatSelector,
|
|||
return rv;
|
||||
}
|
||||
|
||||
UDate timeUDate = aPrTime / PR_USEC_PER_MSEC;
|
||||
|
||||
// Get the date style for the formatter:
|
||||
UDateFormatStyle dateStyle;
|
||||
switch (aDateFormatSelector) {
|
||||
|
@ -116,16 +134,31 @@ DateTimeFormat::FormatPRTime(const nsDateFormatSelector aDateFormatSelector,
|
|||
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
|
||||
UDateFormat* dateTimeFormat = udat_open(timeStyle, dateStyle, mLocale->get(), nullptr, -1, nullptr, -1, &status);
|
||||
UDateFormat* dateTimeFormat;
|
||||
if (aTimeParameters) {
|
||||
nsAutoString timeZoneID(u"GMT");
|
||||
|
||||
int32_t totalOffsetMinutes = (aTimeParameters->tp_gmt_offset + aTimeParameters->tp_dst_offset) / 60;
|
||||
if (totalOffsetMinutes != 0) {
|
||||
char sign = totalOffsetMinutes < 0 ? '-' : '+';
|
||||
int32_t hours = abs(totalOffsetMinutes) / 60;
|
||||
int32_t minutes = abs(totalOffsetMinutes) % 60;
|
||||
timeZoneID.AppendPrintf("%c%02d:%02d", sign, hours, minutes);
|
||||
}
|
||||
|
||||
dateTimeFormat = udat_open(timeStyle, dateStyle, mLocale->get(), reinterpret_cast<const UChar*>(timeZoneID.BeginReading()), timeZoneID.Length(), nullptr, -1, &status);
|
||||
} else {
|
||||
dateTimeFormat = udat_open(timeStyle, dateStyle, mLocale->get(), nullptr, -1, nullptr, -1, &status);
|
||||
}
|
||||
|
||||
if (U_SUCCESS(status) && dateTimeFormat) {
|
||||
aStringOut.SetLength(DATETIME_FORMAT_INITIAL_LEN);
|
||||
dateTimeLen = udat_format(dateTimeFormat, timeUDate, reinterpret_cast<UChar*>(aStringOut.BeginWriting()), DATETIME_FORMAT_INITIAL_LEN, nullptr, &status);
|
||||
dateTimeLen = udat_format(dateTimeFormat, aUDateTime, reinterpret_cast<UChar*>(aStringOut.BeginWriting()), DATETIME_FORMAT_INITIAL_LEN, nullptr, &status);
|
||||
aStringOut.SetLength(dateTimeLen);
|
||||
|
||||
if (status == U_BUFFER_OVERFLOW_ERROR) {
|
||||
status = U_ZERO_ERROR;
|
||||
udat_format(dateTimeFormat, timeUDate, reinterpret_cast<UChar*>(aStringOut.BeginWriting()), dateTimeLen, nullptr, &status);
|
||||
udat_format(dateTimeFormat, aUDateTime, reinterpret_cast<UChar*>(aStringOut.BeginWriting()), dateTimeLen, nullptr, &status);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -140,16 +173,6 @@ DateTimeFormat::FormatPRTime(const nsDateFormatSelector aDateFormatSelector,
|
|||
return rv;
|
||||
}
|
||||
|
||||
// performs a locale sensitive date formatting operation on the PRExplodedTime parameter
|
||||
/*static*/ nsresult
|
||||
DateTimeFormat::FormatPRExplodedTime(const nsDateFormatSelector aDateFormatSelector,
|
||||
const nsTimeFormatSelector aTimeFormatSelector,
|
||||
const PRExplodedTime* aExplodedTime,
|
||||
nsAString& aStringOut)
|
||||
{
|
||||
return FormatPRTime(aDateFormatSelector, aTimeFormatSelector, PR_ImplodeTime(aExplodedTime), aStringOut);
|
||||
}
|
||||
|
||||
/*static*/ void
|
||||
DateTimeFormat::Shutdown()
|
||||
{
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
#include "gtest/gtest.h"
|
||||
#include "DateTimeFormat.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
TEST(DateTimeFormat, FormatPRExplodedTime) {
|
||||
PRTime prTime = 0;
|
||||
PRExplodedTime prExplodedTime;
|
||||
PR_ExplodeTime(prTime, PR_GMTParameters, &prExplodedTime);
|
||||
|
||||
mozilla::DateTimeFormat::mLocale = new nsCString("en-US");
|
||||
|
||||
nsAutoString formattedTime;
|
||||
nsresult rv = mozilla::DateTimeFormat::FormatPRExplodedTime(kDateFormatLong, kTimeFormatSeconds, &prExplodedTime, formattedTime);
|
||||
ASSERT_TRUE(NS_SUCCEEDED(rv));
|
||||
ASSERT_STREQ("January 1, 1970 at 12:00:00 AM", NS_ConvertUTF16toUTF8(formattedTime).get());
|
||||
|
||||
prExplodedTime = { 0, 0, 19, 0, 1, 0, 1970, 4, 0, { (19 * 60), 0 } };
|
||||
rv = mozilla::DateTimeFormat::FormatPRExplodedTime(kDateFormatLong, kTimeFormatSeconds, &prExplodedTime, formattedTime);
|
||||
ASSERT_TRUE(NS_SUCCEEDED(rv));
|
||||
ASSERT_STREQ("January 1, 1970 at 12:19:00 AM", NS_ConvertUTF16toUTF8(formattedTime).get());
|
||||
|
||||
prExplodedTime = { 0, 0, 0, 7, 1, 0, 1970, 4, 0, { (6 * 60 * 60), (1 * 60 * 60) } };
|
||||
rv = mozilla::DateTimeFormat::FormatPRExplodedTime(kDateFormatLong, kTimeFormatSeconds, &prExplodedTime, formattedTime);
|
||||
ASSERT_TRUE(NS_SUCCEEDED(rv));
|
||||
ASSERT_STREQ("January 1, 1970 at 7:00:00 AM", NS_ConvertUTF16toUTF8(formattedTime).get());
|
||||
|
||||
prExplodedTime = { 0, 0, 29, 11, 1, 0, 1970, 4, 0, { (10 * 60 * 60) + (29 * 60), (1 * 60 * 60) } };
|
||||
rv = mozilla::DateTimeFormat::FormatPRExplodedTime(kDateFormatLong, kTimeFormatSeconds, &prExplodedTime, formattedTime);
|
||||
ASSERT_TRUE(NS_SUCCEEDED(rv));
|
||||
ASSERT_STREQ("January 1, 1970 at 11:29:00 AM", NS_ConvertUTF16toUTF8(formattedTime).get());
|
||||
|
||||
prExplodedTime = { 0, 0, 37, 23, 31, 11, 1969, 3, 364, { -(23 * 60), 0 } };
|
||||
rv = mozilla::DateTimeFormat::FormatPRExplodedTime(kDateFormatLong, kTimeFormatSeconds, &prExplodedTime, formattedTime);
|
||||
ASSERT_TRUE(NS_SUCCEEDED(rv));
|
||||
ASSERT_STREQ("December 31, 1969 at 11:37:00 PM", NS_ConvertUTF16toUTF8(formattedTime).get());
|
||||
|
||||
prExplodedTime = { 0, 0, 0, 17, 31, 11, 1969, 3, 364, { -(7 * 60 * 60), 0 } };
|
||||
rv = mozilla::DateTimeFormat::FormatPRExplodedTime(kDateFormatLong, kTimeFormatSeconds, &prExplodedTime, formattedTime);
|
||||
ASSERT_TRUE(NS_SUCCEEDED(rv));
|
||||
ASSERT_STREQ("December 31, 1969 at 5:00:00 PM", NS_ConvertUTF16toUTF8(formattedTime).get());
|
||||
|
||||
prExplodedTime = { 0, 0, 47, 14, 31, 11, 1969, 3, 364, { -((10 * 60 * 60) + (13 * 60)), (1 * 60 * 60) } };
|
||||
rv = mozilla::DateTimeFormat::FormatPRExplodedTime(kDateFormatLong, kTimeFormatSeconds, &prExplodedTime, formattedTime);
|
||||
ASSERT_TRUE(NS_SUCCEEDED(rv));
|
||||
ASSERT_STREQ("December 31, 1969 at 2:47:00 PM", NS_ConvertUTF16toUTF8(formattedTime).get());
|
||||
}
|
||||
|
||||
}
|
|
@ -9,7 +9,10 @@ UNIFIED_SOURCES += [
|
|||
]
|
||||
|
||||
if CONFIG['ENABLE_INTL_API']:
|
||||
UNIFIED_SOURCES += ['TestOSPreferences.cpp']
|
||||
UNIFIED_SOURCES += [
|
||||
'TestDateTimeFormat.cpp',
|
||||
'TestOSPreferences.cpp',
|
||||
]
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'/intl/locale',
|
||||
|
|
|
@ -20,12 +20,12 @@
|
|||
#endif
|
||||
#include "jswrapper.h"
|
||||
|
||||
#include "jit/AtomicOperations.h"
|
||||
#include "js/Conversions.h"
|
||||
#include "vm/ArrayBufferObject.h"
|
||||
#include "vm/GlobalObject.h"
|
||||
#include "vm/Interpreter.h"
|
||||
#include "vm/SharedMem.h"
|
||||
#include "vm/TypedArrayCommon.h"
|
||||
#include "vm/WrapperObject.h"
|
||||
|
||||
#include "gc/Nursery-inl.h"
|
||||
|
|
|
@ -3304,7 +3304,7 @@ function Intl_getDisplayNames(locales, options) {
|
|||
for (let i = 0; i < len; i++) {
|
||||
// a. Let processedKey be ? ToString(? Get(keys, i)).
|
||||
// b. Perform ? CreateDataPropertyOrThrow(processedKeys, i, processedKey).
|
||||
callFunction(std_Array_push, processedKeys, ToString(keys[i]));
|
||||
_DefineDataProperty(processedKeys, i, ToString(keys[i]));
|
||||
}
|
||||
|
||||
// 16. Let names be ? ComputeDisplayNames(r.[[locale]], style, processedKeys).
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include "jsprf.h"
|
||||
|
||||
#include "builtin/TypedObject.h"
|
||||
#include "jit/AtomicOperations.h"
|
||||
#include "jit/InlinableNatives.h"
|
||||
#include "js/GCAPI.h"
|
||||
#include "js/Value.h"
|
||||
|
|
|
@ -44,6 +44,7 @@
|
|||
#include "ctypes/Library.h"
|
||||
#include "gc/Policy.h"
|
||||
#include "gc/Zone.h"
|
||||
#include "jit/AtomicOperations.h"
|
||||
#include "js/Vector.h"
|
||||
|
||||
#include "jsatominlines.h"
|
||||
|
|
|
@ -102,7 +102,7 @@ class BackgroundDecommitTask : public GCParallelTask
|
|||
void run() override;
|
||||
|
||||
private:
|
||||
UnprotectedData<ChunkVector> toDecommit;
|
||||
ActiveThreadOrGCTaskData<ChunkVector> toDecommit;
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -120,14 +120,14 @@ class GCSchedulingTunables
|
|||
UnprotectedData<size_t> gcMaxBytes_;
|
||||
|
||||
/* Maximum nursery size for each zone group. */
|
||||
UnprotectedData<size_t> gcMaxNurseryBytes_;
|
||||
ActiveThreadData<size_t> gcMaxNurseryBytes_;
|
||||
|
||||
/*
|
||||
* The base value used to compute zone->trigger.gcBytes(). When
|
||||
* usage.gcBytes() surpasses threshold.gcBytes() for a zone, the zone may
|
||||
* be scheduled for a GC, depending on the exact circumstances.
|
||||
*/
|
||||
UnprotectedData<size_t> gcZoneAllocThresholdBase_;
|
||||
ActiveThreadOrGCTaskData<size_t> gcZoneAllocThresholdBase_;
|
||||
|
||||
/* Fraction of threshold.gcBytes() which triggers an incremental GC. */
|
||||
UnprotectedData<double> zoneAllocThresholdFactor_;
|
||||
|
@ -142,38 +142,38 @@ class GCSchedulingTunables
|
|||
* Totally disables |highFrequencyGC|, the HeapGrowthFactor, and other
|
||||
* tunables that make GC non-deterministic.
|
||||
*/
|
||||
UnprotectedData<bool> dynamicHeapGrowthEnabled_;
|
||||
ActiveThreadData<bool> dynamicHeapGrowthEnabled_;
|
||||
|
||||
/*
|
||||
* We enter high-frequency mode if we GC a twice within this many
|
||||
* microseconds. This value is stored directly in microseconds.
|
||||
*/
|
||||
UnprotectedData<uint64_t> highFrequencyThresholdUsec_;
|
||||
ActiveThreadData<uint64_t> highFrequencyThresholdUsec_;
|
||||
|
||||
/*
|
||||
* When in the |highFrequencyGC| mode, these parameterize the per-zone
|
||||
* "HeapGrowthFactor" computation.
|
||||
*/
|
||||
UnprotectedData<uint64_t> highFrequencyLowLimitBytes_;
|
||||
UnprotectedData<uint64_t> highFrequencyHighLimitBytes_;
|
||||
UnprotectedData<double> highFrequencyHeapGrowthMax_;
|
||||
UnprotectedData<double> highFrequencyHeapGrowthMin_;
|
||||
ActiveThreadData<uint64_t> highFrequencyLowLimitBytes_;
|
||||
ActiveThreadData<uint64_t> highFrequencyHighLimitBytes_;
|
||||
ActiveThreadData<double> highFrequencyHeapGrowthMax_;
|
||||
ActiveThreadData<double> highFrequencyHeapGrowthMin_;
|
||||
|
||||
/*
|
||||
* When not in |highFrequencyGC| mode, this is the global (stored per-zone)
|
||||
* "HeapGrowthFactor".
|
||||
*/
|
||||
UnprotectedData<double> lowFrequencyHeapGrowth_;
|
||||
ActiveThreadData<double> lowFrequencyHeapGrowth_;
|
||||
|
||||
/*
|
||||
* Doubles the length of IGC slices when in the |highFrequencyGC| mode.
|
||||
*/
|
||||
UnprotectedData<bool> dynamicMarkSliceEnabled_;
|
||||
ActiveThreadData<bool> dynamicMarkSliceEnabled_;
|
||||
|
||||
/*
|
||||
* Controls whether painting can trigger IGC slices.
|
||||
*/
|
||||
UnprotectedData<bool> refreshFrameSlicesEnabled_;
|
||||
ActiveThreadData<bool> refreshFrameSlicesEnabled_;
|
||||
|
||||
/*
|
||||
* Controls the number of empty chunks reserved for future allocation.
|
||||
|
@ -526,7 +526,7 @@ class GCSchedulingState
|
|||
* growth factor is a measure of how large (as a percentage of the last GC)
|
||||
* the heap is allowed to grow before we try to schedule another GC.
|
||||
*/
|
||||
UnprotectedData<bool> inHighFrequencyGCMode_;
|
||||
ActiveThreadData<bool> inHighFrequencyGCMode_;
|
||||
|
||||
public:
|
||||
GCSchedulingState()
|
||||
|
@ -545,8 +545,8 @@ class GCSchedulingState
|
|||
|
||||
template<typename F>
|
||||
struct Callback {
|
||||
UnprotectedData<F> op;
|
||||
UnprotectedData<void*> data;
|
||||
ActiveThreadData<F> op;
|
||||
ActiveThreadData<void*> data;
|
||||
|
||||
Callback()
|
||||
: op(nullptr), data(nullptr)
|
||||
|
@ -557,7 +557,7 @@ struct Callback {
|
|||
};
|
||||
|
||||
template<typename F>
|
||||
using CallbackVector = UnprotectedData<Vector<Callback<F>, 4, SystemAllocPolicy>>;
|
||||
using CallbackVector = ActiveThreadData<Vector<Callback<F>, 4, SystemAllocPolicy>>;
|
||||
|
||||
template <typename T, typename Iter0, typename Iter1>
|
||||
class ChainedIter
|
||||
|
@ -979,7 +979,7 @@ class GCRuntime
|
|||
UnprotectedData<JS::Zone*> systemZone;
|
||||
|
||||
// List of all zone groups (protected by the GC lock).
|
||||
UnprotectedData<ZoneGroupVector> groups;
|
||||
ActiveThreadData<ZoneGroupVector> groups;
|
||||
|
||||
// The unique atoms zone, which has no zone group.
|
||||
WriteOnceData<Zone*> atomsZone;
|
||||
|
@ -1023,9 +1023,9 @@ class GCRuntime
|
|||
// so as to reduce the cost of operations on the available lists.
|
||||
UnprotectedData<ChunkPool> fullChunks_;
|
||||
|
||||
UnprotectedData<RootedValueMap> rootsHash;
|
||||
ActiveThreadData<RootedValueMap> rootsHash;
|
||||
|
||||
UnprotectedData<size_t> maxMallocBytes;
|
||||
ActiveThreadData<size_t> maxMallocBytes;
|
||||
|
||||
// An incrementing id used to assign unique ids to cells that require one.
|
||||
mozilla::Atomic<uint64_t, mozilla::ReleaseAcquire> nextCellUniqueId_;
|
||||
|
@ -1034,18 +1034,18 @@ class GCRuntime
|
|||
* Number of the committed arenas in all GC chunks including empty chunks.
|
||||
*/
|
||||
mozilla::Atomic<uint32_t, mozilla::ReleaseAcquire> numArenasFreeCommitted;
|
||||
UnprotectedData<VerifyPreTracer*> verifyPreData;
|
||||
ActiveThreadData<VerifyPreTracer*> verifyPreData;
|
||||
|
||||
private:
|
||||
UnprotectedData<bool> chunkAllocationSinceLastGC;
|
||||
UnprotectedData<int64_t> lastGCTime;
|
||||
ActiveThreadData<int64_t> lastGCTime;
|
||||
|
||||
UnprotectedData<JSGCMode> mode;
|
||||
ActiveThreadData<JSGCMode> mode;
|
||||
|
||||
mozilla::Atomic<size_t, mozilla::ReleaseAcquire> numActiveZoneIters;
|
||||
|
||||
/* During shutdown, the GC needs to clean up every possible object. */
|
||||
UnprotectedData<bool> cleanUpEverything;
|
||||
ActiveThreadData<bool> cleanUpEverything;
|
||||
|
||||
// Gray marking must be done after all black marking is complete. However,
|
||||
// we do not have write barriers on XPConnect roots. Therefore, XPConnect
|
||||
|
@ -1058,7 +1058,7 @@ class GCRuntime
|
|||
Okay,
|
||||
Failed
|
||||
};
|
||||
UnprotectedData<GrayBufferState> grayBufferState;
|
||||
ActiveThreadData<GrayBufferState> grayBufferState;
|
||||
bool hasBufferedGrayRoots() const { return grayBufferState == GrayBufferState::Okay; }
|
||||
|
||||
// Clear each zone's gray buffers, but do not change the current state.
|
||||
|
@ -1079,83 +1079,83 @@ class GCRuntime
|
|||
mozilla::Atomic<JS::gcreason::Reason, mozilla::Relaxed> majorGCTriggerReason;
|
||||
|
||||
public:
|
||||
UnprotectedData<JS::gcreason::Reason> minorGCTriggerReason;
|
||||
ActiveThreadData<JS::gcreason::Reason> minorGCTriggerReason;
|
||||
|
||||
private:
|
||||
/* Perform full GC if rt->keepAtoms() becomes false. */
|
||||
UnprotectedData<bool> fullGCForAtomsRequested_;
|
||||
ActiveThreadData<bool> fullGCForAtomsRequested_;
|
||||
|
||||
/* Incremented at the start of every minor GC. */
|
||||
UnprotectedData<uint64_t> minorGCNumber;
|
||||
ActiveThreadData<uint64_t> minorGCNumber;
|
||||
|
||||
/* Incremented at the start of every major GC. */
|
||||
UnprotectedData<uint64_t> majorGCNumber;
|
||||
ActiveThreadData<uint64_t> majorGCNumber;
|
||||
|
||||
/* The major GC number at which to release observed type information. */
|
||||
UnprotectedData<uint64_t> jitReleaseNumber;
|
||||
ActiveThreadData<uint64_t> jitReleaseNumber;
|
||||
|
||||
/* Incremented on every GC slice. */
|
||||
UnprotectedData<uint64_t> number;
|
||||
ActiveThreadData<uint64_t> number;
|
||||
|
||||
/* The number at the time of the most recent GC's first slice. */
|
||||
UnprotectedData<uint64_t> startNumber;
|
||||
ActiveThreadData<uint64_t> startNumber;
|
||||
|
||||
/* Whether the currently running GC can finish in multiple slices. */
|
||||
UnprotectedData<bool> isIncremental;
|
||||
ActiveThreadData<bool> isIncremental;
|
||||
|
||||
/* Whether all zones are being collected in first GC slice. */
|
||||
UnprotectedData<bool> isFull;
|
||||
ActiveThreadData<bool> isFull;
|
||||
|
||||
/* Whether the heap will be compacted at the end of GC. */
|
||||
UnprotectedData<bool> isCompacting;
|
||||
ActiveThreadData<bool> isCompacting;
|
||||
|
||||
/* The invocation kind of the current GC, taken from the first slice. */
|
||||
UnprotectedData<JSGCInvocationKind> invocationKind;
|
||||
ActiveThreadData<JSGCInvocationKind> invocationKind;
|
||||
|
||||
/* The initial GC reason, taken from the first slice. */
|
||||
UnprotectedData<JS::gcreason::Reason> initialReason;
|
||||
ActiveThreadData<JS::gcreason::Reason> initialReason;
|
||||
|
||||
/*
|
||||
* The current incremental GC phase. This is also used internally in
|
||||
* non-incremental GC.
|
||||
*/
|
||||
UnprotectedData<State> incrementalState;
|
||||
ActiveThreadOrGCTaskData<State> incrementalState;
|
||||
|
||||
/* Indicates that the last incremental slice exhausted the mark stack. */
|
||||
UnprotectedData<bool> lastMarkSlice;
|
||||
ActiveThreadData<bool> lastMarkSlice;
|
||||
|
||||
/* Whether any sweeping will take place in the separate GC helper thread. */
|
||||
UnprotectedData<bool> sweepOnBackgroundThread;
|
||||
ActiveThreadData<bool> sweepOnBackgroundThread;
|
||||
|
||||
/* Whether observed type information is being released in the current GC. */
|
||||
UnprotectedData<bool> releaseObservedTypes;
|
||||
ActiveThreadData<bool> releaseObservedTypes;
|
||||
|
||||
/* Whether any black->gray edges were found during marking. */
|
||||
UnprotectedData<BlackGrayEdgeVector> foundBlackGrayEdges;
|
||||
ActiveThreadData<BlackGrayEdgeVector> foundBlackGrayEdges;
|
||||
|
||||
/* Singly linked list of zones to be swept in the background. */
|
||||
UnprotectedData<ZoneList> backgroundSweepZones;
|
||||
ActiveThreadOrGCTaskData<ZoneList> backgroundSweepZones;
|
||||
|
||||
/*
|
||||
* Free LIFO blocks are transferred to this allocator before being freed on
|
||||
* the background GC thread after sweeping.
|
||||
*/
|
||||
UnprotectedData<LifoAlloc> blocksToFreeAfterSweeping;
|
||||
ActiveThreadOrGCTaskData<LifoAlloc> blocksToFreeAfterSweeping;
|
||||
|
||||
private:
|
||||
/* Index of current zone group (for stats). */
|
||||
UnprotectedData<unsigned> zoneGroupIndex;
|
||||
ActiveThreadData<unsigned> zoneGroupIndex;
|
||||
|
||||
/*
|
||||
* Incremental sweep state.
|
||||
*/
|
||||
UnprotectedData<JS::Zone*> zoneGroups;
|
||||
UnprotectedData<JS::Zone*> currentZoneGroup;
|
||||
UnprotectedData<bool> sweepingTypes;
|
||||
UnprotectedData<unsigned> finalizePhase;
|
||||
UnprotectedData<JS::Zone*> sweepZone;
|
||||
UnprotectedData<AllocKind> sweepKind;
|
||||
UnprotectedData<bool> abortSweepAfterCurrentGroup;
|
||||
ActiveThreadData<JS::Zone*> zoneGroups;
|
||||
ActiveThreadOrGCTaskData<JS::Zone*> currentZoneGroup;
|
||||
ActiveThreadData<bool> sweepingTypes;
|
||||
ActiveThreadData<unsigned> finalizePhase;
|
||||
ActiveThreadData<JS::Zone*> sweepZone;
|
||||
ActiveThreadData<AllocKind> sweepKind;
|
||||
ActiveThreadData<bool> abortSweepAfterCurrentGroup;
|
||||
|
||||
/*
|
||||
* Concurrent sweep infrastructure.
|
||||
|
@ -1166,17 +1166,17 @@ class GCRuntime
|
|||
/*
|
||||
* List head of arenas allocated during the sweep phase.
|
||||
*/
|
||||
UnprotectedData<Arena*> arenasAllocatedDuringSweep;
|
||||
ActiveThreadData<Arena*> arenasAllocatedDuringSweep;
|
||||
|
||||
/*
|
||||
* Incremental compacting state.
|
||||
*/
|
||||
UnprotectedData<bool> startedCompacting;
|
||||
UnprotectedData<ZoneList> zonesToMaybeCompact;
|
||||
UnprotectedData<Arena*> relocatedArenasToRelease;
|
||||
ActiveThreadData<bool> startedCompacting;
|
||||
ActiveThreadData<ZoneList> zonesToMaybeCompact;
|
||||
ActiveThreadData<Arena*> relocatedArenasToRelease;
|
||||
|
||||
#ifdef JS_GC_ZEAL
|
||||
UnprotectedData<MarkingValidator*> markingValidator;
|
||||
ActiveThreadData<MarkingValidator*> markingValidator;
|
||||
#endif
|
||||
|
||||
/*
|
||||
|
@ -1184,23 +1184,23 @@ class GCRuntime
|
|||
* frame, rather than at the beginning. In this case, the next slice will be
|
||||
* delayed so that we don't get back-to-back slices.
|
||||
*/
|
||||
UnprotectedData<bool> interFrameGC;
|
||||
ActiveThreadData<bool> interFrameGC;
|
||||
|
||||
/* Default budget for incremental GC slice. See js/SliceBudget.h. */
|
||||
UnprotectedData<int64_t> defaultTimeBudget_;
|
||||
ActiveThreadData<int64_t> defaultTimeBudget_;
|
||||
|
||||
/*
|
||||
* We disable incremental GC if we encounter a Class with a trace hook
|
||||
* that does not implement write barriers.
|
||||
*/
|
||||
UnprotectedData<bool> incrementalAllowed;
|
||||
ActiveThreadData<bool> incrementalAllowed;
|
||||
|
||||
/*
|
||||
* Whether compacting GC can is enabled globally.
|
||||
*/
|
||||
UnprotectedData<bool> compactingEnabled;
|
||||
ActiveThreadData<bool> compactingEnabled;
|
||||
|
||||
UnprotectedData<bool> poked;
|
||||
ActiveThreadData<bool> poked;
|
||||
|
||||
/*
|
||||
* These options control the zealousness of the GC. At every allocation,
|
||||
|
@ -1227,16 +1227,16 @@ class GCRuntime
|
|||
* zeal_ value 14 performs periodic shrinking collections.
|
||||
*/
|
||||
#ifdef JS_GC_ZEAL
|
||||
UnprotectedData<uint32_t> zealModeBits;
|
||||
UnprotectedData<int> zealFrequency;
|
||||
UnprotectedData<int> nextScheduled;
|
||||
UnprotectedData<bool> deterministicOnly;
|
||||
UnprotectedData<int> incrementalLimit;
|
||||
ActiveThreadData<uint32_t> zealModeBits;
|
||||
ActiveThreadData<int> zealFrequency;
|
||||
ActiveThreadData<int> nextScheduled;
|
||||
ActiveThreadData<bool> deterministicOnly;
|
||||
ActiveThreadData<int> incrementalLimit;
|
||||
|
||||
UnprotectedData<Vector<JSObject*, 0, SystemAllocPolicy>> selectedForMarking;
|
||||
ActiveThreadData<Vector<JSObject*, 0, SystemAllocPolicy>> selectedForMarking;
|
||||
#endif
|
||||
|
||||
UnprotectedData<bool> fullCompartmentChecks;
|
||||
ActiveThreadData<bool> fullCompartmentChecks;
|
||||
|
||||
Callback<JSGCCallback> gcCallback;
|
||||
Callback<JS::DoCycleCollectionCallback> gcDoCycleCollectionCallback;
|
||||
|
@ -1267,10 +1267,10 @@ class GCRuntime
|
|||
Callback<JSTraceDataOp> grayRootTracer;
|
||||
|
||||
/* Always preserve JIT code during GCs, for testing. */
|
||||
UnprotectedData<bool> alwaysPreserveCode;
|
||||
ActiveThreadData<bool> alwaysPreserveCode;
|
||||
|
||||
#ifdef DEBUG
|
||||
UnprotectedData<bool> arenasEmptyAtShutdown;
|
||||
ActiveThreadData<bool> arenasEmptyAtShutdown;
|
||||
#endif
|
||||
|
||||
/* Synchronize GC heap access among GC helper threads and main threads. */
|
||||
|
@ -1286,7 +1286,7 @@ class GCRuntime
|
|||
* During incremental sweeping, this field temporarily holds the arenas of
|
||||
* the current AllocKind being swept in order of increasing free space.
|
||||
*/
|
||||
UnprotectedData<SortedArenaList> incrementalSweepList;
|
||||
ActiveThreadData<SortedArenaList> incrementalSweepList;
|
||||
|
||||
friend class js::GCHelperState;
|
||||
friend class MarkingValidator;
|
||||
|
|
|
@ -57,13 +57,13 @@ class MarkStack
|
|||
{
|
||||
friend class GCMarker;
|
||||
|
||||
UnprotectedData<uintptr_t*> stack_;
|
||||
UnprotectedData<uintptr_t*> tos_;
|
||||
UnprotectedData<uintptr_t*> end_;
|
||||
ActiveThreadData<uintptr_t*> stack_;
|
||||
ActiveThreadData<uintptr_t*> tos_;
|
||||
ActiveThreadData<uintptr_t*> end_;
|
||||
|
||||
// The capacity we start with and reset() to.
|
||||
UnprotectedData<size_t> baseCapacity_;
|
||||
UnprotectedData<size_t> maxCapacity_;
|
||||
ActiveThreadData<size_t> baseCapacity_;
|
||||
ActiveThreadData<size_t> maxCapacity_;
|
||||
|
||||
public:
|
||||
explicit MarkStack(size_t maxCapacity)
|
||||
|
@ -336,29 +336,29 @@ class GCMarker : public JSTracer
|
|||
MarkStack stack;
|
||||
|
||||
/* The color is only applied to objects and functions. */
|
||||
UnprotectedData<uint32_t> color;
|
||||
ActiveThreadData<uint32_t> color;
|
||||
|
||||
/* Pointer to the top of the stack of arenas we are delaying marking on. */
|
||||
UnprotectedData<js::gc::Arena*> unmarkedArenaStackTop;
|
||||
ActiveThreadData<js::gc::Arena*> unmarkedArenaStackTop;
|
||||
|
||||
/*
|
||||
* If the weakKeys table OOMs, disable the linear algorithm and fall back
|
||||
* to iterating until the next GC.
|
||||
*/
|
||||
UnprotectedData<bool> linearWeakMarkingDisabled_;
|
||||
ActiveThreadData<bool> linearWeakMarkingDisabled_;
|
||||
|
||||
#ifdef DEBUG
|
||||
/* Count of arenas that are currently in the stack. */
|
||||
UnprotectedData<size_t> markLaterArenas;
|
||||
ActiveThreadData<size_t> markLaterArenas;
|
||||
|
||||
/* Assert that start and stop are called with correct ordering. */
|
||||
UnprotectedData<bool> started;
|
||||
ActiveThreadData<bool> started;
|
||||
|
||||
/*
|
||||
* If this is true, all marked objects must belong to a compartment being
|
||||
* GCed. This is used to look for compartment bugs.
|
||||
*/
|
||||
UnprotectedData<bool> strictCompartmentChecking;
|
||||
ActiveThreadData<bool> strictCompartmentChecking;
|
||||
#endif // DEBUG
|
||||
};
|
||||
|
||||
|
|
|
@ -341,7 +341,7 @@ struct Zone : public JS::shadow::Zone,
|
|||
|
||||
private:
|
||||
// The set of compartments in this zone.
|
||||
js::UnprotectedData<CompartmentVector> compartments_;
|
||||
js::ActiveThreadOrGCTaskData<CompartmentVector> compartments_;
|
||||
public:
|
||||
CompartmentVector& compartments() { return compartments_.ref(); }
|
||||
|
||||
|
@ -410,7 +410,7 @@ struct Zone : public JS::shadow::Zone,
|
|||
mozilla::Atomic<ptrdiff_t, mozilla::ReleaseAcquire> gcMallocBytes;
|
||||
|
||||
// GC trigger threshold for allocations on the C heap.
|
||||
js::UnprotectedData<size_t> gcMaxMallocBytes;
|
||||
js::ActiveThreadData<size_t> gcMaxMallocBytes;
|
||||
|
||||
// Whether a GC has been triggered as a result of gcMallocBytes falling
|
||||
// below zero.
|
||||
|
@ -582,7 +582,7 @@ struct Zone : public JS::shadow::Zone,
|
|||
js::ZoneGroupData<js::jit::JitZone*> jitZone_;
|
||||
|
||||
js::UnprotectedData<GCState> gcState_;
|
||||
js::UnprotectedData<bool> gcScheduled_;
|
||||
js::ActiveThreadData<bool> gcScheduled_;
|
||||
js::ZoneGroupData<bool> gcPreserveCode_;
|
||||
js::ZoneGroupData<bool> jitUsingBarriers_;
|
||||
js::ZoneGroupData<bool> keepShapeTables_;
|
||||
|
@ -810,11 +810,11 @@ struct GCManagedDeletePolicy
|
|||
{
|
||||
void operator()(const T* ptr) {
|
||||
if (ptr) {
|
||||
JSContext* cx = TlsContext.get();
|
||||
if (cx->runtime()->zoneGroupFromMainThread()->nursery().isEnabled()) {
|
||||
JSRuntime* rt = TlsContext.get()->runtime();
|
||||
if (CurrentThreadCanAccessRuntime(rt) && rt->zoneGroupFromMainThread()->nursery().isEnabled()) {
|
||||
// The object may contain nursery pointers and must only be
|
||||
// destroyed after a minor GC.
|
||||
cx->runtime()->zoneGroupFromMainThread()->callAfterMinorGC(deletePtr, const_cast<T*>(ptr));
|
||||
rt->zoneGroupFromMainThread()->callAfterMinorGC(deletePtr, const_cast<T*>(ptr));
|
||||
} else {
|
||||
// The object cannot contain nursery pointers so can be
|
||||
// destroyed immediately.
|
||||
|
|
|
@ -17,7 +17,7 @@ ZoneGroup::ZoneGroup(JSRuntime* runtime)
|
|||
context(TlsContext.get()),
|
||||
enterCount(this, 1),
|
||||
zones_(),
|
||||
nursery_(this),
|
||||
nursery_(this, this),
|
||||
storeBuffer_(this, runtime, nursery()),
|
||||
blocksToFreeAfterMinorGC((size_t) JSContext::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
|
||||
caches_(this),
|
||||
|
|
|
@ -54,7 +54,7 @@ class ZoneGroup
|
|||
|
||||
// All zones in the group.
|
||||
private:
|
||||
UnprotectedData<ZoneVector> zones_;
|
||||
ActiveThreadOrGCTaskData<ZoneVector> zones_;
|
||||
public:
|
||||
ZoneVector& zones() { return zones_.ref(); }
|
||||
|
||||
|
@ -64,7 +64,7 @@ class ZoneGroup
|
|||
bool init(size_t maxNurseryBytes);
|
||||
|
||||
private:
|
||||
UnprotectedData<Nursery> nursery_;
|
||||
ZoneGroupData<Nursery> nursery_;
|
||||
ZoneGroupData<gc::StoreBuffer> storeBuffer_;
|
||||
public:
|
||||
Nursery& nursery() { return nursery_.ref(); }
|
||||
|
@ -72,7 +72,7 @@ class ZoneGroup
|
|||
|
||||
// Free LIFO blocks are transferred to this allocator before being freed
|
||||
// after minor GC.
|
||||
UnprotectedData<LifoAlloc> blocksToFreeAfterMinorGC;
|
||||
ActiveThreadData<LifoAlloc> blocksToFreeAfterMinorGC;
|
||||
|
||||
void minorGC(JS::gcreason::Reason reason,
|
||||
gcstats::Phase phase = gcstats::PHASE_MINOR_GC) JS_HAZ_GC_CALL;
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
if (this.Intl) {
|
||||
Object.defineProperty(Object.prototype, 0, {
|
||||
set: function() {}
|
||||
});
|
||||
function checkDisplayNames(names, expected) {}
|
||||
addIntlExtras(Intl);
|
||||
let gDN = Intl.getDisplayNames;
|
||||
checkDisplayNames(gDN('ar', {
|
||||
keys: ['dates/fields/month', ]
|
||||
}), {});
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
JSON.stringify(this);
|
|
@ -0,0 +1,6 @@
|
|||
if (!('oomTest' in this))
|
||||
quit();
|
||||
oomTest(function () {
|
||||
offThreadCompileModule('');
|
||||
finishOffThreadModule();
|
||||
});
|
|
@ -1,6 +1,8 @@
|
|||
// |jit-test| exitstatus: 6;
|
||||
// |jit-test| error: 42;
|
||||
|
||||
for (var x of [0]) {
|
||||
timeout(0.001);
|
||||
for (;;) {}
|
||||
for (var i = 0; ; i++) {
|
||||
if (i === 20000)
|
||||
throw 42;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -124,8 +124,11 @@ testBinary32('rem_u', 41, 8, 1);
|
|||
testBinary32('and', 42, 6, 2);
|
||||
testBinary32('or', 42, 6, 46);
|
||||
testBinary32('xor', 42, 2, 40);
|
||||
testBinary32('shl', 40, 0, 40);
|
||||
testBinary32('shl', 40, 2, 160);
|
||||
testBinary32('shr_s', -40, 0, -40);
|
||||
testBinary32('shr_s', -40, 2, -10);
|
||||
testBinary32('shr_u', -40, 0, -40);
|
||||
testBinary32('shr_u', -40, 2, 1073741814);
|
||||
|
||||
testTrap32('div_s', 42, 0, /integer divide by zero/);
|
||||
|
@ -221,13 +224,16 @@ assertEq(testTrunc(13.37), 1);
|
|||
testBinary64('shl', 1, 63, "0x8000000000000000");
|
||||
testBinary64('shl', 1, 64, 1);
|
||||
testBinary64('shl', 40, 2, 160);
|
||||
testBinary64('shl', 40, 0, 40);
|
||||
|
||||
testBinary64('shr_s', -40, 0, -40);
|
||||
testBinary64('shr_s', -40, 2, -10);
|
||||
testBinary64('shr_s', "0xff00ff0000000", 28, 0xff00ff);
|
||||
testBinary64('shr_s', "0xff00ff0000000", 30, 0x3fc03f);
|
||||
testBinary64('shr_s', "0xff00ff0000000", 31, 0x1fe01f);
|
||||
testBinary64('shr_s', "0xff00ff0000000", 32, 0x0ff00f);
|
||||
|
||||
testBinary64('shr_u', -40, 0, -40);
|
||||
testBinary64('shr_u', -40, 2, "0x3ffffffffffffff6");
|
||||
testBinary64('shr_u', "0x8ffff00ff0000000", 30, "0x23fffc03f");
|
||||
testBinary64('shr_u', "0x8ffff00ff0000000", 31, "0x11fffe01f");
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
load(libdir + "wasm.js");
|
||||
|
||||
// Bug 1337060 causes too much register pressure on x86 by requiring four int64
|
||||
// values in registers at the same time.
|
||||
|
||||
setJitCompilerOption('wasm.test-mode', 1);
|
||||
|
||||
wasmFullPassI64(`
|
||||
(module
|
||||
(func (result i64)
|
||||
i64.const 0x2800000033
|
||||
i64.const 0x9900000044
|
||||
i64.const 0x1000000012
|
||||
i64.const 0x1000000013
|
||||
i64.lt_s
|
||||
select) (export "run" 0))`, createI64("0x2800000033"));
|
||||
|
||||
wasmFullPassI64(`
|
||||
(module
|
||||
(func (result i64)
|
||||
i64.const 0x2800000033
|
||||
i64.const 0x9900000044
|
||||
i64.const 0x1000000013
|
||||
i64.const 0x1000000012
|
||||
i64.lt_s
|
||||
select) (export "run" 0))`, createI64("0x9900000044"));
|
||||
|
||||
wasmFullPassI64(`
|
||||
(module
|
||||
(func (export "run") (result i64) (param f32)
|
||||
i64.const 0x13100000001
|
||||
i64.const 0x23370000002
|
||||
i64.const 0x34480000003
|
||||
i32.const 1
|
||||
select
|
||||
i32.const 1
|
||||
select
|
||||
i64.const 0x45590000004
|
||||
i32.const 1
|
||||
select
|
||||
)
|
||||
)`, createI64("0x13100000001"));
|
|
@ -837,8 +837,12 @@ BacktrackingAllocator::go()
|
|||
if (!processBundle(mir, item.bundle))
|
||||
return false;
|
||||
}
|
||||
|
||||
JitSpew(JitSpew_RegAlloc, "Main allocation loop complete");
|
||||
|
||||
if (!tryAllocatingRegistersForSpillBundles())
|
||||
return false;
|
||||
|
||||
if (!pickStackSlots())
|
||||
return false;
|
||||
|
||||
|
@ -1210,7 +1214,8 @@ BacktrackingAllocator::tryAllocateNonFixed(LiveBundle* bundle,
|
|||
|
||||
// Spill bundles which have no hint or register requirement.
|
||||
if (requirement.kind() == Requirement::NONE && hint.kind() != Requirement::REGISTER) {
|
||||
if (!spill(bundle))
|
||||
JitSpew(JitSpew_RegAlloc, " postponed spill (no hint or register requirement)");
|
||||
if (!spilledBundles.append(bundle))
|
||||
return false;
|
||||
*success = true;
|
||||
return true;
|
||||
|
@ -1230,7 +1235,8 @@ BacktrackingAllocator::tryAllocateNonFixed(LiveBundle* bundle,
|
|||
// Spill bundles which have no register requirement if they didn't get
|
||||
// allocated.
|
||||
if (requirement.kind() == Requirement::NONE) {
|
||||
if (!spill(bundle))
|
||||
JitSpew(JitSpew_RegAlloc, " postponed spill (no register requirement)");
|
||||
if (!spilledBundles.append(bundle))
|
||||
return false;
|
||||
*success = true;
|
||||
return true;
|
||||
|
@ -1588,6 +1594,38 @@ BacktrackingAllocator::spill(LiveBundle* bundle)
|
|||
return bundle->spillSet()->addSpilledBundle(bundle);
|
||||
}
|
||||
|
||||
bool
|
||||
BacktrackingAllocator::tryAllocatingRegistersForSpillBundles()
|
||||
{
|
||||
for (auto it = spilledBundles.begin(); it != spilledBundles.end(); it++) {
|
||||
LiveBundle* bundle = *it;
|
||||
LiveBundleVector conflicting;
|
||||
bool fixed = false;
|
||||
bool success = false;
|
||||
|
||||
if (mir->shouldCancel("Backtracking Try Allocating Spilled Bundles"))
|
||||
return false;
|
||||
|
||||
if (JitSpewEnabled(JitSpew_RegAlloc))
|
||||
JitSpew(JitSpew_RegAlloc, "Spill or allocate %s", bundle->toString().get());
|
||||
|
||||
// Search for any available register which the bundle can be
|
||||
// allocated to.
|
||||
for (size_t i = 0; i < AnyRegister::Total; i++) {
|
||||
if (!tryAllocateRegister(registers[i], bundle, &success, &fixed, conflicting))
|
||||
return false;
|
||||
if (success)
|
||||
break;
|
||||
}
|
||||
|
||||
// If the bundle still has no register, spill the bundle.
|
||||
if (!success && !spill(bundle))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
BacktrackingAllocator::pickStackSlots()
|
||||
{
|
||||
|
|
|
@ -672,6 +672,8 @@ class BacktrackingAllocator : protected RegisterAllocator
|
|||
// All allocated slots of each width.
|
||||
SpillSlotList normalSlots, doubleSlots, quadSlots;
|
||||
|
||||
Vector<LiveBundle*, 4, SystemAllocPolicy> spilledBundles;
|
||||
|
||||
public:
|
||||
BacktrackingAllocator(MIRGenerator* mir, LIRGenerator* lir, LIRGraph& graph, bool testbed)
|
||||
: RegisterAllocator(mir, lir, graph),
|
||||
|
@ -720,6 +722,7 @@ class BacktrackingAllocator : protected RegisterAllocator
|
|||
MOZ_MUST_USE bool splitAndRequeueBundles(LiveBundle* bundle,
|
||||
const LiveBundleVector& newBundles);
|
||||
MOZ_MUST_USE bool spill(LiveBundle* bundle);
|
||||
MOZ_MUST_USE bool tryAllocatingRegistersForSpillBundles();
|
||||
|
||||
bool isReusedInput(LUse* use, LNode* ins, bool considerCopy);
|
||||
bool isRegisterUse(UsePosition* use, LNode* ins, bool considerCopy = false);
|
||||
|
|
|
@ -32,7 +32,6 @@
|
|||
#include "js/GCVector.h"
|
||||
#include "vm/Opcodes.h"
|
||||
#include "vm/SelfHosting.h"
|
||||
#include "vm/TypedArrayCommon.h"
|
||||
#include "vm/TypedArrayObject.h"
|
||||
|
||||
#include "jsboolinlines.h"
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
#include "jit/shared/Assembler-shared.h"
|
||||
#include "js/TrackedOptimizationInfo.h"
|
||||
|
||||
#include "vm/TypedArrayCommon.h"
|
||||
#include "vm/TypedArrayObject.h"
|
||||
|
||||
namespace js {
|
||||
namespace jit {
|
||||
|
|
|
@ -87,10 +87,10 @@ class JitRuntime
|
|||
|
||||
// Executable allocator for all code except wasm code and Ion code with
|
||||
// patchable backedges (see below).
|
||||
UnprotectedData<ExecutableAllocator> execAlloc_;
|
||||
ActiveThreadData<ExecutableAllocator> execAlloc_;
|
||||
|
||||
// Executable allocator for Ion scripts with patchable backedges.
|
||||
UnprotectedData<ExecutableAllocator> backedgeExecAlloc_;
|
||||
ActiveThreadData<ExecutableAllocator> backedgeExecAlloc_;
|
||||
|
||||
// Shared exception-handler tail.
|
||||
ExclusiveAccessLockWriteOnceData<JitCode*> exceptionTail_;
|
||||
|
|
|
@ -4861,6 +4861,9 @@ LIRGenerator::visitBlock(MBasicBlock* block)
|
|||
uint32_t position = block->positionInPhiSuccessor();
|
||||
size_t lirIndex = 0;
|
||||
for (MPhiIterator phi(successor->phisBegin()); phi != successor->phisEnd(); phi++) {
|
||||
if (!gen->ensureBallast())
|
||||
return false;
|
||||
|
||||
MDefinition* opd = phi->getOperand(position);
|
||||
ensureDefined(opd);
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
#include "vm/ArrayObject.h"
|
||||
#include "vm/EnvironmentObject.h"
|
||||
#include "vm/SharedMem.h"
|
||||
#include "vm/TypedArrayCommon.h"
|
||||
#include "vm/TypedArrayObject.h"
|
||||
#include "vm/UnboxedObject.h"
|
||||
|
||||
// Undo windows.h damage on Win64
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
#include "jit/MIRGenerator.h"
|
||||
#include "jit/MIRGraph.h"
|
||||
#include "js/Conversions.h"
|
||||
#include "vm/TypedArrayCommon.h"
|
||||
#include "vm/TypedArrayObject.h"
|
||||
|
||||
#include "jsopcodeinlines.h"
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
#include "jit/SharedICList.h"
|
||||
#include "jit/SharedICRegisters.h"
|
||||
#include "vm/ReceiverGuard.h"
|
||||
#include "vm/TypedArrayCommon.h"
|
||||
#include "vm/TypedArrayObject.h"
|
||||
|
||||
namespace js {
|
||||
namespace jit {
|
||||
|
|
|
@ -670,9 +670,10 @@ void
|
|||
MacroAssembler::lshift64(Imm32 imm, Register64 dest)
|
||||
{
|
||||
MOZ_ASSERT(0 <= imm.value && imm.value < 64);
|
||||
if (imm.value == 0) {
|
||||
if (imm.value == 0)
|
||||
return;
|
||||
} else if (imm.value < 32) {
|
||||
|
||||
if (imm.value < 32) {
|
||||
as_mov(dest.high, lsl(dest.high, imm.value));
|
||||
as_orr(dest.high, dest.high, lsr(dest.low, 32 - imm.value));
|
||||
as_mov(dest.low, lsl(dest.low, imm.value));
|
||||
|
@ -716,6 +717,7 @@ void
|
|||
MacroAssembler::rshiftPtr(Imm32 imm, Register dest)
|
||||
{
|
||||
MOZ_ASSERT(0 <= imm.value && imm.value < 32);
|
||||
if (imm.value)
|
||||
ma_lsr(imm, dest, dest);
|
||||
}
|
||||
|
||||
|
@ -736,6 +738,7 @@ void
|
|||
MacroAssembler::rshiftPtrArithmetic(Imm32 imm, Register dest)
|
||||
{
|
||||
MOZ_ASSERT(0 <= imm.value && imm.value < 32);
|
||||
if (imm.value)
|
||||
ma_asr(imm, dest, dest);
|
||||
}
|
||||
|
||||
|
@ -743,6 +746,8 @@ void
|
|||
MacroAssembler::rshift64Arithmetic(Imm32 imm, Register64 dest)
|
||||
{
|
||||
MOZ_ASSERT(0 <= imm.value && imm.value < 64);
|
||||
if (!imm.value)
|
||||
return;
|
||||
|
||||
if (imm.value < 32) {
|
||||
as_mov(dest.low, lsr(dest.low, imm.value));
|
||||
|
@ -802,6 +807,9 @@ MacroAssembler::rshift64(Imm32 imm, Register64 dest)
|
|||
{
|
||||
MOZ_ASSERT(0 <= imm.value && imm.value < 64);
|
||||
MOZ_ASSERT(0 <= imm.value && imm.value < 64);
|
||||
if (!imm.value)
|
||||
return;
|
||||
|
||||
if (imm.value < 32) {
|
||||
as_mov(dest.low, lsr(dest.low, imm.value));
|
||||
as_orr(dest.low, dest.low, lsl(dest.high, 32 - imm.value));
|
||||
|
|
|
@ -87,7 +87,6 @@
|
|||
#include "vm/String.h"
|
||||
#include "vm/StringBuffer.h"
|
||||
#include "vm/Symbol.h"
|
||||
#include "vm/TypedArrayCommon.h"
|
||||
#include "vm/WrapperObject.h"
|
||||
#include "vm/Xdr.h"
|
||||
#include "wasm/AsmJS.h"
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
#include "vm/SelfHosting.h"
|
||||
#include "vm/Shape.h"
|
||||
#include "vm/StringBuffer.h"
|
||||
#include "vm/TypedArrayCommon.h"
|
||||
#include "vm/TypedArrayObject.h"
|
||||
#include "vm/WrapperObject.h"
|
||||
|
||||
#include "jsatominlines.h"
|
||||
|
|
|
@ -7494,6 +7494,7 @@ static bool
|
|||
ZoneGCHeapGrowthFactorGetter(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
AutoLockGC lock(cx->runtime());
|
||||
args.rval().setNumber(cx->zone()->threshold.gcHeapGrowthFactor());
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -651,7 +651,7 @@ class ArenaLists
|
|||
const BackgroundFinalizeState& backgroundFinalizeState(AllocKind i) const { return backgroundFinalizeState_.ref()[i]; }
|
||||
|
||||
/* For each arena kind, a list of arenas remaining to be swept. */
|
||||
UnprotectedData<AllAllocKindArray<Arena*>> arenaListsToSweep_;
|
||||
ActiveThreadOrGCTaskData<AllAllocKindArray<Arena*>> arenaListsToSweep_;
|
||||
Arena*& arenaListsToSweep(AllocKind i) { return arenaListsToSweep_.ref()[i]; }
|
||||
Arena* arenaListsToSweep(AllocKind i) const { return arenaListsToSweep_.ref()[i]; }
|
||||
|
||||
|
@ -877,7 +877,7 @@ class GCHelperState
|
|||
js::ConditionVariable done;
|
||||
|
||||
// Activity for the helper to do, protected by the GC lock.
|
||||
UnprotectedData<State> state_;
|
||||
ActiveThreadOrGCTaskData<State> state_;
|
||||
|
||||
// Whether work is being performed on some thread.
|
||||
GCLockData<bool> hasThread;
|
||||
|
@ -949,7 +949,7 @@ class GCParallelTask
|
|||
UnprotectedData<TaskState> state;
|
||||
|
||||
// Amount of time this task took to execute.
|
||||
UnprotectedData<mozilla::TimeDuration> duration_;
|
||||
ActiveThreadOrGCTaskData<mozilla::TimeDuration> duration_;
|
||||
|
||||
explicit GCParallelTask(const GCParallelTask&) = delete;
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
#include "vm/Interpreter.h"
|
||||
#include "vm/Shape.h"
|
||||
#include "vm/StopIterationObject.h"
|
||||
#include "vm/TypedArrayCommon.h"
|
||||
#include "vm/TypedArrayObject.h"
|
||||
|
||||
#include "jsscriptinlines.h"
|
||||
|
||||
|
|
|
@ -56,6 +56,40 @@
|
|||
# define HAVE_ARC4RANDOM
|
||||
#endif
|
||||
|
||||
#if defined(__linux__)
|
||||
# include <linux/random.h> // For GRND_NONBLOCK.
|
||||
# include <sys/syscall.h> // For SYS_getrandom.
|
||||
|
||||
// Older glibc versions don't define SYS_getrandom, so we define it here if
|
||||
// it's not available. See bug 995069.
|
||||
# if defined(__x86_64__)
|
||||
# define GETRANDOM_NR 318
|
||||
# elif defined(__i386__)
|
||||
# define GETRANDOM_NR 355
|
||||
# elif defined(__arm__)
|
||||
# define GETRANDOM_NR 384
|
||||
# endif
|
||||
|
||||
# if defined(SYS_getrandom)
|
||||
// We have SYS_getrandom. Use it to check GETRANDOM_NR. Only do this if we set
|
||||
// GETRANDOM_NR so tier 3 platforms with recent glibc are not forced to define
|
||||
// it for no good reason.
|
||||
# if defined(GETRANDOM_NR)
|
||||
static_assert(GETRANDOM_NR == SYS_getrandom,
|
||||
"GETRANDOM_NR should match the actual SYS_getrandom value");
|
||||
# endif
|
||||
# else
|
||||
# define SYS_getrandom GETRANDOM_NR
|
||||
# endif
|
||||
|
||||
# if defined(GRND_NONBLOCK)
|
||||
static_assert(GRND_NONBLOCK == 1, "If GRND_NONBLOCK is not 1 the #define below is wrong");
|
||||
# else
|
||||
# define GRND_NONBLOCK 1
|
||||
# endif
|
||||
|
||||
#endif // defined(__linux__)
|
||||
|
||||
using namespace js;
|
||||
|
||||
using mozilla::Abs;
|
||||
|
@ -686,17 +720,28 @@ js::GenerateRandomSeed()
|
|||
#elif defined(HAVE_ARC4RANDOM)
|
||||
seed = (static_cast<uint64_t>(arc4random()) << 32) | arc4random();
|
||||
#elif defined(XP_UNIX)
|
||||
bool done = false;
|
||||
# if defined(__linux__)
|
||||
// Try the relatively new getrandom syscall first. It's the preferred way
|
||||
// on Linux as /dev/urandom may not work inside chroots and is harder to
|
||||
// sandbox (see bug 995069).
|
||||
int ret = syscall(SYS_getrandom, &seed, sizeof(seed), GRND_NONBLOCK);
|
||||
done = (ret == sizeof(seed));
|
||||
# endif
|
||||
if (!done) {
|
||||
int fd = open("/dev/urandom", O_RDONLY);
|
||||
if (fd >= 0) {
|
||||
mozilla::Unused << read(fd, static_cast<void*>(&seed), sizeof(seed));
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
#else
|
||||
# error "Platform needs to implement GenerateRandomSeed()"
|
||||
#endif
|
||||
|
||||
// Also mix in PRMJ_Now() in case we couldn't read random bits from the OS.
|
||||
return seed ^ PRMJ_Now();
|
||||
uint64_t timestamp = PRMJ_Now();
|
||||
return seed ^ timestamp ^ (timestamp << 32);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -53,7 +53,7 @@
|
|||
#include "vm/ProxyObject.h"
|
||||
#include "vm/RegExpStaticsObject.h"
|
||||
#include "vm/Shape.h"
|
||||
#include "vm/TypedArrayCommon.h"
|
||||
#include "vm/TypedArrayObject.h"
|
||||
|
||||
#include "jsatominlines.h"
|
||||
#include "jsboolinlines.h"
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
#include "vm/NumberObject.h"
|
||||
#include "vm/Probes.h"
|
||||
#include "vm/StringObject.h"
|
||||
#include "vm/TypedArrayCommon.h"
|
||||
#include "vm/TypedArrayObject.h"
|
||||
|
||||
#include "jsatominlines.h"
|
||||
#include "jscompartmentinlines.h"
|
||||
|
|
|
@ -34,6 +34,27 @@ OnBackgroundThread()
|
|||
return false;
|
||||
}
|
||||
|
||||
template <AllowedBackgroundThread Background>
|
||||
void
|
||||
CheckActiveThread<Background>::check() const
|
||||
{
|
||||
// When interrupting a thread on Windows, changes are made to the runtime
|
||||
// and active thread's state from another thread while the active thread is
|
||||
// suspended. We need a way to mark these accesses as being tantamount to
|
||||
// accesses by the active thread. See bug 1323066.
|
||||
#ifndef XP_WIN
|
||||
if (OnBackgroundThread<Background>())
|
||||
return;
|
||||
|
||||
JSContext* cx = TlsContext.get();
|
||||
MOZ_ASSERT(cx == cx->runtime()->activeContext);
|
||||
#endif // XP_WIN
|
||||
}
|
||||
|
||||
template class CheckActiveThread<AllowedBackgroundThread::None>;
|
||||
template class CheckActiveThread<AllowedBackgroundThread::GCTask>;
|
||||
template class CheckActiveThread<AllowedBackgroundThread::IonCompile>;
|
||||
|
||||
template <AllowedBackgroundThread Background>
|
||||
void
|
||||
CheckZoneGroup<Background>::check() const
|
||||
|
|
|
@ -201,6 +201,28 @@ enum class AllowedBackgroundThread
|
|||
GCTaskOrIonCompile
|
||||
};
|
||||
|
||||
template <AllowedBackgroundThread Background>
|
||||
class CheckActiveThread
|
||||
{
|
||||
public:
|
||||
void check() const;
|
||||
};
|
||||
|
||||
// Data which may only be accessed by the runtime's cooperatively scheduled
|
||||
// active thread.
|
||||
template <typename T>
|
||||
using ActiveThreadData =
|
||||
ProtectedDataNoCheckArgs<CheckActiveThread<AllowedBackgroundThread::None>, T>;
|
||||
|
||||
// Data which may only be accessed by the runtime's cooperatively scheduled
|
||||
// active thread, or by various helper thread tasks.
|
||||
template <typename T>
|
||||
using ActiveThreadOrGCTaskData =
|
||||
ProtectedDataNoCheckArgs<CheckActiveThread<AllowedBackgroundThread::GCTask>, T>;
|
||||
template <typename T>
|
||||
using ActiveThreadOrIonCompileData =
|
||||
ProtectedDataNoCheckArgs<CheckActiveThread<AllowedBackgroundThread::IonCompile>, T>;
|
||||
|
||||
template <AllowedBackgroundThread Background>
|
||||
class CheckZoneGroup
|
||||
{
|
||||
|
@ -223,19 +245,13 @@ using ZoneGroupData =
|
|||
ProtectedDataZoneGroupArg<CheckZoneGroup<AllowedBackgroundThread::None>, T>;
|
||||
|
||||
// Data which may only be accessed by threads with exclusive access to the
|
||||
// associated zone group, or by GC helper thread tasks.
|
||||
// associated zone group, or by various helper thread tasks.
|
||||
template <typename T>
|
||||
using ZoneGroupOrGCTaskData =
|
||||
ProtectedDataZoneGroupArg<CheckZoneGroup<AllowedBackgroundThread::GCTask>, T>;
|
||||
|
||||
// Data which may only be accessed by threads with exclusive access to the
|
||||
// associated zone group, or by Ion compilation helper thread tasks.
|
||||
template <typename T>
|
||||
using ZoneGroupOrIonCompileData =
|
||||
ProtectedDataZoneGroupArg<CheckZoneGroup<AllowedBackgroundThread::IonCompile>, T>;
|
||||
|
||||
// Data which may only be accessed by threads with exclusive access to the
|
||||
// associated zone group, or by either GC helper or Ion compilation tasks.
|
||||
template <typename T>
|
||||
using ZoneGroupOrGCTaskOrIonCompileData =
|
||||
ProtectedDataZoneGroupArg<CheckZoneGroup<AllowedBackgroundThread::GCTaskOrIonCompile>, T>;
|
||||
|
|
|
@ -1183,10 +1183,10 @@ void
|
|||
js::GCParallelTask::runFromHelperThread(AutoLockHelperThreadState& locked)
|
||||
{
|
||||
JSContext cx(runtime(), JS::ContextOptions());
|
||||
gc::AutoSetThreadIsPerformingGC performingGC;
|
||||
|
||||
{
|
||||
AutoUnlockHelperThreadState parallelSection(locked);
|
||||
gc::AutoSetThreadIsPerformingGC performingGC;
|
||||
mozilla::TimeStamp timeStart = mozilla::TimeStamp::Now();
|
||||
cx.heapState = JS::HeapState::MajorCollecting;
|
||||
run();
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
#include "gc/Marking.h"
|
||||
#include "js/Value.h"
|
||||
#include "vm/Debugger.h"
|
||||
#include "vm/TypedArrayCommon.h"
|
||||
#include "vm/TypedArrayObject.h"
|
||||
|
||||
#include "jsobjinlines.h"
|
||||
|
||||
|
|
|
@ -95,6 +95,7 @@ JSRuntime::JSRuntime(JSRuntime* parentRuntime)
|
|||
#ifdef DEBUG
|
||||
updateChildRuntimeCount(parentRuntime),
|
||||
#endif
|
||||
activeContext(nullptr),
|
||||
profilerSampleBufferGen_(0),
|
||||
profilerSampleBufferLapCount_(1),
|
||||
telemetryCallback(nullptr),
|
||||
|
@ -191,6 +192,8 @@ JSRuntime::init(JSContext* cx, uint32_t maxbytes, uint32_t maxNurseryBytes)
|
|||
if (CanUseExtraThreads() && !EnsureHelperThreadsInitialized())
|
||||
return false;
|
||||
|
||||
activeContext = cx;
|
||||
|
||||
singletonContext = cx;
|
||||
|
||||
defaultFreeOp_ = js_new<js::FreeOp>(this);
|
||||
|
|
|
@ -297,6 +297,11 @@ struct JSRuntime : public js::MallocProvider<JSRuntime>
|
|||
AutoUpdateChildRuntimeCount updateChildRuntimeCount;
|
||||
#endif
|
||||
|
||||
// The context for the thread which currently has exclusive access to most
|
||||
// contents of the runtime. When execution on the runtime is cooperatively
|
||||
// scheduled, this is the thread which is currently running.
|
||||
mozilla::Atomic<JSContext*, mozilla::ReleaseAcquire> activeContext;
|
||||
|
||||
/*
|
||||
* The profiler sampler generation after the latest sample.
|
||||
*
|
||||
|
@ -354,7 +359,7 @@ struct JSRuntime : public js::MallocProvider<JSRuntime>
|
|||
}
|
||||
|
||||
/* Call this to accumulate telemetry data. */
|
||||
js::UnprotectedData<JSAccumulateTelemetryDataCallback> telemetryCallback;
|
||||
js::ActiveThreadData<JSAccumulateTelemetryDataCallback> telemetryCallback;
|
||||
public:
|
||||
// Accumulates data for Firefox telemetry. |id| is the ID of a JS_TELEMETRY_*
|
||||
// histogram. |key| provides an additional key to identify the histogram.
|
||||
|
@ -364,14 +369,14 @@ struct JSRuntime : public js::MallocProvider<JSRuntime>
|
|||
void setTelemetryCallback(JSRuntime* rt, JSAccumulateTelemetryDataCallback callback);
|
||||
|
||||
public:
|
||||
js::UnprotectedData<JSGetIncumbentGlobalCallback> getIncumbentGlobalCallback;
|
||||
js::UnprotectedData<JSEnqueuePromiseJobCallback> enqueuePromiseJobCallback;
|
||||
js::UnprotectedData<void*> enqueuePromiseJobCallbackData;
|
||||
js::ActiveThreadData<JSGetIncumbentGlobalCallback> getIncumbentGlobalCallback;
|
||||
js::ActiveThreadData<JSEnqueuePromiseJobCallback> enqueuePromiseJobCallback;
|
||||
js::ActiveThreadData<void*> enqueuePromiseJobCallbackData;
|
||||
|
||||
js::UnprotectedData<JSPromiseRejectionTrackerCallback> promiseRejectionTrackerCallback;
|
||||
js::UnprotectedData<void*> promiseRejectionTrackerCallbackData;
|
||||
js::ActiveThreadData<JSPromiseRejectionTrackerCallback> promiseRejectionTrackerCallback;
|
||||
js::ActiveThreadData<void*> promiseRejectionTrackerCallbackData;
|
||||
|
||||
js::UnprotectedData<JS::StartAsyncTaskCallback> startAsyncTaskCallback;
|
||||
js::ActiveThreadData<JS::StartAsyncTaskCallback> startAsyncTaskCallback;
|
||||
js::UnprotectedData<JS::FinishAsyncTaskCallback> finishAsyncTaskCallback;
|
||||
js::ExclusiveData<js::PromiseTaskPtrVector> promiseTasksToDestroy;
|
||||
|
||||
|
@ -388,35 +393,35 @@ struct JSRuntime : public js::MallocProvider<JSRuntime>
|
|||
* Allow relazifying functions in compartments that are active. This is
|
||||
* only used by the relazifyFunctions() testing function.
|
||||
*/
|
||||
js::UnprotectedData<bool> allowRelazificationForTesting;
|
||||
js::ActiveThreadData<bool> allowRelazificationForTesting;
|
||||
|
||||
/* Compartment destroy callback. */
|
||||
js::UnprotectedData<JSDestroyCompartmentCallback> destroyCompartmentCallback;
|
||||
js::ActiveThreadData<JSDestroyCompartmentCallback> destroyCompartmentCallback;
|
||||
|
||||
/* Compartment memory reporting callback. */
|
||||
js::UnprotectedData<JSSizeOfIncludingThisCompartmentCallback> sizeOfIncludingThisCompartmentCallback;
|
||||
js::ActiveThreadData<JSSizeOfIncludingThisCompartmentCallback> sizeOfIncludingThisCompartmentCallback;
|
||||
|
||||
/* Zone destroy callback. */
|
||||
js::UnprotectedData<JSZoneCallback> destroyZoneCallback;
|
||||
js::ActiveThreadData<JSZoneCallback> destroyZoneCallback;
|
||||
|
||||
/* Zone sweep callback. */
|
||||
js::UnprotectedData<JSZoneCallback> sweepZoneCallback;
|
||||
js::ActiveThreadData<JSZoneCallback> sweepZoneCallback;
|
||||
|
||||
/* Call this to get the name of a compartment. */
|
||||
js::UnprotectedData<JSCompartmentNameCallback> compartmentNameCallback;
|
||||
js::ActiveThreadData<JSCompartmentNameCallback> compartmentNameCallback;
|
||||
|
||||
/* Callback for doing memory reporting on external strings. */
|
||||
js::UnprotectedData<JSExternalStringSizeofCallback> externalStringSizeofCallback;
|
||||
js::ActiveThreadData<JSExternalStringSizeofCallback> externalStringSizeofCallback;
|
||||
|
||||
js::UnprotectedData<mozilla::UniquePtr<js::SourceHook>> sourceHook;
|
||||
js::ActiveThreadData<mozilla::UniquePtr<js::SourceHook>> sourceHook;
|
||||
|
||||
js::UnprotectedData<const JSSecurityCallbacks*> securityCallbacks;
|
||||
js::UnprotectedData<const js::DOMCallbacks*> DOMcallbacks;
|
||||
js::UnprotectedData<JSDestroyPrincipalsOp> destroyPrincipals;
|
||||
js::UnprotectedData<JSReadPrincipalsOp> readPrincipals;
|
||||
js::ActiveThreadData<const JSSecurityCallbacks*> securityCallbacks;
|
||||
js::ActiveThreadData<const js::DOMCallbacks*> DOMcallbacks;
|
||||
js::ActiveThreadData<JSDestroyPrincipalsOp> destroyPrincipals;
|
||||
js::ActiveThreadData<JSReadPrincipalsOp> readPrincipals;
|
||||
|
||||
/* Optional warning reporter. */
|
||||
js::UnprotectedData<JS::WarningReporter> warningReporter;
|
||||
js::ActiveThreadData<JS::WarningReporter> warningReporter;
|
||||
|
||||
private:
|
||||
/* Gecko profiling metadata */
|
||||
|
@ -425,7 +430,7 @@ struct JSRuntime : public js::MallocProvider<JSRuntime>
|
|||
js::GeckoProfiler& geckoProfiler() { return geckoProfiler_.ref(); }
|
||||
|
||||
// Heap GC roots for PersistentRooted pointers.
|
||||
js::UnprotectedData<mozilla::EnumeratedArray<JS::RootKind, JS::RootKind::Limit,
|
||||
js::ActiveThreadData<mozilla::EnumeratedArray<JS::RootKind, JS::RootKind::Limit,
|
||||
mozilla::LinkedList<JS::PersistentRooted<void*>>>> heapRoots;
|
||||
|
||||
void tracePersistentRoots(JSTracer* trc);
|
||||
|
@ -445,15 +450,15 @@ struct JSRuntime : public js::MallocProvider<JSRuntime>
|
|||
void setTrustedPrincipals(const JSPrincipals* p) { trustedPrincipals_ = p; }
|
||||
const JSPrincipals* trustedPrincipals() const { return trustedPrincipals_; }
|
||||
|
||||
js::UnprotectedData<const JSWrapObjectCallbacks*> wrapObjectCallbacks;
|
||||
js::UnprotectedData<js::PreserveWrapperCallback> preserveWrapperCallback;
|
||||
js::ActiveThreadData<const JSWrapObjectCallbacks*> wrapObjectCallbacks;
|
||||
js::ActiveThreadData<js::PreserveWrapperCallback> preserveWrapperCallback;
|
||||
|
||||
js::UnprotectedData<js::ScriptEnvironmentPreparer*> scriptEnvironmentPreparer;
|
||||
js::ActiveThreadData<js::ScriptEnvironmentPreparer*> scriptEnvironmentPreparer;
|
||||
|
||||
js::UnprotectedData<js::CTypesActivityCallback> ctypesActivityCallback;
|
||||
js::ActiveThreadData<js::CTypesActivityCallback> ctypesActivityCallback;
|
||||
|
||||
private:
|
||||
js::UnprotectedData<const js::Class*> windowProxyClass_;
|
||||
js::WriteOnceData<const js::Class*> windowProxyClass_;
|
||||
|
||||
public:
|
||||
const js::Class* maybeWindowProxyClass() const {
|
||||
|
@ -468,7 +473,7 @@ struct JSRuntime : public js::MallocProvider<JSRuntime>
|
|||
* Head of circular list of all enabled Debuggers that have
|
||||
* onNewGlobalObject handler methods established.
|
||||
*/
|
||||
js::UnprotectedData<JSCList> onNewGlobalObjectWatchers_;
|
||||
js::ActiveThreadData<JSCList> onNewGlobalObjectWatchers_;
|
||||
public:
|
||||
JSCList& onNewGlobalObjectWatchers() { return onNewGlobalObjectWatchers_.ref(); }
|
||||
|
||||
|
@ -508,16 +513,16 @@ struct JSRuntime : public js::MallocProvider<JSRuntime>
|
|||
// How many compartments there are across all zones. This number includes
|
||||
// off main thread context compartments, so it isn't necessarily equal to the
|
||||
// number of compartments visited by CompartmentsIter.
|
||||
js::UnprotectedData<size_t> numCompartments;
|
||||
js::ActiveThreadData<size_t> numCompartments;
|
||||
|
||||
/* Locale-specific callbacks for string conversion. */
|
||||
js::UnprotectedData<const JSLocaleCallbacks*> localeCallbacks;
|
||||
js::ActiveThreadData<const JSLocaleCallbacks*> localeCallbacks;
|
||||
|
||||
/* Default locale for Internationalization API */
|
||||
js::UnprotectedData<char*> defaultLocale;
|
||||
js::ActiveThreadData<char*> defaultLocale;
|
||||
|
||||
/* Default JSVersion. */
|
||||
js::UnprotectedData<JSVersion> defaultVersion_;
|
||||
js::ActiveThreadData<JSVersion> defaultVersion_;
|
||||
|
||||
private:
|
||||
/* Code coverage output. */
|
||||
|
@ -780,7 +785,7 @@ struct JSRuntime : public js::MallocProvider<JSRuntime>
|
|||
js::WriteOnceData<js::WellKnownSymbols*> wellKnownSymbols;
|
||||
|
||||
/* Shared Intl data for this runtime. */
|
||||
js::UnprotectedData<js::SharedIntlData> sharedIntlData;
|
||||
js::ActiveThreadData<js::SharedIntlData> sharedIntlData;
|
||||
|
||||
void traceSharedIntlData(JSTracer* trc);
|
||||
|
||||
|
@ -851,7 +856,7 @@ struct JSRuntime : public js::MallocProvider<JSRuntime>
|
|||
mozilla::Atomic<bool> offthreadIonCompilationEnabled_;
|
||||
mozilla::Atomic<bool> parallelParsingEnabled_;
|
||||
|
||||
js::UnprotectedData<bool> autoWritableJitCodeActive_;
|
||||
js::ActiveThreadData<bool> autoWritableJitCodeActive_;
|
||||
|
||||
public:
|
||||
|
||||
|
@ -876,12 +881,12 @@ struct JSRuntime : public js::MallocProvider<JSRuntime>
|
|||
}
|
||||
|
||||
/* See comment for JS::SetLargeAllocationFailureCallback in jsapi.h. */
|
||||
js::UnprotectedData<JS::LargeAllocationFailureCallback> largeAllocationFailureCallback;
|
||||
js::UnprotectedData<void*> largeAllocationFailureCallbackData;
|
||||
js::ActiveThreadData<JS::LargeAllocationFailureCallback> largeAllocationFailureCallback;
|
||||
js::ActiveThreadData<void*> largeAllocationFailureCallbackData;
|
||||
|
||||
/* See comment for JS::SetOutOfMemoryCallback in jsapi.h. */
|
||||
js::UnprotectedData<JS::OutOfMemoryCallback> oomCallback;
|
||||
js::UnprotectedData<void*> oomCallbackData;
|
||||
js::ActiveThreadData<JS::OutOfMemoryCallback> oomCallback;
|
||||
js::ActiveThreadData<void*> oomCallbackData;
|
||||
|
||||
/*
|
||||
* These variations of malloc/calloc/realloc will call the
|
||||
|
@ -919,13 +924,13 @@ struct JSRuntime : public js::MallocProvider<JSRuntime>
|
|||
* Debugger.Memory functions like takeCensus use this embedding-provided
|
||||
* function to assess the size of malloc'd blocks of memory.
|
||||
*/
|
||||
js::UnprotectedData<mozilla::MallocSizeOf> debuggerMallocSizeOf;
|
||||
js::ActiveThreadData<mozilla::MallocSizeOf> debuggerMallocSizeOf;
|
||||
|
||||
/* Last time at which an animation was played for this runtime. */
|
||||
mozilla::Atomic<int64_t> lastAnimationTime;
|
||||
|
||||
private:
|
||||
js::UnprotectedData<js::PerformanceMonitoring> performanceMonitoring_;
|
||||
js::ActiveThreadData<js::PerformanceMonitoring> performanceMonitoring_;
|
||||
public:
|
||||
js::PerformanceMonitoring& performanceMonitoring() { return performanceMonitoring_.ref(); }
|
||||
|
||||
|
|
|
@ -45,7 +45,7 @@
|
|||
#include "vm/RegExpObject.h"
|
||||
#include "vm/String.h"
|
||||
#include "vm/StringBuffer.h"
|
||||
#include "vm/TypedArrayCommon.h"
|
||||
#include "vm/TypedArrayObject.h"
|
||||
#include "vm/WrapperObject.h"
|
||||
|
||||
#include "jsatominlines.h"
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
|
||||
#include "gc/Allocator.h"
|
||||
#include "vm/Interpreter.h"
|
||||
#include "vm/TypedArrayCommon.h"
|
||||
#include "vm/TypedArrayObject.h"
|
||||
|
||||
#include "jsatominlines.h"
|
||||
#include "jscntxtinlines.h"
|
||||
|
|
|
@ -22,8 +22,8 @@
|
|||
# include <valgrind/memcheck.h>
|
||||
#endif
|
||||
|
||||
#include "jit/AtomicOperations.h"
|
||||
#include "vm/SharedMem.h"
|
||||
#include "vm/TypedArrayCommon.h"
|
||||
#include "wasm/AsmJS.h"
|
||||
#include "wasm/WasmTypes.h"
|
||||
|
||||
|
|
|
@ -4,11 +4,13 @@
|
|||
* 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/. */
|
||||
|
||||
#ifndef vm_TypedArrayCommon_h
|
||||
#define vm_TypedArrayCommon_h
|
||||
#ifndef vm_TypedArrayObject_inl_h
|
||||
#define vm_TypedArrayObject_inl_h
|
||||
|
||||
/* Utilities and common inline code for TypedArray */
|
||||
|
||||
#include "vm/TypedArrayObject.h"
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/FloatingPoint.h"
|
||||
#include "mozilla/PodOperations.h"
|
||||
|
@ -23,7 +25,6 @@
|
|||
#include "js/Value.h"
|
||||
|
||||
#include "vm/NativeObject.h"
|
||||
#include "vm/TypedArrayObject.h"
|
||||
|
||||
namespace js {
|
||||
|
||||
|
@ -258,12 +259,9 @@ class UnsharedOps
|
|||
}
|
||||
};
|
||||
|
||||
template<class SpecificArray, typename Ops>
|
||||
template<typename T, typename Ops>
|
||||
class ElementSpecific
|
||||
{
|
||||
typedef typename SpecificArray::ElementType T;
|
||||
typedef typename SpecificArray::SomeTypedArray SomeTypedArray;
|
||||
|
||||
public:
|
||||
/*
|
||||
* Copy |source|'s elements into |target|, starting at |target[offset]|.
|
||||
|
@ -272,28 +270,23 @@ class ElementSpecific
|
|||
*/
|
||||
static bool
|
||||
setFromTypedArray(JSContext* cx,
|
||||
Handle<SomeTypedArray*> target, HandleObject source,
|
||||
Handle<TypedArrayObject*> target, Handle<TypedArrayObject*> source,
|
||||
uint32_t offset)
|
||||
{
|
||||
MOZ_ASSERT(SpecificArray::ArrayTypeID() == target->type(),
|
||||
MOZ_ASSERT(TypeIDOfType<T>::id == target->type(),
|
||||
"calling wrong setFromTypedArray specialization");
|
||||
|
||||
MOZ_ASSERT(offset <= target->length());
|
||||
MOZ_ASSERT(source->as<TypedArrayObject>().length() <= target->length() - offset);
|
||||
MOZ_ASSERT(source->length() <= target->length() - offset);
|
||||
|
||||
if (source->is<SomeTypedArray>()) {
|
||||
Rooted<SomeTypedArray*> src(cx, source.as<SomeTypedArray>());
|
||||
if (SomeTypedArray::sameBuffer(target, src))
|
||||
return setFromOverlappingTypedArray(cx, target, src, offset);
|
||||
}
|
||||
if (TypedArrayObject::sameBuffer(target, source))
|
||||
return setFromOverlappingTypedArray(cx, target, source, offset);
|
||||
|
||||
SharedMem<T*> dest =
|
||||
target->template as<TypedArrayObject>().viewDataEither().template cast<T*>() + offset;
|
||||
uint32_t count = source->as<TypedArrayObject>().length();
|
||||
SharedMem<T*> dest = target->viewDataEither().template cast<T*>() + offset;
|
||||
uint32_t count = source->length();
|
||||
|
||||
if (source->as<TypedArrayObject>().type() == target->type()) {
|
||||
Ops::podCopy(dest, source->as<TypedArrayObject>().viewDataEither().template cast<T*>(),
|
||||
count);
|
||||
if (source->type() == target->type()) {
|
||||
Ops::podCopy(dest, source->viewDataEither().template cast<T*>(), count);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -304,8 +297,8 @@ class ElementSpecific
|
|||
# define JS_VOLATILE_ARM
|
||||
#endif
|
||||
|
||||
SharedMem<void*> data = Ops::extract(source.as<TypedArrayObject>());
|
||||
switch (source->as<TypedArrayObject>().type()) {
|
||||
SharedMem<void*> data = Ops::extract(source);
|
||||
switch (source->type()) {
|
||||
case Scalar::Int8: {
|
||||
SharedMem<JS_VOLATILE_ARM int8_t*> src = data.cast<JS_VOLATILE_ARM int8_t*>();
|
||||
for (uint32_t i = 0; i < count; ++i)
|
||||
|
@ -370,10 +363,10 @@ class ElementSpecific
|
|||
* typed array.
|
||||
*/
|
||||
static bool
|
||||
setFromNonTypedArray(JSContext* cx, Handle<SomeTypedArray*> target, HandleObject source,
|
||||
setFromNonTypedArray(JSContext* cx, Handle<TypedArrayObject*> target, HandleObject source,
|
||||
uint32_t len, uint32_t offset = 0)
|
||||
{
|
||||
MOZ_ASSERT(target->type() == SpecificArray::ArrayTypeID(),
|
||||
MOZ_ASSERT(target->type() == TypeIDOfType<T>::id,
|
||||
"target type and NativeType must match");
|
||||
MOZ_ASSERT(!source->is<TypedArrayObject>(),
|
||||
"use setFromTypedArray instead of this method");
|
||||
|
@ -384,8 +377,7 @@ class ElementSpecific
|
|||
// the first potentially side-effectful lookup or conversion.
|
||||
uint32_t bound = Min(source->as<NativeObject>().getDenseInitializedLength(), len);
|
||||
|
||||
SharedMem<T*> dest =
|
||||
target->template as<TypedArrayObject>().viewDataEither().template cast<T*>() + offset;
|
||||
SharedMem<T*> dest = target->viewDataEither().template cast<T*>() + offset;
|
||||
|
||||
MOZ_ASSERT(!canConvertInfallibly(MagicValue(JS_ELEMENTS_HOLE)),
|
||||
"the following loop must abort on holes");
|
||||
|
@ -415,9 +407,7 @@ class ElementSpecific
|
|||
break;
|
||||
|
||||
// Compute every iteration in case getElement/valueToNative is wacky.
|
||||
SharedMem<T*> dest =
|
||||
target->template as<TypedArrayObject>().viewDataEither().template cast<T*>() +
|
||||
offset + i;
|
||||
SharedMem<T*> dest = target->viewDataEither().template cast<T*>() + offset + i;
|
||||
Ops::store(dest, n);
|
||||
}
|
||||
|
||||
|
@ -428,10 +418,10 @@ class ElementSpecific
|
|||
* Copy |source| into the typed array |target|.
|
||||
*/
|
||||
static bool
|
||||
initFromIterablePackedArray(JSContext* cx, Handle<SomeTypedArray*> target,
|
||||
initFromIterablePackedArray(JSContext* cx, Handle<TypedArrayObject*> target,
|
||||
HandleArrayObject source)
|
||||
{
|
||||
MOZ_ASSERT(target->type() == SpecificArray::ArrayTypeID(),
|
||||
MOZ_ASSERT(target->type() == TypeIDOfType<T>::id,
|
||||
"target type and NativeType must match");
|
||||
MOZ_ASSERT(IsPackedArray(source), "source array must be packed");
|
||||
MOZ_ASSERT(source->getDenseInitializedLength() <= target->length());
|
||||
|
@ -442,8 +432,7 @@ class ElementSpecific
|
|||
// Attempt fast-path infallible conversion of dense elements up to the
|
||||
// first potentially side-effectful conversion.
|
||||
|
||||
SharedMem<T*> dest =
|
||||
target->template as<TypedArrayObject>().viewDataEither().template cast<T*>();
|
||||
SharedMem<T*> dest = target->viewDataEither().template cast<T*>();
|
||||
|
||||
const Value* srcValues = source->getDenseElements();
|
||||
for (; i < len; i++) {
|
||||
|
@ -474,8 +463,7 @@ class ElementSpecific
|
|||
MOZ_ASSERT(i < target->length());
|
||||
|
||||
// Compute every iteration in case GC moves the data.
|
||||
SharedMem<T*> newDest =
|
||||
target->template as<TypedArrayObject>().viewDataEither().template cast<T*>();
|
||||
SharedMem<T*> newDest = target->viewDataEither().template cast<T*>();
|
||||
Ops::store(newDest + i, n);
|
||||
}
|
||||
|
||||
|
@ -485,26 +473,24 @@ class ElementSpecific
|
|||
private:
|
||||
static bool
|
||||
setFromOverlappingTypedArray(JSContext* cx,
|
||||
Handle<SomeTypedArray*> target,
|
||||
Handle<SomeTypedArray*> source,
|
||||
Handle<TypedArrayObject*> target,
|
||||
Handle<TypedArrayObject*> source,
|
||||
uint32_t offset)
|
||||
{
|
||||
MOZ_ASSERT(SpecificArray::ArrayTypeID() == target->type(),
|
||||
MOZ_ASSERT(TypeIDOfType<T>::id == target->type(),
|
||||
"calling wrong setFromTypedArray specialization");
|
||||
MOZ_ASSERT(SomeTypedArray::sameBuffer(target, source),
|
||||
MOZ_ASSERT(TypedArrayObject::sameBuffer(target, source),
|
||||
"the provided arrays don't actually overlap, so it's "
|
||||
"undesirable to use this method");
|
||||
|
||||
MOZ_ASSERT(offset <= target->length());
|
||||
MOZ_ASSERT(source->length() <= target->length() - offset);
|
||||
|
||||
SharedMem<T*> dest =
|
||||
target->template as<TypedArrayObject>().viewDataEither().template cast<T*>() + offset;
|
||||
SharedMem<T*> dest = target->viewDataEither().template cast<T*>() + offset;
|
||||
uint32_t len = source->length();
|
||||
|
||||
if (source->type() == target->type()) {
|
||||
SharedMem<T*> src =
|
||||
source->template as<TypedArrayObject>().viewDataEither().template cast<T*>();
|
||||
SharedMem<T*> src = source->viewDataEither().template cast<T*>();
|
||||
Ops::podMove(dest, src, len);
|
||||
return true;
|
||||
}
|
||||
|
@ -515,7 +501,7 @@ class ElementSpecific
|
|||
if (!data)
|
||||
return false;
|
||||
Ops::memcpy(SharedMem<void*>::unshared(data),
|
||||
source->template as<TypedArrayObject>().viewDataEither(),
|
||||
source->viewDataEither(),
|
||||
sourceByteLen);
|
||||
|
||||
switch (source->type()) {
|
||||
|
@ -633,7 +619,7 @@ class ElementSpecific
|
|||
}
|
||||
if (MOZ_UNLIKELY(mozilla::IsNaN(d)))
|
||||
return T(0);
|
||||
if (SpecificArray::ArrayTypeID() == Scalar::Uint8Clamped)
|
||||
if (TypeIDOfType<T>::id == Scalar::Uint8Clamped)
|
||||
return T(d);
|
||||
if (TypeIsUnsigned<T>())
|
||||
return T(JS::ToUint32(d));
|
||||
|
@ -641,247 +627,6 @@ class ElementSpecific
|
|||
}
|
||||
};
|
||||
|
||||
template<typename SomeTypedArray>
|
||||
class TypedArrayMethods
|
||||
{
|
||||
static_assert(mozilla::IsSame<SomeTypedArray, TypedArrayObject>::value,
|
||||
"methods must be shared/unshared-specific, not "
|
||||
"element-type-specific");
|
||||
|
||||
typedef typename SomeTypedArray::BufferType BufferType;
|
||||
|
||||
typedef typename SomeTypedArray::template OfType<int8_t>::Type Int8ArrayType;
|
||||
typedef typename SomeTypedArray::template OfType<uint8_t>::Type Uint8ArrayType;
|
||||
typedef typename SomeTypedArray::template OfType<int16_t>::Type Int16ArrayType;
|
||||
typedef typename SomeTypedArray::template OfType<uint16_t>::Type Uint16ArrayType;
|
||||
typedef typename SomeTypedArray::template OfType<int32_t>::Type Int32ArrayType;
|
||||
typedef typename SomeTypedArray::template OfType<uint32_t>::Type Uint32ArrayType;
|
||||
typedef typename SomeTypedArray::template OfType<float>::Type Float32ArrayType;
|
||||
typedef typename SomeTypedArray::template OfType<double>::Type Float64ArrayType;
|
||||
typedef typename SomeTypedArray::template OfType<uint8_clamped>::Type Uint8ClampedArrayType;
|
||||
|
||||
public:
|
||||
/* set(array[, offset]) */
|
||||
static bool
|
||||
set(JSContext* cx, const CallArgs& args)
|
||||
{
|
||||
MOZ_ASSERT(SomeTypedArray::is(args.thisv()));
|
||||
|
||||
Rooted<SomeTypedArray*> target(cx, &args.thisv().toObject().as<SomeTypedArray>());
|
||||
|
||||
// The first argument must be either a typed array or arraylike.
|
||||
if (args.length() == 0 || !args[0].isObject()) {
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
|
||||
return false;
|
||||
}
|
||||
|
||||
int32_t offset = 0;
|
||||
if (args.length() > 1) {
|
||||
if (!ToInt32(cx, args[1], &offset))
|
||||
return false;
|
||||
|
||||
if (offset < 0 || uint32_t(offset) > target->length()) {
|
||||
// the given offset is bogus
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_INDEX);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
RootedObject arg0(cx, &args[0].toObject());
|
||||
if (arg0->is<TypedArrayObject>()) {
|
||||
if (arg0->as<TypedArrayObject>().length() > target->length() - offset) {
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!setFromTypedArray(cx, target, arg0, offset))
|
||||
return false;
|
||||
} else {
|
||||
uint32_t len;
|
||||
if (!GetLengthProperty(cx, arg0, &len))
|
||||
return false;
|
||||
|
||||
if (uint32_t(offset) > target->length() || len > target->length() - offset) {
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!setFromNonTypedArray(cx, target, arg0, len, offset))
|
||||
return false;
|
||||
}
|
||||
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
setFromTypedArray(JSContext* cx, Handle<SomeTypedArray*> target, HandleObject source,
|
||||
uint32_t offset = 0)
|
||||
{
|
||||
MOZ_ASSERT(source->is<TypedArrayObject>(), "use setFromNonTypedArray");
|
||||
|
||||
bool isShared = target->isSharedMemory() || source->as<TypedArrayObject>().isSharedMemory();
|
||||
|
||||
switch (target->type()) {
|
||||
case Scalar::Int8:
|
||||
if (isShared)
|
||||
return ElementSpecific<Int8ArrayType, SharedOps>::setFromTypedArray(cx, target, source, offset);
|
||||
return ElementSpecific<Int8ArrayType, UnsharedOps>::setFromTypedArray(cx, target, source, offset);
|
||||
case Scalar::Uint8:
|
||||
if (isShared)
|
||||
return ElementSpecific<Uint8ArrayType, SharedOps>::setFromTypedArray(cx, target, source, offset);
|
||||
return ElementSpecific<Uint8ArrayType, UnsharedOps>::setFromTypedArray(cx, target, source, offset);
|
||||
case Scalar::Int16:
|
||||
if (isShared)
|
||||
return ElementSpecific<Int16ArrayType, SharedOps>::setFromTypedArray(cx, target, source, offset);
|
||||
return ElementSpecific<Int16ArrayType, UnsharedOps>::setFromTypedArray(cx, target, source, offset);
|
||||
case Scalar::Uint16:
|
||||
if (isShared)
|
||||
return ElementSpecific<Uint16ArrayType, SharedOps>::setFromTypedArray(cx, target, source, offset);
|
||||
return ElementSpecific<Uint16ArrayType, UnsharedOps>::setFromTypedArray(cx, target, source, offset);
|
||||
case Scalar::Int32:
|
||||
if (isShared)
|
||||
return ElementSpecific<Int32ArrayType, SharedOps>::setFromTypedArray(cx, target, source, offset);
|
||||
return ElementSpecific<Int32ArrayType, UnsharedOps>::setFromTypedArray(cx, target, source, offset);
|
||||
case Scalar::Uint32:
|
||||
if (isShared)
|
||||
return ElementSpecific<Uint32ArrayType, SharedOps>::setFromTypedArray(cx, target, source, offset);
|
||||
return ElementSpecific<Uint32ArrayType, UnsharedOps>::setFromTypedArray(cx, target, source, offset);
|
||||
case Scalar::Float32:
|
||||
if (isShared)
|
||||
return ElementSpecific<Float32ArrayType, SharedOps>::setFromTypedArray(cx, target, source, offset);
|
||||
return ElementSpecific<Float32ArrayType, UnsharedOps>::setFromTypedArray(cx, target, source, offset);
|
||||
case Scalar::Float64:
|
||||
if (isShared)
|
||||
return ElementSpecific<Float64ArrayType, SharedOps>::setFromTypedArray(cx, target, source, offset);
|
||||
return ElementSpecific<Float64ArrayType, UnsharedOps>::setFromTypedArray(cx, target, source, offset);
|
||||
case Scalar::Uint8Clamped:
|
||||
if (isShared)
|
||||
return ElementSpecific<Uint8ClampedArrayType, SharedOps>::setFromTypedArray(cx, target, source, offset);
|
||||
return ElementSpecific<Uint8ClampedArrayType, UnsharedOps>::setFromTypedArray(cx, target, source, offset);
|
||||
case Scalar::Int64:
|
||||
case Scalar::Float32x4:
|
||||
case Scalar::Int8x16:
|
||||
case Scalar::Int16x8:
|
||||
case Scalar::Int32x4:
|
||||
case Scalar::MaxTypedArrayViewType:
|
||||
break;
|
||||
}
|
||||
|
||||
MOZ_CRASH("nonsense target element type");
|
||||
}
|
||||
|
||||
static bool
|
||||
setFromNonTypedArray(JSContext* cx, Handle<SomeTypedArray*> target, HandleObject source,
|
||||
uint32_t len, uint32_t offset = 0)
|
||||
{
|
||||
MOZ_ASSERT(!source->is<TypedArrayObject>(), "use setFromTypedArray");
|
||||
|
||||
bool isShared = target->isSharedMemory();
|
||||
|
||||
switch (target->type()) {
|
||||
case Scalar::Int8:
|
||||
if (isShared)
|
||||
return ElementSpecific<Int8ArrayType, SharedOps>::setFromNonTypedArray(cx, target, source, len, offset);
|
||||
return ElementSpecific<Int8ArrayType, UnsharedOps>::setFromNonTypedArray(cx, target, source, len, offset);
|
||||
case Scalar::Uint8:
|
||||
if (isShared)
|
||||
return ElementSpecific<Uint8ArrayType, SharedOps>::setFromNonTypedArray(cx, target, source, len, offset);
|
||||
return ElementSpecific<Uint8ArrayType, UnsharedOps>::setFromNonTypedArray(cx, target, source, len, offset);
|
||||
case Scalar::Int16:
|
||||
if (isShared)
|
||||
return ElementSpecific<Int16ArrayType, SharedOps>::setFromNonTypedArray(cx, target, source, len, offset);
|
||||
return ElementSpecific<Int16ArrayType, UnsharedOps>::setFromNonTypedArray(cx, target, source, len, offset);
|
||||
case Scalar::Uint16:
|
||||
if (isShared)
|
||||
return ElementSpecific<Uint16ArrayType, SharedOps>::setFromNonTypedArray(cx, target, source, len, offset);
|
||||
return ElementSpecific<Uint16ArrayType, UnsharedOps>::setFromNonTypedArray(cx, target, source, len, offset);
|
||||
case Scalar::Int32:
|
||||
if (isShared)
|
||||
return ElementSpecific<Int32ArrayType, SharedOps>::setFromNonTypedArray(cx, target, source, len, offset);
|
||||
return ElementSpecific<Int32ArrayType, UnsharedOps>::setFromNonTypedArray(cx, target, source, len, offset);
|
||||
case Scalar::Uint32:
|
||||
if (isShared)
|
||||
return ElementSpecific<Uint32ArrayType, SharedOps>::setFromNonTypedArray(cx, target, source, len, offset);
|
||||
return ElementSpecific<Uint32ArrayType, UnsharedOps>::setFromNonTypedArray(cx, target, source, len, offset);
|
||||
case Scalar::Float32:
|
||||
if (isShared)
|
||||
return ElementSpecific<Float32ArrayType, SharedOps>::setFromNonTypedArray(cx, target, source, len, offset);
|
||||
return ElementSpecific<Float32ArrayType, UnsharedOps>::setFromNonTypedArray(cx, target, source, len, offset);
|
||||
case Scalar::Float64:
|
||||
if (isShared)
|
||||
return ElementSpecific<Float64ArrayType, SharedOps>::setFromNonTypedArray(cx, target, source, len, offset);
|
||||
return ElementSpecific<Float64ArrayType, UnsharedOps>::setFromNonTypedArray(cx, target, source, len, offset);
|
||||
case Scalar::Uint8Clamped:
|
||||
if (isShared)
|
||||
return ElementSpecific<Uint8ClampedArrayType, SharedOps>::setFromNonTypedArray(cx, target, source, len, offset);
|
||||
return ElementSpecific<Uint8ClampedArrayType, UnsharedOps>::setFromNonTypedArray(cx, target, source, len, offset);
|
||||
case Scalar::Int64:
|
||||
case Scalar::Float32x4:
|
||||
case Scalar::Int8x16:
|
||||
case Scalar::Int16x8:
|
||||
case Scalar::Int32x4:
|
||||
case Scalar::MaxTypedArrayViewType:
|
||||
break;
|
||||
}
|
||||
MOZ_CRASH("bad target array type");
|
||||
}
|
||||
|
||||
static bool
|
||||
initFromIterablePackedArray(JSContext* cx, Handle<SomeTypedArray*> target,
|
||||
HandleArrayObject source)
|
||||
{
|
||||
bool isShared = target->isSharedMemory();
|
||||
|
||||
switch (target->type()) {
|
||||
case Scalar::Int8:
|
||||
if (isShared)
|
||||
return ElementSpecific<Int8ArrayType, SharedOps>::initFromIterablePackedArray(cx, target, source);
|
||||
return ElementSpecific<Int8ArrayType, UnsharedOps>::initFromIterablePackedArray(cx, target, source);
|
||||
case Scalar::Uint8:
|
||||
if (isShared)
|
||||
return ElementSpecific<Uint8ArrayType, SharedOps>::initFromIterablePackedArray(cx, target, source);
|
||||
return ElementSpecific<Uint8ArrayType, UnsharedOps>::initFromIterablePackedArray(cx, target, source);
|
||||
case Scalar::Int16:
|
||||
if (isShared)
|
||||
return ElementSpecific<Int16ArrayType, SharedOps>::initFromIterablePackedArray(cx, target, source);
|
||||
return ElementSpecific<Int16ArrayType, UnsharedOps>::initFromIterablePackedArray(cx, target, source);
|
||||
case Scalar::Uint16:
|
||||
if (isShared)
|
||||
return ElementSpecific<Uint16ArrayType, SharedOps>::initFromIterablePackedArray(cx, target, source);
|
||||
return ElementSpecific<Uint16ArrayType, UnsharedOps>::initFromIterablePackedArray(cx, target, source);
|
||||
case Scalar::Int32:
|
||||
if (isShared)
|
||||
return ElementSpecific<Int32ArrayType, SharedOps>::initFromIterablePackedArray(cx, target, source);
|
||||
return ElementSpecific<Int32ArrayType, UnsharedOps>::initFromIterablePackedArray(cx, target, source);
|
||||
case Scalar::Uint32:
|
||||
if (isShared)
|
||||
return ElementSpecific<Uint32ArrayType, SharedOps>::initFromIterablePackedArray(cx, target, source);
|
||||
return ElementSpecific<Uint32ArrayType, UnsharedOps>::initFromIterablePackedArray(cx, target, source);
|
||||
case Scalar::Float32:
|
||||
if (isShared)
|
||||
return ElementSpecific<Float32ArrayType, SharedOps>::initFromIterablePackedArray(cx, target, source);
|
||||
return ElementSpecific<Float32ArrayType, UnsharedOps>::initFromIterablePackedArray(cx, target, source);
|
||||
case Scalar::Float64:
|
||||
if (isShared)
|
||||
return ElementSpecific<Float64ArrayType, SharedOps>::initFromIterablePackedArray(cx, target, source);
|
||||
return ElementSpecific<Float64ArrayType, UnsharedOps>::initFromIterablePackedArray(cx, target, source);
|
||||
case Scalar::Uint8Clamped:
|
||||
if (isShared)
|
||||
return ElementSpecific<Uint8ClampedArrayType, SharedOps>::initFromIterablePackedArray(cx, target, source);
|
||||
return ElementSpecific<Uint8ClampedArrayType, UnsharedOps>::initFromIterablePackedArray(cx, target, source);
|
||||
case Scalar::Int64:
|
||||
case Scalar::Float32x4:
|
||||
case Scalar::Int8x16:
|
||||
case Scalar::Int16x8:
|
||||
case Scalar::Int32x4:
|
||||
case Scalar::MaxTypedArrayViewType:
|
||||
break;
|
||||
}
|
||||
MOZ_CRASH("bad target array type");
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace js
|
||||
|
||||
#endif // vm_TypedArrayCommon_h
|
||||
#endif // vm_TypedArrayObject_inl_h
|
|
@ -4,6 +4,7 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "vm/TypedArrayObject-inl.h"
|
||||
#include "vm/TypedArrayObject.h"
|
||||
|
||||
#include "mozilla/Alignment.h"
|
||||
|
@ -41,7 +42,6 @@
|
|||
#include "vm/PIC.h"
|
||||
#include "vm/SelfHosting.h"
|
||||
#include "vm/SharedMem.h"
|
||||
#include "vm/TypedArrayCommon.h"
|
||||
#include "vm/WrapperObject.h"
|
||||
|
||||
#include "jsatominlines.h"
|
||||
|
@ -346,8 +346,6 @@ class TypedArrayObjectTemplate : public TypedArrayObject
|
|||
friend class TypedArrayObject;
|
||||
|
||||
public:
|
||||
typedef NativeType ElementType;
|
||||
|
||||
static constexpr Scalar::Type ArrayTypeID() { return TypeIDOfType<NativeType>::id; }
|
||||
static bool ArrayTypeIsUnsigned() { return TypeIsUnsigned<NativeType>(); }
|
||||
static bool ArrayTypeIsFloatingPoint() { return TypeIsFloatingPoint<NativeType>(); }
|
||||
|
@ -1028,12 +1026,6 @@ JS_FOR_EACH_TYPED_ARRAY(CREATE_TYPED_ARRAY)
|
|||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
struct TypedArrayObject::OfType
|
||||
{
|
||||
typedef TypedArrayObjectTemplate<T> Type;
|
||||
};
|
||||
|
||||
// ES 2016 draft Mar 25, 2016 24.1.1.1.
|
||||
// byteLength = count * unit
|
||||
template<typename T>
|
||||
|
@ -1280,8 +1272,14 @@ TypedArrayObjectTemplate<T>::fromTypedArray(JSContext* cx, HandleObject other, b
|
|||
return nullptr;
|
||||
|
||||
// Step 18.d-g or 24.1.1.4 step 11.
|
||||
if (!TypedArrayMethods<TypedArrayObject>::setFromTypedArray(cx, obj, srcArray))
|
||||
MOZ_ASSERT(!obj->isSharedMemory());
|
||||
if (isShared) {
|
||||
if (!ElementSpecific<T, SharedOps>::setFromTypedArray(cx, obj, srcArray, 0))
|
||||
return nullptr;
|
||||
} else {
|
||||
if (!ElementSpecific<T, UnsharedOps>::setFromTypedArray(cx, obj, srcArray, 0))
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Step 23.
|
||||
return obj;
|
||||
|
@ -1337,7 +1335,8 @@ TypedArrayObjectTemplate<T>::fromObject(JSContext* cx, HandleObject other, Handl
|
|||
return nullptr;
|
||||
|
||||
// Steps 6.d-e.
|
||||
if (!TypedArrayMethods<TypedArrayObject>::initFromIterablePackedArray(cx, obj, array))
|
||||
MOZ_ASSERT(!obj->isSharedMemory());
|
||||
if (!ElementSpecific<T, UnsharedOps>::initFromIterablePackedArray(cx, obj, array))
|
||||
return nullptr;
|
||||
|
||||
// Step 6.f (The assertion isn't applicable for the fast path).
|
||||
|
@ -1403,7 +1402,8 @@ TypedArrayObjectTemplate<T>::fromObject(JSContext* cx, HandleObject other, Handl
|
|||
return nullptr;
|
||||
|
||||
// Steps 11-12.
|
||||
if (!TypedArrayMethods<TypedArrayObject>::setFromNonTypedArray(cx, obj, arrayLike, len))
|
||||
MOZ_ASSERT(!obj->isSharedMemory());
|
||||
if (!ElementSpecific<T, UnsharedOps>::setFromNonTypedArray(cx, obj, arrayLike, len))
|
||||
return nullptr;
|
||||
|
||||
// Step 13.
|
||||
|
@ -1455,7 +1455,7 @@ JS_FOR_EACH_TYPED_ARRAY(CHECK_TYPED_ARRAY_CONSTRUCTOR)
|
|||
static bool
|
||||
TypedArray_lengthGetter(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
return TypedArrayObject::Getter<TypedArrayObject::lengthValue>(cx, argc, vp); \
|
||||
return TypedArrayObject::Getter<TypedArrayObject::lengthValue>(cx, argc, vp);
|
||||
}
|
||||
|
||||
static bool
|
||||
|
@ -1498,12 +1498,105 @@ TypedArrayObject::protoAccessors[] = {
|
|||
JS_PS_END
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
static inline bool
|
||||
SetFromTypedArray(JSContext* cx, Handle<TypedArrayObject*> target,
|
||||
Handle<TypedArrayObject*> source, uint32_t offset)
|
||||
{
|
||||
if (target->isSharedMemory() || source->isSharedMemory())
|
||||
return ElementSpecific<T, SharedOps>::setFromTypedArray(cx, target, source, offset);
|
||||
return ElementSpecific<T, UnsharedOps>::setFromTypedArray(cx, target, source, offset);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static inline bool
|
||||
SetFromNonTypedArray(JSContext* cx, Handle<TypedArrayObject*> target, HandleObject source,
|
||||
uint32_t len, uint32_t offset)
|
||||
{
|
||||
MOZ_ASSERT(!source->is<TypedArrayObject>(), "use SetFromTypedArray");
|
||||
|
||||
if (target->isSharedMemory())
|
||||
return ElementSpecific<T, SharedOps>::setFromNonTypedArray(cx, target, source, len, offset);
|
||||
return ElementSpecific<T, UnsharedOps>::setFromNonTypedArray(cx, target, source, len, offset);
|
||||
}
|
||||
|
||||
/* set(array[, offset]) */
|
||||
/* static */ bool
|
||||
TypedArrayObject::set_impl(JSContext* cx, const CallArgs& args)
|
||||
{
|
||||
MOZ_ASSERT(TypedArrayObject::is(args.thisv()));
|
||||
|
||||
Rooted<TypedArrayObject*> target(cx, &args.thisv().toObject().as<TypedArrayObject>());
|
||||
|
||||
// The first argument must be either a typed array or arraylike.
|
||||
if (args.length() == 0 || !args[0].isObject()) {
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
|
||||
return false;
|
||||
}
|
||||
|
||||
int32_t offset = 0;
|
||||
if (args.length() > 1) {
|
||||
if (!ToInt32(cx, args[1], &offset))
|
||||
return false;
|
||||
|
||||
if (offset < 0 || uint32_t(offset) > target->length()) {
|
||||
// the given offset is bogus
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_INDEX);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
RootedObject arg0(cx, &args[0].toObject());
|
||||
if (arg0->is<TypedArrayObject>()) {
|
||||
Handle<TypedArrayObject*> source = arg0.as<TypedArrayObject>();
|
||||
if (source->length() > target->length() - offset) {
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH);
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (target->type()) {
|
||||
#define SET_FROM_TYPED_ARRAY(T, N) \
|
||||
case Scalar::N: \
|
||||
if (!SetFromTypedArray<T>(cx, target, source, offset)) \
|
||||
return false; \
|
||||
break;
|
||||
JS_FOR_EACH_TYPED_ARRAY(SET_FROM_TYPED_ARRAY)
|
||||
#undef SET_FROM_TYPED_ARRAY
|
||||
default:
|
||||
MOZ_CRASH("Unsupported TypedArray type");
|
||||
}
|
||||
} else {
|
||||
uint32_t len;
|
||||
if (!GetLengthProperty(cx, arg0, &len))
|
||||
return false;
|
||||
|
||||
if (uint32_t(offset) > target->length() || len > target->length() - offset) {
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH);
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (target->type()) {
|
||||
#define SET_FROM_NON_TYPED_ARRAY(T, N) \
|
||||
case Scalar::N: \
|
||||
if (!SetFromNonTypedArray<T>(cx, target, arg0, len, offset)) \
|
||||
return false; \
|
||||
break;
|
||||
JS_FOR_EACH_TYPED_ARRAY(SET_FROM_NON_TYPED_ARRAY)
|
||||
#undef SET_FROM_NON_TYPED_ARRAY
|
||||
default:
|
||||
MOZ_CRASH("Unsupported TypedArray type");
|
||||
}
|
||||
}
|
||||
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
TypedArrayObject::set(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
return CallNonGenericMethod<TypedArrayObject::is,
|
||||
TypedArrayMethods<TypedArrayObject>::set>(cx, args);
|
||||
return CallNonGenericMethod<TypedArrayObject::is, TypedArrayObject::set_impl>(cx, args);
|
||||
}
|
||||
|
||||
/* static */ const JSFunctionSpec
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "jsobj.h"
|
||||
|
||||
#include "gc/Barrier.h"
|
||||
#include "gc/Zone.h"
|
||||
#include "js/Class.h"
|
||||
#include "vm/ArrayBufferObject.h"
|
||||
#include "vm/SharedArrayObject.h"
|
||||
|
@ -80,11 +81,6 @@ class TypedArrayObject : public NativeObject
|
|||
static_assert(js::detail::TypedArrayLengthSlot == LENGTH_SLOT,
|
||||
"bad inlined constant in jsfriendapi.h");
|
||||
|
||||
typedef TypedArrayObject SomeTypedArray;
|
||||
typedef ArrayBufferObject BufferType;
|
||||
|
||||
template<typename T> struct OfType;
|
||||
|
||||
static bool sameBuffer(Handle<TypedArrayObject*> a, Handle<TypedArrayObject*> b) {
|
||||
// Inline buffers.
|
||||
if (!a->hasBuffer() || !b->hasBuffer())
|
||||
|
@ -307,6 +303,9 @@ class TypedArrayObject : public NativeObject
|
|||
static bool is(HandleValue v);
|
||||
|
||||
static bool set(JSContext* cx, unsigned argc, Value* vp);
|
||||
|
||||
private:
|
||||
static bool set_impl(JSContext* cx, const CallArgs& args);
|
||||
};
|
||||
|
||||
MOZ_MUST_USE bool TypedArray_bufferGetter(JSContext* cx, unsigned argc, Value* vp);
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
#include "builtin/SIMD.h"
|
||||
#include "frontend/Parser.h"
|
||||
#include "gc/Policy.h"
|
||||
#include "jit/AtomicOperations.h"
|
||||
#include "js/MemoryMetrics.h"
|
||||
#include "vm/SelfHosting.h"
|
||||
#include "vm/StringBuffer.h"
|
||||
|
|
|
@ -6411,6 +6411,32 @@ BaseCompiler::emitSelect()
|
|||
break;
|
||||
}
|
||||
case ValType::I64: {
|
||||
#ifdef JS_CODEGEN_X86
|
||||
// There may be as many as four Int64 values in registers at a time: two
|
||||
// for the latent branch operands, and two for the true/false values we
|
||||
// normally pop before executing the branch. On x86 this is one value
|
||||
// too many, so we need to generate more complicated code here, and for
|
||||
// simplicity's sake we do so even if the branch operands are not Int64.
|
||||
// However, the resulting control flow diamond is complicated since the
|
||||
// arms of the diamond will have to stay synchronized with respect to
|
||||
// their evaluation stack and regalloc state. To simplify further, we
|
||||
// use a double branch and a temporary boolean value for now.
|
||||
RegI32 tmp = needI32();
|
||||
loadConstI32(tmp, 0);
|
||||
emitBranchPerform(&b);
|
||||
loadConstI32(tmp, 1);
|
||||
masm.bind(&done);
|
||||
|
||||
Label trueValue;
|
||||
RegI64 r0, r1;
|
||||
pop2xI64(&r0, &r1);
|
||||
masm.branch32(Assembler::Equal, tmp, Imm32(0), &trueValue);
|
||||
moveI64(r1, r0);
|
||||
masm.bind(&trueValue);
|
||||
freeI32(tmp);
|
||||
freeI64(r1);
|
||||
pushI64(r0);
|
||||
#else
|
||||
RegI64 r0, r1;
|
||||
pop2xI64(&r0, &r1);
|
||||
emitBranchPerform(&b);
|
||||
|
@ -6418,6 +6444,7 @@ BaseCompiler::emitSelect()
|
|||
masm.bind(&done);
|
||||
freeI64(r1);
|
||||
pushI64(r0);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case ValType::F32: {
|
||||
|
|
|
@ -1502,7 +1502,7 @@ FontFaceSet::CheckLoadingStarted()
|
|||
|
||||
mStatus = FontFaceSetLoadStatus::Loading;
|
||||
(new AsyncEventDispatcher(this, NS_LITERAL_STRING("loading"),
|
||||
false))->RunDOMEventWhenSafe();
|
||||
false))->PostDOMEvent();
|
||||
|
||||
if (PrefEnabled()) {
|
||||
if (mReady) {
|
||||
|
@ -1658,7 +1658,7 @@ FontFaceSet::DispatchLoadingFinishedEvent(
|
|||
}
|
||||
RefPtr<FontFaceSetLoadEvent> event =
|
||||
FontFaceSetLoadEvent::Constructor(this, aType, init);
|
||||
(new AsyncEventDispatcher(this, event))->RunDOMEventWhenSafe();
|
||||
(new AsyncEventDispatcher(this, event))->PostDOMEvent();
|
||||
}
|
||||
|
||||
// nsIDOMEventListener
|
||||
|
|
|
@ -36,10 +36,9 @@ support-files =
|
|||
|
||||
[test_acid3_test46.html]
|
||||
[test_addSheet.html]
|
||||
skip-if = stylo # bug 1290224
|
||||
support-files = additional_sheets_helper.html
|
||||
[test_additional_sheets.html]
|
||||
skip-if = stylo # bug 1290224
|
||||
skip-if = stylo # bug 1337258
|
||||
support-files = additional_sheets_helper.html
|
||||
[test_align_justify_computed_values.html]
|
||||
[test_align_shorthand_serialization.html]
|
||||
|
@ -141,7 +140,6 @@ support-files = bug732209-css.sjs
|
|||
[test_bug795520.html]
|
||||
[test_bug798567.html]
|
||||
[test_bug798843_pref.html]
|
||||
skip-if = stylo # 1332969
|
||||
[test_bug829816.html]
|
||||
[test_bug874919.html]
|
||||
support-files = file_bug829816.css
|
||||
|
@ -184,7 +182,6 @@ skip-if = toolkit == 'android' #bug 536603
|
|||
[test_descriptor_storage.html]
|
||||
[test_descriptor_syntax_errors.html]
|
||||
[test_dont_use_document_colors.html]
|
||||
skip-if = stylo # bug 1332969
|
||||
[test_dynamic_change_causing_reflow.html]
|
||||
[test_exposed_prop_accessors.html]
|
||||
[test_extra_inherit_initial.html]
|
||||
|
@ -227,7 +224,6 @@ skip-if = android_version == '18' #debug-only failure; timed out #Android 4.3 aw
|
|||
[test_media_queries_dynamic_xbl.html]
|
||||
[test_media_query_list.html]
|
||||
[test_moz_device_pixel_ratio.html]
|
||||
skip-if = stylo # bug 1332969
|
||||
[test_namespace_rule.html]
|
||||
[test_of_type_selectors.xhtml]
|
||||
[test_page_parser.html]
|
||||
|
@ -237,7 +233,6 @@ skip-if = stylo # bug 1332969
|
|||
[test_parse_url.html]
|
||||
[test_parser_diagnostics_unprintables.html]
|
||||
[test_pixel_lengths.html]
|
||||
skip-if = stylo # bug 1332969
|
||||
[test_pointer-events.html]
|
||||
[test_position_float_display.html]
|
||||
[test_position_sticky.html]
|
||||
|
@ -320,5 +315,4 @@ skip-if = (toolkit == 'android' || stylo) # TIMED_OUT for android, timeout bug 1
|
|||
[test_visited_reftests.html]
|
||||
skip-if = (toolkit == 'android' || stylo) # TIMED_OUT for android, timeout bug 1328511 for stylo
|
||||
[test_webkit_device_pixel_ratio.html]
|
||||
skip-if = stylo # bug 1332969
|
||||
[test_webkit_flex_display.html]
|
||||
|
|
|
@ -1124,14 +1124,18 @@ function runTest() {
|
|||
var p = Promise.resolve();
|
||||
sources.forEach(function({ win, doc, what}, i) {
|
||||
p = p.then(function() {
|
||||
return setTimeoutZero(); // wait for any previous events to be dispatched
|
||||
}).then(function() {
|
||||
var events = [], face, face2;
|
||||
|
||||
doc.fonts.onloadingdone = function(e) {
|
||||
events.push(e);
|
||||
};
|
||||
doc.fonts.onloadingerror = function(e) {
|
||||
var awaitEvents = new Promise(function(aResolve, aReject) {
|
||||
doc.fonts.onloadingdone = doc.fonts.onloadingerror = function(e) {
|
||||
events.push(e);
|
||||
if (events.length == 2) {
|
||||
aResolve();
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
is(doc.fonts.status, "loaded", "document.fonts.status should have status \"loaded\" (TEST 37) (" + what + ")");
|
||||
|
||||
|
@ -1151,6 +1155,8 @@ function runTest() {
|
|||
is(doc.fonts.status, "loading", "document.fonts.status should have status \"loading\" after second font added (TEST 37) (" + what + ")");
|
||||
|
||||
return doc.fonts.ready;
|
||||
}).then(function() {
|
||||
return awaitEvents;
|
||||
}).then(function() {
|
||||
is(doc.fonts.status, "loaded", "document.fonts.status should have status \"loaded\" after second font loaded (TEST 37) (" + what + ")");
|
||||
is(face2.status, "loaded", "second FontFace should have status \"loaded\" (TEST 37) (" + what + ")");
|
||||
|
@ -1182,14 +1188,18 @@ function runTest() {
|
|||
var p = Promise.resolve();
|
||||
sources.forEach(function({ win, doc, what }) {
|
||||
p = p.then(function() {
|
||||
return setTimeoutZero(); // wait for any previous events to be dispatched
|
||||
}).then(function() {
|
||||
var events = [], face, face2;
|
||||
|
||||
doc.fonts.onloadingdone = function(e) {
|
||||
events.push(e);
|
||||
};
|
||||
doc.fonts.onloadingerror = function(e) {
|
||||
var awaitEvents = new Promise(function(aResolve, aReject) {
|
||||
doc.fonts.onloadingdone = doc.fonts.onloadingerror = function(e) {
|
||||
events.push(e);
|
||||
if (events.length == 4) {
|
||||
aResolve();
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
is(doc.fonts.status, "loaded", "document.fonts.status should have status \"loaded\" (TEST 38) (" + what + ")");
|
||||
|
||||
|
@ -1209,6 +1219,8 @@ function runTest() {
|
|||
is(doc.fonts.status, "loading", "document.fonts.status should have status \"loading\" after second font added (TEST 38) (" + what + ")");
|
||||
|
||||
return doc.fonts.ready;
|
||||
}).then(function() {
|
||||
return awaitEvents;
|
||||
}).then(function() {
|
||||
is(doc.fonts.status, "loaded", "document.fonts.status should have status \"loaded\" after second font failed to load (TEST 38) (" + what + ")");
|
||||
is(face2.status, "error", "second FontFace should have status \"error\" (TEST 38) (" + what + ")");
|
||||
|
@ -1243,21 +1255,27 @@ function runTest() {
|
|||
// (TEST 39) Test that a FontFace for an @font-face rule only has one
|
||||
// loadingdone event dispatched at the FontFaceSet containing it.
|
||||
|
||||
var style = document.querySelector("style");
|
||||
var style, all, events, awaitEvents;
|
||||
|
||||
return setTimeoutZero() // wait for any previous events to be dispatched
|
||||
.then(function() {
|
||||
style = document.querySelector("style");
|
||||
var ruleText = "@font-face { font-family: test; src: url(BitPattern.woff?test39a); } " +
|
||||
"@font-face { font-family: test2; src: url(BitPattern.woff?test39b); }";
|
||||
|
||||
style.textContent = ruleText;
|
||||
|
||||
var all = Array.from(document.fonts);
|
||||
var events = [];
|
||||
all = Array.from(document.fonts);
|
||||
events = [];
|
||||
|
||||
document.fonts.onloadingdone = function(e) {
|
||||
events.push(e);
|
||||
};
|
||||
document.fonts.onloadingerror = function(e) {
|
||||
awaitEvents = new Promise(function(aResolve, aReject) {
|
||||
document.fonts.onloadingdone = document.fonts.onloadingerror = function(e) {
|
||||
events.push(e);
|
||||
if (events.length == 2) {
|
||||
aResolve();
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
is(document.fonts.status, "loaded", "document.fonts.status should have status \"loaded\" (TEST 39)");
|
||||
|
||||
|
@ -1265,7 +1283,7 @@ function runTest() {
|
|||
is(document.fonts.status, "loading", "document.fonts.status should have status \"loading\" after first font loading (TEST 39)");
|
||||
|
||||
return document.fonts.ready
|
||||
.then(function() {
|
||||
}).then(function() {
|
||||
is(document.fonts.status, "loaded", "document.fonts.status should have status \"loaded\" after first font loaded (TEST 39)");
|
||||
is(all[0].status, "loaded", "first FontFace should have status \"loaded\" (TEST 39)");
|
||||
is(all[1].status, "unloaded", "second FontFace should have status \"unloaded\" (TEST 39)");
|
||||
|
@ -1274,6 +1292,8 @@ function runTest() {
|
|||
is(document.fonts.status, "loading", "document.fonts.status should have status \"loading\" after second font loading (TEST 39)");
|
||||
|
||||
return document.fonts.ready;
|
||||
}).then(function() {
|
||||
return awaitEvents;
|
||||
}).then(function() {
|
||||
is(document.fonts.status, "loaded", "document.fonts.status should have status \"loaded\" after second font loaded (TEST 39)");
|
||||
is(all[1].status, "loaded", "second FontFace should have status \"loaded\" (TEST 39)");
|
||||
|
|
|
@ -791,7 +791,9 @@ nsIOService::NewChannelFromURIWithProxyFlagsInternal(nsIURI* aURI,
|
|||
// creating a new channel by calling NewChannel().
|
||||
if (NS_FAILED(rv)) {
|
||||
rv = handler->NewChannel(aURI, getter_AddRefs(channel));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
// The protocol handler does not implement NewChannel2, so
|
||||
// maybe we need to wrap the channel (see comment in MaybeWrap
|
||||
// function).
|
||||
|
|
|
@ -177,7 +177,9 @@ NS_NewChannelInternal(nsIChannel **outChannel,
|
|||
aSecurityFlags,
|
||||
aContentPolicyType,
|
||||
getter_AddRefs(channel));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (aLoadGroup) {
|
||||
rv = channel->SetLoadGroup(aLoadGroup);
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
// do_check_eq(a, b) to avoid outputting characters which confuse
|
||||
// the console
|
||||
|
||||
"use strict";
|
||||
|
||||
var CC = Components.Constructor;
|
||||
const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1",
|
||||
"nsIBinaryInputStream",
|
||||
|
@ -34,6 +36,7 @@ function run_test()
|
|||
|
||||
let binaryStream = new BinaryInputStream(new_file_input_stream(binaryFile));
|
||||
test_file_data = "";
|
||||
let avail = 0;
|
||||
while ((avail = binaryStream.available()) > 0) {
|
||||
test_file_data += binaryStream.readBytes(avail);
|
||||
}
|
||||
|
|
|
@ -17,6 +17,9 @@
|
|||
// run_myipaddress_test();
|
||||
// run_failed_script_test();
|
||||
// run_isresolvable_test();
|
||||
|
||||
"use strict";
|
||||
|
||||
Cu.import("resource://gre/modules/NetUtil.jsm");
|
||||
|
||||
var ios = Components.classes["@mozilla.org/network/io-service;1"]
|
||||
|
@ -166,7 +169,7 @@ resolveCallback.prototype = {
|
|||
return this;
|
||||
},
|
||||
|
||||
onProxyAvailable : function (req, uri, pi, status) {
|
||||
onProxyAvailable : function (req, channel, pi, status) {
|
||||
this.nextFunction(pi);
|
||||
}
|
||||
};
|
||||
|
@ -496,14 +499,14 @@ TestResolveCallback.prototype = {
|
|||
},
|
||||
|
||||
onProxyAvailable:
|
||||
function TestResolveCallback_onProxyAvailable(req, uri, pi, status) {
|
||||
dump("*** uri=" + uri.spec + ", status=" + status + "\n");
|
||||
function TestResolveCallback_onProxyAvailable(req, channel, pi, status) {
|
||||
dump("*** channelURI=" + channel.URI.spec + ", status=" + status + "\n");
|
||||
|
||||
if (this.type == null) {
|
||||
do_check_eq(pi, null);
|
||||
} else {
|
||||
do_check_neq(req, null);
|
||||
do_check_neq(uri, null);
|
||||
do_check_neq(channel, null);
|
||||
do_check_eq(status, 0);
|
||||
do_check_neq(pi, null);
|
||||
check_proxy(pi, this.type, "foopy", 8080, 0, -1, true);
|
||||
|
@ -613,11 +616,11 @@ TestResolveCancelationCallback.prototype = {
|
|||
},
|
||||
|
||||
onProxyAvailable:
|
||||
function TestResolveCancelationCallback_onProxyAvailable(req, uri, pi, status) {
|
||||
dump("*** uri=" + uri.spec + ", status=" + status + "\n");
|
||||
function TestResolveCancelationCallback_onProxyAvailable(req, channel, pi, status) {
|
||||
dump("*** channelURI=" + channel.URI.spec + ", status=" + status + "\n");
|
||||
|
||||
do_check_neq(req, null);
|
||||
do_check_neq(uri, null);
|
||||
do_check_neq(channel, null);
|
||||
do_check_eq(status, Components.results.NS_ERROR_ABORT);
|
||||
do_check_eq(pi, null);
|
||||
|
||||
|
@ -701,6 +704,7 @@ function host_filter_cb(proxy)
|
|||
var uriStrUseProxyList;
|
||||
var uriStrUseProxyList;
|
||||
var hostFilterList;
|
||||
var uriStrFilterList;
|
||||
|
||||
function run_proxy_host_filters_test() {
|
||||
// Get prefs object from DOM
|
||||
|
|
|
@ -1,9 +1,15 @@
|
|||
// This testcase verifies that channels can't be reopened
|
||||
// See https://bugzilla.mozilla.org/show_bug.cgi?id=372486
|
||||
|
||||
"use strict";
|
||||
|
||||
Cu.import("resource://testing-common/httpd.js");
|
||||
Cu.import("resource://gre/modules/NetUtil.jsm");
|
||||
|
||||
const BinaryInputStream = Components.Constructor(
|
||||
"@mozilla.org/binaryinputstream;1", "nsIBinaryInputStream",
|
||||
"setInputStream");
|
||||
|
||||
const NS_ERROR_IN_PROGRESS = 0x804b000f;
|
||||
const NS_ERROR_ALREADY_OPENED = 0x804b0049;
|
||||
|
||||
|
|
|
@ -250,6 +250,39 @@ DWORD RestrictedToken::AddAllSidsForDenyOnly(std::vector<Sid> *exceptions) {
|
|||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
DWORD RestrictedToken::AddDenyOnlySids(const std::vector<Sid>& deny_only_sids) {
|
||||
DCHECK(init_);
|
||||
if (!init_) {
|
||||
return ERROR_NO_TOKEN;
|
||||
}
|
||||
|
||||
DWORD error;
|
||||
scoped_ptr<BYTE[]> buffer = GetTokenInfo(effective_token_, TokenGroups, &error);
|
||||
|
||||
if (!buffer) {
|
||||
return error;
|
||||
}
|
||||
|
||||
TOKEN_GROUPS* token_groups = reinterpret_cast<TOKEN_GROUPS*>(buffer.get());
|
||||
|
||||
// Build the list of the deny only group SIDs
|
||||
for (unsigned int i = 0; i < token_groups->GroupCount ; ++i) {
|
||||
if ((token_groups->Groups[i].Attributes & SE_GROUP_INTEGRITY) == 0 &&
|
||||
(token_groups->Groups[i].Attributes & SE_GROUP_LOGON_ID) == 0) {
|
||||
for (unsigned int j = 0; j < deny_only_sids.size(); ++j) {
|
||||
if (::EqualSid(const_cast<SID*>(deny_only_sids[j].GetPSID()),
|
||||
token_groups->Groups[i].Sid)) {
|
||||
sids_for_deny_only_.push_back(
|
||||
reinterpret_cast<SID*>(token_groups->Groups[i].Sid));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
DWORD RestrictedToken::AddSidForDenyOnly(const Sid &sid) {
|
||||
DCHECK(init_);
|
||||
if (!init_)
|
||||
|
|
|
@ -88,6 +88,17 @@ class RestrictedToken {
|
|||
// access to any resource. It can only be used to deny access.
|
||||
DWORD AddAllSidsForDenyOnly(std::vector<Sid> *exceptions);
|
||||
|
||||
// Lists all sids in the token and mark them as Deny Only if present in the
|
||||
// deny_only_sids parameter.
|
||||
//
|
||||
// If the function succeeds, the return value is ERROR_SUCCESS. If the
|
||||
// function fails, the return value is the win32 error code corresponding to
|
||||
// the error.
|
||||
//
|
||||
// Note: A Sid marked for Deny Only in a token cannot be used to grant
|
||||
// access to any resource. It can only be used to deny access.
|
||||
DWORD AddDenyOnlySids(const std::vector<Sid>& deny_only_sids);
|
||||
|
||||
// Adds a user or group SID for Deny Only in the restricted token.
|
||||
// Parameter: sid is the SID to add in the Deny Only list.
|
||||
// The return value is always ERROR_SUCCESS.
|
||||
|
|
|
@ -27,6 +27,7 @@ DWORD CreateRestrictedToken(TokenLevel security_level,
|
|||
|
||||
std::vector<base::string16> privilege_exceptions;
|
||||
std::vector<Sid> sid_exceptions;
|
||||
std::vector<Sid> deny_only_sids;
|
||||
|
||||
bool deny_sids = true;
|
||||
bool remove_privileges = true;
|
||||
|
@ -48,10 +49,16 @@ DWORD CreateRestrictedToken(TokenLevel security_level,
|
|||
break;
|
||||
}
|
||||
case USER_NON_ADMIN: {
|
||||
sid_exceptions.push_back(WinBuiltinUsersSid);
|
||||
sid_exceptions.push_back(WinWorldSid);
|
||||
sid_exceptions.push_back(WinInteractiveSid);
|
||||
sid_exceptions.push_back(WinAuthenticatedUserSid);
|
||||
deny_sids = false;
|
||||
deny_only_sids.push_back(WinBuiltinAdministratorsSid);
|
||||
deny_only_sids.push_back(WinAccountAdministratorSid);
|
||||
deny_only_sids.push_back(WinAccountDomainAdminsSid);
|
||||
deny_only_sids.push_back(WinAccountCertAdminsSid);
|
||||
deny_only_sids.push_back(WinAccountSchemaAdminsSid);
|
||||
deny_only_sids.push_back(WinAccountEnterpriseAdminsSid);
|
||||
deny_only_sids.push_back(WinAccountPolicyAdminsSid);
|
||||
deny_only_sids.push_back(WinBuiltinHyperVAdminsSid);
|
||||
deny_only_sids.push_back(WinLocalAccountAndAdministratorSid);
|
||||
privilege_exceptions.push_back(SE_CHANGE_NOTIFY_NAME);
|
||||
break;
|
||||
}
|
||||
|
@ -107,6 +114,11 @@ DWORD CreateRestrictedToken(TokenLevel security_level,
|
|||
err_code = restricted_token.AddAllSidsForDenyOnly(&sid_exceptions);
|
||||
if (ERROR_SUCCESS != err_code)
|
||||
return err_code;
|
||||
} else if (!deny_only_sids.empty()) {
|
||||
err_code = restricted_token.AddDenyOnlySids(deny_only_sids);
|
||||
if (ERROR_SUCCESS != err_code) {
|
||||
return err_code;
|
||||
}
|
||||
}
|
||||
|
||||
if (remove_privileges) {
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
Please add a link to the bugzilla bug and patch name that should be re-applied.
|
||||
Also, please update any existing links to their actual mozilla-central changeset.
|
||||
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1287426 bug1287426part5.patch
|
||||
https://hg.mozilla.org/mozilla-central/rev/a05726163a79
|
||||
https://hg.mozilla.org/mozilla-central/rev/7df8d6639971
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1287426 bug1287426part7.patch
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1273372 bug1273372part2.patch
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1273372 bug1273372part3.patch
|
||||
https://hg.mozilla.org/mozilla-central/rev/e834e810a3fa
|
||||
https://hg.mozilla.org/mozilla-central/rev/c70d06fa5302
|
||||
https://hg.mozilla.org/mozilla-central/rev/d24db55deb85
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1321724 bug1321724.patch
|
||||
|
|
|
@ -92,6 +92,6 @@ def make_decision_task(params, symbol, arguments=[], head_rev=None):
|
|||
task['taskGroupId'] = task_id
|
||||
|
||||
# set the schedulerId based on the level
|
||||
task['schedulerId'] = 'gecko-level-{}-cron'.format(params['level'])
|
||||
task['schedulerId'] = 'gecko-level-{}'.format(params['level'])
|
||||
|
||||
return (task_id, task)
|
||||
|
|
|
@ -829,3 +829,279 @@ class ChunkFinder(MachCommandBase):
|
|||
os.remove(dump_tests)
|
||||
if temp_dir:
|
||||
shutil.rmtree(temp_dir)
|
||||
|
||||
|
||||
@CommandProvider
|
||||
class TestInfoCommand(MachCommandBase):
|
||||
from datetime import date, timedelta
|
||||
@Command('test-info', category='testing',
|
||||
description='Display historical test result summary.')
|
||||
@CommandArgument('test_name', nargs='?', metavar='N',
|
||||
help='Test of interest.')
|
||||
@CommandArgument('--branches',
|
||||
default='mozilla-central,mozilla-inbound,autoland',
|
||||
help='Report for named branches (default: mozilla-central,mozilla-inbound,autoland)')
|
||||
@CommandArgument('--start',
|
||||
default=(date.today() - timedelta(7)).strftime("%Y-%m-%d"),
|
||||
help='Start date (YYYY-MM-DD)')
|
||||
@CommandArgument('--end',
|
||||
default=date.today().strftime("%Y-%m-%d"),
|
||||
help='End date (YYYY-MM-DD)')
|
||||
|
||||
def test_info(self, **params):
|
||||
|
||||
import which
|
||||
from mozbuild.base import MozbuildObject
|
||||
|
||||
self.test_name = params['test_name']
|
||||
self.branches = params['branches']
|
||||
self.start = params['start']
|
||||
self.end = params['end']
|
||||
|
||||
if len(self.test_name) < 6:
|
||||
print("'%s' is too short for a test name!" % self.test_name)
|
||||
return
|
||||
|
||||
here = os.path.abspath(os.path.dirname(__file__))
|
||||
build_obj = MozbuildObject.from_environment(cwd=here)
|
||||
|
||||
self._hg = None
|
||||
if conditions.is_hg(build_obj):
|
||||
if self._is_windows():
|
||||
self._hg = which.which('hg.exe')
|
||||
else:
|
||||
self._hg = which.which('hg')
|
||||
|
||||
self._git = None
|
||||
if conditions.is_git(build_obj):
|
||||
if self._is_windows():
|
||||
self._git = which.which('git.exe')
|
||||
else:
|
||||
self._git = which.which('git')
|
||||
|
||||
self.set_test_name()
|
||||
self.report_test_results()
|
||||
self.report_test_durations()
|
||||
self.report_bugs()
|
||||
|
||||
def find_in_hg_or_git(self, test_name):
|
||||
if self._hg:
|
||||
cmd = [self._hg, 'files', '-I', test_name]
|
||||
elif self._git:
|
||||
cmd = [self._git, 'ls-files', test_name]
|
||||
else:
|
||||
return None
|
||||
try:
|
||||
out = subprocess.check_output(cmd).splitlines()
|
||||
except subprocess.CalledProcessError:
|
||||
out = None
|
||||
return out
|
||||
|
||||
def set_test_name(self):
|
||||
# Generating a unified report for a specific test is complicated
|
||||
# by differences in the test name used in various data sources.
|
||||
# Consider:
|
||||
# - It is often convenient to request a report based only on
|
||||
# a short file name, rather than the full path;
|
||||
# - Bugs may be filed in bugzilla against a simple, short test
|
||||
# name or the full path to the test;
|
||||
# - In ActiveData, the full path is usually used, but sometimes
|
||||
# also includes additional path components outside of the
|
||||
# mercurial repo (common for reftests).
|
||||
# This function attempts to find appropriate names for different
|
||||
# queries based on the specified test name.
|
||||
|
||||
import re
|
||||
|
||||
# full_test_name is full path to file in hg (or git)
|
||||
self.full_test_name = None
|
||||
out = self.find_in_hg_or_git(self.test_name)
|
||||
if out and len(out) == 1:
|
||||
self.full_test_name = out[0]
|
||||
elif out and len(out) > 1:
|
||||
print("Ambiguous test name specified. Found:")
|
||||
for line in out:
|
||||
print(line)
|
||||
else:
|
||||
out = self.find_in_hg_or_git('**/%s*' % self.test_name)
|
||||
if out and len(out) == 1:
|
||||
self.full_test_name = out[0]
|
||||
elif out and len(out) > 1:
|
||||
print("Ambiguous test name. Found:")
|
||||
for line in out:
|
||||
print(line)
|
||||
if self.full_test_name:
|
||||
print("Found %s in source control." % self.full_test_name)
|
||||
else:
|
||||
print("Unable to validate test name '%s'!" % self.test_name)
|
||||
self.full_test_name = self.test_name
|
||||
|
||||
# short_name is full_test_name without path
|
||||
self.short_name = None
|
||||
name_idx = self.full_test_name.rfind('/')
|
||||
if name_idx > 0:
|
||||
self.short_name = self.full_test_name[name_idx+1:]
|
||||
|
||||
# robo_name is short_name without ".java" - for robocop
|
||||
self.robo_name = None
|
||||
if self.short_name:
|
||||
robo_idx = self.short_name.rfind('.java')
|
||||
if robo_idx > 0:
|
||||
self.robo_name = self.short_name[:robo_idx]
|
||||
if self.short_name == self.test_name:
|
||||
self.short_name = None
|
||||
|
||||
# activedata_test_name is name in ActiveData
|
||||
self.activedata_test_name = None
|
||||
simple_names = [
|
||||
self.full_test_name,
|
||||
self.test_name,
|
||||
self.short_name,
|
||||
self.robo_name
|
||||
]
|
||||
simple_names = [x for x in simple_names if x]
|
||||
searches = [
|
||||
{"in": {"result.test": simple_names}},
|
||||
]
|
||||
regex_names = [".*%s.*" % re.escape(x) for x in simple_names if x]
|
||||
for r in regex_names:
|
||||
searches.append({"regexp": {"result.test": r}})
|
||||
query = {
|
||||
"from": "unittest",
|
||||
"format": "list",
|
||||
"limit": 10,
|
||||
"groupby": ["result.test"],
|
||||
"where": {"and": [
|
||||
{"or": searches},
|
||||
{"in": {"build.branch": self.branches.split(',')}},
|
||||
{"gt": {"run.timestamp": {"date": self.start}}},
|
||||
{"lt": {"run.timestamp": {"date": self.end}}}
|
||||
]}
|
||||
}
|
||||
print("Querying ActiveData...") # Following query can take a long time
|
||||
data = self.submit(query)
|
||||
if data and len(data) > 0:
|
||||
self.activedata_test_name = [
|
||||
d['result']['test']
|
||||
for p in simple_names + regex_names
|
||||
for d in data
|
||||
if re.match(p+"$", d['result']['test'])
|
||||
][0] # first match is best match
|
||||
if self.activedata_test_name:
|
||||
print("Found records matching '%s' in ActiveData." %
|
||||
self.activedata_test_name)
|
||||
else:
|
||||
print("Unable to find matching records in ActiveData; using %s!" %
|
||||
self.test_name)
|
||||
self.activedata_test_name = self.test_name
|
||||
|
||||
def get_platform(self, record):
|
||||
platform = record['build']['platform']
|
||||
type = record['build']['type']
|
||||
e10s = "-%s" % record['run']['type'] if 'run' in record else ""
|
||||
return "%s/%s%s:" % (platform, type, e10s)
|
||||
|
||||
def submit(self, query):
|
||||
import requests
|
||||
response = requests.post("http://activedata.allizom.org/query",
|
||||
data=json.dumps(query),
|
||||
stream=True)
|
||||
response.raise_for_status()
|
||||
data = response.json()["data"]
|
||||
return data
|
||||
|
||||
def report_test_results(self):
|
||||
# Report test pass/fail summary from ActiveData
|
||||
query = {
|
||||
"from": "unittest",
|
||||
"format": "list",
|
||||
"limit": 100,
|
||||
"groupby": ["build.platform", "build.type", "run.type"],
|
||||
"select": [
|
||||
{"aggregate": "count"},
|
||||
{
|
||||
"name": "failures",
|
||||
"value": {"case": [
|
||||
{"when": {"eq": {"result.ok": "F"}}, "then": 1}
|
||||
]},
|
||||
"aggregate": "sum",
|
||||
"default": 0
|
||||
}
|
||||
],
|
||||
"where": {"and": [
|
||||
{"eq": {"result.test": self.activedata_test_name}},
|
||||
{"in": {"build.branch": self.branches.split(',')}},
|
||||
{"gt": {"run.timestamp": {"date": self.start}}},
|
||||
{"lt": {"run.timestamp": {"date": self.end}}}
|
||||
]}
|
||||
}
|
||||
print("\nTest results for %s on %s between %s and %s" %
|
||||
(self.activedata_test_name, self.branches, self.start, self.end))
|
||||
data = self.submit(query)
|
||||
if data and len(data) > 0:
|
||||
data.sort(key=self.get_platform)
|
||||
for record in data:
|
||||
platform = self.get_platform(record)
|
||||
runs = record['count']
|
||||
failures = record['failures']
|
||||
print("%-30s %6d failures in %6d runs" % (
|
||||
platform, failures, runs))
|
||||
else:
|
||||
print("No test result data found.")
|
||||
|
||||
def report_test_durations(self):
|
||||
# Report test durations summary from ActiveData
|
||||
query = {
|
||||
"from": "unittest",
|
||||
"format": "list",
|
||||
"limit": 100,
|
||||
"groupby": ["build.platform","build.type","run.type"],
|
||||
"select": [
|
||||
{"value":"result.duration","aggregate":"average","name":"average"},
|
||||
{"value":"result.duration","aggregate":"min","name":"min"},
|
||||
{"value":"result.duration","aggregate":"max","name":"max"},
|
||||
{"aggregate":"count"}
|
||||
],
|
||||
"where": {"and": [
|
||||
{"eq": {"result.ok": "T"}},
|
||||
{"eq": {"result.test": self.activedata_test_name}},
|
||||
{"in": {"build.branch": self.branches.split(',')}},
|
||||
{"gt": {"run.timestamp": {"date": self.start}}},
|
||||
{"lt": {"run.timestamp": {"date": self.end}}}
|
||||
]}
|
||||
}
|
||||
data = self.submit(query)
|
||||
print("\nTest durations for %s on %s between %s and %s" %
|
||||
(self.activedata_test_name, self.branches, self.start, self.end))
|
||||
if data and len(data) > 0:
|
||||
data.sort(key=self.get_platform)
|
||||
for record in data:
|
||||
platform = self.get_platform(record)
|
||||
print("%-30s %6.2f s (%.2f s - %.2f s over %d runs)" % (
|
||||
platform, record['average'], record['min'],
|
||||
record['max'], record['count']))
|
||||
else:
|
||||
print("No test durations found.")
|
||||
|
||||
def report_bugs(self):
|
||||
# Report open bugs matching test name
|
||||
import requests
|
||||
search = self.full_test_name
|
||||
if self.test_name:
|
||||
search = '%s,%s' % (search, self.test_name)
|
||||
if self.short_name:
|
||||
search = '%s,%s' % (search, self.short_name)
|
||||
if self.robo_name:
|
||||
search = '%s,%s' % (search, self.robo_name)
|
||||
payload = {'quicksearch': search,
|
||||
'include_fields':'id,summary'}
|
||||
response = requests.get('https://bugzilla.mozilla.org/rest/bug',
|
||||
payload)
|
||||
response.raise_for_status()
|
||||
json_response = response.json()
|
||||
print("\nBugzilla quick search for '%s':" % search)
|
||||
if 'bugs' in json_response:
|
||||
for bug in json_response['bugs']:
|
||||
print("Bug %s: %s" % (bug['id'], bug['summary']))
|
||||
else:
|
||||
print("No bugs found.")
|
||||
|
|
|
@ -2,11 +2,6 @@
|
|||
* 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/. */
|
||||
|
||||
#if _WIN32_WINNT < 0x0600
|
||||
#undef _WIN32_WINNT
|
||||
#define _WIN32_WINNT 0x0600
|
||||
#endif
|
||||
|
||||
#include <shlobj.h>
|
||||
#include <stdio.h>
|
||||
|
||||
|
|
|
@ -31,67 +31,50 @@ using mozilla::JSONWriter;
|
|||
|
||||
ProfileEntry::ProfileEntry()
|
||||
: mTagData(nullptr)
|
||||
, mTagName(0)
|
||||
, mKind(Kind::INVALID)
|
||||
{ }
|
||||
|
||||
// aTagData must not need release (i.e. be a string from the text segment)
|
||||
ProfileEntry::ProfileEntry(char aTagName, const char *aTagData)
|
||||
ProfileEntry::ProfileEntry(Kind aKind, const char *aTagData)
|
||||
: mTagData(aTagData)
|
||||
, mTagName(aTagName)
|
||||
, mKind(aKind)
|
||||
{ }
|
||||
|
||||
ProfileEntry::ProfileEntry(char aTagName, ProfilerMarker *aTagMarker)
|
||||
ProfileEntry::ProfileEntry(Kind aKind, ProfilerMarker *aTagMarker)
|
||||
: mTagMarker(aTagMarker)
|
||||
, mTagName(aTagName)
|
||||
, mKind(aKind)
|
||||
{ }
|
||||
|
||||
ProfileEntry::ProfileEntry(char aTagName, void *aTagPtr)
|
||||
ProfileEntry::ProfileEntry(Kind aKind, void *aTagPtr)
|
||||
: mTagPtr(aTagPtr)
|
||||
, mTagName(aTagName)
|
||||
, mKind(aKind)
|
||||
{ }
|
||||
|
||||
ProfileEntry::ProfileEntry(char aTagName, double aTagDouble)
|
||||
ProfileEntry::ProfileEntry(Kind aKind, double aTagDouble)
|
||||
: mTagDouble(aTagDouble)
|
||||
, mTagName(aTagName)
|
||||
, mKind(aKind)
|
||||
{ }
|
||||
|
||||
ProfileEntry::ProfileEntry(char aTagName, uintptr_t aTagOffset)
|
||||
ProfileEntry::ProfileEntry(Kind aKind, uintptr_t aTagOffset)
|
||||
: mTagOffset(aTagOffset)
|
||||
, mTagName(aTagName)
|
||||
, mKind(aKind)
|
||||
{ }
|
||||
|
||||
ProfileEntry::ProfileEntry(char aTagName, Address aTagAddress)
|
||||
ProfileEntry::ProfileEntry(Kind aKind, Address aTagAddress)
|
||||
: mTagAddress(aTagAddress)
|
||||
, mTagName(aTagName)
|
||||
, mKind(aKind)
|
||||
{ }
|
||||
|
||||
ProfileEntry::ProfileEntry(char aTagName, int aTagInt)
|
||||
ProfileEntry::ProfileEntry(Kind aKind, int aTagInt)
|
||||
: mTagInt(aTagInt)
|
||||
, mTagName(aTagName)
|
||||
, mKind(aKind)
|
||||
{ }
|
||||
|
||||
ProfileEntry::ProfileEntry(char aTagName, char aTagChar)
|
||||
ProfileEntry::ProfileEntry(Kind aKind, char aTagChar)
|
||||
: mTagChar(aTagChar)
|
||||
, mTagName(aTagName)
|
||||
, mKind(aKind)
|
||||
{ }
|
||||
|
||||
bool ProfileEntry::is_ent_hint(char hintChar) {
|
||||
return mTagName == 'h' && mTagChar == hintChar;
|
||||
}
|
||||
|
||||
bool ProfileEntry::is_ent_hint() {
|
||||
return mTagName == 'h';
|
||||
}
|
||||
|
||||
bool ProfileEntry::is_ent(char tagChar) {
|
||||
return mTagName == tagChar;
|
||||
}
|
||||
|
||||
void* ProfileEntry::get_tagPtr() {
|
||||
// No consistency checking. Oh well.
|
||||
return mTagPtr;
|
||||
}
|
||||
|
||||
// END ProfileEntry
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
@ -607,40 +590,40 @@ void ProfileBuffer::StreamSamplesToJSON(SpliceableJSONWriter& aWriter, int aThre
|
|||
|
||||
while (readPos != mWritePos) {
|
||||
ProfileEntry entry = mEntries[readPos];
|
||||
if (entry.mTagName == 'T') {
|
||||
if (entry.isThreadId()) {
|
||||
currentThreadID = entry.mTagInt;
|
||||
currentTime.reset();
|
||||
int readAheadPos = (readPos + 1) % mEntrySize;
|
||||
if (readAheadPos != mWritePos) {
|
||||
ProfileEntry readAheadEntry = mEntries[readAheadPos];
|
||||
if (readAheadEntry.mTagName == 't') {
|
||||
if (readAheadEntry.isTime()) {
|
||||
currentTime = Some(readAheadEntry.mTagDouble);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (currentThreadID == aThreadId && (currentTime.isNothing() || *currentTime >= aSinceTime)) {
|
||||
switch (entry.mTagName) {
|
||||
case 'r':
|
||||
switch (entry.kind()) {
|
||||
case ProfileEntry::Kind::Responsiveness:
|
||||
if (sample.isSome()) {
|
||||
sample->mResponsiveness = Some(entry.mTagDouble);
|
||||
}
|
||||
break;
|
||||
case 'R':
|
||||
case ProfileEntry::Kind::ResidentMemory:
|
||||
if (sample.isSome()) {
|
||||
sample->mRSS = Some(entry.mTagDouble);
|
||||
}
|
||||
break;
|
||||
case 'U':
|
||||
case ProfileEntry::Kind::UnsharedMemory:
|
||||
if (sample.isSome()) {
|
||||
sample->mUSS = Some(entry.mTagDouble);
|
||||
}
|
||||
break;
|
||||
case 'f':
|
||||
case ProfileEntry::Kind::FrameNumber:
|
||||
if (sample.isSome()) {
|
||||
sample->mFrameNumber = Some(entry.mTagInt);
|
||||
}
|
||||
break;
|
||||
case 's':
|
||||
case ProfileEntry::Kind::Sample:
|
||||
{
|
||||
// end the previous sample if there was one
|
||||
if (sample.isSome()) {
|
||||
|
@ -660,49 +643,51 @@ void ProfileBuffer::StreamSamplesToJSON(SpliceableJSONWriter& aWriter, int aThre
|
|||
|
||||
int framePos = (readPos + 1) % mEntrySize;
|
||||
ProfileEntry frame = mEntries[framePos];
|
||||
while (framePos != mWritePos && frame.mTagName != 's' && frame.mTagName != 'T') {
|
||||
while (framePos != mWritePos && !frame.isSample() && !frame.isThreadId()) {
|
||||
int incBy = 1;
|
||||
frame = mEntries[framePos];
|
||||
|
||||
// Read ahead to the next tag, if it's a 'd' tag process it now
|
||||
// Read ahead to the next tag, if it's an EmbeddedString
|
||||
// tag process it now
|
||||
const char* tagStringData = frame.mTagData;
|
||||
int readAheadPos = (framePos + 1) % mEntrySize;
|
||||
// Make sure the string is always null terminated if it fills up
|
||||
// DYNAMIC_MAX_STRING-2
|
||||
tagBuff[DYNAMIC_MAX_STRING-1] = '\0';
|
||||
|
||||
if (readAheadPos != mWritePos && mEntries[readAheadPos].mTagName == 'd') {
|
||||
if (readAheadPos != mWritePos && mEntries[readAheadPos].isEmbeddedString()) {
|
||||
tagStringData = processDynamicTag(framePos, &incBy, tagBuff.get());
|
||||
}
|
||||
|
||||
// Write one frame. It can have either
|
||||
// 1. only location - 'l' containing a memory address
|
||||
// 2. location and line number - 'c' followed by 'd's,
|
||||
// an optional 'n' and an optional 'y'
|
||||
// 3. a JIT return address - 'j' containing native code address
|
||||
if (frame.mTagName == 'l') {
|
||||
// 1. only location - a NativeLeafAddr containing a memory address
|
||||
// 2. location and line number - a CodeLocation followed by
|
||||
// EmbeddedStrings, an optional LineNumber and an
|
||||
// optional Category
|
||||
// 3. a JitReturnAddress containing a native code address
|
||||
if (frame.isNativeLeafAddr()) {
|
||||
// Bug 753041
|
||||
// We need a double cast here to tell GCC that we don't want to sign
|
||||
// extend 32-bit addresses starting with 0xFXXXXXX.
|
||||
unsigned long long pc = (unsigned long long)(uintptr_t)frame.mTagPtr;
|
||||
snprintf(tagBuff.get(), DYNAMIC_MAX_STRING, "%#llx", pc);
|
||||
stack.AppendFrame(UniqueStacks::OnStackFrameKey(tagBuff.get()));
|
||||
} else if (frame.mTagName == 'c') {
|
||||
} else if (frame.isCodeLocation()) {
|
||||
UniqueStacks::OnStackFrameKey frameKey(tagStringData);
|
||||
readAheadPos = (framePos + incBy) % mEntrySize;
|
||||
if (readAheadPos != mWritePos &&
|
||||
mEntries[readAheadPos].mTagName == 'n') {
|
||||
mEntries[readAheadPos].isLineNumber()) {
|
||||
frameKey.mLine = Some((unsigned) mEntries[readAheadPos].mTagInt);
|
||||
incBy++;
|
||||
}
|
||||
readAheadPos = (framePos + incBy) % mEntrySize;
|
||||
if (readAheadPos != mWritePos &&
|
||||
mEntries[readAheadPos].mTagName == 'y') {
|
||||
mEntries[readAheadPos].isCategory()) {
|
||||
frameKey.mCategory = Some((unsigned) mEntries[readAheadPos].mTagInt);
|
||||
incBy++;
|
||||
}
|
||||
stack.AppendFrame(frameKey);
|
||||
} else if (frame.mTagName == 'J') {
|
||||
} else if (frame.isJitReturnAddr()) {
|
||||
// A JIT frame may expand to multiple frames due to inlining.
|
||||
void* pc = frame.mTagPtr;
|
||||
unsigned depth = aUniqueStacks.LookupJITFrameDepth(pc);
|
||||
|
@ -723,7 +708,9 @@ void ProfileBuffer::StreamSamplesToJSON(SpliceableJSONWriter& aWriter, int aThre
|
|||
sample->mStack = stack.GetOrAddIndex();
|
||||
break;
|
||||
}
|
||||
}
|
||||
default:
|
||||
break;
|
||||
} /* switch (entry.kind()) */
|
||||
}
|
||||
readPos = (readPos + 1) % mEntrySize;
|
||||
}
|
||||
|
@ -739,9 +726,9 @@ void ProfileBuffer::StreamMarkersToJSON(SpliceableJSONWriter& aWriter, int aThre
|
|||
int currentThreadID = -1;
|
||||
while (readPos != mWritePos) {
|
||||
ProfileEntry entry = mEntries[readPos];
|
||||
if (entry.mTagName == 'T') {
|
||||
if (entry.isThreadId()) {
|
||||
currentThreadID = entry.mTagInt;
|
||||
} else if (currentThreadID == aThreadId && entry.mTagName == 'm') {
|
||||
} else if (currentThreadID == aThreadId && entry.isMarker()) {
|
||||
const ProfilerMarker* marker = entry.getMarker();
|
||||
if (marker->GetTime() >= aSinceTime) {
|
||||
entry.getMarker()->StreamJSON(aWriter, aUniqueStacks);
|
||||
|
@ -759,7 +746,7 @@ int ProfileBuffer::FindLastSampleOfThread(int aThreadId)
|
|||
readPos != (mReadPos + mEntrySize - 1) % mEntrySize;
|
||||
readPos = (readPos + mEntrySize - 1) % mEntrySize) {
|
||||
ProfileEntry entry = mEntries[readPos];
|
||||
if (entry.mTagName == 'T' && entry.mTagInt == aThreadId) {
|
||||
if (entry.isThreadId() && entry.mTagInt == aThreadId) {
|
||||
return readPos;
|
||||
}
|
||||
}
|
||||
|
@ -774,7 +761,7 @@ void ProfileBuffer::DuplicateLastSample(int aThreadId)
|
|||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mEntries[lastSampleStartPos].mTagName == 'T');
|
||||
MOZ_ASSERT(mEntries[lastSampleStartPos].isThreadId());
|
||||
|
||||
addTag(mEntries[lastSampleStartPos]);
|
||||
|
||||
|
@ -782,20 +769,19 @@ void ProfileBuffer::DuplicateLastSample(int aThreadId)
|
|||
for (int readPos = (lastSampleStartPos + 1) % mEntrySize;
|
||||
readPos != mWritePos;
|
||||
readPos = (readPos + 1) % mEntrySize) {
|
||||
switch (mEntries[readPos].mTagName) {
|
||||
case 'T':
|
||||
switch (mEntries[readPos].kind()) {
|
||||
case ProfileEntry::Kind::ThreadId:
|
||||
// We're done.
|
||||
return;
|
||||
case 't':
|
||||
case ProfileEntry::Kind::Time:
|
||||
// Copy with new time
|
||||
addTag(ProfileEntry('t', (mozilla::TimeStamp::Now() - sStartTime).ToMilliseconds()));
|
||||
addTag(ProfileEntry::Time((mozilla::TimeStamp::Now() - sStartTime).ToMilliseconds()));
|
||||
break;
|
||||
case 'm':
|
||||
case ProfileEntry::Kind::Marker:
|
||||
// Don't copy markers
|
||||
break;
|
||||
// Copy anything else we don't know about
|
||||
// L, B, S, c, s, d, l, f, h, r, t, p
|
||||
default:
|
||||
// Copy anything else we don't know about
|
||||
addTag(mEntries[readPos]);
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -25,6 +25,22 @@
|
|||
#include "mozilla/HashFunctions.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
|
||||
#define PROFILE_ENTRY_KIND_LIST(_) \
|
||||
_(Category, int) \
|
||||
_(CodeLocation, const char *) \
|
||||
_(EmbeddedString, void *) \
|
||||
_(FrameNumber, int) \
|
||||
_(JitReturnAddr, void *) \
|
||||
_(LineNumber, int) \
|
||||
_(NativeLeafAddr, void *) \
|
||||
_(Marker, ProfilerMarker *) \
|
||||
_(ResidentMemory, double) \
|
||||
_(Responsiveness, double) \
|
||||
_(Sample, const char *) \
|
||||
_(ThreadId, int) \
|
||||
_(Time, double) \
|
||||
_(UnsharedMemory, double)
|
||||
|
||||
// NB: Packing this structure has been shown to cause SIGBUS issues on ARM.
|
||||
#ifndef __arm__
|
||||
#pragma pack(push, 1)
|
||||
|
@ -33,28 +49,46 @@
|
|||
class ProfileEntry
|
||||
{
|
||||
public:
|
||||
enum class Kind : uint8_t {
|
||||
INVALID = 0,
|
||||
# define DEF_ENUM_(k, t) k,
|
||||
PROFILE_ENTRY_KIND_LIST(DEF_ENUM_)
|
||||
# undef DEF_ENUM_
|
||||
LIMIT
|
||||
};
|
||||
|
||||
ProfileEntry();
|
||||
|
||||
private:
|
||||
// aTagData must not need release (i.e. be a string from the text segment)
|
||||
ProfileEntry(char aTagName, const char *aTagData);
|
||||
ProfileEntry(char aTagName, void *aTagPtr);
|
||||
ProfileEntry(char aTagName, ProfilerMarker *aTagMarker);
|
||||
ProfileEntry(char aTagName, double aTagDouble);
|
||||
ProfileEntry(char aTagName, uintptr_t aTagOffset);
|
||||
ProfileEntry(char aTagName, Address aTagAddress);
|
||||
ProfileEntry(char aTagName, int aTagLine);
|
||||
ProfileEntry(char aTagName, char aTagChar);
|
||||
bool is_ent_hint(char hintChar);
|
||||
bool is_ent_hint();
|
||||
bool is_ent(char tagName);
|
||||
void* get_tagPtr();
|
||||
ProfileEntry(Kind aKind, const char *aTagData);
|
||||
ProfileEntry(Kind aKind, void *aTagPtr);
|
||||
ProfileEntry(Kind aKind, ProfilerMarker *aTagMarker);
|
||||
ProfileEntry(Kind aKind, double aTagDouble);
|
||||
ProfileEntry(Kind aKind, uintptr_t aTagOffset);
|
||||
ProfileEntry(Kind aKind, Address aTagAddress);
|
||||
ProfileEntry(Kind aKind, int aTagLine);
|
||||
ProfileEntry(Kind aKind, char aTagChar);
|
||||
|
||||
public:
|
||||
# define DEF_MAKE_(k, t) \
|
||||
static ProfileEntry k(t val) { return ProfileEntry(Kind::k, val); }
|
||||
PROFILE_ENTRY_KIND_LIST(DEF_MAKE_)
|
||||
# undef DEF_MAKE_
|
||||
|
||||
Kind kind() const { return mKind; }
|
||||
bool hasKind(Kind k) const { return kind() == k; }
|
||||
|
||||
# define DEF_METHODS_(k, t) \
|
||||
bool is##k() const { return hasKind(Kind::k); }
|
||||
PROFILE_ENTRY_KIND_LIST(DEF_METHODS_)
|
||||
# undef DEF_METHODS_
|
||||
|
||||
const ProfilerMarker* getMarker() {
|
||||
MOZ_ASSERT(mTagName == 'm');
|
||||
MOZ_ASSERT(isMarker());
|
||||
return mTagMarker;
|
||||
}
|
||||
|
||||
char getTagName() const { return mTagName; }
|
||||
|
||||
private:
|
||||
FRIEND_TEST(ThreadProfile, InsertOneTag);
|
||||
FRIEND_TEST(ThreadProfile, InsertOneTagWithTinyBuffer);
|
||||
|
@ -73,7 +107,7 @@ private:
|
|||
int mTagInt;
|
||||
char mTagChar;
|
||||
};
|
||||
char mTagName;
|
||||
Kind mKind;
|
||||
};
|
||||
|
||||
#ifndef __arm__
|
||||
|
|
|
@ -767,9 +767,9 @@ void PseudoStack::flushSamplerOnJSShutdown()
|
|||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static
|
||||
void addDynamicTag(ThreadInfo& aInfo, char aTagName, const char* aStr)
|
||||
void addDynamicCodeLocationTag(ThreadInfo& aInfo, const char* aStr)
|
||||
{
|
||||
aInfo.addTag(ProfileEntry(aTagName, ""));
|
||||
aInfo.addTag(ProfileEntry::CodeLocation(""));
|
||||
// Add one to store the null termination
|
||||
size_t strLen = strlen(aStr) + 1;
|
||||
for (size_t j = 0; j < strLen;) {
|
||||
|
@ -782,7 +782,7 @@ void addDynamicTag(ThreadInfo& aInfo, char aTagName, const char* aStr)
|
|||
memcpy(text, &aStr[j], len);
|
||||
j += sizeof(void*)/sizeof(char);
|
||||
// Cast to *((void**) to pass the text data to a void*
|
||||
aInfo.addTag(ProfileEntry('d', *((void**)(&text[0]))));
|
||||
aInfo.addTag(ProfileEntry::EmbeddedString(*((void**)(&text[0]))));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -797,14 +797,14 @@ void addPseudoEntry(volatile StackEntry& entry, ThreadInfo& aInfo,
|
|||
|
||||
int lineno = -1;
|
||||
|
||||
// First entry has tagName 's' (start)
|
||||
// First entry has kind CodeLocation
|
||||
// Check for magic pointer bit 1 to indicate copy
|
||||
const char* sampleLabel = entry.label();
|
||||
if (entry.isCopyLabel()) {
|
||||
// Store the string using 1 or more 'd' (dynamic) tags
|
||||
// Store the string using 1 or more EmbeddedString tags
|
||||
// that will happen to the preceding tag
|
||||
|
||||
addDynamicTag(aInfo, 'c', sampleLabel);
|
||||
addDynamicCodeLocationTag(aInfo, sampleLabel);
|
||||
if (entry.isJs()) {
|
||||
JSScript* script = entry.script();
|
||||
if (script) {
|
||||
|
@ -827,7 +827,7 @@ void addPseudoEntry(volatile StackEntry& entry, ThreadInfo& aInfo,
|
|||
lineno = entry.line();
|
||||
}
|
||||
} else {
|
||||
aInfo.addTag(ProfileEntry('c', sampleLabel));
|
||||
aInfo.addTag(ProfileEntry::CodeLocation(sampleLabel));
|
||||
|
||||
// XXX: Bug 1010578. Don't assume a CPP entry and try to get the
|
||||
// line for js entries as well.
|
||||
|
@ -837,7 +837,7 @@ void addPseudoEntry(volatile StackEntry& entry, ThreadInfo& aInfo,
|
|||
}
|
||||
|
||||
if (lineno != -1) {
|
||||
aInfo.addTag(ProfileEntry('n', lineno));
|
||||
aInfo.addTag(ProfileEntry::LineNumber(lineno));
|
||||
}
|
||||
|
||||
uint32_t category = entry.category();
|
||||
|
@ -845,7 +845,7 @@ void addPseudoEntry(volatile StackEntry& entry, ThreadInfo& aInfo,
|
|||
MOZ_ASSERT(!(category & StackEntry::FRAME_LABEL_COPY));
|
||||
|
||||
if (category) {
|
||||
aInfo.addTag(ProfileEntry('y', (int)category));
|
||||
aInfo.addTag(ProfileEntry::Category((int)category));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -928,7 +928,7 @@ mergeStacksIntoProfile(ThreadInfo& aInfo, TickSample* aSample,
|
|||
}
|
||||
|
||||
// Start the sample with a root entry.
|
||||
aInfo.addTag(ProfileEntry('s', "(root)"));
|
||||
aInfo.addTag(ProfileEntry::Sample("(root)"));
|
||||
|
||||
// While the pseudo-stack array is ordered oldest-to-youngest, the JS and
|
||||
// native arrays are ordered youngest-to-oldest. We must add frames to
|
||||
|
@ -1012,7 +1012,7 @@ mergeStacksIntoProfile(ThreadInfo& aInfo, TickSample* aSample,
|
|||
|
||||
// Stringifying non-wasm JIT frames is delayed until streaming
|
||||
// time. To re-lookup the entry in the JitcodeGlobalTable, we need to
|
||||
// store the JIT code address ('J') in the circular buffer.
|
||||
// store the JIT code address (OptInfoAddr) in the circular buffer.
|
||||
//
|
||||
// Note that we cannot do this when we are sychronously sampling the
|
||||
// current thread; that is, when called from profiler_get_backtrace. The
|
||||
|
@ -1020,16 +1020,16 @@ mergeStacksIntoProfile(ThreadInfo& aInfo, TickSample* aSample,
|
|||
// amount of time, such as in nsRefreshDriver. Problematically, the
|
||||
// stored backtrace may be alive across a GC during which the profiler
|
||||
// itself is disabled. In that case, the JS engine is free to discard
|
||||
// its JIT code. This means that if we inserted such 'J' entries into
|
||||
// the buffer, nsRefreshDriver would now be holding on to a backtrace
|
||||
// with stale JIT code return addresses.
|
||||
// its JIT code. This means that if we inserted such OptInfoAddr entries
|
||||
// into the buffer, nsRefreshDriver would now be holding on to a
|
||||
// backtrace with stale JIT code return addresses.
|
||||
if (aSample->isSamplingCurrentThread ||
|
||||
jsFrame.kind == JS::ProfilingFrameIterator::Frame_Wasm) {
|
||||
addDynamicTag(aInfo, 'c', jsFrame.label);
|
||||
addDynamicCodeLocationTag(aInfo, jsFrame.label);
|
||||
} else {
|
||||
MOZ_ASSERT(jsFrame.kind == JS::ProfilingFrameIterator::Frame_Ion ||
|
||||
jsFrame.kind == JS::ProfilingFrameIterator::Frame_Baseline);
|
||||
aInfo.addTag(ProfileEntry('J', jsFrames[jsIndex].returnAddress));
|
||||
aInfo.addTag(ProfileEntry::JitReturnAddr(jsFrames[jsIndex].returnAddress));
|
||||
}
|
||||
|
||||
jsIndex--;
|
||||
|
@ -1041,7 +1041,7 @@ mergeStacksIntoProfile(ThreadInfo& aInfo, TickSample* aSample,
|
|||
if (nativeStackAddr) {
|
||||
MOZ_ASSERT(nativeIndex >= 0);
|
||||
aInfo
|
||||
.addTag(ProfileEntry('l', (void*)aNativeStack.pc_array[nativeIndex]));
|
||||
.addTag(ProfileEntry::NativeLeafAddr((void*)aNativeStack.pc_array[nativeIndex]));
|
||||
}
|
||||
if (nativeIndex >= 0) {
|
||||
nativeIndex--;
|
||||
|
@ -1299,7 +1299,7 @@ doSampleStackTrace(ThreadInfo& aInfo, TickSample* aSample,
|
|||
|
||||
#ifdef ENABLE_LEAF_DATA
|
||||
if (aSample && aAddLeafAddresses) {
|
||||
aInfo.addTag(ProfileEntry('l', (void*)aSample->pc));
|
||||
aInfo.addTag(ProfileEntry::NativeLeafAddr((void*)aSample->pc));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
@ -1316,10 +1316,10 @@ Sampler::InplaceTick(TickSample* sample)
|
|||
{
|
||||
ThreadInfo& currThreadInfo = *sample->threadInfo;
|
||||
|
||||
currThreadInfo.addTag(ProfileEntry('T', currThreadInfo.ThreadId()));
|
||||
currThreadInfo.addTag(ProfileEntry::ThreadId(currThreadInfo.ThreadId()));
|
||||
|
||||
mozilla::TimeDuration delta = sample->timestamp - sStartTime;
|
||||
currThreadInfo.addTag(ProfileEntry('t', delta.ToMilliseconds()));
|
||||
currThreadInfo.addTag(ProfileEntry::Time(delta.ToMilliseconds()));
|
||||
|
||||
PseudoStack* stack = currThreadInfo.Stack();
|
||||
|
||||
|
@ -1341,27 +1341,27 @@ Sampler::InplaceTick(TickSample* sample)
|
|||
while (pendingMarkersList && pendingMarkersList->peek()) {
|
||||
ProfilerMarker* marker = pendingMarkersList->popHead();
|
||||
currThreadInfo.addStoredMarker(marker);
|
||||
currThreadInfo.addTag(ProfileEntry('m', marker));
|
||||
currThreadInfo.addTag(ProfileEntry::Marker(marker));
|
||||
}
|
||||
}
|
||||
|
||||
if (currThreadInfo.GetThreadResponsiveness()->HasData()) {
|
||||
mozilla::TimeDuration delta = currThreadInfo.GetThreadResponsiveness()->GetUnresponsiveDuration(sample->timestamp);
|
||||
currThreadInfo.addTag(ProfileEntry('r', delta.ToMilliseconds()));
|
||||
currThreadInfo.addTag(ProfileEntry::Responsiveness(delta.ToMilliseconds()));
|
||||
}
|
||||
|
||||
// rssMemory is equal to 0 when we are not recording.
|
||||
if (sample->rssMemory != 0) {
|
||||
currThreadInfo.addTag(ProfileEntry('R', static_cast<double>(sample->rssMemory)));
|
||||
currThreadInfo.addTag(ProfileEntry::ResidentMemory(static_cast<double>(sample->rssMemory)));
|
||||
}
|
||||
|
||||
// ussMemory is equal to 0 when we are not recording.
|
||||
if (sample->ussMemory != 0) {
|
||||
currThreadInfo.addTag(ProfileEntry('U', static_cast<double>(sample->ussMemory)));
|
||||
currThreadInfo.addTag(ProfileEntry::UnsharedMemory(static_cast<double>(sample->ussMemory)));
|
||||
}
|
||||
|
||||
if (sLastFrameNumber != sFrameNumber) {
|
||||
currThreadInfo.addTag(ProfileEntry('f', sFrameNumber));
|
||||
currThreadInfo.addTag(ProfileEntry::FrameNumber(sFrameNumber));
|
||||
sLastFrameNumber = sFrameNumber;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,9 +23,9 @@ TEST(ThreadProfile, InsertOneTag) {
|
|||
Thread::tid_t tid = 1000;
|
||||
ThreadInfo info("testThread", tid, true, stack, nullptr);
|
||||
RefPtr<ProfileBuffer> pb = new ProfileBuffer(10);
|
||||
pb->addTag(ProfileEntry('t', 123.1));
|
||||
pb->addTag(ProfileEntry::Time(123.1));
|
||||
ASSERT_TRUE(pb->mEntries != nullptr);
|
||||
ASSERT_TRUE(pb->mEntries[pb->mReadPos].mTagName == 't');
|
||||
ASSERT_TRUE(pb->mEntries[pb->mReadPos].kind() == ProfileEntry::Kind::Time);
|
||||
ASSERT_TRUE(pb->mEntries[pb->mReadPos].mTagDouble == 123.1);
|
||||
}
|
||||
|
||||
|
@ -37,13 +37,13 @@ TEST(ThreadProfile, InsertTagsNoWrap) {
|
|||
RefPtr<ProfileBuffer> pb = new ProfileBuffer(100);
|
||||
int test_size = 50;
|
||||
for (int i = 0; i < test_size; i++) {
|
||||
pb->addTag(ProfileEntry('t', i));
|
||||
pb->addTag(ProfileEntry::Time(i));
|
||||
}
|
||||
ASSERT_TRUE(pb->mEntries != nullptr);
|
||||
int readPos = pb->mReadPos;
|
||||
while (readPos != pb->mWritePos) {
|
||||
ASSERT_TRUE(pb->mEntries[readPos].mTagName == 't');
|
||||
ASSERT_TRUE(pb->mEntries[readPos].mTagInt == readPos);
|
||||
ASSERT_TRUE(pb->mEntries[readPos].kind() == ProfileEntry::Kind::Time);
|
||||
ASSERT_TRUE(pb->mEntries[readPos].mTagDouble == readPos);
|
||||
readPos = (readPos + 1) % pb->mEntrySize;
|
||||
}
|
||||
}
|
||||
|
@ -59,15 +59,15 @@ TEST(ThreadProfile, InsertTagsWrap) {
|
|||
RefPtr<ProfileBuffer> pb = new ProfileBuffer(buffer_size);
|
||||
int test_size = 43;
|
||||
for (int i = 0; i < test_size; i++) {
|
||||
pb->addTag(ProfileEntry('t', i));
|
||||
pb->addTag(ProfileEntry::Time(i));
|
||||
}
|
||||
ASSERT_TRUE(pb->mEntries != nullptr);
|
||||
int readPos = pb->mReadPos;
|
||||
int ctr = 0;
|
||||
while (readPos != pb->mWritePos) {
|
||||
ASSERT_TRUE(pb->mEntries[readPos].mTagName == 't');
|
||||
ASSERT_TRUE(pb->mEntries[readPos].kind() == ProfileEntry::Kind::Time);
|
||||
// the first few tags were discarded when we wrapped
|
||||
ASSERT_TRUE(pb->mEntries[readPos].mTagInt == ctr + (test_size - tags));
|
||||
ASSERT_TRUE(pb->mEntries[readPos].mTagDouble == ctr + (test_size - tags));
|
||||
ctr++;
|
||||
readPos = (readPos + 1) % pb->mEntrySize;
|
||||
}
|
||||
|
|
|
@ -64,6 +64,19 @@ NS_IMETHODIMP nsDeviceContextSpecX::Init(nsIWidget *aWidget,
|
|||
if (!settings)
|
||||
return NS_ERROR_NO_INTERFACE;
|
||||
|
||||
bool toFile;
|
||||
settings->GetPrintToFile(&toFile);
|
||||
|
||||
bool toPrinter = !toFile && !aIsPrintPreview;
|
||||
if (!toPrinter) {
|
||||
double width, height;
|
||||
settings->GetEffectivePageSize(&width, &height);
|
||||
width /= TWIPS_PER_POINT_FLOAT;
|
||||
height /= TWIPS_PER_POINT_FLOAT;
|
||||
|
||||
settings->SetCocoaPaperSize(width, height);
|
||||
}
|
||||
|
||||
mPrintSession = settings->GetPMPrintSession();
|
||||
::PMRetain(mPrintSession);
|
||||
mPageFormat = settings->GetPMPageFormat();
|
||||
|
|
|
@ -9,19 +9,6 @@
|
|||
#include "nsPrintOptionsX.h"
|
||||
#include "nsPrintSettingsX.h"
|
||||
|
||||
// The constants for paper orientation were renamed in 10.9. __MAC_10_9 is
|
||||
// defined on OS X 10.9 and later. Although 10.8 and earlier are not supported
|
||||
// at this time, this allows for building on those older OS versions. The
|
||||
// values are consistent across OS versions so the rename does not affect
|
||||
// runtime, just compilation.
|
||||
#ifdef __MAC_10_9
|
||||
#define NS_PAPER_ORIENTATION_PORTRAIT (NSPaperOrientationPortrait)
|
||||
#define NS_PAPER_ORIENTATION_LANDSCAPE (NSPaperOrientationLandscape)
|
||||
#else
|
||||
#define NS_PAPER_ORIENTATION_PORTRAIT (NSPortraitOrientation)
|
||||
#define NS_PAPER_ORIENTATION_LANDSCAPE (NSLandscapeOrientation)
|
||||
#endif
|
||||
|
||||
using namespace mozilla::embedding;
|
||||
|
||||
nsPrintOptionsX::nsPrintOptionsX()
|
||||
|
|
|
@ -9,6 +9,19 @@
|
|||
#include "nsPrintSettingsImpl.h"
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
// The constants for paper orientation were renamed in 10.9. __MAC_10_9 is
|
||||
// defined on OS X 10.9 and later. Although 10.8 and earlier are not supported
|
||||
// at this time, this allows for building on those older OS versions. The
|
||||
// values are consistent across OS versions so the rename does not affect
|
||||
// runtime, just compilation.
|
||||
#ifdef __MAC_10_9
|
||||
#define NS_PAPER_ORIENTATION_PORTRAIT (NSPaperOrientationPortrait)
|
||||
#define NS_PAPER_ORIENTATION_LANDSCAPE (NSPaperOrientationLandscape)
|
||||
#else
|
||||
#define NS_PAPER_ORIENTATION_PORTRAIT (NSPortraitOrientation)
|
||||
#define NS_PAPER_ORIENTATION_LANDSCAPE (NSLandscapeOrientation)
|
||||
#endif
|
||||
|
||||
#define NS_PRINTSETTINGSX_IID \
|
||||
{ 0x0DF2FDBD, 0x906D, 0x4726, \
|
||||
{ 0x9E, 0x4D, 0xCF, 0xE0, 0x87, 0x8D, 0x70, 0x7C } }
|
||||
|
@ -51,8 +64,22 @@ public:
|
|||
void SetInchesScale(float aWidthScale, float aHeightScale);
|
||||
void GetInchesScale(float *aWidthScale, float *aHeightScale);
|
||||
|
||||
NS_IMETHOD SetPaperSizeUnit(int16_t aPaperSizeUnit) override;
|
||||
|
||||
NS_IMETHOD SetScaling(double aScaling) override;
|
||||
NS_IMETHOD SetToFileName(const char16_t * aToFileName) override;
|
||||
|
||||
NS_IMETHOD GetOrientation(int32_t *aOrientation) override;
|
||||
NS_IMETHOD SetOrientation(int32_t aOrientation) override;
|
||||
|
||||
NS_IMETHOD SetUnwriteableMarginTop(double aUnwriteableMarginTop) override;
|
||||
NS_IMETHOD SetUnwriteableMarginLeft(double aUnwriteableMarginLeft) override;
|
||||
NS_IMETHOD SetUnwriteableMarginBottom(double aUnwriteableMarginBottom) override;
|
||||
NS_IMETHOD SetUnwriteableMarginRight(double aUnwriteableMarginRight) override;
|
||||
|
||||
void SetAdjustedPaperSize(double aWidth, double aHeight);
|
||||
void GetAdjustedPaperSize(double *aWidth, double *aHeight);
|
||||
nsresult SetCocoaPaperSize(double aWidth, double aHeight);
|
||||
|
||||
protected:
|
||||
virtual ~nsPrintSettingsX();
|
||||
|
@ -63,6 +90,8 @@ protected:
|
|||
nsresult _Clone(nsIPrintSettings **_retval) override;
|
||||
nsresult _Assign(nsIPrintSettings *aPS) override;
|
||||
|
||||
int GetCocoaUnit(int16_t aGeckoUnit);
|
||||
|
||||
// The out param has a ref count of 1 on return so caller needs to PMRelase() when done.
|
||||
OSStatus CreateDefaultPageFormat(PMPrintSession aSession, PMPageFormat& outFormat);
|
||||
OSStatus CreateDefaultPrintSettings(PMPrintSession aSession, PMPrintSettings& outSettings);
|
||||
|
|
|
@ -254,8 +254,13 @@ NS_IMETHODIMP nsPrintSettingsX::SetPaperHeight(double aPaperHeight)
|
|||
NS_IMETHODIMP
|
||||
nsPrintSettingsX::GetEffectivePageSize(double *aWidth, double *aHeight)
|
||||
{
|
||||
if (kPaperSizeInches == GetCocoaUnit(mPaperSizeUnit)) {
|
||||
*aWidth = NS_INCHES_TO_TWIPS(mAdjustedPaperWidth / mWidthScale);
|
||||
*aHeight = NS_INCHES_TO_TWIPS(mAdjustedPaperHeight / mHeightScale);
|
||||
} else {
|
||||
*aWidth = NS_MILLIMETERS_TO_TWIPS(mAdjustedPaperWidth / mWidthScale);
|
||||
*aHeight = NS_MILLIMETERS_TO_TWIPS(mAdjustedPaperHeight / mHeightScale);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -270,3 +275,221 @@ void nsPrintSettingsX::GetAdjustedPaperSize(double *aWidth, double *aHeight)
|
|||
*aWidth = mAdjustedPaperWidth;
|
||||
*aHeight = mAdjustedPaperHeight;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsPrintSettingsX::SetPaperSizeUnit(int16_t aPaperSizeUnit)
|
||||
{
|
||||
mPaperSizeUnit = aPaperSizeUnit;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsPrintSettingsX::SetScaling(double aScaling)
|
||||
{
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
|
||||
|
||||
NSMutableDictionary* printInfoDict = [mPrintInfo dictionary];
|
||||
[printInfoDict setObject: [NSNumber numberWithFloat: aScaling]
|
||||
forKey: NSPrintScalingFactor];
|
||||
NSPrintInfo* newPrintInfo =
|
||||
[[NSPrintInfo alloc] initWithDictionary: printInfoDict];
|
||||
if (NS_WARN_IF(!newPrintInfo)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
SetCocoaPrintInfo(newPrintInfo);
|
||||
[newPrintInfo release];
|
||||
return NS_OK;
|
||||
|
||||
NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsPrintSettingsX::SetToFileName(const char16_t *aToFileName)
|
||||
{
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
|
||||
|
||||
NSMutableDictionary* printInfoDict = [mPrintInfo dictionary];
|
||||
nsString filename = nsDependentString(aToFileName);
|
||||
|
||||
NSURL* jobSavingURL =
|
||||
[NSURL fileURLWithPath: nsCocoaUtils::ToNSString(filename)];
|
||||
if (jobSavingURL) {
|
||||
[printInfoDict setObject: NSPrintSaveJob forKey: NSPrintJobDisposition];
|
||||
[printInfoDict setObject: jobSavingURL forKey: NSPrintJobSavingURL];
|
||||
}
|
||||
NSPrintInfo* newPrintInfo =
|
||||
[[NSPrintInfo alloc] initWithDictionary: printInfoDict];
|
||||
if (NS_WARN_IF(!newPrintInfo)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
SetCocoaPrintInfo(newPrintInfo);
|
||||
[newPrintInfo release];
|
||||
return NS_OK;
|
||||
|
||||
NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsPrintSettingsX::GetOrientation(int32_t *aOrientation)
|
||||
{
|
||||
if ([mPrintInfo orientation] == NS_PAPER_ORIENTATION_PORTRAIT) {
|
||||
*aOrientation = nsIPrintSettings::kPortraitOrientation;
|
||||
} else {
|
||||
*aOrientation = nsIPrintSettings::kLandscapeOrientation;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsPrintSettingsX::SetOrientation(int32_t aOrientation)
|
||||
{
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
|
||||
|
||||
NSMutableDictionary* printInfoDict = [mPrintInfo dictionary];
|
||||
if (aOrientation == nsIPrintSettings::kPortraitOrientation) {
|
||||
[printInfoDict setObject: [NSNumber numberWithInt: NS_PAPER_ORIENTATION_PORTRAIT]
|
||||
forKey: NSPrintOrientation];
|
||||
} else {
|
||||
[printInfoDict setObject: [NSNumber numberWithInt: NS_PAPER_ORIENTATION_LANDSCAPE]
|
||||
forKey: NSPrintOrientation];
|
||||
}
|
||||
NSPrintInfo* newPrintInfo =
|
||||
[[NSPrintInfo alloc] initWithDictionary: printInfoDict];
|
||||
if (NS_WARN_IF(!newPrintInfo)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
SetCocoaPrintInfo(newPrintInfo);
|
||||
[newPrintInfo release];
|
||||
return NS_OK;
|
||||
|
||||
NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsPrintSettingsX::SetUnwriteableMarginTop(double aUnwriteableMarginTop)
|
||||
{
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
|
||||
|
||||
nsPrintSettings::SetUnwriteableMarginTop(aUnwriteableMarginTop);
|
||||
NSMutableDictionary* printInfoDict = [mPrintInfo dictionary];
|
||||
[printInfoDict setObject : [NSNumber numberWithDouble: aUnwriteableMarginTop]
|
||||
forKey : NSPrintTopMargin];
|
||||
NSPrintInfo* newPrintInfo =
|
||||
[[NSPrintInfo alloc] initWithDictionary: printInfoDict];
|
||||
if (NS_WARN_IF(!newPrintInfo)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
SetCocoaPrintInfo(newPrintInfo);
|
||||
[newPrintInfo release];
|
||||
return NS_OK;
|
||||
|
||||
NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsPrintSettingsX::SetUnwriteableMarginLeft(double aUnwriteableMarginLeft)
|
||||
{
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
|
||||
|
||||
nsPrintSettings::SetUnwriteableMarginLeft(aUnwriteableMarginLeft);
|
||||
NSMutableDictionary* printInfoDict = [mPrintInfo dictionary];
|
||||
[printInfoDict setObject : [NSNumber numberWithDouble: aUnwriteableMarginLeft]
|
||||
forKey : NSPrintLeftMargin];
|
||||
NSPrintInfo* newPrintInfo =
|
||||
[[NSPrintInfo alloc] initWithDictionary: printInfoDict];
|
||||
if (NS_WARN_IF(!newPrintInfo)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
SetCocoaPrintInfo(newPrintInfo);
|
||||
[newPrintInfo release];
|
||||
return NS_OK;
|
||||
|
||||
NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsPrintSettingsX::SetUnwriteableMarginBottom(double aUnwriteableMarginBottom)
|
||||
{
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
|
||||
|
||||
nsPrintSettings::SetUnwriteableMarginBottom(aUnwriteableMarginBottom);
|
||||
NSMutableDictionary* printInfoDict = [mPrintInfo dictionary];
|
||||
[printInfoDict setObject : [NSNumber numberWithDouble: aUnwriteableMarginBottom]
|
||||
forKey : NSPrintBottomMargin];
|
||||
NSPrintInfo* newPrintInfo =
|
||||
[[NSPrintInfo alloc] initWithDictionary: printInfoDict];
|
||||
if (NS_WARN_IF(!newPrintInfo)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
SetCocoaPrintInfo(newPrintInfo);
|
||||
[newPrintInfo release];
|
||||
return NS_OK;
|
||||
|
||||
NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsPrintSettingsX::SetUnwriteableMarginRight(double aUnwriteableMarginRight)
|
||||
{
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
|
||||
|
||||
nsPrintSettings::SetUnwriteableMarginRight(aUnwriteableMarginRight);
|
||||
NSMutableDictionary* printInfoDict = [mPrintInfo dictionary];
|
||||
[printInfoDict setObject : [NSNumber numberWithDouble: aUnwriteableMarginRight]
|
||||
forKey : NSPrintRightMargin];
|
||||
NSPrintInfo* newPrintInfo =
|
||||
[[NSPrintInfo alloc] initWithDictionary: printInfoDict];
|
||||
if (NS_WARN_IF(!newPrintInfo)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
SetCocoaPrintInfo(newPrintInfo);
|
||||
[newPrintInfo release];
|
||||
return NS_OK;
|
||||
|
||||
NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
|
||||
}
|
||||
|
||||
int
|
||||
nsPrintSettingsX::GetCocoaUnit(int16_t aGeckoUnit)
|
||||
{
|
||||
if (aGeckoUnit == kPaperSizeMillimeters)
|
||||
return kPaperSizeMillimeters;
|
||||
else
|
||||
return kPaperSizeInches;
|
||||
}
|
||||
|
||||
nsresult nsPrintSettingsX::SetCocoaPaperSize(double aWidth, double aHeight)
|
||||
{
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
|
||||
|
||||
NSSize paperSize;
|
||||
NSMutableDictionary* printInfoDict = [mPrintInfo dictionary];
|
||||
if ([mPrintInfo orientation] == NS_PAPER_ORIENTATION_PORTRAIT) {
|
||||
// switch widths and heights
|
||||
paperSize = NSMakeSize(aWidth, aHeight);
|
||||
[printInfoDict setObject: [NSValue valueWithSize: paperSize]
|
||||
forKey: NSPrintPaperSize];
|
||||
} else {
|
||||
paperSize = NSMakeSize(aHeight, aWidth);
|
||||
[printInfoDict setObject: [NSValue valueWithSize: paperSize]
|
||||
forKey: NSPrintPaperSize];
|
||||
}
|
||||
NSPrintInfo* newPrintInfo =
|
||||
[[NSPrintInfo alloc] initWithDictionary: printInfoDict];
|
||||
if (NS_WARN_IF(!newPrintInfo)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
SetCocoaPrintInfo(newPrintInfo);
|
||||
[newPrintInfo release];
|
||||
return NS_OK;
|
||||
|
||||
NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
|
||||
}
|
||||
|
|
|
@ -11,11 +11,6 @@
|
|||
* on machines with several LSPs.
|
||||
*/
|
||||
|
||||
#if _WIN32_WINNT < 0x0600
|
||||
// Redefining _WIN32_WINNT for some Vista APIs that we call
|
||||
#undef _WIN32_WINNT
|
||||
#define _WIN32_WINNT 0x0600
|
||||
#endif
|
||||
#include "nsICrashReporter.h"
|
||||
#include "nsISupportsImpl.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче