diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp index 43f25187cb0..566d76bfae9 100644 --- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -200,6 +200,8 @@ #include "nsIBrowserSearchService.h" #endif +static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID); + #if defined(DEBUG_bryner) || defined(DEBUG_chb) //#define DEBUG_DOCSHELL_FOCUS #define DEBUG_PAGE_CACHE @@ -220,6 +222,9 @@ static bool gAddedPreferencesVarCache = false; bool nsDocShell::sUseErrorPages = false; +// Number of documents currently loading +static int32_t gNumberOfDocumentsLoading = 0; + // Global count of existing docshells. static int32_t gDocShellCount = 0; @@ -250,6 +255,18 @@ static PRLogModuleInfo* gDocShellLeakLog; const char kBrandBundleURL[] = "chrome://branding/locale/brand.properties"; const char kAppstringsBundleURL[] = "chrome://global/locale/appstrings.properties"; +static void +FavorPerformanceHint(bool aPerfOverStarvation) +{ + nsCOMPtr appShell = do_GetService(kAppShellCID); + if (appShell) { + appShell->FavorPerformanceHint( + aPerfOverStarvation, + Preferences::GetUint("docshell.event_starvation_delay_hint", + NS_EVENT_STARVATION_DELAY_HINT)); + } +} + //***************************************************************************** // support //***************************************************************************** @@ -7524,6 +7541,14 @@ nsDocShell::EndPageLoad(nsIWebProgress* aProgress, mIsExecutingOnLoadHandler = false; mEODForCurrentDocument = true; + + // If all documents have completed their loading + // favor native event dispatch priorities + // over performance + if (--gNumberOfDocumentsLoading == 0) { + // Hint to use normal native event dispatch priorities + FavorPerformanceHint(false); + } } /* Check if the httpChannel has any cache-control related response headers, * like no-store, no-cache. If so, update SHEntry so that @@ -8653,6 +8678,12 @@ nsDocShell::RestoreFromHistory() mSavingOldViewer = false; mEODForCurrentDocument = false; + // Tell the event loop to favor plevents over user events, see comments + // in CreateContentViewer. + if (++gNumberOfDocumentsLoading == 1) { + FavorPerformanceHint(true); + } + if (oldCv && newCv) { newCv->SetMinFontSize(minFontSize); newCv->SetTextZoom(textZoom); @@ -9065,6 +9096,16 @@ nsDocShell::CreateContentViewer(const nsACString& aContentType, } } + // Give hint to native plevent dispatch mechanism. If a document + // is loading the native plevent dispatch mechanism should favor + // performance over normal native event dispatch priorities. + if (++gNumberOfDocumentsLoading == 1) { + // Hint to favor performance for the plevent notification mechanism. + // We want the pages to load as fast as possible even if its means + // native messages might be starved. + FavorPerformanceHint(true); + } + if (onLocationChangeNeeded) { FireOnLocationChange(this, aRequest, mCurrentURI, 0); } diff --git a/dom/base/nsContentSink.cpp b/dom/base/nsContentSink.cpp index eca7fddf243..8ef3c075275 100644 --- a/dom/base/nsContentSink.cpp +++ b/dom/base/nsContentSink.cpp @@ -201,6 +201,7 @@ nsContentSink::Init(nsIDocument* aDoc, if (sEnablePerfMode != 0) { mDynamicLowerValue = sEnablePerfMode == 1; + FavorPerformanceHint(!mDynamicLowerValue, 0); } return NS_OK; @@ -1407,6 +1408,15 @@ nsContentSink::DidProcessATokenImpl() //---------------------------------------------------------------------- +void +nsContentSink::FavorPerformanceHint(bool perfOverStarvation, uint32_t starvationDelay) +{ + static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID); + nsCOMPtr appShell = do_GetService(kAppShellCID); + if (appShell) + appShell->FavorPerformanceHint(perfOverStarvation, starvationDelay); +} + void nsContentSink::BeginUpdate(nsIDocument *aDocument, nsUpdateType aUpdateType) { @@ -1484,6 +1494,12 @@ nsContentSink::DropParserAndPerfHint(void) // reference. nsRefPtr kungFuDeathGrip(mParser.forget()); + if (mDynamicLowerValue) { + // Reset the performance hint which was set to FALSE + // when mDynamicLowerValue was set. + FavorPerformanceHint(true, 0); + } + if (!mRunsToCompletion) { mDocument->UnblockOnload(true); } @@ -1521,6 +1537,7 @@ nsContentSink::WillParseImpl(void) (currentTime - lastEventTime) < uint32_t(sInteractiveTime)); if (mDynamicLowerValue != newDynLower) { + FavorPerformanceHint(!newDynLower, 0); mDynamicLowerValue = newDynLower; } } diff --git a/dom/base/nsContentSink.h b/dom/base/nsContentSink.h index 398a1f23321..4339b6eeb90 100644 --- a/dom/base/nsContentSink.h +++ b/dom/base/nsContentSink.h @@ -239,6 +239,8 @@ public: static void NotifyDocElementCreated(nsIDocument* aDoc); protected: + void + FavorPerformanceHint(bool perfOverStarvation, uint32_t starvationDelay); inline int32_t GetNotificationInterval() { diff --git a/dom/tests/mochitest/general/test_vibrator.html b/dom/tests/mochitest/general/test_vibrator.html index 765fdf07029..b9ee7d9543c 100644 --- a/dom/tests/mochitest/general/test_vibrator.html +++ b/dom/tests/mochitest/general/test_vibrator.html @@ -65,7 +65,7 @@ function tests(aEnabled) { expectSuccess([1000, 1000.1]); // The following loop shouldn't cause us to crash. See bug 701716. - for (var i = 0; i < 1000; i++) { + for (var i = 0; i < 10000; i++) { navigator.vibrate([100, 100]); } ok(true, "Didn't crash after issuing a lot of vibrate() calls."); diff --git a/dom/tests/mochitest/pointerlock/file_screenClientXYConst.html b/dom/tests/mochitest/pointerlock/file_screenClientXYConst.html index 9378855e76b..97f49e085f7 100644 --- a/dom/tests/mochitest/pointerlock/file_screenClientXYConst.html +++ b/dom/tests/mochitest/pointerlock/file_screenClientXYConst.html @@ -51,9 +51,9 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=633602 var firstCall = !unLockedCoords; if (!firstCall) { todo(false, "mousemove is fired twice."); - } else { - isUnlocked = !document.mozPointerLockElement; } + + isUnlocked = !document.mozPointerLockElement; unLockedCoords = { screenX: e.screenX, screenY: e.screenY, diff --git a/layout/base/nsRefreshDriver.cpp b/layout/base/nsRefreshDriver.cpp index 2366e6bcbfb..a43b4d7e4e4 100644 --- a/layout/base/nsRefreshDriver.cpp +++ b/layout/base/nsRefreshDriver.cpp @@ -1722,7 +1722,7 @@ nsRefreshDriver::Tick(int64_t aNowEpoch, TimeStamp aNowTime) mViewManagerFlushIsPending = false; nsRefPtr vm = mPresContext->GetPresShell()->GetViewManager(); - vm->ProcessPendingUpdates(nsViewManager::eTrySyncUpdate); + vm->ProcessPendingUpdates(); #ifdef MOZ_DUMP_PAINTING if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) { printf_stderr("Ending ProcessPendingUpdates\n"); diff --git a/view/nsViewManager.cpp b/view/nsViewManager.cpp index 7cd703965b1..fce226fe627 100644 --- a/view/nsViewManager.cpp +++ b/view/nsViewManager.cpp @@ -677,7 +677,7 @@ void nsViewManager::WillPaintWindow(nsIWidget* aWidget) LayerManager *manager = aWidget->GetLayerManager(); if (view && (view->ForcedRepaint() || !manager->NeedsWidgetInvalidation())) { - ProcessPendingUpdates(eNoSyncUpdate); + ProcessPendingUpdates(); // Re-get the view pointer here since the ProcessPendingUpdates might have // destroyed it during CallWillPaintOnObservers. view = nsView::GetViewFor(aWidget); @@ -1060,10 +1060,10 @@ nsViewManager::IsPainting(bool& aIsPainting) } void -nsViewManager::ProcessPendingUpdates(UpdatingMode aMode) +nsViewManager::ProcessPendingUpdates() { if (!IsRootVM()) { - RootViewManager()->ProcessPendingUpdates(aMode); + RootViewManager()->ProcessPendingUpdates(); return; } @@ -1075,14 +1075,6 @@ nsViewManager::ProcessPendingUpdates(UpdatingMode aMode) ProcessPendingUpdatesForView(mRootView, true); } - - if (aMode == eTrySyncUpdate) { - nsCOMPtr w; - GetRootWidget(getter_AddRefs(w)); - if (w) { - w->Update(); - } - } } void diff --git a/view/nsViewManager.h b/view/nsViewManager.h index 309838fbd32..585529dcdb1 100644 --- a/view/nsViewManager.h +++ b/view/nsViewManager.h @@ -303,16 +303,11 @@ public: */ static nsView* GetDisplayRootFor(nsView* aView); - enum UpdatingMode { - eNoSyncUpdate, - eTrySyncUpdate - }; - /** * Flush the accumulated dirty region to the widget and update widget * geometry. */ - void ProcessPendingUpdates(UpdatingMode aMode); + void ProcessPendingUpdates(); /** * Just update widget geometry without flushing the dirty region diff --git a/widget/gtk/nsWindow.cpp b/widget/gtk/nsWindow.cpp index 7c2a8d884b4..0e19dee579c 100644 --- a/widget/gtk/nsWindow.cpp +++ b/widget/gtk/nsWindow.cpp @@ -1697,14 +1697,6 @@ nsWindow::Invalidate(const nsIntRect &aRect) return NS_OK; } -void -nsWindow::Update() -{ - if (!ShouldUseOffMainThreadCompositing() && mGdkWindow) { - gdk_window_process_updates(mGdkWindow, true); - } -} - void* nsWindow::GetNativeData(uint32_t aDataType) { diff --git a/widget/gtk/nsWindow.h b/widget/gtk/nsWindow.h index 064d3292fd0..c34c70885c1 100644 --- a/widget/gtk/nsWindow.h +++ b/widget/gtk/nsWindow.h @@ -133,7 +133,6 @@ public: NS_IMETHOD SetCursor(imgIContainer* aCursor, uint32_t aHotspotX, uint32_t aHotspotY) override; NS_IMETHOD Invalidate(const nsIntRect &aRect) override; - virtual void Update() override; virtual void* GetNativeData(uint32_t aDataType) override; void SetNativeData(uint32_t aDataType, uintptr_t aVal) override; NS_IMETHOD SetTitle(const nsAString& aTitle) override; diff --git a/widget/nsBaseAppShell.cpp b/widget/nsBaseAppShell.cpp index 0146c29e6de..aa6468554b9 100644 --- a/widget/nsBaseAppShell.cpp +++ b/widget/nsBaseAppShell.cpp @@ -25,7 +25,11 @@ nsBaseAppShell::nsBaseAppShell() : mSuspendNativeCount(0) , mEventloopNestingLevel(0) , mBlockedWait(nullptr) + , mFavorPerf(0) , mNativeEventPending(false) + , mStarvationDelay(0) + , mSwitchTime(0) + , mLastNativeEventTime(0) , mEventloopNestingState(eEventloopNone) , mRunning(false) , mExiting(false) @@ -175,6 +179,20 @@ nsBaseAppShell::Exit(void) return NS_OK; } +NS_IMETHODIMP +nsBaseAppShell::FavorPerformanceHint(bool favorPerfOverStarvation, + uint32_t starvationDelay) +{ + mStarvationDelay = PR_MillisecondsToInterval(starvationDelay); + if (favorPerfOverStarvation) { + ++mFavorPerf; + } else { + --mFavorPerf; + mSwitchTime = PR_IntervalNow(); + } + return NS_OK; +} + NS_IMETHODIMP nsBaseAppShell::SuspendNative() { @@ -235,6 +253,9 @@ nsBaseAppShell::OnProcessNextEvent(nsIThreadInternal *thr, bool mayWait, OnDispatchedEvent(thr); // in case we blocked it earlier } + PRIntervalTime start = PR_IntervalNow(); + PRIntervalTime limit = THREAD_EVENT_STARVATION_LIMIT; + // Unblock outer nested wait loop (below). if (mBlockedWait) *mBlockedWait = false; @@ -250,7 +271,21 @@ nsBaseAppShell::OnProcessNextEvent(nsIThreadInternal *thr, bool mayWait, // NativeEventCallback to process gecko events. mProcessedGeckoEvents = false; - DoProcessNextNativeEvent(false, recursionDepth); + if (mFavorPerf <= 0 && start > mSwitchTime + mStarvationDelay) { + // Favor pending native events + PRIntervalTime now = start; + bool keepGoing; + do { + mLastNativeEventTime = now; + keepGoing = DoProcessNextNativeEvent(false, recursionDepth); + } while (keepGoing && ((now = PR_IntervalNow()) - start) < limit); + } else { + // Avoid starving native events completely when in performance mode + if (start - mLastNativeEventTime > limit) { + mLastNativeEventTime = start; + DoProcessNextNativeEvent(false, recursionDepth); + } + } while (!NS_HasPendingEvents(thr) && !mProcessedGeckoEvents) { // If we have been asked to exit from Run, then we should not wait for @@ -259,6 +294,7 @@ nsBaseAppShell::OnProcessNextEvent(nsIThreadInternal *thr, bool mayWait, if (mExiting) mayWait = false; + mLastNativeEventTime = PR_IntervalNow(); if (!DoProcessNextNativeEvent(mayWait, recursionDepth) || !mayWait) break; } diff --git a/widget/nsBaseAppShell.h b/widget/nsBaseAppShell.h index fc526162dbd..b2b9479ecbf 100644 --- a/widget/nsBaseAppShell.h +++ b/widget/nsBaseAppShell.h @@ -122,7 +122,11 @@ private: * have been consumed by the inner event loop(s). */ bool *mBlockedWait; + int32_t mFavorPerf; mozilla::Atomic mNativeEventPending; + PRIntervalTime mStarvationDelay; + PRIntervalTime mSwitchTime; + PRIntervalTime mLastNativeEventTime; enum EventloopNestingState { eEventloopNone, // top level thread execution eEventloopXPCOM, // innermost native event loop is ProcessNextNativeEvent diff --git a/widget/nsIAppShell.idl b/widget/nsIAppShell.idl index b98f429266c..3c0af793c63 100644 --- a/widget/nsIAppShell.idl +++ b/widget/nsIAppShell.idl @@ -12,7 +12,7 @@ interface nsIRunnable; * Interface for the native event system layer. This interface is designed * to be used on the main application thread only. */ -[uuid(78dc2b13-5de6-42f3-af57-2535f7fdb93e)] +[uuid(2d10ca53-f143-439a-bb2e-c1fbc71f6a05)] interface nsIAppShell : nsISupports { /** @@ -25,6 +25,24 @@ interface nsIAppShell : nsISupports */ void exit(); + /** + * Give hint to native event queue notification mechanism. If the native + * platform needs to tradeoff performance vs. native event starvation this + * hint tells the native dispatch code which to favor. The default is to + * prevent native event starvation. + * + * Calls to this function may be nested. When the number of calls that pass + * PR_TRUE is subtracted from the number of calls that pass PR_FALSE is + * greater than 0, performance is given precedence over preventing event + * starvation. + * + * The starvationDelay arg is only used when favorPerfOverStarvation is + * PR_FALSE. It is the amount of time in milliseconds to wait before the + * PR_FALSE actually takes effect. + */ + void favorPerformanceHint(in boolean favorPerfOverStarvation, + in unsigned long starvationDelay); + /** * Suspends the use of additional platform-specific methods (besides the * nsIAppShell->run() event loop) to run Gecko events on the main diff --git a/widget/nsIWidget.h b/widget/nsIWidget.h index 61a80adb464..14a5c2dd127 100644 --- a/widget/nsIWidget.h +++ b/widget/nsIWidget.h @@ -114,8 +114,8 @@ typedef void* nsNativeWidget; #define NS_NATIVE_PLUGIN_ID 105 #define NS_IWIDGET_IID \ -{ 0x7a4ece50, 0x5c52, 0x47c2, \ - { 0x8c, 0x9e, 0x32, 0xd2, 0x5a, 0x27, 0x53, 0x34 } } +{ 0x316E4600, 0x15DB, 0x47AE, \ + { 0xBF, 0xE4, 0x5B, 0xCD, 0xFF, 0x80, 0x80, 0x83 } }; /* * Window shadow styles @@ -1503,11 +1503,6 @@ class nsIWidget : public nsISupports { */ NS_IMETHOD Invalidate(const nsIntRect & aRect) = 0; - /** - * Widget implementation may support synchronous painting. - */ - virtual void Update() { } - enum LayerManagerPersistence { LAYER_MANAGER_CURRENT = 0, diff --git a/widget/windows/nsWindow.cpp b/widget/windows/nsWindow.cpp index 2461958ce04..672dbc8bf0d 100644 --- a/widget/windows/nsWindow.cpp +++ b/widget/windows/nsWindow.cpp @@ -2826,14 +2826,6 @@ NS_METHOD nsWindow::Invalidate(const nsIntRect & aRect) return NS_OK; } -void -nsWindow::Update() -{ - if (!ShouldUseOffMainThreadCompositing() && mWnd) { - ::UpdateWindow(mWnd); - } -} - NS_IMETHODIMP nsWindow::MakeFullScreen(bool aFullScreen, nsIScreen* aTargetScreen) { diff --git a/widget/windows/nsWindow.h b/widget/windows/nsWindow.h index 5e1bbbfe124..c3e70d94100 100644 --- a/widget/windows/nsWindow.h +++ b/widget/windows/nsWindow.h @@ -134,7 +134,6 @@ public: bool aUpdateNCArea = false, bool aIncludeChildren = false); NS_IMETHOD Invalidate(const nsIntRect & aRect); - virtual void Update() override; virtual void* GetNativeData(uint32_t aDataType); virtual void FreeNativeData(void * data, uint32_t aDataType); NS_IMETHOD SetTitle(const nsAString& aTitle); diff --git a/widget/windows/winrt/MetroWidget.cpp b/widget/windows/winrt/MetroWidget.cpp index abdffe0b0d6..848012738e2 100644 --- a/widget/windows/winrt/MetroWidget.cpp +++ b/widget/windows/winrt/MetroWidget.cpp @@ -1182,14 +1182,6 @@ MetroWidget::Invalidate(bool aEraseBackground, return NS_OK; } -void -MetroWidget::Update() -{ - if (!ShouldUseOffMainThreadCompositing() && mWnd) { - ::UpdateWindow(mWnd); - } -} - NS_IMETHODIMP MetroWidget::Invalidate(const nsIntRect & aRect) { diff --git a/widget/windows/winrt/MetroWidget.h b/widget/windows/winrt/MetroWidget.h index a2282a3dd30..f1695a78220 100644 --- a/widget/windows/winrt/MetroWidget.h +++ b/widget/windows/winrt/MetroWidget.h @@ -110,7 +110,6 @@ public: bool aUpdateNCArea = false, bool aIncludeChildren = false); NS_IMETHOD Invalidate(const nsIntRect & aRect); - virtual void Update() override; NS_IMETHOD DispatchEvent(mozilla::WidgetGUIEvent* aEvent, nsEventStatus& aStatus); NS_IMETHOD ConstrainPosition(bool aAllowSlop, int32_t *aX, int32_t *aY);