Add more accurate activity state tracking
authoroliver@apple.com <oliver@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 29 May 2013 23:17:59 +0000 (23:17 +0000)
committeroliver@apple.com <oliver@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 29 May 2013 23:17:59 +0000 (23:17 +0000)
https://bugs.webkit.org/show_bug.cgi?id=116893

Reviewed by Gavin Barraclough.

Source/WebCore:

Add a hysteresis to Page activity throttling.  We do this
through a PageThrottler class that now handles entering and
exiting throttling state, and adds a hysteresis to we can
limit the amount of on/off bouncing on the throttle.

Media elements now make use of this to disable timer throttling
while they are playing, and plugins use it to momentarily bounce
from throttled to unthrottled state.

* CMakeLists.txt:
* GNUmakefile.list.am:
* Target.pri:
* WebCore.exp.in:
* WebCore.vcproj/WebCore.vcproj:
* WebCore.vcxproj/WebCore.vcxproj:
* WebCore.vcxproj/WebCore.vcxproj.filters:
* WebCore.xcodeproj/project.pbxproj:
* html/HTMLMediaElement.cpp:
(WebCore::HTMLMediaElement::~HTMLMediaElement):
(WebCore::HTMLMediaElement::pageThrottlerIfPossible):
(WebCore):
(WebCore::HTMLMediaElement::playInternal):
(WebCore::HTMLMediaElement::pauseInternal):
* html/HTMLMediaElement.h:
(WebCore):
* html/MediaController.cpp:
(MediaController::unpause):
* page/ChromeClient.h:
(WebCore::ChromeClient::incrementActivePageCount):
(WebCore::ChromeClient::decrementActivePageCount):
(ChromeClient):
* page/Page.cpp:
(WebCore::Page::Page):
(WebCore::Page::~Page):
(WebCore::Page::setThrottled):
* page/Page.h:
(WebCore):
(Page):
(WebCore::Page::pageThrottler):
* page/PageThrottleState.h: Added.
* page/PageThrottler.cpp: Added.
(WebCore):
(WebCore::PageThrottler::PageThrottler):
(WebCore::PageThrottler::~PageThrottler):
(WebCore::PageThrottler::clearPage):
(WebCore::PageThrottler::throttlePage):
(WebCore::PageThrottler::unthrottlePage):
(WebCore::PageThrottler::setThrottled):
(WebCore::PageThrottler::preventThrottling):
(WebCore::PageThrottler::allowThrottling):
(WebCore::PageThrottler::stopThrottleHysteresisTimer):
(WebCore::PageThrottler::reportInterestingEvent):
(WebCore::PageThrottler::startThrottleHysteresisTimer):
(WebCore::PageThrottler::throttleHysteresisTimerFired):
* page/PageThrottler.h: Added.
(WebCore):
(PageThrottler):
(WebCore::PageThrottler::create):
(WebCore::PageThrottler::shouldThrottleAnimations):
(WebCore::PageThrottler::shouldThrottleTimers):

Source/WebKit2:

This extends the logic in WebCore from a Page to Process
granularity, so we will avoid lowering the child process
priority if there is active content.  This also plumbs in
the logic to allow plugins to report that they have done
something "interesting".  Currently this is somewhat conservative
but even this is sufficient to fix some stuttering issues
that we've seen.

* Shared/ChildProcess.cpp:
(WebKit::ChildProcess::ChildProcess):
* Shared/ChildProcess.h:
(ChildProcess):
(WebKit::ChildProcess::processSuppressionEnabled):
(WebKit::ChildProcess::incrementActiveTaskCount):
(WebKit::ChildProcess::decrementActiveTaskCount):
* Shared/mac/ChildProcessMac.mm:
(WebKit):
(WebKit::ChildProcess::setProcessSuppressionEnabledInternal):
(WebKit::ChildProcess::setProcessSuppressionEnabled):
(WebKit::ChildProcess::incrementActiveTaskCount):
(WebKit::ChildProcess::decrementActiveTaskCount):
(WebKit::ChildProcess::suspensionHysteresisTimerFired):
* WebProcess/Plugins/Netscape/NPRuntimeObjectMap.cpp:
(WebKit::NPRuntimeObjectMap::NPRuntimeObjectMap):
(WebKit::NPRuntimeObjectMap::evaluate):
* WebProcess/Plugins/Netscape/NPRuntimeObjectMap.h:
(WebCore):
(NPRuntimeObjectMap):
* WebProcess/Plugins/PluginView.cpp:
(WebKit::PluginView::PluginView):
* WebProcess/WebCoreSupport/WebChromeClient.cpp:
(WebKit::WebChromeClient::incrementActivePageCount):
(WebKit):
(WebKit::WebChromeClient::decrementActivePageCount):
* WebProcess/WebCoreSupport/WebChromeClient.h:
(WebChromeClient):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@150935 268f45cc-cd09-0410-ab3c-d52691b4dbfc

