1# Copyright (c) 2014 The Chromium Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5import os 6import sys 7import subprocess 8import tempfile 9import StringIO 10 11from py_vulcanize import html_generation_controller 12 13 14html_warning_message = """ 15 16 17<!-- 18WARNING: This file is auto generated. 19 20 Do not edit directly. 21--> 22""" 23 24js_warning_message = """ 25// Copyright 2015 The Chromium Authors. All rights reserved. 26// Use of this source code is governed by a BSD-style license that can be 27// found in the LICENSE file. 28 29/* WARNING: This file is auto generated. 30 * 31 * Do not edit directly. 32 */ 33""" 34 35css_warning_message = """ 36/* Copyright 2015 The Chromium Authors. All rights reserved. 37 * Use of this source code is governed by a BSD-style license that can be 38 * found in the LICENSE file. */ 39 40/* WARNING: This file is auto-generated. 41 * 42 * Do not edit directly. 43 */ 44""" 45 46 47def _AssertIsUTF8(f): 48 if isinstance(f, StringIO.StringIO): 49 return 50 assert f.encoding == 'utf-8' 51 52 53def _MinifyJS(input_js): 54 py_vulcanize_path = os.path.abspath(os.path.join( 55 os.path.dirname(__file__), '..')) 56 rjsmin_path = os.path.abspath( 57 os.path.join(py_vulcanize_path, 'third_party', 'rjsmin', 'rjsmin.py')) 58 59 with tempfile.NamedTemporaryFile() as _: 60 args = [ 61 'python', 62 rjsmin_path 63 ] 64 p = subprocess.Popen(args, 65 stdin=subprocess.PIPE, 66 stdout=subprocess.PIPE, 67 stderr=subprocess.PIPE) 68 res = p.communicate(input=input_js) 69 errorcode = p.wait() 70 if errorcode != 0: 71 sys.stderr.write('rJSmin exited with error code %d' % errorcode) 72 sys.stderr.write(res[1]) 73 raise Exception('Failed to minify, omgah') 74 return res[0] 75 76 77def GenerateJS(load_sequence, 78 use_include_tags_for_scripts=False, 79 dir_for_include_tag_root=None, 80 minify=False, 81 report_sizes=False): 82 f = StringIO.StringIO() 83 GenerateJSToFile(f, 84 load_sequence, 85 use_include_tags_for_scripts, 86 dir_for_include_tag_root, 87 minify=minify, 88 report_sizes=report_sizes) 89 90 return f.getvalue() 91 92 93def GenerateJSToFile(f, 94 load_sequence, 95 use_include_tags_for_scripts=False, 96 dir_for_include_tag_root=None, 97 minify=False, 98 report_sizes=False): 99 _AssertIsUTF8(f) 100 if use_include_tags_for_scripts and dir_for_include_tag_root is None: 101 raise Exception('Must provide dir_for_include_tag_root') 102 103 f.write(js_warning_message) 104 f.write('\n') 105 106 if not minify: 107 flatten_to_file = f 108 else: 109 flatten_to_file = StringIO.StringIO() 110 111 for module in load_sequence: 112 module.AppendJSContentsToFile(flatten_to_file, 113 use_include_tags_for_scripts, 114 dir_for_include_tag_root) 115 if minify: 116 js = flatten_to_file.getvalue() 117 minified_js = _MinifyJS(js) 118 f.write(minified_js) 119 f.write('\n') 120 121 if report_sizes: 122 for module in load_sequence: 123 s = StringIO.StringIO() 124 module.AppendJSContentsToFile(s, 125 use_include_tags_for_scripts, 126 dir_for_include_tag_root) 127 128 # Add minified size info. 129 js = s.getvalue() 130 min_js_size = str(len(_MinifyJS(js))) 131 132 # Print names for this module. Some domain-specific simplifications 133 # are included to make pivoting more obvious. 134 parts = module.name.split('.') 135 if parts[:2] == ['base', 'ui']: 136 parts = ['base_ui'] + parts[2:] 137 if parts[:2] == ['tracing', 'importer']: 138 parts = ['importer'] + parts[2:] 139 tln = parts[0] 140 sln = '.'.join(parts[:2]) 141 142 # Output 143 print '%i\t%s\t%s\t%s\t%s' % (len(js), min_js_size, module.name, tln, sln) 144 sys.stdout.flush() 145 146 147class ExtraScript(object): 148 149 def __init__(self, script_id=None, text_content=None, content_type=None): 150 if script_id is not None: 151 assert script_id[0] != '#' 152 self.script_id = script_id 153 self.text_content = text_content 154 self.content_type = content_type 155 156 def WriteToFile(self, output_file): 157 _AssertIsUTF8(output_file) 158 attrs = [] 159 if self.script_id: 160 attrs.append('id="%s"' % self.script_id) 161 if self.content_type: 162 attrs.append('content-type="%s"' % self.content_type) 163 164 if len(attrs) > 0: 165 output_file.write('<script %s>\n' % ' '.join(attrs)) 166 else: 167 output_file.write('<script>\n') 168 if self.text_content: 169 output_file.write(self.text_content) 170 output_file.write('</script>\n') 171 172 173def _MinifyCSS(css_text): 174 py_vulcanize_path = os.path.abspath(os.path.join( 175 os.path.dirname(__file__), '..')) 176 rcssmin_path = os.path.abspath( 177 os.path.join(py_vulcanize_path, 'third_party', 'rcssmin', 'rcssmin.py')) 178 179 with tempfile.NamedTemporaryFile() as _: 180 rcssmin_args = ['python', rcssmin_path] 181 p = subprocess.Popen(rcssmin_args, 182 stdin=subprocess.PIPE, 183 stdout=subprocess.PIPE, 184 stderr=subprocess.PIPE) 185 res = p.communicate(input=css_text) 186 errorcode = p.wait() 187 if errorcode != 0: 188 sys.stderr.write('rCSSmin exited with error code %d' % errorcode) 189 sys.stderr.write(res[1]) 190 raise Exception('Failed to generate css for %s.' % css_text) 191 return res[0] 192 193 194def GenerateStandaloneHTMLAsString(*args, **kwargs): 195 f = StringIO.StringIO() 196 GenerateStandaloneHTMLToFile(f, *args, **kwargs) 197 return f.getvalue() 198 199 200def GenerateStandaloneHTMLToFile(output_file, 201 load_sequence, 202 title=None, 203 flattened_js_url=None, 204 extra_scripts=None, 205 minify=False, 206 report_sizes=False, 207 output_html_head_and_body=True): 208 """Writes a HTML file with the content of all modules in a load sequence. 209 210 The load_sequence is a list of (HTML or JS) Module objects; the order that 211 they're inserted into the file depends on their type and position in the load 212 sequence. 213 """ 214 _AssertIsUTF8(output_file) 215 extra_scripts = extra_scripts or [] 216 217 if output_html_head_and_body: 218 output_file.write( 219 '<!DOCTYPE html>\n' 220 '<html>\n' 221 ' <head i18n-values="dir:textdirection;">\n' 222 ' <meta http-equiv="Content-Type" content="text/html;' 223 'charset=utf-8">\n') 224 if title: 225 output_file.write(' <title>%s</title>\n ' % title) 226 else: 227 assert title is None 228 229 loader = load_sequence[0].loader 230 231 written_style_sheets = set() 232 233 class HTMLGenerationController( 234 html_generation_controller.HTMLGenerationController): 235 236 def __init__(self, module): 237 self.module = module 238 239 def GetHTMLForStylesheetHRef(self, href): 240 resource = self.module.HRefToResource( 241 href, '<link rel="stylesheet" href="%s">' % href) 242 style_sheet = loader.LoadStyleSheet(resource.name) 243 244 if style_sheet in written_style_sheets: 245 return None 246 written_style_sheets.add(style_sheet) 247 248 text = style_sheet.contents_with_inlined_images 249 if minify: 250 text = _MinifyCSS(text) 251 return '<style>\n%s\n</style>' % text 252 253 for module in load_sequence: 254 controller = HTMLGenerationController(module) 255 module.AppendHTMLContentsToFile(output_file, controller, minify=minify) 256 257 if flattened_js_url: 258 output_file.write('<script src="%s"></script>\n' % flattened_js_url) 259 else: 260 output_file.write('<script>\n') 261 js = GenerateJS(load_sequence, minify=minify, report_sizes=report_sizes) 262 output_file.write(js) 263 output_file.write('</script>\n') 264 265 for extra_script in extra_scripts: 266 extra_script.WriteToFile(output_file) 267 268 if output_html_head_and_body: 269 output_file.write('</head>\n <body>\n </body>\n</html>\n') 270