Bug 952486 - Add a CheckPermissions extended attribute to WebIDL. r=bz

This commit is contained in:
Reuben Morais 2014-05-25 15:31:07 -03:00
parent f4045bcb86
commit 5a17903f23
7 changed files with 108 additions and 17 deletions

View File

@ -339,6 +339,7 @@ public:
// public methods
nsPIDOMWindow* GetPrivateParent();
// callback for close event
void ReallyCloseWindow();

View File

@ -20,7 +20,9 @@
#include "jsfriendapi.h"
#include "js/OldDebugAPI.h"
#include "nsContentUtils.h"
#include "nsGlobalWindow.h"
#include "nsIDOMGlobalPropertyInitializer.h"
#include "nsIPermissionManager.h"
#include "nsIPrincipal.h"
#include "nsIXPConnect.h"
#include "WrapperFactory.h"
@ -2228,6 +2230,28 @@ EnumerateGlobal(JSContext* aCx, JS::Handle<JSObject*> aObj)
return JS_EnumerateStandardClasses(aCx, aObj);
}
bool
CheckPermissions(JSContext* aCx, JSObject* aObj, const char* const aPermissions[])
{
JS::Rooted<JSObject*> rootedObj(aCx, aObj);
nsPIDOMWindow* window = xpc::WindowGlobalOrNull(rootedObj);
if (!window) {
return false;
}
nsCOMPtr<nsIPermissionManager> permMgr = services::GetPermissionManager();
NS_ENSURE_TRUE(permMgr, false);
do {
uint32_t permission = nsIPermissionManager::DENY_ACTION;
permMgr->TestPermissionFromWindow(window, *aPermissions, &permission);
if (permission == nsIPermissionManager::ALLOW_ACTION) {
return true;
}
} while (*(++aPermissions));
return false;
}
bool
GenericBindingGetter(JSContext* cx, unsigned argc, JS::Value* vp)
{

View File

@ -2828,6 +2828,11 @@ AssertReturnTypeMatchesJitinfo(const JSJitInfo* aJitinfo,
JS::Handle<JS::Value> aValue);
#endif
// Returns true if aObj's global has any of the permissions named in aPermissions
// set to nsIPermissionManager::ALLOW_ACTION. aPermissions must be null-terminated.
bool
CheckPermissions(JSContext* aCx, JSObject* aObj, const char* const aPermissions[]);
} // namespace dom
} // namespace mozilla

View File

