"""
This script parses mozilla-central's WebIDL bindings and writes a JSON-formatted
subset of the function bindings to several files:
- "devtools/server/actors/webconsole/webidl-pure-allowlist.js" (for eager evaluation processing)
- "devtools/server/actors/webconsole/webidl-unsafe-getters-names.js" (for preventing automatically call getters that could emit warnings)

Run this script via

> ./mach python devtools/shared/webconsole/GenerateDataFromWebIdls.py

with a mozconfig that references a built non-artifact build.
"""

from os import path, remove, system
import json
import WebIDL
import buildconfig

# This is an explicit list of interfaces to load [Pure] and [Constant]
# annotation for. There are a bunch of things that are pure in other interfaces
# that we don't care about in the context of the devtools.
PURE_INTERFACE_ALLOWLIST = set(
    [
        "Document",
        "Node",
        "DOMTokenList",
        "Element",
        "Performance",
        "URLSearchParams",
        "FormData",
        "Headers",
    ]
)

# This is an explicit list of interfaces to exclude.
DEPRECATED_INTERFACE__EXCLUDE_LIST = set(
    [
        "External",
        "TestExampleInterface",
        "TestInterface",
        "TestJSImplInterface",
        "TestingDeprecatedInterface",
    ]
)

FILE_TEMPLATE = """\
/* 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/. */

// This file is automatically generated by the GenerateDataFromWebIdls.py
// script. Do not modify it manually.
"use strict";

module.exports = %(data)s;
"""

pure_output_file = path.join(
    buildconfig.topsrcdir, "devtools/server/actors/webconsole/webidl-pure-allowlist.js"
)
unsafe_getters_names_file = path.join(
    buildconfig.topsrcdir,
    "devtools/server/actors/webconsole/webidl-unsafe-getters-names.js",
)

input_file = path.join(buildconfig.topobjdir, "dom/bindings/file-lists.json")

if not path.isfile(input_file):
    raise Exception(
        "Script must be run with a mozconfig referencing a non-artifact OBJDIR"
    )

file_list = json.load(open(input_file))

parser = WebIDL.Parser()
for filepath in file_list["webidls"]:
    with open(filepath, "r", encoding="utf8") as f:
        parser.parse(f.read(), filepath)
results = parser.finish()

# TODO: Bug 1616013 - Move more of these to be part of the pure list.
pure_output = {
    "Document": {
        "prototype": [
            "getSelection",
            "hasStorageAccess",
        ],
    },
    "Range": {
        "prototype": [
            "isPointInRange",
            "comparePoint",
            "intersectsNode",
            # These two functions aren't pure because they do trigger
            # layout when they are called, but in the context of eager
            # evaluation, that should be a totally fine thing to do.
            "getClientRects",
            "getBoundingClientRect",
        ],
    },
    "Selection": {
        "prototype": ["getRangeAt", "containsNode"],
    },
}
unsafe_getters_names = []
for result in results:
    if isinstance(result, WebIDL.IDLInterface):
        iface = result.identifier.name

        is_global = result.getExtendedAttribute("Global")

        for member in result.members:
            name = member.identifier.name

            if member.isMethod() and member.affects == "Nothing":
                if (
                    PURE_INTERFACE_ALLOWLIST and not iface in PURE_INTERFACE_ALLOWLIST
                ) or name.startswith("_"):
                    continue

                if is_global:
                    raise Exception(
                        "Global methods and accessors are not supported: " + iface
                    )

                if iface not in pure_output:
                    pure_output[iface] = {}

                if member.isStatic():
                    owner_type = "static"
                else:
                    owner_type = "prototype"

                if owner_type not in pure_output[iface]:
                    pure_output[iface][owner_type] = []

                # All DOM getters are considered eagerly-evaluate-able.
                # Collect methods only.
                #
                # NOTE: We still need to calculate unsafe_getters_names for
                #       object preview.
                if member.isMethod():
                    pure_output[iface][owner_type].append(name)

            if (
                not iface in DEPRECATED_INTERFACE__EXCLUDE_LIST
                and not name in unsafe_getters_names
                and member.isAttr()
                and (
                    member.getExtendedAttribute("Deprecated")
                    or member.getExtendedAttribute("LegacyLenientThis")
                )
            ):
                unsafe_getters_names.append(name)


with open(pure_output_file, "w") as f:
    f.write(FILE_TEMPLATE % {"data": json.dumps(pure_output, indent=2, sort_keys=True)})
print("Successfully generated", pure_output_file)

unsafe_getters_names.sort()
with open(unsafe_getters_names_file, "w") as f:
    f.write(
        FILE_TEMPLATE
        % {"data": json.dumps(unsafe_getters_names, indent=2, sort_keys=True)}
    )
print("Successfully generated", unsafe_getters_names_file)

print("Formatting files...")
system("./mach eslint --fix " + pure_output_file + " " + unsafe_getters_names_file)
print("Files are now properly formatted")

# Parsing the idls generate a parser.out file that we don't have any use of.
if path.exists("parser.out"):
    remove("parser.out")
print("DONE")
