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