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