chromium/third_party/blink/renderer/bindings/scripts/web_idl/exposure.py

# Copyright 2019 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

from .runtime_enabled_features import RuntimeEnabledFeatures


class _Feature(str):
    """Represents a runtime-enabled feature."""

    def __new__(cls, value):
        return str.__new__(cls, value)

    def __init__(self, value):
        str.__init__(self)
        self._is_context_dependent = (
            RuntimeEnabledFeatures.is_context_dependent(self))
        self._is_origin_trial = RuntimeEnabledFeatures.is_origin_trial(self)

    @property
    def is_context_dependent(self):
        return self._is_context_dependent

    @property
    def is_origin_trial(self):
        return self._is_origin_trial


class _GlobalNameAndFeature(object):
    def __init__(self, global_name, feature=None):
        assert isinstance(global_name, str)
        assert feature is None or isinstance(feature, str)

        self._global_name = global_name
        if feature is None:
            self._feature = None
        else:
            self._feature = _Feature(feature)

    @property
    def global_name(self):
        return self._global_name

    @property
    def feature(self):
        return self._feature


class Exposure(object):
    """Represents a set of conditions under which the construct is exposed."""

    def __init__(self, other=None):
        assert other is None or isinstance(other, Exposure)

        if other:
            self._global_names_and_features = tuple(
                other.global_names_and_features)
            self._runtime_enabled_features = tuple(
                other.runtime_enabled_features)
            self._context_independent_runtime_enabled_features = tuple(
                other.context_independent_runtime_enabled_features)
            self._context_dependent_runtime_enabled_features = tuple(
                other.context_dependent_runtime_enabled_features)
            self._origin_trial_features = tuple(other._origin_trial_features)
            self._context_enabled_features = tuple(
                other.context_enabled_features)
            self._only_in_coi_contexts = other.only_in_coi_contexts
            self._only_in_coi_contexts_or_runtime_enabled_features = tuple(
                other.only_in_coi_contexts_or_runtime_enabled_features)
            self._only_in_injection_mitigated_contexts = other.only_in_injection_mitigated_contexts
            self._only_in_isolated_contexts = other.only_in_isolated_contexts
            self._only_in_secure_contexts = other.only_in_secure_contexts
        else:
            self._global_names_and_features = tuple()
            self._runtime_enabled_features = tuple()
            self._context_independent_runtime_enabled_features = tuple()
            self._context_dependent_runtime_enabled_features = tuple()
            self._origin_trial_features = tuple()
            self._context_enabled_features = tuple()
            self._only_in_coi_contexts = False
            self._only_in_coi_contexts_or_runtime_enabled_features = tuple()
            self._only_in_injection_mitigated_contexts = False
            self._only_in_isolated_contexts = False
            self._only_in_secure_contexts = None

    @property
    def global_names_and_features(self):
        """
        Returns a list of pairs of global name and runtime enabled feature,
        which is None if not applicable.
        """
        return self._global_names_and_features

    @property
    def runtime_enabled_features(self):
        """
        Returns a list of runtime enabled features.  This construct is exposed
        only when all these features are enabled.
        """
        return self._runtime_enabled_features

    @property
    def context_independent_runtime_enabled_features(self):
        """Returns runtime enabled features that are context-independent."""
        return self._context_independent_runtime_enabled_features

    @property
    def context_dependent_runtime_enabled_features(self):
        """Returns runtime enabled features that are context-dependent."""
        return self._context_dependent_runtime_enabled_features

    @property
    def origin_trial_features(self):
        """
        Returns a list of origin trial features.
        """
        return self._origin_trial_features

    @property
    def context_enabled_features(self):
        """
        Returns a list of context enabled features.  This construct is exposed
        only when one of these features is enabled in the context.
        """
        return self._context_enabled_features

    @property
    def only_in_coi_contexts(self):
        """
        Returns whether this construct is available only in cross-origin
        isolated contexts. The returned value is a boolean: True if the
        construct is restricted to COI contexts, or False if not.

        https://webidl.spec.whatwg.org/#CrossOriginIsolated
        """
        return self._only_in_coi_contexts

    @property
    def only_in_coi_contexts_or_runtime_enabled_features(self):
        """
        Returns a list of runtime enabled features that affects cross-origin
        isolation.

        If the list is not empty, this construct is available only in
        cross-origin isolated contexts or when any of the specified runtime
        enabled features (supposed to be origin trials) gets enabled.
        """
        return self._only_in_coi_contexts_or_runtime_enabled_features

    @property
    def only_in_injection_mitigated_contexts(self):
        """
        Returns whether this construct is available only in contexts with
        sufficient injection attack mitigations. The returned value is a
        boolean: True if the construct is restricted, False otherwise.
        """
        return self._only_in_injection_mitigated_contexts

    @property
    def only_in_isolated_contexts(self):
        """
        Returns whether this construct is available only in isolated app
        contexts. The returned value is a boolean: True if the construct
        is restricted to isolated application contexts, False if not.

        TODO(crbug.com/1206150): This needs a specification (and definition).
        """
        return self._only_in_isolated_contexts

    @property
    def only_in_secure_contexts(self):
        """
        Returns whether this construct is available only in secure contexts or
        not.  The returned value is either of a boolean (True: unconditionally
        restricted in secure contexts, or False: never restricted) or a list of
        flag names (restricted only when all flags are enabled).

        https://webidl.spec.whatwg.org/#dfn-available-only-in-secure-contexts
        """
        if self._only_in_secure_contexts is None:
            return False
        return self._only_in_secure_contexts

    def is_context_dependent(self, global_names=None):
        """
        Returns True if the exposure of this construct depends on a context.

        Args:
            global_names: When specified, it's taken into account that the
                global object implements |global_names|.
        """
        assert (global_names is None
                or (isinstance(global_names, (list, tuple))
                    and all(isinstance(name, str) for name in global_names)))

        if (self.context_dependent_runtime_enabled_features
                or self.context_enabled_features or self.only_in_coi_contexts
                or self.only_in_injection_mitigated_contexts
                or self.only_in_isolated_contexts
                or self.only_in_secure_contexts):
            return True

        if not global_names:
            return bool(self.global_names_and_features)

        is_context_dependent = False
        for entry in self.global_names_and_features:
            if entry.global_name not in global_names:
                continue
            if entry.feature and entry.feature.is_context_dependent:
                is_context_dependent = True
        return is_context_dependent


