зеркало из https://github.com/mozilla/gecko-dev.git
merge mozilla-inbound to mozilla-central a=merge
This commit is contained in:
Коммит
f52809d2cb
2
CLOBBER
2
CLOBBER
|
@ -22,4 +22,4 @@
|
|||
# changes to stick? As of bug 928195, this shouldn't be necessary! Please
|
||||
# don't change CLOBBER for WebIDL changes any more.
|
||||
|
||||
Bug 946065 needs a CLOBBER
|
||||
Backout of bug 1087560 needed a CLOBBER
|
||||
|
|
|
@ -1082,8 +1082,9 @@ nsSHistory::GloballyEvictContentViewers()
|
|||
|
||||
nsTArray<TransactionAndDistance> transactions;
|
||||
|
||||
nsSHistory *shist = static_cast<nsSHistory*>(PR_LIST_HEAD(&gSHistoryList));
|
||||
while (shist != &gSHistoryList) {
|
||||
PRCList* listEntry = PR_LIST_HEAD(&gSHistoryList);
|
||||
while (listEntry != &gSHistoryList) {
|
||||
nsSHistory* shist = static_cast<nsSHistory*>(listEntry);
|
||||
|
||||
// Maintain a list of the transactions which have viewers and belong to
|
||||
// this particular shist object. We'll add this list to the global list,
|
||||
|
@ -1142,7 +1143,7 @@ nsSHistory::GloballyEvictContentViewers()
|
|||
// We've found all the transactions belonging to shist which have viewers.
|
||||
// Add those transactions to our global list and move on.
|
||||
transactions.AppendElements(shTransactions);
|
||||
shist = static_cast<nsSHistory*>(PR_NEXT_LINK(shist));
|
||||
listEntry = PR_NEXT_LINK(shist);
|
||||
}
|
||||
|
||||
// We now have collected all cached content viewers. First check that we
|
||||
|
|
|
@ -2551,6 +2551,7 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument,
|
|||
JS::Rooted<JSObject*> obj(cx, GetWrapperPreserveColor());
|
||||
|
||||
js::SetProxyExtra(obj, 0, js::PrivateValue(nullptr));
|
||||
js::SetProxyExtra(outerObject, 0, js::PrivateValue(nullptr));
|
||||
|
||||
outerObject = xpc::TransplantObject(cx, obj, outerObject);
|
||||
if (!outerObject) {
|
||||
|
|
|
@ -174,6 +174,24 @@ UnwrapDOMObject(JSObject* obj)
|
|||
return static_cast<T*>(val.toPrivate());
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline T*
|
||||
UnwrapPossiblyNotInitializedDOMObject(JSObject* obj)
|
||||
{
|
||||
// This is used by the OjectMoved JSClass hook which can be called before
|
||||
// JS_NewObject has returned and so before we have a chance to set
|
||||
// DOM_OBJECT_SLOT to anything useful.
|
||||
|
||||
MOZ_ASSERT(IsDOMClass(js::GetObjectClass(obj)),
|
||||
"Don't pass non-DOM objects to this function");
|
||||
|
||||
JS::Value val = js::GetReservedOrProxyPrivateSlot(obj, DOM_OBJECT_SLOT);
|
||||
if (val.isUndefined()) {
|
||||
return nullptr;
|
||||
}
|
||||
return static_cast<T*>(val.toPrivate());
|
||||
}
|
||||
|
||||
inline const DOMJSClass*
|
||||
GetDOMClass(const js::Class* clasp)
|
||||
{
|
||||
|
|
|
@ -1528,7 +1528,7 @@ class CGAbstractClassHook(CGAbstractStaticMethod):
|
|||
args)
|
||||
|
||||
def definition_body_prologue(self):
|
||||
return ("%s* self = UnwrapDOMObject<%s>(obj);\n" %
|
||||
return ("%s* self = UnwrapPossiblyNotInitializedDOMObject<%s>(obj);\n" %
|
||||
(self.descriptor.nativeType, self.descriptor.nativeType))
|
||||
|
||||
def definition_body(self):
|
||||
|
|
|
@ -669,6 +669,12 @@ CreateOffscreen(GLContext* gl,
|
|||
if (!baseCaps.alpha)
|
||||
baseCaps.premultAlpha = true;
|
||||
|
||||
if (gl->IsANGLE()) {
|
||||
// We can't use no-alpha formats on ANGLE yet because of:
|
||||
// https://code.google.com/p/angleproject/issues/detail?id=764
|
||||
baseCaps.alpha = true;
|
||||
}
|
||||
|
||||
// we should really have this behind a
|
||||
// |gfxPlatform::GetPlatform()->GetScreenDepth() == 16| check, but
|
||||
// for now it's just behind a pref for testing/evaluation.
|
||||
|
@ -903,17 +909,24 @@ WebGLContext::SetDimensions(int32_t sWidth, int32_t sHeight)
|
|||
// increment the generation number
|
||||
++mGeneration;
|
||||
|
||||
MakeContextCurrent();
|
||||
|
||||
gl->fViewport(0, 0, mWidth, mHeight);
|
||||
mViewportWidth = mWidth;
|
||||
mViewportHeight = mHeight;
|
||||
// Update our internal stuff:
|
||||
if (gl->WorkAroundDriverBugs()) {
|
||||
if (!mOptions.alpha && gl->Caps().alpha) {
|
||||
mNeedsFakeNoAlpha = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Update mOptions.
|
||||
mOptions.depth = gl->Caps().depth;
|
||||
mOptions.stencil = gl->Caps().stencil;
|
||||
mOptions.antialias = gl->Caps().antialias;
|
||||
|
||||
MakeContextCurrent();
|
||||
|
||||
gl->fViewport(0, 0, mWidth, mHeight);
|
||||
mViewportWidth = mWidth;
|
||||
mViewportHeight = mHeight;
|
||||
|
||||
// Make sure that we clear this out, otherwise
|
||||
// we'll end up displaying random memory
|
||||
gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, 0);
|
||||
|
@ -929,18 +942,13 @@ WebGLContext::SetDimensions(int32_t sWidth, int32_t sHeight)
|
|||
mShouldPresent = true;
|
||||
|
||||
MOZ_ASSERT(gl->Caps().color);
|
||||
MOZ_ASSERT(gl->Caps().alpha == mOptions.alpha);
|
||||
MOZ_ASSERT_IF(!mNeedsFakeNoAlpha, gl->Caps().alpha == mOptions.alpha);
|
||||
MOZ_ASSERT_IF(mNeedsFakeNoAlpha, !mOptions.alpha && gl->Caps().alpha);
|
||||
MOZ_ASSERT(gl->Caps().depth == mOptions.depth);
|
||||
MOZ_ASSERT(gl->Caps().stencil == mOptions.stencil);
|
||||
MOZ_ASSERT(gl->Caps().antialias == mOptions.antialias);
|
||||
MOZ_ASSERT(gl->Caps().preserve == mOptions.preserveDrawingBuffer);
|
||||
|
||||
if (gl->WorkAroundDriverBugs() && gl->IsANGLE()) {
|
||||
if (!mOptions.alpha) {
|
||||
mNeedsFakeNoAlpha = true;
|
||||
}
|
||||
}
|
||||
|
||||
AssertCachedBindings();
|
||||
AssertCachedState();
|
||||
|
||||
|
@ -1252,12 +1260,10 @@ WebGLContext::GetContextAttributes(Nullable<dom::WebGLContextAttributes> &retval
|
|||
|
||||
dom::WebGLContextAttributes& result = retval.SetValue();
|
||||
|
||||
const PixelBufferFormat& format = gl->GetPixelFormat();
|
||||
|
||||
result.mAlpha.Construct(format.alpha > 0);
|
||||
result.mDepth = format.depth > 0;
|
||||
result.mStencil = format.stencil > 0;
|
||||
result.mAntialias = format.samples > 1;
|
||||
result.mAlpha.Construct(mOptions.alpha);
|
||||
result.mDepth = mOptions.depth;
|
||||
result.mStencil = mOptions.stencil;
|
||||
result.mAntialias = mOptions.antialias;
|
||||
result.mPremultipliedAlpha = mOptions.premultipliedAlpha;
|
||||
result.mPreserveDrawingBuffer = mOptions.preserveDrawingBuffer;
|
||||
}
|
||||
|
@ -1845,7 +1851,6 @@ bool WebGLContext::TexImageFromVideoElement(const TexImageTarget texImageTarget,
|
|||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
WebGLContext::ScopedMaskWorkaround::ScopedMaskWorkaround(WebGLContext& webgl)
|
||||
: mWebGL(webgl)
|
||||
, mNeedsChange(NeedsChange(webgl))
|
||||
|
|
|
@ -2104,6 +2104,58 @@ WebGLContext::PixelStorei(GLenum pname, GLint param)
|
|||
}
|
||||
}
|
||||
|
||||
// `width` in pixels.
|
||||
// `stride` in bytes.
|
||||
static bool
|
||||
SetFullAlpha(void* data, GLenum format, GLenum type, size_t width,
|
||||
size_t height, size_t stride)
|
||||
{
|
||||
if (format == LOCAL_GL_ALPHA && type == LOCAL_GL_UNSIGNED_BYTE) {
|
||||
// Just memset the rows.
|
||||
for (size_t j = 0; j < height; ++j) {
|
||||
uint8_t* row = static_cast<uint8_t*>(data) + j*stride;
|
||||
memset(row, 0xff, width);
|
||||
row += stride;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (format == LOCAL_GL_RGBA && type == LOCAL_GL_UNSIGNED_BYTE) {
|
||||
for (size_t j = 0; j < height; ++j) {
|
||||
uint8_t* row = static_cast<uint8_t*>(data) + j*stride;
|
||||
|
||||
uint8_t* pAlpha = row + 3;
|
||||
uint8_t* pAlphaEnd = pAlpha + 4*width;
|
||||
while (pAlpha != pAlphaEnd) {
|
||||
*pAlpha = 0xff;
|
||||
pAlpha += 4;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (format == LOCAL_GL_RGBA && type == LOCAL_GL_FLOAT) {
|
||||
for (size_t j = 0; j < height; ++j) {
|
||||
uint8_t* rowBytes = static_cast<uint8_t*>(data) + j*stride;
|
||||
float* row = reinterpret_cast<float*>(rowBytes);
|
||||
|
||||
float* pAlpha = row + 3;
|
||||
float* pAlphaEnd = pAlpha + 4*width;
|
||||
while (pAlpha != pAlphaEnd) {
|
||||
*pAlpha = 1.0f;
|
||||
pAlpha += 4;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(false, "Unhandled case, how'd we get here?");
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
WebGLContext::ReadPixels(GLint x, GLint y, GLsizei width,
|
||||
GLsizei height, GLenum format,
|
||||
|
@ -2347,61 +2399,25 @@ WebGLContext::ReadPixels(GLint x, GLint y, GLsizei width,
|
|||
|
||||
// if we're reading alpha, we may need to do fixup. Note that we don't allow
|
||||
// GL_ALPHA to readpixels currently, but we had the code written for it already.
|
||||
if (format == LOCAL_GL_ALPHA ||
|
||||
format == LOCAL_GL_RGBA)
|
||||
{
|
||||
bool needAlphaFixup;
|
||||
if (mBoundFramebuffer) {
|
||||
needAlphaFixup = !mBoundFramebuffer->ColorAttachment(0).HasAlpha();
|
||||
} else {
|
||||
needAlphaFixup = gl->GetPixelFormat().alpha == 0;
|
||||
}
|
||||
|
||||
if (needAlphaFixup) {
|
||||
if (format == LOCAL_GL_ALPHA && type == LOCAL_GL_UNSIGNED_BYTE) {
|
||||
// this is easy; it's an 0xff memset per row
|
||||
uint8_t *row = static_cast<uint8_t*>(data);
|
||||
for (GLint j = 0; j < height; ++j) {
|
||||
memset(row, 0xff, checked_plainRowSize.value());
|
||||
row += checked_alignedRowSize.value();
|
||||
}
|
||||
} else if (format == LOCAL_GL_RGBA && type == LOCAL_GL_UNSIGNED_BYTE) {
|
||||
// this is harder, we need to just set the alpha byte here
|
||||
uint8_t *row = static_cast<uint8_t*>(data);
|
||||
for (GLint j = 0; j < height; ++j) {
|
||||
uint8_t *rowp = row;
|
||||
#if MOZ_LITTLE_ENDIAN
|
||||
// offset to get the alpha byte; we're always going to
|
||||
// move by 4 bytes
|
||||
rowp += 3;
|
||||
#endif
|
||||
uint8_t *endrowp = rowp + 4 * width;
|
||||
while (rowp != endrowp) {
|
||||
*rowp = 0xff;
|
||||
rowp += 4;
|
||||
}
|
||||
const bool formatHasAlpha = format == LOCAL_GL_ALPHA ||
|
||||
format == LOCAL_GL_RGBA;
|
||||
if (!formatHasAlpha)
|
||||
return;
|
||||
|
||||
row += checked_alignedRowSize.value();
|
||||
}
|
||||
} else if (format == LOCAL_GL_RGBA && type == LOCAL_GL_FLOAT) {
|
||||
float* row = static_cast<float*>(data);
|
||||
bool needAlphaFilled;
|
||||
if (mBoundFramebuffer) {
|
||||
needAlphaFilled = !mBoundFramebuffer->ColorAttachment(0).HasAlpha();
|
||||
} else {
|
||||
needAlphaFilled = !mOptions.alpha;
|
||||
}
|
||||
|
||||
for (GLint j = 0; j < height; ++j) {
|
||||
float* pAlpha = row + 3;
|
||||
float* pAlphaEnd = pAlpha + 4*width;
|
||||
if (!needAlphaFilled)
|
||||
return;
|
||||
|
||||
while (pAlpha != pAlphaEnd) {
|
||||
*pAlpha = 1.0f;
|
||||
pAlpha += 4;
|
||||
}
|
||||
|
||||
row += checked_alignedRowSize.value();
|
||||
}
|
||||
} else {
|
||||
NS_WARNING("Unhandled case, how'd we get here?");
|
||||
return rv.Throw(NS_ERROR_FAILURE);
|
||||
}
|
||||
}
|
||||
size_t stride = checked_alignedRowSize.value(); // In bytes!
|
||||
if (!SetFullAlpha(data, format, type, width, height, stride)) {
|
||||
return rv.Throw(NS_ERROR_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -312,12 +312,18 @@ WebGLContext::GetParameter(JSContext* cx, GLenum pname, ErrorResult& rv)
|
|||
case LOCAL_GL_RED_BITS:
|
||||
case LOCAL_GL_GREEN_BITS:
|
||||
case LOCAL_GL_BLUE_BITS:
|
||||
case LOCAL_GL_ALPHA_BITS:
|
||||
case LOCAL_GL_DEPTH_BITS: {
|
||||
GLint i = 0;
|
||||
gl->fGetIntegerv(pname, &i);
|
||||
return JS::Int32Value(i);
|
||||
}
|
||||
case LOCAL_GL_ALPHA_BITS: {
|
||||
GLint i = 0;
|
||||
if (!mNeedsFakeNoAlpha) {
|
||||
gl->fGetIntegerv(pname, &i);
|
||||
}
|
||||
return JS::Int32Value(i);
|
||||
}
|
||||
case LOCAL_GL_FRAGMENT_SHADER_DERIVATIVE_HINT: {
|
||||
if (IsExtensionEnabled(WebGLExtensionID::OES_standard_derivatives)) {
|
||||
GLint i = 0;
|
||||
|
|
|
@ -1100,7 +1100,7 @@ WebGLContext::ValidateCopyTexImage(GLenum format,
|
|||
MOZ_ASSERT(IsCopyFunc(func));
|
||||
|
||||
// Default framebuffer format
|
||||
GLenum fboFormat = bool(gl->GetPixelFormat().alpha > 0) ? LOCAL_GL_RGBA : LOCAL_GL_RGB;
|
||||
GLenum fboFormat = mOptions.alpha ? LOCAL_GL_RGBA : LOCAL_GL_RGB;
|
||||
|
||||
if (mBoundFramebuffer) {
|
||||
if (!mBoundFramebuffer->CheckAndInitializeAttachments()) {
|
||||
|
|
|
@ -54,8 +54,9 @@ if (!window.todo) {
|
|||
}
|
||||
if (!window.SimpleTest) {
|
||||
window.SimpleTest = {
|
||||
waitForExplicitFinish: function(){},
|
||||
finish: function(){},
|
||||
requestLongerTimeout: function(){},
|
||||
waitForExplicitFinish: function(){},
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -243,6 +244,13 @@ function OnTestComplete() {
|
|||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
var isAndroid2_3 = (DriverInfo.getOS() == DriverInfo.OS.ANDROID &&
|
||||
DriverInfo.getOSVersion() < OS_VERSION_ANDROID_ICS);
|
||||
if (isAndroid2_3) {
|
||||
var timeoutLengthMultiplier = 2.0;
|
||||
SimpleTest.requestLongerTimeout(timeoutLengthMultiplier);
|
||||
}
|
||||
|
||||
do {
|
||||
var arg = location.search.substr(1);
|
||||
if (arg == 'dump') {
|
||||
|
|
|
@ -1792,7 +1792,11 @@ HTMLMediaElement::CaptureStreamInternal(bool aFinishWhenEnded)
|
|||
if (!window) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#ifdef MOZ_EME
|
||||
if (ContainsRestrictedContent()) {
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
OutputMediaStream* out = mOutputStreams.AppendElement();
|
||||
#ifdef DEBUG
|
||||
// Estimate hints based on the type of the media element
|
||||
|
@ -3997,10 +4001,21 @@ HTMLMediaElement::GetMediaKeys() const
|
|||
return mMediaKeys;
|
||||
}
|
||||
|
||||
bool
|
||||
HTMLMediaElement::ContainsRestrictedContent()
|
||||
{
|
||||
return GetMediaKeys() != nullptr;
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
HTMLMediaElement::SetMediaKeys(mozilla::dom::MediaKeys* aMediaKeys,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
if (MozAudioCaptured()) {
|
||||
aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIGlobalObject> global =
|
||||
do_QueryInterface(OwnerDoc()->GetInnerWindow());
|
||||
if (!global) {
|
||||
|
@ -4035,6 +4050,8 @@ HTMLMediaElement::SetMediaKeys(mozilla::dom::MediaKeys* aMediaKeys,
|
|||
if (mDecoder) {
|
||||
mDecoder->SetCDMProxy(mMediaKeys->GetCDMProxy());
|
||||
}
|
||||
// Update the same-origin status.
|
||||
NotifyDecoderPrincipalChanged();
|
||||
}
|
||||
promise->MaybeResolve(JS::UndefinedHandleValue);
|
||||
return promise.forget();
|
||||
|
|
|
@ -550,6 +550,7 @@ public:
|
|||
// in the URL bar of the browser window.
|
||||
already_AddRefed<nsIPrincipal> GetTopLevelPrincipal();
|
||||
|
||||
bool ContainsRestrictedContent();
|
||||
#endif // MOZ_EME
|
||||
|
||||
bool MozAutoplayEnabled() const
|
||||
|
|
|
@ -1411,6 +1411,13 @@ ContentParent::ShutDownProcess(bool aCloseWithError)
|
|||
// sequence.
|
||||
mCalledClose = true;
|
||||
Close();
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
// Kill Nuwa process forcibly to break its IPC channels and finalize
|
||||
// corresponding parents.
|
||||
if (IsNuwaProcess()) {
|
||||
KillHard();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (aCloseWithError && !mCalledCloseWithError) {
|
||||
|
@ -2931,17 +2938,13 @@ ContentParent::KillHard()
|
|||
if (!KillProcess(OtherProcess(), 1, false)) {
|
||||
NS_WARNING("failed to kill subprocess!");
|
||||
}
|
||||
mSubprocess->SetAlreadyDead();
|
||||
if (mSubprocess) {
|
||||
mSubprocess->SetAlreadyDead();
|
||||
}
|
||||
XRE_GetIOMessageLoop()->PostTask(
|
||||
FROM_HERE,
|
||||
NewRunnableFunction(&ProcessWatcher::EnsureProcessTerminated,
|
||||
OtherProcess(), /*force=*/true));
|
||||
//We do clean-up here
|
||||
MessageLoop::current()->PostDelayedTask(
|
||||
FROM_HERE,
|
||||
NewRunnableMethod(this, &ContentParent::ShutDownProcess,
|
||||
/* closeWithError */ true),
|
||||
3000);
|
||||
// We've now closed the OtherProcess() handle, so must set it to null to
|
||||
// prevent our dtor closing it twice.
|
||||
SetOtherProcess(0);
|
||||
|
|
|
@ -165,7 +165,7 @@ static_assert(QUICK_BUFFERING_LOW_DATA_USECS <= AMPLE_AUDIO_USECS,
|
|||
static const int64_t ESTIMATED_DURATION_FUZZ_FACTOR_USECS = USECS_PER_S / 2;
|
||||
|
||||
static TimeDuration UsecsToDuration(int64_t aUsecs) {
|
||||
return TimeDuration::FromMilliseconds(static_cast<double>(aUsecs) / USECS_PER_MS);
|
||||
return TimeDuration::FromMicroseconds(aUsecs);
|
||||
}
|
||||
|
||||
static int64_t DurationToUsecs(TimeDuration aDuration) {
|
||||
|
@ -2569,6 +2569,7 @@ void MediaDecoderStateMachine::RenderVideoFrame(VideoData* aData,
|
|||
if (container) {
|
||||
container->SetCurrentFrame(ThebesIntSize(aData->mDisplay), aData->mImage,
|
||||
aTarget);
|
||||
MOZ_ASSERT(container->GetFrameDelay() >= 0 || mScheduler->IsRealTime());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2655,6 +2656,7 @@ void MediaDecoderStateMachine::AdvanceFrame()
|
|||
}
|
||||
|
||||
int64_t clock_time = GetClock();
|
||||
TimeStamp nowTime = TimeStamp::Now();
|
||||
// Skip frames up to the frame at the playback position, and figure out
|
||||
// the time remaining until it's time to display the next frame.
|
||||
int64_t remainingTime = AUDIO_DURATION_USECS;
|
||||
|
@ -2721,8 +2723,8 @@ void MediaDecoderStateMachine::AdvanceFrame()
|
|||
|
||||
if (currentFrame) {
|
||||
// Decode one frame and display it.
|
||||
TimeStamp presTime = mPlayStartTime - UsecsToDuration(mPlayDuration) +
|
||||
UsecsToDuration(currentFrame->mTime - mStartTime);
|
||||
int64_t delta = currentFrame->mTime - clock_time;
|
||||
TimeStamp presTime = nowTime + TimeDuration::FromMicroseconds(delta / mPlaybackRate);
|
||||
NS_ASSERTION(currentFrame->mTime >= mStartTime, "Should have positive frame time");
|
||||
// Filter out invalid frames by checking the frame time. FrameTime could be
|
||||
// zero if it's a initial frame.
|
||||
|
@ -2768,7 +2770,7 @@ void MediaDecoderStateMachine::AdvanceFrame()
|
|||
// ready state. Post an update to do so.
|
||||
UpdateReadyState();
|
||||
|
||||
ScheduleStateMachine(remainingTime);
|
||||
ScheduleStateMachine(remainingTime / mPlaybackRate);
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
|
|
@ -38,7 +38,7 @@ AppleATDecoder::AppleATDecoder(const mp4_demuxer::AudioDecoderConfig& aConfig,
|
|||
, mFlushed(false)
|
||||
{
|
||||
MOZ_COUNT_CTOR(AppleATDecoder);
|
||||
LOG("Creating Apple AudioToolbox Audio decoder");
|
||||
LOG("Creating Apple AudioToolbox decoder");
|
||||
LOG("Audio Decoder configuration: %s %d Hz %d channels %d bits per channel",
|
||||
mConfig.mime_type,
|
||||
mConfig.samples_per_second,
|
||||
|
@ -92,7 +92,7 @@ AppleATDecoder::Init()
|
|||
NS_ERROR("Non recognised format");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
LOG("Initializing Apple AudioToolbox Audio decoder");
|
||||
LOG("Initializing Apple AudioToolbox decoder");
|
||||
OSStatus rv = AudioFileStreamOpen(this,
|
||||
_MetadataCallback,
|
||||
_SampleCallback,
|
||||
|
@ -315,10 +315,10 @@ AppleATDecoder::SampleCallback(uint32_t aNumBytes,
|
|||
void
|
||||
AppleATDecoder::SetupDecoder()
|
||||
{
|
||||
AudioStreamBasicDescription inputFormat;
|
||||
|
||||
LOG("Setting up Apple AudioToolbox decoder.");
|
||||
mHaveOutput = false;
|
||||
|
||||
AudioStreamBasicDescription inputFormat;
|
||||
nsresult rv = AppleUtils::GetRichestDecodableFormat(mStream, inputFormat);
|
||||
if (NS_FAILED(rv)) {
|
||||
mCallback->Error();
|
||||
|
|
|
@ -1201,7 +1201,10 @@ GeckoMediaPluginService::ClearStorage()
|
|||
return;
|
||||
}
|
||||
|
||||
if (NS_FAILED(path->Remove(true))) {
|
||||
bool exists = false;
|
||||
if (NS_SUCCEEDED(path->Exists(&exists)) &&
|
||||
exists &&
|
||||
NS_FAILED(path->Remove(true))) {
|
||||
NS_WARNING("Failed to delete GMP storage directory");
|
||||
}
|
||||
NS_DispatchToMainThread(new StorageClearedTask(), NS_DISPATCH_NORMAL);
|
||||
|
|
|
@ -84,6 +84,18 @@ public:
|
|||
mTaskQueue = aTaskQueue;
|
||||
}
|
||||
|
||||
void BreakCycles()
|
||||
{
|
||||
if (mReader) {
|
||||
mReader->BreakCycles();
|
||||
mReader = nullptr;
|
||||
}
|
||||
mTaskQueue = nullptr;
|
||||
#ifdef MOZ_EME
|
||||
mCDMProxy = nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef MOZ_EME
|
||||
virtual nsresult SetCDMProxy(CDMProxy* aProxy)
|
||||
{
|
||||
|
|
|
@ -391,7 +391,7 @@ TrackBuffer::BreakCycles()
|
|||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
for (uint32_t i = 0; i < mDecoders.Length(); ++i) {
|
||||
mDecoders[i]->GetReader()->BreakCycles();
|
||||
mDecoders[i]->BreakCycles();
|
||||
}
|
||||
mDecoders.Clear();
|
||||
|
||||
|
|
|
@ -0,0 +1,178 @@
|
|||
const KEYSYSTEM_TYPE = "org.w3.clearkey";
|
||||
|
||||
function bail(message)
|
||||
{
|
||||
return function(err) {
|
||||
ok(false, message);
|
||||
if (err) {
|
||||
info(err);
|
||||
}
|
||||
SimpleTest.finish();
|
||||
}
|
||||
}
|
||||
|
||||
function ArrayBufferToString(arr)
|
||||
{
|
||||
var str = '';
|
||||
var view = new Uint8Array(arr);
|
||||
for (var i = 0; i < view.length; i++) {
|
||||
str += String.fromCharCode(view[i]);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
function StringToArrayBuffer(str)
|
||||
{
|
||||
var arr = new ArrayBuffer(str.length);
|
||||
var view = new Uint8Array(arr);
|
||||
for (var i = 0; i < str.length; i++) {
|
||||
view[i] = str.charCodeAt(i);
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
function Base64ToHex(str)
|
||||
{
|
||||
var bin = window.atob(str.replace(/-/g, "+").replace(/_/g, "/"));
|
||||
var res = "";
|
||||
for (var i = 0; i < bin.length; i++) {
|
||||
res += ("0" + bin.charCodeAt(i).toString(16)).substr(-2);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
function HexToBase64(hex)
|
||||
{
|
||||
var bin = "";
|
||||
for (var i = 0; i < hex.length; i += 2) {
|
||||
bin += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
|
||||
}
|
||||
return window.btoa(bin).replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
|
||||
}
|
||||
|
||||
function UpdateSessionFunc(test) {
|
||||
return function(ev) {
|
||||
var msgStr = ArrayBufferToString(ev.message);
|
||||
var msg = JSON.parse(msgStr);
|
||||
|
||||
info("got message from CDM: " + msgStr);
|
||||
is(msg.type, test.sessionType, "Key session type should match");
|
||||
ok(msg.kids, "message event should contain key ID array");
|
||||
|
||||
var outKeys = [];
|
||||
|
||||
for (var i = 0; i < msg.kids.length; i++) {
|
||||
var id64 = msg.kids[i];
|
||||
var idHex = Base64ToHex(msg.kids[i]).toLowerCase();
|
||||
var key = test.keys[idHex];
|
||||
|
||||
if (key) {
|
||||
info("found key " + key + " for key id " + idHex);
|
||||
outKeys.push({
|
||||
"kty":"oct",
|
||||
"alg":"A128KW",
|
||||
"kid":id64,
|
||||
"k":HexToBase64(key)
|
||||
});
|
||||
} else {
|
||||
bail("Couldn't find key for key id " + idHex);
|
||||
}
|
||||
}
|
||||
|
||||
var update = JSON.stringify({
|
||||
"keys" : outKeys,
|
||||
"type" : msg.type
|
||||
});
|
||||
info("sending update message to CDM: " + update);
|
||||
|
||||
ev.target.update(StringToArrayBuffer(update)).then(function() {
|
||||
info("MediaKeySession update ok!");
|
||||
}, bail("MediaKeySession update failed"));
|
||||
}
|
||||
}
|
||||
|
||||
function PlayFragmented(test, elem)
|
||||
{
|
||||
return new Promise(function(resolve, reject) {
|
||||
var ms = new MediaSource();
|
||||
elem.src = URL.createObjectURL(ms);
|
||||
|
||||
var sb;
|
||||
var curFragment = 0;
|
||||
|
||||
function addNextFragment() {
|
||||
if (curFragment >= test.fragments.length) {
|
||||
ms.endOfStream();
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
var fragmentFile = test.fragments[curFragment++];
|
||||
|
||||
var req = new XMLHttpRequest();
|
||||
req.open("GET", fragmentFile);
|
||||
req.responseType = "arraybuffer";
|
||||
|
||||
req.addEventListener("load", function() {
|
||||
sb.appendBuffer(new Uint8Array(req.response));
|
||||
});
|
||||
|
||||
info("fetching resource " + fragmentFile);
|
||||
req.send(null);
|
||||
}
|
||||
|
||||
ms.addEventListener("sourceopen", function () {
|
||||
sb = ms.addSourceBuffer(test.type);
|
||||
sb.addEventListener("updateend", addNextFragment);
|
||||
|
||||
addNextFragment();
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
// Returns a promise that is resovled when the media element is ready to have
|
||||
// its play() function called; when it's loaded MSE fragments, or once the load
|
||||
// has started for non-MSE video.
|
||||
function LoadTest(test, elem)
|
||||
{
|
||||
if (test.fragments) {
|
||||
return PlayFragmented(test, elem);
|
||||
}
|
||||
|
||||
// This file isn't fragmented; set the media source normally.
|
||||
return new Promise(function(resolve, reject) {
|
||||
elem.src = test.name;
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
|
||||
function SetupEME(test, token, params)
|
||||
{
|
||||
var v = document.createElement("video");
|
||||
|
||||
var onSetKeysFail = (params && params.onSetKeysFail)
|
||||
? params.onSetKeysFail
|
||||
: bail(token + " Failed to set MediaKeys on <video> element");
|
||||
|
||||
v.addEventListener("encrypted", function(ev) {
|
||||
info(token + " got encrypted event");
|
||||
MediaKeys.create(KEYSYSTEM_TYPE).then(function(mediaKeys) {
|
||||
info(token + " created MediaKeys object ok");
|
||||
mediaKeys.sessions = [];
|
||||
return v.setMediaKeys(mediaKeys);
|
||||
}, bail("failed to create MediaKeys object")).then(function() {
|
||||
info(token + " set MediaKeys on <video> element ok");
|
||||
|
||||
var session = v.mediaKeys.createSession(test.sessionType);
|
||||
if (params && params.onsessioncreated) {
|
||||
params.onsessioncreated(session);
|
||||
}
|
||||
session.addEventListener("message", UpdateSessionFunc(test));
|
||||
session.generateRequest(ev.initDataType, ev.initData).then(function() {
|
||||
}, bail(token + " Failed to initialise MediaKeySession"));
|
||||
|
||||
}, onSetKeysFail);
|
||||
});
|
||||
|
||||
return v;
|
||||
}
|
|
@ -140,6 +140,7 @@ support-files =
|
|||
dirac.ogg^headers^
|
||||
dynamic_redirect.sjs
|
||||
dynamic_resource.sjs
|
||||
eme.js
|
||||
gizmo-frag-cenc1.m4s
|
||||
gizmo-frag-cenc2.m4s
|
||||
gizmo-frag-cencinit.mp4
|
||||
|
@ -360,7 +361,11 @@ skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
|
|||
[test_defaultMuted.html]
|
||||
[test_delay_load.html]
|
||||
skip-if = buildapp == 'b2g' && toolkit != 'gonk' # bug 1082984
|
||||
[test_encryptedMediaExtensions.html]
|
||||
[test_eme_canvas_blocked.html]
|
||||
skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s # bug 1043403, bug 1057908
|
||||
[test_eme_playback.html]
|
||||
skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s # bug 1043403, bug 1057908
|
||||
[test_eme_stream_capture_blocked.html]
|
||||
skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s # bug 1043403, bug 1057908
|
||||
[test_error_in_video_document.html]
|
||||
skip-if = toolkit == 'android' # bug 608634
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test Encrypted Media Extensions</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
<script type="text/javascript" src="manifest.js"></script>
|
||||
<script type="text/javascript" src="eme.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
var manager = new MediaTestManager;
|
||||
|
||||
function startTest(test, token)
|
||||
{
|
||||
manager.started(token);
|
||||
|
||||
var sessions = [];
|
||||
|
||||
var v = SetupEME(test, token);
|
||||
v.preload = "auto"; // Required due to "canplay" not firing for MSE unless we do this.
|
||||
|
||||
v.addEventListener("canplay", function(ev) {
|
||||
var video = ev.target;
|
||||
var canvas = document.createElement("canvas");
|
||||
canvas.width = video.videoWidth;
|
||||
canvas.height = video.videoHeight;
|
||||
document.body.appendChild(canvas);
|
||||
var ctx = canvas.getContext("2d");
|
||||
var threwError = false;
|
||||
try {
|
||||
ctx.drawImage(video, 0, 0);
|
||||
} catch (ex) {
|
||||
threwError = true;
|
||||
}
|
||||
ok(threwError, token + " - Should throw an error when trying to draw EME video to canvas.");
|
||||
manager.finished(token);
|
||||
});
|
||||
|
||||
v.addEventListener("error", bail(token + " got error event"));
|
||||
|
||||
LoadTest(test, v);
|
||||
}
|
||||
|
||||
function beginTest() {
|
||||
manager.runTests(gEMETests, startTest);
|
||||
}
|
||||
|
||||
var prefs = [
|
||||
[ "media.mediasource.enabled", true ],
|
||||
[ "media.mediasource.ignore_codecs", true ],
|
||||
];
|
||||
|
||||
if (/Linux/.test(navigator.userAgent) ||
|
||||
!document.createElement('video').canPlayType("video/mp4")) {
|
||||
// XXX remove once we have mp4 PlatformDecoderModules on all platforms.
|
||||
prefs.push([ "media.fragmented-mp4.exposed", true ]);
|
||||
prefs.push([ "media.fragmented-mp4.use-blank-decoder", true ]);
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SpecialPowers.pushPrefEnv({ "set" : prefs }, beginTest);
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,128 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test Encrypted Media Extensions</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
<script type="text/javascript" src="manifest.js"></script>
|
||||
<script type="text/javascript" src="eme.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
var manager = new MediaTestManager;
|
||||
|
||||
|
||||
function KeysChangeFunc(session, keys, token) {
|
||||
session.keyIdsReceived = [];
|
||||
for (var keyid in keys) {
|
||||
info("Set " + keyid + " to false in session.keyIdsReceived");
|
||||
session.keyIdsReceived[keyid] = false;
|
||||
}
|
||||
return function(ev) {
|
||||
var session = ev.target;
|
||||
session.gotKeysChanged = true;
|
||||
session.getUsableKeyIds().then(function(keyIds) {
|
||||
for (var k = 0; k < keyIds.length; k++) {
|
||||
var kid = Base64ToHex(window.btoa(ArrayBufferToString(keyIds[k])));
|
||||
ok(kid in session.keyIdsReceived, token + " session.keyIdsReceived contained " + kid + " as expected.");
|
||||
session.keyIdsReceived[kid] = true;
|
||||
}
|
||||
}, bail("Failed to get keyIds"));
|
||||
}
|
||||
}
|
||||
|
||||
function startTest(test, token)
|
||||
{
|
||||
manager.started(token);
|
||||
|
||||
var sessions = [];
|
||||
|
||||
var v = SetupEME(test, token,
|
||||
{
|
||||
onsessioncreated: function(session) {
|
||||
sessions.push(session);
|
||||
session.addEventListener("keyschange", KeysChangeFunc(session, test.keys, token), false);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
var gotEncrypted = false;
|
||||
var gotPlaying = false;
|
||||
|
||||
v.addEventListener("encrypted", function(ev) {
|
||||
ok(MediaKeys.isTypeSupported(KEYSYSTEM_TYPE, ev.initDataType, test.type),
|
||||
token + " MediaKeys should support this keysystem");
|
||||
gotEncrypted = true;
|
||||
});
|
||||
|
||||
v.addEventListener("playing", function () { gotPlaying = true; });
|
||||
|
||||
v.addEventListener("ended", function(ev) {
|
||||
ok(true, token + " got ended event");
|
||||
manager.finished(token);
|
||||
|
||||
ok(gotEncrypted, token + " encrypted event should have fired");
|
||||
ok(gotPlaying, token + " playing event should have fired");
|
||||
|
||||
ok(Math.abs(test.duration - v.duration) < 0.1,
|
||||
token + " Duration of video should be corrrect");
|
||||
ok(Math.abs(test.duration - v.currentTime) < 0.1,
|
||||
token + " Current time should be same as duration");
|
||||
|
||||
// Verify all sessions had all keys went sent the to the CDM usable, and thus
|
||||
// that we received keyschange event(s).
|
||||
is(sessions.length, 1, "should have 1 session");
|
||||
for (var i = 0; i < sessions.length; i++) {
|
||||
var session = sessions[i];
|
||||
ok(session.gotKeysChanged, token + " should have received at least one keychange event");
|
||||
for (var kid in session.keyIdsReceived) {
|
||||
ok(session.keyIdsReceived[kid], token + " key with id " + kid + " was usable as expected");
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
v.addEventListener("error", bail(token + " got error event"));
|
||||
|
||||
LoadTest(test, v).then(function(){v.play();}, bail(token + " failed to load"));
|
||||
}
|
||||
|
||||
function testIsTypeSupported()
|
||||
{
|
||||
var t = MediaKeys.isTypeSupported;
|
||||
const clearkey = "org.w3.clearkey";
|
||||
ok(!t("bogus", "bogon", "video/bogus"), "Invalid type.");
|
||||
ok(t(clearkey), "ClearKey supported.");
|
||||
ok(!t(clearkey, "bogus"), "ClearKey bogus initDataType not supported.");
|
||||
ok(t(clearkey, "cenc"), "ClearKey/cenc should be supported.");
|
||||
ok(!t(clearkey, "cenc", "bogus"), "ClearKey/cenc bogus content type should be supported.");
|
||||
ok(t(clearkey, "cenc", 'video/mp4'), "ClearKey/cenc video/mp4 supported.");
|
||||
ok(t(clearkey, "cenc", 'video/mp4; codecs="avc1.4d4015,mp4a.40.2"'), "ClearKey/cenc H.264/AAC supported.");
|
||||
ok(t(clearkey, "cenc", 'audio/mp4'), "ClearKey/cenc audio/mp4 supported.");
|
||||
ok(t(clearkey, "cenc", 'audio/mp4; codecs="mp4a.40.2"'), "ClearKey/cenc AAC LC supported.");
|
||||
}
|
||||
|
||||
function beginTest() {
|
||||
testIsTypeSupported();
|
||||
manager.runTests(gEMETests, startTest);
|
||||
}
|
||||
|
||||
var prefs = [
|
||||
[ "media.mediasource.enabled", true ],
|
||||
[ "media.mediasource.ignore_codecs", true ],
|
||||
];
|
||||
|
||||
if (/Linux/.test(navigator.userAgent) ||
|
||||
!document.createElement('video').canPlayType("video/mp4")) {
|
||||
// XXX remove once we have mp4 PlatformDecoderModules on all platforms.
|
||||
prefs.push([ "media.fragmented-mp4.exposed", true ]);
|
||||
prefs.push([ "media.fragmented-mp4.use-blank-decoder", true ]);
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SpecialPowers.pushPrefEnv({ "set" : prefs }, beginTest);
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,102 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test Encrypted Media Extensions</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
<script type="text/javascript" src="manifest.js"></script>
|
||||
<script type="text/javascript" src="eme.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
var manager = new MediaTestManager;
|
||||
|
||||
function startTest(test, token)
|
||||
{
|
||||
// Three cases:
|
||||
// 1. setting MediaKeys on an element captured by MediaElementSource should fail, and
|
||||
// 2. creating a MediaElementSource on a media element with a MediaKeys should fail, and
|
||||
// 3. capturing a media element with mozCaptureStream that has a MediaKeys should fail.
|
||||
|
||||
// Case 1. setting MediaKeys on an element captured by MediaElementSource should fail.
|
||||
var case1token = token + "_case1";
|
||||
var setKeysFailed = function() {
|
||||
ok(true, case1token + " setMediaKeys failed as expected.");
|
||||
manager.finished(case1token);
|
||||
};
|
||||
var v1 = SetupEME(test, case1token, { onSetKeysFail: setKeysFailed });
|
||||
var context = new AudioContext();
|
||||
var node = context.createMediaElementSource(v1);
|
||||
v1.preload = "auto"; // Required due to "canplay" not firing for MSE unless we do this.
|
||||
v1.addEventListener("error", bail(case1token + " got error event"));
|
||||
v1.addEventListener("canplay", function(ev) {
|
||||
ok(false, case1token + " should never reach canplay, as setMediaKeys should fail");
|
||||
});
|
||||
manager.started(case1token);
|
||||
LoadTest(test, v1);
|
||||
|
||||
|
||||
// Case 2. creating a MediaElementSource on a media element with a MediaKeys should fail.
|
||||
var case2token = token + "_case2";
|
||||
var v2 = SetupEME(test, case2token);
|
||||
v2.preload = "auto"; // Required due to "canplay" not firing for MSE unless we do this.
|
||||
v2.addEventListener("error", bail(case2token + " got error event"));
|
||||
v2.addEventListener("canplay", function(ev) {
|
||||
ok(true, case2token + " should reach canplay");
|
||||
var threw = false;
|
||||
try {
|
||||
var context = new AudioContext();
|
||||
var node = context.createMediaElementSource(v2);
|
||||
} catch (e) {
|
||||
threw = true;
|
||||
}
|
||||
ok(threw, "Should throw an error creating a MediaElementSource on an EME video.");
|
||||
manager.finished(case2token);
|
||||
});
|
||||
manager.started(case2token);
|
||||
LoadTest(test, v2);
|
||||
|
||||
|
||||
// Case 3. capturing a media element with mozCaptureStream that has a MediaKeys should fail.
|
||||
var case3token = token + "_case3";
|
||||
var v3 = SetupEME(test, case3token);
|
||||
v3.preload = "auto"; // Required due to "canplay" not firing for MSE unless we do this.
|
||||
v3.addEventListener("error", bail(case3token + " got error event"));
|
||||
v3.addEventListener("canplay", function(ev) {
|
||||
ok(true, case3token + " should reach canplay");
|
||||
var threw = false;
|
||||
try {
|
||||
var stream = v3.mozCaptureStreamUntilEnded();
|
||||
} catch (e) {
|
||||
threw = true;
|
||||
}
|
||||
ok(threw, "Should throw an error calling mozCaptureStreamUntilEnded an EME video.");
|
||||
manager.finished(case3token);
|
||||
});
|
||||
manager.started(case3token);
|
||||
LoadTest(test, v3);
|
||||
}
|
||||
|
||||
function beginTest() {
|
||||
manager.runTests(gEMETests, startTest);
|
||||
}
|
||||
|
||||
var prefs = [
|
||||
[ "media.mediasource.enabled", true ],
|
||||
[ "media.mediasource.ignore_codecs", true ],
|
||||
];
|
||||
|
||||
if (/Linux/.test(navigator.userAgent) ||
|
||||
!document.createElement('video').canPlayType("video/mp4")) {
|
||||
// XXX remove once we have mp4 PlatformDecoderModules on all platforms.
|
||||
prefs.push([ "media.fragmented-mp4.exposed", true ]);
|
||||
prefs.push([ "media.fragmented-mp4.use-blank-decoder", true ]);
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SpecialPowers.pushPrefEnv({ "set" : prefs }, beginTest);
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -1,278 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test Encrypted Media Extensions</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
<script type="text/javascript" src="manifest.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
var manager = new MediaTestManager;
|
||||
|
||||
const KEYSYSTEM_TYPE = "org.w3.clearkey";
|
||||
|
||||
function bail(message)
|
||||
{
|
||||
return function(err) {
|
||||
ok(false, message);
|
||||
if (err) {
|
||||
info(err);
|
||||
}
|
||||
SimpleTest.finish();
|
||||
}
|
||||
}
|
||||
|
||||
function ArrayBufferToString(arr)
|
||||
{
|
||||
var str = '';
|
||||
var view = new Uint8Array(arr);
|
||||
for (var i = 0; i < view.length; i++) {
|
||||
str += String.fromCharCode(view[i]);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
function StringToArrayBuffer(str)
|
||||
{
|
||||
var arr = new ArrayBuffer(str.length);
|
||||
var view = new Uint8Array(arr);
|
||||
for (var i = 0; i < str.length; i++) {
|
||||
view[i] = str.charCodeAt(i);
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
function Base64ToHex(str)
|
||||
{
|
||||
var bin = window.atob(str.replace(/-/g, "+").replace(/_/g, "/"));
|
||||
var res = "";
|
||||
for (var i = 0; i < bin.length; i++) {
|
||||
res += ("0" + bin.charCodeAt(i).toString(16)).substr(-2);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
function HexToBase64(hex)
|
||||
{
|
||||
var bin = "";
|
||||
for (var i = 0; i < hex.length; i += 2) {
|
||||
bin += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
|
||||
}
|
||||
return window.btoa(bin).replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
|
||||
}
|
||||
|
||||
function UpdateSessionFunc(test) {
|
||||
return function(ev) {
|
||||
var msgStr = ArrayBufferToString(ev.message);
|
||||
var msg = JSON.parse(msgStr);
|
||||
|
||||
info("got message from CDM: " + msgStr);
|
||||
is(msg.type, test.sessionType, "Key session type should match");
|
||||
ok(msg.kids, "message event should contain key ID array");
|
||||
|
||||
var outKeys = [];
|
||||
|
||||
for (var i = 0; i < msg.kids.length; i++) {
|
||||
var id64 = msg.kids[i];
|
||||
var idHex = Base64ToHex(msg.kids[i]).toLowerCase();
|
||||
var key = test.keys[idHex];
|
||||
|
||||
if (key) {
|
||||
info("found key " + key + " for key id " + idHex);
|
||||
outKeys.push({
|
||||
"kty":"oct",
|
||||
"alg":"A128KW",
|
||||
"kid":id64,
|
||||
"k":HexToBase64(key)
|
||||
});
|
||||
} else {
|
||||
bail("Couldn't find key for key id " + idHex);
|
||||
}
|
||||
}
|
||||
|
||||
var update = JSON.stringify({
|
||||
"keys" : outKeys,
|
||||
"type" : msg.type
|
||||
});
|
||||
info("sending update message to CDM: " + update);
|
||||
|
||||
ev.target.update(StringToArrayBuffer(update)).then(function() {
|
||||
info("MediaKeySession update ok!");
|
||||
}, bail("MediaKeySession update failed"));
|
||||
}
|
||||
}
|
||||
|
||||
function PlayFragmented(test, elem)
|
||||
{
|
||||
var ms = new MediaSource();
|
||||
elem.src = URL.createObjectURL(ms);
|
||||
|
||||
var sb;
|
||||
var curFragment = 0;
|
||||
|
||||
function addNextFragment() {
|
||||
if (curFragment >= test.fragments.length) {
|
||||
ms.endOfStream();
|
||||
elem.play();
|
||||
return;
|
||||
}
|
||||
|
||||
var fragmentFile = test.fragments[curFragment++];
|
||||
|
||||
var req = new XMLHttpRequest();
|
||||
req.open("GET", fragmentFile);
|
||||
req.responseType = "arraybuffer";
|
||||
|
||||
req.addEventListener("load", function() {
|
||||
sb.appendBuffer(new Uint8Array(req.response));
|
||||
});
|
||||
|
||||
info("fetching resource " + fragmentFile);
|
||||
req.send(null);
|
||||
}
|
||||
|
||||
ms.addEventListener("sourceopen", function () {
|
||||
sb = ms.addSourceBuffer(test.type);
|
||||
sb.addEventListener("updateend", addNextFragment);
|
||||
|
||||
addNextFragment();
|
||||
});
|
||||
}
|
||||
|
||||
function PlayTest(test, elem)
|
||||
{
|
||||
if (test.fragments) {
|
||||
PlayFragmented(test, elem);
|
||||
return;
|
||||
}
|
||||
|
||||
// This file isn't fragmented; set the media source normally.
|
||||
elem.src = test.name;
|
||||
elem.play();
|
||||
}
|
||||
|
||||
function KeysChangeFunc(session, keys) {
|
||||
session.keyIdsReceived = [];
|
||||
for (var keyid in keys) {
|
||||
info("Set " + keyid + " to false in session.keyIdsReceived");
|
||||
session.keyIdsReceived[keyid] = false;
|
||||
}
|
||||
return function(ev) {
|
||||
var session = ev.target;
|
||||
session.gotKeysChanged = true;
|
||||
session.getUsableKeyIds().then(function(keyIds) {
|
||||
for (var k = 0; k < keyIds.length; k++) {
|
||||
var kid = Base64ToHex(window.btoa(ArrayBufferToString(keyIds[k])));
|
||||
ok(kid in session.keyIdsReceived, "session.keyIdsReceived contained " + kid + " as expected.");
|
||||
session.keyIdsReceived[kid] = true;
|
||||
}
|
||||
}, bail("Failed to get keyIds"));
|
||||
}
|
||||
}
|
||||
|
||||
function startTest(test, token)
|
||||
{
|
||||
manager.started(test._token);
|
||||
|
||||
var v = document.createElement("video");
|
||||
var gotEncrypted = false;
|
||||
var gotPlaying = false;
|
||||
|
||||
v.addEventListener("encrypted", function(ev) {
|
||||
gotEncrypted = true;
|
||||
|
||||
info(token + " got encrypted event");
|
||||
ok(MediaKeys.isTypeSupported(KEYSYSTEM_TYPE, ev.initDataType, test.type),
|
||||
token + " MediaKeys should support this keysystem");
|
||||
|
||||
MediaKeys.create(KEYSYSTEM_TYPE).then(function(mediaKeys) {
|
||||
info(token + " created MediaKeys object ok");
|
||||
mediaKeys.sessions = [];
|
||||
return v.setMediaKeys(mediaKeys);
|
||||
}, bail("failed to create MediaKeys object")).then(function() {
|
||||
info(token + " set MediaKeys on <video> element ok");
|
||||
|
||||
ok(MediaKeys.isTypeSupported(KEYSYSTEM_TYPE, ev.initDataType, test.type),
|
||||
"MediaKeys should still support keysystem after CDM created...");
|
||||
|
||||
var session = v.mediaKeys.createSession(test.sessionType);
|
||||
v.mediaKeys.sessions.push(session);
|
||||
session.addEventListener("keyschange", KeysChangeFunc(session, test.keys), false);
|
||||
session.addEventListener("message", UpdateSessionFunc(test));
|
||||
session.generateRequest(ev.initDataType, ev.initData).then(function() {
|
||||
}, bail(token + " Failed to initialise MediaKeySession"));
|
||||
|
||||
}, bail(token + " Failed to set MediaKeys on <video> element"));
|
||||
});
|
||||
|
||||
v.addEventListener("playing", function () { gotPlaying = true; });
|
||||
|
||||
v.addEventListener("ended", function(ev) {
|
||||
ok(true, token + " got ended event");
|
||||
manager.finished(test._token);
|
||||
|
||||
ok(gotEncrypted, token + " encrypted event should have fired");
|
||||
ok(gotPlaying, token + " playing event should have fired");
|
||||
|
||||
ok(Math.abs(test.duration - v.duration) < 0.1,
|
||||
token + " Duration of video should be corrrect");
|
||||
ok(Math.abs(test.duration - v.currentTime) < 0.1,
|
||||
token + " Current time should be same as duration");
|
||||
// Verify all sessions had all keys went sent the to the CDM usable, and thus
|
||||
// that we received keyschange event(s).
|
||||
var sessions = v.mediaKeys.sessions;
|
||||
is(sessions.length, 1, "should have 1 session");
|
||||
for (var i = 0; i < sessions.length; i++) {
|
||||
var session = sessions[i];
|
||||
ok(session.gotKeysChanged, "Should have received at least one keychange event");
|
||||
for (var kid in session.keyIdsReceived) {
|
||||
ok(session.keyIdsReceived[kid], "key with id " + kid + " was usable as expected");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
v.addEventListener("error", bail(token + " got error event"));
|
||||
|
||||
PlayTest(test, v);
|
||||
}
|
||||
|
||||
function testIsTypeSupported()
|
||||
{
|
||||
var t = MediaKeys.isTypeSupported;
|
||||
const clearkey = "org.w3.clearkey";
|
||||
ok(!t("bogus", "bogon", "video/bogus"), "Invalid type.");
|
||||
ok(t(clearkey), "ClearKey supported.");
|
||||
ok(!t(clearkey, "bogus"), "ClearKey bogus initDataType not supported.");
|
||||
ok(t(clearkey, "cenc"), "ClearKey/cenc should be supported.");
|
||||
ok(!t(clearkey, "cenc", "bogus"), "ClearKey/cenc bogus content type should be supported.");
|
||||
ok(t(clearkey, "cenc", 'video/mp4'), "ClearKey/cenc video/mp4 supported.");
|
||||
ok(t(clearkey, "cenc", 'video/mp4; codecs="avc1.4d4015,mp4a.40.2"'), "ClearKey/cenc H.264/AAC supported.");
|
||||
ok(t(clearkey, "cenc", 'audio/mp4'), "ClearKey/cenc audio/mp4 supported.");
|
||||
ok(t(clearkey, "cenc", 'audio/mp4; codecs="mp4a.40.2"'), "ClearKey/cenc AAC LC supported.");
|
||||
}
|
||||
|
||||
function beginTest() {
|
||||
testIsTypeSupported();
|
||||
manager.runTests(gEMETests, startTest);
|
||||
}
|
||||
|
||||
var prefs = [
|
||||
[ "media.mediasource.enabled", true ],
|
||||
[ "media.mediasource.ignore_codecs", true ],
|
||||
];
|
||||
|
||||
if (/Linux/.test(navigator.userAgent) ||
|
||||
!document.createElement('video').canPlayType("video/mp4")) {
|
||||
// XXX remove once we have mp4 PlatformDecoderModules on all platforms.
|
||||
prefs.push([ "media.fragmented-mp4.exposed", true ]);
|
||||
prefs.push([ "media.fragmented-mp4.use-blank-decoder", true ]);
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SpecialPowers.pushPrefEnv({ "set" : prefs }, beginTest);
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -1303,6 +1303,8 @@ DataChannelTest.prototype = Object.create(PeerConnectionTest.prototype, {
|
|||
is(channel.readyState, "open", peer + " dataChannels[0] switched to state: 'open'");
|
||||
dcOpened = true;
|
||||
onSuccess();
|
||||
} else {
|
||||
info("dataChannelConnected() called, but data channel was open already");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2606,11 +2608,12 @@ PeerConnectionWrapper.prototype = {
|
|||
* Callback to execute when the data channel has been opened
|
||||
*/
|
||||
registerDataChannelOpenEvents : function (onDataChannelOpened) {
|
||||
info(this + ": Register callbacks for 'ondatachannel' and 'onopen'");
|
||||
info(this + ": Register callback for 'ondatachannel'");
|
||||
|
||||
this.ondatachannel = function (targetChannel) {
|
||||
targetChannel.onopen = onDataChannelOpened;
|
||||
this.dataChannels.push(targetChannel);
|
||||
info(this + ": 'ondatachannel' fired, registering 'onopen' callback");
|
||||
targetChannel.onopen = onDataChannelOpened;
|
||||
};
|
||||
},
|
||||
|
||||
|
|
|
@ -285,6 +285,12 @@ AudioContext::CreateMediaElementSource(HTMLMediaElement& aMediaElement,
|
|||
aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
|
||||
return nullptr;
|
||||
}
|
||||
#ifdef MOZ_EME
|
||||
if (aMediaElement.ContainsRestrictedContent()) {
|
||||
aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
nsRefPtr<DOMMediaStream> stream = aMediaElement.MozCaptureStream(aRv);
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
|
|
|
@ -591,7 +591,8 @@ bool ValidateES2CopyTexImageParameters(Context* context, GLenum target, GLint le
|
|||
case GL_RGBA:
|
||||
if (colorbufferFormat != GL_RGBA4 &&
|
||||
colorbufferFormat != GL_RGB5_A1 &&
|
||||
colorbufferFormat != GL_RGBA8_OES)
|
||||
colorbufferFormat != GL_RGBA8_OES &&
|
||||
colorbufferFormat != GL_BGRA8_EXT)
|
||||
{
|
||||
context->recordError(Error(GL_INVALID_OPERATION));
|
||||
return false;
|
||||
|
|
|
@ -5187,8 +5187,6 @@
|
|||
#define LOCAL_EGL_COVERAGE_SAMPLE_RESOLVE_NONE_NV 0x3133
|
||||
#define LOCAL_EGL_COVERAGE_SAMPLE_RESOLVE_NV 0x3131
|
||||
#define LOCAL_EGL_D3D_TEXTURE_2D_SHARE_HANDLE_ANGLE 0x3200
|
||||
#define LOCAL_EGL_D3D11_ELSE_D3D9_DISPLAY_ANGLE ((EGLNativeDisplayType)-2)
|
||||
#define LOCAL_EGL_D3D11_ONLY_DISPLAY_ANGLE ((EGLNativeDisplayType)-3)
|
||||
#define LOCAL_EGL_DEFAULT_DISPLAY ((EGLNativeDisplayType)0)
|
||||
#define LOCAL_EGL_DEPTH_ENCODING_NONE_NV 0
|
||||
#define LOCAL_EGL_DEPTH_ENCODING_NONLINEAR_NV 0x30E3
|
||||
|
|
|
@ -1812,6 +1812,15 @@ GLContext::ChooseGLFormats(const SurfaceCaps& caps) const
|
|||
}
|
||||
}
|
||||
|
||||
if (WorkAroundDriverBugs() &&
|
||||
IsANGLE() &&
|
||||
formats.color_rbFormat == LOCAL_GL_RGBA8)
|
||||
{
|
||||
formats.color_texInternalFormat = LOCAL_GL_BGRA;
|
||||
formats.color_texFormat = LOCAL_GL_BGRA;
|
||||
formats.color_rbFormat = LOCAL_GL_BGRA8_EXT;
|
||||
}
|
||||
|
||||
uint32_t msaaLevel = gfxPrefs::MSAALevel();
|
||||
GLsizei samples = msaaLevel * msaaLevel;
|
||||
samples = std::min(samples, mMaxSamples);
|
||||
|
|
|
@ -55,4 +55,8 @@
|
|||
#define LOCAL_GL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002
|
||||
#define LOCAL_GL_CONTEXT_ROBUST_ACCESS_BIT_ARB 0x00000004
|
||||
|
||||
|
||||
#define LOCAL_EGL_D3D11_ELSE_D3D9_DISPLAY_ANGLE ((EGLNativeDisplayType)-2)
|
||||
#define LOCAL_EGL_D3D11_ONLY_DISPLAY_ANGLE ((EGLNativeDisplayType)-3)
|
||||
|
||||
#endif
|
||||
|
|
|
@ -99,6 +99,19 @@ LoadLibraryForEGLOnWindows(const nsAString& filename)
|
|||
}
|
||||
#endif // XP_WIN
|
||||
|
||||
static EGLDisplay
|
||||
GetAndInitDisplay(GLLibraryEGL& egl, void* displayType)
|
||||
{
|
||||
EGLDisplay display = egl.fGetDisplay(displayType);
|
||||
if (display == EGL_NO_DISPLAY)
|
||||
return EGL_NO_DISPLAY;
|
||||
|
||||
if (!egl.fInitialize(display, nullptr, nullptr))
|
||||
return EGL_NO_DISPLAY;
|
||||
|
||||
return display;
|
||||
}
|
||||
|
||||
bool
|
||||
GLLibraryEGL::EnsureInitialized()
|
||||
{
|
||||
|
@ -185,6 +198,7 @@ GLLibraryEGL::EnsureInitialized()
|
|||
|
||||
GLLibraryLoader::SymLoadStruct earlySymbols[] = {
|
||||
SYMBOL(GetDisplay),
|
||||
SYMBOL(Terminate),
|
||||
SYMBOL(GetCurrentSurface),
|
||||
SYMBOL(GetCurrentContext),
|
||||
SYMBOL(MakeCurrent),
|
||||
|
@ -232,37 +246,43 @@ GLLibraryEGL::EnsureInitialized()
|
|||
"Couldn't find eglQueryStringImplementationANDROID");
|
||||
#endif
|
||||
|
||||
mEGLDisplay = nullptr;
|
||||
mEGLDisplay = GetAndInitDisplay(*this, EGL_DEFAULT_DISPLAY);
|
||||
|
||||
#ifdef XP_WIN
|
||||
// XXX we have no way of knowing if this is ANGLE, or if we're just using
|
||||
// a native EGL on windows. We don't really do the latter right now, so
|
||||
// let's assume it is ANGLE, and try our special types.
|
||||
|
||||
// D3D11 ANGLE only works with OMTC; there's a bug in the non-OMTC layer
|
||||
// manager, and it's pointless to try to fix it. We also don't try D3D11
|
||||
// ANGLE if the layer manager is prefering D3D9 (hrm, do we care?)
|
||||
if (gfxPrefs::LayersOffMainThreadCompositionEnabled() &&
|
||||
!gfxPrefs::LayersPreferD3D9())
|
||||
const char* vendor = (char*)fQueryString(mEGLDisplay, LOCAL_EGL_VENDOR);
|
||||
if (vendor && (strstr(vendor, "TransGaming") != 0 ||
|
||||
strstr(vendor, "Google Inc.") != 0))
|
||||
{
|
||||
if (gfxPrefs::WebGLANGLEForceD3D11()) {
|
||||
mEGLDisplay = fGetDisplay(LOCAL_EGL_D3D11_ONLY_DISPLAY_ANGLE);
|
||||
} else if (gfxPrefs::WebGLANGLETryD3D11()) {
|
||||
mEGLDisplay = fGetDisplay(LOCAL_EGL_D3D11_ELSE_D3D9_DISPLAY_ANGLE);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!mEGLDisplay)
|
||||
mEGLDisplay = fGetDisplay(EGL_DEFAULT_DISPLAY);
|
||||
if (!fInitialize(mEGLDisplay, nullptr, nullptr))
|
||||
return false;
|
||||
|
||||
const char *vendor = (const char*) fQueryString(mEGLDisplay, LOCAL_EGL_VENDOR);
|
||||
if (vendor && (strstr(vendor, "TransGaming") != 0 || strstr(vendor, "Google Inc.") != 0)) {
|
||||
mIsANGLE = true;
|
||||
}
|
||||
|
||||
if (mIsANGLE) {
|
||||
EGLDisplay newDisplay = EGL_NO_DISPLAY;
|
||||
|
||||
// D3D11 ANGLE only works with OMTC; there's a bug in the non-OMTC layer
|
||||
// manager, and it's pointless to try to fix it. We also don't try
|
||||
// D3D11 ANGLE if the layer manager is prefering D3D9 (hrm, do we care?)
|
||||
if (gfxPrefs::LayersOffMainThreadCompositionEnabled() &&
|
||||
!gfxPrefs::LayersPreferD3D9())
|
||||
{
|
||||
if (gfxPrefs::WebGLANGLEForceD3D11()) {
|
||||
newDisplay = GetAndInitDisplay(*this,
|
||||
LOCAL_EGL_D3D11_ONLY_DISPLAY_ANGLE);
|
||||
} else if (gfxPrefs::WebGLANGLETryD3D11()) {
|
||||
newDisplay = GetAndInitDisplay(*this,
|
||||
LOCAL_EGL_D3D11_ELSE_D3D9_DISPLAY_ANGLE);
|
||||
}
|
||||
}
|
||||
|
||||
if (newDisplay != EGL_NO_DISPLAY) {
|
||||
DebugOnly<EGLBoolean> success = fTerminate(mEGLDisplay);
|
||||
MOZ_ASSERT(success == LOCAL_EGL_TRUE);
|
||||
|
||||
mEGLDisplay = newDisplay;
|
||||
|
||||
vendor = (char*)fQueryString(mEGLDisplay, LOCAL_EGL_VENDOR);
|
||||
}
|
||||
}
|
||||
|
||||
InitExtensions();
|
||||
|
||||
GLLibraryLoader::PlatformLookupFunction lookupFunction =
|
||||
|
|
|
@ -104,7 +104,7 @@ namespace gl {
|
|||
class GLLibraryEGL
|
||||
{
|
||||
public:
|
||||
GLLibraryEGL()
|
||||
GLLibraryEGL()
|
||||
: mInitialized(false),
|
||||
mEGLLibrary(nullptr),
|
||||
mIsANGLE(false)
|
||||
|
@ -154,6 +154,14 @@ public:
|
|||
return disp;
|
||||
}
|
||||
|
||||
EGLBoolean fTerminate(EGLDisplay display)
|
||||
{
|
||||
BEFORE_GL_CALL;
|
||||
EGLBoolean ret = mSymbols.fTerminate(display);
|
||||
AFTER_GL_CALL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
EGLSurface fGetCurrentSurface(EGLint id)
|
||||
{
|
||||
BEFORE_GL_CALL;
|
||||
|
@ -469,6 +477,8 @@ public:
|
|||
struct {
|
||||
typedef EGLDisplay (GLAPIENTRY * pfnGetDisplay)(void *display_id);
|
||||
pfnGetDisplay fGetDisplay;
|
||||
typedef EGLBoolean (GLAPIENTRY * pfnTerminate)(EGLDisplay dpy);
|
||||
pfnTerminate fTerminate;
|
||||
typedef EGLSurface (GLAPIENTRY * pfnGetCurrentSurface)(EGLint);
|
||||
pfnGetCurrentSurface fGetCurrentSurface;
|
||||
typedef EGLContext (GLAPIENTRY * pfnGetCurrentContext)(void);
|
||||
|
|
|
@ -289,7 +289,9 @@ ChooseConfig(GLContext* gl, GLLibraryEGL* egl, const SurfaceCaps& caps)
|
|||
EGLConfig config = EGL_NO_CONFIG;
|
||||
for (int i = 0; i < foundConfigs; i++) {
|
||||
EGLConfig cur = configs[i];
|
||||
if (!DoesAttribBitsMatchCapBool(egl, cur, LOCAL_EGL_DEPTH_SIZE,
|
||||
if (!DoesAttribBitsMatchCapBool(egl, cur, LOCAL_EGL_ALPHA_SIZE,
|
||||
caps.alpha) ||
|
||||
!DoesAttribBitsMatchCapBool(egl, cur, LOCAL_EGL_DEPTH_SIZE,
|
||||
caps.depth) ||
|
||||
!DoesAttribBitsMatchCapBool(egl, cur, LOCAL_EGL_STENCIL_SIZE,
|
||||
caps.stencil))
|
||||
|
@ -297,16 +299,6 @@ ChooseConfig(GLContext* gl, GLLibraryEGL* egl, const SurfaceCaps& caps)
|
|||
continue;
|
||||
}
|
||||
|
||||
// We can't enforce alpha on ANGLE yet because of:
|
||||
// https://code.google.com/p/angleproject/issues/detail?id=764
|
||||
if (!gl->IsANGLE()) {
|
||||
if (!DoesAttribBitsMatchCapBool(egl, cur, LOCAL_EGL_ALPHA_SIZE,
|
||||
caps.alpha))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
config = cur;
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -48,6 +48,8 @@ SharedSurface_Basic::Create(GLContext* gl,
|
|||
break;
|
||||
case LOCAL_GL_RGBA:
|
||||
case LOCAL_GL_RGBA8:
|
||||
case LOCAL_GL_BGRA:
|
||||
case LOCAL_GL_BGRA8_EXT:
|
||||
format = SurfaceFormat::B8G8R8A8;
|
||||
break;
|
||||
default:
|
||||
|
|
|
@ -193,5 +193,175 @@ Compositor::DrawDiagnosticsInternal(DiagnosticFlags aFlags,
|
|||
aTransform);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace
|
||||
static float
|
||||
WrapTexCoord(float v)
|
||||
{
|
||||
// fmodf gives negative results for negative numbers;
|
||||
// that is, fmodf(0.75, 1.0) == 0.75, but
|
||||
// fmodf(-0.75, 1.0) == -0.75. For the negative case,
|
||||
// the result we need is 0.25, so we add 1.0f.
|
||||
if (v < 0.0f) {
|
||||
return 1.0f + fmodf(v, 1.0f);
|
||||
}
|
||||
|
||||
return fmodf(v, 1.0f);
|
||||
}
|
||||
|
||||
static void
|
||||
SetRects(size_t n,
|
||||
decomposedRectArrayT* aLayerRects,
|
||||
decomposedRectArrayT* aTextureRects,
|
||||
float x0, float y0, float x1, float y1,
|
||||
float tx0, float ty0, float tx1, float ty1,
|
||||
bool flip_y)
|
||||
{
|
||||
if (flip_y) {
|
||||
std::swap(ty0, ty1);
|
||||
}
|
||||
(*aLayerRects)[n] = gfx::Rect(x0, y0, x1 - x0, y1 - y0);
|
||||
(*aTextureRects)[n] = gfx::Rect(tx0, ty0, tx1 - tx0, ty1 - ty0);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
static inline bool
|
||||
FuzzyEqual(float a, float b)
|
||||
{
|
||||
return fabs(a - b) < 0.0001f;
|
||||
}
|
||||
static inline bool
|
||||
FuzzyLTE(float a, float b)
|
||||
{
|
||||
return a <= b + 0.0001f;
|
||||
}
|
||||
#endif
|
||||
|
||||
size_t
|
||||
DecomposeIntoNoRepeatRects(const gfx::Rect& aRect,
|
||||
const gfx::Rect& aTexCoordRect,
|
||||
decomposedRectArrayT* aLayerRects,
|
||||
decomposedRectArrayT* aTextureRects)
|
||||
{
|
||||
gfx::Rect texCoordRect = aTexCoordRect;
|
||||
|
||||
// If the texture should be flipped, it will have negative height. Detect that
|
||||
// here and compensate for it. We will flip each rect as we emit it.
|
||||
bool flipped = false;
|
||||
if (texCoordRect.height < 0) {
|
||||
flipped = true;
|
||||
texCoordRect.y += texCoordRect.height;
|
||||
texCoordRect.height = -texCoordRect.height;
|
||||
}
|
||||
|
||||
// Wrap the texture coordinates so they are within [0,1] and cap width/height
|
||||
// at 1. We rely on this below.
|
||||
texCoordRect = gfx::Rect(gfx::Point(WrapTexCoord(texCoordRect.x),
|
||||
WrapTexCoord(texCoordRect.y)),
|
||||
gfx::Size(std::min(texCoordRect.width, 1.0f),
|
||||
std::min(texCoordRect.height, 1.0f)));
|
||||
|
||||
NS_ASSERTION(texCoordRect.x >= 0.0f && texCoordRect.x <= 1.0f &&
|
||||
texCoordRect.y >= 0.0f && texCoordRect.y <= 1.0f &&
|
||||
texCoordRect.width >= 0.0f && texCoordRect.width <= 1.0f &&
|
||||
texCoordRect.height >= 0.0f && texCoordRect.height <= 1.0f &&
|
||||
texCoordRect.XMost() >= 0.0f && texCoordRect.XMost() <= 2.0f &&
|
||||
texCoordRect.YMost() >= 0.0f && texCoordRect.YMost() <= 2.0f,
|
||||
"We just wrapped the texture coordinates, didn't we?");
|
||||
|
||||
// Get the top left and bottom right points of the rectangle. Note that
|
||||
// tl.x/tl.y are within [0,1] but br.x/br.y are within [0,2].
|
||||
gfx::Point tl = texCoordRect.TopLeft();
|
||||
gfx::Point br = texCoordRect.BottomRight();
|
||||
|
||||
NS_ASSERTION(tl.x >= 0.0f && tl.x <= 1.0f &&
|
||||
tl.y >= 0.0f && tl.y <= 1.0f &&
|
||||
br.x >= tl.x && br.x <= 2.0f &&
|
||||
br.y >= tl.y && br.y <= 2.0f &&
|
||||
FuzzyLTE(br.x - tl.x, 1.0f) &&
|
||||
FuzzyLTE(br.y - tl.y, 1.0f),
|
||||
"Somehow generated invalid texture coordinates");
|
||||
|
||||
// Then check if we wrap in either the x or y axis.
|
||||
bool xwrap = br.x > 1.0f;
|
||||
bool ywrap = br.y > 1.0f;
|
||||
|
||||
// If xwrap is false, the texture will be sampled from tl.x .. br.x.
|
||||
// If xwrap is true, then it will be split into tl.x .. 1.0, and
|
||||
// 0.0 .. WrapTexCoord(br.x). Same for the Y axis. The destination
|
||||
// rectangle is also split appropriately, according to the calculated
|
||||
// xmid/ymid values.
|
||||
if (!xwrap && !ywrap) {
|
||||
SetRects(0, aLayerRects, aTextureRects,
|
||||
aRect.x, aRect.y, aRect.XMost(), aRect.YMost(),
|
||||
tl.x, tl.y, br.x, br.y,
|
||||
flipped);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// If we are dealing with wrapping br.x and br.y are greater than 1.0 so
|
||||
// wrap them here as well.
|
||||
br = gfx::Point(xwrap ? WrapTexCoord(br.x) : br.x,
|
||||
ywrap ? WrapTexCoord(br.y) : br.y);
|
||||
|
||||
// If we wrap around along the x axis, we will draw first from
|
||||
// tl.x .. 1.0 and then from 0.0 .. br.x (which we just wrapped above).
|
||||
// The same applies for the Y axis. The midpoints we calculate here are
|
||||
// only valid if we actually wrap around.
|
||||
GLfloat xmid = aRect.x + (1.0f - tl.x) / texCoordRect.width * aRect.width;
|
||||
GLfloat ymid = aRect.y + (1.0f - tl.y) / texCoordRect.height * aRect.height;
|
||||
|
||||
NS_ASSERTION(!xwrap ||
|
||||
(xmid > aRect.x &&
|
||||
xmid < aRect.XMost() &&
|
||||
FuzzyEqual((xmid - aRect.x) + (aRect.XMost() - xmid), aRect.width)),
|
||||
"xmid should be within [x,XMost()] and the wrapped rect should have the same width");
|
||||
NS_ASSERTION(!ywrap ||
|
||||
(ymid > aRect.y &&
|
||||
ymid < aRect.YMost() &&
|
||||
FuzzyEqual((ymid - aRect.y) + (aRect.YMost() - ymid), aRect.height)),
|
||||
"ymid should be within [y,YMost()] and the wrapped rect should have the same height");
|
||||
|
||||
if (!xwrap && ywrap) {
|
||||
SetRects(0, aLayerRects, aTextureRects,
|
||||
aRect.x, aRect.y, aRect.XMost(), ymid,
|
||||
tl.x, tl.y, br.x, 1.0f,
|
||||
flipped);
|
||||
SetRects(1, aLayerRects, aTextureRects,
|
||||
aRect.x, ymid, aRect.XMost(), aRect.YMost(),
|
||||
tl.x, 0.0f, br.x, br.y,
|
||||
flipped);
|
||||
return 2;
|
||||
}
|
||||
|
||||
if (xwrap && !ywrap) {
|
||||
SetRects(0, aLayerRects, aTextureRects,
|
||||
aRect.x, aRect.y, xmid, aRect.YMost(),
|
||||
tl.x, tl.y, 1.0f, br.y,
|
||||
flipped);
|
||||
SetRects(1, aLayerRects, aTextureRects,
|
||||
xmid, aRect.y, aRect.XMost(), aRect.YMost(),
|
||||
0.0f, tl.y, br.x, br.y,
|
||||
flipped);
|
||||
return 2;
|
||||
}
|
||||
|
||||
SetRects(0, aLayerRects, aTextureRects,
|
||||
aRect.x, aRect.y, xmid, ymid,
|
||||
tl.x, tl.y, 1.0f, 1.0f,
|
||||
flipped);
|
||||
SetRects(1, aLayerRects, aTextureRects,
|
||||
xmid, aRect.y, aRect.XMost(), ymid,
|
||||
0.0f, tl.y, br.x, 1.0f,
|
||||
flipped);
|
||||
SetRects(2, aLayerRects, aTextureRects,
|
||||
aRect.x, ymid, xmid, aRect.YMost(),
|
||||
tl.x, 0.0f, 1.0f, br.y,
|
||||
flipped);
|
||||
SetRects(3, aLayerRects, aTextureRects,
|
||||
xmid, ymid, aRect.XMost(), aRect.YMost(),
|
||||
0.0f, 0.0f, br.x, br.y,
|
||||
flipped);
|
||||
return 4;
|
||||
}
|
||||
|
||||
} // namespace layers
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -540,6 +540,13 @@ private:
|
|||
|
||||
};
|
||||
|
||||
// Returns the number of rects. (Up to 4)
|
||||
typedef gfx::Rect decomposedRectArrayT[4];
|
||||
size_t DecomposeIntoNoRepeatRects(const gfx::Rect& aRect,
|
||||
const gfx::Rect& aTexCoordRect,
|
||||
decomposedRectArrayT* aLayerRects,
|
||||
decomposedRectArrayT* aTextureRects);
|
||||
|
||||
} // namespace layers
|
||||
} // namespace mozilla
|
||||
|
||||
|
|
|
@ -210,7 +210,6 @@ CompositorD3D11::Initialize()
|
|||
}
|
||||
|
||||
CD3D11_SAMPLER_DESC samplerDesc(D3D11_DEFAULT);
|
||||
samplerDesc.AddressU = samplerDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
|
||||
hr = mDevice->CreateSamplerState(&samplerDesc, byRef(mAttachments->mLinearSamplerState));
|
||||
if (FAILED(hr)) {
|
||||
return false;
|
||||
|
@ -574,7 +573,6 @@ CompositorD3D11::DrawQuad(const gfx::Rect& aRect,
|
|||
IntPoint origin = mCurrentRT->GetOrigin();
|
||||
mVSConstants.renderTargetOffset[0] = origin.x;
|
||||
mVSConstants.renderTargetOffset[1] = origin.y;
|
||||
mVSConstants.layerQuad = aRect;
|
||||
|
||||
mPSConstants.layerOpacity[0] = aOpacity;
|
||||
|
||||
|
@ -627,6 +625,7 @@ CompositorD3D11::DrawQuad(const gfx::Rect& aRect,
|
|||
mContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
|
||||
mContext->VSSetShader(mAttachments->mVSQuadShader[maskType], nullptr, 0);
|
||||
|
||||
const Rect* pTexCoordRect = nullptr;
|
||||
|
||||
switch (aEffectChain.mPrimaryEffect->mType) {
|
||||
case EffectTypes::SOLID_COLOR: {
|
||||
|
@ -646,7 +645,7 @@ CompositorD3D11::DrawQuad(const gfx::Rect& aRect,
|
|||
TexturedEffect* texturedEffect =
|
||||
static_cast<TexturedEffect*>(aEffectChain.mPrimaryEffect.get());
|
||||
|
||||
mVSConstants.textureCoords = texturedEffect->mTextureCoords;
|
||||
pTexCoordRect = &texturedEffect->mTextureCoords;
|
||||
|
||||
TextureSourceD3D11* source = texturedEffect->mTexture->AsSourceD3D11();
|
||||
|
||||
|
@ -682,7 +681,7 @@ CompositorD3D11::DrawQuad(const gfx::Rect& aRect,
|
|||
|
||||
SetSamplerForFilter(Filter::LINEAR);
|
||||
|
||||
mVSConstants.textureCoords = ycbcrEffect->mTextureCoords;
|
||||
pTexCoordRect = &ycbcrEffect->mTextureCoords;
|
||||
|
||||
const int Y = 0, Cb = 1, Cr = 2;
|
||||
TextureSource* source = ycbcrEffect->mTexture;
|
||||
|
@ -749,7 +748,8 @@ CompositorD3D11::DrawQuad(const gfx::Rect& aRect,
|
|||
|
||||
SetSamplerForFilter(effectComponentAlpha->mFilter);
|
||||
|
||||
mVSConstants.textureCoords = effectComponentAlpha->mTextureCoords;
|
||||
pTexCoordRect = &effectComponentAlpha->mTextureCoords;
|
||||
|
||||
RefPtr<ID3D11ShaderResourceView> views[2];
|
||||
|
||||
HRESULT hr;
|
||||
|
@ -774,12 +774,34 @@ CompositorD3D11::DrawQuad(const gfx::Rect& aRect,
|
|||
NS_WARNING("Unknown shader type");
|
||||
return;
|
||||
}
|
||||
if (!UpdateConstantBuffers()) {
|
||||
NS_WARNING("Failed to update shader constant buffers");
|
||||
return;
|
||||
|
||||
if (pTexCoordRect) {
|
||||
Rect layerRects[4];
|
||||
Rect textureRects[4];
|
||||
size_t rects = DecomposeIntoNoRepeatRects(aRect,
|
||||
*pTexCoordRect,
|
||||
&layerRects,
|
||||
&textureRects);
|
||||
for (size_t i = 0; i < rects; i++) {
|
||||
mVSConstants.layerQuad = layerRects[i];
|
||||
mVSConstants.textureCoords = textureRects[i];
|
||||
|
||||
if (!UpdateConstantBuffers()) {
|
||||
NS_WARNING("Failed to update shader constant buffers");
|
||||
break;
|
||||
}
|
||||
mContext->Draw(4, 0);
|
||||
}
|
||||
} else {
|
||||
mVSConstants.layerQuad = aRect;
|
||||
|
||||
if (!UpdateConstantBuffers()) {
|
||||
NS_WARNING("Failed to update shader constant buffers");
|
||||
} else {
|
||||
mContext->Draw(4, 0);
|
||||
}
|
||||
}
|
||||
|
||||
mContext->Draw(4, 0);
|
||||
if (restoreBlendMode) {
|
||||
mContext->OMSetBlendState(mAttachments->mPremulBlendState, sBlendFactor, 0xFFFFFFFF);
|
||||
}
|
||||
|
@ -860,7 +882,7 @@ CompositorD3D11::EndFrame()
|
|||
PaintToTarget();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
mCurrentRT = nullptr;
|
||||
}
|
||||
|
||||
|
|
|
@ -383,171 +383,6 @@ CompositorOGL::Initialize()
|
|||
return true;
|
||||
}
|
||||
|
||||
static GLfloat
|
||||
WrapTexCoord(GLfloat v)
|
||||
{
|
||||
// fmodf gives negative results for negative numbers;
|
||||
// that is, fmodf(0.75, 1.0) == 0.75, but
|
||||
// fmodf(-0.75, 1.0) == -0.75. For the negative case,
|
||||
// the result we need is 0.25, so we add 1.0f.
|
||||
if (v < 0.0f) {
|
||||
return 1.0f + fmodf(v, 1.0f);
|
||||
}
|
||||
|
||||
return fmodf(v, 1.0f);
|
||||
}
|
||||
|
||||
static void
|
||||
SetRects(int n,
|
||||
Rect* aLayerRects,
|
||||
Rect* aTextureRects,
|
||||
GLfloat x0, GLfloat y0, GLfloat x1, GLfloat y1,
|
||||
GLfloat tx0, GLfloat ty0, GLfloat tx1, GLfloat ty1,
|
||||
bool flip_y /* = false */)
|
||||
{
|
||||
if (flip_y) {
|
||||
std::swap(ty0, ty1);
|
||||
}
|
||||
aLayerRects[n] = Rect(x0, y0, x1 - x0, y1 - y0);
|
||||
aTextureRects[n] = Rect(tx0, ty0, tx1 - tx0, ty1 - ty0);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
static inline bool
|
||||
FuzzyEqual(float a, float b)
|
||||
{
|
||||
return fabs(a - b) < 0.0001f;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int
|
||||
DecomposeIntoNoRepeatRects(const Rect& aRect,
|
||||
const Rect& aTexCoordRect,
|
||||
Rect* aLayerRects,
|
||||
Rect* aTextureRects)
|
||||
{
|
||||
Rect texCoordRect = aTexCoordRect;
|
||||
|
||||
// If the texture should be flipped, it will have negative height. Detect that
|
||||
// here and compensate for it. We will flip each rect as we emit it.
|
||||
bool flipped = false;
|
||||
if (texCoordRect.height < 0) {
|
||||
flipped = true;
|
||||
texCoordRect.y += texCoordRect.height;
|
||||
texCoordRect.height = -texCoordRect.height;
|
||||
}
|
||||
|
||||
// Wrap the texture coordinates so they are within [0,1] and cap width/height
|
||||
// at 1. We rely on this below.
|
||||
texCoordRect = Rect(Point(WrapTexCoord(texCoordRect.x),
|
||||
WrapTexCoord(texCoordRect.y)),
|
||||
Size(std::min(texCoordRect.width, 1.0f),
|
||||
std::min(texCoordRect.height, 1.0f)));
|
||||
|
||||
NS_ASSERTION(texCoordRect.x >= 0.0f && texCoordRect.x <= 1.0f &&
|
||||
texCoordRect.y >= 0.0f && texCoordRect.y <= 1.0f &&
|
||||
texCoordRect.width >= 0.0f && texCoordRect.width <= 1.0f &&
|
||||
texCoordRect.height >= 0.0f && texCoordRect.height <= 1.0f &&
|
||||
texCoordRect.XMost() >= 0.0f && texCoordRect.XMost() <= 2.0f &&
|
||||
texCoordRect.YMost() >= 0.0f && texCoordRect.YMost() <= 2.0f,
|
||||
"We just wrapped the texture coordinates, didn't we?");
|
||||
|
||||
// Get the top left and bottom right points of the rectangle. Note that
|
||||
// tl.x/tl.y are within [0,1] but br.x/br.y are within [0,2].
|
||||
Point tl = texCoordRect.TopLeft();
|
||||
Point br = texCoordRect.BottomRight();
|
||||
|
||||
NS_ASSERTION(tl.x >= 0.0f && tl.x <= 1.0f &&
|
||||
tl.y >= 0.0f && tl.y <= 1.0f &&
|
||||
br.x >= tl.x && br.x <= 2.0f &&
|
||||
br.y >= tl.y && br.y <= 2.0f &&
|
||||
br.x - tl.x <= 1.0f &&
|
||||
br.y - tl.y <= 1.0f,
|
||||
"Somehow generated invalid texture coordinates");
|
||||
|
||||
// Then check if we wrap in either the x or y axis.
|
||||
bool xwrap = br.x > 1.0f;
|
||||
bool ywrap = br.y > 1.0f;
|
||||
|
||||
// If xwrap is false, the texture will be sampled from tl.x .. br.x.
|
||||
// If xwrap is true, then it will be split into tl.x .. 1.0, and
|
||||
// 0.0 .. WrapTexCoord(br.x). Same for the Y axis. The destination
|
||||
// rectangle is also split appropriately, according to the calculated
|
||||
// xmid/ymid values.
|
||||
if (!xwrap && !ywrap) {
|
||||
SetRects(0, aLayerRects, aTextureRects,
|
||||
aRect.x, aRect.y, aRect.XMost(), aRect.YMost(),
|
||||
tl.x, tl.y, br.x, br.y,
|
||||
flipped);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// If we are dealing with wrapping br.x and br.y are greater than 1.0 so
|
||||
// wrap them here as well.
|
||||
br = Point(xwrap ? WrapTexCoord(br.x) : br.x,
|
||||
ywrap ? WrapTexCoord(br.y) : br.y);
|
||||
|
||||
// If we wrap around along the x axis, we will draw first from
|
||||
// tl.x .. 1.0 and then from 0.0 .. br.x (which we just wrapped above).
|
||||
// The same applies for the Y axis. The midpoints we calculate here are
|
||||
// only valid if we actually wrap around.
|
||||
GLfloat xmid = aRect.x + (1.0f - tl.x) / texCoordRect.width * aRect.width;
|
||||
GLfloat ymid = aRect.y + (1.0f - tl.y) / texCoordRect.height * aRect.height;
|
||||
|
||||
NS_ASSERTION(!xwrap ||
|
||||
(xmid > aRect.x &&
|
||||
xmid < aRect.XMost() &&
|
||||
FuzzyEqual((xmid - aRect.x) + (aRect.XMost() - xmid), aRect.width)),
|
||||
"xmid should be within [x,XMost()] and the wrapped rect should have the same width");
|
||||
NS_ASSERTION(!ywrap ||
|
||||
(ymid > aRect.y &&
|
||||
ymid < aRect.YMost() &&
|
||||
FuzzyEqual((ymid - aRect.y) + (aRect.YMost() - ymid), aRect.height)),
|
||||
"ymid should be within [y,YMost()] and the wrapped rect should have the same height");
|
||||
|
||||
if (!xwrap && ywrap) {
|
||||
SetRects(0, aLayerRects, aTextureRects,
|
||||
aRect.x, aRect.y, aRect.XMost(), ymid,
|
||||
tl.x, tl.y, br.x, 1.0f,
|
||||
flipped);
|
||||
SetRects(1, aLayerRects, aTextureRects,
|
||||
aRect.x, ymid, aRect.XMost(), aRect.YMost(),
|
||||
tl.x, 0.0f, br.x, br.y,
|
||||
flipped);
|
||||
return 2;
|
||||
}
|
||||
|
||||
if (xwrap && !ywrap) {
|
||||
SetRects(0, aLayerRects, aTextureRects,
|
||||
aRect.x, aRect.y, xmid, aRect.YMost(),
|
||||
tl.x, tl.y, 1.0f, br.y,
|
||||
flipped);
|
||||
SetRects(1, aLayerRects, aTextureRects,
|
||||
xmid, aRect.y, aRect.XMost(), aRect.YMost(),
|
||||
0.0f, tl.y, br.x, br.y,
|
||||
flipped);
|
||||
return 2;
|
||||
}
|
||||
|
||||
SetRects(0, aLayerRects, aTextureRects,
|
||||
aRect.x, aRect.y, xmid, ymid,
|
||||
tl.x, tl.y, 1.0f, 1.0f,
|
||||
flipped);
|
||||
SetRects(1, aLayerRects, aTextureRects,
|
||||
xmid, aRect.y, aRect.XMost(), ymid,
|
||||
0.0f, tl.y, br.x, 1.0f,
|
||||
flipped);
|
||||
SetRects(2, aLayerRects, aTextureRects,
|
||||
aRect.x, ymid, xmid, aRect.YMost(),
|
||||
tl.x, 0.0f, 1.0f, br.y,
|
||||
flipped);
|
||||
SetRects(3, aLayerRects, aTextureRects,
|
||||
xmid, ymid, aRect.XMost(), aRect.YMost(),
|
||||
0.0f, 0.0f, br.x, br.y,
|
||||
flipped);
|
||||
return 4;
|
||||
}
|
||||
|
||||
// |aRect| is the rectangle we want to draw to. We will draw it with
|
||||
// up to 4 draw commands if necessary to avoid wrapping.
|
||||
// |aTexCoordRect| is the rectangle from the texture that we want to
|
||||
|
@ -562,10 +397,10 @@ CompositorOGL::BindAndDrawQuadWithTextureRect(ShaderProgramOGL *aProg,
|
|||
{
|
||||
Rect layerRects[4];
|
||||
Rect textureRects[4];
|
||||
int rects = DecomposeIntoNoRepeatRects(aRect,
|
||||
aTexCoordRect,
|
||||
layerRects,
|
||||
textureRects);
|
||||
size_t rects = DecomposeIntoNoRepeatRects(aRect,
|
||||
aTexCoordRect,
|
||||
&layerRects,
|
||||
&textureRects);
|
||||
BindAndDrawQuads(aProg, rects, layerRects, textureRects);
|
||||
}
|
||||
|
||||
|
|
|
@ -392,7 +392,9 @@ gfxWindowsPlatform::UpdateRenderMode()
|
|||
}
|
||||
|
||||
ID3D11Device *device = GetD3D11Device();
|
||||
if (isVistaOrHigher && !safeMode && tryD2D && device &&
|
||||
if (isVistaOrHigher && !safeMode && tryD2D &&
|
||||
device &&
|
||||
device->GetFeatureLevel() >= D3D_FEATURE_LEVEL_10_0 &&
|
||||
DoesD3D11DeviceSupportResourceSharing(device)) {
|
||||
|
||||
VerifyD2DDevice(d2dForceEnabled);
|
||||
|
|
|
@ -351,7 +351,11 @@ struct ClassExtension
|
|||
*
|
||||
* There may exist weak pointers to an object that are not traced through
|
||||
* when the normal trace APIs are used, for example objects in the wrapper
|
||||
* cache. This hook allows these pointers to be updated.
|
||||
* cache. This hook allows these pointers to be updated.
|
||||
*
|
||||
* Note that this hook can be called before JS_NewObject() returns if a GC
|
||||
* is triggered during construction of the object. This can happen for
|
||||
* global objects for example.
|
||||
*/
|
||||
JSObjectMovedOp objectMovedOp;
|
||||
};
|
||||
|
|
|
@ -64,6 +64,7 @@ var ignoreClasses = {
|
|||
"PRIOMethods": true,
|
||||
"XPCOMFunctions" : true, // I'm a little unsure of this one
|
||||
"_MD_IOVector" : true,
|
||||
"malloc_table_t": true, // replace_malloc
|
||||
};
|
||||
|
||||
// Ignore calls through TYPE.FIELD, where TYPE is the class or struct name containing
|
||||
|
|
|
@ -160,6 +160,7 @@ EmitCheck(ExclusiveContext *cx, BytecodeEmitter *bce, ptrdiff_t delta)
|
|||
ptrdiff_t offset = bce->code().length();
|
||||
|
||||
// Start it off moderately large to avoid repeated resizings early on.
|
||||
// ~98% of cases fit within 1024 bytes.
|
||||
if (bce->code().capacity() == 0 && !bce->code().reserve(1024))
|
||||
return -1;
|
||||
|
||||
|
@ -2225,8 +2226,6 @@ EmitFinishIteratorResult(ExclusiveContext *cx, BytecodeEmitter *bce, bool done)
|
|||
return false;
|
||||
if (!EmitIndex32(cx, JSOP_INITPROP, done_id, bce))
|
||||
return false;
|
||||
if (Emit1(cx, bce, JSOP_ENDINIT) < 0)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -3387,8 +3386,6 @@ EmitDestructuringOpsArrayHelper(ExclusiveContext *cx, BytecodeEmitter *bce, Pars
|
|||
return false;
|
||||
if (Emit1(cx, bce, JSOP_POP) < 0) // ... OBJ? ARRAY
|
||||
return false;
|
||||
if (Emit1(cx, bce, JSOP_ENDINIT) < 0)
|
||||
return false;
|
||||
needToPopIterator = false;
|
||||
} else {
|
||||
if (Emit1(cx, bce, JSOP_DUP) < 0) // ... OBJ? ITER ITER
|
||||
|
@ -6421,9 +6418,6 @@ EmitObject(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
|||
}
|
||||
}
|
||||
|
||||
if (Emit1(cx, bce, JSOP_ENDINIT) < 0)
|
||||
return false;
|
||||
|
||||
if (obj) {
|
||||
/*
|
||||
* The object survived and has a predictable shape: update the original
|
||||
|
@ -6466,8 +6460,7 @@ EmitArrayComp(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
|||
return false;
|
||||
bce->arrayCompDepth = saveDepth;
|
||||
|
||||
/* Emit the usual op needed for decompilation. */
|
||||
return Emit1(cx, bce, JSOP_ENDINIT) >= 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -6555,9 +6548,7 @@ EmitArray(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, uint32_t co
|
|||
if (Emit1(cx, bce, JSOP_POP) < 0) // ARRAY
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Emit an op to finish the array and aid in decompilation. */
|
||||
return Emit1(cx, bce, JSOP_ENDINIT) >= 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
|
@ -7082,7 +7073,8 @@ static int
|
|||
AllocSrcNote(ExclusiveContext *cx, SrcNotesVector ¬es)
|
||||
{
|
||||
// Start it off moderately large to avoid repeated resizings early on.
|
||||
if (notes.capacity() == 0 && !notes.reserve(1024))
|
||||
// ~99% of cases fit within 256 bytes.
|
||||
if (notes.capacity() == 0 && !notes.reserve(256))
|
||||
return -1;
|
||||
|
||||
jssrcnote dummy = 0;
|
||||
|
|
|
@ -26,6 +26,8 @@
|
|||
|
||||
namespace js {
|
||||
|
||||
class AutoLockGC;
|
||||
|
||||
namespace gc {
|
||||
|
||||
typedef Vector<JS::Zone *, 4, SystemAllocPolicy> ZoneVector;
|
||||
|
@ -64,6 +66,24 @@ class ChunkPool
|
|||
};
|
||||
};
|
||||
|
||||
// Performs extra allocation off the main thread so that when memory is
|
||||
// required on the main thread it will already be available and waiting.
|
||||
class BackgroundAllocTask : public GCParallelTask
|
||||
{
|
||||
// Guarded by the GC lock.
|
||||
JSRuntime *runtime;
|
||||
ChunkPool &chunkPool_;
|
||||
|
||||
const bool enabled_;
|
||||
|
||||
public:
|
||||
BackgroundAllocTask(JSRuntime *rt, ChunkPool &pool);
|
||||
bool enabled() const { return enabled_; }
|
||||
|
||||
protected:
|
||||
virtual void run() MOZ_OVERRIDE;
|
||||
};
|
||||
|
||||
/*
|
||||
* Encapsulates all of the GC tunables. These are effectively constant and
|
||||
* should only be modified by setParameter.
|
||||
|
@ -257,7 +277,7 @@ class GCRuntime
|
|||
void minorGC(JS::gcreason::Reason reason);
|
||||
void minorGC(JSContext *cx, JS::gcreason::Reason reason);
|
||||
void evictNursery(JS::gcreason::Reason reason = JS::gcreason::EVICT_NURSERY) { minorGC(reason); }
|
||||
void gcIfNeeded(JSContext *cx);
|
||||
bool gcIfNeeded(JSContext *cx = nullptr);
|
||||
void gc(JSGCInvocationKind gckind, JS::gcreason::Reason reason);
|
||||
void gcSlice(JSGCInvocationKind gckind, JS::gcreason::Reason reason, int64_t millis = 0);
|
||||
void gcFinalSlice(JSGCInvocationKind gckind, JS::gcreason::Reason reason);
|
||||
|
@ -301,8 +321,14 @@ class GCRuntime
|
|||
js::gc::State state() { return incrementalState; }
|
||||
bool isBackgroundSweeping() { return helperState.isBackgroundSweeping(); }
|
||||
void waitBackgroundSweepEnd() { helperState.waitBackgroundSweepEnd(); }
|
||||
void waitBackgroundSweepOrAllocEnd() { helperState.waitBackgroundSweepOrAllocEnd(); }
|
||||
void startBackgroundAllocationIfIdle() { helperState.startBackgroundAllocationIfIdle(); }
|
||||
void waitBackgroundSweepOrAllocEnd() {
|
||||
helperState.waitBackgroundSweepEnd();
|
||||
allocTask.cancel(GCParallelTask::CancelAndWait);
|
||||
}
|
||||
|
||||
#ifdef JSGC_GENERATIONAL
|
||||
void requestMinorGC(JS::gcreason::Reason reason);
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
|
@ -414,7 +440,7 @@ class GCRuntime
|
|||
bool areGrayBitsValid() { return grayBitsValid; }
|
||||
void setGrayBitsInvalid() { grayBitsValid = false; }
|
||||
|
||||
bool isGcNeeded() { return isNeeded; }
|
||||
bool isGcNeeded() { return minorGCRequested || majorGCRequested; }
|
||||
|
||||
double computeHeapGrowthFactor(size_t lastBytes);
|
||||
size_t computeTriggerBytes(double growthFactor, size_t lastBytes);
|
||||
|
@ -451,7 +477,8 @@ class GCRuntime
|
|||
private:
|
||||
// For ArenaLists::allocateFromArena()
|
||||
friend class ArenaLists;
|
||||
Chunk *pickChunk(Zone *zone, AutoMaybeStartBackgroundAllocation &maybeStartBGAlloc);
|
||||
Chunk *pickChunk(const AutoLockGC &lock, Zone *zone,
|
||||
AutoMaybeStartBackgroundAllocation &maybeStartBGAlloc);
|
||||
inline void arenaAllocatedDuringGC(JS::Zone *zone, ArenaHeader *arena);
|
||||
|
||||
template <AllowGC allowGC>
|
||||
|
@ -469,10 +496,13 @@ class GCRuntime
|
|||
void prepareToFreeChunk(ChunkInfo &info);
|
||||
void releaseChunk(Chunk *chunk);
|
||||
|
||||
inline bool wantBackgroundAllocation() const;
|
||||
friend class BackgroundAllocTask;
|
||||
friend class AutoMaybeStartBackgroundAllocation;
|
||||
inline bool wantBackgroundAllocation(const AutoLockGC &lock) const;
|
||||
void startBackgroundAllocTaskIfIdle();
|
||||
|
||||
bool initZeal();
|
||||
void requestInterrupt(JS::gcreason::Reason reason);
|
||||
void requestMajorGC(JS::gcreason::Reason reason);
|
||||
void collect(bool incremental, int64_t budget, JSGCInvocationKind gckind,
|
||||
JS::gcreason::Reason reason);
|
||||
bool gcCycle(bool incremental, int64_t budget, JSGCInvocationKind gckind,
|
||||
|
@ -605,12 +635,13 @@ class GCRuntime
|
|||
*/
|
||||
bool grayBitsValid;
|
||||
|
||||
/*
|
||||
* These flags must be kept separate so that a thread requesting a
|
||||
* compartment GC doesn't cancel another thread's concurrent request for a
|
||||
* full GC.
|
||||
*/
|
||||
volatile uintptr_t isNeeded;
|
||||
volatile uintptr_t majorGCRequested;
|
||||
JS::gcreason::Reason majorGCTriggerReason;
|
||||
|
||||
#ifdef JSGC_GENERATIONAL
|
||||
bool minorGCRequested;
|
||||
JS::gcreason::Reason minorGCTriggerReason;
|
||||
#endif
|
||||
|
||||
/* Incremented at the start of every major GC. */
|
||||
uint64_t majorGCNumber;
|
||||
|
@ -633,9 +664,6 @@ class GCRuntime
|
|||
/* The invocation kind of the current GC, taken from the first slice. */
|
||||
JSGCInvocationKind invocationKind;
|
||||
|
||||
/* The reason that an interrupt-triggered GC should be called. */
|
||||
JS::gcreason::Reason triggerReason;
|
||||
|
||||
/*
|
||||
* If this is 0, all cross-compartment proxies must be registered in the
|
||||
* wrapper map. This checking must be disabled temporarily while creating
|
||||
|
@ -840,6 +868,7 @@ class GCRuntime
|
|||
PRLock *lock;
|
||||
mozilla::DebugOnly<PRThread *> lockOwner;
|
||||
|
||||
BackgroundAllocTask allocTask;
|
||||
GCHelperState helperState;
|
||||
|
||||
/*
|
||||
|
|
|
@ -766,7 +766,8 @@ Statistics::beginSlice(const ZoneGCStats &zoneStats, JS::gcreason::Reason reason
|
|||
beginGC();
|
||||
|
||||
SliceData data(reason, PRMJ_Now(), GetPageFaultCount());
|
||||
(void) slices.append(data); /* Ignore any OOMs here. */
|
||||
if (!slices.append(data))
|
||||
CrashAtUnhandlableOOM("Failed to allocate statistics slice.");
|
||||
|
||||
if (JSAccumulateTelemetryDataCallback cb = runtime->telemetryCallback)
|
||||
(*cb)(JS_TELEMETRY_GC_REASON, reason);
|
||||
|
|
|
@ -303,7 +303,7 @@ void
|
|||
StoreBuffer::setAboutToOverflow()
|
||||
{
|
||||
aboutToOverflow_ = true;
|
||||
runtime_->requestInterrupt(JSRuntime::RequestInterruptMainThread);
|
||||
runtime_->gc.requestMinorGC(JS::gcreason::FULL_STORE_BUFFER);
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
@ -1854,12 +1854,6 @@ BaselineCompiler::emit_JSOP_INITPROP()
|
|||
return emitOpIC(compiler.getStub(&stubSpace_));
|
||||
}
|
||||
|
||||
bool
|
||||
BaselineCompiler::emit_JSOP_ENDINIT()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
typedef bool (*NewbornArrayPushFn)(JSContext *, HandleObject, const Value &);
|
||||
static const VMFunction NewbornArrayPushInfo = FunctionInfo<NewbornArrayPushFn>(NewbornArrayPush);
|
||||
|
||||
|
|
|
@ -104,7 +104,6 @@ namespace jit {
|
|||
_(JSOP_INITPROP) \
|
||||
_(JSOP_INITPROP_GETTER) \
|
||||
_(JSOP_INITPROP_SETTER) \
|
||||
_(JSOP_ENDINIT) \
|
||||
_(JSOP_ARRAYPUSH) \
|
||||
_(JSOP_GETELEM) \
|
||||
_(JSOP_SETELEM) \
|
||||
|
|
|
@ -1639,9 +1639,6 @@ IonBuilder::inspectOpcode(JSOp op)
|
|||
case JSOP_INITELEM_SETTER:
|
||||
return jsop_initelem_getter_setter();
|
||||
|
||||
case JSOP_ENDINIT:
|
||||
return true;
|
||||
|
||||
case JSOP_FUNCALL:
|
||||
return jsop_funcall(GET_ARGC(pc));
|
||||
|
||||
|
|
209
js/src/jsgc.cpp
209
js/src/jsgc.cpp
|
@ -1069,18 +1069,29 @@ GCRuntime::moveChunkToFreePool(Chunk *chunk)
|
|||
}
|
||||
|
||||
inline bool
|
||||
GCRuntime::wantBackgroundAllocation() const
|
||||
GCRuntime::wantBackgroundAllocation(const AutoLockGC &lock) const
|
||||
{
|
||||
/*
|
||||
* To minimize memory waste we do not want to run the background chunk
|
||||
* allocation if we have empty chunks or when the runtime needs just few
|
||||
* of them.
|
||||
*/
|
||||
return helperState.canBackgroundAllocate() &&
|
||||
// To minimize memory waste, we do not want to run the background chunk
|
||||
// allocation if we already have some empty chunks or when the runtime has
|
||||
// a small heap size (and therefore likely has a small growth rate).
|
||||
return allocTask.enabled() &&
|
||||
emptyChunks.count() < tunables.minEmptyChunkCount() &&
|
||||
chunkSet.count() >= 4;
|
||||
}
|
||||
|
||||
void
|
||||
GCRuntime::startBackgroundAllocTaskIfIdle()
|
||||
{
|
||||
AutoLockHelperThreadState helperLock;
|
||||
if (allocTask.isRunning())
|
||||
return;
|
||||
|
||||
// Join the previous invocation of the task. This will return immediately
|
||||
// if the thread has never been started.
|
||||
allocTask.joinWithLockHeld();
|
||||
allocTask.startWithLockHeld();
|
||||
}
|
||||
|
||||
class js::gc::AutoMaybeStartBackgroundAllocation
|
||||
{
|
||||
private:
|
||||
|
@ -1099,17 +1110,14 @@ class js::gc::AutoMaybeStartBackgroundAllocation
|
|||
}
|
||||
|
||||
~AutoMaybeStartBackgroundAllocation() {
|
||||
if (runtime && !runtime->currentThreadOwnsInterruptLock()) {
|
||||
AutoLockHelperThreadState helperLock;
|
||||
AutoLockGC lock(runtime);
|
||||
runtime->gc.startBackgroundAllocationIfIdle();
|
||||
}
|
||||
if (runtime && !runtime->currentThreadOwnsInterruptLock())
|
||||
runtime->gc.startBackgroundAllocTaskIfIdle();
|
||||
}
|
||||
};
|
||||
|
||||
/* The caller must hold the GC lock. */
|
||||
Chunk *
|
||||
GCRuntime::pickChunk(Zone *zone, AutoMaybeStartBackgroundAllocation &maybeStartBackgroundAllocation)
|
||||
GCRuntime::pickChunk(const AutoLockGC &lock, Zone *zone,
|
||||
AutoMaybeStartBackgroundAllocation &maybeStartBackgroundAllocation)
|
||||
{
|
||||
Chunk **listHeadp = getAvailableChunkList(zone);
|
||||
Chunk *chunk = *listHeadp;
|
||||
|
@ -1127,7 +1135,7 @@ GCRuntime::pickChunk(Zone *zone, AutoMaybeStartBackgroundAllocation &maybeStartB
|
|||
MOZ_ASSERT(chunk->unused());
|
||||
MOZ_ASSERT(!chunkSet.has(chunk));
|
||||
|
||||
if (wantBackgroundAllocation())
|
||||
if (wantBackgroundAllocation(lock))
|
||||
maybeStartBackgroundAllocation.tryToStartBackgroundAllocation(rt);
|
||||
|
||||
chunkAllocationSinceLastGC = true;
|
||||
|
@ -1173,13 +1181,17 @@ GCRuntime::GCRuntime(JSRuntime *rt) :
|
|||
decommitThreshold(32 * 1024 * 1024),
|
||||
cleanUpEverything(false),
|
||||
grayBitsValid(false),
|
||||
isNeeded(0),
|
||||
majorGCRequested(0),
|
||||
majorGCTriggerReason(JS::gcreason::NO_REASON),
|
||||
#ifdef JSGC_GENERATIONAL
|
||||
minorGCRequested(false),
|
||||
minorGCTriggerReason(JS::gcreason::NO_REASON),
|
||||
#endif
|
||||
majorGCNumber(0),
|
||||
jitReleaseNumber(0),
|
||||
number(0),
|
||||
startNumber(0),
|
||||
isFull(false),
|
||||
triggerReason(JS::gcreason::NO_REASON),
|
||||
#ifdef DEBUG
|
||||
disableStrictProxyCheckingCount(0),
|
||||
#endif
|
||||
|
@ -1229,6 +1241,7 @@ GCRuntime::GCRuntime(JSRuntime *rt) :
|
|||
#endif
|
||||
lock(nullptr),
|
||||
lockOwner(nullptr),
|
||||
allocTask(rt, emptyChunks),
|
||||
helperState(rt)
|
||||
{
|
||||
setGCMode(JSGC_MODE_GLOBAL);
|
||||
|
@ -1970,7 +1983,7 @@ ArenaLists::allocateFromArena(JS::Zone *zone, AllocKind thingKind,
|
|||
if (maybeLock.isNothing())
|
||||
maybeLock.emplace(rt);
|
||||
|
||||
Chunk *chunk = rt->gc.pickChunk(zone, maybeStartBGAlloc);
|
||||
Chunk *chunk = rt->gc.pickChunk(maybeLock.ref(), zone, maybeStartBGAlloc);
|
||||
if (!chunk)
|
||||
return nullptr;
|
||||
|
||||
|
@ -2937,13 +2950,25 @@ js::MarkCompartmentActive(InterpreterFrame *fp)
|
|||
}
|
||||
|
||||
void
|
||||
GCRuntime::requestInterrupt(JS::gcreason::Reason reason)
|
||||
GCRuntime::requestMajorGC(JS::gcreason::Reason reason)
|
||||
{
|
||||
if (isNeeded)
|
||||
if (majorGCRequested)
|
||||
return;
|
||||
|
||||
isNeeded = true;
|
||||
triggerReason = reason;
|
||||
majorGCRequested = true;
|
||||
majorGCTriggerReason = reason;
|
||||
rt->requestInterrupt(JSRuntime::RequestInterruptMainThread);
|
||||
}
|
||||
|
||||
void
|
||||
GCRuntime::requestMinorGC(JS::gcreason::Reason reason)
|
||||
{
|
||||
MOZ_ASSERT(CurrentThreadCanAccessRuntime(rt));
|
||||
if (minorGCRequested)
|
||||
return;
|
||||
|
||||
minorGCRequested = true;
|
||||
minorGCTriggerReason = reason;
|
||||
rt->requestInterrupt(JSRuntime::RequestInterruptMainThread);
|
||||
}
|
||||
|
||||
|
@ -2972,7 +2997,7 @@ GCRuntime::triggerGC(JS::gcreason::Reason reason)
|
|||
return false;
|
||||
|
||||
JS::PrepareForFullGC(rt);
|
||||
requestInterrupt(reason);
|
||||
requestMajorGC(reason);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -3014,7 +3039,7 @@ GCRuntime::triggerZoneGC(Zone *zone, JS::gcreason::Reason reason)
|
|||
}
|
||||
|
||||
PrepareZoneForGC(zone);
|
||||
requestInterrupt(reason);
|
||||
requestMajorGC(reason);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -3031,10 +3056,8 @@ GCRuntime::maybeGC(Zone *zone)
|
|||
}
|
||||
#endif
|
||||
|
||||
if (isNeeded) {
|
||||
gcSlice(GC_NORMAL, JS::gcreason::MAYBEGC);
|
||||
if (gcIfNeeded())
|
||||
return true;
|
||||
}
|
||||
|
||||
double factor = schedulingState.inHighFrequencyGCMode() ? 0.85 : 0.9;
|
||||
if (zone->usage.gcBytes() > 1024 * 1024 &&
|
||||
|
@ -3269,12 +3292,8 @@ GCHelperState::init()
|
|||
if (!(done = PR_NewCondVar(rt->gc.lock)))
|
||||
return false;
|
||||
|
||||
if (CanUseExtraThreads()) {
|
||||
backgroundAllocation = (GetCPUCount() >= 2);
|
||||
if (CanUseExtraThreads())
|
||||
HelperThreadState().ensureInitialized();
|
||||
} else {
|
||||
backgroundAllocation = false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -3358,28 +3377,6 @@ GCHelperState::work()
|
|||
break;
|
||||
}
|
||||
|
||||
case ALLOCATING: {
|
||||
AutoTraceLog logAllocation(logger, TraceLogger::GCAllocation);
|
||||
do {
|
||||
Chunk *chunk;
|
||||
{
|
||||
AutoUnlockGC unlock(rt);
|
||||
chunk = Chunk::allocate(rt);
|
||||
}
|
||||
|
||||
/* OOM stops the background allocation. */
|
||||
if (!chunk)
|
||||
break;
|
||||
MOZ_ASSERT(chunk->info.numArenasFreeCommitted == 0);
|
||||
rt->gc.emptyChunks.put(chunk);
|
||||
} while (state() == ALLOCATING && rt->gc.wantBackgroundAllocation());
|
||||
|
||||
MOZ_ASSERT(state() == ALLOCATING || state() == CANCEL_ALLOCATION);
|
||||
break;
|
||||
}
|
||||
|
||||
case CANCEL_ALLOCATION:
|
||||
break;
|
||||
}
|
||||
|
||||
setState(IDLE);
|
||||
|
@ -3388,6 +3385,32 @@ GCHelperState::work()
|
|||
PR_NotifyAllCondVar(done);
|
||||
}
|
||||
|
||||
BackgroundAllocTask::BackgroundAllocTask(JSRuntime *rt, ChunkPool &pool)
|
||||
: runtime(rt),
|
||||
chunkPool_(pool),
|
||||
enabled_(CanUseExtraThreads() && GetCPUCount() >= 2)
|
||||
{
|
||||
}
|
||||
|
||||
/* virtual */ void
|
||||
BackgroundAllocTask::run()
|
||||
{
|
||||
TraceLogger *logger = TraceLoggerForCurrentThread();
|
||||
AutoTraceLog logAllocation(logger, TraceLogger::GCAllocation);
|
||||
|
||||
AutoLockGC lock(runtime);
|
||||
while (!cancel_ && runtime->gc.wantBackgroundAllocation(lock)) {
|
||||
Chunk *chunk;
|
||||
{
|
||||
AutoUnlockGC unlock(runtime);
|
||||
chunk = Chunk::allocate(runtime);
|
||||
if (!chunk)
|
||||
break;
|
||||
}
|
||||
chunkPool_.put(chunk);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GCHelperState::startBackgroundSweep(bool shouldShrink)
|
||||
{
|
||||
|
@ -3416,13 +3439,8 @@ GCHelperState::startBackgroundShrink()
|
|||
case SWEEPING:
|
||||
shrinkFlag = true;
|
||||
break;
|
||||
case ALLOCATING:
|
||||
case CANCEL_ALLOCATION:
|
||||
/*
|
||||
* If we have started background allocation there is nothing to
|
||||
* shrink.
|
||||
*/
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("Invalid GC helper thread state.");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3436,26 +3454,6 @@ GCHelperState::waitBackgroundSweepEnd()
|
|||
rt->gc.assertBackgroundSweepingFinished();
|
||||
}
|
||||
|
||||
void
|
||||
GCHelperState::waitBackgroundSweepOrAllocEnd()
|
||||
{
|
||||
AutoLockGC lock(rt);
|
||||
if (state() == ALLOCATING)
|
||||
setState(CANCEL_ALLOCATION);
|
||||
while (state() == SWEEPING || state() == CANCEL_ALLOCATION)
|
||||
waitForBackgroundThread();
|
||||
if (rt->gc.incrementalState == NO_INCREMENTAL)
|
||||
rt->gc.assertBackgroundSweepingFinished();
|
||||
}
|
||||
|
||||
/* Must be called with the GC lock taken. */
|
||||
inline void
|
||||
GCHelperState::startBackgroundAllocationIfIdle()
|
||||
{
|
||||
if (state_ == IDLE)
|
||||
startBackgroundThread(ALLOCATING);
|
||||
}
|
||||
|
||||
/* Must be called with the GC lock taken. */
|
||||
void
|
||||
GCHelperState::doSweep()
|
||||
|
@ -5814,7 +5812,7 @@ GCRuntime::gcCycle(bool incremental, int64_t budget, JSGCInvocationKind gckind,
|
|||
|
||||
AutoTraceSession session(rt, MajorCollecting);
|
||||
|
||||
isNeeded = false;
|
||||
majorGCRequested = false;
|
||||
interFrameGC = true;
|
||||
|
||||
number++;
|
||||
|
@ -5828,15 +5826,20 @@ GCRuntime::gcCycle(bool incremental, int64_t budget, JSGCInvocationKind gckind,
|
|||
// Assert if this is a GC unsafe region.
|
||||
JS::AutoAssertOnGC::VerifyIsSafeToGC(rt);
|
||||
|
||||
/*
|
||||
* As we about to purge caches and clear the mark bits we must wait for
|
||||
* any background finalization to finish. We must also wait for the
|
||||
* background allocation to finish so we can avoid taking the GC lock
|
||||
* when manipulating the chunks during the GC.
|
||||
*/
|
||||
{
|
||||
gcstats::AutoPhase ap(stats, gcstats::PHASE_WAIT_BACKGROUND_THREAD);
|
||||
waitBackgroundSweepOrAllocEnd();
|
||||
|
||||
// As we are about to purge caches and clear the mark bits, wait for
|
||||
// background finalization to finish. It cannot run between slices
|
||||
// so we only need to wait on the first slice.
|
||||
if (incrementalState == NO_INCREMENTAL)
|
||||
waitBackgroundSweepEnd();
|
||||
|
||||
// We must also wait for background allocation to finish so we can
|
||||
// avoid taking the GC lock when manipulating the chunks during the GC.
|
||||
// The background alloc task can run between slices, so we must wait
|
||||
// for it at the start of every slice.
|
||||
allocTask.cancel(GCParallelTask::CancelAndWait);
|
||||
}
|
||||
|
||||
State prevState = incrementalState;
|
||||
|
@ -6145,6 +6148,7 @@ void
|
|||
GCRuntime::minorGC(JS::gcreason::Reason reason)
|
||||
{
|
||||
#ifdef JSGC_GENERATIONAL
|
||||
minorGCRequested = false;
|
||||
TraceLogger *logger = TraceLoggerForMainThread(rt);
|
||||
AutoTraceLog logMinorGC(logger, TraceLogger::MinorGC);
|
||||
nursery.collect(rt, reason, nullptr);
|
||||
|
@ -6158,6 +6162,7 @@ GCRuntime::minorGC(JSContext *cx, JS::gcreason::Reason reason)
|
|||
// Alternate to the runtime-taking form above which allows marking type
|
||||
// objects as needing pretenuring.
|
||||
#ifdef JSGC_GENERATIONAL
|
||||
minorGCRequested = false;
|
||||
TraceLogger *logger = TraceLoggerForMainThread(rt);
|
||||
AutoTraceLog logMinorGC(logger, TraceLogger::MinorGC);
|
||||
Nursery::TypeObjectList pretenureTypes;
|
||||
|
@ -6196,20 +6201,26 @@ GCRuntime::enableGenerationalGC()
|
|||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
GCRuntime::gcIfNeeded(JSContext *cx)
|
||||
bool
|
||||
GCRuntime::gcIfNeeded(JSContext *cx /* = nullptr */)
|
||||
{
|
||||
// This method returns whether a major GC was performed.
|
||||
|
||||
#ifdef JSGC_GENERATIONAL
|
||||
/*
|
||||
* In case of store buffer overflow perform minor GC first so that the
|
||||
* correct reason is seen in the logs.
|
||||
*/
|
||||
if (storeBuffer.isAboutToOverflow())
|
||||
minorGC(cx, JS::gcreason::FULL_STORE_BUFFER);
|
||||
if (minorGCRequested) {
|
||||
if (cx)
|
||||
minorGC(cx, minorGCTriggerReason);
|
||||
else
|
||||
minorGC(minorGCTriggerReason);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (isNeeded)
|
||||
gcSlice(GC_NORMAL, rt->gc.triggerReason, 0);
|
||||
if (majorGCRequested) {
|
||||
gcSlice(GC_NORMAL, rt->gc.majorGCTriggerReason);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
AutoFinishGC::AutoFinishGC(JSRuntime *rt)
|
||||
|
|
|
@ -1005,9 +1005,7 @@ class GCHelperState
|
|||
{
|
||||
enum State {
|
||||
IDLE,
|
||||
SWEEPING,
|
||||
ALLOCATING,
|
||||
CANCEL_ALLOCATION
|
||||
SWEEPING
|
||||
};
|
||||
|
||||
// Associated runtime.
|
||||
|
@ -1033,8 +1031,6 @@ class GCHelperState
|
|||
bool sweepFlag;
|
||||
bool shrinkFlag;
|
||||
|
||||
bool backgroundAllocation;
|
||||
|
||||
friend class js::gc::ArenaLists;
|
||||
|
||||
static void freeElementsAndArray(void **array, void **end) {
|
||||
|
@ -1054,8 +1050,7 @@ class GCHelperState
|
|||
state_(IDLE),
|
||||
thread(nullptr),
|
||||
sweepFlag(false),
|
||||
shrinkFlag(false),
|
||||
backgroundAllocation(true)
|
||||
shrinkFlag(false)
|
||||
{ }
|
||||
|
||||
bool init();
|
||||
|
@ -1072,20 +1067,6 @@ class GCHelperState
|
|||
/* Must be called without the GC lock taken. */
|
||||
void waitBackgroundSweepEnd();
|
||||
|
||||
/* Must be called without the GC lock taken. */
|
||||
void waitBackgroundSweepOrAllocEnd();
|
||||
|
||||
/* Must be called with the GC lock taken. */
|
||||
void startBackgroundAllocationIfIdle();
|
||||
|
||||
bool canBackgroundAllocate() const {
|
||||
return backgroundAllocation;
|
||||
}
|
||||
|
||||
void disableBackgroundAllocation() {
|
||||
backgroundAllocation = false;
|
||||
}
|
||||
|
||||
bool onBackgroundThread();
|
||||
|
||||
/*
|
||||
|
@ -1118,11 +1099,15 @@ class GCParallelTask
|
|||
uint64_t duration_;
|
||||
|
||||
protected:
|
||||
// A flag to signal a request for early completion of the off-thread task.
|
||||
mozilla::Atomic<bool> cancel_;
|
||||
|
||||
virtual void run() = 0;
|
||||
|
||||
public:
|
||||
GCParallelTask() : state(NotStarted), duration_(0) {}
|
||||
|
||||
// Time spent in the most recent invocation of this task.
|
||||
int64_t duration() const { return duration_; }
|
||||
|
||||
// The simple interface to a parallel task works exactly like pthreads.
|
||||
|
@ -1137,6 +1122,17 @@ class GCParallelTask
|
|||
// Instead of dispatching to a helper, run the task on the main thread.
|
||||
void runFromMainThread(JSRuntime *rt);
|
||||
|
||||
// Dispatch a cancelation request.
|
||||
enum CancelMode { CancelNoWait, CancelAndWait};
|
||||
void cancel(CancelMode mode = CancelNoWait) {
|
||||
cancel_ = true;
|
||||
if (mode == CancelAndWait)
|
||||
join();
|
||||
}
|
||||
|
||||
// Check if a task is actively running.
|
||||
bool isRunning() const;
|
||||
|
||||
// This should be friended to HelperThread, but cannot be because it
|
||||
// would introduce several circular dependencies.
|
||||
public:
|
||||
|
|
|
@ -804,6 +804,10 @@ js_DumpPC(JSContext *cx)
|
|||
if (!sprinter.init())
|
||||
return false;
|
||||
ScriptFrameIter iter(cx);
|
||||
if (iter.done()) {
|
||||
fprintf(stdout, "Empty stack.\n");
|
||||
return true;
|
||||
}
|
||||
RootedScript script(cx, iter.script());
|
||||
bool ok = js_DisassembleAtPC(cx, script, true, iter.pc(), false, &sprinter);
|
||||
fprintf(stdout, "%s", sprinter.string());
|
||||
|
|
|
@ -26,17 +26,7 @@ typedef enum JSOp {
|
|||
FOR_EACH_OPCODE(ENUMERATE_OPCODE)
|
||||
#undef ENUMERATE_OPCODE
|
||||
|
||||
JSOP_LIMIT,
|
||||
|
||||
/*
|
||||
* These pseudo-ops help js_DecompileValueGenerator decompile JSOP_SETPROP,
|
||||
* JSOP_SETELEM, and comprehension-tails, respectively. They are never
|
||||
* stored in bytecode, so they don't preempt valid opcodes.
|
||||
*/
|
||||
JSOP_GETPROP2 = JSOP_LIMIT,
|
||||
JSOP_GETELEM2 = JSOP_LIMIT + 1,
|
||||
JSOP_FORLOCAL = JSOP_LIMIT + 2,
|
||||
JSOP_FAKE_LIMIT = JSOP_FORLOCAL
|
||||
JSOP_LIMIT
|
||||
} JSOp;
|
||||
|
||||
/*
|
||||
|
|
|
@ -352,23 +352,10 @@ static inline void
|
|||
WeakMapPostWriteBarrier(JSRuntime *rt, ObjectValueMap *weakMap, JSObject *key)
|
||||
{
|
||||
#ifdef JSGC_GENERATIONAL
|
||||
/*
|
||||
* Strip the barriers from the type before inserting into the store buffer.
|
||||
* This will automatically ensure that barriers do not fire during GC.
|
||||
*
|
||||
* Some compilers complain about instantiating the WeakMap class for
|
||||
* unbarriered type arguments, so we cast to a HashMap instead. Because of
|
||||
* WeakMap's multiple inheritace, We need to do this in two stages, first to
|
||||
* the HashMap base class and then to the unbarriered version.
|
||||
*/
|
||||
ObjectValueMap::Base *baseHashMap = static_cast<ObjectValueMap::Base *>(weakMap);
|
||||
|
||||
typedef HashMap<JSObject *, Value> UnbarrieredMap;
|
||||
UnbarrieredMap *unbarrieredMap = reinterpret_cast<UnbarrieredMap *>(baseHashMap);
|
||||
|
||||
typedef HashKeyRef<UnbarrieredMap, JSObject *> Ref;
|
||||
// Strip the barriers from the type before inserting into the store buffer.
|
||||
// This will automatically ensure that barriers do not fire during GC.
|
||||
if (key && IsInsideNursery(key))
|
||||
rt->gc.storeBuffer.putGeneric(Ref((unbarrieredMap), key));
|
||||
rt->gc.storeBuffer.putGeneric(UnbarrieredRef(weakMap, key));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "jsobj.h"
|
||||
|
||||
#include "gc/Marking.h"
|
||||
#include "gc/StoreBuffer.h"
|
||||
#include "js/HashTable.h"
|
||||
|
||||
namespace js {
|
||||
|
@ -279,6 +280,28 @@ protected:
|
|||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* At times, you will need to ignore barriers when accessing WeakMap entries.
|
||||
* Localize the templatized casting craziness here.
|
||||
*/
|
||||
template <class Key, class Value>
|
||||
static inline gc::HashKeyRef<HashMap<Key, Value, DefaultHasher<Key>, RuntimeAllocPolicy>, Key>
|
||||
UnbarrieredRef(WeakMap<PreBarriered<Key>, RelocatablePtr<Value>> *map, Key key)
|
||||
{
|
||||
/*
|
||||
* Some compilers complain about instantiating the WeakMap class for
|
||||
* unbarriered type arguments, so we cast to a HashMap instead. Because of
|
||||
* WeakMap's multiple inheritance, we need to do this in two stages, first
|
||||
* to the HashMap base class and then to the unbarriered version.
|
||||
*/
|
||||
|
||||
typedef typename WeakMap<PreBarriered<Key>, RelocatablePtr<Value>>::Base BaseMap;
|
||||
auto baseMap = static_cast<BaseMap*>(map);
|
||||
typedef HashMap<Key, Value, DefaultHasher<Key>, RuntimeAllocPolicy> UnbarrieredMap;
|
||||
typedef gc::HashKeyRef<UnbarrieredMap, Key> UnbarrieredKeyRef;
|
||||
return UnbarrieredKeyRef(reinterpret_cast<UnbarrieredMap*>(baseMap), key);
|
||||
}
|
||||
|
||||
/* WeakMap methods exposed so they can be installed in the self-hosting global. */
|
||||
|
||||
extern JSObject *
|
||||
|
|
|
@ -22,22 +22,22 @@ if (typeof findReferences == "function") {
|
|||
function g() { return 42; }
|
||||
function s(v) { }
|
||||
var p = Object.defineProperty({}, 'a', { get:g, set:s });
|
||||
assertEq(referencesVia(p, 'shape; base; getter', g), true);
|
||||
assertEq(referencesVia(p, 'shape; base; setter', s), true);
|
||||
assertEq(referencesVia(p, 'shape; getter', g), true);
|
||||
assertEq(referencesVia(p, 'shape; setter', s), true);
|
||||
|
||||
// If there are multiple objects with the same shape referring to a getter
|
||||
// or setter, findReferences should get all of them, even though the shape
|
||||
// gets 'marked' the first time we visit it.
|
||||
var q = Object.defineProperty({}, 'a', { get:g, set:s });
|
||||
assertEq(referencesVia(p, 'shape; base; getter', g), true);
|
||||
assertEq(referencesVia(q, 'shape; base; getter', g), true);
|
||||
assertEq(referencesVia(p, 'shape; getter', g), true);
|
||||
assertEq(referencesVia(q, 'shape; getter', g), true);
|
||||
|
||||
// If we extend each object's shape chain, both should still be able to
|
||||
// reach the getter, even though the two shapes are each traversed twice.
|
||||
p.b = 9;
|
||||
q.b = 9;
|
||||
assertEq(referencesVia(p, 'shape; parent; base; getter', g), true);
|
||||
assertEq(referencesVia(q, 'shape; parent; base; getter', g), true);
|
||||
assertEq(referencesVia(p, 'shape; parent; getter', g), true);
|
||||
assertEq(referencesVia(q, 'shape; parent; getter', g), true);
|
||||
|
||||
// These are really just ordinary own property references.
|
||||
assertEq(referencesVia(C, 'prototype', Object.getPrototypeOf(o)), true);
|
||||
|
|
|
@ -240,6 +240,11 @@ GlobalObject::create(JSContext *cx, const Class *clasp)
|
|||
|
||||
Rooted<GlobalObject *> global(cx, &obj->as<GlobalObject>());
|
||||
|
||||
// Initialize the private slot to null if present, as GC can call class
|
||||
// hooks before the caller gets to set this to a non-garbage value.
|
||||
if (clasp->flags & JSCLASS_HAS_PRIVATE)
|
||||
global->setPrivate(nullptr);
|
||||
|
||||
cx->compartment()->initGlobal(*global);
|
||||
|
||||
if (!global->setQualifiedVarObj(cx))
|
||||
|
|
|
@ -189,8 +189,8 @@ ParseTask::ParseTask(ExclusiveContext *cx, JSObject *exclusiveContextGlobal, JSC
|
|||
JS::OffThreadCompileCallback callback, void *callbackData)
|
||||
: cx(cx), options(initCx), chars(chars), length(length),
|
||||
alloc(JSRuntime::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
|
||||
exclusiveContextGlobal(initCx, exclusiveContextGlobal), optionsElement(initCx),
|
||||
optionsIntroductionScript(initCx), callback(callback), callbackData(callbackData),
|
||||
exclusiveContextGlobal(initCx, exclusiveContextGlobal),
|
||||
callback(callback), callbackData(callbackData),
|
||||
script(nullptr), errors(cx), overRecursed(false)
|
||||
{
|
||||
}
|
||||
|
@ -761,6 +761,7 @@ js::GCParallelTask::joinWithLockHeld()
|
|||
while (state != Finished)
|
||||
HelperThreadState().wait(GlobalHelperThreadState::CONSUMER);
|
||||
state = NotStarted;
|
||||
cancel_ = false;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -796,6 +797,13 @@ js::GCParallelTask::runFromHelperThread()
|
|||
HelperThreadState().notifyAll(GlobalHelperThreadState::CONSUMER);
|
||||
}
|
||||
|
||||
bool
|
||||
js::GCParallelTask::isRunning() const
|
||||
{
|
||||
MOZ_ASSERT(HelperThreadState().isLocked());
|
||||
return state == Dispatched;
|
||||
}
|
||||
|
||||
void
|
||||
HelperThread::handleGCParallelWorkload()
|
||||
{
|
||||
|
|
|
@ -457,14 +457,6 @@ struct ParseTask
|
|||
// Rooted pointer to the global object used by 'cx'.
|
||||
PersistentRootedObject exclusiveContextGlobal;
|
||||
|
||||
// Saved GC-managed CompileOptions fields that will populate slots in
|
||||
// the ScriptSourceObject. We create the ScriptSourceObject in the
|
||||
// compilation's temporary compartment, so storing these values there
|
||||
// at that point would create cross-compartment references. Instead we
|
||||
// hold them here, and install them after merging the compartments.
|
||||
PersistentRootedObject optionsElement;
|
||||
PersistentRootedScript optionsIntroductionScript;
|
||||
|
||||
// Callback invoked off the main thread when the parse finishes.
|
||||
JS::OffThreadCompileCallback callback;
|
||||
void *callbackData;
|
||||
|
|
|
@ -570,19 +570,19 @@ InitArrayElemOperation(JSContext *cx, jsbytecode *pc, HandleObject obj, uint32_t
|
|||
MOZ_ASSERT(obj->is<ArrayObject>());
|
||||
|
||||
/*
|
||||
* If val is a hole, do not call JSObject::defineElement. In this case,
|
||||
* if the current op is the last element initialiser, set the array length
|
||||
* to one greater than id.
|
||||
* If val is a hole, do not call JSObject::defineElement.
|
||||
*
|
||||
* If val is a hole and current op is JSOP_INITELEM_INC, always call
|
||||
* Furthermore, if the current op is JSOP_INITELEM_INC, always call
|
||||
* SetLengthProperty even if it is not the last element initialiser,
|
||||
* because it may be followed by JSOP_SPREAD, which will not set the array
|
||||
* length if nothing is spreaded.
|
||||
* length if nothing is spread.
|
||||
*
|
||||
* Alternatively, if the current op is JSOP_INITELEM_ARRAY, the length will
|
||||
* have already been set by the earlier JSOP_NEWARRAY; JSOP_INITELEM_ARRAY
|
||||
* cannot follow JSOP_SPREAD.
|
||||
*/
|
||||
if (val.isMagic(JS_ELEMENTS_HOLE)) {
|
||||
JSOp next = JSOp(*GetNextPc(pc));
|
||||
|
||||
if ((op == JSOP_INITELEM_ARRAY && next == JSOP_ENDINIT) || op == JSOP_INITELEM_INC) {
|
||||
if (op == JSOP_INITELEM_INC) {
|
||||
if (!SetLengthProperty(cx, obj, index + 1))
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -1611,6 +1611,7 @@ CASE(JSOP_UNUSED51)
|
|||
CASE(JSOP_UNUSED52)
|
||||
CASE(JSOP_UNUSED57)
|
||||
CASE(JSOP_UNUSED83)
|
||||
CASE(JSOP_UNUSED92)
|
||||
CASE(JSOP_UNUSED103)
|
||||
CASE(JSOP_UNUSED104)
|
||||
CASE(JSOP_UNUSED105)
|
||||
|
@ -3120,14 +3121,6 @@ CASE(JSOP_NEWOBJECT)
|
|||
}
|
||||
END_CASE(JSOP_NEWOBJECT)
|
||||
|
||||
CASE(JSOP_ENDINIT)
|
||||
{
|
||||
/* FIXME remove JSOP_ENDINIT bug 588522 */
|
||||
MOZ_ASSERT(REGS.stackDepth() >= 1);
|
||||
MOZ_ASSERT(REGS.sp[-1].isObject() || REGS.sp[-1].isUndefined());
|
||||
}
|
||||
END_CASE(JSOP_ENDINIT)
|
||||
|
||||
CASE(JSOP_MUTATEPROTO)
|
||||
{
|
||||
MOZ_ASSERT(REGS.stackDepth() >= 2);
|
||||
|
|
|
@ -770,17 +770,9 @@
|
|||
* Stack: => obj
|
||||
*/ \
|
||||
macro(JSOP_NEWOBJECT, 91, "newobject", NULL, 5, 0, 1, JOF_OBJECT) \
|
||||
/*
|
||||
* A no-operation bytecode.
|
||||
*
|
||||
* Indicates the end of object/array initialization, and used for
|
||||
* Type-Inference, decompile, etc.
|
||||
* Category: Literals
|
||||
* Type: Object
|
||||
* Operands:
|
||||
* Stack: =>
|
||||
*/ \
|
||||
macro(JSOP_ENDINIT, 92, "endinit", NULL, 1, 0, 0, JOF_BYTE) \
|
||||
\
|
||||
macro(JSOP_UNUSED92, 92, "unused92", NULL, 1, 0, 0, JOF_BYTE) \
|
||||
\
|
||||
/*
|
||||
* Initialize a named property in an object literal, like '{a: x}'.
|
||||
*
|
||||
|
|
|
@ -1846,23 +1846,8 @@ DebugScopes::proxiedScopesPostWriteBarrier(JSRuntime *rt, ObjectWeakMap *map,
|
|||
const PreBarrieredObject &key)
|
||||
{
|
||||
#ifdef JSGC_GENERATIONAL
|
||||
/*
|
||||
* Strip the barriers from the type before inserting into the store buffer.
|
||||
* This will automatically ensure that barriers do not fire during GC.
|
||||
*
|
||||
* Some compilers complain about instantiating the WeakMap class for
|
||||
* unbarriered type arguments, so we cast to a HashMap instead. Because of
|
||||
* WeakMap's multiple inheritace, We need to do this in two stages, first to
|
||||
* the HashMap base class and then to the unbarriered version.
|
||||
*/
|
||||
ObjectWeakMap::Base *baseHashMap = static_cast<ObjectWeakMap::Base *>(map);
|
||||
|
||||
typedef HashMap<JSObject *, JSObject *> UnbarrieredMap;
|
||||
UnbarrieredMap *unbarrieredMap = reinterpret_cast<UnbarrieredMap *>(baseHashMap);
|
||||
|
||||
typedef gc::HashKeyRef<UnbarrieredMap, JSObject *> Ref;
|
||||
if (key && IsInsideNursery(key))
|
||||
rt->gc.storeBuffer.putGeneric(Ref(unbarrieredMap, key.get()));
|
||||
rt->gc.storeBuffer.putGeneric(UnbarrieredRef(map, key.get()));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ namespace js {
|
|||
*
|
||||
* https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/Bytecode
|
||||
*/
|
||||
static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - 186);
|
||||
static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - 187);
|
||||
|
||||
class XDRBuffer {
|
||||
public:
|
||||
|
|
|
@ -6092,6 +6092,12 @@ nsLayoutUtils::SurfaceFromElement(HTMLVideoElement* aElement,
|
|||
|
||||
NS_WARN_IF_FALSE((aSurfaceFlags & SFE_PREFER_NO_PREMULTIPLY_ALPHA) == 0, "We can't support non-premultiplied alpha for video!");
|
||||
|
||||
#ifdef MOZ_EME
|
||||
if (aElement->ContainsRestrictedContent()) {
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
uint16_t readyState;
|
||||
if (NS_SUCCEEDED(aElement->GetReadyState(&readyState)) &&
|
||||
(readyState == nsIDOMHTMLMediaElement::HAVE_NOTHING ||
|
||||
|
|
|
@ -2463,6 +2463,22 @@ MaxZIndexInList(nsDisplayList* aList, nsDisplayListBuilder* aBuilder)
|
|||
return maxZIndex;
|
||||
}
|
||||
|
||||
// Finds the max z-index of the items in aList that meet the following conditions
|
||||
// 1) have z-index auto or z-index >= 0.
|
||||
// 2) aFrame is a proper ancestor of the item's frame.
|
||||
// Returns -1 if there is no such item.
|
||||
static int32_t
|
||||
MaxZIndexInListOfItemsContainedInFrame(nsDisplayList* aList, nsIFrame* aFrame)
|
||||
{
|
||||
int32_t maxZIndex = -1;
|
||||
for (nsDisplayItem* item = aList->GetBottom(); item; item = item->GetAbove()) {
|
||||
if (nsLayoutUtils::IsProperAncestorFrame(aFrame, item->Frame())) {
|
||||
maxZIndex = std::max(maxZIndex, item->ZIndex());
|
||||
}
|
||||
}
|
||||
return maxZIndex;
|
||||
}
|
||||
|
||||
static void
|
||||
AppendToTop(nsDisplayListBuilder* aBuilder, const nsDisplayListSet& aLists,
|
||||
nsDisplayList* aSource, nsIFrame* aSourceFrame, bool aOwnLayer,
|
||||
|
@ -2997,10 +3013,10 @@ ScrollFrameHelper::BuildDisplayList(nsDisplayListBuilder* aBuilder,
|
|||
// metadata about this scroll box to the compositor process.
|
||||
nsDisplayScrollInfoLayer* layerItem = new (aBuilder) nsDisplayScrollInfoLayer(
|
||||
aBuilder, mScrolledFrame, mOuter);
|
||||
nsDisplayList* positionedDescendants = scrolledContent.PositionedDescendants();
|
||||
if (BuildScrollContainerLayers()) {
|
||||
// We process display items from bottom to top, so if we need to flatten after
|
||||
// the scroll layer items have been processed we need to be on the top.
|
||||
nsDisplayList* positionedDescendants = scrolledContent.PositionedDescendants();
|
||||
if (!positionedDescendants->IsEmpty()) {
|
||||
layerItem->SetOverrideZIndex(MaxZIndexInList(positionedDescendants, aBuilder));
|
||||
positionedDescendants->AppendNewToTop(layerItem);
|
||||
|
@ -3008,7 +3024,14 @@ ScrollFrameHelper::BuildDisplayList(nsDisplayListBuilder* aBuilder,
|
|||
aLists.Outlines()->AppendNewToTop(layerItem);
|
||||
}
|
||||
} else {
|
||||
scrolledContent.BorderBackground()->AppendNewToBottom(layerItem);
|
||||
int32_t zindex =
|
||||
MaxZIndexInListOfItemsContainedInFrame(positionedDescendants, mOuter);
|
||||
if (zindex >= 0) {
|
||||
layerItem->SetOverrideZIndex(zindex);
|
||||
positionedDescendants->AppendNewToTop(layerItem);
|
||||
} else {
|
||||
scrolledContent.Outlines()->AppendNewToTop(layerItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Now display overlay scrollbars and the resizer, if we have one.
|
||||
|
|
|
@ -79,8 +79,7 @@ private:
|
|||
};
|
||||
|
||||
|
||||
ClearKeyDecryptionManager::ClearKeyDecryptionManager(GMPDecryptorHost* aHost)
|
||||
: mHost(aHost)
|
||||
ClearKeyDecryptionManager::ClearKeyDecryptionManager()
|
||||
{
|
||||
CK_LOGD("ClearKeyDecryptionManager ctor");
|
||||
}
|
||||
|
@ -132,7 +131,7 @@ ClearKeyDecryptionManager::CreateSession(uint32_t aPromiseId,
|
|||
string sessionId = GetNewSessionId();
|
||||
assert(mSessions.find(sessionId) == mSessions.end());
|
||||
|
||||
ClearKeySession* session = new ClearKeySession(sessionId, mHost, mCallback);
|
||||
ClearKeySession* session = new ClearKeySession(sessionId, mCallback);
|
||||
session->Init(aPromiseId, aInitData, aInitDataSize);
|
||||
mSessions[sessionId] = session;
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ class ClearKeyDecryptor;
|
|||
class ClearKeyDecryptionManager MOZ_FINAL : public GMPDecryptor
|
||||
{
|
||||
public:
|
||||
explicit ClearKeyDecryptionManager(GMPDecryptorHost* aHost);
|
||||
ClearKeyDecryptionManager();
|
||||
~ClearKeyDecryptionManager();
|
||||
|
||||
virtual void Init(GMPDecryptorCallback* aCallback) MOZ_OVERRIDE;
|
||||
|
@ -60,7 +60,6 @@ public:
|
|||
|
||||
private:
|
||||
GMPDecryptorCallback* mCallback;
|
||||
GMPDecryptorHost* mHost;
|
||||
|
||||
std::map<KeyId, ClearKeyDecryptor*> mDecryptors;
|
||||
std::map<std::string, ClearKeySession*> mSessions;
|
||||
|
|
|
@ -12,10 +12,8 @@
|
|||
using namespace mozilla;
|
||||
|
||||
ClearKeySession::ClearKeySession(const std::string& aSessionId,
|
||||
GMPDecryptorHost* aHost,
|
||||
GMPDecryptorCallback* aCallback)
|
||||
: mSessionId(aSessionId)
|
||||
, mHost(aHost)
|
||||
, mCallback(aCallback)
|
||||
{
|
||||
CK_LOGD("ClearKeySession ctor %p", this);
|
||||
|
|
|
@ -21,7 +21,7 @@ class ClearKeySession
|
|||
{
|
||||
public:
|
||||
ClearKeySession(const std::string& aSessionId,
|
||||
GMPDecryptorHost* aHost, GMPDecryptorCallback *aCallback);
|
||||
GMPDecryptorCallback* aCallback);
|
||||
|
||||
~ClearKeySession();
|
||||
|
||||
|
@ -34,7 +34,6 @@ private:
|
|||
std::vector<KeyId> mKeyIds;
|
||||
|
||||
GMPDecryptorCallback* mCallback;
|
||||
GMPDecryptorHost* mHost;
|
||||
};
|
||||
|
||||
#endif // __ClearKeySession_h__
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
#include <vector>
|
||||
|
||||
#include "ClearKeyUtils.h"
|
||||
#include "mozilla/ArrayUtils.h"
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/Endian.h"
|
||||
#include "mozilla/NullPtr.h"
|
||||
#include "openaes/oaes_lib.h"
|
||||
|
@ -95,7 +97,7 @@ EncodeBase64Web(vector<uint8_t> aBinary, string& aEncoded)
|
|||
|
||||
auto out = aEncoded.begin();
|
||||
auto data = aBinary.begin();
|
||||
for (int i = 0; i < aEncoded.length(); i++) {
|
||||
for (string::size_type i = 0; i < aEncoded.length(); i++) {
|
||||
if (shift) {
|
||||
out[i] = (*data << (6 - shift)) & sMask;
|
||||
data++;
|
||||
|
@ -106,7 +108,12 @@ EncodeBase64Web(vector<uint8_t> aBinary, string& aEncoded)
|
|||
out[i] += (*data >> (shift + 2)) & sMask;
|
||||
shift = (shift + 2) % 8;
|
||||
|
||||
out[i] = sAlphabet[out[i]];
|
||||
// Cast idx to size_t before using it as an array-index,
|
||||
// to pacify clang 'Wchar-subscripts' warning:
|
||||
size_t idx = static_cast<size_t>(out[i]);
|
||||
MOZ_ASSERT(idx < MOZ_ARRAY_LENGTH(sAlphabet),
|
||||
"out of bounds index for 'sAlphabet'");
|
||||
out[i] = sAlphabet[idx];
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
|
@ -35,7 +35,7 @@ GMPGetAPI(const char* aApiName, void* aHostAPI, void** aPluginAPI)
|
|||
return GMPNotImplementedErr;
|
||||
}
|
||||
|
||||
*aPluginAPI = new ClearKeyDecryptionManager(static_cast<GMPDecryptorHost*>(aHostAPI));
|
||||
*aPluginAPI = new ClearKeyDecryptionManager();
|
||||
|
||||
return GMPNoErr;
|
||||
}
|
||||
|
|
|
@ -736,7 +736,7 @@ int WebRtcAec_GetDelayMetricsCore(AecCore* self, int* median, int* std) {
|
|||
|
||||
// Calculate the L1 norm, with median value as central moment.
|
||||
for (i = 0; i < kHistorySizeBlocks; i++) {
|
||||
l1_norm += (float)(fabs(i - my_median) * self->delay_histogram[i]);
|
||||
l1_norm += (float)abs(i - my_median) * self->delay_histogram[i];
|
||||
}
|
||||
*std = (int)(l1_norm / (float)num_delay_values + 0.5f) * kMsPerBlock;
|
||||
|
||||
|
|
|
@ -289,7 +289,7 @@ int32_t WebRtcAgc_AddFarendToDigital(DigitalAgc_t *stt, const int16_t *in_far,
|
|||
int16_t nrSamples)
|
||||
{
|
||||
// Check for valid pointer
|
||||
if (&stt->vadFarend == NULL)
|
||||
if (stt == NULL)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
|
|
@ -249,10 +249,10 @@ void OveruseDetector::UpdateKalman(int64_t t_delta,
|
|||
const double residual = t_ts_delta - slope_*h[0] - offset_;
|
||||
|
||||
const bool stable_state =
|
||||
(BWE_MIN(num_of_deltas_, 60) * fabsf(offset_) < threshold_);
|
||||
(BWE_MIN(num_of_deltas_, 60) * fabs(offset_) < threshold_);
|
||||
// We try to filter out very late frames. For instance periodic key
|
||||
// frames doesn't fit the Gaussian model well.
|
||||
if (fabsf(residual) < 3 * sqrt(var_noise_)) {
|
||||
if (fabs(residual) < 3 * sqrt(var_noise_)) {
|
||||
UpdateNoiseEstimate(residual, min_frame_period, stable_state);
|
||||
} else {
|
||||
UpdateNoiseEstimate(3 * sqrt(var_noise_), min_frame_period, stable_state);
|
||||
|
@ -358,7 +358,7 @@ BandwidthUsage OveruseDetector::Detect(double ts_delta) {
|
|||
return kBwNormal;
|
||||
}
|
||||
const double T = BWE_MIN(num_of_deltas_, 60) * offset_;
|
||||
if (fabsf(T) > threshold_) {
|
||||
if (fabs(T) > threshold_) {
|
||||
if (offset_ > 0) {
|
||||
if (time_over_using_ == -1) {
|
||||
// Initialize the timer. Assume that we've been
|
||||
|
|
|
@ -162,7 +162,7 @@ VCMJitterEstimator::UpdateEstimate(int64_t frameDelayMS, uint32_t frameSizeBytes
|
|||
// deviation is probably due to an incorrect line slope.
|
||||
double deviation = DeviationFromExpectedDelay(frameDelayMS, deltaFS);
|
||||
|
||||
if (abs(deviation) < _numStdDevDelayOutlier * sqrt(_varNoise) ||
|
||||
if (fabs(deviation) < _numStdDevDelayOutlier * sqrt(_varNoise) ||
|
||||
frameSizeBytes > _avgFrameSize + _numStdDevFrameSizeOutlier * sqrt(_varFrameSize))
|
||||
{
|
||||
// Update the variance of the deviation from the
|
||||
|
@ -257,7 +257,7 @@ VCMJitterEstimator::KalmanEstimateChannel(int64_t frameDelayMS,
|
|||
{
|
||||
return;
|
||||
}
|
||||
double sigma = (300.0 * exp(-abs(static_cast<double>(deltaFSBytes)) /
|
||||
double sigma = (300.0 * exp(-fabs(static_cast<double>(deltaFSBytes)) /
|
||||
(1e0 * _maxFrameSize)) + 1) * sqrt(_varNoise);
|
||||
if (sigma < 1.0)
|
||||
{
|
||||
|
|
|
@ -159,12 +159,12 @@ VCMEncodedFrame* VCMReceiver::FrameForDecoding(
|
|||
// Assume that render timing errors are due to changes in the video stream.
|
||||
if (next_render_time_ms < 0) {
|
||||
timing_error = true;
|
||||
} else if (abs(next_render_time_ms - now_ms) > max_video_delay_ms_) {
|
||||
} else if (std::abs(next_render_time_ms - now_ms) > max_video_delay_ms_) {
|
||||
WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCoding,
|
||||
VCMId(vcm_id_, receiver_id_),
|
||||
"This frame is out of our delay bounds, resetting jitter "
|
||||
"buffer: %d > %d",
|
||||
static_cast<int>(abs(next_render_time_ms - now_ms)),
|
||||
static_cast<int>(std::abs(next_render_time_ms - now_ms)),
|
||||
max_video_delay_ms_);
|
||||
timing_error = true;
|
||||
} else if (static_cast<int>(timing_->TargetVideoDelay()) >
|
||||
|
|
|
@ -114,7 +114,7 @@ bool
|
|||
VCMRttFilter::JumpDetection(uint32_t rttMs)
|
||||
{
|
||||
double diffFromAvg = _avgRtt - rttMs;
|
||||
if (abs(diffFromAvg) > _jumpStdDevs * sqrt(_varRtt))
|
||||
if (fabs(diffFromAvg) > _jumpStdDevs * sqrt(_varRtt))
|
||||
{
|
||||
int diffSign = (diffFromAvg >= 0) ? 1 : -1;
|
||||
int jumpCountSign = (_jumpCount >= 0) ? 1 : -1;
|
||||
|
|
|
@ -52,7 +52,7 @@ struct Vp8StreamInfo {
|
|||
MATCHER_P(MatchesVp8StreamInfo, expected, "") {
|
||||
bool res = true;
|
||||
for (int tl = 0; tl < kMaxNumberOfTemporalLayers; ++tl) {
|
||||
if (abs(expected.framerate_fps[tl] - arg.framerate_fps[tl]) > 0.5) {
|
||||
if (fabs(expected.framerate_fps[tl] - arg.framerate_fps[tl]) > 0.5) {
|
||||
*result_listener << " framerate_fps[" << tl
|
||||
<< "] = " << arg.framerate_fps[tl] << " (expected "
|
||||
<< expected.framerate_fps[tl] << ") ";
|
||||
|
|
|
@ -184,7 +184,7 @@ class VideoRtcpAndSyncObserver : public SyncRtcpObserver, public VideoRenderer {
|
|||
// estimated as being synchronized. We don't want to trigger on those.
|
||||
if (time_since_creation < kStartupTimeMs)
|
||||
return;
|
||||
if (abs(latest_audio_ntp - latest_video_ntp) < kInSyncThresholdMs) {
|
||||
if (std::abs(latest_audio_ntp - latest_video_ntp) < kInSyncThresholdMs) {
|
||||
if (first_time_in_sync_ == -1) {
|
||||
first_time_in_sync_ = now_ms;
|
||||
webrtc::test::PrintResult("sync_convergence_time",
|
||||
|
|
|
@ -20,6 +20,7 @@ from manifestparser import TestManifest
|
|||
from marionette import Marionette
|
||||
from mixins.b2g import B2GTestResultMixin, get_b2g_pid, get_dm
|
||||
from mozhttpd import MozHttpd
|
||||
from mozlog.structured.structuredlog import get_default_logger
|
||||
from moztest.adapters.unit import StructuredTestRunner, StructuredTestResult
|
||||
from moztest.results import TestResultCollection, TestResult, relevant_line
|
||||
|
||||
|
@ -222,7 +223,8 @@ class MarionetteTextTestRunner(StructuredTestRunner):
|
|||
marionette=self.marionette,
|
||||
b2g_pid=self.b2g_pid,
|
||||
logger=self.logger,
|
||||
logcat_stdout=self.logcat_stdout)
|
||||
logcat_stdout=self.logcat_stdout,
|
||||
result_callbacks=self.result_callbacks)
|
||||
|
||||
def run(self, test):
|
||||
"Run the given test case or test suite."
|
||||
|
@ -450,7 +452,8 @@ class BaseMarionetteTestRunner(object):
|
|||
device_serial=None, symbols_path=None, timeout=None,
|
||||
shuffle=False, shuffle_seed=random.randint(0, sys.maxint),
|
||||
sdcard=None, this_chunk=1, total_chunks=1, sources=None,
|
||||
server_root=None, gecko_log=None, **kwargs):
|
||||
server_root=None, gecko_log=None, result_callbacks=None,
|
||||
**kwargs):
|
||||
self.address = address
|
||||
self.emulator = emulator
|
||||
self.emulator_binary = emulator_binary
|
||||
|
@ -490,6 +493,22 @@ class BaseMarionetteTestRunner(object):
|
|||
self.mixin_run_tests = []
|
||||
self.manifest_skipped_tests = []
|
||||
self.tests = []
|
||||
self.result_callbacks = result_callbacks if result_callbacks is not None else []
|
||||
|
||||
def gather_debug(test, status):
|
||||
rv = {}
|
||||
marionette = test._marionette_weakref()
|
||||
try:
|
||||
marionette.set_context(marionette.CONTEXT_CHROME)
|
||||
rv['screenshot'] = marionette.screenshot()
|
||||
marionette.set_context(marionette.CONTEXT_CONTENT)
|
||||
rv['source'] = marionette.page_source
|
||||
except:
|
||||
logger = get_default_logger()
|
||||
logger.warning('Failed to gather test failure debug.', exc_info=True)
|
||||
return rv
|
||||
|
||||
self.result_callbacks.append(gather_debug)
|
||||
|
||||
if testvars:
|
||||
if not os.path.exists(testvars):
|
||||
|
@ -789,7 +808,8 @@ class BaseMarionetteTestRunner(object):
|
|||
runner = self.textrunnerclass(logger=self.logger,
|
||||
marionette=self.marionette,
|
||||
capabilities=self.capabilities,
|
||||
logcat_stdout=self.logcat_stdout)
|
||||
logcat_stdout=self.logcat_stdout,
|
||||
result_callbacks=self.result_callbacks)
|
||||
|
||||
results = runner.run(suite)
|
||||
self.results.append(results)
|
||||
|
|
|
@ -124,7 +124,7 @@ class HTMLFormatter(base.BaseFormatter):
|
|||
# use base64 to avoid that some browser (such as Firefox, Opera)
|
||||
# treats '#' as the start of another link if the data URL contains.
|
||||
# use 'charset=utf-8' to show special characters like Chinese.
|
||||
href = 'data:text/plain;charset=utf-8;base64,%s' % base64.b64encode(content)
|
||||
href = 'data:text/plain;charset=utf-8;base64,%s' % base64.b64encode(content.encode('utf-8'))
|
||||
links_html.append(html.a(
|
||||
name.title(),
|
||||
class_=name,
|
||||
|
|
|
@ -10,6 +10,3 @@
|
|||
[Test appending an empty ArrayBuffer.]
|
||||
expected: FAIL
|
||||
|
||||
[Test SourceBuffer.appendBuffer() triggering an 'ended' to 'open' transition.]
|
||||
disabled:
|
||||
if not debug and (os == "win") and (version == "5.1.2600") and (processor == "x86") and (bits == 32): Unstable
|
||||
|
|
|
@ -1,3 +1,15 @@
|
|||
[mediasource-buffered.html]
|
||||
type: testharness
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1066467
|
||||
expected: TIMEOUT
|
||||
[Demuxed content with different lengths]
|
||||
expected: FAIL
|
||||
|
||||
[Muxed tracks with different lengths]
|
||||
expected: FAIL
|
||||
|
||||
[Demuxed content with an empty buffered range on one SourceBuffer]
|
||||
expected: FAIL
|
||||
|
||||
[Muxed content empty buffered ranges.]
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
[mediasource-config-change-webm-a-bitrate.html]
|
||||
type: testharness
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1066467
|
|
@ -1,3 +0,0 @@
|
|||
[mediasource-config-change-webm-av-audio-bitrate.html]
|
||||
type: testharness
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1066467
|
|
@ -1,3 +0,0 @@
|
|||
[mediasource-config-change-webm-av-video-bitrate.html]
|
||||
type: testharness
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1066467
|
|
@ -1,6 +1,5 @@
|
|||
[mediasource-config-change-webm-v-bitrate.html]
|
||||
type: testharness
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1066467
|
||||
[Tests webm video-only bitrate changes.]
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -1,3 +1,15 @@
|
|||
[mediasource-duration.html]
|
||||
type: testharness
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1066467
|
||||
expected: TIMEOUT
|
||||
[Test seek starts on duration truncation below currentTime]
|
||||
expected: TIMEOUT
|
||||
|
||||
[Test appendBuffer completes previous seek to truncated duration]
|
||||
disabled: TIMEOUT or FAIL https://bugzilla.mozilla.org/show_bug.cgi?id=1066467
|
||||
|
||||
[Test endOfStream completes previous seek to truncated duration]
|
||||
expected: TIMEOUT
|
||||
|
||||
[Test setting same duration multiple times does not fire duplicate durationchange]
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
[mediasource-play-then-seek-back.html]
|
||||
type: testharness
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1066467
|
||||
expected: TIMEOUT
|
||||
[Test playing then seeking back.]
|
||||
expected: TIMEOUT
|
||||
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
[mediasource-redundant-seek.html]
|
||||
type: testharness
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1066467
|
||||
expected: TIMEOUT
|
||||
[Test redundant fully prebuffered seek]
|
||||
expected: TIMEOUT
|
||||
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
[mediasource-seek-beyond-duration.html]
|
||||
type: testharness
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1066467
|
||||
expected: TIMEOUT
|
||||
[Test seeking beyond updated media duration.]
|
||||
expected: TIMEOUT
|
||||
|
||||
[Test seeking beyond media duration.]
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1066467
|
||||
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
[mediasource-seek-during-pending-seek.html]
|
||||
type: testharness
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1066467
|
||||
expected: TIMEOUT
|
||||
[Test seeking to a new location before transitioning beyond HAVE_METADATA.]
|
||||
expected: TIMEOUT
|
||||
|
||||
[Test seeking to a new location during a pending seek.]
|
||||
expected: TIMEOUT
|
||||
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
[mediasource-sourcebuffer-mode.html]
|
||||
type: testharness
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1066467
|
||||
expected: TIMEOUT
|
||||
[Test setting SourceBuffer.mode and SourceBuffer.timestampOffset while parsing media segment.]
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -264,7 +264,10 @@ class TickSample {
|
|||
lr(NULL),
|
||||
#endif
|
||||
context(NULL),
|
||||
isSamplingCurrentThread(false) {}
|
||||
isSamplingCurrentThread(false),
|
||||
threadProfile(nullptr),
|
||||
rssMemory(0),
|
||||
ussMemory(0) {}
|
||||
|
||||
void PopulateContext(void* aContext);
|
||||
|
||||
|
|
|
@ -180,6 +180,8 @@
|
|||
|
||||
#include "npapi.h"
|
||||
|
||||
#include <d3d11.h>
|
||||
|
||||
#if !defined(SM_CONVERTIBLESLATEMODE)
|
||||
#define SM_CONVERTIBLESLATEMODE 0x2003
|
||||
#endif
|
||||
|
@ -6735,7 +6737,9 @@ nsWindow::GetPreferredCompositorBackends(nsTArray<LayersBackend>& aHints)
|
|||
}
|
||||
|
||||
ID3D11Device* device = gfxWindowsPlatform::GetPlatform()->GetD3D11Device();
|
||||
if (device && !DoesD3D11DeviceSupportResourceSharing(device)) {
|
||||
if (device &&
|
||||
device->GetFeatureLevel() >= D3D_FEATURE_LEVEL_10_0 &&
|
||||
!DoesD3D11DeviceSupportResourceSharing(device)) {
|
||||
// bug 1083071 - bad things - fall back to basic layers
|
||||
// This should not happen aside from driver bugs, and in particular
|
||||
// should not happen on our test machines, so let's NS_ERROR to ensure
|
||||
|
|
Загрузка…
Ссылка в новой задаче