1# This Source Code Form is subject to the terms of the Mozilla Public 2# License, v. 2.0. If a copy of the MPL was not distributed with this 3# file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 5from __future__ import absolute_import 6 7import re 8import os 9from urlparse import urlparse 10import mozpack.path as mozpath 11from mozpack.chrome.flags import Flags 12from mozpack.errors import errors 13 14 15class ManifestEntry(object): 16 ''' 17 Base class for all manifest entry types. 18 Subclasses may define the following class or member variables: 19 - localized: indicates whether the manifest entry is used for localized 20 data. 21 - type: the manifest entry type (e.g. 'content' in 22 'content global content/global/') 23 - allowed_flags: a set of flags allowed to be defined for the given 24 manifest entry type. 25 26 A manifest entry is attached to a base path, defining where the manifest 27 entry is bound to, and that is used to find relative paths defined in 28 entries. 29 ''' 30 localized = False 31 type = None 32 allowed_flags = [ 33 'application', 34 'platformversion', 35 'os', 36 'osversion', 37 'abi', 38 'xpcnativewrappers', 39 'tablet', 40 'process', 41 'contentaccessible', 42 ] 43 44 def __init__(self, base, *flags): 45 ''' 46 Initialize a manifest entry with the given base path and flags. 47 ''' 48 self.base = base 49 self.flags = Flags(*flags) 50 if not all(f in self.allowed_flags for f in self.flags): 51 errors.fatal('%s unsupported for %s manifest entries' % 52 (','.join(f for f in self.flags 53 if not f in self.allowed_flags), self.type)) 54 55 def serialize(self, *args): 56 ''' 57 Serialize the manifest entry. 58 ''' 59 entry = [self.type] + list(args) 60 flags = str(self.flags) 61 if flags: 62 entry.append(flags) 63 return ' '.join(entry) 64 65 def __eq__(self, other): 66 return self.base == other.base and str(self) == str(other) 67 68 def __ne__(self, other): 69 return not self.__eq__(other) 70 71 def __repr__(self): 72 return '<%s@%s>' % (str(self), self.base) 73 74 def move(self, base): 75 ''' 76 Return a new manifest entry with a different base path. 77 ''' 78 return parse_manifest_line(base, str(self)) 79 80 def rebase(self, base): 81 ''' 82 Return a new manifest entry with all relative paths defined in the 83 entry relative to a new base directory. 84 The base class doesn't define relative paths, so it is equivalent to 85 move(). 86 ''' 87 return self.move(base) 88 89 90class ManifestEntryWithRelPath(ManifestEntry): 91 ''' 92 Abstract manifest entry type with a relative path definition. 93 ''' 94 def __init__(self, base, relpath, *flags): 95 ManifestEntry.__init__(self, base, *flags) 96 self.relpath = relpath 97 98 def __str__(self): 99 return self.serialize(self.relpath) 100 101 def rebase(self, base): 102 ''' 103 Return a new manifest entry with all relative paths defined in the 104 entry relative to a new base directory. 105 ''' 106 clone = ManifestEntry.rebase(self, base) 107 clone.relpath = mozpath.rebase(self.base, base, self.relpath) 108 return clone 109 110 @property 111 def path(self): 112 return mozpath.normpath(mozpath.join(self.base, 113 self.relpath)) 114 115 116class Manifest(ManifestEntryWithRelPath): 117 ''' 118 Class for 'manifest' entries. 119 manifest some/path/to/another.manifest 120 ''' 121 type = 'manifest' 122 123 124class ManifestChrome(ManifestEntryWithRelPath): 125 ''' 126 Abstract class for chrome entries. 127 ''' 128 def __init__(self, base, name, relpath, *flags): 129 ManifestEntryWithRelPath.__init__(self, base, relpath, *flags) 130 self.name = name 131 132 @property 133 def location(self): 134 return mozpath.join(self.base, self.relpath) 135 136 137class ManifestContent(ManifestChrome): 138 ''' 139 Class for 'content' entries. 140 content global content/global/ 141 ''' 142 type = 'content' 143 allowed_flags = ManifestChrome.allowed_flags + [ 144 'contentaccessible', 145 'platform', 146 ] 147 148 def __str__(self): 149 return self.serialize(self.name, self.relpath) 150 151 152class ManifestMultiContent(ManifestChrome): 153 ''' 154 Abstract class for chrome entries with multiple definitions. 155 Used for locale and skin entries. 156 ''' 157 type = None 158 159 def __init__(self, base, name, id, relpath, *flags): 160 ManifestChrome.__init__(self, base, name, relpath, *flags) 161 self.id = id 162 163 def __str__(self): 164 return self.serialize(self.name, self.id, self.relpath) 165 166 167class ManifestLocale(ManifestMultiContent): 168 ''' 169 Class for 'locale' entries. 170 locale global en-US content/en-US/ 171 locale global fr content/fr/ 172 ''' 173 localized = True 174 type = 'locale' 175 176 177class ManifestSkin(ManifestMultiContent): 178 ''' 179 Class for 'skin' entries. 180 skin global classic/1.0 content/skin/classic/ 181 ''' 182 type = 'skin' 183 184 185class ManifestOverload(ManifestEntry): 186 ''' 187 Abstract class for chrome entries defining some kind of overloading. 188 Used for overlay, override or style entries. 189 ''' 190 type = None 191 192 def __init__(self, base, overloaded, overload, *flags): 193 ManifestEntry.__init__(self, base, *flags) 194 self.overloaded = overloaded 195 self.overload = overload 196 197 def __str__(self): 198 return self.serialize(self.overloaded, self.overload) 199 200 201class ManifestOverlay(ManifestOverload): 202 ''' 203 Class for 'overlay' entries. 204 overlay chrome://global/content/viewSource.xul \ 205 chrome://browser/content/viewSourceOverlay.xul 206 ''' 207 type = 'overlay' 208 209 210class ManifestStyle(ManifestOverload): 211 ''' 212 Class for 'style' entries. 213 style chrome://global/content/viewSource.xul \ 214 chrome://browser/skin/ 215 ''' 216 type = 'style' 217 218 219class ManifestOverride(ManifestOverload): 220 ''' 221 Class for 'override' entries. 222 override chrome://global/locale/netError.dtd \ 223 chrome://browser/locale/netError.dtd 224 ''' 225 type = 'override' 226 227 228class ManifestResource(ManifestEntry): 229 ''' 230 Class for 'resource' entries. 231 resource gre-resources toolkit/res/ 232 resource services-sync resource://gre/modules/services-sync/ 233 234 The target may be a relative path or a resource or chrome url. 235 ''' 236 type = 'resource' 237 238 def __init__(self, base, name, target, *flags): 239 ManifestEntry.__init__(self, base, *flags) 240 self.name = name 241 self.target = target 242 243 def __str__(self): 244 return self.serialize(self.name, self.target) 245 246 def rebase(self, base): 247 u = urlparse(self.target) 248 if u.scheme and u.scheme != 'jar': 249 return ManifestEntry.rebase(self, base) 250 clone = ManifestEntry.rebase(self, base) 251 clone.target = mozpath.rebase(self.base, base, self.target) 252 return clone 253 254 255class ManifestBinaryComponent(ManifestEntryWithRelPath): 256 ''' 257 Class for 'binary-component' entries. 258 binary-component some/path/to/a/component.dll 259 ''' 260 type = 'binary-component' 261 262 263class ManifestComponent(ManifestEntryWithRelPath): 264 ''' 265 Class for 'component' entries. 266 component {b2bba4df-057d-41ea-b6b1-94a10a8ede68} foo.js 267 ''' 268 type = 'component' 269 270 def __init__(self, base, cid, file, *flags): 271 ManifestEntryWithRelPath.__init__(self, base, file, *flags) 272 self.cid = cid 273 274 def __str__(self): 275 return self.serialize(self.cid, self.relpath) 276 277 278class ManifestInterfaces(ManifestEntryWithRelPath): 279 ''' 280 Class for 'interfaces' entries. 281 interfaces foo.xpt 282 ''' 283 type = 'interfaces' 284 285 286class ManifestCategory(ManifestEntry): 287 ''' 288 Class for 'category' entries. 289 category command-line-handler m-browser @mozilla.org/browser/clh; 290 ''' 291 type = 'category' 292 293 def __init__(self, base, category, name, value, *flags): 294 ManifestEntry.__init__(self, base, *flags) 295 self.category = category 296 self.name = name 297 self.value = value 298 299 def __str__(self): 300 return self.serialize(self.category, self.name, self.value) 301 302 303class ManifestContract(ManifestEntry): 304 ''' 305 Class for 'contract' entries. 306 contract @mozilla.org/foo;1 {b2bba4df-057d-41ea-b6b1-94a10a8ede68} 307 ''' 308 type = 'contract' 309 310 def __init__(self, base, contractID, cid, *flags): 311 ManifestEntry.__init__(self, base, *flags) 312 self.contractID = contractID 313 self.cid = cid 314 315 def __str__(self): 316 return self.serialize(self.contractID, self.cid) 317 318# All manifest classes by their type name. 319MANIFESTS_TYPES = dict([(c.type, c) for c in globals().values() 320 if type(c) == type and issubclass(c, ManifestEntry) 321 and hasattr(c, 'type') and c.type]) 322 323MANIFEST_RE = re.compile(r'^#.*$') 324 325 326def parse_manifest_line(base, line): 327 ''' 328 Parse a line from a manifest file with the given base directory and 329 return the corresponding ManifestEntry instance. 330 ''' 331 # Remove comments 332 cmd = MANIFEST_RE.sub('', line).strip().split() 333 if not cmd: 334 return None 335 if not cmd[0] in MANIFESTS_TYPES: 336 return errors.fatal('Unknown manifest directive: %s' % cmd[0]) 337 return MANIFESTS_TYPES[cmd[0]](base, *cmd[1:]) 338 339 340def parse_manifest(root, path, fileobj=None): 341 ''' 342 Parse a manifest file. 343 ''' 344 base = mozpath.dirname(path) 345 if root: 346 path = os.path.normpath(os.path.abspath(os.path.join(root, path))) 347 if not fileobj: 348 fileobj = open(path) 349 linenum = 0 350 for line in fileobj: 351 linenum += 1 352 with errors.context(path, linenum): 353 e = parse_manifest_line(base, line) 354 if e: 355 yield e 356 357 358def is_manifest(path): 359 ''' 360 Return whether the given path is that of a manifest file. 361 ''' 362 return path.endswith('.manifest') and not path.endswith('.CRT.manifest') \ 363 and not path.endswith('.exe.manifest') 364