Changeset 9850

Show
Ignore:
Timestamp:
07/29/07 08:16:04 (18 months ago)
Author:
mumme
Message:

Cleaned up Regex/string munging for easier maintainence, be faster.
Add support for declaring media type on style/link nodes.
Add support for path adjustment on AlphaImageLoader? statements in html/css. (not external css files)
bugfix leave [script type=dojo/method] as is.

Added several regression tests for these and other features.
refs: #3594, refs #1441

Location:
dojox/trunk/layout
Files:
1 added
3 modified

Legend:

Unmodified
Added
Removed
  • dojox/trunk/layout/ContentPane.js

    r9803 r9850  
    22 
    33dojo.require("dijit.layout.ContentPane"); 
    4 dojo.require("dojox.string.Builder"); 
    54 
    65(function(){ // private scope, sort of a namespace 
    76 
    87        // TODO: should these methods be moved to dojox.html.cssPathAdjust or something? 
    9         function adjustCssPaths(cssUrl, cssText, imports){ 
    10                 //      summary 
    11                 //  say cssText comes from dojoroot/src/widget/templates/Foobar.css 
    12                 //      it has a css selector: .dojoFoo { background-image: url(images/bar.png);} 
    13                 // then uri should point to dojoroot/src/widget/templates/ 
     8 
     9        // css at-rules must be set before any css declarations according to CSS spec 
     10        // match: 
     11        // @import 'http://dojotoolkit.org/dojo.css'; 
     12        // @import 'you/never/thought/' print; 
     13        // @import url("it/would/work") tv, screen; 
     14        // @import url(/did/you/now.css); 
     15        // but not: 
     16        // @namespace dojo "http://dojotoolkit.org/dojo.css"; /* namespace URL should always be a absolute URI */ 
     17        // @charset 'utf-8'; 
     18        // @media print{ #menuRoot {display:none;} } 
     19 
     20                 
     21        // we adjust all paths that dont start on '/' or contains ':' 
     22        //(?![a-z]+:|\/) 
     23 
     24        if(dojo.isIE){ 
     25                var alphaImageLoader = /(AlphaImageLoader\([^)]*?src=(['"]))(?![a-z]+:|\/)([^\r\n;}]+?)(\2[^)]*\)\s*[;}]?)/g; 
     26        } 
     27 
     28        var cssPaths = /(?:(?:@import\s*(['"])(?![a-z]+:|\/)([^\r\n;{]+?)\1)|url\(\s*(['"]?)(?![a-z]+:|\/)([^\r\n;]+?)\3\s*\))([a-z, \s]*[;}]?)/g; 
     29 
     30        function adjustCssPaths(cssUrl, cssText){ 
     31                //      summary: 
     32                //              adjusts relative paths in cssText to be relative to cssUrl 
     33                //              a path is considered relative if it doesn't start with '/' and not contains ':' 
     34                //      description: 
     35                //              Say we fetch a HTML page from level1/page.html 
     36                //              It has some inline CSS: 
     37                //                      @import "css/page.css" tv, screen; 
     38                //                      ... 
     39                //                      background-image: url(images/aplhaimage.png); 
     40                // 
     41                //              as we fetched this HTML and therefore this CSS 
     42                //              from level1/page.html, these paths needs to be adjusted to: 
     43                //                      @import 'level1/css/page.css' tv, screen; 
     44                //                      ... 
     45                //                      background-image: url(level1/images/alphaimage.png); 
     46                //               
     47                //              In IE it will also adjust relative paths in AlphaImageLoader() 
     48                //                      filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='images/alphaimage.png'); 
     49                //              will be adjusted to: 
     50                //                      filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='level1/images/alphaimage.png'); 
     51                // 
     52                //              Please note that any relative paths in AlphaImageLoader in external css files wont work, as 
     53                //              the paths in AlphaImageLoader is MUST be declared relative to the HTML page, 
     54                //              not relative to the CSS file that declares it 
     55 
    1456                if(!cssText || !cssUrl){ return; } 
    1557 
    16                 var match, str = "", url = "", regex, pad = ""; 
    17                 var urlChrs = "[\\t\\s\\w\\(\\)\\/\\.\\\\'\"-:#=&?~]+"; 
    18                  
    19                 if(imports){ 
    20                         pad = " "; 
    21                         regex = new RegExp('(@import)(\\s+'+urlChrs+')\\s*(;)'); // TODO support media rules 
    22                 }else{ 
    23                         regex = new RegExp('(url\\()\\s*('+urlChrs+')\\s*(\\))'); 
    24                 } 
    25                 var regexProtocol = /https?:\/\//; 
    26                 var regexTrim = new RegExp("^[\\s]*(['\"]?)("+urlChrs+")\\1[\\s]*?$"); 
    27          
    28                 while(match = regex.exec(cssText)){ 
    29                         url = match[2].replace(regexTrim, "$2"); 
    30                         if(!regexProtocol.exec(url)){ 
    31                                 url = (new dojo._Url(cssUrl, url).toString()); 
    32                         } 
    33                         str += cssText.substring(0, match.index) + match[1] + pad + "'" + url + "'" + match[3]; 
    34                         cssText = cssText.substr(match.index + match[0].length); 
    35                 } 
    36  
    37                 str = str + cssText; 
    38  
    39                 if(!imports){ 
    40                         str = arguments.callee(cssUrl, str, true); 
    41                 } 
    42                 return str; // String 
    43         } 
     58                // support the ImageAlphaFilter if it exists, most people use it in IE 6 for transparent PNGs 
     59                // We are NOT going to kill it in IE 7 just because the PNGs work there. Somebody might have 
     60                // other uses for it. 
     61                // If user want to disable css filter in IE6  he/she should 
     62                // unset filter in a declaration that just IE 6 doesn't understands 
     63                // like * > .myselector { filter:none; } 
     64                if(alphaImageLoader){ 
     65                        cssText = cssText.replace(alphaImageLoader, function(ignore, pre, delim, url, post){ 
     66                                return pre + (new dojo._Url(cssUrl, './'+url).toString()) + post; 
     67                        }); 
     68                } 
     69 
     70                return cssText.replace(cssPaths, function(ignore, delimStr, strUrl, delimUrl, urlUrl, media){ 
     71                        if(strUrl){ 
     72                                return '@import "' + (new dojo._Url(cssUrl, './'+strUrl).toString()) + '"' + media; 
     73                        }else{ 
     74                                return 'url(' + (new dojo._Url(cssUrl, './'+urlUrl).toString()) + ')' + media; 
     75                        } 
     76                }); 
     77        } 
     78 
     79        // attributepaths one tag can have multiple paths, example: 
     80        // <input src="..." style="url(..)"/> or <a style="url(..)" href=".."> 
     81        // <img style='filter:progid...AlphaImageLoader(src="noticeTheSrcHereRunsThroughHtmlSrc")' src="img"> 
     82        var htmlAttrPaths = /(<[a-z][a-z0-9]*\s[^>]*)(?:(href|src)=(['"]?)([^>]*?)\3|style=(['"]?)([^>]*?)\5)([^>]*>)/gi; 
    4483 
    4584        function adjustHtmlPaths(htmlUrl, cont){ 
    46                 // TODO: clean up this mess! 
    47                 // use dojox string builder to achive more speed on slow string merging env. 
    48  
    49                 // attributepaths one tag can have multiple paths example: 
    50                 // <input src="..." style="url(..)"/> or <a style="url(..)" href=".."> 
    51                 // strip out the tag and run fix on that. 
    52                 // this guarantees that we won't run replace on another tag's attribute + it was easier do 
    53                 var regexFindTag = /<[a-z][a-z0-9]*[^>]*\s(?:(?:src|href|style)=[^>])+[^>]*>/i; 
    54                 // FIXME: get the url regex part from dojo.regex instead 
    55                 var regexFindAttr = /\s(src|href|style)=(['"]?)([\w()\[\]\/.,\\'"-:;#=&?\s@]+?)\2/i; 
    56                 // these are the supported protocols, all other is considered relative 
    57                 var regexProtocols = /^(?:[#]|(?:(?:https?|ftps?|file|javascript|mailto|news):))/; 
    5885                var url = htmlUrl || "./"; 
    5986 
    60                 var str = "", tag, tagFix = '', attr, path, origPath; 
    61  
    62                 while(tag = regexFindTag.exec(cont)){ 
    63                         str += cont.substring(0, tag.index); 
    64                         cont = cont.substring((tag.index + tag[0].length), cont.length); 
    65                         tag = tag[0]; 
    66  
    67                         // loop through attributes 
    68                         tagFix = ''; 
    69                         while(attr = regexFindAttr.exec(tag)){ 
    70                                 path = ""; origPath = attr[3]; 
    71                                 switch(attr[1].toLowerCase()){ 
    72                                         case "src":// falltrough 
    73                                         case "href": 
    74                                                 if(regexProtocols.exec(origPath)){ 
    75                                                         path = origPath; 
    76                                                 } else { 
    77                                                         path = (new dojo._Url(url, origPath).toString()); 
    78                                                 } 
    79                                                 break; 
    80                                         case "style":// style 
    81                                                 path = adjustCssPaths(url, origPath); 
    82                                                 attr[2] = '"'; 
    83                                                 break; 
    84                                         default: 
    85                                                 path = origPath; 
    86                                 } 
    87                                 fix = " " + attr[1] + '=' + attr[2] + path + attr[2]; 
    88                                 // slices up tag before next attribute check 
    89                                 tagFix += tag.substring(0, attr.index) + fix; 
    90                                 tag = tag.substring((attr.index + attr[0].length), tag.length); 
    91                         } 
    92                         str += tagFix + tag;  
    93                         //console.debug(tagFix + tag); 
    94                 } 
    95                 return str+cont; 
     87                return cont.replace(htmlAttrPaths, 
     88                        function(tag, start, name, delim, relUrl, delim2, cssText, end){ 
     89                                return start + (name ? 
     90                                                        (name + '=' + delim + (new dojo._Url(url, relUrl).toString()) + delim) 
     91                                                : ('style=' + delim2 + adjustCssPaths(url, cssText) + delim2) 
     92                                ) + end; 
     93                        } 
     94                ); 
    9695        } 
    9796 
    9897        function secureForInnerHtml(cont){ 
    99                 /********* remove <!DOCTYPE.. tag **********/ 
    100                 cont = cont.replace(/$\s*<!DOCTYPE\s[^>]+>/i, ""); 
    101  
    102                 /************** <title> ***********/ 
     98                /********* remove <!DOCTYPE.. and <title>..</title> tag **********/ 
    10399                // khtml is picky about dom faults, you can't attach a <style> or <title> node as child of body 
    104100                // must go into head, so we need to cut out those tags 
    105                 var regex = /<title[^>]*>([\s\S]*?)<\/title>/i; 
    106                 while(match = regex.exec(cont)){ 
    107                         cont = cont.substring(0, match.index) + s.substr(match.index + match[0].length); 
    108                 } 
    109  
    110                 return cont; 
     101                return cont.replace(/(?:\s*<!DOCTYPE\s[^>]+>|<title[^>]*>[\s\S]*?<\/title>)/ig, ""); 
    111102        } 
    112103 
    113104        function snarfStyles(/*String*/cssUrl, /*String*/cont, /*Array*/styles){ 
    114105                /****************  cut out all <style> and <link rel="stylesheet" href=".."> **************/ 
    115                 var regex = /(?:<(style)[^>]*>([\s\S]*?)<\/style>|<link ([^>]*rel=['"]?stylesheet['"]?[^>]*)>)/i; 
    116                 var match, attr; 
    117                 while(match = regex.exec(cont)){ 
    118                         if(match[1] && match[1].toLowerCase() == "style"){ 
    119                                 styles.push(adjustCssPaths(cssUrl, match[2])); 
    120                         }else if(attr = match[3].match(/href=(['"]?)([^'">]*)\1/i)){ 
    121                                 styles.push("@import '" + attr[2] + "';"); 
    122                         } 
    123                         cont = cont.substring(0, match.index) + cont.substr(match.index + match[0].length); 
    124                 }; 
    125                 return cont; 
     106                // also return any attributes from this tag (might be a media attribute) 
     107                // if cssUrl is set it will adjust paths accordingly 
     108                styles.attributes = []; 
     109 
     110                return cont.replace(/(?:<style([^>]*)>([\s\S]*?)<\/style>|<link\s+(?=[^>]*rel=['"]?stylesheet)([^>]*?href=(['"])([^>]*?)\4[^>\/]*)\/?>)/gi, 
     111                        function(ignore, styleAttr, cssText, linkAttr, delim, href){ 
     112                                // trim attribute 
     113                                var i, attr = (styleAttr||linkAttr||"").replace(/^\s*([\s\S]*?)\s*$/i, "$1");  
     114                                if(cssText){ 
     115                                        i = styles.push(cssUrl ? adjustCssPaths(cssUrl, cssText) : cssText); 
     116                                }else{ 
     117                                        i = styles.push('@import "' + href + '";') 
     118                                        attr = attr.replace(/\s*(?:rel|href)=(['"])?[^\s]*\1\s*/gi, ""); // remove rel=... and href=... 
     119                                } 
     120                                if(attr){ 
     121                                        attr = attr.split(/\s+/);// split on both "\n", "\t", " " etc 
     122                                        var atObj = {}, tmp; 
     123                                        for(var j = 0, e = attr.length; j < e; j++){ 
     124                                                tmp = attr[j].split('=')// split name='value' 
     125                                                atObj[tmp[0]] = tmp[1].replace(/^\s*['"]?([\s\S]*?)['"]?\s*$/, "$1"); // trim and remove '' 
     126                                        } 
     127                                        styles.attributes[i - 1] = atObj; 
     128                                } 
     129                                return ""; // squelsh the <style> or <link> 
     130                        } 
     131                ); 
    126132        } 
    127133 
     
    131137                // invoke with  
    132138                //      byRef = {errBack:function(){/*add your download error code here*/, downloadRemote: true(default false)}} 
    133                 //      byRef will have {code:} 
     139                //      byRef will have {code: 'jscode'} when this scope leaves 
    134140                byRef.code = ""; 
    135                 var regex = /<script([^>]*)>([\s\S]*?)<\/script>/i; 
    136                 var regexSrc = /src=(['"])([^'"]*)\1/, regexExcl = /type=['"]dojo\/method/i; 
    137                 var tag, match, attr, src, s = ""; 
    138                 while(tag = cont.match(regex)){ 
    139                         s = cont.substring(0, tag.index); 
    140                         cont = cont.substr(tag.index + tag[0].length); 
    141  
    142                         if(byRef.downloadRemote && tag[1].length 
    143                                 && (src = tag[1].match(regexSrc)) 
    144                         ){ 
     141 
     142                function download(src){ 
     143                        if(byRef.downloadRemote){ 
     144                                // console.debug('downloading',src); 
    145145                                dojo.xhrGet({ 
    146                                         url: src[2], 
     146                                        url: src, 
    147147                                        sync: true, 
    148148                                        load: function(code){ 
     
    151151                                        error: byRef.errBack 
    152152                                }); 
    153                         }else if(!regexExcl.test(tag[1])){ 
    154                                 byRef.code += tag[2] + ";"; 
    155                         } 
    156                 } 
    157  
    158                 return s + cont; // String 
     153                        } 
     154                } 
     155                 
     156                // match <script>, <script type="text/..., but not <script type="dojo(/method)... 
     157                return cont.replace(/<script\s*(?![^>]*type=['"]?dojo)(?:[^>]*?src=(['"]?)([^>]*?)\1[^>]*)?>([\s\S]*?)<\/script>/gi, 
     158                        function(ignore, delim, src, code){ 
     159                                if(src){ 
     160                                        download(src); 
     161                                }else{ 
     162                                        byRef.code += code; 
     163                                } 
     164                                return ""; 
     165                        } 
     166                ); 
    159167        } 
    160168 
     
    196204        // summary: 
    197205        //              An extended version of dijit.layout.ContentPane 
    198         //              Supports inline scrips, relative path adjustments, 
    199         //              Java function content generation 
     206        //              Supports infile scrips and external ones declared by <script src='' 
     207        //              relative path adjustments (content fetched from a different folder) 
     208        //              <style> and <link rel='stylesheet' href='..'> tags, 
     209        //              css paths inside cssText is adjusted (if you set adjustPaths = true) 
     210        // 
     211        //              NOTE that dojo.require in script in the fetched file isn't recommended 
     212        //              Many widgets need to be required at page load to work properly 
    200213 
    201214        // adjustPaths: Boolean 
     
    227240        // scriptHasHooks: Boolean 
    228241        //              replace keyword '_container_' in scripts with 'dijit.byId(this.id)' 
     242        // NOTE this name might change in the near future 
    229243        scriptHasHooks: false, 
    230244 
     
    353367                // override dijit.layout.ContentPane._setContent, to enable path adjustments 
    354368                var styles = [];// init vars 
    355  
    356369                if(dojo.isString(cont)){ 
    357                         var url = this.href || './'; 
    358370                        if(this.adjustPaths && this.href){ 
    359                                 cont = adjustHtmlPaths(url, cont); 
     371                                cont = adjustHtmlPaths(this.href, cont); 
    360372                        } 
    361373                        if(this.cleanContent){ 
     
    363375                        } 
    364376                        if(this.renderStyles || this.cleanContent){ 
    365                                 cont = snarfStyles(url, cont, styles); 
     377                                cont = snarfStyles(this.href, cont, styles); 
    366378                        } 
    367379 
     
    439451                // insert css from content into document head 
    440452                this._styleNodes = []; 
    441                 var doc = this.domNode.ownerDocument; 
     453                var st, att, cssText, doc = this.domNode.ownerDocument; 
    442454                var head = doc.getElementsByTagName('head')[0]; 
    443455 
    444                 dojo.forEach(styles, function(cssText){ 
    445                         var st = doc.createElement('style'); 
    446                         st.setAttribute("type", "text/css"); 
     456                for(var i = 0, e = styles.length; i < e; i++){ 
     457                        cssText = styles[i]; att = styles.attributes[i]; 
     458                        st = doc.createElement('style'); 
     459                        st.setAttribute("type", "text/css"); // this is required in CSS spec! 
     460 
     461                        for(var x in att){ 
     462                                st.setAttribute(x, att[x]) 
     463                        } 
     464                         
    447465                        this._styleNodes.push(st); 
    448466                        head.appendChild(st); // must insert into DOM before setting cssText 
     
    453471                                st.appendChild(doc.createTextNode(cssText)); 
    454472                        } 
    455                 }, this); 
     473                } 
    456474        } 
    457475}); 
  • dojox/trunk/layout/tests/ContentPane.html

    r9803 r9850  
    44<head> 
    55        <title>dojox.layout.ContentPane test</title> 
     6        <script > 
     7        function fixPngIE6(){ 
     8                if(this.complete && dojo.isIE < 7){ 
     9                        var r = this.runtimeStyle; 
     10                        if(/.png$/i.test(this.src)){ 
     11                                r.height = this.height; 
     12                                r.width = this.width; 
     13                                r.filter="progid:DXImageTransform.Microsoft.AlphaImageLoader(src='"+this.src+"');"; 
     14                                this.src = this.currentStyle.backgroundImage.replace(/url\(\s*['"]?(.+?)['"]?\s*\)/, "$1"); 
     15                        } 
     16                        this.className = this.className.replace('run_png_fix', ""); 
     17                        r.behaviour = 'none'; 
     18                } 
     19        } 
     20        </script> 
     21        <style type='text/css'> 
     22                .run_png_fix { 
     23                        background-image:url(images/blank.gif); 
     24                        behaviour: expression(fixPngIE6.call(this)); 
     25                } 
     26        </style> 
    627        <script src='../../../dojo/dojo.js' djConfig='isDebug:true, parseOnLoad:true'></script> 
    728        <script> 
     
    4162                                t.assertTrue(dojo.isFunction(handle.addOnUnload)); 
    4263                        } 
     64 
    4365 
    4466                        doh.register("basicChecks", [ 
     
    330352                                                runTest: function(t){ 
    331353 
    332                                                         // TODO: add support for media rules to @import 
    333  
    334354                                                        // we do this test as one big string to emulate as good as possible,  
    335355                                                        // but split it later to easily see where we failed 
    336356                                                        var cssText = ".easy{ background-image:url(images/image.png) }\n" 
    337357                                                        +".dontReplaceEasy{ background-image:url(images/images/image.png) }\n" 
    338                                                         +".hardurl{background-image:url(\t \"../../source/test/%20t'e(s)t.gif(\"1')?foo=bar11103&bar=baz-foo\"  \t);}body{};\n" 
     358                                                        +".hardurl{background-image:url(\t \"../../source/~test/%20t'e(s)t.gif(\"1')?foo=bar11103&bar=baz-foo\"  \t);}body{};\n" 
    339359                                                        +".file{background-image: url(file:///home/nobody/image.png);}\n" 
    340360                                                        +".http{background-image: url(http://dojotoolkit.org/image.png);}\n" 
    341361                                                        +".https{background-image: url(https://dojotoolkit.org/image.png);}\n" 
    342362                                                        +".nonRelative{background-image:url(/toplevelfolder/image.gif);}\n" 
    343                                                         +'@import "css/main.css";' + "@import \t'css/Newbee Url.css'\t;"; 
     363                                                        +'@import "css/main.css";' + "\n@import \t'css/Newbee Url.css'\t;\n" 
     364                                                        +"@import 'http://dojotoolkit.org/dojo.css';\n" 
     365                                                        +"  @import 'you/never/thought/' print;\n" 
     366                                                        +' @import url("it/would/work") tv, screen;'+"\n" 
     367                                                        +' @import url(/did/you/now.css);'+"\n" 
     368                                                        +' @import "yes.i.did";'; 
    344369 
    345370                                                        pane1.href = "deep/nested/file"; 
     
    350375                                                        // hijack internals to snatch the styles before they are inserted to DOM (DOM messes formating) 
    351376                                                        var oldFunc = pane1._renderStyles; 
    352                                                         pane1._renderStyles = (function(){ 
    353                                                                 return function(styles){ 
    354                                                                         adjustedCss = styles.join(); 
    355                                                                 } 
    356                                                         }).call(pane1); 
    357                                                         pane1._setContent('<style>'+cssText+'</style>'); 
     377                                                        return function(styles){ 
     378                                                                adjustedCss = styles.join(); 
     379                                                        } 
     380                                                        pane1._setContent.call(pane1, '<style>'+cssText+'</style>'); 
    358381                                                        pane1._renderStyles = oldFunc; 
    359382 
    360383                                                        adjustedCss = adjustedCss.split("\n"); 
    361384 
    362                                                         var expectedCss = (".easy{ background-image:url('deep/nested/images/image.png') }\n" 
    363                                                         +".dontReplaceEasy{ background-image:url('deep/nested/images/images/image.png') }\n" 
    364                                                         +".hardurl{background-image:url('source/test/%20t'e(s)t.gif(\"1')?foo=bar11103&bar=baz-foo');}body{};\n" 
    365                                                         +".file{background-image: url('file:///home/nobody/image.png');}\n" 
    366                                                         +".http{background-image: url('http://dojotoolkit.org/image.png');}\n" 
    367                                                         +".https{background-image: url('https://dojotoolkit.org/image.png');}\n" 
    368                                                         +".nonRelative{background-image:url('/toplevelfolder/image.gif');}\n" 
    369                                                         +"@import 'deep/nested/css/main.css';@import 'deep/nested/css/Newbee Url.css';").split("\n"); 
     385                                                        var expectedCss = (".easy{ background-image:url(deep/nested/images/image.png) }\n" 
     386                                                        +".dontReplaceEasy{ background-image:url(deep/nested/images/images/image.png) }\n" 
     387                                                        +".hardurl{background-image:url(source/~test/%20t'e(s)t.gif(\"1')?foo=bar11103&bar=baz-foo);}body{};\n" 
     388                                                        +".file{background-image: url(file:///home/nobody/image.png);}\n" 
     389                                                        +".http{background-image: url(http://dojotoolkit.org/image.png);}\n" 
     390                                                        +".https{background-image: url(https://dojotoolkit.org/image.png);}\n" 
     391                                                        +".nonRelative{background-image:url(/toplevelfolder/image.gif);}\n" 
     392                                                        +"@import \"deep/nested/css/main.css\";\n@import \"deep/nested/css/Newbee Url.css\"\t;\n" 
     393                                                        +"@import 'http://dojotoolkit.org/dojo.css';\n" 
     394                                                        +"  @import \"deep/nested/you/never/thought/\" print;\n" 
     395                                                        +' @import url(deep/nested/it/would/work) tv, screen;'+"\n" 
     396                                                        +' @import url(/did/you/now.css);'+"\n" 
     397                                                        +' @import "deep/nested/yes.i.did";').split("\n"); 
    370398 
    371399                                                        // we split and loop to get a faster hint of where it failed 
     
    383411                                                timeout: 1800, 
    384412                                                runTest: function(t){ 
     413 
    385414                                                        var d = new t.Deferred(); 
    386415                                                        setTimeout(d.getTestCallback( 
     
    415444                                                                        t.assertEqual(110, mb.w); // 100px + 1px border + 4px margin = 110px 
    416445                                                                        t.assertEqual(110, mb.h); 
     446 
     447                                                                        // make sure we didn't render the <link media='print' rel='stylesheet' 
     448                                                                        var mb = dojo.marginBox(dojo.byId('linkMediaTest')); 
     449                                                                        t.assertEqual(212, mb.w); // 100px  + 2px border + 4px margin = 112px 
     450                                                                        t.assertEqual(212, mb.h); 
     451 
     452                                                                        // make sure we didn't render the <style media='print'>@import '...'; 
     453                                                                        mb = dojo.marginBox(dojo.byId('importMediaTest')); 
     454                                                                        t.assertEqual(210, mb.w); // 100px + 1px border + 4px margin = 110px 
     455                                                                        t.assertEqual(210, mb.h); 
    417456                                                                } 
    418457                                                        ), 1500); 
     
    459498                                ] 
    460499                        ); 
    461