зеркало из https://github.com/mozilla/pjs.git
Bug 442774 Wheel/touchpad scrolling gets stuck in frame, stop scrolling the web page as a whole r=Olli.pettay, sr=roc
This commit is contained in:
Родитель
8bec6ceb1b
Коммит
bfceec2bca
|
@ -375,47 +375,88 @@ class nsMouseWheelTransaction {
|
|||
public:
|
||||
static nsIFrame* GetTargetFrame() { return sTargetFrame; }
|
||||
static void BeginTransaction(nsIFrame* aTargetFrame,
|
||||
nsGUIEvent* aEvent);
|
||||
static void UpdateTransaction();
|
||||
PRInt32 aNumLines,
|
||||
PRBool aScrollHorizontal);
|
||||
static PRBool UpdateTransaction(PRInt32 aNumLines,
|
||||
PRBool aScrollHorizontal);
|
||||
static void EndTransaction();
|
||||
static void OnEvent(nsEvent* aEvent);
|
||||
static void Shutdown();
|
||||
protected:
|
||||
static nsIntPoint GetScreenPoint(nsGUIEvent* aEvent);
|
||||
static void OnFailToScrollTarget();
|
||||
static void OnTimeout(nsITimer *aTimer, void *aClosure);
|
||||
static void SetTimeout();
|
||||
static PRUint32 GetTimeoutTime();
|
||||
static PRUint32 GetIgnoreMoveDelayTime();
|
||||
|
||||
static nsWeakFrame sTargetFrame;
|
||||
static PRUint32 sTime; // in milliseconds
|
||||
static PRUint32 sMouseMoved; // in milliseconds
|
||||
static nsITimer* sTimer;
|
||||
};
|
||||
|
||||
nsWeakFrame nsMouseWheelTransaction::sTargetFrame(nsnull);
|
||||
PRUint32 nsMouseWheelTransaction::sTime = 0;
|
||||
PRUint32 nsMouseWheelTransaction::sMouseMoved = 0;
|
||||
nsITimer* nsMouseWheelTransaction::sTimer = nsnull;
|
||||
|
||||
void
|
||||
nsMouseWheelTransaction::BeginTransaction(nsIFrame* aTargetFrame,
|
||||
nsGUIEvent* aEvent)
|
||||
static PRBool
|
||||
CanScrollOn(nsIScrollableView* aScrollView, PRInt32 aNumLines,
|
||||
PRBool aScrollHorizontal)
|
||||
{
|
||||
NS_ASSERTION(!sTargetFrame, "previous transaction is not finished!");
|
||||
sTargetFrame = aTargetFrame;
|
||||
UpdateTransaction();
|
||||
NS_PRECONDITION(aScrollView, "aScrollView is null");
|
||||
NS_PRECONDITION(aNumLines, "aNumLines must be non-zero");
|
||||
PRBool canScroll;
|
||||
nsresult rv =
|
||||
aScrollView->CanScroll(aScrollHorizontal, aNumLines > 0, canScroll);
|
||||
return NS_SUCCEEDED(rv) && canScroll;
|
||||
}
|
||||
|
||||
void
|
||||
nsMouseWheelTransaction::UpdateTransaction()
|
||||
nsMouseWheelTransaction::BeginTransaction(nsIFrame* aTargetFrame,
|
||||
PRInt32 aNumLines,
|
||||
PRBool aScrollHorizontal)
|
||||
{
|
||||
NS_ASSERTION(!sTargetFrame, "previous transaction is not finished!");
|
||||
sTargetFrame = aTargetFrame;
|
||||
if (!UpdateTransaction(aNumLines, aScrollHorizontal)) {
|
||||
NS_ERROR("BeginTransaction is called even cannot scroll the frame");
|
||||
EndTransaction();
|
||||
}
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsMouseWheelTransaction::UpdateTransaction(PRInt32 aNumLines,
|
||||
PRBool aScrollHorizontal)
|
||||
{
|
||||
nsIScrollableViewProvider* svp = do_QueryFrame(GetTargetFrame());
|
||||
NS_ENSURE_TRUE(svp, PR_FALSE);
|
||||
nsIScrollableView *scrollView = svp->GetScrollableView();
|
||||
NS_ENSURE_TRUE(scrollView, PR_FALSE);
|
||||
|
||||
if (!CanScrollOn(scrollView, aNumLines, aScrollHorizontal)) {
|
||||
OnFailToScrollTarget();
|
||||
// We should not modify the transaction state when the view will not be
|
||||
// scrolled actually.
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
SetTimeout();
|
||||
// We should use current time instead of nsEvent.time.
|
||||
// 1. Some events doesn't have the correct creation time.
|
||||
// 2. If the computer runs slowly by other processes eating the CPU resource,
|
||||
// the event creation time doesn't keep real time.
|
||||
sTime = PR_IntervalToMilliseconds(PR_IntervalNow());
|
||||
sMouseMoved = 0;
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
nsMouseWheelTransaction::EndTransaction()
|
||||
{
|
||||
if (sTimer)
|
||||
sTimer->Cancel();
|
||||
sTargetFrame = nsnull;
|
||||
}
|
||||
|
||||
|
@ -433,8 +474,11 @@ nsMouseWheelTransaction::OnEvent(nsEvent* aEvent)
|
|||
return;
|
||||
|
||||
if (OutOfTime(sTime, GetTimeoutTime())) {
|
||||
// Time out the current transaction.
|
||||
EndTransaction();
|
||||
// Even if the scroll event which is handled after timeout, but onTimeout
|
||||
// was not fired by timer, then the scroll event will scroll old frame,
|
||||
// therefore, we should call OnTimeout here and ensure to finish the old
|
||||
// transaction.
|
||||
OnTimeout(nsnull, nsnull);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -483,6 +527,61 @@ nsMouseWheelTransaction::OnEvent(nsEvent* aEvent)
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsMouseWheelTransaction::Shutdown()
|
||||
{
|
||||
NS_IF_RELEASE(sTimer);
|
||||
}
|
||||
|
||||
void
|
||||
nsMouseWheelTransaction::OnFailToScrollTarget()
|
||||
{
|
||||
NS_PRECONDITION(sTargetFrame, "We don't have mouse scrolling transaction");
|
||||
// This event is used for automated tests, see bug 442774.
|
||||
nsContentUtils::DispatchTrustedEvent(
|
||||
sTargetFrame->GetContent()->GetOwnerDoc(),
|
||||
sTargetFrame->GetContent(),
|
||||
NS_LITERAL_STRING("MozMouseScrollFailed"),
|
||||
PR_TRUE, PR_TRUE);
|
||||
}
|
||||
|
||||
void
|
||||
nsMouseWheelTransaction::OnTimeout(nsITimer* aTimer, void* aClosure)
|
||||
{
|
||||
if (!sTargetFrame) {
|
||||
// The transaction target was destroyed already
|
||||
EndTransaction();
|
||||
return;
|
||||
}
|
||||
// Store the sTargetFrame, the variable becomes null in EndTransaction.
|
||||
nsIFrame* frame = sTargetFrame;
|
||||
// We need to finish current transaction before DOM event firing. Because
|
||||
// the next DOM event might create strange situation for us.
|
||||
EndTransaction();
|
||||
// This event is used for automated tests, see bug 442774.
|
||||
nsContentUtils::DispatchTrustedEvent(
|
||||
frame->GetContent()->GetOwnerDoc(),
|
||||
frame->GetContent(),
|
||||
NS_LITERAL_STRING("MozMouseScrollTransactionTimeout"),
|
||||
PR_TRUE, PR_TRUE);
|
||||
}
|
||||
|
||||
void
|
||||
nsMouseWheelTransaction::SetTimeout()
|
||||
{
|
||||
if (!sTimer) {
|
||||
nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID);
|
||||
if (!timer)
|
||||
return;
|
||||
timer.swap(sTimer);
|
||||
}
|
||||
sTimer->Cancel();
|
||||
nsresult rv =
|
||||
sTimer->InitWithFuncCallback(OnTimeout, nsnull, GetTimeoutTime(),
|
||||
nsITimer::TYPE_ONE_SHOT);
|
||||
NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "nsITimer::InitWithFuncCallback failed");
|
||||
}
|
||||
|
||||
nsIntPoint
|
||||
nsMouseWheelTransaction::GetScreenPoint(nsGUIEvent* aEvent)
|
||||
{
|
||||
|
@ -621,6 +720,7 @@ nsEventStateManager::~nsEventStateManager()
|
|||
|
||||
--sESMInstanceCount;
|
||||
if(sESMInstanceCount == 0) {
|
||||
nsMouseWheelTransaction::Shutdown();
|
||||
NS_IF_RELEASE(gLastFocusedContent);
|
||||
NS_IF_RELEASE(gLastFocusedDocument);
|
||||
if (gUserInteractionTimerCallback) {
|
||||
|
@ -2632,9 +2732,8 @@ nsEventStateManager::DoScrollText(nsPresContext* aPresContext,
|
|||
nsIFrame* lastScrollFrame = nsMouseWheelTransaction::GetTargetFrame();
|
||||
if (lastScrollFrame) {
|
||||
nsIScrollableViewProvider* svp = do_QueryFrame(lastScrollFrame);
|
||||
if (svp) {
|
||||
scrollView = svp->GetScrollableView();
|
||||
nsMouseWheelTransaction::UpdateTransaction();
|
||||
if (svp && (scrollView = svp->GetScrollableView())) {
|
||||
nsMouseWheelTransaction::UpdateTransaction(aNumLines, aScrollHorizontal);
|
||||
} else {
|
||||
nsMouseWheelTransaction::EndTransaction();
|
||||
lastScrollFrame = nsnull;
|
||||
|
@ -2666,12 +2765,10 @@ nsEventStateManager::DoScrollText(nsPresContext* aPresContext,
|
|||
scrollView->GetLineHeight(&lineHeight);
|
||||
|
||||
if (lineHeight != 0) {
|
||||
PRBool canScroll;
|
||||
nsresult rv = scrollView->CanScroll(aScrollHorizontal,
|
||||
(aNumLines > 0), canScroll);
|
||||
if (NS_SUCCEEDED(rv) && canScroll) {
|
||||
if (CanScrollOn(scrollView, aNumLines, aScrollHorizontal)) {
|
||||
passToParent = PR_FALSE;
|
||||
nsMouseWheelTransaction::BeginTransaction(scrollFrame, aEvent);
|
||||
nsMouseWheelTransaction::BeginTransaction(scrollFrame,
|
||||
aNumLines, aScrollHorizontal);
|
||||
}
|
||||
|
||||
// Comboboxes need special care.
|
||||
|
|
|
@ -48,7 +48,7 @@
|
|||
interface nsIDOMElement;
|
||||
interface nsIDOMHTMLCanvasElement;
|
||||
|
||||
[scriptable, uuid(190be8e6-35af-4e3e-9a9f-719f5b1a44a0)]
|
||||
[scriptable, uuid(8C6263C9-F3EF-419d-80EF-D5D716635FAA)]
|
||||
interface nsIDOMWindowUtils : nsISupports {
|
||||
|
||||
/**
|
||||
|
@ -297,4 +297,16 @@ interface nsIDOMWindowUtils : nsISupports {
|
|||
* fired.
|
||||
*/
|
||||
readonly attribute boolean isMozAfterPaintPending;
|
||||
|
||||
/**
|
||||
* Disable or enable non synthetic test mouse events on *all* windows.
|
||||
*
|
||||
* Cannot be accessed from unprivileged context (not content-accessible).
|
||||
* Will throw a DOM security error if called without UniversalXPConnect
|
||||
* privileges.
|
||||
*
|
||||
* @param aDisable If true, disable all non synthetic test mouse events
|
||||
* on all windows. Otherwise, enable them.
|
||||
*/
|
||||
void disableNonTestMouseEvents(in boolean aDisable);
|
||||
};
|
||||
|
|
|
@ -249,6 +249,7 @@ nsDOMWindowUtils::SendMouseEvent(const nsAString& aType,
|
|||
|
||||
event.clickCount = aClickCount;
|
||||
event.time = PR_IntervalNow();
|
||||
event.flags |= NS_EVENT_FLAG_SYNTETIC_TEST_EVENT;
|
||||
|
||||
float appPerDev = float(widget->GetDeviceContext()->AppUnitsPerDevPixel());
|
||||
event.refPoint.x =
|
||||
|
@ -681,3 +682,21 @@ nsDOMWindowUtils::GetIsMozAfterPaintPending(PRBool *aResult)
|
|||
*aResult = presContext->IsDOMPaintEventPending();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWindowUtils::DisableNonTestMouseEvents(PRBool aDisable)
|
||||
{
|
||||
PRBool hasCap = PR_FALSE;
|
||||
if (NS_FAILED(nsContentUtils::GetSecurityManager()->
|
||||
IsCapabilityEnabled("UniversalXPConnect", &hasCap)) ||
|
||||
!hasCap)
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
|
||||
NS_ENSURE_TRUE(mWindow, NS_ERROR_FAILURE);
|
||||
nsIDocShell *docShell = mWindow->GetDocShell();
|
||||
NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE);
|
||||
nsCOMPtr<nsIPresShell> presShell;
|
||||
docShell->GetPresShell(getter_AddRefs(presShell));
|
||||
NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
|
||||
return presShell->DisableNonTestMouseEvents(aDisable);
|
||||
}
|
||||
|
|
|
@ -101,10 +101,10 @@ class gfxContext;
|
|||
typedef short SelectionType;
|
||||
typedef PRUint32 nsFrameState;
|
||||
|
||||
// b86c23c5-602d-4ca6-a968-379b244fed9e
|
||||
// 780d34b0-00c3-4bbd-b57d-c600aaf53613
|
||||
#define NS_IPRESSHELL_IID \
|
||||
{ 0xb86c23c5, 0x602d, 0x4ca6, \
|
||||
{ 0xa9, 0x68, 0x37, 0x9b, 0x24, 0x4f, 0xed, 0x9e } }
|
||||
{ 0x780d34b0, 0xc3, 0x4bbd, \
|
||||
{ 0xb5, 0x7d, 0xc6, 0x0, 0xaa, 0xf5, 0x36, 0x13 } }
|
||||
|
||||
// Constants for ScrollContentIntoView() function
|
||||
#define NS_PRESSHELL_SCROLL_TOP 0
|
||||
|
@ -772,6 +772,15 @@ public:
|
|||
nsIFrame* GetDrawEventTargetFrame() { return mDrawEventTargetFrame; }
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Stop or restart non synthetic test mouse event handling on *all*
|
||||
* presShells.
|
||||
*
|
||||
* @param aDisable If true, disable all non synthetic test mouse events on all
|
||||
* presShells. Otherwise, enable them.
|
||||
*/
|
||||
NS_IMETHOD DisableNonTestMouseEvents(PRBool aDisable) = 0;
|
||||
|
||||
protected:
|
||||
// IMPORTANT: The ownership implicit in the following member variables
|
||||
// has been explicitly checked. If you add any members to this class,
|
||||
|
|
|
@ -1009,6 +1009,8 @@ public:
|
|||
static PRLogModuleInfo* gLog;
|
||||
#endif
|
||||
|
||||
NS_IMETHOD DisableNonTestMouseEvents(PRBool aDisable);
|
||||
|
||||
protected:
|
||||
virtual ~PresShell();
|
||||
|
||||
|
@ -1175,6 +1177,8 @@ protected:
|
|||
ReflowCountMgr * mReflowCountMgr;
|
||||
#endif
|
||||
|
||||
static PRBool sDisableNonTestMouseEvents;
|
||||
|
||||
private:
|
||||
|
||||
PRBool InZombieDocument(nsIContent *aContent);
|
||||
|
@ -1250,6 +1254,8 @@ public:
|
|||
nsRefPtr<PresShell> mPresShell;
|
||||
};
|
||||
|
||||
PRBool PresShell::sDisableNonTestMouseEvents = PR_FALSE;
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
PRLogModuleInfo* PresShell::gLog;
|
||||
#endif
|
||||
|
@ -5530,6 +5536,13 @@ nsresult PresShell::RetargetEventToParent(nsGUIEvent* aEvent,
|
|||
aEventStatus);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PresShell::DisableNonTestMouseEvents(PRBool aDisable)
|
||||
{
|
||||
sDisableNonTestMouseEvents = aDisable;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PresShell::HandleEvent(nsIView *aView,
|
||||
nsGUIEvent* aEvent,
|
||||
|
@ -5537,7 +5550,9 @@ PresShell::HandleEvent(nsIView *aView,
|
|||
{
|
||||
NS_ASSERTION(aView, "null view");
|
||||
|
||||
if (mIsDestroying || !nsContentUtils::IsSafeToRunScript()) {
|
||||
if (mIsDestroying || !nsContentUtils::IsSafeToRunScript() ||
|
||||
(sDisableNonTestMouseEvents && NS_IS_MOUSE_EVENT(aEvent) &&
|
||||
!(aEvent->flags & NS_EVENT_FLAG_SYNTETIC_TEST_EVENT))) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -264,8 +264,10 @@ function synthesizeMouseScroll(aTarget, aOffsetX, aOffsetY, aEvent, aWindow)
|
|||
var button = aEvent.button || 0;
|
||||
var modifiers = _parseModifiers(aEvent);
|
||||
|
||||
var left = aTarget.boxObject.x;
|
||||
var top = aTarget.boxObject.y;
|
||||
var rect = aTarget.getBoundingClientRect();
|
||||
|
||||
var left = rect.left;
|
||||
var top = rect.top;
|
||||
|
||||
var type = aEvent.type || "DOMMouseScroll";
|
||||
var axis = aEvent.axis || "vertical";
|
||||
|
@ -516,3 +518,14 @@ function synthesizeDrop(element, dragData, effectAllowed)
|
|||
|
||||
return dataTransfer.dropEffect;
|
||||
}
|
||||
|
||||
function disableNonTestMouseEvents(aDisable)
|
||||
{
|
||||
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
|
||||
|
||||
var utils =
|
||||
window.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
|
||||
getInterface(Components.interfaces.nsIDOMWindowUtils);
|
||||
if (utils)
|
||||
utils.disableNonTestMouseEvents(aDisable);
|
||||
}
|
||||
|
|
|
@ -124,6 +124,11 @@ class nsHashKey;
|
|||
// Event has been dispatched at least once
|
||||
#define NS_EVENT_DISPATCHED 0x0400
|
||||
#define NS_EVENT_FLAG_DISPATCHING 0x0800
|
||||
// When an event is synthesized for testing, this flag will be set.
|
||||
// Note that this is currently used only with mouse events. Because this flag
|
||||
// is not needed on other events now. Therfore, if you need this flag on other
|
||||
// events, you can do it.
|
||||
#define NS_EVENT_FLAG_SYNTETIC_TEST_EVENT 0x1000
|
||||
|
||||
#define NS_PRIV_EVENT_UNTRUSTED_PERMITTED 0x8000
|
||||
|
||||
|
|
|
@ -61,6 +61,8 @@ _TEST_FILES = test_bug343416.xul \
|
|||
test_bug444800.xul \
|
||||
test_bug462106.xul \
|
||||
test_keycodes.xul \
|
||||
test_wheeltransaction.xul \
|
||||
window_wheeltransaction.xul \
|
||||
$(NULL)
|
||||
|
||||
ifeq ($(MOZ_WIDGET_TOOLKIT),cocoa)
|
||||
|
|
|
@ -701,6 +701,10 @@ function runTextInputTests()
|
|||
testKey({layout:"Lithuanian", keyCode:48, ctrl:1, alt:1, chars:"0"},
|
||||
"0");
|
||||
}
|
||||
|
||||
// XXX We need to move focus for canceling to search the autocomplete
|
||||
// result. If we don't do here, Fx will crash at end of this tests.
|
||||
document.getElementById("button").focus();
|
||||
}
|
||||
|
||||
function runTest()
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
<?xml version="1.0"?>
|
||||
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
|
||||
type="text/css"?>
|
||||
<window title="Wheel scroll transaction tests"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/MochiKit/packed.js"/>
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
|
||||
|
||||
<body xmlns="http://www.w3.org/1999/xhtml">
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
</body>
|
||||
|
||||
<script class="testbody" type="application/javascript">
|
||||
<![CDATA[
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
window.open("window_wheeltransaction.xul", "_blank",
|
||||
"chrome,width=600,height=600");
|
||||
|
||||
]]>
|
||||
</script>
|
||||
</window>
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Загрузка…
Ссылка в новой задаче