Unit Tests for CDN/Cross-Domain hosted CSS #28

Closed
wants to merge 51 commits into
from

5 participants

@doctyper
Collaborator

Most of the patch below has been integrated, but the unit tests still need to be ported over. For that reason, I'm going to leave this open.

The patch enables downloading CSS from an external domain. It uses an external iframe proxy to perform the required Ajax duties on the external server, then transports the resulting CSS back to the origin domain via the window.name object.

The patch requires access to the external domain, so hotlinking is out of the equation.

Some setup is required, although I tried to keep it as simple as possible:

See test/test-x-domain.html for a demo:

  • Upload x-domain/respond-proxy.html to your external domain
  • Upload x-domain/favicon.ico to your origin domain (Optional)
    • Note: The remote proxy must redirect back to your origin domain after it completes its tasks. If none is set, it defaults to the current page.
    • The advantage to declaring a redirect is speed. Redirecting to favicon.ico is faster than re-loading the current page within the iframe.
  • Reference the file(s) via <link /> element(s):
    <!-- Respond.js proxy on external server -->
    <link href="http://externalcdn.com/respond-proxy.html" id="respond-proxy" rel="respond-proxy" />
    
    <!-- Respond.js redirect (optional, defaults to current page) -->
    <link href="/path/to/favicon.ico" id="respond-redirect" rel="respond-redirect" />

Feedback is welcome. An alternate solution to the link element(s) would be to define the proxy values inside of Respond.js. However, in the spirit of "minimal setup", I thought it'd be best to keep settable parameters outside the script.

I also kept an eye on file size. After several micro-edits this was the lowest increase I could muster:

Vanilla Respond.js: 3.1K (1.7K gzipped)
XDR'd Respond.js: 3.7K (1.9K gzipped)

The proxy retains cross-browser support, though if that were IE-only the file size could come down a bit. According to caniuse.com, browsers not supporting media queries are pretty much limited to the IE's.

Looking forward to your thoughts.

doctyper added some commits Jun 3, 2011
@doctyper doctyper Moving RegExp to accessible variable for later use. defeda4
@doctyper doctyper Exposing variables to share between functions. 56ce21f
@doctyper doctyper Fallback URL is accessed via <meta name="respond-proxy" /> 8765ea9
@doctyper doctyper If proxyURL is defined, add to queue. 560eebd
@doctyper doctyper Move to named function for later access. 624820a
@doctyper doctyper Exposing semi-private variables (is that a thing?) accessed via ifram…
…e proxy.
b5375de
@doctyper doctyper - Branch the ajax function to handle external proxy.
- If URL is external and proxyURL exists, create a dummy iframe (if necessary) and access the proxy.
- Else if internal, proceed as normal.
67f92d5
@doctyper doctyper After completion, check for iframe and remove if needed. eca97d8
@doctyper doctyper Respond.js proxy. The iframe URL is constructed as follows:
http://cdnexample.com/respond-proxy.html?url=http://originalurl.com/x-domain-end-point.html&css=http://cdnexample.com/example.css

The URL is parsed, CSS file is extracted and stored in window.name. The file then redirects to the correct URL for MQ parsing.
e7a2a59
@doctyper doctyper Turns out IE treats two different protocols as same-domain, leading t…
…o a false positive on x-domain. Take two:

- Removing semi-private vars. No longer needed.
fabed6e
@doctyper doctyper Using the Apple-style meta syntax to avoid having multiple meta eleme…
…nts for Respond.js. Here I parse out both the proxy file and the end-point redirect.
353a29f
@doctyper doctyper The proxyInterval polls the iframe for a change in iframe.contentWind…
…ow.name. Once triggered it parses out the embedded CSS and resumes functionality.

It is wrapped in a try/catch statement due to access warnings.
34d0b0c
@doctyper doctyper Send the redirectURL along with the rest of the params. d3f40a6
@doctyper doctyper External check no longer needed. cb16091
@doctyper doctyper Adding a blank end-point redirect. Required for the iframe/window.nam…
…e hack.
b9e0fca
@doctyper doctyper Cross-domain test with Respond.js. e95a7f7
@doctyper doctyper Updating README. 96ea514
@doctyper doctyper Updating README. e460844
@doctyper doctyper Moving proxy files to project root. 0fd2c51
@doctyper doctyper Tweaking meta element. 3b6b740
@doctyper doctyper Updating README. 8f37fbf
@doctyper doctyper Minified response-proxy file. 32b5543
@doctyper doctyper Minified respond-proxy file. a758f30
@doctyper doctyper Merge branch 'master' of github.com:doctyper/Respond a3beea0
@doctyper doctyper Minor whitespace fixes. a063e44
@doctyper doctyper Missing semicolon. 67b7341
@doctyper doctyper All hail Google http://j.mp/iKMI19
Behold, an iframe proxy without annoying clicky noises.
3a2e324
@doctyper doctyper Going against the grain and using an AXO-first approach here. The XML…
…HttpRequest doesn't appear to fire inside an AXO HTMLFile. Since we're now using that approach to resolve UX issues, this method allows XHR to work by testing for AXO objects first.
6af4c5b
@doctyper doctyper Simply writing out an iframe seems to work here. Saves bytes. a56327e
@doctyper doctyper No need to encode the CSS code for window.name. Saving bytes. dc0afad
@doctyper doctyper Adding stricter checks for redirectURL parameter. 5b686dd
@doctyper doctyper Shaving bytes. 964c334
@doctyper doctyper Not sure it matters, but I'd rather pull as much code outside the try…
…/catch block as possible.
371d089
@doctyper doctyper Custom <meta> elements are not part of the HTML5 spec. Switching to <…
…link> elements. Making #respond-redirect an optional parameter (defaults to current page).

