[FIX] qweb: css minified in multiple page for IE

On internet explorer 6, 7, 8 and 9, the limit of CSS rules in a stylesheet is
4095 (http://blogs.msdn.com/b/ieinternals/archive/2011/05/14/10164546.aspx).

This commit breaks down a CSS bundle in several pages for these IE versions.

To do this, the CSS tag added is of the kind : /web/css.0/{xmlid}/{version} in
which there is:

- the whole CSS if there is no more than one page,
- a list of @import pointing to the multiple pages.

note: if a modification lowers the number of page, an old page may stay in
ir_attachment (e.g: go from 4 to 3 pages, the old 4th page of another version
will not be deleted untill the number goes again up to 4).

Note: the method css(self) previously returned an unicode variable (the first
time) or an str variable (the following times, if already cached), the fix
also correct this so an str variable is always returned.

fixes #5050

opw-627116
This commit is contained in:
Nicolas Lempereur 2015-04-02 00:55:26 +02:00 committed by Christophe Matthieu
parent b1dd5d6045
commit 37959d45f3
2 changed files with 49 additions and 7 deletions

View File

@ -534,14 +534,15 @@ class Home(http.Controller):
@http.route([
'/web/css/<xmlid>',
'/web/css/<xmlid>/<version>',
'/web/css.<int:page>/<xmlid>/<version>',
], type='http', auth='public')
def css_bundle(self, xmlid, version=None, **kw):
def css_bundle(self, xmlid, version=None, page=None, **kw):
try:
bundle = AssetsBundle(xmlid)
except QWebTemplateNotFound:
return request.not_found()
response = request.make_response(bundle.css(), [('Content-Type', 'text/css')])
response = request.make_response(bundle.css(page), [('Content-Type', 'text/css')])
return make_conditional(response, bundle.last_modified, max_age=BUNDLE_MAXAGE)
class WebClient(http.Controller):

View File

@ -33,6 +33,8 @@ from openerp.tools.translate import _
_logger = logging.getLogger(__name__)
MAX_CSS_RULES = 4095
#--------------------------------------------------------------------
# QWeb template engine
#--------------------------------------------------------------------
@ -1142,7 +1144,12 @@ class AssetsBundle(object):
else:
url_for = self.context.get('url_for', lambda url: url)
if css and self.stylesheets:
href = '/web/css/%s/%s' % (self.xmlid, self.version)
suffix = ''
if request:
ua = request.httprequest.user_agent
if ua.browser == "msie" and int((ua.version or '0').split('.')[0]) < 10:
suffix = '.0'
href = '/web/css%s/%s/%s' % (suffix, self.xmlid, self.version)
response.append('<link href="%s" rel="stylesheet"/>' % url_for(href))
if js:
src = '/web/js/%s/%s' % (self.xmlid, self.version)
@ -1178,7 +1185,10 @@ class AssetsBundle(object):
self.set_cache('js', content)
return content
def css(self):
def css(self, page_number=None):
if page_number is not None:
return self.css_page(page_number)
content = self.get_cache('css')
if content is None:
self.compile_sass()
@ -1198,12 +1208,43 @@ class AssetsBundle(object):
matches.append(content)
content = u'\n'.join(matches)
if self.css_errors:
return content
self.set_cache('css', content)
if not self.css_errors:
self.set_cache('css', content)
content = content.encode('utf-8')
return content
def css_page(self, page_number):
content = self.get_cache('css.%d' % (page_number,))
if page_number:
return content
if content is None:
css = self.css()
re_rules = '([^{]+\{(?:[^{}]|\{[^{}]*\})*\})'
re_selectors = '()(?:\s*@media\s*[^{]*\{)?(?:\s*(?:[^,{]*(?:,|\{(?:[^}]*\}))))'
css_url = '@import url(\'/web/css.%%d/%s/%s\');' % (self.xmlid, self.version)
pages = [[]]
page = pages[0]
page_selectors = 0
for rule in re.findall(re_rules, css):
selectors = len(re.findall(re_selectors, rule))
if page_selectors + selectors < MAX_CSS_RULES:
page_selectors += selectors
page.append(rule)
else:
pages.append([rule])
page = pages[-1]
page_selectors = selectors
if len(pages) == 1:
pages = []
for idx, page in enumerate(pages):
self.set_cache("css.%d" % (idx+1), ''.join(page))
content = '\n'.join(css_url % i for i in range(1,len(pages)+1))
self.set_cache("css.0", content)
if not content:
return self.css()
return content
def get_cache(self, type):
content = None
domain = [('url', '=', '/web/%s/%s/%s' % (type, self.xmlid, self.version))]