This commit is contained in:
Ryan VanderMeulen 2015-02-04 14:42:46 -05:00
Родитель 8c68f9d15c 9ffcc5d9e5
Коммит c528ddda2c
204 изменённых файлов: 3194 добавлений и 1177 удалений

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

@ -693,50 +693,57 @@ pref("dom.ipc.processPriorityManager.backgroundLRUPoolLevels", 5);
// Kernel parameters for process priorities. These affect how processes are
// killed on low-memory and their relative CPU priorities.
//
// Note: The maximum nice value on Linux is 19, but the max value you should
// use here is 18. NSPR adds 1 to some threads' nice values, to mark
// low-priority threads. If the process priority manager were to renice a
// process (and all its threads) to 19, all threads would have the same
// niceness. Then when we reniced the process to (say) 10, all threads would
// /still/ have the same niceness; we'd effectively have erased NSPR's thread
// priorities.
// The kernel can only accept 6 (OomScoreAdjust, KillUnderKB) pairs. But it is
// okay, kernel will still kill processes with larger OomScoreAdjust first even
// its OomScoreAdjust don't have a corresponding KillUnderKB.
pref("hal.processPriorityManager.gonk.MASTER.OomScoreAdjust", 0);
pref("hal.processPriorityManager.gonk.MASTER.KillUnderKB", 4096);
pref("hal.processPriorityManager.gonk.MASTER.Nice", 0);
pref("hal.processPriorityManager.gonk.MASTER.cgroup", "");
pref("hal.processPriorityManager.gonk.PREALLOC.OomScoreAdjust", 67);
pref("hal.processPriorityManager.gonk.PREALLOC.Nice", 18);
pref("hal.processPriorityManager.gonk.PREALLOC.cgroup", "apps/bg_non_interactive");
pref("hal.processPriorityManager.gonk.FOREGROUND_HIGH.OomScoreAdjust", 67);
pref("hal.processPriorityManager.gonk.FOREGROUND_HIGH.KillUnderKB", 5120);
pref("hal.processPriorityManager.gonk.FOREGROUND_HIGH.Nice", 0);
pref("hal.processPriorityManager.gonk.FOREGROUND_HIGH.cgroup", "apps/critical");
pref("hal.processPriorityManager.gonk.FOREGROUND.OomScoreAdjust", 134);
pref("hal.processPriorityManager.gonk.FOREGROUND.KillUnderKB", 6144);
pref("hal.processPriorityManager.gonk.FOREGROUND.Nice", 1);
pref("hal.processPriorityManager.gonk.FOREGROUND.cgroup", "apps");
pref("hal.processPriorityManager.gonk.FOREGROUND_KEYBOARD.OomScoreAdjust", 200);
pref("hal.processPriorityManager.gonk.FOREGROUND_KEYBOARD.Nice", 1);
pref("hal.processPriorityManager.gonk.FOREGROUND_KEYBOARD.cgroup", "apps");
pref("hal.processPriorityManager.gonk.BACKGROUND_PERCEIVABLE.OomScoreAdjust", 400);
pref("hal.processPriorityManager.gonk.BACKGROUND_PERCEIVABLE.KillUnderKB", 7168);
pref("hal.processPriorityManager.gonk.BACKGROUND_PERCEIVABLE.Nice", 7);
pref("hal.processPriorityManager.gonk.BACKGROUND_PERCEIVABLE.cgroup", "apps/bg_perceivable");
pref("hal.processPriorityManager.gonk.BACKGROUND_HOMESCREEN.OomScoreAdjust", 534);
pref("hal.processPriorityManager.gonk.BACKGROUND_HOMESCREEN.KillUnderKB", 8192);
pref("hal.processPriorityManager.gonk.BACKGROUND_HOMESCREEN.Nice", 18);
pref("hal.processPriorityManager.gonk.BACKGROUND_HOMESCREEN.cgroup", "apps/bg_non_interactive");
pref("hal.processPriorityManager.gonk.BACKGROUND.OomScoreAdjust", 667);
pref("hal.processPriorityManager.gonk.BACKGROUND.KillUnderKB", 20480);
pref("hal.processPriorityManager.gonk.BACKGROUND.Nice", 18);
pref("hal.processPriorityManager.gonk.BACKGROUND.cgroup", "apps/bg_non_interactive");
// Processes get this niceness when they have low CPU priority.
pref("hal.processPriorityManager.gonk.LowCPUNice", 18);
// Control group definitions (i.e., CPU priority groups) for B2G processes.
// Foreground apps
pref("hal.processPriorityManager.gonk.cgroups.apps.cpu_shares", 1024);
pref("hal.processPriorityManager.gonk.cgroups.apps.cpu_notify_on_migrate", 0);
// Foreground apps with high priority, 16x more CPU than foreground ones
pref("hal.processPriorityManager.gonk.cgroups.apps/critical.cpu_shares", 16384);
pref("hal.processPriorityManager.gonk.cgroups.apps/critical.cpu_notify_on_migrate", 0);
// Background perceivable apps, ~10x less CPU than foreground ones
pref("hal.processPriorityManager.gonk.cgroups.apps/bg_perceivable.cpu_shares", 103);
pref("hal.processPriorityManager.gonk.cgroups.apps/bg_perceivable.cpu_notify_on_migrate", 0);
// Background apps, ~20x less CPU than foreground ones and ~2x less than perceivable ones
pref("hal.processPriorityManager.gonk.cgroups.apps/bg_non_interactive.cpu_shares", 52);
pref("hal.processPriorityManager.gonk.cgroups.apps/bg_non_interactive.cpu_notify_on_migrate", 0);
// By default the compositor thread on gonk runs without real-time priority. RT
// priority can be enabled by setting this pref to a value between 1 and 99.

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