Via: http://www.w3.org/TR/html-markup/link.html#link
09292ba
@doctyper doctyper Switching to <link> elements for parameter retrieval. redirectURL def…
…aults to current page if none explicitly set.
2abcd4b
@doctyper doctyper Fixes bug where existing reference to iframe caused respond.update() …
…to fail.
4ecb98e
@doctyper doctyper Safeguard Respond.js from potential infinite-loop (loading the curren…
…t page in an iframe).
7c51756
@doctyper doctyper Updating README. c48ac66
@doctyper doctyper Updating README. 452585f
@doctyper doctyper Updating README. f2263f7
@doctyper doctyper Updating README. d94e62b
@doctyper doctyper Adding fix for loading stylesheets from multiple domains (My patch on…
…ly supports loading from one declared domain as of yet). Non-matching domains now silently fail.
c5f1847
@doctyper doctyper Reusing host check as matchDomain variable. b021d47
@doctyper doctyper Storing window.location variable. c013889
@doctyper doctyper Test should be against proxyURL variable. 4a5113f
@doctyper doctyper Adding comment. 921a6e8
@doctyper doctyper Adding x-domain unit test. 08828be
@doctyper doctyper Fixes conflict with x-domain test. 0571dba
@doctyper doctyper Stop the iframe from loading further content. All we need is its wind…
…ow.name object.
2f5c3c1
@doctyper doctyper Updating README. f9b20b5
@doctyper doctyper Fixes false positive dealing with CSS load order. Fixes conflict when…
… attempting to load from multiple external domains.
ac20cef
@scottjehl
Owner

Awesome work, Doctyper! I'll take a look at this asap.

@scottjehl
Owner

Quick question while I'm reading through this: do you think it'd be worthwhile to instead consider offering an external proxy adapter script that Respond.js can reference locally to make the request on the server side? Basically, if some sort of "parseExternalCSS" option was set to true, and Respond.js encountered a link with an external url, it could just make its normal ajax request to that url with a proxy prefix like "respond.external.php?url=...". That proxy would simply curl that url, prefix any relative asset urls it contains, and echo it out.

Just a thought, as it seems this approach would potentially allow for CDN-hosted CSS, which is a primary concern of those who have requested this feature in the past.

@doctyper
Collaborator

Well, my goal was for a back-end-agnostic solution. Going down that route will get pretty branched since you'll need solutions for several languages.

Proxying the call internally also means you're taking on the extra bandwidth, not the CDN.

I don't think this solution necessarily excludes CDN content. As long as the developer has access to the hosted files (presumably), it shouldn't be a problem. Think of the proxy as an incredibly hacky crossdomain.xml.

@adamesque

FWIW, it seems like if you have to proxy a CDN request through your own domain, you might as well have just hosted it there to begin with. I'm not sure that a local proxy is a win if your use-case involves a CDN.

@scottjehl
Owner

Well, the proxy would only be used for IE's ajax request for the CSS to get the media queries. Otherwise, you'd get the benefits of the CDN.

@rickydazla

I have a specific case -- a Shopify theme (Shopify is a Ruby ecommerce app).
To summarize:

I have set up a repo with all of my source files here: https://github.com/rickydazla/Respondify
(my html background-color should change from white (@320px) to other garish colors with screen width.)

Scott's respond script is not working for me, nor this unfortunately. IE is still showing white background from initial CSS.

Input appreciated. Rick

@scottjehl scottjehl pushed a commit that closed this pull request Jul 19, 2011
scottjehl Added a new external cross-domain proxy script for those who need it.…
… Instructions are included in the README file. Huge, Huge thanks to @doctyper, who crafted the majority of the logic behind this workaround. Fixes #28. Fixes #24.
3500e86
@scottjehl scottjehl closed this in 3500e86 Jul 19, 2011
@scottjehl scottjehl reopened this Jul 19, 2011
@scottjehl
Owner

@doctyper: Thanks so much for this patch! I've finally gotten around to applying most of this as an external patch that can be used in a addition to respond.js. In doing so, I've modified a good deal of it to make it work without modifying respond itself, and I'd really appreciate any feedback you have on the implementation, as it's just a first pass.

One thing I'm noticing is that it's only working in IE8 currently. Was yours working in 6 and 7? Maybe I missed something...

I also still need to update your unit tests and get them into the suite.

The readme is updated, mostly using your text. :)

Lastly, the latest git snapshot is up here http://scottjehl.github.com/Respond/cross-domain/example.html

So, thanks again!
Scott

@chrisjacob

I haven't dug to deep into the cross domain solution you've implemented just yet... but as soon as I heard the host needed to implement a proxy html file it made me think of this awesome Stackoverflow answer...

Cross Domain: Resizing an iframe based on content
http://stackoverflow.com/questions/153152/resizing-an-iframe-based-on-content/362564#362564

I have implemented it for resizing an iframe ... not sure if it has any use in regard to requesting assets like CSS/Images cross domain; but still thought I would mention it incase it does ^_^

@doctyper
Collaborator

@scottjehl Madness. Fixed in #47

@doctyper doctyper closed this Oct 23, 2012
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment