summaryrefslogtreecommitdiff
path: root/uriloader
diff options
context:
space:
mode:
Diffstat (limited to 'uriloader')
-rw-r--r--uriloader/base/nsDocLoader.cpp102
-rw-r--r--uriloader/base/nsDocLoader.h23
2 files changed, 111 insertions, 14 deletions
diff --git a/uriloader/base/nsDocLoader.cpp b/uriloader/base/nsDocLoader.cpp
index 69885b93f8..a4beb48278 100644
--- a/uriloader/base/nsDocLoader.cpp
+++ b/uriloader/base/nsDocLoader.cpp
@@ -4,6 +4,8 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nspr.h"
+#include "mozilla/BasicEvents.h"
+#include "mozilla/EventDispatcher.h"
#include "mozilla/Logging.h"
#include "nsDocLoader.h"
@@ -23,6 +25,7 @@
#include "nsQueryObject.h"
#include "nsIDOMWindow.h"
+#include "nsPIDOMWindow.h"
#include "nsIStringBundle.h"
#include "nsIScriptSecurityManager.h"
@@ -36,7 +39,10 @@
#include "nsIAsyncVerifyRedirectCallback.h"
using mozilla::DebugOnly;
+using mozilla::eLoad;
+using mozilla::EventDispatcher;
using mozilla::LogLevel;
+using mozilla::WidgetEvent;
static NS_DEFINE_CID(kThisImplCID, NS_THIS_DOCLOADER_IMPL_CID);
@@ -114,7 +120,8 @@ nsDocLoader::nsDocLoader()
mIsLoadingDocument(false),
mIsRestoringDocument(false),
mDontFlushLayout(false),
- mIsFlushingLayout(false)
+ mIsFlushingLayout(false),
+ mDocumentOpenedButNotLoaded(false)
{
ClearInternalProgress();
@@ -289,7 +296,7 @@ nsDocLoader::IsBusy()
}
/* Is this document loader busy? */
- if (!mIsLoadingDocument) {
+ if (!IsBlockingLoadEvent()) {
return false;
}
@@ -414,6 +421,7 @@ nsDocLoader::OnStartRequest(nsIRequest *request, nsISupports *aCtxt)
if (!mIsLoadingDocument && (loadFlags & nsIChannel::LOAD_DOCUMENT_URI)) {
bJustStartedLoading = true;
mIsLoadingDocument = true;
+ mDocumentOpenedButNotLoaded = false;
ClearInternalProgress(); // only clear our progress if we are starting a new load....
}
@@ -480,10 +488,11 @@ nsDocLoader::OnStopRequest(nsIRequest *aRequest,
mLoadGroup->GetActiveCount(&count);
MOZ_LOG(gDocLoaderLog, LogLevel::Debug,
- ("DocLoader:%p: OnStopRequest[%p](%s) status=%x mIsLoadingDocument=%s, %u active URLs",
+ ("DocLoader:%p: OnStopRequest[%p](%s) status=%x"
+ "mIsLoadingDocument=%s, mDocumentOpenedButNotLoaded=%s, %u active URLs",
this, aRequest, name.get(),
aStatus, (mIsLoadingDocument ? "true" : "false"),
- count));
+ (mDocumentOpenedButNotLoaded ? "true" : "false"), count));
}
bool bFireTransferring = false;
@@ -598,10 +607,9 @@ nsDocLoader::OnStopRequest(nsIRequest *aRequest,
RemoveRequestInfo(aRequest);
//
- // Only fire the DocLoaderIsEmpty(...) if the document loader has initiated a
- // load. This will handle removing the request from our hashtable as needed.
+ // Only fire the DocLoaderIsEmpty(...) if we may need to fire onload.
//
- if (mIsLoadingDocument) {
+ if (IsBlockingLoadEvent()) {
nsCOMPtr<nsIDocShell> ds = do_QueryInterface(static_cast<nsIRequestObserver*>(this));
bool doNotFlushLayout = false;
if (ds) {
@@ -647,7 +655,7 @@ NS_IMETHODIMP nsDocLoader::GetDocumentChannel(nsIChannel ** aChannel)
void nsDocLoader::DocLoaderIsEmpty(bool aFlushLayout)
{
- if (mIsLoadingDocument) {
+ if (IsBlockingLoadEvent()) {
/* In the unimagineably rude circumstance that onload event handlers
triggered by this function actually kill the window ... ok, it's
not unimagineable; it's happened ... this deathgrip keeps this object
@@ -660,7 +668,9 @@ void nsDocLoader::DocLoaderIsEmpty(bool aFlushLayout)
}
NS_ASSERTION(!mIsFlushingLayout, "Someone screwed up");
- NS_ASSERTION(mDocumentRequest, "No Document Request!");
+ // We may not have a document request if we are in a document.open() situation.
+ NS_ASSERTION(mDocumentRequest || mDocumentOpenedButNotLoaded,
+ "No Document Request!");
// The load group for this DocumentLoader is idle. Flush if we need to.
if (aFlushLayout && !mDontFlushLayout) {
@@ -687,9 +697,14 @@ void nsDocLoader::DocLoaderIsEmpty(bool aFlushLayout)
// And now check whether we're really busy; that might have changed with
// the layout flush.
- // Note, mDocumentRequest can be null if the flushing above re-entered this
- // method.
- if (!IsBusy() && mDocumentRequest) {
+ //
+ // Note, mDocumentRequest can be null while mDocumentOpenedButNotLoaded is
+ // false if the flushing above re-entered this method. Exit in that case.
+ if (IsBusy() || (!mDocumentRequest && !mDocumentOpenedButNotLoaded)) {
+ return;
+ }
+
+ if (mDocumentRequest) {
// Clear out our request info hash, now that our load really is done and
// we don't need it anymore to CalculateMaxProgress().
ClearInternalProgress();
@@ -715,7 +730,7 @@ void nsDocLoader::DocLoaderIsEmpty(bool aFlushLayout)
//
mLoadGroup->SetDefaultLoadRequest(nullptr);
- // Take a ref to our parent now so that we can call DocLoaderIsEmpty() on
+ // Take a ref to our parent now so that we can call ChildDoneWithOnload() on
// it even if our onload handler removes us from the docloader tree.
RefPtr<nsDocLoader> parent = mParent;
@@ -733,6 +748,67 @@ void nsDocLoader::DocLoaderIsEmpty(bool aFlushLayout)
parent->ChildDoneWithOnload(this);
}
}
+ } else {
+ MOZ_ASSERT(mDocumentOpenedButNotLoaded);
+ mDocumentOpenedButNotLoaded = false;
+
+ // Make sure we do the ChildEnteringOnload/ChildDoneWithOnload even if we
+ // plan to skip firing our own load event, because otherwise we might
+ // never end up firing our parent's load event.
+ RefPtr<nsDocLoader> parent = mParent;
+ if (!parent || parent->ChildEnteringOnload(this)) {
+ nsresult loadGroupStatus = NS_OK;
+ mLoadGroup->GetStatus(&loadGroupStatus);
+ // Make sure we're not canceling the loadgroup. If we are, then we should
+ // not fire a load event just like in the normal navigation case.
+ if (NS_SUCCEEDED(loadGroupStatus) ||
+ loadGroupStatus == NS_ERROR_PARSED_DATA_CACHED) {
+ // Can "doc" or "window" ever come back null here? Our state machine
+ // is complicated enough, so I wouldn't bet against it...
+ nsCOMPtr<nsIDocument> doc = do_GetInterface(GetAsSupports(this));
+ if (doc) {
+ doc->SetReadyStateInternal(nsIDocument::READYSTATE_COMPLETE,
+ /* updateTimingInformation = */ false);
+
+ nsCOMPtr<nsPIDOMWindowOuter> window = doc->GetWindow();
+ if (window && !doc->SkipLoadEventAfterClose()) {
+ MOZ_LOG(gDocLoaderLog, LogLevel::Debug,
+ ("DocLoader:%p: Firing load event for document.open\n",
+ this));
+
+ // This is a very cut-down version of
+ // nsDocumentViewer::LoadComplete that doesn't do various things
+ // that are not relevant here because this wasn't an actual
+ // navigation.
+ WidgetEvent event(true, eLoad);
+ event.mFlags.mBubbles = false;
+ event.mFlags.mCancelable = false;
+ // Dispatching to |window|, but using |document| as the target,
+ // per spec.
+ event.mTarget = doc;
+ nsEventStatus unused = nsEventStatus_eIgnore;
+ doc->SetLoadEventFiring(true);
+ EventDispatcher::Dispatch(window, nullptr, &event, nullptr,
+ &unused);
+ doc->SetLoadEventFiring(false);
+
+ // Now unsuppress painting on the presshell, if we
+ // haven't done that yet.
+ nsCOMPtr<nsIPresShell> shell = doc->GetShell();
+ if (shell && !shell->IsDestroying()) {
+ shell->UnsuppressPainting();
+
+ if (!shell->IsDestroying()) {
+ shell->LoadComplete();
+ }
+ }
+ }
+ }
+ }
+ if (parent) {
+ parent->ChildDoneWithOnload(this);
+ }
+ }
}
}
}
diff --git a/uriloader/base/nsDocLoader.h b/uriloader/base/nsDocLoader.h
index 481b1397bc..b469b8e079 100644
--- a/uriloader/base/nsDocLoader.h
+++ b/uriloader/base/nsDocLoader.h
@@ -109,6 +109,8 @@ public:
unsigned long mNotifyMask;
};
+ void SetDocumentOpenedButNotLoaded() { mDocumentOpenedButNotLoaded = true; }
+
protected:
virtual ~nsDocLoader();
@@ -302,6 +304,15 @@ protected:
bool mIsFlushingLayout;
private:
+ /**
+ * This flag indicates that the loader is waiting for completion of
+ * a document.open-triggered "document load". This is set when
+ * document.open() happens and sets up a new parser and cleared out
+ * when we go to fire our load event or end up with a new document
+ * channel.
+ */
+ bool mDocumentOpenedButNotLoaded;
+
static const PLDHashTableOps sRequestInfoHashOps;
// A list of kids that are in the middle of their onload calls and will let
@@ -324,10 +335,20 @@ private:
nsRequestInfo *GetRequestInfo(nsIRequest* aRequest);
void ClearRequestInfoHash();
int64_t CalculateMaxProgress();
-/// void DumpChannelInfo(void);
+ /// void DumpChannelInfo(void);
// used to clear our internal progress state between loads...
void ClearInternalProgress();
+
+ /**
+ * Used to test whether we might need to fire a load event. This
+ * can happen when we have a document load going on, or when we've
+ * had document.open() called and haven't fired the corresponding
+ * load event yet.
+ */
+ bool IsBlockingLoadEvent() const {
+ return mIsLoadingDocument || mDocumentOpenedButNotLoaded;
+ }
};
NS_DEFINE_STATIC_IID_ACCESSOR(nsDocLoader, NS_THIS_DOCLOADER_IMPL_CID)