@ -266,7 +266,7 @@ function initPluginsRow() {
let vulnerableLabel = document.getElementById("browserBundle").getString("pluginActivateVulnerable.label");
let pluginHost = Components.classes["@mozilla.org/plugin/host;1"].getService(Components.interfaces.nsIPluginHost);
let permissionMap = Map();
let permissionMap = new Map();
for (let plugin of pluginHost.getPluginTags()) {
if (plugin.disabled) {

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

@ -28,7 +28,7 @@ const { Promise: promise } = Cu.import("resource://gre/modules/Promise.jsm", {})
this.EXPORTED_SYMBOLS = ["BrowserToolboxProcess"];
let processes = Set();
let processes = new Set();
/**
* Constructor for creating a process that will hold a chrome toolbox.

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

@ -2116,7 +2116,7 @@ Scope.prototype = {
// Creating maps and arrays thousands of times for variables or properties
// with a large number of children fills up a lot of memory. Make sure
// these are instantiated only if needed.
DevToolsUtils.defineLazyPrototypeGetter(Scope.prototype, "_store", Map);
DevToolsUtils.defineLazyPrototypeGetter(Scope.prototype, "_store", () => new Map());
DevToolsUtils.defineLazyPrototypeGetter(Scope.prototype, "_enumItems", Array);
DevToolsUtils.defineLazyPrototypeGetter(Scope.prototype, "_nonEnumItems", Array);

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

@ -617,7 +617,7 @@ Item.prototype = {
// Creating maps thousands of times for widgets with a large number of children
// fills up a lot of memory. Make sure these are instantiated only if needed.
DevToolsUtils.defineLazyPrototypeGetter(Item.prototype, "_itemsByElement", Map);
DevToolsUtils.defineLazyPrototypeGetter(Item.prototype, "_itemsByElement", () => new Map());
/**
* Some generic Widget methods handling Item instances.

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

@ -318,43 +318,6 @@ FindObjectClass(JSContext* cx, JSObject* aGlobalObject)
sObjectClass = js::GetObjectJSClass(obj);
}
static inline nsresult
WrapNative(JSContext *cx, nsISupports *native,
nsWrapperCache *cache, const nsIID* aIID, JS::MutableHandle<JS::Value> vp,
bool aAllowWrapping)
{
if (!native) {
vp.setNull();
return NS_OK;
}
JSObject *wrapper = xpc_FastGetCachedWrapper(cx, cache, vp);
if (wrapper) {
return NS_OK;
}
JS::Rooted<JSObject*> scope(cx, JS::CurrentGlobalOrNull(cx));
return nsDOMClassInfo::XPConnect()->WrapNativeToJSVal(cx, scope, native,
cache, aIID,
aAllowWrapping, vp);
}
static inline nsresult
WrapNative(JSContext *cx, nsISupports *native, const nsIID* aIID,
bool aAllowWrapping, JS::MutableHandle<JS::Value> vp)
{
return WrapNative(cx, native, nullptr, aIID, vp, aAllowWrapping);
}
// Same as the WrapNative above, but use these if aIID is nsISupports' IID.
static inline nsresult
WrapNative(JSContext *cx, nsISupports *native,
bool aAllowWrapping, JS::MutableHandle<JS::Value> vp)
{
return WrapNative(cx, native, nullptr, nullptr, vp, aAllowWrapping);
}
// Helper to handle torn-down inner windows.
static inline nsresult
SetParentToWindow(nsGlobalWindow *win, JSObject **parent)
@ -1349,7 +1312,7 @@ BaseStubConstructor(nsIWeakReference* aWeakOwner,
}
js::AssertSameCompartment(cx, obj);
return WrapNative(cx, native, true, args.rval());
return nsContentUtils::WrapNative(cx, native, args.rval(), true);
}
static nsresult
@ -1792,7 +1755,7 @@ nsDOMConstructor::ToString(nsAString &aResult)
static nsresult
GetXPCProto(nsIXPConnect *aXPConnect, JSContext *cx, nsGlobalWindow *aWin,
const nsGlobalNameStruct *aNameStruct,
nsIXPConnectJSObjectHolder **aProto)
JS::MutableHandle<JSObject*> aProto)
{
NS_ASSERTION(aNameStruct->mType ==
nsGlobalNameStruct::eTypeClassConstructor ||
@ -1813,18 +1776,14 @@ GetXPCProto(nsIXPConnect *aXPConnect, JSContext *cx, nsGlobalWindow *aWin,
}
NS_ENSURE_TRUE(ci, NS_ERROR_UNEXPECTED);
nsCOMPtr<nsIXPConnectJSObjectHolder> proto_holder;
nsresult rv =
aXPConnect->GetWrappedNativePrototype(cx, aWin->GetGlobalJSObject(), ci,
aProto);
getter_AddRefs(proto_holder));
NS_ENSURE_SUCCESS(rv, rv);
JS::Rooted<JSObject*> proto_obj(cx, (*aProto)->GetJSObject());
if (!JS_WrapObject(cx, &proto_obj)) {
return NS_ERROR_FAILURE;
}
NS_IF_RELEASE(*aProto);
return aXPConnect->HoldObject(cx, proto_obj, aProto);
aProto.set(proto_holder->GetJSObject());
return JS_WrapObject(cx, aProto) ? NS_OK : NS_ERROR_FAILURE;
}
// Either ci_data must be non-null or name_struct must be non-null and of type
@ -1852,8 +1811,9 @@ ResolvePrototype(nsIXPConnect *aXPConnect, nsGlobalWindow *aWin, JSContext *cx,
JS::Rooted<JS::Value> v(cx);
js::AssertSameCompartment(cx, obj);
rv = WrapNative(cx, constructor, &NS_GET_IID(nsIDOMDOMConstructor),
false, &v);
rv = nsContentUtils::WrapNative(cx, constructor,
&NS_GET_IID(nsIDOMDOMConstructor), &v,
false);
NS_ENSURE_SUCCESS(rv, rv);
FillPropertyDescriptor(ctorDesc, obj, 0, v);
@ -2223,8 +2183,9 @@ nsWindowSH::GlobalResolve(nsGlobalWindow *aWin, JSContext *cx,
JS::Rooted<JS::Value> v(cx);
js::AssertSameCompartment(cx, obj);
rv = WrapNative(cx, constructor, &NS_GET_IID(nsIDOMDOMConstructor),
false, &v);
rv = nsContentUtils::WrapNative(cx, constructor,
&NS_GET_IID(nsIDOMDOMConstructor), &v,
false);
NS_ENSURE_SUCCESS(rv, rv);
JS::Rooted<JSObject*> class_obj(cx, &v.toObject());
@ -2255,10 +2216,12 @@ nsWindowSH::GlobalResolve(nsGlobalWindow *aWin, JSContext *cx,
// Create the XPConnect prototype for our classinfo, PostCreateProto will
// set up the prototype chain. This will go ahead and define things on the
// actual window's global.
nsCOMPtr<nsIXPConnectJSObjectHolder> proto_holder;
JS::Rooted<JSObject*> dot_prototype(cx);
rv = GetXPCProto(nsDOMClassInfo::sXPConnect, cx, aWin, name_struct,
getter_AddRefs(proto_holder));
&dot_prototype);
NS_ENSURE_SUCCESS(rv, rv);
MOZ_ASSERT(dot_prototype);
bool isXray = xpc::WrapperFactory::IsXrayWrapper(obj);
MOZ_ASSERT_IF(obj != aWin->GetGlobalJSObject(), isXray);
if (!isXray) {
@ -2269,9 +2232,6 @@ nsWindowSH::GlobalResolve(nsGlobalWindow *aWin, JSContext *cx,
// This is the Xray case. Look up the constructor object for this
// prototype.
JS::Rooted<JSObject*> dot_prototype(cx, proto_holder->GetJSObject());
NS_ENSURE_STATE(dot_prototype);
const nsDOMClassInfoData *ci_data;
if (name_struct->mType == nsGlobalNameStruct::eTypeClassConstructor) {
ci_data = &sClassInfoData[name_struct->mDOMClassInfoID];
@ -2301,13 +2261,11 @@ nsWindowSH::GlobalResolve(nsGlobalWindow *aWin, JSContext *cx,
// We need to use the XPConnect prototype for the DOM class that this
// constructor is an alias for (for example for Image we need the prototype
// for HTMLImageElement).
nsCOMPtr<nsIXPConnectJSObjectHolder> proto_holder;
JS::Rooted<JSObject*> dot_prototype(cx);
rv = GetXPCProto(nsDOMClassInfo::sXPConnect, cx, aWin, alias_struct,
getter_AddRefs(proto_holder));
&dot_prototype);
NS_ENSURE_SUCCESS(rv, rv);
JSObject* dot_prototype = proto_holder->GetJSObject();
NS_ENSURE_STATE(dot_prototype);
MOZ_ASSERT(dot_prototype);
const nsDOMClassInfoData *ci_data;
if (alias_struct->mType == nsGlobalNameStruct::eTypeClassConstructor) {
@ -2332,8 +2290,9 @@ nsWindowSH::GlobalResolve(nsGlobalWindow *aWin, JSContext *cx,
JS::Rooted<JS::Value> val(cx);
js::AssertSameCompartment(cx, obj);
rv = WrapNative(cx, constructor, &NS_GET_IID(nsIDOMDOMConstructor),
true, &val);
rv = nsContentUtils::WrapNative(cx, constructor,
&NS_GET_IID(nsIDOMDOMConstructor), &val,
true);
NS_ENSURE_SUCCESS(rv, rv);
NS_ASSERTION(val.isObject(), "Why didn't we get a JSObject?");
@ -2370,7 +2329,7 @@ nsWindowSH::GlobalResolve(nsGlobalWindow *aWin, JSContext *cx,
NS_ENSURE_TRUE(inner, NS_ERROR_UNEXPECTED);
}
rv = WrapNative(cx, native, true, &prop_val);
rv = nsContentUtils::WrapNative(cx, native, &prop_val, true);
}
NS_ENSURE_SUCCESS(rv, rv);

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

@ -29,8 +29,9 @@ interface nsISettingsServiceLock : nsISupports
void get(in string aName, in nsISettingsServiceCallback aCallback);
};
[scriptable, uuid(0505acf0-8e76-11e3-baa8-0800200c9a66)]
[scriptable, uuid(d1ed155c-9f90-47bb-91c2-7eac54d69f4b)]
interface nsISettingsService : nsISupports
{
nsISettingsServiceLock createLock([optional] in nsISettingsTransactionCompleteCallback aCallback);
void receiveMessage(in jsval aMessage);
};

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

@ -201,15 +201,6 @@ IsAndroidAvailable()
#ifndef MOZ_WIDGET_ANDROID
return false;
#else
// PowerVR is very slow at texture allocation for some reason, which causes poor performance.
nsCOMPtr<nsIGfxInfo> gfxInfo = do_GetService("@mozilla.org/gfx/info;1");
nsString vendor;
if (NS_FAILED(gfxInfo->GetAdapterVendorID(vendor)) ||
vendor.Find("Imagination") == 0) {
return nullptr;
}
// We need android.media.MediaCodec which exists in API level 16 and higher.
return AndroidBridge::Bridge()->GetAPIVersion() >= 16;
#endif

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

@ -70,26 +70,18 @@ public:
return MediaCodecDataDecoder::Input(aSample);
}
EGLImage CopySurface() {
if (!EnsureGLContext()) {
return nullptr;
}
nsRefPtr<layers::Image> img = mImageContainer->CreateImage(ImageFormat::SURFACE_TEXTURE);
layers::SurfaceTextureImage::Data data;
data.mSurfTex = mSurfaceTexture.get();
data.mSize = gfx::IntSize(mConfig.display_width, mConfig.display_height);
data.mOriginPos = gl::OriginPos::BottomLeft;
layers::SurfaceTextureImage* stImg = static_cast<layers::SurfaceTextureImage*>(img.get());
stImg->SetData(data);
bool WantCopy() {
// Allocating a texture is incredibly slow on PowerVR
return mGLContext->Vendor() != GLVendor::Imagination;
}
EGLImage CopySurface(layers::Image* img) {
mGLContext->MakeCurrent();
GLuint tex = CreateTextureForOffscreen(mGLContext, mGLContext->GetGLFormats(), data.mSize);
GLuint tex = CreateTextureForOffscreen(mGLContext, mGLContext->GetGLFormats(), img->GetSize());
GLBlitHelper helper(mGLContext);
if (!helper.BlitImageToTexture(img, data.mSize, tex, LOCAL_GL_TEXTURE_2D)) {
if (!helper.BlitImageToTexture(img, img->GetSize(), tex, LOCAL_GL_TEXTURE_2D)) {
mGLContext->fDeleteTextures(1, &tex);
return nullptr;
}
@ -109,39 +101,53 @@ public:
}
virtual nsresult PostOutput(BufferInfo::Param aInfo, MediaFormat::Param aFormat, Microseconds aDuration) MOZ_OVERRIDE {
VideoInfo videoInfo;
videoInfo.mDisplay = nsIntSize(mConfig.display_width, mConfig.display_height);
EGLImage eglImage = CopySurface();
if (!eglImage) {
if (!EnsureGLContext()) {
return NS_ERROR_FAILURE;
}
EGLSync eglSync = nullptr;
if (sEGLLibrary.IsExtensionSupported(GLLibraryEGL::KHR_fence_sync) &&
mGLContext->IsExtensionSupported(GLContext::OES_EGL_sync))
{
MOZ_ASSERT(mGLContext->IsCurrent());
eglSync = sEGLLibrary.fCreateSync(EGL_DISPLAY(),
LOCAL_EGL_SYNC_FENCE,
nullptr);
if (eglSync) {
mGLContext->fFlush();
}
} else {
NS_WARNING("No EGL fence support detected, rendering artifacts may occur!");
}
VideoInfo videoInfo;
videoInfo.mDisplay = nsIntSize(mConfig.display_width, mConfig.display_height);
nsRefPtr<layers::Image> img = mImageContainer->CreateImage(ImageFormat::EGLIMAGE);
layers::EGLImageImage::Data data;
data.mImage = eglImage;
data.mSync = eglSync;
data.mOwns = true;
nsRefPtr<layers::Image> img = mImageContainer->CreateImage(ImageFormat::SURFACE_TEXTURE);
layers::SurfaceTextureImage::Data data;
data.mSurfTex = mSurfaceTexture.get();
data.mSize = gfx::IntSize(mConfig.display_width, mConfig.display_height);
data.mOriginPos = gl::OriginPos::BottomLeft;
layers::EGLImageImage* typedImg = static_cast<layers::EGLImageImage*>(img.get());
typedImg->SetData(data);
layers::SurfaceTextureImage* stImg = static_cast<layers::SurfaceTextureImage*>(img.get());
stImg->SetData(data);
if (WantCopy()) {
EGLImage eglImage = CopySurface(img);
if (!eglImage) {
return NS_ERROR_FAILURE;
}
EGLSync eglSync = nullptr;
if (sEGLLibrary.IsExtensionSupported(GLLibraryEGL::KHR_fence_sync) &&
mGLContext->IsExtensionSupported(GLContext::OES_EGL_sync))
{
MOZ_ASSERT(mGLContext->IsCurrent());
eglSync = sEGLLibrary.fCreateSync(EGL_DISPLAY(),
LOCAL_EGL_SYNC_FENCE,
nullptr);
MOZ_ASSERT(eglSync);
mGLContext->fFlush();
} else {
NS_WARNING("No EGL fence support detected, rendering artifacts may occur!");
}
img = mImageContainer->CreateImage(ImageFormat::EGLIMAGE);
layers::EGLImageImage::Data data;
data.mImage = eglImage;
data.mSync = eglSync;
data.mOwns = true;
data.mSize = gfx::IntSize(mConfig.display_width, mConfig.display_height);
data.mOriginPos = gl::OriginPos::BottomLeft;
layers::EGLImageImage* typedImg = static_cast<layers::EGLImageImage*>(img.get());
typedImg->SetData(data);
}
nsresult rv;
int32_t flags;

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

@ -32,8 +32,7 @@ extern PRLogModuleInfo* GetMediaSourceAPILog();
namespace mozilla {
ContainerParser::ContainerParser()
: mInitData(new LargeDataBuffer())
, mHasInitData(false)
: mHasInitData(false)
{
}
@ -81,10 +80,15 @@ ContainerParser::GetRoundingError()
return 0;
}
bool
ContainerParser::HasCompleteInitData()
{
return mHasInitData && !!mInitData->Length();
}
LargeDataBuffer*
ContainerParser::InitData()
{
MOZ_ASSERT(mHasInitData);
return mInitData;
}
@ -107,7 +111,9 @@ public:
// ...
// DocType == "webm"
// ...
// 0x18538067 // Segment (must be "unknown" size)
// 0x18538067 // Segment (must be "unknown" size or contain a value large
// enough to include the Segment Information and Tracks
// elements that follow)
// 0x1549a966 // -> Segment Info
// 0x1654ae6b // -> One or more Tracks
if (aData->Length() >= 4 &&
@ -147,6 +153,8 @@ public:
mOffset = 0;
mParser = WebMBufferedParser(0);
mOverlappedMapping.Clear();
mInitData = new LargeDataBuffer();
mResource = new SourceBufferResource(NS_LITERAL_CSTRING("video/webm"));
}
// XXX if it only adds new mappings, overlapped but not available
@ -156,22 +164,27 @@ public:
mOverlappedMapping.Clear();
ReentrantMonitor dummy("dummy");
mParser.Append(aData->Elements(), aData->Length(), mapping, dummy);
if (mResource) {
mResource->AppendData(aData);
}
// XXX This is a bit of a hack. Assume if there are no timecodes
// present and it's an init segment that it's _just_ an init segment.
// We should be more precise.
if (initSegment) {
uint32_t length = aData->Length();
if (!mapping.IsEmpty()) {
length = mapping[0].mSyncOffset;
MOZ_ASSERT(length <= aData->Length());
}
MSE_DEBUG("WebMContainerParser(%p)::ParseStartAndEndTimestamps: Stashed init of %u bytes.",
this, length);
if (!mInitData->ReplaceElementsAt(0, mInitData->Length(),
aData->Elements(), length)) {
// Unlikely OOM
return false;
if (initSegment || !HasCompleteInitData()) {
if (mParser.mInitEndOffset > 0) {
MOZ_ASSERT(mParser.mInitEndOffset <= mResource->GetLength());
if (!mInitData->SetLength(mParser.mInitEndOffset)) {
// Super unlikely OOM
return false;
}
char* buffer = reinterpret_cast<char*>(mInitData->Elements());
mResource->ReadFromCache(buffer, 0, mParser.mInitEndOffset);
MSE_DEBUG("WebMContainerParser(%p)::ParseStartAndEndTimestamps: Stashed init of %u bytes.",
this, mParser.mInitEndOffset);
mResource = nullptr;
} else {
MSE_DEBUG("WebMContainerParser(%p)::ParseStartAndEndTimestamps: Incomplete init found.");
}
mHasInitData = true;
}
@ -273,6 +286,7 @@ public:
// manually. This allows the ContainerParser to be shared across different
// timestampOffsets.
mParser = new mp4_demuxer::MoofParser(mStream, 0, 0, &mMonitor);
mInitData = new LargeDataBuffer();
} else if (!mStream || !mParser) {
return false;
}
@ -284,16 +298,20 @@ public:
byteRanges.AppendElement(mbr);
mParser->RebuildFragmentedIndex(byteRanges);
if (initSegment) {
if (initSegment || !HasCompleteInitData()) {
const MediaByteRange& range = mParser->mInitRange;
MSE_DEBUG("MP4ContainerParser(%p)::ParseStartAndEndTimestamps: Stashed init of %u bytes.",
this, range.mEnd - range.mStart);
if (!mInitData->ReplaceElementsAt(0, mInitData->Length(),
aData->Elements() + range.mStart,
range.mEnd - range.mStart)) {
// Super unlikely OOM
return false;
uint32_t length = range.mEnd - range.mStart;
if (length) {
if (!mInitData->SetLength(length)) {
// Super unlikely OOM
return false;
}
char* buffer = reinterpret_cast<char*>(mInitData->Elements());
mResource->ReadFromCache(buffer, range.mStart, length);
MSE_DEBUG("MP4ContainerParser(%p)::ParseStartAndEndTimestamps: Stashed init of %u bytes.",
this, length);
} else {
MSE_DEBUG("MP4ContainerParser(%p)::ParseStartAndEndTimestamps: Incomplete init found.");
}
mHasInitData = true;
}
@ -322,7 +340,6 @@ public:
private:
nsRefPtr<MP4Stream> mStream;
nsAutoPtr<mp4_demuxer::MoofParser> mParser;
nsRefPtr<SourceBufferResource> mResource;
Monitor mMonitor;
};
#endif

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

@ -12,6 +12,7 @@
namespace mozilla {
class LargeDataBuffer;
class SourceBufferResource;
class ContainerParser {
public:
@ -48,10 +49,13 @@ public:
return mHasInitData;
}
bool HasCompleteInitData();
static ContainerParser* CreateForMIMEType(const nsACString& aType);
protected:
nsRefPtr<LargeDataBuffer> mInitData;
nsRefPtr<SourceBufferResource> mResource;
bool mHasInitData;
};

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

@ -248,13 +248,11 @@ MediaSource::RemoveSourceBuffer(SourceBuffer& aSourceBuffer, ErrorResult& aRv)
aRv.Throw(NS_ERROR_DOM_NOT_FOUND_ERR);
return;
}
if (sourceBuffer->Updating()) {
// TODO:
// abort stream append loop (if running)
// set updating to false
// fire "abort" at sourceBuffer
// fire "updateend" at sourceBuffer
}
sourceBuffer->AbortBufferAppend();
// TODO:
// abort stream append loop (if running)
// TODO:
// For all sourceBuffer audioTracks, videoTracks, textTracks:
// set sourceBuffer to null

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

@ -205,7 +205,7 @@ MediaSourceDecoder::DurationChanged(double aOldDuration, double aNewDuration)
}
void
MediaSourceDecoder::SetDecodedDuration(int64_t aDuration)
MediaSourceDecoder::SetInitialDuration(int64_t aDuration)
{
// Only use the decoded duration if one wasn't already
// set.
@ -218,7 +218,7 @@ MediaSourceDecoder::SetDecodedDuration(int64_t aDuration)
if (aDuration >= 0) {
duration /= USECS_PER_S;
}
DoSetMediaSourceDuration(duration);
SetMediaSourceDuration(duration, MSRangeRemovalAction::SKIP);
}
void
@ -226,13 +226,6 @@ MediaSourceDecoder::SetMediaSourceDuration(double aDuration, MSRangeRemovalActio
{
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
double oldDuration = mMediaSourceDuration;
DoSetMediaSourceDuration(aDuration);
ScheduleDurationChange(oldDuration, aDuration, aAction);
}
void
MediaSourceDecoder::DoSetMediaSourceDuration(double aDuration)
{
if (aDuration >= 0) {
mDecoderStateMachine->SetDuration(aDuration * USECS_PER_S);
mMediaSourceDuration = aDuration;
@ -243,6 +236,7 @@ MediaSourceDecoder::DoSetMediaSourceDuration(double aDuration)
if (mReader) {
mReader->SetMediaSourceDuration(mMediaSourceDuration);
}
ScheduleDurationChange(oldDuration, aDuration, aAction);
}
void

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

@ -56,7 +56,7 @@ public:
void Ended();
bool IsExpectingMoreData() MOZ_OVERRIDE;
void SetDecodedDuration(int64_t aDuration);
void SetInitialDuration(int64_t aDuration);
void SetMediaSourceDuration(double aDuration, MSRangeRemovalAction aAction);
double GetMediaSourceDuration();
void DurationChanged(double aOldDuration, double aNewDuration);

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

@ -897,8 +897,6 @@ MediaSourceReader::ReadMetadata(MediaInfo* aInfo, MetadataTags** aTags)
return NS_ERROR_FAILURE;
}
int64_t maxDuration = -1;
if (mAudioTrack) {
MOZ_ASSERT(mAudioTrack->IsReady());
mAudioReader = mAudioTrack->Decoders()[0]->GetReader();
@ -907,9 +905,9 @@ MediaSourceReader::ReadMetadata(MediaInfo* aInfo, MetadataTags** aTags)
MOZ_ASSERT(info.HasAudio());
mInfo.mAudio = info.mAudio;
mInfo.mIsEncrypted = mInfo.mIsEncrypted || info.mIsEncrypted;
maxDuration = std::max(maxDuration, mAudioReader->GetDecoder()->GetMediaDuration());
MSE_DEBUG("MediaSourceReader(%p)::ReadMetadata audio reader=%p maxDuration=%lld",
this, mAudioReader.get(), maxDuration);
MSE_DEBUG("MediaSourceReader(%p)::ReadMetadata audio reader=%p duration=%lld",
this, mAudioReader.get(),
mAudioReader->GetDecoder()->GetMediaDuration());
}
if (mVideoTrack) {
@ -920,17 +918,11 @@ MediaSourceReader::ReadMetadata(MediaInfo* aInfo, MetadataTags** aTags)
MOZ_ASSERT(info.HasVideo());
mInfo.mVideo = info.mVideo;
mInfo.mIsEncrypted = mInfo.mIsEncrypted || info.mIsEncrypted;
maxDuration = std::max(maxDuration, mVideoReader->GetDecoder()->GetMediaDuration());
MSE_DEBUG("MediaSourceReader(%p)::ReadMetadata video reader=%p maxDuration=%lld",
this, mVideoReader.get(), maxDuration);
MSE_DEBUG("MediaSourceReader(%p)::ReadMetadata video reader=%p duration=%lld",
this, mVideoReader.get(),
mVideoReader->GetDecoder()->GetMediaDuration());
}
if (!maxDuration) {
// Treat a duration of 0 as infinity
maxDuration = -1;
}
static_cast<MediaSourceDecoder*>(mDecoder)->SetDecodedDuration(maxDuration);
*aInfo = mInfo;
*aTags = nullptr; // TODO: Handle metadata.

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

@ -46,16 +46,18 @@ class AppendDataRunnable : public nsRunnable {
public:
AppendDataRunnable(SourceBuffer* aSourceBuffer,
LargeDataBuffer* aData,
double aTimestampOffset)
double aTimestampOffset,
uint32_t aUpdateID)
: mSourceBuffer(aSourceBuffer)
, mData(aData)
, mTimestampOffset(aTimestampOffset)
, mUpdateID(aUpdateID)
{
}
NS_IMETHOD Run() MOZ_OVERRIDE MOZ_FINAL {
mSourceBuffer->AppendData(mData, mTimestampOffset);
mSourceBuffer->AppendData(mData, mTimestampOffset, mUpdateID);
return NS_OK;
}
@ -64,6 +66,7 @@ private:
nsRefPtr<SourceBuffer> mSourceBuffer;
nsRefPtr<LargeDataBuffer> mData;
double mTimestampOffset;
uint32_t mUpdateID;
};
class RangeRemovalRunnable : public nsRunnable {
@ -214,20 +217,23 @@ SourceBuffer::Abort(ErrorResult& aRv)
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return;
}
Abort();
AbortBufferAppend();
mTrackBuffer->ResetParserState();
mAppendWindowStart = 0;
mAppendWindowEnd = PositiveInfinity<double>();
// Discard the current decoder so no new data will be added to it.
MSE_DEBUG("SourceBuffer(%p)::Abort() Discarding decoder", this);
mTrackBuffer->DiscardDecoder();
mTrackBuffer->DiscardCurrentDecoder();
}
void
SourceBuffer::Abort()
SourceBuffer::AbortBufferAppend()
{
if (mUpdating) {
// TODO: Abort segment parser loop, buffer append, and stream append loop algorithms.
mPendingAppend.DisconnectIfExists();
// TODO: Abort segment parser loop, and stream append loop algorithms.
// cancel any pending buffer append.
mTrackBuffer->AbortAppendData();
AbortUpdating();
}
}
@ -269,6 +275,8 @@ SourceBuffer::RangeRemoval(double aStart, double aEnd)
void
SourceBuffer::DoRangeRemoval(double aStart, double aEnd)
{
MSE_DEBUG("SourceBuffer(%p)::DoRangeRemoval (updating:%d)",
this, mUpdating);
if (!mUpdating) {
// abort was called in between.
return;
@ -287,7 +295,7 @@ SourceBuffer::Detach()
{
MOZ_ASSERT(NS_IsMainThread());
MSE_DEBUG("SourceBuffer(%p)::Detach", this);
Abort();
AbortBufferAppend();
if (mTrackBuffer) {
mTrackBuffer->Detach();
}
@ -312,6 +320,7 @@ SourceBuffer::SourceBuffer(MediaSource* aMediaSource, const nsACString& aType)
, mTimestampOffset(0)
, mAppendMode(SourceBufferAppendMode::Segments)
, mUpdating(false)
, mUpdateID(0)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aMediaSource);
@ -363,6 +372,7 @@ SourceBuffer::StartUpdating()
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!mUpdating);
mUpdating = true;
mUpdateID++;
QueueAsyncSimpleEvent("updatestart");
}
@ -397,9 +407,11 @@ SourceBuffer::AbortUpdating()
void
SourceBuffer::CheckEndTime()
{
MOZ_ASSERT(NS_IsMainThread());
// Check if we need to update mMediaSource duration
double endTime = GetBufferedEnd();
if (endTime > mMediaSource->Duration()) {
double duration = mMediaSource->Duration();
if (endTime > duration) {
mMediaSource->SetDuration(endTime, MSRangeRemovalAction::SKIP);
}
}
@ -418,14 +430,15 @@ SourceBuffer::AppendData(const uint8_t* aData, uint32_t aLength, ErrorResult& aR
MOZ_ASSERT(mAppendMode == SourceBufferAppendMode::Segments,
"We don't handle timestampOffset for sequence mode yet");
nsRefPtr<nsIRunnable> task =
new AppendDataRunnable(this, data, mTimestampOffset);
new AppendDataRunnable(this, data, mTimestampOffset, mUpdateID);
NS_DispatchToMainThread(task);
}
void
SourceBuffer::AppendData(LargeDataBuffer* aData, double aTimestampOffset)
SourceBuffer::AppendData(LargeDataBuffer* aData, double aTimestampOffset,
uint32_t aUpdateID)
{
if (!mUpdating) {
if (!mUpdating || aUpdateID != mUpdateID) {
// The buffer append algorithm has been interrupted by abort().
//
// If the sequence appendBuffer(), abort(), appendBuffer() occurs before
@ -436,22 +449,53 @@ SourceBuffer::AppendData(LargeDataBuffer* aData, double aTimestampOffset)
}
MOZ_ASSERT(mMediaSource);
MOZ_ASSERT(!mPendingAppend.Exists());
if (aData->Length()) {
if (!mTrackBuffer->AppendData(aData, aTimestampOffset * USECS_PER_S)) {
AppendError(true);
return;
}
if (!aData->Length()) {
StopUpdating();
return;
}
if (mTrackBuffer->HasInitSegment()) {
mMediaSource->QueueInitializationEvent();
}
mPendingAppend.Begin(mTrackBuffer->AppendData(aData, aTimestampOffset * USECS_PER_S)
->RefableThen(NS_GetCurrentThread(), __func__, this,
&SourceBuffer::AppendDataCompletedWithSuccess,
&SourceBuffer::AppendDataErrored));
}
void
SourceBuffer::AppendDataCompletedWithSuccess(bool aGotMedia)
{
mPendingAppend.Complete();
if (!mUpdating) {
// The buffer append algorithm has been interrupted by abort().
return;
}
if (mTrackBuffer->HasInitSegment()) {
mMediaSource->QueueInitializationEvent();
}
if (aGotMedia) {
CheckEndTime();
}
StopUpdating();
}
}
void
SourceBuffer::AppendDataErrored(nsresult aError)
{
mPendingAppend.Complete();
switch (aError) {
case NS_ERROR_ABORT:
// Nothing further to do as the trackbuffer has been shutdown.
// or append was aborted and abort() has handled all the events.
break;
default:
AppendError(true);
break;
}
}
void
SourceBuffer::AppendError(bool aDecoderError)

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

@ -7,6 +7,7 @@
#ifndef mozilla_dom_SourceBuffer_h_
#define mozilla_dom_SourceBuffer_h_
#include "MediaPromise.h"
#include "MediaSource.h"
#include "js/RootingAPI.h"
#include "mozilla/Assertions.h"
@ -32,6 +33,7 @@ class ErrorResult;
class LargeDataBuffer;
class TrackBuffer;
template <typename T> class AsyncEventRunner;
typedef MediaPromise<bool, nsresult, /* IsExclusive = */ true> TrackBufferAppendPromise;
namespace dom {
@ -80,7 +82,7 @@ public:
void AppendBuffer(const ArrayBufferView& aData, ErrorResult& aRv);
void Abort(ErrorResult& aRv);
void Abort();
void AbortBufferAppend();
void Remove(double aStart, double aEnd, ErrorResult& aRv);
/** End WebIDL Methods. */
@ -139,7 +141,8 @@ private:
// Shared implementation of AppendBuffer overloads.
void AppendData(const uint8_t* aData, uint32_t aLength, ErrorResult& aRv);
void AppendData(LargeDataBuffer* aData, double aTimestampOffset);
void AppendData(LargeDataBuffer* aData, double aTimestampOffset,
uint32_t aAppendID);
// Implement the "Append Error Algorithm".
// Will call endOfStream() with "decode" error if aDecodeError is true.
@ -153,6 +156,9 @@ private:
uint32_t aLength,
ErrorResult& aRv);
void AppendDataCompletedWithSuccess(bool aValue);
void AppendDataErrored(nsresult aError);
nsRefPtr<MediaSource> mMediaSource;
uint32_t mEvictionThreshold;
@ -166,6 +172,13 @@ private:
SourceBufferAppendMode mAppendMode;
bool mUpdating;
// Each time mUpdating is set to true, mUpdateID will be incremented.
// This allows for a queued AppendData task to identify if it was earlier
// aborted and another AppendData queued.
uint32_t mUpdateID;
MediaPromiseConsumerHolder<TrackBufferAppendPromise> mPendingAppend;
};
} // namespace dom

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

@ -114,6 +114,11 @@ public:
// Used by SourceBuffer.
void AppendData(LargeDataBuffer* aData);
void Ended();
bool IsEnded()
{
ReentrantMonitorAutoEnter mon(mMonitor);
return mEnded;
}
// Remove data from resource if it holds more than the threshold
// number of bytes. Returns amount evicted.
uint32_t EvictData(uint64_t aPlaybackOffset, uint32_t aThreshold);

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

@ -102,9 +102,19 @@ public:
return true;
}
size_t Length()
{
return mDecoders.Length();
}
void AppendElement(SourceBufferDecoder* aDecoder)
{
mDecoders.AppendElement(aDecoder);
}
private:
TrackBuffer* mOwner;
nsAutoTArray<nsRefPtr<SourceBufferDecoder>,2> mDecoders;
nsAutoTArray<nsRefPtr<SourceBufferDecoder>,1> mDecoders;
};
nsRefPtr<ShutdownPromise>
@ -112,6 +122,7 @@ TrackBuffer::Shutdown()
{
mParentDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
mShutdown = true;
mInitializationPromise.RejectIfExists(NS_ERROR_ABORT, __func__);
MOZ_ASSERT(mShutdownPromise.IsEmpty());
nsRefPtr<ShutdownPromise> p = mShutdownPromise.Ensure(__func__);
@ -138,48 +149,84 @@ TrackBuffer::ContinueShutdown()
return;
}
mCurrentDecoder = nullptr;
mInitializedDecoders.Clear();
mParentDecoder = nullptr;
mShutdownPromise.Resolve(true, __func__);
}
bool
nsRefPtr<TrackBufferAppendPromise>
TrackBuffer::AppendData(LargeDataBuffer* aData, int64_t aTimestampOffset)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mInitializationPromise.IsEmpty());
DecodersToInitialize decoders(this);
nsRefPtr<TrackBufferAppendPromise> p = mInitializationPromise.Ensure(__func__);
bool hadInitData = mParser->HasInitData();
bool hadCompleteInitData = mParser->HasCompleteInitData();
nsRefPtr<LargeDataBuffer> oldInit = mParser->InitData();
bool newInitData = mParser->IsInitSegmentPresent(aData);
// TODO: Run more of the buffer append algorithm asynchronously.
if (mParser->IsInitSegmentPresent(aData)) {
if (newInitData) {
MSE_DEBUG("TrackBuffer(%p)::AppendData: New initialization segment.", this);
if (!decoders.NewDecoder(aTimestampOffset)) {
return false;
}
} else if (!mParser->HasInitData()) {
} else if (!hadInitData) {
MSE_DEBUG("TrackBuffer(%p)::AppendData: Non-init segment appended during initialization.", this);
return false;
mInitializationPromise.Reject(NS_ERROR_FAILURE, __func__);
return p;
}
int64_t start, end;
if (mParser->ParseStartAndEndTimestamps(aData, start, end)) {
int64_t start = 0, end = 0;
bool gotMedia = mParser->ParseStartAndEndTimestamps(aData, start, end);
bool gotInit = mParser->HasCompleteInitData();
if (newInitData) {
if (!gotInit) {
// We need a new decoder, but we can't initialize it yet.
nsRefPtr<SourceBufferDecoder> decoder = NewDecoder(aTimestampOffset);
// The new decoder is stored in mDecoders/mCurrentDecoder, so we
// don't need to do anything with 'decoder'. It's only a placeholder.
if (!decoder) {
mInitializationPromise.Reject(NS_ERROR_FAILURE, __func__);
return p;
}
} else {
if (!decoders.NewDecoder(aTimestampOffset)) {
mInitializationPromise.Reject(NS_ERROR_FAILURE, __func__);
return p;
}
}
} else if (!hadCompleteInitData && gotInit) {
MOZ_ASSERT(mCurrentDecoder);
// Queue pending decoder for initialization now that we have a full
// init segment.
decoders.AppendElement(mCurrentDecoder);
}
if (gotMedia) {
start += aTimestampOffset;
end += aTimestampOffset;
if (mParser->IsMediaSegmentPresent(aData) &&
mLastEndTimestamp &&
if (mLastEndTimestamp &&
(!mParser->TimestampsFuzzyEqual(start, mLastEndTimestamp.value()) ||
mLastTimestampOffset != aTimestampOffset ||
mDecoderPerSegment || (mCurrentDecoder && mCurrentDecoder->WasTrimmed()))) {
mDecoderPerSegment ||
(mCurrentDecoder && mCurrentDecoder->WasTrimmed()))) {
MSE_DEBUG("TrackBuffer(%p)::AppendData: Data last=[%lld, %lld] overlaps [%lld, %lld]",
this, mLastStartTimestamp, mLastEndTimestamp.value(), start, end);
// This data is earlier in the timeline than data we have already
// processed, so we must create a new decoder to handle the decoding.
if (!decoders.NewDecoder(aTimestampOffset)) {
return false;
if (!newInitData) {
// This data is earlier in the timeline than data we have already
// processed or not continuous, so we must create a new decoder
// to handle the decoding.
if (!hadCompleteInitData || !decoders.NewDecoder(aTimestampOffset)) {
mInitializationPromise.Reject(NS_ERROR_FAILURE, __func__);
return p;
}
MSE_DEBUG("TrackBuffer(%p)::AppendData: Decoder marked as initialized.", this);
AppendDataToCurrentResource(oldInit, 0);
}
MSE_DEBUG("TrackBuffer(%p)::AppendData: Decoder marked as initialized.", this);
nsRefPtr<LargeDataBuffer> initData = mParser->InitData();
AppendDataToCurrentResource(initData, end - start);
mLastStartTimestamp = start;
} else {
MSE_DEBUG("TrackBuffer(%p)::AppendData: Segment last=[%lld, %lld] [%lld, %lld]",
@ -190,13 +237,21 @@ TrackBuffer::AppendData(LargeDataBuffer* aData, int64_t aTimestampOffset)
}
if (!AppendDataToCurrentResource(aData, end - start)) {
return false;
mInitializationPromise.Reject(NS_ERROR_FAILURE, __func__);
return p;
}
// Tell our reader that we have more data to ensure that playback starts if
// required when data is appended.
mParentDecoder->GetReader()->MaybeNotifyHaveData();
return true;
if (decoders.Length()) {
// We're going to have to wait for the decoder to initialize, the promise
// will be resolved once initialization completes.
return p;
} else if (gotMedia) {
// Tell our reader that we have more data to ensure that playback starts if
// required when data is appended.
mParentDecoder->GetReader()->MaybeNotifyHaveData();
}
mInitializationPromise.Resolve(gotMedia, __func__);
return p;
}
bool
@ -370,9 +425,9 @@ TrackBuffer::Buffered(dom::TimeRanges* aRanges)
double highestEndTime = 0;
for (uint32_t i = 0; i < mDecoders.Length(); ++i) {
for (uint32_t i = 0; i < mInitializedDecoders.Length(); ++i) {
nsRefPtr<dom::TimeRanges> r = new dom::TimeRanges();
mDecoders[i]->GetBuffered(r);
mInitializedDecoders[i]->GetBuffered(r);
if (r->Length() > 0) {
highestEndTime = std::max(highestEndTime, r->GetEndTime());
aRanges->Union(r, double(mParser->GetRoundingError()) / USECS_PER_S);
@ -388,7 +443,7 @@ TrackBuffer::NewDecoder(int64_t aTimestampOffset)
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mParentDecoder);
DiscardDecoder();
DiscardCurrentDecoder();
nsRefPtr<SourceBufferDecoder> decoder = mParentDecoder->CreateSubDecoder(mType, aTimestampOffset);
if (!decoder) {
@ -410,6 +465,7 @@ bool
TrackBuffer::QueueInitializeDecoder(SourceBufferDecoder* aDecoder)
{
if (NS_WARN_IF(!mTaskQueue)) {
mInitializationPromise.RejectIfExists(NS_ERROR_FAILURE, __func__);
return false;
}
@ -418,8 +474,9 @@ TrackBuffer::QueueInitializeDecoder(SourceBufferDecoder* aDecoder)
&TrackBuffer::InitializeDecoder,
aDecoder);
if (NS_FAILED(mTaskQueue->Dispatch(task))) {
MSE_DEBUG("MediaSourceReader(%p): Failed to enqueue decoder initialization task", this);
MSE_DEBUG("TrackBuffer(%p): Failed to enqueue decoder initialization task", this);
RemoveDecoder(aDecoder);
mInitializationPromise.RejectIfExists(NS_ERROR_FAILURE, __func__);
return false;
}
return true;
@ -428,6 +485,11 @@ TrackBuffer::QueueInitializeDecoder(SourceBufferDecoder* aDecoder)
void
TrackBuffer::InitializeDecoder(SourceBufferDecoder* aDecoder)
{
if (!mParentDecoder) {
MSE_DEBUG("TrackBuffer(%p) was shutdown. Aborting initialization.",
this);
return;
}
// ReadMetadata may block the thread waiting on data, so we must be able
// to leave the monitor while we call it. For the rest of this function
// we want to hold the monitor though, since we run on a different task queue
@ -435,12 +497,21 @@ TrackBuffer::InitializeDecoder(SourceBufferDecoder* aDecoder)
mParentDecoder->GetReentrantMonitor().AssertNotCurrentThreadIn();
ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor());
if (mCurrentDecoder != aDecoder) {
MSE_DEBUG("TrackBuffer(%p) append was cancelled. Aborting initialization.",
this);
// If we reached this point, the SourceBuffer would have disconnected
// the promise. So no need to reject it.
return;
}
// We may be shut down at any time by the reader on another thread. So we need
// to check for this each time we acquire the monitor. If that happens, we
// need to abort immediately, because the reader has forgotten about us, and
// important pieces of our state (like mTaskQueue) have also been torn down.
if (mShutdown) {
MSE_DEBUG("TrackBuffer(%p) was shut down. Aborting initialization.", this);
RemoveDecoder(aDecoder);
return;
}
@ -452,10 +523,28 @@ TrackBuffer::InitializeDecoder(SourceBufferDecoder* aDecoder)
MediaInfo mi;
nsAutoPtr<MetadataTags> tags; // TODO: Handle metadata.
nsresult rv;
// HACK WARNING:
// We only reach this point once we know that we have a complete init segment.
// We don't want the demuxer to do a blocking read as no more data can be
// appended while this routine is running. Marking the SourceBufferResource
// as ended will cause any incomplete reads to abort.
// As this decoder hasn't been initialized yet, the resource isn't yet in use
// and so it is safe to do so.
bool wasEnded = aDecoder->GetResource()->IsEnded();
if (!wasEnded) {
aDecoder->GetResource()->Ended();
}
{
ReentrantMonitorAutoExit mon(mParentDecoder->GetReentrantMonitor());
rv = reader->ReadMetadata(&mi, getter_Transfers(tags));
}
if (!wasEnded) {
// Adding an empty buffer will reopen the SourceBufferResource
nsRefPtr<LargeDataBuffer> emptyBuffer = new LargeDataBuffer;
aDecoder->GetResource()->AppendData(emptyBuffer);
}
// HACK END.
reader->SetIdle();
if (mShutdown) {
@ -475,6 +564,7 @@ TrackBuffer::InitializeDecoder(SourceBufferDecoder* aDecoder)
MSE_DEBUG("TrackBuffer(%p): Reader %p failed to initialize rv=%x audio=%d video=%d",
this, reader, rv, mi.HasAudio(), mi.HasVideo());
RemoveDecoder(aDecoder);
mInitializationPromise.RejectIfExists(NS_ERROR_FAILURE, __func__);
return;
}
@ -487,18 +577,63 @@ TrackBuffer::InitializeDecoder(SourceBufferDecoder* aDecoder)
this, reader, mi.mAudio.mRate, mi.mAudio.mChannels);
}
if (!RegisterDecoder(aDecoder)) {
// XXX: Need to signal error back to owning SourceBuffer.
MSE_DEBUG("TrackBuffer(%p): Reader %p not activated", this, reader);
RefPtr<nsIRunnable> task =
NS_NewRunnableMethodWithArg<SourceBufferDecoder*>(this,
&TrackBuffer::CompleteInitializeDecoder,
aDecoder);
if (NS_FAILED(NS_DispatchToMainThread(task))) {
MSE_DEBUG("TrackBuffer(%p): Failed to enqueue decoder initialization task", this);
RemoveDecoder(aDecoder);
mInitializationPromise.RejectIfExists(NS_ERROR_FAILURE, __func__);
return;
}
}
void
TrackBuffer::CompleteInitializeDecoder(SourceBufferDecoder* aDecoder)
{
if (!mParentDecoder) {
MSE_DEBUG("TrackBuffer(%p) was shutdown. Aborting initialization.",
this);
return;
}
ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor());
if (mCurrentDecoder != aDecoder) {
MSE_DEBUG("TrackBuffer(%p) append was cancelled. Aborting initialization.",
this);
// If we reached this point, the SourceBuffer would have disconnected
// the promise. So no need to reject it.
return;
}
if (mShutdown) {
MSE_DEBUG("TrackBuffer(%p) was shut down. Aborting initialization.", this);
RemoveDecoder(aDecoder);
return;
}
if (!RegisterDecoder(aDecoder)) {
MSE_DEBUG("TrackBuffer(%p): Reader %p not activated",
this, aDecoder->GetReader());
RemoveDecoder(aDecoder);
mInitializationPromise.RejectIfExists(NS_ERROR_FAILURE, __func__);
return;
}
int64_t duration = aDecoder->GetMediaDuration();
if (!duration) {
// Treat a duration of 0 as infinity
duration = -1;
}
mParentDecoder->SetInitialDuration(duration);
// Tell our reader that we have more data to ensure that playback starts if
// required when data is appended.
mParentDecoder->GetReader()->MaybeNotifyHaveData();
MSE_DEBUG("TrackBuffer(%p): Reader %p activated", this, reader);
MSE_DEBUG("TrackBuffer(%p): Reader %p activated",
this, aDecoder->GetReader());
mInitializationPromise.ResolveIfExists(aDecoder->GetRealMediaDuration() > 0, __func__);
}
bool
@ -541,12 +676,10 @@ TrackBuffer::RegisterDecoder(SourceBufferDecoder* aDecoder)
}
void
TrackBuffer::DiscardDecoder()
TrackBuffer::DiscardCurrentDecoder()
{
ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor());
if (mCurrentDecoder) {
mCurrentDecoder->GetResource()->Ended();
}
EndCurrentDecoder();
mCurrentDecoder = nullptr;
}
@ -564,7 +697,7 @@ TrackBuffer::Detach()
{
MOZ_ASSERT(NS_IsMainThread());
if (mCurrentDecoder) {
DiscardDecoder();
DiscardCurrentDecoder();
}
}
@ -572,7 +705,7 @@ bool
TrackBuffer::HasInitSegment()
{
ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor());
return mParser->HasInitData();
return mParser->HasCompleteInitData();
}
bool
@ -580,7 +713,7 @@ TrackBuffer::IsReady()
{
ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor());
MOZ_ASSERT((mInfo.HasAudio() || mInfo.HasVideo()) || mInitializedDecoders.IsEmpty());
return mParser->HasInitData() && (mInfo.HasAudio() || mInfo.HasVideo());
return mInfo.HasAudio() || mInfo.HasVideo();
}
bool
@ -626,7 +759,24 @@ TrackBuffer::ResetDecode()
void
TrackBuffer::ResetParserState()
{
// TODO
MOZ_ASSERT(NS_IsMainThread());
if (mParser->HasInitData() && !mParser->HasCompleteInitData()) {
// We have an incomplete init segment pending. reset current parser and
// discard the current decoder.
mParser = ContainerParser::CreateForMIMEType(mType);
DiscardCurrentDecoder();
}
}
void
TrackBuffer::AbortAppendData()
{
DiscardCurrentDecoder();
// The SourceBuffer would have disconnected its promise.
// However we must ensure that the MediaPromiseHolder handle all pending
// promises.
mInitializationPromise.RejectIfExists(NS_ERROR_ABORT, __func__);
}
const nsTArray<nsRefPtr<SourceBufferDecoder>>&
@ -717,7 +867,7 @@ TrackBuffer::RemoveDecoder(SourceBufferDecoder* aDecoder)
mDecoders.RemoveElement(aDecoder);
if (mCurrentDecoder == aDecoder) {
DiscardDecoder();
DiscardCurrentDecoder();
}
}
aDecoder->GetReader()->GetTaskQueue()->Dispatch(task);

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

