from urllib.parse import urlparse
import mozpack.path as mozpath
from mozpack.chrome.manifest import (
Manifest,
ManifestBinaryComponent,
ManifestChrome,
ManifestInterfaces,
ManifestMultiContent,
ManifestResource,
)
from mozpack.copier import FileRegistry, FileRegistrySubtree, Jarrer
from mozpack.errors import errors
from mozpack.files import ManifestFile
class PiecemealFormatter:
def __init__(self, copier):
assert isinstance(copier, (FileRegistry, FileRegistrySubtree))
self.copier = copier
self._sub_formatter = {}
self._frozen_bases = False
def add_base(self, base, addon=False):
assert not self._frozen_bases
assert base not in self._sub_formatter
assert all(base > b for b in self._sub_formatter)
self._add_base(base, addon)
def _get_base(self, path):
self._frozen_bases = True
base = mozpath.basedir(path, self._sub_formatter.keys())
relpath = mozpath.relpath(path, base) if base else path
return base, relpath
def add(self, path, content):
base, relpath = self._get_base(path)
if base is None:
return self.copier.add(relpath, content)
return self._sub_formatter[base].add(relpath, content)
def add_manifest(self, entry):
base, relpath = self._get_base(entry.base)
assert base is not None
return self._sub_formatter[base].add_manifest(entry.move(relpath))
def add_interfaces(self, path, content):
base, relpath = self._get_base(path)
assert base is not None
return self._sub_formatter[base].add_interfaces(relpath, content)
def contains(self, path):
assert "*" not in path
base, relpath = self._get_base(path)
if base is None:
return self.copier.contains(relpath)
return self._sub_formatter[base].contains(relpath)
class FlatFormatter(PiecemealFormatter):
def _add_base(self, base, addon=False):
self._sub_formatter[base] = FlatSubFormatter(
FileRegistrySubtree(base, self.copier)
)
class FlatSubFormatter:
def __init__(self, copier):
assert isinstance(copier, (FileRegistry, FileRegistrySubtree))
self.copier = copier
self._chrome_db = {}
def add(self, path, content):
self.copier.add(path, content)
def add_manifest(self, entry):
if entry.base:
name = mozpath.basename(entry.base)
else:
name = "chrome"
path = mozpath.normpath(mozpath.join(entry.base, "%s.manifest" % name))
if not self.copier.contains(path):
if entry.base:
parent = mozpath.dirname(entry.base)
relbase = mozpath.basename(entry.base)
relpath = mozpath.join(relbase, mozpath.basename(path))
self.add_manifest(Manifest(parent, relpath))
self.copier.add(path, ManifestFile(entry.base))
if isinstance(entry, ManifestChrome):
data = self._chrome_db.setdefault(entry.name, {})
if isinstance(entry, ManifestMultiContent):
entries = data.setdefault(entry.type, {}).setdefault(entry.id, [])
else:
entries = data.setdefault(entry.type, [])
for e in entries:
if entry == e:
errors.warn('"%s" is duplicated. Skipping.' % entry)
return
if not entry.flags or e.flags and entry.flags == e.flags:
errors.fatal('"%s" overrides "%s"' % (entry, e))
entries.append(entry)
self.copier[path].add(entry)
def add_interfaces(self, path, content):
self.copier.add(path, content)
self.add_manifest(
ManifestInterfaces(mozpath.dirname(path), mozpath.basename(path))
)
def contains(self, path):
assert "*" not in path
return self.copier.contains(path)
class JarFormatter(PiecemealFormatter):
def __init__(self, copier, compress=True):
PiecemealFormatter.__init__(self, copier)
self._compress = compress
def _add_base(self, base, addon=False):
if addon is True:
jarrer = Jarrer(self._compress)
self.copier.add(base + ".xpi", jarrer)
self._sub_formatter[base] = FlatSubFormatter(jarrer)
else:
self._sub_formatter[base] = JarSubFormatter(
FileRegistrySubtree(base, self.copier), self._compress
)
class JarSubFormatter(PiecemealFormatter):
def __init__(self, copier, compress=True):
PiecemealFormatter.__init__(self, copier)
self._frozen_chrome = False
self._compress = compress
self._sub_formatter[""] = FlatSubFormatter(copier)
def _jarize(self, entry, relpath):
base = entry.base
basepath = mozpath.split(relpath)[0]
chromepath = mozpath.join(base, basepath)
entry = (
entry.rebase(chromepath)
.move(mozpath.join(base, "jar:%s.jar!" % basepath))
.rebase(base)
)
return chromepath, entry
def add_manifest(self, entry):
if isinstance(entry, ManifestChrome) and not urlparse(entry.relpath).scheme:
chromepath, entry = self._jarize(entry, entry.relpath)
assert not self._frozen_chrome
if chromepath not in self._sub_formatter:
jarrer = Jarrer(self._compress)
self.copier.add(chromepath + ".jar", jarrer)
self._sub_formatter[chromepath] = FlatSubFormatter(jarrer)
elif isinstance(entry, ManifestResource) and not urlparse(entry.target).scheme:
chromepath, new_entry = self._jarize(entry, entry.target)
if chromepath in self._sub_formatter:
entry = new_entry
PiecemealFormatter.add_manifest(self, entry)
class OmniJarFormatter(JarFormatter):
def __init__(self, copier, omnijar_name, compress=True, non_resources=()):
JarFormatter.__init__(self, copier, compress)
self._omnijar_name = omnijar_name
self._non_resources = non_resources
def _add_base(self, base, addon=False):
if addon:
parent_base = mozpath.basedir(base, self._sub_formatter.keys())
rel_base = mozpath.relpath(base, parent_base)
parent_sub_formatter = self._sub_formatter[parent_base]
if parent_sub_formatter.is_resource(rel_base):
omnijar_sub_formatter = parent_sub_formatter._sub_formatter[
self._omnijar_name
]
self._sub_formatter[base] = FlatSubFormatter(
FileRegistrySubtree(rel_base, omnijar_sub_formatter.copier)
)
return
JarFormatter._add_base(self, base, addon)
else:
self._sub_formatter[base] = OmniJarSubFormatter(
FileRegistrySubtree(base, self.copier),
self._omnijar_name,
self._compress,
self._non_resources,
)
class OmniJarSubFormatter(PiecemealFormatter):
def __init__(self, copier, omnijar_name, compress=True, non_resources=()):
PiecemealFormatter.__init__(self, copier)
self._omnijar_name = omnijar_name
self._compress = compress
self._non_resources = non_resources
self._sub_formatter[""] = FlatSubFormatter(copier)
jarrer = Jarrer(self._compress)
self._sub_formatter[omnijar_name] = FlatSubFormatter(jarrer)
def _get_base(self, path):
base = self._omnijar_name if self.is_resource(path) else ""
if base and not self.copier.contains(base):
self.copier.add(base, self._sub_formatter[base].copier)
return base, path
def add_manifest(self, entry):
base = ""
if not isinstance(entry, ManifestBinaryComponent):
base = self._omnijar_name
formatter = self._sub_formatter[base]
return formatter.add_manifest(entry)
def is_resource(self, path):
if any(mozpath.match(path, p.replace("*", "**")) for p in self._non_resources):
return False
path = mozpath.split(path)
if path[0] == "chrome":
return len(path) == 1 or path[1] != "icons"
if path[0] == "components":
return path[-1].endswith((".js", ".xpt"))
if path[0] == "res":
return len(path) == 1 or (
path[1] != "cursors"
and path[1] != "touchbar"
and path[1] != "MainMenu.nib"
)
if path[0] == "defaults":
return len(path) != 3 or not (
path[2] == "channel-prefs.js" and path[1] in ["pref", "preferences"]
)
if len(path) <= 2 and path[-1] == "greprefs.js":
return True
return path[0] in [
"modules",
"moz-src",
"actors",
"dictionaries",
"hyphenation",
"localization",
"default.locale",
"contentaccessible",
]