tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

regen_atoms.py (6539B)


      1 #!/usr/bin/env python
      2 
      3 # This Source Code Form is subject to the terms of the Mozilla Public
      4 # License, v. 2.0. If a copy of the MPL was not distributed with this
      5 # file, You can obtain one at https://mozilla.org/MPL/2.0/.
      6 
      7 import re
      8 import os
      9 import sys
     10 
     11 from io import BytesIO
     12 
     13 GECKO_DIR = os.path.dirname(__file__.replace("\\", "/"))
     14 sys.path.insert(0, os.path.join(os.path.dirname(GECKO_DIR), "properties"))
     15 
     16 import build
     17 
     18 
     19 # Matches lines like `GK_ATOM(foo, "foo", 0x12345678, true, nsStaticAtom, PseudoElementAtom)`.
     20 PATTERN = re.compile(
     21    r'^GK_ATOM\(([^,]*),[^"]*"([^"]*)",\s*(0x[0-9a-f]+),\s*[^,]*,\s*([^,]*),\s*([^)]*)\)',
     22    re.MULTILINE,
     23 )
     24 FILE = "include/nsGkAtomList.h"
     25 
     26 
     27 def map_atom(ident):
     28    if ident in {
     29        "box",
     30        "loop",
     31        "match",
     32        "mod",
     33        "ref",
     34        "self",
     35        "type",
     36        "use",
     37        "where",
     38        "in",
     39    }:
     40        return ident + "_"
     41    return ident
     42 
     43 
     44 class Atom:
     45    def __init__(self, ident, value, hash, ty, atom_type):
     46        self.ident = "nsGkAtoms_{}".format(ident)
     47        self.original_ident = ident
     48        self.value = value
     49        self.hash = hash
     50        # The Gecko type: "nsStaticAtom", "nsCSSPseudoElementStaticAtom", or
     51        # "nsAnonBoxPseudoStaticAtom".
     52        self.ty = ty
     53        # The type of atom: "Atom", "PseudoElement", "NonInheritingAnonBox",
     54        # or "InheritingAnonBox".
     55        self.atom_type = atom_type
     56 
     57        if (
     58            self.is_pseudo_element()
     59            or self.is_anon_box()
     60            or self.is_tree_pseudo_element()
     61        ):
     62            self.pseudo_ident = (ident.split("_", 1))[1]
     63 
     64        if self.is_anon_box():
     65            assert self.is_inheriting_anon_box() or self.is_non_inheriting_anon_box()
     66 
     67    def type(self):
     68        return self.ty
     69 
     70    def capitalized_pseudo(self):
     71        return self.pseudo_ident[0].upper() + self.pseudo_ident[1:]
     72 
     73    def is_pseudo_element(self):
     74        return self.atom_type == "PseudoElementAtom"
     75 
     76    def is_anon_box(self):
     77        if self.is_tree_pseudo_element():
     78            return False
     79        return self.is_non_inheriting_anon_box() or self.is_inheriting_anon_box()
     80 
     81    def is_non_inheriting_anon_box(self):
     82        assert not self.is_tree_pseudo_element()
     83        return self.atom_type == "NonInheritingAnonBoxAtom"
     84 
     85    def is_inheriting_anon_box(self):
     86        if self.is_tree_pseudo_element():
     87            return False
     88        return self.atom_type == "InheritingAnonBoxAtom"
     89 
     90    def is_tree_pseudo_element(self):
     91        return self.value.startswith(":-moz-tree-")
     92 
     93    def is_named_view_transition_pseudo(self) -> bool:
     94        return (
     95            self.pseudo_ident == "viewTransitionGroup"
     96            or self.pseudo_ident == "viewTransitionImagePair"
     97            or self.pseudo_ident == "viewTransitionOld"
     98            or self.pseudo_ident == "viewTransitionNew"
     99        )
    100 
    101    def is_simple_pseudo_element(self) -> bool:
    102        return not (
    103            self.is_tree_pseudo_element()
    104            or self.pseudo_ident == "highlight"
    105            or self.is_named_view_transition_pseudo()
    106        )
    107 
    108 
    109 def collect_atoms(objdir):
    110    atoms = []
    111    path = os.path.abspath(os.path.join(objdir, FILE))
    112    print("cargo:rerun-if-changed={}".format(path))
    113    with open(path) as f:
    114        content = f.read()
    115        for result in PATTERN.finditer(content):
    116            atoms.append(
    117                Atom(
    118                    result.group(1),
    119                    result.group(2),
    120                    result.group(3),
    121                    result.group(4),
    122                    result.group(5),
    123                )
    124            )
    125    return atoms
    126 
    127 
    128 class FileAvoidWrite(BytesIO):
    129    """File-like object that buffers output and only writes if content changed."""
    130 
    131    def __init__(self, filename):
    132        BytesIO.__init__(self)
    133        self.name = filename
    134 
    135    def write(self, buf):
    136        if isinstance(buf, str):
    137            buf = buf.encode("utf-8")
    138        BytesIO.write(self, buf)
    139 
    140    def close(self):
    141        buf = self.getvalue()
    142        BytesIO.close(self)
    143        try:
    144            with open(self.name, "rb") as f:
    145                old_content = f.read()
    146                if old_content == buf:
    147                    print("{} is not changed, skip".format(self.name))
    148                    return
    149        except IOError:
    150            pass
    151        with open(self.name, "wb") as f:
    152            f.write(buf)
    153 
    154    def __enter__(self):
    155        return self
    156 
    157    def __exit__(self, type, value, traceback):
    158        if not self.closed:
    159            self.close()
    160 
    161 
    162 PRELUDE = """
    163 /* This Source Code Form is subject to the terms of the Mozilla Public
    164 * License, v. 2.0. If a copy of the MPL was not distributed with this
    165 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
    166 
    167 // Autogenerated file created by components/style/gecko/regen_atoms.py.
    168 // DO NOT EDIT DIRECTLY
    169 """[
    170    1:
    171 ]
    172 
    173 RULE_TEMPLATE = """
    174    ("{atom}") => {{{{
    175        #[allow(unsafe_code)] #[allow(unused_unsafe)]
    176        unsafe {{ $crate::string_cache::Atom::from_index_unchecked({index}) }}
    177    }}}};
    178 """[
    179    1:
    180 ]
    181 
    182 MACRO_TEMPLATE = """
    183 /// Returns a static atom by passing the literal string it represents.
    184 #[macro_export]
    185 macro_rules! atom {{
    186 {body}\
    187 }}
    188 """
    189 
    190 
    191 def write_atom_macro(atoms, file_name):
    192    with FileAvoidWrite(file_name) as f:
    193        f.write(PRELUDE)
    194        macro_rules = [
    195            RULE_TEMPLATE.format(atom=atom.value, name=atom.ident, index=i)
    196            for (i, atom) in enumerate(atoms)
    197        ]
    198        f.write(MACRO_TEMPLATE.format(body="".join(macro_rules)))
    199 
    200 
    201 def write_pseudo_elements(atoms, target_filename):
    202    pseudos = []
    203    for atom in atoms:
    204        if (
    205            atom.type() == "nsCSSPseudoElementStaticAtom"
    206            or atom.type() == "nsCSSAnonBoxPseudoStaticAtom"
    207        ):
    208            pseudos.append(atom)
    209 
    210    pseudo_definition_template = os.path.join(
    211        GECKO_DIR, "pseudo_element_definition.mako.rs"
    212    )
    213    print("cargo:rerun-if-changed={}".format(pseudo_definition_template))
    214    contents = build.render(pseudo_definition_template, PSEUDOS=pseudos)
    215 
    216    with FileAvoidWrite(target_filename) as f:
    217        f.write(contents)
    218 
    219 
    220 def generate_atoms(dist, out):
    221    atoms = collect_atoms(dist)
    222    write_atom_macro(atoms, os.path.join(out, "atom_macro.rs"))
    223    write_pseudo_elements(atoms, os.path.join(out, "pseudo_element_definition.rs"))
    224 
    225 
    226 if __name__ == "__main__":
    227    if len(sys.argv) != 3:
    228        print("Usage: {} dist out".format(sys.argv[0]))
    229        exit(2)
    230    generate_atoms(sys.argv[1], sys.argv[2])