@ -1797,10 +1797,11 @@ class MemberCondition:
None, they should be strings that have the pref name (for "pref")
or function name (for "func" and "available").
"""
def __init__(self, pref, func, available=None):
def __init__(self, pref, func, available=None, checkPermissions=None):
assert pref is None or isinstance(pref, str)
assert func is None or isinstance(func, str)
assert available is None or isinstance(available, str)
assert checkPermissions is None or isinstance(checkPermissions, int)
self.pref = pref
def toFuncPtr(val):
@ -1809,10 +1810,15 @@ class MemberCondition:
return "&" + val
self.func = toFuncPtr(func)
self.available = toFuncPtr(available)
if checkPermissions is None:
self.checkPermissions = "nullptr"
else:
self.checkPermissions = "permissions_%i" % checkPermissions
def __eq__(self, other):
return (self.pref == other.pref and self.func == other.func and
self.available == other.available)
self.available == other.available and
self.checkPermissions == other.checkPermissions)
def __ne__(self, other):
return not self.__eq__(other)
@ -1875,12 +1881,13 @@ class PropertyDefiner:
return attr[0]
@staticmethod
def getControllingCondition(interfaceMember):
def getControllingCondition(interfaceMember, descriptor):
return MemberCondition(PropertyDefiner.getStringAttr(interfaceMember,
"Pref"),
PropertyDefiner.getStringAttr(interfaceMember,
"Func"),
getAvailableInTestFunc(interfaceMember))
getAvailableInTestFunc(interfaceMember),
descriptor.checkPermissionsIndicesForMembers.get(interfaceMember.identifier.name))
def generatePrefableArray(self, array, name, specTemplate, specTerminator,
specType, getCondition, getDataTuple, doIdArrays):
@ -1912,12 +1919,12 @@ class PropertyDefiner:
# pref control is added to members while still allowing us to define all
# the members in the smallest number of JSAPI calls.
assert len(array) != 0
lastCondition = getCondition(array[0]) # So we won't put a specTerminator
# at the very front of the list.
lastCondition = getCondition(array[0], self.descriptor) # So we won't put a specTerminator
# at the very front of the list.
specs = []
prefableSpecs = []
prefableTemplate = ' { true, %s, %s, &%s[%d] }'
prefableTemplate = ' { true, %s, %s, %s, &%s[%d] }'
prefCacheTemplate = '&%s[%d].enabled'
def switchToCondition(props, condition):
@ -1931,12 +1938,13 @@ class PropertyDefiner:
prefableSpecs.append(prefableTemplate %
(condition.func,
condition.available,
condition.checkPermissions,
name + "_specs", len(specs)))
switchToCondition(self, lastCondition)
for member in array:
curCondition = getCondition(member)
curCondition = getCondition(member, self.descriptor)
if lastCondition != curCondition:
# Terminate previous list
specs.append(specTerminator)
@ -2049,7 +2057,7 @@ class MethodDefiner(PropertyDefiner):
"methodInfo": not m.isStatic(),
"length": methodLength(m),
"flags": "JSPROP_ENUMERATE",
"condition": PropertyDefiner.getControllingCondition(m),
"condition": PropertyDefiner.getControllingCondition(m, descriptor),
"allowCrossOriginThis": m.getExtendedAttribute("CrossOriginCallable"),
"returnsPromise": m.returnsPromise()
}
@ -2077,7 +2085,7 @@ class MethodDefiner(PropertyDefiner):
"nativeName": stringifier.identifier.name,
"length": 0,
"flags": "JSPROP_ENUMERATE",
"condition": PropertyDefiner.getControllingCondition(stringifier)
"condition": PropertyDefiner.getControllingCondition(stringifier, descriptor)
}
if isChromeOnly(stringifier):
self.chrome.append(toStringDesc)
@ -2090,7 +2098,7 @@ class MethodDefiner(PropertyDefiner):
"nativeName": jsonifier.identifier.name,
"length": 0,
"flags": "JSPROP_ENUMERATE",
"condition": PropertyDefiner.getControllingCondition(jsonifier)
"condition": PropertyDefiner.getControllingCondition(jsonifier, descriptor)
}
if isChromeOnly(jsonifier):
self.chrome.append(toJSONDesc)
@ -2120,7 +2128,7 @@ class MethodDefiner(PropertyDefiner):
if len(array) == 0:
return ""
def condition(m):
def condition(m, d):
return m["condition"]
def specData(m):
@ -2712,6 +2720,9 @@ class CGConstructorEnabled(CGAbstractMethod):
availableIn = getAvailableInTestFunc(iface)
if availableIn:
conditions.append("%s(aCx, aObj)" % availableIn)
checkPermissions = self.descriptor.checkPermissionsIndex
if checkPermissions is not None:
conditions.append("CheckPermissions(aCx, aObj, permissions_%i)" % checkPermissions)
# We should really have some conditions
assert len(conditions)
return CGWrapper(CGList((CGGeneric(cond) for cond in conditions),
@ -10268,6 +10279,15 @@ class CGDescriptor(CGThing):
# wants a custom hook.
cgThings.append(CGClassFinalizeHook(descriptor))
if len(descriptor.permissions):
for (k, v) in sorted(descriptor.permissions.items()):
perms = CGList((CGGeneric('"%s",' % p) for p in k), joiner="\n")
perms.append(CGGeneric("nullptr"))
cgThings.append(CGWrapper(CGIndenter(perms),
pre="static const char* const permissions_%i[] = {\n" % v,
post="\n};\n",
defineOnly=True))
properties = PropertyArrays(descriptor)
cgThings.append(CGGeneric(define=str(properties)))
cgThings.append(CGNativeProperties(descriptor, properties))

View File

@ -420,6 +420,34 @@ class Descriptor(DescriptorProvider):
if '__stringifier' not in self.binaryNames:
self.binaryNames["__stringifier"] = "Stringify"
if not self.interface.isExternal():
self.permissions = dict()
# Adds a permission list to this descriptor and returns the index to use.
def addPermissions(ifaceOrMember):
checkPermissions = ifaceOrMember.getExtendedAttribute("CheckPermissions")
if checkPermissions is None:
return None
# It's a list of whitespace-separated strings
assert(len(checkPermissions) is 1)
assert(checkPermissions[0] is not None)
checkPermissions = checkPermissions[0]
permissionsList = checkPermissions.split()
if len(permissionsList) == 0:
raise TypeError("Need at least one permission name for CheckPermissions")
permissionsList = tuple(sorted(set(permissionsList)))
return self.permissions.setdefault(permissionsList, len(self.permissions))
self.checkPermissionsIndex = addPermissions(self.interface)
self.checkPermissionsIndicesForMembers = dict()
for m in self.interface.members:
permissionsIndex = addPermissions(m)
if permissionsIndex is not None:
self.checkPermissionsIndicesForMembers[m.identifier.name] = permissionsIndex
# Build the prototype chain.
self.prototypeChain = []
parent = interface
@ -496,7 +524,8 @@ class Descriptor(DescriptorProvider):
return (self.interface.getExtendedAttribute("Pref") or
self.interface.getExtendedAttribute("ChromeOnly") or
self.interface.getExtendedAttribute("Func") or
self.interface.getExtendedAttribute("AvailableIn"))
self.interface.getExtendedAttribute("AvailableIn") or
self.interface.getExtendedAttribute("CheckPermissions"))
def needsXrayResolveHooks(self):
"""

