# 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 script implements the `mach devtools-css-db` command. It runs the C preprocessor on the CSS properties header file to get the list of preferences associated with a specific property, and it runs an xpcshell script that uses inIDOMUtils to query the CSS properties used by the browser. This information is used to generate the properties-db.js file. """ import json import os import sys import string import subprocess from mozbuild import shellutil from mozbuild.base import ( MozbuildObject, MachCommandBase, ) from mach.decorators import ( CommandProvider, Command, ) def resolve_path(start, relativePath): """Helper to resolve a path from a start, and a relative path""" return os.path.normpath(os.path.join(start, relativePath)) @CommandProvider class MachCommands(MachCommandBase): @Command( 'devtools-css-db', category='post-build', description='Rebuild the devtool\'s static css properties database.') def generate_css_db(self): """Generate the static css properties database for devtools and write it to file.""" print("Re-generating the css properties database...") preferences = self.get_preferences() db = self.get_properties_db_from_xpcshell() self.output_template({ 'preferences': json.dumps(preferences), 'cssProperties': json.dumps(db['cssProperties']), 'pseudoElements': json.dumps(db['pseudoElements'])}) def get_preferences(self): """Get all of the preferences associated with enabling and disabling a property.""" # Build the command to run the preprocessor on PythonCSSProps.h headerPath = resolve_path(self.topsrcdir, 'layout/style/PythonCSSProps.h') cpp = self.substs['CPP'] if not cpp: print("Unable to find the cpp program. Please do a full, non-artifact") print("build and try this again.") sys.exit(1) cmd = shellutil.split(cpp) cmd += shellutil.split(self.substs['ACDEFINES']) cmd.append(headerPath) # The preprocessed list takes the following form: # [ (name, prop, id, flags, pref, proptype), ... ] preprocessed = eval(subprocess.check_output(cmd)) # Map this list # (name, prop, id, flags, pref, proptype) => (name, pref) preferences = [ (name, pref) for name, prop, id, flags, pref, proptype in preprocessed if 'CSS_PROPERTY_INTERNAL' not in flags] return preferences def get_properties_db_from_xpcshell(self): """Generate the static css properties db for devtools from an xpcshell script.""" build = MozbuildObject.from_environment() # Get the paths script_path = resolve_path(self.topsrcdir, 'devtools/shared/css/generated/generate-properties-db.js') browser_path = resolve_path(self.topobjdir, 'dist/bin/browser') xpcshell_path = build.get_binary_path(what='xpcshell') print(browser_path) # Run the xcpshell script, and set the appdir flag to the browser path so that # we have the proper dependencies for requiring the loader. contents = subprocess.check_output([xpcshell_path, '-a', browser_path, script_path]) return json.loads(contents) def output_template(self, substitutions): """Output a the properties-db.js from a template.""" js_template_path = resolve_path(self.topsrcdir, 'devtools/shared/css/generated/properties-db.js.in') destination_path = resolve_path(self.topsrcdir, 'devtools/shared/css/generated/properties-db.js') with open(js_template_path, 'r') as handle: js_template = handle.read() preamble = '/* THIS IS AN AUTOGENERATED FILE. DO NOT EDIT */\n\n' contents = string.Template(js_template).substitute(substitutions) with open(destination_path, 'w') as destination: destination.write(preamble + contents) print('The database was successfully generated at ' + destination_path)