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