зеркало из https://github.com/mozilla/gecko-dev.git
Merge m-i to m-c, a=merge
This commit is contained in:
Коммит
ecf64b97b2
|
@ -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;
|
||||
|
||||
/*
|
||||
|
|
|
@ -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 ®s,
|
||||
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;
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче