diff --git a/content/base/public/nsIDocument.h b/content/base/public/nsIDocument.h index b5bce997fdb8..c73e8f52c75f 100644 --- a/content/base/public/nsIDocument.h +++ b/content/base/public/nsIDocument.h @@ -101,8 +101,8 @@ class nsFrameLoader; // IID for the nsIDocument interface #define NS_IDOCUMENT_IID \ -{ 0x29f7a5d7, 0xb217, 0x4ea2, \ - {0x95, 0x40, 0x46, 0x41, 0xb9, 0xf5, 0x99, 0xd9 } } +{ 0xdd9bd470, 0x6315, 0x4e67, \ + { 0xa8, 0x8a, 0x78, 0xbf, 0x92, 0xb4, 0x5a, 0xdf } } // Flag for AddStyleSheet(). #define NS_STYLESHEET_FROM_CATALOG (1 << 0) @@ -1128,6 +1128,20 @@ public: virtual nsSMILAnimationController* GetAnimationController() = 0; #endif // MOZ_SMIL + /** + * Prevents user initiated events from being dispatched to the document and + * subdocuments. + */ + virtual void SuppressEventHandling(PRUint32 aIncrease = 1) = 0; + + virtual void UnsuppressEventHandlingAndFireEvents(PRBool aFireEvents) = 0; + + void UnsuppressEventHandling() + { + UnsuppressEventHandlingAndFireEvents(PR_TRUE); + } + + PRUint32 EventHandlingSuppressed() { return mEventsSuppressed; } protected: ~nsIDocument() { @@ -1232,6 +1246,8 @@ protected: // go to. nsCOMPtr mDisplayDocument; + PRUint32 mEventsSuppressed; + private: // JSObject cache. Only to be used for performance // optimizations. This will be set once this document is touched diff --git a/content/base/src/nsDocument.cpp b/content/base/src/nsDocument.cpp index e020f4d9f625..ce6d07bb49a1 100644 --- a/content/base/src/nsDocument.cpp +++ b/content/base/src/nsDocument.cpp @@ -6888,6 +6888,15 @@ CanCacheSubDocument(PLDHashTable *table, PLDHashEntryHdr *hdr, PRBool nsDocument::CanSavePresentation(nsIRequest *aNewRequest) { + if (EventHandlingSuppressed()) { + return PR_FALSE; + } + + nsPIDOMWindow* win = GetInnerWindow(); + if (win && win->TimeoutSuspendCount()) { + return PR_FALSE; + } + // Check our event listener manager for unload/beforeunload listeners. nsCOMPtr piTarget = do_QueryInterface(mScriptGlobalObject); if (piTarget) { @@ -7512,3 +7521,51 @@ nsDocument::GetReadyState(nsAString& aReadyState) } return NS_OK; } + +static PRBool +SuppressEventHandlingInDocument(nsIDocument* aDocument, void* aData) +{ + aDocument->SuppressEventHandling(*static_cast(aData)); + return PR_TRUE; +} + +void +nsDocument::SuppressEventHandling(PRUint32 aIncrease) +{ + mEventsSuppressed += aIncrease; + EnumerateSubDocuments(SuppressEventHandlingInDocument, &aIncrease); +} + +static PRBool +GetAndUnsuppressSubDocuments(nsIDocument* aDocument, void* aData) +{ + PRUint32 suppression = aDocument->EventHandlingSuppressed(); + if (suppression > 0) { + static_cast(aDocument)->DecreaseEventSuppression(); + } + nsCOMArray* docs = static_cast* >(aData); + docs->AppendObject(aDocument); + aDocument->EnumerateSubDocuments(GetAndUnsuppressSubDocuments, docs); + return PR_TRUE; +} + +void +nsDocument::UnsuppressEventHandlingAndFireEvents(PRBool aFireEvents) +{ + if (mEventsSuppressed > 0) { + --mEventsSuppressed; + } + nsCOMArray documents; + documents.AppendObject(this); + EnumerateSubDocuments(GetAndUnsuppressSubDocuments, &documents); + for (PRInt32 i = 0; i < documents.Count(); ++i) { + if (!documents[i]->EventHandlingSuppressed()) { + nsPresShellIterator iter(documents[i]); + nsCOMPtr shell; + while ((shell = iter.GetNextShell())) { + shell->FireOrClearDelayedEvents(aFireEvents); + } + } + } +} + diff --git a/content/base/src/nsDocument.h b/content/base/src/nsDocument.h index 5c943f33896f..6d0705b4939b 100644 --- a/content/base/src/nsDocument.h +++ b/content/base/src/nsDocument.h @@ -986,6 +986,12 @@ public: nsSMILAnimationController* GetAnimationController(); #endif // MOZ_SMIL + virtual void SuppressEventHandling(PRUint32 aIncrease); + + virtual void UnsuppressEventHandlingAndFireEvents(PRBool aFireEvents); + + void DecreaseEventSuppression() { --mEventsSuppressed; } + NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsDocument, nsIDocument) /** diff --git a/content/base/src/nsXMLHttpRequest.cpp b/content/base/src/nsXMLHttpRequest.cpp index 2fd54518a87e..c2ce3295b848 100644 --- a/content/base/src/nsXMLHttpRequest.cpp +++ b/content/base/src/nsXMLHttpRequest.cpp @@ -145,6 +145,21 @@ #define NS_PROGRESS_EVENT_INTERVAL 50 +class nsResumeTimeoutsEvent : public nsRunnable +{ +public: + nsResumeTimeoutsEvent(nsPIDOMWindow* aWindow) : mWindow(aWindow) {} + + NS_IMETHOD Run() + { + mWindow->ResumeTimeouts(PR_FALSE); + return NS_OK; + } + +private: + nsCOMPtr mWindow; +}; + NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMEventListenerWrapper) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMEventListenerWrapper) @@ -2160,7 +2175,8 @@ nsXMLHttpRequest::OnStartRequest(nsIRequest *request, nsISupports *ctxt) request->GetStatus(&status); mErrorLoad = mErrorLoad || NS_FAILED(status); - if (mUpload && !mUploadComplete && !mErrorLoad) { + if (mUpload && !mUploadComplete && !mErrorLoad && + (mState & XML_HTTP_REQUEST_ASYNC)) { mUploadComplete = PR_TRUE; DispatchProgressEvent(mUpload, NS_LITERAL_STRING(LOAD_STR), PR_TRUE, mUploadTotal, mUploadTotal); @@ -2786,16 +2802,20 @@ nsXMLHttpRequest::Send(nsIVariant *aBody) if (!(mState & XML_HTTP_REQUEST_ASYNC)) { mState |= XML_HTTP_REQUEST_SYNCLOOPING; + nsCOMPtr suspendedDoc; nsCOMPtr resumeTimeoutRunnable; if (mOwner) { nsCOMPtr topWindow; if (NS_SUCCEEDED(mOwner->GetTop(getter_AddRefs(topWindow)))) { nsCOMPtr suspendedWindow(do_QueryInterface(topWindow)); - if (suspendedWindow) { - suspendedWindow->SuspendTimeouts(); - resumeTimeoutRunnable = NS_NEW_RUNNABLE_METHOD(nsPIDOMWindow, - suspendedWindow.get(), - ResumeTimeouts); + if (suspendedWindow && + (suspendedWindow = suspendedWindow->GetCurrentInnerWindow())) { + suspendedDoc = do_QueryInterface(suspendedWindow->GetExtantDocument()); + if (suspendedDoc) { + suspendedDoc->SuppressEventHandling(); + } + suspendedWindow->SuspendTimeouts(1, PR_FALSE); + resumeTimeoutRunnable = new nsResumeTimeoutsEvent(suspendedWindow); } } } @@ -2808,6 +2828,12 @@ nsXMLHttpRequest::Send(nsIVariant *aBody) } } + if (suspendedDoc) { + NS_DispatchToCurrentThread( + NS_NEW_RUNNABLE_METHOD(nsIDocument, suspendedDoc.get(), + UnsuppressEventHandling)); + } + if (resumeTimeoutRunnable) { NS_DispatchToCurrentThread(resumeTimeoutRunnable); } @@ -3222,7 +3248,7 @@ nsXMLHttpRequest::OnProgress(nsIRequest *aRequest, nsISupports *aContext, PRUint return NS_OK; } - if (!mErrorLoad) { + if (!mErrorLoad && (mState & XML_HTTP_REQUEST_ASYNC)) { StartProgressEventTimer(); NS_NAMED_LITERAL_STRING(progress, PROGRESS_STR); NS_NAMED_LITERAL_STRING(uploadprogress, UPLOADPROGRESS_STR); @@ -3348,7 +3374,8 @@ NS_IMETHODIMP nsXMLHttpRequest::Notify(nsITimer* aTimer) { mTimerIsActive = PR_FALSE; - if (NS_SUCCEEDED(CheckInnerWindowCorrectness()) && !mErrorLoad) { + if (NS_SUCCEEDED(CheckInnerWindowCorrectness()) && !mErrorLoad && + (mState & XML_HTTP_REQUEST_ASYNC)) { if (mProgressEventWasDelayed) { mProgressEventWasDelayed = PR_FALSE; if (!(XML_HTTP_REQUEST_MPART_HEADERS & mState)) { diff --git a/content/base/test/Makefile.in b/content/base/test/Makefile.in index 74abf79b5232..442c930bd0ce 100644 --- a/content/base/test/Makefile.in +++ b/content/base/test/Makefile.in @@ -146,6 +146,7 @@ _TEST_FILES = test_bug5141.html \ file_bug326337.xml \ file_bug326337_multipart.txt \ file_bug326337_multipart.txt^headers^ \ + test_bug333198.html \ test_bug402150.html \ test_bug402150.html^headers^ \ test_bug401662.html \ diff --git a/content/base/test/test_bug333198.html b/content/base/test/test_bug333198.html new file mode 100644 index 000000000000..573df31ee345 --- /dev/null +++ b/content/base/test/test_bug333198.html @@ -0,0 +1,73 @@ + + + + + Test for Bug 333198 + + + + + +
+Mozilla Bug 333198 +

