File: __init__.py

package info (click to toggle)
firefox 147.0.3-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 4,683,320 kB
  • sloc: cpp: 7,607,359; javascript: 6,533,295; ansic: 3,775,223; python: 1,415,500; xml: 634,561; asm: 438,949; java: 186,241; sh: 62,752; makefile: 18,079; objc: 13,092; perl: 12,808; yacc: 4,583; cs: 3,846; pascal: 3,448; lex: 1,720; ruby: 1,003; php: 436; lisp: 258; awk: 247; sql: 66; sed: 54; csh: 10; exp: 6
file content (147 lines) | stat: -rw-r--r-- 5,289 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

import re
import sys

import yaml
from mozlint import result
from mozlint.pathutils import expand_exclusions

# This simple linter checks for duplicates from
# modules/libpref/init/StaticPrefList.yaml against modules/libpref/init/all.js

# If for any reason a pref needs to appear in both files, add it to this set.
IGNORE_PREFS = {
    "devtools.console.stdout.chrome",  # Uses the 'sticky' attribute.
    "devtools.console.stdout.content",  # Uses the 'sticky' attribute.
    "fission.autostart",  # Uses the 'locked' attribute.
    "browser.dom.window.dump.enabled",  # Uses the 'sticky' attribute.
    "apz.fling_curve_function_y2",  # This pref is a part of a series.
    "dom.postMessage.sharedArrayBuffer.bypassCOOP_COEP.insecure.enabled",  # NOQA: E501; Uses the 'locked' attribute.
    "extensions.backgroundServiceWorker.enabled",  # NOQA: E501; Uses the 'locked' attribute.
    "general.smoothScroll",  # Uses the 'sticky` attribute.
}

# A regular expression to match preference names and values from js preference
# files. This is not an exact parser, but close enough for our purposes.
# The exact parser grammar is defined at https://searchfox.org/mozilla-central/rev/5c2888b35d56928d252acf84e8816fa89a8a6a61/modules/libpref/parser/src/lib.rs#5-30
PATTERN = re.compile(
    r"""
    \s*pref\(
    \s*"
        (?P<pref>[^"]+)
    "
    \s*,
    \s*
        (?P<val>
            "(              # String value
                [^"\\]+     # Any unescaped string character.
                |
                \\.         # An escaped character.
            )*"
            |
            [^,)]+          # other literals: true, false, integers
        )
        (\s*,.*)?           # optional pref-attr: "sticky" | "locked"
    \s*\)
    \s*;.*
    """,
    re.VERBOSE,
)


def get_names(pref_list_filename):
    pref_names = {}
    # We want to transform patterns like 'foo: @VAR@' into valid yaml. This
    # pattern does not happen in 'name', so it's fine to ignore these.
    # We also want to evaluate all branches of #ifdefs for pref names, so we
    # ignore anything else preprocessor related.
    file = open(pref_list_filename, encoding="utf-8").read().replace("@", "")
    try:
        pref_list = yaml.safe_load(file)
    except (OSError, ValueError) as e:
        print(f"{pref_list_filename}: error:\n  {e}", file=sys.stderr)
        sys.exit(1)

    # Caveats on pref["value"]:
    # - StaticPrefList.yaml may contain expressions such as 10*1000, 0.0f, and
    #   even float(M_PI / 6.0). We don't parse these and may therefore fail to
    #   report these prefs if their value in the js pref file still matches.
    # - Some prefs have values dependent on preprocessor directives. In these
    #   cases, the last value takes precedence over values declared at an
    #   earlier line.
    for pref in pref_list:
        if pref["name"] not in IGNORE_PREFS:
            pref_names[pref["name"]] = pref["value"]

    return pref_names


# Check the names of prefs against each other, and if the pref is a duplicate
# that has not previously been noted, add that name to the list of errors.
def check_against(path, pref_names):
    errors = []
    prefs = read_prefs(path)
    for pref in prefs:
        if pref["name"] in pref_names:
            errors.extend(check_value_for_pref(pref, pref_names[pref["name"]], path))
    return errors


def check_value_for_pref(some_pref, some_value, path):
    errors = []
    if some_pref["value"] == some_value:
        errors.append(
            {
                "path": path,
                "message": some_pref["raw"],
                "lineno": some_pref["line"],
                "hint": "Remove the duplicate pref or add it to IGNORE_PREFS.",
                "level": "error",
            }
        )
    return errors


# The entries in the *.js pref files are regular enough to use simple pattern
# matching to load in prefs.
def read_prefs(path):
    prefs = []
    with open(path, encoding="utf-8") as source:
        for lineno, line in enumerate(source, start=1):
            match = PATTERN.match(line)
            if match:
                prefs.append(
                    {
                        "name": match.group("pref"),
                        "value": evaluate_pref(match.group("val")),
                        "line": lineno,
                        "raw": line,
                    }
                )
    return prefs


def evaluate_pref(value):
    bools = {"true": True, "false": False}
    if value in bools:
        return bools[value]
    elif value.isdigit():
        return int(value)
    return value


def checkdupes(paths, config, **kwargs):
    results = []
    errors = []
    pref_names = get_names(config["support-files"][0])
    files = list(expand_exclusions(paths, config, kwargs["root"]))
    for file in files:
        errors.extend(check_against(file, pref_names))
    for error in errors:
        results.append(result.from_config(config, **error))
    return results