Home | History | Annotate | Download | only in py_vulcanize
      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