1# Copyright 2019 The Chromium Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5from .runtime_enabled_features import RuntimeEnabledFeatures
6
7
8class _Feature(str):
9    """Represents a runtime-enabled feature."""
10
11    def __init__(self, value):
12        str.__init__(self, value)
13        self._is_context_dependent = (
14            RuntimeEnabledFeatures.is_context_dependent(self))
15
16    @property
17    def is_context_dependent(self):
18        return self._is_context_dependent
19
20
21class _GlobalNameAndFeature(object):
22    def __init__(self, global_name, feature=None):
23        assert isinstance(global_name, str)
24        assert feature is None or isinstance(feature, str)
25
26        self._global_name = global_name
27        if feature is None:
28            self._feature = None
29        else:
30            self._feature = _Feature(feature)
31
32    @property
33    def global_name(self):
34        return self._global_name
35
36    @property
37    def feature(self):
38        return self._feature
39
40
41class Exposure(object):
42    """Represents a set of conditions under which the construct is exposed."""
43
44    def __init__(self, other=None):
45        assert other is None or isinstance(other, Exposure)
46
47        if other:
48            self._global_names_and_features = tuple(
49                other.global_names_and_features)
50            self._runtime_enabled_features = tuple(
51                other.runtime_enabled_features)
52            self._context_independent_runtime_enabled_features = tuple(
53                other.context_independent_runtime_enabled_features)
54            self._context_dependent_runtime_enabled_features = tuple(
55                other.context_dependent_runtime_enabled_features)
56            self._context_enabled_features = tuple(
57                other.context_enabled_features)
58            self._only_in_secure_contexts = other.only_in_secure_contexts
59        else:
60            self._global_names_and_features = tuple()
61            self._runtime_enabled_features = tuple()
62            self._context_independent_runtime_enabled_features = tuple()
63            self._context_dependent_runtime_enabled_features = tuple()
64            self._context_enabled_features = tuple()
65            self._only_in_secure_contexts = None
66
67    @property
68    def global_names_and_features(self):
69        """
70        Returns a list of pairs of global name and runtime enabled feature,
71        which is None if not applicable.
72        """
73        return self._global_names_and_features
74
75    @property
76    def runtime_enabled_features(self):
77        """
78        Returns a list of runtime enabled features.  This construct is exposed
79        only when all these features are enabled.
80        """
81        return self._runtime_enabled_features
82
83    @property
84    def context_independent_runtime_enabled_features(self):
85        """Returns runtime enabled features that are context-independent."""
86        return self._context_independent_runtime_enabled_features
87
88    @property
89    def context_dependent_runtime_enabled_features(self):
90        """Returns runtime enabled features that are context-dependent."""
91        return self._context_dependent_runtime_enabled_features
92
93    @property
94    def context_enabled_features(self):
95        """
96        Returns a list of context enabled features.  This construct is exposed
97        only when one of these features is enabled in the context.
98        """
99        return self._context_enabled_features
100
101    @property
102    def only_in_secure_contexts(self):
103        """
104        Returns whether this construct is available only in secure contexts or
105        not.  The returned value is either of a boolean (True: unconditionally
106        restricted in secure contexts, or False: never restricted) or a list of
107        flag names (restricted only when all flags are enabled).
108
109        https://heycam.github.io/webidl/#dfn-available-only-in-secure-contexts
110        """
111        if self._only_in_secure_contexts is None:
112            return False
113        return self._only_in_secure_contexts
114
115    def is_context_dependent(self, global_names=None):
116        """
117        Returns True if the exposure of this construct depends on a context.
118
119        Args:
120            global_names: When specified, it's taken into account that the
121                global object implements |global_names|.
122        """
123        assert (global_names is None
124                or (isinstance(global_names, (list, tuple))
125                    and all(isinstance(name, str) for name in global_names)))
126
127        if (self.context_dependent_runtime_enabled_features
128                or self.context_enabled_features
129                or self.only_in_secure_contexts):
130            return True
131
132        if not global_names:
133            return bool(self.global_names_and_features)
134
135        is_context_dependent = False
136        for entry in self.global_names_and_features:
137            if entry.global_name not in global_names:
138                continue
139            if entry.feature and entry.feature.is_context_dependent:
140                is_context_dependent = True
141        return is_context_dependent
142
143
144class ExposureMutable(Exposure):
145    def __init__(self):
146        Exposure.__init__(self)
147
148        self._global_names_and_features = []
149        self._runtime_enabled_features = []
150        self._context_independent_runtime_enabled_features = []
151        self._context_dependent_runtime_enabled_features = []
152        self._context_enabled_features = []
153        self._only_in_secure_contexts = None
154
155    def __getstate__(self):
156        assert False, "ExposureMutable must not be pickled."
157
158    def __setstate__(self, state):
159        assert False, "ExposureMutable must not be pickled."
160
161    def add_global_name_and_feature(self, global_name, feature_name=None):
162        self._global_names_and_features.append(
163            _GlobalNameAndFeature(global_name, feature_name))
164
165    def add_runtime_enabled_feature(self, name):
166        assert isinstance(name, str)
167        feature = _Feature(name)
168        if feature.is_context_dependent:
169            self._context_dependent_runtime_enabled_features.append(feature)
170        else:
171            self._context_independent_runtime_enabled_features.append(feature)
172        self._runtime_enabled_features.append(feature)
173
174    def add_context_enabled_feature(self, name):
175        assert isinstance(name, str)
176        self._context_enabled_features.append(name)
177
178    def set_only_in_secure_contexts(self, value):
179        assert (isinstance(value, (bool, str))
180                or (isinstance(value, (list, tuple))
181                    and all(isinstance(name, str) for name in value)))
182        assert self._only_in_secure_contexts is None
183        if isinstance(value, bool):
184            self._only_in_secure_contexts = value
185        elif isinstance(value, str):
186            self._only_in_secure_contexts = (_Feature(value), )
187        else:
188            self._only_in_secure_contexts = tuple(map(_Feature, value))
189