Add a document api to prevent onload from firing too early. Bug 293818,

r=darin, sr=jst, a=asa


git-svn-id: svn://10.0.0.236/trunk@173388 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
bzbarsky%mit.edu
2005-05-13 19:54:28 +00:00
parent 3db506fbed
commit b0ce4094bf
4 changed files with 147 additions and 30 deletions

View File

@@ -90,8 +90,8 @@ class nsILayoutHistoryState;
// IID for the nsIDocument interface
#define NS_IDOCUMENT_IID \
{ 0x5cb398a4, 0xbd6f, 0x4af5, \
{ 0x81, 0x56, 0x59, 0xfd, 0xcf, 0xe6, 0xab, 0x45 } }
{ 0x660e9925, 0x30e0, 0x4016, \
{ 0x8b, 0x7f, 0x0d, 0x70, 0x6e, 0xba, 0xc9, 0x8e } }
// The base value for the content ID counter.
// This counter is used by the document to
@@ -713,6 +713,17 @@ public:
*/
virtual already_AddRefed<nsILayoutHistoryState> GetLayoutHistoryState() const = 0;
/**
* Methods that can be used to prevent onload firing while an event that
* should block onload is posted. onload is guaranteed to not fire until
* either all calls to BlockOnload() have been matched by calls to
* UnblockOnload() or the load has been stopped altogether (by the user
* pressing the Stop button, say). onload may fire synchronously from inside
* the UnblockOnload() call.
*/
virtual void BlockOnload() = 0;
virtual void UnblockOnload() = 0;
protected:
~nsIDocument()
{

View File

@@ -277,6 +277,72 @@ nsDOMStyleSheetList::StyleSheetRemoved(nsIDocument *aDocument,
}
}
// nsOnloadBlocker implementation
NS_IMPL_ISUPPORTS1(nsOnloadBlocker, nsIRequest)
NS_IMETHODIMP
nsOnloadBlocker::GetName(nsACString &aResult)
{
aResult.AssignLiteral("about:document-onload-blocker");
return NS_OK;
}
NS_IMETHODIMP
nsOnloadBlocker::IsPending(PRBool *_retval)
{
*_retval = PR_TRUE;
return NS_OK;
}
NS_IMETHODIMP
nsOnloadBlocker::GetStatus(nsresult *status)
{
*status = NS_OK;
return NS_OK;
}
NS_IMETHODIMP
nsOnloadBlocker::Cancel(nsresult status)
{
return NS_OK;
}
NS_IMETHODIMP
nsOnloadBlocker::Suspend(void)
{
return NS_OK;
}
NS_IMETHODIMP
nsOnloadBlocker::Resume(void)
{
return NS_OK;
}
NS_IMETHODIMP
nsOnloadBlocker::GetLoadGroup(nsILoadGroup * *aLoadGroup)
{
*aLoadGroup = nsnull;
return NS_OK;
}
NS_IMETHODIMP
nsOnloadBlocker::SetLoadGroup(nsILoadGroup * aLoadGroup)
{
return NS_OK;
}
NS_IMETHODIMP
nsOnloadBlocker::GetLoadFlags(nsLoadFlags *aLoadFlags)
{
*aLoadFlags = nsIRequest::LOAD_NORMAL;
return NS_OK;
}
NS_IMETHODIMP
nsOnloadBlocker::SetLoadFlags(nsLoadFlags aLoadFlags)
{
return NS_OK;
}
class nsDOMImplementation : public nsIDOMDOMImplementation,
public nsIPrivateDOMImplementation
@@ -684,6 +750,9 @@ nsDocument::Init()
// (static cast to the correct interface pointer)
mObservers.InsertElementAt(NS_STATIC_CAST(nsIDocumentObserver*, bindingManager), 0);
mOnloadBlocker = new nsOnloadBlocker();
NS_ENSURE_TRUE(mOnloadBlocker, NS_ERROR_OUT_OF_MEMORY);
NS_NewCSSLoader(this, &mCSSLoader);
NS_ENSURE_TRUE(mCSSLoader, NS_ERROR_OUT_OF_MEMORY);
// Assume we're not HTML and not quirky, until we know otherwise
@@ -4808,3 +4877,32 @@ nsDocument::GetLayoutHistoryState() const
return state;
}
void
nsDocument::BlockOnload()
{
if (mOnloadBlockCount == 0) {
nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup();
if (loadGroup) {
loadGroup->AddRequest(mOnloadBlocker, nsnull);
}
}
++mOnloadBlockCount;
}
void
nsDocument::UnblockOnload()
{
if (mOnloadBlockCount == 0) {
return;
}
--mOnloadBlockCount;
if (mOnloadBlockCount == 0) {
nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup();
if (loadGroup) {
loadGroup->RemoveRequest(mOnloadBlocker, nsnull, NS_OK);
}
}
}

View File

