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:
@@ -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()
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user