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