@@ -81,6 +81,8 @@
#include "nsIRadioGroupContainer.h"
#include "nsIScriptEventManager.h"
#include "nsILayoutHistoryState.h"
#include "nsIRequest.h"
#include "nsILoadGroup.h"
// Put these here so all document impls get them automatically
#include "nsHTMLStyleSheet.h"
@@ -106,6 +108,7 @@ class nsXPathDocumentTearoff;
class nsIRadioVisitor;
class nsIFormControl;
struct nsRadioGroupStruct;
class nsOnloadBlocker;
class nsDocHeaderData
@@ -172,6 +175,17 @@ protected:
void* mScriptObject;
};
class nsOnloadBlocker : public nsIRequest
{
public:
nsOnloadBlocker() {}
NS_DECL_ISUPPORTS
NS_DECL_NSIREQUEST
private:
~nsOnloadBlocker() {}
};
// Base class for our document implementations.
//
@@ -540,6 +554,9 @@ public:
virtual NS_HIDDEN_(void) Destroy();
virtual NS_HIDDEN_(already_AddRefed<nsILayoutHistoryState>) GetLayoutHistoryState() const;
virtual NS_HIDDEN_(void) BlockOnload();
virtual NS_HIDDEN_(void) UnblockOnload();
protected:
void RetrieveRelevantHeaders(nsIChannel *aChannel);
@@ -636,6 +653,9 @@ private:
// 1) We have no script global object.
// 2) We haven't had Destroy() called on us yet.
nsCOMPtr<nsILayoutHistoryState> mLayoutHistoryState;
PRUint32 mOnloadBlockCount;
nsCOMPtr<nsIRequest> mOnloadBlocker;
};

View File

@@ -604,17 +604,15 @@ nsImageLoadingContent::StringToURI(const nsAString& aSpec,
*/
MOZ_DECL_CTOR_COUNTER(ImageEvent)
class ImageEvent : public PLEvent,
public nsDummyLayoutRequest
class ImageEvent : public PLEvent
{
public:
ImageEvent(nsPresContext* aPresContext, nsIContent* aContent,
const nsAString& aMessage, nsILoadGroup *aLoadGroup)
: nsDummyLayoutRequest(nsnull),
mPresContext(aPresContext),
const nsAString& aMessage, nsIDocument* aDocument)
: mPresContext(aPresContext),
mContent(aContent),
mMessage(aMessage),
mLoadGroup(aLoadGroup)
mDocument(aDocument)
{
MOZ_COUNT_CTOR(ImageEvent);
}
@@ -626,7 +624,10 @@ public:
nsCOMPtr<nsPresContext> mPresContext;
nsCOMPtr<nsIContent> mContent;
nsString mMessage;
nsCOMPtr<nsILoadGroup> mLoadGroup;
// Need to hold on to the document in case our event outlives document
// teardown... Wantto be able to get back to the document even if the
// prescontext and content can't.
nsCOMPtr<nsIDocument> mDocument;
};
PR_STATIC_CALLBACK(void*)
@@ -646,8 +647,6 @@ HandleImagePLEvent(PLEvent* aEvent)
evt->mContent->HandleDOMEvent(evt->mPresContext, &event, nsnull,
NS_EVENT_FLAG_INIT, &estatus);
evt->mLoadGroup->RemoveRequest(evt, nsnull, NS_OK);
return nsnull;
}
@@ -655,12 +654,9 @@ PR_STATIC_CALLBACK(void)
DestroyImagePLEvent(PLEvent* aEvent)
{
ImageEvent* evt = NS_STATIC_CAST(ImageEvent*, aEvent);
evt->mDocument->UnblockOnload();
// We're reference counted, and we hold a strong reference to
// ourselves while we're a 'live' PLEvent. Now that the PLEvent is
// destroyed, release ourselves.
NS_RELEASE(evt);
delete evt;
}
nsresult
@@ -687,8 +683,6 @@ nsImageLoadingContent::FireEvent(const nsAString& aEventType)
getter_AddRefs(eventQ));
NS_ENSURE_TRUE(eventQ, rv);
nsCOMPtr<nsILoadGroup> loadGroup = document->GetDocumentLoadGroup();
nsIPresShell *shell = document->GetShellAt(0);
NS_ENSURE_TRUE(shell, NS_ERROR_FAILURE);
@@ -697,25 +691,19 @@ nsImageLoadingContent::FireEvent(const nsAString& aEventType)
nsCOMPtr<nsIContent> ourContent = do_QueryInterface(this);
ImageEvent* evt = new ImageEvent(presContext, ourContent, aEventType,
loadGroup);
ImageEvent* evt = new ImageEvent(presContext, ourContent, aEventType, document);
NS_ENSURE_TRUE(evt, NS_ERROR_OUT_OF_MEMORY);
PL_InitEvent(evt, this, ::HandleImagePLEvent, ::DestroyImagePLEvent);
// The event will own itself while it's in the event queue, once
// removed, it will release itself, and if there are no other
// references, it will be deleted.
NS_ADDREF(evt);
// Block onload for our event. Since we unblock in the event destructor, we
// want to block now, even if posting will fail.
document->BlockOnload();
rv = eventQ->PostEvent(evt);
if (NS_SUCCEEDED(rv)) {
// Add the dummy request (the ImageEvent) to the load group only
// after all the early returns here!
loadGroup->AddRequest(evt, nsnull);
} else {
if (NS_FAILED(rv)) {
PL_DestroyEvent(evt);
}