summaryrefslogtreecommitdiff
path: root/uriloader
diff options
context:
space:
mode:
authorwolfbeast <mcwerewolf@wolfbeast.com>2019-12-22 19:57:30 +0100
committerwolfbeast <mcwerewolf@wolfbeast.com>2019-12-22 19:57:30 +0100
commitdfa7af70ce4cd662add88f5e2a881e1054d91ef2 (patch)
treecde1088a23371942359540a12a0c70bf8a85ac50 /uriloader
parent54091ecab46c93c2e1b2c689e9179a980beaabe6 (diff)
downloaduxp-dfa7af70ce4cd662add88f5e2a881e1054d91ef2.tar.gz
Issue #1118 - Part 5: Change the way document.open() works
This changes the work we do for document.open() in the following ways: - We no longer create a new Window when doing document.open(). We use the same Window but remove all the event listeners on the existing DOM tree and Window before removing the document's existing children to provide a clean slate document to use for .write(). - We no longer create a session history entry (previously would be a wyciwyg URI). We now replace the current one, effectively losing the entry for the original document. - We now support document.open() on windowless documents.
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)