Bug 1756500 - Add [Trial="Name"] condition to WebIDL. r=peterv

Differential Revision: https://phabricator.services.mozilla.com/D139315
This commit is contained in:
Emilio Cobos Álvarez 2022-03-08 15:24:15 +00:00
parent 7852c208be
commit 93ce20c599
10 changed files with 133 additions and 34 deletions

View File

@ -3899,6 +3899,8 @@ class Document : public nsINode,
public:
bool IsThirdPartyForFlashClassifier();
const OriginTrials& Trials() const { return mTrials; }
private:
void DoCacheAllKnownLangPrefs();
void RecomputeLanguageFromCharset();

View File

@ -2402,13 +2402,20 @@ class MemberCondition:
secureContext: A bool indicating whether a secure context is required.
nonExposedGlobals: A set of names of globals. Can be empty, in which case
it's treated the same way as None.
trial: The name of the origin trial.
"""
def __init__(
self, pref=None, func=None, secureContext=False, nonExposedGlobals=None
self,
pref=None,
func=None,
secureContext=False,
nonExposedGlobals=None,
trial=None,
):
assert pref is None or isinstance(pref, str)
assert func is None or isinstance(func, str)
assert trial is None or isinstance(trial, str)
assert isinstance(secureContext, bool)
assert nonExposedGlobals is None or isinstance(nonExposedGlobals, set)
self.pref = pref
@ -2435,12 +2442,18 @@ class MemberCondition:
else:
self.nonExposedGlobals = "0"
if trial:
self.trial = "OriginTrial::" + trial
else:
self.trial = "OriginTrial(0)"
def __eq__(self, other):
return (
self.pref == other.pref
and self.func == other.func
and self.secureContext == other.secureContext
and self.nonExposedGlobals == other.nonExposedGlobals
and self.trial == other.trial
)
def __ne__(self, other):
@ -2452,6 +2465,7 @@ class MemberCondition:
or self.secureContext
or self.func != "nullptr"
or self.nonExposedGlobals != "0"
or self.trial != "OriginTrial(0)"
)
@ -2513,11 +2527,19 @@ class PropertyDefiner:
interface = descriptor.interface
nonExposureSet = interface.exposureSet - interfaceMember.exposureSet
trial = PropertyDefiner.getStringAttr(interfaceMember, "Trial")
if trial and interface.identifier.name in ["Window", "Document"]:
raise TypeError(
"[Trial] not yet supported for %s.%s, see bug 1757935"
% (interface.identifier.name, interfaceMember.identifier.name)
)
return MemberCondition(
PropertyDefiner.getStringAttr(interfaceMember, "Pref"),
PropertyDefiner.getStringAttr(interfaceMember, "Func"),
interfaceMember.getExtendedAttribute("SecureContext") is not None,
nonExposureSet,
trial,
)
@staticmethod
@ -2642,7 +2664,7 @@ class PropertyDefiner:
disablersTemplate = dedent(
"""
static const PrefableDisablers %s_disablers%d = {
%s, %s, %s, %s
%s, %s, %s, %s, %s
};
"""
)
@ -2663,6 +2685,7 @@ class PropertyDefiner:
len(specs),
condition.prefFuncIndex,
toStringBool(condition.secureContext),
condition.trial,
condition.nonExposedGlobals,
condition.func,
)
@ -4094,6 +4117,13 @@ def getRawConditionList(idlobj, cxName, objName, ignoreSecureContext=False):
if func:
assert isinstance(func, list) and len(func) == 1
conditions.append("%s(%s, %s)" % (func[0], cxName, objName))
trial = idlobj.getExtendedAttribute("Trial")
if trial:
assert isinstance(trial, list) and len(trial) == 1
conditions.append(
"OriginTrials::IsEnabled(%s, %s, OriginTrial::%s)"
% (cxName, objName, trial[0])
)
if not ignoreSecureContext and idlobj.getExtendedAttribute("SecureContext"):
conditions.append(
"mozilla::dom::IsSecureContextOrObjectIsFromSecureContext(%s, %s)"
@ -18328,40 +18358,29 @@ class CGBindingRoot(CGThing):
bindingHeaders[CGHeaders.getDeclarationFilename(enums[0])] = True
bindingHeaders["jsapi.h"] = True
# For things that have [UseCounter] or [InstrumentedProps]
descriptorsHaveUseCounters = any(
m.getExtendedAttribute("UseCounter")
for d in descriptors
for m in d.interface.members
)
descriptorsHaveInstrumentedProps = any(
d.instrumentedProps for d in descriptors if d.concrete
)
descriptorsHaveNeedsMissingPropUseCounters = any(
d.needsMissingPropUseCounters for d in descriptors if d.concrete
)
# For things that have [UseCounter] or [InstrumentedProps] or [Trial]
for d in descriptors:
if d.concrete:
if d.instrumentedProps:
bindingHeaders["mozilla/UseCounter.h"] = True
if d.needsMissingPropUseCounters:
bindingHeaders[prefHeader(MISSING_PROP_PREF)] = True
if d.interface.isSerializable():
bindingHeaders["mozilla/dom/StructuredCloneTags.h"] = True
if d.wantsXrays:
bindingHeaders["mozilla/Atomics.h"] = True
bindingHeaders["mozilla/dom/XrayExpandoClass.h"] = True
if d.wantsXrayExpandoClass:
bindingHeaders["XrayWrapper.h"] = True
for m in d.interface.members:
if m.getExtendedAttribute("UseCounter"):
bindingHeaders["mozilla/UseCounter.h"] = True
if m.getExtendedAttribute("Trial"):
bindingHeaders["mozilla/OriginTrials.h"] = True
bindingHeaders["mozilla/UseCounter.h"] = (
descriptorsHaveUseCounters or descriptorsHaveInstrumentedProps
)
# Make sure to not overwrite existing pref header bits!
bindingHeaders[prefHeader(MISSING_PROP_PREF)] = (
bindingHeaders.get(prefHeader(MISSING_PROP_PREF))
or descriptorsHaveNeedsMissingPropUseCounters
)
bindingHeaders["mozilla/dom/SimpleGlobalObject.h"] = any(
CGDictionary.dictionarySafeToJSONify(d) for d in dictionaries
)
bindingHeaders["XrayWrapper.h"] = any(
d.wantsXrays and d.wantsXrayExpandoClass for d in descriptors
)
bindingHeaders["mozilla/dom/XrayExpandoClass.h"] = any(
d.wantsXrays for d in descriptors
)
bindingHeaders["mozilla/dom/StructuredCloneTags.h"] = any(
d.interface.isSerializable() for d in descriptors
)
bindingHeaders["mozilla/Atomics.h"] = any(d.wantsXrays for d in descriptors)
for ancestor in (findAncestorWithInstrumentedProps(d) for d in descriptors):
if not ancestor:

View File

@ -13,6 +13,7 @@
#include "js/Wrapper.h"
#include "mozilla/Assertions.h"
#include "mozilla/Attributes.h"
#include "mozilla/OriginTrials.h"
#include "mozilla/Likely.h"
#include "mozilla/dom/PrototypeList.h" // auto-generated
@ -129,6 +130,15 @@ struct PrefableDisablers {
if (secureContext && !IsSecureContextOrObjectIsFromSecureContext(cx, obj)) {
return false;
}
if (trial != OriginTrial(0) &&
!OriginTrials::IsEnabled(cx, JS::GetNonCCWObjectGlobal(obj), trial)) {
// TODO(emilio): Perhaps reconsider the interaction between [Trial=""] and
// [Pref=""].
//
// In particular, it might be desirable to only check the trial if there
// is no pref or the pref is disabled.
return false;
}
if (enabledFunc && !enabledFunc(cx, JS::GetNonCCWObjectGlobal(obj))) {
return false;
}
@ -141,6 +151,9 @@ struct PrefableDisablers {
// A boolean indicating whether a Secure Context is required.
const bool secureContext;
// An origin trial controlling the feature.
const OriginTrial trial;
// Bitmask of global names that we should not be exposed in.
const uint16_t nonExposedGlobals;

View File

@ -1572,6 +1572,7 @@ class IDLInterfaceOrNamespace(IDLInterfaceOrInterfaceMixinOrNamespace):
or member.getExtendedAttribute("ChromeOnly")
or member.getExtendedAttribute("Pref")
or member.getExtendedAttribute("Func")
or member.getExtendedAttribute("Trial")
or member.getExtendedAttribute("SecureContext")
):
raise WebIDLError(
@ -1783,7 +1784,13 @@ class IDLInterfaceOrNamespace(IDLInterfaceOrInterfaceMixinOrNamespace):
def hasMembersInSlots(self):
return self._ownMembersInSlots != 0
conditionExtendedAttributes = ["Pref", "ChromeOnly", "Func", "SecureContext"]
conditionExtendedAttributes = [
"Pref",
"ChromeOnly",
"Func",
"Trial",
"SecureContext",
]
def isExposedConditionally(self, exclusions=[]):
return any(
@ -1960,6 +1967,7 @@ class IDLInterface(IDLInterfaceOrNamespace):
or identifier == "JSImplementation"
or identifier == "HeaderFile"
or identifier == "Func"
or identifier == "Trial"
or identifier == "Deprecated"
):
# Known extended attributes that take a string value
@ -2046,6 +2054,7 @@ class IDLNamespace(IDLInterfaceOrNamespace):
identifier == "Pref"
or identifier == "HeaderFile"
or identifier == "Func"
or identifier == "Trial"
):
# Known extended attributes that take a string value
if not attr.hasValue():
@ -4898,6 +4907,7 @@ class IDLConst(IDLInterfaceMember):
identifier == "Pref"
or identifier == "ChromeOnly"
or identifier == "Func"
or identifier == "Trial"
or identifier == "SecureContext"
or identifier == "NonEnumerable"
):
@ -5403,6 +5413,7 @@ class IDLAttribute(IDLInterfaceMember):
or identifier == "GetterCanOOM"
or identifier == "ChromeOnly"
or identifier == "Func"
or identifier == "Trial"
or identifier == "SecureContext"
or identifier == "Frozen"
or identifier == "NewObject"
@ -5541,6 +5552,7 @@ class IDLArgument(IDLObjectWithIdentifier):
elif self.dictionaryMember and (
identifier == "ChromeOnly"
or identifier == "Func"
or identifier == "Trial"
or identifier == "Pref"
):
if not self.optional:
@ -6390,6 +6402,7 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
or identifier == "Pref"
or identifier == "Deprecated"
or identifier == "Func"
or identifier == "Trial"
or identifier == "SecureContext"
or identifier == "BinaryName"
or identifier == "NeedsSubjectPrincipal"
@ -6445,6 +6458,7 @@ class IDLConstructor(IDLMethod):
or identifier == "SecureContext"
or identifier == "Throws"
or identifier == "Func"
or identifier == "Trial"
or identifier == "Pref"
or identifier == "UseCounter"
):

View File

@ -990,6 +990,9 @@ class TestInterface : public nsISupports, public nsWrapperCache {
void ConditionalOnSecureContext7();
void ConditionalOnSecureContext8();
bool ConditionalOnSecureContext9();
void ConditionalOnSecureContext10();
// Miscellania
int32_t AttrWithLenientThis();
void SetAttrWithLenientThis(int32_t);

View File

@ -962,6 +962,10 @@ interface TestInterface {
void prefable19();
[Pref="dom.webidl.test1", Func="TestFuncControlledMember", ChromeOnly]
void prefable20();
[Trial="TestTrial"]
void prefable21();
[Pref="dom.webidl.test1", Trial="TestTrial"]
void prefable22();
// Conditionally exposed methods/attributes involving [SecureContext]
[SecureContext]
@ -980,6 +984,10 @@ interface TestInterface {
void conditionalOnSecureContext7();
[SecureContext, Pref="dom.webidl.test1", Func="TestFuncControlledMember"]
void conditionalOnSecureContext8();
[SecureContext, Trial="TestTrial"]
readonly attribute boolean conditionalOnSecureContext9;
[SecureContext, Pref="dom.webidl.test1", Func="TestFuncControlledMember", Trial="TestTrial"]
void conditionalOnSecureContext10();
// Miscellania
[LegacyLenientThis] attribute long attrWithLenientThis;

View File

@ -758,6 +758,12 @@ interface TestExampleInterface {
void prefable18();
[Func="TestFuncControlledMember"]
void prefable19();
[Trial="TestTrial"]
void prefable20();
[Trial="TestTrial"]
readonly attribute boolean prefable21;
[Trial="TestTrial", Func="TestFuncControlledMember"]
readonly attribute boolean prefable22;
// Conditionally exposed methods/attributes involving [SecureContext]
[SecureContext]
@ -776,6 +782,10 @@ interface TestExampleInterface {
void conditionalOnSecureContext7();
[SecureContext, Pref="dom.webidl.test1", Func="TestFuncControlledMember"]
void conditionalOnSecureContext8();
[SecureContext, Trial="TestTrial"]
readonly attribute boolean conditionalOnSecureContext9;
[SecureContext, Trial="TestTrial"]
void conditionalOnSecureContext10();
// Miscellania
[LegacyLenientThis] attribute long attrWithLenientThis;

View File

@ -10,6 +10,12 @@
#include "nsIPrincipal.h"
#include "nsIURI.h"
#include "nsNetUtil.h"
#include "nsContentUtils.h"
#include "xpcpublic.h"
#include "jsapi.h"
#include "js/Wrapper.h"
#include "nsGlobalWindowInner.h"
#include "mozilla/dom/Document.h"
namespace mozilla {
@ -62,4 +68,22 @@ void OriginTrials::UpdateFromToken(const nsAString& aBase64EncodedToken,
mEnabledTrials += trial;
}
bool OriginTrials::IsEnabled(JSContext* aCx, JSObject* aObject,
OriginTrial aTrial) {
if (nsContentUtils::ThreadsafeIsSystemCaller(aCx)) {
return true;
}
if (NS_IsMainThread()) {
if (auto* win = xpc::WindowGlobalOrNull(js::UncheckedUnwrap(aObject))) {
if (dom::Document* doc = win->GetExtantDoc()) {
return doc->Trials().IsEnabled(aTrial);
}
}
return false;
}
// TODO(emilio): Worker support.
MOZ_ASSERT_UNREACHABLE("Not implemented yet");
return false;
}
} // namespace mozilla

View File

@ -12,6 +12,8 @@
#include "nsStringFwd.h"
class nsIPrincipal;
struct JSContext;
class JSObject;
namespace mozilla {
@ -34,6 +36,9 @@ class OriginTrials final {
return mEnabledTrials.contains(aTrial);
}
// Checks whether a given origin trial is enabled for a given call.
static bool IsEnabled(JSContext*, JSObject*, OriginTrial);
private:
EnumSet<OriginTrial> mEnabledTrials;
};

View File

@ -7,7 +7,8 @@ use std::ffi::c_void;
#[repr(u8)]
pub enum OriginTrial {
TestTrial,
// NOTE(emilio): 0 is reserved for WebIDL usage.
TestTrial = 1,
}
impl OriginTrial {