@ -7,8 +7,8 @@
#ifndef MOZILLA_TRACKBUFFER_H_
#define MOZILLA_TRACKBUFFER_H_
#include "SourceBuffer.h"
#include "SourceBufferDecoder.h"
#include "MediaPromise.h"
#include "mozilla/Assertions.h"
#include "mozilla/Attributes.h"
#include "mozilla/mozalloc.h"
@ -40,7 +40,8 @@ public:
// Append data to the current decoder. Also responsible for calling
// NotifyDataArrived on the decoder to keep buffered range computation up
// to date. Returns false if the append failed.
bool AppendData(LargeDataBuffer* aData, int64_t aTimestampOffset /* microseconds */);
nsRefPtr<TrackBufferAppendPromise> AppendData(LargeDataBuffer* aData,
int64_t aTimestampOffset /* microseconds */);
// Evicts data held in the current decoders SourceBufferResource from the
// start of the buffer through to aPlaybackTime. aThreshold is used to
@ -61,7 +62,7 @@ public:
// Mark the current decoder's resource as ended, clear mCurrentDecoder and
// reset mLast{Start,End}Timestamp.
void DiscardDecoder();
void DiscardCurrentDecoder();
// Mark the current decoder's resource as ended.
void EndCurrentDecoder();
@ -100,6 +101,9 @@ public:
// Times are in microseconds.
bool RangeRemoval(int64_t aStart, int64_t aEnd);
// Abort any pending appendBuffer by rejecting any pending promises.
void AbortAppendData();
#ifdef MOZ_EME
nsresult SetCDMProxy(CDMProxy* aProxy);
#endif
@ -130,6 +134,11 @@ private:
// Runs decoder initialization including calling ReadMetadata. Runs as an
// event on the decode thread pool.
void InitializeDecoder(SourceBufferDecoder* aDecoder);
// Once decoder has been initialized, set mediasource duration if required
// and resolve any pending InitializationPromise.
// Setting the mediasource duration must be done on the main thread.
// TODO: Why is that so?
void CompleteInitializeDecoder(SourceBufferDecoder* aDecoder);
// Adds a successfully initialized decoder to mDecoders and (if it's the
// first decoder initialized), initializes mHasAudio/mHasVideo. Called
@ -193,6 +202,9 @@ private:
MediaPromiseHolder<ShutdownPromise> mShutdownPromise;
bool mDecoderPerSegment;
bool mShutdown;
MediaPromiseHolder<TrackBufferAppendPromise> mInitializationPromise;
};
} // namespace mozilla

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

