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 5 import base64 6 import os 7 import re 8 9 10 class Image(object): 11 12 def __init__(self, resource): 13 self.resource = resource 14 self.aliases = [] 15 16 @property 17 def relative_path(self): 18 return self.resource.relative_path 19 20 @property 21 def absolute_path(self): 22 return self.resource.absolute_path 23 24 @property 25 def contents(self): 26 return self.resource.contents 27 28 29 class ParsedStyleSheet(object): 30 31 def __init__(self, loader, containing_dirname, contents): 32 self.loader = loader 33 self.contents = contents 34 self._images = None 35 self._Load(containing_dirname) 36 37 @property 38 def images(self): 39 return self._images 40 41 def AppendDirectlyDependentFilenamesTo(self, dependent_filenames): 42 for i in self.images: 43 dependent_filenames.append(i.resource.absolute_path) 44 45 @property 46 def contents_with_inlined_images(self): 47 images_by_url = {} 48 for i in self.images: 49 for a in i.aliases: 50 images_by_url[a] = i 51 52 def InlineUrl(m): 53 url = m.group('url') 54 image = images_by_url[url] 55 56 ext = os.path.splitext(image.absolute_path)[1] 57 data = base64.standard_b64encode(image.contents) 58 59 return 'url(data:image/%s;base64,%s)' % (ext[1:], data) 60 61 # I'm assuming we only have url()'s associated with images 62 return re.sub('url\((?P<quote>"|\'|)(?P<url>[^"\'()]*)(?P=quote)\)', 63 InlineUrl, self.contents) 64 65 def AppendDirectlyDependentFilenamesTo(self, dependent_filenames): 66 for i in self.images: 67 dependent_filenames.append(i.resource.absolute_path) 68 69 def _Load(self, containing_dirname): 70 if self.contents.find('@import') != -1: 71 raise Exception('@imports are not supported') 72 73 matches = re.findall( 74 'url\((?:["|\']?)([^"\'()]*)(?:["|\']?)\)', 75 self.contents) 76 77 def resolve_url(url): 78 if os.path.isabs(url): 79 # FIXME: module is used here, but py_vulcanize.module is never imported. 80 # However, py_vulcanize.module cannot be imported since py_vulcanize.module may import 81 # style_sheet, leading to an import loop. 82 raise module.DepsException('URL references must be relative') 83 # URLS are relative to this module's directory 84 abs_path = os.path.abspath(os.path.join(containing_dirname, url)) 85 image = self.loader.LoadImage(abs_path) 86 image.aliases.append(url) 87 return image 88 89 self._images = [resolve_url(x) for x in matches] 90 91 92 class StyleSheet(object): 93 """Represents a stylesheet resource referenced by a module via the 94 base.requireStylesheet(xxx) directive.""" 95 96 def __init__(self, loader, name, resource): 97 self.loader = loader 98 self.name = name 99 self.resource = resource 100 self._parsed_style_sheet = None 101 102 @property 103 def filename(self): 104 return self.resource.absolute_path 105 106 @property 107 def contents(self): 108 return self.resource.contents 109 110 def __repr__(self): 111 return 'StyleSheet(%s)' % self.name 112 113 @property 114 def images(self): 115 self._InitParsedStyleSheetIfNeeded() 116 return self._parsed_style_sheet.images 117 118 def AppendDirectlyDependentFilenamesTo(self, dependent_filenames): 119 self._InitParsedStyleSheetIfNeeded() 120 121 dependent_filenames.append(self.resource.absolute_path) 122 self._parsed_style_sheet.AppendDirectlyDependentFilenamesTo( 123 dependent_filenames) 124 125 @property 126 def contents_with_inlined_images(self): 127 self._InitParsedStyleSheetIfNeeded() 128 return self._parsed_style_sheet.contents_with_inlined_images 129 130 def load(self): 131 self._InitParsedStyleSheetIfNeeded() 132 133 def _InitParsedStyleSheetIfNeeded(self): 134 if self._parsed_style_sheet: 135 return 136 module_dirname = os.path.dirname(self.resource.absolute_path) 137 self._parsed_style_sheet = ParsedStyleSheet( 138 self.loader, module_dirname, self.contents) 139