Backout Bug 930793 - Remove favor performance mode (we have still racy b2g tests), a=backout
authorOlli Pettay <Olli.Pettay@helsinki.fi>
Thu, 30 Jan 2014 21:35:44 -0800
changeset 176391 89f9304ff4baf44fa4893998dae116e6746d8783
parent 176390 1bb4238bdde3285e412492b8687da1e7ee5a36e7
child 176392 2a7a697f2d88da4b1290c555c09f5259031c99e0
push id5166
push userlsblakk@mozilla.com
push dateTue, 04 Feb 2014 01:47:54 +0000
treeherdermozilla-aurora@977eb2548b2d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbackout
bugs930793
milestone29.0a1
Backout Bug 930793 - Remove favor performance mode (we have still racy b2g tests), a=backout
content/base/src/nsContentSink.cpp
content/base/src/nsContentSink.h
docshell/base/nsDocShell.cpp
dom/tests/mochitest/general/test_vibrator.html
dom/tests/mochitest/pointerlock/file_screenClientXYConst.html
layout/base/nsRefreshDriver.cpp
view/public/nsViewManager.h
view/src/nsViewManager.cpp
widget/cocoa/nsChildView.h
widget/cocoa/nsChildView.mm
widget/gtk/nsWindow.cpp
widget/gtk/nsWindow.h
widget/nsIAppShell.idl
widget/nsIWidget.h
widget/windows/nsWindow.cpp
widget/windows/nsWindow.h
widget/windows/winrt/MetroWidget.cpp
widget/windows/winrt/MetroWidget.h
widget/xpwidgets/nsBaseAppShell.cpp
widget/xpwidgets/nsBaseAppShell.h
--- a/content/base/src/nsContentSink.cpp
+++ b/content/base/src/nsContentSink.cpp
@@ -195,16 +195,17 @@ nsContentSink::Init(nsIDocument* aDoc,
   mCSSLoader = aDoc->CSSLoader();
 
   mNodeInfoManager = aDoc->NodeInfoManager();
 
   mBackoffCount = sBackoffCount;
 
   if (sEnablePerfMode != 0) {
     mDynamicLowerValue = sEnablePerfMode == 1;
+    FavorPerformanceHint(!mDynamicLowerValue, 0);
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsContentSink::StyleSheetLoaded(nsCSSStyleSheet* aSheet,
                                 bool aWasAlternate,
@@ -1371,16 +1372,25 @@ nsContentSink::DidProcessATokenImpl()
   }
 
   return NS_OK;
 }
 
 //----------------------------------------------------------------------
 
 void
+nsContentSink::FavorPerformanceHint(bool perfOverStarvation, uint32_t starvationDelay)
+{
+  static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
+  nsCOMPtr<nsIAppShell> appShell = do_GetService(kAppShellCID);
+  if (appShell)
+    appShell->FavorPerformanceHint(perfOverStarvation, starvationDelay);
+}
+
+void
 nsContentSink::BeginUpdate(nsIDocument *aDocument, nsUpdateType aUpdateType)
 {
   // Remember nested updates from updates that we started.
   if (mInNotification > 0 && mUpdatesInNotification < 2) {
     ++mUpdatesInNotification;
   }
 
   // If we're in a script and we didn't do the notification,
@@ -1447,16 +1457,22 @@ nsContentSink::DropParserAndPerfHint(voi
   // Do this hack to make sure that the parser
   // doesn't get destroyed, accidently, before
   // the circularity, between sink & parser, is
   // actually broken.
   // Drop our reference to the parser to get rid of a circular
   // reference.
   nsRefPtr<nsParserBase> 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);
   }
 }
 
 bool
 nsContentSink::IsScriptExecutingImpl()
 {
@@ -1484,16 +1500,17 @@ nsContentSink::WillParseImpl(void)
     vm->GetLastUserEventTime(lastEventTime);
 
     bool newDynLower =
       mDocument->IsInBackgroundWindow() ||
       ((currentTime - mBeginLoadTime) > uint32_t(sInitialPerfTime) &&
        (currentTime - lastEventTime) < uint32_t(sInteractiveTime));
     
     if (mDynamicLowerValue != newDynLower) {
+      FavorPerformanceHint(!newDynLower, 0);
       mDynamicLowerValue = newDynLower;
     }
   }
   
   mDeflectedCount = 0;
   mHasPendingEvent = false;
 
   mCurrentParseEndTime = currentTime +
