mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge m-i to m-c
This commit is contained in:
commit
9710fc9cf4
@ -62,7 +62,7 @@ namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
// The default value for kKindTable is "subtitles"
|
||||
static const char* kKindTableDefaultString = kKindTable->tag;
|
||||
static MOZ_CONSTEXPR const char* kKindTableDefaultString = kKindTable->tag;
|
||||
|
||||
/** HTMLTrackElement */
|
||||
HTMLTrackElement::HTMLTrackElement(already_AddRefed<nsINodeInfo> aNodeInfo)
|
||||
|
@ -22,7 +22,7 @@ namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
// Map html attribute string values to TextTrackKind enums.
|
||||
static const nsAttrValue::EnumTable kKindTable[] = {
|
||||
static MOZ_CONSTEXPR nsAttrValue::EnumTable kKindTable[] = {
|
||||
{ "subtitles", static_cast<int16_t>(TextTrackKind::Subtitles) },
|
||||
{ "captions", static_cast<int16_t>(TextTrackKind::Captions) },
|
||||
{ "descriptions", static_cast<int16_t>(TextTrackKind::Descriptions) },
|
||||
|
@ -36,7 +36,10 @@ var ecmaGlobals =
|
||||
"Float32Array",
|
||||
"Float64Array",
|
||||
"Function",
|
||||
"Infinity",
|
||||
// NB: We haven't bothered to resolve constants like Infinity and NaN on
|
||||
// Xrayed windows (which are seen from the XBL scope). We could support
|
||||
// this if needed with some refactoring.
|
||||
{name: "Infinity", xbl: false},
|
||||
"Int16Array",
|
||||
"Int32Array",
|
||||
"Int8Array",
|
||||
@ -46,7 +49,7 @@ var ecmaGlobals =
|
||||
"JSON",
|
||||
"Map",
|
||||
"Math",
|
||||
"NaN",
|
||||
{name: "NaN", xbl: false},
|
||||
"Number",
|
||||
"Object",
|
||||
{name: "ParallelArray", nightly: true},
|
||||
@ -657,9 +660,7 @@ function createInterfaceMap(isXBLScope) {
|
||||
}
|
||||
}
|
||||
|
||||
// Standard ECMAScript global objects are not defined on the XBL scope, but
|
||||
// everything else exists everywhere.
|
||||
addInterfaces(ecmaGlobals, !isXBLScope);
|
||||
addInterfaces(ecmaGlobals, true);
|
||||
addInterfaces(interfaceNamesInGlobalScope, true);
|
||||
|
||||
return interfaceMap;
|
||||
|
@ -5260,23 +5260,16 @@ WorkerPrivate::ConnectMessagePort(JSContext* aCx, uint64_t aMessagePortSerial)
|
||||
return false;
|
||||
}
|
||||
|
||||
nsRefPtr<nsDOMMessageEvent> event;
|
||||
{
|
||||
// Bug 940779 - MessageEventInit contains unrooted JS objects, and
|
||||
// ~nsRefPtr can GC, so make sure 'init' is no longer live before ~nsRefPtr
|
||||
// runs (or the nsRefPtr is even created) to avoid a rooting hazard. Note
|
||||
// that 'init' is live until its destructor runs, not just until its final
|
||||
// use.
|
||||
MessageEventInit init;
|
||||
init.mBubbles = false;
|
||||
init.mCancelable = false;
|
||||
init.mSource = &jsPort.toObject();
|
||||
RootedDictionary<MessageEventInit> init(aCx);
|
||||
init.mBubbles = false;
|
||||
init.mCancelable = false;
|
||||
init.mSource = &jsPort.toObject();
|
||||
|
||||
ErrorResult rv;
|
||||
event = nsDOMMessageEvent::Constructor(globalObject, aCx,
|
||||
NS_LITERAL_STRING("connect"),
|
||||
init, rv);
|
||||
}
|
||||
ErrorResult rv;
|
||||
|
||||
nsRefPtr<nsDOMMessageEvent> event =
|
||||
nsDOMMessageEvent::Constructor(globalObject, aCx,
|
||||
NS_LITERAL_STRING("connect"), init, rv);
|
||||
|
||||
event->SetTrusted(true);
|
||||
|
||||
|
@ -1965,8 +1965,12 @@ XMLHttpRequest::Send(const nsAString& aBody, ErrorResult& aRv)
|
||||
void
|
||||
XMLHttpRequest::Send(JSObject* aBody, ErrorResult& aRv)
|
||||
{
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
JSContext* cx = mWorkerPrivate->GetJSContext();
|
||||
|
||||
MOZ_ASSERT(aBody);
|
||||
JS::Rooted<JSObject*> body(cx, aBody);
|
||||
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
if (mCanceled) {
|
||||
aRv.Throw(UNCATCHABLE_EXCEPTION);
|
||||
@ -1978,15 +1982,13 @@ XMLHttpRequest::Send(JSObject* aBody, ErrorResult& aRv)
|
||||
return;
|
||||
}
|
||||
|
||||
JSContext* cx = mWorkerPrivate->GetJSContext();
|
||||
|
||||
JS::Rooted<JS::Value> valToClone(cx);
|
||||
if (JS_IsArrayBufferObject(aBody) || JS_IsArrayBufferViewObject(aBody) ||
|
||||
file::GetDOMBlobFromJSObject(aBody)) {
|
||||
valToClone.setObject(*aBody);
|
||||
if (JS_IsArrayBufferObject(body) || JS_IsArrayBufferViewObject(body) ||
|
||||
file::GetDOMBlobFromJSObject(body)) {
|
||||
valToClone.setObject(*body);
|
||||
}
|
||||
else {
|
||||
JS::Rooted<JS::Value> obj(cx, JS::ObjectValue(*aBody));
|
||||
JS::Rooted<JS::Value> obj(cx, JS::ObjectValue(*body));
|
||||
JSString* bodyStr = JS::ToString(cx, obj);
|
||||
if (!bodyStr) {
|
||||
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||
|
@ -4,10 +4,14 @@
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
SOURCES += [
|
||||
UNIFIED_SOURCES += [
|
||||
'nsAuthFactory.cpp',
|
||||
'nsAuthGSSAPI.cpp',
|
||||
'nsAuthSASL.cpp',
|
||||
]
|
||||
|
||||
# contains constants whose names conflict with constants in other files
|
||||
SOURCES += [
|
||||
'nsHttpNegotiateAuth.cpp',
|
||||
]
|
||||
|
||||
@ -17,7 +21,7 @@ if CONFIG['OS_ARCH'] == 'WINNT':
|
||||
]
|
||||
DEFINES['USE_SSPI'] = True
|
||||
else:
|
||||
SOURCES += [
|
||||
UNIFIED_SOURCES += [
|
||||
'nsAuthSambaNTLM.cpp',
|
||||
]
|
||||
|
||||
|
@ -13,7 +13,7 @@ XPIDL_SOURCES += [
|
||||
|
||||
XPIDL_MODULE = 'cookie'
|
||||
|
||||
SOURCES += [
|
||||
UNIFIED_SOURCES += [
|
||||
'nsCookieModule.cpp',
|
||||
'nsCookiePermission.cpp',
|
||||
'nsCookiePromptService.cpp',
|
||||
|
@ -4,7 +4,7 @@
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
SOURCES += [
|
||||
UNIFIED_SOURCES += [
|
||||
'nsContentBlocker.cpp',
|
||||
'nsModuleFactory.cpp',
|
||||
]
|
||||
|
@ -4,7 +4,7 @@
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
SOURCES += [
|
||||
UNIFIED_SOURCES += [
|
||||
'nsAutoConfig.cpp',
|
||||
'nsConfigFactory.cpp',
|
||||
'nsJSConfigTriggers.cpp',
|
||||
|
@ -3,6 +3,9 @@
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef nsAutoConfig_h
|
||||
#define nsAutoConfig_h
|
||||
|
||||
#include "nsIAutoConfig.h"
|
||||
#include "nsITimer.h"
|
||||
#include "nsIFile.h"
|
||||
@ -46,3 +49,5 @@ class nsAutoConfig : public nsIAutoConfig,
|
||||
nsCOMPtr<nsITimer> mTimer;
|
||||
nsCString mConfigURL;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -3,6 +3,9 @@
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef nsReadConfig_h
|
||||
#define nsReadConfig_h
|
||||
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIReadConfig.h"
|
||||
#include "nsIAutoConfig.h"
|
||||
@ -33,3 +36,5 @@ class nsReadConfig : public nsIReadConfig,
|
||||
private:
|
||||
nsCOMPtr<nsIAutoConfig> mAutoConfig;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -4,7 +4,7 @@
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
SOURCES += [
|
||||
UNIFIED_SOURCES += [
|
||||
'CharDistribution.cpp',
|
||||
'JpCntx.cpp',
|
||||
'LangBulgarianModel.cpp',
|
||||
|
@ -4,7 +4,7 @@
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
SOURCES += [
|
||||
UNIFIED_SOURCES += [
|
||||
'nsUdetXPCOMWrapper.cpp',
|
||||
'nsUniversalCharDetModule.cpp',
|
||||
]
|
||||
|
@ -6,6 +6,8 @@
|
||||
#ifndef MOZILLA_GFX_BASEPOINT_H_
|
||||
#define MOZILLA_GFX_BASEPOINT_H_
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
@ -19,8 +21,8 @@ struct BasePoint {
|
||||
T x, y;
|
||||
|
||||
// Constructors
|
||||
BasePoint() : x(0), y(0) {}
|
||||
BasePoint(T aX, T aY) : x(aX), y(aY) {}
|
||||
MOZ_CONSTEXPR BasePoint() : x(0), y(0) {}
|
||||
MOZ_CONSTEXPR BasePoint(T aX, T aY) : x(aX), y(aY) {}
|
||||
|
||||
void MoveTo(T aX, T aY) { x = aX; y = aY; }
|
||||
void MoveBy(T aDx, T aDy) { x += aDx; y += aDy; }
|
||||
|
@ -6,6 +6,7 @@
|
||||
#ifndef MOZILLA_GFX_POINT_H_
|
||||
#define MOZILLA_GFX_POINT_H_
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "Types.h"
|
||||
#include "BasePoint.h"
|
||||
#include "BaseSize.h"
|
||||
@ -25,7 +26,7 @@ struct IntPointTyped :
|
||||
typedef BasePoint< int32_t, IntPointTyped<units> > Super;
|
||||
|
||||
IntPointTyped() : Super() {}
|
||||
IntPointTyped(int32_t aX, int32_t aY) : Super(aX, aY) {}
|
||||
MOZ_CONSTEXPR IntPointTyped(int32_t aX, int32_t aY) : Super(aX, aY) {}
|
||||
|
||||
// XXX When all of the code is ported, the following functions to convert to and from
|
||||
// unknown types should be removed.
|
||||
|
@ -36,7 +36,7 @@ public:
|
||||
// We use IDs to identify frames across processes.
|
||||
typedef uint64_t ViewID;
|
||||
static const ViewID NULL_SCROLL_ID; // This container layer does not scroll.
|
||||
static const ViewID START_SCROLL_ID; // This is the ID that scrolling subframes
|
||||
static const ViewID START_SCROLL_ID = 2; // This is the ID that scrolling subframes
|
||||
// will begin at.
|
||||
|
||||
FrameMetrics()
|
||||
|
@ -37,7 +37,6 @@ using namespace mozilla::gfx;
|
||||
|
||||
typedef FrameMetrics::ViewID ViewID;
|
||||
const ViewID FrameMetrics::NULL_SCROLL_ID = 0;
|
||||
const ViewID FrameMetrics::START_SCROLL_ID = 1;
|
||||
|
||||
uint8_t gLayerManagerLayerBuilder;
|
||||
|
||||
|
@ -125,7 +125,7 @@ struct gfxRGBA {
|
||||
* Intialize this color using explicit red, green, blue and alpha
|
||||
* values.
|
||||
*/
|
||||
gfxRGBA(gfxFloat _r, gfxFloat _g, gfxFloat _b, gfxFloat _a=1.0) : r(_r), g(_g), b(_b), a(_a) {}
|
||||
MOZ_CONSTEXPR gfxRGBA(gfxFloat _r, gfxFloat _g, gfxFloat _b, gfxFloat _a=1.0) : r(_r), g(_g), b(_b), a(_a) {}
|
||||
|
||||
/**
|
||||
* Initialize this color from a packed 32-bit color.
|
||||
|
@ -11,6 +11,9 @@
|
||||
#include "nsDebug.h"
|
||||
#include "nsTraceRefcnt.h"
|
||||
|
||||
// Undo the damage done by mozzconf.h
|
||||
#undef compress
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace std;
|
||||
|
||||
|
@ -84,8 +84,8 @@ public:
|
||||
void* mem = shmat(mHandle, nullptr, 0);
|
||||
if (mem == (void*) -1) {
|
||||
char warning[256];
|
||||
snprintf(warning, sizeof(warning)-1,
|
||||
"shmat(): %s (%d)\n", strerror(errno), errno);
|
||||
::snprintf(warning, sizeof(warning)-1,
|
||||
"shmat(): %s (%d)\n", strerror(errno), errno);
|
||||
|
||||
NS_WARNING(warning);
|
||||
|
||||
|
@ -44,7 +44,7 @@ else:
|
||||
EXPORTS.mozilla.ipc += [
|
||||
'Transport_posix.h',
|
||||
]
|
||||
SOURCES += [
|
||||
UNIFIED_SOURCES += [
|
||||
'CrossProcessMutex_unimplemented.cpp',
|
||||
'SharedMemory_posix.cpp',
|
||||
'Transport_posix.cpp',
|
||||
@ -55,18 +55,18 @@ else:
|
||||
# impl.
|
||||
if CONFIG['OS_TARGET'] == 'Android':
|
||||
EXPORTS.mozilla.ipc += ['SharedMemoryBasic_android.h']
|
||||
SOURCES += [
|
||||
UNIFIED_SOURCES += [
|
||||
'SharedMemoryBasic_android.cpp',
|
||||
]
|
||||
else:
|
||||
EXPORTS.mozilla.ipc += ['SharedMemoryBasic_chromium.h']
|
||||
|
||||
if CONFIG['OS_ARCH'] == 'Linux':
|
||||
SOURCES += [
|
||||
UNIFIED_SOURCES += [
|
||||
'ProcessUtils_linux.cpp',
|
||||
]
|
||||
else:
|
||||
SOURCES += [
|
||||
UNIFIED_SOURCES += [
|
||||
'ProcessUtils_none.cpp',
|
||||
]
|
||||
|
||||
@ -74,11 +74,10 @@ EXPORTS.ipc += [
|
||||
'IPCMessageUtils.h',
|
||||
]
|
||||
|
||||
SOURCES += [
|
||||
UNIFIED_SOURCES += [
|
||||
'BrowserProcessSubThread.cpp',
|
||||
'FileDescriptor.cpp',
|
||||
'FileDescriptorUtils.cpp',
|
||||
'GeckoChildProcessHost.cpp',
|
||||
'InputStreamUtils.cpp',
|
||||
'MessageChannel.cpp',
|
||||
'MessageLink.cpp',
|
||||
@ -89,6 +88,12 @@ SOURCES += [
|
||||
'SharedMemory.cpp',
|
||||
'Shmem.cpp',
|
||||
'StringUtil.cpp',
|
||||
]
|
||||
|
||||
# GeckoChildProcessHost.cpp cannot be built in unified mode because it uses plarena.h.
|
||||
# URIUtils.cpp cannot be built in unified mode because of name clashes on strdup.
|
||||
SOURCES += [
|
||||
'GeckoChildProcessHost.cpp',
|
||||
'URIUtils.cpp',
|
||||
]
|
||||
|
||||
|
@ -50,15 +50,15 @@ class ProfileEntry
|
||||
// instances are volatile. These methods would not be available unless they
|
||||
// were marked as volatile as well.
|
||||
|
||||
bool js() volatile {
|
||||
bool js() const volatile {
|
||||
JS_ASSERT_IF(sp == nullptr, script_ != nullptr);
|
||||
return sp == nullptr;
|
||||
}
|
||||
|
||||
uint32_t line() volatile { JS_ASSERT(!js()); return idx; }
|
||||
JSScript *script() volatile { JS_ASSERT(js()); return script_; }
|
||||
void *stackAddress() volatile { return sp; }
|
||||
const char *label() volatile { return string; }
|
||||
uint32_t line() const volatile { JS_ASSERT(!js()); return idx; }
|
||||
JSScript *script() const volatile { JS_ASSERT(js()); return script_; }
|
||||
void *stackAddress() const volatile { return sp; }
|
||||
const char *label() const volatile { return string; }
|
||||
|
||||
void setLine(uint32_t aLine) volatile { JS_ASSERT(!js()); idx = aLine; }
|
||||
void setLabel(const char *aString) volatile { string = aString; }
|
||||
@ -66,7 +66,7 @@ class ProfileEntry
|
||||
void setScript(JSScript *aScript) volatile { script_ = aScript; }
|
||||
|
||||
// We can't know the layout of JSScript, so look in vm/SPSProfiler.cpp.
|
||||
JS_FRIEND_API(jsbytecode *) pc() volatile;
|
||||
JS_FRIEND_API(jsbytecode *) pc() const volatile;
|
||||
JS_FRIEND_API(void) setPC(jsbytecode *pc) volatile;
|
||||
|
||||
static size_t offsetOfString() { return offsetof(ProfileEntry, string); }
|
||||
|
@ -2042,7 +2042,7 @@ js_InitIntlClass(JSContext *cx, HandleObject obj)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
global->markStandardClassInitializedNoProto(&IntlClass);
|
||||
global->setConstructor(JSProto_Intl, ObjectValue(*Intl));
|
||||
|
||||
return Intl;
|
||||
}
|
||||
|
@ -698,12 +698,12 @@ s2b
|
||||
static int
|
||||
hi0bits
|
||||
#ifdef KR_headers
|
||||
(x) register ULong x;
|
||||
(x) ULong x;
|
||||
#else
|
||||
(register ULong x)
|
||||
(ULong x)
|
||||
#endif
|
||||
{
|
||||
register int k = 0;
|
||||
int k = 0;
|
||||
|
||||
if (!(x & 0xffff0000)) {
|
||||
k = 16;
|
||||
@ -737,8 +737,8 @@ lo0bits
|
||||
(ULong *y)
|
||||
#endif
|
||||
{
|
||||
register int k;
|
||||
register ULong x = *y;
|
||||
int k;
|
||||
ULong x = *y;
|
||||
|
||||
if (x & 7) {
|
||||
if (x & 1)
|
||||
@ -1156,7 +1156,7 @@ ulp
|
||||
(U x)
|
||||
#endif
|
||||
{
|
||||
register Long L;
|
||||
Long L;
|
||||
U a;
|
||||
|
||||
L = (word0(x) & Exp_mask) - (P-1)*Exp_msk1;
|
||||
|
@ -55,7 +55,7 @@ FindKeyword(const jschar *s, size_t length)
|
||||
{
|
||||
JS_ASSERT(length != 0);
|
||||
|
||||
register size_t i;
|
||||
size_t i;
|
||||
const KeywordInfo *kw;
|
||||
const char *chars;
|
||||
|
||||
|
189
js/src/jsapi.cpp
189
js/src/jsapi.cpp
@ -1162,61 +1162,54 @@ JS_InitStandardClasses(JSContext *cx, JSObject *objArg)
|
||||
#define TYPED_ARRAY_CLASP(type) (&TypedArrayObject::classes[ScalarTypeRepresentation::type])
|
||||
#define EAGER_ATOM(name) NAME_OFFSET(name)
|
||||
#define EAGER_CLASS_ATOM(name) NAME_OFFSET(name)
|
||||
#define EAGER_ATOM_AND_CLASP(name) EAGER_CLASS_ATOM(name), CLASP(name)
|
||||
#define EAGER_ATOM_AND_OCLASP(name) EAGER_CLASS_ATOM(name), OCLASP(name)
|
||||
|
||||
static JSObject *
|
||||
DummyInit(JSContext *cx, HandleObject obj)
|
||||
{
|
||||
MOZ_ASSUME_UNREACHABLE();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
typedef struct JSStdName {
|
||||
ClassInitializerOp init;
|
||||
size_t atomOffset; /* offset of atom pointer in JSAtomState */
|
||||
const Class *clasp;
|
||||
bool isDummy() const { return init == DummyInit; };
|
||||
} JSStdName;
|
||||
|
||||
static Handle<PropertyName*>
|
||||
StdNameToPropertyName(JSContext *cx, const JSStdName *stdn)
|
||||
static const JSStdName*
|
||||
LookupStdName(JSRuntime *rt, HandleString name, const JSStdName *table)
|
||||
{
|
||||
return AtomStateOffsetToName(cx->runtime()->atomState, stdn->atomOffset);
|
||||
MOZ_ASSERT(name->isAtom());
|
||||
for (unsigned i = 0; table[i].init; i++) {
|
||||
if (table[i].isDummy())
|
||||
continue;
|
||||
JSAtom *atom = AtomStateOffsetToName(rt->atomState, table[i].atomOffset);
|
||||
MOZ_ASSERT(atom);
|
||||
if (name == atom)
|
||||
return &table[i];
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/*
|
||||
* Table of class initializers and their atom offsets in rt->atomState.
|
||||
* If you add a "standard" class, remember to update this table.
|
||||
* Table of standard classes, indexed by JSProtoKey. For entries where the
|
||||
* JSProtoKey does not correspond to a class with a meaningful constructor, we
|
||||
* insert a null entry into the table.
|
||||
*/
|
||||
static const JSStdName standard_class_atoms[] = {
|
||||
{js_InitFunctionClass, EAGER_CLASS_ATOM(Function), &JSFunction::class_},
|
||||
{js_InitObjectClass, EAGER_CLASS_ATOM(Object), &JSObject::class_},
|
||||
{js_InitArrayClass, EAGER_ATOM_AND_OCLASP(Array)},
|
||||
{js_InitBooleanClass, EAGER_ATOM_AND_OCLASP(Boolean)},
|
||||
{js_InitDateClass, EAGER_ATOM_AND_OCLASP(Date)},
|
||||
{js_InitMathClass, EAGER_ATOM_AND_CLASP(Math)},
|
||||
{js_InitNumberClass, EAGER_ATOM_AND_OCLASP(Number)},
|
||||
{js_InitStringClass, EAGER_ATOM_AND_OCLASP(String)},
|
||||
{js_InitExceptionClasses, EAGER_ATOM_AND_OCLASP(Error)},
|
||||
{js_InitRegExpClass, EAGER_ATOM_AND_OCLASP(RegExp)},
|
||||
{js_InitIteratorClasses, EAGER_ATOM_AND_OCLASP(StopIteration)},
|
||||
{js_InitJSONClass, EAGER_ATOM_AND_CLASP(JSON)},
|
||||
{js_InitTypedArrayClasses, EAGER_CLASS_ATOM(ArrayBuffer), &js::ArrayBufferObject::protoClass},
|
||||
{js_InitWeakMapClass, EAGER_ATOM_AND_OCLASP(WeakMap)},
|
||||
{js_InitMapClass, EAGER_ATOM_AND_OCLASP(Map)},
|
||||
{js_InitSetClass, EAGER_ATOM_AND_OCLASP(Set)},
|
||||
#ifdef ENABLE_PARALLEL_JS
|
||||
{js_InitParallelArrayClass, EAGER_ATOM_AND_OCLASP(ParallelArray)},
|
||||
#endif
|
||||
{js_InitProxyClass, EAGER_CLASS_ATOM(Proxy), &ProxyObject::uncallableClass_},
|
||||
#if EXPOSE_INTL_API
|
||||
{js_InitIntlClass, EAGER_ATOM_AND_CLASP(Intl)},
|
||||
#endif
|
||||
#ifdef ENABLE_BINARYDATA
|
||||
{js_InitTypedObjectClass, EAGER_ATOM_AND_CLASP(TypedObject)},
|
||||
#endif
|
||||
{nullptr, 0, nullptr}
|
||||
#define STD_NAME_ENTRY(name, code, init, clasp) { init, EAGER_CLASS_ATOM(name), clasp },
|
||||
#define STD_DUMMY_ENTRY(name, code, init, dummy) { DummyInit, 0, nullptr },
|
||||
static const JSStdName standard_class_names[] = {
|
||||
JS_FOR_PROTOTYPES(STD_NAME_ENTRY, STD_DUMMY_ENTRY)
|
||||
{ nullptr, 0, nullptr }
|
||||
};
|
||||
|
||||
/*
|
||||
* Table of top-level function and constant names and their init functions.
|
||||
* If you add a "standard" global function or property, remember to update
|
||||
* this table.
|
||||
* Table of top-level function and constant names and the init function of the
|
||||
* corresponding standard class that sets them up.
|
||||
*/
|
||||
static const JSStdName standard_class_names[] = {
|
||||
static const JSStdName builtin_property_names[] = {
|
||||
{js_InitObjectClass, EAGER_ATOM(eval), &JSObject::class_},
|
||||
|
||||
/* Global properties and functions defined by the Number class. */
|
||||
@ -1238,32 +1231,6 @@ static const JSStdName standard_class_names[] = {
|
||||
{js_InitStringClass, EAGER_ATOM(uneval), OCLASP(String)},
|
||||
#endif
|
||||
|
||||
/* Exception constructors. */
|
||||
{js_InitExceptionClasses, EAGER_CLASS_ATOM(Error), OCLASP(Error)},
|
||||
{js_InitExceptionClasses, EAGER_CLASS_ATOM(InternalError), OCLASP(Error)},
|
||||
{js_InitExceptionClasses, EAGER_CLASS_ATOM(EvalError), OCLASP(Error)},
|
||||
{js_InitExceptionClasses, EAGER_CLASS_ATOM(RangeError), OCLASP(Error)},
|
||||
{js_InitExceptionClasses, EAGER_CLASS_ATOM(ReferenceError), OCLASP(Error)},
|
||||
{js_InitExceptionClasses, EAGER_CLASS_ATOM(SyntaxError), OCLASP(Error)},
|
||||
{js_InitExceptionClasses, EAGER_CLASS_ATOM(TypeError), OCLASP(Error)},
|
||||
{js_InitExceptionClasses, EAGER_CLASS_ATOM(URIError), OCLASP(Error)},
|
||||
|
||||
{js_InitIteratorClasses, EAGER_CLASS_ATOM(Iterator), &PropertyIteratorObject::class_},
|
||||
|
||||
/* Typed Arrays */
|
||||
{js_InitTypedArrayClasses, EAGER_CLASS_ATOM(ArrayBuffer), &ArrayBufferObject::class_},
|
||||
{js_InitTypedArrayClasses, EAGER_CLASS_ATOM(Int8Array), TYPED_ARRAY_CLASP(TYPE_INT8)},
|
||||
{js_InitTypedArrayClasses, EAGER_CLASS_ATOM(Uint8Array), TYPED_ARRAY_CLASP(TYPE_UINT8)},
|
||||
{js_InitTypedArrayClasses, EAGER_CLASS_ATOM(Int16Array), TYPED_ARRAY_CLASP(TYPE_INT16)},
|
||||
{js_InitTypedArrayClasses, EAGER_CLASS_ATOM(Uint16Array), TYPED_ARRAY_CLASP(TYPE_UINT16)},
|
||||
{js_InitTypedArrayClasses, EAGER_CLASS_ATOM(Int32Array), TYPED_ARRAY_CLASP(TYPE_INT32)},
|
||||
{js_InitTypedArrayClasses, EAGER_CLASS_ATOM(Uint32Array), TYPED_ARRAY_CLASP(TYPE_UINT32)},
|
||||
{js_InitTypedArrayClasses, EAGER_CLASS_ATOM(Float32Array), TYPED_ARRAY_CLASP(TYPE_FLOAT32)},
|
||||
{js_InitTypedArrayClasses, EAGER_CLASS_ATOM(Float64Array), TYPED_ARRAY_CLASP(TYPE_FLOAT64)},
|
||||
{js_InitTypedArrayClasses, EAGER_CLASS_ATOM(Uint8ClampedArray),
|
||||
TYPED_ARRAY_CLASP(TYPE_UINT8_CLAMPED)},
|
||||
{js_InitTypedArrayClasses, EAGER_CLASS_ATOM(DataView), &DataViewObject::class_},
|
||||
|
||||
{nullptr, 0, nullptr}
|
||||
};
|
||||
|
||||
@ -1298,15 +1265,12 @@ static const JSStdName object_prototype_names[] = {
|
||||
#undef EAGER_ATOM
|
||||
#undef EAGER_CLASS_ATOM
|
||||
#undef EAGER_ATOM_CLASP
|
||||
#undef EAGER_ATOM_AND_CLASP
|
||||
|
||||
JS_PUBLIC_API(bool)
|
||||
JS_ResolveStandardClass(JSContext *cx, HandleObject obj, HandleId id, bool *resolved)
|
||||
{
|
||||
JSRuntime *rt;
|
||||
JSAtom *atom;
|
||||
const JSStdName *stdnm;
|
||||
unsigned i;
|
||||
|
||||
AssertHeapIsIdle(cx);
|
||||
CHECK_REQUEST(cx);
|
||||
@ -1321,59 +1285,34 @@ JS_ResolveStandardClass(JSContext *cx, HandleObject obj, HandleId id, bool *reso
|
||||
RootedString idstr(cx, JSID_TO_STRING(id));
|
||||
|
||||
/* Check whether we're resolving 'undefined', and define it if so. */
|
||||
atom = rt->atomState.undefined;
|
||||
if (idstr == atom) {
|
||||
JSAtom *undefinedAtom = rt->atomState.undefined;
|
||||
if (idstr == undefinedAtom) {
|
||||
*resolved = true;
|
||||
RootedValue undefinedValue(cx, UndefinedValue());
|
||||
return JSObject::defineProperty(cx, obj, atom->asPropertyName(), undefinedValue,
|
||||
return JSObject::defineProperty(cx, obj, undefinedAtom->asPropertyName(),
|
||||
undefinedValue,
|
||||
JS_PropertyStub, JS_StrictPropertyStub,
|
||||
JSPROP_PERMANENT | JSPROP_READONLY);
|
||||
}
|
||||
|
||||
/* Try for class constructors/prototypes named by well-known atoms. */
|
||||
stdnm = nullptr;
|
||||
for (i = 0; standard_class_atoms[i].init; i++) {
|
||||
JS_ASSERT(standard_class_atoms[i].clasp);
|
||||
atom = AtomStateOffsetToName(rt->atomState, standard_class_atoms[i].atomOffset);
|
||||
if (idstr == atom) {
|
||||
stdnm = &standard_class_atoms[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
stdnm = LookupStdName(rt, idstr, standard_class_names);
|
||||
|
||||
/* Try less frequently used top-level functions and constants. */
|
||||
if (!stdnm)
|
||||
stdnm = LookupStdName(rt, idstr, builtin_property_names);
|
||||
|
||||
/*
|
||||
* Try even less frequently used names delegated from the global
|
||||
* object to Object.prototype, but only if the Object class hasn't
|
||||
* yet been initialized.
|
||||
*/
|
||||
if (!stdnm) {
|
||||
/* Try less frequently used top-level functions and constants. */
|
||||
for (i = 0; standard_class_names[i].init; i++) {
|
||||
JS_ASSERT(standard_class_names[i].clasp);
|
||||
atom = StdNameToPropertyName(cx, &standard_class_names[i]);
|
||||
if (!atom)
|
||||
return false;
|
||||
if (idstr == atom) {
|
||||
stdnm = &standard_class_names[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
RootedObject proto(cx);
|
||||
if (!JSObject::getProto(cx, obj, &proto))
|
||||
return false;
|
||||
if (!stdnm && !proto) {
|
||||
/*
|
||||
* Try even less frequently used names delegated from the global
|
||||
* object to Object.prototype, but only if the Object class hasn't
|
||||
* yet been initialized.
|
||||
*/
|
||||
for (i = 0; object_prototype_names[i].init; i++) {
|
||||
JS_ASSERT(object_prototype_names[i].clasp);
|
||||
atom = StdNameToPropertyName(cx, &object_prototype_names[i]);
|
||||
if (!atom)
|
||||
return false;
|
||||
if (idstr == atom) {
|
||||
stdnm = &object_prototype_names[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!proto)
|
||||
stdnm = LookupStdName(rt, idstr, object_prototype_names);
|
||||
}
|
||||
|
||||
if (stdnm) {
|
||||
@ -1415,10 +1354,17 @@ JS_EnumerateStandardClasses(JSContext *cx, HandleObject obj)
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Initialize any classes that have not been initialized yet. */
|
||||
for (unsigned i = 0; standard_class_atoms[i].init; i++) {
|
||||
const JSStdName &stdnm = standard_class_atoms[i];
|
||||
if (!obj->as<GlobalObject>().isStandardClassResolved(stdnm.clasp)) {
|
||||
/*
|
||||
* Initialize any classes that have not been initialized yet. Note that
|
||||
* resolving everything in standard_class_names has the effect of resolving
|
||||
* everything in builtin_property_names, so we don't need to iterate over
|
||||
* that separately. Moreover, we'll resolve the Object constructor as well,
|
||||
* so we can also skip object_prototype_names.
|
||||
*/
|
||||
for (unsigned i = 0; standard_class_names[i].init; i++) {
|
||||
const JSStdName &stdnm = standard_class_names[i];
|
||||
// Watch out for dummy entries.
|
||||
if (!stdnm.isDummy() && !obj->as<GlobalObject>().isStandardClassResolved(stdnm.clasp)) {
|
||||
if (!stdnm.init(cx, obj))
|
||||
return false;
|
||||
}
|
||||
@ -1463,6 +1409,23 @@ JS_IdentifyClassPrototype(JSContext *cx, JSObject *obj)
|
||||
return js_IdentifyClassPrototype(obj);
|
||||
}
|
||||
|
||||
extern JS_PUBLIC_API(JSProtoKey)
|
||||
JS_IdToProtoKey(JSContext *cx, JS::HandleId id)
|
||||
{
|
||||
AssertHeapIsIdle(cx);
|
||||
CHECK_REQUEST(cx);
|
||||
|
||||
if (!JSID_IS_ATOM(id))
|
||||
return JSProto_Null;
|
||||
RootedString idstr(cx, JSID_TO_STRING(id));
|
||||
const JSStdName *stdnm = LookupStdName(cx->runtime(), idstr, standard_class_names);
|
||||
if (!stdnm)
|
||||
return JSProto_Null;
|
||||
|
||||
MOZ_ASSERT(MOZ_ARRAY_LENGTH(standard_class_names) == JSProto_LIMIT + 1);
|
||||
return static_cast<JSProtoKey>(stdnm - standard_class_names);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSObject *)
|
||||
JS_GetObjectPrototype(JSContext *cx, JSObject *forObj)
|
||||
{
|
||||
|
@ -1769,6 +1769,9 @@ JS_GetClassPrototype(JSContext *cx, JSProtoKey key, JSObject **objp);
|
||||
extern JS_PUBLIC_API(JSProtoKey)
|
||||
JS_IdentifyClassPrototype(JSContext *cx, JSObject *obj);
|
||||
|
||||
extern JS_PUBLIC_API(JSProtoKey)
|
||||
JS_IdToProtoKey(JSContext *cx, JS::HandleId id);
|
||||
|
||||
/*
|
||||
* Returns the original value of |Function.prototype| from the global object in
|
||||
* which |forObj| was created.
|
||||
|
@ -54,7 +54,7 @@ const char * const js::TypeStrings[] = {
|
||||
js_null_str,
|
||||
};
|
||||
|
||||
#define DEFINE_PROTO_STRING(name,code,init) const char js_##name##_str[] = #name;
|
||||
#define DEFINE_PROTO_STRING(name,code,init,clasp) const char js_##name##_str[] = #name;
|
||||
JS_FOR_EACH_PROTOTYPE(DEFINE_PROTO_STRING)
|
||||
#undef DEFINE_PROTO_STRING
|
||||
|
||||
@ -149,7 +149,7 @@ js::InitCommonNames(JSContext *cx)
|
||||
#define COMMON_NAME_INFO(idpart, id, text) { js_##idpart##_str, sizeof(text) - 1 },
|
||||
FOR_EACH_COMMON_PROPERTYNAME(COMMON_NAME_INFO)
|
||||
#undef COMMON_NAME_INFO
|
||||
#define COMMON_NAME_INFO(name, code, init) { js_##name##_str, sizeof(#name) - 1 },
|
||||
#define COMMON_NAME_INFO(name, code, init, clasp) { js_##name##_str, sizeof(#name) - 1 },
|
||||
JS_FOR_EACH_PROTOTYPE(COMMON_NAME_INFO)
|
||||
#undef COMMON_NAME_INFO
|
||||
};
|
||||
|
@ -108,7 +108,7 @@ extern bool
|
||||
AtomIsInterned(JSContext *cx, JSAtom *atom);
|
||||
|
||||
/* Well-known predefined C strings. */
|
||||
#define DECLARE_PROTO_STR(name,code,init) extern const char js_##name##_str[];
|
||||
#define DECLARE_PROTO_STR(name,code,init,clasp) extern const char js_##name##_str[];
|
||||
JS_FOR_EACH_PROTOTYPE(DECLARE_PROTO_STR)
|
||||
#undef DECLARE_PROTO_STR
|
||||
|
||||
|
@ -560,6 +560,16 @@ js::GetObjectProto(JSContext *cx, JS::Handle<JSObject*> obj, JS::MutableHandle<J
|
||||
return true;
|
||||
}
|
||||
|
||||
JS_FRIEND_API(bool)
|
||||
js::GetOriginalEval(JSContext *cx, HandleObject scope, MutableHandleObject eval)
|
||||
{
|
||||
assertSameCompartment(cx, scope);
|
||||
if (!scope->global().getOrCreateObjectPrototype(cx))
|
||||
return false;
|
||||
eval.set(&scope->global().getOriginalEval().toObject());
|
||||
return true;
|
||||
}
|
||||
|
||||
JS_FRIEND_API(void)
|
||||
js::SetReservedSlotWithBarrier(JSObject *obj, size_t slot, const js::Value &value)
|
||||
{
|
||||
|
@ -544,6 +544,10 @@ SetFunctionNativeReserved(JSObject *fun, size_t which, const JS::Value &val);
|
||||
JS_FRIEND_API(bool)
|
||||
GetObjectProto(JSContext *cx, JS::Handle<JSObject*> obj, JS::MutableHandle<JSObject*> proto);
|
||||
|
||||
JS_FRIEND_API(bool)
|
||||
GetOriginalEval(JSContext *cx, JS::HandleObject scope,
|
||||
JS::MutableHandleObject eval);
|
||||
|
||||
inline void *
|
||||
GetObjectPrivate(JSObject *obj)
|
||||
{
|
||||
|
@ -1945,7 +1945,7 @@ GlobalObject::initIteratorClasses(JSContext *cx, Handle<GlobalObject *> global)
|
||||
if (!DefineConstructorAndPrototype(cx, global, JSProto_StopIteration, proto, proto))
|
||||
return false;
|
||||
|
||||
global->markStandardClassInitializedNoProto(&StopIterationObject::class_);
|
||||
global->setConstructor(JSProto_StopIteration, ObjectValue(*proto));
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -1489,7 +1489,7 @@ js_InitMathClass(JSContext *cx, HandleObject obj)
|
||||
if (!JS_DefineConstDoubles(cx, Math, math_constants))
|
||||
return nullptr;
|
||||
|
||||
obj->as<GlobalObject>().markStandardClassInitializedNoProto(&MathClass);
|
||||
obj->as<GlobalObject>().setConstructor(JSProto_Math, ObjectValue(*Math));
|
||||
|
||||
return Math;
|
||||
}
|
||||
|
@ -2907,13 +2907,13 @@ js_InitNullClass(JSContext *cx, HandleObject obj)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#define DECLARE_PROTOTYPE_CLASS_INIT(name,code,init) \
|
||||
#define DECLARE_PROTOTYPE_CLASS_INIT(name,code,init,clasp) \
|
||||
extern JSObject *init(JSContext *cx, Handle<JSObject*> obj);
|
||||
JS_FOR_EACH_PROTOTYPE(DECLARE_PROTOTYPE_CLASS_INIT)
|
||||
#undef DECLARE_PROTOTYPE_CLASS_INIT
|
||||
|
||||
static const ClassInitializerOp lazy_prototype_init[JSProto_LIMIT] = {
|
||||
#define LAZY_PROTOTYPE_INIT(name,code,init) init,
|
||||
#define LAZY_PROTOTYPE_INIT(name,code,init,clasp) init,
|
||||
JS_FOR_EACH_PROTOTYPE(LAZY_PROTOTYPE_INIT)
|
||||
#undef LAZY_PROTOTYPE_INIT
|
||||
};
|
||||
@ -5391,7 +5391,7 @@ js_GetObjectSlotName(JSTracer *trc, char *buf, size_t bufsize)
|
||||
if (!shape) {
|
||||
const char *slotname = nullptr;
|
||||
if (obj->is<GlobalObject>()) {
|
||||
#define TEST_SLOT_MATCHES_PROTOTYPE(name,code,init) \
|
||||
#define TEST_SLOT_MATCHES_PROTOTYPE(name,code,init,clasp) \
|
||||
if ((code) == slot) { slotname = js_##name##_str; goto found; }
|
||||
JS_FOR_EACH_PROTOTYPE(TEST_SLOT_MATCHES_PROTOTYPE)
|
||||
#undef TEST_SLOT_MATCHES_PROTOTYPE
|
||||
|
@ -885,7 +885,7 @@ js_InitJSONClass(JSContext *cx, HandleObject obj)
|
||||
if (!JS_DefineFunctions(cx, JSON, json_static_methods))
|
||||
return nullptr;
|
||||
|
||||
global->markStandardClassInitializedNoProto(&JSONClass);
|
||||
global->setConstructor(JSProto_JSON, ObjectValue(*JSON));
|
||||
|
||||
return JSON;
|
||||
}
|
||||
|
@ -4,57 +4,98 @@
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/* A higher-order macro for enumerating all JSProtoKey values. */
|
||||
|
||||
#ifndef jsprototypes_h
|
||||
#define jsprototypes_h
|
||||
|
||||
/* A higher-order macro for enumerating all JSProtoKey values. */
|
||||
/*
|
||||
* Enumerator codes in the second column must not change -- they are part of
|
||||
* the JS XDR API. Also note the symbols in the third column are extern "C";
|
||||
* clients should use extern "C" {} as appropriate when using this macro.
|
||||
* Consumers define macros as follows:
|
||||
* macro(name, code, init, clasp)
|
||||
* name: The canonical name of the class.
|
||||
* code: The enumerator code. There are part of the XDR API, and must not change.
|
||||
* init: Initialization function. These are |extern "C";|, and clients should use
|
||||
* |extern "C" {}| as appropriate when using this macro.
|
||||
* clasp: The JSClass for this object, or "dummy" if it doesn't exist.
|
||||
*
|
||||
*
|
||||
* Consumers wishing to iterate over all the JSProtoKey values, can use
|
||||
* JS_FOR_EACH_PROTOTYPE. However, there are certain values that don't correspond
|
||||
* to real constructors, like Null or constructors that are disabled via
|
||||
* preprocessor directives. We still need to include these in the JSProtoKey list
|
||||
* in order to maintain binary XDR compatibility, but we need to provide a tool
|
||||
* to handle them differently. JS_FOR_PROTOTYPES fills this niche.
|
||||
*
|
||||
* Consumers pass two macros to JS_FOR_PROTOTYPES - |real| and |imaginary|. The
|
||||
* former is invoked for entries that have real client-exposed constructors, and
|
||||
* the latter is called for the rest. Consumers that don't care about this
|
||||
* distinction can simply pass the same macro to both, which is exactly what
|
||||
* JS_FOR_EACH_PROTOTYPE does.
|
||||
*/
|
||||
|
||||
#define JS_FOR_EACH_PROTOTYPE(macro) \
|
||||
macro(Null, 0, js_InitNullClass) \
|
||||
macro(Object, 1, js_InitObjectClass) \
|
||||
macro(Function, 2, js_InitFunctionClass) \
|
||||
macro(Array, 3, js_InitArrayClass) \
|
||||
macro(Boolean, 4, js_InitBooleanClass) \
|
||||
macro(JSON, 5, js_InitJSONClass) \
|
||||
macro(Date, 6, js_InitDateClass) \
|
||||
macro(Math, 7, js_InitMathClass) \
|
||||
macro(Number, 8, js_InitNumberClass) \
|
||||
macro(String, 9, js_InitStringClass) \
|
||||
macro(RegExp, 10, js_InitRegExpClass) \
|
||||
macro(Error, 11, js_InitExceptionClasses) \
|
||||
macro(InternalError, 12, js_InitExceptionClasses) \
|
||||
macro(EvalError, 13, js_InitExceptionClasses) \
|
||||
macro(RangeError, 14, js_InitExceptionClasses) \
|
||||
macro(ReferenceError, 15, js_InitExceptionClasses) \
|
||||
macro(SyntaxError, 16, js_InitExceptionClasses) \
|
||||
macro(TypeError, 17, js_InitExceptionClasses) \
|
||||
macro(URIError, 18, js_InitExceptionClasses) \
|
||||
macro(Iterator, 19, js_InitIteratorClasses) \
|
||||
macro(StopIteration, 20, js_InitIteratorClasses) \
|
||||
macro(ArrayBuffer, 21, js_InitTypedArrayClasses) \
|
||||
macro(Int8Array, 22, js_InitTypedArrayClasses) \
|
||||
macro(Uint8Array, 23, js_InitTypedArrayClasses) \
|
||||
macro(Int16Array, 24, js_InitTypedArrayClasses) \
|
||||
macro(Uint16Array, 25, js_InitTypedArrayClasses) \
|
||||
macro(Int32Array, 26, js_InitTypedArrayClasses) \
|
||||
macro(Uint32Array, 27, js_InitTypedArrayClasses) \
|
||||
macro(Float32Array, 28, js_InitTypedArrayClasses) \
|
||||
macro(Float64Array, 29, js_InitTypedArrayClasses) \
|
||||
macro(Uint8ClampedArray, 30, js_InitTypedArrayClasses) \
|
||||
macro(Proxy, 31, js_InitProxyClass) \
|
||||
macro(WeakMap, 32, js_InitWeakMapClass) \
|
||||
macro(Map, 33, js_InitMapClass) \
|
||||
macro(Set, 34, js_InitSetClass) \
|
||||
macro(DataView, 35, js_InitTypedArrayClasses) \
|
||||
macro(ParallelArray, 36, js_InitParallelArrayClass) \
|
||||
macro(Intl, 37, js_InitIntlClass) \
|
||||
macro(TypedObject, 38, js_InitTypedObjectClass) \
|
||||
macro(GeneratorFunction, 39, js_InitIteratorClasses) \
|
||||
#define CLASP(name) (&name##Class)
|
||||
#define OCLASP(name) (&name##Object::class_)
|
||||
#define TYPED_ARRAY_CLASP(type) (&TypedArrayObject::classes[ScalarTypeRepresentation::type])
|
||||
|
||||
#ifdef ENABLE_PARALLEL_JS
|
||||
#define IF_PJS(real,imaginary) real
|
||||
#else
|
||||
#define IF_PJS(real,imaginary) imaginary
|
||||
#endif
|
||||
|
||||
#ifdef EXPOSE_INTL_API
|
||||
#define IF_INTL(real,imaginary) real
|
||||
#else
|
||||
#define IF_INTL(real,imaginary) imaginary
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_BINARYDATA
|
||||
#define IF_BDATA(real,imaginary) real
|
||||
#else
|
||||
#define IF_BDATA(real,imaginary) imaginary
|
||||
#endif
|
||||
|
||||
#define JS_FOR_PROTOTYPES(real,imaginary) \
|
||||
imaginary(Null, 0, js_InitNullClass, dummy) \
|
||||
real(Object, 1, js_InitObjectClass, &JSObject::class_) \
|
||||
real(Function, 2, js_InitFunctionClass, &JSFunction::class_) \
|
||||
real(Array, 3, js_InitArrayClass, OCLASP(Array)) \
|
||||
real(Boolean, 4, js_InitBooleanClass, OCLASP(Boolean)) \
|
||||
real(JSON, 5, js_InitJSONClass, CLASP(JSON)) \
|
||||
real(Date, 6, js_InitDateClass, OCLASP(Date)) \
|
||||
real(Math, 7, js_InitMathClass, CLASP(Math)) \
|
||||
real(Number, 8, js_InitNumberClass, OCLASP(Number)) \
|
||||
real(String, 9, js_InitStringClass, OCLASP(String)) \
|
||||
real(RegExp, 10, js_InitRegExpClass, OCLASP(RegExp)) \
|
||||
real(Error, 11, js_InitExceptionClasses, OCLASP(Error)) \
|
||||
real(InternalError, 12, js_InitExceptionClasses, OCLASP(Error)) \
|
||||
real(EvalError, 13, js_InitExceptionClasses, OCLASP(Error)) \
|
||||
real(RangeError, 14, js_InitExceptionClasses, OCLASP(Error)) \
|
||||
real(ReferenceError, 15, js_InitExceptionClasses, OCLASP(Error)) \
|
||||
real(SyntaxError, 16, js_InitExceptionClasses, OCLASP(Error)) \
|
||||
real(TypeError, 17, js_InitExceptionClasses, OCLASP(Error)) \
|
||||
real(URIError, 18, js_InitExceptionClasses, OCLASP(Error)) \
|
||||
real(Iterator, 19, js_InitIteratorClasses, OCLASP(PropertyIterator)) \
|
||||
real(StopIteration, 20, js_InitIteratorClasses, OCLASP(StopIteration)) \
|
||||
real(ArrayBuffer, 21, js_InitTypedArrayClasses, &js::ArrayBufferObject::protoClass) \
|
||||
real(Int8Array, 22, js_InitTypedArrayClasses, TYPED_ARRAY_CLASP(TYPE_INT8)) \
|
||||
real(Uint8Array, 23, js_InitTypedArrayClasses, TYPED_ARRAY_CLASP(TYPE_UINT8)) \
|
||||
real(Int16Array, 24, js_InitTypedArrayClasses, TYPED_ARRAY_CLASP(TYPE_INT16)) \
|
||||
real(Uint16Array, 25, js_InitTypedArrayClasses, TYPED_ARRAY_CLASP(TYPE_UINT16)) \
|
||||
real(Int32Array, 26, js_InitTypedArrayClasses, TYPED_ARRAY_CLASP(TYPE_INT32)) \
|
||||
real(Uint32Array, 27, js_InitTypedArrayClasses, TYPED_ARRAY_CLASP(TYPE_UINT32)) \
|
||||
real(Float32Array, 28, js_InitTypedArrayClasses, TYPED_ARRAY_CLASP(TYPE_FLOAT32)) \
|
||||
real(Float64Array, 29, js_InitTypedArrayClasses, TYPED_ARRAY_CLASP(TYPE_FLOAT64)) \
|
||||
real(Uint8ClampedArray, 30, js_InitTypedArrayClasses, TYPED_ARRAY_CLASP(TYPE_UINT8_CLAMPED)) \
|
||||
real(Proxy, 31, js_InitProxyClass, &ProxyObject::uncallableClass_) \
|
||||
real(WeakMap, 32, js_InitWeakMapClass, OCLASP(WeakMap)) \
|
||||
real(Map, 33, js_InitMapClass, OCLASP(Map)) \
|
||||
real(Set, 34, js_InitSetClass, OCLASP(Set)) \
|
||||
real(DataView, 35, js_InitTypedArrayClasses, OCLASP(DataView)) \
|
||||
IF_PJS(real,imaginary) (ParallelArray, 36, js_InitParallelArrayClass, OCLASP(ParallelArray)) \
|
||||
IF_INTL(real,imaginary) (Intl, 37, js_InitIntlClass, CLASP(Intl)) \
|
||||
IF_BDATA(real,imaginary)(TypedObject, 38, js_InitTypedObjectClass, CLASP(TypedObject)) \
|
||||
imaginary(GeneratorFunction, 39, js_InitIteratorClasses, dummy) \
|
||||
|
||||
#define JS_FOR_EACH_PROTOTYPE(macro) JS_FOR_PROTOTYPES(macro,macro)
|
||||
|
||||
#endif /* jsprototypes_h */
|
||||
|
@ -3367,6 +3367,6 @@ js_InitProxyClass(JSContext *cx, HandleObject obj)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
global->markStandardClassInitializedNoProto(&ProxyObject::uncallableClass_);
|
||||
global->setConstructor(JSProto_Proxy, ObjectValue(*ctor));
|
||||
return ctor;
|
||||
}
|
||||
|
@ -73,7 +73,7 @@ typedef enum JSType {
|
||||
|
||||
/* Dense index into cached prototypes and class atoms for standard objects. */
|
||||
typedef enum JSProtoKey {
|
||||
#define PROTOKEY_AND_INITIALIZER(name,code,init) JSProto_##name = code,
|
||||
#define PROTOKEY_AND_INITIALIZER(name,code,init,clasp) JSProto_##name = code,
|
||||
JS_FOR_EACH_PROTOTYPE(PROTOKEY_AND_INITIALIZER)
|
||||
#undef PROTOKEY_AND_INITIALIZER
|
||||
JSProto_LIMIT
|
||||
|
@ -200,29 +200,22 @@ class GlobalObject : public JSObject
|
||||
/*
|
||||
* Lazy standard classes need a way to indicate they have been initialized.
|
||||
* Otherwise, when we delete them, we might accidentally recreate them via
|
||||
* a lazy initialization. We use the presence of a ctor or proto in the
|
||||
* global object's slot to indicate that they've been constructed, but this
|
||||
* only works for classes which have a proto and ctor. Classes which don't
|
||||
* have one can call markStandardClassInitializedNoProto(), and we can
|
||||
* always check whether a class is initialized by calling
|
||||
* isStandardClassResolved().
|
||||
* a lazy initialization. We use the presence of an object in the
|
||||
* getConstructor(key) reserved slot to indicate that they've been
|
||||
* initialized.
|
||||
*
|
||||
* Note: A few builtin objects, like JSON and Math, are not constructors,
|
||||
* so getConstructor is a bit of a misnomer.
|
||||
*/
|
||||
bool isStandardClassResolved(const js::Class *clasp) const {
|
||||
JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(clasp);
|
||||
|
||||
// If the constructor is undefined, then it hasn't been initialized.
|
||||
MOZ_ASSERT(getConstructor(key).isUndefined() ||
|
||||
getConstructor(key).isObject());
|
||||
return !getConstructor(key).isUndefined();
|
||||
}
|
||||
|
||||
void markStandardClassInitializedNoProto(const js::Class *clasp) {
|
||||
JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(clasp);
|
||||
|
||||
// We use true so that it's obvious what we're doing (instead of, say,
|
||||
// null, which might be miscontrued as an error in setting Undefined).
|
||||
if (getConstructor(key).isUndefined())
|
||||
setConstructor(key, BooleanValue(true));
|
||||
}
|
||||
|
||||
private:
|
||||
void setDetailsForKey(JSProtoKey key, JSObject *ctor, JSObject *proto) {
|
||||
JS_ASSERT(getConstructor(key).isUndefined());
|
||||
|
@ -446,7 +446,7 @@ struct JSAtomState
|
||||
#define PROPERTYNAME_FIELD(idpart, id, text) js::FixedHeapPtr<js::PropertyName> id;
|
||||
FOR_EACH_COMMON_PROPERTYNAME(PROPERTYNAME_FIELD)
|
||||
#undef PROPERTYNAME_FIELD
|
||||
#define PROPERTYNAME_FIELD(name, code, init) js::FixedHeapPtr<js::PropertyName> name;
|
||||
#define PROPERTYNAME_FIELD(name, code, init, clasp) js::FixedHeapPtr<js::PropertyName> name;
|
||||
JS_FOR_EACH_PROTOTYPE(PROPERTYNAME_FIELD)
|
||||
#undef PROPERTYNAME_FIELD
|
||||
};
|
||||
|
@ -269,7 +269,7 @@ SPSEntryMarker::~SPSEntryMarker()
|
||||
}
|
||||
|
||||
JS_FRIEND_API(jsbytecode*)
|
||||
ProfileEntry::pc() volatile
|
||||
ProfileEntry::pc() const volatile
|
||||
{
|
||||
JS_ASSERT_IF(idx != NullPCIndex, idx >= 0 && uint32_t(idx) < script()->length);
|
||||
return idx == NullPCIndex ? nullptr : script()->code + idx;
|
||||
|
@ -75,6 +75,7 @@ const char* const XPCJSRuntime::mStrings[] = {
|
||||
"__proto__", // IDX_PROTO
|
||||
"__iterator__", // IDX_ITERATOR
|
||||
"__exposedProps__", // IDX_EXPOSEDPROPS
|
||||
"eval", // IDX_EVAL
|
||||
};
|
||||
|
||||
/***************************************************************************/
|
||||
|
@ -652,6 +652,7 @@ public:
|
||||
IDX_PROTO ,
|
||||
IDX_ITERATOR ,
|
||||
IDX_EXPOSEDPROPS ,
|
||||
IDX_EVAL ,
|
||||
IDX_TOTAL_COUNT // just a count of the above
|
||||
};
|
||||
|
||||
|
@ -50,6 +50,7 @@ support-files =
|
||||
[test_bug860494.xul]
|
||||
[test_bug866823.xul]
|
||||
[test_bug895340.xul]
|
||||
[test_xrayToJS.xul]
|
||||
[test_chrometoSource.xul]
|
||||
[test_cows.xul]
|
||||
[test_documentdomain.xul]
|
||||
|
117
js/xpconnect/tests/chrome/test_xrayToJS.xul
Normal file
117
js/xpconnect/tests/chrome/test_xrayToJS.xul
Normal file
@ -0,0 +1,117 @@
|
||||
<?xml version="1.0"?>
|
||||
<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
|
||||
<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=933681
|
||||
-->
|
||||
<window title="Mozilla Bug 933681"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
|
||||
|
||||
<!-- test results are displayed in the html:body -->
|
||||
<body xmlns="http://www.w3.org/1999/xhtml">
|
||||
<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=933681"
|
||||
target="_blank">Mozilla Bug 933681</a>
|
||||
</body>
|
||||
|
||||
<!-- test code goes here -->
|
||||
<script type="application/javascript">
|
||||
<![CDATA[
|
||||
|
||||
/** Test for ES constructors on Xrayed globals. **/
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
const Cu = Components.utils;
|
||||
let global = Cu.getGlobalForObject.bind(Cu);
|
||||
|
||||
simpleConstructors = ['Object', 'Function', 'Array', 'Boolean', 'Date', 'Number',
|
||||
'String', 'RegExp', 'Error', 'InternalError', 'EvalError',
|
||||
'RangeError', 'ReferenceError', 'SyntaxError', 'TypeError',
|
||||
'URIError', 'ArrayBuffer', 'Int8Array', 'Uint8Array',
|
||||
'Int16Array', 'Uint16Array', 'Int32Array', 'Uint32Array',
|
||||
'Float32Array', 'Float64Array', 'Uint8ClampedArray',
|
||||
'WeakMap', 'Map', 'Set'];
|
||||
|
||||
function go() {
|
||||
var iwin = document.getElementById('ifr').contentWindow;
|
||||
|
||||
// Test constructors that can be instantiated with zero arguments.
|
||||
for (var c of simpleConstructors) {
|
||||
ok(iwin[c], "Constructors appear: " + c);
|
||||
is(iwin[c], Cu.unwaiveXrays(iwin.wrappedJSObject[c]),
|
||||
"we end up with the appropriate constructor: " + c);
|
||||
is(Cu.unwaiveXrays(Cu.waiveXrays(new iwin[c]).constructor), iwin[c],
|
||||
"constructor property is set up right: " + c);
|
||||
is(Object.getPrototypeOf(new iwin[c]),
|
||||
Cu.unwaiveXrays(Cu.waiveXrays(iwin[c]).prototype),
|
||||
"prototype is correct: " + c);
|
||||
is(global(new iwin[c]), iwin, "Got the right global: " + c);
|
||||
}
|
||||
|
||||
// Test Object in more detail.
|
||||
var num = new iwin.Object(4);
|
||||
is(num.valueOf(), 4, "primitive object construction works");
|
||||
is(global(num), iwin, "correct global for num");
|
||||
var obj = new iwin.Object();
|
||||
obj.foo = 2;
|
||||
var withProto = iwin.Object.create(obj);
|
||||
is(global(withProto), iwin, "correct global for withProto");
|
||||
is(withProto.foo, 2, "Inherits properly");
|
||||
|
||||
// Test Function.
|
||||
var primitiveFun = new iwin.Function('return 2');
|
||||
is(global(primitiveFun), iwin, "function construction works");
|
||||
is(primitiveFun(), 2, "basic function works");
|
||||
var doSetFoo = new iwin.Function('arg', 'arg.foo = 2;');
|
||||
is(global(doSetFoo), iwin, "function with args works");
|
||||
try {
|
||||
doSetFoo(new Object());
|
||||
ok(false, "should have thrown while setting property on object");
|
||||
} catch (e) {
|
||||
ok(!!/denied/.test(e), "Threw correctly: " + e);
|
||||
}
|
||||
var factoryFun = new iwin.Function('return {foo: 32}');
|
||||
is(global(factoryFun), iwin, "proper global for factoryFun");
|
||||
is(factoryFun().foo, 32, "factoryFun invokable");
|
||||
is(global(factoryFun()), iwin, "minted objects live in the content scope");
|
||||
|
||||
// Test interface objects that don't actually construct things.
|
||||
is(iwin.Math.tan(4.5), Math.tan(4.5), "Math.tan works");
|
||||
is(iwin.Math.E, Math.E, "Math.E works");
|
||||
var json = JSON.stringify({a: 2, b: 'hi', c: {d: 'there'}});
|
||||
is(global(iwin.JSON.parse(json)), iwin, "JSON rehydrated into the right context");
|
||||
is(iwin.JSON.stringify(iwin.JSON.parse(json)), json, "JSON composition identity holds");
|
||||
|
||||
// Test proxies.
|
||||
var targetObject = new iwin.Object();
|
||||
targetObject.foo = 9;
|
||||
var forwardingProxy = new iwin.Proxy(targetObject, new iwin.Object());
|
||||
is(global(forwardingProxy), iwin, "proxy global correct");
|
||||
is(forwardingProxy.foo, 9, "forwards correctly");
|
||||
// NB: COW-implemented proxy handlers are super dangerous, and we should not
|
||||
// encourage them.
|
||||
var handler = {get: function(target, name) { return name * 2; }, __exposedProps__: {get: 'r'}};
|
||||
var doublingProxy = new iwin.Proxy(targetObject, handler);
|
||||
is(global(doublingProxy), iwin, "doubling proxy global correct");
|
||||
is(doublingProxy[3], 6, "Doubles correctly");
|
||||
is(doublingProxy[20], 40, "Doubles correctly");
|
||||
|
||||
// Test eval.
|
||||
var toEval = "({a: 2, b: {foo: 'bar'}, f: function() { return window; }})";
|
||||
is(global(iwin.eval(toEval)), iwin, "eval creates objects in the correct global");
|
||||
is(iwin.eval(toEval).b.foo, 'bar', "eval-ed object looks right");
|
||||
is(iwin.eval(toEval).f(), iwin, "evaled function works right");
|
||||
|
||||
// We could also test DataView and Iterator here for completeness, but it's
|
||||
// more trouble than it's worth.
|
||||
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
]]>
|
||||
</script>
|
||||
<iframe id="ifr" onload="go();" src="http://example.org/tests/js/xpconnect/tests/mochitest/file_empty.html" />
|
||||
</window>
|
@ -807,12 +807,36 @@ XrayTraits::resolveOwnProperty(JSContext *cx, Wrapper &jsWrapper,
|
||||
|
||||
// Check for expando properties first. Note that the expando object lives
|
||||
// in the target compartment.
|
||||
bool found = false;
|
||||
if (expando) {
|
||||
JSAutoCompartment ac(cx, expando);
|
||||
if (!JS_GetPropertyDescriptorById(cx, expando, id, 0, desc))
|
||||
return false;
|
||||
found = !!desc.object();
|
||||
}
|
||||
if (desc.object()) {
|
||||
|
||||
// Next, check for ES builtins.
|
||||
if (!found && JS_IsGlobalObject(target)) {
|
||||
JSProtoKey key = JS_IdToProtoKey(cx, id);
|
||||
JSAutoCompartment ac(cx, target);
|
||||
if (key != JSProto_Null) {
|
||||
MOZ_ASSERT(key < JSProto_LIMIT);
|
||||
RootedObject constructor(cx);
|
||||
if (!JS_GetClassObject(cx, target, key, constructor.address()))
|
||||
return false;
|
||||
MOZ_ASSERT(constructor);
|
||||
desc.value().set(ObjectValue(*constructor));
|
||||
found = true;
|
||||
} else if (id == GetRTIdByIndex(cx, XPCJSRuntime::IDX_EVAL)) {
|
||||
RootedObject eval(cx);
|
||||
if (!js::GetOriginalEval(cx, target, &eval))
|
||||
return false;
|
||||
desc.value().set(ObjectValue(*eval));
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (found) {
|
||||
if (!JS_WrapPropertyDescriptor(cx, desc))
|
||||
return false;
|
||||
// Pretend the property lives on the wrapper.
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include "nsContentCreatorFunctions.h"
|
||||
#include "nsContentList.h"
|
||||
#include "nsStyleSet.h"
|
||||
#include "nsIDOMMutationEvent.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
|
@ -278,15 +278,12 @@ cprMoveMsgToQueue(cpr_msg_queue_t *msgq);
|
||||
cprMsgQueue_t
|
||||
cprCreateMessageQueue (const char *name, uint16_t depth)
|
||||
{
|
||||
static const char fname[] = "cprCreateMessageQueue";
|
||||
cpr_msg_queue_t *msgq;
|
||||
key_t key;
|
||||
static int key_id = 100; /* arbitrary starting number */
|
||||
struct msqid_ds buf;
|
||||
|
||||
msgq =(cpr_msg_queue_t *)cpr_calloc(1, sizeof(cpr_msg_queue_t));
|
||||
if (msgq == NULL) {
|
||||
CPR_ERROR("%s: Malloc failed: %s\n", fname,
|
||||
CPR_ERROR("%s: Malloc failed: %s\n", __FUNCTION__,
|
||||
name ? name : unnamed_string);
|
||||
errno = ENOMEM;
|
||||
return NULL;
|
||||
@ -294,45 +291,13 @@ cprCreateMessageQueue (const char *name, uint16_t depth)
|
||||
|
||||
msgq->name = name ? name : unnamed_string;
|
||||
|
||||
/*
|
||||
* Find a unique key
|
||||
*/
|
||||
key = ftok("/proc/self", key_id++);
|
||||
CSFLogDebug(logTag, "key = %x\n", key);
|
||||
|
||||
if (key == -1) {
|
||||
CPR_ERROR("%s: Key generation failed: %d\n", fname, errno);
|
||||
cpr_free(msgq);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set creation flag so that OS will create the message queue
|
||||
*/
|
||||
msgq->queueId = msgget(key, (IPC_EXCL | IPC_CREAT | 0666));
|
||||
if (msgq->queueId == -1) {
|
||||
if (errno == EEXIST) {
|
||||
CSFLogDebug(logTag, "Q exists so first remove it and then create again\n");
|
||||
/* Remove message queue */
|
||||
msgq->queueId = msgget(key, (IPC_CREAT | 0666));
|
||||
if (msgctl(msgq->queueId, IPC_RMID, &buf) == -1) {
|
||||
|
||||
CPR_ERROR("%s: Destruction failed: %s: %d\n", fname,
|
||||
msgq->name, errno);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
msgq->queueId = msgget(key, (IPC_CREAT | 0666));
|
||||
}
|
||||
} else {
|
||||
CSFLogDebug(logTag, "there was no preexisting q..\n");
|
||||
|
||||
}
|
||||
|
||||
|
||||
msgq->queueId = msgget(IPC_PRIVATE, (IPC_EXCL | IPC_CREAT | 0666));
|
||||
|
||||
if (msgq->queueId == -1) {
|
||||
CPR_ERROR("%s: Creation failed: %s: %d\n", fname, name, errno);
|
||||
CPR_ERROR("%s: Creation failed: %s: %d\n", __FUNCTION__, name, errno);
|
||||
if (errno == EEXIST) {
|
||||
|
||||
}
|
||||
@ -349,7 +314,7 @@ cprCreateMessageQueue (const char *name, uint16_t depth)
|
||||
*/
|
||||
if (pthread_mutex_init(&msgq->mutex, NULL) != 0) {
|
||||
CPR_ERROR("%s: Failed to create msg queue (%s) mutex: %d\n",
|
||||
fname, name, errno);
|
||||
__FUNCTION__, name, errno);
|
||||
(void) msgctl(msgq->queueId, IPC_RMID, &buf);
|
||||
cpr_free(msgq);
|
||||
return NULL;
|
||||
@ -359,14 +324,14 @@ cprCreateMessageQueue (const char *name, uint16_t depth)
|
||||
* Set the extended message queue depth (within bounds)
|
||||
*/
|
||||
if (depth > CPR_MAX_MSG_Q_DEPTH) {
|
||||
CPR_INFO("%s: Depth too large (%d) reset to %d\n", fname, depth,
|
||||
CPR_INFO("%s: Depth too large (%d) reset to %d\n", __FUNCTION__, depth,
|
||||
CPR_MAX_MSG_Q_DEPTH);
|
||||
depth = CPR_MAX_MSG_Q_DEPTH;
|
||||
}
|
||||
|
||||
if (depth < OS_MSGTQL) {
|
||||
if (depth) {
|
||||
CPR_INFO("%s: Depth too small (%d) reset to %d\n", fname, depth, OS_MSGTQL);
|
||||
CPR_INFO("%s: Depth too small (%d) reset to %d\n", __FUNCTION__, depth, OS_MSGTQL);
|
||||
}
|
||||
depth = OS_MSGTQL;
|
||||
}
|
||||
|
@ -71,7 +71,7 @@ DeviceInfoDS* DeviceInfoDS::Create(const int32_t id)
|
||||
}
|
||||
|
||||
DeviceInfoDS::DeviceInfoDS(const int32_t id)
|
||||
: DeviceInfoImpl(id), _dsDevEnum(NULL), _dsMonikerDevEnum(NULL),
|
||||
: DeviceInfoImpl(id), _dsDevEnum(NULL),
|
||||
_CoUninitializeIsRequired(true)
|
||||
{
|
||||
// 1) Initialize the COM library (make Windows load the DLLs).
|
||||
@ -116,7 +116,6 @@ DeviceInfoDS::DeviceInfoDS(const int32_t id)
|
||||
|
||||
DeviceInfoDS::~DeviceInfoDS()
|
||||
{
|
||||
RELEASE_AND_CLEAR(_dsMonikerDevEnum);
|
||||
RELEASE_AND_CLEAR(_dsDevEnum);
|
||||
if (_CoUninitializeIsRequired)
|
||||
{
|
||||
@ -173,7 +172,7 @@ int32_t DeviceInfoDS::GetDeviceInfo(
|
||||
{
|
||||
|
||||
// enumerate all video capture devices
|
||||
RELEASE_AND_CLEAR(_dsMonikerDevEnum);
|
||||
IEnumMoniker* _dsMonikerDevEnum = NULL;
|
||||
HRESULT hr =
|
||||
_dsDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory,
|
||||
&_dsMonikerDevEnum, 0);
|
||||
@ -182,6 +181,7 @@ int32_t DeviceInfoDS::GetDeviceInfo(
|
||||
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
|
||||
"Failed to enumerate CLSID_SystemDeviceEnum, error 0x%x."
|
||||
" No webcam exist?", hr);
|
||||
RELEASE_AND_CLEAR(_dsMonikerDevEnum);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -227,6 +227,7 @@ int32_t DeviceInfoDS::GetDeviceInfo(
|
||||
webrtc::kTraceVideoCapture, _id,
|
||||
"Failed to convert device name to UTF8. %d",
|
||||
GetLastError());
|
||||
RELEASE_AND_CLEAR(_dsMonikerDevEnum);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@ -258,6 +259,7 @@ int32_t DeviceInfoDS::GetDeviceInfo(
|
||||
webrtc::kTraceVideoCapture, _id,
|
||||
"Failed to convert device name to UTF8. %d",
|
||||
GetLastError());
|
||||
RELEASE_AND_CLEAR(_dsMonikerDevEnum);
|
||||
return -1;
|
||||
}
|
||||
if (productUniqueIdUTF8
|
||||
@ -285,6 +287,7 @@ int32_t DeviceInfoDS::GetDeviceInfo(
|
||||
WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, _id, "%s %s",
|
||||
__FUNCTION__, deviceNameUTF8);
|
||||
}
|
||||
RELEASE_AND_CLEAR(_dsMonikerDevEnum);
|
||||
return index;
|
||||
}
|
||||
|
||||
@ -303,8 +306,8 @@ IBaseFilter * DeviceInfoDS::GetDeviceFilter(
|
||||
return NULL;
|
||||
}
|
||||
|
||||
IEnumMoniker* _dsMonikerDevEnum = NULL;
|
||||
// enumerate all video capture devices
|
||||
RELEASE_AND_CLEAR(_dsMonikerDevEnum);
|
||||
HRESULT hr = _dsDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory,
|
||||
&_dsMonikerDevEnum, 0);
|
||||
if (hr != NOERROR)
|
||||
@ -312,6 +315,7 @@ IBaseFilter * DeviceInfoDS::GetDeviceFilter(
|
||||
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
|
||||
"Failed to enumerate CLSID_SystemDeviceEnum, error 0x%x."
|
||||
" No webcam exist?", hr);
|
||||
RELEASE_AND_CLEAR(_dsMonikerDevEnum);
|
||||
return 0;
|
||||
}
|
||||
_dsMonikerDevEnum->Reset();
|
||||
@ -379,6 +383,7 @@ IBaseFilter * DeviceInfoDS::GetDeviceFilter(
|
||||
pM->Release();
|
||||
}
|
||||
}
|
||||
RELEASE_AND_CLEAR(_dsMonikerDevEnum);
|
||||
return captureFilter;
|
||||
}
|
||||
|
||||
|
@ -97,7 +97,6 @@ protected:
|
||||
|
||||
private:
|
||||
ICreateDevEnum* _dsDevEnum;
|
||||
IEnumMoniker* _dsMonikerDevEnum;
|
||||
bool _CoUninitializeIsRequired;
|
||||
|
||||
};
|
||||
|
@ -635,27 +635,35 @@ nsHostResolver::ResolveHost(const char *host,
|
||||
unspecHe->rec->HasUsableResult(flags) &&
|
||||
TimeStamp::NowLoRes() <= (he->rec->expiration +
|
||||
TimeDuration::FromSeconds(mGracePeriod * 60))) {
|
||||
|
||||
MOZ_ASSERT(unspecHe->rec->addr_info || unspecHe->rec->negative,
|
||||
"Entry should be resolved or negative.");
|
||||
|
||||
LOG((" Trying AF_UNSPEC entry for [%s] af: %s.\n",
|
||||
host, (af == PR_AF_INET) ? "AF_INET" : "AF_INET6"));
|
||||
|
||||
// Search for any valid address in the AF_UNSPEC entry
|
||||
// in the cache (not blacklisted and from the right
|
||||
// family).
|
||||
NetAddrElement *addrIter =
|
||||
unspecHe->rec->addr_info->mAddresses.getFirst();
|
||||
he->rec->addr_info = nullptr;
|
||||
while (addrIter) {
|
||||
if ((af == addrIter->mAddress.inet.family) &&
|
||||
!unspecHe->rec->Blacklisted(&addrIter->mAddress)) {
|
||||
if (!he->rec->addr_info) {
|
||||
he->rec->addr_info = new AddrInfo(
|
||||
unspecHe->rec->addr_info->mHostName,
|
||||
unspecHe->rec->addr_info->mCanonicalName);
|
||||
if (unspecHe->rec->negative) {
|
||||
he->rec->negative = unspecHe->rec->negative;
|
||||
} else if (he->rec->addr_info) {
|
||||
// Search for any valid address in the AF_UNSPEC entry
|
||||
// in the cache (not blacklisted and from the right
|
||||
// family).
|
||||
NetAddrElement *addrIter =
|
||||
unspecHe->rec->addr_info->mAddresses.getFirst();
|
||||
while (addrIter) {
|
||||
if ((af == addrIter->mAddress.inet.family) &&
|
||||
!unspecHe->rec->Blacklisted(&addrIter->mAddress)) {
|
||||
if (!he->rec->addr_info) {
|
||||
he->rec->addr_info = new AddrInfo(
|
||||
unspecHe->rec->addr_info->mHostName,
|
||||
unspecHe->rec->addr_info->mCanonicalName);
|
||||
}
|
||||
he->rec->addr_info->AddAddress(
|
||||
new NetAddrElement(*addrIter));
|
||||
}
|
||||
he->rec->addr_info->AddAddress(
|
||||
new NetAddrElement(*addrIter));
|
||||
addrIter = addrIter->getNext();
|
||||
}
|
||||
addrIter = addrIter->getNext();
|
||||
}
|
||||
if (he->rec->HasUsableResult(flags)) {
|
||||
result = he->rec;
|
||||
@ -976,7 +984,7 @@ nsHostResolver::OnLookupComplete(nsHostRecord *rec, nsresult status, AddrInfo *r
|
||||
rec->usingAnyThread = false;
|
||||
}
|
||||
|
||||
if (rec->addr_info && !mShutdown) {
|
||||
if (!mShutdown) {
|
||||
// add to mEvictionQ
|
||||
PR_APPEND_LINK(rec, &mEvictionQ);
|
||||
NS_ADDREF(rec);
|
||||
@ -1170,7 +1178,8 @@ CacheEntryEnumerator(PLDHashTable *table, PLDHashEntryHdr *entry,
|
||||
// We don't pay attention to address literals, only resolved domains.
|
||||
// Also require a host.
|
||||
nsHostRecord *rec = static_cast<nsHostDBEnt*>(entry)->rec;
|
||||
if (!rec->addr_info || !rec->host) {
|
||||
MOZ_ASSERT(rec, "rec should never be null here!");
|
||||
if (!rec || !rec->addr_info || !rec->host) {
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
|
7
security/manager/ssl/tests/unit/tlsserver/generate_certs.sh
Normal file → Executable file
7
security/manager/ssl/tests/unit/tlsserver/generate_certs.sh
Normal file → Executable file
@ -27,7 +27,8 @@ RUN_MOZILLA="$OBJDIR/dist/bin/run-mozilla.sh"
|
||||
CERTUTIL="$OBJDIR/dist/bin/certutil"
|
||||
|
||||
NOISE_FILE=`mktemp`
|
||||
dd if=/dev/urandom of="$NOISE_FILE" bs=1024 count=1
|
||||
# Make a good effort at putting something unique in the noise file.
|
||||
date +%s%N > "$NOISE_FILE"
|
||||
PASSWORD_FILE=`mktemp`
|
||||
|
||||
function cleanup {
|
||||
@ -35,8 +36,8 @@ function cleanup {
|
||||
}
|
||||
|
||||
if [ ! -f "$RUN_MOZILLA" ]; then
|
||||
echo "Could not find run-mozilla.sh at \'$RUN_MOZILLA\'"
|
||||
exit $E_BADARGS
|
||||
echo "Could not find run-mozilla.sh at \'$RUN_MOZILLA\' - I'll try without it"
|
||||
RUN_MOZILLA=""
|
||||
fi
|
||||
|
||||
if [ ! -f "$CERTUTIL" ]; then
|
||||
|
@ -49,7 +49,9 @@ NS_IMPL_CI_INTERFACE_GETTER4(
|
||||
class AsyncStatementClassInfo : public nsIClassInfo
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
MOZ_CONSTEXPR AsyncStatementClassInfo() {}
|
||||
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
||||
NS_IMETHODIMP
|
||||
GetInterfaces(uint32_t *_count, nsIID ***_array)
|
||||
|
@ -51,7 +51,9 @@ NS_IMPL_CI_INTERFACE_GETTER5(
|
||||
class StatementClassInfo : public nsIClassInfo
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
MOZ_CONSTEXPR StatementClassInfo() {}
|
||||
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
||||
NS_IMETHODIMP
|
||||
GetInterfaces(uint32_t *_count, nsIID ***_array)
|
||||
|
@ -1,3 +1,4 @@
|
||||
/* -*- js-indent-level: 2; tab-width: 2; indent-tabs-mode: nil -*- */
|
||||
// Test timeout (seconds)
|
||||
var gTimeoutSeconds = 30;
|
||||
var gConfig;
|
||||
@ -364,7 +365,12 @@ Tester.prototype = {
|
||||
.getService(Ci.nsIXULRuntime)
|
||||
.processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT)
|
||||
{
|
||||
this.MemoryStats.dump((l) => { this.dumper.dump(l + "\n"); });
|
||||
this.MemoryStats.dump((l) => { this.dumper.dump(l + "\n"); },
|
||||
this.currentTestIndex,
|
||||
this.currentTest.path,
|
||||
gConfig.dumpOutputDirectory,
|
||||
gConfig.dumpAboutMemoryAfterTest,
|
||||
gConfig.dumpDMDAfterTest);
|
||||
}
|
||||
|
||||
// Note the test run time
|
||||
|
@ -190,7 +190,8 @@ class MochitestRunner(MozbuildObject):
|
||||
no_autorun=False, repeat=0, run_until_failure=False, slow=False,
|
||||
chunk_by_dir=0, total_chunks=None, this_chunk=None, jsdebugger=False,
|
||||
debug_on_failure=False, start_at=None, end_at=None, e10s=False,
|
||||
dmd=False):
|
||||
dmd=False, dump_output_directory=None, dump_about_memory_after_test=False,
|
||||
dump_dmd_after_test=False):
|
||||
"""Runs a mochitest.
|
||||
|
||||
test_file is a path to a test file. It can be a relative path from the
|
||||
@ -298,6 +299,9 @@ class MochitestRunner(MozbuildObject):
|
||||
options.startAt = start_at
|
||||
options.endAt = end_at
|
||||
options.e10s = e10s
|
||||
options.dumpAboutMemoryAfterTest = dump_about_memory_after_test
|
||||
options.dumpDMDAfterTest = dump_dmd_after_test
|
||||
options.dumpOutputDirectory = dump_output_directory
|
||||
mozinfo.update({"e10s": e10s}) # for test manifest parsing.
|
||||
|
||||
options.failureFile = failure_file_path
|
||||
@ -443,6 +447,18 @@ def MochitestCommand(func):
|
||||
help='Run tests with DMD active.')
|
||||
func = dmd(func)
|
||||
|
||||
dumpAboutMemory = CommandArgument('--dump-about-memory-after-test', action='store_true',
|
||||
help='Dump an about:memory log after every test.')
|
||||
func = dumpAboutMemory(func)
|
||||
|
||||
dumpDMD = CommandArgument('--dump-dmd-after-test', action='store_true',
|
||||
help='Dump a DMD log after every test.')
|
||||
func = dumpDMD(func)
|
||||
|
||||
dumpOutputDirectory = CommandArgument('--dump-output-directory', action='store',
|
||||
help='Specifies the directory in which to place dumped memory reports.')
|
||||
func = dumpOutputDirectory(func)
|
||||
|
||||
path = CommandArgument('test_file', default=None, nargs='?',
|
||||
metavar='TEST',
|
||||
help='Test to run. Can be specified as a single file, a ' \
|
||||
|
@ -357,6 +357,26 @@ class MochitestOptions(optparse.OptionParser):
|
||||
"dest": "dmdPath",
|
||||
"help": "Specifies the path to the directory containing the shared library for DMD.",
|
||||
}],
|
||||
[["--dump-output-directory"],
|
||||
{ "action": "store",
|
||||
"default": None,
|
||||
"dest": "dumpOutputDirectory",
|
||||
"help": "Specifies the directory in which to place dumped memory reports.",
|
||||
}],
|
||||
[["--dump-about-memory-after-test"],
|
||||
{ "action": "store_true",
|
||||
"default": False,
|
||||
"dest": "dumpAboutMemoryAfterTest",
|
||||
"help": "Produce an about:memory dump after each test in the directory specified "
|
||||
"by --dump-output-directory."
|
||||
}],
|
||||
[["--dump-dmd-after-test"],
|
||||
{ "action": "store_true",
|
||||
"default": False,
|
||||
"dest": "dumpDMDAfterTest",
|
||||
"help": "Produce a DMD dump after each test in the directory specified "
|
||||
"by --dump-output-directory."
|
||||
}],
|
||||
]
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
@ -505,6 +525,14 @@ class MochitestOptions(optparse.OptionParser):
|
||||
if not options.repeat:
|
||||
options.repeat = 29
|
||||
|
||||
if options.dumpOutputDirectory is None:
|
||||
options.dumpOutputDirectory = tempfile.gettempdir()
|
||||
|
||||
if options.dumpAboutMemoryAfterTest or options.dumpDMDAfterTest:
|
||||
if not os.path.isdir(options.dumpOutputDirectory):
|
||||
self.error('--dump-output-directory not a directory: %s' %
|
||||
options.dumpOutputDirectory)
|
||||
|
||||
return options
|
||||
|
||||
|
||||
|
@ -344,6 +344,12 @@ class MochitestUtilsMixin(object):
|
||||
self.urlOpts.append("runSlower=true")
|
||||
if options.debugOnFailure:
|
||||
self.urlOpts.append("debugOnFailure=true")
|
||||
if options.dumpOutputDirectory:
|
||||
self.urlOpts.append("dumpOutputDirectory=%s" % encodeURIComponent(options.dumpOutputDirectory))
|
||||
if options.dumpAboutMemoryAfterTest:
|
||||
self.urlOpts.append("dumpAboutMemoryAfterTest=true")
|
||||
if options.dumpDMDAfterTest:
|
||||
self.urlOpts.append("dumpDMDAfterTest=true")
|
||||
|
||||
def buildTestPath(self, options):
|
||||
""" Build the url path to the specific test harness and test file or directory
|
||||
|
@ -212,6 +212,9 @@ class RemoteOptions(MochitestOptions):
|
||||
tempPort = options.httpPort
|
||||
tempSSL = options.sslPort
|
||||
tempIP = options.webServer
|
||||
# We are going to override this option later anyway, just pretend
|
||||
# like it's not set for verification purposes.
|
||||
options.dumpOutputDirectory = None
|
||||
options = MochitestOptions.verifyOptions(self, options, mochitest)
|
||||
options.webServer = tempIP
|
||||
options.app = temp
|
||||
@ -588,6 +591,8 @@ def main():
|
||||
dm.pushFile(os.path.join(options.dmdPath, dmdLibrary), dmdPathOnDevice)
|
||||
options.dmdPath = deviceRoot
|
||||
|
||||
options.dumpOutputDirectory = deviceRoot
|
||||
|
||||
procName = options.app.split('/')[-1]
|
||||
if (dm.processExist(procName)):
|
||||
dm.killProcess(procName)
|
||||
|
@ -1,3 +1,9 @@
|
||||
/* -*- js-indent-level: 4; tab-width: 4; indent-tabs-mode: nil -*- */
|
||||
/* vim:set ts=4 sw=4 sts=4 et: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
var MemoryStats = {};
|
||||
|
||||
/**
|
||||
@ -18,15 +24,25 @@ MemoryStats._hasMemoryStatistics.vsizeMaxContiguous = MEM_STAT_UNKNOWN;
|
||||
MemoryStats._hasMemoryStatistics.residentFast = MEM_STAT_UNKNOWN;
|
||||
MemoryStats._hasMemoryStatistics.heapAllocated = MEM_STAT_UNKNOWN;
|
||||
|
||||
MemoryStats.dump = function (dumpFn) {
|
||||
var mrm;
|
||||
MemoryStats._getService = function (className, interfaceName) {
|
||||
var service;
|
||||
try {
|
||||
mrm = Cc["@mozilla.org/memory-reporter-manager;1"]
|
||||
.getService(Ci.nsIMemoryReporterManager);
|
||||
service = Cc[className].getService(Ci[interfaceName]);
|
||||
} catch (e) {
|
||||
mrm = SpecialPowers.Cc["@mozilla.org/memory-reporter-manager;1"]
|
||||
.getService(SpecialPowers.Ci.nsIMemoryReporterManager);
|
||||
service = SpecialPowers.Cc[className]
|
||||
.getService(SpecialPowers.Ci[interfaceName]);
|
||||
}
|
||||
return service;
|
||||
}
|
||||
|
||||
MemoryStats.dump = function (dumpFn,
|
||||
testNumber,
|
||||
testURL,
|
||||
dumpOutputDirectory,
|
||||
dumpAboutMemory,
|
||||
dumpDMD) {
|
||||
var mrm = MemoryStats._getService("@mozilla.org/memory-reporter-manager;1",
|
||||
"nsIMemoryReporterManager");
|
||||
for (var stat in MemoryStats._hasMemoryStatistics) {
|
||||
var supported = MemoryStats._hasMemoryStatistics[stat];
|
||||
var firstAccess = false;
|
||||
@ -46,4 +62,21 @@ MemoryStats.dump = function (dumpFn) {
|
||||
dumpFn("TEST-INFO | MEMORY STAT " + stat + " not supported in this build configuration.");
|
||||
}
|
||||
}
|
||||
|
||||
if (dumpAboutMemory) {
|
||||
var dumpfile = dumpOutputDirectory + "/about-memory-" + testNumber + ".json.gz";
|
||||
dumpFn("TEST-INFO | " + testURL + " | MEMDUMP-START " + dumpfile);
|
||||
var md = MemoryStats._getService("@mozilla.org/memory-info-dumper;1",
|
||||
"nsIMemoryInfoDumper");
|
||||
md.dumpMemoryReportsToNamedFile(dumpfile, function () {
|
||||
dumpFn("TEST-INFO | " + testURL + " | MEMDUMP-END");
|
||||
}, null);
|
||||
|
||||
}
|
||||
|
||||
if (dumpDMD && typeof(DMDReportAndDump) != undefined) {
|
||||
var dumpfile = dumpOutputDirectory + "/dmd-" + testNumber + ".txt";
|
||||
dumpFn("TEST-INFO | " + testURL + " | DMD-DUMP " + dumpfile);
|
||||
DMDReportAndDump(dumpfile);
|
||||
}
|
||||
};
|
||||
|
@ -1,3 +1,4 @@
|
||||
/* -*- js-indent-level: 4 -*- */
|
||||
/*
|
||||
* e10s event dispatcher from content->chrome
|
||||
*
|
||||
@ -84,6 +85,9 @@ TestRunner._expectedMaxAsserts = 0;
|
||||
TestRunner.timeout = 5 * 60 * 1000; // 5 minutes.
|
||||
TestRunner.maxTimeouts = 4; // halt testing after too many timeouts
|
||||
TestRunner.runSlower = false;
|
||||
TestRunner.dumpOutputDirectory = "";
|
||||
TestRunner.dumpAboutMemoryAfterTest = false;
|
||||
TestRunner.dumpDMDAfterTest = false;
|
||||
TestRunner.slowestTestTime = 0;
|
||||
TestRunner.slowestTestURL = "";
|
||||
|
||||
@ -409,7 +413,11 @@ TestRunner.testFinished = function(tests) {
|
||||
TestRunner._lastTestFinished = TestRunner._currentTest;
|
||||
TestRunner._loopIsRestarting = false;
|
||||
|
||||
MemoryStats.dump(TestRunner.log);
|
||||
MemoryStats.dump(TestRunner.log, TestRunner._currentTest,
|
||||
TestRunner.currentTestURL,
|
||||
TestRunner.dumpOutputDirectory,
|
||||
TestRunner.dumpAboutMemoryAfterTest,
|
||||
TestRunner.dumpDMDAfterTest);
|
||||
|
||||
function cleanUpCrashDumpFiles() {
|
||||
if (!SpecialPowers.removeExpectedCrashDumpFiles(TestRunner._expectingProcessCrash)) {
|
||||
|
@ -131,6 +131,18 @@ if (params.runSlower) {
|
||||
TestRunner.runSlower = true;
|
||||
}
|
||||
|
||||
if (params.dumpOutputDirectory) {
|
||||
TestRunner.dumpOutputDirectory = params.dumpOutputDirectory;
|
||||
}
|
||||
|
||||
if (params.dumpAboutMemoryAfterTest) {
|
||||
TestRunner.dumpAboutMemoryAfterTest = true;
|
||||
}
|
||||
|
||||
if (params.dumpDMDAfterTest) {
|
||||
TestRunner.dumpDMDAfterTest = true;
|
||||
}
|
||||
|
||||
var gTestList = [];
|
||||
var RunSet = {}
|
||||
RunSet.runall = function(e) {
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/DebugOnly.h"
|
||||
#include "mozilla/Likely.h"
|
||||
#include "mozilla/MathAlgorithms.h"
|
||||
|
||||
#include "base/histogram.h"
|
||||
#include "base/pickle.h"
|
||||
@ -45,6 +46,8 @@
|
||||
#include "nsNetUtil.h"
|
||||
#include "plstr.h"
|
||||
#include "nsAppDirectoryServiceDefs.h"
|
||||
#include "mozilla/BackgroundHangMonitor.h"
|
||||
#include "mozilla/ThreadHangStats.h"
|
||||
#include "mozilla/ProcessedStack.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
#include "mozilla/FileUtils.h"
|
||||
@ -247,6 +250,7 @@ public:
|
||||
static void RecordChromeHang(uint32_t duration,
|
||||
Telemetry::ProcessedStack &aStack);
|
||||
#endif
|
||||
static void RecordThreadHangStats(Telemetry::ThreadHangStats& aStats);
|
||||
static nsresult GetHistogramEnumId(const char *name, Telemetry::ID *id);
|
||||
static int64_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf);
|
||||
struct Stat {
|
||||
@ -319,6 +323,9 @@ private:
|
||||
Mutex mHashMutex;
|
||||
HangReports mHangReports;
|
||||
Mutex mHangReportsMutex;
|
||||
// mThreadHangStats stores recorded, inactive thread hang stats
|
||||
Vector<Telemetry::ThreadHangStats> mThreadHangStats;
|
||||
Mutex mThreadHangStatsMutex;
|
||||
nsCOMPtr<nsIMemoryReporter> mReporter;
|
||||
|
||||
CombinedStacks mLateWritesStacks; // This is collected out of the main thread.
|
||||
@ -342,6 +349,7 @@ TelemetryImpl::SizeOfIncludingThisHelper(mozilla::MallocSizeOf aMallocSizeOf)
|
||||
n += mSanitizedSQL.SizeOfExcludingThis(nullptr, aMallocSizeOf);
|
||||
n += mTrackedDBs.SizeOfExcludingThis(nullptr, aMallocSizeOf);
|
||||
n += mHangReports.SizeOfExcludingThis();
|
||||
n += mThreadHangStats.sizeOfExcludingThis(aMallocSizeOf);
|
||||
return n;
|
||||
}
|
||||
|
||||
@ -940,6 +948,7 @@ mHistogramMap(Telemetry::HistogramCount),
|
||||
mCanRecord(XRE_GetProcessType() == GeckoProcessType_Default),
|
||||
mHashMutex("Telemetry::mHashMutex"),
|
||||
mHangReportsMutex("Telemetry::mHangReportsMutex"),
|
||||
mThreadHangStatsMutex("Telemetry::mThreadHangStatsMutex"),
|
||||
mCachedTelemetryData(false),
|
||||
mLastShutdownTime(0),
|
||||
mFailedLockCount(0)
|
||||
@ -1705,6 +1714,180 @@ ReadStack(const char *aFileName, Telemetry::ProcessedStack &aStack)
|
||||
aStack = stack;
|
||||
}
|
||||
|
||||
static JSObject*
|
||||
CreateJSTimeHistogram(JSContext* cx, const Telemetry::TimeHistogram& time)
|
||||
{
|
||||
/* Create JS representation of TimeHistogram,
|
||||
in the format of Chromium-style histograms. */
|
||||
JS::RootedObject ret(cx, JS_NewObject(cx, nullptr, nullptr, nullptr));
|
||||
if (!ret) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!JS_DefineProperty(cx, ret, "min",
|
||||
UINT_TO_JSVAL(time.GetBucketMin(0)),
|
||||
nullptr, nullptr, JSPROP_ENUMERATE) ||
|
||||
!JS_DefineProperty(cx, ret, "max",
|
||||
UINT_TO_JSVAL(time.GetBucketMax(
|
||||
ArrayLength(time) - 1)),
|
||||
nullptr, nullptr, JSPROP_ENUMERATE) ||
|
||||
!JS_DefineProperty(cx, ret, "histogram_type",
|
||||
INT_TO_JSVAL(nsITelemetry::HISTOGRAM_EXPONENTIAL),
|
||||
nullptr, nullptr, JSPROP_ENUMERATE)) {
|
||||
return nullptr;
|
||||
}
|
||||
// TODO: calculate "sum", "log_sum", and "log_sum_squares"
|
||||
if (!JS_DefineProperty(cx, ret, "sum", INT_TO_JSVAL(0),
|
||||
nullptr, nullptr, JSPROP_ENUMERATE) ||
|
||||
!JS_DefineProperty(cx, ret, "log_sum", DOUBLE_TO_JSVAL(0.0),
|
||||
nullptr, nullptr, JSPROP_ENUMERATE) ||
|
||||
!JS_DefineProperty(cx, ret, "log_sum_squares", DOUBLE_TO_JSVAL(0.0),
|
||||
nullptr, nullptr, JSPROP_ENUMERATE)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
JS::RootedObject ranges(
|
||||
cx, JS_NewArrayObject(cx, ArrayLength(time) + 1, nullptr));
|
||||
JS::RootedObject counts(
|
||||
cx, JS_NewArrayObject(cx, ArrayLength(time) + 1, nullptr));
|
||||
if (!ranges || !counts) {
|
||||
return nullptr;
|
||||
}
|
||||
/* In a Chromium-style histogram, the first bucket is an "under" bucket
|
||||
that represents all values below the histogram's range. */
|
||||
JS::RootedValue underRange(cx, INT_TO_JSVAL(time.GetBucketMin(0)));
|
||||
JS::RootedValue underCount(cx, INT_TO_JSVAL(0));
|
||||
if (!JS_SetElement(cx, ranges, 0, &underRange) ||
|
||||
!JS_SetElement(cx, counts, 0, &underCount)) {
|
||||
return nullptr;
|
||||
}
|
||||
for (size_t i = 0; i < ArrayLength(time); i++) {
|
||||
JS::RootedValue range(cx, UINT_TO_JSVAL(time.GetBucketMax(i)));
|
||||
JS::RootedValue count(cx, UINT_TO_JSVAL(time[i]));
|
||||
if (!JS_SetElement(cx, ranges, i + 1, &range) ||
|
||||
!JS_SetElement(cx, counts, i + 1, &count)) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
if (!JS_DefineProperty(cx, ret, "ranges", OBJECT_TO_JSVAL(ranges),
|
||||
nullptr, nullptr, JSPROP_ENUMERATE) ||
|
||||
!JS_DefineProperty(cx, ret, "counts", OBJECT_TO_JSVAL(counts),
|
||||
nullptr, nullptr, JSPROP_ENUMERATE)) {
|
||||
return nullptr;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static JSObject*
|
||||
CreateJSHangHistogram(JSContext* cx, const Telemetry::HangHistogram& hang)
|
||||
{
|
||||
JS::RootedObject ret(cx, JS_NewObject(cx, nullptr, nullptr, nullptr));
|
||||
if (!ret) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const Telemetry::HangHistogram::Stack& hangStack = hang.GetStack();
|
||||
JS::RootedObject stack(cx,
|
||||
JS_NewArrayObject(cx, hangStack.length(), nullptr));
|
||||
if (!ret) {
|
||||
return nullptr;
|
||||
}
|
||||
for (size_t i = 0; i < hangStack.length(); i++) {
|
||||
JS::RootedString string(cx, JS_NewStringCopyZ(cx, hangStack[i]));
|
||||
JS::RootedValue frame(cx, STRING_TO_JSVAL(string));
|
||||
if (!JS_SetElement(cx, stack, i, &frame)) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
JS::RootedObject time(cx, CreateJSTimeHistogram(cx, hang));
|
||||
if (!time ||
|
||||
!JS_DefineProperty(cx, ret, "stack", OBJECT_TO_JSVAL(stack),
|
||||
nullptr, nullptr, JSPROP_ENUMERATE) ||
|
||||
!JS_DefineProperty(cx, ret, "histogram", OBJECT_TO_JSVAL(time),
|
||||
nullptr, nullptr, JSPROP_ENUMERATE)) {
|
||||
return nullptr;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static JSObject*
|
||||
CreateJSThreadHangStats(JSContext* cx, const Telemetry::ThreadHangStats& thread)
|
||||
{
|
||||
JS::RootedObject ret(cx, JS_NewObject(cx, nullptr, nullptr, nullptr));
|
||||
if (!ret) {
|
||||
return nullptr;
|
||||
}
|
||||
JS::RootedString name(cx, JS_NewStringCopyZ(cx, thread.GetName()));
|
||||
if (!name ||
|
||||
!JS_DefineProperty(cx, ret, "name", STRING_TO_JSVAL(name),
|
||||
nullptr, nullptr, JSPROP_ENUMERATE)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
JS::RootedObject activity(cx, CreateJSTimeHistogram(cx, thread.mActivity));
|
||||
if (!activity ||
|
||||
!JS_DefineProperty(cx, ret, "activity", OBJECT_TO_JSVAL(activity),
|
||||
nullptr, nullptr, JSPROP_ENUMERATE)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
JS::RootedObject hangs(cx, JS_NewArrayObject(cx, 0, nullptr));
|
||||
if (!hangs) {
|
||||
return nullptr;
|
||||
}
|
||||
for (size_t i = 0; i < thread.mHangs.length(); i++) {
|
||||
JS::RootedObject obj(cx, CreateJSHangHistogram(cx, thread.mHangs[i]));
|
||||
JS::RootedValue hang(cx, OBJECT_TO_JSVAL(obj));
|
||||
if (!JS_SetElement(cx, hangs, i, &hang)) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
if (!JS_DefineProperty(cx, ret, "hangs", OBJECT_TO_JSVAL(hangs),
|
||||
nullptr, nullptr, JSPROP_ENUMERATE)) {
|
||||
return nullptr;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TelemetryImpl::GetThreadHangStats(JSContext* cx, JS::Value* ret)
|
||||
{
|
||||
JS::RootedObject retObj(cx, JS_NewArrayObject(cx, 0, nullptr));
|
||||
if (!retObj) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
size_t threadIndex = 0;
|
||||
|
||||
/* First add active threads; we need to hold |iter| (and its lock)
|
||||
throughout this method to avoid a race condition where a thread can
|
||||
be recorded twice if the thread is destroyed while this method is
|
||||
running */
|
||||
BackgroundHangMonitor::ThreadHangStatsIterator iter;
|
||||
for (Telemetry::ThreadHangStats* histogram = iter.GetNext();
|
||||
histogram; histogram = iter.GetNext()) {
|
||||
JS::RootedObject obj(cx,
|
||||
CreateJSThreadHangStats(cx, *histogram));
|
||||
JS::RootedValue thread(cx, OBJECT_TO_JSVAL(obj));
|
||||
if (!JS_SetElement(cx, retObj, threadIndex++, &thread)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
// Add saved threads next
|
||||
MutexAutoLock autoLock(mThreadHangStatsMutex);
|
||||
for (size_t i = 0; i < mThreadHangStats.length(); i++) {
|
||||
JS::RootedObject obj(cx,
|
||||
CreateJSThreadHangStats(cx, mThreadHangStats[i]));
|
||||
JS::RootedValue thread(cx, OBJECT_TO_JSVAL(obj));
|
||||
if (!JS_SetElement(cx, retObj, threadIndex++, &thread)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
}
|
||||
*ret = OBJECT_TO_JSVAL(retObj);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
TelemetryImpl::ReadLateWritesStacks(nsIFile* aProfileDir)
|
||||
{
|
||||
@ -2058,6 +2241,17 @@ TelemetryImpl::RecordChromeHang(uint32_t duration,
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
TelemetryImpl::RecordThreadHangStats(Telemetry::ThreadHangStats& aStats)
|
||||
{
|
||||
if (!sTelemetry || !sTelemetry->mCanRecord)
|
||||
return;
|
||||
|
||||
MutexAutoLock autoLock(sTelemetry->mThreadHangStatsMutex);
|
||||
|
||||
sTelemetry->mThreadHangStats.append(Move(aStats));
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS1(TelemetryImpl, nsITelemetry)
|
||||
NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsITelemetry, TelemetryImpl::CreateTelemetryInstance)
|
||||
|
||||
@ -2223,6 +2417,11 @@ void RecordChromeHang(uint32_t duration,
|
||||
}
|
||||
#endif
|
||||
|
||||
void RecordThreadHangStats(ThreadHangStats& aStats)
|
||||
{
|
||||
TelemetryImpl::RecordThreadHangStats(aStats);
|
||||
}
|
||||
|
||||
ProcessedStack::ProcessedStack()
|
||||
{
|
||||
}
|
||||
@ -2439,6 +2638,41 @@ WriteFailedProfileLock(nsIFile* aProfileDir)
|
||||
seekStream->SetEOF();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TimeHistogram::Add(PRIntervalTime aTime)
|
||||
{
|
||||
uint32_t timeMs = PR_IntervalToMilliseconds(aTime);
|
||||
size_t index = mozilla::FloorLog2(timeMs);
|
||||
operator[](index)++;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
HangHistogram::GetHash(const Stack& aStack)
|
||||
{
|
||||
uint32_t hash = 0;
|
||||
for (const char* const* label = aStack.begin();
|
||||
label != aStack.end(); label++) {
|
||||
/* We only need to hash the pointer instead of the text content
|
||||
because we are assuming constant pointers */
|
||||
hash = AddToHash(hash, *label);
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
bool
|
||||
HangHistogram::operator==(const HangHistogram& aOther) const
|
||||
{
|
||||
if (mHash != aOther.mHash) {
|
||||
return false;
|
||||
}
|
||||
if (mStack.length() != aOther.mStack.length()) {
|
||||
return false;
|
||||
}
|
||||
return PodEqual(mStack.begin(), aOther.mStack.begin(), mStack.length());
|
||||
}
|
||||
|
||||
|
||||
} // namespace Telemetry
|
||||
} // namespace mozilla
|
||||
|
||||
|
@ -175,6 +175,20 @@ void RecordChromeHang(uint32_t duration,
|
||||
ProcessedStack &aStack);
|
||||
#endif
|
||||
|
||||
class ThreadHangStats;
|
||||
|
||||
/**
|
||||
* Move a ThreadHangStats to Telemetry storage. Normally Telemetry queries
|
||||
* for active ThreadHangStats through BackgroundHangMonitor, but once a
|
||||
* thread exits, the thread's copy of ThreadHangStats needs to be moved to
|
||||
* inside Telemetry using this function.
|
||||
*
|
||||
* @param aStats ThreadHangStats to save; the data inside aStats
|
||||
* will be moved and aStats should be treated as
|
||||
* invalid after this function returns
|
||||
*/
|
||||
void RecordThreadHangStats(ThreadHangStats& aStats);
|
||||
|
||||
/**
|
||||
* Record a failed attempt at locking the user's profile.
|
||||
*
|
||||
|
@ -311,6 +311,16 @@ TelemetryPing.prototype = {
|
||||
return ret;
|
||||
},
|
||||
|
||||
getThreadHangStats: function getThreadHangStats(stats) {
|
||||
stats.forEach((thread) => {
|
||||
thread.activity = this.packHistogram(thread.activity);
|
||||
thread.hangs.forEach((hang) => {
|
||||
hang.histogram = this.packHistogram(hang.histogram);
|
||||
});
|
||||
});
|
||||
return stats;
|
||||
},
|
||||
|
||||
/**
|
||||
* Descriptive metadata
|
||||
*
|
||||
@ -548,6 +558,7 @@ TelemetryPing.prototype = {
|
||||
histograms: this.getHistograms(Telemetry.histogramSnapshots),
|
||||
slowSQL: Telemetry.slowSQL,
|
||||
chromeHangs: Telemetry.chromeHangs,
|
||||
threadHangStats: this.getThreadHangStats(Telemetry.threadHangStats),
|
||||
lateWrites: Telemetry.lateWrites,
|
||||
addonHistograms: this.getAddonHistograms(),
|
||||
addonDetails: AddonManagerPrivate.getTelemetryDetails(),
|
||||
|
113
toolkit/components/telemetry/ThreadHangStats.h
Normal file
113
toolkit/components/telemetry/ThreadHangStats.h
Normal file
@ -0,0 +1,113 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_BackgroundHangTelemetry_h
|
||||
#define mozilla_BackgroundHangTelemetry_h
|
||||
|
||||
#include "mozilla/Array.h"
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/Move.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
#include "mozilla/PodOperations.h"
|
||||
#include "mozilla/Vector.h"
|
||||
|
||||
#include "nsString.h"
|
||||
#include "prinrval.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace Telemetry {
|
||||
|
||||
static const size_t kTimeHistogramBuckets = 8 * sizeof(PRIntervalTime);
|
||||
|
||||
/* TimeHistogram is an efficient histogram that puts time durations into
|
||||
exponential (base 2) buckets; times are accepted in PRIntervalTime and
|
||||
stored in milliseconds. */
|
||||
class TimeHistogram : public mozilla::Array<uint32_t, kTimeHistogramBuckets>
|
||||
{
|
||||
public:
|
||||
TimeHistogram()
|
||||
{
|
||||
mozilla::PodArrayZero(*this);
|
||||
}
|
||||
// Get minimum (inclusive) range of bucket in milliseconds
|
||||
uint32_t GetBucketMin(size_t aBucket) const {
|
||||
MOZ_ASSERT(aBucket < ArrayLength(*this));
|
||||
return (1u << aBucket) & ~1u; // Bucket 0 starts at 0, not 1
|
||||
}
|
||||
// Get maximum (inclusive) range of bucket in milliseconds
|
||||
uint32_t GetBucketMax(size_t aBucket) const {
|
||||
MOZ_ASSERT(aBucket < ArrayLength(*this));
|
||||
return (1u << (aBucket + 1u)) - 1u;
|
||||
}
|
||||
void Add(PRIntervalTime aTime);
|
||||
};
|
||||
|
||||
/* A hang histogram consists of a stack associated with the
|
||||
hang, along with a time histogram of the hang times. */
|
||||
class HangHistogram : public TimeHistogram
|
||||
{
|
||||
public:
|
||||
typedef mozilla::Vector<const char*, 8> Stack;
|
||||
|
||||
private:
|
||||
static uint32_t GetHash(const Stack& aStack);
|
||||
|
||||
Stack mStack;
|
||||
// Use a hash to speed comparisons
|
||||
const uint32_t mHash;
|
||||
|
||||
public:
|
||||
explicit HangHistogram(Stack&& aStack)
|
||||
: mStack(mozilla::Move(aStack))
|
||||
, mHash(GetHash(mStack))
|
||||
{
|
||||
}
|
||||
HangHistogram(HangHistogram&& aOther)
|
||||
: TimeHistogram(mozilla::Move(aOther))
|
||||
, mStack(mozilla::Move(aOther.mStack))
|
||||
, mHash(mozilla::Move(aOther.mHash))
|
||||
{
|
||||
}
|
||||
bool operator==(const HangHistogram& aOther) const;
|
||||
bool operator!=(const HangHistogram& aOther) const
|
||||
{
|
||||
return !operator==(aOther);
|
||||
}
|
||||
const Stack& GetStack() const {
|
||||
return mStack;
|
||||
}
|
||||
};
|
||||
|
||||
/* Thread hang stats consist of
|
||||
- thread name
|
||||
- time histogram of all task run times
|
||||
- hang histograms of individual hangs. */
|
||||
class ThreadHangStats
|
||||
{
|
||||
private:
|
||||
nsAutoCString mName;
|
||||
|
||||
public:
|
||||
TimeHistogram mActivity;
|
||||
mozilla::Vector<HangHistogram, 4> mHangs;
|
||||
|
||||
explicit ThreadHangStats(const char* aName)
|
||||
: mName(aName)
|
||||
{
|
||||
}
|
||||
ThreadHangStats(ThreadHangStats&& aOther)
|
||||
: mName(mozilla::Move(aOther.mName))
|
||||
, mActivity(mozilla::Move(aOther.mActivity))
|
||||
, mHangs(mozilla::Move(aOther.mHangs))
|
||||
{
|
||||
}
|
||||
const char* GetName() const {
|
||||
return mName.get();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Telemetry
|
||||
} // namespace mozilla
|
||||
#endif // mozilla_BackgroundHangTelemetry_h
|
@ -16,6 +16,7 @@ XPIDL_MODULE = 'telemetry'
|
||||
EXPORTS.mozilla += [
|
||||
'ProcessedStack.h',
|
||||
'Telemetry.h',
|
||||
'ThreadHangStats.h',
|
||||
]
|
||||
|
||||
SOURCES += [
|
||||
|
@ -99,6 +99,23 @@ interface nsITelemetry : nsISupports
|
||||
[implicit_jscontext]
|
||||
readonly attribute jsval chromeHangs;
|
||||
|
||||
/*
|
||||
* An array of thread hang stats,
|
||||
* [<thread>, <thread>, ...]
|
||||
* <thread> represents a single thread,
|
||||
* {"name": "<name>",
|
||||
* "activity": <time>,
|
||||
* "hangs": [<hang>, <hang>, ...]}
|
||||
* <time> represents a histogram of time intervals in milliseconds,
|
||||
* with the same format as histogramSnapshots
|
||||
* <hang> represents a particular hang,
|
||||
* {"stack": <stack>, "histogram": <time>}
|
||||
* <stack> represents the hang's stack,
|
||||
* ["<frame_0>", "<frame_1>", ...]
|
||||
*/
|
||||
[implicit_jscontext]
|
||||
readonly attribute jsval threadHangStats;
|
||||
|
||||
/*
|
||||
* An object with two fields: memoryMap and stacks.
|
||||
* * memoryMap is a list of loaded libraries.
|
||||
|
@ -587,7 +587,7 @@ class nsXULAppInfo : public nsIXULAppInfo,
|
||||
|
||||
{
|
||||
public:
|
||||
nsXULAppInfo() {}
|
||||
MOZ_CONSTEXPR nsXULAppInfo() {}
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_NSIXULAPPINFO
|
||||
NS_DECL_NSIXULRUNTIME
|
||||
|
@ -80,7 +80,7 @@ class StackEntry : public js::ProfileEntry
|
||||
{
|
||||
public:
|
||||
|
||||
bool isCopyLabel() volatile {
|
||||
bool isCopyLabel() const volatile {
|
||||
return !((uintptr_t)stackAddress() & 0x1);
|
||||
}
|
||||
|
||||
|
@ -2690,6 +2690,7 @@ nsNativeThemeWin::ClassicThemeSupportsWidget(nsPresContext* aPresContext,
|
||||
case NS_THEME_SCROLLBAR_THUMB_HORIZONTAL:
|
||||
case NS_THEME_SCROLLBAR_TRACK_VERTICAL:
|
||||
case NS_THEME_SCROLLBAR_TRACK_HORIZONTAL:
|
||||
case NS_THEME_SCROLLBAR_NON_DISAPPEARING:
|
||||
case NS_THEME_SCALE_HORIZONTAL:
|
||||
case NS_THEME_SCALE_VERTICAL:
|
||||
case NS_THEME_SCALE_THUMB_HORIZONTAL:
|
||||
@ -2866,6 +2867,12 @@ nsNativeThemeWin::ClassicGetMinimumWidgetSize(nsRenderingContext* aContext, nsIF
|
||||
|
||||
// (*aResult).height = ::GetSystemMetrics(SM_CYVTHUMB) << 1;
|
||||
break;
|
||||
case NS_THEME_SCROLLBAR_NON_DISAPPEARING:
|
||||
{
|
||||
aResult->SizeTo(::GetSystemMetrics(SM_CXHSCROLL),
|
||||
::GetSystemMetrics(SM_CYVSCROLL));
|
||||
break;
|
||||
}
|
||||
case NS_THEME_RANGE_THUMB: {
|
||||
if (IsRangeHorizontal(aFrame)) {
|
||||
(*aResult).width = 12;
|
||||
|
@ -106,6 +106,7 @@ extern nsresult nsStringInputStreamConstructor(nsISupports *, REFNSIID, void **)
|
||||
#include "mozilla/Omnijar.h"
|
||||
#include "mozilla/HangMonitor.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
#include "mozilla/BackgroundHangMonitor.h"
|
||||
|
||||
#include "nsChromeRegistry.h"
|
||||
#include "nsChromeProtocolHandler.h"
|
||||
@ -587,6 +588,7 @@ NS_InitXPCOM2(nsIServiceManager* *result,
|
||||
mozilla::Telemetry::Init();
|
||||
|
||||
mozilla::HangMonitor::Startup();
|
||||
mozilla::BackgroundHangMonitor::Startup();
|
||||
|
||||
#ifdef MOZ_VISUAL_EVENT_TRACER
|
||||
mozilla::eventtracer::Init();
|
||||
@ -832,6 +834,7 @@ ShutdownXPCOM(nsIServiceManager* servMgr)
|
||||
Omnijar::CleanUp();
|
||||
|
||||
HangMonitor::Shutdown();
|
||||
BackgroundHangMonitor::Shutdown();
|
||||
|
||||
#ifdef MOZ_VISUAL_EVENT_TRACER
|
||||
eventtracer::Shutdown();
|
||||
|
490
xpcom/threads/BackgroundHangMonitor.cpp
Normal file
490
xpcom/threads/BackgroundHangMonitor.cpp
Normal file
@ -0,0 +1,490 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "mozilla/BackgroundHangMonitor.h"
|
||||
#include "mozilla/LinkedList.h"
|
||||
#include "mozilla/Monitor.h"
|
||||
#include "mozilla/Move.h"
|
||||
#include "mozilla/StaticPtr.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
#include "mozilla/ThreadHangStats.h"
|
||||
#include "mozilla/ThreadLocal.h"
|
||||
|
||||
#include "prinrval.h"
|
||||
#include "prthread.h"
|
||||
#include "ThreadStackHelper.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
/**
|
||||
* BackgroundHangManager is the global object that
|
||||
* manages all instances of BackgroundHangThread.
|
||||
*/
|
||||
class BackgroundHangManager : public AtomicRefCounted<BackgroundHangManager>
|
||||
{
|
||||
private:
|
||||
// Background hang monitor thread function
|
||||
static void MonitorThread(void* aData)
|
||||
{
|
||||
PR_SetCurrentThreadName("BgHangManager");
|
||||
/* We do not hold a reference to BackgroundHangManager here
|
||||
because the monitor thread only exists as long as the
|
||||
BackgroundHangManager instance exists. We stop the monitor
|
||||
thread in the BackgroundHangManager destructor, and we can
|
||||
only get to the destructor if we don't hold a reference here. */
|
||||
static_cast<BackgroundHangManager*>(aData)->RunMonitorThread();
|
||||
}
|
||||
|
||||
// Hang monitor thread
|
||||
PRThread* mHangMonitorThread;
|
||||
// Stop hang monitoring
|
||||
bool mShutdown;
|
||||
|
||||
BackgroundHangManager(const BackgroundHangManager&);
|
||||
BackgroundHangManager& operator=(const BackgroundHangManager&);
|
||||
void RunMonitorThread();
|
||||
|
||||
public:
|
||||
static StaticRefPtr<BackgroundHangManager> sInstance;
|
||||
|
||||
// Lock for access to members of this class
|
||||
Monitor mLock;
|
||||
// Current time as seen by hang monitors
|
||||
PRIntervalTime mIntervalNow;
|
||||
// List of BackgroundHangThread instances associated with each thread
|
||||
LinkedList<BackgroundHangThread> mHangThreads;
|
||||
|
||||
void Shutdown()
|
||||
{
|
||||
MonitorAutoLock autoLock(mLock);
|
||||
mShutdown = true;
|
||||
autoLock.Notify();
|
||||
}
|
||||
|
||||
void Wakeup()
|
||||
{
|
||||
// PR_CreateThread could have failed earlier
|
||||
if (mHangMonitorThread) {
|
||||
// Use PR_Interrupt to avoid potentially taking a lock
|
||||
PR_Interrupt(mHangMonitorThread);
|
||||
}
|
||||
}
|
||||
|
||||
BackgroundHangManager();
|
||||
~BackgroundHangManager();
|
||||
};
|
||||
|
||||
/**
|
||||
* BackgroundHangThread is a per-thread object that is used
|
||||
* by all instances of BackgroundHangMonitor to monitor hangs.
|
||||
*/
|
||||
class BackgroundHangThread : public RefCounted<BackgroundHangThread>
|
||||
, public LinkedListElement<BackgroundHangThread>
|
||||
{
|
||||
private:
|
||||
static ThreadLocal<BackgroundHangThread*> sTlsKey;
|
||||
|
||||
BackgroundHangThread(const BackgroundHangThread&);
|
||||
BackgroundHangThread& operator=(const BackgroundHangThread&);
|
||||
|
||||
/* Keep a reference to the manager, so we can keep going even
|
||||
after BackgroundHangManager::Shutdown is called. */
|
||||
const RefPtr<BackgroundHangManager> mManager;
|
||||
// Unique thread ID for identification
|
||||
const PRThread* mThreadID;
|
||||
|
||||
public:
|
||||
static BackgroundHangThread* FindThread();
|
||||
|
||||
static void Startup()
|
||||
{
|
||||
/* We can tolerate init() failing.
|
||||
The if block turns off warn_unused_result. */
|
||||
if (!sTlsKey.init()) {}
|
||||
}
|
||||
|
||||
// Hang timeout in ticks
|
||||
const PRIntervalTime mTimeout;
|
||||
// PermaHang timeout in ticks
|
||||
const PRIntervalTime mMaxTimeout;
|
||||
// Time at last activity
|
||||
PRIntervalTime mInterval;
|
||||
// Time when a hang started
|
||||
PRIntervalTime mHangStart;
|
||||
// Is the thread in a hang
|
||||
bool mHanging;
|
||||
// Is the thread in a waiting state
|
||||
bool mWaiting;
|
||||
// Platform-specific helper to get hang stacks
|
||||
ThreadStackHelper mStackHelper;
|
||||
// Stack of current hang
|
||||
Telemetry::HangHistogram::Stack mHangStack;
|
||||
// Statistics for telemetry
|
||||
Telemetry::ThreadHangStats mStats;
|
||||
|
||||
BackgroundHangThread(const char* aName,
|
||||
uint32_t aTimeoutMs,
|
||||
uint32_t aMaxTimeoutMs);
|
||||
~BackgroundHangThread();
|
||||
|
||||
// Report a hang; aManager->mLock IS locked
|
||||
void ReportHang(PRIntervalTime aHangTime);
|
||||
// Report a permanent hang; aManager->mLock IS locked
|
||||
void ReportPermaHang() const;
|
||||
// Called by BackgroundHangMonitor::NotifyActivity
|
||||
void NotifyActivity();
|
||||
// Called by BackgroundHangMonitor::NotifyWait
|
||||
void NotifyWait()
|
||||
{
|
||||
NotifyActivity();
|
||||
mWaiting = true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
StaticRefPtr<BackgroundHangManager> BackgroundHangManager::sInstance;
|
||||
|
||||
ThreadLocal<BackgroundHangThread*> BackgroundHangThread::sTlsKey;
|
||||
|
||||
|
||||
BackgroundHangManager::BackgroundHangManager()
|
||||
: mShutdown(false)
|
||||
, mLock("BackgroundHangManager")
|
||||
, mIntervalNow(0)
|
||||
{
|
||||
// Lock so we don't race against the new monitor thread
|
||||
MonitorAutoLock autoLock(mLock);
|
||||
mHangMonitorThread = PR_CreateThread(
|
||||
PR_USER_THREAD, MonitorThread, this,
|
||||
PR_PRIORITY_LOW, PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0);
|
||||
|
||||
MOZ_ASSERT(mHangMonitorThread,
|
||||
"Failed to create monitor thread");
|
||||
}
|
||||
|
||||
BackgroundHangManager::~BackgroundHangManager()
|
||||
{
|
||||
MOZ_ASSERT(mShutdown,
|
||||
"Destruction without Shutdown call");
|
||||
MOZ_ASSERT(mHangThreads.isEmpty(),
|
||||
"Destruction with outstanding monitors");
|
||||
MOZ_ASSERT(mHangMonitorThread,
|
||||
"No monitor thread");
|
||||
|
||||
// PR_CreateThread could have failed above due to resource limitation
|
||||
if (mHangMonitorThread) {
|
||||
// The monitor thread can only live as long as the instance lives
|
||||
PR_JoinThread(mHangMonitorThread);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
BackgroundHangManager::RunMonitorThread()
|
||||
{
|
||||
// Keep us locked except when waiting
|
||||
MonitorAutoLock autoLock(mLock);
|
||||
|
||||
/* mIntervalNow is updated at various intervals determined by waitTime.
|
||||
However, if an update latency is too long (due to CPU scheduling, system
|
||||
sleep, etc.), we don't update mIntervalNow at all. This is done so that
|
||||
long latencies in our timing are not detected as hangs. systemTime is
|
||||
used to track PR_IntervalNow() and determine our latency. */
|
||||
|
||||
PRIntervalTime systemTime = PR_IntervalNow();
|
||||
// Default values for the first iteration of thread loop
|
||||
PRIntervalTime waitTime = PR_INTERVAL_NO_WAIT;
|
||||
PRIntervalTime recheckTimeout = PR_INTERVAL_NO_WAIT;
|
||||
|
||||
while (!mShutdown) {
|
||||
|
||||
PR_ClearInterrupt();
|
||||
nsresult rv = autoLock.Wait(waitTime);
|
||||
|
||||
PRIntervalTime newTime = PR_IntervalNow();
|
||||
PRIntervalTime systemInterval = newTime - systemTime;
|
||||
systemTime = newTime;
|
||||
|
||||
/* waitTime is a quarter of the shortest timeout value; If our timing
|
||||
latency is low enough (less than half the shortest timeout value),
|
||||
we can update mIntervalNow. */
|
||||
if (MOZ_LIKELY(waitTime != PR_INTERVAL_NO_TIMEOUT &&
|
||||
systemInterval < 2 * waitTime)) {
|
||||
mIntervalNow += systemInterval;
|
||||
}
|
||||
|
||||
/* If it's before the next recheck timeout, and our wait did not
|
||||
get interrupted (either through Notify or PR_Interrupt), we can
|
||||
keep the current waitTime and skip iterating through hang monitors. */
|
||||
if (MOZ_LIKELY(systemInterval < recheckTimeout &&
|
||||
systemInterval >= waitTime &&
|
||||
rv == NS_OK)) {
|
||||
recheckTimeout -= systemInterval;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* We are in one of the following scenarios,
|
||||
- Hang or permahang recheck timeout
|
||||
- Thread added/removed
|
||||
- Thread wait or hang ended
|
||||
In all cases, we want to go through our list of hang
|
||||
monitors and update waitTime and recheckTimeout. */
|
||||
waitTime = PR_INTERVAL_NO_TIMEOUT;
|
||||
recheckTimeout = PR_INTERVAL_NO_TIMEOUT;
|
||||
|
||||
// Locally hold mIntervalNow
|
||||
PRIntervalTime intervalNow = mIntervalNow;
|
||||
|
||||
// iterate through hang monitors
|
||||
for (BackgroundHangThread* currentThread = mHangThreads.getFirst();
|
||||
currentThread; currentThread = currentThread->getNext()) {
|
||||
|
||||
if (currentThread->mWaiting) {
|
||||
// Thread is waiting, not hanging
|
||||
continue;
|
||||
}
|
||||
PRIntervalTime interval = currentThread->mInterval;
|
||||
PRIntervalTime hangTime = intervalNow - interval;
|
||||
if (MOZ_UNLIKELY(hangTime >= currentThread->mMaxTimeout)) {
|
||||
// A permahang started
|
||||
// Skip subsequent iterations and tolerate a race on mWaiting here
|
||||
currentThread->mWaiting = true;
|
||||
currentThread->ReportPermaHang();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (MOZ_LIKELY(!currentThread->mHanging)) {
|
||||
if (MOZ_UNLIKELY(hangTime >= currentThread->mTimeout)) {
|
||||
// A hang started
|
||||
currentThread->mStackHelper.GetStack(currentThread->mHangStack);
|
||||
currentThread->mHangStart = interval;
|
||||
currentThread->mHanging = true;
|
||||
}
|
||||
} else {
|
||||
if (MOZ_LIKELY(interval != currentThread->mHangStart)) {
|
||||
// A hang ended
|
||||
currentThread->ReportHang(intervalNow - currentThread->mHangStart);
|
||||
currentThread->mHanging = false;
|
||||
}
|
||||
}
|
||||
|
||||
/* If we are hanging, the next time we check for hang status is when
|
||||
the hang turns into a permahang. If we're not hanging, the next
|
||||
recheck timeout is when we may be entering a hang. */
|
||||
PRIntervalTime nextRecheck;
|
||||
if (currentThread->mHanging) {
|
||||
nextRecheck = currentThread->mMaxTimeout;
|
||||
} else {
|
||||
nextRecheck = currentThread->mTimeout;
|
||||
}
|
||||
recheckTimeout = std::min(recheckTimeout, nextRecheck - hangTime);
|
||||
|
||||
/* We wait for a quarter of the shortest timeout
|
||||
value to give mIntervalNow enough granularity. */
|
||||
waitTime = std::min(waitTime, currentThread->mTimeout / 4);
|
||||
}
|
||||
}
|
||||
|
||||
/* We are shutting down now.
|
||||
Wait for all outstanding monitors to unregister. */
|
||||
while (!mHangThreads.isEmpty()) {
|
||||
autoLock.Wait(PR_INTERVAL_NO_TIMEOUT);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
BackgroundHangThread::BackgroundHangThread(const char* aName,
|
||||
uint32_t aTimeoutMs,
|
||||
uint32_t aMaxTimeoutMs)
|
||||
: mManager(BackgroundHangManager::sInstance)
|
||||
, mThreadID(PR_GetCurrentThread())
|
||||
, mTimeout(PR_MillisecondsToInterval(aTimeoutMs))
|
||||
, mMaxTimeout(PR_MillisecondsToInterval(aMaxTimeoutMs))
|
||||
, mInterval(mManager->mIntervalNow)
|
||||
, mHangStart(mInterval)
|
||||
, mHanging(false)
|
||||
, mWaiting(true)
|
||||
, mStats(aName)
|
||||
{
|
||||
if (sTlsKey.initialized()) {
|
||||
sTlsKey.set(this);
|
||||
}
|
||||
// Lock here because LinkedList is not thread-safe
|
||||
MonitorAutoLock autoLock(mManager->mLock);
|
||||
// Add to thread list
|
||||
mManager->mHangThreads.insertBack(this);
|
||||
// Wake up monitor thread to process new thread
|
||||
autoLock.Notify();
|
||||
}
|
||||
|
||||
BackgroundHangThread::~BackgroundHangThread()
|
||||
{
|
||||
// Lock here because LinkedList is not thread-safe
|
||||
MonitorAutoLock autoLock(mManager->mLock);
|
||||
// Remove from thread list
|
||||
remove();
|
||||
// Wake up monitor thread to process removed thread
|
||||
autoLock.Notify();
|
||||
|
||||
// We no longer have a thread
|
||||
if (sTlsKey.initialized()) {
|
||||
sTlsKey.set(nullptr);
|
||||
}
|
||||
|
||||
// Move our copy of ThreadHangStats to Telemetry storage
|
||||
Telemetry::RecordThreadHangStats(mStats);
|
||||
}
|
||||
|
||||
void
|
||||
BackgroundHangThread::ReportHang(PRIntervalTime aHangTime)
|
||||
{
|
||||
// Recovered from a hang; called on the monitor thread
|
||||
// mManager->mLock IS locked
|
||||
|
||||
Telemetry::HangHistogram newHistogram(Move(mHangStack));
|
||||
for (Telemetry::HangHistogram* oldHistogram = mStats.mHangs.begin();
|
||||
oldHistogram != mStats.mHangs.end(); oldHistogram++) {
|
||||
if (newHistogram == *oldHistogram) {
|
||||
// New histogram matches old one
|
||||
oldHistogram->Add(aHangTime);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Add new histogram
|
||||
newHistogram.Add(aHangTime);
|
||||
mStats.mHangs.append(Move(newHistogram));
|
||||
}
|
||||
|
||||
void
|
||||
BackgroundHangThread::ReportPermaHang() const
|
||||
{
|
||||
// Permanently hanged; called on the monitor thread
|
||||
// mManager->mLock IS locked
|
||||
|
||||
// TODO: Add telemetry reporting for perma-hangs
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE void
|
||||
BackgroundHangThread::NotifyActivity()
|
||||
{
|
||||
PRIntervalTime intervalNow = mManager->mIntervalNow;
|
||||
if (mWaiting) {
|
||||
mInterval = intervalNow;
|
||||
mWaiting = false;
|
||||
/* We have to wake up the manager thread because when all threads
|
||||
are waiting, the manager thread waits indefinitely as well. */
|
||||
mManager->Wakeup();
|
||||
} else {
|
||||
PRIntervalTime duration = intervalNow - mInterval;
|
||||
mStats.mActivity.Add(duration);
|
||||
if (MOZ_UNLIKELY(duration >= mTimeout)) {
|
||||
/* Wake up the manager thread to tell it that a hang ended */
|
||||
mManager->Wakeup();
|
||||
}
|
||||
mInterval = intervalNow;
|
||||
}
|
||||
}
|
||||
|
||||
BackgroundHangThread*
|
||||
BackgroundHangThread::FindThread()
|
||||
{
|
||||
if (sTlsKey.initialized()) {
|
||||
// Use TLS if available
|
||||
return sTlsKey.get();
|
||||
}
|
||||
// If TLS is unavailable, we can search through the thread list
|
||||
RefPtr<BackgroundHangManager> manager(BackgroundHangManager::sInstance);
|
||||
MOZ_ASSERT(manager, "Creating BackgroundHangMonitor after shutdown");
|
||||
|
||||
PRThread* threadID = PR_GetCurrentThread();
|
||||
// Lock thread list for traversal
|
||||
MonitorAutoLock autoLock(manager->mLock);
|
||||
for (BackgroundHangThread* thread = manager->mHangThreads.getFirst();
|
||||
thread; thread = thread->getNext()) {
|
||||
if (thread->mThreadID == threadID) {
|
||||
return thread;
|
||||
}
|
||||
}
|
||||
// Current thread is not initialized
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
BackgroundHangMonitor::Startup()
|
||||
{
|
||||
MOZ_ASSERT(!BackgroundHangManager::sInstance, "Already initialized");
|
||||
ThreadStackHelper::Startup();
|
||||
BackgroundHangThread::Startup();
|
||||
BackgroundHangManager::sInstance = new BackgroundHangManager();
|
||||
}
|
||||
|
||||
void
|
||||
BackgroundHangMonitor::Shutdown()
|
||||
{
|
||||
MOZ_ASSERT(BackgroundHangManager::sInstance, "Not initialized");
|
||||
/* Scope our lock inside Shutdown() because the sInstance object can
|
||||
be destroyed as soon as we set sInstance to nullptr below, and
|
||||
we don't want to hold the lock when it's being destroyed. */
|
||||
BackgroundHangManager::sInstance->Shutdown();
|
||||
BackgroundHangManager::sInstance = nullptr;
|
||||
ThreadStackHelper::Shutdown();
|
||||
}
|
||||
|
||||
BackgroundHangMonitor::BackgroundHangMonitor(const char* aName,
|
||||
uint32_t aTimeoutMs,
|
||||
uint32_t aMaxTimeoutMs)
|
||||
: mThread(BackgroundHangThread::FindThread())
|
||||
{
|
||||
if (!mThread) {
|
||||
mThread = new BackgroundHangThread(aName, aTimeoutMs, aMaxTimeoutMs);
|
||||
}
|
||||
}
|
||||
|
||||
BackgroundHangMonitor::BackgroundHangMonitor()
|
||||
: mThread(BackgroundHangThread::FindThread())
|
||||
{
|
||||
MOZ_ASSERT(mThread, "Thread not initialized for hang monitoring");
|
||||
}
|
||||
|
||||
BackgroundHangMonitor::~BackgroundHangMonitor()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
BackgroundHangMonitor::NotifyActivity()
|
||||
{
|
||||
mThread->NotifyActivity();
|
||||
}
|
||||
|
||||
void
|
||||
BackgroundHangMonitor::NotifyWait()
|
||||
{
|
||||
mThread->NotifyWait();
|
||||
}
|
||||
|
||||
|
||||
/* Because we are iterating through the BackgroundHangThread linked list,
|
||||
we need to take a lock. Using MonitorAutoLock as a base class makes
|
||||
sure all of that is taken care of for us. */
|
||||
BackgroundHangMonitor::ThreadHangStatsIterator::ThreadHangStatsIterator()
|
||||
: MonitorAutoLock(BackgroundHangManager::sInstance->mLock)
|
||||
, mThread(BackgroundHangManager::sInstance->mHangThreads.getFirst())
|
||||
{
|
||||
}
|
||||
|
||||
Telemetry::ThreadHangStats*
|
||||
BackgroundHangMonitor::ThreadHangStatsIterator::GetNext()
|
||||
{
|
||||
if (!mThread) {
|
||||
return nullptr;
|
||||
}
|
||||
Telemetry::ThreadHangStats* stats = &mThread->mStats;
|
||||
mThread = mThread->getNext();
|
||||
return stats;
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
201
xpcom/threads/BackgroundHangMonitor.h
Normal file
201
xpcom/threads/BackgroundHangMonitor.h
Normal file
@ -0,0 +1,201 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_BackgroundHangMonitor_h
|
||||
#define mozilla_BackgroundHangMonitor_h
|
||||
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "mozilla/Monitor.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
namespace Telemetry {
|
||||
class ThreadHangStats;
|
||||
};
|
||||
|
||||
class BackgroundHangThread;
|
||||
|
||||
/**
|
||||
* The background hang monitor is responsible for detecting and reporting
|
||||
* hangs in background (non-main) threads. A thread registers itself using
|
||||
* the BackgroundHangMonitor object and periodically calls its methods to
|
||||
* inform the hang monitor of the thread's activity. Each thread is given
|
||||
* a thread name, a timeout, and a maximum timeout. If one of the thread's
|
||||
* tasks runs for longer than the timeout duration but shorter than the
|
||||
* maximum timeout, a (transient) hang is reported. On the other hand, if
|
||||
* a task runs for longer than the maximum timeout duration or never
|
||||
* finishes (e.g. in a deadlock), a permahang is reported.
|
||||
*
|
||||
* Tasks are defined arbitrarily, but are typically represented by events
|
||||
* in an event loop -- processing one event is equivalent to running one
|
||||
* task. To ensure responsiveness, tasks in a thread often have a target
|
||||
* running time. This is a good starting point for determining the timeout
|
||||
* and maximum timeout values. For example, the Compositor thread has a
|
||||
* responsiveness goal of 60Hz or 17ms, so a starting timeout could be
|
||||
* 100ms. Considering some platforms (e.g. Android) can terminate the app
|
||||
* when a critical thread hangs for longer than a few seconds, a good
|
||||
* starting maximum timeout is 4 or 5 seconds.
|
||||
*
|
||||
* A thread registers itself through the BackgroundHangMonitor constructor.
|
||||
* Multiple BackgroundHangMonitor objects can be used in one thread. The
|
||||
* constructor without arguments can be used when it is known that the thread
|
||||
* already has a BackgroundHangMonitor registered. When all instances of
|
||||
* BackgroundHangMonitor are destroyed, the thread is unregistered.
|
||||
*
|
||||
* The thread then uses two methods to inform BackgroundHangMonitor of the
|
||||
* thread's activity:
|
||||
*
|
||||
* > BackgroundHangMonitor::NotifyActivity should be called *before*
|
||||
* starting a task. The task run time is determined by the interval
|
||||
* between this call and the next NotifyActivity call.
|
||||
*
|
||||
* > BackgroundHangMonitor::NotifyWait should be called *before* the
|
||||
* thread enters a wait state (e.g. to wait for a new event). This
|
||||
* prevents a waiting thread from being detected as hanging. The wait
|
||||
* state is automatically cleared at the next NotifyActivity call.
|
||||
*
|
||||
* The following example shows hang monitoring in a simple event loop:
|
||||
*
|
||||
* void thread_main()
|
||||
* {
|
||||
* mozilla::BackgroundHangMonitor hangMonitor("example1", 100, 1000);
|
||||
* while (!exiting) {
|
||||
* hangMonitor.NotifyActivity();
|
||||
* process_next_event();
|
||||
* hangMonitor.NotifyWait();
|
||||
* wait_for_next_event();
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* The following example shows reentrancy in nested event loops:
|
||||
*
|
||||
* void thread_main()
|
||||
* {
|
||||
* mozilla::BackgroundHangMonitor hangMonitor("example2", 100, 1000);
|
||||
* while (!exiting) {
|
||||
* hangMonitor.NotifyActivity();
|
||||
* process_next_event();
|
||||
* hangMonitor.NotifyWait();
|
||||
* wait_for_next_event();
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* void process_next_event()
|
||||
* {
|
||||
* mozilla::BackgroundHangMonitor hangMonitor();
|
||||
* if (is_sync_event) {
|
||||
* while (!finished_event) {
|
||||
* hangMonitor.NotifyActivity();
|
||||
* process_next_event();
|
||||
* hangMonitor.NotifyWait();
|
||||
* wait_for_next_event();
|
||||
* }
|
||||
* } else {
|
||||
* process_nonsync_event();
|
||||
* }
|
||||
* }
|
||||
*
|
||||
*/
|
||||
class BackgroundHangMonitor
|
||||
{
|
||||
private:
|
||||
RefPtr<BackgroundHangThread> mThread;
|
||||
|
||||
public:
|
||||
/**
|
||||
* ThreadHangStatsIterator is used to iterate through the ThreadHangStats
|
||||
* associated with each active monitored thread. Because of an internal
|
||||
* lock while this object is alive, a thread must use only one instance
|
||||
* of this class at a time and must iterate through the list as fast as
|
||||
* possible. The following example shows using the iterator:
|
||||
*
|
||||
* {
|
||||
* // Scope the iter variable so it's destroyed as soon as we're done
|
||||
* BackgroundHangMonitor::ThreadHangStatsIterator iter;
|
||||
* for (ThreadHangStats* histogram = iter.GetNext();
|
||||
* histogram; histogram = iter.GetNext()) {
|
||||
* // Process histogram
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
class ThreadHangStatsIterator : public MonitorAutoLock
|
||||
{
|
||||
private:
|
||||
BackgroundHangThread* mThread;
|
||||
|
||||
ThreadHangStatsIterator(const ThreadHangStatsIterator&);
|
||||
ThreadHangStatsIterator& operator=(const ThreadHangStatsIterator&);
|
||||
|
||||
public:
|
||||
/**
|
||||
* Create an ThreadHangStatsIterator instance and take the internal lock.
|
||||
* Internal lock is released on destruction.
|
||||
*/
|
||||
ThreadHangStatsIterator();
|
||||
|
||||
/**
|
||||
* Get the next item in the list; the first call returns the first item.
|
||||
* Returns nullptr at the end of the list.
|
||||
*/
|
||||
Telemetry::ThreadHangStats* GetNext();
|
||||
};
|
||||
|
||||
/**
|
||||
* Enable hang monitoring.
|
||||
* Must return before using BackgroundHangMonitor.
|
||||
*/
|
||||
static void Startup();
|
||||
|
||||
/**
|
||||
* Disable hang monitoring.
|
||||
* Can be called without destroying all BackgroundHangMonitors first.
|
||||
*/
|
||||
static void Shutdown();
|
||||
|
||||
/**
|
||||
* Start monitoring hangs for the current thread.
|
||||
*
|
||||
* @param aName Name to identify the thread with
|
||||
* @param aTimeoutMs Amount of time in milliseconds without
|
||||
* activity before registering a hang
|
||||
* @param aMaxTimeoutMs Amount of time in milliseconds without
|
||||
* activity before registering a permanent hang
|
||||
*/
|
||||
BackgroundHangMonitor(const char* aName,
|
||||
uint32_t aTimeoutMs,
|
||||
uint32_t aMaxTimeoutMs);
|
||||
|
||||
/**
|
||||
* Monitor hangs using an existing monitor
|
||||
* associated with the current thread.
|
||||
*/
|
||||
BackgroundHangMonitor();
|
||||
|
||||
/**
|
||||
* Destroys the hang monitor; hang monitoring for a thread stops
|
||||
* when all monitors associated with the thread are destroyed.
|
||||
*/
|
||||
~BackgroundHangMonitor();
|
||||
|
||||
/**
|
||||
* Notify the hang monitor of pending current thread activity.
|
||||
* Call this method before starting an "activity" or after
|
||||
* exiting from a wait state.
|
||||
*/
|
||||
void NotifyActivity();
|
||||
|
||||
/**
|
||||
* Notify the hang monitor of current thread wait.
|
||||
* Call this method before entering a wait state; call
|
||||
* NotifyActivity when subsequently exiting the wait state.
|
||||
*/
|
||||
void NotifyWait();
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_BackgroundHangMonitor_h
|
175
xpcom/threads/ThreadStackHelper.cpp
Normal file
175
xpcom/threads/ThreadStackHelper.cpp
Normal file
@ -0,0 +1,175 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "ThreadStackHelper.h"
|
||||
#include "MainThreadUtils.h"
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/Move.h"
|
||||
|
||||
#ifdef XP_LINUX
|
||||
#include <unistd.h>
|
||||
#include <sys/syscall.h>
|
||||
#endif
|
||||
|
||||
#ifdef ANDROID
|
||||
#ifndef SYS_gettid
|
||||
#define SYS_gettid __NR_gettid
|
||||
#endif
|
||||
#ifndef SYS_tgkill
|
||||
#define SYS_tgkill __NR_tgkill
|
||||
#endif
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
void
|
||||
ThreadStackHelper::Startup()
|
||||
{
|
||||
#if defined(XP_LINUX)
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (!sInitialized) {
|
||||
MOZ_ALWAYS_TRUE(!::sem_init(&sSem, 0, 0));
|
||||
}
|
||||
sInitialized++;
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
ThreadStackHelper::Shutdown()
|
||||
{
|
||||
#if defined(XP_LINUX)
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (sInitialized == 1) {
|
||||
MOZ_ALWAYS_TRUE(!::sem_destroy(&sSem));
|
||||
}
|
||||
sInitialized--;
|
||||
#endif
|
||||
}
|
||||
|
||||
ThreadStackHelper::ThreadStackHelper()
|
||||
: mPseudoStack(mozilla_get_pseudo_stack())
|
||||
, mStackBuffer()
|
||||
, mMaxStackSize(mStackBuffer.capacity())
|
||||
{
|
||||
#if defined(XP_LINUX)
|
||||
mThreadID = ::syscall(SYS_gettid);
|
||||
#elif defined(XP_WIN)
|
||||
mInitialized = !!::DuplicateHandle(
|
||||
::GetCurrentProcess(), ::GetCurrentThread(),
|
||||
::GetCurrentProcess(), &mThreadID,
|
||||
THREAD_SUSPEND_RESUME, FALSE, 0);
|
||||
MOZ_ASSERT(mInitialized);
|
||||
#elif defined(XP_MACOSX)
|
||||
mThreadID = mach_thread_self();
|
||||
#endif
|
||||
}
|
||||
|
||||
ThreadStackHelper::~ThreadStackHelper()
|
||||
{
|
||||
#if defined(XP_WIN)
|
||||
if (mInitialized) {
|
||||
MOZ_ALWAYS_TRUE(!!::CloseHandle(mThreadID));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
ThreadStackHelper::GetStack(Stack& aStack)
|
||||
{
|
||||
// Always run PrepareStackBuffer first to clear aStack
|
||||
if (!PrepareStackBuffer(aStack)) {
|
||||
MOZ_ASSERT(false);
|
||||
return;
|
||||
}
|
||||
|
||||
#if defined(XP_LINUX)
|
||||
if (!sInitialized) {
|
||||
MOZ_ASSERT(false);
|
||||
return;
|
||||
}
|
||||
sCurrent = this;
|
||||
struct sigaction sigact = {};
|
||||
sigact.sa_sigaction = SigAction;
|
||||
sigemptyset(&sigact.sa_mask);
|
||||
sigact.sa_flags = SA_SIGINFO | SA_RESTART;
|
||||
if (::sigaction(SIGPROF, &sigact, &sOldSigAction)) {
|
||||
MOZ_ASSERT(false);
|
||||
return;
|
||||
}
|
||||
MOZ_ALWAYS_TRUE(!::syscall(SYS_tgkill, getpid(), mThreadID, SIGPROF));
|
||||
MOZ_ALWAYS_TRUE(!::sem_wait(&sSem));
|
||||
|
||||
#elif defined(XP_WIN)
|
||||
if (!mInitialized) {
|
||||
MOZ_ASSERT(false);
|
||||
return;
|
||||
}
|
||||
if (::SuspendThread(mThreadID) == DWORD(-1)) {
|
||||
MOZ_ASSERT(false);
|
||||
return;
|
||||
}
|
||||
FillStackBuffer();
|
||||
MOZ_ALWAYS_TRUE(::ResumeThread(mThreadID) != DWORD(-1));
|
||||
|
||||
#elif defined(XP_MACOSX)
|
||||
if (::thread_suspend(mThreadID) != KERN_SUCCESS) {
|
||||
MOZ_ASSERT(false);
|
||||
return;
|
||||
}
|
||||
FillStackBuffer();
|
||||
MOZ_ALWAYS_TRUE(::thread_resume(mThreadID) == KERN_SUCCESS);
|
||||
|
||||
#endif
|
||||
aStack = Move(mStackBuffer);
|
||||
}
|
||||
|
||||
#ifdef XP_LINUX
|
||||
|
||||
int ThreadStackHelper::sInitialized;
|
||||
sem_t ThreadStackHelper::sSem;
|
||||
struct sigaction ThreadStackHelper::sOldSigAction;
|
||||
ThreadStackHelper* ThreadStackHelper::sCurrent;
|
||||
|
||||
void
|
||||
ThreadStackHelper::SigAction(int aSignal, siginfo_t* aInfo, void* aContext)
|
||||
{
|
||||
::sigaction(SIGPROF, &sOldSigAction, nullptr);
|
||||
sCurrent->FillStackBuffer();
|
||||
sCurrent = nullptr;
|
||||
::sem_post(&sSem);
|
||||
}
|
||||
|
||||
#endif // XP_LINUX
|
||||
|
||||
bool
|
||||
ThreadStackHelper::PrepareStackBuffer(Stack& aStack) {
|
||||
aStack.clear();
|
||||
if (!mPseudoStack) {
|
||||
return false;
|
||||
}
|
||||
mStackBuffer.clear();
|
||||
return mStackBuffer.reserve(mMaxStackSize);
|
||||
}
|
||||
|
||||
void
|
||||
ThreadStackHelper::FillStackBuffer() {
|
||||
size_t reservedSize = mMaxStackSize;
|
||||
|
||||
// Go from front to back
|
||||
const volatile StackEntry* entry = mPseudoStack->mStack;
|
||||
const volatile StackEntry* end = entry + mPseudoStack->stackSize();
|
||||
for (; reservedSize-- && entry != end; entry++) {
|
||||
/* We only accept non-copy labels, because
|
||||
we are unable to actually copy labels here */
|
||||
if (!entry->isCopyLabel()) {
|
||||
mStackBuffer.infallibleAppend(entry->label());
|
||||
}
|
||||
}
|
||||
// If we exited early due to buffer size, expand the buffer for next time
|
||||
mMaxStackSize += (end - entry);
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
100
xpcom/threads/ThreadStackHelper.h
Normal file
100
xpcom/threads/ThreadStackHelper.h
Normal file
@ -0,0 +1,100 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_ThreadStackHelper_h
|
||||
#define mozilla_ThreadStackHelper_h
|
||||
|
||||
#include "mozilla/ThreadHangStats.h"
|
||||
|
||||
#include "GeckoProfiler.h"
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#if defined(XP_LINUX)
|
||||
#include <signal.h>
|
||||
#include <semaphore.h>
|
||||
#include <sys/types.h>
|
||||
#elif defined(XP_WIN)
|
||||
#include <windows.h>
|
||||
#elif defined(XP_MACOSX)
|
||||
#include <mach/mach.h>
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
/**
|
||||
* ThreadStackHelper is used to retrieve the profiler pseudo-stack of a
|
||||
* thread, as an alternative of using the profiler to take a profile.
|
||||
* The target thread first declares an ThreadStackHelper instance;
|
||||
* then another thread can call ThreadStackHelper::GetStack to retrieve
|
||||
* the pseudo-stack of the target thread at that instant.
|
||||
*
|
||||
* Only non-copying labels are included in the stack, which means labels
|
||||
* with custom text and markers are not included.
|
||||
*/
|
||||
class ThreadStackHelper
|
||||
{
|
||||
public:
|
||||
typedef Telemetry::HangHistogram::Stack Stack;
|
||||
|
||||
private:
|
||||
const PseudoStack* const mPseudoStack;
|
||||
Stack mStackBuffer;
|
||||
size_t mMaxStackSize;
|
||||
|
||||
bool PrepareStackBuffer(Stack& aStack);
|
||||
void FillStackBuffer();
|
||||
|
||||
public:
|
||||
/**
|
||||
* Initialize ThreadStackHelper. Must be called from main thread.
|
||||
*/
|
||||
static void Startup();
|
||||
/**
|
||||
* Uninitialize ThreadStackHelper. Must be called from main thread.
|
||||
*/
|
||||
static void Shutdown();
|
||||
|
||||
/**
|
||||
* Create a ThreadStackHelper instance targeting the current thread.
|
||||
*/
|
||||
ThreadStackHelper();
|
||||
|
||||
~ThreadStackHelper();
|
||||
|
||||
/**
|
||||
* Retrieve the current pseudostack of the thread associated
|
||||
* with this ThreadStackHelper.
|
||||
*
|
||||
* @param aStack Stack instance to be filled.
|
||||
*/
|
||||
void GetStack(Stack& aStack);
|
||||
|
||||
#if defined(XP_LINUX)
|
||||
private:
|
||||
static int sInitialized;
|
||||
static sem_t sSem;
|
||||
static struct sigaction sOldSigAction;
|
||||
static ThreadStackHelper* sCurrent;
|
||||
|
||||
static void SigAction(int aSignal, siginfo_t* aInfo, void* aContext);
|
||||
|
||||
pid_t mThreadID;
|
||||
|
||||
#elif defined(XP_WIN)
|
||||
private:
|
||||
bool mInitialized;
|
||||
HANDLE mThreadID;
|
||||
|
||||
#elif defined(XP_MACOSX)
|
||||
private:
|
||||
thread_act_t mThreadID;
|
||||
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_ThreadStackHelper_h
|
@ -28,12 +28,14 @@ EXPORTS += [
|
||||
]
|
||||
|
||||
EXPORTS.mozilla += [
|
||||
'BackgroundHangMonitor.h',
|
||||
'HangMonitor.h',
|
||||
'LazyIdleThread.h',
|
||||
'SyncRunnable.h',
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'BackgroundHangMonitor.cpp',
|
||||
'HangMonitor.cpp',
|
||||
'LazyIdleThread.cpp',
|
||||
'nsEnvironment.cpp',
|
||||
@ -44,6 +46,7 @@ UNIFIED_SOURCES += [
|
||||
'nsThreadManager.cpp',
|
||||
'nsThreadPool.cpp',
|
||||
'nsTimerImpl.cpp',
|
||||
'ThreadStackHelper.cpp',
|
||||
'TimerThread.cpp',
|
||||
]
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user