From 809b317cb3f4884b31cb8db3a1063cf4ef062a6b Mon Sep 17 00:00:00 2001 From: "jst%mozilla.jstenback.com" Date: Sat, 4 Sep 2004 19:28:46 +0000 Subject: [PATCH] Fixing bug 252326. Fixing some popup blocker problems. Making sure only trusted events get mark trusted, and only allow popups while processing trusted (initiated by the user) events. Patch by trev@gtchat.de and jst@mozilla.org, r=bzbarsky@mit.edu, rs=brendan@mozilla.org, bryner@biranryner.com git-svn-id: svn://10.0.0.236/trunk@161765 18797224-902f-48f8-a5cc-f745e15eee43 --- mozilla/content/events/src/nsDOMEvent.cpp | 228 +++++++++++++- mozilla/content/events/src/nsDOMEvent.h | 8 +- .../content/events/src/nsDOMKeyboardEvent.cpp | 2 - .../content/events/src/nsDOMMouseEvent.cpp | 2 - .../content/events/src/nsDOMMutationEvent.cpp | 2 - .../events/src/nsDOMPopupBlockedEvent.cpp | 2 - mozilla/content/events/src/nsDOMTextEvent.cpp | 2 - mozilla/content/events/src/nsDOMUIEvent.cpp | 4 +- .../events/src/nsEventListenerManager.cpp | 4 + .../events/src/nsEventStateManager.cpp | 12 + .../content/events/src/nsEventStateManager.h | 44 +++ mozilla/content/html/content/src/Makefile.in | 2 +- .../html/content/src/nsHTMLFormElement.cpp | 35 ++- mozilla/docshell/base/nsWebShell.cpp | 8 + mozilla/docshell/base/nsWebShell.h | 3 + mozilla/dom/public/base/Makefile.in | 1 + mozilla/dom/public/base/nsPIDOMWindow.h | 69 ++++ mozilla/dom/src/base/nsGlobalWindow.cpp | 297 ++++++------------ mozilla/dom/src/base/nsGlobalWindow.h | 14 +- .../activex/src/plugin/XPCDocument.cpp | 7 + mozilla/layout/base/nsPresShell.cpp | 35 ++- mozilla/layout/generic/nsObjectFrame.cpp | 2 + mozilla/layout/html/base/src/Makefile.in | 1 + .../layout/html/base/src/nsObjectFrame.cpp | 2 + mozilla/layout/html/base/src/nsPresShell.cpp | 35 ++- mozilla/view/src/nsViewManager.cpp | 5 + mozilla/widget/public/nsGUIEvent.h | 15 +- 27 files changed, 589 insertions(+), 252 deletions(-) diff --git a/mozilla/content/events/src/nsDOMEvent.cpp b/mozilla/content/events/src/nsDOMEvent.cpp index cece0917832..06c0c8872b9 100644 --- a/mozilla/content/events/src/nsDOMEvent.cpp +++ b/mozilla/content/events/src/nsDOMEvent.cpp @@ -39,7 +39,7 @@ #include "nsCOMPtr.h" #include "nsDOMEvent.h" -#include "nsIEventStateManager.h" +#include "nsEventStateManager.h" #include "nsIFrame.h" #include "nsIContent.h" #include "nsIPresShell.h" @@ -68,19 +68,19 @@ static const char* const sEventNames[] = { "popupBlocked", "DOMActivate", "DOMFocusIn", "DOMFocusOut" }; +static char *sPopupAllowedEvents; + + nsDOMEvent::nsDOMEvent(nsPresContext* aPresContext, nsEvent* aEvent) { mPresContext = aPresContext; - mEventIsTrusted = PR_FALSE; if (aEvent) { mEvent = aEvent; mEventIsInternal = PR_FALSE; - mEventIsTrusted = PR_TRUE; } else { mEventIsInternal = PR_TRUE; - mEventIsTrusted = PR_FALSE; /* A derived class might want to allocate its own type of aEvent (derived from nsEvent). To do this, it should take care to pass @@ -89,8 +89,8 @@ nsDOMEvent::nsDOMEvent(nsPresContext* aPresContext, nsEvent* aEvent) nsDOMFooEvent::nsDOMFooEvent(..., nsEvent* aEvent) : nsDOMEvent(..., aEvent ? aEvent : new nsFooEvent()) - Then, to override the mEventIsInternal and mEventIsTrusted - assignments done by the base ctor, it should do this in its own ctor: + Then, to override the mEventIsInternal assignments done by the + base ctor, it should do this in its own ctor: nsDOMFooEvent::nsDOMFooEvent(..., nsEvent* aEvent) ... @@ -98,11 +98,9 @@ nsDOMEvent::nsDOMEvent(nsPresContext* aPresContext, nsEvent* aEvent) ... if (aEvent) { mEventIsInternal = PR_FALSE; - mEventIsTrusted = PR_TRUE; } else { mEventIsInternal = PR_TRUE; - mEventIsTrusted = PR_FALSE; } ... } @@ -286,7 +284,12 @@ nsDOMEvent::HasOriginalTarget(PRBool* aResult) NS_IMETHODIMP nsDOMEvent::SetTrusted(PRBool aTrusted) { - mEventIsTrusted = aTrusted; + if (aTrusted) { + mEvent->internalAppFlags |= NS_APP_EVENT_FLAG_TRUSTED; + } else { + mEvent->internalAppFlags &= ~NS_APP_EVENT_FLAG_TRUSTED; + } + return NS_OK; } @@ -360,7 +363,7 @@ nsDOMEvent::PreventCapture() NS_IMETHODIMP nsDOMEvent::GetIsTrusted(PRBool *aIsTrusted) { - *aIsTrusted = mEventIsTrusted; + *aIsTrusted = mEvent->internalAppFlags & NS_APP_EVENT_FLAG_TRUSTED; return NS_OK; } @@ -461,7 +464,6 @@ nsDOMEvent::InitEvent(const nsAString& aEventTypeArg, PRBool aCanBubbleArg, PRBo NS_ENSURE_SUCCESS(SetEventType(aEventTypeArg), NS_ERROR_FAILURE); mEvent->flags |= aCanBubbleArg ? NS_EVENT_FLAG_NONE : NS_EVENT_FLAG_CANT_BUBBLE; mEvent->flags |= aCancelableArg ? NS_EVENT_FLAG_NONE : NS_EVENT_FLAG_CANT_CANCEL; - mEvent->internalAppFlags |= NS_APP_EVENT_FLAG_NONE; return NS_OK; } @@ -516,10 +518,10 @@ nsDOMEvent::IsHandled(PRBool* aIsHandled) NS_IMETHODIMP nsDOMEvent::SetHandled(PRBool aHandled) { - if(aHandled) + if(aHandled) mEvent->internalAppFlags |= NS_APP_EVENT_FLAG_HANDLED; else - mEvent->internalAppFlags &= ~NS_APP_EVENT_FLAG_HANDLED; + mEvent->internalAppFlags &= ~NS_APP_EVENT_FLAG_HANDLED; return NS_OK; } @@ -532,6 +534,204 @@ nsDOMEvent::GetInternalNSEvent(nsEvent** aNSEvent) return NS_OK; } +// return true if eventName is contained within events, delimited by +// spaces +static PRBool +PopupAllowedForEvent(const char *eventName) +{ + if (!sPopupAllowedEvents) { + nsDOMEvent::PopupAllowedEventsChanged(); + + if (!sPopupAllowedEvents) { + return PR_FALSE; + } + } + + nsDependentCString events(sPopupAllowedEvents); + + nsAFlatCString::const_iterator start, end; + nsAFlatCString::const_iterator startiter(events.BeginReading(start)); + events.EndReading(end); + + while (startiter != end) { + nsAFlatCString::const_iterator enditer(end); + + if (!FindInReadable(nsDependentCString(eventName), startiter, enditer)) + return PR_FALSE; + + // the match is surrounded by spaces, or at a string boundary + if ((startiter == start || *--startiter == ' ') && + (enditer == end || *enditer == ' ')) { + return PR_TRUE; + } + + // Move on and see if there are other matches. (The delimitation + // requirement makes it pointless to begin the next search before + // the end of the invalid match just found.) + startiter = enditer; + } + + return PR_FALSE; +} + +// static +PopupControlState +nsDOMEvent::GetEventPopupControlState(nsEvent *aEvent) +{ + // generally if an event handler is running, new windows are disallowed. + // check for exceptions: + PopupControlState abuse = openAbused; + + switch(aEvent->eventStructType) { + case NS_EVENT : + // For these following events only allow popups if they're + // triggered while handling user input. See + // nsPresShell::HandleEventInternal() for details. + if (nsEventStateManager::IsHandlingUserInput()) { + switch(aEvent->message) { + case NS_FORM_SELECTED : + if (::PopupAllowedForEvent("select")) + abuse = openControlled; + case NS_FORM_CHANGE : + if (::PopupAllowedForEvent("change")) + abuse = openControlled; + } + } + break; + case NS_GUI_EVENT : + // For this following event only allow popups if it's triggered + // while handling user input. See + // nsPresShell::HandleEventInternal() for details. + if (nsEventStateManager::IsHandlingUserInput()) { + switch(aEvent->message) { + case NS_FORM_INPUT : + if (::PopupAllowedForEvent("input")) + abuse = openControlled; + break; + } + } + break; + case NS_INPUT_EVENT : + // For this following event only allow popups if it's triggered + // while handling user input. See + // nsPresShell::HandleEventInternal() for details. + if (nsEventStateManager::IsHandlingUserInput()) { + switch(aEvent->message) { + case NS_FORM_CHANGE : + if (::PopupAllowedForEvent("change")) + abuse = openControlled; + break; + } + } + break; + case NS_KEY_EVENT : + if (aEvent->internalAppFlags & NS_APP_EVENT_FLAG_TRUSTED) { + PRUint32 key = NS_STATIC_CAST(nsKeyEvent *, aEvent)->keyCode; + switch(aEvent->message) { + case NS_KEY_PRESS : + // return key on focused button. see note at NS_MOUSE_LEFT_CLICK. + if (key == nsIDOMKeyEvent::DOM_VK_RETURN) + abuse = openAllowed; + else if (::PopupAllowedForEvent("keypress")) + abuse = openControlled; + break; + case NS_KEY_UP : + // space key on focused button. see note at NS_MOUSE_LEFT_CLICK. + if (key == nsIDOMKeyEvent::DOM_VK_SPACE) + abuse = openAllowed; + else if (::PopupAllowedForEvent("keyup")) + abuse = openControlled; + break; + case NS_KEY_DOWN : + if (::PopupAllowedForEvent("keydown")) + abuse = openControlled; + break; + } + } + break; + case NS_MOUSE_EVENT : + if (aEvent->internalAppFlags & NS_APP_EVENT_FLAG_TRUSTED) { + switch(aEvent->message) { + case NS_MOUSE_LEFT_BUTTON_UP : + if (::PopupAllowedForEvent("mouseup")) + abuse = openControlled; + break; + case NS_MOUSE_LEFT_BUTTON_DOWN : + if (::PopupAllowedForEvent("mousedown")) + abuse = openControlled; + break; + case NS_MOUSE_LEFT_CLICK : + /* Click events get special treatment because of their + historical status as a more legitimate event handler. If + click popups are enabled in the prefs, clear the popup + status completely. */ + if (::PopupAllowedForEvent("click")) + abuse = openAllowed; + break; + case NS_MOUSE_LEFT_DOUBLECLICK : + if (::PopupAllowedForEvent("dblclick")) + abuse = openControlled; + break; + } + } + break; + case NS_SCRIPT_ERROR_EVENT : + switch(aEvent->message) { + case NS_SCRIPT_ERROR : + // Any error event will allow popups, if enabled in the pref. + if (::PopupAllowedForEvent("error")) + abuse = openControlled; + break; + } + break; + case NS_FORM_EVENT : + // For these following events only allow popups if they're + // triggered while handling user input. See + // nsPresShell::HandleEventInternal() for details. + if (nsEventStateManager::IsHandlingUserInput()) { + switch(aEvent->message) { + case NS_FORM_SUBMIT : + if (::PopupAllowedForEvent("submit")) + abuse = openControlled; + break; + case NS_FORM_RESET : + if (::PopupAllowedForEvent("reset")) + abuse = openControlled; + break; + } + } + break; + } + + return abuse; +} + +// static +void +nsDOMEvent::PopupAllowedEventsChanged() +{ + if (sPopupAllowedEvents) { + nsMemory::Free(sPopupAllowedEvents); + } + + nsAdoptingCString str = + nsContentUtils::GetCharPref("dom.popup_allowed_events"); + + // We'll want to do this even if str is empty to avoid looking up + // this pref all the time if it's not set. + sPopupAllowedEvents = ToNewCString(str); +} + +// static +void +nsDOMEvent::Shutdown() +{ + if (sPopupAllowedEvents) { + nsMemory::Free(sPopupAllowedEvents); + } +} + +// static const char* nsDOMEvent::GetEventName(PRUint32 aEventType) { switch(aEventType) { @@ -599,7 +799,7 @@ const char* nsDOMEvent::GetEventName(PRUint32 aEventType) case NS_SCROLL_EVENT: return sEventNames[eDOMEvents_scroll]; case NS_TEXT_TEXT: - return sEventNames[eDOMEvents_text]; + return sEventNames[eDOMEvents_text]; case NS_XUL_POPUP_SHOWING: return sEventNames[eDOMEvents_popupShowing]; case NS_XUL_POPUP_SHOWN: diff --git a/mozilla/content/events/src/nsDOMEvent.h b/mozilla/content/events/src/nsDOMEvent.h index cad93a0653a..3d6b59b9e95 100644 --- a/mozilla/content/events/src/nsDOMEvent.h +++ b/mozilla/content/events/src/nsDOMEvent.h @@ -44,6 +44,7 @@ #include "nsIPrivateDOMEvent.h" #include "nsCOMPtr.h" #include "nsIDOMEventTarget.h" +#include "nsPIDOMWindow.h" #include "nsPresContext.h" #include "nsPoint.h" #include "nsGUIEvent.h" @@ -146,6 +147,12 @@ public: NS_IMETHOD IsHandled(PRBool* aHandled); NS_IMETHOD SetHandled(PRBool aHandled); + static PopupControlState GetEventPopupControlState(nsEvent *aEvent); + + static void PopupAllowedEventsChanged(); + + static void Shutdown(); + protected: // Internal helper functions @@ -161,7 +168,6 @@ protected: nsCOMPtr mExplicitOriginalTarget; nsCOMPtr mTmpRealOriginalTarget; PRPackedBool mEventIsInternal; - PRPackedBool mEventIsTrusted; void* mScriptObject; }; diff --git a/mozilla/content/events/src/nsDOMKeyboardEvent.cpp b/mozilla/content/events/src/nsDOMKeyboardEvent.cpp index 0c1ec2e2862..965ad726907 100644 --- a/mozilla/content/events/src/nsDOMKeyboardEvent.cpp +++ b/mozilla/content/events/src/nsDOMKeyboardEvent.cpp @@ -49,11 +49,9 @@ nsDOMKeyboardEvent::nsDOMKeyboardEvent(nsPresContext* aPresContext, nsKeyEvent* if (aEvent) { mEventIsInternal = PR_FALSE; - mEventIsTrusted = PR_TRUE; } else { mEventIsInternal = PR_TRUE; - mEventIsTrusted = PR_FALSE; mEvent->time = PR_Now(); } } diff --git a/mozilla/content/events/src/nsDOMMouseEvent.cpp b/mozilla/content/events/src/nsDOMMouseEvent.cpp index ab63144982c..6190b23a55f 100644 --- a/mozilla/content/events/src/nsDOMMouseEvent.cpp +++ b/mozilla/content/events/src/nsDOMMouseEvent.cpp @@ -53,11 +53,9 @@ nsDOMMouseEvent::nsDOMMouseEvent(nsPresContext* aPresContext, nsInputEvent* aEve if (aEvent) { mEventIsInternal = PR_FALSE; - mEventIsTrusted = PR_TRUE; } else { mEventIsInternal = PR_TRUE; - mEventIsTrusted = PR_FALSE; mEvent->time = PR_Now(); mEvent->refPoint.x = mEvent->refPoint.y = mEvent->point.x = mEvent->point.y = 0; } diff --git a/mozilla/content/events/src/nsDOMMutationEvent.cpp b/mozilla/content/events/src/nsDOMMutationEvent.cpp index 9dd247679c6..3512b3c389a 100644 --- a/mozilla/content/events/src/nsDOMMutationEvent.cpp +++ b/mozilla/content/events/src/nsDOMMutationEvent.cpp @@ -47,14 +47,12 @@ nsDOMMutationEvent::nsDOMMutationEvent(nsPresContext* aPresContext, nsMutationEv { if ( aEvent ) { mEventIsInternal = PR_FALSE; - mEventIsTrusted = PR_TRUE; nsMutationEvent* mutation = (nsMutationEvent*)aEvent; SetTarget(mutation->mTarget); } else { mEventIsInternal = PR_TRUE; - mEventIsTrusted = PR_FALSE; } } diff --git a/mozilla/content/events/src/nsDOMPopupBlockedEvent.cpp b/mozilla/content/events/src/nsDOMPopupBlockedEvent.cpp index af401ab4f78..d9e7349bbfe 100644 --- a/mozilla/content/events/src/nsDOMPopupBlockedEvent.cpp +++ b/mozilla/content/events/src/nsDOMPopupBlockedEvent.cpp @@ -49,11 +49,9 @@ nsDOMPopupBlockedEvent::nsDOMPopupBlockedEvent(nsPresContext* aPresContext, nsPo if (aEvent) { mEventIsInternal = PR_FALSE; - mEventIsTrusted = PR_TRUE; } else { mEventIsInternal = PR_TRUE; - mEventIsTrusted = PR_FALSE; mEvent->time = PR_Now(); } } diff --git a/mozilla/content/events/src/nsDOMTextEvent.cpp b/mozilla/content/events/src/nsDOMTextEvent.cpp index a6d301e80e5..468946416d5 100644 --- a/mozilla/content/events/src/nsDOMTextEvent.cpp +++ b/mozilla/content/events/src/nsDOMTextEvent.cpp @@ -48,11 +48,9 @@ nsDOMTextEvent::nsDOMTextEvent(nsPresContext* aPresContext, nsTextEvent* aEvent) if (aEvent) { mEventIsInternal = PR_FALSE; - mEventIsTrusted = PR_TRUE; } else { mEventIsInternal = PR_TRUE; - mEventIsTrusted = PR_FALSE; mEvent->time = PR_Now(); } diff --git a/mozilla/content/events/src/nsDOMUIEvent.cpp b/mozilla/content/events/src/nsDOMUIEvent.cpp index 027fce08e41..c50a43ef974 100644 --- a/mozilla/content/events/src/nsDOMUIEvent.cpp +++ b/mozilla/content/events/src/nsDOMUIEvent.cpp @@ -54,15 +54,13 @@ #include "nsIFrame.h" nsDOMUIEvent::nsDOMUIEvent(nsPresContext* aPresContext, nsGUIEvent* aEvent) -: nsDOMEvent(aPresContext, aEvent) + : nsDOMEvent(aPresContext, aEvent) { if (aEvent) { mEventIsInternal = PR_FALSE; - mEventIsTrusted = PR_TRUE; } else { mEventIsInternal = PR_TRUE; - mEventIsTrusted = PR_FALSE; mEvent->time = PR_Now(); } diff --git a/mozilla/content/events/src/nsEventListenerManager.cpp b/mozilla/content/events/src/nsEventListenerManager.cpp index 6b4d79474c6..a09570952ed 100644 --- a/mozilla/content/events/src/nsEventListenerManager.cpp +++ b/mozilla/content/events/src/nsEventListenerManager.cpp @@ -440,6 +440,8 @@ nsresult nsEventListenerManager::RemoveAllListeners(PRBool aScriptOnly) void nsEventListenerManager::Shutdown() { sAddListenerID = JSVAL_VOID; + + nsDOMEvent::Shutdown(); } NS_IMPL_ADDREF(nsEventListenerManager) @@ -1582,6 +1584,8 @@ nsresult nsEventListenerManager::HandleEvent(nsPresContext* aPresContext, } if (NS_SUCCEEDED(ret)) { + nsAutoPopupStatePusher popupStatePusher(nsDOMEvent::GetEventPopupControlState(aEvent)); + for (int k = 0; !mListenersRemoved && listeners && k < listeners->Count(); ++k) { nsListenerStruct* ls = NS_STATIC_CAST(nsListenerStruct*, listeners->ElementAt(k)); if (ls->mFlags & aFlags && ls->mGroupFlags == currentGroup) { diff --git a/mozilla/content/events/src/nsEventStateManager.cpp b/mozilla/content/events/src/nsEventStateManager.cpp index a101aca26d5..2ca21a54772 100644 --- a/mozilla/content/events/src/nsEventStateManager.cpp +++ b/mozilla/content/events/src/nsEventStateManager.cpp @@ -156,6 +156,7 @@ static PRBool sLeftClickOnly = PR_TRUE; static PRBool sKeyCausesActivation = PR_TRUE; static PRUint32 sESMInstanceCount = 0; static PRInt32 sGeneralAccesskeyModifier = -1; // magic value of -1 means uninitialized +PRInt32 nsEventStateManager::sUserInputEventDepth = 0; enum { MOUSE_SCROLL_N_LINES, @@ -283,6 +284,8 @@ nsEventStateManager::Init() prefBranch->AddObserver("mousewheel.withshiftkey.numlines", this, PR_TRUE); prefBranch->AddObserver("mousewheel.withshiftkey.sysnumlines", this, PR_TRUE); #endif + + prefBranch->AddObserver("dom.popup_allowed_events", this, PR_TRUE); } if (sTextfieldSelectModel == eTextfieldSelect_unset) { @@ -358,6 +361,8 @@ nsEventStateManager::Shutdown() prefBranch->RemoveObserver("mousewheel.withnokey.numlines", this); prefBranch->RemoveObserver("mousewheel.withnokey.sysnumlines", this); #endif + + prefBranch->RemoveObserver("dom.popup_allowed_events", this); } m_haveShutdown = PR_TRUE; @@ -408,6 +413,8 @@ nsEventStateManager::Observe(nsISupports *aSubject, } else if (data.EqualsLiteral("mousewheel.withnokey.numlines")) { } else if (data.EqualsLiteral("mousewheel.withnokey.sysnumlines")) { #endif + } else if (data.EqualsLiteral("dom.popup_allowed_events")) { + nsDOMEvent::PopupAllowedEventsChanged(); } } @@ -1042,6 +1049,11 @@ nsEventStateManager::HandleAccessKey(nsPresContext* aPresContext, // B) Click on it if the users prefs indicate to do so. nsEventStatus status = nsEventStatus_eIgnore; nsMouseEvent event(NS_MOUSE_LEFT_CLICK); + + // Propagate trusted state to the new event. + event.internalAppFlags |= + aEvent->internalAppFlags & NS_APP_EVENT_FLAG_TRUSTED; + nsCOMPtr oldTargetContent = mCurrentTargetContent; mCurrentTargetContent = content; content->HandleDOMEvent(mPresContext, &event, nsnull, NS_EVENT_FLAG_INIT, &status); diff --git a/mozilla/content/events/src/nsEventStateManager.h b/mozilla/content/events/src/nsEventStateManager.h index 8f7d44da736..162098b582b 100644 --- a/mozilla/content/events/src/nsEventStateManager.h +++ b/mozilla/content/events/src/nsEventStateManager.h @@ -146,6 +146,21 @@ public: NS_IMETHOD MoveFocusToCaret(PRBool aCanFocusDoc, PRBool *aIsSelectionWithFocus); NS_IMETHOD MoveCaretToFocus(); + static void StartHandlingUserInput() + { + ++sUserInputEventDepth; + } + + static void StopHandlingUserInput() + { + --sUserInputEventDepth; + } + + static PRBool IsHandlingUserInput() + { + return sUserInputEventDepth > 0; + } + protected: friend class CurrentEventShepherd; @@ -334,6 +349,35 @@ protected: nsCOMPtr mClickHoldTimer; #endif + static PRInt32 sUserInputEventDepth; +}; + + +class nsAutoHandlingUserInputStatePusher +{ +public: + nsAutoHandlingUserInputStatePusher(PRBool aIsHandlingUserInput) + : mIsHandlingUserInput(aIsHandlingUserInput) + { + if (aIsHandlingUserInput) { + nsEventStateManager::StartHandlingUserInput(); + } + } + + ~nsAutoHandlingUserInputStatePusher() + { + if (mIsHandlingUserInput) { + nsEventStateManager::StopHandlingUserInput(); + } + } + +protected: + PRBool mIsHandlingUserInput; + +private: + // Not meant to be implemented. + static void* operator new(size_t /*size*/) CPP_THROW_NEW; + static void operator delete(void* /*memory*/); }; #endif // nsEventStateManager_h__ diff --git a/mozilla/content/html/content/src/Makefile.in b/mozilla/content/html/content/src/Makefile.in index d031e908d0e..77e8b124630 100644 --- a/mozilla/content/html/content/src/Makefile.in +++ b/mozilla/content/html/content/src/Makefile.in @@ -130,7 +130,7 @@ include $(topsrcdir)/config/rules.mk INCLUDES += \ -I$(srcdir)/../../style/src \ -I$(srcdir)/../../../base/src \ - -I$(srcdir)/../../base/src \ + -I$(srcdir)/../../../events/src \ $(NULL) DEFINES += -D_IMPL_NS_LAYOUT diff --git a/mozilla/content/html/content/src/nsHTMLFormElement.cpp b/mozilla/content/html/content/src/nsHTMLFormElement.cpp index 60fb9da2f54..2721eb51d18 100644 --- a/mozilla/content/html/content/src/nsHTMLFormElement.cpp +++ b/mozilla/content/html/content/src/nsHTMLFormElement.cpp @@ -45,6 +45,7 @@ #include "nsIDOMEventReceiver.h" #include "nsIHTMLContent.h" #include "nsGenericHTMLElement.h" +#include "nsEventStateManager.h" #include "nsHTMLAtoms.h" #include "nsStyleConsts.h" #include "nsPresContext.h" @@ -68,6 +69,7 @@ #include "nsCategoryManagerUtils.h" #include "nsISimpleEnumerator.h" #include "nsIDOMWindowInternal.h" +#include "nsPIDOMWindow.h" #include "nsRange.h" #include "nsIScriptSecurityManager.h" #include "nsNetUtil.h" @@ -269,6 +271,10 @@ protected: PRPackedBool mIsSubmitting; /** Whether the submission is to be deferred in case a script triggers it */ PRPackedBool mDeferSubmission; + /** Keep track of what the popup state was when the submit was initiated */ + PopupControlState mSubmitPopupState; + /** Keep track of whether a submission was user-initiated or not */ + PRBool mSubmitInitiatedFromUserInput; /** The pending submission object */ nsCOMPtr mPendingSubmission; @@ -425,6 +431,8 @@ nsHTMLFormElement::nsHTMLFormElement(nsINodeInfo *aNodeInfo) mGeneratingReset(PR_FALSE), mIsSubmitting(PR_FALSE), mDeferSubmission(PR_FALSE), + mSubmitPopupState(openAbused), + mSubmitInitiatedFromUserInput(PR_FALSE), mPendingSubmission(nsnull), mSubmittingRequest(nsnull) { @@ -821,7 +829,18 @@ nsHTMLFormElement::DoSubmit(nsPresContext* aPresContext, nsEvent* aEvent) // prepare the submission object // BuildSubmission(aPresContext, submission, aEvent); - + + nsCOMPtr window = + do_QueryInterface(GetOwnerDoc()->GetScriptGlobalObject()); + + if (window) { + mSubmitPopupState = window->GetPopupControlState(); + } else { + mSubmitPopupState = openAbused; + } + + mSubmitInitiatedFromUserInput = nsEventStateManager::IsHandlingUserInput(); + if(mDeferSubmission) { // we are in an event handler, JS submitted so we have to // defer this submission. let's remember it and return @@ -918,9 +937,17 @@ nsHTMLFormElement::SubmitSubmission(nsPresContext* aPresContext, // Submit // nsCOMPtr docShell; - rv = aFormSubmission->SubmitTo(actionURI, target, this, aPresContext, - getter_AddRefs(docShell), - getter_AddRefs(mSubmittingRequest)); + + { + nsAutoPopupStatePusher popupStatePusher(mSubmitPopupState); + + nsAutoHandlingUserInputStatePusher userInpStatePusher(mSubmitInitiatedFromUserInput); + + rv = aFormSubmission->SubmitTo(actionURI, target, this, aPresContext, + getter_AddRefs(docShell), + getter_AddRefs(mSubmittingRequest)); + } + NS_ENSURE_SUBMIT_SUCCESS(rv); // Even if the submit succeeds, it's possible for there to be no docshell diff --git a/mozilla/docshell/base/nsWebShell.cpp b/mozilla/docshell/base/nsWebShell.cpp index ae754636d40..043486105b6 100644 --- a/mozilla/docshell/base/nsWebShell.cpp +++ b/mozilla/docshell/base/nsWebShell.cpp @@ -428,6 +428,9 @@ struct OnLinkClickEvent : public PLEvent { ~OnLinkClickEvent(); void HandleEvent() { + nsCOMPtr window(do_QueryInterface(mHandler->mScriptGlobal)); + nsAutoPopupStatePusher popupStatePusher(window, mPopupState); + mHandler->OnLinkClickSync(mContent, mVerb, mURI, mTargetSpec.get(), mPostDataStream, mHeadersDataStream, @@ -441,6 +444,7 @@ struct OnLinkClickEvent : public PLEvent { nsCOMPtr mHeadersDataStream; nsCOMPtr mContent; nsLinkVerb mVerb; + PopupControlState mPopupState; }; static void PR_CALLBACK HandlePLEvent(OnLinkClickEvent* aEvent) @@ -470,6 +474,10 @@ OnLinkClickEvent::OnLinkClickEvent(nsWebShell* aHandler, mContent = aContent; mVerb = aVerb; + nsCOMPtr window(do_QueryInterface(mHandler->mScriptGlobal)); + + mPopupState = window->GetPopupControlState(); + PL_InitEvent(this, nsnull, (PLHandleEventProc) ::HandlePLEvent, (PLDestroyEventProc) ::DestroyPLEvent); diff --git a/mozilla/docshell/base/nsWebShell.h b/mozilla/docshell/base/nsWebShell.h index 80e5a9a4322..1caabb8aae5 100644 --- a/mozilla/docshell/base/nsWebShell.h +++ b/mozilla/docshell/base/nsWebShell.h @@ -52,6 +52,7 @@ class nsIEventQueue; class nsIController; struct PRThread; +struct OnLinkClickEvent; typedef enum { eCharsetReloadInit, @@ -115,6 +116,8 @@ public: // NS_IMETHOD SetURL(const PRUnichar* aURL); + friend struct OnLinkClickEvent; + protected: // void GetRootWebShellEvenIfChrome(nsIWebShell** aResult); void InitFrameData(); diff --git a/mozilla/dom/public/base/Makefile.in b/mozilla/dom/public/base/Makefile.in index 7901061019d..0f2e433af14 100644 --- a/mozilla/dom/public/base/Makefile.in +++ b/mozilla/dom/public/base/Makefile.in @@ -48,3 +48,4 @@ EXPORTS=nsPIDOMWindow.h nsPIWindowRoot.h nsIFocusController.h include $(topsrcdir)/config/rules.mk +DEFINES += -D_IMPL_NS_LAYOUT diff --git a/mozilla/dom/public/base/nsPIDOMWindow.h b/mozilla/dom/public/base/nsPIDOMWindow.h index 666d89e7769..6d860bce30e 100644 --- a/mozilla/dom/public/base/nsPIDOMWindow.h +++ b/mozilla/dom/public/base/nsPIDOMWindow.h @@ -49,6 +49,18 @@ #include "nsIURI.h" #include "nsCOMPtr.h" +// Popup control state enum. The values in this enum must go from most +// permissive to least permissive so that its safe to push state in +// all situations. Pushing popup state onto the stack never makes the +// current popup state less permissive (see +// GlobalWindowImpl::PushPopupControlState()). +enum PopupControlState { + openAllowed = 0, // open that window without worries + openControlled, // it's a popup, but allow it + openAbused, // it's a popup. disallow it, but allow domain override. + openOverridden // disallow window open +}; + class nsIDocShell; class nsIFocusController; struct nsTimeoutImpl; @@ -105,6 +117,10 @@ public: virtual void SetOpenerScriptURL(nsIURI* aURI) = 0; + virtual PopupControlState PushPopupControlState(PopupControlState aState) const = 0; + virtual void PopPopupControlState(PopupControlState state) const = 0; + virtual PopupControlState GetPopupControlState() const = 0; + protected: nsCOMPtr mChromeEventHandler; // strong nsCOMPtr mDocument; // strong @@ -117,4 +133,57 @@ protected: PRBool mIsDocumentLoaded; }; + +#ifdef _IMPL_NS_LAYOUT +PopupControlState +PushPopupControlState(PopupControlState aState); + +void +PopPopupControlState(PopupControlState aState); +#endif + +// Helper chass that helps with pushing and poping popup control +// state. Note that this class looks different from within code that's +// part of the layout library than it does in code outside the layout +// library. +class nsAutoPopupStatePusher +{ +public: +#ifdef _IMPL_NS_LAYOUT + nsAutoPopupStatePusher(PopupControlState aState) + : mOldState(::PushPopupControlState(aState)) + { + } + + ~nsAutoPopupStatePusher() + { + PopPopupControlState(mOldState); + } +#else + nsAutoPopupStatePusher(nsPIDOMWindow *aWindow, PopupControlState aState) + : mWindow(aWindow), mOldState(openAbused) + { + if (aWindow) { + mOldState = aWindow->PushPopupControlState(aState); + } + } + + ~nsAutoPopupStatePusher() + { + if (mWindow) { + mWindow->PopPopupControlState(mOldState); + } + } +#endif + +protected: + nsPIDOMWindow *mWindow; // WEAK + PopupControlState mOldState; + +private: + // Not meant to be implemented. + static void* operator new(size_t /*size*/) CPP_THROW_NEW; + static void operator delete(void* /*memory*/); +}; + #endif // nsPIDOMWindow_h__ diff --git a/mozilla/dom/src/base/nsGlobalWindow.cpp b/mozilla/dom/src/base/nsGlobalWindow.cpp index 4f3c2822593..cc3fbb773d6 100644 --- a/mozilla/dom/src/base/nsGlobalWindow.cpp +++ b/mozilla/dom/src/base/nsGlobalWindow.cpp @@ -156,11 +156,15 @@ // belonging to the back-end like nsIContentPolicy #include "nsIPopupWindowManager.h" +nsIScriptSecurityManager *GlobalWindowImpl::sSecMan = nsnull; +nsIFactory *GlobalWindowImpl::sComputedDOMStyleFactory = nsnull; + static nsIEntropyCollector *gEntropyCollector = nsnull; static PRInt32 gRefCnt = 0; static PRInt32 gOpenPopupSpamCount = 0; -nsIScriptSecurityManager *GlobalWindowImpl::sSecMan = nsnull; -nsIFactory *GlobalWindowImpl::sComputedDOMStyleFactory = nsnull; +static PopupControlState gPopupControlState = openAbused; +static PRInt32 gRunningTimeoutDepth = 0; + #ifdef DEBUG_jst PRInt32 gTimeoutCnt = 0; #endif @@ -187,13 +191,6 @@ static const char kDOMSecurityWarningsBundleURL[] = "chrome://communicator/local static const char kCryptoContractID[] = NS_CRYPTO_CONTRACTID; static const char kPkcs11ContractID[] = NS_PKCS11_CONTRACTID; -// CheckForAbusePoint return values: -enum { - openAllowed = 0, // open that window without worries - openControlled, // it's a popup, but allow it - openAbused, // it's a popup. disallow it, but allow domain override. - openOverridden // disallow window open -}; // CheckOpenAllow return values: enum { allowNot = 0, // the window opening was denied @@ -204,36 +201,6 @@ enum { }; -// return true if eventName is contained within events, delimited by spaces -static PRBool -ContainsEventName(const char *eventName, const nsAFlatCString& events) -{ - nsAFlatCString::const_iterator start, end; - events.BeginReading(start); - events.EndReading(end); - - nsAFlatCString::const_iterator startiter(start); - - while (startiter != end) { - nsAFlatCString::const_iterator enditer(end); - - if (!FindInReadable(nsDependentCString(eventName), startiter, enditer)) - return PR_FALSE; - - // the match is surrounded by spaces, or at a string boundary - if ((startiter == start || *--startiter == ' ') && - (enditer == end || *enditer == ' ')) - return PR_TRUE; - - /* Move on and see if there are other matches. (The delimitation - requirement makes it pointless to begin the next search before - the end of the invalid match just found.) */ - startiter = enditer; - } - - return PR_FALSE; -} - //***************************************************************************** //*** GlobalWindowImpl: Object Management //***************************************************************************** @@ -250,7 +217,6 @@ GlobalWindowImpl::GlobalWindowImpl() mTimeoutInsertionPoint(&mTimeouts), mTimeoutPublicIdCounter(1), mTimeoutFiringDepth(0), - mLastMouseButtonAction(LL_ZERO), mGlobalObjectOwner(nsnull), mDocShell(nsnull), mCurrentEvent(0) @@ -422,6 +388,42 @@ GlobalWindowImpl::SetOpenerScriptURL(nsIURI* aURI) mOpenerScriptURL = aURI; } +PopupControlState +PushPopupControlState(PopupControlState aState) +{ + PopupControlState oldState = gPopupControlState; + + if (aState < gPopupControlState) { + gPopupControlState = aState; + } + + return oldState; +} + +void +PopPopupControlState(PopupControlState aState) +{ + gPopupControlState = aState; +} + +PopupControlState +GlobalWindowImpl::PushPopupControlState(PopupControlState aState) const +{ + return ::PushPopupControlState(aState); +} + +void +GlobalWindowImpl::PopPopupControlState(PopupControlState aState) const +{ + ::PopPopupControlState(aState); +} + +PopupControlState +GlobalWindowImpl::GetPopupControlState() const +{ + return gPopupControlState; +} + nsresult GlobalWindowImpl::SetNewDocument(nsIDOMDocument* aDocument, PRBool aRemoveEventListeners, @@ -869,11 +871,6 @@ GlobalWindowImpl::HandleDOMEvent(nsPresContext* aPresContext, if (aEvent->message == NS_PAGE_UNLOAD) { mIsDocumentLoaded = PR_FALSE; - } else if ((aEvent->message >= NS_MOUSE_LEFT_BUTTON_UP && - aEvent->message <= NS_MOUSE_RIGHT_BUTTON_DOWN) || - (aEvent->message >= NS_MOUSE_LEFT_DOUBLECLICK && - aEvent->message <= NS_MOUSE_RIGHT_CLICK)) { - mLastMouseButtonAction = PR_Now(); } // Capturing stage @@ -2881,10 +2878,17 @@ GlobalWindowImpl::DisableExternalCapture() static PRBool IsPopupBlocked(nsIDOMDocument* aDoc) { - PRBool blocked = PR_FALSE; + nsCOMPtr pm = + do_GetService(NS_POPUPWINDOWMANAGER_CONTRACTID); + + if (!pm) { + return PR_FALSE; + } + + PRBool blocked = PR_TRUE; nsCOMPtr doc(do_QueryInterface(aDoc)); - nsCOMPtr pm(do_GetService(NS_POPUPWINDOWMANAGER_CONTRACTID)); - if (pm && doc) { + + if (doc) { PRUint32 permission = nsIPopupWindowManager::ALLOW_POPUP; pm->TestPermission(doc->GetDocumentURI(), &permission); blocked = (permission == nsIPopupWindowManager::DENY_POPUP); @@ -2950,7 +2954,7 @@ GlobalWindowImpl::CanSetProperty(const char *aPrefName) * routine to determine whether to allow the new window. * Returns a value from the CheckForAbusePoint enum. */ -PRUint32 +PopupControlState GlobalWindowImpl::CheckForAbusePoint() { nsCOMPtr item(do_QueryInterface(mDocShell)); @@ -2963,162 +2967,9 @@ GlobalWindowImpl::CheckForAbusePoint() return openAllowed; } - PRUint32 abuse = openAllowed; // level of abuse we've detected - - // is the document being loaded or unloaded? - if (!mIsDocumentLoaded) - abuse = openAbused; - - /* Disallow windows after a user-defined click delay. - This is a consideration secondary to document load because - of its stronger response (openOverridden). See bug 247017. */ - if (abuse == openAllowed) { - PRInt32 delay = nsContentUtils::GetIntPref("dom.disable_open_click_delay"); - if (delay != 0) { - PRTime now = PR_Now(); - PRTime ll_delta; - PRUint32 delta; - LL_SUB(ll_delta, now, mLastMouseButtonAction); - LL_L2UI(delta, ll_delta); - if (delta/1000 > (PRUint32) delay) - abuse = openOverridden; - } - } - - /* Is a timer running? - This is a consideration secondary to the user-defined click delay - because that seemed the Right Thing. See bug 197919 comment 45. */ - if (abuse == openAllowed && mRunningTimeout) - abuse = openAbused; - - if (abuse == openAllowed) { - // we'll need to know what DOM event is being processed now, if any - nsEvent *currentEvent = mCurrentEvent; - if (!currentEvent && mDocShell) { - /* The DOM window's current event is accurate for events that make it - all the way to the window. But it doesn't see events handled directly - by a target element. For those, check the EventStateManager. */ - nsCOMPtr presShell; - mDocShell->GetPresShell(getter_AddRefs(presShell)); - if (presShell) { - nsPresContext *presContext = presShell->GetPresContext(); - if (presContext) - presContext->EventStateManager()->GetCurrentEvent(¤tEvent); - } - } - - // fetch pref string detailing which events are allowed - const nsAdoptingCString& eventPref = - nsContentUtils::GetCharPref("dom.popup_allowed_events"); - - // generally if an event handler is running, new windows are disallowed. - // check for exceptions: - if (currentEvent) { - abuse = openAbused; - switch(currentEvent->eventStructType) { - case NS_EVENT : - switch(currentEvent->message) { - case NS_FORM_SELECTED : - if (::ContainsEventName("select", eventPref)) - abuse = openControlled; - break; - case NS_FORM_CHANGE : - if (::ContainsEventName("change", eventPref)) - abuse = openControlled; - break; - case NS_RESIZE_EVENT : - if (::ContainsEventName("resize", eventPref)) - abuse = openControlled; - break; - } - break; - case NS_GUI_EVENT : - switch(currentEvent->message) { - case NS_FORM_INPUT : - if (::ContainsEventName("input", eventPref)) - abuse = openControlled; - break; - } - break; - case NS_INPUT_EVENT : - switch(currentEvent->message) { - case NS_FORM_CHANGE : - if (::ContainsEventName("change", eventPref)) - abuse = openControlled; - break; - } - break; - case NS_KEY_EVENT : { - PRUint32 key = NS_STATIC_CAST(nsKeyEvent *, currentEvent)->keyCode; - switch(currentEvent->message) { - case NS_KEY_PRESS : - // return key on focused button. see note at NS_MOUSE_LEFT_CLICK. - if (key == nsIDOMKeyEvent::DOM_VK_RETURN) - abuse = openAllowed; - else if (::ContainsEventName("keypress", eventPref)) - abuse = openControlled; - break; - case NS_KEY_UP : - // space key on focused button. see note at NS_MOUSE_LEFT_CLICK. - if (key == nsIDOMKeyEvent::DOM_VK_SPACE) - abuse = openAllowed; - else if (::ContainsEventName("keyup", eventPref)) - abuse = openControlled; - break; - case NS_KEY_DOWN : - if (::ContainsEventName("keydown", eventPref)) - abuse = openControlled; - break; - } - break; - } - case NS_MOUSE_EVENT : - switch(currentEvent->message) { - case NS_MOUSE_LEFT_BUTTON_UP : - if (::ContainsEventName("mouseup", eventPref)) - abuse = openControlled; - break; - case NS_MOUSE_LEFT_BUTTON_DOWN : - if (::ContainsEventName("mousedown", eventPref)) - abuse = openControlled; - break; - case NS_MOUSE_LEFT_CLICK : - /* Click events get special treatment because of their - historical status as a more legitimate event handler. - If click popups are enabled in the prefs, clear the - popup status completely. */ - if (::ContainsEventName("click", eventPref)) - abuse = openAllowed; - break; - case NS_MOUSE_LEFT_DOUBLECLICK : - if (::ContainsEventName("dblclick", eventPref)) - abuse = openControlled; - break; - } - break; - case NS_SCRIPT_ERROR_EVENT : - switch(currentEvent->message) { - case NS_SCRIPT_ERROR : - if (::ContainsEventName("error", eventPref)) - abuse = openControlled; - break; - } - break; - case NS_FORM_EVENT : - switch(currentEvent->message) { - case NS_FORM_SUBMIT : - if (::ContainsEventName("submit", eventPref)) - abuse = openControlled; - break; - case NS_FORM_RESET : - if (::ContainsEventName("reset", eventPref)) - abuse = openControlled; - break; - } - break; - } // switch - } // currentEvent - } // abuse == openAllowed + // level of abuse we've detected, initialized to the current popup + // state + PopupControlState abuse = gPopupControlState; // limit the number of simultaneously open popups if (abuse == openAbused || abuse == openControlled) { @@ -3135,7 +2986,7 @@ GlobalWindowImpl::CheckForAbusePoint() or if its target is an extant window. Returns a value from the CheckOpenAllow enum. */ PRUint32 -GlobalWindowImpl::CheckOpenAllow(PRUint32 aAbuseLevel, +GlobalWindowImpl::CheckOpenAllow(PopupControlState aAbuseLevel, const nsAString &aName) { PRUint32 allowWindow = allowNoAbuse; // (also used for openControlled) @@ -3249,7 +3100,7 @@ GlobalWindowImpl::Open(const nsAString& aUrl, { nsresult rv; - PRUint32 abuseLevel = CheckForAbusePoint(); + PopupControlState abuseLevel = CheckForAbusePoint(); PRUint32 allowReason = CheckOpenAllow(abuseLevel, aName); if (allowReason == allowNot) { FireAbuseEvents(PR_TRUE, PR_FALSE, aUrl, aOptions); @@ -3311,7 +3162,7 @@ GlobalWindowImpl::Open(nsIDOMWindow **_retval) } } - PRUint32 abuseLevel = CheckForAbusePoint(); + PopupControlState abuseLevel = CheckForAbusePoint(); PRUint32 allowReason = CheckOpenAllow(abuseLevel, name); if (allowReason == allowNot) { FireAbuseEvents(PR_TRUE, PR_FALSE, url, options); @@ -4894,6 +4745,23 @@ GlobalWindowImpl::SetTimeoutOrInterval(PRBool aIsInterval, PRInt32 *aReturn) timeout->mWindow = this; NS_ADDREF(timeout->mWindow); + // No popups from timeouts by default + timeout->mPopupState = openAbused; + + if (gRunningTimeoutDepth == 0 && gPopupControlState < openAbused) { + // This timeout is *not* set from another timeout and it's set + // while popups are enabled. Propagate the state to the timeout if + // its delay (interval) is equal to or less than what + // "dom.disable_open_click_delay" is set to. + + PRInt32 delay = + nsContentUtils::GetIntPref("dom.disable_open_click_delay"); + + if (interval <= (delay * PR_MSEC_PER_SEC)) { + timeout->mPopupState = gPopupControlState; + } + } + InsertTimeoutIntoList(mTimeoutInsertionPoint, timeout); timeout->mPublicId = ++mTimeoutPublicIdCounter; *aReturn = timeout->mPublicId; @@ -5013,10 +4881,21 @@ GlobalWindowImpl::RunTimeout(nsTimeoutImpl *aTimeout) mRunningTimeout = timeout; timeout->mRunning = PR_TRUE; + // Push this timeouts popup control state, which should only be + // eabled the first time a timeout fires that was created while + // popups were enabled and with a delay less than + // "dom.disable_open_click_delay". + nsAutoPopupStatePusher popupStatePusher(timeout->mPopupState); + + // Clear the timeout's popup state, if any, to prevent interval + // timeouts from repeatedly opening poups. + timeout->mPopupState = openAbused; + // Hold on to the timeout in case mExpr or mFunObj releases its // doc. timeout->AddRef(); + ++gRunningTimeoutDepth; ++mTimeoutFiringDepth; if (timeout->mExpr) { @@ -5047,6 +4926,8 @@ GlobalWindowImpl::RunTimeout(nsTimeoutImpl *aTimeout) } --mTimeoutFiringDepth; + --gRunningTimeoutDepth; + mRunningTimeout = last_running_timeout; timeout->mRunning = PR_FALSE; diff --git a/mozilla/dom/src/base/nsGlobalWindow.h b/mozilla/dom/src/base/nsGlobalWindow.h index 5a5bc37a264..ae3e64d778f 100644 --- a/mozilla/dom/src/base/nsGlobalWindow.h +++ b/mozilla/dom/src/base/nsGlobalWindow.h @@ -192,6 +192,10 @@ public: virtual NS_HIDDEN_(void) SetOpenerScriptURL(nsIURI* aURI); + virtual NS_HIDDEN_(PopupControlState) PushPopupControlState(PopupControlState state) const; + virtual NS_HIDDEN_(void) PopPopupControlState(PopupControlState state) const; + virtual NS_HIDDEN_(PopupControlState) GetPopupControlState() const; + // nsIDOMViewCSS NS_DECL_NSIDOMVIEWCSS @@ -246,8 +250,9 @@ protected: nsresult GetScrollInfo(nsIScrollableView** aScrollableView, float* aP2T, float* aT2P); nsresult SecurityCheckURL(const char *aURL); - PRUint32 CheckForAbusePoint(); - PRUint32 CheckOpenAllow(PRUint32 aAbuseLevel, const nsAString &aName); + PopupControlState CheckForAbusePoint(); + PRUint32 CheckOpenAllow(PopupControlState aAbuseLevel, + const nsAString &aName); void FireAbuseEvents(PRBool aBlocked, PRBool aWindow, const nsAString &aPopupURL, const nsAString &aPopupWindowFeatures); @@ -317,7 +322,6 @@ protected: nsTimeoutImpl** mTimeoutInsertionPoint; PRUint32 mTimeoutPublicIdCounter; PRUint32 mTimeoutFiringDepth; - PRTime mLastMouseButtonAction; nsString mStatus; nsString mDefaultStatus; @@ -445,6 +449,10 @@ struct nsTimeoutImpl // timeouts nsTimeoutImpl *mNext; + // The popup state at timeout creation time if not created from + // another timeout + PopupControlState mPopupState; + private: // reference count for shared usage PRInt32 mRefCnt; diff --git a/mozilla/embedding/browser/activex/src/plugin/XPCDocument.cpp b/mozilla/embedding/browser/activex/src/plugin/XPCDocument.cpp index 1b926f94e59..a3204a7d31f 100644 --- a/mozilla/embedding/browser/activex/src/plugin/XPCDocument.cpp +++ b/mozilla/embedding/browser/activex/src/plugin/XPCDocument.cpp @@ -40,6 +40,7 @@ #include #include +#include "nsPIDOMWindow.h" // A barely documented interface called ITargetFrame from IE // This is needed for targeted Hlink* calls (e.g. HlinkNavigateString) to @@ -1831,6 +1832,12 @@ END_COM_MAP() nsCOMPtr lh = do_QueryInterface(webNav); if (lh) { + nsCOMPtr window = + do_GetInterface(mDOMWindow); + + nsAutoPopupStatePusher popupStatePusher(window, + openAllowed); + lh->OnLinkClick(nsnull, eLinkVerb_Replace, uri, szTargetFrame ? szTargetFrame : mUseTarget); } diff --git a/mozilla/layout/base/nsPresShell.cpp b/mozilla/layout/base/nsPresShell.cpp index c5b364e9de3..1c2645ca397 100644 --- a/mozilla/layout/base/nsPresShell.cpp +++ b/mozilla/layout/base/nsPresShell.cpp @@ -76,8 +76,8 @@ #include "nsIViewObserver.h" #include "nsContainerFrame.h" #include "nsIDeviceContext.h" -#include "nsIEventStateManager.h" -#include "nsIDOMEvent.h" +#include "nsEventStateManager.h" +#include "nsDOMEvent.h" #include "nsGUIEvent.h" #include "nsHTMLParts.h" #include "nsContentUtils.h" @@ -5899,6 +5899,37 @@ PresShell::HandleEventInternal(nsEvent* aEvent, nsIView *aView, nsresult rv = NS_OK; if (!NS_EVENT_NEEDS_FRAME(aEvent) || GetCurrentEventFrame()) { + PRBool isHandlingUserInput = PR_FALSE; + + if (aEvent->internalAppFlags & NS_APP_EVENT_FLAG_TRUSTED) { + switch (aEvent->message) { + case NS_GOTFOCUS: + case NS_LOSTFOCUS: + case NS_ACTIVATE: + case NS_DEACTIVATE: + // Treat focus/blur events as user input if they happen while + // executing trusted script, or no script at all. If they + // happen during execution of non-trusted script, then they + // should not be considerd user input. + if (!nsContentUtils::IsCallerChrome()) { + break; + } + case NS_MOUSE_LEFT_BUTTON_DOWN: + case NS_MOUSE_MIDDLE_BUTTON_DOWN: + case NS_MOUSE_RIGHT_BUTTON_DOWN: + case NS_MOUSE_LEFT_BUTTON_UP: + case NS_MOUSE_RIGHT_BUTTON_UP: + case NS_MOUSE_MIDDLE_BUTTON_UP: + case NS_KEY_PRESS: + case NS_KEY_DOWN: + case NS_KEY_UP: + isHandlingUserInput = PR_TRUE; + } + } + + nsAutoHandlingUserInputStatePusher userInpStatePusher(isHandlingUserInput); + + nsAutoPopupStatePusher popupStatePusher(nsDOMEvent::GetEventPopupControlState(aEvent)); // 1. Give event to event manager for pre event state changes and // generation of synthetic events. diff --git a/mozilla/layout/generic/nsObjectFrame.cpp b/mozilla/layout/generic/nsObjectFrame.cpp index 59e082201df..76002cf11dc 100644 --- a/mozilla/layout/generic/nsObjectFrame.cpp +++ b/mozilla/layout/generic/nsObjectFrame.cpp @@ -2286,6 +2286,8 @@ NS_IMETHODIMP nsPluginInstanceOwner::GetURL(const char *aURL, const char *aTarge } } + nsAutoPopupStatePusher popupStatePusher(openAllowed); + rv = lh->OnLinkClick(content, eLinkVerb_Replace, uri, unitarget.get(), postDataStream, headersDataStream); diff --git a/mozilla/layout/html/base/src/Makefile.in b/mozilla/layout/html/base/src/Makefile.in index 940d9191c19..30dd834283d 100644 --- a/mozilla/layout/html/base/src/Makefile.in +++ b/mozilla/layout/html/base/src/Makefile.in @@ -168,6 +168,7 @@ LOCAL_INCLUDES = \ -I$(srcdir)/../../style/src \ -I$(srcdir)/../../forms/src \ -I$(srcdir)/../../../base/src \ + -I$(srcdir)/../../../../content/events/src \ -I$(srcdir) \ $(NULL) diff --git a/mozilla/layout/html/base/src/nsObjectFrame.cpp b/mozilla/layout/html/base/src/nsObjectFrame.cpp index 59e082201df..76002cf11dc 100644 --- a/mozilla/layout/html/base/src/nsObjectFrame.cpp +++ b/mozilla/layout/html/base/src/nsObjectFrame.cpp @@ -2286,6 +2286,8 @@ NS_IMETHODIMP nsPluginInstanceOwner::GetURL(const char *aURL, const char *aTarge } } + nsAutoPopupStatePusher popupStatePusher(openAllowed); + rv = lh->OnLinkClick(content, eLinkVerb_Replace, uri, unitarget.get(), postDataStream, headersDataStream); diff --git a/mozilla/layout/html/base/src/nsPresShell.cpp b/mozilla/layout/html/base/src/nsPresShell.cpp index c5b364e9de3..1c2645ca397 100644 --- a/mozilla/layout/html/base/src/nsPresShell.cpp +++ b/mozilla/layout/html/base/src/nsPresShell.cpp @@ -76,8 +76,8 @@ #include "nsIViewObserver.h" #include "nsContainerFrame.h" #include "nsIDeviceContext.h" -#include "nsIEventStateManager.h" -#include "nsIDOMEvent.h" +#include "nsEventStateManager.h" +#include "nsDOMEvent.h" #include "nsGUIEvent.h" #include "nsHTMLParts.h" #include "nsContentUtils.h" @@ -5899,6 +5899,37 @@ PresShell::HandleEventInternal(nsEvent* aEvent, nsIView *aView, nsresult rv = NS_OK; if (!NS_EVENT_NEEDS_FRAME(aEvent) || GetCurrentEventFrame()) { + PRBool isHandlingUserInput = PR_FALSE; + + if (aEvent->internalAppFlags & NS_APP_EVENT_FLAG_TRUSTED) { + switch (aEvent->message) { + case NS_GOTFOCUS: + case NS_LOSTFOCUS: + case NS_ACTIVATE: + case NS_DEACTIVATE: + // Treat focus/blur events as user input if they happen while + // executing trusted script, or no script at all. If they + // happen during execution of non-trusted script, then they + // should not be considerd user input. + if (!nsContentUtils::IsCallerChrome()) { + break; + } + case NS_MOUSE_LEFT_BUTTON_DOWN: + case NS_MOUSE_MIDDLE_BUTTON_DOWN: + case NS_MOUSE_RIGHT_BUTTON_DOWN: + case NS_MOUSE_LEFT_BUTTON_UP: + case NS_MOUSE_RIGHT_BUTTON_UP: + case NS_MOUSE_MIDDLE_BUTTON_UP: + case NS_KEY_PRESS: + case NS_KEY_DOWN: + case NS_KEY_UP: + isHandlingUserInput = PR_TRUE; + } + } + + nsAutoHandlingUserInputStatePusher userInpStatePusher(isHandlingUserInput); + + nsAutoPopupStatePusher popupStatePusher(nsDOMEvent::GetEventPopupControlState(aEvent)); // 1. Give event to event manager for pre event state changes and // generation of synthetic events. diff --git a/mozilla/view/src/nsViewManager.cpp b/mozilla/view/src/nsViewManager.cpp index 69e2d8d5765..1cb3d693a9c 100644 --- a/mozilla/view/src/nsViewManager.cpp +++ b/mozilla/view/src/nsViewManager.cpp @@ -1772,6 +1772,11 @@ NS_IMETHODIMP nsViewManager::DispatchEvent(nsGUIEvent *aEvent, nsEventStatus *aS { *aStatus = nsEventStatus_eIgnore; + // Mark all events coming through here as trusted events, as the + // only code that calls this is the widget code that translates OS + // events into mozilla events. + aEvent->internalAppFlags |= NS_APP_EVENT_FLAG_TRUSTED; + switch(aEvent->message) { case NS_SIZE: diff --git a/mozilla/widget/public/nsGUIEvent.h b/mozilla/widget/public/nsGUIEvent.h index 97ae32be08c..bb53927e9c5 100644 --- a/mozilla/widget/public/nsGUIEvent.h +++ b/mozilla/widget/public/nsGUIEvent.h @@ -108,8 +108,15 @@ class nsIURI; #define NS_EVENT_CAPTURE_MASK (~(NS_EVENT_FLAG_INIT | NS_EVENT_FLAG_BUBBLE | NS_EVENT_FLAG_NO_CONTENT_DISPATCH)) #define NS_EVENT_BUBBLE_MASK (~(NS_EVENT_FLAG_INIT | NS_EVENT_FLAG_CAPTURE | NS_EVENT_FLAG_NO_CONTENT_DISPATCH)) -#define NS_APP_EVENT_FLAG_NONE 0x0000 -#define NS_APP_EVENT_FLAG_HANDLED 0x0001 // Similar to NS_EVENT_FLAG_NO_DEFAULT, but it allows focus +// Flags for internalAppFlags + +#define NS_APP_EVENT_FLAG_NONE 0x00000000 + +// Similar to NS_EVENT_FLAG_NO_DEFAULT, but it allows focus +#define NS_APP_EVENT_FLAG_HANDLED 0x00000001 + +// True if the event came from a trusted source +#define NS_APP_EVENT_FLAG_TRUSTED 0x00000002 #define NS_EVENT_TYPE_NULL 0 @@ -344,8 +351,8 @@ struct nsEvent { point(0, 0), refPoint(0, 0), time(0), - flags(0), - internalAppFlags(0), + flags(0), + internalAppFlags(NS_APP_EVENT_FLAG_NONE), userType(0) { }