--- a/content/base/src/nsContentSink.h
+++ b/content/base/src/nsContentSink.h
@@ -232,16 +232,19 @@ protected:
   // we still have stylesheet loads pending.  Otherwise, we'll wait until the
   // stylesheets are all done loading.
 public:
   void StartLayout(bool aIgnorePendingSheets);
 
   static void NotifyDocElementCreated(nsIDocument* aDoc);
 
 protected:
+  void
+  FavorPerformanceHint(bool perfOverStarvation, uint32_t starvationDelay);
+
   inline int32_t GetNotificationInterval()
   {
     if (mDynamicLowerValue) {
       return 1000;
     }
 
     return sNotificationInterval;
   }
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -206,16 +206,19 @@ static NS_DEFINE_CID(kAppShellCID, NS_AP
 using namespace mozilla;
 using namespace mozilla::dom;
 
 // True means sUseErrorPages has been added to preferences var cache.
 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;
 
 // Global count of docshells with the private attribute set
 static uint32_t gNumberOfPrivateDocShells = 0;
 
 // Global reference to the URI fixup service.
 nsIURIFixup *nsDocShell::sURIFixup = 0;
@@ -236,16 +239,27 @@ static uint32_t gValidateOrigin = 0xffff
 static PRLogModuleInfo* gDocShellLog;
 #endif
 static PRLogModuleInfo* gDocShellLeakLog;
 #endif
 
 const char kBrandBundleURL[]      = "chrome://branding/locale/brand.properties";
 const char kAppstringsBundleURL[] = "chrome://global/locale/appstrings.properties";
 
+static void
+FavorPerformanceHint(bool perfOverStarvation)
+{
+    nsCOMPtr<nsIAppShell> appShell = do_GetService(kAppShellCID);
+    if (appShell) {
+        appShell->FavorPerformanceHint(perfOverStarvation,
+                                       Preferences::GetUint("docshell.event_starvation_delay_hint",
+                                                            NS_EVENT_STARVATION_DELAY_HINT));
+    }
+}
+
 //*****************************************************************************
 // <a ping> support
 //*****************************************************************************
 
 #define PREF_PINGS_ENABLED           "browser.send_pings"
 #define PREF_PINGS_MAX_PER_LINK      "browser.send_pings.max_per_link"
 #define PREF_PINGS_REQUIRE_SAME_HOST "browser.send_pings.require_same_host"
 
@@ -6847,16 +6861,24 @@ nsDocShell::EndPageLoad(nsIWebProgress *
     // Notify the ContentViewer that the Document has finished loading.  This
     // will cause any OnLoad(...) and PopState(...) handlers to fire.
     if (!mEODForCurrentDocument && mContentViewer) {
         mIsExecutingOnLoadHandler = true;
         mContentViewer->LoadComplete(aStatus);
         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 
      * when a user goes back/forward to this page, we appropriately do 
      * form value restoration or load from server.
      */
     nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
     if (!httpChannel) // HttpChannel could be hiding underneath a Multipart channel.    
@@ -7850,16 +7872,22 @@ nsDocShell::RestoreFromHistory()
     // but we don't want them to stay around in case the page is reloaded.
     SetLayoutHistoryState(nullptr);
 
     // This is the end of our Embed() replacement
 
     mSavingOldViewer = false;
     mEODForCurrentDocument = false;
 
+    // Tell the event loop to favor plevents over user events, see comments
+    // in CreateContentViewer.
+    if (++gNumberOfDocumentsLoading == 1)
+        FavorPerformanceHint(true);
+
+
     if (oldMUDV && newMUDV) {
         newMUDV->SetMinFontSize(minFontSize);
         newMUDV->SetTextZoom(textZoom);
         newMUDV->SetFullZoom(pageZoom);
         newMUDV->SetAuthorStyleDisabled(styleDisabled);
     }
 
     nsCOMPtr<nsIDocument> document = do_QueryInterface(domDoc);
@@ -8246,16 +8274,26 @@ nsDocShell::CreateContentViewer(const ch
         if (doc) {
           uint32_t partID;
           multiPartChannel->GetPartID(&partID);
           doc->SetPartID(partID);
         }
       }
     }
 
+    // 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, request, mCurrentURI, 0);
     }
   
     return NS_OK;
 }
 
 nsresult
