1#!/usr/bin/env python
2# Copyright (c) 2012 The Chromium Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6# This helps you preview the apps and extensions docs.
7#
8#    ./preview.py --help
9#
10# There are two modes: server- and render- mode. The default is server, in which
11# a webserver is started on a port (default 8000). Navigating to paths on
12# http://localhost:8000, for example
13#
14#     http://localhost:8000/extensions/tabs.html
15#
16# will render the documentation for the extension tabs API.
17#
18# On the other hand, render mode statically renders docs to stdout. Use this
19# to save the output (more convenient than needing to save the page in a
20# browser), handy when uploading the docs somewhere (e.g. for a review),
21# and for profiling the server. For example,
22#
23#    ./preview.py -r extensions/tabs.html
24#
25# will output the documentation for the tabs API on stdout and exit immediately.
26
27# NOTE: RUN THIS FIRST. Or all third_party imports will fail.
28import build_server
29# Copy all the files necessary to run the server. These are cleaned up when the
30# server quits.
31build_server.main()
32
33from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
34import logging
35import optparse
36import posixpath
37import time
38
39from local_renderer import LocalRenderer
40
41class _RequestHandler(BaseHTTPRequestHandler):
42  '''A HTTPRequestHandler that outputs the docs page generated by Handler.
43  '''
44  def do_GET(self):
45    # Sanitize path to guarantee that it stays within the server.
46    if not posixpath.abspath(self.path.lstrip('/')).startswith(
47                             posixpath.abspath('')):
48      return
49
50    # Rewrite paths that would otherwise be served from app.yaml.
51    self.path = {
52      '/robots.txt': '../../server2/robots.txt',
53      '/favicon.ico': '../../server2/chrome-32.ico',
54      '/apple-touch-icon-precomposed.png': '../../server2/chrome-128.png'
55    }.get(self.path, self.path)
56    response = LocalRenderer.Render(self.path, headers=dict(self.headers))
57    self.protocol_version = 'HTTP/1.1'
58    self.send_response(response.status)
59    for k, v in response.headers.iteritems():
60      self.send_header(k, v)
61    self.end_headers()
62    self.wfile.write(response.content.ToString())
63
64if __name__ == '__main__':
65  parser = optparse.OptionParser(
66      description='Runs a server to preview the extension documentation.',
67      usage='usage: %prog [option]...')
68  parser.add_option('-p', '--port', default='8000',
69      help='port to run the server on')
70  parser.add_option('-r', '--render', default='',
71      help='statically render a page and print to stdout rather than starting '
72           'the server, e.g. apps/storage.html. The path may optionally end '
73           'with #n where n is the number of times to render the page before '
74           'printing it, e.g. apps/storage.html#50, to use for profiling.')
75  parser.add_option('-t', '--time', action='store_true',
76      help='Print the time taken rendering rather than the result.')
77
78  (opts, argv) = parser.parse_args()
79
80  if opts.render:
81    if opts.render.find('#') >= 0:
82      (path, iterations) = opts.render.rsplit('#', 1)
83      extra_iterations = int(iterations) - 1
84    else:
85      path = opts.render
86      extra_iterations = 0
87
88    if opts.time:
89      start_time = time.time()
90
91    response = LocalRenderer.Render(path)
92    if response.status != 200:
93      print('Error status: %s' % response.status)
94      exit(1)
95
96    for _ in range(extra_iterations):
97      LocalRenderer.Render(path)
98
99    if opts.time:
100      print('Took %s seconds' % (time.time() - start_time))
101    else:
102      print(response.content.ToString())
103    exit()
104
105  print('Starting previewserver on port %s' % opts.port)
106  print('')
107  print('The extension documentation can be found at:')
108  print('')
109  print('  http://localhost:%s/extensions/' % opts.port)
110  print('')
111  print('The apps documentation can be found at:')
112  print('')
113  print('  http://localhost:%s/apps/' % opts.port)
114  print('')
115
116  logging.getLogger().setLevel(logging.INFO)
117  server = HTTPServer(('', int(opts.port)), _RequestHandler)
118  try:
119    server.serve_forever()
120  finally:
121    server.socket.close()
122