This repository has been archived by the owner on Feb 1, 2019. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 42
/
chromemanifest.py
240 lines (186 loc) · 8.43 KB
/
chromemanifest.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
import os.path
from validator.contextgenerator import ContextGenerator
class ChromeManifest(object):
"""This class enables convenient parsing and iteration of
chrome.manifest files."""
def __init__(self, data, path):
self.context = ContextGenerator(data)
self.lines = data.split('\n')
# Extract the data from the triples in the manifest
triples = []
counter = 0
for line in self.lines:
line = line.strip()
counter += 1
# Skip weird lines.
if line.startswith('#'):
continue
triple = line.split(None, 2)
if not triple:
continue
elif len(triple) == 2:
triple.append('')
if len(triple) < 3:
continue
triples.append({'subject': triple[0],
'predicate': triple[1],
'object': triple[2],
'line': counter,
'filename': path,
'context': self.context})
self.triples = triples
def get_value(self, subject=None, predicate=None, object_=None):
"""Returns the first triple value matching the given subject,
predicate, and/or object"""
for triple in self.triples:
# Filter out non-matches
if (subject and triple['subject'] != subject) or \
(predicate and triple['predicate'] != predicate) or \
(object_ and triple['object'] != object_): # pragma: no cover
continue
# Return the first found.
return triple
return None
def get_objects(self, subject=None, predicate=None):
"""Returns a generator of objects that correspond to the
specified subjects and predicates."""
for triple in self.triples:
# Filter out non-matches
if ((subject and triple['subject'] != subject) or
(predicate and triple['predicate'] != predicate)):
continue
yield triple['object']
def get_triples(self, subject=None, predicate=None, object_=None):
"""Returns triples that correspond to the specified subject,
predicates, and objects."""
for triple in self.triples:
# Filter out non-matches
if subject is not None and triple['subject'] != subject:
continue
if predicate is not None and triple['predicate'] != predicate:
continue
if object_ is not None and triple['object'] != object_:
continue
yield triple
def get_applicable_overlays(self, error_bundle):
"""
Given an error bundle, a list of overlays that are present in the
current package or subpackage are returned.
"""
content_paths = self.get_triples(subject='content')
if not content_paths:
return set()
# Create some variables that will store where the applicable content
# instruction path references and where it links to.
chrome_path = ''
content_root_path = '/'
# Look through each of the listed packages and paths.
for path in content_paths:
chrome_name = path['predicate']
if not path['object']:
continue
path_location = path['object'].strip().split()[0]
# Handle jarred paths differently.
if path_location.startswith('jar:'):
if not error_bundle.is_nested_package:
continue
# Parse out the JAR and it's location within the chrome.
split_jar_url = path_location[4:].split('!', 2)
# Ignore invalid/unsupported JAR URLs.
if len(split_jar_url) != 2:
continue
# Unpack the JAR URL.
jar_path, package_path = split_jar_url
# Ignore the instruction if the JAR it points to doesn't match
# up with the current subpackage tree.
if jar_path != error_bundle.package_stack[0]:
continue
chrome_path = self._url_chunk_join(chrome_name, package_path)
# content_root_path stays at the default: /
break
else:
# If we're in a subpackage, a content instruction referring to
# the root of the package obviously doesn't apply.
if error_bundle.is_nested_package:
continue
chrome_path = self._url_chunk_join(chrome_name, 'content')
content_root_path = '/%s/' % path_location.strip('/')
break
if not chrome_path:
return set()
applicable_overlays = set()
chrome_path = 'chrome://%s' % self._url_chunk_join(chrome_path + '/')
for overlay in self.get_triples(subject='overlay'):
if not overlay['object']:
error_bundle.error(
err_id=('chromemanifest', 'get_applicable_overalys',
'object'),
error='Overlay instruction missing a property.',
description='When overlays are registered in a chrome '
'manifest file, they require a namespace and '
'a chrome URL at minimum.',
filename=overlay['filename'],
line=overlay['line'],
context=self.context) #TODO(basta): Update this!
continue
overlay_url = overlay['object'].split()[0]
if overlay_url.startswith(chrome_path):
overlay_relative_path = overlay_url[len(chrome_path):]
applicable_overlays.add('/%s' %
self._url_chunk_join(content_root_path,
overlay_relative_path))
return applicable_overlays
def reverse_lookup(self, state, path):
"""
Returns a chrome URL for a given path, given the current package depth
in an error bundle.
State may either be an error bundle or the actual package stack.
"""
# Make sure the path starts with a forward slash.
if not path.startswith('/'):
path = '/%s' % path
# If the state is an error bundle, extract the package stack.
if not isinstance(state, list):
state = state.package_stack
content_paths = self.get_triples(subject='content')
for content_path in content_paths:
chrome_name = content_path['predicate']
if not content_path['object']:
continue
path_location = content_path['object'].split()[0]
if path_location.startswith('jar:'):
if not state:
continue
# Parse out the JAR and it's location within the chrome.
split_jar_url = path_location[4:].split('!', 2)
# Ignore invalid/unsupported JAR URLs.
if len(split_jar_url) != 2:
continue
# Unpack the JAR URL.
jar_path, package_path = split_jar_url
if jar_path != state[0]:
continue
return 'chrome://%s' % self._url_chunk_join(chrome_name,
package_path,
path)
else:
if state:
continue
path_location = '/%s/' % path_location.strip('/')
rel_path = os.path.relpath(path, path_location)
if rel_path.startswith('../') or rel_path == '..':
continue
return 'chrome://%s' % self._url_chunk_join(chrome_name,
rel_path)
return None
def _url_chunk_join(self, *args):
"""Join the arguments together to form a predictable URL chunk."""
# Strip slashes from either side of each path piece.
pathlets = map(lambda s: s.strip('/'), args)
# Remove empty pieces.
pathlets = filter(None, pathlets)
url = '/'.join(pathlets)
# If this is a directory, add a trailing slash.
if args[-1].endswith('/'):
url = '%s/' % url
return url