Diff
Modified: trunk/Source/WebCore/ChangeLog (213856 => 213857)
--- trunk/Source/WebCore/ChangeLog 2017-03-13 18:00:25 UTC (rev 213856)
+++ trunk/Source/WebCore/ChangeLog 2017-03-13 18:09:30 UTC (rev 213857)
@@ -1,3 +1,22 @@
+2017-03-13 Chris Dumez <[email protected]>
+
+ Allow termination of background WebProcesses that go over a given CPU usage threshold
+ https://bugs.webkit.org/show_bug.cgi?id=169456
+ <rdar://problem/30960968>
+
+ Reviewed by Andreas Kling.
+
+ Add CPUMonitor utility class to monitor CPU usage and call a provided lambda
+ whenever the process exceeds the provided CPU limit over a given period of
+ time.
+
+ * WebCore.xcodeproj/project.pbxproj:
+ * platform/CPUMonitor.cpp: Added.
+ (WebCore::CPUMonitor::CPUMonitor):
+ (WebCore::CPUMonitor::setCPULimit):
+ (WebCore::CPUMonitor::timerFired):
+ * platform/CPUMonitor.h: Added.
+
2017-03-13 Antoine Quint <[email protected]>
[Modern Media Controls] Volume icon doesn't turn to mute when the knob is set to 0
Modified: trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj (213856 => 213857)
--- trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj 2017-03-13 18:00:25 UTC (rev 213856)
+++ trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj 2017-03-13 18:09:30 UTC (rev 213857)
@@ -1912,6 +1912,8 @@
467302021C4EFE7800BCB357 /* IgnoreOpensDuringUnloadCountIncrementer.h in Headers */ = {isa = PBXBuildFile; fileRef = 467302011C4EFE6600BCB357 /* IgnoreOpensDuringUnloadCountIncrementer.h */; };
4689F1AF1267BAE100E8D380 /* FileMetadata.h in Headers */ = {isa = PBXBuildFile; fileRef = 4689F1AE1267BAE100E8D380 /* FileMetadata.h */; };
46B63F6C1C6E8D19002E914B /* JSEventTargetCustom.h in Headers */ = {isa = PBXBuildFile; fileRef = 46B63F6B1C6E8CDF002E914B /* JSEventTargetCustom.h */; settings = {ATTRIBUTES = (Private, ); }; };
+ 46C696CB1E7205F700597937 /* CPUMonitor.h in Headers */ = {isa = PBXBuildFile; fileRef = 46C696C91E7205E400597937 /* CPUMonitor.h */; settings = {ATTRIBUTES = (Private, ); }; };
+ 46C696CC1E7205FC00597937 /* CPUMonitor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 46C696CA1E7205E400597937 /* CPUMonitor.cpp */; };
46C83EFD1A9BBE2900A79A41 /* GeoNotifier.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 46C83EFB1A9BBE2900A79A41 /* GeoNotifier.cpp */; };
46C83EFE1A9BBE2900A79A41 /* GeoNotifier.h in Headers */ = {isa = PBXBuildFile; fileRef = 46C83EFC1A9BBE2900A79A41 /* GeoNotifier.h */; settings = {ATTRIBUTES = (Private, ); }; };
46DBB6501AB8C96F00D9A813 /* PowerObserverMac.h in Headers */ = {isa = PBXBuildFile; fileRef = 46DBB64E1AB8C96F00D9A813 /* PowerObserverMac.h */; };
@@ -9481,6 +9483,8 @@
467302011C4EFE6600BCB357 /* IgnoreOpensDuringUnloadCountIncrementer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IgnoreOpensDuringUnloadCountIncrementer.h; sourceTree = "<group>"; };
4689F1AE1267BAE100E8D380 /* FileMetadata.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FileMetadata.h; sourceTree = "<group>"; };
46B63F6B1C6E8CDF002E914B /* JSEventTargetCustom.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSEventTargetCustom.h; sourceTree = "<group>"; };
+ 46C696C91E7205E400597937 /* CPUMonitor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CPUMonitor.h; sourceTree = "<group>"; };
+ 46C696CA1E7205E400597937 /* CPUMonitor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CPUMonitor.cpp; sourceTree = "<group>"; };
46C83EFB1A9BBE2900A79A41 /* GeoNotifier.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GeoNotifier.cpp; sourceTree = "<group>"; };
46C83EFC1A9BBE2900A79A41 /* GeoNotifier.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GeoNotifier.h; sourceTree = "<group>"; };
46DBB64E1AB8C96F00D9A813 /* PowerObserverMac.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PowerObserverMac.h; sourceTree = "<group>"; };
@@ -23637,6 +23641,8 @@
D8B6152E1032495100C8554A /* Cookie.h */,
339B5B62131DAA3200F48D02 /* CookiesStrategy.h */,
862F129D18C1572C005C54AF /* CountedUserActivity.h */,
+ 46C696CA1E7205E400597937 /* CPUMonitor.cpp */,
+ 46C696C91E7205E400597937 /* CPUMonitor.h */,
463763061E26FDBA008CD46D /* CPUTime.cpp */,
463763071E26FDBA008CD46D /* CPUTime.h */,
E11AF15011B9A1A300805103 /* Cursor.cpp */,
@@ -29023,6 +29029,7 @@
E139866415478474001E3F65 /* StyleResolver.h in Headers */,
E4BBED4D14FCDBA1003F0B98 /* StyleRule.h in Headers */,
E4946EAF156E64DD00D3297F /* StyleRuleImport.h in Headers */,
+ 46C696CB1E7205F700597937 /* CPUMonitor.h in Headers */,
E461D65F1BB0C80D00CB5645 /* StyleScope.h in Headers */,
F47A5E3E195B8C8A00483100 /* StyleScrollSnapPoints.h in Headers */,
9D6380101AF173220031A15C /* StyleSelfAlignmentData.h in Headers */,
@@ -30262,6 +30269,7 @@
59B597731108656B007159E8 /* BridgeJSC.cpp in Sources */,
7A45032F18DB717200377B34 /* BufferedLineReader.cpp in Sources */,
F55B3DAF1251F12D003EF269 /* ButtonInputType.cpp in Sources */,
+ 46C696CC1E7205FC00597937 /* CPUMonitor.cpp in Sources */,
1A569CF70D7E2B82007C3983 /* c_class.cpp in Sources */,
ECA680C91E67730B00731D20 /* StringUtilities.mm in Sources */,
1A569CF90D7E2B82007C3983 /* c_instance.cpp in Sources */,
Added: trunk/Source/WebCore/platform/CPUMonitor.cpp (0 => 213857)
--- trunk/Source/WebCore/platform/CPUMonitor.cpp (rev 0)
+++ trunk/Source/WebCore/platform/CPUMonitor.cpp 2017-03-13 18:09:30 UTC (rev 213857)
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2017 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 "CPUMonitor.h"
+
+namespace WebCore {
+
+CPUMonitor::CPUMonitor(Seconds checkInterval, ExceededCPULimitHandler&& exceededCPULimitHandler)
+ : m_checkInterval(checkInterval)
+ , m_exceededCPULimitHandler(WTFMove(exceededCPULimitHandler))
+ , m_timer(*this, &CPUMonitor::timerFired)
+{
+}
+
+void CPUMonitor::setCPULimit(std::optional<double> cpuLimit)
+{
+ if (m_cpuLimit == cpuLimit)
+ return;
+
+ m_cpuLimit = cpuLimit;
+ if (m_cpuLimit) {
+ if (!m_timer.isActive()) {
+ m_lastCPUTime = getCPUTime();
+ m_timer.startRepeating(m_checkInterval);
+ }
+ } else
+ m_timer.stop();
+}
+
+void CPUMonitor::timerFired()
+{
+ ASSERT(m_cpuLimit);
+
+ if (!m_lastCPUTime) {
+ m_lastCPUTime = getCPUTime();
+ return;
+ }
+
+ auto cpuTime = getCPUTime();
+ if (!cpuTime)
+ return;
+
+ auto cpuUsagePercent = cpuTime.value().percentageCPUUsageSince(m_lastCPUTime.value());
+ if (cpuUsagePercent > m_cpuLimit.value() * 100)
+ m_exceededCPULimitHandler();
+
+ m_lastCPUTime = cpuTime;
+}
+
+} // namespace WebCore
Added: trunk/Source/WebCore/platform/CPUMonitor.h (0 => 213857)
--- trunk/Source/WebCore/platform/CPUMonitor.h (rev 0)
+++ trunk/Source/WebCore/platform/CPUMonitor.h 2017-03-13 18:09:30 UTC (rev 213857)
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#pragma once
+
+#include "CPUTime.h"
+#include "Timer.h"
+
+#include <wtf/Function.h>
+
+namespace WebCore {
+
+class CPUMonitor {
+public:
+ using ExceededCPULimitHandler = WTF::Function<void()>;
+ WEBCORE_EXPORT CPUMonitor(Seconds checkInterval, ExceededCPULimitHandler&&);
+
+ WEBCORE_EXPORT void setCPULimit(std::optional<double>);
+
+private:
+ void timerFired();
+
+ Seconds m_checkInterval;
+ ExceededCPULimitHandler m_exceededCPULimitHandler;
+ Timer m_timer;
+ std::optional<double> m_cpuLimit;
+ std::optional<CPUTime> m_lastCPUTime;
+};
+
+} // namespace WebCore
Modified: trunk/Source/WebKit2/ChangeLog (213856 => 213857)
--- trunk/Source/WebKit2/ChangeLog 2017-03-13 18:00:25 UTC (rev 213856)
+++ trunk/Source/WebKit2/ChangeLog 2017-03-13 18:09:30 UTC (rev 213857)
@@ -1,3 +1,129 @@
+2017-03-13 Chris Dumez <[email protected]>
+
+ Allow termination of background WebProcesses that go over a given CPU usage threshold
+ https://bugs.webkit.org/show_bug.cgi?id=169456
+ <rdar://problem/30960968>
+
+ Reviewed by Andreas Kling.
+
+ Allow termination of background WebProcesses that go over a given CPU usage threshold.
+ This can be enabled client side via the WKPageConfigurationSetBackgroundCPULimit()
+ SPI. The client provides the actual CPU threshold it wants to use at page level.
+
+ If such limit is set, whenever a WebContent process has no visible pages, we start
+ monitoring its CPU usage over 15 minutes periods. At the end of each period, we
+ check if the process' average CPU usage over this period was greater than the
+ background CPU limit. If it greater, the WebContent process send an IPC message to
+ the UIProcess letting it know that it exceeded the CPU limit. The UI process will
+ then log a message and terminate the process unless it has any audio playing.
+
+ Once a WebProcess has been terminated, we do not let the client know until one of its
+ pages becomes visible again. When this happens, we call the processDidCrash
+ delegate and Safari will take care of reloading the tab and showing the crash
+ banner then. This is done because we do not want to reload content that is
+ using a lot of CPU while in the background.
+
+ * Shared/WebPageCreationParameters.cpp:
+ (WebKit::WebPageCreationParameters::encode):
+ (WebKit::WebPageCreationParameters::decode):
+ * Shared/WebPageCreationParameters.h:
+ Add backgroundCPULimit to WebPageCreationParameters.
+
+ * UIProcess/API/APIPageConfiguration.cpp:
+ (API::PageConfiguration::copy):
+ * UIProcess/API/APIPageConfiguration.h:
+ (API::PageConfiguration::backgroundCPULimit):
+ (API::PageConfiguration::setBackgroundCPULimit):
+ Add backgroundCPULimit to APIPageConfiguration.
+
+ * UIProcess/API/C/WKPageConfigurationRef.cpp:
+ (WKPageConfigurationSetBackgroundCPULimit):
+ * UIProcess/API/C/WKPageConfigurationRef.h:
+ Add SPI to set background CPU limit.
+
+ * UIProcess/WebPageProxy.cpp:
+ (WebKit::WebPageProxy::reattachToWebProcess):
+ Reset m_wasTerminatedDueToResourceExhaustionWhileInBackground flag
+ as the process was restarted.
+
+ (WebKit::WebPageProxy::dispatchActivityStateChange):
+ When the visibility state changes for a page that was terminated
+ while in the background due to exceeded CPU limit, notify the
+ client that the process crashed via the processDidCrash delegate.
+ Safari will use this delegate to reload the tab and show the crash
+ banner.
+
+ (WebKit::WebPageProxy::terminateProcess):
+ Add parameter to terminateProcess() to provide the reason. If the
+ page was terminated due to reaching CPU limit, set a flag so we
+ can delay calling processDidCrash until the page becomes visible
+ again.
+
+ (WebKit::WebPageProxy::creationParameters):
+ Set backgroundCPULimit on the WebPageCreationParameters.
+
+ * UIProcess/WebPageProxy.h:
+
+ * UIProcess/WebProcessProxy.cpp:
+ (WebKit::pagesCopy):
+ Add utility function to copy the list of pages to a Vector.
+
+ (WebKit::WebProcessProxy::didExceedBackgroundCPULimit):
+ When we get an IPC message from a WebContent process to let us
+ know that the process exceeded the background CPU limit, we log
+ a message and we terminate it if it has no audio playing.
+
+ * UIProcess/WebProcessProxy.h:
+
+ * UIProcess/WebProcessProxy.messages.in:
+ Add DidExceedBackgroundCPULimit IPC message so the WebContent process
+ can let us know when it goes over the background CPU limit.
+
+ * WebProcess/WebPage/WebPage.cpp:
+ (WebKit::m_backgroundCPULimit):
+ (WebKit::WebPage::setActivityState):
+ Notify the WebProcess whenever the activity state of a WebPage changes.
+ The WebProcess currently uses this information to determine when the
+ visibility of a page changes. This is needed as we only want to monitor
+ CPU usage of *background* WebContent processes (processes that have no
+ visible WebPage).
+
+ * WebProcess/WebPage/WebPage.h:
+ (WebKit::WebPage::backgroundCPULimit):
+
+ * WebProcess/WebProcess.cpp:
+ (WebKit::WebProcess::createWebPage):
+ (WebKit::WebProcess::removeWebPage):
+ Call updateBackgroundCPULimit() whenever a WebPage is added or removed
+ since the limit is per page.
+
+ (WebKit::WebProcess::updateBackgroundCPULimit):
+ (WebKit::WebProcess::updateBackgroundCPUMonitorState):
+ No-ops on other platforms than Mac at the moment.
+
+ (WebKit::WebProcess::pageActivityStateDidChange):
+ Call updateBackgroundCPUMonitorState() whenever the visibility of the
+ WebPage changes as we only monitor the CPU usage of *background* WebContent
+ processes.
+
+ (WebKit::WebProcess::hasVisibleWebPage):
+ Add utility function to determine if there is any visible WebPage in this
+ WebContent process. If the function returns false, then we consider the
+ WebContent process to be a *background* WebContent process.
+
+ * WebProcess/WebProcess.h:
+
+ * WebProcess/cocoa/WebProcessCocoa.mm:
+ (WebKit::WebProcess::updateBackgroundCPULimit):
+ Compute the WebProcess' background CPU limit given the limit set for each
+ of its WebPages. We use the largest (i.e. most permissive) background CPU
+ limit among all the pages.
+
+ (WebKit::WebProcess::updateBackgroundCPUMonitorState):
+ Update the state of the background CPU monitor. This is called whenever
+ the background CPU limit of the process changes or whenever the visibility
+ of a WebPage changes.
+
2017-03-13 Michael Saboff <[email protected]>
Add iOS plumbing to WebProcess to enable _javascript_Core configuration and logging
Modified: trunk/Source/WebKit2/Shared/WebPageCreationParameters.cpp (213856 => 213857)
--- trunk/Source/WebKit2/Shared/WebPageCreationParameters.cpp 2017-03-13 18:00:25 UTC (rev 213856)
+++ trunk/Source/WebKit2/Shared/WebPageCreationParameters.cpp 2017-03-13 18:09:30 UTC (rev 213857)
@@ -93,6 +93,7 @@
encoder.encodeEnum(userInterfaceLayoutDirection);
encoder.encodeEnum(observedLayoutMilestones);
encoder << overrideContentSecurityPolicy;
+ encoder << backgroundCPULimit;
encoder << urlSchemeHandlers;
#if ENABLE(WEB_RTC)
encoder << iceCandidateFilteringEnabled;
@@ -222,6 +223,9 @@
if (!decoder.decode(parameters.overrideContentSecurityPolicy))
return false;
+ if (!decoder.decode(parameters.backgroundCPULimit))
+ return false;
+
if (!decoder.decode(parameters.urlSchemeHandlers))
return false;
Modified: trunk/Source/WebKit2/Shared/WebPageCreationParameters.h (213856 => 213857)
--- trunk/Source/WebKit2/Shared/WebPageCreationParameters.h 2017-03-13 18:00:25 UTC (rev 213856)
+++ trunk/Source/WebKit2/Shared/WebPageCreationParameters.h 2017-03-13 18:09:30 UTC (rev 213857)
@@ -146,6 +146,7 @@
WebCore::LayoutMilestones observedLayoutMilestones;
String overrideContentSecurityPolicy;
+ std::optional<double> backgroundCPULimit;
HashMap<String, uint64_t> urlSchemeHandlers;
Modified: trunk/Source/WebKit2/UIProcess/API/APIPageConfiguration.cpp (213856 => 213857)
--- trunk/Source/WebKit2/UIProcess/API/APIPageConfiguration.cpp 2017-03-13 18:00:25 UTC (rev 213856)
+++ trunk/Source/WebKit2/UIProcess/API/APIPageConfiguration.cpp 2017-03-13 18:09:30 UTC (rev 213857)
@@ -68,6 +68,7 @@
copy->m_alwaysRunsAtForegroundPriority = this->m_alwaysRunsAtForegroundPriority;
#endif
copy->m_initialCapitalizationEnabled = this->m_initialCapitalizationEnabled;
+ copy->m_backgroundCPULimit = this->m_backgroundCPULimit;
copy->m_controlledByAutomation = this->m_controlledByAutomation;
copy->m_overrideContentSecurityPolicy = this->m_overrideContentSecurityPolicy;
Modified: trunk/Source/WebKit2/UIProcess/API/APIPageConfiguration.h (213856 => 213857)
--- trunk/Source/WebKit2/UIProcess/API/APIPageConfiguration.h 2017-03-13 18:00:25 UTC (rev 213856)
+++ trunk/Source/WebKit2/UIProcess/API/APIPageConfiguration.h 2017-03-13 18:09:30 UTC (rev 213857)
@@ -93,6 +93,9 @@
bool initialCapitalizationEnabled() { return m_initialCapitalizationEnabled; }
void setInitialCapitalizationEnabled(bool initialCapitalizationEnabled) { m_initialCapitalizationEnabled = initialCapitalizationEnabled; }
+ std::optional<double> backgroundCPULimit() const { return m_backgroundCPULimit; }
+ void setBackgroundCPULimit(double cpuLimit) { m_backgroundCPULimit = cpuLimit; }
+
bool waitsForPaintAfterViewDidMoveToWindow() const { return m_waitsForPaintAfterViewDidMoveToWindow; }
void setWaitsForPaintAfterViewDidMoveToWindow(bool shouldSynchronize) { m_waitsForPaintAfterViewDidMoveToWindow = shouldSynchronize; }
@@ -124,6 +127,7 @@
bool m_initialCapitalizationEnabled = true;
bool m_waitsForPaintAfterViewDidMoveToWindow = true;
bool m_controlledByAutomation = false;
+ std::optional<double> m_backgroundCPULimit;
WTF::String m_overrideContentSecurityPolicy;
};
Modified: trunk/Source/WebKit2/UIProcess/API/C/WKPageConfigurationRef.cpp (213856 => 213857)
--- trunk/Source/WebKit2/UIProcess/API/C/WKPageConfigurationRef.cpp 2017-03-13 18:00:25 UTC (rev 213856)
+++ trunk/Source/WebKit2/UIProcess/API/C/WKPageConfigurationRef.cpp 2017-03-13 18:09:30 UTC (rev 213857)
@@ -108,3 +108,8 @@
{
toImpl(configuration)->setInitialCapitalizationEnabled(enabled);
}
+
+void WKPageConfigurationSetBackgroundCPULimit(WKPageConfigurationRef configuration, double cpuLimit)
+{
+ toImpl(configuration)->setBackgroundCPULimit(cpuLimit);
+}
Modified: trunk/Source/WebKit2/UIProcess/API/C/WKPageConfigurationRef.h (213856 => 213857)
--- trunk/Source/WebKit2/UIProcess/API/C/WKPageConfigurationRef.h 2017-03-13 18:00:25 UTC (rev 213856)
+++ trunk/Source/WebKit2/UIProcess/API/C/WKPageConfigurationRef.h 2017-03-13 18:09:30 UTC (rev 213857)
@@ -55,6 +55,7 @@
WK_EXPORT void WKPageConfigurationSetWebsiteDataStore(WKPageConfigurationRef configuration, WKWebsiteDataStoreRef websiteDataStore);
WK_EXPORT void WKPageConfigurationSetInitialCapitalizationEnabled(WKPageConfigurationRef configuration, bool enabled);
+WK_EXPORT void WKPageConfigurationSetBackgroundCPULimit(WKPageConfigurationRef configuration, double cpuLimit); // Terminates process if it uses more than CPU limit over an extended period of time while in the background.
#ifdef __cplusplus
}
Modified: trunk/Source/WebKit2/UIProcess/WebPageProxy.cpp (213856 => 213857)
--- trunk/Source/WebKit2/UIProcess/WebPageProxy.cpp 2017-03-13 18:00:25 UTC (rev 213856)
+++ trunk/Source/WebKit2/UIProcess/WebPageProxy.cpp 2017-03-13 18:09:30 UTC (rev 213857)
@@ -356,6 +356,7 @@
, m_alwaysRunsAtForegroundPriority(m_configuration->alwaysRunsAtForegroundPriority())
#endif
, m_initialCapitalizationEnabled(m_configuration->initialCapitalizationEnabled())
+ , m_backgroundCPULimit(m_configuration->backgroundCPULimit())
, m_backForwardList(WebBackForwardList::create(*this))
, m_maintainsInactiveSelection(false)
, m_waitsForPaintAfterViewDidMoveToWindow(m_configuration->waitsForPaintAfterViewDidMoveToWindow())
@@ -716,6 +717,7 @@
ASSERT(m_process->state() == WebProcessProxy::State::Terminated);
m_isValid = true;
+ m_wasTerminatedDueToResourceExhaustionWhileInBackground = false;
m_process->removeWebPage(m_pageID);
m_process->removeMessageReceiver(Messages::WebPageProxy::messageReceiverName(), m_pageID);
@@ -1528,8 +1530,13 @@
m_activityStateChangeDispatcher->invalidate();
#endif
- if (!isValid())
+ if (!isValid()) {
+ if (m_potentiallyChangedActivityStateFlags & ActivityState::IsVisible && m_wasTerminatedDueToResourceExhaustionWhileInBackground) {
+ m_wasTerminatedDueToResourceExhaustionWhileInBackground = false;
+ processDidCrash();
+ }
return;
+ }
// If the visibility state may have changed, then so may the visually idle & occluded agnostic state.
if (m_potentiallyChangedActivityStateFlags & ActivityState::IsVisible)
@@ -2383,7 +2390,7 @@
m_process->send(Messages::WebPage::SetCustomTextEncodingName(encodingName), m_pageID);
}
-void WebPageProxy::terminateProcess()
+void WebPageProxy::terminateProcess(TerminationReason terminationReason)
{
// NOTE: This uses a check of m_isValid rather than calling isValid() since
// we want this to run even for pages being closed or that already closed.
@@ -2392,6 +2399,7 @@
m_process->requestTermination();
resetStateAfterProcessExited();
+ m_wasTerminatedDueToResourceExhaustionWhileInBackground = terminationReason == TerminationReason::ResourceExhaustionWhileInBackground;
}
SessionState WebPageProxy::sessionState(const std::function<bool (WebBackForwardListItem&)>& filter) const
@@ -5589,6 +5597,7 @@
parameters.userInterfaceLayoutDirection = m_pageClient.userInterfaceLayoutDirection();
parameters.observedLayoutMilestones = m_observedLayoutMilestones;
parameters.overrideContentSecurityPolicy = m_overrideContentSecurityPolicy;
+ parameters.backgroundCPULimit = m_backgroundCPULimit;
for (auto& iterator : m_urlSchemeHandlersByScheme)
parameters.urlSchemeHandlers.set(iterator.key, iterator.value->identifier());
Modified: trunk/Source/WebKit2/UIProcess/WebPageProxy.h (213856 => 213857)
--- trunk/Source/WebKit2/UIProcess/WebPageProxy.h 2017-03-13 18:00:25 UTC (rev 213856)
+++ trunk/Source/WebKit2/UIProcess/WebPageProxy.h 2017-03-13 18:09:30 UTC (rev 213857)
@@ -680,7 +680,8 @@
double estimatedProgress() const;
- void terminateProcess();
+ enum class TerminationReason { ResourceExhaustionWhileInBackground, Other };
+ void terminateProcess(TerminationReason = TerminationReason::Other);
SessionState sessionState(const std::function<bool (WebBackForwardListItem&)>& = nullptr) const;
RefPtr<API::Navigation> restoreFromSessionState(SessionState, bool navigate);
@@ -1721,6 +1722,7 @@
ProcessThrottler::ForegroundActivityToken m_activityToken;
#endif
bool m_initialCapitalizationEnabled;
+ std::optional<double> m_backgroundCPULimit;
Ref<WebBackForwardList> m_backForwardList;
bool m_maintainsInactiveSelection;
@@ -1984,6 +1986,7 @@
#endif
bool m_isUsingHighPerformanceWebGL { false };
+ bool m_wasTerminatedDueToResourceExhaustionWhileInBackground { false };
WeakPtrFactory<WebPageProxy> m_weakPtrFactory;
Modified: trunk/Source/WebKit2/UIProcess/WebProcessProxy.cpp (213856 => 213857)
--- trunk/Source/WebKit2/UIProcess/WebProcessProxy.cpp 2017-03-13 18:00:25 UTC (rev 213856)
+++ trunk/Source/WebKit2/UIProcess/WebProcessProxy.cpp 2017-03-13 18:09:30 UTC (rev 213857)
@@ -1113,6 +1113,33 @@
m_backgroundResponsivenessTimer.processTerminated();
}
+static Vector<RefPtr<WebPageProxy>> pagesCopy(WTF::IteratorRange<WebProcessProxy::WebPageProxyMap::const_iterator::Values> pages)
+{
+ Vector<RefPtr<WebPageProxy>> vector;
+ for (auto& page : pages)
+ vector.append(page);
+ return vector;
+}
+
+void WebProcessProxy::didExceedBackgroundCPULimit()
+{
+ for (auto& page : pages()) {
+ if (page->isViewVisible())
+ return;
+
+ if (page->isPlayingAudio()) {
+ RELEASE_LOG(PerformanceLogging, "%p - WebProcessProxy::didExceedBackgroundCPULimit() WebProcess has exceeded the background CPU limit but we are not terminating it because there is audio playing", this);
+ return;
+ }
+ }
+
+ RELEASE_LOG(PerformanceLogging, "%p - WebProcessProxy::didExceedBackgroundCPULimit() Terminating background WebProcess that has exceeded the background CPU limit", this);
+
+ // We only terminate the process here. We will call processDidCrash() once one of the pages becomes visible again (see WebPageProxy::dispatchActivityStateChange()).
+ for (auto& page : pagesCopy(pages()))
+ page->terminateProcess(WebPageProxy::TerminationReason::ResourceExhaustionWhileInBackground);
+}
+
void WebProcessProxy::updateBackgroundResponsivenessTimer()
{
m_backgroundResponsivenessTimer.updateState();
Modified: trunk/Source/WebKit2/UIProcess/WebProcessProxy.h (213856 => 213857)
--- trunk/Source/WebKit2/UIProcess/WebProcessProxy.h 2017-03-13 18:00:25 UTC (rev 213856)
+++ trunk/Source/WebKit2/UIProcess/WebProcessProxy.h 2017-03-13 18:09:30 UTC (rev 213857)
@@ -164,6 +164,7 @@
bool isUnderMemoryPressure() const { return m_isUnderMemoryPressure; }
void processTerminated();
+ void didExceedBackgroundCPULimit();
private:
explicit WebProcessProxy(WebProcessPool&, WebsiteDataStore*);
Modified: trunk/Source/WebKit2/UIProcess/WebProcessProxy.messages.in (213856 => 213857)
--- trunk/Source/WebKit2/UIProcess/WebProcessProxy.messages.in 2017-03-13 18:00:25 UTC (rev 213856)
+++ trunk/Source/WebKit2/UIProcess/WebProcessProxy.messages.in 2017-03-13 18:09:30 UTC (rev 213857)
@@ -45,6 +45,8 @@
SetIsHoldingLockedFiles(bool isHoldingLockedFiles)
+ DidExceedBackgroundCPULimit()
+
RetainIconForPageURL(String pageURL)
ReleaseIconForPageURL(String pageURL)
Modified: trunk/Source/WebKit2/WebProcess/WebPage/WebPage.cpp (213856 => 213857)
--- trunk/Source/WebKit2/WebProcess/WebPage/WebPage.cpp 2017-03-13 18:00:25 UTC (rev 213856)
+++ trunk/Source/WebKit2/WebProcess/WebPage/WebPage.cpp 2017-03-13 18:09:30 UTC (rev 213857)
@@ -361,6 +361,7 @@
, m_userActivityHysteresis([this](HysteresisState) { updateUserActivity(); })
, m_userInterfaceLayoutDirection(parameters.userInterfaceLayoutDirection)
, m_overrideContentSecurityPolicy { parameters.overrideContentSecurityPolicy }
+ , m_backgroundCPULimit(parameters.backgroundCPULimit)
{
ASSERT(m_pageID);
@@ -2624,6 +2625,7 @@
pluginView->activityStateDidChange(changed);
m_drawingArea->activityStateDidChange(changed, wantsDidUpdateActivityState, callbackIDs);
+ WebProcess::singleton().pageActivityStateDidChange(m_pageID, changed);
if (changed & ActivityState::IsInWindow)
updateIsInWindow();
Modified: trunk/Source/WebKit2/WebProcess/WebPage/WebPage.h (213856 => 213857)
--- trunk/Source/WebKit2/WebProcess/WebPage/WebPage.h 2017-03-13 18:00:25 UTC (rev 213856)
+++ trunk/Source/WebKit2/WebProcess/WebPage/WebPage.h 2017-03-13 18:09:30 UTC (rev 213857)
@@ -972,6 +972,7 @@
#endif
WebURLSchemeHandlerProxy* urlSchemeHandlerForScheme(const String&);
+ std::optional<double> backgroundCPULimit() const { return m_backgroundCPULimit; }
private:
WebPage(uint64_t pageID, WebPageCreationParameters&&);
@@ -1550,6 +1551,7 @@
WebCore::UserInterfaceLayoutDirection m_userInterfaceLayoutDirection { WebCore::UserInterfaceLayoutDirection::LTR };
const String m_overrideContentSecurityPolicy;
+ const std::optional<double> m_backgroundCPULimit;
HashMap<String, std::unique_ptr<WebURLSchemeHandlerProxy>> m_schemeToURLSchemeHandlerProxyMap;
HashMap<uint64_t, WebURLSchemeHandlerProxy*> m_identifierToURLSchemeHandlerProxyMap;
Modified: trunk/Source/WebKit2/WebProcess/WebProcess.cpp (213856 => 213857)
--- trunk/Source/WebKit2/WebProcess/WebProcess.cpp 2017-03-13 18:00:25 UTC (rev 213856)
+++ trunk/Source/WebKit2/WebProcess/WebProcess.cpp 2017-03-13 18:09:30 UTC (rev 213857)
@@ -71,6 +71,7 @@
#include <WebCore/AXObjectCache.h>
#include <WebCore/ApplicationCacheStorage.h>
#include <WebCore/AuthenticationChallenge.h>
+#include <WebCore/CPUMonitor.h>
#include <WebCore/CommonVM.h>
#include <WebCore/CrossOriginPreflightResultCache.h>
#include <WebCore/DNS.h>
@@ -580,6 +581,7 @@
// Balanced by an enableTermination in removeWebPage.
disableTermination();
+ updateBackgroundCPULimit();
} else
result.iterator->value->reinitializeWebPage(WTFMove(parameters));
@@ -594,6 +596,7 @@
m_pageMap.remove(pageID);
enableTermination();
+ updateBackgroundCPULimit();
}
bool WebProcess::shouldTerminate()
@@ -1268,8 +1271,22 @@
{
}
+void WebProcess::updateBackgroundCPULimit()
+{
+}
+
+void WebProcess::updateBackgroundCPUMonitorState()
+{
+}
+
#endif
+void WebProcess::pageActivityStateDidChange(uint64_t, WebCore::ActivityState::Flags changed)
+{
+ if (changed & WebCore::ActivityState::IsVisible)
+ updateBackgroundCPUMonitorState();
+}
+
#if PLATFORM(IOS)
void WebProcess::resetAllGeolocationPermissions()
{
@@ -1561,6 +1578,15 @@
m_dnsPrefetchHystereris.impulse();
}
+bool WebProcess::hasVisibleWebPage() const
+{
+ for (auto& page : m_pageMap.values()) {
+ if (page->isVisible())
+ return true;
+ }
+ return false;
+}
+
#if USE(LIBWEBRTC)
LibWebRTCNetwork& WebProcess::libWebRTCNetwork()
{
Modified: trunk/Source/WebKit2/WebProcess/WebProcess.h (213856 => 213857)
--- trunk/Source/WebKit2/WebProcess/WebProcess.h 2017-03-13 18:00:25 UTC (rev 213856)
+++ trunk/Source/WebKit2/WebProcess/WebProcess.h 2017-03-13 18:09:30 UTC (rev 213857)
@@ -62,6 +62,7 @@
namespace WebCore {
class ApplicationCacheStorage;
+class CPUMonitor;
class CertificateInfo;
class PageGroup;
class ResourceRequest;
@@ -194,6 +195,7 @@
#endif
void updateActivePages();
+ void pageActivityStateDidChange(uint64_t pageID, WebCore::ActivityState::Flags changed);
void setHiddenPageDOMTimerThrottlingIncreaseLimit(int milliseconds);
@@ -318,6 +320,9 @@
void destroyAutomationSessionProxy();
void logDiagnosticMessageForNetworkProcessCrash();
+ bool hasVisibleWebPage() const;
+ void updateBackgroundCPULimit();
+ void updateBackgroundCPUMonitorState();
// ChildProcess
void initializeProcess(const ChildProcessInitializationParameters&) override;
@@ -419,6 +424,10 @@
unsigned m_pagesMarkingLayersAsVolatile { 0 };
bool m_suppressMemoryPressureHandler { false };
+#if PLATFORM(MAC)
+ std::unique_ptr<WebCore::CPUMonitor> m_backgroundCPUMonitor;
+ std::optional<double> m_backgroundCPULimit;
+#endif
HashMap<WebCore::UserGestureToken *, uint64_t> m_userGestureTokens;
Modified: trunk/Source/WebKit2/WebProcess/cocoa/WebProcessCocoa.mm (213856 => 213857)
--- trunk/Source/WebKit2/WebProcess/cocoa/WebProcessCocoa.mm 2017-03-13 18:00:25 UTC (rev 213856)
+++ trunk/Source/WebKit2/WebProcess/cocoa/WebProcessCocoa.mm 2017-03-13 18:09:30 UTC (rev 213857)
@@ -48,6 +48,7 @@
#import <_javascript_Core/Options.h>
#import <WebCore/AXObjectCache.h>
#import <WebCore/CFNetworkSPI.h>
+#import <WebCore/CPUMonitor.h>
#import <WebCore/FileSystem.h>
#import <WebCore/FontCache.h>
#import <WebCore/FontCascade.h>
@@ -77,6 +78,10 @@
namespace WebKit {
+#if PLATFORM(MAC)
+static const Seconds backgroundCPUMonitoringInterval { 15_min };
+#endif
+
void WebProcess::platformSetCacheModel(CacheModel)
{
}
@@ -369,6 +374,49 @@
#endif
}
+void WebProcess::updateBackgroundCPULimit()
+{
+#if PLATFORM(MAC)
+ std::optional<double> backgroundCPULimit;
+
+ // Use the largest limit among all pages in this process.
+ for (auto& page : m_pageMap.values()) {
+ auto pageCPULimit = page->backgroundCPULimit();
+ if (!pageCPULimit) {
+ backgroundCPULimit = std::nullopt;
+ break;
+ }
+ if (!backgroundCPULimit || pageCPULimit > backgroundCPULimit.value())
+ backgroundCPULimit = pageCPULimit;
+ }
+
+ if (m_backgroundCPULimit == backgroundCPULimit)
+ return;
+
+ m_backgroundCPULimit = backgroundCPULimit;
+ updateBackgroundCPUMonitorState();
+#endif
+}
+
+void WebProcess::updateBackgroundCPUMonitorState()
+{
+#if PLATFORM(MAC)
+ if (!m_backgroundCPULimit || hasVisibleWebPage()) {
+ if (m_backgroundCPUMonitor)
+ m_backgroundCPUMonitor->setCPULimit(std::nullopt);
+ return;
+ }
+
+ if (!m_backgroundCPUMonitor) {
+ m_backgroundCPUMonitor = std::make_unique<CPUMonitor>(backgroundCPUMonitoringInterval, [this] {
+ RELEASE_LOG(PerformanceLogging, "%p - WebProcess exceeded background CPU limit of %.1f%%", this, m_backgroundCPULimit.value() * 100);
+ parentProcessConnection()->send(Messages::WebProcessProxy::DidExceedBackgroundCPULimit(), 0);
+ });
+ }
+ m_backgroundCPUMonitor->setCPULimit(m_backgroundCPULimit.value());
+#endif
+}
+
RefPtr<ObjCObjectGraph> WebProcess::transformHandlesToObjects(ObjCObjectGraph& objectGraph)
{
struct Transformer final : ObjCObjectGraph::Transformer {