+ +
+
+
+ + diff --git a/content/events/src/nsEventStateManager.cpp b/content/events/src/nsEventStateManager.cpp index fceab57ac285..6de88ecf77d7 100644 --- a/content/events/src/nsEventStateManager.cpp +++ b/content/events/src/nsEventStateManager.cpp @@ -260,6 +260,40 @@ PrintDocTreeAll(nsIDocShellTreeItem* aItem) } #endif +static nsIDocument* +EventHandlingSuppressed(nsPIDOMEventTarget* aTarget) +{ + nsCOMPtr node = do_QueryInterface(aTarget); + nsCOMPtr doc; + if (node) { + doc = node->GetOwnerDoc(); + } else { + nsCOMPtr window = do_QueryInterface(aTarget); + if (window) { + doc = do_QueryInterface(window->GetExtantDocument()); + } + } + + return (doc && doc->EventHandlingSuppressed()) ? doc.get() : nsnull; +} + +static void +FireBlurEvent(nsPIDOMEventTarget* aTarget, nsEvent* aEvent, nsPresContext* aContext) +{ + NS_ASSERTION(aEvent->message == NS_BLUR_CONTENT, "Wrong event!"); + nsIDocument* doc = EventHandlingSuppressed(aTarget); + if (doc) { + if (aContext) { + nsIPresShell* shell = aContext->GetPresShell(); + if (shell) { + shell->NeedsBlurAfterSuppression(aTarget); + } + } + } else if (aTarget) { + nsEventDispatcher::Dispatch(aTarget, aContext, aEvent); + } +} + class nsUITimerCallback : public nsITimerCallback { public: @@ -1092,14 +1126,11 @@ nsEventStateManager::PreHandleEvent(nsPresContext* aPresContext, if (!isAlreadySuppressed) { // Fire the blur event on the previously focused document. + nsEvent blurEvent(PR_TRUE, NS_BLUR_CONTENT); + blurEvent.flags |= NS_EVENT_FLAG_CANT_BUBBLE; - nsEventStatus blurstatus = nsEventStatus_eIgnore; - nsEvent blurevent(PR_TRUE, NS_BLUR_CONTENT); - blurevent.flags |= NS_EVENT_FLAG_CANT_BUBBLE; - - nsEventDispatcher::Dispatch(gLastFocusedDocument, - gLastFocusedPresContextWeak, - &blurevent, nsnull, &blurstatus); + FireBlurEvent(gLastFocusedDocument, &blurEvent, + gLastFocusedPresContextWeak); nsCOMPtr esm; if (!mCurrentFocus && gLastFocusedContent) { @@ -1120,16 +1151,15 @@ nsEventStateManager::PreHandleEvent(nsPresContext* aPresContext, } } nsCOMPtr blurContent = gLastFocusedContent; - blurevent.target = nsnull; - nsEventDispatcher::Dispatch(gLastFocusedContent, - gLastFocusedPresContextWeak, - &blurevent, nsnull, &blurstatus); + blurEvent.target = nsnull; + FireBlurEvent(gLastFocusedContent, &blurEvent, + gLastFocusedPresContextWeak); } if (ourWindow) { // Clear the target so that Dispatch can set it back correctly. - blurevent.target = nsnull; - nsEventDispatcher::Dispatch(ourWindow, gLastFocusedPresContextWeak, - &blurevent, nsnull, &blurstatus); + blurEvent.target = nsnull; + nsCOMPtr win = do_QueryInterface(ourWindow); + FireBlurEvent(win, &blurEvent, gLastFocusedPresContextWeak); } if (esm) { @@ -1259,10 +1289,8 @@ nsEventStateManager::PreHandleEvent(nsPresContext* aPresContext, // Now fire blurs. We fire a blur on the focused document, element, // and window. - - nsEventStatus status = nsEventStatus_eIgnore; - nsEvent event(PR_TRUE, NS_BLUR_CONTENT); - event.flags |= NS_EVENT_FLAG_CANT_BUBBLE; + nsEvent blurEvent(PR_TRUE, NS_BLUR_CONTENT); + blurEvent.flags |= NS_EVENT_FLAG_CANT_BUBBLE; if (gLastFocusedDocument && gLastFocusedPresContextWeak) { if (gLastFocusedContent) { @@ -1278,8 +1306,7 @@ nsEventStateManager::PreHandleEvent(nsPresContext* aPresContext, nsCOMPtr esm = oldPresContext->EventStateManager(); esm->SetFocusedContent(gLastFocusedContent); - nsEventDispatcher::Dispatch(gLastFocusedContent, oldPresContext, - &event, nsnull, &status); + FireBlurEvent(gLastFocusedContent, &blurEvent, oldPresContext); esm->SetFocusedContent(nsnull); NS_IF_RELEASE(gLastFocusedContent); } @@ -1301,15 +1328,13 @@ nsEventStateManager::PreHandleEvent(nsPresContext* aPresContext, nsCOMPtr window(lastFocusedDocument->GetWindow()); - event.target = nsnull; - nsEventDispatcher::Dispatch(lastFocusedDocument, - lastFocusedPresContext, - &event, nsnull, &status); + blurEvent.target = nsnull; + FireBlurEvent(lastFocusedDocument, &blurEvent, lastFocusedPresContext); if (window) { - event.target = nsnull; - nsEventDispatcher::Dispatch(window, lastFocusedPresContext, - &event, nsnull, &status); + blurEvent.target = nsnull; + nsCOMPtr win = do_QueryInterface(window); + FireBlurEvent(win, &blurEvent ,lastFocusedPresContext); } } } @@ -1442,10 +1467,9 @@ nsEventStateManager::PreHandleEvent(nsPresContext* aPresContext, mFirstDocumentBlurEvent = gLastFocusedDocument; clearFirstDocumentBlurEvent = PR_TRUE; } - - nsEventStatus status = nsEventStatus_eIgnore; - nsEvent event(PR_TRUE, NS_BLUR_CONTENT); - event.flags |= NS_EVENT_FLAG_CANT_BUBBLE; + + nsEvent blurEvent(PR_TRUE, NS_BLUR_CONTENT); + blurEvent.flags |= NS_EVENT_FLAG_CANT_BUBBLE; if (gLastFocusedContent) { nsIPresShell *shell = gLastFocusedDocument->GetPrimaryShell(); @@ -1463,8 +1487,7 @@ nsEventStateManager::PreHandleEvent(nsPresContext* aPresContext, nsCOMPtr focusedContent = do_QueryInterface(focusedElement); if (focusedContent) { // Blur the element. - nsEventDispatcher::Dispatch(focusedContent, oldPresContext, - &event, nsnull, &status); + FireBlurEvent(focusedContent, &blurEvent, oldPresContext); } esm->SetFocusedContent(nsnull); @@ -1479,14 +1502,13 @@ nsEventStateManager::PreHandleEvent(nsPresContext* aPresContext, gLastFocusedPresContextWeak = nsnull; // fire blur on document and window - event.target = nsnull; - nsEventDispatcher::Dispatch(mDocument, aPresContext, &event, nsnull, - &status); + blurEvent.target = nsnull; + FireBlurEvent(mDocument, &blurEvent, aPresContext); if (ourWindow) { - event.target = nsnull; - nsEventDispatcher::Dispatch(ourWindow, aPresContext, &event, nsnull, - &status); + blurEvent.target = nsnull; + nsCOMPtr win = do_QueryInterface(ourWindow); + FireBlurEvent(win, &blurEvent, aPresContext); } if (clearFirstDocumentBlurEvent) { mFirstDocumentBlurEvent = nsnull; @@ -4322,7 +4344,7 @@ nsEventStateManager::ShiftFocusInternal(PRBool aForward, nsIContent* aStart) if (doc) { nsIDocument *sub_doc = doc->GetSubDocumentFor(nextFocus); - if (sub_doc) { + if (sub_doc && !sub_doc->EventHandlingSuppressed()) { nsCOMPtr container = sub_doc->GetContainer(); sub_shell = do_QueryInterface(container); } @@ -5184,9 +5206,8 @@ nsEventStateManager::SendFocusBlur(nsPresContext* aPresContext, nsCOMPtr oldPresContext = shell->GetPresContext(); //fire blur - nsEventStatus status = nsEventStatus_eIgnore; - nsEvent event(PR_TRUE, NS_BLUR_CONTENT); - event.flags |= NS_EVENT_FLAG_CANT_BUBBLE; + nsEvent blurEvent(PR_TRUE, NS_BLUR_CONTENT); + blurEvent.flags |= NS_EVENT_FLAG_CANT_BUBBLE; EnsureDocument(presShell); @@ -5217,8 +5238,7 @@ nsEventStateManager::SendFocusBlur(nsPresContext* aPresContext, nsCxPusher pusher; if (pusher.Push(temp)) { - nsEventDispatcher::Dispatch(temp, oldPresContext, &event, nsnull, - &status); + FireBlurEvent(temp, &blurEvent, oldPresContext); pusher.Pop(); } @@ -5250,9 +5270,8 @@ nsEventStateManager::SendFocusBlur(nsPresContext* aPresContext, if (gLastFocusedDocument && (gLastFocusedDocument != mDocument) && window) { - nsEventStatus status = nsEventStatus_eIgnore; - nsEvent event(PR_TRUE, NS_BLUR_CONTENT); - event.flags |= NS_EVENT_FLAG_CANT_BUBBLE; + nsEvent blurEvent(PR_TRUE, NS_BLUR_CONTENT); + blurEvent.flags |= NS_EVENT_FLAG_CANT_BUBBLE; // Make sure we're not switching command dispatchers, if so, // suppress the blurred one if it isn't already suppressed @@ -5280,8 +5299,7 @@ nsEventStateManager::SendFocusBlur(nsPresContext* aPresContext, nsCxPusher pusher; if (pusher.Push(temp)) { - nsEventDispatcher::Dispatch(temp, gLastFocusedPresContextWeak, &event, - nsnull, &status); + FireBlurEvent(temp, &blurEvent, gLastFocusedPresContextWeak); pusher.Pop(); } @@ -5296,9 +5314,9 @@ nsEventStateManager::SendFocusBlur(nsPresContext* aPresContext, nsCOMPtr target = do_QueryInterface(window); if (pusher.Push(target)) { - nsEventDispatcher::Dispatch(window, gLastFocusedPresContextWeak, &event, - nsnull, &status); - + blurEvent.target = nsnull; + FireBlurEvent(target, &blurEvent, gLastFocusedPresContextWeak); + if (previousFocus && mCurrentFocus != previousFocus) { // The window's blur handler focused something else. // Abort firing any additional blur or focus events. @@ -5351,7 +5369,8 @@ nsEventStateManager::SendFocusBlur(nsPresContext* aPresContext, widget->SetFocus(PR_TRUE); } - if (nsnull != aContent && aContent != mFirstFocusEvent) { + if (nsnull != aContent && aContent != mFirstFocusEvent && + !EventHandlingSuppressed(aContent)) { //Store the first focus event we fire and don't refire focus //to that element while the first focus is still ongoing. @@ -5386,7 +5405,7 @@ nsEventStateManager::SendFocusBlur(nsPresContext* aPresContext, } nsIMEStateManager::OnTextStateFocus(mPresContext, mCurrentFocus); - } else if (!aContent) { + } else if (!aContent && !EventHandlingSuppressed(mDocument)) { //fire focus on document even if the content isn't focusable (ie. text) //see bugzilla bug 93521 nsEventStatus status = nsEventStatus_eIgnore; diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp index 37646433a3a2..0344be93834d 100644 --- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -6021,6 +6021,14 @@ nsDocShell::RestoreFromHistory() nsCOMPtr document = do_QueryInterface(domDoc); if (document) { + nsCOMPtr parent; + GetParent(getter_AddRefs(parent)); + nsCOMPtr parentDoc = do_GetInterface(parent); + nsCOMPtr d = do_QueryInterface(parentDoc); + if (d && d->EventHandlingSuppressed()) { + document->SuppressEventHandling(d->EventHandlingSuppressed()); + } + // Use the uri from the mLSHE we had when we entered this function // (which need not match the document's URI if anchors are involved), // since that's the history entry we're loading. Note that if we use diff --git a/dom/interfaces/base/nsIDOMWindowUtils.idl b/dom/interfaces/base/nsIDOMWindowUtils.idl index cd6abaf30c98..cffe92a7d8df 100644 --- a/dom/interfaces/base/nsIDOMWindowUtils.idl +++ b/dom/interfaces/base/nsIDOMWindowUtils.idl @@ -48,7 +48,7 @@ interface nsIDOMElement; interface nsIDOMHTMLCanvasElement; -[scriptable, uuid(8C6263C9-F3EF-419d-80EF-D5D716635FAA)] +[scriptable, uuid(6e3510b9-806d-4a2a-be79-73d2a495b4b8)] interface nsIDOMWindowUtils : nsISupports { /** @@ -298,6 +298,15 @@ interface nsIDOMWindowUtils : nsISupports { */ readonly attribute boolean isMozAfterPaintPending; + /** + * Suppresses/unsuppresses user initiated event handling in window's document + * and subdocuments. + * + * @throw NS_ERROR_DOM_SECURITY_ERR if called without UniversalXPConnect + * privileges and NS_ERROR_FAILURE if window doesn't have a document. + */ + void suppressEventHandling(in boolean aSuppress); + void clearMozAfterPaintEvents(); /** diff --git a/dom/src/base/nsDOMWindowUtils.cpp b/dom/src/base/nsDOMWindowUtils.cpp index d3e4991024d6..6ad5c76bb1f4 100644 --- a/dom/src/base/nsDOMWindowUtils.cpp +++ b/dom/src/base/nsDOMWindowUtils.cpp @@ -710,3 +710,22 @@ nsDOMWindowUtils::DisableNonTestMouseEvents(PRBool aDisable) NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE); return presShell->DisableNonTestMouseEvents(aDisable); } + +NS_IMETHODIMP +nsDOMWindowUtils::SuppressEventHandling(PRBool aSuppress) +{ + PRBool hasCap = PR_FALSE; + if (NS_FAILED(nsContentUtils::GetSecurityManager()->IsCapabilityEnabled("UniversalXPConnect", &hasCap)) || !hasCap) + return NS_ERROR_DOM_SECURITY_ERR; + + nsCOMPtr doc(do_QueryInterface(mWindow->GetExtantDocument())); + NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE); + + if (aSuppress) { + doc->SuppressEventHandling(); + } else { + doc->UnsuppressEventHandling(); + } + return NS_OK; +} + diff --git a/dom/src/base/nsGlobalWindow.cpp b/dom/src/base/nsGlobalWindow.cpp index bdd844e0d1bd..779ebd4666eb 100644 --- a/dom/src/base/nsGlobalWindow.cpp +++ b/dom/src/base/nsGlobalWindow.cpp @@ -5611,9 +5611,17 @@ nsGlobalWindow::EnterModalState() return; } - static_cast - (static_cast - (top.get()))->mModalStateDepth++; + nsGlobalWindow* topWin = + static_cast(static_cast(top.get())); + if (topWin->mModalStateDepth == 0) { + NS_ASSERTION(!mSuspendedDoc, "Shouldn't have mSuspendedDoc here!"); + + mSuspendedDoc = do_QueryInterface(topWin->GetExtantDocument()); + if (mSuspendedDoc) { + mSuspendedDoc->SuppressEventHandling(); + } + } + topWin->mModalStateDepth++; } // static @@ -5709,6 +5717,22 @@ nsGlobalWindow::LeaveModalState() nsCOMPtr runner = new nsPendingTimeoutRunner(topWin); if (NS_FAILED(NS_DispatchToCurrentThread(runner))) NS_WARNING("failed to dispatch pending timeout runnable"); + + if (mSuspendedDoc) { + nsCOMPtr currentDoc = + do_QueryInterface(topWin->GetExtantDocument()); + if (currentDoc == mSuspendedDoc) { + NS_DispatchToCurrentThread( + NS_NEW_RUNNABLE_METHOD(nsIDocument, mSuspendedDoc.get(), + UnsuppressEventHandling)); + } else { + // Somehow the document was changed. + // Unsuppress event handling in the document but don't even + // try to fire events. + mSuspendedDoc->UnsuppressEventHandlingAndFireEvents(PR_FALSE); + } + mSuspendedDoc = nsnull; + } } } diff --git a/dom/src/base/nsGlobalWindow.h b/dom/src/base/nsGlobalWindow.h index a143fb582f99..ac01a7f81537 100644 --- a/dom/src/base/nsGlobalWindow.h +++ b/dom/src/base/nsGlobalWindow.h @@ -744,6 +744,8 @@ protected: nsDataHashtable mCachedXBLPrototypeHandlers; + nsCOMPtr mSuspendedDoc; + friend class nsDOMScriptableHelper; friend class nsDOMWindowUtils; friend class PostMessageEvent; diff --git a/layout/base/nsDocumentViewer.cpp b/layout/base/nsDocumentViewer.cpp index cefefc71a5f3..473064a4893c 100644 --- a/layout/base/nsDocumentViewer.cpp +++ b/layout/base/nsDocumentViewer.cpp @@ -629,6 +629,9 @@ DocumentViewerImpl::SyncParentSubDocMap() nsCOMPtr parent_doc(do_QueryInterface(dom_doc)); if (parent_doc) { + if (mDocument && parent_doc->GetSubDocumentFor(content) != mDocument) { + mDocument->SuppressEventHandling(parent_doc->EventHandlingSuppressed()); + } return parent_doc->SetSubDocumentFor(content, mDocument); } } diff --git a/layout/base/nsIPresShell.h b/layout/base/nsIPresShell.h index f61209f5aca6..df69f8879460 100644 --- a/layout/base/nsIPresShell.h +++ b/layout/base/nsIPresShell.h @@ -97,14 +97,15 @@ class nsWeakFrame; class nsIScrollableFrame; class gfxASurface; class gfxContext; +class nsPIDOMEventTarget; typedef short SelectionType; typedef PRUint32 nsFrameState; -// 445e6184-5e7e-4a9b-97f7-c9391e6773d2 -#define NS_IPRESSHELL_IID \ -{ 0x445e6184, 0x5e7e, 0x4a9b, \ - { 0x97, 0xf7, 0xc9, 0x39, 0x1e, 0x67, 0x73, 0xd2 } } +// 8355e7a9-4118-47dc-97e3-a3c251332e86 +#define NS_IPRESSHELL_IID \ +{ 0x8355e7a9, 0x4118, 0x47dc, \ + { 0x97, 0xe3, 0xa3, 0xc2, 0x51, 0x33, 0x2e, 0x86 } } // Constants for ScrollContentIntoView() function #define NS_PRESSHELL_SCROLL_TOP 0 @@ -692,6 +693,9 @@ public: */ virtual void Thaw() = 0; + virtual void NeedsBlurAfterSuppression(nsPIDOMEventTarget* aTarget) = 0; + virtual void FireOrClearDelayedEvents(PRBool aFireEvents) = 0; + /** * When this shell is disconnected from its containing docshell, we * lose our container pointer. However, we'd still like to be able to target diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp index b962c4982773..647666b3fa2e 100644 --- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -880,6 +880,8 @@ public: virtual nsresult ReconstructFrames(void); virtual void Freeze(); virtual void Thaw(); + virtual void NeedsBlurAfterSuppression(nsPIDOMEventTarget* aTarget); + virtual void FireOrClearDelayedEvents(PRBool aFireEvents); virtual nsIFrame* GetFrameForPoint(nsIFrame* aFrame, nsPoint aPt); @@ -1152,6 +1154,13 @@ protected: nsRevocableEventPtr mReflowEvent; + PRPackedBool mNeedsGotFocus; + PRPackedBool mNeedsLostFocus; + PRPackedBool mMozTakingFocus; + PRPackedBool mNeedsActivate; + PRPackedBool mNeedsDeactivate; + nsCOMArray mDelayedBlurTargets; + nsCallbackEventRequest* mFirstCallbackEventRequest; nsCallbackEventRequest* mLastCallbackEventRequest; @@ -5621,6 +5630,37 @@ PresShell::HandleEvent(nsIView *aView, return NS_OK; } + if (mDocument && mDocument->EventHandlingSuppressed()) { + switch (aEvent->message) { + case NS_GOTFOCUS: + mNeedsGotFocus = PR_TRUE; + mNeedsLostFocus = PR_FALSE; + mNeedsDeactivate = PR_FALSE; + break; + case NS_LOSTFOCUS: + mNeedsLostFocus = PR_TRUE; + mNeedsGotFocus = PR_FALSE; + mNeedsActivate = PR_FALSE; + mMozTakingFocus = + (aEvent->eventStructType == NS_FOCUS_EVENT && + static_cast(aEvent)->isMozWindowTakingFocus); + break; + case NS_ACTIVATE: + mNeedsActivate = PR_TRUE; + mNeedsDeactivate = PR_FALSE; + mNeedsLostFocus = PR_FALSE; + break; + case NS_DEACTIVATE: + mNeedsDeactivate = PR_TRUE; + mNeedsActivate = PR_FALSE; + mNeedsGotFocus = PR_FALSE; + break; + default: + break; + } + return NS_OK; + } + nsIFrame* frame = static_cast(aView->GetClientData()); PRBool dispatchUsingCoordinates = @@ -6477,6 +6517,79 @@ PresShell::Freeze() mDocument->EnumerateSubDocuments(FreezeSubDocument, nsnull); } +void +PresShell::FireOrClearDelayedEvents(PRBool aFireEvents) +{ + if (!aFireEvents) { + mDelayedBlurTargets.Clear(); + mNeedsGotFocus = mNeedsLostFocus = mMozTakingFocus = mNeedsActivate = + mNeedsDeactivate = PR_FALSE; + return; + } + + if (!mIsDestroying && mDocument) { + nsCOMPtr doc = mDocument; + for (PRInt32 i = 0; + (i < mDelayedBlurTargets.Count()) && + !doc->EventHandlingSuppressed(); ++i) { + nsEvent blurevent(PR_TRUE, NS_BLUR_CONTENT); + blurevent.flags |= NS_EVENT_FLAG_CANT_BUBBLE; + nsEventDispatcher::Dispatch(mDelayedBlurTargets[i], mPresContext, &blurevent); + } + mDelayedBlurTargets.Clear(); + + nsFocusEvent firstEvent(PR_TRUE, NS_EVENT_NULL, nsnull); + nsFocusEvent secondEvent(PR_TRUE, NS_EVENT_NULL, nsnull); + if (mNeedsGotFocus) { + firstEvent.message = NS_GOTFOCUS; + } else if (mNeedsDeactivate) { + firstEvent.message = NS_DEACTIVATE; + } + if (mNeedsActivate) { + secondEvent.message = NS_ACTIVATE; + } else if (mNeedsLostFocus) { + secondEvent.message = NS_LOSTFOCUS; + secondEvent.isMozWindowTakingFocus = mMozTakingFocus; + } + mNeedsGotFocus = mNeedsLostFocus = mMozTakingFocus = mNeedsActivate = + mNeedsDeactivate = PR_FALSE; + + if (firstEvent.message && !mIsDestroying && + !doc->EventHandlingSuppressed()) { + nsIViewManager* vm = GetViewManager(); + if (vm) { + nsIView* view = nsnull; + vm->GetRootView(view); + if (view) { + nsEventStatus status = nsEventStatus_eIgnore; + HandleEvent(view, &firstEvent, &status); + } + } + } + if (secondEvent.message && !mIsDestroying && + !doc->EventHandlingSuppressed()) { + nsIViewManager* vm = GetViewManager(); + if (vm) { + nsIView* view = nsnull; + vm->GetRootView(view); + if (view) { + nsEventStatus status = nsEventStatus_eIgnore; + HandleEvent(view, &secondEvent, &status); + } + } + } + } +} + +void +PresShell::NeedsBlurAfterSuppression(nsPIDOMEventTarget* aTarget) +{ + if (mDocument && mDocument->EventHandlingSuppressed()) { + mDelayedBlurTargets.RemoveObject(aTarget); + mDelayedBlurTargets.AppendObject(aTarget); + } +} + static void StartPluginInstance(PresShell *aShell, nsIContent *aContent) {