cujojs / curl

curl.js is small, fast, extensible module loader that handles AMD, CommonJS Modules/1.1, CSS, HTML/text, and legacy scripts.
https://github.com/cujojs/curl/wiki
Other
1.88k stars 216 forks source link

css! Not good support in IE9 and Opera when cross domain #59

Closed yaoazhen closed 11 years ago

yaoazhen commented 12 years ago

In function isLinkReady, if we visit 'sheet.rules', it will throw a error -'Access is denied' in IE9. This will make isLinkReady return true, then it will break the loop of function ssWatcher below. Actually, link.readyState and onload is support in IE9, it could be complete the loading. And (sheet.cssRules || sheet.rules) would be true, not 'Access is denied' when readyState is 'complete'.

The sample example: http://jsfiddle.net/6BeDS/2/ (By the way IE9 can reproduce the issue only you clean the cache, because IE will using the cache css, even you using ctrl+F5 )

unscriptable commented 12 years ago

Thanks @yaoazhen! We've been meaning to start using the load event now that more browsers support it. :) -- J

asilvas commented 12 years ago

Is this issue specific to v0.6.x or pre-existing?

unscriptable commented 12 years ago

@asilvas I haven't seen the issue. The css! plugin code has not changed since 0.4.x and was tested on IE9. Not sure what's going on yet.

unscriptable commented 12 years ago

I'm not seeing a problem in Opera 11.62. I will check IE9 later.

yaoazhen commented 12 years ago

Thank @unscriptable , In Opera 11.61 on win7, I also see this issue, The js callback will before the css loaded when you visit http://jsfiddle.net/6BeDS/2/ at first time, second time it will using cache, It's same with IE9.

So in this sample, it will log the rgb(0, 0, 0) at first time, and log rgb(255, 0, 0) after.

unscriptable commented 12 years ago

I've been able to reproduce this issue in IE6-9 and Opera 11.61 (but not 11.62). Just to clarify: cross-domain css files are loaded correctly, but the loader does not always wait for the style sheet to finish loading before proceeding.

@yaoazhen Just FYI: If your app does not need to wait for the css file to finish loading, you could use the link! plugin instead.

-- John

yaoazhen commented 12 years ago

Correctly, the loader does not always wait for the style sheet to finish loading before proceeding.

Sadly~ I have to load css file asynchronous, and wait them complete in my strange app.

jeffrose commented 12 years ago

Julian Aubourg has a css $.ajaxTransport that loads and waits for CSS. I do not know if it's been tested against this particular scenario but it might be worthwhile to investigate the technique he's using.

https://github.com/jaubourg/ajaxHooks/blob/master/src/ajax/css.js

unscriptable commented 12 years ago

I did some serious experimentation/research over the weekend and prototyped an entirely new solution. It's almost working everywhere (tested IE6+, FF3.6+, Chrome 19, Safari 5.2, Opera 11.62).

jeffrose commented 12 years ago

Any word on the progress of this issue?

unscriptable commented 12 years ago

Hey @jeffrose, @asilvas, and @yaoazhen!

I've hit a wall. It seems there's no way to do the following two things at the same time in IE, Opera, Safari, and old Firefox:

Safari and FF3.6-9 have no known way to detect 404s using <link> elements (they don't even support onload!). IE 6-9 and Opera fire onload instead of onerror when a link url is invalid. The only way I've found to get around this is to use a different element (<object> or <img>) to detect 404s. This means there are two requests for the stylesheet url, one from the <link> and one from the <object> or <img>. Browsers could potentially send two HTTP requests under this situation, although I've found in practice that it doesn't happen often. (Sorry, I don't have more exact information about which browsers send two HTTP requests and under what conditions.)

Any feedback on what the best approach might be here?
Is it important to detect 404s?
Should we timeout (wait 1 sec, for instance) before trying to detect a 404 with an <object> or <img>?

Thanks!

Just FYI: I've got a fairly solid algorithm to detect when a browser supports onload and onerror. Therefore, as browsers start to support these events, whatever hacks we have in place will slowly become obsolete.

-- John

yaoazhen commented 12 years ago

What I'm think about is shall we load all css with text, and then take the text into a style tag,and I'm force on this way now

asilvas commented 12 years ago

@yaoazhen Loading as text would not be permitted across domain boundaries.

It's an unfortunate limitation, but using a long-running timeout (~5secs) for unsupported browsers seems more reasonable than losing the feature, so long as it does not add a lot of bloat, and is reliable.

jeffrose commented 12 years ago

I was hoping looking at @jaubourg CSS ajax transport would help with a solution but I guess it's simply a difficult problem to thoroughly solve cross-browser.

I do not believe it's important to detect 404s. It seems like a problem that would require developer intervention to resolve and there are other tools that give you the means to detect it, e.g. the Network tab in the Firebug console.

Not loading duplicate stylesheets seems like a useful optimization but couldn't that be somewhat achieved at build time? For instance the RequireJS optimizer traces the code facilitates bundling the JS in a "smart" way. That's not to say you will never fetch duplicate code, but if you take the time to configure and tweak the build optimizer, it should be infrequent. The same result could be achieved with CSS.

In regards to timing out, I would say check the loader configuration and use whatever timeout value is available, e.g. waitSeconds in RequireJS, with the option of also setting a css-specific timeout in the configuration, e.g. cssWaitSeconds.

jaubourg commented 12 years ago

@unscriptable would love to see your approach, even if flawed, somewhere. Maybe we can devise a plan to mitigate the issues you have but that would require some code to look at.

I haven't tested the ajaxHooks css transport recently (not enough time these days) so it's very possible something is broken in recent versions of FF and Chrome. Curious about the way you detect onload support.

Oh and hi everybody :)

unscriptable commented 12 years ago

I pushed a fix for the css! plugin to the dev branch. Please test it in your projects when you get a chance.

Summary:

This version:

The following browsers have working onload and onerror event handlers for elements. The plugin detects this and uses them (yay for working browsers!):

Firefox 12+ Chrome 10+ (?) Safari 6+ (and iOS 6+)

The following browsers use a crazy work-around to capture onload and onerror events (which also works around 31-stylesheet limits):

IE 6 (does not fire onerror) IE 7-9 (yes, IE9 still has the 31-sheet limit, despite claims to the contrary)

The following browsers have working onload, but not onerror event handlers. These don't fail silently, however. They fire a false onload event instead of an onerror event:

IE 10 Opera (all versions. tested up to 12.01)

All other browsers (old FF, old Chrome, old Safari, old Safari Mobile) use the "universal work-around" that sniffs for stylesheet rules. These browsers fail silently if a stylesheet is missing, unfortunately. If this is unacceptable behavior, then we'll have to add a timeout feature of some sort. I'm hoping to avoid this since it's nearly impossible to set the timeout to something reasonable for all users.

Feedback welcome! Thanks guys!

unscriptable commented 12 years ago

Hey @jaubourg!

Take a peek at https://github.com/cujojs/curl/blob/dev/src/curl/plugin/css.js

Unfortunately, there's a lot of shiz in there to deal with older browsers and IE. Basic approach:

Regards,

-- John

asilvas commented 12 years ago

Very nice, John. Thanks!

unscriptable commented 11 years ago

There were still some issues with the latest Opera and XD stylesheets. There's a fix in the dev branch.

unscriptable commented 11 years ago

see #134