26 files changed:
Source/WebCore/CMakeLists.txt
Source/WebCore/ChangeLog
Source/WebCore/GNUmakefile.list.am
Source/WebCore/Target.pri
Source/WebCore/WebCore.exp.in
Source/WebCore/WebCore.vcproj/WebCore.vcproj
Source/WebCore/WebCore.vcxproj/WebCore.vcxproj
Source/WebCore/WebCore.vcxproj/WebCore.vcxproj.filters
Source/WebCore/WebCore.xcodeproj/project.pbxproj
Source/WebCore/html/HTMLMediaElement.cpp
Source/WebCore/html/HTMLMediaElement.h
Source/WebCore/html/MediaController.cpp
Source/WebCore/page/ChromeClient.h
Source/WebCore/page/Page.cpp
Source/WebCore/page/Page.h
Source/WebCore/page/PageThrottler.cpp [new file with mode: 0644]
Source/WebCore/page/PageThrottler.h [new file with mode: 0644]
Source/WebKit2/ChangeLog
Source/WebKit2/Shared/ChildProcess.cpp
Source/WebKit2/Shared/ChildProcess.h
Source/WebKit2/Shared/mac/ChildProcessMac.mm
Source/WebKit2/WebProcess/Plugins/Netscape/NPRuntimeObjectMap.cpp
Source/WebKit2/WebProcess/Plugins/Netscape/NPRuntimeObjectMap.h
Source/WebKit2/WebProcess/Plugins/PluginView.cpp
Source/WebKit2/WebProcess/WebCoreSupport/WebChromeClient.cpp
Source/WebKit2/WebProcess/WebCoreSupport/WebChromeClient.h