View File

@ -38,6 +38,9 @@ typedef bool
JS::Handle<JSObject*> obj,
JS::AutoIdVector& props);
bool
CheckPermissions(JSContext* aCx, JSObject* aObj, const char* const aPermissions[]);
struct ConstantSpec
{
const char* name;
@ -52,7 +55,7 @@ struct Prefable {
if (!enabled) {
return false;
}
if (!enabledFunc && !availableFunc) {
if (!enabledFunc && !availableFunc && !checkPermissions) {
return true;
}
// Just go ahead and root obj, in case enabledFunc GCs
@ -65,6 +68,11 @@ struct Prefable {
!availableFunc(cx, js::GetGlobalForObjectCrossCompartment(rootedObj))) {
return false;
}
if (checkPermissions &&
!CheckPermissions(cx, js::GetGlobalForObjectCrossCompartment(rootedObj),
checkPermissions)) {
return false;
}
return true;
}
@ -79,6 +87,7 @@ struct Prefable {
// is basically a hack to avoid having to codegen PropertyEnabled
// implementations in case when we need to do two separate checks.
PropertyEnabled availableFunc;
const char* const* checkPermissions;
// Array of specs, terminated in whatever way is customary for T.
// Null to indicate a end-of-array for Prefable, when such an
// indicator is needed.

View File

@ -1001,7 +1001,8 @@ class IDLInterface(IDLObjectWithScope):
identifier == "HeaderFile" or
identifier == "NavigatorProperty" or
identifier == "AvailableIn" or
identifier == "Func"):
identifier == "Func" or
identifier == "CheckPermissions"):
# Known extended attributes that take a string value
if not attr.hasValue():
raise WebIDLError("[%s] must have a value" % identifier,
@ -2943,7 +2944,8 @@ class IDLAttribute(IDLInterfaceMember):
identifier == "Func" or
identifier == "Frozen" or
identifier == "AvailableIn" or
identifier == "NewObject"):
identifier == "NewObject" or
identifier == "CheckPermissions"):
# Known attributes that we don't need to do anything with here
pass
else:
@ -3520,7 +3522,8 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
identifier == "AvailableIn" or
identifier == "Pure" or
identifier == "CrossOriginCallable" or
identifier == "WebGLHandlesContextLoss"):
identifier == "WebGLHandlesContextLoss" or
identifier == "CheckPermissions"):
# Known attributes that we don't need to do anything with here
pass
else: