aboutsummaryrefslogtreecommitdiff
path: root/catapult/common/py_vulcanize/py_vulcanize/style_sheet.py
blob: 5338762588eb78cb29f2c3448c1295929befbe53 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# Copyright 2014 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

import base64
import os
import re


class Image(object):

  def __init__(self, resource):
    self.resource = resource
    self.aliases = []

  @property
  def relative_path(self):
    return self.resource.relative_path

  @property
  def absolute_path(self):
    return self.resource.absolute_path

  @property
  def contents(self):
    return self.resource.contents


class ParsedStyleSheet(object):

  def __init__(self, loader, containing_dirname, contents):
    self.loader = loader
    self.contents = contents
    self._images = None
    self._Load(containing_dirname)

  @property
  def images(self):
    return self._images

  def AppendDirectlyDependentFilenamesTo(self, dependent_filenames):
    for i in self.images:
      dependent_filenames.append(i.resource.absolute_path)

  @property
  def contents_with_inlined_images(self):
    images_by_url = {}
    for i in self.images:
      for a in i.aliases:
        images_by_url[a] = i

    def InlineUrl(m):
      url = m.group('url')
      image = images_by_url[url]

      ext = os.path.splitext(image.absolute_path)[1]
      data = base64.standard_b64encode(image.contents)

      return 'url(data:image/%s;base64,%s)' % (ext[1:], data)

    # I'm assuming we only have url()'s associated with images
    return re.sub('url\((?P<quote>"|\'|)(?P<url>[^"\'()]*)(?P=quote)\)',
                  InlineUrl, self.contents)

  def AppendDirectlyDependentFilenamesTo(self, dependent_filenames):
    for i in self.images:
      dependent_filenames.append(i.resource.absolute_path)

  def _Load(self, containing_dirname):
    if self.contents.find('@import') != -1:
      raise Exception('@imports are not supported')

    matches = re.findall(
        'url\((?:["|\']?)([^"\'()]*)(?:["|\']?)\)',
        self.contents)

    def resolve_url(url):
      if os.path.isabs(url):
        # FIXME: module is used here, but py_vulcanize.module is never imported.
        # However, py_vulcanize.module cannot be imported since py_vulcanize.module may import
        # style_sheet, leading to an import loop.
        raise module.DepsException('URL references must be relative')
      # URLS are relative to this module's directory
      abs_path = os.path.abspath(os.path.join(containing_dirname, url))
      image = self.loader.LoadImage(abs_path)
      image.aliases.append(url)
      return image

    self._images = [resolve_url(x) for x in matches]


class StyleSheet(object):
  """Represents a stylesheet resource referenced by a module via the
  base.requireStylesheet(xxx) directive."""

  def __init__(self, loader, name, resource):
    self.loader = loader
    self.name = name
    self.resource = resource
    self._parsed_style_sheet = None

  @property
  def filename(self):
    return self.resource.absolute_path

  @property
  def contents(self):
    return self.resource.contents

  def __repr__(self):
    return 'StyleSheet(%s)' % self.name

  @property
  def images(self):
    self._InitParsedStyleSheetIfNeeded()
    return self._parsed_style_sheet.images

  def AppendDirectlyDependentFilenamesTo(self, dependent_filenames):
    self._InitParsedStyleSheetIfNeeded()

    dependent_filenames.append(self.resource.absolute_path)
    self._parsed_style_sheet.AppendDirectlyDependentFilenamesTo(
        dependent_filenames)

  @property
  def contents_with_inlined_images(self):
    self._InitParsedStyleSheetIfNeeded()
    return self._parsed_style_sheet.contents_with_inlined_images

  def load(self):
    self._InitParsedStyleSheetIfNeeded()

  def _InitParsedStyleSheetIfNeeded(self):
    if self._parsed_style_sheet:
      return
    module_dirname = os.path.dirname(self.resource.absolute_path)
    self._parsed_style_sheet = ParsedStyleSheet(
        self.loader, module_dirname, self.contents)