diff --git a/dom/bindings/BindingUtils.cpp b/dom/bindings/BindingUtils.cpp index 987a83ed7ee5..6964aa01dc09 100644 --- a/dom/bindings/BindingUtils.cpp +++ b/dom/bindings/BindingUtils.cpp @@ -2436,6 +2436,28 @@ CheckPermissions(JSContext* aCx, JSObject* aObj, const char* const aPermissions[ return false; } +bool +CheckAllPermissions(JSContext* aCx, JSObject* aObj, const char* const aPermissions[]) +{ + JS::Rooted rootedObj(aCx, aObj); + nsPIDOMWindow* window = xpc::WindowGlobalOrNull(rootedObj); + if (!window) { + return false; + } + + nsCOMPtr 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 false; + } + } while (*(++aPermissions)); + return true; +} + void HandlePrerenderingViolation(nsPIDOMWindow* aWindow) { diff --git a/dom/bindings/BindingUtils.h b/dom/bindings/BindingUtils.h index dd47529bc142..1bceeaa7ecf6 100644 --- a/dom/bindings/BindingUtils.h +++ b/dom/bindings/BindingUtils.h @@ -3096,6 +3096,11 @@ AssertReturnTypeMatchesJitinfo(const JSJitInfo* aJitinfo, bool CheckPermissions(JSContext* aCx, JSObject* aObj, const char* const aPermissions[]); +// Returns true if aObj's global has all of the permissions named in aPermissions +// set to nsIPermissionManager::ALLOW_ACTION. aPermissions must be null-terminated. +bool +CheckAllPermissions(JSContext* aCx, JSObject* aObj, const char* const aPermissions[]); + // This function is called by the bindings layer for methods/getters/setters // that are not safe to be called in prerendering mode. It checks to make sure // that the |this| object is not running in a global that is in prerendering diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py index 1e67dbec6efb..10e3f5161dc6 100644 --- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -1895,11 +1895,12 @@ 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, checkPermissions=None): + def __init__(self, pref, func, available=None, checkPermissions=None, checkAllPermissions=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) + assert checkAllPermissions is None or isinstance(checkAllPermissions, int) self.pref = pref def toFuncPtr(val): @@ -1912,11 +1913,16 @@ class MemberCondition: self.checkPermissions = "nullptr" else: self.checkPermissions = "permissions_%i" % checkPermissions + if checkAllPermissions is None: + self.checkAllPermissions = "nullptr" + else: + self.checkAllPermissions = "allpermissions_%i" % checkAllPermissions def __eq__(self, other): return (self.pref == other.pref and self.func == other.func and self.available == other.available and - self.checkPermissions == other.checkPermissions) + self.checkPermissions == other.checkPermissions and + self.checkAllPermissions == other.checkAllPermissions) def __ne__(self, other): return not self.__eq__(other) @@ -1984,7 +1990,8 @@ class PropertyDefiner: PropertyDefiner.getStringAttr(interfaceMember, "Func"), getAvailableInTestFunc(interfaceMember), - descriptor.checkPermissionsIndicesForMembers.get(interfaceMember.identifier.name)) + descriptor.checkPermissionsIndicesForMembers.get(interfaceMember.identifier.name), + descriptor.checkAllPermissionsIndicesForMembers.get(interfaceMember.identifier.name)) def generatePrefableArray(self, array, name, specFormatter, specTerminator, specType, getCondition, getDataTuple, doIdArrays): @@ -2022,7 +2029,7 @@ class PropertyDefiner: specs = [] prefableSpecs = [] - prefableTemplate = ' { true, %s, %s, %s, &%s[%d] }' + prefableTemplate = ' { true, %s, %s, %s, %s, &%s[%d] }' prefCacheTemplate = '&%s[%d].enabled' def switchToCondition(props, condition): @@ -2037,6 +2044,7 @@ class PropertyDefiner: (condition.func, condition.available, condition.checkPermissions, + condition.checkAllPermissions, name + "_specs", len(specs))) switchToCondition(self, lastCondition) @@ -3170,6 +3178,9 @@ class CGConstructorEnabled(CGAbstractMethod): checkPermissions = self.descriptor.checkPermissionsIndex if checkPermissions is not None: conditions.append("CheckPermissions(aCx, aObj, permissions_%i)" % checkPermissions) + checkAllPermissions = self.descriptor.checkAllPermissionsIndex + if checkAllPermissions is not None: + conditions.append("CheckAllPermissions(aCx, aObj, allpermissions_%i)" % checkAllPermissions) # We should really have some conditions assert len(body) or len(conditions) @@ -11436,14 +11447,16 @@ class CGDescriptor(CGThing): if descriptor.concrete and descriptor.wrapperCache: cgThings.append(CGClassObjectMovedHook(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)) + for name in ["permissions", "allpermissions"]: + permissions = getattr(descriptor, name) + if len(permissions): + for (k, v) in sorted(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 %s_%i[] = {\n" % (name, v), + post="\n};\n", + defineOnly=True)) # Generate the _ClearCachedFooValue methods before the property arrays that use them. if descriptor.interface.isJSImplemented(): diff --git a/dom/bindings/Configuration.py b/dom/bindings/Configuration.py index 2a43369a8199..1a55e6da55d5 100644 --- a/dom/bindings/Configuration.py +++ b/dom/bindings/Configuration.py @@ -547,10 +547,16 @@ class Descriptor(DescriptorProvider): if not self.interface.isExternal(): self.permissions = dict() + self.allpermissions = dict() # Adds a permission list to this descriptor and returns the index to use. - def addPermissions(ifaceOrMember): - checkPermissions = ifaceOrMember.getExtendedAttribute("CheckPermissions") + def addPermissions(ifaceOrMember, attribute): + if attribute == "CheckAllPermissions": + permissions = self.allpermissions + else: + permissions = self.permissions + + checkPermissions = ifaceOrMember.getExtendedAttribute(attribute) if checkPermissions is None: return None @@ -560,17 +566,22 @@ class Descriptor(DescriptorProvider): checkPermissions = checkPermissions[0] permissionsList = checkPermissions.split() if len(permissionsList) == 0: - raise TypeError("Need at least one permission name for CheckPermissions") + raise TypeError("Need at least one permission name for %s" % attribute) permissionsList = tuple(sorted(set(permissionsList))) - return self.permissions.setdefault(permissionsList, len(self.permissions)) + return permissions.setdefault(permissionsList, len(permissions)) - self.checkPermissionsIndex = addPermissions(self.interface) + self.checkPermissionsIndex = addPermissions(self.interface, "CheckPermissions") self.checkPermissionsIndicesForMembers = dict() + self.checkAllPermissionsIndex = addPermissions(self.interface, "CheckAllPermissions") + self.checkAllPermissionsIndicesForMembers = dict() for m in self.interface.members: - permissionsIndex = addPermissions(m) + permissionsIndex = addPermissions(m, "CheckPermissions") if permissionsIndex is not None: self.checkPermissionsIndicesForMembers[m.identifier.name] = permissionsIndex + allpermissionsIndex = addPermissions(m, "CheckAllPermissions") + if allpermissionsIndex is not None: + self.checkAllPermissionsIndicesForMembers[m.identifier.name] = allpermissionsIndex def isTestInterface(iface): return (iface.identifier.name in ["TestInterface", @@ -580,6 +591,7 @@ class Descriptor(DescriptorProvider): self.featureDetectibleThings = set() if not isTestInterface(self.interface): if (self.interface.getExtendedAttribute("CheckPermissions") or + self.interface.getExtendedAttribute("CheckAllPermissions") or self.interface.getExtendedAttribute("AvailableIn") == "PrivilegedApps"): if self.interface.getNavigatorProperty(): self.featureDetectibleThings.add("Navigator.%s" % self.interface.getNavigatorProperty()) @@ -591,6 +603,7 @@ class Descriptor(DescriptorProvider): for m in self.interface.members: if (m.getExtendedAttribute("CheckPermissions") or + m.getExtendedAttribute("CheckAllPermissions") or m.getExtendedAttribute("AvailableIn") == "PrivilegedApps"): self.featureDetectibleThings.add("%s.%s" % (self.interface.identifier.name, m.identifier.name)) diff --git a/dom/bindings/DOMJSClass.h b/dom/bindings/DOMJSClass.h index 5b84d2f377dd..8984b3946d3d 100644 --- a/dom/bindings/DOMJSClass.h +++ b/dom/bindings/DOMJSClass.h @@ -42,6 +42,9 @@ typedef bool bool CheckPermissions(JSContext* aCx, JSObject* aObj, const char* const aPermissions[]); +bool +CheckAllPermissions(JSContext* aCx, JSObject* aObj, const char* const aPermissions[]); + struct ConstantSpec { const char* name; @@ -56,7 +59,7 @@ struct Prefable { if (!enabled) { return false; } - if (!enabledFunc && !availableFunc && !checkPermissions) { + if (!enabledFunc && !availableFunc && !checkPermissions && !checkAllPermissions) { return true; } if (enabledFunc && @@ -72,6 +75,11 @@ struct Prefable { checkPermissions)) { return false; } + if (checkAllPermissions && + !CheckAllPermissions(cx, js::GetGlobalForObjectCrossCompartment(obj), + checkAllPermissions)) { + return false; + } return true; } @@ -87,6 +95,7 @@ struct Prefable { // implementations in case when we need to do two separate checks. PropertyEnabled availableFunc; const char* const* checkPermissions; + const char* const* checkAllPermissions; // 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. diff --git a/dom/bindings/parser/WebIDL.py b/dom/bindings/parser/WebIDL.py index e1925936b34d..44e42c6c2c1a 100644 --- a/dom/bindings/parser/WebIDL.py +++ b/dom/bindings/parser/WebIDL.py @@ -1134,7 +1134,8 @@ class IDLInterface(IDLObjectWithScope, IDLExposureMixins): member.getExtendedAttribute("Pref") or member.getExtendedAttribute("Func") or member.getExtendedAttribute("AvailableIn") or - member.getExtendedAttribute("CheckPermissions")): + member.getExtendedAttribute("CheckPermissions") or + member.getExtendedAttribute("CheckAllPermissions")): raise WebIDLError("[Alias] must not be used on a " "conditionally exposed operation", [member.location]) @@ -1166,12 +1167,13 @@ class IDLInterface(IDLObjectWithScope, IDLExposureMixins): self.parentScope.primaryGlobalName, [self.location]) - if (self.getExtendedAttribute("CheckPermissions") and - self._exposureGlobalNames != set([self.parentScope.primaryGlobalName])): - raise WebIDLError("[CheckPermissions] used on an interface that is " - "not %s-only" % - self.parentScope.primaryGlobalName, - [self.location]) + for attribute in ["CheckPermissions", "CheckAllPermissions"]: + if (self.getExtendedAttribute(attribute) and + self._exposureGlobalNames != set([self.parentScope.primaryGlobalName])): + raise WebIDLError("[%s] used on an interface that is " + "not %s-only" % + (attribute, self.parentScope.primaryGlobalName), + [self.location]) # Conditional exposure makes no sense for interfaces with no # interface object, unless they're navigator properties. @@ -1385,7 +1387,8 @@ class IDLInterface(IDLObjectWithScope, IDLExposureMixins): identifier == "NavigatorProperty" or identifier == "AvailableIn" or identifier == "Func" or - identifier == "CheckPermissions"): + identifier == "CheckPermissions" or + identifier == "CheckAllPermissions"): # Known extended attributes that take a string value if not attr.hasValue(): raise WebIDLError("[%s] must have a value" % identifier, @@ -1513,7 +1516,8 @@ class IDLInterface(IDLObjectWithScope, IDLExposureMixins): self.getExtendedAttribute("ChromeOnly") or self.getExtendedAttribute("Func") or self.getExtendedAttribute("AvailableIn") or - self.getExtendedAttribute("CheckPermissions")) + self.getExtendedAttribute("CheckPermissions") or + self.getExtendedAttribute("CheckAllPermissions")) class IDLDictionary(IDLObjectWithScope): def __init__(self, location, parentScope, name, parent, members): @@ -3361,12 +3365,13 @@ class IDLInterfaceMember(IDLObjectWithIdentifier, IDLExposureMixins): "%s-only" % self._globalScope.primaryGlobalName, [self.location]) - if (self.getExtendedAttribute("CheckPermissions") and - self.exposureSet != set([self._globalScope.primaryGlobalName])): - raise WebIDLError("[CheckPermissions] used on an interface member " - "that is not %s-only" % - self._globalScope.primaryGlobalName, - [self.location]) + for attribute in ["CheckPermissions", "CheckAllPermissions"]: + if (self.getExtendedAttribute(attribute) and + self.exposureSet != set([self._globalScope.primaryGlobalName])): + raise WebIDLError("[%s] used on an interface member that is " + "not %s-only" % + (attribute, self.parentScope.primaryGlobalName), + [self.location]) if self.isAttr() or self.isMethod(): if self.affects == "Everything" and self.dependsOn != "Everything": @@ -3707,7 +3712,8 @@ class IDLConst(IDLInterfaceMember): identifier == "ChromeOnly" or identifier == "Func" or identifier == "AvailableIn" or - identifier == "CheckPermissions"): + identifier == "CheckPermissions" or + identifier == "CheckAllPermissions"): # Known attributes that we don't need to do anything with here pass else: @@ -3975,6 +3981,7 @@ class IDLAttribute(IDLInterfaceMember): identifier == "NewObject" or identifier == "UnsafeInPrerendering" or identifier == "CheckPermissions" or + identifier == "CheckAllPermissions" or identifier == "BinaryName"): # Known attributes that we don't need to do anything with here pass @@ -4650,6 +4657,7 @@ class IDLMethod(IDLInterfaceMember, IDLScope): identifier == "Func" or identifier == "AvailableIn" or identifier == "CheckPermissions" or + identifier == "CheckAllPermissions" or identifier == "BinaryName" or identifier == "MethodIdentityTestable" or identifier == "StaticClassOverride"):