| # 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/. |
| |
| from __future__ import absolute_import |
| |
| import re |
| import os |
| from urlparse import urlparse |
| import mozpack.path as mozpath |
| from mozpack.chrome.flags import Flags |
| from mozpack.errors import errors |
| |
| |
| class ManifestEntry(object): |
| ''' |
| Base class for all manifest entry types. |
| Subclasses may define the following class or member variables: |
| - localized: indicates whether the manifest entry is used for localized |
| data. |
| - type: the manifest entry type (e.g. 'content' in |
| 'content global content/global/') |
| - allowed_flags: a set of flags allowed to be defined for the given |
| manifest entry type. |
| |
| A manifest entry is attached to a base path, defining where the manifest |
| entry is bound to, and that is used to find relative paths defined in |
| entries. |
| ''' |
| localized = False |
| type = None |
| allowed_flags = [ |
| 'application', |
| 'platformversion', |
| 'os', |
| 'osversion', |
| 'abi', |
| 'xpcnativewrappers', |
| 'tablet', |
| 'process', |
| ] |
| |
| def __init__(self, base, *flags): |
| ''' |
| Initialize a manifest entry with the given base path and flags. |
| ''' |
| self.base = base |
| self.flags = Flags(*flags) |
| if not all(f in self.allowed_flags for f in self.flags): |
| errors.fatal('%s unsupported for %s manifest entries' % |
| (','.join(f for f in self.flags |
| if not f in self.allowed_flags), self.type)) |
| |
| def serialize(self, *args): |
| ''' |
| Serialize the manifest entry. |
| ''' |
| entry = [self.type] + list(args) |
| flags = str(self.flags) |
| if flags: |
| entry.append(flags) |
| return ' '.join(entry) |
| |
| def __eq__(self, other): |
| return self.base == other.base and str(self) == str(other) |
| |
| def __ne__(self, other): |
| return not self.__eq__(other) |
| |
| def __repr__(self): |
| return '<%s@%s>' % (str(self), self.base) |
| |
| def move(self, base): |
| ''' |
| Return a new manifest entry with a different base path. |
| ''' |
| return parse_manifest_line(base, str(self)) |
| |
| def rebase(self, base): |
| ''' |
| Return a new manifest entry with all relative paths defined in the |
| entry relative to a new base directory. |
| The base class doesn't define relative paths, so it is equivalent to |
| move(). |
| ''' |
| return self.move(base) |
| |
| |
| class ManifestEntryWithRelPath(ManifestEntry): |
| ''' |
| Abstract manifest entry type with a relative path definition. |
| ''' |
| def __init__(self, base, relpath, *flags): |
| ManifestEntry.__init__(self, base, *flags) |
| self.relpath = relpath |
| |
| def __str__(self): |
| return self.serialize(self.relpath) |
| |
| def rebase(self, base): |
| ''' |
| Return a new manifest entry with all relative paths defined in the |
| entry relative to a new base directory. |
| ''' |
| clone = ManifestEntry.rebase(self, base) |
| clone.relpath = mozpath.rebase(self.base, base, self.relpath) |
| return clone |
| |
| @property |
| def path(self): |
| return mozpath.normpath(mozpath.join(self.base, |
| self.relpath)) |
| |
| |
| class Manifest(ManifestEntryWithRelPath): |
| ''' |
| Class for 'manifest' entries. |
| manifest some/path/to/another.manifest |
| ''' |
| type = 'manifest' |
| |
| |
| class ManifestChrome(ManifestEntryWithRelPath): |
| ''' |
| Abstract class for chrome entries. |
| ''' |
| def __init__(self, base, name, relpath, *flags): |
| ManifestEntryWithRelPath.__init__(self, base, relpath, *flags) |
| self.name = name |
| |
| @property |
| def location(self): |
| return mozpath.join(self.base, self.relpath) |
| |
| |
| class ManifestContent(ManifestChrome): |
| ''' |
| Class for 'content' entries. |
| content global content/global/ |
| ''' |
| type = 'content' |
| allowed_flags = ManifestChrome.allowed_flags + [ |
| 'contentaccessible', |
| 'platform', |
| ] |
| |
| def __str__(self): |
| return self.serialize(self.name, self.relpath) |
| |
| |
| class ManifestMultiContent(ManifestChrome): |
| ''' |
| Abstract class for chrome entries with multiple definitions. |
| Used for locale and skin entries. |
| ''' |
| type = None |
| |
| def __init__(self, base, name, id, relpath, *flags): |
| ManifestChrome.__init__(self, base, name, relpath, *flags) |
| self.id = id |
| |
| def __str__(self): |
| return self.serialize(self.name, self.id, self.relpath) |
| |
| |
| class ManifestLocale(ManifestMultiContent): |
| ''' |
| Class for 'locale' entries. |
| locale global en-US content/en-US/ |
| locale global fr content/fr/ |
| ''' |
| localized = True |
| type = 'locale' |
| |
| |
| class ManifestSkin(ManifestMultiContent): |
| ''' |
| Class for 'skin' entries. |
| skin global classic/1.0 content/skin/classic/ |
| ''' |
| type = 'skin' |
| |
| |
| class ManifestOverload(ManifestEntry): |
| ''' |
| Abstract class for chrome entries defining some kind of overloading. |
| Used for overlay, override or style entries. |
| ''' |
| type = None |
| |
| def __init__(self, base, overloaded, overload, *flags): |
| ManifestEntry.__init__(self, base, *flags) |
| self.overloaded = overloaded |
| self.overload = overload |
| |
| def __str__(self): |
| return self.serialize(self.overloaded, self.overload) |
| |
| @property |
| def localized(self): |
| u = urlparse(self.overload) |
| return u.scheme == 'chrome' and \ |
| u.path.split('/')[0:2] == ['', 'locale'] |
| |
| |
| class ManifestOverlay(ManifestOverload): |
| ''' |
| Class for 'overlay' entries. |
| overlay chrome://global/content/viewSource.xul \ |
| chrome://browser/content/viewSourceOverlay.xul |
| ''' |
| type = 'overlay' |
| |
| |
| class ManifestStyle(ManifestOverload): |
| ''' |
| Class for 'style' entries. |
| style chrome://global/content/customizeToolbar.xul \ |
| chrome://browser/skin/ |
| ''' |
| type = 'style' |
| |
| |
| class ManifestOverride(ManifestOverload): |
| ''' |
| Class for 'override' entries. |
| override chrome://global/locale/netError.dtd \ |
| chrome://browser/locale/netError.dtd |
| ''' |
| type = 'override' |
| |
| |
| class ManifestResource(ManifestEntry): |
| ''' |
| Class for 'resource' entries. |
| resource gre-resources toolkit/res/ |
| resource services-sync resource://gre/modules/services-sync/ |
| |
| The target may be a relative path or a resource or chrome url. |
| ''' |
| type = 'resource' |
| |
| def __init__(self, base, name, target, *flags): |
| ManifestEntry.__init__(self, base, *flags) |
| self.name = name |
| self.target = target |
| |
| def __str__(self): |
| return self.serialize(self.name, self.target) |
| |
| def rebase(self, base): |
| u = urlparse(self.target) |
| if u.scheme and u.scheme != 'jar': |
| return ManifestEntry.rebase(self, base) |
| clone = ManifestEntry.rebase(self, base) |
| clone.target = mozpath.rebase(self.base, base, self.target) |
| return clone |
| |
| |
| class ManifestBinaryComponent(ManifestEntryWithRelPath): |
| ''' |
| Class for 'binary-component' entries. |
| binary-component some/path/to/a/component.dll |
| ''' |
| type = 'binary-component' |
| |
| |
| class ManifestComponent(ManifestEntryWithRelPath): |
| ''' |
| Class for 'component' entries. |
| component {b2bba4df-057d-41ea-b6b1-94a10a8ede68} foo.js |
| ''' |
| type = 'component' |
| |
| def __init__(self, base, cid, file, *flags): |
| ManifestEntryWithRelPath.__init__(self, base, file, *flags) |
| self.cid = cid |
| |
| def __str__(self): |
| return self.serialize(self.cid, self.relpath) |
| |
| |
| class ManifestInterfaces(ManifestEntryWithRelPath): |
| ''' |
| Class for 'interfaces' entries. |
| interfaces foo.xpt |
| ''' |
| type = 'interfaces' |
| |
| |
| class ManifestCategory(ManifestEntry): |
| ''' |
| Class for 'category' entries. |
| category command-line-handler m-browser @mozilla.org/browser/clh; |
| ''' |
| type = 'category' |
| |
| def __init__(self, base, category, name, value, *flags): |
| ManifestEntry.__init__(self, base, *flags) |
| self.category = category |
| self.name = name |
| self.value = value |
| |
| def __str__(self): |
| return self.serialize(self.category, self.name, self.value) |
| |
| |
| class ManifestContract(ManifestEntry): |
| ''' |
| Class for 'contract' entries. |
| contract @mozilla.org/foo;1 {b2bba4df-057d-41ea-b6b1-94a10a8ede68} |
| ''' |
| type = 'contract' |
| |
| def __init__(self, base, contractID, cid, *flags): |
| ManifestEntry.__init__(self, base, *flags) |
| self.contractID = contractID |
| self.cid = cid |
| |
| def __str__(self): |
| return self.serialize(self.contractID, self.cid) |
| |
| # All manifest classes by their type name. |
| MANIFESTS_TYPES = dict([(c.type, c) for c in globals().values() |
| if type(c) == type and issubclass(c, ManifestEntry) |
| and hasattr(c, 'type') and c.type]) |
| |
| MANIFEST_RE = re.compile(r'^#.*$') |
| |
| |
| def parse_manifest_line(base, line): |
| ''' |
| Parse a line from a manifest file with the given base directory and |
| return the corresponding ManifestEntry instance. |
| ''' |
| # Remove comments |
| cmd = MANIFEST_RE.sub('', line).strip().split() |
| if not cmd: |
| return None |
| if not cmd[0] in MANIFESTS_TYPES: |
| return errors.fatal('Unknown manifest directive: %s' % cmd[0]) |
| return MANIFESTS_TYPES[cmd[0]](base, *cmd[1:]) |
| |
| |
| def parse_manifest(root, path, fileobj=None): |
| ''' |
| Parse a manifest file. |
| ''' |
| base = mozpath.dirname(path) |
| if root: |
| path = os.path.normpath(os.path.abspath(os.path.join(root, path))) |
| if not fileobj: |
| fileobj = open(path) |
| linenum = 0 |
| for line in fileobj: |
| linenum += 1 |
| with errors.context(path, linenum): |
| e = parse_manifest_line(base, line) |
| if e: |
| yield e |
| |
| |
| def is_manifest(path): |
| ''' |
| Return whether the given path is that of a manifest file. |
| ''' |
| return path.endswith('.manifest') and not path.endswith('.CRT.manifest') \ |
| and not path.endswith('.exe.manifest') |