[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([ @http.route([
'/web/css/<xmlid>', '/web/css/<xmlid>',
'/web/css/<xmlid>/<version>', '/web/css/<xmlid>/<version>',
'/web/css.<int:page>/<xmlid>/<version>',
], type='http', auth='public') ], type='http', auth='public')
def css_bundle(self, xmlid, version=None, **kw): def css_bundle(self, xmlid, version=None, page=None, **kw):
try: try:
bundle = AssetsBundle(xmlid) bundle = AssetsBundle(xmlid)
except QWebTemplateNotFound: except QWebTemplateNotFound:
return request.not_found() 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) return make_conditional(response, bundle.last_modified, max_age=BUNDLE_MAXAGE)
class WebClient(http.Controller): class WebClient(http.Controller):

View File

@ -33,6 +33,8 @@ from openerp.tools.translate import _
_logger = logging.getLogger(__name__) _logger = logging.getLogger(__name__)
MAX_CSS_RULES = 4095
#-------------------------------------------------------------------- #--------------------------------------------------------------------
# QWeb template engine # QWeb template engine
#-------------------------------------------------------------------- #--------------------------------------------------------------------
@ -1142,7 +1144,12 @@ class AssetsBundle(object):
else: else:
url_for = self.context.get('url_for', lambda url: url) url_for = self.context.get('url_for', lambda url: url)
if css and self.stylesheets: 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)) response.append('<link href="%s" rel="stylesheet"/>' % url_for(href))
if js: if js:
src = '/web/js/%s/%s' % (self.xmlid, self.version) src = '/web/js/%s/%s' % (self.xmlid, self.version)
@ -1178,7 +1185,10 @@ class AssetsBundle(object):
self.set_cache('js', content) self.set_cache('js', content)
return 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') content = self.get_cache('css')
if content is None: if content is None:
self.compile_sass() self.compile_sass()
@ -1198,12 +1208,43 @@ class AssetsBundle(object):
matches.append(content) matches.append(content)
content = u'\n'.join(matches) content = u'\n'.join(matches)
if self.css_errors: if not self.css_errors:
return content self.set_cache('css', content)
self.set_cache('css', content) content = content.encode('utf-8')
return content 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): def get_cache(self, type):
content = None content = None
domain = [('url', '=', '/web/%s/%s/%s' % (type, self.xmlid, self.version))] domain = [('url', '=', '/web/%s/%s/%s' % (type, self.xmlid, self.version))]