--- a/dom/tests/mochitest/general/test_vibrator.html
+++ b/dom/tests/mochitest/general/test_vibrator.html
@@ -52,17 +52,17 @@ function testSuccesses() {
   expectSuccess(1000);
   expectSuccess(1000.1);
   expectSuccess([0, 0, 0]);
   expectSuccess(['1000', 1000]);
   expectSuccess([1000, 1000]);
   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.");
 }
 
 var origVibratorEnabled = SpecialPowers.getBoolPref('dom.vibrator.enabled');
 
 // Test with the vibrator pref enabled.
--- a/dom/tests/mochitest/pointerlock/file_screenClientXYConst.html
+++ b/dom/tests/mochitest/pointerlock/file_screenClientXYConst.html
@@ -46,19 +46,19 @@ https://bugzilla.mozilla.org/show_bug.cg
         is(unLockedCoords.screenY, lockedCoords.screenY,
            "screenY should be equal to where the mouse was originaly locked");
       }
 
       function moveUnlocked(e) {
         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,
           clientX: e.clientX,
           clientY: e.clientY
         };
 
         if (!firstCall) {
--- a/layout/base/nsRefreshDriver.cpp
+++ b/layout/base/nsRefreshDriver.cpp
@@ -1199,17 +1199,17 @@ nsRefreshDriver::Tick(int64_t aNowEpoch,
 #ifdef MOZ_DUMP_PAINTING
     if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
       printf_stderr("Starting ProcessPendingUpdates\n");
     }
 #endif
 
     mViewManagerFlushIsPending = false;
     nsRefPtr<nsViewManager> vm = mPresContext->GetPresShell()->GetViewManager();
-    vm->ProcessPendingUpdates(nsViewManager::eTrySyncUpdate);
+    vm->ProcessPendingUpdates();
 #ifdef MOZ_DUMP_PAINTING
     if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
       printf_stderr("Ending ProcessPendingUpdates\n");
     }
 #endif
   }
 
   for (uint32_t i = 0; i < mPostRefreshObservers.Length(); ++i) {
--- a/view/public/nsViewManager.h
+++ b/view/public/nsViewManager.h
@@ -297,26 +297,21 @@ public:
   void GetLastUserEventTime(uint32_t& aTime);
 
   /**
    * Find the nearest display root view for the view aView. This is the view for
    * the nearest enclosing popup or the root view for the root document.
    */
   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
    */
   void UpdateWidgetGeometry();
 
   int32_t AppUnitsPerDevPixel() const
   {
--- a/view/src/nsViewManager.cpp
+++ b/view/src/nsViewManager.cpp
@@ -649,17 +649,17 @@ void nsViewManager::InvalidateViews(nsVi
 
 void nsViewManager::WillPaintWindow(nsIWidget* aWidget)
 {
   if (aWidget) {
     nsView* view = nsView::GetViewFor(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);
       if (view) {
         view->SetForcedRepaint(false);
       }
     }
   }
@@ -1032,38 +1032,30 @@ nsIntRect nsViewManager::ViewToWidget(ns
 
 void
 nsViewManager::IsPainting(bool& aIsPainting)
 {
   aIsPainting = IsPainting();
 }
 
 void
-nsViewManager::ProcessPendingUpdates(UpdatingMode aMode)
+nsViewManager::ProcessPendingUpdates()
 {
   if (!IsRootVM()) {
-    RootViewManager()->ProcessPendingUpdates(aMode);
+    RootViewManager()->ProcessPendingUpdates();
     return;
   }
 
   mPresShell->GetPresContext()->RefreshDriver()->RevokeViewManagerFlush();
 
   // Flush things like reflows by calling WillPaint on observer presShells.
   if (mPresShell) {
     CallWillPaintOnObservers();
   }
   ProcessPendingUpdatesForView(mRootView, true);
-
-  if (aMode == eTrySyncUpdate) {
-    nsCOMPtr<nsIWidget> w;
-    GetRootWidget(getter_AddRefs(w));
-    if (w) {
-      w->Update();
-    }
-  }
 }
 
 void
 nsViewManager::UpdateWidgetGeometry()
 {
   if (!IsRootVM()) {
     RootViewManager()->UpdateWidgetGeometry();
     return;
--- a/widget/cocoa/nsChildView.h
+++ b/widget/cocoa/nsChildView.h
@@ -485,17 +485,16 @@ public:
   // between HiDPI and non-HiDPI screens
   void                    BackingScaleFactorChanged();
 
   virtual double          GetDefaultScaleInternal();
 
   virtual int32_t         RoundsWidgetCoordinatesTo() MOZ_OVERRIDE;
 
   NS_IMETHOD              Invalidate(const nsIntRect &aRect);
-  virtual void            Update() MOZ_OVERRIDE;
 
   virtual void*           GetNativeData(uint32_t aDataType);
   virtual nsresult        ConfigureChildren(const nsTArray<Configuration>& aConfigurations);
   virtual nsIntPoint      WidgetToScreenOffset();
   virtual bool            ShowsResizeIndicator(nsIntRect* aResizerRect);
 
   static  bool            ConvertStatus(nsEventStatus aStatus)
                           { return aStatus == nsEventStatus_eConsumeNoDefault; }
--- a/widget/cocoa/nsChildView.mm
+++ b/widget/cocoa/nsChildView.mm
@@ -1576,28 +1576,16 @@ NS_IMETHODIMP nsChildView::Invalidate(co
     [mView setNeedsDisplayInRect:DevPixelsToCocoaPoints(aRect)];
   }
 
   return NS_OK;
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
 }
 
-void
-nsChildView::Update()
-{
-  NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
-
-  if (!ShouldUseOffMainThreadCompositing() && mView) {
-    [mView displayIfNeeded];
-  }
-
-  NS_OBJC_END_TRY_ABORT_BLOCK;
-}
-
 bool
 nsChildView::ComputeShouldAccelerate(bool aDefault)
 {
   // Don't use OpenGL for transparent windows or for popup windows.
   if (!mView || ![[mView window] isOpaque] ||
       [[mView window] isKindOfClass:[PopupWindow class]])
     return false;
 
--- a/widget/gtk/nsWindow.cpp
+++ b/widget/gtk/nsWindow.cpp
@@ -1612,24 +1612,16 @@ nsWindow::Invalidate(const nsIntRect &aR
     LOGDRAW(("Invalidate (rect) [%p]: %d %d %d %d\n", (void *)this,
              rect.x, rect.y, rect.width, rect.height));
 
     gdk_window_invalidate_rect(mGdkWindow, &rect, FALSE);
 
     return NS_OK;
 }
 
-void
-nsWindow::Update()
-{
-    if (!ShouldUseOffMainThreadCompositing() && mGdkWindow) {
-        gdk_window_process_updates(mGdkWindow, true);
-    }
-}
-
 void*
 nsWindow::GetNativeData(uint32_t aDataType)
 {
     switch (aDataType) {
     case NS_NATIVE_WINDOW:
     case NS_NATIVE_WIDGET: {
         if (!mGdkWindow)
             return nullptr;
--- a/widget/gtk/nsWindow.h
+++ b/widget/gtk/nsWindow.h
@@ -133,17 +133,16 @@ public:
     NS_IMETHOD         GetClientBounds(nsIntRect &aRect);
     virtual nsIntPoint GetClientOffset();
     NS_IMETHOD         SetForegroundColor(const nscolor &aColor);
     NS_IMETHOD         SetBackgroundColor(const nscolor &aColor);
     NS_IMETHOD         SetCursor(nsCursor aCursor);
     NS_IMETHOD         SetCursor(imgIContainer* aCursor,
                                  uint32_t aHotspotX, uint32_t aHotspotY);
     NS_IMETHOD         Invalidate(const nsIntRect &aRect);
-    virtual void       Update() MOZ_OVERRIDE;
     virtual void*      GetNativeData(uint32_t aDataType);
     NS_IMETHOD         SetTitle(const nsAString& aTitle);
     NS_IMETHOD         SetIcon(const nsAString& aIconSpec);
     NS_IMETHOD         SetWindowClass(const nsAString& xulWinType);
     virtual nsIntPoint WidgetToScreenOffset();
     NS_IMETHOD         EnableDragDrop(bool aEnable);
     NS_IMETHOD         CaptureMouse(bool aCapture);
     NS_IMETHOD         CaptureRollupEvents(nsIRollupListener *aListener,
--- a/widget/nsIAppShell.idl
+++ b/widget/nsIAppShell.idl
@@ -21,16 +21,34 @@ interface nsIAppShell : nsISupports
   void run();
 
   /**
    * Exit the handle event loop
    */
   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
    * application thread.  Under some circumstances these "additional methods"
    * can cause Gecko event handlers to be re-entered, sometimes leading to
    * hangs and crashes.  Calls to suspendNative() and resumeNative() may be
    * nested.  On some platforms (those that don't use any "additional
    * methods") this will be a no-op.  Does not (in itself) stop Gecko events
    * from being processed on the main application thread.  But if the
--- a/widget/nsIWidget.h
+++ b/widget/nsIWidget.h
@@ -95,18 +95,18 @@ typedef void* nsNativeWidget;
 #ifdef XP_WIN
 #define NS_NATIVE_TSF_THREAD_MGR       100
 #define NS_NATIVE_TSF_CATEGORY_MGR     101
 #define NS_NATIVE_TSF_DISPLAY_ATTR_MGR 102
 #define NS_NATIVE_ICOREWINDOW          103 // winrt specific
 #endif
 
 #define NS_IWIDGET_IID \
-{ 0x0a157edd, 0xd70b, 0x4242, \
-  { 0xad, 0xd5, 0xcb, 0xce, 0x4c, 0xf3, 0x4b, 0x47 } }
+{ 0x67da44c4, 0xe21b, 0x4742, \
+  { 0x9c, 0x2b, 0x26, 0xc7, 0x70, 0x21, 0xde, 0x87 } }
 
 /*
  * Window shadow styles
  * Also used for the -moz-window-shadow CSS property
  */
 
 #define NS_STYLE_WINDOW_SHADOW_NONE             0
 #define NS_STYLE_WINDOW_SHADOW_DEFAULT          1
@@ -1192,21 +1192,16 @@ class nsIWidget : public nsISupports {
     NS_IMETHOD MakeFullScreen(bool aFullScreen) = 0;
 
     /**
      * Invalidate a specified rect for a widget so that it will be repainted
      * later.
      */
     NS_IMETHOD Invalidate(const nsIntRect & aRect) = 0;
 
-    /**
-     * Widget implementation may support synchronous painting.
-     */
-   virtual void Update() { }
-
     enum LayerManagerPersistence
     {
       LAYER_MANAGER_CURRENT = 0,
       LAYER_MANAGER_PERSISTENT
     };
 
     /**
      * Return the widget's LayerManager. The layer tree for that
--- a/widget/windows/nsWindow.cpp
+++ b/widget/windows/nsWindow.cpp
@@ -2770,24 +2770,16 @@ NS_METHOD nsWindow::Invalidate(const nsI
     rect.right  = aRect.x + aRect.width;
     rect.bottom = aRect.y + aRect.height;
 
     VERIFY(::InvalidateRect(mWnd, &rect, FALSE));
   }
   return NS_OK;
 }
 
-void
-nsWindow::Update()
-{
-    if (!ShouldUseOffMainThreadCompositing() && mWnd) {
-        ::UpdateWindow(mWnd);
-    }
-}
-
 NS_IMETHODIMP
 nsWindow::MakeFullScreen(bool aFullScreen)
 {
   // taskbarInfo will be nullptr pre Windows 7 until Bug 680227 is resolved.
   nsCOMPtr<nsIWinTaskbar> taskbarInfo =
     do_GetService(NS_TASKBAR_CONTRACTID);
 
   mFullscreenMode = aFullScreen;
--- a/widget/windows/nsWindow.h
+++ b/widget/windows/nsWindow.h
@@ -132,17 +132,16 @@ public:
   NS_IMETHOD              SetCursor(nsCursor aCursor);
   virtual nsresult        ConfigureChildren(const nsTArray<Configuration>& aConfigurations);
   NS_IMETHOD              MakeFullScreen(bool aFullScreen);
   NS_IMETHOD              HideWindowChrome(bool aShouldHide);
   NS_IMETHOD              Invalidate(bool aEraseBackground = false,
                                      bool aUpdateNCArea = false,
                                      bool aIncludeChildren = false);
   NS_IMETHOD              Invalidate(const nsIntRect & aRect);
-  virtual void            Update() MOZ_OVERRIDE;
   virtual void*           GetNativeData(uint32_t aDataType);
   virtual void            FreeNativeData(void * data, uint32_t aDataType);
   NS_IMETHOD              SetTitle(const nsAString& aTitle);
   NS_IMETHOD              SetIcon(const nsAString& aIconSpec);
   virtual nsIntPoint      WidgetToScreenOffset();
   virtual nsIntSize       ClientToWindowSize(const nsIntSize& aClientSize);
   NS_IMETHOD              DispatchEvent(mozilla::WidgetGUIEvent* aEvent,
                                         nsEventStatus& aStatus);
--- a/widget/windows/winrt/MetroWidget.cpp
+++ b/widget/windows/winrt/MetroWidget.cpp
@@ -1263,24 +1263,16 @@ MetroWidget::Invalidate(const nsIntRect 
     rect.right  = aRect.x + aRect.width;
     rect.bottom = aRect.y + aRect.height;
     InvalidateRect(mWnd, &rect, FALSE);
   }
 
   return NS_OK;
 }
 
-void
-MetroWidget::Update()
-{
-    if (!ShouldUseOffMainThreadCompositing() && mWnd) {
-        ::UpdateWindow(mWnd);
-    }
-}
-
 nsTransparencyMode
 MetroWidget::GetTransparencyMode()
 {
   return mTransparencyMode;
 }
 
 void
 MetroWidget::SetTransparencyMode(nsTransparencyMode aMode)
--- a/widget/windows/winrt/MetroWidget.h
+++ b/widget/windows/winrt/MetroWidget.h
@@ -104,17 +104,16 @@ public:
   NS_IMETHOD    IsEnabled(bool *aState);
   NS_IMETHOD    GetBounds(nsIntRect &aRect);
   NS_IMETHOD    GetScreenBounds(nsIntRect &aRect);
   NS_IMETHOD    GetClientBounds(nsIntRect &aRect);
   NS_IMETHOD    Invalidate(bool aEraseBackground = false,
                 bool aUpdateNCArea = false,
                 bool aIncludeChildren = false);
   NS_IMETHOD    Invalidate(const nsIntRect & aRect);
-  virtual void  Update() MOZ_OVERRIDE;
   NS_IMETHOD    DispatchEvent(mozilla::WidgetGUIEvent* aEvent,
                               nsEventStatus& aStatus);
   NS_IMETHOD    ConstrainPosition(bool aAllowSlop, int32_t *aX, int32_t *aY);
   NS_IMETHOD    Move(double aX, double aY);
   NS_IMETHOD    Resize(double aWidth, double aHeight, bool aRepaint);
   NS_IMETHOD    Resize(double aX, double aY, double aWidth, double aHeight, bool aRepaint);
   NS_IMETHOD    SetFocus(bool aRaise);
   NS_IMETHOD    Enable(bool aState);
--- a/widget/xpwidgets/nsBaseAppShell.cpp
+++ b/widget/xpwidgets/nsBaseAppShell.cpp
@@ -17,17 +17,21 @@
 #define THREAD_EVENT_STARVATION_LIMIT PR_MillisecondsToInterval(20)
 
 NS_IMPL_ISUPPORTS3(nsBaseAppShell, nsIAppShell, nsIThreadObserver, nsIObserver)
 
 nsBaseAppShell::nsBaseAppShell()
   : mSuspendNativeCount(0)
   , mEventloopNestingLevel(0)
   , mBlockedWait(nullptr)
+  , mFavorPerf(0)
   , mNativeEventPending(0)
+  , mStarvationDelay(0)
+  , mSwitchTime(0)
+  , mLastNativeEventTime(0)
   , mEventloopNestingState(eEventloopNone)
   , mRunning(false)
   , mExiting(false)
   , mBlockNativeEvent(false)
 {
 }
 
 nsBaseAppShell::~nsBaseAppShell()
@@ -168,16 +172,30 @@ nsBaseAppShell::Exit(void)
   if (mRunning && !mExiting) {
     MessageLoop::current()->Quit();
   }
   mExiting = true;
   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()
 {
   ++mSuspendNativeCount;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsBaseAppShell::ResumeNative()
@@ -227,40 +245,58 @@ nsBaseAppShell::OnProcessNextEvent(nsITh
     // back to it ASAP, but it seems a gecko event has caused us to
     // spin up a nested XPCOM event loop (eg. modal window), so we
     // really must start processing native events here again.
     mBlockNativeEvent = false;
     if (NS_HasPendingEvents(thr))
       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;
 
   bool *oldBlockedWait = mBlockedWait;
   mBlockedWait = &mayWait;
 
   // When mayWait is true, we need to make sure that there is an event in the
   // thread's event queue before we return.  Otherwise, the thread will block
   // on its event queue waiting for an event.
   bool needEvent = mayWait;
   // Reset prior to invoking DoProcessNextNativeEvent which might cause
   // 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
     // events to process.  Note that an inner nested event loop causes
     // 'mayWait' to become false too, through 'mBlockedWait'.
     if (mExiting)
       mayWait = false;
 
+    mLastNativeEventTime = PR_IntervalNow();
     if (!DoProcessNextNativeEvent(mayWait, recursionDepth) || !mayWait)
       break;
   }
 
   mBlockedWait = oldBlockedWait;
 
   // Make sure that the thread event queue does not block on its monitor, as
   // it normally would do if it did not have any pending events.  To avoid
--- a/widget/xpwidgets/nsBaseAppShell.h
+++ b/widget/xpwidgets/nsBaseAppShell.h
@@ -114,17 +114,21 @@ private:
   nsCOMPtr<nsIRunnable> mDummyEvent;
   /**
    * mBlockedWait points back to a slot that controls the wait loop in
    * an outer OnProcessNextEvent invocation.  Nested calls always set
    * it to false to unblock an outer loop, since all events may
    * have been consumed by the inner event loop(s).
    */
   bool *mBlockedWait;
+  int32_t mFavorPerf;
   mozilla::Atomic<uint32_t> mNativeEventPending;
+  PRIntervalTime mStarvationDelay;
+  PRIntervalTime mSwitchTime;
+  PRIntervalTime mLastNativeEventTime;
   enum EventloopNestingState {
     eEventloopNone,  // top level thread execution
     eEventloopXPCOM, // innermost native event loop is ProcessNextNativeEvent
     eEventloopOther  // innermost native event loop is a native library/plugin etc
   };
   EventloopNestingState mEventloopNestingState;
   nsTArray<SyncSection> mSyncSections;
   bool mRunning;