class ExposureMutable(Exposure):
    def __init__(self):
        Exposure.__init__(self)

        self._global_names_and_features = []
        self._runtime_enabled_features = []
        self._context_independent_runtime_enabled_features = []
        self._context_dependent_runtime_enabled_features = []
        self._origin_trial_features = []
        self._context_enabled_features = []
        self._only_in_coi_contexts = False
        self._only_in_coi_contexts_or_runtime_enabled_features = []
        self._only_in_injection_mitigated_contexts = False
        self._only_in_isolated_contexts = False
        self._only_in_secure_contexts = None

    def __getstate__(self):
        assert False, "ExposureMutable must not be pickled."

    def __setstate__(self, state):
        assert False, "ExposureMutable must not be pickled."

    def add_global_name_and_feature(self, global_name, feature_name=None):
        self._global_names_and_features.append(
            _GlobalNameAndFeature(global_name, feature_name))

    def add_runtime_enabled_feature(self, name):
        assert isinstance(name, str)
        feature = _Feature(name)
        if feature.is_context_dependent:
            self._context_dependent_runtime_enabled_features.append(feature)
        else:
            self._context_independent_runtime_enabled_features.append(feature)
        if feature.is_origin_trial:
            self._origin_trial_features.append(feature)
        self._runtime_enabled_features.append(feature)

    def add_context_enabled_feature(self, name):
        assert isinstance(name, str)
        self._context_enabled_features.append(name)

    def set_only_in_coi_contexts(self, value):
        assert isinstance(value, bool)
        self._only_in_coi_contexts = value

    def add_only_in_coi_contexts_or_runtime_enabled_feature(self, name):
        assert isinstance(name, str)
        self._only_in_coi_contexts_or_runtime_enabled_features.append(
            _Feature(name))

    def set_only_in_injection_mitigated_contexts(self, value):
        assert isinstance(value, bool)
        self._only_in_injection_mitigated_contexts = value

    def set_only_in_isolated_contexts(self, value):
        assert isinstance(value, bool)
        self._only_in_isolated_contexts = value

    def set_only_in_secure_contexts(self, value):
        assert (isinstance(value, (bool, str))
                or (isinstance(value, (list, tuple))
                    and all(isinstance(name, str) for name in value)))
        assert self._only_in_secure_contexts is None
        if isinstance(value, bool):
            self._only_in_secure_contexts = value
        elif isinstance(value, str):
            self._only_in_secure_contexts = (_Feature(value), )
        else:
            self._only_in_secure_contexts = tuple(map(_Feature, value))