This commit is contained in:
Phil Ringnalda 2015-03-07 19:11:54 -08:00
Родитель 10b32a7928 ed470cc6d0
Коммит ecf64b97b2
255 изменённых файлов: 4123 добавлений и 3284 удалений

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

@ -967,7 +967,7 @@ pref("browser.safebrowsing.enabled", true);
pref("browser.safebrowsing.malware.enabled", true);
pref("browser.safebrowsing.downloads.enabled", true);
// Remote lookups are only enabled for Windows in Nightly and Aurora
#if defined(XP_WIN) && !defined(RELEASE_BUILD)
#if defined(XP_WIN)
pref("browser.safebrowsing.downloads.remote.enabled", true);
#else
pref("browser.safebrowsing.downloads.remote.enabled", false);

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

@ -294,7 +294,7 @@ Animation::ComposeStyle(nsRefPtr<css::AnimValuesStyleRule>& aStyleRule,
// from the last animation to first. For animations targetting the
// same property, the later one wins. So if this property is already set,
// we should not override it.
return;
continue;
}
aSetProperties.AddProperty(prop.mProperty);

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

@ -159,7 +159,8 @@ bool
WindowNamedPropertiesHandler::defineProperty(JSContext* aCx,
JS::Handle<JSObject*> aProxy,
JS::Handle<jsid> aId,
JS::MutableHandle<JSPropertyDescriptor> aDesc) const
JS::MutableHandle<JSPropertyDescriptor> aDesc,
JS::ObjectOpResult &result) const
{
ErrorResult rv;
rv.ThrowTypeError(MSG_DEFINEPROPERTY_ON_GSP);
@ -209,10 +210,10 @@ WindowNamedPropertiesHandler::ownPropNames(JSContext* aCx,
bool
WindowNamedPropertiesHandler::delete_(JSContext* aCx,
JS::Handle<JSObject*> aProxy,
JS::Handle<jsid> aId, bool* aBp) const
JS::Handle<jsid> aId,
JS::ObjectOpResult &aResult) const
{
*aBp = false;
return true;
return aResult.failCantDeleteWindowNamedProperty();
}
static bool

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

@ -28,19 +28,19 @@ public:
virtual bool
defineProperty(JSContext* aCx, JS::Handle<JSObject*> aProxy,
JS::Handle<jsid> aId,
JS::MutableHandle<JSPropertyDescriptor> aDesc) const MOZ_OVERRIDE;
JS::MutableHandle<JSPropertyDescriptor> aDesc,
JS::ObjectOpResult &result) const MOZ_OVERRIDE;
virtual bool
ownPropNames(JSContext* aCx, JS::Handle<JSObject*> aProxy, unsigned flags,
JS::AutoIdVector& aProps) const MOZ_OVERRIDE;
virtual bool
delete_(JSContext* aCx, JS::Handle<JSObject*> aProxy, JS::Handle<jsid> aId,
bool* aBp) const MOZ_OVERRIDE;
JS::ObjectOpResult &aResult) const MOZ_OVERRIDE;
virtual bool
preventExtensions(JSContext* aCx, JS::Handle<JSObject*> aProxy,
bool *succeeded) const MOZ_OVERRIDE
JS::ObjectOpResult& aResult) const MOZ_OVERRIDE
{
*succeeded = false;
return true;
return aResult.failCantPreventExtensions();
}
virtual bool
isExtensible(JSContext* aCx, JS::Handle<JSObject*> aProxy,

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

@ -1174,13 +1174,7 @@ nsDOMClassInfo::PostCreatePrototype(JSContext * cx, JSObject * aProto)
NS_ENSURE_SUCCESS(rv, rv);
if (!contentDefinedProperty && desc.object() && !desc.value().isUndefined() &&
!JS_DefineUCProperty(cx, global, mData->mNameUTF16,
NS_strlen(mData->mNameUTF16),
desc.value(),
// Descriptors never store JSNatives for accessors:
// they have either JSFunctions or JSPropertyOps.
desc.attributes() | JSPROP_PROPOP_ACCESSORS,
JS_PROPERTYOP_GETTER(desc.getter()),
JS_PROPERTYOP_SETTER(desc.setter()))) {
NS_strlen(mData->mNameUTF16), desc)) {
return NS_ERROR_UNEXPECTED;
}

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

@ -1030,9 +1030,7 @@ nsDOMWindowUtils::SendWheelEvent(float aX,
wheelEvent.refPoint = ToWidgetPoint(CSSPoint(aX, aY), offset, presContext);
nsEventStatus status;
nsresult rv = widget->DispatchEvent(&wheelEvent, status);
NS_ENSURE_SUCCESS(rv, rv);
widget->DispatchAPZAwareEvent(&wheelEvent);
bool failedX = false;
if ((aOptions & WHEEL_EVENT_EXPECTED_OVERFLOW_DELTA_X_ZERO) &&

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

@ -92,7 +92,7 @@ nsDataDocumentContentPolicy::ShouldLoad(uint32_t aContentType,
requestingPrincipal->GetURI(getter_AddRefs(principalURI));
if (NS_SUCCEEDED(rv) && principalURI) {
nsScriptSecurityManager::ReportError(
nullptr, NS_LITERAL_STRING("CheckSameOriginError"), principalURI,
nullptr, NS_LITERAL_STRING("ExternalDataError"), principalURI,
aContentLocation);
}
}

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

@ -622,19 +622,19 @@ public:
virtual bool defineProperty(JSContext* cx,
JS::Handle<JSObject*> proxy,
JS::Handle<jsid> id,
JS::MutableHandle<JSPropertyDescriptor> desc)
const MOZ_OVERRIDE;
JS::MutableHandle<JSPropertyDescriptor> desc,
JS::ObjectOpResult &result) const MOZ_OVERRIDE;
virtual bool ownPropertyKeys(JSContext *cx,
JS::Handle<JSObject*> proxy,
JS::AutoIdVector &props) const MOZ_OVERRIDE;
virtual bool delete_(JSContext *cx, JS::Handle<JSObject*> proxy,
JS::Handle<jsid> id,
bool *bp) const MOZ_OVERRIDE;
JS::ObjectOpResult &result) const MOZ_OVERRIDE;
virtual bool enumerate(JSContext *cx, JS::Handle<JSObject*> proxy,
JS::MutableHandle<JSObject*> vp) const MOZ_OVERRIDE;
virtual bool preventExtensions(JSContext *cx,
virtual bool preventExtensions(JSContext* cx,
JS::Handle<JSObject*> proxy,
bool *succeeded) const MOZ_OVERRIDE;
JS::ObjectOpResult& result) const MOZ_OVERRIDE;
virtual bool isExtensible(JSContext *cx, JS::Handle<JSObject*> proxy, bool *extensible)
const MOZ_OVERRIDE;
virtual bool has(JSContext *cx, JS::Handle<JSObject*> proxy,
@ -646,8 +646,8 @@ public:
virtual bool set(JSContext *cx, JS::Handle<JSObject*> proxy,
JS::Handle<JSObject*> receiver,
JS::Handle<jsid> id,
bool strict,
JS::MutableHandle<JS::Value> vp) const MOZ_OVERRIDE;
JS::MutableHandle<JS::Value> vp,
JS::ObjectOpResult &result) const MOZ_OVERRIDE;
// SpiderMonkey extensions
virtual bool getPropertyDescriptor(JSContext* cx,
@ -783,8 +783,8 @@ bool
nsOuterWindowProxy::defineProperty(JSContext* cx,
JS::Handle<JSObject*> proxy,
JS::Handle<jsid> id,
JS::MutableHandle<JSPropertyDescriptor> desc)
const
JS::MutableHandle<JSPropertyDescriptor> desc,
JS::ObjectOpResult &result) const
{
int32_t index = GetArrayIndexFromId(cx, id);
if (IsArrayIndex(index)) {
@ -792,7 +792,7 @@ nsOuterWindowProxy::defineProperty(JSContext* cx,
// since we have no indexed setter or indexed creator. That means
// throwing in strict mode (FIXME: Bug 828137), doing nothing in
// non-strict mode.
return true;
return result.succeed();
}
// For now, allow chrome code to define non-configurable properties
@ -803,7 +803,7 @@ nsOuterWindowProxy::defineProperty(JSContext* cx,
return ThrowErrorMessage(cx, MSG_DEFINE_NON_CONFIGURABLE_PROP_ON_WINDOW);
}
return js::Wrapper::defineProperty(cx, proxy, id, desc);
return js::Wrapper::defineProperty(cx, proxy, id, desc, result);
}
bool
@ -825,35 +825,31 @@ nsOuterWindowProxy::ownPropertyKeys(JSContext *cx,
bool
nsOuterWindowProxy::delete_(JSContext *cx, JS::Handle<JSObject*> proxy,
JS::Handle<jsid> id, bool *bp) const
JS::Handle<jsid> id, JS::ObjectOpResult &result) const
{
if (nsCOMPtr<nsIDOMWindow> frame = GetSubframeWindow(cx, proxy, id)) {
// Reject (which means throw if strict, else return false) the delete.
// Except we don't even know whether we're strict. See bug 803157.
*bp = false;
return true;
// Fail (which means throw if strict, else return false).
return result.failCantDeleteWindowElement();
}
int32_t index = GetArrayIndexFromId(cx, id);
if (IsArrayIndex(index)) {
// Indexed, but not supported. Spec says return true.
*bp = true;
return true;
return result.succeed();
}
return js::Wrapper::delete_(cx, proxy, id, bp);
return js::Wrapper::delete_(cx, proxy, id, result);
}
bool
nsOuterWindowProxy::preventExtensions(JSContext *cx,
nsOuterWindowProxy::preventExtensions(JSContext* cx,
JS::Handle<JSObject*> proxy,
bool *succeeded) const
JS::ObjectOpResult& result) const
{
// If [[Extensible]] could be false, then navigating a window could navigate
// to a window that's [[Extensible]] after being at one that wasn't: an
// invariant violation. So never change a window's extensibility.
*succeeded = false;
return true;
return result.failCantPreventExtensions();
}
bool
@ -917,19 +913,17 @@ bool
nsOuterWindowProxy::set(JSContext *cx, JS::Handle<JSObject*> proxy,
JS::Handle<JSObject*> receiver,
JS::Handle<jsid> id,
bool strict,
JS::MutableHandle<JS::Value> vp) const
JS::MutableHandle<JS::Value> vp,
JS::ObjectOpResult &result) const
{
int32_t index = GetArrayIndexFromId(cx, id);
if (IsArrayIndex(index)) {
// Reject (which means throw if and only if strict) the set.
if (strict) {
// XXXbz This needs to throw, but see bug 828137.
}
return true;
// XXX See bug 828137.
return result.succeed();
}
return js::Wrapper::set(cx, proxy, receiver, id, strict, vp);
return js::Wrapper::set(cx, proxy, receiver, id, vp, result);
}
bool
@ -2603,7 +2597,7 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument,
SetWrapper(outerObject);
MOZ_ASSERT(js::GetObjectParent(outerObject) == newInnerGlobal);
MOZ_ASSERT(js::GetGlobalForObjectCrossCompartment(outerObject) == newInnerGlobal);
// Inform the nsJSContext, which is the canonical holder of the outer.
mContext->SetWindowProxy(outerObject);

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

