Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(144)

Side by Side Diff: third_party/chrome/tools/preview.py

Issue 12261015: Import chrome idl into third_party (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 #!/usr/bin/env python
2
3 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
4 # Use of this source code is governed by a BSD-style license that can be
5 # found in the LICENSE file.
6 """Server for viewing the compiled C++ code from tools/json_schema_compiler.
7 """
8
9 import cc_generator
10 import code
11 import cpp_type_generator
12 import cpp_util
13 import h_generator
14 import idl_schema
15 import json_schema
16 import model
17 import optparse
18 import os
19 import sys
20 import urlparse
21 from highlighters import (
22 pygments_highlighter, none_highlighter, hilite_me_highlighter)
23 from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
24
25 class CompilerHandler(BaseHTTPRequestHandler):
26 """A HTTPRequestHandler that outputs the result of tools/json_schema_compiler.
27 """
28 def do_GET(self):
29 parsed_url = urlparse.urlparse(self.path)
30 request_path = self._GetRequestPath(parsed_url)
31
32 chromium_favicon = 'http://codereview.chromium.org/static/favicon.ico'
33
34 head = code.Code()
35 head.Append('<link rel="icon" href="%s">' % chromium_favicon)
36 head.Append('<link rel="shortcut icon" href="%s">' % chromium_favicon)
37
38 body = code.Code()
39
40 try:
41 if os.path.isdir(request_path):
42 self._ShowPanels(parsed_url, head, body)
43 else:
44 self._ShowCompiledFile(parsed_url, head, body)
45 finally:
46 self.wfile.write('<html><head>')
47 self.wfile.write(head.Render())
48 self.wfile.write('</head><body>')
49 self.wfile.write(body.Render())
50 self.wfile.write('</body></html>')
51
52 def _GetRequestPath(self, parsed_url, strip_nav=False):
53 """Get the relative path from the current directory to the requested file.
54 """
55 path = parsed_url.path
56 if strip_nav:
57 path = parsed_url.path.replace('/nav', '')
58 return os.path.normpath(os.curdir + path)
59
60 def _ShowPanels(self, parsed_url, head, body):
61 """Show the previewer frame structure.
62
63 Code panes are populated via XHR after links in the nav pane are clicked.
64 """
65 (head.Append('<style>')
66 .Append('body {')
67 .Append(' margin: 0;')
68 .Append('}')
69 .Append('.pane {')
70 .Append(' height: 100%;')
71 .Append(' overflow-x: auto;')
72 .Append(' overflow-y: scroll;')
73 .Append(' display: inline-block;')
74 .Append('}')
75 .Append('#nav_pane {')
76 .Append(' width: 20%;')
77 .Append('}')
78 .Append('#nav_pane ul {')
79 .Append(' list-style-type: none;')
80 .Append(' padding: 0 0 0 1em;')
81 .Append('}')
82 .Append('#cc_pane {')
83 .Append(' width: 40%;')
84 .Append('}')
85 .Append('#h_pane {')
86 .Append(' width: 40%;')
87 .Append('}')
88 .Append('</style>')
89 )
90
91 body.Append(
92 '<div class="pane" id="nav_pane">%s</div>'
93 '<div class="pane" id="h_pane"></div>'
94 '<div class="pane" id="cc_pane"></div>' %
95 self._RenderNavPane(parsed_url.path[1:])
96 )
97
98 # The Javascript that interacts with the nav pane and panes to show the
99 # compiled files as the URL or highlighting options change.
100 body.Append('''<script type="text/javascript">
101 // Calls a function for each highlighter style <select> element.
102 function forEachHighlighterStyle(callback) {
103 var highlighterStyles =
104 document.getElementsByClassName('highlighter_styles');
105 for (var i = 0; i < highlighterStyles.length; ++i)
106 callback(highlighterStyles[i]);
107 }
108
109 // Called when anything changes, such as the highlighter or hashtag.
110 function updateEverything() {
111 var highlighters = document.getElementById('highlighters');
112 var highlighterName = highlighters.value;
113
114 // Cache in localStorage for when the page loads next.
115 localStorage.highlightersValue = highlighterName;
116
117 // Show/hide the highlighter styles.
118 var highlighterStyleName = '';
119 forEachHighlighterStyle(function(highlighterStyle) {
120 if (highlighterStyle.id === highlighterName + '_styles') {
121 highlighterStyle.removeAttribute('style')
122 highlighterStyleName = highlighterStyle.value;
123 } else {
124 highlighterStyle.setAttribute('style', 'display:none')
125 }
126
127 // Cache in localStorage for when the page next loads.
128 localStorage[highlighterStyle.id + 'Value'] = highlighterStyle.value;
129 });
130
131 // Populate the code panes.
132 function populateViaXHR(elementId, requestPath) {
133 var xhr = new XMLHttpRequest();
134 xhr.onreadystatechange = function() {
135 if (xhr.readyState != 4)
136 return;
137 if (xhr.status != 200) {
138 alert('XHR error to ' + requestPath);
139 return;
140 }
141 document.getElementById(elementId).innerHTML = xhr.responseText;
142 };
143 xhr.open('GET', requestPath, true);
144 xhr.send();
145 }
146
147 var targetName = window.location.hash;
148 targetName = targetName.substring('#'.length);
149 targetName = targetName.split('.', 1)[0]
150
151 if (targetName !== '') {
152 var basePath = window.location.pathname;
153 var query = 'highlighter=' + highlighterName + '&' +
154 'style=' + highlighterStyleName;
155 populateViaXHR('h_pane', basePath + '/' + targetName + '.h?' + query);
156 populateViaXHR('cc_pane', basePath + '/' + targetName + '.cc?' + query);
157 }
158 }
159
160 // Initial load: set the values of highlighter and highlighterStyles from
161 // localStorage.
162 (function() {
163 var cachedValue = localStorage.highlightersValue;
164 if (cachedValue)
165 document.getElementById('highlighters').value = cachedValue;
166
167 forEachHighlighterStyle(function(highlighterStyle) {
168 var cachedValue = localStorage[highlighterStyle.id + 'Value'];
169 if (cachedValue)
170 highlighterStyle.value = cachedValue;
171 });
172 })();
173
174 window.addEventListener('hashchange', updateEverything, false);
175 updateEverything();
176 </script>''')
177
178 def _LoadModel(self, basedir, name):
179 """Loads and returns the model for the |name| API from either its JSON or
180 IDL file, e.g.
181 name=contextMenus will be loaded from |basedir|/context_menus.json,
182 name=alarms will be loaded from |basedir|/alarms.idl.
183 """
184 loaders = {
185 'json': json_schema.Load,
186 'idl': idl_schema.Load
187 }
188 # APIs are referred to like "webRequest" but that's in a file
189 # "web_request.json" so we need to unixify the name.
190 unix_name = model.UnixName(name)
191 for loader_ext, loader_fn in loaders.items():
192 file_path = '%s/%s.%s' % (basedir, unix_name, loader_ext)
193 if os.path.exists(file_path):
194 # For historical reasons these files contain a singleton list with the
195 # model, so just return that single object.
196 return (loader_fn(file_path)[0], file_path)
197 raise ValueError('File for model "%s" not found' % name)
198
199 def _ShowCompiledFile(self, parsed_url, head, body):
200 """Show the compiled version of a json or idl file given the path to the
201 compiled file.
202 """
203 api_model = model.Model()
204
205 request_path = self._GetRequestPath(parsed_url)
206 (file_root, file_ext) = os.path.splitext(request_path)
207 (filedir, filename) = os.path.split(file_root)
208
209 try:
210 # Get main file.
211 (api_def, file_path) = self._LoadModel(filedir, filename)
212 namespace = api_model.AddNamespace(api_def, file_path)
213 type_generator = cpp_type_generator.CppTypeGenerator(
214 'previewserver::api', namespace, namespace.unix_name)
215
216 # Get the model's dependencies.
217 for dependency in api_def.get('dependencies', []):
218 # Dependencies can contain : in which case they don't refer to APIs,
219 # rather, permissions or manifest keys.
220 if ':' in dependency:
221 continue
222 (api_def, file_path) = self._LoadModel(filedir, dependency)
223 referenced_namespace = api_model.AddNamespace(api_def, file_path)
224 if referenced_namespace:
225 type_generator.AddNamespace(referenced_namespace,
226 cpp_util.Classname(referenced_namespace.name).lower())
227
228 # Generate code
229 if file_ext == '.h':
230 cpp_code = (h_generator.HGenerator(namespace, type_generator)
231 .Generate().Render())
232 elif file_ext == '.cc':
233 cpp_code = (cc_generator.CCGenerator(namespace, type_generator)
234 .Generate().Render())
235 else:
236 self.send_error(404, "File not found: %s" % request_path)
237 return
238
239 # Do highlighting on the generated code
240 (highlighter_param, style_param) = self._GetHighlighterParams(parsed_url)
241 head.Append('<style>' +
242 self.server.highlighters[highlighter_param].GetCSS(style_param) +
243 '</style>')
244 body.Append(self.server.highlighters[highlighter_param]
245 .GetCodeElement(cpp_code, style_param))
246 except IOError:
247 self.send_error(404, "File not found: %s" % request_path)
248 return
249 except (TypeError, KeyError, AttributeError,
250 AssertionError, NotImplementedError) as error:
251 body.Append('<pre>')
252 body.Append('compiler error: %s' % error)
253 body.Append('Check server log for more details')
254 body.Append('</pre>')
255 raise
256
257 def _GetHighlighterParams(self, parsed_url):
258 """Get the highlighting parameters from a parsed url.
259 """
260 query_dict = urlparse.parse_qs(parsed_url.query)
261 return (query_dict.get('highlighter', ['pygments'])[0],
262 query_dict.get('style', ['colorful'])[0])
263
264 def _RenderNavPane(self, path):
265 """Renders an HTML nav pane.
266
267 This consists of a select element to set highlight style, and a list of all
268 files at |path| with the appropriate onclick handlers to open either
269 subdirectories or JSON files.
270 """
271 html = code.Code()
272
273 # Highlighter chooser.
274 html.Append('<select id="highlighters" onChange="updateEverything()">')
275 for name, highlighter in self.server.highlighters.items():
276 html.Append('<option value="%s">%s</option>' %
277 (name, highlighter.DisplayName()))
278 html.Append('</select>')
279
280 html.Append('<br/>')
281
282 # Style for each highlighter.
283 # The correct highlighting will be shown by Javascript.
284 for name, highlighter in self.server.highlighters.items():
285 styles = sorted(highlighter.GetStyles())
286 if not styles:
287 continue
288
289 html.Append('<select class="highlighter_styles" id="%s_styles" '
290 'onChange="updateEverything()">' % name)
291 for style in styles:
292 html.Append('<option>%s</option>' % style)
293 html.Append('</select>')
294
295 html.Append('<br/>')
296
297 # The files, with appropriate handlers.
298 html.Append('<ul>')
299
300 # Make path point to a non-empty directory. This can happen if a URL like
301 # http://localhost:8000 is navigated to.
302 if path == '':
303 path = os.curdir
304
305 # Firstly, a .. link if this isn't the root.
306 if not os.path.samefile(os.curdir, path):
307 normpath = os.path.normpath(os.path.join(path, os.pardir))
308 html.Append('<li><a href="/%s">%s/</a>' % (normpath, os.pardir))
309
310 # Each file under path/
311 for filename in sorted(os.listdir(path)):
312 full_path = os.path.join(path, filename)
313 (file_root, file_ext) = os.path.splitext(full_path)
314 if os.path.isdir(full_path) and not full_path.endswith('.xcodeproj'):
315 html.Append('<li><a href="/%s/">%s/</a>' % (full_path, filename))
316 elif file_ext in ['.json', '.idl']:
317 # cc/h panes will automatically update via the hash change event.
318 html.Append('<li><a href="#%s">%s</a>' %
319 (filename, filename))
320
321 html.Append('</ul>')
322
323 return html.Render()
324
325 class PreviewHTTPServer(HTTPServer, object):
326 def __init__(self, server_address, handler, highlighters):
327 super(PreviewHTTPServer, self).__init__(server_address, handler)
328 self.highlighters = highlighters
329
330
331 if __name__ == '__main__':
332 parser = optparse.OptionParser(
333 description='Runs a server to preview the json_schema_compiler output.',
334 usage='usage: %prog [option]...')
335 parser.add_option('-p', '--port', default='8000',
336 help='port to run the server on')
337
338 (opts, argv) = parser.parse_args()
339
340 try:
341 print('Starting previewserver on port %s' % opts.port)
342 print('The extension documentation can be found at:')
343 print('')
344 print(' http://localhost:%s/chrome/common/extensions/api' % opts.port)
345 print('')
346
347 highlighters = {
348 'hilite': hilite_me_highlighter.HiliteMeHighlighter(),
349 'none': none_highlighter.NoneHighlighter()
350 }
351 try:
352 highlighters['pygments'] = pygments_highlighter.PygmentsHighlighter()
353 except ImportError as e:
354 pass
355
356 server = PreviewHTTPServer(('', int(opts.port)),
357 CompilerHandler,
358 highlighters)
359 server.serve_forever()
360 except KeyboardInterrupt:
361 server.socket.close()
OLDNEW
« no previous file with comments | « third_party/chrome/tools/model_test.py ('k') | third_party/chrome/tools/schema_bundle_generator.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698