Bug 185236 part 5. Fire load and error events on stylesheet linking elements. r=peterv

This commit is contained in:
Boris Zbarsky 2011-09-26 17:27:13 -04:00
Родитель e49d5e5860
Коммит 4d807853c0
4 изменённых файлов: 254 добавлений и 4 удалений

Просмотреть файл

@ -12,7 +12,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=364413
<script type="text/javascript" src="/MochiKit/DOM.js"></script>
<script type="text/javascript" src="/MochiKit/Style.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="SimpleTest/test.css" />
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=364413">Mozilla Bug 364413</a>

Просмотреть файл

@ -81,6 +81,7 @@
#include "nsThreadUtils.h"
#include "nsGkAtoms.h"
#include "nsDocShellCID.h"
#include "nsIThreadInternal.h"
#ifdef MOZ_XUL
#include "nsXULPrototypeCache.h"
@ -132,7 +133,8 @@ namespace css {
*********************************************/
class SheetLoadData : public nsIRunnable,
public nsIUnicharStreamLoaderObserver
public nsIUnicharStreamLoaderObserver,
public nsIThreadObserver
{
public:
virtual ~SheetLoadData(void);
@ -167,8 +169,11 @@ public:
already_AddRefed<nsIURI> GetReferrerURI();
void ScheduleLoadEventIfNeeded(nsresult aStatus);
NS_DECL_ISUPPORTS
NS_DECL_NSIRUNNABLE
NS_DECL_NSITHREADOBSERVER
NS_DECL_NSIUNICHARSTREAMLOADEROBSERVER
// Hold a ref to the CSSLoader so we can call back to it to let it
@ -259,6 +264,15 @@ public:
// The charset to use if the transport and sheet don't indicate one.
// May be empty. Must be empty if mOwningElement is non-null.
nsCString mCharsetHint;
// The status our load ended up with; this determines whether we
// should fire error events or load events. This gets initialized
// by ScheduleLoadEventIfNeeded, and is only used after that has
// been called.
nsresult mStatus;
private:
void FireLoadEvent(nsIThreadInternal* aThread);
};
#ifdef MOZ_LOGGING
@ -310,7 +324,8 @@ static const char* const gStateStrings[] = {
/********************************
* SheetLoadData implementation *
********************************/
NS_IMPL_ISUPPORTS2(SheetLoadData, nsIUnicharStreamLoaderObserver, nsIRunnable)
NS_IMPL_ISUPPORTS3(SheetLoadData, nsIUnicharStreamLoaderObserver, nsIRunnable,
nsIThreadObserver)
SheetLoadData::SheetLoadData(Loader* aLoader,
const nsSubstring& aTitle,
@ -437,6 +452,78 @@ SheetLoadData::Run()
return NS_OK;
}
NS_IMETHODIMP
SheetLoadData::OnDispatchedEvent(nsIThreadInternal* aThread)
{
return NS_OK;
}
NS_IMETHODIMP
SheetLoadData::OnProcessNextEvent(nsIThreadInternal* aThread,
PRBool aMayWait,
PRUint32 aRecursionDepth)
{
// We want to fire our load even before or after event processing,
// whichever comes first.
FireLoadEvent(aThread);
return NS_OK;
}
NS_IMETHODIMP
SheetLoadData::AfterProcessNextEvent(nsIThreadInternal* aThread,
PRUint32 aRecursionDepth)
{
// We want to fire our load even before or after event processing,
// whichever comes first.
FireLoadEvent(aThread);
return NS_OK;
}
void
SheetLoadData::FireLoadEvent(nsIThreadInternal* aThread)
{
// First remove ourselves as a thread observer. But we need to keep
// ourselves alive while doing that!
nsRefPtr<SheetLoadData> kungFuDeathGrip(this);
aThread->RemoveObserver(this);
// Now fire the event
nsCOMPtr<nsINode> node = do_QueryInterface(mOwningElement);
NS_ASSERTION(node, "How did that happen???");
nsContentUtils::DispatchTrustedEvent(node->GetOwnerDoc(),
node,
NS_SUCCEEDED(mStatus) ?
NS_LITERAL_STRING("load") :
NS_LITERAL_STRING("error"),
PR_FALSE, PR_FALSE);
// And unblock onload
if (mLoader->mDocument) {
mLoader->mDocument->UnblockOnload(PR_TRUE);
}
}
void
SheetLoadData::ScheduleLoadEventIfNeeded(nsresult aStatus)
{
if (!mOwningElement) {
return;
}
mStatus = aStatus;
nsCOMPtr<nsIThread> thread = do_GetMainThread();
nsCOMPtr<nsIThreadInternal> internalThread = do_QueryInterface(thread);
if (NS_SUCCEEDED(internalThread->AddObserver(this))) {
// Make sure to block onload here
if (mLoader->mDocument) {
mLoader->mDocument->BlockOnload();
}
}
}
/*************************
* Loader Implementation *
*************************/
@ -1661,6 +1748,7 @@ Loader::DoSheetComplete(SheetLoadData* aLoadData, nsresult aStatus,
"should not get marked modified during parsing");
if (!data->mSheetAlreadyComplete) {
data->mSheet->SetComplete();
data->ScheduleLoadEventIfNeeded(aStatus);
}
if (data->mMustNotify && (data->mObserver || !mObservers.IsEmpty())) {
// Don't notify here so we don't trigger script. Remember the
@ -1689,7 +1777,10 @@ Loader::DoSheetComplete(SheetLoadData* aLoadData, nsresult aStatus,
data = data->mNext;
}
// Now that it's marked complete, put the sheet in our cache
// Now that it's marked complete, put the sheet in our cache.
// If we ever start doing this for failure aStatus, we'll need to
// adjust the PostLoadEvent code that thinks anything already
// complete must have loaded succesfully.
if (NS_SUCCEEDED(aStatus) && aLoadData->mURI) {
#ifdef MOZ_XUL
if (IsChromeURI(aLoadData->mURI)) {
@ -2161,6 +2252,12 @@ Loader::PostLoadEvent(nsIURI* aURI,
// We want to notify the observer for this data.
evt->mMustNotify = PR_TRUE;
evt->mSheetAlreadyComplete = PR_TRUE;
// If we get to this code, aSheet loaded correctly at some point, so
// we can just use NS_OK for the status. Note that we do this here
// and not from inside our SheetComplete so that we don't end up
// running the load event async.
evt->ScheduleLoadEventIfNeeded(NS_OK);
}
return rv;

Просмотреть файл

@ -226,6 +226,7 @@ _TEST_FILES = test_acid3_test46.html \
visited_image_loading.sjs \
visited_image_loading_frame.html \
visited_image_loading_frame_empty.html \
test_load_events_on_stylesheets.html \
$(NULL)
_VISITED_REFTEST_FILES = \

Просмотреть файл

@ -0,0 +1,152 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=185236
-->
<head>
<title>Test for Bug 185236</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script>
var pendingEventCounter = 0;
var messagePosted = false;
SimpleTest.waitForExplicitFinish();
addLoadEvent(function() {
is(messagePosted, true, "Should have gotten onmessage event");
is(pendingEventCounter, 0,
"How did onload for the page fire before onload for all the stylesheets?");
SimpleTest.finish();
});
// Count the link we're about to parse
pendingEventCounter = 1;
</script>
<link rel="stylesheet" href="data:text/css,*{}"
onload="--pendingEventCounter;
ok(true, 'Load event firing on basic stylesheet')"
onerror="--pendingEventCounter;
ok(false, 'Error event firing on basic stylesheet')">
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=185236">Mozilla Bug 185236</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
<script type="application/javascript">
/** Test for Bug 185236 **/
// Verify that there are no in-flight sheet loads right now; we should have
// waited for them when we hit the script tag
is(pendingEventCounter, 0, "There should be no pending events");
// Test sheet that will already be complete when we write it out
++pendingEventCounter;
// Make sure that a postMessage we do right now fires after the onload handler
// for the stylesheet. If we ever change the timing of sheet onload, we will
// need to change that.
window.onmessage = function() {
messagePosted = true;
// There are 4 pending events: two from the two direct example.com loads,
// and 2 from the two data:text/css loads that import things
is(pendingEventCounter, 4, "Load event for sheet should have fired");
}
window.postMessage("", "*");
document.write('<link rel="stylesheet" href="data:text/css,*{}"\
onload="--pendingEventCounter;\
ok(true, \'Load event firing on basic stylesheet\')"\
onerror="--pendingEventCounter;\
ok(false, \'Error event firing on basic stylesheet\')">');
// Make sure we have that second stylesheet
is(document.styleSheets.length, 3, "Should have three stylesheets");
// Make sure that the second stylesheet is all loaded
// If we ever switch away from sync loading of already-complete sheets, this
// test will need adjusting
is(document.styleSheets[2].cssRules.length, 1, "Should have one rule");
// Make sure the load event for that stylesheet has not fired yet
is(pendingEventCounter, 1, "There should be one pending event");
++pendingEventCounter;
document.write('<style\
onload="--pendingEventCounter;\
ok(true, \'Load event firing on inline stylesheet\')"\
onerror="--pendingEventCounter;\
ok(false, \'Error event firing on inline stylesheet\')"></style>');
// Make sure the load event for that second stylesheet has not fired yet
is(pendingEventCounter, 2, "There should be two pending events");
++pendingEventCounter;
document.write('<link rel="stylesheet" href="http://www.example.com"\
onload="--pendingEventCounter;\
ok(false, \'Load event firing on broken stylesheet\')"\
onerror="--pendingEventCounter;\
ok(true, \'Error event firing on broken stylesheet\')">');
++pendingEventCounter;
var link = document.createElement("link");
link.rel = "stylesheet";
link.href = "http://www.example.com";
link.onload = function() { --pendingEventCounter;
ok(false, 'Load event firing on broken stylesheet');
};
link.onerror = function() { --pendingEventCounter;
ok(true, 'Error event firing on broken stylesheet');
}
document.body.appendChild(link);
++pendingEventCounter;
link = document.createElement("link");
link.rel = "stylesheet";
link.href = "data:text/css,*{}";
link.onload = function() { --pendingEventCounter;
ok(true, 'Load event firing on external stylesheet');
};
link.onerror = function() { --pendingEventCounter;
ok(false, 'Error event firing on external stylesheet');
}
document.body.appendChild(link);
// Make sure we have that last stylesheet
is(document.styleSheets.length, 7, "Should have seven stylesheets here");
// Make sure that the sixth stylesheet is all loaded
// If we ever switch away from sync loading of already-complete sheets, this
// test will need adjusting
is(document.styleSheets[6].cssRules.length, 1, "Should have one rule");
++pendingEventCounter;
link = document.createElement("link");
link.rel = "stylesheet";
link.href = "data:text/css,@import url('data:text/css,*{}')";
link.onload = function() { --pendingEventCounter;
ok(true, 'Load event firing on external stylesheet');
};
link.onerror = function() { --pendingEventCounter;
ok(false, 'Error event firing on external stylesheet');
}
document.body.appendChild(link);
++pendingEventCounter;
link = document.createElement("link");
link.rel = "stylesheet";
link.href = "data:text/css,@import url('http://www.example.com')";
link.onload = function() { --pendingEventCounter;
ok(false, 'Load event firing on broken stylesheet');
};
link.onerror = function() { --pendingEventCounter;
ok(true, 'Error event firing on broken stylesheet');
}
document.body.appendChild(link);
// Make sure the load events for all those stylesheets have not fired yet
is(pendingEventCounter, 7, "There should be one pending event");
</script>
</pre>
</body>
</html>