From b0ce4094bfcc1a99a2aea5bac64b315dc529d0f6 Mon Sep 17 00:00:00 2001 From: "bzbarsky%mit.edu" Date: Fri, 13 May 2005 19:54:28 +0000 Subject: [PATCH] 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 --- mozilla/content/base/public/nsIDocument.h | 15 ++- mozilla/content/base/src/nsDocument.cpp | 98 +++++++++++++++++++ mozilla/content/base/src/nsDocument.h | 20 ++++ .../base/src/nsImageLoadingContent.cpp | 44 +++------ 4 files changed, 147 insertions(+), 30 deletions(-) diff --git a/mozilla/content/base/public/nsIDocument.h b/mozilla/content/base/public/nsIDocument.h index f6289caef26..045e797b933 100644 --- a/mozilla/content/base/public/nsIDocument.h +++ b/mozilla/content/base/public/nsIDocument.h @@ -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 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() { diff --git a/mozilla/content/base/src/nsDocument.cpp b/mozilla/content/base/src/nsDocument.cpp index 911ba17e451..71bd1c99d90 100644 --- a/mozilla/content/base/src/nsDocument.cpp +++ b/mozilla/content/base/src/nsDocument.cpp @@ -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 loadGroup = GetDocumentLoadGroup(); + if (loadGroup) { + loadGroup->AddRequest(mOnloadBlocker, nsnull); + } + } + ++mOnloadBlockCount; +} + +void +nsDocument::UnblockOnload() +{ + if (mOnloadBlockCount == 0) { + return; + } + + --mOnloadBlockCount; + + if (mOnloadBlockCount == 0) { + nsCOMPtr loadGroup = GetDocumentLoadGroup(); + if (loadGroup) { + loadGroup->RemoveRequest(mOnloadBlocker, nsnull, NS_OK); + } + } +} diff --git a/mozilla/content/base/src/nsDocument.h b/mozilla/content/base/src/nsDocument.h index 75efba8923a..a1f7bdc104e 100644 --- a/mozilla/content/base/src/nsDocument.h +++ b/mozilla/content/base/src/nsDocument.h @@ -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) 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 mLayoutHistoryState; + + PRUint32 mOnloadBlockCount; + nsCOMPtr mOnloadBlocker; }; diff --git a/mozilla/content/base/src/nsImageLoadingContent.cpp b/mozilla/content/base/src/nsImageLoadingContent.cpp index 2e1d4c067dc..fcd96f06ccd 100644 --- a/mozilla/content/base/src/nsImageLoadingContent.cpp +++ b/mozilla/content/base/src/nsImageLoadingContent.cpp @@ -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 mPresContext; nsCOMPtr mContent; nsString mMessage; - nsCOMPtr 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 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 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 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); }