0
# Cross-Domain CSS Support
1
2
The cross-domain proxy system enables respond.js to work with stylesheets hosted on CDNs or different domains by overcoming same-origin policy restrictions. This is essential for modern web applications that serve CSS from content delivery networks.
3
4
## Setup Requirements
5
6
### Required Files
7
8
Three files must be properly configured for cross-domain functionality:
9
10
```html { .api }
11
<!-- External domain: Proxy handler page -->
12
respond-proxy.html
13
14
<!-- Local domain: Redirect image for communication (1x1 transparent GIF) -->
15
respond.proxy.gif
16
17
<!-- Local domain: Proxy script -->
18
respond.proxy.js
19
```
20
21
**File Descriptions:**
22
23
- **respond-proxy.html**: Proxy page hosted on external domain that fetches CSS and returns it via iframe name property
24
- **respond.proxy.gif**: 1x1 pixel transparent GIF used as redirect target for cross-domain communication (35 bytes)
25
- **respond.proxy.js**: Main proxy script that detects external stylesheets and initiates proxy requests
26
27
### HTML Setup
28
29
```html { .api }
30
<!-- Respond.js proxy on external server (CDN) -->
31
<link href="http://externalcdn.com/respond-proxy.html" id="respond-proxy" rel="respond-proxy" />
32
33
<!-- Respond.js redirect location on local server -->
34
<link href="/path/to/respond.proxy.gif" id="respond-redirect" rel="respond-redirect" />
35
36
<!-- Respond.js proxy script on local server -->
37
<script src="/path/to/respond.proxy.js"></script>
38
```
39
40
## Capabilities
41
42
### Cross-Domain CSS Processing
43
44
Enables respond.js to fetch and process CSS files from external domains that would normally be blocked by browser security policies.
45
46
```javascript { .api }
47
/**
48
* Main proxy functions available via respond.proxy.js
49
*/
50
interface CrossDomainProxy {
51
/**
52
* Fetch CSS content from external domain via iframe proxy
53
* @param url - External CSS file URL to fetch
54
* @param callback - Function called with retrieved CSS content
55
*/
56
fakejax(url: string, callback: (css: string) => void): void;
57
58
/**
59
* Check if URL needs base URL resolution
60
* @param href - URL to check against base element
61
* @returns Resolved absolute URL
62
*/
63
checkBaseURL(href: string): string;
64
65
/**
66
* Build URLs list of external stylesheets for processing
67
* Scans all link elements and initiates proxy requests for external stylesheets
68
*/
69
buildUrls(): void;
70
71
/**
72
* Resolve redirect URL for cross-domain communication
73
* Handles IE6/7 relative URL resolution issues
74
*/
75
checkRedirectURL(): void;
76
}
77
78
/**
79
* Proxy page functions (respond-proxy.html)
80
*/
81
interface ProxyPageAPI {
82
/**
83
* Parse query string parameters from proxy page URL
84
* @returns Object containing url and css parameters
85
*/
86
getQueryString(): { url: string; css: string };
87
88
/**
89
* AJAX function for fetching CSS from external domain
90
* @param url - CSS file URL to fetch
91
* @param callback - Function called with CSS content
92
*/
93
ajax(url: string, callback: (response: string) => void): void;
94
95
/**
96
* XMLHttpRequest factory function with fallbacks
97
* @returns XMLHttpRequest instance for current browser
98
*/
99
xmlHttp(): XMLHttpRequest;
100
}
101
```
102
103
### iframe Communication System
104
105
Uses iframe-based communication to securely fetch cross-domain CSS content.
106
107
```javascript { .api }
108
/**
109
* Create iframe proxy for cross-domain CSS fetching
110
* @param url - External CSS file URL to fetch
111
* @param callback - Function called with retrieved CSS content
112
*/
113
function fakejax(url: string, callback: (css: string) => void): void;
114
115
/**
116
* Check and resolve base URL for relative paths
117
* @param href - URL to check against base element
118
* @returns Resolved absolute URL
119
*/
120
function checkBaseURL(href: string): string;
121
```
122
123
## Setup Process
124
125
### Step 1: Upload Proxy Files
126
127
```bash
128
# Upload to external domain (CDN)
129
scp respond-proxy.html user@cdn.example.com:/var/www/html/
130
131
# Upload to local domain
132
scp respond.proxy.gif user@local.example.com:/var/www/html/assets/
133
scp respond.proxy.js user@local.example.com:/var/www/html/js/
134
```
135
136
### Step 2: Configure HTML
137
138
```html
139
<!DOCTYPE html>
140
<html>
141
<head>
142
<!-- External stylesheet that needs proxy -->
143
<link rel="stylesheet" href="http://cdn.example.com/styles/main.css" />
144
145
<!-- Proxy configuration -->
146
<link href="http://cdn.example.com/respond-proxy.html" id="respond-proxy" rel="respond-proxy" />
147
<link href="/assets/respond.proxy.gif" id="respond-redirect" rel="respond-redirect" />
148
149
<!-- Load proxy script -->
150
<script src="/js/respond.proxy.js"></script>
151
</head>
152
<body>
153
<!-- Content -->
154
</body>
155
</html>
156
```
157
158
### Step 3: Verify Configuration
159
160
```javascript
161
// Check if proxy elements are properly configured
162
var proxyLink = document.getElementById('respond-proxy');
163
var redirectLink = document.getElementById('respond-redirect');
164
165
if (proxyLink && redirectLink) {
166
console.log('Proxy configuration found');
167
console.log('Proxy URL:', proxyLink.href);
168
console.log('Redirect URL:', redirectLink.href);
169
} else {
170
console.error('Missing proxy configuration elements');
171
}
172
```
173
174
## Communication Protocol
175
176
### Message Flow
177
178
1. **Detection**: respond.proxy.js detects external stylesheets
179
2. **iframe Creation**: Creates hidden iframe pointing to proxy URL
180
3. **Parameter Passing**: Passes CSS URL and redirect URL as query parameters
181
4. **Proxy Fetch**: respond-proxy.html fetches the CSS content
182
5. **Return Path**: CSS content returned via iframe.contentWindow.name
183
6. **Processing**: respond.js processes the retrieved CSS normally
184
185
### URL Parameters
186
187
```
188
http://cdn.example.com/respond-proxy.html?url=REDIRECT_URL&css=CSS_URL
189
190
Where:
191
- REDIRECT_URL: Encoded local redirect URL (respond.proxy.gif)
192
- CSS_URL: Encoded external CSS file URL to fetch
193
```
194
195
### Example Request
196
197
```
198
http://cdn.example.com/respond-proxy.html?url=http%3A//local.com/respond.proxy.gif&css=http%3A//cdn.example.com/styles/responsive.css
199
```
200
201
## Implementation Details
202
203
### Domain Detection
204
205
```javascript { .api }
206
/**
207
* Check if stylesheet is external and needs proxy processing
208
* @param href - Stylesheet URL to check
209
* @returns true if external domain, false if same domain
210
*/
211
function isExternalStylesheet(href: string): boolean;
212
```
213
214
**Detection Logic:**
215
216
```javascript
217
// External domain patterns that trigger proxy:
218
var externalPatterns = [
219
/^([a-zA-Z:]*\/\/(www\.)?)/, // Absolute URLs with protocol
220
/^\/\// // Protocol-relative URLs
221
];
222
223
// Same domain indicators (no proxy needed):
224
// - Relative paths: ./styles/main.css, ../css/theme.css
225
// - Absolute paths: /css/main.css
226
// - Same host: http://currentdomain.com/css/main.css
227
```
228
229
### iframe Management
230
231
```javascript
232
// iframe creation and cleanup
233
function createProxyIframe() {
234
var iframe;
235
236
if ("ActiveXObject" in window) {
237
// IE-specific iframe creation
238
var AXO = new ActiveXObject("htmlfile");
239
AXO.open();
240
AXO.write('<iframe id="x"></iframe>');
241
AXO.close();
242
iframe = AXO.getElementById("x");
243
} else {
244
// Standard iframe creation
245
iframe = document.createElement("iframe");
246
iframe.style.cssText = "position:absolute;top:-99em";
247
document.documentElement.insertBefore(iframe,
248
document.documentElement.firstElementChild);
249
}
250
251
return iframe;
252
}
253
```
254
255
### Response Handling
256
257
```javascript
258
function checkFrameName(iframe, callback) {
259
var cssText;
260
261
try {
262
// CSS content is returned via iframe name property
263
cssText = iframe.contentWindow.name;
264
} catch (e) {
265
// Cross-domain access denied - continue polling
266
}
267
268
if (cssText) {
269
// Clean up iframe
270
iframe.src = "about:blank";
271
iframe.parentNode.removeChild(iframe);
272
273
// Process retrieved CSS
274
callback(cssText);
275
} else {
276
// Continue polling for response
277
setTimeout(function() {
278
checkFrameName(iframe, callback);
279
}, 100);
280
}
281
}
282
```
283
284
## Security Considerations
285
286
### Same-Origin Policy
287
288
The proxy system works within browser security constraints:
289
290
- **iframe Sandbox**: Uses iframe for secure cross-domain communication
291
- **No Direct XHR**: Avoids blocked XMLHttpRequest cross-domain calls
292
- **Controlled Access**: Only fetches CSS, not arbitrary content
293
- **Domain Validation**: Proxy validates requested URLs
294
295
### HTTPS Compatibility
296
297
```html
298
<!-- HTTPS configuration -->
299
<link href="https://secure-cdn.example.com/respond-proxy.html" id="respond-proxy" rel="respond-proxy" />
300
<link href="/assets/respond.proxy.gif" id="respond-redirect" rel="respond-redirect" />
301
```
302
303
Protocol matching is important:
304
- **HTTPS pages**: Use HTTPS proxy URLs
305
- **HTTP pages**: Use HTTP proxy URLs
306
- **Mixed protocols**: May cause security warnings
307
308
## Common Issues and Solutions
309
310
### Missing Query String
311
312
**Problem**: Proxy fails if respond-proxy.html has query parameters appended.
313
314
```html
315
<!-- Incorrect -->
316
<link href="http://cdn.com/respond-proxy.html?version=1.0" id="respond-proxy" rel="respond-proxy" />
317
318
<!-- Correct -->
319
<link href="http://cdn.com/respond-proxy.html" id="respond-proxy" rel="respond-proxy" />
320
```
321
322
### Base Element Conflicts
323
324
**Problem**: `<base>` element affects URL resolution.
325
326
```html
327
<!-- Base element can interfere with proxy URLs -->
328
<base href="/app/" />
329
330
<!-- Solution: Use absolute URLs for proxy configuration -->
331
<link href="http://cdn.com/respond-proxy.html" id="respond-proxy" rel="respond-proxy" />
332
<link href="http://local.com/respond.proxy.gif" id="respond-redirect" rel="respond-redirect" />
333
```
334
335
### Protocol Mismatches
336
337
**Problem**: Mixed HTTP/HTTPS protocols cause failures.
338
339
```javascript
340
// Automatic protocol matching
341
var proxyURL = location.protocol === 'https:'
342
? 'https://secure-cdn.com/respond-proxy.html'
343
: 'http://cdn.com/respond-proxy.html';
344
345
document.getElementById('respond-proxy').href = proxyURL;
346
```
347
348
### Cache Headers
349
350
**Problem**: CSS files with no-cache headers cause repeated requests.
351
352
```
353
# Server configuration for CDN CSS files
354
Cache-Control: public, max-age=31536000
355
Expires: Thu, 31 Dec 2025 23:55:55 GMT
356
```
357
358
## Testing Cross-Domain Setup
359
360
### Verification Script
361
362
```javascript
363
function testCrossDomainSetup() {
364
var proxyLink = document.getElementById('respond-proxy');
365
var redirectLink = document.getElementById('respond-redirect');
366
var externalStylesheets = [];
367
368
// Check required elements
369
if (!proxyLink) {
370
console.error('Missing respond-proxy link element');
371
return false;
372
}
373
374
if (!redirectLink) {
375
console.error('Missing respond-redirect link element');
376
return false;
377
}
378
379
// Find external stylesheets
380
var links = document.getElementsByTagName('link');
381
for (var i = 0; i < links.length; i++) {
382
var link = links[i];
383
if (link.rel.indexOf('stylesheet') >= 0) {
384
var isExternal = /^([a-zA-Z:]*\/\/)/.test(link.href) &&
385
link.href.indexOf(location.host) === -1;
386
if (isExternal) {
387
externalStylesheets.push(link.href);
388
}
389
}
390
}
391
392
console.log('External stylesheets found:', externalStylesheets);
393
console.log('Proxy URL:', proxyLink.href);
394
console.log('Redirect URL:', redirectLink.href);
395
396
return externalStylesheets.length > 0;
397
}
398
399
// Run test
400
testCrossDomainSetup();
401
```