From 37959d45f3ed088a07fd507b678e5089b6e78537 Mon Sep 17 00:00:00 2001 From: Nicolas Lempereur Date: Thu, 2 Apr 2015 00:55:26 +0200 Subject: [PATCH] [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 --- addons/web/controllers/main.py | 5 +-- openerp/addons/base/ir/ir_qweb.py | 51 ++++++++++++++++++++++++++++--- 2 files changed, 49 insertions(+), 7 deletions(-) diff --git a/addons/web/controllers/main.py b/addons/web/controllers/main.py index 9819e5b895b..695b3f4034a 100644 --- a/addons/web/controllers/main.py +++ b/addons/web/controllers/main.py @@ -534,14 +534,15 @@ class Home(http.Controller): @http.route([ '/web/css/', '/web/css//', + '/web/css.//', ], 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): diff --git a/openerp/addons/base/ir/ir_qweb.py b/openerp/addons/base/ir/ir_qweb.py index 3c3fc70ab67..21b2b2dd476 100644 --- a/openerp/addons/base/ir/ir_qweb.py +++ b/openerp/addons/base/ir/ir_qweb.py @@ -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('' % 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))]