index e5192a2..f52ce74 100644 (file)
@@ -1757,6 +1757,7 @@ set(WebCore_SOURCES
     page/PageGroup.cpp
     page/PageGroupLoadDeferrer.cpp
     page/PageSerializer.cpp
+    page/PageThrottler.cpp
     page/PageVisibilityState.cpp
     page/Performance.cpp
     page/PerformanceEntry.cpp
index 9ace2ce..9be239c 100644 (file)
@@ -1,3 +1,71 @@
+2013-05-28  Oliver Hunt  <oliver@apple.com>
+
+        Add more accurate activity state tracking
+        https://bugs.webkit.org/show_bug.cgi?id=116893
+
+        Reviewed by Gavin Barraclough.
+
+        Add a hysteresis to Page activity throttling.  We do this
+        through a PageThrottler class that now handles entering and
+        exiting throttling state, and adds a hysteresis to we can
+        limit the amount of on/off bouncing on the throttle.
+
+        Media elements now make use of this to disable timer throttling
+        while they are playing, and plugins use it to momentarily bounce
+        from throttled to unthrottled state.
+
+        * CMakeLists.txt:
+        * GNUmakefile.list.am:
+        * Target.pri:
+        * WebCore.exp.in:
+        * WebCore.vcproj/WebCore.vcproj:
+        * WebCore.vcxproj/WebCore.vcxproj:
+        * WebCore.vcxproj/WebCore.vcxproj.filters:
+        * WebCore.xcodeproj/project.pbxproj:
+        * html/HTMLMediaElement.cpp:
+        (WebCore::HTMLMediaElement::~HTMLMediaElement):
+        (WebCore::HTMLMediaElement::pageThrottlerIfPossible):
+        (WebCore):
+        (WebCore::HTMLMediaElement::playInternal):
+        (WebCore::HTMLMediaElement::pauseInternal):
+        * html/HTMLMediaElement.h:
+        (WebCore):
+        * html/MediaController.cpp:
+        (MediaController::unpause):
+        * page/ChromeClient.h:
+        (WebCore::ChromeClient::incrementActivePageCount):
+        (WebCore::ChromeClient::decrementActivePageCount):
+        (ChromeClient):
+        * page/Page.cpp:
+        (WebCore::Page::Page):
+        (WebCore::Page::~Page):
+        (WebCore::Page::setThrottled):
+        * page/Page.h:
+        (WebCore):
+        (Page):
+        (WebCore::Page::pageThrottler):
+        * page/PageThrottleState.h: Added.
+        * page/PageThrottler.cpp: Added.
+        (WebCore):
+        (WebCore::PageThrottler::PageThrottler):
+        (WebCore::PageThrottler::~PageThrottler):
+        (WebCore::PageThrottler::clearPage):
+        (WebCore::PageThrottler::throttlePage):
+        (WebCore::PageThrottler::unthrottlePage):
+        (WebCore::PageThrottler::setThrottled):
+        (WebCore::PageThrottler::preventThrottling):
+        (WebCore::PageThrottler::allowThrottling):
+        (WebCore::PageThrottler::stopThrottleHysteresisTimer):
+        (WebCore::PageThrottler::reportInterestingEvent):
+        (WebCore::PageThrottler::startThrottleHysteresisTimer):
+        (WebCore::PageThrottler::throttleHysteresisTimerFired):
+        * page/PageThrottler.h: Added.
+        (WebCore):
+        (PageThrottler):
+        (WebCore::PageThrottler::create):
+        (WebCore::PageThrottler::shouldThrottleAnimations):
+        (WebCore::PageThrottler::shouldThrottleTimers):
+
 2013-05-29  Kent Tamura  <tkent@chromium.org>
 
         Remove leftover files for ENABLE_PAGE_POPUP and ENABLE_CALENDAR_PICKER
index 1e93d96..a0974c5 100644 (file)
@@ -4259,6 +4259,8 @@ webcore_sources += \
        Source/WebCore/page/PageGroupLoadDeferrer.h \
        Source/WebCore/page/PageSerializer.cpp \
        Source/WebCore/page/PageSerializer.h \
+       Source/WebCore/page/PageThrottler.cpp \
+       Source/WebCore/page/PageThrottler.h \
        Source/WebCore/page/PageVisibilityState.cpp \
        Source/WebCore/page/PageVisibilityState.h \
        Source/WebCore/page/Performance.cpp \
index c6bbda3..1aca204 100644 (file)
@@ -934,6 +934,7 @@ SOURCES += \
     page/PageConsole.cpp \
     page/PageGroup.cpp \
     page/PageGroupLoadDeferrer.cpp \
+    page/PageThrottler.cpp \
     page/PageVisibilityState.cpp \
     page/Performance.cpp \
     page/PerformanceEntry.cpp \
@@ -2099,6 +2100,7 @@ HEADERS += \
     page/PageGroupLoadDeferrer.h \
     page/Page.h \
     page/PageConsole.h \
+    page/PageThrottler.h \
     page/PageVisibilityState.h \
     page/PlugInClient.h \
     page/PopupOpeningObserver.h \
index ce7b579..3d7392e 100644 (file)
@@ -277,6 +277,8 @@ __ZN7WebCore13KeyboardEventC1ERKN3WTF12AtomicStringEbbPNS_9DOMWindowERKNS1_6Stri
 __ZN7WebCore13KeyboardEventC1ERKNS_21PlatformKeyboardEventEPNS_9DOMWindowE
 __ZN7WebCore13NodeTraversal19nextAncestorSiblingEPKNS_4NodeE
 __ZN7WebCore13NodeTraversal8previousEPKNS_4NodeES3_
+__ZN7WebCore13PageThrottler22reportInterestingEventEv
+__ZN7WebCore13PageThrottlerD1Ev
 __ZN7WebCore13QualifiedNameD1Ev
 __ZN7WebCore13ResourceErrorC1EP7NSError
 __ZN7WebCore13ResourceErrorC1EP9__CFError
index 5a5fbfd..f32d2b1 100755 (executable)
                                RelativePath="..\page\Page.cpp"
                                >
                        </File>
-                        <File
+             <File
                                RelativePath="..\page\PageConsole.cpp"
                                >
                        </File>
                                >
                        </File>
                        <File
+                               RelativePath="..\page\PageThrottler.cpp"
+                               >
+                       </File>
+                       <File
+                               RelativePath="..\page\PageThrottler.h"
+                               >
+                       </File>
+                       <File
                                RelativePath="..\page\PageVisibilityState.cpp"
                                >
                        </File>
index 94da049..afa6eac 100644 (file)
     <ClCompile Include="..\page\PageGroup.cpp" />
     <ClCompile Include="..\page\PageGroupLoadDeferrer.cpp" />
     <ClCompile Include="..\page\PageSerializer.cpp" />
+    <ClCompile Include="..\page\PageThrottler.cpp" />
     <ClCompile Include="..\page\PageVisibilityState.cpp" />
     <ClCompile Include="..\page\Performance.cpp" />
     <ClCompile Include="..\page\PerformanceEntry.cpp" />
index 96d9b57..e2b55f8 100644 (file)
     <ClCompile Include="..\page\PageSerializer.cpp">
       <Filter>page</Filter>
     </ClCompile>
+    <ClCompile Include="..\page\PageThrottler.cpp">
+      <Filter>page</Filter>
+    </ClCompile>
     <ClCompile Include="..\page\PageVisibilityState.cpp">
       <Filter>page</Filter>
     </ClCompile>
index 0088f84..a859e8e 100644 (file)
                A715E653134BBBEC00D8E713 /* ProgressShadowElement.h in Headers */ = {isa = PBXBuildFile; fileRef = A715E651134BBBEC00D8E713 /* ProgressShadowElement.h */; };
                A718760E0B2A120100A16ECE /* DragActions.h in Headers */ = {isa = PBXBuildFile; fileRef = A718760D0B2A120100A16ECE /* DragActions.h */; settings = {ATTRIBUTES = (Private, ); }; };
                A71878900B2D04AC00A16ECE /* DragControllerMac.mm in Sources */ = {isa = PBXBuildFile; fileRef = A718788F0B2D04AC00A16ECE /* DragControllerMac.mm */; };
+               A7197F24175689C4007B9442 /* PageThrottler.h in Headers */ = {isa = PBXBuildFile; fileRef = A7197F23175689C4007B9442 /* PageThrottler.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               A7197F2617568AE5007B9442 /* PageThrottler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A7197F2517568AE5007B9442 /* PageThrottler.cpp */; };
                A723F77B1484CA4C008C6DBE /* PlatformExportMacros.h in Headers */ = {isa = PBXBuildFile; fileRef = A723F77A1484CA4C008C6DBE /* PlatformExportMacros.h */; settings = {ATTRIBUTES = (Private, ); }; };
                A72763BF16689BFB002FCACB /* UserActionElementSet.h in Headers */ = {isa = PBXBuildFile; fileRef = A72763BE16689BFB002FCACB /* UserActionElementSet.h */; settings = {ATTRIBUTES = (Private, ); }; };
                A72B66A7169AD88D0034130C /* DOMShadowRoot.h in Copy Generated Headers */ = {isa = PBXBuildFile; fileRef = A7F73ED9169AD7AA00CBAA4B /* DOMShadowRoot.h */; };
                A715E651134BBBEC00D8E713 /* ProgressShadowElement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ProgressShadowElement.h; sourceTree = "<group>"; };
                A718760D0B2A120100A16ECE /* DragActions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DragActions.h; sourceTree = "<group>"; };
                A718788F0B2D04AC00A16ECE /* DragControllerMac.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = DragControllerMac.mm; sourceTree = "<group>"; };
+               A7197F23175689C4007B9442 /* PageThrottler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PageThrottler.h; sourceTree = "<group>"; };
+               A7197F2517568AE5007B9442 /* PageThrottler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PageThrottler.cpp; sourceTree = "<group>"; };
                A71A70C911AFB02000989D6D /* HTMLMeterElement.idl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = HTMLMeterElement.idl; sourceTree = "<group>"; };
                A7211F231678A54200957444 /* NodeRenderingTraversal.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = NodeRenderingTraversal.cpp; sourceTree = "<group>"; };
                A7211F241678A54200957444 /* NodeRenderingTraversal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NodeRenderingTraversal.h; sourceTree = "<group>"; };
                                7A674BDA0F9EBF4E006CF099 /* PageGroupLoadDeferrer.h */,
                                371E65CD13661EED00BEEDB0 /* PageSerializer.cpp */,
                                371E65CB13661EDC00BEEDB0 /* PageSerializer.h */,
+                               A7197F23175689C4007B9442 /* PageThrottler.h */,
+                               A7197F2517568AE5007B9442 /* PageThrottler.cpp */,
                                FFD5B977135CC97800D5E92A /* PageVisibilityState.cpp */,
                                FFD5B978135CC97800D5E92A /* PageVisibilityState.h */,
                                E526AF3E1727F8F200E41781 /* Performance.cpp */,
                                93CCF0270AF6C52900018E89 /* NavigationAction.h in Headers */,
                                979F43D41075E44A0000F83B /* NavigationScheduler.h in Headers */,
                                A9C6E5A60D746458006442E9 /* Navigator.h in Headers */,
+                               A7197F24175689C4007B9442 /* PageThrottler.h in Headers */,
                                E12719C70EEEC16800F61213 /* NavigatorBase.h in Headers */,
                                9711460414EF009A00674FD9 /* NavigatorGeolocation.h in Headers */,
                                8A309C9F123950BE00CB9204 /* NestingLevelIncrementer.h in Headers */,
                                0C45342710CDBBFA00869157 /* JSWebGLUniformLocation.cpp in Sources */,
                                77EF62F312F9DB7400C77BD2 /* JSWebGLVertexArrayObjectOES.cpp in Sources */,
                                31C0FF3D0E4CEFAC007D6FE5 /* JSWebKitAnimationEvent.cpp in Sources */,
+                               A7197F2617568AE5007B9442 /* PageThrottler.cpp in Sources */,
                                A2E8AE3716A49840006BB3AA /* JSWebKitCSSFilterRule.cpp in Sources */,
                                310603741432819C00ABF4BA /* JSWebKitCSSFilterValue.cpp in Sources */,
                                316FE0710E6CCBEE00BF6088 /* JSWebKitCSSKeyframeRule.cpp in Sources */,
index ac2b72a..03e6014 100644 (file)
@@ -70,6 +70,7 @@
 #include "NodeRenderingContext.h"
 #include "Page.h"
 #include "PageGroup.h"
+#include "PageThrottler.h"
 #include "RenderVideo.h"
 #include "RenderView.h"
 #include "ScriptController.h"
@@ -344,6 +345,11 @@ HTMLMediaElement::HTMLMediaElement(const QualifiedName& tagName, Document* docum
 HTMLMediaElement::~HTMLMediaElement()
 {
     LOG(Media, "HTMLMediaElement::~HTMLMediaElement");
+
+    if (!m_paused && m_pageThrottler) {
+        m_pageThrottler->allowThrottling();
+        m_pageThrottler.clear();
+    }
     if (m_isWaitingUntilMediaCanStart)
         document()->removeMediaCanStartListener(this);
     setShouldDelayLoadEvent(false);
@@ -2477,6 +2483,20 @@ void HTMLMediaElement::play()
     playInternal();
 }
 
+PageThrottler* HTMLMediaElement::pageThrottlerIfPossible()
+{
+    if (m_pageThrottler)
+        return m_pageThrottler.get();
+
+    if (!document())
+        return 0;
+
+    if (Page* page = document()->page())
+        m_pageThrottler = page->pageThrottler();
+    
+    return m_pageThrottler.get();
+}
+
 void HTMLMediaElement::playInternal()
 {
     LOG(Media, "HTMLMediaElement::playInternal");
@@ -2492,6 +2512,8 @@ void HTMLMediaElement::playInternal()
         m_mediaController->bringElementUpToSpeed(this);
 
     if (m_paused) {
+        if (PageThrottler* throttler = pageThrottlerIfPossible())
+            throttler->preventThrottling();
         m_paused = false;
         invalidateCachedTime();
         scheduleEvent(eventNames().playEvent);
@@ -2502,7 +2524,6 @@ void HTMLMediaElement::playInternal()
             scheduleEvent(eventNames().playingEvent);
     }
     m_autoplaying = false;
-
     updatePlayState();
     updateMediaController();
 }
@@ -2529,6 +2550,8 @@ void HTMLMediaElement::pauseInternal()
     m_autoplaying = false;
 
     if (!m_paused) {
+        if (PageThrottler* throttler = m_pageThrottler.get())
+            throttler->preventThrottling();
         m_paused = true;
         scheduleTimeupdateEvent(false);
         scheduleEvent(eventNames().pauseEvent);
index c1a82fd..456c76f 100644 (file)
@@ -59,10 +59,11 @@ class MediaElementAudioSourceNode;
 class Event;
 class HTMLSourceElement;
 class HTMLTrackElement;
+class KURL;
 class MediaController;
 class MediaControls;
 class MediaError;
-class KURL;
+class PageThrottler;
 class TimeRanges;
 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
 class Widget;
@@ -755,6 +756,9 @@ private:
     friend class MediaController;
     RefPtr<MediaController> m_mediaController;
 
+    PageThrottler* pageThrottlerIfPossible();
+    RefPtr<PageThrottler> m_pageThrottler;
+
 #if PLATFORM(MAC)
     OwnPtr<DisplaySleepDisabler> m_sleepDisabler;
 #endif
index 33c63a6..cbfa21d 100644 (file)
@@ -183,7 +183,6 @@ void MediaController::unpause()
     // When the unpause() method is invoked, if the MediaController is a paused media controller,
     if (!m_paused)
         return;
-
     // the user agent must change the MediaController into a playing media controller,
     m_paused = false;
     // queue a task to fire a simple event named play at the MediaController,
index 745a1e7..28f4371 100644 (file)
@@ -362,6 +362,12 @@ public:
     virtual void didAddHeaderLayer(GraphicsLayer*) { }
     virtual void didAddFooterLayer(GraphicsLayer*) { }
 
+    // These methods are used to report pages that are performing
+    // some task that we consider to be "active", and so the user
+    // would likely want the page to remain running uninterrupted.
+    virtual void incrementActivePageCount() { }
+    virtual void decrementActivePageCount() { }
+
 protected:
     virtual ~ChromeClient() { }
 };
index e884524..40a6dde 100644 (file)
@@ -59,6 +59,7 @@
 #include "PageCache.h"
 #include "PageConsole.h"
 #include "PageGroup.h"
+#include "PageThrottler.h"
 #include "PlugInClient.h"
 #include "PluginData.h"
 #include "PluginView.h"
@@ -184,6 +185,7 @@ Page::Page(PageClients& pageClients)
 #endif
     , m_alternativeTextClient(pageClients.alternativeTextClient)
     , m_scriptedAnimationsSuspended(false)
+    , m_pageThrottler(PageThrottler::create(this))
     , m_console(PageConsole::create(this))
 {
     ASSERT(m_editorClient);
@@ -231,6 +233,7 @@ Page::~Page()
 #ifndef NDEBUG
     pageCounter.decrement();
 #endif
+    m_pageThrottler->clearPage();
 
 }
 
@@ -957,12 +960,9 @@ void Page::resumeScriptedAnimations()
     }
 }
 
-void Page::setThrottled(bool isThrottled)
+void Page::setThrottled(bool throttled)
 {
-    for (Frame* frame = mainFrame(); frame; frame = frame->tree()->traverseNext()) {
-        if (frame->document())
-            frame->document()->scriptedAnimationControllerSetThrottled(isThrottled);
-    }
+    m_pageThrottler->setThrottled(throttled);
 }
 
 void Page::userStyleSheetLocationChanged()
index ce70373..e9f0a23 100644 (file)
@@ -36,6 +36,7 @@
 #include <wtf/HashMap.h>
 #include <wtf/HashSet.h>
 #include <wtf/Noncopyable.h>
+#include <wtf/RefCounted.h>
 #include <wtf/text/WTFString.h>
 
 #if OS(SOLARIS)
@@ -76,6 +77,7 @@ class MediaCanStartListener;
 class Node;
 class PageConsole;
 class PageGroup;
+class PageThrottler;
 class PlugInClient;
 class PluginData;
 class PluginViewBase;
@@ -110,6 +112,8 @@ struct ArenaSize {
 class Page : public Supplementable<Page> {
     WTF_MAKE_NONCOPYABLE(Page);
     friend class Settings;
+    friend class PageThrottler;
+
 public:
     static void updateStyleForAllPagesAfterGlobalChangeInEnvironment();
 
@@ -389,6 +393,8 @@ public:
     void sawMediaEngine(const String& engineName);
     void resetSeenMediaEngines();
 
+    PageThrottler* pageThrottler() { return m_pageThrottler.get(); }
+
     PageConsole* console() { return m_console.get(); }
 
 #if ENABLE(HIDDEN_PAGE_DOM_TIMER_THROTTLING)
@@ -527,6 +533,8 @@ private:
     AlternativeTextClient* m_alternativeTextClient;
 
     bool m_scriptedAnimationsSuspended;
+    RefPtr<PageThrottler> m_pageThrottler;
+
     OwnPtr<PageConsole> m_console;
 
     HashSet<String> m_seenPlugins;
diff --git a/Source/WebCore/page/PageThrottler.cpp b/Source/WebCore/page/PageThrottler.cpp
new file mode 100644 (file)
index 0000000..14bcfa2
--- /dev/null
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "PageThrottler.h"
+
+#include "Chrome.h"
+#include "ChromeClient.h"
+#include "Frame.h"
+#include "Page.h"
+
+namespace WebCore {
+
+static const double kThrottleHysteresisSeconds = 2.0;
+
+PageThrottler::PageThrottler(Page* page)
+    : m_page(page)
+    , m_activeThrottleBlockers(0)
+    , m_throttleState(PageNotThrottledState)
+    , m_throttleHysteresisTimer(this, &PageThrottler::throttleHysteresisTimerFired)
+{
+    if (ChromeClient* chromeClient = m_page->chrome().client())
+        chromeClient->incrementActivePageCount();
+}
+
+PageThrottler::~PageThrottler()
+{
+    if (m_throttleState != PageThrottledState && m_page) {
+        if (ChromeClient* chromeClient = m_page->chrome().client())
+            chromeClient->decrementActivePageCount();
+    }
+}
+
+void PageThrottler::clearPage()
+{
+    setThrottled(false);
+    m_page = 0;
+}
+
+void PageThrottler::throttlePage()
+{
+    m_throttleState = PageThrottledState;
+
+    if (!m_page)
+        return;
+
+    if (ChromeClient* chromeClient = m_page->chrome().client())
+        chromeClient->decrementActivePageCount();
+
+    for (Frame* frame = m_page->mainFrame(); frame; frame = frame->tree()->traverseNext()) {
+        if (frame->document())
+            frame->document()->scriptedAnimationControllerSetThrottled(true);
+    }
+}
+
+void PageThrottler::unthrottlePage()
+{
+    PageThrottleState oldState = m_throttleState;
+    m_throttleState = PageNotThrottledState;
+
+    if (!m_page || oldState == PageNotThrottledState)
+        return;
+
+    if (oldState == PageThrottledState) {
+        if (ChromeClient* chromeClient = m_page->chrome().client())
+            chromeClient->incrementActivePageCount();
+    }
+    
+    for (Frame* frame = m_page->mainFrame(); frame; frame = frame->tree()->traverseNext()) {
+        if (frame->document())
+            frame->document()->scriptedAnimationControllerSetThrottled(false);
+    }
+}
+
+void PageThrottler::setThrottled(bool isThrottled)
+{
+    if (isThrottled) {
+        m_throttleState = PageWaitingToThrottleState;
+        startThrottleHysteresisTimer();
+    } else {
+        unthrottlePage();
+        stopThrottleHysteresisTimer();
+    }
+}
+
+void PageThrottler::preventThrottling()
+{
+    // If we've already got events that block throttling we can increment
+    // and return early
+    if (m_activeThrottleBlockers++)
+        return;
+
+    if (m_throttleState == PageNotThrottledState)
+        return;
+
+    if (m_throttleState == PageThrottledState)
+        unthrottlePage();
+
+    m_throttleState = PageWaitingToThrottleState;
+    stopThrottleHysteresisTimer();
+}
+
+void PageThrottler::allowThrottling()
+{
+    ASSERT(m_activeThrottleBlockers > 0);
+    m_activeThrottleBlockers--;
+    if (m_activeThrottleBlockers)
+        return;
+
+    if (m_throttleState == PageNotThrottledState)
+        return;
+
+    ASSERT(m_throttleState == PageWaitingToThrottleState);
+    startThrottleHysteresisTimer();
+}
+
+void PageThrottler::stopThrottleHysteresisTimer()
+{
+    m_throttleHysteresisTimer.stop();
+}
+
+void PageThrottler::reportInterestingEvent()
+{
+    if (m_throttleState == PageNotThrottledState)
+        return;
+    if (m_throttleState == PageThrottledState)
+        unthrottlePage();
+    m_throttleState = PageWaitingToThrottleState;
+    startThrottleHysteresisTimer();
+}
+
+void PageThrottler::startThrottleHysteresisTimer()
+{
+    if (m_throttleHysteresisTimer.isActive())
+        m_throttleHysteresisTimer.stop();
+    if (!m_activeThrottleBlockers)
+        m_throttleHysteresisTimer.startOneShot(kThrottleHysteresisSeconds);
+}
+
+void PageThrottler::throttleHysteresisTimerFired(Timer<PageThrottler>*)
+{
+    ASSERT(!m_activeThrottleBlockers);
+    throttlePage();
+}
+
+}
diff --git a/Source/WebCore/page/PageThrottler.h b/Source/WebCore/page/PageThrottler.h
new file mode 100644 (file)
index 0000000..14a246a
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef PageThrottler_h
+#define PageThrottler_h
+
+#include "Timer.h"
+
+#include <wtf/RefCounted.h>
+
+namespace WebCore {
+
+class Page;
+
+class PageThrottler : public RefCounted<PageThrottler> {
+public:
+    static PassRefPtr<PageThrottler> create(Page* page)
+    {
+        return adoptRef(new PageThrottler(page));
+    }
+
+    bool shouldThrottleAnimations() const { return m_throttleState != PageNotThrottledState; }
+    bool shouldThrottleTimers() const { return m_throttleState == PageNotThrottledState; }
+
+    void setThrottled(bool);
+
+    void preventThrottling();
+    void reportInterestingEvent();
+    void allowThrottling();
+    void clearPage();
+
+    ~PageThrottler();
+
+private:
+    enum PageThrottleState {
+        PageNotThrottledState,
+        PageWaitingToThrottleState,
+        PageThrottledState
+    };
+
+    PageThrottler(Page*);
+    void startThrottleHysteresisTimer();
+    void stopThrottleHysteresisTimer();
+    void throttleHysteresisTimerFired(Timer<PageThrottler>*);
+
+    Page* m_page;
+
+    void throttlePage();
+    void unthrottlePage();
+
+    unsigned m_activeThrottleBlockers;
+    PageThrottleState m_throttleState;
+    Timer<PageThrottler> m_throttleHysteresisTimer;
+};
+
+}
+#endif
index f2e9132..bcd8ed6 100644 (file)
@@ -1,3 +1,47 @@
+2013-05-28  Oliver Hunt  <oliver@apple.com>
+
+        Add more accurate activity state tracking
+        https://bugs.webkit.org/show_bug.cgi?id=116893
+
+        Reviewed by Gavin Barraclough.
+
+        This extends the logic in WebCore from a Page to Process
+        granularity, so we will avoid lowering the child process
+        priority if there is active content.  This also plumbs in
+        the logic to allow plugins to report that they have done
+        something "interesting".  Currently this is somewhat conservative
+        but even this is sufficient to fix some stuttering issues
+        that we've seen.
+
+        * Shared/ChildProcess.cpp:
+        (WebKit::ChildProcess::ChildProcess):
+        * Shared/ChildProcess.h:
+        (ChildProcess):
+        (WebKit::ChildProcess::processSuppressionEnabled):
+        (WebKit::ChildProcess::incrementActiveTaskCount):
+        (WebKit::ChildProcess::decrementActiveTaskCount):
+        * Shared/mac/ChildProcessMac.mm:
+        (WebKit):
+        (WebKit::ChildProcess::setProcessSuppressionEnabledInternal):
+        (WebKit::ChildProcess::setProcessSuppressionEnabled):
+        (WebKit::ChildProcess::incrementActiveTaskCount):
+        (WebKit::ChildProcess::decrementActiveTaskCount):
+        (WebKit::ChildProcess::suspensionHysteresisTimerFired):
+        * WebProcess/Plugins/Netscape/NPRuntimeObjectMap.cpp:
+        (WebKit::NPRuntimeObjectMap::NPRuntimeObjectMap):
+        (WebKit::NPRuntimeObjectMap::evaluate):
+        * WebProcess/Plugins/Netscape/NPRuntimeObjectMap.h:
+        (WebCore):
+        (NPRuntimeObjectMap):
+        * WebProcess/Plugins/PluginView.cpp:
+        (WebKit::PluginView::PluginView):
+        * WebProcess/WebCoreSupport/WebChromeClient.cpp:
+        (WebKit::WebChromeClient::incrementActivePageCount):
+        (WebKit):
+        (WebKit::WebChromeClient::decrementActivePageCount):
+        * WebProcess/WebCoreSupport/WebChromeClient.h:
+        (WebChromeClient):
+
 2013-05-29  Anders Carlsson  <andersca@apple.com>
 
         Return earlier if there's no session storage namespace
index a9a876a..7c270e8 100644 (file)
@@ -40,6 +40,11 @@ ChildProcess::ChildProcess()
     : m_terminationTimeout(0)
     , m_terminationCounter(0)
     , m_terminationTimer(RunLoop::main(), this, &ChildProcess::terminationTimerFired)
+#if PLATFORM(MAC)
+    , m_activeTaskCount(0)
+    , m_shouldSuspend(false)
+    , m_suspensionHysteresisTimer(RunLoop::main(), this, &ChildProcess::suspensionHysteresisTimerFired)
+#endif
 {
 }
 
index c1a8673..bd2e0a4 100644 (file)
@@ -62,10 +62,15 @@ public:
     void removeMessageReceiver(CoreIPC::StringReference messageReceiverName, uint64_t destinationID);
 
 #if PLATFORM(MAC)
-    bool processSuppressionEnabled() const { return !m_processSuppressionAssertion; }
     void setProcessSuppressionEnabled(bool);
+    bool processSuppressionEnabled() const { return !m_processSuppressionAssertion; }
+    void incrementActiveTaskCount();
+    void decrementActiveTaskCount();
 
     void setApplicationIsDaemon();
+#else
+    void incrementActiveTaskCount() { }
+    void decrementActiveTaskCount() { }
 #endif
 
     CoreIPC::Connection* parentProcessConnection() const { return m_connection.get(); }
@@ -109,6 +114,11 @@ private:
     CoreIPC::MessageReceiverMap m_messageReceiverMap;
 
 #if PLATFORM(MAC)
+    void suspensionHysteresisTimerFired();
+    void setProcessSuppressionEnabledInternal(bool);
+    size_t m_activeTaskCount;
+    bool m_shouldSuspend;
+    WebCore::RunLoop::Timer<ChildProcess> m_suspensionHysteresisTimer;
     RetainPtr<id> m_processSuppressionAssertion;
 #endif
 };
index 00381f4..5b5dabd 100644 (file)
@@ -59,13 +59,16 @@ using namespace WebCore;
 
 namespace WebKit {
 
-void ChildProcess::setProcessSuppressionEnabled(bool processSuppressionEnabled)
+static const double kSuspensionHysteresisSeconds = 5.0;
+
+void ChildProcess::setProcessSuppressionEnabledInternal(bool processSuppressionEnabled)
 {
 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1090
     if (this->processSuppressionEnabled() == processSuppressionEnabled)
         return;
 
     if (processSuppressionEnabled) {
+        ASSERT(!m_activeTaskCount);
         [[NSProcessInfo processInfo] endActivity:m_processSuppressionAssertion.get()];
         m_processSuppressionAssertion.clear();
     } else {
@@ -77,6 +80,47 @@ void ChildProcess::setProcessSuppressionEnabled(bool processSuppressionEnabled)
 #endif
 }
 
+void ChildProcess::setProcessSuppressionEnabled(bool processSuppressionEnabled)
+{
+    if (this->processSuppressionEnabled() == processSuppressionEnabled)
+        return;
+    if (m_shouldSuspend == processSuppressionEnabled)
+        return;
+    m_shouldSuspend = processSuppressionEnabled;
+    if (m_shouldSuspend) {
+        if (!m_activeTaskCount)
+            m_suspensionHysteresisTimer.startOneShot(kSuspensionHysteresisSeconds);
+        return;
+    }
+    setProcessSuppressionEnabledInternal(false);
+}
+
+void ChildProcess::incrementActiveTaskCount()
+{
+    m_activeTaskCount++;
+    if (m_suspensionHysteresisTimer.isActive())
+        m_suspensionHysteresisTimer.stop();
+    if (m_activeTaskCount)
+        setProcessSuppressionEnabledInternal(false);
+}
+
+void ChildProcess::decrementActiveTaskCount()
+{
+    ASSERT(m_activeTaskCount);
+    m_activeTaskCount--;
+    if (m_activeTaskCount)
+        return;
+    if (m_shouldSuspend)
+        m_suspensionHysteresisTimer.startOneShot(kSuspensionHysteresisSeconds);
+}
+
+void ChildProcess::suspensionHysteresisTimerFired()
+{
+    ASSERT(!m_activeTaskCount);
+    ASSERT(m_shouldSuspend);
+    setProcessSuppressionEnabledInternal(true);
+}
+
 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1090
 static void initializeTimerCoalescingPolicy()
 {
index 1667112..ef06418 100644 (file)
@@ -41,6 +41,8 @@
 #include <JavaScriptCore/StrongInlines.h>
 #include <WebCore/DOMWrapperWorld.h>
 #include <WebCore/Frame.h>
+#include <WebCore/Page.h>
+#include <WebCore/PageThrottler.h>
 #include <WebCore/ScriptController.h>
 
 using namespace JSC;
@@ -49,8 +51,9 @@ using namespace WebCore;
 namespace WebKit {
 
 
-NPRuntimeObjectMap::NPRuntimeObjectMap(PluginView* pluginView)
+NPRuntimeObjectMap::NPRuntimeObjectMap(PluginView* pluginView, PageThrottler* pageThrottler)
     : m_pluginView(pluginView)
+    , m_pageThrottler(pageThrottler)
     , m_finalizationTimer(RunLoop::main(), this, &NPRuntimeObjectMap::invalidateQueuedObjects)
 {
 }
@@ -182,6 +185,7 @@ void NPRuntimeObjectMap::convertJSValueToNPVariant(ExecState* exec, JSValue valu
 
 bool NPRuntimeObjectMap::evaluate(NPObject* npObject, const String& scriptString, NPVariant* result)
 {
+    m_pageThrottler->reportInterestingEvent();
     Strong<JSGlobalObject> globalObject(this->globalObject()->vm(), this->globalObject());
     if (!globalObject)
         return false;
index 8744289..7585afe 100644 (file)
@@ -45,6 +45,10 @@ namespace JSC {
     class JSValue;
 }
 
+namespace WebCore {
+    class PageThrottler;
+}
+
 namespace WebKit {
 
 class JSNPObject;
@@ -54,7 +58,7 @@ class PluginView;
 // A per plug-in map of NPObjects that wrap JavaScript objects.
 class NPRuntimeObjectMap : private JSC::WeakHandleOwner {
 public:
-    explicit NPRuntimeObjectMap(PluginView*);
+    explicit NPRuntimeObjectMap(PluginView*, WebCore::PageThrottler*);
 
     class PluginProtector {
     public:
@@ -95,7 +99,7 @@ private:
     void invalidateQueuedObjects();
 
     PluginView* m_pluginView;
-
+    RefPtr<WebCore::PageThrottler> m_pageThrottler;
     HashMap<JSC::JSObject*, NPJSObject*> m_npJSObjects;
     HashMap<NPObject*, JSC::Weak<JSNPObject>> m_jsNPObjects;
     Vector<NPObject*> m_npObjectsToFinalize;
index ec791f6..ce6df60 100644 (file)
@@ -56,6 +56,7 @@
 #include <WebCore/NetscapePlugInStreamLoader.h>
 #include <WebCore/NetworkingContext.h>
 #include <WebCore/Page.h>
+#include <WebCore/PageThrottler.h>
 #include <WebCore/PlatformMouseEvent.h>
 #include <WebCore/ProtectionSpace.h>
 #include <WebCore/ProxyServer.h>
@@ -277,7 +278,7 @@ PluginView::PluginView(PassRefPtr<HTMLPlugInElement> pluginElement, PassRefPtr<P
     , m_isBeingDestroyed(false)
     , m_pendingURLRequestsTimer(RunLoop::main(), this, &PluginView::pendingURLRequestsTimerFired)
 #if ENABLE(NETSCAPE_PLUGIN_API)
-    , m_npRuntimeObjectMap(this)
+    , m_npRuntimeObjectMap(this, m_webPage->corePage()->pageThrottler())
 #endif
     , m_manualStreamState(StreamStateInitial)
     , m_pluginSnapshotTimer(this, &PluginView::pluginSnapshotTimerFired, pluginSnapshotTimerDelay)
index b8c4cce..873dbcc 100644 (file)
@@ -900,4 +900,14 @@ void WebChromeClient::didAddFooterLayer(GraphicsLayer* footerParent)
         banner->didAddParentLayer(footerParent);
 }
 
+void WebChromeClient::incrementActivePageCount()
+{
+    WebProcess::shared().incrementActiveTaskCount();
+}
+
+void WebChromeClient::decrementActivePageCount()
+{
+    WebProcess::shared().decrementActiveTaskCount();
+}
+
 } // namespace WebKit
index 7044b54..4ef29b5 100644 (file)
@@ -233,6 +233,9 @@ private:
     virtual void didAddHeaderLayer(WebCore::GraphicsLayer*) OVERRIDE;
     virtual void didAddFooterLayer(WebCore::GraphicsLayer*) OVERRIDE;
 
+    virtual void incrementActivePageCount() OVERRIDE;
+    virtual void decrementActivePageCount() OVERRIDE;
+
     String m_cachedToolTip;
     mutable RefPtr<WebFrame> m_cachedFrameSetLargestFrame;
     mutable bool m_cachedMainFrameHasHorizontalScrollbar;