@ -825,9 +825,11 @@ XPCOMObjectToJsval(JSContext* cx, JS::Handle<JSObject*> scope,
#ifdef DEBUG
JSObject* jsobj = rval.toObjectOrNull();
if (jsobj && !js::GetObjectParent(jsobj))
if (jsobj &&
js::GetGlobalForObjectCrossCompartment(jsobj) == jsobj) {
NS_ASSERTION(js::GetObjectClass(jsobj)->flags & JSCLASS_IS_GLOBAL,
"Why did we recreate this wrapper?");
}
#endif
return true;
@ -979,6 +981,27 @@ GetNativePropertyHooks(JSContext *cx, JS::Handle<JSObject*> obj,
return ifaceAndProtoJSClass->mNativeHooks;
}
static JSObject*
XrayCreateFunction(JSContext* cx, JS::Handle<JSObject*> wrapper,
JSNativeWrapper native, unsigned nargs, JS::Handle<jsid> id)
{
JSFunction* fun = js::NewFunctionByIdWithReserved(cx, native.op, nargs, 0,
/* parent = */nullptr, id);
if (!fun) {
return nullptr;
}
SET_JITINFO(fun, native.info);
JSObject* obj = JS_GetFunctionObject(fun);
js::SetFunctionNativeReserved(obj, XRAY_DOM_FUNCTION_PARENT_WRAPPER_SLOT,
JS::ObjectValue(*wrapper));
#ifdef DEBUG
js::SetFunctionNativeReserved(obj, XRAY_DOM_FUNCTION_NATIVE_SLOT_FOR_SELF,
JS::ObjectValue(*obj));
#endif
return obj;
}
static bool
XrayResolveAttribute(JSContext* cx, JS::Handle<JSObject*> wrapper,
JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
@ -1001,23 +1024,18 @@ XrayResolveAttribute(JSContext* cx, JS::Handle<JSObject*> wrapper,
// way to do this is wrap them up as functions ourselves.
desc.setAttributes(attrSpec.flags);
// They all have getters, so we can just make it.
JS::Rooted<JSFunction*> fun(cx,
JS_NewFunctionById(cx, attrSpec.getter.native.op,
0, 0, wrapper, id));
if (!fun)
JS::Rooted<JSObject*> funobj(cx,
XrayCreateFunction(cx, wrapper, attrSpec.getter.native, 0, id));
if (!funobj)
return false;
SET_JITINFO(fun, attrSpec.getter.native.info);
JSObject *funobj = JS_GetFunctionObject(fun);
desc.setGetterObject(funobj);
desc.attributesRef() |= JSPROP_GETTER;
if (attrSpec.setter.native.op) {
// We have a setter! Make it.
fun = JS_NewFunctionById(cx, attrSpec.setter.native.op, 1, 0,
wrapper, id);
if (!fun)
funobj =
XrayCreateFunction(cx, wrapper, attrSpec.setter.native, 1, id);
if (!funobj)
return false;
SET_JITINFO(fun, attrSpec.setter.native.info);
funobj = JS_GetFunctionObject(fun);
desc.setSetterObject(funobj);
desc.attributesRef() |= JSPROP_SETTER;
} else {
@ -1052,22 +1070,24 @@ XrayResolveMethod(JSContext* cx, JS::Handle<JSObject*> wrapper,
cacheOnHolder = true;
const JSFunctionSpec& methodSpec = methodSpecs[i];
JSFunction *fun;
JSObject *funobj;
if (methodSpec.selfHostedName) {
fun = JS::GetSelfHostedFunction(cx, methodSpec.selfHostedName, id, methodSpec.nargs);
JSFunction* fun =
JS::GetSelfHostedFunction(cx, methodSpec.selfHostedName, id,
methodSpec.nargs);
if (!fun) {
return false;
}
MOZ_ASSERT(!methodSpec.call.op, "Bad FunctionSpec declaration: non-null native");
MOZ_ASSERT(!methodSpec.call.info, "Bad FunctionSpec declaration: non-null jitinfo");
funobj = JS_GetFunctionObject(fun);
} else {
fun = JS_NewFunctionById(cx, methodSpec.call.op, methodSpec.nargs, 0, wrapper, id);
if (!fun) {
funobj = XrayCreateFunction(cx, wrapper, methodSpec.call,
methodSpec.nargs, id);
if (!funobj) {
return false;
}
SET_JITINFO(fun, methodSpec.call.info);
}
JSObject *funobj = JS_GetFunctionObject(fun);
desc.value().setObject(*funobj);
desc.setAttributes(methodSpec.flags);
desc.object().set(wrapper);
@ -1421,13 +1441,14 @@ XrayResolveOwnProperty(JSContext* cx, JS::Handle<JSObject*> wrapper,
bool
XrayDefineProperty(JSContext* cx, JS::Handle<JSObject*> wrapper,
JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
JS::MutableHandle<JSPropertyDescriptor> desc, bool* defined)
JS::MutableHandle<JSPropertyDescriptor> desc,
JS::ObjectOpResult &result, bool *defined)
{
if (!js::IsProxy(obj))
return true;
return true;
const DOMProxyHandler* handler = GetDOMProxyHandler(obj);
return handler->defineProperty(cx, wrapper, id, desc, defined);
return handler->defineProperty(cx, wrapper, id, desc, result, defined);
}
template<typename SpecType>
@ -1720,7 +1741,8 @@ ReparentWrapper(JSContext* aCx, JS::Handle<JSObject*> aObjArg)
const DOMJSClass* domClass = GetDOMClass(aObj);
// DOM things are always parented to globals.
JS::Rooted<JSObject*> oldParent(aCx, JS_GetParent(aObj));
JS::Rooted<JSObject*> oldParent(aCx,
js::GetGlobalForObjectCrossCompartment(aObj));
MOZ_ASSERT(js::GetGlobalForObjectCrossCompartment(oldParent) == oldParent);
JS::Rooted<JSObject*> newParent(aCx, domClass->mGetParent(aCx, aObj));

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

@ -349,7 +349,6 @@ class ProtoAndIfaceCache
}
JS::Heap<JSObject*>& EntrySlotMustExist(size_t i) {
MOZ_ASSERT((*this)[i]);
return (*this)[i];
}
@ -473,14 +472,21 @@ public:
} \
} while(0)
// Return the JSObject stored in slot i, if that slot exists. If
// the slot does not exist, return null.
JSObject* EntrySlotIfExists(size_t i) {
FORWARD_OPERATION(EntrySlotIfExists, (i));
}
// Return a reference to slot i, creating it if necessary. There
// may not be an object in the returned slot.
JS::Heap<JSObject*>& EntrySlotOrCreate(size_t i) {
FORWARD_OPERATION(EntrySlotOrCreate, (i));
}
// Return a reference to slot i, which is guaranteed to already
// exist. There may not be an object in the slot, if prototype and
// constructor initialization for one of our bindings failed.
JS::Heap<JSObject*>& EntrySlotMustExist(size_t i) {
FORWARD_OPERATION(EntrySlotMustExist, (i));
}
@ -2450,12 +2456,17 @@ XrayResolveOwnProperty(JSContext* cx, JS::Handle<JSObject*> wrapper,
* wrapper is the Xray JS object.
* obj is the target object of the Xray, a binding's instance object or a
* interface or interface prototype object.
* id and desc are the parameters for the property to be defined.
* result is the out-parameter indicating success (read it only if
* this returns true and also sets *defined to true).
* defined will be set to true if a property was set as a result of this call.
*/
bool
XrayDefineProperty(JSContext* cx, JS::Handle<JSObject*> wrapper,
JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
JS::MutableHandle<JSPropertyDescriptor> desc, bool* defined);
JS::MutableHandle<JSPropertyDescriptor> desc,
JS::ObjectOpResult &result,
bool *defined);
/**
* Add to props the property keys of all indexed or named properties of obj and
@ -2659,15 +2670,6 @@ InterfaceHasInstance(JSContext* cx, int prototypeID, int depth,
bool
ReportLenientThisUnwrappingFailure(JSContext* cx, JSObject* obj);
inline JSObject*
GetUnforgeableHolder(JSObject* aGlobal, prototypes::ID aId)
{
ProtoAndIfaceCache& protoAndIfaceCache = *GetProtoAndIfaceCache(aGlobal);
JSObject* interfaceProto = protoAndIfaceCache.EntrySlotMustExist(aId);
return &js::GetReservedSlot(interfaceProto,
DOM_INTERFACE_PROTO_SLOTS_BASE).toObject();
}
// Given a JSObject* that represents the chrome side of a JS-implemented WebIDL
// interface, get the nsIGlobalObject corresponding to the content side, if any.
// A false return means an exception was thrown.

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

@ -7684,10 +7684,7 @@ class CGResolveHook(CGAbstractBindingMethod):
// has already defined it on the object. Don't try to also
// define it.
if (!desc.value().isUndefined() &&
!JS_DefinePropertyById(cx, obj, id, desc.value(),
desc.attributes() | JSPROP_PROPOP_ACCESSORS,
JS_PROPERTYOP_GETTER(desc.getter()),
JS_PROPERTYOP_SETTER(desc.setter()))) {
!JS_DefinePropertyById(cx, obj, id, desc)) {
return false;
}
*resolvedp = true;
@ -9682,10 +9679,7 @@ class CGResolveOwnPropertyViaResolve(CGAbstractBindingMethod):
// try to also define it.
if (objDesc.object() &&
!objDesc.value().isUndefined() &&
!JS_DefinePropertyById(cx, obj, id, objDesc.value(),
objDesc.attributes() | JSPROP_PROPOP_ACCESSORS,
JS_PROPERTYOP_GETTER(objDesc.getter()),
JS_PROPERTYOP_SETTER(objDesc.setter()))) {
!JS_DefinePropertyById(cx, obj, id, objDesc)) {
return false;
}
}
@ -10221,10 +10215,13 @@ class CGDOMJSProxyHandler_getOwnPropDescriptor(ClassMethod):
class CGDOMJSProxyHandler_defineProperty(ClassMethod):
def __init__(self, descriptor):
# The usual convention is to name the ObjectOpResult out-parameter
# `result`, but that name is a bit overloaded around here.
args = [Argument('JSContext*', 'cx'),
Argument('JS::Handle<JSObject*>', 'proxy'),
Argument('JS::Handle<jsid>', 'id'),
Argument('JS::MutableHandle<JSPropertyDescriptor>', 'desc'),
Argument('JS::ObjectOpResult&', 'opresult'),
Argument('bool*', 'defined')]
ClassMethod.__init__(self, "defineProperty", "bool", args, virtual=True, override=True, const=True)
self.descriptor = descriptor
@ -10242,7 +10239,7 @@ class CGDOMJSProxyHandler_defineProperty(ClassMethod):
if (IsArrayIndex(index)) {
*defined = true;
$*{callSetter}
return true;
return opresult.succeed();
}
""",
callSetter=CGProxyIndexedSetter(self.descriptor).define())
@ -10255,7 +10252,9 @@ class CGDOMJSProxyHandler_defineProperty(ClassMethod):
set += fill(
"""
if (IsArrayIndex(GetArrayIndexFromId(cx, id))) {
return js::IsInNonStrictPropertySet(cx) || ThrowErrorMessage(cx, MSG_NO_INDEXED_SETTER, "${name}");
return js::IsInNonStrictPropertySet(cx)
? opresult.succeed()
: ThrowErrorMessage(cx, MSG_NO_INDEXED_SETTER, "${name}");
}
""",
name=self.descriptor.name)
@ -10275,7 +10274,7 @@ class CGDOMJSProxyHandler_defineProperty(ClassMethod):
*defined = true;
$*{callSetter}
return true;
return opresult.succeed();
""",
callSetter=CGProxyNamedSetter(self.descriptor).define())
else:
@ -10291,7 +10290,9 @@ class CGDOMJSProxyHandler_defineProperty(ClassMethod):
$*{presenceChecker}
if (found) {
return js::IsInNonStrictPropertySet(cx) || ThrowErrorMessage(cx, MSG_NO_NAMED_SETTER, "${name}");
return js::IsInNonStrictPropertySet(cx)
? opresult.succeed()
: ThrowErrorMessage(cx, MSG_NO_NAMED_SETTER, "${name}");
}
""",
presenceChecker=CGProxyNamedPresenceChecker(self.descriptor, foundVar="found").define(),
@ -10306,7 +10307,7 @@ class CGDOMJSProxyHandler_delete(ClassMethod):
args = [Argument('JSContext*', 'cx'),
Argument('JS::Handle<JSObject*>', 'proxy'),
Argument('JS::Handle<jsid>', 'id'),
Argument('bool*', 'bp')]
Argument('JS::ObjectOpResult&', 'opresult')]
ClassMethod.__init__(self, "delete_", "bool", args,
virtual=True, override=True, const=True)
self.descriptor = descriptor
@ -10315,6 +10316,12 @@ class CGDOMJSProxyHandler_delete(ClassMethod):
def getDeleterBody(type, foundVar=None):
"""
type should be "Named" or "Indexed"
The possible outcomes:
- an error happened (the emitted code returns false)
- own property not found (foundVar=false, deleteSucceeded=true)
- own property found and deleted (foundVar=true, deleteSucceeded=true)
- own property found but can't be deleted (foundVar=true, deleteSucceeded=false)
"""
assert type in ("Named", "Indexed")
deleter = self.descriptor.operations[type + 'Deleter']
@ -10323,29 +10330,34 @@ class CGDOMJSProxyHandler_delete(ClassMethod):
raise TypeError("Can't handle a deleter on an interface "
"that has unforgeables. Figure out how "
"that should work!")
decls = ""
if (not deleter.signatures()[0][0].isPrimitive() or
deleter.signatures()[0][0].nullable() or
deleter.signatures()[0][0].tag() != IDLType.Tags.bool):
setBp = "*bp = true;\n"
else:
decls += "bool result;\n"
# See if the deleter method is fallible.
t = deleter.signatures()[0][0]
if t.isPrimitive() and not t.nullable() and t.tag() == IDLType.Tags.bool:
# The deleter method has a boolean out-parameter. When a
# property is found, the out-param indicates whether it was
# successfully deleted.
decls = "bool result;\n"
if foundVar is None:
foundVar = "found"
decls += "bool found = false;\n"
setBp = fill(
setDS = fill(
"""
if (${foundVar}) {
*bp = result;
} else {
*bp = true;
if (!${foundVar}) {
deleteSucceeded = true;
}
""",
foundVar=foundVar)
else:
# No boolean out-parameter: if a property is found,
# deleting it always succeeds.
decls = ""
setDS = "deleteSucceeded = true;\n"
deleterClass = globals()["CGProxy%sDeleter" % type]
body = (decls +
deleterClass(self.descriptor, resultVar="result", foundVar=foundVar).define() +
setBp)
deleterClass(self.descriptor, resultVar="deleteSucceeded",
foundVar=foundVar).define() +
setDS)
elif getattr(self.descriptor, "supports%sProperties" % type)():
presenceCheckerClass = globals()["CGProxy%sPresenceChecker" % type]
foundDecl = ""
@ -10356,7 +10368,7 @@ class CGDOMJSProxyHandler_delete(ClassMethod):
"""
$*{foundDecl}
$*{presenceChecker}
*bp = !${foundVar};
deleteSucceeded = !${foundVar};
""",
foundDecl=foundDecl,
presenceChecker=presenceCheckerClass(self.descriptor, foundVar=foundVar).define(),
@ -10377,9 +10389,9 @@ class CGDOMJSProxyHandler_delete(ClassMethod):
"""
int32_t index = GetArrayIndexFromId(cx, id);
if (IsArrayIndex(index)) {
bool deleteSucceeded;
$*{indexedBody}
// We always return here, even if the property was not found
return true;
return deleteSucceeded ? opresult.succeed() : opresult.failCantDelete();
}
""",
indexedBody=indexedBody)
@ -10392,9 +10404,10 @@ class CGDOMJSProxyHandler_delete(ClassMethod):
delete += fill(
"""
bool found = false;
bool deleteSucceeded;
$*{namedBody}
if (found) {
return true;
return deleteSucceeded ? opresult.succeed() : opresult.failCantDelete();
}
""",
namedBody=namedBody)
@ -10412,7 +10425,7 @@ class CGDOMJSProxyHandler_delete(ClassMethod):
delete += dedent("""
return dom::DOMProxyHandler::delete_(cx, proxy, id, bp);
return dom::DOMProxyHandler::delete_(cx, proxy, id, opresult);
""")
return delete

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

@ -144,12 +144,11 @@ DOMProxyHandler::EnsureExpandoObject(JSContext* cx, JS::Handle<JSObject*> obj)
}
bool
DOMProxyHandler::preventExtensions(JSContext *cx, JS::Handle<JSObject*> proxy,
bool *succeeded) const
DOMProxyHandler::preventExtensions(JSContext* cx, JS::Handle<JSObject*> proxy,
JS::ObjectOpResult& result) const
{
// always extensible per WebIDL
*succeeded = false;
return true;
return result.failCantPreventExtensions();
}
bool
@ -196,18 +195,15 @@ BaseDOMProxyHandler::getOwnPropertyDescriptor(JSContext* cx,
bool
DOMProxyHandler::defineProperty(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
MutableHandle<JSPropertyDescriptor> desc, bool* defined) const
MutableHandle<JSPropertyDescriptor> desc,
JS::ObjectOpResult &result, bool *defined) const
{
if (desc.hasGetterObject() && desc.setter() == JS_StrictPropertyStub) {
return JS_ReportErrorFlagsAndNumber(cx,
JSREPORT_WARNING | JSREPORT_STRICT |
JSREPORT_STRICT_MODE_ERROR,
js::GetErrorMessage, nullptr,
JSMSG_GETTER_ONLY);
return result.failGetterOnly();
}
if (xpc::WrapperFactory::IsXrayWrapper(proxy)) {
return true;
return result.succeed();
}
JSObject* expando = EnsureExpandoObject(cx, proxy);
@ -215,13 +211,16 @@ DOMProxyHandler::defineProperty(JSContext* cx, JS::Handle<JSObject*> proxy, JS::
return false;
}
bool dummy;
return js::DefineOwnProperty(cx, expando, id, desc, &dummy);
if (!js::DefineOwnProperty(cx, expando, id, desc, result)) {
return false;
}
*defined = true;
return true;
}
bool
DOMProxyHandler::set(JSContext *cx, Handle<JSObject*> proxy, Handle<JSObject*> receiver,
Handle<jsid> id, bool strict, MutableHandle<JS::Value> vp) const
Handle<jsid> id, MutableHandle<JS::Value> vp, ObjectOpResult &result) const
{
MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy),
"Should not have a XrayWrapper here");
@ -230,7 +229,7 @@ DOMProxyHandler::set(JSContext *cx, Handle<JSObject*> proxy, Handle<JSObject*> r
return false;
}
if (done) {
return true;
return result.succeed();
}
// Make sure to ignore our named properties when checking for own
@ -255,20 +254,19 @@ DOMProxyHandler::set(JSContext *cx, Handle<JSObject*> proxy, Handle<JSObject*> r
}
return js::SetPropertyIgnoringNamedGetter(cx, this, proxy, receiver, id,
&desc, descIsOwn, strict, vp);
&desc, descIsOwn, vp, result);
}
bool
DOMProxyHandler::delete_(JSContext* cx, JS::Handle<JSObject*> proxy,
JS::Handle<jsid> id, bool* bp) const
JS::Handle<jsid> id, JS::ObjectOpResult &result) const
{
JS::Rooted<JSObject*> expando(cx);
if (!xpc::WrapperFactory::IsXrayWrapper(proxy) && (expando = GetExpandoObject(proxy))) {
return JS_DeletePropertyById2(cx, expando, id, bp);
return JS_DeletePropertyById(cx, expando, id, result);
}
*bp = true;
return true;
return result.succeed();
}
bool

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

@ -105,24 +105,25 @@ public:
{}
bool defineProperty(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
JS::MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE
JS::MutableHandle<JSPropertyDescriptor> desc,
JS::ObjectOpResult &result) const MOZ_OVERRIDE
{
bool unused;
return defineProperty(cx, proxy, id, desc, &unused);
return defineProperty(cx, proxy, id, desc, result, &unused);
}
virtual bool defineProperty(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
JS::MutableHandle<JSPropertyDescriptor> desc, bool* defined)
const;
bool delete_(JSContext* cx, JS::Handle<JSObject*> proxy,
JS::Handle<jsid> id, bool* bp) const MOZ_OVERRIDE;
bool preventExtensions(JSContext *cx, JS::Handle<JSObject*> proxy,
bool *succeeded) const MOZ_OVERRIDE;
JS::MutableHandle<JSPropertyDescriptor> desc,
JS::ObjectOpResult &result, bool *defined) const;
bool delete_(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
JS::ObjectOpResult &result) const MOZ_OVERRIDE;
bool preventExtensions(JSContext* cx, JS::Handle<JSObject*> proxy,
JS::ObjectOpResult& result) const MOZ_OVERRIDE;
bool isExtensible(JSContext *cx, JS::Handle<JSObject*> proxy, bool *extensible)
const MOZ_OVERRIDE;
bool has(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
bool* bp) const MOZ_OVERRIDE;
bool set(JSContext *cx, JS::Handle<JSObject*> proxy, JS::Handle<JSObject*> receiver,
JS::Handle<jsid> id, bool strict, JS::MutableHandle<JS::Value> vp)
JS::Handle<jsid> id, JS::MutableHandle<JS::Value> vp, JS::ObjectOpResult &result)
const MOZ_OVERRIDE;
/*

42
dom/cache/Manager.cpp поставляемый
Просмотреть файл

@ -6,6 +6,7 @@
#include "mozilla/dom/cache/Manager.h"
#include "mozilla/AutoRestore.h"
#include "mozilla/Mutex.h"
#include "mozilla/StaticMutex.h"
#include "mozilla/StaticPtr.h"
@ -197,9 +198,7 @@ public:
MOZ_ALWAYS_TRUE(sFactory->mManagerList.RemoveElement(aManager));
// clean up the factory singleton if there are no more managers
if (sFactory->mManagerList.IsEmpty()) {
DestroyInstance();
}
MaybeDestroyInstance();
}
static void
@ -236,6 +235,7 @@ public:
private:
Factory()
: mInSyncShutdown(false)
{
MOZ_COUNT_CTOR(cache::Manager::Factory);
}
@ -244,6 +244,7 @@ private:
{
MOZ_COUNT_DTOR(cache::Manager::Factory);
MOZ_ASSERT(mManagerList.IsEmpty());
MOZ_ASSERT(!mInSyncShutdown);
}
static nsresult
@ -264,7 +265,7 @@ private:
// Cannot use ClearOnShutdown() because we're on the background thread.
// This is automatically cleared when Factory::Remove() calls
// DestroyInstance().
// MaybeDestroyInstance().
MOZ_ASSERT(!sBackgroundThread);
sBackgroundThread = NS_GetCurrentThread();
}
@ -285,11 +286,19 @@ private:
}
static void
DestroyInstance()
MaybeDestroyInstance()
{
mozilla::ipc::AssertIsOnBackgroundThread();
MOZ_ASSERT(sFactory);
// If the factory is is still in use then we cannot delete yet. This
// could be due to managers still existing or because we are in the
// middle of shutting down. We need to be careful not to delete ourself
// synchronously during shutdown.
if (!sFactory->mManagerList.IsEmpty() || sFactory->mInSyncShutdown) {
return;
}
// Be clear about what we are locking. sFactory is bg thread only, so
// we don't need to lock it here. Just protect sBackgroundThread.
{
@ -320,11 +329,21 @@ private:
MOZ_ASSERT(!sFactory->mManagerList.IsEmpty());
ManagerList::ForwardIterator iter(sFactory->mManagerList);
while (iter.HasMore()) {
nsRefPtr<Manager> manager = iter.GetNext();
manager->Shutdown();
{
// Note that we are synchronously calling shutdown code here. If any
// of the shutdown code synchronously decides to delete the Factory
// we need to delay that delete until the end of this method.
AutoRestore<bool> restore(sFactory->mInSyncShutdown);
sFactory->mInSyncShutdown = true;
ManagerList::ForwardIterator iter(sFactory->mManagerList);
while (iter.HasMore()) {
nsRefPtr<Manager> manager = iter.GetNext();
manager->Shutdown();
}
}
MaybeDestroyInstance();
}
class ShutdownAllRunnable MOZ_FINAL : public nsRunnable
@ -363,6 +382,11 @@ private:
// PBackground thread only.
typedef nsTObserverArray<Manager*> ManagerList;
ManagerList mManagerList;
// This flag is set when we are looping through the list and calling
// Shutdown() on each Manager. We need to be careful not to synchronously
// trigger the deletion of the factory while still executing this loop.
bool mInSyncShutdown;
};
// static

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

@ -87,6 +87,7 @@
#include "mozilla/LookAndFeel.h"
#include "GeckoProfiler.h"
#include "Units.h"
#include "mozilla/layers/APZCTreeManager.h"
#ifdef XP_MACOSX
#import <ApplicationServices/ApplicationServices.h>
@ -2020,6 +2021,21 @@ GetParentFrameToScroll(nsIFrame* aFrame)
return aFrame->GetParent();
}
/*static*/ bool
EventStateManager::CanVerticallyScrollFrameWithWheel(nsIFrame* aFrame)
{
nsIContent* c = aFrame->GetContent();
if (!c) {
return true;
}
nsCOMPtr<nsITextControlElement> ctrl =
do_QueryInterface(c->IsInAnonymousSubtree() ? c->GetBindingParent() : c);
if (ctrl && ctrl->IsSingleLineTextControl()) {
return false;
}
return true;
}
void
EventStateManager::DispatchLegacyMouseScrollEvents(nsIFrame* aTargetFrame,
WidgetWheelEvent* aEvent,
@ -2288,10 +2304,7 @@ EventStateManager::ComputeScrollTarget(nsIFrame* aTargetFrame,
// Don't scroll vertically by mouse-wheel on a single-line text control.
if (checkIfScrollableY) {
nsIContent* c = scrollFrame->GetContent();
nsCOMPtr<nsITextControlElement> ctrl =
do_QueryInterface(c->IsInAnonymousSubtree() ? c->GetBindingParent() : c);
if (ctrl && ctrl->IsSingleLineTextControl()) {
if (!CanVerticallyScrollFrameWithWheel(scrollFrame)) {
continue;
}
}
@ -3007,7 +3020,18 @@ EventStateManager::PostHandleEvent(nsPresContext* aPresContext,
}
WidgetWheelEvent* wheelEvent = aEvent->AsWheelEvent();
switch (WheelPrefs::GetInstance()->ComputeActionFor(wheelEvent)) {
// When APZ is enabled, the actual scroll animation might be handled by
// the compositor.
WheelPrefs::Action action;
if (gfxPrefs::AsyncPanZoomEnabled() &&
layers::APZCTreeManager::WillHandleWheelEvent(wheelEvent))
{
action = WheelPrefs::ACTION_NONE;
} else {
action = WheelPrefs::GetInstance()->ComputeActionFor(wheelEvent);
}
switch (action) {
case WheelPrefs::ACTION_SCROLL: {
// For scrolling of default action, we should honor the mouse wheel
// transaction.

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

@ -225,6 +225,10 @@ public:
// Returns true if the given WidgetWheelEvent will resolve to a scroll action.
static bool WheelEventIsScrollAction(WidgetWheelEvent* aEvent);
// Returns whether or not a frame can be vertically scrolled with a mouse
// wheel (as opposed to, say, a selection or touch scroll).
static bool CanVerticallyScrollFrameWithWheel(nsIFrame* aFrame);
// Holds the point in screen coords that a mouse event was dispatched to,
// before we went into pointer lock mode. This is constantly updated while
// the pointer is not locked, but we don't update it while the pointer is

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

@ -131,6 +131,7 @@ skip-if = buildapp == 'mulet' || buildapp == 'b2g' # b2g(failing when the test g
[test_bug944011.html]
[test_bug944847.html]
[test_bug946632.html]
skip-if = buildapp == 'b2g'
[test_bug967796.html]
skip-if = toolkit == "gonk" || e10s
[test_bug985988.html]
@ -165,7 +166,7 @@ skip-if = toolkit == 'android' #CRASH_DUMP, RANDOM
skip-if = buildapp == 'mulet'
[test_messageEvent.html]
[test_moz_mouse_pixel_scroll_event.html]
skip-if = buildapp == 'mulet'
skip-if = buildapp == 'mulet' || buildapp == 'b2g' # bug 1126090, no wheel events on b2g
[test_onerror_handler_args.html]
[test_wheel_default_action.html]
skip-if = buildapp == 'mulet' || buildapp == 'b2g' || e10s

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

@ -7,6 +7,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=946632
<title>Test for bug 946632 - propagate mouse-wheel vertical scroll events to container</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<style>
.scrollable {
@ -125,7 +126,10 @@ function nextTest()
var test = tests[i];
++i;
prepare(test.check);
test.test();
window.waitForAllPaintsFlushed(function() {
test.test();
});
}
function runTests()

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

@ -200,11 +200,11 @@ GetJSValFromKeyPathString(JSContext* aCx,
if (targetObject) {
// If this fails, we lose, and the web page sees a magical property
// appear on the object :-(
bool succeeded;
if (!JS_DeleteUCProperty2(aCx, targetObject,
targetObjectPropName.get(),
targetObjectPropName.Length(),
&succeeded)) {
JS::ObjectOpResult succeeded;
if (!JS_DeleteUCProperty(aCx, targetObject,
targetObjectPropName.get(),
targetObjectPropName.Length(),
succeeded)) {
IDB_REPORT_INTERNAL_ERR();
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}

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

@ -484,6 +484,8 @@ parent:
*/
async SetDimensions(uint32_t aFlags, int32_t aX, int32_t aY, int32_t aCx, int32_t aCy);
prio(high) sync SynthesizedMouseWheelEvent(WidgetWheelEvent event);
child:
/**
* Notify the remote browser that it has been Show()n on this

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

@ -255,7 +255,6 @@ public:
void OnRemoteBrowserFrameShown(nsISupports* aSubject);
void OnTabParentDestroyed(nsISupports* aSubject);
void OnFrameloaderVisibleChanged(nsISupports* aSubject);
void OnChannelConnected(nsISupports* aSubject);
ProcessPriority CurrentPriority();
ProcessPriority ComputePriority();
@ -566,7 +565,6 @@ ProcessPriorityManagerImpl::Notify(const WakeLockInformation& aInfo)
}
}
NS_IMPL_ISUPPORTS(ParticularProcessPriorityManager,
nsIObserver,
nsITimerCallback,
@ -847,7 +845,6 @@ ParticularProcessPriorityManager::ScheduleResetPriority(const char* aTimeoutPref
mResetPriorityTimer->InitWithCallback(this, timeout, nsITimer::TYPE_ONE_SHOT);
}
NS_IMETHODIMP
ParticularProcessPriorityManager::Notify(nsITimer* aTimer)
{

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

@ -1232,6 +1232,21 @@ bool TabParent::SendMouseWheelEvent(WidgetWheelEvent& event)
return PBrowserParent::SendMouseWheelEvent(event, guid, blockId);
}
bool TabParent::RecvSynthesizedMouseWheelEvent(const mozilla::WidgetWheelEvent& aEvent)
{
nsCOMPtr<nsIWidget> widget = GetWidget();
if (!widget) {
return true;
}
WidgetWheelEvent localEvent(aEvent);
localEvent.widget = widget;
localEvent.refPoint -= GetChildProcessOffset();
widget->DispatchAPZAwareEvent(&localEvent);
return true;
}
static void
DoCommandCallback(mozilla::Command aCommand, void* aData)
{

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

@ -231,6 +231,7 @@ public:
const bool& aPreventDefault) MOZ_OVERRIDE;
virtual bool RecvSetTargetAPZC(const uint64_t& aInputBlockId,
nsTArray<ScrollableLayerGuid>&& aTargets) MOZ_OVERRIDE;
virtual bool RecvSynthesizedMouseWheelEvent(const mozilla::WidgetWheelEvent& aEvent) MOZ_OVERRIDE;
virtual PColorPickerParent*
AllocPColorPickerParent(const nsString& aTitle, const nsString& aInitialColor) MOZ_OVERRIDE;

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

@ -4,6 +4,7 @@
CheckMessage = Remember this decision
CheckLoadURIError = Security Error: Content at %S may not load or link to %S.
CheckSameOriginError = Security Error: Content at %S may not load data from %S.
ExternalDataError = Security Error: Content at %S attempted to load %S, but may not load external data when being used as an image.
# LOCALIZATION NOTE (GetPropertyDeniedOrigins):
# %1$S is the origin of the script which was denied access.

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

@ -104,12 +104,6 @@ public:
// Set the media end time in microseconds
virtual void SetMediaEndTime(int64_t aTime) = 0;
// Make the decoder state machine update the playback position. Called by
// the reader on the decoder thread (Assertions for this checked by
// mDecoderStateMachine). This must be called with the decode monitor
// held.
virtual void UpdatePlaybackPosition(int64_t aTime) = 0;
// May be called by the reader to notify this decoder that the metadata from
// the media file has been read. Call on the decode thread only.
virtual void OnReadMetadataCompleted() = 0;

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

@ -483,7 +483,7 @@ void MediaDecoder::UpdateStreamBlockingForStateMachinePlaying()
void MediaDecoder::RecreateDecodedStream(int64_t aStartTimeUSecs)
{
MOZ_ASSERT(NS_IsMainThread());
GetReentrantMonitor().AssertCurrentThreadIn();
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
DECODER_LOG("RecreateDecodedStream aStartTimeUSecs=%lld!", aStartTimeUSecs);
DestroyDecodedStream();
@ -794,6 +794,7 @@ nsresult MediaDecoder::Seek(double aTime, SeekTarget::Type aSeekType)
// If we are already in the seeking state, the new seek overrides the old one.
if (mPlayState != PLAY_STATE_LOADING) {
mSeekRequest.DisconnectIfExists();
bool paused = false;
if (mOwner) {
paused = mOwner->GetPaused();
@ -1209,43 +1210,7 @@ void MediaDecoder::UpdateReadyStateForData()
mOwner->UpdateReadyStateForData(frameStatus);
}
void MediaDecoder::SeekingStopped(MediaDecoderEventVisibility aEventVisibility)
{
MOZ_ASSERT(NS_IsMainThread());
if (mShuttingDown)
return;
bool seekWasAborted = false;
{
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
// An additional seek was requested while the current seek was
// in operation.
if (mRequestedSeekTarget.IsValid()) {
ChangeState(PLAY_STATE_SEEKING);
seekWasAborted = true;
} else {
UnpinForSeek();
if (aEventVisibility != MediaDecoderEventVisibility::Suppressed) {
ChangeState(mNextState);
}
}
}
PlaybackPositionChanged(aEventVisibility);
if (mOwner) {
UpdateReadyStateForData();
if (!seekWasAborted && (aEventVisibility != MediaDecoderEventVisibility::Suppressed)) {
mOwner->SeekCompleted();
}
}
}
// This is called when seeking stopped *and* we're at the end of the
// media.
void MediaDecoder::SeekingStoppedAtEnd(MediaDecoderEventVisibility aEventVisibility)
void MediaDecoder::OnSeekResolvedInternal(bool aAtEnd, MediaDecoderEventVisibility aEventVisibility)
{
MOZ_ASSERT(NS_IsMainThread());
@ -1264,8 +1229,12 @@ void MediaDecoder::SeekingStoppedAtEnd(MediaDecoderEventVisibility aEventVisibil
seekWasAborted = true;
} else {
UnpinForSeek();
fireEnded = true;
ChangeState(PLAY_STATE_ENDED);
fireEnded = aAtEnd;
if (aAtEnd) {
ChangeState(PLAY_STATE_ENDED);
} else if (aEventVisibility != MediaDecoderEventVisibility::Suppressed) {
ChangeState(aAtEnd ? PLAY_STATE_ENDED : mNextState);
}
}
}
@ -1348,7 +1317,11 @@ void MediaDecoder::ApplyStateToStateMachine(PlayState aState)
mDecoderStateMachine->Play();
break;
case PLAY_STATE_SEEKING:
mDecoderStateMachine->Seek(mRequestedSeekTarget);
mSeekRequest.Begin(ProxyMediaCall(mDecoderStateMachine->GetStateMachineThread(),
mDecoderStateMachine.get(), __func__,
&MediaDecoderStateMachine::Seek, mRequestedSeekTarget)
->RefableThen(NS_GetCurrentThread(), __func__, this,
&MediaDecoder::OnSeekResolved, &MediaDecoder::OnSeekRejected));
mRequestedSeekTarget.Reset();
break;
default:
@ -1661,11 +1634,6 @@ void MediaDecoder::NotifyDataArrived(const char* aBuffer, uint32_t aLength, int6
UpdateReadyStateForData();
}
void MediaDecoder::UpdatePlaybackPosition(int64_t aTime)
{
mDecoderStateMachine->UpdatePlaybackPosition(aTime);
}
// Provide access to the state machine object
MediaDecoderStateMachine* MediaDecoder::GetStateMachine() const {
return mDecoderStateMachine;

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

@ -189,6 +189,7 @@ destroying the MediaDecoder object.
#include "nsIObserver.h"
#include "nsAutoPtr.h"
#include "nsITimer.h"
#include "MediaPromise.h"
#include "MediaResource.h"
#include "mozilla/dom/AudioChannelBinding.h"
#include "mozilla/gfx/Rect.h"
@ -273,6 +274,14 @@ class MediaDecoder : public nsIObserver,
public AbstractMediaDecoder
{
public:
struct SeekResolveValue {
SeekResolveValue(bool aAtEnd, MediaDecoderEventVisibility aEventVisibility)
: mAtEnd(aAtEnd), mEventVisibility(aEventVisibility) {}
bool mAtEnd;
MediaDecoderEventVisibility mEventVisibility;
};
typedef MediaPromise<SeekResolveValue, bool /* aIgnored */, /* IsExclusive = */ true> SeekPromise;
class DecodedStreamGraphListener;
NS_DECL_THREADSAFE_ISUPPORTS
@ -733,12 +742,6 @@ public:
// to buffer, given the current download and playback rates.
bool CanPlayThrough();
// Make the decoder state machine update the playback position. Called by
// the reader on the decoder thread (Assertions for this checked by
// mDecoderStateMachine). This must be called with the decode monitor
// held.
void UpdatePlaybackPosition(int64_t aTime) MOZ_FINAL MOZ_OVERRIDE;
void SetAudioChannel(dom::AudioChannel aChannel) { mAudioChannel = aChannel; }
dom::AudioChannel GetAudioChannel() { return mAudioChannel; }
@ -801,13 +804,22 @@ public:
// Call on the main thread only.
void PlaybackEnded();
// Seeking has stopped. Inform the element on the main
// thread.
void SeekingStopped(MediaDecoderEventVisibility aEventVisibility = MediaDecoderEventVisibility::Observable);
void OnSeekRejected() { mSeekRequest.Complete(); }
void OnSeekResolvedInternal(bool aAtEnd, MediaDecoderEventVisibility aEventVisibility);
// Seeking has stopped at the end of the resource. Inform the element on the main
// thread.
void SeekingStoppedAtEnd(MediaDecoderEventVisibility aEventVisibility = MediaDecoderEventVisibility::Observable);
void OnSeekResolved(SeekResolveValue aVal)
{
mSeekRequest.Complete();
OnSeekResolvedInternal(aVal.mAtEnd, aVal.mEventVisibility);
}
#ifdef MOZ_AUDIO_OFFLOAD
// Temporary hack - see bug 1139206.
void SimulateSeekResolvedForAudioOffload(MediaDecoderEventVisibility aEventVisibility)
{
OnSeekResolvedInternal(false, aEventVisibility);
}
#endif
// Seeking has started. Inform the element on the main
// thread.
@ -1140,6 +1152,8 @@ protected:
// been requested. When a seek is started this is reset to invalid.
SeekTarget mRequestedSeekTarget;
MediaPromiseConsumerHolder<SeekPromise> mSeekRequest;
// True when seeking or otherwise moving the play position around in
// such a manner that progress event data is inaccurate. This is set
// during seek and duration operations to prevent the progress indicator

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

@ -106,11 +106,14 @@ public:
}
// Resets all state related to decoding, emptying all buffers etc.
// Cancels all pending Request*Data() request callbacks, and flushes the
// decode pipeline. The decoder must not call any of the callbacks for
// outstanding Request*Data() calls after this is called. Calls to
// Request*Data() made after this should be processed as usual.
// Cancels all pending Request*Data() request callbacks, rejects any
// outstanding seek promises, and flushes the decode pipeline. The
// decoder must not call any of the callbacks for outstanding
// Request*Data() calls after this is called. Calls to Request*Data()
// made after this should be processed as usual.
//
// Normally this call preceedes a Seek() call, or shutdown.
//
// The first samples of every stream produced after a ResetDecode() call
// *must* be marked as "discontinuities". If it's not, seeking work won't
// properly!
@ -165,15 +168,6 @@ public:
virtual nsRefPtr<SeekPromise>
Seek(int64_t aTime, int64_t aEndTime) = 0;
// Cancels an ongoing seek, if any. Any previously-requested seek is
// guaranteeed to be resolved or rejected in finite time, though no
// guarantees are made about precise nature of the resolve/reject, since the
// promise might have already dispatched a resolution or an error code before
// the cancel arrived.
//
// Must be called on the decode task queue.
virtual void CancelSeek() { };
// Called to move the reader into idle state. When the reader is
// created it is assumed to be active (i.e. not idle). When the media
// element is paused and we don't need to decode any more data, the state

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

@ -238,8 +238,6 @@ MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder,
mDropAudioUntilNextDiscontinuity(false),
mDropVideoUntilNextDiscontinuity(false),
mDecodeToSeekTarget(false),
mWaitingForDecoderSeek(false),
mCancelingSeek(false),
mCurrentTimeBeforeSeek(0),
mLastFrameStatus(MediaDecoderOwner::NEXT_FRAME_UNINITIALIZED),
mDecodingFrozenAtStateDecoding(false),
@ -682,10 +680,10 @@ MediaDecoderStateMachine::IsAudioSeekComplete()
{
AssertCurrentThreadInMonitor();
SAMPLE_LOG("IsAudioSeekComplete() curTarVal=%d mAudDis=%d aqFin=%d aqSz=%d",
mCurrentSeekTarget.IsValid(), mDropAudioUntilNextDiscontinuity, AudioQueue().IsFinished(), AudioQueue().GetSize());
mCurrentSeek.Exists(), mDropAudioUntilNextDiscontinuity, AudioQueue().IsFinished(), AudioQueue().GetSize());
return
!HasAudio() ||
(mCurrentSeekTarget.IsValid() &&
(mCurrentSeek.Exists() &&
!mDropAudioUntilNextDiscontinuity &&
(AudioQueue().IsFinished() || AudioQueue().GetSize() > 0));
}
@ -695,10 +693,10 @@ MediaDecoderStateMachine::IsVideoSeekComplete()
{
AssertCurrentThreadInMonitor();
SAMPLE_LOG("IsVideoSeekComplete() curTarVal=%d mVidDis=%d vqFin=%d vqSz=%d",
mCurrentSeekTarget.IsValid(), mDropVideoUntilNextDiscontinuity, VideoQueue().IsFinished(), VideoQueue().GetSize());
mCurrentSeek.Exists(), mDropVideoUntilNextDiscontinuity, VideoQueue().IsFinished(), VideoQueue().GetSize());
return
!HasVideo() ||
(mCurrentSeekTarget.IsValid() &&
(mCurrentSeek.Exists() &&
!mDropVideoUntilNextDiscontinuity &&
(VideoQueue().IsFinished() || VideoQueue().GetSize() > 0));
}
@ -746,7 +744,7 @@ MediaDecoderStateMachine::OnAudioDecoded(AudioData* aAudioSample)
}
case DECODER_STATE_SEEKING: {
if (!mCurrentSeekTarget.IsValid()) {
if (!mCurrentSeek.Exists()) {
// We've received a sample from a previous decode. Discard it.
return;
}
@ -756,8 +754,8 @@ MediaDecoderStateMachine::OnAudioDecoded(AudioData* aAudioSample)
if (!mDropAudioUntilNextDiscontinuity) {
// We must be after the discontinuity; we're receiving samples
// at or after the seek target.
if (mCurrentSeekTarget.mType == SeekTarget::PrevSyncPoint &&
mCurrentSeekTarget.mTime > mCurrentTimeBeforeSeek &&
if (mCurrentSeek.mTarget.mType == SeekTarget::PrevSyncPoint &&
mCurrentSeek.mTarget.mTime > mCurrentTimeBeforeSeek &&
audio->mTime < mCurrentTimeBeforeSeek) {
// We are doing a fastSeek, but we ended up *before* the previous
// playback position. This is surprising UX, so switch to an accurate
@ -765,9 +763,9 @@ MediaDecoderStateMachine::OnAudioDecoded(AudioData* aAudioSample)
// spec, fastSeek should always be fast, but until we get the time to
// change all Readers to seek to the keyframe after the currentTime
// in this case, we'll just decode forward. Bug 1026330.
mCurrentSeekTarget.mType = SeekTarget::Accurate;
mCurrentSeek.mTarget.mType = SeekTarget::Accurate;
}
if (mCurrentSeekTarget.mType == SeekTarget::PrevSyncPoint) {
if (mCurrentSeek.mTarget.mType == SeekTarget::PrevSyncPoint) {
// Non-precise seek; we can stop the seek at the first sample.
AudioQueue().Push(audio);
} else {
@ -867,7 +865,7 @@ MediaDecoderStateMachine::OnNotDecoded(MediaData::Type aType,
// state.
MOZ_ASSERT(aReason == MediaDecoderReader::END_OF_STREAM);
if (!isAudio && mState == DECODER_STATE_SEEKING &&
mCurrentSeekTarget.IsValid() && mFirstVideoFrameAfterSeek) {
mCurrentSeek.Exists() && mFirstVideoFrameAfterSeek) {
// Null sample. Hit end of stream. If we have decoded a frame,
// insert it into the queue so that we have something to display.
// We make sure to do this before invoking VideoQueue().Finish()
@ -902,7 +900,7 @@ MediaDecoderStateMachine::OnNotDecoded(MediaData::Type aType,
return;
}
case DECODER_STATE_SEEKING: {
if (!mCurrentSeekTarget.IsValid()) {
if (!mCurrentSeek.Exists()) {
// We've received a sample from a previous decode. Discard it.
return;
}
@ -1005,7 +1003,7 @@ MediaDecoderStateMachine::OnVideoDecoded(VideoData* aVideoSample)
return;
}
case DECODER_STATE_SEEKING: {
if (!mCurrentSeekTarget.IsValid()) {
if (!mCurrentSeek.Exists()) {
// We've received a sample from a previous decode. Discard it.
return;
}
@ -1017,8 +1015,8 @@ MediaDecoderStateMachine::OnVideoDecoded(VideoData* aVideoSample)
if (!mDropVideoUntilNextDiscontinuity) {
// We must be after the discontinuity; we're receiving samples
// at or after the seek target.
if (mCurrentSeekTarget.mType == SeekTarget::PrevSyncPoint &&
mCurrentSeekTarget.mTime > mCurrentTimeBeforeSeek &&
if (mCurrentSeek.mTarget.mType == SeekTarget::PrevSyncPoint &&
mCurrentSeek.mTarget.mTime > mCurrentTimeBeforeSeek &&
video->mTime < mCurrentTimeBeforeSeek) {
// We are doing a fastSeek, but we ended up *before* the previous
// playback position. This is surprising UX, so switch to an accurate
@ -1026,9 +1024,9 @@ MediaDecoderStateMachine::OnVideoDecoded(VideoData* aVideoSample)
// spec, fastSeek should always be fast, but until we get the time to
// change all Readers to seek to the keyframe after the currentTime
// in this case, we'll just decode forward. Bug 1026330.
mCurrentSeekTarget.mType = SeekTarget::Accurate;
mCurrentSeek.mTarget.mType = SeekTarget::Accurate;
}
if (mCurrentSeekTarget.mType == SeekTarget::PrevSyncPoint) {
if (mCurrentSeek.mTarget.mType == SeekTarget::PrevSyncPoint) {
// Non-precise seek; we can stop the seek at the first sample.
VideoQueue().Push(video);
} else {
@ -1154,10 +1152,7 @@ nsresult MediaDecoderStateMachine::Init(MediaDecoderStateMachine* aCloneDonor)
void MediaDecoderStateMachine::StopPlayback()
{
// XXXbholley - Needed because DecodeSeek runs on the decode thread.
// Once bug 1135170 lands, this becomes state-machine only, and we can invoke
// DispatchDecodeTasksIfNeeded directly.
MOZ_ASSERT(OnStateMachineThread() || OnDecodeThread());
MOZ_ASSERT(OnStateMachineThread());
DECODER_LOG("StopPlayback()");
AssertCurrentThreadInMonitor();
@ -1174,9 +1169,7 @@ void MediaDecoderStateMachine::StopPlayback()
NS_ASSERTION(!IsPlaying(), "Should report not playing at end of StopPlayback()");
mDecoder->UpdateStreamBlockingForStateMachinePlaying();
nsCOMPtr<nsIRunnable> event =
NS_NewRunnableMethod(this, &MediaDecoderStateMachine::AcquireMonitorAndInvokeDispatchDecodeTasksIfNeeded);
GetStateMachineThread()->Dispatch(event, NS_DISPATCH_NORMAL);
DispatchDecodeTasksIfNeeded();
}
void MediaDecoderStateMachine::MaybeStartPlayback()
@ -1217,9 +1210,7 @@ void MediaDecoderStateMachine::MaybeStartPlayback()
void MediaDecoderStateMachine::UpdatePlaybackPositionInternal(int64_t aTime)
{
// XXXbholley - Needed because DecodeSeek runs on the decode thread.
// Once bug 1135170 lands, this becomes state-machine only.
MOZ_ASSERT(OnStateMachineThread() || OnDecodeThread());
MOZ_ASSERT(OnStateMachineThread());
SAMPLE_LOG("UpdatePlaybackPositionInternal(%lld) (mStartTime=%lld)", aTime, mStartTime);
AssertCurrentThreadInMonitor();
@ -1472,24 +1463,30 @@ void MediaDecoderStateMachine::SetDormant(bool aDormant)
if (aDormant) {
if (mState == DECODER_STATE_SEEKING) {
if (mQueuedSeekTarget.IsValid()) {
if (mQueuedSeek.Exists()) {
// Keep latest seek target
} else if (mSeekTarget.IsValid()) {
mQueuedSeekTarget = mSeekTarget;
} else if (mCurrentSeekTarget.IsValid()) {
mQueuedSeekTarget = mCurrentSeekTarget;
} else if (mPendingSeek.Exists()) {
mQueuedSeek.Steal(mPendingSeek);
} else if (mCurrentSeek.Exists()) {
mQueuedSeek.Steal(mCurrentSeek);
} else {
mQueuedSeekTarget = SeekTarget(mCurrentFrameTime,
SeekTarget::Accurate,
MediaDecoderEventVisibility::Suppressed);
mQueuedSeek.mTarget = SeekTarget(mCurrentFrameTime,
SeekTarget::Accurate,
MediaDecoderEventVisibility::Suppressed);
// XXXbholley - Nobody is listening to this promise. Do we need to pass it
// back to MediaDecoder when we come out of dormant?
nsRefPtr<MediaDecoder::SeekPromise> unused = mQueuedSeek.mPromise.Ensure(__func__);
}
} else {
mQueuedSeekTarget = SeekTarget(mCurrentFrameTime,
SeekTarget::Accurate,
MediaDecoderEventVisibility::Suppressed);
mQueuedSeek.mTarget = SeekTarget(mCurrentFrameTime,
SeekTarget::Accurate,
MediaDecoderEventVisibility::Suppressed);
// XXXbholley - Nobody is listening to this promise. Do we need to pass it
// back to MediaDecoder when we come out of dormant?
nsRefPtr<MediaDecoder::SeekPromise> unused = mQueuedSeek.mPromise.Ensure(__func__);
}
mSeekTarget.Reset();
mCurrentSeekTarget.Reset();
mPendingSeek.RejectIfExists(__func__);
mCurrentSeek.RejectIfExists(__func__);
ScheduleStateMachine();
SetState(DECODER_STATE_DORMANT);
mDecoder->GetReentrantMonitor().NotifyAll();
@ -1623,9 +1620,7 @@ void MediaDecoderStateMachine::PlayInternal()
void MediaDecoderStateMachine::ResetPlayback()
{
// XXXbholley - Needed because DecodeSeek runs on the decode thread.
// Once bug 1135170 lands, this becomes state-machine only.
MOZ_ASSERT(OnStateMachineThread() || OnDecodeThread());
MOZ_ASSERT(OnStateMachineThread());
// We should be reseting because we're seeking, shutting down, or
// entering dormant state. We could also be in the process of going dormant,
@ -1685,22 +1680,23 @@ void MediaDecoderStateMachine::NotifyDataArrived(const char* aBuffer,
}
}
void MediaDecoderStateMachine::Seek(const SeekTarget& aTarget)
nsRefPtr<MediaDecoder::SeekPromise>
MediaDecoderStateMachine::Seek(SeekTarget aTarget)
{
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
MOZ_ASSERT(OnStateMachineThread());
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
mDecodingFrozenAtStateDecoding = false;
if (mState == DECODER_STATE_SHUTDOWN) {
return;
return MediaDecoder::SeekPromise::CreateAndReject(/* aIgnored = */ true, __func__);
}
// We need to be able to seek both at a transport level and at a media level
// to seek.
if (!mDecoder->IsMediaSeekable()) {
DECODER_WARN("Seek() function should not be called on a non-seekable state machine");
return;
return MediaDecoder::SeekPromise::CreateAndReject(/* aIgnored = */ true, __func__);
}
NS_ASSERTION(mState > DECODER_STATE_DECODING_METADATA,
@ -1708,76 +1704,24 @@ void MediaDecoderStateMachine::Seek(const SeekTarget& aTarget)
if (mState < DECODER_STATE_DECODING) {
DECODER_LOG("Seek() Not Enough Data to continue at this stage, queuing seek");
mQueuedSeekTarget = aTarget;
return;
mQueuedSeek.RejectIfExists(__func__);
mQueuedSeek.mTarget = aTarget;
return mQueuedSeek.mPromise.Ensure(__func__);
}
mQueuedSeekTarget.Reset();
mQueuedSeek.RejectIfExists(__func__);
mPendingSeek.RejectIfExists(__func__);
mPendingSeek.mTarget = aTarget;
StartSeek(aTarget);
}
void
MediaDecoderStateMachine::EnqueueStartQueuedSeekTask()
{
nsCOMPtr<nsIRunnable> event =
NS_NewRunnableMethod(this, &MediaDecoderStateMachine::StartQueuedSeek);
NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
}
void
MediaDecoderStateMachine::StartQueuedSeek()
{
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
if (!mQueuedSeekTarget.IsValid()) {
return;
}
StartSeek(mQueuedSeekTarget);
mQueuedSeekTarget.Reset();
}
void
MediaDecoderStateMachine::StartSeek(const SeekTarget& aTarget)
{
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
AssertCurrentThreadInMonitor();
MOZ_ASSERT(mState >= DECODER_STATE_DECODING);
if (mState == DECODER_STATE_SHUTDOWN) {
return;
}
// Bound the seek time to be inside the media range.
int64_t end = GetEndTime();
NS_ASSERTION(mStartTime != -1, "Should know start time by now");
NS_ASSERTION(end != -1, "Should know end time by now");
int64_t seekTime = aTarget.mTime + mStartTime;
seekTime = std::min(seekTime, end);
seekTime = std::max(mStartTime, seekTime);
NS_ASSERTION(seekTime >= mStartTime && seekTime <= end,
"Can only seek in range [0,duration]");
mSeekTarget = SeekTarget(seekTime, aTarget.mType, aTarget.mEventVisibility);
DECODER_LOG("Changed state to SEEKING (to %lld)", mSeekTarget.mTime);
DECODER_LOG("Changed state to SEEKING (to %lld)", mPendingSeek.mTarget.mTime);
SetState(DECODER_STATE_SEEKING);
// TODO: We should re-create the decoded stream after seek completed as we do
// for audio thread since it is until then we know which position we seek to
// as far as fast-seek is concerned. It also fix the problem where stream
// clock seems to go backwards during seeking.
if (mAudioCaptured) {
mDecoder->RecreateDecodedStream(seekTime - mStartTime);
}
ScheduleStateMachine();
return mPendingSeek.mPromise.Ensure(__func__);
}
void MediaDecoderStateMachine::StopAudioThread()
{
// XXXbholley - Needed because DecodeSeek runs on the decode thread.
// Once bug 1135170 lands, this becomes state-machine only.
MOZ_ASSERT(OnStateMachineThread() || OnDecodeThread());
MOZ_ASSERT(OnStateMachineThread());
AssertCurrentThreadInMonitor();
if (mStopAudioThread) {
@ -1838,14 +1782,6 @@ MediaDecoderStateMachine::SetReaderIdle()
mReader->SetIdle();
}
void
MediaDecoderStateMachine::AcquireMonitorAndInvokeDispatchDecodeTasksIfNeeded()
{
MOZ_ASSERT(OnStateMachineThread());
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
DispatchDecodeTasksIfNeeded();
}
void
MediaDecoderStateMachine::DispatchDecodeTasksIfNeeded()
{
@ -1914,21 +1850,75 @@ MediaDecoderStateMachine::DispatchDecodeTasksIfNeeded()
}
}
nsresult
MediaDecoderStateMachine::EnqueueDecodeSeekTask()
void
MediaDecoderStateMachine::InitiateSeek()
{
MOZ_ASSERT(OnStateMachineThread());
AssertCurrentThreadInMonitor();
RefPtr<nsIRunnable> task(
NS_NewRunnableMethod(this, &MediaDecoderStateMachine::DecodeSeek));
nsresult rv = DecodeTaskQueue()->Dispatch(task);
if (NS_FAILED(rv)) {
DECODER_WARN("Dispatch DecodeSeek task failed.");
DecodeError();
mCurrentSeek.RejectIfExists(__func__);
mCurrentSeek.Steal(mPendingSeek);
// Bound the seek time to be inside the media range.
int64_t end = GetEndTime();
NS_ASSERTION(mStartTime != -1, "Should know start time by now");
NS_ASSERTION(end != -1, "Should know end time by now");
int64_t seekTime = mCurrentSeek.mTarget.mTime + mStartTime;
seekTime = std::min(seekTime, end);
seekTime = std::max(mStartTime, seekTime);
NS_ASSERTION(seekTime >= mStartTime && seekTime <= end,
"Can only seek in range [0,duration]");
mCurrentSeek.mTarget.mTime = seekTime;
if (mAudioCaptured) {
// TODO: We should re-create the decoded stream after seek completed as we do
// for audio thread since it is until then we know which position we seek to
// as far as fast-seek is concerned. It also fix the problem where stream
// clock seems to go backwards during seeking.
nsCOMPtr<nsIRunnable> event =
NS_NewRunnableMethodWithArg<int64_t>(mDecoder, &MediaDecoder::RecreateDecodedStream,
seekTime - mStartTime);
NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
}
return rv;
mDropAudioUntilNextDiscontinuity = HasAudio();
mDropVideoUntilNextDiscontinuity = HasVideo();
mDecoder->StopProgressUpdates();
mCurrentTimeBeforeSeek = GetMediaTime();
// Stop playback now to ensure that while we're outside the monitor
// dispatching SeekingStarted, playback doesn't advance and mess with
// mCurrentFrameTime that we've setting to seekTime here.
StopPlayback();
UpdatePlaybackPositionInternal(mCurrentSeek.mTarget.mTime);
// SeekingStarted will do a UpdateReadyStateForData which will
// inform the element and its users that we have no frames
// to display
nsCOMPtr<nsIRunnable> startEvent =
NS_NewRunnableMethodWithArg<MediaDecoderEventVisibility>(
mDecoder,
&MediaDecoder::SeekingStarted,
mCurrentSeek.mTarget.mEventVisibility);
NS_DispatchToMainThread(startEvent, NS_DISPATCH_NORMAL);
// The seek target is different than the current playback position,
// we'll need to seek the playback position, so shutdown our decode
// thread and audio sink.
StopAudioThread();
ResetPlayback();
// Put a reset in the pipe before seek.
ResetDecode();
// Do the seek.
mSeekRequest.Begin(ProxyMediaCall(DecodeTaskQueue(), mReader.get(), __func__,
&MediaDecoderReader::Seek, mCurrentSeek.mTarget.mTime,
GetEndTime())
->RefableThen(mScheduler.get(), __func__, this,
&MediaDecoderStateMachine::OnSeekCompleted,
&MediaDecoderStateMachine::OnSeekFailed));
}
nsresult
@ -1961,7 +1951,7 @@ MediaDecoderStateMachine::EnsureAudioDecodeTaskQueued()
}
if (!IsAudioDecoding() || mAudioDataRequest.Exists() ||
mAudioWaitRequest.Exists() || mWaitingForDecoderSeek) {
mAudioWaitRequest.Exists() || mSeekRequest.Exists()) {
return NS_OK;
}
@ -2007,7 +1997,7 @@ MediaDecoderStateMachine::EnsureVideoDecodeTaskQueued()
}
if (!IsVideoDecoding() || mVideoDataRequest.Exists() ||
mVideoWaitRequest.Exists() || mWaitingForDecoderSeek) {
mVideoWaitRequest.Exists() || mSeekRequest.Exists()) {
return NS_OK;
}
@ -2429,170 +2419,37 @@ MediaDecoderStateMachine::FinishDecodeFirstFrame()
CheckIfDecodeComplete();
MaybeStartPlayback();
if (mQueuedSeekTarget.IsValid()) {
EnqueueStartQueuedSeekTask();
if (mQueuedSeek.Exists()) {
mPendingSeek.Steal(mQueuedSeek);
SetState(DECODER_STATE_SEEKING);
ScheduleStateMachine();
}
return NS_OK;
}
void MediaDecoderStateMachine::DecodeSeek()
{
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
NS_ASSERTION(OnDecodeThread(), "Should be on decode thread.");
if (mState != DECODER_STATE_SEEKING ||
!mSeekTarget.IsValid()) {
DECODER_LOG("Early returning from DecodeSeek");
return;
}
// If there's already an existing seek in progress, we need to handle that.
if (mCurrentSeekTarget.IsValid()) {
// There are 3 states we might be in, listed in the order that they occur:
// (1) Waiting for the seek to be resolved.
// (2) Waiting for the seek to be resolved, having already issued a cancel.
// (3) After seek resolution, waiting for SeekComplete to run.
//
// If we're in the first state, we move to the second. Otherwise, we just wait
// for things to sort themselves out.
if (mWaitingForDecoderSeek && !mCancelingSeek) {
mReader->CancelSeek();
mCancelingSeek = true;
}
return;
}
mCurrentSeekTarget = mSeekTarget;
mSeekTarget.Reset();
mDropAudioUntilNextDiscontinuity = HasAudio();
mDropVideoUntilNextDiscontinuity = HasVideo();
// During the seek, don't have a lock on the decoder state,
// otherwise long seek operations can block the main thread.
// The events dispatched to the main thread are SYNC calls.
// These calls are made outside of the decode monitor lock so
// it is safe for the main thread to makes calls that acquire
// the lock since it won't deadlock. We check the state when
// acquiring the lock again in case shutdown has occurred
// during the time when we didn't have the lock.
int64_t seekTime = mCurrentSeekTarget.mTime;
mDecoder->StopProgressUpdates();
bool currentTimeChanged = false;
mCurrentTimeBeforeSeek = GetMediaTime();
if (mCurrentTimeBeforeSeek != seekTime) {
currentTimeChanged = true;
// Stop playback now to ensure that while we're outside the monitor
// dispatching SeekingStarted, playback doesn't advance and mess with
// mCurrentFrameTime that we've setting to seekTime here.
StopPlayback();
UpdatePlaybackPositionInternal(seekTime);
}
// SeekingStarted will do a UpdateReadyStateForData which will
// inform the element and its users that we have no frames
// to display
{
ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
nsCOMPtr<nsIRunnable> startEvent =
NS_NewRunnableMethodWithArg<MediaDecoderEventVisibility>(
mDecoder,
&MediaDecoder::SeekingStarted,
mCurrentSeekTarget.mEventVisibility);
NS_DispatchToMainThread(startEvent, NS_DISPATCH_SYNC);
}
if (mState != DECODER_STATE_SEEKING) {
// May have shutdown while we released the monitor.
return;
}
mDecodeToSeekTarget = false;
if (!currentTimeChanged) {
DECODER_LOG("Seek !currentTimeChanged...");
mDropAudioUntilNextDiscontinuity = false;
mDropVideoUntilNextDiscontinuity = false;
nsCOMPtr<nsIRunnable> task = NS_NewRunnableMethod(this, &MediaDecoderStateMachine::SeekCompleted);
nsresult rv = GetStateMachineThread()->Dispatch(task, NS_DISPATCH_NORMAL);
if (NS_FAILED(rv)) {
DecodeError();
}
} else {
// The seek target is different than the current playback position,
// we'll need to seek the playback position, so shutdown our decode
// thread and audio sink.
StopAudioThread();
ResetPlayback();
nsresult res;
{
ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
// We must not hold the state machine monitor while we call into
// the reader, since it could do I/O or deadlock some other way.
res = mReader->ResetDecode();
if (NS_SUCCEEDED(res)) {
mReader->Seek(seekTime, GetEndTime())
->Then(DecodeTaskQueue(), __func__, this,
&MediaDecoderStateMachine::OnSeekCompleted,
&MediaDecoderStateMachine::OnSeekFailed);
}
}
if (NS_FAILED(res)) {
DecodeError();
return;
}
mWaitingForDecoderSeek = true;
}
}
void
MediaDecoderStateMachine::OnSeekCompleted(int64_t aTime)
{
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
mWaitingForDecoderSeek = false;
mCancelingSeek = false;
MOZ_ASSERT(OnStateMachineThread());
mSeekRequest.Complete();
// We must decode the first samples of active streams, so we can determine
// the new stream time. So dispatch tasks to do that.
mDecodeToSeekTarget = true;
// XXXbholley - This can become a direct call once bug 1135170 lands.
nsCOMPtr<nsIRunnable> event =
NS_NewRunnableMethod(this, &MediaDecoderStateMachine::AcquireMonitorAndInvokeDispatchDecodeTasksIfNeeded);
GetStateMachineThread()->Dispatch(event, NS_DISPATCH_NORMAL);
DispatchDecodeTasksIfNeeded();
}
void
MediaDecoderStateMachine::OnSeekFailed(nsresult aResult)
{
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
bool wasCanceled = mCancelingSeek;
mWaitingForDecoderSeek = false;
mCancelingSeek = false;
if (NS_FAILED(aResult)) {
DecodeError();
} else if (wasCanceled && mSeekTarget.IsValid() && mState == DECODER_STATE_SEEKING) {
// Try again.
mCurrentSeekTarget = mSeekTarget;
mSeekTarget.Reset();
{
ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
nsCOMPtr<nsIRunnable> startEvent =
NS_NewRunnableMethodWithArg<MediaDecoderEventVisibility>(
mDecoder,
&MediaDecoder::SeekingStarted,
mCurrentSeekTarget.mEventVisibility);
NS_DispatchToMainThread(startEvent, NS_DISPATCH_SYNC);
}
mReader->Seek(mCurrentSeekTarget.mTime, mEndTime)
->Then(DecodeTaskQueue(), __func__, this,
&MediaDecoderStateMachine::OnSeekCompleted,
&MediaDecoderStateMachine::OnSeekFailed);
mWaitingForDecoderSeek = true;
}
MOZ_ASSERT(OnStateMachineThread());
mSeekRequest.Complete();
MOZ_ASSERT(NS_FAILED(aResult), "Cancels should also disconnect mSeekRequest");
DecodeError();
}
void
@ -2601,21 +2458,20 @@ MediaDecoderStateMachine::SeekCompleted()
MOZ_ASSERT(OnStateMachineThread());
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
// We must reset the seek target when exiting this function, but not
// before, as if we dropped the monitor in any function called here,
// we may begin a new seek on the state machine thread, and be in
// an inconsistent state.
AutoSetOnScopeExit<SeekTarget> reset(mCurrentSeekTarget, SeekTarget());
if (mState != DECODER_STATE_SEEKING) {
MOZ_DIAGNOSTIC_ASSERT(mState == DECODER_STATE_DORMANT ||
mState == DECODER_STATE_SHUTDOWN);
// It would be nice to assert mCurrent.Exists() here, but it's possible that
// we've transitioned to DECODER_STATE_SHUTDOWN but not yet gone through
// RunStateMachine in that state, which is where this promise gets rejected.
return;
}
int64_t seekTime = mCurrentSeekTarget.mTime;
int64_t newCurrentTime = mCurrentSeekTarget.mTime;
int64_t seekTime = mCurrentSeek.mTarget.mTime;
int64_t newCurrentTime = seekTime;
// Setup timestamp state.
VideoData* video = VideoQueue().PeekFront();
nsRefPtr<VideoData> video = VideoQueue().PeekFront();
if (seekTime == mEndTime) {
newCurrentTime = mAudioStartTime = seekTime;
} else if (HasAudio()) {
@ -2635,35 +2491,14 @@ MediaDecoderStateMachine::SeekCompleted()
}
mPlayDuration = newCurrentTime - mStartTime;
if (HasVideo()) {
if (video) {
{
ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
RenderVideoFrame(video, TimeStamp::Now());
}
nsCOMPtr<nsIRunnable> event =
NS_NewRunnableMethod(mDecoder, &MediaDecoder::Invalidate);
NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
}
}
MOZ_ASSERT(mState != DECODER_STATE_DECODING_NONE);
mDecoder->StartProgressUpdates();
if (mState == DECODER_STATE_DECODING_METADATA ||
mState == DECODER_STATE_DECODING_FIRSTFRAME ||
mState == DECODER_STATE_DORMANT ||
mState == DECODER_STATE_SHUTDOWN) {
return;
}
// Change state to DECODING or COMPLETED now. SeekingStopped will
// call MediaDecoderStateMachine::Seek to reset our state to SEEKING
// if we need to seek again.
nsCOMPtr<nsIRunnable> stopEvent;
bool isLiveStream = mDecoder->GetResource()->GetLength() == -1;
if (mSeekTarget.IsValid()) {
if (mPendingSeek.Exists()) {
// A new seek target came in while we were processing the old one. No rest
// for the seeking.
DECODER_LOG("A new seek came along while we were finishing the old one - staying in SEEKING");
@ -2673,20 +2508,12 @@ MediaDecoderStateMachine::SeekCompleted()
// this if we're playing a live stream, since the end of media will advance
// once we download more data!
DECODER_LOG("Changed state from SEEKING (to %lld) to COMPLETED", seekTime);
stopEvent = NS_NewRunnableMethodWithArg<MediaDecoderEventVisibility>(
mDecoder,
&MediaDecoder::SeekingStoppedAtEnd,
mCurrentSeekTarget.mEventVisibility);
// Explicitly set our state so we don't decode further, and so
// we report playback ended to the media element.
SetState(DECODER_STATE_COMPLETED);
DispatchDecodeTasksIfNeeded();
} else {
DECODER_LOG("Changed state from SEEKING (to %lld) to DECODING", seekTime);
stopEvent = NS_NewRunnableMethodWithArg<MediaDecoderEventVisibility>(
mDecoder,
&MediaDecoder::SeekingStopped,
mCurrentSeekTarget.mEventVisibility);
StartDecoding();
}
@ -2701,16 +2528,16 @@ MediaDecoderStateMachine::SeekCompleted()
// if we need to buffer after the seek.
mQuickBuffering = false;
// Prevent changes in playback position before 'seeked' is fired for we
// expect currentTime equals seek target in 'seeked' callback.
mScheduler->FreezeScheduling();
{
ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
NS_DispatchToMainThread(stopEvent, NS_DISPATCH_SYNC);
}
mCurrentSeek.Resolve(mState == DECODER_STATE_COMPLETED, __func__);
ScheduleStateMachine();
mScheduler->ThawScheduling();
if (video) {
ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
RenderVideoFrame(video, TimeStamp::Now());
nsCOMPtr<nsIRunnable> event =
NS_NewRunnableMethod(mDecoder, &MediaDecoder::Invalidate);
NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
}
}
// Runnable to dispose of the decoder and state machine on the main thread.
@ -2808,6 +2635,10 @@ nsresult MediaDecoderStateMachine::RunStateMachine()
switch (mState) {
case DECODER_STATE_SHUTDOWN: {
mQueuedSeek.RejectIfExists(__func__);
mPendingSeek.RejectIfExists(__func__);
mCurrentSeek.RejectIfExists(__func__);
if (IsPlaying()) {
StopPlayback();
}
@ -2927,7 +2758,10 @@ nsresult MediaDecoderStateMachine::RunStateMachine()
}
case DECODER_STATE_SEEKING: {
return EnqueueDecodeSeekTask();
if (mPendingSeek.Exists()) {
InitiateSeek();
}
return NS_OK;
}
case DECODER_STATE_COMPLETED: {
@ -2988,13 +2822,11 @@ MediaDecoderStateMachine::FlushDecoding()
MOZ_ASSERT(OnStateMachineThread());
AssertCurrentThreadInMonitor();
// Put a task in the decode queue to abort any decoding operations.
// The reader is not supposed to put any tasks to deliver samples into
// the queue after this runs (unless we request another sample from it).
ResetDecode();
{
// Put a task in the decode queue to abort any decoding operations.
// The reader is not supposed to put any tasks to deliver samples into
// the queue after this runs (unless we request another sample from it).
RefPtr<nsIRunnable> task;
task = NS_NewRunnableMethod(this, &MediaDecoderStateMachine::ResetDecode);
// Wait for the ResetDecode to run and for the decoder to abort
// decoding operations and run any pending callbacks. This is
// important, as we don't want any pending tasks posted to the task
@ -3004,7 +2836,6 @@ MediaDecoderStateMachine::FlushDecoding()
// and shutdown on B2G will fail as there are outstanding video frames
// alive.
ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
DecodeTaskQueue()->Dispatch(task);
DecodeTaskQueue()->AwaitIdle();
}
@ -3017,20 +2848,20 @@ MediaDecoderStateMachine::FlushDecoding()
void
MediaDecoderStateMachine::ResetDecode()
{
NS_ASSERTION(OnDecodeThread(), "Should be on decode thread.");
MOZ_ASSERT(OnStateMachineThread());
AssertCurrentThreadInMonitor();
if (!mReader) {
return;
}
mAudioDataRequest.DisconnectIfExists();
mAudioWaitRequest.DisconnectIfExists();
mVideoDataRequest.DisconnectIfExists();
mVideoWaitRequest.DisconnectIfExists();
mSeekRequest.DisconnectIfExists();
{
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
if (mWaitingForDecoderSeek && !mCancelingSeek) {
mReader->CancelSeek();
mCancelingSeek = true;
}
}
mReader->ResetDecode();
mDecodeToSeekTarget = false;
RefPtr<nsRunnable> resetTask =
NS_NewRunnableMethod(mReader, &MediaDecoderReader::ResetDecode);
DecodeTaskQueue()->Dispatch(resetTask);
}
void MediaDecoderStateMachine::RenderVideoFrame(VideoData* aData,
@ -3277,7 +3108,8 @@ MediaDecoderStateMachine::DropVideoUpToSeekTarget(VideoData* aSample)
MOZ_ASSERT(video);
DECODER_LOG("DropVideoUpToSeekTarget() frame [%lld, %lld] dup=%d",
video->mTime, video->GetEndTime(), video->mDuplicate);
const int64_t target = mCurrentSeekTarget.mTime;
MOZ_ASSERT(mCurrentSeek.Exists());
const int64_t target = mCurrentSeek.mTarget.mTime;
// Duplicate handling: if we're dropping frames up the seek target, we must
// be wary of Theora duplicate frames. They don't have an image, so if the
@ -3325,12 +3157,12 @@ MediaDecoderStateMachine::DropAudioUpToSeekTarget(AudioData* aSample)
{
nsRefPtr<AudioData> audio(aSample);
MOZ_ASSERT(audio &&
mCurrentSeekTarget.IsValid() &&
mCurrentSeekTarget.mType == SeekTarget::Accurate);
mCurrentSeek.Exists() &&
mCurrentSeek.mTarget.mType == SeekTarget::Accurate);
CheckedInt64 startFrame = UsecsToFrames(audio->mTime,
mInfo.mAudio.mRate);
CheckedInt64 targetFrame = UsecsToFrames(mCurrentSeekTarget.mTime,
CheckedInt64 targetFrame = UsecsToFrames(mCurrentSeek.mTarget.mTime,
mInfo.mAudio.mRate);
if (!startFrame.isValid() || !targetFrame.isValid()) {
return NS_ERROR_FAILURE;
@ -3379,7 +3211,7 @@ MediaDecoderStateMachine::DropAudioUpToSeekTarget(AudioData* aSample)
return NS_ERROR_FAILURE;
}
nsRefPtr<AudioData> data(new AudioData(audio->mOffset,
mCurrentSeekTarget.mTime,
mCurrentSeek.mTarget.mTime,
duration.value(),
frames,
audioData.forget(),

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

@ -221,21 +221,8 @@ private:
public:
// Seeks to the decoder to aTarget asynchronously.
// Must be called from the main thread.
void Seek(const SeekTarget& aTarget);
// Dispatches a task to the main thread to seek to mQueuedSeekTarget.
// This is threadsafe and can be called on any thread.
void EnqueueStartQueuedSeekTask();
// Seeks to the decoder to mQueuedSeekTarget asynchronously.
// Must be called from the main thread.
void StartQueuedSeek();
// Seeks to the decoder to aTarget asynchronously.
// Must be called from the main thread.
// The decoder monitor must be held with exactly one lock count.
void StartSeek(const SeekTarget& aTarget);
// Must be called on the state machine thread.
nsRefPtr<MediaDecoder::SeekPromise> Seek(SeekTarget aTarget);
// Returns the current playback position in seconds.
// Called from the main thread to get the current frame time. The decoder
@ -611,9 +598,9 @@ protected:
// The decoder monitor must be held.
nsresult EnqueueDecodeFirstFrameTask();
// Dispatches a task to the decode task queue to seek the decoder.
// Clears any previous seeking state and initiates a new see on the decoder.
// The decoder monitor must be held.
nsresult EnqueueDecodeSeekTask();
void InitiateSeek();
nsresult DispatchAudioDecodeTaskIfNeeded();
@ -641,7 +628,6 @@ protected:
// to idle mode. This is threadsafe, and can be called from any thread.
// The decoder monitor must be held.
void DispatchDecodeTasksIfNeeded();
void AcquireMonitorAndInvokeDispatchDecodeTasksIfNeeded();
// Returns the "media time". This is the absolute time which the media
// playback has reached. i.e. this returns values in the range
@ -820,23 +806,52 @@ protected:
// as mStartTime and mEndTime could have been set separately.
bool mDurationSet;
struct SeekJob {
void Steal(SeekJob& aOther)
{
MOZ_DIAGNOSTIC_ASSERT(!Exists());
mTarget = aOther.mTarget;
aOther.mTarget.Reset();
mPromise = Move(aOther.mPromise);
}
bool Exists()
{
MOZ_ASSERT(mTarget.IsValid() == !mPromise.IsEmpty());
return mTarget.IsValid();
}
void Resolve(bool aAtEnd, const char* aCallSite)
{
mTarget.Reset();
MediaDecoder::SeekResolveValue val(aAtEnd, mTarget.mEventVisibility);
mPromise.Resolve(val, aCallSite);
}
void RejectIfExists(const char* aCallSite)
{
mTarget.Reset();
mPromise.RejectIfExists(true, aCallSite);
}
~SeekJob()
{
MOZ_DIAGNOSTIC_ASSERT(!mTarget.IsValid());
MOZ_DIAGNOSTIC_ASSERT(mPromise.IsEmpty());
}
SeekTarget mTarget;
MediaPromiseHolder<MediaDecoder::SeekPromise> mPromise;
};
// Queued seek - moves to mPendingSeek when DecodeFirstFrame completes.
SeekJob mQueuedSeek;
// Position to seek to in microseconds when the seek state transition occurs.
// The decoder monitor lock must be obtained before reading or writing
// this value. Accessed on main and decode thread.
SeekTarget mSeekTarget;
SeekJob mPendingSeek;
// Position to seek to in microseconds when DecodeFirstFrame completes.
// The decoder monitor lock must be obtained before reading or writing
// this value. Accessed on main and decode thread.
SeekTarget mQueuedSeekTarget;
// The position that we're currently seeking to. This differs from
// mSeekTarget, as mSeekTarget is the target we'll seek to next, whereas
// mCurrentSeekTarget is the position that the decode is in the process
// of seeking to.
// The decoder monitor lock must be obtained before reading or writing
// this value.
SeekTarget mCurrentSeekTarget;
// The position that we're currently seeking to.
SeekJob mCurrentSeek;
// Media Fragment end time in microseconds. Access controlled by decoder monitor.
int64_t mFragmentEndTime;
@ -1099,14 +1114,8 @@ protected:
// mCurrentSeekTarget.
bool mDecodeToSeekTarget;
// True if we've issued Seek() to the reader, but haven't yet received
// OnSeekCompleted. We should avoid trying to decode more audio/video
// until this completes.
bool mWaitingForDecoderSeek;
// True if we're in the process of canceling a seek. This allows us to avoid
// invoking CancelSeek() multiple times.
bool mCancelingSeek;
// Track the current seek promise made by the reader.
MediaPromiseConsumerHolder<MediaDecoderReader::SeekPromise> mSeekRequest;
// We record the playback position before we seek in order to
// determine where the seek terminated relative to the playback position

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

@ -89,15 +89,8 @@ MediaDecoderStateMachineScheduler::Schedule(int64_t aUsecs)
{
mMonitor.AssertCurrentThreadIn();
switch(mState) {
case SCHEDULER_STATE_SHUTDOWN:
if (NS_WARN_IF(mState == SCHEDULER_STATE_SHUTDOWN)) {
return NS_ERROR_FAILURE;
case SCHEDULER_STATE_FROZEN:
mState = SCHEDULER_STATE_FROZEN_WITH_PENDING_TASK;
case SCHEDULER_STATE_FROZEN_WITH_PENDING_TASK:
return NS_OK;
case SCHEDULER_STATE_NONE:
break;
}
aUsecs = std::max<int64_t>(aUsecs, 0);
@ -168,9 +161,6 @@ void
MediaDecoderStateMachineScheduler::ScheduleAndShutdown()
{
mMonitor.AssertCurrentThreadIn();
if (IsFrozen()) {
ThawScheduling();
}
// Schedule next cycle to handle SHUTDOWN in state machine thread.
Schedule();
// This must be set after calling Schedule()
@ -201,33 +191,4 @@ MediaDecoderStateMachineScheduler::ResetTimer()
mTimeout = TimeStamp();
}
void MediaDecoderStateMachineScheduler::FreezeScheduling()
{
mMonitor.AssertCurrentThreadIn();
if (mState == SCHEDULER_STATE_SHUTDOWN) {
return;
}
MOZ_ASSERT(mState == SCHEDULER_STATE_NONE);
mState = !IsScheduled() ? SCHEDULER_STATE_FROZEN :
SCHEDULER_STATE_FROZEN_WITH_PENDING_TASK;
// Nullify pending timer task if any.
++mTimerId;
mTimeout = TimeStamp();
}
void MediaDecoderStateMachineScheduler::ThawScheduling()
{
mMonitor.AssertCurrentThreadIn();
if (mState == SCHEDULER_STATE_SHUTDOWN) {
return;
}
// We should be in frozen state and no pending timer task.
MOZ_ASSERT(IsFrozen() && !IsScheduled());
bool pendingTask = mState == SCHEDULER_STATE_FROZEN_WITH_PENDING_TASK;
mState = SCHEDULER_STATE_NONE;
if (pendingTask) {
Schedule();
}
}
} // namespace mozilla

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

@ -21,8 +21,6 @@ class ReentrantMonitor;
class MediaDecoderStateMachineScheduler {
enum State {
SCHEDULER_STATE_NONE,
SCHEDULER_STATE_FROZEN,
SCHEDULER_STATE_FROZEN_WITH_PENDING_TASK,
SCHEDULER_STATE_SHUTDOWN
};
public:
@ -34,8 +32,6 @@ public:
nsresult Schedule(int64_t aUsecs = 0);
void ScheduleAndShutdown();
nsresult TimeoutExpired(int aTimerId);
void FreezeScheduling();
void ThawScheduling();
bool OnStateMachineThread() const;
bool IsScheduled() const;
@ -48,11 +44,6 @@ public:
return mEventTarget;
}
bool IsFrozen() const {
return mState == SCHEDULER_STATE_FROZEN ||
mState == SCHEDULER_STATE_FROZEN_WITH_PENDING_TASK;
}
private:
~MediaDecoderStateMachineScheduler();
void ResetTimer();

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

@ -1098,6 +1098,8 @@ public:
MOZ_ASSERT(!mOnFailure);
NS_DispatchToMainThread(runnable);
// Do after ErrorCallbackRunnable Run()s, as it checks active window list
NS_DispatchToMainThread(new GetUserMediaListenerRemove(mWindowID, mListener));
}
void
@ -1154,11 +1156,8 @@ public:
manager->RemoveFromWindowList(mWindowID, mListener);
} else {
// This will re-check the window being alive on main-thread
// Note: we must remove the listener on MainThread as well
// and remove the listener on MainThread as well
Fail(aName, aMessage);
// MUST happen after ErrorCallbackRunnable Run()s, as it checks the active window list
NS_DispatchToMainThread(new GetUserMediaListenerRemove(mWindowID, mListener));
}
MOZ_ASSERT(!mOnSuccess);

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

@ -440,6 +440,16 @@ public:
MediaPromiseHolder()
: mMonitor(nullptr) {}
// Move semantics.
MediaPromiseHolder& operator=(MediaPromiseHolder&& aOther)
{
MOZ_ASSERT(!mMonitor && !aOther.mMonitor);
MOZ_DIAGNOSTIC_ASSERT(!mPromise);
mPromise = aOther.mPromise;
aOther.mPromise = nullptr;
return *this;
}
~MediaPromiseHolder() { MOZ_ASSERT(!mPromise); }
already_AddRefed<PromiseType> Ensure(const char* aMethodName) {

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

@ -117,6 +117,8 @@ nsRefPtr<MediaDecoderReader::AudioDataPromise>
MediaSourceReader::RequestAudioData()
{
MOZ_ASSERT(OnDecodeThread());
MOZ_DIAGNOSTIC_ASSERT(mSeekPromise.IsEmpty(), "No sample requests allowed while seeking");
MOZ_DIAGNOSTIC_ASSERT(mAudioPromise.IsEmpty(), "No duplicate sample requests");
nsRefPtr<AudioDataPromise> p = mAudioPromise.Ensure(__func__);
MSE_DEBUGV("");
if (!mAudioTrack) {
@ -263,6 +265,8 @@ nsRefPtr<MediaDecoderReader::VideoDataPromise>
MediaSourceReader::RequestVideoData(bool aSkipToNextKeyframe, int64_t aTimeThreshold)
{
MOZ_ASSERT(OnDecodeThread());
MOZ_DIAGNOSTIC_ASSERT(mSeekPromise.IsEmpty(), "No sample requests allowed while seeking");
MOZ_DIAGNOSTIC_ASSERT(mVideoPromise.IsEmpty(), "No duplicate sample requests");
nsRefPtr<VideoDataPromise> p = mVideoPromise.Ensure(__func__);
MSE_DEBUGV("RequestVideoData(%d, %lld)",
aSkipToNextKeyframe, aTimeThreshold);
@ -736,7 +740,9 @@ MediaSourceReader::Seek(int64_t aTime, int64_t aIgnored /* Used only for ogg whi
MSE_DEBUG("Seek(aTime=%lld, aEnd=%lld, aCurrent=%lld)",
aTime);
MOZ_ASSERT(mSeekPromise.IsEmpty());
MOZ_DIAGNOSTIC_ASSERT(mSeekPromise.IsEmpty());
MOZ_DIAGNOSTIC_ASSERT(mAudioPromise.IsEmpty());
MOZ_DIAGNOSTIC_ASSERT(mVideoPromise.IsEmpty());
nsRefPtr<SeekPromise> p = mSeekPromise.Ensure(__func__);
if (IsShutdown()) {
@ -744,24 +750,6 @@ MediaSourceReader::Seek(int64_t aTime, int64_t aIgnored /* Used only for ogg whi
return p;
}
// Any previous requests we've been waiting on are now unwanted.
mAudioRequest.DisconnectIfExists();
mVideoRequest.DisconnectIfExists();
// Additionally, reject any outstanding promises _we_ made that we might have
// been waiting on the above to fulfill.
mAudioPromise.RejectIfExists(CANCELED, __func__);
mVideoPromise.RejectIfExists(CANCELED, __func__);
// Do the same for any data wait promises.
mAudioWaitPromise.RejectIfExists(WaitForDataRejectValue(MediaData::AUDIO_DATA, WaitForDataRejectValue::CANCELED), __func__);
mVideoWaitPromise.RejectIfExists(WaitForDataRejectValue(MediaData::VIDEO_DATA, WaitForDataRejectValue::CANCELED), __func__);
// Finally, if we were midway seeking a new reader to find a sample, abandon
// that too.
mAudioSeekRequest.DisconnectIfExists();
mVideoSeekRequest.DisconnectIfExists();
// Store pending seek target in case the track buffers don't contain
// the desired time and we delay doing the seek.
mPendingSeekTime = aTime;
@ -778,22 +766,42 @@ MediaSourceReader::Seek(int64_t aTime, int64_t aIgnored /* Used only for ogg whi
return p;
}
void
MediaSourceReader::CancelSeek()
nsresult
MediaSourceReader::ResetDecode()
{
MOZ_ASSERT(OnDecodeThread());
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
MSE_DEBUG("");
// Any previous requests we've been waiting on are now unwanted.
mAudioRequest.DisconnectIfExists();
mVideoRequest.DisconnectIfExists();
mAudioSeekRequest.DisconnectIfExists();
mVideoSeekRequest.DisconnectIfExists();
// Additionally, reject any outstanding promises _we_ made that we might have
// been waiting on the above to fulfill.
mAudioPromise.RejectIfExists(CANCELED, __func__);
mVideoPromise.RejectIfExists(CANCELED, __func__);
mSeekPromise.RejectIfExists(NS_OK, __func__);
// Do the same for any data wait promises.
mAudioWaitPromise.RejectIfExists(WaitForDataRejectValue(MediaData::AUDIO_DATA, WaitForDataRejectValue::CANCELED), __func__);
mVideoWaitPromise.RejectIfExists(WaitForDataRejectValue(MediaData::VIDEO_DATA, WaitForDataRejectValue::CANCELED), __func__);
// Reset miscellaneous seeking state.
mWaitingForSeekData = false;
mPendingSeekTime = -1;
// Reset all the readers.
if (GetAudioReader()) {
mAudioSeekRequest.DisconnectIfExists();
GetAudioReader()->CancelSeek();
GetAudioReader()->ResetDecode();
}
if (GetVideoReader()) {
mVideoSeekRequest.DisconnectIfExists();
GetVideoReader()->CancelSeek();
GetVideoReader()->ResetDecode();
}
mSeekPromise.RejectIfExists(NS_OK, __func__);
return MediaDecoderReader::ResetDecode();
}
void

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

@ -102,7 +102,7 @@ public:
nsRefPtr<SeekPromise>
Seek(int64_t aTime, int64_t aEndTime) MOZ_OVERRIDE;
void CancelSeek() MOZ_OVERRIDE;
nsresult ResetDecode() MOZ_OVERRIDE;
// Acquires the decoder monitor, and is thus callable on any thread.
nsresult GetBuffered(dom::TimeRanges* aBuffered) MOZ_OVERRIDE;

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

@ -131,12 +131,6 @@ SourceBufferDecoder::SetMediaEndTime(int64_t aTime)
MSE_DEBUG("UNIMPLEMENTED");
}
void
SourceBufferDecoder::UpdatePlaybackPosition(int64_t aTime)
{
MSE_DEBUG("UNIMPLEMENTED");
}
bool
SourceBufferDecoder::HasInitializationData()
{

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

@ -64,7 +64,6 @@ public:
virtual void SetMediaEndTime(int64_t aTime) MOZ_FINAL MOZ_OVERRIDE;
virtual void SetMediaSeekable(bool aMediaSeekable) MOZ_FINAL MOZ_OVERRIDE;
virtual void UpdateEstimatedMediaDuration(int64_t aDuration) MOZ_FINAL MOZ_OVERRIDE;
virtual void UpdatePlaybackPosition(int64_t aTime) MOZ_FINAL MOZ_OVERRIDE;
virtual bool HasInitializationData() MOZ_FINAL MOZ_OVERRIDE;
// SourceBufferResource specific interface below.

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

@ -14,55 +14,60 @@ SimpleTest.waitForExplicitFinish();
var updateCount = 0;
runWithMSE(function (ms, v) {
ms.addEventListener("sourceopen", function () {
var sb = ms.addSourceBuffer("video/webm");
fetchWithXHR("seek.webm", function (arrayBuffer) {
// Append entire file covering range [0, 4].
sb.appendBuffer(new Uint8Array(arrayBuffer));
});
var targets = [{ currentTime: 3, videoWidth: 160, videoHeight: 120 },
{ currentTime: 2, videoWidth: 160, videoHeight: 120 },
{ currentTime: 0, videoWidth: 320, videoHeight: 240 }];
var target;
v.addEventListener("loadedmetadata", function () {
var lowResBuffer;
runWithMSE(function (ms, v) {
ms.addEventListener("sourceopen", function () {
var sb = ms.addSourceBuffer("video/webm");
fetchWithXHR("seek.webm")
.then(function (arrayBuffer) {
var p = once(v, 'loadedmetadata');
// Append entire file covering range [0, 4].
sb.appendBuffer(new Uint8Array(arrayBuffer));
return p;
}).then(function() {
is(v.currentTime, 0, "currentTime has correct initial value");
is(v.videoWidth, 320, "videoWidth has correct initial value");
is(v.videoHeight, 240, "videoHeight has correct initial value");
return fetchWithXHR("seek_lowres.webm");
}).then(function (arrayBuffer) {
// Append initialization segment.
var p = once(sb, 'updateend');
info("Appending low-res init segment");
sb.appendBuffer(new Uint8Array(arrayBuffer, 0, 438));
lowResBuffer = arrayBuffer;
return p;
}).then(function() {
var p = once(sb, 'updateend');
info("Appending low-res range [2,4]");
// Append media segment covering range [2, 4].
sb.appendBuffer(new Uint8Array(lowResBuffer, 51003));
return p;
}).then(function() {
ms.endOfStream();
var p = Promise.all([once(v, 'seeked'), once(v, 'resize')]);
info("Seeking to t=3");
v.currentTime = 3;
return p;
}).then(function() {
is(v.currentTime, 3, "Video currentTime at target");
is(v.videoWidth, 160, "videoWidth has correct low-res value");
is(v.videoHeight, 120, "videoHeight has correct low-res value");
fetchWithXHR("seek_lowres.webm", function (arrayBuffer) {
// Append initialization segment.
sb.appendBuffer(new Uint8Array(arrayBuffer, 0, 438));
sb.addEventListener("updateend", function () {
updateCount++;
if (updateCount == 1) {
// Append media segment covering range [2, 4].
sb.appendBuffer(new Uint8Array(arrayBuffer, 51003));
}
else if (updateCount == 2) {
ms.endOfStream();
target = targets.shift();
v.currentTime = target.currentTime;
}
});
});
});
v.addEventListener("seeked", function () {
is(v.currentTime, target.currentTime, "Video currentTime at target");
is(v.videoWidth, target.videoWidth, "videoWidth has correct final value");
is(v.videoHeight, target.videoHeight, "videoHeight has correct final value");
target = targets.shift();
if (target) {
v.currentTime = target.currentTime;
} else {
SimpleTest.finish();
}
var p = Promise.all([once(v, 'seeked'), once(v, 'resize')]);
info("Seeking to t=1");
v.currentTime = 1;
return p;
}).then(function() {
is(v.currentTime, 1, "Video currentTime at target");
is(v.videoWidth, 320, "videoWidth has correct high-res value");
is(v.videoHeight, 240, "videoHeight has correct high-res value");
SimpleTest.finish();
});
});
});

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

@ -1182,8 +1182,9 @@ int64_t OggReader::RangeEndTime(int64_t aStartOffset,
nsresult OggReader::GetSeekRanges(nsTArray<SeekRange>& aRanges)
{
NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
AutoPinned<MediaResource> resource(mDecoder->GetResource());
nsTArray<MediaByteRange> cached;
nsresult res = mDecoder->GetResource()->GetCachedRanges(cached);
nsresult res = resource->GetCachedRanges(cached);
NS_ENSURE_SUCCESS(res, res);
for (uint32_t index = 0; index < cached.Length(); index++) {
@ -1456,12 +1457,6 @@ nsresult OggReader::SeekInternal(int64_t aTarget, int64_t aEndTime)
res = ResetDecode(true);
NS_ENSURE_SUCCESS(res,res);
NS_ASSERTION(mStartTime != -1, "mStartTime should be known");
{
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
mDecoder->UpdatePlaybackPosition(mStartTime);
}
} else {
// TODO: This may seek back unnecessarily far in the video, but we don't
// have a way of asking Skeleton to seek to a different target for each

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

@ -386,7 +386,7 @@ status_t AudioOffloadPlayer::SeekTo(int64_t aTimeUs, bool aDispatchSeekEvents)
nsCOMPtr<nsIRunnable> nsEvent =
NS_NewRunnableMethodWithArg<MediaDecoderEventVisibility>(
mObserver,
&MediaDecoder::SeekingStopped,
&MediaDecoder::SimulateSeekResolvedForAudioOffload,
MediaDecoderEventVisibility::Observable);
NS_DispatchToCurrentThread(nsEvent);
}
@ -571,7 +571,7 @@ size_t AudioOffloadPlayer::FillBuffer(void* aData, size_t aSize)
nsCOMPtr<nsIRunnable> nsEvent =
NS_NewRunnableMethodWithArg<MediaDecoderEventVisibility>(
mObserver,
&MediaDecoder::SeekingStopped,
&MediaDecoder::SimulateSeekResolvedForAudioOffload,
MediaDecoderEventVisibility::Observable);
NS_DispatchToMainThread(nsEvent, NS_DISPATCH_NORMAL);

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

@ -737,6 +737,19 @@ function removeNodeAndSource(n) {
}
}
function once(target, name, cb) {
var p = new Promise(function(resolve, reject) {
target.addEventListener(name, function() {
target.removeEventListener(name, cb);
resolve();
});
});
if (cb) {
p.then(cb);
}
return p;
}
// Number of tests to run in parallel.
var PARALLEL_TESTS = 2;

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

@ -321,8 +321,6 @@ skip-if = buildapp == 'mulet' || os == 'win' || (toolkit == 'android' && process
[test_bug463162.xhtml]
[test_bug465498.html]
skip-if = (toolkit == 'android' && processor == 'x86')
[test_bug493187.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
[test_bug495145.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
[test_bug495300.html]
@ -333,6 +331,7 @@ skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
[test_bug726904.html]
[test_bug874897.html]
[test_bug879717.html]
skip-if = os == 'win' && !debug # bug 1140675
[test_bug883173.html]
[test_bug895091.html]
[test_bug895305.html]
@ -369,7 +368,7 @@ skip-if = (os == 'win' && os_version == '5.1') || (os != 'win' && toolkit != 'go
[test_eme_access_control.html]
skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s # bug 1043403, bug 1057908
[test_eme_canvas_blocked.html]
skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s # bug 1043403, bug 1057908
skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s || (os == 'win' && !debug) # bug 1043403, bug 1057908, bug 1140675
[test_eme_non_fragmented.html]
skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s # bug 1043403, bug 1057908
[test_eme_obs_notification.html]
@ -381,7 +380,7 @@ skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s # bug 1043403, bug 1
[test_eme_requestKeySystemAccess.html]
skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s # bug 1043403, bug 1057908
[test_eme_stream_capture_blocked.html]
skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s # bug 1043403, bug 1057908
skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s || (os == 'win' && !debug) # bug 1043403, bug 1057908, bug 1140675
[test_empty_resource.html]
[test_error_in_video_document.html]
skip-if = toolkit == 'android' || (os == 'win' && !debug) || (os == 'mac' && !debug) # bug 608634

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

@ -1,101 +0,0 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=493187
-->
<head>
<title>Bug 493187 - enter HAVE_FUTURE_DATA when seeking within buffered data even if new data isn't arriving</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
<script type="text/javascript" src="manifest.js"></script>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=493187">Mozilla Bug 493187</a>
<pre id="test">
<script class="testbody" type="text/javascript">
// Decrease parallelism for this test requires decent decoding performance in
// order to pass the test.
PARALLEL_TESTS = 1;
var manager = new MediaTestManager;
function finish(v) {
v.onerror = null;
removeNodeAndSource(v);
manager.finished(v.token);
}
function timeupdate(e) {
var v = e.target;
var b = v.buffered;
var start = b.start(0);
var end = b.end(0);
// We got enough data buffered, try to seek within the buffered range.
if (end - start >= v.duration / 2) {
info("[" + v._name + "] buffered start=" + start + " end=" + end);
v.ontimeupdate = null;
// Seek to the middle of the buffered range.
var t = (start + end) / 2;
info("[" + v._name + "] seeking to " + t);
v.currentTime = t;
}
}
function seeked(e) {
var v = e.target;
info("[" + v._name + "] seeked currentTime=" + v.currentTime + " readyState=" + v.readyState);
// Having seeked within the buffered range, readyState should be at least
// HAVE_FUTURE_DATA now or later (buffered data becoming decoded data).
if (v.readyState >= v.HAVE_FUTURE_DATA) {
finish(v);
return;
}
v.oncanplay = function() {
info("[" + v._name + "] oncanplay currentTime=" + v.currentTime + " readyState=" + v.readyState);
// Pass the test when we receive "oncanplay". Don't check |readyState >= HAVE_FUTURE_DATA|
// for "oncanplay" is dispatched asynchronously, readyState could be changed
// before "oncanplay" is received.
v.oncanplay = null;
v.onended = null;
finish(v);
}
v.onended = function() {
v.oncanplay = null;
v.onended = null;
ok(false, "[" + v._name + "] readyState=" + v.readyState + " not reaching HAVE_FUTURE_DATA before ended");
finish(v);
}
}
function error(e) {
var v = e.target;
info("[" + v._name + "] error=" + v.error.code);
finish(v);
}
function startTest(test, token) {
var v = document.createElement('video');
v.token = token;
manager.started(token);
v.src = test.name;
v._name = test.name;
v.ontimeupdate = timeupdate;
v.onseeked = seeked;
v.onerror = error;
document.body.appendChild(v);
v.play();
}
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({"set": [["media.cache_size", 40000]]}, beginTest);
function beginTest() {
manager.runTests(gSeekTests, startTest);
}
</script>
</pre>
</body>
</html>

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

@ -27,8 +27,7 @@ var readonly = true;
var completed = false;
function startTest() {
if (completed)
return;
ok(!completed, "Should not be completed yet");
ok(!v.seeking, "seeking should default to false");
try {
v.seeking = true;
@ -39,37 +38,28 @@ function startTest() {
}
is(readonly, true, "seeking should be readonly");
v.play();
v.currentTime=seekTime;
v.currentTime = seekTime;
seekFlagStart = v.seeking;
}
function seekStarted() {
if (completed)
return;
ok(v.currentTime >= seekTime - 0.1,
ok(!completed, "should not be completed yet");
ok(Math.abs(v.currentTime - seekTime) < 0.1,
"Video currentTime should be around " + seekTime + ": " + v.currentTime + " (seeking)");
v.pause();
startPassed = true;
}
function seekEnded() {
if (completed)
return;
var t = v.currentTime;
// Since we were playing, and we only paused asynchronously, we can't be
// sure that we paused before the seek finished, so we may have played
// ahead arbitrarily far.
ok(t >= seekTime - 0.1, "Video currentTime should be around " + seekTime + ": " + t + " (seeked)");
v.play();
ok(!completed, "shuld not be completed yet");
ok(Math.abs(v.currentTime - seekTime) < 0.1,
"Video currentTime should be around " + seekTime + ": " + v.currentTime + " (seeked)");
endPassed = true;
seekFlagEnd = v.seeking;
v.play();
}
function playbackEnded() {
if (completed)
return;
ok(!completed, "should not be completed yet");
completed = true;
ok(startPassed, "seeking event");
@ -79,10 +69,10 @@ function playbackEnded() {
finish();
}
v.addEventListener("ended", playbackEnded, false);
v.addEventListener("loadedmetadata", startTest, false);
v.addEventListener("seeking", seekStarted, false);
v.addEventListener("seeked", seekEnded, false);
once(v, "ended", playbackEnded);
once(v, "loadedmetadata", startTest);
once(v, "seeking", seekStarted);
once(v, "seeked", seekEnded);
}

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

@ -170,12 +170,6 @@ BufferDecoder::SetMediaEndTime(int64_t aTime)
// ignore
}
void
BufferDecoder::UpdatePlaybackPosition(int64_t aTime)
{
// ignore
}
void
BufferDecoder::OnReadMetadataCompleted()
{

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

@ -71,8 +71,6 @@ public:
virtual void SetMediaEndTime(int64_t aTime) MOZ_FINAL MOZ_OVERRIDE;
virtual void UpdatePlaybackPosition(int64_t aTime) MOZ_FINAL MOZ_OVERRIDE;
virtual void OnReadMetadataCompleted() MOZ_FINAL MOZ_OVERRIDE;
virtual MediaDecoderOwner* GetOwner() MOZ_FINAL MOZ_OVERRIDE;

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

@ -161,11 +161,12 @@ static bool
NPObjWrapper_AddProperty(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id, JS::MutableHandle<JS::Value> vp);
static bool
NPObjWrapper_DelProperty(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id, bool *succeeded);
NPObjWrapper_DelProperty(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
JS::ObjectOpResult &result);
static bool
NPObjWrapper_SetProperty(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id, bool strict,
JS::MutableHandle<JS::Value> vp);
NPObjWrapper_SetProperty(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
JS::MutableHandle<JS::Value> vp, JS::ObjectOpResult &result);
static bool
NPObjWrapper_GetProperty(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id, JS::MutableHandle<JS::Value> vp);
@ -975,33 +976,33 @@ nsJSObjWrapper::NP_RemoveProperty(NPObject *npobj, NPIdentifier npid)
}
nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj;
bool ok = false;
AutoJSExceptionReporter reporter(cx);
bool deleted = false;
JS::ObjectOpResult result;
JS::Rooted<JSObject*> obj(cx, npjsobj->mJSObj);
JSAutoCompartment ac(cx, obj);
NS_ASSERTION(NPIdentifierIsInt(npid) || NPIdentifierIsString(npid),
"id must be either string or int!\n");
JS::Rooted<jsid> id(cx, NPIdentifierToJSId(npid));
ok = ::JS_DeletePropertyById2(cx, obj, id, &deleted);
if (ok && deleted) {
if (!::JS_DeletePropertyById(cx, obj, id, result))
return false;
if (result) {
// FIXME: See bug 425823, we shouldn't need to do this, and once
// that bug is fixed we can remove this code.
bool hasProp;
ok = ::JS_HasPropertyById(cx, obj, id, &hasProp);
if (!::JS_HasPropertyById(cx, obj, id, &hasProp))
return false;
if (!hasProp)
return true;
if (ok && hasProp) {
// The property might have been deleted, but it got
// re-resolved, so no, it's not really deleted.
deleted = false;
}
// The property might have been deleted, but it got
// re-resolved, so no, it's not really deleted.
result.failCantDelete();
}
return ok && deleted;
return result.reportError(cx, obj, id);
}
//static
@ -1274,7 +1275,8 @@ NPObjWrapper_AddProperty(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<js
}
static bool
NPObjWrapper_DelProperty(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id, bool *succeeded)
NPObjWrapper_DelProperty(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
JS::ObjectOpResult &result)
{
NPObject *npobj = GetNPObject(cx, obj);
@ -1294,20 +1296,23 @@ NPObjWrapper_DelProperty(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<js
if (!ReportExceptionIfPending(cx))
return false;
if (!hasProperty) {
*succeeded = true;
return true;
}
if (!hasProperty)
return result.succeed();
}
*succeeded = npobj->_class->removeProperty(npobj, identifier);
return ReportExceptionIfPending(cx);
// This removeProperty hook may throw an exception and return false; or just
// return false without an exception pending, which behaves like `delete
// obj.prop` returning false: in strict mode it becomes a TypeError. Legacy
// code---nothing else that uses the JSAPI works this way anymore.
bool succeeded = npobj->_class->removeProperty(npobj, identifier);
if (!ReportExceptionIfPending(cx))
return false;
return succeeded ? result.succeed() : result.failCantDelete();
}
static bool
NPObjWrapper_SetProperty(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id, bool strict,
JS::MutableHandle<JS::Value> vp)
NPObjWrapper_SetProperty(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
JS::MutableHandle<JS::Value> vp, JS::ObjectOpResult &result)
{
NPObject *npobj = GetNPObject(cx, obj);
@ -1362,7 +1367,7 @@ NPObjWrapper_SetProperty(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<js
return false;
}
return true;
return result.succeed();
}
static bool

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

@ -37,7 +37,6 @@
#ifdef XP_WIN
#include "mozilla/widget/AudioSession.h"
#include "nsWindowsHelpers.h"
#include "PluginHangUIParent.h"
#endif
@ -114,6 +113,67 @@ mozilla::plugins::SetupBridge(uint32_t aPluginId,
return PPluginModule::Bridge(aContentParent, chromeParent);
}
#ifdef MOZ_CRASHREPORTER_INJECTOR
/**
* Use for executing CreateToolhelp32Snapshot off main thread
*/
class mozilla::plugins::FinishInjectorInitTask : public CancelableTask
{
public:
FinishInjectorInitTask()
: mMutex("FlashInjectorInitTask::mMutex")
, mParent(nullptr)
, mMainThreadMsgLoop(MessageLoop::current())
{
MOZ_ASSERT(NS_IsMainThread());
}
void Init(PluginModuleChromeParent* aParent)
{
MOZ_ASSERT(aParent);
mParent = aParent;
}
void PostToMainThread()
{
mSnapshot.own(CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0));
bool deleteThis = false;
{ // Scope for lock
mozilla::MutexAutoLock lock(mMutex);
if (mMainThreadMsgLoop) {
mMainThreadMsgLoop->PostTask(FROM_HERE, this);
} else {
deleteThis = true;
}
}
if (deleteThis) {
delete this;
}
}
void Run() MOZ_OVERRIDE
{
mParent->DoInjection(mSnapshot);
}
void Cancel() MOZ_OVERRIDE
{
mozilla::MutexAutoLock lock(mMutex);
mMainThreadMsgLoop = nullptr;
}
private:
mozilla::Mutex mMutex;
nsAutoHandle mSnapshot;
PluginModuleChromeParent* mParent;
MessageLoop* mMainThreadMsgLoop;
};
#endif // MOZ_CRASHREPORTER_INJECTOR
namespace {
/**
* Objects of this class remain linked until either an error occurs in the
* plugin initialization sequence, or until
@ -264,6 +324,8 @@ PRCList PluginModuleMapping::sModuleListHead =
bool PluginModuleMapping::sIsLoadModuleOnStack = false;
} // anonymous namespace
void
mozilla::plugins::TerminatePlugin(uint32_t aPluginId)
{
@ -586,6 +648,7 @@ PluginModuleChromeParent::PluginModuleChromeParent(const char* aFilePath, uint32
#ifdef MOZ_CRASHREPORTER_INJECTOR
, mFlashProcess1(0)
, mFlashProcess2(0)
, mFinishInitTask(nullptr)
#endif
, mInitOnAsyncConnect(false)
, mAsyncInitRv(NS_ERROR_NOT_INITIALIZED)
@ -633,6 +696,10 @@ PluginModuleChromeParent::~PluginModuleChromeParent()
UnregisterInjectorCallback(mFlashProcess1);
if (mFlashProcess2)
UnregisterInjectorCallback(mFlashProcess2);
if (mFinishInitTask) {
// mFinishInitTask will be deleted by the main thread message_loop
mFinishInitTask->Cancel();
}
#endif
UnregisterSettingsCallbacks();
@ -2606,22 +2673,41 @@ PluginModuleChromeParent::InitializeInjector()
return;
TimeStamp th32Start = TimeStamp::Now();
nsAutoHandle snapshot(CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0));
if (INVALID_HANDLE_VALUE == snapshot)
mFinishInitTask = mChromeTaskFactory.NewTask<FinishInjectorInitTask>();
mFinishInitTask->Init(this);
if (!::QueueUserWorkItem(&PluginModuleChromeParent::GetToolhelpSnapshot,
mFinishInitTask, WT_EXECUTEDEFAULT)) {
delete mFinishInitTask;
mFinishInitTask = nullptr;
return;
}
TimeStamp th32End = TimeStamp::Now();
mTimeBlocked += (th32End - th32Start);
}
void
PluginModuleChromeParent::DoInjection(const nsAutoHandle& aSnapshot)
{
DWORD pluginProcessPID = GetProcessId(Process()->GetChildProcessHandle());
mFlashProcess1 = GetFlashChildOfPID(pluginProcessPID, snapshot);
mFlashProcess1 = GetFlashChildOfPID(pluginProcessPID, aSnapshot);
if (mFlashProcess1) {
InjectCrashReporterIntoProcess(mFlashProcess1, this);
mFlashProcess2 = GetFlashChildOfPID(mFlashProcess1, snapshot);
mFlashProcess2 = GetFlashChildOfPID(mFlashProcess1, aSnapshot);
if (mFlashProcess2) {
InjectCrashReporterIntoProcess(mFlashProcess2, this);
}
}
mFinishInitTask = nullptr;
}
DWORD WINAPI
PluginModuleChromeParent::GetToolhelpSnapshot(LPVOID aContext)
{
FinishInjectorInitTask* task = static_cast<FinishInjectorInitTask*>(aContext);
MOZ_ASSERT(task);
task->PostToMainThread();
return 0;
}
void

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

@ -11,11 +11,11 @@
#include "mozilla/FileUtils.h"
#include "mozilla/HangMonitor.h"
#include "mozilla/PluginLibrary.h"
#include "mozilla/plugins/ScopedMethodFactory.h"
#include "mozilla/plugins/PluginProcessParent.h"
#include "mozilla/plugins/PPluginModuleParent.h"
#include "mozilla/plugins/PluginMessageUtils.h"
#include "mozilla/plugins/PluginTypes.h"
#include "mozilla/plugins/TaskFactory.h"
#include "mozilla/TimeStamp.h"
#include "npapi.h"
#include "npfunctions.h"
@ -23,6 +23,9 @@
#include "nsDataHashtable.h"
#include "nsHashKeys.h"
#include "nsIObserver.h"
#ifdef XP_WIN
#include "nsWindowsHelpers.h"
#endif
#ifdef MOZ_CRASHREPORTER
#include "nsExceptionHandler.h"
@ -46,6 +49,9 @@ class PluginInstanceParent;
#ifdef XP_WIN
class PluginHangUIParent;
#endif
#ifdef MOZ_CRASHREPORTER_INJECTOR
class FinishInjectorInitTask;
#endif
/**
* PluginModuleParent
@ -273,7 +279,7 @@ protected:
NPNetscapeFuncs* mNPNIface;
NPPluginFuncs* mNPPIface;
nsNPAPIPlugin* mPlugin;
ScopedMethodFactory<PluginModuleParent> mTaskFactory;
TaskFactory<PluginModuleParent> mTaskFactory;
nsString mPluginDumpID;
nsString mBrowserDumpID;
nsString mHangID;
@ -445,7 +451,7 @@ private:
PluginProcessParent* mSubprocess;
uint32_t mPluginId;
ScopedMethodFactory<PluginModuleChromeParent> mChromeTaskFactory;
TaskFactory<PluginModuleChromeParent> mChromeTaskFactory;
enum HangAnnotationFlags
{
@ -493,12 +499,17 @@ private:
friend class mozilla::plugins::PluginAsyncSurrogate;
#ifdef MOZ_CRASHREPORTER_INJECTOR
friend class mozilla::plugins::FinishInjectorInitTask;
void InitializeInjector();
void DoInjection(const nsAutoHandle& aSnapshot);
static DWORD WINAPI GetToolhelpSnapshot(LPVOID aContext);
void OnCrash(DWORD processID) MOZ_OVERRIDE;
DWORD mFlashProcess1;
DWORD mFlashProcess2;
mozilla::plugins::FinishInjectorInitTask* mFinishInitTask;
#endif
void OnProcessLaunched(const bool aSucceeded);

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

@ -2,23 +2,25 @@
* 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_plugins_ScopedMethodFactory_h
#define mozilla_plugins_ScopedMethodFactory_h
#ifndef mozilla_plugins_TaskFactory_h
#define mozilla_plugins_TaskFactory_h
#include <base/task.h>
/*
* This is based on the ScopedRunnableMethodFactory from ipc/chromium/src/base/task.h
* Chromiums factories assert if tasks are created and run on different threads,
* Chromium's factories assert if tasks are created and run on different threads,
* which is something we need to do in PluginModuleParent (hang UI vs. main thread).
* ScopedMethodFactory just provides cancellable tasks that don't assert this.
* TaskFactory just provides cancellable tasks that don't assert this.
* This version also allows both ScopedMethod and regular Tasks to be generated
* by the same Factory object.
*/
namespace mozilla {
namespace plugins {
template<class T>
class ScopedMethodFactory : public RevocableStore
class TaskFactory : public RevocableStore
{
private:
template<class TaskType>
@ -37,7 +39,15 @@ private:
};
public:
explicit ScopedMethodFactory(T* object) : object_(object) { }
explicit TaskFactory(T* object) : object_(object) { }
template <class TaskParamType>
inline TaskParamType* NewTask()
{
typedef TaskWrapper<TaskParamType> TaskWrapper;
TaskWrapper* task = new TaskWrapper(this);
return task;
}
template <class Method>
inline Task* NewRunnableMethod(Method method) {
@ -84,4 +94,4 @@ private:
} // namespace plugins
} // namespace mozilla
#endif // mozilla_plugins_ScopedMethodFactory_h
#endif // mozilla_plugins_TaskFactory_h

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

@ -40,9 +40,9 @@ EXPORTS.mozilla.plugins += [
'PluginUtilsOSX.h',
'PluginWidgetChild.h',
'PluginWidgetParent.h',
'ScopedMethodFactory.h',
'StreamNotifyChild.h',
'StreamNotifyParent.h',
'TaskFactory.h',
]
if CONFIG['OS_ARCH'] == 'WINNT':

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

@ -342,8 +342,8 @@ nsXBLProtoImpl::UndefineFields(JSContext *cx, JS::Handle<JSObject*> obj) const
bool hasProp;
if (::JS_AlreadyHasOwnUCProperty(cx, obj, s, name.Length(), &hasProp) &&
hasProp) {
bool dummy;
::JS_DeleteUCProperty2(cx, obj, s, name.Length(), &dummy);
JS::ObjectOpResult ignored;
::JS_DeleteUCProperty(cx, obj, s, name.Length(), ignored);
}
}
}

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

@ -6,6 +6,18 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1098628
<title>Test for Bug 1098628</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
<script class="testbody" type="text/javascript">
<![CDATA[
/** Test for Bug 1098628 **/
SimpleTest.waitForExplicitFinish();
SimpleTest.expectUncaughtException();
SimpleTest.monitorConsole(SimpleTest.finish, [{errorMessage: new RegExp('flimfniffle')}]);
addLoadEvent(function() {
SimpleTest.executeSoon(SimpleTest.endMonitorConsole);
});
]]>
</script>
<bindings xmlns="http://www.mozilla.org/xbl">
<binding id="test">
<implementation>
@ -23,18 +35,6 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1098628
</div>
<pre id="test">
<script class="testbody" type="text/javascript">
<![CDATA[
/** Test for Bug 1098628 **/
SimpleTest.waitForExplicitFinish();
SimpleTest.expectUncaughtException();
SimpleTest.monitorConsole(SimpleTest.finish, [{errorMessage: new RegExp('flimfniffle')}]);
addLoadEvent(function() {
SimpleTest.executeSoon(SimpleTest.endMonitorConsole);
});
]]>
</script>
</pre>
</body>
</html>

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

@ -1261,7 +1261,7 @@ DrawTargetCG::StrokeRect(const Rect &aRect,
bool pixelAlignedStroke = mTransform.IsAllIntegers() &&
mTransform.PreservesAxisAlignedRectangles() &&
aPattern.GetType() == PatternType::COLOR &&
IsPixelAlignedStroke(aRect, aStrokeOptions.mLineWidth);
IsPixelAlignedStroke(rect, aStrokeOptions.mLineWidth);
CGContextSetShouldAntialias(cg,
aDrawOptions.mAntialiasMode != AntialiasMode::NONE && !pixelAlignedStroke);
@ -1272,7 +1272,7 @@ DrawTargetCG::StrokeRect(const Rect &aRect,
if (isGradient(aPattern)) {
// There's no CGContextClipStrokeRect so we do it by hand
CGContextBeginPath(cg);
CGContextAddRect(cg, RectToCGRect(aRect));
CGContextAddRect(cg, RectToCGRect(rect));
CGContextReplacePathWithStrokedPath(cg);
CGRect extents = CGContextGetPathBoundingBox(cg);
//XXX: should we use EO clip here?
@ -1280,17 +1280,16 @@ DrawTargetCG::StrokeRect(const Rect &aRect,
DrawGradient(mColorSpace, cg, aPattern, extents);
} else {
SetStrokeFromPattern(cg, mColorSpace, aPattern);
// We'd like to use CGContextStrokeRect(cg, RectToCGRect(aRect));
// We'd like to use CGContextStrokeRect(cg, RectToCGRect(rect));
// Unfortunately, newer versions of OS X no longer start at the top-left
// corner and stroke clockwise as older OS X versions and all the other
// Moz2D backends do. (Newer versions start at the top right-hand corner
// and stroke counter-clockwise.) For consistency we draw the rect by hand.
CGRect rect = RectToCGRect(aRect);
CGContextBeginPath(cg);
CGContextMoveToPoint(cg, CGRectGetMinX(rect), CGRectGetMinY(rect));
CGContextAddLineToPoint(cg, CGRectGetMaxX(rect), CGRectGetMinY(rect));
CGContextAddLineToPoint(cg, CGRectGetMaxX(rect), CGRectGetMaxY(rect));
CGContextAddLineToPoint(cg, CGRectGetMinX(rect), CGRectGetMaxY(rect));
CGContextMoveToPoint(cg, rect.x, rect.y);
CGContextAddLineToPoint(cg, rect.XMost(), rect.y);
CGContextAddLineToPoint(cg, rect.XMost(), rect.YMost());
CGContextAddLineToPoint(cg, rect.x, rect.YMost());
CGContextClosePath(cg);
CGContextStrokePath(cg);
}

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

@ -736,6 +736,7 @@ struct ParamTraits<mozilla::layers::FrameMetrics>
WriteParam(aMsg, aParam.mDoSmoothScroll);
WriteParam(aMsg, aParam.mSmoothScrollOffset);
WriteParam(aMsg, aParam.GetLineScrollAmount());
WriteParam(aMsg, aParam.AllowVerticalScrollWithWheel());
WriteParam(aMsg, aParam.GetContentDescription());
}
@ -778,6 +779,7 @@ struct ParamTraits<mozilla::layers::FrameMetrics>
ReadParam(aMsg, aIter, &aResult->mDoSmoothScroll) &&
ReadParam(aMsg, aIter, &aResult->mSmoothScrollOffset) &&
ReadParam(aMsg, aIter, &aResult->mLineScrollAmount) &&
ReadParam(aMsg, aIter, &aResult->mAllowVerticalScrollWithWheel) &&
ReadContentDescription(aMsg, aIter, aResult));
}
};

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

@ -66,6 +66,7 @@ public:
, mExtraResolution(1)
, mBackgroundColor(0, 0, 0, 0)
, mLineScrollAmount(0, 0)
, mAllowVerticalScrollWithWheel(false)
{
}
@ -98,7 +99,8 @@ public:
mExtraResolution == aOther.mExtraResolution &&
mBackgroundColor == aOther.mBackgroundColor &&
mDoSmoothScroll == aOther.mDoSmoothScroll &&
mLineScrollAmount == aOther.mLineScrollAmount;
mLineScrollAmount == aOther.mLineScrollAmount &&
mAllowVerticalScrollWithWheel == aOther.mAllowVerticalScrollWithWheel;
}
bool operator!=(const FrameMetrics& aOther) const
{
@ -520,6 +522,16 @@ public:
mScrollableRect = aScrollableRect;
}
bool AllowVerticalScrollWithWheel() const
{
return mAllowVerticalScrollWithWheel;
}
void SetAllowVerticalScrollWithWheel()
{
mAllowVerticalScrollWithWheel = true;
}
private:
// The pres-shell resolution that has been induced on the document containing
@ -670,6 +682,9 @@ private:
// The value of GetLineScrollAmount(), for scroll frames.
LayoutDeviceIntSize mLineScrollAmount;
// Whether or not the frame can be vertically scrolled with a mouse wheel.
bool mAllowVerticalScrollWithWheel;
};
/**

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

@ -854,6 +854,13 @@ APZCTreeManager::ProcessWheelEvent(WidgetWheelEvent& aEvent,
return status;
}
bool
APZCTreeManager::WillHandleWheelEvent(WidgetWheelEvent* aEvent)
{
return EventStateManager::WheelEventIsScrollAction(aEvent) &&
aEvent->deltaMode == nsIDOMWheelEvent::DOM_DELTA_LINE;
}
nsEventStatus
APZCTreeManager::ReceiveInputEvent(WidgetInputEvent& aEvent,
ScrollableLayerGuid* aOutTargetGuid,
@ -891,9 +898,7 @@ APZCTreeManager::ReceiveInputEvent(WidgetInputEvent& aEvent,
}
case eWheelEventClass: {
WidgetWheelEvent& wheelEvent = *aEvent.AsWheelEvent();
if (wheelEvent.deltaMode != nsIDOMWheelEvent::DOM_DELTA_LINE ||
!EventStateManager::WheelEventIsScrollAction(&wheelEvent))
{
if (!WillHandleWheelEvent(&wheelEvent)) {
// Don't send through APZ if we're not scrolling or if the delta mode
// is not line-based.
return ProcessEvent(aEvent, aOutTargetGuid, aOutInputBlockId);

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

@ -382,6 +382,15 @@ public:
*/
nsRefPtr<const OverscrollHandoffChain> BuildOverscrollHandoffChain(const nsRefPtr<AsyncPanZoomController>& aInitialTarget);
public:
// Returns whether or not a wheel event action will be (or was) performed by
// APZ. If this returns true, the event must not perform a synchronous
// scroll.
//
// Even if this returns false, all wheel events in APZ-aware widgets must
// be sent through APZ so they are transformed correctly for TabParent.
static bool WillHandleWheelEvent(WidgetWheelEvent* aEvent);
protected:
// Protected destructor, to discourage deletion outside of Release():
virtual ~APZCTreeManager();

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

@ -23,6 +23,17 @@ enum CancelAnimationFlags : uint32_t {
ExcludeOverscroll = 1 /* Don't clear overscroll */
};
enum class ScrollSource {
// scrollTo() or something similar.
DOM,
// Touch-screen or trackpad with gesture support.
Touch,
// Mouse wheel.
Wheel
};
typedef uint32_t TouchBehaviorFlags;
}

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

@ -669,6 +669,7 @@ private:
class SmoothScrollAnimation : public AsyncPanZoomAnimation {
public:
SmoothScrollAnimation(AsyncPanZoomController& aApzc,
ScrollSource aSource,
const nsPoint &aInitialPosition,
const nsPoint &aInitialVelocity,
const nsPoint& aDestination, double aSpringConstant,
@ -680,6 +681,7 @@ public:
aSpringConstant, aDampingRatio)
, mYAxisModel(aInitialPosition.y, aDestination.y, aInitialVelocity.y,
aSpringConstant, aDampingRatio)
, mSource(aSource)
{
}
@ -725,7 +727,11 @@ public:
ParentLayerPoint overscroll;
ParentLayerPoint adjustedOffset;
mApzc.mX.AdjustDisplacement(displacement.x, adjustedOffset.x, overscroll.x);
mApzc.mY.AdjustDisplacement(displacement.y, adjustedOffset.y, overscroll.y);
bool forceVerticalOverscroll = mSource == ScrollSource::Wheel &&
!aFrameMetrics.AllowVerticalScrollWithWheel();
mApzc.mY.AdjustDisplacement(displacement.y, adjustedOffset.y, overscroll.y,
forceVerticalOverscroll);
aFrameMetrics.ScrollBy(adjustedOffset / zoom);
@ -771,6 +777,7 @@ public:
private:
AsyncPanZoomController& mApzc;
AxisPhysicsMSDModel mXAxisModel, mYAxisModel;
ScrollSource mSource;
};
void
@ -983,10 +990,10 @@ nsEventStatus AsyncPanZoomController::HandleInputEvent(const InputData& aEvent,
case PanGestureInput::PANGESTURE_MAYSTART: rv = OnPanMayBegin(panGestureInput); break;
case PanGestureInput::PANGESTURE_CANCELLED: rv = OnPanCancelled(panGestureInput); break;
case PanGestureInput::PANGESTURE_START: rv = OnPanBegin(panGestureInput); break;
case PanGestureInput::PANGESTURE_PAN: rv = OnPan(panGestureInput, true); break;
case PanGestureInput::PANGESTURE_PAN: rv = OnPan(panGestureInput, ScrollSource::Touch, true); break;
case PanGestureInput::PANGESTURE_END: rv = OnPanEnd(panGestureInput); break;
case PanGestureInput::PANGESTURE_MOMENTUMSTART: rv = OnPanMomentumStart(panGestureInput); break;
case PanGestureInput::PANGESTURE_MOMENTUMPAN: rv = OnPan(panGestureInput, false); break;
case PanGestureInput::PANGESTURE_MOMENTUMPAN: rv = OnPan(panGestureInput, ScrollSource::Touch, false); break;
case PanGestureInput::PANGESTURE_MOMENTUMEND: rv = OnPanMomentumEnd(panGestureInput); break;
default: NS_WARNING("Unhandled pan gesture"); break;
}
@ -1266,6 +1273,8 @@ nsEventStatus AsyncPanZoomController::OnScaleBegin(const PinchGestureInput& aEve
}
SetState(PINCHING);
mX.SetVelocity(0);
mY.SetVelocity(0);
mLastZoomFocus = aEvent.mLocalFocusPoint - mFrameMetrics.mCompositionBounds.TopLeft();
return nsEventStatus_eConsumeNoDefault;
@ -1439,7 +1448,7 @@ nsEventStatus AsyncPanZoomController::OnScrollWheel(const ScrollWheelInput& aEve
aEvent.modifiers);
move.mLocalPanStartPoint = aEvent.mLocalOrigin;
move.mLocalPanDisplacement = delta;
OnPan(move, false);
OnPan(move, ScrollSource::Wheel, false);
PanGestureInput end(PanGestureInput::PANGESTURE_END, aEvent.mTime, aEvent.mTimeStamp,
aEvent.mOrigin, ScreenPoint(0, 0), aEvent.modifiers);
@ -1459,7 +1468,7 @@ nsEventStatus AsyncPanZoomController::OnScrollWheel(const ScrollWheelInput& aEve
} else {
mFrameMetrics.SetSmoothScrollOffset(mFrameMetrics.GetSmoothScrollOffset() + delta);
}
StartSmoothScroll();
StartSmoothScroll(ScrollSource::Wheel);
break;
}
}
@ -1518,7 +1527,7 @@ nsEventStatus AsyncPanZoomController::OnPanBegin(const PanGestureInput& aEvent)
return nsEventStatus_eConsumeNoDefault;
}
nsEventStatus AsyncPanZoomController::OnPan(const PanGestureInput& aEvent, bool aFingersOnTouchpad) {
nsEventStatus AsyncPanZoomController::OnPan(const PanGestureInput& aEvent, ScrollSource aSource, bool aFingersOnTouchpad) {
APZC_LOG("%p got a pan-pan in state %d\n", this, mState);
if (mState == SMOOTH_SCROLL) {
@ -1548,7 +1557,9 @@ nsEventStatus AsyncPanZoomController::OnPan(const PanGestureInput& aEvent, bool
if (mPanGestureState) {
ScreenPoint panDistance(fabs(aEvent.mPanDisplacement.x), fabs(aEvent.mPanDisplacement.y));
OverscrollHandoffState handoffState(
*mPanGestureState->GetOverscrollHandoffChain(), panDistance);
*mPanGestureState->GetOverscrollHandoffChain(),
panDistance,
aSource);
CallDispatchScroll(aEvent.mLocalPanStartPoint,
aEvent.mLocalPanStartPoint + aEvent.mLocalPanDisplacement,
handoffState);
@ -1882,8 +1893,13 @@ bool AsyncPanZoomController::AttemptScroll(const ParentLayerPoint& aStartPoint,
ReentrantMonitorAutoEnter lock(mMonitor);
ParentLayerPoint adjustedDisplacement;
bool forceVerticalOverscroll =
(aOverscrollHandoffState.mScrollSource == ScrollSource::Wheel &&
!mFrameMetrics.AllowVerticalScrollWithWheel());
bool yChanged = mY.AdjustDisplacement(displacement.y, adjustedDisplacement.y, overscroll.y,
forceVerticalOverscroll);
bool xChanged = mX.AdjustDisplacement(displacement.x, adjustedDisplacement.x, overscroll.x);
bool yChanged = mY.AdjustDisplacement(displacement.y, adjustedDisplacement.y, overscroll.y);
if (xChanged || yChanged) {
ScheduleComposite();
}
@ -2048,7 +2064,7 @@ void AsyncPanZoomController::HandleSmoothScrollOverscroll(const ParentLayerPoint
HandleFlingOverscroll(aVelocity, BuildOverscrollHandoffChain());
}
void AsyncPanZoomController::StartSmoothScroll() {
void AsyncPanZoomController::StartSmoothScroll(ScrollSource aSource) {
SetState(SMOOTH_SCROLL);
nsPoint initialPosition = CSSPoint::ToAppUnits(mFrameMetrics.GetScrollOffset());
// Cast velocity from ParentLayerPoints/ms to CSSPoints/ms then convert to
@ -2058,6 +2074,7 @@ void AsyncPanZoomController::StartSmoothScroll() {
nsPoint destination = CSSPoint::ToAppUnits(mFrameMetrics.GetSmoothScrollOffset());
StartAnimation(new SmoothScrollAnimation(*this,
aSource,
initialPosition, initialVelocity,
destination,
gfxPrefs::ScrollBehaviorSpringConstant(),
@ -2076,8 +2093,11 @@ bool AsyncPanZoomController::CallDispatchScroll(const ParentLayerPoint& aStartPo
// null before calling DispatchScroll(). This is necessary because
// Destroy(), which nulls out mTreeManager, could be called concurrently.
APZCTreeManager* treeManagerLocal = GetApzcTreeManager();
return treeManagerLocal
&& treeManagerLocal->DispatchScroll(this, aStartPoint, aEndPoint,
if (!treeManagerLocal) {
return false;
}
return treeManagerLocal->DispatchScroll(this,
aStartPoint, aEndPoint,
aOverscrollHandoffState);
}
@ -2095,7 +2115,9 @@ void AsyncPanZoomController::TrackTouch(const MultiTouchInput& aEvent) {
if (prevTouchPoint != touchPoint) {
OverscrollHandoffState handoffState(
*CurrentTouchBlock()->GetOverscrollHandoffChain(), panDistance);
*CurrentTouchBlock()->GetOverscrollHandoffChain(),
panDistance,
ScrollSource::Touch);
CallDispatchScroll(prevTouchPoint, touchPoint, handoffState);
}
}
@ -2778,7 +2800,7 @@ void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aLayerMetri
mFrameMetrics.CopySmoothScrollInfoFrom(aLayerMetrics);
CancelAnimation();
mLastDispatchedPaintMetrics = aLayerMetrics;
StartSmoothScroll();
StartSmoothScroll(ScrollSource::DOM);
scrollOffsetUpdated = true; // Ensure that AcknowledgeScrollUpdate is called
}

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

@ -407,7 +407,7 @@ protected:
nsEventStatus OnPanMayBegin(const PanGestureInput& aEvent);
nsEventStatus OnPanCancelled(const PanGestureInput& aEvent);
nsEventStatus OnPanBegin(const PanGestureInput& aEvent);
nsEventStatus OnPan(const PanGestureInput& aEvent, bool aFingersOnTouchpad);
nsEventStatus OnPan(const PanGestureInput& aEvent, ScrollSource aSource, bool aFingersOnTouchpad);
nsEventStatus OnPanEnd(const PanGestureInput& aEvent);
nsEventStatus OnPanMomentumStart(const PanGestureInput& aEvent);
nsEventStatus OnPanMomentumEnd(const PanGestureInput& aEvent);
@ -847,7 +847,7 @@ private:
// Start an overscroll animation with the given initial velocity.
void StartOverscrollAnimation(const ParentLayerPoint& aVelocity);
void StartSmoothScroll();
void StartSmoothScroll(ScrollSource aSource);
/* ===================================================================
* The functions and members in this section are used to make ancestor chains
@ -910,7 +910,8 @@ public:
* state). If this returns false, the caller APZC knows that it should enter
* an overscrolled state itself if it can.
*/
bool AttemptScroll(const ParentLayerPoint& aStartPoint, const ParentLayerPoint& aEndPoint,
bool AttemptScroll(const ParentLayerPoint& aStartPoint,
const ParentLayerPoint& aEndPoint,
OverscrollHandoffState& aOverscrollHandoffState);
void FlushRepaintForOverscrollHandoff();

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

@ -119,13 +119,20 @@ void Axis::StartTouch(ParentLayerCoord aPos, uint32_t aTimestampMs) {
bool Axis::AdjustDisplacement(ParentLayerCoord aDisplacement,
/* ParentLayerCoord */ float& aDisplacementOut,
/* ParentLayerCoord */ float& aOverscrollAmountOut)
/* ParentLayerCoord */ float&
aOverscrollAmountOut,
bool forceOverscroll /* = false */)
{
if (mAxisLocked) {
aOverscrollAmountOut = 0;
aDisplacementOut = 0;
return false;
}
if (forceOverscroll) {
aOverscrollAmountOut = aDisplacement;
aDisplacementOut = 0;
return false;
}
StopSamplingOverscrollAnimation();

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

@ -75,7 +75,8 @@ public:
*/
bool AdjustDisplacement(ParentLayerCoord aDisplacement,
/* ParentLayerCoord */ float& aDisplacementOut,
/* ParentLayerCoord */ float& aOverscrollAmountOut);
/* ParentLayerCoord */ float& aOverscrollAmountOut,
bool forceOverscroll = false);
/**
* Overscrolls this axis by the requested amount in the requested direction.

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

@ -80,7 +80,23 @@ InputQueue::ReceiveTouchInput(const nsRefPtr<AsyncPanZoomController>& aTarget,
block = StartNewTouchBlock(aTarget, aTargetConfirmed, false);
INPQ_LOG("started new touch block %p for target %p\n", block, aTarget.get());
// XXX using the chain from |block| here may be wrong in cases where the
// target isn't confirmed and the real target turns out to be something
// else. For now assume this is rare enough that it's not an issue.
if (block == CurrentBlock() &&
aEvent.mTouches.Length() == 1 &&
block->GetOverscrollHandoffChain()->HasFastMovingApzc()) {
// If we're already in a fast fling, and a single finger goes down, then
// we want special handling for the touch event, because it shouldn't get
// delivered to content. Note that we don't set this flag when going
// from a fast fling to a pinch state (i.e. second finger goes down while
// the first finger is moving).
block->SetDuringFastMotion();
INPQ_LOG("block %p tagged as fast-motion\n", block);
}
CancelAnimationsForNewBlock(block);
MaybeRequestContentResponse(aTarget, block);
} else {
if (!mInputBlockQueue.IsEmpty()) {
@ -176,17 +192,6 @@ InputQueue::CancelAnimationsForNewBlock(CancelableBlockState* aBlock)
// being processed) we only do this animation-cancellation if there are no older
// touch blocks still in the queue.
if (aBlock == CurrentBlock()) {
// XXX using the chain from |block| here may be wrong in cases where the
// target isn't confirmed and the real target turns out to be something
// else. For now assume this is rare enough that it's not an issue.
if (aBlock->GetOverscrollHandoffChain()->HasFastMovingApzc()) {
// If we're already in a fast fling, then we want the touch event to stop the fling
// and to disallow the touch event from being used as part of a fling.
if (TouchBlockState* touch = aBlock->AsTouchBlock()) {
touch->SetDuringFastMotion();
INPQ_LOG("block %p tagged as fast-motion\n", touch);
}
}
aBlock->GetOverscrollHandoffChain()->CancelAnimations(ExcludeOverscroll);
}
}

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

@ -127,8 +127,13 @@ private:
*/
struct OverscrollHandoffState {
OverscrollHandoffState(const OverscrollHandoffChain& aChain,
const ScreenPoint& aPanDistance)
: mChain(aChain), mChainIndex(0), mPanDistance(aPanDistance) {}
const ScreenPoint& aPanDistance,
ScrollSource aScrollSource)
: mChain(aChain),
mChainIndex(0),
mPanDistance(aPanDistance),
mScrollSource(aScrollSource)
{}
// The chain of APZCs along which we hand off scroll.
// This is const to indicate that the chain does not change over the
@ -144,6 +149,8 @@ struct OverscrollHandoffState {
// course of handoff.
// The x/y components of this are non-negative.
const ScreenPoint mPanDistance;
ScrollSource mScrollSource;
};
// Don't pollute other files with this macro for now.
#undef NS_INLINE_DECL_THREADSAFE_MUTABLE_REFCOUNTING

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

@ -343,6 +343,23 @@ CompositorVsyncObserver::Composite(TimeStamp aVsyncTimestamp)
DispatchTouchEvents(aVsyncTimestamp);
}
void
CompositorVsyncObserver::OnForceComposeToTarget()
{
/**
* bug 1138502 - There are cases such as during long-running window resizing events
* where we receive many sync RecvFlushComposites. We also get vsync notifications which
* will increment mVsyncNotificationsSkipped because a composite just occurred. After
* enough vsyncs and RecvFlushComposites occurred, we will disable vsync. Then at the next
* ScheduleComposite, we will enable vsync, then get a RecvFlushComposite, which will
* force us to unobserve vsync again. On some platforms, enabling/disabling vsync is not
* free and this oscillating behavior causes a performance hit. In order to avoid this problem,
* we reset the mVsyncNotificationsSkipped counter to keep vsync enabled.
*/
MOZ_ASSERT(CompositorParent::IsInCompositorThread());
mVsyncNotificationsSkipped = 0;
}
bool
CompositorVsyncObserver::NeedsComposite()
{
@ -1040,6 +1057,10 @@ CompositorParent::ForceComposeToTarget(DrawTarget* aTarget, const nsIntRect* aRe
PROFILER_LABEL("CompositorParent", "ForceComposeToTarget",
js::ProfileEntry::Category::GRAPHICS);
if (mCompositorVsyncObserver) {
mCompositorVsyncObserver->OnForceComposeToTarget();
}
AutoRestore<bool> override(mOverrideComposeReadiness);
mOverrideComposeReadiness = true;

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

@ -106,6 +106,7 @@ public:
bool NeedsComposite();
void CancelCurrentCompositeTask();
void Destroy();
void OnForceComposeToTarget();
private:
virtual ~CompositorVsyncObserver();

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

@ -35,10 +35,8 @@ class JavaScriptBase : public WrapperOwner, public WrapperAnswer, public Base
/*** IPC handlers ***/
bool RecvPreventExtensions(const uint64_t &objId, ReturnStatus *rs,
bool *succeeded) {
return Answer::RecvPreventExtensions(ObjectId::deserialize(objId), rs,
succeeded);
bool RecvPreventExtensions(const uint64_t &objId, ReturnStatus *rs) {
return Answer::RecvPreventExtensions(ObjectId::deserialize(objId), rs);
}
bool RecvGetPropertyDescriptor(const uint64_t &objId, const JSIDVariant &id,
ReturnStatus *rs,
@ -52,13 +50,12 @@ class JavaScriptBase : public WrapperOwner, public WrapperAnswer, public Base
return Answer::RecvGetOwnPropertyDescriptor(ObjectId::deserialize(objId), id, rs, out);
}
bool RecvDefineProperty(const uint64_t &objId, const JSIDVariant &id,
const PPropertyDescriptor &flags,
ReturnStatus *rs) {
const PPropertyDescriptor &flags, ReturnStatus *rs) {
return Answer::RecvDefineProperty(ObjectId::deserialize(objId), id, flags, rs);
}
bool RecvDelete(const uint64_t &objId, const JSIDVariant &id,
ReturnStatus *rs, bool *success) {
return Answer::RecvDelete(ObjectId::deserialize(objId), id, rs, success);
ReturnStatus *rs) {
return Answer::RecvDelete(ObjectId::deserialize(objId), id, rs);
}
bool RecvHas(const uint64_t &objId, const JSIDVariant &id,
@ -75,9 +72,9 @@ class JavaScriptBase : public WrapperOwner, public WrapperAnswer, public Base
return Answer::RecvGet(ObjectId::deserialize(objId), receiverVar, id, rs, result);
}
bool RecvSet(const uint64_t &objId, const ObjectVariant &receiverVar,
const JSIDVariant &id, const bool &strict,
const JSVariant &value, ReturnStatus *rs, JSVariant *result) {
return Answer::RecvSet(ObjectId::deserialize(objId), receiverVar, id, strict, value, rs, result);
const JSIDVariant &id, const JSVariant &value, ReturnStatus *rs,
JSVariant *result) {
return Answer::RecvSet(ObjectId::deserialize(objId), receiverVar, id, value, rs, result);
}
bool RecvIsExtensible(const uint64_t &objId, ReturnStatus *rs,
@ -99,8 +96,8 @@ class JavaScriptBase : public WrapperOwner, public WrapperAnswer, public Base
bool RecvClassName(const uint64_t &objId, nsString *result) {
return Answer::RecvClassName(ObjectId::deserialize(objId), result);
}
bool RecvGetPrototypeOf(const uint64_t &objId, ReturnStatus *rs, ObjectOrNullVariant *result) {
return Answer::RecvGetPrototypeOf(ObjectId::deserialize(objId), rs, result);
bool RecvGetPrototype(const uint64_t &objId, ReturnStatus *rs, ObjectOrNullVariant *result) {
return Answer::RecvGetPrototype(ObjectId::deserialize(objId), rs, result);
}
bool RecvRegExpToShared(const uint64_t &objId, ReturnStatus *rs, nsString *source, uint32_t *flags) {
return Answer::RecvRegExpToShared(ObjectId::deserialize(objId), rs, source, flags);
@ -128,9 +125,8 @@ class JavaScriptBase : public WrapperOwner, public WrapperAnswer, public Base
bool SendDropObject(const ObjectId &objId) {
return Base::SendDropObject(objId.serialize());
}
bool SendPreventExtensions(const ObjectId &objId, ReturnStatus *rs,
bool *succeeded) {
return Base::SendPreventExtensions(objId.serialize(), rs, succeeded);
bool SendPreventExtensions(const ObjectId &objId, ReturnStatus *rs) {
return Base::SendPreventExtensions(objId.serialize(), rs);
}
bool SendGetPropertyDescriptor(const ObjectId &objId, const JSIDVariant &id,
ReturnStatus *rs,
@ -145,12 +141,11 @@ class JavaScriptBase : public WrapperOwner, public WrapperAnswer, public Base
}
bool SendDefineProperty(const ObjectId &objId, const JSIDVariant &id,
const PPropertyDescriptor &flags,
ReturnStatus *rs) {
ReturnStatus *rs) {
return Base::SendDefineProperty(objId.serialize(), id, flags, rs);
}
bool SendDelete(const ObjectId &objId, const JSIDVariant &id,
ReturnStatus *rs, bool *success) {
return Base::SendDelete(objId.serialize(), id, rs, success);
bool SendDelete(const ObjectId &objId, const JSIDVariant &id, ReturnStatus *rs) {
return Base::SendDelete(objId.serialize(), id, rs);
}
bool SendHas(const ObjectId &objId, const JSIDVariant &id,
@ -167,9 +162,9 @@ class JavaScriptBase : public WrapperOwner, public WrapperAnswer, public Base
return Base::SendGet(objId.serialize(), receiverVar, id, rs, result);
}
bool SendSet(const ObjectId &objId, const ObjectVariant &receiverVar,
const JSIDVariant &id, const bool &strict,
const JSVariant &value, ReturnStatus *rs, JSVariant *result) {
return Base::SendSet(objId.serialize(), receiverVar, id, strict, value, rs, result);
const JSIDVariant &id, const JSVariant &value, ReturnStatus *rs,
JSVariant *result) {
return Base::SendSet(objId.serialize(), receiverVar, id, value, rs, result);
}
bool SendIsExtensible(const ObjectId &objId, ReturnStatus *rs,
@ -191,9 +186,9 @@ class JavaScriptBase : public WrapperOwner, public WrapperAnswer, public Base
bool SendClassName(const ObjectId &objId, nsString *result) {
return Base::SendClassName(objId.serialize(), result);
}
bool SendGetPrototypeOf(const ObjectId &objId, ReturnStatus *rs, ObjectOrNullVariant *result) {
return Base::SendGetPrototypeOf(objId.serialize(), rs, result);
}
bool SendGetPrototype(const ObjectId &objId, ReturnStatus *rs, ObjectOrNullVariant *result) {
return Base::SendGetPrototype(objId.serialize(), rs, result);
}
bool SendRegExpToShared(const ObjectId &objId, ReturnStatus *rs,
nsString *source, uint32_t *flags) {

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

@ -605,7 +605,8 @@ UnknownPropertyStub(JSContext *cx, HandleObject obj, HandleId id, MutableHandleV
}
bool
UnknownStrictPropertyStub(JSContext *cx, HandleObject obj, HandleId id, bool strict, MutableHandleValue vp)
UnknownStrictPropertyStub(JSContext *cx, HandleObject obj, HandleId id, MutableHandleValue vp,
ObjectOpResult &result)
{
JS_ReportError(cx, "setter could not be wrapped via CPOWs");
return false;
@ -627,7 +628,7 @@ JavaScriptShared::toDescriptor(JSContext *cx, const PPropertyDescriptor &in,
getter = fromObjectVariant(cx, in.getter().get_ObjectVariant());
if (!getter)
return false;
out.setGetter(JS_DATA_TO_FUNC_PTR(JSPropertyOp, getter.get()));
out.setGetter(JS_DATA_TO_FUNC_PTR(JSGetterOp, getter.get()));
} else {
out.setGetter(UnknownPropertyStub);
}
@ -639,7 +640,7 @@ JavaScriptShared::toDescriptor(JSContext *cx, const PPropertyDescriptor &in,
setter = fromObjectVariant(cx, in.setter().get_ObjectVariant());
if (!setter)
return false;
out.setSetter(JS_DATA_TO_FUNC_PTR(JSStrictPropertyOp, setter.get()));
out.setSetter(JS_DATA_TO_FUNC_PTR(JSSetterOp, setter.get()));
} else {
out.setSetter(UnknownStrictPropertyStub);
}

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

@ -103,11 +103,17 @@ struct ReturnException
JSVariant exn;
};
struct ReturnObjectOpResult
{
uint32_t code;
};
union ReturnStatus
{
ReturnSuccess;
ReturnStopIteration;
ReturnException;
ReturnObjectOpResult;
};
union JSParam

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

@ -24,23 +24,23 @@ both:
async DropObject(uint64_t objId);
// These roughly map to the ProxyHandler hooks that CPOWs need.
prio(high) sync PreventExtensions(uint64_t objId) returns (ReturnStatus rs, bool result);
prio(high) sync PreventExtensions(uint64_t objId) returns (ReturnStatus rs);
prio(high) sync GetPropertyDescriptor(uint64_t objId, JSIDVariant id) returns (ReturnStatus rs, PPropertyDescriptor result);
prio(high) sync GetOwnPropertyDescriptor(uint64_t objId, JSIDVariant id) returns (ReturnStatus rs, PPropertyDescriptor result);
prio(high) sync DefineProperty(uint64_t objId, JSIDVariant id, PPropertyDescriptor descriptor) returns (ReturnStatus rs);
prio(high) sync Delete(uint64_t objId, JSIDVariant id) returns (ReturnStatus rs, bool successful);
prio(high) sync Delete(uint64_t objId, JSIDVariant id) returns (ReturnStatus rs);
prio(high) sync Has(uint64_t objId, JSIDVariant id) returns (ReturnStatus rs, bool has);
prio(high) sync HasOwn(uint64_t objId, JSIDVariant id) returns (ReturnStatus rs, bool has);
prio(high) sync Get(uint64_t objId, ObjectVariant receiver, JSIDVariant id) returns (ReturnStatus rs, JSVariant result);
prio(high) sync Set(uint64_t objId, ObjectVariant receiver, JSIDVariant id, bool strict, JSVariant value) returns (ReturnStatus rs, JSVariant result);
prio(high) sync Set(uint64_t objId, ObjectVariant receiver, JSIDVariant id, JSVariant value) returns (ReturnStatus rs, JSVariant result);
prio(high) sync IsExtensible(uint64_t objId) returns (ReturnStatus rs, bool result);
prio(high) sync CallOrConstruct(uint64_t objId, JSParam[] argv, bool construct) returns (ReturnStatus rs, JSVariant result, JSParam[] outparams);
prio(high) sync HasInstance(uint64_t objId, JSVariant v) returns (ReturnStatus rs, bool has);
prio(high) sync ObjectClassIs(uint64_t objId, uint32_t classValue) returns (bool result);
prio(high) sync ClassName(uint64_t objId) returns (nsString name);
prio(high) sync GetPrototypeOf(uint64_t objId) returns (ReturnStatus rs, ObjectOrNullVariant result);
prio(high) sync GetPrototype(uint64_t objId) returns (ReturnStatus rs, ObjectOrNullVariant result);
prio(high) sync RegExpToShared(uint64_t objId) returns (ReturnStatus rs, nsString source, uint32_t flags);
prio(high) sync GetPropertyKeys(uint64_t objId, uint32_t flags) returns (ReturnStatus rs, JSIDVariant[] ids);

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

@ -65,25 +65,32 @@ WrapperAnswer::ok(ReturnStatus *rs)
}
bool
WrapperAnswer::RecvPreventExtensions(const ObjectId &objId, ReturnStatus *rs,
bool *succeeded)
WrapperAnswer::ok(ReturnStatus *rs, const JS::ObjectOpResult &result)
{
*rs = result
? ReturnStatus(ReturnSuccess())
: ReturnStatus(ReturnObjectOpResult(result.failureCode()));
return true;
}
bool
WrapperAnswer::RecvPreventExtensions(const ObjectId &objId, ReturnStatus *rs)
{
AutoJSAPI jsapi;
if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects())))
return false;
JSContext *cx = jsapi.cx();
*succeeded = false;
RootedObject obj(cx, findObjectById(cx, objId));
if (!obj)
return fail(cx, rs);
if (!JS_PreventExtensions(cx, obj, succeeded))
ObjectOpResult success;
if (!JS_PreventExtensions(cx, obj, success))
return fail(cx, rs);
LOG("%s.preventExtensions()", ReceiverObj(objId));
return ok(rs);
return ok(rs, success);
}
static void
@ -179,22 +186,19 @@ WrapperAnswer::RecvDefineProperty(const ObjectId &objId, const JSIDVariant &idVa
if (!toDescriptor(cx, descriptor, &desc))
return fail(cx, rs);
bool ignored;
if (!js::DefineOwnProperty(cx, obj, id, desc, &ignored))
ObjectOpResult success;
if (!js::DefineOwnProperty(cx, obj, id, desc, success))
return fail(cx, rs);
return ok(rs);
return ok(rs, success);
}
bool
WrapperAnswer::RecvDelete(const ObjectId &objId, const JSIDVariant &idVar, ReturnStatus *rs,
bool *success)
WrapperAnswer::RecvDelete(const ObjectId &objId, const JSIDVariant &idVar, ReturnStatus *rs)
{
AutoJSAPI jsapi;
if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects())))
return false;
JSContext *cx = jsapi.cx();
*success = false;
RootedObject obj(cx, findObjectById(cx, objId));
if (!obj)
@ -206,10 +210,10 @@ WrapperAnswer::RecvDelete(const ObjectId &objId, const JSIDVariant &idVar, Retur
if (!fromJSIDVariant(cx, idVar, &id))
return fail(cx, rs);
if (!JS_DeletePropertyById2(cx, obj, id, success))
ObjectOpResult success;
if (!JS_DeletePropertyById(cx, obj, id, success))
return fail(cx, rs);
return ok(rs);
return ok(rs, success);
}
bool
@ -305,8 +309,8 @@ WrapperAnswer::RecvGet(const ObjectId &objId, const ObjectVariant &receiverVar,
bool
WrapperAnswer::RecvSet(const ObjectId &objId, const ObjectVariant &receiverVar,
const JSIDVariant &idVar, const bool &strict, const JSVariant &value,
ReturnStatus *rs, JSVariant *result)
const JSIDVariant &idVar, const JSVariant &value, ReturnStatus *rs,
JSVariant *resultValue)
{
// We may run scripted setters.
AutoEntryScript aes(xpc::NativeGlobal(scopeForTargetObjects()));
@ -314,7 +318,7 @@ WrapperAnswer::RecvSet(const ObjectId &objId, const ObjectVariant &receiverVar,
// The outparam will be written to the buffer, so it must be set even if
// the parent won't read it.
*result = UndefinedVariant();
*resultValue = UndefinedVariant();
RootedObject obj(cx, findObjectById(cx, objId));
if (!obj)
@ -330,19 +334,19 @@ WrapperAnswer::RecvSet(const ObjectId &objId, const ObjectVariant &receiverVar,
if (!fromJSIDVariant(cx, idVar, &id))
return fail(cx, rs);
MOZ_ASSERT(obj == receiver);
RootedValue val(cx);
if (!fromVariant(cx, value, &val))
return fail(cx, rs);
if (!JS_SetPropertyById(cx, obj, id, val))
ObjectOpResult result;
RootedValue receiverVal(cx, ObjectValue(*receiver));
if (!JS_ForwardSetPropertyTo(cx, obj, id, val, receiverVal, result))
return fail(cx, rs);
if (!toVariant(cx, val, result))
if (!toVariant(cx, val, resultValue))
return fail(cx, rs);
return ok(rs);
return ok(rs, result);
}
bool
@ -538,7 +542,7 @@ WrapperAnswer::RecvClassName(const ObjectId &objId, nsString *name)
}
bool
WrapperAnswer::RecvGetPrototypeOf(const ObjectId &objId, ReturnStatus *rs, ObjectOrNullVariant *result)
WrapperAnswer::RecvGetPrototype(const ObjectId &objId, ReturnStatus *rs, ObjectOrNullVariant *result)
{
*result = NullVariant();
@ -558,7 +562,7 @@ WrapperAnswer::RecvGetPrototypeOf(const ObjectId &objId, ReturnStatus *rs, Objec
if (!toObjectOrNullVariant(cx, proto, result))
return fail(cx, rs);
LOG("getPrototypeOf(%s)", ReceiverObj(objId));
LOG("getPrototype(%s)", ReceiverObj(objId));
return ok(rs);
}

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

@ -18,8 +18,7 @@ class WrapperAnswer : public virtual JavaScriptShared
public:
explicit WrapperAnswer(JSRuntime *rt) : JavaScriptShared(rt) {}
bool RecvPreventExtensions(const ObjectId &objId, ReturnStatus *rs,
bool *succeeded);
bool RecvPreventExtensions(const ObjectId &objId, ReturnStatus *rs);
bool RecvGetPropertyDescriptor(const ObjectId &objId, const JSIDVariant &id,
ReturnStatus *rs,
PPropertyDescriptor *out);
@ -28,10 +27,8 @@ class WrapperAnswer : public virtual JavaScriptShared
ReturnStatus *rs,
PPropertyDescriptor *out);
bool RecvDefineProperty(const ObjectId &objId, const JSIDVariant &id,
const PPropertyDescriptor &flags,
ReturnStatus *rs);
bool RecvDelete(const ObjectId &objId, const JSIDVariant &id,
ReturnStatus *rs, bool *success);
const PPropertyDescriptor &flags, ReturnStatus *rs);
bool RecvDelete(const ObjectId &objId, const JSIDVariant &id, ReturnStatus *rs);
bool RecvHas(const ObjectId &objId, const JSIDVariant &id,
ReturnStatus *rs, bool *bp);
@ -41,8 +38,8 @@ class WrapperAnswer : public virtual JavaScriptShared
const JSIDVariant &id,
ReturnStatus *rs, JSVariant *result);
bool RecvSet(const ObjectId &objId, const ObjectVariant &receiverVar,
const JSIDVariant &id, const bool &strict,
const JSVariant &value, ReturnStatus *rs, JSVariant *result);
const JSIDVariant &id, const JSVariant &value, ReturnStatus *rs,
JSVariant *result);
bool RecvIsExtensible(const ObjectId &objId, ReturnStatus *rs,
bool *result);
@ -53,7 +50,7 @@ class WrapperAnswer : public virtual JavaScriptShared
bool RecvObjectClassIs(const ObjectId &objId, const uint32_t &classValue,
bool *result);
bool RecvClassName(const ObjectId &objId, nsString *result);
bool RecvGetPrototypeOf(const ObjectId &objId, ReturnStatus *rs, ObjectOrNullVariant *result);
bool RecvGetPrototype(const ObjectId &objId, ReturnStatus *rs, ObjectOrNullVariant *result);
bool RecvRegExpToShared(const ObjectId &objId, ReturnStatus *rs, nsString *source, uint32_t *flags);
bool RecvGetPropertyKeys(const ObjectId &objId, const uint32_t &flags,
@ -68,6 +65,7 @@ class WrapperAnswer : public virtual JavaScriptShared
private:
bool fail(JSContext *cx, ReturnStatus *rs);
bool ok(ReturnStatus *rs);
bool ok(ReturnStatus *rs, const JS::ObjectOpResult &result);
};
} // mozilla

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

@ -90,18 +90,22 @@ class CPOWProxyHandler : public BaseProxyHandler
virtual bool getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE;
virtual bool defineProperty(JSContext *cx, HandleObject proxy, HandleId id,
MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE;
MutableHandle<JSPropertyDescriptor> desc,
ObjectOpResult &result) const MOZ_OVERRIDE;
virtual bool ownPropertyKeys(JSContext *cx, HandleObject proxy,
AutoIdVector &props) const MOZ_OVERRIDE;
virtual bool delete_(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const MOZ_OVERRIDE;
virtual bool delete_(JSContext *cx, HandleObject proxy, HandleId id,
ObjectOpResult &result) const MOZ_OVERRIDE;
virtual bool enumerate(JSContext *cx, HandleObject proxy, MutableHandleObject objp) const MOZ_OVERRIDE;
virtual bool preventExtensions(JSContext *cx, HandleObject proxy, bool *succeeded) const MOZ_OVERRIDE;
virtual bool preventExtensions(JSContext *cx, HandleObject proxy,
ObjectOpResult &result) const MOZ_OVERRIDE;
virtual bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) const MOZ_OVERRIDE;
virtual bool has(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const MOZ_OVERRIDE;
virtual bool get(JSContext *cx, HandleObject proxy, HandleObject receiver,
HandleId id, MutableHandleValue vp) const MOZ_OVERRIDE;
virtual bool set(JSContext *cx, JS::HandleObject proxy, JS::HandleObject receiver,
JS::HandleId id, bool strict, JS::MutableHandleValue vp) const MOZ_OVERRIDE;
JS::HandleId id, JS::MutableHandleValue vp,
JS::ObjectOpResult &result) const MOZ_OVERRIDE;
virtual bool call(JSContext *cx, HandleObject proxy, const CallArgs &args) const MOZ_OVERRIDE;
virtual bool construct(JSContext *cx, HandleObject proxy, const CallArgs &args) const MOZ_OVERRIDE;
@ -120,7 +124,7 @@ class CPOWProxyHandler : public BaseProxyHandler
virtual void objectMoved(JSObject *proxy, const JSObject *old) const MOZ_OVERRIDE;
virtual bool isCallable(JSObject *obj) const MOZ_OVERRIDE;
virtual bool isConstructor(JSObject *obj) const MOZ_OVERRIDE;
virtual bool getPrototypeOf(JSContext *cx, HandleObject proxy, MutableHandleObject protop) const MOZ_OVERRIDE;
virtual bool getPrototype(JSContext *cx, HandleObject proxy, MutableHandleObject protop) const MOZ_OVERRIDE;
static const char family;
static const CPOWProxyHandler singleton;
@ -202,14 +206,16 @@ WrapperOwner::getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, Handle
bool
CPOWProxyHandler::defineProperty(JSContext *cx, HandleObject proxy, HandleId id,
MutableHandle<JSPropertyDescriptor> desc) const
MutableHandle<JSPropertyDescriptor> desc,
ObjectOpResult &result) const
{
FORWARD(defineProperty, (cx, proxy, id, desc));
FORWARD(defineProperty, (cx, proxy, id, desc, result));
}
bool
WrapperOwner::defineProperty(JSContext *cx, HandleObject proxy, HandleId id,
MutableHandle<JSPropertyDescriptor> desc)
MutableHandle<JSPropertyDescriptor> desc,
ObjectOpResult &result)
{
ObjectId objId = idOf(proxy);
@ -227,7 +233,7 @@ WrapperOwner::defineProperty(JSContext *cx, HandleObject proxy, HandleId id,
LOG_STACK();
return ok(cx, status);
return ok(cx, status, result);
}
bool
@ -244,13 +250,14 @@ WrapperOwner::ownPropertyKeys(JSContext *cx, HandleObject proxy, AutoIdVector &p
}
bool
CPOWProxyHandler::delete_(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const
CPOWProxyHandler::delete_(JSContext *cx, HandleObject proxy, HandleId id,
ObjectOpResult &result) const
{
FORWARD(delete_, (cx, proxy, id, bp));
FORWARD(delete_, (cx, proxy, id, result));
}
bool
WrapperOwner::delete_(JSContext *cx, HandleObject proxy, HandleId id, bool *bp)
WrapperOwner::delete_(JSContext *cx, HandleObject proxy, HandleId id, ObjectOpResult &result)
{
ObjectId objId = idOf(proxy);
@ -259,12 +266,12 @@ WrapperOwner::delete_(JSContext *cx, HandleObject proxy, HandleId id, bool *bp)
return false;
ReturnStatus status;
if (!SendDelete(objId, idVar, &status, bp))
if (!SendDelete(objId, idVar, &status))
return ipcfail(cx);
LOG_STACK();
return ok(cx, status);
return ok(cx, status, result);
}
bool
@ -431,14 +438,14 @@ WrapperOwner::get(JSContext *cx, HandleObject proxy, HandleObject receiver,
bool
CPOWProxyHandler::set(JSContext *cx, JS::HandleObject proxy, JS::HandleObject receiver,
JS::HandleId id, bool strict, JS::MutableHandleValue vp) const
JS::HandleId id, JS::MutableHandleValue vp, JS::ObjectOpResult &result) const
{
FORWARD(set, (cx, proxy, receiver, id, strict, vp));
FORWARD(set, (cx, proxy, receiver, id, vp, result));
}
bool
WrapperOwner::set(JSContext *cx, JS::HandleObject proxy, JS::HandleObject receiver,
JS::HandleId id, bool strict, JS::MutableHandleValue vp)
JS::HandleId id, JS::MutableHandleValue vp, JS::ObjectOpResult &result)
{
ObjectId objId = idOf(proxy);
@ -455,16 +462,16 @@ WrapperOwner::set(JSContext *cx, JS::HandleObject proxy, JS::HandleObject receiv
return false;
ReturnStatus status;
JSVariant result;
if (!SendSet(objId, receiverVar, idVar, strict, val, &status, &result))
JSVariant resultValue;
if (!SendSet(objId, receiverVar, idVar, val, &status, &resultValue))
return ipcfail(cx);
LOG_STACK();
if (!ok(cx, status))
if (!ok(cx, status, result))
return false;
return fromVariant(cx, result, vp);
return fromVariant(cx, resultValue, vp);
}
bool
@ -481,23 +488,23 @@ WrapperOwner::getOwnEnumerablePropertyKeys(JSContext *cx, HandleObject proxy, Au
}
bool
CPOWProxyHandler::preventExtensions(JSContext *cx, HandleObject proxy, bool *succeeded) const
CPOWProxyHandler::preventExtensions(JSContext *cx, HandleObject proxy, ObjectOpResult &result) const
{
FORWARD(preventExtensions, (cx, proxy, succeeded));
FORWARD(preventExtensions, (cx, proxy, result));
}
bool
WrapperOwner::preventExtensions(JSContext *cx, HandleObject proxy, bool *succeeded)
WrapperOwner::preventExtensions(JSContext *cx, HandleObject proxy, ObjectOpResult &result)
{
ObjectId objId = idOf(proxy);
ReturnStatus status;
if (!SendPreventExtensions(objId, &status, succeeded))
if (!SendPreventExtensions(objId, &status))
return ipcfail(cx);
LOG_STACK();
return ok(cx, status);
return ok(cx, status, result);
}
bool
@ -680,19 +687,19 @@ WrapperOwner::className(JSContext *cx, HandleObject proxy)
}
bool
CPOWProxyHandler::getPrototypeOf(JSContext *cx, HandleObject proxy, MutableHandleObject objp) const
CPOWProxyHandler::getPrototype(JSContext *cx, HandleObject proxy, MutableHandleObject objp) const
{
FORWARD(getPrototypeOf, (cx, proxy, objp));
FORWARD(getPrototype, (cx, proxy, objp));
}
bool
WrapperOwner::getPrototypeOf(JSContext *cx, HandleObject proxy, MutableHandleObject objp)
WrapperOwner::getPrototype(JSContext *cx, HandleObject proxy, MutableHandleObject objp)
{
ObjectId objId = idOf(proxy);
ObjectOrNullVariant val;
ReturnStatus status;
if (!SendGetPrototypeOf(objId, &status, &val))
if (!SendGetPrototype(objId, &status, &val))
return ipcfail(cx);
LOG_STACK();
@ -934,6 +941,16 @@ WrapperOwner::ok(JSContext *cx, const ReturnStatus &status)
return false;
}
bool
WrapperOwner::ok(JSContext *cx, const ReturnStatus &status, ObjectOpResult &result)
{
if (status.type() == ReturnStatus::TReturnObjectOpResult)
return result.fail(status.get_ReturnObjectOpResult().code());
if (!ok(cx, status))
return false;
return result.succeed();
}
static RemoteObject
MakeRemoteObject(JSContext *cx, ObjectId id, HandleObject obj)
{
@ -1013,7 +1030,7 @@ WrapperOwner::fromRemoteObjectVariant(JSContext *cx, RemoteObject objVar)
RootedObject junkScope(cx, xpc::PrivilegedJunkScope());
JSAutoCompartment ac(cx, junkScope);
RootedValue v(cx, UndefinedValue());
// We need to setLazyProto for the getPrototypeOf hook.
// We need to setLazyProto for the getPrototype hook.
ProxyOptions options;
options.setLazyProto(true);
obj = NewProxyObject(cx,

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

@ -32,16 +32,18 @@ class WrapperOwner : public virtual JavaScriptShared
bool getOwnPropertyDescriptor(JSContext *cx, JS::HandleObject proxy, JS::HandleId id,
JS::MutableHandle<JSPropertyDescriptor> desc);
bool defineProperty(JSContext *cx, JS::HandleObject proxy, JS::HandleId id,
JS::MutableHandle<JSPropertyDescriptor> desc);
JS::MutableHandle<JSPropertyDescriptor> desc,
JS::ObjectOpResult &result);
bool ownPropertyKeys(JSContext *cx, JS::HandleObject proxy, JS::AutoIdVector &props);
bool delete_(JSContext *cx, JS::HandleObject proxy, JS::HandleId id, bool *bp);
bool preventExtensions(JSContext *cx, JS::HandleObject proxy, bool *succeeded);
bool delete_(JSContext *cx, JS::HandleObject proxy, JS::HandleId id,
JS::ObjectOpResult &result);
bool preventExtensions(JSContext *cx, JS::HandleObject proxy, JS::ObjectOpResult &result);
bool isExtensible(JSContext *cx, JS::HandleObject proxy, bool *extensible);
bool has(JSContext *cx, JS::HandleObject proxy, JS::HandleId id, bool *bp);
bool get(JSContext *cx, JS::HandleObject proxy, JS::HandleObject receiver,
JS::HandleId id, JS::MutableHandleValue vp);
bool set(JSContext *cx, JS::HandleObject proxy, JS::HandleObject receiver,
JS::HandleId id, bool strict, JS::MutableHandleValue vp);
JS::HandleId id, JS::MutableHandleValue vp, JS::ObjectOpResult &result);
bool callOrConstruct(JSContext *cx, JS::HandleObject proxy, const JS::CallArgs &args,
bool construct);
@ -54,7 +56,7 @@ class WrapperOwner : public virtual JavaScriptShared
bool hasInstance(JSContext *cx, JS::HandleObject proxy, JS::MutableHandleValue v, bool *bp);
bool objectClassIs(JSContext *cx, JS::HandleObject obj, js::ESClassValue classValue);
const char* className(JSContext *cx, JS::HandleObject proxy);
bool getPrototypeOf(JSContext *cx, JS::HandleObject proxy, JS::MutableHandleObject protop);
bool getPrototype(JSContext *cx, JS::HandleObject proxy, JS::MutableHandleObject protop);
bool regexp_toShared(JSContext *cx, JS::HandleObject proxy, js::RegExpGuard *g);
@ -93,6 +95,10 @@ class WrapperOwner : public virtual JavaScriptShared
bool ipcfail(JSContext *cx);
// Check whether a return status is okay, and if not, propagate its error.
//
// If 'status' might be a ReturnObjectOpResult, which is only possible for
// a subset of the operations below, 'result' must be passed.
bool ok(JSContext *cx, const ReturnStatus &status, JS::ObjectOpResult &result);
bool ok(JSContext *cx, const ReturnStatus &status);
bool inactive_;
@ -100,8 +106,7 @@ class WrapperOwner : public virtual JavaScriptShared
/*** Dummy call handlers ***/
public:
virtual bool SendDropObject(const ObjectId &objId) = 0;
virtual bool SendPreventExtensions(const ObjectId &objId, ReturnStatus *rs,
bool *succeeded) = 0;
virtual bool SendPreventExtensions(const ObjectId &objId, ReturnStatus *rs) = 0;
virtual bool SendGetPropertyDescriptor(const ObjectId &objId, const JSIDVariant &id,
ReturnStatus *rs,
PPropertyDescriptor *out) = 0;
@ -113,7 +118,7 @@ class WrapperOwner : public virtual JavaScriptShared
const PPropertyDescriptor &flags,
ReturnStatus *rs) = 0;
virtual bool SendDelete(const ObjectId &objId, const JSIDVariant &id,
ReturnStatus *rs, bool *success) = 0;
ReturnStatus *rs) = 0;
virtual bool SendHas(const ObjectId &objId, const JSIDVariant &id,
ReturnStatus *rs, bool *bp) = 0;
@ -123,8 +128,8 @@ class WrapperOwner : public virtual JavaScriptShared
const JSIDVariant &id,
ReturnStatus *rs, JSVariant *result) = 0;
virtual bool SendSet(const ObjectId &objId, const ObjectVariant &receiverVar,
const JSIDVariant &id, const bool &strict,
const JSVariant &value, ReturnStatus *rs, JSVariant *result) = 0;
const JSIDVariant &id, const JSVariant &value,
ReturnStatus *rs, JSVariant *result) = 0;
virtual bool SendIsExtensible(const ObjectId &objId, ReturnStatus *rs,
bool *result) = 0;
@ -136,7 +141,7 @@ class WrapperOwner : public virtual JavaScriptShared
virtual bool SendObjectClassIs(const ObjectId &objId, const uint32_t &classValue,
bool *result) = 0;
virtual bool SendClassName(const ObjectId &objId, nsString *result) = 0;
virtual bool SendGetPrototypeOf(const ObjectId &objId, ReturnStatus *rs, ObjectOrNullVariant *result) = 0;
virtual bool SendGetPrototype(const ObjectId &objId, ReturnStatus *rs, ObjectOrNullVariant *result) = 0;
virtual bool SendRegExpToShared(const ObjectId &objId, ReturnStatus *rs, nsString *source,
uint32_t *flags) = 0;

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

@ -44,6 +44,158 @@ namespace JS {
class AutoIdVector;
/*
* Per ES6, the [[DefineOwnProperty]] internal method has three different
* possible outcomes:
*
* - It can throw an exception (which we indicate by returning false).
*
* - It can return true, indicating unvarnished success.
*
* - It can return false, indicating "strict failure". The property could
* not be defined. It's an error, but no exception was thrown.
*
* It's not just [[DefineOwnProperty]]: all the mutating internal methods have
* the same three outcomes. (The other affected internal methods are [[Set]],
* [[Delete]], [[SetPrototypeOf]], and [[PreventExtensions]].)
*
* If you think this design is awful, you're not alone. But as it's the
* standard, we must represent these boolean "success" values somehow.
* ObjectOpSuccess is the class for this. It's like a bool, but when it's false
* it also stores an error code.
*
* Typical usage:
*
* ObjectOpResult result;
* if (!DefineProperty(cx, obj, id, ..., result))
* return false;
* if (!result)
* return result.reportError(cx, obj, id);
*
* Users don't have to call `result.report()`; another possible ending is:
*
* argv.rval().setBoolean(bool(result));
* return true;
*/
class ObjectOpResult
{
private:
uint32_t code_;
public:
enum { OkCode = 0, Uninitialized = 0xffffffff };
ObjectOpResult() : code_(Uninitialized) {}
/* Return true if fail() was not called. */
bool ok() const {
MOZ_ASSERT(code_ != Uninitialized);
return code_ == OkCode;
}
explicit operator bool() const { return ok(); }
/* Set this ObjectOpResult to true and return true. */
bool succeed() {
code_ = OkCode;
return true;
}
/*
* Set this ObjectOpResult to false with an error code.
*
* Always returns true, as a convenience. Typical usage will be:
*
* if (funny condition)
* return result.fail(JSMSG_CANT_DO_THE_THINGS);
*
* The true return value indicates that no exception is pending, and it
* would be OK to ignore the failure and continue.
*/
bool fail(uint32_t msg) {
MOZ_ASSERT(msg != OkCode);
code_ = msg;
return true;
}
JS_PUBLIC_API(bool) failCantRedefineProp();
JS_PUBLIC_API(bool) failReadOnly();
JS_PUBLIC_API(bool) failGetterOnly();
JS_PUBLIC_API(bool) failCantSetInterposed();
JS_PUBLIC_API(bool) failCantDelete();
JS_PUBLIC_API(bool) failCantDeleteWindowElement();
JS_PUBLIC_API(bool) failCantDeleteWindowNamedProperty();
JS_PUBLIC_API(bool) failCantPreventExtensions();
uint32_t failureCode() const {
MOZ_ASSERT(!ok());
return code_;
}
/*
* Report an error or warning if necessary; return true to proceed and
* false if an error was reported. Call this when failure should cause
* a warning if extraWarnings are enabled.
*
* The precise rules are like this:
*
* - If ok(), then we succeeded. Do nothing and return true.
* - Otherwise, if |strict| is true, or if cx has both extraWarnings and
* werrorOption enabled, throw a TypeError and return false.
* - Otherwise, if cx has extraWarnings enabled, emit a warning and
* return true.
* - Otherwise, do nothing and return true.
*/
bool checkStrictErrorOrWarning(JSContext *cx, HandleObject obj, HandleId id, bool strict) {
if (ok())
return true;
return reportStrictErrorOrWarning(cx, obj, id, strict);
}
/*
* The same as checkStrictErrorOrWarning(cx, id, strict), except the
* operation is not associated with a particular property id. This is
* used for [[PreventExtensions]] and [[SetPrototypeOf]]. failureCode()
* must not be an error that has "{0}" in the error message.
*/
bool checkStrictErrorOrWarning(JSContext *cx, HandleObject obj, bool strict) {
return ok() || reportStrictErrorOrWarning(cx, obj, strict);
}
/* Throw a TypeError. Call this only if !ok(). */
bool reportError(JSContext *cx, HandleObject obj, HandleId id) {
return reportStrictErrorOrWarning(cx, obj, id, true);
}
/*
* The same as reportError(cx, obj, id), except the operation is not
* associated with a particular property id.
*/
bool reportError(JSContext *cx, HandleObject obj) {
return reportStrictErrorOrWarning(cx, obj, true);
}
/* Helper function for checkStrictErrorOrWarning's slow path. */
JS_PUBLIC_API(bool) reportStrictErrorOrWarning(JSContext *cx, HandleObject obj, HandleId id, bool strict);
JS_PUBLIC_API(bool) reportStrictErrorOrWarning(JSContext *cx, HandleObject obj, bool strict);
/*
* Convenience method. Return true if ok() or if strict is false; otherwise
* throw a TypeError and return false.
*/
bool checkStrict(JSContext *cx, HandleObject obj, HandleId id) {
return checkStrictErrorOrWarning(cx, obj, id, true);
}
/*
* Convenience method. The same as checkStrict(cx, id), except the
* operation is not associated with a particular property id.
*/
bool checkStrict(JSContext *cx, HandleObject obj) {
return checkStrictErrorOrWarning(cx, obj, true);
}
};
}
// JSClass operation signatures.
@ -52,8 +204,10 @@ class AutoIdVector;
// be a string (Unicode property identifier) or an int (element index). The
// *vp out parameter, on success, is the new property value after the action.
typedef bool
(* JSPropertyOp)(JSContext *cx, JS::HandleObject obj, JS::HandleId id,
JS::MutableHandleValue vp);
(* JSGetterOp)(JSContext *cx, JS::HandleObject obj, JS::HandleId id,
JS::MutableHandleValue vp);
typedef JSGetterOp JSAddPropertyOp;
// Set a property named by id in obj, treating the assignment as strict
// mode code if strict is true. Note the jsid id type -- id may be a string
@ -61,25 +215,25 @@ typedef bool
// parameter, on success, is the new property value after the
// set.
typedef bool
(* JSStrictPropertyOp)(JSContext *cx, JS::HandleObject obj, JS::HandleId id,
bool strict, JS::MutableHandleValue vp);
(* JSSetterOp)(JSContext *cx, JS::HandleObject obj, JS::HandleId id,
JS::MutableHandleValue vp, JS::ObjectOpResult &result);
// Delete a property named by id in obj.
//
// If an error occurred, return false as per normal JSAPI error practice.
//
// If no error occurred, but the deletion attempt wasn't allowed (perhaps
// because the property was non-configurable), set *succeeded to false and
// because the property was non-configurable), call result.fail() and
// return true. This will cause |delete obj[id]| to evaluate to false in
// non-strict mode code, and to throw a TypeError in strict mode code.
//
// If no error occurred and the deletion wasn't disallowed (this is *not* the
// same as saying that a deletion actually occurred -- deleting a non-existent
// property, or an inherited property, is allowed -- it's just pointless),
// set *succeeded to true and return true.
// call result.succeed() and return true.
typedef bool
(* JSDeletePropertyOp)(JSContext *cx, JS::HandleObject obj, JS::HandleId id,
bool *succeeded);
JS::ObjectOpResult &result);
// The type of ObjectOps::enumerate. This callback overrides a portion of SpiderMonkey's default
// [[Enumerate]] internal method. When an ordinary object is enumerated, that object and each object
@ -166,7 +320,8 @@ typedef bool
JS::MutableHandleObject objp, JS::MutableHandle<Shape*> propp);
typedef bool
(* DefinePropertyOp)(JSContext *cx, JS::HandleObject obj, JS::HandleId id, JS::HandleValue value,
JSPropertyOp getter, JSStrictPropertyOp setter, unsigned attrs);
JSGetterOp getter, JSSetterOp setter, unsigned attrs,
JS::ObjectOpResult &result);
typedef bool
(* HasPropertyOp)(JSContext *cx, JS::HandleObject obj, JS::HandleId id, bool *foundp);
typedef bool
@ -174,12 +329,13 @@ typedef bool
JS::MutableHandleValue vp);
typedef bool
(* SetPropertyOp)(JSContext *cx, JS::HandleObject obj, JS::HandleObject receiver, JS::HandleId id,
JS::MutableHandleValue vp, bool strict);
JS::MutableHandleValue vp, JS::ObjectOpResult &result);
typedef bool
(* GetOwnPropertyOp)(JSContext *cx, JS::HandleObject obj, JS::HandleId id,
JS::MutableHandle<JSPropertyDescriptor> desc);
typedef bool
(* DeletePropertyOp)(JSContext *cx, JS::HandleObject obj, JS::HandleId id, bool *succeeded);
(* DeletePropertyOp)(JSContext *cx, JS::HandleObject obj, JS::HandleId id,
JS::ObjectOpResult &result);
typedef bool
(* WatchOp)(JSContext *cx, JS::HandleObject obj, JS::HandleId id, JS::HandleObject callable);
@ -243,10 +399,10 @@ typedef void
uint32_t flags; \
\
/* Function pointer members (may be null). */ \
JSPropertyOp addProperty; \
JSAddPropertyOp addProperty; \
JSDeletePropertyOp delProperty; \
JSPropertyOp getProperty; \
JSStrictPropertyOp setProperty; \
JSGetterOp getProperty; \
JSSetterOp setProperty; \
JSEnumerateOp enumerate; \
JSResolveOp resolve; \
JSConvertOp convert; \

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

@ -26,6 +26,7 @@ using JS::MutableHandle;
using JS::MutableHandleObject;
using JS::MutableHandleValue;
using JS::NativeImpl;
using JS::ObjectOpResult;
using JS::PrivateValue;
using JS::Value;
@ -127,13 +128,13 @@ class JS_FRIEND_API(Wrapper);
* 2. A proxy can implement more complicated prototype semantics (if, for
* example, it wants to delegate the prototype lookup to a wrapped object)
* by passing Proxy::LazyProto as the prototype at create time. This
* guarantees that the getPrototypeOf() handler method will be called every
* guarantees that the getPrototype() handler method will be called every
* time the object's prototype chain is accessed.
*
* This system is implemented with two methods: {get,set}PrototypeOf. The
* default implementation of setPrototypeOf throws a TypeError. Since it is
* This system is implemented with two methods: {get,set}Prototype. The
* default implementation of setPrototype throws a TypeError. Since it is
* not possible to create an object without a sense of prototype chain,
* handlers must implement getPrototypeOf if opting in to the dynamic
* handlers must implement getPrototype if opting in to the dynamic
* prototype system.
*/
@ -251,10 +252,13 @@ class JS_FRIEND_API(BaseProxyHandler)
virtual bool getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
MutableHandle<JSPropertyDescriptor> desc) const = 0;
virtual bool defineProperty(JSContext *cx, HandleObject proxy, HandleId id,
MutableHandle<JSPropertyDescriptor> desc) const = 0;
MutableHandle<JSPropertyDescriptor> desc,
ObjectOpResult &result) const = 0;
virtual bool ownPropertyKeys(JSContext *cx, HandleObject proxy,
AutoIdVector &props) const = 0;
virtual bool delete_(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const = 0;
virtual bool delete_(JSContext *cx, HandleObject proxy, HandleId id,
ObjectOpResult &result) const = 0;
/*
* Because [[Enumerate]] is one of the standard traps it should be overridden.
* However for convenience BaseProxyHandler includes a pure virtual implementation,
@ -267,15 +271,17 @@ class JS_FRIEND_API(BaseProxyHandler)
* These methods are standard, but the engine does not normally call them.
* They're opt-in. See "Proxy prototype chains" above.
*
* getPrototypeOf() crashes if called. setPrototypeOf() throws a TypeError.
* getPrototype() crashes if called. setPrototype() throws a TypeError.
*/
virtual bool getPrototypeOf(JSContext *cx, HandleObject proxy, MutableHandleObject protop) const;
virtual bool setPrototypeOf(JSContext *cx, HandleObject proxy, HandleObject proto, bool *bp) const;
virtual bool getPrototype(JSContext *cx, HandleObject proxy, MutableHandleObject protop) const;
virtual bool setPrototype(JSContext *cx, HandleObject proxy, HandleObject proto,
ObjectOpResult &result) const;
/* Non-standard but conceptual kin to {g,s}etPrototypeOf, so lives here. */
/* Non-standard but conceptual kin to {g,s}etPrototype, so lives here. */
virtual bool setImmutablePrototype(JSContext *cx, HandleObject proxy, bool *succeeded) const;
virtual bool preventExtensions(JSContext *cx, HandleObject proxy, bool *succeeded) const = 0;
virtual bool preventExtensions(JSContext *cx, HandleObject proxy,
ObjectOpResult &result) const = 0;
virtual bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) const = 0;
/*
@ -289,7 +295,7 @@ class JS_FRIEND_API(BaseProxyHandler)
virtual bool get(JSContext *cx, HandleObject proxy, HandleObject receiver,
HandleId id, MutableHandleValue vp) const;
virtual bool set(JSContext *cx, HandleObject proxy, HandleObject receiver,
HandleId id, bool strict, MutableHandleValue vp) const;
HandleId id, MutableHandleValue vp, ObjectOpResult &result) const;
/*
* [[Call]] and [[Construct]] are standard internal methods but according
@ -367,27 +373,30 @@ class JS_FRIEND_API(DirectProxyHandler) : public BaseProxyHandler
virtual bool getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE;
virtual bool defineProperty(JSContext *cx, HandleObject proxy, HandleId id,
MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE;
MutableHandle<JSPropertyDescriptor> desc,
ObjectOpResult &result) const MOZ_OVERRIDE;
virtual bool ownPropertyKeys(JSContext *cx, HandleObject proxy,
AutoIdVector &props) const MOZ_OVERRIDE;
virtual bool delete_(JSContext *cx, HandleObject proxy, HandleId id,
bool *bp) const MOZ_OVERRIDE;
ObjectOpResult &result) const MOZ_OVERRIDE;
virtual bool enumerate(JSContext *cx, HandleObject proxy,
MutableHandleObject objp) const MOZ_OVERRIDE;
virtual bool getPrototypeOf(JSContext *cx, HandleObject proxy,
MutableHandleObject protop) const MOZ_OVERRIDE;
virtual bool setPrototypeOf(JSContext *cx, HandleObject proxy, HandleObject proto,
bool *bp) const MOZ_OVERRIDE;
virtual bool getPrototype(JSContext *cx, HandleObject proxy,
MutableHandleObject protop) const MOZ_OVERRIDE;
virtual bool setPrototype(JSContext *cx, HandleObject proxy, HandleObject proto,
ObjectOpResult &result) const MOZ_OVERRIDE;
virtual bool setImmutablePrototype(JSContext *cx, HandleObject proxy,
bool *succeeded) const MOZ_OVERRIDE;
virtual bool preventExtensions(JSContext *cx, HandleObject proxy, bool *succeeded) const MOZ_OVERRIDE;
virtual bool preventExtensions(JSContext *cx, HandleObject proxy,
ObjectOpResult &result) const MOZ_OVERRIDE;
virtual bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) const MOZ_OVERRIDE;
virtual bool has(JSContext *cx, HandleObject proxy, HandleId id,
bool *bp) const MOZ_OVERRIDE;
virtual bool get(JSContext *cx, HandleObject proxy, HandleObject receiver,
HandleId id, MutableHandleValue vp) const MOZ_OVERRIDE;
virtual bool set(JSContext *cx, HandleObject proxy, HandleObject receiver,
HandleId id, bool strict, MutableHandleValue vp) const MOZ_OVERRIDE;
HandleId id, MutableHandleValue vp,
ObjectOpResult &result) const MOZ_OVERRIDE;
virtual bool call(JSContext *cx, HandleObject proxy, const CallArgs &args) const MOZ_OVERRIDE;
virtual bool construct(JSContext *cx, HandleObject proxy, const CallArgs &args) const MOZ_OVERRIDE;

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

@ -45,6 +45,7 @@ class MOZ_STACK_CLASS SourceBufferHolder;
class HandleValueArray;
class ObjectOpResult;
}
// Do the importing.
@ -143,6 +144,8 @@ using JS::FalseHandleValue;
using JS::HandleValueArray;
using JS::ObjectOpResult;
using JS::Zone;
} /* namespace js */

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

@ -710,7 +710,7 @@ InitCollatorClass(JSContext *cx, HandleObject Intl, Handle<GlobalObject*> global
if (!GlobalObject::getIntrinsicValue(cx, cx->global(), cx->names().CollatorCompareGet, &getter))
return nullptr;
if (!DefineProperty(cx, proto, cx->names().compare, UndefinedHandleValue,
JS_DATA_TO_FUNC_PTR(JSPropertyOp, &getter.toObject()),
JS_DATA_TO_FUNC_PTR(JSGetterOp, &getter.toObject()),
nullptr, JSPROP_GETTER | JSPROP_SHARED))
{
return nullptr;
@ -1203,7 +1203,7 @@ InitNumberFormatClass(JSContext *cx, HandleObject Intl, Handle<GlobalObject*> gl
if (!GlobalObject::getIntrinsicValue(cx, cx->global(), cx->names().NumberFormatFormatGet, &getter))
return nullptr;
if (!DefineProperty(cx, proto, cx->names().format, UndefinedHandleValue,
JS_DATA_TO_FUNC_PTR(JSPropertyOp, &getter.toObject()),
JS_DATA_TO_FUNC_PTR(JSGetterOp, &getter.toObject()),
nullptr, JSPROP_GETTER | JSPROP_SHARED))
{
return nullptr;
@ -1662,7 +1662,7 @@ InitDateTimeFormatClass(JSContext *cx, HandleObject Intl, Handle<GlobalObject*>
if (!GlobalObject::getIntrinsicValue(cx, cx->global(), cx->names().DateTimeFormatFormatGet, &getter))
return nullptr;
if (!DefineProperty(cx, proto, cx->names().format, UndefinedHandleValue,
JS_DATA_TO_FUNC_PTR(JSPropertyOp, &getter.toObject()),
JS_DATA_TO_FUNC_PTR(JSGetterOp, &getter.toObject()),
nullptr, JSPROP_GETTER | JSPROP_SHARED))
{
return nullptr;

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

@ -444,25 +444,12 @@ obj_setPrototypeOf(JSContext *cx, unsigned argc, Value *vp)
return true;
}
/* Step 5-6. */
/* Step 5-7. */
RootedObject obj(cx, &args[0].toObject());
RootedObject newProto(cx, args[1].toObjectOrNull());
bool success;
if (!SetPrototype(cx, obj, newProto, &success))
if (!SetPrototype(cx, obj, newProto))
return false;
/* Step 7. */
if (!success) {
UniquePtr<char[], JS::FreePolicy> bytes(DecompileValueGenerator(cx, JSDVG_SEARCH_STACK,
args[0], NullPtr()));
if (!bytes)
return false;
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_SETPROTOTYPEOF_FAIL,
bytes.get());
return false;
}
/* Step 8. */
args.rval().set(args[0]);
return true;
@ -833,8 +820,7 @@ js::obj_defineProperty(JSContext *cx, unsigned argc, Value *vp)
if (!desc.initialize(cx, args.get(2)))
return false;
bool ignored;
if (!StandardDefineProperty(cx, obj, id, desc, true, &ignored))
if (!StandardDefineProperty(cx, obj, id, desc))
return false;
args.rval().setObject(*obj);
@ -898,21 +884,9 @@ obj_preventExtensions(JSContext *cx, unsigned argc, Value *vp)
if (!args.get(0).isObject())
return true;
// Steps 2-3.
// Steps 2-5.
RootedObject obj(cx, &args.get(0).toObject());
bool status;
if (!PreventExtensions(cx, obj, &status))
return false;
// Step 4.
if (!status) {
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_CANT_CHANGE_EXTENSIBILITY);
return false;
}
// Step 5.
return true;
return PreventExtensions(cx, obj);
}
// ES6 draft rev27 (2014/08/24) 19.1.2.5 Object.freeze(O)
@ -1044,16 +1018,9 @@ ProtoSetter(JSContext *cx, unsigned argc, Value *vp)
}
Rooted<JSObject*> newProto(cx, args[0].toObjectOrNull());
bool success;
if (!SetPrototype(cx, obj, newProto, &success))
if (!SetPrototype(cx, obj, newProto))
return false;
if (!success) {
ReportValueError(cx, JSMSG_SETPROTOTYPEOF_FAIL, JSDVG_IGNORE_STACK, thisv, js::NullPtr());
return false;
}
args.rval().setUndefined();
return true;
}

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

@ -1757,7 +1757,8 @@ ReportPropertyError(JSContext *cx,
bool
TypedObject::obj_defineProperty(JSContext *cx, HandleObject obj, HandleId id, HandleValue v,
PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
GetterOp getter, SetterOp setter, unsigned attrs,
ObjectOpResult &result)
{
Rooted<TypedObject *> typedObj(cx, &obj->as<TypedObject>());
return ReportTypedObjTypeError(cx, JSMSG_OBJECT_NOT_EXTENSIBLE, typedObj);
@ -1909,7 +1910,7 @@ TypedObject::obj_getArrayElement(JSContext *cx,
bool
TypedObject::obj_setProperty(JSContext *cx, HandleObject obj, HandleObject receiver, HandleId id,
MutableHandleValue vp, bool strict)
MutableHandleValue vp, ObjectOpResult &result)
{
Rooted<TypedObject *> typedObj(cx, &obj->as<TypedObject>());
@ -1928,13 +1929,13 @@ TypedObject::obj_setProperty(JSContext *cx, HandleObject obj, HandleObject recei
nullptr, JSMSG_CANT_REDEFINE_ARRAY_LENGTH);
return false;
}
return SetNonWritableProperty(cx, id, strict);
return result.failReadOnly();
}
uint32_t index;
if (IdIsIndex(id, &index)) {
if (obj != receiver)
return SetPropertyByDefining(cx, obj, receiver, id, vp, strict, false);
return SetPropertyByDefining(cx, obj, receiver, id, vp, false, result);
if (index >= uint32_t(typedObj->length())) {
JS_ReportErrorNumber(cx, GetErrorMessage,
@ -1945,7 +1946,9 @@ TypedObject::obj_setProperty(JSContext *cx, HandleObject obj, HandleObject recei
Rooted<TypeDescr*> elementType(cx);
elementType = &typedObj->typeDescr().as<ArrayTypeDescr>().elementType();
size_t offset = elementType->size() * index;
return ConvertAndCopyTo(cx, elementType, typedObj, offset, NullPtr(), vp);
if (!ConvertAndCopyTo(cx, elementType, typedObj, offset, NullPtr(), vp))
return false;
return result.succeed();
}
break;
}
@ -1958,16 +1961,18 @@ TypedObject::obj_setProperty(JSContext *cx, HandleObject obj, HandleObject recei
break;
if (obj != receiver)
return SetPropertyByDefining(cx, obj, receiver, id, vp, strict, false);
return SetPropertyByDefining(cx, obj, receiver, id, vp, false, result);
size_t offset = descr->fieldOffset(fieldIndex);
Rooted<TypeDescr*> fieldType(cx, &descr->fieldDescr(fieldIndex));
RootedAtom fieldName(cx, &descr->fieldName(fieldIndex));
return ConvertAndCopyTo(cx, fieldType, typedObj, offset, fieldName, vp);
if (!ConvertAndCopyTo(cx, fieldType, typedObj, offset, fieldName, vp))
return false;
return result.succeed();
}
}
return SetPropertyOnProto(cx, obj, receiver, id, vp, strict);
return SetPropertyOnProto(cx, obj, receiver, id, vp, result);
}
bool
@ -2054,18 +2059,16 @@ IsOwnId(JSContext *cx, HandleObject obj, HandleId id)
}
bool
TypedObject::obj_deleteProperty(JSContext *cx, HandleObject obj, HandleId id, bool *succeeded)
TypedObject::obj_deleteProperty(JSContext *cx, HandleObject obj, HandleId id, ObjectOpResult &result)
{
if (IsOwnId(cx, obj, id))
return ReportPropertyError(cx, JSMSG_CANT_DELETE, id);
RootedObject proto(cx, obj->getProto());
if (!proto) {
*succeeded = false;
return true;
}
if (!proto)
return result.succeed();
return DeleteProperty(cx, proto, id, succeeded);
return DeleteProperty(cx, proto, id, result);
}
bool

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

@ -529,7 +529,8 @@ class TypedObject : public JSObject
MutableHandleObject objp, MutableHandleShape propp);
static bool obj_defineProperty(JSContext *cx, HandleObject obj, HandleId id, HandleValue v,
PropertyOp getter, StrictPropertyOp setter, unsigned attrs);
GetterOp getter, SetterOp setter, unsigned attrs,
ObjectOpResult &result);
static bool obj_hasProperty(JSContext *cx, HandleObject obj, HandleId id, bool *foundp);
@ -540,12 +541,13 @@ class TypedObject : public JSObject
uint32_t index, MutableHandleValue vp);
static bool obj_setProperty(JSContext *cx, HandleObject obj, HandleObject receiver,
HandleId id, MutableHandleValue vp, bool strict);
HandleId id, MutableHandleValue vp, ObjectOpResult &result);
static bool obj_getOwnPropertyDescriptor(JSContext *cx, HandleObject obj, HandleId id,
MutableHandle<JSPropertyDescriptor> desc);
static bool obj_deleteProperty(JSContext *cx, HandleObject obj, HandleId id, bool *succeeded);
static bool obj_deleteProperty(JSContext *cx, HandleObject obj, HandleId id,
ObjectOpResult &result);
static bool obj_enumerate(JSContext *cx, HandleObject obj, AutoIdVector &properties);

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

@ -287,7 +287,8 @@ namespace ArrayType {
bool LengthGetter(JSContext* cx, JS::CallArgs args);
static bool Getter(JSContext* cx, HandleObject obj, HandleId idval, MutableHandleValue vp);
static bool Setter(JSContext* cx, HandleObject obj, HandleId idval, bool strict, MutableHandleValue vp);
static bool Setter(JSContext* cx, HandleObject obj, HandleId idval, MutableHandleValue vp,
ObjectOpResult &result);
static bool AddressOfElement(JSContext* cx, unsigned argc, jsval* vp);
}
@ -300,9 +301,9 @@ namespace StructType {
bool FieldsArrayGetter(JSContext* cx, JS::CallArgs args);
static bool FieldGetter(JSContext* cx, HandleObject obj, HandleId idval,
MutableHandleValue vp);
static bool FieldSetter(JSContext* cx, HandleObject obj, HandleId idval, bool strict,
MutableHandleValue vp);
MutableHandleValue vp);
static bool FieldSetter(JSContext* cx, HandleObject obj, HandleId idval,
MutableHandleValue vp, ObjectOpResult &result);
static bool AddressOfField(JSContext* cx, unsigned argc, jsval* vp);
static bool Define(JSContext* cx, unsigned argc, jsval* vp);
}
@ -4625,7 +4626,8 @@ ArrayType::Getter(JSContext* cx, HandleObject obj, HandleId idval, MutableHandle
}
bool
ArrayType::Setter(JSContext* cx, HandleObject obj, HandleId idval, bool strict, MutableHandleValue vp)
ArrayType::Setter(JSContext* cx, HandleObject obj, HandleId idval, MutableHandleValue vp,
ObjectOpResult &result)
{
// This should never happen, but we'll check to be safe.
if (!CData::IsCData(obj)) {
@ -4637,7 +4639,7 @@ ArrayType::Setter(JSContext* cx, HandleObject obj, HandleId idval, bool strict,
// CData, regardless of CType.)
JSObject* typeObj = CData::GetCType(obj);
if (CType::GetTypeCode(typeObj) != TYPE_array)
return true;
return result.succeed();
// Convert the index to a size_t and bounds-check it.
size_t index;
@ -4647,7 +4649,7 @@ ArrayType::Setter(JSContext* cx, HandleObject obj, HandleId idval, bool strict,
if (!ok && JSID_IS_STRING(idval) && !StringToInteger(cx, JSID_TO_STRING(idval), &dummy)) {
// String either isn't a number, or doesn't fit in size_t.
// Chances are it's a regular property lookup, so return.
return true;
return result.succeed();
}
if (!ok || index >= length) {
JS_ReportError(cx, "invalid index");
@ -4657,7 +4659,9 @@ ArrayType::Setter(JSContext* cx, HandleObject obj, HandleId idval, bool strict,
JSObject* baseType = GetBaseType(typeObj);
size_t elementSize = CType::GetSize(baseType);
char* data = static_cast<char*>(CData::GetData(obj)) + elementSize * index;
return ImplicitConvert(cx, vp, baseType, data, false, nullptr);
if (!ImplicitConvert(cx, vp, baseType, data, false, nullptr))
return false;
return result.succeed();
}
bool
@ -5290,7 +5294,8 @@ StructType::FieldGetter(JSContext* cx, HandleObject obj, HandleId idval, Mutable
}
bool
StructType::FieldSetter(JSContext* cx, HandleObject obj, HandleId idval, bool strict, MutableHandleValue vp)
StructType::FieldSetter(JSContext* cx, HandleObject obj, HandleId idval, MutableHandleValue vp,
ObjectOpResult &result)
{
if (!CData::IsCData(obj)) {
JS_ReportError(cx, "not a CData");
@ -5308,7 +5313,9 @@ StructType::FieldSetter(JSContext* cx, HandleObject obj, HandleId idval, bool st
return false;
char* data = static_cast<char*>(CData::GetData(obj)) + field->mOffset;
return ImplicitConvert(cx, vp, field->mType, data, false, nullptr);
if (!ImplicitConvert(cx, vp, field->mType, data, false, nullptr))
return false;
return result.succeed();
}
bool

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

@ -749,14 +749,8 @@ Parser<FullParseHandler>::cloneDestructuringDefault(ParseNode *opn)
{
MOZ_ASSERT(opn->isKind(PNK_ASSIGN));
ParseNode *target = cloneLeftHandSide(opn->pn_left);
if (!target)
return nullptr;
ParseNode *defaultNode = cloneParseTree(opn->pn_right);
if (!defaultNode)
return nullptr;
return handler.new_<BinaryNode>(opn->getKind(), JSOP_NOP, opn->pn_pos, target, defaultNode);
report(ParseError, false, opn, JSMSG_DEFAULT_IN_PATTERN);
return null();
}
/*

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

@ -348,12 +348,12 @@ JSPropertyDescriptor::trace(JSTracer *trc)
if ((attrs & JSPROP_GETTER) && getter) {
JSObject *tmp = JS_FUNC_TO_DATA_PTR(JSObject *, getter);
MarkObjectRoot(trc, &tmp, "Descriptor::get");
getter = JS_DATA_TO_FUNC_PTR(JSPropertyOp, tmp);
getter = JS_DATA_TO_FUNC_PTR(JSGetterOp, tmp);
}
if ((attrs & JSPROP_SETTER) && setter) {
JSObject *tmp = JS_FUNC_TO_DATA_PTR(JSObject *, setter);
MarkObjectRoot(trc, &tmp, "Descriptor::set");
setter = JS_DATA_TO_FUNC_PTR(JSStrictPropertyOp, tmp);
setter = JS_DATA_TO_FUNC_PTR(JSSetterOp, tmp);
}
}

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

@ -8,6 +8,7 @@ GeckoProgram('gdb-tests', linkage=None)
UNIFIED_SOURCES += [
'gdb-tests.cpp',
'tests/test-Interpreter.cpp',
'tests/test-jsid.cpp',
'tests/test-JSObject.cpp',
'tests/test-JSString.cpp',

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

@ -12,6 +12,10 @@ class InterpreterTypeCache(object):
def __init__(self):
self.tValue = gdb.lookup_type('JS::Value')
self.tJSOp = gdb.lookup_type('JSOp')
self.tScriptFrameIterData = gdb.lookup_type('js::ScriptFrameIter::Data')
self.tInterpreterFrame = gdb.lookup_type('js::InterpreterFrame')
self.tBaselineFrame = gdb.lookup_type('js::jit::BaselineFrame')
self.tRematerializedFrame = gdb.lookup_type('js::jit::RematerializedFrame')
@pretty_printer('js::InterpreterRegs')
class InterpreterRegs(object):
@ -37,3 +41,40 @@ class InterpreterRegs(object):
pc = 'pc = {} ({})'.format(pc.cast(self.cache.void_ptr_t), opcode)
return '{{ {}, {}, {} }}'.format(fp_, sp, pc)
@pretty_printer('js::AbstractFramePtr')
class AbstractFramePtr(object):
Tag_ScriptFrameIterData = 0x0
Tag_InterpreterFrame = 0x1
Tag_BaselineFrame = 0x2
Tag_RematerializedFrame = 0x3
TagMask = 0x3
def __init__(self, value, cache):
self.value = value
self.cache = cache
if not cache.mod_Interpreter:
cache.mod_Interpreter = InterpreterTypeCache()
self.itc = cache.mod_Interpreter
def to_string(self):
ptr = self.value['ptr_']
tag = ptr & AbstractFramePtr.TagMask
ptr = ptr & ~AbstractFramePtr.TagMask
if tag == AbstractFramePtr.Tag_ScriptFrameIterData:
label = 'js::ScriptFrameIter::Data'
ptr = ptr.cast(self.itc.tScriptFrameIterData.pointer())
if tag == AbstractFramePtr.Tag_InterpreterFrame:
label = 'js::InterpreterFrame'
ptr = ptr.cast(self.itc.tInterpreterFrame.pointer())
if tag == AbstractFramePtr.Tag_BaselineFrame:
label = 'js::jit::BaselineFrame'
ptr = ptr.cast(self.itc.tBaselineFrame.pointer())
if tag == AbstractFramePtr.Tag_RematerializedFrame:
label = 'js::jit::RematerializedFrame'
ptr = ptr.cast(self.itc.tRematerializedFrame.pointer())
return 'AbstractFramePtr (({} *) {})'.format(label, ptr)
# Provide the ptr_ field as a child, so it prints after the pretty string
# provided above.
def children(self):
yield ('ptr_', self.value['ptr_'])

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

@ -0,0 +1,92 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* 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 "gdb-tests.h"
#include "jsapi.h"
#include "vm/Stack.h"
namespace js {
void
GDBTestInitInterpreterRegs(InterpreterRegs &regs,
js::InterpreterFrame *fp_,
JS::Value *sp,
uint8_t *pc)
{
regs.fp_ = fp_;
regs.sp = sp;
regs.pc = pc;
}
void
GDBTestInitAbstractFramePtr(AbstractFramePtr &frame, void *ptr)
{
MOZ_ASSERT((uintptr_t(ptr) & AbstractFramePtr::TagMask) == 0);
frame.ptr_ = uintptr_t(ptr) | AbstractFramePtr::Tag_ScriptFrameIterData;
}
void
GDBTestInitAbstractFramePtr(AbstractFramePtr &frame, InterpreterFrame *ptr)
{
MOZ_ASSERT((uintptr_t(ptr) & AbstractFramePtr::TagMask) == 0);
frame.ptr_ = uintptr_t(ptr) | AbstractFramePtr::Tag_InterpreterFrame;
}
void
GDBTestInitAbstractFramePtr(AbstractFramePtr &frame, jit::BaselineFrame *ptr)
{
MOZ_ASSERT((uintptr_t(ptr) & AbstractFramePtr::TagMask) == 0);
frame.ptr_ = uintptr_t(ptr) | AbstractFramePtr::Tag_BaselineFrame;
}
void
GDBTestInitAbstractFramePtr(AbstractFramePtr &frame, jit::RematerializedFrame *ptr)
{
MOZ_ASSERT((uintptr_t(ptr) & AbstractFramePtr::TagMask) == 0);
frame.ptr_ = uintptr_t(ptr) | AbstractFramePtr::Tag_RematerializedFrame;
}
} // namespace js
FRAGMENT(Interpreter, Regs) {
struct FakeFrame {
js::InterpreterFrame frame;
JS::Value slot0;
JS::Value slot1;
JS::Value slot2;
} fakeFrame;
uint8_t fakeOpcode = JSOP_IFEQ;
js::InterpreterRegs regs;
js::GDBTestInitInterpreterRegs(regs, &fakeFrame.frame, &fakeFrame.slot2, &fakeOpcode);
breakpoint();
(void) regs;
}
FRAGMENT(Interpreter, AbstractFramePtr) {
js::AbstractFramePtr sfidptr;
GDBTestInitAbstractFramePtr(sfidptr, (js::ScriptFrameIter::Data *) 0xdeeb0);
js::AbstractFramePtr ifptr;
GDBTestInitAbstractFramePtr(ifptr, (js::InterpreterFrame *) 0x8badf00);
js::AbstractFramePtr bfptr;
GDBTestInitAbstractFramePtr(bfptr, (js::jit::BaselineFrame *) 0xbadcafe0);
js::AbstractFramePtr rfptr;
GDBTestInitAbstractFramePtr(rfptr, (js::jit::RematerializedFrame *) 0xdabbad00);
breakpoint();
(void) sfidptr;
(void) ifptr;
(void) bfptr;
(void) rfptr;
}

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

@ -0,0 +1,14 @@
# Test printing interpreter internal data structures.
assert_subprinter_registered('SpiderMonkey', 'js::InterpreterRegs')
run_fragment('Interpreter.Regs')
assert_pretty('regs', '{ fp_ = , sp = fp_.slots() + 2, pc = (JSOP_IFEQ) }')
run_fragment('Interpreter.AbstractFramePtr')
assert_pretty('sfidptr', 'AbstractFramePtr ((js::ScriptFrameIter::Data *) ) = {ptr_ = 913072}')
assert_pretty('ifptr', 'AbstractFramePtr ((js::InterpreterFrame *) ) = {ptr_ = 146464513}')
assert_pretty('bfptr', 'AbstractFramePtr ((js::jit::BaselineFrame *) ) = {ptr_ = 3135025122}')
assert_pretty('rfptr', 'AbstractFramePtr ((js::jit::RematerializedFrame *) ) = {ptr_ = 3669732611}')

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

@ -166,18 +166,32 @@ assertEq(evaled, true);
assertThrowsInstanceOf(() => new Function('var [...rest = defaults] = [];'), SyntaxError);
// bug 1124480: destructuring defaults should work correctly as part of for-in
b = undefined;
for (var [a, b = 10] in " ") {}
assertEq(b, 10);
var defaultsSupportedInForVar = false;
try
{
new Function('for (var [a, b = 10] in " ") {}');
defaultsSupportedInForVar = true;
}
catch (e)
{
}
b = undefined;
for (let [a, c = 10] in " ") { b = c; }
assertEq(b, 10);
if (defaultsSupportedInForVar) {
new Function(`
b = undefined;
for (var [a, b = 10] in " ") {}
assertEq(b, 10);
b = undefined;
for (var {1: b = 10} in " ") {}
assertEq(b, 10);
b = undefined;
for (let [a, c = 10] in " ") { b = c; }
assertEq(b, 10);
b = undefined;
for (let {1: c = 10} in " ") { b = c; }
assertEq(b, 10);
b = undefined;
for (var {1: b = 10} in " ") {}
assertEq(b, 10);
b = undefined;
for (let {1: c = 10} in " ") { b = c; }
assertEq(b, 10);
`)();
}

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

@ -110,7 +110,7 @@ check("o[- (o)]");
// A few one off tests
check_one("6", (function () { 6() }), " is not a function");
check_one("Array.prototype.reverse.call(...)", (function () { Array.prototype.reverse.call('123'); }), " is read-only");
check_one("0", (function () { Array.prototype.reverse.call('123'); }), " is read-only");
check_one(`(intermediate value)[Symbol.iterator](...).next(...).value`,
function () { var [{ x }] = [null, {}]; }, " is null");
check_one(`(intermediate value)[Symbol.iterator](...).next(...).value`,

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

@ -2005,8 +2005,10 @@ BaselineCompiler::emit_JSOP_STRICTSETELEM()
}
typedef bool (*DeleteElementFn)(JSContext *, HandleValue, HandleValue, bool *);
static const VMFunction DeleteElementStrictInfo = FunctionInfo<DeleteElementFn>(DeleteElement<true>);
static const VMFunction DeleteElementNonStrictInfo = FunctionInfo<DeleteElementFn>(DeleteElement<false>);
static const VMFunction DeleteElementStrictInfo
= FunctionInfo<DeleteElementFn>(DeleteElementJit<true>);
static const VMFunction DeleteElementNonStrictInfo
= FunctionInfo<DeleteElementFn>(DeleteElementJit<false>);
bool
BaselineCompiler::emit_JSOP_DELELEM()
@ -2170,8 +2172,10 @@ BaselineCompiler::emit_JSOP_GETXPROP()
}
typedef bool (*DeletePropertyFn)(JSContext *, HandleValue, HandlePropertyName, bool *);
static const VMFunction DeletePropertyStrictInfo = FunctionInfo<DeletePropertyFn>(DeleteProperty<true>);
static const VMFunction DeletePropertyNonStrictInfo = FunctionInfo<DeletePropertyFn>(DeleteProperty<false>);
static const VMFunction DeletePropertyStrictInfo =
FunctionInfo<DeletePropertyFn>(DeletePropertyJit<true>);
static const VMFunction DeletePropertyNonStrictInfo =
FunctionInfo<DeletePropertyFn>(DeletePropertyJit<false>);
bool
BaselineCompiler::emit_JSOP_DELPROP()

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

@ -8374,7 +8374,7 @@ DoSetPropFallback(JSContext *cx, BaselineFrame *frame, ICSetProp_Fallback *stub_
MOZ_ASSERT(op == JSOP_SETPROP || op == JSOP_STRICTSETPROP);
RootedValue v(cx, rhs);
if (!SetProperty(cx, obj, obj, id, &v, op == JSOP_STRICTSETPROP))
if (!PutProperty(cx, obj, id, &v, op == JSOP_STRICTSETPROP))
return false;
}

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

@ -8147,9 +8147,9 @@ CodeGenerator::visitCallSetProperty(LCallSetProperty *ins)
typedef bool (*DeletePropertyFn)(JSContext *, HandleValue, HandlePropertyName, bool *);
static const VMFunction DeletePropertyStrictInfo =
FunctionInfo<DeletePropertyFn>(DeleteProperty<true>);
FunctionInfo<DeletePropertyFn>(DeletePropertyJit<true>);
static const VMFunction DeletePropertyNonStrictInfo =
FunctionInfo<DeletePropertyFn>(DeleteProperty<false>);
FunctionInfo<DeletePropertyFn>(DeletePropertyJit<false>);
void
CodeGenerator::visitCallDeleteProperty(LCallDeleteProperty *lir)
@ -8165,9 +8165,9 @@ CodeGenerator::visitCallDeleteProperty(LCallDeleteProperty *lir)
typedef bool (*DeleteElementFn)(JSContext *, HandleValue, HandleValue, bool *);
static const VMFunction DeleteElementStrictInfo =
FunctionInfo<DeleteElementFn>(DeleteElement<true>);
FunctionInfo<DeleteElementFn>(DeleteElementJit<true>);
static const VMFunction DeleteElementNonStrictInfo =
FunctionInfo<DeleteElementFn>(DeleteElement<false>);
FunctionInfo<DeleteElementFn>(DeleteElementJit<false>);
void
CodeGenerator::visitCallDeleteElement(LCallDeleteElement *lir)

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

@ -1005,13 +1005,13 @@ EmitGetterCall(JSContext *cx, MacroAssembler &masm,
Register argObjReg = argUintNReg;
Register argIdReg = regSet.takeGeneral();
PropertyOp target = shape->getterOp();
GetterOp target = shape->getterOp();
MOZ_ASSERT(target);
// Push stubCode for marking.
attacher.pushStubCodePointer(masm);
// JSPropertyOp: bool fn(JSContext *cx, HandleObject obj, HandleId id, MutableHandleValue vp)
// JSGetterOp: bool fn(JSContext *cx, HandleObject obj, HandleId id, MutableHandleValue vp)
// Push args on stack first so we can take pointers to make handles.
masm.Push(UndefinedValue());
@ -1463,6 +1463,13 @@ GetPropertyIC::tryAttachTypedArrayLength(JSContext *cx, HandleScript outerScript
return linkAndAttachStub(cx, masm, attacher, ion, "typed array length");
}
static void
PushObjectOpResult(MacroAssembler &masm, uint32_t value = ObjectOpResult::Uninitialized)
{
static_assert(sizeof(ObjectOpResult) == sizeof(int32_t),
"ObjectOpResult size must match size reserved by masm.Push() here");
masm.Push(Imm32(value));
}
static bool
EmitCallProxyGet(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &attacher,
@ -1507,6 +1514,9 @@ EmitCallProxyGet(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &at
masm.Push(object);
masm.movePtr(StackPointer, argProxyReg);
// Unused space, to keep the same stack layout as Proxy::set frames.
PushObjectOpResult(masm, 0);
masm.loadJSContext(argJSContextReg);
if (!masm.icBuildOOLFakeExitFrame(returnAddr, aic))
@ -2098,16 +2108,63 @@ IsCacheableSetPropCallPropertyOp(HandleObject obj, HandleObject holder, HandleSh
if (shape->hasSetterValue())
return false;
// Despite the vehement claims of Shape.h that writable() is only
// relevant for data descriptors, some PropertyOp setters care
// desperately about its value. The flag should be always true, apart
// from these rare instances.
// Despite the vehement claims of Shape.h that writable() is only relevant
// for data descriptors, some SetterOps care desperately about its
// value. The flag should be always true, apart from these rare instances.
if (!shape->writable())
return false;
return true;
}
static bool
ReportStrictErrorOrWarning(JSContext *cx, JS::HandleObject obj, JS::HandleId id, bool strict,
JS::ObjectOpResult &result)
{
return result.reportStrictErrorOrWarning(cx, obj, id, strict);
}
template <class FrameLayout>
void
EmitObjectOpResultCheck(MacroAssembler &masm, Label *failure, bool strict,
Register scratchReg,
Register argJSContextReg,
Register argObjReg,
Register argIdReg,
Register argStrictReg,
Register argResultReg)
{
// if (!result) {
Label noStrictError;
masm.branch32(Assembler::Equal,
Address(StackPointer,
FrameLayout::offsetOfObjectOpResult()),
Imm32(ObjectOpResult::OkCode),
&noStrictError);
// if (!ReportStrictErrorOrWarning(cx, obj, id, strict, &result))
// goto failure;
masm.loadJSContext(argJSContextReg);
masm.computeEffectiveAddress(
Address(StackPointer, FrameLayout::offsetOfId()),
argIdReg);
masm.move32(Imm32(strict), argStrictReg);
masm.computeEffectiveAddress(
Address(StackPointer, FrameLayout::offsetOfObjectOpResult()),
argResultReg);
masm.setupUnalignedABICall(5, scratchReg);
masm.passABIArg(argJSContextReg);
masm.passABIArg(argObjReg);
masm.passABIArg(argIdReg);
masm.passABIArg(argStrictReg);
masm.passABIArg(argResultReg);
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, ReportStrictErrorOrWarning));
masm.branchIfFalseBool(ReturnReg, failure);
// }
masm.bind(&noStrictError);
}
static bool
EmitCallProxySet(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &attacher,
HandleId propId, RegisterSet liveRegs, Register object,
@ -2115,18 +2172,23 @@ EmitCallProxySet(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &at
{
MacroAssembler::AfterICSaveLive aic = masm.icSaveLive(liveRegs);
// Remaining registers should be free, but we need to use |object| still
// so leave it alone.
// Remaining registers should be free, but we still need to use |object| so
// leave it alone.
//
// WARNING: We do not take() the register used by |value|, if any, so
// regSet is going to re-allocate it. Hence the emitted code must not touch
// any of the registers allocated from regSet until after the last use of
// |value|. (We can't afford to take it, either, because x86.)
RegisterSet regSet(RegisterSet::All());
regSet.take(AnyRegister(object));
// Proxy::set(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id,
// bool strict, MutableHandleValue vp)
// MutableHandleValue vp, ObjectOpResult &result)
Register argJSContextReg = regSet.takeGeneral();
Register argProxyReg = regSet.takeGeneral();
Register argIdReg = regSet.takeGeneral();
Register argVpReg = regSet.takeGeneral();
Register argStrictReg = regSet.takeGeneral();
Register argResultReg = regSet.takeGeneral();
Register scratch = regSet.takeGeneral();
@ -2146,8 +2208,11 @@ EmitCallProxySet(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &at
masm.Push(object);
masm.movePtr(StackPointer, argProxyReg);
// Allocate result out-param.
PushObjectOpResult(masm);
masm.movePtr(StackPointer, argResultReg);
masm.loadJSContext(argJSContextReg);
masm.move32(Imm32(strict? 1 : 0), argStrictReg);
if (!masm.icBuildOOLFakeExitFrame(returnAddr, aic))
return false;
@ -2159,13 +2224,19 @@ EmitCallProxySet(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &at
masm.passABIArg(argProxyReg);
masm.passABIArg(argProxyReg);
masm.passABIArg(argIdReg);
masm.passABIArg(argStrictReg);
masm.passABIArg(argVpReg);
masm.passABIArg(argResultReg);
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, Proxy::set));
// Test for failure.
// Test for error.
masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel());
// Test for strict failure. We emit the check even in non-strict mode in
// order to pick up the warning if extraWarnings is enabled.
EmitObjectOpResultCheck<IonOOLProxyExitFrameLayout>(masm, masm.exceptionLabel(), strict,
scratch, argJSContextReg, argProxyReg,
argIdReg, argVpReg, argResultReg);
// masm.leaveExitFrame & pop locals
masm.adjustStack(IonOOLProxyExitFrameLayout::Size());
@ -2376,24 +2447,28 @@ GenerateCallSetter(JSContext *cx, IonScript *ion, MacroAssembler &masm,
Register argVpReg = regSet.takeGeneral();
Register argObjReg = regSet.takeGeneral();
Register argIdReg = regSet.takeGeneral();
Register argStrictReg = regSet.takeGeneral();
Register argResultReg = regSet.takeGeneral();
SetterOp target = shape->setterOp();
MOZ_ASSERT(target);
// JSSetterOp: bool fn(JSContext *cx, HandleObject obj,
// HandleId id, MutableHandleValue vp, ObjectOpResult &result);
// First, allocate an ObjectOpResult on the stack. We push this before
// the stubCode pointer in order to match the layout of
// IonOOLSetterOpExitFrameLayout.
PushObjectOpResult(masm);
masm.movePtr(StackPointer, argResultReg);
attacher.pushStubCodePointer(masm);
StrictPropertyOp target = shape->setterOp();
MOZ_ASSERT(target);
// JSStrictPropertyOp: bool fn(JSContext *cx, HandleObject obj,
// HandleId id, bool strict, MutableHandleValue vp);
// Push args on stack first so we can take pointers to make handles.
// Push args on stack so we can take pointers to make handles.
if (value.constant())
masm.Push(value.value());
else
masm.Push(value.reg());
masm.movePtr(StackPointer, argVpReg);
masm.move32(Imm32(strict ? 1 : 0), argStrictReg);
// push canonical jsid from shape instead of propertyname.
masm.Push(shape->propid(), argIdReg);
masm.movePtr(StackPointer, argIdReg);
@ -2405,22 +2480,27 @@ GenerateCallSetter(JSContext *cx, IonScript *ion, MacroAssembler &masm,
if (!masm.icBuildOOLFakeExitFrame(returnAddr, aic))
return false;
masm.enterFakeExitFrame(IonOOLPropertyOpExitFrameLayout::Token());
masm.enterFakeExitFrame(IonOOLSetterOpExitFrameLayout::Token());
// Make the call.
masm.setupUnalignedABICall(5, scratchReg);
masm.passABIArg(argJSContextReg);
masm.passABIArg(argObjReg);
masm.passABIArg(argIdReg);
masm.passABIArg(argStrictReg);
masm.passABIArg(argVpReg);
masm.passABIArg(argResultReg);
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, target));
// Test for failure.
// Test for error.
masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel());
// Test for failure.
EmitObjectOpResultCheck<IonOOLSetterOpExitFrameLayout>(masm, failure, strict, scratchReg,
argJSContextReg, argObjReg,
argIdReg, argVpReg, argResultReg);
// masm.leaveExitFrame & pop locals.
masm.adjustStack(IonOOLPropertyOpExitFrameLayout::Size());
masm.adjustStack(IonOOLSetterOpExitFrameLayout::Size());
} else {
MOZ_ASSERT(IsCacheableSetPropCallScripted(obj, holder, shape));

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

@ -1310,9 +1310,16 @@ MarkJitExitFrame(JSTracer *trc, const JitFrameIterator &frame)
return;
}
if (frame.isExitFrameLayout<IonOOLPropertyOpExitFrameLayout>()) {
if (frame.isExitFrameLayout<IonOOLPropertyOpExitFrameLayout>() ||
frame.isExitFrameLayout<IonOOLSetterOpExitFrameLayout>())
{
// A SetterOp frame is a different size, but that's the only relevant
// difference between the two. The fields that need marking are all in
// the common base class.
IonOOLPropertyOpExitFrameLayout *oolgetter =
frame.exitFrame()->as<IonOOLPropertyOpExitFrameLayout>();
frame.isExitFrameLayout<IonOOLPropertyOpExitFrameLayout>()
? frame.exitFrame()->as<IonOOLPropertyOpExitFrameLayout>()
: frame.exitFrame()->as<IonOOLSetterOpExitFrameLayout>();
gc::MarkJitCodeRoot(trc, oolgetter->stubCode(), "ion-ool-property-op-code");
gc::MarkValueRoot(trc, oolgetter->vp(), "ion-ool-property-op-vp");
gc::MarkIdRoot(trc, oolgetter->id(), "ion-ool-property-op-id");

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

@ -494,7 +494,8 @@ enum ExitFrameTokenValues
IonDOMMethodExitFrameLayoutToken = 0x3,
IonOOLNativeExitFrameLayoutToken = 0x4,
IonOOLPropertyOpExitFrameLayoutToken = 0x5,
IonOOLProxyExitFrameLayoutToken = 0x6,
IonOOLSetterOpExitFrameLayoutToken = 0x6,
IonOOLProxyExitFrameLayoutToken = 0x7,
ExitFrameLayoutBareToken = 0xFF
};
@ -627,7 +628,7 @@ class IonOOLNativeExitFrameLayout
class IonOOLPropertyOpExitFrameLayout
{
protected: // only to silence a clang warning about unused private fields
protected:
ExitFooterFrame footer_;
ExitFrameLayout exit_;
@ -652,6 +653,10 @@ class IonOOLPropertyOpExitFrameLayout
return sizeof(IonOOLPropertyOpExitFrameLayout);
}
static size_t offsetOfId() {
return offsetof(IonOOLPropertyOpExitFrameLayout, id_);
}
static size_t offsetOfResult() {
return offsetof(IonOOLPropertyOpExitFrameLayout, vp0_);
}
@ -670,16 +675,36 @@ class IonOOLPropertyOpExitFrameLayout
}
};
class IonOOLSetterOpExitFrameLayout : public IonOOLPropertyOpExitFrameLayout
{
protected: // only to silence a clang warning about unused private fields
JS::ObjectOpResult result_;
public:
static JitCode *Token() { return (JitCode *)IonOOLSetterOpExitFrameLayoutToken; }
static size_t offsetOfObjectOpResult() {
return offsetof(IonOOLSetterOpExitFrameLayout, result_);
}
static size_t Size() {
return sizeof(IonOOLSetterOpExitFrameLayout);
}
};
// Proxy::get(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id,
// MutableHandleValue vp)
// Proxy::set(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id,
// bool strict, MutableHandleValue vp)
// MutableHandleValue vp, ObjectOpResult &result)
class IonOOLProxyExitFrameLayout
{
protected: // only to silence a clang warning about unused private fields
ExitFooterFrame footer_;
ExitFrameLayout exit_;
// result out-parameter (unused for Proxy::get)
JS::ObjectOpResult result_;
// The proxy object.
JSObject *proxy_;
@ -708,6 +733,14 @@ class IonOOLProxyExitFrameLayout
return offsetof(IonOOLProxyExitFrameLayout, vp0_);
}
static size_t offsetOfId() {
return offsetof(IonOOLProxyExitFrameLayout, id_);
}
static size_t offsetOfObjectOpResult() {
return offsetof(IonOOLProxyExitFrameLayout, result_);
}
inline JitCode **stubCode() {
return &stubCode_;
}

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

@ -214,7 +214,7 @@ HashType(TypeSet::Type ty)
}
static HashNumber
HashTypeList(const TypeSet::TypeList &types)
HashTypeList(const TempTypeList &types)
{
HashNumber h = 0;
for (uint32_t i = 0; i < types.length(); i++)
@ -1051,7 +1051,7 @@ IonBuilder::trackTypeInfoUnchecked(TrackedTypeSite kind, MIRType mirType,
{
BytecodeSite *site = current->trackedSite();
// OOMs are handled as if optimization tracking were turned off.
OptimizationTypeInfo typeInfo(kind, mirType);
OptimizationTypeInfo typeInfo(alloc(), kind, mirType);
if (!typeInfo.trackTypeSet(typeSet)) {
site->setOptimizations(nullptr);
return;
@ -1065,7 +1065,7 @@ IonBuilder::trackTypeInfoUnchecked(TrackedTypeSite kind, JSObject *obj)
{
BytecodeSite *site = current->trackedSite();
// OOMs are handled as if optimization tracking were turned off.
OptimizationTypeInfo typeInfo(kind, MIRType_Object);
OptimizationTypeInfo typeInfo(alloc(), kind, MIRType_Object);
if (!typeInfo.trackType(TypeSet::ObjectType(obj)))
return;
if (!site->optimizations()->trackTypeInfo(mozilla::Move(typeInfo)))

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

@ -52,6 +52,7 @@ class OptimizationAttempt
};
typedef Vector<OptimizationAttempt, 4, JitAllocPolicy> TempOptimizationAttemptsVector;
typedef Vector<TypeSet::Type, 1, JitAllocPolicy> TempTypeList;
class UniqueTrackedTypes;
@ -59,7 +60,7 @@ class OptimizationTypeInfo
{
JS::TrackedTypeSite site_;
MIRType mirType_;
TypeSet::TypeList types_;
TempTypeList types_;
public:
OptimizationTypeInfo(OptimizationTypeInfo &&other)
@ -68,9 +69,10 @@ class OptimizationTypeInfo
types_(mozilla::Move(other.types_))
{ }
OptimizationTypeInfo(JS::TrackedTypeSite site, MIRType mirType)
OptimizationTypeInfo(TempAllocator &alloc, JS::TrackedTypeSite site, MIRType mirType)
: site_(site),
mirType_(mirType)
mirType_(mirType),
types_(alloc)
{ }
bool trackTypeSet(TemporaryTypeSet *typeSet);
@ -78,7 +80,7 @@ class OptimizationTypeInfo
JS::TrackedTypeSite site() const { return site_; }
MIRType mirType() const { return mirType_; }
const TypeSet::TypeList &types() const { return types_; }
const TempTypeList &types() const { return types_; }
bool operator ==(const OptimizationTypeInfo &other) const;
bool operator !=(const OptimizationTypeInfo &other) const;

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