@ -455,6 +455,7 @@ skip-if = appname == "seamonkey"
[test_playback.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
[test_playback_errors.html]
skip-if = toolkit == 'gonk' # bug 1128845
[test_playback_rate.html]
skip-if = (toolkit == 'android' && processor == 'x86') #bug 845162
[test_playback_rate_playpause.html]
@ -467,7 +468,7 @@ skip-if = true # bug 493692
[test_progress.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
[test_reactivate.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
skip-if = toolkit == 'gonk' || (toolkit == 'android' && processor == 'x86') #x86 only bug 914439 and bug 1128845 on gonk
[test_readyState.html]
[test_referer.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only
@ -525,7 +526,7 @@ skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
[test_trackelementevent.html]
[test_trackevent.html]
[test_unseekable.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only
skip-if = toolkit == 'gonk' || (toolkit == 'android' && processor == 'x86') #x86 only and bug 1128845 on gonk
[test_video_to_canvas.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
[test_video_in_audio_element.html]

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

@ -23,6 +23,7 @@ var manager = new MediaTestManager;
function onloaded(event) {
var v = event.target;
v.removeEventListener("loadedmetadata", onloaded);
v.currentTime = v.duration;
return;
}
@ -31,15 +32,18 @@ function checkNotPlaying(v) {
ok(v.currentTime == 0, "Should not be playing after seek to end and back to beginning");
v._finished = true;
manager.finished(v.token);
removeNodeAndSource(v);
}
function onseeked(event) {
var v = event.target;
v.removeEventListener("seeked", onseeked);
setTimeout(function() { checkNotPlaying(v); }, 500);
}
function onended(event) {
var v = event.target;
v.removeEventListener("ended", onended);
if (v._finished)
return;
v.addEventListener("seeked", onseeked, false);

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

@ -14,13 +14,16 @@ var manager = new MediaTestManager;
function ended(evt) {
var v = evt.target;
v.removeEventListener("ended", ended);
is(v.gotPause, true, "We should have received a \"pause\" event.")
is(v.paused, true, v._name + " must be paused after end");
manager.finished(v.token);
removeNodeAndSource(v);
}
function pause(evt) {
var v = evt.target;
v.removeEventListener("pause", pause);
v.gotPause = true;
}

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

@ -47,6 +47,7 @@ function startTest(test, token) {
}
vout.parentNode.removeChild(vout);
manager.finished(vout.token);
removeNodeAndSource(v);
}}(test, vout, stream);
vout.addEventListener("ended", checkEnded, false);

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

@ -37,6 +37,7 @@ void WebMBufferedParser::Append(const unsigned char* aBuffer, uint32_t aLength,
{
static const uint32_t SEGMENT_ID = 0x18538067;
static const uint32_t SEGINFO_ID = 0x1549a966;
static const uint32_t TRACKS_ID = 0x1654AE6B;
static const uint32_t CLUSTER_ID = 0x1f43b675;
static const uint32_t TIMECODESCALE_ID = 0x2ad7b1;
static const unsigned char TIMECODE_ID = 0xe7;
@ -115,6 +116,10 @@ void WebMBufferedParser::Append(const unsigned char* aBuffer, uint32_t aLength,
mState = READ_VINT;
mNextState = READ_BLOCK_TIMECODE;
break;
case TRACKS_ID:
mSkipBytes = mElement.mSize.mValue;
mState = CHECK_INIT_FOUND;
break;
default:
mSkipBytes = mElement.mSize.mValue;
mState = SKIP_DATA;
@ -192,6 +197,20 @@ void WebMBufferedParser::Append(const unsigned char* aBuffer, uint32_t aLength,
mState = mNextState;
}
break;
case CHECK_INIT_FOUND:
if (mSkipBytes) {
uint32_t left = aLength - (p - aBuffer);
left = std::min(left, mSkipBytes);
p += left;
mSkipBytes -= left;
}
if (!mSkipBytes) {
if (mInitEndOffset < 0) {
mInitEndOffset = mCurrentOffset + (p - aBuffer);
}
mState = READ_ELEMENT_ID;
}
break;
}
}
@ -321,6 +340,7 @@ void WebMBufferedState::NotifyDataArrived(const char* aBuffer, uint32_t aLength,
while (i + 1 < mRangeParsers.Length()) {
if (mRangeParsers[i].mCurrentOffset >= mRangeParsers[i + 1].mStartOffset) {
mRangeParsers[i + 1].mStartOffset = mRangeParsers[i].mStartOffset;
mRangeParsers[i + 1].mInitEndOffset = mRangeParsers[i].mInitEndOffset;
mRangeParsers.RemoveElementAt(i);
} else {
i += 1;
@ -328,5 +348,12 @@ void WebMBufferedState::NotifyDataArrived(const char* aBuffer, uint32_t aLength,
}
}
} // namespace mozilla
int64_t WebMBufferedState::GetInitEndOffset()
{
if (mRangeParsers.IsEmpty()) {
return -1;
}
return mRangeParsers[0].mInitEndOffset;
}
} // namespace mozilla

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

@ -50,9 +50,9 @@ struct WebMTimeDataOffset
struct WebMBufferedParser
{
explicit WebMBufferedParser(int64_t aOffset)
: mStartOffset(aOffset), mCurrentOffset(aOffset), mState(READ_ELEMENT_ID),
mVIntRaw(false), mClusterSyncPos(0), mTimecodeScale(1000000),
mGotTimecodeScale(false)
: mStartOffset(aOffset), mCurrentOffset(aOffset), mInitEndOffset(-1),
mState(READ_ELEMENT_ID), mVIntRaw(false), mClusterSyncPos(0),
mTimecodeScale(1000000), mGotTimecodeScale(false)
{
if (mStartOffset != 0) {
mState = FIND_CLUSTER_SYNC;
@ -95,6 +95,10 @@ struct WebMBufferedParser
// data.
int64_t mCurrentOffset;
// Tracks element's end offset. This indicates the end of the init segment.
// Will only be set if a Segment Information has been found.
int64_t mInitEndOffset;
private:
enum State {
// Parser start state. Expects to begin at a valid EBML element. Move
@ -140,6 +144,11 @@ private:
// for this offset.
READ_BLOCK_TIMECODE,
// Will skip the current tracks element and set mInitEndOffset if an init
// segment has been found.
// Currently, only assumes it's the end of the tracks element.
CHECK_INIT_FOUND,
// Skip mSkipBytes of data before resuming parse at mNextState.
SKIP_DATA,
};
@ -232,6 +241,9 @@ public:
// dependencies to arrive at aTime.
bool GetOffsetForTime(uint64_t aTime, int64_t* aOffset);
// Returns end offset of init segment or -1 if none found.
int64_t GetInitEndOffset();
private:
// Private destructor, to discourage deletion outside of Release():
~WebMBufferedState() {

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

@ -337,7 +337,7 @@ nsresult WebMReader::ReadMetadata(MediaInfo* aInfo,
io.tell = webm_tell;
io.userdata = mDecoder;
int64_t maxOffset = mDecoder->HasInitializationData() ?
mDecoder->GetResource()->GetLength() : -1;
mBufferedState->GetInitEndOffset() : -1;
int r = nestegg_init(&mContext, io, &webm_log, maxOffset);
if (r == -1) {
return NS_ERROR_FAILURE;

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

@ -8,7 +8,7 @@ const Cc = Components.classes;
const Ci = Components.interfaces;
const Cu = Components.utils;
this.EXPORTED_SYMBOLS = [];
this.EXPORTED_SYMBOLS = ["SettingsRequestManager"];
Cu.import("resource://gre/modules/SettingsDB.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
@ -64,6 +64,9 @@ XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
XPCOMUtils.defineLazyServiceGetter(this, "uuidgen",
"@mozilla.org/uuid-generator;1",
"nsIUUIDGenerator");
XPCOMUtils.defineLazyServiceGetter(this, "gSettingsService",
"@mozilla.org/settingsService;1",
"nsISettingsService");
let SettingsPermissions = {
checkPermission: function(aPrincipal, aPerm) {
@ -1036,10 +1039,18 @@ let SettingsRequestManager = {
let mm = aMessage.target;
function returnMessage(name, data) {
try {
mm.sendAsyncMessage(name, data);
} catch (e) {
if (DEBUG) debug("Return message failed, " + name);
if (mm) {
try {
mm.sendAsyncMessage(name, data);
} catch (e) {
if (DEBUG) debug("Return message failed, " + name + ": " + e);
}
} else {
try {
gSettingsService.receiveMessage({ name: name, data: data });
} catch (e) {
if (DEBUG) debug("Direct return message failed, " + name + ": " + e);
}
}
}
@ -1193,4 +1204,5 @@ let SettingsRequestManager = {
}
};
this.SettingsRequestManager = SettingsRequestManager;
SettingsRequestManager.init();

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

@ -79,10 +79,8 @@ function SettingsServiceLock(aSettingsService, aTransactionCallback) {
windowID: undefined,
lockStack: (new Error).stack
};
cpmm.sendAsyncMessage("Settings:CreateLock",
createLockPayload,
undefined,
Services.scriptSecurityManager.getSystemPrincipal());
this.returnMessage("Settings:CreateLock", createLockPayload);
Services.tm.currentThread.dispatch(closeHelper, Ci.nsIThread.DISPATCH_NORMAL);
}
@ -91,12 +89,20 @@ SettingsServiceLock.prototype = {
return !this._open;
},
returnMessage: function(aMessage, aData) {
SettingsRequestManager.receiveMessage({
name: aMessage,
data: aData,
target: undefined,
principal: Services.scriptSecurityManager.getSystemPrincipal()
});
},
runOrFinalizeQueries: function() {
if (!this._requests || Object.keys(this._requests).length == 0) {
this._settingsService.unregisterLock(this._id);
cpmm.sendAsyncMessage("Settings:Finalize", {lockID: this._id}, undefined, Services.scriptSecurityManager.getSystemPrincipal());
this.returnMessage("Settings:Finalize", {lockID: this._id});
} else {
cpmm.sendAsyncMessage("Settings:Run", {lockID: this._id}, undefined, Services.scriptSecurityManager.getSystemPrincipal());
this.returnMessage("Settings:Run", {lockID: this._id});
}
},
@ -124,6 +130,8 @@ SettingsServiceLock.prototype = {
default:
if (DEBUG) debug("Message type " + aMessage.name + " is missing a requestID");
}
this._settingsService.unregisterLock(this._id);
return;
}
@ -138,7 +146,7 @@ SettingsServiceLock.prototype = {
this._open = true;
let settings_names = Object.keys(msg.settings);
if (settings_names.length > 0) {
let name = settings_names[0];
let name = settings_names[0];
if (DEBUG && settings_names.length > 1) {
debug("Warning: overloaded setting:" + name);
}
@ -174,11 +182,9 @@ SettingsServiceLock.prototype = {
}
let reqID = uuidgen.generateUUID().toString();
this._requests[reqID] = makeSettingsServiceRequest(aCallback, aName);
cpmm.sendAsyncMessage("Settings:Get", {requestID: reqID,
lockID: this._id,
name: aName},
undefined,
Services.scriptSecurityManager.getSystemPrincipal());
this.returnMessage("Settings:Get", {requestID: reqID,
lockID: this._id,
name: aName});
},
set: function set(aName, aValue, aCallback) {
@ -190,11 +196,9 @@ SettingsServiceLock.prototype = {
this._requests[reqID] = makeSettingsServiceRequest(aCallback, aName, aValue);
let settings = {};
settings[aName] = aValue;
cpmm.sendAsyncMessage("Settings:Set", {requestID: reqID,
lockID: this._id,
settings: settings},
undefined,
Services.scriptSecurityManager.getSystemPrincipal());
this.returnMessage("Settings:Set", {requestID: reqID,
lockID: this._id,
settings: settings});
},
callHandle: function callHandle(aCallback, aName, aValue) {
@ -239,6 +243,7 @@ function SettingsService()
{
if (VERBOSE) debug("settingsService Constructor");
this._locks = [];
this._serviceLocks = {};
this._createdLocks = 0;
this._unregisteredLocks = 0;
this.init();
@ -263,14 +268,36 @@ SettingsService.prototype = {
}
},
receiveMessage: function(aMessage) {
if (VERBOSE) debug("Entering receiveMessage");
let lockID = aMessage.data.lockID;
if (!lockID) {
if (DEBUG) debug("No lock ID");
return;
}
if (!(lockID in this._serviceLocks)) {
if (DEBUG) debug("Received message for lock " + lockID + " but no lock");
return;
}
if (VERBOSE) debug("Delivering message");
this._serviceLocks[lockID].receiveMessage(aMessage);
},
createLock: function createLock(aCallback) {
if (VERBOSE) debug("Calling createLock");
var lock = new SettingsServiceLock(this, aCallback);
this.registerLock(lock._id);
if (VERBOSE) debug("Created lock " + lock._id);
this.registerLock(lock);
return lock;
},
registerLock: function(aLockID) {
this._locks.push(aLockID);
registerLock: function(aLock) {
if (VERBOSE) debug("Registering lock " + aLock._id);
this._locks.push(aLock._id);
this._serviceLocks[aLock._id] = aLock;
this._createdLocks++;
},
@ -279,6 +306,8 @@ SettingsService.prototype = {
if (lock_index != -1) {
if (VERBOSE) debug("Unregistering lock " + aLockID);
this._locks.splice(lock_index, 1);
this._serviceLocks[aLockID] = null;
delete this._serviceLocks[aLockID];
this._unregisteredLocks++;
}
},

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

@ -10,9 +10,11 @@
#include "nsAppRunner.h"
#include "Composer2D.h"
#include "Effects.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/Preferences.h"
#include "mozilla/Endian.h"
#include "mozilla/MathAlgorithms.h"
#include "mozilla/Preferences.h"
#include "mozilla/TimeStamp.h"
#include "TexturePoolOGL.h"
#include "mozilla/layers/CompositorOGL.h"
#include "mozilla/layers/CompositorParent.h"
@ -66,7 +68,8 @@ using namespace layerscope;
class DebugDataSender;
class DebugGLData;
/* This class handle websocket protocol which included
/*
* This class handle websocket protocol which included
* handshake and data frame's header
*/
class LayerScopeWebSocketHandler : public nsIInputStreamCallback {
@ -81,203 +84,48 @@ public:
LayerScopeWebSocketHandler()
: mState(NoHandshake)
, mConnected(false)
{ }
private:
virtual ~LayerScopeWebSocketHandler()
{
if (mTransport) {
mTransport->Close(NS_OK);
}
}
void OpenStream(nsISocketTransport* aTransport);
public:
void OpenStream(nsISocketTransport* aTransport) {
MOZ_ASSERT(aTransport);
mTransport = aTransport;
mTransport->OpenOutputStream(nsITransport::OPEN_BLOCKING,
0,
0,
getter_AddRefs(mOutputStream));
nsCOMPtr<nsIInputStream> debugInputStream;
mTransport->OpenInputStream(0,
0,
0,
getter_AddRefs(debugInputStream));
mInputStream = do_QueryInterface(debugInputStream);
mInputStream->AsyncWait(this, 0, 0, NS_GetCurrentThread());
}
bool WriteToStream(void *ptr, uint32_t size) {
if (mState == NoHandshake) {
// Not yet handshake, just return true in case of
// LayerScope remove this handle
return true;
} else if (mState == HandshakeFailed) {
return false;
}
// Generate WebSocket header
uint8_t wsHeader[10];
int wsHeaderSize = 0;
const uint8_t opcode = 0x2;
wsHeader[0] = 0x80 | (opcode & 0x0f); // FIN + opcode;
if (size <= 125) {
wsHeaderSize = 2;
wsHeader[1] = size;
} else if (size < 65536) {
wsHeaderSize = 4;
wsHeader[1] = 0x7E;
NetworkEndian::writeUint16(wsHeader + 2, size);
} else {
wsHeaderSize = 10;
wsHeader[1] = 0x7F;
NetworkEndian::writeUint64(wsHeader + 2, size);
}
// Send WebSocket header
nsresult rv;
uint32_t cnt;
rv = mOutputStream->Write(reinterpret_cast<char*>(wsHeader),
wsHeaderSize, &cnt);
if (NS_FAILED(rv))
return false;
uint32_t written = 0;
while (written < size) {
uint32_t cnt;
rv = mOutputStream->Write(reinterpret_cast<char*>(ptr) + written,
size - written, &cnt);
if (NS_FAILED(rv))
return false;
written += cnt;
}
return true;
}
bool WriteToStream(void *aPtr, uint32_t aSize);
// nsIInputStreamCallback
NS_IMETHODIMP OnInputStreamReady(nsIAsyncInputStream *stream) MOZ_OVERRIDE
{
nsTArray<nsCString> protocolString;
ReadInputStreamData(protocolString);
NS_IMETHODIMP OnInputStreamReady(nsIAsyncInputStream *aStream) MOZ_OVERRIDE;
if (WebSocketHandshake(protocolString)) {
mState = HandshakeSuccess;
} else {
mState = HandshakeFailed;
}
return NS_OK;
}
private:
void ReadInputStreamData(nsTArray<nsCString>& aProtocolString)
{
nsLineBuffer<char> lineBuffer;
nsCString line;
bool more = true;
do {
NS_ReadLine(mInputStream.get(), &lineBuffer, line, &more);
virtual ~LayerScopeWebSocketHandler() { CloseConnection(); }
if (line.Length() > 0) {
aProtocolString.AppendElement(line);
}
} while (more && line.Length() > 0);
}
void ReadInputStreamData(nsTArray<nsCString>& aProtocolString);
bool WebSocketHandshake(nsTArray<nsCString>& aProtocolString)
{
nsresult rv;
bool isWebSocket = false;
nsCString version;
nsCString wsKey;
nsCString protocol;
bool WebSocketHandshake(nsTArray<nsCString>& aProtocolString);
// Validate WebSocket client request.
if (aProtocolString.Length() == 0)
return false;
nsresult HandleSocketMessage(nsIAsyncInputStream *aStream);
// Check that the HTTP method is GET
const char* HTTP_METHOD = "GET ";
if (strncmp(aProtocolString[0].get(), HTTP_METHOD, strlen(HTTP_METHOD)) != 0) {
return false;
}
nsresult ProcessInput(uint8_t *aBuffer, uint32_t aCount);
for (uint32_t i = 1; i < aProtocolString.Length(); ++i) {
const char* line = aProtocolString[i].get();
const char* prop_pos = strchr(line, ':');
if (prop_pos != nullptr) {
nsCString key(line, prop_pos - line);
nsCString value(prop_pos + 2);
if (key.EqualsIgnoreCase("upgrade") &&
value.EqualsIgnoreCase("websocket")) {
isWebSocket = true;
} else if (key.EqualsIgnoreCase("sec-websocket-version")) {
version = value;
} else if (key.EqualsIgnoreCase("sec-websocket-key")) {
wsKey = value;
} else if (key.EqualsIgnoreCase("sec-websocket-protocol")) {
protocol = value;
}
}
}
// Copied from WebsocketChannel, helper function to decode data frame
void ApplyMask(uint32_t aMask, uint8_t *aData, uint64_t aLen);
if (!isWebSocket) {
return false;
}
bool HandleDataFrame(uint8_t *aData, uint32_t aSize);
if (!(version.EqualsLiteral("7") ||
version.EqualsLiteral("8") ||
version.EqualsLiteral("13"))) {
return false;
}
if (!(protocol.EqualsIgnoreCase("binary"))) {
return false;
}
// Client request is valid. Start to generate and send server response.
nsAutoCString guid("258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
nsAutoCString res;
SHA1Sum sha1;
nsCString combined(wsKey + guid);
sha1.update(combined.get(), combined.Length());
uint8_t digest[SHA1Sum::kHashSize]; // SHA1 digests are 20 bytes long.
sha1.finish(digest);
nsCString newString(reinterpret_cast<char*>(digest), SHA1Sum::kHashSize);
Base64Encode(newString, res);
nsCString response("HTTP/1.1 101 Switching Protocols\r\n");
response.AppendLiteral("Upgrade: websocket\r\n");
response.AppendLiteral("Connection: Upgrade\r\n");
response.Append(nsCString("Sec-WebSocket-Accept: ") + res + nsCString("\r\n"));
response.AppendLiteral("Sec-WebSocket-Protocol: binary\r\n\r\n");
uint32_t written = 0;
uint32_t size = response.Length();
while (written < size) {
uint32_t cnt;
rv = mOutputStream->Write(const_cast<char*>(response.get()) + written,
size - written, &cnt);
if (NS_FAILED(rv))
return false;
written += cnt;
}
mOutputStream->Flush();
return true;
}
void CloseConnection();
private:
nsCOMPtr<nsIOutputStream> mOutputStream;
nsCOMPtr<nsIAsyncInputStream> mInputStream;
nsCOMPtr<nsISocketTransport> mTransport;
SocketStateType mState;
bool mConnected;
};
NS_IMPL_ISUPPORTS(LayerScopeWebSocketHandler, nsIInputStreamCallback);
/*
* Manage Websocket connections
*/
class LayerScopeWebSocketManager {
public:
LayerScopeWebSocketManager();
@ -720,6 +568,12 @@ public:
int aWidth = 0,
int aHeight = 0);
static void SetLayersTreeSendable(bool aSet) {sLayersTreeSendable = aSet;}
static void SetLayersBufferSendable(bool aSet) {sLayersBufferSendable = aSet;}
static bool GetLayersTreeSendable() {return sLayersTreeSendable;}
// Sender private functions
private:
static void SendColor(void* aLayerRef,
@ -736,8 +590,16 @@ private:
static void SendYCbCrEffect(GLContext* aGLContext,
void* aLayerRef,
const EffectYCbCr* aEffect);
// Data fields
private:
static bool sLayersTreeSendable;
static bool sLayersBufferSendable;
};
bool SenderHelper::sLayersTreeSendable = true;
bool SenderHelper::sLayersBufferSendable = true;
// ----------------------------------------------
// SenderHelper implementation
@ -874,6 +736,8 @@ SenderHelper::SendEffectChain(GLContext* aGLContext,
int aWidth,
int aHeight)
{
if (!sLayersBufferSendable) return;
const Effect* primaryEffect = aEffectChain.mPrimaryEffect;
switch (primaryEffect->mType) {
case EffectTypes::RGB: {
@ -908,6 +772,430 @@ SenderHelper::SendEffectChain(GLContext* aGLContext,
// TODO:
}
// ----------------------------------------------
// LayerScopeWebSocketHandler implementation
// ----------------------------------------------
void
LayerScopeWebSocketHandler::OpenStream(nsISocketTransport* aTransport)
{
MOZ_ASSERT(aTransport);
mTransport = aTransport;
mTransport->OpenOutputStream(nsITransport::OPEN_BLOCKING,
0,
0,
getter_AddRefs(mOutputStream));
nsCOMPtr<nsIInputStream> debugInputStream;
mTransport->OpenInputStream(0,
0,
0,
getter_AddRefs(debugInputStream));
mInputStream = do_QueryInterface(debugInputStream);
mInputStream->AsyncWait(this, 0, 0, NS_GetCurrentThread());
}
bool
LayerScopeWebSocketHandler::WriteToStream(void *aPtr,
uint32_t aSize)
{
if (mState == NoHandshake) {
// Not yet handshake, just return true in case of
// LayerScope remove this handle
return true;
} else if (mState == HandshakeFailed) {
return false;
}
if (!mOutputStream) {
return false;
}
// Generate WebSocket header
uint8_t wsHeader[10];
int wsHeaderSize = 0;
const uint8_t opcode = 0x2;
wsHeader[0] = 0x80 | (opcode & 0x0f); // FIN + opcode;
if (aSize <= 125) {
wsHeaderSize = 2;
wsHeader[1] = aSize;
} else if (aSize < 65536) {
wsHeaderSize = 4;
wsHeader[1] = 0x7E;
NetworkEndian::writeUint16(wsHeader + 2, aSize);
} else {
wsHeaderSize = 10;
wsHeader[1] = 0x7F;
NetworkEndian::writeUint64(wsHeader + 2, aSize);
}
// Send WebSocket header
nsresult rv;
uint32_t cnt;
rv = mOutputStream->Write(reinterpret_cast<char*>(wsHeader),
wsHeaderSize, &cnt);
if (NS_FAILED(rv))
return false;
uint32_t written = 0;
while (written < aSize) {
uint32_t cnt;
rv = mOutputStream->Write(reinterpret_cast<char*>(aPtr) + written,
aSize - written, &cnt);
if (NS_FAILED(rv))
return false;
written += cnt;
}
return true;
}
NS_IMETHODIMP
LayerScopeWebSocketHandler::OnInputStreamReady(nsIAsyncInputStream *aStream)
{
MOZ_ASSERT(mInputStream);
if (!mInputStream) {
return NS_OK;
}
if (!mConnected) {
nsTArray<nsCString> protocolString;
ReadInputStreamData(protocolString);
if (WebSocketHandshake(protocolString)) {
mState = HandshakeSuccess;
mConnected = true;
mInputStream->AsyncWait(this, 0, 0, NS_GetCurrentThread());
} else {
mState = HandshakeFailed;
}
return NS_OK;
} else {
return HandleSocketMessage(aStream);
}
}
void
LayerScopeWebSocketHandler::ReadInputStreamData(nsTArray<nsCString>& aProtocolString)
{
nsLineBuffer<char> lineBuffer;
nsCString line;
bool more = true;
do {
NS_ReadLine(mInputStream.get(), &lineBuffer, line, &more);
if (line.Length() > 0) {
aProtocolString.AppendElement(line);
}
} while (more && line.Length() > 0);
}
bool
LayerScopeWebSocketHandler::WebSocketHandshake(nsTArray<nsCString>& aProtocolString)
{
nsresult rv;
bool isWebSocket = false;
nsCString version;
nsCString wsKey;
nsCString protocol;
// Validate WebSocket client request.
if (aProtocolString.Length() == 0)
return false;
// Check that the HTTP method is GET
const char* HTTP_METHOD = "GET ";
if (strncmp(aProtocolString[0].get(), HTTP_METHOD, strlen(HTTP_METHOD)) != 0) {
return false;
}
for (uint32_t i = 1; i < aProtocolString.Length(); ++i) {
const char* line = aProtocolString[i].get();
const char* prop_pos = strchr(line, ':');
if (prop_pos != nullptr) {
nsCString key(line, prop_pos - line);
nsCString value(prop_pos + 2);
if (key.EqualsIgnoreCase("upgrade") &&
value.EqualsIgnoreCase("websocket")) {
isWebSocket = true;
} else if (key.EqualsIgnoreCase("sec-websocket-version")) {
version = value;
} else if (key.EqualsIgnoreCase("sec-websocket-key")) {
wsKey = value;
} else if (key.EqualsIgnoreCase("sec-websocket-protocol")) {
protocol = value;
}
}
}
if (!isWebSocket) {
return false;
}
if (!(version.EqualsLiteral("7") ||
version.EqualsLiteral("8") ||
version.EqualsLiteral("13"))) {
return false;
}
if (!(protocol.EqualsIgnoreCase("binary"))) {
return false;
}
if (!mOutputStream) {
return false;
}
// Client request is valid. Start to generate and send server response.
nsAutoCString guid("258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
nsAutoCString res;
SHA1Sum sha1;
nsCString combined(wsKey + guid);
sha1.update(combined.get(), combined.Length());
uint8_t digest[SHA1Sum::kHashSize]; // SHA1 digests are 20 bytes long.
sha1.finish(digest);
nsCString newString(reinterpret_cast<char*>(digest), SHA1Sum::kHashSize);
Base64Encode(newString, res);
nsCString response("HTTP/1.1 101 Switching Protocols\r\n");
response.AppendLiteral("Upgrade: websocket\r\n");
response.AppendLiteral("Connection: Upgrade\r\n");
response.Append(nsCString("Sec-WebSocket-Accept: ") + res + nsCString("\r\n"));
response.AppendLiteral("Sec-WebSocket-Protocol: binary\r\n\r\n");
uint32_t written = 0;
uint32_t size = response.Length();
while (written < size) {
uint32_t cnt;
rv = mOutputStream->Write(const_cast<char*>(response.get()) + written,
size - written, &cnt);
if (NS_FAILED(rv))
return false;
written += cnt;
}
mOutputStream->Flush();
return true;
}
nsresult
LayerScopeWebSocketHandler::HandleSocketMessage(nsIAsyncInputStream *aStream)
{
// The reading and parsing of this input stream is customized for layer viewer.
const uint32_t cPacketSize = 1024;
char buffer[cPacketSize];
uint32_t count = 0;
nsresult rv = NS_OK;
do {
rv = mInputStream->Read((char *)buffer, cPacketSize, &count);
// TODO: combine packets if we have to read more than once
if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
mInputStream->AsyncWait(this, 0, 0, NS_GetCurrentThread());
return NS_OK;
}
if (NS_FAILED(rv)) {
break;
}
if (count == 0) {
// NS_BASE_STREAM_CLOSED
CloseConnection();
break;
}
rv = ProcessInput(reinterpret_cast<uint8_t *>(buffer), count);
} while (NS_SUCCEEDED(rv) && mInputStream);
return rv;
}
nsresult
LayerScopeWebSocketHandler::ProcessInput(uint8_t *aBuffer,
uint32_t aCount)
{
uint32_t avail = aCount;
// Decode Websocket data frame
if (avail <= 2) {
NS_WARNING("Packet size is less than 2 bytes");
return NS_OK;
}
// First byte, data type, only care the opcode
// rsvBits: aBuffer[0] & 0x70 (0111 0000)
uint8_t finBit = aBuffer[0] & 0x80; // 1000 0000
uint8_t opcode = aBuffer[0] & 0x0F; // 0000 1111
if (!finBit) {
NS_WARNING("We cannot handle multi-fragments messages in Layerscope websocket parser.");
return NS_OK;
}
// Second byte, data length
uint8_t maskBit = aBuffer[1] & 0x80; // 1000 0000
int64_t payloadLength64 = aBuffer[1] & 0x7F; // 0111 1111
if (!maskBit) {
NS_WARNING("Client to Server should set the mask bit");
return NS_OK;
}
uint32_t framingLength = 2 + 4; // 4 for masks
if (payloadLength64 < 126) {
if (avail < framingLength)
return NS_OK;
} else if (payloadLength64 == 126) {
// 16 bit length field
framingLength += 2;
if (avail < framingLength) {
return NS_OK;
}
payloadLength64 = aBuffer[2] << 8 | aBuffer[3];
} else {
// 64 bit length
framingLength += 8;
if (avail < framingLength) {
return NS_OK;
}
if (aBuffer[2] & 0x80) {
// Section 4.2 says that the most significant bit MUST be
// 0. (i.e. this is really a 63 bit value)
NS_WARNING("High bit of 64 bit length set");
return NS_ERROR_ILLEGAL_VALUE;
}
// copy this in case it is unaligned
payloadLength64 = NetworkEndian::readInt64(aBuffer + 2);
}
uint8_t *payload = aBuffer + framingLength;
avail -= framingLength;
uint32_t payloadLength = static_cast<uint32_t>(payloadLength64);
if (avail < payloadLength) {
NS_WARNING("Packet size mismatch the payload length");
return NS_OK;
}
// Apply mask
uint32_t mask = NetworkEndian::readUint32(payload - 4);
ApplyMask(mask, payload, payloadLength);
if (opcode == 0x8) {
// opcode == 0x8 means connection close
CloseConnection();
return NS_BASE_STREAM_CLOSED;
}
if (!HandleDataFrame(payload, payloadLength)) {
NS_WARNING("Cannot decode payload data by the protocol buffer");
}
return NS_OK;
}
void
LayerScopeWebSocketHandler::ApplyMask(uint32_t aMask,
uint8_t *aData,
uint64_t aLen)
{
if (!aData || aLen == 0) {
return;
}
// Optimally we want to apply the mask 32 bits at a time,
// but the buffer might not be alligned. So we first deal with
// 0 to 3 bytes of preamble individually
while (aLen && (reinterpret_cast<uintptr_t>(aData) & 3)) {
*aData ^= aMask >> 24;
aMask = RotateLeft(aMask, 8);
aData++;
aLen--;
}
// perform mask on full words of data
uint32_t *iData = reinterpret_cast<uint32_t *>(aData);
uint32_t *end = iData + (aLen >> 2);
NetworkEndian::writeUint32(&aMask, aMask);
for (; iData < end; iData++) {
*iData ^= aMask;
}
aMask = NetworkEndian::readUint32(&aMask);
aData = (uint8_t *)iData;
aLen = aLen % 4;
// There maybe up to 3 trailing bytes that need to be dealt with
// individually
while (aLen) {
*aData ^= aMask >> 24;
aMask = RotateLeft(aMask, 8);
aData++;
aLen--;
}
}
bool
LayerScopeWebSocketHandler::HandleDataFrame(uint8_t *aData,
uint32_t aSize)
{
// Handle payload data by protocol buffer
auto p = MakeUnique<CommandPacket>();
p->ParseFromArray(static_cast<void*>(aData), aSize);
if (!p->has_type()) {
MOZ_ASSERT(false, "Protocol buffer decoding failed or cannot recongize it");
return false;
}
switch (p->type()) {
case CommandPacket::LAYERS_TREE:
if (p->has_value()) {
SenderHelper::SetLayersTreeSendable(p->value());
}
break;
case CommandPacket::LAYERS_BUFFER:
if (p->has_value()) {
SenderHelper::SetLayersBufferSendable(p->value());
}
break;
case CommandPacket::NO_OP:
default:
NS_WARNING("Invalid message type");
break;
}
return true;
}
void
LayerScopeWebSocketHandler::CloseConnection()
{
WebSocketHelper::GetSocketManager()->CleanDebugData();
if (mInputStream) {
mInputStream->AsyncWait(nullptr, 0, 0, nullptr);
mInputStream = nullptr;
}
if (mOutputStream) {
mOutputStream = nullptr;
}
if (mTransport) {
mTransport->Close(NS_BASE_STREAM_CLOSED);
mTransport = nullptr;
}
mConnected = false;
}
// ----------------------------------------------
// LayerScopeWebSocketManager implementation
// ----------------------------------------------
@ -923,6 +1211,7 @@ LayerScopeWebSocketManager::LayerScopeWebSocketManager()
LayerScopeWebSocketManager::~LayerScopeWebSocketManager()
{
mServerSocket->Close();
}
void
@ -1005,7 +1294,7 @@ void
LayerScope::SendLayerDump(UniquePtr<Packet> aPacket)
{
// Protect this public function
if (!CheckSendable()) {
if (!CheckSendable() || !SenderHelper::GetLayersTreeSendable()) {
return;
}
WebSocketHelper::GetSocketManager()->AppendDebugData(

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

@ -546,15 +546,15 @@ TiledLayerBuffer<Derived, Tile>::Update(const nsIntRegion& aNewValidRegion,
x += width;
}
mRetainedTiles = newRetainedTiles;
AsDerived().PostValidate(aPaintRegion);
for (unsigned int i = 0; i < newRetainedTiles.Length(); ++i) {
AsDerived().UnlockTile(newRetainedTiles[i]);
for (unsigned int i = 0; i < mRetainedTiles.Length(); ++i) {
AsDerived().UnlockTile(mRetainedTiles[i]);
}
// At this point, oldTileCount should be zero
NS_ABORT_IF_FALSE(oldTileCount == 0, "Failed to release old tiles");
mRetainedTiles = newRetainedTiles;
mValidRegion = aNewValidRegion;
mPaintedRegion.Or(mPaintedRegion, aPaintRegion);
}

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

@ -333,6 +333,9 @@ static TemporaryRef<gl::ShSurfHandle>
CloneSurface(gl::SharedSurface* src, gl::SurfaceFactory* factory)
{
RefPtr<gl::ShSurfHandle> dest = factory->NewShSurfHandle(src->mSize);
if (!dest) {
return nullptr;
}
SharedSurface::ProdCopy(src, dest->Surf(), factory);
return dest.forget();
}

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

@ -517,6 +517,7 @@ TileClient::TileClient(const TileClient& o)
mManager = o.mManager;
mInvalidFront = o.mInvalidFront;
mInvalidBack = o.mInvalidBack;
mOrigin = o.mOrigin;
}
TileClient&
@ -536,6 +537,7 @@ TileClient::operator=(const TileClient& o)
mManager = o.mManager;
mInvalidFront = o.mInvalidFront;
mInvalidBack = o.mInvalidBack;
mOrigin = o.mOrigin;
return *this;
}
@ -1056,6 +1058,31 @@ ClientTiledLayerBuffer::PostValidate(const nsIntRegion& aPaintRegion)
mTilingOrigin = IntPoint(std::numeric_limits<int32_t>::max(),
std::numeric_limits<int32_t>::max());
}
for (size_t i = 0; i < mRetainedTiles.Length(); ++i) {
TileClient& tile = mRetainedTiles[i];
if (tile.mFrontBuffer && tile.mFrontBuffer->IsLocked()) {
// Only worry about padding when not doing low-res because it simplifies
// the math and the artifacts won't be noticable
// Edge padding prevents sampling artifacts when compositing.
if (mResolution == 1) {
nsIntRect unscaledTile = nsIntRect(tile.mOrigin.x, tile.mOrigin.y,
GetTileSize().width, GetTileSize().height);
nsIntRegion tileValidRegion = GetValidRegion();
tileValidRegion.OrWith(aPaintRegion);
// We only need to pad out if the tile has area that's not valid
if (!tileValidRegion.Contains(unscaledTile)) {
tileValidRegion = tileValidRegion.Intersect(unscaledTile);
// translate the region into tile space and pad
tileValidRegion.MoveBy(-nsIntPoint(unscaledTile.x, unscaledTile.y));
RefPtr<DrawTarget> drawTarget = tile.mFrontBuffer->BorrowDrawTarget();
PadDrawTargetOutFromRegion(drawTarget, tileValidRegion);
}
}
}
}
}
void
@ -1144,6 +1171,8 @@ ClientTiledLayerBuffer::ValidateTile(TileClient aTile,
}
}
aTile.mOrigin = gfx::ToIntPoint(aTileOrigin);
if (usingTiledDrawTarget) {
if (createdTextureClient) {
if (!mCompositableClient->AddTextureClient(backBuffer)) {
@ -1247,26 +1276,6 @@ ClientTiledLayerBuffer::ValidateTile(TileClient aTile,
aTile.mInvalidFront.Or(aTile.mInvalidFront, nsIntRect(copyTarget.x, copyTarget.y, copyRect.width, copyRect.height));
}
// only worry about padding when not doing low-res
// because it simplifies the math and the artifacts
// won't be noticable
if (mResolution == 1) {
nsIntRect unscaledTile = nsIntRect(aTileOrigin.x,
aTileOrigin.y,
GetTileSize().width,
GetTileSize().height);
nsIntRegion tileValidRegion = GetValidRegion();
tileValidRegion.Or(tileValidRegion, aDirtyRegion);
// We only need to pad out if the tile has area that's not valid
if (!tileValidRegion.Contains(unscaledTile)) {
tileValidRegion = tileValidRegion.Intersect(unscaledTile);
// translate the region into tile space and pad
tileValidRegion.MoveBy(-nsIntPoint(unscaledTile.x, unscaledTile.y));
PadDrawTargetOutFromRegion(drawTarget, tileValidRegion);
}
}
// The new buffer is now validated, remove the dirty region from it.
aTile.mInvalidBack.SubOut(offsetScaledDirtyRegion);

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

@ -274,6 +274,8 @@ struct TileClient
nsIntRegion mInvalidBack;
nsExpirationState mExpirationState;
gfx::IntPoint mOrigin;
private:
// Copies dirty pixels from the front buffer into the back buffer,
// and records the copied region in aAddPaintedRegion.

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

@ -27,6 +27,7 @@ void protobuf_ShutdownFile_LayerScopePacket_2eproto() {
delete LayersPacket_Layer_Shadow::default_instance_;
delete MetaPacket::default_instance_;
delete Packet::default_instance_;
delete CommandPacket::default_instance_;
}
void protobuf_AddDesc_LayerScopePacket_2eproto() {
@ -47,6 +48,7 @@ void protobuf_AddDesc_LayerScopePacket_2eproto() {
LayersPacket_Layer_Shadow::default_instance_ = new LayersPacket_Layer_Shadow();
MetaPacket::default_instance_ = new MetaPacket();
Packet::default_instance_ = new Packet();
CommandPacket::default_instance_ = new CommandPacket();
FramePacket::default_instance_->InitAsDefaultInstance();
ColorPacket::default_instance_->InitAsDefaultInstance();
TexturePacket::default_instance_->InitAsDefaultInstance();
@ -59,6 +61,7 @@ void protobuf_AddDesc_LayerScopePacket_2eproto() {
LayersPacket_Layer_Shadow::default_instance_->InitAsDefaultInstance();
MetaPacket::default_instance_->InitAsDefaultInstance();
Packet::default_instance_->InitAsDefaultInstance();
CommandPacket::default_instance_->InitAsDefaultInstance();
::google::protobuf::internal::OnShutdown(&protobuf_ShutdownFile_LayerScopePacket_2eproto);
}
@ -3573,6 +3576,220 @@ void Packet::Swap(Packet* other) {
}
// ===================================================================
bool CommandPacket_CmdType_IsValid(int value) {
switch(value) {
case 0:
case 1:
case 2:
return true;
default:
return false;
}
}
#ifndef _MSC_VER
const CommandPacket_CmdType CommandPacket::NO_OP;
const CommandPacket_CmdType CommandPacket::LAYERS_TREE;
const CommandPacket_CmdType CommandPacket::LAYERS_BUFFER;
const CommandPacket_CmdType CommandPacket::CmdType_MIN;
const CommandPacket_CmdType CommandPacket::CmdType_MAX;
const int CommandPacket::CmdType_ARRAYSIZE;
#endif // _MSC_VER
#ifndef _MSC_VER
const int CommandPacket::kTypeFieldNumber;
const int CommandPacket::kValueFieldNumber;
#endif // !_MSC_VER
CommandPacket::CommandPacket()
: ::google::protobuf::MessageLite() {
SharedCtor();
}
void CommandPacket::InitAsDefaultInstance() {
}
CommandPacket::CommandPacket(const CommandPacket& from)
: ::google::protobuf::MessageLite() {
SharedCtor();
MergeFrom(from);
}
void CommandPacket::SharedCtor() {
_cached_size_ = 0;
type_ = 0;
value_ = false;
::memset(_has_bits_, 0, sizeof(_has_bits_));
}
CommandPacket::~CommandPacket() {
SharedDtor();
}
void CommandPacket::SharedDtor() {
if (this != default_instance_) {
}
}
void CommandPacket::SetCachedSize(int size) const {
GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
_cached_size_ = size;
GOOGLE_SAFE_CONCURRENT_WRITES_END();
}
const CommandPacket& CommandPacket::default_instance() {
if (default_instance_ == NULL) protobuf_AddDesc_LayerScopePacket_2eproto(); return *default_instance_;
}
CommandPacket* CommandPacket::default_instance_ = NULL;
CommandPacket* CommandPacket::New() const {
return new CommandPacket;
}
void CommandPacket::Clear() {
if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) {
type_ = 0;
value_ = false;
}
::memset(_has_bits_, 0, sizeof(_has_bits_));
}
bool CommandPacket::MergePartialFromCodedStream(
::google::protobuf::io::CodedInputStream* input) {
#define DO_(EXPRESSION) if (!(EXPRESSION)) return false
::google::protobuf::uint32 tag;
while ((tag = input->ReadTag()) != 0) {
switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) {
// required .mozilla.layers.layerscope.CommandPacket.CmdType type = 1;
case 1: {
if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) {
int value;
DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
int, ::google::protobuf::internal::WireFormatLite::TYPE_ENUM>(
input, &value)));
if (::mozilla::layers::layerscope::CommandPacket_CmdType_IsValid(value)) {
set_type(static_cast< ::mozilla::layers::layerscope::CommandPacket_CmdType >(value));
}
} else {
goto handle_uninterpreted;
}
if (input->ExpectTag(16)) goto parse_value;
break;
}
// optional bool value = 2;
case 2: {
if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) {
parse_value:
DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
bool, ::google::protobuf::internal::WireFormatLite::TYPE_BOOL>(
input, &value_)));
set_has_value();
} else {
goto handle_uninterpreted;
}
if (input->ExpectAtEnd()) return true;
break;
}
default: {
handle_uninterpreted:
if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) {
return true;
}
DO_(::google::protobuf::internal::WireFormatLite::SkipField(input, tag));
break;
}
}
}
return true;
#undef DO_
}
void CommandPacket::SerializeWithCachedSizes(
::google::protobuf::io::CodedOutputStream* output) const {
// required .mozilla.layers.layerscope.CommandPacket.CmdType type = 1;
if (has_type()) {
::google::protobuf::internal::WireFormatLite::WriteEnum(
1, this->type(), output);
}
// optional bool value = 2;
if (has_value()) {
::google::protobuf::internal::WireFormatLite::WriteBool(2, this->value(), output);
}
}
int CommandPacket::ByteSize() const {
int total_size = 0;
if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) {
// required .mozilla.layers.layerscope.CommandPacket.CmdType type = 1;
if (has_type()) {
total_size += 1 +
::google::protobuf::internal::WireFormatLite::EnumSize(this->type());
}
// optional bool value = 2;
if (has_value()) {
total_size += 1 + 1;
}
}
GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
_cached_size_ = total_size;
GOOGLE_SAFE_CONCURRENT_WRITES_END();
return total_size;
}
void CommandPacket::CheckTypeAndMergeFrom(
const ::google::protobuf::MessageLite& from) {
MergeFrom(*::google::protobuf::down_cast<const CommandPacket*>(&from));
}
void CommandPacket::MergeFrom(const CommandPacket& from) {
GOOGLE_CHECK_NE(&from, this);
if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) {
if (from.has_type()) {
set_type(from.type());
}
if (from.has_value()) {
set_value(from.value());
}
}
}
void CommandPacket::CopyFrom(const CommandPacket& from) {
if (&from == this) return;
Clear();
MergeFrom(from);
}
bool CommandPacket::IsInitialized() const {
if ((_has_bits_[0] & 0x00000001) != 0x00000001) return false;
return true;
}
void CommandPacket::Swap(CommandPacket* other) {
if (other != this) {
std::swap(type_, other->type_);
std::swap(value_, other->value_);
std::swap(_has_bits_[0], other->_has_bits_[0]);
std::swap(_cached_size_, other->_cached_size_);
}
}
::std::string CommandPacket::GetTypeName() const {
return "mozilla.layers.layerscope.CommandPacket";
}
// @@protoc_insertion_point(namespace_scope)
} // namespace layerscope

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

@ -45,6 +45,7 @@ class LayersPacket_Layer_Matrix;
class LayersPacket_Layer_Shadow;
class MetaPacket;
class Packet;
class CommandPacket;
enum LayersPacket_Layer_LayerType {
LayersPacket_Layer_LayerType_UnknownLayer = 0,
@ -98,6 +99,16 @@ const Packet_DataType Packet_DataType_DataType_MIN = Packet_DataType_FRAMESTART;
const Packet_DataType Packet_DataType_DataType_MAX = Packet_DataType_META;
const int Packet_DataType_DataType_ARRAYSIZE = Packet_DataType_DataType_MAX + 1;
enum CommandPacket_CmdType {
CommandPacket_CmdType_NO_OP = 0,
CommandPacket_CmdType_LAYERS_TREE = 1,
CommandPacket_CmdType_LAYERS_BUFFER = 2
};
bool CommandPacket_CmdType_IsValid(int value);
const CommandPacket_CmdType CommandPacket_CmdType_CmdType_MIN = CommandPacket_CmdType_NO_OP;
const CommandPacket_CmdType CommandPacket_CmdType_CmdType_MAX = CommandPacket_CmdType_LAYERS_BUFFER;
const int CommandPacket_CmdType_CmdType_ARRAYSIZE = CommandPacket_CmdType_CmdType_MAX + 1;
// ===================================================================
class FramePacket : public ::google::protobuf::MessageLite {
@ -1439,6 +1450,99 @@ class Packet : public ::google::protobuf::MessageLite {
void InitAsDefaultInstance();
static Packet* default_instance_;
};
// -------------------------------------------------------------------
class CommandPacket : public ::google::protobuf::MessageLite {
public:
CommandPacket();
virtual ~CommandPacket();
CommandPacket(const CommandPacket& from);
inline CommandPacket& operator=(const CommandPacket& from) {
CopyFrom(from);
return *this;
}
static const CommandPacket& default_instance();
void Swap(CommandPacket* other);
// implements Message ----------------------------------------------
CommandPacket* New() const;
void CheckTypeAndMergeFrom(const ::google::protobuf::MessageLite& from);
void CopyFrom(const CommandPacket& from);
void MergeFrom(const CommandPacket& from);
void Clear();
bool IsInitialized() const;
int ByteSize() const;
bool MergePartialFromCodedStream(
::google::protobuf::io::CodedInputStream* input);
void SerializeWithCachedSizes(
::google::protobuf::io::CodedOutputStream* output) const;
int GetCachedSize() const { return _cached_size_; }
private:
void SharedCtor();
void SharedDtor();
void SetCachedSize(int size) const;
public:
::std::string GetTypeName() const;
// nested types ----------------------------------------------------
typedef CommandPacket_CmdType CmdType;
static const CmdType NO_OP = CommandPacket_CmdType_NO_OP;
static const CmdType LAYERS_TREE = CommandPacket_CmdType_LAYERS_TREE;
static const CmdType LAYERS_BUFFER = CommandPacket_CmdType_LAYERS_BUFFER;
static inline bool CmdType_IsValid(int value) {
return CommandPacket_CmdType_IsValid(value);
}
static const CmdType CmdType_MIN =
CommandPacket_CmdType_CmdType_MIN;
static const CmdType CmdType_MAX =
CommandPacket_CmdType_CmdType_MAX;
static const int CmdType_ARRAYSIZE =
CommandPacket_CmdType_CmdType_ARRAYSIZE;
// accessors -------------------------------------------------------
// required .mozilla.layers.layerscope.CommandPacket.CmdType type = 1;
inline bool has_type() const;
inline void clear_type();
static const int kTypeFieldNumber = 1;
inline ::mozilla::layers::layerscope::CommandPacket_CmdType type() const;
inline void set_type(::mozilla::layers::layerscope::CommandPacket_CmdType value);
// optional bool value = 2;
inline bool has_value() const;
inline void clear_value();
static const int kValueFieldNumber = 2;
inline bool value() const;
inline void set_value(bool value);
// @@protoc_insertion_point(class_scope:mozilla.layers.layerscope.CommandPacket)
private:
inline void set_has_type();
inline void clear_has_type();
inline void set_has_value();
inline void clear_has_value();
int type_;
bool value_;
mutable int _cached_size_;
::google::protobuf::uint32 _has_bits_[(2 + 31) / 32];
friend void protobuf_AddDesc_LayerScopePacket_2eproto();
friend void protobuf_AssignDesc_LayerScopePacket_2eproto();
friend void protobuf_ShutdownFile_LayerScopePacket_2eproto();
void InitAsDefaultInstance();
static CommandPacket* default_instance_;
};
// ===================================================================
@ -2803,6 +2907,55 @@ inline ::mozilla::layers::layerscope::MetaPacket* Packet::release_meta() {
return temp;
}
// -------------------------------------------------------------------
// CommandPacket
// required .mozilla.layers.layerscope.CommandPacket.CmdType type = 1;
inline bool CommandPacket::has_type() const {
return (_has_bits_[0] & 0x00000001u) != 0;
}
inline void CommandPacket::set_has_type() {
_has_bits_[0] |= 0x00000001u;
}
inline void CommandPacket::clear_has_type() {
_has_bits_[0] &= ~0x00000001u;
}
inline void CommandPacket::clear_type() {
type_ = 0;
clear_has_type();
}
inline ::mozilla::layers::layerscope::CommandPacket_CmdType CommandPacket::type() const {
return static_cast< ::mozilla::layers::layerscope::CommandPacket_CmdType >(type_);
}
inline void CommandPacket::set_type(::mozilla::layers::layerscope::CommandPacket_CmdType value) {
GOOGLE_DCHECK(::mozilla::layers::layerscope::CommandPacket_CmdType_IsValid(value));
set_has_type();
type_ = value;
}
// optional bool value = 2;
inline bool CommandPacket::has_value() const {
return (_has_bits_[0] & 0x00000002u) != 0;
}
inline void CommandPacket::set_has_value() {
_has_bits_[0] |= 0x00000002u;
}
inline void CommandPacket::clear_has_value() {
_has_bits_[0] &= ~0x00000002u;
}
inline void CommandPacket::clear_value() {
value_ = false;
clear_has_value();
}
inline bool CommandPacket::value() const {
return value_;
}
inline void CommandPacket::set_value(bool value) {
set_has_value();
value_ = value;
}
// @@protoc_insertion_point(namespace_scope)

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

@ -4,6 +4,9 @@ option optimize_for = LITE_RUNTIME;
package mozilla.layers.layerscope;
// ===============================
// Server to Client messages
// ===============================
message FramePacket {
optional uint64 value = 1;
}
@ -133,3 +136,17 @@ message Packet {
optional LayersPacket layers = 5;
optional MetaPacket meta = 6;
}
// ===============================
// Client to Server messages
// ===============================
message CommandPacket {
enum CmdType {
NO_OP = 0;
LAYERS_TREE = 1;
LAYERS_BUFFER = 2;
}
required CmdType type = 1;
optional bool value = 2;
}

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

@ -24,6 +24,7 @@
#include <sched.h>
#include <stdio.h>
#include <sys/klog.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/resource.h>
#include <time.h>
@ -47,6 +48,7 @@
#include "HalImpl.h"
#include "HalLog.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/dom/battery/Constants.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/FileUtils.h"
@ -628,28 +630,6 @@ namespace {
/**
* RAII class to help us remember to close file descriptors.
*/
const char *wakeLockFilename = "/sys/power/wake_lock";
const char *wakeUnlockFilename = "/sys/power/wake_unlock";
template<ssize_t n>
bool ReadFromFile(const char *filename, char (&buf)[n])
{
int fd = open(filename, O_RDONLY);
ScopedClose autoClose(fd);
if (fd < 0) {
HAL_LOG("Unable to open file %s.", filename);
return false;
}
ssize_t numRead = read(fd, buf, n);
if (numRead < 0) {
HAL_LOG("Error reading from file %s.", filename);
return false;
}
buf[std::min(numRead, n - 1)] = '\0';
return true;
}
bool WriteToFile(const char *filename, const char *toWrite)
{
@ -775,6 +755,9 @@ static Monitor* sInternalLockCpuMonitor = nullptr;
static void
UpdateCpuSleepState()
{
const char *wakeLockFilename = "/sys/power/wake_lock";
const char *wakeUnlockFilename = "/sys/power/wake_unlock";
sInternalLockCpuMonitor->AssertCurrentThreadOwns();
bool allowed = sCpuSleepAllowed && !sInternalLockCpuCount;
WriteToFile(allowed ? wakeUnlockFilename : wakeLockFilename, "gecko");
@ -1286,7 +1269,6 @@ OomVictimLogger::Observe(
lineTimestampFound = true;
mLastLineChecked = lineTimestamp;
}
// Log interesting lines
for (size_t i = 0; i < regex_count; i++) {
@ -1312,6 +1294,261 @@ OomVictimLogger::Observe(
return NS_OK;
}
/**
* Wraps a particular ProcessPriority, giving us easy access to the prefs that
* are relevant to it.
*
* Creating a PriorityClass also ensures that the control group is created.
*/
class PriorityClass
{
public:
/**
* Create a PriorityClass for the given ProcessPriority. This implicitly
* reads the relevant prefs and opens the cgroup.procs file of the relevant
* control group caching its file descriptor for later use.
*/
PriorityClass(ProcessPriority aPriority);
/**
* Closes the file descriptor for the cgroup.procs file of the associated
* control group.
*/
~PriorityClass();
PriorityClass(const PriorityClass& aOther);
PriorityClass& operator=(const PriorityClass& aOther);
ProcessPriority Priority()
{
return mPriority;
}
int32_t OomScoreAdj()
{
return clamped<int32_t>(mOomScoreAdj, OOM_SCORE_ADJ_MIN, OOM_SCORE_ADJ_MAX);
}
int32_t KillUnderKB()
{
return mKillUnderKB;
}
nsCString CGroup()
{
return mGroup;
}
/**
* Adds a process to this priority class, this moves the process' PID into
* the associated control group.
*
* @param aPid The PID of the process to be added.
*/
void AddProcess(int aPid);
private:
ProcessPriority mPriority;
int32_t mOomScoreAdj;
int32_t mKillUnderKB;
int mCGroupProcsFd;
nsCString mGroup;
/**
* Return a string that identifies where we can find the value of aPref
* that's specific to mPriority. For example, we might return
* "hal.processPriorityManager.gonk.FOREGROUND_HIGH.oomScoreAdjust".
*/
nsCString PriorityPrefName(const char* aPref)
{
return nsPrintfCString("hal.processPriorityManager.gonk.%s.%s",
ProcessPriorityToString(mPriority), aPref);
}
/**
* Get the full path of the cgroup.procs file associated with the group.
*/
nsCString CGroupProcsFilename()
{
nsCString cgroupName = mGroup;
/* If mGroup is empty, our cgroup.procs file is the root procs file,
* located at /dev/cpuctl/cgroup.procs. Otherwise our procs file is
* /dev/cpuctl/NAME/cgroup.procs. */
if (!mGroup.IsEmpty()) {
cgroupName.AppendLiteral("/");
}
return NS_LITERAL_CSTRING("/dev/cpuctl/") + cgroupName +
NS_LITERAL_CSTRING("cgroup.procs");
}
int OpenCGroupProcs()
{
return open(CGroupProcsFilename().get(), O_WRONLY);
}
};
/**
* Try to create the cgroup for the given PriorityClass, if it doesn't already
* exist. This essentially implements mkdir -p; that is, we create parent
* cgroups as necessary. The group parameters are also set according to
* the corresponding preferences.
*
* @param aGroup The name of the group.
* @return true if we successfully created the cgroup, or if it already
* exists. Otherwise, return false.
*/
static bool
EnsureCGroupExists(const nsACString &aGroup)
{
NS_NAMED_LITERAL_CSTRING(kDevCpuCtl, "/dev/cpuctl/");
NS_NAMED_LITERAL_CSTRING(kSlash, "/");
nsAutoCString prefPrefix("hal.processPriorityManager.gonk.cgroups.");
/* If cgroup is not empty, append the cgroup name and a dot to obtain the
* group specific preferences. */
if (!aGroup.IsEmpty()) {
prefPrefix += aGroup + NS_LITERAL_CSTRING(".");
}
nsAutoCString cpuSharesPref(prefPrefix + NS_LITERAL_CSTRING("cpu_shares"));
int cpuShares = Preferences::GetInt(cpuSharesPref.get());
nsAutoCString cpuNotifyOnMigratePref(prefPrefix
+ NS_LITERAL_CSTRING("cpu_notify_on_migrate"));
int cpuNotifyOnMigrate = Preferences::GetInt(cpuNotifyOnMigratePref.get());
// Create mCGroup and its parent directories, as necessary.
nsCString cgroupIter = aGroup + kSlash;
int32_t offset = 0;
while ((offset = cgroupIter.FindChar('/', offset)) != -1) {
nsAutoCString path = kDevCpuCtl + Substring(cgroupIter, 0, offset);
int rv = mkdir(path.get(), 0744);
if (rv == -1 && errno != EEXIST) {
HAL_LOG("Could not create the %s control group.", path.get());
return false;
}
offset++;
}
nsAutoCString pathPrefix(kDevCpuCtl + aGroup + kSlash);
nsAutoCString cpuSharesPath(pathPrefix + NS_LITERAL_CSTRING("cpu.shares"));
if (cpuShares && !WriteToFile(cpuSharesPath.get(),
nsPrintfCString("%d", cpuShares).get())) {
HAL_LOG("Could not set the cpu share for group %s", cpuSharesPath.get());
return false;
}
nsAutoCString notifyOnMigratePath(pathPrefix
+ NS_LITERAL_CSTRING("cpu.notify_on_migrate"));
if (!WriteToFile(notifyOnMigratePath.get(),
nsPrintfCString("%d", cpuNotifyOnMigrate).get())) {
HAL_LOG("Could not set the cpu migration notification flag for group %s",
notifyOnMigratePath.get());
return false;
}
return true;
}
PriorityClass::PriorityClass(ProcessPriority aPriority)
: mPriority(aPriority)
, mOomScoreAdj(0)
, mKillUnderKB(0)
, mCGroupProcsFd(-1)
{
DebugOnly<nsresult> rv;
rv = Preferences::GetInt(PriorityPrefName("OomScoreAdjust").get(),
&mOomScoreAdj);
MOZ_ASSERT(NS_SUCCEEDED(rv), "Missing oom_score_adj preference");
rv = Preferences::GetInt(PriorityPrefName("KillUnderKB").get(),
&mKillUnderKB);
rv = Preferences::GetCString(PriorityPrefName("cgroup").get(), &mGroup);
MOZ_ASSERT(NS_SUCCEEDED(rv), "Missing control group preference");
if (EnsureCGroupExists(mGroup)) {
mCGroupProcsFd = OpenCGroupProcs();
}
}
PriorityClass::~PriorityClass()
{
close(mCGroupProcsFd);
}
PriorityClass::PriorityClass(const PriorityClass& aOther)
: mPriority(aOther.mPriority)
, mOomScoreAdj(aOther.mOomScoreAdj)
, mKillUnderKB(aOther.mKillUnderKB)
, mGroup(aOther.mGroup)
{
mCGroupProcsFd = OpenCGroupProcs();
}
PriorityClass& PriorityClass::operator=(const PriorityClass& aOther)
{
mPriority = aOther.mPriority;
mOomScoreAdj = aOther.mOomScoreAdj;
mKillUnderKB = aOther.mKillUnderKB;
mGroup = aOther.mGroup;
mCGroupProcsFd = OpenCGroupProcs();
return *this;
}
void PriorityClass::AddProcess(int aPid)
{
if (mCGroupProcsFd < 0) {
return;
}
nsPrintfCString str("%d", aPid);
if (write(mCGroupProcsFd, str.get(), strlen(str.get())) < 0) {
HAL_ERR("Couldn't add PID %d to the %s control group", aPid, mGroup.get());
}
}
/**
* Get the PriorityClass associated with the given ProcessPriority.
*
* If you pass an invalid ProcessPriority value, we return null.
*
* The pointers returned here are owned by GetPriorityClass (don't free them
* yourself). They are guaranteed to stick around until shutdown.
*/
PriorityClass*
GetPriorityClass(ProcessPriority aPriority)
{
static StaticAutoPtr<nsTArray<PriorityClass>> priorityClasses;
// Initialize priorityClasses if this is the first time we're running this
// method.
if (!priorityClasses) {
priorityClasses = new nsTArray<PriorityClass>();
ClearOnShutdown(&priorityClasses);
for (int32_t i = 0; i < NUM_PROCESS_PRIORITY; i++) {
priorityClasses->AppendElement(PriorityClass(ProcessPriority(i)));
}
}
if (aPriority < 0 ||
static_cast<uint32_t>(aPriority) >= priorityClasses->Length()) {
return nullptr;
}
return &(*priorityClasses)[aPriority];
}
static void
EnsureKernelLowMemKillerParamsSet()
{
@ -1352,21 +1589,12 @@ EnsureKernelLowMemKillerParamsSet()
// The system doesn't function correctly if we're missing these prefs, so
// crash loudly.
ProcessPriority priority = static_cast<ProcessPriority>(i);
PriorityClass* pc = GetPriorityClass(static_cast<ProcessPriority>(i));
int32_t oomScoreAdj;
if (!NS_SUCCEEDED(Preferences::GetInt(
nsPrintfCString("hal.processPriorityManager.gonk.%s.OomScoreAdjust",
ProcessPriorityToString(priority)).get(),
&oomScoreAdj))) {
MOZ_CRASH();
}
int32_t oomScoreAdj = pc->OomScoreAdj();
int32_t killUnderKB = pc->KillUnderKB();
int32_t killUnderKB;
if (!NS_SUCCEEDED(Preferences::GetInt(
nsPrintfCString("hal.processPriorityManager.gonk.%s.KillUnderKB",
ProcessPriorityToString(priority)).get(),
&killUnderKB))) {
if (killUnderKB == 0) {
// ProcessPriority values like PROCESS_PRIORITY_FOREGROUND_KEYBOARD,
// which has only OomScoreAdjust but lacks KillUnderMB value, will not
// create new LMK parameters.
@ -1397,7 +1625,8 @@ EnsureKernelLowMemKillerParamsSet()
minfreeParams.Cut(minfreeParams.Length() - 1, 1);
if (!adjParams.IsEmpty() && !minfreeParams.IsEmpty()) {
WriteToFile("/sys/module/lowmemorykiller/parameters/adj", adjParams.get());
WriteToFile("/sys/module/lowmemorykiller/parameters/minfree", minfreeParams.get());
WriteToFile("/sys/module/lowmemorykiller/parameters/minfree",
minfreeParams.get());
}
// Set the low-memory-notification threshold.
@ -1419,148 +1648,6 @@ EnsureKernelLowMemKillerParamsSet()
}
}
static void
SetNiceForPid(int aPid, int aNice)
{
errno = 0;
int origProcPriority = getpriority(PRIO_PROCESS, aPid);
if (errno) {
HAL_LOG("Unable to get nice for pid=%d; error %d. SetNiceForPid bailing.",
aPid, errno);
return;
}
int rv = setpriority(PRIO_PROCESS, aPid, aNice);
if (rv) {
HAL_LOG("Unable to set nice for pid=%d; error %d. SetNiceForPid bailing.",
aPid, errno);
return;
}
// On Linux, setpriority(aPid) modifies the priority only of the main
// thread of that process. We have to modify the priorities of all of the
// process's threads as well, so iterate over all the threads and increase
// each of their priorites by aNice - origProcPriority (and also ensure that
// none of the tasks has a lower priority than the main thread).
//
// This is horribly racy.
DIR* tasksDir = opendir(nsPrintfCString("/proc/%d/task/", aPid).get());
if (!tasksDir) {
HAL_LOG("Unable to open /proc/%d/task. SetNiceForPid bailing.", aPid);
return;
}
// Be careful not to leak tasksDir; after this point, we must call closedir().
while (struct dirent* de = readdir(tasksDir)) {
char* endptr = nullptr;
long tidlong = strtol(de->d_name, &endptr, /* base */ 10);
if (*endptr || tidlong < 0 || tidlong > INT32_MAX || tidlong == aPid) {
// if dp->d_name was not an integer, was negative (?!) or too large, or
// was the same as aPid, we're not interested.
//
// (The |tidlong == aPid| check is very important; without it, we'll
// renice aPid twice, and the second renice will be relative to the
// priority set by the first renice.)
continue;
}
int tid = static_cast<int>(tidlong);
// Do not set the priority of threads running with a real-time policy
// as part of the bulk process adjustment. These threads need to run
// at their specified priority in order to meet timing guarantees.
int schedPolicy = sched_getscheduler(tid);
if (schedPolicy == SCHED_FIFO || schedPolicy == SCHED_RR) {
continue;
}
errno = 0;
// Get and set the task's new priority.
int origtaskpriority = getpriority(PRIO_PROCESS, tid);
if (errno) {
HAL_LOG("Unable to get nice for tid=%d (pid=%d); error %d. This isn't "
"necessarily a problem; it could be a benign race condition.",
tid, aPid, errno);
continue;
}
int newtaskpriority =
std::max(origtaskpriority - origProcPriority + aNice, aNice);
// Do not reduce priority of threads already running at priorities greater
// than normal. These threads are likely special service threads that need
// elevated priorities to process audio, display composition, etc.
if (newtaskpriority > origtaskpriority &&
origtaskpriority < ANDROID_PRIORITY_NORMAL) {
continue;
}
rv = setpriority(PRIO_PROCESS, tid, newtaskpriority);
if (rv) {
HAL_LOG("Unable to set nice for tid=%d (pid=%d); error %d. This isn't "
"necessarily a problem; it could be a benign race condition.",
tid, aPid, errno);
continue;
}
}
HAL_LOG("Changed nice for pid %d from %d to %d.",
aPid, origProcPriority, aNice);
closedir(tasksDir);
}
/*
* Used to store the nice value adjustments and oom_adj values for the various
* process priority levels.
*/
struct ProcessPriorityPrefs {
bool initialized;
int lowPriorityNice;
struct {
int nice;
int oomScoreAdj;
} priorities[NUM_PROCESS_PRIORITY];
};
/*
* Reads the preferences for the various process priority levels and sets up
* watchers so that if they're dynamically changed the change is reflected on
* the appropriate variables.
*/
void
EnsureProcessPriorityPrefs(ProcessPriorityPrefs* prefs)
{
if (prefs->initialized) {
return;
}
// Read the preferences for process priority levels
for (int i = PROCESS_PRIORITY_BACKGROUND; i < NUM_PROCESS_PRIORITY; i++) {
ProcessPriority priority = static_cast<ProcessPriority>(i);
// Read the nice values
const char* processPriorityStr = ProcessPriorityToString(priority);
nsPrintfCString niceStr("hal.processPriorityManager.gonk.%s.Nice",
processPriorityStr);
Preferences::AddIntVarCache(&prefs->priorities[i].nice, niceStr.get());
// Read the oom_adj scores
nsPrintfCString oomStr("hal.processPriorityManager.gonk.%s.OomScoreAdjust",
processPriorityStr);
Preferences::AddIntVarCache(&prefs->priorities[i].oomScoreAdj,
oomStr.get());
}
Preferences::AddIntVarCache(&prefs->lowPriorityNice,
"hal.processPriorityManager.gonk.LowCPUNice");
prefs->initialized = true;
}
void
SetProcessPriority(int aPid,
ProcessPriority aPriority,
@ -1579,49 +1666,23 @@ SetProcessPriority(int aPid,
// SetProcessPriority being called early in startup.
EnsureKernelLowMemKillerParamsSet();
static ProcessPriorityPrefs prefs = { 0 };
EnsureProcessPriorityPrefs(&prefs);
PriorityClass* pc = GetPriorityClass(aPriority);
int oomScoreAdj = prefs.priorities[aPriority].oomScoreAdj;
int oomScoreAdj = pc->OomScoreAdj();
RoundOomScoreAdjUpWithBackroundLRU(oomScoreAdj, aBackgroundLRU);
int clampedOomScoreAdj = clamped<int>(oomScoreAdj, OOM_SCORE_ADJ_MIN,
OOM_SCORE_ADJ_MAX);
if (clampedOomScoreAdj != oomScoreAdj) {
HAL_LOG("Clamping OOM adjustment for pid %d to %d", aPid,
clampedOomScoreAdj);
} else {
HAL_LOG("Setting OOM adjustment for pid %d to %d", aPid,
clampedOomScoreAdj);
}
// We try the newer interface first, and fall back to the older interface
// on failure.
if (!WriteToFile(nsPrintfCString("/proc/%d/oom_score_adj", aPid).get(),
nsPrintfCString("%d", clampedOomScoreAdj).get()))
nsPrintfCString("%d", oomScoreAdj).get()))
{
int oomAdj = OomAdjOfOomScoreAdj(clampedOomScoreAdj);
WriteToFile(nsPrintfCString("/proc/%d/oom_adj", aPid).get(),
nsPrintfCString("%d", oomAdj).get());
nsPrintfCString("%d", OomAdjOfOomScoreAdj(oomScoreAdj)).get());
}
int nice = 0;
if (aCPUPriority == PROCESS_CPU_PRIORITY_NORMAL) {
nice = prefs.priorities[aPriority].nice;
} else if (aCPUPriority == PROCESS_CPU_PRIORITY_LOW) {
nice = prefs.lowPriorityNice;
} else {
HAL_ERR("Unknown aCPUPriority value %d", aCPUPriority);
MOZ_ASSERT(false);
return;
}
HAL_LOG("Setting nice for pid %d to %d", aPid, nice);
SetNiceForPid(aPid, nice);
HAL_LOG("Assigning pid %d to cgroup %s", aPid, pc->CGroup().get());
pc->AddProcess(aPid);
}
static bool

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

@ -809,16 +809,24 @@ js::atomics_futexWait(JSContext *cx, unsigned argc, Value *vp)
case JS::PerRuntimeFutexAPI::Timedout:
r.setInt32(AtomicsObject::FutexTimedout);
break;
case JS::PerRuntimeFutexAPI::ErrorTooLong:
// This is a hack, but it's serviceable.
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_ATOMICS_TOO_LONG);
case JS::PerRuntimeFutexAPI::ErrorException:
MOZ_ASSERT(JS_IsExceptionPending(cx));
retval = false;
break;
case JS::PerRuntimeFutexAPI::InterruptForTerminate:
// Throw an uncatchable exception.
JS_ClearPendingException(cx);
retval = false;
break;
case JS::PerRuntimeFutexAPI::WaitingNotAllowed:
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_ATOMICS_WAIT_NOT_ALLOWED);
retval = false;
break;
case JS::PerRuntimeFutexAPI::ErrorTooLong:
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_ATOMICS_TOO_LONG);
retval = false;
break;
default:
MOZ_CRASH();
}
if (w.lower_pri == &w) {

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

@ -191,7 +191,7 @@ ParseEvalStringAsJSON(JSContext *cx, const mozilla::Range<const CharT> chars, Mu
}
static EvalJSONResult
TryEvalJSON(JSContext *cx, JSFlatString *str, MutableHandleValue rval)
TryEvalJSON(JSContext *cx, JSLinearString *str, MutableHandleValue rval)
{
if (str->hasLatin1Chars()) {
AutoCheckCannotGC nogc;
@ -203,13 +203,13 @@ TryEvalJSON(JSContext *cx, JSFlatString *str, MutableHandleValue rval)
return EvalJSON_NotJSON;
}
AutoStableStringChars flatChars(cx);
if (!flatChars.init(cx, str))
AutoStableStringChars linearChars(cx);
if (!linearChars.init(cx, str))
return EvalJSON_Failure;
return flatChars.isLatin1()
? ParseEvalStringAsJSON(cx, flatChars.latin1Range(), rval)
: ParseEvalStringAsJSON(cx, flatChars.twoByteRange(), rval);
return linearChars.isLatin1()
? ParseEvalStringAsJSON(cx, linearChars.latin1Range(), rval)
: ParseEvalStringAsJSON(cx, linearChars.twoByteRange(), rval);
}
// Define subset of ExecuteType so that casting performs the injection.
@ -277,19 +277,19 @@ EvalKernel(JSContext *cx, const CallArgs &args, EvalType evalType, AbstractFrame
thisv = ObjectValue(*thisobj);
}
Rooted<JSFlatString*> flatStr(cx, str->ensureFlat(cx));
if (!flatStr)
RootedLinearString linearStr(cx, str->ensureLinear(cx));
if (!linearStr)
return false;
RootedScript callerScript(cx, caller ? caller.script() : nullptr);
EvalJSONResult ejr = TryEvalJSON(cx, flatStr, args.rval());
EvalJSONResult ejr = TryEvalJSON(cx, linearStr, args.rval());
if (ejr != EvalJSON_NotJSON)
return ejr == EvalJSON_Success;
EvalScriptGuard esg(cx);
if (evalType == DIRECT_EVAL && caller.isNonEvalFunctionFrame())
esg.lookupInEvalCache(flatStr, callerScript, pc);
esg.lookupInEvalCache(linearStr, callerScript, pc);
if (!esg.foundScript()) {
RootedScript maybeScript(cx);
@ -323,18 +323,18 @@ EvalKernel(JSContext *cx, const CallArgs &args, EvalType evalType, AbstractFrame
.setIntroductionInfo(introducerFilename, "eval", lineno, maybeScript, pcOffset)
.maybeMakeStrictMode(evalType == DIRECT_EVAL && IsStrictEvalPC(pc));
AutoStableStringChars flatChars(cx);
if (!flatChars.initTwoByte(cx, flatStr))
AutoStableStringChars linearChars(cx);
if (!linearChars.initTwoByte(cx, linearStr))
return false;
const char16_t *chars = flatChars.twoByteRange().start().get();
SourceBufferHolder::Ownership ownership = flatChars.maybeGiveOwnershipToCaller()
const char16_t *chars = linearChars.twoByteRange().start().get();
SourceBufferHolder::Ownership ownership = linearChars.maybeGiveOwnershipToCaller()
? SourceBufferHolder::GiveOwnership
: SourceBufferHolder::NoOwnership;
SourceBufferHolder srcBuf(chars, flatStr->length(), ownership);
SourceBufferHolder srcBuf(chars, linearStr->length(), ownership);
JSScript *compiled = frontend::CompileScript(cx, &cx->tempLifoAlloc(),
scopeobj, callerScript, staticScope,
options, srcBuf, flatStr, staticLevel);
options, srcBuf, linearStr, staticLevel);
if (!compiled)
return false;
@ -366,17 +366,17 @@ js::DirectEvalStringFromIon(JSContext *cx,
unsigned staticLevel = callerScript->staticLevel() + 1;
Rooted<JSFlatString*> flatStr(cx, str->ensureFlat(cx));
if (!flatStr)
RootedLinearString linearStr(cx, str->ensureLinear(cx));
if (!linearStr)
return false;
EvalJSONResult ejr = TryEvalJSON(cx, flatStr, vp);
EvalJSONResult ejr = TryEvalJSON(cx, linearStr, vp);
if (ejr != EvalJSON_NotJSON)
return ejr == EvalJSON_Success;
EvalScriptGuard esg(cx);
esg.lookupInEvalCache(flatStr, callerScript, pc);
esg.lookupInEvalCache(linearStr, callerScript, pc);
if (!esg.foundScript()) {
RootedScript maybeScript(cx);
@ -405,18 +405,18 @@ js::DirectEvalStringFromIon(JSContext *cx,
.setIntroductionInfo(introducerFilename, "eval", lineno, maybeScript, pcOffset)
.maybeMakeStrictMode(IsStrictEvalPC(pc));
AutoStableStringChars flatChars(cx);
if (!flatChars.initTwoByte(cx, flatStr))
AutoStableStringChars linearChars(cx);
if (!linearChars.initTwoByte(cx, linearStr))
return false;
const char16_t *chars = flatChars.twoByteRange().start().get();
SourceBufferHolder::Ownership ownership = flatChars.maybeGiveOwnershipToCaller()
const char16_t *chars = linearChars.twoByteRange().start().get();
SourceBufferHolder::Ownership ownership = linearChars.maybeGiveOwnershipToCaller()
? SourceBufferHolder::GiveOwnership
: SourceBufferHolder::NoOwnership;
SourceBufferHolder srcBuf(chars, flatStr->length(), ownership);
SourceBufferHolder srcBuf(chars, linearStr->length(), ownership);
JSScript *compiled = frontend::CompileScript(cx, &cx->tempLifoAlloc(),
scopeobj, callerScript, staticScope,
options, srcBuf, flatStr, staticLevel);
options, srcBuf, linearStr, staticLevel);
if (!compiled)
return false;

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

@ -1226,6 +1226,11 @@ MapObject::construct(JSContext *cx, unsigned argc, Value *vp)
return false;
CallArgs args = CallArgsFromVp(argc, vp);
// FIXME: bug 1083752
if (!WarnIfNotConstructing(cx, args, "Map"))
return false;
if (!args.get(0).isNullOrUndefined()) {
RootedValue adderVal(cx);
if (!GetProperty(cx, obj, obj, cx->names().set, &adderVal))
@ -1870,6 +1875,11 @@ SetObject::construct(JSContext *cx, unsigned argc, Value *vp)
return false;
CallArgs args = CallArgsFromVp(argc, vp);
// FIXME: bug 1083752
if (!WarnIfNotConstructing(cx, args, "Set"))
return false;
if (!args.get(0).isNullOrUndefined()) {
RootedValue adderVal(cx);
if (!GetProperty(cx, obj, obj, cx->names().add, &adderVal))

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

@ -97,16 +97,16 @@ function markGCType(typeName, child, why, depth, ptrdness)
if (depth == 0) {
if (!(typeName in gcTypes))
gcTypes[typeName] = Set();
gcTypes[typeName] = new Set();
gcTypes[typeName].add(why);
} else if (depth == 1) {
if (!(typeName in gcPointers))
gcPointers[typeName] = Set();
gcPointers[typeName] = new Set();
gcPointers[typeName].add(why);
}
if (!(typeName in gcFields))
gcFields[typeName] = Map();
gcFields[typeName] = new Map();
gcFields[typeName].set(why, [ child, ptrdness ]);
if (typeName in structureParents) {
@ -148,7 +148,7 @@ addGCPointer('JS::AutoCheckCannotGC');
function explain(csu, indent, seen) {
if (!seen)
seen = Set();
seen = new Set();
seen.add(csu);
if (!(csu in gcFields))
return;

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

@ -82,7 +82,7 @@ const Census = {};
function makeBasisChecker({compare, missing, extra}) {
return function makeWalker(basis) {
if (typeof basis === 'object') {
var unvisited = Set(Object.getOwnPropertyNames(basis));
var unvisited = new Set(Object.getOwnPropertyNames(basis));
return {
enter: prop => {
unvisited.delete(prop);

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

@ -5,4 +5,5 @@
//
Array.prototype.iterator = (function() { { while(0) function Uint8ClampedArray() { } } });
assertEq(Set(["testing", "testing", 123]).size(), 2);
var s = new Set(["testing", "testing", 123]);
assertEq(s.size(), 2);

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

@ -1,8 +1,8 @@
// |jit-test| error: TypeError
var set = Set(['a']);
var set = new Set(['a']);
var n = 5;
for (let v of set) {
if (n === 0)
break;
let g = set(Set(0xffffffff, n), 1);
let g = set(new Set(0xffffffff, n), 1);
}

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

@ -1,7 +1,7 @@
pSandbox = newGlobal();
evalcx("\
x = ArrayBuffer;\
y = Map();\
y = new Map();\
x += 1;\
w = x;\
x += '0';\

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

@ -19,8 +19,8 @@ assertEqArray([...[undefined]], [undefined]);
assertEqArray([...new Int32Array([1, 2, 3])], [1, 2, 3]);
assertEqArray([..."abc"], ["a", "b", "c"]);
assertEqArray([...[1, 2, 3][Symbol.iterator]()], [1, 2, 3]);
assertEqArray([...Set([1, 2, 3])], [1, 2, 3]);
assertEqArray([...Map([["a", "A"], ["b", "B"], ["c", "C"]])].map(([k, v]) => k + v), ["aA", "bB", "cC"]);
assertEqArray([...new Set([1, 2, 3])], [1, 2, 3]);
assertEqArray([...new Map([["a", "A"], ["b", "B"], ["c", "C"]])].map(([k, v]) => k + v), ["aA", "bB", "cC"]);
let itr = {};
itr[Symbol.iterator] = function () {
return {

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

@ -25,7 +25,7 @@ try { // line0 + 1
// other iterable objects
assertEq(eval(...["a + b"][Symbol.iterator]()), 11);
assertEq(eval(...Set(["a + b"])), 11);
assertEq(eval(...new Set(["a + b"])), 11);
let itr = {};
itr[Symbol.iterator] = function() {
return {

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

@ -9,7 +9,7 @@ function checkCommon(f) {
assertEqArray(f.apply(...[null, [1, 2, 3]]), [1, 2, 3]);
// other iterable objects
assertEqArray(f.apply(...Set([null, [1, 2, 3]])), [1, 2, 3]);
assertEqArray(f.apply(...new Set([null, [1, 2, 3]])), [1, 2, 3]);
assertEqArray(f.apply(...[null, [1, 2, 3]][Symbol.iterator]()), [1, 2, 3]);
let itr = {};
itr[Symbol.iterator] = function() {
@ -81,7 +81,7 @@ function checkRest(f) {
assertEqArray(f.apply(null, ...[[undefined]]), [undefined]);
// other iterable objects
assertEqArray(f.apply(null, ...Map([[["a", "A"], ["b", "B"]]])).map(([k, v]) => k + v), ["aA", "bB"]);
assertEqArray(f.apply(null, ...new Map([[["a", "A"], ["b", "B"]]])).map(([k, v]) => k + v), ["aA", "bB"]);
}
checkRest(function(...x) x);

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

@ -22,8 +22,8 @@ function checkLength(f, makeFn) {
assertEq(makeFn("...arg")(f, new Int32Array([1, 2, 3])), 3);
assertEq(makeFn("...arg")(f, "abc"), 3);
assertEq(makeFn("...arg")(f, [1, 2, 3][Symbol.iterator]()), 3);
assertEq(makeFn("...arg")(f, Set([1, 2, 3])), 3);
assertEq(makeFn("...arg")(f, Map([["a", "A"], ["b", "B"], ["c", "C"]])), 3);
assertEq(makeFn("...arg")(f, new Set([1, 2, 3])), 3);
assertEq(makeFn("...arg")(f, new Map([["a", "A"], ["b", "B"], ["c", "C"]])), 3);
let itr = {};
itr[Symbol.iterator] = function() {
return {

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

@ -17,8 +17,8 @@ function checkCommon(f, makeFn) {
assertEqArray(makeFn("...arg")(f, new Int32Array([1, 2, 3])), [1, 2, 3]);
assertEqArray(makeFn("...arg")(f, "abc"), ["a", "b", "c"]);
assertEqArray(makeFn("...arg")(f, [1, 2, 3][Symbol.iterator]()), [1, 2, 3]);
assertEqArray(makeFn("...arg")(f, Set([1, 2, 3])), [1, 2, 3]);
assertEqArray(makeFn("...arg")(f, Map([["a", "A"], ["b", "B"], ["c", "C"]])).map(([k, v]) => k + v), ["aA", "bB", "cC"]);
assertEqArray(makeFn("...arg")(f, new Set([1, 2, 3])), [1, 2, 3]);
assertEqArray(makeFn("...arg")(f, new Map([["a", "A"], ["b", "B"], ["c", "C"]])).map(([k, v]) => k + v), ["aA", "bB", "cC"]);
let itr = {};
itr[Symbol.iterator] = function() {
return {

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

@ -1,6 +1,6 @@
// Clearing an empty Map has no effect.
var m = Map();
var m = new Map();
for (var i = 0; i < 2; i++) {
m.clear();
assertEq(m.size, 0);

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

@ -1,6 +1,6 @@
// Clearing a Map removes its entries; the Map remains usable afterwards.
var m = Map([["a", "b"], ["b", "c"]]);
var m = new Map([["a", "b"], ["b", "c"]]);
assertEq(m.size, 2);
m.clear();
assertEq(m.size, 0);

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

@ -1,6 +1,6 @@
// Clearing a Map with a nontrivial number of elements works.
var m = Map();
var m = new Map();
for (var i = 0; i < 100; i++)
m.set(i, i);
assertEq(m.size, i);

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

@ -1,6 +1,6 @@
// Clearing a Map after deleting some entries works.
var m = Map([["a", 1], ["b", 2], ["c", 3], ["d", 4]]);
var m = new Map([["a", 1], ["b", 2], ["c", 3], ["d", 4]]);
for (var [k, v] of m)
if (k !== "c")
m.delete(k);

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

@ -1,7 +1,7 @@
// Map.clear is unaffected by deleting/monkeypatching Map.prototype.{delete,iterator}.
var data = [["a", 1], ["b", 2]];
var m1 = Map(data), m2 = Map(data);
var m1 = new Map(data), m2 = new Map(data);
delete Map.prototype.delete;
delete Map.prototype.iterator;

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

@ -1,6 +1,6 @@
// Clearing a Map doesn't affect expando properties.
var m = Map();
var m = new Map();
m.x = 3;
m.clear();
assertEq(m.x, 3);

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

@ -2,7 +2,7 @@
load(libdir + "referencesVia.js");
var m = Map();
var m = new Map();
var k = {}, v = {};
m.set(k, v);
assertEq(referencesVia(m, "key", k), true);

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

@ -2,19 +2,19 @@
load(libdir + "iteration.js");
var m = Map();
var m = new Map();
var it = m[Symbol.iterator]();
m.clear();
assertIteratorDone(it, undefined);
m = Map([["a", 1], ["b", 2], ["c", 3], ["d", 4]]);
m = new Map([["a", 1], ["b", 2], ["c", 3], ["d", 4]]);
it = m[Symbol.iterator]();
assertIteratorNext(it, ["a", 1]);
m.clear();
assertIteratorDone(it, undefined);
var log = "";
m = Map([["a", 1], ["b", 2], ["c", 3], ["d", 4]]);
m = new Map([["a", 1], ["b", 2], ["c", 3], ["d", 4]]);
for (var [k, v] of m) {
log += k + v;
if (k == "b")

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

@ -3,7 +3,7 @@
load(libdir + "asserts.js");
load(libdir + "iteration.js");
var m = Map([["a", 1]]);
var m = new Map([["a", 1]]);
var it = m[Symbol.iterator]();
assertIteratorNext(it, ["a", 1]);
m.clear();

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

@ -1,7 +1,17 @@
// The Map constructor creates an empty Map by default.
assertEq(Map().size, 0);
assertEq((new Map).size, 0);
assertEq(Map(undefined).size, 0);
assertEq(new Map(undefined).size, 0);
assertEq(new Map(null).size, 0);
load(libdir + "asserts.js");
var m = new Map();
assertEq(m.size, 0);
m = new Map(undefined);
assertEq(m.size, 0);
m = new Map(null);
assertEq(m.size, 0);
// FIXME: bug 1083752
options("werror");
assertEq(evaluate("Map()", {catchTermination: true}), "terminated");
// assertThrowsInstanceOf(() => Map(), TypeError);
// assertThrowsInstanceOf(() => Map(undefined), TypeError);
// assertThrowsInstanceOf(() => Map(null), TypeError);

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

@ -1,6 +1,6 @@
// The Map constructor can take an argument that is an array of pairs.
var arr = [["zero", 0], ["one", 1], ["two", 2]];
var m = Map(arr);
var m = new Map(arr);
for (var [k, v] of arr)
assertEq(m.get(k), v);

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

@ -1,7 +1,7 @@
// Map can take an argument that is an array of singleton arrays.
var arr = [["a"], ["b"], ["c"]];
var m = Map(arr);
var m = new Map(arr);
assertEq(m.size, 3);
for (var [k, _] of arr) {
assertEq(m.has(k), true);

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

@ -1,6 +1,6 @@
// Map(x) throws if x is not iterable (unless x is undefined).
// new Map(x) throws if x is not iterable (unless x is undefined).
load(libdir + "asserts.js");
var nonIterables = [true, 1, -0, 3.14, NaN, {}, Math, this];
for (let k of nonIterables)
assertThrowsInstanceOf(function () { Map(k); }, TypeError);
assertThrowsInstanceOf(function () { new Map(k); }, TypeError);

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

@ -1,14 +1,14 @@
// Map(arr) throws if arr contains holes (or undefined values).
// new Map(arr) throws if arr contains holes (or undefined values).
load(libdir + "asserts.js");
assertThrowsInstanceOf(function () { Map([undefined]); }, TypeError);
assertThrowsInstanceOf(function () { Map([null]); }, TypeError);
assertThrowsInstanceOf(function () { Map([[0, 0], [1, 1], , [3, 3]]); }, TypeError);
assertThrowsInstanceOf(function () { Map([[0, 0], [1, 1], ,]); }, TypeError);
assertThrowsInstanceOf(function () { new Map([undefined]); }, TypeError);
assertThrowsInstanceOf(function () { new Map([null]); }, TypeError);
assertThrowsInstanceOf(function () { new Map([[0, 0], [1, 1], , [3, 3]]); }, TypeError);
assertThrowsInstanceOf(function () { new Map([[0, 0], [1, 1], ,]); }, TypeError);
// Map(iterable) throws if iterable doesn't have array-like objects
// new Map(iterable) throws if iterable doesn't have array-like objects
assertThrowsInstanceOf(function () { Map([1, 2, 3]); }, TypeError);
assertThrowsInstanceOf(function () { new Map([1, 2, 3]); }, TypeError);
assertThrowsInstanceOf(function () {
let s = new Set([1, 2, "abc"]);
new Map(s);

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

@ -1,7 +1,7 @@
// When the argument to Map contains a key multiple times, the last value is retained.
var arg = [["zero", 7], ["one", 1], ["two", 4], ["zero", 8], ["two", 2], ["zero", 0]];
var m = Map(arg);
var m = new Map(arg);
assertEq(m.get("zero"), 0);
assertEq(m.get("one"), 1);
assertEq(m.get("two"), 2);

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

@ -10,7 +10,7 @@ function data(n) {
done = true;
}
var m = Map(data(50));
var m = new Map(data(50));
assertEq(done, true); // the constructor consumes the argument
assertEq(m.size, 50);
assertEq(m.get(""), 0);

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

@ -1,7 +1,7 @@
// The argument to Map can be a generator-expression.
var arr = [1, 2, "green", "red"];
var m = Map([v, v] for (v of arr));
var m = new Map([v, v] for (v of arr));
assertEq(m.size, 4);
for (var i = 0; i < 4; i++)

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

@ -1,8 +1,10 @@
// The argument to Map may be a generator-iterator that produces no values.
assertEq(Map(x for (x of [])).size, 0);
var m = new Map(x for (x of []));
assertEq(m.size, 0);
function none() {
if (0) yield 0;
}
assertEq(Map(none()).size, 0);
m = new Map(none());
assertEq(m.size, 0);

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

@ -9,4 +9,4 @@ function data2() {
}
var it = data2();
assertThrowsValue(function () { Map(it); }, "oops");
assertThrowsValue(function () { new Map(it); }, "oops");

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

@ -1,6 +1,6 @@
// map.delete(k) decrements the map size iff an entry was actually removed.
var m = Map();
var m = new Map();
m.delete(3);
assertEq(m.size, 0);
m.set({}, 'ok');

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

@ -1,6 +1,6 @@
// for-of can be used to iterate over a Map twice.
var map = Map([['a', 0], ['b', 1], ['c', 2]]);
var map = new Map([['a', 0], ['b', 1], ['c', 2]]);
var log = '';
for (let i = 0; i < 2; i++) {

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

@ -1,6 +1,6 @@
// Nested for-of loops can iterate over a Map.
var map = Map([['a', 0], ['b', 1]]);
var map = new Map([['a', 0], ['b', 1]]);
var log = '';
for (let [k0, v0] of map) {
log += k0 + v0 + ':'

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

@ -1,6 +1,6 @@
// map.iterator() is live: entries added during iteration are visited.
var map = Map();
var map = new Map();
function force(k) {
if (!map.has(k) && k >= 0)
map.set(k, k - 1);

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

@ -2,7 +2,7 @@
load(libdir + "iteration.js");
var map = Map();
var map = new Map();
var iter0 = map[Symbol.iterator](), iter1 = map[Symbol.iterator]();
assertIteratorDone(iter0, undefined); // closes iter0
map.set(1, 2);

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

@ -1,6 +1,6 @@
// Removing and re-adding entries while an iterator is live causes the iterator to visit them again.
var map = Map([['a', 1]]);
var map = new Map([['a', 1]]);
var n = 5;
for (let [k, v] of map) {
assertEq(k, 'a');

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

@ -2,7 +2,7 @@
load(libdir + "eqArrayHelper.js");
var map = Map();
var map = new Map();
for (var i = 7; i !== 1; i = i * 7 % 1117)
map.set("" + i, i);
assertEq(map.size, 557);

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

@ -3,7 +3,7 @@
load(libdir + "iteration.js");
var key = {};
var map = Map([[key, 'value']]);
var map = new Map([[key, 'value']]);
var entry = map[Symbol.iterator]().next().value;
assertEq(Array.isArray(entry), true);
assertEq(Object.getPrototypeOf(entry), Array.prototype);

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

@ -2,7 +2,7 @@
load(libdir + "iteration.js");
var map = Map([['a', 1], ['b', 2]]);
var map = new Map([['a', 1], ['b', 2]]);
var iter = map[Symbol.iterator]();
var a = iter.next(), b = iter.next();
assertIteratorResult(a, ['a', 1], false);

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

@ -2,7 +2,7 @@
load(libdir + "iteration.js");
var map = Map([['a', 1]]);
var map = new Map([['a', 1]]);
var res = map[Symbol.iterator]().next();
assertIteratorResult(res, ['a', 1], false);
res.value[0] = 'b';

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

@ -1,7 +1,7 @@
// for-of works on a cross-compartment wrapper of a Map.
var g = newGlobal();
var mw = g.eval("Map([['a', 1], ['b', 2]])");
var mw = g.eval("new Map([['a', 1], ['b', 2]])");
var log = '';
for (let [k, v] of mw)
log += k + v;

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

@ -8,13 +8,13 @@ var g = newGlobal();
var iterator_fn = Map.prototype[Symbol.iterator];
assertThrowsInstanceOf(function () { iterator_fn.call({}); }, TypeError);
assertThrowsInstanceOf(function () { iterator_fn.call(Set()); }, TypeError);
var mapw = g.eval("Map([['x', 1], ['y', 2]])");
assertThrowsInstanceOf(function () { iterator_fn.call(new Set()); }, TypeError);
var mapw = g.eval("new Map([['x', 1], ['y', 2]])");
assertEqArray(iterator_fn.call(mapw).next().value, ["x", 1]);
var next_fn = Map()[Symbol.iterator]().next;
var next_fn = (new Map())[Symbol.iterator]().next;
assertThrowsInstanceOf(function () { next_fn.call({}); }, TypeError);
assertThrowsInstanceOf(function () { next_fn.call(Set()[Symbol.iterator]()); }, TypeError);
assertThrowsInstanceOf(function () { next_fn.call((new Set())[Symbol.iterator]()); }, TypeError);
var iterw = mapw[Symbol.iterator]();
assertEqArray(next_fn.call(iterw).value, ["x", 1]);
assertEqArray(next_fn.call(iterw).value, ["y", 2]);

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

@ -2,7 +2,7 @@
function test(pairs) {
print(uneval(pairs));
var map = Map(pairs);
var map = new Map(pairs);
var all_keys = '';
var false_keys = '';

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

@ -2,7 +2,7 @@
load(libdir + "iteration.js");
var map = Map([['a', 0], ['b', 1], ['c', 2], ['d', 3]]);
var map = new Map([['a', 0], ['b', 1], ['c', 2], ['d', 3]]);
var iter = map[Symbol.iterator]();
var log = '';
for (let [k, v] of iter) {

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

@ -3,7 +3,7 @@
load(libdir + "asserts.js");
load(libdir + "iteration.js");
var map = Map([['a', 0], ['b', 1], ['c', 2], ['d', 3]]);
var map = new Map([['a', 0], ['b', 1], ['c', 2], ['d', 3]]);
var iter = map[Symbol.iterator]();
assertIteratorNext(iter, ['a', 0]);
assertIteratorNext(iter, ['b', 1]);

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

@ -3,7 +3,7 @@
load(libdir + "iteration.js");
// Make a map.
var map = Map();
var map = new Map();
var SIZE = 7;
for (var j = 0; j < SIZE; j++)
map.set(j, j);

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

@ -1,7 +1,7 @@
// Removing a Map entry already visited by an iterator does not cause any
// entries to be skipped.
var map = Map();
var map = new Map();
for (var i = 0; i < 20; i++)
map.set(String.fromCharCode('A'.charCodeAt(0) + i), i);

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

@ -4,7 +4,7 @@
load(libdir + "iteration.js");
var map = Map();
var map = new Map();
for (var i = 0; i < 32; i++)
map.set(i, i);
var iter = map[Symbol.iterator]();

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

@ -2,7 +2,7 @@
load(libdir + "iteration.js");
var m = Map();
var m = new Map();
var it = m[Symbol.iterator]();
assertIteratorDone(it, undefined); // close the iterator
m.clear();

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

@ -1,6 +1,6 @@
// map.set(k, v) increments the map size iff map didn't already have an entry for k.
var m = Map();
var m = new Map();
m.set('a', 0);
assertEq(m.size, 1);
m.set('a', 0);

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

@ -1,6 +1,6 @@
// Each Map has its own size.
var m1 = Map(), m2 = Map();
var m1 = new Map(), m2 = new Map();
m1.set("x", 3);
assertEq(m1.size, 1);
assertEq(m2.size, 0);

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

@ -14,8 +14,7 @@ assertEq(Map.name, "Map");
assertEq(Object.getPrototypeOf(Map.prototype), Object.prototype);
assertEq(Object.prototype.toString.call(Map.prototype), "[object Map]");
assertEq(Object.prototype.toString.call(new Map), "[object Map]");
assertEq(Object.prototype.toString.call(Map()), "[object Map]");
assertEq(Object.prototype.toString.call(new Map()), "[object Map]");
assertEq(Object.keys(Map.prototype).join(), "");
assertEq(Map.prototype.constructor, Map);

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

@ -1,6 +1,6 @@
// map.keys(), .values(), and .entries() on an empty map produce empty iterators.
var m = Map();
var m = new Map();
var ki = m.keys(), vi = m.values(), ei = m.entries();
var p = Object.getPrototypeOf(ki)
assertEq(Object.getPrototypeOf(vi), p);

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

@ -4,7 +4,7 @@
load(libdir + "iteration.js");
var data = [["one", 1], ["two", 2], ["three", 3], ["four", 4]];
var m = Map(data);
var m = new Map(data);
var ki = m.keys();
assertIteratorNext(ki, "one");

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

@ -1,6 +1,6 @@
// set.add(v) increments set.size iff the set did not already contain v.
var s = Set();
var s = new Set();
for (var i = 0; i < 10; i++) {
assertEq(s.size, i);
s.add(i);

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

@ -1,6 +1,6 @@
// Clearing an empty Set has no effect.
var s = Set();
var s = new Set();
for (var i = 0; i < 2; i++) {
s.clear();
assertEq(s.size, 0);

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

@ -1,6 +1,6 @@
// Clearing a Set removes its elements; the Set remains usable afterwards.
var s = Set(["x", "y", "z", "z", "y"]);
var s = new Set(["x", "y", "z", "z", "y"]);
assertEq(s.size, 3);
s.clear();
assertEq(s.size, 0);

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

@ -1,6 +1,6 @@
// Clearing a Set with a nontrivial number of elements works.
var s = Set();
var s = new Set();
for (var i = 0; i < 100; i++)
s.add(i);
assertEq(s.size, i);

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

@ -1,6 +1,6 @@
// Clearing a Set after deleting some entries works.
var s = Set(["a", "b", "c", "d"]);
var s = new Set(["a", "b", "c", "d"]);
for (var v of s)
if (v !== "c")
s.delete(v);

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

@ -1,7 +1,7 @@
// Set.clear is unaffected by deleting/monkeypatching Set.prototype.{delete,iterator}.
var data = ["a", 1, {}];
var s1 = Set(data), s2 = Set(data);
var s1 = new Set(data), s2 = new Set(data);
delete Set.prototype.delete;
delete Set.prototype.iterator;

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше