1# Copyright 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 re 7 8from tvcm import module 9from tvcm import js_utils 10from tvcm import parse_html_deps 11from tvcm import style_sheet 12 13 14def IsHTMLResourceTheModuleGivenConflictingResourceNames( 15 js_resource, html_resource): # pylint: disable=unused-argument 16 return 'polymer-element' in html_resource.contents 17 18 19class HTMLModule(module.Module): 20 21 @property 22 def _module_dir_name(self): 23 return os.path.dirname(self.resource.absolute_path) 24 25 def Parse(self): 26 try: 27 parser_results = parse_html_deps.HTMLModuleParser().Parse(self.contents) 28 except Exception, ex: 29 raise Exception('While parsing %s: %s' % (self.name, str(ex))) 30 31 self.dependency_metadata = Parse(self.loader, 32 self.name, self._module_dir_name, 33 self.isComponent(), 34 parser_results) 35 self._parser_results = parser_results 36 37 def Load(self): 38 super(HTMLModule, self).Load() 39 40 reachable_names = set([m.name 41 for m in self.all_dependent_modules_recursive]) 42 if 'tr.exportTo' in self.contents: 43 if 'base.base' not in reachable_names: 44 raise Exception('%s: Does not have a dependency on base' % 45 os.path.relpath(self.resource.absolute_path)) 46 47 def GetTVCMDepsModuleType(self): 48 return 'tvcm.HTML_MODULE_TYPE' 49 50 def AppendJSContentsToFile(self, 51 f, 52 use_include_tags_for_scripts, 53 dir_for_include_tag_root): 54 super(HTMLModule, self).AppendJSContentsToFile(f, 55 use_include_tags_for_scripts, 56 dir_for_include_tag_root) 57 for inline_script in self._parser_results.inline_scripts: 58 if not HasPolymerCall(inline_script.stripped_contents): 59 js = inline_script.contents 60 else: 61 js = GetInlineScriptContentWithPolymerizingApplied(inline_script) 62 63 js = js_utils.EscapeJSIfNeeded(js) 64 65 f.write(js) 66 f.write('\n') 67 68 def AppendHTMLContentsToFile(self, f, ctl, minify=False): 69 super(HTMLModule, self).AppendHTMLContentsToFile(f, ctl) 70 71 ctl.current_module = self 72 try: 73 for piece in self._parser_results.YieldHTMLInPieces(ctl, minify=minify): 74 f.write(piece) 75 finally: 76 ctl.current_module = None 77 78 def HRefToResource(self, href, tag_for_err_msg): 79 return _HRefToResource(self.loader, self.name, self._module_dir_name, 80 href, tag_for_err_msg) 81 82 def AppendDirectlyDependentFilenamesTo( 83 self, dependent_filenames, include_raw_scripts=True): 84 super(HTMLModule, self).AppendDirectlyDependentFilenamesTo( 85 dependent_filenames, include_raw_scripts) 86 for contents in self._parser_results.inline_stylesheets: 87 module_dirname = os.path.dirname(self.resource.absolute_path) 88 ss = style_sheet.ParsedStyleSheet( 89 self.loader, module_dirname, contents) 90 ss.AppendDirectlyDependentFilenamesTo(dependent_filenames) 91 92 93def GetInlineScriptContentWithPolymerizingApplied(inline_script): 94 polymer_element_name = GetPolymerElementNameFromOpenTags( 95 inline_script.open_tags) 96 if polymer_element_name is None: 97 raise module.DepsException( 98 'Tagless Polymer() call must be made inside a <polymer-element> tag') 99 100 return UpdatePolymerCallsGivenElementName( 101 inline_script.stripped_contents, polymer_element_name) 102 103 104def GetPolymerElementNameFromOpenTags(open_tags): 105 found_tag = None 106 for tag in reversed(open_tags): 107 if tag.tag == 'polymer-element': 108 found_tag = tag 109 break 110 111 if not found_tag: 112 return None 113 114 for attr in found_tag.attrs: 115 if attr[0] == 'name': 116 return attr[1] 117 118 return None 119 120_POLYMER_RE_1 = 'Polymer(\s*?)\((\s*?)\{' 121_POLYMER_RE_2 = 'Polymer(\s*?)\((\s*?)\)' 122 123 124def HasPolymerCall(js): 125 if re.search(_POLYMER_RE_1, js) is not None: 126 return True 127 if re.search(_POLYMER_RE_2, js) is not None: 128 return True 129 return False 130 131 132def UpdatePolymerCallsGivenElementName(js, polymer_element_name): 133 if re.search(_POLYMER_RE_1, js) is not None: 134 return re.sub(_POLYMER_RE_1, 135 'Polymer\g<1>(\g<2>\'%s\', {' % polymer_element_name, 136 js, 0, re.DOTALL) 137 if re.search(_POLYMER_RE_2, js) is not None: 138 return re.sub(_POLYMER_RE_2, 139 'Polymer\g<1>(\g<2>\'%s\')' % polymer_element_name, 140 js, 0, re.DOTALL) 141 assert False, 'This should never be reached' 142 143 144def _HRefToResource( 145 loader, module_name, module_dir_name, href, tag_for_err_msg): 146 if href[0] == '/': 147 resource = loader.FindResourceGivenRelativePath( 148 os.path.normpath(href[1:])) 149 else: 150 abspath = os.path.normpath(os.path.join(module_dir_name, 151 os.path.normpath(href))) 152 resource = loader.FindResourceGivenAbsolutePath(abspath) 153 154 if not resource: 155 raise module.DepsException( 156 'In %s, the %s cannot be loaded because ' 157 'it is not in the search path' % (module_name, tag_for_err_msg)) 158 try: 159 resource.contents 160 except: 161 raise module.DepsException('In %s, %s points at a nonexistent file ' % ( 162 module_name, tag_for_err_msg)) 163 return resource 164 165 166def Parse(loader, module_name, module_dir_name, is_component, parser_results): 167 res = module.ModuleDependencyMetadata() 168 if is_component: 169 return res 170 171 if parser_results.has_decl is False: 172 raise Exception('%s must have <!DOCTYPE html>' % module_name) 173 174 # External script references.. 175 for href in parser_results.scripts_external: 176 resource = _HRefToResource(loader, module_name, module_dir_name, 177 href, 178 tag_for_err_msg='<script src="%s">' % href) 179 res.dependent_raw_script_relative_paths.append( 180 resource.unix_style_relative_path) 181 182 # External imports. Mostly the same as <script>, but we know its a module. 183 for href in parser_results.imports: 184 if not href.endswith('.html'): 185 raise Exception( 186 'In %s, the <link rel="import" href="%s"> must point at a ' 187 'file with an html suffix' % (module_name, href)) 188 189 resource = _HRefToResource( 190 loader, module_name, module_dir_name, href, 191 tag_for_err_msg='<link rel="import" href="%s">' % href) 192 res.dependent_module_names.append(resource.name) 193 194 # Validate the in-line scripts. 195 for inline_script in parser_results.inline_scripts: 196 stripped_text = inline_script.stripped_contents 197 try: 198 js_utils.ValidateUsesStrictMode('_', stripped_text) 199 except: 200 raise Exception('%s has an inline script tag that is missing ' 201 'a \'use strict\' directive.' % module_name) 202 203 # Style sheets 204 for href in parser_results.stylesheets: 205 resource = _HRefToResource( 206 loader, module_name, module_dir_name, href, 207 tag_for_err_msg='<link rel="stylesheet" href="%s">' % href) 208 res.style_sheet_names.append(resource.name) 209 210 return res 211