From 339dd2bc38737c1cd1c0ecfcbab6b04cfbaa08c8 Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Sat, 4 Apr 2009 01:55:51 -0400 Subject: [PATCH] Backing out changeset ec2bec209571 and changeset 140f64990c51 (fix for bug 481566) due to test failures. --- content/base/src/nsContentSink.cpp | 202 +++++++++++++++----------- content/base/src/nsContentSink.h | 54 +++---- parser/htmlparser/public/nsIParser.h | 8 +- parser/htmlparser/src/nsParser.h | 2 +- widget/public/nsIWidget.h | 2 +- widget/src/os2/nsWindow.cpp | 8 +- widget/src/os2/nsWindow.h | 2 +- widget/src/windows/nsWindow.cpp | 13 +- widget/src/windows/nsWindow.h | 2 +- widget/src/xpwidgets/nsBaseWidget.cpp | 2 +- widget/src/xpwidgets/nsBaseWidget.h | 2 +- 11 files changed, 162 insertions(+), 135 deletions(-) diff --git a/content/base/src/nsContentSink.cpp b/content/base/src/nsContentSink.cpp index 3e85f737a77f..51cf63ef9273 100644 --- a/content/base/src/nsContentSink.cpp +++ b/content/base/src/nsContentSink.cpp @@ -274,33 +274,36 @@ nsContentSink::Init(nsIDocument* aDoc, mNotificationInterval = nsContentUtils::GetIntPref("content.notify.interval", 120000); - mInteractiveDeflectCount = - nsContentUtils::GetIntPref("content.sink.interactive_deflect_count", 0); - mPerfDeflectCount = - nsContentUtils::GetIntPref("content.sink.perf_deflect_count", 200); - mPendingEventMode = - nsContentUtils::GetIntPref("content.sink.pending_event_mode", 1); - mEventProbeRate = - nsContentUtils::GetIntPref("content.sink.event_probe_rate", 1); - mInteractiveParseTime = - nsContentUtils::GetIntPref("content.sink.interactive_parse_time", 3000); - mPerfParseTime = - nsContentUtils::GetIntPref("content.sink.perf_parse_time", 360000); - mInteractiveTime = - nsContentUtils::GetIntPref("content.sink.interactive_time", 750000); - mInitialPerfTime = - nsContentUtils::GetIntPref("content.sink.initial_perf_time", 2000000); - mEnablePerfMode = - nsContentUtils::GetIntPref("content.sink.enable_perf_mode", 0); + // The mMaxTokenProcessingTime controls how long we stay away from + // the event loop when processing token. A lower value makes the app + // more responsive, but may increase page load time. The content + // sink mNotificationInterval gates how frequently the content is + // processed so it will also affect how interactive the app is + // during page load also. The mNotification prevents contents + // flushes from happening too frequently. while + // mMaxTokenProcessingTime prevents flushes from happening too + // infrequently. - if (mEnablePerfMode != 0) { - mDynamicLowerValue = mEnablePerfMode == 1; - FavorPerformanceHint(!mDynamicLowerValue, 0); - } + // The current ratio of 3 to 1 was determined to be the lowest + // mMaxTokenProcessingTime which does not impact page load + // performance. See bugzilla bug 76722 for details. + + mMaxTokenProcessingTime = + nsContentUtils::GetIntPref("content.max.tokenizing.time", + mNotificationInterval * 3); + + // 3/4 second (750000us) default for switching + mDynamicIntervalSwitchThreshold = + nsContentUtils::GetIntPref("content.switch.threshold", 750000); mCanInterruptParser = nsContentUtils::GetBoolPref("content.interrupt.parsing", PR_TRUE); + // 200 determined empirically to provide good user response without + // sampling the clock too often. + mMaxTokensDeflectedInLowFreqMode = + nsContentUtils::GetIntPref("content.max.deflected.tokens", 200); + return NS_OK; } @@ -404,8 +407,6 @@ nsContentSink::ScriptEvaluated(nsresult aResult, nsIScriptElement *aElement, PRBool aIsInline) { - mDeflectedCount = mPerfDeflectCount; - if (mParser) { mParser->ScriptDidExecute(); } @@ -1518,51 +1519,111 @@ nsContentSink::WillResumeImpl() nsresult nsContentSink::DidProcessATokenImpl() { - if (!mCanInterruptParser || !mParser->CanInterrupt()) { + if (!mCanInterruptParser) { return NS_OK; } - + // There is both a high frequency interrupt mode and a low + // frequency interupt mode controlled by the flag + // mDynamicLowerValue The high frequency mode + // interupts the parser frequently to provide UI responsiveness at + // the expense of page load time. The low frequency mode + // interrupts the parser and samples the system clock infrequently + // to provide fast page load time. When the user moves the mouse, + // clicks or types the mode switches to the high frequency + // interrupt mode. If the user stops moving the mouse or typing + // for a duration of time (mDynamicIntervalSwitchThreshold) it + // switches to low frequency interrupt mode. // Get the current user event time nsIPresShell *shell = mDocument->GetPrimaryShell(); + if (!shell) { // If there's no pres shell in the document, return early since // we're not laying anything out here. return NS_OK; } - // Increase before comparing to mEventProbeRate - ++mDeflectedCount; + nsIViewManager* vm = shell->GetViewManager(); + NS_ENSURE_TRUE(vm, NS_ERROR_FAILURE); + PRUint32 eventTime; + nsCOMPtr widget; + nsresult rv = vm->GetWidget(getter_AddRefs(widget)); + if (!widget || NS_FAILED(widget->GetLastInputEventTime(eventTime))) { + // If we can't get the last input time from the widget + // then we will get it from the viewmanager. + rv = vm->GetLastUserEventTime(eventTime); + } - // Check if there's a pending event - if (mPendingEventMode != 0 && !mHasPendingEvent && - (mDeflectedCount % mEventProbeRate) == 0) { - nsIViewManager* vm = shell->GetViewManager(); - NS_ENSURE_TRUE(vm, NS_ERROR_FAILURE); - nsCOMPtr widget; - nsresult rv = vm->GetWidget(getter_AddRefs(widget)); - PRBool hasPendingEvent; - if (widget && NS_SUCCEEDED(widget->HasPendingEvent(hasPendingEvent)) && - hasPendingEvent) { - mHasPendingEvent = PR_TRUE; + NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE); + + if (!mDynamicLowerValue && mLastSampledUserEventTime == eventTime) { + // The default value of mMaxTokensDeflectedInLowFreqMode (200) + // was selected by empirical testing. It provides reasonable + // user response and prevents us from sampling the clock too + // frequently. This value may be decreased if responsiveness is + // valued more than end-to-end pageload time (e.g., for mobile). + if (mDeflectedCount < mMaxTokensDeflectedInLowFreqMode) { + mDeflectedCount++; + // return early to prevent sampling the clock. Note: This + // prevents us from switching to higher frequency (better UI + // responsive) mode, so limit ourselves to doing for no more + // than mMaxTokensDeflectedInLowFreqMode tokens. + + return NS_OK; + } + + // reset count and drop through to the code which samples the + // clock and does the dynamic switch between the high + // frequency and low frequency interruption of the parser. + mDeflectedCount = 0; + } + mLastSampledUserEventTime = eventTime; + + PRUint32 currentTime = PR_IntervalToMicroseconds(PR_IntervalNow()); + + // Get the last user event time and compare it with the current + // time to determine if the lower value for content notification + // and max token processing should be used. But only consider + // using the lower value if the document has already been loading + // for 2 seconds. 2 seconds was chosen because it is greater than + // the default 3/4 of second that is used to determine when to + // switch between the modes and it gives the document a little + // time to create windows. This is important because on some + // systems (Windows, for example) when a window is created and the + // mouse is over it, a mouse move event is sent, which will kick + // us into interactive mode otherwise. It also suppresses reaction + // to pressing the ENTER key in the URL bar... + + PRUint32 delayBeforeLoweringThreshold = + static_cast(((2 * mDynamicIntervalSwitchThreshold) + + NS_DELAY_FOR_WINDOW_CREATION)); + + if ((currentTime - mBeginLoadTime) > delayBeforeLoweringThreshold) { + if ((currentTime - eventTime) < + static_cast(mDynamicIntervalSwitchThreshold)) { + + if (!mDynamicLowerValue) { + // lower the dynamic values to favor application + // responsiveness over page load time. + mDynamicLowerValue = PR_TRUE; + // Set the performance hint to prevent event starvation when + // dispatching PLEvents. This improves application responsiveness + // during page loads. + FavorPerformanceHint(PR_FALSE, 0); + } + + } + else if (mDynamicLowerValue) { + // raise the content notification and MaxTokenProcessing time + // to favor overall page load speed over responsiveness. + mDynamicLowerValue = PR_FALSE; + // Reset the hint that to favoring performance for PLEvent dispatch. + FavorPerformanceHint(PR_TRUE, 0); } } - if (mHasPendingEvent && mPendingEventMode == 2) { - return NS_ERROR_HTMLPARSER_INTERRUPTED; - } - - // Have we processed enough tokens to check time? - if (!mHasPendingEvent && - mDeflectedCount < (mDynamicLowerValue ? mInteractiveDeflectCount : - mPerfDeflectCount)) { - return NS_OK; - } - - mDeflectedCount = 0; - - // Check if it's time to return to the main event loop - if (PR_IntervalToMicroseconds(PR_IntervalNow()) > mCurrentParseEndTime) { + if ((currentTime - mDelayTimerStart) > + static_cast(GetMaxTokenProcessingTime())) { return NS_ERROR_HTMLPARSER_INTERRUPTED; } @@ -1678,39 +1739,10 @@ nsContentSink::DropParserAndPerfHint(void) nsresult nsContentSink::WillParseImpl(void) { - if (!mCanInterruptParser || !mParser->CanInterrupt()) { - return NS_OK; + if (mCanInterruptParser) { + mDelayTimerStart = PR_IntervalToMicroseconds(PR_IntervalNow()); } - nsIPresShell *shell = mDocument->GetPrimaryShell(); - if (!shell) { - return NS_OK; - } - - PRUint32 currentTime = PR_IntervalToMicroseconds(PR_IntervalNow()); - - if (mEnablePerfMode == 0) { - nsIViewManager* vm = shell->GetViewManager(); - NS_ENSURE_TRUE(vm, NS_ERROR_FAILURE); - PRUint32 lastEventTime; - vm->GetLastUserEventTime(lastEventTime); - - PRBool newDynLower = - (currentTime - mBeginLoadTime) > mInitialPerfTime && - (currentTime - lastEventTime) < mInteractiveTime; - - if (mDynamicLowerValue != newDynLower) { - FavorPerformanceHint(!newDynLower, 0); - mDynamicLowerValue = newDynLower; - } - } - - mDeflectedCount = 0; - mHasPendingEvent = PR_FALSE; - - mCurrentParseEndTime = currentTime + - (mDynamicLowerValue ? mInteractiveParseTime : mPerfParseTime); - return NS_OK; } diff --git a/content/base/src/nsContentSink.h b/content/base/src/nsContentSink.h index 0ad5a04327d7..e2dddf2b90c0 100644 --- a/content/base/src/nsContentSink.h +++ b/content/base/src/nsContentSink.h @@ -270,6 +270,15 @@ protected: return mNotificationInterval; } + inline PRInt32 GetMaxTokenProcessingTime() + { + if (mDynamicLowerValue) { + return 3000; + } + + return mMaxTokenProcessingTime; + } + // Overridable hooks into script evaluation virtual void PreEvaluateScript() {return;} virtual void PostEvaluateScript(nsIScriptElement *aElement) {return;} @@ -316,6 +325,12 @@ protected: // Timer used for notification nsCOMPtr mNotificationTimer; + // The number of tokens that have been processed while in the low + // frequency parser interrupt mode without falling through to the + // logic which decides whether to switch to the high frequency + // parser interrupt mode. + PRUint8 mDeflectedCount; + // Do we notify based on time? PRPackedBool mNotifyOnTimer; @@ -335,43 +350,16 @@ protected: // If true, we did get a ReadyToCallDidBuildModel call PRUint8 mDidGetReadyToCallDidBuildModelCall : 1; - // // -- Can interrupt parsing members -- - // + PRUint32 mDelayTimerStart; - // The number of tokens that have been processed since we measured - // if it's time to return to the main event loop. - PRUint32 mDeflectedCount; + // Interrupt parsing during token procesing after # of microseconds + PRInt32 mMaxTokenProcessingTime; - // How many times to deflect in interactive/perf modes - PRInt32 mInteractiveDeflectCount; - PRInt32 mPerfDeflectCount; + // Switch between intervals when time is exceeded + PRInt32 mDynamicIntervalSwitchThreshold; - // 0 = don't check for pending events - // 1 = don't deflect if there are pending events - // 2 = bail if there are pending events - PRInt32 mPendingEventMode; - - // How often to probe for pending events. 1=every token - PRInt32 mEventProbeRate; - - // Is there currently a pending event? - PRBool mHasPendingEvent; - - // When to return to the main event loop - PRInt32 mCurrentParseEndTime; - - // How long to stay off the event loop in interactive/perf modes - PRInt32 mInteractiveParseTime; - PRInt32 mPerfParseTime; - - // How long to be in interactive mode after an event - PRInt32 mInteractiveTime; - // How long to stay in perf mode after initial loading - PRInt32 mInitialPerfTime; - - // Should we switch between perf-mode and interactive-mode - PRBool mEnablePerfMode; + PRInt32 mMaxTokensDeflectedInLowFreqMode; PRInt32 mBeginLoadTime; diff --git a/parser/htmlparser/public/nsIParser.h b/parser/htmlparser/public/nsIParser.h index 22b8016550de..04dc9e449008 100644 --- a/parser/htmlparser/public/nsIParser.h +++ b/parser/htmlparser/public/nsIParser.h @@ -1,4 +1,4 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=2 sw=2 et tw=78: */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 @@ -300,12 +300,6 @@ class nsIParser : public nsISupports { * continue the regular parsing process. */ virtual void ScriptDidExecute() = 0; - - /** - * True if the parser can currently be interrupted. Returns false when - * parsing for example document.write or innerHTML. - */ - virtual PRBool CanInterrupt() = 0; }; NS_DEFINE_STATIC_IID_ACCESSOR(nsIParser, NS_IPARSER_IID) diff --git a/parser/htmlparser/src/nsParser.h b/parser/htmlparser/src/nsParser.h index 0754b2c44f99..3864ff3bb2f4 100644 --- a/parser/htmlparser/src/nsParser.h +++ b/parser/htmlparser/src/nsParser.h @@ -339,7 +339,7 @@ class nsParser : public nsIParser, * @return PR_TRUE if parser can be interrupted, PR_FALSE if it can not be interrupted. * @update kmcclusk 5/18/98 */ - virtual PRBool CanInterrupt(); + PRBool CanInterrupt(void); /** * Set to parser state to indicate whether parsing tokens can be interrupted diff --git a/widget/public/nsIWidget.h b/widget/public/nsIWidget.h index ee13f0bd9e1b..fcc15c7f4610 100644 --- a/widget/public/nsIWidget.h +++ b/widget/public/nsIWidget.h @@ -917,7 +917,7 @@ class nsIWidget : public nsISupports { * is platform dependent, but is compatible with the expression * PR_IntervalToMicroseconds(PR_IntervalNow()). */ - NS_IMETHOD HasPendingEvent(PRBool& aHasPending) = 0; + NS_IMETHOD GetLastInputEventTime(PRUint32& aTime) = 0; /** * Called when when we need to begin secure keyboard input, such as when a password field diff --git a/widget/src/os2/nsWindow.cpp b/widget/src/os2/nsWindow.cpp index 41252a153fe9..ea523ae149e9 100644 --- a/widget/src/os2/nsWindow.cpp +++ b/widget/src/os2/nsWindow.cpp @@ -3549,12 +3549,16 @@ NS_METHOD nsWindow::SetPreferredSize(PRInt32 aWidth, PRInt32 aHeight) } NS_IMETHODIMP -nsWindow::HasPendingEvent(PRBool& aHasPending) +nsWindow::GetLastInputEventTime(PRUint32& aTime) { ULONG ulStatus = WinQueryQueueStatus(HWND_DESKTOP); // If there is pending input then return the current time. - aHasPending = !!(ulStatus & (QS_KEY | QS_MOUSE)); + if (ulStatus & (QS_KEY | QS_MOUSE)) { + gLastInputEventTime = PR_IntervalToMicroseconds(PR_IntervalNow()); + } + + aTime = gLastInputEventTime; return NS_OK; } diff --git a/widget/src/os2/nsWindow.h b/widget/src/os2/nsWindow.h index e55d805e24f9..e72ec45c4dc5 100644 --- a/widget/src/os2/nsWindow.h +++ b/widget/src/os2/nsWindow.h @@ -166,7 +166,7 @@ class nsWindow : public nsBaseWidget, NS_IMETHOD DispatchEvent( struct nsGUIEvent *event, nsEventStatus &aStatus); NS_IMETHOD CaptureRollupEvents(nsIRollupListener * aListener, PRBool aDoCapture, PRBool aConsumeRollupEvent); - NS_IMETHOD HasPendingEvent(PRBool& aHasPending); + NS_IMETHOD GetLastInputEventTime(PRUint32& aTime); // Widget appearance NS_IMETHOD SetColorMap( nsColorMap *aColorMap); diff --git a/widget/src/windows/nsWindow.cpp b/widget/src/windows/nsWindow.cpp index 3795193ee6fd..dcfbbe699c6b 100644 --- a/widget/src/windows/nsWindow.cpp +++ b/widget/src/windows/nsWindow.cpp @@ -8046,12 +8046,21 @@ void nsWindow::StopFlashing() } NS_IMETHODIMP -nsWindow::HasPendingEvent(PRBool& aHasPending) +nsWindow::GetLastInputEventTime(PRUint32& aTime) { WORD qstatus = HIWORD(GetQueueStatus(QS_INPUT)); + // If there is pending input or the user is currently + // moving the window then return the current time. + // Note: When the user is moving the window WIN32 spins + // a separate event loop and input events are not + // reported to the application. nsToolkit* toolkit = (nsToolkit *)mToolkit; - aHasPending = qstatus || (toolkit && toolkit->UserIsMovingWindow()) ; + if (qstatus || (toolkit && toolkit->UserIsMovingWindow())) { + gLastInputEventTime = PR_IntervalToMicroseconds(PR_IntervalNow()); + } + + aTime = gLastInputEventTime; return NS_OK; } diff --git a/widget/src/windows/nsWindow.h b/widget/src/windows/nsWindow.h index 5a6fc423c541..8dfbc36b0a23 100644 --- a/widget/src/windows/nsWindow.h +++ b/widget/src/windows/nsWindow.h @@ -209,7 +209,7 @@ public: NS_IMETHOD CaptureRollupEvents(nsIRollupListener * aListener, PRBool aDoCapture, PRBool aConsumeRollupEvent); NS_IMETHOD GetAttention(PRInt32 aCycleCount); - NS_IMETHOD HasPendingEvent(PRBool& aHasPending); + NS_IMETHOD GetLastInputEventTime(PRUint32& aTime); // Note that the result of GetTopLevelWindow method can be different from the // result of GetTopLevelHWND method. The result can be non-floating window. diff --git a/widget/src/xpwidgets/nsBaseWidget.cpp b/widget/src/xpwidgets/nsBaseWidget.cpp index 36195096925f..f22e8e170b39 100644 --- a/widget/src/xpwidgets/nsBaseWidget.cpp +++ b/widget/src/xpwidgets/nsBaseWidget.cpp @@ -843,7 +843,7 @@ nsBaseWidget::GetAttention(PRInt32 aCycleCount) { } NS_IMETHODIMP -nsBaseWidget::HasPendingEvent(PRBool& aHasPending) { +nsBaseWidget::GetLastInputEventTime(PRUint32& aTime) { return NS_ERROR_NOT_IMPLEMENTED; } diff --git a/widget/src/xpwidgets/nsBaseWidget.h b/widget/src/xpwidgets/nsBaseWidget.h index 04d84e7c5492..b79a28cf3618 100644 --- a/widget/src/xpwidgets/nsBaseWidget.h +++ b/widget/src/xpwidgets/nsBaseWidget.h @@ -127,7 +127,7 @@ public: NS_IMETHOD ScrollWidgets(PRInt32 aDx, PRInt32 aDy); NS_IMETHOD EnableDragDrop(PRBool aEnable); NS_IMETHOD GetAttention(PRInt32 aCycleCount); - NS_IMETHOD HasPendingEvent(PRBool& aHasPending); + NS_IMETHOD GetLastInputEventTime(PRUint32& aTime); NS_IMETHOD SetIcon(const nsAString &anIconSpec); NS_IMETHOD BeginSecureKeyboardInput(); NS_IMETHOD EndSecureKeyboardInput();