Skip to content

Commit

Permalink
Wrap processed stylesheets (close #626) (#627)
Browse files Browse the repository at this point in the history
  • Loading branch information
LavrovArtem authored and churkin committed Jun 9, 2016
1 parent 946539b commit 29613b0
Show file tree
Hide file tree
Showing 9 changed files with 77 additions and 51 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "testcafe-hammerhead",
"description": "A powerful web-proxy used as a core for the TestCafe testing framework (https://github.com/DevExpress/testcafe).",
"version": "9.1.0",
"version": "9.2.0",
"homepage": "https://github.com/DevExpress/testcafe-hammerhead",
"bugs": {
"url": "https://github.com/DevExpress/testcafe-hammerhead/issues"
Expand Down
10 changes: 10 additions & 0 deletions src/client/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import * as styleUtils from './utils/style';
import trim from '../utils/string-trim';
import { getProxyUrl } from './utils/url';
import { processScript } from '../processing/script';
import { SCRIPT_PROCESSING_START_COMMENT, SCRIPT_PROCESSING_END_HEADER_COMMENT, SCRIPT_PROCESSING_END_COMMENT } from '../processing/script/header';
import { STYLESHEET_PROCESSING_START_COMMENT, STYLESHEET_PROCESSING_END_COMMENT } from '../processing/style';
import isJQueryObj from './utils/is-jquery-object';
import extend from './utils/extend';

Expand All @@ -42,6 +44,14 @@ class Hammerhead {
fetchSend: this.sandbox.fetch.FETCH_REQUEST_SEND_EVENT
};

this.PROCESSING_COMMENTS = {
stylesheetStart: STYLESHEET_PROCESSING_START_COMMENT,
stylesheetEnd: STYLESHEET_PROCESSING_END_COMMENT,
scriptStart: SCRIPT_PROCESSING_START_COMMENT,
scriptEndHeader: SCRIPT_PROCESSING_END_HEADER_COMMENT,
scriptEnd: SCRIPT_PROCESSING_END_COMMENT
};

this.EventEmitter = EventEmitter;

// Methods
Expand Down
23 changes: 16 additions & 7 deletions src/processing/script/header.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@ import reEscape from '../../utils/regexp-escape';
import INTERNAL_PROPS from '../../processing/dom/internal-properties';
import INSTRUCTION from './instruction';

const PREFIX = '/*hammerhead|script-processing-header|start*/';
const POSTFIX = '/*hammerhead|script-processing-header|end*/';

export const HEADER = [
PREFIX,
export const SCRIPT_PROCESSING_START_COMMENT = '/*hammerhead|script|start*/';
export const SCRIPT_PROCESSING_END_COMMENT = '/*hammerhead|script|end*/';
export const SCRIPT_PROCESSING_END_HEADER_COMMENT = '/*hammerhead|script|processing-header-end*/';

const HEADER = [
SCRIPT_PROCESSING_START_COMMENT,
'var __w$= typeof window!=="undefined"&&window;',
`__w$ && __w$["${INTERNAL_PROPS.processDomMethodName}"] && __w$["${INTERNAL_PROPS.processDomMethodName}"]();`,
`var ${ INSTRUCTION.getLocation }=__w$?__w$.${ INSTRUCTION.getLocation }:function(l){return l},`,
Expand All @@ -21,14 +23,21 @@ export const HEADER = [
`${ INSTRUCTION.callMethod }=__w$?__w$.${ INSTRUCTION.callMethod }:function(o,p,a){return o[p].apply(o,a)},`,
`${ INSTRUCTION.getEval }=__w$?__w$.${ INSTRUCTION.getEval }:function(e){return e},`,
`${ INSTRUCTION.processScript }=__w$?__w$.${ INSTRUCTION.processScript }:function(s){return s};`,
POSTFIX,
SCRIPT_PROCESSING_END_HEADER_COMMENT,
'\n'
].join('');

// NOTE: IE removes trailing newlines in script.textContent,
// so a trailing newline in RegExp is optional
const HEADER_RE = new RegExp(`${reEscape(PREFIX)}[\\S\\s]+?${reEscape(POSTFIX)}\n?`, 'i');
const HEADER_RE = new RegExp(`${reEscape(SCRIPT_PROCESSING_START_COMMENT)}[\\S\\s]+?${reEscape(SCRIPT_PROCESSING_END_HEADER_COMMENT)}\n?`, 'i');
const PROCESSING_END_COMMENT_RE = new RegExp(`\n?${ reEscape(SCRIPT_PROCESSING_END_COMMENT) }\\s*$`, 'gi');

export function remove (code) {
return code.replace(HEADER_RE, '');
return code
.replace(HEADER_RE, '')
.replace(PROCESSING_END_COMMENT_RE, '');
}

export function add (code) {
return HEADER + code + '\n' + SCRIPT_PROCESSING_END_COMMENT;
}
4 changes: 2 additions & 2 deletions src/processing/script/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

import transform from './transform';
import INSTRUCTION from './instruction';
import { HEADER, remove as removeHeader } from './header';
import { add as addHeader, remove as removeHeader } from './header';
import { parse } from './tools/acorn';
import { generate, Syntax } from './tools/esotope';
import reEscape from '../../utils/regexp-escape';
Expand Down Expand Up @@ -50,7 +50,7 @@ function preprocess (code) {

function postprocess (processed, withHeader, bom) {
if (withHeader)
processed = HEADER + processed;
processed = addHeader(processed);

return bom ? bom + processed : processed;
}
Expand Down
58 changes: 31 additions & 27 deletions src/processing/style.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,51 +4,55 @@
// -------------------------------------------------------------
/* eslint hammerhead/proto-methods: 2 */

import reEscape from '../utils/regexp-escape';
import INTERNAL_ATTRS from '../processing/dom/internal-attributes';

const SOURCE_MAP_REG_EX = /#\s*sourceMappingURL\s*=\s*[^\s]+(\s|\*\/)/i;
const CSS_URL_PROPERTY_VALUE_PATTERN = /(url\s*\(\s*)(?:(')([^\s']*)(')|(")([^\s"]*)(")|([^\s\)]*))(\s*\))|(@import\s+)(?:(')([^\s']*)(')|(")([^\s"]*)("))/g;
const SOURCE_MAP_RE = /#\s*sourceMappingURL\s*=\s*[^\s]+(\s|\*\/)/i;
const CSS_URL_PROPERTY_VALUE_PATTERN = /(url\s*\(\s*)(?:(')([^\s']*)(')|(")([^\s"]*)(")|([^\s\)]*))(\s*\))|(@import\s+)(?:(')([^\s']*)(')|(")([^\s"]*)("))/g;
const STYLESHEET_PROCESSING_START_COMMENT = '/*hammerhead|stylesheet|start*/';
const STYLESHEET_PROCESSING_END_COMMENT = '/*hammerhead|stylesheet|end*/';
const HOVER_PSEUDO_CLASS_RE = /\s*:\s*hover(\W)/gi;
const PSEUDO_CLASS_RE = new RegExp(`\\[${ INTERNAL_ATTRS.hoverPseudoClass }\\](\\W)`, 'ig');
const IS_STYLE_SHEET_PROCESSED_RE = new RegExp(`^\\s*${ reEscape(STYLESHEET_PROCESSING_START_COMMENT) }`, 'gi');
const STYLESHEET_PROCESSING_COMMENTS_RE = new RegExp(`^\\s*${ reEscape(STYLESHEET_PROCESSING_START_COMMENT) }\n?|` +
`\n?${ reEscape(STYLESHEET_PROCESSING_END_COMMENT) }\\s*$`, 'gi');

class StyleProcessor {
constructor () {
this.IS_STYLESHEET_PROCESSED_COMMENT = '/* stylesheet processed via hammerhead */';
this.STYLESHEET_TEXT_START_COMMENT = STYLESHEET_PROCESSING_START_COMMENT;
this.STYLESHEET_TEXT_END_COMMENT = STYLESHEET_PROCESSING_END_COMMENT;
}

process (css, urlReplacer, isStylesheetTable) {
var isStyleSheetProcessingRegEx = new RegExp('^\\s*' +
this.IS_STYLESHEET_PROCESSED_COMMENT.replace(/\/|\*/g, '\\$&'));
var isStylesheetProcessed = isStyleSheetProcessingRegEx.test(css);
if (typeof css !== 'string' || IS_STYLE_SHEET_PROCESSED_RE.test(css))
return css;

if (typeof css === 'string' && !isStylesheetProcessed) {
var prefix = isStylesheetTable ? this.IS_STYLESHEET_PROCESSED_COMMENT + '\n' : '';
var prefix = isStylesheetTable ? STYLESHEET_PROCESSING_START_COMMENT + '\n' : '';
var postfix = isStylesheetTable ? '\n' + STYLESHEET_PROCESSING_END_COMMENT : '';

// NOTE: Replace the :hover pseudo-class.
css = css.replace(/\s*:\s*hover(\W)/gi, '[' + INTERNAL_ATTRS.hoverPseudoClass + ']$1');
// NOTE: Replace the :hover pseudo-class.
css = css.replace(HOVER_PSEUDO_CLASS_RE, '[' + INTERNAL_ATTRS.hoverPseudoClass + ']$1');

// NOTE: Remove the ‘source map’ directive.
css = css.replace(SOURCE_MAP_REG_EX, '$1');
// NOTE: Remove the ‘source map’ directive.
css = css.replace(SOURCE_MAP_RE, '$1');

// NOTE: Replace URLs in CSS rules with proxy URLs.
return prefix + this._replaceStylsheetUrls(css, urlReplacer);
}

return css;
// NOTE: Replace URLs in CSS rules with proxy URLs.
return prefix + this._replaceStylsheetUrls(css, urlReplacer) + postfix;
}

cleanUp (css, parseProxyUrl) {
if (typeof css === 'string') {
css = css
.replace(new RegExp('\\[' + INTERNAL_ATTRS.hoverPseudoClass + '\\](\\W)', 'ig'), ':hover$1')
.replace(new RegExp('^\\s*' + this.IS_STYLESHEET_PROCESSED_COMMENT.replace(/\/|\*/g, '\\$&') + '\n?'), '');
if (typeof css !== 'string')
return css;

return this._replaceStylsheetUrls(css, url => {
var parsedProxyUrl = parseProxyUrl(url);
css = css
.replace(PSEUDO_CLASS_RE, ':hover$1')
.replace(STYLESHEET_PROCESSING_COMMENTS_RE, '');

return parsedProxyUrl ? parsedProxyUrl.destUrl : url;
});
}
return this._replaceStylsheetUrls(css, url => {
var parsedProxyUrl = parseProxyUrl(url);

return css;
return parsedProxyUrl ? parsedProxyUrl.destUrl : url;
});
}

_replaceStylsheetUrls (css, processor) {
Expand Down
5 changes: 3 additions & 2 deletions test/client/fixtures/sandbox/node/document-write-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -141,8 +141,9 @@ test('write style', function () {

strictEqual(getElems(iframeForWrite, 'style').length, getElems(iframeForNativeWrite, 'style').length);
strictEqual(innerHTML(getElems(iframeForWrite, 'style')[0], true), innerHTML(getElems(iframeForNativeWrite, 'style')[0]));
strictEqual(innerHTML(getElems(iframeForWrite, 'style')[0]), styleProcessor.IS_STYLESHEET_PROCESSED_COMMENT +
'\n\ndiv {}\n');
strictEqual(innerHTML(getElems(iframeForWrite, 'style')[0]), styleProcessor.STYLESHEET_TEXT_START_COMMENT +
'\n\ndiv {}\n\n' +
styleProcessor.STYLESHEET_TEXT_END_COMMENT);
strictEqual(innerHTML(getElems(iframeForWrite, 'style')[1], true), innerHTML(getElems(iframeForNativeWrite, 'style')[1]));
strictEqual(innerHTML(getElems(iframeForWrite, 'style')[1]), '');

Expand Down
4 changes: 2 additions & 2 deletions test/client/fixtures/sandbox/node/dom-processor-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -335,8 +335,8 @@ test('stylesheet after innerHTML', function () {
nativeMethods.appendChild.call(document.body, style);

var check = function (cssText) {
strictEqual(cssText.indexOf(styleProcessor.IS_STYLESHEET_PROCESSED_COMMENT), 0);
strictEqual(cssText.indexOf(styleProcessor.IS_STYLESHEET_PROCESSED_COMMENT, 1), -1);
strictEqual(cssText.indexOf(styleProcessor.STYLESHEET_TEXT_START_COMMENT), 0);
strictEqual(cssText.indexOf(styleProcessor.STYLESHEET_TEXT_START_COMMENT, 1), -1);
strictEqual(cssText.replace(/^[\s\S]+url\(([\s\S]+)\)[\s\S]+$/, '$1'), urlUtils.getProxyUrl('http://domain.com'));
};

Expand Down
18 changes: 9 additions & 9 deletions test/server/data/page/expected.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<meta id="metaWithoutContentAttr">
<base href="http://127.0.0.1:1836/sessionId/http://base.url" href-hammerhead-stored-value="http://base.url">
<title></title>
<style type="text/css">/* stylesheet processed via hammerhead */
<style type="text/css">/*hammerhead|stylesheet|start*/

@import "http://127.0.0.1:1836/sessionId/http://some.url";
@import url("http://127.0.0.1:1836/sessionId/http://some.url");
Expand All @@ -34,24 +34,24 @@
background-image: url(http://127.0.0.1:1836/sessionId/http://some.url);
background: repeat-y url(http://127.0.0.1:1836/sessionId/http://base.url/some/other/url) #fc0;
}
</style>
/*hammerhead|stylesheet|end*/</style>
<script type="text/javascript"> { a : __get$(window,"location") } </script>
<script type="text/javascript">
//<![CDATA[
/*hammerhead|script-processing-header|start*/var __w$= typeof window!=="undefined"&&window;__w$ && __w$["hammerhead|process-dom-method"] && __w$["hammerhead|process-dom-method"]();var __get$Loc=__w$?__w$.__get$Loc:function(l){return l},__set$Loc=__w$?__w$.__set$Loc:function(l,v){return l = v},__set$=__w$?__w$.__set$:function(o,p,v){return o[p] = v},__get$=__w$?__w$.__get$:function(o,p){return o[p]},__call$=__w$?__w$.__call$:function(o,p,a){return o[p].apply(o,a)},__get$Eval=__w$?__w$.__get$Eval:function(e){return e},__proc$Script=__w$?__w$.__proc$Script:function(s){return s};/*hammerhead|script-processing-header|end*/
__set$(window,"location",'test'); (function(){return __set$Loc(location,'test')||(location='test');}.apply(this)); __set$(document,"location",'test'); __set$(document,"domain",'test'); __set$(document,"cookie",'test'); //]]></script>
/*hammerhead|script|start*/var __w$= typeof window!=="undefined"&&window;__w$ && __w$["hammerhead|process-dom-method"] && __w$["hammerhead|process-dom-method"]();var __get$Loc=__w$?__w$.__get$Loc:function(l){return l},__set$Loc=__w$?__w$.__set$Loc:function(l,v){return l = v},__set$=__w$?__w$.__set$:function(o,p,v){return o[p] = v},__get$=__w$?__w$.__get$:function(o,p){return o[p]},__call$=__w$?__w$.__call$:function(o,p,a){return o[p].apply(o,a)},__get$Eval=__w$?__w$.__get$Eval:function(e){return e},__proc$Script=__w$?__w$.__proc$Script:function(s){return s};/*hammerhead|script|processing-header-end*/
__set$(window,"location",'test'); (function(){return __set$Loc(location,'test')||(location='test');}.apply(this)); __set$(document,"location",'test'); __set$(document,"domain",'test'); __set$(document,"cookie",'test');
/*hammerhead|script|end*///]]></script>
<script type="text/javascript">
<!--//--><![CDATA[//><!--
/*hammerhead|script-processing-header|start*/var __w$= typeof window!=="undefined"&&window;__w$ && __w$["hammerhead|process-dom-method"] && __w$["hammerhead|process-dom-method"]();var __get$Loc=__w$?__w$.__get$Loc:function(l){return l},__set$Loc=__w$?__w$.__set$Loc:function(l,v){return l = v},__set$=__w$?__w$.__set$:function(o,p,v){return o[p] = v},__get$=__w$?__w$.__get$:function(o,p){return o[p]},__call$=__w$?__w$.__call$:function(o,p,a){return o[p].apply(o,a)},__get$Eval=__w$?__w$.__get$Eval:function(e){return e},__proc$Script=__w$?__w$.__proc$Script:function(s){return s};/*hammerhead|script-processing-header|end*/
var someScript;
//--><!]]>
/*hammerhead|script|start*/var __w$= typeof window!=="undefined"&&window;__w$ && __w$["hammerhead|process-dom-method"] && __w$["hammerhead|process-dom-method"]();var __get$Loc=__w$?__w$.__get$Loc:function(l){return l},__set$Loc=__w$?__w$.__set$Loc:function(l,v){return l = v},__set$=__w$?__w$.__set$:function(o,p,v){return o[p] = v},__get$=__w$?__w$.__get$:function(o,p){return o[p]},__call$=__w$?__w$.__call$:function(o,p,a){return o[p].apply(o,a)},__get$Eval=__w$?__w$.__get$Eval:function(e){return e},__proc$Script=__w$?__w$.__proc$Script:function(s){return s};/*hammerhead|script|processing-header-end*/ var someScript;
/*hammerhead|script|end*///--><!]]>
</script>
<!-- T217636: Health monitor - script incorrectly processes html-comments (flipkart.com) -->
<script type="text/javascript">
<!--<script type="text/javascript">-->
/*hammerhead|script-processing-header|start*/var __w$= typeof window!=="undefined"&&window;__w$ && __w$["hammerhead|process-dom-method"] && __w$["hammerhead|process-dom-method"]();var __get$Loc=__w$?__w$.__get$Loc:function(l){return l},__set$Loc=__w$?__w$.__set$Loc:function(l,v){return l = v},__set$=__w$?__w$.__set$:function(o,p,v){return o[p] = v},__get$=__w$?__w$.__get$:function(o,p){return o[p]},__call$=__w$?__w$.__call$:function(o,p,a){return o[p].apply(o,a)},__get$Eval=__w$?__w$.__get$Eval:function(e){return e},__proc$Script=__w$?__w$.__proc$Script:function(s){return s};/*hammerhead|script-processing-header|end*/
/*hammerhead|script|start*/var __w$= typeof window!=="undefined"&&window;__w$ && __w$["hammerhead|process-dom-method"] && __w$["hammerhead|process-dom-method"]();var __get$Loc=__w$?__w$.__get$Loc:function(l){return l},__set$Loc=__w$?__w$.__set$Loc:function(l,v){return l = v},__set$=__w$?__w$.__set$:function(o,p,v){return o[p] = v},__get$=__w$?__w$.__get$:function(o,p){return o[p]},__call$=__w$?__w$.__call$:function(o,p,a){return o[p].apply(o,a)},__get$Eval=__w$?__w$.__get$Eval:function(e){return e},__proc$Script=__w$?__w$.__proc$Script:function(s){return s};/*hammerhead|script|processing-header-end*/
var someScript;
</script>
/*hammerhead|script|end*/</script>
</head>
<body><script type="text/javascript" class="script-hammerhead-shadow-ui">
(function () {
Expand Down
4 changes: 3 additions & 1 deletion test/server/data/script/expected.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/*hammerhead|script-processing-header|start*/var __w$= typeof window!=="undefined"&&window;__w$ && __w$["hammerhead|process-dom-method"] && __w$["hammerhead|process-dom-method"]();var __get$Loc=__w$?__w$.__get$Loc:function(l){return l},__set$Loc=__w$?__w$.__set$Loc:function(l,v){return l = v},__set$=__w$?__w$.__set$:function(o,p,v){return o[p] = v},__get$=__w$?__w$.__get$:function(o,p){return o[p]},__call$=__w$?__w$.__call$:function(o,p,a){return o[p].apply(o,a)},__get$Eval=__w$?__w$.__get$Eval:function(e){return e},__proc$Script=__w$?__w$.__proc$Script:function(s){return s};/*hammerhead|script-processing-header|end*/
/*hammerhead|script|start*/var __w$= typeof window!=="undefined"&&window;__w$ && __w$["hammerhead|process-dom-method"] && __w$["hammerhead|process-dom-method"]();var __get$Loc=__w$?__w$.__get$Loc:function(l){return l},__set$Loc=__w$?__w$.__set$Loc:function(l,v){return l = v},__set$=__w$?__w$.__set$:function(o,p,v){return o[p] = v},__get$=__w$?__w$.__get$:function(o,p){return o[p]},__call$=__w$?__w$.__call$:function(o,p,a){return o[p].apply(o,a)},__get$Eval=__w$?__w$.__get$Eval:function(e){return e},__proc$Script=__w$?__w$.__proc$Script:function(s){return s};/*hammerhead|script|processing-header-end*/
// fragment of the jQuery v1.4.1
function liveHandler( event ) {
var stop, elems = [], selectors = [], args = arguments,
Expand Down Expand Up @@ -58,3 +58,5 @@ function liveHandler( event ) {

return stop;
}

/*hammerhead|script|end*/

0 comments on commit 29613b0

Please sign in to comment.