This commit is contained in:
Jonas Sicking 2009-04-03 18:25:13 -07:00
Родитель 65ded66704
Коммит 765216df58
10 изменённых файлов: 133 добавлений и 151 удалений

Просмотреть файл

@ -274,36 +274,33 @@ nsContentSink::Init(nsIDocument* aDoc,
mNotificationInterval =
nsContentUtils::GetIntPref("content.notify.interval", 120000);
// 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.
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 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);
if (mEnablePerfMode != 0) {
mDynamicLowerValue = mEnablePerfMode == 1;
FavorPerformanceHint(!mDynamicLowerValue, 0);
}
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;
}
@ -407,6 +404,8 @@ nsContentSink::ScriptEvaluated(nsresult aResult,
nsIScriptElement *aElement,
PRBool aIsInline)
{
mDeflectedCount = mPerfDeflectCount;
if (mParser) {
mParser->ScriptDidExecute();
}
@ -1519,111 +1518,51 @@ nsContentSink::WillResumeImpl()
nsresult
nsContentSink::DidProcessATokenImpl()
{
if (!mCanInterruptParser) {
if (!mCanInterruptParser || !mParser->CanInterrupt()) {
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;
}
nsIViewManager* vm = shell->GetViewManager();
NS_ENSURE_TRUE(vm, NS_ERROR_FAILURE);
PRUint32 eventTime;
nsCOMPtr<nsIWidget> 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);
}
// Increase before comparing to mEventProbeRate
++mDeflectedCount;
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<PRUint32>(((2 * mDynamicIntervalSwitchThreshold) +
NS_DELAY_FOR_WINDOW_CREATION));
if ((currentTime - mBeginLoadTime) > delayBeforeLoweringThreshold) {
if ((currentTime - eventTime) <
static_cast<PRUint32>(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);
// 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<nsIWidget> widget;
nsresult rv = vm->GetWidget(getter_AddRefs(widget));
PRBool hasPendingEvent;
if (widget && NS_SUCCEEDED(widget->HasPendingEvent(hasPendingEvent)) &&
hasPendingEvent) {
mHasPendingEvent = PR_TRUE;
}
}
if ((currentTime - mDelayTimerStart) >
static_cast<PRUint32>(GetMaxTokenProcessingTime())) {
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) {
return NS_ERROR_HTMLPARSER_INTERRUPTED;
}
@ -1739,10 +1678,39 @@ nsContentSink::DropParserAndPerfHint(void)
nsresult
nsContentSink::WillParseImpl(void)
{
if (mCanInterruptParser) {
mDelayTimerStart = PR_IntervalToMicroseconds(PR_IntervalNow());
if (!mCanInterruptParser || !mParser->CanInterrupt()) {
return NS_OK;
}
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;
}

Просмотреть файл

@ -270,15 +270,6 @@ 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;}
@ -325,12 +316,6 @@ protected:
// Timer used for notification
nsCOMPtr<nsITimer> 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;
@ -350,16 +335,43 @@ protected:
// If true, we did get a ReadyToCallDidBuildModel call
PRUint8 mDidGetReadyToCallDidBuildModelCall : 1;
//
// -- Can interrupt parsing members --
PRUint32 mDelayTimerStart;
//
// Interrupt parsing during token procesing after # of microseconds
PRInt32 mMaxTokenProcessingTime;
// The number of tokens that have been processed since we measured
// if it's time to return to the main event loop.
PRUint32 mDeflectedCount;
// Switch between intervals when time is exceeded
PRInt32 mDynamicIntervalSwitchThreshold;
// How many times to deflect in interactive/perf modes
PRInt32 mInteractiveDeflectCount;
PRInt32 mPerfDeflectCount;
PRInt32 mMaxTokensDeflectedInLowFreqMode;
// 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 mBeginLoadTime;

Просмотреть файл

@ -1,4 +1,4 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim: set ts=2 sw=2 et tw=78: */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
@ -300,6 +300,12 @@ 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)

Просмотреть файл

@ -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
*/
PRBool CanInterrupt(void);
virtual PRBool CanInterrupt();
/**
* Set to parser state to indicate whether parsing tokens can be interrupted

Просмотреть файл

@ -917,7 +917,7 @@ class nsIWidget : public nsISupports {
* is platform dependent, but is compatible with the expression
* PR_IntervalToMicroseconds(PR_IntervalNow()).
*/
NS_IMETHOD GetLastInputEventTime(PRUint32& aTime) = 0;
NS_IMETHOD HasPendingEvent(PRBool& aHasPending) = 0;
/**
* Called when when we need to begin secure keyboard input, such as when a password field

Просмотреть файл

@ -3549,16 +3549,12 @@ NS_METHOD nsWindow::SetPreferredSize(PRInt32 aWidth, PRInt32 aHeight)
}
NS_IMETHODIMP
nsWindow::GetLastInputEventTime(PRUint32& aTime)
nsWindow::HasPendingEvent(PRBool& aHasPending)
{
ULONG ulStatus = WinQueryQueueStatus(HWND_DESKTOP);
// If there is pending input then return the current time.
if (ulStatus & (QS_KEY | QS_MOUSE)) {
gLastInputEventTime = PR_IntervalToMicroseconds(PR_IntervalNow());
}
aTime = gLastInputEventTime;
*aHasPending = !!(ulStatus & (QS_KEY | QS_MOUSE));
return NS_OK;
}

Просмотреть файл

@ -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 GetLastInputEventTime(PRUint32& aTime);
NS_IMETHOD HasPendingEvent(PRBool& aHasPending);
// Widget appearance
NS_IMETHOD SetColorMap( nsColorMap *aColorMap);

Просмотреть файл

@ -209,7 +209,7 @@ public:
NS_IMETHOD CaptureRollupEvents(nsIRollupListener * aListener, PRBool aDoCapture, PRBool aConsumeRollupEvent);
NS_IMETHOD GetAttention(PRInt32 aCycleCount);
NS_IMETHOD GetLastInputEventTime(PRUint32& aTime);
NS_IMETHOD HasPendingEvent(PRBool& aHasPending);
// Note that the result of GetTopLevelWindow method can be different from the
// result of GetTopLevelHWND method. The result can be non-floating window.

Просмотреть файл

@ -843,7 +843,7 @@ nsBaseWidget::GetAttention(PRInt32 aCycleCount) {
}
NS_IMETHODIMP
nsBaseWidget::GetLastInputEventTime(PRUint32& aTime) {
nsBaseWidget::HasPendingEvent(PRBool& aHasPending) {
return NS_ERROR_NOT_IMPLEMENTED;
}

Просмотреть файл

@ -127,7 +127,7 @@ public:
NS_IMETHOD ScrollWidgets(PRInt32 aDx, PRInt32 aDy);
NS_IMETHOD EnableDragDrop(PRBool aEnable);
NS_IMETHOD GetAttention(PRInt32 aCycleCount);
NS_IMETHOD GetLastInputEventTime(PRUint32& aTime);
NS_IMETHOD HasPendingEvent(PRBool& aHasPending);
NS_IMETHOD SetIcon(const nsAString &anIconSpec);
NS_IMETHOD BeginSecureKeyboardInput();
NS_IMETHOD EndSecureKeyboardInput();